diff --git a/.ci/docs b/.ci/docs index 361144726b9..8d25e63479b 100644 --- a/.ci/docs +++ b/.ci/docs @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runDocs( env: env) diff --git a/.ci/kitchen-amazon1-py2 b/.ci/kitchen-amazon1-py2 deleted file mode 100644 index 7caf2ef7610..00000000000 --- a/.ci/kitchen-amazon1-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'amazon', - distro_version: '1', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 7, - use_spot_instances: false) - -// vim: ft=groovy diff --git a/.ci/kitchen-amazon2-py2 b/.ci/kitchen-amazon2-py2 deleted file mode 100644 index 96f8f498458..00000000000 --- a/.ci/kitchen-amazon2-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'amazon', - distro_version: '2', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-amazon2-py3 b/.ci/kitchen-amazon2-py3 index d0df5cb2fdb..eb96bf296f2 100644 --- a/.ci/kitchen-amazon2-py3 +++ b/.ci/kitchen-amazon2-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-archlts-py2 b/.ci/kitchen-archlts-py2 deleted file mode 100644 index fa7682e9483..00000000000 --- a/.ci/kitchen-archlts-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'arch', - distro_version: 'lts', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '-n integration.modules.test_pkg', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-archlts-py3 b/.ci/kitchen-archlts-py3 index f529c5e36c1..305138db08a 100644 --- a/.ci/kitchen-archlts-py3 +++ b/.ci/kitchen-archlts-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-centos6-py2 b/.ci/kitchen-centos6-py2 deleted file mode 100644 index 763a5f385e7..00000000000 --- a/.ci/kitchen-centos6-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'centos', - distro_version: '6', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2 b/.ci/kitchen-centos7-py2 deleted file mode 100644 index f0e9c0b250a..00000000000 --- a/.ci/kitchen-centos7-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'centos', - distro_version: '7', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-cloud b/.ci/kitchen-centos7-py2-cloud deleted file mode 100644 index ddc0f0bd99d..00000000000 --- a/.ci/kitchen-centos7-py2-cloud +++ /dev/null @@ -1,17 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 0, - distro_name: 'centos', - distro_version: '7', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - kitchen_platforms_file: '/var/jenkins/workspace/nox-cloud-platforms.yml', - nox_env_name: 'runtests-cloud', - nox_passthrough_opts: '', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-m2crypto b/.ci/kitchen-centos7-py2-m2crypto deleted file mode 100644 index 3515f2d595c..00000000000 --- a/.ci/kitchen-centos7-py2-m2crypto +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'centos', - distro_version: '7', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq-m2crypto', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-proxy b/.ci/kitchen-centos7-py2-proxy deleted file mode 100644 index 7129c7e8f37..00000000000 --- a/.ci/kitchen-centos7-py2-proxy +++ /dev/null @@ -1,17 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'centos', - distro_version: '7', - env: env, - extra_codecov_flags: ["proxy"], - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--proxy', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-pycryptodomex b/.ci/kitchen-centos7-py2-pycryptodomex deleted file mode 100644 index 4c2864ed01f..00000000000 --- a/.ci/kitchen-centos7-py2-pycryptodomex +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'centos', - distro_version: '7', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq-pycryptodomex', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-tcp b/.ci/kitchen-centos7-py2-tcp deleted file mode 100644 index d0d31731f97..00000000000 --- a/.ci/kitchen-centos7-py2-tcp +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'centos', - distro_version: '7', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-tcp', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py2-tornado b/.ci/kitchen-centos7-py2-tornado deleted file mode 100644 index 0193bbc3b58..00000000000 --- a/.ci/kitchen-centos7-py2-tornado +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'centos', - distro_version: '7', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-tornado', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-centos7-py3 b/.ci/kitchen-centos7-py3 index 4047eed1fbb..f73e9245a81 100644 --- a/.ci/kitchen-centos7-py3 +++ b/.ci/kitchen-centos7-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-centos7-py3-cloud b/.ci/kitchen-centos7-py3-cloud index 8f3355e3b8a..8bd418d89f0 100644 --- a/.ci/kitchen-centos7-py3-cloud +++ b/.ci/kitchen-centos7-py3-cloud @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 0, diff --git a/.ci/kitchen-centos7-py3-m2crypto b/.ci/kitchen-centos7-py3-m2crypto index edd5a334bfa..4a2fde9c59c 100644 --- a/.ci/kitchen-centos7-py3-m2crypto +++ b/.ci/kitchen-centos7-py3-m2crypto @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-centos7-py3-proxy b/.ci/kitchen-centos7-py3-proxy index ccbe3acf1e9..cb8cd5ad454 100644 --- a/.ci/kitchen-centos7-py3-proxy +++ b/.ci/kitchen-centos7-py3-proxy @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-centos7-py3-pycryptodomex b/.ci/kitchen-centos7-py3-pycryptodomex index 5945f576502..836ee316a63 100644 --- a/.ci/kitchen-centos7-py3-pycryptodomex +++ b/.ci/kitchen-centos7-py3-pycryptodomex @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-centos7-py3-tcp b/.ci/kitchen-centos7-py3-tcp index aaebe5aa168..a34e47772a0 100644 --- a/.ci/kitchen-centos7-py3-tcp +++ b/.ci/kitchen-centos7-py3-tcp @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-centos8-py3 b/.ci/kitchen-centos8-py3 index 5dbdf94c509..f7edec3e1f5 100644 --- a/.ci/kitchen-centos8-py3 +++ b/.ci/kitchen-centos8-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-debian10-py3 b/.ci/kitchen-debian10-py3 index 8d1d1dd6c0d..ac24cdc9dd1 100644 --- a/.ci/kitchen-debian10-py3 +++ b/.ci/kitchen-debian10-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-debian8-py2 b/.ci/kitchen-debian8-py2 deleted file mode 100644 index e2620e580db..00000000000 --- a/.ci/kitchen-debian8-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'debian', - distro_version: '8', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-debian9-py2 b/.ci/kitchen-debian9-py2 deleted file mode 100644 index 7549d070485..00000000000 --- a/.ci/kitchen-debian9-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'debian', - distro_version: '9', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-debian9-py3 b/.ci/kitchen-debian9-py3 index 53e7a3bc1fc..efb6a71443e 100644 --- a/.ci/kitchen-debian9-py3 +++ b/.ci/kitchen-debian9-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-fedora30-py2 b/.ci/kitchen-fedora30-py2 deleted file mode 100644 index 78c7a717ae7..00000000000 --- a/.ci/kitchen-fedora30-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'fedora', - distro_version: '30', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-fedora30-py3 b/.ci/kitchen-fedora30-py3 index 8a44d643f76..7507ef02046 100644 --- a/.ci/kitchen-fedora30-py3 +++ b/.ci/kitchen-fedora30-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-fedora31-py3 b/.ci/kitchen-fedora31-py3 index 22d4a05d219..38876a62f70 100644 --- a/.ci/kitchen-fedora31-py3 +++ b/.ci/kitchen-fedora31-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-macosxhighsierra-py2 b/.ci/kitchen-macosxhighsierra-py2 deleted file mode 100644 index 4ee5cbb9a0d..00000000000 --- a/.ci/kitchen-macosxhighsierra-py2 +++ /dev/null @@ -1,20 +0,0 @@ -@Library('salt@master-1.6') _ - -// Pre-nox pipeline -runTestSuite( - concurrent_builds: 1, - distro_name: 'macosx', - distro_version: 'highsierra', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave-mac', - kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', - kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '', - python_version: 'py2', - run_full: params.runFull, - testrun_timeout: 6, - use_spot_instances: false) - -// vim: ft=groovy diff --git a/.ci/kitchen-macosxhighsierra-py3 b/.ci/kitchen-macosxhighsierra-py3 index bd8ce925d53..5c8fa38e086 100644 --- a/.ci/kitchen-macosxhighsierra-py3 +++ b/.ci/kitchen-macosxhighsierra-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ // Pre-nox pipeline runTestSuite( @@ -11,7 +11,7 @@ runTestSuite( kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '', + nox_passthrough_opts: '--ssh-tests', python_version: 'py3', run_full: params.runFull, testrun_timeout: 6, diff --git a/.ci/kitchen-macosxmojave-py2 b/.ci/kitchen-macosxmojave-py2 deleted file mode 100644 index a9c17bb5fcd..00000000000 --- a/.ci/kitchen-macosxmojave-py2 +++ /dev/null @@ -1,20 +0,0 @@ -@Library('salt@master-1.6') _ - -// Pre-nox pipeline -runTestSuite( - concurrent_builds: 1, - distro_name: 'macosx', - distro_version: 'mojave', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave-mac', - kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', - kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '', - python_version: 'py2', - run_full: params.runFull, - testrun_timeout: 6, - use_spot_instances: false) - -// vim: ft=groovy diff --git a/.ci/kitchen-macosxmojave-py3 b/.ci/kitchen-macosxmojave-py3 index 9f3f0a21b41..d3427816829 100644 --- a/.ci/kitchen-macosxmojave-py3 +++ b/.ci/kitchen-macosxmojave-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ // Pre-nox pipeline runTestSuite( @@ -11,7 +11,7 @@ runTestSuite( kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '', + nox_passthrough_opts: '--ssh-tests', python_version: 'py3', run_full: params.runFull, testrun_timeout: 6, diff --git a/.ci/kitchen-macosxsierra-py2 b/.ci/kitchen-macosxsierra-py2 deleted file mode 100644 index 85cb6dffac9..00000000000 --- a/.ci/kitchen-macosxsierra-py2 +++ /dev/null @@ -1,20 +0,0 @@ -@Library('salt@master-1.6') _ - -// Pre-nox pipeline -runTestSuite( - concurrent_builds: 1, - distro_name: 'macosx', - distro_version: 'sierra', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave-mac', - kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', - kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '', - python_version: 'py2', - run_full: params.runFull, - testrun_timeout: 6, - use_spot_instances: false) - -// vim: ft=groovy diff --git a/.ci/kitchen-macosxsierra-py3 b/.ci/kitchen-macosxsierra-py3 index b9308af2991..508a8fcaa65 100644 --- a/.ci/kitchen-macosxsierra-py3 +++ b/.ci/kitchen-macosxsierra-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ // Pre-nox pipeline runTestSuite( @@ -11,7 +11,7 @@ runTestSuite( kitchen_platforms_file: '/var/jenkins/workspace/pre-golden-nox-platforms.yml', kitchen_verifier_file: '/var/jenkins/workspace/nox-verifier.yml', nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '', + nox_passthrough_opts: '--ssh-tests', python_version: 'py3', run_full: params.runFull, testrun_timeout: 6, diff --git a/.ci/kitchen-opensuse15-py2 b/.ci/kitchen-opensuse15-py2 deleted file mode 100644 index b65d901ab64..00000000000 --- a/.ci/kitchen-opensuse15-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'opensuse', - distro_version: '15', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-opensuse15-py3 b/.ci/kitchen-opensuse15-py3 index 5b731e161c3..60aeb638c54 100644 --- a/.ci/kitchen-opensuse15-py3 +++ b/.ci/kitchen-opensuse15-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-ubuntu1604-py2 b/.ci/kitchen-ubuntu1604-py2 deleted file mode 100644 index 7c113c5652c..00000000000 --- a/.ci/kitchen-ubuntu1604-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'ubuntu', - distro_version: '1604', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-m2crypto b/.ci/kitchen-ubuntu1604-py2-m2crypto deleted file mode 100644 index 9a73c22c4f2..00000000000 --- a/.ci/kitchen-ubuntu1604-py2-m2crypto +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'ubuntu', - distro_version: '1604', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq-m2crypto', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-proxy b/.ci/kitchen-ubuntu1604-py2-proxy deleted file mode 100644 index 55ca1971159..00000000000 --- a/.ci/kitchen-ubuntu1604-py2-proxy +++ /dev/null @@ -1,17 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'ubuntu', - distro_version: '1604', - env: env, - extra_codecov_flags: ["proxy"], - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--proxy', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-pycryptodomex b/.ci/kitchen-ubuntu1604-py2-pycryptodomex deleted file mode 100644 index 67bac97f066..00000000000 --- a/.ci/kitchen-ubuntu1604-py2-pycryptodomex +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'ubuntu', - distro_version: '1604', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq-pycryptodomex', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-tcp b/.ci/kitchen-ubuntu1604-py2-tcp deleted file mode 100644 index d2870c3e562..00000000000 --- a/.ci/kitchen-ubuntu1604-py2-tcp +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'ubuntu', - distro_version: '1604', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-tcp', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py2-tornado b/.ci/kitchen-ubuntu1604-py2-tornado deleted file mode 100644 index 92e0f14b75c..00000000000 --- a/.ci/kitchen-ubuntu1604-py2-tornado +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'ubuntu', - distro_version: '1604', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-tornado', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1604-py3 b/.ci/kitchen-ubuntu1604-py3 index 435db9b2fc2..8ded93bf5ff 100644 --- a/.ci/kitchen-ubuntu1604-py3 +++ b/.ci/kitchen-ubuntu1604-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-ubuntu1604-py3-m2crypto b/.ci/kitchen-ubuntu1604-py3-m2crypto index 156d6aa0335..5256ff92bbf 100644 --- a/.ci/kitchen-ubuntu1604-py3-m2crypto +++ b/.ci/kitchen-ubuntu1604-py3-m2crypto @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-ubuntu1604-py3-proxy b/.ci/kitchen-ubuntu1604-py3-proxy index 9297b432910..af8bfe5af26 100644 --- a/.ci/kitchen-ubuntu1604-py3-proxy +++ b/.ci/kitchen-ubuntu1604-py3-proxy @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-ubuntu1604-py3-pycryptodomex b/.ci/kitchen-ubuntu1604-py3-pycryptodomex index e49e539a48e..efae604860c 100644 --- a/.ci/kitchen-ubuntu1604-py3-pycryptodomex +++ b/.ci/kitchen-ubuntu1604-py3-pycryptodomex @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-ubuntu1604-py3-tcp b/.ci/kitchen-ubuntu1604-py3-tcp index f14bd8628d9..0b86041b88f 100644 --- a/.ci/kitchen-ubuntu1604-py3-tcp +++ b/.ci/kitchen-ubuntu1604-py3-tcp @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-ubuntu1804-py2 b/.ci/kitchen-ubuntu1804-py2 deleted file mode 100644 index 2e70b7cc7ef..00000000000 --- a/.ci/kitchen-ubuntu1804-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'ubuntu', - distro_version: '1804', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--ssh-tests', - python_version: 'py2', - testrun_timeout: 6, - use_spot_instances: true) - -// vim: ft=groovy diff --git a/.ci/kitchen-ubuntu1804-py3 b/.ci/kitchen-ubuntu1804-py3 index f74e8f46ffd..de723f86822 100644 --- a/.ci/kitchen-ubuntu1804-py3 +++ b/.ci/kitchen-ubuntu1804-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-windows2016-py2 b/.ci/kitchen-windows2016-py2 deleted file mode 100644 index d9d2257f2ee..00000000000 --- a/.ci/kitchen-windows2016-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'windows', - distro_version: '2016', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--unit', - python_version: 'py2', - testrun_timeout: 8, - use_spot_instances: false) - -// vim: ft=groovy diff --git a/.ci/kitchen-windows2016-py3 b/.ci/kitchen-windows2016-py3 index a0fc54ca89f..17904791542 100644 --- a/.ci/kitchen-windows2016-py3 +++ b/.ci/kitchen-windows2016-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/kitchen-windows2019-py2 b/.ci/kitchen-windows2019-py2 deleted file mode 100644 index 906c256cb0f..00000000000 --- a/.ci/kitchen-windows2019-py2 +++ /dev/null @@ -1,16 +0,0 @@ -@Library('salt@master-1.6') _ - -runTestSuite( - concurrent_builds: 1, - distro_name: 'windows', - distro_version: '2019', - env: env, - golden_images_branch: 'master', - jenkins_slave_label: 'kitchen-slave', - nox_env_name: 'runtests-zeromq', - nox_passthrough_opts: '--unit', - python_version: 'py2', - testrun_timeout: 8, - use_spot_instances: false) - -// vim: ft=groovy diff --git a/.ci/kitchen-windows2019-py3 b/.ci/kitchen-windows2019-py3 index 7bb114cd87f..f0ac7d44d9a 100644 --- a/.ci/kitchen-windows2019-py3 +++ b/.ci/kitchen-windows2019-py3 @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runTestSuite( concurrent_builds: 1, diff --git a/.ci/lint b/.ci/lint index d3c77bee442..ceeefc3a760 100644 --- a/.ci/lint +++ b/.ci/lint @@ -1,4 +1,4 @@ -@Library('salt@master-1.6') _ +@Library('salt@master-1.7') _ runLint( env: env) diff --git a/.ci/pre-commit b/.ci/pre-commit new file mode 100644 index 00000000000..ad263df0a8b --- /dev/null +++ b/.ci/pre-commit @@ -0,0 +1,6 @@ +@Library('salt@master-1.7') _ + +runPreCommit( + env: env) + +// vim: ft=groovy diff --git a/.codecov.yml b/.codecov.yml index 7f55f72c93b..d7157478d1b 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -2,21 +2,23 @@ codecov: ci: - drone.saltstack.com - jenkinsci.saltstack.com +# max_report_age: 24 # The age you want coverage reports to expire at, or if you +# # want to disable this check. Expired reports will not be processed by codecov. +# require_ci_to_pass: yes # Less spammy. Only notify on passing builds. - branch: master # notify: -# require_ci_to_pass: yes # Less spammy. Only notify on passing builds. -# after_n_builds: 46 # Only notify after N builds +# after_n_builds: 25 # Only notify after N builds # # This value is the output of: # # sh -c 'echo "$(ls .ci/ | grep kitchen | wc -l)"' +# wait_for_ci: yes # Should Codecov wait for all CI statuses to complete before sending ours. +# # Note: Codecov considers all non-codecov statues to be CI statuses -# Disable Notifications - notify: off ignore: - ^*.py$ # python files at the repo root, ie, setup.py - doc/.* # ignore any code under doc/ + - salt/ext/.* # ignore any code under salt/ext coverage: round: up @@ -25,9 +27,13 @@ coverage: status: project: # measuring the overall project coverage - default: false # disable the default status that measures entire project + default: + informational: true # Use Codecov in informational mode. Default is false. If true is specified the + # resulting status will pass no matter what the coverage is or what other settings + # are specified. Informational mode is great to use if you want to expose codecov + # information to other developers in your pull request without necessarily gating + # PRs on that information. salt: # declare a new status context "salt" - enabled: yes # must be yes|true to enable this status paths: "!tests/" # remove all files in "tests/" target: auto # will use the coverage from the base commit (pull request base or parent commit) coverage to compare against. base: auto # will use the pull request base if the commit is on a pull request. If not, the parent commit will be used. @@ -36,7 +42,6 @@ coverage: if_not_found: success # if parent is not found report status as success, error, or failure if_ci_failed: error # if ci fails report status as success, error, or failure tests: # declare a new status context "tests" - enabled: yes # must be yes|true to enable this status #target: 100% # we always want 100% coverage here target: auto # auto while we get this going base: auto # will use the pull request base if the commit is on a pull request. If not, the parent commit will be used. @@ -50,7 +55,11 @@ coverage: # entire pull requests Coverage Diff. Checking if the lines # adjusted are covered at least X%. default: - enabled: yes # must be yes|true to enable this status + informational: true # Use Codecov in informational mode. Default is false. If true is specified the + # resulting status will pass no matter what the coverage is or what other settings + # are specified. Informational mode is great to use if you want to expose codecov + # information to other developers in your pull request without necessarily gating + # PRs on that information. target: 100% # Newly added lines must have 100% coverage if_no_uploads: error # will post commit status of "error" if no coverage reports were uploaded # options: success, error, failure @@ -59,7 +68,11 @@ coverage: changes: # if there are any unexpected changes in coverage default: - enabled: yes # must be yes|true to enable this status + informational: true # Use Codecov in informational mode. Default is false. If true is specified the + # resulting status will pass no matter what the coverage is or what other settings + # are specified. Informational mode is great to use if you want to expose codecov + # information to other developers in your pull request without necessarily gating + # PRs on that information. if_no_uploads: error if_not_found: success if_ci_failed: error @@ -68,9 +81,11 @@ flags: salt: paths: - salt/ + carryforward: true # https://docs.codecov.io/docs/carryforward-flags tests: paths: - tests/ + carryforward: true #comment: # layout: "reach, diff, flags, files" diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index e69de29bb2d..1203dbcf845 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Blacken Salt +0b2a5613b345f17339cb90e60b407199b3d26980 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b8495c0e82a..ad768ec5832 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -67,19 +67,19 @@ Fixing issues ============= If you wish to help us fix the issue you're reporting, -[Salt's documentation](http://docs.saltstack.com/en/latest/index.html) +[Salt's documentation](http://docs.saltstack.com/en/master/index.html) already includes information to help you setup a development environment, -under [Developing Salt](http://docs.saltstack.com/en/latest/topics/development/hacking.html). +under [Developing Salt](http://docs.saltstack.com/en/master/topics/development/hacking.html). -[SaltStack's Contributing documentation](https://docs.saltstack.com/en/latest/topics/development/contributing.html) +[SaltStack's Contributing documentation](https://docs.saltstack.com/en/master/topics/development/contributing.html) is also helpful, as it explains sending in pull requests, keeping your salt branches in sync, and knowing -[which branch](https://docs.saltstack.com/en/latest/topics/development/contributing.html#which-salt-branch) +[which branch](https://docs.saltstack.com/en/master/topics/development/contributing.html#which-salt-branch) new features or bug fixes should be submitted against. Fix the issue you have in hand and, if possible, also add a test case to Salt's testing suite. Then, create a -[pull request](http://docs.saltstack.com/en/latest/topics/development/contributing.html#sending-a-github-pull-request), +[pull request](http://docs.saltstack.com/en/master/topics/development/contributing.html#sending-a-github-pull-request), and **that's it**! Salt's development team will review your fix and if everything is OK, your fix diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000000..e18eb921685 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,35 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG]" +labels: Bug +assignees: '' + +--- + +**Description** +A clear and concise description of what the bug is. + +**Setup** +(Please provide relevant configs and/or SLS files (be sure to remove sensitive info). + +**Steps to Reproduce the behavior** +(Include debug logs if possible and relevant) + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Versions Report** +
salt --versions-report +(Provided by running salt --versions-report. Please also mention any differences in master/minion versions.) + +``` +PASTE HERE +``` +
+ +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..983d0bfcbec --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: true +contact_links: + - name: Salt Community Slack + url: https://saltstackcommunity.slack.com/ + about: Please ask and answer questions here. + - name: Salt-Users Forum + url: https://groups.google.com/forum/#!forum/salt-users + about: Please ask and answer questions here. + - name: Salt on Freenode + url: http://webchat.freenode.net/?channels=salt&uio=Mj10cnVlJjk9dHJ1ZSYxMD10cnVl83 + about: Please ask and answer questions here. + - name: Security vulnerabilities + email: security@saltstack.com + about: Please report security vulnerabilities here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000000..2b918d9ce13 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[FEATURE REQUEST]" +labels: Feature +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. + +**Please Note** +If this feature request would be considered a substantial change or addition, this should go through a SEP process here https://github.com/saltstack/salt-enhancement-proposals, instead of a feature request. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4849806867f..d05ff8cd077 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,7 @@ ### What does this PR do? ### What issues does this PR fix or reference? +Fixes: ### Previous Behavior Remove this section if not relevant @@ -8,16 +9,16 @@ Remove this section if not relevant ### New Behavior Remove this section if not relevant -### Tests written? +### Merge requirements satisfied? **[NOTICE] Bug fixes or features added to Salt require tests.** -Please review the [test documentation](https://docs.saltstack.com/en/latest/topics/tutorials/writing_tests.html) for details on how to implement tests into Salt's test suite. - -Yes/No + +- [ ] Docs +- [ ] Changelog - https://docs.saltstack.com/en/latest/topics/development/changelog.html +- [ ] Tests written/updated ### Commits signed with GPG? - Yes/No -Please review [Salt's Contributing Guide](https://docs.saltstack.com/en/latest/topics/development/contributing.html) for best practices. +Please review [Salt's Contributing Guide](https://docs.saltstack.com/en/master/topics/development/contributing.html) for best practices. See GitHub's [page on GPG signing](https://help.github.com/articles/signing-commits-using-gpg/) for more information about signing commits with GPG. diff --git a/.github/stale.yml b/.github/stale.yml index 0fb0fc239cd..b34faf368eb 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -2,10 +2,10 @@ # Number of days of inactivity before an issue becomes stale # 600 is approximately 1 year and 8 months -daysUntilStale: 30 +daysUntilStale: 90 # Number of days of inactivity before a stale issue is closed -daysUntilClose: 7 +daysUntilClose: false # Issues with these labels will never be considered stale exemptLabels: @@ -17,6 +17,7 @@ exemptLabels: - Bug - Feature - Test Failure + -ZD # Label to use when marking an issue as stale staleLabel: stale @@ -36,4 +37,3 @@ closeComment: false # Limit to only `issues` or `pulls` only: issues - diff --git a/.gitignore b/.gitignore index 8a480d24b5f..25709b759ec 100644 --- a/.gitignore +++ b/.gitignore @@ -114,3 +114,4 @@ kitchen.local.yml .bundle/ Gemfile.lock /artifacts/ +requirements/static/py*/*.log diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000000..16eaf94f7e0 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,108 @@ +--- +stages: + - lint + - test + +include: + - local: 'cicd/kitchen_template.yml' + - local: 'cicd/kitchen_testruns.yml' + +# pre-commit-run-all: +# image: +# name: registry.gitlab.com/saltstack/pop/cicd/containers/ubuntu1804:latest +# entrypoint: [""] +# stage: lint +# variables: +# PRE_COMMIT_HOME: "${CI_PROJECT_DIR}/pre-commit-cache" +# only: +# refs: +# - merge_requests +# cache: +# key: pre-commit-cache +# paths: +# - pre-commit-cache/ +# script: +# - pip3 install pre-commit +# - pre-commit run -a -v --color always + +lint-salt-full: + image: registry.gitlab.com/saltstack/pop/cicd/containers/ubuntu1804:latest + stage: lint + tags: + - saltstack-internal + cache: + key: nox-lint-cache + paths: + - .nox + only: + refs: + - schedules + script: + - python --version + - pip3 install -U nox-py2==2019.6.25 + - nox --version + - nox --install-only -e lint-salt + - EC=254 + - export PYLINT_REPORT=pylint-report-salt-full.log + - nox -e lint-salt + - EC=$? + - exit $EC + +lint-tests-full: + image: registry.gitlab.com/saltstack/pop/cicd/containers/ubuntu1804:latest + stage: lint + tags: + - saltstack-internal + cache: + key: nox-lint-cache + paths: + - .nox + only: + refs: + - schedules + script: + - python --version + - pip3 install -U nox-py2==2019.6.25 + - nox --version + - nox --install-only -e lint-tests + - EC=254 + - export PYLINT_REPORT=pylint-report-tests-full.log + - nox -e lint-tests + - EC=$? + - exit $EC + +docs-build-html: + image: registry.gitlab.com/saltstack/pop/cicd/containers/ubuntu1804:latest + stage: test + tags: + - saltstack-internal + cache: + key: nox-docs-cache + paths: + - .nox + only: + refs: + - schedules + script: + - python --version + - pip install -U nox-py2==2019.6.25 + - nox --version + - nox -e 'docs-html(compress=True)' + +docs-build-man-pages: + image: registry.gitlab.com/saltstack/pop/cicd/containers/ubuntu1804:latest + stage: test + tags: + - saltstack-internal + cache: + key: nox-docs-cache + paths: + - .nox + only: + refs: + - schedules + script: + - python --version + - pip install -U nox-py2==2019.6.25 + - nox --version + - nox -e 'docs-man(compress=True, update=False)' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 25cf9b07077..22e31a5d96f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,120 +1,11 @@ +default_language_version: + python: python3 + exclude: ^(doc/_static/.*|doc/_themes/.*)$ repos: - repo: https://github.com/saltstack/pip-tools-compile-impersonate rev: master hooks: - - id: pip-tools-compile - alias: compile-linux-py2.7-zmq-requirements - name: Linux Py2.7 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/linux\.in)$ - exclude: ^requirements/static/(lint|cloud|docs|darwin|windows)\.in$ - args: - - -v - - --py-version=2.7 - - --platform=linux - - --include=requirements/base.txt - - --include=requirements/zeromq.txt - - --include=requirements/pytest.txt - - - id: pip-tools-compile - alias: compile-darwin-py2.7-zmq-requirements - name: Darwin Py2.7 ZeroMQ Requirements - files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ - args: - - -v - - --py-version=2.7 - - --platform=darwin - - --include=pkg/osx/req.txt - - --include=pkg/osx/req_ext.txt - - --include=requirements/base.txt - - --include=requirements/zeromq.txt - - --include=requirements/pytest.txt - - --passthrough-line-from-input=^pyobjc(.*)$ - - - id: pip-tools-compile - alias: compile-windows-py2.7-zmq-requirements - name: Windows Py2.7 ZeroMQ Requirements - files: ^(pkg/windows/(req|req_win)\.txt|requirements/((base|zeromq|pytest)\.txt|static/windows\.in))$ - args: - - -v - - --py-version=2.7 - - --platform=windows - - --include=pkg/windows/req.txt - - --include=pkg/windows/req_win.txt - - --include=requirements/base.txt - - --include=requirements/zeromq.txt - - --include=requirements/pytest.txt - - - id: pip-tools-compile - alias: compile-cloud-py2.7-requirements - name: Cloud Py2.7 Requirements - files: ^requirements/(static/cloud\.in)$ - args: - - -v - - --py-version=2.7 - - - id: pip-tools-compile - alias: compile-linux-crypto-py2.7-requirements - name: Linux Py2.7 Crypto Requirements - files: ^requirements/(crypto\.txt|static/crypto\.in)$ - args: - - -v - - --py-version=2.7 - - --platform=linux - - --out-prefix=linux - - - id: pip-tools-compile - alias: compile-darwin-crypto-py2.7-requirements - name: Darwin Py2.7 Crypto Requirements - files: ^requirements/(crypto\.txt|static/crypto\.in)$ - args: - - -v - - --py-version=2.7 - - --platform=darwin - - --out-prefix=darwin - - - id: pip-tools-compile - alias: compile-windows-crypto-py2.7-requirements - name: Windows Py2.7 Crypto Requirements - files: ^requirements/(crypto\.txt|static/crypto\.in)$ - args: - - -v - - --py-version=2.7 - - --platform=windows - - --out-prefix=windows - - - - id: pip-tools-compile - alias: compile-linux-py3.4-zmq-requirements - name: Linux Py3.4 ZeroMQ Requirements - files: ^requirements/((base|zeromq|pytest)\.txt|static/linux\.in)$ - exclude: ^requirements/static/(centos-6|amzn-2018\.03|lint|cloud|docs|darwin|windows)\.in$ - args: - - -v - - --py-version=3.4 - - --platform=linux - - --include=requirements/base.txt - - --include=requirements/zeromq.txt - - --include=requirements/pytest.txt - - - id: pip-tools-compile - alias: compile-cloud-py3.4-requirements - name: Cloud Py3.4 Requirements - files: ^requirements/(static/cloud\.in)$ - args: - - -v - - --py-version=3.4 - - - id: pip-tools-compile - alias: compile-linux-crypto-py3.4-requirements - name: Linux Py3.4 Crypto Requirements - files: ^requirements/(crypto\.txt|static/crypto\.in)$ - args: - - -v - - --py-version=3.4 - - --platform=linux - - --out-prefix=linux - - id: pip-tools-compile alias: compile-linux-py3.5-zmq-requirements @@ -132,13 +23,14 @@ repos: - id: pip-tools-compile alias: compile-darwin-py3.5-zmq-requirements name: Darwin Py3.5 ZeroMQ Requirements - files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ + files: ^(pkg/osx/(req|req_ext|req_pyobjc)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ args: - -v - --py-version=3.5 - --platform=darwin - --include=pkg/osx/req.txt - --include=pkg/osx/req_ext.txt + - --include=pkg/osx/req_pyobjc.txt - --include=requirements/base.txt - --include=requirements/zeromq.txt - --include=requirements/pytest.txt @@ -175,6 +67,15 @@ repos: - --py-version=3.5 - --platform=linux + - id: pip-tools-compile + alias: compile-changelog-requirements + name: Changelog Py3.5 Requirements + files: ^requirements/static/changelog\.in$ + args: + - -v + - --py-version=3.5 + - --platform=linux + - id: pip-tools-compile alias: compile-linux-crypto-py3.5-requirements name: Linux Py3.5 Crypto Requirements @@ -214,7 +115,6 @@ repos: - --py-version=3.5 - --platform=linux - - id: pip-tools-compile alias: compile-linux-py3.6-zmq-requirements name: Linux Py3.6 ZeroMQ Requirements @@ -231,13 +131,14 @@ repos: - id: pip-tools-compile alias: compile-darwin-py3.6-zmq-requirements name: Darwin Py3.6 ZeroMQ Requirements - files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ + files: ^(pkg/osx/(req|req_ext|req_pyobjc)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ args: - -v - --py-version=3.6 - --platform=darwin - --include=pkg/osx/req.txt - --include=pkg/osx/req_ext.txt + - --include=pkg/osx/req_pyobjc.txt - --include=requirements/base.txt - --include=requirements/zeromq.txt - --include=requirements/pytest.txt @@ -274,6 +175,15 @@ repos: - --py-version=3.6 - --platform=linux + - id: pip-tools-compile + alias: compile-changelog-requirements + name: Changelog Py3.6 Requirements + files: ^requirements/static/changelog\.in$ + args: + - -v + - --py-version=3.6 + - --platform=linux + - id: pip-tools-compile alias: compile-linux-crypto-py3.6-requirements name: Linux Py3.6 Crypto Requirements @@ -313,7 +223,6 @@ repos: - --py-version=3.6 - --platform=linux - - id: pip-tools-compile alias: compile-linux-py3.7-zmq-requirements name: Linux Py3.7 ZeroMQ Requirements @@ -330,13 +239,14 @@ repos: - id: pip-tools-compile alias: compile-darwin-py3.7-zmq-requirements name: Darwin Py3.7 ZeroMQ Requirements - files: ^(pkg/osx/(req|req_ext)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ + files: ^(pkg/osx/(req|req_ext|req_pyobjc)\.txt|requirements/((base|zeromq|pytest)\.txt|static/darwin\.in))$ args: - -v - --py-version=3.7 - --platform=darwin - --include=pkg/osx/req.txt - --include=pkg/osx/req_ext.txt + - --include=pkg/osx/req_pyobjc.txt - --include=requirements/base.txt - --include=requirements/zeromq.txt - --include=requirements/pytest.txt @@ -373,6 +283,15 @@ repos: - --py-version=3.7 - --platform=linux + - id: pip-tools-compile + alias: compile-changelog-requirements + name: Changelog Py3.7 Requirements + files: ^requirements/static/changelog\.in$ + args: + - -v + - --py-version=3.7 + - --platform=linux + - id: pip-tools-compile alias: compile-linux-crypto-py3.7-requirements name: Linux Py3.7 Crypto Requirements @@ -412,6 +331,32 @@ repos: - --py-version=3.7 - --platform=linux + - repo: https://github.com/timothycrosley/isort + rev: "1e78a9acf3110e1f9721feb591f89a451fc9876a" + hooks: + - id: isort + additional_dependencies: ['toml'] + # This tells pre-commit not to pass files to isort. + # This should be kept in sync with pyproject.toml + exclude: > + (?x)^( + templates/.*| + salt/ext/.*| + tests/kitchen/.* + )$ + + - repo: https://github.com/psf/black + rev: 19.10b0 + hooks: + - id: black + # This tells pre-commit not to pass files to black. + # This should be kept in sync with pyproject.toml + exclude: > + (?x)^( + templates/.*| + salt/ext/.*| + tests/kitchen/.* + )$ - repo: https://github.com/saltstack/salt-nox-pre-commit rev: master diff --git a/.pylintrc b/.pylintrc index 57105fd554a..0321d2cbc63 100644 --- a/.pylintrc +++ b/.pylintrc @@ -8,11 +8,11 @@ extension-pkg-whitelist= # Add files or directories to the blacklist. They should be base names, not # paths. ignore=CVS, - ext + ext, # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. -ignore-patterns= +ignore-patterns=salt.ext.* # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). @@ -77,6 +77,7 @@ disable=R, no-member, unsubscriptable-object, un-indexed-curly-braces-error, + whitespace-before-colon, indentation-is-not-a-multiple-of-four-comment, blacklisted-name, invalid-name, @@ -126,7 +127,9 @@ disable=R, str-format-in-logging, import-outside-toplevel, deprecated-method, - keyword-arg-before-vararg + repr-flag-used-in-string, + keyword-arg-before-vararg, + incompatible-py3-code # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -396,6 +399,7 @@ init-import=no # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=six.moves, + salt.ext.six.moves, past.builtins, future.builtins, builtins, @@ -472,7 +476,9 @@ ignored-classes=SQLObject # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis). It # supports qualified module names, as well as Unix pattern matching. -ignored-modules= +ignored-modules=salt.ext.six.moves, + six.moves, + _MovedItems, # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. @@ -511,7 +517,7 @@ min-similarity-lines=4 fileperms-default=0644 # File paths to ignore file permission. Glob patterns allowed. -fileperms-ignore-paths=setup.py,noxfile.py,tests/runtests.py,tests/jenkins*.py,tests/saltsh.py,tests/buildpackage.py +fileperms-ignore-paths=setup.py,noxfile.py,tests/runtests.py,tests/jenkins*.py,tests/saltsh.py,tests/buildpackage.py,tests/unit/files/rosters/ansible/roster.py [MODERNIZE] diff --git a/CHANGELOG.md b/CHANGELOG.md index d1b7259e5bf..42b7ab0eb44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,3 @@ -# Changelog All notable changes to Salt will be documented in this file. This changelog follows [keepachangelog](https://keepachangelog.com/en/1.0.0/) format, and is intended for human consumption. @@ -6,7 +5,9 @@ This changelog follows [keepachangelog](https://keepachangelog.com/en/1.0.0/) fo This project versioning is _similar_ to [Semantic Versioning](https://semver.org), and is documented in [SEP 14](https://github.com/saltstack/salt-enhancement-proposals/pull/20/files). Versions are `MAJOR.PATCH`. -### 3000.1 +# Changelog + +## 3001 - Sodium ### Removed @@ -15,6 +16,22 @@ Versions are `MAJOR.PATCH`. ### Changed ### Fixed +- [#56237](https://github.com/saltstack/salt/pull/56237) - Fix alphabetical ordering and remove duplicates across all documentation indexes - [@myii](https://github.com/myii) +- [#56325](https://github.com/saltstack/salt/pull/56325) - Fix hyperlinks to `salt.serializers` and other documentation issues - [@myii](https://github.com/myii) + +### Added +- [#56627](https://github.com/saltstack/salt/pull/56627) - Add new salt-ssh set_path option + +## 3000.1 + +### Removed + +### Deprecated + +### Changed + +### Fixed + - [#56082](https://github.com/saltstack/salt/pull/56082) - Fix saltversioninfo grain for new version - [#56143](https://github.com/saltstack/salt/pull/56143) - Use encoding when caching pillar data - [#56172](https://github.com/saltstack/salt/pull/56172) - Only change mine data if using new allow_tgt feature @@ -40,6 +57,11 @@ Versions are `MAJOR.PATCH`. - [#56310](https://github.com/saltstack/salt/pull/56310) - Only process ADMX files when loading policies - [#56327](https://github.com/saltstack/salt/pull/56327) - keep cache_copied_files variable a list - [#56360](https://github.com/saltstack/salt/pull/56360) - dont require virtualenv.virtualenv_version call, removed in 20.0.10 +- [#56378](https://github.com/saltstack/salt/pull/56378) - Include _version.py if building wheel +- [#56376](https://github.com/saltstack/salt/pull/56376) - Fix win deps +- [#56418](https://github.com/saltstack/salt/pull/56418) - Ensure version.py included before we install +- [#56435](https://github.com/saltstack/salt/pull/56435) - Update mac build scripts + ### Added diff --git a/changelog/55836.added b/changelog/55836.added new file mode 100644 index 00000000000..5bec52a40aa --- /dev/null +++ b/changelog/55836.added @@ -0,0 +1 @@ +Add towncrier tool to the Salt project to help manage CHANGELOG.md file. diff --git a/cicd/kitchen_template.yml b/cicd/kitchen_template.yml new file mode 100644 index 00000000000..99aad6f59cc --- /dev/null +++ b/cicd/kitchen_template.yml @@ -0,0 +1,50 @@ + +.run-kitchen: + image: ruby:2.6.3 + stage: test + tags: + - saltstack-kitchen + only: + refs: + - schedules + variables: + FORCE_FULL: 'true' + GOLDEN_IMAGES_CI_BRANCH: master + NOX_ENABLE_FROM_FILENAMES: 'true' + NOX_ENV_NAME: runtests-zeromq + NOX_PASSTHROUGH_OPTS: '--ssh-tests' + SALT_KITCHEN_DRIVER: kitchen-conf/driver.yml + SALT_KITCHEN_PLATFORMS: kitchen-conf/nox-platforms.yml + SALT_KITCHEN_VERIFIER: kitchen-conf/nox-verifier.yml + TEST_SUITE: py3 + USE_SPOT_INSTANCES: 'true' + script: + - apt update + - apt -y install moreutils rsync dos2unix + - mkdir -p ~/.ssh + - echo "${KITCHEN_SSHKEY}" | tr -d '\r' > ~/.ssh/kitchen.pem + - chmod 700 ~/.ssh/ + - chmod 600 ~/.ssh/kitchen.pem + - git clone https://gitlab.com/saltstack/open/cicd/kitchen-conf.git + - bundle install --with ec2 windows --without docker vagrant + - t=$(shuf -i 30-150 -n 1); echo "Sleeping $t seconds"; sleep $t + - if [ "${USE_SPOT_INSTANCES}" == "true" ]; then cp -f kitchen-conf/spot.yml .kitchen.local.yml; fi + - 'bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM || (bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM; rm .kitchen.local.yml; bundle exec kitchen create $TEST_SUITE-$TEST_PLATFORM)' + - bundle exec kitchen diagnose $TEST_SUITE-$TEST_PLATFORM > kitchen-diagnose-info.txt + - grep 'image_id:' kitchen-diagnose-info.txt + - grep 'instance_type:' -A5 kitchen-diagnose-info.txt + - rm -f kitchen-diagnose-info.txt + - rm -f .kitchen.local.yml + - ssh-agent /bin/bash -xc 'ssh-add ~/.ssh/kitchen.pem; bundle exec kitchen converge $TEST_SUITE-$TEST_PLATFORM' + - TEST_EXIT_CODE=0 + - 'DONT_DOWNLOAD_ARTEFACTS=1 bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM|ts -s || TEST_EXIT_CODE=$?' + - 'ONLY_DOWNLOAD_ARTEFACTS=1 bundle exec kitchen verify $TEST_SUITE-$TEST_PLATFORM|ts -s || true' + - bundle exec kitchen destroy $TEST_SUITE-$TEST_PLATFORM + - exit $TEST_EXIT_CODE + artifacts: + when: always + paths: + - artifacts/ + - .kitchen/ + expire_in: 6 months + diff --git a/cicd/kitchen_testruns.yml b/cicd/kitchen_testruns.yml new file mode 100644 index 00000000000..c3c5ed3f6dd --- /dev/null +++ b/cicd/kitchen_testruns.yml @@ -0,0 +1,123 @@ + +kitchen-amazon2-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: amazon-2 + +kitchen-archlts-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: arch-lts + +kitchen-archlts-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: arch-lts + NOX_PASSTHROUGH_OPTS: '-n integration.modules.test_pkg' + +kitchen-centos7-py3-m2crypto: + extends: .run-kitchen + variables: + TEST_PLATFORM: centos-7 + NOX_ENV_NAME: runtests-zeromq-m2crypto + +kitchen-centos7-py3-proxy: + extends: .run-kitchen + variables: + TEST_PLATFORM: centos-7 + NOX_PASSTHROUGH_OPTS: '--proxy' + +kitchen-centos7-py3-pycryptodomex: + extends: .run-kitchen + variables: + TEST_PLATFORM: centos-7 + NOX_ENV_NAME: runtests-zeromq-pycryptodomex + +kitchen-centos7-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: centos-7 + +kitchen-centos7-py3-tcp: + extends: .run-kitchen + variables: + TEST_PLATFORM: centos-7 + NOX_ENV_NAME: runtests-tcp + +kitchen-centos8-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: centos-8 + +kitchen-debian9-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: debian-9 + +kitchen-debian10-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: debian-10 + +kitchen-fedora30-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: fedora-30 + +kitchen-fedora31-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: fedora-31 + +kitchen-opensuse15-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: opensuse-15 + +kitchen-ubuntu1604-py3-m2crypto: + extends: .run-kitchen + variables: + TEST_PLATFORM: ubuntu-1604 + NOX_ENV_NAME: runtests-zeromq-m2crypto + +kitchen-ubuntu1604-py3-proxy: + extends: .run-kitchen + variables: + TEST_PLATFORM: ubuntu-1604 + NOX_PASSTHROUGH_OPTS: '--proxy' + +kitchen-ubuntu1604-py3-pycryptodomex: + extends: .run-kitchen + variables: + TEST_PLATFORM: ubuntu-1604 + NOX_ENV_NAME: runtests-zeromq-pycryptodomex + +kitchen-ubuntu1604-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: ubuntu-1604 + +kitchen-ubuntu1604-py3-tcp: + extends: .run-kitchen + variables: + TEST_PLATFORM: ubuntu-1604 + NOX_ENV_NAME: runtests-tcp + +kitchen-ubuntu1804-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: ubuntu-1804 + +kitchen-windows2016-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: windows-2016 + NOX_PASSTHROUGH_OPTS: '--unit' + USE_SPOT_INSTANCES: 'false' + +kitchen-windows2019-py3: + extends: .run-kitchen + variables: + TEST_PLATFORM: windows-2019 + NOX_PASSTHROUGH_OPTS: '--unit' + USE_SPOT_INSTANCES: 'false' diff --git a/conf/master b/conf/master index 77addaf4e95..457f68dc135 100644 --- a/conf/master +++ b/conf/master @@ -506,6 +506,12 @@ # Boolean to run command via sudo. #ssh_sudo: False +# Boolean to run ssh_pre_flight script defined in roster. By default +# the script will only run if the thin_dir does not exist on the targeted +# minion. This forces the script to run regardless of the thin dir existing +# or not. +#ssh_run_pre_flight: True + # Number of seconds to wait for a response when establishing an SSH connection. #ssh_timeout: 60 diff --git a/doc/.scripts/setup-transifex-config b/doc/.scripts/setup-transifex-config index 398a3f875fe..36dd95cf717 100755 --- a/doc/.scripts/setup-transifex-config +++ b/doc/.scripts/setup-transifex-config @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -8,54 +8,49 @@ ~~~~~~~~~~~~~~~~~~~~~~ Setup the Transifex client configuration file -''' +""" + +import getpass # Import python libs import os import sys -import getpass + import ConfigParser -HOST = 'https://www.transifex.com' +HOST = "https://www.transifex.com" RCFILE = os.path.abspath( - os.environ.get( - 'TRANSIFEX_RC', - os.path.expanduser('~/.transifexrc') - ) + os.environ.get("TRANSIFEX_RC", os.path.expanduser("~/.transifexrc")) ) def main(): - ''' + """ Run the setup code - ''' + """ print( - 'This script will setup a Transifex client configuration file, or, ' - 'if it already exists, make some minimal checks to see if it\'s ' - 'properly configured\n' + "This script will setup a Transifex client configuration file, or, " + "if it already exists, make some minimal checks to see if it's " + "properly configured\n" ) if not os.path.exists(RCFILE): while True: - username = os.environ.get('TRANSIFEX_USER', None) + username = os.environ.get("TRANSIFEX_USER", None) if username is not None: break try: - username = raw_input( - 'What is your username on Transifex.com? ' - ) + username = raw_input("What is your username on Transifex.com? ") if username: break except KeyboardInterrupt: print sys.exit(1) while True: - password = os.environ.get('TRANSIFEX_PASS', None) + password = os.environ.get("TRANSIFEX_PASS", None) if password is not None: break try: - password = getpass.getpass( - 'What is your password on Transifex.com? ' - ) + password = getpass.getpass("What is your password on Transifex.com? ") if password: break except KeyboardInterrupt: @@ -64,16 +59,16 @@ def main(): config = ConfigParser.SafeConfigParser() config.add_section(HOST) - config.set(HOST, 'token', '') - config.set(HOST, 'hostname', HOST) - config.set(HOST, 'username', username) - config.set(HOST, 'password', password) + config.set(HOST, "token", "") + config.set(HOST, "hostname", HOST) + config.set(HOST, "username", username) + config.set(HOST, "password", password) - config.write(open(RCFILE, 'w')) - print('username and password stored in \'{0}\''.format(RCFILE)) + config.write(open(RCFILE, "w")) + print("username and password stored in '{0}'".format(RCFILE)) os.chmod(RCFILE, 0600) - print('Secured the permissions on \'{0}\' to 0600'.format(RCFILE)) + print("Secured the permissions on '{0}' to 0600".format(RCFILE)) sys.exit(0) @@ -82,24 +77,30 @@ def main(): config.read([RCFILE]) if not config.has_section(HOST): - print('\'~/.transifexrc\' is not properly configured, it\'s missing ' - 'the {0} section'.format(HOST)) + print( + "'~/.transifexrc' is not properly configured, it's missing " + "the {0} section".format(HOST) + ) - for setting in ('username', 'password', 'hostname', 'token'): + for setting in ("username", "password", "hostname", "token"): if not config.has_option(HOST, setting): - print('\'~/.transifexrc\' is not properly configured, it\'s ' - 'missing the {0} option'.format(setting)) + print( + "'~/.transifexrc' is not properly configured, it's " + "missing the {0} option".format(setting) + ) sys.exit(1) - if setting == 'token': + if setting == "token": # Token should be left empty continue if not config.get(HOST, setting): - print('\'~/.transifexrc\' is not properly configured, it\'s ' - 'missing a value for the {0} option'.format(setting)) + print( + "'~/.transifexrc' is not properly configured, it's " + "missing a value for the {0} option".format(setting) + ) sys.exit(1) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/doc/_ext/httpdomain.py b/doc/_ext/httpdomain.py index 5829167e422..a49695532b1 100644 --- a/doc/_ext/httpdomain.py +++ b/doc/_ext/httpdomain.py @@ -13,18 +13,16 @@ import re from docutils import nodes from docutils.parsers.rst.roles import set_classes - from pygments.lexer import RegexLexer, bygroups from pygments.lexers import get_lexer_by_name -from pygments.token import Literal, Text, Operator, Keyword, Name, Number +from pygments.token import Keyword, Literal, Name, Number, Operator, Text from pygments.util import ClassNotFound - from sphinx import addnodes -from sphinx.roles import XRefRole -from sphinx.domains import Domain, ObjType, Index from sphinx.directives import ObjectDescription -from sphinx.util.nodes import make_refnode +from sphinx.domains import Domain, Index, ObjType +from sphinx.roles import XRefRole from sphinx.util.docfields import GroupedField, TypedField +from sphinx.util.nodes import make_refnode class DocRef(object): @@ -44,252 +42,275 @@ class DocRef(object): location of the RFC which defines some HTTP method. """ - return '{0}#{1}{2}'.format(self.base_url, self.anchor, self.section) + return "{0}#{1}{2}".format(self.base_url, self.anchor, self.section) #: The URL of the HTTP/1.1 RFC which defines the HTTP methods OPTIONS, GET, #: HEAD, POST, PUT, DELETE, TRACE, and CONNECT. -RFC2616 = 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html' +RFC2616 = "http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html" #: The name to use for section anchors in RFC2616. -RFC2616ANCHOR = 'sec' +RFC2616ANCHOR = "sec" #: The URL of the RFC which defines the HTTP PATCH method. -RFC5789 = 'http://tools.ietf.org/html/rfc5789' +RFC5789 = "http://tools.ietf.org/html/rfc5789" #: The name to use for section anchors in RFC5789. -RFC5789ANCHOR = 'section-' +RFC5789ANCHOR = "section-" #: Mapping from lowercase HTTP method name to :class:`DocRef` object which #: maintains the URL which points to the section of the RFC which defines that #: HTTP method. DOCREFS = { - 'patch': DocRef(RFC5789, RFC5789ANCHOR, 2), - 'options': DocRef(RFC2616, RFC2616ANCHOR, 9.2), - 'get': DocRef(RFC2616, RFC2616ANCHOR, 9.3), - 'head': DocRef(RFC2616, RFC2616ANCHOR, 9.4), - 'post': DocRef(RFC2616, RFC2616ANCHOR, 9.5), - 'put': DocRef(RFC2616, RFC2616ANCHOR, 9.6), - 'delete': DocRef(RFC2616, RFC2616ANCHOR, 9.7), - 'trace': DocRef(RFC2616, RFC2616ANCHOR, 9.8), - 'connect': DocRef(RFC2616, RFC2616ANCHOR, 9.9) + "patch": DocRef(RFC5789, RFC5789ANCHOR, 2), + "options": DocRef(RFC2616, RFC2616ANCHOR, 9.2), + "get": DocRef(RFC2616, RFC2616ANCHOR, 9.3), + "head": DocRef(RFC2616, RFC2616ANCHOR, 9.4), + "post": DocRef(RFC2616, RFC2616ANCHOR, 9.5), + "put": DocRef(RFC2616, RFC2616ANCHOR, 9.6), + "delete": DocRef(RFC2616, RFC2616ANCHOR, 9.7), + "trace": DocRef(RFC2616, RFC2616ANCHOR, 9.8), + "connect": DocRef(RFC2616, RFC2616ANCHOR, 9.9), } HTTP_STATUS_CODES = { - 100: 'Continue', - 101: 'Switching Protocols', - 102: 'Processing', - 200: 'OK', - 201: 'Created', - 202: 'Accepted', - 203: 'Non Authoritative Information', - 204: 'No Content', - 205: 'Reset Content', - 206: 'Partial Content', - 207: 'Multi Status', - 226: 'IM Used', # see RFC 3229 - 300: 'Multiple Choices', - 301: 'Moved Permanently', - 302: 'Found', - 303: 'See Other', - 304: 'Not Modified', - 305: 'Use Proxy', - 307: 'Temporary Redirect', - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', # unused - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Request Entity Too Large', - 414: 'Request URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Requested Range Not Satisfiable', - 417: 'Expectation Failed', - 418: "I'm a teapot", # see RFC 2324 - 422: 'Unprocessable Entity', - 423: 'Locked', - 424: 'Failed Dependency', - 426: 'Upgrade Required', - 449: 'Retry With', # proprietary MS extension - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 507: 'Insufficient Storage', - 510: 'Not Extended' + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi Status", + 226: "IM Used", # see RFC 3229 + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", # unused + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", # see RFC 2324 + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 426: "Upgrade Required", + 449: "Retry With", # proprietary MS extension + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 507: "Insufficient Storage", + 510: "Not Extended", } -http_sig_param_re = re.compile(r'\((?:(?P[^:)]+):)?(?P[\w_]+)\)', - re.VERBOSE) +http_sig_param_re = re.compile( + r"\((?:(?P[^:)]+):)?(?P[\w_]+)\)", re.VERBOSE +) def http_resource_anchor(method, path): - path = re.sub(r'[<>:/]', '-', path) - return method.lower() + '-' + path + path = re.sub(r"[<>:/]", "-", path) + return method.lower() + "-" + path class HTTPResource(ObjectDescription): doc_field_types = [ - TypedField('parameter', label='Parameters', - names=('param', 'parameter', 'arg', 'argument'), - typerolename='obj', typenames=('paramtype', 'type')), - TypedField('jsonparameter', label='JSON Parameters', - names=('jsonparameter', 'jsonparam', 'json'), - typerolename='obj', typenames=('jsonparamtype', 'jsontype')), - TypedField('queryparameter', label='Query Parameters', - names=('queryparameter', 'queryparam', 'qparam', 'query'), - typerolename='obj', typenames=('queryparamtype', 'querytype', 'qtype')), - GroupedField('formparameter', label='Form Parameters', - names=('formparameter', 'formparam', 'fparam', 'form')), - GroupedField('requestheader', label='Request Headers', - rolename='mailheader', - names=('reqheader', 'requestheader')), - GroupedField('responseheader', label='Response Headers', - rolename='mailheader', - names=('resheader', 'responseheader')), - GroupedField('statuscode', label='Status Codes', - rolename='statuscode', - names=('statuscode', 'status', 'code')) + TypedField( + "parameter", + label="Parameters", + names=("param", "parameter", "arg", "argument"), + typerolename="obj", + typenames=("paramtype", "type"), + ), + TypedField( + "jsonparameter", + label="JSON Parameters", + names=("jsonparameter", "jsonparam", "json"), + typerolename="obj", + typenames=("jsonparamtype", "jsontype"), + ), + TypedField( + "queryparameter", + label="Query Parameters", + names=("queryparameter", "queryparam", "qparam", "query"), + typerolename="obj", + typenames=("queryparamtype", "querytype", "qtype"), + ), + GroupedField( + "formparameter", + label="Form Parameters", + names=("formparameter", "formparam", "fparam", "form"), + ), + GroupedField( + "requestheader", + label="Request Headers", + rolename="mailheader", + names=("reqheader", "requestheader"), + ), + GroupedField( + "responseheader", + label="Response Headers", + rolename="mailheader", + names=("resheader", "responseheader"), + ), + GroupedField( + "statuscode", + label="Status Codes", + rolename="statuscode", + names=("statuscode", "status", "code"), + ), ] method = NotImplemented def handle_signature(self, sig, signode): - method = self.method.upper() + ' ' + method = self.method.upper() + " " signode += addnodes.desc_name(method, method) offset = 0 for match in http_sig_param_re.finditer(sig): - path = sig[offset:match.start()] + path = sig[offset : match.start()] signode += addnodes.desc_name(path, path) params = addnodes.desc_parameterlist() - typ = match.group('type') + typ = match.group("type") if typ: - typ = typ + ': ' + typ = typ + ": " params += addnodes.desc_annotation(typ, typ) - name = match.group('name') + name = match.group("name") params += addnodes.desc_parameter(name, name) signode += params offset = match.end() if offset < len(sig): - path = sig[offset:len(sig)] + path = sig[offset : len(sig)] signode += addnodes.desc_name(path, path) - fullname = self.method.upper() + ' ' + path - signode['method'] = self.method - signode['path'] = sig - signode['fullname'] = fullname + fullname = self.method.upper() + " " + path + signode["method"] = self.method + signode["path"] = sig + signode["fullname"] = fullname return (fullname, self.method, sig) def needs_arglist(self): return False def add_target_and_index(self, name_cls, sig, signode): - signode['ids'].append(http_resource_anchor(*name_cls[1:])) - self.env.domaindata['http'][self.method][sig] = (self.env.docname, '') + signode["ids"].append(http_resource_anchor(*name_cls[1:])) + self.env.domaindata["http"][self.method][sig] = (self.env.docname, "") def get_index_text(self, modname, name): - return '' + return "" class HTTPOptions(HTTPResource): - method = 'options' + method = "options" class HTTPHead(HTTPResource): - method = 'head' + method = "head" class HTTPPatch(HTTPResource): - method = 'patch' + method = "patch" class HTTPPost(HTTPResource): - method = 'post' + method = "post" class HTTPGet(HTTPResource): - method = 'get' + method = "get" class HTTPPut(HTTPResource): - method = 'put' + method = "put" class HTTPDelete(HTTPResource): - method = 'delete' + method = "delete" class HTTPTrace(HTTPResource): - method = 'trace' + method = "trace" -def http_statuscode_role(name, rawtext, text, lineno, inliner, - options={}, content=[]): +def http_statuscode_role(name, rawtext, text, lineno, inliner, options={}, content=[]): if text.isdigit(): code = int(text) try: status = HTTP_STATUS_CODES[code] except KeyError: - msg = inliner.reporter.error('%d is invalid HTTP status code' - % code, lineno=lineno) + msg = inliner.reporter.error( + "%d is invalid HTTP status code" % code, lineno=lineno + ) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] else: try: - code, status = re.split(r'\s', text.strip(), 1) + code, status = re.split(r"\s", text.strip(), 1) code = int(code) except ValueError: msg = inliner.reporter.error( - 'HTTP status code must be an integer (e.g. `200`) or ' - 'start with an integer (e.g. `200 OK`); %r is invalid' % - text, - line=lineno + "HTTP status code must be an integer (e.g. `200`) or " + "start with an integer (e.g. `200 OK`); %r is invalid" % text, + line=lineno, ) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] nodes.reference(rawtext) if code == 226: - url = 'http://www.ietf.org/rfc/rfc3229.txt' + url = "http://www.ietf.org/rfc/rfc3229.txt" if code == 418: - url = 'http://www.ietf.org/rfc/rfc2324.txt' + url = "http://www.ietf.org/rfc/rfc2324.txt" if code == 449: - url = 'http://msdn.microsoft.com/en-us/library' \ - '/dd891478(v=prot.10).aspx' + url = "http://msdn.microsoft.com/en-us/library" "/dd891478(v=prot.10).aspx" elif code in HTTP_STATUS_CODES: - url = 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html' \ - '#sec10.' + ('%d.%d' % (code // 100, 1 + code % 100)) + url = "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" "#sec10." + ( + "%d.%d" % (code // 100, 1 + code % 100) + ) else: - url = '' + url = "" set_classes(options) - node = nodes.reference(rawtext, '%d %s' % (code, status), - refuri=url, **options) + node = nodes.reference(rawtext, "%d %s" % (code, status), refuri=url, **options) return [node], [] -def http_method_role(name, rawtext, text, lineno, inliner, - options={}, content=[]): +def http_method_role(name, rawtext, text, lineno, inliner, options={}, content=[]): method = str(text).lower() if method not in DOCREFS: - msg = inliner.reporter.error('%s is not valid HTTP method' % method, - lineno=lineno) + msg = inliner.reporter.error( + "%s is not valid HTTP method" % method, lineno=lineno + ) prb = inliner.problematic(rawtext, rawtext, msg) return [prb], [msg] url = str(DOCREFS[method]) @@ -298,51 +319,61 @@ def http_method_role(name, rawtext, text, lineno, inliner, class HTTPXRefRole(XRefRole): - def __init__(self, method, **kwargs): XRefRole.__init__(self, **kwargs) self.method = method def process_link(self, env, refnode, has_explicit_title, title, target): - if not target.startswith('/'): + if not target.startswith("/"): pass if not has_explicit_title: - title = self.method.upper() + ' ' + title + title = self.method.upper() + " " + title return title, target class HTTPIndex(Index): - name = 'routingtable' - localname = 'HTTP Routing Table' - shortname = 'routing table' + name = "routingtable" + localname = "HTTP Routing Table" + shortname = "routing table" def __init__(self, *args, **kwargs): super(HTTPIndex, self).__init__(*args, **kwargs) - self.ignore = [[l for l in x.split('/') if l] - for x in self.domain.env.config['http_index_ignore_prefixes']] + self.ignore = [ + [l for l in x.split("/") if l] + for x in self.domain.env.config["http_index_ignore_prefixes"] + ] self.ignore.sort(key=lambda x: -len(x)) def grouping_prefix(self, path): - letters = [x for x in path.split('/') if x] + letters = [x for x in path.split("/") if x] for prefix in self.ignore: - if letters[:len(prefix)] == prefix: - return '/' + '/'.join(letters[:len(prefix) + 1]) - return '/%s' % (letters[0] if letters else '',) + if letters[: len(prefix)] == prefix: + return "/" + "/".join(letters[: len(prefix) + 1]) + return "/%s" % (letters[0] if letters else "",) def generate(self, docnames=None): content = {} - items = ((method, path, info) + items = ( + (method, path, info) for method, routes in self.domain.routes.items() - for path, info in routes.items()) + for path, info in routes.items() + ) items = sorted(items, key=lambda item: item[1]) for method, path, info in items: entries = content.setdefault(self.grouping_prefix(path), []) - entries.append([ - method.upper() + ' ' + path, 0, info[0], - http_resource_anchor(method, path), '', '', info[1] - ]) + entries.append( + [ + method.upper() + " " + path, + 0, + info[0], + http_resource_anchor(method, path), + "", + "", + info[1], + ] + ) content = sorted(content.items(), key=lambda k: k[0]) return (content, True) @@ -350,53 +381,53 @@ class HTTPIndex(Index): class HTTPDomain(Domain): """HTTP domain.""" - name = 'http' - label = 'HTTP' + name = "http" + label = "HTTP" object_types = { - 'options': ObjType('options', 'options', 'obj'), - 'head': ObjType('head', 'head', 'obj'), - 'post': ObjType('post', 'post', 'obj'), - 'get': ObjType('get', 'get', 'obj'), - 'put': ObjType('put', 'put', 'obj'), - 'patch': ObjType('patch', 'patch', 'obj'), - 'delete': ObjType('delete', 'delete', 'obj'), - 'trace': ObjType('trace', 'trace', 'obj') + "options": ObjType("options", "options", "obj"), + "head": ObjType("head", "head", "obj"), + "post": ObjType("post", "post", "obj"), + "get": ObjType("get", "get", "obj"), + "put": ObjType("put", "put", "obj"), + "patch": ObjType("patch", "patch", "obj"), + "delete": ObjType("delete", "delete", "obj"), + "trace": ObjType("trace", "trace", "obj"), } directives = { - 'options': HTTPOptions, - 'head': HTTPHead, - 'post': HTTPPost, - 'get': HTTPGet, - 'put': HTTPPut, - 'patch': HTTPPatch, - 'delete': HTTPDelete, - 'trace': HTTPTrace + "options": HTTPOptions, + "head": HTTPHead, + "post": HTTPPost, + "get": HTTPGet, + "put": HTTPPut, + "patch": HTTPPatch, + "delete": HTTPDelete, + "trace": HTTPTrace, } roles = { - 'options': HTTPXRefRole('options'), - 'head': HTTPXRefRole('head'), - 'post': HTTPXRefRole('post'), - 'get': HTTPXRefRole('get'), - 'put': HTTPXRefRole('put'), - 'patch': HTTPXRefRole('patch'), - 'delete': HTTPXRefRole('delete'), - 'trace': HTTPXRefRole('trace'), - 'statuscode': http_statuscode_role, - 'method': http_method_role + "options": HTTPXRefRole("options"), + "head": HTTPXRefRole("head"), + "post": HTTPXRefRole("post"), + "get": HTTPXRefRole("get"), + "put": HTTPXRefRole("put"), + "patch": HTTPXRefRole("patch"), + "delete": HTTPXRefRole("delete"), + "trace": HTTPXRefRole("trace"), + "statuscode": http_statuscode_role, + "method": http_method_role, } initial_data = { - 'options': {}, # path: (docname, synopsis) - 'head': {}, - 'post': {}, - 'get': {}, - 'put': {}, - 'patch': {}, - 'delete': {}, - 'trace': {} + "options": {}, # path: (docname, synopsis) + "head": {}, + "post": {}, + "get": {}, + "put": {}, + "patch": {}, + "delete": {}, + "trace": {}, } # indices = [HTTPIndex] @@ -412,17 +443,15 @@ class HTTPDomain(Domain): if info[0] == docname: del routes[path] - def resolve_xref(self, env, fromdocname, builder, typ, target, - node, contnode): + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): try: info = self.data[str(typ)][target] except KeyError: return else: anchor = http_resource_anchor(typ, target) - title = typ.upper() + ' ' + target - return make_refnode(builder, fromdocname, info[0], anchor, - contnode, title) + title = typ.upper() + " " + target + return make_refnode(builder, fromdocname, info[0], anchor, contnode, title) def get_objects(self): for method, routes in self.routes.items(): @@ -434,16 +463,16 @@ class HTTPDomain(Domain): class HTTPLexer(RegexLexer): """Lexer for HTTP sessions.""" - name = 'HTTP' - aliases = ['http'] + name = "HTTP" + aliases = ["http"] flags = re.DOTALL def header_callback(self, match): - if match.group(1).lower() == 'content-type': + if match.group(1).lower() == "content-type": content_type = match.group(5).strip() - if ';' in content_type: - content_type = content_type[:content_type.find(';')].strip() + if ";" in content_type: + content_type = content_type[: content_type.find(";")].strip() self.content_type = content_type yield match.start(1), Name.Attribute, match.group(1) yield match.start(2), Text, match.group(2) @@ -458,11 +487,12 @@ class HTTPLexer(RegexLexer): yield match.start(3), Text, match.group(3) def content_callback(self, match): - content_type = getattr(self, 'content_type', None) + content_type = getattr(self, "content_type", None) content = match.group() offset = match.start() if content_type: from pygments.lexers import get_lexer_for_mimetype + try: lexer = get_lexer_for_mimetype(content_type) except ClassNotFound: @@ -474,33 +504,50 @@ class HTTPLexer(RegexLexer): yield offset, Text, content tokens = { - 'root': [ - (r'(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|TRACE)( +)([^ ]+)( +)' - r'(HTTPS?)(/)(1\.[01])(\r?\n|$)', - bygroups(Name.Function, Text, Name.Namespace, Text, - Keyword.Reserved, Operator, Number, Text), - 'headers'), - (r'(HTTPS?)(/)(1\.[01])( +)(\d{3})( +)([^\r\n]+)(\r?\n|$)', - bygroups(Keyword.Reserved, Operator, Number, Text, Number, - Text, Name.Exception, Text), - 'headers'), + "root": [ + ( + r"(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|TRACE)( +)([^ ]+)( +)" + r"(HTTPS?)(/)(1\.[01])(\r?\n|$)", + bygroups( + Name.Function, + Text, + Name.Namespace, + Text, + Keyword.Reserved, + Operator, + Number, + Text, + ), + "headers", + ), + ( + r"(HTTPS?)(/)(1\.[01])( +)(\d{3})( +)([^\r\n]+)(\r?\n|$)", + bygroups( + Keyword.Reserved, + Operator, + Number, + Text, + Number, + Text, + Name.Exception, + Text, + ), + "headers", + ), ], - 'headers': [ - (r'([^\s:]+)( *)(:)( *)([^\r\n]+)(\r?\n|$)', header_callback), - (r'([\t ]+)([^\r\n]+)(\r?\n|$)', continuous_header_callback), - (r'\r?\n', Text, 'content') + "headers": [ + (r"([^\s:]+)( *)(:)( *)([^\r\n]+)(\r?\n|$)", header_callback), + (r"([\t ]+)([^\r\n]+)(\r?\n|$)", continuous_header_callback), + (r"\r?\n", Text, "content"), ], - 'content': [ - (r'.+', content_callback) - ] + "content": [(r".+", content_callback)], } def setup(app): app.add_domain(HTTPDomain) try: - get_lexer_by_name('http') + get_lexer_by_name("http") except ClassNotFound: - app.add_lexer('http', HTTPLexer()) - app.add_config_value('http_index_ignore_prefixes', [], None) - + app.add_lexer("http", HTTPLexer()) + app.add_config_value("http_index_ignore_prefixes", [], None) diff --git a/doc/_ext/saltautodoc.py b/doc/_ext/saltautodoc.py index 439bfe0c251..a729c4220c8 100644 --- a/doc/_ext/saltautodoc.py +++ b/doc/_ext/saltautodoc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,23 +7,23 @@ ~~~~~~~~~~~~~~ Properly handle ``__func_alias__`` -''' +""" # Import Sphinx libs from sphinx.ext.autodoc import FunctionDocumenter as FunctionDocumenter class SaltFunctionDocumenter(FunctionDocumenter): - ''' + """ Simple override of sphinx.ext.autodoc.FunctionDocumenter to properly render salt's aliased function names. - ''' + """ def format_name(self): - ''' + """ Format the function name - ''' - if not hasattr(self.module, '__func_alias__'): + """ + if not hasattr(self.module, "__func_alias__"): # Resume normal sphinx.ext.autodoc operation return super(FunctionDocumenter, self).format_name() @@ -46,4 +46,4 @@ def setup(app): # add_autodocumenter() must be called after the initial setup and the # 'builder-inited' event, as sphinx.ext.autosummary will restore the # original documenter on 'builder-inited' - app.connect('env-before-read-docs', add_documenter) + app.connect("env-before-read-docs", add_documenter) diff --git a/doc/_ext/saltdomain.py b/doc/_ext/saltdomain.py index 0f877376632..603adea926d 100644 --- a/doc/_ext/saltdomain.py +++ b/doc/_ext/saltdomain.py @@ -2,70 +2,66 @@ import itertools import os import re +import salt from docutils import nodes from docutils.parsers.rst import Directive - from docutils.statemachine import ViewList from sphinx import addnodes from sphinx.directives import ObjectDescription from sphinx.domains import Domain, ObjType +from sphinx.domains import python as python_domain from sphinx.domains.python import PyObject from sphinx.locale import _ from sphinx.roles import XRefRole -from sphinx.util.nodes import make_refnode -from sphinx.util.nodes import nested_parse_with_titles -from sphinx.util.nodes import set_source_info - -from sphinx.domains import python as python_domain - -import salt +from sphinx.util.nodes import make_refnode, nested_parse_with_titles, set_source_info class Event(PyObject): - ''' + """ Document Salt events - ''' - domain = 'salt' + """ + + domain = "salt" class LiterateCoding(Directive): - ''' + """ Auto-doc SLS files using literate-style comment/code separation - ''' + """ + has_content = False required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False def parse_file(self, fpath): - ''' + """ Read a file on the file system (relative to salt's base project dir) :returns: A file-like object. :raises IOError: If the file cannot be found or read. - ''' - sdir = os.path.abspath(os.path.join(os.path.dirname(salt.__file__), - os.pardir)) - with open(os.path.join(sdir, fpath), 'rb') as f: + """ + sdir = os.path.abspath(os.path.join(os.path.dirname(salt.__file__), os.pardir)) + with open(os.path.join(sdir, fpath), "rb") as f: return f.readlines() def parse_lit(self, lines): - ''' + """ Parse a string line-by-line delineating comments and code :returns: An tuple of boolean/list-of-string pairs. True designates a comment; False designates code. - ''' - comment_char = '#' # TODO: move this into a directive option - comment = re.compile(r'^\s*{0}[ \n]'.format(comment_char)) + """ + comment_char = "#" # TODO: move this into a directive option + comment = re.compile(r"^\s*{0}[ \n]".format(comment_char)) section_test = lambda val: bool(comment.match(val)) sections = [] for is_doc, group in itertools.groupby(lines, section_test): if is_doc: - text = [comment.sub('', i).rstrip('\r\n') for i in group] + text = [comment.sub("", i).rstrip("\r\n") for i in group] else: - text = [i.rstrip('\r\n') for i in group] + text = [i.rstrip("\r\n") for i in group] sections.append((is_doc, text)) @@ -79,33 +75,33 @@ class LiterateCoding(Directive): return [document.reporter.warning(str(exc), line=self.lineno)] node = nodes.container() - node['classes'] = ['lit-container'] + node["classes"] = ["lit-container"] node.document = self.state.document enum = nodes.enumerated_list() - enum['classes'] = ['lit-docs'] + enum["classes"] = ["lit-docs"] node.append(enum) # make first list item list_item = nodes.list_item() - list_item['classes'] = ['lit-item'] + list_item["classes"] = ["lit-item"] for is_doc, line in lines: - if is_doc and line == ['']: + if is_doc and line == [""]: continue section = nodes.section() if is_doc: - section['classes'] = ['lit-annotation'] + section["classes"] = ["lit-annotation"] nested_parse_with_titles(self.state, ViewList(line), section) else: - section['classes'] = ['lit-content'] + section["classes"] = ["lit-content"] - code = '\n'.join(line) + code = "\n".join(line) literal = nodes.literal_block(code, code) - literal['language'] = 'yaml' + literal["language"] = "yaml" set_source_info(self, literal) section.append(literal) @@ -116,42 +112,41 @@ class LiterateCoding(Directive): if len(list_item.children) == 2: enum.append(list_item) list_item = nodes.list_item() - list_item['classes'] = ['lit-item'] + list_item["classes"] = ["lit-item"] # Non-semantic div for styling bg = nodes.container() - bg['classes'] = ['lit-background'] + bg["classes"] = ["lit-background"] node.append(bg) return [node] class LiterateFormula(LiterateCoding): - ''' + """ Customizations to handle finding and parsing SLS files - ''' + """ def parse_file(self, sls_path): - ''' + """ Given a typical Salt SLS path (e.g.: apache.vhosts.standard), find the file on the file system and parse it - ''' + """ config = self.state.document.settings.env.config formulas_dirs = config.formulas_dirs - fpath = sls_path.replace('.', '/') + fpath = sls_path.replace(".", "/") - name_options = ( - '{0}.sls'.format(fpath), - os.path.join(fpath, 'init.sls') - ) + name_options = ("{0}.sls".format(fpath), os.path.join(fpath, "init.sls")) - paths = [os.path.join(fdir, fname) - for fname in name_options - for fdir in formulas_dirs] + paths = [ + os.path.join(fdir, fname) + for fname in name_options + for fdir in formulas_dirs + ] for i in paths: try: - with open(i, 'rb') as f: + with open(i, "rb") as f: return f.readlines() except IOError: pass @@ -160,7 +155,7 @@ class LiterateFormula(LiterateCoding): class CurrentFormula(Directive): - domain = 'salt' + domain = "salt" has_content = False required_arguments = 1 optional_arguments = 0 @@ -170,15 +165,15 @@ class CurrentFormula(Directive): def run(self): env = self.state.document.settings.env modname = self.arguments[0].strip() - if modname == 'None': - env.temp_data['salt:formula'] = None + if modname == "None": + env.temp_data["salt:formula"] = None else: - env.temp_data['salt:formula'] = modname + env.temp_data["salt:formula"] = modname return [] class Formula(Directive): - domain = 'salt' + domain = "salt" has_content = True required_arguments = 1 @@ -186,30 +181,31 @@ class Formula(Directive): env = self.state.document.settings.env formname = self.arguments[0].strip() - env.temp_data['salt:formula'] = formname + env.temp_data["salt:formula"] = formname - if 'noindex' in self.options: + if "noindex" in self.options: return [] - env.domaindata['salt']['formulas'][formname] = ( - env.docname, - self.options.get('synopsis', ''), - self.options.get('platform', ''), - 'deprecated' in self.options) + env.domaindata["salt"]["formulas"][formname] = ( + env.docname, + self.options.get("synopsis", ""), + self.options.get("platform", ""), + "deprecated" in self.options, + ) - targetnode = nodes.target('', '', ids=['module-' + formname], - ismod=True) + targetnode = nodes.target("", "", ids=["module-" + formname], ismod=True) self.state.document.note_explicit_target(targetnode) - indextext = u'{0}-formula)'.format(formname) - inode = addnodes.index(entries=[('single', indextext, - 'module-' + formname, '')]) + indextext = u"{0}-formula)".format(formname) + inode = addnodes.index( + entries=[("single", indextext, "module-" + formname, "")] + ) return [targetnode, inode] class State(Directive): - domain = 'salt' + domain = "salt" has_content = True required_arguments = 1 @@ -217,19 +213,18 @@ class State(Directive): env = self.state.document.settings.env statename = self.arguments[0].strip() - if 'noindex' in self.options: + if "noindex" in self.options: return [] - targetnode = nodes.target('', '', ids=['module-' + statename], - ismod=True) + targetnode = nodes.target("", "", ids=["module-" + statename], ismod=True) self.state.document.note_explicit_target(targetnode) - formula = env.temp_data.get('salt:formula') + formula = env.temp_data.get("salt:formula") - indextext = u'{1} ({0}-formula)'.format(formula, statename) - inode = addnodes.index(entries=[ - ('single', indextext, 'module-{0}'.format(statename), ''), - ]) + indextext = u"{1} ({0}-formula)".format(formula, statename) + inode = addnodes.index( + entries=[("single", indextext, "module-{0}".format(statename), ""),] + ) return [targetnode, inode] @@ -239,55 +234,56 @@ class SLSXRefRole(XRefRole): class SaltModuleIndex(python_domain.PythonModuleIndex): - name = 'modindex' - localname = _('Salt Module Index') - shortname = _('all salt modules') + name = "modindex" + localname = _("Salt Module Index") + shortname = _("all salt modules") class SaltDomain(python_domain.PythonDomain): - name = 'salt' - label = 'Salt' + name = "salt" + label = "Salt" data_version = 2 object_types = python_domain.PythonDomain.object_types - object_types.update({ - 'state': ObjType(_('state'), 'state'), - }) + object_types.update( + {"state": ObjType(_("state"), "state"),} + ) directives = python_domain.PythonDomain.directives - directives.update({ - 'event': Event, - 'state': State, - 'formula': LiterateFormula, - 'currentformula': CurrentFormula, - 'saltconfig': LiterateCoding, - }) - + directives.update( + { + "event": Event, + "state": State, + "formula": LiterateFormula, + "currentformula": CurrentFormula, + "saltconfig": LiterateCoding, + } + ) roles = python_domain.PythonDomain.roles - roles.update({ - 'formula': SLSXRefRole(), - }) + roles.update( + {"formula": SLSXRefRole(),} + ) initial_data = python_domain.PythonDomain.initial_data - initial_data.update({ - 'formulas': {}, - }) + initial_data.update( + {"formulas": {},} + ) indices = [ SaltModuleIndex, ] - def resolve_xref(self, env, fromdocname, builder, type, target, node, - contnode): - if type == 'formula' and target in self.data['formulas']: - doc, _, _, _ = self.data['formulas'].get(target, (None, None)) + def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): + if type == "formula" and target in self.data["formulas"]: + doc, _, _, _ = self.data["formulas"].get(target, (None, None)) if doc: - return make_refnode(builder, fromdocname, doc, target, - contnode, target) + return make_refnode(builder, fromdocname, doc, target, contnode, target) else: - super(SaltDomain, self).resolve_xref(env, fromdocname, builder, - type, target, node, contnode) + super(SaltDomain, self).resolve_xref( + env, fromdocname, builder, type, target, node, contnode + ) + # Monkey-patch the Python domain remove the python module index python_domain.PythonDomain.indices = [SaltModuleIndex] @@ -296,18 +292,34 @@ python_domain.PythonDomain.indices = [SaltModuleIndex] def setup(app): app.add_domain(SaltDomain) - formulas_path = 'templates/formulas' - formulas_dir = os.path.join(os.path.abspath(os.path.dirname(salt.__file__)), - formulas_path) - app.add_config_value('formulas_dirs', [formulas_dir], 'env') + formulas_path = "templates/formulas" + formulas_dir = os.path.join( + os.path.abspath(os.path.dirname(salt.__file__)), formulas_path + ) + app.add_config_value("formulas_dirs", [formulas_dir], "env") - app.add_crossref_type(directivename="conf_master", rolename="conf_master", - indextemplate="pair: %s; conf/master") - app.add_crossref_type(directivename="conf_minion", rolename="conf_minion", - indextemplate="pair: %s; conf/minion") - app.add_crossref_type(directivename="conf_proxy", rolename="conf_proxy", - indextemplate="pair: %s; conf/proxy") - app.add_crossref_type(directivename="conf_log", rolename="conf_log", - indextemplate="pair: %s; conf/logging") - app.add_crossref_type(directivename="jinja_ref", rolename="jinja_ref", - indextemplate="pair: %s; jinja filters") + app.add_crossref_type( + directivename="conf_master", + rolename="conf_master", + indextemplate="pair: %s; conf/master", + ) + app.add_crossref_type( + directivename="conf_minion", + rolename="conf_minion", + indextemplate="pair: %s; conf/minion", + ) + app.add_crossref_type( + directivename="conf_proxy", + rolename="conf_proxy", + indextemplate="pair: %s; conf/proxy", + ) + app.add_crossref_type( + directivename="conf_log", + rolename="conf_log", + indextemplate="pair: %s; conf/logging", + ) + app.add_crossref_type( + directivename="jinja_ref", + rolename="jinja_ref", + indextemplate="pair: %s; jinja filters", + ) diff --git a/doc/_ext/saltrepo.py b/doc/_ext/saltrepo.py index 524e0757e33..fd75ae21cee 100644 --- a/doc/_ext/saltrepo.py +++ b/doc/_ext/saltrepo.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" saltrepo ~~~~~~~~ SaltStack Repository Sphinx directives -''' +""" + def source_read_handler(app, docname, source): - if '|repo_primary_branch|' in source[0]: + if "|repo_primary_branch|" in source[0]: source[0] = source[0].replace( - '|repo_primary_branch|', - app.config.html_context['repo_primary_branch'] + "|repo_primary_branch|", app.config.html_context["repo_primary_branch"] ) def setup(app): - app.connect('source-read', source_read_handler) + app.connect("source-read", source_read_handler) return { - 'version': 'builtin', - 'parallel_read_safe': True, - 'parallel_write_safe': True, + "version": "builtin", + "parallel_read_safe": True, + "parallel_write_safe": True, } diff --git a/doc/_ext/shorturls.py b/doc/_ext/shorturls.py index 753aee5fc6a..3100c1a29a4 100644 --- a/doc/_ext/shorturls.py +++ b/doc/_ext/shorturls.py @@ -1,22 +1,24 @@ -''' +""" Short-URL redirects -''' +""" import json import os import sphinx.ext.intersphinx -DOCS_URL = 'http://docs.saltstack.com/en/latest/' +DOCS_URL = "http://docs.saltstack.com/en/latest/" + def write_urls_index(app, exc): - ''' + """ Generate a JSON file to serve as an index for short-URL lookups - ''' - inventory = os.path.join(app.builder.outdir, 'objects.inv') + """ + inventory = os.path.join(app.builder.outdir, "objects.inv") objects = sphinx.ext.intersphinx.fetch_inventory(app, DOCS_URL, inventory) - with open(os.path.join(app.builder.outdir, 'shorturls.json'), 'w') as f: + with open(os.path.join(app.builder.outdir, "shorturls.json"), "w") as f: json.dump(objects, f) + def setup(app): - app.connect('build-finished', write_urls_index) + app.connect("build-finished", write_urls_index) diff --git a/doc/_ext/youtube.py b/doc/_ext/youtube.py index 3340b99899e..35be8c1c78a 100644 --- a/doc/_ext/youtube.py +++ b/doc/_ext/youtube.py @@ -36,8 +36,10 @@ from __future__ import division import re + from docutils import nodes from docutils.parsers.rst import directives + try: from sphinx.util.compat import Directive except ImportError: @@ -140,7 +142,9 @@ class YouTube(Directive): aspect = None width = get_size(self.options, "width") height = get_size(self.options, "height") - return [youtube(id=self.arguments[0], aspect=aspect, width=width, height=height)] + return [ + youtube(id=self.arguments[0], aspect=aspect, width=width, height=height) + ] def setup(app): diff --git a/doc/conf.py b/doc/conf.py index f5f53435d6c..d9ac2b934ae 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,19 +1,19 @@ # -*- coding: utf-8 -*- # pylint: disable=C0103,W0622 -''' +""" Sphinx documentation for Salt -''' -import sys +""" import os import re -import types +import sys import time +import types -from sphinx.directives import TocTree +from sphinx.directives.other import TocTree class Mock(object): - ''' + """ Mock out specified imports. This allows autodoc to do its thing without having oodles of req'd @@ -22,8 +22,11 @@ class Mock(object): This Mock class can be configured to return a specific values at specific names, if required. http://read-the-docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules - ''' - def __init__(self, mapping=None, *args, **kwargs): # pylint: disable=unused-argument + """ + + def __init__( + self, mapping=None, *args, **kwargs + ): # pylint: disable=unused-argument """ Mapping allows autodoc to bypass the Mock object, but actually assign a specific value, expected by a specific attribute returned. @@ -41,9 +44,9 @@ class Mock(object): def __getattr__(self, name): if name in self.__mapping: data = self.__mapping.get(name) - elif name in ('__file__', '__path__'): - data = '/dev/null' - elif name in ('__mro_entries__', '__qualname__'): + elif name in ("__file__", "__path__"): + data = "/dev/null" + elif name in ("__mro_entries__", "__qualname__"): raise AttributeError("'Mock' object has no attribute '%s'" % (name)) else: data = Mock(mapping=self.__mapping) @@ -61,7 +64,7 @@ class Mock(object): def mock_decorator_with_params(*oargs, **okwargs): # pylint: disable=unused-argument - ''' + """ Optionally mock a decorator that takes parameters E.g.: @@ -69,153 +72,144 @@ def mock_decorator_with_params(*oargs, **okwargs): # pylint: disable=unused-arg @blah(stuff=True) def things(): pass - ''' + """ + def inner(fn, *iargs, **ikwargs): # pylint: disable=unused-argument - if hasattr(fn, '__call__'): + if hasattr(fn, "__call__"): return fn return Mock() + return inner MOCK_MODULES = [ # Python stdlib - 'user', - + "user", # salt core - 'concurrent', - 'Crypto', - 'Crypto.Signature', - 'Crypto.Cipher', - 'Crypto.Hash', - 'Crypto.PublicKey', - 'Crypto.Random', - 'Crypto.Signature', - 'Crypto.Signature.PKCS1_v1_5', - 'M2Crypto', - 'msgpack', - 'yaml', - 'yaml.constructor', - 'yaml.nodes', - 'yaml.parser', - 'yaml.scanner', - 'zmq', - 'zmq.eventloop', - 'zmq.eventloop.ioloop', - + "Crypto", + "Crypto.Signature", + "Crypto.Cipher", + "Crypto.Hash", + "Crypto.PublicKey", + "Crypto.Random", + "Crypto.Signature", + "Crypto.Signature.PKCS1_v1_5", + "M2Crypto", + "msgpack", + "yaml", + "yaml.constructor", + "yaml.nodes", + "yaml.parser", + "yaml.scanner", + "zmq", + "zmq.eventloop", + "zmq.eventloop.ioloop", # third-party libs for cloud modules - 'libcloud', - 'libcloud.compute', - 'libcloud.compute.base', - 'libcloud.compute.deployment', - 'libcloud.compute.providers', - 'libcloud.compute.types', - 'libcloud.loadbalancer', - 'libcloud.loadbalancer.types', - 'libcloud.loadbalancer.providers', - 'libcloud.common', - 'libcloud.common.google', - + "libcloud", + "libcloud.compute", + "libcloud.compute.base", + "libcloud.compute.deployment", + "libcloud.compute.providers", + "libcloud.compute.types", + "libcloud.loadbalancer", + "libcloud.loadbalancer.types", + "libcloud.loadbalancer.providers", + "libcloud.common", + "libcloud.common.google", # third-party libs for netapi modules - 'cherrypy', - 'cherrypy.lib', - 'cherrypy.process', - 'cherrypy.wsgiserver', - 'cherrypy.wsgiserver.ssl_builtin', - - 'tornado', - 'tornado.concurrent', - 'tornado.escape', - 'tornado.gen', - 'tornado.httpclient', - 'tornado.httpserver', - 'tornado.httputil', - 'tornado.ioloop', - 'tornado.iostream', - 'tornado.netutil', - 'tornado.simple_httpclient', - 'tornado.stack_context', - 'tornado.web', - 'tornado.websocket', - 'tornado.locks', - - 'ws4py', - 'ws4py.server', - 'ws4py.server.cherrypyserver', - 'ws4py.websocket', - + "cherrypy", + "cherrypy.lib", + "cherrypy.process", + "cherrypy.wsgiserver", + "cherrypy.wsgiserver.ssl_builtin", + "tornado", + "tornado.concurrent", + "tornado.escape", + "tornado.gen", + "tornado.httpclient", + "tornado.httpserver", + "tornado.httputil", + "tornado.ioloop", + "tornado.iostream", + "tornado.netutil", + "tornado.simple_httpclient", + "tornado.stack_context", + "tornado.web", + "tornado.websocket", + "tornado.locks", + "ws4py", + "ws4py.server", + "ws4py.server.cherrypyserver", + "ws4py.websocket", # modules, renderers, states, returners, et al - 'ClusterShell', - 'ClusterShell.NodeSet', - 'MySQLdb', - 'MySQLdb.cursors', - 'OpenSSL', - 'avahi', - 'boto.regioninfo', - 'concurrent', - 'dbus', - 'django', - 'dns', - 'dns.resolver', - 'dson', - 'hjson', - 'jnpr', - 'jnpr.junos', - 'jnpr.junos.utils', - 'jnpr.junos.utils.config', - 'jnpr.junos.utils.sw', - 'keyring', - 'libvirt', - 'lxml', - 'lxml.etree', - 'msgpack', - 'nagios_json', - 'napalm', - 'netaddr', - 'netaddr.IPAddress', - 'netaddr.core', - 'netaddr.core.AddrFormatError', - 'ntsecuritycon', - 'psutil', - 'pycassa', - 'pyconnman', - 'pyiface', - 'pymongo', - 'pyroute2', - 'pyroute2.ipdb', - 'rabbitmq_server', - 'redis', - 'rpm', - 'rpmUtils', - 'rpmUtils.arch', - 'salt.ext.six.moves.winreg', - 'twisted', - 'twisted.internet', - 'twisted.internet.protocol', - 'twisted.internet.protocol.DatagramProtocol', - 'win32security', - 'yum', - 'zfs', + "ClusterShell", + "ClusterShell.NodeSet", + "MySQLdb", + "MySQLdb.cursors", + "OpenSSL", + "avahi", + "boto.regioninfo", + "dbus", + "django", + "dns", + "dns.resolver", + "dson", + "hjson", + "jnpr", + "jnpr.junos", + "jnpr.junos.utils", + "jnpr.junos.utils.config", + "jnpr.junos.utils.sw", + "keyring", + "libvirt", + "lxml", + "lxml.etree", + "msgpack", + "nagios_json", + "napalm", + "netaddr", + "netaddr.IPAddress", + "netaddr.core", + "netaddr.core.AddrFormatError", + "ntsecuritycon", + "psutil", + "pycassa", + "pyconnman", + "pyiface", + "pymongo", + "pyroute2", + "pyroute2.ipdb", + "rabbitmq_server", + "redis", + "rpm", + "rpmUtils", + "rpmUtils.arch", + "salt.ext.six.moves.winreg", + "twisted", + "twisted.internet", + "twisted.internet.protocol", + "twisted.internet.protocol.DatagramProtocol", + "win32security", + "yum", + "zfs", ] MOCK_MODULES_MAPPING = { - 'cherrypy': {'config': mock_decorator_with_params}, - 'ntsecuritycon': { - 'STANDARD_RIGHTS_REQUIRED': 0, - 'SYNCHRONIZE': 0, - }, - 'psutil': {'total': 0}, # Otherwise it will crash Sphinx + "cherrypy": {"config": mock_decorator_with_params}, + "ntsecuritycon": {"STANDARD_RIGHTS_REQUIRED": 0, "SYNCHRONIZE": 0,}, + "psutil": {"total": 0}, # Otherwise it will crash Sphinx } for mod_name in MOCK_MODULES: sys.modules[mod_name] = Mock(mapping=MOCK_MODULES_MAPPING.get(mod_name)) # Define a fake version attribute for the following libs. -sys.modules['libcloud'].__version__ = '0.0.0' -sys.modules['msgpack'].version = (1, 0, 0) -sys.modules['psutil'].version_info = (3, 0, 0) -sys.modules['pymongo'].version = '0.0.0' -sys.modules['tornado'].version_info = (0, 0, 0) -sys.modules['boto.regioninfo']._load_json_file = {'endpoints': None} +sys.modules["libcloud"].__version__ = "0.0.0" +sys.modules["msgpack"].version = (1, 0, 0) +sys.modules["psutil"].version_info = (3, 0, 0) +sys.modules["pymongo"].version = "0.0.0" +sys.modules["tornado"].version_info = (0, 0, 0) +sys.modules["boto.regioninfo"]._load_json_file = {"endpoints": None} # -- Add paths to PYTHONPATH --------------------------------------------------- @@ -224,102 +218,114 @@ try: except NameError: # sphinx-intl and six execute some code which will raise this NameError # assume we're in the doc/ directory - docs_basepath = os.path.abspath(os.path.dirname('.')) + docs_basepath = os.path.abspath(os.path.dirname(".")) addtl_paths = ( os.pardir, # salt itself (for autodoc) - '_ext', # custom Sphinx extensions + "_ext", # custom Sphinx extensions ) for addtl_path in addtl_paths: sys.path.insert(0, os.path.abspath(os.path.join(docs_basepath, addtl_path))) - # We're now able to import salt -import salt.version +import salt.version # isort:skip - -formulas_dir = os.path.join(os.pardir, docs_basepath, 'formulas') +formulas_dir = os.path.join(os.pardir, docs_basepath, "formulas") # ----- Intersphinx Settings ------------------------------------------------> -intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None) -} +intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} # <---- Intersphinx Settings ------------------------------------------------- # -- General Configuration ----------------------------------------------------- # Set a var if we're building docs for the live site or not -on_saltstack = 'SALT_ON_SALTSTACK' in os.environ +on_saltstack = "SALT_ON_SALTSTACK" in os.environ -project = 'Salt' -repo_primary_branch = 'master' # This is the default branch on GitHub for the Salt project +project = "Salt" +repo_primary_branch = ( + "master" # This is the default branch on GitHub for the Salt project +) version = salt.version.__version__ -latest_release = os.environ.get('LATEST_RELEASE', 'latest_release') # latest release (2019.2.3) -previous_release = os.environ.get('PREVIOUS_RELEASE', 'previous_release') # latest release from previous branch (2018.3.5) -previous_release_dir = os.environ.get('PREVIOUS_RELEASE_DIR', 'previous_release_dir') # path on web server for previous branch (2018.3) -next_release = '' # next release -next_release_dir = '' # path on web server for next release branch +latest_release = os.environ.get( + "LATEST_RELEASE", "latest_release" +) # latest release (2019.2.3) +previous_release = os.environ.get( + "PREVIOUS_RELEASE", "previous_release" +) # latest release from previous branch (2018.3.5) +previous_release_dir = os.environ.get( + "PREVIOUS_RELEASE_DIR", "previous_release_dir" +) # path on web server for previous branch (2018.3) +next_release = "" # next release +next_release_dir = "" # path on web server for next release branch -today = '' -copyright = '' +today = "" +copyright = "" if on_saltstack: - today = "Generated on " + time.strftime("%B %d, %Y") + " at " + time.strftime("%X %Z") + "." + today = ( + "Generated on " + + time.strftime("%B %d, %Y") + + " at " + + time.strftime("%X %Z") + + "." + ) copyright = time.strftime("%Y") # < --- START do not merge these settings to other branches START ---> # -build_type = os.environ.get('BUILD_TYPE', repo_primary_branch) # latest, previous, master, next +build_type = os.environ.get( + "BUILD_TYPE", repo_primary_branch +) # latest, previous, master, next # < --- END do not merge these settings to other branches END ---> # # Set google custom search engine if build_type == repo_primary_branch: release = latest_release - search_cx = '011515552685726825874:v1had6i279q' # master - #search_cx = '011515552685726825874:x17j5zl74g8' # develop -elif build_type == 'next': + search_cx = "011515552685726825874:v1had6i279q" # master + # search_cx = '011515552685726825874:x17j5zl74g8' # develop +elif build_type == "next": release = next_release - search_cx = '011515552685726825874:ht0p8miksrm' # latest -elif build_type == 'previous': + search_cx = "011515552685726825874:ht0p8miksrm" # latest +elif build_type == "previous": release = previous_release - if release.startswith('3000'): - search_cx = '011515552685726825874:3skhaozjtyn' # 3000 - elif release.startswith('2019.2'): - search_cx = '011515552685726825874:huvjhlpptnm' # 2019.2 - elif release.startswith('2018.3'): - search_cx = '011515552685726825874:vadptdpvyyu' # 2018.3 - elif release.startswith('2017.7'): - search_cx = '011515552685726825874:w-hxmnbcpou' # 2017.7 - elif release.startswith('2016.11'): - search_cx = '011515552685726825874:dlsj745pvhq' # 2016.11 + if release.startswith("3000"): + search_cx = "011515552685726825874:3skhaozjtyn" # 3000 + elif release.startswith("2019.2"): + search_cx = "011515552685726825874:huvjhlpptnm" # 2019.2 + elif release.startswith("2018.3"): + search_cx = "011515552685726825874:vadptdpvyyu" # 2018.3 + elif release.startswith("2017.7"): + search_cx = "011515552685726825874:w-hxmnbcpou" # 2017.7 + elif release.startswith("2016.11"): + search_cx = "011515552685726825874:dlsj745pvhq" # 2016.11 else: - search_cx = '011515552685726825874:ht0p8miksrm' # latest -else: # latest or something else + search_cx = "011515552685726825874:ht0p8miksrm" # latest +else: # latest or something else release = latest_release - search_cx = '011515552685726825874:ht0p8miksrm' # latest + search_cx = "011515552685726825874:ht0p8miksrm" # latest -needs_sphinx = '1.3' +needs_sphinx = "1.3" -spelling_lang = 'en_US' -language = 'en' +spelling_lang = "en_US" +language = "en" locale_dirs = [ - '_locale', + "_locale", ] -master_doc = 'contents' -templates_path = ['_templates'] -exclude_patterns = ['_build', '_incl/*', 'ref/cli/_includes/*.rst'] +master_doc = "contents" +templates_path = ["_templates"] +exclude_patterns = ["_build", "_incl/*", "ref/cli/_includes/*.rst"] extensions = [ - 'saltdomain', # Must come early - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon', - 'sphinx.ext.autosummary', - 'sphinx.ext.extlinks', - 'sphinx.ext.intersphinx', - 'httpdomain', - 'youtube', - 'saltrepo' + "saltdomain", # Must come early + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.autosummary", + "sphinx.ext.extlinks", + "sphinx.ext.intersphinx", + "httpdomain", + "youtube", + "saltrepo" #'saltautodoc', # Must be AFTER autodoc #'shorturls', ] @@ -329,14 +335,14 @@ try: except ImportError: pass else: - extensions += ['sphinxcontrib.spelling'] + extensions += ["sphinxcontrib.spelling"] -modindex_common_prefix = ['salt.'] +modindex_common_prefix = ["salt."] autosummary_generate = True # strip git rev as there won't necessarily be a release based on it -stripped_release = re.sub(r'-\d+-g[0-9a-f]+$', '', release) +stripped_release = re.sub(r"-\d+-g[0-9a-f]+$", "", release) # Define a substitution for linking to the latest release tarball rst_prolog = """\ @@ -374,87 +380,91 @@ rst_prolog = """\

x86_64: salt-{release}-py3-x86_64.pkg | md5

-""".format(release=stripped_release) +""".format( + release=stripped_release +) # A shortcut for linking to tickets on the GitHub issue tracker extlinks = { - 'blob': ('https://github.com/saltstack/salt/blob/%s/%%s' % repo_primary_branch, None), - 'issue': ('https://github.com/saltstack/salt/issues/%s', 'issue #'), - 'pull': ('https://github.com/saltstack/salt/pull/%s', 'PR #'), - 'formula_url': ('https://github.com/saltstack-formulas/%s', ''), + "blob": ( + "https://github.com/saltstack/salt/blob/%s/%%s" % repo_primary_branch, + None, + ), + "issue": ("https://github.com/saltstack/salt/issues/%s", "issue #"), + "pull": ("https://github.com/saltstack/salt/pull/%s", "PR #"), + "formula_url": ("https://github.com/saltstack-formulas/%s", ""), } # ----- Localization --------------------------------------------------------> -locale_dirs = ['locale/'] +locale_dirs = ["locale/"] gettext_compact = False # <---- Localization --------------------------------------------------------- ### HTML options # set 'HTML_THEME=saltstack' to use previous theme -html_theme = os.environ.get('HTML_THEME', 'saltstack2') -html_theme_path = ['_themes'] -html_title = u'' -html_short_title = 'Salt' +html_theme = os.environ.get("HTML_THEME", "saltstack2") +html_theme_path = ["_themes"] +html_title = u"" +html_short_title = "Salt" -html_static_path = ['_static'] -html_logo = None # specified in the theme layout.html -html_favicon = 'favicon.ico' +html_static_path = ["_static"] +html_logo = None # specified in the theme layout.html +html_favicon = "favicon.ico" smartquotes = False # Use Google customized search or use Sphinx built-in JavaScript search if on_saltstack: - html_search_template = 'googlesearch.html' + html_search_template = "googlesearch.html" else: - html_search_template = 'searchbox.html' + html_search_template = "searchbox.html" html_additional_pages = { - '404': '404.html', + "404": "404.html", } html_default_sidebars = [ html_search_template, - 'version.html', - 'localtoc.html', - 'relations.html', - 'sourcelink.html', - 'saltstack.html', + "version.html", + "localtoc.html", + "relations.html", + "sourcelink.html", + "saltstack.html", ] html_sidebars = { - 'ref/**/all/salt.*': [ + "ref/**/all/salt.*": [ html_search_template, - 'version.html', - 'modules-sidebar.html', - 'localtoc.html', - 'relations.html', - 'sourcelink.html', - 'saltstack.html', - ], - 'ref/formula/all/*': [ + "version.html", + "modules-sidebar.html", + "localtoc.html", + "relations.html", + "sourcelink.html", + "saltstack.html", ], + "ref/formula/all/*": [], } html_context = { - 'on_saltstack': on_saltstack, - 'html_default_sidebars': html_default_sidebars, - 'github_base': 'https://github.com/saltstack/salt', - 'github_issues': 'https://github.com/saltstack/salt/issues', - 'github_downloads': 'https://github.com/saltstack/salt/downloads', - 'latest_release': latest_release, - 'previous_release': previous_release, - 'previous_release_dir': previous_release_dir, - 'next_release': next_release, - 'next_release_dir': next_release_dir, - 'search_cx': search_cx, - 'build_type': build_type, - 'today': today, - 'copyright': copyright, - 'repo_primary_branch': repo_primary_branch + "on_saltstack": on_saltstack, + "html_default_sidebars": html_default_sidebars, + "github_base": "https://github.com/saltstack/salt", + "github_issues": "https://github.com/saltstack/salt/issues", + "github_downloads": "https://github.com/saltstack/salt/downloads", + "latest_release": latest_release, + "previous_release": previous_release, + "previous_release_dir": previous_release_dir, + "next_release": next_release, + "next_release_dir": next_release_dir, + "search_cx": search_cx, + "build_type": build_type, + "today": today, + "copyright": copyright, + "repo_primary_branch": repo_primary_branch, } html_use_index = True -html_last_updated_fmt = '%b %d, %Y' +html_last_updated_fmt = "%b %d, %Y" html_show_sourcelink = False html_show_sphinx = True html_show_copyright = True @@ -462,20 +472,20 @@ html_show_copyright = True ### Latex options latex_documents = [ - ('contents', 'Salt.tex', 'Salt Documentation', 'SaltStack, Inc.', 'manual'), + ("contents", "Salt.tex", "Salt Documentation", "SaltStack, Inc.", "manual"), ] -latex_logo = '_static/salt-logo.png' +latex_logo = "_static/salt-logo.png" latex_elements = { - 'inputenc': '', # use XeTeX instead of the inputenc LaTeX package. - 'utf8extra': '', - 'preamble': r''' + "inputenc": "", # use XeTeX instead of the inputenc LaTeX package. + "utf8extra": "", + "preamble": r""" \usepackage{fontspec} \setsansfont{Linux Biolinum O} \setromanfont{Linux Libertine O} \setmonofont{Source Code Pro} -''', +""", } ### Linux Biolinum, Linux Libertine: http://www.linuxlibertine.org/ ### Source Code Pro: https://github.com/adobe-fonts/source-code-pro/releases @@ -483,34 +493,34 @@ latex_elements = { ### Linkcheck options linkcheck_ignore = [ - r'http://127.0.0.1', - r'http://salt:\d+', - r'http://local:\d+', - r'https://console.aws.amazon.com', - r'http://192.168.33.10', - r'http://domain:\d+', - r'http://123.456.789.012:\d+', - r'http://localhost', - r'https://groups.google.com/forum/#!forum/salt-users', - r'http://logstash.net/docs/latest/inputs/udp', - r'http://logstash.net/docs/latest/inputs/zeromq', - r'http://www.youtube.com/saltstack', - r'https://raven.readthedocs.io', - r'https://getsentry.com', - r'https://salt-cloud.readthedocs.io', - r'https://salt.readthedocs.io', - r'http://www.pip-installer.org/', - r'http://www.windowsazure.com/', - r'https://github.com/watching', - r'dash-feed://', - r'https://github.com/saltstack/salt/', - r'http://bootstrap.saltstack.org', - r'https://bootstrap.saltstack.com', - r'https://raw.githubusercontent.com/saltstack/salt-bootstrap/stable/bootstrap-salt.sh', - r'media.readthedocs.org/dash/salt/latest/salt.xml', - r'https://portal.aws.amazon.com/gp/aws/securityCredentials', - r'https://help.github.com/articles/fork-a-repo', - r'dash-feed://https%3A//media.readthedocs.org/dash/salt/latest/salt.xml', + r"http://127.0.0.1", + r"http://salt:\d+", + r"http://local:\d+", + r"https://console.aws.amazon.com", + r"http://192.168.33.10", + r"http://domain:\d+", + r"http://123.456.789.012:\d+", + r"http://localhost", + r"https://groups.google.com/forum/#!forum/salt-users", + r"http://logstash.net/docs/latest/inputs/udp", + r"http://logstash.net/docs/latest/inputs/zeromq", + r"http://www.youtube.com/saltstack", + r"https://raven.readthedocs.io", + r"https://getsentry.com", + r"https://salt-cloud.readthedocs.io", + r"https://salt.readthedocs.io", + r"http://www.pip-installer.org/", + r"http://www.windowsazure.com/", + r"https://github.com/watching", + r"dash-feed://", + r"https://github.com/saltstack/salt/", + r"http://bootstrap.saltstack.org", + r"https://bootstrap.saltstack.com", + r"https://raw.githubusercontent.com/saltstack/salt-bootstrap/stable/bootstrap-salt.sh", + r"media.readthedocs.org/dash/salt/latest/salt.xml", + r"https://portal.aws.amazon.com/gp/aws/securityCredentials", + r"https://help.github.com/articles/fork-a-repo", + r"dash-feed://https%3A//media.readthedocs.org/dash/salt/latest/salt.xml", ] linkcheck_anchors = False @@ -519,53 +529,53 @@ linkcheck_anchors = False # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). authors = [ - 'Thomas S. Hatch and many others, please see the Authors file', + "Thomas S. Hatch and many others, please see the Authors file", ] man_pages = [ - ('contents', 'salt', 'Salt Documentation', authors, 7), - ('ref/cli/salt', 'salt', 'salt', authors, 1), - ('ref/cli/salt-master', 'salt-master', 'salt-master Documentation', authors, 1), - ('ref/cli/salt-minion', 'salt-minion', 'salt-minion Documentation', authors, 1), - ('ref/cli/salt-key', 'salt-key', 'salt-key Documentation', authors, 1), - ('ref/cli/salt-cp', 'salt-cp', 'salt-cp Documentation', authors, 1), - ('ref/cli/salt-call', 'salt-call', 'salt-call Documentation', authors, 1), - ('ref/cli/salt-proxy', 'salt-proxy', 'salt-proxy Documentation', authors, 1), - ('ref/cli/salt-syndic', 'salt-syndic', 'salt-syndic Documentation', authors, 1), - ('ref/cli/salt-run', 'salt-run', 'salt-run Documentation', authors, 1), - ('ref/cli/salt-ssh', 'salt-ssh', 'salt-ssh Documentation', authors, 1), - ('ref/cli/salt-cloud', 'salt-cloud', 'Salt Cloud Command', authors, 1), - ('ref/cli/salt-api', 'salt-api', 'salt-api Command', authors, 1), - ('ref/cli/salt-unity', 'salt-unity', 'salt-unity Command', authors, 1), - ('ref/cli/spm', 'spm', 'Salt Package Manager Command', authors, 1), + ("contents", "salt", "Salt Documentation", authors, 7), + ("ref/cli/salt", "salt", "salt", authors, 1), + ("ref/cli/salt-master", "salt-master", "salt-master Documentation", authors, 1), + ("ref/cli/salt-minion", "salt-minion", "salt-minion Documentation", authors, 1), + ("ref/cli/salt-key", "salt-key", "salt-key Documentation", authors, 1), + ("ref/cli/salt-cp", "salt-cp", "salt-cp Documentation", authors, 1), + ("ref/cli/salt-call", "salt-call", "salt-call Documentation", authors, 1), + ("ref/cli/salt-proxy", "salt-proxy", "salt-proxy Documentation", authors, 1), + ("ref/cli/salt-syndic", "salt-syndic", "salt-syndic Documentation", authors, 1), + ("ref/cli/salt-run", "salt-run", "salt-run Documentation", authors, 1), + ("ref/cli/salt-ssh", "salt-ssh", "salt-ssh Documentation", authors, 1), + ("ref/cli/salt-cloud", "salt-cloud", "Salt Cloud Command", authors, 1), + ("ref/cli/salt-api", "salt-api", "salt-api Command", authors, 1), + ("ref/cli/salt-unity", "salt-unity", "salt-unity Command", authors, 1), + ("ref/cli/spm", "spm", "Salt Package Manager Command", authors, 1), ] ### epub options -epub_title = 'Salt Documentation' -epub_author = 'SaltStack, Inc.' +epub_title = "Salt Documentation" +epub_author = "SaltStack, Inc." epub_publisher = epub_author epub_copyright = copyright -epub_scheme = 'URL' -epub_identifier = 'http://saltstack.com/' +epub_scheme = "URL" +epub_identifier = "http://saltstack.com/" epub_tocdup = False -#epub_tocdepth = 3 +# epub_tocdepth = 3 def skip_mod_init_member(app, what, name, obj, skip, options): # pylint: disable=too-many-arguments,unused-argument - if name.startswith('_'): + if name.startswith("_"): return True - if isinstance(obj, types.FunctionType) and obj.__name__ == 'mod_init': + if isinstance(obj, types.FunctionType) and obj.__name__ == "mod_init": return True return False def _normalize_version(args): _, path = args - return '.'.join([x.zfill(4) for x in (path.split('/')[-1].split('.'))]) + return ".".join([x.zfill(4) for x in (path.split("/")[-1].split("."))]) class ReleasesTree(TocTree): @@ -573,12 +583,12 @@ class ReleasesTree(TocTree): def run(self): rst = super(ReleasesTree, self).run() - entries = rst[0][0]['entries'][:] + entries = rst[0][0]["entries"][:] entries.sort(key=_normalize_version, reverse=True) - rst[0][0]['entries'][:] = entries + rst[0][0]["entries"][:] = entries return rst def setup(app): - app.add_directive('releasestree', ReleasesTree) - app.connect('autodoc-skip-member', skip_mod_init_member) + app.add_directive("releasestree", ReleasesTree) + app.connect("autodoc-skip-member", skip_mod_init_member) diff --git a/doc/glossary.rst b/doc/glossary.rst index e779a0e063f..0cb0fc20376 100644 --- a/doc/glossary.rst +++ b/doc/glossary.rst @@ -104,7 +104,7 @@ Glossary or stored externally. Job ID - A unique identifier to represent a given :term:`job`. This is often + A unique identifier to represent a given :term:`job `. This is often shortened to JID. Low State @@ -227,7 +227,7 @@ Glossary Contains a set of :term:`state declarations `. State Compiler - Translates :term:`highdata` into lowdata. + Translates :term:`highdata ` into lowdata. State Declaration A data structure which contains a unique ID and describes one or more diff --git a/doc/ref/beacons/all/index.rst b/doc/ref/beacons/all/index.rst index 7fccfc5b151..42e9dd9e44d 100644 --- a/doc/ref/beacons/all/index.rst +++ b/doc/ref/beacons/all/index.rst @@ -11,16 +11,18 @@ beacon modules :template: autosummary.rst.tmpl adb + aix_account avahi_announce bonjour_announce btmp + cert_info diskusage glxinfo haproxy inotify journald load - log + log_beacon memusage napalm_beacon network_info @@ -32,7 +34,10 @@ beacon modules sensehat service sh + smartos_imgadm + smartos_vmadm status telegram_bot_msg twilio_txt_msg + watchdog wtmp diff --git a/doc/ref/beacons/all/salt.beacons.aix_account.rst b/doc/ref/beacons/all/salt.beacons.aix_account.rst new file mode 100644 index 00000000000..f7467322ac5 --- /dev/null +++ b/doc/ref/beacons/all/salt.beacons.aix_account.rst @@ -0,0 +1,5 @@ +salt.beacons.aix_account module +=============================== + +.. automodule:: salt.beacons.aix_account + :members: diff --git a/doc/ref/beacons/all/salt.beacons.cert_info.rst b/doc/ref/beacons/all/salt.beacons.cert_info.rst new file mode 100644 index 00000000000..05b7856d12c --- /dev/null +++ b/doc/ref/beacons/all/salt.beacons.cert_info.rst @@ -0,0 +1,6 @@ +====================== +salt.beacons.cert_info +====================== + +.. automodule:: salt.beacons.cert_info + :members: diff --git a/doc/ref/beacons/all/salt.beacons.log.rst b/doc/ref/beacons/all/salt.beacons.log_beacon.rst similarity index 100% rename from doc/ref/beacons/all/salt.beacons.log.rst rename to doc/ref/beacons/all/salt.beacons.log_beacon.rst diff --git a/doc/ref/beacons/all/salt.beacons.smartos_imgadm.rst b/doc/ref/beacons/all/salt.beacons.smartos_imgadm.rst new file mode 100644 index 00000000000..abbf5b918ef --- /dev/null +++ b/doc/ref/beacons/all/salt.beacons.smartos_imgadm.rst @@ -0,0 +1,5 @@ +salt.beacons.smartos_imgadm module +================================== + +.. automodule:: salt.beacons.smartos_imgadm + :members: diff --git a/doc/ref/beacons/all/salt.beacons.smartos_vmadm.rst b/doc/ref/beacons/all/salt.beacons.smartos_vmadm.rst new file mode 100644 index 00000000000..852c327f374 --- /dev/null +++ b/doc/ref/beacons/all/salt.beacons.smartos_vmadm.rst @@ -0,0 +1,5 @@ +salt.beacons.smartos_vmadm module +================================= + +.. automodule:: salt.beacons.smartos_vmadm + :members: diff --git a/doc/ref/beacons/all/salt.beacons.watchdog.rst b/doc/ref/beacons/all/salt.beacons.watchdog.rst new file mode 100644 index 00000000000..8cea358f657 --- /dev/null +++ b/doc/ref/beacons/all/salt.beacons.watchdog.rst @@ -0,0 +1,5 @@ +salt.beacons.watchdog module +============================ + +.. automodule:: salt.beacons.watchdog + :members: diff --git a/doc/ref/cli/index.rst b/doc/ref/cli/index.rst index fccf51d1db9..99e31b3cb6a 100644 --- a/doc/ref/cli/index.rst +++ b/doc/ref/cli/index.rst @@ -2,6 +2,12 @@ Command Line Reference ====================== +salt-api +======== +.. toctree:: + + salt-api + salt-call ========= .. toctree:: @@ -80,12 +86,6 @@ salt-unity salt-unity -salt-api -======== -.. toctree:: - - salt-api - spm === .. toctree:: diff --git a/doc/ref/cli/salt-ssh.rst b/doc/ref/cli/salt-ssh.rst index 70454abae59..df76cfcc07d 100644 --- a/doc/ref/cli/salt-ssh.rst +++ b/doc/ref/cli/salt-ssh.rst @@ -105,6 +105,14 @@ Options Pass a JID to be used instead of generating one. +.. option:: --pre-flight + + Run the ssh_pre_flight script defined in the roster. + By default this script will only run if the thin dir + does not exist on the target minion. This option will + force the script to run regardless of the thin dir + existing or not. + Authentication Options ---------------------- diff --git a/doc/ref/clouds/all/index.rst b/doc/ref/clouds/all/index.rst index 1f7a97bf02c..3a9c383987e 100644 --- a/doc/ref/clouds/all/index.rst +++ b/doc/ref/clouds/all/index.rst @@ -12,6 +12,7 @@ cloud modules aliyun azurearm + clc cloudstack digitalocean dimensiondata @@ -19,12 +20,14 @@ cloud modules gce gogrid joyent + libvirt linode lxc msazure oneandone opennebula openstack + packet parallels profitbricks proxmox diff --git a/doc/ref/clouds/all/salt.cloud.clouds.clc.rst b/doc/ref/clouds/all/salt.cloud.clouds.clc.rst new file mode 100644 index 00000000000..03991947dbd --- /dev/null +++ b/doc/ref/clouds/all/salt.cloud.clouds.clc.rst @@ -0,0 +1,6 @@ +===================== +salt.cloud.clouds.clc +===================== + +.. automodule:: salt.cloud.clouds.clc + :members: diff --git a/doc/ref/clouds/all/salt.cloud.clouds.libvirt.rst b/doc/ref/clouds/all/salt.cloud.clouds.libvirt.rst new file mode 100644 index 00000000000..32342f932ec --- /dev/null +++ b/doc/ref/clouds/all/salt.cloud.clouds.libvirt.rst @@ -0,0 +1,6 @@ +========================= +salt.cloud.clouds.libvirt +========================= + +.. automodule:: salt.cloud.clouds.libvirt + :members: diff --git a/doc/ref/clouds/all/salt.cloud.clouds.packet.rst b/doc/ref/clouds/all/salt.cloud.clouds.packet.rst new file mode 100644 index 00000000000..7898814742f --- /dev/null +++ b/doc/ref/clouds/all/salt.cloud.clouds.packet.rst @@ -0,0 +1,6 @@ +======================== +salt.cloud.clouds.packet +======================== + +.. automodule:: salt.cloud.clouds.packet + :members: diff --git a/doc/ref/configuration/index.rst b/doc/ref/configuration/index.rst index b5098a4978b..bd54bc25c5a 100644 --- a/doc/ref/configuration/index.rst +++ b/doc/ref/configuration/index.rst @@ -5,8 +5,8 @@ Configuring Salt ================ Salt configuration is very simple. The default configuration for the -:term:`master` will work for most installations and the only requirement for -setting up a :term:`minion` is to set the location of the master in the minion +:term:`master ` will work for most installations and the only requirement for +setting up a :term:`minion ` is to set the location of the master in the minion configuration file. The configuration files will be installed to :file:`/etc/salt` and are named diff --git a/doc/ref/configuration/master.rst b/doc/ref/configuration/master.rst index 3ce679e2095..ea21b4e85cf 100644 --- a/doc/ref/configuration/master.rst +++ b/doc/ref/configuration/master.rst @@ -484,6 +484,22 @@ grains for the master. enable_gpu_grains: True +.. conf_master:: skip_grains + +``skip_grains`` +--------------------- + +Default: ``False`` + +MasterMinions should omit grains. A MasterMinion is "a minion function object +for generic use on the master" that omit pillar. A RunnerClient creates a +MasterMinion omitting states and renderer. Setting to True can improve master +performance. + +.. code-block:: yaml + + skip_grains: True + .. conf_master:: job_cache ``job_cache`` @@ -1341,6 +1357,15 @@ salt-ssh. groupA: minion1,minion2 groupB: minion1,minion3 +.. conf_master:: ssh_run_pre_flight + +Default: False + +Run the ssh_pre_flight script defined in the salt-ssh roster. By default +the script will only run when the thin dir does not exist on the targeted +minion. This will force the script to run and not check if the thin dir +exists first. + .. conf_master:: thin_extra_mods ``thin_extra_mods`` @@ -4008,7 +4033,7 @@ ext_pillar keys to override those from :conf_master:`pillar_roots`. ext_pillar_first: False -.. conf_minion:: pillarenv_from_saltenv +.. conf_master:: pillarenv_from_saltenv ``pillarenv_from_saltenv`` -------------------------- diff --git a/doc/ref/configuration/minion.rst b/doc/ref/configuration/minion.rst index 1abf8675bc3..b01537251c8 100644 --- a/doc/ref/configuration/minion.rst +++ b/doc/ref/configuration/minion.rst @@ -710,7 +710,7 @@ This directory may contain sensitive data and should be protected accordingly. cachedir: /var/cache/salt/minion -.. conf_master:: color_theme +.. conf_minion:: color_theme ``color_theme`` --------------- @@ -831,12 +831,28 @@ Default: ``False`` The minion can locally cache grain data instead of refreshing the data each time the grain is referenced. By default this feature is disabled, -to enable set grains_cache to ``True``. +to enable set ``grains_cache`` to ``True``. .. code-block:: yaml grains_cache: False +.. conf_minion:: grains_cache_expiration + +``grains_cache_expiration`` +--------------------------- + +Default: ``300`` + +Grains cache expiration, in seconds. If the cache file is older than this number +of seconds then the grains cache will be dumped and fully re-populated with +fresh data. Defaults to 5 minutes. Will have no effect if +:conf_minion:`grains_cache` is not enabled. + +.. code-block:: yaml + + grains_cache_expiration: 300 + .. conf_minion:: grains_deep_merge ``grains_deep_merge`` @@ -2207,6 +2223,9 @@ auto-loading modules when states run, set this value to ``False``. .. conf_minion:: clean_dynamic_modules +``clean_dynamic_modules`` +------------------------- + Default: ``True`` clean_dynamic_modules keeps the dynamic modules on the minion in sync with @@ -2700,7 +2719,7 @@ minion to clean the keys. Default: ``''`` Fingerprint of the master public key to validate the identity of your Salt master -before the initial key exchange. The master fingerprint can be found by running +before the initial key exchange. The master fingerprint can be found as ``master.pub`` by running "salt-key -F master" on the Salt master. .. code-block:: yaml diff --git a/doc/ref/engines/all/index.rst b/doc/ref/engines/all/index.rst index f54245e23a2..461a891cc79 100644 --- a/doc/ref/engines/all/index.rst +++ b/doc/ref/engines/all/index.rst @@ -15,11 +15,13 @@ engine modules http_logstash ircbot junos_syslog + libvirt_events logentries logstash_engine napalm_syslog reactor redis_sentinel + script slack sqs_events stalekey diff --git a/doc/ref/engines/all/salt.engines.libvirt_events.rst b/doc/ref/engines/all/salt.engines.libvirt_events.rst new file mode 100644 index 00000000000..b8de412614f --- /dev/null +++ b/doc/ref/engines/all/salt.engines.libvirt_events.rst @@ -0,0 +1,6 @@ +salt.engines.libvirt_events module +================================== + +.. automodule:: salt.engines.libvirt_events + :members: + :undoc-members: diff --git a/doc/ref/engines/all/salt.engines.script.rst b/doc/ref/engines/all/salt.engines.script.rst new file mode 100644 index 00000000000..20f742dc1b3 --- /dev/null +++ b/doc/ref/engines/all/salt.engines.script.rst @@ -0,0 +1,6 @@ +salt.engines.script module +========================== + +.. automodule:: salt.engines.script + :members: + :undoc-members: diff --git a/doc/ref/grains/all/index.rst b/doc/ref/grains/all/index.rst index 9fd6a0171e6..77b4e66a6b0 100644 --- a/doc/ref/grains/all/index.rst +++ b/doc/ref/grains/all/index.rst @@ -10,19 +10,28 @@ grains modules :toctree: :template: autosummary.rst.tmpl - cimc chronos + cimc core disks esxi extra + fibre_channel fx2 + iscsi junos marathon mdadm + mdata metadata + minion_process napalm + nvme + nxos opts panos philips_hue rest_sample + smartos + ssh_sample + zfs diff --git a/doc/ref/grains/all/salt.grains.fibre_channel.rst b/doc/ref/grains/all/salt.grains.fibre_channel.rst new file mode 100644 index 00000000000..9c4d3045eaf --- /dev/null +++ b/doc/ref/grains/all/salt.grains.fibre_channel.rst @@ -0,0 +1,6 @@ +========================= +salt.grains.fibre_channel +========================= + +.. automodule:: salt.grains.fibre_channel + :members: diff --git a/doc/ref/grains/all/salt.grains.iscsi.rst b/doc/ref/grains/all/salt.grains.iscsi.rst new file mode 100644 index 00000000000..11d16faf3d3 --- /dev/null +++ b/doc/ref/grains/all/salt.grains.iscsi.rst @@ -0,0 +1,6 @@ +================= +salt.grains.iscsi +================= + +.. automodule:: salt.grains.iscsi + :members: diff --git a/doc/ref/grains/all/salt.grains.mdata.rst b/doc/ref/grains/all/salt.grains.mdata.rst new file mode 100644 index 00000000000..34599b7329f --- /dev/null +++ b/doc/ref/grains/all/salt.grains.mdata.rst @@ -0,0 +1,6 @@ +================= +salt.grains.mdata +================= + +.. automodule:: salt.grains.mdata + :members: diff --git a/doc/ref/grains/all/salt.grains.minion_process.rst b/doc/ref/grains/all/salt.grains.minion_process.rst new file mode 100644 index 00000000000..254e646416c --- /dev/null +++ b/doc/ref/grains/all/salt.grains.minion_process.rst @@ -0,0 +1,6 @@ +========================== +salt.grains.minion_process +========================== + +.. automodule:: salt.grains.minion_process + :members: diff --git a/doc/ref/grains/all/salt.grains.nvme.rst b/doc/ref/grains/all/salt.grains.nvme.rst new file mode 100644 index 00000000000..519fe755505 --- /dev/null +++ b/doc/ref/grains/all/salt.grains.nvme.rst @@ -0,0 +1,6 @@ +================ +salt.grains.nvme +================ + +.. automodule:: salt.grains.nvme + :members: diff --git a/doc/ref/grains/all/salt.grains.nxos.rst b/doc/ref/grains/all/salt.grains.nxos.rst new file mode 100644 index 00000000000..79b7448a56f --- /dev/null +++ b/doc/ref/grains/all/salt.grains.nxos.rst @@ -0,0 +1,6 @@ +================ +salt.grains.nxos +================ + +.. automodule:: salt.grains.nxos + :members: diff --git a/doc/ref/grains/all/salt.grains.smartos.rst b/doc/ref/grains/all/salt.grains.smartos.rst new file mode 100644 index 00000000000..0b5a9a28efc --- /dev/null +++ b/doc/ref/grains/all/salt.grains.smartos.rst @@ -0,0 +1,6 @@ +=================== +salt.grains.smartos +=================== + +.. automodule:: salt.grains.smartos + :members: diff --git a/doc/ref/grains/all/salt.grains.ssh_sample.rst b/doc/ref/grains/all/salt.grains.ssh_sample.rst new file mode 100644 index 00000000000..84828a51714 --- /dev/null +++ b/doc/ref/grains/all/salt.grains.ssh_sample.rst @@ -0,0 +1,6 @@ +====================== +salt.grains.ssh_sample +====================== + +.. automodule:: salt.grains.ssh_sample + :members: diff --git a/doc/ref/grains/all/salt.grains.zfs.rst b/doc/ref/grains/all/salt.grains.zfs.rst new file mode 100644 index 00000000000..f0875ecb1d0 --- /dev/null +++ b/doc/ref/grains/all/salt.grains.zfs.rst @@ -0,0 +1,6 @@ +=============== +salt.grains.zfs +=============== + +.. automodule:: salt.grains.zfs + :members: diff --git a/doc/ref/modules/all/index.rst b/doc/ref/modules/all/index.rst index 359af7e1e00..19a2b53600d 100644 --- a/doc/ref/modules/all/index.rst +++ b/doc/ref/modules/all/index.rst @@ -13,6 +13,7 @@ execution modules salt.modules.pkg salt.modules.service salt.modules.shadow + salt.modules.sysctl salt.modules.user .. currentmodule:: salt.modules @@ -23,6 +24,7 @@ execution modules acme aix_group + aix_shadow aixpkg aliases alternatives @@ -31,6 +33,7 @@ execution modules apcups apf apkpkg + aptly aptpkg archive arista_pyeapi @@ -40,6 +43,7 @@ execution modules augeas_cfg aws_sqs azurearm_compute + azurearm_dns azurearm_network azurearm_resource bamboohr @@ -50,6 +54,7 @@ execution modules boto3_elasticache boto3_elasticsearch boto3_route53 + boto3_sns boto_apigateway boto_asg boto_cfn @@ -73,10 +78,12 @@ execution modules boto_lambda boto_rds boto_route53 + boto_s3 boto_s3_bucket boto_secgroup boto_sns boto_sqs + boto_ssm boto_vpc bower bridge @@ -92,6 +99,7 @@ execution modules chef chocolatey chronos + chroot cimc ciscoconfparse_mod cisconso @@ -104,6 +112,7 @@ execution modules cp cpan cron + cryptdev csf cyg daemontools @@ -112,10 +121,10 @@ execution modules ddns deb_apache deb_postgres - debuild_pkgbuild debconfmod debian_ip debian_service + debuild_pkgbuild defaults devmap dig @@ -136,7 +145,10 @@ execution modules elasticsearch environ eselect + esxcluster + esxdatacenter esxi + esxvm etcd_mod ethtool event @@ -150,6 +162,8 @@ execution modules freebsdpkg freebsdports freebsdservice + freezer + gcp_addon gem genesis gentoo_service @@ -158,6 +172,7 @@ execution modules github glance glanceng + glassfish glusterfs gnomedesktop google_chat @@ -172,14 +187,15 @@ execution modules hashutil heat hg + highstate_doc hosts http + icinga2 ifttt ilo - icinga2 incron - influxdbmod influxdb08mod + influxdbmod infoblox ini_manage inspectlib @@ -226,6 +242,8 @@ execution modules linux_acl linux_ip linux_lvm + linux_service + linux_shadow linux_sysctl localemod locate @@ -237,11 +255,9 @@ execution modules lxd mac_assistive mac_brew_pkg - macdefaults mac_desktop mac_group mac_keychain - macpackage mac_pkgutil mac_portspkg mac_power @@ -253,6 +269,8 @@ execution modules mac_timezone mac_user mac_xattr + macdefaults + macpackage makeconf mandrill marathon @@ -276,16 +294,15 @@ execution modules nacl nagios nagios_rpc - namecheap_domains_dns namecheap_domains + namecheap_domains_dns namecheap_domains_ns namecheap_ssl namecheap_users - napalm_mod - napalm_netacl napalm_bgp napalm_formula napalm_mod + napalm_netacl napalm_network napalm_ntp napalm_probes @@ -302,6 +319,7 @@ execution modules network neutron neutronng + nexus nfs3 nftables nginx @@ -401,9 +419,7 @@ execution modules sensehat sensors serverdensity_device - service servicenow - shadow slack_notify slsutil smartos_imgadm @@ -413,6 +429,7 @@ execution modules smbios smf_service smtp + snapper solaris_fmadm solaris_group solaris_shadow @@ -428,7 +445,6 @@ execution modules ssh ssh_pkg ssh_service - snapper state status statuspage @@ -449,8 +465,8 @@ execution modules telemetry temp test - testinframod test_virtual + testinframod textfsm_mod timezone tls @@ -469,12 +485,14 @@ execution modules vault vbox_guest vboxmanage + vcenter victorops virt virtualenv_mod vmctl vsphere webutil + win_auditpol win_autoruns win_certutil win_dacl @@ -496,7 +514,6 @@ execution modules win_pki win_powercfg win_psget - winrepo win_servermanager win_service win_shadow @@ -509,6 +526,8 @@ execution modules win_useradd win_wua win_wusa + winrepo + wordpress x509 xapi_virt xbpspkg diff --git a/doc/ref/modules/all/salt.modules.aix_shadow.rst b/doc/ref/modules/all/salt.modules.aix_shadow.rst new file mode 100644 index 00000000000..a3159fd0fd6 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.aix_shadow.rst @@ -0,0 +1,6 @@ +salt.modules.aix_shadow module +============================== + +.. automodule:: salt.modules.aix_shadow + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.aptly.rst b/doc/ref/modules/all/salt.modules.aptly.rst new file mode 100644 index 00000000000..8ecbc8ced01 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.aptly.rst @@ -0,0 +1,6 @@ +salt.modules.aptly module +========================= + +.. automodule:: salt.modules.aptly + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.azurearm_dns.rst b/doc/ref/modules/all/salt.modules.azurearm_dns.rst new file mode 100644 index 00000000000..fe216be18d9 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.azurearm_dns.rst @@ -0,0 +1,6 @@ +salt.modules.azurearm_dns module +================================ + +.. automodule:: salt.modules.azurearm_dns + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.boto3_sns.rst b/doc/ref/modules/all/salt.modules.boto3_sns.rst new file mode 100644 index 00000000000..3031c5c65b3 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.boto3_sns.rst @@ -0,0 +1,6 @@ +salt.modules.boto3_sns module +============================= + +.. automodule:: salt.modules.boto3_sns + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.boto_s3.rst b/doc/ref/modules/all/salt.modules.boto_s3.rst new file mode 100644 index 00000000000..8f930bbc0d6 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.boto_s3.rst @@ -0,0 +1,6 @@ +salt.modules.boto_s3 module +=========================== + +.. automodule:: salt.modules.boto_s3 + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.boto_ssm.rst b/doc/ref/modules/all/salt.modules.boto_ssm.rst new file mode 100644 index 00000000000..66c763cb917 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.boto_ssm.rst @@ -0,0 +1,6 @@ +salt.modules.boto_ssm module +============================ + +.. automodule:: salt.modules.boto_ssm + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.chroot.rst b/doc/ref/modules/all/salt.modules.chroot.rst new file mode 100644 index 00000000000..f8da99aaf01 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.chroot.rst @@ -0,0 +1,6 @@ +salt.modules.chroot module +========================== + +.. automodule:: salt.modules.chroot + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.cryptdev.rst b/doc/ref/modules/all/salt.modules.cryptdev.rst new file mode 100644 index 00000000000..c62ebb2d748 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.cryptdev.rst @@ -0,0 +1,6 @@ +salt.modules.cryptdev module +============================ + +.. automodule:: salt.modules.cryptdev + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.esxcluster.rst b/doc/ref/modules/all/salt.modules.esxcluster.rst new file mode 100644 index 00000000000..8c086929ba3 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.esxcluster.rst @@ -0,0 +1,6 @@ +salt.modules.esxcluster module +============================== + +.. automodule:: salt.modules.esxcluster + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.esxdatacenter.rst b/doc/ref/modules/all/salt.modules.esxdatacenter.rst new file mode 100644 index 00000000000..4fb8d810969 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.esxdatacenter.rst @@ -0,0 +1,6 @@ +salt.modules.esxdatacenter module +================================= + +.. automodule:: salt.modules.esxdatacenter + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.esxvm.rst b/doc/ref/modules/all/salt.modules.esxvm.rst new file mode 100644 index 00000000000..5d9305247cc --- /dev/null +++ b/doc/ref/modules/all/salt.modules.esxvm.rst @@ -0,0 +1,6 @@ +salt.modules.esxvm module +========================= + +.. automodule:: salt.modules.esxvm + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.freezer.rst b/doc/ref/modules/all/salt.modules.freezer.rst new file mode 100644 index 00000000000..4b077e148df --- /dev/null +++ b/doc/ref/modules/all/salt.modules.freezer.rst @@ -0,0 +1,6 @@ +salt.modules.freezer module +=========================== + +.. automodule:: salt.modules.freezer + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.gcp_addon.rst b/doc/ref/modules/all/salt.modules.gcp_addon.rst new file mode 100644 index 00000000000..415cacfb84c --- /dev/null +++ b/doc/ref/modules/all/salt.modules.gcp_addon.rst @@ -0,0 +1,6 @@ +salt.modules.gcp_addon module +============================= + +.. automodule:: salt.modules.gcp_addon + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.glassfish.rst b/doc/ref/modules/all/salt.modules.glassfish.rst new file mode 100644 index 00000000000..8297783c122 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.glassfish.rst @@ -0,0 +1,6 @@ +salt.modules.glassfish module +============================= + +.. automodule:: salt.modules.glassfish + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.highstate_doc.rst b/doc/ref/modules/all/salt.modules.highstate_doc.rst new file mode 100644 index 00000000000..ccf7ea4f7b4 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.highstate_doc.rst @@ -0,0 +1,6 @@ +salt.modules.highstate_doc module +================================= + +.. automodule:: salt.modules.highstate_doc + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.linux_service.rst b/doc/ref/modules/all/salt.modules.linux_service.rst new file mode 100644 index 00000000000..17c1de6e0e3 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.linux_service.rst @@ -0,0 +1,6 @@ +========================== +salt.modules.linux_service +========================== + +.. automodule:: salt.modules.linux_service + :members: diff --git a/doc/ref/modules/all/salt.modules.linux_shadow.rst b/doc/ref/modules/all/salt.modules.linux_shadow.rst new file mode 100644 index 00000000000..5edc575f104 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.linux_shadow.rst @@ -0,0 +1,6 @@ +========================= +salt.modules.linux_shadow +========================= + +.. automodule:: salt.modules.linux_shadow + :members: diff --git a/doc/ref/modules/all/salt.modules.nexus.rst b/doc/ref/modules/all/salt.modules.nexus.rst new file mode 100644 index 00000000000..09d619c15de --- /dev/null +++ b/doc/ref/modules/all/salt.modules.nexus.rst @@ -0,0 +1,6 @@ +salt.modules.nexus module +========================= + +.. automodule:: salt.modules.nexus + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.pkg.rst b/doc/ref/modules/all/salt.modules.pkg.rst index 09ca1b88542..88db6e06f44 100644 --- a/doc/ref/modules/all/salt.modules.pkg.rst +++ b/doc/ref/modules/all/salt.modules.pkg.rst @@ -39,4 +39,3 @@ Execution Module Used for .. _Homebrew: http://brew.sh/ .. _OpenCSW: http://www.opencsw.org/ - diff --git a/doc/ref/modules/all/salt.modules.salt_version.rst b/doc/ref/modules/all/salt.modules.salt_version.rst index f101ea269fd..8dc12690faf 100644 --- a/doc/ref/modules/all/salt.modules.salt_version.rst +++ b/doc/ref/modules/all/salt.modules.salt_version.rst @@ -3,4 +3,4 @@ salt.modules.salt_version ========================= .. automodule:: salt.modules.salt_version - :members: + :members: \ No newline at end of file diff --git a/doc/ref/modules/all/salt.modules.saltcheck.rst b/doc/ref/modules/all/salt.modules.saltcheck.rst index 967ae6f225d..933d0875636 100644 --- a/doc/ref/modules/all/salt.modules.saltcheck.rst +++ b/doc/ref/modules/all/salt.modules.saltcheck.rst @@ -4,4 +4,4 @@ salt.modules.saltcheck .. automodule:: salt.modules.saltcheck :members: - :exclude-members: SaltCheck, StateTestLoader + :exclude-members: SaltCheck, StateTestLoader, run_state_tests_ssh diff --git a/doc/ref/modules/all/salt.modules.shadow.rst b/doc/ref/modules/all/salt.modules.shadow.rst index 3b03032d28e..8d3df985c59 100644 --- a/doc/ref/modules/all/salt.modules.shadow.rst +++ b/doc/ref/modules/all/salt.modules.shadow.rst @@ -13,7 +13,8 @@ modules: ====================================== ======================================== Execution Module Used for ====================================== ======================================== -:py:mod:`~salt.modules.shadow` Linux +:py:mod:`~salt.modules.aix_shadow` AIX +:py:mod:`~salt.modules.linux_shadow` Linux :py:mod:`~salt.modules.bsd_shadow` FreeBSD, OpenBSD, NetBSD :py:mod:`~salt.modules.solaris_shadow` Solaris-based OSes :py:mod:`~salt.modules.win_shadow` Windows diff --git a/doc/ref/modules/all/salt.modules.sysctl.rst b/doc/ref/modules/all/salt.modules.sysctl.rst new file mode 100644 index 00000000000..d78c0059423 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.sysctl.rst @@ -0,0 +1,20 @@ +.. _virtual-sysctl: + +=================== +salt.modules.sysctl +=================== + +.. py:module:: salt.modules.sysctl + :synopsis: A virtual module for managing sysctl parameters + +``sysctl`` is a virtual module that is fulfilled by one of the following modules: + +============================================ ======================================== +Execution Module Used for +============================================ ======================================== +:py:mod:`~salt.modules.freebsd_sysctl` FreeBSD +:py:mod:`~salt.modules.linux_sysctl` Linux +:py:mod:`~salt.modules.mac_sysctl` macOS +:py:mod:`~salt.modules.netbsd_sysctl` NetBSD +:py:mod:`~salt.modules.openbsd_sysctl` OpenBSD +============================================ ======================================== diff --git a/doc/ref/modules/all/salt.modules.vcenter.rst b/doc/ref/modules/all/salt.modules.vcenter.rst new file mode 100644 index 00000000000..863f92e85d4 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.vcenter.rst @@ -0,0 +1,6 @@ +salt.modules.vcenter module +=========================== + +.. automodule:: salt.modules.vcenter + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.win_auditpol.rst b/doc/ref/modules/all/salt.modules.win_auditpol.rst new file mode 100644 index 00000000000..a5fabb2e3ce --- /dev/null +++ b/doc/ref/modules/all/salt.modules.win_auditpol.rst @@ -0,0 +1,6 @@ +salt.modules.win_auditpol module +================================ + +.. automodule:: salt.modules.win_auditpol + :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.win_wusa.rst b/doc/ref/modules/all/salt.modules.win_wusa.rst index 7eb615ee23a..b2dbe8be608 100644 --- a/doc/ref/modules/all/salt.modules.win_wusa.rst +++ b/doc/ref/modules/all/salt.modules.win_wusa.rst @@ -4,3 +4,4 @@ salt.modules.win_wusa .. automodule:: salt.modules.win_wusa :members: + :undoc-members: diff --git a/doc/ref/modules/all/salt.modules.wordpress.rst b/doc/ref/modules/all/salt.modules.wordpress.rst new file mode 100644 index 00000000000..d21aba9e5b7 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.wordpress.rst @@ -0,0 +1,6 @@ +salt.modules.wordpress module +============================= + +.. automodule:: salt.modules.wordpress + :members: + :undoc-members: diff --git a/doc/ref/netapi/all/index.rst b/doc/ref/netapi/all/index.rst index a05ec291e29..3bcefb5f24d 100644 --- a/doc/ref/netapi/all/index.rst +++ b/doc/ref/netapi/all/index.rst @@ -4,9 +4,12 @@ netapi modules ============== -.. toctree:: - :maxdepth: 2 +.. currentmodule:: salt.netapi - salt.netapi.rest_cherrypy - salt.netapi.rest_tornado - salt.netapi.rest_wsgi \ No newline at end of file +.. autosummary:: + :toctree: + :template: autosummary.rst.tmpl + + rest_cherrypy + rest_tornado + rest_wsgi diff --git a/doc/ref/netapi/all/salt.netapi.rest_cherrypy.rst b/doc/ref/netapi/all/salt.netapi.rest_cherrypy.rst index 5758bc04c98..f8e02f6f15b 100644 --- a/doc/ref/netapi/all/salt.netapi.rest_cherrypy.rst +++ b/doc/ref/netapi/all/salt.netapi.rest_cherrypy.rst @@ -5,6 +5,8 @@ rest_cherrypy .. contents:: :local: +.. automodule:: salt.netapi.rest_cherrypy + .. automodule:: salt.netapi.rest_cherrypy.app .. automodule:: salt.netapi.rest_cherrypy.wsgi diff --git a/doc/ref/netapi/all/salt.netapi.rest_tornado.rst b/doc/ref/netapi/all/salt.netapi.rest_tornado.rst index d21a44e3b8c..916e58172b4 100644 --- a/doc/ref/netapi/all/salt.netapi.rest_tornado.rst +++ b/doc/ref/netapi/all/salt.netapi.rest_tornado.rst @@ -2,6 +2,8 @@ rest_tornado ============ +.. automodule:: salt.netapi.rest_tornado + .. automodule:: salt.netapi.rest_tornado.saltnado .. automodule:: salt.netapi.rest_tornado.saltnado_websockets @@ -54,4 +56,4 @@ REST URI Reference --------- .. autoclass:: WebhookSaltAPIHandler - :members: post \ No newline at end of file + :members: post diff --git a/doc/ref/output/all/index.rst b/doc/ref/output/all/index.rst index 23c8f85b90d..5f0a8bc7493 100644 --- a/doc/ref/output/all/index.rst +++ b/doc/ref/output/all/index.rst @@ -12,6 +12,7 @@ Follow one of the below links for further information and examples :toctree: :template: autosummary.rst.tmpl + dson highstate json_out key diff --git a/doc/ref/output/all/salt.output.dson.rst b/doc/ref/output/all/salt.output.dson.rst new file mode 100644 index 00000000000..fa9553ff4fb --- /dev/null +++ b/doc/ref/output/all/salt.output.dson.rst @@ -0,0 +1,6 @@ +================ +salt.output.dson +================ + +.. automodule:: salt.output.dson + :members: diff --git a/doc/ref/pillar/all/index.rst b/doc/ref/pillar/all/index.rst index b4c578228ab..b5e60f9a1d8 100644 --- a/doc/ref/pillar/all/index.rst +++ b/doc/ref/pillar/all/index.rst @@ -21,6 +21,7 @@ pillar modules django_orm ec2_pillar etcd_pillar + extra_minion_data_in_pillar file_tree foreman git_pillar @@ -33,6 +34,7 @@ pillar modules makostack mongo mysql + nacl netbox neutron nodegroups @@ -42,6 +44,7 @@ pillar modules puppet reclass_adapter redismod + rethinkdb_pillar s3 saltclass sql_base diff --git a/doc/ref/pillar/all/salt.pillar.extra_minion_data_in_pillar.rst b/doc/ref/pillar/all/salt.pillar.extra_minion_data_in_pillar.rst new file mode 100644 index 00000000000..16ff48a6271 --- /dev/null +++ b/doc/ref/pillar/all/salt.pillar.extra_minion_data_in_pillar.rst @@ -0,0 +1,6 @@ +======================================= +salt.pillar.extra_minion_data_in_pillar +======================================= + +.. automodule:: salt.pillar.extra_minion_data_in_pillar + :members: diff --git a/doc/ref/pillar/all/salt.pillar.nacl.rst b/doc/ref/pillar/all/salt.pillar.nacl.rst new file mode 100644 index 00000000000..7d245c0d7ea --- /dev/null +++ b/doc/ref/pillar/all/salt.pillar.nacl.rst @@ -0,0 +1,6 @@ +================ +salt.pillar.nacl +================ + +.. automodule:: salt.pillar.nacl + :members: diff --git a/doc/ref/pillar/all/salt.pillar.rethinkdb_pillar.rst b/doc/ref/pillar/all/salt.pillar.rethinkdb_pillar.rst new file mode 100644 index 00000000000..bc6acb7203c --- /dev/null +++ b/doc/ref/pillar/all/salt.pillar.rethinkdb_pillar.rst @@ -0,0 +1,6 @@ +============================ +salt.pillar.rethinkdb_pillar +============================ + +.. automodule:: salt.pillar.rethinkdb_pillar + :members: diff --git a/doc/ref/proxy/all/index.rst b/doc/ref/proxy/all/index.rst index 33ae5f4df3a..76e214be70f 100644 --- a/doc/ref/proxy/all/index.rst +++ b/doc/ref/proxy/all/index.rst @@ -11,12 +11,15 @@ proxy modules :template: autosummary.rst.tmpl arista_pyeapi - cimc chronos cimc cisconso + docker dummy + esxcluster + esxdatacenter esxi + esxvm fx2 junos marathon @@ -28,3 +31,4 @@ proxy modules philips_hue rest_sample ssh_sample + vcenter diff --git a/doc/ref/proxy/all/salt.proxy.docker.rst b/doc/ref/proxy/all/salt.proxy.docker.rst new file mode 100644 index 00000000000..b83652be443 --- /dev/null +++ b/doc/ref/proxy/all/salt.proxy.docker.rst @@ -0,0 +1,6 @@ +salt.proxy.docker module +======================== + +.. automodule:: salt.proxy.docker + :members: + :undoc-members: diff --git a/doc/ref/proxy/all/salt.proxy.esxcluster.rst b/doc/ref/proxy/all/salt.proxy.esxcluster.rst new file mode 100644 index 00000000000..38a6331bbbc --- /dev/null +++ b/doc/ref/proxy/all/salt.proxy.esxcluster.rst @@ -0,0 +1,6 @@ +salt.proxy.esxcluster module +============================ + +.. automodule:: salt.proxy.esxcluster + :members: + :undoc-members: diff --git a/doc/ref/proxy/all/salt.proxy.esxdatacenter.rst b/doc/ref/proxy/all/salt.proxy.esxdatacenter.rst new file mode 100644 index 00000000000..f190574f8f5 --- /dev/null +++ b/doc/ref/proxy/all/salt.proxy.esxdatacenter.rst @@ -0,0 +1,6 @@ +salt.proxy.esxdatacenter module +=============================== + +.. automodule:: salt.proxy.esxdatacenter + :members: + :undoc-members: diff --git a/doc/ref/proxy/all/salt.proxy.esxvm.rst b/doc/ref/proxy/all/salt.proxy.esxvm.rst new file mode 100644 index 00000000000..fbbf2fc821d --- /dev/null +++ b/doc/ref/proxy/all/salt.proxy.esxvm.rst @@ -0,0 +1,6 @@ +salt.proxy.esxvm module +======================= + +.. automodule:: salt.proxy.esxvm + :members: + :undoc-members: diff --git a/doc/ref/proxy/all/salt.proxy.vcenter.rst b/doc/ref/proxy/all/salt.proxy.vcenter.rst new file mode 100644 index 00000000000..264c38d6885 --- /dev/null +++ b/doc/ref/proxy/all/salt.proxy.vcenter.rst @@ -0,0 +1,6 @@ +salt.proxy.vcenter module +========================= + +.. automodule:: salt.proxy.vcenter + :members: + :undoc-members: diff --git a/doc/ref/renderers/all/index.rst b/doc/ref/renderers/all/index.rst index 5bc34d41f05..1ef8f32fb23 100644 --- a/doc/ref/renderers/all/index.rst +++ b/doc/ref/renderers/all/index.rst @@ -10,6 +10,7 @@ renderer modules :toctree: :template: autosummary.rst.tmpl + aws_kms cheetah dson genshi @@ -20,6 +21,7 @@ renderer modules json5 mako msgpack + nacl pass py pydsl diff --git a/doc/ref/renderers/all/salt.renderers.aws_kms.rst b/doc/ref/renderers/all/salt.renderers.aws_kms.rst new file mode 100644 index 00000000000..af048a96156 --- /dev/null +++ b/doc/ref/renderers/all/salt.renderers.aws_kms.rst @@ -0,0 +1,6 @@ +====================== +salt.renderers.aws_kms +====================== + +.. automodule:: salt.renderers.aws_kms + :members: diff --git a/doc/ref/renderers/all/salt.renderers.nacl.rst b/doc/ref/renderers/all/salt.renderers.nacl.rst new file mode 100644 index 00000000000..41c5da00108 --- /dev/null +++ b/doc/ref/renderers/all/salt.renderers.nacl.rst @@ -0,0 +1,6 @@ +=================== +salt.renderers.nacl +=================== + +.. automodule:: salt.renderers.nacl + :members: diff --git a/doc/ref/returners/all/index.rst b/doc/ref/returners/all/index.rst index 68831349e34..95b5fc5b847 100644 --- a/doc/ref/returners/all/index.rst +++ b/doc/ref/returners/all/index.rst @@ -40,6 +40,7 @@ returner modules redis_return sentry_return slack_returner + slack_webhook_return sms_return smtp_return splunk diff --git a/doc/ref/returners/all/salt.returners.slack_webhook_return.rst b/doc/ref/returners/all/salt.returners.slack_webhook_return.rst new file mode 100644 index 00000000000..3d3d7125389 --- /dev/null +++ b/doc/ref/returners/all/salt.returners.slack_webhook_return.rst @@ -0,0 +1,6 @@ +=================================== +salt.returners.slack_webhook_return +=================================== + +.. automodule:: salt.returners.slack_webhook_return + :members: diff --git a/doc/ref/runners/all/index.rst b/doc/ref/runners/all/index.rst index ebbab3a420d..3a9409b4e37 100644 --- a/doc/ref/runners/all/index.rst +++ b/doc/ref/runners/all/index.rst @@ -15,6 +15,7 @@ runner modules bgp cache cloud + config ddns digicertapi doc diff --git a/doc/ref/runners/all/salt.runners.config.rst b/doc/ref/runners/all/salt.runners.config.rst new file mode 100644 index 00000000000..cc8a4463bc8 --- /dev/null +++ b/doc/ref/runners/all/salt.runners.config.rst @@ -0,0 +1,6 @@ +=================== +salt.runners.config +=================== + +.. automodule:: salt.runners.config + :members: diff --git a/doc/ref/states/all/index.rst b/doc/ref/states/all/index.rst index 0c71b922810..292ea696463 100644 --- a/doc/ref/states/all/index.rst +++ b/doc/ref/states/all/index.rst @@ -25,6 +25,7 @@ state modules augeas aws_sqs azurearm_compute + azurearm_dns azurearm_network azurearm_resource beacon @@ -33,6 +34,7 @@ state modules boto3_elasticache boto3_elasticsearch boto3_route53 + boto3_sns boto_apigateway boto_asg boto_cfn @@ -57,6 +59,7 @@ state modules boto_lc boto_rds boto_route53 + boto_s3 boto_s3_bucket boto_secgroup boto_sns @@ -74,6 +77,7 @@ state modules cmd composer cron + cryptdev csf cyg ddns @@ -85,14 +89,18 @@ state modules docker_network docker_volume drac + dvs elasticsearch elasticsearch_index elasticsearch_index_template environ eselect + esxcluster + esxdatacenter + esxi + esxvm etcd_mod ethtool - esxi event file firewall @@ -102,6 +110,7 @@ state modules github glance glance_image + glassfish glusterfs gnomedesktop gpg @@ -116,6 +125,7 @@ state modules group heat hg + highstate_doc host http icinga2 @@ -167,11 +177,15 @@ state modules lvs_server lvs_service lxc + lxd + lxd_container + lxd_image + lxd_profile mac_assistive - macdefaults mac_keychain - macpackage mac_xattr + macdefaults + macpackage makeconf marathon_app mdadm_raid @@ -183,22 +197,27 @@ state modules mongodb_user monit mount + mssql_database + mssql_login + mssql_role + mssql_user msteams mysql_database mysql_grants mysql_query mysql_user + net_napalm_yang netacl netconfig netntp netsnmp netusers network - net_napalm_yang neutron_network neutron_secgroup neutron_secgroup_rule neutron_subnet + nexus nfs_export nftables npm @@ -214,9 +233,10 @@ state modules pagerduty_service pagerduty_user panos + pbm pcs - pecl pdbedit + pecl pip_state pkg pkgbuild @@ -316,6 +336,7 @@ state modules win_wua win_wusa winrepo + wordpress x509 xml xmpp @@ -326,11 +347,12 @@ state modules zabbix_template zabbix_user zabbix_usergroup + zabbix_usermacro zabbix_valuemap zcbuildout zenoss - zk_concurrency zfs + zk_concurrency zone zookeeper zpool diff --git a/doc/ref/states/all/salt.states.azurearm_dns.rst b/doc/ref/states/all/salt.states.azurearm_dns.rst new file mode 100644 index 00000000000..c068de777bf --- /dev/null +++ b/doc/ref/states/all/salt.states.azurearm_dns.rst @@ -0,0 +1,5 @@ +salt.states.azurearm_dns module +=============================== + +.. automodule:: salt.states.azurearm_dns + :members: diff --git a/doc/ref/states/all/salt.states.boto3_sns.rst b/doc/ref/states/all/salt.states.boto3_sns.rst new file mode 100644 index 00000000000..65248061d28 --- /dev/null +++ b/doc/ref/states/all/salt.states.boto3_sns.rst @@ -0,0 +1,5 @@ +salt.states.boto3_sns module +============================ + +.. automodule:: salt.states.boto3_sns + :members: diff --git a/doc/ref/states/all/salt.states.boto_s3.rst b/doc/ref/states/all/salt.states.boto_s3.rst new file mode 100644 index 00000000000..cdb00afaf06 --- /dev/null +++ b/doc/ref/states/all/salt.states.boto_s3.rst @@ -0,0 +1,5 @@ +salt.states.boto_s3 module +========================== + +.. automodule:: salt.states.boto_s3 + :members: diff --git a/doc/ref/states/all/salt.states.cryptdev.rst b/doc/ref/states/all/salt.states.cryptdev.rst new file mode 100644 index 00000000000..3672c9c9036 --- /dev/null +++ b/doc/ref/states/all/salt.states.cryptdev.rst @@ -0,0 +1,5 @@ +salt.states.cryptdev module +=========================== + +.. automodule:: salt.states.cryptdev + :members: diff --git a/doc/ref/states/all/salt.states.dvs.rst b/doc/ref/states/all/salt.states.dvs.rst new file mode 100644 index 00000000000..8b7887bb3fe --- /dev/null +++ b/doc/ref/states/all/salt.states.dvs.rst @@ -0,0 +1,5 @@ +salt.states.dvs module +====================== + +.. automodule:: salt.states.dvs + :members: diff --git a/doc/ref/states/all/salt.states.esxcluster.rst b/doc/ref/states/all/salt.states.esxcluster.rst new file mode 100644 index 00000000000..fd0baaf2082 --- /dev/null +++ b/doc/ref/states/all/salt.states.esxcluster.rst @@ -0,0 +1,5 @@ +salt.states.esxcluster module +============================= + +.. automodule:: salt.states.esxcluster + :members: diff --git a/doc/ref/states/all/salt.states.esxdatacenter.rst b/doc/ref/states/all/salt.states.esxdatacenter.rst new file mode 100644 index 00000000000..4ffaf2b65a3 --- /dev/null +++ b/doc/ref/states/all/salt.states.esxdatacenter.rst @@ -0,0 +1,5 @@ +salt.states.esxdatacenter module +================================ + +.. automodule:: salt.states.esxdatacenter + :members: diff --git a/doc/ref/states/all/salt.states.esxvm.rst b/doc/ref/states/all/salt.states.esxvm.rst new file mode 100644 index 00000000000..b20f6b060bc --- /dev/null +++ b/doc/ref/states/all/salt.states.esxvm.rst @@ -0,0 +1,5 @@ +salt.states.esxvm module +======================== + +.. automodule:: salt.states.esxvm + :members: diff --git a/doc/ref/states/all/salt.states.glassfish.rst b/doc/ref/states/all/salt.states.glassfish.rst new file mode 100644 index 00000000000..663ccfdf95f --- /dev/null +++ b/doc/ref/states/all/salt.states.glassfish.rst @@ -0,0 +1,5 @@ +salt.states.glassfish module +============================ + +.. automodule:: salt.states.glassfish + :members: diff --git a/doc/ref/states/all/salt.states.highstate_doc.rst b/doc/ref/states/all/salt.states.highstate_doc.rst new file mode 100644 index 00000000000..fbaee623a5d --- /dev/null +++ b/doc/ref/states/all/salt.states.highstate_doc.rst @@ -0,0 +1,5 @@ +salt.states.highstate_doc module +================================ + +.. automodule:: salt.states.highstate_doc + :members: diff --git a/doc/ref/states/all/salt.states.lxd.rst b/doc/ref/states/all/salt.states.lxd.rst new file mode 100644 index 00000000000..758e71b682d --- /dev/null +++ b/doc/ref/states/all/salt.states.lxd.rst @@ -0,0 +1,5 @@ +salt.states.lxd module +====================== + +.. automodule:: salt.states.lxd + :members: diff --git a/doc/ref/states/all/salt.states.lxd_container.rst b/doc/ref/states/all/salt.states.lxd_container.rst new file mode 100644 index 00000000000..72228e3ad18 --- /dev/null +++ b/doc/ref/states/all/salt.states.lxd_container.rst @@ -0,0 +1,5 @@ +salt.states.lxd_container module +================================ + +.. automodule:: salt.states.lxd_container + :members: diff --git a/doc/ref/states/all/salt.states.lxd_image.rst b/doc/ref/states/all/salt.states.lxd_image.rst new file mode 100644 index 00000000000..ec40159930f --- /dev/null +++ b/doc/ref/states/all/salt.states.lxd_image.rst @@ -0,0 +1,5 @@ +salt.states.lxd_image module +============================ + +.. automodule:: salt.states.lxd_image + :members: diff --git a/doc/ref/states/all/salt.states.lxd_profile.rst b/doc/ref/states/all/salt.states.lxd_profile.rst new file mode 100644 index 00000000000..f3f4e2fcbd5 --- /dev/null +++ b/doc/ref/states/all/salt.states.lxd_profile.rst @@ -0,0 +1,5 @@ +salt.states.lxd_profile module +============================== + +.. automodule:: salt.states.lxd_profile + :members: diff --git a/doc/ref/states/all/salt.states.mssql_database.rst b/doc/ref/states/all/salt.states.mssql_database.rst new file mode 100644 index 00000000000..6e11650298b --- /dev/null +++ b/doc/ref/states/all/salt.states.mssql_database.rst @@ -0,0 +1,5 @@ +salt.states.mssql_database module +================================= + +.. automodule:: salt.states.mssql_database + :members: diff --git a/doc/ref/states/all/salt.states.mssql_login.rst b/doc/ref/states/all/salt.states.mssql_login.rst new file mode 100644 index 00000000000..d579f056ca3 --- /dev/null +++ b/doc/ref/states/all/salt.states.mssql_login.rst @@ -0,0 +1,5 @@ +salt.states.mssql_login module +============================== + +.. automodule:: salt.states.mssql_login + :members: diff --git a/doc/ref/states/all/salt.states.mssql_role.rst b/doc/ref/states/all/salt.states.mssql_role.rst new file mode 100644 index 00000000000..48aef6908b0 --- /dev/null +++ b/doc/ref/states/all/salt.states.mssql_role.rst @@ -0,0 +1,5 @@ +salt.states.mssql_role module +============================= + +.. automodule:: salt.states.mssql_role + :members: diff --git a/doc/ref/states/all/salt.states.mssql_user.rst b/doc/ref/states/all/salt.states.mssql_user.rst new file mode 100644 index 00000000000..ac335693ceb --- /dev/null +++ b/doc/ref/states/all/salt.states.mssql_user.rst @@ -0,0 +1,5 @@ +salt.states.mssql_user module +============================= + +.. automodule:: salt.states.mssql_user + :members: diff --git a/doc/ref/states/all/salt.states.nexus.rst b/doc/ref/states/all/salt.states.nexus.rst new file mode 100644 index 00000000000..41e6aa415b3 --- /dev/null +++ b/doc/ref/states/all/salt.states.nexus.rst @@ -0,0 +1,5 @@ +salt.states.nexus module +======================== + +.. automodule:: salt.states.nexus + :members: diff --git a/doc/ref/states/all/salt.states.pbm.rst b/doc/ref/states/all/salt.states.pbm.rst new file mode 100644 index 00000000000..e5b2d9a6b67 --- /dev/null +++ b/doc/ref/states/all/salt.states.pbm.rst @@ -0,0 +1,5 @@ +salt.states.pbm module +====================== + +.. automodule:: salt.states.pbm + :members: diff --git a/doc/ref/states/all/salt.states.saltutil.rst b/doc/ref/states/all/salt.states.saltutil.rst index f9e0360eb83..0ed2dd2f1a8 100644 --- a/doc/ref/states/all/salt.states.saltutil.rst +++ b/doc/ref/states/all/salt.states.saltutil.rst @@ -3,4 +3,4 @@ salt.states.saltutil ==================== .. automodule:: salt.states.saltutil - :members: \ No newline at end of file + :members: diff --git a/doc/ref/states/all/salt.states.wordpress.rst b/doc/ref/states/all/salt.states.wordpress.rst new file mode 100644 index 00000000000..ac5f78bd969 --- /dev/null +++ b/doc/ref/states/all/salt.states.wordpress.rst @@ -0,0 +1,5 @@ +salt.states.wordpress module +============================ + +.. automodule:: salt.states.wordpress + :members: diff --git a/doc/ref/states/all/salt.states.zabbix_usermacro.rst b/doc/ref/states/all/salt.states.zabbix_usermacro.rst new file mode 100644 index 00000000000..08c758551a6 --- /dev/null +++ b/doc/ref/states/all/salt.states.zabbix_usermacro.rst @@ -0,0 +1,5 @@ +salt.states.zabbix_usermacro module +=================================== + +.. automodule:: salt.states.zabbix_usermacro + :members: diff --git a/doc/ref/states/requisites.rst b/doc/ref/states/requisites.rst index fd03e6d9631..81855f7f997 100644 --- a/doc/ref/states/requisites.rst +++ b/doc/ref/states/requisites.rst @@ -68,7 +68,7 @@ first line in the stanza) or the ``- name`` parameter. - require: - pkg: vim -Glog matching in requisites +Glob matching in requisites ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 0.9.8 @@ -1039,8 +1039,8 @@ if the gluster commands return a 0 ret value. - /etc/crontab - 'entry1' -runas -~~~~~ +listen +~~~~~~ .. versionadded:: 2014.7.0 diff --git a/doc/ref/tokens/all/index.rst b/doc/ref/tokens/all/index.rst index 5b46333fa25..7e9f2ef9ef4 100644 --- a/doc/ref/tokens/all/index.rst +++ b/doc/ref/tokens/all/index.rst @@ -1,8 +1,8 @@ .. _all-salt.tokens: -============ -auth modules -============ +============= +token modules +============= .. currentmodule:: salt.tokens diff --git a/doc/security/index.rst b/doc/security/index.rst index ddde5b7a2ea..e2996f9d0f6 100644 --- a/doc/security/index.rst +++ b/doc/security/index.rst @@ -13,7 +13,6 @@ Security disclosure policy .. code-block:: text -----BEGIN PGP PUBLIC KEY BLOCK----- - Version: GnuPG/MacGPG2 v2.0.22 (Darwin) mQINBFO15mMBEADa3CfQwk5ED9wAQ8fFDku277CegG3U1hVGdcxqKNvucblwoKCb hRK6u9ihgaO9V9duV2glwgjytiBI/z6lyWqdaD37YXG/gTL+9Md+qdSDeaOa/9eg @@ -39,30 +38,43 @@ Security disclosure policy El1BLAg+m+1UpE1L7zJT1il6PqVyEFAWBxW46wXCCkGssFsvz2yRp0PDX8A6u4yq rTkt09uYht1is61joLDJ/kq3+6k8gJWkDOW+2NMrmf+/qcdYCMYXmrtOpg/wF27W GMNAkbdyzgeX/MbUBCGCMdzhevRuivOI5bu4vT5s3KdshG+yhzV45bapKRd5VN+1 - mZRquQINBFO15mMBEAC5UuLii9ZLz6qHfIJp35IOW9U8SOf7QFhzXR7NZ3DmJsd3 - f6Nb/habQFIHjm3K9wbpj+FvaW2oWRlFVvYdzjUq6c82GUUjW1dnqgUvFwdmM835 - 1n0YQ2TonmyaF882RvsRZrbJ65uvy7SQxlouXaAYOdqwLsPxBEOyOnMPSktW5V2U - IWyxsNP3sADchWIGq9p5D3Y/loyIMsS1dj+TjoQZOKSj7CuRT98+8yhGAY8YBEXu - 9r3I9o6mDkuPpAljuMc8r09Im6az2egtK/szKt4Hy1bpSSBZU4W/XR7XwQNywmb3 - wxjmYT6Od3Mwj0jtzc3gQiH8hcEy3+BO+NNmyzFVyIwOLziwjmEcw62S57wYKUVn - HD2nglMsQa8Ve0e6ABBMEY7zGEGStva59rfgeh0jUMJiccGiUDTMs0tdkC6knYKb - u/fdRqNYFoNuDcSeLEw4DdCuP01l2W4yY+fiK6hAcL25amjzc+yYo9eaaqTn6RAT - bzdhHQZdpAMxY+vNT0+NhP1Zo5gYBMR65Zp/VhFsf67ijb03FUtdw9N8dHwiR2m8 - vVA8kO/gCD6wS2p9RdXqrJ9JhnHYWjiVuXR+f755ZAndyQfRtowMdQIoiXuJEXYw - 6XN+/BX81gJaynJYc0uw0MnxWQX+A5m8HqEsbIFUXBYXPgbwXTm7c4IHGgXXdwAR - AQABiQIlBBgBAgAPBQJTteZjAhsMBQkHhh+AAAoJENVw8tNOoHk91rcQAIhxLv4g - duF/J1Cyf6Wixz4rqslBQ7DgNztdIUMjCThg3eB6pvIzY5d3DNROmwU5JvGP1rEw - hNiJhgBDFaB0J/y28uSci+orhKDTHb/cn30IxfuAuqrv9dujvmlgM7JUswOtLZhs - 5FYGa6v1RORRWhUx2PQsF6ORg22QAaagc7OlaO3BXBoiE/FWsnEQCUsc7GnnPqi7 - um45OJl/pJntsBUKvivEU20fj7j1UpjmeWz56NcjXoKtEvGh99gM5W2nSMLE3aPw - vcKhS4yRyLjOe19NfYbtID8m8oshUDji0XjQ1z5NdGcf2V1YNGHU5xyK6zwyGxgV - xZqaWnbhDTu1UnYBna8BiUobkuqclb4T9k2WjbrUSmTwKixokCOirFDZvqISkgmN - r6/g3w2TRi11/LtbUciF0FN2pd7rj5mWrOBPEFYJmrB6SQeswWNhr5RIsXrQd/Ho - zvNm0HnUNEe6w5YBfA6sXQy8B0Zs6pcgLogkFB15TuHIIIpxIsVRv5z8SlEnB7HQ - Io9hZT58yjhekJuzVQB9loU0C/W0lzci/pXTt6fd9puYQe1DG37pSifRG6kfHxrR - if6nRyrfdTlawqbqdkoqFDmEybAM9/hv3BqriGahGGH/hgplNQbYoXfNwYMYaHuB - aSkJvrOQW8bpuAzgVyd7TyNFv+t1kLlfaRYJ - =wBTJ + mZRqiQJVBBMBAgA/AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgBYhBIq+Tvzw + 9LJL/yqvkNVw8tNOoHk9BQJb0e5rBQkL3m8IAAoJENVw8tNOoHk9fzMP/ApQtkQD + BmoYEBTF6BH1bywzDw5OHpnBSLbuoYtA3gkhnm/83MzFDcGn22pgo2Fv0MuHltWI + G2oExzje7szmcM6Xg3ZTKapJ3/p2J+P33tkJA1LWpg+DdgdQlqrjlXKwEnikszuB + 9IMhbjoPeBzwiUtsBQmcwbVgwMzbscwoV5DJ/gLDCkgF4rp2uKEYAcBi8s9NGX6p + zQsb9Sb0/bKdCrszAcvUn4WYB6WbAPttvutYHtg/nZfXEeX/SgBueXo3lO9vzFlO + r3Zgk7WeucsEqa9Qo0VLOq28HykixM5mEJKsAQrNIqM1DqXgfDch8RJAHzgMBHFH + Qi9hJXk1/6OA2FPXQGcA9Td5Dt0i1Z7wMrAUMj3s9gNMVCD0hQqEKfUtpyV7KBAj + AO5j8Wr8KafnRm6czBCkcV0SRzHQSHdYyncozWwPgWOaRC9AY9fEDz8lBaSoB/C+ + dyO/xZMTWoaWqkHozVoHIrCc4CAtZTye/5mxFhq15Q1Iy/NjelrMTCD1kql1dNIP + oOgfOYl1xLMQIBwrrCrgeRIvxEgKRf9KOLbSrS7+3vOKoxf+LD4AQfLci8dFyH+I + t0Z43nk93yTOI82RTdz5GwUXIKcvGhsJ8bgNlGTxM1R/Sl8Sg8diE2PRAp/fk7+g + CwOM8VkeyrDM2k1cy64d8USkbR7YtT3otyFQuQINBFO15mMBEAC5UuLii9ZLz6qH + fIJp35IOW9U8SOf7QFhzXR7NZ3DmJsd3f6Nb/habQFIHjm3K9wbpj+FvaW2oWRlF + VvYdzjUq6c82GUUjW1dnqgUvFwdmM8351n0YQ2TonmyaF882RvsRZrbJ65uvy7SQ + xlouXaAYOdqwLsPxBEOyOnMPSktW5V2UIWyxsNP3sADchWIGq9p5D3Y/loyIMsS1 + dj+TjoQZOKSj7CuRT98+8yhGAY8YBEXu9r3I9o6mDkuPpAljuMc8r09Im6az2egt + K/szKt4Hy1bpSSBZU4W/XR7XwQNywmb3wxjmYT6Od3Mwj0jtzc3gQiH8hcEy3+BO + +NNmyzFVyIwOLziwjmEcw62S57wYKUVnHD2nglMsQa8Ve0e6ABBMEY7zGEGStva5 + 9rfgeh0jUMJiccGiUDTMs0tdkC6knYKbu/fdRqNYFoNuDcSeLEw4DdCuP01l2W4y + Y+fiK6hAcL25amjzc+yYo9eaaqTn6RATbzdhHQZdpAMxY+vNT0+NhP1Zo5gYBMR6 + 5Zp/VhFsf67ijb03FUtdw9N8dHwiR2m8vVA8kO/gCD6wS2p9RdXqrJ9JhnHYWjiV + uXR+f755ZAndyQfRtowMdQIoiXuJEXYw6XN+/BX81gJaynJYc0uw0MnxWQX+A5m8 + HqEsbIFUXBYXPgbwXTm7c4IHGgXXdwARAQABiQI8BBgBAgAmAhsMFiEEir5O/PD0 + skv/Kq+Q1XDy006geT0FAlvR7oMFCQvebyAACgkQ1XDy006geT2Hxw//Zha8j8Uc + 4B+DmHhZIvPmHp9aFI4DWhC7CBDrYKztBz42H6eX+UsBu4p+uBDKdW9xJH+Qt/zF + nf/zB5Bhc/wFceVRCAkWxPdiIQeo5XQGjZeORjle7E9iunTko+5q1q9I7IgqWYrn + jRmulDvRhO7AoUrqGACDrV6t0F1/XPB8seR2i6axFmFlt1qBHasRq11yksdgNYiD + KXaovf7csDGPGOCWEKMX7BFGpdK/dWdNYfH0Arfom0U5TqNfvGtP4yRPx2bcs7/1 + VXPj7IqhBgOtA9pwtMjFki8HGkqj7bB2ErFBOnSwqqNnNcbnhiO6D74SHVGAHhKZ + whaMPDg76EvjAezoLHg7KWYOyUkWJSLa+YoM9r4+PJuEuW/XuaZCNbrAhek+p3pD + ywhElvZe/2UFk619qKzwSbTzk7a90rxLQ2wwtd0vxAW/GyjWl4/kOMZhI5+LAk1l + REucE0fSQxzCTeXu2ObvFR9ic02IYGH3Koz8CrGReEI1J05041Y5IhKxdsvGOD2W + e7ymcblYW4Gz8eYFlLeNJkj/38R7qmNZ028XHzAZDCAWDiTFrnCoglyk+U0JRHfg + HTsdvoc8mBdT/s24LhnfAbpLizlrZZquuOF6NLQSkbuLtmIwf+h9ynEEJxEkGGWg + 7JqB1tMjNHLkRpveO/DTYB+iffpba1nCguk= + =OgRY -----END PGP PUBLIC KEY BLOCK----- The SaltStack Security Team is available at security@saltstack.com for diff --git a/doc/topics/beacons/index.rst b/doc/topics/beacons/index.rst index 602b3c622d5..1a6b79224fc 100644 --- a/doc/topics/beacons/index.rst +++ b/doc/topics/beacons/index.rst @@ -241,7 +241,7 @@ Add the following to ``/srv/reactor/revert.sls``: In addition to :ref:`setting ` ``disable_during_state_run: True`` for an inotify beacon whose reaction is to modify the watched file, it is important to ensure the state applied is - also :term:`idempotent`. + also :term:`idempotent `. .. note:: diff --git a/doc/topics/cloud/libvirt.rst b/doc/topics/cloud/libvirt.rst index 33099b0b334..959982d4d16 100644 --- a/doc/topics/cloud/libvirt.rst +++ b/doc/topics/cloud/libvirt.rst @@ -71,20 +71,20 @@ The profile can be realized now with a salt command: .. code-block:: bash - # salt-cloud -p centos7 my-centos7-clone + salt-cloud -p centos7 my-centos7-clone This will create an instance named ``my-centos7-clone`` on the cloud host. Also the minion id will be set to ``my-centos7-clone``. If the command was executed on the salt-master, its Salt key will automatically -be signed on the master. +be accepted on the master. Once the instance has been created with salt-minion installed, connectivity to it can be verified with Salt: .. code-block:: bash - # salt my-centos7-clone test.version + salt my-centos7-clone test.version Required Settings @@ -97,26 +97,76 @@ The following settings are always required for libvirt: provider: local-kvm # the domain to clone base_domain: base-centos7-64 - # how to obtain the IP address of the cloned instance - # ip-learning or qemu-agent - ip_source: ip-learning -The ``ip_source`` setting controls how the IP address of the cloned instance is determined. -When using ``ip-learning`` the IP is requested from libvirt. This needs a recent libvirt -version and may only work for NAT networks. Another option is to use ``qemu-agent`` this requires -that the qemu-agent is installed and configured to run at startup in the base domain. + +SSH Key Authentication +====================== +Instead of specifying a password, an authorized key can be used for the minion setup. Ensure that +the ssh user of your base image has the public key you want to use in ~/.ssh/authorized_keys. If +you want to use a non-root user you will likely want to configure salt-cloud to use sudo. + +An example using root: + +.. code-block:: yaml + + centos7: + provider: local-kvm + # the domain to clone + base_domain: base-centos7-64 + ssh_username: root + private_key: /path/to/private/key + +An example using a non-root user: + +.. code-block:: yaml + + centos7: + provider: local-kvm + # the domain to clone + base_domain: base-centos7-64 + ssh_username: centos + private_key: /path/to/private/key + sudo: True + sudo_password: "--redacted--" Optional Settings ================= .. code-block:: yaml - # Username and password - ssh_username: root - password: my-secret-password + centos7: + # ssh settings + # use forwarded agent instead of a local key + ssh_agent: True + ssh_port: 4910 - # Cloning strategy: full or quick - clone_strategy: quick + # credentials + ssh_username: root + # password will be used for sudo if defined, use sudo_password if using ssh keys + password: my-secret-password + private_key: /path/to/private/key + sudo: True + sudo_password: "--redacted--" + + # bootstrap options + deploy_command: sh /tmp/.saltcloud/deploy.sh + script_args: -F + + # minion config + grains: + sushi: more tasty + # point at the another master at another port + minion: + master: 192.168.16.1 + master_port: 5506 + + # libvirt settings + # clone_strategy: [ quick | full ] # default is full + clone_strategy: quick + # ip_source: [ ip-learning | qemu-agent ] # default is ip-learning + ip_source: qemu-agent + # validate_xml: [ false | true ] # default is true + validate_xml: false The ``clone_strategy`` controls how the clone is done. In case of ``full`` the disks are copied creating a standalone clone. If ``quick`` is used the disks of the base domain @@ -126,3 +176,13 @@ the expense of slower write performance. The quick strategy has a number of requ * The disks must be of type qcow2 * The base domain must be turned off * The base domain must not change after creating the clone + +The ``ip_source`` setting controls how the IP address of the cloned instance is determined. +When using ``ip-learning`` the IP is requested from libvirt. This needs a recent libvirt +version and may only work for NAT/routed networks where libvirt runs the dhcp server. +Another option is to use ``qemu-agent`` this requires that the qemu-agent is installed and +configured to run at startup in the base domain. + +The ``validate_xml`` setting is available to disable xml validation by libvirt when cloning. + +See also :mod:`salt.cloud.clouds.libvirt` diff --git a/doc/topics/cloud/openstack.rst b/doc/topics/cloud/openstack.rst index ccb1fefba83..f41a566e499 100644 --- a/doc/topics/cloud/openstack.rst +++ b/doc/topics/cloud/openstack.rst @@ -2,4 +2,4 @@ Getting Started with Openstack ============================== -.. automodule:: salt.cloud.clouds.openstack +See :mod:`salt.cloud.clouds.openstack` diff --git a/doc/topics/cloud/releases/index.rst b/doc/topics/cloud/releases/index.rst index cf593bb75dc..05287513f1c 100644 --- a/doc/topics/cloud/releases/index.rst +++ b/doc/topics/cloud/releases/index.rst @@ -7,6 +7,7 @@ Legacy salt-cloud Release Notes =============================== .. versionchanged:: 2014.1.0 + As of Salt's 2014.1.0 release salt-cloud is part of mainline Salt. Future salt-cloud release notes will be included in Salt's regular release notes. diff --git a/doc/topics/development/changelog.rst b/doc/topics/development/changelog.rst new file mode 100644 index 00000000000..c47deaa956a --- /dev/null +++ b/doc/topics/development/changelog.rst @@ -0,0 +1,87 @@ +.. _changelog: + +========= +Changelog +========= + +With the addition of `SEP 01`_ the `keepachangelog`_ format was introduced into +our CHANGELOG.md file. The Salt project is using the `towncrier`_ tool to manage +the Changelog.md file. The reason this tool was added to manage the changelog +was because we were previously managing the file manually and it would cause +many merge conflicts. This tool allows us to add changelog entries into separate +files and before a release we simply need to run ``towncrier --version=`` +for it to compile the changelog correctly. + + +.. _add-changelog: + +How do I add a changelog entry +------------------------------ + +To add a changelog entry you will need to add a file in the `changelog` directory. +The file name should follow the syntax ``.``. + +The types are in alignment with keepachangelog: + + removed: + any features that have been removed + + deprecated: + any features that will soon be removed + + changed: + any changes in current existing features + + fixed: + any bug fixes + + added: + any new features added + +For example if you are fixing a bug for issue number #1234 your filename would +look like this: changelog/1234.fixed. The contents of the file should contain +a summary of what you are fixing. + +This does require that an issue be linked to all of the types above. + +.. _generate-changelog: + +How to generate the changelog +----------------------------- + +This step is only used when we need to generate the changelog right before releasing. +You should NOT run towncrier on your PR, unless you are preparing the final PR +to update the changelog before a release. + +You can run the `towncrier` tool directly or you can use nox to help run the command +and ensure towncrier is installed in a virtual environment. The instructions below +will detail both approaches. + +If you want to see what output towncrier will produce before generating the change log +you can run towncrier in draft mode: + +.. code-block:: bash + + towncrier --draft --version=3001 + +.. code-block:: bash + + nox -e 'changelog(draft=True)' -- 3000.1 + +Version will need to be set to whichever version we are about to release. Once you are +confident the draft output looks correct you can now generate the changelog by running: + +.. code-block:: bash + + towncrier --version=3001 + +.. code-block:: bash + + nox -e 'changelog(draft=False)' -- 3000.1 + +After this is run towncrier will automatically remove all the files in the changelog directory. + + +.. _`SEP 01`: https://github.com/saltstack/salt-enhancement-proposals/pull/2 +.. _`keepachangelog`: https://keepachangelog.com/en/1.0.0/ +.. _`towncrier`: https://pypi.org/project/towncrier/ diff --git a/doc/topics/development/contributing.rst b/doc/topics/development/contributing.rst index 3c3863426fe..7e3a3ef452d 100644 --- a/doc/topics/development/contributing.rst +++ b/doc/topics/development/contributing.rst @@ -52,6 +52,11 @@ processes that used to be manual. Rather than having to remember to run several different tools before you commit, you only have to run ``git commit``, and you will be notified about style and lint issues before you ever open a PR. +.. warning:: + Currently there is an issue with the pip-tools-compile pre-commit hook on windows. + The details around this issue are included here: + https://github.com/saltstack/salt/issues/56642. + Please ensure you export ``SKIP=pip-tools-compile`` to skip pip-tools-compile. Salt Coding Style ----------------- @@ -139,7 +144,7 @@ Fork a Repo Guide_>`_ and is well worth reading. GOOD - .. code-block:: + .. code-block:: bash Fix broken things in file1 and file2 @@ -157,7 +162,7 @@ Fork a Repo Guide_>`_ and is well worth reading. BAD - .. code-block:: + .. code-block:: bash Fixes broken things @@ -259,7 +264,7 @@ Fork a Repo Guide_>`_ and is well worth reading. take a couple of hours. Depending on your GitHub notification settings you may also receive an email message about the test results. - Test progress and results can be found at http://jenkins.saltstack.com/. + Test progress and results can be found at https://jenkinsci.saltstack.com/. .. _which-salt-branch: diff --git a/doc/topics/development/conventions/documentation.rst b/doc/topics/development/conventions/documentation.rst index 4317bd5a8be..d29ee82f26c 100644 --- a/doc/topics/development/conventions/documentation.rst +++ b/doc/topics/development/conventions/documentation.rst @@ -306,7 +306,7 @@ Documentation Changes and Fixes Documentation changes and fixes should be made against the earliest supported release branch that the update applies to. The practice of updating a release branch instead of making all documentation changes against Salt's main, default -branch, ``develop``, is necessary in order for the docs to be as up-to-date as +branch, ``master``, is necessary in order for the docs to be as up-to-date as possible when the docs are built. The workflow mentioned above is also in line with the recommendations outlined diff --git a/doc/topics/development/conventions/release.rst b/doc/topics/development/conventions/release.rst index ffe22b3db19..1e1306c9e3c 100644 --- a/doc/topics/development/conventions/release.rst +++ b/doc/topics/development/conventions/release.rst @@ -2,8 +2,8 @@ Salt Release Process ==================== -The goal for Salt projects is to cut a new feature release every six -months. This document outlines the process for these releases, and the +The goal for Salt projects is to cut a new feature release every three to +four months. This document outlines the process for these releases, and the subsequent bug fix releases which follow. @@ -11,44 +11,21 @@ Feature Release Process ======================= When a new release is ready to be cut, the person responsible for cutting the -release will follow the following steps (written using the 2019.2.0 release as an +release will follow the following steps (written using the 3000 release as an example): #. Create first public draft of release notes with major features. #. Remove any deprecations for the upcoming release. -#. Notify salt-users and salt-announce google groups when the feature freeze - branch creation will occur so they can try to get their work merged. -#. Create QA test plan. Review features planned for the release and determine if - there is sufficient test coverage. #. Ensure all required features are merged. -#. Complete one last merge forward from the previous branch. -#. Create feature release branch with the name of the release. (ex. fluorine) #. Create issue to start the process of deprecating for the next feature release. -#. Create jenkins jobs to test the new feature release branch. -#. Inform salt-users and salt-announce google groups feature branch and - freeze is complete. -#. Add new feature branch to salt-jenkins repo and the kitchen yaml file. -#. Fix tests failing in jenkins test runs. -#. Finalize QA test plan and add all required tests. #. Run through a manual test run based off of the head of the feature branch. -#. Convert the feature release branch to the version number. For example (v2019.2) - This is based off of the year and month that is planned to release. -#. Migrate both the jenkins test jobs and salt-jenkins repo to the new branch number. -#. Notify salt-users and salt-announce google groups of the new version branch - number and migrate any PRs to the new branch. -#. Delete old feature release branch name (ex. fluorine) #. Update all name references to version number in the docs. For example - all fluorine references in the docs needs to be moved to v2019.2.0 -#. Create RC release branch. (ex. 2019.2.0.rc) -#. Create new jenkins test jobs with new RC release branch -#. Notify salt-users and salt-announce google groups of the new RC branch. -#. Fix tests failing in jenkins test runs. + all neon references in the docs needs to be moved to v3000 #. Review the release notes with major features. #. Generate the new man pages for the release. -#. Create internal RC tag for testing. +#. Create internal RC tag for testing from the head of the master branch. #. Build latest windows, mac, ubuntu, debian and redhat packages. #. Run manual and package tests against new RC packages. -#. Update release candidate docs with the new version. (ex. 2019.2.0rc1) #. Push the internal tag live to salt's repo. #. Publish release archive to pypi based off tag. #. Push the RC packages live. @@ -56,15 +33,14 @@ example): #. Triage incoming issues based on the new RC release. #. Fix RC issues once they are categorized as a release blocker. #. Depending on the issues found during the RC process make a decesion - on whether to release based off the RC or go through another RC process, - repeating the steps starting at ensuring the tests are not failing. + on whether to release based off the RC or go through another RC process #. If a RC is categorized as stable, build all required packages. #. Test all release packages. #. Test links from `repo.saltstack.com`_. #. Update installation instructions with new release number at `repo.saltstack.com`_. -#. Update and build docs to include new version (2019.2) as the latest. +#. Update and build docs to include new version (3000) as the latest. #. Pre-announce on salt-users google group that we are about to update our repo. -#. Publish release (v2019.2.0) archive to pypi based off tag. +#. Publish release (v3000) archive to pypi based off tag. #. Publish all packages live to repo. #. Publish the docs. #. Create release at `github`_ @@ -73,39 +49,39 @@ example): community channel. -Maintenance and Bugfix Releases -=============================== +Bugfix Releases +=============== -Once a feature release branch has been cut from ``develop``, the branch moves -into a "feature freeze" state. The new release branch enters the ``merge-forward`` -chain and only bugfixes should be applied against the new branch. Once major bugs -have been fixed, a bugfix release can be cut: +Once a feature release branch has been cut from the ``master`` branch, if +serious bugs or a CVE is found for the most recent release a bugfix release +will need to be cut. A temporary branch will be created based off of the previous +release tag. For example, if it is determined that a 3000.1 release needs to occur +a 3000.1 branch will be created based off of the v3000 tag. The fixes that need +to go into 3000.1 will be added and merged into this branch. Here are the steps +for a bugfix release. #. Ensure all required bug fixes are merged. -#. Inform salt-users and salt-announce we are going to branch for the release. -#. Complete one last merge forward from the previous branch. -#. Create release branch with the version of the release. (ex. 2019.2.1) +#. Create release branch with the version of the release. (ex. 3000.1) #. Create jenkins jobs that test the new release branch. -#. Fix tests failing in jeknins test runs. #. Run through a manual test run based off of the head of the branch. #. Generate the new man pages for the release. -#. Create internal tag for testing.(ex v2019.2.1) +#. Create internal tag for testing.(ex v3000.1) #. Build all release packages. #. Run manual and package tests against new packages. #. Update installation instructions with new release number at `repo.saltstack.com`_. -#. Update and build docs to include new version. (ex. 2019.2.1) +#. Update and build docs to include new version. (ex. 3000.1) #. Pre-announce on salt-users google groups that we are about to update our repo. #. Push the internal tag live to salt's repo. #. Publish release archive to pypi based off tag. #. Push the packages live. -#. Publish release (v2019.2.1) archive to pypi based off tag. +#. Publish release (v3000) archive to pypi based off tag. #. Publish all packages live to repo. #. Publish the docs. #. Create release at `github`_ #. Update win-repo-ng with new salt versions. #. Announce release is live to irc, salt-users, salt-announce and release slack channel. -For more information about the difference between the ``develop`` branch and +For more information about the difference between the ``master`` branch and bugfix release branches, please refer to the :ref:`Which Salt Branch? ` section of Salt's :ref:`Contributing ` documentation. diff --git a/doc/topics/development/tests/index.rst b/doc/topics/development/tests/index.rst index c0d36acbfc0..b052e190526 100644 --- a/doc/topics/development/tests/index.rst +++ b/doc/topics/development/tests/index.rst @@ -359,7 +359,7 @@ As soon as the pull request is merged, the changes will be added to the next branch test run on Jenkins. For a full list of currently running test environments, go to -http://jenkins.saltstack.com. +https://jenkinsci.saltstack.com. Using Salt-Cloud on Jenkins diff --git a/doc/topics/grains/index.rst b/doc/topics/grains/index.rst index 8cfe7d1da97..008121521bc 100644 --- a/doc/topics/grains/index.rst +++ b/doc/topics/grains/index.rst @@ -102,7 +102,7 @@ same way as in the above example, only without a top-level ``grains:`` key: Matching Grains in the Top File =============================== -With correctly configured grains on the Minion, the :term:`top file` used in +With correctly configured grains on the Minion, the :term:`top file ` used in Pillar or during Highstate can be made very efficient. For example, consider the following configuration: diff --git a/doc/topics/installation/index.rst b/doc/topics/installation/index.rst index 0da5de29f3c..5a6acdae24a 100644 --- a/doc/topics/installation/index.rst +++ b/doc/topics/installation/index.rst @@ -120,7 +120,7 @@ Salt should run on any Unix-like platform so long as the dependencies are met. Salt defaults to the `ZeroMQ`_ transport. The ``--salt-transport`` installation -option is available, but currently only supports the ``szeromq`` option. This +option is available, but currently only supports the ``zeromq`` option. This may be expanded in the future. .. code-block:: bash diff --git a/doc/topics/installation/suse.rst b/doc/topics/installation/suse.rst index 75e97cbf053..9894d33b4ec 100644 --- a/doc/topics/installation/suse.rst +++ b/doc/topics/installation/suse.rst @@ -134,20 +134,10 @@ For openSUSE 15.1 run the following as root: SUSE Linux Enterprise --------------------- -For SLE 12 run the following as root: +For SLE 15 and above run the following as root: .. code-block:: bash - zypper addrepo http://download.opensuse.org/repositories/systemsmanagement:/saltstack/SLE_12/systemsmanagement:saltstack.repo - zypper refresh - zypper install salt salt-minion salt-master - -For SLE 11 SP4 run the following as root: - -.. code-block:: bash - - zypper addrepo http://download.opensuse.org/repositories/systemsmanagement:/saltstack/SLE_11_SP4/systemsmanagement:saltstack.repo - zypper refresh zypper install salt salt-minion salt-master Now go to the :ref:`Configuring Salt` page. diff --git a/doc/topics/releases/3000.1.rst b/doc/topics/releases/3000.1.rst index e078145db74..086a2cc10db 100644 --- a/doc/topics/releases/3000.1.rst +++ b/doc/topics/releases/3000.1.rst @@ -9,17 +9,192 @@ Version 3000.1 is a bugfix release for :ref:`3000 `. Statistics ========== -- Total Merges: **39** -- Total Issue References: **14** -- Total PR References: **40** - -- Contributors: **15** (`Ch3LL`_, `UtahDave`_, `cmcmarrow`_, `dwoz`_, `frogunder`_, `garethgreenaway`_, `lorengordon`_, `mchugh19`_, `oeuftete`_, `raddessi`_, `s0undt3ch`_, `sjorge`_, `terminalmage`_, `twangboy`_, `waynew`_) +- Total Merges: **53** +- Total Issue References: **15** +- Total PR References: **54** +- Contributors: **16** (`Ch3LL`_, `UtahDave`_, `bryceml`_, `cmcmarrow`_, `dwoz`_, `frogunder`_, `garethgreenaway`_, `lorengordon`_, `mchugh19`_, `oeuftete`_, `raddessi`_, `s0undt3ch`_, `sjorge`_, `terminalmage`_, `twangboy`_, `waynew`_) Changelog for v3000..v3000.1 ============================ -*Generated at: 2020-03-13 17:24:15 UTC* +*Generated at: 2020-03-27 16:48:41 UTC* + +* **PR** `#56455`_: (`s0undt3ch`_) Fix gitpython windows requirement + @ *2020-03-27 16:31:57 UTC* + + * c5a700e01e Merge pull request `#56455`_ from s0undt3ch/hotfix/gitpython + + * d9791c393f Revert and fix windows requirements + + * 4b573c1c94 Revert "Fix win deps" + +* **PR** `#56446`_: (`frogunder`_) 3000.1 releasenotes updates + @ *2020-03-24 20:28:23 UTC* + + * 7ba36325d9 Merge pull request `#56446`_ from frogunder/releasenotes_3000.1_updates + + * 6b47f474af 3000.1 releasenotes updates + +* **PR** `#56435`_: (`twangboy`_) Update mac build scripts + @ *2020-03-24 19:47:40 UTC* + + * 4d2bc7004d Merge pull request `#56435`_ from twangboy/update_mac_build_scripts + + * 5d244b0aa6 Merge branch 'master' into update_mac_build_scripts + + * 8fdf52a243 Update gitpython to 2.1.15 + + * afcedc5232 Remove --ignore-installed + + * e6cc2a6192 Separate pyobjc requirements + + * 262ad2e98a Compile static requirements + + * 224f72d0f2 Update noxfile and .precommit + + * 68a36f2e37 Add req_ext.txt + + * 5851a5c2cd Roll back changes, fix dependencies + +* **ISSUE** `#56433`_: (`Ch3LL`_) integration.states.test_pip_state.PipStateTest.test_pip_installed_specific_env failing on MAC (refs: `#56436`_) + +* **PR** `#56436`_: (`Ch3LL`_) Fix `#56433`_ - test_pip_installed_specific_env + @ *2020-03-24 19:47:20 UTC* + + * 8a8ae8ebe4 Merge pull request `#56436`_ from Ch3LL/pip_custom_pypi + + * 55655ff96c Merge branch 'master' into pip_custom_pypi + +* **PR** `#56423`_: (`Ch3LL`_) Update changelog with package fixes + @ *2020-03-24 16:42:17 UTC* + + * 3a993d73a9 Merge pull request `#56423`_ from Ch3LL/changelog_again + + * 963c16e1a7 update pr number for mac build changes in changelog + + * 83e22b77c7 Update changelog with package fixes + +* **PR** `#56417`_: (`twangboy`_) Update mac build scripts + @ *2020-03-23 18:45:34 UTC* + + * d2a5bd8add Merge pull request `#56417`_ from twangboy/update_mac_build_scripts + + * 3bda8ddb82 Update noxfile and pre-commit + + * de58c52d66 Merge branch 'update_mac_build_scripts' of https://github.com/twangboy/salt into update_mac_build_scripts + + * e7f08d5349 Update static requirements + + * a53977de5b Merge branch 'update_mac_build_scripts' of github.com:twangboy/salt into update_mac_build_scripts + + * 04e5cde9dd `pkg/osx/req_ext.txt` no longer exists + + * be4a272d07 Update static requirements + + * 02dfe4119c Add pyobjc in its own requirements file + + * 6b2ac2be64 Remove pyobjc, enforce pip install location + + * 30ca5d04ec Remove cryptography, it gets installed by pyopenssl + + * fda8abf4a8 Update static requirements + + * 732ecc83c1 Update pre-commit config + + * 1fa9864e3d Don't set a version for pyopenssl + + * 461b198978 Update dependencies, remove req_ext.txt + +* **PR** `#56418`_: (`Ch3LL`_) Ensure version.py included before we install + @ *2020-03-23 18:27:46 UTC* + + * 74575a6993 Merge pull request `#56418`_ from Ch3LL/egg_version + + * 85d7c784ef Fix integration setup egg test + + * a8a22a4040 Ensure version.py included before we install + + * 86fe450c82 Fix `#56433`_ - test_pip_installed_specific_env + +* **PR** `#56403`_: (`frogunder`_) update 3000.1 releasenotes + @ *2020-03-17 23:50:21 UTC* + + * 249367b462 Merge pull request `#56403`_ from frogunder/update_releasenotes_3000.1 + + * 9d972c96e8 update 3000.1 releasenotes + +* **PR** `#56398`_: (`Ch3LL`_) Add additional PRs to 3000.1 changelog + @ *2020-03-17 18:08:15 UTC* + + * 0de5c1e136 Merge pull request `#56398`_ from Ch3LL/change_3000.1 + + * 79c337b3db Add additional PRs to 3000.1 changelog + +* **PR** `#56376`_: (`twangboy`_) Fix win deps + @ *2020-03-17 17:02:46 UTC* + + * 5ac09decb9 Merge pull request `#56376`_ from twangboy/fix_win_deps + + * 6c83beeb9e Fix win deps + +* **PR** `#56378`_: (`Ch3LL`_) Include _version.py if building wheel + @ *2020-03-17 17:01:33 UTC* + + * e72a8d2cbc Merge pull request `#56378`_ from Ch3LL/wheel_version + + * 22cccd2107 Use virtualenv test helper that already exists and fix setup.py + + * 293b1fddf2 cleanup code + + * ddfb065bfb add bdist_wheel test + + * fceff0287c ensure name is included in new version + + * 0cbf6d3246 Ensure SaltStackVersion attributes in _version.py correct + + * 39cdf5382d Include _version.py if building wheel + +* **PR** `#56387`_: (`bryceml`_) update gpg key expiration date + @ *2020-03-17 16:59:30 UTC* + + * 6a9326fb70 Merge pull request `#56387`_ from bryceml/update_gpg_key + + * b74b26ccf3 update gpg key expiration date + +* **PR** `#55822`_: (`cmcmarrow`_) fix_Indefinitely_code + @ *2020-03-16 17:34:11 UTC* + + * fcee692c4a Merge pull request `#55822`_ from cmcmarrow/win_task_repeat + + * e257fb2804 Merge branch 'master' into win_task_repeat + + * 07cada0f8f Merge branch 'master' into win_task_repeat + + * 4b80301338 Merge branch 'master' into win_task_repeat + + * 9df94569c4 Merge branch 'master' into win_task_repeat + + * 22a2d0b9ec Merge branch 'master' into win_task_repeat + + * dee9c134e7 Merge branch 'master' into win_task_repeat + + * beaf05d514 Update test_win_task.py + + * 6e923f75fc Merge branch 'master' into win_task_repeat + + * 5839da81ee add test + + * 2aa6338ed0 fix Indefinitely code + +* **PR** `#56373`_: (`frogunder`_) update 3000.1 releasenotes + @ *2020-03-13 18:58:43 UTC* + + * c11ef89200 Merge pull request `#56373`_ from frogunder/releasenotes_3000.1 + + * a5ca8b5277 Fix doc test errors + + * 47e483187e update 3000.1 releasenotes * **PR** `#56365`_: (`Ch3LL`_) Update 3000.1 changelog @ *2020-03-13 17:21:02 UTC* @@ -405,7 +580,7 @@ Changelog for v3000..v3000.1 * **ISSUE** `#56121`_: (`githubcdr`_) salt-minion broken after upgrade to 3000 (refs: `#56143`_) -* **ISSUE** `#51854`_: (`Oloremo`_) Fluorine: minion_pillar_cache: True leads to exception (refs: `#52195`_, `#56143`_) +* **ISSUE** `#51854`_: (`Oloremo`_) Fluorine: minion_pillar_cache: True leads to exception (refs: `#56143`_, `#52195`_) * **PR** `#56143`_: (`waynew`_) Use encoding when caching pillar data @ *2020-03-10 01:33:37 UTC* @@ -478,6 +653,7 @@ Changelog for v3000..v3000.1 .. _`#52195`: https://github.com/saltstack/salt/pull/52195 .. _`#53152`: https://github.com/saltstack/salt/issues/53152 .. _`#55185`: https://github.com/saltstack/salt/issues/55185 +.. _`#55822`: https://github.com/saltstack/salt/pull/55822 .. _`#55888`: https://github.com/saltstack/salt/pull/55888 .. _`#55894`: https://github.com/saltstack/salt/pull/55894 .. _`#55906`: https://github.com/saltstack/salt/pull/55906 @@ -528,9 +704,24 @@ Changelog for v3000..v3000.1 .. _`#56358`: https://github.com/saltstack/salt/pull/56358 .. _`#56360`: https://github.com/saltstack/salt/pull/56360 .. _`#56365`: https://github.com/saltstack/salt/pull/56365 +.. _`#56373`: https://github.com/saltstack/salt/pull/56373 +.. _`#56376`: https://github.com/saltstack/salt/pull/56376 +.. _`#56378`: https://github.com/saltstack/salt/pull/56378 +.. _`#56387`: https://github.com/saltstack/salt/pull/56387 +.. _`#56398`: https://github.com/saltstack/salt/pull/56398 +.. _`#56403`: https://github.com/saltstack/salt/pull/56403 +.. _`#56417`: https://github.com/saltstack/salt/pull/56417 +.. _`#56418`: https://github.com/saltstack/salt/pull/56418 +.. _`#56423`: https://github.com/saltstack/salt/pull/56423 +.. _`#56433`: https://github.com/saltstack/salt/issues/56433 +.. _`#56435`: https://github.com/saltstack/salt/pull/56435 +.. _`#56436`: https://github.com/saltstack/salt/pull/56436 +.. _`#56446`: https://github.com/saltstack/salt/pull/56446 +.. _`#56455`: https://github.com/saltstack/salt/pull/56455 .. _`Ch3LL`: https://github.com/Ch3LL .. _`Oloremo`: https://github.com/Oloremo .. _`UtahDave`: https://github.com/UtahDave +.. _`bryceml`: https://github.com/bryceml .. _`cmcmarrow`: https://github.com/cmcmarrow .. _`dwoz`: https://github.com/dwoz .. _`finalduty`: https://github.com/finalduty diff --git a/doc/topics/releases/3000.rst b/doc/topics/releases/3000.rst index 9affa1549c2..baf03d9b4e9 100644 --- a/doc/topics/releases/3000.rst +++ b/doc/topics/releases/3000.rst @@ -555,7 +555,7 @@ the ``engine_module`` parameter in the engine configuration. host: localhost port: 24224 - .. code-block:: + .. code-block:: none @type forward diff --git a/doc/topics/releases/sodium.rst b/doc/topics/releases/sodium.rst index 407a12d78bc..4bb34f8dd25 100644 --- a/doc/topics/releases/sodium.rst +++ b/doc/topics/releases/sodium.rst @@ -16,3 +16,64 @@ also support the syntax used in :py:mod:`module.run `. The old syntax for the mine_function - as a dict, or as a list with dicts that contain more than exactly one key - is still supported but discouraged in favor of the more uniform syntax of module.run. + + +New Grains +========== + +systempath +---------- + +This grain provides the same information as the ``path`` grain, only formatted +as a list of directories. + + +Salt-SSH updates +================ + +ssh_pre_flight +-------------- + +A new Salt-SSH roster option `ssh_pre_flight` has been added. This enables you to run a +script before Salt-SSH tries to run any commands. You can set this option in the roster +for a specific minion or use the `roster_defaults` to set it for all minions. + +Example for setting `ssh_pre_flight` for specific host in roster file + +.. code-block:: yaml + + minion1: + host: localhost + user: root + passwd: P@ssword + ssh_pre_flight: /srv/salt/pre_flight.sh + +Example for setting `ssh_pre_flight` using roster_defaults, so all minions +run this script. + +.. code-block:: yaml + + roster_defaults: + ssh_pre_flight: /srv/salt/pre_flight.sh + +The `ssh_pre_flight` script will only run if the thin dir is not currently on the +minion. If you want to force the script to run you have the following options: + +* Wipe the thin dir on the targeted minion using the -w arg. +* Set ssh_run_pre_flight to True in the config. +* Run salt-ssh with the --pre-flight arg. + +set_path +-------- + +A new salt-ssh roster option `set_path` has been added. This allows you to set +the path environment variable used to run the salt-ssh command on the target minion. +You can set this setting in your roster file like so: + +.. code-block:: yaml + + minion1: + host: localhost + user: root + passwd: P@ssword + set_path: '$PATH:/usr/local/bin/' diff --git a/doc/topics/ssh/index.rst b/doc/topics/ssh/index.rst index fc4450b1536..a21f4c63eb7 100644 --- a/doc/topics/ssh/index.rst +++ b/doc/topics/ssh/index.rst @@ -242,6 +242,59 @@ Boolean-style options should be specified in their YAML representation. At last you can create ``~/.salt/Saltfile`` and ``salt-ssh`` will automatically load it by default. +Advanced options with salt-ssh +============================== + +Salt's ability to allow users to have custom grains and custom modules +is also applicable to using salt-ssh. This is done through first packing +the custom grains into the thin tarball before it is deployed on the system. + +For this to happen, the ``config`` file must be explicit enough to indicate +where the custom grains are located on the machine like so: + +.. code-block:: yaml + + file_client: local + file_roots: + base: + - /home/user/.salt + - /home/user/.salt/_states + - /home/user/.salt/_grains + module_dirs: + - /home/user/.salt + pillar_roots: + base: + - /home/user/.salt/_pillar + root_dir: /tmp/.salt-root + +It's better to be explicit rather than implicit in this situation. This will +allow urls all under `salt://` to be resolved such as `salt://_grains/custom_grain.py`. + +One can confirm this action by executing a properly setup salt-ssh minion with +`salt-ssh minion grains.items`. During this process, a `saltutil.sync_all` is +ran to discover the thin tarball and then consumed. Output similar to this +indicates a successful sync with custom grains. + +.. code-block:: yaml + + local: + ---------- + ... + executors: + grains: + - grains.custom_grain + log_handlers: + ... + +This is especially important when using a custom `file_roots` that differ from +`/etc/salt/`. + +.. note:: + + Please see https://docs.saltstack.com/en/latest/topics/grains/ for more + information on grains and custom grains. + + Debugging salt-ssh ================== diff --git a/doc/topics/ssh/roster.rst b/doc/topics/ssh/roster.rst index f9d1f0dfd29..6a0dc8d354b 100644 --- a/doc/topics/ssh/roster.rst +++ b/doc/topics/ssh/roster.rst @@ -61,6 +61,31 @@ The information which can be stored in a roster ``target`` is the following: # components. Defaults to /tmp/salt-. cmd_umask: # umask to enforce for the salt-call command. Should be in # octal (so for 0o077 in YAML you would do 0077, or 63) + ssh_pre_flight: # Path to a script that will run before all other salt-ssh + # commands. Will only run the first time when the thin dir + # does not exist, unless --pre-flight is passed to salt-ssh + # command or ssh_run_pre_flight is set to true in the config + # Added in Sodium Release. + set_path: # Set the path environment variable, to ensure the expected python + # binary is in the salt-ssh path, when running the command. + # Example: '$PATH:/usr/local/bin/'. Added in Sodium Release. + + +.. _ssh_pre_flight: + +ssh_pre_flight +-------------- + +A Salt-SSH roster option `ssh_pre_flight` was added in the Sodium release. This enables +you to run a script before Salt-SSH tries to run any commands. You can set this option +in the roster for a specific minion or use the `roster_defaults` to set it for all minions. +This script will only run if the thin dir is not currently on the minion. This means it will +only run on the first run of salt-ssh or if you have recently wiped out your thin dir. If +you want to intentionally run the script again you have a couple of options: + +* Wipe out your thin dir by using the -w salt-ssh arg. +* Set ssh_run_pre_flight to True in the config +* Run salt-ssh with the --pre-flight arg. .. _roster_defaults: diff --git a/doc/topics/targeting/compound.rst b/doc/topics/targeting/compound.rst index 868667f7c5a..14ee06773e9 100644 --- a/doc/topics/targeting/compound.rst +++ b/doc/topics/targeting/compound.rst @@ -6,7 +6,7 @@ Compound matchers Compound matchers allow very granular minion targeting using any of Salt's matchers. The default matcher is a :mod:`glob ` match, just as -with CLI and :term:`top file` matching. To match using anything other than a +with CLI and :term:`top file ` matching. To match using anything other than a glob, prefix the match string with the appropriate letter from the table below, followed by an ``@`` sign. @@ -34,7 +34,7 @@ matches the :mod:`regular expression ` ``web-dc1-srv.*``: salt -C 'webserv* and G@os:Debian or E@web-dc1-srv.*' test.version -That same example expressed in a :term:`top file` looks like the following: +That same example expressed in a :term:`top file ` looks like the following: .. code-block:: yaml diff --git a/doc/topics/targeting/globbing.rst b/doc/topics/targeting/globbing.rst index fd59f0bfcf9..73e0bf035c3 100644 --- a/doc/topics/targeting/globbing.rst +++ b/doc/topics/targeting/globbing.rst @@ -11,7 +11,7 @@ configuration setting. .. tip:: minion id and minion keys - The :term:`minion id` is used to generate the minion's public/private keys + The :term:`minion id ` is used to generate the minion's public/private keys and if it ever changes the master must then accept the new key as though the minion was a new host. @@ -19,8 +19,8 @@ Globbing ======== The default matching that Salt utilizes is :py:mod:`shell-style globbing -` around the :term:`minion id`. This also works for states -in the :term:`top file`. +` around the :term:`minion id `. This also works for states +in the :term:`top file `. .. note:: @@ -83,7 +83,7 @@ Match both ``web1-prod`` and ``web1-devel`` minions: salt -E 'web1-(prod|devel)' test.version -When using regular expressions in a State's :term:`top file`, you must specify +When using regular expressions in a State's :term:`top file `, you must specify the matcher as the first option. The following example executes the contents of ``webserver.sls`` on the above-mentioned minions. diff --git a/doc/topics/targeting/index.rst b/doc/topics/targeting/index.rst index c954e503865..ad4cd717cd5 100644 --- a/doc/topics/targeting/index.rst +++ b/doc/topics/targeting/index.rst @@ -12,7 +12,7 @@ For example the command ``salt web1 apache.signal restart`` to restart the Apache httpd server specifies the machine ``web1`` as the target and the command will only be run on that one minion. -Similarly when using States, the following :term:`top file` specifies that only +Similarly when using States, the following :term:`top file ` specifies that only the ``web1`` minion should execute the contents of ``webserver.sls``: .. code-block:: yaml diff --git a/doc/topics/targeting/nodegroups.rst b/doc/topics/targeting/nodegroups.rst index 0aba1c5c074..3e8aaa6fa44 100644 --- a/doc/topics/targeting/nodegroups.rst +++ b/doc/topics/targeting/nodegroups.rst @@ -63,11 +63,11 @@ To match a nodegroup on the CLI, use the ``-N`` command-line option: .. note:: The ``N@`` classifier historically could not be used in compound matches - within the CLI or :term:`top file`, it was only recognized in the + within the CLI or :term:`top file `, it was only recognized in the :conf_master:`nodegroups` master config file parameter. As of the 2019.2.0 release, this limitation no longer exists. -To match a nodegroup in your :term:`top file`, make sure to put ``- match: +To match a nodegroup in your :term:`top file `, make sure to put ``- match: nodegroup`` on the line directly following the nodegroup name. .. code-block:: yaml diff --git a/doc/topics/tutorials/cloud_controller.rst b/doc/topics/tutorials/cloud_controller.rst index bf578e09393..ba6f25980a8 100644 --- a/doc/topics/tutorials/cloud_controller.rst +++ b/doc/topics/tutorials/cloud_controller.rst @@ -161,7 +161,7 @@ prone to errors. Virtual Machine generation applications are available for many platforms: kiwi: (openSUSE, SLES, RHEL, CentOS) - https://suse.github.io/kiwi/ + https://opensuse.github.io/kiwi/ vm-builder: https://wiki.debian.org/VMBuilder diff --git a/doc/topics/tutorials/gitfs.rst b/doc/topics/tutorials/gitfs.rst index 7443fea8a83..48a2006c588 100644 --- a/doc/topics/tutorials/gitfs.rst +++ b/doc/topics/tutorials/gitfs.rst @@ -155,6 +155,12 @@ including by installing XCode_. .. _pip: http://www.pip-installer.org/ .. _XCode: https://developer.apple.com/xcode/ +.. warning:: + GitPython advises against the use of its library for long-running processes + (such as a salt-master or salt-minion). Please see their warning on potential + leaks of system resources: + https://github.com/gitpython-developers/GitPython#leakage-of-system-resources. + .. warning:: Keep in mind that if GitPython has been previously installed on the master @@ -181,7 +187,6 @@ including by installing XCode_. pip.installed: - name: 'GitPython < 2.0.9' - Simple Configuration ==================== @@ -527,7 +532,7 @@ would only fetch branches and tags (the default). Global Remotes ============== -.. versionadded:: 2018.3.0 +.. versionadded:: 2018.3.0 for all_saltenvs, sodium for fallback The ``all_saltenvs`` per-remote configuration parameter overrides the logic Salt uses to map branches/tags to fileserver environments (i.e. saltenvs). This @@ -539,6 +544,8 @@ allows a single branch/tag to appear in *all* GitFS saltenvs. environment defined via some other fileserver backend (e.g. :conf_master:`file_roots`). +The ``fallback`` global or per-remote configuration can also be used. + This is very useful in particular when working with :ref:`salt formulas `. Prior to the addition of this feature, it was necessary to push a branch/tag to the remote repo for each saltenv in which that formula @@ -555,6 +562,15 @@ single branch. - http://foo.com/quux.git: - all_saltenvs: anything +If you want to also test working branches of the formula repository, use +``fallback``: + +.. code-block:: yaml + + gitfs_remotes: + - http://foo.com/quux.git: + - fallback: anything + .. _gitfs-update-intervals: Update Intervals diff --git a/doc/topics/tutorials/http.rst b/doc/topics/tutorials/http.rst index 9616a7ee72f..03cbc574b2d 100644 --- a/doc/topics/tutorials/http.rst +++ b/doc/topics/tutorials/http.rst @@ -188,7 +188,7 @@ password may be passed in as ``username`` and ``password``, respectively. salt.utils.http.query( 'http://example.com', username='larry', - password=`5700g3543v4r`, + password='5700g3543v4r', ) Cookies and Sessions diff --git a/doc/topics/tutorials/modules.rst b/doc/topics/tutorials/modules.rst index 4d6e03b3c97..64f0b9ef992 100644 --- a/doc/topics/tutorials/modules.rst +++ b/doc/topics/tutorials/modules.rst @@ -9,7 +9,7 @@ Remote execution tutorial Order your minions around ========================= -Now that you have a :term:`master` and at least one :term:`minion` +Now that you have a :term:`master ` and at least one :term:`minion ` communicating with each other you can perform commands on the minion via the :command:`salt` command. Salt calls are comprised of three main components: diff --git a/doc/topics/tutorials/pillar.rst b/doc/topics/tutorials/pillar.rst index a75b32c2372..66e9fdf5a18 100644 --- a/doc/topics/tutorials/pillar.rst +++ b/doc/topics/tutorials/pillar.rst @@ -141,7 +141,7 @@ This example sets up user data with a UID: The same directory lookups that exist in states exist in pillar, so the file ``users/init.sls`` can be referenced with ``users`` in the :term:`top - file`. + file `. The top file will need to be updated to include this sls file: diff --git a/doc/topics/tutorials/writing_tests.rst b/doc/topics/tutorials/writing_tests.rst index 8577664186b..de45f955e58 100644 --- a/doc/topics/tutorials/writing_tests.rst +++ b/doc/topics/tutorials/writing_tests.rst @@ -482,7 +482,7 @@ Automated Test Runs =================== SaltStack maintains a Jenkins server which can be viewed at -https://jenkins.saltstack.com. The tests executed from this Jenkins server +https://jenkinsci.saltstack.com. The tests executed from this Jenkins server create fresh virtual machines for each test run, then execute the destructive tests on the new, clean virtual machine. This allows for the execution of tests across supported platforms. diff --git a/doc/topics/windows/windows-package-manager.rst b/doc/topics/windows/windows-package-manager.rst index bb238649665..3f68e73b94a 100644 --- a/doc/topics/windows/windows-package-manager.rst +++ b/doc/topics/windows/windows-package-manager.rst @@ -453,7 +453,7 @@ Available parameters are as follows: a GUID. You can find this value in the registry under the following keys: - Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall - - Software\\Wow6432None\\Microsoft\\Windows\\CurrentVersion\\Uninstall + - Software\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall :param str uninstall_flags: Any flags that need to be passed to the uninstaller to make it perform a diff --git a/noxfile.py b/noxfile.py index 4c6ae27d00b..253eb2ca24a 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,45 +1,51 @@ # -*- coding: utf-8 -*- -''' +""" noxfile ~~~~~~~ Nox configuration script -''' +""" # pylint: disable=resource-leakage # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import os -import sys +from __future__ import absolute_import, print_function, unicode_literals + +import datetime import glob import json +import os import pprint import shutil +import sys import tempfile -import datetime +# fmt: off if __name__ == '__main__': sys.stderr.write('Do not execute this file directly. Use nox instead, it will know how to handle this file\n') sys.stderr.flush() exit(1) +# fmt: on # Import 3rd-party libs -import nox -from nox.command import CommandFailed +import nox # isort:skip +from nox.command import CommandFailed # isort:skip + IS_PY3 = sys.version_info > (2,) # Be verbose when runing under a CI context -PIP_INSTALL_SILENT = (os.environ.get('JENKINS_URL') or os.environ.get('CI') or os.environ.get('DRONE')) is None +PIP_INSTALL_SILENT = ( + os.environ.get("JENKINS_URL") or os.environ.get("CI") or os.environ.get("DRONE") +) is None # Global Path Definitions REPO_ROOT = os.path.abspath(os.path.dirname(__file__)) -SITECUSTOMIZE_DIR = os.path.join(REPO_ROOT, 'tests', 'support', 'coverage') -IS_DARWIN = sys.platform.lower().startswith('darwin') -IS_WINDOWS = sys.platform.lower().startswith('win') +SITECUSTOMIZE_DIR = os.path.join(REPO_ROOT, "tests", "support", "coverage") +IS_DARWIN = sys.platform.lower().startswith("darwin") +IS_WINDOWS = sys.platform.lower().startswith("win") # Python versions to run against -_PYTHON_VERSIONS = ('2', '2.7', '3', '3.4', '3.5', '3.6', '3.7') +_PYTHON_VERSIONS = ("2", "2.7", "3", "3.4", "3.5", "3.6", "3.7") # Nox options # Reuse existing virtualenvs @@ -51,17 +57,18 @@ nox.options.error_on_missing_interpreters = False os.chdir(REPO_ROOT) RUNTESTS_LOGFILE = os.path.join( - 'artifacts', 'logs', - 'runtests-{}.log'.format(datetime.datetime.now().strftime('%Y%m%d%H%M%S.%f')) + "artifacts", + "logs", + "runtests-{}.log".format(datetime.datetime.now().strftime("%Y%m%d%H%M%S.%f")), ) # Prevent Python from writing bytecode -os.environ[str('PYTHONDONTWRITEBYTECODE')] = str('1') +os.environ[str("PYTHONDONTWRITEBYTECODE")] = str("1") def _create_ci_directories(): - for dirname in ('logs', 'coverage', 'xml-unittests-output'): - path = os.path.join('artifacts', dirname) + for dirname in ("logs", "coverage", "xml-unittests-output"): + path = os.path.join("artifacts", dirname) if not os.path.exists(path): os.makedirs(path) @@ -77,12 +84,15 @@ def _get_session_python_version_info(session): # https://github.com/theacodes/nox/pull/181 session._runner.global_config.install_only = False session_py_version = session.run( - 'python', '-c' + "python", + "-c" 'import sys; sys.stdout.write("{}.{}.{}".format(*sys.version_info))', silent=True, log=False, ) - version_info = tuple(int(part) for part in session_py_version.split('.') if part.isdigit()) + version_info = tuple( + int(part) for part in session_py_version.split(".") if part.isdigit() + ) session._runner._real_python_version_info = version_info finally: session._runner.global_config.install_only = old_install_only_value @@ -100,8 +110,9 @@ def _get_session_python_site_packages_dir(session): # https://github.com/theacodes/nox/pull/181 session._runner.global_config.install_only = False site_packages_dir = session.run( - 'python', '-c' - 'import sys; from distutils.sysconfig import get_python_lib; sys.stdout.write(get_python_lib())', + "python", + "-c" + "import sys; from distutils.sysconfig import get_python_lib; sys.stdout.write(get_python_lib())", silent=True, log=False, ) @@ -114,8 +125,8 @@ def _get_session_python_site_packages_dir(session): def _get_pydir(session): version_info = _get_session_python_version_info(session) if version_info < (2, 7): - session.error('Only Python >= 2.7 is supported') - return 'py{}.{}'.format(*version_info) + session.error("Only Python >= 2.7 is supported") + return "py{}.{}".format(*version_info) def _get_distro_info(session): @@ -129,10 +140,10 @@ def _get_distro_info(session): # For additional information as to why see: # https://github.com/theacodes/nox/pull/181 session._runner.global_config.install_only = False - session.install('--progress-bar=off', 'distro', silent=PIP_INSTALL_SILENT) - output = session.run('distro', '-j', silent=True) + session.install("--progress-bar=off", "distro", silent=PIP_INSTALL_SILENT) + output = session.run("distro", "-j", silent=True) distro = json.loads(output.strip()) - session.log('Distro information:\n%s', pprint.pformat(distro)) + session.log("Distro information:\n%s", pprint.pformat(distro)) session._runner._distro = distro finally: session._runner.global_config.install_only = old_install_only_value @@ -140,37 +151,32 @@ def _get_distro_info(session): def _install_system_packages(session): - ''' + """ Because some python packages are provided by the distribution and cannot be pip installed, and because we don't want the whole system python packages on our virtualenvs, we copy the required system python packages into the virtualenv - ''' + """ system_python_packages = { - '__debian_based_distros__': [ - '/usr/lib/python{py_version}/dist-packages/*apt*' - ] + "__debian_based_distros__": ["/usr/lib/python{py_version}/dist-packages/*apt*"] } distro = _get_distro_info(session) - if not distro['id'].startswith(('debian', 'ubuntu')): + if not distro["id"].startswith(("debian", "ubuntu")): # This only applies to debian based distributions return - system_python_packages['{id}-{version}'.format(**distro)] = \ - system_python_packages['{id}-{version_parts[major]}'.format(**distro)] = \ - system_python_packages['__debian_based_distros__'][:] + system_python_packages["{id}-{version}".format(**distro)] = system_python_packages[ + "{id}-{version_parts[major]}".format(**distro) + ] = system_python_packages["__debian_based_distros__"][:] distro_keys = [ - '{id}'.format(**distro), - '{id}-{version}'.format(**distro), - '{id}-{version_parts[major]}'.format(**distro) + "{id}".format(**distro), + "{id}-{version}".format(**distro), + "{id}-{version_parts[major]}".format(**distro), ] version_info = _get_session_python_version_info(session) - py_version_keys = [ - '{}'.format(*version_info), - '{}.{}'.format(*version_info) - ] + py_version_keys = ["{}".format(*version_info), "{}.{}".format(*version_info)] session_site_packages_dir = _get_session_python_site_packages_dir(session) for distro_key in distro_keys: if distro_key not in system_python_packages: @@ -183,11 +189,15 @@ def _install_system_packages(session): continue for match in matches: src = os.path.realpath(match) - dst = os.path.join(session_site_packages_dir, os.path.basename(match)) + dst = os.path.join( + session_site_packages_dir, os.path.basename(match) + ) if os.path.exists(dst): - session.log('Not overwritting already existing %s with %s', dst, src) + session.log( + "Not overwritting already existing %s with %s", dst, src + ) continue - session.log('Copying %s into %s', src, dst) + session.log("Copying %s into %s", src, dst) if os.path.isdir(src): shutil.copytree(src, dst) else: @@ -198,83 +208,79 @@ def _get_distro_pip_constraints(session, transport): # Install requirements distro_constraints = [] - if transport == 'tcp': + if transport == "tcp": # The TCP requirements are the exact same requirements as the ZeroMQ ones - transport = 'zeromq' + transport = "zeromq" pydir = _get_pydir(session) if IS_WINDOWS: - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - '{}-windows.txt'.format(transport)) + _distro_constraints = os.path.join( + "requirements", "static", pydir, "{}-windows.txt".format(transport) + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - 'windows.txt') + _distro_constraints = os.path.join( + "requirements", "static", pydir, "windows.txt" + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - 'windows-crypto.txt') + _distro_constraints = os.path.join( + "requirements", "static", pydir, "windows-crypto.txt" + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) elif IS_DARWIN: - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - '{}-darwin.txt'.format(transport)) + _distro_constraints = os.path.join( + "requirements", "static", pydir, "{}-darwin.txt".format(transport) + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - 'darwin.txt') + _distro_constraints = os.path.join( + "requirements", "static", pydir, "darwin.txt" + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - 'darwin-crypto.txt') + _distro_constraints = os.path.join( + "requirements", "static", pydir, "darwin-crypto.txt" + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) else: _install_system_packages(session) distro = _get_distro_info(session) distro_keys = [ - 'linux', - '{id}'.format(**distro), - '{id}-{version}'.format(**distro), - '{id}-{version_parts[major]}'.format(**distro) + "linux", + "{id}".format(**distro), + "{id}-{version}".format(**distro), + "{id}-{version_parts[major]}".format(**distro), ] for distro_key in distro_keys: - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - '{}.txt'.format(distro_key)) + _distro_constraints = os.path.join( + "requirements", "static", pydir, "{}.txt".format(distro_key) + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - '{}-crypto.txt'.format(distro_key)) + _distro_constraints = os.path.join( + "requirements", "static", pydir, "{}-crypto.txt".format(distro_key) + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - '{}-{}.txt'.format(transport, distro_key)) + _distro_constraints = os.path.join( + "requirements", + "static", + pydir, + "{}-{}.txt".format(transport, distro_key), + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) distro_constraints.append(_distro_constraints) - _distro_constraints = os.path.join('requirements', - 'static', - pydir, - '{}-{}-crypto.txt'.format(transport, distro_key)) + _distro_constraints = os.path.join( + "requirements", + "static", + pydir, + "{}-{}-crypto.txt".format(transport, distro_key), + ) if os.path.exists(_distro_constraints): distro_constraints.append(_distro_constraints) return distro_constraints @@ -285,24 +291,23 @@ def _install_requirements(session, transport, *extra_requirements): distro_constraints = _get_distro_pip_constraints(session, transport) _requirements_files = [ - os.path.join('requirements', 'base.txt'), - os.path.join('requirements', 'zeromq.txt'), - os.path.join('requirements', 'pytest.txt') + os.path.join("requirements", "base.txt"), + os.path.join("requirements", "zeromq.txt"), + os.path.join("requirements", "pytest.txt"), ] - if sys.platform.startswith('linux'): + if sys.platform.startswith("linux"): + requirements_files = [os.path.join("requirements", "static", "linux.in")] + elif sys.platform.startswith("win"): requirements_files = [ - os.path.join('requirements', 'static', 'linux.in') + os.path.join("pkg", "windows", "req.txt"), + os.path.join("requirements", "static", "windows.in"), ] - elif sys.platform.startswith('win'): + elif sys.platform.startswith("darwin"): requirements_files = [ - os.path.join('pkg', 'windows', 'req.txt'), - os.path.join('requirements', 'static', 'windows.in') - ] - elif sys.platform.startswith('darwin'): - requirements_files = [ - os.path.join('pkg', 'osx', 'req.txt'), - os.path.join('pkg', 'osx', 'req_ext.txt'), - os.path.join('requirements', 'static', 'darwin.in') + os.path.join("pkg", "osx", "req.txt"), + os.path.join("pkg", "osx", "req_ext.txt"), + os.path.join("pkg", "osx", "req_pyobjc.txt"), + os.path.join("requirements", "static", "darwin.in"), ] while True: @@ -313,45 +318,41 @@ def _install_requirements(session, transport, *extra_requirements): if requirements_file not in _requirements_files: _requirements_files.append(requirements_file) - session.log('Processing {}'.format(requirements_file)) + session.log("Processing {}".format(requirements_file)) with open(requirements_file) as rfh: # pylint: disable=resource-leakage for line in rfh: line = line.strip() if not line: continue - if line.startswith('-r'): - reqfile = os.path.join(os.path.dirname(requirements_file), line.strip().split()[-1]) + if line.startswith("-r"): + reqfile = os.path.join( + os.path.dirname(requirements_file), line.strip().split()[-1] + ) if reqfile in _requirements_files: continue _requirements_files.append(reqfile) continue for requirements_file in _requirements_files: - install_command = [ - '--progress-bar=off', '-r', requirements_file - ] + install_command = ["--progress-bar=off", "-r", requirements_file] for distro_constraint in distro_constraints: - install_command.extend([ - '--constraint', distro_constraint - ]) + install_command.extend(["--constraint", distro_constraint]) session.install(*install_command, silent=PIP_INSTALL_SILENT) if extra_requirements: install_command = [ - '--progress-bar=off', + "--progress-bar=off", ] for distro_constraint in distro_constraints: - install_command.extend([ - '--constraint', distro_constraint - ]) + install_command.extend(["--constraint", distro_constraint]) install_command += list(extra_requirements) session.install(*install_command, silent=PIP_INSTALL_SILENT) def _run_with_coverage(session, *test_cmd): - session.install('--progress-bar=off', 'coverage==5.0.1', silent=PIP_INSTALL_SILENT) - session.run('coverage', 'erase') - python_path_env_var = os.environ.get('PYTHONPATH') or None + session.install("--progress-bar=off", "coverage==5.0.1", silent=PIP_INSTALL_SILENT) + session.run("coverage", "erase") + python_path_env_var = os.environ.get("PYTHONPATH") or None if python_path_env_var is None: python_path_env_var = SITECUSTOMIZE_DIR else: @@ -363,41 +364,45 @@ def _run_with_coverage(session, *test_cmd): env = { # The updated python path so that sitecustomize is importable - 'PYTHONPATH': python_path_env_var, + "PYTHONPATH": python_path_env_var, # The full path to the .coverage data file. Makes sure we always write # them to the same directory - 'COVERAGE_FILE': os.path.abspath(os.path.join(REPO_ROOT, '.coverage')), + "COVERAGE_FILE": os.path.abspath(os.path.join(REPO_ROOT, ".coverage")), # Instruct sub processes to also run under coverage - 'COVERAGE_PROCESS_START': os.path.join(REPO_ROOT, '.coveragerc') + "COVERAGE_PROCESS_START": os.path.join(REPO_ROOT, ".coveragerc"), } if IS_DARWIN: # Don't nuke our multiprocessing efforts objc! # https://stackoverflow.com/questions/50168647/multiprocessing-causes-python-to-crash-and-gives-an-error-may-have-been-in-progr - env['OBJC_DISABLE_INITIALIZE_FORK_SAFETY'] = 'YES' + env["OBJC_DISABLE_INITIALIZE_FORK_SAFETY"] = "YES" try: session.run(*test_cmd, env=env) finally: # Always combine and generate the XML coverage report try: - session.run('coverage', 'combine') + session.run("coverage", "combine") except CommandFailed: # Sometimes some of the coverage files are corrupt which would trigger a CommandFailed # exception pass # Generate report for salt code coverage session.run( - 'coverage', 'xml', - '-o', os.path.join('artifacts', 'coverage', 'salt.xml'), - '--omit=tests/*', - '--include=salt/*' + "coverage", + "xml", + "-o", + os.path.join("artifacts", "coverage", "salt.xml"), + "--omit=tests/*", + "--include=salt/*", ) # Generate report for tests code coverage session.run( - 'coverage', 'xml', - '-o', os.path.join('artifacts', 'coverage', 'tests.xml'), - '--omit=salt/*', - '--include=tests/*' + "coverage", + "xml", + "-o", + os.path.join("artifacts", "coverage", "tests.xml"), + "--omit=salt/*", + "--include=tests/*", ) @@ -406,32 +411,42 @@ def _runtests(session, coverage, cmd_args): _create_ci_directories() try: if coverage is True: - _run_with_coverage(session, 'coverage', 'run', os.path.join('tests', 'runtests.py'), *cmd_args) + _run_with_coverage( + session, + "coverage", + "run", + os.path.join("tests", "runtests.py"), + *cmd_args + ) else: - cmd_args = ['python', os.path.join('tests', 'runtests.py')] + list(cmd_args) + cmd_args = ["python", os.path.join("tests", "runtests.py")] + list(cmd_args) env = None if IS_DARWIN: # Don't nuke our multiprocessing efforts objc! # https://stackoverflow.com/questions/50168647/multiprocessing-causes-python-to-crash-and-gives-an-error-may-have-been-in-progr - env = {'OBJC_DISABLE_INITIALIZE_FORK_SAFETY': 'YES'} + env = {"OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES"} session.run(*cmd_args, env=env) except CommandFailed: # pylint: disable=try-except-raise # Disabling re-running failed tests for the time being raise # pylint: disable=unreachable - names_file_path = os.path.join('artifacts', 'failed-tests.txt') - session.log('Re-running failed tests if possible') - session.install('--progress-bar=off', 'xunitparser==1.3.3', silent=PIP_INSTALL_SILENT) + names_file_path = os.path.join("artifacts", "failed-tests.txt") + session.log("Re-running failed tests if possible") + session.install( + "--progress-bar=off", "xunitparser==1.3.3", silent=PIP_INSTALL_SILENT + ) session.run( - 'python', - os.path.join('tests', 'support', 'generate-names-file-from-failed-test-reports.py'), - names_file_path + "python", + os.path.join( + "tests", "support", "generate-names-file-from-failed-test-reports.py" + ), + names_file_path, ) if not os.path.exists(names_file_path): session.log( - 'Failed tests file(%s) was not found. Not rerunning failed tests.', - names_file_path + "Failed tests file(%s) was not found. Not rerunning failed tests.", + names_file_path, ) # raise the original exception raise @@ -439,8 +454,8 @@ def _runtests(session, coverage, cmd_args): contents = rfh.read().strip() if not contents: session.log( - 'The failed tests file(%s) is empty. Not rerunning failed tests.', - names_file_path + "The failed tests file(%s) is empty. Not rerunning failed tests.", + names_file_path, ) # raise the original exception raise @@ -448,415 +463,416 @@ def _runtests(session, coverage, cmd_args): if failed_tests_count > 500: # 500 test failures?! Something else must have gone wrong, don't even bother session.error( - 'Total failed tests({}) > 500. No point on re-running the failed tests'.format( + "Total failed tests({}) > 500. No point on re-running the failed tests".format( failed_tests_count ) ) for idx, flag in enumerate(cmd_args[:]): - if '--names-file=' in flag: + if "--names-file=" in flag: cmd_args.pop(idx) break - elif flag == '--names-file': + elif flag == "--names-file": cmd_args.pop(idx) # pop --names-file cmd_args.pop(idx) # pop the actual names file break - cmd_args.append('--names-file={}'.format(names_file_path)) + cmd_args.append("--names-file={}".format(names_file_path)) if coverage is True: - _run_with_coverage(session, 'coverage', 'run', '-m', 'tests.runtests', *cmd_args) + _run_with_coverage( + session, "coverage", "run", "-m", "tests.runtests", *cmd_args + ) else: - session.run('python', os.path.join('tests', 'runtests.py'), *cmd_args) + session.run("python", os.path.join("tests", "runtests.py"), *cmd_args) # pylint: enable=unreachable -@nox.session(python=_PYTHON_VERSIONS, name='runtests-parametrized') -@nox.parametrize('coverage', [False, True]) -@nox.parametrize('transport', ['zeromq', 'tcp']) -@nox.parametrize('crypto', [None, 'm2crypto', 'pycryptodomex']) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-parametrized") +@nox.parametrize("coverage", [False, True]) +@nox.parametrize("transport", ["zeromq", "tcp"]) +@nox.parametrize("crypto", [None, "m2crypto", "pycryptodomex"]) def runtests_parametrized(session, coverage, transport, crypto): # Install requirements - _install_requirements(session, transport, 'unittest-xml-reporting==2.5.2') + _install_requirements(session, transport, "unittest-xml-reporting==2.5.2") if crypto: - if crypto == 'm2crypto': - session.run('pip', 'uninstall', '-y', 'pycrypto', 'pycryptodome', 'pycryptodomex', silent=True) + if crypto == "m2crypto": + session.run( + "pip", + "uninstall", + "-y", + "pycrypto", + "pycryptodome", + "pycryptodomex", + silent=True, + ) else: - session.run('pip', 'uninstall', '-y', 'm2crypto', silent=True) + session.run("pip", "uninstall", "-y", "m2crypto", silent=True) distro_constraints = _get_distro_pip_constraints(session, transport) install_command = [ - '--progress-bar=off', + "--progress-bar=off", ] for distro_constraint in distro_constraints: - install_command.extend([ - '--constraint', distro_constraint - ]) + install_command.extend(["--constraint", distro_constraint]) install_command.append(crypto) session.install(*install_command, silent=PIP_INSTALL_SILENT) cmd_args = [ - '--tests-logfile={}'.format(RUNTESTS_LOGFILE), - '--transport={}'.format(transport) + "--tests-logfile={}".format(RUNTESTS_LOGFILE), + "--transport={}".format(transport), ] + session.posargs _runtests(session, coverage, cmd_args) @nox.session(python=_PYTHON_VERSIONS) -@nox.parametrize('coverage', [False, True]) +@nox.parametrize("coverage", [False, True]) def runtests(session, coverage): - ''' + """ runtests.py session with zeromq transport and default crypto - ''' + """ session.notify( - 'runtests-parametrized-{}(coverage={}, crypto=None, transport=\'zeromq\')'.format( - session.python, - coverage + "runtests-parametrized-{}(coverage={}, crypto=None, transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-tcp') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-tcp") +@nox.parametrize("coverage", [False, True]) def runtests_tcp(session, coverage): - ''' + """ runtests.py session with TCP transport and default crypto - ''' + """ session.notify( - 'runtests-parametrized-{}(coverage={}, crypto=None, transport=\'tcp\')'.format( - session.python, - coverage + "runtests-parametrized-{}(coverage={}, crypto=None, transport='tcp')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-zeromq') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-zeromq") +@nox.parametrize("coverage", [False, True]) def runtests_zeromq(session, coverage): - ''' + """ runtests.py session with zeromq transport and default crypto - ''' + """ session.notify( - 'runtests-parametrized-{}(coverage={}, crypto=None, transport=\'zeromq\')'.format( - session.python, - coverage + "runtests-parametrized-{}(coverage={}, crypto=None, transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-m2crypto') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-m2crypto") +@nox.parametrize("coverage", [False, True]) def runtests_m2crypto(session, coverage): - ''' + """ runtests.py session with zeromq transport and m2crypto - ''' + """ session.notify( - 'runtests-parametrized-{}(coverage={}, crypto=\'m2crypto\', transport=\'zeromq\')'.format( - session.python, - coverage + "runtests-parametrized-{}(coverage={}, crypto='m2crypto', transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-tcp-m2crypto') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-tcp-m2crypto") +@nox.parametrize("coverage", [False, True]) def runtests_tcp_m2crypto(session, coverage): - ''' + """ runtests.py session with TCP transport and m2crypto - ''' + """ session.notify( - 'runtests-parametrized-{}(coverage={}, crypto=\'m2crypto\', transport=\'tcp\')'.format( - session.python, - coverage + "runtests-parametrized-{}(coverage={}, crypto='m2crypto', transport='tcp')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-zeromq-m2crypto') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-zeromq-m2crypto") +@nox.parametrize("coverage", [False, True]) def runtests_zeromq_m2crypto(session, coverage): - ''' + """ runtests.py session with zeromq transport and m2crypto - ''' + """ session.notify( - 'runtests-parametrized-{}(coverage={}, crypto=\'m2crypto\', transport=\'zeromq\')'.format( - session.python, - coverage + "runtests-parametrized-{}(coverage={}, crypto='m2crypto', transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-pycryptodomex') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-pycryptodomex") +@nox.parametrize("coverage", [False, True]) def runtests_pycryptodomex(session, coverage): - ''' + """ runtests.py session with zeromq transport and pycryptodomex - ''' + """ session.notify( - 'runtests-parametrized-{}(coverage={}, crypto=\'pycryptodomex\', transport=\'zeromq\')'.format( - session.python, - coverage + "runtests-parametrized-{}(coverage={}, crypto='pycryptodomex', transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-tcp-pycryptodomex') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-tcp-pycryptodomex") +@nox.parametrize("coverage", [False, True]) def runtests_tcp_pycryptodomex(session, coverage): - ''' + """ runtests.py session with TCP transport and pycryptodomex - ''' + """ session.notify( - 'runtests-parametrized-{}(coverage={}, crypto=\'pycryptodomex\', transport=\'tcp\')'.format( - session.python, - coverage + "runtests-parametrized-{}(coverage={}, crypto='pycryptodomex', transport='tcp')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-zeromq-pycryptodomex') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-zeromq-pycryptodomex") +@nox.parametrize("coverage", [False, True]) def runtests_zeromq_pycryptodomex(session, coverage): - ''' + """ runtests.py session with zeromq transport and pycryptodomex - ''' + """ session.notify( - 'runtests-parametrized-{}(coverage={}, crypto=\'pycryptodomex\', transport=\'zeromq\')'.format( - session.python, - coverage + "runtests-parametrized-{}(coverage={}, crypto='pycryptodomex', transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-cloud') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-cloud") +@nox.parametrize("coverage", [False, True]) def runtests_cloud(session, coverage): # Install requirements - _install_requirements(session, 'zeromq', 'unittest-xml-reporting==2.2.1') + _install_requirements(session, "zeromq", "unittest-xml-reporting==2.2.1") pydir = _get_pydir(session) - cloud_requirements = os.path.join('requirements', 'static', pydir, 'cloud.txt') + cloud_requirements = os.path.join("requirements", "static", pydir, "cloud.txt") - session.install('--progress-bar=off', '-r', cloud_requirements, silent=PIP_INSTALL_SILENT) + session.install( + "--progress-bar=off", "-r", cloud_requirements, silent=PIP_INSTALL_SILENT + ) cmd_args = [ - '--tests-logfile={}'.format(RUNTESTS_LOGFILE), - '--cloud-provider-tests' + "--tests-logfile={}".format(RUNTESTS_LOGFILE), + "--cloud-provider-tests", ] + session.posargs _runtests(session, coverage, cmd_args) -@nox.session(python=_PYTHON_VERSIONS, name='runtests-tornado') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="runtests-tornado") +@nox.parametrize("coverage", [False, True]) def runtests_tornado(session, coverage): # Install requirements - _install_requirements(session, 'zeromq', 'unittest-xml-reporting==2.2.1') - session.install('--progress-bar=off', 'tornado==5.0.2', silent=PIP_INSTALL_SILENT) - session.install('--progress-bar=off', 'pyzmq==17.0.0', silent=PIP_INSTALL_SILENT) + _install_requirements(session, "zeromq", "unittest-xml-reporting==2.2.1") + session.install("--progress-bar=off", "tornado==5.0.2", silent=PIP_INSTALL_SILENT) + session.install("--progress-bar=off", "pyzmq==17.0.0", silent=PIP_INSTALL_SILENT) - cmd_args = [ - '--tests-logfile={}'.format(RUNTESTS_LOGFILE) - ] + session.posargs + cmd_args = ["--tests-logfile={}".format(RUNTESTS_LOGFILE)] + session.posargs _runtests(session, coverage, cmd_args) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-parametrized') -@nox.parametrize('coverage', [False, True]) -@nox.parametrize('transport', ['zeromq', 'tcp']) -@nox.parametrize('crypto', [None, 'm2crypto', 'pycryptodomex']) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-parametrized") +@nox.parametrize("coverage", [False, True]) +@nox.parametrize("transport", ["zeromq", "tcp"]) +@nox.parametrize("crypto", [None, "m2crypto", "pycryptodomex"]) def pytest_parametrized(session, coverage, transport, crypto): # Install requirements _install_requirements(session, transport) if crypto: - if crypto == 'm2crypto': - session.run('pip', 'uninstall', '-y', 'pycrypto', 'pycryptodome', 'pycryptodomex', silent=True) + if crypto == "m2crypto": + session.run( + "pip", + "uninstall", + "-y", + "pycrypto", + "pycryptodome", + "pycryptodomex", + silent=True, + ) else: - session.run('pip', 'uninstall', '-y', 'm2crypto', silent=True) + session.run("pip", "uninstall", "-y", "m2crypto", silent=True) distro_constraints = _get_distro_pip_constraints(session, transport) install_command = [ - '--progress-bar=off', + "--progress-bar=off", ] for distro_constraint in distro_constraints: - install_command.extend([ - '--constraint', distro_constraint - ]) + install_command.extend(["--constraint", distro_constraint]) install_command.append(crypto) session.install(*install_command, silent=PIP_INSTALL_SILENT) cmd_args = [ - '--rootdir', REPO_ROOT, - '--log-file={}'.format(RUNTESTS_LOGFILE), - '--log-file-level=debug', - '--no-print-logs', - '-ra', - '-s', - '--transport={}'.format(transport) + "--rootdir", + REPO_ROOT, + "--log-file={}".format(RUNTESTS_LOGFILE), + "--log-file-level=debug", + "--no-print-logs", + "-ra", + "-s", + "--transport={}".format(transport), ] + session.posargs _pytest(session, coverage, cmd_args) @nox.session(python=_PYTHON_VERSIONS) -@nox.parametrize('coverage', [False, True]) +@nox.parametrize("coverage", [False, True]) def pytest(session, coverage): - ''' + """ pytest session with zeromq transport and default crypto - ''' + """ session.notify( - 'pytest-parametrized-{}(coverage={}, crypto=None, transport=\'zeromq\')'.format( - session.python, - coverage + "pytest-parametrized-{}(coverage={}, crypto=None, transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-tcp') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-tcp") +@nox.parametrize("coverage", [False, True]) def pytest_tcp(session, coverage): - ''' + """ pytest session with TCP transport and default crypto - ''' + """ session.notify( - 'pytest-parametrized-{}(coverage={}, crypto=None, transport=\'tcp\')'.format( - session.python, - coverage + "pytest-parametrized-{}(coverage={}, crypto=None, transport='tcp')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-zeromq') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-zeromq") +@nox.parametrize("coverage", [False, True]) def pytest_zeromq(session, coverage): - ''' + """ pytest session with zeromq transport and default crypto - ''' + """ session.notify( - 'pytest-parametrized-{}(coverage={}, crypto=None, transport=\'zeromq\')'.format( - session.python, - coverage + "pytest-parametrized-{}(coverage={}, crypto=None, transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-m2crypto') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-m2crypto") +@nox.parametrize("coverage", [False, True]) def pytest_m2crypto(session, coverage): - ''' + """ pytest session with zeromq transport and m2crypto - ''' + """ session.notify( - 'pytest-parametrized-{}(coverage={}, crypto=\'m2crypto\', transport=\'zeromq\')'.format( - session.python, - coverage + "pytest-parametrized-{}(coverage={}, crypto='m2crypto', transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-tcp-m2crypto') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-tcp-m2crypto") +@nox.parametrize("coverage", [False, True]) def pytest_tcp_m2crypto(session, coverage): - ''' + """ pytest session with TCP transport and m2crypto - ''' + """ session.notify( - 'pytest-parametrized-{}(coverage={}, crypto=\'m2crypto\', transport=\'tcp\')'.format( - session.python, - coverage + "pytest-parametrized-{}(coverage={}, crypto='m2crypto', transport='tcp')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-zeromq-m2crypto') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-zeromq-m2crypto") +@nox.parametrize("coverage", [False, True]) def pytest_zeromq_m2crypto(session, coverage): - ''' + """ pytest session with zeromq transport and m2crypto - ''' + """ session.notify( - 'pytest-parametrized-{}(coverage={}, crypto=\'m2crypto\', transport=\'zeromq\')'.format( - session.python, - coverage + "pytest-parametrized-{}(coverage={}, crypto='m2crypto', transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-pycryptodomex') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-pycryptodomex") +@nox.parametrize("coverage", [False, True]) def pytest_pycryptodomex(session, coverage): - ''' + """ pytest session with zeromq transport and pycryptodomex - ''' + """ session.notify( - 'pytest-parametrized-{}(coverage={}, crypto=\'pycryptodomex\', transport=\'zeromq\')'.format( - session.python, - coverage + "pytest-parametrized-{}(coverage={}, crypto='pycryptodomex', transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-tcp-pycryptodomex') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-tcp-pycryptodomex") +@nox.parametrize("coverage", [False, True]) def pytest_tcp_pycryptodomex(session, coverage): - ''' + """ pytest session with TCP transport and pycryptodomex - ''' + """ session.notify( - 'pytest-parametrized-{}(coverage={}, crypto=\'pycryptodomex\', transport=\'tcp\')'.format( - session.python, - coverage + "pytest-parametrized-{}(coverage={}, crypto='pycryptodomex', transport='tcp')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-zeromq-pycryptodomex') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-zeromq-pycryptodomex") +@nox.parametrize("coverage", [False, True]) def pytest_zeromq_pycryptodomex(session, coverage): - ''' + """ pytest session with zeromq transport and pycryptodomex - ''' + """ session.notify( - 'pytest-parametrized-{}(coverage={}, crypto=\'pycryptodomex\', transport=\'zeromq\')'.format( - session.python, - coverage + "pytest-parametrized-{}(coverage={}, crypto='pycryptodomex', transport='zeromq')".format( + session.python, coverage ) ) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-cloud') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-cloud") +@nox.parametrize("coverage", [False, True]) def pytest_cloud(session, coverage): # Install requirements - _install_requirements(session, 'zeromq') + _install_requirements(session, "zeromq") pydir = _get_pydir(session) - cloud_requirements = os.path.join('requirements', 'static', pydir, 'cloud.txt') + cloud_requirements = os.path.join("requirements", "static", pydir, "cloud.txt") - session.install('--progress-bar=off', '-r', cloud_requirements, silent=PIP_INSTALL_SILENT) + session.install( + "--progress-bar=off", "-r", cloud_requirements, silent=PIP_INSTALL_SILENT + ) cmd_args = [ - '--rootdir', REPO_ROOT, - '--log-file={}'.format(RUNTESTS_LOGFILE), - '--log-file-level=debug', - '--no-print-logs', - '-ra', - '-s', - os.path.join('tests', 'integration', 'cloud', 'providers') + "--rootdir", + REPO_ROOT, + "--log-file={}".format(RUNTESTS_LOGFILE), + "--log-file-level=debug", + "--no-print-logs", + "-ra", + "-s", + os.path.join("tests", "integration", "cloud", "providers"), ] + session.posargs _pytest(session, coverage, cmd_args) -@nox.session(python=_PYTHON_VERSIONS, name='pytest-tornado') -@nox.parametrize('coverage', [False, True]) +@nox.session(python=_PYTHON_VERSIONS, name="pytest-tornado") +@nox.parametrize("coverage", [False, True]) def pytest_tornado(session, coverage): # Install requirements - _install_requirements(session, 'zeromq') - session.install('--progress-bar=off', 'tornado==5.0.2', silent=PIP_INSTALL_SILENT) - session.install('--progress-bar=off', 'pyzmq==17.0.0', silent=PIP_INSTALL_SILENT) + _install_requirements(session, "zeromq") + session.install("--progress-bar=off", "tornado==5.0.2", silent=PIP_INSTALL_SILENT) + session.install("--progress-bar=off", "pyzmq==17.0.0", silent=PIP_INSTALL_SILENT) cmd_args = [ - '--rootdir', REPO_ROOT, - '--log-file={}'.format(RUNTESTS_LOGFILE), - '--log-file-level=debug', - '--no-print-logs', - '-ra', - '-s', + "--rootdir", + REPO_ROOT, + "--log-file={}".format(RUNTESTS_LOGFILE), + "--log-file-level=debug", + "--no-print-logs", + "-ra", + "-s", ] + session.posargs _pytest(session, coverage, cmd_args) @@ -869,36 +885,37 @@ def _pytest(session, coverage, cmd_args): if IS_DARWIN: # Don't nuke our multiprocessing efforts objc! # https://stackoverflow.com/questions/50168647/multiprocessing-causes-python-to-crash-and-gives-an-error-may-have-been-in-progr - env = {'OBJC_DISABLE_INITIALIZE_FORK_SAFETY': 'YES'} + env = {"OBJC_DISABLE_INITIALIZE_FORK_SAFETY": "YES"} try: if coverage is True: - _run_with_coverage(session, 'coverage', 'run', '-m', 'py.test', *cmd_args) + _run_with_coverage(session, "coverage", "run", "-m", "py.test", *cmd_args) else: - session.run('py.test', *cmd_args, env=env) + session.run("py.test", *cmd_args, env=env) except CommandFailed: # pylint: disable=try-except-raise # Not rerunning failed tests for now raise # pylint: disable=unreachable # Re-run failed tests - session.log('Re-running failed tests') + session.log("Re-running failed tests") for idx, parg in enumerate(cmd_args): - if parg.startswith('--junitxml='): - cmd_args[idx] = parg.replace('.xml', '-rerun-failed.xml') - cmd_args.append('--lf') + if parg.startswith("--junitxml="): + cmd_args[idx] = parg.replace(".xml", "-rerun-failed.xml") + cmd_args.append("--lf") if coverage is True: - _run_with_coverage(session, 'coverage', 'run', '-m', 'py.test', *cmd_args) + _run_with_coverage(session, "coverage", "run", "-m", "py.test", *cmd_args) else: - session.run('py.test', *cmd_args, env=env) + session.run("py.test", *cmd_args, env=env) # pylint: enable=unreachable class Tee: - ''' + """ Python class to mimic linux tee behaviour - ''' + """ + def __init__(self, first, second): self._first = first self._second = second @@ -914,36 +931,25 @@ class Tee: def _lint(session, rcfile, flags, paths, tee_output=True): - _install_requirements(session, 'zeromq') - requirements_file = 'requirements/static/lint.in' - distro_constraints = [ - 'requirements/static/{}/lint.txt'.format(_get_pydir(session)) - ] - install_command = [ - '--progress-bar=off', '-r', requirements_file - ] + _install_requirements(session, "zeromq") + requirements_file = "requirements/static/lint.in" + distro_constraints = ["requirements/static/{}/lint.txt".format(_get_pydir(session))] + install_command = ["--progress-bar=off", "-r", requirements_file] for distro_constraint in distro_constraints: - install_command.extend([ - '--constraint', distro_constraint - ]) + install_command.extend(["--constraint", distro_constraint]) session.install(*install_command, silent=PIP_INSTALL_SILENT) if tee_output: - session.run('pylint', '--version') - pylint_report_path = os.environ.get('PYLINT_REPORT') + session.run("pylint", "--version") + pylint_report_path = os.environ.get("PYLINT_REPORT") - cmd_args = [ - 'pylint', - '--rcfile={}'.format(rcfile) - ] + list(flags) + list(paths) + cmd_args = ["pylint", "--rcfile={}".format(rcfile)] + list(flags) + list(paths) - cmd_kwargs = { - 'env': {'PYTHONUNBUFFERED': '1'} - } + cmd_kwargs = {"env": {"PYTHONUNBUFFERED": "1"}} if tee_output: - stdout = tempfile.TemporaryFile(mode='w+b') - cmd_kwargs['stdout'] = Tee(stdout, sys.__stdout__) + stdout = tempfile.TemporaryFile(mode="w+b") + cmd_kwargs["stdout"] = Tee(stdout, sys.__stdout__) lint_failed = False try: @@ -957,188 +963,190 @@ def _lint(session, rcfile, flags, paths, tee_output=True): contents = stdout.read() if contents: if IS_PY3: - contents = contents.decode('utf-8') + contents = contents.decode("utf-8") else: - contents = contents.encode('utf-8') + contents = contents.encode("utf-8") sys.stdout.write(contents) sys.stdout.flush() if pylint_report_path: # Write report - with open(pylint_report_path, 'w') as wfh: + with open(pylint_report_path, "w") as wfh: wfh.write(contents) - session.log('Report file written to %r', pylint_report_path) + session.log("Report file written to %r", pylint_report_path) stdout.close() def _lint_pre_commit(session, rcfile, flags, paths): - if 'VIRTUAL_ENV' not in os.environ: + if "VIRTUAL_ENV" not in os.environ: session.error( - 'This should be running from within a virtualenv and ' - '\'VIRTUAL_ENV\' was not found as an environment variable.' + "This should be running from within a virtualenv and " + "'VIRTUAL_ENV' was not found as an environment variable." ) - if 'pre-commit' not in os.environ['VIRTUAL_ENV']: + if "pre-commit" not in os.environ["VIRTUAL_ENV"]: session.error( - 'This should be running from within a pre-commit virtualenv and ' - '\'VIRTUAL_ENV\'({}) does not appear to be a pre-commit virtualenv.'.format( - os.environ['VIRTUAL_ENV'] + "This should be running from within a pre-commit virtualenv and " + "'VIRTUAL_ENV'({}) does not appear to be a pre-commit virtualenv.".format( + os.environ["VIRTUAL_ENV"] ) ) from nox.virtualenv import VirtualEnv + # Let's patch nox to make it run inside the pre-commit virtualenv try: session._runner.venv = VirtualEnv( # pylint: disable=unexpected-keyword-arg - os.environ['VIRTUAL_ENV'], + os.environ["VIRTUAL_ENV"], interpreter=session._runner.func.python, reuse_existing=True, - venv=True + venv=True, ) except TypeError: # This is still nox-py2 session._runner.venv = VirtualEnv( - os.environ['VIRTUAL_ENV'], + os.environ["VIRTUAL_ENV"], interpreter=session._runner.func.python, reuse_existing=True, ) _lint(session, rcfile, flags, paths, tee_output=False) -@nox.session(python='3') +@nox.session(python="3") def lint(session): - ''' - Run PyLint against Salt and its test suite. Set PYLINT_REPORT to a path to capture output. - ''' - session.notify('lint-salt-{}'.format(session.python)) - session.notify('lint-tests-{}'.format(session.python)) + """ + Run PyLint against Salt and it's test suite. Set PYLINT_REPORT to a path to capture output. + """ + session.notify("lint-salt-{}".format(session.python)) + session.notify("lint-tests-{}".format(session.python)) -@nox.session(python='3', name='lint-salt') +@nox.session(python="3", name="lint-salt") def lint_salt(session): - ''' + """ Run PyLint against Salt. Set PYLINT_REPORT to a path to capture output. - ''' - flags = [ - '--disable=I' - ] + """ + flags = ["--disable=I"] if session.posargs: paths = session.posargs else: - paths = ['setup.py', 'noxfile.py', 'salt/'] - _lint(session, '.pylintrc', flags, paths) + paths = ["setup.py", "noxfile.py", "salt/"] + _lint(session, ".pylintrc", flags, paths) -@nox.session(python='3', name='lint-tests') +@nox.session(python="3", name="lint-tests") def lint_tests(session): - ''' - Run PyLint against Salt and its test suite. Set PYLINT_REPORT to a path to capture output. - ''' - flags = [ - '--disable=I' - ] + """ + Run PyLint against Salt and it's test suite. Set PYLINT_REPORT to a path to capture output. + """ + flags = ["--disable=I"] if session.posargs: paths = session.posargs else: - paths = ['tests/'] - _lint(session, '.pylintrc', flags, paths) + paths = ["tests/"] + _lint(session, ".pylintrc", flags, paths) -@nox.session(python=False, name='lint-salt-pre-commit') +@nox.session(python=False, name="lint-salt-pre-commit") def lint_salt_pre_commit(session): - ''' + """ Run PyLint against Salt. Set PYLINT_REPORT to a path to capture output. - ''' - flags = [ - '--disable=I' - ] + """ + flags = ["--disable=I"] if session.posargs: paths = session.posargs else: - paths = ['setup.py', 'noxfile.py', 'salt/'] - _lint_pre_commit(session, '.pylintrc', flags, paths) + paths = ["setup.py", "noxfile.py", "salt/"] + _lint_pre_commit(session, ".pylintrc", flags, paths) -@nox.session(python=False, name='lint-tests-pre-commit') +@nox.session(python=False, name="lint-tests-pre-commit") def lint_tests_pre_commit(session): - ''' - Run PyLint against Salt and its test suite. Set PYLINT_REPORT to a path to capture output. - ''' - flags = [ - '--disable=I' - ] + """ + Run PyLint against Salt and it's test suite. Set PYLINT_REPORT to a path to capture output. + """ + flags = ["--disable=I"] if session.posargs: paths = session.posargs else: - paths = ['tests/'] - _lint_pre_commit(session, '.pylintrc', flags, paths) + paths = ["tests/"] + _lint_pre_commit(session, ".pylintrc", flags, paths) -@nox.session(python='3') -@nox.parametrize('update', [False, True]) -@nox.parametrize('compress', [False, True]) +@nox.session(python="3") +@nox.parametrize("update", [False, True]) +@nox.parametrize("compress", [False, True]) def docs(session, compress, update): - ''' + """ Build Salt's Documentation - ''' - session.notify('docs-html(compress={})'.format(compress)) - session.notify('docs-man(compress={}, update={})'.format(compress, update)) + """ + session.notify("docs-html(compress={})".format(compress)) + session.notify("docs-man(compress={}, update={})".format(compress, update)) -@nox.session(name='docs-html', python='3') -@nox.parametrize('compress', [False, True]) +@nox.session(name="docs-html", python="3") +@nox.parametrize("compress", [False, True]) def docs_html(session, compress): - ''' + """ Build Salt's HTML Documentation - ''' + """ pydir = _get_pydir(session) - if pydir == 'py3.4': - session.error('Sphinx only runs on Python >= 3.5') - requirements_file = 'requirements/static/docs.in' - distro_constraints = [ - 'requirements/static/{}/docs.txt'.format(_get_pydir(session)) - ] - install_command = [ - '--progress-bar=off', '-r', requirements_file - ] + if pydir == "py3.4": + session.error("Sphinx only runs on Python >= 3.5") + requirements_file = "requirements/static/docs.in" + distro_constraints = ["requirements/static/{}/docs.txt".format(pydir)] + install_command = ["--progress-bar=off", "-r", requirements_file] for distro_constraint in distro_constraints: - install_command.extend([ - '--constraint', distro_constraint - ]) + install_command.extend(["--constraint", distro_constraint]) session.install(*install_command, silent=PIP_INSTALL_SILENT) - os.chdir('doc/') - session.run('make', 'clean', external=True) - session.run('make', 'html', 'SPHINXOPTS=-W', external=True) + os.chdir("doc/") + session.run("make", "clean", external=True) + session.run("make", "html", "SPHINXOPTS=-W", external=True) if compress: - session.run('tar', '-cJvf', 'html-archive.tar.xz', '_build/html', external=True) - os.chdir('..') + session.run("tar", "-cJvf", "html-archive.tar.xz", "_build/html", external=True) + os.chdir("..") -@nox.session(name='docs-man', python='3') -@nox.parametrize('update', [False, True]) -@nox.parametrize('compress', [False, True]) +@nox.session(name="docs-man", python="3") +@nox.parametrize("update", [False, True]) +@nox.parametrize("compress", [False, True]) def docs_man(session, compress, update): - ''' + """ Build Salt's Manpages Documentation - ''' + """ pydir = _get_pydir(session) - if pydir == 'py3.4': - session.error('Sphinx only runs on Python >= 3.5') - requirements_file = 'requirements/static/docs.in' - distro_constraints = [ - 'requirements/static/{}/docs.txt'.format(_get_pydir(session)) - ] - install_command = [ - '--progress-bar=off', '-r', requirements_file - ] + if pydir == "py3.4": + session.error("Sphinx only runs on Python >= 3.5") + requirements_file = "requirements/static/docs.in" + distro_constraints = ["requirements/static/{}/docs.txt".format(pydir)] + install_command = ["--progress-bar=off", "-r", requirements_file] for distro_constraint in distro_constraints: - install_command.extend([ - '--constraint', distro_constraint - ]) + install_command.extend(["--constraint", distro_constraint]) session.install(*install_command, silent=PIP_INSTALL_SILENT) - os.chdir('doc/') - session.run('make', 'clean', external=True) - session.run('make', 'man', 'SPHINXOPTS=-W', external=True) + os.chdir("doc/") + session.run("make", "clean", external=True) + session.run("make", "man", "SPHINXOPTS=-W", external=True) if update: - session.run('rm', '-rf', 'man/', external=True) - session.run('cp', '-Rp', '_build/man', 'man/', external=True) + session.run("rm", "-rf", "man/", external=True) + session.run("cp", "-Rp", "_build/man", "man/", external=True) if compress: - session.run('tar', '-cJvf', 'man-archive.tar.xz', '_build/man', external=True) - os.chdir('..') + session.run("tar", "-cJvf", "man-archive.tar.xz", "_build/man", external=True) + os.chdir("..") + + +@nox.session(name="changelog", python="3") +@nox.parametrize("draft", [False, True]) +def changelog(session, draft): + """ + Generate salt's changelog + """ + requirements_file = "requirements/static/changelog.in" + distro_constraints = [ + "requirements/static/{}/changelog.txt".format(_get_pydir(session)) + ] + install_command = ["--progress-bar=off", "-r", requirements_file] + for distro_constraint in distro_constraints: + install_command.extend(["--constraint", distro_constraint]) + session.install(*install_command, silent=PIP_INSTALL_SILENT) + + town_cmd = ["towncrier", "--version={}".format(session.posargs[0])] + if draft: + town_cmd.append("--draft") + session.run(*town_cmd) diff --git a/pkg/freeze/freeze.py b/pkg/freeze/freeze.py index 354862a05b2..2f5fc1ba625 100644 --- a/pkg/freeze/freeze.py +++ b/pkg/freeze/freeze.py @@ -2,11 +2,11 @@ from bbfreeze import Freezer -includes = ['zmq', 'zmq.utils.strtypes', 'zmq.utils.jsonapi'] -excludes = ['Tkinter', 'tcl', 'Tkconstants'] +includes = ["zmq", "zmq.utils.strtypes", "zmq.utils.jsonapi"] +excludes = ["Tkinter", "tcl", "Tkconstants"] fre = Freezer(distdir="bb_salt", includes=includes, excludes=excludes) -fre.addScript('/usr/bin/salt-minion') +fre.addScript("/usr/bin/salt-minion") fre.use_compression = 0 fre.include_py = True fre() diff --git a/pkg/osx/build_env.sh b/pkg/osx/build_env.sh index 71f716b38c7..0b289610a1b 100755 --- a/pkg/osx/build_env.sh +++ b/pkg/osx/build_env.sh @@ -271,7 +271,7 @@ $MAKE install ############################################################################ # upgrade pip ############################################################################ -$PIP install --upgrade pip +$PIP install --upgrade pip wheel ############################################################################ # Download and install salt python dependencies @@ -283,7 +283,9 @@ cd $BUILDDIR echo "################################################################################" echo "Installing Salt Dependencies with pip (normal)" echo "################################################################################" -$PIP install -r $SRCDIR/pkg/osx/req.txt \ +$PIP install -r $SRCDIR/pkg/osx/req.txt -r $SRCDIR/pkg/osx/req_pyobjc.txt \ + --target=$PYDIR/site-packages \ + --ignore-installed \ --no-cache-dir echo "################################################################################" diff --git a/pkg/osx/req.txt b/pkg/osx/req.txt index b51ee6917f4..8ac69e55985 100644 --- a/pkg/osx/req.txt +++ b/pkg/osx/req.txt @@ -5,10 +5,10 @@ certifi cffi==1.12.2 CherryPy==17.4.1 click==7.0 +cryptography==2.6.1 enum34==1.1.6 futures==3.2.0 ; python_version < "3.0" -gitdb==0.6.4 -gitpython==2.1.11 +gitpython==2.1.15 idna==2.8 ipaddress==1.0.22 jinja2==2.10.1 @@ -16,12 +16,10 @@ linode-python==1.1.1 Mako==1.0.7 markupsafe==1.1.1 msgpack-python==0.5.6 -psutil==5.6.1 +psutil==5.6.6 pyasn1==0.4.5 pycparser==2.19 pycryptodome==3.8.1 -pyobjc==5.1.2 -pyopenssl python-dateutil==2.8.0 python-gnupg==0.4.4 pyyaml==5.1.2 @@ -29,6 +27,5 @@ pyzmq==18.0.1 requests==2.21.0 setproctitle singledispatch==3.4.0.3; python_version < '3.4' -smmap==0.9.0 timelib==0.2.4 vultr==1.0.1 diff --git a/pkg/osx/req_ext.txt b/pkg/osx/req_ext.txt index 76f7628a78e..0db65482463 100644 --- a/pkg/osx/req_ext.txt +++ b/pkg/osx/req_ext.txt @@ -1,2 +1 @@ -cryptography==2.6.1 pyopenssl==19.0.0 diff --git a/pkg/osx/req_pyobjc.in b/pkg/osx/req_pyobjc.in new file mode 100644 index 00000000000..6c276d6a174 --- /dev/null +++ b/pkg/osx/req_pyobjc.in @@ -0,0 +1,6 @@ +# To properly generate the pyobjc dependency tree, run, on a mac, the following +# pip install pip-tools +# pip-compile --output-file=pkg/osx/req_pyobjc.txt pkg/osx/req_pyobjc.in +# +# Should only be necessary when changing the pyobjc version bellow +pyobjc==5.1.2 diff --git a/pkg/osx/req_pyobjc.txt b/pkg/osx/req_pyobjc.txt new file mode 100644 index 00000000000..90fd0a3762f --- /dev/null +++ b/pkg/osx/req_pyobjc.txt @@ -0,0 +1,104 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile --output-file=pkg/osx/req_pyobjc.txt pkg/osx/req_pyobjc.in +# +pyobjc-core==5.1.2 # via pyobjc, pyobjc-framework-accounts, pyobjc-framework-addressbook, pyobjc-framework-adsupport, pyobjc-framework-applescriptkit, pyobjc-framework-applescriptobjc, pyobjc-framework-applicationservices, pyobjc-framework-automator, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-businesschat, pyobjc-framework-calendarstore, pyobjc-framework-cfnetwork, pyobjc-framework-cloudkit, pyobjc-framework-cocoa, pyobjc-framework-collaboration, pyobjc-framework-colorsync, pyobjc-framework-contacts, pyobjc-framework-contactsui, pyobjc-framework-coreaudio, pyobjc-framework-coreaudiokit, pyobjc-framework-corebluetooth, pyobjc-framework-coredata, pyobjc-framework-corelocation, pyobjc-framework-coremedia, pyobjc-framework-coremediaio, pyobjc-framework-coreml, pyobjc-framework-coreservices, pyobjc-framework-corespotlight, pyobjc-framework-coretext, pyobjc-framework-corewlan, pyobjc-framework-cryptotokenkit, pyobjc-framework-dictionaryservices, pyobjc-framework-discrecording, pyobjc-framework-discrecordingui, pyobjc-framework-diskarbitration, pyobjc-framework-dvdplayback, pyobjc-framework-eventkit, pyobjc-framework-exceptionhandling, pyobjc-framework-externalaccessory, pyobjc-framework-findersync, pyobjc-framework-fsevents, pyobjc-framework-gamecenter, pyobjc-framework-gamecontroller, pyobjc-framework-gamekit, pyobjc-framework-gameplaykit, pyobjc-framework-imagecapturecore, pyobjc-framework-imserviceplugin, pyobjc-framework-inputmethodkit, pyobjc-framework-installerplugins, pyobjc-framework-instantmessage, pyobjc-framework-intents, pyobjc-framework-iosurface, pyobjc-framework-ituneslibrary, pyobjc-framework-latentsemanticmapping, pyobjc-framework-launchservices, pyobjc-framework-libdispatch, pyobjc-framework-localauthentication, pyobjc-framework-mapkit, pyobjc-framework-mediaaccessibility, pyobjc-framework-medialibrary, pyobjc-framework-mediaplayer, pyobjc-framework-mediatoolbox, pyobjc-framework-modelio, pyobjc-framework-multipeerconnectivity, pyobjc-framework-naturallanguage, pyobjc-framework-netfs, pyobjc-framework-network, pyobjc-framework-networkextension, pyobjc-framework-notificationcenter, pyobjc-framework-opendirectory, pyobjc-framework-osakit, pyobjc-framework-photos, pyobjc-framework-photosui, pyobjc-framework-preferencepanes, pyobjc-framework-pubsub, pyobjc-framework-qtkit, pyobjc-framework-quartz, pyobjc-framework-safariservices, pyobjc-framework-scenekit, pyobjc-framework-screensaver, pyobjc-framework-scriptingbridge, pyobjc-framework-searchkit, pyobjc-framework-security, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface, pyobjc-framework-servicemanagement, pyobjc-framework-social, pyobjc-framework-spritekit, pyobjc-framework-storekit, pyobjc-framework-syncservices, pyobjc-framework-systemconfiguration, pyobjc-framework-usernotifications, pyobjc-framework-videosubscriberaccount, pyobjc-framework-videotoolbox, pyobjc-framework-vision, pyobjc-framework-webkit +pyobjc-framework-accounts==5.1.2 # via pyobjc, pyobjc-framework-cloudkit +pyobjc-framework-addressbook==5.1.2 # via pyobjc +pyobjc-framework-adsupport==5.1.2 # via pyobjc +pyobjc-framework-applescriptkit==5.1.2 # via pyobjc +pyobjc-framework-applescriptobjc==5.1.2 # via pyobjc +pyobjc-framework-applicationservices==5.1.2 # via pyobjc +pyobjc-framework-automator==5.1.2 # via pyobjc +pyobjc-framework-avfoundation==5.1.2 # via pyobjc, pyobjc-framework-mediaplayer +pyobjc-framework-avkit==5.1.2 # via pyobjc +pyobjc-framework-businesschat==5.1.2 # via pyobjc +pyobjc-framework-calendarstore==5.1.2 # via pyobjc +pyobjc-framework-cfnetwork==5.1.2 # via pyobjc +pyobjc-framework-cloudkit==5.1.2 # via pyobjc +pyobjc-framework-cocoa==5.1.2 # via pyobjc, pyobjc-framework-accounts, pyobjc-framework-addressbook, pyobjc-framework-adsupport, pyobjc-framework-applescriptkit, pyobjc-framework-applescriptobjc, pyobjc-framework-applicationservices, pyobjc-framework-automator, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-businesschat, pyobjc-framework-calendarstore, pyobjc-framework-cfnetwork, pyobjc-framework-cloudkit, pyobjc-framework-collaboration, pyobjc-framework-colorsync, pyobjc-framework-contacts, pyobjc-framework-contactsui, pyobjc-framework-coreaudio, pyobjc-framework-coreaudiokit, pyobjc-framework-corebluetooth, pyobjc-framework-coredata, pyobjc-framework-corelocation, pyobjc-framework-coremedia, pyobjc-framework-coremediaio, pyobjc-framework-coreml, pyobjc-framework-corespotlight, pyobjc-framework-coretext, pyobjc-framework-corewlan, pyobjc-framework-cryptotokenkit, pyobjc-framework-discrecording, pyobjc-framework-discrecordingui, pyobjc-framework-diskarbitration, pyobjc-framework-dvdplayback, pyobjc-framework-eventkit, pyobjc-framework-exceptionhandling, pyobjc-framework-externalaccessory, pyobjc-framework-findersync, pyobjc-framework-fsevents, pyobjc-framework-gamecenter, pyobjc-framework-gamecontroller, pyobjc-framework-gamekit, pyobjc-framework-gameplaykit, pyobjc-framework-imagecapturecore, pyobjc-framework-imserviceplugin, pyobjc-framework-inputmethodkit, pyobjc-framework-installerplugins, pyobjc-framework-instantmessage, pyobjc-framework-intents, pyobjc-framework-iosurface, pyobjc-framework-ituneslibrary, pyobjc-framework-latentsemanticmapping, pyobjc-framework-localauthentication, pyobjc-framework-mapkit, pyobjc-framework-mediaaccessibility, pyobjc-framework-medialibrary, pyobjc-framework-mediatoolbox, pyobjc-framework-modelio, pyobjc-framework-multipeerconnectivity, pyobjc-framework-naturallanguage, pyobjc-framework-netfs, pyobjc-framework-network, pyobjc-framework-networkextension, pyobjc-framework-notificationcenter, pyobjc-framework-opendirectory, pyobjc-framework-osakit, pyobjc-framework-photos, pyobjc-framework-photosui, pyobjc-framework-preferencepanes, pyobjc-framework-pubsub, pyobjc-framework-qtkit, pyobjc-framework-quartz, pyobjc-framework-safariservices, pyobjc-framework-scenekit, pyobjc-framework-screensaver, pyobjc-framework-scriptingbridge, pyobjc-framework-security, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface, pyobjc-framework-servicemanagement, pyobjc-framework-social, pyobjc-framework-spritekit, pyobjc-framework-storekit, pyobjc-framework-syncservices, pyobjc-framework-systemconfiguration, pyobjc-framework-usernotifications, pyobjc-framework-videosubscriberaccount, pyobjc-framework-videotoolbox, pyobjc-framework-vision, pyobjc-framework-webkit +pyobjc-framework-collaboration==5.1.2 # via pyobjc +pyobjc-framework-colorsync==5.1.2 # via pyobjc +pyobjc-framework-contacts==5.1.2 # via pyobjc, pyobjc-framework-contactsui +pyobjc-framework-contactsui==5.1.2 # via pyobjc +pyobjc-framework-coreaudio==5.1.2 # via pyobjc, pyobjc-framework-coreaudiokit +pyobjc-framework-coreaudiokit==5.1.2 # via pyobjc +pyobjc-framework-corebluetooth==5.1.2 # via pyobjc +pyobjc-framework-coredata==5.1.2 # via pyobjc, pyobjc-framework-cloudkit, pyobjc-framework-syncservices +pyobjc-framework-corelocation==5.1.2 # via pyobjc, pyobjc-framework-cloudkit, pyobjc-framework-mapkit +pyobjc-framework-coremedia==5.1.2 # via pyobjc, pyobjc-framework-videotoolbox +pyobjc-framework-coremediaio==5.1.2 # via pyobjc +pyobjc-framework-coreml==5.1.2 # via pyobjc, pyobjc-framework-vision +pyobjc-framework-coreservices==5.1.2 # via pyobjc, pyobjc-framework-dictionaryservices, pyobjc-framework-launchservices, pyobjc-framework-searchkit +pyobjc-framework-corespotlight==5.1.2 # via pyobjc +pyobjc-framework-coretext==5.1.2 # via pyobjc +pyobjc-framework-corewlan==5.1.2 # via pyobjc +pyobjc-framework-cryptotokenkit==5.1.2 # via pyobjc +pyobjc-framework-dictionaryservices==5.1.2 # via pyobjc +pyobjc-framework-discrecording==5.1.2 # via pyobjc, pyobjc-framework-discrecordingui +pyobjc-framework-discrecordingui==5.1.2 # via pyobjc +pyobjc-framework-diskarbitration==5.1.2 # via pyobjc +pyobjc-framework-dvdplayback==5.1.2 # via pyobjc +pyobjc-framework-eventkit==5.1.2 # via pyobjc +pyobjc-framework-exceptionhandling==5.1.2 # via pyobjc +pyobjc-framework-externalaccessory==5.1.2 # via pyobjc +pyobjc-framework-findersync==5.1.2 # via pyobjc +pyobjc-framework-fsevents==5.1.2 # via pyobjc, pyobjc-framework-coreservices +pyobjc-framework-gamecenter==5.1.2 # via pyobjc +pyobjc-framework-gamecontroller==5.1.2 # via pyobjc +pyobjc-framework-gamekit==5.1.2 # via pyobjc +pyobjc-framework-gameplaykit==5.1.2 # via pyobjc +pyobjc-framework-imagecapturecore==5.1.2 # via pyobjc +pyobjc-framework-imserviceplugin==5.1.2 # via pyobjc +pyobjc-framework-inputmethodkit==5.1.2 # via pyobjc +pyobjc-framework-installerplugins==5.1.2 # via pyobjc +pyobjc-framework-instantmessage==5.1.2 # via pyobjc +pyobjc-framework-intents==5.1.2 # via pyobjc +pyobjc-framework-iosurface==5.1.2 # via pyobjc +pyobjc-framework-ituneslibrary==5.1.2 # via pyobjc +pyobjc-framework-latentsemanticmapping==5.1.2 # via pyobjc +pyobjc-framework-launchservices==5.1.2 # via pyobjc +pyobjc-framework-libdispatch==5.1.2 # via pyobjc +pyobjc-framework-localauthentication==5.1.2 # via pyobjc +pyobjc-framework-mapkit==5.1.2 # via pyobjc +pyobjc-framework-mediaaccessibility==5.1.2 # via pyobjc +pyobjc-framework-medialibrary==5.1.2 # via pyobjc +pyobjc-framework-mediaplayer==5.1.2 # via pyobjc +pyobjc-framework-mediatoolbox==5.1.2 # via pyobjc +pyobjc-framework-modelio==5.1.2 # via pyobjc +pyobjc-framework-multipeerconnectivity==5.1.2 # via pyobjc +pyobjc-framework-naturallanguage==5.1.2 # via pyobjc +pyobjc-framework-netfs==5.1.2 # via pyobjc +pyobjc-framework-network==5.1.2 # via pyobjc +pyobjc-framework-networkextension==5.1.2 # via pyobjc +pyobjc-framework-notificationcenter==5.1.2 # via pyobjc +pyobjc-framework-opendirectory==5.1.2 # via pyobjc +pyobjc-framework-osakit==5.1.2 # via pyobjc +pyobjc-framework-photos==5.1.2 # via pyobjc +pyobjc-framework-photosui==5.1.2 # via pyobjc +pyobjc-framework-preferencepanes==5.1.2 # via pyobjc +pyobjc-framework-pubsub==5.1.2 # via pyobjc +pyobjc-framework-qtkit==5.1.2 # via pyobjc +pyobjc-framework-quartz==5.1.2 # via pyobjc, pyobjc-framework-applicationservices, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-coretext, pyobjc-framework-gamekit, pyobjc-framework-instantmessage, pyobjc-framework-mapkit, pyobjc-framework-medialibrary, pyobjc-framework-modelio, pyobjc-framework-qtkit, pyobjc-framework-scenekit, pyobjc-framework-spritekit, pyobjc-framework-videotoolbox, pyobjc-framework-vision +pyobjc-framework-safariservices==5.1.2 # via pyobjc +pyobjc-framework-scenekit==5.1.2 # via pyobjc +pyobjc-framework-screensaver==5.1.2 # via pyobjc +pyobjc-framework-scriptingbridge==5.1.2 # via pyobjc +pyobjc-framework-searchkit==5.1.2 # via pyobjc +pyobjc-framework-security==5.1.2 # via pyobjc, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface +pyobjc-framework-securityfoundation==5.1.2 # via pyobjc +pyobjc-framework-securityinterface==5.1.2 # via pyobjc +pyobjc-framework-servicemanagement==5.1.2 # via pyobjc +pyobjc-framework-social==5.1.2 # via pyobjc +pyobjc-framework-spritekit==5.1.2 # via pyobjc, pyobjc-framework-gameplaykit +pyobjc-framework-storekit==5.1.2 # via pyobjc +pyobjc-framework-syncservices==5.1.2 # via pyobjc +pyobjc-framework-systemconfiguration==5.1.2 # via pyobjc +pyobjc-framework-usernotifications==5.1.2 # via pyobjc +pyobjc-framework-videosubscriberaccount==5.1.2 # via pyobjc +pyobjc-framework-videotoolbox==5.1.2 # via pyobjc +pyobjc-framework-vision==5.1.2 # via pyobjc +pyobjc-framework-webkit==5.1.2 # via pyobjc +pyobjc==5.1.2 diff --git a/pkg/rpm/build.py b/pkg/rpm/build.py index 50731fef63f..1019fa8b5b0 100755 --- a/pkg/rpm/build.py +++ b/pkg/rpm/build.py @@ -1,50 +1,55 @@ #! /bin/env python from __future__ import print_function -import sys -import os -import tarfile + import argparse -from os.path import dirname, join, abspath +import os +import sys +import tarfile +from os.path import abspath, dirname, join from shutil import copy from subprocess import check_call -parser = argparse.ArgumentParser( - description='Build salt rpms', - ) -parser.add_argument('buildid', - help='The build id to use i.e. the bit after the salt version in the package name', - ) +parser = argparse.ArgumentParser(description="Build salt rpms",) +parser.add_argument( + "buildid", + help="The build id to use i.e. the bit after the salt version in the package name", +) args = parser.parse_args() -src = abspath(join(dirname(__file__), '../..')) +src = abspath(join(dirname(__file__), "../..")) sys.path.append(src) -import salt.version +import salt.version # isort:skip salt_version = salt.version.__saltstack_version__.string -rpmbuild = join(os.environ['HOME'], 'rpmbuild') -copy(join(src, 'pkg/rpm/salt.spec'), join(rpmbuild, 'SPECS')) -for f in os.listdir(join(src, 'pkg/rpm')): - if f in ['salt.spec', 'build.py']: +rpmbuild = join(os.environ["HOME"], "rpmbuild") +copy(join(src, "pkg/rpm/salt.spec"), join(rpmbuild, "SPECS")) +for f in os.listdir(join(src, "pkg/rpm")): + if f in ["salt.spec", "build.py"]: continue - copy(join(src, 'pkg/rpm', f), join(rpmbuild, 'SOURCES')) + copy(join(src, "pkg/rpm", f), join(rpmbuild, "SOURCES")) def srcfilter(ti): - if '/.git' in ti.name: + if "/.git" in ti.name: return None return ti -with tarfile.open(join(rpmbuild, 'SOURCES/salt-%s.tar.gz' % salt_version), 'w|gz') as tf: - tf.add(src, arcname='salt-%s' % salt_version, - filter=srcfilter) + +with tarfile.open( + join(rpmbuild, "SOURCES/salt-%s.tar.gz" % salt_version), "w|gz" +) as tf: + tf.add(src, arcname="salt-%s" % salt_version, filter=srcfilter) -cmd = ['rpmbuild', '-bb', - '--define=salt_version %s' % salt_version, - '--define=buildid %s' % args.buildid, - 'salt.spec'] -print('Executing: %s' % ' '.join('"%s"' % c for c in cmd)) -check_call(cmd, cwd=join(rpmbuild, 'SPECS')) +cmd = [ + "rpmbuild", + "-bb", + "--define=salt_version %s" % salt_version, + "--define=buildid %s" % args.buildid, + "salt.spec", +] +print("Executing: %s" % " ".join('"%s"' % c for c in cmd)) +check_call(cmd, cwd=join(rpmbuild, "SPECS")) diff --git a/pkg/salt-common.logrotate b/pkg/salt-common.logrotate index 3cd002308e8..3f8b4c7b578 100644 --- a/pkg/salt-common.logrotate +++ b/pkg/salt-common.logrotate @@ -21,3 +21,11 @@ compress notifempty } + +/var/log/salt/api { + weekly + missingok + rotate 7 + compress + notifempty +} diff --git a/pkg/smartos/esky/_syspaths.py b/pkg/smartos/esky/_syspaths.py index 62bea942954..d718aa17e9c 100644 --- a/pkg/smartos/esky/_syspaths.py +++ b/pkg/smartos/esky/_syspaths.py @@ -3,26 +3,26 @@ import sys # http://stackoverflow.com/a/404750 # determine if application is a script file or frozen exe -if getattr(sys, 'frozen', False): +if getattr(sys, "frozen", False): application_path = os.path.dirname(sys.executable) elif __file__: application_path = os.path.dirname(__file__) -ROOT_DIR=application_path.split("bin/appdata")[0] +ROOT_DIR = application_path.split("bin/appdata")[0] # Copied from syspaths.py -SHARE_DIR = os.path.join(ROOT_DIR, 'usr', 'share', 'salt') -CONFIG_DIR = os.path.join(ROOT_DIR, 'etc') -CACHE_DIR = os.path.join(ROOT_DIR, 'var', 'cache', 'salt') -SOCK_DIR = os.path.join(ROOT_DIR, 'var', 'run', 'salt') -SRV_ROOT_DIR = os.path.join(ROOT_DIR, 'srv') -BASE_FILE_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, 'salt') -BASE_PILLAR_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, 'pillar') -BASE_THORIUM_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, 'thorium') -BASE_MASTER_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, 'salt-master') -LOGS_DIR = os.path.join(ROOT_DIR, 'var', 'log', 'salt') -PIDFILE_DIR = os.path.join(ROOT_DIR, 'var', 'run') -SPM_PARENT_PATH = os.path.join(ROOT_DIR, 'spm') -SPM_FORMULA_PATH = os.path.join(SPM_PARENT_PATH, 'salt') -SPM_PILLAR_PATH = os.path.join(SPM_PARENT_PATH, 'pillar') -SPM_REACTOR_PATH = os.path.join(SPM_PARENT_PATH, 'reactor') +SHARE_DIR = os.path.join(ROOT_DIR, "usr", "share", "salt") +CONFIG_DIR = os.path.join(ROOT_DIR, "etc") +CACHE_DIR = os.path.join(ROOT_DIR, "var", "cache", "salt") +SOCK_DIR = os.path.join(ROOT_DIR, "var", "run", "salt") +SRV_ROOT_DIR = os.path.join(ROOT_DIR, "srv") +BASE_FILE_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, "salt") +BASE_PILLAR_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, "pillar") +BASE_THORIUM_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, "thorium") +BASE_MASTER_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, "salt-master") +LOGS_DIR = os.path.join(ROOT_DIR, "var", "log", "salt") +PIDFILE_DIR = os.path.join(ROOT_DIR, "var", "run") +SPM_PARENT_PATH = os.path.join(ROOT_DIR, "spm") +SPM_FORMULA_PATH = os.path.join(SPM_PARENT_PATH, "salt") +SPM_PILLAR_PATH = os.path.join(SPM_PARENT_PATH, "pillar") +SPM_REACTOR_PATH = os.path.join(SPM_PARENT_PATH, "reactor") diff --git a/pkg/smartos/esky/sodium_grabber_installer.py b/pkg/smartos/esky/sodium_grabber_installer.py index 74ed51f5b84..57e1b2b76ab 100755 --- a/pkg/smartos/esky/sodium_grabber_installer.py +++ b/pkg/smartos/esky/sodium_grabber_installer.py @@ -1,23 +1,24 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -''' +""" The setup script for sodium_grabber -''' +""" # pylint: disable=C0111,E1101,E1103,F0401,W0611 -from distutils.core import setup, Extension +from distutils.core import Extension, setup from os import path HERE = path.dirname(__file__) SETUP_KWARGS = {} -sodium_grabber = Extension('sodium_grabber', - sources=[path.join(HERE, 'sodium_grabber.c')], - libraries=['sodium'], +sodium_grabber = Extension( + "sodium_grabber", + sources=[path.join(HERE, "sodium_grabber.c")], + libraries=["sodium"], ) -SETUP_KWARGS['ext_modules'] = [sodium_grabber] -SETUP_KWARGS['name'] = "sodium_grabber" +SETUP_KWARGS["ext_modules"] = [sodium_grabber] +SETUP_KWARGS["name"] = "sodium_grabber" -if __name__ == '__main__': +if __name__ == "__main__": setup(**SETUP_KWARGS) diff --git a/pkg/windows/buildenv/salt.ico b/pkg/windows/buildenv/salt.ico index 47380de47ca..4ac60ca2e22 100644 Binary files a/pkg/windows/buildenv/salt.ico and b/pkg/windows/buildenv/salt.ico differ diff --git a/pkg/windows/portable.py b/pkg/windows/portable.py index e806b116a32..0019942a3e2 100644 --- a/pkg/windows/portable.py +++ b/pkg/windows/portable.py @@ -1,46 +1,44 @@ #!/usr/bin/python from __future__ import print_function -import sys -import os import getopt +import os +import sys def display_help(): - print('####################################################################') - print('# #') - print('# File: portable.py #') - print('# Description: #') - print('# - search and replace within a binary file #') - print('# #') - print('# Parameters: #') - print('# -f, --file : target file #') - print('# -s, --search : term to search for #') - print('# Default is the base path for the python #') - print('# executable that is running this script. #') - print('# In Py2 that would be C:\\Python27 #') - print('# -r, --replace : replace with this #') + print("####################################################################") + print("# #") + print("# File: portable.py #") + print("# Description: #") + print("# - search and replace within a binary file #") + print("# #") + print("# Parameters: #") + print("# -f, --file : target file #") + print("# -s, --search : term to search for #") + print("# Default is the base path for the python #") + print("# executable that is running this script. #") + print("# In Py2 that would be C:\\Python27 #") + print("# -r, --replace : replace with this #") print('# default is ".." #') - print('# #') - print('# example: #') - print('# portable.py -f -s -r #') - print('# #') - print('####################################################################') + print("# #") + print("# example: #") + print("# portable.py -f -s -r #") + print("# #") + print("####################################################################") sys.exit(2) def main(argv): - target = '' + target = "" search = os.path.dirname(sys.executable) - replace = '..' + replace = ".." try: - opts, args = getopt.getopt(argv, - "hf:s:r:", - ["file=", "search=", "replace="]) + opts, args = getopt.getopt(argv, "hf:s:r:", ["file=", "search=", "replace="]) except getopt.GetoptError: display_help() for opt, arg in opts: - if opt == '-h': + if opt == "-h": display_help() elif opt in ("-f", "--file"): target = arg @@ -48,16 +46,16 @@ def main(argv): search = arg elif opt in ("-r", "--replace"): replace = arg - if target == '': + if target == "": display_help() if sys.version_info >= (3, 0): - search = search.encode('utf-8') - replace = replace.encode('utf-8') - f = open(target, 'rb').read() + search = search.encode("utf-8") + replace = replace.encode("utf-8") + f = open(target, "rb").read() f = f.replace(search, replace) f = f.replace(search.lower(), replace) - open(target, 'wb').write(f) + open(target, "wb").write(f) if __name__ == "__main__": diff --git a/pkg/windows/req.txt b/pkg/windows/req.txt index e0f2f00ee7e..dac131d2923 100644 --- a/pkg/windows/req.txt +++ b/pkg/windows/req.txt @@ -5,24 +5,22 @@ certifi cffi==1.12.2 CherryPy==17.4.1 cryptography==2.6.1 -enum34==1.1.6 -futures==3.2.0 ; python_version < "3.0" -gitdb==0.6.4 -GitPython==2.1.10 +enum34==1.1.6; python_version < '3.4' +futures==3.2.0; python_version < "3.0" idna==2.8 ioloop==0.1a0 ipaddress==1.0.22 jinja2==2.10.1 -libnacl==1.6.1 +libnacl==1.7.1 lxml==4.3.0 Mako==1.0.7 markupsafe==1.1.1 msgpack-python==0.5.6 -psutil==5.6.1 +psutil==5.6.6 pyasn1==0.4.5 pycparser==2.19 pycryptodomex==3.8.1 -pycurl==7.43.0.2 +pycurl==7.43.0.5 pymssql==2.1.4 PyMySQL==0.9.3 pyopenssl==19.0.0 @@ -34,7 +32,11 @@ pyzmq==18.0.1 requests==2.21.0 setproctitle singledispatch==3.4.0.3; python_version < '3.4' -smmap==0.9.0 timelib==0.2.4 watchdog==0.9.0 wheel==0.33.4 + +# GitPython +GitPython==2.1.10 +gitdb2==2.0.5 # via gitpython +smmap2==2.0.5 # via gitdb2 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000000..9969e02d0e9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,49 @@ +[tool.black] +exclude= """ +/( + salt/ext + | tests/kitchen + | templates +)/ +""" + +[tool.isort] +multi_line_output = 3 +include_trailing_comma = true +force_grid_wrap = 0 +use_parentheses = true +line_length = 88 +ensure_newline_before_comments=true +skip="salt/ext,tests/kitchen,templates" + +[tool.towncrier] + package = "salt" + package_dir = "salt" + filename = "CHANGELOG.md" + directory = "changelog/" + start_string = "# Changelog\n" + + [[tool.towncrier.type]] + directory = "removed" + name = "Removed" + showcontent = true + + [[tool.towncrier.type]] + directory = "deprecated" + name = "Deprecated" + showcontent = true + + [[tool.towncrier.type]] + directory = "changed" + name = "Changed" + showcontent = true + + [[tool.towncrier.type]] + directory = "fixed" + name = "Fixed" + showcontent = true + + [[tool.towncrier.type]] + directory = "added" + name = "Added" + showcontent = true diff --git a/requirements/static/changelog.in b/requirements/static/changelog.in new file mode 100644 index 00000000000..0d2d2d7db84 --- /dev/null +++ b/requirements/static/changelog.in @@ -0,0 +1 @@ +towncrier diff --git a/requirements/static/docs.in b/requirements/static/docs.in index 54e97c85128..83755be646b 100644 --- a/requirements/static/docs.in +++ b/requirements/static/docs.in @@ -1 +1 @@ -sphinx>=2.0.1 +sphinx>=3.0.1 diff --git a/requirements/static/linux.in b/requirements/static/linux.in index c20b006e2bf..a12d551f9ed 100644 --- a/requirements/static/linux.in +++ b/requirements/static/linux.in @@ -17,7 +17,7 @@ jxmlease kazoo keyring==5.7.1 kubernetes<4.0 -libnacl==1.6.0 +libnacl>=1.7.1 mock>=3.0.5 more-itertools==5.0.0 moto diff --git a/requirements/static/py2.7/cloud.txt b/requirements/static/py2.7/cloud.txt deleted file mode 100644 index 988fd29f2ee..00000000000 --- a/requirements/static/py2.7/cloud.txt +++ /dev/null @@ -1,135 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/cloud.txt -v requirements/static/cloud.in -# -adal==1.2.1 # via azure-datalake-store, msrestazure -asn1crypto==0.24.0 # via cryptography -azure-applicationinsights==0.1.0 # via azure -azure-batch==4.1.3 # via azure -azure-common==1.1.18 # via azure-applicationinsights, azure-batch, azure-cosmosdb-table, azure-eventgrid, azure-graphrbac, azure-keyvault, azure-loganalytics, azure-mgmt-advisor, azure-mgmt-applicationinsights, azure-mgmt-authorization, azure-mgmt-batch, azure-mgmt-batchai, azure-mgmt-billing, azure-mgmt-cdn, azure-mgmt-cognitiveservices, azure-mgmt-commerce, azure-mgmt-compute, azure-mgmt-consumption, azure-mgmt-containerinstance, azure-mgmt-containerregistry, azure-mgmt-containerservice, azure-mgmt-cosmosdb, azure-mgmt-datafactory, azure-mgmt-datalake-analytics, azure-mgmt-datalake-store, azure-mgmt-datamigration, azure-mgmt-devspaces, azure-mgmt-devtestlabs, azure-mgmt-dns, azure-mgmt-eventgrid, azure-mgmt-eventhub, azure-mgmt-hanaonazure, azure-mgmt-iotcentral, azure-mgmt-iothub, azure-mgmt-iothubprovisioningservices, azure-mgmt-keyvault, azure-mgmt-loganalytics, azure-mgmt-logic, azure-mgmt-machinelearningcompute, azure-mgmt-managementgroups, azure-mgmt-managementpartner, azure-mgmt-maps, azure-mgmt-marketplaceordering, azure-mgmt-media, azure-mgmt-monitor, azure-mgmt-msi, azure-mgmt-network, azure-mgmt-notificationhubs, azure-mgmt-policyinsights, azure-mgmt-powerbiembedded, azure-mgmt-rdbms, azure-mgmt-recoveryservices, azure-mgmt-recoveryservicesbackup, azure-mgmt-redis, azure-mgmt-relay, azure-mgmt-reservations, azure-mgmt-resource, azure-mgmt-scheduler, azure-mgmt-search, azure-mgmt-servicebus, azure-mgmt-servicefabric, azure-mgmt-signalr, azure-mgmt-sql, azure-mgmt-storage, azure-mgmt-subscription, azure-mgmt-trafficmanager, azure-mgmt-web, azure-servicebus, azure-servicefabric, azure-servicemanagement-legacy, azure-storage-blob, azure-storage-common, azure-storage-file, azure-storage-queue -azure-cosmosdb-nspkg==2.0.2 # via azure-cosmosdb-table -azure-cosmosdb-table==1.0.5 # via azure -azure-datalake-store==0.0.44 # via azure -azure-eventgrid==1.2.0 # via azure -azure-graphrbac==0.40.0 # via azure -azure-keyvault==1.1.0 # via azure -azure-loganalytics==0.1.0 # via azure -azure-mgmt-advisor==1.0.1 # via azure-mgmt -azure-mgmt-applicationinsights==0.1.1 # via azure-mgmt -azure-mgmt-authorization==0.50.0 # via azure-mgmt -azure-mgmt-batch==5.0.1 # via azure-mgmt -azure-mgmt-batchai==2.0.0 # via azure-mgmt -azure-mgmt-billing==0.2.0 # via azure-mgmt -azure-mgmt-cdn==3.1.0 # via azure-mgmt -azure-mgmt-cognitiveservices==3.0.0 # via azure-mgmt -azure-mgmt-commerce==1.0.1 # via azure-mgmt -azure-mgmt-compute==4.6.0 # via azure-mgmt -azure-mgmt-consumption==2.0.0 # via azure-mgmt -azure-mgmt-containerinstance==1.4.1 # via azure-mgmt -azure-mgmt-containerregistry==2.7.0 # via azure-mgmt -azure-mgmt-containerservice==4.4.0 # via azure-mgmt -azure-mgmt-cosmosdb==0.4.1 # via azure-mgmt -azure-mgmt-datafactory==0.6.0 # via azure-mgmt -azure-mgmt-datalake-analytics==0.6.0 # via azure-mgmt -azure-mgmt-datalake-nspkg==3.0.1 # via azure-mgmt-datalake-analytics, azure-mgmt-datalake-store -azure-mgmt-datalake-store==0.5.0 # via azure-mgmt -azure-mgmt-datamigration==1.0.0 # via azure-mgmt -azure-mgmt-devspaces==0.1.0 # via azure-mgmt -azure-mgmt-devtestlabs==2.2.0 # via azure-mgmt -azure-mgmt-dns==2.1.0 # via azure-mgmt -azure-mgmt-eventgrid==1.0.0 # via azure-mgmt -azure-mgmt-eventhub==2.5.0 # via azure-mgmt -azure-mgmt-hanaonazure==0.1.1 # via azure-mgmt -azure-mgmt-iotcentral==0.1.0 # via azure-mgmt -azure-mgmt-iothub==0.5.0 # via azure-mgmt -azure-mgmt-iothubprovisioningservices==0.2.0 # via azure-mgmt -azure-mgmt-keyvault==1.1.0 # via azure-mgmt -azure-mgmt-loganalytics==0.2.0 # via azure-mgmt -azure-mgmt-logic==3.0.0 # via azure-mgmt -azure-mgmt-machinelearningcompute==0.4.1 # via azure-mgmt -azure-mgmt-managementgroups==0.1.0 # via azure-mgmt -azure-mgmt-managementpartner==0.1.0 # via azure-mgmt -azure-mgmt-maps==0.1.0 # via azure-mgmt -azure-mgmt-marketplaceordering==0.1.0 # via azure-mgmt -azure-mgmt-media==1.0.0 # via azure-mgmt -azure-mgmt-monitor==0.5.2 # via azure-mgmt -azure-mgmt-msi==0.2.0 # via azure-mgmt -azure-mgmt-network==2.6.0 # via azure-mgmt -azure-mgmt-notificationhubs==2.0.0 # via azure-mgmt -azure-mgmt-nspkg==3.0.2 # via azure-mgmt-advisor, azure-mgmt-applicationinsights, azure-mgmt-authorization, azure-mgmt-batch, azure-mgmt-batchai, azure-mgmt-billing, azure-mgmt-cdn, azure-mgmt-cognitiveservices, azure-mgmt-commerce, azure-mgmt-compute, azure-mgmt-consumption, azure-mgmt-containerinstance, azure-mgmt-containerregistry, azure-mgmt-containerservice, azure-mgmt-cosmosdb, azure-mgmt-datafactory, azure-mgmt-datalake-nspkg, azure-mgmt-datamigration, azure-mgmt-devspaces, azure-mgmt-devtestlabs, azure-mgmt-dns, azure-mgmt-eventgrid, azure-mgmt-eventhub, azure-mgmt-hanaonazure, azure-mgmt-iotcentral, azure-mgmt-iothub, azure-mgmt-iothubprovisioningservices, azure-mgmt-keyvault, azure-mgmt-loganalytics, azure-mgmt-logic, azure-mgmt-machinelearningcompute, azure-mgmt-managementgroups, azure-mgmt-managementpartner, azure-mgmt-maps, azure-mgmt-marketplaceordering, azure-mgmt-media, azure-mgmt-monitor, azure-mgmt-msi, azure-mgmt-network, azure-mgmt-notificationhubs, azure-mgmt-policyinsights, azure-mgmt-powerbiembedded, azure-mgmt-rdbms, azure-mgmt-recoveryservices, azure-mgmt-recoveryservicesbackup, azure-mgmt-redis, azure-mgmt-relay, azure-mgmt-reservations, azure-mgmt-resource, azure-mgmt-scheduler, azure-mgmt-search, azure-mgmt-servicebus, azure-mgmt-servicefabric, azure-mgmt-signalr, azure-mgmt-sql, azure-mgmt-storage, azure-mgmt-subscription, azure-mgmt-trafficmanager, azure-mgmt-web -azure-mgmt-policyinsights==0.1.0 # via azure-mgmt -azure-mgmt-powerbiembedded==2.0.0 # via azure-mgmt -azure-mgmt-rdbms==1.8.0 # via azure-mgmt -azure-mgmt-recoveryservices==0.3.0 # via azure-mgmt -azure-mgmt-recoveryservicesbackup==0.3.0 # via azure-mgmt -azure-mgmt-redis==5.0.0 # via azure-mgmt -azure-mgmt-relay==0.1.0 # via azure-mgmt -azure-mgmt-reservations==0.2.1 # via azure-mgmt -azure-mgmt-resource==2.1.0 # via azure-mgmt -azure-mgmt-scheduler==2.0.0 # via azure-mgmt -azure-mgmt-search==2.0.0 # via azure-mgmt -azure-mgmt-servicebus==0.5.3 # via azure-mgmt -azure-mgmt-servicefabric==0.2.0 # via azure-mgmt -azure-mgmt-signalr==0.1.1 # via azure-mgmt -azure-mgmt-sql==0.9.1 # via azure-mgmt -azure-mgmt-storage==2.0.0 # via azure-mgmt -azure-mgmt-subscription==0.2.0 # via azure-mgmt -azure-mgmt-trafficmanager==0.50.0 # via azure-mgmt -azure-mgmt-web==0.35.0 # via azure-mgmt -azure-mgmt==4.0.0 # via azure -azure-nspkg==3.0.2 # via azure-applicationinsights, azure-batch, azure-common, azure-cosmosdb-nspkg, azure-datalake-store, azure-eventgrid, azure-graphrbac, azure-keyvault, azure-loganalytics, azure-mgmt-nspkg, azure-servicebus, azure-servicefabric, azure-servicemanagement-legacy, azure-storage-nspkg -azure-servicebus==0.21.1 # via azure -azure-servicefabric==6.3.0.0 # via azure -azure-servicemanagement-legacy==0.20.6 # via azure -azure-storage-blob==1.5.0 # via azure -azure-storage-common==1.4.0 # via azure-cosmosdb-table, azure-storage-blob, azure-storage-file, azure-storage-queue -azure-storage-file==1.4.0 # via azure -azure-storage-nspkg==3.1.0 # via azure-storage-common -azure-storage-queue==1.4.0 # via azure -azure==4.0.0 -certifi==2019.3.9 # via msrest, requests -cffi==1.12.2 # via azure-datalake-store, cryptography -chardet==3.0.4 # via requests -click==7.0 # via flask -cryptography==2.6.1 # via adal, azure-cosmosdb-table, azure-keyvault, azure-storage-common, pyopenssl, requests-ntlm, smbprotocol -dnspython==1.16.0 # via ldapdomaindump -enum34==1.1.6 # via cryptography, msrest -flask==1.0.2 # via impacket -future==0.17.1 # via ldapdomaindump -futures==3.2.0 # via azure-cosmosdb-table, azure-datalake-store, azure-storage-blob, azure-storage-file -idna==2.8 # via requests -impacket==0.9.19 ; python_version < "3.0" -ipaddress==1.0.22 # via cryptography -isodate==0.6.0 # via msrest -itsdangerous==1.1.0 # via flask -jinja2==2.10.1 # via flask -ldap3==2.5.1 # via impacket, ldapdomaindump -ldapdomaindump==0.9.1 # via impacket -markupsafe==1.1.1 # via jinja2 -msrest==0.6.6 # via azure-applicationinsights, azure-eventgrid, azure-keyvault, azure-loganalytics, azure-mgmt-cdn, azure-mgmt-compute, azure-mgmt-containerinstance, azure-mgmt-containerregistry, azure-mgmt-containerservice, azure-mgmt-dns, azure-mgmt-eventhub, azure-mgmt-keyvault, azure-mgmt-media, azure-mgmt-network, azure-mgmt-rdbms, azure-mgmt-resource, azure-mgmt-servicebus, azure-mgmt-servicefabric, azure-mgmt-signalr, azure-servicefabric, msrestazure -msrestazure==0.6.0 # via azure-batch, azure-eventgrid, azure-graphrbac, azure-keyvault, azure-mgmt-advisor, azure-mgmt-applicationinsights, azure-mgmt-authorization, azure-mgmt-batch, azure-mgmt-batchai, azure-mgmt-billing, azure-mgmt-cdn, azure-mgmt-cognitiveservices, azure-mgmt-commerce, azure-mgmt-compute, azure-mgmt-consumption, azure-mgmt-containerinstance, azure-mgmt-containerregistry, azure-mgmt-containerservice, azure-mgmt-cosmosdb, azure-mgmt-datafactory, azure-mgmt-datalake-analytics, azure-mgmt-datalake-store, azure-mgmt-datamigration, azure-mgmt-devspaces, azure-mgmt-devtestlabs, azure-mgmt-dns, azure-mgmt-eventgrid, azure-mgmt-eventhub, azure-mgmt-hanaonazure, azure-mgmt-iotcentral, azure-mgmt-iothub, azure-mgmt-iothubprovisioningservices, azure-mgmt-keyvault, azure-mgmt-loganalytics, azure-mgmt-logic, azure-mgmt-machinelearningcompute, azure-mgmt-managementgroups, azure-mgmt-managementpartner, azure-mgmt-maps, azure-mgmt-marketplaceordering, azure-mgmt-media, azure-mgmt-monitor, azure-mgmt-msi, azure-mgmt-network, azure-mgmt-notificationhubs, azure-mgmt-policyinsights, azure-mgmt-powerbiembedded, azure-mgmt-rdbms, azure-mgmt-recoveryservices, azure-mgmt-recoveryservicesbackup, azure-mgmt-redis, azure-mgmt-relay, azure-mgmt-reservations, azure-mgmt-resource, azure-mgmt-scheduler, azure-mgmt-search, azure-mgmt-servicebus, azure-mgmt-servicefabric, azure-mgmt-signalr, azure-mgmt-sql, azure-mgmt-storage, azure-mgmt-subscription, azure-mgmt-trafficmanager, azure-mgmt-web -netaddr==0.7.19 -ntlm-auth==1.3.0 # via requests-ntlm, smbprotocol -oauthlib==3.0.1 # via requests-oauthlib -pathlib2==2.3.3 # via azure-datalake-store -profitbricks==4.1.3 -pyasn1==0.4.5 # via impacket, ldap3, smbprotocol -pycparser==2.19 # via cffi -pycryptodomex==3.8.1 # via impacket -pyjwt==1.7.1 # via adal -pyopenssl==19.0.0 # via impacket -pypsexec==0.1.0 -python-dateutil==2.8.0 # via adal, azure-cosmosdb-table, azure-storage-common -pywinrm==0.3.0 -requests-ntlm==1.1.0 # via pywinrm -requests-oauthlib==1.2.0 # via msrest -requests==2.21.0 # via adal, azure-cosmosdb-table, azure-datalake-store, azure-keyvault, azure-servicebus, azure-servicemanagement-legacy, azure-storage-common, msrest, profitbricks, pywinrm, requests-ntlm, requests-oauthlib -scandir==1.10.0 # via pathlib2 -six==1.12.0 # via cryptography, impacket, isodate, pathlib2, profitbricks, pyopenssl, pypsexec, python-dateutil, pywinrm, smbprotocol -smbprotocol==0.1.1 # via pypsexec -typing==3.6.6 # via msrest -urllib3==1.24.2 # via requests -werkzeug==0.15.6 # via flask -xmltodict==0.12.0 # via pywinrm diff --git a/requirements/static/py2.7/darwin-crypto.txt b/requirements/static/py2.7/darwin-crypto.txt deleted file mode 100644 index 28c30874aa4..00000000000 --- a/requirements/static/py2.7/darwin-crypto.txt +++ /dev/null @@ -1,9 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/darwin-crypto.txt -v requirements/static/crypto.in -# -m2crypto==0.35.2 -pycryptodomex==3.9.0 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py2.7/darwin.txt b/requirements/static/py2.7/darwin.txt deleted file mode 100644 index ea5d60a34b2..00000000000 --- a/requirements/static/py2.7/darwin.txt +++ /dev/null @@ -1,146 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in -# -apache-libcloud==2.4.0 -appdirs==1.4.3 # via virtualenv -argh==0.26.2 # via watchdog -asn1crypto==1.3.0 # via certvalidator, cryptography, oscrypto -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 ; python_version < "3.0" -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 -certvalidator==0.11.1 # via vcert -cffi==1.12.2 -chardet==3.0.4 # via requests -cheetah3==3.1.0 -cheroot==6.5.5 # via cherrypy -cherrypy==17.4.1 -click==7.0 -clustershell==1.8.1 -configparser==4.0.2 # via importlib-metadata -contextlib2==0.6.0.post1 # via cherrypy, importlib-metadata, importlib-resources, virtualenv -cookies==2.2.1 # via responses -croniter==0.3.29 -cryptography==2.6.1 -distlib==0.3.0 # via virtualenv -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.3 # via python-jose -enum34==1.1.6 -filelock==3.0.12 # via virtualenv -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -genshi==0.7.3 -gitdb2==2.0.5 # via gitpython -gitdb==0.6.4 -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -idna==2.8 -importlib-metadata==0.23 # via importlib-resources, pluggy, pytest, virtualenv -importlib-resources==1.3.1 # via virtualenv -ipaddress==1.0.22 -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -keyring==5.7.1 -kubernetes==3.0.0 -linode-python==1.1.1 -lxml==4.3.3 # via junos-eznc, ncclient -mako==1.0.7 -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.5.6 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -oscrypto==1.2.0 # via certvalidator -packaging==19.2 # via pytest -paramiko==2.4.2 # via junos-eznc, ncclient, scp -pathlib2==2.3.3 # via importlib-metadata, importlib-resources, pytest, virtualenv -pathtools==0.1.2 # via watchdog -pluggy==0.13.1 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 -pycparser==2.19 -pycryptodome==3.8.1 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyparsing==2.4.5 # via packaging -pyserial==3.4 # via junos-eznc -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.7.10 -pytest-salt==2019.12.27 -pytest-tempdir==2019.10.12 -pytest==4.6.6 -python-dateutil==2.8.0 -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==5.1.2 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -singledispatch==3.4.0.3 ; python_version < "3.4" -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, packaging, pathlib2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, vcert, virtualenv, websocket-client -smmap2==2.0.5 # via gitdb2 -smmap==0.9.0 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -typing==3.7.4.1 # via importlib-resources -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -vcert==0.7.3 -virtualenv==20.0.10 -vultr==1.0.1 -watchdog==0.9.0 -wcwidth==0.1.7 # via pytest -websocket-client==0.40.0 # via docker, kubernetes -wempy==0.1.5 ; python_version < "3" -werkzeug==0.15.6 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -yamlordereddictloader==0.4.0 -zc.lockfile==1.4 # via cherrypy -zipp==0.6.0 # via importlib-metadata, importlib-resources -# Passthrough dependencies from pkg/osx/req.txt -pyobjc==5.1.2 diff --git a/requirements/static/py2.7/linux-crypto.txt b/requirements/static/py2.7/linux-crypto.txt deleted file mode 100644 index 5f46f1c9e77..00000000000 --- a/requirements/static/py2.7/linux-crypto.txt +++ /dev/null @@ -1,9 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/linux-crypto.txt -v requirements/static/crypto.in -# -m2crypto==0.35.2 -pycryptodomex==3.9.0 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py2.7/linux.txt b/requirements/static/py2.7/linux.txt deleted file mode 100644 index b6bab5e0f61..00000000000 --- a/requirements/static/py2.7/linux.txt +++ /dev/null @@ -1,138 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/linux.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/linux.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==1.3.0 # via certvalidator, cryptography, oscrypto -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 ; python_version < "3.0" -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 -certvalidator==0.11.1 # via vcert -cffi==1.12.2 -chardet==3.0.4 # via requests -cheetah3==3.1.0 -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -configparser==4.0.2 # via importlib-metadata -contextlib2==0.5.5 # via cherrypy, importlib-metadata -cookies==2.2.1 # via responses -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl, vcert -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.3 # via python-jose -enum34==1.1.6 # via cryptography, vcert -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -genshi==0.7.3 -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -hgtools==8.1.1 -idna==2.8 # via requests -importlib-metadata==0.23 # via pluggy, pytest -ipaddress==1.0.22 # via cryptography, docker, kubernetes, vcert -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -libnacl==1.6.0 -lxml==4.3.3 # via junos-eznc, ncclient -mako==1.1.0 -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 -more-itertools==5.0.0 -moto==1.3.7 -msgpack==0.5.6 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -oscrypto==1.2.0 # via certvalidator -packaging==19.2 # via pytest -paramiko==2.4.2 -pathlib2==2.3.3 # via importlib-metadata, pytest -pathtools==0.1.2 # via watchdog -pluggy==0.13.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -pycrypto==2.6.1 ; sys_platform not in "win32,darwin" -pycryptodome==3.8.1 # via python-jose -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyparsing==2.4.5 # via packaging -pyserial==3.4 # via junos-eznc -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.7.10 -pytest-salt==2019.12.27 -pytest-tempdir==2019.10.12 -pytest==4.6.6 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto, vcert -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==5.1.2 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -setuptools-scm==3.2.0 -singledispatch==3.4.0.3 ; python_version < "3.4" -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, packaging, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, vcert, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -vcert==0.7.3 -virtualenv==16.4.3 -watchdog==0.9.0 -wcwidth==0.1.7 # via pytest -websocket-client==0.40.0 # via docker, kubernetes -wempy==0.1.5 ; python_version < "3" -werkzeug==0.15.6 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy -zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py2.7/windows-crypto.txt b/requirements/static/py2.7/windows-crypto.txt deleted file mode 100644 index 4fc8e9899d1..00000000000 --- a/requirements/static/py2.7/windows-crypto.txt +++ /dev/null @@ -1,9 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/windows-crypto.txt -v requirements/static/crypto.in -# -m2crypto==0.35.2 -pycryptodomex==3.9.0 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py2.7/windows.txt b/requirements/static/py2.7/windows.txt deleted file mode 100644 index 340ca703070..00000000000 --- a/requirements/static/py2.7/windows.txt +++ /dev/null @@ -1,134 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py2.7/windows.txt -v pkg/windows/req.txt pkg/windows/req_win.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/windows.in -# -argh==0.26.2 # via watchdog -asn1crypto==0.24.0 # via cryptography -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports-abc==0.5 ; python_version < "3.0" -backports.functools-lru-cache==1.5 # via cheroot, jaraco.functools -backports.ssl-match-hostname==3.7.0.1 -backports.tempfile==1.0 # via moto -backports.weakref==1.0.post1 # via backports.tempfile -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 -cffi==1.12.2 -chardet==3.0.4 # via requests -cheetah3==3.1.0 -cheroot==6.5.5 # via cherrypy -cherrypy==17.4.1 -colorama==0.4.1 # via pytest -configparser==4.0.2 # via importlib-metadata -contextlib2==0.5.5 # via cherrypy, importlib-metadata -cookies==2.2.1 # via responses -cryptography==2.6.1 -dmidecode==0.9.0 -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==2.7.0 -docutils==0.14 # via botocore -ecdsa==0.13.3 # via python-jose -enum34==1.1.6 -funcsigs==1.0.2 # via mock, pytest -functools32==3.2.3.post2 # via jsonschema -future==0.17.1 # via python-jose -futures==3.2.0 ; python_version < "3.0" -genshi==0.7.3 -gitdb2==2.0.5 # via gitpython -gitdb==0.6.4 -gitpython==2.1.10 -google-auth==1.6.3 # via kubernetes -idna==2.8 -importlib-metadata==0.23 # via pluggy, pytest -ioloop==0.1a0 -ipaddress==1.0.22 -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -keyring==5.7.1 -kubernetes==3.0.0 -libnacl==1.6.1 -lxml==4.3.0 -mako==1.0.7 -markupsafe==1.1.1 -meld3==1.0.2 # via supervisor -mock==3.0.5 ; python_version < "3.6" -more-itertools==5.0.0 -moto==1.3.7 -msgpack-python==0.5.6 -msgpack==0.5.6 -packaging==19.2 # via pytest -patch==1.16 -pathlib2==2.3.3 # via importlib-metadata, pytest -pathtools==0.1.2 # via watchdog -pluggy==0.13.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 -pycparser==2.19 -pycryptodome==3.8.1 # via python-jose -pycryptodomex==3.8.1 ; sys_platform == "win32" -pycurl==7.43.0.2 -pygit2==0.28.2 -pymssql==2.1.4 -pymysql==0.9.3 -pyopenssl==19.0.0 -pyparsing==2.4.5 # via packaging -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.7.10 -pytest-salt==2019.12.27 -pytest-tempdir==2019.10.12 -pytest==4.6.6 -python-dateutil==2.8.0 -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pythonnet==2.3.0 -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pywin32==224 -pyyaml==5.1.2 -pyzmq==18.0.1 ; python_version != "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -sed==0.3.1 -setproctitle==1.1.10 -singledispatch==3.4.0.3 ; python_version < "3.4" -six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, packaging, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, singledispatch, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -smmap==0.9.0 -strict-rfc3339==0.7 -supervisor==3.3.5 ; python_version < "3" -tempora==1.14.1 # via portend -timelib==0.2.4 -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -virtualenv==16.4.3 -watchdog==0.9.0 -wcwidth==0.1.7 # via pytest -websocket-client==0.40.0 # via docker, kubernetes -wempy==0.1.5 ; python_version < "3" -werkzeug==0.15.6 # via moto -wheel==0.33.4 -wmi==1.4.9 -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy -zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py3.4/cloud.txt b/requirements/static/py3.4/cloud.txt deleted file mode 100644 index 948a2ab717e..00000000000 --- a/requirements/static/py3.4/cloud.txt +++ /dev/null @@ -1,116 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/cloud.txt -v requirements/static/cloud.in -# -adal==1.2.1 # via azure-datalake-store, msrestazure -asn1crypto==0.24.0 # via cryptography -azure-applicationinsights==0.1.0 # via azure -azure-batch==4.1.3 # via azure -azure-common==1.1.18 # via azure-applicationinsights, azure-batch, azure-cosmosdb-table, azure-eventgrid, azure-graphrbac, azure-keyvault, azure-loganalytics, azure-mgmt-advisor, azure-mgmt-applicationinsights, azure-mgmt-authorization, azure-mgmt-batch, azure-mgmt-batchai, azure-mgmt-billing, azure-mgmt-cdn, azure-mgmt-cognitiveservices, azure-mgmt-commerce, azure-mgmt-compute, azure-mgmt-consumption, azure-mgmt-containerinstance, azure-mgmt-containerregistry, azure-mgmt-containerservice, azure-mgmt-cosmosdb, azure-mgmt-datafactory, azure-mgmt-datalake-analytics, azure-mgmt-datalake-store, azure-mgmt-datamigration, azure-mgmt-devspaces, azure-mgmt-devtestlabs, azure-mgmt-dns, azure-mgmt-eventgrid, azure-mgmt-eventhub, azure-mgmt-hanaonazure, azure-mgmt-iotcentral, azure-mgmt-iothub, azure-mgmt-iothubprovisioningservices, azure-mgmt-keyvault, azure-mgmt-loganalytics, azure-mgmt-logic, azure-mgmt-machinelearningcompute, azure-mgmt-managementgroups, azure-mgmt-managementpartner, azure-mgmt-maps, azure-mgmt-marketplaceordering, azure-mgmt-media, azure-mgmt-monitor, azure-mgmt-msi, azure-mgmt-network, azure-mgmt-notificationhubs, azure-mgmt-policyinsights, azure-mgmt-powerbiembedded, azure-mgmt-rdbms, azure-mgmt-recoveryservices, azure-mgmt-recoveryservicesbackup, azure-mgmt-redis, azure-mgmt-relay, azure-mgmt-reservations, azure-mgmt-resource, azure-mgmt-scheduler, azure-mgmt-search, azure-mgmt-servicebus, azure-mgmt-servicefabric, azure-mgmt-signalr, azure-mgmt-sql, azure-mgmt-storage, azure-mgmt-subscription, azure-mgmt-trafficmanager, azure-mgmt-web, azure-servicebus, azure-servicefabric, azure-servicemanagement-legacy, azure-storage-blob, azure-storage-common, azure-storage-file, azure-storage-queue -azure-cosmosdb-nspkg==2.0.2 # via azure-cosmosdb-table -azure-cosmosdb-table==1.0.5 # via azure -azure-datalake-store==0.0.44 # via azure -azure-eventgrid==1.2.0 # via azure -azure-graphrbac==0.40.0 # via azure -azure-keyvault==1.1.0 # via azure -azure-loganalytics==0.1.0 # via azure -azure-mgmt-advisor==1.0.1 # via azure-mgmt -azure-mgmt-applicationinsights==0.1.1 # via azure-mgmt -azure-mgmt-authorization==0.50.0 # via azure-mgmt -azure-mgmt-batch==5.0.1 # via azure-mgmt -azure-mgmt-batchai==2.0.0 # via azure-mgmt -azure-mgmt-billing==0.2.0 # via azure-mgmt -azure-mgmt-cdn==3.1.0 # via azure-mgmt -azure-mgmt-cognitiveservices==3.0.0 # via azure-mgmt -azure-mgmt-commerce==1.0.1 # via azure-mgmt -azure-mgmt-compute==4.6.0 # via azure-mgmt -azure-mgmt-consumption==2.0.0 # via azure-mgmt -azure-mgmt-containerinstance==1.4.1 # via azure-mgmt -azure-mgmt-containerregistry==2.7.0 # via azure-mgmt -azure-mgmt-containerservice==4.4.0 # via azure-mgmt -azure-mgmt-cosmosdb==0.4.1 # via azure-mgmt -azure-mgmt-datafactory==0.6.0 # via azure-mgmt -azure-mgmt-datalake-analytics==0.6.0 # via azure-mgmt -azure-mgmt-datalake-nspkg==3.0.1 # via azure-mgmt-datalake-analytics, azure-mgmt-datalake-store -azure-mgmt-datalake-store==0.5.0 # via azure-mgmt -azure-mgmt-datamigration==1.0.0 # via azure-mgmt -azure-mgmt-devspaces==0.1.0 # via azure-mgmt -azure-mgmt-devtestlabs==2.2.0 # via azure-mgmt -azure-mgmt-dns==2.1.0 # via azure-mgmt -azure-mgmt-eventgrid==1.0.0 # via azure-mgmt -azure-mgmt-eventhub==2.5.0 # via azure-mgmt -azure-mgmt-hanaonazure==0.1.1 # via azure-mgmt -azure-mgmt-iotcentral==0.1.0 # via azure-mgmt -azure-mgmt-iothub==0.5.0 # via azure-mgmt -azure-mgmt-iothubprovisioningservices==0.2.0 # via azure-mgmt -azure-mgmt-keyvault==1.1.0 # via azure-mgmt -azure-mgmt-loganalytics==0.2.0 # via azure-mgmt -azure-mgmt-logic==3.0.0 # via azure-mgmt -azure-mgmt-machinelearningcompute==0.4.1 # via azure-mgmt -azure-mgmt-managementgroups==0.1.0 # via azure-mgmt -azure-mgmt-managementpartner==0.1.0 # via azure-mgmt -azure-mgmt-maps==0.1.0 # via azure-mgmt -azure-mgmt-marketplaceordering==0.1.0 # via azure-mgmt -azure-mgmt-media==1.0.0 # via azure-mgmt -azure-mgmt-monitor==0.5.2 # via azure-mgmt -azure-mgmt-msi==0.2.0 # via azure-mgmt -azure-mgmt-network==2.6.0 # via azure-mgmt -azure-mgmt-notificationhubs==2.0.0 # via azure-mgmt -azure-mgmt-nspkg==3.0.2 # via azure-mgmt-advisor, azure-mgmt-applicationinsights, azure-mgmt-authorization, azure-mgmt-batch, azure-mgmt-batchai, azure-mgmt-billing, azure-mgmt-cognitiveservices, azure-mgmt-commerce, azure-mgmt-consumption, azure-mgmt-cosmosdb, azure-mgmt-datafactory, azure-mgmt-datalake-nspkg, azure-mgmt-datamigration, azure-mgmt-devspaces, azure-mgmt-devtestlabs, azure-mgmt-dns, azure-mgmt-eventgrid, azure-mgmt-hanaonazure, azure-mgmt-iotcentral, azure-mgmt-iothub, azure-mgmt-iothubprovisioningservices, azure-mgmt-keyvault, azure-mgmt-loganalytics, azure-mgmt-logic, azure-mgmt-machinelearningcompute, azure-mgmt-managementgroups, azure-mgmt-managementpartner, azure-mgmt-maps, azure-mgmt-marketplaceordering, azure-mgmt-monitor, azure-mgmt-msi, azure-mgmt-notificationhubs, azure-mgmt-policyinsights, azure-mgmt-powerbiembedded, azure-mgmt-recoveryservices, azure-mgmt-recoveryservicesbackup, azure-mgmt-redis, azure-mgmt-relay, azure-mgmt-reservations, azure-mgmt-scheduler, azure-mgmt-search, azure-mgmt-servicefabric, azure-mgmt-signalr, azure-mgmt-sql, azure-mgmt-storage, azure-mgmt-subscription, azure-mgmt-trafficmanager, azure-mgmt-web -azure-mgmt-policyinsights==0.1.0 # via azure-mgmt -azure-mgmt-powerbiembedded==2.0.0 # via azure-mgmt -azure-mgmt-rdbms==1.8.0 # via azure-mgmt -azure-mgmt-recoveryservices==0.3.0 # via azure-mgmt -azure-mgmt-recoveryservicesbackup==0.3.0 # via azure-mgmt -azure-mgmt-redis==5.0.0 # via azure-mgmt -azure-mgmt-relay==0.1.0 # via azure-mgmt -azure-mgmt-reservations==0.2.1 # via azure-mgmt -azure-mgmt-resource==2.1.0 # via azure-mgmt -azure-mgmt-scheduler==2.0.0 # via azure-mgmt -azure-mgmt-search==2.0.0 # via azure-mgmt -azure-mgmt-servicebus==0.5.3 # via azure-mgmt -azure-mgmt-servicefabric==0.2.0 # via azure-mgmt -azure-mgmt-signalr==0.1.1 # via azure-mgmt -azure-mgmt-sql==0.9.1 # via azure-mgmt -azure-mgmt-storage==2.0.0 # via azure-mgmt -azure-mgmt-subscription==0.2.0 # via azure-mgmt -azure-mgmt-trafficmanager==0.50.0 # via azure-mgmt -azure-mgmt-web==0.35.0 # via azure-mgmt -azure-mgmt==4.0.0 # via azure -azure-nspkg==3.0.2 # via azure-applicationinsights, azure-batch, azure-cosmosdb-nspkg, azure-eventgrid, azure-graphrbac, azure-keyvault, azure-loganalytics, azure-mgmt-nspkg, azure-servicebus, azure-servicefabric, azure-servicemanagement-legacy -azure-servicebus==0.21.1 # via azure -azure-servicefabric==6.3.0.0 # via azure -azure-servicemanagement-legacy==0.20.6 # via azure -azure-storage-blob==1.5.0 # via azure -azure-storage-common==1.4.0 # via azure-cosmosdb-table, azure-storage-blob, azure-storage-file, azure-storage-queue -azure-storage-file==1.4.0 # via azure -azure-storage-queue==1.4.0 # via azure -azure==4.0.0 -certifi==2019.3.9 # via msrest, requests -cffi==1.12.2 # via azure-datalake-store, cryptography -chardet==3.0.4 # via requests -cryptography==2.6.1 # via adal, azure-cosmosdb-table, azure-keyvault, azure-storage-common, requests-ntlm, smbprotocol -idna==2.8 # via requests -isodate==0.6.0 # via msrest -msrest==0.6.6 # via azure-applicationinsights, azure-eventgrid, azure-keyvault, azure-loganalytics, azure-mgmt-cdn, azure-mgmt-compute, azure-mgmt-containerinstance, azure-mgmt-containerregistry, azure-mgmt-containerservice, azure-mgmt-dns, azure-mgmt-eventhub, azure-mgmt-keyvault, azure-mgmt-media, azure-mgmt-network, azure-mgmt-rdbms, azure-mgmt-resource, azure-mgmt-servicebus, azure-mgmt-servicefabric, azure-mgmt-signalr, azure-servicefabric, msrestazure -msrestazure==0.6.0 # via azure-batch, azure-eventgrid, azure-graphrbac, azure-keyvault, azure-mgmt-advisor, azure-mgmt-applicationinsights, azure-mgmt-authorization, azure-mgmt-batch, azure-mgmt-batchai, azure-mgmt-billing, azure-mgmt-cdn, azure-mgmt-cognitiveservices, azure-mgmt-commerce, azure-mgmt-compute, azure-mgmt-consumption, azure-mgmt-containerinstance, azure-mgmt-containerregistry, azure-mgmt-containerservice, azure-mgmt-cosmosdb, azure-mgmt-datafactory, azure-mgmt-datalake-analytics, azure-mgmt-datalake-store, azure-mgmt-datamigration, azure-mgmt-devspaces, azure-mgmt-devtestlabs, azure-mgmt-dns, azure-mgmt-eventgrid, azure-mgmt-eventhub, azure-mgmt-hanaonazure, azure-mgmt-iotcentral, azure-mgmt-iothub, azure-mgmt-iothubprovisioningservices, azure-mgmt-keyvault, azure-mgmt-loganalytics, azure-mgmt-logic, azure-mgmt-machinelearningcompute, azure-mgmt-managementgroups, azure-mgmt-managementpartner, azure-mgmt-maps, azure-mgmt-marketplaceordering, azure-mgmt-media, azure-mgmt-monitor, azure-mgmt-msi, azure-mgmt-network, azure-mgmt-notificationhubs, azure-mgmt-policyinsights, azure-mgmt-powerbiembedded, azure-mgmt-rdbms, azure-mgmt-recoveryservices, azure-mgmt-recoveryservicesbackup, azure-mgmt-redis, azure-mgmt-relay, azure-mgmt-reservations, azure-mgmt-resource, azure-mgmt-scheduler, azure-mgmt-search, azure-mgmt-servicebus, azure-mgmt-servicefabric, azure-mgmt-signalr, azure-mgmt-sql, azure-mgmt-storage, azure-mgmt-subscription, azure-mgmt-trafficmanager, azure-mgmt-web -netaddr==0.7.19 -ntlm-auth==1.3.0 # via requests-ntlm, smbprotocol -oauthlib==3.0.1 # via requests-oauthlib -profitbricks==4.1.3 -pyasn1==0.4.5 # via smbprotocol -pycparser==2.19 # via cffi -pyjwt==1.7.1 # via adal -pypsexec==0.1.0 -python-dateutil==2.8.0 # via adal, azure-cosmosdb-table, azure-storage-common -pywinrm==0.3.0 -requests-ntlm==1.1.0 # via pywinrm -requests-oauthlib==1.2.0 # via msrest -requests==2.21.0 # via adal, azure-cosmosdb-table, azure-datalake-store, azure-keyvault, azure-servicebus, azure-servicemanagement-legacy, azure-storage-common, msrest, profitbricks, pywinrm, requests-ntlm, requests-oauthlib -six==1.12.0 # via cryptography, isodate, profitbricks, pypsexec, python-dateutil, pywinrm, smbprotocol -smbprotocol==0.1.1 # via pypsexec -typing==3.6.6 # via msrest -urllib3==1.24.2 # via requests -xmltodict==0.12.0 # via pywinrm diff --git a/requirements/static/py3.4/linux-crypto.txt b/requirements/static/py3.4/linux-crypto.txt deleted file mode 100644 index a83659e7678..00000000000 --- a/requirements/static/py3.4/linux-crypto.txt +++ /dev/null @@ -1,9 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/linux-crypto.txt -v requirements/static/crypto.in -# -m2crypto==0.35.2 -pycryptodomex==3.9.3 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.4/linux.txt b/requirements/static/py3.4/linux.txt deleted file mode 100644 index 4bc5ed8cc94..00000000000 --- a/requirements/static/py3.4/linux.txt +++ /dev/null @@ -1,125 +0,0 @@ -# -# This file is autogenerated by pip-compile -# To update, run: -# -# pip-compile -o requirements/static/py3.4/linux.txt -v requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/linux.in -# -apache-libcloud==2.0.0 -argh==0.26.2 # via watchdog -asn1crypto==1.3.0 # via certvalidator, cryptography, oscrypto -atomicwrites==1.3.0 # via pytest -attrs==19.1.0 # via pytest -aws-xray-sdk==0.95 # via moto -backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via docker, websocket-client -bcrypt==3.1.6 # via paramiko -boto3==1.9.132 -boto==2.49.0 -botocore==1.12.132 # via boto3, moto, s3transfer -cachetools==3.1.0 # via google-auth -certifi==2019.3.9 -certvalidator==0.11.1 # via vcert -cffi==1.12.2 -chardet==3.0.4 # via requests -cheetah3==3.1.0 -cheroot==6.5.4 # via cherrypy -cherrypy==17.3.0 -contextlib2==0.5.5 # via cherrypy -croniter==0.3.29 -cryptography==2.6.1 # via moto, paramiko, pyopenssl, vcert -dnspython==1.16.0 -docker-pycreds==0.4.0 # via docker -docker==3.7.2 -docutils==0.14 # via botocore -ecdsa==0.13.3 # via python-jose -future==0.17.1 # via python-jose -genshi==0.7.3 -gitdb2==2.0.5 # via gitpython -gitpython==2.1.11 -google-auth==1.6.3 # via kubernetes -hgtools==8.1.1 -idna==2.8 # via requests -importlib-metadata==0.23 # via pluggy, pytest -ipaddress==1.0.22 # via kubernetes -jaraco.functools==2.0 # via tempora -jinja2==2.10.1 -jmespath==0.9.4 -jsondiff==1.1.1 # via moto -jsonpickle==1.1 # via aws-xray-sdk -jsonschema==2.6.0 -junos-eznc==2.2.0 -jxmlease==1.0.1 -kazoo==2.6.1 -keyring==5.7.1 -kubernetes==3.0.0 -libnacl==1.6.0 -lxml==4.3.3 # via junos-eznc, ncclient -mako==1.1.0 -markupsafe==1.1.1 -mock==3.0.5 -more-itertools==5.0.0 -moto==1.3.7 -msgpack==0.5.6 -ncclient==0.6.4 # via junos-eznc -netaddr==0.7.19 # via junos-eznc -oscrypto==1.2.0 # via certvalidator -packaging==19.2 # via pytest -paramiko==2.4.2 -pathlib2==2.3.3 # via importlib-metadata, pytest -pathtools==0.1.2 # via watchdog -pluggy==0.13.0 # via pytest -portend==2.4 # via cherrypy -psutil==5.6.1 -py==1.8.0 # via pytest -pyaml==19.4.1 # via moto -pyasn1-modules==0.2.4 # via google-auth -pyasn1==0.4.5 # via paramiko, pyasn1-modules, rsa -pycparser==2.19 # via cffi -pycrypto==2.6.1 ; sys_platform not in "win32,darwin" -pycryptodome==3.8.1 # via python-jose -pygit2==0.28.2 -pyinotify==0.9.6 -pynacl==1.3.0 # via paramiko -pyopenssl==19.0.0 -pyparsing==2.4.5 # via packaging -pyserial==3.4 # via junos-eznc -pytest-helpers-namespace==2019.1.8 -pytest-salt-runtests-bridge==2019.7.10 -pytest-salt==2019.12.27 -pytest-tempdir==2019.10.12 -pytest==4.6.6 -python-dateutil==2.8.0 # via botocore, croniter, kubernetes, moto, vcert -python-etcd==0.4.5 -python-gnupg==0.4.4 -python-jose==2.0.2 # via moto -pytz==2019.1 # via moto, tempora -pyvmomi==6.7.1.2018.12 -pyyaml==5.1.2 -pyzmq==17.0.0 ; python_version == "3.4" -requests==2.21.0 -responses==0.10.6 # via moto -rfc3987==1.3.8 -rsa==4.0 # via google-auth -s3transfer==0.2.0 # via boto3 -salttesting==2017.6.1 -scandir==1.10.0 # via pathlib2 -scp==0.13.2 # via junos-eznc -selectors2==2.0.1 # via ncclient -setproctitle==1.1.10 -setuptools-scm==3.2.0 -six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kazoo, kubernetes, mock, more-itertools, moto, ncclient, packaging, pathlib2, pygit2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, vcert, websocket-client -smmap2==2.0.5 # via gitdb2 -strict-rfc3339==0.7 -tempora==1.14.1 # via portend -timelib==0.2.4 -urllib3==1.24.2 # via botocore, kubernetes, python-etcd, requests -vcert==0.7.3 -virtualenv==16.4.3 -watchdog==0.9.0 -wcwidth==0.1.7 # via pytest -websocket-client==0.40.0 # via docker, kubernetes -werkzeug==0.15.6 # via moto -wrapt==1.11.1 # via aws-xray-sdk -xmltodict==0.12.0 # via moto -zc.lockfile==1.4 # via cherrypy -zipp==0.6.0 # via importlib-metadata diff --git a/requirements/static/py3.5/changelog.txt b/requirements/static/py3.5/changelog.txt new file mode 100644 index 00000000000..ecef1b39a0e --- /dev/null +++ b/requirements/static/py3.5/changelog.txt @@ -0,0 +1,12 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.5/changelog.txt -v requirements/static/changelog.in +# +click==7.1.1 # via towncrier +incremental==17.5.0 # via towncrier +jinja2==2.11.2 # via towncrier +markupsafe==1.1.1 # via jinja2 +toml==0.10.0 # via towncrier +towncrier==19.2.0 diff --git a/requirements/static/py3.5/darwin-crypto.txt b/requirements/static/py3.5/darwin-crypto.txt index a6a86036f2e..492a8bd1855 100644 --- a/requirements/static/py3.5/darwin-crypto.txt +++ b/requirements/static/py3.5/darwin-crypto.txt @@ -6,4 +6,3 @@ # m2crypto==0.35.2 pycryptodomex==3.9.0 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.5/darwin.txt b/requirements/static/py3.5/darwin.txt index ed303ecfb2d..0b1aba1a7cf 100644 --- a/requirements/static/py3.5/darwin.txt +++ b/requirements/static/py3.5/darwin.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.5/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in +# pip-compile -o requirements/static/py3.5/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt pkg/osx/req_pyobjc.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in # apache-libcloud==2.4.0 appdirs==1.4.3 # via virtualenv @@ -41,8 +41,7 @@ filelock==3.0.12 # via virtualenv future==0.17.1 # via python-jose genshi==0.7.3 gitdb2==2.0.5 # via gitpython -gitdb==0.6.4 -gitpython==2.1.11 +gitpython==2.1.15 google-auth==1.6.3 # via kubernetes idna==2.8 importlib-metadata==0.23 # via importlib-resources, pluggy, pytest, virtualenv @@ -76,7 +75,7 @@ pathlib2==2.3.3 # via pytest pathtools==0.1.2 # via watchdog pluggy==0.13.1 # via pytest portend==2.4 # via cherrypy -psutil==5.6.1 +psutil==5.6.6 py==1.8.0 # via pytest pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth @@ -110,7 +109,6 @@ scp==0.13.2 # via junos-eznc setproctitle==1.1.10 six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, packaging, pathlib2, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, vcert, virtualenv, websocket-client smmap2==2.0.5 # via gitdb2 -smmap==0.9.0 strict-rfc3339==0.7 tempora==1.14.1 # via portend timelib==0.2.4 @@ -127,5 +125,102 @@ xmltodict==0.12.0 # via moto yamlordereddictloader==0.4.0 zc.lockfile==1.4 # via cherrypy zipp==0.6.0 # via importlib-metadata, importlib-resources -# Passthrough dependencies from pkg/osx/req.txt +# Passthrough dependencies from pkg/osx/req_pyobjc.txt +pyobjc-core==5.1.2 # via pyobjc, pyobjc-framework-accounts, pyobjc-framework-addressbook, pyobjc-framework-adsupport, pyobjc-framework-applescriptkit, pyobjc-framework-applescriptobjc, pyobjc-framework-applicationservices, pyobjc-framework-automator, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-businesschat, pyobjc-framework-calendarstore, pyobjc-framework-cfnetwork, pyobjc-framework-cloudkit, pyobjc-framework-cocoa, pyobjc-framework-collaboration, pyobjc-framework-colorsync, pyobjc-framework-contacts, pyobjc-framework-contactsui, pyobjc-framework-coreaudio, pyobjc-framework-coreaudiokit, pyobjc-framework-corebluetooth, pyobjc-framework-coredata, pyobjc-framework-corelocation, pyobjc-framework-coremedia, pyobjc-framework-coremediaio, pyobjc-framework-coreml, pyobjc-framework-coreservices, pyobjc-framework-corespotlight, pyobjc-framework-coretext, pyobjc-framework-corewlan, pyobjc-framework-cryptotokenkit, pyobjc-framework-dictionaryservices, pyobjc-framework-discrecording, pyobjc-framework-discrecordingui, pyobjc-framework-diskarbitration, pyobjc-framework-dvdplayback, pyobjc-framework-eventkit, pyobjc-framework-exceptionhandling, pyobjc-framework-externalaccessory, pyobjc-framework-findersync, pyobjc-framework-fsevents, pyobjc-framework-gamecenter, pyobjc-framework-gamecontroller, pyobjc-framework-gamekit, pyobjc-framework-gameplaykit, pyobjc-framework-imagecapturecore, pyobjc-framework-imserviceplugin, pyobjc-framework-inputmethodkit, pyobjc-framework-installerplugins, pyobjc-framework-instantmessage, pyobjc-framework-intents, pyobjc-framework-iosurface, pyobjc-framework-ituneslibrary, pyobjc-framework-latentsemanticmapping, pyobjc-framework-launchservices, pyobjc-framework-libdispatch, pyobjc-framework-localauthentication, pyobjc-framework-mapkit, pyobjc-framework-mediaaccessibility, pyobjc-framework-medialibrary, pyobjc-framework-mediaplayer, pyobjc-framework-mediatoolbox, pyobjc-framework-modelio, pyobjc-framework-multipeerconnectivity, pyobjc-framework-naturallanguage, pyobjc-framework-netfs, pyobjc-framework-network, pyobjc-framework-networkextension, pyobjc-framework-notificationcenter, pyobjc-framework-opendirectory, pyobjc-framework-osakit, pyobjc-framework-photos, pyobjc-framework-photosui, pyobjc-framework-preferencepanes, pyobjc-framework-pubsub, pyobjc-framework-qtkit, pyobjc-framework-quartz, pyobjc-framework-safariservices, pyobjc-framework-scenekit, pyobjc-framework-screensaver, pyobjc-framework-scriptingbridge, pyobjc-framework-searchkit, pyobjc-framework-security, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface, pyobjc-framework-servicemanagement, pyobjc-framework-social, pyobjc-framework-spritekit, pyobjc-framework-storekit, pyobjc-framework-syncservices, pyobjc-framework-systemconfiguration, pyobjc-framework-usernotifications, pyobjc-framework-videosubscriberaccount, pyobjc-framework-videotoolbox, pyobjc-framework-vision, pyobjc-framework-webkit +pyobjc-framework-accounts==5.1.2 # via pyobjc, pyobjc-framework-cloudkit +pyobjc-framework-addressbook==5.1.2 # via pyobjc +pyobjc-framework-adsupport==5.1.2 # via pyobjc +pyobjc-framework-applescriptkit==5.1.2 # via pyobjc +pyobjc-framework-applescriptobjc==5.1.2 # via pyobjc +pyobjc-framework-applicationservices==5.1.2 # via pyobjc +pyobjc-framework-automator==5.1.2 # via pyobjc +pyobjc-framework-avfoundation==5.1.2 # via pyobjc, pyobjc-framework-mediaplayer +pyobjc-framework-avkit==5.1.2 # via pyobjc +pyobjc-framework-businesschat==5.1.2 # via pyobjc +pyobjc-framework-calendarstore==5.1.2 # via pyobjc +pyobjc-framework-cfnetwork==5.1.2 # via pyobjc +pyobjc-framework-cloudkit==5.1.2 # via pyobjc +pyobjc-framework-cocoa==5.1.2 # via pyobjc, pyobjc-framework-accounts, pyobjc-framework-addressbook, pyobjc-framework-adsupport, pyobjc-framework-applescriptkit, pyobjc-framework-applescriptobjc, pyobjc-framework-applicationservices, pyobjc-framework-automator, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-businesschat, pyobjc-framework-calendarstore, pyobjc-framework-cfnetwork, pyobjc-framework-cloudkit, pyobjc-framework-collaboration, pyobjc-framework-colorsync, pyobjc-framework-contacts, pyobjc-framework-contactsui, pyobjc-framework-coreaudio, pyobjc-framework-coreaudiokit, pyobjc-framework-corebluetooth, pyobjc-framework-coredata, pyobjc-framework-corelocation, pyobjc-framework-coremedia, pyobjc-framework-coremediaio, pyobjc-framework-coreml, pyobjc-framework-corespotlight, pyobjc-framework-coretext, pyobjc-framework-corewlan, pyobjc-framework-cryptotokenkit, pyobjc-framework-discrecording, pyobjc-framework-discrecordingui, pyobjc-framework-diskarbitration, pyobjc-framework-dvdplayback, pyobjc-framework-eventkit, pyobjc-framework-exceptionhandling, pyobjc-framework-externalaccessory, pyobjc-framework-findersync, pyobjc-framework-fsevents, pyobjc-framework-gamecenter, pyobjc-framework-gamecontroller, pyobjc-framework-gamekit, pyobjc-framework-gameplaykit, pyobjc-framework-imagecapturecore, pyobjc-framework-imserviceplugin, pyobjc-framework-inputmethodkit, pyobjc-framework-installerplugins, pyobjc-framework-instantmessage, pyobjc-framework-intents, pyobjc-framework-iosurface, pyobjc-framework-ituneslibrary, pyobjc-framework-latentsemanticmapping, pyobjc-framework-localauthentication, pyobjc-framework-mapkit, pyobjc-framework-mediaaccessibility, pyobjc-framework-medialibrary, pyobjc-framework-mediatoolbox, pyobjc-framework-modelio, pyobjc-framework-multipeerconnectivity, pyobjc-framework-naturallanguage, pyobjc-framework-netfs, pyobjc-framework-network, pyobjc-framework-networkextension, pyobjc-framework-notificationcenter, pyobjc-framework-opendirectory, pyobjc-framework-osakit, pyobjc-framework-photos, pyobjc-framework-photosui, pyobjc-framework-preferencepanes, pyobjc-framework-pubsub, pyobjc-framework-qtkit, pyobjc-framework-quartz, pyobjc-framework-safariservices, pyobjc-framework-scenekit, pyobjc-framework-screensaver, pyobjc-framework-scriptingbridge, pyobjc-framework-security, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface, pyobjc-framework-servicemanagement, pyobjc-framework-social, pyobjc-framework-spritekit, pyobjc-framework-storekit, pyobjc-framework-syncservices, pyobjc-framework-systemconfiguration, pyobjc-framework-usernotifications, pyobjc-framework-videosubscriberaccount, pyobjc-framework-videotoolbox, pyobjc-framework-vision, pyobjc-framework-webkit +pyobjc-framework-collaboration==5.1.2 # via pyobjc +pyobjc-framework-colorsync==5.1.2 # via pyobjc +pyobjc-framework-contacts==5.1.2 # via pyobjc, pyobjc-framework-contactsui +pyobjc-framework-contactsui==5.1.2 # via pyobjc +pyobjc-framework-coreaudio==5.1.2 # via pyobjc, pyobjc-framework-coreaudiokit +pyobjc-framework-coreaudiokit==5.1.2 # via pyobjc +pyobjc-framework-corebluetooth==5.1.2 # via pyobjc +pyobjc-framework-coredata==5.1.2 # via pyobjc, pyobjc-framework-cloudkit, pyobjc-framework-syncservices +pyobjc-framework-corelocation==5.1.2 # via pyobjc, pyobjc-framework-cloudkit, pyobjc-framework-mapkit +pyobjc-framework-coremedia==5.1.2 # via pyobjc, pyobjc-framework-videotoolbox +pyobjc-framework-coremediaio==5.1.2 # via pyobjc +pyobjc-framework-coreml==5.1.2 # via pyobjc, pyobjc-framework-vision +pyobjc-framework-coreservices==5.1.2 # via pyobjc, pyobjc-framework-dictionaryservices, pyobjc-framework-launchservices, pyobjc-framework-searchkit +pyobjc-framework-corespotlight==5.1.2 # via pyobjc +pyobjc-framework-coretext==5.1.2 # via pyobjc +pyobjc-framework-corewlan==5.1.2 # via pyobjc +pyobjc-framework-cryptotokenkit==5.1.2 # via pyobjc +pyobjc-framework-dictionaryservices==5.1.2 # via pyobjc +pyobjc-framework-discrecording==5.1.2 # via pyobjc, pyobjc-framework-discrecordingui +pyobjc-framework-discrecordingui==5.1.2 # via pyobjc +pyobjc-framework-diskarbitration==5.1.2 # via pyobjc +pyobjc-framework-dvdplayback==5.1.2 # via pyobjc +pyobjc-framework-eventkit==5.1.2 # via pyobjc +pyobjc-framework-exceptionhandling==5.1.2 # via pyobjc +pyobjc-framework-externalaccessory==5.1.2 # via pyobjc +pyobjc-framework-findersync==5.1.2 # via pyobjc +pyobjc-framework-fsevents==5.1.2 # via pyobjc, pyobjc-framework-coreservices +pyobjc-framework-gamecenter==5.1.2 # via pyobjc +pyobjc-framework-gamecontroller==5.1.2 # via pyobjc +pyobjc-framework-gamekit==5.1.2 # via pyobjc +pyobjc-framework-gameplaykit==5.1.2 # via pyobjc +pyobjc-framework-imagecapturecore==5.1.2 # via pyobjc +pyobjc-framework-imserviceplugin==5.1.2 # via pyobjc +pyobjc-framework-inputmethodkit==5.1.2 # via pyobjc +pyobjc-framework-installerplugins==5.1.2 # via pyobjc +pyobjc-framework-instantmessage==5.1.2 # via pyobjc +pyobjc-framework-intents==5.1.2 # via pyobjc +pyobjc-framework-iosurface==5.1.2 # via pyobjc +pyobjc-framework-ituneslibrary==5.1.2 # via pyobjc +pyobjc-framework-latentsemanticmapping==5.1.2 # via pyobjc +pyobjc-framework-launchservices==5.1.2 # via pyobjc +pyobjc-framework-libdispatch==5.1.2 # via pyobjc +pyobjc-framework-localauthentication==5.1.2 # via pyobjc +pyobjc-framework-mapkit==5.1.2 # via pyobjc +pyobjc-framework-mediaaccessibility==5.1.2 # via pyobjc +pyobjc-framework-medialibrary==5.1.2 # via pyobjc +pyobjc-framework-mediaplayer==5.1.2 # via pyobjc +pyobjc-framework-mediatoolbox==5.1.2 # via pyobjc +pyobjc-framework-modelio==5.1.2 # via pyobjc +pyobjc-framework-multipeerconnectivity==5.1.2 # via pyobjc +pyobjc-framework-naturallanguage==5.1.2 # via pyobjc +pyobjc-framework-netfs==5.1.2 # via pyobjc +pyobjc-framework-network==5.1.2 # via pyobjc +pyobjc-framework-networkextension==5.1.2 # via pyobjc +pyobjc-framework-notificationcenter==5.1.2 # via pyobjc +pyobjc-framework-opendirectory==5.1.2 # via pyobjc +pyobjc-framework-osakit==5.1.2 # via pyobjc +pyobjc-framework-photos==5.1.2 # via pyobjc +pyobjc-framework-photosui==5.1.2 # via pyobjc +pyobjc-framework-preferencepanes==5.1.2 # via pyobjc +pyobjc-framework-pubsub==5.1.2 # via pyobjc +pyobjc-framework-qtkit==5.1.2 # via pyobjc +pyobjc-framework-quartz==5.1.2 # via pyobjc, pyobjc-framework-applicationservices, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-coretext, pyobjc-framework-gamekit, pyobjc-framework-instantmessage, pyobjc-framework-mapkit, pyobjc-framework-medialibrary, pyobjc-framework-modelio, pyobjc-framework-qtkit, pyobjc-framework-scenekit, pyobjc-framework-spritekit, pyobjc-framework-videotoolbox, pyobjc-framework-vision +pyobjc-framework-safariservices==5.1.2 # via pyobjc +pyobjc-framework-scenekit==5.1.2 # via pyobjc +pyobjc-framework-screensaver==5.1.2 # via pyobjc +pyobjc-framework-scriptingbridge==5.1.2 # via pyobjc +pyobjc-framework-searchkit==5.1.2 # via pyobjc +pyobjc-framework-security==5.1.2 # via pyobjc, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface +pyobjc-framework-securityfoundation==5.1.2 # via pyobjc +pyobjc-framework-securityinterface==5.1.2 # via pyobjc +pyobjc-framework-servicemanagement==5.1.2 # via pyobjc +pyobjc-framework-social==5.1.2 # via pyobjc +pyobjc-framework-spritekit==5.1.2 # via pyobjc, pyobjc-framework-gameplaykit +pyobjc-framework-storekit==5.1.2 # via pyobjc +pyobjc-framework-syncservices==5.1.2 # via pyobjc +pyobjc-framework-systemconfiguration==5.1.2 # via pyobjc +pyobjc-framework-usernotifications==5.1.2 # via pyobjc +pyobjc-framework-videosubscriberaccount==5.1.2 # via pyobjc +pyobjc-framework-videotoolbox==5.1.2 # via pyobjc +pyobjc-framework-vision==5.1.2 # via pyobjc +pyobjc-framework-webkit==5.1.2 # via pyobjc pyobjc==5.1.2 diff --git a/requirements/static/py3.5/docs.txt b/requirements/static/py3.5/docs.txt index f159bfa46cd..716e7396cc9 100644 --- a/requirements/static/py3.5/docs.txt +++ b/requirements/static/py3.5/docs.txt @@ -5,26 +5,26 @@ # pip-compile -o requirements/static/py3.5/docs.txt -v requirements/static/docs.in # alabaster==0.7.12 # via sphinx -babel==2.7.0 # via sphinx -certifi==2019.3.9 # via requests +babel==2.8.0 # via sphinx +certifi==2020.4.5.1 # via requests chardet==3.0.4 # via requests -docutils==0.14 # via sphinx -idna==2.8 # via requests -imagesize==1.1.0 # via sphinx -jinja2==2.10.1 # via sphinx +docutils==0.16 # via sphinx +idna==2.9 # via requests +imagesize==1.2.0 # via sphinx +jinja2==2.11.2 # via sphinx markupsafe==1.1.1 # via jinja2 -packaging==19.0 # via sphinx -pygments==2.4.2 # via sphinx -pyparsing==2.4.0 # via packaging -pytz==2019.1 # via babel -requests==2.22.0 # via sphinx -six==1.12.0 # via packaging -snowballstemmer==1.2.1 # via sphinx -sphinx==2.0.1 -sphinxcontrib-applehelp==1.0.1 # via sphinx -sphinxcontrib-devhelp==1.0.1 # via sphinx -sphinxcontrib-htmlhelp==1.0.2 # via sphinx +packaging==20.3 # via sphinx +pygments==2.6.1 # via sphinx +pyparsing==2.4.7 # via packaging +pytz==2019.3 # via babel +requests==2.23.0 # via sphinx +six==1.14.0 # via packaging +snowballstemmer==2.0.0 # via sphinx +sphinx==3.0.1 +sphinxcontrib-applehelp==1.0.2 # via sphinx +sphinxcontrib-devhelp==1.0.2 # via sphinx +sphinxcontrib-htmlhelp==1.0.3 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx -sphinxcontrib-qthelp==1.0.2 # via sphinx -sphinxcontrib-serializinghtml==1.1.3 # via sphinx -urllib3==1.25.3 # via requests +sphinxcontrib-qthelp==1.0.3 # via sphinx +sphinxcontrib-serializinghtml==1.1.4 # via sphinx +urllib3==1.25.9 # via requests diff --git a/requirements/static/py3.5/lint.txt b/requirements/static/py3.5/lint.txt index b664bb5c135..0c00e2c843e 100644 --- a/requirements/static/py3.5/lint.txt +++ b/requirements/static/py3.5/lint.txt @@ -13,4 +13,5 @@ pycodestyle==2.5.0 # via saltpylint pylint==2.4.4 saltpylint==2019.11.14 six==1.12.0 # via astroid +typed-ast==1.4.1 # via astroid wrapt==1.11.1 # via astroid diff --git a/requirements/static/py3.5/linux-crypto.txt b/requirements/static/py3.5/linux-crypto.txt index 59c45171e55..6cdeaecccc4 100644 --- a/requirements/static/py3.5/linux-crypto.txt +++ b/requirements/static/py3.5/linux-crypto.txt @@ -6,4 +6,3 @@ # m2crypto==0.35.2 pycryptodomex==3.9.3 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.5/linux.txt b/requirements/static/py3.5/linux.txt index c3611cfbcc7..14666c9a6ea 100644 --- a/requirements/static/py3.5/linux.txt +++ b/requirements/static/py3.5/linux.txt @@ -11,7 +11,6 @@ atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 @@ -52,7 +51,7 @@ jxmlease==1.0.1 kazoo==2.6.1 keyring==5.7.1 kubernetes==3.0.0 -libnacl==1.6.0 +libnacl==1.7.1 lxml==4.3.3 # via junos-eznc, ncclient mako==1.1.0 markupsafe==1.1.1 @@ -69,7 +68,7 @@ pathlib2==2.3.3 # via pytest pathtools==0.1.2 # via watchdog pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy -psutil==5.6.1 +psutil==5.6.6 py==1.8.0 # via pytest pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth diff --git a/requirements/static/py3.5/windows-crypto.txt b/requirements/static/py3.5/windows-crypto.txt index 1e209d42238..091858543a9 100644 --- a/requirements/static/py3.5/windows-crypto.txt +++ b/requirements/static/py3.5/windows-crypto.txt @@ -6,4 +6,3 @@ # m2crypto==0.35.2 pycryptodomex==3.9.0 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.5/windows.txt b/requirements/static/py3.5/windows.txt index ff3940c2a86..bd1a5c93b4f 100644 --- a/requirements/static/py3.5/windows.txt +++ b/requirements/static/py3.5/windows.txt @@ -30,11 +30,9 @@ docker-pycreds==0.4.0 # via docker docker==2.7.0 docutils==0.14 # via botocore ecdsa==0.13.3 # via python-jose -enum34==1.1.6 future==0.17.1 # via python-jose genshi==0.7.3 -gitdb2==2.0.5 # via gitpython -gitdb==0.6.4 +gitdb2==2.0.5 gitpython==2.1.10 google-auth==1.6.3 # via kubernetes idna==2.8 @@ -49,7 +47,7 @@ jsonpickle==1.1 # via aws-xray-sdk jsonschema==2.6.0 keyring==5.7.1 kubernetes==3.0.0 -libnacl==1.6.1 +libnacl==1.7.1 lxml==4.3.0 mako==1.0.7 markupsafe==1.1.1 @@ -64,7 +62,7 @@ pathlib2==2.3.3 # via pytest pathtools==0.1.2 # via watchdog pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy -psutil==5.6.1 +psutil==5.6.6 py==1.8.0 # via pytest pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth @@ -72,7 +70,7 @@ pyasn1==0.4.5 pycparser==2.19 pycryptodome==3.8.1 # via python-jose pycryptodomex==3.8.1 ; sys_platform == "win32" -pycurl==7.43.0.2 +pycurl==7.43.0.5 pygit2==0.28.2 pymssql==2.1.4 pymysql==0.9.3 @@ -102,8 +100,7 @@ salttesting==2017.6.1 sed==0.3.1 setproctitle==1.1.10 six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, packaging, pathlib2, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -smmap==0.9.0 +smmap2==2.0.5 strict-rfc3339==0.7 tempora==1.14.1 # via portend timelib==0.2.4 diff --git a/requirements/static/py3.6/changelog.txt b/requirements/static/py3.6/changelog.txt new file mode 100644 index 00000000000..dd0c469a336 --- /dev/null +++ b/requirements/static/py3.6/changelog.txt @@ -0,0 +1,12 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.6/changelog.txt -v requirements/static/changelog.in +# +click==7.1.1 # via towncrier +incremental==17.5.0 # via towncrier +jinja2==2.11.2 # via towncrier +markupsafe==1.1.1 # via jinja2 +toml==0.10.0 # via towncrier +towncrier==19.2.0 diff --git a/requirements/static/py3.6/darwin-crypto.txt b/requirements/static/py3.6/darwin-crypto.txt index 9c11d937f08..3cc68e7cab0 100644 --- a/requirements/static/py3.6/darwin-crypto.txt +++ b/requirements/static/py3.6/darwin-crypto.txt @@ -6,4 +6,3 @@ # m2crypto==0.35.2 pycryptodomex==3.9.0 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.6/darwin.txt b/requirements/static/py3.6/darwin.txt index e2c69cf7d24..f535ab50668 100644 --- a/requirements/static/py3.6/darwin.txt +++ b/requirements/static/py3.6/darwin.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.6/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in +# pip-compile -o requirements/static/py3.6/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt pkg/osx/req_pyobjc.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in # apache-libcloud==2.4.0 appdirs==1.4.3 # via virtualenv @@ -41,8 +41,7 @@ filelock==3.0.12 # via virtualenv future==0.17.1 # via python-jose genshi==0.7.3 gitdb2==2.0.5 # via gitpython -gitdb==0.6.4 -gitpython==2.1.11 +gitpython==2.1.15 google-auth==1.6.3 # via kubernetes idna==2.8 importlib-metadata==0.23 # via importlib-resources, pluggy, pytest, virtualenv @@ -75,7 +74,7 @@ paramiko==2.4.2 # via junos-eznc, ncclient, scp pathtools==0.1.2 # via watchdog pluggy==0.13.1 # via pytest portend==2.4 # via cherrypy -psutil==5.6.1 +psutil==5.6.6 py==1.8.0 # via pytest pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth @@ -109,7 +108,6 @@ scp==0.13.2 # via junos-eznc setproctitle==1.1.10 six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, packaging, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, vcert, virtualenv, websocket-client smmap2==2.0.5 # via gitdb2 -smmap==0.9.0 strict-rfc3339==0.7 tempora==1.14.1 # via portend timelib==0.2.4 @@ -126,5 +124,102 @@ xmltodict==0.12.0 # via moto yamlordereddictloader==0.4.0 zc.lockfile==1.4 # via cherrypy zipp==0.6.0 # via importlib-metadata, importlib-resources -# Passthrough dependencies from pkg/osx/req.txt +# Passthrough dependencies from pkg/osx/req_pyobjc.txt +pyobjc-core==5.1.2 # via pyobjc, pyobjc-framework-accounts, pyobjc-framework-addressbook, pyobjc-framework-adsupport, pyobjc-framework-applescriptkit, pyobjc-framework-applescriptobjc, pyobjc-framework-applicationservices, pyobjc-framework-automator, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-businesschat, pyobjc-framework-calendarstore, pyobjc-framework-cfnetwork, pyobjc-framework-cloudkit, pyobjc-framework-cocoa, pyobjc-framework-collaboration, pyobjc-framework-colorsync, pyobjc-framework-contacts, pyobjc-framework-contactsui, pyobjc-framework-coreaudio, pyobjc-framework-coreaudiokit, pyobjc-framework-corebluetooth, pyobjc-framework-coredata, pyobjc-framework-corelocation, pyobjc-framework-coremedia, pyobjc-framework-coremediaio, pyobjc-framework-coreml, pyobjc-framework-coreservices, pyobjc-framework-corespotlight, pyobjc-framework-coretext, pyobjc-framework-corewlan, pyobjc-framework-cryptotokenkit, pyobjc-framework-dictionaryservices, pyobjc-framework-discrecording, pyobjc-framework-discrecordingui, pyobjc-framework-diskarbitration, pyobjc-framework-dvdplayback, pyobjc-framework-eventkit, pyobjc-framework-exceptionhandling, pyobjc-framework-externalaccessory, pyobjc-framework-findersync, pyobjc-framework-fsevents, pyobjc-framework-gamecenter, pyobjc-framework-gamecontroller, pyobjc-framework-gamekit, pyobjc-framework-gameplaykit, pyobjc-framework-imagecapturecore, pyobjc-framework-imserviceplugin, pyobjc-framework-inputmethodkit, pyobjc-framework-installerplugins, pyobjc-framework-instantmessage, pyobjc-framework-intents, pyobjc-framework-iosurface, pyobjc-framework-ituneslibrary, pyobjc-framework-latentsemanticmapping, pyobjc-framework-launchservices, pyobjc-framework-libdispatch, pyobjc-framework-localauthentication, pyobjc-framework-mapkit, pyobjc-framework-mediaaccessibility, pyobjc-framework-medialibrary, pyobjc-framework-mediaplayer, pyobjc-framework-mediatoolbox, pyobjc-framework-modelio, pyobjc-framework-multipeerconnectivity, pyobjc-framework-naturallanguage, pyobjc-framework-netfs, pyobjc-framework-network, pyobjc-framework-networkextension, pyobjc-framework-notificationcenter, pyobjc-framework-opendirectory, pyobjc-framework-osakit, pyobjc-framework-photos, pyobjc-framework-photosui, pyobjc-framework-preferencepanes, pyobjc-framework-pubsub, pyobjc-framework-qtkit, pyobjc-framework-quartz, pyobjc-framework-safariservices, pyobjc-framework-scenekit, pyobjc-framework-screensaver, pyobjc-framework-scriptingbridge, pyobjc-framework-searchkit, pyobjc-framework-security, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface, pyobjc-framework-servicemanagement, pyobjc-framework-social, pyobjc-framework-spritekit, pyobjc-framework-storekit, pyobjc-framework-syncservices, pyobjc-framework-systemconfiguration, pyobjc-framework-usernotifications, pyobjc-framework-videosubscriberaccount, pyobjc-framework-videotoolbox, pyobjc-framework-vision, pyobjc-framework-webkit +pyobjc-framework-accounts==5.1.2 # via pyobjc, pyobjc-framework-cloudkit +pyobjc-framework-addressbook==5.1.2 # via pyobjc +pyobjc-framework-adsupport==5.1.2 # via pyobjc +pyobjc-framework-applescriptkit==5.1.2 # via pyobjc +pyobjc-framework-applescriptobjc==5.1.2 # via pyobjc +pyobjc-framework-applicationservices==5.1.2 # via pyobjc +pyobjc-framework-automator==5.1.2 # via pyobjc +pyobjc-framework-avfoundation==5.1.2 # via pyobjc, pyobjc-framework-mediaplayer +pyobjc-framework-avkit==5.1.2 # via pyobjc +pyobjc-framework-businesschat==5.1.2 # via pyobjc +pyobjc-framework-calendarstore==5.1.2 # via pyobjc +pyobjc-framework-cfnetwork==5.1.2 # via pyobjc +pyobjc-framework-cloudkit==5.1.2 # via pyobjc +pyobjc-framework-cocoa==5.1.2 # via pyobjc, pyobjc-framework-accounts, pyobjc-framework-addressbook, pyobjc-framework-adsupport, pyobjc-framework-applescriptkit, pyobjc-framework-applescriptobjc, pyobjc-framework-applicationservices, pyobjc-framework-automator, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-businesschat, pyobjc-framework-calendarstore, pyobjc-framework-cfnetwork, pyobjc-framework-cloudkit, pyobjc-framework-collaboration, pyobjc-framework-colorsync, pyobjc-framework-contacts, pyobjc-framework-contactsui, pyobjc-framework-coreaudio, pyobjc-framework-coreaudiokit, pyobjc-framework-corebluetooth, pyobjc-framework-coredata, pyobjc-framework-corelocation, pyobjc-framework-coremedia, pyobjc-framework-coremediaio, pyobjc-framework-coreml, pyobjc-framework-corespotlight, pyobjc-framework-coretext, pyobjc-framework-corewlan, pyobjc-framework-cryptotokenkit, pyobjc-framework-discrecording, pyobjc-framework-discrecordingui, pyobjc-framework-diskarbitration, pyobjc-framework-dvdplayback, pyobjc-framework-eventkit, pyobjc-framework-exceptionhandling, pyobjc-framework-externalaccessory, pyobjc-framework-findersync, pyobjc-framework-fsevents, pyobjc-framework-gamecenter, pyobjc-framework-gamecontroller, pyobjc-framework-gamekit, pyobjc-framework-gameplaykit, pyobjc-framework-imagecapturecore, pyobjc-framework-imserviceplugin, pyobjc-framework-inputmethodkit, pyobjc-framework-installerplugins, pyobjc-framework-instantmessage, pyobjc-framework-intents, pyobjc-framework-iosurface, pyobjc-framework-ituneslibrary, pyobjc-framework-latentsemanticmapping, pyobjc-framework-localauthentication, pyobjc-framework-mapkit, pyobjc-framework-mediaaccessibility, pyobjc-framework-medialibrary, pyobjc-framework-mediatoolbox, pyobjc-framework-modelio, pyobjc-framework-multipeerconnectivity, pyobjc-framework-naturallanguage, pyobjc-framework-netfs, pyobjc-framework-network, pyobjc-framework-networkextension, pyobjc-framework-notificationcenter, pyobjc-framework-opendirectory, pyobjc-framework-osakit, pyobjc-framework-photos, pyobjc-framework-photosui, pyobjc-framework-preferencepanes, pyobjc-framework-pubsub, pyobjc-framework-qtkit, pyobjc-framework-quartz, pyobjc-framework-safariservices, pyobjc-framework-scenekit, pyobjc-framework-screensaver, pyobjc-framework-scriptingbridge, pyobjc-framework-security, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface, pyobjc-framework-servicemanagement, pyobjc-framework-social, pyobjc-framework-spritekit, pyobjc-framework-storekit, pyobjc-framework-syncservices, pyobjc-framework-systemconfiguration, pyobjc-framework-usernotifications, pyobjc-framework-videosubscriberaccount, pyobjc-framework-videotoolbox, pyobjc-framework-vision, pyobjc-framework-webkit +pyobjc-framework-collaboration==5.1.2 # via pyobjc +pyobjc-framework-colorsync==5.1.2 # via pyobjc +pyobjc-framework-contacts==5.1.2 # via pyobjc, pyobjc-framework-contactsui +pyobjc-framework-contactsui==5.1.2 # via pyobjc +pyobjc-framework-coreaudio==5.1.2 # via pyobjc, pyobjc-framework-coreaudiokit +pyobjc-framework-coreaudiokit==5.1.2 # via pyobjc +pyobjc-framework-corebluetooth==5.1.2 # via pyobjc +pyobjc-framework-coredata==5.1.2 # via pyobjc, pyobjc-framework-cloudkit, pyobjc-framework-syncservices +pyobjc-framework-corelocation==5.1.2 # via pyobjc, pyobjc-framework-cloudkit, pyobjc-framework-mapkit +pyobjc-framework-coremedia==5.1.2 # via pyobjc, pyobjc-framework-videotoolbox +pyobjc-framework-coremediaio==5.1.2 # via pyobjc +pyobjc-framework-coreml==5.1.2 # via pyobjc, pyobjc-framework-vision +pyobjc-framework-coreservices==5.1.2 # via pyobjc, pyobjc-framework-dictionaryservices, pyobjc-framework-launchservices, pyobjc-framework-searchkit +pyobjc-framework-corespotlight==5.1.2 # via pyobjc +pyobjc-framework-coretext==5.1.2 # via pyobjc +pyobjc-framework-corewlan==5.1.2 # via pyobjc +pyobjc-framework-cryptotokenkit==5.1.2 # via pyobjc +pyobjc-framework-dictionaryservices==5.1.2 # via pyobjc +pyobjc-framework-discrecording==5.1.2 # via pyobjc, pyobjc-framework-discrecordingui +pyobjc-framework-discrecordingui==5.1.2 # via pyobjc +pyobjc-framework-diskarbitration==5.1.2 # via pyobjc +pyobjc-framework-dvdplayback==5.1.2 # via pyobjc +pyobjc-framework-eventkit==5.1.2 # via pyobjc +pyobjc-framework-exceptionhandling==5.1.2 # via pyobjc +pyobjc-framework-externalaccessory==5.1.2 # via pyobjc +pyobjc-framework-findersync==5.1.2 # via pyobjc +pyobjc-framework-fsevents==5.1.2 # via pyobjc, pyobjc-framework-coreservices +pyobjc-framework-gamecenter==5.1.2 # via pyobjc +pyobjc-framework-gamecontroller==5.1.2 # via pyobjc +pyobjc-framework-gamekit==5.1.2 # via pyobjc +pyobjc-framework-gameplaykit==5.1.2 # via pyobjc +pyobjc-framework-imagecapturecore==5.1.2 # via pyobjc +pyobjc-framework-imserviceplugin==5.1.2 # via pyobjc +pyobjc-framework-inputmethodkit==5.1.2 # via pyobjc +pyobjc-framework-installerplugins==5.1.2 # via pyobjc +pyobjc-framework-instantmessage==5.1.2 # via pyobjc +pyobjc-framework-intents==5.1.2 # via pyobjc +pyobjc-framework-iosurface==5.1.2 # via pyobjc +pyobjc-framework-ituneslibrary==5.1.2 # via pyobjc +pyobjc-framework-latentsemanticmapping==5.1.2 # via pyobjc +pyobjc-framework-launchservices==5.1.2 # via pyobjc +pyobjc-framework-libdispatch==5.1.2 # via pyobjc +pyobjc-framework-localauthentication==5.1.2 # via pyobjc +pyobjc-framework-mapkit==5.1.2 # via pyobjc +pyobjc-framework-mediaaccessibility==5.1.2 # via pyobjc +pyobjc-framework-medialibrary==5.1.2 # via pyobjc +pyobjc-framework-mediaplayer==5.1.2 # via pyobjc +pyobjc-framework-mediatoolbox==5.1.2 # via pyobjc +pyobjc-framework-modelio==5.1.2 # via pyobjc +pyobjc-framework-multipeerconnectivity==5.1.2 # via pyobjc +pyobjc-framework-naturallanguage==5.1.2 # via pyobjc +pyobjc-framework-netfs==5.1.2 # via pyobjc +pyobjc-framework-network==5.1.2 # via pyobjc +pyobjc-framework-networkextension==5.1.2 # via pyobjc +pyobjc-framework-notificationcenter==5.1.2 # via pyobjc +pyobjc-framework-opendirectory==5.1.2 # via pyobjc +pyobjc-framework-osakit==5.1.2 # via pyobjc +pyobjc-framework-photos==5.1.2 # via pyobjc +pyobjc-framework-photosui==5.1.2 # via pyobjc +pyobjc-framework-preferencepanes==5.1.2 # via pyobjc +pyobjc-framework-pubsub==5.1.2 # via pyobjc +pyobjc-framework-qtkit==5.1.2 # via pyobjc +pyobjc-framework-quartz==5.1.2 # via pyobjc, pyobjc-framework-applicationservices, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-coretext, pyobjc-framework-gamekit, pyobjc-framework-instantmessage, pyobjc-framework-mapkit, pyobjc-framework-medialibrary, pyobjc-framework-modelio, pyobjc-framework-qtkit, pyobjc-framework-scenekit, pyobjc-framework-spritekit, pyobjc-framework-videotoolbox, pyobjc-framework-vision +pyobjc-framework-safariservices==5.1.2 # via pyobjc +pyobjc-framework-scenekit==5.1.2 # via pyobjc +pyobjc-framework-screensaver==5.1.2 # via pyobjc +pyobjc-framework-scriptingbridge==5.1.2 # via pyobjc +pyobjc-framework-searchkit==5.1.2 # via pyobjc +pyobjc-framework-security==5.1.2 # via pyobjc, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface +pyobjc-framework-securityfoundation==5.1.2 # via pyobjc +pyobjc-framework-securityinterface==5.1.2 # via pyobjc +pyobjc-framework-servicemanagement==5.1.2 # via pyobjc +pyobjc-framework-social==5.1.2 # via pyobjc +pyobjc-framework-spritekit==5.1.2 # via pyobjc, pyobjc-framework-gameplaykit +pyobjc-framework-storekit==5.1.2 # via pyobjc +pyobjc-framework-syncservices==5.1.2 # via pyobjc +pyobjc-framework-systemconfiguration==5.1.2 # via pyobjc +pyobjc-framework-usernotifications==5.1.2 # via pyobjc +pyobjc-framework-videosubscriberaccount==5.1.2 # via pyobjc +pyobjc-framework-videotoolbox==5.1.2 # via pyobjc +pyobjc-framework-vision==5.1.2 # via pyobjc +pyobjc-framework-webkit==5.1.2 # via pyobjc pyobjc==5.1.2 diff --git a/requirements/static/py3.6/docs.txt b/requirements/static/py3.6/docs.txt index 6484fdd0782..3787a7d6d10 100644 --- a/requirements/static/py3.6/docs.txt +++ b/requirements/static/py3.6/docs.txt @@ -5,26 +5,26 @@ # pip-compile -o requirements/static/py3.6/docs.txt -v requirements/static/docs.in # alabaster==0.7.12 # via sphinx -babel==2.7.0 # via sphinx -certifi==2019.3.9 # via requests +babel==2.8.0 # via sphinx +certifi==2020.4.5.1 # via requests chardet==3.0.4 # via requests -docutils==0.14 # via sphinx -idna==2.8 # via requests -imagesize==1.1.0 # via sphinx -jinja2==2.10.1 # via sphinx +docutils==0.16 # via sphinx +idna==2.9 # via requests +imagesize==1.2.0 # via sphinx +jinja2==2.11.2 # via sphinx markupsafe==1.1.1 # via jinja2 -packaging==19.0 # via sphinx -pygments==2.4.2 # via sphinx -pyparsing==2.4.0 # via packaging -pytz==2019.1 # via babel -requests==2.22.0 # via sphinx -six==1.12.0 # via packaging -snowballstemmer==1.2.1 # via sphinx -sphinx==2.0.1 -sphinxcontrib-applehelp==1.0.1 # via sphinx -sphinxcontrib-devhelp==1.0.1 # via sphinx -sphinxcontrib-htmlhelp==1.0.2 # via sphinx +packaging==20.3 # via sphinx +pygments==2.6.1 # via sphinx +pyparsing==2.4.7 # via packaging +pytz==2019.3 # via babel +requests==2.23.0 # via sphinx +six==1.14.0 # via packaging +snowballstemmer==2.0.0 # via sphinx +sphinx==3.0.1 +sphinxcontrib-applehelp==1.0.2 # via sphinx +sphinxcontrib-devhelp==1.0.2 # via sphinx +sphinxcontrib-htmlhelp==1.0.3 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx -sphinxcontrib-qthelp==1.0.2 # via sphinx -sphinxcontrib-serializinghtml==1.1.3 # via sphinx -urllib3==1.25.3 # via requests +sphinxcontrib-qthelp==1.0.3 # via sphinx +sphinxcontrib-serializinghtml==1.1.4 # via sphinx +urllib3==1.25.9 # via requests diff --git a/requirements/static/py3.6/lint.txt b/requirements/static/py3.6/lint.txt index b16d5e38cf9..0d87b1621b7 100644 --- a/requirements/static/py3.6/lint.txt +++ b/requirements/static/py3.6/lint.txt @@ -13,4 +13,5 @@ pycodestyle==2.5.0 # via saltpylint pylint==2.4.4 saltpylint==2019.11.14 six==1.12.0 # via astroid +typed-ast==1.4.1 # via astroid wrapt==1.11.1 # via astroid diff --git a/requirements/static/py3.6/linux-crypto.txt b/requirements/static/py3.6/linux-crypto.txt index 3b92d88ffb2..efb7b5dc6ea 100644 --- a/requirements/static/py3.6/linux-crypto.txt +++ b/requirements/static/py3.6/linux-crypto.txt @@ -6,4 +6,3 @@ # m2crypto==0.35.2 pycryptodomex==3.9.3 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.6/linux.txt b/requirements/static/py3.6/linux.txt index 247bdc6e2be..fe71cde741d 100644 --- a/requirements/static/py3.6/linux.txt +++ b/requirements/static/py3.6/linux.txt @@ -11,7 +11,6 @@ atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 @@ -52,7 +51,7 @@ jxmlease==1.0.1 kazoo==2.6.1 keyring==5.7.1 kubernetes==3.0.0 -libnacl==1.6.0 +libnacl==1.7.1 lxml==4.3.3 # via junos-eznc, ncclient mako==1.1.0 markupsafe==1.1.1 @@ -68,7 +67,7 @@ paramiko==2.4.2 pathtools==0.1.2 # via watchdog pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy -psutil==5.6.1 +psutil==5.6.6 py==1.8.0 # via pytest pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth diff --git a/requirements/static/py3.6/windows-crypto.txt b/requirements/static/py3.6/windows-crypto.txt index 2e4eaf54bcc..6f688649568 100644 --- a/requirements/static/py3.6/windows-crypto.txt +++ b/requirements/static/py3.6/windows-crypto.txt @@ -6,4 +6,3 @@ # m2crypto==0.35.2 pycryptodomex==3.9.0 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.6/windows.txt b/requirements/static/py3.6/windows.txt index 73638629c2e..00f7ad27a9b 100644 --- a/requirements/static/py3.6/windows.txt +++ b/requirements/static/py3.6/windows.txt @@ -30,11 +30,9 @@ docker-pycreds==0.4.0 # via docker docker==2.7.0 docutils==0.14 # via botocore ecdsa==0.13.3 # via python-jose -enum34==1.1.6 future==0.17.1 # via python-jose genshi==0.7.3 -gitdb2==2.0.5 # via gitpython -gitdb==0.6.4 +gitdb2==2.0.5 gitpython==2.1.10 google-auth==1.6.3 # via kubernetes idna==2.8 @@ -49,7 +47,7 @@ jsonpickle==1.1 # via aws-xray-sdk jsonschema==2.6.0 keyring==5.7.1 kubernetes==3.0.0 -libnacl==1.6.1 +libnacl==1.7.1 lxml==4.3.0 mako==1.0.7 markupsafe==1.1.1 @@ -63,7 +61,7 @@ patch==1.16 pathtools==0.1.2 # via watchdog pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy -psutil==5.6.1 +psutil==5.6.6 py==1.8.0 # via pytest pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth @@ -71,7 +69,7 @@ pyasn1==0.4.5 pycparser==2.19 pycryptodome==3.8.1 # via python-jose pycryptodomex==3.8.1 ; sys_platform == "win32" -pycurl==7.43.0.2 +pycurl==7.43.0.5 pygit2==0.28.2 pymssql==2.1.4 pymysql==0.9.3 @@ -101,8 +99,7 @@ salttesting==2017.6.1 sed==0.3.1 setproctitle==1.1.10 six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, packaging, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -smmap==0.9.0 +smmap2==2.0.5 strict-rfc3339==0.7 tempora==1.14.1 # via portend timelib==0.2.4 diff --git a/requirements/static/py3.7/changelog.txt b/requirements/static/py3.7/changelog.txt new file mode 100644 index 00000000000..b6a06719840 --- /dev/null +++ b/requirements/static/py3.7/changelog.txt @@ -0,0 +1,12 @@ +# +# This file is autogenerated by pip-compile +# To update, run: +# +# pip-compile -o requirements/static/py3.7/changelog.txt -v requirements/static/changelog.in +# +click==7.1.1 # via towncrier +incremental==17.5.0 # via towncrier +jinja2==2.11.2 # via towncrier +markupsafe==1.1.1 # via jinja2 +toml==0.10.0 # via towncrier +towncrier==19.2.0 diff --git a/requirements/static/py3.7/darwin-crypto.txt b/requirements/static/py3.7/darwin-crypto.txt index 370308dc087..0a4ffcf4c8d 100644 --- a/requirements/static/py3.7/darwin-crypto.txt +++ b/requirements/static/py3.7/darwin-crypto.txt @@ -6,4 +6,3 @@ # m2crypto==0.35.2 pycryptodomex==3.9.0 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.7/darwin.txt b/requirements/static/py3.7/darwin.txt index 14b7c3e643c..bcb24aa1d74 100644 --- a/requirements/static/py3.7/darwin.txt +++ b/requirements/static/py3.7/darwin.txt @@ -2,7 +2,7 @@ # This file is autogenerated by pip-compile # To update, run: # -# pip-compile -o requirements/static/py3.7/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in +# pip-compile -o requirements/static/py3.7/darwin.txt -v pkg/osx/req.txt pkg/osx/req_ext.txt pkg/osx/req_pyobjc.txt requirements/base.txt requirements/zeromq.txt requirements/pytest.txt requirements/static/darwin.in # apache-libcloud==2.4.0 appdirs==1.4.3 # via virtualenv @@ -41,8 +41,7 @@ filelock==3.0.12 # via virtualenv future==0.17.1 # via python-jose genshi==0.7.3 gitdb2==2.0.5 # via gitpython -gitdb==0.6.4 -gitpython==2.1.11 +gitpython==2.1.15 google-auth==1.6.3 # via kubernetes idna==2.8 importlib-metadata==0.23 # via pluggy, pytest, virtualenv @@ -74,7 +73,7 @@ paramiko==2.4.2 # via junos-eznc, ncclient, scp pathtools==0.1.2 # via watchdog pluggy==0.13.1 # via pytest portend==2.4 # via cherrypy -psutil==5.6.1 +psutil==5.6.6 py==1.8.0 # via pytest pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth @@ -108,7 +107,6 @@ scp==0.13.2 # via junos-eznc setproctitle==1.1.10 six==1.12.0 # via bcrypt, cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, junos-eznc, kubernetes, mock, more-itertools, moto, ncclient, packaging, pynacl, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, vcert, virtualenv, websocket-client smmap2==2.0.5 # via gitdb2 -smmap==0.9.0 strict-rfc3339==0.7 tempora==1.14.1 # via portend timelib==0.2.4 @@ -125,5 +123,102 @@ xmltodict==0.12.0 # via moto yamlordereddictloader==0.4.0 zc.lockfile==1.4 # via cherrypy zipp==0.6.0 # via importlib-metadata -# Passthrough dependencies from pkg/osx/req.txt +# Passthrough dependencies from pkg/osx/req_pyobjc.txt +pyobjc-core==5.1.2 # via pyobjc, pyobjc-framework-accounts, pyobjc-framework-addressbook, pyobjc-framework-adsupport, pyobjc-framework-applescriptkit, pyobjc-framework-applescriptobjc, pyobjc-framework-applicationservices, pyobjc-framework-automator, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-businesschat, pyobjc-framework-calendarstore, pyobjc-framework-cfnetwork, pyobjc-framework-cloudkit, pyobjc-framework-cocoa, pyobjc-framework-collaboration, pyobjc-framework-colorsync, pyobjc-framework-contacts, pyobjc-framework-contactsui, pyobjc-framework-coreaudio, pyobjc-framework-coreaudiokit, pyobjc-framework-corebluetooth, pyobjc-framework-coredata, pyobjc-framework-corelocation, pyobjc-framework-coremedia, pyobjc-framework-coremediaio, pyobjc-framework-coreml, pyobjc-framework-coreservices, pyobjc-framework-corespotlight, pyobjc-framework-coretext, pyobjc-framework-corewlan, pyobjc-framework-cryptotokenkit, pyobjc-framework-dictionaryservices, pyobjc-framework-discrecording, pyobjc-framework-discrecordingui, pyobjc-framework-diskarbitration, pyobjc-framework-dvdplayback, pyobjc-framework-eventkit, pyobjc-framework-exceptionhandling, pyobjc-framework-externalaccessory, pyobjc-framework-findersync, pyobjc-framework-fsevents, pyobjc-framework-gamecenter, pyobjc-framework-gamecontroller, pyobjc-framework-gamekit, pyobjc-framework-gameplaykit, pyobjc-framework-imagecapturecore, pyobjc-framework-imserviceplugin, pyobjc-framework-inputmethodkit, pyobjc-framework-installerplugins, pyobjc-framework-instantmessage, pyobjc-framework-intents, pyobjc-framework-iosurface, pyobjc-framework-ituneslibrary, pyobjc-framework-latentsemanticmapping, pyobjc-framework-launchservices, pyobjc-framework-libdispatch, pyobjc-framework-localauthentication, pyobjc-framework-mapkit, pyobjc-framework-mediaaccessibility, pyobjc-framework-medialibrary, pyobjc-framework-mediaplayer, pyobjc-framework-mediatoolbox, pyobjc-framework-modelio, pyobjc-framework-multipeerconnectivity, pyobjc-framework-naturallanguage, pyobjc-framework-netfs, pyobjc-framework-network, pyobjc-framework-networkextension, pyobjc-framework-notificationcenter, pyobjc-framework-opendirectory, pyobjc-framework-osakit, pyobjc-framework-photos, pyobjc-framework-photosui, pyobjc-framework-preferencepanes, pyobjc-framework-pubsub, pyobjc-framework-qtkit, pyobjc-framework-quartz, pyobjc-framework-safariservices, pyobjc-framework-scenekit, pyobjc-framework-screensaver, pyobjc-framework-scriptingbridge, pyobjc-framework-searchkit, pyobjc-framework-security, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface, pyobjc-framework-servicemanagement, pyobjc-framework-social, pyobjc-framework-spritekit, pyobjc-framework-storekit, pyobjc-framework-syncservices, pyobjc-framework-systemconfiguration, pyobjc-framework-usernotifications, pyobjc-framework-videosubscriberaccount, pyobjc-framework-videotoolbox, pyobjc-framework-vision, pyobjc-framework-webkit +pyobjc-framework-accounts==5.1.2 # via pyobjc, pyobjc-framework-cloudkit +pyobjc-framework-addressbook==5.1.2 # via pyobjc +pyobjc-framework-adsupport==5.1.2 # via pyobjc +pyobjc-framework-applescriptkit==5.1.2 # via pyobjc +pyobjc-framework-applescriptobjc==5.1.2 # via pyobjc +pyobjc-framework-applicationservices==5.1.2 # via pyobjc +pyobjc-framework-automator==5.1.2 # via pyobjc +pyobjc-framework-avfoundation==5.1.2 # via pyobjc, pyobjc-framework-mediaplayer +pyobjc-framework-avkit==5.1.2 # via pyobjc +pyobjc-framework-businesschat==5.1.2 # via pyobjc +pyobjc-framework-calendarstore==5.1.2 # via pyobjc +pyobjc-framework-cfnetwork==5.1.2 # via pyobjc +pyobjc-framework-cloudkit==5.1.2 # via pyobjc +pyobjc-framework-cocoa==5.1.2 # via pyobjc, pyobjc-framework-accounts, pyobjc-framework-addressbook, pyobjc-framework-adsupport, pyobjc-framework-applescriptkit, pyobjc-framework-applescriptobjc, pyobjc-framework-applicationservices, pyobjc-framework-automator, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-businesschat, pyobjc-framework-calendarstore, pyobjc-framework-cfnetwork, pyobjc-framework-cloudkit, pyobjc-framework-collaboration, pyobjc-framework-colorsync, pyobjc-framework-contacts, pyobjc-framework-contactsui, pyobjc-framework-coreaudio, pyobjc-framework-coreaudiokit, pyobjc-framework-corebluetooth, pyobjc-framework-coredata, pyobjc-framework-corelocation, pyobjc-framework-coremedia, pyobjc-framework-coremediaio, pyobjc-framework-coreml, pyobjc-framework-corespotlight, pyobjc-framework-coretext, pyobjc-framework-corewlan, pyobjc-framework-cryptotokenkit, pyobjc-framework-discrecording, pyobjc-framework-discrecordingui, pyobjc-framework-diskarbitration, pyobjc-framework-dvdplayback, pyobjc-framework-eventkit, pyobjc-framework-exceptionhandling, pyobjc-framework-externalaccessory, pyobjc-framework-findersync, pyobjc-framework-fsevents, pyobjc-framework-gamecenter, pyobjc-framework-gamecontroller, pyobjc-framework-gamekit, pyobjc-framework-gameplaykit, pyobjc-framework-imagecapturecore, pyobjc-framework-imserviceplugin, pyobjc-framework-inputmethodkit, pyobjc-framework-installerplugins, pyobjc-framework-instantmessage, pyobjc-framework-intents, pyobjc-framework-iosurface, pyobjc-framework-ituneslibrary, pyobjc-framework-latentsemanticmapping, pyobjc-framework-localauthentication, pyobjc-framework-mapkit, pyobjc-framework-mediaaccessibility, pyobjc-framework-medialibrary, pyobjc-framework-mediatoolbox, pyobjc-framework-modelio, pyobjc-framework-multipeerconnectivity, pyobjc-framework-naturallanguage, pyobjc-framework-netfs, pyobjc-framework-network, pyobjc-framework-networkextension, pyobjc-framework-notificationcenter, pyobjc-framework-opendirectory, pyobjc-framework-osakit, pyobjc-framework-photos, pyobjc-framework-photosui, pyobjc-framework-preferencepanes, pyobjc-framework-pubsub, pyobjc-framework-qtkit, pyobjc-framework-quartz, pyobjc-framework-safariservices, pyobjc-framework-scenekit, pyobjc-framework-screensaver, pyobjc-framework-scriptingbridge, pyobjc-framework-security, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface, pyobjc-framework-servicemanagement, pyobjc-framework-social, pyobjc-framework-spritekit, pyobjc-framework-storekit, pyobjc-framework-syncservices, pyobjc-framework-systemconfiguration, pyobjc-framework-usernotifications, pyobjc-framework-videosubscriberaccount, pyobjc-framework-videotoolbox, pyobjc-framework-vision, pyobjc-framework-webkit +pyobjc-framework-collaboration==5.1.2 # via pyobjc +pyobjc-framework-colorsync==5.1.2 # via pyobjc +pyobjc-framework-contacts==5.1.2 # via pyobjc, pyobjc-framework-contactsui +pyobjc-framework-contactsui==5.1.2 # via pyobjc +pyobjc-framework-coreaudio==5.1.2 # via pyobjc, pyobjc-framework-coreaudiokit +pyobjc-framework-coreaudiokit==5.1.2 # via pyobjc +pyobjc-framework-corebluetooth==5.1.2 # via pyobjc +pyobjc-framework-coredata==5.1.2 # via pyobjc, pyobjc-framework-cloudkit, pyobjc-framework-syncservices +pyobjc-framework-corelocation==5.1.2 # via pyobjc, pyobjc-framework-cloudkit, pyobjc-framework-mapkit +pyobjc-framework-coremedia==5.1.2 # via pyobjc, pyobjc-framework-videotoolbox +pyobjc-framework-coremediaio==5.1.2 # via pyobjc +pyobjc-framework-coreml==5.1.2 # via pyobjc, pyobjc-framework-vision +pyobjc-framework-coreservices==5.1.2 # via pyobjc, pyobjc-framework-dictionaryservices, pyobjc-framework-launchservices, pyobjc-framework-searchkit +pyobjc-framework-corespotlight==5.1.2 # via pyobjc +pyobjc-framework-coretext==5.1.2 # via pyobjc +pyobjc-framework-corewlan==5.1.2 # via pyobjc +pyobjc-framework-cryptotokenkit==5.1.2 # via pyobjc +pyobjc-framework-dictionaryservices==5.1.2 # via pyobjc +pyobjc-framework-discrecording==5.1.2 # via pyobjc, pyobjc-framework-discrecordingui +pyobjc-framework-discrecordingui==5.1.2 # via pyobjc +pyobjc-framework-diskarbitration==5.1.2 # via pyobjc +pyobjc-framework-dvdplayback==5.1.2 # via pyobjc +pyobjc-framework-eventkit==5.1.2 # via pyobjc +pyobjc-framework-exceptionhandling==5.1.2 # via pyobjc +pyobjc-framework-externalaccessory==5.1.2 # via pyobjc +pyobjc-framework-findersync==5.1.2 # via pyobjc +pyobjc-framework-fsevents==5.1.2 # via pyobjc, pyobjc-framework-coreservices +pyobjc-framework-gamecenter==5.1.2 # via pyobjc +pyobjc-framework-gamecontroller==5.1.2 # via pyobjc +pyobjc-framework-gamekit==5.1.2 # via pyobjc +pyobjc-framework-gameplaykit==5.1.2 # via pyobjc +pyobjc-framework-imagecapturecore==5.1.2 # via pyobjc +pyobjc-framework-imserviceplugin==5.1.2 # via pyobjc +pyobjc-framework-inputmethodkit==5.1.2 # via pyobjc +pyobjc-framework-installerplugins==5.1.2 # via pyobjc +pyobjc-framework-instantmessage==5.1.2 # via pyobjc +pyobjc-framework-intents==5.1.2 # via pyobjc +pyobjc-framework-iosurface==5.1.2 # via pyobjc +pyobjc-framework-ituneslibrary==5.1.2 # via pyobjc +pyobjc-framework-latentsemanticmapping==5.1.2 # via pyobjc +pyobjc-framework-launchservices==5.1.2 # via pyobjc +pyobjc-framework-libdispatch==5.1.2 # via pyobjc +pyobjc-framework-localauthentication==5.1.2 # via pyobjc +pyobjc-framework-mapkit==5.1.2 # via pyobjc +pyobjc-framework-mediaaccessibility==5.1.2 # via pyobjc +pyobjc-framework-medialibrary==5.1.2 # via pyobjc +pyobjc-framework-mediaplayer==5.1.2 # via pyobjc +pyobjc-framework-mediatoolbox==5.1.2 # via pyobjc +pyobjc-framework-modelio==5.1.2 # via pyobjc +pyobjc-framework-multipeerconnectivity==5.1.2 # via pyobjc +pyobjc-framework-naturallanguage==5.1.2 # via pyobjc +pyobjc-framework-netfs==5.1.2 # via pyobjc +pyobjc-framework-network==5.1.2 # via pyobjc +pyobjc-framework-networkextension==5.1.2 # via pyobjc +pyobjc-framework-notificationcenter==5.1.2 # via pyobjc +pyobjc-framework-opendirectory==5.1.2 # via pyobjc +pyobjc-framework-osakit==5.1.2 # via pyobjc +pyobjc-framework-photos==5.1.2 # via pyobjc +pyobjc-framework-photosui==5.1.2 # via pyobjc +pyobjc-framework-preferencepanes==5.1.2 # via pyobjc +pyobjc-framework-pubsub==5.1.2 # via pyobjc +pyobjc-framework-qtkit==5.1.2 # via pyobjc +pyobjc-framework-quartz==5.1.2 # via pyobjc, pyobjc-framework-applicationservices, pyobjc-framework-avfoundation, pyobjc-framework-avkit, pyobjc-framework-coretext, pyobjc-framework-gamekit, pyobjc-framework-instantmessage, pyobjc-framework-mapkit, pyobjc-framework-medialibrary, pyobjc-framework-modelio, pyobjc-framework-qtkit, pyobjc-framework-scenekit, pyobjc-framework-spritekit, pyobjc-framework-videotoolbox, pyobjc-framework-vision +pyobjc-framework-safariservices==5.1.2 # via pyobjc +pyobjc-framework-scenekit==5.1.2 # via pyobjc +pyobjc-framework-screensaver==5.1.2 # via pyobjc +pyobjc-framework-scriptingbridge==5.1.2 # via pyobjc +pyobjc-framework-searchkit==5.1.2 # via pyobjc +pyobjc-framework-security==5.1.2 # via pyobjc, pyobjc-framework-securityfoundation, pyobjc-framework-securityinterface +pyobjc-framework-securityfoundation==5.1.2 # via pyobjc +pyobjc-framework-securityinterface==5.1.2 # via pyobjc +pyobjc-framework-servicemanagement==5.1.2 # via pyobjc +pyobjc-framework-social==5.1.2 # via pyobjc +pyobjc-framework-spritekit==5.1.2 # via pyobjc, pyobjc-framework-gameplaykit +pyobjc-framework-storekit==5.1.2 # via pyobjc +pyobjc-framework-syncservices==5.1.2 # via pyobjc +pyobjc-framework-systemconfiguration==5.1.2 # via pyobjc +pyobjc-framework-usernotifications==5.1.2 # via pyobjc +pyobjc-framework-videosubscriberaccount==5.1.2 # via pyobjc +pyobjc-framework-videotoolbox==5.1.2 # via pyobjc +pyobjc-framework-vision==5.1.2 # via pyobjc +pyobjc-framework-webkit==5.1.2 # via pyobjc pyobjc==5.1.2 diff --git a/requirements/static/py3.7/docs.txt b/requirements/static/py3.7/docs.txt index 917bdc06faa..695c6adc856 100644 --- a/requirements/static/py3.7/docs.txt +++ b/requirements/static/py3.7/docs.txt @@ -5,26 +5,26 @@ # pip-compile -o requirements/static/py3.7/docs.txt -v requirements/static/docs.in # alabaster==0.7.12 # via sphinx -babel==2.7.0 # via sphinx -certifi==2019.3.9 # via requests +babel==2.8.0 # via sphinx +certifi==2020.4.5.1 # via requests chardet==3.0.4 # via requests -docutils==0.14 # via sphinx -idna==2.8 # via requests -imagesize==1.1.0 # via sphinx -jinja2==2.10.1 # via sphinx +docutils==0.16 # via sphinx +idna==2.9 # via requests +imagesize==1.2.0 # via sphinx +jinja2==2.11.2 # via sphinx markupsafe==1.1.1 # via jinja2 -packaging==19.0 # via sphinx -pygments==2.4.2 # via sphinx -pyparsing==2.4.0 # via packaging -pytz==2019.1 # via babel -requests==2.22.0 # via sphinx -six==1.12.0 # via packaging -snowballstemmer==1.2.1 # via sphinx -sphinx==2.0.1 -sphinxcontrib-applehelp==1.0.1 # via sphinx -sphinxcontrib-devhelp==1.0.1 # via sphinx -sphinxcontrib-htmlhelp==1.0.2 # via sphinx +packaging==20.3 # via sphinx +pygments==2.6.1 # via sphinx +pyparsing==2.4.7 # via packaging +pytz==2019.3 # via babel +requests==2.23.0 # via sphinx +six==1.14.0 # via packaging +snowballstemmer==2.0.0 # via sphinx +sphinx==3.0.1 +sphinxcontrib-applehelp==1.0.2 # via sphinx +sphinxcontrib-devhelp==1.0.2 # via sphinx +sphinxcontrib-htmlhelp==1.0.3 # via sphinx sphinxcontrib-jsmath==1.0.1 # via sphinx -sphinxcontrib-qthelp==1.0.2 # via sphinx -sphinxcontrib-serializinghtml==1.1.3 # via sphinx -urllib3==1.25.3 # via requests +sphinxcontrib-qthelp==1.0.3 # via sphinx +sphinxcontrib-serializinghtml==1.1.4 # via sphinx +urllib3==1.25.9 # via requests diff --git a/requirements/static/py3.7/lint.txt b/requirements/static/py3.7/lint.txt index b0920c1d6e6..91653a9b8b5 100644 --- a/requirements/static/py3.7/lint.txt +++ b/requirements/static/py3.7/lint.txt @@ -13,4 +13,5 @@ pycodestyle==2.5.0 # via saltpylint pylint==2.4.4 saltpylint==2019.11.14 six==1.12.0 # via astroid +typed-ast==1.4.1 # via astroid wrapt==1.11.1 # via astroid diff --git a/requirements/static/py3.7/linux-crypto.txt b/requirements/static/py3.7/linux-crypto.txt index a376a05e786..1139152146b 100644 --- a/requirements/static/py3.7/linux-crypto.txt +++ b/requirements/static/py3.7/linux-crypto.txt @@ -6,4 +6,3 @@ # m2crypto==0.35.2 pycryptodomex==3.9.3 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.7/linux.txt b/requirements/static/py3.7/linux.txt index 17e9bc785ae..4705805999f 100644 --- a/requirements/static/py3.7/linux.txt +++ b/requirements/static/py3.7/linux.txt @@ -11,7 +11,6 @@ atomicwrites==1.3.0 # via pytest attrs==19.1.0 # via pytest aws-xray-sdk==0.95 # via moto backports.functools-lru-cache==1.5 # via cheroot -backports.ssl-match-hostname==3.7.0.1 # via websocket-client bcrypt==3.1.6 # via paramiko boto3==1.9.132 boto==2.49.0 @@ -52,7 +51,7 @@ jxmlease==1.0.1 kazoo==2.6.1 keyring==5.7.1 kubernetes==3.0.0 -libnacl==1.6.0 +libnacl==1.7.1 lxml==4.3.3 # via junos-eznc, ncclient mako==1.1.0 markupsafe==1.1.1 @@ -68,7 +67,7 @@ paramiko==2.4.2 pathtools==0.1.2 # via watchdog pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy -psutil==5.6.1 +psutil==5.6.6 py==1.8.0 # via pytest pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth diff --git a/requirements/static/py3.7/windows-crypto.txt b/requirements/static/py3.7/windows-crypto.txt index ee941ca2d08..82aeec9d3bf 100644 --- a/requirements/static/py3.7/windows-crypto.txt +++ b/requirements/static/py3.7/windows-crypto.txt @@ -6,4 +6,3 @@ # m2crypto==0.35.2 pycryptodomex==3.9.0 -typing==3.7.4.1 # via m2crypto diff --git a/requirements/static/py3.7/windows.txt b/requirements/static/py3.7/windows.txt index 32ea9cedbb0..f2ccd825fa9 100644 --- a/requirements/static/py3.7/windows.txt +++ b/requirements/static/py3.7/windows.txt @@ -30,11 +30,9 @@ docker-pycreds==0.4.0 # via docker docker==2.7.0 docutils==0.14 # via botocore ecdsa==0.13.3 # via python-jose -enum34==1.1.6 future==0.17.1 # via python-jose genshi==0.7.3 -gitdb2==2.0.5 # via gitpython -gitdb==0.6.4 +gitdb2==2.0.5 gitpython==2.1.10 google-auth==1.6.3 # via kubernetes idna==2.8 @@ -49,7 +47,7 @@ jsonpickle==1.1 # via aws-xray-sdk jsonschema==2.6.0 keyring==5.7.1 kubernetes==3.0.0 -libnacl==1.6.1 +libnacl==1.7.1 lxml==4.3.0 mako==1.0.7 markupsafe==1.1.1 @@ -63,7 +61,7 @@ patch==1.16 pathtools==0.1.2 # via watchdog pluggy==0.13.0 # via pytest portend==2.4 # via cherrypy -psutil==5.6.1 +psutil==5.6.6 py==1.8.0 # via pytest pyaml==19.4.1 # via moto pyasn1-modules==0.2.4 # via google-auth @@ -71,7 +69,7 @@ pyasn1==0.4.5 pycparser==2.19 pycryptodome==3.8.1 # via python-jose pycryptodomex==3.8.1 ; sys_platform == "win32" -pycurl==7.43.0.2 +pycurl==7.43.0.5 pygit2==0.28.2 pymssql==2.1.4 pymysql==0.9.3 @@ -101,8 +99,7 @@ salttesting==2017.6.1 sed==0.3.1 setproctitle==1.1.10 six==1.12.0 # via cheroot, cherrypy, cryptography, docker, docker-pycreds, google-auth, kubernetes, mock, more-itertools, moto, packaging, pygit2, pyopenssl, pytest, python-dateutil, python-jose, pyvmomi, responses, salttesting, tempora, websocket-client -smmap2==2.0.5 # via gitdb2 -smmap==0.9.0 +smmap2==2.0.5 strict-rfc3339==0.7 tempora==1.14.1 # via portend timelib==0.2.4 diff --git a/requirements/static/windows.in b/requirements/static/windows.in index 4f45539cf41..4df0b152a82 100644 --- a/requirements/static/windows.in +++ b/requirements/static/windows.in @@ -11,7 +11,6 @@ jmespath jsonschema keyring==5.7.1 kubernetes<4.0 -libnacl mock>=3.0.5; python_version < '3.6' more-itertools==5.0.0 moto<=1.3.7 @@ -19,7 +18,6 @@ patch pygit2 python-etcd>0.4.2 msgpack-python >= 0.4.2, != 0.5.5 -psutil pyopenssl python-gnupg pyvmomi diff --git a/salt/__init__.py b/salt/__init__.py index 117523b1d26..c620fb55ca0 100644 --- a/salt/__init__.py +++ b/salt/__init__.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" Salt package -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import warnings -import sys + import importlib +import sys +import warnings class TornadoImporter(object): - def find_module(self, module_name, package_path=None): - if module_name.startswith('tornado'): + if module_name.startswith("tornado"): return self return None def load_module(self, name): - mod = importlib.import_module('salt.ext.{}'.format(name)) + mod = importlib.import_module("salt.ext.{}".format(name)) sys.modules[name] = mod return mod @@ -29,35 +29,36 @@ sys.meta_path = [TornadoImporter()] + sys.meta_path # All salt related deprecation warnings should be shown once each! warnings.filterwarnings( - 'once', # Show once - '', # No deprecation message match + "once", # Show once + "", # No deprecation message match DeprecationWarning, # This filter is for DeprecationWarnings - r'^(salt|salt\.(.*))$' # Match module(s) 'salt' and 'salt.' + r"^(salt|salt\.(.*))$", # Match module(s) 'salt' and 'salt.' ) # While we are supporting Python2.6, hide nested with-statements warnings warnings.filterwarnings( - 'ignore', - 'With-statements now directly support multiple context managers', - DeprecationWarning + "ignore", + "With-statements now directly support multiple context managers", + DeprecationWarning, ) # Filter the backports package UserWarning about being re-imported warnings.filterwarnings( - 'ignore', - '^Module backports was already imported from (.*), but (.*) is being added to sys.path$', - UserWarning + "ignore", + "^Module backports was already imported from (.*), but (.*) is being added to sys.path$", + UserWarning, ) def __define_global_system_encoding_variable__(): import sys + # This is the most trustworthy source of the system encoding, though, if # salt is being imported after being daemonized, this information is lost # and reset to None encoding = None - if not sys.platform.startswith('win') and sys.stdin is not None: + if not sys.platform.startswith("win") and sys.stdin is not None: # On linux we can rely on sys.stdin for the encoding since it # most commonly matches the filesystem encoding. This however # does not apply to windows @@ -68,6 +69,7 @@ def __define_global_system_encoding_variable__(): # encoding. MS Windows has problems with this and reports the wrong # encoding import locale + try: encoding = locale.getdefaultlocale()[-1] except ValueError: @@ -83,16 +85,16 @@ def __define_global_system_encoding_variable__(): # the way back to ascii encoding = sys.getdefaultencoding() if not encoding: - if sys.platform.startswith('darwin'): + if sys.platform.startswith("darwin"): # Mac OS X uses UTF-8 - encoding = 'utf-8' - elif sys.platform.startswith('win'): + encoding = "utf-8" + elif sys.platform.startswith("win"): # Windows uses a configurable encoding; on Windows, Python uses the name “mbcs” # to refer to whatever the currently configured encoding is. - encoding = 'mbcs' + encoding = "mbcs" else: # On linux default to ascii as a last resort - encoding = 'ascii' + encoding = "ascii" # We can't use six.moves.builtins because these builtins get deleted sooner # than expected. See: @@ -103,7 +105,7 @@ def __define_global_system_encoding_variable__(): import builtins # pylint: disable=import-error # Define the detected encoding as a built-in variable for ease of use - setattr(builtins, '__salt_system_encoding__', encoding) + setattr(builtins, "__salt_system_encoding__", encoding) # This is now garbage collectable del sys diff --git a/salt/_compat.py b/salt/_compat.py index e999605d2c2..491233b59e7 100644 --- a/salt/_compat.py +++ b/salt/_compat.py @@ -1,19 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" Salt compatibility code -''' +""" # pylint: disable=import-error,unused-import,invalid-name,W0231,W0233 # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import sys -import logging +from __future__ import absolute_import, print_function, unicode_literals + import binascii +import logging +import sys # Import 3rd-party libs from salt.exceptions import SaltException -from salt.ext.six import binary_type, string_types, text_type, integer_types -from salt.ext.six.moves import cStringIO, StringIO +from salt.ext.six import binary_type, integer_types, string_types, text_type +from salt.ext.six.moves import StringIO, cStringIO log = logging.getLogger(__name__) @@ -42,59 +43,61 @@ PY3 = sys.version_info.major == 3 if PY3: import builtins + exceptions = builtins else: import exceptions if ElementTree is not None: - if not hasattr(ElementTree, 'ParseError'): + if not hasattr(ElementTree, "ParseError"): + class ParseError(Exception): - ''' + """ older versions of ElementTree do not have ParseError - ''' + """ ElementTree.ParseError = ParseError -def text_(s, encoding='latin-1', errors='strict'): - ''' +def text_(s, encoding="latin-1", errors="strict"): + """ If ``s`` is an instance of ``binary_type``, return ``s.decode(encoding, errors)``, otherwise return ``s`` - ''' + """ return s.decode(encoding, errors) if isinstance(s, binary_type) else s -def bytes_(s, encoding='latin-1', errors='strict'): - ''' +def bytes_(s, encoding="latin-1", errors="strict"): + """ If ``s`` is an instance of ``text_type``, return ``s.encode(encoding, errors)``, otherwise return ``s`` - ''' + """ return s.encode(encoding, errors) if isinstance(s, text_type) else s def ascii_native_(s): - ''' + """ Python 3: If ``s`` is an instance of ``text_type``, return ``s.encode('ascii')``, otherwise return ``str(s, 'ascii', 'strict')`` Python 2: If ``s`` is an instance of ``text_type``, return ``s.encode('ascii')``, otherwise return ``str(s)`` - ''' + """ if isinstance(s, text_type): - s = s.encode('ascii') + s = s.encode("ascii") - return str(s, 'ascii', 'strict') if PY3 else s + return str(s, "ascii", "strict") if PY3 else s -def native_(s, encoding='latin-1', errors='strict'): - ''' +def native_(s, encoding="latin-1", errors="strict"): + """ Python 3: If ``s`` is an instance of ``text_type``, return ``s``, otherwise return ``str(s, encoding, errors)`` Python 2: If ``s`` is an instance of ``text_type``, return ``s.encode(encoding, errors)``, otherwise return ``str(s)`` - ''' + """ if PY3: out = s if isinstance(s, text_type) else str(s, encoding, errors) else: @@ -104,9 +107,9 @@ def native_(s, encoding='latin-1', errors='strict'): def string_io(data=None): # cStringIO can't handle unicode - ''' + """ Pass data through to stringIO module and return result - ''' + """ try: return cStringIO(bytes(data)) except (UnicodeEncodeError, TypeError): @@ -123,12 +126,13 @@ except ImportError: class IPv6AddressScoped(ipaddress.IPv6Address): - ''' + """ Represent and manipulate single IPv6 Addresses. Scope-aware version - ''' + """ + def __init__(self, address): - ''' + """ Instantiate a new IPv6 address object. Scope is moved to an attribute 'scope'. Args: @@ -143,15 +147,15 @@ class IPv6AddressScoped(ipaddress.IPv6Address): AddressValueError: If address isn't a valid IPv6 address. :param address: - ''' + """ # pylint: disable-all - if not hasattr(self, '_is_packed_binary'): + if not hasattr(self, "_is_packed_binary"): # This method (below) won't be around for some Python 3 versions # and we need check this differently anyway self._is_packed_binary = lambda p: isinstance(p, bytes) # pylint: enable-all - if isinstance(address, string_types) and '%' in address: - buff = address.split('%') + if isinstance(address, string_types) and "%" in address: + buff = address.split("%") if len(buff) != 2: raise SaltException('Invalid IPv6 address: "{}"'.format(address)) address, self.__scope = buff @@ -176,19 +180,21 @@ class IPv6AddressScoped(ipaddress.IPv6Address): self._ip = int(binascii.hexlify(address), 16) else: address = str(address) - if '/' in address: - raise ipaddress.AddressValueError("Unexpected '/' in {}".format(address)) + if "/" in address: + raise ipaddress.AddressValueError( + "Unexpected '/' in {}".format(address) + ) self._ip = self._ip_int_from_string(address) def _is_packed_binary(self, data): - ''' + """ Check if data is hexadecimal packed :param data: :return: - ''' + """ packed = False - if isinstance(data, bytes) and len(data) == 16 and b':' not in data: + if isinstance(data, bytes) and len(data) == 16 and b":" not in data: try: packed = bool(int(binascii.hexlify(data), 16)) except ValueError: @@ -198,25 +204,32 @@ class IPv6AddressScoped(ipaddress.IPv6Address): @property def scope(self): - ''' + """ Return scope of IPv6 address. :return: - ''' + """ return self.__scope def __str__(self): - return text_type(self._string_from_ip_int(self._ip) + - ('%' + self.scope if self.scope is not None else '')) + return text_type( + self._string_from_ip_int(self._ip) + + ("%" + self.scope if self.scope is not None else "") + ) class IPv6InterfaceScoped(ipaddress.IPv6Interface, IPv6AddressScoped): - ''' + """ Update - ''' + """ + def __init__(self, address): - if PY3 and isinstance(address, (bytes, int)) or \ - not PY3 and isinstance(address, int): + if ( + PY3 + and isinstance(address, (bytes, int)) + or not PY3 + and isinstance(address, int) + ): IPv6AddressScoped.__init__(self, address) self.network = ipaddress.IPv6Network(self._ip) self._prefixlen = self._max_prefixlen diff --git a/salt/_logging/__init__.py b/salt/_logging/__init__.py index ce33c32a9b7..6438964e717 100644 --- a/salt/_logging/__init__.py +++ b/salt/_logging/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" salt._logging ~~~~~~~~~~~~~ @@ -9,10 +9,9 @@ The ``salt._logging`` package should be imported as soon as possible since salt tweaks the python's logging system. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import salt libs from salt._logging.impl import * # pylint: disable=wildcard-import diff --git a/salt/_logging/handlers.py b/salt/_logging/handlers.py index 78466879011..844eaf7fd2d 100644 --- a/salt/_logging/handlers.py +++ b/salt/_logging/handlers.py @@ -1,29 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" salt._logging.handlers ~~~~~~~~~~~~~~~~~~~~~~ Salt's logging handlers -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import sys + import copy import logging import logging.handlers +import sys from collections import deque # Import salt libs -from salt._logging.mixins import NewStyleClassMixin, ExcInfoOnLogLevelFormatMixin +from salt._logging.mixins import ExcInfoOnLogLevelFormatMixin, NewStyleClassMixin from salt.ext.six.moves import queue # pylint: disable=import-error,no-name-in-module -#from salt.utils.versions import warn_until_date + +# from salt.utils.versions import warn_until_date log = logging.getLogger(__name__) class TemporaryLoggingHandler(logging.NullHandler): - ''' + """ This logging handler will store all the log records up to its maximum queue size at which stage the first messages stored will be dropped. @@ -36,15 +38,15 @@ class TemporaryLoggingHandler(logging.NullHandler): records will be dispatched to the provided handlers. .. versionadded:: 0.17.0 - ''' + """ def __init__(self, level=logging.NOTSET, max_queue_size=10000): - #warn_until_date( + # warn_until_date( # '20220101', # 'Please stop using \'{name}.TemporaryLoggingHandler\'. ' # '\'{name}.TemporaryLoggingHandler\' will go away after ' # '{{date}}.'.format(name=__name__) - #) + # ) self.__max_queue_size = max_queue_size super(TemporaryLoggingHandler, self).__init__(level=level) self.__messages = deque(maxlen=max_queue_size) @@ -55,9 +57,9 @@ class TemporaryLoggingHandler(logging.NullHandler): self.release() def sync_with_handlers(self, handlers=()): - ''' + """ Sync the stored log records to the provided log handlers. - ''' + """ if not handlers: return @@ -71,41 +73,42 @@ class TemporaryLoggingHandler(logging.NullHandler): handler.handle(record) -class StreamHandler(ExcInfoOnLogLevelFormatMixin, - logging.StreamHandler, - NewStyleClassMixin): - ''' +class StreamHandler( + ExcInfoOnLogLevelFormatMixin, logging.StreamHandler, NewStyleClassMixin +): + """ Stream handler which properly handles exc_info on a per handler basis - ''' + """ -class FileHandler(ExcInfoOnLogLevelFormatMixin, - logging.FileHandler, - NewStyleClassMixin): - ''' +class FileHandler( + ExcInfoOnLogLevelFormatMixin, logging.FileHandler, NewStyleClassMixin +): + """ File handler which properly handles exc_info on a per handler basis - ''' + """ -class SysLogHandler(ExcInfoOnLogLevelFormatMixin, - logging.handlers.SysLogHandler, - NewStyleClassMixin): - ''' +class SysLogHandler( + ExcInfoOnLogLevelFormatMixin, logging.handlers.SysLogHandler, NewStyleClassMixin +): + """ Syslog handler which properly handles exc_info on a per handler basis - ''' + """ + def handleError(self, record): - ''' + """ Override the default error handling mechanism for py3 Deal with syslog os errors when the log file does not exist - ''' + """ handled = False if sys.stderr and sys.version_info >= (3, 5, 4): exc_type, exc, exc_traceback = sys.exc_info() try: - if exc_type.__name__ in 'FileNotFoundError': + if exc_type.__name__ in "FileNotFoundError": sys.stderr.write( - '[WARNING ] The log_file does not exist. Logging not ' - 'setup correctly or syslog service not started.\n' + "[WARNING ] The log_file does not exist. Logging not " + "setup correctly or syslog service not started.\n" ) handled = True finally: @@ -117,34 +120,40 @@ class SysLogHandler(ExcInfoOnLogLevelFormatMixin, super(SysLogHandler, self).handleError(record) -class RotatingFileHandler(ExcInfoOnLogLevelFormatMixin, - logging.handlers.RotatingFileHandler, - NewStyleClassMixin): - ''' +class RotatingFileHandler( + ExcInfoOnLogLevelFormatMixin, + logging.handlers.RotatingFileHandler, + NewStyleClassMixin, +): + """ Rotating file handler which properly handles exc_info on a per handler basis - ''' + """ + def handleError(self, record): - ''' + """ Override the default error handling mechanism Deal with log file rotation errors due to log file in use more softly. - ''' + """ handled = False # Can't use "salt.utils.platform.is_windows()" in this file - if (sys.platform.startswith('win') and - logging.raiseExceptions and - sys.stderr): # see Python issue 13807 + if ( + sys.platform.startswith("win") and logging.raiseExceptions and sys.stderr + ): # see Python issue 13807 exc_type, exc, exc_traceback = sys.exc_info() try: # PermissionError is used since Python 3.3. # OSError is used for previous versions of Python. - if exc_type.__name__ in ('PermissionError', 'OSError') and exc.winerror == 32: + if ( + exc_type.__name__ in ("PermissionError", "OSError") + and exc.winerror == 32 + ): if self.level <= logging.WARNING: sys.stderr.write( '[WARNING ] Unable to rotate the log file "{0}" ' - 'because it is in use\n'.format(self.baseFilename) + "because it is in use\n".format(self.baseFilename) ) handled = True finally: @@ -156,17 +165,22 @@ class RotatingFileHandler(ExcInfoOnLogLevelFormatMixin, super(RotatingFileHandler, self).handleError(record) -class WatchedFileHandler(ExcInfoOnLogLevelFormatMixin, - logging.handlers.WatchedFileHandler, - NewStyleClassMixin): - ''' +class WatchedFileHandler( + ExcInfoOnLogLevelFormatMixin, + logging.handlers.WatchedFileHandler, + NewStyleClassMixin, +): + """ Watched file handler which properly handles exc_info on a per handler basis - ''' + """ if sys.version_info < (3, 2): - class QueueHandler(ExcInfoOnLogLevelFormatMixin, logging.Handler, NewStyleClassMixin): - ''' + + class QueueHandler( + ExcInfoOnLogLevelFormatMixin, logging.Handler, NewStyleClassMixin + ): + """ This handler sends events to a queue. Typically, it would be used together with a multiprocessing Queue to centralise logging to file in one process (in a multi-process application), so as to avoid file write contention @@ -174,38 +188,40 @@ if sys.version_info < (3, 2): This code is new in Python 3.2, but this class can be copy pasted into user code for use with earlier Python versions. - ''' + """ def __init__(self, queue): - ''' + """ Initialise an instance, using the passed queue. - ''' - #warn_until_date( + """ + # warn_until_date( # '20220101', # 'Please stop using \'{name}.QueueHandler\' and instead ' # 'use \'logging.handlers.QueueHandler\'. ' # '\'{name}.QueueHandler\' will go away after ' # '{{date}}.'.format(name=__name__) - #) + # ) logging.Handler.__init__(self) self.queue = queue def enqueue(self, record): - ''' + """ Enqueue a record. The base implementation uses put_nowait. You may want to override this method if you want to use blocking, timeouts or custom queue implementations. - ''' + """ try: self.queue.put_nowait(record) except queue.Full: - sys.stderr.write('[WARNING ] Message queue is full, ' - 'unable to write "{0}" to log'.format(record)) + sys.stderr.write( + "[WARNING ] Message queue is full, " + 'unable to write "{0}" to log'.format(record) + ) def prepare(self, record): - ''' + """ Prepares a record for queuing. The object returned by this method is enqueued. The base implementation formats the record to merge the message @@ -214,7 +230,7 @@ if sys.version_info < (3, 2): You might want to override this method if you want to convert the record to a dict or JSON string, or send a modified copy of the record while leaving the original intact. - ''' + """ # The format operation gets traceback text into record.exc_text # (if there's exception data), and also returns the formatted # message. We can then use this to replace the original @@ -232,46 +248,51 @@ if sys.version_info < (3, 2): return record def emit(self, record): - ''' + """ Emit a record. Writes the LogRecord to the queue, preparing it for pickling first. - ''' + """ try: self.enqueue(self.prepare(record)) except Exception: # pylint: disable=broad-except self.handleError(record) + + elif sys.version_info < (3, 7): # On python versions lower than 3.7, we sill subclass and overwrite prepare to include the fix for: # https://bugs.python.org/issue35726 - class QueueHandler(ExcInfoOnLogLevelFormatMixin, logging.handlers.QueueHandler): # pylint: disable=no-member,inconsistent-mro - + class QueueHandler( + ExcInfoOnLogLevelFormatMixin, logging.handlers.QueueHandler + ): # pylint: disable=no-member,inconsistent-mro def __init__(self, queue): # pylint: disable=useless-super-delegation super(QueueHandler, self).__init__(queue) - #warn_until_date( + # warn_until_date( # '20220101', # 'Please stop using \'{name}.QueueHandler\' and instead ' # 'use \'logging.handlers.QueueHandler\'. ' # '\'{name}.QueueHandler\' will go away after ' # '{{date}}.'.format(name=__name__) - #) + # ) def enqueue(self, record): - ''' + """ Enqueue a record. The base implementation uses put_nowait. You may want to override this method if you want to use blocking, timeouts or custom queue implementations. - ''' + """ try: self.queue.put_nowait(record) except queue.Full: - sys.stderr.write('[WARNING ] Message queue is full, ' - 'unable to write "{}" to log.\n'.format(record)) + sys.stderr.write( + "[WARNING ] Message queue is full, " + 'unable to write "{}" to log.\n'.format(record) + ) def prepare(self, record): - ''' + """ Prepares a record for queuing. The object returned by this method is enqueued. The base implementation formats the record to merge the message @@ -280,7 +301,7 @@ elif sys.version_info < (3, 7): You might want to override this method if you want to convert the record to a dict or JSON string, or send a modified copy of the record while leaving the original intact. - ''' + """ # The format operation gets traceback text into record.exc_text # (if there's exception data), and also returns the formatted # message. We can then use this to replace the original @@ -296,29 +317,35 @@ elif sys.version_info < (3, 7): record.exc_info = None record.exc_text = None return record -else: - class QueueHandler(ExcInfoOnLogLevelFormatMixin, logging.handlers.QueueHandler): # pylint: disable=no-member,inconsistent-mro + +else: + + class QueueHandler( + ExcInfoOnLogLevelFormatMixin, logging.handlers.QueueHandler + ): # pylint: disable=no-member,inconsistent-mro def __init__(self, queue): # pylint: disable=useless-super-delegation super(QueueHandler, self).__init__(queue) - #warn_until_date( + # warn_until_date( # '20220101', # 'Please stop using \'{name}.QueueHandler\' and instead ' # 'use \'logging.handlers.QueueHandler\'. ' # '\'{name}.QueueHandler\' will go away after ' # '{{date}}.'.format(name=__name__) - #) + # ) def enqueue(self, record): - ''' + """ Enqueue a record. The base implementation uses put_nowait. You may want to override this method if you want to use blocking, timeouts or custom queue implementations. - ''' + """ try: self.queue.put_nowait(record) except queue.Full: - sys.stderr.write('[WARNING ] Message queue is full, ' - 'unable to write "{0}" to log.\n'.format(record)) + sys.stderr.write( + "[WARNING ] Message queue is full, " + 'unable to write "{0}" to log.\n'.format(record) + ) diff --git a/salt/_logging/impl.py b/salt/_logging/impl.py index 347259bcf50..5a6292b445c 100644 --- a/salt/_logging/impl.py +++ b/salt/_logging/impl.py @@ -1,17 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" salt._logging.impl ~~~~~~~~~~~~~~~~~~ Salt's logging implementation classes/functionality -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import re import sys import types -import logging + +# Import 3rd-party libs +import salt.ext.six as six # Let's define these custom logging levels before importing the salt._logging.mixins # since they will be used there @@ -21,67 +25,74 @@ GARBAGE = logging.GARBAGE = 1 QUIET = logging.QUIET = 1000 # Import Salt libs -from salt._logging.handlers import StreamHandler -#from salt._logging.handlers import SysLogHandler -#from salt._logging.handlers import RotatingFileHandler -#from salt._logging.handlers import WatchedFileHandler -from salt._logging.handlers import TemporaryLoggingHandler -from salt._logging.mixins import LoggingMixinMeta -from salt._logging.mixins import NewStyleClassMixin -from salt.exceptions import LoggingRuntimeError -from salt.utils.ctx import RequestContext -from salt.utils.textformat import TextFormat +from salt._logging.handlers import StreamHandler # isort:skip -# Import 3rd-party libs -import salt.ext.six as six -#from salt.ext.six.moves.urllib.parse import urlparse # pylint: disable=import-error,no-name-in-module +# from salt._logging.handlers import SysLogHandler # isort:skip +# from salt._logging.handlers import RotatingFileHandler # isort:skip +# from salt._logging.handlers import WatchedFileHandler # isort:skip +from salt._logging.handlers import TemporaryLoggingHandler # isort:skip +from salt._logging.mixins import LoggingMixinMeta # isort:skip +from salt._logging.mixins import NewStyleClassMixin # isort:skip +from salt.exceptions import LoggingRuntimeError # isort:skip +from salt.utils.ctx import RequestContext # isort:skip +from salt.utils.textformat import TextFormat # isort:skip + +# from salt.ext.six.moves.urllib.parse import urlparse # pylint: disable=import-error,no-name-in-module LOG_LEVELS = { - 'all': logging.NOTSET, - 'debug': logging.DEBUG, - 'error': logging.ERROR, - 'critical': logging.CRITICAL, - 'garbage': GARBAGE, - 'info': logging.INFO, - 'profile': PROFILE, - 'quiet': QUIET, - 'trace': TRACE, - 'warning': logging.WARNING, + "all": logging.NOTSET, + "debug": logging.DEBUG, + "error": logging.ERROR, + "critical": logging.CRITICAL, + "garbage": GARBAGE, + "info": logging.INFO, + "profile": PROFILE, + "quiet": QUIET, + "trace": TRACE, + "warning": logging.WARNING, } LOG_VALUES_TO_LEVELS = dict((v, k) for (k, v) in LOG_LEVELS.items()) LOG_COLORS = { - 'levels': { - 'QUIET': TextFormat('reset'), - 'CRITICAL': TextFormat('bold', 'red'), - 'ERROR': TextFormat('bold', 'red'), - 'WARNING': TextFormat('bold', 'yellow'), - 'INFO': TextFormat('bold', 'green'), - 'PROFILE': TextFormat('bold', 'cyan'), - 'DEBUG': TextFormat('bold', 'cyan'), - 'TRACE': TextFormat('bold', 'magenta'), - 'GARBAGE': TextFormat('bold', 'blue'), - 'NOTSET': TextFormat('reset'), - 'SUBDEBUG': TextFormat('bold', 'cyan'), # used by multiprocessing.log_to_stderr() - 'SUBWARNING': TextFormat('bold', 'yellow'), # used by multiprocessing.log_to_stderr() + "levels": { + "QUIET": TextFormat("reset"), + "CRITICAL": TextFormat("bold", "red"), + "ERROR": TextFormat("bold", "red"), + "WARNING": TextFormat("bold", "yellow"), + "INFO": TextFormat("bold", "green"), + "PROFILE": TextFormat("bold", "cyan"), + "DEBUG": TextFormat("bold", "cyan"), + "TRACE": TextFormat("bold", "magenta"), + "GARBAGE": TextFormat("bold", "blue"), + "NOTSET": TextFormat("reset"), + "SUBDEBUG": TextFormat( + "bold", "cyan" + ), # used by multiprocessing.log_to_stderr() + "SUBWARNING": TextFormat( + "bold", "yellow" + ), # used by multiprocessing.log_to_stderr() }, - 'msgs': { - 'QUIET': TextFormat('reset'), - 'CRITICAL': TextFormat('bold', 'red'), - 'ERROR': TextFormat('red'), - 'WARNING': TextFormat('yellow'), - 'INFO': TextFormat('green'), - 'PROFILE': TextFormat('bold', 'cyan'), - 'DEBUG': TextFormat('cyan'), - 'TRACE': TextFormat('magenta'), - 'GARBAGE': TextFormat('blue'), - 'NOTSET': TextFormat('reset'), - 'SUBDEBUG': TextFormat('bold', 'cyan'), # used by multiprocessing.log_to_stderr() - 'SUBWARNING': TextFormat('bold', 'yellow'), # used by multiprocessing.log_to_stderr() + "msgs": { + "QUIET": TextFormat("reset"), + "CRITICAL": TextFormat("bold", "red"), + "ERROR": TextFormat("red"), + "WARNING": TextFormat("yellow"), + "INFO": TextFormat("green"), + "PROFILE": TextFormat("bold", "cyan"), + "DEBUG": TextFormat("cyan"), + "TRACE": TextFormat("magenta"), + "GARBAGE": TextFormat("blue"), + "NOTSET": TextFormat("reset"), + "SUBDEBUG": TextFormat( + "bold", "cyan" + ), # used by multiprocessing.log_to_stderr() + "SUBWARNING": TextFormat( + "bold", "yellow" + ), # used by multiprocessing.log_to_stderr() }, - 'name': TextFormat('bold', 'green'), - 'process': TextFormat('bold', 'blue'), + "name": TextFormat("bold", "green"), + "process": TextFormat("bold", "blue"), } # Make a list of log level names sorted by log level @@ -89,22 +100,22 @@ SORTED_LEVEL_NAMES = [ l[0] for l in sorted(six.iteritems(LOG_LEVELS), key=lambda x: x[1]) ] -MODNAME_PATTERN = re.compile(r'(?P%%\(name\)(?:\-(?P[\d]+))?s)') +MODNAME_PATTERN = re.compile(r"(?P%%\(name\)(?:\-(?P[\d]+))?s)") # ----- REMOVE ME ON REFACTOR COMPLETE ------------------------------------------------------------------------------> class __NullLoggingHandler(TemporaryLoggingHandler): - ''' + """ This class exists just to better identify which temporary logging handler is being used for what. - ''' + """ class __StoreLoggingHandler(TemporaryLoggingHandler): - ''' + """ This class exists just to better identify which temporary logging handler is being used for what. - ''' + """ # Store a reference to the temporary queue logging handler @@ -121,35 +132,31 @@ LOGGING_STORE_HANDLER = __StoreLoggingHandler() class SaltLogRecord(logging.LogRecord): def __init__(self, *args, **kwargs): logging.LogRecord.__init__(self, *args, **kwargs) - self.bracketname = '[{:<17}]'.format(str(self.name)) - self.bracketlevel = '[{:<8}]'.format(str(self.levelname)) - self.bracketprocess = '[{:>5}]'.format(str(self.process)) + self.bracketname = "[{:<17}]".format(str(self.name)) + self.bracketlevel = "[{:<8}]".format(str(self.levelname)) + self.bracketprocess = "[{:>5}]".format(str(self.process)) class SaltColorLogRecord(SaltLogRecord): def __init__(self, *args, **kwargs): SaltLogRecord.__init__(self, *args, **kwargs) - reset = TextFormat('reset') - clevel = LOG_COLORS['levels'].get(self.levelname, reset) - cmsg = LOG_COLORS['msgs'].get(self.levelname, reset) + reset = TextFormat("reset") + clevel = LOG_COLORS["levels"].get(self.levelname, reset) + cmsg = LOG_COLORS["msgs"].get(self.levelname, reset) - self.colorname = '{}[{:<17}]{}'.format(LOG_COLORS['name'], - self.name, - reset) - self.colorlevel = '{}[{:<8}]{}'.format(clevel, - self.levelname, - reset) - self.colorprocess = '{}[{:>5}]{}'.format(LOG_COLORS['process'], - self.process, - reset) - self.colormsg = '{}{}{}'.format(cmsg, self.getMessage(), reset) + self.colorname = "{}[{:<17}]{}".format(LOG_COLORS["name"], self.name, reset) + self.colorlevel = "{}[{:<8}]{}".format(clevel, self.levelname, reset) + self.colorprocess = "{}[{:>5}]{}".format( + LOG_COLORS["process"], self.process, reset + ) + self.colormsg = "{}{}{}".format(cmsg, self.getMessage(), reset) def get_log_record_factory(): - ''' + """ Get the logging log record factory - ''' + """ try: return get_log_record_factory.__factory__ except AttributeError: @@ -157,9 +164,9 @@ def get_log_record_factory(): def set_log_record_factory(factory): - ''' + """ Set the logging log record factory - ''' + """ get_log_record_factory.__factory__ = factory if not six.PY2: logging.setLogRecordFactory(factory) @@ -172,9 +179,11 @@ set_log_record_factory(SaltLogRecord) LOGGING_LOGGER_CLASS = logging.getLoggerClass() -class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS, NewStyleClassMixin)): +class SaltLoggingClass( + six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS, NewStyleClassMixin) +): def __new__(cls, *args): - ''' + """ We override `__new__` in our logging logger class in order to provide some additional features like expand the module name padding if length is being used, and also some Unicode fixes. @@ -184,17 +193,19 @@ class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS logging.getLogger(__name__) - ''' + """ instance = super(SaltLoggingClass, cls).__new__(cls) try: - max_logger_length = len(max( - list(logging.Logger.manager.loggerDict), key=len - )) + max_logger_length = len( + max(list(logging.Logger.manager.loggerDict), key=len) + ) for handler in logging.root.handlers: - if handler in (LOGGING_NULL_HANDLER, - LOGGING_STORE_HANDLER, - LOGGING_TEMP_HANDLER): + if handler in ( + LOGGING_NULL_HANDLER, + LOGGING_STORE_HANDLER, + LOGGING_TEMP_HANDLER, + ): continue formatter = handler.formatter @@ -205,7 +216,7 @@ class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS handler.createLock() handler.acquire() - fmt = formatter._fmt.replace('%', '%%') + fmt = formatter._fmt.replace("%", "%%") match = MODNAME_PATTERN.search(fmt) if not match: @@ -213,12 +224,12 @@ class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS handler.release() return instance - if 'digits' not in match.groupdict(): + if "digits" not in match.groupdict(): # No digits group. Release handler and return. handler.release() return instance - digits = match.group('digits') + digits = match.group("digits") if not digits or not (digits and digits.isdigit()): # No valid digits. Release handler and return. handler.release() @@ -226,10 +237,9 @@ class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS if int(digits) < max_logger_length: # Formatter digits value is lower than current max, update. - fmt = fmt.replace(match.group('name'), '%%(name)-%ds') + fmt = fmt.replace(match.group("name"), "%%(name)-%ds") formatter = logging.Formatter( - fmt % max_logger_length, - datefmt=formatter.datefmt + fmt % max_logger_length, datefmt=formatter.datefmt ) handler.setFormatter(formatter) handler.release() @@ -238,45 +248,52 @@ class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS pass return instance - def _log(self, level, msg, args, exc_info=None, - extra=None, # pylint: disable=arguments-differ - stack_info=False, - stacklevel=1, - exc_info_on_loglevel=None): + def _log( + self, + level, + msg, + args, + exc_info=None, + extra=None, # pylint: disable=arguments-differ + stack_info=False, + stacklevel=1, + exc_info_on_loglevel=None, + ): if extra is None: extra = {} # pylint: disable=no-member - current_jid = RequestContext.current.get('data', {}).get('jid', None) - log_fmt_jid = RequestContext.current.get('opts', {}).get('log_fmt_jid', None) + current_jid = RequestContext.current.get("data", {}).get("jid", None) + log_fmt_jid = RequestContext.current.get("opts", {}).get("log_fmt_jid", None) # pylint: enable=no-member if current_jid is not None: - extra['jid'] = current_jid + extra["jid"] = current_jid if log_fmt_jid is not None: - extra['log_fmt_jid'] = log_fmt_jid + extra["log_fmt_jid"] = log_fmt_jid # If both exc_info and exc_info_on_loglevel are both passed, let's fail if exc_info and exc_info_on_loglevel: raise LoggingRuntimeError( - 'Only one of \'exc_info\' and \'exc_info_on_loglevel\' is ' - 'permitted' + "Only one of 'exc_info' and 'exc_info_on_loglevel' is " "permitted" ) if exc_info_on_loglevel is not None: if isinstance(exc_info_on_loglevel, six.string_types): - exc_info_on_loglevel = LOG_LEVELS.get(exc_info_on_loglevel, - logging.ERROR) + exc_info_on_loglevel = LOG_LEVELS.get( + exc_info_on_loglevel, logging.ERROR + ) elif not isinstance(exc_info_on_loglevel, int): raise RuntimeError( - 'The value of \'exc_info_on_loglevel\' needs to be a ' - 'logging level or a logging level name, not \'{}\'' - .format(exc_info_on_loglevel) + "The value of 'exc_info_on_loglevel' needs to be a " + "logging level or a logging level name, not '{}'".format( + exc_info_on_loglevel + ) ) if extra is None: - extra = {'exc_info_on_loglevel': exc_info_on_loglevel} + extra = {"exc_info_on_loglevel": exc_info_on_loglevel} else: - extra['exc_info_on_loglevel'] = exc_info_on_loglevel + extra["exc_info_on_loglevel"] = exc_info_on_loglevel if sys.version_info < (3,): LOGGING_LOGGER_CLASS._log( @@ -284,24 +301,46 @@ class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS ) elif sys.version_info < (3, 8): LOGGING_LOGGER_CLASS._log( - self, level, msg, args, exc_info=exc_info, extra=extra, - stack_info=stack_info + self, + level, + msg, + args, + exc_info=exc_info, + extra=extra, + stack_info=stack_info, ) else: LOGGING_LOGGER_CLASS._log( - self, level, msg, args, exc_info=exc_info, extra=extra, - stack_info=stack_info, stacklevel=stacklevel + self, + level, + msg, + args, + exc_info=exc_info, + extra=extra, + stack_info=stack_info, + stacklevel=stacklevel, ) - def makeRecord(self, name, level, fn, lno, msg, args, exc_info, - func=None, extra=None, sinfo=None): + def makeRecord( + self, + name, + level, + fn, + lno, + msg, + args, + exc_info, + func=None, + extra=None, + sinfo=None, + ): # Let's remove exc_info_on_loglevel from extra - exc_info_on_loglevel = extra.pop('exc_info_on_loglevel') + exc_info_on_loglevel = extra.pop("exc_info_on_loglevel") - jid = extra.pop('jid', '') + jid = extra.pop("jid", "") if jid: - log_fmt_jid = extra.pop('log_fmt_jid') - jid = log_fmt_jid % {'jid': jid} + log_fmt_jid = extra.pop("log_fmt_jid") + jid = log_fmt_jid % {"jid": jid} if not extra: # If nothing else is in extra, make it None @@ -310,30 +349,31 @@ class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS # Let's try to make every logging message unicode try: salt_system_encoding = __salt_system_encoding__ - if salt_system_encoding == 'ascii': + if salt_system_encoding == "ascii": # Encoding detection most likely failed, let's use the utf-8 # value which we defaulted before __salt_system_encoding__ was # implemented - salt_system_encoding = 'utf-8' + salt_system_encoding = "utf-8" except NameError: - salt_system_encoding = 'utf-8' + salt_system_encoding = "utf-8" if isinstance(msg, six.string_types) and not isinstance(msg, six.text_type): try: - _msg = msg.decode(salt_system_encoding, 'replace') + _msg = msg.decode(salt_system_encoding, "replace") except UnicodeDecodeError: - _msg = msg.decode(salt_system_encoding, 'ignore') + _msg = msg.decode(salt_system_encoding, "ignore") else: _msg = msg _args = [] for item in args: - if isinstance(item, six.string_types) \ - and not isinstance(item, six.text_type): + if isinstance(item, six.string_types) and not isinstance( + item, six.text_type + ): try: - _args.append(item.decode(salt_system_encoding, 'replace')) + _args.append(item.decode(salt_system_encoding, "replace")) except UnicodeDecodeError: - _args.append(item.decode(salt_system_encoding, 'ignore')) + _args.append(item.decode(salt_system_encoding, "ignore")) else: _args.append(item) _args = tuple(_args) @@ -342,34 +382,20 @@ class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS # Recreate what's done for Py >= 3.5 _log_record_factory = get_log_record_factory() logrecord = _log_record_factory( - name, - level, - fn, - lno, - _msg, - _args, - exc_info, - func) + name, level, fn, lno, _msg, _args, exc_info, func + ) if extra is not None: for key in extra: - if (key in ['message', 'asctime']) or (key in logrecord.__dict__): + if (key in ["message", "asctime"]) or (key in logrecord.__dict__): raise KeyError( - 'Attempt to overwrite \'{}\' in LogRecord'.format(key) + "Attempt to overwrite '{}' in LogRecord".format(key) ) logrecord.__dict__[key] = extra[key] else: logrecord = LOGGING_LOGGER_CLASS.makeRecord( - self, - name, - level, - fn, - lno, - _msg, - _args, - exc_info, - func, - sinfo) + self, name, level, fn, lno, _msg, _args, exc_info, func, sinfo + ) if exc_info_on_loglevel is not None: # Let's add some custom attributes to the LogRecord class in order @@ -389,10 +415,10 @@ class SaltLoggingClass(six.with_metaclass(LoggingMixinMeta, LOGGING_LOGGER_CLASS if logging.getLoggerClass() is not SaltLoggingClass: logging.setLoggerClass(SaltLoggingClass) - logging.addLevelName(QUIET, 'QUIET') - logging.addLevelName(PROFILE, 'PROFILE') - logging.addLevelName(TRACE, 'TRACE') - logging.addLevelName(GARBAGE, 'GARBAGE') + logging.addLevelName(QUIET, "QUIET") + logging.addLevelName(PROFILE, "PROFILE") + logging.addLevelName(TRACE, "TRACE") + logging.addLevelName(GARBAGE, "GARBAGE") # ----- REMOVE ON REFACTORING COMPLETE --------------------------------------------------------------------------> if not logging.root.handlers: @@ -417,18 +443,18 @@ log = logging.getLogger(__name__) def __get_exposed_module_attributes(): - ''' + """ This function just ``dir()``'s this module and filters out any functions or variables which should not be available when wildcard importing it - ''' + """ exposed = [] module = sys.modules[__name__] for name in dir(module): - if name.startswith('_'): + if name.startswith("_"): continue obj = getattr(module, name) if not isinstance(obj, types.FunctionType): - if name.startswith(('LOG_', 'SORTED_')): + if name.startswith(("LOG_", "SORTED_")): exposed.append(name) continue if obj.__module__ != __name__: diff --git a/salt/_logging/mixins.py b/salt/_logging/mixins.py index 82d673dd22e..57cb1842761 100644 --- a/salt/_logging/mixins.py +++ b/salt/_logging/mixins.py @@ -1,70 +1,72 @@ # -*- coding: utf-8 -*- -''' +""" salt._logging.mixins ~~~~~~~~~~~~~~~~~~~~ Logging related mix-ins -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import sys + import logging +import sys class NewStyleClassMixin(object): - ''' + """ Simple new style class to make pylint shut up! This is required because SaltLoggingClass can't subclass object directly: 'Cannot create a consistent method resolution order (MRO) for bases' - ''' + """ class LoggingProfileMixin(object): - ''' + """ Simple mix-in class to add a trace method to python's logging. - ''' + """ def profile(self, msg, *args, **kwargs): - self.log(getattr(logging, 'PROFILE', 15), msg, *args, **kwargs) + self.log(getattr(logging, "PROFILE", 15), msg, *args, **kwargs) class LoggingTraceMixin(object): - ''' + """ Simple mix-in class to add a trace method to python's logging. - ''' + """ def trace(self, msg, *args, **kwargs): - self.log(getattr(logging, 'TRACE', 5), msg, *args, **kwargs) + self.log(getattr(logging, "TRACE", 5), msg, *args, **kwargs) class LoggingGarbageMixin(object): - ''' + """ Simple mix-in class to add a garbage method to python's logging. - ''' + """ def garbage(self, msg, *args, **kwargs): - self.log(getattr(logging, 'GARBAGE', 5), msg, *args, **kwargs) + self.log(getattr(logging, "GARBAGE", 5), msg, *args, **kwargs) class LoggingMixinMeta(type): - ''' + """ This class is called whenever a new instance of ``SaltLoggingClass`` is created. What this class does is check if any of the bases have a `trace()` or a `garbage()` method defined, if they don't we add the respective mix-ins to the bases. - ''' + """ + def __new__(mcs, name, bases, attrs): include_profile = include_trace = include_garbage = True bases = list(bases) - if name == 'SaltLoggingClass': + if name == "SaltLoggingClass": for base in bases: - if hasattr(base, 'trace'): + if hasattr(base, "trace"): include_trace = False - if hasattr(base, 'garbage'): + if hasattr(base, "garbage"): include_garbage = False if include_profile: bases.append(LoggingProfileMixin) @@ -76,17 +78,19 @@ class LoggingMixinMeta(type): class ExcInfoOnLogLevelFormatMixin(object): - ''' + """ Logging handler class mixin to properly handle including exc_info on a per logging handler basis - ''' + """ def format(self, record): - ''' + """ Format the log record to include exc_info if the handler is enabled for a specific log level - ''' + """ formatted_record = super(ExcInfoOnLogLevelFormatMixin, self).format(record) - exc_info_on_loglevel = getattr(record, 'exc_info_on_loglevel', None) - exc_info_on_loglevel_formatted = getattr(record, 'exc_info_on_loglevel_formatted', None) + exc_info_on_loglevel = getattr(record, "exc_info_on_loglevel", None) + exc_info_on_loglevel_formatted = getattr( + record, "exc_info_on_loglevel_formatted", None + ) if exc_info_on_loglevel is None and exc_info_on_loglevel_formatted is None: return formatted_record @@ -98,21 +102,26 @@ class ExcInfoOnLogLevelFormatMixin(object): return formatted_record # If we reached this far it means we should include exc_info - if not record.exc_info_on_loglevel_instance and not exc_info_on_loglevel_formatted: + if ( + not record.exc_info_on_loglevel_instance + and not exc_info_on_loglevel_formatted + ): # This should actually never occur return formatted_record if record.exc_info_on_loglevel_formatted is None: # Let's cache the formatted exception to avoid recurring conversions and formatting calls - if self.formatter is None: # pylint: disable=access-member-before-definition + if ( + self.formatter is None + ): # pylint: disable=access-member-before-definition self.formatter = logging._defaultFormatter record.exc_info_on_loglevel_formatted = self.formatter.formatException( record.exc_info_on_loglevel_instance ) # Let's format the record to include exc_info just like python's logging formatted does - if formatted_record[-1:] != '\n': - formatted_record += '\n' + if formatted_record[-1:] != "\n": + formatted_record += "\n" try: formatted_record += record.exc_info_on_loglevel_formatted @@ -126,8 +135,7 @@ class ExcInfoOnLogLevelFormatMixin(object): # encodings, e.g. UTF-8 for the filesystem and latin-1 # for a script. See issue 13232. formatted_record += record.exc_info_on_loglevel_formatted.decode( - sys.getfilesystemencoding(), - 'replace' + sys.getfilesystemencoding(), "replace" ) # Reset the record.exc_info_on_loglevel_instance because it might need # to "travel" through a multiprocessing process and it might contain diff --git a/salt/acl/__init__.py b/salt/acl/__init__.py index 29050679a54..9b090c584c3 100644 --- a/salt/acl/__init__.py +++ b/salt/acl/__init__.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" The acl module handles publisher_acl operations Additional information on publisher_acl can be found by reading the salt documentation: http://docs.saltstack.com/en/latest/ref/publisheracl.html -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals @@ -19,37 +19,46 @@ from salt.ext import six class PublisherACL(object): - ''' + """ Represents the publisher ACL and provides methods to query the ACL for given operations - ''' + """ + def __init__(self, blacklist): self.blacklist = blacklist def user_is_blacklisted(self, user): - ''' + """ Takes a username as a string and returns a boolean. True indicates that the provided user has been blacklisted - ''' - return not salt.utils.stringutils.check_whitelist_blacklist(user, blacklist=self.blacklist.get('users', [])) + """ + return not salt.utils.stringutils.check_whitelist_blacklist( + user, blacklist=self.blacklist.get("users", []) + ) def cmd_is_blacklisted(self, cmd): # If this is a regular command, it is a single function if isinstance(cmd, six.string_types): cmd = [cmd] for fun in cmd: - if not salt.utils.stringutils.check_whitelist_blacklist(fun, blacklist=self.blacklist.get('modules', [])): + if not salt.utils.stringutils.check_whitelist_blacklist( + fun, blacklist=self.blacklist.get("modules", []) + ): return True return False def user_is_whitelisted(self, user): - return salt.utils.stringutils.check_whitelist_blacklist(user, whitelist=self.blacklist.get('users', [])) + return salt.utils.stringutils.check_whitelist_blacklist( + user, whitelist=self.blacklist.get("users", []) + ) def cmd_is_whitelisted(self, cmd): # If this is a regular command, it is a single function if isinstance(cmd, str): cmd = [cmd] for fun in cmd: - if salt.utils.stringutils.check_whitelist_blacklist(fun, whitelist=self.blacklist.get('modules', [])): + if salt.utils.stringutils.check_whitelist_blacklist( + fun, whitelist=self.blacklist.get("modules", []) + ): return True return False diff --git a/salt/auth/__init__.py b/salt/auth/__init__.py index 329e4a62c93..240b5356770 100644 --- a/salt/auth/__init__.py +++ b/salt/auth/__init__.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Salt's pluggable authentication system This system allows for authentication to be managed in a module pluggable way so that any external authentication system can be used inside of Salt -''' +""" # 1. Create auth loader instance # 2. Accept arguments as a dict @@ -15,13 +15,12 @@ so that any external authentication system can be used inside of Salt # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import collections -import time +import getpass import logging import random -import getpass -from salt.ext.six.moves import input -from salt.ext import six +import time # Import salt libs import salt.config @@ -36,28 +35,33 @@ 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__) -AUTH_INTERNAL_KEYWORDS = frozenset([ - 'client', - 'cmd', - 'eauth', - 'fun', - 'gather_job_timeout', - 'kwarg', - 'match', - 'metadata', - 'print_event', - 'raw', - 'yield_pub_data' -]) +AUTH_INTERNAL_KEYWORDS = frozenset( + [ + "client", + "cmd", + "eauth", + "fun", + "gather_job_timeout", + "kwarg", + "match", + "metadata", + "print_event", + "raw", + "yield_pub_data", + ] +) class LoadAuth(object): - ''' + """ Wrap the authentication system to handle peripheral components - ''' + """ + def __init__(self, opts, ckminions=None): self.opts = opts self.max_fail = 1.0 @@ -67,55 +71,54 @@ class LoadAuth(object): self.ckminions = ckminions or salt.utils.minions.CkMinions(opts) def load_name(self, load): - ''' + """ Return the primary name associate with the load, if an empty string is returned then the load does not match the function - ''' - if 'eauth' not in load: - return '' - fstr = '{0}.auth'.format(load['eauth']) + """ + if "eauth" not in load: + return "" + fstr = "{0}.auth".format(load["eauth"]) if fstr not in self.auth: - return '' + return "" try: - pname_arg = salt.utils.args.arg_lookup(self.auth[fstr])['args'][0] + pname_arg = salt.utils.args.arg_lookup(self.auth[fstr])["args"][0] return load[pname_arg] except IndexError: - return '' + return "" def __auth_call(self, load): - ''' + """ Return the token and set the cache data for use Do not call this directly! Use the time_auth method to overcome timing attacks - ''' - if 'eauth' not in load: + """ + if "eauth" not in load: return False - fstr = '{0}.auth'.format(load['eauth']) + fstr = "{0}.auth".format(load["eauth"]) if fstr not in self.auth: return False # When making auth calls, only username, password, auth, and token # are valid, so we strip anything else out. - _valid = ['username', 'password', 'eauth', 'token'] + _valid = ["username", "password", "eauth", "token"] _load = {key: value for (key, value) in load.items() if key in _valid} fcall = salt.utils.args.format_call( - self.auth[fstr], - _load, - expected_extra_kws=AUTH_INTERNAL_KEYWORDS) + self.auth[fstr], _load, expected_extra_kws=AUTH_INTERNAL_KEYWORDS + ) try: - if 'kwargs' in fcall: - return self.auth[fstr](*fcall['args'], **fcall['kwargs']) + if "kwargs" in fcall: + return self.auth[fstr](*fcall["args"], **fcall["kwargs"]) else: - return self.auth[fstr](*fcall['args']) + return self.auth[fstr](*fcall["args"]) except Exception as e: # pylint: disable=broad-except - log.debug('Authentication module threw %s', e) + log.debug("Authentication module threw %s", e) return False def time_auth(self, load): - ''' + """ Make sure that all failures happen in the same amount of time - ''' + """ start = time.time() ret = self.__auth_call(load) if ret: @@ -125,127 +128,130 @@ class LoadAuth(object): self.max_fail = f_time deviation = self.max_fail / 4 r_time = random.SystemRandom().uniform( - self.max_fail - deviation, - self.max_fail + deviation - ) + self.max_fail - deviation, self.max_fail + deviation + ) while start + r_time > time.time(): time.sleep(0.001) return False def __get_acl(self, load): - ''' + """ Returns ACL for a specific user. Returns None if eauth doesn't provide any for the user. I. e. None means: use acl declared in master config. - ''' - if 'eauth' not in load: + """ + if "eauth" not in load: return None - mod = self.opts['eauth_acl_module'] + mod = self.opts["eauth_acl_module"] if not mod: - mod = load['eauth'] - fstr = '{0}.acl'.format(mod) + mod = load["eauth"] + fstr = "{0}.acl".format(mod) if fstr not in self.auth: return None fcall = salt.utils.args.format_call( - self.auth[fstr], - load, - expected_extra_kws=AUTH_INTERNAL_KEYWORDS) + self.auth[fstr], load, expected_extra_kws=AUTH_INTERNAL_KEYWORDS + ) try: - return self.auth[fstr](*fcall['args'], **fcall['kwargs']) + return self.auth[fstr](*fcall["args"], **fcall["kwargs"]) except Exception as e: # pylint: disable=broad-except - log.debug('Authentication module threw %s', e) + log.debug("Authentication module threw %s", e) return None def __process_acl(self, load, auth_list): - ''' + """ Allows eauth module to modify the access list right before it'll be applied to the request. For example ldap auth module expands entries - ''' - if 'eauth' not in load: + """ + if "eauth" not in load: return auth_list - fstr = '{0}.process_acl'.format(load['eauth']) + fstr = "{0}.process_acl".format(load["eauth"]) if fstr not in self.auth: return auth_list try: return self.auth[fstr](auth_list, self.opts) except Exception as e: # pylint: disable=broad-except - log.debug('Authentication module threw %s', e) + log.debug("Authentication module threw %s", e) return auth_list def get_groups(self, load): - ''' + """ Read in a load and return the groups a user is a member of by asking the appropriate provider - ''' - if 'eauth' not in load: + """ + if "eauth" not in load: return False - fstr = '{0}.groups'.format(load['eauth']) + fstr = "{0}.groups".format(load["eauth"]) if fstr not in self.auth: return False fcall = salt.utils.args.format_call( - self.auth[fstr], - load, - expected_extra_kws=AUTH_INTERNAL_KEYWORDS) + self.auth[fstr], load, expected_extra_kws=AUTH_INTERNAL_KEYWORDS + ) try: - return self.auth[fstr](*fcall['args'], **fcall['kwargs']) + return self.auth[fstr](*fcall["args"], **fcall["kwargs"]) except IndexError: return False except Exception: # pylint: disable=broad-except return None def _allow_custom_expire(self, load): - ''' + """ Return bool if requesting user is allowed to set custom expire - ''' - expire_override = self.opts.get('token_expire_user_override', False) + """ + expire_override = self.opts.get("token_expire_user_override", False) if expire_override is True: return True if isinstance(expire_override, collections.Mapping): - expire_whitelist = expire_override.get(load['eauth'], []) + expire_whitelist = expire_override.get(load["eauth"], []) if isinstance(expire_whitelist, collections.Iterable): - if load.get('username') in expire_whitelist: + if load.get("username") in expire_whitelist: return True return False def mk_token(self, load): - ''' + """ Run time_auth and create a token. Return False or the token - ''' + """ if not self.authenticate_eauth(load): return {} if self._allow_custom_expire(load): - token_expire = load.pop('token_expire', self.opts['token_expire']) + token_expire = load.pop("token_expire", self.opts["token_expire"]) else: - _ = load.pop('token_expire', None) - token_expire = self.opts['token_expire'] + _ = load.pop("token_expire", None) + token_expire = self.opts["token_expire"] - tdata = {'start': time.time(), - 'expire': time.time() + token_expire, - 'name': self.load_name(load), - 'eauth': load['eauth']} + tdata = { + "start": time.time(), + "expire": time.time() + token_expire, + "name": self.load_name(load), + "eauth": load["eauth"], + } - if self.opts['keep_acl_in_token']: + if self.opts["keep_acl_in_token"]: acl_ret = self.__get_acl(load) - tdata['auth_list'] = acl_ret + tdata["auth_list"] = acl_ret groups = self.get_groups(load) if groups: - tdata['groups'] = groups + tdata["groups"] = groups - return self.tokens["{0}.mk_token".format(self.opts['eauth_tokens'])](self.opts, tdata) + return self.tokens["{0}.mk_token".format(self.opts["eauth_tokens"])]( + self.opts, tdata + ) def get_tok(self, tok): - ''' + """ Return the name associated with the token, or False if the token is not valid - ''' + """ tdata = {} try: - tdata = self.tokens["{0}.get_token".format(self.opts['eauth_tokens'])](self.opts, tok) + tdata = self.tokens["{0}.get_token".format(self.opts["eauth_tokens"])]( + self.opts, tok + ) except salt.exceptions.SaltDeserializationError: log.warning("Failed to load token %r - removing broken/empty file.", tok) rm_tok = True @@ -254,7 +260,7 @@ class LoadAuth(object): return {} rm_tok = False - if tdata.get('expire', 0) < time.time(): + if tdata.get("expire", 0) < time.time(): # If expire isn't present in the token it's invalid and needs # to be removed. Also, if it's present and has expired - in # other words, the expiration is before right now, it should @@ -267,42 +273,44 @@ class LoadAuth(object): return tdata def list_tokens(self): - ''' + """ List all tokens in eauth_tokn storage. - ''' - return self.tokens["{0}.list_tokens".format(self.opts['eauth_tokens'])](self.opts) + """ + return self.tokens["{0}.list_tokens".format(self.opts["eauth_tokens"])]( + self.opts + ) def rm_token(self, tok): - ''' + """ Remove the given token from token storage. - ''' - self.tokens["{0}.rm_token".format(self.opts['eauth_tokens'])](self.opts, tok) + """ + self.tokens["{0}.rm_token".format(self.opts["eauth_tokens"])](self.opts, tok) def authenticate_token(self, load): - ''' + """ Authenticate a user by the token specified in load. Return the token object or False if auth failed. - ''' - token = self.get_tok(load['token']) + """ + token = self.get_tok(load["token"]) # Bail if the token is empty or if the eauth type specified is not allowed - if not token or token['eauth'] not in self.opts['external_auth']: + if not token or token["eauth"] not in self.opts["external_auth"]: log.warning('Authentication failure of type "token" occurred.') return False return token def authenticate_eauth(self, load): - ''' + """ Authenticate a user by the external auth module specified in load. Return True on success or False on failure. - ''' - if 'eauth' not in load: + """ + if "eauth" not in load: log.warning('Authentication failure of type "eauth" occurred.') return False - if load['eauth'] not in self.opts['external_auth']: - log.debug('The eauth system "%s" is not enabled', load['eauth']) + if load["eauth"] not in self.opts["external_auth"]: + log.debug('The eauth system "%s" is not enabled', load["eauth"]) log.warning('Authentication failure of type "eauth" occurred.') return False @@ -315,43 +323,50 @@ class LoadAuth(object): return True def authenticate_key(self, load, key): - ''' + """ Authenticate a user by the key passed in load. Return the effective user id (name) if it's different from the specified one (for sudo). If the effective user id is the same as the passed one, return True on success or False on failure. - ''' + """ error_msg = 'Authentication failure of type "user" occurred.' - auth_key = load.pop('key', None) + auth_key = load.pop("key", None) if auth_key is None: log.warning(error_msg) return False - if 'user' in load: - auth_user = AuthUser(load['user']) + if "user" in load: + auth_user = AuthUser(load["user"]) if auth_user.is_sudo(): # If someone sudos check to make sure there is no ACL's around their username - if auth_key != key[self.opts.get('user', 'root')]: + if auth_key != key[self.opts.get("user", "root")]: log.warning(error_msg) return False return auth_user.sudo_name() - elif load['user'] == self.opts.get('user', 'root') or load['user'] == 'root': - if auth_key != key[self.opts.get('user', 'root')]: + elif ( + load["user"] == self.opts.get("user", "root") or load["user"] == "root" + ): + if auth_key != key[self.opts.get("user", "root")]: + log.warning( + "Master runs as %r, but user in payload is %r", + self.opts.get("user", "root"), + load["user"], + ) log.warning(error_msg) return False elif auth_user.is_running_user(): - if auth_key != key.get(load['user']): + if auth_key != key.get(load["user"]): log.warning(error_msg) return False - elif auth_key == key.get('root'): + elif auth_key == key.get("root"): pass else: - if load['user'] in key: + if load["user"] in key: # User is authorised, check key and check perms - if auth_key != key[load['user']]: + if auth_key != key[load["user"]]: log.warning(error_msg) return False - return load['user'] + return load["user"] else: log.warning(error_msg) return False @@ -362,35 +377,35 @@ class LoadAuth(object): return True def get_auth_list(self, load, token=None): - ''' + """ Retrieve access list for the user specified in load. The list is built by eauth module or from master eauth configuration. Return None if current configuration doesn't provide any ACL for the user. Return an empty list if the user has no rights to execute anything on this master and returns non-empty list if user is allowed to execute particular functions. - ''' + """ # Get auth list from token - if token and self.opts['keep_acl_in_token'] and 'auth_list' in token: - return token['auth_list'] + if token and self.opts["keep_acl_in_token"] and "auth_list" in token: + return token["auth_list"] # Get acl from eauth module. auth_list = self.__get_acl(load) if auth_list is not None: return auth_list - eauth = token['eauth'] if token else load['eauth'] - if eauth not in self.opts['external_auth']: + eauth = token["eauth"] if token else load["eauth"] + if eauth not in self.opts["external_auth"]: # No matching module is allowed in config log.debug('The eauth system "%s" is not enabled', eauth) - log.warning('Authorization failure occurred.') + log.warning("Authorization failure occurred.") return None if token: - name = token['name'] - groups = token.get('groups') + name = token["name"] + groups = token.get("groups") else: name = self.load_name(load) # The username we are attempting to auth with groups = self.get_groups(load) # The groups this user belongs to - eauth_config = self.opts['external_auth'][eauth] + eauth_config = self.opts["external_auth"][eauth] if not eauth_config: log.debug('eauth "%s" configuration is empty', eauth) @@ -399,19 +414,16 @@ class LoadAuth(object): # We now have an authenticated session and it is time to determine # what the user has access to. - auth_list = self.ckminions.fill_auth_list( - eauth_config, - name, - groups) + auth_list = self.ckminions.fill_auth_list(eauth_config, name, groups) auth_list = self.__process_acl(load, auth_list) - log.trace('Compiled auth_list: %s', auth_list) + log.trace("Compiled auth_list: %s", auth_list) return auth_list def check_authentication(self, load, auth_type, key=None, show_username=False): - ''' + """ .. versionadded:: 2018.3.0 Go through various checks to see if the token/eauth/user can be authenticated. @@ -424,193 +436,208 @@ class LoadAuth(object): If an error is encountered, return immediately with the relevant error dictionary as authentication has failed. Otherwise, return the username and valid auth_list. - ''' + """ auth_list = [] - username = load.get('username', 'UNKNOWN') - ret = {'auth_list': auth_list, - 'username': username, - 'error': {}} + username = load.get("username", "UNKNOWN") + ret = {"auth_list": auth_list, "username": username, "error": {}} # Authenticate - if auth_type == 'token': + if auth_type == "token": token = self.authenticate_token(load) if not token: - ret['error'] = {'name': 'TokenAuthenticationError', - 'message': 'Authentication failure of type "token" occurred.'} + ret["error"] = { + "name": "TokenAuthenticationError", + "message": 'Authentication failure of type "token" occurred.', + } return ret # Update username for token - username = token['name'] - ret['username'] = username + username = token["name"] + ret["username"] = username auth_list = self.get_auth_list(load, token=token) - elif auth_type == 'eauth': + elif auth_type == "eauth": if not self.authenticate_eauth(load): - ret['error'] = {'name': 'EauthAuthenticationError', - 'message': 'Authentication failure of type "eauth" occurred for ' - 'user {0}.'.format(username)} + ret["error"] = { + "name": "EauthAuthenticationError", + "message": 'Authentication failure of type "eauth" occurred for ' + "user {0}.".format(username), + } return ret auth_list = self.get_auth_list(load) - elif auth_type == 'user': + elif auth_type == "user": auth_ret = self.authenticate_key(load, key) 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) - ret['error'] = {'name': 'UserAuthenticationError', 'message': msg} + msg = "{0} for user {1}.".format(msg, username) + ret["error"] = {"name": "UserAuthenticationError", "message": msg} return ret # Verify that the caller has root on master if auth_ret is not True: - if AuthUser(load['user']).is_sudo(): - if not self.opts['sudo_acl'] or not self.opts['publisher_acl']: + if AuthUser(load["user"]).is_sudo(): + if not self.opts["sudo_acl"] or not self.opts["publisher_acl"]: auth_ret = True if auth_ret is not True: # Avoid a circular import import salt.utils.master + auth_list = salt.utils.master.get_values_of_matching_keys( - self.opts['publisher_acl'], auth_ret) + self.opts["publisher_acl"], auth_ret + ) if not auth_list: - ret['error'] = {'name': 'UserAuthenticationError', 'message': msg} + ret["error"] = {"name": "UserAuthenticationError", "message": msg} return ret else: - ret['error'] = {'name': 'SaltInvocationError', - 'message': 'Authentication type not supported.'} + ret["error"] = { + "name": "SaltInvocationError", + "message": "Authentication type not supported.", + } return ret # Authentication checks passed - ret['auth_list'] = auth_list + ret["auth_list"] = auth_list return ret class Resolver(object): - ''' + """ The class used to resolve options for the command line and for generic interactive interfaces - ''' + """ + def __init__(self, opts): self.opts = opts self.auth = salt.loader.auth(opts) 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']) + master_uri = "tcp://{}:{}".format( + salt.utils.zeromq.ip_bracket(self.opts["interface"]), + six.text_type(self.opts["ret_port"]), ) - with salt.transport.client.ReqChannel.factory(self.opts, - crypt='clear', - master_uri=master_uri) as channel: + with salt.transport.client.ReqChannel.factory( + self.opts, crypt="clear", master_uri=master_uri + ) as channel: return channel.send(load) def cli(self, eauth): - ''' + """ Execute the CLI options to fill in the extra data needed for the defined eauth system - ''' + """ ret = {} if not eauth: - print('External authentication system has not been specified') + print("External authentication system has not been specified") return ret - fstr = '{0}.auth'.format(eauth) + fstr = "{0}.auth".format(eauth) if fstr not in self.auth: - print(('The specified external authentication system "{0}" is ' - 'not available').format(eauth)) - print("Available eauth types: {0}".format(", ".join(self.auth.file_mapping.keys()))) + print( + ( + 'The specified external authentication system "{0}" is ' + "not available" + ).format(eauth) + ) + print( + "Available eauth types: {0}".format( + ", ".join(self.auth.file_mapping.keys()) + ) + ) return ret args = salt.utils.args.arg_lookup(self.auth[fstr]) - for arg in args['args']: + for arg in args["args"]: if arg in self.opts: ret[arg] = self.opts[arg] - elif arg.startswith('pass'): - ret[arg] = getpass.getpass('{0}: '.format(arg)) + elif arg.startswith("pass"): + ret[arg] = getpass.getpass("{0}: ".format(arg)) else: - ret[arg] = input('{0}: '.format(arg)) - for kwarg, default in list(args['kwargs'].items()): + ret[arg] = input("{0}: ".format(arg)) + for kwarg, default in list(args["kwargs"].items()): if kwarg in self.opts: - ret['kwarg'] = self.opts[kwarg] + ret["kwarg"] = self.opts[kwarg] else: - ret[kwarg] = input('{0} [{1}]: '.format(kwarg, default)) + ret[kwarg] = input("{0} [{1}]: ".format(kwarg, default)) # Use current user if empty - if 'username' in ret and not ret['username']: - ret['username'] = salt.utils.user.get_user() + if "username" in ret and not ret["username"]: + ret["username"] = salt.utils.user.get_user() return ret def token_cli(self, eauth, load): - ''' + """ Create the token from the CLI and request the correct data to authenticate via the passed authentication mechanism - ''' - load['cmd'] = 'mk_token' - load['eauth'] = eauth + """ + load["cmd"] = "mk_token" + load["eauth"] = eauth tdata = self._send_token_request(load) - if 'token' not in tdata: + if "token" not in tdata: return tdata try: with salt.utils.files.set_umask(0o177): - with salt.utils.files.fopen(self.opts['token_file'], 'w+') as fp_: - fp_.write(tdata['token']) + with salt.utils.files.fopen(self.opts["token_file"], "w+") as fp_: + fp_.write(tdata["token"]) except (IOError, OSError): pass return tdata def mk_token(self, load): - ''' + """ Request a token from the master - ''' - load['cmd'] = 'mk_token' + """ + load["cmd"] = "mk_token" tdata = self._send_token_request(load) return tdata def get_token(self, token): - ''' + """ Request a token from the master - ''' + """ load = {} - load['token'] = token - load['cmd'] = 'get_token' + load["token"] = token + load["cmd"] = "get_token" tdata = self._send_token_request(load) return tdata class AuthUser(object): - ''' + """ Represents a user requesting authentication to the salt master - ''' + """ def __init__(self, user): - ''' + """ Instantiate an AuthUser object. Takes a user to reprsent, as a string. - ''' + """ self.user = user def is_sudo(self): - ''' + """ Determines if the user is running with sudo Returns True if the user is running with sudo and False if the user is not running with sudo - ''' - return self.user.startswith('sudo_') + """ + return self.user.startswith("sudo_") def is_running_user(self): - ''' + """ Determines if the user is the same user as the one running this process Returns True if the user is the same user as the one running this process and False if not. - ''' + """ return self.user == salt.utils.user.get_user() def sudo_name(self): - ''' + """ Returns the username of the sudoer, i.e. self.user without the 'sudo_' prefix. - ''' - return self.user.split('_', 1)[-1] + """ + return self.user.split("_", 1)[-1] diff --git a/salt/auth/auto.py b/salt/auth/auto.py index f2cbe7e15b4..080f3f6e9ca 100644 --- a/salt/auth/auto.py +++ b/salt/auth/auto.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" An "Always Approved" eauth interface to test against, not intended for production use -''' +""" def auth(username, password): # pylint: disable=unused-argument - ''' + """ Authenticate! - ''' + """ return True diff --git a/salt/auth/django.py b/salt/auth/django.py index d51882380b7..915513fbc64 100644 --- a/salt/auth/django.py +++ b/salt/auth/django.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Provide authentication using Django Web Framework :depends: - Django Web Framework @@ -45,26 +45,28 @@ When a user attempts to authenticate via Django, Salt will import the package indicated via the keyword ``^model``. That model must have the fields indicated above, though the model DOES NOT have to be named 'SaltExternalAuthModel'. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import sys - # Import 3rd-party libs from salt.ext import six + # pylint: disable=import-error try: import django from django.db import connection # pylint: disable=no-name-in-module + HAS_DJANGO = True except Exception as exc: # pylint: disable=broad-except # If Django is installed and is not detected, uncomment # the following line to display additional information - #log.warning('Could not load Django auth module. Found exception: %s', exc) + # log.warning('Could not load Django auth module. Found exception: %s', exc) HAS_DJANGO = False # pylint: enable=import-error @@ -72,7 +74,7 @@ DJANGO_AUTH_CLASS = None log = logging.getLogger(__name__) -__virtualname__ = 'django' +__virtualname__ = "django" def __virtual__(): @@ -91,9 +93,9 @@ def is_connection_usable(): def __django_auth_setup(): - ''' + """ Prepare the connection to the Django authentication framework - ''' + """ if django.VERSION >= (1, 7): django.setup() @@ -106,24 +108,28 @@ def __django_auth_setup(): # they are needed. When using framework facilities outside the # web application container we need to run django.setup() to # get the model definitions cached. - if '^model' in __opts__['external_auth']['django']: - django_model_fullname = __opts__['external_auth']['django']['^model'] - django_model_name = django_model_fullname.split('.')[-1] - django_module_name = '.'.join(django_model_fullname.split('.')[0:-1]) + if "^model" in __opts__["external_auth"]["django"]: + django_model_fullname = __opts__["external_auth"]["django"]["^model"] + django_model_name = django_model_fullname.split(".")[-1] + django_module_name = ".".join(django_model_fullname.split(".")[0:-1]) - django_auth_module = __import__(django_module_name, globals(), locals(), 'SaltExternalAuthModel') - DJANGO_AUTH_CLASS_str = 'django_auth_module.{0}'.format(django_model_name) + # pylint: disable=possibly-unused-variable + django_auth_module = __import__( + django_module_name, globals(), locals(), "SaltExternalAuthModel" + ) + # pylint: enable=possibly-unused-variable + DJANGO_AUTH_CLASS_str = "django_auth_module.{0}".format(django_model_name) DJANGO_AUTH_CLASS = eval(DJANGO_AUTH_CLASS_str) # pylint: disable=W0123 def auth(username, password): - ''' + """ Simple Django auth - ''' - django_auth_path = __opts__['django_auth_path'] + """ + django_auth_path = __opts__["django_auth_path"] if django_auth_path not in sys.path: sys.path.append(django_auth_path) - os.environ.setdefault('DJANGO_SETTINGS_MODULE', __opts__['django_auth_settings']) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", __opts__["django_auth_settings"]) __django_auth_setup() @@ -131,21 +137,24 @@ def auth(username, password): connection.close() import django.contrib.auth # pylint: disable=import-error,3rd-party-module-not-gated,no-name-in-module + user = django.contrib.auth.authenticate(username=username, password=password) if user is not None: if user.is_active: - log.debug('Django authentication successful') + log.debug("Django authentication successful") return True else: - log.debug('Django authentication: the password is valid but the account is disabled.') + log.debug( + "Django authentication: the password is valid but the account is disabled." + ) else: - log.debug('Django authentication failed.') + log.debug("Django authentication failed.") return False def acl(username): - ''' + """ :param username: Username to filter for :return: Dictionary that can be slotted into the ``__opts__`` structure for @@ -181,7 +190,7 @@ def acl(username): - server1: - .* - ''' + """ __django_auth_setup() if username is None: @@ -203,10 +212,14 @@ def acl(username): for d in auth_dict[a.user_fk.username]: if isinstance(d, dict): if a.minion_or_fn_matcher in six.iterkeys(d): - auth_dict[a.user_fk.username][a.minion_or_fn_matcher].append(a.minion_fn) + auth_dict[a.user_fk.username][a.minion_or_fn_matcher].append( + a.minion_fn + ) found = True if not found: - auth_dict[a.user_fk.username].append({a.minion_or_fn_matcher: [a.minion_fn]}) + auth_dict[a.user_fk.username].append( + {a.minion_or_fn_matcher: [a.minion_fn]} + ) - log.debug('django auth_dict is %s', auth_dict) + log.debug("django auth_dict is %s", auth_dict) return auth_dict diff --git a/salt/auth/file.py b/salt/auth/file.py index 89715f5c6c9..2ac4d2543d0 100644 --- a/salt/auth/file.py +++ b/salt/auth/file.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Provide authentication using local files .. versionadded:: 2018.3.0 @@ -93,10 +93,11 @@ When using ``htdigest`` the ``^realm`` must be set: cory: - .* -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -106,7 +107,7 @@ import salt.utils.versions log = logging.getLogger(__name__) -__virtualname__ = 'file' +__virtualname__ = "file" def __virtual__(): @@ -114,75 +115,86 @@ def __virtual__(): def _get_file_auth_config(): - ''' + """ Setup defaults and check configuration variables for auth backends - ''' + """ config = { - 'filetype': 'text', - 'hashtype': 'plaintext', - 'field_separator': ':', - 'username_field': 1, - 'password_field': 2, + "filetype": "text", + "hashtype": "plaintext", + "field_separator": ":", + "username_field": 1, + "password_field": 2, } - for opt in __opts__['external_auth'][__virtualname__]: - if opt.startswith('^'): - config[opt[1:]] = __opts__['external_auth'][__virtualname__][opt] + for opt in __opts__["external_auth"][__virtualname__]: + if opt.startswith("^"): + config[opt[1:]] = __opts__["external_auth"][__virtualname__][opt] - if 'filename' not in config: - log.error('salt.auth.file: An authentication file must be specified ' - 'via external_auth:file:^filename') + if "filename" not in config: + log.error( + "salt.auth.file: An authentication file must be specified " + "via external_auth:file:^filename" + ) return False - if not os.path.exists(config['filename']): - log.error('salt.auth.file: The configured external_auth:file:^filename (%s)' - 'does not exist on the filesystem', config['filename']) + if not os.path.exists(config["filename"]): + log.error( + "salt.auth.file: The configured external_auth:file:^filename (%s)" + "does not exist on the filesystem", + config["filename"], + ) return False - config['username_field'] = int(config['username_field']) - config['password_field'] = int(config['password_field']) + config["username_field"] = int(config["username_field"]) + config["password_field"] = int(config["password_field"]) return config def _text(username, password, **kwargs): - ''' + """ The text file function can authenticate plaintext and digest methods that are available in the :py:func:`hashutil.digest ` function. - ''' + """ - filename = kwargs['filename'] - hashtype = kwargs['hashtype'] - field_separator = kwargs['field_separator'] - username_field = kwargs['username_field']-1 - password_field = kwargs['password_field']-1 + filename = kwargs["filename"] + hashtype = kwargs["hashtype"] + field_separator = kwargs["field_separator"] + username_field = kwargs["username_field"] - 1 + password_field = kwargs["password_field"] - 1 - with salt.utils.files.fopen(filename, 'r') as pwfile: + with salt.utils.files.fopen(filename, "r") as pwfile: for line in pwfile.readlines(): fields = line.strip().split(field_separator) try: this_username = fields[username_field] except IndexError: - log.error('salt.auth.file: username field (%s) does not exist ' - 'in file %s', username_field, filename) + log.error( + "salt.auth.file: username field (%s) does not exist " "in file %s", + username_field, + filename, + ) return False try: this_password = fields[password_field] except IndexError: - log.error('salt.auth.file: password field (%s) does not exist ' - 'in file %s', password_field, filename) + log.error( + "salt.auth.file: password field (%s) does not exist " "in file %s", + password_field, + filename, + ) return False if this_username == username: - if hashtype == 'plaintext': + if hashtype == "plaintext": if this_password == password: return True else: # Exceptions for unknown hash types will be raised by hashutil.digest - if this_password == __salt__['hashutil.digest'](password, hashtype): + if this_password == __salt__["hashutil.digest"](password, hashtype): return True # Short circuit if we've already found the user but the password was wrong @@ -191,73 +203,74 @@ def _text(username, password, **kwargs): def _htpasswd(username, password, **kwargs): - ''' + """ Provide authentication via Apache-style htpasswd files - ''' + """ from passlib.apache import HtpasswdFile - pwfile = HtpasswdFile(kwargs['filename']) + pwfile = HtpasswdFile(kwargs["filename"]) # passlib below version 1.6 uses 'verify' function instead of 'check_password' - if salt.utils.versions.version_cmp(kwargs['passlib_version'], '1.6') < 0: + if salt.utils.versions.version_cmp(kwargs["passlib_version"], "1.6") < 0: return pwfile.verify(username, password) else: return pwfile.check_password(username, password) def _htdigest(username, password, **kwargs): - ''' + """ Provide authentication via Apache-style htdigest files - ''' + """ - realm = kwargs.get('realm', None) + realm = kwargs.get("realm", None) if not realm: - log.error('salt.auth.file: A ^realm must be defined in ' - 'external_auth:file for htdigest filetype') + log.error( + "salt.auth.file: A ^realm must be defined in " + "external_auth:file for htdigest filetype" + ) return False from passlib.apache import HtdigestFile - pwfile = HtdigestFile(kwargs['filename']) + pwfile = HtdigestFile(kwargs["filename"]) # passlib below version 1.6 uses 'verify' function instead of 'check_password' - if salt.utils.versions.version_cmp(kwargs['passlib_version'], '1.6') < 0: + if salt.utils.versions.version_cmp(kwargs["passlib_version"], "1.6") < 0: return pwfile.verify(username, realm, password) else: return pwfile.check_password(username, realm, password) def _htfile(username, password, **kwargs): - ''' + """ Gate function for _htpasswd and _htdigest authentication backends - ''' + """ - filetype = kwargs.get('filetype', 'htpasswd').lower() + filetype = kwargs.get("filetype", "htpasswd").lower() try: import passlib - kwargs['passlib_version'] = passlib.__version__ + + kwargs["passlib_version"] = passlib.__version__ except ImportError: - log.error('salt.auth.file: The python-passlib library is required ' - 'for %s filetype', filetype) + log.error( + "salt.auth.file: The python-passlib library is required " "for %s filetype", + filetype, + ) return False - if filetype == 'htdigest': + if filetype == "htdigest": return _htdigest(username, password, **kwargs) else: return _htpasswd(username, password, **kwargs) -FILETYPE_FUNCTION_MAP = { - 'text': _text, - 'htpasswd': _htfile, - 'htdigest': _htfile -} +FILETYPE_FUNCTION_MAP = {"text": _text, "htpasswd": _htfile, "htdigest": _htfile} def auth(username, password): - ''' + """ File based authentication ^filename @@ -296,13 +309,13 @@ def auth(username, password): numbering beginning at 1 (one). Default: ``2`` - ''' + """ config = _get_file_auth_config() if not config: return False - auth_function = FILETYPE_FUNCTION_MAP.get(config['filetype'], 'text') + auth_function = FILETYPE_FUNCTION_MAP.get(config["filetype"], "text") return auth_function(username, password, **config) diff --git a/salt/auth/keystone.py b/salt/auth/keystone.py index eb53e7ad852..6e44f282baa 100644 --- a/salt/auth/keystone.py +++ b/salt/auth/keystone.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Provide authentication using OpenStack Keystone :depends: - keystoneclient Python module -''' +""" from __future__ import absolute_import, print_function, unicode_literals + try: from keystoneclient.v2_0 import client from keystoneclient.exceptions import AuthorizationFailure, Unauthorized @@ -14,30 +15,31 @@ except ImportError: def get_auth_url(): - ''' + """ Try and get the URL from the config, else return localhost - ''' + """ try: - return __opts__['keystone.auth_url'] + return __opts__["keystone.auth_url"] except KeyError: - return 'http://localhost:35357/v2.0' + return "http://localhost:35357/v2.0" def auth(username, password): - ''' + """ Try and authenticate - ''' + """ try: - keystone = client.Client(username=username, password=password, - auth_url=get_auth_url()) + keystone = client.Client( + username=username, password=password, auth_url=get_auth_url() + ) return keystone.authenticate() except (AuthorizationFailure, Unauthorized): return False -if __name__ == '__main__': +if __name__ == "__main__": __opts__ = {} - if auth('test', 'test'): + if auth("test", "test"): print("Authenticated") else: print("Failed to authenticate") diff --git a/salt/auth/ldap.py b/salt/auth/ldap.py index 44afb5d7475..1ccd06001df 100644 --- a/salt/auth/ldap.py +++ b/salt/auth/ldap.py @@ -1,97 +1,113 @@ # -*- coding: utf-8 -*- -''' +""" Provide authentication using simple LDAP binds :depends: - ldap Python module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import logging + import itertools -from salt.ext import six +import logging + +import salt.utils.data +import salt.utils.stringutils + +# Import third party libs +from jinja2 import Environment # Import salt libs from salt.exceptions import CommandExecutionError, SaltInvocationError -import salt.utils.stringutils -import salt.utils.data +from salt.ext import six log = logging.getLogger(__name__) -# Import third party libs -from jinja2 import Environment + try: # pylint: disable=no-name-in-module import ldap import ldap.modlist import ldap.filter + HAS_LDAP = True # pylint: enable=no-name-in-module except ImportError: HAS_LDAP = False # Defaults, override in master config -__defopts__ = {'auth.ldap.basedn': '', - 'auth.ldap.uri': '', - 'auth.ldap.server': 'localhost', - 'auth.ldap.port': '389', - 'auth.ldap.starttls': False, - 'auth.ldap.tls': False, - 'auth.ldap.no_verify': False, - 'auth.ldap.anonymous': False, - 'auth.ldap.scope': 2, - 'auth.ldap.groupou': 'Groups', - 'auth.ldap.accountattributename': 'memberUid', - 'auth.ldap.groupattribute': 'memberOf', - 'auth.ldap.persontype': 'person', - 'auth.ldap.groupclass': 'posixGroup', - 'auth.ldap.activedirectory': False, - 'auth.ldap.freeipa': False, - 'auth.ldap.minion_stripdomains': [], - } +__defopts__ = { + "auth.ldap.basedn": "", + "auth.ldap.uri": "", + "auth.ldap.server": "localhost", + "auth.ldap.port": "389", + "auth.ldap.starttls": False, + "auth.ldap.tls": False, + "auth.ldap.no_verify": False, + "auth.ldap.anonymous": False, + "auth.ldap.scope": 2, + "auth.ldap.groupou": "Groups", + "auth.ldap.accountattributename": "memberUid", + "auth.ldap.groupattribute": "memberOf", + "auth.ldap.persontype": "person", + "auth.ldap.groupclass": "posixGroup", + "auth.ldap.activedirectory": False, + "auth.ldap.freeipa": False, + "auth.ldap.minion_stripdomains": [], +} def _config(key, mandatory=True, opts=None): - ''' + """ Return a value for 'name' from master config file options or defaults. - ''' + """ try: if opts: - value = opts['auth.ldap.{0}'.format(key)] + value = opts["auth.ldap.{0}".format(key)] else: - value = __opts__['auth.ldap.{0}'.format(key)] + value = __opts__["auth.ldap.{0}".format(key)] except KeyError: try: - value = __defopts__['auth.ldap.{0}'.format(key)] + value = __defopts__["auth.ldap.{0}".format(key)] except KeyError: if mandatory: - msg = 'missing auth.ldap.{0} in master config'.format(key) + msg = "missing auth.ldap.{0} in master config".format(key) raise SaltInvocationError(msg) return False return value def _render_template(param, username): - ''' + """ Render config template, substituting username where found. - ''' + """ env = Environment() template = env.from_string(param) - variables = {'username': username} + variables = {"username": username} return template.render(variables) class _LDAPConnection(object): - ''' + """ Setup an LDAP connection. - ''' + """ - def __init__(self, uri, server, port, - starttls, tls, no_verify, - binddn, bindpw, - anonymous, accountattributename, activedirectory=False): - ''' + def __init__( + self, + uri, + server, + port, + starttls, + tls, + no_verify, + binddn, + bindpw, + anonymous, + accountattributename, + activedirectory=False, + ): + """ Bind to an LDAP directory using passed credentials. - ''' + """ self.uri = uri self.server = server self.port = port @@ -101,151 +117,180 @@ class _LDAPConnection(object): self.bindpw = bindpw if not HAS_LDAP: raise CommandExecutionError( - 'LDAP connection could not be made, the python-ldap module is ' - 'not installed. Install python-ldap to use LDAP external auth.' + "LDAP connection could not be made, the python-ldap module is " + "not installed. Install python-ldap to use LDAP external auth." ) if self.starttls and self.tls: raise CommandExecutionError( - 'Cannot bind with both starttls and tls enabled.' - 'Please enable only one of the protocols' + "Cannot bind with both starttls and tls enabled." + "Please enable only one of the protocols" ) - schema = 'ldaps' if tls else 'ldap' - if self.uri == '': - self.uri = '{0}://{1}:{2}'.format(schema, self.server, self.port) + schema = "ldaps" if tls else "ldap" + if self.uri == "": + self.uri = "{0}://{1}:{2}".format(schema, self.server, self.port) try: if no_verify: - ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, - ldap.OPT_X_TLS_NEVER) + ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) - self.ldap = ldap.initialize('{0}'.format(self.uri)) + self.ldap = ldap.initialize("{0}".format(self.uri)) self.ldap.protocol_version = 3 # ldap.VERSION3 self.ldap.set_option(ldap.OPT_REFERRALS, 0) # Needed for AD if not anonymous: if self.bindpw is None or len(self.bindpw) < 1: raise CommandExecutionError( - 'LDAP bind password is not set: password cannot be empty if auth.ldap.anonymous is False' + "LDAP bind password is not set: password cannot be empty if auth.ldap.anonymous is False" ) if self.starttls: self.ldap.start_tls_s() self.ldap.simple_bind_s(self.binddn, self.bindpw) except Exception as ldap_error: # pylint: disable=broad-except raise CommandExecutionError( - 'Failed to bind to LDAP server {0} as {1}: {2}'.format( + "Failed to bind to LDAP server {0} as {1}: {2}".format( self.uri, self.binddn, ldap_error ) ) def _bind_for_search(anonymous=False, opts=None): - ''' + """ Bind with binddn and bindpw only for searching LDAP :param anonymous: Try binding anonymously :param opts: Pass in when __opts__ is not available :return: LDAPConnection object - ''' + """ # Get config params; create connection dictionary connargs = {} # config params (auth.ldap.*) params = { - 'mandatory': ['uri', 'server', 'port', 'starttls', 'tls', - 'no_verify', 'anonymous', - 'accountattributename', 'activedirectory'], - 'additional': ['binddn', 'bindpw', 'filter', 'groupclass', - 'auth_by_group_membership_only'], + "mandatory": [ + "uri", + "server", + "port", + "starttls", + "tls", + "no_verify", + "anonymous", + "accountattributename", + "activedirectory", + ], + "additional": [ + "binddn", + "bindpw", + "filter", + "groupclass", + "auth_by_group_membership_only", + ], } paramvalues = {} - for param in params['mandatory']: + for param in params["mandatory"]: paramvalues[param] = _config(param, opts=opts) - for param in params['additional']: + for param in params["additional"]: paramvalues[param] = _config(param, mandatory=False, opts=opts) - paramvalues['anonymous'] = anonymous + paramvalues["anonymous"] = anonymous # Only add binddn/bindpw to the connargs when they're set, as they're not # mandatory for initializing the LDAP object, but if they're provided # initially, a bind attempt will be done during the initialization to # validate them - if paramvalues['binddn']: - connargs['binddn'] = paramvalues['binddn'] - if paramvalues['bindpw']: - params['mandatory'].append('bindpw') + if paramvalues["binddn"]: + connargs["binddn"] = paramvalues["binddn"] + if paramvalues["bindpw"]: + params["mandatory"].append("bindpw") - for name in params['mandatory']: + for name in params["mandatory"]: connargs[name] = paramvalues[name] - if not paramvalues['anonymous']: - if paramvalues['binddn'] and paramvalues['bindpw']: + if not paramvalues["anonymous"]: + if paramvalues["binddn"] and paramvalues["bindpw"]: # search for the user's DN to be used for the actual authentication return _LDAPConnection(**connargs).ldap def _bind(username, password, anonymous=False, opts=None): - ''' + """ Authenticate via an LDAP bind - ''' + """ # Get config params; create connection dictionary - basedn = _config('basedn', opts=opts) - scope = _config('scope', opts=opts) + basedn = _config("basedn", opts=opts) + scope = _config("scope", opts=opts) connargs = {} # config params (auth.ldap.*) params = { - 'mandatory': ['uri', 'server', 'port', 'starttls', 'tls', - 'no_verify', 'anonymous', - 'accountattributename', 'activedirectory'], - 'additional': ['binddn', 'bindpw', 'filter', 'groupclass', - 'auth_by_group_membership_only'], + "mandatory": [ + "uri", + "server", + "port", + "starttls", + "tls", + "no_verify", + "anonymous", + "accountattributename", + "activedirectory", + ], + "additional": [ + "binddn", + "bindpw", + "filter", + "groupclass", + "auth_by_group_membership_only", + ], } paramvalues = {} - for param in params['mandatory']: + for param in params["mandatory"]: paramvalues[param] = _config(param, opts=opts) - for param in params['additional']: + for param in params["additional"]: paramvalues[param] = _config(param, mandatory=False, opts=opts) - paramvalues['anonymous'] = anonymous - if paramvalues['binddn']: + paramvalues["anonymous"] = anonymous + if paramvalues["binddn"]: # the binddn can also be composited, e.g. # - {{ username }}@domain.com # - cn={{ username }},ou=users,dc=company,dc=tld # so make sure to render it first before using it - paramvalues['binddn'] = _render_template(paramvalues['binddn'], username) - paramvalues['binddn'] = ldap.filter.escape_filter_chars(paramvalues['binddn']) + paramvalues["binddn"] = _render_template(paramvalues["binddn"], username) + paramvalues["binddn"] = ldap.filter.escape_filter_chars(paramvalues["binddn"]) - if paramvalues['filter']: + if paramvalues["filter"]: escaped_username = ldap.filter.escape_filter_chars(username) - paramvalues['filter'] = _render_template(paramvalues['filter'], escaped_username) + paramvalues["filter"] = _render_template( + paramvalues["filter"], escaped_username + ) # Only add binddn/bindpw to the connargs when they're set, as they're not # mandatory for initializing the LDAP object, but if they're provided # initially, a bind attempt will be done during the initialization to # validate them - if paramvalues['binddn']: - connargs['binddn'] = paramvalues['binddn'] - if paramvalues['bindpw']: - params['mandatory'].append('bindpw') + if paramvalues["binddn"]: + connargs["binddn"] = paramvalues["binddn"] + if paramvalues["bindpw"]: + params["mandatory"].append("bindpw") - for name in params['mandatory']: + for name in params["mandatory"]: connargs[name] = paramvalues[name] - if not paramvalues['anonymous']: - if paramvalues['binddn'] and paramvalues['bindpw']: + if not paramvalues["anonymous"]: + if paramvalues["binddn"] and paramvalues["bindpw"]: # search for the user's DN to be used for the actual authentication _ldap = _LDAPConnection(**connargs).ldap log.debug( - 'Running LDAP user dn search with filter:%s, dn:%s, ' - 'scope:%s', paramvalues['filter'], basedn, scope + "Running LDAP user dn search with filter:%s, dn:%s, " "scope:%s", + paramvalues["filter"], + basedn, + scope, ) - result = _ldap.search_s(basedn, int(scope), paramvalues['filter']) + result = _ldap.search_s(basedn, int(scope), paramvalues["filter"]) if len(result) < 1: - log.warning('Unable to find user %s', username) + log.warning("Unable to find user %s", username) return False elif len(result) > 1: # Active Directory returns something odd. Though we do not @@ -261,71 +306,81 @@ def _bind(username, password, anonymous=False, opts=None): cns = [tup[0] for tup in result] total_not_none = sum(1 for c in cns if c is not None) if total_not_none > 1: - log.error('LDAP lookup found multiple results for user %s', username) + log.error( + "LDAP lookup found multiple results for user %s", username + ) return False elif total_not_none == 0: - log.error('LDAP lookup--unable to find CN matching user %s', username) + log.error( + "LDAP lookup--unable to find CN matching user %s", username + ) return False - connargs['binddn'] = result[0][0] - if paramvalues['binddn'] and not paramvalues['bindpw']: - connargs['binddn'] = paramvalues['binddn'] - elif paramvalues['binddn'] and not paramvalues['bindpw']: - connargs['binddn'] = paramvalues['binddn'] + connargs["binddn"] = result[0][0] + if paramvalues["binddn"] and not paramvalues["bindpw"]: + connargs["binddn"] = paramvalues["binddn"] + elif paramvalues["binddn"] and not paramvalues["bindpw"]: + connargs["binddn"] = paramvalues["binddn"] # Update connection dictionary with the user's password - connargs['bindpw'] = password + connargs["bindpw"] = password # Attempt bind with user dn and password - if paramvalues['anonymous']: - log.debug('Attempting anonymous LDAP bind') + if paramvalues["anonymous"]: + log.debug("Attempting anonymous LDAP bind") else: - log.debug('Attempting LDAP bind with user dn: %s', connargs['binddn']) + log.debug("Attempting LDAP bind with user dn: %s", connargs["binddn"]) try: ldap_conn = _LDAPConnection(**connargs).ldap except Exception: # pylint: disable=broad-except - connargs.pop('bindpw', None) # Don't log the password - log.error('Failed to authenticate user dn via LDAP: %s', connargs) - log.debug('Error authenticating user dn via LDAP:', exc_info=True) + connargs.pop("bindpw", None) # Don't log the password + log.error("Failed to authenticate user dn via LDAP: %s", connargs) + log.debug("Error authenticating user dn via LDAP:", exc_info=True) return False - log.debug('Successfully authenticated user dn via LDAP: %s', connargs['binddn']) + log.debug("Successfully authenticated user dn via LDAP: %s", connargs["binddn"]) return ldap_conn def auth(username, password): - ''' + """ Simple LDAP auth - ''' + """ if not HAS_LDAP: - log.error('LDAP authentication requires python-ldap module') + log.error("LDAP authentication requires python-ldap module") return False bind = None # If bind credentials are configured, verify that we receive a valid bind - if _config('binddn', mandatory=False) and _config('bindpw', mandatory=False): - search_bind = _bind_for_search(anonymous=_config('anonymous', mandatory=False)) + if _config("binddn", mandatory=False) and _config("bindpw", mandatory=False): + search_bind = _bind_for_search(anonymous=_config("anonymous", mandatory=False)) # If username & password are not None, attempt to verify they are valid if search_bind and username and password: - bind = _bind(username, password, - anonymous=_config('auth_by_group_membership_only', mandatory=False) - and _config('anonymous', mandatory=False)) + bind = _bind( + username, + password, + anonymous=_config("auth_by_group_membership_only", mandatory=False) + and _config("anonymous", mandatory=False), + ) else: - bind = _bind(username, password, - anonymous=_config('auth_by_group_membership_only', mandatory=False) - and _config('anonymous', mandatory=False)) + bind = _bind( + username, + password, + anonymous=_config("auth_by_group_membership_only", mandatory=False) + and _config("anonymous", mandatory=False), + ) if bind: - log.debug('LDAP authentication successful') + log.debug("LDAP authentication successful") return bind - log.error('LDAP _bind authentication FAILED') + log.error("LDAP _bind authentication FAILED") return False def groups(username, **kwargs): - ''' + """ Authenticate against an LDAP group Behavior is highly dependent on if Active Directory is in use. @@ -338,108 +393,157 @@ def groups(username, **kwargs): and returns members of those groups. Then we check against the username entered. - ''' + """ group_list = [] # If bind credentials are configured, use them instead of user's - if _config('binddn', mandatory=False) and _config('bindpw', mandatory=False): - bind = _bind_for_search(anonymous=_config('anonymous', mandatory=False)) + if _config("binddn", mandatory=False) and _config("bindpw", mandatory=False): + bind = _bind_for_search(anonymous=_config("anonymous", mandatory=False)) else: - bind = _bind(username, kwargs.get('password', ''), - anonymous=_config('auth_by_group_membership_only', mandatory=False) - and _config('anonymous', mandatory=False)) + bind = _bind( + username, + kwargs.get("password", ""), + anonymous=_config("auth_by_group_membership_only", mandatory=False) + and _config("anonymous", mandatory=False), + ) if bind: - log.debug('ldap bind to determine group membership succeeded!') + log.debug("ldap bind to determine group membership succeeded!") - if _config('activedirectory'): + if _config("activedirectory"): try: - get_user_dn_search = '(&({0}={1})(objectClass={2}))'.format(_config('accountattributename'), - username, - _config('persontype')) - user_dn_results = bind.search_s(_config('basedn'), - ldap.SCOPE_SUBTREE, - get_user_dn_search, [str('distinguishedName')]) # future lint: disable=blacklisted-function + get_user_dn_search = "(&({0}={1})(objectClass={2}))".format( + _config("accountattributename"), username, _config("persontype") + ) + user_dn_results = bind.search_s( + _config("basedn"), + ldap.SCOPE_SUBTREE, + get_user_dn_search, + [str("distinguishedName")], + ) # future lint: disable=blacklisted-function except Exception as e: # pylint: disable=broad-except - log.error('Exception thrown while looking up user DN in AD: %s', e) + log.error("Exception thrown while looking up user DN in AD: %s", e) return group_list if not user_dn_results: - log.error('Could not get distinguished name for user %s', username) + log.error("Could not get distinguished name for user %s", username) return group_list # LDAP results are always tuples. First entry in the tuple is the DN dn = ldap.filter.escape_filter_chars(user_dn_results[0][0]) - ldap_search_string = '(&(member={0})(objectClass={1}))'.format(dn, _config('groupclass')) - log.debug('Running LDAP group membership search: %s', ldap_search_string) + ldap_search_string = "(&(member={0})(objectClass={1}))".format( + dn, _config("groupclass") + ) + log.debug("Running LDAP group membership search: %s", ldap_search_string) try: - search_results = bind.search_s(_config('basedn'), - ldap.SCOPE_SUBTREE, - ldap_search_string, - [salt.utils.stringutils.to_str(_config('accountattributename')), str('cn')]) # future lint: disable=blacklisted-function + search_results = bind.search_s( + _config("basedn"), + ldap.SCOPE_SUBTREE, + ldap_search_string, + [ + salt.utils.stringutils.to_str(_config("accountattributename")), + str("cn"), + ], + ) # future lint: disable=blacklisted-function except Exception as e: # pylint: disable=broad-except - log.error('Exception thrown while retrieving group membership in AD: %s', e) + log.error( + "Exception thrown while retrieving group membership in AD: %s", e + ) return group_list for _, entry in search_results: - if 'cn' in entry: - group_list.append(salt.utils.stringutils.to_unicode(entry['cn'][0])) - log.debug('User %s is a member of groups: %s', username, group_list) + if "cn" in entry: + group_list.append(salt.utils.stringutils.to_unicode(entry["cn"][0])) + log.debug("User %s is a member of groups: %s", username, group_list) - elif _config('freeipa'): + elif _config("freeipa"): escaped_username = ldap.filter.escape_filter_chars(username) - search_base = _config('group_basedn') - search_string = _render_template(_config('group_filter'), escaped_username) - search_results = bind.search_s(search_base, - ldap.SCOPE_SUBTREE, - search_string, - [salt.utils.stringutils.to_str(_config('accountattributename')), salt.utils.stringutils.to_str(_config('groupattribute')), str('cn')]) # future lint: disable=blacklisted-function + search_base = _config("group_basedn") + search_string = _render_template(_config("group_filter"), escaped_username) + search_results = bind.search_s( + search_base, + ldap.SCOPE_SUBTREE, + search_string, + [ + salt.utils.stringutils.to_str(_config("accountattributename")), + salt.utils.stringutils.to_str(_config("groupattribute")), + str("cn"), + ], + ) # future lint: disable=blacklisted-function for entry, result in search_results: - for user in itertools.chain(result.get(_config('accountattributename'), []), - result.get(_config('groupattribute'), [])): - if username == salt.utils.stringutils.to_unicode(user).split(',')[0].split('=')[-1]: - group_list.append(entry.split(',')[0].split('=')[-1]) + for user in itertools.chain( + result.get(_config("accountattributename"), []), + result.get(_config("groupattribute"), []), + ): + if ( + username + == salt.utils.stringutils.to_unicode(user) + .split(",")[0] + .split("=")[-1] + ): + group_list.append(entry.split(",")[0].split("=")[-1]) - log.debug('User %s is a member of groups: %s', username, group_list) + log.debug("User %s is a member of groups: %s", username, group_list) - if not auth(username, kwargs['password']): - log.error('LDAP username and password do not match') + if not auth(username, kwargs["password"]): + log.error("LDAP username and password do not match") return [] else: - if _config('groupou'): - search_base = 'ou={0},{1}'.format(_config('groupou'), _config('basedn')) + if _config("groupou"): + search_base = "ou={0},{1}".format(_config("groupou"), _config("basedn")) else: - search_base = '{0}'.format(_config('basedn')) - search_string = '(&({0}={1})(objectClass={2}))'.format(_config('accountattributename'), - username, _config('groupclass')) - search_results = bind.search_s(search_base, - ldap.SCOPE_SUBTREE, - search_string, - [salt.utils.stringutils.to_str(_config('accountattributename')), - str('cn'), # future lint: disable=blacklisted-function - salt.utils.stringutils.to_str(_config('groupattribute'))]) + search_base = "{0}".format(_config("basedn")) + search_string = "(&({0}={1})(objectClass={2}))".format( + _config("accountattributename"), username, _config("groupclass") + ) + search_results = bind.search_s( + search_base, + ldap.SCOPE_SUBTREE, + search_string, + [ + salt.utils.stringutils.to_str(_config("accountattributename")), + str("cn"), # future lint: disable=blacklisted-function + salt.utils.stringutils.to_str(_config("groupattribute")), + ], + ) for _, entry in search_results: - if username in salt.utils.data.decode(entry[_config('accountattributename')]): - group_list.append(salt.utils.stringutils.to_unicode(entry['cn'][0])) + if username in salt.utils.data.decode( + entry[_config("accountattributename")] + ): + group_list.append(salt.utils.stringutils.to_unicode(entry["cn"][0])) for user, entry in search_results: - if username == salt.utils.stringutils.to_unicode(user).split(',')[0].split('=')[-1]: - for group in salt.utils.data.decode(entry[_config('groupattribute')]): - group_list.append(salt.utils.stringutils.to_unicode(group).split(',')[0].split('=')[-1]) - log.debug('User %s is a member of groups: %s', username, group_list) + if ( + username + == salt.utils.stringutils.to_unicode(user) + .split(",")[0] + .split("=")[-1] + ): + for group in salt.utils.data.decode( + entry[_config("groupattribute")] + ): + group_list.append( + salt.utils.stringutils.to_unicode(group) + .split(",")[0] + .split("=")[-1] + ) + log.debug("User %s is a member of groups: %s", username, group_list) # Only test user auth on first call for job. # 'show_jid' only exists on first payload so we can use that for the conditional. - if 'show_jid' in kwargs and not _bind(username, kwargs.get('password'), - anonymous=_config('auth_by_group_membership_only', mandatory=False) and - _config('anonymous', mandatory=False)): - log.error('LDAP username and password do not match') + if "show_jid" in kwargs and not _bind( + username, + kwargs.get("password"), + anonymous=_config("auth_by_group_membership_only", mandatory=False) + and _config("anonymous", mandatory=False), + ): + log.error("LDAP username and password do not match") return [] else: - log.error('ldap bind to determine group membership FAILED!') + log.error("ldap bind to determine group membership FAILED!") return group_list def __expand_ldap_entries(entries, opts=None): - ''' + """ :param entries: ldap subtree in external_auth config option :param opts: Opts to use when __opts__ not defined @@ -458,7 +562,7 @@ def __expand_ldap_entries(entries, opts=None): - allowed_fn_list_attribute^ This function only gets called if auth.ldap.activedirectory = True - ''' + """ bind = _bind_for_search(opts=opts) acl_tree = [] for user_or_group_dict in entries: @@ -468,26 +572,25 @@ def __expand_ldap_entries(entries, opts=None): for minion_or_ou, matchers in six.iteritems(user_or_group_dict): permissions = matchers retrieved_minion_ids = [] - if minion_or_ou.startswith('ldap('): - search_base = minion_or_ou.lstrip('ldap(').rstrip(')') + if minion_or_ou.startswith("ldap("): + search_base = minion_or_ou.lstrip("ldap(").rstrip(")") - search_string = '(objectClass=computer)' + search_string = "(objectClass=computer)" try: - search_results = bind.search_s(search_base, - ldap.SCOPE_SUBTREE, - search_string, - [str('cn')]) # future lint: disable=blacklisted-function + search_results = bind.search_s( + search_base, ldap.SCOPE_SUBTREE, search_string, [str("cn")] + ) # future lint: disable=blacklisted-function for ldap_match in search_results: try: - minion_id = ldap_match[1]['cn'][0].lower() + minion_id = ldap_match[1]["cn"][0].lower() # Some LDAP/AD trees only have the FQDN of machines # in their computer lists. auth.minion_stripdomains # lets a user strip off configured domain names # and arrive at the basic minion_id - if opts.get('auth.ldap.minion_stripdomains', None): - for domain in opts['auth.ldap.minion_stripdomains']: + if opts.get("auth.ldap.minion_stripdomains", None): + for domain in opts["auth.ldap.minion_stripdomains"]: if minion_id.endswith(domain): - minion_id = minion_id[:-len(domain)] + minion_id = minion_id[: -len(domain)] break retrieved_minion_ids.append(minion_id) except TypeError: @@ -498,30 +601,36 @@ def __expand_ldap_entries(entries, opts=None): for minion_id in retrieved_minion_ids: acl_tree.append({minion_id: permissions}) - log.trace('Expanded acl_tree is: %s', acl_tree) + log.trace("Expanded acl_tree is: %s", acl_tree) except ldap.NO_SUCH_OBJECT: pass else: acl_tree.append({minion_or_ou: matchers}) - log.trace('__expand_ldap_entries: %s', acl_tree) + log.trace("__expand_ldap_entries: %s", acl_tree) return acl_tree def process_acl(auth_list, opts=None): - ''' + """ Query LDAP, retrieve list of minion_ids from an OU or other search. For each minion_id returned from the LDAP search, copy the perms matchers into the auth dictionary :param auth_list: :param opts: __opts__ for when __opts__ is not injected :return: Modified auth list. - ''' + """ ou_names = [] for item in auth_list: if isinstance(item, six.string_types): continue - ou_names.extend([potential_ou for potential_ou in item.keys() if potential_ou.startswith('ldap(')]) + ou_names.extend( + [ + potential_ou + for potential_ou in item.keys() + if potential_ou.startswith("ldap(") + ] + ) if ou_names: auth_list = __expand_ldap_entries(auth_list, opts) return auth_list diff --git a/salt/auth/mysql.py b/salt/auth/mysql.py index 86d00a43738..7c56bfab532 100644 --- a/salt/auth/mysql.py +++ b/salt/auth/mysql.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Provide authentication using MySQL. When using MySQL as an authentication backend, you will need to create or @@ -47,9 +47,10 @@ Enable MySQL authentication. - test.* :depends: - MySQL-python Python module -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging log = logging.getLogger(__name__) @@ -64,6 +65,7 @@ except ImportError: try: # MySQLdb import failed, try to import PyMySQL import pymysql + pymysql.install_as_MySQLdb() import MySQLdb import MySQLdb.cursors @@ -74,52 +76,51 @@ except ImportError: def __virtual__(): - ''' + """ Confirm that a python mysql client is installed. - ''' - return bool(MySQLdb), 'No python mysql client installed.' if MySQLdb is None else '' + """ + return bool(MySQLdb), "No python mysql client installed." if MySQLdb is None else "" def __get_connection_info(): - ''' + """ Grab MySQL Connection Details - ''' + """ conn_info = {} try: - conn_info['hostname'] = __opts__['mysql_auth']['hostname'] - conn_info['username'] = __opts__['mysql_auth']['username'] - conn_info['password'] = __opts__['mysql_auth']['password'] - conn_info['database'] = __opts__['mysql_auth']['database'] + conn_info["hostname"] = __opts__["mysql_auth"]["hostname"] + conn_info["username"] = __opts__["mysql_auth"]["username"] + conn_info["password"] = __opts__["mysql_auth"]["password"] + conn_info["database"] = __opts__["mysql_auth"]["database"] - conn_info['auth_sql'] = __opts__['mysql_auth']['auth_sql'] + conn_info["auth_sql"] = __opts__["mysql_auth"]["auth_sql"] except KeyError as e: - log.error('%s does not exist', e) + log.error("%s does not exist", e) return None return conn_info def auth(username, password): - ''' + """ Authenticate using a MySQL user table - ''' + """ _info = __get_connection_info() if _info is None: return False try: - conn = MySQLdb.connect(_info['hostname'], - _info['username'], - _info['password'], - _info['database']) + conn = MySQLdb.connect( + _info["hostname"], _info["username"], _info["password"], _info["database"] + ) except OperationalError as e: log.error(e) return False cur = conn.cursor() - cur.execute(_info['auth_sql'].format(username, password)) + cur.execute(_info["auth_sql"].format(username, password)) if cur.rowcount == 1: return True diff --git a/salt/auth/pam.py b/salt/auth/pam.py index 1635f6fa429..59ffab20dbc 100644 --- a/salt/auth/pam.py +++ b/salt/auth/pam.py @@ -4,7 +4,7 @@ # (c) 2007 Chris AtLee # Licensed under the MIT license: # http://www.opensource.org/licenses/mit-license.php -''' +""" Authenticate against PAM Provides an authenticate function that will allow the caller to authenticate @@ -33,26 +33,39 @@ authenticated against. This defaults to `login` This should not be needed with python >= 3.3, because the `os` modules has the `getgrouplist` function. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging -from ctypes import CDLL, POINTER, Structure, CFUNCTYPE, cast, pointer, sizeof -from ctypes import c_void_p, c_uint, c_char_p, c_char, c_int +from ctypes import ( + CDLL, + CFUNCTYPE, + POINTER, + Structure, + c_char, + c_char_p, + c_int, + c_uint, + c_void_p, + cast, + pointer, + sizeof, +) from ctypes.util import find_library # Import Salt libs import salt.utils.user -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin # Import 3rd-party libs from salt.ext import six +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin log = logging.getLogger(__name__) try: - LIBC = CDLL(find_library('c')) + LIBC = CDLL(find_library("c")) CALLOC = LIBC.calloc CALLOC.restype = c_void_p @@ -62,7 +75,7 @@ try: STRDUP.argstypes = [c_char_p] STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!! except Exception: # pylint: disable=broad-except - log.trace('Failed to load libc using ctypes', exc_info=True) + log.trace("Failed to load libc using ctypes", exc_info=True) HAS_LIBC = False else: HAS_LIBC = True @@ -75,12 +88,11 @@ PAM_TEXT_INFO = 4 class PamHandle(Structure): - ''' + """ Wrapper class for pam_handle_t - ''' - _fields_ = [ - ('handle', c_void_p) - ] + """ + + _fields_ = [("handle", c_void_p)] def __init__(self): Structure.__init__(self) @@ -88,57 +100,51 @@ class PamHandle(Structure): class PamMessage(Structure): - ''' + """ Wrapper class for pam_message structure - ''' + """ + _fields_ = [ - ("msg_style", c_int), - ("msg", c_char_p), - ] + ("msg_style", c_int), + ("msg", c_char_p), + ] def __repr__(self): - return ''.format(self.msg_style, self.msg) + return "".format(self.msg_style, self.msg) class PamResponse(Structure): - ''' + """ Wrapper class for pam_response structure - ''' + """ + _fields_ = [ - ('resp', c_char_p), - ('resp_retcode', c_int), - ] + ("resp", c_char_p), + ("resp_retcode", c_int), + ] def __repr__(self): - return ''.format(self.resp_retcode, self.resp) + return "".format(self.resp_retcode, self.resp) CONV_FUNC = CFUNCTYPE( - c_int, - c_int, - POINTER(POINTER(PamMessage)), - POINTER(POINTER(PamResponse)), - c_void_p) + c_int, c_int, POINTER(POINTER(PamMessage)), POINTER(POINTER(PamResponse)), c_void_p +) class PamConv(Structure): - ''' + """ Wrapper class for pam_conv structure - ''' - _fields_ = [ - ('conv', CONV_FUNC), - ('appdata_ptr', c_void_p) - ] + """ + + _fields_ = [("conv", CONV_FUNC), ("appdata_ptr", c_void_p)] try: - LIBPAM = CDLL(find_library('pam')) + LIBPAM = CDLL(find_library("pam")) PAM_START = LIBPAM.pam_start PAM_START.restype = c_int - PAM_START.argtypes = [c_char_p, - c_char_p, - POINTER(PamConv), - POINTER(PamHandle)] + PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv), POINTER(PamHandle)] PAM_AUTHENTICATE = LIBPAM.pam_authenticate PAM_AUTHENTICATE.restype = c_int @@ -152,29 +158,29 @@ try: PAM_END.restype = c_int PAM_END.argtypes = [PamHandle, c_int] except Exception: # pylint: disable=broad-except - log.trace('Failed to load pam using ctypes', exc_info=True) + log.trace("Failed to load pam using ctypes", exc_info=True) HAS_PAM = False else: HAS_PAM = True def __virtual__(): - ''' + """ Only load on Linux systems - ''' + """ return HAS_LIBC and HAS_PAM def authenticate(username, password): - ''' + """ Returns True if the given username and password authenticate for the given service. Returns False otherwise ``username``: the username to authenticate ``password``: the password in plain text - ''' - service = __opts__.get('auth.pam.service', 'login') + """ + service = __opts__.get("auth.pam.service", "login") if isinstance(username, six.text_type): username = username.encode(__salt_system_encoding__) @@ -185,10 +191,10 @@ def authenticate(username, password): @CONV_FUNC def my_conv(n_messages, messages, p_response, app_data): - ''' + """ Simple conversation function that responds to any prompt where the echo is off with the supplied password - ''' + """ # Create an array of n_messages response objects addr = CALLOC(n_messages, sizeof(PamResponse)) p_response[0] = cast(addr, POINTER(PamResponse)) @@ -217,16 +223,16 @@ def authenticate(username, password): def auth(username, password, **kwargs): - ''' + """ Authenticate via pam - ''' + """ return authenticate(username, password) def groups(username, *args, **kwargs): - ''' + """ Retrieve groups for a given user for this auth provider Uses system groups - ''' + """ return salt.utils.user.get_group_list(username) diff --git a/salt/auth/pki.py b/salt/auth/pki.py index 03d0d66e36c..7fb1cf90726 100644 --- a/salt/auth/pki.py +++ b/salt/auth/pki.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Majority of code shamelessly stolen from # http://www.v13.gr/blog/?p=303 -''' +""" Authenticate via a PKI certificate. .. note:: @@ -14,16 +14,21 @@ a user via their public cert against a pre-defined Certificate Authority. TODO: Add a 'ca_dir' option to configure a directory of CA files, a la Apache. :depends: - pyOpenSSL module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +# Import salt libs +import salt.utils.files + # Import third party libs # pylint: disable=import-error try: try: from M2Crypto import X509 + HAS_M2 = True except ImportError: HAS_M2 = False @@ -37,23 +42,21 @@ except ImportError: HAS_DEPS = False # pylint: enable=import-error -# Import salt libs -import salt.utils.files log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Requires newer pycrypto and pyOpenSSL - ''' + """ if HAS_DEPS: return True return False def auth(username, password, **kwargs): - ''' + """ Returns True if the given user cert (password is the cert contents) was issued by the CA and if cert's Common Name is equal to username. @@ -73,22 +76,22 @@ def auth(username, password, **kwargs): ca_file: /etc/pki/tls/ca_certs/trusted-ca.crt your_user: - .* - ''' + """ pem = password - cacert_file = __salt__['config.get']('external_auth:pki:ca_file') + cacert_file = __salt__["config.get"]("external_auth:pki:ca_file") - log.debug('Attempting to authenticate via pki.') - log.debug('Using CA file: %s', cacert_file) - log.debug('Certificate contents: %s', pem) + log.debug("Attempting to authenticate via pki.") + log.debug("Using CA file: %s", cacert_file) + log.debug("Certificate contents: %s", pem) if HAS_M2: cert = X509.load_cert_string(pem, X509.FORMAT_PEM) cacert = X509.load_cert(cacert_file, X509.FORMAT_PEM) if cert.verify(cacert.get_pubkey()): - log.info('Successfully authenticated certificate: {0}'.format(pem)) + log.info("Successfully authenticated certificate: {0}".format(pem)) return True else: - log.info('Failed to authenticate certificate: {0}'.format(pem)) + log.info("Failed to authenticate certificate: {0}".format(pem)) return False c = OpenSSL.crypto @@ -113,7 +116,7 @@ def auth(username, password, **kwargs): # - signature # http://usefulfor.com/nothing/2009/06/10/x509-certificate-basics/ der_cert = der[0] - #der_algo = der[1] + # der_algo = der[1] der_sig = der[2] # The signature is a BIT STRING (Type 3) @@ -129,17 +132,19 @@ def auth(username, password, **kwargs): # First byte is the number of unused bits. This should be 0 # http://msdn.microsoft.com/en-us/library/windows/desktop/bb540792(v=vs.85).aspx - if sig0[0] != '\x00': - raise Exception('Number of unused bits is strange') + if sig0[0] != "\x00": + raise Exception("Number of unused bits is strange") # Now get the signature itself sig = sig0[1:] # And verify the certificate try: c.verify(cacert, sig, der_cert, algo) - assert dict(cert.get_subject().get_components())['CN'] == username, "Certificate's CN should match the username" - log.info('Successfully authenticated certificate: %s', pem) + assert ( + dict(cert.get_subject().get_components())["CN"] == username + ), "Certificate's CN should match the username" + log.info("Successfully authenticated certificate: %s", pem) return True except (OpenSSL.crypto.Error, AssertionError): - log.info('Failed to authenticate certificate: %s', pem) + log.info("Failed to authenticate certificate: %s", pem) return False diff --git a/salt/auth/rest.py b/salt/auth/rest.py index 65eba702f7e..98295f35728 100644 --- a/salt/auth/rest.py +++ b/salt/auth/rest.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Provide authentication using a REST call REST auth can be defined like any other eauth module: @@ -21,10 +21,11 @@ run any execution module and all runners. The REST call should return a JSON object that maps to a regular eauth YAML structure as above. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -32,7 +33,7 @@ import salt.utils.http log = logging.getLogger(__name__) -__virtualname__ = 'rest' +__virtualname__ = "rest" def __virtual__(): @@ -41,30 +42,31 @@ def __virtual__(): def rest_auth_setup(): - if '^url' in __opts__['external_auth']['rest']: - return __opts__['external_auth']['rest']['^url'] + if "^url" in __opts__["external_auth"]["rest"]: + return __opts__["external_auth"]["rest"]["^url"] else: return False def auth(username, password): - ''' + """ REST authentication - ''' + """ url = rest_auth_setup() - data = {'username': username, 'password': password} + data = {"username": username, "password": password} # Post to the API endpoint. If 200 is returned then the result will be the ACLs # for this user - result = salt.utils.http.query(url, method='POST', data=data, status=True, - decode=True) - if result['status'] == 200: - log.debug('eauth REST call returned 200: %s', result) - if result['dict'] is not None: - return result['dict'] + result = salt.utils.http.query( + url, method="POST", data=data, status=True, decode=True + ) + if result["status"] == 200: + log.debug("eauth REST call returned 200: %s", result) + if result["dict"] is not None: + return result["dict"] return True else: - log.debug('eauth REST call failed: %s', result) + log.debug("eauth REST call failed: %s", result) return False diff --git a/salt/auth/sharedsecret.py b/salt/auth/sharedsecret.py index ff3411af432..7b531a6adcf 100644 --- a/salt/auth/sharedsecret.py +++ b/salt/auth/sharedsecret.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -'''Provide authentication using configured shared secret +"""Provide authentication using configured shared secret .. code-block:: yaml @@ -28,17 +28,18 @@ See the documentation for cherrypy to setup the headers in your frontal. .. versionadded:: Beryllium -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging log = logging.getLogger(__name__) def auth(username, password): - ''' + """ Shared secret authentication - ''' - return password == __opts__.get('sharedsecret') + """ + return password == __opts__.get("sharedsecret") diff --git a/salt/auth/yubico.py b/salt/auth/yubico.py index 08506e01d54..c4c39c2b28f 100644 --- a/salt/auth/yubico.py +++ b/salt/auth/yubico.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Provide authentication using YubiKey. .. versionadded:: 2015.5.0 @@ -36,31 +36,34 @@ two values in your /etc/salt/master configuration. Please wait five to ten minutes after generating the key before testing so that the API key will be updated on all the YubiCloud servers. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -from __future__ import print_function + import logging log = logging.getLogger(__name__) try: from yubico_client import Yubico, yubico_exceptions + HAS_YUBICO = True except ImportError: HAS_YUBICO = False def __get_yubico_users(username): - ''' + """ Grab the YubiKey Client ID & Secret Key - ''' + """ user = {} try: - if __opts__['yubico_users'].get(username, None): - (user['id'], user['key']) = list(__opts__['yubico_users'][username].values()) + if __opts__["yubico_users"].get(username, None): + (user["id"], user["key"]) = list( + __opts__["yubico_users"][username].values() + ) else: return None except KeyError: @@ -70,17 +73,17 @@ def __get_yubico_users(username): def auth(username, password): - ''' + """ Authenticate against yubico server - ''' + """ _cred = __get_yubico_users(username) - client = Yubico(_cred['id'], _cred['key']) + client = Yubico(_cred["id"], _cred["key"]) try: return client.verify(password) except yubico_exceptions.StatusCodeError as e: - log.info('Unable to verify YubiKey `%s`', e) + log.info("Unable to verify YubiKey `%s`", e) return False @@ -88,10 +91,10 @@ def groups(username, *args, **kwargs): return False -if __name__ == '__main__': - __opts__ = {'yubico_users': {'damian': {'id': '12345', 'key': 'ABC123'}}} +if __name__ == "__main__": + __opts__ = {"yubico_users": {"damian": {"id": "12345", "key": "ABC123"}}} - if auth('damian', 'OPT'): + if auth("damian", "OPT"): print("Authenticated") else: print("Failed to authenticate") diff --git a/salt/beacons/__init__.py b/salt/beacons/__init__.py index b14c583cb32..f24c06f7762 100644 --- a/salt/beacons/__init__.py +++ b/salt/beacons/__init__.py @@ -1,27 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" This package contains the loader modules for the salt streams system -''' +""" # Import Python libs from __future__ import absolute_import -import logging + import copy +import logging import re # Import Salt libs import salt.loader import salt.utils.event import salt.utils.minion -from salt.ext.six.moves import map from salt.exceptions import CommandExecutionError +from salt.ext.six.moves import map log = logging.getLogger(__name__) class Beacon(object): - ''' + """ This class is used to evaluate and execute on the beacon system - ''' + """ + def __init__(self, opts, functions): self.opts = opts self.functions = functions @@ -29,7 +31,7 @@ class Beacon(object): self.interval_map = dict() def process(self, config, grains): - ''' + """ Process the configured beacons The config must be a list and looks like this in yaml @@ -40,13 +42,13 @@ class Beacon(object): - files: - /etc/fstab: {} - /var/cache/foo: {} - ''' + """ ret = [] b_config = copy.deepcopy(config) - if 'enabled' in b_config and not b_config['enabled']: + if "enabled" in b_config and not b_config["enabled"]: return for mod in config: - if mod == 'enabled': + if mod == "enabled": continue # Convert beacons that are lists to a dict to make processing easier @@ -57,51 +59,62 @@ class Beacon(object): elif isinstance(config[mod], dict): current_beacon_config = config[mod] - if 'enabled' in current_beacon_config: - if not current_beacon_config['enabled']: - log.trace('Beacon %s disabled', mod) + if "enabled" in current_beacon_config: + if not current_beacon_config["enabled"]: + log.trace("Beacon %s disabled", mod) continue else: # remove 'enabled' item before processing the beacon if isinstance(config[mod], dict): - del config[mod]['enabled'] + del config[mod]["enabled"] else: - self._remove_list_item(config[mod], 'enabled') + self._remove_list_item(config[mod], "enabled") - log.trace('Beacon processing: %s', mod) + log.trace("Beacon processing: %s", mod) beacon_name = None - if self._determine_beacon_config(current_beacon_config, 'beacon_module'): - beacon_name = current_beacon_config['beacon_module'] + if self._determine_beacon_config(current_beacon_config, "beacon_module"): + beacon_name = current_beacon_config["beacon_module"] else: beacon_name = mod - fun_str = '{0}.beacon'.format(beacon_name) - validate_str = '{0}.validate'.format(beacon_name) + fun_str = "{0}.beacon".format(beacon_name) + validate_str = "{0}.validate".format(beacon_name) if fun_str in self.beacons: - runonce = self._determine_beacon_config(current_beacon_config, 'run_once') - interval = self._determine_beacon_config(current_beacon_config, 'interval') + runonce = self._determine_beacon_config( + current_beacon_config, "run_once" + ) + interval = self._determine_beacon_config( + current_beacon_config, "interval" + ) if interval: - b_config = self._trim_config(b_config, mod, 'interval') + b_config = self._trim_config(b_config, mod, "interval") if not self._process_interval(mod, interval): - log.trace('Skipping beacon %s. Interval not reached.', mod) + log.trace("Skipping beacon %s. Interval not reached.", mod) continue - if self._determine_beacon_config(current_beacon_config, 'disable_during_state_run'): - log.trace('Evaluting if beacon %s should be skipped due to a state run.', mod) - b_config = self._trim_config(b_config, mod, 'disable_during_state_run') + if self._determine_beacon_config( + current_beacon_config, "disable_during_state_run" + ): + log.trace( + "Evaluting if beacon %s should be skipped due to a state run.", + mod, + ) + b_config = self._trim_config( + b_config, mod, "disable_during_state_run" + ) is_running = False running_jobs = salt.utils.minion.running(self.opts) for job in running_jobs: - if re.match('state.*', job['fun']): + if re.match("state.*", job["fun"]): is_running = True if is_running: - close_str = '{0}.close'.format(beacon_name) + close_str = "{0}.close".format(beacon_name) if close_str in self.beacons: - log.info('Closing beacon %s. State run in progress.', mod) + log.info("Closing beacon %s. State run in progress.", mod) self.beacons[close_str](b_config[mod]) else: - log.info('Skipping beacon %s. State run in progress.', mod) + log.info("Skipping beacon %s. State run in progress.", mod) continue # Update __grains__ on the beacon - self.beacons[fun_str].__globals__['__grains__'] = grains + self.beacons[fun_str].__globals__["__grains__"] = grains # Run the validate function if it's available, # otherwise there is a warning about it being missing @@ -109,31 +122,32 @@ class Beacon(object): valid, vcomment = self.beacons[validate_str](b_config[mod]) if not valid: - log.info('Beacon %s configuration invalid, ' - 'not running.\n%s', mod, vcomment) + log.info( + "Beacon %s configuration invalid, " "not running.\n%s", + mod, + vcomment, + ) continue # Fire the beacon! raw = self.beacons[fun_str](b_config[mod]) for data in raw: - tag = 'salt/beacon/{0}/{1}/'.format(self.opts['id'], mod) - if 'tag' in data: - tag += data.pop('tag') - if 'id' not in data: - data['id'] = self.opts['id'] - ret.append({'tag': tag, - 'data': data, - 'beacon_name': beacon_name}) + tag = "salt/beacon/{0}/{1}/".format(self.opts["id"], mod) + if "tag" in data: + tag += data.pop("tag") + if "id" not in data: + data["id"] = self.opts["id"] + ret.append({"tag": tag, "data": data, "beacon_name": beacon_name}) if runonce: self.disable_beacon(mod) else: - log.warning('Unable to process beacon %s', mod) + log.warning("Unable to process beacon %s", mod) return ret def _trim_config(self, b_config, mod, key): - ''' + """ Take a beacon configuration and strip out the interval bits - ''' + """ if isinstance(b_config[mod], list): self._remove_list_item(b_config[mod], key) elif isinstance(b_config[mod], dict): @@ -141,9 +155,9 @@ class Beacon(object): return b_config def _determine_beacon_config(self, current_beacon_config, key): - ''' + """ Process a beacon configuration to determine its interval - ''' + """ interval = False if isinstance(current_beacon_config, dict): @@ -152,30 +166,30 @@ class Beacon(object): return interval def _process_interval(self, mod, interval): - ''' + """ Process beacons with intervals Return True if a beacon should be run on this loop - ''' - log.trace('Processing interval %s for beacon mod %s', interval, mod) - loop_interval = self.opts['loop_interval'] + """ + log.trace("Processing interval %s for beacon mod %s", interval, mod) + loop_interval = self.opts["loop_interval"] if mod in self.interval_map: - log.trace('Processing interval in map') + log.trace("Processing interval in map") counter = self.interval_map[mod] - log.trace('Interval counter: %s', counter) + log.trace("Interval counter: %s", counter) if counter * loop_interval >= interval: self.interval_map[mod] = 1 return True else: self.interval_map[mod] += 1 else: - log.trace('Interval process inserting mod: %s', mod) + log.trace("Interval process inserting mod: %s", mod) self.interval_map[mod] = 1 return False def _get_index(self, beacon_config, label): - ''' + """ Return the index of a labeled config item in the beacon config, -1 if the index is not found - ''' + """ indexes = [index for index, item in enumerate(beacon_config) if label in item] if len(indexes) < 1: @@ -184,51 +198,47 @@ class Beacon(object): return indexes[0] def _remove_list_item(self, beacon_config, label): - ''' + """ Remove an item from a beacon config list - ''' + """ index = self._get_index(beacon_config, label) del beacon_config[index] def _update_enabled(self, name, enabled_value): - ''' + """ Update whether an individual beacon is enabled - ''' + """ - if isinstance(self.opts['beacons'][name], dict): + if isinstance(self.opts["beacons"][name], dict): # Backwards compatibility - self.opts['beacons'][name]['enabled'] = enabled_value + self.opts["beacons"][name]["enabled"] = enabled_value else: - enabled_index = self._get_index(self.opts['beacons'][name], 'enabled') + enabled_index = self._get_index(self.opts["beacons"][name], "enabled") if enabled_index >= 0: - self.opts['beacons'][name][enabled_index]['enabled'] = enabled_value + self.opts["beacons"][name][enabled_index]["enabled"] = enabled_value else: - self.opts['beacons'][name].append({'enabled': enabled_value}) + self.opts["beacons"][name].append({"enabled": enabled_value}) - def _get_beacons(self, - include_opts=True, - include_pillar=True): - ''' + def _get_beacons(self, include_opts=True, include_pillar=True): + """ Return the beacons data structure - ''' + """ beacons = {} if include_pillar: - pillar_beacons = self.opts.get('pillar', {}).get('beacons', {}) + pillar_beacons = self.opts.get("pillar", {}).get("beacons", {}) if not isinstance(pillar_beacons, dict): - raise ValueError('Beacons must be of type dict.') + raise ValueError("Beacons must be of type dict.") beacons.update(pillar_beacons) if include_opts: - opts_beacons = self.opts.get('beacons', {}) + opts_beacons = self.opts.get("beacons", {}) if not isinstance(opts_beacons, dict): - raise ValueError('Beacons must be of type dict.') + raise ValueError("Beacons must be of type dict.") beacons.update(opts_beacons) return beacons - def list_beacons(self, - include_pillar=True, - include_opts=True): - ''' + def list_beacons(self, include_pillar=True, include_opts=True): + """ List the beacon items include_pillar: Whether to include beacons that are @@ -236,214 +246,266 @@ class Beacon(object): include_opts: Whether to include beacons that are configured in opts, default is True. - ''' - beacons = self._get_beacons(include_pillar=include_pillar, - include_opts=include_opts) + """ + beacons = self._get_beacons( + include_pillar=include_pillar, include_opts=include_opts + ) # Fire the complete event back along with the list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': True, 'beacons': beacons}, - tag='/salt/minion/minion_beacons_list_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + {"complete": True, "beacons": beacons}, + tag="/salt/minion/minion_beacons_list_complete", + ) return True def list_available_beacons(self): - ''' + """ List the available beacons - ''' - _beacons = ['{0}'.format(_beacon.replace('.beacon', '')) - for _beacon in self.beacons if '.beacon' in _beacon] + """ + _beacons = [ + "{0}".format(_beacon.replace(".beacon", "")) + for _beacon in self.beacons + if ".beacon" in _beacon + ] # Fire the complete event back along with the list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': True, 'beacons': _beacons}, - tag='/salt/minion/minion_beacons_list_available_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + {"complete": True, "beacons": _beacons}, + tag="/salt/minion/minion_beacons_list_available_complete", + ) return True def validate_beacon(self, name, beacon_data): - ''' + """ Return available beacon functions - ''' - validate_str = '{}.validate'.format(name) + """ + validate_str = "{}.validate".format(name) # Run the validate function if it's available, # otherwise there is a warning about it being missing if validate_str in self.beacons: - if 'enabled' in beacon_data: - del beacon_data['enabled'] + if "enabled" in beacon_data: + del beacon_data["enabled"] valid, vcomment = self.beacons[validate_str](beacon_data) else: - vcomment = 'Beacon {0} does not have a validate' \ - ' function, skipping validation.'.format(name) + vcomment = ( + "Beacon {0} does not have a validate" + " function, skipping validation.".format(name) + ) valid = True # Fire the complete event back along with the list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': True, - 'vcomment': vcomment, - 'valid': valid}, - tag='/salt/minion/minion_beacon_validation_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + {"complete": True, "vcomment": vcomment, "valid": valid}, + tag="/salt/minion/minion_beacon_validation_complete", + ) return True def add_beacon(self, name, beacon_data): - ''' + """ Add a beacon item - ''' + """ data = {} data[name] = beacon_data if name in self._get_beacons(include_opts=False): - comment = 'Cannot update beacon item {0}, ' \ - 'because it is configured in pillar.'.format(name) + comment = ( + "Cannot update beacon item {0}, " + "because it is configured in pillar.".format(name) + ) complete = False else: - if name in self.opts['beacons']: - comment = 'Updating settings for beacon ' \ - 'item: {0}'.format(name) + if name in self.opts["beacons"]: + comment = "Updating settings for beacon " "item: {0}".format(name) else: - comment = 'Added new beacon item: {0}'.format(name) + comment = "Added new beacon item: {0}".format(name) complete = True - self.opts['beacons'].update(data) + self.opts["beacons"].update(data) # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_add_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + { + "complete": complete, + "comment": comment, + "beacons": self.opts["beacons"], + }, + tag="/salt/minion/minion_beacon_add_complete", + ) return True def modify_beacon(self, name, beacon_data): - ''' + """ Modify a beacon item - ''' + """ data = {} data[name] = beacon_data if name in self._get_beacons(include_opts=False): - comment = 'Cannot modify beacon item {0}, ' \ - 'it is configured in pillar.'.format(name) + comment = ( + "Cannot modify beacon item {0}, " + "it is configured in pillar.".format(name) + ) complete = False else: - comment = 'Updating settings for beacon ' \ - 'item: {0}'.format(name) + comment = "Updating settings for beacon " "item: {0}".format(name) complete = True - self.opts['beacons'].update(data) + self.opts["beacons"].update(data) # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_modify_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + { + "complete": complete, + "comment": comment, + "beacons": self.opts["beacons"], + }, + tag="/salt/minion/minion_beacon_modify_complete", + ) return True def delete_beacon(self, name): - ''' + """ Delete a beacon item - ''' + """ if name in self._get_beacons(include_opts=False): - comment = 'Cannot delete beacon item {0}, ' \ - 'it is configured in pillar.'.format(name) + comment = ( + "Cannot delete beacon item {0}, " + "it is configured in pillar.".format(name) + ) complete = False else: - if name in self.opts['beacons']: - del self.opts['beacons'][name] - comment = 'Deleting beacon item: {0}'.format(name) + if name in self.opts["beacons"]: + del self.opts["beacons"][name] + comment = "Deleting beacon item: {0}".format(name) else: - comment = 'Beacon item {0} not found.'.format(name) + comment = "Beacon item {0} not found.".format(name) complete = True # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_delete_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + { + "complete": complete, + "comment": comment, + "beacons": self.opts["beacons"], + }, + tag="/salt/minion/minion_beacon_delete_complete", + ) return True def enable_beacons(self): - ''' + """ Enable beacons - ''' + """ - self.opts['beacons']['enabled'] = True + self.opts["beacons"]["enabled"] = True # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': True, 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacons_enabled_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + {"complete": True, "beacons": self.opts["beacons"]}, + tag="/salt/minion/minion_beacons_enabled_complete", + ) return True def disable_beacons(self): - ''' + """ Enable beacons - ''' + """ - self.opts['beacons']['enabled'] = False + self.opts["beacons"]["enabled"] = False # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': True, 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacons_disabled_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + {"complete": True, "beacons": self.opts["beacons"]}, + tag="/salt/minion/minion_beacons_disabled_complete", + ) return True def enable_beacon(self, name): - ''' + """ Enable a beacon - ''' + """ if name in self._get_beacons(include_opts=False): - comment = 'Cannot enable beacon item {0}, ' \ - 'it is configured in pillar.'.format(name) + comment = ( + "Cannot enable beacon item {0}, " + "it is configured in pillar.".format(name) + ) complete = False else: self._update_enabled(name, True) - comment = 'Enabling beacon item {0}'.format(name) + comment = "Enabling beacon item {0}".format(name) complete = True # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_enabled_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + { + "complete": complete, + "comment": comment, + "beacons": self.opts["beacons"], + }, + tag="/salt/minion/minion_beacon_enabled_complete", + ) return True def disable_beacon(self, name): - ''' + """ Disable a beacon - ''' + """ if name in self._get_beacons(include_opts=False): - comment = 'Cannot disable beacon item {0}, ' \ - 'it is configured in pillar.'.format(name) + comment = ( + "Cannot disable beacon item {0}, " + "it is configured in pillar.".format(name) + ) complete = False else: self._update_enabled(name, False) - comment = 'Disabling beacon item {0}'.format(name) + comment = "Disabling beacon item {0}".format(name) complete = True # Fire the complete event back along with updated list of beacons - with salt.utils.event.get_event('minion', opts=self.opts) as evt: - evt.fire_event({'complete': complete, 'comment': comment, - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_disabled_complete') + with salt.utils.event.get_event("minion", opts=self.opts) as evt: + evt.fire_event( + { + "complete": complete, + "comment": comment, + "beacons": self.opts["beacons"], + }, + tag="/salt/minion/minion_beacon_disabled_complete", + ) return True def reset(self): - ''' + """ Reset the beacons to defaults - ''' - self.opts['beacons'] = {} - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': True, 'comment': 'Beacons have been reset', - 'beacons': self.opts['beacons']}, - tag='/salt/minion/minion_beacon_reset_complete') + """ + self.opts["beacons"] = {} + evt = salt.utils.event.get_event("minion", opts=self.opts) + evt.fire_event( + { + "complete": True, + "comment": "Beacons have been reset", + "beacons": self.opts["beacons"], + }, + tag="/salt/minion/minion_beacon_reset_complete", + ) return True diff --git a/salt/beacons/adb.py b/salt/beacons/adb.py index a2a64d668ff..02972f6b3d9 100644 --- a/salt/beacons/adb.py +++ b/salt/beacons/adb.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to emit adb device state changes for Android devices .. versionadded:: 2016.3.0 -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging # Salt libs @@ -15,14 +16,14 @@ from salt.ext.six.moves import map log = logging.getLogger(__name__) -__virtualname__ = 'adb' +__virtualname__ = "adb" last_state = {} -last_state_extra = {'value': False, 'no_devices': False} +last_state_extra = {"value": False, "no_devices": False} def __virtual__(): - which_result = salt.utils.path.which('adb') + which_result = salt.utils.path.which("adb") if which_result is None: return False else: @@ -30,38 +31,53 @@ def __virtual__(): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for adb beacon should be a dictionary with states array if not isinstance(config, list): - log.info('Configuration for adb beacon must be a list.') - return False, ('Configuration for adb beacon must be a list.') + log.info("Configuration for adb beacon must be a list.") + return False, ("Configuration for adb beacon must be a list.") _config = {} list(map(_config.update, config)) - if 'states' not in _config: - log.info('Configuration for adb beacon must include a states array.') - return False, ('Configuration for adb beacon must include a states array.') + if "states" not in _config: + log.info("Configuration for adb beacon must include a states array.") + return False, ("Configuration for adb beacon must include a states array.") else: - if not isinstance(_config['states'], list): - log.info('Configuration for adb beacon must include a states array.') - return False, ('Configuration for adb beacon must include a states array.') + if not isinstance(_config["states"], list): + log.info("Configuration for adb beacon must include a states array.") + return False, ("Configuration for adb beacon must include a states array.") else: - states = ['offline', 'bootloader', 'device', 'host', - 'recovery', 'no permissions', - 'sideload', 'unauthorized', 'unknown', 'missing'] - if any(s not in states for s in _config['states']): - log.info('Need a one of the following adb ' - 'states: %s', ', '.join(states)) - return False, ('Need a one of the following adb ' - 'states: {0}'.format(', '.join(states))) - return True, 'Valid beacon configuration' + states = [ + "offline", + "bootloader", + "device", + "host", + "recovery", + "no permissions", + "sideload", + "unauthorized", + "unknown", + "missing", + ] + if any(s not in states for s in _config["states"]): + log.info( + "Need a one of the following adb " "states: %s", ", ".join(states) + ) + return ( + False, + ( + "Need a one of the following adb " + "states: {0}".format(", ".join(states)) + ), + ) + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Emit the status of all devices returned by adb Specify the device states that should emit an event, @@ -79,47 +95,60 @@ def beacon(config): - no_devices_event: True - battery_low: 25 - ''' + """ - log.trace('adb beacon starting') + log.trace("adb beacon starting") ret = [] _config = {} list(map(_config.update, config)) - out = __salt__['cmd.run']('adb devices', runas=_config.get('user', None)) + out = __salt__["cmd.run"]("adb devices", runas=_config.get("user", None)) - lines = out.split('\n')[1:] + lines = out.split("\n")[1:] last_state_devices = list(last_state.keys()) found_devices = [] for line in lines: try: - device, state = line.split('\t') + device, state = line.split("\t") found_devices.append(device) - if device not in last_state_devices or \ - ('state' in last_state[device] and last_state[device]['state'] != state): - if state in _config['states']: - ret.append({'device': device, 'state': state, 'tag': state}) - last_state[device] = {'state': state} + if device not in last_state_devices or ( + "state" in last_state[device] and last_state[device]["state"] != state + ): + if state in _config["states"]: + ret.append({"device": device, "state": state, "tag": state}) + last_state[device] = {"state": state} - if 'battery_low' in _config: + if "battery_low" in _config: val = last_state.get(device, {}) - cmd = 'adb -s {0} shell cat /sys/class/power_supply/*/capacity'.format(device) - battery_levels = __salt__['cmd.run'](cmd, runas=_config.get('user', None)).split('\n') + cmd = "adb -s {0} shell cat /sys/class/power_supply/*/capacity".format( + device + ) + battery_levels = __salt__["cmd.run"]( + cmd, runas=_config.get("user", None) + ).split("\n") for l in battery_levels: battery_level = int(l) if 0 < battery_level < 100: - if 'battery' not in val or battery_level != val['battery']: - if ('battery' not in val or val['battery'] > _config['battery_low']) and \ - battery_level <= _config['battery_low']: - ret.append({'device': device, 'battery_level': battery_level, 'tag': 'battery_low'}) + if "battery" not in val or battery_level != val["battery"]: + if ( + "battery" not in val + or val["battery"] > _config["battery_low"] + ) and battery_level <= _config["battery_low"]: + ret.append( + { + "device": device, + "battery_level": battery_level, + "tag": "battery_low", + } + ) if device not in last_state: last_state[device] = {} - last_state[device].update({'battery': battery_level}) + last_state[device].update({"battery": battery_level}) except ValueError: continue @@ -127,18 +156,18 @@ def beacon(config): # Find missing devices and remove them / send an event for device in last_state_devices: if device not in found_devices: - if 'missing' in _config['states']: - ret.append({'device': device, 'state': 'missing', 'tag': 'missing'}) + if "missing" in _config["states"]: + ret.append({"device": device, "state": "missing", "tag": "missing"}) del last_state[device] # Maybe send an event if we don't have any devices - if 'no_devices_event' in _config and _config['no_devices_event'] is True: - if len(found_devices) == 0 and not last_state_extra['no_devices']: - ret.append({'tag': 'no_devices'}) + if "no_devices_event" in _config and _config["no_devices_event"] is True: + if len(found_devices) == 0 and not last_state_extra["no_devices"]: + ret.append({"tag": "no_devices"}) # Did we have no devices listed this time around? - last_state_extra['no_devices'] = len(found_devices) == 0 + last_state_extra["no_devices"] = len(found_devices) == 0 return ret diff --git a/salt/beacons/aix_account.py b/salt/beacons/aix_account.py index 8cb9c13ae8f..04314ec08c3 100644 --- a/salt/beacons/aix_account.py +++ b/salt/beacons/aix_account.py @@ -1,47 +1,56 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to fire event when we notice a AIX user is locked due to many failed login attempts. .. versionadded:: 2018.3.0 :depends: none -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging log = logging.getLogger(__name__) -__virtualname__ = 'aix_account' +__virtualname__ = "aix_account" def __virtual__(): - ''' + """ Only load if kernel is AIX - ''' - if __grains__['kernel'] == ('AIX'): + """ + if __grains__["kernel"] == ("AIX"): return __virtualname__ - return (False, 'The aix_account beacon module failed to load: ' - 'only available on AIX systems.') + return ( + False, + "The aix_account beacon module failed to load: " + "only available on AIX systems.", + ) def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for aix_account beacon should be a dictionary if not isinstance(config, dict): - return False, ('Configuration for aix_account beacon must be a dict.') - if 'user' not in config: - return False, ('Configuration for aix_account beacon must ' - 'include a user or ALL for all users.') - return True, 'Valid beacon configuration' + return False, ("Configuration for aix_account beacon must be a dict.") + if "user" not in config: + return ( + False, + ( + "Configuration for aix_account beacon must " + "include a user or ALL for all users." + ), + ) + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Checks for locked accounts due to too many invalid login attempts, 3 or higher. .. code-block:: yaml @@ -51,13 +60,13 @@ def beacon(config): user: ALL interval: 120 - ''' + """ ret = [] - user = config['user'] + user = config["user"] - locked_accounts = __salt__['shadow.login_failures'](user) - ret.append({'accounts': locked_accounts}) + locked_accounts = __salt__["shadow.login_failures"](user) + ret.append({"accounts": locked_accounts}) return ret diff --git a/salt/beacons/avahi_announce.py b/salt/beacons/avahi_announce.py index 1806164a069..1b7ade54462 100644 --- a/salt/beacons/avahi_announce.py +++ b/salt/beacons/avahi_announce.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to announce via avahi (zeroconf) .. versionadded:: 2016.11.0 @@ -10,9 +10,10 @@ Dependencies - python-avahi - dbus-python -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging import time @@ -22,6 +23,7 @@ from salt.ext.six.moves import map # Import 3rd Party libs try: import avahi + HAS_PYAVAHI = True except ImportError: HAS_PYAVAHI = False @@ -29,11 +31,16 @@ except ImportError: try: import dbus from dbus import DBusException + BUS = dbus.SystemBus() - SERVER = dbus.Interface(BUS.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), - avahi.DBUS_INTERFACE_SERVER) - GROUP = dbus.Interface(BUS.get_object(avahi.DBUS_NAME, SERVER.EntryGroupNew()), - avahi.DBUS_INTERFACE_ENTRY_GROUP) + SERVER = dbus.Interface( + BUS.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER), + avahi.DBUS_INTERFACE_SERVER, + ) + GROUP = dbus.Interface( + BUS.get_object(avahi.DBUS_NAME, SERVER.EntryGroupNew()), + avahi.DBUS_INTERFACE_ENTRY_GROUP, + ) HAS_DBUS = True except (ImportError, NameError): HAS_DBUS = False @@ -42,7 +49,7 @@ except DBusException: log = logging.getLogger(__name__) -__virtualname__ = 'avahi_announce' +__virtualname__ = "avahi_announce" LAST_GRAINS = {} @@ -51,33 +58,41 @@ def __virtual__(): if HAS_PYAVAHI: if HAS_DBUS: return __virtualname__ - return False, 'The {0} beacon cannot be loaded. The ' \ - '\'python-dbus\' dependency is missing.'.format(__virtualname__) - return False, 'The {0} beacon cannot be loaded. The ' \ - '\'python-avahi\' dependency is missing.'.format(__virtualname__) + return ( + False, + "The {0} beacon cannot be loaded. The " + "'python-dbus' dependency is missing.".format(__virtualname__), + ) + return ( + False, + "The {0} beacon cannot be loaded. The " + "'python-avahi' dependency is missing.".format(__virtualname__), + ) def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ _config = {} list(map(_config.update, config)) if not isinstance(config, list): - return False, ('Configuration for avahi_announce ' - 'beacon must be a list.') + return False, ("Configuration for avahi_announce beacon must be a list.") - elif not all(x in _config for x in ('servicetype', - 'port', - 'txt')): - return False, ('Configuration for avahi_announce beacon ' - 'must contain servicetype, port and txt items.') - return True, 'Valid beacon configuration.' + elif not all(x in _config for x in ("servicetype", "port", "txt")): + return ( + False, + ( + "Configuration for avahi_announce beacon " + "must contain servicetype, port and txt items." + ), + ) + return True, "Valid beacon configuration." def _enforce_txt_record_maxlen(key, value): - ''' + """ Enforces the TXT record maximum length of 255 characters. TXT record length includes key, value, and '='. @@ -88,16 +103,16 @@ def _enforce_txt_record_maxlen(key, value): :return: The value of the TXT record. It may be truncated if it exceeds the maximum permitted length. In case of truncation, '...' is appended to indicate that the entire value is not present. - ''' + """ # Add 1 for '=' seperator between key and value if len(key) + len(value) + 1 > 255: # 255 - 3 ('...') - 1 ('=') = 251 - return value[:251 - len(key)] + '...' + return value[: 251 - len(key)] + "..." return value def beacon(config): - ''' + """ Broadcast values via zeroconf If the announced values are static, it is advised to set run_once: True @@ -149,7 +164,7 @@ def beacon(config): ProdName: grains.productname SerialNo: grains.serialnumber Comments: 'this is a test' - ''' + """ ret = [] changes = {} txt = {} @@ -159,76 +174,100 @@ def beacon(config): _config = {} list(map(_config.update, config)) - if 'servicename' in _config: - servicename = _config['servicename'] + if "servicename" in _config: + servicename = _config["servicename"] else: - servicename = __grains__['host'] + servicename = __grains__["host"] # Check for hostname change - if LAST_GRAINS and LAST_GRAINS['host'] != servicename: - changes['servicename'] = servicename + if LAST_GRAINS and LAST_GRAINS["host"] != servicename: + changes["servicename"] = servicename - if LAST_GRAINS and _config.get('reset_on_change', False): + if LAST_GRAINS and _config.get("reset_on_change", False): # Check for IP address change in the case when we reset on change - if LAST_GRAINS.get('ipv4', []) != __grains__.get('ipv4', []): - changes['ipv4'] = __grains__.get('ipv4', []) - if LAST_GRAINS.get('ipv6', []) != __grains__.get('ipv6', []): - changes['ipv6'] = __grains__.get('ipv6', []) + if LAST_GRAINS.get("ipv4", []) != __grains__.get("ipv4", []): + changes["ipv4"] = __grains__.get("ipv4", []) + if LAST_GRAINS.get("ipv6", []) != __grains__.get("ipv6", []): + changes["ipv6"] = __grains__.get("ipv6", []) - for item in _config['txt']: - changes_key = 'txt.' + salt.utils.stringutils.to_unicode(item) - if _config['txt'][item].startswith('grains.'): - grain = _config['txt'][item][7:] + for item in _config["txt"]: + changes_key = "txt." + salt.utils.stringutils.to_unicode(item) + if _config["txt"][item].startswith("grains."): + grain = _config["txt"][item][7:] grain_index = None - square_bracket = grain.find('[') - if square_bracket != -1 and grain[-1] == ']': - grain_index = int(grain[square_bracket+1:-1]) + square_bracket = grain.find("[") + if square_bracket != -1 and grain[-1] == "]": + grain_index = int(grain[square_bracket + 1 : -1]) grain = grain[:square_bracket] - grain_value = __grains__.get(grain, '') + grain_value = __grains__.get(grain, "") if isinstance(grain_value, list): if grain_index is not None: grain_value = grain_value[grain_index] else: - grain_value = ','.join(grain_value) + grain_value = ",".join(grain_value) txt[item] = _enforce_txt_record_maxlen(item, grain_value) - if LAST_GRAINS and (LAST_GRAINS.get(grain, '') != __grains__.get(grain, '')): + if LAST_GRAINS and ( + LAST_GRAINS.get(grain, "") != __grains__.get(grain, "") + ): changes[changes_key] = txt[item] else: - txt[item] = _enforce_txt_record_maxlen(item, _config['txt'][item]) + txt[item] = _enforce_txt_record_maxlen(item, _config["txt"][item]) if not LAST_GRAINS: changes[changes_key] = txt[item] if changes: if not LAST_GRAINS: - changes['servicename'] = servicename - changes['servicetype'] = _config['servicetype'] - changes['port'] = _config['port'] - changes['ipv4'] = __grains__.get('ipv4', []) - changes['ipv6'] = __grains__.get('ipv6', []) - GROUP.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0), - servicename, _config['servicetype'], '', '', - dbus.UInt16(_config['port']), avahi.dict_to_txt_array(txt)) + changes["servicename"] = servicename + changes["servicetype"] = _config["servicetype"] + changes["port"] = _config["port"] + changes["ipv4"] = __grains__.get("ipv4", []) + changes["ipv6"] = __grains__.get("ipv6", []) + GROUP.AddService( + avahi.IF_UNSPEC, + avahi.PROTO_UNSPEC, + dbus.UInt32(0), + servicename, + _config["servicetype"], + "", + "", + dbus.UInt16(_config["port"]), + avahi.dict_to_txt_array(txt), + ) GROUP.Commit() - elif _config.get('reset_on_change', False) or 'servicename' in changes: + elif _config.get("reset_on_change", False) or "servicename" in changes: # A change in 'servicename' requires a reset because we can only # directly update TXT records GROUP.Reset() - reset_wait = _config.get('reset_wait', 0) + reset_wait = _config.get("reset_wait", 0) if reset_wait > 0: time.sleep(reset_wait) - GROUP.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0), - servicename, _config['servicetype'], '', '', - dbus.UInt16(_config['port']), avahi.dict_to_txt_array(txt)) + GROUP.AddService( + avahi.IF_UNSPEC, + avahi.PROTO_UNSPEC, + dbus.UInt32(0), + servicename, + _config["servicetype"], + "", + "", + dbus.UInt16(_config["port"]), + avahi.dict_to_txt_array(txt), + ) GROUP.Commit() else: - GROUP.UpdateServiceTxt(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0), - servicename, _config['servicetype'], '', - avahi.dict_to_txt_array(txt)) + GROUP.UpdateServiceTxt( + avahi.IF_UNSPEC, + avahi.PROTO_UNSPEC, + dbus.UInt32(0), + servicename, + _config["servicetype"], + "", + avahi.dict_to_txt_array(txt), + ) - ret.append({'tag': 'result', 'changes': changes}) + ret.append({"tag": "result", "changes": changes}) - if _config.get('copy_grains', False): + if _config.get("copy_grains", False): LAST_GRAINS = __grains__.copy() else: LAST_GRAINS = __grains__ diff --git a/salt/beacons/bonjour_announce.py b/salt/beacons/bonjour_announce.py index e1f1d83fe42..15760eca4a3 100644 --- a/salt/beacons/bonjour_announce.py +++ b/salt/beacons/bonjour_announce.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to announce via Bonjour (zeroconf) -''' +""" # Import Python libs from __future__ import absolute_import + import atexit import logging import select @@ -17,13 +18,14 @@ from salt.ext.six.moves import map # Import 3rd Party libs try: import pybonjour + HAS_PYBONJOUR = True except ImportError: HAS_PYBONJOUR = False log = logging.getLogger(__name__) -__virtualname__ = 'bonjour_announce' +__virtualname__ = "bonjour_announce" LAST_GRAINS = {} SD_REF = None @@ -36,42 +38,46 @@ def __virtual__(): def _close_sd_ref(): - ''' + """ Close the SD_REF object if it isn't NULL For use with atexit.register - ''' + """ global SD_REF if SD_REF: SD_REF.close() SD_REF = None -def _register_callback(sdRef, flags, errorCode, name, regtype, domain): # pylint: disable=unused-argument +def _register_callback( + sdRef, flags, errorCode, name, regtype, domain +): # pylint: disable=unused-argument if errorCode != pybonjour.kDNSServiceErr_NoError: - log.error('Bonjour registration failed with error code %s', errorCode) + log.error("Bonjour registration failed with error code %s", errorCode) def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ _config = {} list(map(_config.update, config)) if not isinstance(config, list): - return False, ('Configuration for bonjour_announce ' - 'beacon must be a list.') + return False, ("Configuration for bonjour_announce beacon must be a list.") - elif not all(x in _config for x in ('servicetype', - 'port', - 'txt')): - return False, ('Configuration for bonjour_announce beacon ' - 'must contain servicetype, port and txt items.') - return True, 'Valid beacon configuration.' + elif not all(x in _config for x in ("servicetype", "port", "txt")): + return ( + False, + ( + "Configuration for bonjour_announce beacon " + "must contain servicetype, port and txt items." + ), + ) + return True, "Valid beacon configuration." def _enforce_txt_record_maxlen(key, value): - ''' + """ Enforces the TXT record maximum length of 255 characters. TXT record length includes key, value, and '='. @@ -82,16 +88,16 @@ def _enforce_txt_record_maxlen(key, value): :return: The value of the TXT record. It may be truncated if it exceeds the maximum permitted length. In case of truncation, '...' is appended to indicate that the entire value is not present. - ''' + """ # Add 1 for '=' seperator between key and value if len(key) + len(value) + 1 > 255: # 255 - 3 ('...') - 1 ('=') = 251 - return value[:251 - len(key)] + '...' + return value[: 251 - len(key)] + "..." return value def beacon(config): - ''' + """ Broadcast values via zeroconf If the announced values are static, it is advised to set run_once: True @@ -143,7 +149,7 @@ def beacon(config): ProdName: grains.productname SerialNo: grains.serialnumber Comments: 'this is a test' - ''' + """ ret = [] changes = {} txt = {} @@ -154,42 +160,44 @@ def beacon(config): _config = {} list(map(_config.update, config)) - if 'servicename' in _config: - servicename = _config['servicename'] + if "servicename" in _config: + servicename = _config["servicename"] else: - servicename = __grains__['host'] + servicename = __grains__["host"] # Check for hostname change - if LAST_GRAINS and LAST_GRAINS['host'] != servicename: - changes['servicename'] = servicename + if LAST_GRAINS and LAST_GRAINS["host"] != servicename: + changes["servicename"] = servicename - if LAST_GRAINS and _config.get('reset_on_change', False): + if LAST_GRAINS and _config.get("reset_on_change", False): # Check for IP address change in the case when we reset on change - if LAST_GRAINS.get('ipv4', []) != __grains__.get('ipv4', []): - changes['ipv4'] = __grains__.get('ipv4', []) - if LAST_GRAINS.get('ipv6', []) != __grains__.get('ipv6', []): - changes['ipv6'] = __grains__.get('ipv6', []) + if LAST_GRAINS.get("ipv4", []) != __grains__.get("ipv4", []): + changes["ipv4"] = __grains__.get("ipv4", []) + if LAST_GRAINS.get("ipv6", []) != __grains__.get("ipv6", []): + changes["ipv6"] = __grains__.get("ipv6", []) - for item in _config['txt']: - changes_key = 'txt.' + salt.utils.stringutils.to_unicode(item) - if _config['txt'][item].startswith('grains.'): - grain = _config['txt'][item][7:] + for item in _config["txt"]: + changes_key = "txt." + salt.utils.stringutils.to_unicode(item) + if _config["txt"][item].startswith("grains."): + grain = _config["txt"][item][7:] grain_index = None - square_bracket = grain.find('[') - if square_bracket != -1 and grain[-1] == ']': - grain_index = int(grain[square_bracket+1:-1]) + square_bracket = grain.find("[") + if square_bracket != -1 and grain[-1] == "]": + grain_index = int(grain[square_bracket + 1 : -1]) grain = grain[:square_bracket] - grain_value = __grains__.get(grain, '') + grain_value = __grains__.get(grain, "") if isinstance(grain_value, list): if grain_index is not None: grain_value = grain_value[grain_index] else: - grain_value = ','.join(grain_value) + grain_value = ",".join(grain_value) txt[item] = _enforce_txt_record_maxlen(item, grain_value) - if LAST_GRAINS and (LAST_GRAINS.get(grain, '') != __grains__.get(grain, '')): + if LAST_GRAINS and ( + LAST_GRAINS.get(grain, "") != __grains__.get(grain, "") + ): changes[changes_key] = txt[item] else: - txt[item] = _enforce_txt_record_maxlen(item, _config['txt'][item]) + txt[item] = _enforce_txt_record_maxlen(item, _config["txt"][item]) if not LAST_GRAINS: changes[changes_key] = txt[item] @@ -197,49 +205,49 @@ def beacon(config): if changes: txt_record = pybonjour.TXTRecord(items=txt) if not LAST_GRAINS: - changes['servicename'] = servicename - changes['servicetype'] = _config['servicetype'] - changes['port'] = _config['port'] - changes['ipv4'] = __grains__.get('ipv4', []) - changes['ipv6'] = __grains__.get('ipv6', []) + changes["servicename"] = servicename + changes["servicetype"] = _config["servicetype"] + changes["port"] = _config["port"] + changes["ipv4"] = __grains__.get("ipv4", []) + changes["ipv6"] = __grains__.get("ipv6", []) SD_REF = pybonjour.DNSServiceRegister( name=servicename, - regtype=_config['servicetype'], - port=_config['port'], + regtype=_config["servicetype"], + port=_config["port"], txtRecord=txt_record, - callBack=_register_callback) + callBack=_register_callback, + ) atexit.register(_close_sd_ref) ready = select.select([SD_REF], [], []) if SD_REF in ready[0]: pybonjour.DNSServiceProcessResult(SD_REF) - elif _config.get('reset_on_change', False) or 'servicename' in changes: + elif _config.get("reset_on_change", False) or "servicename" in changes: # A change in 'servicename' requires a reset because we can only # directly update TXT records SD_REF.close() SD_REF = None - reset_wait = _config.get('reset_wait', 0) + reset_wait = _config.get("reset_wait", 0) if reset_wait > 0: time.sleep(reset_wait) SD_REF = pybonjour.DNSServiceRegister( name=servicename, - regtype=_config['servicetype'], - port=_config['port'], + regtype=_config["servicetype"], + port=_config["port"], txtRecord=txt_record, - callBack=_register_callback) + callBack=_register_callback, + ) ready = select.select([SD_REF], [], []) if SD_REF in ready[0]: pybonjour.DNSServiceProcessResult(SD_REF) else: - txt_record_raw = six.text_type(txt_record).encode('utf-8') + txt_record_raw = six.text_type(txt_record).encode("utf-8") pybonjour.DNSServiceUpdateRecord( - SD_REF, - RecordRef=None, - flags=0, - rdata=txt_record_raw) + SD_REF, RecordRef=None, flags=0, rdata=txt_record_raw + ) - ret.append({'tag': 'result', 'changes': changes}) + ret.append({"tag": "result", "changes": changes}) - if _config.get('copy_grains', False): + if _config.get("copy_grains", False): LAST_GRAINS = __grains__.copy() else: LAST_GRAINS = __grains__ diff --git a/salt/beacons/btmp.py b/salt/beacons/btmp.py index 806680d5528..adf7ab2fea6 100644 --- a/salt/beacons/btmp.py +++ b/salt/beacons/btmp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to fire events at failed login of users .. versionadded:: 2015.5.0 @@ -89,48 +89,52 @@ Match the event like so in the master config file: API key to post to Slack, a bot user is likely better suited for this. The :py:mod:`slack engine ` documentation has information on how to set up a bot user. -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import datetime import logging import os import struct -# Import Salt Libs -import salt.utils.stringutils -import salt.utils.files - # Import 3rd-party libs import salt.ext.six +import salt.utils.files + +# Import Salt Libs +import salt.utils.stringutils + # pylint: disable=import-error from salt.ext.six.moves import map + # pylint: enable=import-error -__virtualname__ = 'btmp' -BTMP = '/var/log/btmp' -FMT = b'hi32s4s32s256shhiii4i20x' +__virtualname__ = "btmp" +BTMP = "/var/log/btmp" +FMT = b"hi32s4s32s256shhiii4i20x" FIELDS = [ - 'type', - 'PID', - 'line', - 'inittab', - 'user', - 'hostname', - 'exit_status', - 'session', - 'time', - 'addr' - ] + "type", + "PID", + "line", + "inittab", + "user", + "hostname", + "exit_status", + "session", + "time", + "addr", +] SIZE = struct.calcsize(FMT) -LOC_KEY = 'btmp.loc' +LOC_KEY = "btmp.loc" log = logging.getLogger(__name__) # pylint: disable=import-error try: import dateutil.parser as dateutil_parser + _TIME_SUPPORTED = True except ImportError: _TIME_SUPPORTED = False @@ -143,120 +147,110 @@ def __virtual__(): def _validate_time_range(trange, status, msg): - ''' + """ Check time range - ''' + """ # If trange is empty, just return the current status & msg if not trange: return status, msg if not isinstance(trange, dict): status = False - msg = ('The time_range parameter for ' - 'btmp beacon must ' - 'be a dictionary.') + msg = "The time_range parameter for " "btmp beacon must " "be a dictionary." - if not all(k in trange for k in ('start', 'end')): + if not all(k in trange for k in ("start", "end")): status = False - msg = ('The time_range parameter for ' - 'btmp beacon must contain ' - 'start & end options.') + msg = ( + "The time_range parameter for " + "btmp beacon must contain " + "start & end options." + ) return status, msg def _gather_group_members(group, groups, users): - ''' + """ Gather group members - ''' - _group = __salt__['group.info'](group) + """ + _group = __salt__["group.info"](group) if not _group: - log.warning('Group %s does not exist, ignoring.', group) + log.warning("Group %s does not exist, ignoring.", group) return - for member in _group['members']: + for member in _group["members"]: if member not in users: users[member] = groups[group] def _check_time_range(time_range, now): - ''' + """ Check time range - ''' + """ if _TIME_SUPPORTED: - _start = dateutil_parser.parse(time_range['start']) - _end = dateutil_parser.parse(time_range['end']) + _start = dateutil_parser.parse(time_range["start"]) + _end = dateutil_parser.parse(time_range["end"]) return bool(_start <= now <= _end) else: - log.error('Dateutil is required.') + log.error("Dateutil is required.") return False def _get_loc(): - ''' + """ return the active file location - ''' + """ if LOC_KEY in __context__: return __context__[LOC_KEY] def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ vstatus = True - vmsg = 'Valid beacon configuration' + vmsg = "Valid beacon configuration" # Configuration for load beacon should be a list of dicts if not isinstance(config, list): vstatus = False - vmsg = ('Configuration for btmp beacon must ' - 'be a list.') + vmsg = "Configuration for btmp beacon must " "be a list." else: _config = {} list(map(_config.update, config)) - if 'users' in _config: - if not isinstance(_config['users'], dict): + if "users" in _config: + if not isinstance(_config["users"], dict): vstatus = False - vmsg = ('User configuration for btmp beacon must ' - 'be a dictionary.') + vmsg = "User configuration for btmp beacon must " "be a dictionary." else: - for user in _config['users']: - _time_range = _config['users'][user].get('time_range', {}) - vstatus, vmsg = _validate_time_range(_time_range, - vstatus, - vmsg) + for user in _config["users"]: + _time_range = _config["users"][user].get("time_range", {}) + vstatus, vmsg = _validate_time_range(_time_range, vstatus, vmsg) if not vstatus: return vstatus, vmsg - if 'groups' in _config: - if not isinstance(_config['groups'], dict): + if "groups" in _config: + if not isinstance(_config["groups"], dict): vstatus = False - vmsg = ('Group configuration for btmp beacon must ' - 'be a dictionary.') + vmsg = "Group configuration for btmp beacon must " "be a dictionary." else: - for group in _config['groups']: - _time_range = _config['groups'][group].get('time_range', {}) - vstatus, vmsg = _validate_time_range(_time_range, - vstatus, - vmsg) + for group in _config["groups"]: + _time_range = _config["groups"][group].get("time_range", {}) + vstatus, vmsg = _validate_time_range(_time_range, vstatus, vmsg) if not vstatus: return vstatus, vmsg - if 'defaults' in _config: - if not isinstance(_config['defaults'], dict): + if "defaults" in _config: + if not isinstance(_config["defaults"], dict): vstatus = False - vmsg = ('Defaults configuration for btmp beacon must ' - 'be a dictionary.') + vmsg = "Defaults configuration for btmp beacon must " "be a dictionary." else: - _time_range = _config['defaults'].get('time_range', {}) - vstatus, vmsg = _validate_time_range(_time_range, - vstatus, - vmsg) + _time_range = _config["defaults"].get("time_range", {}) + vstatus, vmsg = _validate_time_range(_time_range, vstatus, vmsg) if not vstatus: return vstatus, vmsg @@ -264,9 +258,9 @@ def validate(config): def beacon(config): - ''' + """ Read the last btmp file and return information on the failed logins - ''' + """ ret = [] users = {} @@ -274,16 +268,16 @@ def beacon(config): defaults = None for config_item in config: - if 'users' in config_item: - users = config_item['users'] + if "users" in config_item: + users = config_item["users"] - if 'groups' in config_item: - groups = config_item['groups'] + if "groups" in config_item: + groups = config_item["groups"] - if 'defaults' in config_item: - defaults = config_item['defaults'] + if "defaults" in config_item: + defaults = config_item["defaults"] - with salt.utils.files.fopen(BTMP, 'rb') as fp_: + with salt.utils.files.fopen(BTMP, "rb") as fp_: loc = __context__.get(LOC_KEY, 0) if loc == 0: fp_.seek(0, 2) @@ -304,27 +298,26 @@ def beacon(config): if isinstance(event[field], salt.ext.six.string_types): if isinstance(event[field], bytes): event[field] = salt.utils.stringutils.to_unicode(event[field]) - event[field] = event[field].strip('\x00') + event[field] = event[field].strip("\x00") for group in groups: _gather_group_members(group, groups, users) if users: - if event['user'] in users: - _user = users[event['user']] - if isinstance(_user, dict) and 'time_range' in _user: - if _check_time_range(_user['time_range'], now): + if event["user"] in users: + _user = users[event["user"]] + if isinstance(_user, dict) and "time_range" in _user: + if _check_time_range(_user["time_range"], now): ret.append(event) else: - if defaults and 'time_range' in defaults: - if _check_time_range(defaults['time_range'], - now): + if defaults and "time_range" in defaults: + if _check_time_range(defaults["time_range"], now): ret.append(event) else: ret.append(event) else: - if defaults and 'time_range' in defaults: - if _check_time_range(defaults['time_range'], now): + if defaults and "time_range" in defaults: + if _check_time_range(defaults["time_range"], now): ret.append(event) else: ret.append(event) diff --git a/salt/beacons/cert_info.py b/salt/beacons/cert_info.py index e9aa080623b..a1e71f9e2ea 100644 --- a/salt/beacons/cert_info.py +++ b/salt/beacons/cert_info.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to monitor certificate expiration dates from files on the filesystem. .. versionadded:: 3000 @@ -7,24 +7,26 @@ Beacon to monitor certificate expiration dates from files on the filesystem. :maintainer: :maturity: new :depends: OpenSSL -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals -from datetime import datetime + import logging +from datetime import datetime + +# pylint: enable=import-error,no-name-in-module,redefined-builtin,3rd-party-module-not-gated +import salt.utils.files # Import salt libs # pylint: disable=import-error,no-name-in-module,redefined-builtin,3rd-party-module-not-gated from salt.ext.six.moves import map as _map from salt.ext.six.moves import range as _range -# pylint: enable=import-error,no-name-in-module,redefined-builtin,3rd-party-module-not-gated -import salt.utils.files - # Import Third Party Libs try: from OpenSSL import crypto + HAS_OPENSSL = True except ImportError: HAS_OPENSSL = False @@ -33,7 +35,7 @@ log = logging.getLogger(__name__) DEFAULT_NOTIFY_DAYS = 45 -__virtualname__ = 'cert_info' +__virtualname__ = "cert_info" def __virtual__(): @@ -44,24 +46,26 @@ def __virtual__(): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ _config = {} list(_map(_config.update, config)) # Configuration for cert_info beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for cert_info beacon must be a list.') + return False, ("Configuration for cert_info beacon must be a list.") - if 'files' not in _config: - return False, ('Configuration for cert_info beacon ' - 'must contain files option.') - return True, 'Valid beacon configuration' + if "files" not in _config: + return ( + False, + ("Configuration for cert_info beacon must contain files option."), + ) + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Monitor the certificate files on the minion. Specify a notification threshold in days and only emit a beacon if any certificates are @@ -81,7 +85,7 @@ def beacon(config): - notify_days: 45 - interval: 86400 - ''' + """ ret = [] certificates = [] CryptoError = crypto.Error # pylint: disable=invalid-name @@ -89,75 +93,97 @@ def beacon(config): _config = {} list(_map(_config.update, config)) - global_notify_days = _config.get('notify_days', DEFAULT_NOTIFY_DAYS) + global_notify_days = _config.get("notify_days", DEFAULT_NOTIFY_DAYS) - for cert_path in _config.get('files', []): + for cert_path in _config.get("files", []): notify_days = global_notify_days if isinstance(cert_path, dict): try: - notify_days = cert_path[cert_path.keys()[0]].get('notify_days', global_notify_days) + notify_days = cert_path[cert_path.keys()[0]].get( + "notify_days", global_notify_days + ) cert_path = cert_path.keys()[0] except IndexError as exc: - log.error('Unable to load certificate %s (%s)', cert_path, exc) + log.error("Unable to load certificate %s (%s)", cert_path, exc) continue try: with salt.utils.files.fopen(cert_path) as fp_: cert = crypto.load_certificate(crypto.FILETYPE_PEM, fp_.read()) except (IOError, CryptoError) as exc: - log.error('Unable to load certificate %s (%s)', cert_path, exc) + log.error("Unable to load certificate %s (%s)", cert_path, exc) continue - cert_date = datetime.strptime(cert.get_notAfter().decode(encoding='UTF-8'), "%Y%m%d%H%M%SZ") + cert_date = datetime.strptime( + cert.get_notAfter().decode(encoding="UTF-8"), "%Y%m%d%H%M%SZ" + ) date_diff = (cert_date - datetime.today()).days - log.debug('Certificate %s expires in %s days.', cert_path, date_diff) + log.debug("Certificate %s expires in %s days.", cert_path, date_diff) if notify_days < 0 or date_diff <= notify_days: - log.debug('Certificate %s triggered beacon due to %s day notification threshold.', cert_path, notify_days) + log.debug( + "Certificate %s triggered beacon due to %s day notification threshold.", + cert_path, + notify_days, + ) extensions = [] for ext in _range(0, cert.get_extension_count()): extensions.append( { - 'ext_name': cert.get_extension(ext).get_short_name().decode(encoding='UTF-8'), - 'ext_data': str(cert.get_extension(ext)) + "ext_name": cert.get_extension(ext) + .get_short_name() + .decode(encoding="UTF-8"), + "ext_data": str(cert.get_extension(ext)), } ) certificates.append( { - 'cert_path': cert_path, - 'issuer': ','.join( - ['{0}="{1}"'.format( - t[0].decode(encoding='UTF-8'), - t[1].decode(encoding='UTF-8') - ) for t in cert.get_issuer().get_components()]), - 'issuer_dict': { - k.decode('UTF-8'): v.decode('UTF-8') for k, v in cert.get_issuer().get_components() + "cert_path": cert_path, + "issuer": ",".join( + [ + '{0}="{1}"'.format( + t[0].decode(encoding="UTF-8"), + t[1].decode(encoding="UTF-8"), + ) + for t in cert.get_issuer().get_components() + ] + ), + "issuer_dict": { + k.decode("UTF-8"): v.decode("UTF-8") + for k, v in cert.get_issuer().get_components() }, - 'notAfter_raw': cert.get_notAfter().decode(encoding='UTF-8'), - 'notAfter': cert_date.strftime("%Y-%m-%d %H:%M:%SZ"), - 'notBefore_raw': cert.get_notBefore().decode(encoding='UTF-8'), - 'notBefore': datetime.strptime( - cert.get_notBefore().decode(encoding='UTF-8'), "%Y%m%d%H%M%SZ" - ).strftime("%Y-%m-%d %H:%M:%SZ"), - 'serial_number': cert.get_serial_number(), - 'signature_algorithm': cert.get_signature_algorithm().decode(encoding='UTF-8'), - 'subject': ','.join( - ['{0}="{1}"'.format( - t[0].decode(encoding='UTF-8'), - t[1].decode(encoding='UTF-8') - ) for t in cert.get_subject().get_components()]), - 'subject_dict': { - k.decode('UTF-8'): v.decode('UTF-8') for k, v in cert.get_subject().get_components() + "notAfter_raw": cert.get_notAfter().decode(encoding="UTF-8"), + "notAfter": cert_date.strftime("%Y-%m-%d %H:%M:%SZ"), + "notBefore_raw": cert.get_notBefore().decode(encoding="UTF-8"), + "notBefore": datetime.strptime( + cert.get_notBefore().decode(encoding="UTF-8"), "%Y%m%d%H%M%SZ" + ).strftime("%Y-%m-%d %H:%M:%SZ"), + "serial_number": cert.get_serial_number(), + "signature_algorithm": cert.get_signature_algorithm().decode( + encoding="UTF-8" + ), + "subject": ",".join( + [ + '{0}="{1}"'.format( + t[0].decode(encoding="UTF-8"), + t[1].decode(encoding="UTF-8"), + ) + for t in cert.get_subject().get_components() + ] + ), + "subject_dict": { + k.decode("UTF-8"): v.decode("UTF-8") + for k, v in cert.get_subject().get_components() }, - 'version': cert.get_version(), - 'extensions': extensions, - 'has_expired': cert.has_expired() + "version": cert.get_version(), + "extensions": extensions, + "has_expired": cert.has_expired(), } ) if certificates: - ret.append({'certificates': certificates}) + ret.append({"certificates": certificates}) return ret diff --git a/salt/beacons/diskusage.py b/salt/beacons/diskusage.py index b16a5db6d2a..6a0199b56a6 100644 --- a/salt/beacons/diskusage.py +++ b/salt/beacons/diskusage.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to monitor disk usage. .. versionadded:: 2015.5.0 :depends: python-psutil -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging import re @@ -17,13 +18,14 @@ import salt.utils.platform # Import Third Party Libs try: import psutil + HAS_PSUTIL = True except ImportError: HAS_PSUTIL = False log = logging.getLogger(__name__) -__virtualname__ = 'diskusage' +__virtualname__ = "diskusage" def __virtual__(): @@ -34,18 +36,17 @@ def __virtual__(): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for diskusage beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for diskusage beacon ' - 'must be a list.') - return True, 'Valid beacon configuration' + return False, ("Configuration for diskusage beacon must be a list.") + return True, "Valid beacon configuration" def beacon(config): - r''' + r""" Monitor the disk usage of the minion Specify thresholds for each disk and only emit a beacon if any of them are @@ -84,7 +85,7 @@ def beacon(config): which means that if a regular expression matches another defined mount point, it will override the previously defined threshold. - ''' + """ parts = psutil.disk_partitions(all=True) ret = [] for mounts in config: @@ -93,16 +94,16 @@ def beacon(config): # Because we're using regular expressions # if our mount doesn't end with a $, insert one. mount_re = mount - if not mount.endswith('$'): - mount_re = '{0}$'.format(mount) + if not mount.endswith("$"): + mount_re = "{0}$".format(mount) if salt.utils.platform.is_windows(): # mount_re comes in formatted with a $ at the end # can be `C:\\$` or `C:\\\\$` # re string must be like `C:\\\\` regardless of \\ or \\\\ # also, psutil returns uppercase - mount_re = re.sub(r':\\\$', r':\\\\', mount_re) - mount_re = re.sub(r':\\\\\$', r':\\\\', mount_re) + mount_re = re.sub(r":\\\$", r":\\\\", mount_re) + mount_re = re.sub(r":\\\\\$", r":\\\\", mount_re) mount_re = mount_re.upper() for part in parts: @@ -112,14 +113,14 @@ def beacon(config): try: _current_usage = psutil.disk_usage(_mount) except OSError: - log.warning('%s is not a valid mount point.', _mount) + log.warning("%s is not a valid mount point.", _mount) continue current_usage = _current_usage.percent monitor_usage = mounts[mount] - if '%' in monitor_usage: - monitor_usage = re.sub('%', '', monitor_usage) + if "%" in monitor_usage: + monitor_usage = re.sub("%", "", monitor_usage) monitor_usage = float(monitor_usage) if current_usage >= monitor_usage: - ret.append({'diskusage': current_usage, 'mount': _mount}) + ret.append({"diskusage": current_usage, "mount": _mount}) return ret diff --git a/salt/beacons/glxinfo.py b/salt/beacons/glxinfo.py index 48b8709dfbb..b82e4ceacd0 100644 --- a/salt/beacons/glxinfo.py +++ b/salt/beacons/glxinfo.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to emit when a display is available to a linux machine .. versionadded:: 2016.3.0 -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging # Salt libs @@ -15,14 +16,14 @@ from salt.ext.six.moves import map log = logging.getLogger(__name__) -__virtualname__ = 'glxinfo' +__virtualname__ = "glxinfo" last_state = {} def __virtual__(): - which_result = salt.utils.path.which('glxinfo') + which_result = salt.utils.path.which("glxinfo") if which_result is None: return False else: @@ -30,24 +31,29 @@ def __virtual__(): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for glxinfo beacon should be a dictionary if not isinstance(config, list): - return False, ('Configuration for glxinfo beacon must be a list.') + return False, ("Configuration for glxinfo beacon must be a list.") _config = {} list(map(_config.update, config)) - if 'user' not in _config: - return False, ('Configuration for glxinfo beacon must ' - 'include a user as glxinfo is not available to root.') - return True, 'Valid beacon configuration' + if "user" not in _config: + return ( + False, + ( + "Configuration for glxinfo beacon must " + "include a user as glxinfo is not available to root." + ), + ) + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Emit the status of a connected display to the minion Mainly this is used to detect when the display fails to connect @@ -60,23 +66,24 @@ def beacon(config): - user: frank - screen_event: True - ''' + """ - log.trace('glxinfo beacon starting') + log.trace("glxinfo beacon starting") ret = [] _config = {} list(map(_config.update, config)) - retcode = __salt__['cmd.retcode']('DISPLAY=:0 glxinfo', - runas=_config['user'], python_shell=True) + retcode = __salt__["cmd.retcode"]( + "DISPLAY=:0 glxinfo", runas=_config["user"], python_shell=True + ) - if 'screen_event' in _config and _config['screen_event']: - last_value = last_state.get('screen_available', False) + if "screen_event" in _config and _config["screen_event"]: + last_value = last_state.get("screen_available", False) screen_available = retcode == 0 - if last_value != screen_available or 'screen_available' not in last_state: - ret.append({'tag': 'screen_event', 'screen_available': screen_available}) + if last_value != screen_available or "screen_available" not in last_state: + ret.append({"tag": "screen_event", "screen_available": screen_available}) - last_state['screen_available'] = screen_available + last_state["screen_available"] = screen_available return ret diff --git a/salt/beacons/haproxy.py b/salt/beacons/haproxy.py index 172e86d0653..dbe3ee0a5cc 100644 --- a/salt/beacons/haproxy.py +++ b/salt/beacons/haproxy.py @@ -1,66 +1,69 @@ # -*- coding: utf-8 -*- -''' +""" Watch current connections of haproxy server backends. Fire an event when over a specified threshold. .. versionadded:: 2016.11.0 -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging + from salt.ext.six.moves import map log = logging.getLogger(__name__) -__virtualname__ = 'haproxy' +__virtualname__ = "haproxy" def __virtual__(): - ''' + """ Only load the module if haproxyctl module is installed - ''' - if 'haproxy.get_sessions' in __salt__: + """ + if "haproxy.get_sessions" in __salt__: return __virtualname__ else: - log.debug('Not loading haproxy beacon') + log.debug("Not loading haproxy beacon") return False def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ if not isinstance(config, list): - return False, ('Configuration for haproxy beacon must ' - 'be a list.') + return False, ("Configuration for haproxy beacon must be a list.") else: _config = {} list(map(_config.update, config)) - if 'backends' not in _config: - return False, ('Configuration for haproxy beacon ' - 'requires backends.') + if "backends" not in _config: + return False, ("Configuration for haproxy beacon requires backends.") else: - if not isinstance(_config['backends'], dict): - return False, ('Backends for haproxy beacon ' - 'must be a dictionary.') + if not isinstance(_config["backends"], dict): + return False, ("Backends for haproxy beacon must be a dictionary.") else: - for backend in _config['backends']: - log.debug('_config %s', _config['backends'][backend]) - if 'servers' not in _config['backends'][backend]: - return False, ('Backends for haproxy beacon ' - 'require servers.') + for backend in _config["backends"]: + log.debug("_config %s", _config["backends"][backend]) + if "servers" not in _config["backends"][backend]: + return ( + False, + ("Backends for haproxy beacon require servers."), + ) else: - _servers = _config['backends'][backend]['servers'] + _servers = _config["backends"][backend]["servers"] if not isinstance(_servers, list): - return False, ('Servers for haproxy beacon ' - 'must be a list.') - return True, 'Valid beacon configuration' + return ( + False, + ("Servers for haproxy beacon must be a list."), + ) + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Check if current number of sessions of a server for a specific haproxy backend is over a defined threshold. @@ -75,27 +78,30 @@ def beacon(config): - web1 - web2 - interval: 120 - ''' + """ ret = [] _config = {} list(map(_config.update, config)) - for backend in _config.get('backends', ()): - backend_config = _config['backends'][backend] - threshold = backend_config['threshold'] - for server in backend_config['servers']: - scur = __salt__['haproxy.get_sessions'](server, backend) + for backend in _config.get("backends", ()): + backend_config = _config["backends"][backend] + threshold = backend_config["threshold"] + for server in backend_config["servers"]: + scur = __salt__["haproxy.get_sessions"](server, backend) if scur: if int(scur) > int(threshold): - _server = {'server': server, - 'scur': scur, - 'threshold': threshold, - } - log.debug('Emit because %s > %s' - ' for %s in %s', scur, - threshold, - server, - backend) + _server = { + "server": server, + "scur": scur, + "threshold": threshold, + } + log.debug( + "Emit because %s > %s" " for %s in %s", + scur, + threshold, + server, + backend, + ) ret.append(_server) return ret diff --git a/salt/beacons/inotify.py b/salt/beacons/inotify.py index 422c9c4559f..16420e5c846 100644 --- a/salt/beacons/inotify.py +++ b/salt/beacons/inotify.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Watch files and translate the changes into salt events :depends: - pyinotify Python module >= 0.9.5 @@ -13,9 +13,10 @@ Watch files and translate the changes into salt events :note: The `inotify` beacon only works on OSes that have `inotify` kernel support. -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import collections import fnmatch import logging @@ -24,25 +25,28 @@ import re # Import salt libs import salt.ext.six + # pylint: disable=import-error from salt.ext.six.moves import map + # pylint: enable=import-error # Import third party libs try: import pyinotify + HAS_PYINOTIFY = True DEFAULT_MASK = pyinotify.IN_CREATE | pyinotify.IN_DELETE | pyinotify.IN_MODIFY MASKS = {} for var in dir(pyinotify): - if var.startswith('IN_'): + if var.startswith("IN_"): key = var[3:].lower() MASKS[key] = getattr(pyinotify, var) except ImportError: HAS_PYINOTIFY = False DEFAULT_MASK = None -__virtualname__ = 'inotify' +__virtualname__ = "inotify" log = logging.getLogger(__name__) @@ -54,108 +58,146 @@ def __virtual__(): def _get_mask(mask): - ''' + """ Return the int that represents the mask - ''' + """ return MASKS.get(mask, 0) def _enqueue(revent): - ''' + """ Enqueue the event - ''' - __context__['inotify.queue'].append(revent) + """ + __context__["inotify.queue"].append(revent) def _get_notifier(config): - ''' + """ Check the context for the notifier and construct it if not present - ''' - if 'inotify.notifier' not in __context__: - __context__['inotify.queue'] = collections.deque() + """ + if "inotify.notifier" not in __context__: + __context__["inotify.queue"] = collections.deque() wm = pyinotify.WatchManager() - __context__['inotify.notifier'] = pyinotify.Notifier(wm, _enqueue) - if ('coalesce' in config and - isinstance(config['coalesce'], bool) and - config['coalesce']): - __context__['inotify.notifier'].coalesce_events() - return __context__['inotify.notifier'] + __context__["inotify.notifier"] = pyinotify.Notifier(wm, _enqueue) + if ( + "coalesce" in config + and isinstance(config["coalesce"], bool) + and config["coalesce"] + ): + __context__["inotify.notifier"].coalesce_events() + return __context__["inotify.notifier"] def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ VALID_MASK = [ - 'access', - 'attrib', - 'close_nowrite', - 'close_write', - 'create', - 'delete', - 'delete_self', - 'excl_unlink', - 'ignored', - 'modify', - 'moved_from', - 'moved_to', - 'move_self', - 'oneshot', - 'onlydir', - 'open', - 'unmount' + "access", + "attrib", + "close_nowrite", + "close_write", + "create", + "delete", + "delete_self", + "excl_unlink", + "ignored", + "modify", + "moved_from", + "moved_to", + "move_self", + "oneshot", + "onlydir", + "open", + "unmount", ] # Configuration for inotify beacon should be a dict of dicts if not isinstance(config, list): - return False, 'Configuration for inotify beacon must be a list.' + return False, "Configuration for inotify beacon must be a list." else: _config = {} list(map(_config.update, config)) - if 'files' not in _config: - return False, 'Configuration for inotify beacon must include files.' + if "files" not in _config: + return False, "Configuration for inotify beacon must include files." else: - if not isinstance(_config['files'], dict): - return False, ('Configuration for inotify beacon invalid, ' - 'files must be a dict.') + if not isinstance(_config["files"], dict): + return ( + False, + ( + "Configuration for inotify beacon invalid, " + "files must be a dict." + ), + ) - for path in _config.get('files'): + for path in _config.get("files"): - if not isinstance(_config['files'][path], dict): - return False, ('Configuration for inotify beacon must ' - 'be a list of dictionaries.') + if not isinstance(_config["files"][path], dict): + return ( + False, + ( + "Configuration for inotify beacon must " + "be a list of dictionaries." + ), + ) else: - if not any(j in ['mask', - 'recurse', - 'auto_add'] for j in _config['files'][path]): - return False, ('Configuration for inotify beacon must ' - 'contain mask, recurse or auto_add items.') + if not any( + j in ["mask", "recurse", "auto_add"] + for j in _config["files"][path] + ): + return ( + False, + ( + "Configuration for inotify beacon must " + "contain mask, recurse or auto_add items." + ), + ) - if 'auto_add' in _config['files'][path]: - if not isinstance(_config['files'][path]['auto_add'], bool): - return False, ('Configuration for inotify beacon ' - 'auto_add must be boolean.') + if "auto_add" in _config["files"][path]: + if not isinstance(_config["files"][path]["auto_add"], bool): + return ( + False, + ( + "Configuration for inotify beacon " + "auto_add must be boolean." + ), + ) - if 'recurse' in _config['files'][path]: - if not isinstance(_config['files'][path]['recurse'], bool): - return False, ('Configuration for inotify beacon ' - 'recurse must be boolean.') + if "recurse" in _config["files"][path]: + if not isinstance(_config["files"][path]["recurse"], bool): + return ( + False, + ( + "Configuration for inotify beacon " + "recurse must be boolean." + ), + ) - if 'mask' in _config['files'][path]: - if not isinstance(_config['files'][path]['mask'], list): - return False, ('Configuration for inotify beacon ' - 'mask must be list.') - for mask in _config['files'][path]['mask']: + if "mask" in _config["files"][path]: + if not isinstance(_config["files"][path]["mask"], list): + return ( + False, + ( + "Configuration for inotify beacon " + "mask must be list." + ), + ) + for mask in _config["files"][path]["mask"]: if mask not in VALID_MASK: - return False, ('Configuration for inotify beacon ' - 'invalid mask option {0}.'.format(mask)) - return True, 'Valid beacon configuration' + return ( + False, + ( + "Configuration for inotify beacon " + "invalid mask option {0}.".format(mask) + ), + ) + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Watch the configured files Example Config @@ -218,7 +260,7 @@ def beacon(config): This option is top-level (at the same level as the path) and therefore affects all paths that are being watched. This is due to this option being at the Notifier level in pyinotify. - ''' + """ _config = {} list(map(_config.update, config)) @@ -230,34 +272,33 @@ def beacon(config): if notifier.check_events(1): notifier.read_events() notifier.process_events() - queue = __context__['inotify.queue'] + queue = __context__["inotify.queue"] while queue: event = queue.popleft() _append = True # Find the matching path in config path = event.path - while path != '/': - if path in _config.get('files', {}): + while path != "/": + if path in _config.get("files", {}): break path = os.path.dirname(path) - excludes = _config['files'][path].get('exclude', '') + excludes = _config["files"][path].get("exclude", "") if excludes and isinstance(excludes, list): for exclude in excludes: if isinstance(exclude, dict): _exclude = next(iter(exclude)) - if exclude[_exclude].get('regex', False): + if exclude[_exclude].get("regex", False): try: if re.search(_exclude, event.pathname): _append = False except Exception: # pylint: disable=broad-except - log.warning('Failed to compile regex: %s', - _exclude) + log.warning("Failed to compile regex: %s", _exclude) else: exclude = _exclude - elif '*' in exclude: + elif "*" in exclude: if fnmatch.fnmatch(event.pathname, exclude): _append = False else: @@ -265,12 +306,14 @@ def beacon(config): _append = False if _append: - sub = {'tag': event.path, - 'path': event.pathname, - 'change': event.maskname} + sub = { + "tag": event.path, + "path": event.pathname, + "change": event.maskname, + } ret.append(sub) else: - log.info('Excluding %s from event for %s', event.pathname, path) + log.info("Excluding %s from event for %s", event.pathname, path) # Get paths currently being watched current = set() @@ -279,10 +322,10 @@ def beacon(config): # Update existing watches and add new ones # TODO: make the config handle more options - for path in _config.get('files', ()): + for path in _config.get("files", ()): - if isinstance(_config['files'][path], dict): - mask = _config['files'][path].get('mask', DEFAULT_MASK) + if isinstance(_config["files"][path], dict): + mask = _config["files"][path].get("mask", DEFAULT_MASK) if isinstance(mask, list): r_mask = 0 for sub in mask: @@ -292,8 +335,8 @@ def beacon(config): else: r_mask = mask mask = r_mask - rec = _config['files'][path].get('recurse', False) - auto_add = _config['files'][path].get('auto_add', False) + rec = _config["files"][path].get("recurse", False) + auto_add = _config["files"][path].get("auto_add", False) else: mask = DEFAULT_MASK rec = False @@ -310,7 +353,7 @@ def beacon(config): if update: wm.update_watch(wd, mask=mask, rec=rec, auto_add=auto_add) elif os.path.exists(path): - excludes = _config['files'][path].get('exclude', '') + excludes = _config["files"][path].get("exclude", "") excl = None if isinstance(excludes, list): excl = [] @@ -328,6 +371,6 @@ def beacon(config): def close(config): - if 'inotify.notifier' in __context__: - __context__['inotify.notifier'].stop() - del __context__['inotify.notifier'] + if "inotify.notifier" in __context__: + __context__["inotify.notifier"].stop() + del __context__["inotify.notifier"] diff --git a/salt/beacons/journald.py b/salt/beacons/journald.py index 9fdb186bf70..e52be2540c6 100644 --- a/salt/beacons/journald.py +++ b/salt/beacons/journald.py @@ -1,27 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" A simple beacon to watch journald for specific entries -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals +import logging + +import salt.ext.six + # Import salt libs import salt.utils.data -import salt.ext.six from salt.ext.six.moves import map # Import third party libs try: import systemd.journal # pylint: disable=no-name-in-module + HAS_SYSTEMD = True except ImportError: HAS_SYSTEMD = False -import logging + log = logging.getLogger(__name__) -__virtualname__ = 'journald' +__virtualname__ = "journald" def __virtual__(): @@ -31,38 +35,43 @@ def __virtual__(): def _get_journal(): - ''' + """ Return the active running journal object - ''' - if 'systemd.journald' in __context__: - return __context__['systemd.journald'] - __context__['systemd.journald'] = systemd.journal.Reader() + """ + if "systemd.journald" in __context__: + return __context__["systemd.journald"] + __context__["systemd.journald"] = systemd.journal.Reader() # get to the end of the journal - __context__['systemd.journald'].seek_tail() - __context__['systemd.journald'].get_previous() - return __context__['systemd.journald'] + __context__["systemd.journald"].seek_tail() + __context__["systemd.journald"].get_previous() + return __context__["systemd.journald"] def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for journald beacon should be a list of dicts if not isinstance(config, list): - return (False, 'Configuration for journald beacon must be a list.') + return (False, "Configuration for journald beacon must be a list.") else: _config = {} list(map(_config.update, config)) - for name in _config.get('services', {}): - if not isinstance(_config['services'][name], dict): - return False, ('Services configuration for journald beacon ' - 'must be a list of dictionaries.') - return True, 'Valid beacon configuration' + for name in _config.get("services", {}): + if not isinstance(_config["services"][name], dict): + return ( + False, + ( + "Services configuration for journald beacon " + "must be a list of dictionaries." + ), + ) + return True, "Valid beacon configuration" def beacon(config): - ''' + """ The journald beacon allows for the systemd journal to be parsed and linked objects to be turned into events. @@ -76,7 +85,7 @@ def beacon(config): sshd: SYSLOG_IDENTIFIER: sshd PRIORITY: 6 - ''' + """ ret = [] journal = _get_journal() @@ -88,17 +97,17 @@ def beacon(config): if not cur: break - for name in _config.get('services', {}): + for name in _config.get("services", {}): n_flag = 0 - for key in _config['services'][name]: + for key in _config["services"][name]: if isinstance(key, salt.ext.six.string_types): key = salt.utils.data.decode(key) if key in cur: - if _config['services'][name][key] == cur[key]: + if _config["services"][name][key] == cur[key]: n_flag += 1 - if n_flag == len(_config['services'][name]): + if n_flag == len(_config["services"][name]): # Match! sub = salt.utils.data.simple_types_filter(cur) - sub.update({'tag': name}) + sub.update({"tag": name}) ret.append(sub) return ret diff --git a/salt/beacons/load.py b/salt/beacons/load.py index 67f488f00da..79c7b5eb0cc 100644 --- a/salt/beacons/load.py +++ b/salt/beacons/load.py @@ -1,23 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to emit system load averages -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging import os # Import Salt libs import salt.utils.platform -from salt.ext.six.moves import map # Import Py3 compat -from salt.ext.six.moves import zip +from salt.ext.six.moves import map, zip log = logging.getLogger(__name__) -__virtualname__ = 'load' +__virtualname__ = "load" LAST_STATUS = {} @@ -30,53 +30,76 @@ def __virtual__(): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for load beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for load beacon must be a list.') + return False, ("Configuration for load beacon must be a list.") else: _config = {} list(map(_config.update, config)) - if 'emitatstartup' in _config: - if not isinstance(_config['emitatstartup'], bool): - return False, ('Configuration for load beacon option ' - 'emitatstartup must be a boolean.') + if "emitatstartup" in _config: + if not isinstance(_config["emitatstartup"], bool): + return ( + False, + ( + "Configuration for load beacon option " + "emitatstartup must be a boolean." + ), + ) - if 'onchangeonly' in _config: - if not isinstance(_config['onchangeonly'], bool): - return False, ('Configuration for load beacon option ' - 'onchangeonly must be a boolean.') + if "onchangeonly" in _config: + if not isinstance(_config["onchangeonly"], bool): + return ( + False, + ( + "Configuration for load beacon option " + "onchangeonly must be a boolean." + ), + ) - if 'averages' not in _config: - return False, ('Averages configuration is required' - ' for load beacon.') + if "averages" not in _config: + return False, ("Averages configuration is required for load beacon.") else: - if not any(j in ['1m', '5m', '15m'] for j - in _config.get('averages', {})): - return False, ('Averages configuration for load beacon ' - 'must contain 1m, 5m or 15m items.') + if not any(j in ["1m", "5m", "15m"] for j in _config.get("averages", {})): + return ( + False, + ( + "Averages configuration for load beacon " + "must contain 1m, 5m or 15m items." + ), + ) - for item in ['1m', '5m', '15m']: - if not isinstance(_config['averages'][item], list): - return False, ('Averages configuration for load beacon: ' - '1m, 5m and 15m items must be ' - 'a list of two items.') + for item in ["1m", "5m", "15m"]: + if not isinstance(_config["averages"][item], list): + return ( + False, + ( + "Averages configuration for load beacon: " + "1m, 5m and 15m items must be " + "a list of two items." + ), + ) else: - if len(_config['averages'][item]) != 2: - return False, ('Configuration for load beacon: ' - '1m, 5m and 15m items must be ' - 'a list of two items.') + if len(_config["averages"][item]) != 2: + return ( + False, + ( + "Configuration for load beacon: " + "1m, 5m and 15m items must be " + "a list of two items." + ), + ) - return True, 'Valid beacon configuration' + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Emit the load averages of this host. Specify thresholds for each load average @@ -108,70 +131,79 @@ def beacon(config): - emitatstartup: True - onchangeonly: False - ''' - log.trace('load beacon starting') + """ + log.trace("load beacon starting") _config = {} list(map(_config.update, config)) # Default config if not present - if 'emitatstartup' not in _config: - _config['emitatstartup'] = True - if 'onchangeonly' not in _config: - _config['onchangeonly'] = False + if "emitatstartup" not in _config: + _config["emitatstartup"] = True + if "onchangeonly" not in _config: + _config["onchangeonly"] = False ret = [] avgs = os.getloadavg() - avg_keys = ['1m', '5m', '15m'] + avg_keys = ["1m", "5m", "15m"] avg_dict = dict(zip(avg_keys, avgs)) - if _config['onchangeonly']: + if _config["onchangeonly"]: if not LAST_STATUS: - for k in ['1m', '5m', '15m']: + for k in ["1m", "5m", "15m"]: LAST_STATUS[k] = avg_dict[k] - if not _config['emitatstartup']: + if not _config["emitatstartup"]: log.debug("Don't emit because emitatstartup is False") return ret send_beacon = False # Check each entry for threshold - for k in ['1m', '5m', '15m']: - if k in _config.get('averages', {}): - if _config['onchangeonly']: + for k in ["1m", "5m", "15m"]: + if k in _config.get("averages", {}): + if _config["onchangeonly"]: # Emit if current is more that threshold and old value less # that threshold - if float(avg_dict[k]) > float(_config['averages'][k][1]) and \ - float(LAST_STATUS[k]) < float(_config['averages'][k][1]): - log.debug('Emit because %f > %f and last was ' - '%f', float(avg_dict[k]), - float(_config['averages'][k][1]), - float(LAST_STATUS[k])) + if float(avg_dict[k]) > float(_config["averages"][k][1]) and float( + LAST_STATUS[k] + ) < float(_config["averages"][k][1]): + log.debug( + "Emit because %f > %f and last was " "%f", + float(avg_dict[k]), + float(_config["averages"][k][1]), + float(LAST_STATUS[k]), + ) send_beacon = True break # Emit if current is less that threshold and old value more # that threshold - if float(avg_dict[k]) < float(_config['averages'][k][0]) and \ - float(LAST_STATUS[k]) > float(_config['averages'][k][0]): - log.debug('Emit because %f < %f and last was' - '%f', float(avg_dict[k]), - float(_config['averages'][k][0]), - float(LAST_STATUS[k])) + if float(avg_dict[k]) < float(_config["averages"][k][0]) and float( + LAST_STATUS[k] + ) > float(_config["averages"][k][0]): + log.debug( + "Emit because %f < %f and last was" "%f", + float(avg_dict[k]), + float(_config["averages"][k][0]), + float(LAST_STATUS[k]), + ) send_beacon = True break else: # Emit no matter LAST_STATUS - if float(avg_dict[k]) < float(_config['averages'][k][0]) or \ - float(avg_dict[k]) > float(_config['averages'][k][1]): - log.debug('Emit because %f < %f or > ' - '%f', float(avg_dict[k]), - float(_config['averages'][k][0]), - float(_config['averages'][k][1])) + if float(avg_dict[k]) < float(_config["averages"][k][0]) or float( + avg_dict[k] + ) > float(_config["averages"][k][1]): + log.debug( + "Emit because %f < %f or > " "%f", + float(avg_dict[k]), + float(_config["averages"][k][0]), + float(_config["averages"][k][1]), + ) send_beacon = True break - if _config['onchangeonly']: - for k in ['1m', '5m', '15m']: + if _config["onchangeonly"]: + for k in ["1m", "5m", "15m"]: LAST_STATUS[k] = avg_dict[k] if send_beacon: diff --git a/salt/beacons/log_beacon.py b/salt/beacons/log_beacon.py index 59d82032f4c..1c2f1716965 100644 --- a/salt/beacons/log_beacon.py +++ b/salt/beacons/log_beacon.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to fire events at specific log messages. .. versionadded:: 2017.7.0 -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging # Import salt libs @@ -15,21 +16,21 @@ import salt.utils.files import salt.utils.platform from salt.ext.six.moves import map - try: import re + HAS_REGEX = True except ImportError: HAS_REGEX = False -__virtualname__ = 'log' -LOC_KEY = 'log.loc' +__virtualname__ = "log" +LOC_KEY = "log.loc" SKEL = {} -SKEL['tag'] = '' -SKEL['match'] = 'no' -SKEL['raw'] = '' -SKEL['error'] = '' +SKEL["tag"] = "" +SKEL["match"] = "no" +SKEL["raw"] = "" +SKEL["error"] = "" log = logging.getLogger(__name__) @@ -42,33 +43,32 @@ def __virtual__(): def _get_loc(): - ''' + """ return the active file location - ''' + """ if LOC_KEY in __context__: return __context__[LOC_KEY] def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ _config = {} list(map(_config.update, config)) # Configuration for log beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for log beacon must be a list.') + return False, ("Configuration for log beacon must be a list.") - if 'file' not in _config: - return False, ('Configuration for log beacon ' - 'must contain file option.') - return True, 'Valid beacon configuration' + if "file" not in _config: + return False, ("Configuration for log beacon must contain file option.") + return True, "Valid beacon configuration" # TODO: match values should be returned in the event def beacon(config): - ''' + """ Read the log file and return match whole string .. code-block:: yaml @@ -85,20 +85,20 @@ def beacon(config): regex matching is based on the `re`_ module .. _re: https://docs.python.org/3.6/library/re.html#regular-expression-syntax - ''' + """ _config = {} list(map(_config.update, config)) ret = [] - if 'file' not in _config: + if "file" not in _config: event = SKEL.copy() - event['tag'] = 'global' - event['error'] = 'file not defined in config' + event["tag"] = "global" + event["error"] = "file not defined in config" ret.append(event) return ret - with salt.utils.files.fopen(_config['file'], 'r') as fp_: + with salt.utils.files.fopen(_config["file"], "r") as fp_: loc = __context__.get(LOC_KEY, 0) if loc == 0: fp_.seek(0, 2) @@ -110,20 +110,20 @@ def beacon(config): fp_.seek(loc) txt = fp_.read() - log.info('txt %s', txt) + log.info("txt %s", txt) d = {} - for tag in _config.get('tags', {}): - if 'regex' not in _config['tags'][tag]: + for tag in _config.get("tags", {}): + if "regex" not in _config["tags"][tag]: continue - if len(_config['tags'][tag]['regex']) < 1: + if len(_config["tags"][tag]["regex"]) < 1: continue try: - d[tag] = re.compile(r'{0}'.format(_config['tags'][tag]['regex'])) + d[tag] = re.compile(r"{0}".format(_config["tags"][tag]["regex"])) except Exception as e: # pylint: disable=broad-except event = SKEL.copy() - event['tag'] = tag - event['error'] = 'bad regex' + event["tag"] = tag + event["error"] = "bad regex" ret.append(event) for line in txt.splitlines(): @@ -132,13 +132,13 @@ def beacon(config): m = reg.match(line) if m: event = SKEL.copy() - event['tag'] = tag - event['raw'] = line - event['match'] = 'yes' + event["tag"] = tag + event["raw"] = line + event["match"] = "yes" ret.append(event) except Exception: # pylint: disable=broad-except event = SKEL.copy() - event['tag'] = tag - event['error'] = 'bad match' + event["tag"] = tag + event["error"] = "bad match" ret.append(event) return ret diff --git a/salt/beacons/memusage.py b/salt/beacons/memusage.py index 0d4a2770a42..29c9d0c41fc 100644 --- a/salt/beacons/memusage.py +++ b/salt/beacons/memusage.py @@ -1,28 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to monitor memory usage. .. versionadded:: 2016.3.0 :depends: python-psutil -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging import re + from salt.ext.six.moves import map # Import Third Party Libs try: import psutil + HAS_PSUTIL = True except ImportError: HAS_PSUTIL = False log = logging.getLogger(__name__) -__virtualname__ = 'memusage' +__virtualname__ = "memusage" def __virtual__(): @@ -33,26 +36,24 @@ def __virtual__(): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for memusage beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for memusage ' - 'beacon must be a list.') + return False, ("Configuration for memusage beacon must be a list.") else: _config = {} list(map(_config.update, config)) - if 'percent' not in _config: - return False, ('Configuration for memusage beacon ' - 'requires percent.') + if "percent" not in _config: + return False, ("Configuration for memusage beacon requires percent.") - return True, 'Valid beacon configuration' + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Monitor the memory usage of the minion Specify thresholds for percent used and only emit a beacon @@ -63,7 +64,7 @@ def beacon(config): beacons: memusage: - percent: 63% - ''' + """ ret = [] _config = {} @@ -72,10 +73,10 @@ def beacon(config): _current_usage = psutil.virtual_memory() current_usage = _current_usage.percent - monitor_usage = _config['percent'] - if '%' in monitor_usage: - monitor_usage = re.sub('%', '', monitor_usage) + monitor_usage = _config["percent"] + if "%" in monitor_usage: + monitor_usage = re.sub("%", "", monitor_usage) monitor_usage = float(monitor_usage) if current_usage >= monitor_usage: - ret.append({'memusage': current_usage}) + ret.append({"memusage": current_usage}) return ret diff --git a/salt/beacons/napalm_beacon.py b/salt/beacons/napalm_beacon.py index 15a2ca972a2..21bb7057226 100644 --- a/salt/beacons/napalm_beacon.py +++ b/salt/beacons/napalm_beacon.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Watch NAPALM functions and fire events on specific triggers =========================================================== @@ -166,49 +166,51 @@ Event structure example: The event examplified above has been fired when the device identified by the Minion id ``edge01.bjm01`` has been synchronized with a NTP server at a stratum level greater than 5. -''' +""" from __future__ import absolute_import, unicode_literals +import logging + # Import Python std lib import re -import logging + +import salt.utils.napalm # Import Salt modules from salt.ext import six -import salt.utils.napalm log = logging.getLogger(__name__) -_numeric_regex = re.compile(r'^(<|>|<=|>=|==|!=)\s*(\d+(\.\d+){0,1})$') +_numeric_regex = re.compile(r"^(<|>|<=|>=|==|!=)\s*(\d+(\.\d+){0,1})$") # the numeric regex will match the right operand, e.g '>= 20', '< 100', '!= 20', '< 1000.12' etc. _numeric_operand = { - '<': '__lt__', - '>': '__gt__', - '>=': '__ge__', - '<=': '__le__', - '==': '__eq__', - '!=': '__ne__', + "<": "__lt__", + ">": "__gt__", + ">=": "__ge__", + "<=": "__le__", + "==": "__eq__", + "!=": "__ne__", } # mathematical operand - private method map -__virtualname__ = 'napalm' +__virtualname__ = "napalm" def __virtual__(): - ''' + """ This beacon can only work when running under a regular or a proxy minion, managed through napalm. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) def _compare(cur_cmp, cur_struct): - ''' + """ Compares two objects and return a boolean value when there's a match. - ''' + """ if isinstance(cur_cmp, dict) and isinstance(cur_struct, dict): - log.debug('Comparing dict to dict') + log.debug("Comparing dict to dict") for cmp_key, cmp_value in six.iteritems(cur_cmp): - if cmp_key == '*': + if cmp_key == "*": # matches any key from the source dictionary if isinstance(cmp_value, dict): found = False @@ -237,69 +239,81 @@ def _compare(cur_cmp, cur_struct): else: return _compare(cmp_value, cur_struct[cmp_key]) elif isinstance(cur_cmp, (list, tuple)) and isinstance(cur_struct, (list, tuple)): - log.debug('Comparing list to list') + log.debug("Comparing list to list") found = False for cur_cmp_ele in cur_cmp: for cur_struct_ele in cur_struct: found |= _compare(cur_cmp_ele, cur_struct_ele) return found elif isinstance(cur_cmp, dict) and isinstance(cur_struct, (list, tuple)): - log.debug('Comparing dict to list (of dicts?)') + log.debug("Comparing dict to list (of dicts?)") found = False for cur_struct_ele in cur_struct: found |= _compare(cur_cmp, cur_struct_ele) return found elif isinstance(cur_cmp, bool) and isinstance(cur_struct, bool): - log.debug('Comparing booleans: %s ? %s', cur_cmp, cur_struct) + log.debug("Comparing booleans: %s ? %s", cur_cmp, cur_struct) return cur_cmp == cur_struct - elif isinstance(cur_cmp, (six.string_types, six.text_type)) and \ - isinstance(cur_struct, (six.string_types, six.text_type)): - log.debug('Comparing strings (and regex?): %s ? %s', cur_cmp, cur_struct) + elif isinstance(cur_cmp, (six.string_types, six.text_type)) and isinstance( + cur_struct, (six.string_types, six.text_type) + ): + log.debug("Comparing strings (and regex?): %s ? %s", cur_cmp, cur_struct) # Trying literal match matched = re.match(cur_cmp, cur_struct, re.I) if matched: return True return False - elif isinstance(cur_cmp, (six.integer_types, float)) and \ - isinstance(cur_struct, (six.integer_types, float)): - log.debug('Comparing numeric values: %d ? %d', cur_cmp, cur_struct) + elif isinstance(cur_cmp, (six.integer_types, float)) and isinstance( + cur_struct, (six.integer_types, float) + ): + log.debug("Comparing numeric values: %d ? %d", cur_cmp, cur_struct) # numeric compare return cur_cmp == cur_struct - elif isinstance(cur_struct, (six.integer_types, float)) and \ - isinstance(cur_cmp, (six.string_types, six.text_type)): + elif isinstance(cur_struct, (six.integer_types, float)) and isinstance( + cur_cmp, (six.string_types, six.text_type) + ): # Comapring the numerical value agains a presumably mathematical value - log.debug('Comparing a numeric value (%d) with a string (%s)', cur_struct, cur_cmp) + log.debug( + "Comparing a numeric value (%d) with a string (%s)", cur_struct, cur_cmp + ) numeric_compare = _numeric_regex.match(cur_cmp) # determine if the value to compare agains is a mathematical operand if numeric_compare: compare_value = numeric_compare.group(2) - return getattr(float(cur_struct), _numeric_operand[numeric_compare.group(1)])(float(compare_value)) + return getattr( + float(cur_struct), _numeric_operand[numeric_compare.group(1)] + )(float(compare_value)) return False return False def validate(config): - ''' + """ Validate the beacon configuration. - ''' + """ # Must be a list of dicts. if not isinstance(config, list): - return False, 'Configuration for napalm beacon must be a list.' + return False, "Configuration for napalm beacon must be a list." for mod in config: fun = mod.keys()[0] fun_cfg = mod.values()[0] if not isinstance(fun_cfg, dict): - return False, 'The match structure for the {} execution function output must be a dictionary'.format(fun) + return ( + False, + "The match structure for the {} execution function output must be a dictionary".format( + fun + ), + ) if fun not in __salt__: - return False, 'Execution function {} is not availabe!'.format(fun) - return True, 'Valid configuration for the napal beacon!' + return False, "Execution function {} is not availabe!".format(fun) + return True, "Valid configuration for the napal beacon!" def beacon(config): - ''' + """ Watch napalm function and fire events. - ''' - log.debug('Executing napalm beacon with config:') + """ + log.debug("Executing napalm beacon with config:") log.debug(config) ret = [] for mod in config: @@ -308,22 +322,22 @@ def beacon(config): event = {} fun = mod.keys()[0] fun_cfg = mod.values()[0] - args = fun_cfg.pop('_args', []) - kwargs = fun_cfg.pop('_kwargs', {}) - log.debug('Executing {fun} with {args} and {kwargs}'.format( - fun=fun, - args=args, - kwargs=kwargs - )) + args = fun_cfg.pop("_args", []) + kwargs = fun_cfg.pop("_kwargs", {}) + log.debug( + "Executing {fun} with {args} and {kwargs}".format( + fun=fun, args=args, kwargs=kwargs + ) + ) fun_ret = __salt__[fun](*args, **kwargs) - log.debug('Got the reply from the minion:') + log.debug("Got the reply from the minion:") log.debug(fun_ret) - if not fun_ret.get('result', False): - log.error('Error whilst executing {}'.format(fun)) + if not fun_ret.get("result", False): + log.error("Error whilst executing {}".format(fun)) log.error(fun_ret) continue - fun_ret_out = fun_ret['out'] - log.debug('Comparing to:') + fun_ret_out = fun_ret["out"] + log.debug("Comparing to:") log.debug(fun_cfg) try: fun_cmp_result = _compare(fun_cfg, fun_ret_out) @@ -332,21 +346,18 @@ def beacon(config): # catch any exception and continue # to not jeopardise the execution of the next function in the list continue - log.debug('Result of comparison: {res}'.format(res=fun_cmp_result)) + log.debug("Result of comparison: {res}".format(res=fun_cmp_result)) if fun_cmp_result: - log.info('Matched {fun} with {cfg}'.format( - fun=fun, - cfg=fun_cfg - )) - event['tag'] = '{os}/{fun}'.format(os=__grains__['os'], fun=fun) - event['fun'] = fun - event['args'] = args - event['kwargs'] = kwargs - event['data'] = fun_ret - event['match'] = fun_cfg - log.debug('Queueing event:') + log.info("Matched {fun} with {cfg}".format(fun=fun, cfg=fun_cfg)) + event["tag"] = "{os}/{fun}".format(os=__grains__["os"], fun=fun) + event["fun"] = fun + event["args"] = args + event["kwargs"] = kwargs + event["data"] = fun_ret + event["match"] = fun_cfg + log.debug("Queueing event:") log.debug(event) ret.append(event) - log.debug('NAPALM beacon generated the events:') + log.debug("NAPALM beacon generated the events:") log.debug(ret) return ret diff --git a/salt/beacons/network_info.py b/salt/beacons/network_info.py index 056d395010f..38948f42221 100644 --- a/salt/beacons/network_info.py +++ b/salt/beacons/network_info.py @@ -1,39 +1,49 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to monitor statistics from ethernet adapters .. versionadded:: 2015.5.0 -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging +from salt.ext.six.moves import map + # Import third party libs # pylint: disable=import-error try: import salt.utils.psutil_compat as psutil + HAS_PSUTIL = True except ImportError: HAS_PSUTIL = False -from salt.ext.six.moves import map # pylint: enable=import-error log = logging.getLogger(__name__) -__virtualname__ = 'network_info' +__virtualname__ = "network_info" -__attrs = ['bytes_sent', 'bytes_recv', 'packets_sent', - 'packets_recv', 'errin', 'errout', - 'dropin', 'dropout'] +__attrs = [ + "bytes_sent", + "bytes_recv", + "packets_sent", + "packets_recv", + "errin", + "errout", + "dropin", + "dropout", +] def _to_list(obj): - ''' + """ Convert snetinfo object to list - ''' + """ ret = {} for attr in __attrs: @@ -44,42 +54,55 @@ def _to_list(obj): def __virtual__(): if not HAS_PSUTIL: - return (False, 'cannot load network_info beacon: psutil not available') + return (False, "cannot load network_info beacon: psutil not available") return __virtualname__ def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ VALID_ITEMS = [ - 'type', 'bytes_sent', 'bytes_recv', 'packets_sent', - 'packets_recv', 'errin', 'errout', 'dropin', - 'dropout' + "type", + "bytes_sent", + "bytes_recv", + "packets_sent", + "packets_recv", + "errin", + "errout", + "dropin", + "dropout", ] # Configuration for load beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for network_info beacon must be a list.') + return False, ("Configuration for network_info beacon must be a list.") else: _config = {} list(map(_config.update, config)) - for item in _config.get('interfaces', {}): - if not isinstance(_config['interfaces'][item], dict): - return False, ('Configuration for network_info beacon must ' - 'be a list of dictionaries.') + for item in _config.get("interfaces", {}): + if not isinstance(_config["interfaces"][item], dict): + return ( + False, + ( + "Configuration for network_info beacon must " + "be a list of dictionaries." + ), + ) else: - if not any(j in VALID_ITEMS for j in _config['interfaces'][item]): - return False, ('Invalid configuration item in ' - 'Beacon configuration.') - return True, 'Valid beacon configuration' + if not any(j in VALID_ITEMS for j in _config["interfaces"][item]): + return ( + False, + ("Invalid configuration item in Beacon configuration."), + ) + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Emit the network statistics of this host. Specify thresholds for each network stat @@ -125,42 +148,47 @@ def beacon(config): dropout: 100 - ''' + """ ret = [] _config = {} list(map(_config.update, config)) - log.debug('psutil.net_io_counters %s', psutil.net_io_counters) + log.debug("psutil.net_io_counters %s", psutil.net_io_counters) _stats = psutil.net_io_counters(pernic=True) - log.debug('_stats %s', _stats) - for interface in _config.get('interfaces', {}): + log.debug("_stats %s", _stats) + for interface in _config.get("interfaces", {}): if interface in _stats: - interface_config = _config['interfaces'][interface] + interface_config = _config["interfaces"][interface] _if_stats = _stats[interface] _diff = False for attr in __attrs: if attr in interface_config: - if 'type' in interface_config and \ - interface_config['type'] == 'equal': - if getattr(_if_stats, attr, None) == \ - int(interface_config[attr]): + if ( + "type" in interface_config + and interface_config["type"] == "equal" + ): + if getattr(_if_stats, attr, None) == int( + interface_config[attr] + ): _diff = True - elif 'type' in interface_config and \ - interface_config['type'] == 'greater': - if getattr(_if_stats, attr, None) > \ - int(interface_config[attr]): + elif ( + "type" in interface_config + and interface_config["type"] == "greater" + ): + if getattr(_if_stats, attr, None) > int(interface_config[attr]): _diff = True else: - log.debug('attr %s', getattr(_if_stats, - attr, None)) + log.debug("attr %s", getattr(_if_stats, attr, None)) else: - if getattr(_if_stats, attr, None) == \ - int(interface_config[attr]): + if getattr(_if_stats, attr, None) == int( + interface_config[attr] + ): _diff = True if _diff: - ret.append({'interface': interface, - 'network_info': _to_list(_if_stats)}) + ret.append( + {"interface": interface, "network_info": _to_list(_if_stats)} + ) return ret diff --git a/salt/beacons/network_settings.py b/salt/beacons/network_settings.py index 1f077dddd27..3aee76d829f 100644 --- a/salt/beacons/network_settings.py +++ b/salt/beacons/network_settings.py @@ -1,43 +1,69 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to monitor network adapter setting changes on Linux .. versionadded:: 2016.3.0 -''' +""" from __future__ import absolute_import, unicode_literals + +import ast +import logging +import re + +import salt.loader +from salt.ext.six.moves import map + # Import third party libs try: from pyroute2 import IPDB + IP = IPDB() HAS_PYROUTE2 = True except ImportError: IP = None HAS_PYROUTE2 = False -import ast -import re -import salt.loader -import logging -from salt.ext.six.moves import map log = logging.getLogger(__name__) -__virtual_name__ = 'network_settings' +__virtual_name__ = "network_settings" -ATTRS = ['family', 'txqlen', 'ipdb_scope', 'index', 'operstate', 'group', - 'carrier_changes', 'ipaddr', 'neighbours', 'ifname', 'promiscuity', - 'linkmode', 'broadcast', 'address', 'num_tx_queues', 'ipdb_priority', - 'kind', 'qdisc', 'mtu', 'num_rx_queues', 'carrier', 'flags', - 'ifi_type', 'ports'] +ATTRS = [ + "family", + "txqlen", + "ipdb_scope", + "index", + "operstate", + "group", + "carrier_changes", + "ipaddr", + "neighbours", + "ifname", + "promiscuity", + "linkmode", + "broadcast", + "address", + "num_tx_queues", + "ipdb_priority", + "kind", + "qdisc", + "mtu", + "num_rx_queues", + "carrier", + "flags", + "ifi_type", + "ports", +] LAST_STATS = {} class Hashabledict(dict): - ''' + """ Helper class that implements a hash function for a dictionary - ''' + """ + def __hash__(self): return hash(tuple(sorted(self.items()))) @@ -49,35 +75,44 @@ def __virtual__(): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ if not isinstance(config, list): - return False, ('Configuration for network_settings ' - 'beacon must be a list.') + return False, ("Configuration for network_settings beacon must be a list.") else: _config = {} list(map(_config.update, config)) - interfaces = _config.get('interfaces', {}) + interfaces = _config.get("interfaces", {}) if isinstance(interfaces, list): - #Old syntax - return False, ('interfaces section for network_settings beacon' - ' must be a dictionary.') + # Old syntax + return ( + False, + ( + "interfaces section for network_settings beacon" + " must be a dictionary." + ), + ) for item in interfaces: - if not isinstance(_config['interfaces'][item], dict): - return False, ('Interface attributes for network_settings beacon' - ' must be a dictionary.') - if not all(j in ATTRS for j in _config['interfaces'][item]): - return False, ('Invalid attributes in beacon configuration.') - return True, 'Valid beacon configuration' + if not isinstance(_config["interfaces"][item], dict): + return ( + False, + ( + "Interface attributes for network_settings beacon" + " must be a dictionary." + ), + ) + if not all(j in ATTRS for j in _config["interfaces"][item]): + return False, ("Invalid attributes in beacon configuration.") + return True, "Valid beacon configuration" def _copy_interfaces_info(interfaces): - ''' + """ Return a dictionary with a copy of each interface attributes in ATTRS - ''' + """ ret = {} for interface in interfaces: @@ -93,7 +128,7 @@ def _copy_interfaces_info(interfaces): def beacon(config): - ''' + """ Watch for changes on network settings By default, the beacon will emit when there is a value change on one of the @@ -134,13 +169,13 @@ def beacon(config): ipaddr: promiscuity: - ''' + """ _config = {} list(map(_config.update, config)) ret = [] interfaces = [] - expanded_config = {'interfaces': {}} + expanded_config = {"interfaces": {}} global LAST_STATS @@ -151,14 +186,14 @@ def beacon(config): if not LAST_STATS: LAST_STATS = _stats - if 'coalesce' in _config and _config['coalesce']: + if "coalesce" in _config and _config["coalesce"]: coalesce = True changes = {} - log.debug('_stats %s', _stats) + log.debug("_stats %s", _stats) # Get list of interfaces included in config that are registered in the # system, including interfaces defined by wildcards (eth*, wlan*) - for interface_config in _config.get('interfaces', {}): + for interface_config in _config.get("interfaces", {}): if interface_config in _stats: interfaces.append(interface_config) else: @@ -167,22 +202,24 @@ def beacon(config): match = re.search(interface_config, interface_stat) if match: interfaces.append(interface_stat) - expanded_config['interfaces'][interface_stat] = _config['interfaces'][interface_config] + expanded_config["interfaces"][interface_stat] = _config[ + "interfaces" + ][interface_config] if expanded_config: - _config['interfaces'].update(expanded_config['interfaces']) + _config["interfaces"].update(expanded_config["interfaces"]) # config updated so update _config list(map(_config.update, config)) - log.debug('interfaces %s', interfaces) + log.debug("interfaces %s", interfaces) for interface in interfaces: _send_event = False _diff_stats = _stats[interface] - LAST_STATS[interface] _ret_diff = {} - interface_config = _config['interfaces'][interface] + interface_config = _config["interfaces"][interface] - log.debug('_diff_stats %s', _diff_stats) + log.debug("_diff_stats %s", _diff_stats) if _diff_stats: _diff_stats_dict = {} LAST_STATS[interface] = _stats[interface] @@ -192,9 +229,8 @@ def beacon(config): for attr in interface_config: if attr in _diff_stats_dict: config_value = None - if interface_config[attr] and \ - 'onvalue' in interface_config[attr]: - config_value = interface_config[attr]['onvalue'] + if interface_config[attr] and "onvalue" in interface_config[attr]: + config_value = interface_config[attr]["onvalue"] new_value = ast.literal_eval(_diff_stats_dict[attr]) if not config_value or config_value == new_value: _send_event = True @@ -204,13 +240,13 @@ def beacon(config): if coalesce: changes[interface] = _ret_diff else: - ret.append({'tag': interface, - 'interface': interface, - 'change': _ret_diff}) + ret.append( + {"tag": interface, "interface": interface, "change": _ret_diff} + ) if coalesce and changes: grains_info = salt.loader.grains(__opts__, True) __grains__.update(grains_info) - ret.append({'tag': 'result', 'changes': changes}) + ret.append({"tag": "result", "changes": changes}) return ret diff --git a/salt/beacons/pkg.py b/salt/beacons/pkg.py index 2bdfd8c8e60..b656de37edf 100644 --- a/salt/beacons/pkg.py +++ b/salt/beacons/pkg.py @@ -1,51 +1,51 @@ # -*- coding: utf-8 -*- -''' +""" Watch for pkgs that have upgrades, then fire an event. .. versionadded:: 2016.3.0 -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals import logging -__virtualname__ = 'pkg' +__virtualname__ = "pkg" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if strace is installed - ''' - return __virtualname__ if 'pkg.upgrade_available' in __salt__ else False + """ + return __virtualname__ if "pkg.upgrade_available" in __salt__ else False def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for pkg beacon should be a list if not isinstance(config, list): - return False, ('Configuration for pkg beacon must be a list.') + return False, ("Configuration for pkg beacon must be a list.") # Configuration for pkg beacon should contain pkgs pkgs_found = False pkgs_not_list = False for config_item in config: - if 'pkgs' in config_item: + if "pkgs" in config_item: pkgs_found = True - if isinstance(config_item['pkgs'], list): + if isinstance(config_item["pkgs"], list): pkgs_not_list = True if not pkgs_found or not pkgs_not_list: - return False, 'Configuration for pkg beacon requires list of pkgs.' - return True, 'Valid beacon configuration' + return False, "Configuration for pkg beacon requires list of pkgs." + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Check if installed packages are the latest versions and fire an event for those that have upgrades. @@ -57,23 +57,21 @@ def beacon(config): - zsh - apache2 - refresh: True - ''' + """ ret = [] _refresh = False pkgs = [] for config_item in config: - if 'pkgs' in config_item: - pkgs += config_item['pkgs'] - if 'refresh' in config and config['refresh']: + if "pkgs" in config_item: + pkgs += config_item["pkgs"] + if "refresh" in config and config["refresh"]: _refresh = True for pkg in pkgs: - _installed = __salt__['pkg.version'](pkg) - _latest = __salt__['pkg.latest_version'](pkg, refresh=_refresh) + _installed = __salt__["pkg.version"](pkg) + _latest = __salt__["pkg.latest_version"](pkg, refresh=_refresh) if _installed and _latest: - _pkg = {'pkg': pkg, - 'version': _latest - } + _pkg = {"pkg": pkg, "version": _latest} ret.append(_pkg) return ret diff --git a/salt/beacons/proxy_example.py b/salt/beacons/proxy_example.py index 018743657f8..7b647e81306 100644 --- a/salt/beacons/proxy_example.py +++ b/salt/beacons/proxy_example.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Example beacon to use with salt-proxy .. code-block:: yaml @@ -7,10 +7,11 @@ Example beacon to use with salt-proxy beacons: proxy_example: endpoint: beacon -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging # Import salt libs @@ -19,32 +20,32 @@ from salt.ext.six.moves import map # Important: If used with salt-proxy # this is required for the beacon to load!!! -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] -__virtualname__ = 'proxy_example' +__virtualname__ = "proxy_example" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Trivially let the beacon load for the test example. For a production beacon we should probably have some expression here. - ''' + """ return True def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ if not isinstance(config, list): - return False, ('Configuration for proxy_example beacon must be a list.') - return True, 'Valid beacon configuration' + return False, ("Configuration for proxy_example beacon must be a list.") + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Called several times each second https://docs.saltstack.com/en/latest/topics/beacons/#the-beacon-function @@ -53,7 +54,7 @@ def beacon(config): beacons: proxy_example: - endpoint: beacon - ''' + """ # Important!!! # Although this toy example makes an HTTP call # to get beacon information @@ -63,9 +64,6 @@ def beacon(config): _config = {} list(map(_config.update, config)) - beacon_url = '{0}{1}'.format(__opts__['proxy']['url'], - _config['endpoint']) - ret = salt.utils.http.query(beacon_url, - decode_type='json', - decode=True) - return [ret['dict']] + beacon_url = "{0}{1}".format(__opts__["proxy"]["url"], _config["endpoint"]) + ret = salt.utils.http.query(beacon_url, decode_type="json", decode=True) + return [ret["dict"]] diff --git a/salt/beacons/ps.py b/salt/beacons/ps.py index 4544c4ac1e0..b7b8f8a9838 100644 --- a/salt/beacons/ps.py +++ b/salt/beacons/ps.py @@ -1,57 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" Send events covering process status -''' +""" # Import Python Libs from __future__ import absolute_import, unicode_literals + import logging +from salt.ext.six.moves import map + # Import third party libs # pylint: disable=import-error try: import salt.utils.psutil_compat as psutil + HAS_PSUTIL = True except ImportError: HAS_PSUTIL = False -from salt.ext.six.moves import map # pylint: enable=import-error log = logging.getLogger(__name__) # pylint: disable=invalid-name -__virtualname__ = 'ps' +__virtualname__ = "ps" def __virtual__(): if not HAS_PSUTIL: - return (False, 'cannot load ps beacon: psutil not available') + return (False, "cannot load ps beacon: psutil not available") return __virtualname__ def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for ps beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for ps beacon must be a list.') + return False, ("Configuration for ps beacon must be a list.") else: _config = {} list(map(_config.update, config)) - if 'processes' not in _config: - return False, ('Configuration for ps beacon requires processes.') + if "processes" not in _config: + return False, ("Configuration for ps beacon requires processes.") else: - if not isinstance(_config['processes'], dict): - return False, ('Processes for ps beacon must be a dictionary.') + if not isinstance(_config["processes"], dict): + return False, ("Processes for ps beacon must be a dictionary.") - return True, 'Valid beacon configuration' + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Scan for processes and fire events Example Config @@ -66,7 +69,7 @@ def beacon(config): The config above sets up beacons to check that processes are running or stopped. - ''' + """ ret = [] procs = [] for proc in psutil.process_iter(): @@ -77,15 +80,15 @@ def beacon(config): _config = {} list(map(_config.update, config)) - for process in _config.get('processes', {}): + for process in _config.get("processes", {}): ret_dict = {} - if _config['processes'][process] == 'running': + if _config["processes"][process] == "running": if process in procs: - ret_dict[process] = 'Running' + ret_dict[process] = "Running" ret.append(ret_dict) - elif _config['processes'][process] == 'stopped': + elif _config["processes"][process] == "stopped": if process not in procs: - ret_dict[process] = 'Stopped' + ret_dict[process] = "Stopped" ret.append(ret_dict) else: if process not in procs: diff --git a/salt/beacons/salt_proxy.py b/salt/beacons/salt_proxy.py index 2274b2d2ed4..ce1d77f96dc 100644 --- a/salt/beacons/salt_proxy.py +++ b/salt/beacons/salt_proxy.py @@ -1,33 +1,35 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to manage and report the status of one or more salt proxy processes .. versionadded:: 2015.8.3 -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import logging + from salt.ext.six.moves import map log = logging.getLogger(__name__) def _run_proxy_processes(proxies): - ''' + """ Iterate over a list of proxy names and restart any that aren't running - ''' + """ ret = [] for proxy in proxies: result = {} - if not __salt__['salt_proxy.is_running'](proxy)['result']: - __salt__['salt_proxy.configure_proxy'](proxy, start=True) - result[proxy] = 'Proxy {0} was started'.format(proxy) + if not __salt__["salt_proxy.is_running"](proxy)["result"]: + __salt__["salt_proxy.configure_proxy"](proxy, start=True) + result[proxy] = "Proxy {0} was started".format(proxy) else: - msg = 'Proxy {0} is already running'.format(proxy) + msg = "Proxy {0} is already running".format(proxy) result[proxy] = msg log.debug(msg) ret.append(result) @@ -35,30 +37,28 @@ def _run_proxy_processes(proxies): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for adb beacon should be a dictionary with states array if not isinstance(config, list): - log.info('Configuration for salt_proxy beacon must be a list.') - return False, ('Configuration for salt_proxy beacon must be a list.') + log.info("Configuration for salt_proxy beacon must be a list.") + return False, ("Configuration for salt_proxy beacon must be a list.") else: _config = {} list(map(_config.update, config)) - if 'proxies' not in _config: - return False, ('Configuration for salt_proxy' - ' beacon requires proxies.') + if "proxies" not in _config: + return False, ("Configuration for salt_proxy beacon requires proxies.") else: - if not isinstance(_config['proxies'], dict): - return False, ('Proxies for salt_proxy ' - 'beacon must be a dictionary.') - return True, 'Valid beacon configuration' + if not isinstance(_config["proxies"], dict): + return False, ("Proxies for salt_proxy beacon must be a dictionary.") + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Handle configured proxies .. code-block:: yaml @@ -68,10 +68,10 @@ def beacon(config): - proxies: p8000: {} p8001: {} - ''' - log.trace('salt proxy beacon called') + """ + log.trace("salt proxy beacon called") _config = {} list(map(_config.update, config)) - return _run_proxy_processes(_config['proxies']) + return _run_proxy_processes(_config["proxies"]) diff --git a/salt/beacons/sensehat.py b/salt/beacons/sensehat.py index 34d9b6bc419..24cbde55bf8 100644 --- a/salt/beacons/sensehat.py +++ b/salt/beacons/sensehat.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Monitor temperature, humidity and pressure using the SenseHat of a Raspberry Pi =============================================================================== @@ -8,7 +8,7 @@ Monitor temperature, humidity and pressure using the SenseHat of a Raspberry Pi :maintainer: Benedikt Werner <1benediktwerner@gmail.com> :maturity: new :depends: sense_hat Python module -''' +""" from __future__ import absolute_import, unicode_literals @@ -22,30 +22,28 @@ log = logging.getLogger(__name__) def __virtual__(): - return 'sensehat.get_pressure' in __salt__ + return "sensehat.get_pressure" in __salt__ def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for sensehat beacon should be a list if not isinstance(config, list): - return False, ('Configuration for sensehat beacon ' - 'must be a list.') + return False, ("Configuration for sensehat beacon must be a list.") else: _config = {} list(map(_config.update, config)) - if 'sensors' not in _config: - return False, ('Configuration for sensehat' - ' beacon requires sensors.') + if "sensors" not in _config: + return False, ("Configuration for sensehat beacon requires sensors.") - return True, 'Valid beacon configuration' + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Monitor the temperature, humidity and pressure using the SenseHat sensors. You can either specify a threshold for each value and only emit a beacon @@ -67,43 +65,36 @@ def beacon(config): temperature: [20, 40] temperature_from_pressure: 40 pressure: 1500 - ''' + """ ret = [] - min_default = { - 'humidity': '0', - 'pressure': '0', - 'temperature': '-273.15' - } + min_default = {"humidity": "0", "pressure": "0", "temperature": "-273.15"} _config = {} list(map(_config.update, config)) - for sensor in _config.get('sensors', {}): - sensor_function = 'sensehat.get_{0}'.format(sensor) + for sensor in _config.get("sensors", {}): + sensor_function = "sensehat.get_{0}".format(sensor) if sensor_function not in __salt__: - log.error('No sensor for meassuring %s. Skipping.', sensor) + log.error("No sensor for meassuring %s. Skipping.", sensor) continue - sensor_config = _config['sensors'][sensor] + sensor_config = _config["sensors"][sensor] if isinstance(sensor_config, list): sensor_min = six.text_type(sensor_config[0]) sensor_max = six.text_type(sensor_config[1]) else: - sensor_min = min_default.get(sensor, '0') + sensor_min = min_default.get(sensor, "0") sensor_max = six.text_type(sensor_config) - if '%' in sensor_min: - sensor_min = re.sub('%', '', sensor_min) - if '%' in sensor_max: - sensor_max = re.sub('%', '', sensor_max) + if "%" in sensor_min: + sensor_min = re.sub("%", "", sensor_min) + if "%" in sensor_max: + sensor_max = re.sub("%", "", sensor_max) sensor_min = float(sensor_min) sensor_max = float(sensor_max) current_value = __salt__[sensor_function]() if not sensor_min <= current_value <= sensor_max: - ret.append({ - 'tag': 'sensehat/{0}'.format(sensor), - sensor: current_value - }) + ret.append({"tag": "sensehat/{0}".format(sensor), sensor: current_value}) return ret diff --git a/salt/beacons/service.py b/salt/beacons/service.py index e63e97fbd7c..5132089edcc 100644 --- a/salt/beacons/service.py +++ b/salt/beacons/service.py @@ -1,48 +1,53 @@ # -*- coding: utf-8 -*- -''' +""" Send events covering service status -''' +""" # Import Python Libs from __future__ import absolute_import, unicode_literals -import os import logging +import os import time + from salt.ext.six.moves import map log = logging.getLogger(__name__) # pylint: disable=invalid-name LAST_STATUS = {} -__virtualname__ = 'service' +__virtualname__ = "service" def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for service beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for service beacon must be a list.') + return False, ("Configuration for service beacon must be a list.") else: _config = {} list(map(_config.update, config)) - if 'services' not in _config: - return False, ('Configuration for service beacon' - ' requires services.') + if "services" not in _config: + return False, ("Configuration for service beacon requires services.") else: - for config_item in _config['services']: - if not isinstance(_config['services'][config_item], dict): - return False, ('Configuration for service beacon must ' - 'be a list of dictionaries.') + for config_item in _config["services"]: + if not isinstance(_config["services"][config_item], dict): + return ( + False, + ( + "Configuration for service beacon must " + "be a list of dictionaries." + ), + ) - return True, 'Valid beacon configuration' + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Scan for the configured services and fire events Example Config @@ -101,19 +106,19 @@ def beacon(config): onchangeonly: True delay: 30 uncleanshutdown: /run/nginx.pid - ''' + """ ret = [] _config = {} list(map(_config.update, config)) - for service in _config.get('services', {}): + for service in _config.get("services", {}): ret_dict = {} - service_config = _config['services'][service] + service_config = _config["services"][service] - ret_dict[service] = {'running': __salt__['service.status'](service)} - ret_dict['service_name'] = service - ret_dict['tag'] = service + ret_dict[service] = {"running": __salt__["service.status"](service)} + ret_dict["service_name"] = service + ret_dict["tag"] = service currtime = time.time() # If no options is given to the service, we fall back to the defaults @@ -121,42 +126,44 @@ def beacon(config): # key:values are then added to the service dictionary. if not service_config: service_config = {} - if 'oncleanshutdown' not in service_config: - service_config['oncleanshutdown'] = False - if 'emitatstartup' not in service_config: - service_config['emitatstartup'] = True - if 'onchangeonly' not in service_config: - service_config['onchangeonly'] = False - if 'delay' not in service_config: - service_config['delay'] = 0 + if "oncleanshutdown" not in service_config: + service_config["oncleanshutdown"] = False + if "emitatstartup" not in service_config: + service_config["emitatstartup"] = True + if "onchangeonly" not in service_config: + service_config["onchangeonly"] = False + if "delay" not in service_config: + service_config["delay"] = 0 # We only want to report the nature of the shutdown # if the current running status is False # as well as if the config for the beacon asks for it - if 'uncleanshutdown' in service_config and not ret_dict[service]['running']: - filename = service_config['uncleanshutdown'] - ret_dict[service]['uncleanshutdown'] = True if os.path.exists(filename) else False - if 'onchangeonly' in service_config and service_config['onchangeonly'] is True: + if "uncleanshutdown" in service_config and not ret_dict[service]["running"]: + filename = service_config["uncleanshutdown"] + ret_dict[service]["uncleanshutdown"] = ( + True if os.path.exists(filename) else False + ) + if "onchangeonly" in service_config and service_config["onchangeonly"] is True: if service not in LAST_STATUS: LAST_STATUS[service] = ret_dict[service] - if service_config['delay'] > 0: - LAST_STATUS[service]['time'] = currtime - elif not service_config['emitatstartup']: + if service_config["delay"] > 0: + LAST_STATUS[service]["time"] = currtime + elif not service_config["emitatstartup"]: continue else: ret.append(ret_dict) - if LAST_STATUS[service]['running'] != ret_dict[service]['running']: + if LAST_STATUS[service]["running"] != ret_dict[service]["running"]: LAST_STATUS[service] = ret_dict[service] - if service_config['delay'] > 0: - LAST_STATUS[service]['time'] = currtime + if service_config["delay"] > 0: + LAST_STATUS[service]["time"] = currtime else: ret.append(ret_dict) - if 'time' in LAST_STATUS[service]: - elapsedtime = int(round(currtime - LAST_STATUS[service]['time'])) - if elapsedtime > service_config['delay']: - del LAST_STATUS[service]['time'] + if "time" in LAST_STATUS[service]: + elapsedtime = int(round(currtime - LAST_STATUS[service]["time"])) + if elapsedtime > service_config["delay"]: + del LAST_STATUS[service]["time"] ret.append(ret_dict) else: ret.append(ret_dict) diff --git a/salt/beacons/sh.py b/salt/beacons/sh.py index 46b25824502..9619ef222a6 100644 --- a/salt/beacons/sh.py +++ b/salt/beacons/sh.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Watch the shell commands being executed actively. This beacon requires strace. -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals @@ -14,97 +14,97 @@ import salt.utils.path import salt.utils.stringutils import salt.utils.vt -__virtualname__ = 'sh' +__virtualname__ = "sh" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if strace is installed - ''' - return __virtualname__ if salt.utils.path.which('strace') else False + """ + return __virtualname__ if salt.utils.path.which("strace") else False def _get_shells(): - ''' + """ Return the valid shells on this system - ''' + """ start = time.time() - if 'sh.last_shells' in __context__: - if start - __context__['sh.last_shells'] > 5: - __context__['sh.last_shells'] = start + if "sh.last_shells" in __context__: + if start - __context__["sh.last_shells"] > 5: + __context__["sh.last_shells"] = start else: - __context__['sh.shells'] = __salt__['cmd.shells']() + __context__["sh.shells"] = __salt__["cmd.shells"]() else: - __context__['sh.last_shells'] = start - __context__['sh.shells'] = __salt__['cmd.shells']() - return __context__['sh.shells'] + __context__["sh.last_shells"] = start + __context__["sh.shells"] = __salt__["cmd.shells"]() + return __context__["sh.shells"] def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for sh beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for sh beacon must be a list.') - return True, 'Valid beacon configuration' + return False, ("Configuration for sh beacon must be a list.") + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Scan the shell execve routines. This beacon will convert all login shells .. code-block:: yaml beacons: sh: [] - ''' + """ ret = [] - pkey = 'sh.vt' + pkey = "sh.vt" shells = _get_shells() - ps_out = __salt__['status.procs']() + ps_out = __salt__["status.procs"]() track_pids = [] for pid in ps_out: - if any(ps_out[pid].get('cmd', '').lstrip('-') in shell for shell in shells): + if any(ps_out[pid].get("cmd", "").lstrip("-") in shell for shell in shells): track_pids.append(pid) if pkey not in __context__: __context__[pkey] = {} for pid in track_pids: if pid not in __context__[pkey]: - cmd = ['strace', '-f', '-e', 'execve', '-p', '{0}'.format(pid)] + cmd = ["strace", "-f", "-e", "execve", "-p", "{0}".format(pid)] __context__[pkey][pid] = {} - __context__[pkey][pid]['vt'] = salt.utils.vt.Terminal( - cmd, - log_stdout=True, - log_stderr=True, - stream_stdout=False, - stream_stderr=False) - __context__[pkey][pid]['user'] = ps_out[pid].get('user') + __context__[pkey][pid]["vt"] = salt.utils.vt.Terminal( + cmd, + log_stdout=True, + log_stderr=True, + stream_stdout=False, + stream_stderr=False, + ) + __context__[pkey][pid]["user"] = ps_out[pid].get("user") for pid in list(__context__[pkey]): - out = '' - err = '' - while __context__[pkey][pid]['vt'].has_unread_data: - tout, terr = __context__[pkey][pid]['vt'].recv() + out = "" + err = "" + while __context__[pkey][pid]["vt"].has_unread_data: + tout, terr = __context__[pkey][pid]["vt"].recv() if not terr: break - out += salt.utils.stringutils.to_unicode(tout or '') + out += salt.utils.stringutils.to_unicode(tout or "") err += terr - for line in err.split('\n'): - event = {'args': [], - 'tag': pid} - if 'execve' in line: - comps = line.split('execve')[1].split('"') + for line in err.split("\n"): + event = {"args": [], "tag": pid} + if "execve" in line: + comps = line.split("execve")[1].split('"') for ind, field in enumerate(comps): if ind == 1: - event['cmd'] = field + event["cmd"] = field continue if ind % 2 != 0: - event['args'].append(field) - event['user'] = __context__[pkey][pid]['user'] + event["args"].append(field) + event["user"] = __context__[pkey][pid]["user"] ret.append(event) - if not __context__[pkey][pid]['vt'].isalive(): - __context__[pkey][pid]['vt'].close() + if not __context__[pkey][pid]["vt"].isalive(): + __context__[pkey][pid]["vt"].close() __context__[pkey].pop(pid) return ret diff --git a/salt/beacons/smartos_imgadm.py b/salt/beacons/smartos_imgadm.py index 1f5fd518986..266b122d8d2 100644 --- a/salt/beacons/smartos_imgadm.py +++ b/salt/beacons/smartos_imgadm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Beacon that fires events on image import/delete. .. code-block:: yaml @@ -17,79 +17,81 @@ Beacon that fires events on image import/delete. imgadm: - interval: 60 - startup_import_event: True -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging # Import 3rd-party libs # pylint: disable=import-error from salt.ext.six.moves import map + # pylint: enable=import-error -__virtualname__ = 'imgadm' +__virtualname__ = "imgadm" IMGADM_STATE = { - 'first_run': True, - 'images': [], + "first_run": True, + "images": [], } log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Provides imgadm beacon on SmartOS - ''' - if 'imgadm.list' in __salt__: + """ + if "imgadm.list" in __salt__: return True else: return ( False, - '{0} beacon can only be loaded on SmartOS compute nodes'.format( + "{0} beacon can only be loaded on SmartOS compute nodes".format( __virtualname__ - ) + ), ) def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ vcfg_ret = True - vcfg_msg = 'Valid beacon configuration' + vcfg_msg = "Valid beacon configuration" if not isinstance(config, list): vcfg_ret = False - vcfg_msg = 'Configuration for imgadm beacon must be a list!' + vcfg_msg = "Configuration for imgadm beacon must be a list!" return vcfg_ret, vcfg_msg def beacon(config): - ''' + """ Poll imgadm and compare available images - ''' + """ ret = [] # NOTE: lookup current images - current_images = __salt__['imgadm.list'](verbose=True) + current_images = __salt__["imgadm.list"](verbose=True) # NOTE: apply configuration - if IMGADM_STATE['first_run']: - log.info('Applying configuration for imgadm beacon') + if IMGADM_STATE["first_run"]: + log.info("Applying configuration for imgadm beacon") _config = {} list(map(_config.update, config)) - if 'startup_import_event' not in _config or not _config['startup_import_event']: - IMGADM_STATE['images'] = current_images + if "startup_import_event" not in _config or not _config["startup_import_event"]: + IMGADM_STATE["images"] = current_images # NOTE: import events for uuid in current_images: event = {} - if uuid not in IMGADM_STATE['images']: - event['tag'] = "imported/{}".format(uuid) + if uuid not in IMGADM_STATE["images"]: + event["tag"] = "imported/{}".format(uuid) for label in current_images[uuid]: event[label] = current_images[uuid][label] @@ -97,23 +99,24 @@ def beacon(config): ret.append(event) # NOTE: delete events - for uuid in IMGADM_STATE['images']: + for uuid in IMGADM_STATE["images"]: event = {} if uuid not in current_images: - event['tag'] = "deleted/{}".format(uuid) - for label in IMGADM_STATE['images'][uuid]: - event[label] = IMGADM_STATE['images'][uuid][label] + event["tag"] = "deleted/{}".format(uuid) + for label in IMGADM_STATE["images"][uuid]: + event[label] = IMGADM_STATE["images"][uuid][label] if event: ret.append(event) # NOTE: update stored state - IMGADM_STATE['images'] = current_images + IMGADM_STATE["images"] = current_images # NOTE: disable first_run - if IMGADM_STATE['first_run']: - IMGADM_STATE['first_run'] = False + if IMGADM_STATE["first_run"]: + IMGADM_STATE["first_run"] = False return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/beacons/smartos_vmadm.py b/salt/beacons/smartos_vmadm.py index 4a7cb02eb63..9f2da08da5b 100644 --- a/salt/beacons/smartos_vmadm.py +++ b/salt/beacons/smartos_vmadm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Beacon that fires events on vm state changes .. code-block:: yaml @@ -17,84 +17,85 @@ Beacon that fires events on vm state changes vmadm: - interval: 60 - startup_create_event: True -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging # Import 3rd-party libs # pylint: disable=import-error from salt.ext.six.moves import map + # pylint: enable=import-error -__virtualname__ = 'vmadm' +__virtualname__ = "vmadm" VMADM_STATE = { - 'first_run': True, - 'vms': [], + "first_run": True, + "vms": [], } log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Provides vmadm beacon on SmartOS - ''' - if 'vmadm.list' in __salt__: + """ + if "vmadm.list" in __salt__: return True else: return ( False, - '{0} beacon can only be loaded on SmartOS compute nodes'.format( + "{0} beacon can only be loaded on SmartOS compute nodes".format( __virtualname__ - ) + ), ) def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ vcfg_ret = True - vcfg_msg = 'Valid beacon configuration' + vcfg_msg = "Valid beacon configuration" if not isinstance(config, list): vcfg_ret = False - vcfg_msg = 'Configuration for vmadm beacon must be a list!' + vcfg_msg = "Configuration for vmadm beacon must be a list!" return vcfg_ret, vcfg_msg def beacon(config): - ''' + """ Poll vmadm for changes - ''' + """ ret = [] # NOTE: lookup current images - current_vms = __salt__['vmadm.list']( - keyed=True, - order='uuid,state,alias,hostname,dns_domain', + current_vms = __salt__["vmadm.list"]( + keyed=True, order="uuid,state,alias,hostname,dns_domain", ) # NOTE: apply configuration - if VMADM_STATE['first_run']: - log.info('Applying configuration for vmadm beacon') + if VMADM_STATE["first_run"]: + log.info("Applying configuration for vmadm beacon") _config = {} list(map(_config.update, config)) - if 'startup_create_event' not in _config or not _config['startup_create_event']: - VMADM_STATE['vms'] = current_vms + if "startup_create_event" not in _config or not _config["startup_create_event"]: + VMADM_STATE["vms"] = current_vms # NOTE: create events for uuid in current_vms: event = {} - if uuid not in VMADM_STATE['vms']: - event['tag'] = "created/{}".format(uuid) + if uuid not in VMADM_STATE["vms"]: + event["tag"] = "created/{}".format(uuid) for label in current_vms[uuid]: - if label == 'state': + if label == "state": continue event[label] = current_vms[uuid][label] @@ -102,14 +103,14 @@ def beacon(config): ret.append(event) # NOTE: deleted events - for uuid in VMADM_STATE['vms']: + for uuid in VMADM_STATE["vms"]: event = {} if uuid not in current_vms: - event['tag'] = "deleted/{}".format(uuid) - for label in VMADM_STATE['vms'][uuid]: - if label == 'state': + event["tag"] = "deleted/{}".format(uuid) + for label in VMADM_STATE["vms"][uuid]: + if label == "state": continue - event[label] = VMADM_STATE['vms'][uuid][label] + event[label] = VMADM_STATE["vms"][uuid][label] if event: ret.append(event) @@ -117,12 +118,17 @@ def beacon(config): # NOTE: state change events for uuid in current_vms: event = {} - if VMADM_STATE['first_run'] or \ - uuid not in VMADM_STATE['vms'] or \ - current_vms[uuid].get('state', 'unknown') != VMADM_STATE['vms'][uuid].get('state', 'unknown'): - event['tag'] = "{}/{}".format(current_vms[uuid].get('state', 'unknown'), uuid) + if ( + VMADM_STATE["first_run"] + or uuid not in VMADM_STATE["vms"] + or current_vms[uuid].get("state", "unknown") + != VMADM_STATE["vms"][uuid].get("state", "unknown") + ): + event["tag"] = "{}/{}".format( + current_vms[uuid].get("state", "unknown"), uuid + ) for label in current_vms[uuid]: - if label == 'state': + if label == "state": continue event[label] = current_vms[uuid][label] @@ -130,12 +136,13 @@ def beacon(config): ret.append(event) # NOTE: update stored state - VMADM_STATE['vms'] = current_vms + VMADM_STATE["vms"] = current_vms # NOTE: disable first_run - if VMADM_STATE['first_run']: - VMADM_STATE['first_run'] = False + if VMADM_STATE["first_run"]: + VMADM_STATE["first_run"] = False return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/beacons/status.py b/salt/beacons/status.py index e2f92e0620c..290ec941abc 100644 --- a/salt/beacons/status.py +++ b/salt/beacons/status.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The status beacon is intended to send a basic health check event up to the master, this allows for event driven routines based on presence to be set up. @@ -87,12 +87,14 @@ markers for specific list items: Not all status functions are supported for every operating system. Be certain to check the minion log for errors after configuring this beacon. -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals -import logging + import datetime +import logging + import salt.exceptions # Import salt libs @@ -100,16 +102,16 @@ import salt.utils.platform log = logging.getLogger(__name__) -__virtualname__ = 'status' +__virtualname__ = "status" def validate(config): - ''' + """ Validate the the config is a dict - ''' + """ if not isinstance(config, list): - return False, ('Configuration for status beacon must be a list.') - return True, 'Valid beacon configuration' + return False, ("Configuration for status beacon must be a list.") + return True, "Valid beacon configuration" def __virtual__(): @@ -117,20 +119,22 @@ def __virtual__(): def beacon(config): - ''' + """ Return status for requested information - ''' + """ log.debug(config) ctime = datetime.datetime.utcnow().isoformat() if len(config) < 1: - config = [{ - 'loadavg': ['all'], - 'cpustats': ['all'], - 'meminfo': ['all'], - 'vmstats': ['all'], - 'time': ['all'], - }] + config = [ + { + "loadavg": ["all"], + "cpustats": ["all"], + "meminfo": ["all"], + "vmstats": ["all"], + "time": ["all"], + } + ] if not isinstance(config, list): # To support the old dictionary config format @@ -141,17 +145,21 @@ def beacon(config): for func in entry: ret[func] = {} try: - data = __salt__['status.{0}'.format(func)]() + data = __salt__["status.{0}".format(func)]() except salt.exceptions.CommandExecutionError as exc: - log.debug('Status beacon attempted to process function %s ' - 'but encountered error: %s', func, exc) + log.debug( + "Status beacon attempted to process function %s " + "but encountered error: %s", + func, + exc, + ) continue if not isinstance(entry[func], list): func_items = [entry[func]] else: func_items = entry[func] for item in func_items: - if item == 'all': + if item == "all": ret[func] = data else: try: @@ -160,9 +168,8 @@ def beacon(config): except TypeError: ret[func][item] = data[int(item)] except KeyError as exc: - ret[func] = 'Status beacon is incorrectly configured: {0}'.format(exc) + ret[ + func + ] = "Status beacon is incorrectly configured: {0}".format(exc) - return [{ - 'tag': ctime, - 'data': ret, - }] + return [{"tag": ctime, "data": ret}] diff --git a/salt/beacons/telegram_bot_msg.py b/salt/beacons/telegram_bot_msg.py index 51816b9d5b1..82daa152a37 100644 --- a/salt/beacons/telegram_bot_msg.py +++ b/salt/beacons/telegram_bot_msg.py @@ -1,20 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to emit Telegram messages Requires the python-telegram-bot library -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging + from salt.ext.six.moves import map # Import 3rd Party libs try: import telegram - logging.getLogger('telegram').setLevel(logging.CRITICAL) + + logging.getLogger("telegram").setLevel(logging.CRITICAL) HAS_TELEGRAM = True except ImportError: HAS_TELEGRAM = False @@ -22,7 +25,7 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'telegram_bot_msg' +__virtualname__ = "telegram_bot_msg" def __virtual__(): @@ -33,30 +36,37 @@ def __virtual__(): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ if not isinstance(config, list): - return False, ('Configuration for telegram_bot_msg ' - 'beacon must be a list.') + return False, ("Configuration for telegram_bot_msg beacon must be a list.") _config = {} list(map(_config.update, config)) - if not all(_config.get(required_config) - for required_config in ['token', 'accept_from']): - return False, ('Not all required configuration for ' - 'telegram_bot_msg are set.') + if not all( + _config.get(required_config) for required_config in ["token", "accept_from"] + ): + return ( + False, + ("Not all required configuration for telegram_bot_msg are set."), + ) - if not isinstance(_config.get('accept_from'), list): - return False, ('Configuration for telegram_bot_msg, ' - 'accept_from must be a list of usernames.') + if not isinstance(_config.get("accept_from"), list): + return ( + False, + ( + "Configuration for telegram_bot_msg, " + "accept_from must be a list of usernames." + ), + ) - return True, 'Valid beacon configuration.' + return True, "Valid beacon configuration." def beacon(config): - ''' + """ Emit a dict with a key "msgs" whose value is a list of messages sent to the configured bot by one of the allowed usernames. @@ -69,22 +79,22 @@ def beacon(config): - "" - interval: 10 - ''' + """ _config = {} list(map(_config.update, config)) - log.debug('telegram_bot_msg beacon starting') + log.debug("telegram_bot_msg beacon starting") ret = [] output = {} - output['msgs'] = [] + output["msgs"] = [] - bot = telegram.Bot(_config['token']) + bot = telegram.Bot(_config["token"]) updates = bot.get_updates(limit=100, timeout=0, network_delay=10) - log.debug('Num updates: %d', len(updates)) + log.debug("Num updates: %d", len(updates)) if not updates: - log.debug('Telegram Bot beacon has no new messages') + log.debug("Telegram Bot beacon has no new messages") return ret latest_update_id = 0 @@ -94,13 +104,13 @@ def beacon(config): if update.update_id > latest_update_id: latest_update_id = update.update_id - if message.chat.username in _config['accept_from']: - output['msgs'].append(message.to_dict()) + if message.chat.username in _config["accept_from"]: + output["msgs"].append(message.to_dict()) # mark in the server that previous messages are processed bot.get_updates(offset=latest_update_id + 1) - log.debug('Emitting %d messages.', len(output['msgs'])) - if output['msgs']: + log.debug("Emitting %d messages.", len(output["msgs"])) + if output["msgs"]: ret.append(output) return ret diff --git a/salt/beacons/twilio_txt_msg.py b/salt/beacons/twilio_txt_msg.py index 7e506aa39ab..13897f3862a 100644 --- a/salt/beacons/twilio_txt_msg.py +++ b/salt/beacons/twilio_txt_msg.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to emit Twilio text messages -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals @@ -14,9 +14,10 @@ from salt.ext.six.moves import map # Import 3rd Party libs try: import twilio + # Grab version, ensure elements are ints twilio_version = tuple([int(x) for x in twilio.__version_info__]) - if twilio_version > (5, ): + if twilio_version > (5,): from twilio.rest import Client as TwilioRestClient else: from twilio.rest import TwilioRestClient @@ -26,7 +27,7 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'twilio_txt_msg' +__virtualname__ = "twilio_txt_msg" def __virtual__(): @@ -37,28 +38,32 @@ def __virtual__(): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ # Configuration for twilio_txt_msg beacon should be a list of dicts if not isinstance(config, list): - return False, ('Configuration for twilio_txt_msg beacon ' - 'must be a list.') + return False, ("Configuration for twilio_txt_msg beacon must be a list.") else: _config = {} list(map(_config.update, config)) - if not all(x in _config for x in ('account_sid', - 'auth_token', - 'twilio_number')): - return False, ('Configuration for twilio_txt_msg beacon ' - 'must contain account_sid, auth_token ' - 'and twilio_number items.') - return True, 'Valid beacon configuration' + if not all( + x in _config for x in ("account_sid", "auth_token", "twilio_number") + ): + return ( + False, + ( + "Configuration for twilio_txt_msg beacon " + "must contain account_sid, auth_token " + "and twilio_number items." + ), + ) + return True, "Valid beacon configuration" def beacon(config): - ''' + """ Emit a dict name "texts" whose value is a list of texts. @@ -71,40 +76,40 @@ def beacon(config): - twilio_number: "+15555555555" - interval: 10 - ''' - log.trace('twilio_txt_msg beacon starting') + """ + log.trace("twilio_txt_msg beacon starting") _config = {} list(map(_config.update, config)) ret = [] - if not all([_config['account_sid'], - _config['auth_token'], - _config['twilio_number']]): + if not all( + [_config["account_sid"], _config["auth_token"], _config["twilio_number"]] + ): return ret output = {} - output['texts'] = [] - client = TwilioRestClient(_config['account_sid'], _config['auth_token']) - messages = client.messages.list(to=_config['twilio_number']) - log.trace('Num messages: %d', len(messages)) + output["texts"] = [] + client = TwilioRestClient(_config["account_sid"], _config["auth_token"]) + messages = client.messages.list(to=_config["twilio_number"]) + log.trace("Num messages: %d", len(messages)) if len(messages) < 1: - log.trace('Twilio beacon has no texts') + log.trace("Twilio beacon has no texts") return ret for message in messages: item = {} - item['id'] = six.text_type(message.sid) - item['body'] = six.text_type(message.body) - item['from'] = six.text_type(message.from_) - item['sent'] = six.text_type(message.date_sent) - item['images'] = [] + item["id"] = six.text_type(message.sid) + item["body"] = six.text_type(message.body) + item["from"] = six.text_type(message.from_) + item["sent"] = six.text_type(message.date_sent) + item["images"] = [] if int(message.num_media): media = client.media(message.sid).list() if media: for pic in media: - item['images'].append(six.text_type(pic.uri)) - output['texts'].append(item) + item["images"].append(six.text_type(pic.uri)) + output["texts"].append(item) message.delete() ret.append(output) return ret diff --git a/salt/beacons/watchdog.py b/salt/beacons/watchdog.py index 3df616b6086..9a5b00278a3 100644 --- a/salt/beacons/watchdog.py +++ b/salt/beacons/watchdog.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" watchdog beacon .. versionadded:: 2019.2.0 @@ -8,9 +8,10 @@ Watch files and translate the changes into salt events :depends: - watchdog Python module >= 0.8.3 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import collections import logging @@ -20,24 +21,27 @@ from salt.ext.six.moves import map try: from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler + HAS_WATCHDOG = True except ImportError: HAS_WATCHDOG = False class FileSystemEventHandler(object): """ A dummy class to make the import work """ + def __init__(self): pass -__virtualname__ = 'watchdog' + +__virtualname__ = "watchdog" log = logging.getLogger(__name__) DEFAULT_MASK = [ - 'create', - 'delete', - 'modify', - 'move', + "create", + "delete", + "modify", + "move", ] @@ -48,16 +52,16 @@ class Handler(FileSystemEventHandler): self.queue = queue def on_created(self, event): - self._append_if_mask(event, 'create') + self._append_if_mask(event, "create") def on_modified(self, event): - self._append_if_mask(event, 'modify') + self._append_if_mask(event, "modify") def on_deleted(self, event): - self._append_if_mask(event, 'delete') + self._append_if_mask(event, "delete") def on_moved(self, event): - self._append_if_mask(event, 'move') + self._append_if_mask(event, "move") def _append_if_mask(self, event, mask): logging.debug(event) @@ -76,25 +80,25 @@ def __virtual__(): def _get_queue(config): - ''' + """ Check the context for the notifier and construct it if not present - ''' + """ - if 'watchdog.observer' not in __context__: + if "watchdog.observer" not in __context__: queue = collections.deque() observer = Observer() - for path in config.get('directories', {}): - path_params = config.get('directories').get(path) - masks = path_params.get('mask', DEFAULT_MASK) + for path in config.get("directories", {}): + path_params = config.get("directories").get(path) + masks = path_params.get("mask", DEFAULT_MASK) event_handler = Handler(queue, masks) observer.schedule(event_handler, path) observer.start() - __context__['watchdog.observer'] = observer - __context__['watchdog.queue'] = queue + __context__["watchdog.observer"] = observer + __context__["watchdog.queue"] = queue - return __context__['watchdog.queue'] + return __context__["watchdog.queue"] class ValidationError(Exception): @@ -102,76 +106,74 @@ class ValidationError(Exception): def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ try: _validate(config) - return True, 'Valid beacon configuration' + return True, "Valid beacon configuration" except ValidationError as error: return False, str(error) def _validate(config): if not isinstance(config, list): - raise ValidationError( - 'Configuration for watchdog beacon must be a list.') + raise ValidationError("Configuration for watchdog beacon must be a list.") _config = {} for part in config: _config.update(part) - if 'directories' not in _config: + if "directories" not in _config: raise ValidationError( - 'Configuration for watchdog beacon must include directories.') + "Configuration for watchdog beacon must include directories." + ) - if not isinstance(_config['directories'], dict): + if not isinstance(_config["directories"], dict): raise ValidationError( - 'Configuration for watchdog beacon directories must be a ' - 'dictionary.') + "Configuration for watchdog beacon directories must be a " "dictionary." + ) - for path in _config['directories']: - _validate_path(_config['directories'][path]) + for path in _config["directories"]: + _validate_path(_config["directories"][path]) def _validate_path(path_config): if not isinstance(path_config, dict): raise ValidationError( - 'Configuration for watchdog beacon directory path must be ' - 'a dictionary.') + "Configuration for watchdog beacon directory path must be " "a dictionary." + ) - if 'mask' in path_config: - _validate_mask(path_config['mask']) + if "mask" in path_config: + _validate_mask(path_config["mask"]) def _validate_mask(mask_config): valid_mask = [ - 'create', - 'modify', - 'delete', - 'move', + "create", + "modify", + "delete", + "move", ] if not isinstance(mask_config, list): - raise ValidationError( - 'Configuration for watchdog beacon mask must be list.') + raise ValidationError("Configuration for watchdog beacon mask must be list.") if any(mask not in valid_mask for mask in mask_config): - raise ValidationError( - 'Configuration for watchdog beacon contains invalid mask') + raise ValidationError("Configuration for watchdog beacon contains invalid mask") def to_salt_event(event): return { - 'tag': __virtualname__, - 'path': event.src_path, - 'change': event.event_type, + "tag": __virtualname__, + "path": event.src_path, + "change": event.event_type, } def beacon(config): - ''' + """ Watch the configured directories Example Config @@ -194,7 +196,7 @@ def beacon(config): * modify - The watched directory is modified * delete - File or directory is deleted from watched directory * move - File or directory is moved or renamed in the watched directory - ''' + """ _config = {} list(map(_config.update, config)) @@ -209,7 +211,7 @@ def beacon(config): def close(config): - observer = __context__.pop('watchdog.observer', None) + observer = __context__.pop("watchdog.observer", None) if observer: observer.stop() diff --git a/salt/beacons/wtmp.py b/salt/beacons/wtmp.py index c5b4f11b772..13fc5597223 100644 --- a/salt/beacons/wtmp.py +++ b/salt/beacons/wtmp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Beacon to fire events at login of users as registered in the wtmp file .. versionadded:: 2015.5.0 @@ -115,43 +115,47 @@ Match the event like so in the master config file: API key to post to Slack, a bot user is likely better suited for this. The :py:mod:`slack engine ` documentation has information on how to set up a bot user. -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import datetime import logging import os import struct +import salt.utils.files + # Import salt libs import salt.utils.stringutils -import salt.utils.files # Import 3rd-party libs from salt.ext import six + # pylint: disable=import-error from salt.ext.six.moves import map + # pylint: enable=import-error -__virtualname__ = 'wtmp' -WTMP = '/var/log/wtmp' -FMT = b'hi32s4s32s256shhiii4i20x' +__virtualname__ = "wtmp" +WTMP = "/var/log/wtmp" +FMT = b"hi32s4s32s256shhiii4i20x" FIELDS = [ - 'type', - 'PID', - 'line', - 'inittab', - 'user', - 'hostname', - 'exit_status', - 'session', - 'time', - 'addr' + "type", + "PID", + "line", + "inittab", + "user", + "hostname", + "exit_status", + "session", + "time", + "addr", ] SIZE = struct.calcsize(FMT) -LOC_KEY = 'wtmp.loc' -TTY_KEY_PREFIX = 'wtmp.tty.' +LOC_KEY = "wtmp.loc" +TTY_KEY_PREFIX = "wtmp.tty." LOGIN_TYPE = 7 LOGOUT_TYPE = 8 @@ -160,6 +164,7 @@ log = logging.getLogger(__name__) # pylint: disable=import-error try: import dateutil.parser as dateutil_parser + _TIME_SUPPORTED = True except ImportError: _TIME_SUPPORTED = False @@ -172,119 +177,110 @@ def __virtual__(): def _validate_time_range(trange, status, msg): - ''' + """ Check time range - ''' + """ # If trange is empty, just return the current status & msg if not trange: return status, msg if not isinstance(trange, dict): status = False - msg = ('The time_range parameter for ' - 'wtmp beacon must ' - 'be a dictionary.') + msg = "The time_range parameter for " "wtmp beacon must " "be a dictionary." - if not all(k in trange for k in ('start', 'end')): + if not all(k in trange for k in ("start", "end")): status = False - msg = ('The time_range parameter for ' - 'wtmp beacon must contain ' - 'start & end options.') + msg = ( + "The time_range parameter for " + "wtmp beacon must contain " + "start & end options." + ) return status, msg def _gather_group_members(group, groups, users): - ''' + """ Gather group members - ''' - _group = __salt__['group.info'](group) + """ + _group = __salt__["group.info"](group) if not _group: - log.warning('Group %s does not exist, ignoring.', group) + log.warning("Group %s does not exist, ignoring.", group) return - for member in _group['members']: + for member in _group["members"]: if member not in users: users[member] = groups[group] def _check_time_range(time_range, now): - ''' + """ Check time range - ''' + """ if _TIME_SUPPORTED: - _start = dateutil_parser.parse(time_range['start']) - _end = dateutil_parser.parse(time_range['end']) + _start = dateutil_parser.parse(time_range["start"]) + _end = dateutil_parser.parse(time_range["end"]) return bool(_start <= now <= _end) else: - log.error('Dateutil is required.') + log.error("Dateutil is required.") return False def _get_loc(): - ''' + """ return the active file location - ''' + """ if LOC_KEY in __context__: return __context__[LOC_KEY] def validate(config): - ''' + """ Validate the beacon configuration - ''' + """ vstatus = True - vmsg = 'Valid beacon configuration' + vmsg = "Valid beacon configuration" # Configuration for wtmp beacon should be a list of dicts if not isinstance(config, list): vstatus = False - vmsg = ('Configuration for wtmp beacon must be a list.') + vmsg = "Configuration for wtmp beacon must be a list." else: _config = {} list(map(_config.update, config)) - if 'users' in _config: - if not isinstance(_config['users'], dict): + if "users" in _config: + if not isinstance(_config["users"], dict): vstatus = False - vmsg = ('User configuration for wtmp beacon must ' - 'be a dictionary.') + vmsg = "User configuration for wtmp beacon must " "be a dictionary." else: - for user in _config['users']: - _time_range = _config['users'][user].get('time_range', {}) - vstatus, vmsg = _validate_time_range(_time_range, - vstatus, - vmsg) + for user in _config["users"]: + _time_range = _config["users"][user].get("time_range", {}) + vstatus, vmsg = _validate_time_range(_time_range, vstatus, vmsg) if not vstatus: return vstatus, vmsg - if 'groups' in _config: - if not isinstance(_config['groups'], dict): + if "groups" in _config: + if not isinstance(_config["groups"], dict): vstatus = False - vmsg = ('Group configuration for wtmp beacon must ' - 'be a dictionary.') + vmsg = "Group configuration for wtmp beacon must " "be a dictionary." else: - for group in _config['groups']: - _time_range = _config['groups'][group].get('time_range', {}) - vstatus, vmsg = _validate_time_range(_time_range, - vstatus, - vmsg) + for group in _config["groups"]: + _time_range = _config["groups"][group].get("time_range", {}) + vstatus, vmsg = _validate_time_range(_time_range, vstatus, vmsg) if not vstatus: return vstatus, vmsg - if 'defaults' in _config: - if not isinstance(_config['defaults'], dict): + if "defaults" in _config: + if not isinstance(_config["defaults"], dict): vstatus = False - vmsg = ('Defaults configuration for wtmp beacon must ' - 'be a dictionary.') + vmsg = "Defaults configuration for wtmp beacon must " "be a dictionary." else: - _time_range = _config['defaults'].get('time_range', {}) - vstatus, vmsg = _validate_time_range(_time_range, - vstatus, - vmsg) + _time_range = _config["defaults"].get("time_range", {}) + vstatus, vmsg = _validate_time_range(_time_range, vstatus, vmsg) if not vstatus: return vstatus, vmsg @@ -292,9 +288,9 @@ def validate(config): def beacon(config): - ''' + """ Read the last wtmp file and return information on the logins - ''' + """ ret = [] users = {} @@ -305,26 +301,26 @@ def beacon(config): logout_type = LOGOUT_TYPE for config_item in config: - if 'users' in config_item: - users = config_item['users'] + if "users" in config_item: + users = config_item["users"] - if 'groups' in config_item: - groups = config_item['groups'] + if "groups" in config_item: + groups = config_item["groups"] - if 'defaults' in config_item: - defaults = config_item['defaults'] + if "defaults" in config_item: + defaults = config_item["defaults"] - if config_item == 'ut_type': + if config_item == "ut_type": try: - login_type = config_item['ut_type']['login'] + login_type = config_item["ut_type"]["login"] except KeyError: pass try: - logout_type = config_item['ut_type']['logout'] + logout_type = config_item["ut_type"]["logout"] except KeyError: pass - with salt.utils.files.fopen(WTMP, 'rb') as fp_: + with salt.utils.files.fopen(WTMP, "rb") as fp_: loc = __context__.get(LOC_KEY, 0) if loc == 0: fp_.seek(0, 2) @@ -345,16 +341,20 @@ def beacon(config): if isinstance(event[field], six.string_types): if isinstance(event[field], bytes): event[field] = salt.utils.stringutils.to_unicode(event[field]) - event[field] = event[field].strip('\x00') + event[field] = event[field].strip("\x00") - if event['type'] == login_type: - event['action'] = 'login' + if event["type"] == login_type: + event["action"] = "login" # Store the tty to identify the logout event - __context__['{0}{1}'.format(TTY_KEY_PREFIX, event['line'])] = event['user'] - elif event['type'] == logout_type: - event['action'] = 'logout' + __context__["{0}{1}".format(TTY_KEY_PREFIX, event["line"])] = event[ + "user" + ] + elif event["type"] == logout_type: + event["action"] = "logout" try: - event['user'] = __context__.pop('{0}{1}'.format(TTY_KEY_PREFIX, event['line'])) + event["user"] = __context__.pop( + "{0}{1}".format(TTY_KEY_PREFIX, event["line"]) + ) except KeyError: pass @@ -362,21 +362,20 @@ def beacon(config): _gather_group_members(group, groups, users) if users: - if event['user'] in users: - _user = users[event['user']] - if isinstance(_user, dict) and 'time_range' in _user: - if _check_time_range(_user['time_range'], now): + if event["user"] in users: + _user = users[event["user"]] + if isinstance(_user, dict) and "time_range" in _user: + if _check_time_range(_user["time_range"], now): ret.append(event) else: - if defaults and 'time_range' in defaults: - if _check_time_range(defaults['time_range'], - now): + if defaults and "time_range" in defaults: + if _check_time_range(defaults["time_range"], now): ret.append(event) else: ret.append(event) else: - if defaults and 'time_range' in defaults: - if _check_time_range(defaults['time_range'], now): + if defaults and "time_range" in defaults: + if _check_time_range(defaults["time_range"], now): ret.append(event) else: ret.append(event) diff --git a/salt/cache/__init__.py b/salt/cache/__init__.py index 68ffcdcff39..b7c6ca98095 100644 --- a/salt/cache/__init__.py +++ b/salt/cache/__init__.py @@ -1,33 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Loader mechanism for caching data, with data expiration, etc. .. versionadded:: 2016.11.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time # Import Salt libs import salt.config +import salt.loader +import salt.syspaths from salt.ext import six from salt.payload import Serial from salt.utils.odict import OrderedDict -import salt.loader -import salt.syspaths log = logging.getLogger(__name__) def factory(opts, **kwargs): - ''' + """ Creates and returns the cache class. If memory caching is enabled by opts MemCache class will be instantiated. If not Cache class will be returned. - ''' - if opts.get('memcache_expire_seconds', 0): + """ + if opts.get("memcache_expire_seconds", 0): cls = MemCache else: cls = Cache @@ -35,7 +36,7 @@ def factory(opts, **kwargs): class Cache(object): - ''' + """ Base caching object providing access to the modular cache subsystem. Related configuration options: @@ -66,22 +67,23 @@ class Cache(object): Key name is a string identifier of a data container (like a file inside a directory) which will hold the data. - ''' + """ + def __init__(self, opts, cachedir=None, **kwargs): self.opts = opts if cachedir is None: - self.cachedir = opts.get('cachedir', salt.syspaths.CACHE_DIR) + self.cachedir = opts.get("cachedir", salt.syspaths.CACHE_DIR) else: self.cachedir = cachedir - self.driver = opts.get('cache', salt.config.DEFAULT_MASTER_OPTS['cache']) + self.driver = opts.get("cache", salt.config.DEFAULT_MASTER_OPTS["cache"]) self.serial = Serial(opts) self._modules = None self._kwargs = kwargs - self._kwargs['cachedir'] = self.cachedir + self._kwargs["cachedir"] = self.cachedir def __lazy_init(self): self._modules = salt.loader.cache(self.opts, self.serial) - fun = '{0}.init_kwargs'.format(self.driver) + fun = "{0}.init_kwargs".format(self.driver) if fun in self.modules: self._kwargs = self.modules[fun](self._kwargs) else: @@ -94,7 +96,7 @@ class Cache(object): return self._modules def cache(self, bank, key, fun, loop_fun=None, **kwargs): - ''' + """ Check cache for the data. If it is there, check to see if it needs to be refreshed. @@ -106,8 +108,8 @@ class Cache(object): the second function is passed in as ``loop_fun``. Each item in the return list from the first function will be the only argument for the second function. - ''' - expire_seconds = kwargs.get('expire', 86400) # 1 day + """ + expire_seconds = kwargs.get("expire", 86400) # 1 day updated = self.updated(bank, key) update_cache = False @@ -132,7 +134,7 @@ class Cache(object): return data def store(self, bank, key, data): - ''' + """ Store data using the specified module :param bank: @@ -151,12 +153,12 @@ class Cache(object): :raises SaltCacheError: Raises an exception if cache driver detected an error accessing data in the cache backend (auth, permissions, etc). - ''' - fun = '{0}.store'.format(self.driver) + """ + fun = "{0}.store".format(self.driver) return self.modules[fun](bank, key, data, **self._kwargs) def fetch(self, bank, key): - ''' + """ Fetch data using the specified module :param bank: @@ -175,12 +177,12 @@ class Cache(object): :raises SaltCacheError: Raises an exception if cache driver detected an error accessing data in the cache backend (auth, permissions, etc). - ''' - fun = '{0}.fetch'.format(self.driver) + """ + fun = "{0}.fetch".format(self.driver) return self.modules[fun](bank, key, **self._kwargs) def updated(self, bank, key): - ''' + """ Get the last updated epoch for the specified key :param bank: @@ -199,12 +201,12 @@ class Cache(object): :raises SaltCacheError: Raises an exception if cache driver detected an error accessing data in the cache backend (auth, permissions, etc). - ''' - fun = '{0}.updated'.format(self.driver) + """ + fun = "{0}.updated".format(self.driver) return self.modules[fun](bank, key, **self._kwargs) def flush(self, bank, key=None): - ''' + """ Remove the key from the cache bank with all the key content. If no key is specified remove the entire bank with all keys and sub-banks inside. @@ -220,12 +222,12 @@ class Cache(object): :raises SaltCacheError: Raises an exception if cache driver detected an error accessing data in the cache backend (auth, permissions, etc). - ''' - fun = '{0}.flush'.format(self.driver) + """ + fun = "{0}.flush".format(self.driver) return self.modules[fun](bank, key=key, **self._kwargs) def list(self, bank): - ''' + """ Lists entries stored in the specified bank. :param bank: @@ -239,12 +241,12 @@ class Cache(object): :raises SaltCacheError: Raises an exception if cache driver detected an error accessing data in the cache backend (auth, permissions, etc). - ''' - fun = '{0}.list'.format(self.driver) + """ + fun = "{0}.list".format(self.driver) return self.modules[fun](bank, **self._kwargs) def contains(self, bank, key=None): - ''' + """ Checks if the specified bank contains the specified key. :param bank: @@ -264,25 +266,26 @@ class Cache(object): :raises SaltCacheError: Raises an exception if cache driver detected an error accessing data in the cache backend (auth, permissions, etc). - ''' - fun = '{0}.contains'.format(self.driver) + """ + fun = "{0}.contains".format(self.driver) return self.modules[fun](bank, key, **self._kwargs) class MemCache(Cache): - ''' + """ Short-lived in-memory cache store keeping values on time and/or size (count) basis. - ''' + """ + # {: odict({: [atime, data], ...}), ...} data = {} def __init__(self, opts, **kwargs): super(MemCache, self).__init__(opts, **kwargs) - self.expire = opts.get('memcache_expire_seconds', 10) - self.max = opts.get('memcache_max_items', 1024) - self.cleanup = opts.get('memcache_full_cleanup', False) - self.debug = opts.get('memcache_debug', False) + self.expire = opts.get("memcache_expire_seconds", 10) + self.max = opts.get("memcache_max_items", 1024) + self.cleanup = opts.get("memcache_full_cleanup", False) + self.debug = opts.get("memcache_debug", False) if self.debug: self.call = 0 self.hit = 0 @@ -299,7 +302,7 @@ class MemCache(Cache): break def _get_storage_id(self): - fun = '{0}.storage_id'.format(self.driver) + fun = "{0}.storage_id".format(self.driver) if fun in self.modules: return self.modules[fun](self.kwargs) else: @@ -324,8 +327,10 @@ class MemCache(Cache): if self.debug: self.hit += 1 log.debug( - 'MemCache stats (call/hit/rate): %s/%s/%s', - self.call, self.hit, float(self.hit) / self.call + "MemCache stats (call/hit/rate): %s/%s/%s", + self.call, + self.hit, + float(self.hit) / self.call, ) # update atime and return record[0] = now diff --git a/salt/cache/consul.py b/salt/cache/consul.py index 70c2f0c3767..8e611021882 100644 --- a/salt/cache/consul.py +++ b/salt/cache/consul.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Minion data cache plugin for Consul key/value data store. .. versionadded:: 2016.11.2 @@ -45,116 +45,118 @@ value to ``consul``: .. _`Consul documentation`: https://www.consul.io/docs/index.html .. _`python-consul documentation`: https://python-consul.readthedocs.io/en/latest/#consul -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging + +from salt.exceptions import SaltCacheError + try: import consul + HAS_CONSUL = True except ImportError: HAS_CONSUL = False -from salt.exceptions import SaltCacheError log = logging.getLogger(__name__) api = None # Define the module's virtual name -__virtualname__ = 'consul' +__virtualname__ = "consul" -__func_alias__ = {'list_': 'list'} +__func_alias__ = {"list_": "list"} def __virtual__(): - ''' + """ Confirm this python-consul package is installed - ''' + """ if not HAS_CONSUL: - return (False, "Please install python-consul package to use consul data cache driver") + return ( + False, + "Please install python-consul package to use consul data cache driver", + ) consul_kwargs = { - 'host': __opts__.get('consul.host', '127.0.0.1'), - 'port': __opts__.get('consul.port', 8500), - 'token': __opts__.get('consul.token', None), - 'scheme': __opts__.get('consul.scheme', 'http'), - 'consistency': __opts__.get('consul.consistency', 'default'), - 'dc': __opts__.get('consul.dc', None), - 'verify': __opts__.get('consul.verify', True), - } + "host": __opts__.get("consul.host", "127.0.0.1"), + "port": __opts__.get("consul.port", 8500), + "token": __opts__.get("consul.token", None), + "scheme": __opts__.get("consul.scheme", "http"), + "consistency": __opts__.get("consul.consistency", "default"), + "dc": __opts__.get("consul.dc", None), + "verify": __opts__.get("consul.verify", True), + } try: global api api = consul.Consul(**consul_kwargs) except AttributeError: - return (False, "Failed to invoke consul.Consul, please make sure you have python-consul >= 0.2.0 installed") + return ( + False, + "Failed to invoke consul.Consul, please make sure you have python-consul >= 0.2.0 installed", + ) return __virtualname__ def store(bank, key, data): - ''' + """ Store a key value. - ''' - c_key = '{0}/{1}'.format(bank, key) + """ + c_key = "{0}/{1}".format(bank, key) try: - c_data = __context__['serial'].dumps(data) + c_data = __context__["serial"].dumps(data) api.kv.put(c_key, c_data) except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error writing the key, {0}: {1}'.format( - c_key, exc - ) + "There was an error writing the key, {0}: {1}".format(c_key, exc) ) def fetch(bank, key): - ''' + """ Fetch a key value. - ''' - c_key = '{0}/{1}'.format(bank, key) + """ + c_key = "{0}/{1}".format(bank, key) try: _, value = api.kv.get(c_key) if value is None: return {} - return __context__['serial'].loads(value['Value']) + return __context__["serial"].loads(value["Value"]) except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error reading the key, {0}: {1}'.format( - c_key, exc - ) + "There was an error reading the key, {0}: {1}".format(c_key, exc) ) def flush(bank, key=None): - ''' + """ Remove the key from the cache bank with all the key content. - ''' + """ if key is None: c_key = bank else: - c_key = '{0}/{1}'.format(bank, key) + c_key = "{0}/{1}".format(bank, key) try: return api.kv.delete(c_key, recurse=key is None) except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error removing the key, {0}: {1}'.format( - c_key, exc - ) + "There was an error removing the key, {0}: {1}".format(c_key, exc) ) def list_(bank): - ''' + """ Return an iterable object containing all entries stored in the specified bank. - ''' + """ try: - _, keys = api.kv.get(bank + '/', keys=True, separator='/') + _, keys = api.kv.get(bank + "/", keys=True, separator="/") except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error getting the key "{0}": {1}'.format( - bank, exc - ) + 'There was an error getting the key "{0}": {1}'.format(bank, exc) ) if keys is None: keys = [] @@ -163,25 +165,23 @@ def list_(bank): # so we have to return a list of unique names only. out = set() for key in keys: - out.add(key[len(bank) + 1:].rstrip('/')) + out.add(key[len(bank) + 1 :].rstrip("/")) keys = list(out) return keys def contains(bank, key): - ''' + """ Checks if the specified bank contains the specified key. - ''' + """ if key is None: return True # any key could be a branch and a leaf at the same time in Consul else: try: - c_key = '{0}/{1}'.format(bank, key) + c_key = "{0}/{1}".format(bank, key) _, value = api.kv.get(c_key) except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error getting the key, {0}: {1}'.format( - c_key, exc - ) + "There was an error getting the key, {0}: {1}".format(c_key, exc) ) return value is not None diff --git a/salt/cache/etcd_cache.py b/salt/cache/etcd_cache.py index ecd49048d30..9325e6fd7c6 100644 --- a/salt/cache/etcd_cache.py +++ b/salt/cache/etcd_cache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Minion data cache plugin for Etcd key/value data store. .. versionadded:: develop @@ -47,17 +47,21 @@ value to ``etcd``: .. _`Etcd documentation`: https://github.com/coreos/etcd .. _`python-etcd documentation`: http://python-etcd.readthedocs.io/en/latest/ -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import logging + import base64 +import logging + +from salt.exceptions import SaltCacheError + try: import etcd + HAS_ETCD = True except ImportError: HAS_ETCD = False -from salt.exceptions import SaltCacheError _DEFAULT_PATH_PREFIX = "/salt_cache" @@ -72,44 +76,46 @@ path_prefix = None # Module properties -__virtualname__ = 'etcd' -__func_alias__ = {'ls': 'list'} +__virtualname__ = "etcd" +__func_alias__ = {"ls": "list"} def __virtual__(): - ''' + """ Confirm that python-etcd package is installed. - ''' + """ if not HAS_ETCD: - return (False, "Please install python-etcd package to use etcd data " - "cache driver") + return ( + False, + "Please install python-etcd package to use etcd data cache driver", + ) return __virtualname__ def _init_client(): - '''Setup client and init datastore. - ''' + """Setup client and init datastore. + """ global client, path_prefix if client is not None: return etcd_kwargs = { - 'host': __opts__.get('etcd.host', '127.0.0.1'), - 'port': __opts__.get('etcd.port', 2379), - 'protocol': __opts__.get('etcd.protocol', 'http'), - 'allow_reconnect': __opts__.get('etcd.allow_reconnect', True), - 'allow_redirect': __opts__.get('etcd.allow_redirect', False), - 'srv_domain': __opts__.get('etcd.srv_domain', None), - 'read_timeout': __opts__.get('etcd.read_timeout', 60), - 'username': __opts__.get('etcd.username', None), - 'password': __opts__.get('etcd.password', None), - 'cert': __opts__.get('etcd.cert', None), - 'ca_cert': __opts__.get('etcd.ca_cert', None), + "host": __opts__.get("etcd.host", "127.0.0.1"), + "port": __opts__.get("etcd.port", 2379), + "protocol": __opts__.get("etcd.protocol", "http"), + "allow_reconnect": __opts__.get("etcd.allow_reconnect", True), + "allow_redirect": __opts__.get("etcd.allow_redirect", False), + "srv_domain": __opts__.get("etcd.srv_domain", None), + "read_timeout": __opts__.get("etcd.read_timeout", 60), + "username": __opts__.get("etcd.username", None), + "password": __opts__.get("etcd.password", None), + "cert": __opts__.get("etcd.cert", None), + "ca_cert": __opts__.get("etcd.ca_cert", None), } - path_prefix = __opts__.get('etcd.path_prefix', _DEFAULT_PATH_PREFIX) + path_prefix = __opts__.get("etcd.path_prefix", _DEFAULT_PATH_PREFIX) if path_prefix != "": - path_prefix = '/{0}'.format(path_prefix.strip('/')) + path_prefix = "/{0}".format(path_prefix.strip("/")) log.info("etcd: Setting up client with params: %r", etcd_kwargs) client = etcd.Client(**etcd_kwargs) try: @@ -120,48 +126,46 @@ def _init_client(): def store(bank, key, data): - ''' + """ Store a key value. - ''' + """ _init_client() - etcd_key = '{0}/{1}/{2}'.format(path_prefix, bank, key) + etcd_key = "{0}/{1}/{2}".format(path_prefix, bank, key) try: - value = __context__['serial'].dumps(data) + value = __context__["serial"].dumps(data) client.write(etcd_key, base64.b64encode(value)) except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error writing the key, {0}: {1}'.format(etcd_key, exc) + "There was an error writing the key, {0}: {1}".format(etcd_key, exc) ) def fetch(bank, key): - ''' + """ Fetch a key value. - ''' + """ _init_client() - etcd_key = '{0}/{1}/{2}'.format(path_prefix, bank, key) + etcd_key = "{0}/{1}/{2}".format(path_prefix, bank, key) try: value = client.read(etcd_key).value - return __context__['serial'].loads(base64.b64decode(value)) + return __context__["serial"].loads(base64.b64decode(value)) except etcd.EtcdKeyNotFound: return {} except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error reading the key, {0}: {1}'.format( - etcd_key, exc - ) + "There was an error reading the key, {0}: {1}".format(etcd_key, exc) ) def flush(bank, key=None): - ''' + """ Remove the key from the cache bank with all the key content. - ''' + """ _init_client() if key is None: - etcd_key = '{0}/{1}'.format(path_prefix, bank) + etcd_key = "{0}/{1}".format(path_prefix, bank) else: - etcd_key = '{0}/{1}/{2}'.format(path_prefix, bank, key) + etcd_key = "{0}/{1}/{2}".format(path_prefix, bank, key) try: client.read(etcd_key) except etcd.EtcdKeyNotFound: @@ -170,19 +174,17 @@ def flush(bank, key=None): client.delete(etcd_key, recursive=True) except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error removing the key, {0}: {1}'.format( - etcd_key, exc - ) + "There was an error removing the key, {0}: {1}".format(etcd_key, exc) ) def _walk(r): - ''' + """ Recursively walk dirs. Return flattened list of keys. r: etcd.EtcdResult - ''' + """ if not r.dir: - return [r.key.split('/', 3)[3]] + return [r.key.split("/", 3)[3]] keys = [] for c in client.read(r.key).children: @@ -191,28 +193,26 @@ def _walk(r): def ls(bank): - ''' + """ Return an iterable object containing all entries stored in the specified bank. - ''' + """ _init_client() - path = '{0}/{1}'.format(path_prefix, bank) + path = "{0}/{1}".format(path_prefix, bank) try: return _walk(client.read(path)) except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error getting the key "{0}": {1}'.format( - bank, exc - ) + 'There was an error getting the key "{0}": {1}'.format(bank, exc) ) def contains(bank, key): - ''' + """ Checks if the specified bank contains the specified key. - ''' + """ _init_client() - etcd_key = '{0}/{1}/{2}'.format(path_prefix, bank, key) + etcd_key = "{0}/{1}/{2}".format(path_prefix, bank, key) try: r = client.read(etcd_key) # return True for keys, not dirs @@ -221,7 +221,5 @@ def contains(bank, key): return False except Exception as exc: # pylint: disable=broad-except raise SaltCacheError( - 'There was an error getting the key, {0}: {1}'.format( - etcd_key, exc - ) + "There was an error getting the key, {0}: {1}".format(etcd_key, exc) ) diff --git a/salt/cache/localfs.py b/salt/cache/localfs.py index 58aea7c1ec8..fe97270394a 100644 --- a/salt/cache/localfs.py +++ b/salt/cache/localfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Cache data in filesystem. .. versionadded:: 2016.11.0 @@ -9,102 +9,97 @@ require any configuration. Expiration values can be set in the relevant config file (``/etc/salt/master`` for the master, ``/etc/salt/cloud`` for Salt Cloud, etc). -''' +""" from __future__ import absolute_import, print_function, unicode_literals + +import errno import logging import os import os.path -import errno import shutil import tempfile -from salt.exceptions import SaltCacheError import salt.utils.atomicfile import salt.utils.files +from salt.exceptions import SaltCacheError log = logging.getLogger(__name__) -__func_alias__ = {'list_': 'list'} +__func_alias__ = {"list_": "list"} def __cachedir(kwargs=None): - if kwargs and 'cachedir' in kwargs: - return kwargs['cachedir'] - return __opts__.get('cachedir', salt.syspaths.CACHE_DIR) + if kwargs and "cachedir" in kwargs: + return kwargs["cachedir"] + return __opts__.get("cachedir", salt.syspaths.CACHE_DIR) def init_kwargs(kwargs): - return {'cachedir': __cachedir(kwargs)} + return {"cachedir": __cachedir(kwargs)} def get_storage_id(kwargs): - return ('localfs', __cachedir(kwargs)) + return ("localfs", __cachedir(kwargs)) def store(bank, key, data, cachedir): - ''' + """ Store information in a file. - ''' + """ base = os.path.join(cachedir, os.path.normpath(bank)) try: os.makedirs(base) except OSError as exc: if exc.errno != errno.EEXIST: raise SaltCacheError( - 'The cache directory, {0}, could not be created: {1}'.format( - base, exc - ) + "The cache directory, {0}, could not be created: {1}".format(base, exc) ) - outfile = os.path.join(base, '{0}.p'.format(key)) + outfile = os.path.join(base, "{0}.p".format(key)) tmpfh, tmpfname = tempfile.mkstemp(dir=base) os.close(tmpfh) try: - with salt.utils.files.fopen(tmpfname, 'w+b') as fh_: - fh_.write(__context__['serial'].dumps(data)) + with salt.utils.files.fopen(tmpfname, "w+b") as fh_: + fh_.write(__context__["serial"].dumps(data)) # On Windows, os.rename will fail if the destination file exists. salt.utils.atomicfile.atomic_rename(tmpfname, outfile) except IOError as exc: raise SaltCacheError( - 'There was an error writing the cache file, {0}: {1}'.format( - base, exc - ) + "There was an error writing the cache file, {0}: {1}".format(base, exc) ) def fetch(bank, key, cachedir): - ''' + """ Fetch information from a file. - ''' + """ inkey = False - key_file = os.path.join(cachedir, os.path.normpath(bank), '{0}.p'.format(key)) + key_file = os.path.join(cachedir, os.path.normpath(bank), "{0}.p".format(key)) if not os.path.isfile(key_file): # The bank includes the full filename, and the key is inside the file - key_file = os.path.join(cachedir, os.path.normpath(bank) + '.p') + key_file = os.path.join(cachedir, os.path.normpath(bank) + ".p") inkey = True if not os.path.isfile(key_file): log.debug('Cache file "%s" does not exist', key_file) return {} try: - with salt.utils.files.fopen(key_file, 'rb') as fh_: + with salt.utils.files.fopen(key_file, "rb") as fh_: if inkey: - return __context__['serial'].load(fh_)[key] + return __context__["serial"].load(fh_)[key] else: - return __context__['serial'].load(fh_) + return __context__["serial"].load(fh_) except IOError as exc: raise SaltCacheError( - 'There was an error reading the cache file "{0}": {1}'.format( - key_file, exc - ) + 'There was an error reading the cache file "{0}": {1}'.format(key_file, exc) ) def updated(bank, key, cachedir): - ''' + """ Return the epoch of the mtime for this cache file - ''' - key_file = os.path.join(cachedir, os.path.normpath(bank), '{0}.p'.format(key)) + """ + key_file = os.path.join(cachedir, os.path.normpath(bank), "{0}.p".format(key)) if not os.path.isfile(key_file): log.warning('Cache file "%s" does not exist', key_file) return None @@ -112,16 +107,14 @@ def updated(bank, key, cachedir): return int(os.path.getmtime(key_file)) except IOError as exc: raise SaltCacheError( - 'There was an error reading the mtime for "{0}": {1}'.format( - key_file, exc - ) + 'There was an error reading the mtime for "{0}": {1}'.format(key_file, exc) ) def flush(bank, key=None, cachedir=None): - ''' + """ Remove the key from the cache bank with all the key content. - ''' + """ if cachedir is None: cachedir = __cachedir() @@ -132,23 +125,21 @@ def flush(bank, key=None, cachedir=None): return False shutil.rmtree(target) else: - target = os.path.join(cachedir, os.path.normpath(bank), '{0}.p'.format(key)) + target = os.path.join(cachedir, os.path.normpath(bank), "{0}.p".format(key)) if not os.path.isfile(target): return False os.remove(target) except OSError as exc: raise SaltCacheError( - 'There was an error removing "{0}": {1}'.format( - target, exc - ) + 'There was an error removing "{0}": {1}'.format(target, exc) ) return True def list_(bank, cachedir): - ''' + """ Return an iterable object containing all entries stored in the specified bank. - ''' + """ base = os.path.join(cachedir, os.path.normpath(bank)) if not os.path.isdir(base): return [] @@ -156,13 +147,11 @@ def list_(bank, cachedir): items = os.listdir(base) except OSError as exc: raise SaltCacheError( - 'There was an error accessing directory "{0}": {1}'.format( - base, exc - ) + 'There was an error accessing directory "{0}": {1}'.format(base, exc) ) ret = [] for item in items: - if item.endswith('.p'): + if item.endswith(".p"): ret.append(item.rstrip(item[-2:])) else: ret.append(item) @@ -170,12 +159,12 @@ def list_(bank, cachedir): def contains(bank, key, cachedir): - ''' + """ Checks if the specified bank contains the specified key. - ''' + """ if key is None: base = os.path.join(cachedir, os.path.normpath(bank)) return os.path.isdir(base) else: - keyfile = os.path.join(cachedir, os.path.normpath(bank), '{0}.p'.format(key)) + keyfile = os.path.join(cachedir, os.path.normpath(bank), "{0}.p".format(key)) return os.path.isfile(keyfile) diff --git a/salt/cache/mysql_cache.py b/salt/cache/mysql_cache.py index 2e4c9fc99b5..803f8efa68a 100644 --- a/salt/cache/mysql_cache.py +++ b/salt/cache/mysql_cache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Minion data cache plugin for MySQL database. .. versionadded:: develop @@ -42,10 +42,13 @@ value to ``mysql``: .. _`MySQL documentation`: https://github.com/coreos/mysql .. _`python-mysql documentation`: http://python-mysql.readthedocs.io/en/latest/ -''' +""" from __future__ import absolute_import, print_function, unicode_literals -from time import sleep + import logging +from time import sleep + +from salt.exceptions import SaltCacheError try: # Trying to import MySQLdb @@ -57,6 +60,7 @@ except ImportError: try: # MySQLdb import failed, try to import PyMySQL import pymysql + pymysql.install_as_MySQLdb() import MySQLdb import MySQLdb.cursors @@ -65,7 +69,6 @@ except ImportError: except ImportError: MySQLdb = None -from salt.exceptions import SaltCacheError _DEFAULT_DATABASE_NAME = "salt_cache" _DEFAULT_CACHE_TABLE_NAME = "cache" @@ -78,24 +81,24 @@ _table_name = None # Module properties -__virtualname__ = 'mysql' -__func_alias__ = {'ls': 'list'} +__virtualname__ = "mysql" +__func_alias__ = {"ls": "list"} def __virtual__(): - ''' + """ Confirm that a python mysql client is installed. - ''' - return bool(MySQLdb), 'No python mysql client installed.' if MySQLdb is None else '' + """ + return bool(MySQLdb), "No python mysql client installed." if MySQLdb is None else "" def run_query(conn, query, retries=3): - ''' + """ Get a cursor and run a query. Reconnect up to `retries` times if needed. Returns: cursor, affected rows counter Raises: SaltCacheError, AttributeError, OperationalError - ''' + """ try: cur = conn.cursor() out = cur.execute(query) @@ -119,16 +122,15 @@ def run_query(conn, query, retries=3): def _create_table(): - ''' + """ Create table if needed - ''' + """ # Explicitely check if the table already exists as the library logs a # warning on CREATE TABLE query = """SELECT COUNT(TABLE_NAME) FROM information_schema.tables WHERE table_schema = '{0}' AND table_name = '{1}'""".format( - _mysql_kwargs['db'], - _table_name, - ) + _mysql_kwargs["db"], _table_name, + ) cur, _ = run_query(client, query) r = cur.fetchone() cur.close() @@ -140,7 +142,9 @@ def _create_table(): etcd_key CHAR(255), data MEDIUMBLOB, PRIMARY KEY(bank, etcd_key) - );""".format(_table_name) + );""".format( + _table_name + ) log.info("mysql_cache: creating table %s", _table_name) cur, _ = run_query(client, query) cur.close() @@ -154,67 +158,67 @@ def _init_client(): global _mysql_kwargs, _table_name _mysql_kwargs = { - 'host': __opts__.get('mysql.host', '127.0.0.1'), - 'user': __opts__.get('mysql.user', None), - 'passwd': __opts__.get('mysql.password', None), - 'db': __opts__.get('mysql.database', _DEFAULT_DATABASE_NAME), - 'port': __opts__.get('mysql.port', 3306), - 'unix_socket': __opts__.get('mysql.unix_socket', None), - 'connect_timeout': __opts__.get('mysql.connect_timeout', None), - 'autocommit': True, + "host": __opts__.get("mysql.host", "127.0.0.1"), + "user": __opts__.get("mysql.user", None), + "passwd": __opts__.get("mysql.password", None), + "db": __opts__.get("mysql.database", _DEFAULT_DATABASE_NAME), + "port": __opts__.get("mysql.port", 3306), + "unix_socket": __opts__.get("mysql.unix_socket", None), + "connect_timeout": __opts__.get("mysql.connect_timeout", None), + "autocommit": True, } - _table_name = __opts__.get('mysql.table_name', _table_name) + _table_name = __opts__.get("mysql.table_name", _table_name) # TODO: handle SSL connection parameters for k, v in _mysql_kwargs.items(): if v is None: _mysql_kwargs.pop(k) kwargs_copy = _mysql_kwargs.copy() - kwargs_copy['passwd'] = "" + kwargs_copy["passwd"] = "" log.info("mysql_cache: Setting up client with params: %r", kwargs_copy) # The MySQL client is created later on by run_query _create_table() def store(bank, key, data): - ''' + """ Store a key value. - ''' + """ _init_client() - data = __context__['serial'].dumps(data) - query = b"REPLACE INTO {0} (bank, etcd_key, data) values('{1}', '{2}', " \ - b"'{3}')".format(_table_name, - bank, - key, - data) + data = __context__["serial"].dumps(data) + query = ( + b"REPLACE INTO {0} (bank, etcd_key, data) values('{1}', '{2}', " + b"'{3}')".format(_table_name, bank, key, data) + ) cur, cnt = run_query(client, query) cur.close() if cnt not in (1, 2): raise SaltCacheError( - 'Error storing {0} {1} returned {2}'.format(bank, key, cnt) + "Error storing {0} {1} returned {2}".format(bank, key, cnt) ) def fetch(bank, key): - ''' + """ Fetch a key value. - ''' + """ _init_client() query = "SELECT data FROM {0} WHERE bank='{1}' AND etcd_key='{2}'".format( - _table_name, bank, key) + _table_name, bank, key + ) cur, _ = run_query(client, query) r = cur.fetchone() cur.close() if r is None: return {} - return __context__['serial'].loads(r[0]) + return __context__["serial"].loads(r[0]) def flush(bank, key=None): - ''' + """ Remove the key from the cache bank with all the key content. - ''' + """ _init_client() query = "DELETE FROM {0} WHERE bank='{1}'".format(_table_name, bank) if key is not None: @@ -225,13 +229,12 @@ def flush(bank, key=None): def ls(bank): - ''' + """ Return an iterable object containing all entries stored in the specified bank. - ''' + """ _init_client() - query = "SELECT etcd_key FROM {0} WHERE bank='{1}'".format( - _table_name, bank) + query = "SELECT etcd_key FROM {0} WHERE bank='{1}'".format(_table_name, bank) cur, _ = run_query(client, query) out = [row[0] for row in cur.fetchall()] cur.close() @@ -239,12 +242,13 @@ def ls(bank): def contains(bank, key): - ''' + """ Checks if the specified bank contains the specified key. - ''' + """ _init_client() - query = "SELECT COUNT(data) FROM {0} WHERE bank='{1}' " \ - "AND etcd_key='{2}'".format(_table_name, bank, key) + query = "SELECT COUNT(data) FROM {0} WHERE bank='{1}' " "AND etcd_key='{2}'".format( + _table_name, bank, key + ) cur, _ = run_query(client, query) r = cur.fetchone() cur.close() diff --git a/salt/cache/redis_cache.py b/salt/cache/redis_cache.py index 569b3bc9403..21bdcae3f79 100644 --- a/salt/cache/redis_cache.py +++ b/salt/cache/redis_cache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Redis ===== @@ -134,45 +134,52 @@ Cluster Configuration Example: cache.redis.bank_keys_prefix: #BANKEYS cache.redis.key_prefix: #KEY cache.redis.separator: '@' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import stdlib import logging +from salt.exceptions import SaltCacheError + +# Import salt +from salt.ext.six.moves import range + # Import third party libs try: import redis from redis.exceptions import ConnectionError as RedisConnectionError from redis.exceptions import ResponseError as RedisResponseError + HAS_REDIS = True except ImportError: HAS_REDIS = False try: + # pylint: disable=no-name-in-module from rediscluster import StrictRedisCluster + + # pylint: enable=no-name-in-module + HAS_REDIS_CLUSTER = True except ImportError: HAS_REDIS_CLUSTER = False -# Import salt -from salt.ext.six.moves import range -from salt.exceptions import SaltCacheError # ----------------------------------------------------------------------------- # module properties # ----------------------------------------------------------------------------- -__virtualname__ = 'redis' -__func_alias__ = {'list_': 'list'} +__virtualname__ = "redis" +__func_alias__ = {"list_": "list"} log = logging.getLogger(__file__) -_BANK_PREFIX = '$BANK' -_KEY_PREFIX = '$KEY' -_BANK_KEYS_PREFIX = '$BANKEYS' -_SEPARATOR = '_' +_BANK_PREFIX = "$BANK" +_KEY_PREFIX = "$KEY" +_BANK_KEYS_PREFIX = "$BANKEYS" +_SEPARATOR = "_" REDIS_SERVER = None @@ -182,14 +189,14 @@ REDIS_SERVER = None def __virtual__(): - ''' + """ The redis library must be installed for this module to work. The redis redis cluster library must be installed if cluster_mode is True - ''' + """ if not HAS_REDIS: return (False, "Please install the python-redis package.") - if not HAS_REDIS_CLUSTER and _get_redis_cache_opts()['cluster_mode']: + if not HAS_REDIS_CLUSTER and _get_redis_cache_opts()["cluster_mode"]: return (False, "Please install the redis-py-cluster package.") return __virtualname__ @@ -198,124 +205,125 @@ def __virtual__(): # helper functions -- will not be exported # ----------------------------------------------------------------------------- + def init_kwargs(kwargs): return {} def _get_redis_cache_opts(): - ''' + """ Return the Redis server connection details from the __opts__. - ''' + """ return { - 'host': __opts__.get('cache.redis.host', 'localhost'), - 'port': __opts__.get('cache.redis.port', 6379), - 'unix_socket_path': __opts__.get('cache.redis.unix_socket_path', None), - 'db': __opts__.get('cache.redis.db', '0'), - 'password': __opts__.get('cache.redis.password', ''), - 'cluster_mode': __opts__.get('cache.redis.cluster_mode', False), - 'startup_nodes': __opts__.get('cache.redis.cluster.startup_nodes', {}), - 'skip_full_coverage_check': __opts__.get('cache.redis.cluster.skip_full_coverage_check', False), + "host": __opts__.get("cache.redis.host", "localhost"), + "port": __opts__.get("cache.redis.port", 6379), + "unix_socket_path": __opts__.get("cache.redis.unix_socket_path", None), + "db": __opts__.get("cache.redis.db", "0"), + "password": __opts__.get("cache.redis.password", ""), + "cluster_mode": __opts__.get("cache.redis.cluster_mode", False), + "startup_nodes": __opts__.get("cache.redis.cluster.startup_nodes", {}), + "skip_full_coverage_check": __opts__.get( + "cache.redis.cluster.skip_full_coverage_check", False + ), } def _get_redis_server(opts=None): - ''' + """ Return the Redis server instance. Caching the object instance. - ''' + """ global REDIS_SERVER if REDIS_SERVER: return REDIS_SERVER if not opts: opts = _get_redis_cache_opts() - if opts['cluster_mode']: - REDIS_SERVER = StrictRedisCluster(startup_nodes=opts['startup_nodes'], - skip_full_coverage_check=opts['skip_full_coverage_check']) + if opts["cluster_mode"]: + REDIS_SERVER = StrictRedisCluster( + startup_nodes=opts["startup_nodes"], + skip_full_coverage_check=opts["skip_full_coverage_check"], + ) else: - REDIS_SERVER = redis.StrictRedis(opts['host'], - opts['port'], - unix_socket_path=opts['unix_socket_path'], - db=opts['db'], - password=opts['password']) + REDIS_SERVER = redis.StrictRedis( + opts["host"], + opts["port"], + unix_socket_path=opts["unix_socket_path"], + db=opts["db"], + password=opts["password"], + ) return REDIS_SERVER def _get_redis_keys_opts(): - ''' + """ Build the key opts based on the user options. - ''' + """ return { - 'bank_prefix': __opts__.get('cache.redis.bank_prefix', _BANK_PREFIX), - 'bank_keys_prefix': __opts__.get('cache.redis.bank_keys_prefix', _BANK_KEYS_PREFIX), - 'key_prefix': __opts__.get('cache.redis.key_prefix', _KEY_PREFIX), - 'separator': __opts__.get('cache.redis.separator', _SEPARATOR) + "bank_prefix": __opts__.get("cache.redis.bank_prefix", _BANK_PREFIX), + "bank_keys_prefix": __opts__.get( + "cache.redis.bank_keys_prefix", _BANK_KEYS_PREFIX + ), + "key_prefix": __opts__.get("cache.redis.key_prefix", _KEY_PREFIX), + "separator": __opts__.get("cache.redis.separator", _SEPARATOR), } def _get_bank_redis_key(bank): - ''' + """ Return the Redis key for the bank given the name. - ''' + """ opts = _get_redis_keys_opts() - return '{prefix}{separator}{bank}'.format( - prefix=opts['bank_prefix'], - separator=opts['separator'], - bank=bank + return "{prefix}{separator}{bank}".format( + prefix=opts["bank_prefix"], separator=opts["separator"], bank=bank ) def _get_key_redis_key(bank, key): - ''' + """ Return the Redis key given the bank name and the key name. - ''' + """ opts = _get_redis_keys_opts() - return '{prefix}{separator}{bank}/{key}'.format( - prefix=opts['key_prefix'], - separator=opts['separator'], - bank=bank, - key=key + return "{prefix}{separator}{bank}/{key}".format( + prefix=opts["key_prefix"], separator=opts["separator"], bank=bank, key=key ) def _get_bank_keys_redis_key(bank): - ''' + """ Return the Redis key for the SET of keys under a certain bank, given the bank name. - ''' + """ opts = _get_redis_keys_opts() - return '{prefix}{separator}{bank}'.format( - prefix=opts['bank_keys_prefix'], - separator=opts['separator'], - bank=bank + return "{prefix}{separator}{bank}".format( + prefix=opts["bank_keys_prefix"], separator=opts["separator"], bank=bank ) def _build_bank_hier(bank, redis_pipe): - ''' + """ Build the bank hierarchy from the root of the tree. If already exists, it won't rewrite. It's using the Redis pipeline, so there will be only one interaction with the remote server. - ''' - bank_list = bank.split('/') + """ + bank_list = bank.split("/") parent_bank_path = bank_list[0] for bank_name in bank_list[1:]: prev_bank_redis_key = _get_bank_redis_key(parent_bank_path) redis_pipe.sadd(prev_bank_redis_key, bank_name) - log.debug('Adding %s to %s', bank_name, prev_bank_redis_key) - parent_bank_path = '{curr_path}/{bank_name}'.format( - curr_path=parent_bank_path, - bank_name=bank_name + log.debug("Adding %s to %s", bank_name, prev_bank_redis_key) + parent_bank_path = "{curr_path}/{bank_name}".format( + curr_path=parent_bank_path, bank_name=bank_name ) # this becomes the parent of the next return True -def _get_banks_to_remove(redis_server, bank, path=''): - ''' +def _get_banks_to_remove(redis_server, bank, path=""): + """ A simple tree tarversal algorithm that builds the list of banks to remove, starting from an arbitrary node in the tree. - ''' - current_path = bank if not path else '{path}/{bank}'.format(path=path, bank=bank) + """ + current_path = bank if not path else "{path}/{bank}".format(path=path, bank=bank) bank_paths_to_remove = [current_path] # as you got here, you'll be removed @@ -324,60 +332,65 @@ def _get_banks_to_remove(redis_server, bank, path=''): if not child_banks: return bank_paths_to_remove # this bank does not have any child banks so we stop here for child_bank in child_banks: - bank_paths_to_remove.extend(_get_banks_to_remove(redis_server, child_bank, path=current_path)) + bank_paths_to_remove.extend( + _get_banks_to_remove(redis_server, child_bank, path=current_path) + ) # go one more level deeper # and also remove the children of this child bank (if any) return bank_paths_to_remove + # ----------------------------------------------------------------------------- # cache subsystem functions # ----------------------------------------------------------------------------- def store(bank, key, data): - ''' + """ Store the data in a Redis key. - ''' + """ redis_server = _get_redis_server() redis_pipe = redis_server.pipeline() redis_key = _get_key_redis_key(bank, key) redis_bank_keys = _get_bank_keys_redis_key(bank) try: _build_bank_hier(bank, redis_pipe) - value = __context__['serial'].dumps(data) + value = __context__["serial"].dumps(data) redis_pipe.set(redis_key, value) - log.debug('Setting the value for %s under %s (%s)', key, bank, redis_key) + log.debug("Setting the value for %s under %s (%s)", key, bank, redis_key) redis_pipe.sadd(redis_bank_keys, key) - log.debug('Adding %s to %s', key, redis_bank_keys) + log.debug("Adding %s to %s", key, redis_bank_keys) redis_pipe.execute() except (RedisConnectionError, RedisResponseError) as rerr: - mesg = 'Cannot set the Redis cache key {rkey}: {rerr}'.format(rkey=redis_key, - rerr=rerr) + mesg = "Cannot set the Redis cache key {rkey}: {rerr}".format( + rkey=redis_key, rerr=rerr + ) log.error(mesg) raise SaltCacheError(mesg) def fetch(bank, key): - ''' + """ Fetch data from the Redis cache. - ''' + """ redis_server = _get_redis_server() redis_key = _get_key_redis_key(bank, key) redis_value = None try: redis_value = redis_server.get(redis_key) except (RedisConnectionError, RedisResponseError) as rerr: - mesg = 'Cannot fetch the Redis cache key {rkey}: {rerr}'.format(rkey=redis_key, - rerr=rerr) + mesg = "Cannot fetch the Redis cache key {rkey}: {rerr}".format( + rkey=redis_key, rerr=rerr + ) log.error(mesg) raise SaltCacheError(mesg) if redis_value is None: return {} - return __context__['serial'].loads(redis_value) + return __context__["serial"].loads(redis_value) def flush(bank, key=None): - ''' + """ Remove the key from the cache bank with all the key content. If no key is specified, remove the entire bank with all keys and sub-banks inside. This function is using the Redis pipelining for best performance. @@ -395,7 +408,7 @@ def flush(bank, key=None): (using the ``register_script`` feature) and call it whenever we flush. This script would only need to build this sub-tree causing problems. It can be added later and the behaviour should not change as the user needs to explicitly allow Salt inject scripts in their Redis instance. - ''' + """ redis_server = _get_redis_server() redis_pipe = redis_server.pipeline() if key is None: @@ -407,19 +420,21 @@ def flush(bank, key=None): # Redis key of the SET that stores the bank keys redis_pipe.smembers(bank_keys_redis_key) # fetch these keys log.debug( - 'Fetching the keys of the %s bank (%s)', - bank_to_remove, bank_keys_redis_key + "Fetching the keys of the %s bank (%s)", + bank_to_remove, + bank_keys_redis_key, ) try: - log.debug('Executing the pipe...') - subtree_keys = redis_pipe.execute() # here are the keys under these banks to be removed + log.debug("Executing the pipe...") + subtree_keys = ( + redis_pipe.execute() + ) # here are the keys under these banks to be removed # this retunrs a list of sets, e.g.: # [set([]), set(['my-key']), set(['my-other-key', 'yet-another-key'])] # one set corresponding to a bank except (RedisConnectionError, RedisResponseError) as rerr: - mesg = 'Cannot retrieve the keys under these cache banks: {rbanks}: {rerr}'.format( - rbanks=', '.join(bank_paths_to_remove), - rerr=rerr + mesg = "Cannot retrieve the keys under these cache banks: {rbanks}: {rerr}".format( + rbanks=", ".join(bank_paths_to_remove), rerr=rerr ) log.error(mesg) raise SaltCacheError(mesg) @@ -432,56 +447,60 @@ def flush(bank, key=None): redis_key = _get_key_redis_key(bank_path, key) redis_pipe.delete(redis_key) # kill 'em all! log.debug( - 'Removing the key %s under the %s bank (%s)', - key, bank_path, redis_key + "Removing the key %s under the %s bank (%s)", + key, + bank_path, + redis_key, ) bank_keys_redis_key = _get_bank_keys_redis_key(bank_path) redis_pipe.delete(bank_keys_redis_key) log.debug( - 'Removing the bank-keys key for the %s bank (%s)', - bank_path, bank_keys_redis_key + "Removing the bank-keys key for the %s bank (%s)", + bank_path, + bank_keys_redis_key, ) # delete the Redis key where are stored # the list of keys under this bank bank_key = _get_bank_redis_key(bank_path) redis_pipe.delete(bank_key) - log.debug('Removing the %s bank (%s)', bank_path, bank_key) + log.debug("Removing the %s bank (%s)", bank_path, bank_key) # delete the bank key itself else: redis_key = _get_key_redis_key(bank, key) redis_pipe.delete(redis_key) # delete the key cached - log.debug( - 'Removing the key %s under the %s bank (%s)', - key, bank, redis_key - ) + log.debug("Removing the key %s under the %s bank (%s)", key, bank, redis_key) bank_keys_redis_key = _get_bank_keys_redis_key(bank) redis_pipe.srem(bank_keys_redis_key, key) log.debug( - 'De-referencing the key %s from the bank-keys of the %s bank (%s)', - key, bank, bank_keys_redis_key + "De-referencing the key %s from the bank-keys of the %s bank (%s)", + key, + bank, + bank_keys_redis_key, ) # but also its reference from $BANKEYS list try: redis_pipe.execute() # Fluuuush except (RedisConnectionError, RedisResponseError) as rerr: - mesg = 'Cannot flush the Redis cache bank {rbank}: {rerr}'.format(rbank=bank, - rerr=rerr) + mesg = "Cannot flush the Redis cache bank {rbank}: {rerr}".format( + rbank=bank, rerr=rerr + ) log.error(mesg) raise SaltCacheError(mesg) return True def list_(bank): - ''' + """ Lists entries stored in the specified bank. - ''' + """ redis_server = _get_redis_server() bank_redis_key = _get_bank_redis_key(bank) try: banks = redis_server.smembers(bank_redis_key) except (RedisConnectionError, RedisResponseError) as rerr: - mesg = 'Cannot list the Redis cache key {rkey}: {rerr}'.format(rkey=bank_redis_key, - rerr=rerr) + mesg = "Cannot list the Redis cache key {rkey}: {rerr}".format( + rkey=bank_redis_key, rerr=rerr + ) log.error(mesg) raise SaltCacheError(mesg) if not banks: @@ -490,15 +509,16 @@ def list_(bank): def contains(bank, key): - ''' + """ Checks if the specified bank contains the specified key. - ''' + """ redis_server = _get_redis_server() bank_redis_key = _get_bank_redis_key(bank) try: return redis_server.sismember(bank_redis_key, key) except (RedisConnectionError, RedisResponseError) as rerr: - mesg = 'Cannot retrieve the Redis cache key {rkey}: {rerr}'.format(rkey=bank_redis_key, - rerr=rerr) + mesg = "Cannot retrieve the Redis cache key {rkey}: {rerr}".format( + rkey=bank_redis_key, rerr=rerr + ) log.error(mesg) raise SaltCacheError(mesg) diff --git a/salt/cli/__init__.py b/salt/cli/__init__.py index 5bfd3835577..bc7e3060fcf 100644 --- a/salt/cli/__init__.py +++ b/salt/cli/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" The management of salt command line utilities are stored in here -''' +""" diff --git a/salt/cli/api.py b/salt/cli/api.py index 8c3da7ef9e8..b44915762f0 100644 --- a/salt/cli/api.py +++ b/salt/cli/api.py @@ -1,60 +1,61 @@ # -*- coding: utf-8 -*- -''' +""" salt.cli.api ~~~~~~~~~~~~~ Salt's api cli parser. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.client.netapi import salt.utils.files import salt.utils.parsers as parsers -from salt.utils.verify import check_user, verify_log_files, verify_log +from salt.utils.verify import check_user, verify_log, verify_log_files log = logging.getLogger(__name__) class SaltAPI(parsers.SaltAPIParser): - ''' + """ The cli parser object used to fire up the salt api system. - ''' + """ def prepare(self): - ''' + """ Run the preparation sequence required to start a salt-api daemon. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).prepare() - ''' + """ super(SaltAPI, self).prepare() try: - if self.config['verify_env']: - logfile = self.config['log_file'] + if self.config["verify_env"]: + logfile = self.config["log_file"] if logfile is not None: # Logfile is not using Syslog, verify with salt.utils.files.set_umask(0o027): - verify_log_files([logfile], self.config['user']) + verify_log_files([logfile], self.config["user"]) except OSError as err: - log.exception('Failed to prepare salt environment') + log.exception("Failed to prepare salt environment") self.shutdown(err.errno) self.setup_logfile_logger() verify_log(self.config) - log.info('Setting up the Salt API') + log.info("Setting up the Salt API") self.api = salt.client.netapi.NetapiClient(self.config) self.daemonize_if_required() self.set_pidfile() def start(self): - ''' + """ Start the actual master. If sub-classed, don't **ever** forget to run: @@ -62,18 +63,18 @@ class SaltAPI(parsers.SaltAPIParser): super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. - ''' + """ super(SaltAPI, self).start() - if check_user(self.config['user']): - log.info('The salt-api is starting up') + if check_user(self.config["user"]): + log.info("The salt-api is starting up") self.api.run() def shutdown(self, exitcode=0, exitmsg=None): - ''' + """ If sub-classed, run any shutdown operations on this method. - ''' - log.info('The salt-api is shutting down..') - msg = 'The salt-api is shutdown. ' + """ + log.info("The salt-api is shutting down..") + msg = "The salt-api is shutdown. " if exitmsg is not None: exitmsg = msg + exitmsg else: diff --git a/salt/cli/batch.py b/salt/cli/batch.py index 723944b60a4..b836a16dd1b 100644 --- a/salt/cli/batch.py +++ b/salt/cli/batch.py @@ -1,71 +1,76 @@ # -*- coding: utf-8 -*- -''' +""" Execute batch runs -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import copy + +# pylint: enable=import-error,no-name-in-module,redefined-builtin +import logging import math import time -import copy from datetime import datetime, timedelta +import salt.client +import salt.exceptions +import salt.output + # Import salt libs import salt.utils.stringutils -import salt.client -import salt.output -import salt.exceptions # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six from salt.ext.six.moves import range -# pylint: enable=import-error,no-name-in-module,redefined-builtin -import logging log = logging.getLogger(__name__) class Batch(object): - ''' + """ Manage the execution of batch runs - ''' + """ + def __init__(self, opts, eauth=None, quiet=False, parser=None): self.opts = opts self.eauth = eauth if eauth else {} self.pub_kwargs = eauth if eauth else {} self.quiet = quiet - self.local = salt.client.get_local_client(opts['conf_file']) + self.local = salt.client.get_local_client(opts["conf_file"]) self.minions, self.ping_gen, self.down_minions = self.__gather_minions() self.options = parser def __gather_minions(self): - ''' + """ Return a list of minions to use for the batch run - ''' - args = [self.opts['tgt'], - 'test.ping', - [], - self.opts['timeout'], - ] + """ + args = [ + self.opts["tgt"], + "test.ping", + [], + self.opts["timeout"], + ] - selected_target_option = self.opts.get('selected_target_option', None) + selected_target_option = self.opts.get("selected_target_option", None) if selected_target_option is not None: args.append(selected_target_option) else: - args.append(self.opts.get('tgt_type', 'glob')) + args.append(self.opts.get("tgt_type", "glob")) - self.pub_kwargs['yield_pub_data'] = True - ping_gen = self.local.cmd_iter(*args, - gather_job_timeout=self.opts['gather_job_timeout'], - **self.pub_kwargs) + self.pub_kwargs["yield_pub_data"] = True + ping_gen = self.local.cmd_iter( + *args, gather_job_timeout=self.opts["gather_job_timeout"], **self.pub_kwargs + ) # Broadcast to targets fret = set() nret = set() for ret in ping_gen: - if ('minions' and 'jid') in ret: - for minion in ret['minions']: + if ("minions" and "jid") in ret: + for minion in ret["minions"]: nret.add(minion) continue else: @@ -73,30 +78,37 @@ class Batch(object): m = next(six.iterkeys(ret)) except StopIteration: if not self.quiet: - salt.utils.stringutils.print_cli('No minions matched the target.') + salt.utils.stringutils.print_cli( + "No minions matched the target." + ) break if m is not None: fret.add(m) return (list(fret), ping_gen, nret.difference(fret)) def get_bnum(self): - ''' + """ Return the active number of minions to maintain - ''' + """ partition = lambda x: float(x) / 100.0 * len(self.minions) try: - if isinstance(self.opts['batch'], six.string_types) and '%' in self.opts['batch']: - res = partition(float(self.opts['batch'].strip('%'))) + if ( + isinstance(self.opts["batch"], six.string_types) + and "%" in self.opts["batch"] + ): + res = partition(float(self.opts["batch"].strip("%"))) if res < 1: return int(math.ceil(res)) else: return int(res) else: - return int(self.opts['batch']) + return int(self.opts["batch"]) except ValueError: if not self.quiet: - salt.utils.stringutils.print_cli('Invalid batch data sent: {0}\nData must be in the ' - 'form of %10, 10% or 3'.format(self.opts['batch'])) + salt.utils.stringutils.print_cli( + "Invalid batch data sent: {0}\nData must be in the " + "form of %10, 10% or 3".format(self.opts["batch"]) + ) def __update_wait(self, wait): now = datetime.now() @@ -107,15 +119,16 @@ class Batch(object): del wait[:i] def run(self): - ''' + """ Execute the batch run - ''' - args = [[], - self.opts['fun'], - self.opts['arg'], - self.opts['timeout'], - 'list', - ] + """ + args = [ + [], + self.opts["fun"], + self.opts["arg"], + self.opts["timeout"], + "list", + ] bnum = self.get_bnum() # No targets to run if not self.minions: @@ -125,7 +138,7 @@ class Batch(object): ret = {} iters = [] # wait the specified time before decide a job is actually done - bwait = self.opts.get('batch_wait', 0) + bwait = self.opts.get("batch_wait", 0) wait = [] if self.options: @@ -147,7 +160,11 @@ class Batch(object): # We already know some minions didn't respond to the ping, so inform # the user we won't be attempting to run a job on them for down_minion in self.down_minions: - salt.utils.stringutils.print_cli('Minion {0} did not respond. No job will be sent.'.format(down_minion)) + salt.utils.stringutils.print_cli( + "Minion {0} did not respond. No job will be sent.".format( + down_minion + ) + ) # Iterate while we still have things to execute while len(ret) < len(self.minions): @@ -172,22 +189,25 @@ class Batch(object): if next_: if not self.quiet: - salt.utils.stringutils.print_cli('\nExecuting run on {0}\n'.format(sorted(next_))) + salt.utils.stringutils.print_cli( + "\nExecuting run on {0}\n".format(sorted(next_)) + ) # create a new iterator for this batch of minions new_iter = self.local.cmd_iter_no_block( - *args, - raw=self.opts.get('raw', False), - ret=self.opts.get('return', ''), - show_jid=show_jid, - verbose=show_verbose, - gather_job_timeout=self.opts['gather_job_timeout'], - **self.eauth) + *args, + raw=self.opts.get("raw", False), + ret=self.opts.get("return", ""), + show_jid=show_jid, + verbose=show_verbose, + gather_job_timeout=self.opts["gather_job_timeout"], + **self.eauth + ) # add it to our iterators and to the minion_tracker iters.append(new_iter) minion_tracker[new_iter] = {} # every iterator added is 'active' and has its set of minions - minion_tracker[new_iter]['minions'] = next_ - minion_tracker[new_iter]['active'] = True + minion_tracker[new_iter]["minions"] = next_ + minion_tracker[new_iter]["active"] = True else: time.sleep(0.02) @@ -214,19 +234,29 @@ class Batch(object): if ncnt > 5: break continue - if self.opts.get('raw'): - parts.update({part['data']['id']: part}) - if part['data']['id'] in minion_tracker[queue]['minions']: - minion_tracker[queue]['minions'].remove(part['data']['id']) + if self.opts.get("raw"): + parts.update({part["data"]["id"]: part}) + if part["data"]["id"] in minion_tracker[queue]["minions"]: + minion_tracker[queue]["minions"].remove( + part["data"]["id"] + ) else: - salt.utils.stringutils.print_cli('minion {0} was already deleted from tracker, probably a duplicate key'.format(part['id'])) + salt.utils.stringutils.print_cli( + "minion {0} was already deleted from tracker, probably a duplicate key".format( + part["id"] + ) + ) else: parts.update(part) for id in part: - if id in minion_tracker[queue]['minions']: - minion_tracker[queue]['minions'].remove(id) + if id in minion_tracker[queue]["minions"]: + minion_tracker[queue]["minions"].remove(id) else: - salt.utils.stringutils.print_cli('minion {0} was already deleted from tracker, probably a duplicate key'.format(id)) + salt.utils.stringutils.print_cli( + "minion {0} was already deleted from tracker, probably a duplicate key".format( + id + ) + ) except StopIteration: # if a iterator is done: # - set it to inactive @@ -234,14 +264,14 @@ class Batch(object): # check if the tracker contains the iterator if queue in minion_tracker: - minion_tracker[queue]['active'] = False + minion_tracker[queue]["active"] = False # add all minions that belong to this iterator and # that have not responded to parts{} with an empty response - for minion in minion_tracker[queue]['minions']: + for minion in minion_tracker[queue]["minions"]: if minion not in parts: parts[minion] = {} - parts[minion]['ret'] = {} + parts[minion]["ret"] = {} for minion, data in six.iteritems(parts): if minion in active: @@ -250,45 +280,47 @@ class Batch(object): wait.append(datetime.now() + timedelta(seconds=bwait)) # Munge retcode into return data failhard = False - if 'retcode' in data and isinstance(data['ret'], dict) and 'retcode' not in data['ret']: - data['ret']['retcode'] = data['retcode'] - if self.opts.get('failhard') and data['ret']['retcode'] > 0: + if ( + "retcode" in data + and isinstance(data["ret"], dict) + and "retcode" not in data["ret"] + ): + data["ret"]["retcode"] = data["retcode"] + if self.opts.get("failhard") and data["ret"]["retcode"] > 0: failhard = True else: - if self.opts.get('failhard') and data['retcode'] > 0: + if self.opts.get("failhard") and data["retcode"] > 0: failhard = True - if self.opts.get('raw'): + if self.opts.get("raw"): ret[minion] = data yield data else: - ret[minion] = data['ret'] - yield {minion: data['ret']} + ret[minion] = data["ret"] + yield {minion: data["ret"]} if not self.quiet: - ret[minion] = data['ret'] - data[minion] = data.pop('ret') - if 'out' in data: - out = data.pop('out') + ret[minion] = data["ret"] + data[minion] = data.pop("ret") + if "out" in data: + out = data.pop("out") else: out = None - salt.output.display_output( - data, - out, - self.opts) + salt.output.display_output(data, out, self.opts) if failhard: log.error( - 'Minion %s returned with non-zero exit code. ' - 'Batch run stopped due to failhard', minion + "Minion %s returned with non-zero exit code. " + "Batch run stopped due to failhard", + minion, ) raise StopIteration # remove inactive iterators from the iters list for queue in minion_tracker: # only remove inactive queues - if not minion_tracker[queue]['active'] and queue in iters: + if not minion_tracker[queue]["active"] and queue in iters: iters.remove(queue) # also remove the iterator's minions from the active list - for minion in minion_tracker[queue]['minions']: + for minion in minion_tracker[queue]["minions"]: if minion in active: active.remove(minion) if bwait: diff --git a/salt/cli/call.py b/salt/cli/call.py index ab1103c51bd..3f7f45fa09a 100644 --- a/salt/cli/call.py +++ b/salt/cli/call.py @@ -1,44 +1,45 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals + import os -import salt.utils.parsers -from salt.utils.verify import verify_log -from salt.config import _expand_glob_path import salt.cli.caller import salt.defaults.exitcodes +import salt.utils.parsers +from salt.config import _expand_glob_path +from salt.utils.verify import verify_log class SaltCall(salt.utils.parsers.SaltCallOptionParser): - ''' + """ Used to locally execute a salt command - ''' + """ def run(self): - ''' + """ Execute the salt call! - ''' + """ self.parse_args() if self.options.file_root: # check if the argument is pointing to a file on disk file_root = os.path.abspath(self.options.file_root) - self.config['file_roots'] = {'base': _expand_glob_path([file_root])} + self.config["file_roots"] = {"base": _expand_glob_path([file_root])} if self.options.pillar_root: # check if the argument is pointing to a file on disk pillar_root = os.path.abspath(self.options.pillar_root) - self.config['pillar_roots'] = {'base': _expand_glob_path([pillar_root])} + self.config["pillar_roots"] = {"base": _expand_glob_path([pillar_root])} if self.options.states_dir: # check if the argument is pointing to a file on disk states_dir = os.path.abspath(self.options.states_dir) - self.config['states_dirs'] = [states_dir] + self.config["states_dirs"] = [states_dir] if self.options.local: - self.config['file_client'] = 'local' + self.config["file_client"] = "local" if self.options.master: - self.config['master'] = self.options.master + self.config["master"] = self.options.master # Setup file logging! self.setup_logfile_logger() diff --git a/salt/cli/caller.py b/salt/cli/caller.py index b755fb48018..37ea9c3b324 100644 --- a/salt/cli/caller.py +++ b/salt/cli/caller.py @@ -1,19 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" The caller module is used as a front-end to manage direct calls to the salt minion modules. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import logging import os import sys -import logging import traceback # Import salt libs import salt +import salt.defaults.exitcodes import salt.loader import salt.minion import salt.output @@ -26,62 +27,63 @@ import salt.utils.jid import salt.utils.minion import salt.utils.profile import salt.utils.stringutils -import salt.defaults.exitcodes -from salt.log import LOG_LEVELS - -# Import 3rd-party libs -from salt.ext import six # Custom exceptions from salt.exceptions import ( - SaltClientError, - CommandNotFoundError, CommandExecutionError, + CommandNotFoundError, + SaltClientError, SaltInvocationError, ) +# Import 3rd-party libs +from salt.ext import six +from salt.log import LOG_LEVELS + log = logging.getLogger(__name__) class Caller(object): - ''' + """ Factory class to create salt-call callers for different transport - ''' + """ + @staticmethod def factory(opts, **kwargs): # Default to ZeroMQ for now - ttype = 'zeromq' + ttype = "zeromq" # determine the ttype - if 'transport' in opts: - ttype = opts['transport'] - elif 'transport' in opts.get('pillar', {}).get('master', {}): - ttype = opts['pillar']['master']['transport'] + if "transport" in opts: + ttype = opts["transport"] + elif "transport" in opts.get("pillar", {}).get("master", {}): + ttype = opts["pillar"]["master"]["transport"] # switch on available ttypes - if ttype in ('zeromq', 'tcp', 'detect'): + if ttype in ("zeromq", "tcp", "detect"): return ZeroMQCaller(opts, **kwargs) else: - raise Exception('Callers are only defined for ZeroMQ and TCP') + raise Exception("Callers are only defined for ZeroMQ and TCP") # return NewKindOfCaller(opts, **kwargs) class BaseCaller(object): - ''' + """ Base class for caller transports - ''' + """ + def __init__(self, opts): - ''' + """ Pass in command line opts - ''' + """ self.opts = opts - self.opts['caller'] = True + self.opts["caller"] = True self.serial = salt.payload.Serial(self.opts) # Handle this here so other deeper code which might # be imported as part of the salt api doesn't do a # nasty sys.exit() and tick off our developer users try: - if self.opts.get('proxyid'): + if self.opts.get("proxyid"): self.minion = salt.minion.SProxyMinion(opts) else: self.minion = salt.minion.SMinion(opts) @@ -89,30 +91,30 @@ class BaseCaller(object): raise SystemExit(six.text_type(exc)) def print_docs(self): - ''' + """ Pick up the documentation for all of the modules and print it out. - ''' + """ docs = {} for name, func in six.iteritems(self.minion.functions): if name not in docs: if func.__doc__: docs[name] = func.__doc__ for name in sorted(docs): - if name.startswith(self.opts.get('fun', '')): - salt.utils.stringutils.print_cli('{0}:\n{1}\n'.format(name, docs[name])) + if name.startswith(self.opts.get("fun", "")): + salt.utils.stringutils.print_cli("{0}:\n{1}\n".format(name, docs[name])) def print_grains(self): - ''' + """ Print out the grains - ''' - grains = self.minion.opts.get('grains') or salt.loader.grains(self.opts) - salt.output.display_output({'local': grains}, 'grains', self.opts) + """ + grains = self.minion.opts.get("grains") or salt.loader.grains(self.opts) + salt.output.display_output({"local": grains}, "grains", self.opts) def run(self): - ''' + """ Execute the salt call logic - ''' - profiling_enabled = self.opts.get('profiling_enabled', False) + """ + profiling_enabled = self.opts.get("profiling_enabled", False) try: pr = salt.utils.profile.activate_profile(profiling_enabled) try: @@ -120,116 +122,128 @@ class BaseCaller(object): finally: salt.utils.profile.output_profile( pr, - stats_path=self.opts.get('profiling_path', '/tmp/stats'), - stop=True) - out = ret.get('out', 'nested') - if self.opts['print_metadata']: + stats_path=self.opts.get("profiling_path", "/tmp/stats"), + stop=True, + ) + out = ret.get("out", "nested") + if self.opts["print_metadata"]: print_ret = ret - out = 'nested' + out = "nested" else: - print_ret = ret.get('return', {}) + print_ret = ret.get("return", {}) salt.output.display_output( - {'local': print_ret}, - out=out, - opts=self.opts, - _retcode=ret.get('retcode', 0)) + {"local": print_ret}, + out=out, + opts=self.opts, + _retcode=ret.get("retcode", 0), + ) # _retcode will be available in the kwargs of the outputter function - if self.opts.get('retcode_passthrough', False): - sys.exit(ret['retcode']) - elif ret['retcode'] != salt.defaults.exitcodes.EX_OK: + if self.opts.get("retcode_passthrough", False): + sys.exit(ret["retcode"]) + elif ret["retcode"] != salt.defaults.exitcodes.EX_OK: sys.exit(salt.defaults.exitcodes.EX_GENERIC) except SaltInvocationError as err: raise SystemExit(err) def call(self): - ''' + """ Call the module - ''' + """ ret = {} - fun = self.opts['fun'] - ret['jid'] = salt.utils.jid.gen_jid(self.opts) + fun = self.opts["fun"] + ret["jid"] = salt.utils.jid.gen_jid(self.opts) proc_fn = os.path.join( - salt.minion.get_proc_dir(self.opts['cachedir']), - ret['jid'] + salt.minion.get_proc_dir(self.opts["cachedir"]), ret["jid"] ) if fun not in self.minion.functions: - docs = self.minion.functions['sys.doc']('{0}*'.format(fun)) + docs = self.minion.functions["sys.doc"]("{0}*".format(fun)) if docs: docs[fun] = self.minion.functions.missing_fun_string(fun) - ret['out'] = 'nested' - ret['return'] = docs + ret["out"] = "nested" + ret["return"] = docs return ret sys.stderr.write(self.minion.functions.missing_fun_string(fun)) - mod_name = fun.split('.')[0] + mod_name = fun.split(".")[0] if mod_name in self.minion.function_errors: - sys.stderr.write(' Possible reasons: {0}\n'.format(self.minion.function_errors[mod_name])) + sys.stderr.write( + " Possible reasons: {0}\n".format( + self.minion.function_errors[mod_name] + ) + ) else: - sys.stderr.write('\n') + sys.stderr.write("\n") sys.exit(-1) - metadata = self.opts.get('metadata') + metadata = self.opts.get("metadata") if metadata is not None: metadata = salt.utils.args.yamlify_arg(metadata) try: sdata = { - 'fun': fun, - 'pid': os.getpid(), - 'jid': ret['jid'], - 'tgt': 'salt-call'} + "fun": fun, + "pid": os.getpid(), + "jid": ret["jid"], + "tgt": "salt-call", + } if metadata is not None: - sdata['metadata'] = metadata + sdata["metadata"] = metadata args, kwargs = salt.minion.load_args_and_kwargs( self.minion.functions[fun], salt.utils.args.parse_input( - self.opts['arg'], - no_parse=self.opts.get('no_parse', [])), - data=sdata) + self.opts["arg"], no_parse=self.opts.get("no_parse", []) + ), + data=sdata, + ) try: - with salt.utils.files.fopen(proc_fn, 'w+b') as fp_: + with salt.utils.files.fopen(proc_fn, "w+b") as fp_: fp_.write(self.serial.dumps(sdata)) except NameError: # Don't require msgpack with local pass except IOError: sys.stderr.write( - 'Cannot write to process directory. ' - 'Do you have permissions to ' - 'write to {0} ?\n'.format(proc_fn)) + "Cannot write to process directory. " + "Do you have permissions to " + "write to {0} ?\n".format(proc_fn) + ) func = self.minion.functions[fun] - data = { - 'arg': args, - 'fun': fun - } + data = {"arg": args, "fun": fun} data.update(kwargs) - executors = getattr(self.minion, 'module_executors', []) or \ - salt.utils.args.yamlify_arg( - self.opts.get('module_executors', '[direct_call]') - ) - if self.opts.get('executor_opts', None): - data['executor_opts'] = salt.utils.args.yamlify_arg( - self.opts['executor_opts'] + executors = getattr( + self.minion, "module_executors", [] + ) or salt.utils.args.yamlify_arg( + self.opts.get("module_executors", "[direct_call]") + ) + if self.opts.get("executor_opts", None): + data["executor_opts"] = salt.utils.args.yamlify_arg( + self.opts["executor_opts"] ) if isinstance(executors, six.string_types): executors = [executors] try: for name in executors: - fname = '{0}.execute'.format(name) + fname = "{0}.execute".format(name) if fname not in self.minion.executors: - raise SaltInvocationError("Executor '{0}' is not available".format(name)) - ret['return'] = self.minion.executors[fname](self.opts, data, func, args, kwargs) - if ret['return'] is not None: + raise SaltInvocationError( + "Executor '{0}' is not available".format(name) + ) + ret["return"] = self.minion.executors[fname]( + self.opts, data, func, args, kwargs + ) + if ret["return"] is not None: break except TypeError as exc: - sys.stderr.write('\nPassed invalid arguments: {0}.\n\nUsage:\n'.format(exc)) + sys.stderr.write( + "\nPassed invalid arguments: {0}.\n\nUsage:\n".format(exc) + ) salt.utils.stringutils.print_cli(func.__doc__) active_level = LOG_LEVELS.get( - self.opts['log_level'].lower(), logging.ERROR) + self.opts["log_level"].lower(), logging.ERROR + ) if active_level <= logging.DEBUG: trace = traceback.format_exc() sys.stderr.write(trace) sys.exit(salt.defaults.exitcodes.EX_GENERIC) try: - retcode = sys.modules[ - func.__module__].__context__.get('retcode', 0) + retcode = sys.modules[func.__module__].__context__.get("retcode", 0) except AttributeError: retcode = salt.defaults.exitcodes.EX_GENERIC @@ -237,52 +251,54 @@ class BaseCaller(object): # No nonzero retcode in __context__ dunder. Check if return # is a dictionary with a "result" or "success" key. try: - func_result = all(ret['return'].get(x, True) - for x in ('result', 'success')) + func_result = all( + ret["return"].get(x, True) for x in ("result", "success") + ) except Exception: # pylint: disable=broad-except # return data is not a dict func_result = True if not func_result: retcode = salt.defaults.exitcodes.EX_GENERIC - ret['retcode'] = retcode + ret["retcode"] = retcode except (CommandExecutionError) as exc: - msg = 'Error running \'{0}\': {1}\n' - active_level = LOG_LEVELS.get( - self.opts['log_level'].lower(), logging.ERROR) + msg = "Error running '{0}': {1}\n" + active_level = LOG_LEVELS.get(self.opts["log_level"].lower(), logging.ERROR) if active_level <= logging.DEBUG: sys.stderr.write(traceback.format_exc()) sys.stderr.write(msg.format(fun, exc)) sys.exit(salt.defaults.exitcodes.EX_GENERIC) except CommandNotFoundError as exc: - msg = 'Command required for \'{0}\' not found: {1}\n' + msg = "Command required for '{0}' not found: {1}\n" sys.stderr.write(msg.format(fun, exc)) sys.exit(salt.defaults.exitcodes.EX_GENERIC) try: os.remove(proc_fn) except (IOError, OSError): pass - if hasattr(self.minion.functions[fun], '__outputter__'): + if hasattr(self.minion.functions[fun], "__outputter__"): oput = self.minion.functions[fun].__outputter__ if isinstance(oput, six.string_types): - ret['out'] = oput - is_local = self.opts['local'] or self.opts.get( - 'file_client', False) == 'local' or self.opts.get( - 'master_type') == 'disable' - returners = self.opts.get('return', '').split(',') + ret["out"] = oput + is_local = ( + self.opts["local"] + or self.opts.get("file_client", False) == "local" + or self.opts.get("master_type") == "disable" + ) + returners = self.opts.get("return", "").split(",") if (not is_local) or returners: - ret['id'] = self.opts['id'] - ret['fun'] = fun - ret['fun_args'] = self.opts['arg'] + ret["id"] = self.opts["id"] + ret["fun"] = fun + ret["fun_args"] = self.opts["arg"] if metadata is not None: - ret['metadata'] = metadata + ret["metadata"] = metadata for returner in returners: if not returner: # if we got an empty returner somehow, skip continue try: - ret['success'] = True - self.minion.returners['{0}.returner'.format(returner)](ret) + ret["success"] = True + self.minion.returners["{0}.returner".format(returner)](ret) except Exception: # pylint: disable=broad-except pass @@ -290,34 +306,36 @@ class BaseCaller(object): if not is_local: try: mret = ret.copy() - mret['jid'] = 'req' + mret["jid"] = "req" self.return_pub(mret) except Exception: # pylint: disable=broad-except pass - elif self.opts['cache_jobs']: + elif self.opts["cache_jobs"]: # Local job cache has been enabled - salt.utils.minion.cache_jobs(self.opts, ret['jid'], ret) + salt.utils.minion.cache_jobs(self.opts, ret["jid"], ret) return ret class ZeroMQCaller(BaseCaller): - ''' + """ Object to wrap the calling of local salt modules for the salt-call command - ''' + """ + def __init__(self, opts): # pylint: disable=useless-super-delegation - ''' + """ Pass in the command line options - ''' + """ super(ZeroMQCaller, self).__init__(opts) def return_pub(self, ret): - ''' + """ Return the data up to the master - ''' - with salt.transport.client.ReqChannel.factory(self.opts, - usage='salt_call') as channel: - load = {'cmd': '_return', 'id': self.opts['id']} + """ + with salt.transport.client.ReqChannel.factory( + self.opts, usage="salt_call" + ) as channel: + load = {"cmd": "_return", "id": self.opts["id"]} for key, value in six.iteritems(ret): load[key] = value channel.send(load) diff --git a/salt/cli/cp.py b/salt/cli/cp.py index a40f825972c..f71f098d200 100644 --- a/salt/cli/cp.py +++ b/salt/cli/cp.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" The cp module is used to execute the logic used by the salt-cp command line application, salt-cp is NOT intended to broadcast large files, it is intended to handle text files. Salt-cp can be used to distribute configuration files -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import errno import logging @@ -34,14 +35,14 @@ log = logging.getLogger(__name__) class SaltCPCli(salt.utils.parsers.SaltCPOptionParser): - ''' + """ Run the salt-cp command line client - ''' + """ def run(self): - ''' + """ Execute salt-cp - ''' + """ self.parse_args() # Setup file logging! @@ -53,9 +54,10 @@ class SaltCPCli(salt.utils.parsers.SaltCPOptionParser): class SaltCP(object): - ''' + """ Create a salt cp object, used to distribute simple files with salt - ''' + """ + def __init__(self, opts): self.opts = opts self.is_windows = salt.utils.platform.is_windows() @@ -69,9 +71,9 @@ class SaltCP(object): return None def _recurse(self, path): - ''' + """ Get a list of all specified files - ''' + """ files = {} empty_dirs = [] try: @@ -79,7 +81,7 @@ class SaltCP(object): except OSError as exc: if exc.errno == errno.ENOENT: # Path does not exist - sys.stderr.write('{0} does not exist\n'.format(path)) + sys.stderr.write("{0} does not exist\n".format(path)) sys.exit(42) elif exc.errno in (errno.EINVAL, errno.ENOTDIR): # Path is a file (EINVAL on Windows, ENOTDIR otherwise) @@ -97,107 +99,108 @@ class SaltCP(object): def _list_files(self): files = {} empty_dirs = set() - for fn_ in self.opts['src']: + for fn_ in self.opts["src"]: files_, empty_dirs_ = self._recurse(fn_) files.update(files_) empty_dirs.update(empty_dirs_) return files, sorted(empty_dirs) def _file_dict(self, fn_): - ''' + """ Take a path and return the contents of the file as a string - ''' + """ if not os.path.isfile(fn_): - err = 'The referenced file, {0} is not available.'.format(fn_) - sys.stderr.write(err + '\n') + err = "The referenced file, {0} is not available.".format(fn_) + sys.stderr.write(err + "\n") sys.exit(42) - with salt.utils.files.fopen(fn_, 'r') as fp_: + with salt.utils.files.fopen(fn_, "r") as fp_: data = fp_.read() return {fn_: data} def _load_files(self): - ''' + """ Parse the files indicated in opts['src'] and load them into a python object for transport - ''' + """ files = {} - for fn_ in self.opts['src']: + for fn_ in self.opts["src"]: if os.path.isfile(fn_): files.update(self._file_dict(fn_)) elif os.path.isdir(fn_): salt.utils.stringutils.print_cli( - fn_ + ' is a directory, only files are supported ' + fn_ + " is a directory, only files are supported " 'in non-chunked mode. Use "--chunked" command ' - 'line argument.') + "line argument." + ) sys.exit(1) return files def run(self): - ''' + """ Make the salt client call - ''' - if self.opts['chunked']: + """ + if self.opts["chunked"]: ret = self.run_chunked() else: ret = self.run_oldstyle() - salt.output.display_output( - ret, - self.opts.get('output', 'nested'), - self.opts) + salt.output.display_output(ret, self.opts.get("output", "nested"), self.opts) def run_oldstyle(self): - ''' + """ Make the salt client call in old-style all-in-one call method - ''' - arg = [self._load_files(), self.opts['dest']] - local = salt.client.get_local_client(self.opts['conf_file']) - args = [self.opts['tgt'], - 'cp.recv', - arg, - self.opts['timeout'], - ] + """ + arg = [self._load_files(), self.opts["dest"]] + local = salt.client.get_local_client(self.opts["conf_file"]) + args = [ + self.opts["tgt"], + "cp.recv", + arg, + self.opts["timeout"], + ] - selected_target_option = self.opts.get('selected_target_option', None) + selected_target_option = self.opts.get("selected_target_option", None) if selected_target_option is not None: args.append(selected_target_option) return local.cmd(*args) def run_chunked(self): - ''' + """ Make the salt client call in the new fasion chunked multi-call way - ''' + """ files, empty_dirs = self._list_files() - dest = self.opts['dest'] - gzip = self.opts['gzip'] - tgt = self.opts['tgt'] - timeout = self.opts['timeout'] - selected_target_option = self.opts.get('selected_target_option') + dest = self.opts["dest"] + gzip = self.opts["gzip"] + tgt = self.opts["tgt"] + timeout = self.opts["timeout"] + selected_target_option = self.opts.get("selected_target_option") - dest_is_dir = bool(empty_dirs) \ - or len(files) > 1 \ - or bool(re.search(r'[\\/]$', dest)) + dest_is_dir = ( + bool(empty_dirs) or len(files) > 1 or bool(re.search(r"[\\/]$", dest)) + ) - reader = salt.utils.gzip_util.compress_file \ - if gzip \ + reader = ( + salt.utils.gzip_util.compress_file + if gzip else salt.utils.itertools.read_file + ) _res = salt.utils.minions.CkMinions(self.opts).check_minions( - tgt, - tgt_type=selected_target_option or 'glob') - minions = _res['minions'] + tgt, tgt_type=selected_target_option or "glob" + ) + minions = _res["minions"] - local = salt.client.get_local_client(self.opts['conf_file']) + local = salt.client.get_local_client(self.opts["conf_file"]) def _get_remote_path(fn_): - if fn_ in self.opts['src']: + if fn_ in self.opts["src"]: # This was a filename explicitly passed on the CLI - return os.path.join(dest, os.path.basename(fn_)) \ - if dest_is_dir \ - else dest + return ( + os.path.join(dest, os.path.basename(fn_)) if dest_is_dir else dest + ) else: - for path in self.opts['src']: + for path in self.opts["src"]: relpath = os.path.relpath(fn_, path + os.sep) if relpath.startswith(parent): # File is not within this dir @@ -205,32 +208,32 @@ class SaltCP(object): return os.path.join(dest, os.path.basename(path), relpath) else: # pylint: disable=useless-else-on-loop # Should not happen - log.error('Failed to find remote path for %s', fn_) + log.error("Failed to find remote path for %s", fn_) return None ret = {} - parent = '..' + os.sep + parent = ".." + os.sep for fn_, mode in six.iteritems(files): remote_path = _get_remote_path(fn_) index = 1 failed = {} - for chunk in reader(fn_, chunk_size=self.opts['salt_cp_chunk_size']): + for chunk in reader(fn_, chunk_size=self.opts["salt_cp_chunk_size"]): chunk = base64.b64encode(salt.utils.stringutils.to_bytes(chunk)) append = index > 1 log.debug( - 'Copying %s to %starget \'%s\' as %s%s', + "Copying %s to %starget '%s' as %s%s", fn_, - '{0} '.format(selected_target_option) - if selected_target_option - else '', + "{0} ".format(selected_target_option) + if selected_target_option + else "", tgt, remote_path, - ' (chunk #{0})'.format(index) if append else '' + " (chunk #{0})".format(index) if append else "", ) args = [ tgt, - 'cp.recv_chunked', + "cp.recv_chunked", [remote_path, chunk, append, gzip, mode], timeout, ] @@ -242,11 +245,11 @@ class SaltCP(object): if not result: # Publish failed msg = ( - 'Publish failed.{0} It may be necessary to ' - 'decrease salt_cp_chunk_size (current value: ' - '{1})'.format( - ' File partially transferred.' if index > 1 else '', - self.opts['salt_cp_chunk_size'], + "Publish failed.{0} It may be necessary to " + "decrease salt_cp_chunk_size (current value: " + "{1})".format( + " File partially transferred." if index > 1 else "", + self.opts["salt_cp_chunk_size"], ) ) for minion in minions: @@ -269,14 +272,16 @@ class SaltCP(object): for dirname in empty_dirs: remote_path = _get_remote_path(dirname) log.debug( - 'Creating empty dir %s on %starget \'%s\'', + "Creating empty dir %s on %starget '%s'", dirname, - '{0} '.format(selected_target_option) # pylint: disable=str-format-in-logging - if selected_target_option - else '', + "{0} ".format( + selected_target_option + ) # pylint: disable=str-format-in-logging + if selected_target_option + else "", tgt, ) - args = [tgt, 'cp.recv_chunked', [remote_path, None], timeout] + args = [tgt, "cp.recv_chunked", [remote_path, None], timeout] if selected_target_option is not None: args.append(selected_target_option) diff --git a/salt/cli/daemons.py b/salt/cli/daemons.py index 9d421016f83..21237e1d3ab 100644 --- a/salt/cli/daemons.py +++ b/salt/cli/daemons.py @@ -1,56 +1,56 @@ # coding: utf-8 -*- -''' +""" Make me some salt! -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os import warnings -from salt.utils.verify import verify_log - - -# All salt related deprecation warnings should be shown once each! -warnings.filterwarnings( - 'once', # Show once - '', # No deprecation message match - DeprecationWarning, # This filter is for DeprecationWarnings - r'^(salt|salt\.(.*))$' # Match module(s) 'salt' and 'salt.' -) - -# While we are supporting Python2.6, hide nested with-statements warnings -warnings.filterwarnings( - 'ignore', - 'With-statements now directly support multiple context managers', - DeprecationWarning -) - -# Filter the backports package UserWarning about being re-imported -warnings.filterwarnings( - 'ignore', - '^Module backports was already imported from (.*), but (.*) is being added to sys.path$', - UserWarning -) # Import salt libs # We import log ASAP because we NEED to make sure that any logger instance salt # instantiates is using salt.log.setup.SaltLoggingClass import salt.log.setup - +import salt.utils.kinds as kinds +from salt.exceptions import SaltClientError, SaltSystemExit, get_error_message # the try block below bypasses an issue at build time so that modules don't # cause the build to fail from salt.utils import migrations -import salt.utils.kinds as kinds +from salt.utils.verify import verify_log + +# All salt related deprecation warnings should be shown once each! +warnings.filterwarnings( + "once", # Show once + "", # No deprecation message match + DeprecationWarning, # This filter is for DeprecationWarnings + r"^(salt|salt\.(.*))$", # Match module(s) 'salt' and 'salt.' +) + +# While we are supporting Python2.6, hide nested with-statements warnings +warnings.filterwarnings( + "ignore", + "With-statements now directly support multiple context managers", + DeprecationWarning, +) + +# Filter the backports package UserWarning about being re-imported +warnings.filterwarnings( + "ignore", + "^Module backports was already imported from (.*), but (.*) is being added to sys.path$", + UserWarning, +) + try: from salt.utils.zeromq import ip_bracket import salt.utils.parsers from salt.utils.verify import check_user, verify_env, verify_socket except ImportError as exc: - if exc.args[0] != 'No module named _msgpack': + if exc.args[0] != "No module named _msgpack": raise -from salt.exceptions import SaltSystemExit, SaltClientError, get_error_message # Let's instantiate log using salt.log.setup.logging.getLogger() so pylint @@ -59,67 +59,73 @@ log = salt.log.setup.logging.getLogger(__name__) class DaemonsMixin(object): # pylint: disable=no-init - ''' + """ Uses the same functions for all daemons - ''' + """ + def verify_hash_type(self): - ''' + """ Verify and display a nag-messsage to the log if vulnerable hash-type is used. :return: - ''' - if self.config['hash_type'].lower() in ['md5', 'sha1']: + """ + if self.config["hash_type"].lower() in ["md5", "sha1"]: log.warning( - 'IMPORTANT: Do not use %s hashing algorithm! Please set ' + "IMPORTANT: Do not use %s hashing algorithm! Please set " '"hash_type" to sha256 in Salt %s config!', - self.config['hash_type'], self.__class__.__name__ + self.config["hash_type"], + self.__class__.__name__, ) def action_log_info(self, action): - ''' + """ Say daemon starting. :param action :return: - ''' - log.info('%s the Salt %s', action, self.__class__.__name__) + """ + log.info("%s the Salt %s", action, self.__class__.__name__) def start_log_info(self): - ''' + """ Say daemon starting. :return: - ''' - log.info('The Salt %s is starting up', self.__class__.__name__) + """ + log.info("The Salt %s is starting up", self.__class__.__name__) def shutdown_log_info(self): - ''' + """ Say daemon shutting down. :return: - ''' - log.info('The Salt %s is shut down', self.__class__.__name__) + """ + log.info("The Salt %s is shut down", self.__class__.__name__) def environment_failure(self, error): - ''' + """ Log environment failure for the daemon and exit with the error code. :param error: :return: - ''' + """ log.exception( - 'Failed to create environment for %s: %s', - self.__class__.__name__, get_error_message(error) + "Failed to create environment for %s: %s", + self.__class__.__name__, + get_error_message(error), ) self.shutdown(error) -class Master(salt.utils.parsers.MasterOptionParser, DaemonsMixin): # pylint: disable=no-init - ''' +class Master( + salt.utils.parsers.MasterOptionParser, DaemonsMixin +): # pylint: disable=no-init + """ Creates a master server - ''' + """ + def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument - if hasattr(self.master, 'process_manager'): + if hasattr(self.master, "process_manager"): # escalate signal to the process manager processes self.master.process_manager.stop_restarting() self.master.process_manager.send_signal_to_processes(signum) @@ -128,61 +134,62 @@ class Master(salt.utils.parsers.MasterOptionParser, DaemonsMixin): # pylint: di super(Master, self)._handle_signals(signum, sigframe) def prepare(self): - ''' + """ Run the preparation sequence required to start a salt master server. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).prepare() - ''' + """ super(Master, self).prepare() try: - if self.config['verify_env']: + if self.config["verify_env"]: v_dirs = [ - self.config['pki_dir'], - os.path.join(self.config['pki_dir'], 'minions'), - os.path.join(self.config['pki_dir'], 'minions_pre'), - os.path.join(self.config['pki_dir'], 'minions_denied'), - os.path.join(self.config['pki_dir'], - 'minions_autosign'), - os.path.join(self.config['pki_dir'], - 'minions_rejected'), - self.config['cachedir'], - os.path.join(self.config['cachedir'], 'jobs'), - os.path.join(self.config['cachedir'], 'proc'), - self.config['sock_dir'], - self.config['token_dir'], - self.config['syndic_dir'], - self.config['sqlite_queue_dir'], - ] + self.config["pki_dir"], + os.path.join(self.config["pki_dir"], "minions"), + os.path.join(self.config["pki_dir"], "minions_pre"), + os.path.join(self.config["pki_dir"], "minions_denied"), + os.path.join(self.config["pki_dir"], "minions_autosign"), + os.path.join(self.config["pki_dir"], "minions_rejected"), + self.config["cachedir"], + os.path.join(self.config["cachedir"], "jobs"), + os.path.join(self.config["cachedir"], "proc"), + self.config["sock_dir"], + self.config["token_dir"], + self.config["syndic_dir"], + self.config["sqlite_queue_dir"], + ] verify_env( v_dirs, - self.config['user'], - permissive=self.config['permissive_pki_access'], - root_dir=self.config['root_dir'], - pki_dir=self.config['pki_dir'], + self.config["user"], + permissive=self.config["permissive_pki_access"], + root_dir=self.config["root_dir"], + pki_dir=self.config["pki_dir"], ) # Clear out syndics from cachedir - for syndic_file in os.listdir(self.config['syndic_dir']): - os.remove(os.path.join(self.config['syndic_dir'], syndic_file)) + for syndic_file in os.listdir(self.config["syndic_dir"]): + os.remove(os.path.join(self.config["syndic_dir"], syndic_file)) except OSError as error: self.environment_failure(error) self.setup_logfile_logger() verify_log(self.config) - self.action_log_info('Setting up') + self.action_log_info("Setting up") # TODO: AIO core is separate from transport - if not verify_socket(self.config['interface'], - self.config['publish_port'], - self.config['ret_port']): - self.shutdown(4, 'The ports are not available to bind') - self.config['interface'] = ip_bracket(self.config['interface']) + if not verify_socket( + self.config["interface"], + self.config["publish_port"], + self.config["ret_port"], + ): + self.shutdown(4, "The ports are not available to bind") + self.config["interface"] = ip_bracket(self.config["interface"]) migrations.migrate_paths(self.config) # Late import so logging works correctly import salt.master + self.master = salt.master.Master(self.config) self.daemonize_if_required() @@ -190,7 +197,7 @@ class Master(salt.utils.parsers.MasterOptionParser, DaemonsMixin): # pylint: di salt.utils.process.notify_systemd() def start(self): - ''' + """ Start the actual master. If sub-classed, don't **ever** forget to run: @@ -198,19 +205,19 @@ class Master(salt.utils.parsers.MasterOptionParser, DaemonsMixin): # pylint: di super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. - ''' + """ super(Master, self).start() - if check_user(self.config['user']): - self.action_log_info('Starting up') + if check_user(self.config["user"]): + self.action_log_info("Starting up") self.verify_hash_type() self.master.start() def shutdown(self, exitcode=0, exitmsg=None): - ''' + """ If sub-classed, run any shutdown operations on this method. - ''' + """ self.shutdown_log_info() - msg = 'The salt master is shutdown. ' + msg = "The salt master is shutdown. " if exitmsg is not None: exitmsg = msg + exitmsg else: @@ -218,34 +225,36 @@ class Master(salt.utils.parsers.MasterOptionParser, DaemonsMixin): # pylint: di super(Master, self).shutdown(exitcode, exitmsg) -class Minion(salt.utils.parsers.MinionOptionParser, DaemonsMixin): # pylint: disable=no-init - ''' +class Minion( + salt.utils.parsers.MinionOptionParser, DaemonsMixin +): # pylint: disable=no-init + """ Create a minion server - ''' + """ def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument # escalate signal to the process manager processes - if hasattr(self.minion, 'stop'): + if hasattr(self.minion, "stop"): self.minion.stop(signum) super(Minion, self)._handle_signals(signum, sigframe) # pylint: disable=no-member def prepare(self): - ''' + """ Run the preparation sequence required to start a salt minion. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).prepare() - ''' + """ super(Minion, self).prepare() try: - if self.config['verify_env']: - confd = self.config.get('default_include') + if self.config["verify_env"]: + confd = self.config.get("default_include") if confd: # If 'default_include' is specified in config, then use it - if '*' in confd: + if "*" in confd: # Value is of the form "minion.d/*.conf" confd = os.path.dirname(confd) if not os.path.isabs(confd): @@ -253,65 +262,67 @@ class Minion(salt.utils.parsers.MinionOptionParser, DaemonsMixin): # pylint: di # path, consider it relative to folder of 'conf_file' # (/etc/salt by default) confd = os.path.join( - os.path.dirname(self.config['conf_file']), confd + os.path.dirname(self.config["conf_file"]), confd ) else: confd = os.path.join( - os.path.dirname(self.config['conf_file']), 'minion.d' + os.path.dirname(self.config["conf_file"]), "minion.d" ) v_dirs = [ - self.config['pki_dir'], - self.config['cachedir'], - self.config['sock_dir'], - self.config['extension_modules'], - confd, - ] + self.config["pki_dir"], + self.config["cachedir"], + self.config["sock_dir"], + self.config["extension_modules"], + confd, + ] verify_env( v_dirs, - self.config['user'], - permissive=self.config['permissive_pki_access'], - root_dir=self.config['root_dir'], - pki_dir=self.config['pki_dir'], + self.config["user"], + permissive=self.config["permissive_pki_access"], + root_dir=self.config["root_dir"], + pki_dir=self.config["pki_dir"], ) except OSError as error: self.environment_failure(error) self.setup_logfile_logger() verify_log(self.config) - log.info('Setting up the Salt Minion "%s"', self.config['id']) + log.info('Setting up the Salt Minion "%s"', self.config["id"]) migrations.migrate_paths(self.config) # Bail out if we find a process running and it matches out pidfile if self.check_running(): - self.action_log_info('An instance is already running. Exiting') + self.action_log_info("An instance is already running. Exiting") self.shutdown(1) - transport = self.config.get('transport').lower() + transport = self.config.get("transport").lower() # TODO: AIO core is separate from transport - if transport in ('zeromq', 'tcp', 'detect'): + if transport in ("zeromq", "tcp", "detect"): # Late import so logging works correctly import salt.minion + # If the minion key has not been accepted, then Salt enters a loop # waiting for it, if we daemonize later then the minion could halt # the boot process waiting for a key to be accepted on the master. # This is the latest safe place to daemonize self.daemonize_if_required() self.set_pidfile() - if self.config.get('master_type') == 'func': + if self.config.get("master_type") == "func": salt.minion.eval_master_func(self.config) self.minion = salt.minion.MinionManager(self.config) else: log.error( - 'The transport \'%s\' is not supported. Please use one of ' - 'the following: tcp, zeromq, or detect.', transport + "The transport '%s' is not supported. Please use one of " + "the following: tcp, zeromq, or detect.", + transport, ) self.shutdown(1) def start(self): - ''' + """ Start the actual minion. If sub-classed, don't **ever** forget to run: @@ -319,7 +330,7 @@ class Minion(salt.utils.parsers.MinionOptionParser, DaemonsMixin): # pylint: di super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. - ''' + """ super(Minion, self).start() while True: try: @@ -332,23 +343,23 @@ class Minion(salt.utils.parsers.MinionOptionParser, DaemonsMixin): # pylint: di def _real_start(self): try: - if check_user(self.config['user']): - self.action_log_info('Starting up') + if check_user(self.config["user"]): + self.action_log_info("Starting up") self.verify_hash_type() self.minion.tune_in() if self.minion.restart: - raise SaltClientError('Minion could not connect to Master') + raise SaltClientError("Minion could not connect to Master") except (KeyboardInterrupt, SaltSystemExit) as error: - self.action_log_info('Stopping') + self.action_log_info("Stopping") if isinstance(error, KeyboardInterrupt): - log.warning('Exiting on Ctrl-c') + log.warning("Exiting on Ctrl-c") self.shutdown() else: log.error(error) self.shutdown(error.code) def call(self, cleanup_protecteds): - ''' + """ Start the actual minion as a caller minion. cleanup_protecteds is list of yard host addresses that should not be @@ -359,41 +370,51 @@ class Minion(salt.utils.parsers.MinionOptionParser, DaemonsMixin): # pylint: di super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. - ''' + """ try: self.prepare() - if check_user(self.config['user']): - self.minion.opts['__role'] = kinds.APPL_KIND_NAMES[kinds.applKinds.caller] + if check_user(self.config["user"]): + self.minion.opts["__role"] = kinds.APPL_KIND_NAMES[ + kinds.applKinds.caller + ] self.minion.call_in() except (KeyboardInterrupt, SaltSystemExit) as exc: - self.action_log_info('Stopping') + self.action_log_info("Stopping") if isinstance(exc, KeyboardInterrupt): - log.warning('Exiting on Ctrl-c') + log.warning("Exiting on Ctrl-c") self.shutdown() else: log.error(exc) self.shutdown(exc.code) def shutdown(self, exitcode=0, exitmsg=None): - ''' + """ If sub-classed, run any shutdown operations on this method. :param exitcode :param exitmsg - ''' - self.action_log_info('Shutting down') - if hasattr(self, 'minion') and hasattr(self.minion, 'destroy'): + """ + self.action_log_info("Shutting down") + if hasattr(self, "minion") and hasattr(self.minion, "destroy"): self.minion.destroy() super(Minion, self).shutdown( - exitcode, ('The Salt {0} is shutdown. {1}'.format( - self.__class__.__name__, (exitmsg or '')).strip())) + exitcode, + ( + "The Salt {0} is shutdown. {1}".format( + self.__class__.__name__, (exitmsg or "") + ).strip() + ), + ) + # pylint: enable=no-member -class ProxyMinion(salt.utils.parsers.ProxyMinionOptionParser, DaemonsMixin): # pylint: disable=no-init - ''' +class ProxyMinion( + salt.utils.parsers.ProxyMinionOptionParser, DaemonsMixin +): # pylint: disable=no-init + """ Create a proxy minion server - ''' + """ def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument # escalate signal to the process manager processes @@ -402,17 +423,17 @@ class ProxyMinion(salt.utils.parsers.ProxyMinionOptionParser, DaemonsMixin): # # pylint: disable=no-member def prepare(self): - ''' + """ Run the preparation sequence required to start a salt proxy minion. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).prepare() - ''' + """ super(ProxyMinion, self).prepare() if not self.values.proxyid: - self.error('salt-proxy requires --proxyid') + self.error("salt-proxy requires --proxyid") # Proxies get their ID from the command line. This may need to change in # the future. @@ -421,11 +442,11 @@ class ProxyMinion(salt.utils.parsers.ProxyMinionOptionParser, DaemonsMixin): # # self.config['id'] = self.values.proxyid try: - if self.config['verify_env']: - confd = self.config.get('default_include') + if self.config["verify_env"]: + confd = self.config.get("default_include") if confd: # If 'default_include' is specified in config, then use it - if '*' in confd: + if "*" in confd: # Value is of the form "minion.d/*.conf" confd = os.path.dirname(confd) if not os.path.isabs(confd): @@ -433,57 +454,58 @@ class ProxyMinion(salt.utils.parsers.ProxyMinionOptionParser, DaemonsMixin): # # path, consider it relative to folder of 'conf_file' # (/etc/salt by default) confd = os.path.join( - os.path.dirname(self.config['conf_file']), confd + os.path.dirname(self.config["conf_file"]), confd ) else: confd = os.path.join( - os.path.dirname(self.config['conf_file']), 'proxy.d' + os.path.dirname(self.config["conf_file"]), "proxy.d" ) v_dirs = [ - self.config['pki_dir'], - self.config['cachedir'], - self.config['sock_dir'], - self.config['extension_modules'], + self.config["pki_dir"], + self.config["cachedir"], + self.config["sock_dir"], + self.config["extension_modules"], confd, ] verify_env( v_dirs, - self.config['user'], - permissive=self.config['permissive_pki_access'], - root_dir=self.config['root_dir'], - pki_dir=self.config['pki_dir'], + self.config["user"], + permissive=self.config["permissive_pki_access"], + root_dir=self.config["root_dir"], + pki_dir=self.config["pki_dir"], ) except OSError as error: self.environment_failure(error) self.setup_logfile_logger() verify_log(self.config) - self.action_log_info('Setting up "{0}"'.format(self.config['id'])) + self.action_log_info('Setting up "{0}"'.format(self.config["id"])) migrations.migrate_paths(self.config) # Bail out if we find a process running and it matches out pidfile if self.check_running(): - self.action_log_info('An instance is already running. Exiting') + self.action_log_info("An instance is already running. Exiting") self.shutdown(1) # TODO: AIO core is separate from transport # Late import so logging works correctly import salt.minion + # If the minion key has not been accepted, then Salt enters a loop # waiting for it, if we daemonize later then the minion could halt # the boot process waiting for a key to be accepted on the master. # This is the latest safe place to daemonize self.daemonize_if_required() self.set_pidfile() - if self.config.get('master_type') == 'func': + if self.config.get("master_type") == "func": salt.minion.eval_master_func(self.config) self.minion = salt.minion.ProxyMinionManager(self.config) def start(self): - ''' + """ Start the actual proxy minion. If sub-classed, don't **ever** forget to run: @@ -491,84 +513,93 @@ class ProxyMinion(salt.utils.parsers.ProxyMinionOptionParser, DaemonsMixin): # super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. - ''' + """ super(ProxyMinion, self).start() try: - if check_user(self.config['user']): - self.action_log_info('The Proxy Minion is starting up') + if check_user(self.config["user"]): + self.action_log_info("The Proxy Minion is starting up") self.verify_hash_type() self.minion.tune_in() if self.minion.restart: - raise SaltClientError('Proxy Minion could not connect to Master') + raise SaltClientError("Proxy Minion could not connect to Master") except (KeyboardInterrupt, SaltSystemExit) as exc: - self.action_log_info('Proxy Minion Stopping') + self.action_log_info("Proxy Minion Stopping") if isinstance(exc, KeyboardInterrupt): - log.warning('Exiting on Ctrl-c') + log.warning("Exiting on Ctrl-c") self.shutdown() else: log.error(exc) self.shutdown(exc.code) def shutdown(self, exitcode=0, exitmsg=None): - ''' + """ If sub-classed, run any shutdown operations on this method. :param exitcode :param exitmsg - ''' - if hasattr(self, 'minion') and 'proxymodule' in self.minion.opts: - proxy_fn = self.minion.opts['proxymodule'].loaded_base_name + '.shutdown' - self.minion.opts['proxymodule'][proxy_fn](self.minion.opts) - self.action_log_info('Shutting down') + """ + if hasattr(self, "minion") and "proxymodule" in self.minion.opts: + proxy_fn = self.minion.opts["proxymodule"].loaded_base_name + ".shutdown" + self.minion.opts["proxymodule"][proxy_fn](self.minion.opts) + self.action_log_info("Shutting down") super(ProxyMinion, self).shutdown( - exitcode, ('The Salt {0} is shutdown. {1}'.format( - self.__class__.__name__, (exitmsg or '')).strip())) + exitcode, + ( + "The Salt {0} is shutdown. {1}".format( + self.__class__.__name__, (exitmsg or "") + ).strip() + ), + ) + # pylint: enable=no-member -class Syndic(salt.utils.parsers.SyndicOptionParser, DaemonsMixin): # pylint: disable=no-init - ''' +class Syndic( + salt.utils.parsers.SyndicOptionParser, DaemonsMixin +): # pylint: disable=no-init + """ Create a syndic server - ''' + """ def prepare(self): - ''' + """ Run the preparation sequence required to start a salt syndic minion. If sub-classed, don't **ever** forget to run: super(YourSubClass, self).prepare() - ''' + """ super(Syndic, self).prepare() try: - if self.config['verify_env']: + if self.config["verify_env"]: verify_env( [ - self.config['pki_dir'], - self.config['cachedir'], - self.config['sock_dir'], - self.config['extension_modules'], + self.config["pki_dir"], + self.config["cachedir"], + self.config["sock_dir"], + self.config["extension_modules"], ], - self.config['user'], - permissive=self.config['permissive_pki_access'], - root_dir=self.config['root_dir'], - pki_dir=self.config['pki_dir'], + self.config["user"], + permissive=self.config["permissive_pki_access"], + root_dir=self.config["root_dir"], + pki_dir=self.config["pki_dir"], ) except OSError as error: self.environment_failure(error) self.setup_logfile_logger() verify_log(self.config) - self.action_log_info('Setting up "{0}"'.format(self.config['id'])) + self.action_log_info('Setting up "{0}"'.format(self.config["id"])) # Late import so logging works correctly import salt.minion + self.daemonize_if_required() self.syndic = salt.minion.SyndicManager(self.config) self.set_pidfile() def start(self): - ''' + """ Start the actual syndic. If sub-classed, don't **ever** forget to run: @@ -576,25 +607,30 @@ class Syndic(salt.utils.parsers.SyndicOptionParser, DaemonsMixin): # pylint: di super(YourSubClass, self).start() NOTE: Run any required code before calling `super()`. - ''' + """ super(Syndic, self).start() - if check_user(self.config['user']): - self.action_log_info('Starting up') + if check_user(self.config["user"]): + self.action_log_info("Starting up") self.verify_hash_type() try: self.syndic.tune_in() except KeyboardInterrupt: - self.action_log_info('Stopping') + self.action_log_info("Stopping") self.shutdown() def shutdown(self, exitcode=0, exitmsg=None): - ''' + """ If sub-classed, run any shutdown operations on this method. :param exitcode :param exitmsg - ''' - self.action_log_info('Shutting down') + """ + self.action_log_info("Shutting down") super(Syndic, self).shutdown( - exitcode, ('The Salt {0} is shutdown. {1}'.format( - self.__class__.__name__, (exitmsg or '')).strip())) + exitcode, + ( + "The Salt {0} is shutdown. {1}".format( + self.__class__.__name__, (exitmsg or "") + ).strip() + ), + ) diff --git a/salt/cli/key.py b/salt/cli/key.py index 6e20d6b1dee..3c7a2436f04 100644 --- a/salt/cli/key.py +++ b/salt/cli/key.py @@ -1,26 +1,26 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals - import salt.utils.parsers from salt.utils.verify import check_user, verify_log class SaltKey(salt.utils.parsers.SaltKeyOptionParser): - ''' + """ Initialize the Salt key manager - ''' + """ def run(self): - ''' + """ Execute salt-key - ''' + """ import salt.key + self.parse_args() self.setup_logfile_logger() verify_log(self.config) key = salt.key.KeyCLI(self.config) - if check_user(self.config['user']): + if check_user(self.config["user"]): key.run() diff --git a/salt/cli/run.py b/salt/cli/run.py index 600cc55700c..f626b06ab92 100644 --- a/salt/cli/run.py +++ b/salt/cli/run.py @@ -1,24 +1,25 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +import salt.defaults.exitcodes # pylint: disable=W0611 import salt.utils.parsers import salt.utils.profile -from salt.utils.verify import check_user, verify_log from salt.exceptions import SaltClientError from salt.ext import six -import salt.defaults.exitcodes # pylint: disable=W0611 +from salt.utils.verify import check_user, verify_log class SaltRun(salt.utils.parsers.SaltRunOptionParser): - ''' + """ Used to execute Salt runners - ''' + """ def run(self): - ''' + """ Execute salt-run - ''' + """ import salt.runner + self.parse_args() # Setup file logging! @@ -34,7 +35,7 @@ class SaltRun(salt.utils.parsers.SaltRunOptionParser): # Run this here so SystemExit isn't raised anywhere else when # someone tries to use the runners via the python API try: - if check_user(self.config['user']): + if check_user(self.config["user"]): pr = salt.utils.profile.activate_profile(profiling_enabled) try: ret = runner.run() @@ -44,15 +45,14 @@ class SaltRun(salt.utils.parsers.SaltRunOptionParser): # runners might still use it. For this reason, we # also check ret['data']['retcode'] if # ret['retcode'] is not available. - if isinstance(ret, dict) and 'retcode' in ret: - self.exit(ret['retcode']) - elif isinstance(ret, dict) and 'retcode' in ret.get('data', {}): - self.exit(ret['data']['retcode']) + if isinstance(ret, dict) and "retcode" in ret: + self.exit(ret["retcode"]) + elif isinstance(ret, dict) and "retcode" in ret.get("data", {}): + self.exit(ret["data"]["retcode"]) finally: salt.utils.profile.output_profile( - pr, - stats_path=self.options.profiling_path, - stop=True) + pr, stats_path=self.options.profiling_path, stop=True + ) except SaltClientError as exc: raise SystemExit(six.text_type(exc)) diff --git a/salt/cli/salt.py b/salt/cli/salt.py index 4500b4cb33e..0bca30b1b8d 100644 --- a/salt/cli/salt.py +++ b/salt/cli/salt.py @@ -2,18 +2,16 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import sys -sys.modules['pkg_resources'] = None + import os +import sys # Import Salt libs import salt.defaults.exitcodes +import salt.log import salt.utils.job import salt.utils.parsers import salt.utils.stringutils -import salt.log -from salt.utils.args import yamlify_arg -from salt.utils.verify import verify_log from salt.exceptions import ( AuthenticationError, AuthorizationError, @@ -21,26 +19,31 @@ from salt.exceptions import ( LoaderError, SaltClientError, SaltInvocationError, - SaltSystemExit + SaltSystemExit, ) # Import 3rd-party libs from salt.ext import six +from salt.utils.args import yamlify_arg +from salt.utils.verify import verify_log + +sys.modules["pkg_resources"] = None class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): - ''' + """ The execution of a salt command happens here - ''' + """ def run(self): - ''' + """ Execute the salt command line - ''' + """ import salt.client + self.parse_args() - if self.config['log_level'] not in ('quiet', ): + if self.config["log_level"] not in ("quiet",): # Setup file logging! self.setup_logfile_logger() verify_log(self.config) @@ -48,14 +51,15 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): try: # We don't need to bail on config file permission errors # if the CLI process is run with the -a flag - skip_perm_errors = self.options.eauth != '' + skip_perm_errors = self.options.eauth != "" self.local_client = salt.client.get_local_client( self.get_config_file_path(), skip_perm_errors=skip_perm_errors, - auto_reconnect=True) + auto_reconnect=True, + ) except SaltClientError as exc: - self.exit(2, '{0}\n'.format(exc)) + self.exit(2, "{0}\n".format(exc)) return if self.options.batch or self.options.static: @@ -68,87 +72,95 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): if self.options.preview_target: minion_list = self._preview_target() - self._output_ret(minion_list, self.config.get('output', 'nested')) + self._output_ret(minion_list, self.config.get("output", "nested")) return if self.options.timeout <= 0: - self.options.timeout = self.local_client.opts['timeout'] + self.options.timeout = self.local_client.opts["timeout"] kwargs = { - 'tgt': self.config['tgt'], - 'fun': self.config['fun'], - 'arg': self.config['arg'], - 'timeout': self.options.timeout, - 'show_timeout': self.options.show_timeout, - 'show_jid': self.options.show_jid} + "tgt": self.config["tgt"], + "fun": self.config["fun"], + "arg": self.config["arg"], + "timeout": self.options.timeout, + "show_timeout": self.options.show_timeout, + "show_jid": self.options.show_jid, + } - if 'token' in self.config: + if "token" in self.config: import salt.utils.files - try: - with salt.utils.files.fopen(os.path.join(self.config['cachedir'], '.root_key'), 'r') as fp_: - kwargs['key'] = fp_.readline() - except IOError: - kwargs['token'] = self.config['token'] - kwargs['delimiter'] = self.options.delimiter + try: + with salt.utils.files.fopen( + os.path.join(self.config["cachedir"], ".root_key"), "r" + ) as fp_: + kwargs["key"] = fp_.readline() + except IOError: + kwargs["token"] = self.config["token"] + + kwargs["delimiter"] = self.options.delimiter if self.selected_target_option: - kwargs['tgt_type'] = self.selected_target_option + kwargs["tgt_type"] = self.selected_target_option else: - kwargs['tgt_type'] = 'glob' + kwargs["tgt_type"] = "glob" # If batch_safe_limit is set, check minions matching target and # potentially switch to batch execution if self.options.batch_safe_limit > 1: if len(self._preview_target()) >= self.options.batch_safe_limit: - salt.utils.stringutils.print_cli('\nNOTICE: Too many minions targeted, switching to batch execution.') + salt.utils.stringutils.print_cli( + "\nNOTICE: Too many minions targeted, switching to batch execution." + ) self.options.batch = self.options.batch_safe_size self._run_batch() return - if getattr(self.options, 'return'): - kwargs['ret'] = getattr(self.options, 'return') + if getattr(self.options, "return"): + kwargs["ret"] = getattr(self.options, "return") - if getattr(self.options, 'return_config'): - kwargs['ret_config'] = getattr(self.options, 'return_config') + if getattr(self.options, "return_config"): + kwargs["ret_config"] = getattr(self.options, "return_config") - if getattr(self.options, 'return_kwargs'): - kwargs['ret_kwargs'] = yamlify_arg( - getattr(self.options, 'return_kwargs')) + if getattr(self.options, "return_kwargs"): + kwargs["ret_kwargs"] = yamlify_arg(getattr(self.options, "return_kwargs")) - if getattr(self.options, 'module_executors'): - kwargs['module_executors'] = yamlify_arg(getattr(self.options, 'module_executors')) + if getattr(self.options, "module_executors"): + kwargs["module_executors"] = yamlify_arg( + getattr(self.options, "module_executors") + ) - if getattr(self.options, 'executor_opts'): - kwargs['executor_opts'] = yamlify_arg(getattr(self.options, 'executor_opts')) + if getattr(self.options, "executor_opts"): + kwargs["executor_opts"] = yamlify_arg( + getattr(self.options, "executor_opts") + ) - if getattr(self.options, 'metadata'): - kwargs['metadata'] = yamlify_arg( - getattr(self.options, 'metadata')) + if getattr(self.options, "metadata"): + kwargs["metadata"] = yamlify_arg(getattr(self.options, "metadata")) # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials - if 'token' not in kwargs and 'key' not in kwargs and self.options.eauth: + if "token" not in kwargs and "key" not in kwargs and self.options.eauth: # This is expensive. Don't do it unless we need to. import salt.auth + resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: - tok = resolver.token_cli( - self.options.eauth, - res - ) + tok = resolver.token_cli(self.options.eauth, res) if tok: - kwargs['token'] = tok.get('token', '') + kwargs["token"] = tok.get("token", "") if not res: - sys.stderr.write('ERROR: Authentication failed\n') + sys.stderr.write("ERROR: Authentication failed\n") sys.exit(2) kwargs.update(res) - kwargs['eauth'] = self.options.eauth + kwargs["eauth"] = self.options.eauth - if self.config['async']: + if self.config["async"]: jid = self.local_client.cmd_async(**kwargs) - salt.utils.stringutils.print_cli('Executed command with job ID: {0}'.format(jid)) + salt.utils.stringutils.print_cli( + "Executed command with job ID: {0}".format(jid) + ) return # local will be None when there was an error @@ -161,35 +173,35 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): try: if self.options.subset: cmd_func = self.local_client.cmd_subset - kwargs['sub'] = self.options.subset - kwargs['cli'] = True + kwargs["sub"] = self.options.subset + kwargs["cli"] = True else: cmd_func = self.local_client.cmd_cli if self.options.progress: - kwargs['progress'] = True - self.config['progress'] = True + kwargs["progress"] = True + self.config["progress"] = True ret = {} for progress in cmd_func(**kwargs): - out = 'progress' + out = "progress" try: self._progress_ret(progress, out) except LoaderError as exc: raise SaltSystemExit(exc) - if 'return_count' not in progress: + if "return_count" not in progress: ret.update(progress) self._progress_end(out) self._print_returns_summary(ret) - elif self.config['fun'] == 'sys.doc': + elif self.config["fun"] == "sys.doc": ret = {} - out = '' + out = "" for full_ret in self.local_client.cmd_cli(**kwargs): ret_, out, retcode = self._format_ret(full_ret) ret.update(ret_) self._output_ret(ret, out, retcode=retcode) else: if self.options.verbose: - kwargs['verbose'] = True + kwargs["verbose"] = True ret = {} for full_ret in cmd_func(**kwargs): try: @@ -201,8 +213,8 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): errors.append(full_ret) # Returns summary - if self.config['cli_summary'] is True: - if self.config['fun'] != 'sys.doc': + if self.config["cli_summary"] is True: + if self.config["fun"] != "sys.doc": if self.options.output is None: self._print_returns_summary(ret) self._print_errors_summary(errors) @@ -211,54 +223,59 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): # returned 'ok' with a retcode of 0. # This is the final point before the 'salt' cmd returns, # which is why we set the retcode here. - if not all(exit_code == salt.defaults.exitcodes.EX_OK for exit_code in retcodes): - sys.stderr.write('ERROR: Minions returned with non-zero exit code\n') + if not all( + exit_code == salt.defaults.exitcodes.EX_OK for exit_code in retcodes + ): + sys.stderr.write("ERROR: Minions returned with non-zero exit code\n") sys.exit(salt.defaults.exitcodes.EX_GENERIC) - except (AuthenticationError, - AuthorizationError, - SaltInvocationError, - EauthAuthenticationError, - SaltClientError) as exc: + except ( + AuthenticationError, + AuthorizationError, + SaltInvocationError, + EauthAuthenticationError, + SaltClientError, + ) as exc: ret = six.text_type(exc) - self._output_ret(ret, '', retcode=1) + self._output_ret(ret, "", retcode=1) def _preview_target(self): - ''' + """ Return a list of minions from a given target - ''' - return self.local_client.gather_minions(self.config['tgt'], self.selected_target_option or 'glob') + """ + return self.local_client.gather_minions( + self.config["tgt"], self.selected_target_option or "glob" + ) def _run_batch(self): import salt.cli.batch + eauth = {} - if 'token' in self.config: - eauth['token'] = self.config['token'] + if "token" in self.config: + eauth["token"] = self.config["token"] # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials - if 'token' not in eauth and self.options.eauth: + if "token" not in eauth and self.options.eauth: # This is expensive. Don't do it unless we need to. import salt.auth + resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: - tok = resolver.token_cli( - self.options.eauth, - res - ) + tok = resolver.token_cli(self.options.eauth, res) if tok: - eauth['token'] = tok.get('token', '') + eauth["token"] = tok.get("token", "") if not res: - sys.stderr.write('ERROR: Authentication failed\n') + sys.stderr.write("ERROR: Authentication failed\n") sys.exit(2) eauth.update(res) - eauth['eauth'] = self.options.eauth + eauth["eauth"] = self.options.eauth if self.options.static: if not self.options.batch: - self.config['batch'] = '100%' + self.config["batch"] = "100%" try: batch = salt.cli.batch.Batch(self.config, eauth=eauth, quiet=True) @@ -270,12 +287,14 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): for res in batch.run(): ret.update(res) - self._output_ret(ret, '') + self._output_ret(ret, "") else: try: - self.config['batch'] = self.options.batch - batch = salt.cli.batch.Batch(self.config, eauth=eauth, parser=self.options) + self.config["batch"] = self.options.batch + batch = salt.cli.batch.Batch( + self.config, eauth=eauth, parser=self.options + ) except SaltClientError: # We will print errors to the console further down the stack sys.exit(1) @@ -291,17 +310,17 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): def _print_errors_summary(self, errors): if errors: - salt.utils.stringutils.print_cli('\n') - salt.utils.stringutils.print_cli('---------------------------') - salt.utils.stringutils.print_cli('Errors') - salt.utils.stringutils.print_cli('---------------------------') + salt.utils.stringutils.print_cli("\n") + salt.utils.stringutils.print_cli("---------------------------") + salt.utils.stringutils.print_cli("Errors") + salt.utils.stringutils.print_cli("---------------------------") for error in errors: salt.utils.stringutils.print_cli(self._format_error(error)) def _print_returns_summary(self, ret): - ''' + """ Display returns summary - ''' + """ return_counter = 0 not_return_counter = 0 not_return_minions = [] @@ -310,12 +329,11 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): failed_minions = [] for each_minion in ret: minion_ret = ret[each_minion] - if isinstance(minion_ret, dict) and 'ret' in minion_ret: - minion_ret = ret[each_minion].get('ret') - if ( - isinstance(minion_ret, six.string_types) - and minion_ret.startswith("Minion did not return") - ): + if isinstance(minion_ret, dict) and "ret" in minion_ret: + minion_ret = ret[each_minion].get("ret") + if isinstance(minion_ret, six.string_types) and minion_ret.startswith( + "Minion did not return" + ): if "Not connected" in minion_ret: not_connected_minions.append(each_minion) elif "No response" in minion_ret: @@ -326,85 +344,101 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): return_counter += 1 if self._get_retcode(ret[each_minion]): failed_minions.append(each_minion) - salt.utils.stringutils.print_cli('\n') - salt.utils.stringutils.print_cli('-------------------------------------------') - salt.utils.stringutils.print_cli('Summary') - salt.utils.stringutils.print_cli('-------------------------------------------') - salt.utils.stringutils.print_cli('# of minions targeted: {0}'.format(return_counter + not_return_counter)) - salt.utils.stringutils.print_cli('# of minions returned: {0}'.format(return_counter)) - salt.utils.stringutils.print_cli('# of minions that did not return: {0}'.format(not_return_counter)) - salt.utils.stringutils.print_cli('# of minions with errors: {0}'.format(len(failed_minions))) + salt.utils.stringutils.print_cli("\n") + salt.utils.stringutils.print_cli("-------------------------------------------") + salt.utils.stringutils.print_cli("Summary") + salt.utils.stringutils.print_cli("-------------------------------------------") + salt.utils.stringutils.print_cli( + "# of minions targeted: {0}".format(return_counter + not_return_counter) + ) + salt.utils.stringutils.print_cli( + "# of minions returned: {0}".format(return_counter) + ) + salt.utils.stringutils.print_cli( + "# of minions that did not return: {0}".format(not_return_counter) + ) + salt.utils.stringutils.print_cli( + "# of minions with errors: {0}".format(len(failed_minions)) + ) if self.options.verbose: if not_connected_minions: - salt.utils.stringutils.print_cli('Minions not connected: {0}'.format(" ".join(not_connected_minions))) + salt.utils.stringutils.print_cli( + "Minions not connected: {0}".format(" ".join(not_connected_minions)) + ) if not_response_minions: - salt.utils.stringutils.print_cli('Minions not responding: {0}'.format(" ".join(not_response_minions))) + salt.utils.stringutils.print_cli( + "Minions not responding: {0}".format(" ".join(not_response_minions)) + ) if failed_minions: - salt.utils.stringutils.print_cli('Minions with failures: {0}'.format(" ".join(failed_minions))) - salt.utils.stringutils.print_cli('-------------------------------------------') + salt.utils.stringutils.print_cli( + "Minions with failures: {0}".format(" ".join(failed_minions)) + ) + salt.utils.stringutils.print_cli("-------------------------------------------") def _progress_end(self, out): import salt.output + salt.output.progress_end(self.progress_bar) def _progress_ret(self, progress, out): - ''' + """ Print progress events - ''' + """ import salt.output + # Get the progress bar - if not hasattr(self, 'progress_bar'): + if not hasattr(self, "progress_bar"): try: self.progress_bar = salt.output.get_progress(self.config, out, progress) except Exception: # pylint: disable=broad-except - raise LoaderError('\nWARNING: Install the `progressbar` python package. ' - 'Requested job was still run but output cannot be displayed.\n') + raise LoaderError( + "\nWARNING: Install the `progressbar` python package. " + "Requested job was still run but output cannot be displayed.\n" + ) salt.output.update_progress(self.config, progress, self.progress_bar, out) def _output_ret(self, ret, out, retcode=0): - ''' + """ Print the output from a single return to the terminal - ''' + """ import salt.output + # Handle special case commands - if self.config['fun'] == 'sys.doc' and not isinstance(ret, Exception): + if self.config["fun"] == "sys.doc" and not isinstance(ret, Exception): self._print_docs(ret) else: # Determine the proper output method and run it - salt.output.display_output(ret, - out=out, - opts=self.config, - _retcode=retcode) + salt.output.display_output(ret, out=out, opts=self.config, _retcode=retcode) if not ret: - sys.stderr.write('ERROR: No return received\n') + sys.stderr.write("ERROR: No return received\n") sys.exit(2) def _format_ret(self, full_ret): - ''' + """ Take the full return data and format it to simple output - ''' + """ ret = {} - out = '' + out = "" retcode = 0 for key, data in six.iteritems(full_ret): - ret[key] = data['ret'] - if 'out' in data: - out = data['out'] + ret[key] = data["ret"] + if "out" in data: + out = data["out"] ret_retcode = self._get_retcode(data) if ret_retcode > retcode: retcode = ret_retcode return ret, out, retcode def _get_retcode(self, ret): - ''' + """ Determine a retcode for a given return - ''' + """ retcode = 0 # if there is a dict with retcode, use that - if isinstance(ret, dict) and ret.get('retcode', 0) != 0: - if isinstance(ret.get('retcode', 0), dict): - return max(six.itervalues(ret.get('retcode', {0: 0}))) - return ret['retcode'] + if isinstance(ret, dict) and ret.get("retcode", 0) != 0: + if isinstance(ret.get("retcode", 0), dict): + return max(six.itervalues(ret.get("retcode", {0: 0}))) + return ret["retcode"] # if its a boolean, False means 1 elif isinstance(ret, bool) and not ret: return 1 @@ -412,32 +446,36 @@ class SaltCMD(salt.utils.parsers.SaltCMDOptionParser): def _format_error(self, minion_error): for minion, error_doc in six.iteritems(minion_error): - error = 'Minion [{0}] encountered exception \'{1}\''.format(minion, error_doc['message']) + error = "Minion [{0}] encountered exception '{1}'".format( + minion, error_doc["message"] + ) return error def _print_docs(self, ret): - ''' + """ Print out the docstrings for all of the functions on the minions - ''' + """ import salt.output + docs = {} if not ret: - self.exit(2, 'No minions found to gather docs from\n') + self.exit(2, "No minions found to gather docs from\n") if isinstance(ret, six.string_types): - self.exit(2, '{0}\n'.format(ret)) + self.exit(2, "{0}\n".format(ret)) for host in ret: - if isinstance(ret[host], six.string_types) \ - and (ret[host].startswith("Minion did not return") - or ret[host] == 'VALUE_TRIMMED'): + if isinstance(ret[host], six.string_types) and ( + ret[host].startswith("Minion did not return") + or ret[host] == "VALUE_TRIMMED" + ): continue for fun in ret[host]: if fun not in docs and ret[host][fun]: docs[fun] = ret[host][fun] if self.options.output: for fun in sorted(docs): - salt.output.display_output({fun: docs[fun]}, 'nested', self.config) + salt.output.display_output({fun: docs[fun]}, "nested", self.config) else: for fun in sorted(docs): - salt.utils.stringutils.print_cli('{0}:'.format(fun)) + salt.utils.stringutils.print_cli("{0}:".format(fun)) salt.utils.stringutils.print_cli(docs[fun]) - salt.utils.stringutils.print_cli('') + salt.utils.stringutils.print_cli("") diff --git a/salt/cli/spm.py b/salt/cli/spm.py index 4df63d5737e..6b1616efccc 100644 --- a/salt/cli/spm.py +++ b/salt/cli/spm.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" salt.cli.spm ~~~~~~~~~~~~~ Salt's spm cli parser. .. versionadded:: 2015.8.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -14,28 +14,27 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.spm import salt.utils.parsers as parsers -from salt.utils.verify import verify_log, verify_env +from salt.utils.verify import verify_env, verify_log class SPM(parsers.SPMParser): - ''' + """ The cli parser object used to fire up the salt spm system. - ''' + """ def run(self): - ''' + """ Run the api - ''' + """ ui = salt.spm.SPMCmdlineInterface() self.parse_args() self.setup_logfile_logger() v_dirs = [ - self.config['spm_cache_dir'], + self.config["spm_cache_dir"], ] - verify_env(v_dirs, - self.config['user'], - root_dir=self.config['root_dir'], - ) + verify_env( + v_dirs, self.config["user"], root_dir=self.config["root_dir"], + ) verify_log(self.config) client = salt.spm.SPMClient(ui, self.config) client.run(self.args) diff --git a/salt/cli/ssh.py b/salt/cli/ssh.py index 19ecf8845fd..04f08dccf5e 100644 --- a/salt/cli/ssh.py +++ b/salt/cli/ssh.py @@ -1,21 +1,23 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals + import sys + import salt.client.ssh import salt.utils.parsers from salt.utils.verify import verify_log class SaltSSH(salt.utils.parsers.SaltSSHOptionParser): - ''' + """ Used to Execute the salt ssh routine - ''' + """ def run(self): - if '-H' in sys.argv or '--hosts' in sys.argv: - sys.argv += ['x', 'x'] # Hack: pass a mandatory two options - # that won't be used anyways with -H or --hosts + if "-H" in sys.argv or "--hosts" in sys.argv: + sys.argv += ["x", "x"] # Hack: pass a mandatory two options + # that won't be used anyways with -H or --hosts self.parse_args() self.setup_logfile_logger() verify_log(self.config) diff --git a/salt/client/__init__.py b/salt/client/__init__.py index 08fbe191d49..6aa7099ecf3 100644 --- a/salt/client/__init__.py +++ b/salt/client/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The client module is used to create a client connection to the publisher The data structure needs to be: {'enc': 'clear', @@ -7,7 +7,21 @@ The data structure needs to be: 'arg':, ('arg1', 'arg2', ...), 'tgt': '', 'key': ''} -''' +""" + + +# Import salt libs + +# Import third party libs + +# pylint: disable=import-error + +# Try to import range from https://github.com/ytoolshed/range +# +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals + +import logging # The components here are simple, and they need to be and stay simple, we # want a client to have 3 external concerns, and maybe a forth configurable @@ -17,23 +31,22 @@ The data structure needs to be: # 2. What is the function being run? # 3. What arguments need to be passed to the function? # 4. How long do we wait for all of the replies? -# -# Import python libs -from __future__ import absolute_import, print_function, unicode_literals import os +import random import sys import time -import random -import logging from datetime import datetime -# Import salt libs -import salt.config import salt.cache +import salt.config import salt.defaults.exitcodes -import salt.payload -import salt.transport.client + +# Import tornado +import salt.ext.tornado.gen # pylint: disable=F0401 import salt.loader +import salt.payload +import salt.syspaths as syspaths +import salt.transport.client import salt.utils.args import salt.utils.event import salt.utils.files @@ -44,43 +57,38 @@ import salt.utils.stringutils import salt.utils.user import salt.utils.verify import salt.utils.zeromq -import salt.syspaths as syspaths from salt.exceptions import ( AuthenticationError, AuthorizationError, EauthAuthenticationError, PublishError, + SaltClientError, SaltInvocationError, SaltReqTimeoutError, - SaltClientError ) - -# Import third party libs from salt.ext import six -# pylint: disable=import-error -# Try to import range from https://github.com/ytoolshed/range HAS_RANGE = False try: import seco.range + HAS_RANGE = True except ImportError: pass # pylint: enable=import-error -# Import tornado -import salt.ext.tornado.gen # pylint: disable=F0401 log = logging.getLogger(__name__) def get_local_client( - c_path=os.path.join(syspaths.CONFIG_DIR, 'master'), - mopts=None, - skip_perm_errors=False, - io_loop=None, - auto_reconnect=False): - ''' + c_path=os.path.join(syspaths.CONFIG_DIR, "master"), + mopts=None, + skip_perm_errors=False, + io_loop=None, + auto_reconnect=False, +): + """ .. versionadded:: 2014.7.0 Read in the config and return the correct LocalClient object based on @@ -91,12 +99,13 @@ def get_local_client( operation for obtaining events. Eg use of set_event_handler() API. Otherwise, operation will be synchronous. - ''' + """ if mopts: opts = mopts else: # Late import to prevent circular import import salt.config + opts = salt.config.client_config(c_path) # TODO: AIO core is separate from transport @@ -104,11 +113,12 @@ def get_local_client( mopts=opts, skip_perm_errors=skip_perm_errors, io_loop=io_loop, - auto_reconnect=auto_reconnect) + auto_reconnect=auto_reconnect, + ) class LocalClient(object): - ''' + """ The interface used by the :command:`salt` CLI tool on the Salt Master ``LocalClient`` is used to send a command to Salt minions to execute @@ -134,26 +144,33 @@ class LocalClient(object): local = salt.client.LocalClient() local.cmd('*', 'test.fib', [10]) - ''' - def __init__(self, - c_path=os.path.join(syspaths.CONFIG_DIR, 'master'), - mopts=None, skip_perm_errors=False, - io_loop=None, keep_loop=False, auto_reconnect=False): - ''' + """ + + def __init__( + self, + c_path=os.path.join(syspaths.CONFIG_DIR, "master"), + mopts=None, + skip_perm_errors=False, + io_loop=None, + keep_loop=False, + auto_reconnect=False, + ): + """ :param IOLoop io_loop: io_loop used for events. Pass in an io_loop if you want asynchronous operation for obtaining events. Eg use of set_event_handler() API. Otherwise, operation will be synchronous. - ''' + """ if mopts: self.opts = mopts else: if os.path.isdir(c_path): log.warning( - '%s expects a file path not a directory path(%s) to ' - 'its \'c_path\' keyword argument', - self.__class__.__name__, c_path + "%s expects a file path not a directory path(%s) to " + "its 'c_path' keyword argument", + self.__class__.__name__, + c_path, ) self.opts = salt.config.client_config(c_path) self.serial = salt.payload.Serial(self.opts) @@ -162,124 +179,126 @@ class LocalClient(object): self.key = self.__read_master_key() self.auto_reconnect = auto_reconnect self.event = salt.utils.event.get_event( - 'master', - self.opts['sock_dir'], - self.opts['transport'], - opts=self.opts, - listen=False, - io_loop=io_loop, - keep_loop=keep_loop) + "master", + self.opts["sock_dir"], + self.opts["transport"], + opts=self.opts, + listen=False, + io_loop=io_loop, + keep_loop=keep_loop, + ) self.utils = salt.loader.utils(self.opts) self.functions = salt.loader.minion_mods(self.opts, utils=self.utils) self.returners = salt.loader.returners(self.opts, self.functions) def __read_master_key(self): - ''' + """ Read in the rotating master authentication key - ''' + """ key_user = self.salt_user - if key_user == 'root': - if self.opts.get('user', 'root') != 'root': - key_user = self.opts.get('user', 'root') - if key_user.startswith('sudo_'): - key_user = self.opts.get('user', 'root') + if key_user == "root": + if self.opts.get("user", "root") != "root": + key_user = self.opts.get("user", "root") + if key_user.startswith("sudo_"): + key_user = self.opts.get("user", "root") if salt.utils.platform.is_windows(): # The username may contain '\' if it is in Windows # 'DOMAIN\username' format. Fix this for the keyfile path. - key_user = key_user.replace('\\', '_') - keyfile = os.path.join(self.opts['cachedir'], - '.{0}_key'.format(key_user)) + key_user = key_user.replace("\\", "_") + keyfile = os.path.join(self.opts["cachedir"], ".{0}_key".format(key_user)) try: # Make sure all key parent directories are accessible - salt.utils.verify.check_path_traversal(self.opts['cachedir'], - key_user, - self.skip_perm_errors) - with salt.utils.files.fopen(keyfile, 'r') as key: + salt.utils.verify.check_path_traversal( + self.opts["cachedir"], key_user, self.skip_perm_errors + ) + with salt.utils.files.fopen(keyfile, "r") as key: return salt.utils.stringutils.to_unicode(key.read()) except (OSError, IOError, SaltClientError): # Fall back to eauth - return '' + return "" def _convert_range_to_list(self, tgt): - ''' + """ convert a seco.range range into a list target - ''' - range_ = seco.range.Range(self.opts['range_server']) + """ + range_ = seco.range.Range(self.opts["range_server"]) try: return range_.expand(tgt) except seco.range.RangeException as err: - print('Range server exception: {0}'.format(err)) + print("Range server exception: {0}".format(err)) return [] def _get_timeout(self, timeout): - ''' + """ Return the timeout to use - ''' + """ if timeout is None: - return self.opts['timeout'] + return self.opts["timeout"] if isinstance(timeout, int): return timeout if isinstance(timeout, six.string_types): try: return int(timeout) except ValueError: - return self.opts['timeout'] + return self.opts["timeout"] # Looks like the timeout is invalid, use config - return self.opts['timeout'] + return self.opts["timeout"] def gather_job_info(self, jid, tgt, tgt_type, listen=True, **kwargs): - ''' + """ Return the information about a given job - ''' - log.debug('Checking whether jid %s is still running', jid) - timeout = int(kwargs.get('gather_job_timeout', self.opts['gather_job_timeout'])) + """ + log.debug("Checking whether jid %s is still running", jid) + timeout = int(kwargs.get("gather_job_timeout", self.opts["gather_job_timeout"])) - pub_data = self.run_job(tgt, - 'saltutil.find_job', - arg=[jid], - tgt_type=tgt_type, - timeout=timeout, - listen=listen, - **kwargs - ) + pub_data = self.run_job( + tgt, + "saltutil.find_job", + arg=[jid], + tgt_type=tgt_type, + timeout=timeout, + listen=listen, + **kwargs + ) - if 'jid' in pub_data: - self.event.subscribe(pub_data['jid']) + if "jid" in pub_data: + self.event.subscribe(pub_data["jid"]) return pub_data def _check_pub_data(self, pub_data, listen=True): - ''' + """ Common checks on the pub_data data structure returned from running pub - ''' - if pub_data == '': + """ + if pub_data == "": # Failed to authenticate, this could be a bunch of things raise EauthAuthenticationError( - 'Failed to authenticate! This is most likely because this ' - 'user is not permitted to execute commands, but there is a ' - 'small possibility that a disk error occurred (check ' - 'disk/inode usage).' + "Failed to authenticate! This is most likely because this " + "user is not permitted to execute commands, but there is a " + "small possibility that a disk error occurred (check " + "disk/inode usage)." ) # Failed to connect to the master and send the pub - if 'error' in pub_data: - print(pub_data['error']) - log.debug('_check_pub_data() error: %s', pub_data['error']) + if "error" in pub_data: + print(pub_data["error"]) + log.debug("_check_pub_data() error: %s", pub_data["error"]) return {} - elif 'jid' not in pub_data: + elif "jid" not in pub_data: return {} - if pub_data['jid'] == '0': - print('Failed to connect to the Master, ' - 'is the Salt Master running?') + if pub_data["jid"] == "0": + print("Failed to connect to the Master, " "is the Salt Master running?") return {} # If we order masters (via a syndic), don't short circuit if no minions # are found - if not self.opts.get('order_masters'): + if not self.opts.get("order_masters"): # Check for no minions - if not pub_data['minions']: - print('No minions matched the target. ' - 'No command was sent, no jid was assigned.') + if not pub_data["minions"]: + print( + "No minions matched the target. " + "No command was sent, no jid was assigned." + ) return {} # don't install event subscription listeners when the request is asynchronous @@ -287,26 +306,27 @@ class LocalClient(object): if not listen: return pub_data - if self.opts.get('order_masters'): - self.event.subscribe('syndic/.*/{0}'.format(pub_data['jid']), 'regex') + if self.opts.get("order_masters"): + self.event.subscribe("syndic/.*/{0}".format(pub_data["jid"]), "regex") - self.event.subscribe('salt/job/{0}'.format(pub_data['jid'])) + self.event.subscribe("salt/job/{0}".format(pub_data["jid"])) return pub_data def run_job( - self, - tgt, - fun, - arg=(), - tgt_type='glob', - ret='', - timeout=None, - jid='', - kwarg=None, - listen=False, - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + tgt_type="glob", + ret="", + timeout=None, + jid="", + kwarg=None, + listen=False, + **kwargs + ): + """ Asynchronously send a command to connected minions Prep the job directory and publish a command to any targeted minions. @@ -319,7 +339,7 @@ class LocalClient(object): >>> local.run_job('*', 'test.sleep', [300]) {'jid': '20131219215650131543', 'minions': ['jerry']} - ''' + """ arg = salt.utils.args.condition_input(arg, kwarg) try: @@ -332,11 +352,12 @@ class LocalClient(object): jid=jid, timeout=self._get_timeout(timeout), listen=listen, - **kwargs) + **kwargs + ) except SaltClientError: # Re-raise error with specific message raise SaltClientError( - 'The salt master could not be contacted. Is master running?' + "The salt master could not be contacted. Is master running?" ) except AuthenticationError as err: six.reraise(*sys.exc_info()) @@ -349,24 +370,27 @@ class LocalClient(object): return self._check_pub_data(pub_data, listen=listen) def gather_minions(self, tgt, expr_form): - _res = salt.utils.minions.CkMinions(self.opts).check_minions(tgt, tgt_type=expr_form) - return _res['minions'] + _res = salt.utils.minions.CkMinions(self.opts).check_minions( + tgt, tgt_type=expr_form + ) + return _res["minions"] @salt.ext.tornado.gen.coroutine def run_job_async( - self, - tgt, - fun, - arg=(), - tgt_type='glob', - ret='', - timeout=None, - jid='', - kwarg=None, - listen=True, - io_loop=None, - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + tgt_type="glob", + ret="", + timeout=None, + jid="", + kwarg=None, + listen=True, + io_loop=None, + **kwargs + ): + """ Asynchronously send a command to connected minions Prep the job directory and publish a command to any targeted minions. @@ -379,25 +403,26 @@ class LocalClient(object): >>> local.run_job_async('*', 'test.sleep', [300]) {'jid': '20131219215650131543', 'minions': ['jerry']} - ''' + """ arg = salt.utils.args.condition_input(arg, kwarg) try: pub_data = yield self.pub_async( - tgt, - fun, - arg, - tgt_type, - ret, - jid=jid, - timeout=self._get_timeout(timeout), - io_loop=io_loop, - listen=listen, - **kwargs) + tgt, + fun, + arg, + tgt_type, + ret, + jid=jid, + timeout=self._get_timeout(timeout), + io_loop=io_loop, + listen=listen, + **kwargs + ) except SaltClientError: # Re-raise error with specific message raise SaltClientError( - 'The salt master could not be contacted. Is master running?' + "The salt master could not be contacted. Is master running?" ) except AuthenticationError as err: raise AuthenticationError(err) @@ -410,16 +435,9 @@ class LocalClient(object): raise salt.ext.tornado.gen.Return(self._check_pub_data(pub_data, listen=listen)) def cmd_async( - self, - tgt, - fun, - arg=(), - tgt_type='glob', - ret='', - jid='', - kwarg=None, - **kwargs): - ''' + self, tgt, fun, arg=(), tgt_type="glob", ret="", jid="", kwarg=None, **kwargs + ): + """ Asynchronously send a command to connected minions The function signature is the same as :py:meth:`cmd` with the @@ -431,35 +449,31 @@ class LocalClient(object): >>> local.cmd_async('*', 'test.sleep', [300]) '20131219215921857715' - ''' + """ arg = salt.utils.args.condition_input(arg, kwarg) - pub_data = self.run_job(tgt, - fun, - arg, - tgt_type, - ret, - jid=jid, - listen=False, - **kwargs) + pub_data = self.run_job( + tgt, fun, arg, tgt_type, ret, jid=jid, listen=False, **kwargs + ) try: - return pub_data['jid'] + return pub_data["jid"] except KeyError: return 0 def cmd_subset( - self, - tgt, - fun, - arg=(), - tgt_type='glob', - ret='', - kwarg=None, - sub=3, - cli=False, - progress=False, - full_return=False, - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + tgt_type="glob", + ret="", + kwarg=None, + sub=3, + cli=False, + progress=False, + full_return=False, + **kwargs + ): + """ Execute a command on a random subset of the targeted systems The function signature is the same as :py:meth:`cmd` with the @@ -473,11 +487,8 @@ class LocalClient(object): >>> SLC.cmd_subset('*', 'test.ping', sub=1) {'jerry': True} - ''' - minion_ret = self.cmd(tgt, - 'sys.list_functions', - tgt_type=tgt_type, - **kwargs) + """ + minion_ret = self.cmd(tgt, "sys.list_functions", tgt_type=tgt_type, **kwargs) minions = list(minion_ret) random.shuffle(minions) f_tgt = [] @@ -490,27 +501,29 @@ class LocalClient(object): if cli: func = self.cmd_cli return func( - f_tgt, - fun, - arg, - tgt_type='list', - ret=ret, - kwarg=kwarg, - progress=progress, - full_return=full_return, - **kwargs) + f_tgt, + fun, + arg, + tgt_type="list", + ret=ret, + kwarg=kwarg, + progress=progress, + full_return=full_return, + **kwargs + ) def cmd_batch( - self, - tgt, - fun, - arg=(), - tgt_type='glob', - ret='', - kwarg=None, - batch='10%', - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + tgt_type="glob", + ret="", + kwarg=None, + batch="10%", + **kwargs + ): + """ Iteratively execute a command on subsets of minions at a time The function signature is the same as :py:meth:`cmd` with the @@ -528,7 +541,7 @@ class LocalClient(object): {'jerry': {...}} {'dave': {...}} {'stewart': {...}} - ''' + """ # We need to re-import salt.utils.args here # even though it has already been imported. # when cmd_batch is called via the NetAPI @@ -539,31 +552,33 @@ class LocalClient(object): import salt.cli.batch arg = salt.utils.args.condition_input(arg, kwarg) - opts = {'tgt': tgt, - 'fun': fun, - 'arg': arg, - 'tgt_type': tgt_type, - 'ret': ret, - 'batch': batch, - 'failhard': kwargs.get('failhard', self.opts.get('failhard', False)), - 'raw': kwargs.get('raw', False)} + opts = { + "tgt": tgt, + "fun": fun, + "arg": arg, + "tgt_type": tgt_type, + "ret": ret, + "batch": batch, + "failhard": kwargs.get("failhard", self.opts.get("failhard", False)), + "raw": kwargs.get("raw", False), + } - if 'timeout' in kwargs: - opts['timeout'] = kwargs['timeout'] - if 'gather_job_timeout' in kwargs: - opts['gather_job_timeout'] = kwargs['gather_job_timeout'] - if 'batch_wait' in kwargs: - opts['batch_wait'] = int(kwargs['batch_wait']) + if "timeout" in kwargs: + opts["timeout"] = kwargs["timeout"] + if "gather_job_timeout" in kwargs: + opts["gather_job_timeout"] = kwargs["gather_job_timeout"] + if "batch_wait" in kwargs: + opts["batch_wait"] = int(kwargs["batch_wait"]) eauth = {} - if 'eauth' in kwargs: - eauth['eauth'] = kwargs.pop('eauth') - if 'username' in kwargs: - eauth['username'] = kwargs.pop('username') - if 'password' in kwargs: - eauth['password'] = kwargs.pop('password') - if 'token' in kwargs: - eauth['token'] = kwargs.pop('token') + if "eauth" in kwargs: + eauth["eauth"] = kwargs.pop("eauth") + if "username" in kwargs: + eauth["username"] = kwargs.pop("username") + if "password" in kwargs: + eauth["password"] = kwargs.pop("password") + if "token" in kwargs: + eauth["token"] = kwargs.pop("token") for key, val in six.iteritems(self.opts): if key not in opts: @@ -572,18 +587,20 @@ class LocalClient(object): for ret in batch.run(): yield ret - def cmd(self, - tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - jid='', - full_return=False, - kwarg=None, - **kwargs): - ''' + def cmd( + self, + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + jid="", + full_return=False, + kwarg=None, + **kwargs + ): + """ Synchronously execute a command on targeted minions The cmd method will execute and wait for the timeout period for all @@ -685,39 +702,33 @@ class LocalClient(object): :returns: A dictionary with the result of the execution, keyed by minion ID. A compound command will return a sub-dictionary keyed by function name. - ''' + """ arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub try: - pub_data = self.run_job(tgt, - fun, - arg, - tgt_type, - ret, - timeout, - jid, - listen=True, - **kwargs) + pub_data = self.run_job( + tgt, fun, arg, tgt_type, ret, timeout, jid, listen=True, **kwargs + ) if not pub_data: return pub_data ret = {} for fn_ret in self.get_cli_event_returns( - pub_data['jid'], - pub_data['minions'], - self._get_timeout(timeout), - tgt, - tgt_type, - **kwargs): + pub_data["jid"], + pub_data["minions"], + self._get_timeout(timeout), + tgt, + tgt_type, + **kwargs + ): if fn_ret: for mid, data in six.iteritems(fn_ret): - ret[mid] = (data if full_return - else data.get('ret', {})) + ret[mid] = data if full_return else data.get("ret", {}) - for failed in list(set(pub_data['minions']) - set(ret)): + for failed in list(set(pub_data["minions"]) - set(ret)): ret[failed] = False return ret finally: @@ -725,18 +736,19 @@ class LocalClient(object): self.event.close_pub() def cmd_cli( - self, - tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - verbose=False, - kwarg=None, - progress=False, - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + verbose=False, + kwarg=None, + progress=False, + **kwargs + ): + """ Used by the :command:`salt` CLI. This method returns minion returns as they come back and attempts to block until all minions return. @@ -745,34 +757,29 @@ class LocalClient(object): :param verbose: Print extra information about the running command :returns: A generator - ''' + """ arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub try: self.pub_data = self.run_job( - tgt, - fun, - arg, - tgt_type, - ret, - timeout, - listen=True, - **kwargs) + tgt, fun, arg, tgt_type, ret, timeout, listen=True, **kwargs + ) if not self.pub_data: yield self.pub_data else: try: for fn_ret in self.get_cli_event_returns( - self.pub_data['jid'], - self.pub_data['minions'], - self._get_timeout(timeout), - tgt, - tgt_type, - verbose, - progress, - **kwargs): + self.pub_data["jid"], + self.pub_data["minions"], + self._get_timeout(timeout), + tgt, + tgt_type, + verbose, + progress, + **kwargs + ): if not fn_ret: continue @@ -780,30 +787,31 @@ class LocalClient(object): yield fn_ret except KeyboardInterrupt: raise SystemExit( - '\n' - 'This job\'s jid is: {0}\n' - 'Exiting gracefully on Ctrl-c\n' - 'The minions may not have all finished running and any ' - 'remaining minions will return upon completion. To look ' - 'up the return data for this job later, run the following ' - 'command:\n\n' - 'salt-run jobs.lookup_jid {0}'.format(self.pub_data['jid']) + "\n" + "This job's jid is: {0}\n" + "Exiting gracefully on Ctrl-c\n" + "The minions may not have all finished running and any " + "remaining minions will return upon completion. To look " + "up the return data for this job later, run the following " + "command:\n\n" + "salt-run jobs.lookup_jid {0}".format(self.pub_data["jid"]) ) finally: if not was_listening: self.event.close_pub() def cmd_iter( - self, - tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - kwarg=None, - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + kwarg=None, + **kwargs + ): + """ Yields the individual minion returns as they come in The function signature is the same as :py:meth:`cmd` with the @@ -823,53 +831,50 @@ class LocalClient(object): {'jerry': {'ret': True}} {'dave': {'ret': True}} {'stewart': {'ret': True}} - ''' + """ arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub try: pub_data = self.run_job( - tgt, - fun, - arg, - tgt_type, - ret, - timeout, - listen=True, - **kwargs) + tgt, fun, arg, tgt_type, ret, timeout, listen=True, **kwargs + ) if not pub_data: yield pub_data else: - if kwargs.get('yield_pub_data'): + if kwargs.get("yield_pub_data"): yield pub_data - for fn_ret in self.get_iter_returns(pub_data['jid'], - pub_data['minions'], - timeout=self._get_timeout(timeout), - tgt=tgt, - tgt_type=tgt_type, - **kwargs): + for fn_ret in self.get_iter_returns( + pub_data["jid"], + pub_data["minions"], + timeout=self._get_timeout(timeout), + tgt=tgt, + tgt_type=tgt_type, + **kwargs + ): if not fn_ret: continue yield fn_ret - self._clean_up_subscriptions(pub_data['jid']) + self._clean_up_subscriptions(pub_data["jid"]) finally: if not was_listening: self.event.close_pub() def cmd_iter_no_block( - self, - tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - kwarg=None, - show_jid=False, - verbose=False, - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + kwarg=None, + show_jid=False, + verbose=False, + **kwargs + ): + """ Yields the individual minion returns as they come in, or None when no returns are available. @@ -890,105 +895,94 @@ class LocalClient(object): {'dave': {'ret': True}} None {'stewart': {'ret': True}} - ''' + """ arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub try: pub_data = self.run_job( - tgt, - fun, - arg, - tgt_type, - ret, - timeout, - listen=True, - **kwargs) + tgt, fun, arg, tgt_type, ret, timeout, listen=True, **kwargs + ) if not pub_data: yield pub_data else: - for fn_ret in self.get_iter_returns(pub_data['jid'], - pub_data['minions'], - timeout=timeout, - tgt=tgt, - tgt_type=tgt_type, - block=False, - **kwargs): + for fn_ret in self.get_iter_returns( + pub_data["jid"], + pub_data["minions"], + timeout=timeout, + tgt=tgt, + tgt_type=tgt_type, + block=False, + **kwargs + ): if fn_ret and any([show_jid, verbose]): for minion in fn_ret: - fn_ret[minion]['jid'] = pub_data['jid'] + fn_ret[minion]["jid"] = pub_data["jid"] yield fn_ret - self._clean_up_subscriptions(pub_data['jid']) + self._clean_up_subscriptions(pub_data["jid"]) finally: if not was_listening: self.event.close_pub() def cmd_full_return( - self, - tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - verbose=False, - kwarg=None, - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + verbose=False, + kwarg=None, + **kwargs + ): + """ Execute a salt command and return - ''' + """ arg = salt.utils.args.condition_input(arg, kwarg) was_listening = self.event.cpub try: pub_data = self.run_job( - tgt, - fun, - arg, - tgt_type, - ret, - timeout, - listen=True, - **kwargs) + tgt, fun, arg, tgt_type, ret, timeout, listen=True, **kwargs + ) if not pub_data: return pub_data - return (self.get_cli_static_event_returns(pub_data['jid'], - pub_data['minions'], - timeout, - tgt, - tgt_type, - verbose)) + return self.get_cli_static_event_returns( + pub_data["jid"], pub_data["minions"], timeout, tgt, tgt_type, verbose + ) finally: if not was_listening: self.event.close_pub() def get_cli_returns( - self, - jid, - minions, - timeout=None, - tgt='*', - tgt_type='glob', - verbose=False, - show_jid=False, - **kwargs): - ''' + self, + jid, + minions, + timeout=None, + tgt="*", + tgt_type="glob", + verbose=False, + show_jid=False, + **kwargs + ): + """ Starts a watcher looking at the return data for a specified JID :returns: all of the information for the JID - ''' + """ if verbose: - msg = 'Executing job with jid {0}'.format(jid) + msg = "Executing job with jid {0}".format(jid) print(msg) - print('-' * len(msg) + '\n') + print("-" * len(msg) + "\n") elif show_jid: - print('jid: {0}'.format(jid)) + print("jid: {0}".format(jid)) if timeout is None: - timeout = self.opts['timeout'] + timeout = self.opts["timeout"] fret = {} # make sure the minions is a set (since we do set operations on it) minions = set(minions) @@ -1017,11 +1011,8 @@ class LocalClient(object): raise StopIteration() # TODO: tests!! - def get_returns_no_block( - self, - tag, - match_type=None): - ''' + def get_returns_no_block(self, tag, match_type=None): + """ Raw function to just return events of jid excluding timeout logic Yield either the raw event data or None @@ -1029,28 +1020,35 @@ class LocalClient(object): Pass a list of additional regular expressions as `tags_regex` to search the event bus for non-return data, such as minion lists returned from syndics. - ''' + """ while True: - raw = self.event.get_event(wait=0.01, tag=tag, match_type=match_type, full=True, - no_block=True, auto_reconnect=self.auto_reconnect) + raw = self.event.get_event( + wait=0.01, + tag=tag, + match_type=match_type, + full=True, + no_block=True, + auto_reconnect=self.auto_reconnect, + ) yield raw def get_iter_returns( - self, - jid, - minions, - timeout=None, - tgt='*', - tgt_type='glob', - expect_minions=False, - block=True, - **kwargs): - ''' + self, + jid, + minions, + timeout=None, + tgt="*", + tgt_type="glob", + expect_minions=False, + block=True, + **kwargs + ): + """ Watch the event system and return job data as it comes in :returns: all of the information for the JID - ''' + """ if not isinstance(minions, set): if isinstance(minions, six.string_types): minions = set([minions]) @@ -1058,8 +1056,10 @@ class LocalClient(object): minions = set(list(minions)) if timeout is None: - timeout = self.opts['timeout'] - gather_job_timeout = int(kwargs.get('gather_job_timeout', self.opts['gather_job_timeout'])) + timeout = self.opts["timeout"] + gather_job_timeout = int( + kwargs.get("gather_job_timeout", self.opts["gather_job_timeout"]) + ) start = int(time.time()) # timeouts per minion, id_ -> timeout time @@ -1069,33 +1069,44 @@ class LocalClient(object): missing = set() # Check to see if the jid is real, if not return the empty dict try: - if self.returners['{0}.get_load'.format(self.opts['master_job_cache'])](jid) == {}: - log.warning('jid does not exist') + if ( + self.returners["{0}.get_load".format(self.opts["master_job_cache"])]( + jid + ) + == {} + ): + log.warning("jid does not exist") yield {} # stop the iteration, since the jid is invalid raise StopIteration() except Exception as exc: # pylint: disable=broad-except - log.warning('Returner unavailable: %s', exc, exc_info_on_loglevel=logging.DEBUG) + log.warning( + "Returner unavailable: %s", exc, exc_info_on_loglevel=logging.DEBUG + ) # Wait for the hosts to check in last_time = False # iterator for this job's return - if self.opts['order_masters']: + if self.opts["order_masters"]: # If we are a MoM, we need to gather expected minions from downstreams masters. - ret_iter = self.get_returns_no_block('(salt/job|syndic/.*)/{0}'.format(jid), 'regex') + ret_iter = self.get_returns_no_block( + "(salt/job|syndic/.*)/{0}".format(jid), "regex" + ) else: - ret_iter = self.get_returns_no_block('salt/job/{0}'.format(jid)) + ret_iter = self.get_returns_no_block("salt/job/{0}".format(jid)) # iterator for the info of this job jinfo_iter = [] # open event jids that need to be un-subscribed from later open_jids = set() timeout_at = time.time() + timeout - gather_syndic_wait = time.time() + self.opts['syndic_wait'] + gather_syndic_wait = time.time() + self.opts["syndic_wait"] # are there still minions running the job out there # start as True so that we ping at least once minions_running = True log.debug( - 'get_iter_returns for jid %s sent to %s will timeout at %s', - jid, minions, datetime.fromtimestamp(timeout_at).time() + "get_iter_returns for jid %s sent to %s will timeout at %s", + jid, + minions, + datetime.fromtimestamp(timeout_at).time(), ) while True: # Process events until timeout is reached or all minions have returned @@ -1103,37 +1114,47 @@ class LocalClient(object): # if we got None, then there were no events if raw is None: break - if 'minions' in raw.get('data', {}): - minions.update(raw['data']['minions']) - if 'missing' in raw.get('data', {}): - missing.update(raw['data']['missing']) + if "minions" in raw.get("data", {}): + minions.update(raw["data"]["minions"]) + if "missing" in raw.get("data", {}): + missing.update(raw["data"]["missing"]) continue - if 'return' not in raw['data']: + if "return" not in raw["data"]: continue - if kwargs.get('raw', False): - found.add(raw['data']['id']) + if kwargs.get("raw", False): + found.add(raw["data"]["id"]) yield raw else: - found.add(raw['data']['id']) - ret = {raw['data']['id']: {'ret': raw['data']['return']}} - if 'out' in raw['data']: - ret[raw['data']['id']]['out'] = raw['data']['out'] - if 'retcode' in raw['data']: - ret[raw['data']['id']]['retcode'] = raw['data']['retcode'] - if 'jid' in raw['data']: - ret[raw['data']['id']]['jid'] = raw['data']['jid'] - if kwargs.get('_cmd_meta', False): - ret[raw['data']['id']].update(raw['data']) - log.debug('jid %s return from %s', jid, raw['data']['id']) + found.add(raw["data"]["id"]) + ret = {raw["data"]["id"]: {"ret": raw["data"]["return"]}} + if "out" in raw["data"]: + ret[raw["data"]["id"]]["out"] = raw["data"]["out"] + if "retcode" in raw["data"]: + ret[raw["data"]["id"]]["retcode"] = raw["data"]["retcode"] + if "jid" in raw["data"]: + ret[raw["data"]["id"]]["jid"] = raw["data"]["jid"] + if kwargs.get("_cmd_meta", False): + ret[raw["data"]["id"]].update(raw["data"]) + log.debug("jid %s return from %s", jid, raw["data"]["id"]) yield ret # if we have all of the returns (and we aren't a syndic), no need for anything fancy - if len(found.intersection(minions)) >= len(minions) and not self.opts['order_masters']: + if ( + len(found.intersection(minions)) >= len(minions) + and not self.opts["order_masters"] + ): # All minions have returned, break out of the loop - log.debug('jid %s found all minions %s', jid, found) + log.debug("jid %s found all minions %s", jid, found) break - elif len(found.intersection(minions)) >= len(minions) and self.opts['order_masters']: - if len(found) >= len(minions) and len(minions) > 0 and time.time() > gather_syndic_wait: + elif ( + len(found.intersection(minions)) >= len(minions) + and self.opts["order_masters"] + ): + if ( + len(found) >= len(minions) + and len(minions) > 0 + and time.time() > gather_syndic_wait + ): # There were some minions to find and we found them # However, this does not imply that *all* masters have yet responded with expected minion lists. # Therefore, continue to wait up to the syndic_wait period (calculated in gather_syndic_wait) to see @@ -1154,18 +1175,22 @@ class LocalClient(object): # re-do the ping if time.time() > timeout_at and minions_running: # since this is a new ping, no one has responded yet - jinfo = self.gather_job_info(jid, list(minions - found), 'list', **kwargs) + jinfo = self.gather_job_info( + jid, list(minions - found), "list", **kwargs + ) minions_running = False # if we weren't assigned any jid that means the master thinks # we have nothing to send - if 'jid' not in jinfo: + if "jid" not in jinfo: jinfo_iter = [] else: - jinfo_iter = self.get_returns_no_block('salt/job/{0}'.format(jinfo['jid'])) + jinfo_iter = self.get_returns_no_block( + "salt/job/{0}".format(jinfo["jid"]) + ) timeout_at = time.time() + gather_job_timeout # if you are a syndic, wait a little longer - if self.opts['order_masters']: - timeout_at += self.opts.get('syndic_wait', 1) + if self.opts["order_masters"]: + timeout_at += self.opts.get("syndic_wait", 1) # check for minions that are running the job still for raw in jinfo_iter: @@ -1173,55 +1198,60 @@ class LocalClient(object): if raw is None: break try: - if raw['data']['retcode'] > 0: - log.error('saltutil returning errors on minion %s', raw['data']['id']) - minions.remove(raw['data']['id']) + if raw["data"]["retcode"] > 0: + log.error( + "saltutil returning errors on minion %s", raw["data"]["id"] + ) + minions.remove(raw["data"]["id"]) break except KeyError as exc: # This is a safe pass. We're just using the try/except to # avoid having to deep-check for keys. - missing_key = exc.__str__().strip('\'"') - if missing_key == 'retcode': - log.debug('retcode missing from client return') + missing_key = exc.__str__().strip("'\"") + if missing_key == "retcode": + log.debug("retcode missing from client return") else: log.debug( - 'Passing on saltutil error. Key \'%s\' missing ' - 'from client return. This may be an error in ' - 'the client.', missing_key + "Passing on saltutil error. Key '%s' missing " + "from client return. This may be an error in " + "the client.", + missing_key, ) # Keep track of the jid events to unsubscribe from later - open_jids.add(jinfo['jid']) + open_jids.add(jinfo["jid"]) # TODO: move to a library?? - if 'minions' in raw.get('data', {}): - minions.update(raw['data']['minions']) + if "minions" in raw.get("data", {}): + minions.update(raw["data"]["minions"]) continue - if 'syndic' in raw.get('data', {}): - minions.update(raw['syndic']) + if "syndic" in raw.get("data", {}): + minions.update(raw["syndic"]) continue - if 'return' not in raw.get('data', {}): + if "return" not in raw.get("data", {}): continue # if the job isn't running there anymore... don't count - if raw['data']['return'] == {}: + if raw["data"]["return"] == {}: continue # if the minion throws an exception containing the word "return" # the master will try to handle the string as a dict in the next # step. Check if we have a string, log the issue and continue. - if isinstance(raw['data']['return'], six.string_types): + if isinstance(raw["data"]["return"], six.string_types): log.error("unexpected return from minion: %s", raw) continue - if 'return' in raw['data']['return'] and \ - raw['data']['return']['return'] == {}: + if ( + "return" in raw["data"]["return"] + and raw["data"]["return"]["return"] == {} + ): continue # if we didn't originally target the minion, lets add it to the list - if raw['data']['id'] not in minions: - minions.add(raw['data']['id']) + if raw["data"]["id"] not in minions: + minions.add(raw["data"]["id"]) # update this minion's timeout, as long as the job is still running - minion_timeouts[raw['data']['id']] = time.time() + timeout + minion_timeouts[raw["data"]["id"]] = time.time() + timeout # a minion returned, so we know its running somewhere minions_running = True @@ -1253,7 +1283,7 @@ class LocalClient(object): if expect_minions: for minion in list((minions - found)): - yield {minion: {'failed': True}} + yield {minion: {"failed": True}} # Filter out any minions marked as missing for which we received # returns (prevents false events sent due to higher-level masters not @@ -1263,69 +1293,73 @@ class LocalClient(object): # Report on missing minions if missing: for minion in missing: - yield {minion: {'failed': True}} + yield {minion: {"failed": True}} - def get_returns( - self, - jid, - minions, - timeout=None): - ''' + def get_returns(self, jid, minions, timeout=None): + """ Get the returns for the command line interface via the event system - ''' + """ minions = set(minions) if timeout is None: - timeout = self.opts['timeout'] + timeout = self.opts["timeout"] start = int(time.time()) timeout_at = start + timeout log.debug( - 'get_returns for jid %s sent to %s will timeout at %s', - jid, minions, datetime.fromtimestamp(timeout_at).time() + "get_returns for jid %s sent to %s will timeout at %s", + jid, + minions, + datetime.fromtimestamp(timeout_at).time(), ) found = set() ret = {} # Check to see if the jid is real, if not return the empty dict try: - if self.returners['{0}.get_load'.format(self.opts['master_job_cache'])](jid) == {}: - log.warning('jid does not exist') + if ( + self.returners["{0}.get_load".format(self.opts["master_job_cache"])]( + jid + ) + == {} + ): + log.warning("jid does not exist") return ret except Exception as exc: # pylint: disable=broad-except - raise SaltClientError('Master job cache returner [{0}] failed to verify jid. ' - 'Exception details: {1}'.format(self.opts['master_job_cache'], exc)) + raise SaltClientError( + "Master job cache returner [{0}] failed to verify jid. " + "Exception details: {1}".format(self.opts["master_job_cache"], exc) + ) # Wait for the hosts to check in while True: time_left = timeout_at - int(time.time()) wait = max(1, time_left) raw = self.event.get_event(wait, jid, auto_reconnect=self.auto_reconnect) - if raw is not None and 'return' in raw: - found.add(raw['id']) - ret[raw['id']] = raw['return'] + if raw is not None and "return" in raw: + found.add(raw["id"]) + ret[raw["id"]] = raw["return"] if len(found.intersection(minions)) >= len(minions): # All minions have returned, break out of the loop - log.debug('jid %s found all minions', jid) + log.debug("jid %s found all minions", jid) break continue # Then event system timeout was reached and nothing was returned if len(found.intersection(minions)) >= len(minions): # All minions have returned, break out of the loop - log.debug('jid %s found all minions', jid) + log.debug("jid %s found all minions", jid) break if int(time.time()) > timeout_at: log.info( - 'jid %s minions %s did not return in time', - jid, (minions - found) + "jid %s minions %s did not return in time", jid, (minions - found) ) break time.sleep(0.01) return ret def get_full_returns(self, jid, minions, timeout=None): - ''' + """ This method starts off a watcher looking at the return data for a specified jid, it returns all of the information for the jid - ''' + """ # TODO: change this from ret to return... or the other way. # Its inconsistent, we should pick one @@ -1334,20 +1368,22 @@ class LocalClient(object): event_iter = self.get_event_iter_returns(jid, minions, timeout=timeout) try: - data = self.returners['{0}.get_jid'.format(self.opts['master_job_cache'])](jid) + data = self.returners["{0}.get_jid".format(self.opts["master_job_cache"])]( + jid + ) except Exception as exc: # pylint: disable=broad-except - raise SaltClientError('Returner {0} could not fetch jid data. ' - 'Exception details: {1}'.format( - self.opts['master_job_cache'], - exc)) + raise SaltClientError( + "Returner {0} could not fetch jid data. " + "Exception details: {1}".format(self.opts["master_job_cache"], exc) + ) for minion in data: m_data = {} - if 'return' in data[minion]: - m_data['ret'] = data[minion].get('return') + if "return" in data[minion]: + m_data["ret"] = data[minion].get("return") else: - m_data['ret'] = data[minion].get('return') - if 'out' in data[minion]: - m_data['out'] = data[minion]['out'] + m_data["ret"] = data[minion].get("return") + if "out" in data[minion]: + m_data["out"] = data[minion]["out"] if minion in ret: ret[minion].update(m_data) else: @@ -1377,26 +1413,29 @@ class LocalClient(object): return ret def get_cache_returns(self, jid): - ''' + """ Execute a single pass to gather the contents of the job cache - ''' + """ ret = {} try: - data = self.returners['{0}.get_jid'.format(self.opts['master_job_cache'])](jid) + data = self.returners["{0}.get_jid".format(self.opts["master_job_cache"])]( + jid + ) except Exception as exc: # pylint: disable=broad-except - raise SaltClientError('Could not examine master job cache. ' - 'Error occurred in {0} returner. ' - 'Exception details: {1}'.format(self.opts['master_job_cache'], - exc)) + raise SaltClientError( + "Could not examine master job cache. " + "Error occurred in {0} returner. " + "Exception details: {1}".format(self.opts["master_job_cache"], exc) + ) for minion in data: m_data = {} - if 'return' in data[minion]: - m_data['ret'] = data[minion].get('return') + if "return" in data[minion]: + m_data["ret"] = data[minion].get("return") else: - m_data['ret'] = data[minion].get('return') - if 'out' in data[minion]: - m_data['out'] = data[minion]['out'] + m_data["ret"] = data[minion].get("return") + if "out" in data[minion]: + m_data["out"] = data[minion]["out"] if minion in ret: ret[minion].update(m_data) else: @@ -1405,29 +1444,30 @@ class LocalClient(object): return ret def get_cli_static_event_returns( - self, - jid, - minions, - timeout=None, - tgt='*', - tgt_type='glob', - verbose=False, - show_timeout=False, - show_jid=False): - ''' + self, + jid, + minions, + timeout=None, + tgt="*", + tgt_type="glob", + verbose=False, + show_timeout=False, + show_jid=False, + ): + """ Get the returns for the command line interface via the event system - ''' - log.trace('entered - function get_cli_static_event_returns()') + """ + log.trace("entered - function get_cli_static_event_returns()") minions = set(minions) if verbose: - msg = 'Executing job with jid {0}'.format(jid) + msg = "Executing job with jid {0}".format(jid) print(msg) - print('-' * len(msg) + '\n') + print("-" * len(msg) + "\n") elif show_jid: - print('jid: {0}'.format(jid)) + print("jid: {0}".format(jid)) if timeout is None: - timeout = self.opts['timeout'] + timeout = self.opts["timeout"] start = int(time.time()) timeout_at = start + timeout @@ -1435,31 +1475,40 @@ class LocalClient(object): ret = {} # Check to see if the jid is real, if not return the empty dict try: - if self.returners['{0}.get_load'.format(self.opts['master_job_cache'])](jid) == {}: - log.warning('jid does not exist') + if ( + self.returners["{0}.get_load".format(self.opts["master_job_cache"])]( + jid + ) + == {} + ): + log.warning("jid does not exist") return ret except Exception as exc: # pylint: disable=broad-except - raise SaltClientError('Load could not be retrieved from ' - 'returner {0}. Exception details: {1}'.format( - self.opts['master_job_cache'], - exc)) + raise SaltClientError( + "Load could not be retrieved from " + "returner {0}. Exception details: {1}".format( + self.opts["master_job_cache"], exc + ) + ) # Wait for the hosts to check in while True: # Process events until timeout is reached or all minions have returned time_left = timeout_at - int(time.time()) # Wait 0 == forever, use a minimum of 1s wait = max(1, time_left) - jid_tag = 'salt/job/{0}'.format(jid) - raw = self.event.get_event(wait, jid_tag, auto_reconnect=self.auto_reconnect) - if raw is not None and 'return' in raw: - if 'minions' in raw.get('data', {}): - minions.update(raw['data']['minions']) + jid_tag = "salt/job/{0}".format(jid) + raw = self.event.get_event( + wait, jid_tag, auto_reconnect=self.auto_reconnect + ) + if raw is not None and "return" in raw: + if "minions" in raw.get("data", {}): + minions.update(raw["data"]["minions"]) continue - found.add(raw['id']) - ret[raw['id']] = {'ret': raw['return']} - ret[raw['id']]['success'] = raw.get('success', False) - if 'out' in raw: - ret[raw['id']]['out'] = raw['out'] + found.add(raw["id"]) + ret[raw["id"]] = {"ret": raw["return"]} + ret[raw["id"]]["success"] = raw.get("success", False) + if "out" in raw: + ret[raw["id"]]["out"] = raw["out"] if len(found.intersection(minions)) >= len(minions): # All minions have returned, break out of the loop break @@ -1470,14 +1519,17 @@ class LocalClient(object): break if int(time.time()) > timeout_at: if verbose or show_timeout: - if self.opts.get('minion_data_cache', False) \ - or tgt_type in ('glob', 'pcre', 'list'): + if self.opts.get("minion_data_cache", False) or tgt_type in ( + "glob", + "pcre", + "list", + ): if len(found) < len(minions): fail = sorted(list(minions.difference(found))) for minion in fail: ret[minion] = { - 'out': 'no_return', - 'ret': 'Minion did not return' + "out": "no_return", + "ret": "Minion did not return", } break time.sleep(0.01) @@ -1486,75 +1538,90 @@ class LocalClient(object): return ret def get_cli_event_returns( - self, - jid, - minions, - timeout=None, - tgt='*', - tgt_type='glob', - verbose=False, - progress=False, - show_timeout=False, - show_jid=False, - **kwargs): - ''' + self, + jid, + minions, + timeout=None, + tgt="*", + tgt_type="glob", + verbose=False, + progress=False, + show_timeout=False, + show_jid=False, + **kwargs + ): + """ Get the returns for the command line interface via the event system - ''' - log.trace('func get_cli_event_returns()') + """ + log.trace("func get_cli_event_returns()") if verbose: - msg = 'Executing job with jid {0}'.format(jid) + msg = "Executing job with jid {0}".format(jid) print(msg) - print('-' * len(msg) + '\n') + print("-" * len(msg) + "\n") elif show_jid: - print('jid: {0}'.format(jid)) + print("jid: {0}".format(jid)) # lazy load the connected minions connected_minions = None return_count = 0 - for ret in self.get_iter_returns(jid, - minions, - timeout=timeout, - tgt=tgt, - tgt_type=tgt_type, - # (gtmanfred) expect_minions is popped here incase it is passed from a client - # call. If this is not popped, then it would be passed twice to - # get_iter_returns. - expect_minions=(kwargs.pop('expect_minions', False) or verbose or show_timeout), - **kwargs - ): - log.debug('return event: %s', ret) + for ret in self.get_iter_returns( + jid, + minions, + timeout=timeout, + tgt=tgt, + tgt_type=tgt_type, + # (gtmanfred) expect_minions is popped here incase it is passed from a client + # call. If this is not popped, then it would be passed twice to + # get_iter_returns. + expect_minions=( + kwargs.pop("expect_minions", False) or verbose or show_timeout + ), + **kwargs + ): + log.debug("return event: %s", ret) return_count = return_count + 1 if progress: for id_, min_ret in six.iteritems(ret): - if not min_ret.get('failed') is True: - yield {'minion_count': len(minions), 'return_count': return_count} + if not min_ret.get("failed") is True: + yield { + "minion_count": len(minions), + "return_count": return_count, + } # replace the return structure for missing minions for id_, min_ret in six.iteritems(ret): - if min_ret.get('failed') is True: + if min_ret.get("failed") is True: if connected_minions is None: - connected_minions = salt.utils.minions.CkMinions(self.opts).connected_ids() - if self.opts['minion_data_cache'] \ - and salt.cache.factory(self.opts).contains('minions/{0}'.format(id_), 'data') \ - and connected_minions \ - and id_ not in connected_minions: + connected_minions = salt.utils.minions.CkMinions( + self.opts + ).connected_ids() + if ( + self.opts["minion_data_cache"] + and salt.cache.factory(self.opts).contains( + "minions/{0}".format(id_), "data" + ) + and connected_minions + and id_ not in connected_minions + ): yield { id_: { - 'out': 'no_return', - 'ret': 'Minion did not return. [Not connected]', - 'retcode': salt.defaults.exitcodes.EX_GENERIC + "out": "no_return", + "ret": "Minion did not return. [Not connected]", + "retcode": salt.defaults.exitcodes.EX_GENERIC, } } else: # don't report syndics as unresponsive minions - if not os.path.exists(os.path.join(self.opts['syndic_dir'], id_)): + if not os.path.exists( + os.path.join(self.opts["syndic_dir"], id_) + ): yield { id_: { - 'out': 'no_return', - 'ret': 'Minion did not return. [No response]', - 'retcode': salt.defaults.exitcodes.EX_GENERIC + "out": "no_return", + "ret": "Minion did not return. [No response]", + "retcode": salt.defaults.exitcodes.EX_GENERIC, } } else: @@ -1563,20 +1630,23 @@ class LocalClient(object): self._clean_up_subscriptions(jid) def get_event_iter_returns(self, jid, minions, timeout=None): - ''' + """ Gather the return data from the event system, break hard when timeout is reached. - ''' - log.trace('entered - function get_event_iter_returns()') + """ + log.trace("entered - function get_event_iter_returns()") if timeout is None: - timeout = self.opts['timeout'] + timeout = self.opts["timeout"] timeout_at = time.time() + timeout found = set() # Check to see if the jid is real, if not return the empty dict - if self.returners['{0}.get_load'.format(self.opts['master_job_cache'])](jid) == {}: - log.warning('jid does not exist') + if ( + self.returners["{0}.get_load".format(self.opts["master_job_cache"])](jid) + == {} + ): + log.warning("jid does not exist") yield {} # stop the iteration, since the jid is invalid raise StopIteration() @@ -1586,102 +1656,117 @@ class LocalClient(object): if raw is None or time.time() > timeout_at: # Timeout reached break - if 'minions' in raw.get('data', {}): + if "minions" in raw.get("data", {}): continue try: # There might be two jobs for the same minion, so we have to check for the jid - if jid == raw['jid']: - found.add(raw['id']) - ret = {raw['id']: {'ret': raw['return']}} + if jid == raw["jid"]: + found.add(raw["id"]) + ret = {raw["id"]: {"ret": raw["return"]}} else: continue except KeyError: # Ignore other erroneous messages continue - if 'out' in raw: - ret[raw['id']]['out'] = raw['out'] + if "out" in raw: + ret[raw["id"]]["out"] = raw["out"] yield ret time.sleep(0.02) - def _prep_pub(self, - tgt, - fun, - arg, - tgt_type, - ret, - jid, - timeout, - **kwargs): - ''' + def _resolve_nodegroup(self, ng): + """ + Resolve a nodegroup into its configured components + """ + if ng not in self.opts["nodegroups"]: + conf_file = self.opts.get("conf_file", "the master config file") + raise SaltInvocationError( + "Node group {0} unavailable in {1}".format(ng, conf_file) + ) + return salt.utils.minions.nodegroup_comp(ng, self.opts["nodegroups"]) + + def _prep_pub(self, tgt, fun, arg, tgt_type, ret, jid, timeout, **kwargs): + """ Set up the payload_kwargs to be sent down to the master - ''' - if tgt_type == 'nodegroup': - if tgt not in self.opts['nodegroups']: - conf_file = self.opts.get( - 'conf_file', 'the master config file' - ) - raise SaltInvocationError( - 'Node group {0} unavailable in {1}'.format( - tgt, conf_file - ) - ) - tgt = salt.utils.minions.nodegroup_comp(tgt, - self.opts['nodegroups']) - tgt_type = 'compound' + """ + if tgt_type == "nodegroup": + tgt = self._resolve_nodegroup(tgt) + tgt_type = "compound" + + if tgt_type == "compound": + # Resolve all nodegroups, so that the minions don't have to. + new_tgt = list() + log.debug("compound resolution: original tgt: %s", tgt) + + if isinstance(tgt, six.string_types): + tgt = tgt.split() + + for word in tgt: + if word.startswith("N@") and len(word) > 2: + resolved = self._resolve_nodegroup(word[2:]) + new_tgt.extend(resolved) + else: + new_tgt.append(word) + + log.debug("compound resolution: new_tgt: %s", new_tgt) + tgt = " ".join(new_tgt) # Convert a range expression to a list of nodes and change expression # form to list - if tgt_type == 'range' and HAS_RANGE: + if tgt_type == "range" and HAS_RANGE: tgt = self._convert_range_to_list(tgt) - tgt_type = 'list' + tgt_type = "list" # If an external job cache is specified add it to the ret list - if self.opts.get('ext_job_cache'): + if self.opts.get("ext_job_cache"): if ret: - ret += ',{0}'.format(self.opts['ext_job_cache']) + ret += ",{0}".format(self.opts["ext_job_cache"]) else: - ret = self.opts['ext_job_cache'] + ret = self.opts["ext_job_cache"] # format the payload - make a function that does this in the payload # module # Generate the standard keyword args to feed to format_payload - payload_kwargs = {'cmd': 'publish', - 'tgt': tgt, - 'fun': fun, - 'arg': arg, - 'key': self.key, - 'tgt_type': tgt_type, - 'ret': ret, - 'jid': jid} + payload_kwargs = { + "cmd": "publish", + "tgt": tgt, + "fun": fun, + "arg": arg, + "key": self.key, + "tgt_type": tgt_type, + "ret": ret, + "jid": jid, + } # if kwargs are passed, pack them. if kwargs: - payload_kwargs['kwargs'] = kwargs + payload_kwargs["kwargs"] = kwargs # If we have a salt user, add it to the payload - if self.opts['syndic_master'] and 'user' in kwargs: - payload_kwargs['user'] = kwargs['user'] + if self.opts["syndic_master"] and "user" in kwargs: + payload_kwargs["user"] = kwargs["user"] elif self.salt_user: - payload_kwargs['user'] = self.salt_user + payload_kwargs["user"] = self.salt_user # If we're a syndication master, pass the timeout - if self.opts['order_masters']: - payload_kwargs['to'] = timeout + if self.opts["order_masters"]: + payload_kwargs["to"] = timeout return payload_kwargs - def pub(self, - tgt, - fun, - arg=(), - tgt_type='glob', - ret='', - jid='', - timeout=5, - listen=False, - **kwargs): - ''' + def pub( + self, + tgt, + fun, + arg=(), + tgt_type="glob", + ret="", + jid="", + timeout=5, + listen=False, + **kwargs + ): + """ Take the required arguments and publish the given command. Arguments: tgt: @@ -1701,35 +1786,29 @@ class LocalClient(object): id, this will inform the client where to get the job results minions: A set, the targets that the tgt passed should match. - ''' + """ # Make sure the publisher is running by checking the unix socket - if (self.opts.get('ipc_mode', '') != 'tcp' and - not os.path.exists(os.path.join(self.opts['sock_dir'], - 'publish_pull.ipc'))): + if self.opts.get("ipc_mode", "") != "tcp" and not os.path.exists( + os.path.join(self.opts["sock_dir"], "publish_pull.ipc") + ): log.error( - 'Unable to connect to the salt master publisher at %s', - self.opts['sock_dir'] + "Unable to connect to the salt master publisher at %s", + self.opts["sock_dir"], ) raise SaltClientError payload_kwargs = self._prep_pub( - tgt, - fun, - arg, - tgt_type, - ret, - jid, - timeout, - **kwargs) - - master_uri = 'tcp://{}:{}'.format( - salt.utils.zeromq.ip_bracket(self.opts['interface']), - six.text_type(self.opts['ret_port']) + tgt, fun, arg, tgt_type, ret, jid, timeout, **kwargs ) - with salt.transport.client.ReqChannel.factory(self.opts, - crypt='clear', - master_uri=master_uri) as channel: + master_uri = "tcp://{}:{}".format( + salt.utils.zeromq.ip_bracket(self.opts["interface"]), + six.text_type(self.opts["ret_port"]), + ) + + with salt.transport.client.ReqChannel.factory( + self.opts, crypt="clear", master_uri=master_uri + ) as channel: try: # Ensure that the event subscriber is connected. # If not, we won't get a response, so error out @@ -1739,13 +1818,13 @@ class LocalClient(object): except SaltReqTimeoutError as err: log.error(err) raise SaltReqTimeoutError( - 'Salt request timed out. The master is not responding. You ' - 'may need to run your command with `--async` in order to ' - 'bypass the congested event bus. With `--async`, the CLI tool ' - 'will print the job id (jid) and exit immediately without ' - 'listening for responses. You can then use ' - '`salt-run jobs.lookup_jid` to look up the results of the job ' - 'in the job cache later.' + "Salt request timed out. The master is not responding. You " + "may need to run your command with `--async` in order to " + "bypass the congested event bus. With `--async`, the CLI tool " + "will print the job id (jid) and exit immediately without " + "listening for responses. You can then use " + "`salt-run jobs.lookup_jid` to look up the results of the job " + "in the job cache later." ) if not payload: @@ -1755,17 +1834,17 @@ class LocalClient(object): if key == self.key: return payload self.key = key - payload_kwargs['key'] = self.key + payload_kwargs["key"] = self.key payload = channel.send(payload_kwargs) - error = payload.pop('error', None) + error = payload.pop("error", None) if error is not None: if isinstance(error, dict): - err_name = error.get('name', '') - err_msg = error.get('message', '') - if err_name == 'AuthenticationError': + err_name = error.get("name", "") + err_msg = error.get("message", "") + if err_name == "AuthenticationError": raise AuthenticationError(err_msg) - elif err_name == 'AuthorizationError': + elif err_name == "AuthorizationError": raise AuthorizationError(err_msg) raise PublishError(error) @@ -1773,22 +1852,23 @@ class LocalClient(object): if not payload: return payload - return {'jid': payload['load']['jid'], - 'minions': payload['load']['minions']} + return {"jid": payload["load"]["jid"], "minions": payload["load"]["minions"]} @salt.ext.tornado.gen.coroutine - def pub_async(self, - tgt, - fun, - arg=(), - tgt_type='glob', - ret='', - jid='', - timeout=5, - io_loop=None, - listen=True, - **kwargs): - ''' + def pub_async( + self, + tgt, + fun, + arg=(), + tgt_type="glob", + ret="", + jid="", + timeout=5, + io_loop=None, + listen=True, + **kwargs + ): + """ Take the required arguments and publish the given command. Arguments: tgt: @@ -1808,34 +1888,31 @@ class LocalClient(object): id, this will inform the client where to get the job results minions: A set, the targets that the tgt passed should match. - ''' + """ # Make sure the publisher is running by checking the unix socket - if (self.opts.get('ipc_mode', '') != 'tcp' and - not os.path.exists(os.path.join(self.opts['sock_dir'], - 'publish_pull.ipc'))): + if self.opts.get("ipc_mode", "") != "tcp" and not os.path.exists( + os.path.join(self.opts["sock_dir"], "publish_pull.ipc") + ): log.error( - 'Unable to connect to the salt master publisher at %s', - self.opts['sock_dir'] + "Unable to connect to the salt master publisher at %s", + self.opts["sock_dir"], ) raise SaltClientError payload_kwargs = self._prep_pub( - tgt, - fun, - arg, - tgt_type, - ret, - jid, - timeout, - **kwargs) + tgt, fun, arg, tgt_type, ret, jid, timeout, **kwargs + ) - master_uri = 'tcp://' + salt.utils.zeromq.ip_bracket(self.opts['interface']) + \ - ':' + six.text_type(self.opts['ret_port']) + master_uri = ( + "tcp://" + + salt.utils.zeromq.ip_bracket(self.opts["interface"]) + + ":" + + six.text_type(self.opts["ret_port"]) + ) - with salt.transport.client.AsyncReqChannel.factory(self.opts, - io_loop=io_loop, - crypt='clear', - master_uri=master_uri) as channel: + with salt.transport.client.AsyncReqChannel.factory( + self.opts, io_loop=io_loop, crypt="clear", master_uri=master_uri + ) as channel: try: # Ensure that the event subscriber is connected. # If not, we won't get a response, so error out @@ -1844,13 +1921,13 @@ class LocalClient(object): payload = yield channel.send(payload_kwargs, timeout=timeout) except SaltReqTimeoutError: raise SaltReqTimeoutError( - 'Salt request timed out. The master is not responding. You ' - 'may need to run your command with `--async` in order to ' - 'bypass the congested event bus. With `--async`, the CLI tool ' - 'will print the job id (jid) and exit immediately without ' - 'listening for responses. You can then use ' - '`salt-run jobs.lookup_jid` to look up the results of the job ' - 'in the job cache later.' + "Salt request timed out. The master is not responding. You " + "may need to run your command with `--async` in order to " + "bypass the congested event bus. With `--async`, the CLI tool " + "will print the job id (jid) and exit immediately without " + "listening for responses. You can then use " + "`salt-run jobs.lookup_jid` to look up the results of the job " + "in the job cache later." ) if not payload: @@ -1860,17 +1937,17 @@ class LocalClient(object): if key == self.key: raise salt.ext.tornado.gen.Return(payload) self.key = key - payload_kwargs['key'] = self.key + payload_kwargs["key"] = self.key payload = yield channel.send(payload_kwargs) - error = payload.pop('error', None) + error = payload.pop("error", None) if error is not None: if isinstance(error, dict): - err_name = error.get('name', '') - err_msg = error.get('message', '') - if err_name == 'AuthenticationError': + err_name = error.get("name", "") + err_msg = error.get("message", "") + if err_name == "AuthenticationError": raise AuthenticationError(err_msg) - elif err_name == 'AuthorizationError': + elif err_name == "AuthorizationError": raise AuthorizationError(err_msg) raise PublishError(error) @@ -1878,74 +1955,80 @@ class LocalClient(object): if not payload: raise salt.ext.tornado.gen.Return(payload) - raise salt.ext.tornado.gen.Return({'jid': payload['load']['jid'], - 'minions': payload['load']['minions']}) + raise salt.ext.tornado.gen.Return( + {"jid": payload["load"]["jid"], "minions": payload["load"]["minions"]} + ) # pylint: disable=W1701 def __del__(self): # This IS really necessary! # When running tests, if self.events is not destroyed, we leak 2 # threads per test case which uses self.client - if hasattr(self, 'event'): + if hasattr(self, "event"): # The call below will take care of calling 'self.event.destroy()' del self.event + # pylint: enable=W1701 def _clean_up_subscriptions(self, job_id): - if self.opts.get('order_masters'): - self.event.unsubscribe('syndic/.*/{0}'.format(job_id), 'regex') - self.event.unsubscribe('salt/job/{0}'.format(job_id)) + if self.opts.get("order_masters"): + self.event.unsubscribe("syndic/.*/{0}".format(job_id), "regex") + self.event.unsubscribe("salt/job/{0}".format(job_id)) class FunctionWrapper(dict): - ''' + """ Create a function wrapper that looks like the functions dict on the minion but invoked commands on the minion via a LocalClient. This allows SLS files to be loaded with an object that calls down to the minion when the salt functions dict is referenced. - ''' + """ + def __init__(self, opts, minion): super(FunctionWrapper, self).__init__() self.opts = opts self.minion = minion - self.local = LocalClient(self.opts['conf_file']) + self.local = LocalClient(self.opts["conf_file"]) self.functions = self.__load_functions() def __missing__(self, key): - ''' + """ Since the function key is missing, wrap this call to a command to the minion of said key if it is available in the self.functions set - ''' + """ if key not in self.functions: raise KeyError return self.run_key(key) def __load_functions(self): - ''' + """ Find out what functions are available on the minion - ''' - return set(self.local.cmd(self.minion, - 'sys.list_functions').get(self.minion, [])) + """ + return set( + self.local.cmd(self.minion, "sys.list_functions").get(self.minion, []) + ) def run_key(self, key): - ''' + """ Return a function that executes the arguments passed via the local client - ''' + """ + def func(*args, **kwargs): - ''' + """ Run a remote call - ''' + """ args = list(args) for _key, _val in kwargs.items(): - args.append('{0}={1}'.format(_key, _val)) + args.append("{0}={1}".format(_key, _val)) return self.local.cmd(self.minion, key, args) + return func class Caller(object): - ''' + """ ``Caller`` is the same interface used by the :command:`salt-call` command-line tool on the Salt Minion. @@ -1981,11 +2064,12 @@ class Caller(object): __opts__ = salt.config.minion_config('/etc/salt/minion') __opts__['file_client'] = 'local' caller = salt.client.Caller(mopts=__opts__) - ''' - def __init__(self, c_path=os.path.join(syspaths.CONFIG_DIR, 'minion'), - mopts=None): + """ + + def __init__(self, c_path=os.path.join(syspaths.CONFIG_DIR, "minion"), mopts=None): # Late-import of the minion module to keep the CLI as light as possible import salt.minion + if mopts: self.opts = mopts else: @@ -1993,7 +2077,7 @@ class Caller(object): self.sminion = salt.minion.SMinion(self.opts) def cmd(self, fun, *args, **kwargs): - ''' + """ Call an execution module with the given arguments and keyword arguments .. versionchanged:: 2015.8.0 @@ -2007,23 +2091,22 @@ class Caller(object): caller.cmd('event.send', 'myco/myevent/something', data={'foo': 'Foo'}, with_env=['GIT_COMMIT'], with_grains=True) - ''' + """ return self.sminion.functions[fun](*args, **kwargs) def function(self, fun, *args, **kwargs): - ''' + """ Call a single salt function - ''' + """ func = self.sminion.functions[fun] args, kwargs = salt.minion.load_args_and_kwargs( - func, - salt.utils.args.parse_input(args), - kwargs) + func, salt.utils.args.parse_input(args), kwargs + ) return func(*args, **kwargs) class ProxyCaller(object): - ''' + """ ``ProxyCaller`` is the same interface used by the :command:`salt-call` with the args ``--proxyid `` command-line tool on the Salt Proxy Minion. @@ -2059,15 +2142,17 @@ class ProxyCaller(object): ` requires that ``--proxyid`` be an argument on the commandline for the script this is used in, or that the string ``proxy`` is in the name of the script. - ''' - def __init__(self, c_path=os.path.join(syspaths.CONFIG_DIR, 'proxy'), mopts=None): + """ + + def __init__(self, c_path=os.path.join(syspaths.CONFIG_DIR, "proxy"), mopts=None): # Late-import of the minion module to keep the CLI as light as possible import salt.minion + self.opts = mopts or salt.config.proxy_config(c_path) self.sminion = salt.minion.SProxyMinion(self.opts) def cmd(self, fun, *args, **kwargs): - ''' + """ Call an execution module with the given arguments and keyword arguments .. code-block:: python @@ -2076,22 +2161,24 @@ class ProxyCaller(object): caller.cmd('event.send', 'myco/myevent/something', data={'foo': 'Foo'}, with_env=['GIT_COMMIT'], with_grains=True) - ''' + """ func = self.sminion.functions[fun] - data = { - 'arg': args, - 'fun': fun - } + data = {"arg": args, "fun": fun} data.update(kwargs) - executors = getattr(self.sminion, 'module_executors', []) or \ - self.opts.get('module_executors', ['direct_call']) + executors = getattr(self.sminion, "module_executors", []) or self.opts.get( + "module_executors", ["direct_call"] + ) if isinstance(executors, six.string_types): executors = [executors] for name in executors: - fname = '{0}.execute'.format(name) + fname = "{0}.execute".format(name) if fname not in self.sminion.executors: - raise SaltInvocationError("Executor '{0}' is not available".format(name)) - return_data = self.sminion.executors[fname](self.opts, data, func, args, kwargs) + raise SaltInvocationError( + "Executor '{0}' is not available".format(name) + ) + return_data = self.sminion.executors[fname]( + self.opts, data, func, args, kwargs + ) if return_data is not None: break return return_data diff --git a/salt/client/api.py b/salt/client/api.py index abcce42f320..b25668d0730 100644 --- a/salt/client/api.py +++ b/salt/client/api.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This module provides the point of entry for client applications to interface to salt. The purpose is to have a simplified consistent interface for various client applications. @@ -13,62 +13,65 @@ client applications. http://docs.saltstack.com/ref/clients/index.html -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os +import salt.auth +import salt.client + # Import Salt libs import salt.config -import salt.auth -import salt.client import salt.runner -import salt.wheel +import salt.syspaths as syspaths import salt.utils.args import salt.utils.event -import salt.syspaths as syspaths +import salt.wheel from salt.exceptions import EauthAuthenticationError def tokenify(cmd, token=None): - ''' + """ If token is not None Then assign token to 'token' key of cmd dict and return cmd Otherwise return cmd - ''' + """ if token is not None: - cmd['token'] = token + cmd["token"] = token return cmd class APIClient(object): - ''' + """ Provide a uniform method of accessing the various client interfaces in Salt in the form of low-data data structures. For example: - ''' + """ + def __init__(self, opts=None, listen=True): if not opts: opts = salt.config.client_config( os.environ.get( - 'SALT_MASTER_CONFIG', - os.path.join(syspaths.CONFIG_DIR, 'master') + "SALT_MASTER_CONFIG", os.path.join(syspaths.CONFIG_DIR, "master") ) ) self.opts = opts - self.localClient = salt.client.get_local_client(self.opts['conf_file']) + self.localClient = salt.client.get_local_client(self.opts["conf_file"]) self.runnerClient = salt.runner.RunnerClient(self.opts) self.wheelClient = salt.wheel.Wheel(self.opts) self.resolver = salt.auth.Resolver(self.opts) self.event = salt.utils.event.get_event( - 'master', - self.opts['sock_dir'], - self.opts['transport'], - opts=self.opts, - listen=listen) + "master", + self.opts["sock_dir"], + self.opts["transport"], + opts=self.opts, + listen=listen, + ) def run(self, cmd): - ''' + """ Execute the salt command given by cmd dict. cmd is a dictionary of the following form: @@ -117,65 +120,67 @@ class APIClient(object): password: the user's password. Required if token is missing. eauth: the authentication type such as 'pam' or 'ldap'. Required if token is missing - ''' + """ cmd = dict(cmd) # make copy - client = 'minion' # default to local minion client - mode = cmd.get('mode', 'async') + client = "minion" # default to local minion client + mode = cmd.get("mode", "async") # check for wheel or runner prefix to fun name to use wheel or runner client - funparts = cmd.get('fun', '').split('.') - if len(funparts) > 2 and funparts[0] in ['wheel', 'runner']: # master + funparts = cmd.get("fun", "").split(".") + if len(funparts) > 2 and funparts[0] in ["wheel", "runner"]: # master client = funparts[0] - cmd['fun'] = '.'.join(funparts[1:]) # strip prefix + cmd["fun"] = ".".join(funparts[1:]) # strip prefix - if not ('token' in cmd or - ('eauth' in cmd and 'password' in cmd and 'username' in cmd)): - raise EauthAuthenticationError('No authentication credentials given') + if not ( + "token" in cmd + or ("eauth" in cmd and "password" in cmd and "username" in cmd) + ): + raise EauthAuthenticationError("No authentication credentials given") - executor = getattr(self, '{0}_{1}'.format(client, mode)) + executor = getattr(self, "{0}_{1}".format(client, mode)) result = executor(**cmd) return result def minion_async(self, **kwargs): - ''' + """ Wrap LocalClient for running :ref:`execution modules ` and immediately return the job ID. The results of the job can then be retrieved at a later time. .. seealso:: :ref:`python-api` - ''' + """ return self.localClient.run_job(**kwargs) def minion_sync(self, **kwargs): - ''' + """ Wrap LocalClient for running :ref:`execution modules ` .. seealso:: :ref:`python-api` - ''' + """ return self.localClient.cmd(**kwargs) def runner_async(self, **kwargs): - ''' + """ Wrap RunnerClient for executing :ref:`runner modules ` Expects that one of the kwargs is key 'fun' whose value is the namestring of the function to call - ''' + """ return self.runnerClient.master_call(**kwargs) runner_sync = runner_async # always runner asynchronous, so works in either mode def wheel_sync(self, **kwargs): - ''' + """ Wrap Wheel to enable executing :ref:`wheel modules ` Expects that one of the kwargs is key 'fun' whose value is the namestring of the function to call - ''' + """ return self.wheelClient.master_call(**kwargs) wheel_async = wheel_sync # always wheel_sync, so it works either mode def signature(self, cmd): - ''' + """ Convenience function that returns dict of function signature(s) specified by cmd. cmd is dict of the form: @@ -204,37 +209,40 @@ class APIClient(object): eauth: the authentication type such as 'pam' or 'ldap'. Required if token is missing Adds client per the command. - ''' - cmd['client'] = 'minion' - if len(cmd['module'].split('.')) > 2 and cmd['module'].split('.')[0] in ['runner', 'wheel']: - cmd['client'] = 'master' + """ + cmd["client"] = "minion" + if len(cmd["module"].split(".")) > 2 and cmd["module"].split(".")[0] in [ + "runner", + "wheel", + ]: + cmd["client"] = "master" return self._signature(cmd) def _signature(self, cmd): - ''' + """ Expects everything that signature does and also a client type string. client can either be master or minion. - ''' + """ result = {} - client = cmd.get('client', 'minion') - if client == 'minion': - cmd['fun'] = 'sys.argspec' - cmd['kwarg'] = dict(module=cmd['module']) + client = cmd.get("client", "minion") + if client == "minion": + cmd["fun"] = "sys.argspec" + cmd["kwarg"] = dict(module=cmd["module"]) result = self.run(cmd) - elif client == 'master': - parts = cmd['module'].split('.') + elif client == "master": + parts = cmd["module"].split(".") client = parts[0] - module = '.'.join(parts[1:]) # strip prefix - if client == 'wheel': + module = ".".join(parts[1:]) # strip prefix + if client == "wheel": functions = self.wheelClient.functions - elif client == 'runner': + elif client == "runner": functions = self.runnerClient.functions - result = {'master': salt.utils.args.argspec_report(functions, module)} + result = {"master": salt.utils.args.argspec_report(functions, module)} return result def create_token(self, creds): - ''' + """ Create token with creds. Token authorizes salt access if successful authentication with the credentials in creds. @@ -270,57 +278,61 @@ class APIClient(object): "test.*" ] - ''' + """ try: tokenage = self.resolver.mk_token(creds) except Exception as ex: # pylint: disable=broad-except raise EauthAuthenticationError( - "Authentication failed with {0}.".format(repr(ex))) + "Authentication failed with {0}.".format(repr(ex)) + ) - if 'token' not in tokenage: - raise EauthAuthenticationError("Authentication failed with provided credentials.") + if "token" not in tokenage: + raise EauthAuthenticationError( + "Authentication failed with provided credentials." + ) # Grab eauth config for the current backend for the current user - tokenage_eauth = self.opts['external_auth'][tokenage['eauth']] - if tokenage['name'] in tokenage_eauth: - tokenage['perms'] = tokenage_eauth[tokenage['name']] + tokenage_eauth = self.opts["external_auth"][tokenage["eauth"]] + if tokenage["name"] in tokenage_eauth: + tokenage["perms"] = tokenage_eauth[tokenage["name"]] else: - tokenage['perms'] = tokenage_eauth['*'] + tokenage["perms"] = tokenage_eauth["*"] - tokenage['user'] = tokenage['name'] - tokenage['username'] = tokenage['name'] + tokenage["user"] = tokenage["name"] + tokenage["username"] = tokenage["name"] return tokenage def verify_token(self, token): - ''' + """ If token is valid Then returns user name associated with token Else False. - ''' + """ try: result = self.resolver.get_token(token) except Exception as ex: # pylint: disable=broad-except raise EauthAuthenticationError( - "Token validation failed with {0}.".format(repr(ex))) + "Token validation failed with {0}.".format(repr(ex)) + ) return result - def get_event(self, wait=0.25, tag='', full=False): - ''' + def get_event(self, wait=0.25, tag="", full=False): + """ Get a single salt event. If no events are available, then block for up to ``wait`` seconds. Return the event if it matches the tag (or ``tag`` is empty) Otherwise return None If wait is 0 then block forever or until next event becomes available. - ''' + """ return self.event.get_event(wait=wait, tag=tag, full=full, auto_reconnect=True) def fire_event(self, data, tag): - ''' + """ fires event with data and tag This only works if api is running with same user permissions as master Need to convert this to a master call with appropriate authentication - ''' - return self.event.fire_event(data, salt.utils.event.tagify(tag, 'wui')) + """ + return self.event.fire_event(data, salt.utils.event.tagify(tag, "wui")) diff --git a/salt/client/mixins.py b/salt/client/mixins.py index 131aa1e3de6..9b36dae61a3 100644 --- a/salt/client/mixins.py +++ b/salt/client/mixins.py @@ -1,21 +1,27 @@ # coding: utf-8 -''' +""" A collection of mixins useful for the various *Client interfaces -''' +""" # Import Python libs -from __future__ import absolute_import, print_function, with_statement, unicode_literals -import fnmatch -import signal -import logging -import weakref -import traceback +from __future__ import absolute_import, print_function, unicode_literals, with_statement + import collections import copy as pycopy +import fnmatch +import logging +import signal +import traceback +import weakref # Import Salt libs import salt.exceptions + +# Import 3rd-party libs +import salt.ext.tornado.stack_context +import salt.log.setup import salt.minion +import salt.transport.client import salt.utils.args import salt.utils.doc import salt.utils.error @@ -28,44 +34,42 @@ import salt.utils.process import salt.utils.state import salt.utils.user import salt.utils.versions -import salt.transport.client -import salt.log.setup from salt.ext import six -# Import 3rd-party libs -import salt.ext.tornado.stack_context - log = logging.getLogger(__name__) -CLIENT_INTERNAL_KEYWORDS = frozenset([ - 'client', - 'cmd', - 'eauth', - 'fun', - 'kwarg', - 'match', - 'token', - '__jid__', - '__tag__', - '__user__', - 'username', - 'password', - 'full_return', - 'print_event' -]) +CLIENT_INTERNAL_KEYWORDS = frozenset( + [ + "client", + "cmd", + "eauth", + "fun", + "kwarg", + "match", + "token", + "__jid__", + "__tag__", + "__user__", + "username", + "password", + "full_return", + "print_event", + ] +) class ClientFuncsDict(collections.MutableMapping): - ''' + """ Class to make a read-only dict for accessing runner funcs "directly" - ''' + """ + def __init__(self, client): self.client = client def __getattr__(self, attr): - ''' + """ Provide access eg. to 'pack' - ''' + """ return getattr(self.client.functions, attr) def __setitem__(self, key, val): @@ -75,38 +79,40 @@ class ClientFuncsDict(collections.MutableMapping): raise NotImplementedError() def __getitem__(self, key): - ''' + """ Return a function that you can call with regular func params, but will do all the _proc_function magic - ''' + """ if key not in self.client.functions: raise KeyError def wrapper(*args, **kwargs): - low = {'fun': key, - 'args': args, - 'kwargs': kwargs, - } + low = { + "fun": key, + "args": args, + "kwargs": kwargs, + } pub_data = {} # Copy kwargs keys so we can iterate over and pop the pub data kwargs_keys = list(kwargs) # pull out pub_data if you have it for kwargs_key in kwargs_keys: - if kwargs_key.startswith('__pub_'): + if kwargs_key.startswith("__pub_"): pub_data[kwargs_key] = kwargs.pop(kwargs_key) - async_pub = self.client._gen_async_pub(pub_data.get('__pub_jid')) + async_pub = self.client._gen_async_pub(pub_data.get("__pub_jid")) user = salt.utils.user.get_specific_user() return self.client._proc_function( key, low, user, - async_pub['tag'], # TODO: fix - async_pub['jid'], # TODO: fix + async_pub["tag"], # TODO: fix + async_pub["jid"], # TODO: fix False, # Don't daemonize ) + return wrapper def __len__(self): @@ -117,37 +123,38 @@ class ClientFuncsDict(collections.MutableMapping): class SyncClientMixin(object): - ''' + """ A mixin for *Client interfaces to abstract common function execution - ''' + """ + functions = () def functions_dict(self): - ''' + """ Return a dict that will mimic the "functions" dict used all over salt. It creates a wrapper around the function allowing **kwargs, and if pub_data is passed in as kwargs, will re-use the JID passed in - ''' + """ return ClientFuncsDict(self) def master_call(self, **kwargs): - ''' + """ Execute a function through the master network interface. - ''' + """ load = kwargs - load['cmd'] = self.client + load["cmd"] = self.client - with salt.transport.client.ReqChannel.factory(self.opts, - crypt='clear', - usage='master_call') as channel: + with salt.transport.client.ReqChannel.factory( + self.opts, crypt="clear", usage="master_call" + ) as channel: ret = channel.send(load) if isinstance(ret, collections.Mapping): - if 'error' in ret: - salt.utils.error.raise_error(**ret['error']) + if "error" in ret: + salt.utils.error.raise_error(**ret["error"]) return ret def cmd_sync(self, low, timeout=None, full_return=False): - ''' + """ Execute a runner function synchronously; eauth is respected This function requires that :conf_master:`external_auth` is configured @@ -161,23 +168,36 @@ class SyncClientMixin(object): 'password': 'saltdev', 'eauth': 'pam', }) - ''' - with salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=True) as event: + """ + with salt.utils.event.get_master_event( + self.opts, self.opts["sock_dir"], listen=True + ) as event: job = self.master_call(**low) - ret_tag = salt.utils.event.tagify('ret', base=job['tag']) + ret_tag = salt.utils.event.tagify("ret", base=job["tag"]) if timeout is None: - timeout = self.opts.get('rest_timeout', 300) - ret = event.get_event(tag=ret_tag, full=True, wait=timeout, auto_reconnect=True) + timeout = self.opts.get("rest_timeout", 300) + ret = event.get_event( + tag=ret_tag, full=True, wait=timeout, auto_reconnect=True + ) if ret is None: raise salt.exceptions.SaltClientTimeout( - "RunnerClient job '{0}' timed out".format(job['jid']), - jid=job['jid']) + "RunnerClient job '{0}' timed out".format(job["jid"]), + jid=job["jid"], + ) - return ret if full_return else ret['data']['return'] + return ret if full_return else ret["data"]["return"] - def cmd(self, fun, arg=None, pub_data=None, kwarg=None, print_event=True, full_return=False): - ''' + def cmd( + self, + fun, + arg=None, + pub_data=None, + kwarg=None, + print_event=True, + full_return=False, + ): + """ Execute a function .. code-block:: python @@ -204,72 +224,71 @@ class SyncClientMixin(object): }, } - ''' + """ if arg is None: arg = tuple() if not isinstance(arg, list) and not isinstance(arg, tuple): raise salt.exceptions.SaltInvocationError( - 'arg must be formatted as a list/tuple' + "arg must be formatted as a list/tuple" ) if pub_data is None: pub_data = {} if not isinstance(pub_data, dict): raise salt.exceptions.SaltInvocationError( - 'pub_data must be formatted as a dictionary' + "pub_data must be formatted as a dictionary" ) if kwarg is None: kwarg = {} if not isinstance(kwarg, dict): raise salt.exceptions.SaltInvocationError( - 'kwarg must be formatted as a dictionary' + "kwarg must be formatted as a dictionary" ) arglist = salt.utils.args.parse_input( - arg, - no_parse=self.opts.get('no_parse', [])) + arg, no_parse=self.opts.get("no_parse", []) + ) # if you were passed kwarg, add it to arglist if kwarg: - kwarg['__kwarg__'] = True + kwarg["__kwarg__"] = True arglist.append(kwarg) args, kwargs = salt.minion.load_args_and_kwargs( self.functions[fun], arglist, pub_data ) - low = {'fun': fun, - 'arg': args, - 'kwarg': kwargs} + low = {"fun": fun, "arg": args, "kwarg": kwargs} return self.low(fun, low, print_event=print_event, full_return=full_return) @property def mminion(self): - if not hasattr(self, '_mminion'): - self._mminion = salt.minion.MasterMinion(self.opts, states=False, rend=False) + if not hasattr(self, "_mminion"): + self._mminion = salt.minion.MasterMinion( + self.opts, states=False, rend=False + ) return self._mminion @property def store_job(self): - ''' + """ Helper that allows us to turn off storing jobs for different classes that may incorporate this mixin. - ''' + """ try: class_name = self.__class__.__name__.lower() except AttributeError: log.warning( - 'Unable to determine class name', - exc_info_on_loglevel=logging.DEBUG + "Unable to determine class name", exc_info_on_loglevel=logging.DEBUG ) return True try: - return self.opts['{0}_returns'.format(class_name)] + return self.opts["{0}_returns".format(class_name)] except KeyError: # No such option, assume this isn't one we care about gating and # just return True. return True def low(self, fun, low, print_event=True, full_return=False): - ''' + """ Execute a function from low data Low data includes: required: @@ -280,23 +299,24 @@ class SyncClientMixin(object): - __user__: user who is running the command - __jid__: jid to run under - __tag__: tag to run under - ''' + """ # fire the mminion loading (if not already done) here # this is not to clutter the output with the module loading # if we have a high debug level. self.mminion # pylint: disable=W0104 - jid = low.get('__jid__', salt.utils.jid.gen_jid(self.opts)) - tag = low.get('__tag__', salt.utils.event.tagify(jid, prefix=self.tag_prefix)) + jid = low.get("__jid__", salt.utils.jid.gen_jid(self.opts)) + tag = low.get("__tag__", salt.utils.event.tagify(jid, prefix=self.tag_prefix)) - data = {'fun': '{0}.{1}'.format(self.client, fun), - 'jid': jid, - 'user': low.get('__user__', 'UNKNOWN'), - } + data = { + "fun": "{0}.{1}".format(self.client, fun), + "jid": jid, + "user": low.get("__user__", "UNKNOWN"), + } if print_event: - print_func = self.print_async_event \ - if hasattr(self, 'print_async_event') \ - else None + print_func = ( + self.print_async_event if hasattr(self, "print_async_event") else None + ) else: # Suppress printing of return event (this keeps us from printing # runner/wheel output during orchestration). @@ -304,25 +324,26 @@ class SyncClientMixin(object): with salt.utils.event.NamespacedEvent( salt.utils.event.get_event( - 'master', - self.opts['sock_dir'], - self.opts['transport'], + "master", + self.opts["sock_dir"], + self.opts["transport"], opts=self.opts, listen=False, ), tag, - print_func=print_func + print_func=print_func, ) as namespaced_event: # TODO: test that they exist # TODO: Other things to inject?? - func_globals = {'__jid__': jid, - '__user__': data['user'], - '__tag__': tag, - # weak ref to avoid the Exception in interpreter - # teardown of event - '__jid_event__': weakref.proxy(namespaced_event), - } + func_globals = { + "__jid__": jid, + "__user__": data["user"], + "__tag__": tag, + # weak ref to avoid the Exception in interpreter + # teardown of event + "__jid_event__": weakref.proxy(namespaced_event), + } try: self_functions = pycopy.copy(self.functions) @@ -333,9 +354,9 @@ class SyncClientMixin(object): completed_funcs = [] for mod_name in six.iterkeys(self_functions): - if '.' not in mod_name: + if "." not in mod_name: continue - mod, _ = mod_name.split('.', 1) + mod, _ = mod_name.split(".", 1) if mod in completed_funcs: continue completed_funcs.append(mod) @@ -351,110 +372,124 @@ class SyncClientMixin(object): # we make the transition we will load "kwargs" using format_call if # there are no kwargs in the low object passed in. - if 'arg' in low and 'kwarg' in low: - args = low['arg'] - kwargs = low['kwarg'] + if "arg" in low and "kwarg" in low: + args = low["arg"] + kwargs = low["kwarg"] else: f_call = salt.utils.args.format_call( self.functions[fun], low, - expected_extra_kws=CLIENT_INTERNAL_KEYWORDS + expected_extra_kws=CLIENT_INTERNAL_KEYWORDS, ) - args = f_call.get('args', ()) - kwargs = f_call.get('kwargs', {}) + args = f_call.get("args", ()) + kwargs = f_call.get("kwargs", {}) # Update the event data with loaded args and kwargs - data['fun_args'] = list(args) + ([kwargs] if kwargs else []) - func_globals['__jid_event__'].fire_event(data, 'new') + data["fun_args"] = list(args) + ([kwargs] if kwargs else []) + func_globals["__jid_event__"].fire_event(data, "new") # Initialize a context for executing the method. - with salt.ext.tornado.stack_context.StackContext(self.functions.context_dict.clone): + with salt.ext.tornado.stack_context.StackContext( + self.functions.context_dict.clone + ): func = self.functions[fun] try: - data['return'] = func(*args, **kwargs) + data["return"] = func(*args, **kwargs) except TypeError as exc: - data['return'] = '\nPassed invalid arguments: {0}\n\nUsage:\n{1}'.format(exc, func.__doc__) + data[ + "return" + ] = "\nPassed invalid arguments: {0}\n\nUsage:\n{1}".format( + exc, func.__doc__ + ) try: - data['success'] = self.context.get('retcode', 0) == 0 + data["success"] = self.context.get("retcode", 0) == 0 except AttributeError: # Assume a True result if no context attribute - data['success'] = True - if isinstance(data['return'], dict) and 'data' in data['return']: + data["success"] = True + if isinstance(data["return"], dict) and "data" in data["return"]: # some functions can return boolean values - data['success'] = salt.utils.state.check_result(data['return']['data']) + data["success"] = salt.utils.state.check_result( + data["return"]["data"] + ) except (Exception, SystemExit) as ex: # pylint: disable=broad-except if isinstance(ex, salt.exceptions.NotImplemented): - data['return'] = six.text_type(ex) + data["return"] = six.text_type(ex) else: - data['return'] = 'Exception occurred in {0} {1}: {2}'.format( - self.client, - fun, - traceback.format_exc(), - ) - data['success'] = False + data["return"] = "Exception occurred in {0} {1}: {2}".format( + self.client, fun, traceback.format_exc(), + ) + data["success"] = False if self.store_job: try: salt.utils.job.store_job( self.opts, { - 'id': self.opts['id'], - 'tgt': self.opts['id'], - 'jid': data['jid'], - 'return': data, + "id": self.opts["id"], + "tgt": self.opts["id"], + "jid": data["jid"], + "return": data, }, event=None, mminion=self.mminion, - ) + ) except salt.exceptions.SaltCacheError: - log.error('Could not store job cache info. ' - 'Job details for this run may be unavailable.') + log.error( + "Could not store job cache info. " + "Job details for this run may be unavailable." + ) # Outputters _can_ mutate data so write to the job cache first! - namespaced_event.fire_event(data, 'ret') + namespaced_event.fire_event(data, "ret") # if we fired an event, make sure to delete the event object. # This will ensure that we call destroy, which will do the 0MQ linger - log.info('Runner completed: %s', data['jid']) - return data if full_return else data['return'] + log.info("Runner completed: %s", data["jid"]) + return data if full_return else data["return"] def get_docs(self, arg=None): - ''' + """ Return a dictionary of functions and the inline documentation for each - ''' + """ if arg: - if '*' in arg: + if "*" in arg: target_mod = arg _use_fnmatch = True else: - target_mod = arg + '.' if not arg.endswith('.') else arg + target_mod = arg + "." if not arg.endswith(".") else arg _use_fnmatch = False if _use_fnmatch: - docs = [(fun, self.functions[fun].__doc__) - for fun in fnmatch.filter(self.functions, target_mod)] + docs = [ + (fun, self.functions[fun].__doc__) + for fun in fnmatch.filter(self.functions, target_mod) + ] else: - docs = [(fun, self.functions[fun].__doc__) - for fun in sorted(self.functions) - if fun == arg or fun.startswith(target_mod)] + docs = [ + (fun, self.functions[fun].__doc__) + for fun in sorted(self.functions) + if fun == arg or fun.startswith(target_mod) + ] else: - docs = [(fun, self.functions[fun].__doc__) - for fun in sorted(self.functions)] + docs = [ + (fun, self.functions[fun].__doc__) for fun in sorted(self.functions) + ] docs = dict(docs) return salt.utils.doc.strip_rst(docs) class AsyncClientMixin(object): - ''' + """ A mixin for *Client interfaces to enable easy asynchronous function execution - ''' + """ + client = None tag_prefix = None def _proc_function(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 - ''' + """ if daemonize and not salt.utils.platform.is_windows(): # Shutdown the multiprocessing before daemonizing salt.log.setup.shutdown_multiprocessing_logging() @@ -465,14 +500,14 @@ class AsyncClientMixin(object): salt.log.setup.setup_multiprocessing_logging() # pack a few things into low - low['__jid__'] = jid - low['__user__'] = user - low['__tag__'] = tag + low["__jid__"] = jid + low["__user__"] = user + low["__tag__"] = tag return self.low(fun, low, full_return=False) def cmd_async(self, low): - ''' + """ Execute a function asynchronously; eauth is respected This function requires that :conf_master:`external_auth` is configured @@ -488,25 +523,26 @@ class AsyncClientMixin(object): 'password': 'saltdev', }) {'jid': '20131219224744416681', 'tag': 'salt/wheel/20131219224744416681'} - ''' + """ return self.master_call(**low) def _gen_async_pub(self, jid=None): if jid is None: jid = salt.utils.jid.gen_jid(self.opts) tag = salt.utils.event.tagify(jid, prefix=self.tag_prefix) - return {'tag': tag, 'jid': jid} + return {"tag": tag, "jid": jid} - def asynchronous(self, fun, low, user='UNKNOWN', pub=None): - ''' + def asynchronous(self, fun, low, user="UNKNOWN", pub=None): + """ Execute the function in a multiprocess and return the event tag to use to watch for the return - ''' + """ async_pub = pub if pub is not None else self._gen_async_pub() proc = salt.utils.process.SignalHandlingProcess( - target=self._proc_function, - name='ProcessFunc', - args=(fun, low, user, async_pub['tag'], async_pub['jid'])) + target=self._proc_function, + name="ProcessFunc", + args=(fun, low, user, async_pub["tag"], async_pub["jid"]), + ) with salt.utils.process.default_signals(signal.SIGINT, signal.SIGTERM): # Reset current signals before starting the process in # order not to inherit the current signal handlers @@ -515,36 +551,40 @@ class AsyncClientMixin(object): return async_pub def print_async_event(self, suffix, event): - ''' + """ Print all of the events with the prefix 'tag' - ''' + """ if not isinstance(event, dict): return # if we are "quiet", don't print - if self.opts.get('quiet', False): + if self.opts.get("quiet", False): return # some suffixes we don't want to print - if suffix in ('new',): + if suffix in ("new",): return try: - outputter = self.opts.get('output', event.get('outputter', None) or event.get('return').get('outputter')) + outputter = self.opts.get( + "output", + event.get("outputter", None) or event.get("return").get("outputter"), + ) except AttributeError: outputter = None # if this is a ret, we have our own set of rules - if suffix == 'ret': + if suffix == "ret": # Check if outputter was passed in the return data. If this is the case, # then the return data will be a dict two keys: 'data' and 'outputter' - if isinstance(event.get('return'), dict) \ - and set(event['return']) == set(('data', 'outputter')): - event_data = event['return']['data'] - outputter = event['return']['outputter'] + if isinstance(event.get("return"), dict) and set(event["return"]) == set( + ("data", "outputter") + ): + event_data = event["return"]["data"] + outputter = event["return"]["outputter"] else: - event_data = event['return'] + event_data = event["return"] else: - event_data = {'suffix': suffix, 'event': event} + event_data = {"suffix": suffix, "event": event} salt.output.display_output(event_data, outputter, self.opts) diff --git a/salt/client/netapi.py b/salt/client/netapi.py index 5c2ce9d514f..0693ebd590f 100644 --- a/salt/client/netapi.py +++ b/salt/client/netapi.py @@ -1,11 +1,12 @@ # encoding: utf-8 -''' +""" The main entry point for salt-api -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import signal + import logging +import signal # Import salt-api libs import salt.loader @@ -15,9 +16,10 @@ log = logging.getLogger(__name__) class RunNetapi(salt.utils.process.SignalHandlingProcess): - ''' + """ Runner class that's pickable for netapi modules - ''' + """ + def __init__(self, opts, fname, **kwargs): super(RunNetapi, self).__init__(**kwargs) self.opts = opts @@ -28,18 +30,18 @@ class RunNetapi(salt.utils.process.SignalHandlingProcess): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['opts'], - state['fname'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["opts"], + state["fname"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'fname': self.fname, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "fname": self.fname, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def run(self): @@ -49,34 +51,36 @@ class RunNetapi(salt.utils.process.SignalHandlingProcess): class NetapiClient(object): - ''' + """ Start each netapi module that is configured to run - ''' + """ + def __init__(self, opts): self.opts = opts - self.process_manager = salt.utils.process.ProcessManager(name='NetAPIProcessManager') + self.process_manager = salt.utils.process.ProcessManager( + name="NetAPIProcessManager" + ) self.netapi = salt.loader.netapi(self.opts) def run(self): - ''' + """ Load and start all available api modules - ''' + """ if not self.netapi: log.error("Did not find any netapi configurations, nothing to start") kwargs = {} if salt.utils.platform.is_windows(): - kwargs['log_queue'] = salt.log.setup.get_multiprocessing_logging_queue() - kwargs['log_queue_level'] = salt.log.setup.get_multiprocessing_logging_level() + kwargs["log_queue"] = salt.log.setup.get_multiprocessing_logging_queue() + kwargs[ + "log_queue_level" + ] = salt.log.setup.get_multiprocessing_logging_level() for fun in self.netapi: - if fun.endswith('.start'): - log.info('Starting %s netapi module', fun) + if fun.endswith(".start"): + log.info("Starting %s netapi module", fun) self.process_manager.add_process( - RunNetapi, - args=(self.opts, fun), - kwargs=kwargs, - name='RunNetapi' + RunNetapi, args=(self.opts, fun), kwargs=kwargs, name="RunNetapi" ) # Install the SIGINT/SIGTERM handlers if not done so far diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py index 48815408378..5a2ccbd5d78 100644 --- a/salt/client/ssh/__init__.py +++ b/salt/client/ssh/__init__.py @@ -1,37 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" Create ssh executor system -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 +import binascii import copy +import datetime import getpass +import hashlib import logging import multiprocessing -import subprocess -import hashlib -import tarfile import os import re +import subprocess import sys +import tarfile +import tempfile import time import uuid -import tempfile -import binascii -import sys -import datetime -# Import salt libs -import salt.output import salt.client.ssh.shell import salt.client.ssh.wrapper import salt.config -import salt.exceptions import salt.defaults.exitcodes -import salt.log +import salt.exceptions import salt.loader +import salt.log import salt.minion + +# Import salt libs +import salt.output import salt.roster import salt.serializers.yaml import salt.state @@ -47,23 +48,24 @@ import salt.utils.stringutils import salt.utils.thin import salt.utils.url import salt.utils.verify -from salt.utils.platform import is_windows -from salt.utils.process import Process -import salt.roster -from salt.template import compile_template # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import input # pylint: disable=import-error,redefined-builtin +from salt.template import compile_template +from salt.utils.platform import is_windows +from salt.utils.process import Process +from salt.utils.zeromq import zmq + try: import saltwinshell + HAS_WINSHELL = True except ImportError: HAS_WINSHELL = False -from salt.utils.zeromq import zmq # The directory where salt thin is deployed -DEFAULT_THIN_DIR = '/var/tmp/.%%USER%%_%%FQDNUUID%%_salt' +DEFAULT_THIN_DIR = "/var/tmp/.%%USER%%_%%FQDNUUID%%_salt" # RSTR is just a delimiter to distinguish the beginning of salt STDOUT # and STDERR. There is no special meaning. Messages prior to RSTR in @@ -78,11 +80,11 @@ DEFAULT_THIN_DIR = '/var/tmp/.%%USER%%_%%FQDNUUID%%_salt' # Failure in SHIM # RSTR in stderr, No RSTR in stdout: # Undefined behavior -RSTR = '_edbc7885e4f9aac9b83b35999b68d015148caf467b78fa39c05f669c0ff89878' +RSTR = "_edbc7885e4f9aac9b83b35999b68d015148caf467b78fa39c05f669c0ff89878" # The regex to find RSTR in output - Must be on an output line by itself # NOTE - must use non-grouping match groups or output splitting will fail. -RSTR_RE = r'(?:^|\r?\n)' + RSTR + r'(?:\r?\n|$)' +RSTR_RE = r"(?:^|\r?\n)" + RSTR + r"(?:\r?\n|$)" # METHODOLOGY: # @@ -127,15 +129,20 @@ RSTR_RE = r'(?:^|\r?\n)' + RSTR + r'(?:\r?\n|$)' # to be able to define the string with indentation for readability but # still strip the white space for compactness and to avoid issues with # some multi-line embedded python code having indentation errors -SSH_SH_SHIM = \ - '\n'.join( - [s.strip() for s in r'''/bin/sh << 'EOF' +SSH_SH_SHIM = "\n".join( + [ + s.strip() + for s in r'''/bin/sh << 'EOF' set -e set -u DEBUG="{{DEBUG}}" if [ -n "$DEBUG" ] then set -x fi +SET_PATH="{{SET_PATH}}" +if [ -n "$SET_PATH" ] + then export PATH={{SET_PATH}} +fi SUDO="" if [ -n "{{SUDO}}" ] then SUDO="sudo " @@ -184,13 +191,17 @@ echo "ERROR: Unable to locate appropriate python command" >&2 exit $EX_PYTHON_INVALID EOF'''.format( EX_THIN_PYTHON_INVALID=salt.defaults.exitcodes.EX_THIN_PYTHON_INVALID, - ).split('\n')]) + ).split( + "\n" + ) + ] +) if not is_windows(): - shim_file = os.path.join(os.path.dirname(__file__), 'ssh_py_shim.py') + shim_file = os.path.join(os.path.dirname(__file__), "ssh_py_shim.py") if not os.path.exists(shim_file): # On esky builds we only have the .pyc file - shim_file += 'c' + shim_file += "c" with salt.utils.files.fopen(shim_file) as ssh_py_shim: SSH_PY_SHIM = ssh_py_shim.read() @@ -198,162 +209,152 @@ log = logging.getLogger(__name__) class SSH(object): - ''' + """ Create an SSH execution system - ''' - ROSTER_UPDATE_FLAG = '#__needs_update' + """ + + ROSTER_UPDATE_FLAG = "#__needs_update" def __init__(self, opts): self.__parsed_rosters = {SSH.ROSTER_UPDATE_FLAG: True} - pull_sock = os.path.join(opts['sock_dir'], 'master_event_pull.ipc') + pull_sock = os.path.join(opts["sock_dir"], "master_event_pull.ipc") if os.path.exists(pull_sock) and zmq: self.event = salt.utils.event.get_event( - 'master', - opts['sock_dir'], - opts['transport'], - opts=opts, - listen=False) + "master", opts["sock_dir"], opts["transport"], opts=opts, listen=False + ) else: self.event = None self.opts = opts - if self.opts['regen_thin']: - self.opts['ssh_wipe'] = True - if not salt.utils.path.which('ssh'): - raise salt.exceptions.SaltSystemExit(code=-1, - msg='No ssh binary found in path -- ssh must be installed for salt-ssh to run. Exiting.') - self.opts['_ssh_version'] = ssh_version() - self.tgt_type = self.opts['selected_target_option'] \ - if self.opts['selected_target_option'] else 'glob' + if self.opts["regen_thin"]: + self.opts["ssh_wipe"] = True + if not salt.utils.path.which("ssh"): + raise salt.exceptions.SaltSystemExit( + code=-1, + msg="No ssh binary found in path -- ssh must be installed for salt-ssh to run. Exiting.", + ) + self.opts["_ssh_version"] = ssh_version() + self.tgt_type = ( + self.opts["selected_target_option"] + if self.opts["selected_target_option"] + else "glob" + ) self._expand_target() - self.roster = salt.roster.Roster(self.opts, self.opts.get('roster', 'flat')) - self.targets = self.roster.targets( - self.opts['tgt'], - self.tgt_type) + self.roster = salt.roster.Roster(self.opts, self.opts.get("roster", "flat")) + self.targets = self.roster.targets(self.opts["tgt"], self.tgt_type) if not self.targets: self._update_targets() # If we're in a wfunc, we need to get the ssh key location from the # top level opts, stored in __master_opts__ - if '__master_opts__' in self.opts: - if self.opts['__master_opts__'].get('ssh_use_home_key') and \ - os.path.isfile(os.path.expanduser('~/.ssh/id_rsa')): - priv = os.path.expanduser('~/.ssh/id_rsa') + if "__master_opts__" in self.opts: + if self.opts["__master_opts__"].get("ssh_use_home_key") and os.path.isfile( + os.path.expanduser("~/.ssh/id_rsa") + ): + priv = os.path.expanduser("~/.ssh/id_rsa") else: - priv = self.opts['__master_opts__'].get( - 'ssh_priv', - os.path.join( - self.opts['__master_opts__']['pki_dir'], - 'ssh', - 'salt-ssh.rsa' - ) - ) + priv = self.opts["__master_opts__"].get( + "ssh_priv", + os.path.join( + self.opts["__master_opts__"]["pki_dir"], "ssh", "salt-ssh.rsa" + ), + ) else: priv = self.opts.get( - 'ssh_priv', - os.path.join( - self.opts['pki_dir'], - 'ssh', - 'salt-ssh.rsa' - ) - ) - if priv != 'agent-forwarding': + "ssh_priv", os.path.join(self.opts["pki_dir"], "ssh", "salt-ssh.rsa") + ) + if priv != "agent-forwarding": if not os.path.isfile(priv): try: salt.client.ssh.shell.gen_key(priv) except OSError: raise salt.exceptions.SaltClientError( - 'salt-ssh could not be run because it could not generate keys.\n\n' - 'You can probably resolve this by executing this script with ' - 'increased permissions via sudo or by running as root.\n' - 'You could also use the \'-c\' option to supply a configuration ' - 'directory that you have permissions to read and write to.' + "salt-ssh could not be run because it could not generate keys.\n\n" + "You can probably resolve this by executing this script with " + "increased permissions via sudo or by running as root.\n" + "You could also use the '-c' option to supply a configuration " + "directory that you have permissions to read and write to." ) self.defaults = { - 'user': self.opts.get( - 'ssh_user', - salt.config.DEFAULT_MASTER_OPTS['ssh_user'] + "user": self.opts.get( + "ssh_user", salt.config.DEFAULT_MASTER_OPTS["ssh_user"] ), - 'port': self.opts.get( - 'ssh_port', - salt.config.DEFAULT_MASTER_OPTS['ssh_port'] + "port": self.opts.get( + "ssh_port", salt.config.DEFAULT_MASTER_OPTS["ssh_port"] ), - 'passwd': self.opts.get( - 'ssh_passwd', - salt.config.DEFAULT_MASTER_OPTS['ssh_passwd'] + "passwd": self.opts.get( + "ssh_passwd", salt.config.DEFAULT_MASTER_OPTS["ssh_passwd"] ), - 'priv': priv, - 'priv_passwd': self.opts.get( - 'ssh_priv_passwd', - salt.config.DEFAULT_MASTER_OPTS['ssh_priv_passwd'] + "priv": priv, + "priv_passwd": self.opts.get( + "ssh_priv_passwd", salt.config.DEFAULT_MASTER_OPTS["ssh_priv_passwd"] ), - 'timeout': self.opts.get( - 'ssh_timeout', - salt.config.DEFAULT_MASTER_OPTS['ssh_timeout'] - ) + self.opts.get( - 'timeout', - salt.config.DEFAULT_MASTER_OPTS['timeout'] - ), - 'sudo': self.opts.get( - 'ssh_sudo', - salt.config.DEFAULT_MASTER_OPTS['ssh_sudo'] - ), - 'sudo_user': self.opts.get( - 'ssh_sudo_user', - salt.config.DEFAULT_MASTER_OPTS['ssh_sudo_user'] - ), - 'identities_only': self.opts.get( - 'ssh_identities_only', - salt.config.DEFAULT_MASTER_OPTS['ssh_identities_only'] - ), - 'remote_port_forwards': self.opts.get( - 'ssh_remote_port_forwards' - ), - 'ssh_options': self.opts.get( - 'ssh_options' + "timeout": self.opts.get( + "ssh_timeout", salt.config.DEFAULT_MASTER_OPTS["ssh_timeout"] ) + + self.opts.get("timeout", salt.config.DEFAULT_MASTER_OPTS["timeout"]), + "sudo": self.opts.get( + "ssh_sudo", salt.config.DEFAULT_MASTER_OPTS["ssh_sudo"] + ), + "sudo_user": self.opts.get( + "ssh_sudo_user", salt.config.DEFAULT_MASTER_OPTS["ssh_sudo_user"] + ), + "identities_only": self.opts.get( + "ssh_identities_only", + salt.config.DEFAULT_MASTER_OPTS["ssh_identities_only"], + ), + "remote_port_forwards": self.opts.get("ssh_remote_port_forwards"), + "ssh_options": self.opts.get("ssh_options"), } - if self.opts.get('rand_thin_dir'): - self.defaults['thin_dir'] = os.path.join( - '/var/tmp', - '.{0}'.format(uuid.uuid4().hex[:6])) - self.opts['ssh_wipe'] = 'True' + if self.opts.get("rand_thin_dir"): + self.defaults["thin_dir"] = os.path.join( + "/var/tmp", ".{0}".format(uuid.uuid4().hex[:6]) + ) + self.opts["ssh_wipe"] = "True" self.serial = salt.payload.Serial(opts) self.returners = salt.loader.returners(self.opts, {}) self.fsclient = salt.fileclient.FSClient(self.opts) - self.thin = salt.utils.thin.gen_thin(self.opts['cachedir'], - extra_mods=self.opts.get('thin_extra_mods'), - overwrite=self.opts['regen_thin'], - python2_bin=self.opts['python2_bin'], - python3_bin=self.opts['python3_bin'], - extended_cfg=self.opts.get('ssh_ext_alternatives')) + self.thin = salt.utils.thin.gen_thin( + self.opts["cachedir"], + extra_mods=self.opts.get("thin_extra_mods"), + overwrite=self.opts["regen_thin"], + python2_bin=self.opts["python2_bin"], + python3_bin=self.opts["python3_bin"], + extended_cfg=self.opts.get("ssh_ext_alternatives"), + ) self.mods = mod_data(self.fsclient) def _get_roster(self): - ''' + """ Read roster filename as a key to the data. :return: - ''' + """ roster_file = salt.roster.get_roster_file(self.opts) if roster_file not in self.__parsed_rosters: - roster_data = compile_template(roster_file, salt.loader.render(self.opts, {}), - self.opts['renderer'], self.opts['renderer_blacklist'], - self.opts['renderer_whitelist']) + roster_data = compile_template( + roster_file, + salt.loader.render(self.opts, {}), + self.opts["renderer"], + self.opts["renderer_blacklist"], + self.opts["renderer_whitelist"], + ) self.__parsed_rosters[roster_file] = roster_data return roster_file def _expand_target(self): - ''' + """ Figures out if the target is a reachable host without wildcards, expands if any. :return: - ''' + """ # TODO: Support -L - target = self.opts['tgt'] + target = self.opts["tgt"] if isinstance(target, list): return - hostname = self.opts['tgt'].split('@')[-1] - needs_expansion = '*' not in hostname and \ - salt.utils.network.is_reachable_host(hostname) and \ - salt.utils.network.is_ip(hostname) + hostname = self.opts["tgt"].split("@")[-1] + needs_expansion = ( + "*" not in hostname + and salt.utils.network.is_reachable_host(hostname) + and salt.utils.network.is_ip(hostname) + ) if needs_expansion: hostname = salt.utils.network.ip_to_host(hostname) if hostname is None: @@ -364,187 +365,198 @@ class SSH(object): roster_data = self.__parsed_rosters[roster_filename] if not isinstance(roster_data, bool): for host_id in roster_data: - if hostname in [host_id, roster_data.get('host')]: - if hostname != self.opts['tgt']: - self.opts['tgt'] = hostname + if hostname in [host_id, roster_data.get("host")]: + if hostname != self.opts["tgt"]: + self.opts["tgt"] = hostname self.__parsed_rosters[self.ROSTER_UPDATE_FLAG] = False return def _update_roster(self): - ''' + """ Update default flat roster with the passed in information. :return: - ''' + """ roster_file = self._get_roster() if os.access(roster_file, os.W_OK): if self.__parsed_rosters[self.ROSTER_UPDATE_FLAG]: - with salt.utils.files.fopen(roster_file, 'a') as roster_fp: - roster_fp.write('# Automatically added by "{s_user}" at {s_time}\n{hostname}:\n host: ' - '{hostname}\n user: {user}' - '\n passwd: {passwd}\n'.format(s_user=getpass.getuser(), - s_time=datetime.datetime.utcnow().isoformat(), - hostname=self.opts.get('tgt', ''), - user=self.opts.get('ssh_user', ''), - passwd=self.opts.get('ssh_passwd', ''))) - log.info('The host {0} has been added to the roster {1}'.format(self.opts.get('tgt', ''), - roster_file)) + with salt.utils.files.fopen(roster_file, "a") as roster_fp: + roster_fp.write( + '# Automatically added by "{s_user}" at {s_time}\n{hostname}:\n host: ' + "{hostname}\n user: {user}" + "\n passwd: {passwd}\n".format( + s_user=getpass.getuser(), + s_time=datetime.datetime.utcnow().isoformat(), + hostname=self.opts.get("tgt", ""), + user=self.opts.get("ssh_user", ""), + passwd=self.opts.get("ssh_passwd", ""), + ) + ) + log.info( + "The host {0} has been added to the roster {1}".format( + self.opts.get("tgt", ""), roster_file + ) + ) else: - log.error('Unable to update roster {0}: access denied'.format(roster_file)) + log.error("Unable to update roster {0}: access denied".format(roster_file)) def _update_targets(self): - ''' + """ Uptade targets in case hostname was directly passed without the roster. :return: - ''' + """ - hostname = self.opts.get('tgt', '') - if '@' in hostname: - user, hostname = hostname.split('@', 1) + hostname = self.opts.get("tgt", "") + if "@" in hostname: + user, hostname = hostname.split("@", 1) else: - user = self.opts.get('ssh_user') - if hostname == '*': - hostname = '' + user = self.opts.get("ssh_user") + if hostname == "*": + hostname = "" if salt.utils.network.is_reachable_host(hostname): hostname = salt.utils.network.ip_to_host(hostname) - self.opts['tgt'] = hostname + self.opts["tgt"] = hostname self.targets[hostname] = { - 'passwd': self.opts.get('ssh_passwd', ''), - 'host': hostname, - 'user': user, + "passwd": self.opts.get("ssh_passwd", ""), + "host": hostname, + "user": user, } - if self.opts.get('ssh_update_roster'): + if self.opts.get("ssh_update_roster"): self._update_roster() def get_pubkey(self): - ''' + """ Return the key string for the SSH public key - ''' - if '__master_opts__' in self.opts and \ - self.opts['__master_opts__'].get('ssh_use_home_key') and \ - os.path.isfile(os.path.expanduser('~/.ssh/id_rsa')): - priv = os.path.expanduser('~/.ssh/id_rsa') + """ + if ( + "__master_opts__" in self.opts + and self.opts["__master_opts__"].get("ssh_use_home_key") + and os.path.isfile(os.path.expanduser("~/.ssh/id_rsa")) + ): + priv = os.path.expanduser("~/.ssh/id_rsa") else: priv = self.opts.get( - 'ssh_priv', - os.path.join( - self.opts['pki_dir'], - 'ssh', - 'salt-ssh.rsa' - ) - ) - pub = '{0}.pub'.format(priv) - with salt.utils.files.fopen(pub, 'r') as fp_: - return '{0} rsa root@master'.format(fp_.read().split()[1]) + "ssh_priv", os.path.join(self.opts["pki_dir"], "ssh", "salt-ssh.rsa") + ) + pub = "{0}.pub".format(priv) + with salt.utils.files.fopen(pub, "r") as fp_: + return "{0} rsa root@master".format(fp_.read().split()[1]) def key_deploy(self, host, ret): - ''' + """ Deploy the SSH key if the minions don't auth - ''' - if not isinstance(ret[host], dict) or self.opts.get('ssh_key_deploy'): + """ + if not isinstance(ret[host], dict) or self.opts.get("ssh_key_deploy"): target = self.targets[host] - if target.get('passwd', False) or self.opts['ssh_passwd']: + if target.get("passwd", False) or self.opts["ssh_passwd"]: self._key_deploy_run(host, target, False) return ret - if ret[host].get('stderr', '').count('Permission denied'): + if ret[host].get("stderr", "").count("Permission denied"): target = self.targets[host] # permission denied, attempt to auto deploy ssh key - print(('Permission denied for host {0}, do you want to deploy ' - 'the salt-ssh key? (password required):').format(host)) - deploy = input('[Y/n] ') - if deploy.startswith(('n', 'N')): + print( + ( + "Permission denied for host {0}, do you want to deploy " + "the salt-ssh key? (password required):" + ).format(host) + ) + deploy = input("[Y/n] ") + if deploy.startswith(("n", "N")): return ret - target['passwd'] = getpass.getpass( - 'Password for {0}@{1}: '.format(target['user'], host) - ) + target["passwd"] = getpass.getpass( + "Password for {0}@{1}: ".format(target["user"], host) + ) return self._key_deploy_run(host, target, True) return ret def _key_deploy_run(self, host, target, re_run=True): - ''' + """ The ssh-copy-id routine - ''' + """ argv = [ - 'ssh.set_auth_key', - target.get('user', 'root'), + "ssh.set_auth_key", + target.get("user", "root"), self.get_pubkey(), ] single = Single( - self.opts, - argv, - host, - mods=self.mods, - fsclient=self.fsclient, - thin=self.thin, - **target) - if salt.utils.path.which('ssh-copy-id'): + self.opts, + argv, + host, + mods=self.mods, + fsclient=self.fsclient, + thin=self.thin, + **target + ) + if salt.utils.path.which("ssh-copy-id"): # we have ssh-copy-id, use it! stdout, stderr, retcode = single.shell.copy_id() else: stdout, stderr, retcode = single.run() if re_run: - target.pop('passwd') + target.pop("passwd") single = Single( - self.opts, - self.opts['argv'], - host, - mods=self.mods, - fsclient=self.fsclient, - thin=self.thin, - **target) + self.opts, + self.opts["argv"], + host, + mods=self.mods, + fsclient=self.fsclient, + thin=self.thin, + **target + ) stdout, stderr, retcode = single.cmd_block() try: data = salt.utils.json.find_json(stdout) - return {host: data.get('local', data)} + return {host: data.get("local", data)} except Exception: # pylint: disable=broad-except if stderr: return {host: stderr} - return {host: 'Bad Return'} + return {host: "Bad Return"} if salt.defaults.exitcodes.EX_OK != retcode: return {host: stderr} return {host: stdout} def handle_routine(self, que, opts, host, target, mine=False): - ''' + """ Run the routine in a "Thread", put a dict on the queue - ''' + """ opts = copy.deepcopy(opts) single = Single( - opts, - opts['argv'], - host, - mods=self.mods, - fsclient=self.fsclient, - thin=self.thin, - mine=mine, - **target) - ret = {'id': single.id} + opts, + opts["argv"], + host, + mods=self.mods, + fsclient=self.fsclient, + thin=self.thin, + mine=mine, + **target + ) + ret = {"id": single.id} stdout, stderr, retcode = single.run() # This job is done, yield try: data = salt.utils.json.find_json(stdout) - if len(data) < 2 and 'local' in data: - ret['ret'] = data['local'] + if len(data) < 2 and "local" in data: + ret["ret"] = data["local"] else: - ret['ret'] = { - 'stdout': stdout, - 'stderr': stderr, - 'retcode': retcode, + ret["ret"] = { + "stdout": stdout, + "stderr": stderr, + "retcode": retcode, } except Exception: # pylint: disable=broad-except - ret['ret'] = { - 'stdout': stdout, - 'stderr': stderr, - 'retcode': retcode, + ret["ret"] = { + "stdout": stdout, + "stderr": stderr, + "retcode": retcode, } que.put(ret) def handle_ssh(self, mine=False): - ''' + """ Spin up the needed threads or processes and execute the subsequent routines - ''' + """ que = multiprocessing.Queue() running = {} target_iter = self.targets.__iter__() @@ -553,9 +565,9 @@ class SSH(object): init = False while True: if not self.targets: - log.error('No matching targets found in roster.') + log.error("No matching targets found in roster.") break - if len(running) < self.opts.get('ssh_max_procs', 25) and not init: + if len(running) < self.opts.get("ssh_max_procs", 25) and not init: try: host = next(target_iter) except StopIteration: @@ -564,40 +576,40 @@ class SSH(object): for default in self.defaults: if default not in self.targets[host]: self.targets[host][default] = self.defaults[default] - if 'host' not in self.targets[host]: - self.targets[host]['host'] = host - if self.targets[host].get('winrm') and not HAS_WINSHELL: + if "host" not in self.targets[host]: + self.targets[host]["host"] = host + if self.targets[host].get("winrm") and not HAS_WINSHELL: returned.add(host) rets.add(host) - log_msg = 'Please contact sales@saltstack.com for access to the enterprise saltwinshell module.' + log_msg = "Please contact sales@saltstack.com for access to the enterprise saltwinshell module." log.debug(log_msg) - no_ret = {'fun_args': [], - 'jid': None, - 'return': log_msg, - 'retcode': 1, - 'fun': '', - 'id': host} + no_ret = { + "fun_args": [], + "jid": None, + "return": log_msg, + "retcode": 1, + "fun": "", + "id": host, + } yield {host: no_ret} continue args = ( - que, - self.opts, - host, - self.targets[host], - mine, - ) - routine = Process( - target=self.handle_routine, - args=args) + que, + self.opts, + host, + self.targets[host], + mine, + ) + routine = Process(target=self.handle_routine, args=args) routine.start() - running[host] = {'thread': routine} + running[host] = {"thread": routine} continue ret = {} try: ret = que.get(False) - if 'id' in ret: - returned.add(ret['id']) - yield {ret['id']: ret['ret']} + if "id" in ret: + returned.add(ret["id"]) + yield {ret["id"]: ret["ret"]} except Exception: # pylint: disable=broad-except # This bare exception is here to catch spurious exceptions # thrown by que.get during healthy operation. Please do not @@ -605,27 +617,28 @@ class SSH(object): # control program flow. pass for host in running: - if not running[host]['thread'].is_alive(): + if not running[host]["thread"].is_alive(): if host not in returned: # Try to get any returns that came through since we # last checked try: while True: ret = que.get(False) - if 'id' in ret: - returned.add(ret['id']) - yield {ret['id']: ret['ret']} + if "id" in ret: + returned.add(ret["id"]) + yield {ret["id"]: ret["ret"]} except Exception: # pylint: disable=broad-except pass if host not in returned: - error = ('Target \'{0}\' did not return any data, ' - 'probably due to an error.').format(host) - ret = {'id': host, - 'ret': error} + error = ( + "Target '{0}' did not return any data, " + "probably due to an error." + ).format(host) + ret = {"id": host, "ret": error} log.error(error) - yield {ret['id']: ret['ret']} - running[host]['thread'].join() + yield {ret["id"]: ret["ret"]} + running[host]["thread"].join() rets.add(host) for host in rets: if host in running: @@ -633,45 +646,51 @@ class SSH(object): if len(rets) >= len(self.targets): break # Sleep when limit or all threads started - if len(running) >= self.opts.get('ssh_max_procs', 25) or len(self.targets) >= len(running): + if len(running) >= self.opts.get("ssh_max_procs", 25) or len( + self.targets + ) >= len(running): time.sleep(0.1) def run_iter(self, mine=False, jid=None): - ''' + """ Execute and yield returns as they come in, do not print to the display mine The Single objects will use mine_functions defined in the roster, pillar, or master config (they will be checked in that order) and will modify the argv with the arguments from mine_functions - ''' - fstr = '{0}.prep_jid'.format(self.opts['master_job_cache']) - jid = self.returners[fstr](passed_jid=jid or self.opts.get('jid', None)) + """ + fstr = "{0}.prep_jid".format(self.opts["master_job_cache"]) + jid = self.returners[fstr](passed_jid=jid or self.opts.get("jid", None)) # Save the invocation information - argv = self.opts['argv'] + argv = self.opts["argv"] - if self.opts.get('raw_shell', False): - fun = 'ssh._raw' + if self.opts.get("raw_shell", False): + fun = "ssh._raw" args = argv else: - fun = argv[0] if argv else '' + fun = argv[0] if argv else "" args = argv[1:] job_load = { - 'jid': jid, - 'tgt_type': self.tgt_type, - 'tgt': self.opts['tgt'], - 'user': self.opts['user'], - 'fun': fun, - 'arg': args, - } + "jid": jid, + "tgt_type": self.tgt_type, + "tgt": self.opts["tgt"], + "user": self.opts["user"], + "fun": fun, + "arg": args, + } # save load to the master job cache - if self.opts['master_job_cache'] == 'local_cache': - self.returners['{0}.save_load'.format(self.opts['master_job_cache'])](jid, job_load, minions=self.targets.keys()) + if self.opts["master_job_cache"] == "local_cache": + self.returners["{0}.save_load".format(self.opts["master_job_cache"])]( + jid, job_load, minions=self.targets.keys() + ) else: - self.returners['{0}.save_load'.format(self.opts['master_job_cache'])](jid, job_load) + self.returners["{0}.save_load".format(self.opts["master_job_cache"])]( + jid, job_load + ) for ret in self.handle_ssh(mine=mine): host = next(six.iterkeys(ret)) @@ -679,92 +698,96 @@ class SSH(object): if self.event: id_, data = next(six.iteritems(ret)) if isinstance(data, six.text_type): - data = {'return': data} - if 'id' not in data: - data['id'] = id_ - data['jid'] = jid # make the jid in the payload the same as the jid in the tag + data = {"return": data} + if "id" not in data: + data["id"] = id_ + data[ + "jid" + ] = jid # make the jid in the payload the same as the jid in the tag self.event.fire_event( - data, - salt.utils.event.tagify( - [jid, 'ret', host], - 'job')) + data, salt.utils.event.tagify([jid, "ret", host], "job") + ) yield ret def cache_job(self, jid, id_, ret, fun): - ''' + """ Cache the job information - ''' - self.returners['{0}.returner'.format(self.opts['master_job_cache'])]({'jid': jid, - 'id': id_, - 'return': ret, - 'fun': fun}) + """ + self.returners["{0}.returner".format(self.opts["master_job_cache"])]( + {"jid": jid, "id": id_, "return": ret, "fun": fun} + ) def run(self, jid=None): - ''' + """ Execute the overall routine, print results via outputters - ''' - if self.opts.get('list_hosts'): + """ + if self.opts.get("list_hosts"): self._get_roster() ret = {} for roster_file in self.__parsed_rosters: - if roster_file.startswith('#'): + if roster_file.startswith("#"): continue ret[roster_file] = {} for host_id in self.__parsed_rosters[roster_file]: - hostname = self.__parsed_rosters[roster_file][host_id]['host'] + hostname = self.__parsed_rosters[roster_file][host_id]["host"] ret[roster_file][host_id] = hostname - salt.output.display_output(ret, 'nested', self.opts) + salt.output.display_output(ret, "nested", self.opts) sys.exit() - fstr = '{0}.prep_jid'.format(self.opts['master_job_cache']) - jid = self.returners[fstr](passed_jid=jid or self.opts.get('jid', None)) + fstr = "{0}.prep_jid".format(self.opts["master_job_cache"]) + jid = self.returners[fstr](passed_jid=jid or self.opts.get("jid", None)) # Save the invocation information - argv = self.opts['argv'] + argv = self.opts["argv"] - if self.opts.get('raw_shell', False): - fun = 'ssh._raw' + if self.opts.get("raw_shell", False): + fun = "ssh._raw" args = argv else: - fun = argv[0] if argv else '' + fun = argv[0] if argv else "" args = argv[1:] job_load = { - 'jid': jid, - 'tgt_type': self.tgt_type, - 'tgt': self.opts['tgt'], - 'user': self.opts['user'], - 'fun': fun, - 'arg': args, - } + "jid": jid, + "tgt_type": self.tgt_type, + "tgt": self.opts["tgt"], + "user": self.opts["user"], + "fun": fun, + "arg": args, + } # save load to the master job cache try: if isinstance(jid, bytes): - jid = jid.decode('utf-8') - if self.opts['master_job_cache'] == 'local_cache': - self.returners['{0}.save_load'.format(self.opts['master_job_cache'])](jid, job_load, minions=self.targets.keys()) + jid = jid.decode("utf-8") + if self.opts["master_job_cache"] == "local_cache": + self.returners["{0}.save_load".format(self.opts["master_job_cache"])]( + jid, job_load, minions=self.targets.keys() + ) else: - self.returners['{0}.save_load'.format(self.opts['master_job_cache'])](jid, job_load) + self.returners["{0}.save_load".format(self.opts["master_job_cache"])]( + jid, job_load + ) except Exception as exc: # pylint: disable=broad-except log.exception(exc) log.error( - 'Could not save load with returner %s: %s', - self.opts['master_job_cache'], exc + "Could not save load with returner %s: %s", + self.opts["master_job_cache"], + exc, ) - if self.opts.get('verbose'): - msg = 'Executing job with jid {0}'.format(jid) + if self.opts.get("verbose"): + msg = "Executing job with jid {0}".format(jid) print(msg) - print('-' * len(msg) + '\n') - print('') + print("-" * len(msg) + "\n") + print("") sret = {} - outputter = self.opts.get('output', 'nested') + outputter = self.opts.get("output", "nested") final_exit = 0 for ret in self.handle_ssh(): host = next(six.iterkeys(ret)) if isinstance(ret[host], dict): - host_ret = ret[host].get('retcode', 0) + host_ret = ret[host].get("retcode", 0) if host_ret != 0: final_exit = 1 else: @@ -774,109 +797,112 @@ class SSH(object): self.cache_job(jid, host, ret[host], fun) ret = self.key_deploy(host, ret) - if isinstance(ret[host], dict) and (ret[host].get('stderr') or '').startswith('ssh:'): - ret[host] = ret[host]['stderr'] + if isinstance(ret[host], dict) and ( + ret[host].get("stderr") or "" + ).startswith("ssh:"): + ret[host] = ret[host]["stderr"] if not isinstance(ret[host], dict): p_data = {host: ret[host]} - elif 'return' not in ret[host]: + elif "return" not in ret[host]: p_data = ret else: - outputter = ret[host].get('out', self.opts.get('output', 'nested')) - p_data = {host: ret[host].get('return', {})} - if self.opts.get('static'): + outputter = ret[host].get("out", self.opts.get("output", "nested")) + p_data = {host: ret[host].get("return", {})} + if self.opts.get("static"): sret.update(p_data) else: - salt.output.display_output( - p_data, - outputter, - self.opts) + salt.output.display_output(p_data, outputter, self.opts) if self.event: id_, data = next(six.iteritems(ret)) if isinstance(data, six.text_type): - data = {'return': data} - if 'id' not in data: - data['id'] = id_ - data['jid'] = jid # make the jid in the payload the same as the jid in the tag + data = {"return": data} + if "id" not in data: + data["id"] = id_ + data[ + "jid" + ] = jid # make the jid in the payload the same as the jid in the tag self.event.fire_event( - data, - salt.utils.event.tagify( - [jid, 'ret', host], - 'job')) - if self.opts.get('static'): - salt.output.display_output( - sret, - outputter, - self.opts) + data, salt.utils.event.tagify([jid, "ret", host], "job") + ) + if self.opts.get("static"): + salt.output.display_output(sret, outputter, self.opts) if final_exit: sys.exit(salt.defaults.exitcodes.EX_AGGREGATE) class Single(object): - ''' + """ Hold onto a single ssh execution - ''' + """ + # 1. Get command ready # 2. Check if target has salt # 3. deploy salt-thin # 4. execute requested command via salt-thin def __init__( - self, - opts, - argv, - id_, - host, - user=None, - port=None, - passwd=None, - priv=None, - priv_passwd=None, - timeout=30, - sudo=False, - tty=False, - mods=None, - fsclient=None, - thin=None, - mine=False, - minion_opts=None, - identities_only=False, - sudo_user=None, - remote_port_forwards=None, - winrm=False, - ssh_options=None, - **kwargs): + self, + opts, + argv, + id_, + host, + user=None, + port=None, + passwd=None, + priv=None, + priv_passwd=None, + timeout=30, + sudo=False, + tty=False, + mods=None, + fsclient=None, + thin=None, + mine=False, + minion_opts=None, + identities_only=False, + sudo_user=None, + remote_port_forwards=None, + winrm=False, + ssh_options=None, + **kwargs + ): # Get mine setting and mine_functions if defined in kwargs (from roster) self.mine = mine - self.mine_functions = kwargs.get('mine_functions') - self.cmd_umask = kwargs.get('cmd_umask', None) + self.mine_functions = kwargs.get("mine_functions") + self.cmd_umask = kwargs.get("cmd_umask", None) self.winrm = winrm self.opts = opts self.tty = tty - if kwargs.get('disable_wipe'): + if kwargs.get("disable_wipe"): self.wipe = False else: - self.wipe = bool(self.opts.get('ssh_wipe')) - if kwargs.get('thin_dir'): - self.thin_dir = kwargs['thin_dir'] + self.wipe = bool(self.opts.get("ssh_wipe")) + if kwargs.get("thin_dir"): + self.thin_dir = kwargs["thin_dir"] elif self.winrm: saltwinshell.set_winvars(self) - self.python_env = kwargs.get('ssh_python_env') + self.python_env = kwargs.get("ssh_python_env") else: if user: - thin_dir = DEFAULT_THIN_DIR.replace('%%USER%%', user) + thin_dir = DEFAULT_THIN_DIR.replace("%%USER%%", user) else: - thin_dir = DEFAULT_THIN_DIR.replace('%%USER%%', 'root') + thin_dir = DEFAULT_THIN_DIR.replace("%%USER%%", "root") self.thin_dir = thin_dir.replace( - '%%FQDNUUID%%', - uuid.uuid3(uuid.NAMESPACE_DNS, - salt.utils.network.get_fqhostname()).hex[:6] + "%%FQDNUUID%%", + uuid.uuid3(uuid.NAMESPACE_DNS, salt.utils.network.get_fqhostname()).hex[ + :6 + ], ) - self.opts['thin_dir'] = self.thin_dir + self.opts["thin_dir"] = self.thin_dir self.fsclient = fsclient - self.context = {'master_opts': self.opts, - 'fileclient': self.fsclient} + self.context = {"master_opts": self.opts, "fileclient": self.fsclient} + + self.ssh_pre_flight = kwargs.get("ssh_pre_flight", None) + + if self.ssh_pre_flight: + self.ssh_pre_file = os.path.basename(self.ssh_pre_flight) if isinstance(argv, six.string_types): self.argv = [argv] @@ -885,38 +911,43 @@ class Single(object): self.fun, self.args, self.kwargs = self.__arg_comps() self.id = id_ + self.set_path = kwargs.get("set_path", "") self.mods = mods if isinstance(mods, dict) else {} - args = {'host': host, - 'user': user, - 'port': port, - 'passwd': passwd, - 'priv': priv, - 'priv_passwd': priv_passwd, - 'timeout': timeout, - 'sudo': sudo, - 'tty': tty, - 'mods': self.mods, - 'identities_only': identities_only, - 'sudo_user': sudo_user, - 'remote_port_forwards': remote_port_forwards, - 'winrm': winrm, - 'ssh_options': ssh_options} + args = { + "host": host, + "user": user, + "port": port, + "passwd": passwd, + "priv": priv, + "priv_passwd": priv_passwd, + "timeout": timeout, + "sudo": sudo, + "tty": tty, + "mods": self.mods, + "identities_only": identities_only, + "sudo_user": sudo_user, + "remote_port_forwards": remote_port_forwards, + "winrm": winrm, + "ssh_options": ssh_options, + } # Pre apply changeable defaults self.minion_opts = { - 'grains_cache': True, - 'log_file': 'salt-call.log', - } - self.minion_opts.update(opts.get('ssh_minion_opts', {})) + "grains_cache": True, + "log_file": "salt-call.log", + } + self.minion_opts.update(opts.get("ssh_minion_opts", {})) if minion_opts is not None: self.minion_opts.update(minion_opts) # Post apply system needed defaults - self.minion_opts.update({ - 'root_dir': os.path.join(self.thin_dir, 'running_data'), - 'id': self.id, - 'sock_dir': '/', - 'fileserver_list_cache_time': 3, - }) + self.minion_opts.update( + { + "root_dir": os.path.join(self.thin_dir, "running_data"), + "id": self.id, + "sock_dir": "/", + "fileserver_list_cache_time": 3, + } + ) self.minion_config = salt.serializers.yaml.serialize(self.minion_opts) self.target = kwargs self.target.update(args) @@ -925,59 +956,77 @@ class Single(object): self.shell = salt.client.ssh.shell.gen_shell(opts, **args) if self.winrm: # Determine if Windows client is x86 or AMD64 - arch, _, _ = self.shell.exec_cmd('powershell $ENV:PROCESSOR_ARCHITECTURE') + arch, _, _ = self.shell.exec_cmd("powershell $ENV:PROCESSOR_ARCHITECTURE") self.arch = arch.strip() - self.thin = thin if thin else salt.utils.thin.thin_path(opts['cachedir']) + self.thin = thin if thin else salt.utils.thin.thin_path(opts["cachedir"]) def __arg_comps(self): - ''' + """ Return the function name and the arg list - ''' - fun = self.argv[0] if self.argv else '' + """ + fun = self.argv[0] if self.argv else "" parsed = salt.utils.args.parse_input( - self.argv[1:], - condition=False, - no_parse=self.opts.get('no_parse', [])) + self.argv[1:], condition=False, no_parse=self.opts.get("no_parse", []) + ) args = parsed[0] kws = parsed[1] return fun, args, kws def _escape_arg(self, arg): - ''' + """ Properly escape argument to protect special characters from shell interpretation. This avoids having to do tricky argument quoting. Effectively just escape all characters in the argument that are not alphanumeric! - ''' + """ if self.winrm: return arg - return ''.join(['\\' + char if re.match(r'\W', char) else char for char in arg]) + return "".join(["\\" + char if re.match(r"\W", char) else char for char in arg]) + + def run_ssh_pre_flight(self): + """ + Run our pre_flight script before running any ssh commands + """ + script = os.path.join(tempfile.gettempdir(), self.ssh_pre_file) + + self.shell.send(self.ssh_pre_flight, script) + + return self.execute_script(script) + + def check_thin_dir(self): + """ + check if the thindir exists on the remote machine + """ + stdout, stderr, retcode = self.shell.exec_cmd( + "test -d {0}".format(self.thin_dir) + ) + if retcode != 0: + return False + return True def deploy(self): - ''' + """ Deploy salt-thin - ''' + """ self.shell.send( - self.thin, - os.path.join(self.thin_dir, 'salt-thin.tgz'), + self.thin, os.path.join(self.thin_dir, "salt-thin.tgz"), ) self.deploy_ext() return True def deploy_ext(self): - ''' + """ Deploy the ext_mods tarball - ''' - if self.mods.get('file'): + """ + if self.mods.get("file"): self.shell.send( - self.mods['file'], - os.path.join(self.thin_dir, 'salt-ext_mods.tgz'), + self.mods["file"], os.path.join(self.thin_dir, "salt-ext_mods.tgz"), ) return True def run(self, deploy_attempted=False): - ''' + """ Execute the routine, the routine can be either: 1. Execute a raw shell command 2. Execute a wrapper func @@ -987,11 +1036,39 @@ class Single(object): attempt Returns tuple of (stdout, stderr, retcode) - ''' + """ stdout = stderr = retcode = None - if self.opts.get('raw_shell', False): - cmd_str = ' '.join([self._escape_arg(arg) for arg in self.argv]) + if self.ssh_pre_flight: + if not self.opts.get("ssh_run_pre_flight", False) and self.check_thin_dir(): + log.info( + "{0} thin dir already exists. Not running ssh_pre_flight script".format( + self.thin_dir + ) + ) + elif not os.path.exists(self.ssh_pre_flight): + log.error( + "The ssh_pre_flight script {0} does not exist".format( + self.ssh_pre_flight + ) + ) + else: + stdout, stderr, retcode = self.run_ssh_pre_flight() + if stderr: + log.error( + "Error running ssh_pre_flight script {0}".format( + self.ssh_pre_file + ) + ) + return stdout, stderr, retcode + log.info( + "Successfully ran the ssh_pre_flight script: {0}".format( + self.ssh_pre_file + ) + ) + + if self.opts.get("raw_shell", False): + cmd_str = " ".join([self._escape_arg(arg) for arg in self.argv]) stdout, stderr, retcode = self.shell.exec_cmd(cmd_str) elif self.fun in self.wfuncs or self.mine: @@ -1003,33 +1080,33 @@ class Single(object): return stdout, stderr, retcode def run_wfunc(self): - ''' + """ Execute a wrapper function Returns tuple of (json_data, '') - ''' + """ # Ensure that opts/grains are up to date # Execute routine data_cache = False data = None - cdir = os.path.join(self.opts['cachedir'], 'minions', self.id) + cdir = os.path.join(self.opts["cachedir"], "minions", self.id) if not os.path.isdir(cdir): os.makedirs(cdir) - datap = os.path.join(cdir, 'ssh_data.p') + datap = os.path.join(cdir, "ssh_data.p") refresh = False if not os.path.isfile(datap): refresh = True else: passed_time = (time.time() - os.stat(datap).st_mtime) / 60 - if passed_time > self.opts.get('cache_life', 60): + if passed_time > self.opts.get("cache_life", 60): refresh = True - if self.opts.get('refresh_cache'): + if self.opts.get("refresh_cache"): refresh = True conf_grains = {} # Save conf file grains before they get clobbered - if 'ssh_grains' in self.opts: - conf_grains = self.opts['ssh_grains'] + if "ssh_grains" in self.opts: + conf_grains = self.opts["ssh_grains"] if not data_cache: refresh = True if refresh: @@ -1040,81 +1117,83 @@ class Single(object): self.id, fsclient=self.fsclient, minion_opts=self.minion_opts, - **self.target) - opts_pkg = pre_wrapper['test.opts_pkg']() # pylint: disable=E1102 - if '_error' in opts_pkg: - #Refresh failed - retcode = opts_pkg['retcode'] - ret = salt.utils.json.dumps({'local': opts_pkg}) + **self.target + ) + opts_pkg = pre_wrapper["test.opts_pkg"]() # pylint: disable=E1102 + if "_error" in opts_pkg: + # Refresh failed + retcode = opts_pkg["retcode"] + ret = salt.utils.json.dumps({"local": opts_pkg}) return ret, retcode - opts_pkg['file_roots'] = self.opts['file_roots'] - opts_pkg['pillar_roots'] = self.opts['pillar_roots'] - opts_pkg['ext_pillar'] = self.opts['ext_pillar'] - opts_pkg['extension_modules'] = self.opts['extension_modules'] - opts_pkg['module_dirs'] = self.opts['module_dirs'] - opts_pkg['_ssh_version'] = self.opts['_ssh_version'] - opts_pkg['__master_opts__'] = self.context['master_opts'] - if 'known_hosts_file' in self.opts: - opts_pkg['known_hosts_file'] = self.opts['known_hosts_file'] - if '_caller_cachedir' in self.opts: - opts_pkg['_caller_cachedir'] = self.opts['_caller_cachedir'] + opts_pkg["file_roots"] = self.opts["file_roots"] + opts_pkg["pillar_roots"] = self.opts["pillar_roots"] + opts_pkg["ext_pillar"] = self.opts["ext_pillar"] + opts_pkg["extension_modules"] = self.opts["extension_modules"] + opts_pkg["module_dirs"] = self.opts["module_dirs"] + opts_pkg["_ssh_version"] = self.opts["_ssh_version"] + opts_pkg["__master_opts__"] = self.context["master_opts"] + if "known_hosts_file" in self.opts: + opts_pkg["known_hosts_file"] = self.opts["known_hosts_file"] + if "_caller_cachedir" in self.opts: + opts_pkg["_caller_cachedir"] = self.opts["_caller_cachedir"] else: - opts_pkg['_caller_cachedir'] = self.opts['cachedir'] + opts_pkg["_caller_cachedir"] = self.opts["cachedir"] # Use the ID defined in the roster file - opts_pkg['id'] = self.id + opts_pkg["id"] = self.id retcode = 0 # Restore master grains for grain in conf_grains: - opts_pkg['grains'][grain] = conf_grains[grain] + opts_pkg["grains"][grain] = conf_grains[grain] # Enable roster grains support - if 'grains' in self.target: - for grain in self.target['grains']: - opts_pkg['grains'][grain] = self.target['grains'][grain] + if "grains" in self.target: + for grain in self.target["grains"]: + opts_pkg["grains"][grain] = self.target["grains"][grain] popts = {} - popts.update(opts_pkg['__master_opts__']) + popts.update(opts_pkg["__master_opts__"]) popts.update(opts_pkg) pillar = salt.pillar.Pillar( - popts, - opts_pkg['grains'], - opts_pkg['id'], - opts_pkg.get('saltenv', 'base') - ) + popts, + opts_pkg["grains"], + opts_pkg["id"], + opts_pkg.get("saltenv", "base"), + ) pillar_data = pillar.compile_pillar() # TODO: cache minion opts in datap in master.py - data = {'opts': opts_pkg, - 'grains': opts_pkg['grains'], - 'pillar': pillar_data} + data = { + "opts": opts_pkg, + "grains": opts_pkg["grains"], + "pillar": pillar_data, + } if data_cache: - with salt.utils.files.fopen(datap, 'w+b') as fp_: - fp_.write( - self.serial.dumps(data) - ) + with salt.utils.files.fopen(datap, "w+b") as fp_: + fp_.write(self.serial.dumps(data)) if not data and data_cache: - with salt.utils.files.fopen(datap, 'rb') as fp_: + with salt.utils.files.fopen(datap, "rb") as fp_: data = self.serial.load(fp_) - opts = data.get('opts', {}) - opts['grains'] = data.get('grains') + opts = data.get("opts", {}) + opts["grains"] = data.get("grains") # Restore master grains for grain in conf_grains: - opts['grains'][grain] = conf_grains[grain] + opts["grains"][grain] = conf_grains[grain] # Enable roster grains support - if 'grains' in self.target: - for grain in self.target['grains']: - opts['grains'][grain] = self.target['grains'][grain] + if "grains" in self.target: + for grain in self.target["grains"]: + opts["grains"][grain] = self.target["grains"][grain] - opts['pillar'] = data.get('pillar') + opts["pillar"] = data.get("pillar") wrapper = salt.client.ssh.wrapper.FunctionWrapper( opts, self.id, fsclient=self.fsclient, minion_opts=self.minion_opts, - **self.target) + **self.target + ) self.wfuncs = salt.loader.ssh_wrapper(opts, wrapper, self.context) wrapper.wfuncs = self.wfuncs @@ -1127,18 +1206,20 @@ class Single(object): if self.mine_functions and self.fun in self.mine_functions: mine_fun_data = self.mine_functions[self.fun] - elif opts['pillar'] and self.fun in opts['pillar'].get('mine_functions', {}): - mine_fun_data = opts['pillar']['mine_functions'][self.fun] - elif self.fun in self.context['master_opts'].get('mine_functions', {}): - mine_fun_data = self.context['master_opts']['mine_functions'][self.fun] + elif opts["pillar"] and self.fun in opts["pillar"].get( + "mine_functions", {} + ): + mine_fun_data = opts["pillar"]["mine_functions"][self.fun] + elif self.fun in self.context["master_opts"].get("mine_functions", {}): + mine_fun_data = self.context["master_opts"]["mine_functions"][self.fun] if isinstance(mine_fun_data, dict): - mine_fun = mine_fun_data.pop('mine_function', mine_fun) + mine_fun = mine_fun_data.pop("mine_function", mine_fun) mine_args = mine_fun_data elif isinstance(mine_fun_data, list): for item in mine_fun_data[:]: - if isinstance(item, dict) and 'mine_function' in item: - mine_fun = item['mine_function'] + if isinstance(item, dict) and "mine_function" in item: + mine_fun = item["mine_function"] mine_fun_data.pop(mine_fun_data.index(item)) mine_args = mine_fun_data else: @@ -1158,37 +1239,42 @@ class Single(object): else: result = self.wfuncs[self.fun](*self.args, **self.kwargs) except TypeError as exc: - result = 'TypeError encountered executing {0}: {1}'.format(self.fun, exc) + result = "TypeError encountered executing {0}: {1}".format(self.fun, exc) log.error(result, exc_info_on_loglevel=logging.DEBUG) retcode = 1 except Exception as exc: # pylint: disable=broad-except - result = 'An Exception occurred while executing {0}: {1}'.format(self.fun, exc) + result = "An Exception occurred while executing {0}: {1}".format( + self.fun, exc + ) log.error(result, exc_info_on_loglevel=logging.DEBUG) retcode = 1 # Mimic the json data-structure that "salt-call --local" will # emit (as seen in ssh_py_shim.py) - if isinstance(result, dict) and 'local' in result: - ret = salt.utils.json.dumps({'local': result['local']}) + if isinstance(result, dict) and "local" in result: + ret = salt.utils.json.dumps({"local": result["local"]}) else: - ret = salt.utils.json.dumps({'local': {'return': result}}) + ret = salt.utils.json.dumps({"local": {"return": result}}) return ret, retcode def _cmd_str(self): - ''' + """ Prepare the command string - ''' - sudo = 'sudo' if self.target['sudo'] else '' - sudo_user = self.target['sudo_user'] - if '_caller_cachedir' in self.opts: - cachedir = self.opts['_caller_cachedir'] + """ + sudo = "sudo" if self.target["sudo"] else "" + sudo_user = self.target["sudo_user"] + if "_caller_cachedir" in self.opts: + cachedir = self.opts["_caller_cachedir"] else: - cachedir = self.opts['cachedir'] - thin_code_digest, thin_sum = salt.utils.thin.thin_sum(cachedir, 'sha1') - debug = '' - if not self.opts.get('log_level'): - self.opts['log_level'] = 'info' - if salt.log.LOG_LEVELS['debug'] >= salt.log.LOG_LEVELS[self.opts.get('log_level', 'info')]: - debug = '1' + cachedir = self.opts["cachedir"] + thin_code_digest, thin_sum = salt.utils.thin.thin_sum(cachedir, "sha1") + debug = "" + if not self.opts.get("log_level"): + self.opts["log_level"] = "info" + if ( + salt.log.LOG_LEVELS["debug"] + >= salt.log.LOG_LEVELS[self.opts.get("log_level", "info")] + ): + debug = "1" arg_str = ''' OPTIONS.config = \ """ @@ -1204,23 +1290,25 @@ OPTIONS.wipe = {wipe} OPTIONS.tty = {tty} OPTIONS.cmd_umask = {cmd_umask} OPTIONS.code_checksum = {code_checksum} -ARGS = {arguments}\n'''.format(config=self.minion_config, - delimeter=RSTR, - saltdir=self.thin_dir, - checksum=thin_sum, - hashfunc='sha1', - version=salt.version.__version__, - ext_mods=self.mods.get('version', ''), - wipe=self.wipe, - tty=self.tty, - cmd_umask=self.cmd_umask, - code_checksum=thin_code_digest, - arguments=self.argv) - py_code = SSH_PY_SHIM.replace('#%%OPTS', arg_str) +ARGS = {arguments}\n'''.format( + config=self.minion_config, + delimeter=RSTR, + saltdir=self.thin_dir, + checksum=thin_sum, + hashfunc="sha1", + version=salt.version.__version__, + ext_mods=self.mods.get("version", ""), + wipe=self.wipe, + tty=self.tty, + cmd_umask=self.cmd_umask, + code_checksum=thin_code_digest, + arguments=self.argv, + ) + py_code = SSH_PY_SHIM.replace("#%%OPTS", arg_str) if six.PY2: - py_code_enc = py_code.encode('base64') + py_code_enc = py_code.encode("base64") else: - py_code_enc = base64.encodebytes(py_code.encode('utf-8')).decode('utf-8') + py_code_enc = base64.encodebytes(py_code.encode("utf-8")).decode("utf-8") if not self.winrm: cmd = SSH_SH_SHIM.format( DEBUG=debug, @@ -1228,32 +1316,52 @@ ARGS = {arguments}\n'''.format(config=self.minion_config, SUDO_USER=sudo_user, SSH_PY_CODE=py_code_enc, HOST_PY_MAJOR=sys.version_info[0], + SET_PATH=self.set_path, ) else: cmd = saltwinshell.gen_shim(py_code_enc) return cmd - def shim_cmd(self, cmd_str, extension='py'): - ''' + def execute_script(self, script, extension="py", pre_dir=""): + """ + execute a script on the minion then delete + """ + if extension == "ps1": + ret = self.shell.exec_cmd('"powershell {0}"'.format(script)) + else: + if not self.winrm: + ret = self.shell.exec_cmd("/bin/sh '{0}{1}'".format(pre_dir, script)) + else: + ret = saltwinshell.call_python(self, script) + + # Remove file from target system + if not self.winrm: + self.shell.exec_cmd("rm '{0}{1}'".format(pre_dir, script)) + else: + self.shell.exec_cmd("del {0}".format(script)) + + return ret + + def shim_cmd(self, cmd_str, extension="py"): + """ Run a shim command. If tty is enabled, we must scp the shim to the target system and execute it there - ''' + """ if not self.tty and not self.winrm: return self.shell.exec_cmd(cmd_str) # Write the shim to a temporary file in the default temp directory - with tempfile.NamedTemporaryFile(mode='w+b', - prefix='shim_', - delete=False) as shim_tmp_file: + with tempfile.NamedTemporaryFile( + mode="w+b", prefix="shim_", delete=False + ) as shim_tmp_file: shim_tmp_file.write(salt.utils.stringutils.to_bytes(cmd_str)) # Copy shim to target system, under $HOME/. - target_shim_file = '.{0}.{1}'.format( - binascii.hexlify(os.urandom(6)).decode('ascii'), - extension + target_shim_file = ".{0}.{1}".format( + binascii.hexlify(os.urandom(6)).decode("ascii"), extension ) if self.winrm: target_shim_file = saltwinshell.get_target_shim_file(self, target_shim_file) @@ -1265,25 +1373,14 @@ ARGS = {arguments}\n'''.format(config=self.minion_config, except IOError: pass - # Execute shim - if extension == 'ps1': - ret = self.shell.exec_cmd('"powershell {0}"'.format(target_shim_file)) - else: - if not self.winrm: - ret = self.shell.exec_cmd('/bin/sh \'$HOME/{0}\''.format(target_shim_file)) - else: - ret = saltwinshell.call_python(self, target_shim_file) - - # Remove shim from target system - if not self.winrm: - self.shell.exec_cmd('rm \'$HOME/{0}\''.format(target_shim_file)) - else: - self.shell.exec_cmd('del {0}'.format(target_shim_file)) + ret = self.execute_script( + script=target_shim_file, extension=extension, pre_dir="$HOME/" + ) return ret def cmd_block(self, is_retry=False): - ''' + """ Prepare the pre-check command to send to the subsystem 1. execute SHIM + command @@ -1292,41 +1389,47 @@ ARGS = {arguments}\n'''.format(config=self.minion_config, 4. re-execute SHIM + command 5. split SHIM results from command results 6. return command results - ''' + """ self.argv = _convert_args(self.argv) log.debug( - 'Performing shimmed, blocking command as follows:\n%s', - ' '.join([six.text_type(arg) for arg in self.argv]) + "Performing shimmed, blocking command as follows:\n%s", + " ".join([six.text_type(arg) for arg in self.argv]), ) cmd_str = self._cmd_str() stdout, stderr, retcode = self.shim_cmd(cmd_str) - log.trace('STDOUT %s\n%s', self.target['host'], stdout) - log.trace('STDERR %s\n%s', self.target['host'], stderr) - log.debug('RETCODE %s: %s', self.target['host'], retcode) + log.trace("STDOUT %s\n%s", self.target["host"], stdout) + log.trace("STDERR %s\n%s", self.target["host"], stderr) + log.debug("RETCODE %s: %s", self.target["host"], retcode) error = self.categorize_shim_errors(stdout, stderr, retcode) if error: - if error == 'Python environment not found on Windows system': + if error == "Python environment not found on Windows system": saltwinshell.deploy_python(self) stdout, stderr, retcode = self.shim_cmd(cmd_str) while re.search(RSTR_RE, stdout): stdout = re.split(RSTR_RE, stdout, 1)[1].strip() while re.search(RSTR_RE, stderr): stderr = re.split(RSTR_RE, stderr, 1)[1].strip() - elif error == 'Undefined SHIM state': + elif error == "Undefined SHIM state": self.deploy() stdout, stderr, retcode = self.shim_cmd(cmd_str) if not re.search(RSTR_RE, stdout) or not re.search(RSTR_RE, stderr): # If RSTR is not seen in both stdout and stderr then there # was a thin deployment problem. - return 'ERROR: Failure deploying thin, undefined state: {0}'.format(stdout), stderr, retcode + return ( + "ERROR: Failure deploying thin, undefined state: {0}".format( + stdout + ), + stderr, + retcode, + ) while re.search(RSTR_RE, stdout): stdout = re.split(RSTR_RE, stdout, 1)[1].strip() while re.search(RSTR_RE, stderr): stderr = re.split(RSTR_RE, stderr, 1)[1].strip() else: - return 'ERROR: {0}'.format(error), stderr, retcode + return "ERROR: {0}".format(error), stderr, retcode # FIXME: this discards output from ssh_shim if the shim succeeds. It should # always save the shim output regardless of shim success or failure. @@ -1342,9 +1445,12 @@ ARGS = {arguments}\n'''.format(config=self.minion_config, else: # RSTR was found in stdout but not stderr - which means there # is a SHIM command for the master. - shim_command = re.split(r'\r?\n', stdout, 1)[0].strip() - log.debug('SHIM retcode(%s) and command: %s', retcode, shim_command) - if 'deploy' == shim_command and retcode == salt.defaults.exitcodes.EX_THIN_DEPLOY: + shim_command = re.split(r"\r?\n", stdout, 1)[0].strip() + log.debug("SHIM retcode(%s) and command: %s", retcode, shim_command) + if ( + "deploy" == shim_command + and retcode == salt.defaults.exitcodes.EX_THIN_DEPLOY + ): self.deploy() stdout, stderr, retcode = self.shim_cmd(cmd_str) if not re.search(RSTR_RE, stdout) or not re.search(RSTR_RE, stderr): @@ -1352,33 +1458,41 @@ ARGS = {arguments}\n'''.format(config=self.minion_config, # If RSTR is not seen in both stdout and stderr then there # was a thin deployment problem. log.error( - 'ERROR: Failure deploying thin, retrying:\n' - 'STDOUT:\n%s\nSTDERR:\n%s\nRETCODE: %s', - stdout, stderr, retcode + "ERROR: Failure deploying thin, retrying:\n" + "STDOUT:\n%s\nSTDERR:\n%s\nRETCODE: %s", + stdout, + stderr, + retcode, ) return self.cmd_block() elif not re.search(RSTR_RE, stdout): # If RSTR is not seen in stdout with tty, then there # was a thin deployment problem. log.error( - 'ERROR: Failure deploying thin, retrying:\n' - 'STDOUT:\n%s\nSTDERR:\n%s\nRETCODE: %s', - stdout, stderr, retcode + "ERROR: Failure deploying thin, retrying:\n" + "STDOUT:\n%s\nSTDERR:\n%s\nRETCODE: %s", + stdout, + stderr, + retcode, ) while re.search(RSTR_RE, stdout): stdout = re.split(RSTR_RE, stdout, 1)[1].strip() if self.tty: - stderr = '' + stderr = "" else: while re.search(RSTR_RE, stderr): stderr = re.split(RSTR_RE, stderr, 1)[1].strip() - elif 'ext_mods' == shim_command: + elif "ext_mods" == shim_command: self.deploy_ext() stdout, stderr, retcode = self.shim_cmd(cmd_str) if not re.search(RSTR_RE, stdout) or not re.search(RSTR_RE, stderr): # If RSTR is not seen in both stdout and stderr then there # was a thin deployment problem. - return 'ERROR: Failure deploying ext_mods: {0}'.format(stdout), stderr, retcode + return ( + "ERROR: Failure deploying ext_mods: {0}".format(stdout), + stderr, + retcode, + ) while re.search(RSTR_RE, stdout): stdout = re.split(RSTR_RE, stdout, 1)[1].strip() while re.search(RSTR_RE, stderr): @@ -1389,7 +1503,7 @@ ARGS = {arguments}\n'''.format(config=self.minion_config, def categorize_shim_errors(self, stdout_bytes, stderr_bytes, retcode): stdout = salt.utils.stringutils.to_unicode(stdout_bytes) stderr = salt.utils.stringutils.to_unicode(stderr_bytes) - if re.search(RSTR_RE, stdout) and stdout != RSTR+'\n': + if re.search(RSTR_RE, stdout) and stdout != RSTR + "\n": # RSTR was found in stdout which means that the shim # functioned without *errors* . . . but there may be shim # commands, unless the only thing we found is RSTR @@ -1397,99 +1511,93 @@ ARGS = {arguments}\n'''.format(config=self.minion_config, if re.search(RSTR_RE, stderr): # Undefined state - return 'Undefined SHIM state' + return "Undefined SHIM state" - if stderr.startswith('Permission denied'): + if stderr.startswith("Permission denied"): # SHIM was not even reached return None - perm_error_fmt = 'Permissions problem, target user may need '\ - 'to be root or use sudo:\n {0}' + perm_error_fmt = ( + "Permissions problem, target user may need " "to be root or use sudo:\n {0}" + ) def _version_mismatch_error(): messages = { 2: { - 6: 'Install Python 2.7 / Python 3 Salt dependencies on the Salt SSH master \n' - 'to interact with Python 2.7 / Python 3 targets', - 7: 'Install Python 2.6 / Python 3 Salt dependencies on the Salt SSH master \n' - 'to interact with Python 2.6 / Python 3 targets', + 6: "Install Python 2.7 / Python 3 Salt dependencies on the Salt SSH master \n" + "to interact with Python 2.7 / Python 3 targets", + 7: "Install Python 2.6 / Python 3 Salt dependencies on the Salt SSH master \n" + "to interact with Python 2.6 / Python 3 targets", }, 3: { - 'default': '- Install Python 2.6/2.7 Salt dependencies on the Salt SSH \n' - ' master to interact with Python 2.6/2.7 targets\n' - '- Install Python 3 on the target machine(s)', + "default": "- Install Python 2.6/2.7 Salt dependencies on the Salt SSH \n" + " master to interact with Python 2.6/2.7 targets\n" + "- Install Python 3 on the target machine(s)", }, - 'default': 'Matching major/minor Python release (>=2.6) needed both on the Salt SSH \n' - 'master and target machine', + "default": "Matching major/minor Python release (>=2.6) needed both on the Salt SSH \n" + "master and target machine", } major, minor = sys.version_info[:2] help_msg = ( messages.get(major, {}).get(minor) - or messages.get(major, {}).get('default') - or messages['default'] + or messages.get(major, {}).get("default") + or messages["default"] ) - return 'Python version error. Recommendation(s) follow:\n' + help_msg + return "Python version error. Recommendation(s) follow:\n" + help_msg errors = [ ( (), - 'sudo: no tty present and no askpass program specified', - 'sudo expected a password, NOPASSWD required' + "sudo: no tty present and no askpass program specified", + "sudo expected a password, NOPASSWD required", ), ( (salt.defaults.exitcodes.EX_THIN_PYTHON_INVALID,), - 'Python interpreter is too old', - _version_mismatch_error() + "Python interpreter is too old", + _version_mismatch_error(), ), ( (salt.defaults.exitcodes.EX_THIN_CHECKSUM,), - 'checksum mismatched', - 'The salt thin transfer was corrupted' + "checksum mismatched", + "The salt thin transfer was corrupted", ), ( (salt.defaults.exitcodes.EX_SCP_NOT_FOUND,), - 'scp not found', - 'No scp binary. openssh-clients package required' + "scp not found", + "No scp binary. openssh-clients package required", ), ( (salt.defaults.exitcodes.EX_CANTCREAT,), - 'salt path .* exists but is not a directory', - 'A necessary path for salt thin unexpectedly exists:\n ' + stderr, + "salt path .* exists but is not a directory", + "A necessary path for salt thin unexpectedly exists:\n " + stderr, ), ( (), - 'sudo: sorry, you must have a tty to run sudo', - 'sudo is configured with requiretty' + "sudo: sorry, you must have a tty to run sudo", + "sudo is configured with requiretty", ), + ((), "Failed to open log file", perm_error_fmt.format(stderr)), + ((), "Permission denied:.*/salt", perm_error_fmt.format(stderr)), ( (), - 'Failed to open log file', - perm_error_fmt.format(stderr) - ), - ( - (), - 'Permission denied:.*/salt', - perm_error_fmt.format(stderr) - ), - ( - (), - 'Failed to create directory path.*/salt', - perm_error_fmt.format(stderr) + "Failed to create directory path.*/salt", + perm_error_fmt.format(stderr), ), ( (salt.defaults.exitcodes.EX_SOFTWARE,), - 'exists but is not', - 'An internal error occurred with the shim, please investigate:\n ' + stderr, + "exists but is not", + "An internal error occurred with the shim, please investigate:\n " + + stderr, ), ( (), - 'The system cannot find the path specified', - 'Python environment not found on Windows system', + "The system cannot find the path specified", + "Python environment not found on Windows system", ), ( (), - 'is not recognized', - 'Python environment not found on Windows system', + "is not recognized", + "Python environment not found on Windows system", ), ] @@ -1499,32 +1607,32 @@ ARGS = {arguments}\n'''.format(config=self.minion_config, return None def check_refresh(self, data, ret): - ''' + """ Stub out check_refresh - ''' + """ return def module_refresh(self): - ''' + """ Module refresh is not needed, stub it out - ''' + """ return def lowstate_file_refs(chunks): - ''' + """ Create a list of file ref objects to reconcile - ''' + """ refs = {} for chunk in chunks: - saltenv = 'base' + saltenv = "base" crefs = [] for state in chunk: - if state == '__env__': + if state == "__env__": saltenv = chunk[state] - elif state == 'saltenv': + elif state == "saltenv": saltenv = chunk[state] - elif state.startswith('__'): + elif state.startswith("__"): continue crefs.extend(salt_refs(chunk[state])) if crefs: @@ -1535,10 +1643,10 @@ def lowstate_file_refs(chunks): def salt_refs(data): - ''' + """ Pull salt file references out of the states - ''' - proto = 'salt://' + """ + proto = "salt://" ret = [] if isinstance(data, six.string_types): if data.startswith(proto): @@ -1552,28 +1660,28 @@ def salt_refs(data): def mod_data(fsclient): - ''' + """ Generate the module arguments for the shim data - ''' + """ # TODO, change out for a fileserver backend sync_refs = [ - 'modules', - 'states', - 'grains', - 'renderers', - 'returners', - ] + "modules", + "states", + "grains", + "renderers", + "returners", + ] ret = {} envs = fsclient.envs() - ver_base = '' + ver_base = "" for env in envs: files = fsclient.file_list(env) for ref in sync_refs: mods_data = {} - pref = '_{0}'.format(ref) + pref = "_{0}".format(ref) for fn_ in sorted(files): if fn_.startswith(pref): - if fn_.endswith(('.py', '.so', '.pyx')): + if fn_.endswith((".py", ".so", ".pyx")): full = salt.utils.url.create(fn_) mod_path = fsclient.cache_file(full, env) if not os.path.isfile(mod_path): @@ -1594,17 +1702,16 @@ def mod_data(fsclient): ver = hashlib.sha1(ver_base).hexdigest() ext_tar_path = os.path.join( - fsclient.opts['cachedir'], - 'ext_mods.{0}.tgz'.format(ver)) - mods = {'version': ver, - 'file': ext_tar_path} + fsclient.opts["cachedir"], "ext_mods.{0}.tgz".format(ver) + ) + mods = {"version": ver, "file": ext_tar_path} if os.path.isfile(ext_tar_path): return mods - tfp = tarfile.open(ext_tar_path, 'w:gz') - verfile = os.path.join(fsclient.opts['cachedir'], 'ext_mods.ver') - with salt.utils.files.fopen(verfile, 'w+') as fp_: + tfp = tarfile.open(ext_tar_path, "w:gz") + verfile = os.path.join(fsclient.opts["cachedir"], "ext_mods.ver") + with salt.utils.files.fopen(verfile, "w+") as fp_: fp_.write(ver) - tfp.add(verfile, 'ext_version') + tfp.add(verfile, "ext_version") for ref in ret: for fn_ in ret[ref]: tfp.add(ret[ref][fn_], os.path.join(ref, fn_)) @@ -1613,17 +1720,16 @@ def mod_data(fsclient): def ssh_version(): - ''' + """ Returns the version of the installed ssh command - ''' + """ # This function needs more granular checks and to be validated against # older versions of ssh ret = subprocess.Popen( - ['ssh', '-V'], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate() + ["ssh", "-V"], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ).communicate() try: - version_parts = ret[1].split(b',')[0].split(b'_')[1] + version_parts = ret[1].split(b",")[0].split(b"_")[1] parts = [] for part in version_parts: try: @@ -1636,17 +1742,17 @@ def ssh_version(): def _convert_args(args): - ''' + """ Take a list of args, and convert any dicts inside the list to keyword args in the form of `key=value`, ready to be passed to salt-ssh - ''' + """ converted = [] for arg in args: if isinstance(arg, dict): for key in list(arg.keys()): - if key == '__kwarg__': + if key == "__kwarg__": continue - converted.append('{0}={1}'.format(key, arg[key])) + converted.append("{0}={1}".format(key, arg[key])) else: converted.append(arg) return converted diff --git a/salt/client/ssh/client.py b/salt/client/ssh/client.py index e8e634ca12d..f9d0c5fc869 100644 --- a/salt/client/ssh/client.py +++ b/salt/client/ssh/client.py @@ -2,123 +2,104 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import copy import logging +import os import random # Import Salt libs import salt.config -import salt.utils.args import salt.syspaths as syspaths +import salt.utils.args from salt.exceptions import SaltClientError # Temporary log = logging.getLogger(__name__) class SSHClient(object): - ''' + """ Create a client object for executing routines via the salt-ssh backend .. versionadded:: 2015.5.0 - ''' - def __init__(self, - c_path=os.path.join(syspaths.CONFIG_DIR, 'master'), - mopts=None, - disable_custom_roster=False): + """ + + def __init__( + self, + c_path=os.path.join(syspaths.CONFIG_DIR, "master"), + mopts=None, + disable_custom_roster=False, + ): if mopts: self.opts = mopts else: if os.path.isdir(c_path): log.warning( - '%s expects a file path not a directory path(%s) to ' - 'its \'c_path\' keyword argument', - self.__class__.__name__, c_path + "%s expects a file path not a directory path(%s) to " + "its 'c_path' keyword argument", + self.__class__.__name__, + c_path, ) self.opts = salt.config.client_config(c_path) # Salt API should never offer a custom roster! - self.opts['__disable_custom_roster'] = disable_custom_roster + self.opts["__disable_custom_roster"] = disable_custom_roster def _prep_ssh( - self, - tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - kwarg=None, - **kwargs): - ''' + self, tgt, fun, arg=(), timeout=None, tgt_type="glob", kwarg=None, **kwargs + ): + """ Prepare the arguments - ''' + """ opts = copy.deepcopy(self.opts) opts.update(kwargs) if timeout: - opts['timeout'] = timeout + opts["timeout"] = timeout arg = salt.utils.args.condition_input(arg, kwarg) - opts['argv'] = [fun] + arg - opts['selected_target_option'] = tgt_type - opts['tgt'] = tgt - opts['arg'] = arg + opts["argv"] = [fun] + arg + opts["selected_target_option"] = tgt_type + opts["tgt"] = tgt + opts["arg"] = arg return salt.client.ssh.SSH(opts) def cmd_iter( - self, - tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - kwarg=None, - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + kwarg=None, + **kwargs + ): + """ Execute a single command via the salt-ssh subsystem and return a generator .. versionadded:: 2015.5.0 - ''' - ssh = self._prep_ssh( - tgt, - fun, - arg, - timeout, - tgt_type, - kwarg, - **kwargs) - for ret in ssh.run_iter(jid=kwargs.get('jid', None)): + """ + ssh = self._prep_ssh(tgt, fun, arg, timeout, tgt_type, kwarg, **kwargs) + for ret in ssh.run_iter(jid=kwargs.get("jid", None)): yield ret - def cmd(self, - tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - kwarg=None, - **kwargs): - ''' + def cmd( + self, tgt, fun, arg=(), timeout=None, tgt_type="glob", kwarg=None, **kwargs + ): + """ Execute a single command via the salt-ssh subsystem and return all routines at once .. versionadded:: 2015.5.0 - ''' - ssh = self._prep_ssh( - tgt, - fun, - arg, - timeout, - tgt_type, - kwarg, - **kwargs) + """ + ssh = self._prep_ssh(tgt, fun, arg, timeout, tgt_type, kwarg, **kwargs) final = {} - for ret in ssh.run_iter(jid=kwargs.get('jid', None)): + for ret in ssh.run_iter(jid=kwargs.get("jid", None)): final.update(ret) return final def cmd_sync(self, low): - ''' + """ Execute a salt-ssh call synchronously. .. versionadded:: 2015.5.0 @@ -135,24 +116,26 @@ class SSHClient(object): 'kwarg'={} }) {'silver': {'fun_args': [], 'jid': '20141202152721523072', 'return': True, 'retcode': 0, 'success': True, 'fun': 'test.ping', 'id': 'silver'}} - ''' + """ kwargs = copy.deepcopy(low) - for ignore in ['tgt', 'fun', 'arg', 'timeout', 'tgt_type', 'kwarg']: + for ignore in ["tgt", "fun", "arg", "timeout", "tgt_type", "kwarg"]: if ignore in kwargs: del kwargs[ignore] - return self.cmd(low['tgt'], - low['fun'], - low.get('arg', []), - low.get('timeout'), - low.get('tgt_type'), - low.get('kwarg'), - **kwargs) + return self.cmd( + low["tgt"], + low["fun"], + low.get("arg", []), + low.get("timeout"), + low.get("tgt_type"), + low.get("kwarg"), + **kwargs + ) def cmd_async(self, low, timeout=None): - ''' + """ Execute aa salt-ssh asynchronously WARNING: Eauth is **NOT** respected @@ -167,22 +150,23 @@ class SSHClient(object): 'kwarg'={} }) {'silver': {'fun_args': [], 'jid': '20141202152721523072', 'return': True, 'retcode': 0, 'success': True, 'fun': 'test.ping', 'id': 'silver'}} - ''' + """ # TODO Not implemented raise SaltClientError def cmd_subset( - self, - tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - kwarg=None, - sub=3, - **kwargs): - ''' + self, + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + kwarg=None, + sub=3, + **kwargs + ): + """ Execute a command on a random subset of the targeted systems The function signature is the same as :py:meth:`cmd` with the @@ -198,17 +182,16 @@ class SSHClient(object): {'jerry': True} .. versionadded:: 2017.7.0 - ''' - minion_ret = self.cmd(tgt, - 'sys.list_functions', - tgt_type=tgt_type, - **kwargs) + """ + minion_ret = self.cmd(tgt, "sys.list_functions", tgt_type=tgt_type, **kwargs) minions = list(minion_ret) random.shuffle(minions) f_tgt = [] for minion in minions: - if fun in minion_ret[minion]['return']: + if fun in minion_ret[minion]["return"]: f_tgt.append(minion) if len(f_tgt) >= sub: break - return self.cmd_iter(f_tgt, fun, arg, timeout, tgt_type='list', ret=ret, kwarg=kwarg, **kwargs) + return self.cmd_iter( + f_tgt, fun, arg, timeout, tgt_type="list", ret=ret, kwarg=kwarg, **kwargs + ) diff --git a/salt/client/ssh/shell.py b/salt/client/ssh/shell.py index bd55c514ee0..d62aa8f11a8 100644 --- a/salt/client/ssh/shell.py +++ b/salt/client/ssh/shell.py @@ -1,34 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Manage transport commands via ssh -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging +import os + # Import python libs import re -import os +import subprocess import sys import time -import logging -import subprocess # Import salt libs import salt.defaults.exitcodes import salt.utils.json import salt.utils.nb_popen import salt.utils.vt - from salt.ext import six log = logging.getLogger(__name__) -SSH_PASSWORD_PROMPT_RE = re.compile(r'(?:.*)[Pp]assword(?: for .*)?:', re.M) -KEY_VALID_RE = re.compile(r'.*\(yes\/no\).*') -SSH_PRIVATE_KEY_PASSWORD_PROMPT_RE = re.compile(r'Enter passphrase for key', re.M) +SSH_PASSWORD_PROMPT_RE = re.compile(r"(?:.*)[Pp]assword(?: for .*)?:", re.M) +KEY_VALID_RE = re.compile(r".*\(yes\/no\).*") +SSH_PRIVATE_KEY_PASSWORD_PROMPT_RE = re.compile(r"Enter passphrase for key", re.M) # Keep these in sync with ./__init__.py -RSTR = '_edbc7885e4f9aac9b83b35999b68d015148caf467b78fa39c05f669c0ff89878' -RSTR_RE = re.compile(r'(?:^|\r?\n)' + RSTR + r'(?:\r?\n|$)') +RSTR = "_edbc7885e4f9aac9b83b35999b68d015148caf467b78fa39c05f669c0ff89878" +RSTR_RE = re.compile(r"(?:^|\r?\n)" + RSTR + r"(?:\r?\n|$)") class NoPasswdError(Exception): @@ -40,9 +40,9 @@ class KeyAcceptError(Exception): def gen_key(path): - ''' + """ Generate a key for use with salt-ssh - ''' + """ cmd = 'ssh-keygen -P "" -f {0} -t rsa -q'.format(path) if not os.path.isdir(os.path.dirname(path)): os.makedirs(os.path.dirname(path)) @@ -50,15 +50,16 @@ def gen_key(path): def gen_shell(opts, **kwargs): - ''' + """ Return the correct shell interface for the target system - ''' - if kwargs['winrm']: + """ + if kwargs["winrm"]: try: import saltwinshell + shell = saltwinshell.Shell(opts, **kwargs) except ImportError: - log.error('The saltwinshell library is not available') + log.error("The saltwinshell library is not available") sys.exit(salt.defaults.exitcodes.EX_GENERIC) else: shell = Shell(opts, **kwargs) @@ -66,30 +67,32 @@ def gen_shell(opts, **kwargs): class Shell(object): - ''' + """ Create a shell connection object to encapsulate ssh executions - ''' + """ + def __init__( - self, - opts, - host, - user=None, - port=None, - passwd=None, - priv=None, - priv_passwd=None, - timeout=None, - sudo=False, - tty=False, - mods=None, - identities_only=False, - sudo_user=None, - remote_port_forwards=None, - winrm=False, - ssh_options=None): + self, + opts, + host, + user=None, + port=None, + passwd=None, + priv=None, + priv_passwd=None, + timeout=None, + sudo=False, + tty=False, + mods=None, + identities_only=False, + sudo_user=None, + remote_port_forwards=None, + winrm=False, + ssh_options=None, + ): self.opts = opts # ssh , but scp [ (4, 9): - options.append('GSSAPIAuthentication=no') - options.append('ConnectTimeout={0}'.format(self.timeout)) - if self.opts.get('ignore_host_keys'): - options.append('StrictHostKeyChecking=no') - if self.opts.get('no_host_keys'): - options.extend(['StrictHostKeyChecking=no', - 'UserKnownHostsFile=/dev/null']) - known_hosts = self.opts.get('known_hosts_file') + options.append("PasswordAuthentication=no") + if self.opts.get("_ssh_version", (0,)) > (4, 9): + options.append("GSSAPIAuthentication=no") + options.append("ConnectTimeout={0}".format(self.timeout)) + if self.opts.get("ignore_host_keys"): + options.append("StrictHostKeyChecking=no") + if self.opts.get("no_host_keys"): + options.extend(["StrictHostKeyChecking=no", "UserKnownHostsFile=/dev/null"]) + known_hosts = self.opts.get("known_hosts_file") if known_hosts and os.path.isfile(known_hosts): - options.append('UserKnownHostsFile={0}'.format(known_hosts)) + options.append("UserKnownHostsFile={0}".format(known_hosts)) if self.port: - options.append('Port={0}'.format(self.port)) - if self.priv and self.priv != 'agent-forwarding': - options.append('IdentityFile={0}'.format(self.priv)) + options.append("Port={0}".format(self.port)) + if self.priv and self.priv != "agent-forwarding": + options.append("IdentityFile={0}".format(self.priv)) if self.user: - options.append('User={0}'.format(self.user)) + options.append("User={0}".format(self.user)) if self.identities_only: - options.append('IdentitiesOnly=yes') + options.append("IdentitiesOnly=yes") ret = [] for option in options: - ret.append('-o {0} '.format(option)) - return ''.join(ret) + ret.append("-o {0} ".format(option)) + return "".join(ret) def _passwd_opts(self): - ''' + """ Return options to pass to ssh - ''' + """ # TODO ControlMaster does not work without ControlPath # user could take advantage of it if they set ControlPath in their # ssh config. Also, ControlPersist not widely available. - options = ['ControlMaster=auto', - 'StrictHostKeyChecking=no', - ] - if self.opts['_ssh_version'] > (4, 9): - options.append('GSSAPIAuthentication=no') - options.append('ConnectTimeout={0}'.format(self.timeout)) - if self.opts.get('ignore_host_keys'): - options.append('StrictHostKeyChecking=no') - if self.opts.get('no_host_keys'): - options.extend(['StrictHostKeyChecking=no', - 'UserKnownHostsFile=/dev/null']) + options = [ + "ControlMaster=auto", + "StrictHostKeyChecking=no", + ] + if self.opts["_ssh_version"] > (4, 9): + options.append("GSSAPIAuthentication=no") + options.append("ConnectTimeout={0}".format(self.timeout)) + if self.opts.get("ignore_host_keys"): + options.append("StrictHostKeyChecking=no") + if self.opts.get("no_host_keys"): + options.extend(["StrictHostKeyChecking=no", "UserKnownHostsFile=/dev/null"]) if self.passwd: - options.extend(['PasswordAuthentication=yes', - 'PubkeyAuthentication=yes']) + options.extend(["PasswordAuthentication=yes", "PubkeyAuthentication=yes"]) else: - options.extend(['PasswordAuthentication=no', - 'PubkeyAuthentication=yes', - 'KbdInteractiveAuthentication=no', - 'ChallengeResponseAuthentication=no', - 'BatchMode=yes']) + options.extend( + [ + "PasswordAuthentication=no", + "PubkeyAuthentication=yes", + "KbdInteractiveAuthentication=no", + "ChallengeResponseAuthentication=no", + "BatchMode=yes", + ] + ) if self.port: - options.append('Port={0}'.format(self.port)) + options.append("Port={0}".format(self.port)) if self.user: - options.append('User={0}'.format(self.user)) + options.append("User={0}".format(self.user)) if self.identities_only: - options.append('IdentitiesOnly=yes') + options.append("IdentitiesOnly=yes") ret = [] for option in options: - ret.append('-o {0} '.format(option)) - return ''.join(ret) + ret.append("-o {0} ".format(option)) + return "".join(ret) def _ssh_opts(self): - return ' '.join(['-o {0}'.format(opt) - for opt in self.ssh_options]) + return " ".join(["-o {0}".format(opt) for opt in self.ssh_options]) def _copy_id_str_old(self): - ''' + """ Return the string to execute ssh-copy-id - ''' + """ if self.passwd: # Using single quotes prevents shell expansion and # passwords containing '$' return "{0} {1} '{2} -p {3} {4} {5}@{6}'".format( - 'ssh-copy-id', - '-i {0}.pub'.format(self.priv), - self._passwd_opts(), - self.port, - self._ssh_opts(), - self.user, - self.host) + "ssh-copy-id", + "-i {0}.pub".format(self.priv), + self._passwd_opts(), + self.port, + self._ssh_opts(), + self.user, + self.host, + ) return None def _copy_id_str_new(self): - ''' + """ Since newer ssh-copy-id commands ingest option differently we need to have two commands - ''' + """ if self.passwd: # Using single quotes prevents shell expansion and # passwords containing '$' return "{0} {1} {2} -p {3} {4} {5}@{6}".format( - 'ssh-copy-id', - '-i {0}.pub'.format(self.priv), - self._passwd_opts(), - self.port, - self._ssh_opts(), - self.user, - self.host) + "ssh-copy-id", + "-i {0}.pub".format(self.priv), + self._passwd_opts(), + self.port, + self._ssh_opts(), + self.user, + self.host, + ) return None def copy_id(self): - ''' + """ Execute ssh-copy-id to plant the id file on the target - ''' + """ stdout, stderr, retcode = self._run_cmd(self._copy_id_str_old()) - if salt.defaults.exitcodes.EX_OK != retcode and 'Usage' in stderr: + if salt.defaults.exitcodes.EX_OK != retcode and "Usage" in stderr: stdout, stderr, retcode = self._run_cmd(self._copy_id_str_new()) return stdout, stderr, retcode - def _cmd_str(self, cmd, ssh='ssh'): - ''' + def _cmd_str(self, cmd, ssh="ssh"): + """ Return the cmd string to execute - ''' + """ # TODO: if tty, then our SSH_SHIM cannot be supplied from STDIN Will # need to deliver the SHIM to the remote host and execute it there command = [ssh] - if ssh != 'scp': + if ssh != "scp": command.append(self.host) - if self.tty and ssh == 'ssh': - command.append('-t -t') + if self.tty and ssh == "ssh": + command.append("-t -t") if self.passwd or self.priv: command.append(self.priv and self._key_opts() or self._passwd_opts()) - if ssh != 'scp' and self.remote_port_forwards: - command.append(' '.join(['-R {0}'.format(item) - for item in self.remote_port_forwards.split(',')])) + if ssh != "scp" and self.remote_port_forwards: + command.append( + " ".join( + [ + "-R {0}".format(item) + for item in self.remote_port_forwards.split(",") + ] + ) + ) if self.ssh_options: command.append(self._ssh_opts()) command.append(cmd) - return ' '.join(command) + return " ".join(command) def _old_run_cmd(self, cmd): - ''' + """ Cleanly execute the command string - ''' + """ try: proc = subprocess.Popen( - cmd, - shell=True, - stderr=subprocess.PIPE, - stdout=subprocess.PIPE, + cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, ) data = proc.communicate() return data[0], data[1], proc.returncode except Exception: # pylint: disable=broad-except - return ('local', 'Unknown Error', None) + return ("local", "Unknown Error", None) def _run_nb_cmd(self, cmd): - ''' + """ cmd iterator - ''' + """ try: proc = salt.utils.nb_popen.NonBlockingPopen( - cmd, - shell=True, - stderr=subprocess.PIPE, - stdout=subprocess.PIPE, + cmd, shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, ) while True: time.sleep(0.1) @@ -305,20 +311,20 @@ class Shell(object): err = self.get_error(err) yield out, err, rcode except Exception: # pylint: disable=broad-except - yield ('', 'Unknown Error', None) + yield ("", "Unknown Error", None) def exec_nb_cmd(self, cmd): - ''' + """ Yield None until cmd finished - ''' + """ r_out = [] r_err = [] rcode = None cmd = self._cmd_str(cmd) - logmsg = 'Executing non-blocking command: {0}'.format(cmd) + logmsg = "Executing non-blocking command: {0}".format(cmd) if self.passwd: - logmsg = logmsg.replace(self.passwd, ('*' * 6)) + logmsg = logmsg.replace(self.passwd, ("*" * 6)) log.debug(logmsg) for out, err, rcode in self._run_nb_cmd(cmd): @@ -327,19 +333,19 @@ class Shell(object): if err is not None: r_err.append(err) yield None, None, None - yield ''.join(r_out), ''.join(r_err), rcode + yield "".join(r_out), "".join(r_err), rcode def exec_cmd(self, cmd): - ''' + """ Execute a remote command - ''' + """ cmd = self._cmd_str(cmd) - logmsg = 'Executing command: {0}'.format(cmd) + logmsg = "Executing command: {0}".format(cmd) if self.passwd: - logmsg = logmsg.replace(self.passwd, ('*' * 6)) - if 'decode("base64")' in logmsg or 'base64.b64decode(' in logmsg: - log.debug('Executed SHIM command. Command logged to TRACE') + logmsg = logmsg.replace(self.passwd, ("*" * 6)) + if 'decode("base64")' in logmsg or "base64.b64decode(" in logmsg: + log.debug("Executed SHIM command. Command logged to TRACE") log.trace(logmsg) else: log.debug(logmsg) @@ -348,49 +354,50 @@ class Shell(object): return ret def send(self, local, remote, makedirs=False): - ''' + """ scp a file or files to a remote system - ''' + """ if makedirs: - self.exec_cmd('mkdir -p {0}'.format(os.path.dirname(remote))) + self.exec_cmd("mkdir -p {0}".format(os.path.dirname(remote))) # scp needs [= pymap[ns] and os.path.exists(os.path.join(OPTIONS.saltdir, ns)): + if ( + c_vn[0] == pymap[ns][0] + and c_vn >= pymap[ns] + and os.path.exists(os.path.join(OPTIONS.saltdir, ns)) + ): return py_cmd sys.exit(EX_THIN_PYTHON_INVALID) def main(argv): # pylint: disable=W0613 - ''' + """ Main program body - ''' + """ thin_path = os.path.join(OPTIONS.saltdir, THIN_ARCHIVE) if os.path.isfile(thin_path): if OPTIONS.checksum != get_hash(thin_path, OPTIONS.hashfunc): @@ -256,49 +277,60 @@ def main(argv): # pylint: disable=W0613 unpack_thin(thin_path) # Salt thin now is available to use else: - if not sys.platform.startswith('win'): - scpstat = subprocess.Popen(['/bin/sh', '-c', 'command -v scp']).wait() + if not sys.platform.startswith("win"): + scpstat = subprocess.Popen(["/bin/sh", "-c", "command -v scp"]).wait() if scpstat != 0: sys.exit(EX_SCP_NOT_FOUND) if os.path.exists(OPTIONS.saltdir) and not os.path.isdir(OPTIONS.saltdir): sys.stderr.write( 'ERROR: salt path "{0}" exists but is' - ' not a directory\n'.format(OPTIONS.saltdir) + " not a directory\n".format(OPTIONS.saltdir) ) sys.exit(EX_CANTCREAT) if not os.path.exists(OPTIONS.saltdir): need_deployment() - code_checksum_path = os.path.normpath(os.path.join(OPTIONS.saltdir, 'code-checksum')) - if not os.path.exists(code_checksum_path) or not os.path.isfile(code_checksum_path): - sys.stderr.write('WARNING: Unable to locate current code checksum: {0}.\n'.format(code_checksum_path)) + code_checksum_path = os.path.normpath( + os.path.join(OPTIONS.saltdir, "code-checksum") + ) + if not os.path.exists(code_checksum_path) or not os.path.isfile( + code_checksum_path + ): + sys.stderr.write( + "WARNING: Unable to locate current code checksum: {0}.\n".format( + code_checksum_path + ) + ) need_deployment() - with open(code_checksum_path, 'r') as vpo: + with open(code_checksum_path, "r") as vpo: cur_code_cs = vpo.readline().strip() if cur_code_cs != OPTIONS.code_checksum: - sys.stderr.write('WARNING: current code checksum {0} is different to {1}.\n'.format(cur_code_cs, - OPTIONS.code_checksum)) + sys.stderr.write( + "WARNING: current code checksum {0} is different to {1}.\n".format( + cur_code_cs, OPTIONS.code_checksum + ) + ) need_deployment() # Salt thin exists and is up-to-date - fall through and use it - salt_call_path = os.path.join(OPTIONS.saltdir, 'salt-call') + salt_call_path = os.path.join(OPTIONS.saltdir, "salt-call") if not os.path.isfile(salt_call_path): sys.stderr.write('ERROR: thin is missing "{0}"\n'.format(salt_call_path)) need_deployment() - with open(os.path.join(OPTIONS.saltdir, 'minion'), 'w') as config: - config.write(OPTIONS.config + '\n') + with open(os.path.join(OPTIONS.saltdir, "minion"), "w") as config: + config.write(OPTIONS.config + "\n") if OPTIONS.ext_mods: ext_path = os.path.join(OPTIONS.saltdir, EXT_ARCHIVE) if os.path.exists(ext_path): unpack_ext(ext_path) else: - version_path = os.path.join(OPTIONS.saltdir, 'ext_version') + version_path = os.path.join(OPTIONS.saltdir, "ext_version") if not os.path.exists(version_path) or not os.path.isfile(version_path): need_ext() - with open(version_path, 'r') as vpo: + with open(version_path, "r") as vpo: cur_version = vpo.readline().strip() if cur_version != OPTIONS.ext_mods: need_ext() @@ -311,39 +343,46 @@ def main(argv): # pylint: disable=W0613 salt_argv = [ get_executable(), salt_call_path, - '--retcode-passthrough', - '--local', - '--metadata', - '--out', 'json', - '-l', 'quiet', - '-c', OPTIONS.saltdir + "--retcode-passthrough", + "--local", + "--metadata", + "--out", + "json", + "-l", + "quiet", + "-c", + OPTIONS.saltdir, ] try: - if argv_prepared[-1].startswith('--no-parse='): + if argv_prepared[-1].startswith("--no-parse="): salt_argv.append(argv_prepared.pop(-1)) except (IndexError, TypeError): pass - salt_argv.append('--') + salt_argv.append("--") salt_argv.extend(argv_prepared) - sys.stderr.write('SALT_ARGV: {0}\n'.format(salt_argv)) + sys.stderr.write("SALT_ARGV: {0}\n".format(salt_argv)) # Only emit the delimiter on *both* stdout and stderr when completely successful. # Yes, the flush() is necessary. - sys.stdout.write(OPTIONS.delimiter + '\n') + sys.stdout.write(OPTIONS.delimiter + "\n") sys.stdout.flush() if not OPTIONS.tty: - sys.stderr.write(OPTIONS.delimiter + '\n') + sys.stderr.write(OPTIONS.delimiter + "\n") sys.stderr.flush() if OPTIONS.cmd_umask is not None: old_umask = os.umask(OPTIONS.cmd_umask) # pylint: disable=blacklisted-function if OPTIONS.tty: - proc = subprocess.Popen(salt_argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen( + salt_argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) # Returns bytes instead of string on python 3 stdout, _ = proc.communicate() - sys.stdout.write(stdout.decode(encoding=get_system_encoding(), errors="replace")) + sys.stdout.write( + stdout.decode(encoding=get_system_encoding(), errors="replace") + ) sys.stdout.flush() retcode = proc.returncode if OPTIONS.wipe: @@ -358,5 +397,5 @@ def main(argv): # pylint: disable=W0613 return retcode -if __name__ == '__main__': +if __name__ == "__main__": sys.exit(main(sys.argv)) diff --git a/salt/client/ssh/state.py b/salt/client/ssh/state.py index ca2a2fc791e..0aef867ecda 100644 --- a/salt/client/ssh/state.py +++ b/salt/client/ssh/state.py @@ -1,19 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" Create ssh executor system -''' +""" from __future__ import absolute_import, print_function + # Import python libs import logging import os +import shutil import tarfile import tempfile -import shutil from contextlib import closing +import salt.client.ssh + # Import salt libs import salt.client.ssh.shell -import salt.client.ssh +import salt.loader +import salt.minion +import salt.roster +import salt.state import salt.utils.files import salt.utils.json import salt.utils.path @@ -21,10 +27,6 @@ import salt.utils.stringutils import salt.utils.thin import salt.utils.url import salt.utils.verify -import salt.roster -import salt.state -import salt.loader -import salt.minion # Import 3rd-party libs from salt.ext import six @@ -33,41 +35,45 @@ log = logging.getLogger(__name__) class SSHState(salt.state.State): - ''' + """ Create a State object which wraps the SSH functions for state operations - ''' + """ + def __init__(self, opts, pillar=None, wrapper=None): self.wrapper = wrapper super(SSHState, self).__init__(opts, pillar) def load_modules(self, data=None, proxy=None): - ''' + """ Load up the modules for remote compilation via ssh - ''' + """ self.functions = self.wrapper self.utils = salt.loader.utils(self.opts) self.serializers = salt.loader.serializers(self.opts) locals_ = salt.loader.minion_mods(self.opts, utils=self.utils) - self.states = salt.loader.states(self.opts, locals_, self.utils, self.serializers) + self.states = salt.loader.states( + self.opts, locals_, self.utils, self.serializers + ) self.rend = salt.loader.render(self.opts, self.functions) def check_refresh(self, data, ret): - ''' + """ Stub out check_refresh - ''' + """ return def module_refresh(self): - ''' + """ Module refresh is not needed, stub it out - ''' + """ return class SSHHighState(salt.state.BaseHighState): - ''' + """ Used to compile the highstate on the master - ''' + """ + stack = [] def __init__(self, opts, pillar=None, wrapper=None, fsclient=None): @@ -84,55 +90,57 @@ class SSHHighState(salt.state.BaseHighState): salt.state.HighState.stack.append(self) def load_dynamic(self, matches): - ''' + """ Stub out load_dynamic - ''' + """ return def _master_tops(self): - ''' + """ Evaluate master_tops locally - ''' - if 'id' not in self.opts: - log.error('Received call for external nodes without an id') + """ + if "id" not in self.opts: + log.error("Received call for external nodes without an id") return {} - if not salt.utils.verify.valid_id(self.opts, self.opts['id']): + if not salt.utils.verify.valid_id(self.opts, self.opts["id"]): return {} # Evaluate all configured master_tops interfaces grains = {} ret = {} - if 'grains' in self.opts: - grains = self.opts['grains'] + if "grains" in self.opts: + grains = self.opts["grains"] for fun in self.tops: - if fun not in self.opts.get('master_tops', {}): + if fun not in self.opts.get("master_tops", {}): continue try: ret.update(self.tops[fun](opts=self.opts, grains=grains)) except Exception as exc: # pylint: disable=broad-except # If anything happens in the top generation, log it and move on log.error( - 'Top function %s failed with error %s for minion %s', - fun, exc, self.opts['id'] + "Top function %s failed with error %s for minion %s", + fun, + exc, + self.opts["id"], ) return ret -def lowstate_file_refs(chunks, extras=''): - ''' +def lowstate_file_refs(chunks, extras=""): + """ Create a list of file ref objects to reconcile - ''' + """ refs = {} for chunk in chunks: if not isinstance(chunk, dict): continue - saltenv = 'base' + saltenv = "base" crefs = [] for state in chunk: - if state == '__env__': + if state == "__env__": saltenv = chunk[state] - elif state.startswith('__'): + elif state.startswith("__"): continue crefs.extend(salt_refs(chunk[state])) if saltenv not in refs: @@ -140,7 +148,7 @@ def lowstate_file_refs(chunks, extras=''): if crefs: refs[saltenv].append(crefs) if extras: - extra_refs = extras.split(',') + extra_refs = extras.split(",") if extra_refs: for env in refs: for x in extra_refs: @@ -149,10 +157,10 @@ def lowstate_file_refs(chunks, extras=''): def salt_refs(data, ret=None): - ''' + """ Pull salt file references out of the states - ''' - proto = 'salt://' + """ + proto = "salt://" if ret is None: ret = [] if isinstance(data, six.string_types): @@ -167,57 +175,59 @@ def salt_refs(data, ret=None): return ret -def prep_trans_tar(file_client, chunks, file_refs, pillar=None, id_=None, roster_grains=None): - ''' +def prep_trans_tar( + file_client, chunks, file_refs, pillar=None, id_=None, roster_grains=None +): + """ Generate the execution package from the saltenv file refs and a low state data structure - ''' + """ gendir = tempfile.mkdtemp() trans_tar = salt.utils.files.mkstemp() - lowfn = os.path.join(gendir, 'lowstate.json') - pillarfn = os.path.join(gendir, 'pillar.json') - roster_grainsfn = os.path.join(gendir, 'roster_grains.json') + lowfn = os.path.join(gendir, "lowstate.json") + pillarfn = os.path.join(gendir, "pillar.json") + roster_grainsfn = os.path.join(gendir, "roster_grains.json") sync_refs = [ - [salt.utils.url.create('_modules')], - [salt.utils.url.create('_states')], - [salt.utils.url.create('_grains')], - [salt.utils.url.create('_renderers')], - [salt.utils.url.create('_returners')], - [salt.utils.url.create('_output')], - [salt.utils.url.create('_utils')], - ] - with salt.utils.files.fopen(lowfn, 'w+') as fp_: + [salt.utils.url.create("_modules")], + [salt.utils.url.create("_states")], + [salt.utils.url.create("_grains")], + [salt.utils.url.create("_renderers")], + [salt.utils.url.create("_returners")], + [salt.utils.url.create("_output")], + [salt.utils.url.create("_utils")], + ] + with salt.utils.files.fopen(lowfn, "w+") as fp_: salt.utils.json.dump(chunks, fp_) if pillar: - with salt.utils.files.fopen(pillarfn, 'w+') as fp_: + with salt.utils.files.fopen(pillarfn, "w+") as fp_: salt.utils.json.dump(pillar, fp_) if roster_grains: - with salt.utils.files.fopen(roster_grainsfn, 'w+') as fp_: + with salt.utils.files.fopen(roster_grainsfn, "w+") as fp_: salt.utils.json.dump(roster_grains, fp_) if id_ is None: - id_ = '' + id_ = "" try: - cachedir = os.path.join('salt-ssh', id_).rstrip(os.sep) + cachedir = os.path.join("salt-ssh", id_).rstrip(os.sep) except AttributeError: # Minion ID should always be a str, but don't let an int break this - cachedir = os.path.join('salt-ssh', six.text_type(id_)).rstrip(os.sep) + cachedir = os.path.join("salt-ssh", six.text_type(id_)).rstrip(os.sep) for saltenv in file_refs: # Location where files in this saltenv will be cached - cache_dest_root = os.path.join(cachedir, 'files', saltenv) + cache_dest_root = os.path.join(cachedir, "files", saltenv) file_refs[saltenv].extend(sync_refs) env_root = os.path.join(gendir, saltenv) if not os.path.isdir(env_root): os.makedirs(env_root) for ref in file_refs[saltenv]: for name in ref: - short = salt.utils.url.parse(name)[0].lstrip('/') + short = salt.utils.url.parse(name)[0].lstrip("/") cache_dest = os.path.join(cache_dest_root, short) try: path = file_client.cache_file(name, saltenv, cachedir=cachedir) except IOError: - path = '' + path = "" if path: tgt = os.path.join(env_root, short) tgt_dir = os.path.dirname(tgt) @@ -228,15 +238,13 @@ def prep_trans_tar(file_client, chunks, file_refs, pillar=None, id_=None, roster try: files = file_client.cache_dir(name, saltenv, cachedir=cachedir) except IOError: - files = '' + files = "" if files: for filename in files: - fn = filename[len(file_client.get_cachedir(cache_dest)):].strip('/') - tgt = os.path.join( - env_root, - short, - fn, - ) + fn = filename[ + len(file_client.get_cachedir(cache_dest)) : + ].strip("/") + tgt = os.path.join(env_root, short, fn,) tgt_dir = os.path.dirname(tgt) if not os.path.isdir(tgt_dir): os.makedirs(tgt_dir) @@ -248,11 +256,11 @@ def prep_trans_tar(file_client, chunks, file_refs, pillar=None, id_=None, roster except OSError: cwd = None os.chdir(gendir) - with closing(tarfile.open(trans_tar, 'w:gz')) as tfp: + with closing(tarfile.open(trans_tar, "w:gz")) as tfp: for root, dirs, files in salt.utils.path.os_walk(gendir): for name in files: full = os.path.join(root, name) - tfp.add(full[len(gendir):].lstrip(os.sep)) + tfp.add(full[len(gendir) :].lstrip(os.sep)) if cwd: os.chdir(cwd) shutil.rmtree(gendir) diff --git a/salt/client/ssh/wrapper/__init__.py b/salt/client/ssh/wrapper/__init__.py index 09f93446420..acddbdd07c5 100644 --- a/salt/client/ssh/wrapper/__init__.py +++ b/salt/client/ssh/wrapper/__init__.py @@ -1,49 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" The ssh client wrapper system contains the routines that are used to alter how executions are run in the salt-ssh system, this allows for state routines to be easily rewritten to execute in a way that makes them do the same tasks as ZeroMQ salt, but via ssh. -''' +""" # Import python libs from __future__ import absolute_import, print_function + import copy +import salt.client.ssh + # Import salt libs import salt.loader import salt.utils.data import salt.utils.json -import salt.client.ssh # Import 3rd-party libs from salt.ext import six class FunctionWrapper(object): - ''' + """ Create an object that acts like the salt function dict and makes function calls remotely via the SSH shell system - ''' + """ + def __init__( - self, - opts, - id_, - host, - wfuncs=None, - mods=None, - fsclient=None, - cmd_prefix=None, - aliases=None, - minion_opts=None, - **kwargs): + self, + opts, + id_, + host, + wfuncs=None, + mods=None, + fsclient=None, + cmd_prefix=None, + aliases=None, + minion_opts=None, + **kwargs + ): super(FunctionWrapper, self).__init__() self.cmd_prefix = cmd_prefix self.wfuncs = wfuncs if isinstance(wfuncs, dict) else {} self.opts = opts self.mods = mods if isinstance(mods, dict) else {} - self.kwargs = {'id_': id_, - 'host': host} + self.kwargs = {"id_": id_, "host": host} self.fsclient = fsclient self.kwargs.update(kwargs) self.aliases = aliases @@ -52,11 +55,11 @@ class FunctionWrapper(object): self.minion_opts = minion_opts def __contains__(self, key): - ''' + """ We need to implement a __contains__ method, othwerwise when someone does a contains comparison python assumes this is a sequence, and does __getitem__ keys 0 and up until IndexError - ''' + """ try: self[key] # pylint: disable=W0104 return True @@ -64,33 +67,35 @@ class FunctionWrapper(object): return False def __getitem__(self, cmd): - ''' + """ Return the function call to simulate the salt local lookup system - ''' - if '.' not in cmd and not self.cmd_prefix: + """ + if "." not in cmd and not self.cmd_prefix: # Form of salt.cmd.run in Jinja -- it's expecting a subdictionary # containing only 'cmd' module calls, in that case. Create a new # FunctionWrapper which contains the prefix 'cmd' (again, for the # salt.cmd.run example) kwargs = copy.deepcopy(self.kwargs) - id_ = kwargs.pop('id_') - host = kwargs.pop('host') - return FunctionWrapper(self.opts, - id_, - host, - wfuncs=self.wfuncs, - mods=self.mods, - fsclient=self.fsclient, - cmd_prefix=cmd, - aliases=self.aliases, - minion_opts=self.minion_opts, - **kwargs) + id_ = kwargs.pop("id_") + host = kwargs.pop("host") + return FunctionWrapper( + self.opts, + id_, + host, + wfuncs=self.wfuncs, + mods=self.mods, + fsclient=self.fsclient, + cmd_prefix=cmd, + aliases=self.aliases, + minion_opts=self.minion_opts, + **kwargs + ) if self.cmd_prefix: # We're in an inner FunctionWrapper as created by the code block # above. Reconstruct the original cmd in the form 'cmd.run' and # then evaluate as normal - cmd = '{0}.{1}'.format(self.cmd_prefix, cmd) + cmd = "{0}.{1}".format(self.cmd_prefix, cmd) if cmd in self.wfuncs: return self.wfuncs[cmd] @@ -99,60 +104,69 @@ class FunctionWrapper(object): return self.aliases[cmd] def caller(*args, **kwargs): - ''' + """ The remote execution function - ''' + """ argv = [cmd] argv.extend([salt.utils.json.dumps(arg) for arg in args]) argv.extend( - ['{0}={1}'.format(salt.utils.stringutils.to_str(key), - salt.utils.json.dumps(val)) - for key, val in six.iteritems(kwargs)] + [ + "{0}={1}".format( + salt.utils.stringutils.to_str(key), salt.utils.json.dumps(val) + ) + for key, val in six.iteritems(kwargs) + ] ) single = salt.client.ssh.Single( - self.opts, - argv, - mods=self.mods, - disable_wipe=True, - fsclient=self.fsclient, - minion_opts=self.minion_opts, - **self.kwargs + self.opts, + argv, + mods=self.mods, + disable_wipe=True, + fsclient=self.fsclient, + minion_opts=self.minion_opts, + **self.kwargs ) stdout, stderr, retcode = single.cmd_block() - if stderr.count('Permission Denied'): - return {'_error': 'Permission Denied', - 'stdout': stdout, - 'stderr': stderr, - 'retcode': retcode} + if stderr.count("Permission Denied"): + return { + "_error": "Permission Denied", + "stdout": stdout, + "stderr": stderr, + "retcode": retcode, + } try: ret = salt.utils.json.loads(stdout) - if len(ret) < 2 and 'local' in ret: - ret = ret['local'] - ret = ret.get('return', {}) + if len(ret) < 2 and "local" in ret: + ret = ret["local"] + ret = ret.get("return", {}) except ValueError: - ret = {'_error': 'Failed to return clean data', - 'stderr': stderr, - 'stdout': stdout, - 'retcode': retcode} + ret = { + "_error": "Failed to return clean data", + "stderr": stderr, + "stdout": stdout, + "retcode": retcode, + } return ret + return caller def __setitem__(self, cmd, value): - ''' + """ Set aliases for functions - ''' - if '.' not in cmd and not self.cmd_prefix: + """ + if "." not in cmd and not self.cmd_prefix: # Form of salt.cmd.run in Jinja -- it's expecting a subdictionary # containing only 'cmd' module calls, in that case. We don't # support assigning directly to prefixes in this way - raise KeyError('Cannot assign to module key {0} in the ' - 'FunctionWrapper'.format(cmd)) + raise KeyError( + "Cannot assign to module key {0} in the " "FunctionWrapper".format(cmd) + ) if self.cmd_prefix: # We're in an inner FunctionWrapper as created by the first code # block in __getitem__. Reconstruct the original cmd in the form # 'cmd.run' and then evaluate as normal - cmd = '{0}.{1}'.format(self.cmd_prefix, cmd) + cmd = "{0}.{1}".format(self.cmd_prefix, cmd) if cmd in self.wfuncs: self.wfuncs[cmd] = value @@ -163,9 +177,9 @@ class FunctionWrapper(object): self.aliases[cmd] = value def get(self, cmd, default): - ''' + """ Mirrors behavior of dict.get - ''' + """ if cmd in self: return self[cmd] else: diff --git a/salt/client/ssh/wrapper/config.py b/salt/client/ssh/wrapper/config.py index b36829c54ef..5f4f980af96 100644 --- a/salt/client/ssh/wrapper/config.py +++ b/salt/client/ssh/wrapper/config.py @@ -1,61 +1,70 @@ # -*- coding: utf-8 -*- -''' +""" Return config information -''' +""" # Import python libs from __future__ import absolute_import, print_function -import re + import os +import re + +import salt.syspaths as syspaths # Import salt libs import salt.utils.data import salt.utils.files -import salt.syspaths as syspaths # Import 3rd-party libs from salt.ext import six # Set up the default values for all systems -DEFAULTS = {'mongo.db': 'salt', - 'mongo.host': 'salt', - 'mongo.password': '', - 'mongo.port': 27017, - 'mongo.user': '', - 'redis.db': '0', - 'redis.host': 'salt', - 'redis.port': 6379, - 'test.foo': 'unconfigured', - 'ca.cert_base_path': '/etc/pki', - 'solr.cores': [], - 'solr.host': 'localhost', - 'solr.port': '8983', - 'solr.baseurl': '/solr', - 'solr.type': 'master', - 'solr.request_timeout': None, - 'solr.init_script': '/etc/rc.d/solr', - 'solr.dih.import_options': {'clean': False, 'optimize': True, - 'commit': True, 'verbose': False}, - 'solr.backup_path': None, - 'solr.num_backups': 1, - 'poudriere.config': '/usr/local/etc/poudriere.conf', - 'poudriere.config_dir': '/usr/local/etc/poudriere.d', - 'ldap.server': 'localhost', - 'ldap.port': '389', - 'ldap.tls': False, - 'ldap.scope': 2, - 'ldap.attrs': None, - 'ldap.binddn': '', - 'ldap.bindpw': '', - 'hosts.file': '/etc/hosts', - 'aliases.file': '/etc/aliases', - 'virt': {'tunnel': False, - 'images': os.path.join(syspaths.SRV_ROOT_DIR, 'salt-images')}, - } +DEFAULTS = { + "mongo.db": "salt", + "mongo.host": "salt", + "mongo.password": "", + "mongo.port": 27017, + "mongo.user": "", + "redis.db": "0", + "redis.host": "salt", + "redis.port": 6379, + "test.foo": "unconfigured", + "ca.cert_base_path": "/etc/pki", + "solr.cores": [], + "solr.host": "localhost", + "solr.port": "8983", + "solr.baseurl": "/solr", + "solr.type": "master", + "solr.request_timeout": None, + "solr.init_script": "/etc/rc.d/solr", + "solr.dih.import_options": { + "clean": False, + "optimize": True, + "commit": True, + "verbose": False, + }, + "solr.backup_path": None, + "solr.num_backups": 1, + "poudriere.config": "/usr/local/etc/poudriere.conf", + "poudriere.config_dir": "/usr/local/etc/poudriere.d", + "ldap.server": "localhost", + "ldap.port": "389", + "ldap.tls": False, + "ldap.scope": 2, + "ldap.attrs": None, + "ldap.binddn": "", + "ldap.bindpw": "", + "hosts.file": "/etc/hosts", + "aliases.file": "/etc/aliases", + "virt": { + "tunnel": False, + "images": os.path.join(syspaths.SRV_ROOT_DIR, "salt-images"), + }, +} -def backup_mode(backup=''): - ''' +def backup_mode(backup=""): + """ Return the backup mode CLI Example: @@ -63,14 +72,14 @@ def backup_mode(backup=''): .. code-block:: bash salt '*' config.backup_mode - ''' + """ if backup: return backup - return option('backup_mode') + return option("backup_mode") def manage_mode(mode): - ''' + """ Return a mode value, normalized to a string CLI Example: @@ -78,7 +87,7 @@ def manage_mode(mode): .. code-block:: bash salt '*' config.manage_mode - ''' + """ # config.manage_mode should no longer be invoked from the __salt__ dunder # in Salt code, this function is only being left here for backwards # compatibility. @@ -86,7 +95,7 @@ def manage_mode(mode): def valid_fileproto(uri): - ''' + """ Returns a boolean value based on whether or not the URI passed has a valid remote file protocol designation @@ -95,20 +104,15 @@ def valid_fileproto(uri): .. code-block:: bash salt '*' config.valid_fileproto salt://path/to/file - ''' + """ try: - return bool(re.match('^(?:salt|https?|ftp)://', uri)) + return bool(re.match("^(?:salt|https?|ftp)://", uri)) except Exception: # pylint: disable=broad-except return False -def option( - value, - default='', - omit_opts=False, - omit_master=False, - omit_pillar=False): - ''' +def option(value, default="", omit_opts=False, omit_master=False, omit_pillar=False): + """ Pass in a generic option and receive the value that will be assigned CLI Example: @@ -116,13 +120,13 @@ def option( .. code-block:: bash salt '*' config.option redis.host - ''' + """ if not omit_opts: if value in __opts__: return __opts__[value] if not omit_master: - if value in __pillar__.get('master', {}): - return __pillar__['master'][value] + if value in __pillar__.get("master", {}): + return __pillar__["master"][value] if not omit_pillar: if value in __pillar__: return __pillar__[value] @@ -131,12 +135,8 @@ def option( return default -def merge(value, - default='', - omit_opts=False, - omit_master=False, - omit_pillar=False): - ''' +def merge(value, default="", omit_opts=False, omit_master=False, omit_pillar=False): + """ Retrieves an option based on key, merging all matches. Same as ``option()`` except that it merges all matches, rather than taking @@ -147,7 +147,7 @@ def merge(value, .. code-block:: bash salt '*' config.merge schedule - ''' + """ ret = None if not omit_opts: if value in __opts__: @@ -155,8 +155,8 @@ def merge(value, if isinstance(ret, six.string_types): return ret if not omit_master: - if value in __pillar__.get('master', {}): - tmp = __pillar__['master'][value] + if value in __pillar__.get("master", {}): + tmp = __pillar__["master"][value] if ret is None: ret = tmp if isinstance(ret, six.string_types): @@ -164,8 +164,7 @@ def merge(value, elif isinstance(ret, dict) and isinstance(tmp, dict): tmp.update(ret) ret = tmp - elif (isinstance(ret, (list, tuple)) and - isinstance(tmp, (list, tuple))): + elif isinstance(ret, (list, tuple)) and isinstance(tmp, (list, tuple)): ret = list(ret) + list(tmp) if not omit_pillar: if value in __pillar__: @@ -177,16 +176,15 @@ def merge(value, elif isinstance(ret, dict) and isinstance(tmp, dict): tmp.update(ret) ret = tmp - elif (isinstance(ret, (list, tuple)) and - isinstance(tmp, (list, tuple))): + elif isinstance(ret, (list, tuple)) and isinstance(tmp, (list, tuple)): ret = list(ret) + list(tmp) if ret is None and value in DEFAULTS: return DEFAULTS[value] return ret or default -def get(key, default=''): - ''' +def get(key, default=""): + """ .. versionadded: 0.14.0 Attempt to retrieve the named value from opts, pillar, grains of the master @@ -215,24 +213,26 @@ def get(key, default=''): .. code-block:: bash salt '*' config.get pkg:apache - ''' - ret = salt.utils.data.traverse_dict_and_list(__opts__, key, '_|-') - if ret != '_|-': + """ + ret = salt.utils.data.traverse_dict_and_list(__opts__, key, "_|-") + if ret != "_|-": return ret - ret = salt.utils.data.traverse_dict_and_list(__grains__, key, '_|-') - if ret != '_|-': + ret = salt.utils.data.traverse_dict_and_list(__grains__, key, "_|-") + if ret != "_|-": return ret - ret = salt.utils.data.traverse_dict_and_list(__pillar__, key, '_|-') - if ret != '_|-': + ret = salt.utils.data.traverse_dict_and_list(__pillar__, key, "_|-") + if ret != "_|-": return ret - ret = salt.utils.data.traverse_dict_and_list(__pillar__.get('master', {}), key, '_|-') - if ret != '_|-': + ret = salt.utils.data.traverse_dict_and_list( + __pillar__.get("master", {}), key, "_|-" + ) + if ret != "_|-": return ret return default def dot_vals(value): - ''' + """ Pass in a configuration value that should be preceded by the module name and a dot, this will return a list of all read key/value pairs @@ -241,12 +241,12 @@ def dot_vals(value): .. code-block:: bash salt '*' config.dot_vals host - ''' + """ ret = {} - for key, val in six.iteritems(__pillar__.get('master', {})): - if key.startswith('{0}.'.format(value)): + for key, val in six.iteritems(__pillar__.get("master", {})): + if key.startswith("{0}.".format(value)): ret[key] = val for key, val in six.iteritems(__opts__): - if key.startswith('{0}.'.format(value)): + if key.startswith("{0}.".format(value)): ret[key] = val return ret diff --git a/salt/client/ssh/wrapper/cp.py b/salt/client/ssh/wrapper/cp.py index 894e62f94c8..e369d8475fd 100644 --- a/salt/client/ssh/wrapper/cp.py +++ b/salt/client/ssh/wrapper/cp.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Wrap the cp module allowing for managed ssh file transfers -''' +""" # Import Python libs from __future__ import absolute_import, print_function + import logging import os @@ -17,147 +18,125 @@ from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) -def get_file(path, - dest, - saltenv='base', - makedirs=False, - template=None, - gzip=None): - ''' +def get_file(path, dest, saltenv="base", makedirs=False, template=None, gzip=None): + """ Send a file from the master to the location in specified .. note:: gzip compression is not supported in the salt-ssh version of cp.get_file. The argument is only accepted for interface compatibility. - ''' + """ if gzip is not None: - log.warning('The gzip argument to cp.get_file in salt-ssh is ' - 'unsupported') + log.warning("The gzip argument to cp.get_file in salt-ssh is " "unsupported") if template is not None: (path, dest) = _render_filenames(path, dest, saltenv, template) - src = __context__['fileclient'].cache_file( - path, - saltenv, - cachedir=os.path.join('salt-ssh', __salt__.kwargs['id_'])) - single = salt.client.ssh.Single( - __opts__, - '', - **__salt__.kwargs) + src = __context__["fileclient"].cache_file( + path, saltenv, cachedir=os.path.join("salt-ssh", __salt__.kwargs["id_"]) + ) + single = salt.client.ssh.Single(__opts__, "", **__salt__.kwargs) ret = single.shell.send(src, dest, makedirs) return not ret[2] -def get_dir(path, dest, saltenv='base'): - ''' +def get_dir(path, dest, saltenv="base"): + """ Transfer a directory down - ''' - src = __context__['fileclient'].cache_dir( - path, - saltenv, - cachedir=os.path.join('salt-ssh', __salt__.kwargs['id_'])) - src = ' '.join(src) - single = salt.client.ssh.Single( - __opts__, - '', - **__salt__.kwargs) + """ + src = __context__["fileclient"].cache_dir( + path, saltenv, cachedir=os.path.join("salt-ssh", __salt__.kwargs["id_"]) + ) + src = " ".join(src) + single = salt.client.ssh.Single(__opts__, "", **__salt__.kwargs) ret = single.shell.send(src, dest) return not ret[2] -def get_url(path, dest, saltenv='base'): - ''' +def get_url(path, dest, saltenv="base"): + """ retrieve a URL - ''' - src = __context__['fileclient'].cache_file( - path, - saltenv, - cachedir=os.path.join('salt-ssh', __salt__.kwargs['id_'])) - single = salt.client.ssh.Single( - __opts__, - '', - **__salt__.kwargs) + """ + src = __context__["fileclient"].cache_file( + path, saltenv, cachedir=os.path.join("salt-ssh", __salt__.kwargs["id_"]) + ) + single = salt.client.ssh.Single(__opts__, "", **__salt__.kwargs) ret = single.shell.send(src, dest) return not ret[2] -def list_states(saltenv='base'): - ''' +def list_states(saltenv="base"): + """ List all the available state modules in an environment - ''' - return __context__['fileclient'].list_states(saltenv) + """ + return __context__["fileclient"].list_states(saltenv) -def list_master(saltenv='base', prefix=''): - ''' +def list_master(saltenv="base", prefix=""): + """ List all of the files stored on the master - ''' - return __context__['fileclient'].file_list(saltenv, prefix) + """ + return __context__["fileclient"].file_list(saltenv, prefix) -def list_master_dirs(saltenv='base', prefix=''): - ''' +def list_master_dirs(saltenv="base", prefix=""): + """ List all of the directories stored on the master - ''' - return __context__['fileclient'].dir_list(saltenv, prefix) + """ + return __context__["fileclient"].dir_list(saltenv, prefix) -def list_master_symlinks(saltenv='base', prefix=''): - ''' +def list_master_symlinks(saltenv="base", prefix=""): + """ List all of the symlinks stored on the master - ''' - return __context__['fileclient'].symlink_list(saltenv, prefix) + """ + return __context__["fileclient"].symlink_list(saltenv, prefix) def _render_filenames(path, dest, saltenv, template): - ''' + """ Process markup in the :param:`path` and :param:`dest` variables (NOT the files under the paths they ultimately point to) according to the markup format provided by :param:`template`. - ''' + """ if not template: return (path, dest) # render the path as a template using path_template_engine as the engine if template not in salt.utils.templates.TEMPLATE_REGISTRY: raise CommandExecutionError( - 'Attempted to render file paths with unavailable engine ' - '{0}'.format(template) + "Attempted to render file paths with unavailable engine " + "{0}".format(template) ) kwargs = {} - kwargs['salt'] = __salt__ - kwargs['pillar'] = __pillar__ - kwargs['grains'] = __grains__ - kwargs['opts'] = __opts__ - kwargs['saltenv'] = saltenv + kwargs["salt"] = __salt__ + kwargs["pillar"] = __pillar__ + kwargs["grains"] = __grains__ + kwargs["opts"] = __opts__ + kwargs["saltenv"] = saltenv def _render(contents): - ''' + """ Render :param:`contents` into a literal pathname by writing it to a temp file, rendering that file, and returning the result. - ''' + """ # write out path to temp file tmp_path_fn = salt.utils.files.mkstemp() - with salt.utils.files.fopen(tmp_path_fn, 'w+') as fp_: + with salt.utils.files.fopen(tmp_path_fn, "w+") as fp_: fp_.write(salt.utils.stringutils.to_str(contents)) data = salt.utils.templates.TEMPLATE_REGISTRY[template]( - tmp_path_fn, - to_str=True, - **kwargs + tmp_path_fn, to_str=True, **kwargs ) salt.utils.files.safe_rm(tmp_path_fn) - if not data['result']: + if not data["result"]: # Failed to render the template raise CommandExecutionError( - 'Failed to render file path with error: {0}'.format( - data['data'] - ) + "Failed to render file path with error: {0}".format(data["data"]) ) else: - return data['data'] + return data["data"] path = _render(path) dest = _render(dest) diff --git a/salt/client/ssh/wrapper/grains.py b/salt/client/ssh/wrapper/grains.py index a7bc5eb11c2..b58250af294 100644 --- a/salt/client/ssh/wrapper/grains.py +++ b/salt/client/ssh/wrapper/grains.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Return/control aspects of the grains data -''' +""" # Import python libs from __future__ import absolute_import, print_function + import collections import copy import math @@ -24,34 +25,34 @@ __grains__ = {} def _serial_sanitizer(instr): - ''' + """ Replaces the last 1/4 of a string with X's - ''' + """ length = len(instr) - index = int(math.floor(length * .75)) - return '{0}{1}'.format(instr[:index], 'X' * (length - index)) + index = int(math.floor(length * 0.75)) + return "{0}{1}".format(instr[:index], "X" * (length - index)) -_FQDN_SANITIZER = lambda x: 'MINION.DOMAINNAME' -_HOSTNAME_SANITIZER = lambda x: 'MINION' -_DOMAINNAME_SANITIZER = lambda x: 'DOMAINNAME' +_FQDN_SANITIZER = lambda x: "MINION.DOMAINNAME" +_HOSTNAME_SANITIZER = lambda x: "MINION" +_DOMAINNAME_SANITIZER = lambda x: "DOMAINNAME" # A dictionary of grain -> function mappings for sanitizing grain output. This # is used when the 'sanitize' flag is given. _SANITIZERS = { - 'serialnumber': _serial_sanitizer, - 'domain': _DOMAINNAME_SANITIZER, - 'fqdn': _FQDN_SANITIZER, - 'id': _FQDN_SANITIZER, - 'host': _HOSTNAME_SANITIZER, - 'localhost': _HOSTNAME_SANITIZER, - 'nodename': _HOSTNAME_SANITIZER, + "serialnumber": _serial_sanitizer, + "domain": _DOMAINNAME_SANITIZER, + "fqdn": _FQDN_SANITIZER, + "id": _FQDN_SANITIZER, + "host": _HOSTNAME_SANITIZER, + "localhost": _HOSTNAME_SANITIZER, + "nodename": _HOSTNAME_SANITIZER, } -def get(key, default='', delimiter=DEFAULT_TARGET_DELIM, ordered=True): - ''' +def get(key, default="", delimiter=DEFAULT_TARGET_DELIM, ordered=True): + """ Attempt to retrieve the named value from grains, if the named value is not available return the passed default. The default return is an empty string. @@ -70,20 +71,16 @@ def get(key, default='', delimiter=DEFAULT_TARGET_DELIM, ordered=True): .. code-block:: bash salt '*' grains.get pkg:apache - ''' + """ if ordered is True: grains = __grains__ else: grains = salt.utils.json.loads(salt.utils.json.dumps(__grains__)) - return salt.utils.data.traverse_dict_and_list( - __grains__, - key, - default, - delimiter) + return salt.utils.data.traverse_dict_and_list(__grains__, key, default, delimiter) def has_value(key): - ''' + """ Determine whether a named value exists in the grains dictionary. Given a grains dictionary that contains the following structure:: @@ -99,14 +96,16 @@ def has_value(key): .. code-block:: bash salt '*' grains.has_value pkg:apache - ''' - return True \ - if salt.utils.data.traverse_dict_and_list(__grains__, key, False) \ + """ + return ( + True + if salt.utils.data.traverse_dict_and_list(__grains__, key, False) else False + ) def items(sanitize=False): - ''' + """ Return all of the minion's grains CLI Example: @@ -120,7 +119,7 @@ def items(sanitize=False): .. code-block:: bash salt '*' grains.items sanitize=True - ''' + """ if salt.utils.data.is_true(sanitize): out = dict(__grains__) for key, func in six.iteritems(_SANITIZERS): @@ -132,7 +131,7 @@ def items(sanitize=False): def item(*args, **kwargs): - ''' + """ Return one or more grains CLI Example: @@ -147,14 +146,14 @@ def item(*args, **kwargs): .. code-block:: bash salt '*' grains.item host sanitize=True - ''' + """ ret = {} for arg in args: try: ret[arg] = __grains__[arg] except KeyError: pass - if salt.utils.data.is_true(kwargs.get('sanitize')): + if salt.utils.data.is_true(kwargs.get("sanitize")): for arg, func in six.iteritems(_SANITIZERS): if arg in ret: ret[arg] = func(ret[arg]) @@ -162,7 +161,7 @@ def item(*args, **kwargs): def ls(): # pylint: disable=C0103 - ''' + """ Return a list of all available grains CLI Example: @@ -170,16 +169,12 @@ def ls(): # pylint: disable=C0103 .. code-block:: bash salt '*' grains.ls - ''' + """ return sorted(__grains__) -def filter_by(lookup_dict, - grain='os_family', - merge=None, - default='default', - base=None): - ''' +def filter_by(lookup_dict, grain="os_family", merge=None, default="default", base=None): + """ .. versionadded:: 0.17.0 Look up the given grain in a given dictionary for the current OS and return @@ -256,13 +251,10 @@ def filter_by(lookup_dict, salt '*' grains.filter_by '{Debian: Debheads rule, RedHat: I love my hat}' # this one will render {D: {E: I, G: H}, J: K} salt '*' grains.filter_by '{A: B, C: {D: {E: F,G: H}}}' 'xxx' '{D: {E: I},J: K}' 'C' - ''' + """ ret = lookup_dict.get( - __grains__.get( - grain, default), - lookup_dict.get( - default, None) - ) + __grains__.get(grain, default), lookup_dict.get(default, None) + ) if base and base in lookup_dict: base_values = lookup_dict[base] @@ -271,12 +263,14 @@ def filter_by(lookup_dict, elif isinstance(base_values, collections.Mapping): if not isinstance(ret, collections.Mapping): - raise SaltException('filter_by default and look-up values must both be dictionaries.') + raise SaltException( + "filter_by default and look-up values must both be dictionaries." + ) ret = salt.utils.dictupdate.update(copy.deepcopy(base_values), ret) if merge: if not isinstance(merge, collections.Mapping): - raise SaltException('filter_by merge argument must be a dictionary.') + raise SaltException("filter_by merge argument must be a dictionary.") else: if ret is None: ret = merge diff --git a/salt/client/ssh/wrapper/mine.py b/salt/client/ssh/wrapper/mine.py index 5f493b9ae52..fcad99999ca 100644 --- a/salt/client/ssh/wrapper/mine.py +++ b/salt/client/ssh/wrapper/mine.py @@ -1,21 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Wrapper function for mine operations for salt-ssh .. versionadded:: 2015.5.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function + import copy # Import salt libs import salt.client.ssh -def get(tgt, fun, tgt_type='glob', roster='flat'): - ''' +def get(tgt, fun, tgt_type="glob", roster="flat"): + """ Get data from the mine based on the target, function and tgt_type This will actually run the function on all targeted minions (like @@ -34,17 +35,17 @@ def get(tgt, fun, tgt_type='glob', roster='flat'): salt-ssh '*' mine.get '*' network.interfaces salt-ssh '*' mine.get 'myminion' network.interfaces roster=flat salt-ssh '*' mine.get '192.168.5.0' network.ipaddrs roster=scan - ''' + """ # Set up opts for the SSH object - opts = copy.deepcopy(__context__['master_opts']) + opts = copy.deepcopy(__context__["master_opts"]) minopts = copy.deepcopy(__opts__) opts.update(minopts) if roster: - opts['roster'] = roster - opts['argv'] = [fun] - opts['selected_target_option'] = tgt_type - opts['tgt'] = tgt - opts['arg'] = [] + opts["roster"] = roster + opts["argv"] = [fun] + opts["selected_target_option"] = tgt_type + opts["tgt"] = tgt + opts["arg"] = [] # Create the SSH object to handle the actual call ssh = salt.client.ssh.SSH(opts) @@ -56,8 +57,8 @@ def get(tgt, fun, tgt_type='glob', roster='flat'): cret = {} for host in rets: - if 'return' in rets[host]: - cret[host] = rets[host]['return'] + if "return" in rets[host]: + cret[host] = rets[host]["return"] else: cret[host] = rets[host] return cret diff --git a/salt/client/ssh/wrapper/pillar.py b/salt/client/ssh/wrapper/pillar.py index 64d58ac3743..a27f1fdda74 100644 --- a/salt/client/ssh/wrapper/pillar.py +++ b/salt/client/ssh/wrapper/pillar.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Extract the pillar data for this minion -''' +""" from __future__ import absolute_import, print_function # Import python libs @@ -14,8 +14,8 @@ import salt.utils.dictupdate from salt.defaults import DEFAULT_TARGET_DELIM -def get(key, default='', merge=False, delimiter=DEFAULT_TARGET_DELIM): - ''' +def get(key, default="", merge=False, delimiter=DEFAULT_TARGET_DELIM): + """ .. versionadded:: 0.14 Attempt to retrieve the named value from pillar, if the named value is not @@ -50,22 +50,19 @@ def get(key, default='', merge=False, delimiter=DEFAULT_TARGET_DELIM): .. code-block:: bash salt '*' pillar.get pkg:apache - ''' + """ if merge: ret = salt.utils.data.traverse_dict_and_list(__pillar__, key, {}, delimiter) - if isinstance(ret, collections.Mapping) and \ - isinstance(default, collections.Mapping): + if isinstance(ret, collections.Mapping) and isinstance( + default, collections.Mapping + ): return salt.utils.dictupdate.update(default, ret) - return salt.utils.data.traverse_dict_and_list( - __pillar__, - key, - default, - delimiter) + return salt.utils.data.traverse_dict_and_list(__pillar__, key, default, delimiter) def item(*args): - ''' + """ .. versionadded:: 0.16.2 Return one or more pillar entries @@ -76,7 +73,7 @@ def item(*args): salt '*' pillar.item foo salt '*' pillar.item foo bar baz - ''' + """ ret = {} for arg in args: try: @@ -87,7 +84,7 @@ def item(*args): def raw(key=None): - ''' + """ Return the raw pillar data that is available in the module. This will show the pillar as it is loaded as the __pillar__ dict. @@ -101,7 +98,7 @@ def raw(key=None): pillar raw data.:: salt '*' pillar.raw key='roles' - ''' + """ if key: ret = __pillar__.get(key, {}) else: @@ -111,7 +108,7 @@ def raw(key=None): def keys(key, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ .. versionadded:: 2015.8.0 Attempt to retrieve a list of keys from the named value from the pillar. @@ -127,9 +124,8 @@ def keys(key, delimiter=DEFAULT_TARGET_DELIM): .. code-block:: bash salt '*' pillar.keys web:sites - ''' - ret = salt.utils.data.traverse_dict_and_list( - __pillar__, key, KeyError, delimiter) + """ + ret = salt.utils.data.traverse_dict_and_list(__pillar__, key, KeyError, delimiter) if ret is KeyError: raise KeyError("Pillar key not found: {0}".format(key)) diff --git a/salt/client/ssh/wrapper/publish.py b/salt/client/ssh/wrapper/publish.py index 51e8264a4cb..baba62b5f8a 100644 --- a/salt/client/ssh/wrapper/publish.py +++ b/salt/client/ssh/wrapper/publish.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" .. versionadded:: 2015.5.0 Salt-ssh wrapper functions for the publish module. @@ -8,9 +8,10 @@ Publish will never actually execute on the minions, so we just create new salt-ssh calls and return the data from them. No access control is needed because calls cannot originate from the minions. -''' +""" # Import python libs from __future__ import absolute_import, print_function + import copy import logging @@ -22,15 +23,17 @@ import salt.utils.args log = logging.getLogger(__name__) -def _publish(tgt, - fun, - arg=None, - tgt_type='glob', - returner='', - timeout=None, - form='clean', - roster=None): - ''' +def _publish( + tgt, + fun, + arg=None, + tgt_type="glob", + returner="", + timeout=None, + form="clean", + roster=None, +): + """ Publish a command "from the minion out to other minions". In reality, the minion does not execute this function, it is executed by the master. Thus, no access control is enabled, as minions cannot initiate publishes @@ -53,14 +56,14 @@ def _publish(tgt, .. code-block:: bash salt-ssh system.example.com publish.publish '*' cmd.run 'ls -la /tmp' - ''' - if fun.startswith('publish.'): - log.info('Cannot publish publish calls. Returning {}') + """ + if fun.startswith("publish."): + log.info("Cannot publish publish calls. Returning {}") return {} # TODO: implement returners? Do they make sense for salt-ssh calls? if returner: - log.warning('Returners currently not supported in salt-ssh publish') + log.warning("Returners currently not supported in salt-ssh publish") # Make sure args have been processed if arg is None: @@ -73,17 +76,17 @@ def _publish(tgt, arg = [] # Set up opts for the SSH object - opts = copy.deepcopy(__context__['master_opts']) + opts = copy.deepcopy(__context__["master_opts"]) minopts = copy.deepcopy(__opts__) opts.update(minopts) if roster: - opts['roster'] = roster + opts["roster"] = roster if timeout: - opts['timeout'] = timeout - opts['argv'] = [fun] + arg - opts['selected_target_option'] = tgt_type - opts['tgt'] = tgt - opts['arg'] = arg + opts["timeout"] = timeout + opts["argv"] = [fun] + arg + opts["selected_target_option"] = tgt_type + opts["tgt"] = tgt + opts["arg"] = arg # Create the SSH object to handle the actual call ssh = salt.client.ssh.SSH(opts) @@ -93,11 +96,11 @@ def _publish(tgt, for ret in ssh.run_iter(): rets.update(ret) - if form == 'clean': + if form == "clean": cret = {} for host in rets: - if 'return' in rets[host]: - cret[host] = rets[host]['return'] + if "return" in rets[host]: + cret[host] = rets[host]["return"] else: cret[host] = rets[host] return cret @@ -105,14 +108,8 @@ def _publish(tgt, return rets -def publish(tgt, - fun, - arg=None, - tgt_type='glob', - returner='', - timeout=5, - roster=None): - ''' +def publish(tgt, fun, arg=None, tgt_type="glob", returner="", timeout=5, roster=None): + """ Publish a command "from the minion out to other minions". In reality, the minion does not execute this function, it is executed by the master. Thus, no access control is enabled, as minions cannot initiate publishes @@ -169,25 +166,21 @@ def publish(tgt, - ''' - return _publish(tgt, - fun, - arg=arg, - tgt_type=tgt_type, - returner=returner, - timeout=timeout, - form='clean', - roster=roster) + """ + return _publish( + tgt, + fun, + arg=arg, + tgt_type=tgt_type, + returner=returner, + timeout=timeout, + form="clean", + roster=roster, + ) -def full_data(tgt, - fun, - arg=None, - tgt_type='glob', - returner='', - timeout=5, - roster=None): - ''' +def full_data(tgt, fun, arg=None, tgt_type="glob", returner="", timeout=5, roster=None): + """ Return the full data about the publication, this is invoked in the same way as the publish function @@ -207,19 +200,21 @@ def full_data(tgt, salt-ssh '*' publish.full_data test.kwarg arg='cheese=spam' - ''' - return _publish(tgt, - fun, - arg=arg, - tgt_type=tgt_type, - returner=returner, - timeout=timeout, - form='full', - roster=roster) + """ + return _publish( + tgt, + fun, + arg=arg, + tgt_type=tgt_type, + returner=returner, + timeout=timeout, + form="full", + roster=roster, + ) def runner(fun, arg=None, timeout=5): - ''' + """ Execute a runner on the master and return the data from the runnr function CLI Example: @@ -227,7 +222,7 @@ def runner(fun, arg=None, timeout=5): .. code-block:: bash salt-ssh '*' publish.runner jobs.lookup_jid 20140916125524463507 - ''' + """ # Form args as list if not isinstance(arg, list): arg = [salt.utils.args.yamlify_arg(arg)] @@ -237,5 +232,5 @@ def runner(fun, arg=None, timeout=5): arg = [] # Create and run the runner - runner = salt.runner.RunnerClient(__opts__['__master_opts__']) + runner = salt.runner.RunnerClient(__opts__["__master_opts__"]) return runner.cmd(fun, arg) diff --git a/salt/client/ssh/wrapper/saltcheck.py b/salt/client/ssh/wrapper/saltcheck.py index e90244d3b1d..0b5319fe6bd 100644 --- a/salt/client/ssh/wrapper/saltcheck.py +++ b/salt/client/ssh/wrapper/saltcheck.py @@ -1,45 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" Wrap the saltcheck module to copy files to ssh minion before running tests -''' +""" # Import Python libs from __future__ import absolute_import, print_function + import logging -import tempfile import os import shutil import tarfile +import tempfile from contextlib import closing +import salt.utils.files +import salt.utils.json # Import salt libs import salt.utils.url -import salt.utils.files -import salt.utils.json log = logging.getLogger(__name__) -def update_master_cache(states, saltenv='base'): - ''' +def update_master_cache(states, saltenv="base"): + """ Replace standard saltcheck version with similar logic but replacing cp.cache_dir with generating files, tar'ing them up, copying tarball to remote host, extracting tar to state cache directory, and cleanup of files - ''' - cache = __opts__['cachedir'] - state_cache = os.path.join(cache, 'files', saltenv) + """ + cache = __opts__["cachedir"] + state_cache = os.path.join(cache, "files", saltenv) # Setup for copying states to gendir gendir = tempfile.mkdtemp() trans_tar = salt.utils.files.mkstemp() - if 'cp.fileclient_{0}'.format(id(__opts__)) not in __context__: - __context__['cp.fileclient_{0}'.format(id(__opts__))] = \ - salt.fileclient.get_file_client(__opts__) + if "cp.fileclient_{0}".format(id(__opts__)) not in __context__: + __context__[ + "cp.fileclient_{0}".format(id(__opts__)) + ] = salt.fileclient.get_file_client(__opts__) # generate cp.list_states output and save to gendir - cp_output = salt.utils.json.dumps(__salt__['cp.list_states']()) - cp_output_file = os.path.join(gendir, 'cp_output.txt') - with salt.utils.files.fopen(cp_output_file, 'w') as fp: + cp_output = salt.utils.json.dumps(__salt__["cp.list_states"]()) + cp_output_file = os.path.join(gendir, "cp_output.txt") + with salt.utils.files.fopen(cp_output_file, "w") as fp: fp.write(cp_output) # cp state directories to gendir @@ -47,41 +49,47 @@ def update_master_cache(states, saltenv='base'): sls_list = salt.utils.args.split_input(states) for state_name in sls_list: # generate low data for each state and save to gendir - state_low_file = os.path.join(gendir, state_name + '.low') - state_low_output = salt.utils.json.dumps(__salt__['state.show_low_sls'](state_name)) - with salt.utils.files.fopen(state_low_file, 'w') as fp: + state_low_file = os.path.join(gendir, state_name + ".low") + state_low_output = salt.utils.json.dumps( + __salt__["state.show_low_sls"](state_name) + ) + with salt.utils.files.fopen(state_low_file, "w") as fp: fp.write(state_low_output) state_name = state_name.replace(".", os.sep) if state_name in already_processed: log.debug("Already cached state for %s", state_name) else: - file_copy_file = os.path.join(gendir, state_name + '.copy') - log.debug('copying %s to %s', state_name, gendir) + file_copy_file = os.path.join(gendir, state_name + ".copy") + log.debug("copying %s to %s", state_name, gendir) qualified_name = salt.utils.url.create(state_name, saltenv) # Duplicate cp.get_dir to gendir - copy_result = __context__['cp.fileclient_{0}'.format(id(__opts__))].get_dir( - qualified_name, gendir, saltenv) + copy_result = __context__["cp.fileclient_{0}".format(id(__opts__))].get_dir( + qualified_name, gendir, saltenv + ) if copy_result: copy_result = [dir.replace(gendir, state_cache) for dir in copy_result] copy_result_output = salt.utils.json.dumps(copy_result) - with salt.utils.files.fopen(file_copy_file, 'w') as fp: + with salt.utils.files.fopen(file_copy_file, "w") as fp: fp.write(copy_result_output) already_processed.append(state_name) else: # If files were not copied, assume state.file.sls was given and just copy state state_name = os.path.dirname(state_name) - file_copy_file = os.path.join(gendir, state_name + '.copy') + file_copy_file = os.path.join(gendir, state_name + ".copy") if state_name in already_processed: - log.debug('Already cached state for %s', state_name) + log.debug("Already cached state for %s", state_name) else: qualified_name = salt.utils.url.create(state_name, saltenv) - copy_result = __context__['cp.fileclient_{0}'.format(id(__opts__))].get_dir( - qualified_name, gendir, saltenv) + copy_result = __context__[ + "cp.fileclient_{0}".format(id(__opts__)) + ].get_dir(qualified_name, gendir, saltenv) if copy_result: - copy_result = [dir.replace(gendir, state_cache) for dir in copy_result] + copy_result = [ + dir.replace(gendir, state_cache) for dir in copy_result + ] copy_result_output = salt.utils.json.dumps(copy_result) - with salt.utils.files.fopen(file_copy_file, 'w') as fp: + with salt.utils.files.fopen(file_copy_file, "w") as fp: fp.write(copy_result_output) already_processed.append(state_name) @@ -92,21 +100,18 @@ def update_master_cache(states, saltenv='base'): except OSError: cwd = None os.chdir(gendir) - with closing(tarfile.open(trans_tar, 'w:gz')) as tfp: + with closing(tarfile.open(trans_tar, "w:gz")) as tfp: for root, dirs, files in salt.utils.path.os_walk(gendir): for name in files: full = os.path.join(root, name) - tfp.add(full[len(gendir):].lstrip(os.sep)) + tfp.add(full[len(gendir) :].lstrip(os.sep)) if cwd: os.chdir(cwd) shutil.rmtree(gendir) # Copy tarfile to ssh host - single = salt.client.ssh.Single( - __opts__, - '', - **__salt__.kwargs) - thin_dir = __opts__['thin_dir'] + single = salt.client.ssh.Single(__opts__, "", **__salt__.kwargs) + thin_dir = __opts__["thin_dir"] ret = single.shell.send(trans_tar, thin_dir) # Clean up local tar @@ -118,28 +123,30 @@ def update_master_cache(states, saltenv='base'): tar_path = os.path.join(thin_dir, os.path.basename(trans_tar)) # Extract remote tarball to cache directory and remove tar file # TODO this could be better handled by a single state/connection due to ssh overhead - ret = __salt__['file.mkdir'](state_cache) - ret = __salt__['archive.tar']('xf', tar_path, dest=state_cache) - ret = __salt__['file.remove'](tar_path) + ret = __salt__["file.mkdir"](state_cache) + ret = __salt__["archive.tar"]("xf", tar_path, dest=state_cache) + ret = __salt__["file.remove"](tar_path) return ret -def run_state_tests(states, saltenv='base', check_all=False): - ''' +def run_state_tests(states, saltenv="base", check_all=False): + """ Define common functions to activite this wrapping module and tar copy. After file copies are finished, run the usual local saltcheck function - ''' + """ ret = update_master_cache(states, saltenv) - ret = __salt__['saltcheck.run_state_tests_ssh'](states, saltenv=saltenv, check_all=check_all) + ret = __salt__["saltcheck.run_state_tests_ssh"]( + states, saltenv=saltenv, check_all=check_all + ) return ret -def run_highstate_tests(saltenv='base'): - ''' +def run_highstate_tests(saltenv="base"): + """ Lookup top files for minion, pass results to wrapped run_state_tests for copy and run - ''' - top_states = __salt__['state.show_top']().get(saltenv) - state_string = ','.join(top_states) + """ + top_states = __salt__["state.show_top"]().get(saltenv) + state_string = ",".join(top_states) ret = run_state_tests(state_string, saltenv) return ret diff --git a/salt/client/ssh/wrapper/state.py b/salt/client/ssh/wrapper/state.py index 0e5bcaa1f27..20b2607d197 100644 --- a/salt/client/ssh/wrapper/state.py +++ b/salt/client/ssh/wrapper/state.py @@ -1,17 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Create ssh executor system -''' +""" from __future__ import absolute_import, print_function + +import logging + # Import python libs import os import time -import logging -# Import salt libs -from salt.exceptions import SaltInvocationError import salt.client.ssh.shell import salt.client.ssh.state +import salt.loader +import salt.log +import salt.minion +import salt.roster +import salt.state import salt.utils.args import salt.utils.data import salt.utils.files @@ -21,55 +26,43 @@ import salt.utils.json import salt.utils.platform import salt.utils.state import salt.utils.thin -import salt.roster -import salt.state -import salt.loader -import salt.minion -import salt.log + +# Import salt libs +from salt.exceptions import SaltInvocationError # Import 3rd-party libs from salt.ext import six -__func_alias__ = { - 'apply_': 'apply' -} +__func_alias__ = {"apply_": "apply"} log = logging.getLogger(__name__) -def _ssh_state(chunks, st_kwargs, - kwargs, test=False): - ''' +def _ssh_state(chunks, st_kwargs, kwargs, test=False): + """ Function to run a state with the given chunk via salt-ssh - ''' + """ file_refs = salt.client.ssh.state.lowstate_file_refs( - chunks, - _merge_extra_filerefs( - kwargs.get('extra_filerefs', ''), - __opts__.get('extra_filerefs', '') - ) - ) + chunks, + _merge_extra_filerefs( + kwargs.get("extra_filerefs", ""), __opts__.get("extra_filerefs", "") + ), + ) # Create the tar containing the state pkg and relevant files. trans_tar = salt.client.ssh.state.prep_trans_tar( - __context__['fileclient'], - chunks, - file_refs, - __pillar__, - st_kwargs['id_']) - trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, __opts__['hash_type']) - cmd = 'state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}'.format( - __opts__['thin_dir'], - test, - trans_tar_sum, - __opts__['hash_type']) + __context__["fileclient"], chunks, file_refs, __pillar__, st_kwargs["id_"] + ) + trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, __opts__["hash_type"]) + cmd = "state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}".format( + __opts__["thin_dir"], test, trans_tar_sum, __opts__["hash_type"] + ) single = salt.client.ssh.Single( - __opts__, - cmd, - fsclient=__context__['fileclient'], - minion_opts=__salt__.minion_opts, - **st_kwargs) - single.shell.send( - trans_tar, - '{0}/salt_state.tgz'.format(__opts__['thin_dir'])) + __opts__, + cmd, + fsclient=__context__["fileclient"], + minion_opts=__salt__.minion_opts, + **st_kwargs + ) + single.shell.send(trans_tar, "{0}/salt_state.tgz".format(__opts__["thin_dir"])) stdout, stderr, _ = single.cmd_block() # Clean up our tar @@ -80,7 +73,9 @@ def _ssh_state(chunks, st_kwargs, # Read in the JSON data and return the data structure try: - return salt.utils.data.decode(salt.utils.json.loads(stdout, object_hook=salt.utils.data.encode_dict)) + return salt.utils.data.decode( + salt.utils.json.loads(stdout, object_hook=salt.utils.data.encode_dict) + ) except Exception as e: # pylint: disable=broad-except log.error("JSON Render failed for: %s\n%s", stdout, stderr) log.error(str(e)) @@ -90,38 +85,38 @@ def _ssh_state(chunks, st_kwargs, def _set_retcode(ret, highstate=None): - ''' + """ Set the return code based on the data back from the state system - ''' + """ # Set default retcode to 0 - __context__['retcode'] = 0 + __context__["retcode"] = 0 if isinstance(ret, list): - __context__['retcode'] = 1 + __context__["retcode"] = 1 return if not salt.utils.state.check_result(ret, highstate=highstate): - __context__['retcode'] = 2 + __context__["retcode"] = 2 def _check_pillar(kwargs, pillar=None): - ''' + """ Check the pillar for errors, refuse to run the state if there are errors in the pillar and return the pillar errors - ''' - if kwargs.get('force'): + """ + if kwargs.get("force"): return True pillar_dict = pillar if pillar is not None else __pillar__ - if '_errors' in pillar_dict: + if "_errors" in pillar_dict: return False return True def _wait(jid): - ''' + """ Wait for all previously started state jobs to finish running - ''' + """ if jid is None: jid = salt.utils.jid.gen_jid(__opts__) states = _prior_running_states(jid) @@ -131,75 +126,73 @@ def _wait(jid): def _merge_extra_filerefs(*args): - ''' + """ Takes a list of filerefs and returns a merged list - ''' + """ ret = [] for arg in args: if isinstance(arg, six.string_types): if arg: - ret.extend(arg.split(',')) + ret.extend(arg.split(",")) elif isinstance(arg, list): if arg: ret.extend(arg) - return ','.join(ret) + return ",".join(ret) def _cleanup_slsmod_low_data(low_data): - ''' + """ Set "slsmod" keys to None to make low_data JSON serializable - ''' + """ for i in low_data: - if 'slsmod' in i: - i['slsmod'] = None + if "slsmod" in i: + i["slsmod"] = None def _cleanup_slsmod_high_data(high_data): - ''' + """ Set "slsmod" keys to None to make high_data JSON serializable - ''' + """ for i in six.itervalues(high_data): - if 'stateconf' in i: - stateconf_data = i['stateconf'][1] - if 'slsmod' in stateconf_data: - stateconf_data['slsmod'] = None + if "stateconf" in i: + stateconf_data = i["stateconf"][1] + if "slsmod" in stateconf_data: + stateconf_data["slsmod"] = None def _parse_mods(mods): - ''' + """ Parse modules. - ''' + """ if isinstance(mods, six.string_types): - mods = [item.strip() for item in mods.split(',') if item.strip()] + mods = [item.strip() for item in mods.split(",") if item.strip()] return mods -def sls(mods, saltenv='base', test=None, exclude=None, **kwargs): - ''' +def sls(mods, saltenv="base", test=None, exclude=None, **kwargs): + """ Create the seed file for a state.sls run - ''' + """ st_kwargs = __salt__.kwargs - __opts__['grains'] = __grains__ - __pillar__.update(kwargs.get('pillar', {})) + __opts__["grains"] = __grains__ + __pillar__.update(kwargs.get("pillar", {})) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) st_ = salt.client.ssh.state.SSHHighState( - opts, - __pillar__, - __salt__, - __context__['fileclient']) + opts, __pillar__, __salt__, __context__["fileclient"] + ) st_.push_active() mods = _parse_mods(mods) high_data, errors = st_.render_highstate({saltenv: mods}) if exclude: if isinstance(exclude, six.string_types): - exclude = exclude.split(',') - if '__exclude__' in high_data: - high_data['__exclude__'].extend(exclude) + exclude = exclude.split(",") + if "__exclude__" in high_data: + high_data["__exclude__"].extend(exclude) else: - high_data['__exclude__'] = exclude + high_data["__exclude__"] = exclude high_data, ext_errors = st_.state.reconcile_extend(high_data) errors += ext_errors errors += st_.state.verify_high(high_data) @@ -214,40 +207,37 @@ def sls(mods, saltenv='base', test=None, exclude=None, **kwargs): # Compile and verify the raw chunks chunks = st_.state.compile_high_data(high_data) file_refs = salt.client.ssh.state.lowstate_file_refs( - chunks, - _merge_extra_filerefs( - kwargs.get('extra_filerefs', ''), - opts.get('extra_filerefs', '') - ) - ) + chunks, + _merge_extra_filerefs( + kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "") + ), + ) - roster = salt.roster.Roster(opts, opts.get('roster', 'flat')) - roster_grains = roster.opts['grains'] + roster = salt.roster.Roster(opts, opts.get("roster", "flat")) + roster_grains = roster.opts["grains"] # Create the tar containing the state pkg and relevant files. _cleanup_slsmod_low_data(chunks) trans_tar = salt.client.ssh.state.prep_trans_tar( - __context__['fileclient'], - chunks, - file_refs, - __pillar__, - st_kwargs['id_'], - roster_grains) - trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts['hash_type']) - cmd = 'state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}'.format( - opts['thin_dir'], - test, - trans_tar_sum, - opts['hash_type']) + __context__["fileclient"], + chunks, + file_refs, + __pillar__, + st_kwargs["id_"], + roster_grains, + ) + trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts["hash_type"]) + cmd = "state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}".format( + opts["thin_dir"], test, trans_tar_sum, opts["hash_type"] + ) single = salt.client.ssh.Single( - opts, - cmd, - fsclient=__context__['fileclient'], - minion_opts=__salt__.minion_opts, - **st_kwargs) - single.shell.send( - trans_tar, - '{0}/salt_state.tgz'.format(opts['thin_dir'])) + opts, + cmd, + fsclient=__context__["fileclient"], + minion_opts=__salt__.minion_opts, + **st_kwargs + ) + single.shell.send(trans_tar, "{0}/salt_state.tgz".format(opts["thin_dir"])) stdout, stderr, _ = single.cmd_block() # Clean up our tar @@ -268,7 +258,7 @@ def sls(mods, saltenv='base', test=None, exclude=None, **kwargs): def running(concurrent=False): - ''' + """ Return a list of strings that contain state return data if a state function is already running. This function is used to prevent multiple state calls from being run at the same time. @@ -278,36 +268,36 @@ def running(concurrent=False): .. code-block:: bash salt '*' state.running - ''' + """ ret = [] if concurrent: return ret - active = __salt__['saltutil.is_running']('state.*') + active = __salt__["saltutil.is_running"]("state.*") for data in active: err = ( 'The function "{0}" is running as PID {1} and was started at ' - '{2} with jid {3}' + "{2} with jid {3}" ).format( - data['fun'], - data['pid'], - salt.utils.jid.jid_to_time(data['jid']), - data['jid'], + data["fun"], + data["pid"], + salt.utils.jid.jid_to_time(data["jid"]), + data["jid"], ) ret.append(err) return ret def _prior_running_states(jid): - ''' + """ Return a list of dicts of prior calls to state functions. This function is used to queue state calls so only one is run at a time. - ''' + """ ret = [] - active = __salt__['saltutil.is_running']('state.*') + active = __salt__["saltutil.is_running"]("state.*") for data in active: try: - data_jid = int(data['jid']) + data_jid = int(data["jid"]) except ValueError: continue if data_jid < int(jid): @@ -316,27 +306,30 @@ def _prior_running_states(jid): def _check_queue(queue, kwargs): - ''' + """ Utility function to queue the state run if requested and to check for conflicts in currently running states - ''' + """ if queue: - _wait(kwargs.get('__pub_jid')) + _wait(kwargs.get("__pub_jid")) else: - conflict = running(concurrent=kwargs.get('concurrent', False)) + conflict = running(concurrent=kwargs.get("concurrent", False)) if conflict: - __context__['retcode'] = 1 + __context__["retcode"] = 1 return conflict def _get_initial_pillar(opts): - return __pillar__ if __opts__['__cli'] == 'salt-call' \ - and opts['pillarenv'] == __opts__['pillarenv'] \ + return ( + __pillar__ + if __opts__["__cli"] == "salt-call" + and opts["pillarenv"] == __opts__["pillarenv"] else None + ) def low(data, **kwargs): - ''' + """ Execute a single low data call This function is mostly intended for testing the state system @@ -345,52 +338,48 @@ def low(data, **kwargs): .. code-block:: bash salt '*' state.low '{"state": "pkg", "fun": "installed", "name": "vi"}' - ''' + """ st_kwargs = __salt__.kwargs - __opts__['grains'] = __grains__ + __opts__["grains"] = __grains__ chunks = [data] st_ = salt.client.ssh.state.SSHHighState( - __opts__, - __pillar__, - __salt__, - __context__['fileclient']) + __opts__, __pillar__, __salt__, __context__["fileclient"] + ) for chunk in chunks: - chunk['__id__'] = chunk['name'] if not chunk.get('__id__') else chunk['__id__'] + chunk["__id__"] = chunk["name"] if not chunk.get("__id__") else chunk["__id__"] err = st_.state.verify_data(data) if err: return err file_refs = salt.client.ssh.state.lowstate_file_refs( - chunks, - _merge_extra_filerefs( - kwargs.get('extra_filerefs', ''), - __opts__.get('extra_filerefs', '') - ) - ) - roster = salt.roster.Roster(__opts__, __opts__.get('roster', 'flat')) - roster_grains = roster.opts['grains'] + chunks, + _merge_extra_filerefs( + kwargs.get("extra_filerefs", ""), __opts__.get("extra_filerefs", "") + ), + ) + roster = salt.roster.Roster(__opts__, __opts__.get("roster", "flat")) + roster_grains = roster.opts["grains"] # Create the tar containing the state pkg and relevant files. trans_tar = salt.client.ssh.state.prep_trans_tar( - __context__['fileclient'], - chunks, - file_refs, - __pillar__, - st_kwargs['id_'], - roster_grains) - trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, __opts__['hash_type']) - cmd = 'state.pkg {0}/salt_state.tgz pkg_sum={1} hash_type={2}'.format( - __opts__['thin_dir'], - trans_tar_sum, - __opts__['hash_type']) + __context__["fileclient"], + chunks, + file_refs, + __pillar__, + st_kwargs["id_"], + roster_grains, + ) + trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, __opts__["hash_type"]) + cmd = "state.pkg {0}/salt_state.tgz pkg_sum={1} hash_type={2}".format( + __opts__["thin_dir"], trans_tar_sum, __opts__["hash_type"] + ) single = salt.client.ssh.Single( - __opts__, - cmd, - fsclient=__context__['fileclient'], - minion_opts=__salt__.minion_opts, - **st_kwargs) - single.shell.send( - trans_tar, - '{0}/salt_state.tgz'.format(__opts__['thin_dir'])) + __opts__, + cmd, + fsclient=__context__["fileclient"], + minion_opts=__salt__.minion_opts, + **st_kwargs + ) + single.shell.send(trans_tar, "{0}/salt_state.tgz".format(__opts__["thin_dir"])) stdout, stderr, _ = single.cmd_block() # Clean up our tar @@ -411,22 +400,22 @@ def low(data, **kwargs): def _get_test_value(test=None, **kwargs): - ''' + """ Determine the correct value for the test flag. - ''' + """ ret = True if test is None: if salt.utils.args.test_mode(test=test, **kwargs): ret = True else: - ret = __opts__.get('test', None) + ret = __opts__.get("test", None) else: ret = test return ret def high(data, **kwargs): - ''' + """ Execute the compound calls stored in a single set of high data This function is mostly intended for testing the state system @@ -435,52 +424,48 @@ def high(data, **kwargs): .. code-block:: bash salt '*' state.high '{"vim": {"pkg": ["installed"]}}' - ''' - __pillar__.update(kwargs.get('pillar', {})) + """ + __pillar__.update(kwargs.get("pillar", {})) st_kwargs = __salt__.kwargs - __opts__['grains'] = __grains__ + __opts__["grains"] = __grains__ opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) st_ = salt.client.ssh.state.SSHHighState( - opts, - __pillar__, - __salt__, - __context__['fileclient']) + opts, __pillar__, __salt__, __context__["fileclient"] + ) st_.push_active() chunks = st_.state.compile_high_data(data) file_refs = salt.client.ssh.state.lowstate_file_refs( - chunks, - _merge_extra_filerefs( - kwargs.get('extra_filerefs', ''), - opts.get('extra_filerefs', '') - ) - ) + chunks, + _merge_extra_filerefs( + kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "") + ), + ) - roster = salt.roster.Roster(opts, opts.get('roster', 'flat')) - roster_grains = roster.opts['grains'] + roster = salt.roster.Roster(opts, opts.get("roster", "flat")) + roster_grains = roster.opts["grains"] # Create the tar containing the state pkg and relevant files. _cleanup_slsmod_low_data(chunks) trans_tar = salt.client.ssh.state.prep_trans_tar( - __context__['fileclient'], - chunks, - file_refs, - __pillar__, - st_kwargs['id_'], - roster_grains) - trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts['hash_type']) - cmd = 'state.pkg {0}/salt_state.tgz pkg_sum={1} hash_type={2}'.format( - opts['thin_dir'], - trans_tar_sum, - opts['hash_type']) + __context__["fileclient"], + chunks, + file_refs, + __pillar__, + st_kwargs["id_"], + roster_grains, + ) + trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts["hash_type"]) + cmd = "state.pkg {0}/salt_state.tgz pkg_sum={1} hash_type={2}".format( + opts["thin_dir"], trans_tar_sum, opts["hash_type"] + ) single = salt.client.ssh.Single( - opts, - cmd, - fsclient=__context__['fileclient'], - minion_opts=__salt__.minion_opts, - **st_kwargs) - single.shell.send( - trans_tar, - '{0}/salt_state.tgz'.format(opts['thin_dir'])) + opts, + cmd, + fsclient=__context__["fileclient"], + minion_opts=__salt__.minion_opts, + **st_kwargs + ) + single.shell.send(trans_tar, "{0}/salt_state.tgz".format(opts["thin_dir"])) stdout, stderr, _ = single.cmd_block() # Clean up our tar @@ -500,9 +485,8 @@ def high(data, **kwargs): return stdout -def apply_(mods=None, - **kwargs): - ''' +def apply_(mods=None, **kwargs): + """ .. versionadded:: 2015.5.3 Apply states! This function will call highstate or state.sls based on the @@ -516,15 +500,14 @@ def apply_(mods=None, salt '*' state.apply salt '*' state.apply test salt '*' state.apply test,pkgs - ''' + """ if mods: return sls(mods, **kwargs) return highstate(**kwargs) -def request(mods=None, - **kwargs): - ''' +def request(mods=None, **kwargs): + """ .. versionadded:: 2017.7.3 Request that the local admin execute a state run via @@ -538,35 +521,37 @@ def request(mods=None, salt '*' state.request salt '*' state.request test salt '*' state.request test,pkgs - ''' - kwargs['test'] = True + """ + kwargs["test"] = True ret = apply_(mods, **kwargs) - notify_path = os.path.join(__opts__['cachedir'], 'req_state.p') + notify_path = os.path.join(__opts__["cachedir"], "req_state.p") serial = salt.payload.Serial(__opts__) req = check_request() - req.update({kwargs.get('name', 'default'): { - 'test_run': ret, - 'mods': mods, - 'kwargs': kwargs + req.update( + { + kwargs.get("name", "default"): { + "test_run": ret, + "mods": mods, + "kwargs": kwargs, } - }) + } + ) with salt.utils.files.set_umask(0o077): try: if salt.utils.platform.is_windows(): # Make sure cache file isn't read-only - __salt__['cmd.run']('attrib -R "{0}"'.format(notify_path)) - with salt.utils.files.fopen(notify_path, 'w+b') as fp_: + __salt__["cmd.run"]('attrib -R "{0}"'.format(notify_path)) + with salt.utils.files.fopen(notify_path, "w+b") as fp_: serial.dump(req, fp_) except (IOError, OSError): log.error( - 'Unable to write state request file %s. Check permission.', - notify_path + "Unable to write state request file %s. Check permission.", notify_path ) return ret def check_request(name=None): - ''' + """ .. versionadded:: 2017.7.3 Return the state request information, if any @@ -576,11 +561,11 @@ def check_request(name=None): .. code-block:: bash salt '*' state.check_request - ''' - notify_path = os.path.join(__opts__['cachedir'], 'req_state.p') + """ + notify_path = os.path.join(__opts__["cachedir"], "req_state.p") serial = salt.payload.Serial(__opts__) if os.path.isfile(notify_path): - with salt.utils.files.fopen(notify_path, 'rb') as fp_: + with salt.utils.files.fopen(notify_path, "rb") as fp_: # Not sure if this needs to be decoded since it is being returned, # and msgpack serialization will encode it to bytes anyway. req = serial.load(fp_) @@ -591,7 +576,7 @@ def check_request(name=None): def clear_request(name=None): - ''' + """ .. versionadded:: 2017.7.3 Clear out the state execution request without executing it @@ -601,8 +586,8 @@ def clear_request(name=None): .. code-block:: bash salt '*' state.clear_request - ''' - notify_path = os.path.join(__opts__['cachedir'], 'req_state.p') + """ + notify_path = os.path.join(__opts__["cachedir"], "req_state.p") serial = salt.payload.Serial(__opts__) if not os.path.isfile(notify_path): return True @@ -621,19 +606,19 @@ def clear_request(name=None): try: if salt.utils.platform.is_windows(): # Make sure cache file isn't read-only - __salt__['cmd.run']('attrib -R "{0}"'.format(notify_path)) - with salt.utils.files.fopen(notify_path, 'w+b') as fp_: + __salt__["cmd.run"]('attrib -R "{0}"'.format(notify_path)) + with salt.utils.files.fopen(notify_path, "w+b") as fp_: serial.dump(req, fp_) except (IOError, OSError): log.error( - 'Unable to write state request file %s. Check permission.', - notify_path + "Unable to write state request file %s. Check permission.", + notify_path, ) return True -def run_request(name='default', **kwargs): - ''' +def run_request(name="default", **kwargs): + """ .. versionadded:: 2017.7.3 Execute the pending state request @@ -643,20 +628,20 @@ def run_request(name='default', **kwargs): .. code-block:: bash salt '*' state.run_request - ''' + """ req = check_request() if name not in req: return {} n_req = req[name] - if 'mods' not in n_req or 'kwargs' not in n_req: + if "mods" not in n_req or "kwargs" not in n_req: return {} - req[name]['kwargs'].update(kwargs) - if 'test' in n_req['kwargs']: - n_req['kwargs'].pop('test') + req[name]["kwargs"].update(kwargs) + if "test" in n_req["kwargs"]: + n_req["kwargs"].pop("test") if req: - ret = apply_(n_req['mods'], **n_req['kwargs']) + ret = apply_(n_req["mods"], **n_req["kwargs"]) try: - os.remove(os.path.join(__opts__['cachedir'], 'req_state.p')) + os.remove(os.path.join(__opts__["cachedir"], "req_state.p")) except (IOError, OSError): pass return ret @@ -664,7 +649,7 @@ def run_request(name='default', **kwargs): def highstate(test=None, **kwargs): - ''' + """ Retrieve the state data from the salt master for this minion and execute it CLI Example: @@ -675,58 +660,53 @@ def highstate(test=None, **kwargs): salt '*' state.highstate exclude=sls_to_exclude salt '*' state.highstate exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]" - ''' - __pillar__.update(kwargs.get('pillar', {})) + """ + __pillar__.update(kwargs.get("pillar", {})) st_kwargs = __salt__.kwargs - __opts__['grains'] = __grains__ + __opts__["grains"] = __grains__ opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) st_ = salt.client.ssh.state.SSHHighState( - opts, - __pillar__, - __salt__, - __context__['fileclient']) + opts, __pillar__, __salt__, __context__["fileclient"] + ) st_.push_active() chunks = st_.compile_low_chunks() file_refs = salt.client.ssh.state.lowstate_file_refs( - chunks, - _merge_extra_filerefs( - kwargs.get('extra_filerefs', ''), - opts.get('extra_filerefs', '') - ) - ) + chunks, + _merge_extra_filerefs( + kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "") + ), + ) # Check for errors for chunk in chunks: if not isinstance(chunk, dict): - __context__['retcode'] = 1 + __context__["retcode"] = 1 return chunks - roster = salt.roster.Roster(opts, opts.get('roster', 'flat')) - roster_grains = roster.opts['grains'] + roster = salt.roster.Roster(opts, opts.get("roster", "flat")) + roster_grains = roster.opts["grains"] # Create the tar containing the state pkg and relevant files. _cleanup_slsmod_low_data(chunks) trans_tar = salt.client.ssh.state.prep_trans_tar( - __context__['fileclient'], - chunks, - file_refs, - __pillar__, - st_kwargs['id_'], - roster_grains) - trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts['hash_type']) - cmd = 'state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}'.format( - opts['thin_dir'], - test, - trans_tar_sum, - opts['hash_type']) + __context__["fileclient"], + chunks, + file_refs, + __pillar__, + st_kwargs["id_"], + roster_grains, + ) + trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts["hash_type"]) + cmd = "state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}".format( + opts["thin_dir"], test, trans_tar_sum, opts["hash_type"] + ) single = salt.client.ssh.Single( - opts, - cmd, - fsclient=__context__['fileclient'], - minion_opts=__salt__.minion_opts, - **st_kwargs) - single.shell.send( - trans_tar, - '{0}/salt_state.tgz'.format(opts['thin_dir'])) + opts, + cmd, + fsclient=__context__["fileclient"], + minion_opts=__salt__.minion_opts, + **st_kwargs + ) + single.shell.send(trans_tar, "{0}/salt_state.tgz".format(opts["thin_dir"])) stdout, stderr, _ = single.cmd_block() # Clean up our tar @@ -747,7 +727,7 @@ def highstate(test=None, **kwargs): def top(topfn, test=None, **kwargs): - ''' + """ Execute a specific top file instead of the default CLI Example: @@ -757,58 +737,53 @@ def top(topfn, test=None, **kwargs): salt '*' state.top reverse_top.sls salt '*' state.top reverse_top.sls exclude=sls_to_exclude salt '*' state.top reverse_top.sls exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]" - ''' - __pillar__.update(kwargs.get('pillar', {})) + """ + __pillar__.update(kwargs.get("pillar", {})) st_kwargs = __salt__.kwargs - __opts__['grains'] = __grains__ + __opts__["grains"] = __grains__ opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) if salt.utils.args.test_mode(test=test, **kwargs): - opts['test'] = True + opts["test"] = True else: - opts['test'] = __opts__.get('test', None) + opts["test"] = __opts__.get("test", None) st_ = salt.client.ssh.state.SSHHighState( - opts, - __pillar__, - __salt__, - __context__['fileclient']) - st_.opts['state_top'] = os.path.join('salt://', topfn) + opts, __pillar__, __salt__, __context__["fileclient"] + ) + st_.opts["state_top"] = os.path.join("salt://", topfn) st_.push_active() chunks = st_.compile_low_chunks() file_refs = salt.client.ssh.state.lowstate_file_refs( - chunks, - _merge_extra_filerefs( - kwargs.get('extra_filerefs', ''), - opts.get('extra_filerefs', '') - ) - ) + chunks, + _merge_extra_filerefs( + kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "") + ), + ) - roster = salt.roster.Roster(opts, opts.get('roster', 'flat')) - roster_grains = roster.opts['grains'] + roster = salt.roster.Roster(opts, opts.get("roster", "flat")) + roster_grains = roster.opts["grains"] # Create the tar containing the state pkg and relevant files. _cleanup_slsmod_low_data(chunks) trans_tar = salt.client.ssh.state.prep_trans_tar( - __context__['fileclient'], - chunks, - file_refs, - __pillar__, - st_kwargs['id_'], - roster_grains) - trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts['hash_type']) - cmd = 'state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}'.format( - opts['thin_dir'], - test, - trans_tar_sum, - opts['hash_type']) + __context__["fileclient"], + chunks, + file_refs, + __pillar__, + st_kwargs["id_"], + roster_grains, + ) + trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts["hash_type"]) + cmd = "state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}".format( + opts["thin_dir"], test, trans_tar_sum, opts["hash_type"] + ) single = salt.client.ssh.Single( - opts, - cmd, - fsclient=__context__['fileclient'], - minion_opts=__salt__.minion_opts, - **st_kwargs) - single.shell.send( - trans_tar, - '{0}/salt_state.tgz'.format(opts['thin_dir'])) + opts, + cmd, + fsclient=__context__["fileclient"], + minion_opts=__salt__.minion_opts, + **st_kwargs + ) + single.shell.send(trans_tar, "{0}/salt_state.tgz".format(opts["thin_dir"])) stdout, stderr, _ = single.cmd_block() # Clean up our tar @@ -829,7 +804,7 @@ def top(topfn, test=None, **kwargs): def show_highstate(**kwargs): - ''' + """ Retrieve the highstate data from the salt master and display it CLI Example: @@ -837,14 +812,12 @@ def show_highstate(**kwargs): .. code-block:: bash salt '*' state.show_highstate - ''' - __opts__['grains'] = __grains__ + """ + __opts__["grains"] = __grains__ opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) st_ = salt.client.ssh.state.SSHHighState( - opts, - __pillar__, - __salt__, - __context__['fileclient']) + opts, __pillar__, __salt__, __context__["fileclient"] + ) st_.push_active() chunks = st_.compile_highstate() _cleanup_slsmod_high_data(chunks) @@ -852,7 +825,7 @@ def show_highstate(**kwargs): def show_lowstate(**kwargs): - ''' + """ List out the low data that will be applied to this minion CLI Example: @@ -860,14 +833,12 @@ def show_lowstate(**kwargs): .. code-block:: bash salt '*' state.show_lowstate - ''' - __opts__['grains'] = __grains__ + """ + __opts__["grains"] = __grains__ opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) st_ = salt.client.ssh.state.SSHHighState( - opts, - __pillar__, - __salt__, - __context__['fileclient']) + opts, __pillar__, __salt__, __context__["fileclient"] + ) st_.push_active() chunks = st_.compile_low_chunks() _cleanup_slsmod_low_data(chunks) @@ -875,7 +846,7 @@ def show_lowstate(**kwargs): def sls_id(id_, mods, test=None, queue=False, **kwargs): - ''' + """ Call a single ID from the named module(s) and handle all requisites The state ID comes *before* the module ID(s) on the command line. @@ -905,35 +876,33 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs): salt '*' state.sls_id my_state my_module salt '*' state.sls_id my_state my_module,a_common_module - ''' + """ st_kwargs = __salt__.kwargs conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict - orig_test = __opts__.get('test', None) + orig_test = __opts__.get("test", None) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - opts['test'] = _get_test_value(test, **kwargs) + opts["test"] = _get_test_value(test, **kwargs) # Since this is running a specific ID within a specific SLS file, fall back # to the 'base' saltenv if none is configured and none was passed. - if opts['saltenv'] is None: - opts['saltenv'] = 'base' + if opts["saltenv"] is None: + opts["saltenv"] = "base" st_ = salt.client.ssh.state.SSHHighState( - __opts__, - __pillar__, - __salt__, - __context__['fileclient']) + __opts__, __pillar__, __salt__, __context__["fileclient"] + ) - if not _check_pillar(kwargs, st_.opts['pillar']): - __context__['retcode'] = 5 - err = ['Pillar failed to render with the following messages:'] - err += __pillar__['_errors'] + if not _check_pillar(kwargs, st_.opts["pillar"]): + __context__["retcode"] = 5 + err = ["Pillar failed to render with the following messages:"] + err += __pillar__["_errors"] return err split_mods = _parse_mods(mods) st_.push_active() - high_, errors = st_.render_highstate({opts['saltenv']: split_mods}) + high_, errors = st_.render_highstate({opts["saltenv"]: split_mods}) errors += st_.state.verify_high(high_) # Apply requisites to high data high_, req_in_errors = st_.state.requisite_in(high_) @@ -942,30 +911,27 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs): # but it is required to get the unit tests to pass. errors.extend(req_in_errors) if errors: - __context__['retcode'] = 1 + __context__["retcode"] = 1 return errors chunks = st_.state.compile_high_data(high_) - chunk = [x for x in chunks if x.get('__id__', '') == id_] + chunk = [x for x in chunks if x.get("__id__", "") == id_] if not chunk: raise SaltInvocationError( - 'No matches for ID \'{0}\' found in SLS \'{1}\' within saltenv ' - '\'{2}\''.format(id_, mods, opts['saltenv']) + "No matches for ID '{0}' found in SLS '{1}' within saltenv " + "'{2}'".format(id_, mods, opts["saltenv"]) ) - ret = _ssh_state(chunk, - st_kwargs, - kwargs, - test=test) + ret = _ssh_state(chunk, st_kwargs, kwargs, test=test) _set_retcode(ret, highstate=highstate) # Work around Windows multiprocessing bug, set __opts__['test'] back to # value from before this function was run. - __opts__['test'] = orig_test + __opts__["test"] = orig_test return ret -def show_sls(mods, saltenv='base', test=None, **kwargs): - ''' +def show_sls(mods, saltenv="base", test=None, **kwargs): + """ Display the state data from a specific sls or list of sls files on the master @@ -974,19 +940,17 @@ def show_sls(mods, saltenv='base', test=None, **kwargs): .. code-block:: bash salt '*' state.show_sls core,edit.vim dev - ''' - __pillar__.update(kwargs.get('pillar', {})) - __opts__['grains'] = __grains__ + """ + __pillar__.update(kwargs.get("pillar", {})) + __opts__["grains"] = __grains__ opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) if salt.utils.args.test_mode(test=test, **kwargs): - opts['test'] = True + opts["test"] = True else: - opts['test'] = __opts__.get('test', None) + opts["test"] = __opts__.get("test", None) st_ = salt.client.ssh.state.SSHHighState( - opts, - __pillar__, - __salt__, - __context__['fileclient']) + opts, __pillar__, __salt__, __context__["fileclient"] + ) st_.push_active() mods = _parse_mods(mods) high_data, errors = st_.render_highstate({saltenv: mods}) @@ -1005,8 +969,8 @@ def show_sls(mods, saltenv='base', test=None, **kwargs): return high_data -def show_low_sls(mods, saltenv='base', test=None, **kwargs): - ''' +def show_low_sls(mods, saltenv="base", test=None, **kwargs): + """ Display the low state data from a specific sls or list of sls files on the master. @@ -1017,20 +981,18 @@ def show_low_sls(mods, saltenv='base', test=None, **kwargs): .. code-block:: bash salt '*' state.show_sls core,edit.vim dev - ''' - __pillar__.update(kwargs.get('pillar', {})) - __opts__['grains'] = __grains__ + """ + __pillar__.update(kwargs.get("pillar", {})) + __opts__["grains"] = __grains__ opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) if salt.utils.args.test_mode(test=test, **kwargs): - opts['test'] = True + opts["test"] = True else: - opts['test'] = __opts__.get('test', None) + opts["test"] = __opts__.get("test", None) st_ = salt.client.ssh.state.SSHHighState( - opts, - __pillar__, - __salt__, - __context__['fileclient']) + opts, __pillar__, __salt__, __context__["fileclient"] + ) st_.push_active() mods = _parse_mods(mods) high_data, errors = st_.render_highstate({saltenv: mods}) @@ -1051,7 +1013,7 @@ def show_low_sls(mods, saltenv='base', test=None, **kwargs): def show_top(**kwargs): - ''' + """ Return the top data that the minion will use for a highstate CLI Example: @@ -1059,14 +1021,12 @@ def show_top(**kwargs): .. code-block:: bash salt '*' state.show_top - ''' - __opts__['grains'] = __grains__ + """ + __opts__["grains"] = __grains__ opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) st_ = salt.client.ssh.state.SSHHighState( - opts, - __pillar__, - __salt__, - __context__['fileclient']) + opts, __pillar__, __salt__, __context__["fileclient"] + ) top_data = st_.get_top() errors = [] errors += st_.verify_tops(top_data) @@ -1077,7 +1037,7 @@ def show_top(**kwargs): def single(fun, name, test=None, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 Execute a single state function with the named kwargs, returns False if @@ -1094,32 +1054,29 @@ def single(fun, name, test=None, **kwargs): salt '*' state.single pkg.installed name=vim - ''' + """ st_kwargs = __salt__.kwargs - __opts__['grains'] = __grains__ + __opts__["grains"] = __grains__ # state.fun -> [state, fun] - comps = fun.split('.') + comps = fun.split(".") if len(comps) < 2: - __context__['retcode'] = 1 - return 'Invalid function passed' + __context__["retcode"] = 1 + return "Invalid function passed" # Create the low chunk, using kwargs as a base - kwargs.update({'state': comps[0], - 'fun': comps[1], - '__id__': name, - 'name': name}) + kwargs.update({"state": comps[0], "fun": comps[1], "__id__": name, "name": name}) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) # Set test mode if salt.utils.args.test_mode(test=test, **kwargs): - opts['test'] = True + opts["test"] = True else: - opts['test'] = __opts__.get('test', None) + opts["test"] = __opts__.get("test", None) # Get the override pillar data - __pillar__.update(kwargs.get('pillar', {})) + __pillar__.update(kwargs.get("pillar", {})) # Create the State environment st_ = salt.client.ssh.state.SSHState(opts, __pillar__) @@ -1127,7 +1084,7 @@ def single(fun, name, test=None, **kwargs): # Verify the low chunk err = st_.verify_data(kwargs) if err: - __context__['retcode'] = 1 + __context__["retcode"] = 1 return err # Must be a list of low-chunks @@ -1136,47 +1093,44 @@ def single(fun, name, test=None, **kwargs): # Retrieve file refs for the state run, so we can copy relevant files down # to the minion before executing the state file_refs = salt.client.ssh.state.lowstate_file_refs( - chunks, - _merge_extra_filerefs( - kwargs.get('extra_filerefs', ''), - opts.get('extra_filerefs', '') - ) - ) + chunks, + _merge_extra_filerefs( + kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "") + ), + ) - roster = salt.roster.Roster(opts, opts.get('roster', 'flat')) - roster_grains = roster.opts['grains'] + roster = salt.roster.Roster(opts, opts.get("roster", "flat")) + roster_grains = roster.opts["grains"] # Create the tar containing the state pkg and relevant files. trans_tar = salt.client.ssh.state.prep_trans_tar( - __context__['fileclient'], - chunks, - file_refs, - __pillar__, - st_kwargs['id_'], - roster_grains) + __context__["fileclient"], + chunks, + file_refs, + __pillar__, + st_kwargs["id_"], + roster_grains, + ) # Create a hash so we can verify the tar on the target system - trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts['hash_type']) + trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, opts["hash_type"]) # We use state.pkg to execute the "state package" - cmd = 'state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}'.format( - opts['thin_dir'], - test, - trans_tar_sum, - opts['hash_type']) + cmd = "state.pkg {0}/salt_state.tgz test={1} pkg_sum={2} hash_type={3}".format( + opts["thin_dir"], test, trans_tar_sum, opts["hash_type"] + ) # Create a salt-ssh Single object to actually do the ssh work single = salt.client.ssh.Single( - opts, - cmd, - fsclient=__context__['fileclient'], - minion_opts=__salt__.minion_opts, - **st_kwargs) + opts, + cmd, + fsclient=__context__["fileclient"], + minion_opts=__salt__.minion_opts, + **st_kwargs + ) # Copy the tar down - single.shell.send( - trans_tar, - '{0}/salt_state.tgz'.format(opts['thin_dir'])) + single.shell.send(trans_tar, "{0}/salt_state.tgz".format(opts["thin_dir"])) # Run the state.pkg command on the target stdout, stderr, _ = single.cmd_block() diff --git a/salt/cloud/__init__.py b/salt/cloud/__init__.py index ad170f88726..a2ae81445db 100644 --- a/salt/cloud/__init__.py +++ b/salt/cloud/__init__.py @@ -1,34 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" The top level interface used to translate configuration data back to the correct cloud modules -''' +""" # Import python libs -from __future__ import absolute_import, print_function, generators, unicode_literals -import os +from __future__ import absolute_import, generators, print_function, unicode_literals + import copy import glob -import time -import signal import logging -import traceback import multiprocessing +import os +import signal import sys +import time +import traceback from itertools import groupby -# Import salt.cloud libs -from salt.exceptions import ( - SaltCloudNotFound, - SaltCloudException, - SaltCloudSystemExit, - SaltCloudConfigError -) +import salt.client # Import salt libs import salt.config -import salt.client import salt.loader +import salt.syspaths import salt.utils.args import salt.utils.cloud import salt.utils.context @@ -36,10 +31,19 @@ import salt.utils.crypt import salt.utils.data import salt.utils.dictupdate import salt.utils.files +import salt.utils.user import salt.utils.verify import salt.utils.yaml -import salt.utils.user -import salt.syspaths + +# Import salt.cloud libs +from salt.exceptions import ( + SaltCloudConfigError, + SaltCloudException, + SaltCloudNotFound, + SaltCloudSystemExit, +) +from salt.ext import six +from salt.ext.six.moves import input # pylint: disable=import-error,redefined-builtin from salt.template import compile_template # Import third party libs @@ -50,45 +54,47 @@ except ImportError: import Crypto.Random except ImportError: pass # pycrypto < 2.1 -from salt.ext import six -from salt.ext.six.moves import input # pylint: disable=import-error,redefined-builtin # Get logging started log = logging.getLogger(__name__) def communicator(func): - '''Warning, this is a picklable decorator !''' + """Warning, this is a picklable decorator !""" + def _call(queue, args, kwargs): - '''called with [queue, args, kwargs] as first optional arg''' - kwargs['queue'] = queue + """called with [queue, args, kwargs] as first optional arg""" + kwargs["queue"] = queue ret = None try: ret = func(*args, **kwargs) - queue.put('END') + queue.put("END") except KeyboardInterrupt as ex: trace = traceback.format_exc() - queue.put('KEYBOARDINT') - queue.put('Keyboard interrupt') - queue.put('{0}\n{1}\n'.format(ex, trace)) + queue.put("KEYBOARDINT") + queue.put("Keyboard interrupt") + queue.put("{0}\n{1}\n".format(ex, trace)) except Exception as ex: # pylint: disable=broad-except trace = traceback.format_exc() - queue.put('ERROR') - queue.put('Exception') - queue.put('{0}\n{1}\n'.format(ex, trace)) + queue.put("ERROR") + queue.put("Exception") + queue.put("{0}\n{1}\n".format(ex, trace)) return ret + return _call -def enter_mainloop(target, - mapped_args=None, - args=None, - kwargs=None, - pool=None, - pool_size=None, - callback=None, - queue=None): - ''' +def enter_mainloop( + target, + mapped_args=None, + args=None, + kwargs=None, + pool=None, + pool_size=None, + callback=None, + queue=None, +): + """ Manage a multiprocessing pool - If the queue does not output anything, the pool runs indefinitely @@ -134,7 +140,7 @@ def enter_mainloop(target, You may use the 'communicator' decorator to generate such a function (see end of this file) - ''' + """ if not kwargs: kwargs = {} if not pool_size: @@ -147,11 +153,12 @@ def enter_mainloop(target, if mapped_args is not None and not mapped_args: msg = ( - 'We are called to asynchronously execute {0}' - ' but we do no have anything to execute, weird,' - ' we bail out'.format(target)) + "We are called to asynchronously execute {0}" + " but we do no have anything to execute, weird," + " we bail out".format(target) + ) log.error(msg) - raise SaltCloudSystemExit('Exception caught\n{0}'.format(msg)) + raise SaltCloudSystemExit("Exception caught\n{0}".format(msg)) elif mapped_args is not None: iterable = [[queue, [arg], kwargs] for arg in mapped_args] ret = pool.map(func=target, iterable=iterable) @@ -159,16 +166,16 @@ def enter_mainloop(target, ret = pool.apply(target, [queue, args, kwargs]) while True: test = queue.get() - if test in ['ERROR', 'KEYBOARDINT']: + if test in ["ERROR", "KEYBOARDINT"]: type_ = queue.get() trace = queue.get() - msg = 'Caught {0}, terminating workers\n'.format(type_) - msg += 'TRACE: {0}\n'.format(trace) + msg = "Caught {0}, terminating workers\n".format(type_) + msg += "TRACE: {0}\n".format(trace) log.error(msg) pool.terminate() pool.join() - raise SaltCloudSystemExit('Exception caught\n{0}'.format(msg)) - elif test in ['END'] or (callback and callback(test)): + raise SaltCloudSystemExit("Exception caught\n{0}".format(msg)) + elif test in ["END"] or (callback and callback(test)): pool.close() pool.join() break @@ -178,9 +185,10 @@ def enter_mainloop(target, class CloudClient(object): - ''' + """ The client class to wrap cloud interactions - ''' + """ + def __init__(self, path=None, opts=None, config_dir=None, pillars=None): if opts: self.opts = opts @@ -188,136 +196,134 @@ class CloudClient(object): self.opts = salt.config.cloud_config(path) # Check the cache-dir exists. If not, create it. - v_dirs = [self.opts['cachedir']] + v_dirs = [self.opts["cachedir"]] salt.utils.verify.verify_env(v_dirs, salt.utils.user.get_user()) if pillars: - for name, provider in six.iteritems(pillars.pop('providers', {})): - driver = provider['driver'] - provider['profiles'] = {} - self.opts['providers'].update({name: {driver: provider}}) - for name, profile in six.iteritems(pillars.pop('profiles', {})): - provider = profile['provider'].split(':')[0] - driver = next(six.iterkeys(self.opts['providers'][provider])) - profile['provider'] = '{0}:{1}'.format(provider, driver) - profile['profile'] = name - self.opts['profiles'].update({name: profile}) - self.opts['providers'][provider][driver]['profiles'].update({name: profile}) - for name, map_dct in six.iteritems(pillars.pop('maps', {})): - if 'maps' not in self.opts: - self.opts['maps'] = {} - self.opts['maps'][name] = map_dct + for name, provider in six.iteritems(pillars.pop("providers", {})): + driver = provider["driver"] + provider["profiles"] = {} + self.opts["providers"].update({name: {driver: provider}}) + for name, profile in six.iteritems(pillars.pop("profiles", {})): + provider = profile["provider"].split(":")[0] + driver = next(six.iterkeys(self.opts["providers"][provider])) + profile["provider"] = "{0}:{1}".format(provider, driver) + profile["profile"] = name + self.opts["profiles"].update({name: profile}) + self.opts["providers"][provider][driver]["profiles"].update( + {name: profile} + ) + for name, map_dct in six.iteritems(pillars.pop("maps", {})): + if "maps" not in self.opts: + self.opts["maps"] = {} + self.opts["maps"][name] = map_dct self.opts.update(pillars) def _opts_defaults(self, **kwargs): - ''' + """ Set the opts dict to defaults and allow for opts to be overridden in the kwargs - ''' + """ # Let's start with the default salt cloud configuration opts = salt.config.DEFAULT_CLOUD_OPTS.copy() # Update it with the loaded configuration opts.update(self.opts.copy()) # Reset some of the settings to sane values - opts['parallel'] = False - opts['keep_tmp'] = False - opts['deploy'] = True - opts['update_bootstrap'] = False - opts['show_deploy_args'] = False - opts['script_args'] = '' + opts["parallel"] = False + opts["keep_tmp"] = False + opts["deploy"] = True + opts["update_bootstrap"] = False + opts["show_deploy_args"] = False + opts["script_args"] = "" # Update it with the passed kwargs - if 'kwargs' in kwargs: - opts.update(kwargs['kwargs']) + if "kwargs" in kwargs: + opts.update(kwargs["kwargs"]) opts.update(kwargs) - profile = opts.get('profile', None) + profile = opts.get("profile", None) # filter other profiles if one is specified if profile: - tmp_profiles = opts.get('profiles', {}).copy() + tmp_profiles = opts.get("profiles", {}).copy() for _profile in [a for a in tmp_profiles]: if not _profile == profile: tmp_profiles.pop(_profile) # if profile is specified and we have enough info about providers # also filter them to speedup methods like # __filter_non_working_providers - providers = [a.get('provider', '').split(':')[0] - for a in six.itervalues(tmp_profiles) - if a.get('provider', '')] + providers = [ + a.get("provider", "").split(":")[0] + for a in six.itervalues(tmp_profiles) + if a.get("provider", "") + ] if providers: - _providers = opts.get('providers', {}) + _providers = opts.get("providers", {}) for provider in _providers.copy(): if provider not in providers: _providers.pop(provider) return opts def low(self, fun, low): - ''' + """ Pass the cloud function and low data structure to run - ''' + """ l_fun = getattr(self, fun) f_call = salt.utils.args.format_call(l_fun, low) - return l_fun(*f_call.get('args', ()), **f_call.get('kwargs', {})) + return l_fun(*f_call.get("args", ()), **f_call.get("kwargs", {})) def list_sizes(self, provider=None): - ''' + """ List all available sizes in configured cloud systems - ''' + """ mapper = salt.cloud.Map(self._opts_defaults()) - return salt.utils.data.simple_types_filter( - mapper.size_list(provider) - ) + return salt.utils.data.simple_types_filter(mapper.size_list(provider)) def list_images(self, provider=None): - ''' + """ List all available images in configured cloud systems - ''' + """ mapper = salt.cloud.Map(self._opts_defaults()) - return salt.utils.data.simple_types_filter( - mapper.image_list(provider) - ) + return salt.utils.data.simple_types_filter(mapper.image_list(provider)) def list_locations(self, provider=None): - ''' + """ List all available locations in configured cloud systems - ''' + """ mapper = salt.cloud.Map(self._opts_defaults()) - return salt.utils.data.simple_types_filter( - mapper.location_list(provider) - ) + return salt.utils.data.simple_types_filter(mapper.location_list(provider)) - def query(self, query_type='list_nodes'): - ''' + def query(self, query_type="list_nodes"): + """ Query basic instance information - ''' + """ mapper = salt.cloud.Map(self._opts_defaults()) - mapper.opts['selected_query_option'] = 'list_nodes' + mapper.opts["selected_query_option"] = "list_nodes" return mapper.map_providers_parallel(query_type) - def full_query(self, query_type='list_nodes_full'): - ''' + def full_query(self, query_type="list_nodes_full"): + """ Query all instance information - ''' + """ mapper = salt.cloud.Map(self._opts_defaults()) - mapper.opts['selected_query_option'] = 'list_nodes_full' + mapper.opts["selected_query_option"] = "list_nodes_full" return mapper.map_providers_parallel(query_type) - def select_query(self, query_type='list_nodes_select'): - ''' + def select_query(self, query_type="list_nodes_select"): + """ Query select instance information - ''' + """ mapper = salt.cloud.Map(self._opts_defaults()) - mapper.opts['selected_query_option'] = 'list_nodes_select' + mapper.opts["selected_query_option"] = "list_nodes_select" return mapper.map_providers_parallel(query_type) - def min_query(self, query_type='list_nodes_min'): - ''' + def min_query(self, query_type="list_nodes_min"): + """ Query select instance information - ''' + """ mapper = salt.cloud.Map(self._opts_defaults()) - mapper.opts['selected_query_option'] = 'list_nodes_min' + mapper.opts["selected_query_option"] = "list_nodes_min" return mapper.map_providers_parallel(query_type) def profile(self, profile, names, vm_overrides=None, **kwargs): - ''' + """ Pass in a profile to create, names is a list of vm names to allocate vm_overrides is a special dict that will be per node options @@ -347,44 +353,40 @@ class CloudClient(object): 'status': 'new'}} - ''' + """ if not vm_overrides: vm_overrides = {} - kwargs['profile'] = profile + kwargs["profile"] = profile mapper = salt.cloud.Map(self._opts_defaults(**kwargs)) if isinstance(names, six.string_types): - names = names.split(',') + names = names.split(",") return salt.utils.data.simple_types_filter( mapper.run_profile(profile, names, vm_overrides=vm_overrides) ) def map_run(self, path=None, **kwargs): - ''' + """ To execute a map - ''' + """ kwarg = {} if path: - kwarg['map'] = path + kwarg["map"] = path kwarg.update(kwargs) mapper = salt.cloud.Map(self._opts_defaults(**kwarg)) dmap = mapper.map_data() - return salt.utils.data.simple_types_filter( - mapper.run_map(dmap) - ) + return salt.utils.data.simple_types_filter(mapper.run_map(dmap)) def destroy(self, names): - ''' + """ Destroy the named VMs - ''' + """ mapper = salt.cloud.Map(self._opts_defaults(destroy=True)) if isinstance(names, six.string_types): - names = names.split(',') - return salt.utils.data.simple_types_filter( - mapper.destroy(names) - ) + names = names.split(",") + return salt.utils.data.simple_types_filter(mapper.destroy(names)) def create(self, provider, names, **kwargs): - ''' + """ Create the named VMs, without using a profile Example: @@ -394,20 +396,20 @@ class CloudClient(object): client.create(provider='my-ec2-config', names=['myinstance'], image='ami-1624987f', size='t1.micro', ssh_username='ec2-user', securitygroup='default', delvol_on_destroy=True) - ''' + """ mapper = salt.cloud.Map(self._opts_defaults()) - providers = self.opts['providers'] + providers = self.opts["providers"] if provider in providers: - provider += ':{0}'.format(next(six.iterkeys(providers[provider]))) + provider += ":{0}".format(next(six.iterkeys(providers[provider]))) else: return False if isinstance(names, six.string_types): - names = names.split(',') + names = names.split(",") ret = {} for name in names: vm_ = kwargs.copy() - vm_['name'] = name - vm_['driver'] = provider + vm_["name"] = name + vm_["driver"] = provider # This function doesn't require a profile, but many cloud drivers # check for profile information (which includes the provider key) to @@ -415,15 +417,14 @@ class CloudClient(object): # the profile and provider defaults here avoids errors in other # cloud functions relying on these keys. See SaltStack Issue #41971 # and PR #38166 for more information. - vm_['profile'] = None - vm_['provider'] = provider + vm_["profile"] = None + vm_["provider"] = provider - ret[name] = salt.utils.data.simple_types_filter( - mapper.create(vm_)) + ret[name] = salt.utils.data.simple_types_filter(mapper.create(vm_)) return ret def extra_action(self, names, provider, action, **kwargs): - ''' + """ Perform actions with block storage devices Example: @@ -436,26 +437,24 @@ class CloudClient(object): client.extra_action(names=['salt-net'], action='network_create', provider='my-nova', kwargs={'cidr': '192.168.100.0/24'} ) - ''' + """ mapper = salt.cloud.Map(self._opts_defaults()) providers = mapper.map_providers_parallel() if provider in providers: - provider += ':{0}'.format(next(six.iterkeys(providers[provider]))) + provider += ":{0}".format(next(six.iterkeys(providers[provider]))) else: return False if isinstance(names, six.string_types): - names = names.split(',') + names = names.split(",") ret = {} for name in names: extra_ = kwargs.copy() - extra_['name'] = name - extra_['provider'] = provider - extra_['profile'] = None - extra_['action'] = action - ret[name] = salt.utils.data.simple_types_filter( - mapper.extras(extra_) - ) + extra_["name"] = name + extra_["provider"] = provider + extra_["profile"] = None + extra_["action"] = action + ret[name] = salt.utils.data.simple_types_filter(mapper.extras(extra_)) return ret def action( @@ -465,9 +464,9 @@ class CloudClient(object): names=None, provider=None, instance=None, - kwargs=None + kwargs=None, ): - ''' + """ Execute a single action via the cloud plugin backend Examples: @@ -478,24 +477,21 @@ class CloudClient(object): client.action(fun='show_image', provider='my-ec2-config', kwargs={'image': 'ami-10314d79'} ) - ''' + """ if kwargs is None: kwargs = {} - mapper = salt.cloud.Map(self._opts_defaults( - action=fun, - names=names, - **kwargs)) + mapper = salt.cloud.Map(self._opts_defaults(action=fun, names=names, **kwargs)) if instance: if names: raise SaltCloudConfigError( - 'Please specify either a list of \'names\' or a single ' - '\'instance\', but not both.' + "Please specify either a list of 'names' or a single " + "'instance', but not both." ) names = [instance] if names and not provider: - self.opts['action'] = fun + self.opts["action"] = fun return mapper.do_action(names, kwargs) if provider and not names: @@ -506,15 +502,16 @@ class CloudClient(object): # are given, then we also need to exit. We can only have one # or the other. raise SaltCloudConfigError( - 'Either an instance (or list of names) or a provider must be ' - 'specified, but not both.' + "Either an instance (or list of names) or a provider must be " + "specified, but not both." ) class Cloud(object): - ''' + """ An object for the creation of new VMs - ''' + """ + def __init__(self, opts): self.opts = opts self.clouds = salt.loader.clouds(self.opts) @@ -522,80 +519,80 @@ class Cloud(object): self.__cached_provider_queries = {} def get_configured_providers(self): - ''' + """ Return the configured providers - ''' + """ providers = set() - for alias, drivers in six.iteritems(self.opts['providers']): + for alias, drivers in six.iteritems(self.opts["providers"]): if len(drivers) > 1: for driver in drivers: - providers.add('{0}:{1}'.format(alias, driver)) + providers.add("{0}:{1}".format(alias, driver)) continue providers.add(alias) return providers def lookup_providers(self, lookup): - ''' + """ Get a dict describing the configured providers - ''' + """ if lookup is None: - lookup = 'all' - if lookup == 'all': + lookup = "all" + if lookup == "all": providers = set() - for alias, drivers in six.iteritems(self.opts['providers']): + for alias, drivers in six.iteritems(self.opts["providers"]): for driver in drivers: providers.add((alias, driver)) if not providers: - raise SaltCloudSystemExit( - 'There are no cloud providers configured.' - ) + raise SaltCloudSystemExit("There are no cloud providers configured.") return providers - if ':' in lookup: - alias, driver = lookup.split(':') - if alias not in self.opts['providers'] or \ - driver not in self.opts['providers'][alias]: + if ":" in lookup: + alias, driver = lookup.split(":") + if ( + alias not in self.opts["providers"] + or driver not in self.opts["providers"][alias] + ): raise SaltCloudSystemExit( - 'No cloud providers matched \'{0}\'. Available: {1}'.format( - lookup, ', '.join(self.get_configured_providers()) + "No cloud providers matched '{0}'. Available: {1}".format( + lookup, ", ".join(self.get_configured_providers()) ) ) providers = set() - for alias, drivers in six.iteritems(self.opts['providers']): + for alias, drivers in six.iteritems(self.opts["providers"]): for driver in drivers: if lookup in (alias, driver): providers.add((alias, driver)) if not providers: raise SaltCloudSystemExit( - 'No cloud providers matched \'{0}\'. ' - 'Available selections: {1}'.format( - lookup, ', '.join(self.get_configured_providers()) + "No cloud providers matched '{0}'. " + "Available selections: {1}".format( + lookup, ", ".join(self.get_configured_providers()) ) ) return providers def lookup_profiles(self, provider, lookup): - ''' + """ Return a dictionary describing the configured profiles - ''' + """ if provider is None: - provider = 'all' + provider = "all" if lookup is None: - lookup = 'all' + lookup = "all" - if lookup == 'all': + if lookup == "all": profiles = set() provider_profiles = set() - for alias, info in six.iteritems(self.opts['profiles']): - providers = info.get('provider') + for alias, info in six.iteritems(self.opts["profiles"]): + providers = info.get("provider") if providers: - given_prov_name = providers.split(':')[0] - salt_prov_name = providers.split(':')[1] + given_prov_name = providers.split(":")[0] + salt_prov_name = providers.split(":")[1] if given_prov_name == provider: provider_profiles.add((alias, given_prov_name)) elif salt_prov_name == provider: @@ -603,29 +600,27 @@ class Cloud(object): profiles.add((alias, given_prov_name)) if not profiles: - raise SaltCloudSystemExit( - 'There are no cloud profiles configured.' - ) + raise SaltCloudSystemExit("There are no cloud profiles configured.") - if provider != 'all': + if provider != "all": return provider_profiles return profiles - def map_providers(self, query='list_nodes', cached=False): - ''' + def map_providers(self, query="list_nodes", cached=False): + """ Return a mapping of what named VMs are running on what VM providers based on what providers are defined in the configuration and VMs - ''' + """ if cached is True and query in self.__cached_provider_queries: return self.__cached_provider_queries[query] pmap = {} - for alias, drivers in six.iteritems(self.opts['providers']): + for alias, drivers in six.iteritems(self.opts["providers"]): for driver, details in six.iteritems(drivers): - fun = '{0}.{1}'.format(driver, query) + fun = "{0}.{1}".format(driver, query) if fun not in self.clouds: - log.error('Public cloud provider %s is not available', driver) + log.error("Public cloud provider %s is not available", driver) continue if alias not in pmap: pmap[alias] = {} @@ -633,14 +628,16 @@ class Cloud(object): try: with salt.utils.context.func_globals_inject( self.clouds[fun], - __active_provider_name__=':'.join([alias, driver]) + __active_provider_name__=":".join([alias, driver]), ): pmap[alias][driver] = self.clouds[fun]() except Exception as err: # pylint: disable=broad-except log.debug( - 'Failed to execute \'%s()\' while querying for ' - 'running nodes: %s', fun, err, - exc_info_on_loglevel=logging.DEBUG + "Failed to execute '%s()' while querying for " + "running nodes: %s", + fun, + err, + exc_info_on_loglevel=logging.DEBUG, ) # Failed to communicate with the provider, don't list any # nodes @@ -648,13 +645,13 @@ class Cloud(object): self.__cached_provider_queries[query] = pmap return pmap - def map_providers_parallel(self, query='list_nodes', cached=False): - ''' + def map_providers_parallel(self, query="list_nodes", cached=False): + """ Return a mapping of what named VMs are running on what VM providers based on what providers are defined in the configuration and VMs Same as map_providers but query in parallel. - ''' + """ if cached is True and query in self.__cached_provider_queries: return self.__cached_provider_queries[query] @@ -662,39 +659,45 @@ class Cloud(object): multiprocessing_data = [] # Optimize Providers - opts['providers'] = self._optimize_providers(opts['providers']) - for alias, drivers in six.iteritems(opts['providers']): + opts["providers"] = self._optimize_providers(opts["providers"]) + for alias, drivers in six.iteritems(opts["providers"]): # Make temp query for this driver to avoid overwrite next this_query = query for driver, details in six.iteritems(drivers): # If driver has function list_nodes_min, just replace it # with query param to check existing vms on this driver # for minimum information, Otherwise still use query param. - if opts.get('selected_query_option') is None and '{0}.list_nodes_min'.format(driver) in self.clouds: - this_query = 'list_nodes_min' + if ( + opts.get("selected_query_option") is None + and "{0}.list_nodes_min".format(driver) in self.clouds + ): + this_query = "list_nodes_min" - fun = '{0}.{1}'.format(driver, this_query) + fun = "{0}.{1}".format(driver, this_query) if fun not in self.clouds: - log.error('Public cloud provider %s is not available', driver) + log.error("Public cloud provider %s is not available", driver) continue - multiprocessing_data.append({ - 'fun': fun, - 'opts': opts, - 'query': this_query, - 'alias': alias, - 'driver': driver - }) + multiprocessing_data.append( + { + "fun": fun, + "opts": opts, + "query": this_query, + "alias": alias, + "driver": driver, + } + ) output = {} if not multiprocessing_data: return output data_count = len(multiprocessing_data) - pool = multiprocessing.Pool(data_count < 10 and data_count or 10, - init_pool_worker) - parallel_pmap = enter_mainloop(_run_parallel_map_providers_query, - multiprocessing_data, - pool=pool) + pool = multiprocessing.Pool( + data_count < 10 and data_count or 10, init_pool_worker + ) + parallel_pmap = enter_mainloop( + _run_parallel_map_providers_query, multiprocessing_data, pool=pool + ) for alias, driver, details in parallel_pmap: if not details: # There's no providers details?! Skip it! @@ -706,8 +709,9 @@ class Cloud(object): self.__cached_provider_queries[query] = output return output - def get_running_by_names(self, names, query='list_nodes', cached=False, - profile=None): + def get_running_by_names( + self, names, query="list_nodes", cached=False, profile=None + ): if isinstance(names, six.string_types): names = [names] @@ -726,7 +730,11 @@ class Cloud(object): # the search returns the same instance for each provider # because amazon returns all instances in a region, not # availability zone. - if profile and alias not in self.opts['profiles'][profile]['provider'].split(':')[0]: + if ( + profile + and alias + not in self.opts["profiles"][profile]["provider"].split(":")[0] + ): continue for vm_name, details in six.iteritems(vms): @@ -735,13 +743,19 @@ class Cloud(object): if vm_name not in names: continue - elif driver == 'ec2' and 'aws' in handled_drivers and \ - 'aws' in matches[handled_drivers['aws']] and \ - vm_name in matches[handled_drivers['aws']]['aws']: + elif ( + driver == "ec2" + and "aws" in handled_drivers + and "aws" in matches[handled_drivers["aws"]] + and vm_name in matches[handled_drivers["aws"]]["aws"] + ): continue - elif driver == 'aws' and 'ec2' in handled_drivers and \ - 'ec2' in matches[handled_drivers['ec2']] and \ - vm_name in matches[handled_drivers['ec2']]['ec2']: + elif ( + driver == "aws" + and "ec2" in handled_drivers + and "ec2" in matches[handled_drivers["ec2"]] + and vm_name in matches[handled_drivers["ec2"]]["ec2"] + ): continue if alias not in matches: @@ -753,9 +767,9 @@ class Cloud(object): return matches def _optimize_providers(self, providers): - ''' + """ Return an optimized mapping of available providers - ''' + """ new_providers = {} provider_by_driver = {} @@ -767,9 +781,9 @@ class Cloud(object): provider_by_driver[name][alias] = data for driver, providers_data in six.iteritems(provider_by_driver): - fun = '{0}.optimize_providers'.format(driver) + fun = "{0}.optimize_providers".format(driver) if fun not in self.clouds: - log.debug('The \'%s\' cloud driver is unable to be optimized.', driver) + log.debug("The '%s' cloud driver is unable to be optimized.", driver) for name, prov_data in six.iteritems(providers_data): if name not in new_providers: @@ -786,10 +800,10 @@ class Cloud(object): return new_providers - def location_list(self, lookup='all'): - ''' + def location_list(self, lookup="all"): + """ Return a mapping of all location data for available providers - ''' + """ data = {} lookups = self.lookup_providers(lookup) @@ -797,14 +811,15 @@ class Cloud(object): return data for alias, driver in lookups: - fun = '{0}.avail_locations'.format(driver) + fun = "{0}.avail_locations".format(driver) if fun not in self.clouds: # The capability to gather locations is not supported by this # cloud module log.debug( - 'The \'%s\' cloud driver defined under \'%s\' provider ' - 'alias is unable to get the locations information', - driver, alias + "The '%s' cloud driver defined under '%s' provider " + "alias is unable to get the locations information", + driver, + alias, ) continue @@ -814,21 +829,22 @@ class Cloud(object): try: with salt.utils.context.func_globals_inject( - self.clouds[fun], - __active_provider_name__=':'.join([alias, driver]) + self.clouds[fun], __active_provider_name__=":".join([alias, driver]) ): data[alias][driver] = self.clouds[fun]() except Exception as err: # pylint: disable=broad-except log.error( - 'Failed to get the output of \'%s()\': %s', - fun, err, exc_info_on_loglevel=logging.DEBUG + "Failed to get the output of '%s()': %s", + fun, + err, + exc_info_on_loglevel=logging.DEBUG, ) return data - def image_list(self, lookup='all'): - ''' + def image_list(self, lookup="all"): + """ Return a mapping of all image data for available providers - ''' + """ data = {} lookups = self.lookup_providers(lookup) @@ -836,14 +852,15 @@ class Cloud(object): return data for alias, driver in lookups: - fun = '{0}.avail_images'.format(driver) + fun = "{0}.avail_images".format(driver) if fun not in self.clouds: # The capability to gather images is not supported by this # cloud module log.debug( - 'The \'%s\' cloud driver defined under \'%s\' provider ' - 'alias is unable to get the images information', - driver, alias + "The '%s' cloud driver defined under '%s' provider " + "alias is unable to get the images information", + driver, + alias, ) continue @@ -852,21 +869,22 @@ class Cloud(object): try: with salt.utils.context.func_globals_inject( - self.clouds[fun], - __active_provider_name__=':'.join([alias, driver]) + self.clouds[fun], __active_provider_name__=":".join([alias, driver]) ): data[alias][driver] = self.clouds[fun]() except Exception as err: # pylint: disable=broad-except log.error( - 'Failed to get the output of \'%s()\': %s', - fun, err, exc_info_on_loglevel=logging.DEBUG + "Failed to get the output of '%s()': %s", + fun, + err, + exc_info_on_loglevel=logging.DEBUG, ) return data - def size_list(self, lookup='all'): - ''' + def size_list(self, lookup="all"): + """ Return a mapping of all image data for available providers - ''' + """ data = {} lookups = self.lookup_providers(lookup) @@ -874,14 +892,15 @@ class Cloud(object): return data for alias, driver in lookups: - fun = '{0}.avail_sizes'.format(driver) + fun = "{0}.avail_sizes".format(driver) if fun not in self.clouds: # The capability to gather sizes is not supported by this # cloud module log.debug( - 'The \'%s\' cloud driver defined under \'%s\' provider ' - 'alias is unable to get the sizes information', - driver, alias + "The '%s' cloud driver defined under '%s' provider " + "alias is unable to get the sizes information", + driver, + alias, ) continue @@ -890,21 +909,22 @@ class Cloud(object): try: with salt.utils.context.func_globals_inject( - self.clouds[fun], - __active_provider_name__=':'.join([alias, driver]) + self.clouds[fun], __active_provider_name__=":".join([alias, driver]) ): data[alias][driver] = self.clouds[fun]() except Exception as err: # pylint: disable=broad-except log.error( - 'Failed to get the output of \'%s()\': %s', - fun, err, exc_info_on_loglevel=logging.DEBUG + "Failed to get the output of '%s()': %s", + fun, + err, + exc_info_on_loglevel=logging.DEBUG, ) return data - def provider_list(self, lookup='all'): - ''' + def provider_list(self, lookup="all"): + """ Return a mapping of all image data for available providers - ''' + """ data = {} lookups = self.lookup_providers(lookup) if not lookups: @@ -917,10 +937,10 @@ class Cloud(object): data[alias][driver] = {} return data - def profile_list(self, provider, lookup='all'): - ''' + def profile_list(self, provider, lookup="all"): + """ Return a mapping of all configured profiles - ''' + """ data = {} lookups = self.lookup_profiles(provider, lookup) @@ -935,22 +955,20 @@ class Cloud(object): return data def create_all(self): - ''' + """ Create/Verify the VMs in the VM data - ''' + """ ret = [] - for vm_name, vm_details in six.iteritems(self.opts['profiles']): - ret.append( - {vm_name: self.create(vm_details)} - ) + for vm_name, vm_details in six.iteritems(self.opts["profiles"]): + ret.append({vm_name: self.create(vm_details)}) return ret def destroy(self, names, cached=False): - ''' + """ Destroy the named VMs - ''' + """ processed = {} names = set(names) matching = self.get_running_by_names(names, cached=cached) @@ -961,28 +979,30 @@ class Cloud(object): for name in vms: if name in names: vms_to_destroy.add((alias, driver, name)) - if self.opts['parallel']: - parallel_data.append({ - 'opts': self.opts, - 'name': name, - 'alias': alias, - 'driver': driver, - }) + if self.opts["parallel"]: + parallel_data.append( + { + "opts": self.opts, + "name": name, + "alias": alias, + "driver": driver, + } + ) # destroying in parallel - if self.opts['parallel'] and parallel_data: + if self.opts["parallel"] and parallel_data: # set the pool size based on configuration or default to # the number of machines we're destroying - if 'pool_size' in self.opts: - pool_size = self.opts['pool_size'] + if "pool_size" in self.opts: + pool_size = self.opts["pool_size"] else: pool_size = len(parallel_data) - log.info('Destroying in parallel mode; ' - 'Cloud pool size: %s', pool_size) + log.info("Destroying in parallel mode; " "Cloud pool size: %s", pool_size) # kick off the parallel destroy output_multip = enter_mainloop( - _destroy_multiprocessing, parallel_data, pool_size=pool_size) + _destroy_multiprocessing, parallel_data, pool_size=pool_size + ) # massage the multiprocessing output a bit ret_multip = {} @@ -992,9 +1012,9 @@ class Cloud(object): # build up a data structure similar to what the non-parallel # destroy uses for obj in parallel_data: - alias = obj['alias'] - driver = obj['driver'] - name = obj['name'] + alias = obj["alias"] + driver = obj["driver"] + name = obj["name"] if alias not in processed: processed[alias] = {} if driver not in processed[alias]: @@ -1005,12 +1025,11 @@ class Cloud(object): # not destroying in parallel else: - log.info('Destroying in non-parallel mode.') + log.info("Destroying in non-parallel mode.") for alias, driver, name in vms_to_destroy: - fun = '{0}.destroy'.format(driver) + fun = "{0}.destroy".format(driver) with salt.utils.context.func_globals_inject( - self.clouds[fun], - __active_provider_name__=':'.join([alias, driver]) + self.clouds[fun], __active_provider_name__=":".join([alias, driver]) ): ret = self.clouds[fun](name) if alias not in processed: @@ -1030,39 +1049,42 @@ class Cloud(object): continue vm_ = { - 'name': name, - 'profile': None, - 'provider': ':'.join([alias, driver]), - 'driver': driver + "name": name, + "profile": None, + "provider": ":".join([alias, driver]), + "driver": driver, } minion_dict = salt.config.get_cloud_config_value( - 'minion', vm_, self.opts, default={} + "minion", vm_, self.opts, default={} ) key_file = os.path.join( - self.opts['pki_dir'], 'minions', minion_dict.get('id', name) + self.opts["pki_dir"], "minions", minion_dict.get("id", name) ) - globbed_key_file = glob.glob('{0}.*'.format(key_file)) + globbed_key_file = glob.glob("{0}.*".format(key_file)) if not os.path.isfile(key_file) and not globbed_key_file: # There's no such key file!? It might have been renamed - if isinstance(ret, dict) and 'newname' in ret: - salt.utils.cloud.remove_key( - self.opts['pki_dir'], ret['newname'] - ) + if isinstance(ret, dict) and "newname" in ret: + salt.utils.cloud.remove_key(self.opts["pki_dir"], ret["newname"]) continue if os.path.isfile(key_file) and not globbed_key_file: # Single key entry. Remove it! - salt.utils.cloud.remove_key(self.opts['pki_dir'], os.path.basename(key_file)) + salt.utils.cloud.remove_key( + self.opts["pki_dir"], os.path.basename(key_file) + ) continue # Since we have globbed matches, there are probably some keys for which their minion # configuration has append_domain set. - if not os.path.isfile(key_file) and globbed_key_file and len(globbed_key_file) == 1: + if ( + not os.path.isfile(key_file) + and globbed_key_file + and len(globbed_key_file) == 1 + ): # Single entry, let's remove it! salt.utils.cloud.remove_key( - self.opts['pki_dir'], - os.path.basename(globbed_key_file[0]) + self.opts["pki_dir"], os.path.basename(globbed_key_file[0]) ) continue @@ -1072,67 +1094,51 @@ class Cloud(object): # who's name starts with the machine name we're deleting. # We need to ask one by one!? print( - 'There are several minion keys who\'s name starts ' - 'with \'{0}\'. We need to ask you which one should be ' - 'deleted:'.format( - name - ) + "There are several minion keys who's name starts " + "with '{0}'. We need to ask you which one should be " + "deleted:".format(name) ) while True: for idx, filename in enumerate(globbed_key_file): - print(' {0}: {1}'.format( - idx, os.path.basename(filename) - )) - selection = input( - 'Which minion key should be deleted(number)? ' - ) + print(" {0}: {1}".format(idx, os.path.basename(filename))) + selection = input("Which minion key should be deleted(number)? ") try: selection = int(selection) except ValueError: - print( - '\'{0}\' is not a valid selection.'.format(selection) - ) + print("'{0}' is not a valid selection.".format(selection)) try: - filename = os.path.basename( - globbed_key_file.pop(selection) - ) + filename = os.path.basename(globbed_key_file.pop(selection)) except Exception: # pylint: disable=broad-except continue - delete = input( - 'Delete \'{0}\'? [Y/n]? '.format(filename) - ) - if delete == '' or delete.lower().startswith('y'): - salt.utils.cloud.remove_key( - self.opts['pki_dir'], filename - ) - print('Deleted \'{0}\''.format(filename)) + delete = input("Delete '{0}'? [Y/n]? ".format(filename)) + if delete == "" or delete.lower().startswith("y"): + salt.utils.cloud.remove_key(self.opts["pki_dir"], filename) + print("Deleted '{0}'".format(filename)) break - print('Did not delete \'{0}\''.format(filename)) + print("Did not delete '{0}'".format(filename)) break if names and not processed: # These machines were asked to be destroyed but could not be found raise SaltCloudSystemExit( - 'The following VM\'s were not found: {0}'.format( - ', '.join(names) - ) + "The following VM's were not found: {0}".format(", ".join(names)) ) elif names and processed: - processed['Not Found'] = names + processed["Not Found"] = names elif not processed: - raise SaltCloudSystemExit('No machines were destroyed!') + raise SaltCloudSystemExit("No machines were destroyed!") return processed def reboot(self, names): - ''' + """ Reboot the named VMs - ''' + """ ret = [] pmap = self.map_providers_parallel() acts = {} @@ -1142,119 +1148,101 @@ class Cloud(object): if node in names: acts[prov].append(node) for prov, names_ in six.iteritems(acts): - fun = '{0}.reboot'.format(prov) + fun = "{0}.reboot".format(prov) for name in names_: - ret.append({ - name: self.clouds[fun](name) - }) + ret.append({name: self.clouds[fun](name)}) return ret def create(self, vm_, local_master=True): - ''' + """ Create a single VM - ''' + """ output = {} minion_dict = salt.config.get_cloud_config_value( - 'minion', vm_, self.opts, default={} + "minion", vm_, self.opts, default={} ) - alias, driver = vm_['provider'].split(':') - fun = '{0}.create'.format(driver) + alias, driver = vm_["provider"].split(":") + fun = "{0}.create".format(driver) if fun not in self.clouds: log.error( - 'Creating \'%s\' using \'%s\' as the provider ' - 'cannot complete since \'%s\' is not available', - vm_['name'], vm_['provider'], driver + "Creating '%s' using '%s' as the provider " + "cannot complete since '%s' is not available", + vm_["name"], + vm_["provider"], + driver, ) return - deploy = salt.config.get_cloud_config_value('deploy', vm_, self.opts) - make_master = salt.config.get_cloud_config_value( - 'make_master', - vm_, - self.opts - ) + deploy = salt.config.get_cloud_config_value("deploy", vm_, self.opts) + make_master = salt.config.get_cloud_config_value("make_master", vm_, self.opts) if deploy: - if not make_master and 'master' not in minion_dict: + if not make_master and "master" not in minion_dict: log.warning( - 'There\'s no master defined on the \'%s\' VM settings.', - vm_['name'] + "There's no master defined on the '%s' VM settings.", vm_["name"] ) - if 'pub_key' not in vm_ and 'priv_key' not in vm_: - log.debug('Generating minion keys for \'%s\'', vm_['name']) + if "pub_key" not in vm_ and "priv_key" not in vm_: + log.debug("Generating minion keys for '%s'", vm_["name"]) priv, pub = salt.utils.cloud.gen_keys( - salt.config.get_cloud_config_value( - 'keysize', - vm_, - self.opts - ) + salt.config.get_cloud_config_value("keysize", vm_, self.opts) ) - vm_['pub_key'] = pub - vm_['priv_key'] = priv + vm_["pub_key"] = pub + vm_["priv_key"] = priv else: # Note(pabelanger): We still reference pub_key and priv_key when # deploy is disabled. - vm_['pub_key'] = None - vm_['priv_key'] = None + vm_["pub_key"] = None + vm_["priv_key"] = None - key_id = minion_dict.get('id', vm_['name']) + key_id = minion_dict.get("id", vm_["name"]) - domain = vm_.get('domain') - if vm_.get('use_fqdn') and domain: - minion_dict['append_domain'] = domain + domain = vm_.get("domain") + if vm_.get("use_fqdn") and domain: + minion_dict["append_domain"] = domain - if 'append_domain' in minion_dict: - key_id = '.'.join([key_id, minion_dict['append_domain']]) + if "append_domain" in minion_dict: + key_id = ".".join([key_id, minion_dict["append_domain"]]) - if make_master is True and 'master_pub' not in vm_ and 'master_pem' not in vm_: - log.debug('Generating the master keys for \'%s\'', vm_['name']) + if make_master is True and "master_pub" not in vm_ and "master_pem" not in vm_: + log.debug("Generating the master keys for '%s'", vm_["name"]) master_priv, master_pub = salt.utils.cloud.gen_keys( - salt.config.get_cloud_config_value( - 'keysize', - vm_, - self.opts - ) + salt.config.get_cloud_config_value("keysize", vm_, self.opts) ) - vm_['master_pub'] = master_pub - vm_['master_pem'] = master_priv + vm_["master_pub"] = master_pub + vm_["master_pem"] = master_priv if local_master is True and deploy is True: # Accept the key on the local master - salt.utils.cloud.accept_key( - self.opts['pki_dir'], vm_['pub_key'], key_id - ) + salt.utils.cloud.accept_key(self.opts["pki_dir"], vm_["pub_key"], key_id) - vm_['os'] = salt.config.get_cloud_config_value( - 'script', - vm_, - self.opts - ) + vm_["os"] = salt.config.get_cloud_config_value("script", vm_, self.opts) try: - vm_['inline_script'] = salt.config.get_cloud_config_value( - 'inline_script', - vm_, - self.opts + vm_["inline_script"] = salt.config.get_cloud_config_value( + "inline_script", vm_, self.opts ) except KeyError: pass try: - alias, driver = vm_['provider'].split(':') - func = '{0}.create'.format(driver) + alias, driver = vm_["provider"].split(":") + func = "{0}.create".format(driver) with salt.utils.context.func_globals_inject( - self.clouds[fun], - __active_provider_name__=':'.join([alias, driver]) + self.clouds[fun], __active_provider_name__=":".join([alias, driver]) ): output = self.clouds[func](vm_) - if output is not False and 'sync_after_install' in self.opts: - if self.opts['sync_after_install'] not in ( - 'all', 'modules', 'states', 'grains'): - log.error('Bad option for sync_after_install') + if output is not False and "sync_after_install" in self.opts: + if self.opts["sync_after_install"] not in ( + "all", + "modules", + "states", + "grains", + ): + log.error("Bad option for sync_after_install") return output # A small pause helps the sync work more reliably @@ -1264,52 +1252,51 @@ class Cloud(object): while int(time.time()) < start + 60: # We'll try every seconds, up to a minute mopts_ = salt.config.DEFAULT_MASTER_OPTS - conf_path = '/'.join(self.opts['conf_file'].split('/')[:-1]) + conf_path = "/".join(self.opts["conf_file"].split("/")[:-1]) mopts_.update( - salt.config.master_config( - os.path.join(conf_path, - 'master') - ) + salt.config.master_config(os.path.join(conf_path, "master")) ) client = salt.client.get_local_client(mopts=mopts_) ret = client.cmd( - vm_['name'], - 'saltutil.sync_{0}'.format(self.opts['sync_after_install']), - timeout=self.opts['timeout'] + vm_["name"], + "saltutil.sync_{0}".format(self.opts["sync_after_install"]), + timeout=self.opts["timeout"], ) if ret: log.info( - six.u('Synchronized the following dynamic modules: ' - ' {0}').format(ret) + six.u( + "Synchronized the following dynamic modules: " " {0}" + ).format(ret) ) break except KeyError as exc: log.exception( - 'Failed to create VM %s. Configuration value %s needs ' - 'to be set', vm_['name'], exc + "Failed to create VM %s. Configuration value %s needs " "to be set", + vm_["name"], + exc, ) # If it's a map then we need to respect the 'requires' # so we do it later try: - opt_map = self.opts['map'] + opt_map = self.opts["map"] except KeyError: opt_map = False - if self.opts['parallel'] and self.opts['start_action'] and not opt_map: - log.info('Running %s on %s', self.opts['start_action'], vm_['name']) + if self.opts["parallel"] and self.opts["start_action"] and not opt_map: + log.info("Running %s on %s", self.opts["start_action"], vm_["name"]) client = salt.client.get_local_client(mopts=self.opts) action_out = client.cmd( - vm_['name'], - self.opts['start_action'], - timeout=self.opts['timeout'] * 60 + vm_["name"], + self.opts["start_action"], + timeout=self.opts["timeout"] * 60, ) - output['ret'] = action_out + output["ret"] = action_out return output @staticmethod def vm_config(name, main, provider, profile, overrides): - ''' + """ Create vm config. :param str name: The name of the vm @@ -1317,60 +1304,64 @@ class Cloud(object): :param dict provider: The provider config :param dict profile: The profile config :param dict overrides: The vm's config overrides - ''' + """ vm = main.copy() vm = salt.utils.dictupdate.update(vm, provider) vm = salt.utils.dictupdate.update(vm, profile) vm.update(overrides) - vm['name'] = name + vm["name"] = name return vm def extras(self, extra_): - ''' + """ Extra actions - ''' + """ output = {} - alias, driver = extra_['provider'].split(':') - fun = '{0}.{1}'.format(driver, extra_['action']) + alias, driver = extra_["provider"].split(":") + fun = "{0}.{1}".format(driver, extra_["action"]) if fun not in self.clouds: log.error( - 'Creating \'%s\' using \'%s\' as the provider ' - 'cannot complete since \'%s\' is not available', - extra_['name'], extra_['provider'], driver + "Creating '%s' using '%s' as the provider " + "cannot complete since '%s' is not available", + extra_["name"], + extra_["provider"], + driver, ) return try: with salt.utils.context.func_globals_inject( - self.clouds[fun], - __active_provider_name__=extra_['provider'] + self.clouds[fun], __active_provider_name__=extra_["provider"] ): output = self.clouds[fun](**extra_) except KeyError as exc: log.exception( - 'Failed to perform %s.%s on %s. ' - 'Configuration value %s needs to be set', - extra_['provider'], extra_['action'], extra_['name'], exc + "Failed to perform %s.%s on %s. " + "Configuration value %s needs to be set", + extra_["provider"], + extra_["action"], + extra_["name"], + exc, ) return output def run_profile(self, profile, names, vm_overrides=None): - ''' + """ Parse over the options passed on the command line and determine how to handle them - ''' - if profile not in self.opts['profiles']: - msg = 'Profile {0} is not defined'.format(profile) + """ + if profile not in self.opts["profiles"]: + msg = "Profile {0} is not defined".format(profile) log.error(msg) - return {'Error': msg} + return {"Error": msg} ret = {} if not vm_overrides: vm_overrides = {} try: - with salt.utils.files.fopen(self.opts['conf_file'], 'r') as mcc: + with salt.utils.files.fopen(self.opts["conf_file"], "r") as mcc: main_cloud_config = salt.utils.yaml.safe_load(mcc) if not main_cloud_config: main_cloud_config = {} @@ -1383,28 +1374,26 @@ class Cloud(object): main_cloud_config = {} mapped_providers = self.map_providers_parallel() - profile_details = self.opts['profiles'][profile] + profile_details = self.opts["profiles"][profile] vms = {} for prov, val in six.iteritems(mapped_providers): prov_name = next(iter(val)) for node in mapped_providers[prov][prov_name]: vms[node] = mapped_providers[prov][prov_name][node] - vms[node]['provider'] = prov - vms[node]['driver'] = prov_name - alias, driver = profile_details['provider'].split(':') + vms[node]["provider"] = prov + vms[node]["driver"] = prov_name + alias, driver = profile_details["provider"].split(":") - provider_details = self.opts['providers'][alias][driver].copy() - del provider_details['profiles'] + provider_details = self.opts["providers"][alias][driver].copy() + del provider_details["profiles"] for name in names: if name in vms: - prov = vms[name]['provider'] - driv = vms[name]['driver'] - msg = '{0} already exists under {1}:{2}'.format( - name, prov, driv - ) + prov = vms[name]["provider"] + driv = vms[name]["driver"] + msg = "{0} already exists under {1}:{2}".format(name, prov, driv) log.error(msg) - ret[name] = {'Error': msg} + ret[name] = {"Error": msg} continue vm_ = self.vm_config( @@ -1414,15 +1403,12 @@ class Cloud(object): profile_details, vm_overrides, ) - if self.opts['parallel']: - process = multiprocessing.Process( - target=self.create, - args=(vm_,) - ) + if self.opts["parallel"]: + process = multiprocessing.Process(target=self.create, args=(vm_,)) process.start() ret[name] = { - 'Provisioning': 'VM being provisioned in parallel. ' - 'PID: {0}'.format(process.pid) + "Provisioning": "VM being provisioned in parallel. " + "PID: {0}".format(process.pid) } continue @@ -1431,23 +1417,23 @@ class Cloud(object): # here because self.create takes care of that ret[name] = self.create(vm_) if not ret[name]: - ret[name] = {'Error': 'Failed to deploy VM'} + ret[name] = {"Error": "Failed to deploy VM"} if len(names) == 1: - raise SaltCloudSystemExit('Failed to deploy VM') + raise SaltCloudSystemExit("Failed to deploy VM") continue - if self.opts.get('show_deploy_args', False) is False: - ret[name].pop('deploy_kwargs', None) + if self.opts.get("show_deploy_args", False) is False: + ret[name].pop("deploy_kwargs", None) except (SaltCloudSystemExit, SaltCloudConfigError) as exc: if len(names) == 1: raise - ret[name] = {'Error': str(exc)} + ret[name] = {"Error": str(exc)} return ret def do_action(self, names, kwargs): - ''' + """ Perform an action on a VM which may be specific to this cloud provider - ''' + """ ret = {} invalid_functions = {} names = set(names) @@ -1459,9 +1445,9 @@ class Cloud(object): if not names: break valid_function = True - fun = '{0}.{1}'.format(driver, self.opts['action']) + fun = "{0}.{1}".format(driver, self.opts["action"]) if fun not in self.clouds: - log.info('\'%s()\' is not available. Not actioning...', fun) + log.info("'%s()' is not available. Not actioning...", fun) valid_function = False for vm_name, vm_details in six.iteritems(vms): if not names: @@ -1469,12 +1455,14 @@ class Cloud(object): if vm_name not in names: if not isinstance(vm_details, dict): vm_details = {} - if 'id' in vm_details and vm_details['id'] in names: - vm_name = vm_details['id'] + if "id" in vm_details and vm_details["id"] in names: + vm_name = vm_details["id"] else: log.debug( - 'vm:%s in provider:%s is not in name ' - 'list:\'%s\'', vm_name, driver, names + "vm:%s in provider:%s is not in name " "list:'%s'", + vm_name, + driver, + names, ) continue @@ -1487,7 +1475,7 @@ class Cloud(object): with salt.utils.context.func_globals_inject( self.clouds[fun], - __active_provider_name__=':'.join([alias, driver]) + __active_provider_name__=":".join([alias, driver]), ): if alias not in ret: ret[alias] = {} @@ -1501,18 +1489,18 @@ class Cloud(object): if kwargs: ret[alias][driver][vm_name] = self.clouds[fun]( - vm_name, kwargs, call='action' + vm_name, kwargs, call="action" ) else: ret[alias][driver][vm_name] = self.clouds[fun]( - vm_name, call='action' + vm_name, call="action" ) names.remove(vm_name) # Set the return information for the VMs listed in the invalid_functions dict. missing_vms = set() if invalid_functions: - ret['Invalid Actions'] = invalid_functions + ret["Invalid Actions"] = invalid_functions invalid_func_vms = set() for key, val in six.iteritems(invalid_functions): invalid_func_vms = invalid_func_vms.union(set(val)) @@ -1520,8 +1508,8 @@ class Cloud(object): # Find the VMs that are in names, but not in set of invalid functions. missing_vms = names.difference(invalid_func_vms) if missing_vms: - ret['Not Found'] = list(missing_vms) - ret['Not Actioned/Not Running'] = list(names) + ret["Not Found"] = list(missing_vms) + ret["Not Actioned/Not Running"] = list(names) if not names: return ret @@ -1534,132 +1522,123 @@ class Cloud(object): # If we reach this point, the Not Actioned and Not Found lists will be the same, # But we want to list both for clarity/consistency with the invalid functions lists. - ret['Not Actioned/Not Running'] = list(names) - ret['Not Found'] = list(names) + ret["Not Actioned/Not Running"] = list(names) + ret["Not Found"] = list(names) return ret def do_function(self, prov, func, kwargs): - ''' + """ Perform a function against a cloud provider - ''' + """ matches = self.lookup_providers(prov) if len(matches) > 1: raise SaltCloudSystemExit( - 'More than one results matched \'{0}\'. Please specify ' - 'one of: {1}'.format( + "More than one results matched '{0}'. Please specify " + "one of: {1}".format( prov, - ', '.join([ - '{0}:{1}'.format(alias, driver) for - (alias, driver) in matches - ]) + ", ".join( + ["{0}:{1}".format(alias, driver) for (alias, driver) in matches] + ), ) ) alias, driver = matches.pop() - fun = '{0}.{1}'.format(driver, func) + fun = "{0}.{1}".format(driver, func) if fun not in self.clouds: raise SaltCloudSystemExit( - 'The \'{0}\' cloud provider alias, for the \'{1}\' driver, does ' - 'not define the function \'{2}\''.format(alias, driver, func) + "The '{0}' cloud provider alias, for the '{1}' driver, does " + "not define the function '{2}'".format(alias, driver, func) ) - log.debug( - 'Trying to execute \'%s\' with the following kwargs: %s', - fun, kwargs - ) + log.debug("Trying to execute '%s' with the following kwargs: %s", fun, kwargs) with salt.utils.context.func_globals_inject( - self.clouds[fun], - __active_provider_name__=':'.join([alias, driver]) + self.clouds[fun], __active_provider_name__=":".join([alias, driver]) ): if kwargs: return { - alias: { - driver: self.clouds[fun]( - call='function', kwargs=kwargs - ) - } + alias: {driver: self.clouds[fun](call="function", kwargs=kwargs)} } - return { - alias: { - driver: self.clouds[fun](call='function') - } - } + return {alias: {driver: self.clouds[fun](call="function")}} def __filter_non_working_providers(self): - ''' + """ Remove any mis-configured cloud providers from the available listing - ''' - for alias, drivers in six.iteritems(self.opts['providers'].copy()): + """ + for alias, drivers in six.iteritems(self.opts["providers"].copy()): for driver in drivers.copy(): - fun = '{0}.get_configured_provider'.format(driver) + fun = "{0}.get_configured_provider".format(driver) if fun not in self.clouds: # Mis-configured provider that got removed? log.warning( - 'The cloud driver, \'%s\', configured under the ' - '\'%s\' cloud provider alias, could not be loaded. ' - 'Please check your provider configuration files and ' - 'ensure all required dependencies are installed ' - 'for the \'%s\' driver.\n' - 'In rare cases, this could indicate the \'%s()\' ' - 'function could not be found.\nRemoving \'%s\' from ' - 'the available providers list', - driver, alias, driver, fun, driver + "The cloud driver, '%s', configured under the " + "'%s' cloud provider alias, could not be loaded. " + "Please check your provider configuration files and " + "ensure all required dependencies are installed " + "for the '%s' driver.\n" + "In rare cases, this could indicate the '%s()' " + "function could not be found.\nRemoving '%s' from " + "the available providers list", + driver, + alias, + driver, + fun, + driver, ) - self.opts['providers'][alias].pop(driver) + self.opts["providers"][alias].pop(driver) - if alias not in self.opts['providers']: + if alias not in self.opts["providers"]: continue - if not self.opts['providers'][alias]: - self.opts['providers'].pop(alias) + if not self.opts["providers"][alias]: + self.opts["providers"].pop(alias) continue with salt.utils.context.func_globals_inject( - self.clouds[fun], - __active_provider_name__=':'.join([alias, driver]) + self.clouds[fun], __active_provider_name__=":".join([alias, driver]) ): if self.clouds[fun]() is False: log.warning( - 'The cloud driver, \'%s\', configured under the ' - '\'%s\' cloud provider alias is not properly ' - 'configured. Removing it from the available ' - 'providers list.', driver, alias + "The cloud driver, '%s', configured under the " + "'%s' cloud provider alias is not properly " + "configured. Removing it from the available " + "providers list.", + driver, + alias, ) - self.opts['providers'][alias].pop(driver) + self.opts["providers"][alias].pop(driver) - if alias not in self.opts['providers']: + if alias not in self.opts["providers"]: continue - if not self.opts['providers'][alias]: - self.opts['providers'].pop(alias) + if not self.opts["providers"][alias]: + self.opts["providers"].pop(alias) class Map(Cloud): - ''' + """ Create a VM stateful map execution object - ''' + """ + def __init__(self, opts): Cloud.__init__(self, opts) self.rendered_map = self.read() - def interpolated_map(self, query='list_nodes', cached=False): + def interpolated_map(self, query="list_nodes", cached=False): rendered_map = self.read().copy() interpolated_map = {} for profile, mapped_vms in six.iteritems(rendered_map): names = set(mapped_vms) - if profile not in self.opts['profiles']: - if 'Errors' not in interpolated_map: - interpolated_map['Errors'] = {} + if profile not in self.opts["profiles"]: + if "Errors" not in interpolated_map: + interpolated_map["Errors"] = {} msg = ( - 'No provider for the mapped \'{0}\' profile was found. ' - 'Skipped VMS: {1}'.format( - profile, ', '.join(names) - ) + "No provider for the mapped '{0}' profile was found. " + "Skipped VMS: {1}".format(profile, ", ".join(names)) ) log.info(msg) - interpolated_map['Errors'][profile] = msg + interpolated_map["Errors"][profile] = msg continue matching = self.get_running_by_names(names, query, cached) @@ -1680,14 +1659,14 @@ class Map(Cloud): if not names: continue - profile_details = self.opts['profiles'][profile] - alias, driver = profile_details['provider'].split(':') + profile_details = self.opts["profiles"][profile] + alias, driver = profile_details["provider"].split(":") for vm_name in names: if alias not in interpolated_map: interpolated_map[alias] = {} if driver not in interpolated_map[alias]: interpolated_map[alias][driver] = {} - interpolated_map[alias][driver][vm_name] = 'Absent' + interpolated_map[alias][driver][vm_name] = "Absent" return interpolated_map @@ -1696,7 +1675,7 @@ class Map(Cloud): for alias, drivers in six.iteritems(query_map.copy()): for driver, vms in six.iteritems(drivers.copy()): for vm_name, vm_details in six.iteritems(vms.copy()): - if vm_details == 'Absent': + if vm_details == "Absent": query_map[alias][driver].pop(vm_name) if not query_map[alias][driver]: query_map[alias].pop(driver) @@ -1707,9 +1686,9 @@ class Map(Cloud): def get_vmnames_by_action(self, action): query_map = self.interpolated_map("list_nodes") matching_states = { - 'start': ['stopped'], - 'stop': ['running', 'active'], - 'reboot': ['running', 'active'], + "start": ["stopped"], + "stop": ["running", "active"], + "reboot": ["running", "active"], } vm_names = [] for alias, drivers in six.iteritems(query_map): @@ -1722,73 +1701,75 @@ class Map(Cloud): state_action = matching_states[action] except KeyError: log.error( - 'The use of \'%s\' as an action is not supported ' - 'in this context. Only \'start\', \'stop\', and ' - '\'reboot\' are supported options.', action + "The use of '%s' as an action is not supported " + "in this context. Only 'start', 'stop', and " + "'reboot' are supported options.", + action, ) raise SaltCloudException() - if vm_details != 'Absent' and vm_details['state'].lower() in state_action: + if ( + vm_details != "Absent" + and vm_details["state"].lower() in state_action + ): vm_names.append(vm_name) return vm_names def read(self): - ''' + """ Read in the specified map and return the map structure - ''' + """ map_ = None - if self.opts.get('map', None) is None: - if self.opts.get('map_data', None) is None: - if self.opts.get('map_pillar', None) is None: + if self.opts.get("map", None) is None: + if self.opts.get("map_data", None) is None: + if self.opts.get("map_pillar", None) is None: pass - elif self.opts.get('map_pillar') not in self.opts.get('maps'): + elif self.opts.get("map_pillar") not in self.opts.get("maps"): log.error( - 'The specified map not found in pillar at ' - '\'cloud:maps:%s\'', self.opts['map_pillar'] + "The specified map not found in pillar at " "'cloud:maps:%s'", + self.opts["map_pillar"], ) raise SaltCloudNotFound() else: # 'map_pillar' is provided, try to use it - map_ = self.opts['maps'][self.opts.get('map_pillar')] + map_ = self.opts["maps"][self.opts.get("map_pillar")] else: # 'map_data' is provided, try to use it - map_ = self.opts['map_data'] + map_ = self.opts["map_data"] else: # 'map' is provided, try to use it local_minion_opts = copy.deepcopy(self.opts) - local_minion_opts['file_client'] = 'local' + local_minion_opts["file_client"] = "local" self.minion = salt.minion.MasterMinion(local_minion_opts) - if not os.path.isfile(self.opts['map']): - if not (self.opts['map']).startswith('salt://'): + if not os.path.isfile(self.opts["map"]): + if not (self.opts["map"]).startswith("salt://"): log.error( - 'The specified map file does not exist: \'%s\'', - self.opts['map'] + "The specified map file does not exist: '%s'", self.opts["map"] ) raise SaltCloudNotFound() - if (self.opts['map']).startswith('salt://'): - cached_map = self.minion.functions['cp.cache_file'](self.opts['map']) + if (self.opts["map"]).startswith("salt://"): + cached_map = self.minion.functions["cp.cache_file"](self.opts["map"]) else: - cached_map = self.opts['map'] + cached_map = self.opts["map"] try: - renderer = self.opts.get('renderer', 'jinja|yaml') + renderer = self.opts.get("renderer", "jinja|yaml") rend = salt.loader.render(self.opts, {}) - blacklist = self.opts.get('renderer_blacklist') - whitelist = self.opts.get('renderer_whitelist') + blacklist = self.opts.get("renderer_blacklist") + whitelist = self.opts.get("renderer_whitelist") map_ = compile_template( cached_map, rend, renderer, blacklist, whitelist ) except Exception as exc: # pylint: disable=broad-except log.error( - 'Rendering map %s failed, render error:\n%s', - self.opts['map'], exc, - exc_info_on_loglevel=logging.DEBUG + "Rendering map %s failed, render error:\n%s", + self.opts["map"], + exc, + exc_info_on_loglevel=logging.DEBUG, ) return {} - if 'include' in map_: - map_ = salt.config.include_config( - map_, self.opts['map'], verbose=False - ) + if "include" in map_: + map_ = salt.config.include_config(map_, self.opts["map"], verbose=False) if not map_: return {} @@ -1810,12 +1791,12 @@ class Map(Cloud): # - bar2: overrides = {} try: - overrides.setdefault('name', name) + overrides.setdefault("name", name) except AttributeError: log.error( - 'Cannot use \'name\' as a minion id in a cloud map as it ' - 'is a reserved word. Please change \'name\' to a different ' - 'minion id reference.' + "Cannot use 'name' as a minion id in a cloud map as it " + "is a reserved word. Please change 'name' to a different " + "minion id reference." ) return {} entries[name] = overrides @@ -1833,7 +1814,7 @@ class Map(Cloud): # foo: bar entries = {} for name, overrides in six.iteritems(mapped): - overrides.setdefault('name', name) + overrides.setdefault("name", name) entries[name] = overrides map_[profile] = entries continue @@ -1845,15 +1826,15 @@ class Map(Cloud): map_[profile] = {} for name in mapped: - map_[profile][name] = {'name': name} + map_[profile][name] = {"name": name} return map_ def _has_loop(self, dmap, seen=None, val=None): if seen is None: - for values in six.itervalues(dmap['create']): + for values in six.itervalues(dmap["create"]): seen = [] try: - machines = values['requires'] + machines = values["requires"] except KeyError: machines = [] for machine in machines: @@ -1865,7 +1846,7 @@ class Map(Cloud): seen.append(val) try: - machines = dmap['create'][val]['requires'] + machines = dmap["create"][val]["requires"] except KeyError: machines = [] @@ -1876,18 +1857,18 @@ class Map(Cloud): def _calcdep(self, dmap, machine, data, level): try: - deplist = data['requires'] + deplist = data["requires"] except KeyError: return level levels = [] for name in deplist: try: - data = dmap['create'][name] + data = dmap["create"][name] except KeyError: try: - data = dmap['existing'][name] + data = dmap["existing"][name] except KeyError: - msg = 'Missing dependency in cloud map' + msg = "Missing dependency in cloud map" log.error(msg) raise SaltCloudException(msg) levels.append(self._calcdep(dmap, name, data, level)) @@ -1895,42 +1876,44 @@ class Map(Cloud): return level def map_data(self, cached=False): - ''' + """ Create a data map of what to execute on - ''' - ret = {'create': {}} + """ + ret = {"create": {}} pmap = self.map_providers_parallel(cached=cached) exist = set() defined = set() rendered_map = copy.deepcopy(self.rendered_map) for profile_name, nodes in six.iteritems(rendered_map): - if profile_name not in self.opts['profiles']: + if profile_name not in self.opts["profiles"]: msg = ( - 'The required profile, \'{0}\', defined in the map ' - 'does not exist. The defined nodes, {1}, will not ' - 'be created.'.format( - profile_name, - ', '.join('\'{0}\''.format(node) for node in nodes) + "The required profile, '{0}', defined in the map " + "does not exist. The defined nodes, {1}, will not " + "be created.".format( + profile_name, ", ".join("'{0}'".format(node) for node in nodes) ) ) log.error(msg) - if 'errors' not in ret: - ret['errors'] = {} - ret['errors'][profile_name] = msg + if "errors" not in ret: + ret["errors"] = {} + ret["errors"][profile_name] = msg continue - profile_data = self.opts['profiles'].get(profile_name) + profile_data = self.opts["profiles"].get(profile_name) for nodename, overrides in six.iteritems(nodes): # Get associated provider data, in case something like size # or image is specified in the provider file. See issue #32510. - if 'provider' in overrides and overrides['provider'] != profile_data['provider']: - alias, driver = overrides.get('provider').split(':') + if ( + "provider" in overrides + and overrides["provider"] != profile_data["provider"] + ): + alias, driver = overrides.get("provider").split(":") else: - alias, driver = profile_data.get('provider').split(':') + alias, driver = profile_data.get("provider").split(":") - provider_details = copy.deepcopy(self.opts['providers'][alias][driver]) - del provider_details['profiles'] + provider_details = copy.deepcopy(self.opts["providers"][alias][driver]) + del provider_details["profiles"] # Update the provider details information with profile data # Profile data and node overrides should override provider data, if defined. @@ -1939,38 +1922,39 @@ class Map(Cloud): nodedata = copy.deepcopy(provider_details) # Update profile data with the map overrides - for setting in ('grains', 'master', 'minion', 'volumes', - 'requires'): - deprecated = 'map_{0}'.format(setting) + for setting in ("grains", "master", "minion", "volumes", "requires"): + deprecated = "map_{0}".format(setting) if deprecated in overrides: log.warning( - 'The use of \'%s\' on the \'%s\' mapping has ' - 'been deprecated. The preferred way now is to ' - 'just define \'%s\'. For now, salt-cloud will do ' - 'the proper thing and convert the deprecated ' - 'mapping into the preferred one.', - deprecated, nodename, setting + "The use of '%s' on the '%s' mapping has " + "been deprecated. The preferred way now is to " + "just define '%s'. For now, salt-cloud will do " + "the proper thing and convert the deprecated " + "mapping into the preferred one.", + deprecated, + nodename, + setting, ) overrides[setting] = overrides.pop(deprecated) # merge minion grains from map file - if 'minion' in overrides and \ - 'minion' in nodedata and \ - 'grains' in overrides['minion'] and \ - 'grains' in nodedata['minion']: - nodedata['minion']['grains'].update( - overrides['minion']['grains'] - ) - del overrides['minion']['grains'] + if ( + "minion" in overrides + and "minion" in nodedata + and "grains" in overrides["minion"] + and "grains" in nodedata["minion"] + ): + nodedata["minion"]["grains"].update(overrides["minion"]["grains"]) + del overrides["minion"]["grains"] # remove minion key if now is empty dict - if not overrides['minion']: - del overrides['minion'] + if not overrides["minion"]: + del overrides["minion"] nodedata = salt.utils.dictupdate.update(nodedata, overrides) # Add the computed information to the return data - ret['create'][nodename] = nodedata + ret["create"][nodename] = nodedata # Add the node name to the defined set - alias, driver = nodedata['provider'].split(':') + alias, driver = nodedata["provider"].split(":") defined.add((alias, driver, nodename)) def get_matching_by_name(name): @@ -1979,14 +1963,14 @@ class Map(Cloud): for driver, vms in six.iteritems(drivers): for vm_name, details in six.iteritems(vms): if vm_name == name and driver not in matches: - matches[driver] = details['state'] + matches[driver] = details["state"] return matches for alias, drivers in six.iteritems(pmap): for driver, vms in six.iteritems(drivers): for name, details in six.iteritems(vms): exist.add((alias, driver, name)) - if name not in ret['create']: + if name not in ret["create"]: continue # The machine is set to be created. Does it already exist? @@ -1996,140 +1980,140 @@ class Map(Cloud): # A machine by the same name exists for item in matching: - if name not in ret['create']: + if name not in ret["create"]: # Machine already removed break - log.warning("'%s' already exists, removing from " - 'the create map.', name) + log.warning( + "'%s' already exists, removing from " "the create map.", + name, + ) - if 'existing' not in ret: - ret['existing'] = {} - ret['existing'][name] = ret['create'].pop(name) + if "existing" not in ret: + ret["existing"] = {} + ret["existing"][name] = ret["create"].pop(name) - if 'hard' in self.opts and self.opts['hard']: - if self.opts['enable_hard_maps'] is False: + if "hard" in self.opts and self.opts["hard"]: + if self.opts["enable_hard_maps"] is False: raise SaltCloudSystemExit( - 'The --hard map can be extremely dangerous to use, ' - 'and therefore must explicitly be enabled in the main ' - 'configuration file, by setting \'enable_hard_maps\' ' - 'to True' + "The --hard map can be extremely dangerous to use, " + "and therefore must explicitly be enabled in the main " + "configuration file, by setting 'enable_hard_maps' " + "to True" ) # Hard maps are enabled, Look for the items to delete. - ret['destroy'] = exist.difference(defined) + ret["destroy"] = exist.difference(defined) return ret def run_map(self, dmap): - ''' + """ Execute the contents of the VM map - ''' + """ if self._has_loop(dmap): - msg = 'Uh-oh, that cloud map has a dependency loop!' + msg = "Uh-oh, that cloud map has a dependency loop!" log.error(msg) raise SaltCloudException(msg) # Go through the create list and calc dependencies - for key, val in six.iteritems(dmap['create']): - log.info('Calculating dependencies for %s', key) + for key, val in six.iteritems(dmap["create"]): + log.info("Calculating dependencies for %s", key) level = 0 level = self._calcdep(dmap, key, val, level) - log.debug('Got execution order %s for %s', level, key) - dmap['create'][key]['level'] = level + log.debug("Got execution order %s for %s", level, key) + dmap["create"][key]["level"] = level try: - existing_list = six.iteritems(dmap['existing']) + existing_list = six.iteritems(dmap["existing"]) except KeyError: existing_list = six.iteritems({}) for key, val in existing_list: - log.info('Calculating dependencies for %s', key) + log.info("Calculating dependencies for %s", key) level = 0 level = self._calcdep(dmap, key, val, level) - log.debug('Got execution order %s for %s', level, key) - dmap['existing'][key]['level'] = level + log.debug("Got execution order %s for %s", level, key) + dmap["existing"][key]["level"] = level # Now sort the create list based on dependencies - create_list = sorted(six.iteritems(dmap['create']), key=lambda x: x[1]['level']) + create_list = sorted(six.iteritems(dmap["create"]), key=lambda x: x[1]["level"]) output = {} - if self.opts['parallel']: + if self.opts["parallel"]: parallel_data = [] master_name = None master_minion_name = None master_host = None master_finger = None try: - master_name, master_profile = next(( - (name, profile) for name, profile in create_list - if profile.get('make_master', False) is True - )) + master_name, master_profile = next( + ( + (name, profile) + for name, profile in create_list + if profile.get("make_master", False) is True + ) + ) master_minion_name = master_name - log.debug('Creating new master \'%s\'', master_name) - if salt.config.get_cloud_config_value( - 'deploy', - master_profile, - self.opts - ) is False: + log.debug("Creating new master '%s'", master_name) + if ( + salt.config.get_cloud_config_value("deploy", master_profile, self.opts) + is False + ): raise SaltCloudSystemExit( - 'Cannot proceed with \'make_master\' when salt deployment ' - 'is disabled(ex: --no-deploy).' + "Cannot proceed with 'make_master' when salt deployment " + "is disabled(ex: --no-deploy)." ) # Generate the master keys - log.debug('Generating master keys for \'%s\'', master_profile['name']) + log.debug("Generating master keys for '%s'", master_profile["name"]) priv, pub = salt.utils.cloud.gen_keys( - salt.config.get_cloud_config_value( - 'keysize', - master_profile, - self.opts - ) + salt.config.get_cloud_config_value("keysize", master_profile, self.opts) ) - master_profile['master_pub'] = pub - master_profile['master_pem'] = priv + master_profile["master_pub"] = pub + master_profile["master_pem"] = priv # Generate the fingerprint of the master pubkey in order to # mitigate man-in-the-middle attacks master_temp_pub = salt.utils.files.mkstemp() - with salt.utils.files.fopen(master_temp_pub, 'w') as mtp: + with salt.utils.files.fopen(master_temp_pub, "w") as mtp: mtp.write(pub) - master_finger = salt.utils.crypt.pem_finger(master_temp_pub, sum_type=self.opts['hash_type']) + master_finger = salt.utils.crypt.pem_finger( + master_temp_pub, sum_type=self.opts["hash_type"] + ) os.unlink(master_temp_pub) - if master_profile.get('make_minion', True) is True: - master_profile.setdefault('minion', {}) - if 'id' in master_profile['minion']: - master_minion_name = master_profile['minion']['id'] + if master_profile.get("make_minion", True) is True: + master_profile.setdefault("minion", {}) + if "id" in master_profile["minion"]: + master_minion_name = master_profile["minion"]["id"] # Set this minion's master as local if the user has not set it - if 'master' not in master_profile['minion']: - master_profile['minion']['master'] = '127.0.0.1' + if "master" not in master_profile["minion"]: + master_profile["minion"]["master"] = "127.0.0.1" if master_finger is not None: - master_profile['master_finger'] = master_finger + master_profile["master_finger"] = master_finger # Generate the minion keys to pre-seed the master: for name, profile in create_list: make_minion = salt.config.get_cloud_config_value( - 'make_minion', profile, self.opts, default=True + "make_minion", profile, self.opts, default=True ) if make_minion is False: continue - log.debug('Generating minion keys for \'%s\'', profile['name']) + log.debug("Generating minion keys for '%s'", profile["name"]) priv, pub = salt.utils.cloud.gen_keys( - salt.config.get_cloud_config_value( - 'keysize', - profile, - self.opts - ) + salt.config.get_cloud_config_value("keysize", profile, self.opts) ) - profile['pub_key'] = pub - profile['priv_key'] = priv + profile["pub_key"] = pub + profile["priv_key"] = priv # Store the minion's public key in order to be pre-seeded in # the master - master_profile.setdefault('preseed_minion_keys', {}) - master_profile['preseed_minion_keys'].update({name: pub}) + master_profile.setdefault("preseed_minion_keys", {}) + master_profile["preseed_minion_keys"].update({name: pub}) local_master = False - if master_profile['minion'].get('local_master', False) and \ - master_profile['minion'].get('master', None) is not None: + if ( + master_profile["minion"].get("local_master", False) + and master_profile["minion"].get("master", None) is not None + ): # The minion is explicitly defining a master and it's # explicitly saying it's the local one local_master = True @@ -2138,53 +2122,55 @@ class Map(Cloud): if not isinstance(out, dict): log.debug( - 'Master creation details is not a dictionary: {0}'.format( - out - ) + "Master creation details is not a dictionary: {0}".format(out) ) - elif 'Errors' in out: + elif "Errors" in out: raise SaltCloudSystemExit( - 'An error occurred while creating the master, not ' - 'continuing: {0}'.format(out['Errors']) + "An error occurred while creating the master, not " + "continuing: {0}".format(out["Errors"]) ) deploy_kwargs = ( - self.opts.get('show_deploy_args', False) is True and + self.opts.get("show_deploy_args", False) is True + and # Get the needed data - out.get('deploy_kwargs', {}) or + out.get("deploy_kwargs", {}) + or # Strip the deploy_kwargs from the returned data since we don't # want it shown in the console. - out.pop('deploy_kwargs', {}) + out.pop("deploy_kwargs", {}) ) - master_host = deploy_kwargs.get('salt_host', deploy_kwargs.get('host', None)) + master_host = deploy_kwargs.get( + "salt_host", deploy_kwargs.get("host", None) + ) if master_host is None: raise SaltCloudSystemExit( - 'Host for new master {0} was not found, ' - 'aborting map'.format( - master_name - ) + "Host for new master {0} was not found, " + "aborting map".format(master_name) ) output[master_name] = out except StopIteration: - log.debug('No make_master found in map') + log.debug("No make_master found in map") # Local master? # Generate the fingerprint of the master pubkey in order to # mitigate man-in-the-middle attacks - master_pub = os.path.join(self.opts['pki_dir'], 'master.pub') + master_pub = os.path.join(self.opts["pki_dir"], "master.pub") if os.path.isfile(master_pub): - master_finger = salt.utils.crypt.pem_finger(master_pub, sum_type=self.opts['hash_type']) + master_finger = salt.utils.crypt.pem_finger( + master_pub, sum_type=self.opts["hash_type"] + ) opts = self.opts.copy() - if self.opts['parallel']: + if self.opts["parallel"]: # Force display_ssh_output to be False since the console will # need to be reset afterwards log.info( - 'Since parallel deployment is in use, ssh console output ' - 'is disabled. All ssh output will be logged though' + "Since parallel deployment is in use, ssh console output " + "is disabled. All ssh output will be logged though" ) - opts['display_ssh_output'] = False + opts["display_ssh_output"] = False local_master = master_name is None @@ -2193,76 +2179,92 @@ class Map(Cloud): # Already deployed, it's the master's minion continue - if 'minion' in profile and profile['minion'].get('local_master', False) and \ - profile['minion'].get('master', None) is not None: + if ( + "minion" in profile + and profile["minion"].get("local_master", False) + and profile["minion"].get("master", None) is not None + ): # The minion is explicitly defining a master and it's # explicitly saying it's the local one local_master = True if master_finger is not None and local_master is False: - profile['master_finger'] = master_finger + profile["master_finger"] = master_finger if master_host is not None: - profile.setdefault('minion', {}) - profile['minion'].setdefault('master', master_host) + profile.setdefault("minion", {}) + profile["minion"].setdefault("master", master_host) - if self.opts['parallel']: - parallel_data.append({ - 'opts': opts, - 'name': name, - 'profile': profile, - 'local_master': local_master - }) + if self.opts["parallel"]: + parallel_data.append( + { + "opts": opts, + "name": name, + "profile": profile, + "local_master": local_master, + } + ) continue # Not deploying in parallel try: - output[name] = self.create( - profile, local_master=local_master - ) - if self.opts.get('show_deploy_args', False) is False \ - and 'deploy_kwargs' in output \ - and isinstance(output[name], dict): - output[name].pop('deploy_kwargs', None) + output[name] = self.create(profile, local_master=local_master) + if ( + self.opts.get("show_deploy_args", False) is False + and "deploy_kwargs" in output + and isinstance(output[name], dict) + ): + output[name].pop("deploy_kwargs", None) except SaltCloudException as exc: log.error( - 'Failed to deploy \'%s\'. Error: %s', - name, exc, exc_info_on_loglevel=logging.DEBUG + "Failed to deploy '%s'. Error: %s", + name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) - output[name] = {'Error': str(exc)} + output[name] = {"Error": str(exc)} - for name in dmap.get('destroy', ()): + for name in dmap.get("destroy", ()): output[name] = self.destroy(name) - if self.opts['parallel'] and parallel_data: - if 'pool_size' in self.opts: - pool_size = self.opts['pool_size'] + if self.opts["parallel"] and parallel_data: + if "pool_size" in self.opts: + pool_size = self.opts["pool_size"] else: pool_size = len(parallel_data) - log.info('Cloud pool size: %s', pool_size) + log.info("Cloud pool size: %s", pool_size) output_multip = enter_mainloop( - _create_multiprocessing, parallel_data, pool_size=pool_size) + _create_multiprocessing, parallel_data, pool_size=pool_size + ) # We have deployed in parallel, now do start action in # correct order based on dependencies. - if self.opts['start_action']: + if self.opts["start_action"]: actionlist = [] grp = -1 - for key, val in groupby(six.itervalues(dmap['create']), lambda x: x['level']): + for key, val in groupby( + six.itervalues(dmap["create"]), lambda x: x["level"] + ): actionlist.append([]) grp += 1 for item in val: - actionlist[grp].append(item['name']) + actionlist[grp].append(item["name"]) out = {} for group in actionlist: - log.info('Running %s on %s', self.opts['start_action'], ', '.join(group)) + log.info( + "Running %s on %s", self.opts["start_action"], ", ".join(group) + ) client = salt.client.get_local_client() - out.update(client.cmd( - ','.join(group), self.opts['start_action'], - timeout=self.opts['timeout'] * 60, tgt_type='list' - )) + out.update( + client.cmd( + ",".join(group), + self.opts["start_action"], + timeout=self.opts["timeout"] * 60, + tgt_type="list", + ) + ) for obj in output_multip: - next(six.itervalues(obj))['ret'] = out[next(six.iterkeys(obj))] + next(six.itervalues(obj))["ret"] = out[next(six.iterkeys(obj))] output.update(obj) else: for obj in output_multip: @@ -2272,105 +2274,102 @@ class Map(Cloud): def init_pool_worker(): - ''' + """ Make every worker ignore KeyboarInterrup's since it will be handled by the parent process. - ''' + """ signal.signal(signal.SIGINT, signal.SIG_IGN) def create_multiprocessing(parallel_data, queue=None): - ''' + """ This function will be called from another process when running a map in parallel mode. The result from the create is always a json object. - ''' + """ salt.utils.crypt.reinit_crypto() - parallel_data['opts']['output'] = 'json' - cloud = Cloud(parallel_data['opts']) + parallel_data["opts"]["output"] = "json" + cloud = Cloud(parallel_data["opts"]) try: output = cloud.create( - parallel_data['profile'], - local_master=parallel_data['local_master'] + parallel_data["profile"], local_master=parallel_data["local_master"] ) except SaltCloudException as exc: log.error( - 'Failed to deploy \'%s\'. Error: %s', - parallel_data['name'], exc, exc_info_on_loglevel=logging.DEBUG + "Failed to deploy '%s'. Error: %s", + parallel_data["name"], + exc, + exc_info_on_loglevel=logging.DEBUG, ) - return {parallel_data['name']: {'Error': str(exc)}} + return {parallel_data["name"]: {"Error": str(exc)}} - if parallel_data['opts'].get('show_deploy_args', False) is False and isinstance(output, dict): - output.pop('deploy_kwargs', None) + if parallel_data["opts"].get("show_deploy_args", False) is False and isinstance( + output, dict + ): + output.pop("deploy_kwargs", None) - return { - parallel_data['name']: salt.utils.data.simple_types_filter(output) - } + return {parallel_data["name"]: salt.utils.data.simple_types_filter(output)} def destroy_multiprocessing(parallel_data, queue=None): - ''' + """ This function will be called from another process when running a map in parallel mode. The result from the destroy is always a json object. - ''' + """ salt.utils.crypt.reinit_crypto() - parallel_data['opts']['output'] = 'json' - clouds = salt.loader.clouds(parallel_data['opts']) + parallel_data["opts"]["output"] = "json" + clouds = salt.loader.clouds(parallel_data["opts"]) try: - fun = clouds['{0}.destroy'.format(parallel_data['driver'])] + fun = clouds["{0}.destroy".format(parallel_data["driver"])] with salt.utils.context.func_globals_inject( fun, - __active_provider_name__=':'.join([ - parallel_data['alias'], - parallel_data['driver'] - ]) + __active_provider_name__=":".join( + [parallel_data["alias"], parallel_data["driver"]] + ), ): - output = fun(parallel_data['name']) + output = fun(parallel_data["name"]) except SaltCloudException as exc: log.error( - 'Failed to destroy %s. Error: %s', - parallel_data['name'], exc, exc_info_on_loglevel=logging.DEBUG + "Failed to destroy %s. Error: %s", + parallel_data["name"], + exc, + exc_info_on_loglevel=logging.DEBUG, ) - return {parallel_data['name']: {'Error': str(exc)}} + return {parallel_data["name"]: {"Error": str(exc)}} - return { - parallel_data['name']: salt.utils.data.simple_types_filter(output) - } + return {parallel_data["name"]: salt.utils.data.simple_types_filter(output)} def run_parallel_map_providers_query(data, queue=None): - ''' + """ This function will be called from another process when building the providers map. - ''' + """ salt.utils.crypt.reinit_crypto() - cloud = Cloud(data['opts']) + cloud = Cloud(data["opts"]) try: with salt.utils.context.func_globals_inject( - cloud.clouds[data['fun']], - __active_provider_name__=':'.join([ - data['alias'], - data['driver'] - ]) + cloud.clouds[data["fun"]], + __active_provider_name__=":".join([data["alias"], data["driver"]]), ): return ( - data['alias'], - data['driver'], - salt.utils.data.simple_types_filter( - cloud.clouds[data['fun']]() - ) + data["alias"], + data["driver"], + salt.utils.data.simple_types_filter(cloud.clouds[data["fun"]]()), ) except Exception as err: # pylint: disable=broad-except log.debug( - 'Failed to execute \'%s()\' while querying for running nodes: %s', - data['fun'], err, exc_info_on_loglevel=logging.DEBUG + "Failed to execute '%s()' while querying for running nodes: %s", + data["fun"], + err, + exc_info_on_loglevel=logging.DEBUG, ) # Failed to communicate with the provider, don't list any nodes - return data['alias'], data['driver'], () + return data["alias"], data["driver"], () # for pickle and multiprocessing, we can't use directly decorators diff --git a/salt/cloud/cli.py b/salt/cloud/cli.py index ef5b7deead2..6d208f0b188 100644 --- a/salt/cloud/cli.py +++ b/salt/cloud/cli.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Primary interfaces for the salt-cloud system -''' +""" # Need to get data from 4 sources! # CLI options # salt cloud config - CONFIG_DIR + '/cloud' @@ -13,10 +13,10 @@ Primary interfaces for the salt-cloud system # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import sys -import logging -from salt.ext.six.moves import input # Import salt libs import salt.cloud @@ -28,53 +28,52 @@ import salt.utils.cloud import salt.utils.parsers import salt.utils.user from salt.exceptions import SaltCloudException, SaltCloudSystemExit -from salt.utils.verify import check_user, verify_env, verify_log_files, verify_log # Import 3rd-party libs from salt.ext import six +from salt.ext.six.moves import input +from salt.utils.verify import check_user, verify_env, verify_log, verify_log_files log = logging.getLogger(__name__) +# pylint: disable=broad-except + class SaltCloud(salt.utils.parsers.SaltCloudParser): - def run(self): - ''' + """ Execute the salt-cloud command line - ''' + """ # Parse shell arguments self.parse_args() - salt_master_user = self.config.get('user') + salt_master_user = self.config.get("user") if salt_master_user is None: salt_master_user = salt.utils.user.get_user() if not check_user(salt_master_user): self.error( - 'If salt-cloud is running on a master machine, salt-cloud ' - 'needs to run as the same user as the salt-master, \'{0}\'. ' - 'If salt-cloud is not running on a salt-master, the ' - 'appropriate write permissions must be granted to \'{1}\'. ' - 'Please run salt-cloud as root, \'{0}\', or change ' - 'permissions for \'{1}\'.'.format( - salt_master_user, - syspaths.CONFIG_DIR - ) + "If salt-cloud is running on a master machine, salt-cloud " + "needs to run as the same user as the salt-master, '{0}'. " + "If salt-cloud is not running on a salt-master, the " + "appropriate write permissions must be granted to '{1}'. " + "Please run salt-cloud as root, '{0}', or change " + "permissions for '{1}'.".format(salt_master_user, syspaths.CONFIG_DIR) ) try: - if self.config['verify_env']: + if self.config["verify_env"]: verify_env( - [os.path.dirname(self.config['conf_file'])], + [os.path.dirname(self.config["conf_file"])], salt_master_user, - root_dir=self.config['root_dir'], + root_dir=self.config["root_dir"], ) - logfile = self.config['log_file'] + logfile = self.config["log_file"] if logfile is not None: # Logfile is not using Syslog, verify verify_log_files([logfile], salt_master_user) except (IOError, OSError) as err: - log.error('Error while verifying the environment: %s', err) + log.error("Error while verifying the environment: %s", err) sys.exit(err.errno) # Setup log file logging @@ -83,28 +82,28 @@ class SaltCloud(salt.utils.parsers.SaltCloudParser): if self.options.update_bootstrap: ret = salt.utils.cloud.update_bootstrap(self.config) - salt.output.display_output(ret, - self.options.output, - opts=self.config) + salt.output.display_output(ret, self.options.output, opts=self.config) self.exit(salt.defaults.exitcodes.EX_OK) - log.info('salt-cloud starting') + log.info("salt-cloud starting") try: mapper = salt.cloud.Map(self.config) except SaltCloudSystemExit as exc: self.handle_exception(exc.args, exc) except SaltCloudException as exc: - msg = 'There was an error generating the mapper.' + msg = "There was an error generating the mapper." self.handle_exception(msg, exc) - names = self.config.get('names', None) + names = self.config.get("names", None) if names is not None: filtered_rendered_map = {} for map_profile in mapper.rendered_map: filtered_map_profile = {} for name in mapper.rendered_map[map_profile]: if name in names: - filtered_map_profile[name] = mapper.rendered_map[map_profile][name] + filtered_map_profile[name] = mapper.rendered_map[map_profile][ + name + ] if filtered_map_profile: filtered_rendered_map[map_profile] = filtered_map_profile mapper.rendered_map = filtered_rendered_map @@ -112,175 +111,187 @@ class SaltCloud(salt.utils.parsers.SaltCloudParser): ret = {} if self.selected_query_option is not None: - if self.selected_query_option == 'list_providers': + if self.selected_query_option == "list_providers": + # pylint: disable=broad-except try: ret = mapper.provider_list() - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error listing providers: {0}' + except (SaltCloudException, Exception,) as exc: + msg = "There was an error listing providers: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except - elif self.selected_query_option == 'list_profiles': + elif self.selected_query_option == "list_profiles": provider = self.options.list_profiles + # pylint: disable=broad-except try: ret = mapper.profile_list(provider) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error listing profiles: {0}' + except (SaltCloudException, Exception,) as exc: + msg = "There was an error listing profiles: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except - elif self.config.get('map', None): - log.info('Applying map from \'%s\'.', self.config['map']) + elif self.config.get("map", None): + log.info("Applying map from '%s'.", self.config["map"]) + # pylint: disable=broad-except try: - ret = mapper.interpolated_map( - query=self.selected_query_option - ) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error with a custom map: {0}' + ret = mapper.interpolated_map(query=self.selected_query_option) + except (SaltCloudException, Exception,) as exc: + msg = "There was an error with a custom map: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except else: + # pylint: disable=broad-except try: ret = mapper.map_providers_parallel( query=self.selected_query_option ) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error with a map: {0}' + except (SaltCloudException, Exception,) as exc: + msg = "There was an error with a map: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except elif self.options.list_locations is not None: + # pylint: disable=broad-except try: - ret = mapper.location_list( - self.options.list_locations - ) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error listing locations: {0}' + ret = mapper.location_list(self.options.list_locations) + except (SaltCloudException, Exception,) as exc: + msg = "There was an error listing locations: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except elif self.options.list_images is not None: + # pylint: disable=broad-except try: - ret = mapper.image_list( - self.options.list_images - ) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error listing images: {0}' + ret = mapper.image_list(self.options.list_images) + except (SaltCloudException, Exception,) as exc: + msg = "There was an error listing images: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except elif self.options.list_sizes is not None: + # pylint: disable=broad-except try: - ret = mapper.size_list( - self.options.list_sizes - ) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error listing sizes: {0}' + ret = mapper.size_list(self.options.list_sizes) + except (SaltCloudException, Exception,) as exc: + msg = "There was an error listing sizes: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except - elif self.options.destroy and (self.config.get('names', None) or - self.config.get('map', None)): - map_file = self.config.get('map', None) - names = self.config.get('names', ()) + elif self.options.destroy and ( + self.config.get("names", None) or self.config.get("map", None) + ): + map_file = self.config.get("map", None) + names = self.config.get("names", ()) if map_file is not None: if names != (): - msg = 'Supplying a mapfile, \'{0}\', in addition to instance names {1} ' \ - 'with the \'--destroy\' or \'-d\' function is not supported. ' \ - 'Please choose to delete either the entire map file or individual ' \ - 'instances.'.format(map_file, names) + msg = ( + "Supplying a mapfile, '{0}', in addition to instance names {1} " + "with the '--destroy' or '-d' function is not supported. " + "Please choose to delete either the entire map file or individual " + "instances.".format(map_file, names) + ) self.handle_exception(msg, SaltCloudSystemExit) - log.info('Applying map from \'%s\'.', map_file) - matching = mapper.delete_map(query='list_nodes') + log.info("Applying map from '%s'.", map_file) + matching = mapper.delete_map(query="list_nodes") else: matching = mapper.get_running_by_names( - names, - profile=self.options.profile + names, profile=self.options.profile ) if not matching: - print('No machines were found to be destroyed') + print("No machines were found to be destroyed") self.exit(salt.defaults.exitcodes.EX_OK) - msg = 'The following virtual machines are set to be destroyed:\n' + msg = "The following virtual machines are set to be destroyed:\n" names = set() for alias, drivers in six.iteritems(matching): - msg += ' {0}:\n'.format(alias) + msg += " {0}:\n".format(alias) for driver, vms in six.iteritems(drivers): - msg += ' {0}:\n'.format(driver) + msg += " {0}:\n".format(driver) for name in vms: - msg += ' {0}\n'.format(name) + msg += " {0}\n".format(name) names.add(name) + # pylint: disable=broad-except try: if self.print_confirm(msg): ret = mapper.destroy(names, cached=True) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error destroying machines: {0}' + except (SaltCloudException, Exception,) as exc: + msg = "There was an error destroying machines: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except - elif self.options.action and (self.config.get('names', None) or - self.config.get('map', None)): - if self.config.get('map', None): - log.info('Applying map from \'%s\'.', self.config['map']) + elif self.options.action and ( + self.config.get("names", None) or self.config.get("map", None) + ): + if self.config.get("map", None): + log.info("Applying map from '%s'.", self.config["map"]) try: names = mapper.get_vmnames_by_action(self.options.action) except SaltCloudException as exc: - msg = 'There was an error actioning virtual machines.' + msg = "There was an error actioning virtual machines." self.handle_exception(msg, exc) else: - names = self.config.get('names', None) + names = self.config.get("names", None) kwargs = {} machines = [] msg = ( - 'The following virtual machines are set to be actioned with ' - '"{0}":\n'.format( - self.options.action - ) + "The following virtual machines are set to be actioned with " + '"{0}":\n'.format(self.options.action) ) for name in names: - if '=' in name: + if "=" in name: # This is obviously not a machine name, treat it as a kwarg - key, value = name.split('=', 1) + key, value = name.split("=", 1) kwargs[key] = value else: - msg += ' {0}\n'.format(name) + msg += " {0}\n".format(name) machines.append(name) names = machines + # pylint: disable=broad-except try: if self.print_confirm(msg): ret = mapper.do_action(names, kwargs) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error actioning machines: {0}' + except (SaltCloudException, Exception,) as exc: + msg = "There was an error actioning machines: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except elif self.options.function: kwargs = {} args = self.args[:] for arg in args[:]: - if '=' in arg: - key, value = arg.split('=', 1) + if "=" in arg: + key, value = arg.split("=", 1) kwargs[key] = value args.remove(arg) if args: self.error( - 'Any arguments passed to --function need to be passed ' - 'as kwargs. Ex: image=ami-54cf5c3d. Remaining ' - 'arguments: {0}'.format(args) + "Any arguments passed to --function need to be passed " + "as kwargs. Ex: image=ami-54cf5c3d. Remaining " + "arguments: {0}".format(args) ) + # pylint: disable=broad-except try: ret = mapper.do_function( self.function_provider, self.function_name, kwargs ) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error running the function: {0}' + except (SaltCloudException, Exception,) as exc: + msg = "There was an error running the function: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except - elif self.options.profile and self.config.get('names', False): + elif self.options.profile and self.config.get("names", False): + # pylint: disable=broad-except try: - ret = mapper.run_profile( - self.options.profile, - self.config.get('names') - ) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was a profile error: {0}' + ret = mapper.run_profile(self.options.profile, self.config.get("names")) + except (SaltCloudException, Exception,) as exc: + msg = "There was a profile error: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except elif self.options.set_password: username = self.credential_username @@ -288,47 +299,47 @@ class SaltCloud(salt.utils.parsers.SaltCloudParser): # TODO: check if provider is configured # set the password salt.utils.cloud.store_password_in_keyring(provider_name, username) - elif self.config.get('map', None) and \ - self.selected_query_option is None: + elif self.config.get("map", None) and self.selected_query_option is None: if not mapper.rendered_map: - sys.stderr.write('No nodes defined in this map') + sys.stderr.write("No nodes defined in this map") self.exit(salt.defaults.exitcodes.EX_GENERIC) + # pylint: disable=broad-except try: ret = {} run_map = True - log.info('Applying map from \'%s\'.', self.config['map']) + log.info("Applying map from '%s'.", self.config["map"]) dmap = mapper.map_data() - msg = '' - if 'errors' in dmap: + msg = "" + if "errors" in dmap: # display profile errors - msg += 'Found the following errors:\n' - for profile_name, error in six.iteritems(dmap['errors']): - msg += ' {0}: {1}\n'.format(profile_name, error) + msg += "Found the following errors:\n" + for profile_name, error in six.iteritems(dmap["errors"]): + msg += " {0}: {1}\n".format(profile_name, error) sys.stderr.write(msg) sys.stderr.flush() - msg = '' - if 'existing' in dmap: - msg += ('The following virtual machines already exist:\n') - for name in dmap['existing']: - msg += ' {0}\n'.format(name) + msg = "" + if "existing" in dmap: + msg += "The following virtual machines already exist:\n" + for name in dmap["existing"]: + msg += " {0}\n".format(name) - if dmap['create']: - msg += ('The following virtual machines are set to be ' - 'created:\n') - for name in dmap['create']: - msg += ' {0}\n'.format(name) + if dmap["create"]: + msg += "The following virtual machines are set to be " "created:\n" + for name in dmap["create"]: + msg += " {0}\n".format(name) - if 'destroy' in dmap: - msg += ('The following virtual machines are set to be ' - 'destroyed:\n') - for name in dmap['destroy']: - msg += ' {0}\n'.format(name) + if "destroy" in dmap: + msg += ( + "The following virtual machines are set to be " "destroyed:\n" + ) + for name in dmap["destroy"]: + msg += " {0}\n".format(name) - if not dmap['create'] and not dmap.get('destroy', None): - if not dmap.get('existing', None): + if not dmap["create"] and not dmap.get("destroy", None): + if not dmap.get("existing", None): # nothing to create or destroy & nothing exists print(msg) self.exit(1) @@ -340,68 +351,71 @@ class SaltCloud(salt.utils.parsers.SaltCloudParser): if self.print_confirm(msg): ret = mapper.run_map(dmap) - if self.config.get('parallel', False) is False: - log.info('Complete') + if self.config.get("parallel", False) is False: + log.info("Complete") - if dmap.get('existing', None): - for name in dmap['existing']: - if 'ec2' in dmap['existing'][name]['provider']: - msg = 'Instance already exists, or is terminated and has the same name.' + if dmap.get("existing", None): + for name in dmap["existing"]: + if "ec2" in dmap["existing"][name]["provider"]: + msg = "Instance already exists, or is terminated and has the same name." else: - msg = 'Already running.' - ret[name] = {'Message': msg} + msg = "Already running." + ret[name] = {"Message": msg} - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was a query error: {0}' + except (SaltCloudException, Exception,) as exc: + msg = "There was a query error: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except elif self.options.bootstrap: host = self.options.bootstrap - if self.args and '=' not in self.args[0]: + if self.args and "=" not in self.args[0]: minion_id = self.args.pop(0) else: minion_id = host vm_ = { - 'driver': '', - 'ssh_host': host, - 'name': minion_id, + "driver": "", + "ssh_host": host, + "name": minion_id, } args = self.args[:] for arg in args[:]: - if '=' in arg: - key, value = arg.split('=', 1) + if "=" in arg: + key, value = arg.split("=", 1) vm_[key] = value args.remove(arg) if args: self.error( - 'Any arguments passed to --bootstrap need to be passed as ' - 'kwargs. Ex: ssh_username=larry. Remaining arguments: {0}'.format(args) + "Any arguments passed to --bootstrap need to be passed as " + "kwargs. Ex: ssh_username=larry. Remaining arguments: {0}".format( + args + ) ) + # pylint: disable=broad-except try: ret = salt.utils.cloud.bootstrap(vm_, self.config) - except (SaltCloudException, Exception) as exc: # pylint: disable=broad-except - msg = 'There was an error bootstrapping the minion: {0}' + except (SaltCloudException, Exception,) as exc: + msg = "There was an error bootstrapping the minion: {0}" self.handle_exception(msg, exc) + # pylint: enable=broad-except else: - self.error('Nothing was done. Using the proper arguments?') + self.error("Nothing was done. Using the proper arguments?") - salt.output.display_output(ret, - self.options.output, - opts=self.config) + salt.output.display_output(ret, self.options.output, opts=self.config) self.exit(salt.defaults.exitcodes.EX_OK) def print_confirm(self, msg): if self.options.assume_yes: return True print(msg) - res = input('Proceed? [N/y] ') - if not res.lower().startswith('y'): + res = input("Proceed? [N/y] ") + if not res.lower().startswith("y"): return False - print('... proceeding') + print("... proceeding") return True def handle_exception(self, msg, exc): @@ -411,11 +425,8 @@ class SaltCloud(salt.utils.parsers.SaltCloudParser): # This is a salt cloud system exit if exc.exit_code > 0: # the exit code is bigger than 0, it's an error - msg = 'Error: {0}'.format(msg) - self.exit( - exc.exit_code, - msg.format(exc).rstrip() + '\n' - ) + msg = "Error: {0}".format(msg) + self.exit(exc.exit_code, msg.format(exc).rstrip() + "\n") # It's not a system exit but it's an error we can # handle self.error(msg.format(exc)) @@ -426,7 +437,7 @@ class SaltCloud(salt.utils.parsers.SaltCloudParser): msg.format(exc), # Show the traceback if the debug logging level is # enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) # pylint: enable=str-format-in-logging self.exit(salt.defaults.exitcodes.EX_GENERIC) diff --git a/salt/cloud/clouds/aliyun.py b/salt/cloud/clouds/aliyun.py index e7ccbe706f4..20e3123c036 100644 --- a/salt/cloud/clouds/aliyun.py +++ b/salt/cloud/clouds/aliyun.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" AliYun ECS Cloud Module ======================= @@ -23,39 +23,46 @@ Set up the cloud configuration at ``/etc/salt/cloud.providers`` or driver: aliyun :depends: requests -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time -import pprint -import logging -import hmac -import uuid -import sys + import base64 +import hmac +import logging +import pprint +import sys +import time +import uuid from hashlib import sha1 -# Import Salt libs -from salt.ext.six.moves.urllib.parse import quote as _quote # pylint: disable=import-error,no-name-in-module +import salt.config as config # Import salt cloud libs import salt.utils.cloud import salt.utils.data import salt.utils.json -import salt.config as config from salt.exceptions import ( + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, SaltCloudNotFound, SaltCloudSystemExit, - SaltCloudExecutionFailure, - SaltCloudExecutionTimeout ) -from salt.utils.stringutils import to_bytes # Import 3rd-party libs from salt.ext import six + +# Import Salt libs +# pylint: disable=import-error,no-name-in-module +from salt.ext.six.moves.urllib.parse import quote as _quote + +# pylint: enable=import-error,no-name-in-module +from salt.utils.stringutils import to_bytes + try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False @@ -65,32 +72,32 @@ log = logging.getLogger(__name__) ALIYUN_LOCATIONS = { # 'us-west-2': 'ec2_us_west_oregon', - 'cn-hangzhou': 'AliYun HangZhou Region', - 'cn-beijing': 'AliYun BeiJing Region', - 'cn-hongkong': 'AliYun HongKong Region', - 'cn-qingdao': 'AliYun QingDao Region', - 'cn-shanghai': 'AliYun ShangHai Region', - 'cn-shenzhen': 'AliYun ShenZheng Region', - 'ap-northeast-1': 'AliYun DongJing Region', - 'ap-southeast-1': 'AliYun XinJiaPo Region', - 'ap-southeast-2': 'AliYun XiNi Region', - 'eu-central-1': 'EU FalaKeFu Region', - 'me-east-1': 'ME DiBai Region', - 'us-east-1': 'US FuJiNiYa Region', - 'us-west-1': 'US GuiGu Region', + "cn-hangzhou": "AliYun HangZhou Region", + "cn-beijing": "AliYun BeiJing Region", + "cn-hongkong": "AliYun HongKong Region", + "cn-qingdao": "AliYun QingDao Region", + "cn-shanghai": "AliYun ShangHai Region", + "cn-shenzhen": "AliYun ShenZheng Region", + "ap-northeast-1": "AliYun DongJing Region", + "ap-southeast-1": "AliYun XinJiaPo Region", + "ap-southeast-2": "AliYun XiNi Region", + "eu-central-1": "EU FalaKeFu Region", + "me-east-1": "ME DiBai Region", + "us-east-1": "US FuJiNiYa Region", + "us-west-1": "US GuiGu Region", } -DEFAULT_LOCATION = 'cn-hangzhou' +DEFAULT_LOCATION = "cn-hangzhou" -DEFAULT_ALIYUN_API_VERSION = '2014-05-26' +DEFAULT_ALIYUN_API_VERSION = "2014-05-26" -__virtualname__ = 'aliyun' +__virtualname__ = "aliyun" # Only load in this module if the aliyun configurations are in place def __virtual__(): - ''' + """ Check for aliyun configurations - ''' + """ if get_configured_provider() is False: return False @@ -101,399 +108,380 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('id', 'key') + __opts__, __active_provider_name__ or __virtualname__, ("id", "key") ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'requests': HAS_REQUESTS} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"requests": HAS_REQUESTS}) def avail_locations(call=None): - ''' + """ Return a dict of all available VM locations on the cloud provider with relevant data - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) - params = {'Action': 'DescribeRegions'} + params = {"Action": "DescribeRegions"} items = query(params=params) ret = {} - for region in items['Regions']['Region']: - ret[region['RegionId']] = {} + for region in items["Regions"]["Region"]: + ret[region["RegionId"]] = {} for item in region: - ret[region['RegionId']][item] = six.text_type(region[item]) + ret[region["RegionId"]][item] = six.text_type(region[item]) return ret def avail_images(kwargs=None, call=None): - ''' + """ Return a list of the images that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) if not isinstance(kwargs, dict): kwargs = {} provider = get_configured_provider() - location = provider.get('location', DEFAULT_LOCATION) + location = provider.get("location", DEFAULT_LOCATION) - if 'location' in kwargs: - location = kwargs['location'] + if "location" in kwargs: + location = kwargs["location"] params = { - 'Action': 'DescribeImages', - 'RegionId': location, - 'PageSize': '100', + "Action": "DescribeImages", + "RegionId": location, + "PageSize": "100", } items = query(params=params) ret = {} - for image in items['Images']['Image']: - ret[image['ImageId']] = {} + for image in items["Images"]["Image"]: + ret[image["ImageId"]] = {} for item in image: - ret[image['ImageId']][item] = six.text_type(image[item]) + ret[image["ImageId"]][item] = six.text_type(image[item]) return ret def avail_sizes(call=None): - ''' + """ Return a list of the image sizes that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) - params = {'Action': 'DescribeInstanceTypes'} + params = {"Action": "DescribeInstanceTypes"} items = query(params=params) ret = {} - for image in items['InstanceTypes']['InstanceType']: - ret[image['InstanceTypeId']] = {} + for image in items["InstanceTypes"]["InstanceType"]: + ret[image["InstanceTypeId"]] = {} for item in image: - ret[image['InstanceTypeId']][item] = six.text_type(image[item]) + ret[image["InstanceTypeId"]][item] = six.text_type(image[item]) return ret def get_location(vm_=None): - ''' + """ Return the aliyun region to use, in this order: - CLI parameter - VM parameter - Cloud profile setting - ''' + """ return __opts__.get( - 'location', + "location", config.get_cloud_config_value( - 'location', + "location", vm_ or get_configured_provider(), __opts__, default=DEFAULT_LOCATION, - search_global=False - ) + search_global=False, + ), ) def list_availability_zones(call=None): - ''' + """ List all availability zones in the current region - ''' + """ ret = {} - params = {'Action': 'DescribeZones', - 'RegionId': get_location()} + params = {"Action": "DescribeZones", "RegionId": get_location()} items = query(params) - for zone in items['Zones']['Zone']: - ret[zone['ZoneId']] = {} + for zone in items["Zones"]["Zone"]: + ret[zone["ZoneId"]] = {} for item in zone: - ret[zone['ZoneId']][item] = six.text_type(zone[item]) + ret[zone["ZoneId"]][item] = six.text_type(zone[item]) return ret def list_nodes_min(call=None): - ''' + """ Return a list of the VMs that are on the provider. Only a list of VM names, and their state, is returned. This is the minimum amount of information needed to check for existing VMs. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_min function must be called with -f or --function.' + "The list_nodes_min function must be called with -f or --function." ) ret = {} location = get_location() params = { - 'Action': 'DescribeInstanceStatus', - 'RegionId': location, + "Action": "DescribeInstanceStatus", + "RegionId": location, } nodes = query(params) - log.debug( - 'Total %s instance found in Region %s', - nodes['TotalCount'], location - ) - if 'Code' in nodes or nodes['TotalCount'] == 0: + log.debug("Total %s instance found in Region %s", nodes["TotalCount"], location) + if "Code" in nodes or nodes["TotalCount"] == 0: return ret - for node in nodes['InstanceStatuses']['InstanceStatus']: - ret[node['InstanceId']] = {} + for node in nodes["InstanceStatuses"]["InstanceStatus"]: + ret[node["InstanceId"]] = {} for item in node: - ret[node['InstanceId']][item] = node[item] + ret[node["InstanceId"]][item] = node[item] return ret def list_nodes(call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) nodes = list_nodes_full() ret = {} for instanceId in nodes: node = nodes[instanceId] - ret[node['name']] = { - 'id': node['id'], - 'name': node['name'], - 'public_ips': node['public_ips'], - 'private_ips': node['private_ips'], - 'size': node['size'], - 'state': six.text_type(node['state']), + ret[node["name"]] = { + "id": node["id"], + "name": node["name"], + "public_ips": node["public_ips"], + "private_ips": node["private_ips"], + "size": node["size"], + "state": six.text_type(node["state"]), } return ret def list_nodes_full(call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f ' - 'or --function.' + "The list_nodes_full function must be called with -f " "or --function." ) ret = {} location = get_location() params = { - 'Action': 'DescribeInstanceStatus', - 'RegionId': location, - 'PageSize': '50' + "Action": "DescribeInstanceStatus", + "RegionId": location, + "PageSize": "50", } result = query(params=params) - log.debug( - 'Total %s instance found in Region %s', - result['TotalCount'], location - ) - if 'Code' in result or result['TotalCount'] == 0: + log.debug("Total %s instance found in Region %s", result["TotalCount"], location) + if "Code" in result or result["TotalCount"] == 0: return ret # aliyun max 100 top instance in api - result_instancestatus = result['InstanceStatuses']['InstanceStatus'] - if result['TotalCount'] > 50: - params['PageNumber'] = '2' + result_instancestatus = result["InstanceStatuses"]["InstanceStatus"] + if result["TotalCount"] > 50: + params["PageNumber"] = "2" result = query(params=params) - result_instancestatus.update(result['InstanceStatuses']['InstanceStatus']) + result_instancestatus.update(result["InstanceStatuses"]["InstanceStatus"]) for node in result_instancestatus: - instanceId = node.get('InstanceId', '') + instanceId = node.get("InstanceId", "") - params = { - 'Action': 'DescribeInstanceAttribute', - 'InstanceId': instanceId - } + params = {"Action": "DescribeInstanceAttribute", "InstanceId": instanceId} items = query(params=params) - if 'Code' in items: - log.warning('Query instance:%s attribute failed', instanceId) + if "Code" in items: + log.warning("Query instance:%s attribute failed", instanceId) continue - name = items['InstanceName'] + name = items["InstanceName"] ret[name] = { - 'id': items['InstanceId'], - 'name': name, - 'image': items['ImageId'], - 'size': 'TODO', - 'state': items['Status'] + "id": items["InstanceId"], + "name": name, + "image": items["ImageId"], + "size": "TODO", + "state": items["Status"], } for item in items: value = items[item] if value is not None: value = six.text_type(value) if item == "PublicIpAddress": - ret[name]['public_ips'] = items[item]['IpAddress'] - if item == "InnerIpAddress" and 'private_ips' not in ret[name]: - ret[name]['private_ips'] = items[item]['IpAddress'] - if item == 'VpcAttributes': - vpc_ips = items[item]['PrivateIpAddress']['IpAddress'] + ret[name]["public_ips"] = items[item]["IpAddress"] + if item == "InnerIpAddress" and "private_ips" not in ret[name]: + ret[name]["private_ips"] = items[item]["IpAddress"] + if item == "VpcAttributes": + vpc_ips = items[item]["PrivateIpAddress"]["IpAddress"] if vpc_ips: - ret[name]['private_ips'] = vpc_ips + ret[name]["private_ips"] = vpc_ips ret[name][item] = value - provider = __active_provider_name__ or 'aliyun' - if ':' in provider: - comps = provider.split(':') + provider = __active_provider_name__ or "aliyun" + if ":" in provider: + comps = provider.split(":") provider = comps[0] - __opts__['update_cachedir'] = True - __utils__['cloud.cache_node_list'](ret, provider, __opts__) + __opts__["update_cachedir"] = True + __utils__["cloud.cache_node_list"](ret, provider, __opts__) return ret def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full('function'), __opts__['query.selection'], call, + list_nodes_full("function"), __opts__["query.selection"], call, ) def list_securitygroup(call=None): - ''' + """ Return a list of security group - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) params = { - 'Action': 'DescribeSecurityGroups', - 'RegionId': get_location(), - 'PageSize': '50', + "Action": "DescribeSecurityGroups", + "RegionId": get_location(), + "PageSize": "50", } result = query(params) - if 'Code' in result: + if "Code" in result: return {} ret = {} - for sg in result['SecurityGroups']['SecurityGroup']: - ret[sg['SecurityGroupId']] = {} + for sg in result["SecurityGroups"]["SecurityGroup"]: + ret[sg["SecurityGroupId"]] = {} for item in sg: - ret[sg['SecurityGroupId']][item] = sg[item] + ret[sg["SecurityGroupId"]][item] = sg[item] return ret def get_image(vm_): - ''' + """ Return the image object to use - ''' + """ images = avail_images() - vm_image = six.text_type(config.get_cloud_config_value( - 'image', vm_, __opts__, search_global=False - )) + vm_image = six.text_type( + config.get_cloud_config_value("image", vm_, __opts__, search_global=False) + ) if not vm_image: - raise SaltCloudNotFound('No image specified for this VM.') + raise SaltCloudNotFound("No image specified for this VM.") if vm_image and six.text_type(vm_image) in images: - return images[vm_image]['ImageId'] + return images[vm_image]["ImageId"] raise SaltCloudNotFound( - 'The specified image, \'{0}\', could not be found.'.format(vm_image) + "The specified image, '{0}', could not be found.".format(vm_image) ) def get_securitygroup(vm_): - ''' + """ Return the security group - ''' + """ sgs = list_securitygroup() securitygroup = config.get_cloud_config_value( - 'securitygroup', vm_, __opts__, search_global=False + "securitygroup", vm_, __opts__, search_global=False ) if not securitygroup: - raise SaltCloudNotFound('No securitygroup ID specified for this VM.') + raise SaltCloudNotFound("No securitygroup ID specified for this VM.") if securitygroup and six.text_type(securitygroup) in sgs: - return sgs[securitygroup]['SecurityGroupId'] + return sgs[securitygroup]["SecurityGroupId"] raise SaltCloudNotFound( - 'The specified security group, \'{0}\', could not be found.'.format( - securitygroup) + "The specified security group, '{0}', could not be found.".format(securitygroup) ) def get_size(vm_): - ''' + """ Return the VM's size. Used by create_node(). - ''' + """ sizes = avail_sizes() - vm_size = six.text_type(config.get_cloud_config_value( - 'size', vm_, __opts__, search_global=False - )) + vm_size = six.text_type( + config.get_cloud_config_value("size", vm_, __opts__, search_global=False) + ) if not vm_size: - raise SaltCloudNotFound('No size specified for this VM.') + raise SaltCloudNotFound("No size specified for this VM.") if vm_size and six.text_type(vm_size) in sizes: - return sizes[vm_size]['InstanceTypeId'] + return sizes[vm_size]["InstanceTypeId"] raise SaltCloudNotFound( - 'The specified size, \'{0}\', could not be found.'.format(vm_size) + "The specified size, '{0}', could not be found.".format(vm_size) ) def __get_location(vm_): - ''' + """ Return the VM's location - ''' + """ locations = avail_locations() - vm_location = six.text_type(config.get_cloud_config_value( - 'location', vm_, __opts__, search_global=False - )) + vm_location = six.text_type( + config.get_cloud_config_value("location", vm_, __opts__, search_global=False) + ) if not vm_location: - raise SaltCloudNotFound('No location specified for this VM.') + raise SaltCloudNotFound("No location specified for this VM.") if vm_location and six.text_type(vm_location) in locations: - return locations[vm_location]['RegionId'] + return locations[vm_location]["RegionId"] raise SaltCloudNotFound( - 'The specified location, \'{0}\', could not be found.'.format( - vm_location - ) + "The specified location, '{0}', could not be found.".format(vm_location) ) def start(name, call=None): - ''' + """ Start a node CLI Examples: @@ -501,25 +489,22 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start myinstance - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - log.info('Starting node %s', name) + log.info("Starting node %s", name) - instanceId = _get_node(name)['InstanceId'] + instanceId = _get_node(name)["InstanceId"] - params = {'Action': 'StartInstance', - 'InstanceId': instanceId} + params = {"Action": "StartInstance", "InstanceId": instanceId} result = query(params) return result def stop(name, force=False, call=None): - ''' + """ Stop a node CLI Examples: @@ -528,20 +513,18 @@ def stop(name, force=False, call=None): salt-cloud -a stop myinstance salt-cloud -a stop myinstance force=True - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - log.info('Stopping node %s', name) + log.info("Stopping node %s", name) - instanceId = _get_node(name)['InstanceId'] + instanceId = _get_node(name)["InstanceId"] params = { - 'Action': 'StopInstance', - 'InstanceId': instanceId, - 'ForceStop': six.text_type(force).lower() + "Action": "StopInstance", + "InstanceId": instanceId, + "ForceStop": six.text_type(force).lower(), } result = query(params) @@ -549,7 +532,7 @@ def stop(name, force=False, call=None): def reboot(name, call=None): - ''' + """ Reboot a node CLI Examples: @@ -557,45 +540,47 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot myinstance - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - log.info('Rebooting node %s', name) + log.info("Rebooting node %s", name) - instance_id = _get_node(name)['InstanceId'] + instance_id = _get_node(name)["InstanceId"] - params = {'Action': 'RebootInstance', - 'InstanceId': instance_id} + params = {"Action": "RebootInstance", "InstanceId": instance_id} result = query(params) return result def create_node(kwargs): - ''' + """ Convenience function to make the rest api call for node creation. - ''' + """ if not isinstance(kwargs, dict): kwargs = {} # Required parameters params = { - 'Action': 'CreateInstance', - 'InstanceType': kwargs.get('size_id', ''), - 'RegionId': kwargs.get('region_id', DEFAULT_LOCATION), - 'ImageId': kwargs.get('image_id', ''), - 'SecurityGroupId': kwargs.get('securitygroup_id', ''), - 'InstanceName': kwargs.get('name', ''), + "Action": "CreateInstance", + "InstanceType": kwargs.get("size_id", ""), + "RegionId": kwargs.get("region_id", DEFAULT_LOCATION), + "ImageId": kwargs.get("image_id", ""), + "SecurityGroupId": kwargs.get("securitygroup_id", ""), + "InstanceName": kwargs.get("name", ""), } # Optional parameters' optional = [ - 'InstanceName', 'InternetChargeType', - 'InternetMaxBandwidthIn', 'InternetMaxBandwidthOut', - 'HostName', 'Password', 'SystemDisk.Category', 'VSwitchId' + "InstanceName", + "InternetChargeType", + "InternetMaxBandwidthIn", + "InternetMaxBandwidthOut", + "HostName", + "Password", + "SystemDisk.Category", + "VSwitchId" # 'DataDisk.n.Size', 'DataDisk.n.Category', 'DataDisk.n.SnapshotId' ] @@ -605,146 +590,154 @@ def create_node(kwargs): # invoke web call result = query(params) - return result['InstanceId'] + return result["InstanceId"] def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'aliyun', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "aliyun", vm_["profile"], vm_=vm_ + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) kwargs = { - 'name': vm_['name'], - 'size_id': get_size(vm_), - 'image_id': get_image(vm_), - 'region_id': __get_location(vm_), - 'securitygroup_id': get_securitygroup(vm_), + "name": vm_["name"], + "size_id": get_size(vm_), + "image_id": get_image(vm_), + "region_id": __get_location(vm_), + "securitygroup_id": get_securitygroup(vm_), } - if 'vswitch_id' in vm_: - kwargs['VSwitchId'] = vm_['vswitch_id'] - if 'internet_chargetype' in vm_: - kwargs['InternetChargeType'] = vm_['internet_chargetype'] - if 'internet_maxbandwidthin' in vm_: - kwargs['InternetMaxBandwidthIn'] = six.text_type(vm_['internet_maxbandwidthin']) - if 'internet_maxbandwidthout' in vm_: - kwargs['InternetMaxBandwidthOut'] = six.text_type(vm_['internet_maxbandwidthOut']) - if 'hostname' in vm_: - kwargs['HostName'] = vm_['hostname'] - if 'password' in vm_: - kwargs['Password'] = vm_['password'] - if 'instance_name' in vm_: - kwargs['InstanceName'] = vm_['instance_name'] - if 'systemdisk_category' in vm_: - kwargs['SystemDisk.Category'] = vm_['systemdisk_category'] + if "vswitch_id" in vm_: + kwargs["VSwitchId"] = vm_["vswitch_id"] + if "internet_chargetype" in vm_: + kwargs["InternetChargeType"] = vm_["internet_chargetype"] + if "internet_maxbandwidthin" in vm_: + kwargs["InternetMaxBandwidthIn"] = six.text_type(vm_["internet_maxbandwidthin"]) + if "internet_maxbandwidthout" in vm_: + kwargs["InternetMaxBandwidthOut"] = six.text_type( + vm_["internet_maxbandwidthOut"] + ) + if "hostname" in vm_: + kwargs["HostName"] = vm_["hostname"] + if "password" in vm_: + kwargs["Password"] = vm_["password"] + if "instance_name" in vm_: + kwargs["InstanceName"] = vm_["instance_name"] + if "systemdisk_category" in vm_: + kwargs["SystemDisk.Category"] = vm_["systemdisk_category"] - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']('requesting', kwargs, list(kwargs)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]("requesting", kwargs, list(kwargs)), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: ret = create_node(kwargs) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on Aliyun ECS\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: %s', - vm_['name'], six.text_type(exc), + "Error creating %s on Aliyun ECS\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: %s", + vm_["name"], + six.text_type(exc), # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False # repair ip address error and start vm time.sleep(8) - params = {'Action': 'StartInstance', - 'InstanceId': ret} + params = {"Action": "StartInstance", "InstanceId": ret} query(params) def __query_node_data(vm_name): - data = show_instance(vm_name, call='action') + data = show_instance(vm_name, call="action") if not data: # Trigger an error in the wait_for_ip function return False - if data.get('PublicIpAddress', None) is not None: + if data.get("PublicIpAddress", None) is not None: return data try: data = salt.utils.cloud.wait_for_ip( __query_node_data, - update_args=(vm_['name'],), + update_args=(vm_["name"],), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10), + "wait_for_ip_interval", vm_, __opts__, default=10 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) - if data['public_ips']: - ssh_ip = data['public_ips'][0] - elif data['private_ips']: - ssh_ip = data['private_ips'][0] + if data["public_ips"]: + ssh_ip = data["public_ips"][0] + elif data["private_ips"]: + ssh_ip = data["private_ips"][0] else: - log.info('No available ip:cant connect to salt') + log.info("No available ip:cant connect to salt") return False - log.debug('VM %s is now running', ssh_ip) - vm_['ssh_host'] = ssh_ip + log.debug("VM %s is now running", ssh_ip) + vm_["ssh_host"] = ssh_ip # The instance is booted and accessible, let's Salt it! - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data) - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def _compute_signature(parameters, access_key_secret): - ''' + """ Generate aliyun request signature - ''' + """ def percent_encode(line): if not isinstance(line, six.string_types): @@ -752,24 +745,23 @@ def _compute_signature(parameters, access_key_secret): s = line if sys.stdin.encoding is None: - s = line.decode().encode('utf8') + s = line.decode().encode("utf8") else: - s = line.decode(sys.stdin.encoding).encode('utf8') - res = _quote(s, '') - res = res.replace('+', '%20') - res = res.replace('*', '%2A') - res = res.replace('%7E', '~') + s = line.decode(sys.stdin.encoding).encode("utf8") + res = _quote(s, "") + res = res.replace("+", "%20") + res = res.replace("*", "%2A") + res = res.replace("%7E", "~") return res sortedParameters = sorted(list(parameters.items()), key=lambda items: items[0]) - canonicalizedQueryString = '' + canonicalizedQueryString = "" for k, v in sortedParameters: - canonicalizedQueryString += '&' + percent_encode(k) \ - + '=' + percent_encode(v) + canonicalizedQueryString += "&" + percent_encode(k) + "=" + percent_encode(v) # All aliyun API only support GET method - stringToSign = 'GET&%2F&' + percent_encode(canonicalizedQueryString[1:]) + stringToSign = "GET&%2F&" + percent_encode(canonicalizedQueryString[1:]) h = hmac.new(to_bytes(access_key_secret + "&"), stringToSign, sha1) signature = base64.encodestring(h.digest()).strip() @@ -777,29 +769,29 @@ def _compute_signature(parameters, access_key_secret): def query(params=None): - ''' + """ Make a web call to aliyun ECS REST API - ''' - path = 'https://ecs-cn-hangzhou.aliyuncs.com' + """ + path = "https://ecs-cn-hangzhou.aliyuncs.com" access_key_id = config.get_cloud_config_value( - 'id', get_configured_provider(), __opts__, search_global=False + "id", get_configured_provider(), __opts__, search_global=False ) access_key_secret = config.get_cloud_config_value( - 'key', get_configured_provider(), __opts__, search_global=False + "key", get_configured_provider(), __opts__, search_global=False ) timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) # public interface parameters parameters = { - 'Format': 'JSON', - 'Version': DEFAULT_ALIYUN_API_VERSION, - 'AccessKeyId': access_key_id, - 'SignatureVersion': '1.0', - 'SignatureMethod': 'HMAC-SHA1', - 'SignatureNonce': six.text_type(uuid.uuid1()), - 'TimeStamp': timestamp, + "Format": "JSON", + "Version": DEFAULT_ALIYUN_API_VERSION, + "AccessKeyId": access_key_id, + "SignatureVersion": "1.0", + "SignatureMethod": "HMAC-SHA1", + "SignatureNonce": six.text_type(uuid.uuid1()), + "TimeStamp": timestamp, } # include action or function parameters @@ -808,16 +800,13 @@ def query(params=None): # Calculate the string for Signature signature = _compute_signature(parameters, access_key_secret) - parameters['Signature'] = signature + parameters["Signature"] = signature request = requests.get(path, params=parameters, verify=True) if request.status_code != 200: raise SaltCloudSystemExit( - 'An error occurred while querying aliyun ECS. HTTP Code: {0} ' - 'Error: \'{1}\''.format( - request.status_code, - request.text - ) + "An error occurred while querying aliyun ECS. HTTP Code: {0} " + "Error: '{1}'".format(request.status_code, request.text) ) log.debug(request.url) @@ -825,30 +814,28 @@ def query(params=None): content = request.text result = salt.utils.json.loads(content) - if 'Code' in result: - raise SaltCloudSystemExit( - pprint.pformat(result.get('Message', {})) - ) + if "Code" in result: + raise SaltCloudSystemExit(pprint.pformat(result.get("Message", {}))) return result def script(vm_): - ''' + """ Return the script deployment object - ''' + """ deploy_script = salt.utils.cloud.os_script( - config.get_cloud_config_value('script', vm_, __opts__), + config.get_cloud_config_value("script", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) return deploy_script def show_disk(name, call=None): - ''' + """ Show the disk details of the instance CLI Examples: @@ -856,29 +843,26 @@ def show_disk(name, call=None): .. code-block:: bash salt-cloud -a show_disk aliyun myinstance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_disks action must be called with -a or --action.' + "The show_disks action must be called with -a or --action." ) ret = {} - params = { - 'Action': 'DescribeInstanceDisks', - 'InstanceId': name - } + params = {"Action": "DescribeInstanceDisks", "InstanceId": name} items = query(params=params) - for disk in items['Disks']['Disk']: - ret[disk['DiskId']] = {} + for disk in items["Disks"]["Disk"]: + ret[disk["DiskId"]] = {} for item in disk: - ret[disk['DiskId']][item] = six.text_type(disk[item]) + ret[disk["DiskId"]][item] = six.text_type(disk[item]) return ret def list_monitor_data(kwargs=None, call=None): - ''' + """ Get monitor data of the instance. If instance name is missing, will show all the instance monitor data on the region. @@ -888,42 +872,39 @@ def list_monitor_data(kwargs=None, call=None): salt-cloud -f list_monitor_data aliyun salt-cloud -f list_monitor_data aliyun name=AY14051311071990225bd - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_monitor_data must be called with -f or --function.' + "The list_monitor_data must be called with -f or --function." ) if not isinstance(kwargs, dict): kwargs = {} ret = {} - params = { - 'Action': 'GetMonitorData', - 'RegionId': get_location() - } - if 'name' in kwargs: - params['InstanceId'] = kwargs['name'] + params = {"Action": "GetMonitorData", "RegionId": get_location()} + if "name" in kwargs: + params["InstanceId"] = kwargs["name"] items = query(params=params) - monitorData = items['MonitorData'] + monitorData = items["MonitorData"] - for data in monitorData['InstanceMonitorData']: - ret[data['InstanceId']] = {} + for data in monitorData["InstanceMonitorData"]: + ret[data["InstanceId"]] = {} for item in data: - ret[data['InstanceId']][item] = six.text_type(data[item]) + ret[data["InstanceId"]][item] = six.text_type(data[item]) return ret def show_instance(name, call=None): - ''' + """ Show the details from aliyun instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) return _get_node(name) @@ -937,37 +918,35 @@ def _get_node(name): except KeyError: attempts -= 1 log.debug( - 'Failed to get the data for node \'%s\'. Remaining ' - 'attempts: %s', name, attempts + "Failed to get the data for node '%s'. Remaining " "attempts: %s", + name, + attempts, ) # Just a little delay between attempts... time.sleep(0.5) - raise SaltCloudNotFound( - 'The specified instance {0} not found'.format(name) - ) + raise SaltCloudNotFound("The specified instance {0} not found".format(name)) def show_image(kwargs, call=None): - ''' + """ Show the details from aliyun image - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_images function must be called with ' - '-f or --function' + "The show_images function must be called with " "-f or --function" ) if not isinstance(kwargs, dict): kwargs = {} location = get_location() - if 'location' in kwargs: - location = kwargs['location'] + if "location" in kwargs: + location = kwargs["location"] params = { - 'Action': 'DescribeImages', - 'RegionId': location, - 'ImageId': kwargs['image'] + "Action": "DescribeImages", + "RegionId": location, + "ImageId": kwargs["image"], } ret = {} @@ -975,24 +954,21 @@ def show_image(kwargs, call=None): # DescribeImages so far support input multi-image. And # if not found certain image, the response will include # blank image list other than 'not found' error message - if 'Code' in items or not items['Images']['Image']: - raise SaltCloudNotFound('The specified image could not be found.') + if "Code" in items or not items["Images"]["Image"]: + raise SaltCloudNotFound("The specified image could not be found.") - log.debug( - 'Total %s image found in Region %s', - items['TotalCount'], location - ) + log.debug("Total %s image found in Region %s", items["TotalCount"], location) - for image in items['Images']['Image']: - ret[image['ImageId']] = {} + for image in items["Images"]["Image"]: + ret[image["ImageId"]] = {} for item in image: - ret[image['ImageId']][item] = six.text_type(image[item]) + ret[image["ImageId"]][item] = six.text_type(image[item]) return ret def destroy(name, call=None): - ''' + """ Destroy a node. CLI Example: @@ -1001,45 +977,38 @@ def destroy(name, call=None): salt-cloud -a destroy myinstance salt-cloud -d myinstance - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - instanceId = _get_node(name)['InstanceId'] + instanceId = _get_node(name)["InstanceId"] # have to stop instance before del it - stop_params = { - 'Action': 'StopInstance', - 'InstanceId': instanceId - } + stop_params = {"Action": "StopInstance", "InstanceId": instanceId} query(stop_params) - params = { - 'Action': 'DeleteInstance', - 'InstanceId': instanceId - } + params = {"Action": "DeleteInstance", "InstanceId": instanceId} node = query(params) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return node diff --git a/salt/cloud/clouds/azurearm.py b/salt/cloud/clouds/azurearm.py index 047fdac0a94..b3d3fbe9abb 100644 --- a/salt/cloud/clouds/azurearm.py +++ b/salt/cloud/clouds/azurearm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure ARM Cloud Module ====================== @@ -84,13 +84,12 @@ Example ``/etc/salt/cloud.providers`` or *Note: review the details of Service Principals. Owner role is more than you normally need, and you can restrict scope to a resource group or individual resources. -''' +""" # pylint: disable=wrong-import-position,wrong-import-order from __future__ import absolute_import, print_function, unicode_literals -from multiprocessing import cpu_count -from multiprocessing.pool import ThreadPool + import importlib import logging import os @@ -98,9 +97,9 @@ import os.path import pprint import string import time +from multiprocessing import cpu_count +from multiprocessing.pool import ThreadPool -# Salt libs -from salt.ext import six import salt.cache import salt.config as config import salt.loader @@ -111,11 +110,14 @@ import salt.utils.yaml import salt.version from salt.exceptions import ( SaltCloudConfigError, - SaltCloudSystemExit, SaltCloudExecutionFailure, SaltCloudExecutionTimeout, + SaltCloudSystemExit, ) +# Salt libs +from salt.ext import six + # Import 3rd-party libs HAS_LIBS = False try: @@ -123,80 +125,76 @@ try: import azure.mgmt.network.models as network_models from azure.storage.blob.blockblobservice import BlockBlobService from msrestazure.azure_exceptions import CloudError + HAS_LIBS = True except ImportError: pass -__virtualname__ = 'azurearm' +__virtualname__ = "azurearm" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Check for Azure configurations. - ''' + """ if get_configured_provider() is False: return False if get_dependencies() is False: return ( False, - 'The following dependencies are required to use the AzureARM driver: ' - 'Microsoft Azure SDK for Python >= 2.0rc6, ' - 'Microsoft Azure Storage SDK for Python >= 0.32, ' - 'MS REST Azure (msrestazure) >= 0.4' + "The following dependencies are required to use the AzureARM driver: " + "Microsoft Azure SDK for Python >= 2.0rc6, " + "Microsoft Azure Storage SDK for Python >= 0.32, " + "MS REST Azure (msrestazure) >= 0.4", ) return __virtualname__ def get_api_versions(call=None, kwargs=None): # pylint: disable=unused-argument - ''' + """ Get a resource type api versions - ''' + """ if kwargs is None: kwargs = {} - if 'resource_provider' not in kwargs: - raise SaltCloudSystemExit( - 'A resource_provider must be specified' - ) + if "resource_provider" not in kwargs: + raise SaltCloudSystemExit("A resource_provider must be specified") - if 'resource_type' not in kwargs: - raise SaltCloudSystemExit( - 'A resource_type must be specified' - ) + if "resource_type" not in kwargs: + raise SaltCloudSystemExit("A resource_type must be specified") api_versions = [] try: - resconn = get_conn(client_type='resource') + resconn = get_conn(client_type="resource") provider_query = resconn.providers.get( - resource_provider_namespace=kwargs['resource_provider'] + resource_provider_namespace=kwargs["resource_provider"] ) for resource in provider_query.resource_types: - if six.text_type(resource.resource_type) == kwargs['resource_type']: + if six.text_type(resource.resource_type) == kwargs["resource_type"]: resource_dict = resource.as_dict() - api_versions = resource_dict['api_versions'] + api_versions = resource_dict["api_versions"] except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', exc.message) + __utils__["azurearm.log_cloud_error"]("resource", exc.message) return api_versions def get_resource_by_id(resource_id, api_version, extract_value=None): - ''' + """ Get an AzureARM resource by id - ''' + """ ret = {} try: - resconn = get_conn(client_type='resource') + resconn = get_conn(client_type="resource") resource_query = resconn.resources.get_by_id( - resource_id=resource_id, - api_version=api_version + resource_id=resource_id, api_version=api_version ) resource_dict = resource_query.as_dict() if extract_value is not None: @@ -204,32 +202,33 @@ def get_resource_by_id(resource_id, api_version, extract_value=None): else: ret = resource_dict except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', exc.message) - ret = {'Error': exc.message} + __utils__["azurearm.log_cloud_error"]("resource", exc.message) + ret = {"Error": exc.message} return ret def get_configured_provider(): - ''' + """ Return the first configured provider instance. - ''' + """ + def __is_provider_configured(opts, provider, required_keys=()): - ''' + """ Check if the provider is configured. - ''' - if ':' in provider: - alias, driver = provider.split(':') - if alias not in opts['providers']: + """ + if ":" in provider: + alias, driver = provider.split(":") + if alias not in opts["providers"]: return False - if driver not in opts['providers'][alias]: + if driver not in opts["providers"][alias]: return False for key in required_keys: - if opts['providers'][alias][driver].get(key, None) is None: + if opts["providers"][alias][driver].get(key, None) is None: return False - return opts['providers'][alias][driver] + return opts["providers"][alias][driver] - for alias, drivers in six.iteritems(opts['providers']): + for alias, drivers in six.iteritems(opts["providers"]): for driver, provider_details in six.iteritems(drivers): if driver != provider: continue @@ -251,186 +250,166 @@ def get_configured_provider(): provider = __is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('subscription_id', 'tenant', 'client_id', 'secret') + ("subscription_id", "tenant", "client_id", "secret"), ) if provider is False: provider = __is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('subscription_id', 'username', 'password') + ("subscription_id", "username", "password"), ) return provider def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'azurearm': HAS_LIBS} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"azurearm": HAS_LIBS}) def get_conn(client_type): - ''' + """ Return a connection object for a client type. - ''' + """ conn_kwargs = {} - conn_kwargs['subscription_id'] = salt.utils.stringutils.to_str( + conn_kwargs["subscription_id"] = salt.utils.stringutils.to_str( config.get_cloud_config_value( - 'subscription_id', - get_configured_provider(), __opts__, search_global=False + "subscription_id", get_configured_provider(), __opts__, search_global=False ) ) cloud_env = config.get_cloud_config_value( - 'cloud_environment', - get_configured_provider(), __opts__, search_global=False + "cloud_environment", get_configured_provider(), __opts__, search_global=False ) if cloud_env is not None: - conn_kwargs['cloud_environment'] = cloud_env + conn_kwargs["cloud_environment"] = cloud_env tenant = config.get_cloud_config_value( - 'tenant', - get_configured_provider(), __opts__, search_global=False + "tenant", get_configured_provider(), __opts__, search_global=False ) if tenant is not None: client_id = config.get_cloud_config_value( - 'client_id', - get_configured_provider(), __opts__, search_global=False + "client_id", get_configured_provider(), __opts__, search_global=False ) secret = config.get_cloud_config_value( - 'secret', - get_configured_provider(), __opts__, search_global=False + "secret", get_configured_provider(), __opts__, search_global=False ) - conn_kwargs.update({'client_id': client_id, 'secret': secret, - 'tenant': tenant}) + conn_kwargs.update({"client_id": client_id, "secret": secret, "tenant": tenant}) else: username = config.get_cloud_config_value( - 'username', - get_configured_provider(), __opts__, search_global=False + "username", get_configured_provider(), __opts__, search_global=False ) password = config.get_cloud_config_value( - 'password', - get_configured_provider(), __opts__, search_global=False + "password", get_configured_provider(), __opts__, search_global=False ) - conn_kwargs.update({'username': username, 'password': password}) + conn_kwargs.update({"username": username, "password": password}) - client = __utils__['azurearm.get_client']( - client_type=client_type, **conn_kwargs - ) + client = __utils__["azurearm.get_client"](client_type=client_type, **conn_kwargs) return client def get_location(call=None, kwargs=None): # pylint: disable=unused-argument - ''' + """ Return the location that is configured for this provider - ''' + """ if not kwargs: kwargs = {} vm_dict = get_configured_provider() vm_dict.update(kwargs) return config.get_cloud_config_value( - 'location', - vm_dict, __opts__, search_global=False + "location", vm_dict, __opts__, search_global=False ) def avail_locations(call=None): - ''' + """ Return a dict of all available regions. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) ret = {} - ret['locations'] = [] + ret["locations"] = [] try: - resconn = get_conn(client_type='resource') + resconn = get_conn(client_type="resource") provider_query = resconn.providers.get( - resource_provider_namespace='Microsoft.Compute' + resource_provider_namespace="Microsoft.Compute" ) locations = [] for resource in provider_query.resource_types: - if six.text_type(resource.resource_type) == 'virtualMachines': + if six.text_type(resource.resource_type) == "virtualMachines": resource_dict = resource.as_dict() - locations = resource_dict['locations'] + locations = resource_dict["locations"] for location in locations: - lowercase = location.lower().replace(' ', '') - ret['locations'].append(lowercase) + lowercase = location.lower().replace(" ", "") + ret["locations"].append(lowercase) except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', exc.message) - ret = {'Error': exc.message} + __utils__["azurearm.log_cloud_error"]("resource", exc.message) + ret = {"Error": exc.message} return ret def avail_images(call=None): - ''' + """ Return a dict of all available images on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) - compconn = get_conn(client_type='compute') + compconn = get_conn(client_type="compute") region = get_location() publishers = [] ret = {} def _get_publisher_images(publisher): - ''' + """ Get all images from a specific publisher - ''' + """ data = {} try: offers = compconn.virtual_machine_images.list_offers( - location=region, - publisher_name=publisher, + location=region, publisher_name=publisher, ) for offer_obj in offers: offer = offer_obj.as_dict() skus = compconn.virtual_machine_images.list_skus( - location=region, - publisher_name=publisher, - offer=offer['name'], + location=region, publisher_name=publisher, offer=offer["name"], ) for sku_obj in skus: sku = sku_obj.as_dict() results = compconn.virtual_machine_images.list( location=region, publisher_name=publisher, - offer=offer['name'], - skus=sku['name'], + offer=offer["name"], + skus=sku["name"], ) for version_obj in results: version = version_obj.as_dict() - name = '|'.join(( - publisher, - offer['name'], - sku['name'], - version['name'], - )) + name = "|".join( + (publisher, offer["name"], sku["name"], version["name"],) + ) data[name] = { - 'publisher': publisher, - 'offer': offer['name'], - 'sku': sku['name'], - 'version': version['name'], + "publisher": publisher, + "offer": offer["name"], + "sku": sku["name"], + "version": version["name"], } except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', exc.message) + __utils__["azurearm.log_cloud_error"]("compute", exc.message) data = {publisher: exc.message} return data @@ -441,9 +420,9 @@ def avail_images(call=None): ) for publisher_obj in publishers_query: publisher = publisher_obj.as_dict() - publishers.append(publisher['name']) + publishers.append(publisher["name"]) except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', exc.message) + __utils__["azurearm.log_cloud_error"]("compute", exc.message) pool = ThreadPool(cpu_count() * 6) results = pool.map_async(_get_publisher_images, publishers) @@ -455,126 +434,124 @@ def avail_images(call=None): def avail_sizes(call=None): - ''' + """ Return a list of sizes available from the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) - compconn = get_conn(client_type='compute') + compconn = get_conn(client_type="compute") ret = {} location = get_location() try: - sizes = compconn.virtual_machine_sizes.list( - location=location - ) + sizes = compconn.virtual_machine_sizes.list(location=location) for size_obj in sizes: size = size_obj.as_dict() - ret[size['name']] = size + ret[size["name"]] = size except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', exc.message) - ret = {'Error': exc.message} + __utils__["azurearm.log_cloud_error"]("compute", exc.message) + ret = {"Error": exc.message} return ret def list_nodes(call=None): - ''' + """ List VMs on this Azure account - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} nodes = list_nodes_full() for node in nodes: - ret[node] = {'name': node} - for prop in ('id', 'image', 'size', 'state', 'private_ips', 'public_ips'): + ret[node] = {"name": node} + for prop in ("id", "image", "size", "state", "private_ips", "public_ips"): ret[node][prop] = nodes[node].get(prop) return ret def list_nodes_full(call=None): - ''' + """ List all VMs on the subscription with full information - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) - netapi_versions = get_api_versions(kwargs={ - 'resource_provider': 'Microsoft.Network', - 'resource_type': 'networkInterfaces' + netapi_versions = get_api_versions( + kwargs={ + "resource_provider": "Microsoft.Network", + "resource_type": "networkInterfaces", } ) netapi_version = netapi_versions[0] - compconn = get_conn(client_type='compute') + compconn = get_conn(client_type="compute") ret = {} def _get_node_info(node): - ''' + """ Get node info. - ''' + """ node_ret = {} - node['id'] = node['vm_id'] - node['size'] = node['hardware_profile']['vm_size'] - node['state'] = node['provisioning_state'] - node['public_ips'] = [] - node['private_ips'] = [] - node_ret[node['name']] = node + node["id"] = node["vm_id"] + node["size"] = node["hardware_profile"]["vm_size"] + node["state"] = node["provisioning_state"] + node["public_ips"] = [] + node["private_ips"] = [] + node_ret[node["name"]] = node try: - image_ref = node['storage_profile']['image_reference'] - node['image'] = '|'.join([ - image_ref['publisher'], - image_ref['offer'], - image_ref['sku'], - image_ref['version'], - ]) + image_ref = node["storage_profile"]["image_reference"] + node["image"] = "|".join( + [ + image_ref["publisher"], + image_ref["offer"], + image_ref["sku"], + image_ref["version"], + ] + ) except (TypeError, KeyError): try: - node['image'] = node['storage_profile']['os_disk']['image']['uri'] + node["image"] = node["storage_profile"]["os_disk"]["image"]["uri"] except (TypeError, KeyError): - node['image'] = node.get('storage_profile', {}).get('image_reference', {}).get('id') + node["image"] = ( + node.get("storage_profile", {}).get("image_reference", {}).get("id") + ) try: - netifaces = node['network_profile']['network_interfaces'] + netifaces = node["network_profile"]["network_interfaces"] for index, netiface in enumerate(netifaces): netiface_name = get_resource_by_id( - netiface['id'], - netapi_version, - 'name' + netiface["id"], netapi_version, "name" ) netiface, pubips, privips = _get_network_interface( - netiface_name, - node['resource_group'] + netiface_name, node["resource_group"] ) - node['network_profile']['network_interfaces'][index].update(netiface) - node['public_ips'].extend(pubips) - node['private_ips'].extend(privips) + node["network_profile"]["network_interfaces"][index].update(netiface) + node["public_ips"].extend(pubips) + node["private_ips"].extend(privips) except Exception: # pylint: disable=broad-except pass - node_ret[node['name']] = node + node_ret[node["name"]] = node return node_ret for group in list_resource_groups(): nodes = [] - nodes_query = compconn.virtual_machines.list( - resource_group_name=group - ) + nodes_query = compconn.virtual_machines.list(resource_group_name=group) for node_obj in nodes_query: node = node_obj.as_dict() - node['resource_group'] = group + node["resource_group"] = group nodes.append(node) pool = ThreadPool(cpu_count() * 6) @@ -588,246 +565,231 @@ def list_nodes_full(call=None): def list_resource_groups(call=None): - ''' + """ List resource groups associated with the subscription - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_hosted_services function must be called with ' - '-f or --function' + "The list_hosted_services function must be called with " "-f or --function" ) - resconn = get_conn(client_type='resource') + resconn = get_conn(client_type="resource") ret = {} try: groups = resconn.resource_groups.list() for group_obj in groups: group = group_obj.as_dict() - ret[group['name']] = group + ret[group["name"]] = group except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', exc.message) - ret = {'Error': exc.message} + __utils__["azurearm.log_cloud_error"]("resource", exc.message) + ret = {"Error": exc.message} return ret def show_instance(name, call=None): - ''' + """ Show the details from AzureARM concerning an instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) try: - node = list_nodes_full('function')[name] + node = list_nodes_full("function")[name] except KeyError: - log.debug('Failed to get data for node \'%s\'', name) + log.debug("Failed to get data for node '%s'", name) node = {} - __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](node, __active_provider_name__, __opts__) return node def delete_interface(call=None, kwargs=None): # pylint: disable=unused-argument - ''' + """ Delete a network interface. - ''' + """ if kwargs is None: kwargs = {} - netconn = get_conn(client_type='network') + netconn = get_conn(client_type="network") - if kwargs.get('resource_group') is None: - kwargs['resource_group'] = config.get_cloud_config_value( - 'resource_group', {}, __opts__, search_global=True + if kwargs.get("resource_group") is None: + kwargs["resource_group"] = config.get_cloud_config_value( + "resource_group", {}, __opts__, search_global=True ) ips = [] iface = netconn.network_interfaces.get( - kwargs['resource_group'], - kwargs['iface_name'], + kwargs["resource_group"], kwargs["iface_name"], ) iface_name = iface.name for ip_ in iface.ip_configurations: ips.append(ip_.name) poller = netconn.network_interfaces.delete( - kwargs['resource_group'], - kwargs['iface_name'], + kwargs["resource_group"], kwargs["iface_name"], ) poller.wait() for ip_ in ips: - poller = netconn.public_ip_addresses.delete(kwargs['resource_group'], ip_) + poller = netconn.public_ip_addresses.delete(kwargs["resource_group"], ip_) poller.wait() return {iface_name: ips} def _get_public_ip(name, resource_group): - ''' + """ Get the public ip address details by name. - ''' - netconn = get_conn(client_type='network') + """ + netconn = get_conn(client_type="network") try: pubip_query = netconn.public_ip_addresses.get( - resource_group_name=resource_group, - public_ip_address_name=name + resource_group_name=resource_group, public_ip_address_name=name ) pubip = pubip_query.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', exc.message) - pubip = {'error': exc.message} + __utils__["azurearm.log_cloud_error"]("network", exc.message) + pubip = {"error": exc.message} return pubip def _get_network_interface(name, resource_group): - ''' + """ Get a network interface. - ''' + """ public_ips = [] private_ips = [] - netapi_versions = get_api_versions(kwargs={ - 'resource_provider': 'Microsoft.Network', - 'resource_type': 'publicIPAddresses' + netapi_versions = get_api_versions( + kwargs={ + "resource_provider": "Microsoft.Network", + "resource_type": "publicIPAddresses", } ) netapi_version = netapi_versions[0] - netconn = get_conn(client_type='network') + netconn = get_conn(client_type="network") netiface_query = netconn.network_interfaces.get( - resource_group_name=resource_group, - network_interface_name=name + resource_group_name=resource_group, network_interface_name=name ) netiface = netiface_query.as_dict() - for index, ip_config in enumerate(netiface['ip_configurations']): - if ip_config.get('private_ip_address') is not None: - private_ips.append(ip_config['private_ip_address']) - if 'id' in ip_config.get('public_ip_address', {}): + for index, ip_config in enumerate(netiface["ip_configurations"]): + if ip_config.get("private_ip_address") is not None: + private_ips.append(ip_config["private_ip_address"]) + if "id" in ip_config.get("public_ip_address", {}): public_ip_name = get_resource_by_id( - ip_config['public_ip_address']['id'], - netapi_version, - 'name' + ip_config["public_ip_address"]["id"], netapi_version, "name" ) public_ip = _get_public_ip(public_ip_name, resource_group) - public_ips.append(public_ip['ip_address']) - netiface['ip_configurations'][index]['public_ip_address'].update(public_ip) + public_ips.append(public_ip["ip_address"]) + netiface["ip_configurations"][index]["public_ip_address"].update(public_ip) return netiface, public_ips, private_ips def create_network_interface(call=None, kwargs=None): - ''' + """ Create a network interface. - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The create_network_interface action must be called with -a or --action.' + "The create_network_interface action must be called with -a or --action." ) # pylint: disable=invalid-name - IPAllocationMethod = getattr( - network_models, - 'IPAllocationMethod' - ) + IPAllocationMethod = getattr(network_models, "IPAllocationMethod") # pylint: disable=invalid-name - NetworkInterface = getattr( - network_models, - 'NetworkInterface' - ) + NetworkInterface = getattr(network_models, "NetworkInterface") # pylint: disable=invalid-name NetworkInterfaceIPConfiguration = getattr( - network_models, - 'NetworkInterfaceIPConfiguration' + network_models, "NetworkInterfaceIPConfiguration" ) # pylint: disable=invalid-name - PublicIPAddress = getattr( - network_models, - 'PublicIPAddress' - ) + PublicIPAddress = getattr(network_models, "PublicIPAddress") if not isinstance(kwargs, dict): kwargs = {} vm_ = kwargs - netconn = get_conn(client_type='network') + netconn = get_conn(client_type="network") - if kwargs.get('location') is None: - kwargs['location'] = get_location() + if kwargs.get("location") is None: + kwargs["location"] = get_location() - if kwargs.get('network') is None: - kwargs['network'] = config.get_cloud_config_value( - 'network', vm_, __opts__, search_global=False + if kwargs.get("network") is None: + kwargs["network"] = config.get_cloud_config_value( + "network", vm_, __opts__, search_global=False ) - if kwargs.get('subnet') is None: - kwargs['subnet'] = config.get_cloud_config_value( - 'subnet', vm_, __opts__, search_global=False + if kwargs.get("subnet") is None: + kwargs["subnet"] = config.get_cloud_config_value( + "subnet", vm_, __opts__, search_global=False ) - if kwargs.get('network_resource_group') is None: - kwargs['network_resource_group'] = config.get_cloud_config_value( - 'resource_group', vm_, __opts__, search_global=False + if kwargs.get("network_resource_group") is None: + kwargs["network_resource_group"] = config.get_cloud_config_value( + "resource_group", vm_, __opts__, search_global=False ) - if kwargs.get('iface_name') is None: - kwargs['iface_name'] = '{0}-iface0'.format(vm_['name']) + if kwargs.get("iface_name") is None: + kwargs["iface_name"] = "{0}-iface0".format(vm_["name"]) try: subnet_obj = netconn.subnets.get( - resource_group_name=kwargs['network_resource_group'], - virtual_network_name=kwargs['network'], - subnet_name=kwargs['subnet'], + resource_group_name=kwargs["network_resource_group"], + virtual_network_name=kwargs["network"], + subnet_name=kwargs["subnet"], ) except CloudError as exc: raise SaltCloudSystemExit( '{0} (Resource Group: "{1}", VNET: "{2}", Subnet: "{3}")'.format( exc.message, - kwargs['network_resource_group'], - kwargs['network'], - kwargs['subnet'] + kwargs["network_resource_group"], + kwargs["network"], + kwargs["subnet"], ) ) ip_kwargs = {} ip_configurations = None - if 'load_balancer_backend_address_pools' in kwargs: - pool_dicts = kwargs['load_balancer_backend_address_pools'] + if "load_balancer_backend_address_pools" in kwargs: + pool_dicts = kwargs["load_balancer_backend_address_pools"] if isinstance(pool_dicts, dict): pool_ids = [] for load_bal, be_pools in pool_dicts.items(): for pool in be_pools: try: lbbep_data = netconn.load_balancer_backend_address_pools.get( - kwargs['resource_group'], - load_bal, - pool, + kwargs["resource_group"], load_bal, pool, ) - pool_ids.append({'id': lbbep_data.as_dict()['id']}) + pool_ids.append({"id": lbbep_data.as_dict()["id"]}) except CloudError as exc: - log.error('There was a cloud error: %s', six.text_type(exc)) + log.error("There was a cloud error: %s", six.text_type(exc)) except KeyError as exc: - log.error('There was an error getting the Backend Pool ID: %s', six.text_type(exc)) - ip_kwargs['load_balancer_backend_address_pools'] = pool_ids + log.error( + "There was an error getting the Backend Pool ID: %s", + six.text_type(exc), + ) + ip_kwargs["load_balancer_backend_address_pools"] = pool_ids - if 'private_ip_address' in kwargs.keys(): - ip_kwargs['private_ip_address'] = kwargs['private_ip_address'] - ip_kwargs['private_ip_allocation_method'] = IPAllocationMethod.static + if "private_ip_address" in kwargs.keys(): + ip_kwargs["private_ip_address"] = kwargs["private_ip_address"] + ip_kwargs["private_ip_allocation_method"] = IPAllocationMethod.static else: - ip_kwargs['private_ip_allocation_method'] = IPAllocationMethod.dynamic + ip_kwargs["private_ip_allocation_method"] = IPAllocationMethod.dynamic - if kwargs.get('allocate_public_ip') is True: - pub_ip_name = '{0}-ip'.format(kwargs['iface_name']) + if kwargs.get("allocate_public_ip") is True: + pub_ip_name = "{0}-ip".format(kwargs["iface_name"]) poller = netconn.public_ip_addresses.create_or_update( - resource_group_name=kwargs['resource_group'], + resource_group_name=kwargs["resource_group"], public_ip_address_name=pub_ip_name, parameters=PublicIPAddress( - location=kwargs['location'], + location=kwargs["location"], public_ip_allocation_method=IPAllocationMethod.static, ), ) @@ -836,241 +798,208 @@ def create_network_interface(call=None, kwargs=None): while True: try: pub_ip_data = netconn.public_ip_addresses.get( - kwargs['resource_group'], - pub_ip_name, + kwargs["resource_group"], pub_ip_name, ) if pub_ip_data.ip_address: # pylint: disable=no-member - ip_kwargs['public_ip_address'] = PublicIPAddress( + ip_kwargs["public_ip_address"] = PublicIPAddress( id=six.text_type(pub_ip_data.id), # pylint: disable=no-member ) ip_configurations = [ NetworkInterfaceIPConfiguration( - name='{0}-ip'.format(kwargs['iface_name']), + name="{0}-ip".format(kwargs["iface_name"]), subnet=subnet_obj, **ip_kwargs ) ] break except CloudError as exc: - log.error('There was a cloud error: %s', exc) + log.error("There was a cloud error: %s", exc) count += 1 if count > 120: - raise ValueError('Timed out waiting for public IP Address.') + raise ValueError("Timed out waiting for public IP Address.") time.sleep(5) else: - priv_ip_name = '{0}-ip'.format(kwargs['iface_name']) + priv_ip_name = "{0}-ip".format(kwargs["iface_name"]) ip_configurations = [ NetworkInterfaceIPConfiguration( - name=priv_ip_name, - subnet=subnet_obj, - **ip_kwargs + name=priv_ip_name, subnet=subnet_obj, **ip_kwargs ) ] network_security_group = None - if kwargs.get('security_group') is not None: + if kwargs.get("security_group") is not None: network_security_group = netconn.network_security_groups.get( - resource_group_name=kwargs['resource_group'], - network_security_group_name=kwargs['security_group'], + resource_group_name=kwargs["resource_group"], + network_security_group_name=kwargs["security_group"], ) iface_params = NetworkInterface( - location=kwargs['location'], + location=kwargs["location"], network_security_group=network_security_group, ip_configurations=ip_configurations, ) poller = netconn.network_interfaces.create_or_update( - kwargs['resource_group'], kwargs['iface_name'], iface_params + kwargs["resource_group"], kwargs["iface_name"], iface_params ) try: poller.wait() except Exception as exc: # pylint: disable=broad-except - log.warning('Network interface creation could not be polled. ' - 'It is likely that we are reusing an existing interface. (%s)', exc) + log.warning( + "Network interface creation could not be polled. " + "It is likely that we are reusing an existing interface. (%s)", + exc, + ) count = 0 while True: try: - return _get_network_interface(kwargs['iface_name'], kwargs['resource_group']) + return _get_network_interface( + kwargs["iface_name"], kwargs["resource_group"] + ) except CloudError: count += 1 if count > 120: - raise ValueError('Timed out waiting for operation to complete.') + raise ValueError("Timed out waiting for operation to complete.") time.sleep(5) def request_instance(vm_): - ''' + """ Request a VM from Azure. - ''' - compconn = get_conn(client_type='compute') + """ + compconn = get_conn(client_type="compute") # pylint: disable=invalid-name - CachingTypes = getattr( - compute_models, 'CachingTypes' - ) + CachingTypes = getattr(compute_models, "CachingTypes") # pylint: disable=invalid-name - DataDisk = getattr( - compute_models, 'DataDisk' - ) + DataDisk = getattr(compute_models, "DataDisk") # pylint: disable=invalid-name - DiskCreateOptionTypes = getattr( - compute_models, 'DiskCreateOptionTypes' - ) + DiskCreateOptionTypes = getattr(compute_models, "DiskCreateOptionTypes") # pylint: disable=invalid-name - HardwareProfile = getattr( - compute_models, 'HardwareProfile' - ) + HardwareProfile = getattr(compute_models, "HardwareProfile") # pylint: disable=invalid-name - ImageReference = getattr( - compute_models, 'ImageReference' - ) + ImageReference = getattr(compute_models, "ImageReference") # pylint: disable=invalid-name - LinuxConfiguration = getattr( - compute_models, 'LinuxConfiguration' - ) + LinuxConfiguration = getattr(compute_models, "LinuxConfiguration") # pylint: disable=invalid-name - SshConfiguration = getattr( - compute_models, 'SshConfiguration' - ) + SshConfiguration = getattr(compute_models, "SshConfiguration") # pylint: disable=invalid-name - SshPublicKey = getattr( - compute_models, 'SshPublicKey' - ) + SshPublicKey = getattr(compute_models, "SshPublicKey") # pylint: disable=invalid-name - NetworkInterfaceReference = getattr( - compute_models, 'NetworkInterfaceReference' - ) + NetworkInterfaceReference = getattr(compute_models, "NetworkInterfaceReference") # pylint: disable=invalid-name - NetworkProfile = getattr( - compute_models, 'NetworkProfile' - ) + NetworkProfile = getattr(compute_models, "NetworkProfile") # pylint: disable=invalid-name - OSDisk = getattr( - compute_models, 'OSDisk' - ) + OSDisk = getattr(compute_models, "OSDisk") # pylint: disable=invalid-name - OSProfile = getattr( - compute_models, 'OSProfile' - ) + OSProfile = getattr(compute_models, "OSProfile") # pylint: disable=invalid-name - StorageProfile = getattr( - compute_models, 'StorageProfile' - ) + StorageProfile = getattr(compute_models, "StorageProfile") # pylint: disable=invalid-name - VirtualHardDisk = getattr( - compute_models, 'VirtualHardDisk' - ) + VirtualHardDisk = getattr(compute_models, "VirtualHardDisk") # pylint: disable=invalid-name - VirtualMachine = getattr( - compute_models, 'VirtualMachine' - ) + VirtualMachine = getattr(compute_models, "VirtualMachine") # pylint: disable=invalid-name - VirtualMachineSizeTypes = getattr( - compute_models, 'VirtualMachineSizeTypes' - ) + VirtualMachineSizeTypes = getattr(compute_models, "VirtualMachineSizeTypes") subscription_id = config.get_cloud_config_value( - 'subscription_id', - get_configured_provider(), __opts__, search_global=False + "subscription_id", get_configured_provider(), __opts__, search_global=False ) - if vm_.get('driver') is None: - vm_['driver'] = 'azurearm' + if vm_.get("driver") is None: + vm_["driver"] = "azurearm" - if vm_.get('location') is None: - vm_['location'] = get_location() + if vm_.get("location") is None: + vm_["location"] = get_location() - if vm_.get('resource_group') is None: - vm_['resource_group'] = config.get_cloud_config_value( - 'resource_group', vm_, __opts__, search_global=True + if vm_.get("resource_group") is None: + vm_["resource_group"] = config.get_cloud_config_value( + "resource_group", vm_, __opts__, search_global=True ) - if vm_.get('name') is None: - vm_['name'] = config.get_cloud_config_value( - 'name', vm_, __opts__, search_global=True + if vm_.get("name") is None: + vm_["name"] = config.get_cloud_config_value( + "name", vm_, __opts__, search_global=True ) # pylint: disable=unused-variable iface_data, public_ips, private_ips = create_network_interface( - call='action', - kwargs=vm_ + call="action", kwargs=vm_ ) - vm_['iface_id'] = iface_data['id'] + vm_["iface_id"] = iface_data["id"] - disk_name = '{0}-vol0'.format(vm_['name']) + disk_name = "{0}-vol0".format(vm_["name"]) vm_username = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, search_global=True, + "ssh_username", + vm_, + __opts__, + search_global=True, default=config.get_cloud_config_value( - 'win_username', vm_, __opts__, search_global=True - ) + "win_username", vm_, __opts__, search_global=True + ), ) ssh_publickeyfile_contents = None ssh_publickeyfile = config.get_cloud_config_value( - 'ssh_publickeyfile', - vm_, - __opts__, - search_global=False, - default=None + "ssh_publickeyfile", vm_, __opts__, search_global=False, default=None ) if ssh_publickeyfile is not None: try: - with salt.utils.files.fopen(ssh_publickeyfile, 'r') as spkc_: + with salt.utils.files.fopen(ssh_publickeyfile, "r") as spkc_: ssh_publickeyfile_contents = spkc_.read() except Exception as exc: # pylint: disable=broad-except raise SaltCloudConfigError( "Failed to read ssh publickey file '{0}': " - "{1}".format(ssh_publickeyfile, - exc.args[-1]) + "{1}".format(ssh_publickeyfile, exc.args[-1]) ) disable_password_authentication = config.get_cloud_config_value( - 'disable_password_authentication', + "disable_password_authentication", vm_, __opts__, search_global=False, - default=False + default=False, ) os_kwargs = {} win_installer = config.get_cloud_config_value( - 'win_installer', vm_, __opts__, search_global=True + "win_installer", vm_, __opts__, search_global=True ) if not win_installer and ssh_publickeyfile_contents is not None: sshpublickey = SshPublicKey( key_data=ssh_publickeyfile_contents, - path='/home/{0}/.ssh/authorized_keys'.format(vm_username), - ) - sshconfiguration = SshConfiguration( - public_keys=[sshpublickey], + path="/home/{0}/.ssh/authorized_keys".format(vm_username), ) + sshconfiguration = SshConfiguration(public_keys=[sshpublickey],) linuxconfiguration = LinuxConfiguration( disable_password_authentication=disable_password_authentication, ssh=sshconfiguration, ) - os_kwargs['linux_configuration'] = linuxconfiguration + os_kwargs["linux_configuration"] = linuxconfiguration vm_password = None else: vm_password = salt.utils.stringutils.to_str( config.get_cloud_config_value( - 'ssh_password', vm_, __opts__, search_global=True, + "ssh_password", + vm_, + __opts__, + search_global=True, default=config.get_cloud_config_value( - 'win_password', vm_, __opts__, search_global=True - ) + "win_password", vm_, __opts__, search_global=True + ), ) ) - if win_installer or (vm_password is not None and not disable_password_authentication): + if win_installer or ( + vm_password is not None and not disable_password_authentication + ): if not isinstance(vm_password, str): - raise SaltCloudSystemExit( - 'The admin password must be a string.' - ) + raise SaltCloudSystemExit("The admin password must be a string.") if len(vm_password) < 8 or len(vm_password) > 123: raise SaltCloudSystemExit( - 'The admin password must be between 8-123 characters long.' + "The admin password must be between 8-123 characters long." ) complexity = 0 if any(char.isdigit() for char in vm_password): @@ -1083,24 +1012,18 @@ def request_instance(vm_): complexity += 1 if complexity < 3: raise SaltCloudSystemExit( - 'The admin password must contain at least 3 of the following types: ' - 'upper, lower, digits, special characters' + "The admin password must contain at least 3 of the following types: " + "upper, lower, digits, special characters" ) - os_kwargs['admin_password'] = vm_password + os_kwargs["admin_password"] = vm_password availability_set = config.get_cloud_config_value( - 'availability_set', - vm_, - __opts__, - search_global=False, - default=None + "availability_set", vm_, __opts__, search_global=False, default=None ) if availability_set is not None and isinstance(availability_set, six.string_types): availability_set = { - 'id': '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/availabilitySets/{2}'.format( - subscription_id, - vm_['resource_group'], - availability_set + "id": "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Compute/availabilitySets/{2}".format( + subscription_id, vm_["resource_group"], availability_set ) } else: @@ -1110,10 +1033,10 @@ def request_instance(vm_): storage_endpoint_suffix = cloud_env.suffixes.storage_endpoint - if isinstance(vm_.get('volumes'), six.string_types): - volumes = salt.utils.yaml.safe_load(vm_['volumes']) + if isinstance(vm_.get("volumes"), six.string_types): + volumes = salt.utils.yaml.safe_load(vm_["volumes"]) else: - volumes = vm_.get('volumes') + volumes = vm_.get("volumes") data_disks = None if isinstance(volumes, list): @@ -1125,147 +1048,129 @@ def request_instance(vm_): luns = [] for volume in volumes: if isinstance(volume, six.string_types): - volume = {'name': volume} + volume = {"name": volume} volume.setdefault( - 'name', + "name", volume.get( - 'name', + "name", volume.get( - 'name', - '{0}-datadisk{1}'.format(vm_['name'], six.text_type(lun)) - ) - ) + "name", "{0}-datadisk{1}".format(vm_["name"], six.text_type(lun)) + ), + ), ) volume.setdefault( - 'disk_size_gb', - volume.get( - 'logical_disk_size_in_gb', - volume.get('size', 100) - ) + "disk_size_gb", + volume.get("logical_disk_size_in_gb", volume.get("size", 100)), ) # Old kwarg was host_caching, new name is caching - volume.setdefault('caching', volume.get('host_caching', 'ReadOnly')) + volume.setdefault("caching", volume.get("host_caching", "ReadOnly")) while lun in luns: lun += 1 if lun > 15: - log.error('Maximum lun count has been reached') + log.error("Maximum lun count has been reached") break - volume.setdefault('lun', lun) + volume.setdefault("lun", lun) lun += 1 # The default vhd is {vm_name}-datadisk{lun}.vhd - if 'media_link' in volume: - volume['vhd'] = VirtualHardDisk(volume['media_link']) - del volume['media_link'] - elif volume.get('vhd') == 'unmanaged': - volume['vhd'] = VirtualHardDisk( - 'https://{0}.blob.{1}/vhds/{2}-datadisk{3}.vhd'.format( - vm_['storage_account'], + if "media_link" in volume: + volume["vhd"] = VirtualHardDisk(volume["media_link"]) + del volume["media_link"] + elif volume.get("vhd") == "unmanaged": + volume["vhd"] = VirtualHardDisk( + "https://{0}.blob.{1}/vhds/{2}-datadisk{3}.vhd".format( + vm_["storage_account"], storage_endpoint_suffix, - vm_['name'], - volume['lun'], + vm_["name"], + volume["lun"], ), ) - elif 'vhd' in volume: - volume['vhd'] = VirtualHardDisk(volume['vhd']) + elif "vhd" in volume: + volume["vhd"] = VirtualHardDisk(volume["vhd"]) - if 'image' in volume: - volume['create_option'] = 'from_image' - elif 'attach' in volume: - volume['create_option'] = 'attach' + if "image" in volume: + volume["create_option"] = "from_image" + elif "attach" in volume: + volume["create_option"] = "attach" else: - volume['create_option'] = 'empty' + volume["create_option"] = "empty" data_disks.append(DataDisk(**volume)) img_ref = None - if vm_['image'].startswith('http') or vm_.get('vhd') == 'unmanaged': - if vm_['image'].startswith('http'): - source_image = VirtualHardDisk(vm_['image']) + if vm_["image"].startswith("http") or vm_.get("vhd") == "unmanaged": + if vm_["image"].startswith("http"): + source_image = VirtualHardDisk(vm_["image"]) else: source_image = None - if '|' in vm_['image']: - img_pub, img_off, img_sku, img_ver = vm_['image'].split('|') + if "|" in vm_["image"]: + img_pub, img_off, img_sku, img_ver = vm_["image"].split("|") img_ref = ImageReference( - publisher=img_pub, - offer=img_off, - sku=img_sku, - version=img_ver, + publisher=img_pub, offer=img_off, sku=img_sku, version=img_ver, ) - elif vm_['image'].startswith('/subscriptions'): - img_ref = ImageReference(id=vm_['image']) + elif vm_["image"].startswith("/subscriptions"): + img_ref = ImageReference(id=vm_["image"]) if win_installer: - os_type = 'Windows' + os_type = "Windows" else: - os_type = 'Linux' + os_type = "Linux" os_disk = OSDisk( caching=CachingTypes.none, create_option=DiskCreateOptionTypes.from_image, name=disk_name, vhd=VirtualHardDisk( - 'https://{0}.blob.{1}/vhds/{2}.vhd'.format( - vm_['storage_account'], - storage_endpoint_suffix, - disk_name, + "https://{0}.blob.{1}/vhds/{2}.vhd".format( + vm_["storage_account"], storage_endpoint_suffix, disk_name, ), ), os_type=os_type, image=source_image, - disk_size_gb=vm_.get('os_disk_size_gb') + disk_size_gb=vm_.get("os_disk_size_gb"), ) else: source_image = None os_type = None os_disk = OSDisk( create_option=DiskCreateOptionTypes.from_image, - disk_size_gb=vm_.get('os_disk_size_gb') + disk_size_gb=vm_.get("os_disk_size_gb"), ) - if '|' in vm_['image']: - img_pub, img_off, img_sku, img_ver = vm_['image'].split('|') + if "|" in vm_["image"]: + img_pub, img_off, img_sku, img_ver = vm_["image"].split("|") img_ref = ImageReference( - publisher=img_pub, - offer=img_off, - sku=img_sku, - version=img_ver, + publisher=img_pub, offer=img_off, sku=img_sku, version=img_ver, ) - elif vm_['image'].startswith('/subscriptions'): - img_ref = ImageReference(id=vm_['image']) + elif vm_["image"].startswith("/subscriptions"): + img_ref = ImageReference(id=vm_["image"]) userdata_file = config.get_cloud_config_value( - 'userdata_file', vm_, __opts__, search_global=False, default=None + "userdata_file", vm_, __opts__, search_global=False, default=None ) userdata = config.get_cloud_config_value( - 'userdata', vm_, __opts__, search_global=False, default=None + "userdata", vm_, __opts__, search_global=False, default=None ) userdata_template = config.get_cloud_config_value( - 'userdata_template', vm_, __opts__, search_global=False, default=None + "userdata_template", vm_, __opts__, search_global=False, default=None ) if userdata_file: if os.path.exists(userdata_file): - with salt.utils.files.fopen(userdata_file, 'r') as fh_: + with salt.utils.files.fopen(userdata_file, "r") as fh_: userdata = fh_.read() if userdata and userdata_template: userdata_sendkeys = config.get_cloud_config_value( - 'userdata_sendkeys', vm_, __opts__, search_global=False, default=None + "userdata_sendkeys", vm_, __opts__, search_global=False, default=None ) if userdata_sendkeys: - vm_['priv_key'], vm_['pub_key'] = salt.utils.cloud.gen_keys( - config.get_cloud_config_value( - 'keysize', - vm_, - __opts__ - ) + vm_["priv_key"], vm_["pub_key"] = salt.utils.cloud.gen_keys( + config.get_cloud_config_value("keysize", vm_, __opts__) ) - key_id = vm_.get('name') - if 'append_domain' in vm_: - key_id = '.'.join([key_id, vm_['append_domain']]) + key_id = vm_.get("name") + if "append_domain" in vm_: + key_id = ".".join([key_id, vm_["append_domain"]]) - salt.utils.cloud.accept_key( - __opts__['pki_dir'], vm_['pub_key'], key_id - ) + salt.utils.cloud.accept_key(__opts__["pki_dir"], vm_["pub_key"], key_id) userdata = salt.utils.cloud.userdata_template(__opts__, vm_, userdata) @@ -1273,85 +1178,79 @@ def request_instance(vm_): if userdata is not None or userdata_file is not None: try: if win_installer: - publisher = 'Microsoft.Compute' - virtual_machine_extension_type = 'CustomScriptExtension' - type_handler_version = '1.8' - if userdata_file and userdata_file.endswith('.ps1'): - command_prefix = 'powershell -ExecutionPolicy Unrestricted -File ' + publisher = "Microsoft.Compute" + virtual_machine_extension_type = "CustomScriptExtension" + type_handler_version = "1.8" + if userdata_file and userdata_file.endswith(".ps1"): + command_prefix = "powershell -ExecutionPolicy Unrestricted -File " else: - command_prefix = '' + command_prefix = "" else: - publisher = 'Microsoft.Azure.Extensions' - virtual_machine_extension_type = 'CustomScript' - type_handler_version = '2.0' - command_prefix = '' + publisher = "Microsoft.Azure.Extensions" + virtual_machine_extension_type = "CustomScript" + type_handler_version = "2.0" + command_prefix = "" settings = {} if userdata: - settings['commandToExecute'] = userdata - elif userdata_file.startswith('http'): - settings['fileUris'] = [userdata_file] - settings['commandToExecute'] = command_prefix + './' + userdata_file[userdata_file.rfind('/')+1:] + settings["commandToExecute"] = userdata + elif userdata_file.startswith("http"): + settings["fileUris"] = [userdata_file] + settings["commandToExecute"] = ( + command_prefix + + "./" + + userdata_file[userdata_file.rfind("/") + 1 :] + ) custom_extension = { - 'resource_group': vm_['resource_group'], - 'virtual_machine_name': vm_['name'], - 'extension_name': vm_['name'] + '_custom_userdata_script', - 'location': vm_['location'], - 'publisher': publisher, - 'virtual_machine_extension_type': virtual_machine_extension_type, - 'type_handler_version': type_handler_version, - 'auto_upgrade_minor_version': True, - 'settings': settings, - 'protected_settings': None + "resource_group": vm_["resource_group"], + "virtual_machine_name": vm_["name"], + "extension_name": vm_["name"] + "_custom_userdata_script", + "location": vm_["location"], + "publisher": publisher, + "virtual_machine_extension_type": virtual_machine_extension_type, + "type_handler_version": type_handler_version, + "auto_upgrade_minor_version": True, + "settings": settings, + "protected_settings": None, } except Exception as exc: # pylint: disable=broad-except - log.exception('Failed to encode userdata: %s', exc) + log.exception("Failed to encode userdata: %s", exc) params = VirtualMachine( - location=vm_['location'], + location=vm_["location"], plan=None, hardware_profile=HardwareProfile( - vm_size=getattr( - VirtualMachineSizeTypes, vm_['size'].lower() - ), + vm_size=getattr(VirtualMachineSizeTypes, vm_["size"].lower()), ), storage_profile=StorageProfile( - os_disk=os_disk, - data_disks=data_disks, - image_reference=img_ref, + os_disk=os_disk, data_disks=data_disks, image_reference=img_ref, ), os_profile=OSProfile( - admin_username=vm_username, - computer_name=vm_['name'], - **os_kwargs + admin_username=vm_username, computer_name=vm_["name"], **os_kwargs ), network_profile=NetworkProfile( - network_interfaces=[ - NetworkInterfaceReference(id=vm_['iface_id']), - ], + network_interfaces=[NetworkInterfaceReference(id=vm_["iface_id"])], ), availability_set=availability_set, ) - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']( - 'requesting', - vm_, - ['name', 'profile', 'provider', 'driver'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "requesting", vm_, ["name", "profile", "provider", "driver"] ), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: vm_create = compconn.virtual_machines.create_or_update( - resource_group_name=vm_['resource_group'], - vm_name=vm_['name'], - parameters=params + resource_group_name=vm_["resource_group"], + vm_name=vm_["name"], + parameters=params, ) vm_create.wait() vm_result = vm_create.result() @@ -1359,67 +1258,73 @@ def request_instance(vm_): if custom_extension: create_or_update_vmextension(kwargs=custom_extension) except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', exc.message) + __utils__["azurearm.log_cloud_error"]("compute", exc.message) vm_result = {} return vm_result def create(vm_): - ''' + """ Create a single VM from a data dict. - ''' + """ try: - if vm_['profile'] and config.is_profile_configured( - __opts__, - __active_provider_name__ or 'azurearm', - vm_['profile'], - vm_=vm_ - ) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or "azurearm", + vm_["profile"], + vm_=vm_, + ) + is False + ): return False except AttributeError: pass - if vm_.get('bootstrap_interface') is None: - vm_['bootstrap_interface'] = 'public' + if vm_.get("bootstrap_interface") is None: + vm_["bootstrap_interface"] = "public" - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']( - 'creating', vm_, ['name', 'profile', 'provider', 'driver'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] ), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - __utils__['cloud.cachedir_index_add']( - vm_['name'], vm_['profile'], 'azurearm', vm_['driver'] + __utils__["cloud.cachedir_index_add"]( + vm_["name"], vm_["profile"], "azurearm", vm_["driver"] ) - if not vm_.get('location'): - vm_['location'] = get_location(kwargs=vm_) + if not vm_.get("location"): + vm_["location"] = get_location(kwargs=vm_) - log.info('Creating Cloud VM %s in %s', vm_['name'], vm_['location']) + log.info("Creating Cloud VM %s in %s", vm_["name"], vm_["location"]) vm_request = request_instance(vm_=vm_) - if not vm_request or 'error' in vm_request: - err_message = 'Error creating VM {0}! ({1})'.format(vm_['name'], six.text_type(vm_request)) + if not vm_request or "error" in vm_request: + err_message = "Error creating VM {0}! ({1})".format( + vm_["name"], six.text_type(vm_request) + ) log.error(err_message) raise SaltCloudSystemExit(err_message) def _query_node_data(name, bootstrap_interface): - ''' + """ Query node data. - ''' - data = show_instance(name, call='action') + """ + data = show_instance(name, call="action") if not data: return False ip_address = None - if bootstrap_interface == 'public': - ip_address = data['public_ips'][0] - if bootstrap_interface == 'private': - ip_address = data['private_ips'][0] + if bootstrap_interface == "public": + ip_address = data["public_ips"][0] + if bootstrap_interface == "private": + ip_address = data["private_ips"][0] if ip_address is None: return False return ip_address @@ -1427,61 +1332,57 @@ def create(vm_): try: data = salt.utils.cloud.wait_for_ip( _query_node_data, - update_args=(vm_['name'], vm_['bootstrap_interface'],), + update_args=(vm_["name"], vm_["bootstrap_interface"],), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10), + "wait_for_ip_interval", vm_, __opts__, default=10 + ), interval_multiplier=config.get_cloud_config_value( - 'wait_for_ip_interval_multiplier', vm_, __opts__, default=1), + "wait_for_ip_interval_multiplier", vm_, __opts__, default=1 + ), ) except ( SaltCloudExecutionTimeout, SaltCloudExecutionFailure, - SaltCloudSystemExit + SaltCloudSystemExit, ) as exc: try: log.warning(exc) finally: raise SaltCloudSystemExit(six.text_type(exc)) - vm_['ssh_host'] = data - if not vm_.get('ssh_username'): - vm_['ssh_username'] = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__ + vm_["ssh_host"] = data + if not vm_.get("ssh_username"): + vm_["ssh_username"] = config.get_cloud_config_value( + "ssh_username", vm_, __opts__ ) - vm_['password'] = config.get_cloud_config_value( - 'ssh_password', vm_, __opts__ - ) - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + vm_["password"] = config.get_cloud_config_value("ssh_password", vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) - data = show_instance(vm_['name'], call='action') - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], - pprint.pformat(data) - ) + data = show_instance(vm_["name"], call="action") + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) ret.update(data) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']( - 'created', - vm_, ['name', 'profile', 'provider', 'driver'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] ), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def destroy(name, call=None, kwargs=None): # pylint: disable=unused-argument - ''' + """ Destroy a VM. CLI Examples: @@ -1490,38 +1391,35 @@ def destroy(name, call=None, kwargs=None): # pylint: disable=unused-argument salt-cloud -d myminion salt-cloud -a destroy myminion service_name=myservice - ''' + """ if kwargs is None: kwargs = {} - if call == 'function': + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - compconn = get_conn(client_type='compute') + compconn = get_conn(client_type="compute") - node_data = show_instance(name, call='action') - if node_data['storage_profile']['os_disk'].get('managed_disk'): - vhd = node_data['storage_profile']['os_disk']['managed_disk']['id'] + node_data = show_instance(name, call="action") + if node_data["storage_profile"]["os_disk"].get("managed_disk"): + vhd = node_data["storage_profile"]["os_disk"]["managed_disk"]["id"] else: - vhd = node_data['storage_profile']['os_disk']['vhd']['uri'] + vhd = node_data["storage_profile"]["os_disk"]["vhd"]["uri"] ret = {name: {}} - log.debug('Deleting VM') - result = compconn.virtual_machines.delete(node_data['resource_group'], name) + log.debug("Deleting VM") + result = compconn.virtual_machines.delete(node_data["resource_group"], name) result.wait() - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir']( - name, - __active_provider_name__.split(':')[0], - __opts__ + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ ) cleanup_disks = config.get_cloud_config_value( - 'cleanup_disks', + "cleanup_disks", get_configured_provider(), __opts__, search_global=False, @@ -1530,109 +1428,113 @@ def destroy(name, call=None, kwargs=None): # pylint: disable=unused-argument if cleanup_disks: cleanup_vhds = kwargs.get( - 'delete_vhd', + "delete_vhd", config.get_cloud_config_value( - 'cleanup_vhds', + "cleanup_vhds", get_configured_provider(), __opts__, search_global=False, - default=False - ) + default=False, + ), ) if cleanup_vhds: - log.debug('Deleting vhd') + log.debug("Deleting vhd") - comps = vhd.split('/') + comps = vhd.split("/") container = comps[-2] blob = comps[-1] - ret[name]['delete_disk'] = { - 'delete_disks': cleanup_disks, - 'delete_vhd': cleanup_vhds, - 'container': container, - 'blob': blob, + ret[name]["delete_disk"] = { + "delete_disks": cleanup_disks, + "delete_vhd": cleanup_vhds, + "container": container, + "blob": blob, } - if vhd.startswith('http'): - ret[name]['data'] = delete_blob( - kwargs={'container': container, 'blob': blob}, - call='function' + if vhd.startswith("http"): + ret[name]["data"] = delete_blob( + kwargs={"container": container, "blob": blob}, call="function" ) else: - ret[name]['data'] = delete_managed_disk( - kwargs={'resource_group': node_data['resource_group'], - 'container': container, - 'blob': blob}, - call='function' + ret[name]["data"] = delete_managed_disk( + kwargs={ + "resource_group": node_data["resource_group"], + "container": container, + "blob": blob, + }, + call="function", ) cleanup_data_disks = kwargs.get( - 'delete_data_disks', + "delete_data_disks", config.get_cloud_config_value( - 'cleanup_data_disks', + "cleanup_data_disks", get_configured_provider(), __opts__, search_global=False, - default=False - ) + default=False, + ), ) if cleanup_data_disks: - log.debug('Deleting data_disks') - ret[name]['data_disks'] = {} + log.debug("Deleting data_disks") + ret[name]["data_disks"] = {} - for disk in node_data['storage_profile']['data_disks']: - datavhd = disk.get('managed_disk', {}).get('id') or disk.get('vhd', {}).get('uri') - comps = datavhd.split('/') + for disk in node_data["storage_profile"]["data_disks"]: + datavhd = disk.get("managed_disk", {}).get("id") or disk.get( + "vhd", {} + ).get("uri") + comps = datavhd.split("/") container = comps[-2] blob = comps[-1] - ret[name]['data_disks'][disk['name']] = { - 'delete_disks': cleanup_disks, - 'delete_vhd': cleanup_vhds, - 'container': container, - 'blob': blob, + ret[name]["data_disks"][disk["name"]] = { + "delete_disks": cleanup_disks, + "delete_vhd": cleanup_vhds, + "container": container, + "blob": blob, } - if datavhd.startswith('http'): - ret[name]['data'] = delete_blob( - kwargs={'container': container, 'blob': blob}, - call='function' + if datavhd.startswith("http"): + ret[name]["data"] = delete_blob( + kwargs={"container": container, "blob": blob}, call="function" ) else: - ret[name]['data'] = delete_managed_disk( - kwargs={'resource_group': node_data['resource_group'], - 'container': container, - 'blob': blob}, - call='function' + ret[name]["data"] = delete_managed_disk( + kwargs={ + "resource_group": node_data["resource_group"], + "container": container, + "blob": blob, + }, + call="function", ) cleanup_interfaces = config.get_cloud_config_value( - 'cleanup_interfaces', + "cleanup_interfaces", get_configured_provider(), __opts__, search_global=False, - default=False + default=False, ) if cleanup_interfaces: - ret[name]['cleanup_network'] = { - 'cleanup_interfaces': cleanup_interfaces, - 'resource_group': node_data['resource_group'], - 'data': [], + ret[name]["cleanup_network"] = { + "cleanup_interfaces": cleanup_interfaces, + "resource_group": node_data["resource_group"], + "data": [], } - ifaces = node_data['network_profile']['network_interfaces'] + ifaces = node_data["network_profile"]["network_interfaces"] for iface in ifaces: - resource_group = iface['id'].split('/')[4] - ret[name]['cleanup_network']['data'].append( + resource_group = iface["id"].split("/")[4] + ret[name]["cleanup_network"]["data"].append( delete_interface( kwargs={ - 'resource_group': resource_group, - 'iface_name': iface['name'], + "resource_group": resource_group, + "iface_name": iface["name"], }, - call='function', + call="function", ) ) @@ -1640,83 +1542,77 @@ def destroy(name, call=None, kwargs=None): # pylint: disable=unused-argument def list_storage_accounts(call=None): - ''' + """ List storage accounts within the subscription. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_storage_accounts function must be called with ' - '-f or --function' + "The list_storage_accounts function must be called with " "-f or --function" ) - storconn = get_conn(client_type='storage') + storconn = get_conn(client_type="storage") ret = {} try: accounts_query = storconn.storage_accounts.list() - accounts = __utils__['azurearm.paged_object_to_list'](accounts_query) + accounts = __utils__["azurearm.paged_object_to_list"](accounts_query) for account in accounts: - ret[account['name']] = account + ret[account["name"]] = account except CloudError as exc: - __utils__['azurearm.log_cloud_error']('storage', exc.message) - ret = {'Error': exc.message} + __utils__["azurearm.log_cloud_error"]("storage", exc.message) + ret = {"Error": exc.message} return ret def _get_cloud_environment(): - ''' + """ Get the cloud environment object. - ''' + """ cloud_environment = config.get_cloud_config_value( - 'cloud_environment', - get_configured_provider(), __opts__, search_global=False - ) + "cloud_environment", get_configured_provider(), __opts__, search_global=False + ) try: - cloud_env_module = importlib.import_module('msrestazure.azure_cloud') - cloud_env = getattr(cloud_env_module, cloud_environment or 'AZURE_PUBLIC_CLOUD') + cloud_env_module = importlib.import_module("msrestazure.azure_cloud") + cloud_env = getattr(cloud_env_module, cloud_environment or "AZURE_PUBLIC_CLOUD") except (AttributeError, ImportError): raise SaltCloudSystemExit( - 'The azure {0} cloud environment is not available.'.format(cloud_environment) + "The azure {0} cloud environment is not available.".format( + cloud_environment + ) ) return cloud_env def _get_block_blob_service(kwargs=None): - ''' + """ Get the block blob storage service. - ''' - resource_group = kwargs.get('resource_group') or config.get_cloud_config_value( - 'resource_group', - get_configured_provider(), __opts__, search_global=False - ) - sas_token = kwargs.get('sas_token') or config.get_cloud_config_value( - 'sas_token', - get_configured_provider(), __opts__, search_global=False - ) - storage_account = kwargs.get('storage_account') or config.get_cloud_config_value( - 'storage_account', - get_configured_provider(), __opts__, search_global=False - ) - storage_key = kwargs.get('storage_key') or config.get_cloud_config_value( - 'storage_key', - get_configured_provider(), __opts__, search_global=False - ) + """ + resource_group = kwargs.get("resource_group") or config.get_cloud_config_value( + "resource_group", get_configured_provider(), __opts__, search_global=False + ) + sas_token = kwargs.get("sas_token") or config.get_cloud_config_value( + "sas_token", get_configured_provider(), __opts__, search_global=False + ) + storage_account = kwargs.get("storage_account") or config.get_cloud_config_value( + "storage_account", get_configured_provider(), __opts__, search_global=False + ) + storage_key = kwargs.get("storage_key") or config.get_cloud_config_value( + "storage_key", get_configured_provider(), __opts__, search_global=False + ) if not resource_group: - raise SaltCloudSystemExit( - 'A resource group must be specified' - ) + raise SaltCloudSystemExit("A resource group must be specified") if not storage_account: - raise SaltCloudSystemExit( - 'A storage account must be specified' - ) + raise SaltCloudSystemExit("A storage account must be specified") if not storage_key: - storconn = get_conn(client_type='storage') - storage_keys = storconn.storage_accounts.list_keys(resource_group, storage_account) + storconn = get_conn(client_type="storage") + storage_keys = storconn.storage_accounts.list_keys( + resource_group, storage_account + ) storage_keys = {v.key_name: v.value for v in storage_keys.keys} storage_key = next(six.itervalues(storage_keys)) @@ -1724,33 +1620,34 @@ def _get_block_blob_service(kwargs=None): endpoint_suffix = cloud_env.suffixes.storage_endpoint - return BlockBlobService(storage_account, storage_key, - sas_token=sas_token, - endpoint_suffix=endpoint_suffix) + return BlockBlobService( + storage_account, + storage_key, + sas_token=sas_token, + endpoint_suffix=endpoint_suffix, + ) def list_blobs(call=None, kwargs=None): # pylint: disable=unused-argument - ''' + """ List blobs. - ''' + """ if kwargs is None: kwargs = {} - if 'container' not in kwargs: - raise SaltCloudSystemExit( - 'A container must be specified' - ) + if "container" not in kwargs: + raise SaltCloudSystemExit("A container must be specified") storageservice = _get_block_blob_service(kwargs) ret = {} try: - for blob in storageservice.list_blobs(kwargs['container']).items: + for blob in storageservice.list_blobs(kwargs["container"]).items: ret[blob.name] = { - 'blob_type': blob.properties.blob_type, - 'last_modified': blob.properties.last_modified.isoformat(), - 'server_encrypted': blob.properties.server_encrypted, - } + "blob_type": blob.properties.blob_type, + "last_modified": blob.properties.last_modified.isoformat(), + "server_encrypted": blob.properties.server_encrypted, + } except Exception as exc: # pylint: disable=broad-except log.warning(six.text_type(exc)) @@ -1758,132 +1655,125 @@ def list_blobs(call=None, kwargs=None): # pylint: disable=unused-argument def delete_blob(call=None, kwargs=None): # pylint: disable=unused-argument - ''' + """ Delete a blob from a container. - ''' + """ if kwargs is None: kwargs = {} - if 'container' not in kwargs: - raise SaltCloudSystemExit( - 'A container must be specified' - ) + if "container" not in kwargs: + raise SaltCloudSystemExit("A container must be specified") - if 'blob' not in kwargs: - raise SaltCloudSystemExit( - 'A blob must be specified' - ) + if "blob" not in kwargs: + raise SaltCloudSystemExit("A blob must be specified") storageservice = _get_block_blob_service(kwargs) - storageservice.delete_blob(kwargs['container'], kwargs['blob']) + storageservice.delete_blob(kwargs["container"], kwargs["blob"]) return True def delete_managed_disk(call=None, kwargs=None): # pylint: disable=unused-argument - ''' + """ Delete a managed disk from a resource group. - ''' + """ - compconn = get_conn(client_type='compute') + compconn = get_conn(client_type="compute") try: - compconn.disks.delete(kwargs['resource_group'], kwargs['blob']) + compconn.disks.delete(kwargs["resource_group"], kwargs["blob"]) except Exception as exc: # pylint: disable=broad-except - log.error('Error deleting managed disk %s - %s', kwargs.get('blob'), six.text_type(exc)) + log.error( + "Error deleting managed disk %s - %s", + kwargs.get("blob"), + six.text_type(exc), + ) return False return True def list_virtual_networks(call=None, kwargs=None): - ''' + """ List virtual networks. - ''' + """ if kwargs is None: kwargs = {} - if call == 'action': + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function' + "The avail_sizes function must be called with " "-f or --function" ) - netconn = get_conn(client_type='network') + netconn = get_conn(client_type="network") resource_groups = list_resource_groups() ret = {} for group in resource_groups: try: - networks = netconn.virtual_networks.list( - resource_group_name=group - ) + networks = netconn.virtual_networks.list(resource_group_name=group) except CloudError: networks = {} for network_obj in networks: network = network_obj.as_dict() - ret[network['name']] = network - ret[network['name']]['subnets'] = list_subnets( - kwargs={'resource_group': group, 'network': network['name']} + ret[network["name"]] = network + ret[network["name"]]["subnets"] = list_subnets( + kwargs={"resource_group": group, "network": network["name"]} ) return ret def list_subnets(call=None, kwargs=None): - ''' + """ List subnets in a virtual network. - ''' + """ if kwargs is None: kwargs = {} - if call == 'action': + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function' + "The avail_sizes function must be called with " "-f or --function" ) - netconn = get_conn(client_type='network') + netconn = get_conn(client_type="network") - resource_group = kwargs.get('resource_group') or config.get_cloud_config_value( - 'resource_group', - get_configured_provider(), __opts__, search_global=False - ) + resource_group = kwargs.get("resource_group") or config.get_cloud_config_value( + "resource_group", get_configured_provider(), __opts__, search_global=False + ) - if not resource_group and 'group' in kwargs and 'resource_group' not in kwargs: - resource_group = kwargs['group'] + if not resource_group and "group" in kwargs and "resource_group" not in kwargs: + resource_group = kwargs["group"] if not resource_group: - raise SaltCloudSystemExit( - 'A resource group must be specified' + raise SaltCloudSystemExit("A resource group must be specified") + + if kwargs.get("network") is None: + kwargs["network"] = config.get_cloud_config_value( + "network", get_configured_provider(), __opts__, search_global=False ) - if kwargs.get('network') is None: - kwargs['network'] = config.get_cloud_config_value( - 'network', get_configured_provider(), __opts__, search_global=False - ) - - if 'network' not in kwargs or kwargs['network'] is None: - raise SaltCloudSystemExit( - 'A "network" must be specified' - ) + if "network" not in kwargs or kwargs["network"] is None: + raise SaltCloudSystemExit('A "network" must be specified') ret = {} - subnets = netconn.subnets.list(resource_group, kwargs['network']) + subnets = netconn.subnets.list(resource_group, kwargs["network"]) for subnet in subnets: ret[subnet.name] = subnet.as_dict() - ret[subnet.name]['ip_configurations'] = {} + ret[subnet.name]["ip_configurations"] = {} for ip_ in subnet.ip_configurations: - comps = ip_.id.split('/') + comps = ip_.id.split("/") name = comps[-1] - ret[subnet.name]['ip_configurations'][name] = ip_.as_dict() - ret[subnet.name]['ip_configurations'][name]['subnet'] = subnet.name - ret[subnet.name]['resource_group'] = resource_group + ret[subnet.name]["ip_configurations"][name] = ip_.as_dict() + ret[subnet.name]["ip_configurations"][name]["subnet"] = subnet.name + ret[subnet.name]["resource_group"] = resource_group return ret -def create_or_update_vmextension(call=None, kwargs=None): # pylint: disable=unused-argument - ''' +def create_or_update_vmextension( + call=None, kwargs=None +): # pylint: disable=unused-argument + """ .. versionadded:: 2019.2.0 Create or update a VM extension object "inside" of a VM object. @@ -1905,61 +1795,50 @@ def create_or_update_vmextension(call=None, kwargs=None): # pylint: disable=unu type_handler_version: < default: 2.0 > auto_upgrade_minor_version: < default: True > protected_settings: < default: None > - ''' + """ if kwargs is None: kwargs = {} - if 'extension_name' not in kwargs: - raise SaltCloudSystemExit( - 'An extension name must be specified' - ) + if "extension_name" not in kwargs: + raise SaltCloudSystemExit("An extension name must be specified") - if 'virtual_machine_name' not in kwargs: - raise SaltCloudSystemExit( - 'A virtual machine name must be specified' - ) + if "virtual_machine_name" not in kwargs: + raise SaltCloudSystemExit("A virtual machine name must be specified") - compconn = get_conn(client_type='compute') + compconn = get_conn(client_type="compute") # pylint: disable=invalid-name - VirtualMachineExtension = getattr( - compute_models, 'VirtualMachineExtension' + VirtualMachineExtension = getattr(compute_models, "VirtualMachineExtension") + + resource_group = kwargs.get("resource_group") or config.get_cloud_config_value( + "resource_group", get_configured_provider(), __opts__, search_global=False ) - resource_group = kwargs.get('resource_group') or config.get_cloud_config_value( - 'resource_group', - get_configured_provider(), __opts__, search_global=False - ) - if not resource_group: - raise SaltCloudSystemExit( - 'A resource group must be specified' - ) + raise SaltCloudSystemExit("A resource group must be specified") - location = kwargs.get('location') or get_location() + location = kwargs.get("location") or get_location() if not location: - raise SaltCloudSystemExit( - 'A location must be specified' - ) + raise SaltCloudSystemExit("A location must be specified") - publisher = kwargs.get('publisher', 'Microsoft.Azure.Extensions') - virtual_machine_extension_type = kwargs.get('virtual_machine_extension_type', 'CustomScript') - type_handler_version = kwargs.get('type_handler_version', '2.0') - auto_upgrade_minor_version = kwargs.get('auto_upgrade_minor_version', True) - settings = kwargs.get('settings', {}) - protected_settings = kwargs.get('protected_settings') + publisher = kwargs.get("publisher", "Microsoft.Azure.Extensions") + virtual_machine_extension_type = kwargs.get( + "virtual_machine_extension_type", "CustomScript" + ) + type_handler_version = kwargs.get("type_handler_version", "2.0") + auto_upgrade_minor_version = kwargs.get("auto_upgrade_minor_version", True) + settings = kwargs.get("settings", {}) + protected_settings = kwargs.get("protected_settings") if not isinstance(settings, dict): + raise SaltCloudSystemExit("VM extension settings are not valid") + elif "commandToExecute" not in settings and "script" not in settings: raise SaltCloudSystemExit( - 'VM extension settings are not valid' - ) - elif 'commandToExecute' not in settings and 'script' not in settings: - raise SaltCloudSystemExit( - 'VM extension settings are not valid. Either commandToExecute or script must be specified.' + "VM extension settings are not valid. Either commandToExecute or script must be specified." ) - log.info('Creating VM extension %s', kwargs['extension_name']) + log.info("Creating VM extension %s", kwargs["extension_name"]) ret = {} try: @@ -1970,26 +1849,29 @@ def create_or_update_vmextension(call=None, kwargs=None): # pylint: disable=unu type_handler_version=type_handler_version, auto_upgrade_minor_version=auto_upgrade_minor_version, settings=settings, - protected_settings=protected_settings + protected_settings=protected_settings, ) poller = compconn.virtual_machine_extensions.create_or_update( resource_group, - kwargs['virtual_machine_name'], - kwargs['extension_name'], - params + kwargs["virtual_machine_name"], + kwargs["extension_name"], + params, ) ret = poller.result() ret = ret.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', 'Error attempting to create the VM extension: {0}'.format(exc.message)) - ret = {'error': exc.message} + __utils__["azurearm.log_cloud_error"]( + "compute", + "Error attempting to create the VM extension: {0}".format(exc.message), + ) + ret = {"error": exc.message} return ret def stop(name, call=None): - ''' + """ .. versionadded:: 2019.2.0 Stop (deallocate) a VM @@ -1999,18 +1881,15 @@ def stop(name, call=None): .. code-block:: bash salt-cloud -a stop myminion - ''' - if call == 'function': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call == "function": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - compconn = get_conn(client_type='compute') + compconn = get_conn(client_type="compute") resource_group = config.get_cloud_config_value( - 'resource_group', - get_configured_provider(), __opts__, search_global=False - ) + "resource_group", get_configured_provider(), __opts__, search_global=False + ) ret = {} if not resource_group: @@ -2018,39 +1897,43 @@ def stop(name, call=None): for group in groups: try: instance = compconn.virtual_machines.deallocate( - vm_name=name, - resource_group_name=group + vm_name=name, resource_group_name=group ) instance.wait() vm_result = instance.result() ret = vm_result.as_dict() break except CloudError as exc: - if 'was not found' in exc.message: + if "was not found" in exc.message: continue else: - ret = {'error': exc.message} + ret = {"error": exc.message} if not ret: - __utils__['azurearm.log_cloud_error']('compute', 'Unable to find virtual machine with name: {0}'.format(name)) - ret = {'error': 'Unable to find virtual machine with name: {0}'.format(name)} + __utils__["azurearm.log_cloud_error"]( + "compute", "Unable to find virtual machine with name: {0}".format(name) + ) + ret = { + "error": "Unable to find virtual machine with name: {0}".format(name) + } else: try: instance = compconn.virtual_machines.deallocate( - vm_name=name, - resource_group_name=resource_group - ) + vm_name=name, resource_group_name=resource_group + ) instance.wait() vm_result = instance.result() ret = vm_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', 'Error attempting to stop {0}: {1}'.format(name, exc.message)) - ret = {'error': exc.message} + __utils__["azurearm.log_cloud_error"]( + "compute", "Error attempting to stop {0}: {1}".format(name, exc.message) + ) + ret = {"error": exc.message} return ret def start(name, call=None): - ''' + """ .. versionadded:: 2019.2.0 Start a VM @@ -2060,18 +1943,17 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start myminion - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The start action must be called with -a or --action.' + "The start action must be called with -a or --action." ) - compconn = get_conn(client_type='compute') + compconn = get_conn(client_type="compute") resource_group = config.get_cloud_config_value( - 'resource_group', - get_configured_provider(), __opts__, search_global=False - ) + "resource_group", get_configured_provider(), __opts__, search_global=False + ) ret = {} if not resource_group: @@ -2079,32 +1961,37 @@ def start(name, call=None): for group in groups: try: instance = compconn.virtual_machines.start( - vm_name=name, - resource_group_name=group + vm_name=name, resource_group_name=group ) instance.wait() vm_result = instance.result() ret = vm_result.as_dict() break except CloudError as exc: - if 'was not found' in exc.message: + if "was not found" in exc.message: continue else: - ret = {'error': exc.message} + ret = {"error": exc.message} if not ret: - __utils__['azurearm.log_cloud_error']('compute', 'Unable to find virtual machine with name: {0}'.format(name)) - ret = {'error': 'Unable to find virtual machine with name: {0}'.format(name)} + __utils__["azurearm.log_cloud_error"]( + "compute", "Unable to find virtual machine with name: {0}".format(name) + ) + ret = { + "error": "Unable to find virtual machine with name: {0}".format(name) + } else: try: instance = compconn.virtual_machines.start( - vm_name=name, - resource_group_name=resource_group - ) + vm_name=name, resource_group_name=resource_group + ) instance.wait() vm_result = instance.result() ret = vm_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', 'Error attempting to start {0}: {1}'.format(name, exc.message)) - ret = {'error': exc.message} + __utils__["azurearm.log_cloud_error"]( + "compute", + "Error attempting to start {0}: {1}".format(name, exc.message), + ) + ret = {"error": exc.message} return ret diff --git a/salt/cloud/clouds/clc.py b/salt/cloud/clouds/clc.py index 70bd6367177..f78e947c22e 100644 --- a/salt/cloud/clouds/clc.py +++ b/salt/cloud/clouds/clc.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" CenturyLink Cloud Module -=================== +======================== .. versionadded:: Oyxgen @@ -29,9 +29,12 @@ clc-sdk can be installed via pip: For sdk reference see: https://github.com/CenturyLinkCloud/clc-python-sdk Flask -------- +----- + flask can be installed via pip: + .. code-block:: bash + pip install flask Configuration @@ -59,10 +62,11 @@ cloud configuration at connect to a cloud provider, while cloud profile configuration continues to use ``provider`` to refer to the cloud provider configuration that you define. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import importlib import logging import time @@ -78,28 +82,32 @@ log = logging.getLogger(__name__) # Attempt to import clc-sdk lib try: - #when running this in linode's Ubuntu 16.x version the following line is required to get the clc sdk libraries to load - importlib.import_module('clc') + # when running this in linode's Ubuntu 16.x version the following line is required to get the clc sdk libraries to load + importlib.import_module("clc") import clc + HAS_CLC = True except ImportError: HAS_CLC = False # Disable InsecureRequestWarning generated on python > 2.6 try: - from requests.packages.urllib3 import disable_warnings # pylint: disable=no-name-in-module + from requests.packages.urllib3 import ( + disable_warnings, + ) # pylint: disable=no-name-in-module + disable_warnings() except Exception: # pylint: disable=broad-except pass -__virtualname__ = 'clc' +__virtualname__ = "clc" # Only load in this module if the CLC configurations are in place def __virtual__(): - ''' + """ Check for CLC configuration and if required libs are available. - ''' + """ if get_configured_provider() is False or get_dependencies() is False: return False @@ -110,50 +118,53 @@ def get_configured_provider(): return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('token', 'token_pass', 'user', 'password', ) + ("token", "token_pass", "user", "password",), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' + """ deps = { - 'clc': HAS_CLC, + "clc": HAS_CLC, } - return config.check_driver_dependencies( - __virtualname__, - deps - ) + return config.check_driver_dependencies(__virtualname__, deps) def get_creds(): user = config.get_cloud_config_value( - 'user', get_configured_provider(), __opts__, search_global=False + "user", get_configured_provider(), __opts__, search_global=False ) password = config.get_cloud_config_value( - 'password', get_configured_provider(), __opts__, search_global=False + "password", get_configured_provider(), __opts__, search_global=False ) accountalias = config.get_cloud_config_value( - 'accountalias', get_configured_provider(), __opts__, search_global=False + "accountalias", get_configured_provider(), __opts__, search_global=False ) token = config.get_cloud_config_value( - 'token', get_configured_provider(), __opts__, search_global=False + "token", get_configured_provider(), __opts__, search_global=False ) token_pass = config.get_cloud_config_value( - 'token_pass', get_configured_provider(), __opts__, search_global=False + "token_pass", get_configured_provider(), __opts__, search_global=False ) - creds = {'user': user, 'password': password, 'token': token, 'token_pass': token_pass, 'accountalias': accountalias} + creds = { + "user": user, + "password": password, + "token": token, + "token_pass": token_pass, + "accountalias": accountalias, + } return creds def list_nodes_full(call=None, for_output=True): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) creds = get_creds() clc.v1.SetCredentials(creds["token"], creds["token_pass"]) @@ -171,14 +182,14 @@ def get_queue_data(call=None, for_output=True): def get_monthly_estimate(call=None, for_output=True): - ''' + """ Return a list of the VMs that are on the provider - ''' + """ creds = get_creds() clc.v1.SetCredentials(creds["token"], creds["token_pass"]) - if call == 'action': + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) try: billing_raw = clc.v1.Billing.GetAccountSummary(alias=creds["accountalias"]) @@ -191,14 +202,14 @@ def get_monthly_estimate(call=None, for_output=True): def get_month_to_date(call=None, for_output=True): - ''' + """ Return a list of the VMs that are on the provider - ''' + """ creds = get_creds() clc.v1.SetCredentials(creds["token"], creds["token_pass"]) - if call == 'action': + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) try: billing_raw = clc.v1.Billing.GetAccountSummary(alias=creds["accountalias"]) @@ -211,9 +222,9 @@ def get_month_to_date(call=None, for_output=True): def get_server_alerts(call=None, for_output=True, **kwargs): - ''' + """ Return a list of alerts from CLC as reported by their infra - ''' + """ for key, value in kwargs.items(): servername = "" if key == "servername": @@ -225,10 +236,10 @@ def get_server_alerts(call=None, for_output=True, **kwargs): def get_group_estimate(call=None, for_output=True, **kwargs): - ''' + """ Return a list of the VMs that are on the provider usage: "salt-cloud -f get_group_estimate clc group=Dev location=VA1" - ''' + """ for key, value in kwargs.items(): group = "" location = "" @@ -238,12 +249,14 @@ def get_group_estimate(call=None, for_output=True, **kwargs): location = value creds = get_creds() clc.v1.SetCredentials(creds["token"], creds["token_pass"]) - if call == 'action': + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) try: - billing_raw = clc.v1.Billing.GetGroupEstimate(group=group, alias=creds["accountalias"], location=location) + billing_raw = clc.v1.Billing.GetGroupEstimate( + group=group, alias=creds["accountalias"], location=location + ) billing_raw = salt.utils.json.dumps(billing_raw) billing = salt.utils.json.loads(billing_raw) estimate = round(billing["MonthlyEstimate"], 2) @@ -254,9 +267,9 @@ def get_group_estimate(call=None, for_output=True, **kwargs): def avail_images(call=None): - ''' + """ returns a list of images available to you - ''' + """ all_servers = list_nodes_full() templates = {} for server in all_servers: @@ -266,9 +279,9 @@ def avail_images(call=None): def avail_locations(call=None): - ''' + """ returns a list of locations available to you - ''' + """ creds = get_creds() clc.v1.SetCredentials(creds["token"], creds["token_pass"]) locations = clc.v1.Account.GetLocations() @@ -276,16 +289,16 @@ def avail_locations(call=None): def avail_sizes(call=None): - ''' + """ use templates for this - ''' + """ return {"Sizes": "Sizes are built into templates. Choose appropriate template"} def get_build_status(req_id, nodename): - ''' + """ get the build status from CLC to make sure we dont return to early - ''' + """ counter = 0 req_id = six.text_type(req_id) while counter < 10: @@ -299,75 +312,88 @@ def get_build_status(req_id, nodename): return internal_ip_address else: counter = counter + 1 - log.info('Creating Cloud VM %s Time out in %s minutes', - nodename, six.text_type(10 - counter)) + log.info( + "Creating Cloud VM %s Time out in %s minutes", + nodename, + six.text_type(10 - counter), + ) time.sleep(60) def create(vm_): - ''' + """ get the system build going - ''' + """ creds = get_creds() clc.v1.SetCredentials(creds["token"], creds["token_pass"]) cloud_profile = config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('token',) + __opts__, __active_provider_name__ or __virtualname__, ("token",) ) group = config.get_cloud_config_value( - 'group', vm_, __opts__, search_global=False, default=None, + "group", vm_, __opts__, search_global=False, default=None, ) - name = vm_['name'] + name = vm_["name"] description = config.get_cloud_config_value( - 'description', vm_, __opts__, search_global=False, default=None, + "description", vm_, __opts__, search_global=False, default=None, ) ram = config.get_cloud_config_value( - 'ram', vm_, __opts__, search_global=False, default=None, + "ram", vm_, __opts__, search_global=False, default=None, ) backup_level = config.get_cloud_config_value( - 'backup_level', vm_, __opts__, search_global=False, default=None, + "backup_level", vm_, __opts__, search_global=False, default=None, ) template = config.get_cloud_config_value( - 'template', vm_, __opts__, search_global=False, default=None, + "template", vm_, __opts__, search_global=False, default=None, ) password = config.get_cloud_config_value( - 'password', vm_, __opts__, search_global=False, default=None, + "password", vm_, __opts__, search_global=False, default=None, ) cpu = config.get_cloud_config_value( - 'cpu', vm_, __opts__, search_global=False, default=None, + "cpu", vm_, __opts__, search_global=False, default=None, ) network = config.get_cloud_config_value( - 'network', vm_, __opts__, search_global=False, default=None, + "network", vm_, __opts__, search_global=False, default=None, ) location = config.get_cloud_config_value( - 'location', vm_, __opts__, search_global=False, default=None, + "location", vm_, __opts__, search_global=False, default=None, ) if len(name) > 6: name = name[0:6] if len(password) < 9: - password = '' - clc_return = clc.v1.Server.Create(alias=None, location=(location), name=(name), template=(template), cpu=(cpu), ram=(ram), backup_level=(backup_level), group=(group), network=(network), description=(description), password=(password)) + password = "" + clc_return = clc.v1.Server.Create( + alias=None, + location=(location), + name=(name), + template=(template), + cpu=(cpu), + ram=(ram), + backup_level=(backup_level), + group=(group), + network=(network), + description=(description), + password=(password), + ) req_id = clc_return["RequestID"] - vm_['ssh_host'] = get_build_status(req_id, name) - __utils__['cloud.fire_event']( - 'event', - 'waiting for ssh', - 'salt/cloud/{0}/waiting_for_ssh'.format(name), - sock_dir=__opts__['sock_dir'], - args={'ip_address': vm_['ssh_host']}, - transport=__opts__['transport'] + vm_["ssh_host"] = get_build_status(req_id, name) + __utils__["cloud.fire_event"]( + "event", + "waiting for ssh", + "salt/cloud/{0}/waiting_for_ssh".format(name), + sock_dir=__opts__["sock_dir"], + args={"ip_address": vm_["ssh_host"]}, + transport=__opts__["transport"], ) # Bootstrap! - ret = __utils__['cloud.bootstrap'](vm_, __opts__) - return_message = {"Server Name": name, "IP Address": vm_['ssh_host']} + ret = __utils__["cloud.bootstrap"](vm_, __opts__) + return_message = {"Server Name": name, "IP Address": vm_["ssh_host"]} ret.update(return_message) return return_message def destroy(name, call=None): - ''' + """ destroy the vm - ''' + """ return {"status": "destroying must be done via https://control.ctl.io at this time"} diff --git a/salt/cloud/clouds/cloudstack.py b/salt/cloud/clouds/cloudstack.py index 36f0a2bfbc9..e4afec1d93b 100644 --- a/salt/cloud/clouds/cloudstack.py +++ b/salt/cloud/clouds/cloudstack.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" CloudStack Cloud Module ======================= @@ -20,37 +20,40 @@ Use of this module requires the ``apikey``, ``secretkey``, ``host`` and path: /client/api driver: cloudstack -''' +""" # pylint: disable=invalid-name,function-redefined # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import pprint + import logging +import pprint # Import salt cloud libs import salt.config as config import salt.utils.cloud import salt.utils.event from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import -from salt.utils.functools import namespaced_function from salt.exceptions import SaltCloudSystemExit -from salt.utils.versions import LooseVersion as _LooseVersion from salt.ext import six +from salt.utils.functools import namespaced_function +from salt.utils.versions import LooseVersion as _LooseVersion # CloudStackNetwork will be needed during creation of a new node # pylint: disable=import-error try: from libcloud.compute.drivers.cloudstack import CloudStackNetwork + # This work-around for Issue #32743 is no longer needed for libcloud >= # 1.4.0. However, older versions of libcloud must still be supported with # this work-around. This work-around can be removed when the required # minimum version of libcloud is 2.0.0 (See PR #40837 - which is # implemented in Salt 2018.3.0). - if _LooseVersion(libcloud.__version__) < _LooseVersion('1.4.0'): + if _LooseVersion(libcloud.__version__) < _LooseVersion("1.4.0"): # See https://github.com/saltstack/salt/issues/32743 import libcloud.security - libcloud.security.CA_CERTS_PATH.append('/etc/ssl/certs/YaST-CA.pem') + + libcloud.security.CA_CERTS_PATH.append("/etc/ssl/certs/YaST-CA.pem") HAS_LIBS = True except ImportError: HAS_LIBS = False @@ -71,14 +74,14 @@ list_nodes_full = namespaced_function(list_nodes_full, globals()) list_nodes_select = namespaced_function(list_nodes_select, globals()) show_instance = namespaced_function(show_instance, globals()) -__virtualname__ = 'cloudstack' +__virtualname__ = "cloudstack" # Only load in this module if the CLOUDSTACK configurations are in place def __virtual__(): - ''' + """ Set up the libcloud functions and check for CloudStack configurations. - ''' + """ if get_configured_provider() is False: return False @@ -89,125 +92,136 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('apikey', 'secretkey', 'host', 'path') + ("apikey", "secretkey", "host", "path"), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'libcloud': HAS_LIBS} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"libcloud": HAS_LIBS}) def get_conn(): - ''' + """ Return a conn object for the passed VM data - ''' + """ driver = get_driver(Provider.CLOUDSTACK) - verify_ssl_cert = config.get_cloud_config_value('verify_ssl_cert', - get_configured_provider(), - __opts__, - default=True, - search_global=False) + verify_ssl_cert = config.get_cloud_config_value( + "verify_ssl_cert", + get_configured_provider(), + __opts__, + default=True, + search_global=False, + ) if verify_ssl_cert is False: try: import libcloud.security + libcloud.security.VERIFY_SSL_CERT = False except (ImportError, AttributeError): raise SaltCloudSystemExit( - 'Could not disable SSL certificate verification. ' - 'Not loading module.' + "Could not disable SSL certificate verification. " "Not loading module." ) return driver( key=config.get_cloud_config_value( - 'apikey', get_configured_provider(), __opts__, search_global=False + "apikey", get_configured_provider(), __opts__, search_global=False ), secret=config.get_cloud_config_value( - 'secretkey', get_configured_provider(), __opts__, - search_global=False + "secretkey", get_configured_provider(), __opts__, search_global=False ), secure=config.get_cloud_config_value( - 'secure', get_configured_provider(), __opts__, - default=True, search_global=False + "secure", + get_configured_provider(), + __opts__, + default=True, + search_global=False, ), host=config.get_cloud_config_value( - 'host', get_configured_provider(), __opts__, search_global=False + "host", get_configured_provider(), __opts__, search_global=False ), path=config.get_cloud_config_value( - 'path', get_configured_provider(), __opts__, search_global=False + "path", get_configured_provider(), __opts__, search_global=False ), port=config.get_cloud_config_value( - 'port', get_configured_provider(), __opts__, - default=None, search_global=False - ) + "port", + get_configured_provider(), + __opts__, + default=None, + search_global=False, + ), ) def get_location(conn, vm_): - ''' + """ Return the node location to use - ''' + """ locations = conn.list_locations() # Default to Dallas if not otherwise set - loc = config.get_cloud_config_value('location', vm_, __opts__, default=2) + loc = config.get_cloud_config_value("location", vm_, __opts__, default=2) for location in locations: - if six.text_type(loc) in (six.text_type(location.id), six.text_type(location.name)): + if six.text_type(loc) in ( + six.text_type(location.id), + six.text_type(location.name), + ): return location def get_security_groups(conn, vm_): - ''' + """ Return a list of security groups to use, defaulting to ['default'] - ''' + """ securitygroup_enabled = config.get_cloud_config_value( - 'securitygroup_enabled', vm_, __opts__, default=True + "securitygroup_enabled", vm_, __opts__, default=True ) if securitygroup_enabled: return config.get_cloud_config_value( - 'securitygroup', vm_, __opts__, default=['default'] + "securitygroup", vm_, __opts__, default=["default"] ) else: return False def get_password(vm_): - ''' + """ Return the password to use - ''' + """ return config.get_cloud_config_value( - 'password', vm_, __opts__, default=config.get_cloud_config_value( - 'passwd', vm_, __opts__, search_global=False - ), search_global=False + "password", + vm_, + __opts__, + default=config.get_cloud_config_value( + "passwd", vm_, __opts__, search_global=False + ), + search_global=False, ) def get_key(): - ''' + """ Returns the ssh private key for VM access - ''' + """ return config.get_cloud_config_value( - 'private_key', get_configured_provider(), __opts__, search_global=False + "private_key", get_configured_provider(), __opts__, search_global=False ) def get_keypair(vm_): - ''' + """ Return the keypair to use - ''' - keypair = config.get_cloud_config_value('keypair', vm_, __opts__) + """ + keypair = config.get_cloud_config_value("keypair", vm_, __opts__) if keypair: return keypair @@ -216,11 +230,11 @@ def get_keypair(vm_): def get_ip(data): - ''' + """ Return the IP address of the VM If the VM has public IP as defined by libcloud module then use it Otherwise try to extract the private IP and use that one. - ''' + """ try: ip = data.public_ips[0] except Exception: # pylint: disable=broad-except @@ -229,10 +243,10 @@ def get_ip(data): def get_networkid(vm_): - ''' + """ Return the networkid to use, only valid for Advanced Zone - ''' - networkid = config.get_cloud_config_value('networkid', vm_, __opts__) + """ + networkid = config.get_cloud_config_value("networkid", vm_, __opts__) if networkid is not None: return networkid @@ -241,22 +255,27 @@ def get_networkid(vm_): def get_project(conn, vm_): - ''' + """ Return the project to use. - ''' + """ try: projects = conn.ex_list_projects() except AttributeError: # with versions <0.15 of libcloud this is causing an AttributeError. - log.warning('Cannot get projects, you may need to update libcloud to 0.15 or later') + log.warning( + "Cannot get projects, you may need to update libcloud to 0.15 or later" + ) return False - projid = config.get_cloud_config_value('projectid', vm_, __opts__) + projid = config.get_cloud_config_value("projectid", vm_, __opts__) if not projid: return False for project in projects: - if six.text_type(projid) in (six.text_type(project.id), six.text_type(project.name)): + if six.text_type(projid) in ( + six.text_type(project.id), + six.text_type(project.name), + ): return project log.warning("Couldn't find project %s in projects", projid) @@ -264,110 +283,125 @@ def get_project(conn, vm_): def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'cloudstack', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or "cloudstack", + vm_["profile"], + vm_=vm_, + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - sock_dir=__opts__['sock_dir'], - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + sock_dir=__opts__["sock_dir"], + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) conn = get_conn() # pylint: disable=not-callable kwargs = { - 'name': vm_['name'], - 'image': get_image(conn, vm_), - 'size': get_size(conn, vm_), - 'location': get_location(conn, vm_), + "name": vm_["name"], + "image": get_image(conn, vm_), + "size": get_size(conn, vm_), + "location": get_location(conn, vm_), } # pylint: enable=not-callable sg = get_security_groups(conn, vm_) if sg is not False: - kwargs['ex_security_groups'] = sg + kwargs["ex_security_groups"] = sg if get_keypair(vm_) is not False: - kwargs['ex_keyname'] = get_keypair(vm_) + kwargs["ex_keyname"] = get_keypair(vm_) if get_networkid(vm_) is not False: - kwargs['networkids'] = get_networkid(vm_) - kwargs['networks'] = ( # The only attr that is used is 'id'. - CloudStackNetwork(None, None, None, - kwargs['networkids'], - None, None), - ) + kwargs["networkids"] = get_networkid(vm_) + kwargs["networks"] = ( # The only attr that is used is 'id'. + CloudStackNetwork(None, None, None, kwargs["networkids"], None, None), + ) if get_project(conn, vm_) is not False: - kwargs['project'] = get_project(conn, vm_) + kwargs["project"] = get_project(conn, vm_) event_data = kwargs.copy() - event_data['image'] = kwargs['image'].name - event_data['size'] = kwargs['size'].name + event_data["image"] = kwargs["image"].name + event_data["size"] = kwargs["size"].name - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - sock_dir=__opts__['sock_dir'], + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + sock_dir=__opts__["sock_dir"], args={ - 'kwargs': __utils__['cloud.filter_event']( - 'requesting', + "kwargs": __utils__["cloud.filter_event"]( + "requesting", event_data, - ['name', 'profile', 'provider', 'driver', 'image', 'size'], + ["name", "profile", "provider", "driver", "image", "size"], ), }, - transport=__opts__['transport'] + transport=__opts__["transport"], ) displayname = cloudstack_displayname(vm_) if displayname: - kwargs['ex_displayname'] = displayname + kwargs["ex_displayname"] = displayname else: - kwargs['ex_displayname'] = kwargs['name'] + kwargs["ex_displayname"] = kwargs["name"] volumes = {} ex_blockdevicemappings = block_device_mappings(vm_) if ex_blockdevicemappings: for ex_blockdevicemapping in ex_blockdevicemappings: - if 'VirtualName' not in ex_blockdevicemapping: - ex_blockdevicemapping['VirtualName'] = '{0}-{1}'.format(vm_['name'], len(volumes)) - __utils__['cloud.fire_event']( - 'event', - 'requesting volume', - 'salt/cloud/{0}/requesting'.format(ex_blockdevicemapping['VirtualName']), - sock_dir=__opts__['sock_dir'], - args={'kwargs': {'name': ex_blockdevicemapping['VirtualName'], - 'device': ex_blockdevicemapping['DeviceName'], - 'size': ex_blockdevicemapping['VolumeSize']}}, + if "VirtualName" not in ex_blockdevicemapping: + ex_blockdevicemapping["VirtualName"] = "{0}-{1}".format( + vm_["name"], len(volumes) + ) + __utils__["cloud.fire_event"]( + "event", + "requesting volume", + "salt/cloud/{0}/requesting".format( + ex_blockdevicemapping["VirtualName"] + ), + sock_dir=__opts__["sock_dir"], + args={ + "kwargs": { + "name": ex_blockdevicemapping["VirtualName"], + "device": ex_blockdevicemapping["DeviceName"], + "size": ex_blockdevicemapping["VolumeSize"], + } + }, ) try: - volumes[ex_blockdevicemapping['DeviceName']] = conn.create_volume( - ex_blockdevicemapping['VolumeSize'], - ex_blockdevicemapping['VirtualName'] - ) + volumes[ex_blockdevicemapping["DeviceName"]] = conn.create_volume( + ex_blockdevicemapping["VolumeSize"], + ex_blockdevicemapping["VirtualName"], + ) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating volume %s on CLOUDSTACK\n\n' - 'The following exception was thrown by libcloud when trying to ' - 'requesting a volume: \n%s', - ex_blockdevicemapping['VirtualName'], exc, + "Error creating volume %s on CLOUDSTACK\n\n" + "The following exception was thrown by libcloud when trying to " + "requesting a volume: \n%s", + ex_blockdevicemapping["VirtualName"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False else: @@ -376,12 +410,13 @@ def create(vm_): data = conn.create_node(**kwargs) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on CLOUDSTACK\n\n' - 'The following exception was thrown by libcloud when trying to ' - 'run the initial deployment: \n%s', - vm_['name'], exc, + "Error creating %s on CLOUDSTACK\n\n" + "The following exception was thrown by libcloud when trying to " + "run the initial deployment: \n%s", + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False @@ -390,162 +425,162 @@ def create(vm_): conn.attach_volume(data, volumes[device_name], device_name) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error attaching volume %s on CLOUDSTACK\n\n' - 'The following exception was thrown by libcloud when trying to ' - 'attach a volume: \n%s', - ex_blockdevicemapping.get('VirtualName', 'UNKNOWN'), exc, + "Error attaching volume %s on CLOUDSTACK\n\n" + "The following exception was thrown by libcloud when trying to " + "attach a volume: \n%s", + ex_blockdevicemapping.get("VirtualName", "UNKNOWN"), + exc, # Show the traceback if the debug logging level is enabled - exc_info=log.isEnabledFor(logging.DEBUG) + exc_info=log.isEnabledFor(logging.DEBUG), ) return False ssh_username = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, default='root' + "ssh_username", vm_, __opts__, default="root" ) - vm_['ssh_host'] = get_ip(data) - vm_['password'] = data.extra['password'] - vm_['key_filename'] = get_key() - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + vm_["ssh_host"] = get_ip(data) + vm_["password"] = data.extra["password"] + vm_["key_filename"] = get_key() + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data.__dict__) - if 'password' in data.extra: - del data.extra['password'] + if "password" in data.extra: + del data.extra["password"] - log.info('Created Cloud VM \'%s\'', vm_['name']) + log.info("Created Cloud VM '%s'", vm_["name"]) log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data.__dict__) + "'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data.__dict__) ) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - sock_dir=__opts__['sock_dir'], - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + sock_dir=__opts__["sock_dir"], + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + transport=__opts__["transport"], ) return ret def destroy(name, conn=None, call=None): - ''' + """ Delete a single VM, and all of its volumes - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - sock_dir=__opts__['sock_dir'], - args={'name': name}, + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + sock_dir=__opts__["sock_dir"], + args={"name": name}, ) if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 node = get_node(conn, name) # pylint: disable=not-callable if node is None: - log.error('Unable to find the VM %s', name) + log.error("Unable to find the VM %s", name) volumes = conn.list_volumes(node) if volumes is None: - log.error('Unable to find volumes of the VM %s', name) + log.error("Unable to find volumes of the VM %s", name) # TODO add an option like 'delete_sshkeys' below for volume in volumes: - if volume.extra['volume_type'] != 'DATADISK': + if volume.extra["volume_type"] != "DATADISK": log.info( - 'Ignoring volume type %s: %s', - volume.extra['volume_type'], volume.name + "Ignoring volume type %s: %s", volume.extra["volume_type"], volume.name ) continue - log.info('Detaching volume: %s', volume.name) - __utils__['cloud.fire_event']( - 'event', - 'detaching volume', - 'salt/cloud/{0}/detaching'.format(volume.name), - sock_dir=__opts__['sock_dir'], - args={'name': volume.name}, + log.info("Detaching volume: %s", volume.name) + __utils__["cloud.fire_event"]( + "event", + "detaching volume", + "salt/cloud/{0}/detaching".format(volume.name), + sock_dir=__opts__["sock_dir"], + args={"name": volume.name}, ) if not conn.detach_volume(volume): - log.error('Failed to Detach volume: %s', volume.name) + log.error("Failed to Detach volume: %s", volume.name) return False - log.info('Detached volume: %s', volume.name) - __utils__['cloud.fire_event']( - 'event', - 'detached volume', - 'salt/cloud/{0}/detached'.format(volume.name), - sock_dir=__opts__['sock_dir'], - args={'name': volume.name}, + log.info("Detached volume: %s", volume.name) + __utils__["cloud.fire_event"]( + "event", + "detached volume", + "salt/cloud/{0}/detached".format(volume.name), + sock_dir=__opts__["sock_dir"], + args={"name": volume.name}, ) - log.info('Destroying volume: %s', volume.name) - __utils__['cloud.fire_event']( - 'event', - 'destroying volume', - 'salt/cloud/{0}/destroying'.format(volume.name), - sock_dir=__opts__['sock_dir'], - args={'name': volume.name}, + log.info("Destroying volume: %s", volume.name) + __utils__["cloud.fire_event"]( + "event", + "destroying volume", + "salt/cloud/{0}/destroying".format(volume.name), + sock_dir=__opts__["sock_dir"], + args={"name": volume.name}, ) if not conn.destroy_volume(volume): - log.error('Failed to Destroy volume: %s', volume.name) + log.error("Failed to Destroy volume: %s", volume.name) return False - log.info('Destroyed volume: %s', volume.name) - __utils__['cloud.fire_event']( - 'event', - 'destroyed volume', - 'salt/cloud/{0}/destroyed'.format(volume.name), - sock_dir=__opts__['sock_dir'], - args={'name': volume.name}, + log.info("Destroyed volume: %s", volume.name) + __utils__["cloud.fire_event"]( + "event", + "destroyed volume", + "salt/cloud/{0}/destroyed".format(volume.name), + sock_dir=__opts__["sock_dir"], + args={"name": volume.name}, ) - log.info('Destroying VM: %s', name) + log.info("Destroying VM: %s", name) ret = conn.destroy_node(node) if not ret: - log.error('Failed to Destroy VM: %s', name) + log.error("Failed to Destroy VM: %s", name) return False - log.info('Destroyed VM: %s', name) + log.info("Destroyed VM: %s", name) # Fire destroy action - event = salt.utils.event.SaltEvent('master', __opts__['sock_dir']) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - sock_dir=__opts__['sock_dir'], - args={'name': name}, + event = salt.utils.event.SaltEvent("master", __opts__["sock_dir"]) + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + sock_dir=__opts__["sock_dir"], + args={"name": name}, ) - if __opts__['delete_sshkeys'] is True: + if __opts__["delete_sshkeys"] is True: salt.utils.cloud.remove_sshkey(node.public_ips[0]) return True def block_device_mappings(vm_): - ''' + """ Return the block device mapping: :: [{'DeviceName': '/dev/sdb', 'VirtualName': 'ephemeral0'}, {'DeviceName': '/dev/sdc', 'VirtualName': 'ephemeral1'}] - ''' + """ return config.get_cloud_config_value( - 'block_device_mappings', vm_, __opts__, search_global=True + "block_device_mappings", vm_, __opts__, search_global=True ) def cloudstack_displayname(vm_): - ''' + """ Return display name of VM: :: "minion1" - ''' + """ return config.get_cloud_config_value( - 'cloudstack_displayname', vm_, __opts__, search_global=True + "cloudstack_displayname", vm_, __opts__, search_global=True ) diff --git a/salt/cloud/clouds/digitalocean.py b/salt/cloud/clouds/digitalocean.py index 362356d52a6..7d3e9c7bf04 100644 --- a/salt/cloud/clouds/digitalocean.py +++ b/salt/cloud/clouds/digitalocean.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" DigitalOcean Cloud Module ========================= @@ -23,29 +23,31 @@ under the "SSH Keys" section. driver: digitalocean :depends: requests -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import decimal import logging import os import pprint import time +import salt.config as config + # Import Salt Libs import salt.utils.cloud import salt.utils.files import salt.utils.json import salt.utils.stringutils -import salt.config as config from salt.exceptions import ( SaltCloudConfigError, - SaltInvocationError, + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, SaltCloudNotFound, SaltCloudSystemExit, - SaltCloudExecutionFailure, - SaltCloudExecutionTimeout + SaltInvocationError, ) from salt.ext import six from salt.ext.six.moves import zip @@ -53,6 +55,7 @@ from salt.ext.six.moves import zip # Import Third Party Libs try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False @@ -60,15 +63,15 @@ except ImportError: # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'digitalocean' -__virtual_aliases__ = ('digital_ocean', 'do') +__virtualname__ = "digitalocean" +__virtual_aliases__ = ("digital_ocean", "do") # Only load in this module if the DIGITALOCEAN configurations are in place def __virtual__(): - ''' + """ Check for DigitalOcean configurations - ''' + """ if get_configured_provider() is False: return False @@ -79,56 +82,53 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( opts=__opts__, provider=__active_provider_name__ or __virtualname__, aliases=__virtual_aliases__, - required_keys=('personal_access_token',) + required_keys=("personal_access_token",), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'requests': HAS_REQUESTS} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"requests": HAS_REQUESTS}) def avail_locations(call=None): - ''' + """ Return a dict of all available VM locations on the cloud provider with relevant data - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) - items = query(method='regions') + items = query(method="regions") ret = {} - for region in items['regions']: - ret[region['name']] = {} + for region in items["regions"]: + ret[region["name"]] = {} for item in six.iterkeys(region): - ret[region['name']][item] = six.text_type(region[item]) + ret[region["name"]][item] = six.text_type(region[item]) return ret def avail_images(call=None): - ''' + """ Return a list of the images that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) fetch = True @@ -136,16 +136,18 @@ def avail_images(call=None): ret = {} while fetch: - items = query(method='images', command='?page=' + six.text_type(page) + '&per_page=200') + items = query( + method="images", command="?page=" + six.text_type(page) + "&per_page=200" + ) - for image in items['images']: - ret[image['name']] = {} + for image in items["images"]: + ret[image["name"]] = {} for item in six.iterkeys(image): - ret[image['name']][item] = image[item] + ret[image["name"]][item] = image[item] page += 1 try: - fetch = 'next' in items['links']['pages'] + fetch = "next" in items["links"]["pages"] except KeyError: fetch = False @@ -153,415 +155,446 @@ def avail_images(call=None): def avail_sizes(call=None): - ''' + """ Return a list of the image sizes that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) - items = query(method='sizes', command='?per_page=100') + items = query(method="sizes", command="?per_page=100") ret = {} - for size in items['sizes']: - ret[size['slug']] = {} + for size in items["sizes"]: + ret[size["slug"]] = {} for item in six.iterkeys(size): - ret[size['slug']][item] = six.text_type(size[item]) + ret[size["slug"]][item] = six.text_type(size[item]) return ret def list_nodes(call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) return _list_nodes() def list_nodes_full(call=None, for_output=True): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) return _list_nodes(full=True, for_output=for_output) def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full('function'), __opts__['query.selection'], call, + list_nodes_full("function"), __opts__["query.selection"], call, ) def get_image(vm_): - ''' + """ Return the image object to use - ''' + """ images = avail_images() vm_image = config.get_cloud_config_value( - 'image', vm_, __opts__, search_global=False + "image", vm_, __opts__, search_global=False ) if not isinstance(vm_image, six.string_types): vm_image = six.text_type(vm_image) for image in images: - if vm_image in (images[image]['name'], - images[image]['slug'], - images[image]['id']): - if images[image]['slug'] is not None: - return images[image]['slug'] - return int(images[image]['id']) + if vm_image in ( + images[image]["name"], + images[image]["slug"], + images[image]["id"], + ): + if images[image]["slug"] is not None: + return images[image]["slug"] + return int(images[image]["id"]) raise SaltCloudNotFound( - 'The specified image, \'{0}\', could not be found.'.format(vm_image) + "The specified image, '{0}', could not be found.".format(vm_image) ) def get_size(vm_): - ''' + """ Return the VM's size. Used by create_node(). - ''' + """ sizes = avail_sizes() - vm_size = six.text_type(config.get_cloud_config_value( - 'size', vm_, __opts__, search_global=False - )) + vm_size = six.text_type( + config.get_cloud_config_value("size", vm_, __opts__, search_global=False) + ) for size in sizes: - if vm_size.lower() == sizes[size]['slug']: - return sizes[size]['slug'] + if vm_size.lower() == sizes[size]["slug"]: + return sizes[size]["slug"] raise SaltCloudNotFound( - 'The specified size, \'{0}\', could not be found.'.format(vm_size) + "The specified size, '{0}', could not be found.".format(vm_size) ) def get_location(vm_): - ''' + """ Return the VM's location - ''' + """ locations = avail_locations() - vm_location = six.text_type(config.get_cloud_config_value( - 'location', vm_, __opts__, search_global=False - )) + vm_location = six.text_type( + config.get_cloud_config_value("location", vm_, __opts__, search_global=False) + ) for location in locations: - if vm_location in (locations[location]['name'], - locations[location]['slug']): - return locations[location]['slug'] + if vm_location in (locations[location]["name"], locations[location]["slug"]): + return locations[location]["slug"] raise SaltCloudNotFound( - 'The specified location, \'{0}\', could not be found.'.format( - vm_location - ) + "The specified location, '{0}', could not be found.".format(vm_location) ) def create_node(args): - ''' + """ Create a node - ''' - node = query(method='droplets', args=args, http_method='post') + """ + node = query(method="droplets", args=args, http_method="post") return node def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'digitalocean', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or "digitalocean", + vm_["profile"], + vm_=vm_, + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) kwargs = { - 'name': vm_['name'], - 'size': get_size(vm_), - 'image': get_image(vm_), - 'region': get_location(vm_), - 'ssh_keys': [], - 'tags': [] + "name": vm_["name"], + "size": get_size(vm_), + "image": get_image(vm_), + "region": get_location(vm_), + "ssh_keys": [], + "tags": [], } # backwards compat ssh_key_name = config.get_cloud_config_value( - 'ssh_key_name', vm_, __opts__, search_global=False + "ssh_key_name", vm_, __opts__, search_global=False ) if ssh_key_name: - kwargs['ssh_keys'].append(get_keyid(ssh_key_name)) + kwargs["ssh_keys"].append(get_keyid(ssh_key_name)) ssh_key_names = config.get_cloud_config_value( - 'ssh_key_names', vm_, __opts__, search_global=False, default=False + "ssh_key_names", vm_, __opts__, search_global=False, default=False ) if ssh_key_names: - for key in ssh_key_names.split(','): - kwargs['ssh_keys'].append(get_keyid(key)) + for key in ssh_key_names.split(","): + kwargs["ssh_keys"].append(get_keyid(key)) key_filename = config.get_cloud_config_value( - 'ssh_key_file', vm_, __opts__, search_global=False, default=None + "ssh_key_file", vm_, __opts__, search_global=False, default=None ) if key_filename is not None and not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined key_filename \'{0}\' does not exist'.format( - key_filename - ) + "The defined key_filename '{0}' does not exist".format(key_filename) ) - if not __opts__.get('ssh_agent', False) and key_filename is None: + if not __opts__.get("ssh_agent", False) and key_filename is None: raise SaltCloudConfigError( - 'The DigitalOcean driver requires an ssh_key_file and an ssh_key_name ' - 'because it does not supply a root password upon building the server.' + "The DigitalOcean driver requires an ssh_key_file and an ssh_key_name " + "because it does not supply a root password upon building the server." ) ssh_interface = config.get_cloud_config_value( - 'ssh_interface', vm_, __opts__, search_global=False, default='public' + "ssh_interface", vm_, __opts__, search_global=False, default="public" ) - if ssh_interface in ['private', 'public']: + if ssh_interface in ["private", "public"]: log.info("ssh_interface: Setting interface for ssh to %s", ssh_interface) - kwargs['ssh_interface'] = ssh_interface + kwargs["ssh_interface"] = ssh_interface else: raise SaltCloudConfigError( "The DigitalOcean driver requires ssh_interface to be defined as 'public' or 'private'." ) private_networking = config.get_cloud_config_value( - 'private_networking', vm_, __opts__, search_global=False, default=None, + "private_networking", vm_, __opts__, search_global=False, default=None, ) if private_networking is not None: if not isinstance(private_networking, bool): - raise SaltCloudConfigError("'private_networking' should be a boolean value.") - kwargs['private_networking'] = private_networking + raise SaltCloudConfigError( + "'private_networking' should be a boolean value." + ) + kwargs["private_networking"] = private_networking - if not private_networking and ssh_interface == 'private': + if not private_networking and ssh_interface == "private": raise SaltCloudConfigError( - "The DigitalOcean driver requires ssh_interface if defined as 'private' " - "then private_networking should be set as 'True'." - ) + "The DigitalOcean driver requires ssh_interface if defined as 'private' " + "then private_networking should be set as 'True'." + ) backups_enabled = config.get_cloud_config_value( - 'backups_enabled', vm_, __opts__, search_global=False, default=None, + "backups_enabled", vm_, __opts__, search_global=False, default=None, ) if backups_enabled is not None: if not isinstance(backups_enabled, bool): raise SaltCloudConfigError("'backups_enabled' should be a boolean value.") - kwargs['backups'] = backups_enabled + kwargs["backups"] = backups_enabled ipv6 = config.get_cloud_config_value( - 'ipv6', vm_, __opts__, search_global=False, default=None, + "ipv6", vm_, __opts__, search_global=False, default=None, ) if ipv6 is not None: if not isinstance(ipv6, bool): raise SaltCloudConfigError("'ipv6' should be a boolean value.") - kwargs['ipv6'] = ipv6 + kwargs["ipv6"] = ipv6 monitoring = config.get_cloud_config_value( - 'monitoring', vm_, __opts__, search_global=False, default=None, + "monitoring", vm_, __opts__, search_global=False, default=None, ) if monitoring is not None: if not isinstance(monitoring, bool): raise SaltCloudConfigError("'monitoring' should be a boolean value.") - kwargs['monitoring'] = monitoring + kwargs["monitoring"] = monitoring - kwargs['tags'] = config.get_cloud_config_value( - 'tags', vm_, __opts__, search_global=False, default=False + kwargs["tags"] = config.get_cloud_config_value( + "tags", vm_, __opts__, search_global=False, default=False ) userdata_file = config.get_cloud_config_value( - 'userdata_file', vm_, __opts__, search_global=False, default=None + "userdata_file", vm_, __opts__, search_global=False, default=None ) if userdata_file is not None: try: - with salt.utils.files.fopen(userdata_file, 'r') as fp_: - kwargs['user_data'] = salt.utils.cloud.userdata_template( + with salt.utils.files.fopen(userdata_file, "r") as fp_: + kwargs["user_data"] = salt.utils.cloud.userdata_template( __opts__, vm_, salt.utils.stringutils.to_unicode(fp_.read()) ) except Exception as exc: # pylint: disable=broad-except - log.exception( - 'Failed to read userdata from %s: %s', userdata_file, exc) + log.exception("Failed to read userdata from %s: %s", userdata_file, exc) create_dns_record = config.get_cloud_config_value( - 'create_dns_record', vm_, __opts__, search_global=False, default=None, + "create_dns_record", vm_, __opts__, search_global=False, default=None, ) if create_dns_record: - log.info('create_dns_record: will attempt to write DNS records') + log.info("create_dns_record: will attempt to write DNS records") default_dns_domain = None - dns_domain_name = vm_['name'].split('.') + dns_domain_name = vm_["name"].split(".") if len(dns_domain_name) > 2: - log.debug('create_dns_record: inferring default dns_hostname, dns_domain from minion name as FQDN') - default_dns_hostname = '.'.join(dns_domain_name[:-2]) - default_dns_domain = '.'.join(dns_domain_name[-2:]) + log.debug( + "create_dns_record: inferring default dns_hostname, dns_domain from minion name as FQDN" + ) + default_dns_hostname = ".".join(dns_domain_name[:-2]) + default_dns_domain = ".".join(dns_domain_name[-2:]) else: - log.debug("create_dns_record: can't infer dns_domain from %s", vm_['name']) + log.debug("create_dns_record: can't infer dns_domain from %s", vm_["name"]) default_dns_hostname = dns_domain_name[0] dns_hostname = config.get_cloud_config_value( - 'dns_hostname', vm_, __opts__, search_global=False, default=default_dns_hostname, + "dns_hostname", + vm_, + __opts__, + search_global=False, + default=default_dns_hostname, ) dns_domain = config.get_cloud_config_value( - 'dns_domain', vm_, __opts__, search_global=False, default=default_dns_domain, + "dns_domain", + vm_, + __opts__, + search_global=False, + default=default_dns_domain, ) if dns_hostname and dns_domain: - log.info('create_dns_record: using dns_hostname="%s", dns_domain="%s"', dns_hostname, dns_domain) - __add_dns_addr__ = lambda t, d: post_dns_record(dns_domain=dns_domain, - name=dns_hostname, - record_type=t, - record_data=d) + log.info( + 'create_dns_record: using dns_hostname="%s", dns_domain="%s"', + dns_hostname, + dns_domain, + ) + __add_dns_addr__ = lambda t, d: post_dns_record( + dns_domain=dns_domain, name=dns_hostname, record_type=t, record_data=d + ) - log.debug('create_dns_record: %s', __add_dns_addr__) + log.debug("create_dns_record: %s", __add_dns_addr__) else: - log.error('create_dns_record: could not determine dns_hostname and/or dns_domain') + log.error( + "create_dns_record: could not determine dns_hostname and/or dns_domain" + ) raise SaltCloudConfigError( - '\'create_dns_record\' must be a dict specifying "domain" ' + "'create_dns_record' must be a dict specifying \"domain\" " 'and "hostname" or the minion name must be an FQDN.' ) - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']('requesting', kwargs, list(kwargs)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]("requesting", kwargs, list(kwargs)), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: ret = create_node(kwargs) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on DIGITALOCEAN\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: %s', - vm_['name'], exc, + "Error creating %s on DIGITALOCEAN\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: %s", + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False def __query_node_data(vm_name): - data = show_instance(vm_name, 'action') + data = show_instance(vm_name, "action") if not data: # Trigger an error in the wait_for_ip function return False - if data['networks'].get('v4'): - for network in data['networks']['v4']: - if network['type'] == 'public': + if data["networks"].get("v4"): + for network in data["networks"]["v4"]: + if network["type"] == "public": return data return False try: data = salt.utils.cloud.wait_for_ip( __query_node_data, - update_args=(vm_['name'],), + update_args=(vm_["name"],), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10), + "wait_for_ip_interval", vm_, __opts__, default=10 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) - if not vm_.get('ssh_host'): - vm_['ssh_host'] = None + if not vm_.get("ssh_host"): + vm_["ssh_host"] = None # add DNS records, set ssh_host, default to first found IP, preferring IPv4 for ssh bootstrap script target - addr_families, dns_arec_types = (('v4', 'v6'), ('A', 'AAAA')) + addr_families, dns_arec_types = (("v4", "v6"), ("A", "AAAA")) arec_map = dict(list(zip(addr_families, dns_arec_types))) - for facing, addr_family, ip_address in [(net['type'], family, net['ip_address']) - for family in addr_families - for net in data['networks'][family]]: + for facing, addr_family, ip_address in [ + (net["type"], family, net["ip_address"]) + for family in addr_families + for net in data["networks"][family] + ]: log.info('found %s IP%s interface for "%s"', facing, addr_family, ip_address) dns_rec_type = arec_map[addr_family] - if facing == 'public': + if facing == "public": if create_dns_record: __add_dns_addr__(dns_rec_type, ip_address) if facing == ssh_interface: - if not vm_['ssh_host']: - vm_['ssh_host'] = ip_address + if not vm_["ssh_host"]: + vm_["ssh_host"] = ip_address - if vm_['ssh_host'] is None: + if vm_["ssh_host"] is None: raise SaltCloudSystemExit( - 'No suitable IP addresses found for ssh minion bootstrapping: {0}'.format(repr(data['networks'])) + "No suitable IP addresses found for ssh minion bootstrapping: {0}".format( + repr(data["networks"]) + ) ) log.debug( - 'Found public IP address to use for ssh minion bootstrapping: %s', - vm_['ssh_host'] + "Found public IP address to use for ssh minion bootstrapping: %s", + vm_["ssh_host"], ) - vm_['key_filename'] = key_filename - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + vm_["key_filename"] = key_filename + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data) - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret -def query(method='droplets', droplet_id=None, command=None, args=None, http_method='get'): - ''' +def query( + method="droplets", droplet_id=None, command=None, args=None, http_method="get" +): + """ Make a web call to DigitalOcean - ''' - base_path = six.text_type(config.get_cloud_config_value( - 'api_root', - get_configured_provider(), - __opts__, - search_global=False, - default='https://api.digitalocean.com/v2' - )) + """ + base_path = six.text_type( + config.get_cloud_config_value( + "api_root", + get_configured_provider(), + __opts__, + search_global=False, + default="https://api.digitalocean.com/v2", + ) + ) - path = '{0}/{1}/'.format(base_path, method) + path = "{0}/{1}/".format(base_path, method) if droplet_id: - path += '{0}/'.format(droplet_id) + path += "{0}/".format(droplet_id) if command: path += command @@ -570,20 +603,30 @@ def query(method='droplets', droplet_id=None, command=None, args=None, http_meth args = {} personal_access_token = config.get_cloud_config_value( - 'personal_access_token', get_configured_provider(), __opts__, search_global=False + "personal_access_token", + get_configured_provider(), + __opts__, + search_global=False, ) data = salt.utils.json.dumps(args) requester = getattr(requests, http_method) - request = requester(path, data=data, headers={'Authorization': 'Bearer ' + personal_access_token, 'Content-Type': 'application/json'}) + request = requester( + path, + data=data, + headers={ + "Authorization": "Bearer " + personal_access_token, + "Content-Type": "application/json", + }, + ) if request.status_code > 299: raise SaltCloudSystemExit( - 'An error occurred while querying DigitalOcean. HTTP Code: {0} ' - 'Error: \'{1}\''.format( + "An error occurred while querying DigitalOcean. HTTP Code: {0} " + "Error: '{1}'".format( request.status_code, # request.read() - request.text + request.text, ) ) @@ -596,39 +639,37 @@ def query(method='droplets', droplet_id=None, command=None, args=None, http_meth content = request.text result = salt.utils.json.loads(content) - if result.get('status', '').lower() == 'error': - raise SaltCloudSystemExit( - pprint.pformat(result.get('error_message', {})) - ) + if result.get("status", "").lower() == "error": + raise SaltCloudSystemExit(pprint.pformat(result.get("error_message", {}))) return result def script(vm_): - ''' + """ Return the script deployment object - ''' + """ deploy_script = salt.utils.cloud.os_script( - config.get_cloud_config_value('script', vm_, __opts__), + config.get_cloud_config_value("script", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) return deploy_script def show_instance(name, call=None): - ''' + """ Show the details from DigitalOcean concerning a droplet - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) node = _get_node(name) - __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](node, __active_provider_name__, __opts__) return node @@ -640,8 +681,9 @@ def _get_node(name): except KeyError: attempts -= 1 log.debug( - 'Failed to get the data for node \'%s\'. Remaining ' - 'attempts: %s', name, attempts + "Failed to get the data for node '%s'. Remaining " "attempts: %s", + name, + attempts, ) # Just a little delay between attempts... time.sleep(0.5) @@ -649,14 +691,12 @@ def _get_node(name): def list_keypairs(call=None): - ''' + """ Return a dict of all available VM locations on the cloud provider with relevant data - ''' - if call != 'function': - log.error( - 'The list_keypairs function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The list_keypairs function must be called with -f or --function.") return False fetch = True @@ -664,19 +704,19 @@ def list_keypairs(call=None): ret = {} while fetch: - items = query(method='account/keys', command='?page=' + six.text_type(page) + - '&per_page=100') + items = query( + method="account/keys", + command="?page=" + six.text_type(page) + "&per_page=100", + ) - for key_pair in items['ssh_keys']: - name = key_pair['name'] + for key_pair in items["ssh_keys"]: + name = key_pair["name"] if name in ret: raise SaltCloudSystemExit( - 'A duplicate key pair name, \'{0}\', was found in DigitalOcean\'s ' - 'key pair list. Please change the key name stored by DigitalOcean. ' - 'Be sure to adjust the value of \'ssh_key_file\' in your cloud ' - 'profile or provider configuration, if necessary.'.format( - name - ) + "A duplicate key pair name, '{0}', was found in DigitalOcean's " + "key pair list. Please change the key name stored by DigitalOcean. " + "Be sure to adjust the value of 'ssh_key_file' in your cloud " + "profile or provider configuration, if necessary.".format(name) ) ret[name] = {} for item in six.iterkeys(key_pair): @@ -684,7 +724,7 @@ def list_keypairs(call=None): page += 1 try: - fetch = 'next' in items['links']['pages'] + fetch = "next" in items["links"]["pages"] except KeyError: fetch = False @@ -692,33 +732,31 @@ def list_keypairs(call=None): def show_keypair(kwargs=None, call=None): - ''' + """ Show the details of an SSH keypair - ''' - if call != 'function': - log.error( - 'The show_keypair function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The show_keypair function must be called with -f or --function.") return False if not kwargs: kwargs = {} - if 'keyname' not in kwargs: - log.error('A keyname is required.') + if "keyname" not in kwargs: + log.error("A keyname is required.") return False - keypairs = list_keypairs(call='function') - keyid = keypairs[kwargs['keyname']]['id'] - log.debug('Key ID is %s', keyid) + keypairs = list_keypairs(call="function") + keyid = keypairs[kwargs["keyname"]]["id"] + log.debug("Key ID is %s", keyid) - details = query(method='account/keys', command=keyid) + details = query(method="account/keys", command=keyid) return details def import_keypair(kwargs=None, call=None): - ''' + """ Upload public key to cloud provider. Similar to EC2 import_keypair. @@ -727,82 +765,74 @@ def import_keypair(kwargs=None, call=None): kwargs file(mandatory): public key file-name keyname(mandatory): public key name in the provider - ''' - with salt.utils.files.fopen(kwargs['file'], 'r') as public_key_filename: - public_key_content = salt.utils.stringutils.to_unicode(public_key_filename.read()) + """ + with salt.utils.files.fopen(kwargs["file"], "r") as public_key_filename: + public_key_content = salt.utils.stringutils.to_unicode( + public_key_filename.read() + ) - digitalocean_kwargs = { - 'name': kwargs['keyname'], - 'public_key': public_key_content - } + digitalocean_kwargs = {"name": kwargs["keyname"], "public_key": public_key_content} created_result = create_key(digitalocean_kwargs, call=call) return created_result def create_key(kwargs=None, call=None): - ''' + """ Upload a public key - ''' - if call != 'function': - log.error( - 'The create_key function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The create_key function must be called with -f or --function.") return False try: result = query( - method='account', - command='keys', - args={'name': kwargs['name'], - 'public_key': kwargs['public_key']}, - http_method='post' + method="account", + command="keys", + args={"name": kwargs["name"], "public_key": kwargs["public_key"]}, + http_method="post", ) except KeyError: - log.info('`name` and `public_key` arguments must be specified') + log.info("`name` and `public_key` arguments must be specified") return False return result def remove_key(kwargs=None, call=None): - ''' + """ Delete public key - ''' - if call != 'function': - log.error( - 'The create_key function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The create_key function must be called with -f or --function.") return False try: result = query( - method='account', - command='keys/' + kwargs['id'], - http_method='delete' + method="account", command="keys/" + kwargs["id"], http_method="delete" ) except KeyError: - log.info('`id` argument must be specified') + log.info("`id` argument must be specified") return False return result def get_keyid(keyname): - ''' + """ Return the ID of the keyname - ''' + """ if not keyname: return None - keypairs = list_keypairs(call='function') - keyid = keypairs[keyname]['id'] + keypairs = list_keypairs(call="function") + keyid = keypairs[keyname]["id"] if keyid: return keyid - raise SaltCloudNotFound('The specified ssh key could not be found.') + raise SaltCloudNotFound("The specified ssh key could not be found.") def destroy(name, call=None): - ''' + """ Destroy a node. Will check termination protection and warn if enabled. CLI Example: @@ -810,24 +840,23 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud --destroy mymachine - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - data = show_instance(name, call='action') - node = query(method='droplets', droplet_id=data['id'], http_method='delete') + data = show_instance(name, call="action") + node = query(method="droplets", droplet_id=data["id"], http_method="delete") ## This is all terribly optomistic: # vm_ = get_vm_config(name=name) @@ -840,11 +869,9 @@ def destroy(name, call=None): delete_dns_record = True if not isinstance(delete_dns_record, bool): - raise SaltCloudConfigError( - '\'delete_dns_record\' should be a boolean value.' - ) + raise SaltCloudConfigError("'delete_dns_record' should be a boolean value.") # When the "to do" a few lines up is resolved, remove these lines and use the if/else logic below. - log.debug('Deleting DNS records for %s.', name) + log.debug("Deleting DNS records for %s.", name) destroy_dns_records(name) # Until the "to do" from line 754 is taken care of, we don't need this logic. @@ -856,46 +883,54 @@ def destroy(name, call=None): # for line in pprint.pformat(dir()).splitlines(): # log.debug('delete context: %s', line) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) return node def post_dns_record(**kwargs): - ''' + """ Creates a DNS record for the given name if the domain is managed with DO. - ''' - if 'kwargs' in kwargs: # flatten kwargs if called via salt-cloud -f - f_kwargs = kwargs['kwargs'] - del kwargs['kwargs'] + """ + if "kwargs" in kwargs: # flatten kwargs if called via salt-cloud -f + f_kwargs = kwargs["kwargs"] + del kwargs["kwargs"] kwargs.update(f_kwargs) - mandatory_kwargs = ('dns_domain', 'name', 'record_type', 'record_data') + mandatory_kwargs = ("dns_domain", "name", "record_type", "record_data") for i in mandatory_kwargs: if kwargs[i]: pass else: - error = '{0}="{1}" ## all mandatory args must be provided: {2}'.format(i, kwargs[i], mandatory_kwargs) + error = '{0}="{1}" ## all mandatory args must be provided: {2}'.format( + i, kwargs[i], mandatory_kwargs + ) raise SaltInvocationError(error) - domain = query(method='domains', droplet_id=kwargs['dns_domain']) + domain = query(method="domains", droplet_id=kwargs["dns_domain"]) if domain: result = query( - method='domains', - droplet_id=kwargs['dns_domain'], - command='records', - args={'type': kwargs['record_type'], 'name': kwargs['name'], 'data': kwargs['record_data']}, - http_method='post' + method="domains", + droplet_id=kwargs["dns_domain"], + command="records", + args={ + "type": kwargs["record_type"], + "name": kwargs["name"], + "data": kwargs["record_data"], + }, + http_method="post", ) return result @@ -903,41 +938,43 @@ def post_dns_record(**kwargs): def destroy_dns_records(fqdn): - ''' + """ Deletes DNS records for the given hostname if the domain is managed with DO. - ''' - domain = '.'.join(fqdn.split('.')[-2:]) - hostname = '.'.join(fqdn.split('.')[:-2]) + """ + domain = ".".join(fqdn.split(".")[-2:]) + hostname = ".".join(fqdn.split(".")[:-2]) # TODO: remove this when the todo on 754 is available try: - response = query(method='domains', droplet_id=domain, command='records') + response = query(method="domains", droplet_id=domain, command="records") except SaltCloudSystemExit: - log.debug('Failed to find domains.') + log.debug("Failed to find domains.") return False log.debug("found DNS records: %s", pprint.pformat(response)) - records = response['domain_records'] + records = response["domain_records"] if records: - record_ids = [r['id'] for r in records if r['name'].decode() == hostname] + record_ids = [r["id"] for r in records if r["name"].decode() == hostname] log.debug("deleting DNS record IDs: %s", record_ids) for id_ in record_ids: try: - log.info('deleting DNS record %s', id_) + log.info("deleting DNS record %s", id_) ret = query( - method='domains', + method="domains", droplet_id=domain, - command='records/{0}'.format(id_), - http_method='delete' + command="records/{0}".format(id_), + http_method="delete", ) except SaltCloudSystemExit: - log.error('failed to delete DNS domain %s record ID %s.', domain, hostname) - log.debug('DNS deletion REST call returned: %s', pprint.pformat(ret)) + log.error( + "failed to delete DNS domain %s record ID %s.", domain, hostname + ) + log.debug("DNS deletion REST call returned: %s", pprint.pformat(ret)) return False def show_pricing(kwargs=None, call=None): - ''' + """ Show pricing for a particular profile. This is only an estimate, based on unofficial pricing sources. @@ -948,35 +985,35 @@ def show_pricing(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_pricing my-digitalocean-config profile=my-profile - ''' - profile = __opts__['profiles'].get(kwargs['profile'], {}) + """ + profile = __opts__["profiles"].get(kwargs["profile"], {}) if not profile: - return {'Error': 'The requested profile was not found'} + return {"Error": "The requested profile was not found"} # Make sure the profile belongs to DigitalOcean - provider = profile.get('provider', '0:0') - comps = provider.split(':') - if len(comps) < 2 or comps[1] != 'digitalocean': - return {'Error': 'The requested profile does not belong to DigitalOcean'} + provider = profile.get("provider", "0:0") + comps = provider.split(":") + if len(comps) < 2 or comps[1] != "digitalocean": + return {"Error": "The requested profile does not belong to DigitalOcean"} raw = {} ret = {} sizes = avail_sizes() - ret['per_hour'] = decimal.Decimal(sizes[profile['size']]['price_hourly']) + ret["per_hour"] = decimal.Decimal(sizes[profile["size"]]["price_hourly"]) - ret['per_day'] = ret['per_hour'] * 24 - ret['per_week'] = ret['per_day'] * 7 - ret['per_month'] = decimal.Decimal(sizes[profile['size']]['price_monthly']) - ret['per_year'] = ret['per_week'] * 52 + ret["per_day"] = ret["per_hour"] * 24 + ret["per_week"] = ret["per_day"] * 7 + ret["per_month"] = decimal.Decimal(sizes[profile["size"]]["price_monthly"]) + ret["per_year"] = ret["per_week"] * 52 - if kwargs.get('raw', False): - ret['_raw'] = raw + if kwargs.get("raw", False): + ret["_raw"] = raw - return {profile['profile']: ret} + return {profile["profile"]: ret} def list_floating_ips(call=None): - ''' + """ Return a list of the floating ips that are on the provider .. versionadded:: 2016.3.0 @@ -986,11 +1023,11 @@ def list_floating_ips(call=None): .. code-block:: bash salt-cloud -f list_floating_ips my-digitalocean-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_floating_ips function must be called with ' - '-f or --function, or with the --list-floating-ips option' + "The list_floating_ips function must be called with " + "-f or --function, or with the --list-floating-ips option" ) fetch = True @@ -998,17 +1035,19 @@ def list_floating_ips(call=None): ret = {} while fetch: - items = query(method='floating_ips', - command='?page=' + six.text_type(page) + '&per_page=200') + items = query( + method="floating_ips", + command="?page=" + six.text_type(page) + "&per_page=200", + ) - for floating_ip in items['floating_ips']: - ret[floating_ip['ip']] = {} + for floating_ip in items["floating_ips"]: + ret[floating_ip["ip"]] = {} for item in six.iterkeys(floating_ip): - ret[floating_ip['ip']][item] = floating_ip[item] + ret[floating_ip["ip"]][item] = floating_ip[item] page += 1 try: - fetch = 'next' in items['links']['pages'] + fetch = "next" in items["links"]["pages"] except KeyError: fetch = False @@ -1016,7 +1055,7 @@ def list_floating_ips(call=None): def show_floating_ip(kwargs=None, call=None): - ''' + """ Show the details of a floating IP .. versionadded:: 2016.3.0 @@ -1026,30 +1065,28 @@ def show_floating_ip(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_floating_ip my-digitalocean-config floating_ip='45.55.96.47' - ''' - if call != 'function': - log.error( - 'The show_floating_ip function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The show_floating_ip function must be called with -f or --function.") return False if not kwargs: kwargs = {} - if 'floating_ip' not in kwargs: - log.error('A floating IP is required.') + if "floating_ip" not in kwargs: + log.error("A floating IP is required.") return False - floating_ip = kwargs['floating_ip'] - log.debug('Floating ip is %s', floating_ip) + floating_ip = kwargs["floating_ip"] + log.debug("Floating ip is %s", floating_ip) - details = query(method='floating_ips', command=floating_ip) + details = query(method="floating_ips", command=floating_ip) return details def create_floating_ip(kwargs=None, call=None): - ''' + """ Create a new floating IP .. versionadded:: 2016.3.0 @@ -1061,37 +1098,39 @@ def create_floating_ip(kwargs=None, call=None): salt-cloud -f create_floating_ip my-digitalocean-config region='NYC2' salt-cloud -f create_floating_ip my-digitalocean-config droplet_id='1234567' - ''' - if call != 'function': + """ + if call != "function": log.error( - 'The create_floating_ip function must be called with -f or --function.' + "The create_floating_ip function must be called with -f or --function." ) return False if not kwargs: kwargs = {} - if 'droplet_id' in kwargs: - result = query(method='floating_ips', - args={'droplet_id': kwargs['droplet_id']}, - http_method='post') + if "droplet_id" in kwargs: + result = query( + method="floating_ips", + args={"droplet_id": kwargs["droplet_id"]}, + http_method="post", + ) return result - elif 'region' in kwargs: - result = query(method='floating_ips', - args={'region': kwargs['region']}, - http_method='post') + elif "region" in kwargs: + result = query( + method="floating_ips", args={"region": kwargs["region"]}, http_method="post" + ) return result else: - log.error('A droplet_id or region is required.') + log.error("A droplet_id or region is required.") return False def delete_floating_ip(kwargs=None, call=None): - ''' + """ Delete a floating IP .. versionadded:: 2016.3.0 @@ -1101,32 +1140,30 @@ def delete_floating_ip(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_floating_ip my-digitalocean-config floating_ip='45.55.96.47' - ''' - if call != 'function': + """ + if call != "function": log.error( - 'The delete_floating_ip function must be called with -f or --function.' + "The delete_floating_ip function must be called with -f or --function." ) return False if not kwargs: kwargs = {} - if 'floating_ip' not in kwargs: - log.error('A floating IP is required.') + if "floating_ip" not in kwargs: + log.error("A floating IP is required.") return False - floating_ip = kwargs['floating_ip'] - log.debug('Floating ip is %s', kwargs['floating_ip']) + floating_ip = kwargs["floating_ip"] + log.debug("Floating ip is %s", kwargs["floating_ip"]) - result = query(method='floating_ips', - command=floating_ip, - http_method='delete') + result = query(method="floating_ips", command=floating_ip, http_method="delete") return result def assign_floating_ip(kwargs=None, call=None): - ''' + """ Assign a floating IP .. versionadded:: 2016.3.0 @@ -1136,30 +1173,32 @@ def assign_floating_ip(kwargs=None, call=None): .. code-block:: bash salt-cloud -f assign_floating_ip my-digitalocean-config droplet_id=1234567 floating_ip='45.55.96.47' - ''' - if call != 'function': + """ + if call != "function": log.error( - 'The assign_floating_ip function must be called with -f or --function.' + "The assign_floating_ip function must be called with -f or --function." ) return False if not kwargs: kwargs = {} - if 'floating_ip' and 'droplet_id' not in kwargs: - log.error('A floating IP and droplet_id is required.') + if "floating_ip" and "droplet_id" not in kwargs: + log.error("A floating IP and droplet_id is required.") return False - result = query(method='floating_ips', - command=kwargs['floating_ip'] + '/actions', - args={'droplet_id': kwargs['droplet_id'], 'type': 'assign'}, - http_method='post') + result = query( + method="floating_ips", + command=kwargs["floating_ip"] + "/actions", + args={"droplet_id": kwargs["droplet_id"], "type": "assign"}, + http_method="post", + ) return result def unassign_floating_ip(kwargs=None, call=None): - ''' + """ Unassign a floating IP .. versionadded:: 2016.3.0 @@ -1169,59 +1208,62 @@ def unassign_floating_ip(kwargs=None, call=None): .. code-block:: bash salt-cloud -f unassign_floating_ip my-digitalocean-config floating_ip='45.55.96.47' - ''' - if call != 'function': + """ + if call != "function": log.error( - 'The inassign_floating_ip function must be called with -f or --function.' + "The inassign_floating_ip function must be called with -f or --function." ) return False if not kwargs: kwargs = {} - if 'floating_ip' not in kwargs: - log.error('A floating IP is required.') + if "floating_ip" not in kwargs: + log.error("A floating IP is required.") return False - result = query(method='floating_ips', - command=kwargs['floating_ip'] + '/actions', - args={'type': 'unassign'}, - http_method='post') + result = query( + method="floating_ips", + command=kwargs["floating_ip"] + "/actions", + args={"type": "unassign"}, + http_method="post", + ) return result def _list_nodes(full=False, for_output=False): - ''' + """ Helper function to format and parse node data. - ''' + """ fetch = True page = 1 ret = {} while fetch: - items = query(method='droplets', - command='?page=' + six.text_type(page) + '&per_page=200') - for node in items['droplets']: - name = node['name'] + items = query( + method="droplets", command="?page=" + six.text_type(page) + "&per_page=200" + ) + for node in items["droplets"]: + name = node["name"] ret[name] = {} if full: ret[name] = _get_full_output(node, for_output=for_output) else: - public_ips, private_ips = _get_ips(node['networks']) + public_ips, private_ips = _get_ips(node["networks"]) ret[name] = { - 'id': node['id'], - 'image': node['image']['name'], - 'name': name, - 'private_ips': private_ips, - 'public_ips': public_ips, - 'size': node['size_slug'], - 'state': six.text_type(node['status']), + "id": node["id"], + "image": node["image"]["name"], + "name": name, + "private_ips": private_ips, + "public_ips": public_ips, + "size": node["size_slug"], + "state": six.text_type(node["status"]), } page += 1 try: - fetch = 'next' in items['links']['pages'] + fetch = "next" in items["links"]["pages"] except KeyError: fetch = False @@ -1229,7 +1271,7 @@ def _list_nodes(full=False, for_output=False): def reboot(name, call=None): - ''' + """ Reboot a droplet in DigitalOcean. .. versionadded:: 2015.8.8 @@ -1242,31 +1284,37 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot droplet_name - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The restart action must be called with -a or --action.' + "The restart action must be called with -a or --action." ) - data = show_instance(name, call='action') - if data.get('status') == 'off': - return {'success': True, - 'action': 'stop', - 'status': 'off', - 'msg': 'Machine is already off.'} + data = show_instance(name, call="action") + if data.get("status") == "off": + return { + "success": True, + "action": "stop", + "status": "off", + "msg": "Machine is already off.", + } - ret = query(droplet_id=data['id'], - command='actions', - args={'type': 'reboot'}, - http_method='post') + ret = query( + droplet_id=data["id"], + command="actions", + args={"type": "reboot"}, + http_method="post", + ) - return {'success': True, - 'action': ret['action']['type'], - 'state': ret['action']['status']} + return { + "success": True, + "action": ret["action"]["type"], + "state": ret["action"]["status"], + } def start(name, call=None): - ''' + """ Start a droplet in DigitalOcean. .. versionadded:: 2015.8.8 @@ -1279,31 +1327,37 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start droplet_name - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The start action must be called with -a or --action.' + "The start action must be called with -a or --action." ) - data = show_instance(name, call='action') - if data.get('status') == 'active': - return {'success': True, - 'action': 'start', - 'status': 'active', - 'msg': 'Machine is already running.'} + data = show_instance(name, call="action") + if data.get("status") == "active": + return { + "success": True, + "action": "start", + "status": "active", + "msg": "Machine is already running.", + } - ret = query(droplet_id=data['id'], - command='actions', - args={'type': 'power_on'}, - http_method='post') + ret = query( + droplet_id=data["id"], + command="actions", + args={"type": "power_on"}, + http_method="post", + ) - return {'success': True, - 'action': ret['action']['type'], - 'state': ret['action']['status']} + return { + "success": True, + "action": ret["action"]["type"], + "state": ret["action"]["status"], + } def stop(name, call=None): - ''' + """ Stop a droplet in DigitalOcean. .. versionadded:: 2015.8.8 @@ -1316,34 +1370,38 @@ def stop(name, call=None): .. code-block:: bash salt-cloud -a stop droplet_name - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - data = show_instance(name, call='action') - if data.get('status') == 'off': - return {'success': True, - 'action': 'stop', - 'status': 'off', - 'msg': 'Machine is already off.'} + data = show_instance(name, call="action") + if data.get("status") == "off": + return { + "success": True, + "action": "stop", + "status": "off", + "msg": "Machine is already off.", + } - ret = query(droplet_id=data['id'], - command='actions', - args={'type': 'shutdown'}, - http_method='post') + ret = query( + droplet_id=data["id"], + command="actions", + args={"type": "shutdown"}, + http_method="post", + ) - return {'success': True, - 'action': ret['action']['type'], - 'state': ret['action']['status']} + return { + "success": True, + "action": ret["action"]["type"], + "state": ret["action"]["status"], + } def _get_full_output(node, for_output=False): - ''' + """ Helper function for _list_nodes to loop through all node information. Returns a dictionary containing the full information of a node. - ''' + """ ret = {} for item in six.iterkeys(node): value = node[item] @@ -1354,31 +1412,31 @@ def _get_full_output(node, for_output=False): def _get_ips(networks): - ''' + """ Helper function for list_nodes. Returns public and private ip lists based on a given network dictionary. - ''' - v4s = networks.get('v4') - v6s = networks.get('v6') + """ + v4s = networks.get("v4") + v6s = networks.get("v6") public_ips = [] private_ips = [] if v4s: for item in v4s: - ip_type = item.get('type') - ip_address = item.get('ip_address') - if ip_type == 'public': + ip_type = item.get("type") + ip_address = item.get("ip_address") + if ip_type == "public": public_ips.append(ip_address) - if ip_type == 'private': + if ip_type == "private": private_ips.append(ip_address) if v6s: for item in v6s: - ip_type = item.get('type') - ip_address = item.get('ip_address') - if ip_type == 'public': + ip_type = item.get("type") + ip_address = item.get("ip_address") + if ip_type == "public": public_ips.append(ip_address) - if ip_type == 'private': + if ip_type == "private": private_ips.append(ip_address) return public_ips, private_ips diff --git a/salt/cloud/clouds/dimensiondata.py b/salt/cloud/clouds/dimensiondata.py index 3145e4e3e06..48deae87c11 100644 --- a/salt/cloud/clouds/dimensiondata.py +++ b/salt/cloud/clouds/dimensiondata.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Dimension Data Cloud Module =========================== @@ -20,13 +20,24 @@ using the existing Libcloud driver for Dimension Data. :maintainer: Anthony Shaw :depends: libcloud >= 1.2.1 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import socket import pprint +import socket + +import salt.config as config +import salt.utils.cloud +from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import +from salt.exceptions import ( + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, + SaltCloudSystemExit, +) +from salt.utils.functools import namespaced_function from salt.utils.versions import LooseVersion as _LooseVersion # Import libcloud @@ -45,27 +56,19 @@ try: # this work-around. This work-around can be removed when the required # minimum version of libcloud is 2.0.0 (See PR #40837 - which is # implemented in Salt 2018.3.0). - if _LooseVersion(libcloud.__version__) < _LooseVersion('1.4.0'): + if _LooseVersion(libcloud.__version__) < _LooseVersion("1.4.0"): # See https://github.com/saltstack/salt/issues/32743 import libcloud.security - libcloud.security.CA_CERTS_PATH.append('/etc/ssl/certs/YaST-CA.pem') + + libcloud.security.CA_CERTS_PATH.append("/etc/ssl/certs/YaST-CA.pem") HAS_LIBCLOUD = True except ImportError: HAS_LIBCLOUD = False -# Import salt.cloud libs -from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import -from salt.utils.functools import namespaced_function -import salt.utils.cloud -import salt.config as config -from salt.exceptions import ( - SaltCloudSystemExit, - SaltCloudExecutionFailure, - SaltCloudExecutionTimeout -) try: from netaddr import all_matching_cidrs # pylint: disable=unused-import + HAS_NETADDR = True except ImportError: HAS_NETADDR = False @@ -91,63 +94,62 @@ get_node = namespaced_function(get_node, globals()) # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'dimensiondata' +__virtualname__ = "dimensiondata" def __virtual__(): - ''' + """ Set up the libcloud functions and check for dimensiondata configurations. - ''' + """ if get_configured_provider() is False: return False if get_dependencies() is False: return False - for provider, details in six.iteritems(__opts__['providers']): - if 'dimensiondata' not in details: + for provider, details in six.iteritems(__opts__["providers"]): + if "dimensiondata" not in details: continue return __virtualname__ def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, - __active_provider_name__ or 'dimensiondata', - ('user_id', 'key', 'region') + __active_provider_name__ or "dimensiondata", + ("user_id", "key", "region"), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - deps = { - 'libcloud': HAS_LIBCLOUD, - 'netaddr': HAS_NETADDR - } - return config.check_driver_dependencies( - __virtualname__, - deps - ) + """ + deps = {"libcloud": HAS_LIBCLOUD, "netaddr": HAS_NETADDR} + return config.check_driver_dependencies(__virtualname__, deps) def _query_node_data(vm_, data): running = False try: - node = show_instance(vm_['name'], 'action') # pylint: disable=not-callable - running = (node['state'] == NodeState.RUNNING) - log.debug('Loaded node data for %s:\nname: %s\nstate: %s', - vm_['name'], pprint.pformat(node['name']), node['state']) + node = show_instance(vm_["name"], "action") # pylint: disable=not-callable + running = node["state"] == NodeState.RUNNING + log.debug( + "Loaded node data for %s:\nname: %s\nstate: %s", + vm_["name"], + pprint.pformat(node["name"]), + node["state"], + ) except Exception as err: # pylint: disable=broad-except log.error( - 'Failed to get nodes list: %s', err, + "Failed to get nodes list: %s", + err, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) # Trigger a failure in the wait for IP function return running @@ -156,198 +158,212 @@ def _query_node_data(vm_, data): # Still not running, trigger another iteration return - private = node['private_ips'] - public = node['public_ips'] + private = node["private_ips"] + public = node["public_ips"] if private and not public: - log.warning('Private IPs returned, but not public. Checking for misidentified IPs.') + log.warning( + "Private IPs returned, but not public. Checking for misidentified IPs." + ) for private_ip in private: private_ip = preferred_ip(vm_, [private_ip]) if private_ip is False: continue if salt.utils.cloud.is_public_ip(private_ip): - log.warning('%s is a public IP', private_ip) + log.warning("%s is a public IP", private_ip) data.public_ips.append(private_ip) else: - log.warning('%s is a private IP', private_ip) + log.warning("%s is a private IP", private_ip) if private_ip not in data.private_ips: data.private_ips.append(private_ip) - if ssh_interface(vm_) == 'private_ips' and data.private_ips: + if ssh_interface(vm_) == "private_ips" and data.private_ips: return data if private: data.private_ips = private - if ssh_interface(vm_) == 'private_ips': + if ssh_interface(vm_) == "private_ips": return data if public: data.public_ips = public - if ssh_interface(vm_) != 'private_ips': + if ssh_interface(vm_) != "private_ips": return data - log.debug('Contents of the node data:') + log.debug("Contents of the node data:") log.debug(data) def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured( - __opts__, - __active_provider_name__ or 'dimensiondata', - vm_['profile']) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "dimensiondata", vm_["profile"] + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) conn = get_conn() - location = conn.ex_get_location_by_id(vm_['location']) + location = conn.ex_get_location_by_id(vm_["location"]) images = conn.list_images(location=location) - image = [x for x in images if x.id == vm_['image']][0] + image = [x for x in images if x.id == vm_["image"]][0] network_domains = conn.ex_list_network_domains(location=location) try: - network_domain = [y for y in network_domains - if y.name == vm_['network_domain']][0] + network_domain = [ + y for y in network_domains if y.name == vm_["network_domain"] + ][0] except IndexError: network_domain = conn.ex_create_network_domain( location=location, - name=vm_['network_domain'], - plan='ADVANCED', - description='' + name=vm_["network_domain"], + plan="ADVANCED", + description="", ) try: - vlan = [y for y in conn.ex_list_vlans( - location=location, - network_domain=network_domain) - if y.name == vm_['vlan']][0] + vlan = [ + y + for y in conn.ex_list_vlans( + location=location, network_domain=network_domain + ) + if y.name == vm_["vlan"] + ][0] except (IndexError, KeyError): # Use the first VLAN in the network domain - vlan = conn.ex_list_vlans( - location=location, - network_domain=network_domain)[0] + vlan = conn.ex_list_vlans(location=location, network_domain=network_domain)[0] kwargs = { - 'name': vm_['name'], - 'image': image, - 'ex_description': vm_['description'], - 'ex_network_domain': network_domain, - 'ex_vlan': vlan, - 'ex_is_started': vm_['is_started'] + "name": vm_["name"], + "image": image, + "ex_description": vm_["description"], + "ex_network_domain": network_domain, + "ex_vlan": vlan, + "ex_is_started": vm_["is_started"], } event_data = _to_event_data(kwargs) - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']('requesting', event_data, list(event_data)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "requesting", event_data, list(event_data) + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) # Initial password (excluded from event payload) - initial_password = NodeAuthPassword(vm_['auth']) - kwargs['auth'] = initial_password + initial_password = NodeAuthPassword(vm_["auth"]) + kwargs["auth"] = initial_password try: data = conn.create_node(**kwargs) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on DIMENSIONDATA\n\n' - 'The following exception was thrown by libcloud when trying to ' - 'run the initial deployment: \n%s', - vm_['name'], exc, - exc_info_on_loglevel=logging.DEBUG + "Error creating %s on DIMENSIONDATA\n\n" + "The following exception was thrown by libcloud when trying to " + "run the initial deployment: \n%s", + vm_["name"], + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False try: - data = __utils__['cloud.wait_for_ip']( + data = __utils__["cloud.wait_for_ip"]( _query_node_data, update_args=(vm_, data), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=25 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=25 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=30), + "wait_for_ip_interval", vm_, __opts__, default=30 + ), max_failures=config.get_cloud_config_value( - 'wait_for_ip_max_failures', vm_, __opts__, default=60), + "wait_for_ip_max_failures", vm_, __opts__, default=60 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) # pylint: disable=not-callable + destroy(vm_["name"]) # pylint: disable=not-callable except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) - log.debug('VM is now running') - if ssh_interface(vm_) == 'private_ips': + log.debug("VM is now running") + if ssh_interface(vm_) == "private_ips": ip_address = preferred_ip(vm_, data.private_ips) else: ip_address = preferred_ip(vm_, data.public_ips) - log.debug('Using IP address %s', ip_address) + log.debug("Using IP address %s", ip_address) - if __utils__['cloud.get_salt_interface'](vm_, __opts__) == 'private_ips': + if __utils__["cloud.get_salt_interface"](vm_, __opts__) == "private_ips": salt_ip_address = preferred_ip(vm_, data.private_ips) - log.info('Salt interface set to: %s', salt_ip_address) + log.info("Salt interface set to: %s", salt_ip_address) else: salt_ip_address = preferred_ip(vm_, data.public_ips) - log.debug('Salt interface set to: %s', salt_ip_address) + log.debug("Salt interface set to: %s", salt_ip_address) if not ip_address: - raise SaltCloudSystemExit( - 'No IP addresses could be found.' - ) + raise SaltCloudSystemExit("No IP addresses could be found.") - vm_['salt_host'] = salt_ip_address - vm_['ssh_host'] = ip_address - vm_['password'] = vm_['auth'] + vm_["salt_host"] = salt_ip_address + vm_["ssh_host"] = ip_address + vm_["password"] = vm_["auth"] - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data.__dict__) - if 'password' in data.extra: - del data.extra['password'] + if "password" in data.extra: + del data.extra["password"] - log.info('Created Cloud VM \'%s\'', vm_['name']) + log.info("Created Cloud VM '%s'", vm_["name"]) log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data.__dict__) + "'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data.__dict__) ) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def create_lb(kwargs=None, call=None): - r''' + r""" Create a load-balancer configuration. CLI Example: @@ -356,109 +372,102 @@ def create_lb(kwargs=None, call=None): salt-cloud -f create_lb dimensiondata \ name=dev-lb port=80 protocol=http \ members=w1,w2,w3 algorithm=ROUND_ROBIN - ''' + """ conn = get_conn() - if call != 'function': + if call != "function": raise SaltCloudSystemExit( - 'The create_lb function must be called with -f or --function.' + "The create_lb function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when creating a health check.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when creating a health check.") return False - if 'port' not in kwargs: - log.error( - 'A port or port-range must be specified for the load-balancer.' - ) + if "port" not in kwargs: + log.error("A port or port-range must be specified for the load-balancer.") return False - if 'networkdomain' not in kwargs: - log.error( - 'A network domain must be specified for the load-balancer.' - ) + if "networkdomain" not in kwargs: + log.error("A network domain must be specified for the load-balancer.") return False - if 'members' in kwargs: + if "members" in kwargs: members = [] ip = "" - membersList = kwargs.get('members').split(',') - log.debug('MemberList: %s', membersList) + membersList = kwargs.get("members").split(",") + log.debug("MemberList: %s", membersList) for member in membersList: try: - log.debug('Member: %s', member) + log.debug("Member: %s", member) node = get_node(conn, member) # pylint: disable=not-callable - log.debug('Node: %s', node) + log.debug("Node: %s", node) ip = node.private_ips[0] except Exception as err: # pylint: disable=broad-except log.error( - 'Failed to get node ip: %s', err, + "Failed to get node ip: %s", + err, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - members.append(Member(ip, ip, kwargs['port'])) + members.append(Member(ip, ip, kwargs["port"])) else: members = None - log.debug('Members: %s', members) + log.debug("Members: %s", members) - networkdomain = kwargs['networkdomain'] - name = kwargs['name'] - port = kwargs['port'] - protocol = kwargs.get('protocol', None) - algorithm = kwargs.get('algorithm', None) + networkdomain = kwargs["networkdomain"] + name = kwargs["name"] + port = kwargs["port"] + protocol = kwargs.get("protocol", None) + algorithm = kwargs.get("algorithm", None) lb_conn = get_lb_conn(conn) network_domains = conn.ex_list_network_domains() network_domain = [y for y in network_domains if y.name == networkdomain][0] - log.debug('Network Domain: %s', network_domain.id) + log.debug("Network Domain: %s", network_domain.id) lb_conn.ex_set_current_network_domain(network_domain.id) event_data = _to_event_data(kwargs) - __utils__['cloud.fire_event']( - 'event', - 'create load_balancer', - 'salt/cloud/loadbalancer/creating', + __utils__["cloud.fire_event"]( + "event", + "create load_balancer", + "salt/cloud/loadbalancer/creating", args=event_data, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - lb = lb_conn.create_balancer( - name, port, protocol, algorithm, members - ) + lb = lb_conn.create_balancer(name, port, protocol, algorithm, members) event_data = _to_event_data(kwargs) - __utils__['cloud.fire_event']( - 'event', - 'created load_balancer', - 'salt/cloud/loadbalancer/created', + __utils__["cloud.fire_event"]( + "event", + "created load_balancer", + "salt/cloud/loadbalancer/created", args=event_data, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return _expand_balancer(lb) def _expand_balancer(lb): - ''' + """ Convert the libcloud load-balancer object into something more serializable. - ''' + """ ret = {} ret.update(lb.__dict__) return ret def preferred_ip(vm_, ips): - ''' + """ Return the preferred Internet protocol. Either 'ipv4' (default) or 'ipv6'. - ''' + """ proto = config.get_cloud_config_value( - 'protocol', vm_, __opts__, default='ipv4', search_global=False + "protocol", vm_, __opts__, default="ipv4", search_global=False ) family = socket.AF_INET - if proto == 'ipv6': + if proto == "ipv6": family = socket.AF_INET6 for ip in ips: try: @@ -470,18 +479,17 @@ def preferred_ip(vm_, ips): def ssh_interface(vm_): - ''' + """ Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. - ''' + """ return config.get_cloud_config_value( - 'ssh_interface', vm_, __opts__, default='public_ips', - search_global=False + "ssh_interface", vm_, __opts__, default="public_ips", search_global=False ) def stop(name, call=None): - ''' + """ Stop a VM in DimensionData. name: @@ -492,19 +500,19 @@ def stop(name, call=None): .. code-block:: bash salt-cloud -a stop vm_name - ''' + """ conn = get_conn() node = get_node(conn, name) # pylint: disable=not-callable - log.debug('Node of Cloud VM: %s', node) + log.debug("Node of Cloud VM: %s", node) status = conn.ex_shutdown_graceful(node) - log.debug('Status of Cloud VM: %s', status) + log.debug("Status of Cloud VM: %s", status) return status def start(name, call=None): - ''' + """ Stop a VM in DimensionData. :param str name: @@ -515,75 +523,59 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a stop vm_name - ''' + """ conn = get_conn() node = get_node(conn, name) # pylint: disable=not-callable - log.debug('Node of Cloud VM: %s', node) + log.debug("Node of Cloud VM: %s", node) status = conn.ex_start_node(node) - log.debug('Status of Cloud VM: %s', status) + log.debug("Status of Cloud VM: %s", status) return status def get_conn(): - ''' + """ Return a conn object for the passed VM data - ''' + """ vm_ = get_configured_provider() driver = get_driver(Provider.DIMENSIONDATA) - region = config.get_cloud_config_value( - 'region', vm_, __opts__ - ) + region = config.get_cloud_config_value("region", vm_, __opts__) - user_id = config.get_cloud_config_value( - 'user_id', vm_, __opts__ - ) - key = config.get_cloud_config_value( - 'key', vm_, __opts__ - ) + user_id = config.get_cloud_config_value("user_id", vm_, __opts__) + key = config.get_cloud_config_value("key", vm_, __opts__) if key is not None: - log.debug('DimensionData authenticating using password') + log.debug("DimensionData authenticating using password") - return driver( - user_id, - key, - region=region - ) + return driver(user_id, key, region=region) def get_lb_conn(dd_driver=None): - ''' + """ Return a load-balancer conn object - ''' + """ vm_ = get_configured_provider() - region = config.get_cloud_config_value( - 'region', vm_, __opts__ - ) + region = config.get_cloud_config_value("region", vm_, __opts__) - user_id = config.get_cloud_config_value( - 'user_id', vm_, __opts__ - ) - key = config.get_cloud_config_value( - 'key', vm_, __opts__ - ) + user_id = config.get_cloud_config_value("user_id", vm_, __opts__) + key = config.get_cloud_config_value("key", vm_, __opts__) if not dd_driver: raise SaltCloudSystemExit( - 'Missing dimensiondata_driver for get_lb_conn method.' + "Missing dimensiondata_driver for get_lb_conn method." ) return get_driver_lb(Provider_lb.DIMENSIONDATA)(user_id, key, region=region) def _to_event_data(obj): - ''' + """ Convert the specified object into a form that can be serialised by msgpack as event data. :param obj: The object to convert. - ''' + """ if obj is None: return None @@ -608,7 +600,7 @@ def _to_event_data(obj): event_data = {} for attribute_name in dir(obj): - if attribute_name.startswith('_'): + if attribute_name.startswith("_"): continue attribute_value = getattr(obj, attribute_name) diff --git a/salt/cloud/clouds/ec2.py b/salt/cloud/clouds/ec2.py index 68f752cb2d1..3cfd075e23d 100644 --- a/salt/cloud/clouds/ec2.py +++ b/salt/cloud/clouds/ec2.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The EC2 Cloud Module ==================== @@ -72,74 +72,81 @@ To use the EC2 cloud module, set up the cloud configuration at userdata_file: /etc/salt/my-userdata-file :depends: requests -''' +""" # pylint: disable=invalid-name,function-redefined # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from functools import cmp_to_key -import os -import sys -import stat -import time -import uuid -import pprint -import logging + +import base64 +import binascii +import datetime +import decimal +import hashlib # Import libs for talking to the EC2 API import hmac -import hashlib -import binascii -import datetime -import base64 +import logging +import os +import pprint import re -import decimal +import stat +import sys +import time +import uuid +from functools import cmp_to_key + +import salt.config as config +import salt.utils.aws as aws # Import Salt Libs import salt.utils.cloud import salt.utils.compat import salt.utils.files import salt.utils.hashutils +import salt.utils.http as http import salt.utils.json import salt.utils.msgpack import salt.utils.stringutils import salt.utils.yaml from salt._compat import ElementTree as ET -import salt.utils.http as http -import salt.utils.aws as aws -import salt.config as config from salt.exceptions import ( - SaltCloudException, - SaltCloudSystemExit, SaltCloudConfigError, + SaltCloudException, + SaltCloudExecutionFailure, SaltCloudExecutionTimeout, - SaltCloudExecutionFailure + SaltCloudSystemExit, ) # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six from salt.ext.six.moves import map, range, zip -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse, urlencode as _urlencode +from salt.ext.six.moves.urllib.parse import urlencode as _urlencode +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # Import 3rd-Party Libs # Try to import PyCrypto, which may not be installed on a RAET-based system try: from M2Crypto import RSA + HAS_M2 = True except ImportError: HAS_M2 = False try: import Crypto + # PKCS1_v1_5 was added in PyCrypto 2.5 from Crypto.Cipher import PKCS1_v1_5 # pylint: disable=E0611 from Crypto.Hash import SHA # pylint: disable=E0611,W0611 + HAS_PYCRYPTO = True except ImportError: HAS_PYCRYPTO = False try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False @@ -150,41 +157,41 @@ log = logging.getLogger(__name__) EC2_LOCATIONS = { - 'ap-northeast-1': 'ec2_ap_northeast', - 'ap-northeast-2': 'ec2_ap_northeast_2', - 'ap-southeast-1': 'ec2_ap_southeast', - 'ap-southeast-2': 'ec2_ap_southeast_2', - 'eu-west-1': 'ec2_eu_west', - 'eu-central-1': 'ec2_eu_central', - 'sa-east-1': 'ec2_sa_east', - 'us-east-1': 'ec2_us_east', - 'us-gov-west-1': 'ec2_us_gov_west_1', - 'us-west-1': 'ec2_us_west', - 'us-west-2': 'ec2_us_west_oregon', + "ap-northeast-1": "ec2_ap_northeast", + "ap-northeast-2": "ec2_ap_northeast_2", + "ap-southeast-1": "ec2_ap_southeast", + "ap-southeast-2": "ec2_ap_southeast_2", + "eu-west-1": "ec2_eu_west", + "eu-central-1": "ec2_eu_central", + "sa-east-1": "ec2_sa_east", + "us-east-1": "ec2_us_east", + "us-gov-west-1": "ec2_us_gov_west_1", + "us-west-1": "ec2_us_west", + "us-west-2": "ec2_us_west_oregon", } -DEFAULT_LOCATION = 'us-east-1' +DEFAULT_LOCATION = "us-east-1" -DEFAULT_EC2_API_VERSION = '2014-10-01' +DEFAULT_EC2_API_VERSION = "2014-10-01" EC2_RETRY_CODES = [ - 'RequestLimitExceeded', - 'InsufficientInstanceCapacity', - 'InternalError', - 'Unavailable', - 'InsufficientAddressCapacity', - 'InsufficientReservedInstanceCapacity', + "RequestLimitExceeded", + "InsufficientInstanceCapacity", + "InternalError", + "Unavailable", + "InsufficientAddressCapacity", + "InsufficientReservedInstanceCapacity", ] -JS_COMMENT_RE = re.compile(r'/\*.*?\*/', re.S) +JS_COMMENT_RE = re.compile(r"/\*.*?\*/", re.S) -__virtualname__ = 'ec2' +__virtualname__ = "ec2" # Only load in this module if the EC2 configurations are in place def __virtual__(): - ''' + """ Set up the libcloud functions and check for EC2 configurations - ''' + """ if get_configured_provider() is False: return False @@ -195,34 +202,26 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('id', 'key') + __opts__, __active_provider_name__ or __virtualname__, ("id", "key") ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - deps = { - 'requests': HAS_REQUESTS, - 'pycrypto or m2crypto': HAS_M2 or HAS_PYCRYPTO - } - return config.check_driver_dependencies( - __virtualname__, - deps - ) + """ + deps = {"requests": HAS_REQUESTS, "pycrypto or m2crypto": HAS_M2 or HAS_PYCRYPTO} + return config.check_driver_dependencies(__virtualname__, deps) def _xml_to_dict(xmltree): - ''' + """ Convert an XML tree into a dict - ''' + """ if sys.version_info < (2, 7): children_len = len(xmltree.getchildren()) else: @@ -230,16 +229,16 @@ def _xml_to_dict(xmltree): if children_len < 1: name = xmltree.tag - if '}' in name: - comps = name.split('}') + if "}" in name: + comps = name.split("}") name = comps[1] return {name: xmltree.text} xmldict = {} for item in xmltree: name = item.tag - if '}' in name: - comps = name.split('}') + if "}" in name: + comps = name.split("}") name = comps[1] if name not in xmldict: if sys.version_info < (2, 7): @@ -261,7 +260,7 @@ def _xml_to_dict(xmltree): def optimize_providers(providers): - ''' + """ Return an optimized list of providers. We want to reduce the duplication of querying @@ -271,28 +270,29 @@ def optimize_providers(providers): the same data will be returned for each provider, thus causing un-wanted duplicate data and API calls to EC2. - ''' + """ tmp_providers = {} optimized_providers = {} for name, data in six.iteritems(providers): - if 'location' not in data: - data['location'] = DEFAULT_LOCATION + if "location" not in data: + data["location"] = DEFAULT_LOCATION - if data['location'] not in tmp_providers: - tmp_providers[data['location']] = {} + if data["location"] not in tmp_providers: + tmp_providers[data["location"]] = {} - creds = (data['id'], data['key']) - if creds not in tmp_providers[data['location']]: - tmp_providers[data['location']][creds] = {'name': name, - 'data': data, - } + creds = (data["id"], data["key"]) + if creds not in tmp_providers[data["location"]]: + tmp_providers[data["location"]][creds] = { + "name": name, + "data": data, + } for location, tmp_data in six.iteritems(tmp_providers): for creds, data in six.iteritems(tmp_data): _id, _key = creds - _name = data['name'] - _data = data['data'] + _name = data["name"] + _data = data["data"] if _name not in optimized_providers: optimized_providers[_name] = _data @@ -300,14 +300,20 @@ def optimize_providers(providers): def sign(key, msg): - return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest() + return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest() -def query(params=None, setname=None, requesturl=None, location=None, - return_url=False, return_root=False): +def query( + params=None, + setname=None, + requesturl=None, + location=None, + return_url=False, + return_root=False, +): provider = get_configured_provider() - service_url = provider.get('service_url', 'amazonaws.com') + service_url = provider.get("service_url", "amazonaws.com") # Retrieve access credentials from meta-data, or use provided access_key_id, secret_access_key, token = aws.creds(provider) @@ -315,99 +321,127 @@ def query(params=None, setname=None, requesturl=None, location=None, attempts = 0 while attempts < aws.AWS_MAX_RETRIES: params_with_headers = params.copy() - timestamp = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') + timestamp = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") if not location: location = get_location() if not requesturl: endpoint = provider.get( - 'endpoint', - 'ec2.{0}.{1}'.format(location, service_url) + "endpoint", "ec2.{0}.{1}".format(location, service_url) ) - requesturl = 'https://{0}/'.format(endpoint) + requesturl = "https://{0}/".format(endpoint) endpoint = _urlparse(requesturl).netloc endpoint_path = _urlparse(requesturl).path else: endpoint = _urlparse(requesturl).netloc endpoint_path = _urlparse(requesturl).path - if endpoint == '': + if endpoint == "": endpoint_err = ( - 'Could not find a valid endpoint in the ' - 'requesturl: {0}. Looking for something ' - 'like https://some.ec2.endpoint/?args').format(requesturl) + "Could not find a valid endpoint in the " + "requesturl: {0}. Looking for something " + "like https://some.ec2.endpoint/?args" + ).format(requesturl) log.error(endpoint_err) if return_url is True: - return {'error': endpoint_err}, requesturl - return {'error': endpoint_err} + return {"error": endpoint_err}, requesturl + return {"error": endpoint_err} - log.debug('Using EC2 endpoint: %s', endpoint) + log.debug("Using EC2 endpoint: %s", endpoint) # AWS v4 signature - method = 'GET' + method = "GET" region = location - service = 'ec2' + service = "ec2" canonical_uri = _urlparse(requesturl).path host = endpoint.strip() # Create a date for headers and the credential string t = datetime.datetime.utcnow() - amz_date = t.strftime('%Y%m%dT%H%M%SZ') # Format date as YYYYMMDD'T'HHMMSS'Z' - datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope + amz_date = t.strftime("%Y%m%dT%H%M%SZ") # Format date as YYYYMMDD'T'HHMMSS'Z' + datestamp = t.strftime("%Y%m%d") # Date w/o time, used in credential scope - canonical_headers = 'host:' + host + '\n' + 'x-amz-date:' + amz_date + '\n' - signed_headers = 'host;x-amz-date' + canonical_headers = "host:" + host + "\n" + "x-amz-date:" + amz_date + "\n" + signed_headers = "host;x-amz-date" - payload_hash = salt.utils.hashutils.sha256_digest('') + payload_hash = salt.utils.hashutils.sha256_digest("") - ec2_api_version = provider.get( - 'ec2_api_version', - DEFAULT_EC2_API_VERSION - ) + ec2_api_version = provider.get("ec2_api_version", DEFAULT_EC2_API_VERSION) - params_with_headers['Version'] = ec2_api_version + params_with_headers["Version"] = ec2_api_version keys = sorted(list(params_with_headers)) values = map(params_with_headers.get, keys) querystring = _urlencode(list(zip(keys, values))) - querystring = querystring.replace('+', '%20') + querystring = querystring.replace("+", "%20") - canonical_request = method + '\n' + canonical_uri + '\n' + \ - querystring + '\n' + canonical_headers + '\n' + \ - signed_headers + '\n' + payload_hash + canonical_request = ( + method + + "\n" + + canonical_uri + + "\n" + + querystring + + "\n" + + canonical_headers + + "\n" + + signed_headers + + "\n" + + payload_hash + ) - algorithm = 'AWS4-HMAC-SHA256' - credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request' + algorithm = "AWS4-HMAC-SHA256" + credential_scope = ( + datestamp + "/" + region + "/" + service + "/" + "aws4_request" + ) - string_to_sign = algorithm + '\n' + amz_date + '\n' + \ - credential_scope + '\n' + \ - salt.utils.hashutils.sha256_digest(canonical_request) + string_to_sign = ( + algorithm + + "\n" + + amz_date + + "\n" + + credential_scope + + "\n" + + salt.utils.hashutils.sha256_digest(canonical_request) + ) - kDate = sign(('AWS4' + provider['key']).encode('utf-8'), datestamp) + kDate = sign(("AWS4" + provider["key"]).encode("utf-8"), datestamp) kRegion = sign(kDate, region) kService = sign(kRegion, service) - signing_key = sign(kService, 'aws4_request') + signing_key = sign(kService, "aws4_request") - signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), - hashlib.sha256).hexdigest() + signature = hmac.new( + signing_key, (string_to_sign).encode("utf-8"), hashlib.sha256 + ).hexdigest() - authorization_header = algorithm + ' ' + 'Credential=' + \ - provider['id'] + '/' + credential_scope + \ - ', ' + 'SignedHeaders=' + signed_headers + \ - ', ' + 'Signature=' + signature - headers = {'x-amz-date': amz_date, 'Authorization': authorization_header} + authorization_header = ( + algorithm + + " " + + "Credential=" + + provider["id"] + + "/" + + credential_scope + + ", " + + "SignedHeaders=" + + signed_headers + + ", " + + "Signature=" + + signature + ) + headers = {"x-amz-date": amz_date, "Authorization": authorization_header} - log.debug('EC2 Request: %s', requesturl) - log.trace('EC2 Request Parameters: %s', params_with_headers) + log.debug("EC2 Request: %s", requesturl) + log.trace("EC2 Request Parameters: %s", params_with_headers) try: - result = requests.get(requesturl, headers=headers, params=params_with_headers) - log.debug( - 'EC2 Response Status Code: %s', - # result.getcode() - result.status_code + result = requests.get( + requesturl, headers=headers, params=params_with_headers ) - log.trace('EC2 Response Text: %s', result.text) + log.debug( + "EC2 Response Status Code: %s", + # result.getcode() + result.status_code, + ) + log.trace("EC2 Response Text: %s", result.text) result.raise_for_status() break except requests.exceptions.HTTPError as exc: @@ -415,32 +449,39 @@ def query(params=None, setname=None, requesturl=None, location=None, data = _xml_to_dict(root) # check to see if we should retry the query - err_code = data.get('Errors', {}).get('Error', {}).get('Code', '') + err_code = data.get("Errors", {}).get("Error", {}).get("Code", "") if err_code and err_code in EC2_RETRY_CODES: attempts += 1 log.error( - 'EC2 Response Status Code and Error: [%s %s] %s; ' - 'Attempts remaining: %s', - exc.response.status_code, exc, data, attempts + "EC2 Response Status Code and Error: [%s %s] %s; " + "Attempts remaining: %s", + exc.response.status_code, + exc, + data, + attempts, ) aws.sleep_exponential_backoff(attempts) continue log.error( - 'EC2 Response Status Code and Error: [%s %s] %s', - exc.response.status_code, exc, data + "EC2 Response Status Code and Error: [%s %s] %s", + exc.response.status_code, + exc, + data, ) if return_url is True: - return {'error': data}, requesturl - return {'error': data} + return {"error": data}, requesturl + return {"error": data} else: log.error( - 'EC2 Response Status Code and Error: [%s %s] %s', - exc.response.status_code, exc, data + "EC2 Response Status Code and Error: [%s %s] %s", + exc.response.status_code, + exc, + data, ) if return_url is True: - return {'error': data}, requesturl - return {'error': data} + return {"error": data}, requesturl + return {"error": data} response = result.text @@ -456,7 +497,7 @@ def query(params=None, setname=None, requesturl=None, location=None, children_len = len(root) for item in range(0, children_len): - comps = root[item].tag.split('}') + comps = root[item].tag.split("}") if comps[1] == setname: items = root[item] @@ -470,14 +511,16 @@ def query(params=None, setname=None, requesturl=None, location=None, return ret -def _wait_for_spot_instance(update_callback, - update_args=None, - update_kwargs=None, - timeout=10 * 60, - interval=30, - interval_multiplier=1, - max_failures=10): - ''' +def _wait_for_spot_instance( + update_callback, + update_args=None, + update_kwargs=None, + timeout=10 * 60, + interval=30, + interval_multiplier=1, + max_failures=10, +): + """ Helper function that waits for a spot instance request to become active for a specific maximum amount of time. @@ -499,7 +542,7 @@ def _wait_for_spot_instance(update_callback, :returns: The update_callback returned data :raises: SaltCloudExecutionTimeout - ''' + """ if update_args is None: update_args = () if update_kwargs is None: @@ -508,31 +551,30 @@ def _wait_for_spot_instance(update_callback, duration = timeout while True: log.debug( - 'Waiting for spot instance reservation. Giving up in ' - '00:%02d:%02d', int(timeout // 60), int(timeout % 60) + "Waiting for spot instance reservation. Giving up in " "00:%02d:%02d", + int(timeout // 60), + int(timeout % 60), ) data = update_callback(*update_args, **update_kwargs) if data is False: log.debug( - 'update_callback has returned False which is considered a ' - 'failure. Remaining Failures: %s', max_failures + "update_callback has returned False which is considered a " + "failure. Remaining Failures: %s", + max_failures, ) max_failures -= 1 if max_failures <= 0: raise SaltCloudExecutionFailure( - 'Too many failures occurred while waiting for ' - 'the spot instance reservation to become active.' + "Too many failures occurred while waiting for " + "the spot instance reservation to become active." ) elif data is not None: return data if timeout < 0: raise SaltCloudExecutionTimeout( - 'Unable to get an active spot instance request for ' - '00:{0:02d}:{1:02d}'.format( - int(duration // 60), - int(duration % 60) - ) + "Unable to get an active spot instance request for " + "00:{0:02d}:{1:02d}".format(int(duration // 60), int(duration % 60)) ) time.sleep(interval) timeout -= interval @@ -541,471 +583,447 @@ def _wait_for_spot_instance(update_callback, interval *= interval_multiplier if interval > timeout: interval = timeout + 1 - log.info('Interval multiplier in effect; interval is ' - 'now %ss', interval) + log.info("Interval multiplier in effect; interval is " "now %ss", interval) def avail_sizes(call=None): - ''' + """ Return a dict of all available VM sizes on the cloud provider with relevant data. Latest version can be found at: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) sizes = { - 'Cluster Compute': { - 'cc2.8xlarge': { - 'id': 'cc2.8xlarge', - 'cores': '16 (2 x Intel Xeon E5-2670, eight-core with ' - 'hyperthread)', - 'disk': '3360 GiB (4 x 840 GiB)', - 'ram': '60.5 GiB' + "Cluster Compute": { + "cc2.8xlarge": { + "id": "cc2.8xlarge", + "cores": "16 (2 x Intel Xeon E5-2670, eight-core with " "hyperthread)", + "disk": "3360 GiB (4 x 840 GiB)", + "ram": "60.5 GiB", }, - 'cc1.4xlarge': { - 'id': 'cc1.4xlarge', - 'cores': '8 (2 x Intel Xeon X5570, quad-core with ' - 'hyperthread)', - 'disk': '1690 GiB (2 x 840 GiB)', - 'ram': '22.5 GiB' + "cc1.4xlarge": { + "id": "cc1.4xlarge", + "cores": "8 (2 x Intel Xeon X5570, quad-core with " "hyperthread)", + "disk": "1690 GiB (2 x 840 GiB)", + "ram": "22.5 GiB", }, }, - 'Cluster CPU': { - 'cg1.4xlarge': { - 'id': 'cg1.4xlarge', - 'cores': '8 (2 x Intel Xeon X5570, quad-core with ' - 'hyperthread), plus 2 NVIDIA Tesla M2050 GPUs', - 'disk': '1680 GiB (2 x 840 GiB)', - 'ram': '22.5 GiB' + "Cluster CPU": { + "cg1.4xlarge": { + "id": "cg1.4xlarge", + "cores": "8 (2 x Intel Xeon X5570, quad-core with " + "hyperthread), plus 2 NVIDIA Tesla M2050 GPUs", + "disk": "1680 GiB (2 x 840 GiB)", + "ram": "22.5 GiB", }, }, - 'Compute Optimized': { - 'c4.large': { - 'id': 'c4.large', - 'cores': '2', - 'disk': 'EBS - 500 Mbps', - 'ram': '3.75 GiB' + "Compute Optimized": { + "c4.large": { + "id": "c4.large", + "cores": "2", + "disk": "EBS - 500 Mbps", + "ram": "3.75 GiB", }, - 'c4.xlarge': { - 'id': 'c4.xlarge', - 'cores': '4', - 'disk': 'EBS - 750 Mbps', - 'ram': '7.5 GiB' + "c4.xlarge": { + "id": "c4.xlarge", + "cores": "4", + "disk": "EBS - 750 Mbps", + "ram": "7.5 GiB", }, - 'c4.2xlarge': { - 'id': 'c4.2xlarge', - 'cores': '8', - 'disk': 'EBS - 1000 Mbps', - 'ram': '15 GiB' + "c4.2xlarge": { + "id": "c4.2xlarge", + "cores": "8", + "disk": "EBS - 1000 Mbps", + "ram": "15 GiB", }, - 'c4.4xlarge': { - 'id': 'c4.4xlarge', - 'cores': '16', - 'disk': 'EBS - 2000 Mbps', - 'ram': '30 GiB' + "c4.4xlarge": { + "id": "c4.4xlarge", + "cores": "16", + "disk": "EBS - 2000 Mbps", + "ram": "30 GiB", }, - 'c4.8xlarge': { - 'id': 'c4.8xlarge', - 'cores': '36', - 'disk': 'EBS - 4000 Mbps', - 'ram': '60 GiB' + "c4.8xlarge": { + "id": "c4.8xlarge", + "cores": "36", + "disk": "EBS - 4000 Mbps", + "ram": "60 GiB", }, - 'c3.large': { - 'id': 'c3.large', - 'cores': '2', - 'disk': '32 GiB (2 x 16 GiB SSD)', - 'ram': '3.75 GiB' + "c3.large": { + "id": "c3.large", + "cores": "2", + "disk": "32 GiB (2 x 16 GiB SSD)", + "ram": "3.75 GiB", }, - 'c3.xlarge': { - 'id': 'c3.xlarge', - 'cores': '4', - 'disk': '80 GiB (2 x 40 GiB SSD)', - 'ram': '7.5 GiB' + "c3.xlarge": { + "id": "c3.xlarge", + "cores": "4", + "disk": "80 GiB (2 x 40 GiB SSD)", + "ram": "7.5 GiB", }, - 'c3.2xlarge': { - 'id': 'c3.2xlarge', - 'cores': '8', - 'disk': '160 GiB (2 x 80 GiB SSD)', - 'ram': '15 GiB' + "c3.2xlarge": { + "id": "c3.2xlarge", + "cores": "8", + "disk": "160 GiB (2 x 80 GiB SSD)", + "ram": "15 GiB", }, - 'c3.4xlarge': { - 'id': 'c3.4xlarge', - 'cores': '16', - 'disk': '320 GiB (2 x 160 GiB SSD)', - 'ram': '30 GiB' + "c3.4xlarge": { + "id": "c3.4xlarge", + "cores": "16", + "disk": "320 GiB (2 x 160 GiB SSD)", + "ram": "30 GiB", }, - 'c3.8xlarge': { - 'id': 'c3.8xlarge', - 'cores': '32', - 'disk': '640 GiB (2 x 320 GiB SSD)', - 'ram': '60 GiB' - } - }, - 'Dense Storage': { - 'd2.xlarge': { - 'id': 'd2.xlarge', - 'cores': '4', - 'disk': '6 TiB (3 x 2 TiB hard disk drives)', - 'ram': '30.5 GiB' - }, - 'd2.2xlarge': { - 'id': 'd2.2xlarge', - 'cores': '8', - 'disk': '12 TiB (6 x 2 TiB hard disk drives)', - 'ram': '61 GiB' - }, - 'd2.4xlarge': { - 'id': 'd2.4xlarge', - 'cores': '16', - 'disk': '24 TiB (12 x 2 TiB hard disk drives)', - 'ram': '122 GiB' - }, - 'd2.8xlarge': { - 'id': 'd2.8xlarge', - 'cores': '36', - 'disk': '24 TiB (24 x 2 TiB hard disk drives)', - 'ram': '244 GiB' + "c3.8xlarge": { + "id": "c3.8xlarge", + "cores": "32", + "disk": "640 GiB (2 x 320 GiB SSD)", + "ram": "60 GiB", }, }, - 'GPU': { - 'g2.2xlarge': { - 'id': 'g2.2xlarge', - 'cores': '8', - 'disk': '60 GiB (1 x 60 GiB SSD)', - 'ram': '15 GiB' + "Dense Storage": { + "d2.xlarge": { + "id": "d2.xlarge", + "cores": "4", + "disk": "6 TiB (3 x 2 TiB hard disk drives)", + "ram": "30.5 GiB", }, - 'g2.8xlarge': { - 'id': 'g2.8xlarge', - 'cores': '32', - 'disk': '240 GiB (2 x 120 GiB SSD)', - 'ram': '60 GiB' + "d2.2xlarge": { + "id": "d2.2xlarge", + "cores": "8", + "disk": "12 TiB (6 x 2 TiB hard disk drives)", + "ram": "61 GiB", + }, + "d2.4xlarge": { + "id": "d2.4xlarge", + "cores": "16", + "disk": "24 TiB (12 x 2 TiB hard disk drives)", + "ram": "122 GiB", + }, + "d2.8xlarge": { + "id": "d2.8xlarge", + "cores": "36", + "disk": "24 TiB (24 x 2 TiB hard disk drives)", + "ram": "244 GiB", }, }, - 'GPU Compute': { - 'p2.xlarge': { - 'id': 'p2.xlarge', - 'cores': '4', - 'disk': 'EBS', - 'ram': '61 GiB' + "GPU": { + "g2.2xlarge": { + "id": "g2.2xlarge", + "cores": "8", + "disk": "60 GiB (1 x 60 GiB SSD)", + "ram": "15 GiB", }, - 'p2.8xlarge': { - 'id': 'p2.8xlarge', - 'cores': '32', - 'disk': 'EBS', - 'ram': '488 GiB' - }, - 'p2.16xlarge': { - 'id': 'p2.16xlarge', - 'cores': '64', - 'disk': 'EBS', - 'ram': '732 GiB' + "g2.8xlarge": { + "id": "g2.8xlarge", + "cores": "32", + "disk": "240 GiB (2 x 120 GiB SSD)", + "ram": "60 GiB", }, }, - 'High I/O': { - 'i2.xlarge': { - 'id': 'i2.xlarge', - 'cores': '4', - 'disk': 'SSD (1 x 800 GiB)', - 'ram': '30.5 GiB' + "GPU Compute": { + "p2.xlarge": { + "id": "p2.xlarge", + "cores": "4", + "disk": "EBS", + "ram": "61 GiB", }, - 'i2.2xlarge': { - 'id': 'i2.2xlarge', - 'cores': '8', - 'disk': 'SSD (2 x 800 GiB)', - 'ram': '61 GiB' + "p2.8xlarge": { + "id": "p2.8xlarge", + "cores": "32", + "disk": "EBS", + "ram": "488 GiB", }, - 'i2.4xlarge': { - 'id': 'i2.4xlarge', - 'cores': '16', - 'disk': 'SSD (4 x 800 GiB)', - 'ram': '122 GiB' - }, - 'i2.8xlarge': { - 'id': 'i2.8xlarge', - 'cores': '32', - 'disk': 'SSD (8 x 800 GiB)', - 'ram': '244 GiB' - } - }, - 'High Memory': { - 'x1.16xlarge': { - 'id': 'x1.16xlarge', - 'cores': '64 (with 5.45 ECUs each)', - 'disk': '1920 GiB (1 x 1920 GiB)', - 'ram': '976 GiB' - }, - 'x1.32xlarge': { - 'id': 'x1.32xlarge', - 'cores': '128 (with 2.73 ECUs each)', - 'disk': '3840 GiB (2 x 1920 GiB)', - 'ram': '1952 GiB' - }, - 'r4.large': { - 'id': 'r4.large', - 'cores': '2 (with 3.45 ECUs each)', - 'disk': 'EBS', - 'ram': '15.25 GiB' - }, - 'r4.xlarge': { - 'id': 'r4.xlarge', - 'cores': '4 (with 3.35 ECUs each)', - 'disk': 'EBS', - 'ram': '30.5 GiB' - }, - 'r4.2xlarge': { - 'id': 'r4.2xlarge', - 'cores': '8 (with 3.35 ECUs each)', - 'disk': 'EBS', - 'ram': '61 GiB' - }, - 'r4.4xlarge': { - 'id': 'r4.4xlarge', - 'cores': '16 (with 3.3 ECUs each)', - 'disk': 'EBS', - 'ram': '122 GiB' - }, - 'r4.8xlarge': { - 'id': 'r4.8xlarge', - 'cores': '32 (with 3.1 ECUs each)', - 'disk': 'EBS', - 'ram': '244 GiB' - }, - 'r4.16xlarge': { - 'id': 'r4.16xlarge', - 'cores': '64 (with 3.05 ECUs each)', - 'disk': 'EBS', - 'ram': '488 GiB' - }, - 'r3.large': { - 'id': 'r3.large', - 'cores': '2 (with 3.25 ECUs each)', - 'disk': '32 GiB (1 x 32 GiB SSD)', - 'ram': '15 GiB' - }, - 'r3.xlarge': { - 'id': 'r3.xlarge', - 'cores': '4 (with 3.25 ECUs each)', - 'disk': '80 GiB (1 x 80 GiB SSD)', - 'ram': '30.5 GiB' - }, - 'r3.2xlarge': { - 'id': 'r3.2xlarge', - 'cores': '8 (with 3.25 ECUs each)', - 'disk': '160 GiB (1 x 160 GiB SSD)', - 'ram': '61 GiB' - }, - 'r3.4xlarge': { - 'id': 'r3.4xlarge', - 'cores': '16 (with 3.25 ECUs each)', - 'disk': '320 GiB (1 x 320 GiB SSD)', - 'ram': '122 GiB' - }, - 'r3.8xlarge': { - 'id': 'r3.8xlarge', - 'cores': '32 (with 3.25 ECUs each)', - 'disk': '640 GiB (2 x 320 GiB SSD)', - 'ram': '244 GiB' - } - }, - 'High-Memory Cluster': { - 'cr1.8xlarge': { - 'id': 'cr1.8xlarge', - 'cores': '16 (2 x Intel Xeon E5-2670, eight-core)', - 'disk': '240 GiB (2 x 120 GiB SSD)', - 'ram': '244 GiB' + "p2.16xlarge": { + "id": "p2.16xlarge", + "cores": "64", + "disk": "EBS", + "ram": "732 GiB", }, }, - 'High Storage': { - 'hs1.8xlarge': { - 'id': 'hs1.8xlarge', - 'cores': '16 (8 cores + 8 hyperthreads)', - 'disk': '48 TiB (24 x 2 TiB hard disk drives)', - 'ram': '117 GiB' + "High I/O": { + "i2.xlarge": { + "id": "i2.xlarge", + "cores": "4", + "disk": "SSD (1 x 800 GiB)", + "ram": "30.5 GiB", + }, + "i2.2xlarge": { + "id": "i2.2xlarge", + "cores": "8", + "disk": "SSD (2 x 800 GiB)", + "ram": "61 GiB", + }, + "i2.4xlarge": { + "id": "i2.4xlarge", + "cores": "16", + "disk": "SSD (4 x 800 GiB)", + "ram": "122 GiB", + }, + "i2.8xlarge": { + "id": "i2.8xlarge", + "cores": "32", + "disk": "SSD (8 x 800 GiB)", + "ram": "244 GiB", }, }, - 'General Purpose': { - 't2.nano': { - 'id': 't2.nano', - 'cores': '1', - 'disk': 'EBS', - 'ram': '512 MiB' + "High Memory": { + "x1.16xlarge": { + "id": "x1.16xlarge", + "cores": "64 (with 5.45 ECUs each)", + "disk": "1920 GiB (1 x 1920 GiB)", + "ram": "976 GiB", }, - 't2.micro': { - 'id': 't2.micro', - 'cores': '1', - 'disk': 'EBS', - 'ram': '1 GiB' + "x1.32xlarge": { + "id": "x1.32xlarge", + "cores": "128 (with 2.73 ECUs each)", + "disk": "3840 GiB (2 x 1920 GiB)", + "ram": "1952 GiB", }, - 't2.small': { - 'id': 't2.small', - 'cores': '1', - 'disk': 'EBS', - 'ram': '2 GiB' + "r4.large": { + "id": "r4.large", + "cores": "2 (with 3.45 ECUs each)", + "disk": "EBS", + "ram": "15.25 GiB", }, - 't2.medium': { - 'id': 't2.medium', - 'cores': '2', - 'disk': 'EBS', - 'ram': '4 GiB' + "r4.xlarge": { + "id": "r4.xlarge", + "cores": "4 (with 3.35 ECUs each)", + "disk": "EBS", + "ram": "30.5 GiB", }, - 't2.large': { - 'id': 't2.large', - 'cores': '2', - 'disk': 'EBS', - 'ram': '8 GiB' + "r4.2xlarge": { + "id": "r4.2xlarge", + "cores": "8 (with 3.35 ECUs each)", + "disk": "EBS", + "ram": "61 GiB", }, - 't2.xlarge': { - 'id': 't2.xlarge', - 'cores': '4', - 'disk': 'EBS', - 'ram': '16 GiB' + "r4.4xlarge": { + "id": "r4.4xlarge", + "cores": "16 (with 3.3 ECUs each)", + "disk": "EBS", + "ram": "122 GiB", }, - 't2.2xlarge': { - 'id': 't2.2xlarge', - 'cores': '8', - 'disk': 'EBS', - 'ram': '32 GiB' + "r4.8xlarge": { + "id": "r4.8xlarge", + "cores": "32 (with 3.1 ECUs each)", + "disk": "EBS", + "ram": "244 GiB", }, - 'm4.large': { - 'id': 'm4.large', - 'cores': '2', - 'disk': 'EBS - 450 Mbps', - 'ram': '8 GiB' + "r4.16xlarge": { + "id": "r4.16xlarge", + "cores": "64 (with 3.05 ECUs each)", + "disk": "EBS", + "ram": "488 GiB", }, - 'm4.xlarge': { - 'id': 'm4.xlarge', - 'cores': '4', - 'disk': 'EBS - 750 Mbps', - 'ram': '16 GiB' + "r3.large": { + "id": "r3.large", + "cores": "2 (with 3.25 ECUs each)", + "disk": "32 GiB (1 x 32 GiB SSD)", + "ram": "15 GiB", }, - 'm4.2xlarge': { - 'id': 'm4.2xlarge', - 'cores': '8', - 'disk': 'EBS - 1000 Mbps', - 'ram': '32 GiB' + "r3.xlarge": { + "id": "r3.xlarge", + "cores": "4 (with 3.25 ECUs each)", + "disk": "80 GiB (1 x 80 GiB SSD)", + "ram": "30.5 GiB", }, - 'm4.4xlarge': { - 'id': 'm4.4xlarge', - 'cores': '16', - 'disk': 'EBS - 2000 Mbps', - 'ram': '64 GiB' + "r3.2xlarge": { + "id": "r3.2xlarge", + "cores": "8 (with 3.25 ECUs each)", + "disk": "160 GiB (1 x 160 GiB SSD)", + "ram": "61 GiB", }, - 'm4.10xlarge': { - 'id': 'm4.10xlarge', - 'cores': '40', - 'disk': 'EBS - 4000 Mbps', - 'ram': '160 GiB' + "r3.4xlarge": { + "id": "r3.4xlarge", + "cores": "16 (with 3.25 ECUs each)", + "disk": "320 GiB (1 x 320 GiB SSD)", + "ram": "122 GiB", }, - 'm4.16xlarge': { - 'id': 'm4.16xlarge', - 'cores': '64', - 'disk': 'EBS - 10000 Mbps', - 'ram': '256 GiB' + "r3.8xlarge": { + "id": "r3.8xlarge", + "cores": "32 (with 3.25 ECUs each)", + "disk": "640 GiB (2 x 320 GiB SSD)", + "ram": "244 GiB", }, - 'm3.medium': { - 'id': 'm3.medium', - 'cores': '1', - 'disk': 'SSD (1 x 4)', - 'ram': '3.75 GiB' + }, + "High-Memory Cluster": { + "cr1.8xlarge": { + "id": "cr1.8xlarge", + "cores": "16 (2 x Intel Xeon E5-2670, eight-core)", + "disk": "240 GiB (2 x 120 GiB SSD)", + "ram": "244 GiB", }, - 'm3.large': { - 'id': 'm3.large', - 'cores': '2', - 'disk': 'SSD (1 x 32)', - 'ram': '7.5 GiB' + }, + "High Storage": { + "hs1.8xlarge": { + "id": "hs1.8xlarge", + "cores": "16 (8 cores + 8 hyperthreads)", + "disk": "48 TiB (24 x 2 TiB hard disk drives)", + "ram": "117 GiB", }, - 'm3.xlarge': { - 'id': 'm3.xlarge', - 'cores': '4', - 'disk': 'SSD (2 x 40)', - 'ram': '15 GiB' + }, + "General Purpose": { + "t2.nano": {"id": "t2.nano", "cores": "1", "disk": "EBS", "ram": "512 MiB"}, + "t2.micro": {"id": "t2.micro", "cores": "1", "disk": "EBS", "ram": "1 GiB"}, + "t2.small": {"id": "t2.small", "cores": "1", "disk": "EBS", "ram": "2 GiB"}, + "t2.medium": { + "id": "t2.medium", + "cores": "2", + "disk": "EBS", + "ram": "4 GiB", }, - 'm3.2xlarge': { - 'id': 'm3.2xlarge', - 'cores': '8', - 'disk': 'SSD (2 x 80)', - 'ram': '30 GiB' + "t2.large": {"id": "t2.large", "cores": "2", "disk": "EBS", "ram": "8 GiB"}, + "t2.xlarge": { + "id": "t2.xlarge", + "cores": "4", + "disk": "EBS", + "ram": "16 GiB", }, - } + "t2.2xlarge": { + "id": "t2.2xlarge", + "cores": "8", + "disk": "EBS", + "ram": "32 GiB", + }, + "m4.large": { + "id": "m4.large", + "cores": "2", + "disk": "EBS - 450 Mbps", + "ram": "8 GiB", + }, + "m4.xlarge": { + "id": "m4.xlarge", + "cores": "4", + "disk": "EBS - 750 Mbps", + "ram": "16 GiB", + }, + "m4.2xlarge": { + "id": "m4.2xlarge", + "cores": "8", + "disk": "EBS - 1000 Mbps", + "ram": "32 GiB", + }, + "m4.4xlarge": { + "id": "m4.4xlarge", + "cores": "16", + "disk": "EBS - 2000 Mbps", + "ram": "64 GiB", + }, + "m4.10xlarge": { + "id": "m4.10xlarge", + "cores": "40", + "disk": "EBS - 4000 Mbps", + "ram": "160 GiB", + }, + "m4.16xlarge": { + "id": "m4.16xlarge", + "cores": "64", + "disk": "EBS - 10000 Mbps", + "ram": "256 GiB", + }, + "m3.medium": { + "id": "m3.medium", + "cores": "1", + "disk": "SSD (1 x 4)", + "ram": "3.75 GiB", + }, + "m3.large": { + "id": "m3.large", + "cores": "2", + "disk": "SSD (1 x 32)", + "ram": "7.5 GiB", + }, + "m3.xlarge": { + "id": "m3.xlarge", + "cores": "4", + "disk": "SSD (2 x 40)", + "ram": "15 GiB", + }, + "m3.2xlarge": { + "id": "m3.2xlarge", + "cores": "8", + "disk": "SSD (2 x 80)", + "ram": "30 GiB", + }, + }, } return sizes def avail_images(kwargs=None, call=None): - ''' + """ Return a dict of all available VM images on the cloud provider. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) if not isinstance(kwargs, dict): kwargs = {} - if 'owner' in kwargs: - owner = kwargs['owner'] + if "owner" in kwargs: + owner = kwargs["owner"] else: provider = get_configured_provider() owner = config.get_cloud_config_value( - 'owner', provider, __opts__, default='amazon' + "owner", provider, __opts__, default="amazon" ) ret = {} - params = {'Action': 'DescribeImages', - 'Owner': owner} - images = aws.query(params, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + params = {"Action": "DescribeImages", "Owner": owner} + images = aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) for image in images: - ret[image['imageId']] = image + ret[image["imageId"]] = image return ret def script(vm_): - ''' + """ Return the script deployment object - ''' + """ return salt.utils.cloud.os_script( - config.get_cloud_config_value('script', vm_, __opts__), + config.get_cloud_config_value("script", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) def keyname(vm_): - ''' + """ Return the keyname - ''' - return config.get_cloud_config_value( - 'keyname', vm_, __opts__, search_global=False - ) + """ + return config.get_cloud_config_value("keyname", vm_, __opts__, search_global=False) def securitygroup(vm_): - ''' + """ Return the security group - ''' + """ return config.get_cloud_config_value( - 'securitygroup', vm_, __opts__, search_global=False + "securitygroup", vm_, __opts__, search_global=False ) def iam_profile(vm_): - ''' + """ Return the IAM profile. The IAM instance profile to associate with the instances. @@ -1022,38 +1040,37 @@ def iam_profile(vm_): Example: s3access - ''' + """ return config.get_cloud_config_value( - 'iam_profile', vm_, __opts__, search_global=False + "iam_profile", vm_, __opts__, search_global=False ) def ssh_interface(vm_): - ''' + """ Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. - ''' + """ ret = config.get_cloud_config_value( - 'ssh_interface', vm_, __opts__, default='public_ips', - search_global=False + "ssh_interface", vm_, __opts__, default="public_ips", search_global=False ) - if ret not in ('public_ips', 'private_ips'): + if ret not in ("public_ips", "private_ips"): log.warning( - 'Invalid ssh_interface: %s. ' + "Invalid ssh_interface: %s. " 'Allowed options are ("public_ips", "private_ips"). ' - 'Defaulting to "public_ips".', ret + 'Defaulting to "public_ips".', + ret, ) - ret = 'public_ips' + ret = "public_ips" return ret def get_ssh_gateway_config(vm_): - ''' + """ Return the ssh_gateway configuration. - ''' + """ ssh_gateway = config.get_cloud_config_value( - 'ssh_gateway', vm_, __opts__, default=None, - search_global=False + "ssh_gateway", vm_, __opts__, default=None, search_global=False ) # Check to see if a SSH Gateway will be used. @@ -1063,110 +1080,105 @@ def get_ssh_gateway_config(vm_): # Create dictionary of configuration items # ssh_gateway - ssh_gateway_config = {'ssh_gateway': ssh_gateway} + ssh_gateway_config = {"ssh_gateway": ssh_gateway} # ssh_gateway_port - ssh_gateway_config['ssh_gateway_port'] = config.get_cloud_config_value( - 'ssh_gateway_port', vm_, __opts__, default=None, - search_global=False + ssh_gateway_config["ssh_gateway_port"] = config.get_cloud_config_value( + "ssh_gateway_port", vm_, __opts__, default=None, search_global=False ) # ssh_gateway_username - ssh_gateway_config['ssh_gateway_user'] = config.get_cloud_config_value( - 'ssh_gateway_username', vm_, __opts__, default=None, - search_global=False + ssh_gateway_config["ssh_gateway_user"] = config.get_cloud_config_value( + "ssh_gateway_username", vm_, __opts__, default=None, search_global=False ) # ssh_gateway_private_key - ssh_gateway_config['ssh_gateway_key'] = config.get_cloud_config_value( - 'ssh_gateway_private_key', vm_, __opts__, default=None, - search_global=False + ssh_gateway_config["ssh_gateway_key"] = config.get_cloud_config_value( + "ssh_gateway_private_key", vm_, __opts__, default=None, search_global=False ) # ssh_gateway_password - ssh_gateway_config['ssh_gateway_password'] = config.get_cloud_config_value( - 'ssh_gateway_password', vm_, __opts__, default=None, - search_global=False + ssh_gateway_config["ssh_gateway_password"] = config.get_cloud_config_value( + "ssh_gateway_password", vm_, __opts__, default=None, search_global=False ) # ssh_gateway_command - ssh_gateway_config['ssh_gateway_command'] = config.get_cloud_config_value( - 'ssh_gateway_command', vm_, __opts__, default=None, - search_global=False + ssh_gateway_config["ssh_gateway_command"] = config.get_cloud_config_value( + "ssh_gateway_command", vm_, __opts__, default=None, search_global=False ) # Check if private key exists - key_filename = ssh_gateway_config['ssh_gateway_key'] + key_filename = ssh_gateway_config["ssh_gateway_key"] if key_filename is not None and not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined ssh_gateway_private_key \'{0}\' does not exist' - .format(key_filename) + "The defined ssh_gateway_private_key '{0}' does not exist".format( + key_filename + ) ) - elif ( - key_filename is None and - not ssh_gateway_config['ssh_gateway_password'] - ): + elif key_filename is None and not ssh_gateway_config["ssh_gateway_password"]: raise SaltCloudConfigError( - 'No authentication method. Please define: ' - ' ssh_gateway_password or ssh_gateway_private_key' + "No authentication method. Please define: " + " ssh_gateway_password or ssh_gateway_private_key" ) return ssh_gateway_config def get_location(vm_=None): - ''' + """ Return the EC2 region to use, in this order: - CLI parameter - VM parameter - Cloud profile setting - ''' + """ return __opts__.get( - 'location', + "location", config.get_cloud_config_value( - 'location', + "location", vm_ or get_configured_provider(), __opts__, default=DEFAULT_LOCATION, - search_global=False - ) + search_global=False, + ), ) def avail_locations(call=None): - ''' + """ List all available locations - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) ret = {} - params = {'Action': 'DescribeRegions'} - result = aws.query(params, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + params = {"Action": "DescribeRegions"} + result = aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) for region in result: - ret[region['regionName']] = { - 'name': region['regionName'], - 'endpoint': region['regionEndpoint'], + ret[region["regionName"]] = { + "name": region["regionName"], + "endpoint": region["regionEndpoint"], } return ret def get_availability_zone(vm_): - ''' + """ Return the availability zone to use - ''' + """ avz = config.get_cloud_config_value( - 'availability_zone', vm_, __opts__, search_global=False + "availability_zone", vm_, __opts__, search_global=False ) if avz is None: @@ -1177,94 +1189,98 @@ def get_availability_zone(vm_): # Validate user-specified AZ if avz not in zones: raise SaltCloudException( - 'The specified availability zone isn\'t valid in this region: ' - '{0}\n'.format( - avz - ) + "The specified availability zone isn't valid in this region: " + "{0}\n".format(avz) ) # check specified AZ is available - elif zones[avz] != 'available': + elif zones[avz] != "available": raise SaltCloudException( - 'The specified availability zone isn\'t currently available: ' - '{0}\n'.format( - avz - ) + "The specified availability zone isn't currently available: " + "{0}\n".format(avz) ) return avz def get_tenancy(vm_): - ''' + """ Returns the Tenancy to use. Can be "dedicated" or "default". Cannot be present for spot instances. - ''' - return config.get_cloud_config_value( - 'tenancy', vm_, __opts__, search_global=False - ) + """ + return config.get_cloud_config_value("tenancy", vm_, __opts__, search_global=False) def get_imageid(vm_): - ''' + """ Returns the ImageId to use - ''' - image = config.get_cloud_config_value( - 'image', vm_, __opts__, search_global=False - ) - if image.startswith('ami-'): + """ + image = config.get_cloud_config_value("image", vm_, __opts__, search_global=False) + if image.startswith("ami-"): return image # a poor man's cache - if not hasattr(get_imageid, 'images'): + if not hasattr(get_imageid, "images"): get_imageid.images = {} elif image in get_imageid.images: return get_imageid.images[image] - params = {'Action': 'DescribeImages', - 'Filter.0.Name': 'name', - 'Filter.0.Value.0': image} + params = { + "Action": "DescribeImages", + "Filter.0.Name": "name", + "Filter.0.Value.0": image, + } # Query AWS, sort by 'creationDate' and get the last imageId - _t = lambda x: datetime.datetime.strptime(x['creationDate'], '%Y-%m-%dT%H:%M:%S.%fZ') - image_id = sorted(aws.query(params, location=get_location(), - provider=get_provider(), opts=__opts__, sigver='4'), - key=cmp_to_key(lambda i, j: salt.utils.compat.cmp(_t(i), _t(j))) - )[-1]['imageId'] + _t = lambda x: datetime.datetime.strptime( + x["creationDate"], "%Y-%m-%dT%H:%M:%S.%fZ" + ) + image_id = sorted( + aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ), + key=cmp_to_key(lambda i, j: salt.utils.compat.cmp(_t(i), _t(j))), + )[-1]["imageId"] get_imageid.images[image] = image_id return image_id def _get_subnetname_id(subnetname): - ''' + """ Returns the SubnetId of a SubnetName to use - ''' - params = {'Action': 'DescribeSubnets'} - for subnet in aws.query(params, location=get_location(), - provider=get_provider(), opts=__opts__, sigver='4'): - tags = subnet.get('tagSet', {}).get('item', {}) + """ + params = {"Action": "DescribeSubnets"} + for subnet in aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ): + tags = subnet.get("tagSet", {}).get("item", {}) if not isinstance(tags, list): tags = [tags] for tag in tags: - if tag['key'] == 'Name' and tag['value'] == subnetname: - log.debug( - 'AWS Subnet ID of %s is %s', - subnetname, subnet['subnetId'] - ) - return subnet['subnetId'] + if tag["key"] == "Name" and tag["value"] == subnetname: + log.debug("AWS Subnet ID of %s is %s", subnetname, subnet["subnetId"]) + return subnet["subnetId"] return None def get_subnetid(vm_): - ''' + """ Returns the SubnetId to use - ''' + """ subnetid = config.get_cloud_config_value( - 'subnetid', vm_, __opts__, search_global=False + "subnetid", vm_, __opts__, search_global=False ) if subnetid: return subnetid subnetname = config.get_cloud_config_value( - 'subnetname', vm_, __opts__, search_global=False + "subnetname", vm_, __opts__, search_global=False ) if subnetname: return _get_subnetname_id(subnetname) @@ -1272,34 +1288,35 @@ def get_subnetid(vm_): def _get_securitygroupname_id(securitygroupname_list): - ''' + """ Returns the SecurityGroupId of a SecurityGroupName to use - ''' + """ securitygroupid_set = set() if not isinstance(securitygroupname_list, list): securitygroupname_list = [securitygroupname_list] - params = {'Action': 'DescribeSecurityGroups'} - for sg in aws.query(params, location=get_location(), - provider=get_provider(), opts=__opts__, sigver='4'): - if sg['groupName'] in securitygroupname_list: + params = {"Action": "DescribeSecurityGroups"} + for sg in aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ): + if sg["groupName"] in securitygroupname_list: log.debug( - 'AWS SecurityGroup ID of %s is %s', - sg['groupName'], sg['groupId'] + "AWS SecurityGroup ID of %s is %s", sg["groupName"], sg["groupId"] ) - securitygroupid_set.add(sg['groupId']) + securitygroupid_set.add(sg["groupId"]) return list(securitygroupid_set) def securitygroupid(vm_): - ''' + """ Returns the SecurityGroupId - ''' + """ securitygroupid_set = set() securitygroupid_list = config.get_cloud_config_value( - 'securitygroupid', - vm_, - __opts__, - search_global=False + "securitygroupid", vm_, __opts__, search_global=False ) # If the list is None, then the set will remain empty # If the list is already a set then calling 'set' on it is a no-op @@ -1309,184 +1326,204 @@ def securitygroupid(vm_): securitygroupid_set = securitygroupid_set.union(set(securitygroupid_list)) securitygroupname_list = config.get_cloud_config_value( - 'securitygroupname', vm_, __opts__, search_global=False + "securitygroupname", vm_, __opts__, search_global=False ) if securitygroupname_list: if not isinstance(securitygroupname_list, list): securitygroupname_list = [securitygroupname_list] - params = {'Action': 'DescribeSecurityGroups'} - for sg in aws.query(params, location=get_location(), - provider=get_provider(), opts=__opts__, sigver='4'): - if sg['groupName'] in securitygroupname_list: + params = {"Action": "DescribeSecurityGroups"} + for sg in aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ): + if sg["groupName"] in securitygroupname_list: log.debug( - 'AWS SecurityGroup ID of %s is %s', - sg['groupName'], sg['groupId'] + "AWS SecurityGroup ID of %s is %s", sg["groupName"], sg["groupId"] ) - securitygroupid_set.add(sg['groupId']) + securitygroupid_set.add(sg["groupId"]) return list(securitygroupid_set) def get_placementgroup(vm_): - ''' + """ Returns the PlacementGroup to use - ''' + """ return config.get_cloud_config_value( - 'placementgroup', vm_, __opts__, search_global=False + "placementgroup", vm_, __opts__, search_global=False ) def get_spot_config(vm_): - ''' + """ Returns the spot instance configuration for the provided vm - ''' + """ return config.get_cloud_config_value( - 'spot_config', vm_, __opts__, search_global=False + "spot_config", vm_, __opts__, search_global=False ) def get_provider(vm_=None): - ''' + """ Extract the provider name from vm - ''' + """ if vm_ is None: - provider = __active_provider_name__ or 'ec2' + provider = __active_provider_name__ or "ec2" else: - provider = vm_.get('provider', 'ec2') + provider = vm_.get("provider", "ec2") - if ':' in provider: - prov_comps = provider.split(':') + if ":" in provider: + prov_comps = provider.split(":") provider = prov_comps[0] return provider def _list_availability_zones(vm_=None): - ''' + """ List all availability zones in the current region - ''' + """ ret = {} - params = {'Action': 'DescribeAvailabilityZones', - 'Filter.0.Name': 'region-name', - 'Filter.0.Value.0': get_location(vm_)} - result = aws.query(params, - location=get_location(vm_), - provider=get_provider(), - opts=__opts__, - sigver='4') + params = { + "Action": "DescribeAvailabilityZones", + "Filter.0.Name": "region-name", + "Filter.0.Value.0": get_location(vm_), + } + result = aws.query( + params, + location=get_location(vm_), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) for zone in result: - ret[zone['zoneName']] = zone['zoneState'] + ret[zone["zoneName"]] = zone["zoneState"] return ret def block_device_mappings(vm_): - ''' + """ Return the block device mapping: .. code-block:: python [{'DeviceName': '/dev/sdb', 'VirtualName': 'ephemeral0'}, {'DeviceName': '/dev/sdc', 'VirtualName': 'ephemeral1'}] - ''' + """ return config.get_cloud_config_value( - 'block_device_mappings', vm_, __opts__, search_global=True + "block_device_mappings", vm_, __opts__, search_global=True ) def _request_eip(interface, vm_): - ''' + """ Request and return Elastic IP - ''' - params = {'Action': 'AllocateAddress'} - params['Domain'] = interface.setdefault('domain', 'vpc') - eips = aws.query(params, - return_root=True, - location=get_location(vm_), - provider=get_provider(), - opts=__opts__, - sigver='4') + """ + params = {"Action": "AllocateAddress"} + params["Domain"] = interface.setdefault("domain", "vpc") + eips = aws.query( + params, + return_root=True, + location=get_location(vm_), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) for eip in eips: - if 'allocationId' in eip: - return eip['allocationId'] + if "allocationId" in eip: + return eip["allocationId"] return None def _create_eni_if_necessary(interface, vm_): - ''' + """ Create an Elastic Interface if necessary and return a Network Interface Specification - ''' - if 'NetworkInterfaceId' in interface and interface['NetworkInterfaceId'] is not None: - return {'DeviceIndex': interface['DeviceIndex'], - 'NetworkInterfaceId': interface['NetworkInterfaceId']} + """ + if ( + "NetworkInterfaceId" in interface + and interface["NetworkInterfaceId"] is not None + ): + return { + "DeviceIndex": interface["DeviceIndex"], + "NetworkInterfaceId": interface["NetworkInterfaceId"], + } - params = {'Action': 'DescribeSubnets'} - subnet_query = aws.query(params, - return_root=True, - location=get_location(vm_), - provider=get_provider(), - opts=__opts__, - sigver='4') + params = {"Action": "DescribeSubnets"} + subnet_query = aws.query( + params, + return_root=True, + location=get_location(vm_), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) - if 'SecurityGroupId' not in interface and 'securitygroupname' in interface: - interface['SecurityGroupId'] = _get_securitygroupname_id(interface['securitygroupname']) - if 'SubnetId' not in interface and 'subnetname' in interface: - interface['SubnetId'] = _get_subnetname_id(interface['subnetname']) + if "SecurityGroupId" not in interface and "securitygroupname" in interface: + interface["SecurityGroupId"] = _get_securitygroupname_id( + interface["securitygroupname"] + ) + if "SubnetId" not in interface and "subnetname" in interface: + interface["SubnetId"] = _get_subnetname_id(interface["subnetname"]) subnet_id = _get_subnet_id_for_interface(subnet_query, interface) if not subnet_id: raise SaltCloudConfigError( - 'No such subnet <{0}>'.format(interface.get('SubnetId')) + "No such subnet <{0}>".format(interface.get("SubnetId")) ) - params = {'SubnetId': subnet_id} + params = {"SubnetId": subnet_id} - for k in 'Description', 'PrivateIpAddress', 'SecondaryPrivateIpAddressCount': + for k in "Description", "PrivateIpAddress", "SecondaryPrivateIpAddressCount": if k in interface: params[k] = interface[k] - for k in 'PrivateIpAddresses', 'SecurityGroupId': + for k in "PrivateIpAddresses", "SecurityGroupId": if k in interface: params.update(_param_from_config(k, interface[k])) - if 'AssociatePublicIpAddress' in interface: + if "AssociatePublicIpAddress" in interface: # Associating a public address in a VPC only works when the interface is not # created beforehand, but as a part of the machine creation request. - for k in ('DeviceIndex', 'AssociatePublicIpAddress', 'NetworkInterfaceId'): + for k in ("DeviceIndex", "AssociatePublicIpAddress", "NetworkInterfaceId"): if k in interface: params[k] = interface[k] - params['DeleteOnTermination'] = interface.get('delete_interface_on_terminate', True) + params["DeleteOnTermination"] = interface.get( + "delete_interface_on_terminate", True + ) return params - params['Action'] = 'CreateNetworkInterface' + params["Action"] = "CreateNetworkInterface" - result = aws.query(params, - return_root=True, - location=get_location(vm_), - provider=get_provider(), - opts=__opts__, - sigver='4') - - eni_desc = result[1] - if not eni_desc or not eni_desc.get('networkInterfaceId'): - raise SaltCloudException('Failed to create interface: {0}'.format(result)) - - eni_id = eni_desc.get('networkInterfaceId') - log.debug( - 'Created network interface %s inst %s', - eni_id, interface['DeviceIndex'] + result = aws.query( + params, + return_root=True, + location=get_location(vm_), + provider=get_provider(), + opts=__opts__, + sigver="4", ) - associate_public_ip = interface.get('AssociatePublicIpAddress', False) + eni_desc = result[1] + if not eni_desc or not eni_desc.get("networkInterfaceId"): + raise SaltCloudException("Failed to create interface: {0}".format(result)) + + eni_id = eni_desc.get("networkInterfaceId") + log.debug("Created network interface %s inst %s", eni_id, interface["DeviceIndex"]) + + associate_public_ip = interface.get("AssociatePublicIpAddress", False) if isinstance(associate_public_ip, six.string_types): # Assume id of EIP as value _associate_eip_with_interface(eni_id, associate_public_ip, vm_=vm_) - if interface.get('associate_eip'): - _associate_eip_with_interface(eni_id, interface.get('associate_eip'), vm_=vm_) - elif interface.get('allocate_new_eip'): + if interface.get("associate_eip"): + _associate_eip_with_interface(eni_id, interface.get("associate_eip"), vm_=vm_) + elif interface.get("allocate_new_eip"): _new_eip = _request_eip(interface, vm_) _associate_eip_with_interface(eni_id, _new_eip, vm_=vm_) - elif interface.get('allocate_new_eips'): + elif interface.get("allocate_new_eips"): addr_list = _list_interface_private_addrs(eni_desc) eip_list = [] for idx, addr in enumerate(addr_list): @@ -1494,98 +1531,101 @@ def _create_eni_if_necessary(interface, vm_): for idx, addr in enumerate(addr_list): _associate_eip_with_interface(eni_id, eip_list[idx], addr, vm_=vm_) - if 'Name' in interface: - tag_params = {'Action': 'CreateTags', - 'ResourceId.0': eni_id, - 'Tag.0.Key': 'Name', - 'Tag.0.Value': interface['Name']} - tag_response = aws.query(tag_params, - return_root=True, - location=get_location(vm_), - provider=get_provider(), - opts=__opts__, - sigver='4') - if 'error' in tag_response: - log.error('Failed to set name of interface {0}') + if "Name" in interface: + tag_params = { + "Action": "CreateTags", + "ResourceId.0": eni_id, + "Tag.0.Key": "Name", + "Tag.0.Value": interface["Name"], + } + tag_response = aws.query( + tag_params, + return_root=True, + location=get_location(vm_), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) + if "error" in tag_response: + log.error("Failed to set name of interface {0}") - return {'DeviceIndex': interface['DeviceIndex'], - 'NetworkInterfaceId': eni_id} + return {"DeviceIndex": interface["DeviceIndex"], "NetworkInterfaceId": eni_id} def _get_subnet_id_for_interface(subnet_query, interface): for subnet_query_result in subnet_query: - if 'item' in subnet_query_result: - if isinstance(subnet_query_result['item'], dict): - subnet_id = _get_subnet_from_subnet_query(subnet_query_result['item'], - interface) + if "item" in subnet_query_result: + if isinstance(subnet_query_result["item"], dict): + subnet_id = _get_subnet_from_subnet_query( + subnet_query_result["item"], interface + ) if subnet_id is not None: return subnet_id else: - for subnet in subnet_query_result['item']: + for subnet in subnet_query_result["item"]: subnet_id = _get_subnet_from_subnet_query(subnet, interface) if subnet_id is not None: return subnet_id def _get_subnet_from_subnet_query(subnet_query, interface): - if 'subnetId' in subnet_query: - if interface.get('SubnetId'): - if subnet_query['subnetId'] == interface['SubnetId']: - return subnet_query['subnetId'] + if "subnetId" in subnet_query: + if interface.get("SubnetId"): + if subnet_query["subnetId"] == interface["SubnetId"]: + return subnet_query["subnetId"] else: - return subnet_query['subnetId'] + return subnet_query["subnetId"] def _list_interface_private_addrs(eni_desc): - ''' + """ Returns a list of all of the private IP addresses attached to a network interface. The 'primary' address will be listed first. - ''' - primary = eni_desc.get('privateIpAddress') + """ + primary = eni_desc.get("privateIpAddress") if not primary: return None addresses = [primary] - lst = eni_desc.get('privateIpAddressesSet', {}).get('item', []) + lst = eni_desc.get("privateIpAddressesSet", {}).get("item", []) if not isinstance(lst, list): return addresses for entry in lst: - if entry.get('primary') == 'true': + if entry.get("primary") == "true": continue - if entry.get('privateIpAddress'): - addresses.append(entry.get('privateIpAddress')) + if entry.get("privateIpAddress"): + addresses.append(entry.get("privateIpAddress")) return addresses def _modify_eni_properties(eni_id, properties=None, vm_=None): - ''' + """ Change properties of the interface with id eni_id to the values in properties dict - ''' + """ if not isinstance(properties, dict): - raise SaltCloudException( - 'ENI properties must be a dictionary' - ) + raise SaltCloudException("ENI properties must be a dictionary") - params = {'Action': 'ModifyNetworkInterfaceAttribute', - 'NetworkInterfaceId': eni_id} + params = {"Action": "ModifyNetworkInterfaceAttribute", "NetworkInterfaceId": eni_id} for k, v in six.iteritems(properties): params[k] = v - result = aws.query(params, - return_root=True, - location=get_location(vm_), - provider=get_provider(), - opts=__opts__, - sigver='4') + result = aws.query( + params, + return_root=True, + location=get_location(vm_), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) - if isinstance(result, dict) and result.get('error'): + if isinstance(result, dict) and result.get("error"): raise SaltCloudException( - 'Could not change interface <{0}> attributes <\'{1}\'>'.format( + "Could not change interface <{0}> attributes <'{1}'>".format( eni_id, properties ) ) @@ -1594,7 +1634,7 @@ def _modify_eni_properties(eni_id, properties=None, vm_=None): def _associate_eip_with_interface(eni_id, eip_id, private_ip=None, vm_=None): - ''' + """ Accept the id of a network interface, and the id of an elastic ip address, and associate the two of them, such that traffic sent to the elastic ip address will be forwarded (NATted) to this network interface. @@ -1602,75 +1642,90 @@ def _associate_eip_with_interface(eni_id, eip_id, private_ip=None, vm_=None): Optionally specify the private (10.x.x.x) IP address that traffic should be NATted to - useful if you have multiple IP addresses assigned to an interface. - ''' - params = {'Action': 'AssociateAddress', - 'NetworkInterfaceId': eni_id, - 'AllocationId': eip_id} + """ + params = { + "Action": "AssociateAddress", + "NetworkInterfaceId": eni_id, + "AllocationId": eip_id, + } if private_ip: - params['PrivateIpAddress'] = private_ip + params["PrivateIpAddress"] = private_ip - result = aws.query(params, - return_root=True, - location=get_location(vm_), - provider=get_provider(), - opts=__opts__, - sigver='4') - - if not result[2].get('associationId'): - raise SaltCloudException( - 'Could not associate elastic ip address ' - '<{0}> with network interface <{1}>'.format( - eip_id, eni_id - ) - ) - - log.debug( - 'Associated ElasticIP address %s with interface %s', - eip_id, eni_id + result = aws.query( + params, + return_root=True, + location=get_location(vm_), + provider=get_provider(), + opts=__opts__, + sigver="4", ) - return result[2].get('associationId') + if not result[2].get("associationId"): + raise SaltCloudException( + "Could not associate elastic ip address " + "<{0}> with network interface <{1}>".format(eip_id, eni_id) + ) + + log.debug("Associated ElasticIP address %s with interface %s", eip_id, eni_id) + + return result[2].get("associationId") def _update_enis(interfaces, instance, vm_=None): config_enis = {} instance_enis = [] for interface in interfaces: - if 'DeviceIndex' in interface: - if interface['DeviceIndex'] in config_enis: - log.error( - 'Duplicate DeviceIndex in profile. Cannot update ENIs.' - ) + if "DeviceIndex" in interface: + if interface["DeviceIndex"] in config_enis: + log.error("Duplicate DeviceIndex in profile. Cannot update ENIs.") return None - config_enis[six.text_type(interface['DeviceIndex'])] = interface - query_enis = instance[0]['instancesSet']['item']['networkInterfaceSet']['item'] + config_enis[six.text_type(interface["DeviceIndex"])] = interface + query_enis = instance[0]["instancesSet"]["item"]["networkInterfaceSet"]["item"] if isinstance(query_enis, list): for query_eni in query_enis: - instance_enis.append((query_eni['networkInterfaceId'], query_eni['attachment'])) + instance_enis.append( + (query_eni["networkInterfaceId"], query_eni["attachment"]) + ) else: - instance_enis.append((query_enis['networkInterfaceId'], query_enis['attachment'])) + instance_enis.append( + (query_enis["networkInterfaceId"], query_enis["attachment"]) + ) for eni_id, eni_data in instance_enis: delete_on_terminate = True - if 'DeleteOnTermination' in config_enis[eni_data['deviceIndex']]: - delete_on_terminate = config_enis[eni_data['deviceIndex']]['DeleteOnTermination'] - elif 'delete_interface_on_terminate' in config_enis[eni_data['deviceIndex']]: - delete_on_terminate = config_enis[eni_data['deviceIndex']]['delete_interface_on_terminate'] + if "DeleteOnTermination" in config_enis[eni_data["deviceIndex"]]: + delete_on_terminate = config_enis[eni_data["deviceIndex"]][ + "DeleteOnTermination" + ] + elif "delete_interface_on_terminate" in config_enis[eni_data["deviceIndex"]]: + delete_on_terminate = config_enis[eni_data["deviceIndex"]][ + "delete_interface_on_terminate" + ] - params_attachment = {'Attachment.AttachmentId': eni_data['attachmentId'], - 'Attachment.DeleteOnTermination': delete_on_terminate} - set_eni_attachment_attributes = _modify_eni_properties(eni_id, params_attachment, vm_=vm_) + params_attachment = { + "Attachment.AttachmentId": eni_data["attachmentId"], + "Attachment.DeleteOnTermination": delete_on_terminate, + } + set_eni_attachment_attributes = _modify_eni_properties( + eni_id, params_attachment, vm_=vm_ + ) - if 'SourceDestCheck' in config_enis[eni_data['deviceIndex']]: - params_sourcedest = {'SourceDestCheck.Value': config_enis[eni_data['deviceIndex']]['SourceDestCheck']} - set_eni_sourcedest_property = _modify_eni_properties(eni_id, params_sourcedest, vm_=vm_) + if "SourceDestCheck" in config_enis[eni_data["deviceIndex"]]: + params_sourcedest = { + "SourceDestCheck.Value": config_enis[eni_data["deviceIndex"]][ + "SourceDestCheck" + ] + } + set_eni_sourcedest_property = _modify_eni_properties( + eni_id, params_sourcedest, vm_=vm_ + ) return None def _param_from_config(key, data): - ''' + """ Return EC2 API parameters based on the given config data. Examples: @@ -1718,17 +1773,17 @@ def _param_from_config(key, data): >>> _param_from_config('IamInstanceProfile', data) {'IamInstanceProfile.Arn': 'dummyarn', 'IamInstanceProfile.Name': 'Tester'} - ''' + """ param = {} if isinstance(data, dict): for k, v in six.iteritems(data): - param.update(_param_from_config('{0}.{1}'.format(key, k), v)) + param.update(_param_from_config("{0}.{1}".format(key, k), v)) elif isinstance(data, list) or isinstance(data, tuple): for idx, conf_item in enumerate(data): - prefix = '{0}.{1}'.format(key, idx) + prefix = "{0}.{1}".format(key, idx) param.update(_param_from_config(prefix, conf_item)) else: @@ -1742,41 +1797,42 @@ def _param_from_config(key, data): def request_instance(vm_=None, call=None): - ''' + """ Put together all of the information necessary to request an instance on EC2, and then fire off the request the instance. Returns data about the instance - ''' - if call == 'function': + """ + if call == "function": # Technically this function may be called other ways too, but it # definitely cannot be called with --function. raise SaltCloudSystemExit( - 'The request_instance action must be called with -a or --action.' + "The request_instance action must be called with -a or --action." ) - location = vm_.get('location', get_location(vm_)) + location = vm_.get("location", get_location(vm_)) # do we launch a regular vm or a spot instance? # see http://goo.gl/hYZ13f for more information on EC2 API spot_config = get_spot_config(vm_) if spot_config is not None: - if 'spot_price' not in spot_config: + if "spot_price" not in spot_config: raise SaltCloudSystemExit( - 'Spot instance config for {0} requires a spot_price ' - 'attribute.'.format(vm_['name']) + "Spot instance config for {0} requires a spot_price " + "attribute.".format(vm_["name"]) ) - params = {'Action': 'RequestSpotInstances', - 'InstanceCount': '1', - 'Type': spot_config['type'] - if 'type' in spot_config else 'one-time', - 'SpotPrice': spot_config['spot_price']} + params = { + "Action": "RequestSpotInstances", + "InstanceCount": "1", + "Type": spot_config["type"] if "type" in spot_config else "one-time", + "SpotPrice": spot_config["spot_price"], + } # All of the necessary launch parameters for a VM when using # spot instances are the same except for the prefix below # being tacked on. - spot_prefix = 'LaunchSpecification.' + spot_prefix = "LaunchSpecification." # regular EC2 instance else: @@ -1787,227 +1843,225 @@ def request_instance(vm_=None, call=None): # InstanceId as the name). This interface is expected to change, so # use at your own risk. min_instance = config.get_cloud_config_value( - 'min_instance', vm_, __opts__, search_global=False, default=1 + "min_instance", vm_, __opts__, search_global=False, default=1 ) max_instance = config.get_cloud_config_value( - 'max_instance', vm_, __opts__, search_global=False, default=1 + "max_instance", vm_, __opts__, search_global=False, default=1 ) - params = {'Action': 'RunInstances', - 'MinCount': min_instance, - 'MaxCount': max_instance} + params = { + "Action": "RunInstances", + "MinCount": min_instance, + "MaxCount": max_instance, + } # Normal instances should have no prefix. - spot_prefix = '' + spot_prefix = "" image_id = get_imageid(vm_) - params[spot_prefix + 'ImageId'] = image_id + params[spot_prefix + "ImageId"] = image_id userdata = None userdata_file = config.get_cloud_config_value( - 'userdata_file', vm_, __opts__, search_global=False, default=None + "userdata_file", vm_, __opts__, search_global=False, default=None ) if userdata_file is None: userdata = config.get_cloud_config_value( - 'userdata', vm_, __opts__, search_global=False, default=None + "userdata", vm_, __opts__, search_global=False, default=None ) else: - log.trace('userdata_file: %s', userdata_file) + log.trace("userdata_file: %s", userdata_file) if os.path.exists(userdata_file): - with salt.utils.files.fopen(userdata_file, 'r') as fh_: + with salt.utils.files.fopen(userdata_file, "r") as fh_: userdata = salt.utils.stringutils.to_unicode(fh_.read()) userdata = salt.utils.cloud.userdata_template(__opts__, vm_, userdata) if userdata is not None: try: - params[spot_prefix + 'UserData'] = base64.b64encode( + params[spot_prefix + "UserData"] = base64.b64encode( salt.utils.stringutils.to_bytes(userdata) ) except Exception as exc: # pylint: disable=broad-except - log.exception('Failed to encode userdata: %s', exc) + log.exception("Failed to encode userdata: %s", exc) - vm_size = config.get_cloud_config_value( - 'size', vm_, __opts__, search_global=False - ) - params[spot_prefix + 'InstanceType'] = vm_size + vm_size = config.get_cloud_config_value("size", vm_, __opts__, search_global=False) + params[spot_prefix + "InstanceType"] = vm_size ex_keyname = keyname(vm_) if ex_keyname: - params[spot_prefix + 'KeyName'] = ex_keyname + params[spot_prefix + "KeyName"] = ex_keyname ex_securitygroup = securitygroup(vm_) if ex_securitygroup: if not isinstance(ex_securitygroup, list): - params[spot_prefix + 'SecurityGroup.1'] = ex_securitygroup + params[spot_prefix + "SecurityGroup.1"] = ex_securitygroup else: for counter, sg_ in enumerate(ex_securitygroup): - params[spot_prefix + 'SecurityGroup.{0}'.format(counter)] = sg_ + params[spot_prefix + "SecurityGroup.{0}".format(counter)] = sg_ ex_iam_profile = iam_profile(vm_) if ex_iam_profile: try: - if ex_iam_profile.startswith('arn:aws:iam:'): - params[ - spot_prefix + 'IamInstanceProfile.Arn' - ] = ex_iam_profile + if ex_iam_profile.startswith("arn:aws:iam:"): + params[spot_prefix + "IamInstanceProfile.Arn"] = ex_iam_profile else: - params[ - spot_prefix + 'IamInstanceProfile.Name' - ] = ex_iam_profile + params[spot_prefix + "IamInstanceProfile.Name"] = ex_iam_profile except AttributeError: - raise SaltCloudConfigError( - '\'iam_profile\' should be a string value.' - ) + raise SaltCloudConfigError("'iam_profile' should be a string value.") az_ = get_availability_zone(vm_) if az_ is not None: - params[spot_prefix + 'Placement.AvailabilityZone'] = az_ + params[spot_prefix + "Placement.AvailabilityZone"] = az_ tenancy_ = get_tenancy(vm_) if tenancy_ is not None: if spot_config is not None: raise SaltCloudConfigError( - 'Spot instance config for {0} does not support ' - 'specifying tenancy.'.format(vm_['name']) + "Spot instance config for {0} does not support " + "specifying tenancy.".format(vm_["name"]) ) - params['Placement.Tenancy'] = tenancy_ + params["Placement.Tenancy"] = tenancy_ subnetid_ = get_subnetid(vm_) if subnetid_ is not None: - params[spot_prefix + 'SubnetId'] = subnetid_ + params[spot_prefix + "SubnetId"] = subnetid_ ex_securitygroupid = securitygroupid(vm_) if ex_securitygroupid: if not isinstance(ex_securitygroupid, list): - params[spot_prefix + 'SecurityGroupId.1'] = ex_securitygroupid + params[spot_prefix + "SecurityGroupId.1"] = ex_securitygroupid else: for counter, sg_ in enumerate(ex_securitygroupid): - params[ - spot_prefix + 'SecurityGroupId.{0}'.format(counter) - ] = sg_ + params[spot_prefix + "SecurityGroupId.{0}".format(counter)] = sg_ placementgroup_ = get_placementgroup(vm_) if placementgroup_ is not None: - params[spot_prefix + 'Placement.GroupName'] = placementgroup_ + params[spot_prefix + "Placement.GroupName"] = placementgroup_ blockdevicemappings_holder = block_device_mappings(vm_) if blockdevicemappings_holder: for _bd in blockdevicemappings_holder: - if 'tag' in _bd: - _bd.pop('tag') + if "tag" in _bd: + _bd.pop("tag") ex_blockdevicemappings = blockdevicemappings_holder if ex_blockdevicemappings: - params.update(_param_from_config(spot_prefix + 'BlockDeviceMapping', - ex_blockdevicemappings)) + params.update( + _param_from_config( + spot_prefix + "BlockDeviceMapping", ex_blockdevicemappings + ) + ) network_interfaces = config.get_cloud_config_value( - 'network_interfaces', - vm_, - __opts__, - search_global=False + "network_interfaces", vm_, __opts__, search_global=False ) if network_interfaces: eni_devices = [] for interface in network_interfaces: - log.debug('Create network interface: %s', interface) + log.debug("Create network interface: %s", interface) _new_eni = _create_eni_if_necessary(interface, vm_) eni_devices.append(_new_eni) - params.update(_param_from_config(spot_prefix + 'NetworkInterface', - eni_devices)) + params.update(_param_from_config(spot_prefix + "NetworkInterface", eni_devices)) set_ebs_optimized = config.get_cloud_config_value( - 'ebs_optimized', vm_, __opts__, search_global=False + "ebs_optimized", vm_, __opts__, search_global=False ) if set_ebs_optimized is not None: if not isinstance(set_ebs_optimized, bool): - raise SaltCloudConfigError( - '\'ebs_optimized\' should be a boolean value.' - ) - params[spot_prefix + 'EbsOptimized'] = set_ebs_optimized + raise SaltCloudConfigError("'ebs_optimized' should be a boolean value.") + params[spot_prefix + "EbsOptimized"] = set_ebs_optimized set_del_root_vol_on_destroy = config.get_cloud_config_value( - 'del_root_vol_on_destroy', vm_, __opts__, search_global=False + "del_root_vol_on_destroy", vm_, __opts__, search_global=False ) set_termination_protection = config.get_cloud_config_value( - 'termination_protection', vm_, __opts__, search_global=False + "termination_protection", vm_, __opts__, search_global=False ) if set_termination_protection is not None: if not isinstance(set_termination_protection, bool): raise SaltCloudConfigError( - '\'termination_protection\' should be a boolean value.' + "'termination_protection' should be a boolean value." + ) + params.update( + _param_from_config( + spot_prefix + "DisableApiTermination", set_termination_protection ) - params.update(_param_from_config(spot_prefix + 'DisableApiTermination', - set_termination_protection)) - - if set_del_root_vol_on_destroy and not isinstance(set_del_root_vol_on_destroy, bool): - raise SaltCloudConfigError( - '\'del_root_vol_on_destroy\' should be a boolean value.' ) - vm_['set_del_root_vol_on_destroy'] = set_del_root_vol_on_destroy + if set_del_root_vol_on_destroy and not isinstance( + set_del_root_vol_on_destroy, bool + ): + raise SaltCloudConfigError( + "'del_root_vol_on_destroy' should be a boolean value." + ) + + vm_["set_del_root_vol_on_destroy"] = set_del_root_vol_on_destroy if set_del_root_vol_on_destroy: # first make sure to look up the root device name # as Ubuntu and CentOS (and most likely other OSs) # use different device identifiers - log.info('Attempting to look up root device name for image id %s on ' - 'VM %s', image_id, vm_['name']) + log.info( + "Attempting to look up root device name for image id %s on " "VM %s", + image_id, + vm_["name"], + ) - rd_params = { - 'Action': 'DescribeImages', - 'ImageId.1': image_id - } + rd_params = {"Action": "DescribeImages", "ImageId.1": image_id} try: - rd_data = aws.query(rd_params, - location=get_location(vm_), - provider=get_provider(), - opts=__opts__, - sigver='4') - if 'error' in rd_data: - return rd_data['error'] - log.debug('EC2 Response: \'%s\'', rd_data) + rd_data = aws.query( + rd_params, + location=get_location(vm_), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) + if "error" in rd_data: + return rd_data["error"] + log.debug("EC2 Response: '%s'", rd_data) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error getting root device name for image id %s for ' - 'VM %s: \n%s', image_id, vm_['name'], exc, + "Error getting root device name for image id %s for " "VM %s: \n%s", + image_id, + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) raise # make sure we have a response if not rd_data: - err_msg = 'There was an error querying EC2 for the root device ' \ - 'of image id {0}. Empty response.'.format(image_id) + err_msg = ( + "There was an error querying EC2 for the root device " + "of image id {0}. Empty response.".format(image_id) + ) raise SaltCloudSystemExit(err_msg) # pull the root device name from the result and use it when # launching the new VM rd_name = None rd_type = None - if 'blockDeviceMapping' in rd_data[0]: + if "blockDeviceMapping" in rd_data[0]: # Some ami instances do not have a root volume. Ignore such cases - if rd_data[0]['blockDeviceMapping'] is not None: - item = rd_data[0]['blockDeviceMapping']['item'] + if rd_data[0]["blockDeviceMapping"] is not None: + item = rd_data[0]["blockDeviceMapping"]["item"] if isinstance(item, list): item = item[0] - rd_name = item['deviceName'] + rd_name = item["deviceName"] # Grab the volume type - rd_type = item['ebs'].get('volumeType', None) + rd_type = item["ebs"].get("volumeType", None) - log.info('Found root device name: %s', rd_name) + log.info("Found root device name: %s", rd_name) if rd_name is not None: if ex_blockdevicemappings: - dev_list = [ - dev['DeviceName'] for dev in ex_blockdevicemappings - ] + dev_list = [dev["DeviceName"] for dev in ex_blockdevicemappings] else: dev_list = [] @@ -2018,115 +2072,128 @@ def request_instance(vm_=None, call=None): dev_index = len(dev_list) # Add the device name in since it wasn't already there params[ - '{0}BlockDeviceMapping.{1}.DeviceName'.format( + "{0}BlockDeviceMapping.{1}.DeviceName".format( spot_prefix, dev_index ) ] = rd_name # Set the termination value - termination_key = '{0}BlockDeviceMapping.{1}.Ebs.DeleteOnTermination'.format(spot_prefix, dev_index) + termination_key = "{0}BlockDeviceMapping.{1}.Ebs.DeleteOnTermination".format( + spot_prefix, dev_index + ) params[termination_key] = six.text_type(set_del_root_vol_on_destroy).lower() # Use default volume type if not specified - if ex_blockdevicemappings and dev_index < len(ex_blockdevicemappings) and \ - 'Ebs.VolumeType' not in ex_blockdevicemappings[dev_index]: - type_key = '{0}BlockDeviceMapping.{1}.Ebs.VolumeType'.format(spot_prefix, dev_index) + if ( + ex_blockdevicemappings + and dev_index < len(ex_blockdevicemappings) + and "Ebs.VolumeType" not in ex_blockdevicemappings[dev_index] + ): + type_key = "{0}BlockDeviceMapping.{1}.Ebs.VolumeType".format( + spot_prefix, dev_index + ) params[type_key] = rd_type set_del_all_vols_on_destroy = config.get_cloud_config_value( - 'del_all_vols_on_destroy', vm_, __opts__, search_global=False, default=False + "del_all_vols_on_destroy", vm_, __opts__, search_global=False, default=False ) - if set_del_all_vols_on_destroy and not isinstance(set_del_all_vols_on_destroy, bool): + if set_del_all_vols_on_destroy and not isinstance( + set_del_all_vols_on_destroy, bool + ): raise SaltCloudConfigError( - '\'del_all_vols_on_destroy\' should be a boolean value.' + "'del_all_vols_on_destroy' should be a boolean value." ) - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), args={ - 'kwargs': __utils__['cloud.filter_event']( - 'requesting', params, list(params) + "kwargs": __utils__["cloud.filter_event"]( + "requesting", params, list(params) ), - 'location': location, + "location": location, }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) provider = get_provider(vm_) try: - data = aws.query(params, - 'instancesSet', - location=location, - provider=provider, - opts=__opts__, - sigver='4') - if 'error' in data: - return data['error'] + data = aws.query( + params, + "instancesSet", + location=location, + provider=provider, + opts=__opts__, + sigver="4", + ) + if "error" in data: + return data["error"] except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on EC2 when trying to run the initial ' - 'deployment: \n%s', vm_['name'], exc, + "Error creating %s on EC2 when trying to run the initial " + "deployment: \n%s", + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) raise # if we're using spot instances, we need to wait for the spot request # to become active before we continue if spot_config: - sir_id = data[0]['spotInstanceRequestId'] + sir_id = data[0]["spotInstanceRequestId"] - vm_['spotRequestId'] = sir_id + vm_["spotRequestId"] = sir_id def __query_spot_instance_request(sir_id, location): - params = {'Action': 'DescribeSpotInstanceRequests', - 'SpotInstanceRequestId.1': sir_id} - data = aws.query(params, - location=location, - provider=provider, - opts=__opts__, - sigver='4') + params = { + "Action": "DescribeSpotInstanceRequests", + "SpotInstanceRequestId.1": sir_id, + } + data = aws.query( + params, location=location, provider=provider, opts=__opts__, sigver="4" + ) if not data: - log.error( - 'There was an error while querying EC2. Empty response' - ) + log.error("There was an error while querying EC2. Empty response") # Trigger a failure in the wait for spot instance method return False - if isinstance(data, dict) and 'error' in data: - log.warning('There was an error in the query. %s', data['error']) + if isinstance(data, dict) and "error" in data: + log.warning("There was an error in the query. %s", data["error"]) # Trigger a failure in the wait for spot instance method return False - log.debug('Returned query data: %s', data) + log.debug("Returned query data: %s", data) - state = data[0].get('state') + state = data[0].get("state") - if state == 'active': + if state == "active": return data - if state == 'open': + if state == "open": # Still waiting for an active state - log.info('Spot instance status: %s', data[0]['status']['message']) + log.info("Spot instance status: %s", data[0]["status"]["message"]) return None - if state in ['cancelled', 'failed', 'closed']: + if state in ["cancelled", "failed", "closed"]: # Request will never be active, fail - log.error('Spot instance request resulted in state \'{0}\'. ' - 'Nothing else we can do here.') + log.error( + "Spot instance request resulted in state '{0}'. " + "Nothing else we can do here." + ) return False - __utils__['cloud.fire_event']( - 'event', - 'waiting for spot instance', - 'salt/cloud/{0}/waiting_for_spot'.format(vm_['name']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "waiting for spot instance", + "salt/cloud/{0}/waiting_for_spot".format(vm_["name"]), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: @@ -2134,35 +2201,40 @@ def request_instance(vm_=None, call=None): __query_spot_instance_request, update_args=(sir_id, location), timeout=config.get_cloud_config_value( - 'wait_for_spot_timeout', vm_, __opts__, default=10 * 60), + "wait_for_spot_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_spot_interval', vm_, __opts__, default=30), + "wait_for_spot_interval", vm_, __opts__, default=30 + ), interval_multiplier=config.get_cloud_config_value( - 'wait_for_spot_interval_multiplier', - vm_, - __opts__, - default=1), + "wait_for_spot_interval_multiplier", vm_, __opts__, default=1 + ), max_failures=config.get_cloud_config_value( - 'wait_for_spot_max_failures', - vm_, - __opts__, - default=10), + "wait_for_spot_max_failures", vm_, __opts__, default=10 + ), ) - log.debug('wait_for_spot_instance data %s', data) + log.debug("wait_for_spot_instance data %s", data) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # Cancel the existing spot instance request - params = {'Action': 'CancelSpotInstanceRequests', - 'SpotInstanceRequestId.1': sir_id} - data = aws.query(params, - location=location, - provider=provider, - opts=__opts__, - sigver='4') + params = { + "Action": "CancelSpotInstanceRequests", + "SpotInstanceRequestId.1": sir_id, + } + data = aws.query( + params, + location=location, + provider=provider, + opts=__opts__, + sigver="4", + ) - log.debug('Canceled spot instance request %s. Data ' - 'returned: %s', sir_id, data) + log.debug( + "Canceled spot instance request %s. Data " "returned: %s", + sir_id, + data, + ) except SaltCloudSystemExit: pass @@ -2173,53 +2245,54 @@ def request_instance(vm_=None, call=None): def query_instance(vm_=None, call=None): - ''' + """ Query an instance upon creation from the EC2 API - ''' - if call == 'function': + """ + if call == "function": # Technically this function may be called other ways too, but it # definitely cannot be called with --function. raise SaltCloudSystemExit( - 'The query_instance action must be called with -a or --action.' + "The query_instance action must be called with -a or --action." ) - instance_id = vm_['instance_id'] - location = vm_.get('location', get_location(vm_)) - __utils__['cloud.fire_event']( - 'event', - 'querying instance', - 'salt/cloud/{0}/querying'.format(vm_['name']), - args={'instance_id': instance_id}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + instance_id = vm_["instance_id"] + location = vm_.get("location", get_location(vm_)) + __utils__["cloud.fire_event"]( + "event", + "querying instance", + "salt/cloud/{0}/querying".format(vm_["name"]), + args={"instance_id": instance_id}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.debug('The new VM instance_id is %s', instance_id) + log.debug("The new VM instance_id is %s", instance_id) - params = {'Action': 'DescribeInstances', - 'InstanceId.1': instance_id} + params = {"Action": "DescribeInstances", "InstanceId.1": instance_id} provider = get_provider(vm_) attempts = 0 while attempts < aws.AWS_MAX_RETRIES: - data, requesturl = aws.query(params, # pylint: disable=unbalanced-tuple-unpacking - location=location, - provider=provider, - opts=__opts__, - return_url=True, - sigver='4') - log.debug('The query returned: %s', data) + data, requesturl = aws.query( + params, # pylint: disable=unbalanced-tuple-unpacking + location=location, + provider=provider, + opts=__opts__, + return_url=True, + sigver="4", + ) + log.debug("The query returned: %s", data) - if isinstance(data, dict) and 'error' in data: + if isinstance(data, dict) and "error" in data: log.warning( - 'There was an error in the query. %s attempts ' - 'remaining: %s', attempts, data['error'] + "There was an error in the query. %s attempts " "remaining: %s", + attempts, + data["error"], ) elif isinstance(data, list) and not data: log.warning( - 'Query returned an empty list. %s attempts ' - 'remaining.', attempts + "Query returned an empty list. %s attempts " "remaining.", attempts ) else: break @@ -2229,93 +2302,84 @@ def query_instance(vm_=None, call=None): continue else: raise SaltCloudSystemExit( - 'An error occurred while creating VM: {0}'.format(data['error']) + "An error occurred while creating VM: {0}".format(data["error"]) ) def __query_ip_address(params, url): # pylint: disable=W0613 - data = aws.query(params, - location=location, - provider=provider, - opts=__opts__, - sigver='4') + data = aws.query( + params, location=location, provider=provider, opts=__opts__, sigver="4" + ) if not data: - log.error( - 'There was an error while querying EC2. Empty response' - ) + log.error("There was an error while querying EC2. Empty response") # Trigger a failure in the wait for IP function return False - if isinstance(data, dict) and 'error' in data: - log.warning('There was an error in the query. %s', data['error']) + if isinstance(data, dict) and "error" in data: + log.warning("There was an error in the query. %s", data["error"]) # Trigger a failure in the wait for IP function return False - log.debug('Returned query data: %s', data) + log.debug("Returned query data: %s", data) - if ssh_interface(vm_) == 'public_ips': - if 'ipAddress' in data[0]['instancesSet']['item']: + if ssh_interface(vm_) == "public_ips": + if "ipAddress" in data[0]["instancesSet"]["item"]: return data else: - log.error( - 'Public IP not detected.' - ) + log.error("Public IP not detected.") - if ssh_interface(vm_) == 'private_ips': - if 'privateIpAddress' in data[0]['instancesSet']['item']: + if ssh_interface(vm_) == "private_ips": + if "privateIpAddress" in data[0]["instancesSet"]["item"]: return data else: - log.error( - 'Private IP not detected.' - ) + log.error("Private IP not detected.") try: data = salt.utils.cloud.wait_for_ip( __query_ip_address, update_args=(params, requesturl), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10), + "wait_for_ip_interval", vm_, __opts__, default=10 + ), interval_multiplier=config.get_cloud_config_value( - 'wait_for_ip_interval_multiplier', vm_, __opts__, default=1), + "wait_for_ip_interval_multiplier", vm_, __opts__, default=1 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) - if 'reactor' in vm_ and vm_['reactor'] is True: - __utils__['cloud.fire_event']( - 'event', - 'instance queried', - 'salt/cloud/{0}/query_reactor'.format(vm_['name']), - args={'data': data}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + if "reactor" in vm_ and vm_["reactor"] is True: + __utils__["cloud.fire_event"]( + "event", + "instance queried", + "salt/cloud/{0}/query_reactor".format(vm_["name"]), + args={"data": data}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return data def wait_for_instance( - vm_=None, - data=None, - ip_address=None, - display_ssh_output=True, - call=None, - ): - ''' + vm_=None, data=None, ip_address=None, display_ssh_output=True, call=None, +): + """ Wait for an instance upon creation from the EC2 API, to become available - ''' - if call == 'function': + """ + if call == "function": # Technically this function may be called other ways too, but it # definitely cannot be called with --function. raise SaltCloudSystemExit( - 'The wait_for_instance action must be called with -a or --action.' + "The wait_for_instance action must be called with -a or --action." ) if vm_ is None: @@ -2324,57 +2388,51 @@ def wait_for_instance( if data is None: data = {} - ssh_gateway_config = vm_.get( - 'gateway', get_ssh_gateway_config(vm_) - ) + ssh_gateway_config = vm_.get("gateway", get_ssh_gateway_config(vm_)) - __utils__['cloud.fire_event']( - 'event', - 'waiting for ssh', - 'salt/cloud/{0}/waiting_for_ssh'.format(vm_['name']), - args={'ip_address': ip_address}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "waiting for ssh", + "salt/cloud/{0}/waiting_for_ssh".format(vm_["name"]), + args={"ip_address": ip_address}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) ssh_connect_timeout = config.get_cloud_config_value( - 'ssh_connect_timeout', vm_, __opts__, 900 # 15 minutes - ) - ssh_port = config.get_cloud_config_value( - 'ssh_port', vm_, __opts__, 22 + "ssh_connect_timeout", vm_, __opts__, 900 # 15 minutes ) + ssh_port = config.get_cloud_config_value("ssh_port", vm_, __opts__, 22) - if config.get_cloud_config_value('win_installer', vm_, __opts__): + if config.get_cloud_config_value("win_installer", vm_, __opts__): username = config.get_cloud_config_value( - 'win_username', vm_, __opts__, default='Administrator' + "win_username", vm_, __opts__, default="Administrator" ) win_passwd = config.get_cloud_config_value( - 'win_password', vm_, __opts__, default='' + "win_password", vm_, __opts__, default="" ) win_deploy_auth_retries = config.get_cloud_config_value( - 'win_deploy_auth_retries', vm_, __opts__, default=10 + "win_deploy_auth_retries", vm_, __opts__, default=10 ) win_deploy_auth_retry_delay = config.get_cloud_config_value( - 'win_deploy_auth_retry_delay', vm_, __opts__, default=1 + "win_deploy_auth_retry_delay", vm_, __opts__, default=1 ) use_winrm = config.get_cloud_config_value( - 'use_winrm', vm_, __opts__, default=False + "use_winrm", vm_, __opts__, default=False ) winrm_verify_ssl = config.get_cloud_config_value( - 'winrm_verify_ssl', vm_, __opts__, default=True + "winrm_verify_ssl", vm_, __opts__, default=True ) - if win_passwd and win_passwd == 'auto': - log.debug('Waiting for auto-generated Windows EC2 password') + if win_passwd and win_passwd == "auto": + log.debug("Waiting for auto-generated Windows EC2 password") while True: password_data = get_password_data( - name=vm_['name'], - kwargs={ - 'key_file': vm_['private_key'], - }, - call='action', + name=vm_["name"], + kwargs={"key_file": vm_["private_key"]}, + call="action", ) - win_passwd = password_data.get('password', None) + win_passwd = password_data.get("password", None) if win_passwd is None: log.debug(password_data) # This wait is so high, because the password is unlikely to @@ -2383,33 +2441,33 @@ def wait_for_instance( else: logging_data = password_data - logging_data['password'] = 'XXX-REDACTED-XXX' - logging_data['passwordData'] = 'XXX-REDACTED-XXX' + logging_data["password"] = "XXX-REDACTED-XXX" + logging_data["passwordData"] = "XXX-REDACTED-XXX" log.debug(logging_data) - vm_['win_password'] = win_passwd + vm_["win_password"] = win_passwd break # SMB used whether psexec or winrm - if not salt.utils.cloud.wait_for_port(ip_address, - port=445, - timeout=ssh_connect_timeout): - raise SaltCloudSystemExit( - 'Failed to connect to remote windows host' - ) + if not salt.utils.cloud.wait_for_port( + ip_address, port=445, timeout=ssh_connect_timeout + ): + raise SaltCloudSystemExit("Failed to connect to remote windows host") # If not using winrm keep same psexec behavior if not use_winrm: - log.debug('Trying to authenticate via SMB using psexec') + log.debug("Trying to authenticate via SMB using psexec") - if not salt.utils.cloud.validate_windows_cred(ip_address, - username, - win_passwd, - retries=win_deploy_auth_retries, - retry_delay=win_deploy_auth_retry_delay): + if not salt.utils.cloud.validate_windows_cred( + ip_address, + username, + win_passwd, + retries=win_deploy_auth_retries, + retry_delay=win_deploy_auth_retry_delay, + ): raise SaltCloudSystemExit( - 'Failed to authenticate against remote windows host (smb)' + "Failed to authenticate against remote windows host (smb)" ) # If using winrm @@ -2417,108 +2475,106 @@ def wait_for_instance( # Default HTTPS port can be changed in cloud configuration winrm_port = config.get_cloud_config_value( - 'winrm_port', vm_, __opts__, default=5986 + "winrm_port", vm_, __opts__, default=5986 ) # Wait for winrm port to be available - if not salt.utils.cloud.wait_for_port(ip_address, - port=winrm_port, - timeout=ssh_connect_timeout): + if not salt.utils.cloud.wait_for_port( + ip_address, port=winrm_port, timeout=ssh_connect_timeout + ): raise SaltCloudSystemExit( - 'Failed to connect to remote windows host (winrm)' + "Failed to connect to remote windows host (winrm)" ) - log.debug('Trying to authenticate via Winrm using pywinrm') + log.debug("Trying to authenticate via Winrm using pywinrm") - if not salt.utils.cloud.wait_for_winrm(ip_address, - winrm_port, - username, - win_passwd, - timeout=ssh_connect_timeout, - verify=winrm_verify_ssl): + if not salt.utils.cloud.wait_for_winrm( + ip_address, + winrm_port, + username, + win_passwd, + timeout=ssh_connect_timeout, + verify=winrm_verify_ssl, + ): raise SaltCloudSystemExit( - 'Failed to authenticate against remote windows host' + "Failed to authenticate against remote windows host" ) - elif salt.utils.cloud.wait_for_port(ip_address, - port=ssh_port, - timeout=ssh_connect_timeout, - gateway=ssh_gateway_config - ): + elif salt.utils.cloud.wait_for_port( + ip_address, + port=ssh_port, + timeout=ssh_connect_timeout, + gateway=ssh_gateway_config, + ): # If a known_hosts_file is configured, this instance will not be # accessible until it has a host key. Since this is provided on # supported instances by cloud-init, and viewable to us only from the # console output (which may take several minutes to become available, # we have some more waiting to do here. known_hosts_file = config.get_cloud_config_value( - 'known_hosts_file', vm_, __opts__, default=None + "known_hosts_file", vm_, __opts__, default=None ) if known_hosts_file: console = {} - while 'output_decoded' not in console: + while "output_decoded" not in console: console = get_console_output( - instance_id=vm_['instance_id'], - call='action', - location=get_location(vm_) + instance_id=vm_["instance_id"], + call="action", + location=get_location(vm_), ) pprint.pprint(console) time.sleep(5) - output = salt.utils.stringutils.to_unicode(console['output_decoded']) - comps = output.split('-----BEGIN SSH HOST KEY KEYS-----') + output = salt.utils.stringutils.to_unicode(console["output_decoded"]) + comps = output.split("-----BEGIN SSH HOST KEY KEYS-----") if len(comps) < 2: # Fail; there are no host keys return False - comps = comps[1].split('-----END SSH HOST KEY KEYS-----') - keys = '' + comps = comps[1].split("-----END SSH HOST KEY KEYS-----") + keys = "" for line in comps[0].splitlines(): if not line: continue - keys += '\n{0} {1}'.format(ip_address, line) + keys += "\n{0} {1}".format(ip_address, line) - with salt.utils.files.fopen(known_hosts_file, 'a') as fp_: + with salt.utils.files.fopen(known_hosts_file, "a") as fp_: fp_.write(salt.utils.stringutils.to_str(keys)) fp_.close() - for user in vm_['usernames']: + for user in vm_["usernames"]: if salt.utils.cloud.wait_for_passwd( host=ip_address, port=ssh_port, username=user, ssh_timeout=config.get_cloud_config_value( - 'wait_for_passwd_timeout', vm_, __opts__, default=1 * 60 + "wait_for_passwd_timeout", vm_, __opts__, default=1 * 60 ), - key_filename=vm_['key_filename'], + key_filename=vm_["key_filename"], display_ssh_output=display_ssh_output, gateway=ssh_gateway_config, maxtries=config.get_cloud_config_value( - 'wait_for_passwd_maxtries', vm_, __opts__, default=15 + "wait_for_passwd_maxtries", vm_, __opts__, default=15 ), known_hosts_file=config.get_cloud_config_value( - 'known_hosts_file', vm_, __opts__, - default='/dev/null' + "known_hosts_file", vm_, __opts__, default="/dev/null" ), ): - __opts__['ssh_username'] = user - vm_['ssh_username'] = user + __opts__["ssh_username"] = user + vm_["ssh_username"] = user break else: - raise SaltCloudSystemExit( - 'Failed to authenticate against remote ssh' - ) + raise SaltCloudSystemExit("Failed to authenticate against remote ssh") else: - raise SaltCloudSystemExit( - 'Failed to connect to remote ssh' - ) + raise SaltCloudSystemExit("Failed to connect to remote ssh") - if 'reactor' in vm_ and vm_['reactor'] is True: - __utils__['cloud.fire_event']( - 'event', - 'ssh is available', - 'salt/cloud/{0}/ssh_ready_reactor'.format(vm_['name']), - args={'ip_address': ip_address}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + if "reactor" in vm_ and vm_["reactor"] is True: + __utils__["cloud.fire_event"]( + "event", + "ssh is available", + "salt/cloud/{0}/ssh_ready_reactor".format(vm_["name"]), + args={"ip_address": ip_address}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return vm_ @@ -2527,21 +2583,19 @@ def wait_for_instance( def _validate_key_path_and_mode(key_filename): if key_filename is None: raise SaltCloudSystemExit( - 'The required \'private_key\' configuration setting is missing from the ' - '\'ec2\' driver.' + "The required 'private_key' configuration setting is missing from the " + "'ec2' driver." ) if not os.path.exists(key_filename): raise SaltCloudSystemExit( - 'The EC2 key file \'{0}\' does not exist.\n'.format( - key_filename - ) + "The EC2 key file '{0}' does not exist.\n".format(key_filename) ) key_mode = stat.S_IMODE(os.stat(key_filename).st_mode) if key_mode not in (0o400, 0o600): raise SaltCloudSystemExit( - 'The EC2 key file \'{0}\' needs to be set to mode 0400 or 0600.\n'.format( + "The EC2 key file '{0}' needs to be set to mode 0400 or 0600.\n".format( key_filename ) ) @@ -2550,116 +2604,113 @@ def _validate_key_path_and_mode(key_filename): def create(vm_=None, call=None): - ''' + """ Create a single VM from a data dict - ''' + """ if call: - raise SaltCloudSystemExit( - 'You cannot create an instance with -a or -f.' - ) + raise SaltCloudSystemExit("You cannot create an instance with -a or -f.") try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'ec2', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "ec2", vm_["profile"], vm_=vm_ + ) + is False + ): return False except AttributeError: pass # Check for private_key and keyfile name for bootstrapping new instances - deploy = config.get_cloud_config_value( - 'deploy', vm_, __opts__, default=True - ) + deploy = config.get_cloud_config_value("deploy", vm_, __opts__, default=True) win_password = config.get_cloud_config_value( - 'win_password', vm_, __opts__, default='' + "win_password", vm_, __opts__, default="" ) key_filename = config.get_cloud_config_value( - 'private_key', vm_, __opts__, search_global=False, default=None + "private_key", vm_, __opts__, search_global=False, default=None ) if deploy: # The private_key and keyname settings are only needed for bootstrapping # new instances when deploy is True _validate_key_path_and_mode(key_filename) - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - __utils__['cloud.cachedir_index_add']( - vm_['name'], vm_['profile'], 'ec2', vm_['driver'] + __utils__["cloud.cachedir_index_add"]( + vm_["name"], vm_["profile"], "ec2", vm_["driver"] ) - vm_['key_filename'] = key_filename + vm_["key_filename"] = key_filename # wait_for_instance requires private_key - vm_['private_key'] = key_filename + vm_["private_key"] = key_filename # Get SSH Gateway config early to verify the private_key, # if used, exists or not. We don't want to deploy an instance # and not be able to access it via the gateway. - vm_['gateway'] = get_ssh_gateway_config(vm_) + vm_["gateway"] = get_ssh_gateway_config(vm_) location = get_location(vm_) - vm_['location'] = location + vm_["location"] = location - log.info('Creating Cloud VM %s in %s', vm_['name'], location) - vm_['usernames'] = salt.utils.cloud.ssh_usernames( + log.info("Creating Cloud VM %s in %s", vm_["name"], location) + vm_["usernames"] = salt.utils.cloud.ssh_usernames( vm_, __opts__, default_users=( - 'ec2-user', # Amazon Linux, Fedora, RHEL; FreeBSD - 'centos', # CentOS AMIs from AWS Marketplace - 'ubuntu', # Ubuntu - 'admin', # Debian GNU/Linux - 'bitnami', # BitNami AMIs - 'root' # Last resort, default user on RHEL 5, SUSE - ) + "ec2-user", # Amazon Linux, Fedora, RHEL; FreeBSD + "centos", # CentOS AMIs from AWS Marketplace + "ubuntu", # Ubuntu + "admin", # Debian GNU/Linux + "bitnami", # BitNami AMIs + "root", # Last resort, default user on RHEL 5, SUSE + ), ) - if 'instance_id' in vm_: + if "instance_id" in vm_: # This was probably created via another process, and doesn't have # things like salt keys created yet, so let's create them now. - if 'pub_key' not in vm_ and 'priv_key' not in vm_: - log.debug('Generating minion keys for \'%s\'', vm_['name']) - vm_['priv_key'], vm_['pub_key'] = salt.utils.cloud.gen_keys( - salt.config.get_cloud_config_value( - 'keysize', - vm_, - __opts__ - ) + if "pub_key" not in vm_ and "priv_key" not in vm_: + log.debug("Generating minion keys for '%s'", vm_["name"]) + vm_["priv_key"], vm_["pub_key"] = salt.utils.cloud.gen_keys( + salt.config.get_cloud_config_value("keysize", vm_, __opts__) ) else: # Put together all of the information required to request the instance, # and then fire off the request for it if keyname(vm_) is None: raise SaltCloudSystemExit( - 'The required \'keyname\' configuration setting is missing from the ' - '\'ec2\' driver.' + "The required 'keyname' configuration setting is missing from the " + "'ec2' driver." ) data, vm_ = request_instance(vm_, location) # If data is a str, it's an error if isinstance(data, six.string_types): - log.error('Error requesting instance: %s', data) + log.error("Error requesting instance: %s", data) return {} # Pull the instance ID, valid for both spot and normal instances # Multiple instances may have been spun up, get all their IDs - vm_['instance_id_list'] = [] + vm_["instance_id_list"] = [] for instance in data: - vm_['instance_id_list'].append(instance['instanceId']) + vm_["instance_id_list"].append(instance["instanceId"]) - vm_['instance_id'] = vm_['instance_id_list'].pop() - if vm_['instance_id_list']: + vm_["instance_id"] = vm_["instance_id_list"].pop() + if vm_["instance_id_list"]: # Multiple instances were spun up, get one now, and queue the rest - queue_instances(vm_['instance_id_list']) + queue_instances(vm_["instance_id_list"]) # Wait for vital information, such as IP addresses, to be available # for the new instance @@ -2667,94 +2718,83 @@ def create(vm_=None, call=None): # Now that the instance is available, tag it appropriately. Should # mitigate race conditions with tags - tags = config.get_cloud_config_value('tag', - vm_, - __opts__, - {}, - search_global=False) + tags = config.get_cloud_config_value("tag", vm_, __opts__, {}, search_global=False) if not isinstance(tags, dict): - raise SaltCloudConfigError( - '\'tag\' should be a dict.' - ) + raise SaltCloudConfigError("'tag' should be a dict.") for value in six.itervalues(tags): if not isinstance(value, six.string_types): raise SaltCloudConfigError( - '\'tag\' values must be strings. Try quoting the values. ' + "'tag' values must be strings. Try quoting the values. " 'e.g. "2013-09-19T20:09:46Z".' ) - tags['Name'] = vm_['name'] + tags["Name"] = vm_["name"] - __utils__['cloud.fire_event']( - 'event', - 'setting tags', - 'salt/cloud/{0}/tagging'.format(vm_['name']), - args={'tags': tags}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "setting tags", + "salt/cloud/{0}/tagging".format(vm_["name"]), + args={"tags": tags}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) salt.utils.cloud.wait_for_fun( set_tags, timeout=30, - name=vm_['name'], + name=vm_["name"], tags=tags, - instance_id=vm_['instance_id'], - call='action', - location=location + instance_id=vm_["instance_id"], + call="action", + location=location, ) # Once instance tags are set, tag the spot request if configured - if 'spot_config' in vm_ and 'tag' in vm_['spot_config']: + if "spot_config" in vm_ and "tag" in vm_["spot_config"]: - if not isinstance(vm_['spot_config']['tag'], dict): - raise SaltCloudConfigError( - '\'tag\' should be a dict.' - ) + if not isinstance(vm_["spot_config"]["tag"], dict): + raise SaltCloudConfigError("'tag' should be a dict.") - for value in six.itervalues(vm_['spot_config']['tag']): + for value in six.itervalues(vm_["spot_config"]["tag"]): if not isinstance(value, str): raise SaltCloudConfigError( - '\'tag\' values must be strings. Try quoting the values. ' + "'tag' values must be strings. Try quoting the values. " 'e.g. "2013-09-19T20:09:46Z".' ) spot_request_tags = {} - if 'spotRequestId' not in vm_: - raise SaltCloudConfigError('Failed to find spotRequestId') + if "spotRequestId" not in vm_: + raise SaltCloudConfigError("Failed to find spotRequestId") - sir_id = vm_['spotRequestId'] + sir_id = vm_["spotRequestId"] - spot_request_tags['Name'] = vm_['name'] + spot_request_tags["Name"] = vm_["name"] - for k, v in six.iteritems(vm_['spot_config']['tag']): + for k, v in six.iteritems(vm_["spot_config"]["tag"]): spot_request_tags[k] = v - __utils__['cloud.fire_event']( - 'event', - 'setting tags', - 'salt/cloud/spot_request_{0}/tagging'.format(sir_id), - args={'tags': spot_request_tags}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "setting tags", + "salt/cloud/spot_request_{0}/tagging".format(sir_id), + args={"tags": spot_request_tags}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) salt.utils.cloud.wait_for_fun( set_tags, timeout=30, - name=vm_['name'], + name=vm_["name"], tags=spot_request_tags, instance_id=sir_id, - call='action', - location=location + call="action", + location=location, ) network_interfaces = config.get_cloud_config_value( - 'network_interfaces', - vm_, - __opts__, - search_global=False + "network_interfaces", vm_, __opts__, search_global=False ) if network_interfaces: @@ -2762,35 +2802,33 @@ def create(vm_=None, call=None): # At this point, the node is created and tagged, and now needs to be # bootstrapped, once the necessary port is available. - log.info('Created node %s', vm_['name']) + log.info("Created node %s", vm_["name"]) - instance = data[0]['instancesSet']['item'] + instance = data[0]["instancesSet"]["item"] # Wait for the necessary port to become available to bootstrap - if ssh_interface(vm_) == 'private_ips': - ip_address = instance['privateIpAddress'] - log.info('Salt node data. Private_ip: %s', ip_address) + if ssh_interface(vm_) == "private_ips": + ip_address = instance["privateIpAddress"] + log.info("Salt node data. Private_ip: %s", ip_address) else: - ip_address = instance['ipAddress'] - log.info('Salt node data. Public_ip: %s', ip_address) - vm_['ssh_host'] = ip_address + ip_address = instance["ipAddress"] + log.info("Salt node data. Public_ip: %s", ip_address) + vm_["ssh_host"] = ip_address - if salt.utils.cloud.get_salt_interface(vm_, __opts__) == 'private_ips': - salt_ip_address = instance['privateIpAddress'] - log.info('Salt interface set to: %s', salt_ip_address) + if salt.utils.cloud.get_salt_interface(vm_, __opts__) == "private_ips": + salt_ip_address = instance["privateIpAddress"] + log.info("Salt interface set to: %s", salt_ip_address) else: - salt_ip_address = instance['ipAddress'] - log.debug('Salt interface set to: %s', salt_ip_address) - vm_['salt_host'] = salt_ip_address + salt_ip_address = instance["ipAddress"] + log.debug("Salt interface set to: %s", salt_ip_address) + vm_["salt_host"] = salt_ip_address if deploy: display_ssh_output = config.get_cloud_config_value( - 'display_ssh_output', vm_, __opts__, default=True + "display_ssh_output", vm_, __opts__, default=True ) - vm_ = wait_for_instance( - vm_, data, ip_address, display_ssh_output - ) + vm_ = wait_for_instance(vm_, data, ip_address, display_ssh_output) # The instance is booted and accessible, let's Salt it! ret = instance.copy() @@ -2800,82 +2838,80 @@ def create(vm_=None, call=None): # 2. Profile config # 3. Global configuration volumes = config.get_cloud_config_value( - 'volumes', vm_, __opts__, search_global=True + "volumes", vm_, __opts__, search_global=True ) if volumes: - __utils__['cloud.fire_event']( - 'event', - 'attaching volumes', - 'salt/cloud/{0}/attaching_volumes'.format(vm_['name']), - args={'volumes': volumes}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "attaching volumes", + "salt/cloud/{0}/attaching_volumes".format(vm_["name"]), + args={"volumes": volumes}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Create and attach volumes to node %s', vm_['name']) + log.info("Create and attach volumes to node %s", vm_["name"]) created = create_attach_volumes( - vm_['name'], + vm_["name"], { - 'volumes': volumes, - 'zone': ret['placement']['availabilityZone'], - 'instance_id': ret['instanceId'], - 'del_all_vols_on_destroy': vm_.get('del_all_vols_on_destroy', False) + "volumes": volumes, + "zone": ret["placement"]["availabilityZone"], + "instance_id": ret["instanceId"], + "del_all_vols_on_destroy": vm_.get("del_all_vols_on_destroy", False), }, - call='action' + call="action", ) - ret['Attached Volumes'] = created + ret["Attached Volumes"] = created # Associate instance with a ssm document, if present ssm_document = config.get_cloud_config_value( - 'ssm_document', vm_, __opts__, None, search_global=False + "ssm_document", vm_, __opts__, None, search_global=False ) if ssm_document: - log.debug('Associating with ssm document: %s', ssm_document) + log.debug("Associating with ssm document: %s", ssm_document) assoc = ssm_create_association( - vm_['name'], - {'ssm_document': ssm_document}, - instance_id=vm_['instance_id'], - call='action' + vm_["name"], + {"ssm_document": ssm_document}, + instance_id=vm_["instance_id"], + call="action", ) - if isinstance(assoc, dict) and assoc.get('error', None): + if isinstance(assoc, dict) and assoc.get("error", None): log.error( - 'Failed to associate instance %s with ssm document %s', - vm_['instance_id'], ssm_document + "Failed to associate instance %s with ssm document %s", + vm_["instance_id"], + ssm_document, ) return {} - for key, value in six.iteritems(__utils__['cloud.bootstrap'](vm_, __opts__)): + for key, value in six.iteritems(__utils__["cloud.bootstrap"](vm_, __opts__)): ret.setdefault(key, value) - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(instance) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(instance)) event_data = { - 'name': vm_['name'], - 'profile': vm_['profile'], - 'provider': vm_['driver'], - 'instance_id': vm_['instance_id'], + "name": vm_["name"], + "profile": vm_["profile"], + "provider": vm_["driver"], + "instance_id": vm_["instance_id"], } if volumes: - event_data['volumes'] = volumes + event_data["volumes"] = volumes if ssm_document: - event_data['ssm_document'] = ssm_document + event_data["ssm_document"] = ssm_document - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', event_data, list(event_data)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]("created", event_data, list(event_data)), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) # Ensure that the latest node data is returned - node = _get_node(instance_id=vm_['instance_id']) - __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__) + node = _get_node(instance_id=vm_["instance_id"]) + __utils__["cloud.cache_node"](node, __active_provider_name__, __opts__) ret.update(node) # Add any block device tags specified @@ -2883,13 +2919,13 @@ def create(vm_=None, call=None): blockdevicemappings_holder = block_device_mappings(vm_) if blockdevicemappings_holder: for _bd in blockdevicemappings_holder: - if 'tag' in _bd: - ex_blockdevicetags[_bd['DeviceName']] = _bd['tag'] + if "tag" in _bd: + ex_blockdevicetags[_bd["DeviceName"]] = _bd["tag"] block_device_volume_id_map = {} if ex_blockdevicetags: - for _device, _map in six.iteritems(ret['blockDeviceMapping']): + for _device, _map in six.iteritems(ret["blockDeviceMapping"]): bd_items = [] if isinstance(_map, dict): bd_items.append(_map) @@ -2898,38 +2934,43 @@ def create(vm_=None, call=None): bd_items.append(mapitem) for blockitem in bd_items: - if blockitem['deviceName'] in ex_blockdevicetags and 'Name' not in ex_blockdevicetags[blockitem['deviceName']]: - ex_blockdevicetags[blockitem['deviceName']]['Name'] = vm_['name'] - if blockitem['deviceName'] in ex_blockdevicetags: - block_device_volume_id_map[blockitem[ret['rootDeviceType']]['volumeId']] = ex_blockdevicetags[blockitem['deviceName']] + if ( + blockitem["deviceName"] in ex_blockdevicetags + and "Name" not in ex_blockdevicetags[blockitem["deviceName"]] + ): + ex_blockdevicetags[blockitem["deviceName"]]["Name"] = vm_["name"] + if blockitem["deviceName"] in ex_blockdevicetags: + block_device_volume_id_map[ + blockitem[ret["rootDeviceType"]]["volumeId"] + ] = ex_blockdevicetags[blockitem["deviceName"]] if block_device_volume_id_map: for volid, tags in six.iteritems(block_device_volume_id_map): - __utils__['cloud.fire_event']( - 'event', - 'setting tags', - 'salt/cloud/block_volume_{0}/tagging'.format(str(volid)), - args={'tags': tags}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "setting tags", + "salt/cloud/block_volume_{0}/tagging".format(str(volid)), + args={"tags": tags}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - __utils__['cloud.wait_for_fun']( + __utils__["cloud.wait_for_fun"]( set_tags, timeout=30, - name=vm_['name'], + name=vm_["name"], tags=tags, resource_id=volid, - call='action', - location=location + call="action", + location=location, ) return ret def queue_instances(instances): - ''' + """ Queue a set of instances to be provisioned later. Expects a list. Currently this only queries node data, and then places it in the cloud @@ -2939,92 +2980,86 @@ def queue_instances(instances): For more information about the salt-cloud-reactor, see: https://github.com/saltstack-formulas/salt-cloud-reactor - ''' + """ for instance_id in instances: node = _get_node(instance_id=instance_id) - __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](node, __active_provider_name__, __opts__) def create_attach_volumes(name, kwargs, call=None, wait_to_finish=True): - ''' + """ Create and attach volumes to created node - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The create_attach_volumes action must be called with ' - '-a or --action.' + "The create_attach_volumes action must be called with " "-a or --action." ) - if 'instance_id' not in kwargs: - kwargs['instance_id'] = _get_node(name)['instanceId'] + if "instance_id" not in kwargs: + kwargs["instance_id"] = _get_node(name)["instanceId"] - if isinstance(kwargs['volumes'], six.string_types): - volumes = salt.utils.yaml.safe_load(kwargs['volumes']) + if isinstance(kwargs["volumes"], six.string_types): + volumes = salt.utils.yaml.safe_load(kwargs["volumes"]) else: - volumes = kwargs['volumes'] + volumes = kwargs["volumes"] ret = [] for volume in volumes: created = False - volume_name = '{0} on {1}'.format(volume['device'], name) + volume_name = "{0} on {1}".format(volume["device"], name) - volume_dict = { - 'volume_name': volume_name, - 'zone': kwargs['zone'] - } - if 'volume_id' in volume: - volume_dict['volume_id'] = volume['volume_id'] - elif 'snapshot' in volume: - volume_dict['snapshot'] = volume['snapshot'] - elif 'size' in volume: - volume_dict['size'] = volume['size'] + volume_dict = {"volume_name": volume_name, "zone": kwargs["zone"]} + if "volume_id" in volume: + volume_dict["volume_id"] = volume["volume_id"] + elif "snapshot" in volume: + volume_dict["snapshot"] = volume["snapshot"] + elif "size" in volume: + volume_dict["size"] = volume["size"] else: raise SaltCloudConfigError( - 'Cannot create volume. Please define one of \'volume_id\', ' - '\'snapshot\', or \'size\'' + "Cannot create volume. Please define one of 'volume_id', " + "'snapshot', or 'size'" ) - if 'tags' in volume: - volume_dict['tags'] = volume['tags'] - if 'type' in volume: - volume_dict['type'] = volume['type'] - if 'iops' in volume: - volume_dict['iops'] = volume['iops'] - if 'encrypted' in volume: - volume_dict['encrypted'] = volume['encrypted'] - if 'kmskeyid' in volume: - volume_dict['kmskeyid'] = volume['kmskeyid'] + if "tags" in volume: + volume_dict["tags"] = volume["tags"] + if "type" in volume: + volume_dict["type"] = volume["type"] + if "iops" in volume: + volume_dict["iops"] = volume["iops"] + if "encrypted" in volume: + volume_dict["encrypted"] = volume["encrypted"] + if "kmskeyid" in volume: + volume_dict["kmskeyid"] = volume["kmskeyid"] - if 'volume_id' not in volume_dict: - created_volume = create_volume(volume_dict, call='function', wait_to_finish=wait_to_finish) + if "volume_id" not in volume_dict: + created_volume = create_volume( + volume_dict, call="function", wait_to_finish=wait_to_finish + ) created = True - if 'volumeId' in created_volume: - volume_dict['volume_id'] = created_volume['volumeId'] + if "volumeId" in created_volume: + volume_dict["volume_id"] = created_volume["volumeId"] attach = attach_volume( name, - {'volume_id': volume_dict['volume_id'], - 'device': volume['device']}, - instance_id=kwargs['instance_id'], - call='action' + {"volume_id": volume_dict["volume_id"], "device": volume["device"]}, + instance_id=kwargs["instance_id"], + call="action", ) # Update the delvol parameter for this volume - delvols_on_destroy = kwargs.get('del_all_vols_on_destroy', None) + delvols_on_destroy = kwargs.get("del_all_vols_on_destroy", None) if attach and created and delvols_on_destroy is not None: - _toggle_delvol(instance_id=kwargs['instance_id'], - device=volume['device'], - value=delvols_on_destroy) + _toggle_delvol( + instance_id=kwargs["instance_id"], + device=volume["device"], + value=delvols_on_destroy, + ) if attach: - msg = ( - '{0} attached to {1} (aka {2}) as device {3}'.format( - volume_dict['volume_id'], - kwargs['instance_id'], - name, - volume['device'] - ) + msg = "{0} attached to {1} (aka {2}) as device {3}".format( + volume_dict["volume_id"], kwargs["instance_id"], name, volume["device"] ) log.info(msg) ret.append(msg) @@ -3032,79 +3067,81 @@ def create_attach_volumes(name, kwargs, call=None, wait_to_finish=True): def stop(name, call=None): - ''' + """ Stop a node - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - log.info('Stopping node %s', name) + log.info("Stopping node %s", name) - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] - __utils__['cloud.fire_event']( - 'event', - 'stopping instance', - 'salt/cloud/{0}/stopping'.format(name), - args={'name': name, 'instance_id': instance_id}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "stopping instance", + "salt/cloud/{0}/stopping".format(name), + args={"name": name, "instance_id": instance_id}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - params = {'Action': 'StopInstances', - 'InstanceId.1': instance_id} - result = aws.query(params, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + params = {"Action": "StopInstances", "InstanceId.1": instance_id} + result = aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return result def start(name, call=None): - ''' + """ Start a node - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The start action must be called with -a or --action.' + "The start action must be called with -a or --action." ) - log.info('Starting node %s', name) + log.info("Starting node %s", name) - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] - __utils__['cloud.fire_event']( - 'event', - 'starting instance', - 'salt/cloud/{0}/starting'.format(name), - args={'name': name, 'instance_id': instance_id}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting instance", + "salt/cloud/{0}/starting".format(name), + args={"name": name, "instance_id": instance_id}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - params = {'Action': 'StartInstances', - 'InstanceId.1': instance_id} - result = aws.query(params, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + params = {"Action": "StartInstances", "InstanceId.1": instance_id} + result = aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return result -def set_tags(name=None, - tags=None, - call=None, - location=None, - instance_id=None, - resource_id=None, - kwargs=None): # pylint: disable=W0613 - ''' +def set_tags( + name=None, + tags=None, + call=None, + location=None, + instance_id=None, + resource_id=None, + kwargs=None, +): # pylint: disable=W0613 + """ Set tags for a resource. Normally a VM name or instance_id is passed in, but a resource_id may be passed instead. If both are passed in, the instance_id will be used. @@ -3115,7 +3152,7 @@ def set_tags(name=None, salt-cloud -a set_tags mymachine tag1=somestuff tag2='Other stuff' salt-cloud -a set_tags resource_id=vol-3267ab32 tag=somestuff - ''' + """ if kwargs is None: kwargs = {} @@ -3123,93 +3160,89 @@ def set_tags(name=None, location = get_location() if instance_id is None: - if 'resource_id' in kwargs: - resource_id = kwargs['resource_id'] - del kwargs['resource_id'] + if "resource_id" in kwargs: + resource_id = kwargs["resource_id"] + del kwargs["resource_id"] - if 'instance_id' in kwargs: - instance_id = kwargs['instance_id'] - del kwargs['instance_id'] + if "instance_id" in kwargs: + instance_id = kwargs["instance_id"] + del kwargs["instance_id"] if resource_id is None: if instance_id is None: - instance_id = _get_node(name=name, instance_id=None, location=location)['instanceId'] + instance_id = _get_node(name=name, instance_id=None, location=location)[ + "instanceId" + ] else: instance_id = resource_id # This second check is a safety, in case the above still failed to produce # a usable ID if instance_id is None: - return { - 'Error': 'A valid instance_id or resource_id was not specified.' - } + return {"Error": "A valid instance_id or resource_id was not specified."} - params = {'Action': 'CreateTags', - 'ResourceId.1': instance_id} + params = {"Action": "CreateTags", "ResourceId.1": instance_id} - log.debug('Tags to set for %s: %s', name, tags) + log.debug("Tags to set for %s: %s", name, tags) if kwargs and not tags: tags = kwargs for idx, (tag_k, tag_v) in enumerate(six.iteritems(tags)): - params['Tag.{0}.Key'.format(idx)] = tag_k - params['Tag.{0}.Value'.format(idx)] = tag_v + params["Tag.{0}.Key".format(idx)] = tag_k + params["Tag.{0}.Value".format(idx)] = tag_v attempts = 0 while attempts < aws.AWS_MAX_RETRIES: - aws.query(params, - setname='tagSet', - location=location, - provider=get_provider(), - opts=__opts__, - sigver='4') - - settags = get_tags( - instance_id=instance_id, call='action', location=location + aws.query( + params, + setname="tagSet", + location=location, + provider=get_provider(), + opts=__opts__, + sigver="4", ) - log.debug('Setting the tags returned: %s', settags) + settags = get_tags(instance_id=instance_id, call="action", location=location) + + log.debug("Setting the tags returned: %s", settags) failed_to_set_tags = False for tag in settags: - if tag['key'] not in tags: + if tag["key"] not in tags: # We were not setting this tag continue - if tag.get('value') is None and tags.get(tag['key']) == '': + if tag.get("value") is None and tags.get(tag["key"]) == "": # This is a correctly set tag with no value continue - if six.text_type(tags.get(tag['key'])) != six.text_type(tag['value']): + if six.text_type(tags.get(tag["key"])) != six.text_type(tag["value"]): # Not set to the proper value!? log.debug( - 'Setting the tag %s returned %s instead of %s', - tag['key'], tags.get(tag['key']), tag['value'] + "Setting the tag %s returned %s instead of %s", + tag["key"], + tags.get(tag["key"]), + tag["value"], ) failed_to_set_tags = True break if failed_to_set_tags: - log.warning('Failed to set tags. Remaining attempts %s', attempts) + log.warning("Failed to set tags. Remaining attempts %s", attempts) attempts += 1 aws.sleep_exponential_backoff(attempts) continue return settags - raise SaltCloudSystemExit( - 'Failed to set tags on {0}!'.format(name) - ) + raise SaltCloudSystemExit("Failed to set tags on {0}!".format(name)) -def get_tags(name=None, - instance_id=None, - call=None, - location=None, - kwargs=None, - resource_id=None): # pylint: disable=W0613 - ''' +def get_tags( + name=None, instance_id=None, call=None, location=None, kwargs=None, resource_id=None +): # pylint: disable=W0613 + """ Retrieve tags for a resource. Normally a VM name or instance_id is passed in, but a resource_id may be passed instead. If both are passed in, the instance_id will be used. @@ -3220,39 +3253,41 @@ def get_tags(name=None, salt-cloud -a get_tags mymachine salt-cloud -a get_tags resource_id=vol-3267ab32 - ''' + """ if location is None: location = get_location() if instance_id is None: if resource_id is None: if name: - instance_id = _get_node(name)['instanceId'] - elif 'instance_id' in kwargs: - instance_id = kwargs['instance_id'] - elif 'resource_id' in kwargs: - instance_id = kwargs['resource_id'] + instance_id = _get_node(name)["instanceId"] + elif "instance_id" in kwargs: + instance_id = kwargs["instance_id"] + elif "resource_id" in kwargs: + instance_id = kwargs["resource_id"] else: instance_id = resource_id - params = {'Action': 'DescribeTags', - 'Filter.1.Name': 'resource-id', - 'Filter.1.Value': instance_id} + params = { + "Action": "DescribeTags", + "Filter.1.Name": "resource-id", + "Filter.1.Value": instance_id, + } - return aws.query(params, - setname='tagSet', - location=location, - provider=get_provider(), - opts=__opts__, - sigver='4') + return aws.query( + params, + setname="tagSet", + location=location, + provider=get_provider(), + opts=__opts__, + sigver="4", + ) -def del_tags(name=None, - kwargs=None, - call=None, - instance_id=None, - resource_id=None): # pylint: disable=W0613 - ''' +def del_tags( + name=None, kwargs=None, call=None, instance_id=None, resource_id=None +): # pylint: disable=W0613 + """ Delete tags for a resource. Normally a VM name or instance_id is passed in, but a resource_id may be passed instead. If both are passed in, the instance_id will be used. @@ -3264,34 +3299,35 @@ def del_tags(name=None, salt-cloud -a del_tags mymachine tags=mytag, salt-cloud -a del_tags mymachine tags=tag1,tag2,tag3 salt-cloud -a del_tags resource_id=vol-3267ab32 tags=tag1,tag2,tag3 - ''' + """ if kwargs is None: kwargs = {} - if 'tags' not in kwargs: + if "tags" not in kwargs: raise SaltCloudSystemExit( - 'A tag or tags must be specified using tags=list,of,tags' + "A tag or tags must be specified using tags=list,of,tags" ) - if not name and 'resource_id' in kwargs: - instance_id = kwargs['resource_id'] - del kwargs['resource_id'] + if not name and "resource_id" in kwargs: + instance_id = kwargs["resource_id"] + del kwargs["resource_id"] if not instance_id: - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] - params = {'Action': 'DeleteTags', - 'ResourceId.1': instance_id} + params = {"Action": "DeleteTags", "ResourceId.1": instance_id} - for idx, tag in enumerate(kwargs['tags'].split(',')): - params['Tag.{0}.Key'.format(idx)] = tag + for idx, tag in enumerate(kwargs["tags"].split(",")): + params["Tag.{0}.Key".format(idx)] = tag - aws.query(params, - setname='tagSet', - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + aws.query( + params, + setname="tagSet", + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) if resource_id: return get_tags(resource_id=resource_id) @@ -3300,7 +3336,7 @@ def del_tags(name=None, def rename(name, kwargs, call=None): - ''' + """ Properly rename a node. Pass in the new name as "new name". CLI Example: @@ -3308,23 +3344,21 @@ def rename(name, kwargs, call=None): .. code-block:: bash salt-cloud -a rename mymachine newname=yourmachine - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The rename action must be called with -a or --action.' + "The rename action must be called with -a or --action." ) - log.info('Renaming %s to %s', name, kwargs['newname']) + log.info("Renaming %s to %s", name, kwargs["newname"]) - set_tags(name, {'Name': kwargs['newname']}, call='action') + set_tags(name, {"Name": kwargs["newname"]}, call="action") - salt.utils.cloud.rename_key( - __opts__['pki_dir'], name, kwargs['newname'] - ) + salt.utils.cloud.rename_key(__opts__["pki_dir"], name, kwargs["newname"]) def destroy(name, call=None): - ''' + """ Destroy a node. Will check termination protection and warn if enabled. CLI Example: @@ -3332,68 +3366,57 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud --destroy mymachine - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) node_metadata = _get_node(name) - instance_id = node_metadata['instanceId'] - sir_id = node_metadata.get('spotInstanceRequestId') + instance_id = node_metadata["instanceId"] + sir_id = node_metadata.get("spotInstanceRequestId") protected = show_term_protect( - name=name, - instance_id=instance_id, - call='action', - quiet=True + name=name, instance_id=instance_id, call="action", quiet=True ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name, 'instance_id': instance_id}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name, "instance_id": instance_id}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if protected == 'true': + if protected == "true": raise SaltCloudSystemExit( - 'This instance has been protected from being destroyed. ' - 'Use the following command to disable protection:\n\n' - 'salt-cloud -a disable_term_protect {0}'.format( - name - ) + "This instance has been protected from being destroyed. " + "Use the following command to disable protection:\n\n" + "salt-cloud -a disable_term_protect {0}".format(name) ) ret = {} # Default behavior is to rename EC2 VMs when destroyed # via salt-cloud, unless explicitly set to False. - rename_on_destroy = config.get_cloud_config_value('rename_on_destroy', - get_configured_provider(), - __opts__, - search_global=False) + rename_on_destroy = config.get_cloud_config_value( + "rename_on_destroy", get_configured_provider(), __opts__, search_global=False + ) if rename_on_destroy is not False: - newname = '{0}-DEL{1}'.format(name, uuid.uuid4().hex) - rename(name, kwargs={'newname': newname}, call='action') + newname = "{0}-DEL{1}".format(name, uuid.uuid4().hex) + rename(name, kwargs={"newname": newname}, call="action") log.info( - 'Machine will be identified as %s until it has been ' - 'cleaned up.', newname + "Machine will be identified as %s until it has been " "cleaned up.", newname ) - ret['newname'] = newname + ret["newname"] = newname - params = {'Action': 'TerminateInstances', - 'InstanceId.1': instance_id} + params = {"Action": "TerminateInstances", "InstanceId.1": instance_id} location = get_location() provider = get_provider() - result = aws.query(params, - location=location, - provider=provider, - opts=__opts__, - sigver='4') + result = aws.query( + params, location=location, provider=provider, opts=__opts__, sigver="4" + ) log.info(result) ret.update(result[0]) @@ -3401,34 +3424,36 @@ def destroy(name, call=None): # If this instance is part of a spot instance request, we # need to cancel it as well if sir_id is not None: - params = {'Action': 'CancelSpotInstanceRequests', - 'SpotInstanceRequestId.1': sir_id} - result = aws.query(params, - location=location, - provider=provider, - opts=__opts__, - sigver='4') - ret['spotInstance'] = result[0] + params = { + "Action": "CancelSpotInstanceRequests", + "SpotInstanceRequestId.1": sir_id, + } + result = aws.query( + params, location=location, provider=provider, opts=__opts__, sigver="4" + ) + ret["spotInstance"] = result[0] - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name, 'instance_id': instance_id}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name, "instance_id": instance_id}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - __utils__['cloud.cachedir_index_del'](name) + __utils__["cloud.cachedir_index_del"](name) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) return ret def reboot(name, call=None): - ''' + """ Reboot a node. CLI Example: @@ -3436,48 +3461,50 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot mymachine - ''' - instance_id = _get_node(name)['instanceId'] - params = {'Action': 'RebootInstances', - 'InstanceId.1': instance_id} + """ + instance_id = _get_node(name)["instanceId"] + params = {"Action": "RebootInstances", "InstanceId.1": instance_id} - result = aws.query(params, - setname='tagSet', - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + result = aws.query( + params, + setname="tagSet", + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) if result == []: - log.info('Complete') + log.info("Complete") - return {'Reboot': 'Complete'} + return {"Reboot": "Complete"} def show_image(kwargs, call=None): - ''' + """ Show the details from EC2 concerning an AMI - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_image action must be called with -f or --function.' + "The show_image action must be called with -f or --function." ) - params = {'ImageId.1': kwargs['image'], - 'Action': 'DescribeImages'} - result = aws.query(params, - setname='tagSet', - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + params = {"ImageId.1": kwargs["image"], "Action": "DescribeImages"} + result = aws.query( + params, + setname="tagSet", + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) log.info(result) return result def show_instance(name=None, instance_id=None, call=None, kwargs=None): - ''' + """ Show the details from EC2 concerning an AMI. Can be called as an action (which requires a name): @@ -3492,24 +3519,21 @@ def show_instance(name=None, instance_id=None, call=None, kwargs=None): salt-cloud -f show_instance my-ec2 name=myinstance salt-cloud -f show_instance my-ec2 instance_id=i-d34db33f - ''' - if not name and call == 'action': - raise SaltCloudSystemExit( - 'The show_instance action requires a name.' - ) + """ + if not name and call == "action": + raise SaltCloudSystemExit("The show_instance action requires a name.") - if call == 'function': - name = kwargs.get('name', None) - instance_id = kwargs.get('instance_id', None) + if call == "function": + name = kwargs.get("name", None) + instance_id = kwargs.get("instance_id", None) if not name and not instance_id: raise SaltCloudSystemExit( - 'The show_instance function requires ' - 'either a name or an instance_id' + "The show_instance function requires " "either a name or an instance_id" ) node = _get_node(name=name, instance_id=instance_id) - __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](node, __active_provider_name__, __opts__) return node @@ -3517,16 +3541,16 @@ def _get_node(name=None, instance_id=None, location=None): if location is None: location = get_location() - params = {'Action': 'DescribeInstances'} + params = {"Action": "DescribeInstances"} - if six.text_type(name).startswith('i-') and (len(name) == 10 or len(name) == 19): + if six.text_type(name).startswith("i-") and (len(name) == 10 or len(name) == 19): instance_id = name if instance_id: - params['InstanceId.1'] = instance_id + params["InstanceId.1"] = instance_id else: - params['Filter.1.Name'] = 'tag:Name' - params['Filter.1.Value.1'] = name + params["Filter.1.Name"] = "tag:Name" + params["Filter.1.Value.1"] = name log.trace(params) @@ -3535,83 +3559,81 @@ def _get_node(name=None, instance_id=None, location=None): attempts = 0 while attempts < aws.AWS_MAX_RETRIES: try: - instances = aws.query(params, - location=location, - provider=provider, - opts=__opts__, - sigver='4') + instances = aws.query( + params, location=location, provider=provider, opts=__opts__, sigver="4" + ) instance_info = _extract_instance_info(instances).values() return next(iter(instance_info)) except IndexError: attempts += 1 log.debug( - 'Failed to get the data for node \'%s\'. Remaining ' - 'attempts: %s', instance_id or name, attempts + "Failed to get the data for node '%s'. Remaining " "attempts: %s", + instance_id or name, + attempts, ) aws.sleep_exponential_backoff(attempts) return {} def list_nodes_full(location=None, call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f ' - 'or --function.' + "The list_nodes_full function must be called with -f " "or --function." ) return _list_nodes_full(location or get_location()) def _extract_name_tag(item): - if 'tagSet' in item and item['tagSet'] is not None: - tagset = item['tagSet'] - if isinstance(tagset['item'], list): - for tag in tagset['item']: - if tag['key'] == 'Name': - return tag['value'] - return item['instanceId'] - return item['tagSet']['item']['value'] - return item['instanceId'] + if "tagSet" in item and item["tagSet"] is not None: + tagset = item["tagSet"] + if isinstance(tagset["item"], list): + for tag in tagset["item"]: + if tag["key"] == "Name": + return tag["value"] + return item["instanceId"] + return item["tagSet"]["item"]["value"] + return item["instanceId"] def _extract_instance_info(instances): - ''' + """ Given an instance query, return a dict of all instance data - ''' + """ ret = {} for instance in instances: # items could be type dict or list (for stopped EC2 instances) - if isinstance(instance['instancesSet']['item'], list): - for item in instance['instancesSet']['item']: + if isinstance(instance["instancesSet"]["item"], list): + for item in instance["instancesSet"]["item"]: name = _extract_name_tag(item) ret[name] = item - ret[name]['name'] = name + ret[name]["name"] = name ret[name].update( dict( - id=item['instanceId'], - image=item['imageId'], - size=item['instanceType'], - state=item['instanceState']['name'], - private_ips=item.get('privateIpAddress', []), - public_ips=item.get('ipAddress', []) + id=item["instanceId"], + image=item["imageId"], + size=item["instanceType"], + state=item["instanceState"]["name"], + private_ips=item.get("privateIpAddress", []), + public_ips=item.get("ipAddress", []), ) ) else: - item = instance['instancesSet']['item'] + item = instance["instancesSet"]["item"] name = _extract_name_tag(item) ret[name] = item - ret[name]['name'] = name + ret[name]["name"] = name ret[name].update( dict( - id=item['instanceId'], - image=item['imageId'], - size=item['instanceType'], - state=item['instanceState']['name'], - private_ips=item.get('privateIpAddress', []), - public_ips=item.get('ipAddress', []) + id=item["instanceId"], + image=item["imageId"], + size=item["instanceType"], + state=item["instanceState"]["name"], + private_ips=item.get("privateIpAddress", []), + public_ips=item.get("ipAddress", []), ) ) @@ -3619,195 +3641,206 @@ def _extract_instance_info(instances): def _list_nodes_full(location=None): - ''' + """ Return a list of the VMs that in this location - ''' - provider = __active_provider_name__ or 'ec2' - if ':' in provider: - comps = provider.split(':') + """ + provider = __active_provider_name__ or "ec2" + if ":" in provider: + comps = provider.split(":") provider = comps[0] - params = {'Action': 'DescribeInstances'} - instances = aws.query(params, - location=location, - provider=provider, - opts=__opts__, - sigver='4') - if 'error' in instances: + params = {"Action": "DescribeInstances"} + instances = aws.query( + params, location=location, provider=provider, opts=__opts__, sigver="4" + ) + if "error" in instances: raise SaltCloudSystemExit( - 'An error occurred while listing nodes: {0}'.format( - instances['error']['Errors']['Error']['Message'] + "An error occurred while listing nodes: {0}".format( + instances["error"]["Errors"]["Error"]["Message"] ) ) ret = _extract_instance_info(instances) - __utils__['cloud.cache_node_list'](ret, provider, __opts__) + __utils__["cloud.cache_node_list"](ret, provider, __opts__) return ret def list_nodes_min(location=None, call=None): - ''' + """ Return a list of the VMs that are on the provider. Only a list of VM names, and their state, is returned. This is the minimum amount of information needed to check for existing VMs. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_min function must be called with -f or --function.' + "The list_nodes_min function must be called with -f or --function." ) ret = {} - params = {'Action': 'DescribeInstances'} - instances = aws.query(params, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') - if 'error' in instances: + params = {"Action": "DescribeInstances"} + instances = aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) + if "error" in instances: raise SaltCloudSystemExit( - 'An error occurred while listing nodes: {0}'.format( - instances['error']['Errors']['Error']['Message'] + "An error occurred while listing nodes: {0}".format( + instances["error"]["Errors"]["Error"]["Message"] ) ) for instance in instances: - if isinstance(instance['instancesSet']['item'], list): - items = instance['instancesSet']['item'] + if isinstance(instance["instancesSet"]["item"], list): + items = instance["instancesSet"]["item"] else: - items = [instance['instancesSet']['item']] + items = [instance["instancesSet"]["item"]] for item in items: - state = item['instanceState']['name'] + state = item["instanceState"]["name"] name = _extract_name_tag(item) - id = item['instanceId'] - ret[name] = {'state': state, 'id': id} + id = item["instanceId"] + ret[name] = {"state": state, "id": id} return ret def list_nodes(call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} nodes = list_nodes_full(get_location()) - if 'error' in nodes: + if "error" in nodes: raise SaltCloudSystemExit( - 'An error occurred while listing nodes: {0}'.format( - nodes['error']['Errors']['Error']['Message'] + "An error occurred while listing nodes: {0}".format( + nodes["error"]["Errors"]["Error"]["Message"] ) ) for node in nodes: ret[node] = { - 'id': nodes[node]['id'], - 'image': nodes[node]['image'], - 'name': nodes[node]['name'], - 'size': nodes[node]['size'], - 'state': nodes[node]['state'], - 'private_ips': nodes[node]['private_ips'], - 'public_ips': nodes[node]['public_ips'], + "id": nodes[node]["id"], + "image": nodes[node]["image"], + "name": nodes[node]["name"], + "size": nodes[node]["size"], + "state": nodes[node]["state"], + "private_ips": nodes[node]["private_ips"], + "public_ips": nodes[node]["public_ips"], } return ret def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full(get_location()), __opts__['query.selection'], call, + list_nodes_full(get_location()), __opts__["query.selection"], call, ) def show_term_protect(name=None, instance_id=None, call=None, quiet=False): - ''' + """ Show the details from EC2 concerning an instance's termination protection state - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_term_protect action must be called with -a or --action.' + "The show_term_protect action must be called with -a or --action." ) if not instance_id: - instance_id = _get_node(name)['instanceId'] - params = {'Action': 'DescribeInstanceAttribute', - 'InstanceId': instance_id, - 'Attribute': 'disableApiTermination'} - result = aws.query(params, - location=get_location(), - provider=get_provider(), - return_root=True, - opts=__opts__, - sigver='4') + instance_id = _get_node(name)["instanceId"] + params = { + "Action": "DescribeInstanceAttribute", + "InstanceId": instance_id, + "Attribute": "disableApiTermination", + } + result = aws.query( + params, + location=get_location(), + provider=get_provider(), + return_root=True, + opts=__opts__, + sigver="4", + ) disable_protect = False for item in result: - if 'value' in item: - disable_protect = item['value'] + if "value" in item: + disable_protect = item["value"] break log.log( logging.DEBUG if quiet is True else logging.INFO, - 'Termination Protection is %s for %s', - disable_protect == 'true' and 'enabled' or 'disabled', name + "Termination Protection is %s for %s", + disable_protect == "true" and "enabled" or "disabled", + name, ) return disable_protect def show_detailed_monitoring(name=None, instance_id=None, call=None, quiet=False): - ''' + """ Show the details from EC2 regarding cloudwatch detailed monitoring. - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_detailed_monitoring action must be called with -a or --action.' + "The show_detailed_monitoring action must be called with -a or --action." ) location = get_location() - if six.text_type(name).startswith('i-') and (len(name) == 10 or len(name) == 19): + if six.text_type(name).startswith("i-") and (len(name) == 10 or len(name) == 19): instance_id = name if not name and not instance_id: raise SaltCloudSystemExit( - 'The show_detailed_monitoring action must be provided with a name or instance\ - ID' + "The show_detailed_monitoring action must be provided with a name or instance\ + ID" ) matched = _get_node(name=name, instance_id=instance_id, location=location) log.log( logging.DEBUG if quiet is True else logging.INFO, - 'Detailed Monitoring is %s for %s', matched['monitoring'], name + "Detailed Monitoring is %s for %s", + matched["monitoring"], + name, ) - return matched['monitoring'] + return matched["monitoring"] def _toggle_term_protect(name, value): - ''' + """ Enable or Disable termination protection on a node - ''' - instance_id = _get_node(name)['instanceId'] - params = {'Action': 'ModifyInstanceAttribute', - 'InstanceId': instance_id, - 'DisableApiTermination.Value': value} + """ + instance_id = _get_node(name)["instanceId"] + params = { + "Action": "ModifyInstanceAttribute", + "InstanceId": instance_id, + "DisableApiTermination.Value": value, + } - result = aws.query(params, - location=get_location(), - provider=get_provider(), - return_root=True, - opts=__opts__, - sigver='4') + result = aws.query( + params, + location=get_location(), + provider=get_provider(), + return_root=True, + opts=__opts__, + sigver="4", + ) - return show_term_protect(name=name, instance_id=instance_id, call='action') + return show_term_protect(name=name, instance_id=instance_id, call="action") def enable_term_protect(name, call=None): - ''' + """ Enable termination protection on a node CLI Example: @@ -3815,18 +3848,17 @@ def enable_term_protect(name, call=None): .. code-block:: bash salt-cloud -a enable_term_protect mymachine - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The enable_term_protect action must be called with ' - '-a or --action.' + "The enable_term_protect action must be called with " "-a or --action." ) - return _toggle_term_protect(name, 'true') + return _toggle_term_protect(name, "true") def disable_term_protect(name, call=None): - ''' + """ Disable termination protection on a node CLI Example: @@ -3834,71 +3866,70 @@ def disable_term_protect(name, call=None): .. code-block:: bash salt-cloud -a disable_term_protect mymachine - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The enable_term_protect action must be called with ' - '-a or --action.' + "The enable_term_protect action must be called with " "-a or --action." ) - return _toggle_term_protect(name, 'false') + return _toggle_term_protect(name, "false") def disable_detailed_monitoring(name, call=None): - ''' + """ Enable/disable detailed monitoring on a node CLI Example: - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The enable_term_protect action must be called with ' - '-a or --action.' + "The enable_term_protect action must be called with " "-a or --action." ) - instance_id = _get_node(name)['instanceId'] - params = {'Action': 'UnmonitorInstances', - 'InstanceId.1': instance_id} + instance_id = _get_node(name)["instanceId"] + params = {"Action": "UnmonitorInstances", "InstanceId.1": instance_id} - result = aws.query(params, - location=get_location(), - provider=get_provider(), - return_root=True, - opts=__opts__, - sigver='4') + result = aws.query( + params, + location=get_location(), + provider=get_provider(), + return_root=True, + opts=__opts__, + sigver="4", + ) - return show_detailed_monitoring(name=name, instance_id=instance_id, call='action') + return show_detailed_monitoring(name=name, instance_id=instance_id, call="action") def enable_detailed_monitoring(name, call=None): - ''' + """ Enable/disable detailed monitoring on a node CLI Example: - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The enable_term_protect action must be called with ' - '-a or --action.' + "The enable_term_protect action must be called with " "-a or --action." ) - instance_id = _get_node(name)['instanceId'] - params = {'Action': 'MonitorInstances', - 'InstanceId.1': instance_id} + instance_id = _get_node(name)["instanceId"] + params = {"Action": "MonitorInstances", "InstanceId.1": instance_id} - result = aws.query(params, - location=get_location(), - provider=get_provider(), - return_root=True, - opts=__opts__, - sigver='4') + result = aws.query( + params, + location=get_location(), + provider=get_provider(), + return_root=True, + opts=__opts__, + sigver="4", + ) - return show_detailed_monitoring(name=name, instance_id=instance_id, call='action') + return show_detailed_monitoring(name=name, instance_id=instance_id, call="action") def show_delvol_on_destroy(name, kwargs=None, call=None): - ''' + """ Do not delete all/specified EBS volumes upon instance termination CLI Example: @@ -3906,53 +3937,53 @@ def show_delvol_on_destroy(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a show_delvol_on_destroy mymachine - ''' + """ - if call != 'action': + if call != "action": raise SaltCloudSystemExit( - 'The show_delvol_on_destroy action must be called ' - 'with -a or --action.' + "The show_delvol_on_destroy action must be called " "with -a or --action." ) if not kwargs: kwargs = {} - instance_id = kwargs.get('instance_id', None) - device = kwargs.get('device', None) - volume_id = kwargs.get('volume_id', None) + instance_id = kwargs.get("instance_id", None) + device = kwargs.get("device", None) + volume_id = kwargs.get("volume_id", None) if instance_id is None: - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] - params = {'Action': 'DescribeInstances', - 'InstanceId.1': instance_id} + params = {"Action": "DescribeInstances", "InstanceId.1": instance_id} - data = aws.query(params, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) - blockmap = data[0]['instancesSet']['item']['blockDeviceMapping'] + blockmap = data[0]["instancesSet"]["item"]["blockDeviceMapping"] - if not isinstance(blockmap['item'], list): - blockmap['item'] = [blockmap['item']] + if not isinstance(blockmap["item"], list): + blockmap["item"] = [blockmap["item"]] items = [] - for idx, item in enumerate(blockmap['item']): - device_name = item['deviceName'] + for idx, item in enumerate(blockmap["item"]): + device_name = item["deviceName"] if device is not None and device != device_name: continue - if volume_id is not None and volume_id != item['ebs']['volumeId']: + if volume_id is not None and volume_id != item["ebs"]["volumeId"]: continue info = { - 'device_name': device_name, - 'volume_id': item['ebs']['volumeId'], - 'deleteOnTermination': item['ebs']['deleteOnTermination'] + "device_name": device_name, + "volume_id": item["ebs"]["volumeId"], + "deleteOnTermination": item["ebs"]["deleteOnTermination"], } items.append(info) @@ -3961,7 +3992,7 @@ def show_delvol_on_destroy(name, kwargs=None, call=None): def keepvol_on_destroy(name, kwargs=None, call=None): - ''' + """ Do not delete all/specified EBS volumes upon instance termination CLI Example: @@ -3969,24 +4000,23 @@ def keepvol_on_destroy(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a keepvol_on_destroy mymachine - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The keepvol_on_destroy action must be called with -a or --action.' + "The keepvol_on_destroy action must be called with -a or --action." ) if not kwargs: kwargs = {} - device = kwargs.get('device', None) - volume_id = kwargs.get('volume_id', None) + device = kwargs.get("device", None) + volume_id = kwargs.get("volume_id", None) - return _toggle_delvol(name=name, device=device, - volume_id=volume_id, value='false') + return _toggle_delvol(name=name, device=device, volume_id=volume_id, value="false") def delvol_on_destroy(name, kwargs=None, call=None): - ''' + """ Delete all/specified EBS volumes upon instance termination CLI Example: @@ -3994,78 +4024,85 @@ def delvol_on_destroy(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a delvol_on_destroy mymachine - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The delvol_on_destroy action must be called with -a or --action.' + "The delvol_on_destroy action must be called with -a or --action." ) if not kwargs: kwargs = {} - device = kwargs.get('device', None) - volume_id = kwargs.get('volume_id', None) + device = kwargs.get("device", None) + volume_id = kwargs.get("volume_id", None) - return _toggle_delvol(name=name, device=device, - volume_id=volume_id, value='true') + return _toggle_delvol(name=name, device=device, volume_id=volume_id, value="true") -def _toggle_delvol(name=None, instance_id=None, device=None, volume_id=None, - value=None, requesturl=None): +def _toggle_delvol( + name=None, + instance_id=None, + device=None, + volume_id=None, + value=None, + requesturl=None, +): if not instance_id: - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] if requesturl: - data = aws.query(requesturl=requesturl, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + requesturl=requesturl, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) else: - params = {'Action': 'DescribeInstances', - 'InstanceId.1': instance_id} - data, requesturl = aws.query(params, # pylint: disable=unbalanced-tuple-unpacking - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + params = {"Action": "DescribeInstances", "InstanceId.1": instance_id} + data, requesturl = aws.query( + params, # pylint: disable=unbalanced-tuple-unpacking + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) - blockmap = data[0]['instancesSet']['item']['blockDeviceMapping'] + blockmap = data[0]["instancesSet"]["item"]["blockDeviceMapping"] - params = {'Action': 'ModifyInstanceAttribute', - 'InstanceId': instance_id} + params = {"Action": "ModifyInstanceAttribute", "InstanceId": instance_id} - if not isinstance(blockmap['item'], list): - blockmap['item'] = [blockmap['item']] + if not isinstance(blockmap["item"], list): + blockmap["item"] = [blockmap["item"]] - for idx, item in enumerate(blockmap['item']): - device_name = item['deviceName'] + for idx, item in enumerate(blockmap["item"]): + device_name = item["deviceName"] if device is not None and device != device_name: continue - if volume_id is not None and volume_id != item['ebs']['volumeId']: + if volume_id is not None and volume_id != item["ebs"]["volumeId"]: continue - params['BlockDeviceMapping.{0}.DeviceName'.format(idx)] = device_name - params['BlockDeviceMapping.{0}.Ebs.DeleteOnTermination'.format(idx)] = value + params["BlockDeviceMapping.{0}.DeviceName".format(idx)] = device_name + params["BlockDeviceMapping.{0}.Ebs.DeleteOnTermination".format(idx)] = value - aws.query(params, - return_root=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + aws.query( + params, + return_root=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) - kwargs = {'instance_id': instance_id, - 'device': device, - 'volume_id': volume_id} - return show_delvol_on_destroy(name, kwargs, call='action') + kwargs = {"instance_id": instance_id, "device": device, "volume_id": volume_id} + return show_delvol_on_destroy(name, kwargs, call="action") def register_image(kwargs=None, call=None): - ''' + """ Create an ami from a snapshot CLI Example: @@ -4074,63 +4111,68 @@ def register_image(kwargs=None, call=None): salt-cloud -f register_image my-ec2-config ami_name=my_ami description="my description" root_device_name=/dev/xvda snapshot_id=snap-xxxxxxxx - ''' + """ - if call != 'function': - log.error( - 'The create_volume function must be called with -f or --function.' - ) + if call != "function": + log.error("The create_volume function must be called with -f or --function.") return False - if 'ami_name' not in kwargs: - log.error('ami_name must be specified to register an image.') + if "ami_name" not in kwargs: + log.error("ami_name must be specified to register an image.") return False - block_device_mapping = kwargs.get('block_device_mapping', None) + block_device_mapping = kwargs.get("block_device_mapping", None) if not block_device_mapping: - if 'snapshot_id' not in kwargs: - log.error('snapshot_id or block_device_mapping must be specified to register an image.') + if "snapshot_id" not in kwargs: + log.error( + "snapshot_id or block_device_mapping must be specified to register an image." + ) return False - if 'root_device_name' not in kwargs: - log.error('root_device_name or block_device_mapping must be specified to register an image.') + if "root_device_name" not in kwargs: + log.error( + "root_device_name or block_device_mapping must be specified to register an image." + ) return False - block_device_mapping = [{ - 'DeviceName': kwargs['root_device_name'], - 'Ebs': { - 'VolumeType': kwargs.get('volume_type', 'gp2'), - 'SnapshotId': kwargs['snapshot_id'], - } - }] + block_device_mapping = [ + { + "DeviceName": kwargs["root_device_name"], + "Ebs": { + "VolumeType": kwargs.get("volume_type", "gp2"), + "SnapshotId": kwargs["snapshot_id"], + }, + } + ] if not isinstance(block_device_mapping, list): block_device_mapping = [block_device_mapping] - params = {'Action': 'RegisterImage', - 'Name': kwargs['ami_name']} + params = {"Action": "RegisterImage", "Name": kwargs["ami_name"]} - params.update(_param_from_config('BlockDeviceMapping', block_device_mapping)) + params.update(_param_from_config("BlockDeviceMapping", block_device_mapping)) - if 'root_device_name' in kwargs: - params['RootDeviceName'] = kwargs['root_device_name'] + if "root_device_name" in kwargs: + params["RootDeviceName"] = kwargs["root_device_name"] - if 'description' in kwargs: - params['Description'] = kwargs['description'] + if "description" in kwargs: + params["Description"] = kwargs["description"] - if 'virtualization_type' in kwargs: - params['VirtualizationType'] = kwargs['virtualization_type'] + if "virtualization_type" in kwargs: + params["VirtualizationType"] = kwargs["virtualization_type"] - if 'architecture' in kwargs: - params['Architecture'] = kwargs['architecture'] + if "architecture" in kwargs: + params["Architecture"] = kwargs["architecture"] log.debug(params) - data = aws.query(params, - return_url=True, - return_root=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + return_root=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) r_data = {} for d in data[0]: @@ -4141,15 +4183,15 @@ def register_image(kwargs=None, call=None): def volume_create(**kwargs): - ''' + """ Wrapper around create_volume. Here just to ensure the compatibility with the cloud module. - ''' - return create_volume(kwargs, 'function') + """ + return create_volume(kwargs, "function") def create_volume(kwargs=None, call=None, wait_to_finish=False): - ''' + """ Create a volume. zone @@ -4197,99 +4239,100 @@ def create_volume(kwargs=None, call=None, wait_to_finish=False): salt-cloud -f create_volume my-ec2-config zone=us-east-1b salt-cloud -f create_volume my-ec2-config zone=us-east-1b tags='{"tag1": "val1", "tag2", "val2"}' - ''' - if call != 'function': - log.error( - 'The create_volume function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The create_volume function must be called with -f or --function.") return False - if 'zone' not in kwargs: - log.error('An availability zone must be specified to create a volume.') + if "zone" not in kwargs: + log.error("An availability zone must be specified to create a volume.") return False - if 'size' not in kwargs and 'snapshot' not in kwargs: + if "size" not in kwargs and "snapshot" not in kwargs: # This number represents GiB - kwargs['size'] = '10' + kwargs["size"] = "10" - params = {'Action': 'CreateVolume', - 'AvailabilityZone': kwargs['zone']} + params = {"Action": "CreateVolume", "AvailabilityZone": kwargs["zone"]} - if 'size' in kwargs: - params['Size'] = kwargs['size'] + if "size" in kwargs: + params["Size"] = kwargs["size"] - if 'snapshot' in kwargs: - params['SnapshotId'] = kwargs['snapshot'] + if "snapshot" in kwargs: + params["SnapshotId"] = kwargs["snapshot"] - if 'type' in kwargs: - params['VolumeType'] = kwargs['type'] + if "type" in kwargs: + params["VolumeType"] = kwargs["type"] - if 'iops' in kwargs and kwargs.get('type', 'standard') == 'io1': - params['Iops'] = kwargs['iops'] + if "iops" in kwargs and kwargs.get("type", "standard") == "io1": + params["Iops"] = kwargs["iops"] # You can't set `encrypted` if you pass a snapshot - if 'encrypted' in kwargs and 'snapshot' not in kwargs: - params['Encrypted'] = kwargs['encrypted'] - if 'kmskeyid' in kwargs: - params['KmsKeyId'] = kwargs['kmskeyid'] - if 'kmskeyid' in kwargs and 'encrypted' not in kwargs: - log.error( - 'If a KMS Key ID is specified, encryption must be enabled' - ) + if "encrypted" in kwargs and "snapshot" not in kwargs: + params["Encrypted"] = kwargs["encrypted"] + if "kmskeyid" in kwargs: + params["KmsKeyId"] = kwargs["kmskeyid"] + if "kmskeyid" in kwargs and "encrypted" not in kwargs: + log.error("If a KMS Key ID is specified, encryption must be enabled") return False log.debug(params) - data = aws.query(params, - return_url=True, - return_root=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + return_root=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) r_data = {} for d in data[0]: for k, v in six.iteritems(d): r_data[k] = v - volume_id = r_data['volumeId'] + volume_id = r_data["volumeId"] # Allow tags to be set upon creation - if 'tags' in kwargs: - if isinstance(kwargs['tags'], six.string_types): - tags = salt.utils.yaml.safe_load(kwargs['tags']) + if "tags" in kwargs: + if isinstance(kwargs["tags"], six.string_types): + tags = salt.utils.yaml.safe_load(kwargs["tags"]) else: - tags = kwargs['tags'] + tags = kwargs["tags"] if isinstance(tags, dict): - new_tags = set_tags(tags=tags, - resource_id=volume_id, - call='action', - location=get_location()) - r_data['tags'] = new_tags + new_tags = set_tags( + tags=tags, resource_id=volume_id, call="action", location=get_location() + ) + r_data["tags"] = new_tags # Waits till volume is available if wait_to_finish: - salt.utils.cloud.run_func_until_ret_arg(fun=describe_volumes, - kwargs={'volume_id': volume_id}, - fun_call=call, - argument_being_watched='status', - required_argument_response='available') + salt.utils.cloud.run_func_until_ret_arg( + fun=describe_volumes, + kwargs={"volume_id": volume_id}, + fun_call=call, + argument_being_watched="status", + required_argument_response="available", + ) return r_data def __attach_vol_to_instance(params, kws, instance_id): - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) if data[0]: log.warning( - 'Error attaching volume %s to instance %s. Retrying!', - kws['volume_id'], instance_id + "Error attaching volume %s to instance %s. Retrying!", + kws["volume_id"], + instance_id, ) return False @@ -4297,39 +4340,41 @@ def __attach_vol_to_instance(params, kws, instance_id): def attach_volume(name=None, kwargs=None, instance_id=None, call=None): - ''' + """ Attach a volume to an instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The attach_volume action must be called with -a or --action.' + "The attach_volume action must be called with -a or --action." ) if not kwargs: kwargs = {} - if 'instance_id' in kwargs: - instance_id = kwargs['instance_id'] + if "instance_id" in kwargs: + instance_id = kwargs["instance_id"] if name and not instance_id: - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] if not name and not instance_id: - log.error('Either a name or an instance_id is required.') + log.error("Either a name or an instance_id is required.") return False - if 'volume_id' not in kwargs: - log.error('A volume_id is required.') + if "volume_id" not in kwargs: + log.error("A volume_id is required.") return False - if 'device' not in kwargs: - log.error('A device is required (ex. /dev/sdb1).') + if "device" not in kwargs: + log.error("A device is required (ex. /dev/sdb1).") return False - params = {'Action': 'AttachVolume', - 'VolumeId': kwargs['volume_id'], - 'InstanceId': instance_id, - 'Device': kwargs['device']} + params = { + "Action": "AttachVolume", + "VolumeId": kwargs["volume_id"], + "InstanceId": instance_id, + "Device": kwargs["device"], + } log.debug(params) @@ -4339,22 +4384,25 @@ def attach_volume(name=None, kwargs=None, instance_id=None, call=None): __attach_vol_to_instance, update_args=(params, kwargs, instance_id), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10), + "wait_for_ip_interval", vm_, __opts__, default=10 + ), interval_multiplier=config.get_cloud_config_value( - 'wait_for_ip_interval_multiplier', vm_, __opts__, default=1), + "wait_for_ip_interval_multiplier", vm_, __opts__, default=1 + ), ) return data def show_volume(kwargs=None, call=None): - ''' + """ Wrapper around describe_volumes. Here just to keep functionality. Might be depreciated later. - ''' + """ if not kwargs: kwargs = {} @@ -4362,234 +4410,233 @@ def show_volume(kwargs=None, call=None): def detach_volume(name=None, kwargs=None, instance_id=None, call=None): - ''' + """ Detach a volume from an instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The detach_volume action must be called with -a or --action.' + "The detach_volume action must be called with -a or --action." ) if not kwargs: kwargs = {} - if 'volume_id' not in kwargs: - log.error('A volume_id is required.') + if "volume_id" not in kwargs: + log.error("A volume_id is required.") return False - params = {'Action': 'DetachVolume', - 'VolumeId': kwargs['volume_id']} + params = {"Action": "DetachVolume", "VolumeId": kwargs["volume_id"]} - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def delete_volume(name=None, kwargs=None, instance_id=None, call=None): - ''' + """ Delete a volume - ''' + """ if not kwargs: kwargs = {} - if 'volume_id' not in kwargs: - log.error('A volume_id is required.') + if "volume_id" not in kwargs: + log.error("A volume_id is required.") return False - params = {'Action': 'DeleteVolume', - 'VolumeId': kwargs['volume_id']} + params = {"Action": "DeleteVolume", "VolumeId": kwargs["volume_id"]} - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def volume_list(**kwargs): - ''' + """ Wrapper around describe_volumes. Here just to ensure the compatibility with the cloud module. - ''' - return describe_volumes(kwargs, 'function') + """ + return describe_volumes(kwargs, "function") def describe_volumes(kwargs=None, call=None): - ''' + """ Describe a volume (or volumes) volume_id One or more volume IDs. Multiple IDs must be separated by ",". TODO: Add all of the filters. - ''' - if call != 'function': + """ + if call != "function": log.error( - 'The describe_volumes function must be called with -f ' - 'or --function.' + "The describe_volumes function must be called with -f " "or --function." ) return False if not kwargs: kwargs = {} - params = {'Action': 'DescribeVolumes'} + params = {"Action": "DescribeVolumes"} - if 'volume_id' in kwargs: - volume_id = kwargs['volume_id'].split(',') + if "volume_id" in kwargs: + volume_id = kwargs["volume_id"].split(",") for volume_index, volume_id in enumerate(volume_id): - params['VolumeId.{0}'.format(volume_index)] = volume_id + params["VolumeId.{0}".format(volume_index)] = volume_id log.debug(params) - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def create_keypair(kwargs=None, call=None): - ''' + """ Create an SSH keypair - ''' - if call != 'function': - log.error( - 'The create_keypair function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The create_keypair function must be called with -f or --function.") return False if not kwargs: kwargs = {} - if 'keyname' not in kwargs: - log.error('A keyname is required.') + if "keyname" not in kwargs: + log.error("A keyname is required.") return False - params = {'Action': 'CreateKeyPair', - 'KeyName': kwargs['keyname']} + params = {"Action": "CreateKeyPair", "KeyName": kwargs["keyname"]} - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def import_keypair(kwargs=None, call=None): - ''' + """ Import an SSH public key. .. versionadded:: 2015.8.3 - ''' - if call != 'function': - log.error( - 'The import_keypair function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The import_keypair function must be called with -f or --function.") return False if not kwargs: kwargs = {} - if 'keyname' not in kwargs: - log.error('A keyname is required.') + if "keyname" not in kwargs: + log.error("A keyname is required.") return False - if 'file' not in kwargs: - log.error('A public key file is required.') + if "file" not in kwargs: + log.error("A public key file is required.") return False - params = {'Action': 'ImportKeyPair', - 'KeyName': kwargs['keyname']} + params = {"Action": "ImportKeyPair", "KeyName": kwargs["keyname"]} - public_key_file = kwargs['file'] + public_key_file = kwargs["file"] if os.path.exists(public_key_file): - with salt.utils.files.fopen(public_key_file, 'r') as fh_: + with salt.utils.files.fopen(public_key_file, "r") as fh_: public_key = salt.utils.stringutils.to_unicode(fh_.read()) if public_key is not None: - params['PublicKeyMaterial'] = base64.b64encode(public_key) + params["PublicKeyMaterial"] = base64.b64encode(public_key) - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def show_keypair(kwargs=None, call=None): - ''' + """ Show the details of an SSH keypair - ''' - if call != 'function': - log.error( - 'The show_keypair function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The show_keypair function must be called with -f or --function.") return False if not kwargs: kwargs = {} - if 'keyname' not in kwargs: - log.error('A keyname is required.') + if "keyname" not in kwargs: + log.error("A keyname is required.") return False - params = {'Action': 'DescribeKeyPairs', - 'KeyName.1': kwargs['keyname']} + params = {"Action": "DescribeKeyPairs", "KeyName.1": kwargs["keyname"]} - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def delete_keypair(kwargs=None, call=None): - ''' + """ Delete an SSH keypair - ''' - if call != 'function': - log.error( - 'The delete_keypair function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The delete_keypair function must be called with -f or --function.") return False if not kwargs: kwargs = {} - if 'keyname' not in kwargs: - log.error('A keyname is required.') + if "keyname" not in kwargs: + log.error("A keyname is required.") return False - params = {'Action': 'DeleteKeyPair', - 'KeyName': kwargs['keyname']} + params = {"Action": "DeleteKeyPair", "KeyName": kwargs["keyname"]} - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def create_snapshot(kwargs=None, call=None, wait_to_finish=False): - ''' + """ Create a snapshot. volume_id @@ -4605,133 +4652,137 @@ def create_snapshot(kwargs=None, call=None, wait_to_finish=False): salt-cloud -f create_snapshot my-ec2-config volume_id=vol-351d8826 salt-cloud -f create_snapshot my-ec2-config volume_id=vol-351d8826 \\ description="My Snapshot Description" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_snapshot function must be called with -f ' - 'or --function.' + "The create_snapshot function must be called with -f " "or --function." ) if kwargs is None: kwargs = {} - volume_id = kwargs.get('volume_id', None) - description = kwargs.get('description', '') + volume_id = kwargs.get("volume_id", None) + description = kwargs.get("description", "") if volume_id is None: - raise SaltCloudSystemExit( - 'A volume_id must be specified to create a snapshot.' - ) + raise SaltCloudSystemExit("A volume_id must be specified to create a snapshot.") - params = {'Action': 'CreateSnapshot', - 'VolumeId': volume_id, - 'Description': description} + params = { + "Action": "CreateSnapshot", + "VolumeId": volume_id, + "Description": description, + } log.debug(params) - data = aws.query(params, - return_url=True, - return_root=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4')[0] + data = aws.query( + params, + return_url=True, + return_root=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + )[0] r_data = {} for d in data: for k, v in six.iteritems(d): r_data[k] = v - if 'snapshotId' in r_data: - snapshot_id = r_data['snapshotId'] + if "snapshotId" in r_data: + snapshot_id = r_data["snapshotId"] # Waits till volume is available if wait_to_finish: - salt.utils.cloud.run_func_until_ret_arg(fun=describe_snapshots, - kwargs={'snapshot_id': snapshot_id}, - fun_call=call, - argument_being_watched='status', - required_argument_response='completed') + salt.utils.cloud.run_func_until_ret_arg( + fun=describe_snapshots, + kwargs={"snapshot_id": snapshot_id}, + fun_call=call, + argument_being_watched="status", + required_argument_response="completed", + ) return r_data def delete_snapshot(kwargs=None, call=None): - ''' + """ Delete a snapshot - ''' - if call != 'function': + """ + if call != "function": log.error( - 'The delete_snapshot function must be called with -f ' - 'or --function.' + "The delete_snapshot function must be called with -f " "or --function." ) return False - if 'snapshot_id' not in kwargs: - log.error('A snapshot_id must be specified to delete a snapshot.') + if "snapshot_id" not in kwargs: + log.error("A snapshot_id must be specified to delete a snapshot.") return False - params = {'Action': 'DeleteSnapshot'} + params = {"Action": "DeleteSnapshot"} - if 'snapshot_id' in kwargs: - params['SnapshotId'] = kwargs['snapshot_id'] + if "snapshot_id" in kwargs: + params["SnapshotId"] = kwargs["snapshot_id"] log.debug(params) - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def copy_snapshot(kwargs=None, call=None): - ''' + """ Copy a snapshot - ''' - if call != 'function': - log.error( - 'The copy_snapshot function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The copy_snapshot function must be called with -f or --function.") return False - if 'source_region' not in kwargs: - log.error('A source_region must be specified to copy a snapshot.') + if "source_region" not in kwargs: + log.error("A source_region must be specified to copy a snapshot.") return False - if 'source_snapshot_id' not in kwargs: - log.error('A source_snapshot_id must be specified to copy a snapshot.') + if "source_snapshot_id" not in kwargs: + log.error("A source_snapshot_id must be specified to copy a snapshot.") return False - if 'description' not in kwargs: - kwargs['description'] = '' + if "description" not in kwargs: + kwargs["description"] = "" - params = {'Action': 'CopySnapshot'} + params = {"Action": "CopySnapshot"} - if 'source_region' in kwargs: - params['SourceRegion'] = kwargs['source_region'] + if "source_region" in kwargs: + params["SourceRegion"] = kwargs["source_region"] - if 'source_snapshot_id' in kwargs: - params['SourceSnapshotId'] = kwargs['source_snapshot_id'] + if "source_snapshot_id" in kwargs: + params["SourceSnapshotId"] = kwargs["source_snapshot_id"] - if 'description' in kwargs: - params['Description'] = kwargs['description'] + if "description" in kwargs: + params["Description"] = kwargs["description"] log.debug(params) - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def describe_snapshots(kwargs=None, call=None): - ''' + """ Describe a snapshot (or snapshots) snapshot_id @@ -4747,95 +4798,90 @@ def describe_snapshots(kwargs=None, call=None): Multiple aws account IDs must be separated by ",". TODO: Add all of the filters. - ''' - if call != 'function': + """ + if call != "function": log.error( - 'The describe_snapshot function must be called with -f ' - 'or --function.' + "The describe_snapshot function must be called with -f " "or --function." ) return False - params = {'Action': 'DescribeSnapshots'} + params = {"Action": "DescribeSnapshots"} # The AWS correct way is to use non-plurals like snapshot_id INSTEAD of snapshot_ids. - if 'snapshot_ids' in kwargs: - kwargs['snapshot_id'] = kwargs['snapshot_ids'] + if "snapshot_ids" in kwargs: + kwargs["snapshot_id"] = kwargs["snapshot_ids"] - if 'snapshot_id' in kwargs: - snapshot_ids = kwargs['snapshot_id'].split(',') + if "snapshot_id" in kwargs: + snapshot_ids = kwargs["snapshot_id"].split(",") for snapshot_index, snapshot_id in enumerate(snapshot_ids): - params['SnapshotId.{0}'.format(snapshot_index)] = snapshot_id + params["SnapshotId.{0}".format(snapshot_index)] = snapshot_id - if 'owner' in kwargs: - owners = kwargs['owner'].split(',') + if "owner" in kwargs: + owners = kwargs["owner"].split(",") for owner_index, owner in enumerate(owners): - params['Owner.{0}'.format(owner_index)] = owner + params["Owner.{0}".format(owner_index)] = owner - if 'restorable_by' in kwargs: - restorable_bys = kwargs['restorable_by'].split(',') + if "restorable_by" in kwargs: + restorable_bys = kwargs["restorable_by"].split(",") for restorable_by_index, restorable_by in enumerate(restorable_bys): - params[ - 'RestorableBy.{0}'.format(restorable_by_index) - ] = restorable_by + params["RestorableBy.{0}".format(restorable_by_index)] = restorable_by log.debug(params) - data = aws.query(params, - return_url=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_url=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) return data def get_console_output( - name=None, - location=None, - instance_id=None, - call=None, - kwargs=None, - ): - ''' + name=None, location=None, instance_id=None, call=None, kwargs=None, +): + """ Show the console output from the instance. By default, returns decoded data, not the Base64-encoded data that is actually returned from the EC2 API. - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The get_console_output action must be called with ' - '-a or --action.' + "The get_console_output action must be called with " "-a or --action." ) if location is None: location = get_location() if not instance_id: - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] if kwargs is None: kwargs = {} if instance_id is None: - if 'instance_id' in kwargs: - instance_id = kwargs['instance_id'] - del kwargs['instance_id'] + if "instance_id" in kwargs: + instance_id = kwargs["instance_id"] + del kwargs["instance_id"] - params = {'Action': 'GetConsoleOutput', - 'InstanceId': instance_id} + params = {"Action": "GetConsoleOutput", "InstanceId": instance_id} ret = {} - data = aws.query(params, - return_root=True, - location=location, - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_root=True, + location=location, + provider=get_provider(), + opts=__opts__, + sigver="4", + ) for item in data: - if next(six.iterkeys(item)) == 'output': - ret['output_decoded'] = binascii.a2b_base64(next(six.itervalues(item))) + if next(six.iterkeys(item)) == "output": + ret["output_decoded"] = binascii.a2b_base64(next(six.itervalues(item))) else: ret[next(six.iterkeys(item))] = next(six.itervalues(item)) @@ -4843,12 +4889,9 @@ def get_console_output( def get_password_data( - name=None, - kwargs=None, - instance_id=None, - call=None, - ): - ''' + name=None, kwargs=None, instance_id=None, call=None, +): + """ Return password data for a Windows instance. By default only the encrypted password data will be returned. However, if a @@ -4867,55 +4910,55 @@ def get_password_data( salt-cloud -a get_password_data mymachine key_file=/root/ec2key.pem Note: PKCS1_v1_5 was added in PyCrypto 2.5 - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The get_password_data action must be called with ' - '-a or --action.' + "The get_password_data action must be called with " "-a or --action." ) if not instance_id: - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] if kwargs is None: kwargs = {} if instance_id is None: - if 'instance_id' in kwargs: - instance_id = kwargs['instance_id'] - del kwargs['instance_id'] + if "instance_id" in kwargs: + instance_id = kwargs["instance_id"] + del kwargs["instance_id"] - params = {'Action': 'GetPasswordData', - 'InstanceId': instance_id} + params = {"Action": "GetPasswordData", "InstanceId": instance_id} ret = {} - data = aws.query(params, - return_root=True, - location=get_location(), - provider=get_provider(), - opts=__opts__, - sigver='4') + data = aws.query( + params, + return_root=True, + location=get_location(), + provider=get_provider(), + opts=__opts__, + sigver="4", + ) for item in data: ret[next(six.iterkeys(item))] = next(six.itervalues(item)) if not HAS_M2 and not HAS_PYCRYPTO: - if 'key' in kwargs or 'key_file' in kwargs: + if "key" in kwargs or "key_file" in kwargs: log.warn("No crypto library is installed, can not decrypt password") return ret - if 'key' not in kwargs: - if 'key_file' in kwargs: - with salt.utils.files.fopen(kwargs['key_file'], 'r') as kf_: - kwargs['key'] = salt.utils.stringutils.to_unicode(kf_.read()) + if "key" not in kwargs: + if "key_file" in kwargs: + with salt.utils.files.fopen(kwargs["key_file"], "r") as kf_: + kwargs["key"] = salt.utils.stringutils.to_unicode(kf_.read()) - if 'key' in kwargs: - pwdata = ret.get('passwordData', None) + if "key" in kwargs: + pwdata = ret.get("passwordData", None) if pwdata is not None: - rsa_key = kwargs['key'] + rsa_key = kwargs["key"] pwdata = base64.b64decode(pwdata) if HAS_M2: - key = RSA.load_key_string(rsa_key.encode('ascii')) + key = RSA.load_key_string(rsa_key.encode("ascii")) password = key.private_decrypt(pwdata, RSA.pkcs1_padding) else: dsize = Crypto.Hash.SHA.digest_size @@ -4923,13 +4966,13 @@ def get_password_data( key_obj = Crypto.PublicKey.RSA.importKey(rsa_key) key_obj = PKCS1_v1_5.new(key_obj) password = key_obj.decrypt(pwdata, sentinel) - ret['password'] = salt.utils.stringutils.to_unicode(password) + ret["password"] = salt.utils.stringutils.to_unicode(password) return ret def update_pricing(kwargs=None, call=None): - ''' + """ Download most recent pricing information from AWS and convert to a local JSON file. @@ -4941,58 +4984,58 @@ def update_pricing(kwargs=None, call=None): salt-cloud -f update_pricing my-ec2-config type=linux .. versionadded:: 2015.8.0 - ''' + """ sources = { - 'linux': 'https://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js', - 'rhel': 'https://a0.awsstatic.com/pricing/1/ec2/rhel-od.min.js', - 'sles': 'https://a0.awsstatic.com/pricing/1/ec2/sles-od.min.js', - 'mswin': 'https://a0.awsstatic.com/pricing/1/ec2/mswin-od.min.js', - 'mswinsql': 'https://a0.awsstatic.com/pricing/1/ec2/mswinSQL-od.min.js', - 'mswinsqlweb': 'https://a0.awsstatic.com/pricing/1/ec2/mswinSQLWeb-od.min.js', + "linux": "https://a0.awsstatic.com/pricing/1/ec2/linux-od.min.js", + "rhel": "https://a0.awsstatic.com/pricing/1/ec2/rhel-od.min.js", + "sles": "https://a0.awsstatic.com/pricing/1/ec2/sles-od.min.js", + "mswin": "https://a0.awsstatic.com/pricing/1/ec2/mswin-od.min.js", + "mswinsql": "https://a0.awsstatic.com/pricing/1/ec2/mswinSQL-od.min.js", + "mswinsqlweb": "https://a0.awsstatic.com/pricing/1/ec2/mswinSQLWeb-od.min.js", } if kwargs is None: kwargs = {} - if 'type' not in kwargs: + if "type" not in kwargs: for source in sources: _parse_pricing(sources[source], source) else: - _parse_pricing(sources[kwargs['type']], kwargs['type']) + _parse_pricing(sources[kwargs["type"]], kwargs["type"]) def _parse_pricing(url, name): - ''' + """ Download and parse an individual pricing file from AWS .. versionadded:: 2015.8.0 - ''' + """ price_js = http.query(url, text=True) items = [] - current_item = '' + current_item = "" - price_js = re.sub(JS_COMMENT_RE, '', price_js['text']) - price_js = price_js.strip().rstrip(');').lstrip('callback(') + price_js = re.sub(JS_COMMENT_RE, "", price_js["text"]) + price_js = price_js.strip().rstrip(");").lstrip("callback(") for keyword in ( - 'vers', - 'config', - 'rate', - 'valueColumns', - 'currencies', - 'instanceTypes', - 'type', - 'ECU', - 'storageGB', - 'name', - 'vCPU', - 'memoryGiB', - 'storageGiB', - 'USD', + "vers", + "config", + "rate", + "valueColumns", + "currencies", + "instanceTypes", + "type", + "ECU", + "storageGB", + "name", + "vCPU", + "memoryGiB", + "storageGiB", + "USD", ): price_js = price_js.replace(keyword, '"{0}"'.format(keyword)) - for keyword in ('region', 'price', 'size'): + for keyword in ("region", "price", "size"): price_js = price_js.replace(keyword, '"{0}"'.format(keyword)) price_js = price_js.replace('"{0}"s'.format(keyword), '"{0}s"'.format(keyword)) @@ -5001,24 +5044,22 @@ def _parse_pricing(url, name): # Turn the data into something that's easier/faster to process regions = {} price_json = salt.utils.json.loads(price_js) - for region in price_json['config']['regions']: + for region in price_json["config"]["regions"]: sizes = {} - for itype in region['instanceTypes']: - for size in itype['sizes']: - sizes[size['size']] = size - regions[region['region']] = sizes + for itype in region["instanceTypes"]: + for size in itype["sizes"]: + sizes[size["size"]] = size + regions[region["region"]] = sizes - outfile = os.path.join( - __opts__['cachedir'], 'ec2-pricing-{0}.p'.format(name) - ) - with salt.utils.files.fopen(outfile, 'w') as fho: + outfile = os.path.join(__opts__["cachedir"], "ec2-pricing-{0}.p".format(name)) + with salt.utils.files.fopen(outfile, "w") as fho: salt.utils.msgpack.dump(regions, fho) return True def show_pricing(kwargs=None, call=None): - ''' + """ Show pricing for a particular profile. This is only an estimate, based on unofficial pricing sources. @@ -5037,82 +5078,81 @@ def show_pricing(kwargs=None, call=None): salt-cloud -f update_pricing .. versionadded:: 2015.8.0 - ''' - profile = __opts__['profiles'].get(kwargs['profile'], {}) + """ + profile = __opts__["profiles"].get(kwargs["profile"], {}) if not profile: - return {'Error': 'The requested profile was not found'} + return {"Error": "The requested profile was not found"} # Make sure the profile belongs to ec2 - provider = profile.get('provider', '0:0') - comps = provider.split(':') - if len(comps) < 2 or comps[1] != 'ec2': - return {'Error': 'The requested profile does not belong to EC2'} + provider = profile.get("provider", "0:0") + comps = provider.split(":") + if len(comps) < 2 or comps[1] != "ec2": + return {"Error": "The requested profile does not belong to EC2"} - image_id = profile.get('image', None) - image_dict = show_image({'image': image_id}, 'function') + image_id = profile.get("image", None) + image_dict = show_image({"image": image_id}, "function") image_info = image_dict[0] # Find out what platform it is - if image_info.get('imageOwnerAlias', '') == 'amazon': - if image_info.get('platform', '') == 'windows': - image_description = image_info.get('description', '') - if 'sql' in image_description.lower(): - if 'web' in image_description.lower(): - name = 'mswinsqlweb' + if image_info.get("imageOwnerAlias", "") == "amazon": + if image_info.get("platform", "") == "windows": + image_description = image_info.get("description", "") + if "sql" in image_description.lower(): + if "web" in image_description.lower(): + name = "mswinsqlweb" else: - name = 'mswinsql' + name = "mswinsql" else: - name = 'mswin' - elif image_info.get('imageLocation', '').strip().startswith('amazon/suse'): - name = 'sles' + name = "mswin" + elif image_info.get("imageLocation", "").strip().startswith("amazon/suse"): + name = "sles" else: - name = 'linux' - elif image_info.get('imageOwnerId', '') == '309956199498': - name = 'rhel' + name = "linux" + elif image_info.get("imageOwnerId", "") == "309956199498": + name = "rhel" else: - name = 'linux' + name = "linux" - pricefile = os.path.join( - __opts__['cachedir'], 'ec2-pricing-{0}.p'.format(name) - ) + pricefile = os.path.join(__opts__["cachedir"], "ec2-pricing-{0}.p".format(name)) if not os.path.isfile(pricefile): - update_pricing({'type': name}, 'function') + update_pricing({"type": name}, "function") - with salt.utils.files.fopen(pricefile, 'r') as fhi: - ec2_price = salt.utils.stringutils.to_unicode( - salt.utils.msgpack.load(fhi)) + with salt.utils.files.fopen(pricefile, "r") as fhi: + ec2_price = salt.utils.stringutils.to_unicode(salt.utils.msgpack.load(fhi)) region = get_location(profile) - size = profile.get('size', None) + size = profile.get("size", None) if size is None: - return {'Error': 'The requested profile does not contain a size'} + return {"Error": "The requested profile does not contain a size"} try: raw = ec2_price[region][size] except KeyError: - return {'Error': 'The size ({0}) in the requested profile does not have ' - 'a price associated with it for the {1} region'.format(size, region)} + return { + "Error": "The size ({0}) in the requested profile does not have " + "a price associated with it for the {1} region".format(size, region) + } ret = {} - if kwargs.get('raw', False): - ret['_raw'] = raw + if kwargs.get("raw", False): + ret["_raw"] = raw - ret['per_hour'] = 0 - for col in raw.get('valueColumns', []): - ret['per_hour'] += decimal.Decimal(col['prices'].get('USD', 0)) + ret["per_hour"] = 0 + for col in raw.get("valueColumns", []): + ret["per_hour"] += decimal.Decimal(col["prices"].get("USD", 0)) - ret['per_hour'] = decimal.Decimal(ret['per_hour']) - ret['per_day'] = ret['per_hour'] * 24 - ret['per_week'] = ret['per_day'] * 7 - ret['per_month'] = ret['per_day'] * 30 - ret['per_year'] = ret['per_week'] * 52 + ret["per_hour"] = decimal.Decimal(ret["per_hour"]) + ret["per_day"] = ret["per_hour"] * 24 + ret["per_week"] = ret["per_day"] * 7 + ret["per_month"] = ret["per_day"] * 30 + ret["per_year"] = ret["per_week"] * 52 - return {profile['profile']: ret} + return {profile["profile"]: ret} def ssm_create_association(name=None, kwargs=None, instance_id=None, call=None): - ''' + """ Associates the specified SSM document with the specified instance http://docs.aws.amazon.com/ssm/latest/APIReference/API_CreateAssociation.html @@ -5122,48 +5162,51 @@ def ssm_create_association(name=None, kwargs=None, instance_id=None, call=None): .. code-block:: bash salt-cloud -a ssm_create_association ec2-instance-name ssm_document=ssm-document-name - ''' + """ - if call != 'action': + if call != "action": raise SaltCloudSystemExit( - 'The ssm_create_association action must be called with ' - '-a or --action.' + "The ssm_create_association action must be called with " "-a or --action." ) if not kwargs: kwargs = {} - if 'instance_id' in kwargs: - instance_id = kwargs['instance_id'] + if "instance_id" in kwargs: + instance_id = kwargs["instance_id"] if name and not instance_id: - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] if not name and not instance_id: - log.error('Either a name or an instance_id is required.') + log.error("Either a name or an instance_id is required.") return False - if 'ssm_document' not in kwargs: - log.error('A ssm_document is required.') + if "ssm_document" not in kwargs: + log.error("A ssm_document is required.") return False - params = {'Action': 'CreateAssociation', - 'InstanceId': instance_id, - 'Name': kwargs['ssm_document']} + params = { + "Action": "CreateAssociation", + "InstanceId": instance_id, + "Name": kwargs["ssm_document"], + } - result = aws.query(params, - return_root=True, - location=get_location(), - provider=get_provider(), - product='ssm', - opts=__opts__, - sigver='4') + result = aws.query( + params, + return_root=True, + location=get_location(), + provider=get_provider(), + product="ssm", + opts=__opts__, + sigver="4", + ) log.info(result) return result def ssm_describe_association(name=None, kwargs=None, instance_id=None, call=None): - ''' + """ Describes the associations for the specified SSM document or instance. http://docs.aws.amazon.com/ssm/latest/APIReference/API_DescribeAssociation.html @@ -5173,40 +5216,43 @@ def ssm_describe_association(name=None, kwargs=None, instance_id=None, call=None .. code-block:: bash salt-cloud -a ssm_describe_association ec2-instance-name ssm_document=ssm-document-name - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The ssm_describe_association action must be called with ' - '-a or --action.' + "The ssm_describe_association action must be called with " "-a or --action." ) if not kwargs: kwargs = {} - if 'instance_id' in kwargs: - instance_id = kwargs['instance_id'] + if "instance_id" in kwargs: + instance_id = kwargs["instance_id"] if name and not instance_id: - instance_id = _get_node(name)['instanceId'] + instance_id = _get_node(name)["instanceId"] if not name and not instance_id: - log.error('Either a name or an instance_id is required.') + log.error("Either a name or an instance_id is required.") return False - if 'ssm_document' not in kwargs: - log.error('A ssm_document is required.') + if "ssm_document" not in kwargs: + log.error("A ssm_document is required.") return False - params = {'Action': 'DescribeAssociation', - 'InstanceId': instance_id, - 'Name': kwargs['ssm_document']} + params = { + "Action": "DescribeAssociation", + "InstanceId": instance_id, + "Name": kwargs["ssm_document"], + } - result = aws.query(params, - return_root=True, - location=get_location(), - provider=get_provider(), - product='ssm', - opts=__opts__, - sigver='4') + result = aws.query( + params, + return_root=True, + location=get_location(), + provider=get_provider(), + product="ssm", + opts=__opts__, + sigver="4", + ) log.info(result) return result diff --git a/salt/cloud/clouds/gce.py b/salt/cloud/clouds/gce.py index 30d929d6ed9..821a2fb014a 100644 --- a/salt/cloud/clouds/gce.py +++ b/salt/cloud/clouds/gce.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Copyright 2013 Google Inc. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); @@ -43,17 +43,30 @@ Example Provider Configuration :maintainer: Eric Johnson :maintainer: Russell Tolle :depends: libcloud >= 1.0.0 -''' +""" # pylint: disable=invalid-name,function-redefined # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys -import re -import pprint + import logging +import os +import pprint +import re +import sys from ast import literal_eval + +import salt.config as config +import salt.utils.cloud +import salt.utils.files +import salt.utils.http +import salt.utils.msgpack +from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import +from salt.exceptions import SaltCloudSystemExit +from salt.ext import six + +# Import salt libs +from salt.utils.functools import namespaced_function from salt.utils.versions import LooseVersion as _LooseVersion # Import 3rd-party libs @@ -68,43 +81,33 @@ try: from libcloud.common.google import ( ResourceInUseError, ResourceNotFoundError, - ) + ) + # This work-around for Issue #32743 is no longer needed for libcloud >= # 1.4.0. However, older versions of libcloud must still be supported with # this work-around. This work-around can be removed when the required # minimum version of libcloud is 2.0.0 (See PR #40837 - which is # implemented in Salt 2018.3.0). - if _LooseVersion(libcloud.__version__) < _LooseVersion('1.4.0'): + if _LooseVersion(libcloud.__version__) < _LooseVersion("1.4.0"): # See https://github.com/saltstack/salt/issues/32743 import libcloud.security - libcloud.security.CA_CERTS_PATH.append('/etc/ssl/certs/YaST-CA.pem') + + libcloud.security.CA_CERTS_PATH.append("/etc/ssl/certs/YaST-CA.pem") HAS_LIBCLOUD = True except ImportError: LIBCLOUD_IMPORT_ERROR = sys.exc_info() HAS_LIBCLOUD = False # pylint: enable=import-error -# Import salt libs -from salt.utils.functools import namespaced_function -from salt.ext import six -import salt.utils.cloud -import salt.utils.files -import salt.utils.http -import salt.utils.msgpack -import salt.config as config -from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import -from salt.exceptions import ( - SaltCloudSystemExit, -) # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'gce' +__virtualname__ = "gce" # custom UA -_UA_PRODUCT = 'salt-cloud' -_UA_VERSION = '0.2.0' +_UA_PRODUCT = "salt-cloud" +_UA_VERSION = "0.2.0" # Redirect GCE functions to this module namespace avail_locations = namespaced_function(avail_locations, globals()) @@ -114,214 +117,207 @@ list_nodes = namespaced_function(list_nodes, globals()) list_nodes_full = namespaced_function(list_nodes_full, globals()) list_nodes_select = namespaced_function(list_nodes_select, globals()) -GCE_VM_NAME_REGEX = re.compile(r'^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$') +GCE_VM_NAME_REGEX = re.compile(r"^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$") # Only load in this module if the GCE configurations are in place def __virtual__(): - ''' + """ Set up the libcloud functions and check for GCE configurations. - ''' + """ if get_configured_provider() is False: return False if get_dependencies() is False: return False - for provider, details in six.iteritems(__opts__['providers']): - if 'gce' not in details: + for provider, details in six.iteritems(__opts__["providers"]): + if "gce" not in details: continue - parameters = details['gce'] - pathname = os.path.expanduser(parameters['service_account_private_key']) + parameters = details["gce"] + pathname = os.path.expanduser(parameters["service_account_private_key"]) # empty pathname will tell libcloud to use instance credentials - if pathname and salt.utils.cloud.check_key_path_and_mode( - provider, pathname - ) is False: + if ( + pathname + and salt.utils.cloud.check_key_path_and_mode(provider, pathname) is False + ): return False return __virtualname__ def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, - __active_provider_name__ or 'gce', - ('project', - 'service_account_email_address', - 'service_account_private_key') + __active_provider_name__ or "gce", + ("project", "service_account_email_address", "service_account_private_key"), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' + """ if LIBCLOUD_IMPORT_ERROR: log.error("Failure when importing LibCloud: ", exc_info=LIBCLOUD_IMPORT_ERROR) - log.error("Note: The libcloud dependency is called 'apache-libcloud' on PyPi/pip.") - return config.check_driver_dependencies( - __virtualname__, - {'libcloud': HAS_LIBCLOUD} - ) + log.error( + "Note: The libcloud dependency is called 'apache-libcloud' on PyPi/pip." + ) + return config.check_driver_dependencies(__virtualname__, {"libcloud": HAS_LIBCLOUD}) def get_lb_conn(gce_driver=None): - ''' + """ Return a load-balancer conn object - ''' + """ if not gce_driver: - raise SaltCloudSystemExit( - 'Missing gce_driver for get_lb_conn method.' - ) + raise SaltCloudSystemExit("Missing gce_driver for get_lb_conn method.") return get_driver_lb(Provider_lb.GCE)(gce_driver=gce_driver) def get_conn(): - ''' + """ Return a conn object for the passed VM data - ''' + """ driver = get_driver(Provider.GCE) provider = get_configured_provider() - project = config.get_cloud_config_value('project', provider, __opts__) + project = config.get_cloud_config_value("project", provider, __opts__) email = config.get_cloud_config_value( - 'service_account_email_address', - provider, - __opts__) + "service_account_email_address", provider, __opts__ + ) private_key = config.get_cloud_config_value( - 'service_account_private_key', - provider, - __opts__) + "service_account_private_key", provider, __opts__ + ) gce = driver(email, private_key, project=project) - gce.connection.user_agent_append('{0}/{1}'.format(_UA_PRODUCT, - _UA_VERSION)) + gce.connection.user_agent_append("{0}/{1}".format(_UA_PRODUCT, _UA_VERSION)) return gce def _expand_item(item): - ''' + """ Convert the libcloud object into something more serializable. - ''' + """ ret = {} ret.update(item.__dict__) return ret def _expand_node(node): - ''' + """ Convert the libcloud Node object into something more serializable. - ''' + """ ret = {} ret.update(node.__dict__) try: - del ret['extra']['boot_disk'] + del ret["extra"]["boot_disk"] except Exception: # pylint: disable=W0703 pass - zone = ret['extra']['zone'] - ret['extra']['zone'] = {} - ret['extra']['zone'].update(zone.__dict__) + zone = ret["extra"]["zone"] + ret["extra"]["zone"] = {} + ret["extra"]["zone"].update(zone.__dict__) # Remove unserializable GCENodeDriver objects - if 'driver' in ret: - del ret['driver'] - if 'driver' in ret['extra']['zone']: - del ret['extra']['zone']['driver'] + if "driver" in ret: + del ret["driver"] + if "driver" in ret["extra"]["zone"]: + del ret["extra"]["zone"]["driver"] return ret def _expand_disk(disk): - ''' + """ Convert the libcloud Volume object into something more serializable. - ''' + """ ret = {} ret.update(disk.__dict__) - zone = ret['extra']['zone'] - ret['extra']['zone'] = {} - ret['extra']['zone'].update(zone.__dict__) + zone = ret["extra"]["zone"] + ret["extra"]["zone"] = {} + ret["extra"]["zone"].update(zone.__dict__) return ret def _expand_address(addy): - ''' + """ Convert the libcloud GCEAddress object into something more serializable. - ''' + """ ret = {} ret.update(addy.__dict__) - ret['extra']['zone'] = addy.region.name + ret["extra"]["zone"] = addy.region.name return ret def _expand_balancer(lb): - ''' + """ Convert the libcloud load-balancer object into something more serializable. - ''' + """ ret = {} ret.update(lb.__dict__) - hc = ret['extra']['healthchecks'] - ret['extra']['healthchecks'] = [] + hc = ret["extra"]["healthchecks"] + ret["extra"]["healthchecks"] = [] for item in hc: - ret['extra']['healthchecks'].append(_expand_item(item)) + ret["extra"]["healthchecks"].append(_expand_item(item)) - fwr = ret['extra']['forwarding_rule'] - tp = ret['extra']['forwarding_rule'].targetpool - reg = ret['extra']['forwarding_rule'].region - ret['extra']['forwarding_rule'] = {} - ret['extra']['forwarding_rule'].update(fwr.__dict__) - ret['extra']['forwarding_rule']['targetpool'] = tp.name - ret['extra']['forwarding_rule']['region'] = reg.name + fwr = ret["extra"]["forwarding_rule"] + tp = ret["extra"]["forwarding_rule"].targetpool + reg = ret["extra"]["forwarding_rule"].region + ret["extra"]["forwarding_rule"] = {} + ret["extra"]["forwarding_rule"].update(fwr.__dict__) + ret["extra"]["forwarding_rule"]["targetpool"] = tp.name + ret["extra"]["forwarding_rule"]["region"] = reg.name - tp = ret['extra']['targetpool'] - hc = ret['extra']['targetpool'].healthchecks - nodes = ret['extra']['targetpool'].nodes - region = ret['extra']['targetpool'].region - zones = ret['extra']['targetpool'].region.zones + tp = ret["extra"]["targetpool"] + hc = ret["extra"]["targetpool"].healthchecks + nodes = ret["extra"]["targetpool"].nodes + region = ret["extra"]["targetpool"].region + zones = ret["extra"]["targetpool"].region.zones - ret['extra']['targetpool'] = {} - ret['extra']['targetpool'].update(tp.__dict__) - ret['extra']['targetpool']['region'] = _expand_item(region) - ret['extra']['targetpool']['nodes'] = [] + ret["extra"]["targetpool"] = {} + ret["extra"]["targetpool"].update(tp.__dict__) + ret["extra"]["targetpool"]["region"] = _expand_item(region) + ret["extra"]["targetpool"]["nodes"] = [] for n in nodes: - ret['extra']['targetpool']['nodes'].append(_expand_node(n)) - ret['extra']['targetpool']['healthchecks'] = [] + ret["extra"]["targetpool"]["nodes"].append(_expand_node(n)) + ret["extra"]["targetpool"]["healthchecks"] = [] for hci in hc: - ret['extra']['targetpool']['healthchecks'].append(hci.name) - ret['extra']['targetpool']['region']['zones'] = [] + ret["extra"]["targetpool"]["healthchecks"].append(hci.name) + ret["extra"]["targetpool"]["region"]["zones"] = [] for z in zones: - ret['extra']['targetpool']['region']['zones'].append(z.name) + ret["extra"]["targetpool"]["region"]["zones"].append(z.name) return ret def show_instance(vm_name, call=None): - ''' + """ Show the details of the existing instance. - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) conn = get_conn() node = _expand_node(conn.ex_get_node(vm_name)) - __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](node, __active_provider_name__, __opts__) return node def avail_sizes(conn=None): - ''' + """ Return a dict of available instances sizes (a.k.a machine types) and convert them to something more serializable. - ''' + """ if not conn: conn = get_conn() - raw_sizes = conn.list_sizes('all') # get *all* the machine types! + raw_sizes = conn.list_sizes("all") # get *all* the machine types! sizes = [] for size in raw_sizes: - zone = size.extra['zone'] - size.extra['zone'] = {} - size.extra['zone'].update(zone.__dict__) + zone = size.extra["zone"] + size.extra["zone"] = {} + size.extra["zone"].update(zone.__dict__) mtype = {} mtype.update(size.__dict__) sizes.append(mtype) @@ -329,7 +325,7 @@ def avail_sizes(conn=None): def avail_images(conn=None): - ''' + """ Return a dict of all available VM images on the cloud provider with relevant data. @@ -340,7 +336,7 @@ def avail_images(conn=None): https://cloud.google.com/compute/docs/operating-systems/ If image names overlap, the image in the current project is used. - ''' + """ if not conn: conn = get_conn() @@ -349,9 +345,15 @@ def avail_images(conn=None): # % gcloud compute images list # and looking at the "PROJECT" column in the output. public_image_projects = ( - 'centos-cloud', 'coreos-cloud', 'debian-cloud', 'google-containers', - 'opensuse-cloud', 'rhel-cloud', 'suse-cloud', 'ubuntu-os-cloud', - 'windows-cloud' + "centos-cloud", + "coreos-cloud", + "debian-cloud", + "google-containers", + "opensuse-cloud", + "rhel-cloud", + "suse-cloud", + "ubuntu-os-cloud", + "windows-cloud", ) for project in public_image_projects: all_images.extend(conn.list_images(project)) @@ -364,47 +366,48 @@ def avail_images(conn=None): for img in all_images: ret[img.name] = {} for attr in dir(img): - if attr.startswith('_'): + if attr.startswith("_"): continue ret[img.name][attr] = getattr(img, attr) return ret def __get_image(conn, vm_): - ''' + """ The get_image for GCE allows partial name matching and returns a libcloud object. - ''' + """ img = config.get_cloud_config_value( - 'image', vm_, __opts__, default='debian-7', search_global=False) + "image", vm_, __opts__, default="debian-7", search_global=False + ) return conn.ex_get_image(img) def __get_location(conn, vm_): - ''' + """ Need to override libcloud to find the zone. - ''' - location = config.get_cloud_config_value( - 'location', vm_, __opts__) + """ + location = config.get_cloud_config_value("location", vm_, __opts__) return conn.ex_get_zone(location) def __get_size(conn, vm_): - ''' + """ Need to override libcloud to find the machine type in the proper zone. - ''' + """ size = config.get_cloud_config_value( - 'size', vm_, __opts__, default='n1-standard-1', search_global=False) + "size", vm_, __opts__, default="n1-standard-1", search_global=False + ) return conn.ex_get_size(size, __get_location(conn, vm_)) def __get_tags(vm_): - ''' + """ Get configured tags. - ''' + """ t = config.get_cloud_config_value( - 'tags', vm_, __opts__, - default='[]', search_global=False) + "tags", vm_, __opts__, default="[]", search_global=False + ) # Consider warning the user that the tags in the cloud profile # could not be interpreted, bad formatting? try: @@ -417,12 +420,12 @@ def __get_tags(vm_): def __get_metadata(vm_): - ''' + """ Get configured metadata and add 'salt-cloud-profile'. - ''' + """ md = config.get_cloud_config_value( - 'metadata', vm_, __opts__, - default='{}', search_global=False) + "metadata", vm_, __opts__, default="{}", search_global=False + ) # Consider warning the user that the metadata in the cloud profile # could not be interpreted, bad formatting? try: @@ -430,29 +433,26 @@ def __get_metadata(vm_): except Exception: # pylint: disable=W0703 metadata = None if not metadata or not isinstance(metadata, dict): - metadata = {'items': [{ - 'key': 'salt-cloud-profile', - 'value': vm_['profile'] - }]} + metadata = {"items": [{"key": "salt-cloud-profile", "value": vm_["profile"]}]} else: - metadata['salt-cloud-profile'] = vm_['profile'] + metadata["salt-cloud-profile"] = vm_["profile"] items = [] for k, v in six.iteritems(metadata): - items.append({'key': k, 'value': v}) - metadata = {'items': items} + items.append({"key": k, "value": v}) + metadata = {"items": items} return metadata def __get_host(node, vm_): - ''' + """ Return public IP, private IP, or hostname for the libcloud 'node' object - ''' - if __get_ssh_interface(vm_) == 'private_ips' or vm_['external_ip'] is None: + """ + if __get_ssh_interface(vm_) == "private_ips" or vm_["external_ip"] is None: ip_address = node.private_ips[0] - log.info('Salt node data. Private_ip: %s', ip_address) + log.info("Salt node data. Private_ip: %s", ip_address) else: ip_address = node.public_ips[0] - log.info('Salt node data. Public_ip: %s', ip_address) + log.info("Salt node data. Public_ip: %s", ip_address) if ip_address: return ip_address @@ -461,69 +461,65 @@ def __get_host(node, vm_): def __get_network(conn, vm_): - ''' + """ Return a GCE libcloud network object with matching name - ''' + """ network = config.get_cloud_config_value( - 'network', vm_, __opts__, - default='default', search_global=False) + "network", vm_, __opts__, default="default", search_global=False + ) return conn.ex_get_network(network) def __get_subnetwork(vm_): - ''' + """ Get configured subnetwork. - ''' + """ ex_subnetwork = config.get_cloud_config_value( - 'subnetwork', vm_, __opts__, - search_global=False) + "subnetwork", vm_, __opts__, search_global=False + ) return ex_subnetwork def __get_region(conn, vm_): - ''' + """ Return a GCE libcloud region object with matching name. - ''' + """ location = __get_location(conn, vm_) - region = '-'.join(location.name.split('-')[:2]) + region = "-".join(location.name.split("-")[:2]) return conn.ex_get_region(region) def __get_ssh_interface(vm_): - ''' + """ Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. - ''' + """ return config.get_cloud_config_value( - 'ssh_interface', vm_, __opts__, default='public_ips', - search_global=False + "ssh_interface", vm_, __opts__, default="public_ips", search_global=False ) def __create_orget_address(conn, name, region): - ''' + """ Reuse or create a static IP address. Returns a native GCEAddress construct to use with libcloud. - ''' + """ try: addy = conn.ex_get_address(name, region) except ResourceNotFoundError: # pylint: disable=W0703 - addr_kwargs = { - 'name': name, - 'region': region - } + addr_kwargs = {"name": name, "region": region} new_addy = create_address(addr_kwargs, "function") - addy = conn.ex_get_address(new_addy['name'], new_addy['region']) + addy = conn.ex_get_address(new_addy["name"], new_addy["region"]) return addy def _parse_allow(allow): - ''' + """ Convert firewall rule allowed user-string to specified REST API format. - ''' + """ # input=> tcp:53,tcp:80,tcp:443,icmp,tcp:4201,udp:53 # output<= [ # {"IPProtocol": "tcp", "ports": ["53","80","443","4201"]}, @@ -532,16 +528,14 @@ def _parse_allow(allow): # ] seen_protos = {} allow_dict = [] - protocols = allow.split(',') + protocols = allow.split(",") for p in protocols: - pairs = p.split(':') - if pairs[0].lower() not in ['tcp', 'udp', 'icmp']: + pairs = p.split(":") + if pairs[0].lower() not in ["tcp", "udp", "icmp"]: raise SaltCloudSystemExit( - 'Unsupported protocol {0}. Must be tcp, udp, or icmp.'.format( - pairs[0] - ) + "Unsupported protocol {0}. Must be tcp, udp, or icmp.".format(pairs[0]) ) - if len(pairs) == 1 or pairs[0].lower() == 'icmp': + if len(pairs) == 1 or pairs[0].lower() == "icmp": seen_protos[pairs[0]] = [] else: if pairs[0] not in seen_protos: @@ -549,28 +543,32 @@ def _parse_allow(allow): else: seen_protos[pairs[0]].append(pairs[1]) for k in seen_protos: - d = {'IPProtocol': k} + d = {"IPProtocol": k} if seen_protos[k]: - d['ports'] = seen_protos[k] + d["ports"] = seen_protos[k] allow_dict.append(d) log.debug("firewall allowed protocols/ports: %s", allow_dict) return allow_dict def __get_ssh_credentials(vm_): - ''' + """ Get configured SSH credentials. - ''' + """ ssh_user = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, default=os.getenv('USER')) + "ssh_username", vm_, __opts__, default=os.getenv("USER") + ) ssh_key = config.get_cloud_config_value( - 'ssh_keyfile', vm_, __opts__, - default=os.path.expanduser('~/.ssh/google_compute_engine')) + "ssh_keyfile", + vm_, + __opts__, + default=os.path.expanduser("~/.ssh/google_compute_engine"), + ) return ssh_user, ssh_key def create_network(kwargs=None, call=None): - ''' + """ ... versionchanged:: 2017.7.0 Create a GCE network. Must specify name and cidr. @@ -580,64 +578,52 @@ def create_network(kwargs=None, call=None): salt-cloud -f create_network gce name=mynet cidr=10.10.10.0/24 mode=legacy description=optional salt-cloud -f create_network gce name=mynet description=optional - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_network function must be called with -f or --function.' + "The create_network function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when creating a network.") + return False + + mode = kwargs.get("mode", "legacy") + cidr = kwargs.get("cidr", None) + if cidr is None and mode == "legacy": log.error( - 'A name must be specified when creating a network.' + "A network CIDR range must be specified when creating a legacy network." ) return False - mode = kwargs.get('mode', 'legacy') - cidr = kwargs.get('cidr', None) - if cidr is None and mode == 'legacy': - log.error( - 'A network CIDR range must be specified when creating a legacy network.' - ) - return False - - name = kwargs['name'] - desc = kwargs.get('description', None) + name = kwargs["name"] + desc = kwargs.get("description", None) conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'creating network', - 'salt/cloud/net/creating', - args={ - 'name': name, - 'cidr': cidr, - 'description': desc, - 'mode': mode - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "creating network", + "salt/cloud/net/creating", + args={"name": name, "cidr": cidr, "description": desc, "mode": mode}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) network = conn.ex_create_network(name, cidr, desc, mode) - __utils__['cloud.fire_event']( - 'event', - 'created network', - 'salt/cloud/net/created', - args={ - 'name': name, - 'cidr': cidr, - 'description': desc, - 'mode': mode - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created network", + "salt/cloud/net/created", + args={"name": name, "cidr": cidr, "description": desc, "mode": mode}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return _expand_item(network) def delete_network(kwargs=None, call=None): - ''' + """ Permanently delete a network. CLI Example: @@ -645,58 +631,52 @@ def delete_network(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_network gce name=mynet - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_network function must be called with -f or --function.' + "The delete_network function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when deleting a network.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when deleting a network.") return False - name = kwargs['name'] + name = kwargs["name"] conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'deleting network', - 'salt/cloud/net/deleting', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleting network", + "salt/cloud/net/deleting", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: - result = conn.ex_destroy_network( - conn.ex_get_network(name) - ) + result = conn.ex_destroy_network(conn.ex_get_network(name)) except ResourceNotFoundError as exc: log.error( - 'Nework %s was not found. Exception was: %s', - name, exc, exc_info_on_loglevel=logging.DEBUG + "Nework %s was not found. Exception was: %s", + name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - __utils__['cloud.fire_event']( - 'event', - 'deleted network', - 'salt/cloud/net/deleted', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleted network", + "salt/cloud/net/deleted", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def show_network(kwargs=None, call=None): - ''' + """ Show the details of an existing network. CLI Example: @@ -704,23 +684,21 @@ def show_network(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_network gce name=mynet - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_network function must be called with -f or --function.' - ) - if not kwargs or 'name' not in kwargs: - log.error( - 'Must specify name of network.' + "The show_network function must be called with -f or --function." ) + if not kwargs or "name" not in kwargs: + log.error("Must specify name of network.") return False conn = get_conn() - return _expand_item(conn.ex_get_network(kwargs['name'])) + return _expand_item(conn.ex_get_network(kwargs["name"])) def create_subnetwork(kwargs=None, call=None): - ''' + """ ... versionadded:: 2017.7.0 Create a GCE Subnetwork. Must specify name, cidr, network, and region. @@ -729,80 +707,72 @@ def create_subnetwork(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_subnetwork gce name=mysubnet network=mynet1 region=us-west1 cidr=10.0.0.0/24 description=optional - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_subnetwork function must be called with -f or --function.' + "The create_subnetwork function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'Must specify name of subnet.' - ) + if not kwargs or "name" not in kwargs: + log.error("Must specify name of subnet.") return False - if 'network' not in kwargs: - log.errror( - 'Must specify name of network to create subnet under.' - ) + if "network" not in kwargs: + log.errror("Must specify name of network to create subnet under.") return False - if 'cidr' not in kwargs: - log.errror( - 'A network CIDR range must be specified when creating a subnet.' - ) + if "cidr" not in kwargs: + log.errror("A network CIDR range must be specified when creating a subnet.") return False - if 'region' not in kwargs: - log.error( - 'A region must be specified when creating a subnetwork.' - ) + if "region" not in kwargs: + log.error("A region must be specified when creating a subnetwork.") return False - name = kwargs['name'] - cidr = kwargs['cidr'] - network = kwargs['network'] - region = kwargs['region'] - desc = kwargs.get('description', None) + name = kwargs["name"] + cidr = kwargs["cidr"] + network = kwargs["network"] + region = kwargs["region"] + desc = kwargs.get("description", None) conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'create subnetwork', - 'salt/cloud/subnet/creating', + __utils__["cloud.fire_event"]( + "event", + "create subnetwork", + "salt/cloud/subnet/creating", args={ - 'name': name, - 'network': network, - 'cidr': cidr, - 'region': region, - 'description': desc + "name": name, + "network": network, + "cidr": cidr, + "region": region, + "description": desc, }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) subnet = conn.ex_create_subnetwork(name, cidr, network, region, desc) - __utils__['cloud.fire_event']( - 'event', - 'created subnetwork', - 'salt/cloud/subnet/created', + __utils__["cloud.fire_event"]( + "event", + "created subnetwork", + "salt/cloud/subnet/created", args={ - 'name': name, - 'network': network, - 'cidr': cidr, - 'region': region, - 'description': desc + "name": name, + "network": network, + "cidr": cidr, + "region": region, + "description": desc, }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return _expand_item(subnet) def delete_subnetwork(kwargs=None, call=None): - ''' + """ ... versionadded:: 2017.7.0 Delete a GCE Subnetwork. Must specify name and region. @@ -811,65 +781,57 @@ def delete_subnetwork(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_subnetwork gce name=mysubnet network=mynet1 region=us-west1 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_subnet function must be called with -f or --function.' + "The delete_subnet function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'Must specify name of subnet.' - ) + if not kwargs or "name" not in kwargs: + log.error("Must specify name of subnet.") return False - if 'region' not in kwargs: - log.error( - 'Must specify region of subnet.' - ) + if "region" not in kwargs: + log.error("Must specify region of subnet.") return False - name = kwargs['name'] - region = kwargs['region'] + name = kwargs["name"] + region = kwargs["region"] conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'deleting subnetwork', - 'salt/cloud/subnet/deleting', - args={ - 'name': name, - 'region': region - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleting subnetwork", + "salt/cloud/subnet/deleting", + args={"name": name, "region": region}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: result = conn.ex_destroy_subnetwork(name, region) except ResourceNotFoundError as exc: log.error( - 'Subnetwork %s was not found. Exception was: %s', - name, exc, exc_info_on_loglevel=logging.DEBUG + "Subnetwork %s was not found. Exception was: %s", + name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - __utils__['cloud.fire_event']( - 'event', - 'deleted subnetwork', - 'salt/cloud/subnet/deleted', - args={ - 'name': name, - 'region': region - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleted subnetwork", + "salt/cloud/subnet/deleted", + args={"name": name, "region": region}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def show_subnetwork(kwargs=None, call=None): - ''' + """ ... versionadded:: 2017.7.0 Show details of an existing GCE Subnetwork. Must specify name and region. @@ -879,32 +841,28 @@ def show_subnetwork(kwargs=None, call=None): salt-cloud -f show_subnetwork gce name=mysubnet region=us-west1 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_subnetwork function must be called with -f or --function.' + "The show_subnetwork function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'Must specify name of subnet.' - ) + if not kwargs or "name" not in kwargs: + log.error("Must specify name of subnet.") return False - if 'region' not in kwargs: - log.error( - 'Must specify region of subnet.' - ) + if "region" not in kwargs: + log.error("Must specify region of subnet.") return False - name = kwargs['name'] - region = kwargs['region'] + name = kwargs["name"] + region = kwargs["region"] conn = get_conn() return _expand_item(conn.ex_get_subnetwork(name, region)) def create_fwrule(kwargs=None, call=None): - ''' + """ Create a GCE firewall rule. The 'default' network is used if not specified. CLI Example: @@ -912,76 +870,65 @@ def create_fwrule(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_fwrule gce name=allow-http allow=tcp:80 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_fwrule function must be called with -f or --function.' + "The create_fwrule function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when creating a firewall rule.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when creating a firewall rule.") return False - if 'allow' not in kwargs: - log.error( - 'Must use "allow" to specify allowed protocols/ports.' - ) + if "allow" not in kwargs: + log.error('Must use "allow" to specify allowed protocols/ports.') return False - name = kwargs['name'] - network_name = kwargs.get('network', 'default') - allow = _parse_allow(kwargs['allow']) - src_range = kwargs.get('src_range', '0.0.0.0/0') - src_tags = kwargs.get('src_tags', None) - dst_tags = kwargs.get('dst_tags', None) + name = kwargs["name"] + network_name = kwargs.get("network", "default") + allow = _parse_allow(kwargs["allow"]) + src_range = kwargs.get("src_range", "0.0.0.0/0") + src_tags = kwargs.get("src_tags", None) + dst_tags = kwargs.get("dst_tags", None) if src_range: - src_range = src_range.split(',') + src_range = src_range.split(",") if src_tags: - src_tags = src_tags.split(',') + src_tags = src_tags.split(",") if dst_tags: - dst_tags = dst_tags.split(',') + dst_tags = dst_tags.split(",") conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'create firewall', - 'salt/cloud/firewall/creating', - args={ - 'name': name, - 'network': network_name, - 'allow': kwargs['allow'], - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "create firewall", + "salt/cloud/firewall/creating", + args={"name": name, "network": network_name, "allow": kwargs["allow"]}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) fwrule = conn.ex_create_firewall( - name, allow, + name, + allow, network=network_name, source_ranges=src_range, source_tags=src_tags, - target_tags=dst_tags + target_tags=dst_tags, ) - __utils__['cloud.fire_event']( - 'event', - 'created firewall', - 'salt/cloud/firewall/created', - args={ - 'name': name, - 'network': network_name, - 'allow': kwargs['allow'], - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created firewall", + "salt/cloud/firewall/created", + args={"name": name, "network": network_name, "allow": kwargs["allow"]}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return _expand_item(fwrule) def delete_fwrule(kwargs=None, call=None): - ''' + """ Permanently delete a firewall rule. CLI Example: @@ -989,58 +936,52 @@ def delete_fwrule(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_fwrule gce name=allow-http - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_fwrule function must be called with -f or --function.' + "The delete_fwrule function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when deleting a firewall rule.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when deleting a firewall rule.") return False - name = kwargs['name'] + name = kwargs["name"] conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'delete firewall', - 'salt/cloud/firewall/deleting', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "delete firewall", + "salt/cloud/firewall/deleting", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: - result = conn.ex_destroy_firewall( - conn.ex_get_firewall(name) - ) + result = conn.ex_destroy_firewall(conn.ex_get_firewall(name)) except ResourceNotFoundError as exc: log.error( - 'Rule %s was not found. Exception was: %s', - name, exc, exc_info_on_loglevel=logging.DEBUG + "Rule %s was not found. Exception was: %s", + name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - __utils__['cloud.fire_event']( - 'event', - 'deleted firewall', - 'salt/cloud/firewall/deleted', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleted firewall", + "salt/cloud/firewall/deleted", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def show_fwrule(kwargs=None, call=None): - ''' + """ Show the details of an existing firewall rule. CLI Example: @@ -1048,23 +989,21 @@ def show_fwrule(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_fwrule gce name=allow-http - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_fwrule function must be called with -f or --function.' - ) - if not kwargs or 'name' not in kwargs: - log.error( - 'Must specify name of network.' + "The show_fwrule function must be called with -f or --function." ) + if not kwargs or "name" not in kwargs: + log.error("Must specify name of network.") return False conn = get_conn() - return _expand_item(conn.ex_get_firewall(kwargs['name'])) + return _expand_item(conn.ex_get_firewall(kwargs["name"])) def create_hc(kwargs=None, call=None): - ''' + """ Create an HTTP health check configuration. CLI Example: @@ -1072,75 +1011,78 @@ def create_hc(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_hc gce name=hc path=/healthy port=80 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_hc function must be called with -f or --function.' + "The create_hc function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when creating a health check.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when creating a health check.") return False - name = kwargs['name'] - host = kwargs.get('host', None) - path = kwargs.get('path', None) - port = kwargs.get('port', None) - interval = kwargs.get('interval', None) - timeout = kwargs.get('timeout', None) - unhealthy_threshold = kwargs.get('unhealthy_threshold', None) - healthy_threshold = kwargs.get('healthy_threshold', None) + name = kwargs["name"] + host = kwargs.get("host", None) + path = kwargs.get("path", None) + port = kwargs.get("port", None) + interval = kwargs.get("interval", None) + timeout = kwargs.get("timeout", None) + unhealthy_threshold = kwargs.get("unhealthy_threshold", None) + healthy_threshold = kwargs.get("healthy_threshold", None) conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'create health_check', - 'salt/cloud/healthcheck/creating', + __utils__["cloud.fire_event"]( + "event", + "create health_check", + "salt/cloud/healthcheck/creating", args={ - 'name': name, - 'host': host, - 'path': path, - 'port': port, - 'interval': interval, - 'timeout': timeout, - 'unhealthy_threshold': unhealthy_threshold, - 'healthy_threshold': healthy_threshold, + "name": name, + "host": host, + "path": path, + "port": port, + "interval": interval, + "timeout": timeout, + "unhealthy_threshold": unhealthy_threshold, + "healthy_threshold": healthy_threshold, }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) hc = conn.ex_create_healthcheck( - name, host=host, path=path, port=port, interval=interval, - timeout=timeout, unhealthy_threshold=unhealthy_threshold, - healthy_threshold=healthy_threshold + name, + host=host, + path=path, + port=port, + interval=interval, + timeout=timeout, + unhealthy_threshold=unhealthy_threshold, + healthy_threshold=healthy_threshold, ) - __utils__['cloud.fire_event']( - 'event', - 'created health_check', - 'salt/cloud/healthcheck/created', + __utils__["cloud.fire_event"]( + "event", + "created health_check", + "salt/cloud/healthcheck/created", args={ - 'name': name, - 'host': host, - 'path': path, - 'port': port, - 'interval': interval, - 'timeout': timeout, - 'unhealthy_threshold': unhealthy_threshold, - 'healthy_threshold': healthy_threshold, + "name": name, + "host": host, + "path": path, + "port": port, + "interval": interval, + "timeout": timeout, + "unhealthy_threshold": unhealthy_threshold, + "healthy_threshold": healthy_threshold, }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return _expand_item(hc) def delete_hc(kwargs=None, call=None): - ''' + """ Permanently delete a health check. CLI Example: @@ -1148,58 +1090,52 @@ def delete_hc(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_hc gce name=hc - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_hc function must be called with -f or --function.' + "The delete_hc function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when deleting a health check.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when deleting a health check.") return False - name = kwargs['name'] + name = kwargs["name"] conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'delete health_check', - 'salt/cloud/healthcheck/deleting', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "delete health_check", + "salt/cloud/healthcheck/deleting", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: - result = conn.ex_destroy_healthcheck( - conn.ex_get_healthcheck(name) - ) + result = conn.ex_destroy_healthcheck(conn.ex_get_healthcheck(name)) except ResourceNotFoundError as exc: log.error( - 'Health check %s was not found. Exception was: %s', - name, exc, exc_info_on_loglevel=logging.DEBUG + "Health check %s was not found. Exception was: %s", + name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - __utils__['cloud.fire_event']( - 'event', - 'deleted health_check', - 'salt/cloud/healthcheck/deleted', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleted health_check", + "salt/cloud/healthcheck/deleted", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def show_hc(kwargs=None, call=None): - ''' + """ Show the details of an existing health check. CLI Example: @@ -1207,23 +1143,21 @@ def show_hc(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_hc gce name=hc - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_hc function must be called with -f or --function.' - ) - if not kwargs or 'name' not in kwargs: - log.error( - 'Must specify name of health check.' + "The show_hc function must be called with -f or --function." ) + if not kwargs or "name" not in kwargs: + log.error("Must specify name of health check.") return False conn = get_conn() - return _expand_item(conn.ex_get_healthcheck(kwargs['name'])) + return _expand_item(conn.ex_get_healthcheck(kwargs["name"])) def create_address(kwargs=None, call=None): - ''' + """ Create a static address in a region. CLI Example: @@ -1231,56 +1165,52 @@ def create_address(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_address gce name=my-ip region=us-central1 address=IP - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_address function must be called with -f or --function.' + "The create_address function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when creating an address.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when creating an address.") return False - if 'region' not in kwargs: - log.error( - 'A region must be specified for the address.' - ) + if "region" not in kwargs: + log.error("A region must be specified for the address.") return False - name = kwargs['name'] - ex_region = kwargs['region'] + name = kwargs["name"] + ex_region = kwargs["region"] ex_address = kwargs.get("address", None) conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'create address', - 'salt/cloud/address/creating', + __utils__["cloud.fire_event"]( + "event", + "create address", + "salt/cloud/address/creating", args=kwargs, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) addy = conn.ex_create_address(name, ex_region, ex_address) - __utils__['cloud.fire_event']( - 'event', - 'created address', - 'salt/cloud/address/created', + __utils__["cloud.fire_event"]( + "event", + "created address", + "salt/cloud/address/created", args=kwargs, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Created GCE Address %s', name) + log.info("Created GCE Address %s", name) return _expand_address(addy) def delete_address(kwargs=None, call=None): - ''' + """ Permanently delete a static address. CLI Example: @@ -1288,69 +1218,62 @@ def delete_address(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_address gce name=my-ip - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_address function must be called with -f or --function.' + "The delete_address function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when deleting an address.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when deleting an address.") return False - if not kwargs or 'region' not in kwargs: - log.error( - 'A region must be specified when deleting an address.' - ) + if not kwargs or "region" not in kwargs: + log.error("A region must be specified when deleting an address.") return False - name = kwargs['name'] - ex_region = kwargs['region'] + name = kwargs["name"] + ex_region = kwargs["region"] conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'delete address', - 'salt/cloud/address/deleting', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "delete address", + "salt/cloud/address/deleting", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: - result = conn.ex_destroy_address( - conn.ex_get_address(name, ex_region) - ) + result = conn.ex_destroy_address(conn.ex_get_address(name, ex_region)) except ResourceNotFoundError as exc: log.error( - 'Address %s in region %s was not found. Exception was: %s', - name, ex_region, exc, exc_info_on_loglevel=logging.DEBUG + "Address %s in region %s was not found. Exception was: %s", + name, + ex_region, + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - __utils__['cloud.fire_event']( - 'event', - 'deleted address', - 'salt/cloud/address/deleted', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleted address", + "salt/cloud/address/deleted", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Deleted GCE Address %s', name) + log.info("Deleted GCE Address %s", name) return result def show_address(kwargs=None, call=None): - ''' + """ Show the details of an existing static address. CLI Example: @@ -1358,29 +1281,25 @@ def show_address(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_address gce name=mysnapshot region=us-central1 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_snapshot function must be called with -f or --function.' - ) - if not kwargs or 'name' not in kwargs: - log.error( - 'Must specify name.' + "The show_snapshot function must be called with -f or --function." ) + if not kwargs or "name" not in kwargs: + log.error("Must specify name.") return False - if not kwargs or 'region' not in kwargs: - log.error( - 'Must specify region.' - ) + if not kwargs or "region" not in kwargs: + log.error("Must specify region.") return False conn = get_conn() - return _expand_address(conn.ex_get_address(kwargs['name'], kwargs['region'])) + return _expand_address(conn.ex_get_address(kwargs["name"], kwargs["region"])) def create_lb(kwargs=None, call=None): - ''' + """ Create a load-balancer configuration. CLI Example: @@ -1388,82 +1307,79 @@ def create_lb(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_lb gce name=lb region=us-central1 ports=80 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_lb function must be called with -f or --function.' + "The create_lb function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when creating a health check.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when creating a health check.") return False - if 'ports' not in kwargs: - log.error( - 'A port or port-range must be specified for the load-balancer.' - ) + if "ports" not in kwargs: + log.error("A port or port-range must be specified for the load-balancer.") return False - if 'region' not in kwargs: - log.error( - 'A region must be specified for the load-balancer.' - ) + if "region" not in kwargs: + log.error("A region must be specified for the load-balancer.") return False - if 'members' not in kwargs: - log.error( - 'A comma-separated list of members must be specified.' - ) + if "members" not in kwargs: + log.error("A comma-separated list of members must be specified.") return False - name = kwargs['name'] - ports = kwargs['ports'] - ex_region = kwargs['region'] - members = kwargs.get('members').split(',') + name = kwargs["name"] + ports = kwargs["ports"] + ex_region = kwargs["region"] + members = kwargs.get("members").split(",") - protocol = kwargs.get('protocol', 'tcp') - algorithm = kwargs.get('algorithm', None) - ex_healthchecks = kwargs.get('healthchecks', None) + protocol = kwargs.get("protocol", "tcp") + algorithm = kwargs.get("algorithm", None) + ex_healthchecks = kwargs.get("healthchecks", None) # pylint: disable=W0511 conn = get_conn() lb_conn = get_lb_conn(conn) - ex_address = kwargs.get('address', None) + ex_address = kwargs.get("address", None) if ex_address is not None: ex_address = __create_orget_address(conn, ex_address, ex_region) if ex_healthchecks: - ex_healthchecks = ex_healthchecks.split(',') + ex_healthchecks = ex_healthchecks.split(",") - __utils__['cloud.fire_event']( - 'event', - 'create load_balancer', - 'salt/cloud/loadbalancer/creating', + __utils__["cloud.fire_event"]( + "event", + "create load_balancer", + "salt/cloud/loadbalancer/creating", args=kwargs, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) lb = lb_conn.create_balancer( - name, ports, protocol, algorithm, members, - ex_region=ex_region, ex_healthchecks=ex_healthchecks, - ex_address=ex_address + name, + ports, + protocol, + algorithm, + members, + ex_region=ex_region, + ex_healthchecks=ex_healthchecks, + ex_address=ex_address, ) - __utils__['cloud.fire_event']( - 'event', - 'created load_balancer', - 'salt/cloud/loadbalancer/created', + __utils__["cloud.fire_event"]( + "event", + "created load_balancer", + "salt/cloud/loadbalancer/created", args=kwargs, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return _expand_balancer(lb) def delete_lb(kwargs=None, call=None): - ''' + """ Permanently delete a load-balancer. CLI Example: @@ -1471,58 +1387,52 @@ def delete_lb(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_lb gce name=lb - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_hc function must be called with -f or --function.' + "The delete_hc function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when deleting a health check.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when deleting a health check.") return False - name = kwargs['name'] + name = kwargs["name"] lb_conn = get_lb_conn(get_conn()) - __utils__['cloud.fire_event']( - 'event', - 'delete load_balancer', - 'salt/cloud/loadbalancer/deleting', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "delete load_balancer", + "salt/cloud/loadbalancer/deleting", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: - result = lb_conn.destroy_balancer( - lb_conn.get_balancer(name) - ) + result = lb_conn.destroy_balancer(lb_conn.get_balancer(name)) except ResourceNotFoundError as exc: log.error( - 'Load balancer %s was not found. Exception was: %s', - name, exc, exc_info_on_loglevel=logging.DEBUG + "Load balancer %s was not found. Exception was: %s", + name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - __utils__['cloud.fire_event']( - 'event', - 'deleted load_balancer', - 'salt/cloud/loadbalancer/deleted', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleted load_balancer", + "salt/cloud/loadbalancer/deleted", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def show_lb(kwargs=None, call=None): - ''' + """ Show the details of an existing load-balancer. CLI Example: @@ -1530,23 +1440,21 @@ def show_lb(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_lb gce name=lb - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_lb function must be called with -f or --function.' - ) - if not kwargs or 'name' not in kwargs: - log.error( - 'Must specify name of load-balancer.' + "The show_lb function must be called with -f or --function." ) + if not kwargs or "name" not in kwargs: + log.error("Must specify name of load-balancer.") return False lb_conn = get_lb_conn(get_conn()) - return _expand_balancer(lb_conn.get_balancer(kwargs['name'])) + return _expand_balancer(lb_conn.get_balancer(kwargs["name"])) def attach_lb(kwargs=None, call=None): - ''' + """ Add an existing node/member to an existing load-balancer configuration. CLI Example: @@ -1554,53 +1462,49 @@ def attach_lb(kwargs=None, call=None): .. code-block:: bash salt-cloud -f attach_lb gce name=lb member=myinstance - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The attach_lb function must be called with -f or --function.' + "The attach_lb function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A load-balancer name must be specified.' - ) + if not kwargs or "name" not in kwargs: + log.error("A load-balancer name must be specified.") return False - if 'member' not in kwargs: - log.error( - 'A node name name must be specified.' - ) + if "member" not in kwargs: + log.error("A node name name must be specified.") return False conn = get_conn() - node = conn.ex_get_node(kwargs['member']) + node = conn.ex_get_node(kwargs["member"]) lb_conn = get_lb_conn(conn) - lb = lb_conn.get_balancer(kwargs['name']) + lb = lb_conn.get_balancer(kwargs["name"]) - __utils__['cloud.fire_event']( - 'event', - 'attach load_balancer', - 'salt/cloud/loadbalancer/attaching', + __utils__["cloud.fire_event"]( + "event", + "attach load_balancer", + "salt/cloud/loadbalancer/attaching", args=kwargs, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) result = lb_conn.balancer_attach_compute_node(lb, node) - __utils__['cloud.fire_event']( - 'event', - 'attached load_balancer', - 'salt/cloud/loadbalancer/attached', + __utils__["cloud.fire_event"]( + "event", + "attached load_balancer", + "salt/cloud/loadbalancer/attached", args=kwargs, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return _expand_item(result) def detach_lb(kwargs=None, call=None): - ''' + """ Remove an existing node/member from an existing load-balancer configuration. CLI Example: @@ -1608,65 +1512,62 @@ def detach_lb(kwargs=None, call=None): .. code-block:: bash salt-cloud -f detach_lb gce name=lb member=myinstance - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The detach_lb function must be called with -f or --function.' + "The detach_lb function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A load-balancer name must be specified.' - ) + if not kwargs or "name" not in kwargs: + log.error("A load-balancer name must be specified.") return False - if 'member' not in kwargs: - log.error( - 'A node name name must be specified.' - ) + if "member" not in kwargs: + log.error("A node name name must be specified.") return False conn = get_conn() lb_conn = get_lb_conn(conn) - lb = lb_conn.get_balancer(kwargs['name']) + lb = lb_conn.get_balancer(kwargs["name"]) member_list = lb_conn.balancer_list_members(lb) remove_member = None for member in member_list: - if member.id == kwargs['member']: + if member.id == kwargs["member"]: remove_member = member break if not remove_member: log.error( - 'The specified member %s was not a member of LB %s.', - kwargs['member'], kwargs['name'] + "The specified member %s was not a member of LB %s.", + kwargs["member"], + kwargs["name"], ) return False - __utils__['cloud.fire_event']( - 'event', - 'detach load_balancer', - 'salt/cloud/loadbalancer/detaching', + __utils__["cloud.fire_event"]( + "event", + "detach load_balancer", + "salt/cloud/loadbalancer/detaching", args=kwargs, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) result = lb_conn.balancer_detach_member(lb, remove_member) - __utils__['cloud.fire_event']( - 'event', - 'detached load_balancer', - 'salt/cloud/loadbalancer/detached', + __utils__["cloud.fire_event"]( + "event", + "detached load_balancer", + "salt/cloud/loadbalancer/detached", args=kwargs, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def delete_snapshot(kwargs=None, call=None): - ''' + """ Permanently delete a disk snapshot. CLI Example: @@ -1674,58 +1575,52 @@ def delete_snapshot(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_snapshot gce name=disk-snap-1 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_snapshot function must be called with -f or --function.' + "The delete_snapshot function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when deleting a snapshot.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when deleting a snapshot.") return False - name = kwargs['name'] + name = kwargs["name"] conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'delete snapshot', - 'salt/cloud/snapshot/deleting', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "delete snapshot", + "salt/cloud/snapshot/deleting", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: - result = conn.destroy_volume_snapshot( - conn.ex_get_snapshot(name) - ) + result = conn.destroy_volume_snapshot(conn.ex_get_snapshot(name)) except ResourceNotFoundError as exc: log.error( - 'Snapshot %s was not found. Exception was: %s', - name, exc, exc_info_on_loglevel=logging.DEBUG + "Snapshot %s was not found. Exception was: %s", + name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - __utils__['cloud.fire_event']( - 'event', - 'deleted snapshot', - 'salt/cloud/snapshot/deleted', - args={ - 'name': name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleted snapshot", + "salt/cloud/snapshot/deleted", + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def delete_disk(kwargs=None, call=None): - ''' + """ Permanently delete a persistent disk. CLI Example: @@ -1733,63 +1628,63 @@ def delete_disk(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_disk gce disk_name=pd - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_disk function must be called with -f or --function.' + "The delete_disk function must be called with -f or --function." ) - if not kwargs or 'disk_name' not in kwargs: - log.error( - 'A disk_name must be specified when deleting a disk.' - ) + if not kwargs or "disk_name" not in kwargs: + log.error("A disk_name must be specified when deleting a disk.") return False conn = get_conn() - disk = conn.ex_get_volume(kwargs.get('disk_name')) + disk = conn.ex_get_volume(kwargs.get("disk_name")) - __utils__['cloud.fire_event']( - 'event', - 'delete disk', - 'salt/cloud/disk/deleting', + __utils__["cloud.fire_event"]( + "event", + "delete disk", + "salt/cloud/disk/deleting", args={ - 'name': disk.name, - 'location': disk.extra['zone'].name, - 'size': disk.size, + "name": disk.name, + "location": disk.extra["zone"].name, + "size": disk.size, }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: result = conn.destroy_volume(disk) except ResourceInUseError as exc: log.error( - 'Disk %s is in use and must be detached before deleting.\n' - 'The following exception was thrown by libcloud:\n%s', - disk.name, exc, exc_info_on_loglevel=logging.DEBUG + "Disk %s is in use and must be detached before deleting.\n" + "The following exception was thrown by libcloud:\n%s", + disk.name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - __utils__['cloud.fire_event']( - 'event', - 'deleted disk', - 'salt/cloud/disk/deleted', + __utils__["cloud.fire_event"]( + "event", + "deleted disk", + "salt/cloud/disk/deleted", args={ - 'name': disk.name, - 'location': disk.extra['zone'].name, - 'size': disk.size, + "name": disk.name, + "location": disk.extra["zone"].name, + "size": disk.size, }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def create_disk(kwargs=None, call=None): - ''' + """ Create a new persistent disk. Must specify `disk_name` and `location`, and optionally can specify 'disk_type' as pd-standard or pd-ssd, which defaults to pd-standard. Can also specify an `image` or `snapshot` but @@ -1800,81 +1695,75 @@ def create_disk(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_disk gce disk_name=pd size=300 location=us-central1-b - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_disk function must be called with -f or --function.' + "The create_disk function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('disk_name', None) - image = kwargs.get('image', None) - location = kwargs.get('location', None) - size = kwargs.get('size', None) - snapshot = kwargs.get('snapshot', None) - disk_type = kwargs.get('type', 'pd-standard') + name = kwargs.get("disk_name", None) + image = kwargs.get("image", None) + location = kwargs.get("location", None) + size = kwargs.get("size", None) + snapshot = kwargs.get("snapshot", None) + disk_type = kwargs.get("type", "pd-standard") if location is None: - log.error( - 'A location (zone) must be specified when creating a disk.' - ) + log.error("A location (zone) must be specified when creating a disk.") return False if name is None: - log.error( - 'A disk_name must be specified when creating a disk.' - ) + log.error("A disk_name must be specified when creating a disk.") return False if size is None and image is None and snapshot is None: - log.error( - 'Must specify image, snapshot, or size.' - ) + log.error("Must specify image, snapshot, or size.") return False conn = get_conn() - location = conn.ex_get_zone(kwargs['location']) + location = conn.ex_get_zone(kwargs["location"]) use_existing = True - __utils__['cloud.fire_event']( - 'event', - 'create disk', - 'salt/cloud/disk/creating', + __utils__["cloud.fire_event"]( + "event", + "create disk", + "salt/cloud/disk/creating", args={ - 'name': name, - 'location': location.name, - 'image': image, - 'snapshot': snapshot, + "name": name, + "location": location.name, + "image": image, + "snapshot": snapshot, }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) disk = conn.create_volume( size, name, location, snapshot, image, use_existing, disk_type ) - __utils__['cloud.fire_event']( - 'event', - 'created disk', - 'salt/cloud/disk/created', + __utils__["cloud.fire_event"]( + "event", + "created disk", + "salt/cloud/disk/created", args={ - 'name': name, - 'location': location.name, - 'image': image, - 'snapshot': snapshot, + "name": name, + "location": location.name, + "image": image, + "snapshot": snapshot, }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return _expand_disk(disk) def create_snapshot(kwargs=None, call=None): - ''' + """ Create a new disk snapshot. Must specify `name` and `disk_name`. CLI Example: @@ -1882,68 +1771,60 @@ def create_snapshot(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_snapshot gce name=snap1 disk_name=pd - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_snapshot function must be called with -f or --function.' + "The create_snapshot function must be called with -f or --function." ) - if not kwargs or 'name' not in kwargs: - log.error( - 'A name must be specified when creating a snapshot.' - ) + if not kwargs or "name" not in kwargs: + log.error("A name must be specified when creating a snapshot.") return False - if 'disk_name' not in kwargs: - log.error( - 'A disk_name must be specified when creating a snapshot.' - ) + if "disk_name" not in kwargs: + log.error("A disk_name must be specified when creating a snapshot.") return False conn = get_conn() - name = kwargs.get('name') - disk_name = kwargs.get('disk_name') + name = kwargs.get("name") + disk_name = kwargs.get("disk_name") try: disk = conn.ex_get_volume(disk_name) except ResourceNotFoundError as exc: log.error( - 'Disk %s was not found. Exception was: %s', - disk_name, exc, exc_info_on_loglevel=logging.DEBUG + "Disk %s was not found. Exception was: %s", + disk_name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - __utils__['cloud.fire_event']( - 'event', - 'create snapshot', - 'salt/cloud/snapshot/creating', - args={ - 'name': name, - 'disk_name': disk_name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "create snapshot", + "salt/cloud/snapshot/creating", + args={"name": name, "disk_name": disk_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) snapshot = conn.create_volume_snapshot(disk, name) - __utils__['cloud.fire_event']( - 'event', - 'created snapshot', - 'salt/cloud/snapshot/created', - args={ - 'name': name, - 'disk_name': disk_name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created snapshot", + "salt/cloud/snapshot/created", + args={"name": name, "disk_name": disk_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return _expand_item(snapshot) def show_disk(name=None, kwargs=None, call=None): # pylint: disable=W0613 - ''' + """ Show the details of an existing disk. CLI Example: @@ -1952,19 +1833,17 @@ def show_disk(name=None, kwargs=None, call=None): # pylint: disable=W0613 salt-cloud -a show_disk myinstance disk_name=mydisk salt-cloud -f show_disk gce disk_name=mydisk - ''' - if not kwargs or 'disk_name' not in kwargs: - log.error( - 'Must specify disk_name.' - ) + """ + if not kwargs or "disk_name" not in kwargs: + log.error("Must specify disk_name.") return False conn = get_conn() - return _expand_disk(conn.ex_get_volume(kwargs['disk_name'])) + return _expand_disk(conn.ex_get_volume(kwargs["disk_name"])) def show_snapshot(kwargs=None, call=None): - ''' + """ Show the details of an existing snapshot. CLI Example: @@ -1972,23 +1851,21 @@ def show_snapshot(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_snapshot gce name=mysnapshot - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_snapshot function must be called with -f or --function.' - ) - if not kwargs or 'name' not in kwargs: - log.error( - 'Must specify name.' + "The show_snapshot function must be called with -f or --function." ) + if not kwargs or "name" not in kwargs: + log.error("Must specify name.") return False conn = get_conn() - return _expand_item(conn.ex_get_snapshot(kwargs['name'])) + return _expand_item(conn.ex_get_snapshot(kwargs["name"])) def detach_disk(name=None, kwargs=None, call=None): - ''' + """ Detach a disk from an instance. CLI Example: @@ -1996,60 +1873,50 @@ def detach_disk(name=None, kwargs=None, call=None): .. code-block:: bash salt-cloud -a detach_disk myinstance disk_name=mydisk - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The detach_Disk action must be called with -a or --action.' + "The detach_Disk action must be called with -a or --action." ) if not name: - log.error( - 'Must specify an instance name.' - ) + log.error("Must specify an instance name.") return False - if not kwargs or 'disk_name' not in kwargs: - log.error( - 'Must specify a disk_name to detach.' - ) + if not kwargs or "disk_name" not in kwargs: + log.error("Must specify a disk_name to detach.") return False node_name = name - disk_name = kwargs['disk_name'] + disk_name = kwargs["disk_name"] conn = get_conn() node = conn.ex_get_node(node_name) disk = conn.ex_get_volume(disk_name) - __utils__['cloud.fire_event']( - 'event', - 'detach disk', - 'salt/cloud/disk/detaching', - args={ - 'name': node_name, - 'disk_name': disk_name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "detach disk", + "salt/cloud/disk/detaching", + args={"name": node_name, "disk_name": disk_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) result = conn.detach_volume(disk, node) - __utils__['cloud.fire_event']( - 'event', - 'detached disk', - 'salt/cloud/disk/detached', - args={ - 'name': node_name, - 'disk_name': disk_name, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "detached disk", + "salt/cloud/disk/detached", + args={"name": node_name, "disk_name": disk_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def attach_disk(name=None, kwargs=None, call=None): - ''' + """ Attach an existing disk to an existing instance. CLI Example: @@ -2057,78 +1924,63 @@ def attach_disk(name=None, kwargs=None, call=None): .. code-block:: bash salt-cloud -a attach_disk myinstance disk_name=mydisk mode=READ_WRITE - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The attach_disk action must be called with -a or --action.' + "The attach_disk action must be called with -a or --action." ) if not name: - log.error( - 'Must specify an instance name.' - ) + log.error("Must specify an instance name.") return False - if not kwargs or 'disk_name' not in kwargs: - log.error( - 'Must specify a disk_name to attach.' - ) + if not kwargs or "disk_name" not in kwargs: + log.error("Must specify a disk_name to attach.") return False node_name = name - disk_name = kwargs['disk_name'] - mode = kwargs.get('mode', 'READ_WRITE').upper() - boot = kwargs.get('boot', False) - auto_delete = kwargs.get('auto_delete', False) - if boot and boot.lower() in ['true', 'yes', 'enabled']: + disk_name = kwargs["disk_name"] + mode = kwargs.get("mode", "READ_WRITE").upper() + boot = kwargs.get("boot", False) + auto_delete = kwargs.get("auto_delete", False) + if boot and boot.lower() in ["true", "yes", "enabled"]: boot = True else: boot = False - if mode not in ['READ_WRITE', 'READ_ONLY']: - log.error( - 'Mode must be either READ_ONLY or (default) READ_WRITE.' - ) + if mode not in ["READ_WRITE", "READ_ONLY"]: + log.error("Mode must be either READ_ONLY or (default) READ_WRITE.") return False conn = get_conn() node = conn.ex_get_node(node_name) disk = conn.ex_get_volume(disk_name) - __utils__['cloud.fire_event']( - 'event', - 'attach disk', - 'salt/cloud/disk/attaching', - args={ - 'name': node_name, - 'disk_name': disk_name, - 'mode': mode, - 'boot': boot, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "attach disk", + "salt/cloud/disk/attaching", + args={"name": node_name, "disk_name": disk_name, "mode": mode, "boot": boot}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - result = conn.attach_volume(node, disk, ex_mode=mode, ex_boot=boot, - ex_auto_delete=auto_delete) + result = conn.attach_volume( + node, disk, ex_mode=mode, ex_boot=boot, ex_auto_delete=auto_delete + ) - __utils__['cloud.fire_event']( - 'event', - 'attached disk', - 'salt/cloud/disk/attached', - args={ - 'name': node_name, - 'disk_name': disk_name, - 'mode': mode, - 'boot': boot, - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "attached disk", + "salt/cloud/disk/attached", + args={"name": node_name, "disk_name": disk_name, "mode": mode, "boot": boot}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def reboot(vm_name, call=None): - ''' + """ Call GCE 'reset' on the instance. CLI Example: @@ -2136,41 +1988,39 @@ def reboot(vm_name, call=None): .. code-block:: bash salt-cloud -a reboot myinstance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The reboot action must be called with -a or --action.' + "The reboot action must be called with -a or --action." ) conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'reboot instance', - 'salt/cloud/{0}/rebooting'.format(vm_name), - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "reboot instance", + "salt/cloud/{0}/rebooting".format(vm_name), + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - result = conn.reboot_node( - conn.ex_get_node(vm_name) - ) + result = conn.reboot_node(conn.ex_get_node(vm_name)) - __utils__['cloud.fire_event']( - 'event', - 'reboot instance', - 'salt/cloud/{0}/rebooted'.format(vm_name), - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "reboot instance", + "salt/cloud/{0}/rebooted".format(vm_name), + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def start(vm_name, call=None): - ''' + """ Call GCE 'start on the instance. .. versionadded:: 2017.7.0 @@ -2180,41 +2030,39 @@ def start(vm_name, call=None): .. code-block:: bash salt-cloud -a start myinstance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The start action must be called with -a or --action.' + "The start action must be called with -a or --action." ) conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'start instance', - 'salt/cloud/{0}/starting'.format(vm_name), - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "start instance", + "salt/cloud/{0}/starting".format(vm_name), + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - result = conn.ex_start_node( - conn.ex_get_node(vm_name) - ) + result = conn.ex_start_node(conn.ex_get_node(vm_name)) - __utils__['cloud.fire_event']( - 'event', - 'start instance', - 'salt/cloud/{0}/started'.format(vm_name), - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "start instance", + "salt/cloud/{0}/started".format(vm_name), + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def stop(vm_name, call=None): - ''' + """ Call GCE 'stop' on the instance. .. versionadded:: 2017.7.0 @@ -2224,41 +2072,37 @@ def stop(vm_name, call=None): .. code-block:: bash salt-cloud -a stop myinstance - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") conn = get_conn() - __utils__['cloud.fire_event']( - 'event', - 'stop instance', - 'salt/cloud/{0}/stopping'.format(vm_name), - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "stop instance", + "salt/cloud/{0}/stopping".format(vm_name), + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - result = conn.ex_stop_node( - conn.ex_get_node(vm_name) - ) + result = conn.ex_stop_node(conn.ex_get_node(vm_name)) - __utils__['cloud.fire_event']( - 'event', - 'stop instance', - 'salt/cloud/{0}/stopped'.format(vm_name), - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "stop instance", + "salt/cloud/{0}/stopped".format(vm_name), + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result def destroy(vm_name, call=None): - ''' + """ Call 'destroy' on the instance. Can be called with "-a destroy" or -d CLI Example: @@ -2267,8 +2111,8 @@ def destroy(vm_name, call=None): salt-cloud -a destroy myinstance1 myinstance2 ... salt-cloud -d myinstance1 myinstance2 ... - ''' - if call and call != 'action': + """ + if call and call != "action": raise SaltCloudSystemExit( 'The destroy action must be called with -d or "-a destroy".' ) @@ -2279,22 +2123,22 @@ def destroy(vm_name, call=None): node = conn.ex_get_node(vm_name) except Exception as exc: # pylint: disable=W0703 log.error( - 'Could not locate instance %s\n\n' - 'The following exception was thrown by libcloud when trying to ' - 'run the initial deployment: \n%s', - vm_name, exc, exc_info_on_loglevel=logging.DEBUG - ) - raise SaltCloudSystemExit( - 'Could not find instance {0}.'.format(vm_name) + "Could not locate instance %s\n\n" + "The following exception was thrown by libcloud when trying to " + "run the initial deployment: \n%s", + vm_name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) + raise SaltCloudSystemExit("Could not find instance {0}.".format(vm_name)) - __utils__['cloud.fire_event']( - 'event', - 'delete instance', - 'salt/cloud/{0}/deleting'.format(vm_name), - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "delete instance", + "salt/cloud/{0}/deleting".format(vm_name), + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) # Use the instance metadata to see if its salt cloud profile was @@ -2302,49 +2146,53 @@ def destroy(vm_name, call=None): # to see if the 'delete_boot_pd' value is set to delete the disk # along with the instance. profile = None - if node.extra['metadata'] and 'items' in node.extra['metadata']: - for md in node.extra['metadata']['items']: - if md['key'] == 'salt-cloud-profile': - profile = md['value'] + if node.extra["metadata"] and "items" in node.extra["metadata"]: + for md in node.extra["metadata"]["items"]: + if md["key"] == "salt-cloud-profile": + profile = md["value"] vm_ = get_configured_provider() delete_boot_pd = False - if profile and profile in vm_['profiles'] and 'delete_boot_pd' in vm_['profiles'][profile]: - delete_boot_pd = vm_['profiles'][profile]['delete_boot_pd'] + if ( + profile + and profile in vm_["profiles"] + and "delete_boot_pd" in vm_["profiles"][profile] + ): + delete_boot_pd = vm_["profiles"][profile]["delete_boot_pd"] try: inst_deleted = conn.destroy_node(node) except Exception as exc: # pylint: disable=W0703 log.error( - 'Could not destroy instance %s\n\n' - 'The following exception was thrown by libcloud when trying to ' - 'run the initial deployment: \n%s', - vm_name, exc, exc_info_on_loglevel=logging.DEBUG + "Could not destroy instance %s\n\n" + "The following exception was thrown by libcloud when trying to " + "run the initial deployment: \n%s", + vm_name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) - raise SaltCloudSystemExit( - 'Could not destroy instance {0}.'.format(vm_name) - ) - __utils__['cloud.fire_event']( - 'event', - 'delete instance', - 'salt/cloud/{0}/deleted'.format(vm_name), - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + raise SaltCloudSystemExit("Could not destroy instance {0}.".format(vm_name)) + __utils__["cloud.fire_event"]( + "event", + "delete instance", + "salt/cloud/{0}/deleted".format(vm_name), + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) if delete_boot_pd: log.info( - 'delete_boot_pd is enabled for the instance profile, ' - 'attempting to delete disk' - ) - __utils__['cloud.fire_event']( - 'event', - 'delete disk', - 'salt/cloud/disk/deleting', - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + "delete_boot_pd is enabled for the instance profile, " + "attempting to delete disk" + ) + __utils__["cloud.fire_event"]( + "event", + "delete disk", + "salt/cloud/disk/deleting", + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: conn.destroy_volume(conn.ex_get_volume(vm_name)) @@ -2353,28 +2201,32 @@ def destroy(vm_name, call=None): # to allow completion of instance deletion. Just log the error # and keep going. log.error( - 'Could not destroy disk %s\n\n' - 'The following exception was thrown by libcloud when trying ' - 'to run the initial deployment: \n%s', - vm_name, exc, exc_info_on_loglevel=logging.DEBUG + "Could not destroy disk %s\n\n" + "The following exception was thrown by libcloud when trying " + "to run the initial deployment: \n%s", + vm_name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) - __utils__['cloud.fire_event']( - 'event', - 'deleted disk', - 'salt/cloud/disk/deleted', - args={'name': vm_name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "deleted disk", + "salt/cloud/disk/deleted", + args={"name": vm_name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](vm_name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + vm_name, __active_provider_name__.split(":")[0], __opts__ + ) return inst_deleted def create_attach_volumes(name, kwargs, call=None): - ''' + """ .. versionadded:: 2017.7.0 Create and attach multiple volumes to a node. The 'volumes' and 'node' @@ -2399,219 +2251,222 @@ def create_attach_volumes(name, kwargs, call=None): Volumes are attached in the order in which they are given, thus on a new node the first volume will be /dev/sdb, the second /dev/sdc, and so on. - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The create_attach_volumes action must be called with ' - '-a or --action.' + "The create_attach_volumes action must be called with " "-a or --action." ) - volumes = literal_eval(kwargs['volumes']) - node = kwargs['node'] + volumes = literal_eval(kwargs["volumes"]) + node = kwargs["node"] conn = get_conn() node_data = _expand_node(conn.ex_get_node(node)) - letter = ord('a') - 1 + letter = ord("a") - 1 for idx, volume in enumerate(volumes): - volume_name = '{0}-sd{1}'.format(name, chr(letter + 2 + idx)) + volume_name = "{0}-sd{1}".format(name, chr(letter + 2 + idx)) volume_dict = { - 'disk_name': volume_name, - 'location': node_data['extra']['zone']['name'], - 'size': volume['size'], - 'type': volume.get('type', 'pd-standard'), - 'image': volume.get('image', None), - 'snapshot': volume.get('snapshot', None), - 'auto_delete': volume.get('auto_delete', False) + "disk_name": volume_name, + "location": node_data["extra"]["zone"]["name"], + "size": volume["size"], + "type": volume.get("type", "pd-standard"), + "image": volume.get("image", None), + "snapshot": volume.get("snapshot", None), + "auto_delete": volume.get("auto_delete", False), } - create_disk(volume_dict, 'function') - attach_disk(name, volume_dict, 'action') + create_disk(volume_dict, "function") + attach_disk(name, volume_dict, "action") def request_instance(vm_): - ''' + """ Request a single GCE instance from a data dict. .. versionchanged: 2017.7.0 - ''' - if not GCE_VM_NAME_REGEX.match(vm_['name']): + """ + if not GCE_VM_NAME_REGEX.match(vm_["name"]): raise SaltCloudSystemExit( - 'VM names must start with a letter, only contain letters, numbers, or dashes ' - 'and cannot end in a dash.' + "VM names must start with a letter, only contain letters, numbers, or dashes " + "and cannot end in a dash." ) try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'gce', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "gce", vm_["profile"], vm_=vm_ + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'create instance', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "create instance", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) conn = get_conn() kwargs = { - 'name': vm_['name'], - 'size': __get_size(conn, vm_), - 'image': __get_image(conn, vm_), - 'location': __get_location(conn, vm_), - 'ex_network': __get_network(conn, vm_), - 'ex_subnetwork': __get_subnetwork(vm_), - 'ex_tags': __get_tags(vm_), - 'ex_metadata': __get_metadata(vm_), + "name": vm_["name"], + "size": __get_size(conn, vm_), + "image": __get_image(conn, vm_), + "location": __get_location(conn, vm_), + "ex_network": __get_network(conn, vm_), + "ex_subnetwork": __get_subnetwork(vm_), + "ex_tags": __get_tags(vm_), + "ex_metadata": __get_metadata(vm_), } external_ip = config.get_cloud_config_value( - 'external_ip', vm_, __opts__, default='ephemeral' + "external_ip", vm_, __opts__, default="ephemeral" ) - if external_ip.lower() == 'ephemeral': - external_ip = 'ephemeral' - elif external_ip == 'None': + if external_ip.lower() == "ephemeral": + external_ip = "ephemeral" + elif external_ip == "None": external_ip = None else: region = __get_region(conn, vm_) external_ip = __create_orget_address(conn, external_ip, region) - kwargs['external_ip'] = external_ip - vm_['external_ip'] = external_ip + kwargs["external_ip"] = external_ip + vm_["external_ip"] = external_ip if LIBCLOUD_VERSION_INFO > (0, 15, 1): - kwargs.update({ - 'ex_disk_type': config.get_cloud_config_value( - 'ex_disk_type', vm_, __opts__, default='pd-standard'), - 'ex_disk_auto_delete': config.get_cloud_config_value( - 'ex_disk_auto_delete', vm_, __opts__, default=True), - 'ex_disks_gce_struct': config.get_cloud_config_value( - 'ex_disks_gce_struct', vm_, __opts__, default=None), - 'ex_service_accounts': config.get_cloud_config_value( - 'ex_service_accounts', vm_, __opts__, default=None), - 'ex_can_ip_forward': config.get_cloud_config_value( - 'ip_forwarding', vm_, __opts__, default=False), - 'ex_preemptible': config.get_cloud_config_value( - 'preemptible', vm_, __opts__, default=False) - }) - if kwargs.get('ex_disk_type') not in ('pd-standard', 'pd-ssd'): + kwargs.update( + { + "ex_disk_type": config.get_cloud_config_value( + "ex_disk_type", vm_, __opts__, default="pd-standard" + ), + "ex_disk_auto_delete": config.get_cloud_config_value( + "ex_disk_auto_delete", vm_, __opts__, default=True + ), + "ex_disks_gce_struct": config.get_cloud_config_value( + "ex_disks_gce_struct", vm_, __opts__, default=None + ), + "ex_service_accounts": config.get_cloud_config_value( + "ex_service_accounts", vm_, __opts__, default=None + ), + "ex_can_ip_forward": config.get_cloud_config_value( + "ip_forwarding", vm_, __opts__, default=False + ), + "ex_preemptible": config.get_cloud_config_value( + "preemptible", vm_, __opts__, default=False + ), + } + ) + if kwargs.get("ex_disk_type") not in ("pd-standard", "pd-ssd"): raise SaltCloudSystemExit( - 'The value of \'ex_disk_type\' needs to be one of: ' - '\'pd-standard\', \'pd-ssd\'' + "The value of 'ex_disk_type' needs to be one of: " + "'pd-standard', 'pd-ssd'" ) - log.info( - 'Creating GCE instance %s in %s', - vm_['name'], kwargs['location'].name - ) - log.debug('Create instance kwargs %s', kwargs) + log.info("Creating GCE instance %s in %s", vm_["name"], kwargs["location"].name) + log.debug("Create instance kwargs %s", kwargs) - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']('requesting', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "requesting", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: node_data = conn.create_node(**kwargs) except Exception as exc: # pylint: disable=W0703 log.error( - 'Error creating %s on GCE\n\n' - 'The following exception was thrown by libcloud when trying to ' - 'run the initial deployment: \n%s', - vm_['name'], exc, exc_info_on_loglevel=logging.DEBUG + "Error creating %s on GCE\n\n" + "The following exception was thrown by libcloud when trying to " + "run the initial deployment: \n%s", + vm_["name"], + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False volumes = config.get_cloud_config_value( - 'volumes', vm_, __opts__, search_global=True + "volumes", vm_, __opts__, search_global=True ) if volumes: - __utils__['cloud.fire_event']( - 'event', - 'attaching volumes', - 'salt/cloud/{0}/attaching_volumes'.format(vm_['name']), - args={'volumes': volumes}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "attaching volumes", + "salt/cloud/{0}/attaching_volumes".format(vm_["name"]), + args={"volumes": volumes}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Create and attach volumes to node %s', vm_['name']) + log.info("Create and attach volumes to node %s", vm_["name"]) create_attach_volumes( - vm_['name'], - { - 'volumes': volumes, - 'node': node_data - }, - call='action' + vm_["name"], {"volumes": volumes, "node": node_data}, call="action" ) try: - node_dict = show_instance(node_data['name'], 'action') + node_dict = show_instance(node_data["name"], "action") except TypeError: # node_data is a libcloud Node which is unsubscriptable - node_dict = show_instance(node_data.name, 'action') + node_dict = show_instance(node_data.name, "action") return node_dict, node_data def create(vm_=None, call=None): - ''' + """ Create a single GCE instance from a data dict. - ''' + """ if call: - raise SaltCloudSystemExit( - 'You cannot create an instance with -a or -f.' - ) + raise SaltCloudSystemExit("You cannot create an instance with -a or -f.") node_info = request_instance(vm_) if isinstance(node_info, bool): - raise SaltCloudSystemExit( - 'There was an error creating the GCE instance.' - ) + raise SaltCloudSystemExit("There was an error creating the GCE instance.") node_dict = node_info[0] node_data = node_info[1] ssh_user, ssh_key = __get_ssh_credentials(vm_) - vm_['ssh_host'] = __get_host(node_data, vm_) - vm_['key_filename'] = ssh_key + vm_["ssh_host"] = __get_host(node_data, vm_) + vm_["key_filename"] = ssh_key - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(node_dict) - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.trace( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(node_dict) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.trace("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(node_dict)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def update_pricing(kwargs=None, call=None): - ''' + """ Download most recent pricing information from GCE and save locally CLI Examples: @@ -2621,21 +2476,19 @@ def update_pricing(kwargs=None, call=None): salt-cloud -f update_pricing my-gce-config .. versionadded:: 2015.8.0 - ''' - url = 'https://cloudpricingcalculator.appspot.com/static/data/pricelist.json' - price_json = salt.utils.http.query(url, decode=True, decode_type='json') + """ + url = "https://cloudpricingcalculator.appspot.com/static/data/pricelist.json" + price_json = salt.utils.http.query(url, decode=True, decode_type="json") - outfile = os.path.join( - __opts__['cachedir'], 'gce-pricing.p' - ) - with salt.utils.files.fopen(outfile, 'w') as fho: - salt.utils.msgpack.dump(price_json['dict'], fho) + outfile = os.path.join(__opts__["cachedir"], "gce-pricing.p") + with salt.utils.files.fopen(outfile, "w") as fho: + salt.utils.msgpack.dump(price_json["dict"], fho) return True def show_pricing(kwargs=None, call=None): - ''' + """ Show pricing for a particular profile. This is only an estimate, based on unofficial pricing sources. @@ -2646,51 +2499,49 @@ def show_pricing(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_pricing my-gce-config profile=my-profile - ''' - profile = __opts__['profiles'].get(kwargs['profile'], {}) + """ + profile = __opts__["profiles"].get(kwargs["profile"], {}) if not profile: - return {'Error': 'The requested profile was not found'} + return {"Error": "The requested profile was not found"} # Make sure the profile belongs to DigitalOcean - provider = profile.get('provider', '0:0') - comps = provider.split(':') - if len(comps) < 2 or comps[1] != 'gce': - return {'Error': 'The requested profile does not belong to GCE'} + provider = profile.get("provider", "0:0") + comps = provider.split(":") + if len(comps) < 2 or comps[1] != "gce": + return {"Error": "The requested profile does not belong to GCE"} - comps = profile.get('location', 'us').split('-') + comps = profile.get("location", "us").split("-") region = comps[0] - size = 'CP-COMPUTEENGINE-VMIMAGE-{0}'.format(profile['size'].upper()) - pricefile = os.path.join( - __opts__['cachedir'], 'gce-pricing.p' - ) + size = "CP-COMPUTEENGINE-VMIMAGE-{0}".format(profile["size"].upper()) + pricefile = os.path.join(__opts__["cachedir"], "gce-pricing.p") if not os.path.exists(pricefile): update_pricing() - with salt.utils.files.fopen(pricefile, 'r') as fho: + with salt.utils.files.fopen(pricefile, "r") as fho: sizes = salt.utils.msgpack.load(fho) - per_hour = float(sizes['gcp_price_list'][size][region]) + per_hour = float(sizes["gcp_price_list"][size][region]) - week1_discount = float(sizes['gcp_price_list']['sustained_use_tiers']['0.25']) - week2_discount = float(sizes['gcp_price_list']['sustained_use_tiers']['0.50']) - week3_discount = float(sizes['gcp_price_list']['sustained_use_tiers']['0.75']) - week4_discount = float(sizes['gcp_price_list']['sustained_use_tiers']['1.0']) - week1 = per_hour * (730/4) * week1_discount - week2 = per_hour * (730/4) * week2_discount - week3 = per_hour * (730/4) * week3_discount - week4 = per_hour * (730/4) * week4_discount + week1_discount = float(sizes["gcp_price_list"]["sustained_use_tiers"]["0.25"]) + week2_discount = float(sizes["gcp_price_list"]["sustained_use_tiers"]["0.50"]) + week3_discount = float(sizes["gcp_price_list"]["sustained_use_tiers"]["0.75"]) + week4_discount = float(sizes["gcp_price_list"]["sustained_use_tiers"]["1.0"]) + week1 = per_hour * (730 / 4) * week1_discount + week2 = per_hour * (730 / 4) * week2_discount + week3 = per_hour * (730 / 4) * week3_discount + week4 = per_hour * (730 / 4) * week4_discount raw = sizes ret = {} - ret['per_hour'] = per_hour - ret['per_day'] = ret['per_hour'] * 24 - ret['per_week'] = ret['per_day'] * 7 - ret['per_month'] = week1 + week2 + week3 + week4 - ret['per_year'] = ret['per_month'] * 12 + ret["per_hour"] = per_hour + ret["per_day"] = ret["per_hour"] * 24 + ret["per_week"] = ret["per_day"] * 7 + ret["per_month"] = week1 + week2 + week3 + week4 + ret["per_year"] = ret["per_month"] * 12 - if kwargs.get('raw', False): - ret['_raw'] = raw + if kwargs.get("raw", False): + ret["_raw"] = raw - return {profile['profile']: ret} + return {profile["profile"]: ret} diff --git a/salt/cloud/clouds/gogrid.py b/salt/cloud/clouds/gogrid.py index 190a46aefd0..20936dbf5e1 100644 --- a/salt/cloud/clouds/gogrid.py +++ b/salt/cloud/clouds/gogrid.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" GoGrid Cloud Module ==================== @@ -36,32 +36,33 @@ Set up the cloud configuration at ``/etc/salt/cloud.providers`` or with the GoGrid driver. Map files will work with GoGrid, but the ``-P`` argument should not be used on maps referencing GoGrid instances. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import pprint -import logging import time # Import salt cloud libs import salt.config as config import salt.utils.cloud import salt.utils.hashutils -from salt.exceptions import SaltCloudSystemExit, SaltCloudException +from salt.exceptions import SaltCloudException, SaltCloudSystemExit from salt.ext import six # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'gogrid' +__virtualname__ = "gogrid" # Only load in this module if the GoGrid configurations are in place def __virtual__(): - ''' + """ Check for GoGrid configs - ''' + """ if get_configured_provider() is False: return False @@ -69,128 +70,136 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('apikey', 'sharedsecret') + ("apikey", "sharedsecret"), ) def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'gogrid', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "gogrid", vm_["profile"], vm_=vm_ + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if len(vm_['name']) > 20: - raise SaltCloudException('VM names must not be longer than 20 characters') + if len(vm_["name"]) > 20: + raise SaltCloudException("VM names must not be longer than 20 characters") - log.info('Creating Cloud VM %s', vm_['name']) - image_id = avail_images()[vm_['image']]['id'] - if 'assign_public_ip' in vm_: - host_ip = vm_['assign_public_ip'] + log.info("Creating Cloud VM %s", vm_["name"]) + image_id = avail_images()[vm_["image"]]["id"] + if "assign_public_ip" in vm_: + host_ip = vm_["assign_public_ip"] else: public_ips = list_public_ips() if not public_ips: - raise SaltCloudException('No more IPs available') + raise SaltCloudException("No more IPs available") host_ip = next(iter(public_ips)) create_kwargs = { - 'name': vm_['name'], - 'image': image_id, - 'ram': vm_['size'], - 'ip': host_ip, + "name": vm_["name"], + "image": image_id, + "ram": vm_["size"], + "ip": host_ip, } - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', create_kwargs, list(create_kwargs)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", create_kwargs, list(create_kwargs) + ), }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: - data = _query('grid', 'server/add', args=create_kwargs) + data = _query("grid", "server/add", args=create_kwargs) except Exception: # pylint: disable=broad-except log.error( - 'Error creating %s on GOGRID\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment:\n', vm_['name'], + "Error creating %s on GOGRID\n\n" + "The following exception was thrown when trying to " + "run the initial deployment:\n", + vm_["name"], # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False ssh_username = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, default='root' + "ssh_username", vm_, __opts__, default="root" ) def wait_for_apipass(): - ''' + """ Wait for the password to become available, via the API - ''' + """ try: passwords = list_passwords() - return passwords[vm_['name']][0]['password'] + return passwords[vm_["name"]][0]["password"] except KeyError: pass time.sleep(5) return False - vm_['password'] = salt.utils.cloud.wait_for_fun( + vm_["password"] = salt.utils.cloud.wait_for_fun( wait_for_apipass, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) - vm_['ssh_host'] = host_ip - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + vm_["ssh_host"] = host_ip + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data) - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def list_nodes(full=False, call=None): - ''' + """ List of nodes, keeping only a brief listing CLI Example: @@ -198,27 +207,27 @@ def list_nodes(full=False, call=None): .. code-block:: bash salt-cloud -Q - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} - nodes = list_nodes_full('function') + nodes = list_nodes_full("function") if full: return nodes for node in nodes: ret[node] = {} - for item in ('id', 'image', 'size', 'public_ips', 'private_ips', 'state'): + for item in ("id", "image", "size", "public_ips", "private_ips", "state"): ret[node][item] = nodes[node][item] return ret def list_nodes_full(call=None): - ''' + """ List nodes, with all available information CLI Example: @@ -226,28 +235,28 @@ def list_nodes_full(call=None): .. code-block:: bash salt-cloud -F - ''' - response = _query('grid', 'server/list') + """ + response = _query("grid", "server/list") ret = {} - for item in response['list']: - name = item['name'] + for item in response["list"]: + name = item["name"] ret[name] = item - ret[name]['image_info'] = item['image'] - ret[name]['image'] = item['image']['friendlyName'] - ret[name]['size'] = item['ram']['name'] - ret[name]['public_ips'] = [item['ip']['ip']] - ret[name]['private_ips'] = [] - ret[name]['state_info'] = item['state'] - if 'active' in item['state']['description']: - ret[name]['state'] = 'RUNNING' + ret[name]["image_info"] = item["image"] + ret[name]["image"] = item["image"]["friendlyName"] + ret[name]["size"] = item["ram"]["name"] + ret[name]["public_ips"] = [item["ip"]["ip"]] + ret[name]["private_ips"] = [] + ret[name]["state_info"] = item["state"] + if "active" in item["state"]["description"]: + ret[name]["state"] = "RUNNING" return ret def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields CLI Example: @@ -255,66 +264,66 @@ def list_nodes_select(call=None): .. code-block:: bash salt-cloud -S - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full('function'), __opts__['query.selection'], call, + list_nodes_full("function"), __opts__["query.selection"], call, ) def avail_locations(): - ''' + """ Available locations - ''' - response = list_common_lookups(kwargs={'lookup': 'ip.datacenter'}) + """ + response = list_common_lookups(kwargs={"lookup": "ip.datacenter"}) ret = {} - for item in response['list']: - name = item['name'] + for item in response["list"]: + name = item["name"] ret[name] = item return ret def avail_sizes(): - ''' + """ Available sizes - ''' - response = list_common_lookups(kwargs={'lookup': 'server.ram'}) + """ + response = list_common_lookups(kwargs={"lookup": "server.ram"}) ret = {} - for item in response['list']: - name = item['name'] + for item in response["list"]: + name = item["name"] ret[name] = item return ret def avail_images(): - ''' + """ Available images - ''' - response = _query('grid', 'image/list') + """ + response = _query("grid", "image/list") ret = {} - for item in response['list']: - name = item['friendlyName'] + for item in response["list"]: + name = item["friendlyName"] ret[name] = item return ret def list_passwords(kwargs=None, call=None): - ''' + """ List all password on the account .. versionadded:: 2015.8.0 - ''' - response = _query('support', 'password/list') + """ + response = _query("support", "password/list") ret = {} - for item in response['list']: - if 'server' in item: - server = item['server']['name'] + for item in response["list"]: + if "server" in item: + server = item["server"]["name"] if server not in ret: ret[server] = [] ret[server].append(item) @@ -323,7 +332,7 @@ def list_passwords(kwargs=None, call=None): def list_public_ips(kwargs=None, call=None): - ''' + """ List all available public IPs. CLI Example: @@ -339,51 +348,51 @@ def list_public_ips(kwargs=None, call=None): salt-cloud -f list_public_ips state=assigned .. versionadded:: 2015.8.0 - ''' + """ if kwargs is None: kwargs = {} args = {} - if 'state' in kwargs: - if kwargs['state'] == 'assigned': - args['ip.state'] = 'Assigned' + if "state" in kwargs: + if kwargs["state"] == "assigned": + args["ip.state"] = "Assigned" else: - args['ip.state'] = 'Unassigned' + args["ip.state"] = "Unassigned" else: - args['ip.state'] = 'Unassigned' + args["ip.state"] = "Unassigned" - args['ip.type'] = 'Public' + args["ip.type"] = "Public" - response = _query('grid', 'ip/list', args=args) + response = _query("grid", "ip/list", args=args) ret = {} - for item in response['list']: - name = item['ip'] + for item in response["list"]: + name = item["ip"] ret[name] = item return ret def list_common_lookups(kwargs=None, call=None): - ''' + """ List common lookups for a particular type of item .. versionadded:: 2015.8.0 - ''' + """ if kwargs is None: kwargs = {} args = {} - if 'lookup' in kwargs: - args['lookup'] = kwargs['lookup'] + if "lookup" in kwargs: + args["lookup"] = kwargs["lookup"] - response = _query('common', 'lookup/list', args=args) + response = _query("common", "lookup/list", args=args) return response def destroy(name, call=None): - ''' + """ Destroy a machine by name CLI Example: @@ -391,41 +400,42 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud -d vm_name - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - response = _query('grid', 'server/delete', args={'name': name}) + response = _query("grid", "server/delete", args={"name": name}) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) return response def reboot(name, call=None): - ''' + """ Reboot a machine by name CLI Example: @@ -435,12 +445,12 @@ def reboot(name, call=None): salt-cloud -a reboot vm_name .. versionadded:: 2015.8.0 - ''' - return _query('grid', 'server/power', args={'name': name, 'power': 'restart'}) + """ + return _query("grid", "server/power", args={"name": name, "power": "restart"}) def stop(name, call=None): - ''' + """ Stop a machine by name CLI Example: @@ -450,12 +460,12 @@ def stop(name, call=None): salt-cloud -a stop vm_name .. versionadded:: 2015.8.0 - ''' - return _query('grid', 'server/power', args={'name': name, 'power': 'stop'}) + """ + return _query("grid", "server/power", args={"name": name, "power": "stop"}) def start(name, call=None): - ''' + """ Start a machine by name CLI Example: @@ -465,12 +475,12 @@ def start(name, call=None): salt-cloud -a start vm_name .. versionadded:: 2015.8.0 - ''' - return _query('grid', 'server/power', args={'name': name, 'power': 'start'}) + """ + return _query("grid", "server/power", args={"name": name, "power": "start"}) def show_instance(name, call=None): - ''' + """ Start a machine by name CLI Example: @@ -480,71 +490,66 @@ def show_instance(name, call=None): salt-cloud -a show_instance vm_name .. versionadded:: 2015.8.0 - ''' - response = _query('grid', 'server/get', args={'name': name}) + """ + response = _query("grid", "server/get", args={"name": name}) ret = {} - for item in response['list']: - name = item['name'] + for item in response["list"]: + name = item["name"] ret[name] = item - ret[name]['image_info'] = item['image'] - ret[name]['image'] = item['image']['friendlyName'] - ret[name]['size'] = item['ram']['name'] - ret[name]['public_ips'] = [item['ip']['ip']] - ret[name]['private_ips'] = [] - ret[name]['state_info'] = item['state'] - if 'active' in item['state']['description']: - ret[name]['state'] = 'RUNNING' + ret[name]["image_info"] = item["image"] + ret[name]["image"] = item["image"]["friendlyName"] + ret[name]["size"] = item["ram"]["name"] + ret[name]["public_ips"] = [item["ip"]["ip"]] + ret[name]["private_ips"] = [] + ret[name]["state_info"] = item["state"] + if "active" in item["state"]["description"]: + ret[name]["state"] = "RUNNING" return ret -def _query(action=None, - command=None, - args=None, - method='GET', - header_dict=None, - data=None): - ''' +def _query( + action=None, command=None, args=None, method="GET", header_dict=None, data=None +): + """ Make a web call to GoGrid .. versionadded:: 2015.8.0 - ''' + """ vm_ = get_configured_provider() - apikey = config.get_cloud_config_value( - 'apikey', vm_, __opts__, search_global=False - ) + apikey = config.get_cloud_config_value("apikey", vm_, __opts__, search_global=False) sharedsecret = config.get_cloud_config_value( - 'sharedsecret', vm_, __opts__, search_global=False + "sharedsecret", vm_, __opts__, search_global=False ) - path = 'https://api.gogrid.com/api/' + path = "https://api.gogrid.com/api/" if action: path += action if command: - path += '/{0}'.format(command) + path += "/{0}".format(command) - log.debug('GoGrid URL: %s', path) + log.debug("GoGrid URL: %s", path) if not isinstance(args, dict): args = {} epoch = six.text_type(int(time.time())) - hashtext = ''.join((apikey, sharedsecret, epoch)) - args['sig'] = salt.utils.hashutils.md5_digest(hashtext) - args['format'] = 'json' - args['v'] = '1.0' - args['api_key'] = apikey + hashtext = "".join((apikey, sharedsecret, epoch)) + args["sig"] = salt.utils.hashutils.md5_digest(hashtext) + args["format"] = "json" + args["v"] = "1.0" + args["api_key"] = apikey if header_dict is None: header_dict = {} - if method != 'POST': - header_dict['Accept'] = 'application/json' + if method != "POST": + header_dict["Accept"] = "application/json" decode = True - if method == 'DELETE': + if method == "DELETE": decode = False return_content = None @@ -555,11 +560,11 @@ def _query(action=None, data=data, header_dict=header_dict, decode=decode, - decode_type='json', + decode_type="json", text=True, status=True, opts=__opts__, ) - log.debug('GoGrid Response Status Code: %s', result['status']) + log.debug("GoGrid Response Status Code: %s", result["status"]) - return result['dict'] + return result["dict"] diff --git a/salt/cloud/clouds/joyent.py b/salt/cloud/clouds/joyent.py index e5bb0468903..45e8b77ffec 100644 --- a/salt/cloud/clouds/joyent.py +++ b/salt/cloud/clouds/joyent.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Joyent Cloud Module =================== @@ -47,20 +47,42 @@ included: api_host_suffix: .api.myhostname.com :depends: PyCrypto -''' +""" # pylint: disable=invalid-name,function-redefined # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import logging + import base64 -import pprint -import inspect import datetime +import inspect +import logging +import os +import pprint + +import salt.config as config +import salt.utils.cloud +import salt.utils.files +import salt.utils.http +import salt.utils.json +import salt.utils.yaml +from salt.cloud.libcloudfuncs import node_state +from salt.exceptions import ( + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, + SaltCloudNotFound, + SaltCloudSystemExit, +) + +# Import salt libs +from salt.ext import six +from salt.ext.six.moves import ( # pylint: disable=import-error,no-name-in-module + http_client, +) try: from M2Crypto import EVP + HAS_REQUIRED_CRYPTO = True HAS_M2 = True except ImportError: @@ -68,47 +90,33 @@ except ImportError: try: from Cryptodome.Hash import SHA256 from Cryptodome.Signature import PKCS1_v1_5 + HAS_REQUIRED_CRYPTO = True except ImportError: try: from Crypto.Hash import SHA256 from Crypto.Signature import PKCS1_v1_5 + HAS_REQUIRED_CRYPTO = True except ImportError: HAS_REQUIRED_CRYPTO = False -# Import salt libs -from salt.ext import six -from salt.ext.six.moves import http_client # pylint: disable=import-error,no-name-in-module -import salt.utils.cloud -import salt.utils.files -import salt.utils.http -import salt.utils.json -import salt.utils.yaml -import salt.config as config -from salt.cloud.libcloudfuncs import node_state -from salt.exceptions import ( - SaltCloudSystemExit, - SaltCloudExecutionFailure, - SaltCloudExecutionTimeout, - SaltCloudNotFound, -) # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'joyent' +__virtualname__ = "joyent" -JOYENT_API_HOST_SUFFIX = '.api.joyentcloud.com' -JOYENT_API_VERSION = '~7.2' +JOYENT_API_HOST_SUFFIX = ".api.joyentcloud.com" +JOYENT_API_VERSION = "~7.2" JOYENT_LOCATIONS = { - 'us-east-1': 'North Virginia, USA', - 'us-west-1': 'Bay Area, California, USA', - 'us-sw-1': 'Las Vegas, Nevada, USA', - 'eu-ams-1': 'Amsterdam, Netherlands' + "us-east-1": "North Virginia, USA", + "us-west-1": "Bay Area, California, USA", + "us-sw-1": "Las Vegas, Nevada, USA", + "eu-ams-1": "Amsterdam, Netherlands", } -DEFAULT_LOCATION = 'us-east-1' +DEFAULT_LOCATION = "us-east-1" # joyent no longer reports on all data centers, so setting this value to true # causes the list_nodes function to get information on machines from all @@ -119,17 +127,17 @@ VALID_RESPONSE_CODES = [ http_client.OK, http_client.ACCEPTED, http_client.CREATED, - http_client.NO_CONTENT + http_client.NO_CONTENT, ] # Only load in this module if the Joyent configurations are in place def __virtual__(): - ''' + """ Check for Joyent configs - ''' + """ if HAS_REQUIRED_CRYPTO is False: - return False, 'Either PyCrypto or Cryptodome needs to be installed.' + return False, "Either PyCrypto or Cryptodome needs to be installed." if get_configured_provider() is False: return False @@ -137,103 +145,102 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('user', 'password') + __opts__, __active_provider_name__ or __virtualname__, ("user", "password") ) def get_image(vm_): - ''' + """ Return the image object to use - ''' + """ images = avail_images() - vm_image = config.get_cloud_config_value('image', vm_, __opts__) + vm_image = config.get_cloud_config_value("image", vm_, __opts__) if vm_image and six.text_type(vm_image) in images: - images[vm_image]['name'] = images[vm_image]['id'] + images[vm_image]["name"] = images[vm_image]["id"] return images[vm_image] raise SaltCloudNotFound( - 'The specified image, \'{0}\', could not be found.'.format(vm_image) + "The specified image, '{0}', could not be found.".format(vm_image) ) def get_size(vm_): - ''' + """ Return the VM's size object - ''' + """ sizes = avail_sizes() - vm_size = config.get_cloud_config_value('size', vm_, __opts__) + vm_size = config.get_cloud_config_value("size", vm_, __opts__) if not vm_size: - raise SaltCloudNotFound('No size specified for this VM.') + raise SaltCloudNotFound("No size specified for this VM.") if vm_size and six.text_type(vm_size) in sizes: return sizes[vm_size] raise SaltCloudNotFound( - 'The specified size, \'{0}\', could not be found.'.format(vm_size) + "The specified size, '{0}', could not be found.".format(vm_size) ) def query_instance(vm_=None, call=None): - ''' + """ Query an instance upon creation from the Joyent API - ''' - if isinstance(vm_, six.string_types) and call == 'action': - vm_ = {'name': vm_, 'provider': 'joyent'} + """ + if isinstance(vm_, six.string_types) and call == "action": + vm_ = {"name": vm_, "provider": "joyent"} - if call == 'function': + if call == "function": # Technically this function may be called other ways too, but it # definitely cannot be called with --function. raise SaltCloudSystemExit( - 'The query_instance action must be called with -a or --action.' + "The query_instance action must be called with -a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'querying instance', - 'salt/cloud/{0}/querying'.format(vm_['name']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "querying instance", + "salt/cloud/{0}/querying".format(vm_["name"]), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) def _query_ip_address(): - data = show_instance(vm_['name'], call='action') + data = show_instance(vm_["name"], call="action") if not data: - log.error( - 'There was an error while querying Joyent. Empty response' - ) + log.error("There was an error while querying Joyent. Empty response") # Trigger a failure in the wait for IP function return False - if isinstance(data, dict) and 'error' in data: - log.warning('There was an error in the query %s', data.get('error')) + if isinstance(data, dict) and "error" in data: + log.warning("There was an error in the query %s", data.get("error")) # Trigger a failure in the wait for IP function return False - log.debug('Returned query data: %s', data) + log.debug("Returned query data: %s", data) - if 'primaryIp' in data[1]: + if "primaryIp" in data[1]: # Wait for SSH to be fully configured on the remote side - if data[1]['state'] == 'running': - return data[1]['primaryIp'] + if data[1]["state"] == "running": + return data[1]["primaryIp"] return None try: data = salt.utils.cloud.wait_for_ip( _query_ip_address, timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10), + "wait_for_ip_interval", vm_, __opts__, default=10 + ), interval_multiplier=config.get_cloud_config_value( - 'wait_for_ip_interval_multiplier', vm_, __opts__, default=1), + "wait_for_ip_interval_multiplier", vm_, __opts__, default=1 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: @@ -248,7 +255,7 @@ def query_instance(vm_=None, call=None): def create(vm_): - ''' + """ Create a single VM from a data dict CLI Example: @@ -256,134 +263,141 @@ def create(vm_): .. code-block:: bash salt-cloud -p profile_name vm_name - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'joyent', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "joyent", vm_["profile"], vm_=vm_ + ) + is False + ): return False except AttributeError: pass key_filename = config.get_cloud_config_value( - 'private_key', vm_, __opts__, search_global=False, default=None + "private_key", vm_, __opts__, search_global=False, default=None ) - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) log.info( - 'Creating Cloud VM %s in %s', - vm_['name'], vm_.get('location', DEFAULT_LOCATION) + "Creating Cloud VM %s in %s", vm_["name"], vm_.get("location", DEFAULT_LOCATION) ) # added . for fqdn hostnames - salt.utils.cloud.check_name(vm_['name'], 'a-zA-Z0-9-.') + salt.utils.cloud.check_name(vm_["name"], "a-zA-Z0-9-.") kwargs = { - 'name': vm_['name'], - 'image': get_image(vm_), - 'size': get_size(vm_), - 'location': vm_.get('location', DEFAULT_LOCATION) + "name": vm_["name"], + "image": get_image(vm_), + "size": get_size(vm_), + "location": vm_.get("location", DEFAULT_LOCATION), } # Let's not assign a default here; only assign a network value if # one is explicitly configured - if 'networks' in vm_: - kwargs['networks'] = vm_.get('networks') + if "networks" in vm_: + kwargs["networks"] = vm_.get("networks") - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', kwargs, list(kwargs)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", kwargs, list(kwargs) + ), }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) data = create_node(**kwargs) if data == {}: - log.error('Error creating %s on JOYENT', vm_['name']) + log.error("Error creating %s on JOYENT", vm_["name"]) return False query_instance(vm_) - data = show_instance(vm_['name'], call='action') + data = show_instance(vm_["name"], call="action") - vm_['key_filename'] = key_filename - vm_['ssh_host'] = data[1]['primaryIp'] + vm_["key_filename"] = key_filename + vm_["ssh_host"] = data[1]["primaryIp"] - __utils__['cloud.bootstrap'](vm_, __opts__) + __utils__["cloud.bootstrap"](vm_, __opts__) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return data[1] def create_node(**kwargs): - ''' + """ convenience function to make the rest api call for node creation. - ''' - name = kwargs['name'] - size = kwargs['size'] - image = kwargs['image'] - location = kwargs['location'] - networks = kwargs.get('networks') - tag = kwargs.get('tag') - locality = kwargs.get('locality') - metadata = kwargs.get('metadata') - firewall_enabled = kwargs.get('firewall_enabled') + """ + name = kwargs["name"] + size = kwargs["size"] + image = kwargs["image"] + location = kwargs["location"] + networks = kwargs.get("networks") + tag = kwargs.get("tag") + locality = kwargs.get("locality") + metadata = kwargs.get("metadata") + firewall_enabled = kwargs.get("firewall_enabled") create_data = { - 'name': name, - 'package': size['name'], - 'image': image['name'], + "name": name, + "package": size["name"], + "image": image["name"], } if networks is not None: - create_data['networks'] = networks + create_data["networks"] = networks if locality is not None: - create_data['locality'] = locality + create_data["locality"] = locality if metadata is not None: for key, value in six.iteritems(metadata): - create_data['metadata.{0}'.format(key)] = value + create_data["metadata.{0}".format(key)] = value if tag is not None: for key, value in six.iteritems(tag): - create_data['tag.{0}'.format(key)] = value + create_data["tag.{0}".format(key)] = value if firewall_enabled is not None: - create_data['firewall_enabled'] = firewall_enabled + create_data["firewall_enabled"] = firewall_enabled data = salt.utils.json.dumps(create_data) - ret = query(command='my/machines', data=data, method='POST', - location=location) + ret = query(command="my/machines", data=data, method="POST", location=location) if ret[0] in VALID_RESPONSE_CODES: return ret[1] else: - log.error('Failed to create node %s: %s', name, ret[1]) + log.error("Failed to create node %s: %s", name, ret[1]) return {} def destroy(name, call=None): - ''' + """ destroy a machine by name :param name: name given to the machine @@ -397,43 +411,47 @@ def destroy(name, call=None): salt-cloud -d vm_name - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) node = get_node(name) - ret = query(command='my/machines/{0}'.format(node['id']), - location=node['location'], method='DELETE') - - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + ret = query( + command="my/machines/{0}".format(node["id"]), + location=node["location"], + method="DELETE", ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], + ) + + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) return ret[0] in VALID_RESPONSE_CODES def reboot(name, call=None): - ''' + """ reboot a machine by name :param name: name given to the machine :param call: call value in this case is 'action' @@ -444,16 +462,21 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot vm_name - ''' + """ node = get_node(name) - ret = take_action(name=name, call=call, method='POST', - command='my/machines/{0}'.format(node['id']), - location=node['location'], data={'action': 'reboot'}) + ret = take_action( + name=name, + call=call, + method="POST", + command="my/machines/{0}".format(node["id"]), + location=node["location"], + data={"action": "reboot"}, + ) return ret[0] in VALID_RESPONSE_CODES def stop(name, call=None): - ''' + """ stop a machine by name :param name: name given to the machine :param call: call value in this case is 'action' @@ -464,16 +487,21 @@ def stop(name, call=None): .. code-block:: bash salt-cloud -a stop vm_name - ''' + """ node = get_node(name) - ret = take_action(name=name, call=call, method='POST', - command='my/machines/{0}'.format(node['id']), - location=node['location'], data={'action': 'stop'}) + ret = take_action( + name=name, + call=call, + method="POST", + command="my/machines/{0}".format(node["id"]), + location=node["location"], + data={"action": "stop"}, + ) return ret[0] in VALID_RESPONSE_CODES def start(name, call=None): - ''' + """ start a machine by name :param name: name given to the machine :param call: call value in this case is 'action' @@ -485,18 +513,29 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start vm_name - ''' + """ node = get_node(name) - ret = take_action(name=name, call=call, method='POST', - command='my/machines/{0}'.format(node['id']), - location=node['location'], data={'action': 'start'}) + ret = take_action( + name=name, + call=call, + method="POST", + command="my/machines/{0}".format(node["id"]), + location=node["location"], + data={"action": "start"}, + ) return ret[0] in VALID_RESPONSE_CODES -def take_action(name=None, call=None, command=None, data=None, method='GET', - location=DEFAULT_LOCATION): +def take_action( + name=None, + call=None, + command=None, + data=None, + method="GET", + location=DEFAULT_LOCATION, +): - ''' + """ take action call used by start,stop, reboot :param name: name given to the machine :param call: call value in this case is 'action' @@ -505,13 +544,11 @@ def take_action(name=None, call=None, command=None, data=None, method='GET', :method: GET,POST,or DELETE :location: data center to execute the command on :return: true if successful - ''' + """ caller = inspect.stack()[1][3] - if call != 'action': - raise SaltCloudSystemExit( - 'This action must be called with -a or --action.' - ) + if call != "action": + raise SaltCloudSystemExit("This action must be called with -a or --action.") if data: data = salt.utils.json.dumps(data) @@ -519,17 +556,19 @@ def take_action(name=None, call=None, command=None, data=None, method='GET', ret = [] try: - ret = query(command=command, data=data, method=method, - location=location) - log.info('Success %s for node %s', caller, name) + ret = query(command=command, data=data, method=method, location=location) + log.info("Success %s for node %s", caller, name) except Exception as exc: # pylint: disable=broad-except - if 'InvalidState' in six.text_type(exc): + if "InvalidState" in six.text_type(exc): ret = [200, {}] else: log.error( - 'Failed to invoke %s node %s: %s', caller, name, exc, + "Failed to invoke %s node %s: %s", + caller, + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) ret = [100, {}] @@ -537,51 +576,47 @@ def take_action(name=None, call=None, command=None, data=None, method='GET', def ssh_interface(vm_): - ''' + """ Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. - ''' + """ return config.get_cloud_config_value( - 'ssh_interface', vm_, __opts__, default='public_ips', - search_global=False + "ssh_interface", vm_, __opts__, default="public_ips", search_global=False ) def get_location(vm_=None): - ''' + """ Return the joyent data center to use, in this order: - CLI parameter - VM parameter - Cloud profile setting - ''' + """ return __opts__.get( - 'location', + "location", config.get_cloud_config_value( - 'location', + "location", vm_ or get_configured_provider(), __opts__, default=DEFAULT_LOCATION, - search_global=False - ) + search_global=False, + ), ) def avail_locations(call=None): - ''' + """ List all available locations - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) ret = {} for key in JOYENT_LOCATIONS: - ret[key] = { - 'name': key, - 'region': JOYENT_LOCATIONS[key] - } + ret[key] = {"name": key, "region": JOYENT_LOCATIONS[key]} # this can be enabled when the bug in the joyent get data centers call is # corrected, currently only the European dc (new api) returns the correct @@ -600,42 +635,42 @@ def avail_locations(call=None): def has_method(obj, method_name): - ''' + """ Find if the provided object has a specific method - ''' + """ if method_name in dir(obj): return True - log.error('Method \'%s\' not yet supported!', method_name) + log.error("Method '%s' not yet supported!", method_name) return False def key_list(items=None): - ''' + """ convert list to dictionary using the key as the identifier :param items: array to iterate over :return: dictionary - ''' + """ if items is None: items = [] ret = {} if items and isinstance(items, list): for item in items: - if 'name' in item: + if "name" in item: # added for consistency with old code - if 'id' not in item: - item['id'] = item['name'] - ret[item['name']] = item + if "id" not in item: + item["id"] = item["name"] + ret[item["name"]] = item return ret def get_node(name): - ''' + """ gets the node from the full node list by name :param name: name of the vm :return: node object - ''' + """ nodes = list_nodes() if name in nodes: return nodes[name] @@ -643,7 +678,7 @@ def get_node(name): def show_instance(name, call=None): - ''' + """ get details about a machine :param name: name given to the machine :param call: call value in this case is 'action' @@ -654,56 +689,67 @@ def show_instance(name, call=None): .. code-block:: bash salt-cloud -a show_instance vm_name - ''' + """ node = get_node(name) - ret = query(command='my/machines/{0}'.format(node['id']), - location=node['location'], method='GET') + ret = query( + command="my/machines/{0}".format(node["id"]), + location=node["location"], + method="GET", + ) return ret def joyent_node_state(id_): - ''' + """ Convert joyent returned state to state common to other data center return values for consistency :param id_: joyent state value :return: state value - ''' - states = {'running': 0, - 'stopped': 2, - 'stopping': 2, - 'provisioning': 3, - 'deleted': 2, - 'unknown': 4} + """ + states = { + "running": 0, + "stopped": 2, + "stopping": 2, + "provisioning": 3, + "deleted": 2, + "unknown": 4, + } if id_ not in states: - id_ = 'unknown' + id_ = "unknown" return node_state(states[id_]) def reformat_node(item=None, full=False): - ''' + """ Reformat the returned data from joyent, determine public/private IPs and strip out fields if necessary to provide either full or brief content. :param item: node dictionary :param full: full or brief output :return: dict - ''' + """ desired_keys = [ - 'id', 'name', 'state', 'public_ips', 'private_ips', 'size', 'image', - 'location' + "id", + "name", + "state", + "public_ips", + "private_ips", + "size", + "image", + "location", ] - item['private_ips'] = [] - item['public_ips'] = [] - if 'ips' in item: - for ip in item['ips']: + item["private_ips"] = [] + item["public_ips"] = [] + if "ips" in item: + for ip in item["ips"]: if salt.utils.cloud.is_public_ip(ip): - item['public_ips'].append(ip) + item["public_ips"].append(ip) else: - item['private_ips'].append(ip) + item["private_ips"].append(ip) # add any undefined desired keys for key in desired_keys: @@ -720,14 +766,14 @@ def reformat_node(item=None, full=False): for key in to_del: del item[key] - if 'state' in item: - item['state'] = joyent_node_state(item['state']) + if "state" in item: + item["state"] = joyent_node_state(item["state"]) return item def list_nodes(full=False, call=None): - ''' + """ list of nodes, keeping only a brief listing CLI Example: @@ -735,40 +781,38 @@ def list_nodes(full=False, call=None): .. code-block:: bash salt-cloud -Q - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} if POLL_ALL_LOCATIONS: for location in JOYENT_LOCATIONS: - result = query(command='my/machines', location=location, - method='GET') + result = query(command="my/machines", location=location, method="GET") if result[0] in VALID_RESPONSE_CODES: nodes = result[1] for node in nodes: - if 'name' in node: - node['location'] = location - ret[node['name']] = reformat_node(item=node, full=full) + if "name" in node: + node["location"] = location + ret[node["name"]] = reformat_node(item=node, full=full) else: - log.error('Invalid response when listing Joyent nodes: %s', result[1]) + log.error("Invalid response when listing Joyent nodes: %s", result[1]) else: location = get_location() - result = query(command='my/machines', location=location, - method='GET') + result = query(command="my/machines", location=location, method="GET") nodes = result[1] for node in nodes: - if 'name' in node: - node['location'] = location - ret[node['name']] = reformat_node(item=node, full=full) + if "name" in node: + node["location"] = location + ret[node["name"]] = reformat_node(item=node, full=full) return ret def list_nodes_full(call=None): - ''' + """ list of nodes, maintaining all content provided from joyent listings CLI Example: @@ -776,46 +820,46 @@ def list_nodes_full(call=None): .. code-block:: bash salt-cloud -F - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) return list_nodes(full=True) def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full('function'), __opts__['query.selection'], call, + list_nodes_full("function"), __opts__["query.selection"], call, ) def _get_proto(): - ''' + """ Checks configuration to see whether the user has SSL turned on. Default is: .. code-block:: yaml use_ssl: True - ''' + """ use_ssl = config.get_cloud_config_value( - 'use_ssl', + "use_ssl", get_configured_provider(), __opts__, search_global=False, - default=True + default=True, ) if use_ssl is True: - return 'https' - return 'http' + return "https" + return "http" def avail_images(call=None): - ''' + """ Get list of available images CLI Example: @@ -829,39 +873,41 @@ def avail_images(call=None): .. code-block:: yaml image_url: images.joyent.com/images - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) user = config.get_cloud_config_value( - 'user', get_configured_provider(), __opts__, search_global=False + "user", get_configured_provider(), __opts__, search_global=False ) img_url = config.get_cloud_config_value( - 'image_url', + "image_url", get_configured_provider(), __opts__, search_global=False, - default='{0}{1}/{2}/images'.format(DEFAULT_LOCATION, JOYENT_API_HOST_SUFFIX, user) + default="{0}{1}/{2}/images".format( + DEFAULT_LOCATION, JOYENT_API_HOST_SUFFIX, user + ), ) - if not img_url.startswith('http://') and not img_url.startswith('https://'): - img_url = '{0}://{1}'.format(_get_proto(), img_url) + if not img_url.startswith("http://") and not img_url.startswith("https://"): + img_url = "{0}://{1}".format(_get_proto(), img_url) - rcode, data = query(command='my/images', method='GET') + rcode, data = query(command="my/images", method="GET") log.debug(data) ret = {} for image in data: - ret[image['name']] = image + ret[image["name"]] = image return ret def avail_sizes(call=None): - ''' + """ get list of available packages CLI Example: @@ -869,65 +915,58 @@ def avail_sizes(call=None): .. code-block:: bash salt-cloud --list-sizes - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) - rcode, items = query(command='my/packages') + rcode, items = query(command="my/packages") if rcode not in VALID_RESPONSE_CODES: return {} return key_list(items=items) def list_keys(kwargs=None, call=None): - ''' + """ List the keys available - ''' - if call != 'function': - log.error( - 'The list_keys function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The list_keys function must be called with -f or --function.") return False if not kwargs: kwargs = {} ret = {} - rcode, data = query(command='my/keys', method='GET') + rcode, data = query(command="my/keys", method="GET") for pair in data: - ret[pair['name']] = pair['key'] - return {'keys': ret} + ret[pair["name"]] = pair["key"] + return {"keys": ret} def show_key(kwargs=None, call=None): - ''' + """ List the keys available - ''' - if call != 'function': - log.error( - 'The list_keys function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The list_keys function must be called with -f or --function.") return False if not kwargs: kwargs = {} - if 'keyname' not in kwargs: - log.error('A keyname is required.') + if "keyname" not in kwargs: + log.error("A keyname is required.") return False - rcode, data = query( - command='my/keys/{0}'.format(kwargs['keyname']), - method='GET', - ) - return {'keys': {data['name']: data['key']}} + rcode, data = query(command="my/keys/{0}".format(kwargs["keyname"]), method="GET",) + return {"keys": {data["name"]: data["key"]}} def import_key(kwargs=None, call=None): - ''' + """ List the keys available CLI Example: @@ -935,45 +974,39 @@ def import_key(kwargs=None, call=None): .. code-block:: bash salt-cloud -f import_key joyent keyname=mykey keyfile=/tmp/mykey.pub - ''' - if call != 'function': - log.error( - 'The import_key function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The import_key function must be called with -f or --function.") return False if not kwargs: kwargs = {} - if 'keyname' not in kwargs: - log.error('A keyname is required.') + if "keyname" not in kwargs: + log.error("A keyname is required.") return False - if 'keyfile' not in kwargs: - log.error('The location of the SSH keyfile is required.') + if "keyfile" not in kwargs: + log.error("The location of the SSH keyfile is required.") return False - if not os.path.isfile(kwargs['keyfile']): - log.error('The specified keyfile (%s) does not exist.', kwargs['keyfile']) + if not os.path.isfile(kwargs["keyfile"]): + log.error("The specified keyfile (%s) does not exist.", kwargs["keyfile"]) return False - with salt.utils.files.fopen(kwargs['keyfile'], 'r') as fp_: - kwargs['key'] = salt.utils.stringutils.to_unicode(fp_.read()) + with salt.utils.files.fopen(kwargs["keyfile"], "r") as fp_: + kwargs["key"] = salt.utils.stringutils.to_unicode(fp_.read()) - send_data = {'name': kwargs['keyname'], 'key': kwargs['key']} - kwargs['data'] = salt.utils.json.dumps(send_data) + send_data = {"name": kwargs["keyname"], "key": kwargs["key"]} + kwargs["data"] = salt.utils.json.dumps(send_data) - rcode, data = query( - command='my/keys', - method='POST', - data=kwargs['data'], - ) + rcode, data = query(command="my/keys", method="POST", data=kwargs["data"],) log.debug(pprint.pformat(data)) - return {'keys': {data['name']: data['key']}} + return {"keys": {data["name"]: data["key"]}} def delete_key(kwargs=None, call=None): - ''' + """ List the keys available CLI Example: @@ -981,84 +1014,95 @@ def delete_key(kwargs=None, call=None): .. code-block:: bash salt-cloud -f delete_key joyent keyname=mykey - ''' - if call != 'function': - log.error( - 'The delete_keys function must be called with -f or --function.' - ) + """ + if call != "function": + log.error("The delete_keys function must be called with -f or --function.") return False if not kwargs: kwargs = {} - if 'keyname' not in kwargs: - log.error('A keyname is required.') + if "keyname" not in kwargs: + log.error("A keyname is required.") return False rcode, data = query( - command='my/keys/{0}'.format(kwargs['keyname']), - method='DELETE', + command="my/keys/{0}".format(kwargs["keyname"]), method="DELETE", ) return data -def get_location_path(location=DEFAULT_LOCATION, api_host_suffix=JOYENT_API_HOST_SUFFIX): - ''' +def get_location_path( + location=DEFAULT_LOCATION, api_host_suffix=JOYENT_API_HOST_SUFFIX +): + """ create url from location variable :param location: joyent data center location :return: url - ''' - return '{0}://{1}{2}'.format(_get_proto(), location, api_host_suffix) + """ + return "{0}://{1}{2}".format(_get_proto(), location, api_host_suffix) -def query(action=None, - command=None, - args=None, - method='GET', - location=None, - data=None): - ''' +def query(action=None, command=None, args=None, method="GET", location=None, data=None): + """ Make a web call to Joyent - ''' + """ user = config.get_cloud_config_value( - 'user', get_configured_provider(), __opts__, search_global=False + "user", get_configured_provider(), __opts__, search_global=False ) if not user: - log.error('username is required for Joyent API requests. Please set one in your provider configuration') + log.error( + "username is required for Joyent API requests. Please set one in your provider configuration" + ) password = config.get_cloud_config_value( - 'password', get_configured_provider(), __opts__, - search_global=False + "password", get_configured_provider(), __opts__, search_global=False ) verify_ssl = config.get_cloud_config_value( - 'verify_ssl', get_configured_provider(), __opts__, - search_global=False, default=True + "verify_ssl", + get_configured_provider(), + __opts__, + search_global=False, + default=True, ) ssh_keyfile = config.get_cloud_config_value( - 'private_key', get_configured_provider(), __opts__, - search_global=False, default=True + "private_key", + get_configured_provider(), + __opts__, + search_global=False, + default=True, ) if not ssh_keyfile: - log.error('ssh_keyfile is required for Joyent API requests. Please set one in your provider configuration') + log.error( + "ssh_keyfile is required for Joyent API requests. Please set one in your provider configuration" + ) ssh_keyname = config.get_cloud_config_value( - 'keyname', get_configured_provider(), __opts__, - search_global=False, default=True + "keyname", + get_configured_provider(), + __opts__, + search_global=False, + default=True, ) if not ssh_keyname: - log.error('ssh_keyname is required for Joyent API requests. Please set one in your provider configuration') + log.error( + "ssh_keyname is required for Joyent API requests. Please set one in your provider configuration" + ) if not location: location = get_location() api_host_suffix = config.get_cloud_config_value( - 'api_host_suffix', get_configured_provider(), __opts__, - search_global=False, default=JOYENT_API_HOST_SUFFIX + "api_host_suffix", + get_configured_provider(), + __opts__, + search_global=False, + default=JOYENT_API_HOST_SUFFIX, ) path = get_location_path(location=location, api_host_suffix=api_host_suffix) @@ -1067,43 +1111,42 @@ def query(action=None, path += action if command: - path += '/{0}'.format(command) + path += "/{0}".format(command) - log.debug('User: \'%s\' on PATH: %s', user, path) + log.debug("User: '%s' on PATH: %s", user, path) if (not user) or (not ssh_keyfile) or (not ssh_keyname) or (not location): return None timenow = datetime.datetime.utcnow() - timestamp = timenow.strftime('%a, %d %b %Y %H:%M:%S %Z').strip() + timestamp = timenow.strftime("%a, %d %b %Y %H:%M:%S %Z").strip() rsa_key = salt.crypt.get_rsa_key(ssh_keyfile, None) if HAS_M2: - md = EVP.MessageDigest('sha256') + md = EVP.MessageDigest("sha256") md.update(timestamp.encode(__salt_system_encoding__)) digest = md.final() - signed = rsa_key.sign(digest, algo='sha256') + signed = rsa_key.sign(digest, algo="sha256") else: rsa_ = PKCS1_v1_5.new(rsa_key) hash_ = SHA256.new() hash_.update(timestamp.encode(__salt_system_encoding__)) signed = rsa_.sign(hash_) signed = base64.b64encode(signed) - user_arr = user.split('/') + user_arr = user.split("/") if len(user_arr) == 1: - keyid = '/{0}/keys/{1}'.format(user_arr[0], ssh_keyname) + keyid = "/{0}/keys/{1}".format(user_arr[0], ssh_keyname) elif len(user_arr) == 2: - keyid = '/{0}/users/{1}/keys/{2}'.format(user_arr[0], user_arr[1], ssh_keyname) + keyid = "/{0}/users/{1}/keys/{2}".format(user_arr[0], user_arr[1], ssh_keyname) else: - log.error('Malformed user string') + log.error("Malformed user string") headers = { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - 'X-Api-Version': JOYENT_API_VERSION, - 'Date': timestamp, - 'Authorization': 'Signature keyId="{0}",algorithm="rsa-sha256" {1}'.format( - keyid, - signed.decode(__salt_system_encoding__) + "Content-Type": "application/json", + "Accept": "application/json", + "X-Api-Version": JOYENT_API_VERSION, + "Date": timestamp, + "Authorization": 'Signature keyId="{0}",algorithm="rsa-sha256" {1}'.format( + keyid, signed.decode(__salt_system_encoding__) ), } @@ -1128,12 +1171,12 @@ def query(action=None, verify_ssl=verify_ssl, opts=__opts__, ) - log.debug('Joyent Response Status Code: %s', result['status']) - if 'headers' not in result: - return [result['status'], result['error']] + log.debug("Joyent Response Status Code: %s", result["status"]) + if "headers" not in result: + return [result["status"], result["error"]] - if 'Content-Length' in result['headers']: - content = result['text'] + if "Content-Length" in result["headers"]: + content = result["text"] return_content = salt.utils.yaml.safe_load(content) - return [result['status'], return_content] + return [result["status"], return_content] diff --git a/salt/cloud/clouds/libvirt.py b/salt/cloud/clouds/libvirt.py index ad8c7175622..4401c480529 100644 --- a/salt/cloud/clouds/libvirt.py +++ b/salt/cloud/clouds/libvirt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Libvirt Cloud Module ==================== @@ -46,7 +46,7 @@ Tested on: - Fedora 23 (libvirt 1.2.18, qemu 2.4.1) - Centos 7 (libvirt 1.2.17, qemu 1.5.3) -''' +""" # TODO: look at event descriptions here: # https://docs.saltstack.com/en/latest/topics/cloud/reactor.html @@ -61,18 +61,8 @@ import logging import os import sys import uuid - from xml.etree import ElementTree -# Import 3rd-party libs -from salt.ext import six -try: - import libvirt # pylint: disable=import-error - from libvirt import libvirtError - HAS_LIBVIRT = True -except ImportError: - HAS_LIBVIRT = False - # Import salt libs import salt.config as config import salt.utils.cloud @@ -80,31 +70,49 @@ from salt.exceptions import ( SaltCloudConfigError, SaltCloudExecutionFailure, SaltCloudNotFound, - SaltCloudSystemExit + SaltCloudSystemExit, ) -VIRT_STATE_NAME_MAP = {0: 'running', - 1: 'running', - 2: 'running', - 3: 'paused', - 4: 'shutdown', - 5: 'shutdown', - 6: 'crashed'} +# Import 3rd-party libs +from salt.ext import six + +try: + import libvirt # pylint: disable=import-error + + # pylint: disable=no-name-in-module + from libvirt import libvirtError + + # pylint: enable=no-name-in-module + + HAS_LIBVIRT = True +except ImportError: + HAS_LIBVIRT = False + + +VIRT_STATE_NAME_MAP = { + 0: "running", + 1: "running", + 2: "running", + 3: "paused", + 4: "shutdown", + 5: "shutdown", + 6: "crashed", +} IP_LEARNING_XML = """ """ -__virtualname__ = 'libvirt' +__virtualname__ = "libvirt" # Set up logging log = logging.getLogger(__name__) def libvirt_error_handler(ctx, error): # pylint: disable=unused-argument - ''' + """ Redirect stderr prints from libvirt to salt logging. - ''' + """ log.debug("libvirt error %s", error) @@ -113,7 +121,7 @@ if HAS_LIBVIRT: def __virtual__(): - ''' + """ This function determines whether or not to make this cloud module available upon execution. Most often, it uses get_configured_provider() to determine @@ -124,24 +132,22 @@ def __virtual__(): then that name should be returned instead of True. @return True|False|str - ''' + """ if not HAS_LIBVIRT: - return False, 'Unable to locate or import python libvirt library.' + return False, "Unable to locate or import python libvirt library." if get_configured_provider() is False: - return False, 'The \'libvirt\' provider is not configured.' + return False, "The 'libvirt' provider is not configured." return __virtualname__ def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('url',) + __opts__, __active_provider_name__ or __virtualname__, ("url",) ) @@ -152,16 +158,14 @@ def __get_conn(url): conn = libvirt.open(url) except Exception: # pylint: disable=broad-except raise SaltCloudExecutionFailure( - 'Sorry, {0} failed to open a connection to the hypervisor ' - 'software at {1}'.format( - __grains__['fqdn'], url - ) + "Sorry, {0} failed to open a connection to the hypervisor " + "software at {1}".format(__grains__["fqdn"], url) ) return conn def list_nodes(call=None): - ''' + """ Return a list of the VMs id (str) @@ -171,28 +175,32 @@ def list_nodes(call=None): private_ips (list) public_ips (list) - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called ' - 'with -f or --function.' + "The list_nodes function must be called " "with -f or --function." ) - providers = __opts__.get('providers', {}) + providers = __opts__.get("providers", {}) ret = {} - providers_to_check = [_f for _f in [cfg.get('libvirt') for cfg in six.itervalues(providers)] if _f] + providers_to_check = [ + _f for _f in [cfg.get("libvirt") for cfg in six.itervalues(providers)] if _f + ] for provider in providers_to_check: - conn = __get_conn(provider['url']) + conn = __get_conn(provider["url"]) domains = conn.listAllDomains() for domain in domains: data = { - 'id': domain.UUIDString(), - 'image': '', - 'size': '', - 'state': VIRT_STATE_NAME_MAP[domain.state()[0]], - 'private_ips': [], - 'public_ips': get_domain_ips(domain, libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE)} + "id": domain.UUIDString(), + "image": "", + "size": "", + "state": VIRT_STATE_NAME_MAP[domain.state()[0]], + "private_ips": [], + "public_ips": get_domain_ips( + domain, libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE + ), + } # TODO: Annoyingly name is not guaranteed to be unique, but the id will not work in other places ret[domain.name()] = data @@ -200,40 +208,34 @@ def list_nodes(call=None): def list_nodes_full(call=None): - ''' + """ Because this module is not specific to any cloud providers, there will be no nodes to list. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called ' - 'with -f or --function.' + "The list_nodes_full function must be called " "with -f or --function." ) return list_nodes(call) def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_select function must be called ' - 'with -f or --function.' + "The list_nodes_select function must be called " "with -f or --function." ) - selection = __opts__.get('query.selection') + selection = __opts__.get("query.selection") if not selection: - raise SaltCloudSystemExit( - 'query.selection not found in /etc/salt/cloud' - ) + raise SaltCloudSystemExit("query.selection not found in /etc/salt/cloud") # TODO: somewhat doubt the implementation of cloud.list_nodes_select - return salt.utils.cloud.list_nodes_select( - list_nodes_full(), selection, call, - ) + return salt.utils.cloud.list_nodes_select(list_nodes_full(), selection, call,) def to_ip_addr_type(addr_type): @@ -255,12 +257,12 @@ def get_domain_ips(domain, ip_source): return ips for (name, val) in six.iteritems(addresses): - if val['addrs']: - for addr in val['addrs']: - tp = to_ip_addr_type(addr['type']) + if val["addrs"]: + for addr in val["addrs"]: + tp = to_ip_addr_type(addr["type"]) log.info("Found address %s", addr) if tp == "ipv4": - ips.append(addr['addr']) + ips.append(addr["addr"]) return ips @@ -268,7 +270,7 @@ def get_domain_ip(domain, idx, ip_source, skip_loopback=True): ips = get_domain_ips(domain, ip_source) if skip_loopback: - ips = [ip for ip in ips if not ip.startswith('127.')] + ips = [ip for ip in ips if not ip.startswith("127.")] if not ips or len(ips) <= idx: return None @@ -277,62 +279,81 @@ def get_domain_ip(domain, idx, ip_source, skip_loopback=True): def create(vm_): - ''' + """ Provision a single machine - ''' - clone_strategy = vm_.get('clone_strategy') or 'full' + """ + clone_strategy = vm_.get("clone_strategy") or "full" - if clone_strategy not in ('quick', 'full'): - raise SaltCloudSystemExit("'clone_strategy' must be one of quick or full. Got '{0}'".format(clone_strategy)) + if clone_strategy not in ("quick", "full"): + raise SaltCloudSystemExit( + "'clone_strategy' must be one of quick or full. Got '{0}'".format( + clone_strategy + ) + ) - ip_source = vm_.get('ip_source') or 'ip-learning' + ip_source = vm_.get("ip_source") or "ip-learning" - if ip_source not in ('ip-learning', 'qemu-agent'): - raise SaltCloudSystemExit("'ip_source' must be one of qemu-agent or ip-learning. Got '{0}'".format(ip_source)) + if ip_source not in ("ip-learning", "qemu-agent"): + raise SaltCloudSystemExit( + "'ip_source' must be one of qemu-agent or ip-learning. Got '{0}'".format( + ip_source + ) + ) - validate_xml = vm_.get('validate_xml') if vm_.get('validate_xml') is not None else True + validate_xml = ( + vm_.get("validate_xml") if vm_.get("validate_xml") is not None else True + ) - log.info("Cloning '%s' with strategy '%s' validate_xml='%s'", vm_['name'], clone_strategy, validate_xml) + log.info( + "Cloning '%s' with strategy '%s' validate_xml='%s'", + vm_["name"], + clone_strategy, + validate_xml, + ) try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'libvirt', - vm_['profile']) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "libvirt", vm_["profile"] + ) + is False + ): return False except AttributeError: pass # TODO: check name qemu/libvirt will choke on some characters (like '/')? - name = vm_['name'] + name = vm_["name"] - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(name), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(name), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) key_filename = config.get_cloud_config_value( - 'private_key', vm_, __opts__, search_global=False, default=None + "private_key", vm_, __opts__, search_global=False, default=None ) if key_filename is not None and not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined key_filename \'{0}\' does not exist'.format( - key_filename - ) + "The defined key_filename '{0}' does not exist".format(key_filename) ) - vm_['key_filename'] = key_filename + vm_["key_filename"] = key_filename # wait_for_instance requires private_key - vm_['private_key'] = key_filename + vm_["private_key"] = key_filename cleanup = [] try: # clone the vm - base = vm_['base_domain'] - conn = __get_conn(vm_['url']) + base = vm_["base_domain"] + conn = __get_conn(vm_["url"]) try: # for idempotency the salt-bootstrap needs -F argument @@ -344,37 +365,45 @@ def create(vm_): xml = domain.XMLDesc(0) kwargs = { - 'name': name, - 'base_domain': base, + "name": name, + "base_domain": base, } - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(name), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(name), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', kwargs, list(kwargs)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", kwargs, list(kwargs) + ), }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) log.debug("Source machine XML '%s'", xml) domain_xml = ElementTree.fromstring(xml) - domain_xml.find('./name').text = name - if domain_xml.find('./description') is None: - description_elem = ElementTree.Element('description') + domain_xml.find("./name").text = name + if domain_xml.find("./description") is None: + description_elem = ElementTree.Element("description") domain_xml.insert(0, description_elem) - description = domain_xml.find('./description') + description = domain_xml.find("./description") description.text = "Cloned from {0}".format(base) - domain_xml.remove(domain_xml.find('./uuid')) + domain_xml.remove(domain_xml.find("./uuid")) - for iface_xml in domain_xml.findall('./devices/interface'): - iface_xml.remove(iface_xml.find('./mac')) + for iface_xml in domain_xml.findall("./devices/interface"): + iface_xml.remove(iface_xml.find("./mac")) # enable IP learning, this might be a default behaviour... # Don't always enable since it can cause problems through libvirt-4.5 - if ip_source == 'ip-learning' and iface_xml.find("./filterref/parameter[@name='CTRL_IP_LEARNING']") is None: + if ( + ip_source == "ip-learning" + and iface_xml.find( + "./filterref/parameter[@name='CTRL_IP_LEARNING']" + ) + is None + ): iface_xml.append(ElementTree.fromstring(IP_LEARNING_XML)) # If a qemu agent is defined we need to fix the path to its socket @@ -385,44 +414,63 @@ def create(vm_): # for agent_xml in domain_xml.findall("""./devices/channel[@type='unix']"""): # is org.qemu.guest_agent.0 an option? - if agent_xml.find("""./target[@type='virtio'][@name='org.qemu.guest_agent.0']""") is not None: + if ( + agent_xml.find( + """./target[@type='virtio'][@name='org.qemu.guest_agent.0']""" + ) + is not None + ): source_element = agent_xml.find("""./source[@mode='bind']""") # see if there is a path element that needs rewriting - if source_element and 'path' in source_element.attrib: - path = source_element.attrib['path'] - new_path = path.replace('/domain-{0}/'.format(base), '/domain-{0}/'.format(name)) + if source_element and "path" in source_element.attrib: + path = source_element.attrib["path"] + new_path = path.replace( + "/domain-{0}/".format(base), "/domain-{0}/".format(name) + ) log.debug("Rewriting agent socket path to %s", new_path) - source_element.attrib['path'] = new_path + source_element.attrib["path"] = new_path - for disk in domain_xml.findall("""./devices/disk[@device='disk'][@type='file']"""): + for disk in domain_xml.findall( + """./devices/disk[@device='disk'][@type='file']""" + ): # print "Disk: ", ElementTree.tostring(disk) # check if we can clone driver = disk.find("./driver[@name='qemu']") if driver is None: # Err on the safe side - raise SaltCloudExecutionFailure("Non qemu driver disk encountered bailing out.") - disk_type = driver.attrib.get('type') + raise SaltCloudExecutionFailure( + "Non qemu driver disk encountered bailing out." + ) + disk_type = driver.attrib.get("type") log.info("disk attributes %s", disk.attrib) - if disk_type == 'qcow2': - source = disk.find("./source").attrib['file'] + if disk_type == "qcow2": + source = disk.find("./source").attrib["file"] pool, volume = find_pool_and_volume(conn, source) - if clone_strategy == 'quick': - new_volume = pool.createXML(create_volume_with_backing_store_xml(volume), 0) + if clone_strategy == "quick": + new_volume = pool.createXML( + create_volume_with_backing_store_xml(volume), 0 + ) else: - new_volume = pool.createXMLFrom(create_volume_xml(volume), volume, 0) - cleanup.append({'what': 'volume', 'item': new_volume}) + new_volume = pool.createXMLFrom( + create_volume_xml(volume), volume, 0 + ) + cleanup.append({"what": "volume", "item": new_volume}) - disk.find("./source").attrib['file'] = new_volume.path() - elif disk_type == 'raw': - source = disk.find("./source").attrib['file'] + disk.find("./source").attrib["file"] = new_volume.path() + elif disk_type == "raw": + source = disk.find("./source").attrib["file"] pool, volume = find_pool_and_volume(conn, source) # TODO: more control on the cloned disk type - new_volume = pool.createXMLFrom(create_volume_xml(volume), volume, 0) - cleanup.append({'what': 'volume', 'item': new_volume}) + new_volume = pool.createXMLFrom( + create_volume_xml(volume), volume, 0 + ) + cleanup.append({"what": "volume", "item": new_volume}) - disk.find("./source").attrib['file'] = new_volume.path() + disk.find("./source").attrib["file"] = new_volume.path() else: - raise SaltCloudExecutionFailure("Disk type '{0}' not supported".format(disk_type)) + raise SaltCloudExecutionFailure( + "Disk type '{0}' not supported".format(disk_type) + ) clone_xml = salt.utils.stringutils.to_str(ElementTree.tostring(domain_xml)) log.debug("Clone XML '%s'", clone_xml) @@ -430,39 +478,47 @@ def create(vm_): validate_flags = libvirt.VIR_DOMAIN_DEFINE_VALIDATE if validate_xml else 0 clone_domain = conn.defineXMLFlags(clone_xml, validate_flags) - cleanup.append({'what': 'domain', 'item': clone_domain}) + cleanup.append({"what": "domain", "item": clone_domain}) clone_domain.createWithFlags(libvirt.VIR_DOMAIN_START_FORCE_BOOT) log.debug("VM '%s'", vm_) - if ip_source == 'qemu-agent': + if ip_source == "qemu-agent": ip_source = libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT - elif ip_source == 'ip-learning': + elif ip_source == "ip-learning": ip_source = libvirt.VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE address = salt.utils.cloud.wait_for_ip( get_domain_ip, update_args=(clone_domain, 0, ip_source), - timeout=config.get_cloud_config_value('wait_for_ip_timeout', vm_, __opts__, default=10 * 60), - interval=config.get_cloud_config_value('wait_for_ip_interval', vm_, __opts__, default=10), - interval_multiplier=config.get_cloud_config_value('wait_for_ip_interval_multiplier', vm_, __opts__, default=1), + timeout=config.get_cloud_config_value( + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), + interval=config.get_cloud_config_value( + "wait_for_ip_interval", vm_, __opts__, default=10 + ), + interval_multiplier=config.get_cloud_config_value( + "wait_for_ip_interval_multiplier", vm_, __opts__, default=1 + ), ) - log.info('Address = %s', address) + log.info("Address = %s", address) - vm_['ssh_host'] = address + vm_["ssh_host"] = address # the bootstrap script needs to be installed first in /etc/salt/cloud.deploy.d/ # salt-cloud -u is your friend - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(name), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(name), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret @@ -473,7 +529,7 @@ def create(vm_): def do_cleanup(cleanup): - ''' + """ Clean up clone domain leftovers as much as possible. Extra robust clean up in order to deal with some small changes in libvirt @@ -488,29 +544,31 @@ def do_cleanup(cleanup): none .. versionadded: 2017.7.3 - ''' - log.info('Cleaning up after exception') + """ + log.info("Cleaning up after exception") for leftover in cleanup: - what = leftover['what'] - item = leftover['item'] - if what == 'domain': - log.info('Cleaning up %s %s', what, item.name()) + what = leftover["what"] + item = leftover["item"] + if what == "domain": + log.info("Cleaning up %s %s", what, item.name()) try: item.destroy() - log.debug('%s %s forced off', what, item.name()) + log.debug("%s %s forced off", what, item.name()) except libvirtError: pass try: - item.undefineFlags(libvirt.VIR_DOMAIN_UNDEFINE_MANAGED_SAVE+ - libvirt.VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA+ - libvirt.VIR_DOMAIN_UNDEFINE_NVRAM) - log.debug('%s %s undefined', what, item.name()) + item.undefineFlags( + libvirt.VIR_DOMAIN_UNDEFINE_MANAGED_SAVE + + libvirt.VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA + + libvirt.VIR_DOMAIN_UNDEFINE_NVRAM + ) + log.debug("%s %s undefined", what, item.name()) except libvirtError: pass - if what == 'volume': + if what == "volume": try: item.delete() - log.debug('%s %s cleaned up', what, item.name()) + log.debug("%s %s cleaned up", what, item.name()) except libvirtError: pass @@ -536,22 +594,23 @@ def destroy(name, call=None): """ log.info("Attempting to delete instance %s", name) - if call == 'function': + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) found = [] - providers = __opts__.get('providers', {}) - providers_to_check = [_f for _f in [cfg.get('libvirt') for cfg in six.itervalues(providers)] if _f] + providers = __opts__.get("providers", {}) + providers_to_check = [ + _f for _f in [cfg.get("libvirt") for cfg in six.itervalues(providers)] if _f + ] for provider in providers_to_check: - conn = __get_conn(provider['url']) - log.info("looking at %s", provider['url']) + conn = __get_conn(provider["url"]) + log.info("looking at %s", provider["url"]) try: domain = conn.lookupByName(name) - found.append({'domain': domain, 'conn': conn}) + found.append({"domain": domain, "conn": conn}) except libvirtError: pass @@ -561,42 +620,44 @@ def destroy(name, call=None): if len(found) > 1: return "{0} doesn't identify a unique machine leaving things".format(name) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - destroy_domain(found[0]['conn'], found[0]['domain']) + destroy_domain(found[0]["conn"], found[0]["domain"]) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) def destroy_domain(conn, domain): - log.info('Destroying domain %s', domain.name()) + log.info("Destroying domain %s", domain.name()) try: domain.destroy() except libvirtError: pass volumes = get_domain_volumes(conn, domain) for volume in volumes: - log.debug('Removing volume %s', volume.name()) + log.debug("Removing volume %s", volume.name()) volume.delete() - log.debug('Undefining domain %s', domain.name()) - domain.undefineFlags(libvirt.VIR_DOMAIN_UNDEFINE_MANAGED_SAVE+ - libvirt.VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA+ - libvirt.VIR_DOMAIN_UNDEFINE_NVRAM) + log.debug("Undefining domain %s", domain.name()) + domain.undefineFlags( + libvirt.VIR_DOMAIN_UNDEFINE_MANAGED_SAVE + + libvirt.VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA + + libvirt.VIR_DOMAIN_UNDEFINE_NVRAM + ) def create_volume_xml(volume): @@ -613,10 +674,10 @@ def create_volume_xml(volume): """ volume_xml = ElementTree.fromstring(template) # TODO: generate name - volume_xml.find('name').text = generate_new_name(volume.name()) + volume_xml.find("name").text = generate_new_name(volume.name()) log.debug("Volume: %s", dir(volume)) - volume_xml.find('capacity').text = six.text_type(volume.info()[1]) - volume_xml.find('./target/path').text = volume.path() + volume_xml.find("capacity").text = six.text_type(volume.info()[1]) + volume_xml.find("./target/path").text = volume.path() xml_string = salt.utils.stringutils.to_str(ElementTree.tostring(volume_xml)) log.debug("Creating %s", xml_string) return xml_string @@ -639,10 +700,10 @@ def create_volume_with_backing_store_xml(volume): """ volume_xml = ElementTree.fromstring(template) # TODO: generate name - volume_xml.find('name').text = generate_new_name(volume.name()) + volume_xml.find("name").text = generate_new_name(volume.name()) log.debug("volume: %s", dir(volume)) - volume_xml.find('capacity').text = six.text_type(volume.info()[1]) - volume_xml.find('./backingStore/path').text = volume.path() + volume_xml.find("capacity").text = six.text_type(volume.info()[1]) + volume_xml.find("./backingStore/path").text = volume.path() xml_string = salt.utils.stringutils.to_str(ElementTree.tostring(volume_xml)) log.debug("Creating %s", xml_string) return xml_string @@ -651,19 +712,19 @@ def create_volume_with_backing_store_xml(volume): def find_pool_and_volume(conn, path): # active and persistent storage pools # TODO: should we filter on type? - for sp in conn.listAllStoragePools(2+4): + for sp in conn.listAllStoragePools(2 + 4): for v in sp.listAllVolumes(): if v.path() == path: return sp, v - raise SaltCloudNotFound('Could not find volume for path {0}'.format(path)) + raise SaltCloudNotFound("Could not find volume for path {0}".format(path)) def generate_new_name(orig_name): - if '.' not in orig_name: - return '{0}-{1}'.format(orig_name, uuid.uuid1()) + if "." not in orig_name: + return "{0}-{1}".format(orig_name, uuid.uuid1()) - name, ext = orig_name.rsplit('.', 1) - return '{0}-{1}.{2}'.format(name, uuid.uuid1(), ext) + name, ext = orig_name.rsplit(".", 1) + return "{0}-{1}.{2}".format(name, uuid.uuid1(), ext) def get_domain_volumes(conn, domain): @@ -671,7 +732,7 @@ def get_domain_volumes(conn, domain): xml = ElementTree.fromstring(domain.XMLDesc(0)) for disk in xml.findall("""./devices/disk[@device='disk'][@type='file']"""): if disk.find("./driver[@name='qemu'][@type='qcow2']") is not None: - source = disk.find("./source").attrib['file'] + source = disk.find("./source").attrib["file"] try: pool, volume = find_pool_and_volume(conn, source) volumes.append(volume) diff --git a/salt/cloud/clouds/linode.py b/salt/cloud/clouds/linode.py index 0c1d13771b1..421a76c3f1d 100644 --- a/salt/cloud/clouds/linode.py +++ b/salt/cloud/clouds/linode.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Linode Cloud Module using Linode's REST API =========================================== @@ -23,26 +23,27 @@ Set up the cloud configuration at ``/etc/salt/cloud.providers`` or ``/etc/salt/c image: CentOS 7 location: London, England, UK -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime import logging import pprint import re import time -import datetime # Import Salt Libs import salt.config as config -from salt.ext import six -from salt.ext.six.moves import range from salt.exceptions import ( SaltCloudConfigError, SaltCloudException, SaltCloudNotFound, - SaltCloudSystemExit + SaltCloudSystemExit, ) +from salt.ext import six +from salt.ext.six.moves import range # Get logging started log = logging.getLogger(__name__) @@ -52,44 +53,23 @@ LASTCALL = int(time.mktime(datetime.datetime.now().timetuple())) # Human-readable status fields (documentation: https://www.linode.com/api/linode/linode.list) LINODE_STATUS = { - 'boot_failed': { - 'code': -2, - 'descr': 'Boot Failed (not in use)', - }, - 'beeing_created': { - 'code': -1, - 'descr': 'Being Created', - }, - 'brand_new': { - 'code': 0, - 'descr': 'Brand New', - }, - 'running': { - 'code': 1, - 'descr': 'Running', - }, - 'poweroff': { - 'code': 2, - 'descr': 'Powered Off', - }, - 'shutdown': { - 'code': 3, - 'descr': 'Shutting Down (not in use)', - }, - 'save_to_disk': { - 'code': 4, - 'descr': 'Saved to Disk (not in use)', - }, + "boot_failed": {"code": -2, "descr": "Boot Failed (not in use)"}, + "beeing_created": {"code": -1, "descr": "Being Created"}, + "brand_new": {"code": 0, "descr": "Brand New"}, + "running": {"code": 1, "descr": "Running"}, + "poweroff": {"code": 2, "descr": "Powered Off"}, + "shutdown": {"code": 3, "descr": "Shutting Down (not in use)"}, + "save_to_disk": {"code": 4, "descr": "Saved to Disk (not in use)"}, } -__virtualname__ = 'linode' +__virtualname__ = "linode" # Only load in this module if the Linode configurations are in place def __virtual__(): - ''' + """ Check for Linode configs. - ''' + """ if get_configured_provider() is False: return False @@ -97,18 +77,16 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('apikey', 'password',) + __opts__, __active_provider_name__ or __virtualname__, ("apikey", "password",) ) def avail_images(call=None): - ''' + """ Return available Linode images. CLI Example: @@ -117,24 +95,24 @@ def avail_images(call=None): salt-cloud --list-images my-linode-config salt-cloud -f avail_images my-linode-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The avail_images function must be called with -f or --function.' + "The avail_images function must be called with -f or --function." ) - response = _query('avail', 'distributions') + response = _query("avail", "distributions") ret = {} - for item in response['DATA']: - name = item['LABEL'] + for item in response["DATA"]: + name = item["LABEL"] ret[name] = item return ret def avail_locations(call=None): - ''' + """ Return available Linode datacenter locations. CLI Example: @@ -143,24 +121,24 @@ def avail_locations(call=None): salt-cloud --list-locations my-linode-config salt-cloud -f avail_locations my-linode-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The avail_locations function must be called with -f or --function.' + "The avail_locations function must be called with -f or --function." ) - response = _query('avail', 'datacenters') + response = _query("avail", "datacenters") ret = {} - for item in response['DATA']: - name = item['LOCATION'] + for item in response["DATA"]: + name = item["LOCATION"] ret[name] = item return ret def avail_sizes(call=None): - ''' + """ Return available Linode sizes. CLI Example: @@ -169,24 +147,24 @@ def avail_sizes(call=None): salt-cloud --list-sizes my-linode-config salt-cloud -f avail_sizes my-linode-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The avail_locations function must be called with -f or --function.' + "The avail_locations function must be called with -f or --function." ) - response = _query('avail', 'LinodePlans') + response = _query("avail", "LinodePlans") ret = {} - for item in response['DATA']: - name = item['LABEL'] + for item in response["DATA"]: + name = item["LABEL"] ret[name] = item return ret def boot(name=None, kwargs=None, call=None): - ''' + """ Boot a Linode. name @@ -218,31 +196,27 @@ def boot(name=None, kwargs=None, call=None): salt-cloud -f boot my-linode-config name=my-instance config_id=10 salt-cloud -f boot my-linode-config linode_id=1225876 config_id=10 - ''' - if name is None and call == 'action': - raise SaltCloudSystemExit( - 'The boot action requires a \'name\'.' - ) + """ + if name is None and call == "action": + raise SaltCloudSystemExit("The boot action requires a 'name'.") if kwargs is None: kwargs = {} - linode_id = kwargs.get('linode_id', None) - config_id = kwargs.get('config_id', None) - check_running = kwargs.get('check_running', True) + linode_id = kwargs.get("linode_id", None) + config_id = kwargs.get("config_id", None) + check_running = kwargs.get("check_running", True) - if call == 'function': - name = kwargs.get('name', None) + if call == "function": + name = kwargs.get("name", None) if name is None and linode_id is None: raise SaltCloudSystemExit( - 'The boot function requires either a \'name\' or a \'linode_id\'.' + "The boot function requires either a 'name' or a 'linode_id'." ) if config_id is None: - raise SaltCloudSystemExit( - 'The boot function requires a \'config_id\'.' - ) + raise SaltCloudSystemExit("The boot function requires a 'config_id'.") if linode_id is None: linode_id = get_linode_id_from_name(name) @@ -252,28 +226,28 @@ def boot(name=None, kwargs=None, call=None): # Check if Linode is running first if check_running is True: - status = get_linode(kwargs={'linode_id': linode_id})['STATUS'] - if status == '1': + status = get_linode(kwargs={"linode_id": linode_id})["STATUS"] + if status == "1": raise SaltCloudSystemExit( - 'Cannot boot Linode {0}. ' - 'Linode {0} is already running.'.format(linode_item) + "Cannot boot Linode {0}. " + "Linode {0} is already running.".format(linode_item) ) # Boot the VM and get the JobID from Linode - response = _query('linode', 'boot', - args={'LinodeID': linode_id, - 'ConfigID': config_id})['DATA'] - boot_job_id = response['JobID'] + response = _query( + "linode", "boot", args={"LinodeID": linode_id, "ConfigID": config_id} + )["DATA"] + boot_job_id = response["JobID"] if not _wait_for_job(linode_id, boot_job_id): - log.error('Boot failed for Linode %s.', linode_item) + log.error("Boot failed for Linode %s.", linode_item) return False return True def clone(kwargs=None, call=None): - ''' + """ Clone a Linode. linode_id @@ -290,47 +264,50 @@ def clone(kwargs=None, call=None): .. code-block:: bash salt-cloud -f clone my-linode-config linode_id=1234567 datacenter_id=2 plan_id=5 - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The clone function must be called with -f or --function.' + "The clone function must be called with -f or --function." ) if kwargs is None: kwargs = {} - linode_id = kwargs.get('linode_id', None) - datacenter_id = kwargs.get('datacenter_id', None) - plan_id = kwargs.get('plan_id', None) + linode_id = kwargs.get("linode_id", None) + datacenter_id = kwargs.get("datacenter_id", None) + plan_id = kwargs.get("plan_id", None) required_params = [linode_id, datacenter_id, plan_id] for item in required_params: if item is None: raise SaltCloudSystemExit( - 'The clone function requires a \'linode_id\', \'datacenter_id\', ' - 'and \'plan_id\' to be provided.' + "The clone function requires a 'linode_id', 'datacenter_id', " + "and 'plan_id' to be provided." ) clone_args = { - 'LinodeID': linode_id, - 'DatacenterID': datacenter_id, - 'PlanID': plan_id + "LinodeID": linode_id, + "DatacenterID": datacenter_id, + "PlanID": plan_id, } - return _query('linode', 'clone', args=clone_args) + return _query("linode", "clone", args=clone_args) def create(vm_): - ''' + """ Create a single Linode VM. - ''' - name = vm_['name'] + """ + name = vm_["name"] try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'linode', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "linode", vm_["profile"], vm_=vm_ + ) + is False + ): return False except AttributeError: pass @@ -338,28 +315,30 @@ def create(vm_): if _validate_name(name) is False: return False - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(name), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(name), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', name) + log.info("Creating Cloud VM %s", name) data = {} - kwargs = {'name': name} + kwargs = {"name": name} plan_id = None - size = vm_.get('size') + size = vm_.get("size") if size: - kwargs['size'] = size - plan_id = get_plan_id(kwargs={'label': size}) + kwargs["size"] = size + plan_id = get_plan_id(kwargs={"label": size}) datacenter_id = None - location = vm_.get('location') + location = vm_.get("location") if location: try: datacenter_id = get_datacenter_id(location) @@ -368,87 +347,99 @@ def create(vm_): # use the create function from Linode's API. Dallas's datacenter id is 2. datacenter_id = 2 - clonefrom_name = vm_.get('clonefrom') + clonefrom_name = vm_.get("clonefrom") cloning = True if clonefrom_name else False if cloning: linode_id = get_linode_id_from_name(clonefrom_name) - clone_source = get_linode(kwargs={'linode_id': linode_id}) + clone_source = get_linode(kwargs={"linode_id": linode_id}) kwargs = { - 'clonefrom': clonefrom_name, - 'image': 'Clone of {0}'.format(clonefrom_name), + "clonefrom": clonefrom_name, + "image": "Clone of {0}".format(clonefrom_name), } if size is None: - size = clone_source['TOTALRAM'] - kwargs['size'] = size - plan_id = clone_source['PLANID'] + size = clone_source["TOTALRAM"] + kwargs["size"] = size + plan_id = clone_source["PLANID"] if location is None: - datacenter_id = clone_source['DATACENTERID'] + datacenter_id = clone_source["DATACENTERID"] # Create new Linode from cloned Linode try: - result = clone(kwargs={'linode_id': linode_id, - 'datacenter_id': datacenter_id, - 'plan_id': plan_id}) + result = clone( + kwargs={ + "linode_id": linode_id, + "datacenter_id": datacenter_id, + "plan_id": plan_id, + } + ) except Exception as err: # pylint: disable=broad-except log.error( - 'Error cloning \'%s\' on Linode.\n\n' - 'The following exception was thrown by Linode when trying to ' - 'clone the specified machine:\n%s', - clonefrom_name, err, exc_info_on_loglevel=logging.DEBUG + "Error cloning '%s' on Linode.\n\n" + "The following exception was thrown by Linode when trying to " + "clone the specified machine:\n%s", + clonefrom_name, + err, + exc_info_on_loglevel=logging.DEBUG, ) return False else: - kwargs['image'] = vm_['image'] + kwargs["image"] = vm_["image"] # Create Linode try: - result = _query('linode', 'create', args={ - 'PLANID': plan_id, - 'DATACENTERID': datacenter_id - }) + result = _query( + "linode", + "create", + args={"PLANID": plan_id, "DATACENTERID": datacenter_id}, + ) except Exception as err: # pylint: disable=broad-except log.error( - 'Error creating %s on Linode\n\n' - 'The following exception was thrown by Linode when trying to ' - 'run the initial deployment:\n%s', - name, err, exc_info_on_loglevel=logging.DEBUG + "Error creating %s on Linode\n\n" + "The following exception was thrown by Linode when trying to " + "run the initial deployment:\n%s", + name, + err, + exc_info_on_loglevel=logging.DEBUG, ) return False - if 'ERRORARRAY' in result: - for error_data in result['ERRORARRAY']: + if "ERRORARRAY" in result: + for error_data in result["ERRORARRAY"]: log.error( - 'Error creating %s on Linode\n\n' - 'The Linode API returned the following: %s\n', - name, error_data['ERRORMESSAGE'] + "Error creating %s on Linode\n\n" + "The Linode API returned the following: %s\n", + name, + error_data["ERRORMESSAGE"], ) return False - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(name), - args=__utils__['cloud.filter_event']('requesting', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(name), + args=__utils__["cloud.filter_event"]( + "requesting", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - node_id = _clean_data(result)['LinodeID'] - data['id'] = node_id + node_id = _clean_data(result)["LinodeID"] + data["id"] = node_id - if not _wait_for_status(node_id, status=(_get_status_id_by_name('brand_new'))): + if not _wait_for_status(node_id, status=(_get_status_id_by_name("brand_new"))): log.error( - 'Error creating %s on LINODE\n\n' - 'while waiting for initial ready status', - name, exc_info_on_loglevel=logging.DEBUG + "Error creating %s on LINODE\n\n" "while waiting for initial ready status", + name, + exc_info_on_loglevel=logging.DEBUG, ) # Update the Linode's Label to reflect the given VM name - update_linode(node_id, update_args={'Label': name}) - log.debug('Set name for %s - was linode%s.', name, node_id) + update_linode(node_id, update_args={"Label": name}) + log.debug("Set name for %s - was linode%s.", name, node_id) # Add private IP address if requested private_ip_assignment = get_private_ip(vm_) @@ -460,85 +451,89 @@ def create(vm_): # If ssh_interface is set to use private_ips, but assign_private_ip # wasn't set to True, let's help out and create a private ip. - if ssh_interface == 'private_ips' and private_ip_assignment is False: + if ssh_interface == "private_ips" and private_ip_assignment is False: create_private_ip(node_id) private_ip_assignment = True if cloning: - config_id = get_config_id(kwargs={'linode_id': node_id})['config_id'] + config_id = get_config_id(kwargs={"linode_id": node_id})["config_id"] else: # Create disks and get ids - log.debug('Creating disks for %s', name) - root_disk_id = create_disk_from_distro(vm_, node_id)['DiskID'] - swap_disk_id = create_swap_disk(vm_, node_id)['DiskID'] + log.debug("Creating disks for %s", name) + root_disk_id = create_disk_from_distro(vm_, node_id)["DiskID"] + swap_disk_id = create_swap_disk(vm_, node_id)["DiskID"] # Create a ConfigID using disk ids - config_id = create_config(kwargs={'name': name, - 'linode_id': node_id, - 'root_disk_id': root_disk_id, - 'swap_disk_id': swap_disk_id})['ConfigID'] + config_id = create_config( + kwargs={ + "name": name, + "linode_id": node_id, + "root_disk_id": root_disk_id, + "swap_disk_id": swap_disk_id, + } + )["ConfigID"] # Boot the Linode - boot(kwargs={'linode_id': node_id, - 'config_id': config_id, - 'check_running': False}) + boot(kwargs={"linode_id": node_id, "config_id": config_id, "check_running": False}) - node_data = get_linode(kwargs={'linode_id': node_id}) + node_data = get_linode(kwargs={"linode_id": node_id}) ips = get_ips(node_id) - state = int(node_data['STATUS']) + state = int(node_data["STATUS"]) - data['image'] = kwargs['image'] - data['name'] = name - data['size'] = size - data['state'] = _get_status_descr_by_id(state) - data['private_ips'] = ips['private_ips'] - data['public_ips'] = ips['public_ips'] + data["image"] = kwargs["image"] + data["name"] = name + data["size"] = size + data["state"] = _get_status_descr_by_id(state) + data["private_ips"] = ips["private_ips"] + data["public_ips"] = ips["public_ips"] # Pass the correct IP address to the bootstrap ssh_host key - if ssh_interface == 'private_ips': - vm_['ssh_host'] = data['private_ips'][0] + if ssh_interface == "private_ips": + vm_["ssh_host"] = data["private_ips"][0] else: - vm_['ssh_host'] = data['public_ips'][0] + vm_["ssh_host"] = data["public_ips"][0] # If a password wasn't supplied in the profile or provider config, set it now. - vm_['password'] = get_password(vm_) + vm_["password"] = get_password(vm_) # Make public_ips and private_ips available to the bootstrap script. - vm_['public_ips'] = ips['public_ips'] - vm_['private_ips'] = ips['private_ips'] + vm_["public_ips"] = ips["public_ips"] + vm_["private_ips"] = ips["private_ips"] # Send event that the instance has booted. - __utils__['cloud.fire_event']( - 'event', - 'waiting for ssh', - 'salt/cloud/{0}/waiting_for_ssh'.format(name), - sock_dir=__opts__['sock_dir'], - args={'ip_address': vm_['ssh_host']}, - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "waiting for ssh", + "salt/cloud/{0}/waiting_for_ssh".format(name), + sock_dir=__opts__["sock_dir"], + args={"ip_address": vm_["ssh_host"]}, + transport=__opts__["transport"], ) # Bootstrap! - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data) - log.info('Created Cloud VM \'%s\'', name) - log.debug('\'%s\' VM creation details:\n%s', name, pprint.pformat(data)) + log.info("Created Cloud VM '%s'", name) + log.debug("'%s' VM creation details:\n%s", name, pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(name), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(name), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def create_config(kwargs=None, call=None): - ''' + """ Creates a Linode Configuration Profile. name @@ -560,21 +555,21 @@ def create_config(kwargs=None, call=None): kernel_id The ID of the kernel to use for this configuration profile. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The create_config function must be called with -f or --function.' + "The create_config function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - linode_id = kwargs.get('linode_id', None) - root_disk_id = kwargs.get('root_disk_id', None) - swap_disk_id = kwargs.get('swap_disk_id', None) - data_disk_id = kwargs.get('data_disk_id', None) - kernel_id = kwargs.get('kernel_id', None) + name = kwargs.get("name", None) + linode_id = kwargs.get("linode_id", None) + root_disk_id = kwargs.get("root_disk_id", None) + swap_disk_id = kwargs.get("swap_disk_id", None) + data_disk_id = kwargs.get("data_disk_id", None) + kernel_id = kwargs.get("kernel_id", None) if kernel_id is None: # 138 appears to always be the latest 64-bit kernel for Linux @@ -584,27 +579,28 @@ def create_config(kwargs=None, call=None): for item in required_params: if item is None: raise SaltCloudSystemExit( - 'The create_config functions requires a \'name\', \'linode_id\', ' - '\'root_disk_id\', and \'swap_disk_id\'.' + "The create_config functions requires a 'name', 'linode_id', " + "'root_disk_id', and 'swap_disk_id'." ) - disklist = '{0},{1}'.format(root_disk_id, swap_disk_id) + disklist = "{0},{1}".format(root_disk_id, swap_disk_id) if data_disk_id is not None: - disklist = '{0},{1},{2}'.format(root_disk_id, swap_disk_id, data_disk_id) + disklist = "{0},{1},{2}".format(root_disk_id, swap_disk_id, data_disk_id) - config_args = {'LinodeID': linode_id, - 'KernelID': kernel_id, - 'Label': name, - 'DiskList': disklist - } + config_args = { + "LinodeID": linode_id, + "KernelID": kernel_id, + "Label": name, + "DiskList": disklist, + } - result = _query('linode', 'config.create', args=config_args) + result = _query("linode", "config.create", args=config_args) return _clean_data(result) def create_disk_from_distro(vm_, linode_id, swap_size=None): - r''' + r""" Creates the disk for the Linode from the distribution. vm\_ @@ -616,7 +612,7 @@ def create_disk_from_distro(vm_, linode_id, swap_size=None): swap_size The size of the disk, in MB. - ''' + """ kwargs = {} if swap_size is None: @@ -626,26 +622,28 @@ def create_disk_from_distro(vm_, linode_id, swap_size=None): root_password = get_password(vm_) if pub_key: - kwargs.update({'rootSSHKey': pub_key}) + kwargs.update({"rootSSHKey": pub_key}) if root_password: - kwargs.update({'rootPass': root_password}) + kwargs.update({"rootPass": root_password}) else: - raise SaltCloudConfigError( - 'The Linode driver requires a password.' - ) + raise SaltCloudConfigError("The Linode driver requires a password.") - kwargs.update({'LinodeID': linode_id, - 'DistributionID': get_distribution_id(vm_), - 'Label': vm_['name'], - 'Size': get_disk_size(vm_, swap_size, linode_id)}) + kwargs.update( + { + "LinodeID": linode_id, + "DistributionID": get_distribution_id(vm_), + "Label": vm_["name"], + "Size": get_disk_size(vm_, swap_size, linode_id), + } + ) - result = _query('linode', 'disk.createfromdistribution', args=kwargs) + result = _query("linode", "disk.createfromdistribution", args=kwargs) return _clean_data(result) def create_swap_disk(vm_, linode_id, swap_size=None): - r''' + r""" Creates the disk for the specified Linode. vm\_ @@ -656,25 +654,23 @@ def create_swap_disk(vm_, linode_id, swap_size=None): swap_size The size of the disk, in MB. - ''' + """ kwargs = {} if not swap_size: swap_size = get_swap_size(vm_) - kwargs.update({'LinodeID': linode_id, - 'Label': vm_['name'], - 'Type': 'swap', - 'Size': swap_size - }) + kwargs.update( + {"LinodeID": linode_id, "Label": vm_["name"], "Type": "swap", "Size": swap_size} + ) - result = _query('linode', 'disk.create', args=kwargs) + result = _query("linode", "disk.create", args=kwargs) return _clean_data(result) def create_data_disk(vm_=None, linode_id=None, data_size=None): - r''' + r""" Create a data disk for the linode (type is hardcoded to ext4 at the moment) .. versionadded:: 2016.3.0 @@ -688,34 +684,37 @@ def create_data_disk(vm_=None, linode_id=None, data_size=None): data_size The size of the disk, in MB. - ''' + """ kwargs = {} - kwargs.update({'LinodeID': linode_id, - 'Label': vm_['name']+"_data", - 'Type': 'ext4', - 'Size': data_size - }) + kwargs.update( + { + "LinodeID": linode_id, + "Label": vm_["name"] + "_data", + "Type": "ext4", + "Size": data_size, + } + ) - result = _query('linode', 'disk.create', args=kwargs) + result = _query("linode", "disk.create", args=kwargs) return _clean_data(result) def create_private_ip(linode_id): - r''' + r""" Creates a private IP for the specified Linode. linode_id The ID of the Linode to create the IP address for. - ''' - kwargs = {'LinodeID': linode_id} - result = _query('linode', 'ip.addprivate', args=kwargs) + """ + kwargs = {"LinodeID": linode_id} + result = _query("linode", "ip.addprivate", args=kwargs) return _clean_data(result) def destroy(name, call=None): - ''' + """ Destroys a Linode by name. name @@ -726,43 +725,46 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud -d vm_name - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) linode_id = get_linode_id_from_name(name) - response = _query('linode', 'delete', args={'LinodeID': linode_id, 'skipChecks': True}) - - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + response = _query( + "linode", "delete", args={"LinodeID": linode_id, "skipChecks": True} ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], + ) + + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) return response def get_config_id(kwargs=None, call=None): - ''' + """ Returns a config_id for a given linode. .. versionadded:: 2015.8.0 @@ -781,131 +783,138 @@ def get_config_id(kwargs=None, call=None): salt-cloud -f get_config_id my-linode-config name=my-linode salt-cloud -f get_config_id my-linode-config linode_id=1234567 - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The get_config_id function must be called with -f or --function.' + "The get_config_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - linode_id = kwargs.get('linode_id', None) + name = kwargs.get("name", None) + linode_id = kwargs.get("linode_id", None) if name is None and linode_id is None: raise SaltCloudSystemExit( - 'The get_config_id function requires either a \'name\' or a \'linode_id\' ' - 'to be provided.' + "The get_config_id function requires either a 'name' or a 'linode_id' " + "to be provided." ) if linode_id is None: linode_id = get_linode_id_from_name(name) - response = _query('linode', 'config.list', args={'LinodeID': linode_id})['DATA'] - config_id = {'config_id': response[0]['ConfigID']} + response = _query("linode", "config.list", args={"LinodeID": linode_id})["DATA"] + config_id = {"config_id": response[0]["ConfigID"]} return config_id def get_datacenter_id(location): - ''' + """ Returns the Linode Datacenter ID. location The location, or name, of the datacenter to get the ID from. - ''' + """ - return avail_locations()[location]['DATACENTERID'] + return avail_locations()[location]["DATACENTERID"] def get_disk_size(vm_, swap, linode_id): - r''' + r""" Returns the size of of the root disk in MB. vm\_ The VM to get the disk size for. - ''' - disk_size = get_linode(kwargs={'linode_id': linode_id})['TOTALHD'] + """ + disk_size = get_linode(kwargs={"linode_id": linode_id})["TOTALHD"] return config.get_cloud_config_value( - 'disk_size', vm_, __opts__, default=disk_size - swap + "disk_size", vm_, __opts__, default=disk_size - swap ) def get_data_disk_size(vm_, swap, linode_id): - ''' + """ Return the size of of the data disk in MB .. versionadded:: 2016.3.0 - ''' - disk_size = get_linode(kwargs={'linode_id': linode_id})['TOTALHD'] + """ + disk_size = get_linode(kwargs={"linode_id": linode_id})["TOTALHD"] root_disk_size = config.get_cloud_config_value( - 'disk_size', vm_, __opts__, default=disk_size - swap + "disk_size", vm_, __opts__, default=disk_size - swap ) return disk_size - root_disk_size - swap def get_distribution_id(vm_): - r''' + r""" Returns the distribution ID for a VM vm\_ The VM to get the distribution ID for - ''' - distributions = _query('avail', 'distributions')['DATA'] - vm_image_name = config.get_cloud_config_value('image', vm_, __opts__) + """ + distributions = _query("avail", "distributions")["DATA"] + vm_image_name = config.get_cloud_config_value("image", vm_, __opts__) - distro_id = '' + distro_id = "" for distro in distributions: - if vm_image_name == distro['LABEL']: - distro_id = distro['DISTRIBUTIONID'] + if vm_image_name == distro["LABEL"]: + distro_id = distro["DISTRIBUTIONID"] return distro_id if not distro_id: raise SaltCloudNotFound( - 'The DistributionID for the \'{0}\' profile could not be found.\n' - 'The \'{1}\' instance could not be provisioned. The following distributions ' - 'are available:\n{2}'.format( + "The DistributionID for the '{0}' profile could not be found.\n" + "The '{1}' instance could not be provisioned. The following distributions " + "are available:\n{2}".format( vm_image_name, - vm_['name'], - pprint.pprint(sorted([distro['LABEL'].encode(__salt_system_encoding__) for distro in distributions])) + vm_["name"], + pprint.pprint( + sorted( + [ + distro["LABEL"].encode(__salt_system_encoding__) + for distro in distributions + ] + ) + ), ) ) def get_ips(linode_id=None): - ''' + """ Returns public and private IP addresses. linode_id Limits the IP addresses returned to the specified Linode ID. - ''' + """ if linode_id: - ips = _query('linode', 'ip.list', args={'LinodeID': linode_id}) + ips = _query("linode", "ip.list", args={"LinodeID": linode_id}) else: - ips = _query('linode', 'ip.list') + ips = _query("linode", "ip.list") - ips = ips['DATA'] + ips = ips["DATA"] ret = {} for item in ips: - node_id = six.text_type(item['LINODEID']) - if item['ISPUBLIC'] == 1: - key = 'public_ips' + node_id = six.text_type(item["LINODEID"]) + if item["ISPUBLIC"] == 1: + key = "public_ips" else: - key = 'private_ips' + key = "private_ips" if ret.get(node_id) is None: - ret.update({node_id: {'public_ips': [], 'private_ips': []}}) - ret[node_id][key].append(item['IPADDRESS']) + ret.update({node_id: {"public_ips": [], "private_ips": []}}) + ret[node_id][key].append(item["IPADDRESS"]) # If linode_id was specified, only return the ips, and not the # dictionary based on the linode ID as a key. if linode_id: - _all_ips = {'public_ips': [], 'private_ips': []} + _all_ips = {"public_ips": [], "private_ips": []} matching_id = ret.get(six.text_type(linode_id)) if matching_id: - _all_ips['private_ips'] = matching_id['private_ips'] - _all_ips['public_ips'] = matching_id['public_ips'] + _all_ips["private_ips"] = matching_id["private_ips"] + _all_ips["public_ips"] = matching_id["public_ips"] ret = _all_ips @@ -913,7 +922,7 @@ def get_ips(linode_id=None): def get_linode(kwargs=None, call=None): - ''' + """ Returns data for a single named Linode. name @@ -931,70 +940,71 @@ def get_linode(kwargs=None, call=None): salt-cloud -f get_linode my-linode-config name=my-instance salt-cloud -f get_linode my-linode-config linode_id=1234567 - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_linode function must be called with -f or --function.' + "The get_linode function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - linode_id = kwargs.get('linode_id', None) + name = kwargs.get("name", None) + linode_id = kwargs.get("linode_id", None) if name is None and linode_id is None: raise SaltCloudSystemExit( - 'The get_linode function requires either a \'name\' or a \'linode_id\'.' + "The get_linode function requires either a 'name' or a 'linode_id'." ) if linode_id is None: linode_id = get_linode_id_from_name(name) - result = _query('linode', 'list', args={'LinodeID': linode_id}) + result = _query("linode", "list", args={"LinodeID": linode_id}) - return result['DATA'][0] + return result["DATA"][0] def get_linode_id_from_name(name): - ''' + """ Returns the Linode ID for a VM from the provided name. name The name of the Linode from which to get the Linode ID. Required. - ''' - nodes = _query('linode', 'list')['DATA'] + """ + nodes = _query("linode", "list")["DATA"] - linode_id = '' + linode_id = "" for node in nodes: - if name == node['LABEL']: - linode_id = node['LINODEID'] + if name == node["LABEL"]: + linode_id = node["LINODEID"] return linode_id if not linode_id: raise SaltCloudNotFound( - 'The specified name, {0}, could not be found.'.format(name) + "The specified name, {0}, could not be found.".format(name) ) def get_password(vm_): - r''' + r""" Return the password to use for a VM. vm\_ The configuration to obtain the password from. - ''' + """ return config.get_cloud_config_value( - 'password', vm_, __opts__, + "password", + vm_, + __opts__, default=config.get_cloud_config_value( - 'passwd', vm_, __opts__, - search_global=False + "passwd", vm_, __opts__, search_global=False ), - search_global=False + search_global=False, ) def _decode_linode_plan_label(label): - ''' + """ Attempts to decode a user-supplied Linode plan label into the format in Linode API output @@ -1003,20 +1013,24 @@ def _decode_linode_plan_label(label): Example: `Linode 2048` will decode to `Linode 2GB` - ''' + """ sizes = avail_sizes() if label not in sizes: - if 'GB' in label: + if "GB" in label: raise SaltCloudException( - 'Invalid Linode plan ({}) specified - call avail_sizes() for all available options'.format(label) + "Invalid Linode plan ({}) specified - call avail_sizes() for all available options".format( + label + ) ) else: plan = label.split() if len(plan) != 2: raise SaltCloudException( - 'Invalid Linode plan ({}) specified - call avail_sizes() for all available options'.format(label) + "Invalid Linode plan ({}) specified - call avail_sizes() for all available options".format( + label + ) ) plan_type = plan[0] @@ -1024,33 +1038,38 @@ def _decode_linode_plan_label(label): plan_size = int(plan[1]) except TypeError: plan_size = 0 - log.debug('Failed to decode Linode plan label in Cloud Profile: %s', label) + log.debug( + "Failed to decode Linode plan label in Cloud Profile: %s", label + ) - if plan_type == 'Linode' and plan_size == 1024: - plan_type = 'Nanode' + if plan_type == "Linode" and plan_size == 1024: + plan_type = "Nanode" - plan_size = plan_size/1024 + plan_size = plan_size / 1024 new_label = "{} {}GB".format(plan_type, plan_size) if new_label not in sizes: raise SaltCloudException( - 'Invalid Linode plan ({}) specified - call avail_sizes() for all available options'.format(new_label) + "Invalid Linode plan ({}) specified - call avail_sizes() for all available options".format( + new_label + ) ) log.warning( - 'An outdated Linode plan label was detected in your Cloud ' - 'Profile (%s). Please update the profile to use the new ' - 'label format (%s) for the requested Linode plan size.', - label, new_label + "An outdated Linode plan label was detected in your Cloud " + "Profile (%s). Please update the profile to use the new " + "label format (%s) for the requested Linode plan size.", + label, + new_label, ) label = new_label - return sizes[label]['PLANID'] + return sizes[label]["PLANID"] def get_plan_id(kwargs=None, call=None): - ''' + """ Returns the Linode Plan ID. label @@ -1061,20 +1080,18 @@ def get_plan_id(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_plan_id linode label="Linode 1024" - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The show_instance action must be called with -f or --function.' + "The show_instance action must be called with -f or --function." ) if kwargs is None: kwargs = {} - label = kwargs.get('label', None) + label = kwargs.get("label", None) if label is None: - raise SaltCloudException( - 'The get_plan_id function requires a \'label\'.' - ) + raise SaltCloudException("The get_plan_id function requires a 'label'.") label = _decode_linode_plan_label(label) @@ -1082,72 +1099,70 @@ def get_plan_id(kwargs=None, call=None): def get_private_ip(vm_): - ''' + """ Return True if a private ip address is requested - ''' + """ return config.get_cloud_config_value( - 'assign_private_ip', vm_, __opts__, default=False + "assign_private_ip", vm_, __opts__, default=False ) def get_data_disk(vm_): - ''' + """ Return True if a data disk is requested .. versionadded:: 2016.3.0 - ''' + """ return config.get_cloud_config_value( - 'allocate_data_disk', vm_, __opts__, default=False + "allocate_data_disk", vm_, __opts__, default=False ) def get_pub_key(vm_): - r''' + r""" Return the SSH pubkey. vm\_ The configuration to obtain the public key from. - ''' + """ return config.get_cloud_config_value( - 'ssh_pubkey', vm_, __opts__, search_global=False + "ssh_pubkey", vm_, __opts__, search_global=False ) def get_swap_size(vm_): - r''' + r""" Returns the amoutn of swap space to be used in MB. vm\_ The VM profile to obtain the swap size from. - ''' - return config.get_cloud_config_value( - 'swap', vm_, __opts__, default=128 - ) + """ + return config.get_cloud_config_value("swap", vm_, __opts__, default=128) def get_vm_size(vm_): - r''' + r""" Returns the VM's size. vm\_ The VM to get the size for. - ''' - vm_size = config.get_cloud_config_value('size', vm_, __opts__) - ram = avail_sizes()[vm_size]['RAM'] + """ + vm_size = config.get_cloud_config_value("size", vm_, __opts__) + ram = avail_sizes()[vm_size]["RAM"] - if vm_size.startswith('Linode'): - vm_size = vm_size.replace('Linode ', '') + if vm_size.startswith("Linode"): + vm_size = vm_size.replace("Linode ", "") if ram == int(vm_size): return ram else: raise SaltCloudNotFound( - 'The specified size, {0}, could not be found.'.format(vm_size) + "The specified size, {0}, could not be found.".format(vm_size) ) def list_nodes(call=None): - ''' + """ Returns a list of linodes, keeping only a brief listing. CLI Example: @@ -1163,16 +1178,16 @@ def list_nodes(call=None): The ``image`` label only displays information about the VM's distribution vendor, such as "Debian" or "RHEL" and does not display the actual image name. This is due to a limitation of the Linode API. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) return _list_linodes(full=False) def list_nodes_full(call=None): - ''' + """ List linodes, with all available information. CLI Example: @@ -1188,16 +1203,16 @@ def list_nodes_full(call=None): The ``image`` label only displays information about the VM's distribution vendor, such as "Debian" or "RHEL" and does not display the actual image name. This is due to a limitation of the Linode API. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) return _list_linodes(full=True) def list_nodes_min(call=None): - ''' + """ Return a list of the VMs that are on the provider. Only a list of VM names and their state is returned. This is the minimum amount of information needed to check for existing VMs. @@ -1210,20 +1225,20 @@ def list_nodes_min(call=None): salt-cloud -f list_nodes_min my-linode-config salt-cloud --function list_nodes_min my-linode-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_min function must be called with -f or --function.' + "The list_nodes_min function must be called with -f or --function." ) ret = {} - nodes = _query('linode', 'list')['DATA'] + nodes = _query("linode", "list")["DATA"] for node in nodes: - name = node['LABEL'] + name = node["LABEL"] this_node = { - 'id': six.text_type(node['LINODEID']), - 'state': _get_status_descr_by_id(int(node['STATUS'])) + "id": six.text_type(node["LINODEID"]), + "state": _get_status_descr_by_id(int(node["STATUS"])), } ret[name] = this_node @@ -1232,16 +1247,16 @@ def list_nodes_min(call=None): def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields. - ''' - return __utils__['cloud.list_nodes_select']( - list_nodes_full(), __opts__['query.selection'], call, + """ + return __utils__["cloud.list_nodes_select"]( + list_nodes_full(), __opts__["query.selection"], call, ) def reboot(name, call=None): - ''' + """ Reboot a linode. .. versionadded:: 2015.8.0 @@ -1254,26 +1269,26 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot vm_name - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudException( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) node_id = get_linode_id_from_name(name) - response = _query('linode', 'reboot', args={'LinodeID': node_id}) + response = _query("linode", "reboot", args={"LinodeID": node_id}) data = _clean_data(response) - reboot_jid = data['JobID'] + reboot_jid = data["JobID"] if not _wait_for_job(node_id, reboot_jid): - log.error('Reboot failed for %s.', name) + log.error("Reboot failed for %s.", name) return False return data def show_instance(name, call=None): - ''' + """ Displays details about a particular Linode VM. Either a name or a linode_id must be provided. @@ -1293,30 +1308,32 @@ def show_instance(name, call=None): The ``image`` label only displays information about the VM's distribution vendor, such as "Debian" or "RHEL" and does not display the actual image name. This is due to a limitation of the Linode API. - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudException( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) node_id = get_linode_id_from_name(name) - node_data = get_linode(kwargs={'linode_id': node_id}) + node_data = get_linode(kwargs={"linode_id": node_id}) ips = get_ips(node_id) - state = int(node_data['STATUS']) + state = int(node_data["STATUS"]) - ret = {'id': node_data['LINODEID'], - 'image': node_data['DISTRIBUTIONVENDOR'], - 'name': node_data['LABEL'], - 'size': node_data['TOTALRAM'], - 'state': _get_status_descr_by_id(state), - 'private_ips': ips['private_ips'], - 'public_ips': ips['public_ips']} + ret = { + "id": node_data["LINODEID"], + "image": node_data["DISTRIBUTIONVENDOR"], + "name": node_data["LABEL"], + "size": node_data["TOTALRAM"], + "state": _get_status_descr_by_id(state), + "private_ips": ips["private_ips"], + "public_ips": ips["public_ips"], + } return ret def show_pricing(kwargs=None, call=None): - ''' + """ Show pricing for a particular profile. This is only an estimate, based on unofficial pricing sources. @@ -1327,41 +1344,37 @@ def show_pricing(kwargs=None, call=None): .. code-block:: bash salt-cloud -f show_pricing my-linode-config profile=my-linode-profile - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudException( - 'The show_instance action must be called with -f or --function.' + "The show_instance action must be called with -f or --function." ) - profile = __opts__['profiles'].get(kwargs['profile'], {}) + profile = __opts__["profiles"].get(kwargs["profile"], {}) if not profile: - raise SaltCloudNotFound( - 'The requested profile was not found.' - ) + raise SaltCloudNotFound("The requested profile was not found.") # Make sure the profile belongs to Linode - provider = profile.get('provider', '0:0') - comps = provider.split(':') - if len(comps) < 2 or comps[1] != 'linode': - raise SaltCloudException( - 'The requested profile does not belong to Linode.' - ) + provider = profile.get("provider", "0:0") + comps = provider.split(":") + if len(comps) < 2 or comps[1] != "linode": + raise SaltCloudException("The requested profile does not belong to Linode.") - plan_id = get_plan_id(kwargs={'label': profile['size']}) - response = _query('avail', 'linodeplans', args={'PlanID': plan_id})['DATA'][0] + plan_id = get_plan_id(kwargs={"label": profile["size"]}) + response = _query("avail", "linodeplans", args={"PlanID": plan_id})["DATA"][0] ret = {} - ret['per_hour'] = response['HOURLY'] - ret['per_day'] = ret['per_hour'] * 24 - ret['per_week'] = ret['per_day'] * 7 - ret['per_month'] = response['PRICE'] - ret['per_year'] = ret['per_month'] * 12 + ret["per_hour"] = response["HOURLY"] + ret["per_day"] = ret["per_hour"] * 24 + ret["per_week"] = ret["per_day"] * 7 + ret["per_month"] = response["PRICE"] + ret["per_year"] = ret["per_month"] * 12 - return {profile['profile']: ret} + return {profile["profile"]: ret} def start(name, call=None): - ''' + """ Start a VM in Linode. name @@ -1372,34 +1385,31 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a stop vm_name - ''' - if call != 'action': - raise SaltCloudException( - 'The start action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudException("The start action must be called with -a or --action.") node_id = get_linode_id_from_name(name) - node = get_linode(kwargs={'linode_id': node_id}) + node = get_linode(kwargs={"linode_id": node_id}) - if node['STATUS'] == 1: - return {'success': True, - 'action': 'start', - 'state': 'Running', - 'msg': 'Machine already running'} + if node["STATUS"] == 1: + return { + "success": True, + "action": "start", + "state": "Running", + "msg": "Machine already running", + } - response = _query('linode', 'boot', args={'LinodeID': node_id})['DATA'] + response = _query("linode", "boot", args={"LinodeID": node_id})["DATA"] - if _wait_for_job(node_id, response['JobID']): - return {'state': 'Running', - 'action': 'start', - 'success': True} + if _wait_for_job(node_id, response["JobID"]): + return {"state": "Running", "action": "start", "success": True} else: - return {'action': 'start', - 'success': False} + return {"action": "start", "success": False} def stop(name, call=None): - ''' + """ Stop a VM in Linode. name @@ -1410,33 +1420,26 @@ def stop(name, call=None): .. code-block:: bash salt-cloud -a stop vm_name - ''' - if call != 'action': - raise SaltCloudException( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudException("The stop action must be called with -a or --action.") node_id = get_linode_id_from_name(name) - node = get_linode(kwargs={'linode_id': node_id}) + node = get_linode(kwargs={"linode_id": node_id}) - if node['STATUS'] == 2: - return {'success': True, - 'state': 'Stopped', - 'msg': 'Machine already stopped'} + if node["STATUS"] == 2: + return {"success": True, "state": "Stopped", "msg": "Machine already stopped"} - response = _query('linode', 'shutdown', args={'LinodeID': node_id})['DATA'] + response = _query("linode", "shutdown", args={"LinodeID": node_id})["DATA"] - if _wait_for_job(node_id, response['JobID']): - return {'state': 'Stopped', - 'action': 'stop', - 'success': True} + if _wait_for_job(node_id, response["JobID"]): + return {"state": "Stopped", "action": "stop", "success": True} else: - return {'action': 'stop', - 'success': False} + return {"action": "stop", "success": False} def update_linode(linode_id, update_args=None): - ''' + """ Updates a Linode's properties. linode_id @@ -1444,146 +1447,144 @@ def update_linode(linode_id, update_args=None): update_args The args to update the Linode with. Must be in dictionary form. - ''' - update_args.update({'LinodeID': linode_id}) + """ + update_args.update({"LinodeID": linode_id}) - result = _query('linode', 'update', args=update_args) + result = _query("linode", "update", args=update_args) return _clean_data(result) def _clean_data(api_response): - ''' + """ Returns the DATA response from a Linode API query as a single pre-formatted dictionary api_response The query to be cleaned. - ''' + """ data = {} - data.update(api_response['DATA']) + data.update(api_response["DATA"]) if not data: - response_data = api_response['DATA'] + response_data = api_response["DATA"] data.update(response_data) return data def _list_linodes(full=False): - ''' + """ Helper function to format and parse linode data - ''' - nodes = _query('linode', 'list')['DATA'] + """ + nodes = _query("linode", "list")["DATA"] ips = get_ips() ret = {} for node in nodes: this_node = {} - linode_id = six.text_type(node['LINODEID']) + linode_id = six.text_type(node["LINODEID"]) - this_node['id'] = linode_id - this_node['image'] = node['DISTRIBUTIONVENDOR'] - this_node['name'] = node['LABEL'] - this_node['size'] = node['TOTALRAM'] + this_node["id"] = linode_id + this_node["image"] = node["DISTRIBUTIONVENDOR"] + this_node["name"] = node["LABEL"] + this_node["size"] = node["TOTALRAM"] - state = int(node['STATUS']) - this_node['state'] = _get_status_descr_by_id(state) + state = int(node["STATUS"]) + this_node["state"] = _get_status_descr_by_id(state) for key, val in six.iteritems(ips): if key == linode_id: - this_node['private_ips'] = val['private_ips'] - this_node['public_ips'] = val['public_ips'] + this_node["private_ips"] = val["private_ips"] + this_node["public_ips"] = val["public_ips"] if full: - this_node['extra'] = node + this_node["extra"] = node - ret[node['LABEL']] = this_node + ret[node["LABEL"]] = this_node return ret -def _query(action=None, - command=None, - args=None, - method='GET', - header_dict=None, - data=None, - url='https://api.linode.com/'): - ''' +def _query( + action=None, + command=None, + args=None, + method="GET", + header_dict=None, + data=None, + url="https://api.linode.com/", +): + """ Make a web call to the Linode API. - ''' + """ global LASTCALL vm_ = get_configured_provider() ratelimit_sleep = config.get_cloud_config_value( - 'ratelimit_sleep', vm_, __opts__, search_global=False, default=0, - ) - apikey = config.get_cloud_config_value( - 'apikey', vm_, __opts__, search_global=False + "ratelimit_sleep", vm_, __opts__, search_global=False, default=0, ) + apikey = config.get_cloud_config_value("apikey", vm_, __opts__, search_global=False) if not isinstance(args, dict): args = {} - if 'api_key' not in args.keys(): - args['api_key'] = apikey + if "api_key" not in args.keys(): + args["api_key"] = apikey - if action and 'api_action' not in args.keys(): - args['api_action'] = '{0}.{1}'.format(action, command) + if action and "api_action" not in args.keys(): + args["api_action"] = "{0}.{1}".format(action, command) if header_dict is None: header_dict = {} - if method != 'POST': - header_dict['Accept'] = 'application/json' + if method != "POST": + header_dict["Accept"] = "application/json" decode = True - if method == 'DELETE': + if method == "DELETE": decode = False now = int(time.mktime(datetime.datetime.now().timetuple())) if LASTCALL >= now: time.sleep(ratelimit_sleep) - result = __utils__['http.query']( + result = __utils__["http.query"]( url, method, params=args, data=data, header_dict=header_dict, decode=decode, - decode_type='json', + decode_type="json", text=True, status=True, - hide_fields=['api_key', 'rootPass'], + hide_fields=["api_key", "rootPass"], opts=__opts__, ) - if 'ERRORARRAY' in result['dict']: - if result['dict']['ERRORARRAY']: + if "ERRORARRAY" in result["dict"]: + if result["dict"]["ERRORARRAY"]: error_list = [] - for error in result['dict']['ERRORARRAY']: - msg = error['ERRORMESSAGE'] + for error in result["dict"]["ERRORARRAY"]: + msg = error["ERRORMESSAGE"] if msg == "Authentication failed": - raise SaltCloudSystemExit( - 'Linode API Key is expired or invalid' - ) + raise SaltCloudSystemExit("Linode API Key is expired or invalid") else: error_list.append(msg) raise SaltCloudException( - 'Linode API reported error(s): {}'.format(", ".join(error_list)) + "Linode API reported error(s): {}".format(", ".join(error_list)) ) LASTCALL = int(time.mktime(datetime.datetime.now().timetuple())) - log.debug('Linode Response Status Code: %s', result['status']) + log.debug("Linode Response Status Code: %s", result["status"]) - return result['dict'] + return result["dict"] def _wait_for_job(linode_id, job_id, timeout=300, quiet=True): - ''' + """ Wait for a Job to return. linode_id @@ -1597,27 +1598,27 @@ def _wait_for_job(linode_id, job_id, timeout=300, quiet=True): quiet Log status updates to debug logs when True. Otherwise, logs to info. - ''' + """ interval = 5 iterations = int(timeout / interval) for i in range(0, iterations): - jobs_result = _query('linode', - 'job.list', - args={'LinodeID': linode_id})['DATA'] - if jobs_result[0]['JOBID'] == job_id and jobs_result[0]['HOST_SUCCESS'] == 1: + jobs_result = _query("linode", "job.list", args={"LinodeID": linode_id})["DATA"] + if jobs_result[0]["JOBID"] == job_id and jobs_result[0]["HOST_SUCCESS"] == 1: return True time.sleep(interval) log.log( logging.INFO if not quiet else logging.DEBUG, - 'Still waiting on Job %s for Linode %s.', job_id, linode_id + "Still waiting on Job %s for Linode %s.", + job_id, + linode_id, ) return False def _wait_for_status(linode_id, status=None, timeout=300, quiet=True): - ''' + """ Wait for a certain status from Linode. linode_id @@ -1631,9 +1632,9 @@ def _wait_for_status(linode_id, status=None, timeout=300, quiet=True): quiet Log status updates to debug logs when False. Otherwise, logs to info. - ''' + """ if status is None: - status = _get_status_id_by_name('brand_new') + status = _get_status_id_by_name("brand_new") status_desc_waiting = _get_status_descr_by_id(status) @@ -1641,58 +1642,60 @@ def _wait_for_status(linode_id, status=None, timeout=300, quiet=True): iterations = int(timeout / interval) for i in range(0, iterations): - result = get_linode(kwargs={'linode_id': linode_id}) + result = get_linode(kwargs={"linode_id": linode_id}) - if result['STATUS'] == status: + if result["STATUS"] == status: return True - status_desc_result = _get_status_descr_by_id(result['STATUS']) + status_desc_result = _get_status_descr_by_id(result["STATUS"]) time.sleep(interval) log.log( logging.INFO if not quiet else logging.DEBUG, - 'Status for Linode %s is \'%s\', waiting for \'%s\'.', - linode_id, status_desc_result, status_desc_waiting + "Status for Linode %s is '%s', waiting for '%s'.", + linode_id, + status_desc_result, + status_desc_waiting, ) return False def _get_status_descr_by_id(status_id): - ''' + """ Return linode status by ID status_id linode VM status ID - ''' + """ for status_name, status_data in six.iteritems(LINODE_STATUS): - if status_data['code'] == int(status_id): - return status_data['descr'] + if status_data["code"] == int(status_id): + return status_data["descr"] return LINODE_STATUS.get(status_id, None) def _get_status_id_by_name(status_name): - ''' + """ Return linode status description by internalstatus name status_name internal linode VM status name - ''' - return LINODE_STATUS.get(status_name, {}).get('code', None) + """ + return LINODE_STATUS.get(status_name, {}).get("code", None) def _validate_name(name): - ''' + """ Checks if the provided name fits Linode's labeling parameters. .. versionadded:: 2015.5.6 name The VM name to validate - ''' + """ name = six.text_type(name) name_length = len(name) - regex = re.compile(r'^[a-zA-Z0-9][A-Za-z0-9_-]*[a-zA-Z0-9]$') + regex = re.compile(r"^[a-zA-Z0-9][A-Za-z0-9_-]*[a-zA-Z0-9]$") if name_length < 3 or name_length > 48: ret = False @@ -1703,20 +1706,19 @@ def _validate_name(name): if ret is False: log.warning( - 'A Linode label may only contain ASCII letters or numbers, dashes, and ' - 'underscores, must begin and end with letters or numbers, and be at least ' - 'three characters in length.' + "A Linode label may only contain ASCII letters or numbers, dashes, and " + "underscores, must begin and end with letters or numbers, and be at least " + "three characters in length." ) return ret def _get_ssh_interface(vm_): - ''' + """ Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. - ''' + """ return config.get_cloud_config_value( - 'ssh_interface', vm_, __opts__, default='public_ips', - search_global=False + "ssh_interface", vm_, __opts__, default="public_ips", search_global=False ) diff --git a/salt/cloud/clouds/lxc.py b/salt/cloud/clouds/lxc.py index bc8528a7e76..318919301b3 100644 --- a/salt/cloud/clouds/lxc.py +++ b/salt/cloud/clouds/lxc.py @@ -1,31 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Install Salt on an LXC Container ================================ .. versionadded:: 2014.7.0 Please read :ref:`core config documentation `. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import os import pprint import time +import salt.client +import salt.config as config +import salt.runner + # Import salt cloud libs import salt.utils.cloud import salt.utils.json -import salt.config as config from salt.exceptions import SaltCloudSystemExit -import salt.client -import salt.runner - - # Import 3rd-party libs from salt.ext import six @@ -33,24 +33,24 @@ from salt.ext import six log = logging.getLogger(__name__) __FUN_TIMEOUT = { - 'cmd.run': 60 * 60, - 'test.ping': 10, - 'lxc.info': 40, - 'lxc.list': 300, - 'lxc.templates': 100, - 'grains.items': 100, + "cmd.run": 60 * 60, + "test.ping": 10, + "lxc.info": 40, + "lxc.list": 300, + "lxc.templates": 100, + "grains.items": 100, } __CACHED_CALLS = {} __CACHED_FUNS = { - 'test.ping': 3 * 60, # cache ping for 3 minutes - 'lxc.list': 2 # cache lxc.list for 2 seconds + "test.ping": 3 * 60, # cache ping for 3 minutes + "lxc.list": 2, # cache lxc.list for 2 seconds } def __virtual__(): - ''' + """ Needs no special configuration - ''' + """ return True @@ -58,29 +58,27 @@ def _get_grain_id(id_): if not get_configured_provider(): return infos = get_configured_provider() - return 'salt.cloud.lxc.{0}.{1}'.format(infos['target'], id_) + return "salt.cloud.lxc.{0}.{1}".format(infos["target"], id_) -def _minion_opts(cfg='minion'): - if 'conf_file' in __opts__: - default_dir = os.path.dirname(__opts__['conf_file']) +def _minion_opts(cfg="minion"): + if "conf_file" in __opts__: + default_dir = os.path.dirname(__opts__["conf_file"]) else: - default_dir = __opts__['config_dir'], - cfg = os.environ.get( - 'SALT_MINION_CONFIG', os.path.join(default_dir, cfg)) + default_dir = (__opts__["config_dir"],) + cfg = os.environ.get("SALT_MINION_CONFIG", os.path.join(default_dir, cfg)) opts = config.minion_config(cfg) return opts -def _master_opts(cfg='master'): - if 'conf_file' in __opts__: - default_dir = os.path.dirname(__opts__['conf_file']) +def _master_opts(cfg="master"): + if "conf_file" in __opts__: + default_dir = os.path.dirname(__opts__["conf_file"]) else: - default_dir = __opts__['config_dir'], - cfg = os.environ.get( - 'SALT_MASTER_CONFIG', os.path.join(default_dir, cfg)) + default_dir = (__opts__["config_dir"],) + cfg = os.environ.get("SALT_MASTER_CONFIG", os.path.join(default_dir, cfg)) opts = config.master_config(cfg) - opts['output'] = 'quiet' + opts["output"] = "quiet" return opts @@ -95,7 +93,7 @@ def _runner(): def _salt(fun, *args, **kw): - '''Execute a salt function on a specific minion + """Execute a salt function on a specific minion Special kwargs: @@ -105,32 +103,31 @@ def _salt(fun, *args, **kw): timeout for jobs salt_job_poll poll interval to wait for job finish result - ''' + """ try: - poll = kw.pop('salt_job_poll') + poll = kw.pop("salt_job_poll") except KeyError: poll = 0.1 try: - target = kw.pop('salt_target') + target = kw.pop("salt_target") except KeyError: target = None try: - timeout = int(kw.pop('salt_timeout')) + timeout = int(kw.pop("salt_timeout")) except (KeyError, ValueError): # try to has some low timeouts for very basic commands timeout = __FUN_TIMEOUT.get( - fun, - 900 # wait up to 15 minutes for the default timeout + fun, 900 # wait up to 15 minutes for the default timeout ) try: - kwargs = kw.pop('kwargs') + kwargs = kw.pop("kwargs") except KeyError: kwargs = {} if not target: infos = get_configured_provider() if not infos: return - target = infos['target'] + target = infos["target"] laps = time.time() cache = False if fun in __CACHED_FUNS: @@ -139,23 +136,23 @@ def _salt(fun, *args, **kw): try: sargs = salt.utils.json.dumps(args) except TypeError: - sargs = '' + sargs = "" try: skw = salt.utils.json.dumps(kw) except TypeError: - skw = '' + skw = "" try: skwargs = salt.utils.json.dumps(kwargs) except TypeError: - skwargs = '' + skwargs = "" cache_key = (laps, target, fun, sargs, skw, skwargs) if not cache or (cache and (cache_key not in __CACHED_CALLS)): conn = _client() runner = _runner() rkwargs = kwargs.copy() - rkwargs['timeout'] = timeout - rkwargs.setdefault('tgt_type', 'list') - kwargs.setdefault('tgt_type', 'list') + rkwargs["timeout"] = timeout + rkwargs.setdefault("tgt_type", "list") + kwargs.setdefault("tgt_type", "list") ping_retries = 0 # the target(s) have environ one minute to respond # we call 60 ping request, this prevent us @@ -163,16 +160,14 @@ def _salt(fun, *args, **kw): ping_max_retries = 60 ping = True # do not check ping... if we are pinguing - if fun == 'test.ping': + if fun == "test.ping": ping_retries = ping_max_retries + 1 # be sure that the executors are alive while ping_retries <= ping_max_retries: try: if ping_retries > 0: time.sleep(1) - pings = conn.cmd(tgt=target, - timeout=10, - fun='test.ping') + pings = conn.cmd(tgt=target, timeout=10, fun="test.ping") values = list(pings.values()) if not values: ping = False @@ -180,69 +175,61 @@ def _salt(fun, *args, **kw): if v is not True: ping = False if not ping: - raise ValueError('Unreachable') + raise ValueError("Unreachable") break except Exception: # pylint: disable=broad-except ping = False ping_retries += 1 - log.error('%s unreachable, retrying', target) + log.error("%s unreachable, retrying", target) if not ping: - raise SaltCloudSystemExit('Target {0} unreachable'.format(target)) - jid = conn.cmd_async(tgt=target, - fun=fun, - arg=args, - kwarg=kw, - **rkwargs) - cret = conn.cmd(tgt=target, - fun='saltutil.find_job', - arg=[jid], - timeout=10, - **kwargs) + raise SaltCloudSystemExit("Target {0} unreachable".format(target)) + jid = conn.cmd_async(tgt=target, fun=fun, arg=args, kwarg=kw, **rkwargs) + cret = conn.cmd( + tgt=target, fun="saltutil.find_job", arg=[jid], timeout=10, **kwargs + ) running = bool(cret.get(target, False)) endto = time.time() + timeout while running: rkwargs = { - 'tgt': target, - 'fun': 'saltutil.find_job', - 'arg': [jid], - 'timeout': 10 + "tgt": target, + "fun": "saltutil.find_job", + "arg": [jid], + "timeout": 10, } cret = conn.cmd(**rkwargs) running = bool(cret.get(target, False)) if not running: break if running and (time.time() > endto): - raise Exception('Timeout {0}s for {1} is elapsed'.format( - timeout, pprint.pformat(rkwargs))) + raise Exception( + "Timeout {0}s for {1} is elapsed".format( + timeout, pprint.pformat(rkwargs) + ) + ) time.sleep(poll) # timeout for the master to return data about a specific job - wait_for_res = float({ - 'test.ping': '5', - }.get(fun, '120')) + wait_for_res = float({"test.ping": "5"}.get(fun, "120")) while wait_for_res: wait_for_res -= 0.5 - cret = runner.cmd( - 'jobs.lookup_jid', - [jid, {'__kwarg__': True}]) + cret = runner.cmd("jobs.lookup_jid", [jid, {"__kwarg__": True}]) if target in cret: ret = cret[target] break # recent changes - elif 'data' in cret and 'outputter' in cret: - ret = cret['data'] + elif "data" in cret and "outputter" in cret: + ret = cret["data"] break # special case, some answers may be crafted # to handle the unresponsivness of a specific command # which is also meaningful, e.g. a minion not yet provisioned - if fun in ['test.ping'] and not wait_for_res: - ret = { - 'test.ping': False, - }.get(fun, False) + if fun in ["test.ping"] and not wait_for_res: + ret = {"test.ping": False}.get(fun, False) time.sleep(0.5) try: - if 'is not available.' in ret: + if "is not available." in ret: raise SaltCloudSystemExit( - 'module/function {0} is not available'.format(fun)) + "module/function {0} is not available".format(fun) + ) except SaltCloudSystemExit: # pylint: disable=try-except-raise raise except TypeError: @@ -255,24 +242,23 @@ def _salt(fun, *args, **kw): def avail_images(): - return _salt('lxc.templates') + return _salt("lxc.templates") def list_nodes(conn=None, call=None): hide = False - names = __opts__.get('names', []) - profiles = __opts__.get('profiles', {}) - profile = __opts__.get('profile', - __opts__.get('internal_lxc_profile', [])) - destroy_opt = __opts__.get('destroy', False) - action = __opts__.get('action', '') - for opt in ['full_query', 'select_query', 'query']: + names = __opts__.get("names", []) + profiles = __opts__.get("profiles", {}) + profile = __opts__.get("profile", __opts__.get("internal_lxc_profile", [])) + destroy_opt = __opts__.get("destroy", False) + action = __opts__.get("action", "") + for opt in ["full_query", "select_query", "query"]: if __opts__.get(opt, False): - call = 'full' + call = "full" if destroy_opt: - call = 'full' + call = "full" if action and not call: - call = 'action' + call = "action" if profile and names and not destroy_opt: hide = True if not get_configured_provider(): @@ -280,36 +266,38 @@ def list_nodes(conn=None, call=None): path = None if profile and profile in profiles: - path = profiles[profile].get('path', None) - lxclist = _salt('lxc.list', extra=True, path=path) + path = profiles[profile].get("path", None) + lxclist = _salt("lxc.list", extra=True, path=path) nodes = {} for state, lxcs in six.iteritems(lxclist): for lxcc, linfos in six.iteritems(lxcs): info = { - 'id': lxcc, - 'name': lxcc, # required for cloud cache - 'image': None, - 'size': linfos['size'], - 'state': state.lower(), - 'public_ips': linfos['public_ips'], - 'private_ips': linfos['private_ips'], + "id": lxcc, + "name": lxcc, # required for cloud cache + "image": None, + "size": linfos["size"], + "state": state.lower(), + "public_ips": linfos["public_ips"], + "private_ips": linfos["private_ips"], } # in creation mode, we need to go inside the create method # so we hide the running vm from being seen as already installed # do not also mask half configured nodes which are explicitly asked # to be acted on, on the command line - if (call in ['full'] or not hide) and ((lxcc in names and call in ['action']) or call in ['full']): + if (call in ["full"] or not hide) and ( + (lxcc in names and call in ["action"]) or call in ["full"] + ): nodes[lxcc] = { - 'id': lxcc, - 'name': lxcc, # required for cloud cache - 'image': None, - 'size': linfos['size'], - 'state': state.lower(), - 'public_ips': linfos['public_ips'], - 'private_ips': linfos['private_ips'], + "id": lxcc, + "name": lxcc, # required for cloud cache + "image": None, + "size": linfos["size"], + "state": state.lower(), + "public_ips": linfos["public_ips"], + "private_ips": linfos["private_ips"], } else: - nodes[lxcc] = {'id': lxcc, 'state': state.lower()} + nodes[lxcc] = {"id": lxcc, "state": state.lower()} return nodes @@ -317,162 +305,160 @@ def list_nodes_full(conn=None, call=None): if not get_configured_provider(): return if not call: - call = 'action' + call = "action" return list_nodes(conn=conn, call=call) def show_instance(name, call=None): - ''' + """ Show the details from the provider concerning an instance - ''' + """ if not get_configured_provider(): return if not call: - call = 'action' + call = "action" nodes = list_nodes_full(call=call) - __utils__['cloud.cache_node'](nodes[name], __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](nodes[name], __active_provider_name__, __opts__) return nodes[name] def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ if not call: - call = 'select' + call = "select" if not get_configured_provider(): return - info = ['id', 'name', 'image', 'size', 'state', 'public_ips', 'private_ips'] + info = ["id", "name", "image", "size", "state", "public_ips", "private_ips"] return salt.utils.cloud.list_nodes_select( - list_nodes_full(call='action'), - __opts__.get('query.selection', info), call) + list_nodes_full(call="action"), __opts__.get("query.selection", info), call + ) def _checkpoint(ret): - sret = ''' + sret = """ id: {name} -last message: {comment}'''.format(**ret) - keys = list(ret['changes'].items()) +last message: {comment}""".format( + **ret + ) + keys = list(ret["changes"].items()) keys.sort() for ch, comment in keys: - sret += ( - '\n' - ' {0}:\n' - ' {1}' - ).format(ch, comment.replace( - '\n', - '\n' - ' ')) - if not ret['result']: - if 'changes' in ret: - del ret['changes'] + sret += ("\n" " {0}:\n" " {1}").format( + ch, comment.replace("\n", "\n" " ") + ) + if not ret["result"]: + if "changes" in ret: + del ret["changes"] raise SaltCloudSystemExit(sret) log.info(sret) return sret def destroy(vm_, call=None): - '''Destroy a lxc container''' - destroy_opt = __opts__.get('destroy', False) - profiles = __opts__.get('profiles', {}) - profile = __opts__.get('profile', - __opts__.get('internal_lxc_profile', [])) + """Destroy a lxc container""" + destroy_opt = __opts__.get("destroy", False) + profiles = __opts__.get("profiles", {}) + profile = __opts__.get("profile", __opts__.get("internal_lxc_profile", [])) path = None if profile and profile in profiles: - path = profiles[profile].get('path', None) - action = __opts__.get('action', '') - if action != 'destroy' and not destroy_opt: + path = profiles[profile].get("path", None) + action = __opts__.get("action", "") + if action != "destroy" and not destroy_opt: raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) if not get_configured_provider(): return - ret = {'comment': '{0} was not found'.format(vm_), - 'result': False} - if _salt('lxc.info', vm_, path=path): - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(vm_), - args={'name': vm_, 'instance_id': vm_}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + ret = {"comment": "{0} was not found".format(vm_), "result": False} + if _salt("lxc.info", vm_, path=path): + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(vm_), + args={"name": vm_, "instance_id": vm_}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - cret = _salt('lxc.destroy', vm_, stop=True, path=path) - ret['result'] = cret['result'] - if ret['result']: - ret['comment'] = '{0} was destroyed'.format(vm_) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(vm_), - args={'name': vm_, 'instance_id': vm_}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + cret = _salt("lxc.destroy", vm_, stop=True, path=path) + ret["result"] = cret["result"] + if ret["result"]: + ret["comment"] = "{0} was destroyed".format(vm_) + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(vm_), + args={"name": vm_, "instance_id": vm_}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](vm_, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + vm_, __active_provider_name__.split(":")[0], __opts__ + ) return ret def create(vm_, call=None): - '''Create an lxc Container. + """Create an lxc Container. This function is idempotent and will try to either provision or finish the provision of an lxc container. NOTE: Most of the initialization code has been moved and merged with the lxc runner and lxc.init functions - ''' + """ prov = get_configured_provider(vm_) if not prov: return # we cant use profile as a configuration key as it conflicts # with salt cloud internals - profile = vm_.get( - 'lxc_profile', - vm_.get('container_profile', None)) + profile = vm_.get("lxc_profile", vm_.get("container_profile", None)) event_data = vm_.copy() - event_data['profile'] = profile + event_data["profile"] = profile - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', event_data, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", event_data, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - ret = {'name': vm_['name'], 'changes': {}, 'result': True, 'comment': ''} - if 'pub_key' not in vm_ and 'priv_key' not in vm_: - log.debug('Generating minion keys for %s', vm_['name']) - vm_['priv_key'], vm_['pub_key'] = salt.utils.cloud.gen_keys( - salt.config.get_cloud_config_value( - 'keysize', vm_, __opts__)) + ret = {"name": vm_["name"], "changes": {}, "result": True, "comment": ""} + if "pub_key" not in vm_ and "priv_key" not in vm_: + log.debug("Generating minion keys for %s", vm_["name"]) + vm_["priv_key"], vm_["pub_key"] = salt.utils.cloud.gen_keys( + salt.config.get_cloud_config_value("keysize", vm_, __opts__) + ) # get the minion key pair to distribute back to the container kwarg = copy.deepcopy(vm_) - kwarg['host'] = prov['target'] - kwarg['profile'] = profile + kwarg["host"] = prov["target"] + kwarg["profile"] = profile - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']('requesting', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "requesting", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - cret = _runner().cmd('lxc.cloud_init', [vm_['name']], kwarg=kwarg) - ret['runner_return'] = cret - ret['result'] = cret['result'] - if not ret['result']: - ret['Error'] = 'Error while creating {0},'.format(vm_['name']) + cret = _runner().cmd("lxc.cloud_init", [vm_["name"]], kwarg=kwarg) + ret["runner_return"] = cret + ret["result"] = cret["result"] + if not ret["result"]: + ret["Error"] = "Error while creating {0},".format(vm_["name"]) else: - ret['changes']['created'] = 'created' + ret["changes"]["created"] = "created" # When using cloud states to manage LXC containers # __opts__['profile'] is not implicitly reset between operations @@ -481,17 +467,19 @@ def create(vm_, call=None): # But in cloud state we do want to check at first if it really # exists hence the need to remove profile from global opts once # current container is created. - if 'profile' in __opts__: - __opts__['internal_lxc_profile'] = __opts__['profile'] - del __opts__['profile'] + if "profile" in __opts__: + __opts__["internal_lxc_profile"] = __opts__["profile"] + del __opts__["profile"] - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret @@ -499,72 +487,67 @@ def create(vm_, call=None): def get_provider(name): data = None - if name in __opts__['providers']: - data = __opts__['providers'][name] - if 'lxc' in data: - data = data['lxc'] + if name in __opts__["providers"]: + data = __opts__["providers"][name] + if "lxc" in data: + data = data["lxc"] else: data = None return data def get_configured_provider(vm_=None): - ''' + """ Return the contextual provider of None if no configured one can be found. - ''' + """ if vm_ is None: vm_ = {} - dalias, driver = __active_provider_name__.split(':') + dalias, driver = __active_provider_name__.split(":") data = None - tgt = 'unknown' - img_provider = __opts__.get('list_images', '') - arg_providers = __opts__.get('names', []) + tgt = "unknown" + img_provider = __opts__.get("list_images", "") + arg_providers = __opts__.get("names", []) matched = False # --list-images level if img_provider: - tgt = 'provider: {0}'.format(img_provider) + tgt = "provider: {0}".format(img_provider) if dalias == img_provider: data = get_provider(img_provider) matched = True # providers are set in configuration - if not data and 'profile' not in __opts__ and arg_providers: + if not data and "profile" not in __opts__ and arg_providers: for name in arg_providers: - tgt = 'provider: {0}'.format(name) + tgt = "provider: {0}".format(name) if dalias == name: data = get_provider(name) if data: matched = True break # -p is providen, get the uplinked provider - elif 'profile' in __opts__: - curprof = __opts__['profile'] - profs = __opts__['profiles'] - tgt = 'profile: {0}'.format(curprof) - if ( - curprof in profs and - profs[curprof]['provider'] == __active_provider_name__ - ): - prov, cdriver = profs[curprof]['provider'].split(':') - tgt += ' provider: {0}'.format(prov) + elif "profile" in __opts__: + curprof = __opts__["profile"] + profs = __opts__["profiles"] + tgt = "profile: {0}".format(curprof) + if curprof in profs and profs[curprof]["provider"] == __active_provider_name__: + prov, cdriver = profs[curprof]["provider"].split(":") + tgt += " provider: {0}".format(prov) data = get_provider(prov) matched = True # fallback if we have only __active_provider_name__ - if ( - (__opts__.get('destroy', False) and not data) or ( - not matched and __active_provider_name__ - ) + if (__opts__.get("destroy", False) and not data) or ( + not matched and __active_provider_name__ ): - data = __opts__.get('providers', - {}).get(dalias, {}).get(driver, {}) + data = __opts__.get("providers", {}).get(dalias, {}).get(driver, {}) # in all cases, verify that the linked saltmaster is alive. if data: - ret = _salt('test.ping', salt_target=data['target']) + ret = _salt("test.ping", salt_target=data["target"]) if ret: return data else: log.error( - 'Configured provider %s minion: %s is unreachable', - __active_provider_name__, data['target'] + "Configured provider %s minion: %s is unreachable", + __active_provider_name__, + data["target"], ) return False diff --git a/salt/cloud/clouds/msazure.py b/salt/cloud/clouds/msazure.py index 5d391468aff..ba2b8bb93f4 100644 --- a/salt/cloud/clouds/msazure.py +++ b/salt/cloud/clouds/msazure.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure Cloud Module ================== @@ -35,11 +35,12 @@ Example ``/etc/salt/cloud.providers`` or subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617 certificate_path: /etc/salt/azure.pem management_host: management.core.windows.net -''' +""" # pylint: disable=E0102 # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import pprint @@ -47,28 +48,32 @@ import time # Import salt libs import salt.config as config -from salt.exceptions import SaltCloudSystemExit import salt.utils.cloud import salt.utils.stringutils import salt.utils.yaml +from salt.exceptions import SaltCloudSystemExit # Import 3rd-party libs from salt.ext import six + HAS_LIBS = False try: import azure import azure.storage import azure.servicemanagement - from azure.common import (AzureConflictHttpError, - AzureMissingResourceHttpError, - AzureException) + from azure.common import ( + AzureConflictHttpError, + AzureMissingResourceHttpError, + AzureException, + ) import salt.utils.msazure from salt.utils.msazure import object_to_dict + HAS_LIBS = True except ImportError: pass -__virtualname__ = 'azure' +__virtualname__ = "azure" # Get logging started @@ -77,9 +82,9 @@ log = logging.getLogger(__name__) # Only load in this module if the AZURE configurations are in place def __virtual__(): - ''' + """ Check for Azure configurations. - ''' + """ if get_configured_provider() is False: return False @@ -90,46 +95,41 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('subscription_id', 'certificate_path') + ("subscription_id", "certificate_path"), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'azure': HAS_LIBS} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"azure": HAS_LIBS}) def get_conn(): - ''' + """ Return a conn object for the passed VM data - ''' + """ certificate_path = config.get_cloud_config_value( - 'certificate_path', - get_configured_provider(), __opts__, search_global=False + "certificate_path", get_configured_provider(), __opts__, search_global=False ) subscription_id = salt.utils.stringutils.to_str( config.get_cloud_config_value( - 'subscription_id', - get_configured_provider(), __opts__, search_global=False + "subscription_id", get_configured_provider(), __opts__, search_global=False ) ) management_host = config.get_cloud_config_value( - 'management_host', + "management_host", get_configured_provider(), __opts__, search_global=False, - default='management.core.windows.net' + default="management.core.windows.net", ) return azure.servicemanagement.ServiceManagementService( subscription_id, certificate_path, management_host @@ -137,27 +137,27 @@ def get_conn(): def script(vm_): - ''' + """ Return the script deployment object - ''' + """ return salt.utils.cloud.os_script( - config.get_cloud_config_value('script', vm_, __opts__), + config.get_cloud_config_value("script", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) def avail_locations(conn=None, call=None): - ''' + """ List available locations for Azure - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) if not conn: @@ -167,21 +167,21 @@ def avail_locations(conn=None, call=None): locations = conn.list_locations() for location in locations: ret[location.name] = { - 'name': location.name, - 'display_name': location.display_name, - 'available_services': location.available_services, + "name": location.name, + "display_name": location.display_name, + "available_services": location.available_services, } return ret def avail_images(conn=None, call=None): - ''' + """ List available images for Azure - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) if not conn: @@ -196,13 +196,13 @@ def avail_images(conn=None, call=None): def avail_sizes(call=None): - ''' + """ Return a list of sizes from Azure - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) conn = get_conn() @@ -214,30 +214,30 @@ def avail_sizes(call=None): def list_nodes(conn=None, call=None): - ''' + """ List VMs on this Azure account - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} nodes = list_nodes_full(conn, call) for node in nodes: - ret[node] = {'name': node} - for prop in ('id', 'image', 'size', 'state', 'private_ips', 'public_ips'): + ret[node] = {"name": node} + for prop in ("id", "image", "size", "state", "private_ips", "public_ips"): ret[node][prop] = nodes[node].get(prop) return ret def list_nodes_full(conn=None, call=None): - ''' + """ List VMs on this Azure account, with full information - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) if not conn: @@ -246,47 +246,48 @@ def list_nodes_full(conn=None, call=None): ret = {} services = list_hosted_services(conn=conn, call=call) for service in services: - for deployment in services[service]['deployments']: - deploy_dict = services[service]['deployments'][deployment] + for deployment in services[service]["deployments"]: + deploy_dict = services[service]["deployments"][deployment] deploy_dict_no_role_info = copy.deepcopy(deploy_dict) - del deploy_dict_no_role_info['role_list'] - del deploy_dict_no_role_info['role_instance_list'] - roles = deploy_dict['role_list'] + del deploy_dict_no_role_info["role_list"] + del deploy_dict_no_role_info["role_instance_list"] + roles = deploy_dict["role_list"] for role in roles: - role_instances = deploy_dict['role_instance_list'] + role_instances = deploy_dict["role_instance_list"] ret[role] = roles[role] ret[role].update(role_instances[role]) - ret[role]['id'] = role - ret[role]['hosted_service'] = service - if role_instances[role]['power_state'] == 'Started': - ret[role]['state'] = 'running' - elif role_instances[role]['power_state'] == 'Stopped': - ret[role]['state'] = 'stopped' + ret[role]["id"] = role + ret[role]["hosted_service"] = service + if role_instances[role]["power_state"] == "Started": + ret[role]["state"] = "running" + elif role_instances[role]["power_state"] == "Stopped": + ret[role]["state"] = "stopped" else: - ret[role]['state'] = 'pending' - ret[role]['private_ips'] = [] - ret[role]['public_ips'] = [] - ret[role]['deployment'] = deploy_dict_no_role_info - ret[role]['url'] = deploy_dict['url'] - ip_address = role_instances[role]['ip_address'] + ret[role]["state"] = "pending" + ret[role]["private_ips"] = [] + ret[role]["public_ips"] = [] + ret[role]["deployment"] = deploy_dict_no_role_info + ret[role]["url"] = deploy_dict["url"] + ip_address = role_instances[role]["ip_address"] if ip_address: if salt.utils.cloud.is_public_ip(ip_address): - ret[role]['public_ips'].append(ip_address) + ret[role]["public_ips"].append(ip_address) else: - ret[role]['private_ips'].append(ip_address) - ret[role]['size'] = role_instances[role]['instance_size'] - ret[role]['image'] = roles[role]['role_info']['os_virtual_hard_disk']['source_image_name'] + ret[role]["private_ips"].append(ip_address) + ret[role]["size"] = role_instances[role]["instance_size"] + ret[role]["image"] = roles[role]["role_info"]["os_virtual_hard_disk"][ + "source_image_name" + ] return ret def list_hosted_services(conn=None, call=None): - ''' + """ List VMs on this Azure account, with full information - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_hosted_services function must be called with ' - '-f or --function' + "The list_hosted_services function must be called with " "-f or --function" ) if not conn: @@ -297,201 +298,211 @@ def list_hosted_services(conn=None, call=None): for service in services: props = service.hosted_service_properties ret[service.service_name] = { - 'name': service.service_name, - 'url': service.url, - 'affinity_group': props.affinity_group, - 'date_created': props.date_created, - 'date_last_modified': props.date_last_modified, - 'description': props.description, - 'extended_properties': props.extended_properties, - 'label': props.label, - 'location': props.location, - 'status': props.status, - 'deployments': {}, + "name": service.service_name, + "url": service.url, + "affinity_group": props.affinity_group, + "date_created": props.date_created, + "date_last_modified": props.date_last_modified, + "description": props.description, + "extended_properties": props.extended_properties, + "label": props.label, + "location": props.location, + "status": props.status, + "deployments": {}, } deployments = conn.get_hosted_service_properties( service_name=service.service_name, embed_detail=True ) for deployment in deployments.deployments: - ret[service.service_name]['deployments'][deployment.name] = { - 'configuration': deployment.configuration, - 'created_time': deployment.created_time, - 'deployment_slot': deployment.deployment_slot, - 'extended_properties': deployment.extended_properties, - 'input_endpoint_list': deployment.input_endpoint_list, - 'label': deployment.label, - 'last_modified_time': deployment.last_modified_time, - 'locked': deployment.locked, - 'name': deployment.name, - 'persistent_vm_downtime_info': deployment.persistent_vm_downtime_info, - 'private_id': deployment.private_id, - 'role_instance_list': {}, - 'role_list': {}, - 'rollback_allowed': deployment.rollback_allowed, - 'sdk_version': deployment.sdk_version, - 'status': deployment.status, - 'upgrade_domain_count': deployment.upgrade_domain_count, - 'upgrade_status': deployment.upgrade_status, - 'url': deployment.url, + ret[service.service_name]["deployments"][deployment.name] = { + "configuration": deployment.configuration, + "created_time": deployment.created_time, + "deployment_slot": deployment.deployment_slot, + "extended_properties": deployment.extended_properties, + "input_endpoint_list": deployment.input_endpoint_list, + "label": deployment.label, + "last_modified_time": deployment.last_modified_time, + "locked": deployment.locked, + "name": deployment.name, + "persistent_vm_downtime_info": deployment.persistent_vm_downtime_info, + "private_id": deployment.private_id, + "role_instance_list": {}, + "role_list": {}, + "rollback_allowed": deployment.rollback_allowed, + "sdk_version": deployment.sdk_version, + "status": deployment.status, + "upgrade_domain_count": deployment.upgrade_domain_count, + "upgrade_status": deployment.upgrade_status, + "url": deployment.url, } for role_instance in deployment.role_instance_list: - ret[service.service_name]['deployments'][deployment.name]['role_instance_list'][role_instance.role_name] = { - 'fqdn': role_instance.fqdn, - 'instance_error_code': role_instance.instance_error_code, - 'instance_fault_domain': role_instance.instance_fault_domain, - 'instance_name': role_instance.instance_name, - 'instance_size': role_instance.instance_size, - 'instance_state_details': role_instance.instance_state_details, - 'instance_status': role_instance.instance_status, - 'instance_upgrade_domain': role_instance.instance_upgrade_domain, - 'ip_address': role_instance.ip_address, - 'power_state': role_instance.power_state, - 'role_name': role_instance.role_name, + ret[service.service_name]["deployments"][deployment.name][ + "role_instance_list" + ][role_instance.role_name] = { + "fqdn": role_instance.fqdn, + "instance_error_code": role_instance.instance_error_code, + "instance_fault_domain": role_instance.instance_fault_domain, + "instance_name": role_instance.instance_name, + "instance_size": role_instance.instance_size, + "instance_state_details": role_instance.instance_state_details, + "instance_status": role_instance.instance_status, + "instance_upgrade_domain": role_instance.instance_upgrade_domain, + "ip_address": role_instance.ip_address, + "power_state": role_instance.power_state, + "role_name": role_instance.role_name, } for role in deployment.role_list: - ret[service.service_name]['deployments'][deployment.name]['role_list'][role.role_name] = { - 'role_name': role.role_name, - 'os_version': role.os_version, + ret[service.service_name]["deployments"][deployment.name]["role_list"][ + role.role_name + ] = { + "role_name": role.role_name, + "os_version": role.os_version, } role_info = conn.get_role( service_name=service.service_name, deployment_name=deployment.name, role_name=role.role_name, ) - ret[service.service_name]['deployments'][deployment.name]['role_list'][role.role_name]['role_info'] = { - 'availability_set_name': role_info.availability_set_name, - 'configuration_sets': role_info.configuration_sets, - 'data_virtual_hard_disks': role_info.data_virtual_hard_disks, - 'os_version': role_info.os_version, - 'role_name': role_info.role_name, - 'role_size': role_info.role_size, - 'role_type': role_info.role_type, + ret[service.service_name]["deployments"][deployment.name]["role_list"][ + role.role_name + ]["role_info"] = { + "availability_set_name": role_info.availability_set_name, + "configuration_sets": role_info.configuration_sets, + "data_virtual_hard_disks": role_info.data_virtual_hard_disks, + "os_version": role_info.os_version, + "role_name": role_info.role_name, + "role_size": role_info.role_size, + "role_type": role_info.role_type, } - ret[service.service_name]['deployments'][deployment.name]['role_list'][role.role_name]['role_info']['os_virtual_hard_disk'] = { - 'disk_label': role_info.os_virtual_hard_disk.disk_label, - 'disk_name': role_info.os_virtual_hard_disk.disk_name, - 'host_caching': role_info.os_virtual_hard_disk.host_caching, - 'media_link': role_info.os_virtual_hard_disk.media_link, - 'os': role_info.os_virtual_hard_disk.os, - 'source_image_name': role_info.os_virtual_hard_disk.source_image_name, + ret[service.service_name]["deployments"][deployment.name]["role_list"][ + role.role_name + ]["role_info"]["os_virtual_hard_disk"] = { + "disk_label": role_info.os_virtual_hard_disk.disk_label, + "disk_name": role_info.os_virtual_hard_disk.disk_name, + "host_caching": role_info.os_virtual_hard_disk.host_caching, + "media_link": role_info.os_virtual_hard_disk.media_link, + "os": role_info.os_virtual_hard_disk.os, + "source_image_name": role_info.os_virtual_hard_disk.source_image_name, } return ret def list_nodes_select(conn=None, call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ if not conn: conn = get_conn() return salt.utils.cloud.list_nodes_select( - list_nodes_full(conn, 'function'), __opts__['query.selection'], call, + list_nodes_full(conn, "function"), __opts__["query.selection"], call, ) def show_instance(name, call=None): - ''' + """ Show the details from the provider concerning an instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) nodes = list_nodes_full() # Find under which cloud service the name is listed, if any if name not in nodes: return {} - if 'name' not in nodes[name]: - nodes[name]['name'] = nodes[name]['id'] + if "name" not in nodes[name]: + nodes[name]["name"] = nodes[name]["id"] try: - __utils__['cloud.cache_node'](nodes[name], __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](nodes[name], __active_provider_name__, __opts__) except TypeError: - log.warning('Unable to show cache node data; this may be because the node has been deleted') + log.warning( + "Unable to show cache node data; this may be because the node has been deleted" + ) return nodes[name] def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'azure', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "azure", vm_["profile"], vm_=vm_ + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) conn = get_conn() - label = vm_.get('label', vm_['name']) - service_name = vm_.get('service_name', vm_['name']) + label = vm_.get("label", vm_["name"]) + service_name = vm_.get("service_name", vm_["name"]) service_kwargs = { - 'service_name': service_name, - 'label': label, - 'description': vm_.get('desc', vm_['name']), + "service_name": service_name, + "label": label, + "description": vm_.get("desc", vm_["name"]), } loc_error = False - if 'location' in vm_: - if 'affinity_group' in vm_: + if "location" in vm_: + if "affinity_group" in vm_: loc_error = True else: - service_kwargs['location'] = vm_['location'] - elif 'affinity_group' in vm_: - service_kwargs['affinity_group'] = vm_['affinity_group'] + service_kwargs["location"] = vm_["location"] + elif "affinity_group" in vm_: + service_kwargs["affinity_group"] = vm_["affinity_group"] else: loc_error = True if loc_error: raise SaltCloudSystemExit( - 'Either a location or affinity group must be specified, but not both' + "Either a location or affinity group must be specified, but not both" ) - ssh_port = config.get_cloud_config_value('port', vm_, __opts__, - default=22, search_global=True) + ssh_port = config.get_cloud_config_value( + "port", vm_, __opts__, default=22, search_global=True + ) ssh_endpoint = azure.servicemanagement.ConfigurationSetInputEndpoint( - name='SSH', - protocol='TCP', - port=ssh_port, - local_port=22, + name="SSH", protocol="TCP", port=ssh_port, local_port=22, ) network_config = azure.servicemanagement.ConfigurationSet() network_config.input_endpoints.input_endpoints.append(ssh_endpoint) - network_config.configuration_set_type = 'NetworkConfiguration' + network_config.configuration_set_type = "NetworkConfiguration" - if 'win_username' in vm_: + if "win_username" in vm_: system_config = azure.servicemanagement.WindowsConfigurationSet( - computer_name=vm_['name'], - admin_username=vm_['win_username'], - admin_password=vm_['win_password'], + computer_name=vm_["name"], + admin_username=vm_["win_username"], + admin_password=vm_["win_password"], ) - smb_port = '445' - if 'smb_port' in vm_: - smb_port = vm_['smb_port'] + smb_port = "445" + if "smb_port" in vm_: + smb_port = vm_["smb_port"] smb_endpoint = azure.servicemanagement.ConfigurationSetInputEndpoint( - name='SMB', - protocol='TCP', - port=smb_port, - local_port=smb_port, + name="SMB", protocol="TCP", port=smb_port, local_port=smb_port, ) network_config.input_endpoints.input_endpoints.append(smb_endpoint) @@ -502,128 +513,134 @@ def create(vm_): else: system_config = azure.servicemanagement.LinuxConfigurationSet( - host_name=vm_['name'], - user_name=vm_['ssh_username'], - user_password=vm_['ssh_password'], + host_name=vm_["name"], + user_name=vm_["ssh_username"], + user_password=vm_["ssh_password"], disable_ssh_password_authentication=False, ) # TODO: Might need to create a storage account - media_link = vm_['media_link'] + media_link = vm_["media_link"] # TODO: Probably better to use more than just the name in the media_link - media_link += '/{0}.vhd'.format(vm_['name']) - os_hd = azure.servicemanagement.OSVirtualHardDisk(vm_['image'], media_link) + media_link += "/{0}.vhd".format(vm_["name"]) + os_hd = azure.servicemanagement.OSVirtualHardDisk(vm_["image"], media_link) vm_kwargs = { - 'service_name': service_name, - 'deployment_name': service_name, - 'deployment_slot': vm_['slot'], - 'label': label, - 'role_name': vm_['name'], - 'system_config': system_config, - 'os_virtual_hard_disk': os_hd, - 'role_size': vm_['size'], - 'network_config': network_config, + "service_name": service_name, + "deployment_name": service_name, + "deployment_slot": vm_["slot"], + "label": label, + "role_name": vm_["name"], + "system_config": system_config, + "os_virtual_hard_disk": os_hd, + "role_size": vm_["size"], + "network_config": network_config, } - if 'virtual_network_name' in vm_: - vm_kwargs['virtual_network_name'] = vm_['virtual_network_name'] - if 'subnet_name' in vm_: - network_config.subnet_names.append(vm_['subnet_name']) + if "virtual_network_name" in vm_: + vm_kwargs["virtual_network_name"] = vm_["virtual_network_name"] + if "subnet_name" in vm_: + network_config.subnet_names.append(vm_["subnet_name"]) - log.debug('vm_kwargs: %s', vm_kwargs) + log.debug("vm_kwargs: %s", vm_kwargs) - event_kwargs = {'service_kwargs': service_kwargs.copy(), - 'vm_kwargs': vm_kwargs.copy()} - del event_kwargs['vm_kwargs']['system_config'] - del event_kwargs['vm_kwargs']['os_virtual_hard_disk'] - del event_kwargs['vm_kwargs']['network_config'] - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']('requesting', event_kwargs, list(event_kwargs)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + event_kwargs = { + "service_kwargs": service_kwargs.copy(), + "vm_kwargs": vm_kwargs.copy(), + } + del event_kwargs["vm_kwargs"]["system_config"] + del event_kwargs["vm_kwargs"]["os_virtual_hard_disk"] + del event_kwargs["vm_kwargs"]["network_config"] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "requesting", event_kwargs, list(event_kwargs) + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.debug('vm_kwargs: %s', vm_kwargs) + log.debug("vm_kwargs: %s", vm_kwargs) # Azure lets you open winrm on a new VM # Can open up specific ports in Azure; but not on Windows try: conn.create_hosted_service(**service_kwargs) except AzureConflictHttpError: - log.debug('Cloud service already exists') + log.debug("Cloud service already exists") except Exception as exc: # pylint: disable=broad-except - error = 'The hosted service name is invalid.' + error = "The hosted service name is invalid." if error in six.text_type(exc): log.error( - 'Error creating %s on Azure.\n\n' - 'The hosted service name is invalid. The name can contain ' - 'only letters, numbers, and hyphens. The name must start with ' - 'a letter and must end with a letter or a number.', - vm_['name'], + "Error creating %s on Azure.\n\n" + "The hosted service name is invalid. The name can contain " + "only letters, numbers, and hyphens. The name must start with " + "a letter and must end with a letter or a number.", + vm_["name"], # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) else: log.error( - 'Error creating %s on Azure\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: \n%s', - vm_['name'], exc, + "Error creating %s on Azure\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: \n%s", + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False try: result = conn.create_virtual_machine_deployment(**vm_kwargs) - log.debug('Request ID for machine: %s', result.request_id) + log.debug("Request ID for machine: %s", result.request_id) _wait_for_async(conn, result.request_id) except AzureConflictHttpError: - log.debug('Conflict error. The deployment may already exist, trying add_role') + log.debug("Conflict error. The deployment may already exist, trying add_role") # Deleting two useless keywords - del vm_kwargs['deployment_slot'] - del vm_kwargs['label'] - del vm_kwargs['virtual_network_name'] + del vm_kwargs["deployment_slot"] + del vm_kwargs["label"] + del vm_kwargs["virtual_network_name"] result = conn.add_role(**vm_kwargs) _wait_for_async(conn, result.request_id) except Exception as exc: # pylint: disable=broad-except - error = 'The hosted service name is invalid.' + error = "The hosted service name is invalid." if error in six.text_type(exc): log.error( - 'Error creating %s on Azure.\n\n' - 'The VM name is invalid. The name can contain ' - 'only letters, numbers, and hyphens. The name must start with ' - 'a letter and must end with a letter or a number.', - vm_['name'], + "Error creating %s on Azure.\n\n" + "The VM name is invalid. The name can contain " + "only letters, numbers, and hyphens. The name must start with " + "a letter and must end with a letter or a number.", + vm_["name"], # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) else: log.error( - 'Error creating %s on Azure.\n\n' - 'The Virtual Machine could not be created. If you ' - 'are using an already existing Cloud Service, ' - 'make sure you set up the `port` variable corresponding ' - 'to the SSH port exists and that the port number is not ' - 'already in use.\nThe following exception was thrown when trying to ' - 'run the initial deployment: \n%s', - vm_['name'], exc, + "Error creating %s on Azure.\n\n" + "The Virtual Machine could not be created. If you " + "are using an already existing Cloud Service, " + "make sure you set up the `port` variable corresponding " + "to the SSH port exists and that the port number is not " + "already in use.\nThe following exception was thrown when trying to " + "run the initial deployment: \n%s", + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False def wait_for_hostname(): - ''' + """ Wait for the IP address to become available - ''' + """ try: - conn.get_role(service_name, service_name, vm_['name']) - data = show_instance(vm_['name'], call='action') - if 'url' in data and data['url'] != six.text_type(''): - return data['url'] + conn.get_role(service_name, service_name, vm_["name"]) + data = show_instance(vm_["name"], call="action") + if "url" in data and data["url"] != six.text_type(""): + return data["url"] except AzureMissingResourceHttpError: pass time.sleep(1) @@ -632,83 +649,85 @@ def create(vm_): hostname = salt.utils.cloud.wait_for_fun( wait_for_hostname, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) if not hostname: - log.error('Failed to get a value for the hostname.') + log.error("Failed to get a value for the hostname.") return False - vm_['ssh_host'] = hostname.replace('http://', '').replace('/', '') - vm_['password'] = config.get_cloud_config_value( - 'ssh_password', vm_, __opts__ - ) - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + vm_["ssh_host"] = hostname.replace("http://", "").replace("/", "") + vm_["password"] = config.get_cloud_config_value("ssh_password", vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) # Attaching volumes volumes = config.get_cloud_config_value( - 'volumes', vm_, __opts__, search_global=True + "volumes", vm_, __opts__, search_global=True ) if volumes: - __utils__['cloud.fire_event']( - 'event', - 'attaching volumes', - 'salt/cloud/{0}/attaching_volumes'.format(vm_['name']), - args=__utils__['cloud.filter_event']('attaching_volumes', vm_, ['volumes']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "attaching volumes", + "salt/cloud/{0}/attaching_volumes".format(vm_["name"]), + args=__utils__["cloud.filter_event"]("attaching_volumes", vm_, ["volumes"]), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Create and attach volumes to node %s', vm_['name']) + log.info("Create and attach volumes to node %s", vm_["name"]) created = create_attach_volumes( - vm_['name'], + vm_["name"], { - 'volumes': volumes, - 'service_name': service_name, - 'deployment_name': vm_['name'], - 'media_link': media_link, - 'role_name': vm_['name'], - 'del_all_vols_on_destroy': vm_.get('set_del_all_vols_on_destroy', False) + "volumes": volumes, + "service_name": service_name, + "deployment_name": vm_["name"], + "media_link": media_link, + "role_name": vm_["name"], + "del_all_vols_on_destroy": vm_.get( + "set_del_all_vols_on_destroy", False + ), }, - call='action' + call="action", ) - ret['Attached Volumes'] = created + ret["Attached Volumes"] = created - data = show_instance(vm_['name'], call='action') - log.info('Created Cloud VM \'%s\'', vm_) - log.debug('\'%s\' VM creation details:\n%s', vm_['name'], pprint.pformat(data)) + data = show_instance(vm_["name"], call="action") + log.info("Created Cloud VM '%s'", vm_) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) ret.update(data) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def create_attach_volumes(name, kwargs, call=None, wait_to_finish=True): - ''' + """ Create and attach volumes to created node - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The create_attach_volumes action must be called with ' - '-a or --action.' + "The create_attach_volumes action must be called with " "-a or --action." ) if kwargs is None: kwargs = {} - if isinstance(kwargs['volumes'], six.string_types): - volumes = salt.utils.yaml.safe_load(kwargs['volumes']) + if isinstance(kwargs["volumes"], six.string_types): + volumes = salt.utils.yaml.safe_load(kwargs["volumes"]) else: - volumes = kwargs['volumes'] + volumes = kwargs["volumes"] # From the Azure .NET SDK doc # @@ -749,60 +768,64 @@ def create_attach_volumes(name, kwargs, call=None, wait_to_finish=True): volume.setdefault("host_caching", "ReadOnly") volume.setdefault("lun", 0) # The media link is vm_name-disk-[0-15].vhd - volume.setdefault("media_link", - kwargs["media_link"][:-4] + "-disk-{0}.vhd".format(volume["lun"])) - volume.setdefault("disk_label", - kwargs["role_name"] + "-disk-{0}".format(volume["lun"])) - volume_dict = { - 'volume_name': volume["lun"], - 'disk_label': volume["disk_label"] - } + volume.setdefault( + "media_link", + kwargs["media_link"][:-4] + "-disk-{0}.vhd".format(volume["lun"]), + ) + volume.setdefault( + "disk_label", kwargs["role_name"] + "-disk-{0}".format(volume["lun"]) + ) + volume_dict = {"volume_name": volume["lun"], "disk_label": volume["disk_label"]} # Preparing the volume dict to be passed with ** - kwargs_add_data_disk = ["lun", "host_caching", "media_link", - "disk_label", "disk_name", - "logical_disk_size_in_gb", - "source_media_link"] + kwargs_add_data_disk = [ + "lun", + "host_caching", + "media_link", + "disk_label", + "disk_name", + "logical_disk_size_in_gb", + "source_media_link", + ] for key in set(volume.keys()) - set(kwargs_add_data_disk): del volume[key] - attach = conn.add_data_disk(kwargs["service_name"], kwargs["deployment_name"], kwargs["role_name"], - **volume) + attach = conn.add_data_disk( + kwargs["service_name"], + kwargs["deployment_name"], + kwargs["role_name"], + **volume + ) log.debug(attach) # If attach is None then everything is fine if attach: - msg = ( - '{0} attached to {1} (aka {2})'.format( - volume_dict['volume_name'], - kwargs['role_name'], - name, - ) + msg = "{0} attached to {1} (aka {2})".format( + volume_dict["volume_name"], kwargs["role_name"], name, ) log.info(msg) ret.append(msg) else: - log.error('Error attaching %s on Azure', volume_dict) + log.error("Error attaching %s on Azure", volume_dict) return ret def create_attach_volumes(name, kwargs, call=None, wait_to_finish=True): - ''' + """ Create and attach volumes to created node - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The create_attach_volumes action must be called with ' - '-a or --action.' + "The create_attach_volumes action must be called with " "-a or --action." ) if kwargs is None: kwargs = {} - if isinstance(kwargs['volumes'], six.string_types): - volumes = salt.utils.yaml.safe_load(kwargs['volumes']) + if isinstance(kwargs["volumes"], six.string_types): + volumes = salt.utils.yaml.safe_load(kwargs["volumes"]) else: - volumes = kwargs['volumes'] + volumes = kwargs["volumes"] # From the Azure .NET SDK doc # @@ -843,35 +866,39 @@ def create_attach_volumes(name, kwargs, call=None, wait_to_finish=True): volume.setdefault("host_caching", "ReadOnly") volume.setdefault("lun", 0) # The media link is vm_name-disk-[0-15].vhd - volume.setdefault("media_link", - kwargs["media_link"][:-4] + "-disk-{0}.vhd".format(volume["lun"])) - volume.setdefault("disk_label", - kwargs["role_name"] + "-disk-{0}".format(volume["lun"])) - volume_dict = { - 'volume_name': volume["lun"], - 'disk_label': volume["disk_label"] - } + volume.setdefault( + "media_link", + kwargs["media_link"][:-4] + "-disk-{0}.vhd".format(volume["lun"]), + ) + volume.setdefault( + "disk_label", kwargs["role_name"] + "-disk-{0}".format(volume["lun"]) + ) + volume_dict = {"volume_name": volume["lun"], "disk_label": volume["disk_label"]} # Preparing the volume dict to be passed with ** - kwargs_add_data_disk = ["lun", "host_caching", "media_link", - "disk_label", "disk_name", - "logical_disk_size_in_gb", - "source_media_link"] + kwargs_add_data_disk = [ + "lun", + "host_caching", + "media_link", + "disk_label", + "disk_name", + "logical_disk_size_in_gb", + "source_media_link", + ] for key in set(volume.keys()) - set(kwargs_add_data_disk): del volume[key] - result = conn.add_data_disk(kwargs["service_name"], - kwargs["deployment_name"], - kwargs["role_name"], - **volume) + result = conn.add_data_disk( + kwargs["service_name"], + kwargs["deployment_name"], + kwargs["role_name"], + **volume + ) _wait_for_async(conn, result.request_id) - msg = ( - '{0} attached to {1} (aka {2})'.format( - volume_dict['volume_name'], - kwargs['role_name'], - name) - ) + msg = "{0} attached to {1} (aka {2})".format( + volume_dict["volume_name"], kwargs["role_name"], name + ) log.info(msg) ret.append(msg) return ret @@ -879,27 +906,31 @@ def create_attach_volumes(name, kwargs, call=None, wait_to_finish=True): # Helper function for azure tests def _wait_for_async(conn, request_id): - ''' + """ Helper function for azure tests - ''' + """ count = 0 - log.debug('Waiting for asynchronous operation to complete') + log.debug("Waiting for asynchronous operation to complete") result = conn.get_operation_status(request_id) - while result.status == 'InProgress': + while result.status == "InProgress": count = count + 1 if count > 120: - raise ValueError('Timed out waiting for asynchronous operation to complete.') + raise ValueError( + "Timed out waiting for asynchronous operation to complete." + ) time.sleep(5) result = conn.get_operation_status(request_id) - if result.status != 'Succeeded': - raise AzureException('Operation failed. {message} ({code})' - .format(message=result.error.message, - code=result.error.code)) + if result.status != "Succeeded": + raise AzureException( + "Operation failed. {message} ({code})".format( + message=result.error.message, code=result.error.code + ) + ) def destroy(name, conn=None, call=None, kwargs=None): - ''' + """ Destroy a VM CLI Examples: @@ -908,11 +939,10 @@ def destroy(name, conn=None, call=None, kwargs=None): salt-cloud -d myminion salt-cloud -a destroy myminion service_name=myservice - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) if not conn: @@ -921,109 +951,129 @@ def destroy(name, conn=None, call=None, kwargs=None): if kwargs is None: kwargs = {} - instance_data = show_instance(name, call='action') - service_name = instance_data['deployment']['name'] - disk_name = instance_data['role_info']['os_virtual_hard_disk']['disk_name'] + instance_data = show_instance(name, call="action") + service_name = instance_data["deployment"]["name"] + disk_name = instance_data["role_info"]["os_virtual_hard_disk"]["disk_name"] ret = {} # TODO: Add the ability to delete or not delete a hosted service when # deleting a VM try: - log.debug('Deleting role') + log.debug("Deleting role") result = conn.delete_role(service_name, service_name, name) - delete_type = 'delete_role' + delete_type = "delete_role" except AzureException: - log.debug('Failed to delete role, deleting deployment') + log.debug("Failed to delete role, deleting deployment") try: result = conn.delete_deployment(service_name, service_name) except AzureConflictHttpError as exc: log.error(exc.message) - raise SaltCloudSystemExit('{0}: {1}'.format(name, exc.message)) - delete_type = 'delete_deployment' + raise SaltCloudSystemExit("{0}: {1}".format(name, exc.message)) + delete_type = "delete_deployment" _wait_for_async(conn, result.request_id) ret[name] = { - delete_type: {'request_id': result.request_id}, + delete_type: {"request_id": result.request_id}, } - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) cleanup_disks = config.get_cloud_config_value( - 'cleanup_disks', - get_configured_provider(), __opts__, search_global=False, default=False, + "cleanup_disks", + get_configured_provider(), + __opts__, + search_global=False, + default=False, ) if cleanup_disks: - cleanup_vhds = kwargs.get('delete_vhd', config.get_cloud_config_value( - 'cleanup_vhds', - get_configured_provider(), __opts__, search_global=False, default=False, - )) - log.debug('Deleting disk %s', disk_name) + cleanup_vhds = kwargs.get( + "delete_vhd", + config.get_cloud_config_value( + "cleanup_vhds", + get_configured_provider(), + __opts__, + search_global=False, + default=False, + ), + ) + log.debug("Deleting disk %s", disk_name) if cleanup_vhds: - log.debug('Deleting vhd') + log.debug("Deleting vhd") def wait_for_destroy(): - ''' + """ Wait for the VM to be deleted - ''' + """ try: - data = delete_disk(kwargs={'name': disk_name, 'delete_vhd': cleanup_vhds}, call='function') + data = delete_disk( + kwargs={"name": disk_name, "delete_vhd": cleanup_vhds}, + call="function", + ) return data except AzureConflictHttpError: - log.debug('Waiting for VM to be destroyed...') + log.debug("Waiting for VM to be destroyed...") time.sleep(5) return False data = salt.utils.cloud.wait_for_fun( wait_for_destroy, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', {}, __opts__, default=15 * 60), + "wait_for_fun_timeout", {}, __opts__, default=15 * 60 + ), ) - ret[name]['delete_disk'] = { - 'name': disk_name, - 'delete_vhd': cleanup_vhds, - 'data': data + ret[name]["delete_disk"] = { + "name": disk_name, + "delete_vhd": cleanup_vhds, + "data": data, } # Services can't be cleaned up unless disks are too cleanup_services = config.get_cloud_config_value( - 'cleanup_services', - get_configured_provider(), __opts__, search_global=False, default=False + "cleanup_services", + get_configured_provider(), + __opts__, + search_global=False, + default=False, ) if cleanup_services: - log.debug('Deleting service %s', service_name) + log.debug("Deleting service %s", service_name) def wait_for_disk_delete(): - ''' + """ Wait for the disk to be deleted - ''' + """ try: - data = delete_service(kwargs={'name': service_name}, call='function') + data = delete_service( + kwargs={"name": service_name}, call="function" + ) return data except AzureConflictHttpError: - log.debug('Waiting for disk to be deleted...') + log.debug("Waiting for disk to be deleted...") time.sleep(5) return False data = salt.utils.cloud.wait_for_fun( wait_for_disk_delete, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', {}, __opts__, default=15 * 60), + "wait_for_fun_timeout", {}, __opts__, default=15 * 60 + ), ) - ret[name]['delete_services'] = { - 'name': service_name, - 'data': data - } + ret[name]["delete_services"] = {"name": service_name, "data": data} return ret def list_storage_services(conn=None, call=None): - ''' + """ List VMs on this Azure account, with full information - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - ('The list_storage_services function must be called ' - 'with -f or --function.') + ( + "The list_storage_services function must be called " + "with -f or --function." + ) ) if not conn: @@ -1033,18 +1083,18 @@ def list_storage_services(conn=None, call=None): accounts = conn.list_storage_accounts() for service in accounts.storage_services: ret[service.service_name] = { - 'capabilities': service.capabilities, - 'service_name': service.service_name, - 'storage_service_properties': service.storage_service_properties, - 'extended_properties': service.extended_properties, - 'storage_service_keys': service.storage_service_keys, - 'url': service.url, + "capabilities": service.capabilities, + "service_name": service.service_name, + "storage_service_properties": service.storage_service_properties, + "extended_properties": service.extended_properties, + "storage_service_keys": service.storage_service_keys, + "url": service.url, } return ret def get_operation_status(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Get Operation Status, based on a request ID @@ -1054,38 +1104,38 @@ def get_operation_status(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f get_operation_status my-azure id=0123456789abcdef0123456789abcdef - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_instance function must be called with -f or --function.' + "The show_instance function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'id' not in kwargs: + if "id" not in kwargs: raise SaltCloudSystemExit('A request ID must be specified as "id"') if not conn: conn = get_conn() - data = conn.get_operation_status(kwargs['id']) + data = conn.get_operation_status(kwargs["id"]) ret = { - 'http_status_code': data.http_status_code, - 'id': kwargs['id'], - 'status': data.status + "http_status_code": data.http_status_code, + "id": kwargs["id"], + "status": data.status, } - if hasattr(data.error, 'code'): - ret['error'] = { - 'code': data.error.code, - 'message': data.error.message, + if hasattr(data.error, "code"): + ret["error"] = { + "code": data.error.code, + "message": data.error.message, } return ret def list_storage(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List storage accounts associated with the account @@ -1095,10 +1145,10 @@ def list_storage(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f list_storage my-azure - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_storage function must be called with -f or --function.' + "The list_storage function must be called with -f or --function." ) if not conn: @@ -1113,7 +1163,7 @@ def list_storage(kwargs=None, conn=None, call=None): def show_storage(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List storage service properties @@ -1123,10 +1173,10 @@ def show_storage(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f show_storage my-azure name=my_storage - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_storage function must be called with -f or --function.' + "The show_storage function must be called with -f or --function." ) if not conn: @@ -1135,12 +1185,10 @@ def show_storage(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - data = conn.get_storage_account_properties( - kwargs['name'], - ) + data = conn.get_storage_account_properties(kwargs["name"],) return object_to_dict(data) @@ -1149,7 +1197,7 @@ get_storage = show_storage def show_storage_keys(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Show storage account keys @@ -1159,10 +1207,10 @@ def show_storage_keys(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f show_storage_keys my-azure name=my_storage - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_storage_keys function must be called with -f or --function.' + "The show_storage_keys function must be called with -f or --function." ) if not conn: @@ -1171,19 +1219,19 @@ def show_storage_keys(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') try: - data = conn.get_storage_account_keys( - kwargs['name'], - ) + data = conn.get_storage_account_keys(kwargs["name"],) except AzureMissingResourceHttpError as exc: - storage_data = show_storage(kwargs={'name': kwargs['name']}, call='function') - if storage_data['storage_service_properties']['status'] == 'Creating': - raise SaltCloudSystemExit('The storage account keys have not yet been created.') + storage_data = show_storage(kwargs={"name": kwargs["name"]}, call="function") + if storage_data["storage_service_properties"]["status"] == "Creating": + raise SaltCloudSystemExit( + "The storage account keys have not yet been created." + ) else: - raise SaltCloudSystemExit('{0}: {1}'.format(kwargs['name'], exc.message)) + raise SaltCloudSystemExit("{0}: {1}".format(kwargs["name"], exc.message)) return object_to_dict(data) @@ -1192,7 +1240,7 @@ get_storage_keys = show_storage_keys def create_storage(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Create a new storage account @@ -1202,10 +1250,10 @@ def create_storage(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f create_storage my-azure name=my_storage label=my_storage location='West US' - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_storage function must be called with -f or --function.' + "The show_storage function must be called with -f or --function." ) if kwargs is None: @@ -1214,37 +1262,40 @@ def create_storage(kwargs=None, conn=None, call=None): if not conn: conn = get_conn() - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - if 'description' not in kwargs: + if "description" not in kwargs: raise SaltCloudSystemExit('A description must be specified as "description"') - if 'label' not in kwargs: + if "label" not in kwargs: raise SaltCloudSystemExit('A label must be specified as "label"') - if 'location' not in kwargs and 'affinity_group' not in kwargs: - raise SaltCloudSystemExit('Either a location or an affinity_group ' - 'must be specified (but not both)') + if "location" not in kwargs and "affinity_group" not in kwargs: + raise SaltCloudSystemExit( + "Either a location or an affinity_group " "must be specified (but not both)" + ) try: data = conn.create_storage_account( - service_name=kwargs['name'], - label=kwargs['label'], - description=kwargs.get('description', None), - location=kwargs.get('location', None), - affinity_group=kwargs.get('affinity_group', None), - extended_properties=kwargs.get('extended_properties', None), - geo_replication_enabled=kwargs.get('geo_replication_enabled', None), - account_type=kwargs.get('account_type', 'Standard_GRS'), + service_name=kwargs["name"], + label=kwargs["label"], + description=kwargs.get("description", None), + location=kwargs.get("location", None), + affinity_group=kwargs.get("affinity_group", None), + extended_properties=kwargs.get("extended_properties", None), + geo_replication_enabled=kwargs.get("geo_replication_enabled", None), + account_type=kwargs.get("account_type", "Standard_GRS"), ) - return {'Success': 'The storage account was successfully created'} + return {"Success": "The storage account was successfully created"} except AzureConflictHttpError: - raise SaltCloudSystemExit('There was a conflict. This usually means that the storage account already exists.') + raise SaltCloudSystemExit( + "There was a conflict. This usually means that the storage account already exists." + ) def update_storage(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Update a storage account's properties @@ -1254,10 +1305,10 @@ def update_storage(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f update_storage my-azure name=my_storage label=my_storage - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_storage function must be called with -f or --function.' + "The show_storage function must be called with -f or --function." ) if not conn: @@ -1266,22 +1317,22 @@ def update_storage(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') data = conn.update_storage_account( - service_name=kwargs['name'], - label=kwargs.get('label', None), - description=kwargs.get('description', None), - extended_properties=kwargs.get('extended_properties', None), - geo_replication_enabled=kwargs.get('geo_replication_enabled', None), - account_type=kwargs.get('account_type', 'Standard_GRS'), + service_name=kwargs["name"], + label=kwargs.get("label", None), + description=kwargs.get("description", None), + extended_properties=kwargs.get("extended_properties", None), + geo_replication_enabled=kwargs.get("geo_replication_enabled", None), + account_type=kwargs.get("account_type", "Standard_GRS"), ) - return show_storage(kwargs={'name': kwargs['name']}, call='function') + return show_storage(kwargs={"name": kwargs["name"]}, call="function") def regenerate_storage_keys(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Regenerate storage account keys. Requires a key_type ("primary" or @@ -1292,10 +1343,10 @@ def regenerate_storage_keys(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f regenerate_storage_keys my-azure name=my_storage key_type=primary - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_storage function must be called with -f or --function.' + "The show_storage function must be called with -f or --function." ) if not conn: @@ -1304,24 +1355,27 @@ def regenerate_storage_keys(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - if 'key_type' not in kwargs or kwargs['key_type'] not in ('primary', 'secondary'): - raise SaltCloudSystemExit('A key_type must be specified ("primary" or "secondary")') + if "key_type" not in kwargs or kwargs["key_type"] not in ("primary", "secondary"): + raise SaltCloudSystemExit( + 'A key_type must be specified ("primary" or "secondary")' + ) try: data = conn.regenerate_storage_account_keys( - service_name=kwargs['name'], - key_type=kwargs['key_type'], + service_name=kwargs["name"], key_type=kwargs["key_type"], ) - return show_storage_keys(kwargs={'name': kwargs['name']}, call='function') + return show_storage_keys(kwargs={"name": kwargs["name"]}, call="function") except AzureConflictHttpError: - raise SaltCloudSystemExit('There was a conflict. This usually means that the storage account already exists.') + raise SaltCloudSystemExit( + "There was a conflict. This usually means that the storage account already exists." + ) def delete_storage(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Delete a specific storage account @@ -1331,30 +1385,30 @@ def delete_storage(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f delete_storage my-azure name=my_storage - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_storage function must be called with -f or --function.' + "The delete_storage function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') if not conn: conn = get_conn() try: - data = conn.delete_storage_account(kwargs['name']) - return {'Success': 'The storage account was successfully deleted'} + data = conn.delete_storage_account(kwargs["name"]) + return {"Success": "The storage account was successfully deleted"} except AzureMissingResourceHttpError as exc: - raise SaltCloudSystemExit('{0}: {1}'.format(kwargs['name'], exc.message)) + raise SaltCloudSystemExit("{0}: {1}".format(kwargs["name"], exc.message)) def list_services(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List hosted services associated with the account @@ -1364,10 +1418,10 @@ def list_services(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f list_services my-azure - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_services function must be called with -f or --function.' + "The list_services function must be called with -f or --function." ) if not conn: @@ -1377,12 +1431,12 @@ def list_services(kwargs=None, conn=None, call=None): ret = {} for item in data.hosted_services: ret[item.service_name] = object_to_dict(item) - ret[item.service_name]['name'] = item.service_name + ret[item.service_name]["name"] = item.service_name return ret def show_service(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List hosted service properties @@ -1392,10 +1446,10 @@ def show_service(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f show_service my-azure name=my_service - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_service function must be called with -f or --function.' + "The show_service function must be called with -f or --function." ) if not conn: @@ -1404,19 +1458,18 @@ def show_service(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') data = conn.get_hosted_service_properties( - kwargs['name'], - kwargs.get('details', False) + kwargs["name"], kwargs.get("details", False) ) ret = object_to_dict(data) return ret def create_service(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Create a new hosted service @@ -1426,10 +1479,10 @@ def create_service(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f create_service my-azure name=my_service label=my_service location='West US' - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_service function must be called with -f or --function.' + "The create_service function must be called with -f or --function." ) if not conn: @@ -1438,32 +1491,35 @@ def create_service(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - if 'label' not in kwargs: + if "label" not in kwargs: raise SaltCloudSystemExit('A label must be specified as "label"') - if 'location' not in kwargs and 'affinity_group' not in kwargs: - raise SaltCloudSystemExit('Either a location or an affinity_group ' - 'must be specified (but not both)') + if "location" not in kwargs and "affinity_group" not in kwargs: + raise SaltCloudSystemExit( + "Either a location or an affinity_group " "must be specified (but not both)" + ) try: data = conn.create_hosted_service( - kwargs['name'], - kwargs['label'], - kwargs.get('description', None), - kwargs.get('location', None), - kwargs.get('affinity_group', None), - kwargs.get('extended_properties', None), + kwargs["name"], + kwargs["label"], + kwargs.get("description", None), + kwargs.get("location", None), + kwargs.get("affinity_group", None), + kwargs.get("extended_properties", None), ) - return {'Success': 'The service was successfully created'} + return {"Success": "The service was successfully created"} except AzureConflictHttpError: - raise SaltCloudSystemExit('There was a conflict. This usually means that the service already exists.') + raise SaltCloudSystemExit( + "There was a conflict. This usually means that the service already exists." + ) def delete_service(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Delete a specific service associated with the account @@ -1473,30 +1529,30 @@ def delete_service(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f delete_service my-azure name=my_service - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_service function must be called with -f or --function.' + "The delete_service function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') if not conn: conn = get_conn() try: - conn.delete_hosted_service(kwargs['name']) - return {'Success': 'The service was successfully deleted'} + conn.delete_hosted_service(kwargs["name"]) + return {"Success": "The service was successfully deleted"} except AzureMissingResourceHttpError as exc: - raise SaltCloudSystemExit('{0}: {1}'.format(kwargs['name'], exc.message)) + raise SaltCloudSystemExit("{0}: {1}".format(kwargs["name"], exc.message)) def list_disks(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List disks associated with the account @@ -1506,10 +1562,10 @@ def list_disks(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f list_disks my-azure - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_disks function must be called with -f or --function.' + "The list_disks function must be called with -f or --function." ) if not conn: @@ -1523,7 +1579,7 @@ def list_disks(kwargs=None, conn=None, call=None): def show_disk(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Return information about a disk @@ -1533,10 +1589,10 @@ def show_disk(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f show_disk my-azure name=my_disk - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The get_disk function must be called with -f or --function.' + "The get_disk function must be called with -f or --function." ) if not conn: @@ -1545,10 +1601,10 @@ def show_disk(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - data = conn.get_disk(kwargs['name']) + data = conn.get_disk(kwargs["name"]) return object_to_dict(data) @@ -1557,7 +1613,7 @@ get_disk = show_disk def cleanup_unattached_disks(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Cleans up all disks associated with the account, which are not attached. @@ -1570,32 +1626,33 @@ def cleanup_unattached_disks(kwargs=None, conn=None, call=None): salt-cloud -f cleanup_unattached_disks my-azure name=my_disk salt-cloud -f cleanup_unattached_disks my-azure name=my_disk delete_vhd=True - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_disk function must be called with -f or --function.' + "The delete_disk function must be called with -f or --function." ) if kwargs is None: kwargs = {} - disks = list_disks(kwargs=kwargs, conn=conn, call='function') + disks = list_disks(kwargs=kwargs, conn=conn, call="function") for disk in disks: - if disks[disk]['attached_to'] is None: + if disks[disk]["attached_to"] is None: del_kwargs = { - 'name': disks[disk]['name'], - 'delete_vhd': kwargs.get('delete_vhd', False) + "name": disks[disk]["name"], + "delete_vhd": kwargs.get("delete_vhd", False), } log.info( - 'Deleting disk %s, deleting VHD: %s', - del_kwargs['name'], del_kwargs['delete_vhd'] + "Deleting disk %s, deleting VHD: %s", + del_kwargs["name"], + del_kwargs["delete_vhd"], ) - data = delete_disk(kwargs=del_kwargs, call='function') + data = delete_disk(kwargs=del_kwargs, call="function") return True def delete_disk(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Delete a specific disk associated with the account @@ -1606,30 +1663,30 @@ def delete_disk(kwargs=None, conn=None, call=None): salt-cloud -f delete_disk my-azure name=my_disk salt-cloud -f delete_disk my-azure name=my_disk delete_vhd=True - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_disk function must be called with -f or --function.' + "The delete_disk function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') if not conn: conn = get_conn() try: - data = conn.delete_disk(kwargs['name'], kwargs.get('delete_vhd', False)) - return {'Success': 'The disk was successfully deleted'} + data = conn.delete_disk(kwargs["name"], kwargs.get("delete_vhd", False)) + return {"Success": "The disk was successfully deleted"} except AzureMissingResourceHttpError as exc: - raise SaltCloudSystemExit('{0}: {1}'.format(kwargs['name'], exc.message)) + raise SaltCloudSystemExit("{0}: {1}".format(kwargs["name"], exc.message)) def update_disk(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Update a disk's properties @@ -1640,10 +1697,10 @@ def update_disk(kwargs=None, conn=None, call=None): salt-cloud -f update_disk my-azure name=my_disk label=my_disk salt-cloud -f update_disk my-azure name=my_disk new_name=another_disk - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_disk function must be called with -f or --function.' + "The show_disk function must be called with -f or --function." ) if not conn: @@ -1652,23 +1709,25 @@ def update_disk(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - old_data = show_disk(kwargs={'name': kwargs['name']}, call='function') + old_data = show_disk(kwargs={"name": kwargs["name"]}, call="function") data = conn.update_disk( - disk_name=kwargs['name'], - has_operating_system=kwargs.get('has_operating_system', old_data['has_operating_system']), - label=kwargs.get('label', old_data['label']), - media_link=kwargs.get('media_link', old_data['media_link']), - name=kwargs.get('new_name', old_data['name']), - os=kwargs.get('os', old_data['os']), + disk_name=kwargs["name"], + has_operating_system=kwargs.get( + "has_operating_system", old_data["has_operating_system"] + ), + label=kwargs.get("label", old_data["label"]), + media_link=kwargs.get("media_link", old_data["media_link"]), + name=kwargs.get("new_name", old_data["name"]), + os=kwargs.get("os", old_data["os"]), ) - return show_disk(kwargs={'name': kwargs['name']}, call='function') + return show_disk(kwargs={"name": kwargs["name"]}, call="function") def list_service_certificates(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List certificates associated with the service @@ -1678,22 +1737,22 @@ def list_service_certificates(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f list_service_certificates my-azure name=my_service - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_service_certificates function must be called with -f or --function.' + "The list_service_certificates function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A service name must be specified as "name"') if not conn: conn = get_conn() - data = conn.list_service_certificates(service_name=kwargs['name']) + data = conn.list_service_certificates(service_name=kwargs["name"]) ret = {} for item in data.certificates: ret[item.thumbprint] = object_to_dict(item) @@ -1701,7 +1760,7 @@ def list_service_certificates(kwargs=None, conn=None, call=None): def show_service_certificate(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Return information about a service certificate @@ -1712,10 +1771,10 @@ def show_service_certificate(kwargs=None, conn=None, call=None): salt-cloud -f show_service_certificate my-azure name=my_service_certificate \\ thumbalgorithm=sha1 thumbprint=0123456789ABCDEF - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The get_service_certificate function must be called with -f or --function.' + "The get_service_certificate function must be called with -f or --function." ) if not conn: @@ -1724,19 +1783,19 @@ def show_service_certificate(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A service name must be specified as "name"') - if 'thumbalgorithm' not in kwargs: - raise SaltCloudSystemExit('A thumbalgorithm must be specified as "thumbalgorithm"') + if "thumbalgorithm" not in kwargs: + raise SaltCloudSystemExit( + 'A thumbalgorithm must be specified as "thumbalgorithm"' + ) - if 'thumbprint' not in kwargs: + if "thumbprint" not in kwargs: raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') data = conn.get_service_certificate( - kwargs['name'], - kwargs['thumbalgorithm'], - kwargs['thumbprint'], + kwargs["name"], kwargs["thumbalgorithm"], kwargs["thumbprint"], ) return object_to_dict(data) @@ -1746,7 +1805,7 @@ get_service_certificate = show_service_certificate def add_service_certificate(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Add a new service certificate @@ -1757,10 +1816,10 @@ def add_service_certificate(kwargs=None, conn=None, call=None): salt-cloud -f add_service_certificate my-azure name=my_service_certificate \\ data='...CERT_DATA...' certificate_format=sha1 password=verybadpass - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The add_service_certificate function must be called with -f or --function.' + "The add_service_certificate function must be called with -f or --function." ) if not conn: @@ -1769,33 +1828,37 @@ def add_service_certificate(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - if 'data' not in kwargs: + if "data" not in kwargs: raise SaltCloudSystemExit('Certificate data must be specified as "data"') - if 'certificate_format' not in kwargs: - raise SaltCloudSystemExit('A certificate_format must be specified as "certificate_format"') + if "certificate_format" not in kwargs: + raise SaltCloudSystemExit( + 'A certificate_format must be specified as "certificate_format"' + ) - if 'password' not in kwargs: + if "password" not in kwargs: raise SaltCloudSystemExit('A password must be specified as "password"') try: data = conn.add_service_certificate( - kwargs['name'], - kwargs['data'], - kwargs['certificate_format'], - kwargs['password'], + kwargs["name"], + kwargs["data"], + kwargs["certificate_format"], + kwargs["password"], ) - return {'Success': 'The service certificate was successfully added'} + return {"Success": "The service certificate was successfully added"} except AzureConflictHttpError: - raise SaltCloudSystemExit('There was a conflict. This usually means that the ' - 'service certificate already exists.') + raise SaltCloudSystemExit( + "There was a conflict. This usually means that the " + "service certificate already exists." + ) def delete_service_certificate(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Delete a specific certificate associated with the service @@ -1806,22 +1869,24 @@ def delete_service_certificate(kwargs=None, conn=None, call=None): salt-cloud -f delete_service_certificate my-azure name=my_service_certificate \\ thumbalgorithm=sha1 thumbprint=0123456789ABCDEF - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_service_certificate function must be called with -f or --function.' + "The delete_service_certificate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - if 'thumbalgorithm' not in kwargs: - raise SaltCloudSystemExit('A thumbalgorithm must be specified as "thumbalgorithm"') + if "thumbalgorithm" not in kwargs: + raise SaltCloudSystemExit( + 'A thumbalgorithm must be specified as "thumbalgorithm"' + ) - if 'thumbprint' not in kwargs: + if "thumbprint" not in kwargs: raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') if not conn: @@ -1829,17 +1894,15 @@ def delete_service_certificate(kwargs=None, conn=None, call=None): try: data = conn.delete_service_certificate( - kwargs['name'], - kwargs['thumbalgorithm'], - kwargs['thumbprint'], + kwargs["name"], kwargs["thumbalgorithm"], kwargs["thumbprint"], ) - return {'Success': 'The service certificate was successfully deleted'} + return {"Success": "The service certificate was successfully deleted"} except AzureMissingResourceHttpError as exc: - raise SaltCloudSystemExit('{0}: {1}'.format(kwargs['name'], exc.message)) + raise SaltCloudSystemExit("{0}: {1}".format(kwargs["name"], exc.message)) def list_management_certificates(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List management certificates associated with the subscription @@ -1849,10 +1912,10 @@ def list_management_certificates(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f list_management_certificates my-azure name=my_management - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_management_certificates function must be called with -f or --function.' + "The list_management_certificates function must be called with -f or --function." ) if not conn: @@ -1866,7 +1929,7 @@ def list_management_certificates(kwargs=None, conn=None, call=None): def show_management_certificate(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Return information about a management_certificate @@ -1877,10 +1940,10 @@ def show_management_certificate(kwargs=None, conn=None, call=None): salt-cloud -f get_management_certificate my-azure name=my_management_certificate \\ thumbalgorithm=sha1 thumbprint=0123456789ABCDEF - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The get_management_certificate function must be called with -f or --function.' + "The get_management_certificate function must be called with -f or --function." ) if not conn: @@ -1889,10 +1952,10 @@ def show_management_certificate(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'thumbprint' not in kwargs: + if "thumbprint" not in kwargs: raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') - data = conn.get_management_certificate(kwargs['thumbprint']) + data = conn.get_management_certificate(kwargs["thumbprint"]) return object_to_dict(data) @@ -1901,7 +1964,7 @@ get_management_certificate = show_management_certificate def add_management_certificate(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Add a new management certificate @@ -1912,10 +1975,10 @@ def add_management_certificate(kwargs=None, conn=None, call=None): salt-cloud -f add_management_certificate my-azure public_key='...PUBKEY...' \\ thumbprint=0123456789ABCDEF data='...CERT_DATA...' - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The add_management_certificate function must be called with -f or --function.' + "The add_management_certificate function must be called with -f or --function." ) if not conn: @@ -1924,29 +1987,29 @@ def add_management_certificate(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'public_key' not in kwargs: + if "public_key" not in kwargs: raise SaltCloudSystemExit('A public_key must be specified as "public_key"') - if 'thumbprint' not in kwargs: + if "thumbprint" not in kwargs: raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') - if 'data' not in kwargs: + if "data" not in kwargs: raise SaltCloudSystemExit('Certificate data must be specified as "data"') try: conn.add_management_certificate( - kwargs['name'], - kwargs['thumbprint'], - kwargs['data'], + kwargs["name"], kwargs["thumbprint"], kwargs["data"], ) - return {'Success': 'The management certificate was successfully added'} + return {"Success": "The management certificate was successfully added"} except AzureConflictHttpError: - raise SaltCloudSystemExit('There was a conflict. ' - 'This usually means that the management certificate already exists.') + raise SaltCloudSystemExit( + "There was a conflict. " + "This usually means that the management certificate already exists." + ) def delete_management_certificate(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Delete a specific certificate associated with the management @@ -1957,30 +2020,30 @@ def delete_management_certificate(kwargs=None, conn=None, call=None): salt-cloud -f delete_management_certificate my-azure name=my_management_certificate \\ thumbalgorithm=sha1 thumbprint=0123456789ABCDEF - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_management_certificate function must be called with -f or --function.' + "The delete_management_certificate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'thumbprint' not in kwargs: + if "thumbprint" not in kwargs: raise SaltCloudSystemExit('A thumbprint must be specified as "thumbprint"') if not conn: conn = get_conn() try: - conn.delete_management_certificate(kwargs['thumbprint']) - return {'Success': 'The management certificate was successfully deleted'} + conn.delete_management_certificate(kwargs["thumbprint"]) + return {"Success": "The management certificate was successfully deleted"} except AzureMissingResourceHttpError as exc: - raise SaltCloudSystemExit('{0}: {1}'.format(kwargs['thumbprint'], exc.message)) + raise SaltCloudSystemExit("{0}: {1}".format(kwargs["thumbprint"], exc.message)) def list_virtual_networks(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List input endpoints associated with the deployment @@ -1990,19 +2053,19 @@ def list_virtual_networks(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f list_virtual_networks my-azure service=myservice deployment=mydeployment - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_virtual_networks function must be called with -f or --function.' + "The list_virtual_networks function must be called with -f or --function." ) - path = 'services/networking/virtualnetwork' + path = "services/networking/virtualnetwork" data = query(path) return data def list_input_endpoints(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List input endpoints associated with the deployment @@ -2012,54 +2075,56 @@ def list_input_endpoints(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f list_input_endpoints my-azure service=myservice deployment=mydeployment - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_input_endpoints function must be called with -f or --function.' + "The list_input_endpoints function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'service' not in kwargs: + if "service" not in kwargs: raise SaltCloudSystemExit('A service name must be specified as "service"') - if 'deployment' not in kwargs: + if "deployment" not in kwargs: raise SaltCloudSystemExit('A deployment name must be specified as "deployment"') - path = 'services/hostedservices/{0}/deployments/{1}'.format( - kwargs['service'], - kwargs['deployment'], + path = "services/hostedservices/{0}/deployments/{1}".format( + kwargs["service"], kwargs["deployment"], ) data = query(path) if data is None: raise SaltCloudSystemExit( - 'There was an error listing endpoints with the {0} service on the {1} deployment.'.format( - kwargs['service'], - kwargs['deployment'] + "There was an error listing endpoints with the {0} service on the {1} deployment.".format( + kwargs["service"], kwargs["deployment"] ) ) ret = {} for item in data: - if 'Role' in item: - role = item['Role'] + if "Role" in item: + role = item["Role"] if not isinstance(role, dict): return ret - input_endpoint = role['ConfigurationSets']['ConfigurationSet'].get('InputEndpoints', {}).get('InputEndpoint') + input_endpoint = ( + role["ConfigurationSets"]["ConfigurationSet"] + .get("InputEndpoints", {}) + .get("InputEndpoint") + ) if not input_endpoint: continue if not isinstance(input_endpoint, list): input_endpoint = [input_endpoint] for endpoint in input_endpoint: - ret[endpoint['Name']] = endpoint + ret[endpoint["Name"]] = endpoint return ret return ret def show_input_endpoint(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Show an input endpoint associated with the deployment @@ -2070,28 +2135,28 @@ def show_input_endpoint(kwargs=None, conn=None, call=None): salt-cloud -f show_input_endpoint my-azure service=myservice \\ deployment=mydeployment name=SSH - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_input_endpoint function must be called with -f or --function.' + "The show_input_endpoint function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('An endpoint name must be specified as "name"') - data = list_input_endpoints(kwargs=kwargs, call='function') - return data.get(kwargs['name'], None) + data = list_input_endpoints(kwargs=kwargs, call="function") + return data.get(kwargs["name"], None) # For consistency with Azure SDK get_input_endpoint = show_input_endpoint -def update_input_endpoint(kwargs=None, conn=None, call=None, activity='update'): - ''' +def update_input_endpoint(kwargs=None, conn=None, call=None, activity="update"): + """ .. versionadded:: 2015.8.0 Update an input endpoint associated with the deployment. Please note that @@ -2105,48 +2170,52 @@ def update_input_endpoint(kwargs=None, conn=None, call=None, activity='update'): deployment=mydeployment role=myrole name=HTTP local_port=80 \\ port=80 protocol=tcp enable_direct_server_return=False \\ timeout_for_tcp_idle_connection=4 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The update_input_endpoint function must be called with -f or --function.' + "The update_input_endpoint function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'service' not in kwargs: + if "service" not in kwargs: raise SaltCloudSystemExit('A service name must be specified as "service"') - if 'deployment' not in kwargs: + if "deployment" not in kwargs: raise SaltCloudSystemExit('A deployment name must be specified as "deployment"') - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('An endpoint name must be specified as "name"') - if 'role' not in kwargs: + if "role" not in kwargs: raise SaltCloudSystemExit('An role name must be specified as "role"') - if activity != 'delete': - if 'port' not in kwargs: + if activity != "delete": + if "port" not in kwargs: raise SaltCloudSystemExit('An endpoint port must be specified as "port"') - if 'protocol' not in kwargs: - raise SaltCloudSystemExit('An endpoint protocol (tcp or udp) must be specified as "protocol"') + if "protocol" not in kwargs: + raise SaltCloudSystemExit( + 'An endpoint protocol (tcp or udp) must be specified as "protocol"' + ) - if 'local_port' not in kwargs: - kwargs['local_port'] = kwargs['port'] + if "local_port" not in kwargs: + kwargs["local_port"] = kwargs["port"] - if 'enable_direct_server_return' not in kwargs: - kwargs['enable_direct_server_return'] = False - kwargs['enable_direct_server_return'] = six.text_type(kwargs['enable_direct_server_return']).lower() + if "enable_direct_server_return" not in kwargs: + kwargs["enable_direct_server_return"] = False + kwargs["enable_direct_server_return"] = six.text_type( + kwargs["enable_direct_server_return"] + ).lower() - if 'timeout_for_tcp_idle_connection' not in kwargs: - kwargs['timeout_for_tcp_idle_connection'] = 4 + if "timeout_for_tcp_idle_connection" not in kwargs: + kwargs["timeout_for_tcp_idle_connection"] = 4 - old_endpoints = list_input_endpoints(kwargs, call='function') + old_endpoints = list_input_endpoints(kwargs, call="function") - endpoints_xml = '' - endpoint_xml = ''' + endpoints_xml = "" + endpoint_xml = """ {local_port} {name} @@ -2154,29 +2223,33 @@ def update_input_endpoint(kwargs=None, conn=None, call=None, activity='update'): {protocol} {enable_direct_server_return} {timeout_for_tcp_idle_connection} - ''' + """ - if activity == 'add': - old_endpoints[kwargs['name']] = kwargs - old_endpoints[kwargs['name']]['Name'] = kwargs['name'] + if activity == "add": + old_endpoints[kwargs["name"]] = kwargs + old_endpoints[kwargs["name"]]["Name"] = kwargs["name"] for endpoint in old_endpoints: - if old_endpoints[endpoint]['Name'] == kwargs['name']: - if activity != 'delete': + if old_endpoints[endpoint]["Name"] == kwargs["name"]: + if activity != "delete": this_endpoint_xml = endpoint_xml.format(**kwargs) endpoints_xml += this_endpoint_xml else: this_endpoint_xml = endpoint_xml.format( - local_port=old_endpoints[endpoint]['LocalPort'], - name=old_endpoints[endpoint]['Name'], - port=old_endpoints[endpoint]['Port'], - protocol=old_endpoints[endpoint]['Protocol'], - enable_direct_server_return=old_endpoints[endpoint]['EnableDirectServerReturn'], - timeout_for_tcp_idle_connection=old_endpoints[endpoint].get('IdleTimeoutInMinutes', 4), + local_port=old_endpoints[endpoint]["LocalPort"], + name=old_endpoints[endpoint]["Name"], + port=old_endpoints[endpoint]["Port"], + protocol=old_endpoints[endpoint]["Protocol"], + enable_direct_server_return=old_endpoints[endpoint][ + "EnableDirectServerReturn" + ], + timeout_for_tcp_idle_connection=old_endpoints[endpoint].get( + "IdleTimeoutInMinutes", 4 + ), ) endpoints_xml += this_endpoint_xml - request_xml = ''' @@ -2187,17 +2260,17 @@ xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> -'''.format(endpoints_xml) +""".format( + endpoints_xml + ) - path = 'services/hostedservices/{0}/deployments/{1}/roles/{2}'.format( - kwargs['service'], - kwargs['deployment'], - kwargs['role'], + path = "services/hostedservices/{0}/deployments/{1}/roles/{2}".format( + kwargs["service"], kwargs["deployment"], kwargs["role"], ) query( path=path, - method='PUT', - header_dict={'Content-Type': 'application/xml'}, + method="PUT", + header_dict={"Content-Type": "application/xml"}, data=request_xml, decode=False, ) @@ -2205,7 +2278,7 @@ xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> def add_input_endpoint(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Add an input endpoint to the deployment. Please note that @@ -2219,17 +2292,14 @@ def add_input_endpoint(kwargs=None, conn=None, call=None): deployment=mydeployment role=myrole name=HTTP local_port=80 \\ port=80 protocol=tcp enable_direct_server_return=False \\ timeout_for_tcp_idle_connection=4 - ''' + """ return update_input_endpoint( - kwargs=kwargs, - conn=conn, - call='function', - activity='add', + kwargs=kwargs, conn=conn, call="function", activity="add", ) def delete_input_endpoint(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Delete an input endpoint from the deployment. Please note that @@ -2241,17 +2311,14 @@ def delete_input_endpoint(kwargs=None, conn=None, call=None): salt-cloud -f delete_input_endpoint my-azure service=myservice \\ deployment=mydeployment role=myrole name=HTTP - ''' + """ return update_input_endpoint( - kwargs=kwargs, - conn=conn, - call='function', - activity='delete', + kwargs=kwargs, conn=conn, call="function", activity="delete", ) def show_deployment(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Return information about a deployment @@ -2261,10 +2328,10 @@ def show_deployment(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f show_deployment my-azure name=my_deployment - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The get_deployment function must be called with -f or --function.' + "The get_deployment function must be called with -f or --function." ) if not conn: @@ -2273,15 +2340,16 @@ def show_deployment(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'service_name' not in kwargs: + if "service_name" not in kwargs: raise SaltCloudSystemExit('A service name must be specified as "service_name"') - if 'deployment_name' not in kwargs: - raise SaltCloudSystemExit('A deployment name must be specified as "deployment_name"') + if "deployment_name" not in kwargs: + raise SaltCloudSystemExit( + 'A deployment name must be specified as "deployment_name"' + ) data = conn.get_deployment_by_name( - service_name=kwargs['service_name'], - deployment_name=kwargs['deployment_name'], + service_name=kwargs["service_name"], deployment_name=kwargs["deployment_name"], ) return object_to_dict(data) @@ -2291,7 +2359,7 @@ get_deployment = show_deployment def list_affinity_groups(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List input endpoints associated with the deployment @@ -2301,10 +2369,10 @@ def list_affinity_groups(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f list_affinity_groups my-azure - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_affinity_groups function must be called with -f or --function.' + "The list_affinity_groups function must be called with -f or --function." ) if not conn: @@ -2318,7 +2386,7 @@ def list_affinity_groups(kwargs=None, conn=None, call=None): def show_affinity_group(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Show an affinity group associated with the account @@ -2329,10 +2397,10 @@ def show_affinity_group(kwargs=None, conn=None, call=None): salt-cloud -f show_affinity_group my-azure service=myservice \\ deployment=mydeployment name=SSH - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_affinity_group function must be called with -f or --function.' + "The show_affinity_group function must be called with -f or --function." ) if not conn: @@ -2341,10 +2409,10 @@ def show_affinity_group(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('An affinity group name must be specified as "name"') - data = conn.get_affinity_group_properties(affinity_group_name=kwargs['name']) + data = conn.get_affinity_group_properties(affinity_group_name=kwargs["name"]) return object_to_dict(data) @@ -2353,7 +2421,7 @@ get_affinity_group = show_affinity_group def create_affinity_group(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Create a new affinity group @@ -2363,10 +2431,10 @@ def create_affinity_group(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f create_affinity_group my-azure name=my_affinity_group - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_affinity_group function must be called with -f or --function.' + "The create_affinity_group function must be called with -f or --function." ) if not conn: @@ -2375,29 +2443,31 @@ def create_affinity_group(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - if 'label' not in kwargs: + if "label" not in kwargs: raise SaltCloudSystemExit('A label must be specified as "label"') - if 'location' not in kwargs: + if "location" not in kwargs: raise SaltCloudSystemExit('A location must be specified as "location"') try: conn.create_affinity_group( - kwargs['name'], - kwargs['label'], - kwargs['location'], - kwargs.get('description', None), + kwargs["name"], + kwargs["label"], + kwargs["location"], + kwargs.get("description", None), ) - return {'Success': 'The affinity group was successfully created'} + return {"Success": "The affinity group was successfully created"} except AzureConflictHttpError: - raise SaltCloudSystemExit('There was a conflict. This usually means that the affinity group already exists.') + raise SaltCloudSystemExit( + "There was a conflict. This usually means that the affinity group already exists." + ) def update_affinity_group(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Update an affinity group's properties @@ -2407,10 +2477,10 @@ def update_affinity_group(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f update_affinity_group my-azure name=my_group label=my_group - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The update_affinity_group function must be called with -f or --function.' + "The update_affinity_group function must be called with -f or --function." ) if not conn: @@ -2419,22 +2489,22 @@ def update_affinity_group(kwargs=None, conn=None, call=None): if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') - if 'label' not in kwargs: + if "label" not in kwargs: raise SaltCloudSystemExit('A label must be specified as "label"') conn.update_affinity_group( - affinity_group_name=kwargs['name'], - label=kwargs['label'], - description=kwargs.get('description', None), + affinity_group_name=kwargs["name"], + label=kwargs["label"], + description=kwargs.get("description", None), ) - return show_affinity_group(kwargs={'name': kwargs['name']}, call='function') + return show_affinity_group(kwargs={"name": kwargs["name"]}, call="function") def delete_affinity_group(kwargs=None, conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Delete a specific affinity group associated with the account @@ -2444,54 +2514,58 @@ def delete_affinity_group(kwargs=None, conn=None, call=None): .. code-block:: bash salt-cloud -f delete_affinity_group my-azure name=my_affinity_group - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_affinity_group function must be called with -f or --function.' + "The delete_affinity_group function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('A name must be specified as "name"') if not conn: conn = get_conn() try: - conn.delete_affinity_group(kwargs['name']) - return {'Success': 'The affinity group was successfully deleted'} + conn.delete_affinity_group(kwargs["name"]) + return {"Success": "The affinity group was successfully deleted"} except AzureMissingResourceHttpError as exc: - raise SaltCloudSystemExit('{0}: {1}'.format(kwargs['name'], exc.message)) + raise SaltCloudSystemExit("{0}: {1}".format(kwargs["name"], exc.message)) def get_storage_conn(storage_account=None, storage_key=None, conn_kwargs=None): - ''' + """ .. versionadded:: 2015.8.0 Return a storage_conn object for the storage account - ''' + """ if conn_kwargs is None: conn_kwargs = {} if not storage_account: storage_account = config.get_cloud_config_value( - 'storage_account', - get_configured_provider(), __opts__, search_global=False, - default=conn_kwargs.get('storage_account', None) + "storage_account", + get_configured_provider(), + __opts__, + search_global=False, + default=conn_kwargs.get("storage_account", None), ) if not storage_key: storage_key = config.get_cloud_config_value( - 'storage_key', - get_configured_provider(), __opts__, search_global=False, - default=conn_kwargs.get('storage_key', None) + "storage_key", + get_configured_provider(), + __opts__, + search_global=False, + default=conn_kwargs.get("storage_key", None), ) return azure.storage.BlobService(storage_account, storage_key) def make_blob_url(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Creates the URL to access a blob @@ -2515,30 +2589,30 @@ def make_blob_url(kwargs=None, storage_conn=None, call=None): host_base: Live host base URL. If not specified, derives the host base from the provider configuration. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The make_blob_url function must be called with -f or --function.' + "The make_blob_url function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'container' not in kwargs: + if "container" not in kwargs: raise SaltCloudSystemExit('A container name must be specified as "container"') - if 'blob' not in kwargs: + if "blob" not in kwargs: raise SaltCloudSystemExit('A blob name must be specified as "blob"') if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) data = storage_conn.make_blob_url( - kwargs['container'], - kwargs['blob'], - kwargs.get('account', None), - kwargs.get('protocol', None), - kwargs.get('host_base', None), + kwargs["container"], + kwargs["blob"], + kwargs.get("account", None), + kwargs.get("protocol", None), + kwargs.get("host_base", None), ) ret = {} for item in data.containers: @@ -2547,7 +2621,7 @@ def make_blob_url(kwargs=None, storage_conn=None, call=None): def list_storage_containers(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List containers associated with the storage account @@ -2557,10 +2631,10 @@ def list_storage_containers(kwargs=None, storage_conn=None, call=None): .. code-block:: bash salt-cloud -f list_storage_containers my-azure - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_storage_containers function must be called with -f or --function.' + "The list_storage_containers function must be called with -f or --function." ) if not storage_conn: @@ -2574,7 +2648,7 @@ def list_storage_containers(kwargs=None, storage_conn=None, call=None): def create_storage_container(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Create a storage container @@ -2594,10 +2668,10 @@ def create_storage_container(kwargs=None, storage_conn=None, call=None): Optional. Possible values include: container, blob fail_on_exist: Specify whether to throw an exception when the container exists. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_storage_container function must be called with -f or --function.' + "The create_storage_container function must be called with -f or --function." ) if not storage_conn: @@ -2605,18 +2679,20 @@ def create_storage_container(kwargs=None, storage_conn=None, call=None): try: storage_conn.create_container( - container_name=kwargs['name'], - x_ms_meta_name_values=kwargs.get('meta_name_values', None), - x_ms_blob_public_access=kwargs.get('blob_public_access', None), - fail_on_exist=kwargs.get('fail_on_exist', False), + container_name=kwargs["name"], + x_ms_meta_name_values=kwargs.get("meta_name_values", None), + x_ms_blob_public_access=kwargs.get("blob_public_access", None), + fail_on_exist=kwargs.get("fail_on_exist", False), ) - return {'Success': 'The storage container was successfully created'} + return {"Success": "The storage container was successfully created"} except AzureConflictHttpError: - raise SaltCloudSystemExit('There was a conflict. This usually means that the storage container already exists.') + raise SaltCloudSystemExit( + "There was a conflict. This usually means that the storage container already exists." + ) def show_storage_container(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Show a container associated with the storage account @@ -2629,24 +2705,25 @@ def show_storage_container(kwargs=None, storage_conn=None, call=None): name: Name of container to show. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_storage_container function must be called with -f or --function.' + "The show_storage_container function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: - raise SaltCloudSystemExit('An storage container name must be specified as "name"') + if "name" not in kwargs: + raise SaltCloudSystemExit( + 'An storage container name must be specified as "name"' + ) if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) data = storage_conn.get_container_properties( - container_name=kwargs['name'], - x_ms_lease_id=kwargs.get('lease_id', None), + container_name=kwargs["name"], x_ms_lease_id=kwargs.get("lease_id", None), ) return data @@ -2656,7 +2733,7 @@ get_storage_container = show_storage_container def show_storage_container_metadata(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Show a storage container's metadata @@ -2672,24 +2749,25 @@ def show_storage_container_metadata(kwargs=None, storage_conn=None, call=None): lease_id: If specified, show_storage_container_metadata only succeeds if the container's lease is active and matches this ID. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_storage_container function must be called with -f or --function.' + "The show_storage_container function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: - raise SaltCloudSystemExit('An storage container name must be specified as "name"') + if "name" not in kwargs: + raise SaltCloudSystemExit( + 'An storage container name must be specified as "name"' + ) if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) data = storage_conn.get_container_metadata( - container_name=kwargs['name'], - x_ms_lease_id=kwargs.get('lease_id', None), + container_name=kwargs["name"], x_ms_lease_id=kwargs.get("lease_id", None), ) return data @@ -2699,7 +2777,7 @@ get_storage_container_metadata = show_storage_container_metadata def set_storage_container_metadata(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Set a storage container's metadata @@ -2719,20 +2797,22 @@ def set_storage_container_metadata(kwargs=None, storage_conn=None, call=None): lease_id: If specified, set_storage_container_metadata only succeeds if the container's lease is active and matches this ID. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_storage_container function must be called with -f or --function.' + "The create_storage_container function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: - raise SaltCloudSystemExit('An storage container name must be specified as "name"') + if "name" not in kwargs: + raise SaltCloudSystemExit( + 'An storage container name must be specified as "name"' + ) x_ms_meta_name_values = salt.utils.yaml.safe_load( - kwargs.get('meta_name_values', '') + kwargs.get("meta_name_values", "") ) if not storage_conn: @@ -2740,17 +2820,17 @@ def set_storage_container_metadata(kwargs=None, storage_conn=None, call=None): try: storage_conn.set_container_metadata( - container_name=kwargs['name'], + container_name=kwargs["name"], x_ms_meta_name_values=x_ms_meta_name_values, - x_ms_lease_id=kwargs.get('lease_id', None), + x_ms_lease_id=kwargs.get("lease_id", None), ) - return {'Success': 'The storage container was successfully updated'} + return {"Success": "The storage container was successfully updated"} except AzureConflictHttpError: - raise SaltCloudSystemExit('There was a conflict.') + raise SaltCloudSystemExit("There was a conflict.") def show_storage_container_acl(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Show a storage container's acl @@ -2766,24 +2846,25 @@ def show_storage_container_acl(kwargs=None, storage_conn=None, call=None): lease_id: If specified, show_storage_container_acl only succeeds if the container's lease is active and matches this ID. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_storage_container function must be called with -f or --function.' + "The show_storage_container function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: - raise SaltCloudSystemExit('An storage container name must be specified as "name"') + if "name" not in kwargs: + raise SaltCloudSystemExit( + 'An storage container name must be specified as "name"' + ) if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) data = storage_conn.get_container_acl( - container_name=kwargs['name'], - x_ms_lease_id=kwargs.get('lease_id', None), + container_name=kwargs["name"], x_ms_lease_id=kwargs.get("lease_id", None), ) return data @@ -2793,7 +2874,7 @@ get_storage_container_acl = show_storage_container_acl def set_storage_container_acl(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Set a storage container's acl @@ -2813,10 +2894,10 @@ def set_storage_container_acl(kwargs=None, storage_conn=None, call=None): lease_id: If specified, set_storage_container_acl only succeeds if the container's lease is active and matches this ID. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_storage_container function must be called with -f or --function.' + "The create_storage_container function must be called with -f or --function." ) if not storage_conn: @@ -2824,18 +2905,18 @@ def set_storage_container_acl(kwargs=None, storage_conn=None, call=None): try: data = storage_conn.set_container_acl( - container_name=kwargs['name'], - signed_identifiers=kwargs.get('signed_identifiers', None), - x_ms_blob_public_access=kwargs.get('blob_public_access', None), - x_ms_lease_id=kwargs.get('lease_id', None), + container_name=kwargs["name"], + signed_identifiers=kwargs.get("signed_identifiers", None), + x_ms_blob_public_access=kwargs.get("blob_public_access", None), + x_ms_lease_id=kwargs.get("lease_id", None), ) - return {'Success': 'The storage container was successfully updated'} + return {"Success": "The storage container was successfully updated"} except AzureConflictHttpError: - raise SaltCloudSystemExit('There was a conflict.') + raise SaltCloudSystemExit("There was a conflict.") def delete_storage_container(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Delete a container associated with the storage account @@ -2853,31 +2934,33 @@ def delete_storage_container(kwargs=None, storage_conn=None, call=None): lease_id: If specified, delete_storage_container only succeeds if the container's lease is active and matches this ID. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The delete_storage_container function must be called with -f or --function.' + "The delete_storage_container function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: - raise SaltCloudSystemExit('An storage container name must be specified as "name"') + if "name" not in kwargs: + raise SaltCloudSystemExit( + 'An storage container name must be specified as "name"' + ) if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) data = storage_conn.delete_container( - container_name=kwargs['name'], - fail_not_exist=kwargs.get('fail_not_exist', None), - x_ms_lease_id=kwargs.get('lease_id', None), + container_name=kwargs["name"], + fail_not_exist=kwargs.get("fail_not_exist", None), + x_ms_lease_id=kwargs.get("lease_id", None), ) return data def lease_storage_container(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Lease a container associated with the storage account @@ -2913,50 +2996,50 @@ def lease_storage_container(kwargs=None, storage_conn=None, call=None): proposed_lease_id: Optional for acquire, required for change. Proposed lease ID, in a GUID string format. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The lease_storage_container function must be called with -f or --function.' + "The lease_storage_container function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'name' not in kwargs: - raise SaltCloudSystemExit('An storage container name must be specified as "name"') - - lease_actions = ('acquire', 'renew', 'release', 'break', 'change') - - if kwargs.get('lease_action', None) not in lease_actions: + if "name" not in kwargs: raise SaltCloudSystemExit( - 'A lease_action must be one of: {0}'.format( - ', '.join(lease_actions) - ) + 'An storage container name must be specified as "name"' ) - if kwargs['lease_action'] != 'acquire' and 'lease_id' not in kwargs: + lease_actions = ("acquire", "renew", "release", "break", "change") + + if kwargs.get("lease_action", None) not in lease_actions: + raise SaltCloudSystemExit( + "A lease_action must be one of: {0}".format(", ".join(lease_actions)) + ) + + if kwargs["lease_action"] != "acquire" and "lease_id" not in kwargs: raise SaltCloudSystemExit( 'A lease ID must be specified for the "{0}" lease action ' - 'as "lease_id"'.format(kwargs['lease_action']) + 'as "lease_id"'.format(kwargs["lease_action"]) ) if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) data = storage_conn.lease_container( - container_name=kwargs['name'], - x_ms_lease_action=kwargs['lease_action'], - x_ms_lease_id=kwargs.get('lease_id', None), - x_ms_lease_duration=kwargs.get('lease_duration', 60), - x_ms_lease_break_period=kwargs.get('lease_break_period', None), - x_ms_proposed_lease_id=kwargs.get('proposed_lease_id', None), + container_name=kwargs["name"], + x_ms_lease_action=kwargs["lease_action"], + x_ms_lease_id=kwargs.get("lease_id", None), + x_ms_lease_duration=kwargs.get("lease_duration", 60), + x_ms_lease_break_period=kwargs.get("lease_break_period", None), + x_ms_proposed_lease_id=kwargs.get("proposed_lease_id", None), ) return data def list_blobs(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 List blobs associated with the container @@ -3010,17 +3093,19 @@ def list_blobs(kwargs=None, storage_conn=None, call=None): placeholder for all blobs whose names begin with the same substring up to the appearance of the delimiter character. The delimiter may be a single character or a string. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_blobs function must be called with -f or --function.' + "The list_blobs function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'container' not in kwargs: - raise SaltCloudSystemExit('An storage container name must be specified as "container"') + if "container" not in kwargs: + raise SaltCloudSystemExit( + 'An storage container name must be specified as "container"' + ) if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) @@ -3029,7 +3114,7 @@ def list_blobs(kwargs=None, storage_conn=None, call=None): def show_blob_service_properties(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Show a blob's service properties @@ -3039,17 +3124,17 @@ def show_blob_service_properties(kwargs=None, storage_conn=None, call=None): .. code-block:: bash salt-cloud -f show_blob_service_properties my-azure - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_blob_service_properties function must be called with -f or --function.' + "The show_blob_service_properties function must be called with -f or --function." ) if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) data = storage_conn.get_blob_service_properties( - timeout=kwargs.get('timeout', None), + timeout=kwargs.get("timeout", None), ) return data @@ -3059,7 +3144,7 @@ get_blob_service_properties = show_blob_service_properties def set_blob_service_properties(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Sets the properties of a storage account's Blob service, including @@ -3077,30 +3162,32 @@ def set_blob_service_properties(kwargs=None, storage_conn=None, call=None): a StorageServiceProperties object. timeout: Optional. The timeout parameter is expressed in seconds. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The set_blob_service_properties function must be called with -f or --function.' + "The set_blob_service_properties function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'properties' not in kwargs: - raise SaltCloudSystemExit('The blob service properties name must be specified as "properties"') + if "properties" not in kwargs: + raise SaltCloudSystemExit( + 'The blob service properties name must be specified as "properties"' + ) if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) data = storage_conn.get_blob_service_properties( - storage_service_properties=kwargs['properties'], - timeout=kwargs.get('timeout', None), + storage_service_properties=kwargs["properties"], + timeout=kwargs.get("timeout", None), ) return data def show_blob_properties(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Returns all user-defined metadata, standard HTTP properties, and @@ -3118,19 +3205,19 @@ def show_blob_properties(kwargs=None, storage_conn=None, call=None): Name of existing blob. lease_id: Required if the blob has an active lease. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_blob_properties function must be called with -f or --function.' + "The show_blob_properties function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'container' not in kwargs: + if "container" not in kwargs: raise SaltCloudSystemExit('The container name must be specified as "container"') - if 'blob' not in kwargs: + if "blob" not in kwargs: raise SaltCloudSystemExit('The blob name must be specified as "blob"') if not storage_conn: @@ -3138,12 +3225,12 @@ def show_blob_properties(kwargs=None, storage_conn=None, call=None): try: data = storage_conn.get_blob_properties( - container_name=kwargs['container'], - blob_name=kwargs['blob'], - x_ms_lease_id=kwargs.get('lease_id', None), + container_name=kwargs["container"], + blob_name=kwargs["blob"], + x_ms_lease_id=kwargs.get("lease_id", None), ) except AzureMissingResourceHttpError: - raise SaltCloudSystemExit('The specified blob does not exist.') + raise SaltCloudSystemExit("The specified blob does not exist.") return data @@ -3153,7 +3240,7 @@ get_blob_properties = show_blob_properties def set_blob_properties(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Set a blob's properties @@ -3188,41 +3275,43 @@ def set_blob_properties(kwargs=None, storage_conn=None, call=None): attachment, it indicates that the user-agent should not display the response, but instead show a Save As dialog with a filename other than the blob name specified. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The set_blob_properties function must be called with -f or --function.' + "The set_blob_properties function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'container' not in kwargs: - raise SaltCloudSystemExit('The blob container name must be specified as "container"') + if "container" not in kwargs: + raise SaltCloudSystemExit( + 'The blob container name must be specified as "container"' + ) - if 'blob' not in kwargs: + if "blob" not in kwargs: raise SaltCloudSystemExit('The blob name must be specified as "blob"') if not storage_conn: storage_conn = get_storage_conn(conn_kwargs=kwargs) data = storage_conn.get_blob_properties( - container_name=kwargs['container'], - blob_name=kwargs['blob'], - x_ms_blob_cache_control=kwargs.get('blob_cache_control', None), - x_ms_blob_content_type=kwargs.get('blob_content_type', None), - x_ms_blob_content_md5=kwargs.get('blob_content_md5', None), - x_ms_blob_content_encoding=kwargs.get('blob_content_encoding', None), - x_ms_blob_content_language=kwargs.get('blob_content_language', None), - x_ms_lease_id=kwargs.get('lease_id', None), - x_ms_blob_content_disposition=kwargs.get('blob_content_disposition', None), + container_name=kwargs["container"], + blob_name=kwargs["blob"], + x_ms_blob_cache_control=kwargs.get("blob_cache_control", None), + x_ms_blob_content_type=kwargs.get("blob_content_type", None), + x_ms_blob_content_md5=kwargs.get("blob_content_md5", None), + x_ms_blob_content_encoding=kwargs.get("blob_content_encoding", None), + x_ms_blob_content_language=kwargs.get("blob_content_language", None), + x_ms_lease_id=kwargs.get("lease_id", None), + x_ms_blob_content_disposition=kwargs.get("blob_content_disposition", None), ) return data def put_blob(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Upload a blob @@ -3269,22 +3358,24 @@ def put_blob(kwargs=None, storage_conn=None, call=None): A dict containing name, value for metadata. lease_id: Required if the blob has an active lease. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The put_blob function must be called with -f or --function.' + "The put_blob function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'container' not in kwargs: - raise SaltCloudSystemExit('The blob container name must be specified as "container"') + if "container" not in kwargs: + raise SaltCloudSystemExit( + 'The blob container name must be specified as "container"' + ) - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('The blob name must be specified as "name"') - if 'blob_path' not in kwargs and 'blob_content' not in kwargs: + if "blob_path" not in kwargs and "blob_content" not in kwargs: raise SaltCloudSystemExit( 'Either a path to a file needs to be passed in as "blob_path" or ' 'the contents of a blob as "blob_content."' @@ -3297,7 +3388,7 @@ def put_blob(kwargs=None, storage_conn=None, call=None): def get_blob(kwargs=None, storage_conn=None, call=None): - ''' + """ .. versionadded:: 2015.8.0 Download a blob @@ -3339,22 +3430,24 @@ def get_blob(kwargs=None, storage_conn=None, call=None): Number of times to retry download of blob chunk if an error occurs. retry_wait: Sleep time in secs between retries. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The get_blob function must be called with -f or --function.' + "The get_blob function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if 'container' not in kwargs: - raise SaltCloudSystemExit('The blob container name must be specified as "container"') + if "container" not in kwargs: + raise SaltCloudSystemExit( + 'The blob container name must be specified as "container"' + ) - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltCloudSystemExit('The blob name must be specified as "name"') - if 'local_path' not in kwargs and 'return_content' not in kwargs: + if "local_path" not in kwargs and "return_content" not in kwargs: raise SaltCloudSystemExit( 'Either a local path needs to be passed in as "local_path" or ' '"return_content" to return the blob contents directly' @@ -3366,41 +3459,36 @@ def get_blob(kwargs=None, storage_conn=None, call=None): return salt.utils.msazure.get_blob(storage_conn=storage_conn, **kwargs) -def query(path, method='GET', data=None, params=None, header_dict=None, decode=True): - ''' +def query(path, method="GET", data=None, params=None, header_dict=None, decode=True): + """ Perform a query directly against the Azure REST API - ''' + """ certificate_path = config.get_cloud_config_value( - 'certificate_path', - get_configured_provider(), __opts__, search_global=False + "certificate_path", get_configured_provider(), __opts__, search_global=False ) subscription_id = salt.utils.stringutils.to_str( config.get_cloud_config_value( - 'subscription_id', - get_configured_provider(), __opts__, search_global=False + "subscription_id", get_configured_provider(), __opts__, search_global=False ) ) management_host = config.get_cloud_config_value( - 'management_host', + "management_host", get_configured_provider(), __opts__, search_global=False, - default='management.core.windows.net' + default="management.core.windows.net", ) backend = config.get_cloud_config_value( - 'backend', - get_configured_provider(), __opts__, search_global=False + "backend", get_configured_provider(), __opts__, search_global=False ) - url = 'https://{management_host}/{subscription_id}/{path}'.format( - management_host=management_host, - subscription_id=subscription_id, - path=path, + url = "https://{management_host}/{subscription_id}/{path}".format( + management_host=management_host, subscription_id=subscription_id, path=path, ) if header_dict is None: header_dict = {} - header_dict['x-ms-version'] = '2014-06-01' + header_dict["x-ms-version"] = "2014-06-01" result = salt.utils.http.query( url, @@ -3413,8 +3501,8 @@ def query(path, method='GET', data=None, params=None, header_dict=None, decode=T cert=certificate_path, backend=backend, decode=decode, - decode_type='xml', + decode_type="xml", ) - if 'dict' in result: - return result['dict'] + if "dict" in result: + return result["dict"] return diff --git a/salt/cloud/clouds/oneandone.py b/salt/cloud/clouds/oneandone.py index 5d62a2ba8a9..9c229af08e0 100644 --- a/salt/cloud/clouds/oneandone.py +++ b/salt/cloud/clouds/oneandone.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" 1&1 Cloud Server Module ======================= @@ -89,10 +89,11 @@ Create a block storage sudo salt-cloud -f create_block_storage my-oneandone-config name='SaltTest2' description='SaltTestDescription' size=50 datacenter_id='5091F6D8CBFEF9C26ACE957C652D5D49' -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import pprint @@ -100,22 +101,32 @@ import time # Import salt libs import salt.config as config -from salt.exceptions import ( - SaltCloudConfigError, - SaltCloudNotFound, - SaltCloudExecutionFailure, - SaltCloudExecutionTimeout, - SaltCloudSystemExit -) -import salt.utils.files # Import salt.cloud libs import salt.utils.cloud +import salt.utils.files import salt.utils.stringutils +from salt.exceptions import ( + SaltCloudConfigError, + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, + SaltCloudNotFound, + SaltCloudSystemExit, +) from salt.ext import six try: - from oneandone.client import OneAndOneService, Server, Hdd, BlockStorage, SshKey # pylint: disable=no-name-in-module + # pylint: disable=no-name-in-module + from oneandone.client import ( + OneAndOneService, + Server, + Hdd, + BlockStorage, + SshKey, + ) + + # pylint: enable=no-name-in-module + HAS_ONEANDONE = True except ImportError: HAS_ONEANDONE = False @@ -123,14 +134,14 @@ except ImportError: # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'oneandone' +__virtualname__ = "oneandone" # Only load in this module if the 1&1 configurations are in place def __virtual__(): - ''' + """ Check for 1&1 configurations. - ''' + """ if get_configured_provider() is False: return False @@ -141,89 +152,85 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('api_token',) + __opts__, __active_provider_name__ or __virtualname__, ("api_token",) ) def get_dependencies(): - ''' + """ Warn if dependencies are not met. - ''' + """ return config.check_driver_dependencies( - __virtualname__, - {'oneandone': HAS_ONEANDONE} + __virtualname__, {"oneandone": HAS_ONEANDONE} ) def get_conn(): - ''' + """ Return a conn object for the passed VM data - ''' + """ return OneAndOneService( api_token=config.get_cloud_config_value( - 'api_token', - get_configured_provider(), - __opts__, - search_global=False + "api_token", get_configured_provider(), __opts__, search_global=False ) ) def get_size(vm_): - ''' + """ Return the VM's size object - ''' + """ vm_size = config.get_cloud_config_value( - 'fixed_instance_size', vm_, __opts__, default=None, - search_global=False + "fixed_instance_size", vm_, __opts__, default=None, search_global=False ) sizes = avail_sizes() if not vm_size: - size = next((item for item in sizes if item['name'] == 'S'), None) + size = next((item for item in sizes if item["name"] == "S"), None) return size - size = next((item for item in sizes if item['name'] == vm_size or item['id'] == vm_size), None) + size = next( + (item for item in sizes if item["name"] == vm_size or item["id"] == vm_size), + None, + ) if size: return size raise SaltCloudNotFound( - 'The specified size, \'{0}\', could not be found.'.format(vm_size) + "The specified size, '{0}', could not be found.".format(vm_size) ) def get_image(vm_): - ''' + """ Return the image object to use - ''' - vm_image = config.get_cloud_config_value('image', vm_, __opts__).encode( - 'ascii', 'salt-cloud-force-ascii' + """ + vm_image = config.get_cloud_config_value("image", vm_, __opts__).encode( + "ascii", "salt-cloud-force-ascii" ) images = avail_images() for key, value in six.iteritems(images): - if vm_image and vm_image in (images[key]['id'], images[key]['name']): + if vm_image and vm_image in (images[key]["id"], images[key]["name"]): return images[key] raise SaltCloudNotFound( - 'The specified image, \'{0}\', could not be found.'.format(vm_image) + "The specified image, '{0}', could not be found.".format(vm_image) ) def avail_locations(conn=None, call=None): - ''' + """ List available locations/datacenters for 1&1 - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) datacenters = [] @@ -232,19 +239,19 @@ def avail_locations(conn=None, call=None): conn = get_conn() for datacenter in conn.list_datacenters(): - datacenters.append({datacenter['country_code']: datacenter}) + datacenters.append({datacenter["country_code"]: datacenter}) - return {'Locations': datacenters} + return {"Locations": datacenters} def create_block_storage(kwargs=None, call=None): - ''' + """ Create a block storage - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) conn = get_conn() @@ -254,25 +261,23 @@ def create_block_storage(kwargs=None, call=None): data = conn.create_block_storage(block_storage=block_storage) - return {'BlockStorage': data} + return {"BlockStorage": data} def _get_block_storage(kwargs): - ''' + """ Construct a block storage instance from passed arguments - ''' + """ if kwargs is None: kwargs = {} - block_storage_name = kwargs.get('name', None) - block_storage_size = kwargs.get('size', None) - block_storage_description = kwargs.get('description', None) - datacenter_id = kwargs.get('datacenter_id', None) - server_id = kwargs.get('server_id', None) + block_storage_name = kwargs.get("name", None) + block_storage_size = kwargs.get("size", None) + block_storage_description = kwargs.get("description", None) + datacenter_id = kwargs.get("datacenter_id", None) + server_id = kwargs.get("server_id", None) - block_storage = BlockStorage( - name=block_storage_name, - size=block_storage_size) + block_storage = BlockStorage(name=block_storage_name, size=block_storage_size) if block_storage_description: block_storage.description = block_storage_description @@ -287,28 +292,26 @@ def _get_block_storage(kwargs): def _get_ssh_key(kwargs): - ''' + """ Construct an SshKey instance from passed arguments - ''' - ssh_key_name = kwargs.get('name', None) - ssh_key_description = kwargs.get('description', None) - public_key = kwargs.get('public_key', None) + """ + ssh_key_name = kwargs.get("name", None) + ssh_key_description = kwargs.get("description", None) + public_key = kwargs.get("public_key", None) return SshKey( - name=ssh_key_name, - description=ssh_key_description, - public_key=public_key + name=ssh_key_name, description=ssh_key_description, public_key=public_key ) def create_ssh_key(kwargs=None, call=None): - ''' + """ Create an ssh key - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) conn = get_conn() @@ -318,17 +321,17 @@ def create_ssh_key(kwargs=None, call=None): data = conn.create_ssh_key(ssh_key=ssh_key) - return {'SshKey': data} + return {"SshKey": data} def avail_images(conn=None, call=None): - ''' + """ Return a list of the server appliances that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) if not conn: @@ -337,20 +340,20 @@ def avail_images(conn=None, call=None): ret = {} for appliance in conn.list_appliances(): - ret[appliance['name']] = appliance + ret[appliance["name"]] = appliance return ret def avail_sizes(call=None): - ''' + """ Return a dict of all available VM sizes on the cloud provider with relevant data. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) conn = get_conn() @@ -361,26 +364,26 @@ def avail_sizes(call=None): def script(vm_): - ''' + """ Return the script deployment object - ''' + """ return salt.utils.cloud.os_script( - config.get_cloud_config_value('script', vm_, __opts__), + config.get_cloud_config_value("script", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) def list_nodes(conn=None, call=None): - ''' + """ Return a list of VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) if not conn: @@ -394,37 +397,36 @@ def list_nodes(conn=None, call=None): private_ips = [] ret = {} - size = node.get('hardware').get('fixed_instance_size_id', 'Custom size') + size = node.get("hardware").get("fixed_instance_size_id", "Custom size") - if node.get('private_networks'): - for private_ip in node['private_networks']: + if node.get("private_networks"): + for private_ip in node["private_networks"]: private_ips.append(private_ip) - if node.get('ips'): - for public_ip in node['ips']: - public_ips.append(public_ip['ip']) + if node.get("ips"): + for public_ip in node["ips"]: + public_ips.append(public_ip["ip"]) server = { - 'id': node['id'], - 'image': node['image']['id'], - 'size': size, - 'state': node['status']['state'], - 'private_ips': private_ips, - 'public_ips': public_ips + "id": node["id"], + "image": node["image"]["id"], + "size": size, + "state": node["status"]["state"], + "private_ips": private_ips, + "public_ips": public_ips, } - ret[node['name']] = server + ret[node["name"]] = server return ret def list_nodes_full(conn=None, call=None): - ''' + """ Return a list of the VMs that are on the provider, with all fields - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or ' - '--function.' + "The list_nodes_full function must be called with -f or " "--function." ) if not conn: @@ -434,50 +436,43 @@ def list_nodes_full(conn=None, call=None): nodes = conn.list_servers() for node in nodes: - ret[node['name']] = node + ret[node["name"]] = node return ret def list_nodes_select(conn=None, call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ if not conn: conn = get_conn() return salt.utils.cloud.list_nodes_select( - list_nodes_full(conn, 'function'), - __opts__['query.selection'], - call, + list_nodes_full(conn, "function"), __opts__["query.selection"], call, ) def show_instance(name, call=None): - ''' + """ Show the details from the provider concerning an instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) nodes = list_nodes_full() - __utils__['cloud.cache_node']( - nodes[name], - __active_provider_name__, - __opts__ - ) + __utils__["cloud.cache_node"](nodes[name], __active_provider_name__, __opts__) return nodes[name] def _get_server(vm_): - ''' + """ Construct server instance from cloud profile config - ''' + """ description = config.get_cloud_config_value( - 'description', vm_, __opts__, default=None, - search_global=False + "description", vm_, __opts__, default=None, search_global=False ) ssh_key = load_public_key(vm_) @@ -487,81 +482,69 @@ def _get_server(vm_): ram = None fixed_instance_size_id = None - if 'fixed_instance_size' in vm_: + if "fixed_instance_size" in vm_: fixed_instance_size = get_size(vm_) - fixed_instance_size_id = fixed_instance_size['id'] - elif (vm_['vcore'] and vm_['cores_per_processor'] and - vm_['ram'] and vm_['hdds']): + fixed_instance_size_id = fixed_instance_size["id"] + elif vm_["vcore"] and vm_["cores_per_processor"] and vm_["ram"] and vm_["hdds"]: vcore = config.get_cloud_config_value( - 'vcore', vm_, __opts__, default=None, - search_global=False + "vcore", vm_, __opts__, default=None, search_global=False ) cores_per_processor = config.get_cloud_config_value( - 'cores_per_processor', vm_, __opts__, default=None, - search_global=False + "cores_per_processor", vm_, __opts__, default=None, search_global=False ) ram = config.get_cloud_config_value( - 'ram', vm_, __opts__, default=None, - search_global=False + "ram", vm_, __opts__, default=None, search_global=False ) else: - raise SaltCloudConfigError("'fixed_instance_size' or 'vcore'," - "'cores_per_processor', 'ram', and 'hdds'" - "must be provided.") + raise SaltCloudConfigError( + "'fixed_instance_size' or 'vcore'," + "'cores_per_processor', 'ram', and 'hdds'" + "must be provided." + ) appliance_id = config.get_cloud_config_value( - 'appliance_id', vm_, __opts__, default=None, - search_global=False + "appliance_id", vm_, __opts__, default=None, search_global=False ) password = config.get_cloud_config_value( - 'password', vm_, __opts__, default=None, - search_global=False + "password", vm_, __opts__, default=None, search_global=False ) firewall_policy_id = config.get_cloud_config_value( - 'firewall_policy_id', vm_, __opts__, default=None, - search_global=False + "firewall_policy_id", vm_, __opts__, default=None, search_global=False ) ip_id = config.get_cloud_config_value( - 'ip_id', vm_, __opts__, default=None, - search_global=False + "ip_id", vm_, __opts__, default=None, search_global=False ) load_balancer_id = config.get_cloud_config_value( - 'load_balancer_id', vm_, __opts__, default=None, - search_global=False + "load_balancer_id", vm_, __opts__, default=None, search_global=False ) monitoring_policy_id = config.get_cloud_config_value( - 'monitoring_policy_id', vm_, __opts__, default=None, - search_global=False + "monitoring_policy_id", vm_, __opts__, default=None, search_global=False ) datacenter_id = config.get_cloud_config_value( - 'datacenter_id', vm_, __opts__, default=None, - search_global=False + "datacenter_id", vm_, __opts__, default=None, search_global=False ) private_network_id = config.get_cloud_config_value( - 'private_network_id', vm_, __opts__, default=None, - search_global=False + "private_network_id", vm_, __opts__, default=None, search_global=False ) power_on = config.get_cloud_config_value( - 'power_on', vm_, __opts__, default=True, - search_global=False + "power_on", vm_, __opts__, default=True, search_global=False ) public_key = config.get_cloud_config_value( - 'public_key_ids', vm_, __opts__, default=True, - search_global=False + "public_key_ids", vm_, __opts__, default=True, search_global=False ) # Contruct server object return Server( - name=vm_['name'], + name=vm_["name"], description=description, fixed_instance_size_id=fixed_instance_size_id, vcore=vcore, @@ -577,43 +560,39 @@ def _get_server(vm_): datacenter_id=datacenter_id, rsa_key=ssh_key, private_network_id=private_network_id, - public_key=public_key + public_key=public_key, ) def _get_hdds(vm_): - ''' + """ Construct VM hdds from cloud profile config - ''' + """ _hdds = config.get_cloud_config_value( - 'hdds', vm_, __opts__, default=None, - search_global=False + "hdds", vm_, __opts__, default=None, search_global=False ) hdds = [] for hdd in _hdds: - hdds.append( - Hdd( - size=hdd['size'], - is_main=hdd['is_main'] - ) - ) + hdds.append(Hdd(size=hdd["size"], is_main=hdd["is_main"])) return hdds def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if (vm_['profile'] and - config.is_profile_configured(__opts__, - (__active_provider_name__ or - 'oneandone'), - vm_['profile']) is False): + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, (__active_provider_name__ or "oneandone"), vm_["profile"] + ) + is False + ): return False except AttributeError: pass @@ -625,67 +604,68 @@ def create(vm_): # Assemble the composite server object. server = _get_server(vm_) - if not bool(server.specs['hardware']['fixed_instance_size_id']): + if not bool(server.specs["hardware"]["fixed_instance_size_id"]): # Assemble the hdds object. hdds = _get_hdds(vm_) - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args={'name': vm_['name']}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args={"name": vm_["name"]}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: data = conn.create_server(server=server, hdds=hdds) - _wait_for_completion(conn, - get_wait_timeout(vm_), - data['id']) + _wait_for_completion(conn, get_wait_timeout(vm_), data["id"]) except Exception as exc: # pylint: disable=W0703 log.error( - 'Error creating %s on 1and1\n\n' - 'The following exception was thrown by the 1and1 library ' - 'when trying to run the initial deployment: \n%s', - vm_['name'], exc, exc_info_on_loglevel=logging.DEBUG + "Error creating %s on 1and1\n\n" + "The following exception was thrown by the 1and1 library " + "when trying to run the initial deployment: \n%s", + vm_["name"], + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - vm_['server_id'] = data['id'] - password = data['first_password'] + vm_["server_id"] = data["id"] + password = data["first_password"] def __query_node_data(vm_, data): - ''' + """ Query node data until node becomes available. - ''' + """ running = False try: - data = show_instance(vm_['name'], 'action') + data = show_instance(vm_["name"], "action") if not data: return False log.debug( - 'Loaded node data for %s:\nname: %s\nstate: %s', - vm_['name'], - pprint.pformat(data['name']), - data['status']['state'] + "Loaded node data for %s:\nname: %s\nstate: %s", + vm_["name"], + pprint.pformat(data["name"]), + data["status"]["state"], ) except Exception as err: # pylint: disable=broad-except log.error( - 'Failed to get nodes list: %s', err, + "Failed to get nodes list: %s", + err, # Show the trackback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) # Trigger a failure in the wait for IP function return False - running = data['status']['state'].lower() == 'powered_on' + running = data["status"]["state"].lower() == "powered_on" if not running: # Still not running, trigger another iteration return - vm_['ssh_host'] = data['ips'][0]['ip'] + vm_["ssh_host"] = data["ips"][0]["ip"] return data @@ -694,48 +674,50 @@ def create(vm_): __query_node_data, update_args=(vm_, data), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10), + "wait_for_ip_interval", vm_, __opts__, default=10 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc.message)) - log.debug('VM is now running') - log.info('Created Cloud VM %s', vm_) - log.debug('%s VM creation details:\n%s', vm_, pprint.pformat(data)) + log.debug("VM is now running") + log.info("Created Cloud VM %s", vm_) + log.debug("%s VM creation details:\n%s", vm_, pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), args={ - 'name': vm_['name'], - 'profile': vm_['profile'], - 'provider': vm_['driver'], + "name": vm_["name"], + "profile": vm_["profile"], + "provider": vm_["driver"], }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if 'ssh_host' in vm_: - vm_['password'] = password - vm_['key_filename'] = get_key_filename(vm_) - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + if "ssh_host" in vm_: + vm_["password"] = password + vm_["key_filename"] = get_key_filename(vm_) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data) return ret else: - raise SaltCloudSystemExit('A valid IP address was not found.') + raise SaltCloudSystemExit("A valid IP address was not found.") def destroy(name, call=None): - ''' + """ destroy a server by name :param name: name given to the server @@ -749,48 +731,45 @@ def destroy(name, call=None): salt-cloud -d vm_name - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) conn = get_conn() node = get_node(conn, name) - conn.delete_server(server_id=node['id']) + conn.delete_server(server_id=node["id"]) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir']( - name, - __active_provider_name__.split(':')[0], - __opts__ + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ ) return True def reboot(name, call=None): - ''' + """ reboot a server by name :param name: name given to the machine :param call: call value in this case is 'action' @@ -801,17 +780,17 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot vm_name - ''' + """ conn = get_conn() node = get_node(conn, name) - conn.modify_server_status(server_id=node['id'], action='REBOOT') + conn.modify_server_status(server_id=node["id"], action="REBOOT") return True def stop(name, call=None): - ''' + """ stop a server by name :param name: name given to the machine :param call: call value in this case is 'action' @@ -822,17 +801,17 @@ def stop(name, call=None): .. code-block:: bash salt-cloud -a stop vm_name - ''' + """ conn = get_conn() node = get_node(conn, name) - conn.stop_server(server_id=node['id']) + conn.stop_server(server_id=node["id"]) return True def start(name, call=None): - ''' + """ start a server by name :param name: name given to the machine :param call: call value in this case is 'action' @@ -844,98 +823,91 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start vm_name - ''' + """ conn = get_conn() node = get_node(conn, name) - conn.start_server(server_id=node['id']) + conn.start_server(server_id=node["id"]) return True def get_node(conn, name): - ''' + """ Return a node for the named VM - ''' + """ for node in conn.list_servers(per_page=1000): - if node['name'] == name: + if node["name"] == name: return node def get_key_filename(vm_): - ''' + """ Check SSH private key file and return absolute path if exists. - ''' + """ key_filename = config.get_cloud_config_value( - 'ssh_private_key', vm_, __opts__, search_global=False, default=None + "ssh_private_key", vm_, __opts__, search_global=False, default=None ) if key_filename is not None: key_filename = os.path.expanduser(key_filename) if not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined ssh_private_key \'{0}\' does not exist'.format( - key_filename - ) + "The defined ssh_private_key '{0}' does not exist".format(key_filename) ) return key_filename def load_public_key(vm_): - ''' + """ Load the public key file if exists. - ''' + """ public_key_filename = config.get_cloud_config_value( - 'ssh_public_key', vm_, __opts__, search_global=False, default=None + "ssh_public_key", vm_, __opts__, search_global=False, default=None ) if public_key_filename is not None: public_key_filename = os.path.expanduser(public_key_filename) if not os.path.isfile(public_key_filename): raise SaltCloudConfigError( - 'The defined ssh_public_key \'{0}\' does not exist'.format( + "The defined ssh_public_key '{0}' does not exist".format( public_key_filename ) ) - with salt.utils.files.fopen(public_key_filename, 'r') as public_key: - key = salt.utils.stringutils.to_unicode(public_key.read().replace('\n', '')) + with salt.utils.files.fopen(public_key_filename, "r") as public_key: + key = salt.utils.stringutils.to_unicode(public_key.read().replace("\n", "")) return key def get_wait_timeout(vm_): - ''' + """ Return the wait_for_timeout for resource provisioning. - ''' + """ return config.get_cloud_config_value( - 'wait_for_timeout', vm_, __opts__, default=15 * 60, - search_global=False + "wait_for_timeout", vm_, __opts__, default=15 * 60, search_global=False ) def _wait_for_completion(conn, wait_timeout, server_id): - ''' + """ Poll request status until resource is provisioned. - ''' + """ wait_timeout = time.time() + wait_timeout while wait_timeout > time.time(): time.sleep(5) server = conn.get_server(server_id) - server_state = server['status']['state'].lower() + server_state = server["status"]["state"].lower() if server_state == "powered_on": return - elif server_state == 'failed': - raise Exception('Server creation failed for {0}'.format(server_id)) - elif server_state in ('active', - 'enabled', - 'deploying', - 'configuring'): + elif server_state == "failed": + raise Exception("Server creation failed for {0}".format(server_id)) + elif server_state in ("active", "enabled", "deploying", "configuring"): continue else: - raise Exception( - 'Unknown server state {0}'.format(server_state)) + raise Exception("Unknown server state {0}".format(server_state)) raise Exception( - 'Timed out waiting for server create completion for {0}'.format(server_id) + "Timed out waiting for server create completion for {0}".format(server_id) ) diff --git a/salt/cloud/clouds/opennebula.py b/salt/cloud/clouds/opennebula.py index 9e22d92b26b..5a251606395 100644 --- a/salt/cloud/clouds/opennebula.py +++ b/salt/cloud/clouds/opennebula.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" OpenNebula Cloud Module ======================= @@ -58,10 +58,11 @@ to find the IP of the new VM. data="Name = test RULE = [PROTOCOL = TCP, RULE_TYPE = inbound, \\ RANGE = 1000:2000]" -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import pprint @@ -69,21 +70,23 @@ import time # Import Salt Libs import salt.config as config +import salt.utils.data +import salt.utils.files from salt.exceptions import ( SaltCloudConfigError, SaltCloudExecutionFailure, SaltCloudExecutionTimeout, SaltCloudNotFound, - SaltCloudSystemExit + SaltCloudSystemExit, ) -import salt.utils.data -import salt.utils.files # Import Third Party Libs from salt.ext import six + try: import salt.ext.six.moves.xmlrpc_client # pylint: disable=E0611 from lxml import etree + HAS_XML_LIBS = True except ImportError: HAS_XML_LIBS = False @@ -92,13 +95,13 @@ except ImportError: # Get Logging Started log = logging.getLogger(__name__) -__virtualname__ = 'opennebula' +__virtualname__ = "opennebula" def __virtual__(): - ''' + """ Check for OpenNebula configs. - ''' + """ if get_configured_provider() is False: return False @@ -109,28 +112,25 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('xml_rpc', 'user', 'password') + ("xml_rpc", "user", "password"), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'lmxl': HAS_XML_LIBS} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"lmxl": HAS_XML_LIBS}) def avail_images(call=None): - ''' + """ Return available OpenNebula images. CLI Example: @@ -141,27 +141,27 @@ def avail_images(call=None): salt-cloud --function avail_images opennebula salt-cloud -f avail_images opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) image_pool = server.one.imagepool.info(auth, -2, -1, -1)[1] images = {} for image in _get_xml(image_pool): - images[image.find('NAME').text] = _xml_to_dict(image) + images[image.find("NAME").text] = _xml_to_dict(image) return images def avail_locations(call=None): - ''' + """ Return available OpenNebula locations. CLI Example: @@ -172,45 +172,45 @@ def avail_locations(call=None): salt-cloud --function avail_locations opennebula salt-cloud -f avail_locations opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option.' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) host_pool = server.one.hostpool.info(auth)[1] locations = {} for host in _get_xml(host_pool): - locations[host.find('NAME').text] = _xml_to_dict(host) + locations[host.find("NAME").text] = _xml_to_dict(host) return locations def avail_sizes(call=None): - ''' + """ Because sizes are built into templates with OpenNebula, there will be no sizes to return here. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option.' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option." ) log.warning( - 'Because sizes are built into templates with OpenNebula, there are no sizes ' - 'to return.' + "Because sizes are built into templates with OpenNebula, there are no sizes " + "to return." ) return {} def list_clusters(call=None): - ''' + """ Returns a list of clusters in OpenNebula. .. versionadded:: 2016.3.0 @@ -220,25 +220,25 @@ def list_clusters(call=None): .. code-block:: bash salt-cloud -f list_clusters opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_clusters function must be called with -f or --function.' + "The list_clusters function must be called with -f or --function." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) cluster_pool = server.one.clusterpool.info(auth)[1] clusters = {} for cluster in _get_xml(cluster_pool): - clusters[cluster.find('NAME').text] = _xml_to_dict(cluster) + clusters[cluster.find("NAME").text] = _xml_to_dict(cluster) return clusters def list_datastores(call=None): - ''' + """ Returns a list of data stores on OpenNebula. .. versionadded:: 2016.3.0 @@ -248,25 +248,25 @@ def list_datastores(call=None): .. code-block:: bash salt-cloud -f list_datastores opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_datastores function must be called with -f or --function.' + "The list_datastores function must be called with -f or --function." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) datastore_pool = server.one.datastorepool.info(auth)[1] datastores = {} for datastore in _get_xml(datastore_pool): - datastores[datastore.find('NAME').text] = _xml_to_dict(datastore) + datastores[datastore.find("NAME").text] = _xml_to_dict(datastore) return datastores def list_hosts(call=None): - ''' + """ Returns a list of hosts on OpenNebula. .. versionadded:: 2016.3.0 @@ -276,17 +276,17 @@ def list_hosts(call=None): .. code-block:: bash salt-cloud -f list_hosts opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_hosts function must be called with -f or --function.' + "The list_hosts function must be called with -f or --function." ) return avail_locations() def list_nodes(call=None): - ''' + """ Return a list of VMs on OpenNebula. CLI Example: @@ -298,17 +298,17 @@ def list_nodes(call=None): salt-cloud --function list_nodes opennebula salt-cloud -f list_nodes opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) return _list_nodes(full=False) def list_nodes_full(call=None): - ''' + """ Return a list of the VMs on OpenNebula. CLI Example: @@ -320,31 +320,31 @@ def list_nodes_full(call=None): salt-cloud --function list_nodes_full opennebula salt-cloud -f list_nodes_full opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) return _list_nodes(full=True) def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) - return __utils__['cloud.list_nodes_select']( - list_nodes_full('function'), __opts__['query.selection'], call, + return __utils__["cloud.list_nodes_select"]( + list_nodes_full("function"), __opts__["query.selection"], call, ) def list_security_groups(call=None): - ''' + """ Lists all security groups available to the user and the user's groups. .. versionadded:: 2016.3.0 @@ -354,25 +354,25 @@ def list_security_groups(call=None): .. code-block:: bash salt-cloud -f list_security_groups opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_security_groups function must be called with -f or --function.' + "The list_security_groups function must be called with -f or --function." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) secgroup_pool = server.one.secgrouppool.info(auth, -2, -1, -1)[1] groups = {} for group in _get_xml(secgroup_pool): - groups[group.find('NAME').text] = _xml_to_dict(group) + groups[group.find("NAME").text] = _xml_to_dict(group) return groups def list_templates(call=None): - ''' + """ Lists all templates available to the user and the user's groups. .. versionadded:: 2016.3.0 @@ -382,25 +382,25 @@ def list_templates(call=None): .. code-block:: bash salt-cloud -f list_templates opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_templates function must be called with -f or --function.' + "The list_templates function must be called with -f or --function." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) template_pool = server.one.templatepool.info(auth, -2, -1, -1)[1] templates = {} for template in _get_xml(template_pool): - templates[template.find('NAME').text] = _xml_to_dict(template) + templates[template.find("NAME").text] = _xml_to_dict(template) return templates def list_vns(call=None): - ''' + """ Lists all virtual networks available to the user and the user's groups. .. versionadded:: 2016.3.0 @@ -410,25 +410,25 @@ def list_vns(call=None): .. code-block:: bash salt-cloud -f list_vns opennebula - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_vns function must be called with -f or --function.' + "The list_vns function must be called with -f or --function." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) vn_pool = server.one.vnpool.info(auth, -2, -1, -1)[1] vns = {} for v_network in _get_xml(vn_pool): - vns[v_network.find('NAME').text] = _xml_to_dict(v_network) + vns[v_network.find("NAME").text] = _xml_to_dict(v_network) return vns def reboot(name, call=None): - ''' + """ Reboot a VM. .. versionadded:: 2016.3.0 @@ -441,19 +441,19 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot my-vm - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The start action must be called with -a or --action.' + "The start action must be called with -a or --action." ) - log.info('Rebooting node %s', name) + log.info("Rebooting node %s", name) - return vm_action(name, kwargs={'action': 'reboot'}, call=call) + return vm_action(name, kwargs={"action": "reboot"}, call=call) def start(name, call=None): - ''' + """ Start a VM. .. versionadded:: 2016.3.0 @@ -466,19 +466,19 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start my-vm - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The start action must be called with -a or --action.' + "The start action must be called with -a or --action." ) - log.info('Starting node %s', name) + log.info("Starting node %s", name) - return vm_action(name, kwargs={'action': 'resume'}, call=call) + return vm_action(name, kwargs={"action": "resume"}, call=call) def stop(name, call=None): - ''' + """ Stop a VM. .. versionadded:: 2016.3.0 @@ -491,19 +491,19 @@ def stop(name, call=None): .. code-block:: bash salt-cloud -a stop my-vm - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The start action must be called with -a or --action.' + "The start action must be called with -a or --action." ) - log.info('Stopping node %s', name) + log.info("Stopping node %s", name) - return vm_action(name, kwargs={'action': 'stop'}, call=call) + return vm_action(name, kwargs={"action": "stop"}, call=call) def get_one_version(kwargs=None, call=None): - ''' + """ Returns the OpenNebula version. .. versionadded:: 2016.3.5 @@ -513,24 +513,24 @@ def get_one_version(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_one_version one_provider_name - ''' + """ - if call == 'action': + if call == "action": raise SaltCloudSystemExit( - 'The get_cluster_id function must be called with -f or --function.' + "The get_cluster_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) return server.one.system.version(auth)[1] def get_cluster_id(kwargs=None, call=None): - ''' + """ Returns a cluster's ID from the given cluster name. .. versionadded:: 2016.3.0 @@ -540,33 +540,29 @@ def get_cluster_id(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_cluster_id opennebula name=my-cluster-name - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_cluster_id function must be called with -f or --function.' + "The get_cluster_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) if name is None: - raise SaltCloudSystemExit( - 'The get_cluster_id function requires a name.' - ) + raise SaltCloudSystemExit("The get_cluster_id function requires a name.") try: - ret = list_clusters()[name]['id'] + ret = list_clusters()[name]["id"] except KeyError: - raise SaltCloudSystemExit( - 'The cluster \'{0}\' could not be found'.format(name) - ) + raise SaltCloudSystemExit("The cluster '{0}' could not be found".format(name)) return ret def get_datastore_id(kwargs=None, call=None): - ''' + """ Returns a data store's ID from the given data store name. .. versionadded:: 2016.3.0 @@ -576,33 +572,31 @@ def get_datastore_id(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_datastore_id opennebula name=my-datastore-name - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_datastore_id function must be called with -f or --function.' + "The get_datastore_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) if name is None: - raise SaltCloudSystemExit( - 'The get_datastore_id function requires a name.' - ) + raise SaltCloudSystemExit("The get_datastore_id function requires a name.") try: - ret = list_datastores()[name]['id'] + ret = list_datastores()[name]["id"] except KeyError: raise SaltCloudSystemExit( - 'The datastore \'{0}\' could not be found.'.format(name) + "The datastore '{0}' could not be found.".format(name) ) return ret def get_host_id(kwargs=None, call=None): - ''' + """ Returns a host's ID from the given host name. .. versionadded:: 2016.3.0 @@ -612,52 +606,48 @@ def get_host_id(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_host_id opennebula name=my-host-name - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_host_id function must be called with -f or --function.' + "The get_host_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) if name is None: - raise SaltCloudSystemExit( - 'The get_host_id function requires a name.' - ) + raise SaltCloudSystemExit("The get_host_id function requires a name.") try: - ret = avail_locations()[name]['id'] + ret = avail_locations()[name]["id"] except KeyError: - raise SaltCloudSystemExit( - 'The host \'{0}\' could not be found'.format(name) - ) + raise SaltCloudSystemExit("The host '{0}' could not be found".format(name)) return ret def get_image(vm_): - r''' + r""" Return the image object to use. vm\_ The VM dictionary for which to obtain an image. - ''' + """ images = avail_images() - vm_image = six.text_type(config.get_cloud_config_value( - 'image', vm_, __opts__, search_global=False - )) + vm_image = six.text_type( + config.get_cloud_config_value("image", vm_, __opts__, search_global=False) + ) for image in images: - if vm_image in (images[image]['name'], images[image]['id']): - return images[image]['id'] + if vm_image in (images[image]["name"], images[image]["id"]): + return images[image]["id"] raise SaltCloudNotFound( - 'The specified image, \'{0}\', could not be found.'.format(vm_image) + "The specified image, '{0}', could not be found.".format(vm_image) ) def get_image_id(kwargs=None, call=None): - ''' + """ Returns an image's ID from the given image name. .. versionadded:: 2016.3.0 @@ -667,59 +657,52 @@ def get_image_id(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_image_id opennebula name=my-image-name - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_image_id function must be called with -f or --function.' + "The get_image_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) if name is None: - raise SaltCloudSystemExit( - 'The get_image_id function requires a name.' - ) + raise SaltCloudSystemExit("The get_image_id function requires a name.") try: - ret = avail_images()[name]['id'] + ret = avail_images()[name]["id"] except KeyError: - raise SaltCloudSystemExit( - 'The image \'{0}\' could not be found'.format(name) - ) + raise SaltCloudSystemExit("The image '{0}' could not be found".format(name)) return ret def get_location(vm_): - r''' + r""" Return the VM's location. vm\_ The VM dictionary for which to obtain a location. - ''' + """ locations = avail_locations() - vm_location = six.text_type(config.get_cloud_config_value( - 'location', vm_, __opts__, search_global=False - )) + vm_location = six.text_type( + config.get_cloud_config_value("location", vm_, __opts__, search_global=False) + ) - if vm_location == 'None': + if vm_location == "None": return None for location in locations: - if vm_location in (locations[location]['name'], - locations[location]['id']): - return locations[location]['id'] + if vm_location in (locations[location]["name"], locations[location]["id"]): + return locations[location]["id"] raise SaltCloudNotFound( - 'The specified location, \'{0}\', could not be found.'.format( - vm_location - ) + "The specified location, '{0}', could not be found.".format(vm_location) ) def get_secgroup_id(kwargs=None, call=None): - ''' + """ Returns a security group's ID from the given security group name. .. versionadded:: 2016.3.0 @@ -729,33 +712,31 @@ def get_secgroup_id(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_secgroup_id opennebula name=my-secgroup-name - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_secgroup_id function must be called with -f or --function.' + "The get_secgroup_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) if name is None: - raise SaltCloudSystemExit( - 'The get_secgroup_id function requires a \'name\'.' - ) + raise SaltCloudSystemExit("The get_secgroup_id function requires a 'name'.") try: - ret = list_security_groups()[name]['id'] + ret = list_security_groups()[name]["id"] except KeyError: raise SaltCloudSystemExit( - 'The security group \'{0}\' could not be found.'.format(name) + "The security group '{0}' could not be found.".format(name) ) return ret def get_template_image(kwargs=None, call=None): - ''' + """ Returns a template's image from the given template name. .. versionadded:: 2018.3.0 @@ -763,33 +744,31 @@ def get_template_image(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_template_image opennebula name=my-template-name - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_template_image function must be called with -f or --function.' + "The get_template_image function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) if name is None: - raise SaltCloudSystemExit( - 'The get_template_image function requires a \'name\'.' - ) + raise SaltCloudSystemExit("The get_template_image function requires a 'name'.") try: - ret = list_templates()[name]['template']['disk']['image'] + ret = list_templates()[name]["template"]["disk"]["image"] except KeyError: raise SaltCloudSystemExit( - 'The image for template \'{0}\' could not be found.'.format(name) + "The image for template '{0}' could not be found.".format(name) ) return ret def get_template_id(kwargs=None, call=None): - ''' + """ Returns a template's ID from the given template name. .. versionadded:: 2016.3.0 @@ -799,54 +778,50 @@ def get_template_id(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_template_id opennebula name=my-template-name - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_template_id function must be called with -f or --function.' + "The get_template_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) if name is None: - raise SaltCloudSystemExit( - 'The get_template_id function requires a \'name\'.' - ) + raise SaltCloudSystemExit("The get_template_id function requires a 'name'.") try: - ret = list_templates()[name]['id'] + ret = list_templates()[name]["id"] except KeyError: - raise SaltCloudSystemExit( - 'The template \'{0}\' could not be found.'.format(name) - ) + raise SaltCloudSystemExit("The template '{0}' could not be found.".format(name)) return ret def get_template(vm_): - r''' + r""" Return the template id for a VM. .. versionadded:: 2016.11.0 vm\_ The VM dictionary for which to obtain a template. - ''' + """ - vm_template = six.text_type(config.get_cloud_config_value( - 'template', vm_, __opts__, search_global=False - )) + vm_template = six.text_type( + config.get_cloud_config_value("template", vm_, __opts__, search_global=False) + ) try: - return list_templates()[vm_template]['id'] + return list_templates()[vm_template]["id"] except KeyError: raise SaltCloudNotFound( - 'The specified template, \'{0}\', could not be found.'.format(vm_template) + "The specified template, '{0}', could not be found.".format(vm_template) ) def get_vm_id(kwargs=None, call=None): - ''' + """ Returns a virtual machine's ID from the given virtual machine's name. .. versionadded:: 2016.3.0 @@ -856,33 +831,29 @@ def get_vm_id(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_vm_id opennebula name=my-vm - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_vm_id function must be called with -f or --function.' + "The get_vm_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) if name is None: - raise SaltCloudSystemExit( - 'The get_vm_id function requires a name.' - ) + raise SaltCloudSystemExit("The get_vm_id function requires a name.") try: - ret = list_nodes()[name]['id'] + ret = list_nodes()[name]["id"] except KeyError: - raise SaltCloudSystemExit( - 'The VM \'{0}\' could not be found.'.format(name) - ) + raise SaltCloudSystemExit("The VM '{0}' could not be found.".format(name)) return ret def get_vn_id(kwargs=None, call=None): - ''' + """ Returns a virtual network's ID from the given virtual network's name. .. versionadded:: 2016.3.0 @@ -892,80 +863,78 @@ def get_vn_id(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_vn_id opennebula name=my-vn-name - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The get_vn_id function must be called with -f or --function.' + "The get_vn_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) if name is None: - raise SaltCloudSystemExit( - 'The get_vn_id function requires a name.' - ) + raise SaltCloudSystemExit("The get_vn_id function requires a name.") try: - ret = list_vns()[name]['id'] + ret = list_vns()[name]["id"] except KeyError: - raise SaltCloudSystemExit( - 'The VN \'{0}\' could not be found.'.format(name) - ) + raise SaltCloudSystemExit("The VN '{0}' could not be found.".format(name)) return ret def _get_device_template(disk, disk_info, template=None): - ''' + """ Returns the template format to create a disk in open nebula .. versionadded:: 2018.3.0 - ''' + """ + def _require_disk_opts(*args): for arg in args: if arg not in disk_info: raise SaltCloudSystemExit( - 'The disk {0} requires a {1}\ - argument'.format(disk, arg) + "The disk {0} requires a {1}\ + argument".format( + disk, arg + ) ) - _require_disk_opts('disk_type', 'size') + _require_disk_opts("disk_type", "size") - size = disk_info['size'] - disk_type = disk_info['disk_type'] + size = disk_info["size"] + disk_type = disk_info["disk_type"] - if disk_type == 'clone': - if 'image' in disk_info: - clone_image = disk_info['image'] + if disk_type == "clone": + if "image" in disk_info: + clone_image = disk_info["image"] else: - clone_image = get_template_image(kwargs={'name': - template}) + clone_image = get_template_image(kwargs={"name": template}) - clone_image_id = get_image_id(kwargs={'name': clone_image}) - temp = 'DISK=[IMAGE={0}, IMAGE_ID={1}, CLONE=YES,\ - SIZE={2}]'.format(clone_image, clone_image_id, - size) + clone_image_id = get_image_id(kwargs={"name": clone_image}) + temp = "DISK=[IMAGE={0}, IMAGE_ID={1}, CLONE=YES,\ + SIZE={2}]".format( + clone_image, clone_image_id, size + ) return temp - if disk_type == 'volatile': - _require_disk_opts('type') - v_type = disk_info['type'] - temp = 'DISK=[TYPE={0}, SIZE={1}]'.format(v_type, size) + if disk_type == "volatile": + _require_disk_opts("type") + v_type = disk_info["type"] + temp = "DISK=[TYPE={0}, SIZE={1}]".format(v_type, size) - if v_type == 'fs': - _require_disk_opts('format') - format = disk_info['format'] - temp = 'DISK=[TYPE={0}, SIZE={1}, FORMAT={2}]'.format(v_type, - size, format) + if v_type == "fs": + _require_disk_opts("format") + format = disk_info["format"] + temp = "DISK=[TYPE={0}, SIZE={1}, FORMAT={2}]".format(v_type, size, format) return temp - #TODO add persistant disk_type + # TODO add persistant disk_type def create(vm_): - r''' + r""" Create a single VM from a data dict. vm\_ @@ -993,194 +962,202 @@ def create(vm_): salt-cloud -p my-opennebula-profile vm_name memory=16384 cpu=2.5 vcpu=16 - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'opennebula', - vm_['profile']) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "opennebula", vm_["profile"] + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) kwargs = { - 'name': vm_['name'], - 'template_id': get_template(vm_), - 'region_id': get_location(vm_), + "name": vm_["name"], + "template_id": get_template(vm_), + "region_id": get_location(vm_), } - if 'template' in vm_: - kwargs['image_id'] = get_template_id({'name': vm_['template']}) + if "template" in vm_: + kwargs["image_id"] = get_template_id({"name": vm_["template"]}) private_networking = config.get_cloud_config_value( - 'private_networking', vm_, __opts__, search_global=False, default=None + "private_networking", vm_, __opts__, search_global=False, default=None ) - kwargs['private_networking'] = 'true' if private_networking else 'false' + kwargs["private_networking"] = "true" if private_networking else "false" - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', kwargs, list(kwargs)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", kwargs, list(kwargs) + ), }, - sock_dir=__opts__['sock_dir'], + sock_dir=__opts__["sock_dir"], ) template = [] - if kwargs.get('region_id'): - template.append('SCHED_REQUIREMENTS="ID={0}"'.format(kwargs.get('region_id'))) - if vm_.get('memory'): - template.append('MEMORY={0}'.format(vm_.get('memory'))) - if vm_.get('cpu'): - template.append('CPU={0}'.format(vm_.get('cpu'))) - if vm_.get('vcpu'): - template.append('VCPU={0}'.format(vm_.get('vcpu'))) - if vm_.get('disk'): - get_disks = vm_.get('disk') - template_name = vm_['image'] + if kwargs.get("region_id"): + template.append('SCHED_REQUIREMENTS="ID={0}"'.format(kwargs.get("region_id"))) + if vm_.get("memory"): + template.append("MEMORY={0}".format(vm_.get("memory"))) + if vm_.get("cpu"): + template.append("CPU={0}".format(vm_.get("cpu"))) + if vm_.get("vcpu"): + template.append("VCPU={0}".format(vm_.get("vcpu"))) + if vm_.get("disk"): + get_disks = vm_.get("disk") + template_name = vm_["image"] for disk in get_disks: - template.append(_get_device_template(disk, get_disks[disk], - template=template_name)) - if 'CLONE' not in six.text_type(template): + template.append( + _get_device_template(disk, get_disks[disk], template=template_name) + ) + if "CLONE" not in six.text_type(template): raise SaltCloudSystemExit( - 'Missing an image disk to clone. Must define a clone disk alongside all other disk definitions.' + "Missing an image disk to clone. Must define a clone disk alongside all other disk definitions." ) template_args = "\n".join(template) try: server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - cret = server.one.template.instantiate(auth, - int(kwargs['template_id']), - kwargs['name'], - False, - template_args) + auth = ":".join([user, password]) + cret = server.one.template.instantiate( + auth, int(kwargs["template_id"]), kwargs["name"], False, template_args + ) if not cret[0]: log.error( - 'Error creating %s on OpenNebula\n\n' - 'The following error was returned when trying to ' - 'instantiate the template: %s', - vm_['name'], cret[1], + "Error creating %s on OpenNebula\n\n" + "The following error was returned when trying to " + "instantiate the template: %s", + vm_["name"], + cret[1], # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on OpenNebula\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: %s', - vm_['name'], exc, + "Error creating %s on OpenNebula\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: %s", + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False - fqdn = vm_.get('fqdn_base') + fqdn = vm_.get("fqdn_base") if fqdn is not None: - fqdn = '{0}.{1}'.format(vm_['name'], fqdn) + fqdn = "{0}.{1}".format(vm_["name"], fqdn) def __query_node_data(vm_name): - node_data = show_instance(vm_name, call='action') + node_data = show_instance(vm_name, call="action") if not node_data: # Trigger an error in the wait_for_ip function return False - if node_data['state'] == '7': + if node_data["state"] == "7": return False - if node_data['lcm_state'] == '3': + if node_data["lcm_state"] == "3": return node_data try: - data = __utils__['cloud.wait_for_ip']( + data = __utils__["cloud.wait_for_ip"]( __query_node_data, - update_args=(vm_['name'],), + update_args=(vm_["name"],), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=2), + "wait_for_ip_interval", vm_, __opts__, default=2 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) key_filename = config.get_cloud_config_value( - 'private_key', vm_, __opts__, search_global=False, default=None + "private_key", vm_, __opts__, search_global=False, default=None ) if key_filename is not None and not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined key_filename \'{0}\' does not exist'.format( - key_filename - ) + "The defined key_filename '{0}' does not exist".format(key_filename) ) if fqdn: - vm_['ssh_host'] = fqdn - private_ip = '0.0.0.0' + vm_["ssh_host"] = fqdn + private_ip = "0.0.0.0" else: try: - private_ip = data['private_ips'][0] + private_ip = data["private_ips"][0] except KeyError: try: - private_ip = data['template']['nic']['ip'] + private_ip = data["template"]["nic"]["ip"] except KeyError: # if IPv6 is used try this as last resort # OpenNebula does not yet show ULA address here so take global - private_ip = data['template']['nic']['ip6_global'] + private_ip = data["template"]["nic"]["ip6_global"] - vm_['ssh_host'] = private_ip + vm_["ssh_host"] = private_ip ssh_username = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, default='root' + "ssh_username", vm_, __opts__, default="root" ) - vm_['username'] = ssh_username - vm_['key_filename'] = key_filename + vm_["username"] = ssh_username + vm_["key_filename"] = key_filename - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) - ret['id'] = data['id'] - ret['image'] = vm_['image'] - ret['name'] = vm_['name'] - ret['size'] = data['template']['memory'] - ret['state'] = data['state'] - ret['private_ips'] = private_ip - ret['public_ips'] = [] + ret["id"] = data["id"] + ret["image"] = vm_["image"] + ret["name"] = vm_["name"] + ret["size"] = data["template"]["memory"] + ret["state"] = data["state"] + ret["private_ips"] = private_ip + ret["public_ips"] = [] - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], ) return ret def destroy(name, call=None): - ''' + """ Destroy a node. Will check termination protection and warn if enabled. name @@ -1195,54 +1172,51 @@ def destroy(name, call=None): salt-cloud --action destroy vm_name salt-cloud -a destroy vm_name - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) - data = show_instance(name, call='action') - node = server.one.vm.action(auth, 'delete', int(data['id'])) + data = show_instance(name, call="action") + node = server.one.vm.action(auth, "delete", int(data["id"])) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir']( - name, - __active_provider_name__.split(':')[0], - __opts__ + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ ) data = { - 'action': 'vm.delete', - 'deleted': node[0], - 'node_id': node[1], - 'error_code': node[2] + "action": "vm.delete", + "deleted": node[0], + "node_id": node[1], + "error_code": node[2], } return data def image_allocate(call=None, kwargs=None): - ''' + """ Allocates a new image in OpenNebula. .. versionadded:: 2016.3.0 @@ -1272,65 +1246,65 @@ def image_allocate(call=None, kwargs=None): salt-cloud -f image_allocate opennebula datastore_name=default \\ data='NAME="Ubuntu 14.04" PATH="/home/one_user/images/ubuntu_desktop.img" \\ DESCRIPTION="Ubuntu 14.04 for development."' - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The image_allocate function must be called with -f or --function.' + "The image_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - path = kwargs.get('path', None) - data = kwargs.get('data', None) - datastore_id = kwargs.get('datastore_id', None) - datastore_name = kwargs.get('datastore_name', None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) + datastore_id = kwargs.get("datastore_id", None) + datastore_name = kwargs.get("datastore_name", None) if datastore_id: if datastore_name: log.warning( - 'Both a \'datastore_id\' and a \'datastore_name\' were provided. ' - '\'datastore_id\' will take precedence.' + "Both a 'datastore_id' and a 'datastore_name' were provided. " + "'datastore_id' will take precedence." ) elif datastore_name: - datastore_id = get_datastore_id(kwargs={'name': datastore_name}) + datastore_id = get_datastore_id(kwargs={"name": datastore_name}) else: raise SaltCloudSystemExit( - 'The image_allocate function requires either a \'datastore_id\' or a ' - '\'datastore_name\' to be provided.' + "The image_allocate function requires either a 'datastore_id' or a " + "'datastore_name' to be provided." ) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The image_allocate function requires either a file \'path\' or \'data\' ' - 'to be provided.' + "The image_allocate function requires either a file 'path' or 'data' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.image.allocate(auth, data, int(datastore_id)) ret = { - 'action': 'image.allocate', - 'allocated': response[0], - 'image_id': response[1], - 'error_code': response[2], + "action": "image.allocate", + "allocated": response[0], + "image_id": response[1], + "error_code": response[2], } return ret def image_clone(call=None, kwargs=None): - ''' + """ Clones an existing image. .. versionadded:: 2016.3.0 @@ -1350,55 +1324,55 @@ def image_clone(call=None, kwargs=None): salt-cloud -f image_clone opennebula name=my-new-image image_id=10 salt-cloud -f image_clone opennebula name=my-new-image image_name=my-image-to-clone - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The image_clone function must be called with -f or --function.' + "The image_clone function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - image_id = kwargs.get('image_id', None) - image_name = kwargs.get('image_name', None) + name = kwargs.get("name", None) + image_id = kwargs.get("image_id", None) + image_name = kwargs.get("image_name", None) if name is None: raise SaltCloudSystemExit( - 'The image_clone function requires a \'name\' to be provided.' + "The image_clone function requires a 'name' to be provided." ) if image_id: if image_name: log.warning( - 'Both the \'image_id\' and \'image_name\' arguments were provided. ' - '\'image_id\' will take precedence.' + "Both the 'image_id' and 'image_name' arguments were provided. " + "'image_id' will take precedence." ) elif image_name: - image_id = get_image_id(kwargs={'name': image_name}) + image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( - 'The image_clone function requires either an \'image_id\' or an ' - '\'image_name\' to be provided.' + "The image_clone function requires either an 'image_id' or an " + "'image_name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.image.clone(auth, int(image_id), name) data = { - 'action': 'image.clone', - 'cloned': response[0], - 'cloned_image_id': response[1], - 'cloned_image_name': name, - 'error_code': response[2], + "action": "image.clone", + "cloned": response[0], + "cloned_image_id": response[1], + "cloned_image_name": name, + "error_code": response[2], } return data def image_delete(call=None, kwargs=None): - ''' + """ Deletes the given image from OpenNebula. Either a name or an image_id must be supplied. @@ -1416,48 +1390,48 @@ def image_delete(call=None, kwargs=None): salt-cloud -f image_delete opennebula name=my-image salt-cloud --function image_delete opennebula image_id=100 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The image_delete function must be called with -f or --function.' + "The image_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - image_id = kwargs.get('image_id', None) + name = kwargs.get("name", None) + image_id = kwargs.get("image_id", None) if image_id: if name: log.warning( - 'Both the \'image_id\' and \'name\' arguments were provided. ' - '\'image_id\' will take precedence.' + "Both the 'image_id' and 'name' arguments were provided. " + "'image_id' will take precedence." ) elif name: - image_id = get_image_id(kwargs={'name': name}) + image_id = get_image_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( - 'The image_delete function requires either an \'image_id\' or a ' - '\'name\' to be provided.' + "The image_delete function requires either an 'image_id' or a " + "'name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.image.delete(auth, int(image_id)) data = { - 'action': 'image.delete', - 'deleted': response[0], - 'image_id': response[1], - 'error_code': response[2], + "action": "image.delete", + "deleted": response[0], + "image_id": response[1], + "error_code": response[2], } return data def image_info(call=None, kwargs=None): - ''' + """ Retrieves information for a given image. Either a name or an image_id must be supplied. @@ -1477,45 +1451,45 @@ def image_info(call=None, kwargs=None): salt-cloud -f image_info opennebula name=my-image salt-cloud --function image_info opennebula image_id=5 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The image_info function must be called with -f or --function.' + "The image_info function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - image_id = kwargs.get('image_id', None) + name = kwargs.get("name", None) + image_id = kwargs.get("image_id", None) if image_id: if name: log.warning( - 'Both the \'image_id\' and \'name\' arguments were provided. ' - '\'image_id\' will take precedence.' + "Both the 'image_id' and 'name' arguments were provided. " + "'image_id' will take precedence." ) elif name: - image_id = get_image_id(kwargs={'name': name}) + image_id = get_image_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( - 'The image_info function requires either a \'name or an \'image_id\' ' - 'to be provided.' + "The image_info function requires either a 'name or an 'image_id' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) info = {} response = server.one.image.info(auth, int(image_id))[1] tree = _get_xml(response) - info[tree.find('NAME').text] = _xml_to_dict(tree) + info[tree.find("NAME").text] = _xml_to_dict(tree) return info def image_persistent(call=None, kwargs=None): - ''' + """ Sets the Image as persistent or not persistent. .. versionadded:: 2016.3.0 @@ -1536,55 +1510,57 @@ def image_persistent(call=None, kwargs=None): salt-cloud -f image_persistent opennebula name=my-image persist=True salt-cloud --function image_persistent opennebula image_id=5 persist=False - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The image_persistent function must be called with -f or --function.' + "The image_persistent function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - persist = kwargs.get('persist', None) - image_id = kwargs.get('image_id', None) + name = kwargs.get("name", None) + persist = kwargs.get("persist", None) + image_id = kwargs.get("image_id", None) if persist is None: raise SaltCloudSystemExit( - 'The image_persistent function requires \'persist\' to be set to \'True\' ' - 'or \'False\'.' + "The image_persistent function requires 'persist' to be set to 'True' " + "or 'False'." ) if image_id: if name: log.warning( - 'Both the \'image_id\' and \'name\' arguments were provided. ' - '\'image_id\' will take precedence.' + "Both the 'image_id' and 'name' arguments were provided. " + "'image_id' will take precedence." ) elif name: - image_id = get_image_id(kwargs={'name': name}) + image_id = get_image_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( - 'The image_persistent function requires either a \'name\' or an ' - '\'image_id\' to be provided.' + "The image_persistent function requires either a 'name' or an " + "'image_id' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - response = server.one.image.persistent(auth, int(image_id), salt.utils.data.is_true(persist)) + auth = ":".join([user, password]) + response = server.one.image.persistent( + auth, int(image_id), salt.utils.data.is_true(persist) + ) data = { - 'action': 'image.persistent', - 'response': response[0], - 'image_id': response[1], - 'error_code': response[2], + "action": "image.persistent", + "response": response[0], + "image_id": response[1], + "error_code": response[2], } return data def image_snapshot_delete(call=None, kwargs=None): - ''' + """ Deletes a snapshot from the image. .. versionadded:: 2016.3.0 @@ -1606,54 +1582,54 @@ def image_snapshot_delete(call=None, kwargs=None): salt-cloud -f image_snapshot_delete vm_id=106 snapshot_id=45 salt-cloud -f image_snapshot_delete vm_name=my-vm snapshot_id=111 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The image_snapshot_delete function must be called with -f or --function.' + "The image_snapshot_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} - image_id = kwargs.get('image_id', None) - image_name = kwargs.get('image_name', None) - snapshot_id = kwargs.get('snapshot_id', None) + image_id = kwargs.get("image_id", None) + image_name = kwargs.get("image_name", None) + snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( - 'The image_snapshot_delete function requires a \'snapshot_id\' to be provided.' + "The image_snapshot_delete function requires a 'snapshot_id' to be provided." ) if image_id: if image_name: log.warning( - 'Both the \'image_id\' and \'image_name\' arguments were provided. ' - '\'image_id\' will take precedence.' + "Both the 'image_id' and 'image_name' arguments were provided. " + "'image_id' will take precedence." ) elif image_name: - image_id = get_image_id(kwargs={'name': image_name}) + image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( - 'The image_snapshot_delete function requires either an \'image_id\' ' - 'or a \'image_name\' to be provided.' + "The image_snapshot_delete function requires either an 'image_id' " + "or a 'image_name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.image.snapshotdelete(auth, int(image_id), int(snapshot_id)) data = { - 'action': 'image.snapshotdelete', - 'deleted': response[0], - 'snapshot_id': response[1], - 'error_code': response[2], + "action": "image.snapshotdelete", + "deleted": response[0], + "snapshot_id": response[1], + "error_code": response[2], } return data def image_snapshot_revert(call=None, kwargs=None): - ''' + """ Reverts an image state to a previous snapshot. .. versionadded:: 2016.3.0 @@ -1673,54 +1649,54 @@ def image_snapshot_revert(call=None, kwargs=None): salt-cloud -f image_snapshot_revert vm_id=106 snapshot_id=45 salt-cloud -f image_snapshot_revert vm_name=my-vm snapshot_id=120 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The image_snapshot_revert function must be called with -f or --function.' + "The image_snapshot_revert function must be called with -f or --function." ) if kwargs is None: kwargs = {} - image_id = kwargs.get('image_id', None) - image_name = kwargs.get('image_name', None) - snapshot_id = kwargs.get('snapshot_id', None) + image_id = kwargs.get("image_id", None) + image_name = kwargs.get("image_name", None) + snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( - 'The image_snapshot_revert function requires a \'snapshot_id\' to be provided.' + "The image_snapshot_revert function requires a 'snapshot_id' to be provided." ) if image_id: if image_name: log.warning( - 'Both the \'image_id\' and \'image_name\' arguments were provided. ' - '\'image_id\' will take precedence.' + "Both the 'image_id' and 'image_name' arguments were provided. " + "'image_id' will take precedence." ) elif image_name: - image_id = get_image_id(kwargs={'name': image_name}) + image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( - 'The image_snapshot_revert function requires either an \'image_id\' or ' - 'an \'image_name\' to be provided.' + "The image_snapshot_revert function requires either an 'image_id' or " + "an 'image_name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.image.snapshotrevert(auth, int(image_id), int(snapshot_id)) data = { - 'action': 'image.snapshotrevert', - 'reverted': response[0], - 'snapshot_id': response[1], - 'error_code': response[2], + "action": "image.snapshotrevert", + "reverted": response[0], + "snapshot_id": response[1], + "error_code": response[2], } return data def image_snapshot_flatten(call=None, kwargs=None): - ''' + """ Flattens the snapshot of an image and discards others. .. versionadded:: 2016.3.0 @@ -1740,55 +1716,55 @@ def image_snapshot_flatten(call=None, kwargs=None): salt-cloud -f image_snapshot_flatten vm_id=106 snapshot_id=45 salt-cloud -f image_snapshot_flatten vm_name=my-vm snapshot_id=45 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The image_snapshot_flatten function must be called with -f or --function.' + "The image_snapshot_flatten function must be called with -f or --function." ) if kwargs is None: kwargs = {} - image_id = kwargs.get('image_id', None) - image_name = kwargs.get('image_name', None) - snapshot_id = kwargs.get('snapshot_id', None) + image_id = kwargs.get("image_id", None) + image_name = kwargs.get("image_name", None) + snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( - 'The image_stanpshot_flatten function requires a \'snapshot_id\' ' - 'to be provided.' + "The image_stanpshot_flatten function requires a 'snapshot_id' " + "to be provided." ) if image_id: if image_name: log.warning( - 'Both the \'image_id\' and \'image_name\' arguments were provided. ' - '\'image_id\' will take precedence.' + "Both the 'image_id' and 'image_name' arguments were provided. " + "'image_id' will take precedence." ) elif image_name: - image_id = get_image_id(kwargs={'name': image_name}) + image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( - 'The image_snapshot_flatten function requires either an ' - '\'image_id\' or an \'image_name\' to be provided.' + "The image_snapshot_flatten function requires either an " + "'image_id' or an 'image_name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.image.snapshotflatten(auth, int(image_id), int(snapshot_id)) data = { - 'action': 'image.snapshotflatten', - 'flattened': response[0], - 'snapshot_id': response[1], - 'error_code': response[2], + "action": "image.snapshotflatten", + "flattened": response[0], + "snapshot_id": response[1], + "error_code": response[2], } return data def image_update(call=None, kwargs=None): - ''' + """ Replaces the image template contents. .. versionadded:: 2016.3.0 @@ -1819,25 +1795,25 @@ def image_update(call=None, kwargs=None): salt-cloud -f image_update opennebula image_name="Ubuntu 14.04" update_type=merge \\ data='NAME="Ubuntu Dev" PATH="/home/one_user/images/ubuntu_desktop.img" \\ DESCRIPTION = "Ubuntu 14.04 for development."' - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The image_allocate function must be called with -f or --function.' + "The image_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - image_id = kwargs.get('image_id', None) - image_name = kwargs.get('image_name', None) - path = kwargs.get('path', None) - data = kwargs.get('data', None) - update_type = kwargs.get('update_type', None) - update_args = ['replace', 'merge'] + image_id = kwargs.get("image_id", None) + image_name = kwargs.get("image_name", None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) + update_type = kwargs.get("update_type", None) + update_args = ["replace", "merge"] if update_type is None: raise SaltCloudSystemExit( - 'The image_update function requires an \'update_type\' to be provided.' + "The image_update function requires an 'update_type' to be provided." ) if update_type == update_args[0]: @@ -1846,57 +1822,56 @@ def image_update(call=None, kwargs=None): update_number = 1 else: raise SaltCloudSystemExit( - 'The update_type argument must be either {0} or {1}.'.format( - update_args[0], - update_args[1] + "The update_type argument must be either {0} or {1}.".format( + update_args[0], update_args[1] ) ) if image_id: if image_name: log.warning( - 'Both the \'image_id\' and \'image_name\' arguments were provided. ' - '\'image_id\' will take precedence.' + "Both the 'image_id' and 'image_name' arguments were provided. " + "'image_id' will take precedence." ) elif image_name: - image_id = get_image_id(kwargs={'name': image_name}) + image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( - 'The image_update function requires either an \'image_id\' or an ' - '\'image_name\' to be provided.' + "The image_update function requires either an 'image_id' or an " + "'image_name' to be provided." ) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The image_update function requires either \'data\' or a file \'path\' ' - 'to be provided.' + "The image_update function requires either 'data' or a file 'path' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.image.update(auth, int(image_id), data, int(update_number)) ret = { - 'action': 'image.update', - 'updated': response[0], - 'image_id': response[1], - 'error_code': response[2], + "action": "image.update", + "updated": response[0], + "image_id": response[1], + "error_code": response[2], } return ret def show_instance(name, call=None): - ''' + """ Show the details from OpenNebula concerning a named VM. name @@ -1912,20 +1887,20 @@ def show_instance(name, call=None): salt-cloud --action show_instance vm_name salt-cloud -a show_instance vm_name - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) node = _get_node(name) - __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](node, __active_provider_name__, __opts__) return node def secgroup_allocate(call=None, kwargs=None): - ''' + """ Allocates a new security group in OpenNebula. .. versionadded:: 2016.3.0 @@ -1947,49 +1922,49 @@ def secgroup_allocate(call=None, kwargs=None): salt-cloud -f secgroup_allocate opennebula \\ data="NAME = test RULE = [PROTOCOL = TCP, RULE_TYPE = inbound, \\ RANGE = 1000:2000]" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The secgroup_allocate function must be called with -f or --function.' + "The secgroup_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - path = kwargs.get('path', None) - data = kwargs.get('data', None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The secgroup_allocate function requires either \'data\' or a file ' - '\'path\' to be provided.' + "The secgroup_allocate function requires either 'data' or a file " + "'path' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.secgroup.allocate(auth, data) ret = { - 'action': 'secgroup.allocate', - 'allocated': response[0], - 'secgroup_id': response[1], - 'error_code': response[2], + "action": "secgroup.allocate", + "allocated": response[0], + "secgroup_id": response[1], + "error_code": response[2], } return ret def secgroup_clone(call=None, kwargs=None): - ''' + """ Clones an existing security group. .. versionadded:: 2016.3.0 @@ -2011,55 +1986,55 @@ def secgroup_clone(call=None, kwargs=None): salt-cloud -f secgroup_clone opennebula name=my-cloned-secgroup secgroup_id=0 salt-cloud -f secgroup_clone opennebula name=my-cloned-secgroup secgroup_name=my-secgroup - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The secgroup_clone function must be called with -f or --function.' + "The secgroup_clone function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - secgroup_id = kwargs.get('secgroup_id', None) - secgroup_name = kwargs.get('secgroup_name', None) + name = kwargs.get("name", None) + secgroup_id = kwargs.get("secgroup_id", None) + secgroup_name = kwargs.get("secgroup_name", None) if name is None: raise SaltCloudSystemExit( - 'The secgroup_clone function requires a \'name\' to be provided.' + "The secgroup_clone function requires a 'name' to be provided." ) if secgroup_id: if secgroup_name: log.warning( - 'Both the \'secgroup_id\' and \'secgroup_name\' arguments were provided. ' - '\'secgroup_id\' will take precedence.' + "Both the 'secgroup_id' and 'secgroup_name' arguments were provided. " + "'secgroup_id' will take precedence." ) elif secgroup_name: - secgroup_id = get_secgroup_id(kwargs={'name': secgroup_name}) + secgroup_id = get_secgroup_id(kwargs={"name": secgroup_name}) else: raise SaltCloudSystemExit( - 'The secgroup_clone function requires either a \'secgroup_id\' or a ' - '\'secgroup_name\' to be provided.' + "The secgroup_clone function requires either a 'secgroup_id' or a " + "'secgroup_name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.secgroup.clone(auth, int(secgroup_id), name) data = { - 'action': 'secgroup.clone', - 'cloned': response[0], - 'cloned_secgroup_id': response[1], - 'cloned_secgroup_name': name, - 'error_code': response[2], + "action": "secgroup.clone", + "cloned": response[0], + "cloned_secgroup_id": response[1], + "cloned_secgroup_name": name, + "error_code": response[2], } return data def secgroup_delete(call=None, kwargs=None): - ''' + """ Deletes the given security group from OpenNebula. Either a name or a secgroup_id must be supplied. @@ -2078,48 +2053,48 @@ def secgroup_delete(call=None, kwargs=None): salt-cloud -f secgroup_delete opennebula name=my-secgroup salt-cloud --function secgroup_delete opennebula secgroup_id=100 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The secgroup_delete function must be called with -f or --function.' + "The secgroup_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - secgroup_id = kwargs.get('secgroup_id', None) + name = kwargs.get("name", None) + secgroup_id = kwargs.get("secgroup_id", None) if secgroup_id: if name: log.warning( - 'Both the \'secgroup_id\' and \'name\' arguments were provided. ' - '\'secgroup_id\' will take precedence.' + "Both the 'secgroup_id' and 'name' arguments were provided. " + "'secgroup_id' will take precedence." ) elif name: - secgroup_id = get_secgroup_id(kwargs={'name': name}) + secgroup_id = get_secgroup_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( - 'The secgroup_delete function requires either a \'name\' or a ' - '\'secgroup_id\' to be provided.' + "The secgroup_delete function requires either a 'name' or a " + "'secgroup_id' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.secgroup.delete(auth, int(secgroup_id)) data = { - 'action': 'secgroup.delete', - 'deleted': response[0], - 'secgroup_id': response[1], - 'error_code': response[2], + "action": "secgroup.delete", + "deleted": response[0], + "secgroup_id": response[1], + "error_code": response[2], } return data def secgroup_info(call=None, kwargs=None): - ''' + """ Retrieves information for the given security group. Either a name or a secgroup_id must be supplied. @@ -2139,45 +2114,45 @@ def secgroup_info(call=None, kwargs=None): salt-cloud -f secgroup_info opennebula name=my-secgroup salt-cloud --function secgroup_info opennebula secgroup_id=5 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The secgroup_info function must be called with -f or --function.' + "The secgroup_info function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - secgroup_id = kwargs.get('secgroup_id', None) + name = kwargs.get("name", None) + secgroup_id = kwargs.get("secgroup_id", None) if secgroup_id: if name: log.warning( - 'Both the \'secgroup_id\' and \'name\' arguments were provided. ' - '\'secgroup_id\' will take precedence.' + "Both the 'secgroup_id' and 'name' arguments were provided. " + "'secgroup_id' will take precedence." ) elif name: - secgroup_id = get_secgroup_id(kwargs={'name': name}) + secgroup_id = get_secgroup_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( - 'The secgroup_info function requires either a name or a secgroup_id ' - 'to be provided.' + "The secgroup_info function requires either a name or a secgroup_id " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) info = {} response = server.one.secgroup.info(auth, int(secgroup_id))[1] tree = _get_xml(response) - info[tree.find('NAME').text] = _xml_to_dict(tree) + info[tree.find("NAME").text] = _xml_to_dict(tree) return info def secgroup_update(call=None, kwargs=None): - ''' + """ Replaces the security group template contents. .. versionadded:: 2016.3.0 @@ -2212,25 +2187,25 @@ def secgroup_update(call=None, kwargs=None): update_type=replace salt-cloud -f secgroup_update opennebula secgroup_name=my-secgroup update_type=merge \\ data="Name = test RULE = [PROTOCOL = TCP, RULE_TYPE = inbound, RANGE = 1000:2000]" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The secgroup_allocate function must be called with -f or --function.' + "The secgroup_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - secgroup_id = kwargs.get('secgroup_id', None) - secgroup_name = kwargs.get('secgroup_name', None) - path = kwargs.get('path', None) - data = kwargs.get('data', None) - update_type = kwargs.get('update_type', None) - update_args = ['replace', 'merge'] + secgroup_id = kwargs.get("secgroup_id", None) + secgroup_name = kwargs.get("secgroup_name", None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) + update_type = kwargs.get("update_type", None) + update_args = ["replace", "merge"] if update_type is None: raise SaltCloudSystemExit( - 'The secgroup_update function requires an \'update_type\' to be provided.' + "The secgroup_update function requires an 'update_type' to be provided." ) if update_type == update_args[0]: @@ -2239,57 +2214,58 @@ def secgroup_update(call=None, kwargs=None): update_number = 1 else: raise SaltCloudSystemExit( - 'The update_type argument must be either {0} or {1}.'.format( - update_args[0], - update_args[1] + "The update_type argument must be either {0} or {1}.".format( + update_args[0], update_args[1] ) ) if secgroup_id: if secgroup_name: log.warning( - 'Both the \'secgroup_id\' and \'secgroup_name\' arguments were provided. ' - '\'secgroup_id\' will take precedence.' + "Both the 'secgroup_id' and 'secgroup_name' arguments were provided. " + "'secgroup_id' will take precedence." ) elif secgroup_name: - secgroup_id = get_secgroup_id(kwargs={'name': secgroup_name}) + secgroup_id = get_secgroup_id(kwargs={"name": secgroup_name}) else: raise SaltCloudSystemExit( - 'The secgroup_update function requires either a \'secgroup_id\' or a ' - '\'secgroup_name\' to be provided.' + "The secgroup_update function requires either a 'secgroup_id' or a " + "'secgroup_name' to be provided." ) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The secgroup_update function requires either \'data\' or a file \'path\' ' - 'to be provided.' + "The secgroup_update function requires either 'data' or a file 'path' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - response = server.one.secgroup.update(auth, int(secgroup_id), data, int(update_number)) + auth = ":".join([user, password]) + response = server.one.secgroup.update( + auth, int(secgroup_id), data, int(update_number) + ) ret = { - 'action': 'secgroup.update', - 'updated': response[0], - 'secgroup_id': response[1], - 'error_code': response[2], + "action": "secgroup.update", + "updated": response[0], + "secgroup_id": response[1], + "error_code": response[2], } return ret def template_allocate(call=None, kwargs=None): - ''' + """ Allocates a new template in OpenNebula. .. versionadded:: 2016.3.0 @@ -2313,49 +2289,49 @@ def template_allocate(call=None, kwargs=None): MEMORY="1024" NETWORK="yes" NIC=[NETWORK="192net",NETWORK_UNAME="oneadmin"] \\ OS=[ARCH="x86_64"] SUNSTONE_CAPACITY_SELECT="YES" SUNSTONE_NETWORK_SELECT="YES" \\ VCPU="1"' - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The template_allocate function must be called with -f or --function.' + "The template_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - path = kwargs.get('path', None) - data = kwargs.get('data', None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The template_allocate function requires either \'data\' or a file ' - '\'path\' to be provided.' + "The template_allocate function requires either 'data' or a file " + "'path' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.template.allocate(auth, data) ret = { - 'action': 'template.allocate', - 'allocated': response[0], - 'template_id': response[1], - 'error_code': response[2], + "action": "template.allocate", + "allocated": response[0], + "template_id": response[1], + "error_code": response[2], } return ret def template_clone(call=None, kwargs=None): - ''' + """ Clones an existing virtual machine template. .. versionadded:: 2016.3.0 @@ -2378,57 +2354,57 @@ def template_clone(call=None, kwargs=None): salt-cloud -f template_clone opennebula name=my-new-template template_id=0 salt-cloud -f template_clone opennebula name=my-new-template template_name=my-template - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The template_clone function must be called with -f or --function.' + "The template_clone function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - template_id = kwargs.get('template_id', None) - template_name = kwargs.get('template_name', None) - clone_images = kwargs.get('clone_images', False) + name = kwargs.get("name", None) + template_id = kwargs.get("template_id", None) + template_name = kwargs.get("template_name", None) + clone_images = kwargs.get("clone_images", False) if name is None: raise SaltCloudSystemExit( - 'The template_clone function requires a name to be provided.' + "The template_clone function requires a name to be provided." ) if template_id: if template_name: log.warning( - 'Both the \'template_id\' and \'template_name\' arguments were provided. ' - '\'template_id\' will take precedence.' + "Both the 'template_id' and 'template_name' arguments were provided. " + "'template_id' will take precedence." ) elif template_name: - template_id = get_template_id(kwargs={'name': template_name}) + template_id = get_template_id(kwargs={"name": template_name}) else: raise SaltCloudSystemExit( - 'The template_clone function requires either a \'template_id\' ' - 'or a \'template_name\' to be provided.' + "The template_clone function requires either a 'template_id' " + "or a 'template_name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.template.clone(auth, int(template_id), name, clone_images) data = { - 'action': 'template.clone', - 'cloned': response[0], - 'cloned_template_id': response[1], - 'cloned_template_name': name, - 'error_code': response[2], + "action": "template.clone", + "cloned": response[0], + "cloned_template_id": response[1], + "cloned_template_name": name, + "error_code": response[2], } return data def template_delete(call=None, kwargs=None): - ''' + """ Deletes the given template from OpenNebula. Either a name or a template_id must be supplied. @@ -2446,48 +2422,48 @@ def template_delete(call=None, kwargs=None): salt-cloud -f template_delete opennebula name=my-template salt-cloud --function template_delete opennebula template_id=5 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The template_delete function must be called with -f or --function.' + "The template_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - template_id = kwargs.get('template_id', None) + name = kwargs.get("name", None) + template_id = kwargs.get("template_id", None) if template_id: if name: log.warning( - 'Both the \'template_id\' and \'name\' arguments were provided. ' - '\'template_id\' will take precedence.' + "Both the 'template_id' and 'name' arguments were provided. " + "'template_id' will take precedence." ) elif name: - template_id = get_template_id(kwargs={'name': name}) + template_id = get_template_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( - 'The template_delete function requires either a \'name\' or a \'template_id\' ' - 'to be provided.' + "The template_delete function requires either a 'name' or a 'template_id' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.template.delete(auth, int(template_id)) data = { - 'action': 'template.delete', - 'deleted': response[0], - 'template_id': response[1], - 'error_code': response[2], + "action": "template.delete", + "deleted": response[0], + "template_id": response[1], + "error_code": response[2], } return data def template_instantiate(call=None, kwargs=None): - ''' + """ Instantiates a new virtual machine from a template. .. versionadded:: 2016.3.0 @@ -2514,55 +2490,55 @@ def template_instantiate(call=None, kwargs=None): salt-cloud -f template_instantiate opennebula vm_name=my-new-vm template_id=0 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The template_instantiate function must be called with -f or --function.' + "The template_instantiate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - vm_name = kwargs.get('vm_name', None) - template_id = kwargs.get('template_id', None) - template_name = kwargs.get('template_name', None) + vm_name = kwargs.get("vm_name", None) + template_id = kwargs.get("template_id", None) + template_name = kwargs.get("template_name", None) if vm_name is None: raise SaltCloudSystemExit( - 'The template_instantiate function requires a \'vm_name\' to be provided.' + "The template_instantiate function requires a 'vm_name' to be provided." ) if template_id: if template_name: log.warning( - 'Both the \'template_id\' and \'template_name\' arguments were provided. ' - '\'template_id\' will take precedence.' + "Both the 'template_id' and 'template_name' arguments were provided. " + "'template_id' will take precedence." ) elif template_name: - template_id = get_template_id(kwargs={'name': template_name}) + template_id = get_template_id(kwargs={"name": template_name}) else: raise SaltCloudSystemExit( - 'The template_instantiate function requires either a \'template_id\' ' - 'or a \'template_name\' to be provided.' + "The template_instantiate function requires either a 'template_id' " + "or a 'template_name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.template.instantiate(auth, int(template_id), vm_name) data = { - 'action': 'template.instantiate', - 'instantiated': response[0], - 'instantiated_vm_id': response[1], - 'vm_name': vm_name, - 'error_code': response[2], + "action": "template.instantiate", + "instantiated": response[0], + "instantiated_vm_id": response[1], + "vm_name": vm_name, + "error_code": response[2], } return data def template_update(call=None, kwargs=None): - ''' + """ Replaces the template contents. .. versionadded:: 2016.3.0 @@ -2597,25 +2573,25 @@ def template_update(call=None, kwargs=None): MEMORY="1024" NETWORK="yes" NIC=[NETWORK="192net",NETWORK_UNAME="oneadmin"] \\ OS=[ARCH="x86_64"] SUNSTONE_CAPACITY_SELECT="YES" SUNSTONE_NETWORK_SELECT="YES" \\ VCPU="1"' - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The template_update function must be called with -f or --function.' + "The template_update function must be called with -f or --function." ) if kwargs is None: kwargs = {} - template_id = kwargs.get('template_id', None) - template_name = kwargs.get('template_name', None) - path = kwargs.get('path', None) - data = kwargs.get('data', None) - update_type = kwargs.get('update_type', None) - update_args = ['replace', 'merge'] + template_id = kwargs.get("template_id", None) + template_name = kwargs.get("template_name", None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) + update_type = kwargs.get("update_type", None) + update_args = ["replace", "merge"] if update_type is None: raise SaltCloudSystemExit( - 'The template_update function requires an \'update_type\' to be provided.' + "The template_update function requires an 'update_type' to be provided." ) if update_type == update_args[0]: @@ -2624,57 +2600,58 @@ def template_update(call=None, kwargs=None): update_number = 1 else: raise SaltCloudSystemExit( - 'The update_type argument must be either {0} or {1}.'.format( - update_args[0], - update_args[1] + "The update_type argument must be either {0} or {1}.".format( + update_args[0], update_args[1] ) ) if template_id: if template_name: log.warning( - 'Both the \'template_id\' and \'template_name\' arguments were provided. ' - '\'template_id\' will take precedence.' + "Both the 'template_id' and 'template_name' arguments were provided. " + "'template_id' will take precedence." ) elif template_name: - template_id = get_template_id(kwargs={'name': template_name}) + template_id = get_template_id(kwargs={"name": template_name}) else: raise SaltCloudSystemExit( - 'The template_update function requires either a \'template_id\' ' - 'or a \'template_name\' to be provided.' + "The template_update function requires either a 'template_id' " + "or a 'template_name' to be provided." ) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The template_update function requires either \'data\' or a file ' - '\'path\' to be provided.' + "The template_update function requires either 'data' or a file " + "'path' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - response = server.one.template.update(auth, int(template_id), data, int(update_number)) + auth = ":".join([user, password]) + response = server.one.template.update( + auth, int(template_id), data, int(update_number) + ) ret = { - 'action': 'template.update', - 'updated': response[0], - 'template_id': response[1], - 'error_code': response[2], + "action": "template.update", + "updated": response[0], + "template_id": response[1], + "error_code": response[2], } return ret def vm_action(name, kwargs=None, call=None): - ''' + """ Submits an action to be performed on a given virtual machine. .. versionadded:: 2016.3.0 @@ -2708,38 +2685,38 @@ def vm_action(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_action my-vm action='release' - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_action function must be called with -a or --action.' + "The vm_action function must be called with -a or --action." ) if kwargs is None: kwargs = {} - action = kwargs.get('action', None) + action = kwargs.get("action", None) if action is None: raise SaltCloudSystemExit( - 'The vm_action function must have an \'action\' provided.' + "The vm_action function must have an 'action' provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.action(auth, action, vm_id) data = { - 'action': 'vm.action.' + six.text_type(action), - 'actioned': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.action." + six.text_type(action), + "actioned": response[0], + "vm_id": response[1], + "error_code": response[2], } return data def vm_allocate(call=None, kwargs=None): - ''' + """ Allocates a new virtual machine in OpenNebula. .. versionadded:: 2016.3.0 @@ -2764,50 +2741,50 @@ def vm_allocate(call=None, kwargs=None): salt-cloud -f vm_allocate path=/path/to/vm_template.txt salt-cloud --function vm_allocate path=/path/to/vm_template.txt hold=True - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The vm_allocate function must be called with -f or --function.' + "The vm_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - path = kwargs.get('path', None) - data = kwargs.get('data', None) - hold = kwargs.get('hold', False) + path = kwargs.get("path", None) + data = kwargs.get("data", None) + hold = kwargs.get("hold", False) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vm_allocate function requires either \'data\' or a file \'path\' ' - 'to be provided.' + "The vm_allocate function requires either 'data' or a file 'path' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.vm.allocate(auth, data, salt.utils.data.is_true(hold)) ret = { - 'action': 'vm.allocate', - 'allocated': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.allocate", + "allocated": response[0], + "vm_id": response[1], + "error_code": response[2], } return ret def vm_attach(name, kwargs=None, call=None): - ''' + """ Attaches a new disk to the given virtual machine. .. versionadded:: 2016.3.0 @@ -2831,50 +2808,50 @@ def vm_attach(name, kwargs=None, call=None): salt-cloud -a vm_attach my-vm path=/path/to/disk_file.txt salt-cloud -a vm_attach my-vm data="DISK=[DISK_ID=1]" - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_attach action must be called with -a or --action.' + "The vm_attach action must be called with -a or --action." ) if kwargs is None: kwargs = {} - path = kwargs.get('path', None) - data = kwargs.get('data', None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vm_attach function requires either \'data\' or a file ' - '\'path\' to be provided.' + "The vm_attach function requires either 'data' or a file " + "'path' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.attach(auth, vm_id, data) ret = { - 'action': 'vm.attach', - 'attached': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.attach", + "attached": response[0], + "vm_id": response[1], + "error_code": response[2], } return ret def vm_attach_nic(name, kwargs=None, call=None): - ''' + """ Attaches a new network interface to the given virtual machine. .. versionadded:: 2016.3.0 @@ -2898,50 +2875,50 @@ def vm_attach_nic(name, kwargs=None, call=None): salt-cloud -a vm_attach_nic my-vm path=/path/to/nic_file.txt salt-cloud -a vm_attach_nic my-vm data="NIC=[NETWORK_ID=1]" - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_attach_nic action must be called with -a or --action.' + "The vm_attach_nic action must be called with -a or --action." ) if kwargs is None: kwargs = {} - path = kwargs.get('path', None) - data = kwargs.get('data', None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vm_attach_nic function requires either \'data\' or a file ' - '\'path\' to be provided.' + "The vm_attach_nic function requires either 'data' or a file " + "'path' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.attachnic(auth, vm_id, data) ret = { - 'action': 'vm.attachnic', - 'nic_attached': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.attachnic", + "nic_attached": response[0], + "vm_id": response[1], + "error_code": response[2], } return ret def vm_deploy(name, kwargs=None, call=None): - ''' + """ Initiates the instance of the given VM on the target host. .. versionadded:: 2016.3.0 @@ -2980,67 +2957,69 @@ def vm_deploy(name, kwargs=None, call=None): salt-cloud -a vm_deploy my-vm host_id=1 capacity_maintained=False salt-cloud -a vm_deploy my-vm host_name=host01 datastore_id=1 salt-cloud -a vm_deploy my-vm host_name=host01 datastore_name=default - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_deploy action must be called with -a or --action.' + "The vm_deploy action must be called with -a or --action." ) if kwargs is None: kwargs = {} - host_id = kwargs.get('host_id', None) - host_name = kwargs.get('host_name', None) - capacity_maintained = kwargs.get('capacity_maintained', True) - datastore_id = kwargs.get('datastore_id', None) - datastore_name = kwargs.get('datastore_name', None) + host_id = kwargs.get("host_id", None) + host_name = kwargs.get("host_name", None) + capacity_maintained = kwargs.get("capacity_maintained", True) + datastore_id = kwargs.get("datastore_id", None) + datastore_name = kwargs.get("datastore_name", None) if host_id: if host_name: log.warning( - 'Both the \'host_id\' and \'host_name\' arguments were provided. ' - '\'host_id\' will take precedence.' + "Both the 'host_id' and 'host_name' arguments were provided. " + "'host_id' will take precedence." ) elif host_name: - host_id = get_host_id(kwargs={'name': host_name}) + host_id = get_host_id(kwargs={"name": host_name}) else: raise SaltCloudSystemExit( - 'The vm_deploy function requires a \'host_id\' or a \'host_name\' ' - 'to be provided.' + "The vm_deploy function requires a 'host_id' or a 'host_name' " + "to be provided." ) if datastore_id: if datastore_name: log.warning( - 'Both the \'datastore_id\' and \'datastore_name\' arguments were provided. ' - '\'datastore_id\' will take precedence.' + "Both the 'datastore_id' and 'datastore_name' arguments were provided. " + "'datastore_id' will take precedence." ) elif datastore_name: - datastore_id = get_datastore_id(kwargs={'name': datastore_name}) + datastore_id = get_datastore_id(kwargs={"name": datastore_name}) else: - datastore_id = '-1' + datastore_id = "-1" server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = get_vm_id(kwargs={'name': name}) - response = server.one.vm.deploy(auth, - int(vm_id), - int(host_id), - salt.utils.data.is_true(capacity_maintained), - int(datastore_id)) + auth = ":".join([user, password]) + vm_id = get_vm_id(kwargs={"name": name}) + response = server.one.vm.deploy( + auth, + int(vm_id), + int(host_id), + salt.utils.data.is_true(capacity_maintained), + int(datastore_id), + ) data = { - 'action': 'vm.deploy', - 'deployed': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.deploy", + "deployed": response[0], + "vm_id": response[1], + "error_code": response[2], } return data def vm_detach(name, kwargs=None, call=None): - ''' + """ Detaches a disk from a virtual machine. .. versionadded:: 2016.3.0 @@ -3056,38 +3035,38 @@ def vm_detach(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_detach my-vm disk_id=1 - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_detach action must be called with -a or --action.' + "The vm_detach action must be called with -a or --action." ) if kwargs is None: kwargs = {} - disk_id = kwargs.get('disk_id', None) + disk_id = kwargs.get("disk_id", None) if disk_id is None: raise SaltCloudSystemExit( - 'The vm_detach function requires a \'disk_id\' to be provided.' + "The vm_detach function requires a 'disk_id' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.detach(auth, vm_id, int(disk_id)) data = { - 'action': 'vm.detach', - 'detached': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.detach", + "detached": response[0], + "vm_id": response[1], + "error_code": response[2], } return data def vm_detach_nic(name, kwargs=None, call=None): - ''' + """ Detaches a disk from a virtual machine. .. versionadded:: 2016.3.0 @@ -3103,38 +3082,38 @@ def vm_detach_nic(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_detach_nic my-vm nic_id=1 - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_detach_nic action must be called with -a or --action.' + "The vm_detach_nic action must be called with -a or --action." ) if kwargs is None: kwargs = {} - nic_id = kwargs.get('nic_id', None) + nic_id = kwargs.get("nic_id", None) if nic_id is None: raise SaltCloudSystemExit( - 'The vm_detach_nic function requires a \'nic_id\' to be provided.' + "The vm_detach_nic function requires a 'nic_id' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.detachnic(auth, vm_id, int(nic_id)) data = { - 'action': 'vm.detachnic', - 'nic_detached': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.detachnic", + "nic_detached": response[0], + "vm_id": response[1], + "error_code": response[2], } return data def vm_disk_save(name, kwargs=None, call=None): - ''' + """ Sets the disk to be saved in the given image. .. versionadded:: 2016.3.0 @@ -3163,48 +3142,45 @@ def vm_disk_save(name, kwargs=None, call=None): salt-cloud -a vm_disk_save my-vm disk_id=1 image_name=my-new-image salt-cloud -a vm_disk_save my-vm disk_id=1 image_name=my-new-image image_type=CONTEXT snapshot_id=10 - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_disk_save action must be called with -a or --action.' + "The vm_disk_save action must be called with -a or --action." ) if kwargs is None: kwargs = {} - disk_id = kwargs.get('disk_id', None) - image_name = kwargs.get('image_name', None) - image_type = kwargs.get('image_type', '') - snapshot_id = int(kwargs.get('snapshot_id', '-1')) + disk_id = kwargs.get("disk_id", None) + image_name = kwargs.get("image_name", None) + image_type = kwargs.get("image_type", "") + snapshot_id = int(kwargs.get("snapshot_id", "-1")) if disk_id is None or image_name is None: raise SaltCloudSystemExit( - 'The vm_disk_save function requires a \'disk_id\' and an \'image_name\' ' - 'to be provided.' + "The vm_disk_save function requires a 'disk_id' and an 'image_name' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.disksave(auth, - vm_id, - int(disk_id), - image_name, - image_type, - snapshot_id) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) + response = server.one.vm.disksave( + auth, vm_id, int(disk_id), image_name, image_type, snapshot_id + ) data = { - 'action': 'vm.disksave', - 'saved': response[0], - 'image_id': response[1], - 'error_code': response[2], + "action": "vm.disksave", + "saved": response[0], + "image_id": response[1], + "error_code": response[2], } return data def vm_disk_snapshot_create(name, kwargs=None, call=None): - ''' + """ Takes a new snapshot of the disk image. .. versionadded:: 2016.3.0 @@ -3223,44 +3199,41 @@ def vm_disk_snapshot_create(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_disk_snapshot_create my-vm disk_id=0 description="My Snapshot Description" - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_disk_snapshot_create action must be called with -a or --action.' + "The vm_disk_snapshot_create action must be called with -a or --action." ) if kwargs is None: kwargs = {} - disk_id = kwargs.get('disk_id', None) - description = kwargs.get('description', None) + disk_id = kwargs.get("disk_id", None) + description = kwargs.get("description", None) if disk_id is None or description is None: raise SaltCloudSystemExit( - 'The vm_disk_snapshot_create function requires a \'disk_id\' and a \'description\' ' - 'to be provided.' + "The vm_disk_snapshot_create function requires a 'disk_id' and a 'description' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.disksnapshotcreate(auth, - vm_id, - int(disk_id), - description) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) + response = server.one.vm.disksnapshotcreate(auth, vm_id, int(disk_id), description) data = { - 'action': 'vm.disksnapshotcreate', - 'created': response[0], - 'snapshot_id': response[1], - 'error_code': response[2], + "action": "vm.disksnapshotcreate", + "created": response[0], + "snapshot_id": response[1], + "error_code": response[2], } return data def vm_disk_snapshot_delete(name, kwargs=None, call=None): - ''' + """ Deletes a disk snapshot based on the given VM and the disk_id. .. versionadded:: 2016.3.0 @@ -3279,44 +3252,43 @@ def vm_disk_snapshot_delete(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_disk_snapshot_delete my-vm disk_id=0 snapshot_id=6 - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_disk_snapshot_delete action must be called with -a or --action.' + "The vm_disk_snapshot_delete action must be called with -a or --action." ) if kwargs is None: kwargs = {} - disk_id = kwargs.get('disk_id', None) - snapshot_id = kwargs.get('snapshot_id', None) + disk_id = kwargs.get("disk_id", None) + snapshot_id = kwargs.get("snapshot_id", None) if disk_id is None or snapshot_id is None: raise SaltCloudSystemExit( - 'The vm_disk_snapshot_create function requires a \'disk_id\' and a \'snapshot_id\' ' - 'to be provided.' + "The vm_disk_snapshot_create function requires a 'disk_id' and a 'snapshot_id' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.disksnapshotdelete(auth, - vm_id, - int(disk_id), - int(snapshot_id)) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) + response = server.one.vm.disksnapshotdelete( + auth, vm_id, int(disk_id), int(snapshot_id) + ) data = { - 'action': 'vm.disksnapshotdelete', - 'deleted': response[0], - 'snapshot_id': response[1], - 'error_code': response[2], + "action": "vm.disksnapshotdelete", + "deleted": response[0], + "snapshot_id": response[1], + "error_code": response[2], } return data def vm_disk_snapshot_revert(name, kwargs=None, call=None): - ''' + """ Reverts a disk state to a previously taken snapshot. .. versionadded:: 2016.3.0 @@ -3335,44 +3307,43 @@ def vm_disk_snapshot_revert(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_disk_snapshot_revert my-vm disk_id=0 snapshot_id=6 - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_disk_snapshot_revert action must be called with -a or --action.' + "The vm_disk_snapshot_revert action must be called with -a or --action." ) if kwargs is None: kwargs = {} - disk_id = kwargs.get('disk_id', None) - snapshot_id = kwargs.get('snapshot_id', None) + disk_id = kwargs.get("disk_id", None) + snapshot_id = kwargs.get("snapshot_id", None) if disk_id is None or snapshot_id is None: raise SaltCloudSystemExit( - 'The vm_disk_snapshot_revert function requires a \'disk_id\' and a \'snapshot_id\' ' - 'to be provided.' + "The vm_disk_snapshot_revert function requires a 'disk_id' and a 'snapshot_id' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.disksnapshotrevert(auth, - vm_id, - int(disk_id), - int(snapshot_id)) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) + response = server.one.vm.disksnapshotrevert( + auth, vm_id, int(disk_id), int(snapshot_id) + ) data = { - 'action': 'vm.disksnapshotrevert', - 'deleted': response[0], - 'snapshot_id': response[1], - 'error_code': response[2], + "action": "vm.disksnapshotrevert", + "deleted": response[0], + "snapshot_id": response[1], + "error_code": response[2], } return data def vm_info(name, call=None): - ''' + """ Retrieves information for a given virtual machine. A VM name must be supplied. .. versionadded:: 2016.3.0 @@ -3385,15 +3356,15 @@ def vm_info(name, call=None): .. code-block:: bash salt-cloud -a vm_info my-vm - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_info action must be called with -a or --action.' + "The vm_info action must be called with -a or --action." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.info(auth, vm_id) if response[0] is False: @@ -3401,12 +3372,12 @@ def vm_info(name, call=None): else: info = {} tree = _get_xml(response[1]) - info[tree.find('NAME').text] = _xml_to_dict(tree) + info[tree.find("NAME").text] = _xml_to_dict(tree) return info def vm_migrate(name, kwargs=None, call=None): - ''' + """ Migrates the specified virtual machine to the specified target host. .. versionadded:: 2016.3.0 @@ -3445,72 +3416,74 @@ def vm_migrate(name, kwargs=None, call=None): salt-cloud -a vm_migrate my-vm host_id=0 datastore_id=1 salt-cloud -a vm_migrate my-vm host_id=0 datastore_id=1 live_migration=True salt-cloud -a vm_migrate my-vm host_name=host01 datastore_name=default - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_migrate action must be called with -a or --action.' + "The vm_migrate action must be called with -a or --action." ) if kwargs is None: kwargs = {} - host_id = kwargs.get('host_id', None) - host_name = kwargs.get('host_name', None) - live_migration = kwargs.get('live_migration', False) - capacity_maintained = kwargs.get('capacity_maintained', True) - datastore_id = kwargs.get('datastore_id', None) - datastore_name = kwargs.get('datastore_name', None) + host_id = kwargs.get("host_id", None) + host_name = kwargs.get("host_name", None) + live_migration = kwargs.get("live_migration", False) + capacity_maintained = kwargs.get("capacity_maintained", True) + datastore_id = kwargs.get("datastore_id", None) + datastore_name = kwargs.get("datastore_name", None) if datastore_id: if datastore_name: log.warning( - 'Both the \'datastore_id\' and \'datastore_name\' arguments were provided. ' - '\'datastore_id\' will take precedence.' + "Both the 'datastore_id' and 'datastore_name' arguments were provided. " + "'datastore_id' will take precedence." ) elif datastore_name: - datastore_id = get_datastore_id(kwargs={'name': datastore_name}) + datastore_id = get_datastore_id(kwargs={"name": datastore_name}) else: raise SaltCloudSystemExit( - 'The vm_migrate function requires either a \'datastore_id\' or a ' - '\'datastore_name\' to be provided.' + "The vm_migrate function requires either a 'datastore_id' or a " + "'datastore_name' to be provided." ) if host_id: if host_name: log.warning( - 'Both the \'host_id\' and \'host_name\' arguments were provided. ' - '\'host_id\' will take precedence.' + "Both the 'host_id' and 'host_name' arguments were provided. " + "'host_id' will take precedence." ) elif host_name: - host_id = get_host_id(kwargs={'name': host_name}) + host_id = get_host_id(kwargs={"name": host_name}) else: raise SaltCloudSystemExit( - 'The vm_migrate function requires either a \'host_id\' ' - 'or a \'host_name\' to be provided.' + "The vm_migrate function requires either a 'host_id' " + "or a 'host_name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.migrate(auth, - vm_id, - int(host_id), - salt.utils.data.is_true(live_migration), - salt.utils.data.is_true(capacity_maintained), - int(datastore_id)) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) + response = server.one.vm.migrate( + auth, + vm_id, + int(host_id), + salt.utils.data.is_true(live_migration), + salt.utils.data.is_true(capacity_maintained), + int(datastore_id), + ) data = { - 'action': 'vm.migrate', - 'migrated': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.migrate", + "migrated": response[0], + "vm_id": response[1], + "error_code": response[2], } return data def vm_monitoring(name, call=None): - ''' + """ Returns the monitoring records for a given virtual machine. A VM name must be supplied. @@ -3528,32 +3501,32 @@ def vm_monitoring(name, call=None): .. code-block:: bash salt-cloud -a vm_monitoring my-vm - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_monitoring action must be called with -a or --action.' + "The vm_monitoring action must be called with -a or --action." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.monitoring(auth, vm_id) if response[0] is False: log.error( - 'There was an error retrieving the specified VM\'s monitoring ' - 'information.' + "There was an error retrieving the specified VM's monitoring " + "information." ) return {} else: info = {} for vm_ in _get_xml(response[1]): - info[vm_.find('ID').text] = _xml_to_dict(vm_) + info[vm_.find("ID").text] = _xml_to_dict(vm_) return info def vm_resize(name, kwargs=None, call=None): - ''' + """ Changes the capacity of the virtual machine. .. versionadded:: 2016.3.0 @@ -3584,51 +3557,53 @@ def vm_resize(name, kwargs=None, call=None): salt-cloud -a vm_resize my-vm path=/path/to/capacity_template.txt salt-cloud -a vm_resize my-vm path=/path/to/capacity_template.txt capacity_maintained=False salt-cloud -a vm_resize my-vm data="CPU=1 VCPU=1 MEMORY=1024" - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_resize action must be called with -a or --action.' + "The vm_resize action must be called with -a or --action." ) if kwargs is None: kwargs = {} - path = kwargs.get('path', None) - data = kwargs.get('data', None) - capacity_maintained = kwargs.get('capacity_maintained', True) + path = kwargs.get("path", None) + data = kwargs.get("data", None) + capacity_maintained = kwargs.get("capacity_maintained", True) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vm_resize function requires either \'data\' or a file \'path\' ' - 'to be provided.' + "The vm_resize function requires either 'data' or a file 'path' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) - response = server.one.vm.resize(auth, vm_id, data, salt.utils.data.is_true(capacity_maintained)) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) + response = server.one.vm.resize( + auth, vm_id, data, salt.utils.data.is_true(capacity_maintained) + ) ret = { - 'action': 'vm.resize', - 'resized': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.resize", + "resized": response[0], + "vm_id": response[1], + "error_code": response[2], } return ret def vm_snapshot_create(vm_name, kwargs=None, call=None): - ''' + """ Creates a new virtual machine snapshot from the provided VM. .. versionadded:: 2016.3.0 @@ -3644,38 +3619,38 @@ def vm_snapshot_create(vm_name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_snapshot_create my-vm snapshot_name=my-new-snapshot - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_snapshot_create action must be called with -a or --action.' + "The vm_snapshot_create action must be called with -a or --action." ) if kwargs is None: kwargs = {} - snapshot_name = kwargs.get('snapshot_name', None) + snapshot_name = kwargs.get("snapshot_name", None) if snapshot_name is None: raise SaltCloudSystemExit( - 'The vm_snapshot_create function requires a \'snapshot_name\' to be provided.' + "The vm_snapshot_create function requires a 'snapshot_name' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': vm_name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": vm_name})) response = server.one.vm.snapshotcreate(auth, vm_id, snapshot_name) data = { - 'action': 'vm.snapshotcreate', - 'snapshot_created': response[0], - 'snapshot_id': response[1], - 'error_code': response[2], + "action": "vm.snapshotcreate", + "snapshot_created": response[0], + "snapshot_id": response[1], + "error_code": response[2], } return data def vm_snapshot_delete(vm_name, kwargs=None, call=None): - ''' + """ Deletes a virtual machine snapshot from the provided VM. .. versionadded:: 2016.3.0 @@ -3691,38 +3666,38 @@ def vm_snapshot_delete(vm_name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_snapshot_delete my-vm snapshot_id=8 - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_snapshot_delete action must be called with -a or --action.' + "The vm_snapshot_delete action must be called with -a or --action." ) if kwargs is None: kwargs = {} - snapshot_id = kwargs.get('snapshot_id', None) + snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( - 'The vm_snapshot_delete function requires a \'snapshot_id\' to be provided.' + "The vm_snapshot_delete function requires a 'snapshot_id' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': vm_name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": vm_name})) response = server.one.vm.snapshotdelete(auth, vm_id, int(snapshot_id)) data = { - 'action': 'vm.snapshotdelete', - 'snapshot_deleted': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.snapshotdelete", + "snapshot_deleted": response[0], + "vm_id": response[1], + "error_code": response[2], } return data def vm_snapshot_revert(vm_name, kwargs=None, call=None): - ''' + """ Reverts a virtual machine to a snapshot .. versionadded:: 2016.3.0 @@ -3738,38 +3713,38 @@ def vm_snapshot_revert(vm_name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_snapshot_revert my-vm snapshot_id=42 - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_snapshot_revert action must be called with -a or --action.' + "The vm_snapshot_revert action must be called with -a or --action." ) if kwargs is None: kwargs = {} - snapshot_id = kwargs.get('snapshot_id', None) + snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( - 'The vm_snapshot_revert function requires a \'snapshot_id\' to be provided.' + "The vm_snapshot_revert function requires a 'snapshot_id' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': vm_name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": vm_name})) response = server.one.vm.snapshotrevert(auth, vm_id, int(snapshot_id)) data = { - 'action': 'vm.snapshotrevert', - 'snapshot_reverted': response[0], - 'vm_id': response[1], - 'error_code': response[2], + "action": "vm.snapshotrevert", + "snapshot_reverted": response[0], + "vm_id": response[1], + "error_code": response[2], } return data def vm_update(name, kwargs=None, call=None): - ''' + """ Replaces the user template contents. .. versionadded:: 2016.3.0 @@ -3794,23 +3769,23 @@ def vm_update(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a vm_update my-vm path=/path/to/user_template_file.txt update_type='replace' - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The vm_update action must be called with -a or --action.' + "The vm_update action must be called with -a or --action." ) if kwargs is None: kwargs = {} - path = kwargs.get('path', None) - data = kwargs.get('data', None) - update_type = kwargs.get('update_type', None) - update_args = ['replace', 'merge'] + path = kwargs.get("path", None) + data = kwargs.get("data", None) + update_type = kwargs.get("update_type", None) + update_args = ["replace", "merge"] if update_type is None: raise SaltCloudSystemExit( - 'The vm_update function requires an \'update_type\' to be provided.' + "The vm_update function requires an 'update_type' to be provided." ) if update_type == update_args[0]: @@ -3819,44 +3794,43 @@ def vm_update(name, kwargs=None, call=None): update_number = 1 else: raise SaltCloudSystemExit( - 'The update_type argument must be either {0} or {1}.'.format( - update_args[0], - update_args[1] + "The update_type argument must be either {0} or {1}.".format( + update_args[0], update_args[1] ) ) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vm_update function requires either \'data\' or a file \'path\' ' - 'to be provided.' + "The vm_update function requires either 'data' or a file 'path' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) - vm_id = int(get_vm_id(kwargs={'name': name})) + auth = ":".join([user, password]) + vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.update(auth, vm_id, data, int(update_number)) ret = { - 'action': 'vm.update', - 'updated': response[0], - 'resource_id': response[1], - 'error_code': response[2], + "action": "vm.update", + "updated": response[0], + "resource_id": response[1], + "error_code": response[2], } return ret def vn_add_ar(call=None, kwargs=None): - ''' + """ Adds address ranges to a given virtual network. .. versionadded:: 2016.3.0 @@ -3885,65 +3859,65 @@ def vn_add_ar(call=None, kwargs=None): salt-cloud -f vn_add_ar opennebula vn_id=3 path=/path/to/address_range.txt salt-cloud -f vn_add_ar opennebula vn_name=my-vn \\ data="AR=[TYPE=IP4, IP=192.168.0.5, SIZE=10]" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The vn_add_ar function must be called with -f or --function.' + "The vn_add_ar function must be called with -f or --function." ) if kwargs is None: kwargs = {} - vn_id = kwargs.get('vn_id', None) - vn_name = kwargs.get('vn_name', None) - path = kwargs.get('path', None) - data = kwargs.get('data', None) + vn_id = kwargs.get("vn_id", None) + vn_name = kwargs.get("vn_name", None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) if vn_id: if vn_name: log.warning( - 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' - '\'vn_id\' will take precedence.' + "Both the 'vn_id' and 'vn_name' arguments were provided. " + "'vn_id' will take precedence." ) elif vn_name: - vn_id = get_vn_id(kwargs={'name': vn_name}) + vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( - 'The vn_add_ar function requires a \'vn_id\' and a \'vn_name\' to ' - 'be provided.' + "The vn_add_ar function requires a 'vn_id' and a 'vn_name' to " + "be provided." ) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vn_add_ar function requires either \'data\' or a file \'path\' ' - 'to be provided.' + "The vn_add_ar function requires either 'data' or a file 'path' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.vn.add_ar(auth, int(vn_id), data) ret = { - 'action': 'vn.add_ar', - 'address_range_added': response[0], - 'resource_id': response[1], - 'error_code': response[2], + "action": "vn.add_ar", + "address_range_added": response[0], + "resource_id": response[1], + "error_code": response[2], } return ret def vn_allocate(call=None, kwargs=None): - ''' + """ Allocates a new virtual network in OpenNebula. .. versionadded:: 2016.3.0 @@ -3972,62 +3946,62 @@ def vn_allocate(call=None, kwargs=None): .. code-block:: bash salt-cloud -f vn_allocate opennebula path=/path/to/vn_file.txt - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The vn_allocate function must be called with -f or --function.' + "The vn_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} - cluster_id = kwargs.get('cluster_id', None) - cluster_name = kwargs.get('cluster_name', None) - path = kwargs.get('path', None) - data = kwargs.get('data', None) + cluster_id = kwargs.get("cluster_id", None) + cluster_name = kwargs.get("cluster_name", None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vn_allocate function requires either \'data\' or a file \'path\' ' - 'to be provided.' + "The vn_allocate function requires either 'data' or a file 'path' " + "to be provided." ) if cluster_id: if cluster_name: log.warning( - 'Both the \'cluster_id\' and \'cluster_name\' arguments were provided. ' - '\'cluster_id\' will take precedence.' - ) + "Both the 'cluster_id' and 'cluster_name' arguments were provided. " + "'cluster_id' will take precedence." + ) elif cluster_name: - cluster_id = get_cluster_id(kwargs={'name': cluster_name}) + cluster_id = get_cluster_id(kwargs={"name": cluster_name}) else: - cluster_id = '-1' + cluster_id = "-1" server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.vn.allocate(auth, data, int(cluster_id)) ret = { - 'action': 'vn.allocate', - 'allocated': response[0], - 'vn_id': response[1], - 'error_code': response[2], + "action": "vn.allocate", + "allocated": response[0], + "vn_id": response[1], + "error_code": response[2], } return ret def vn_delete(call=None, kwargs=None): - ''' + """ Deletes the given virtual network from OpenNebula. Either a name or a vn_id must be supplied. @@ -4045,48 +4019,47 @@ def vn_delete(call=None, kwargs=None): salt-cloud -f vn_delete opennebula name=my-virtual-network salt-cloud --function vn_delete opennebula vn_id=3 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The vn_delete function must be called with -f or --function.' + "The vn_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - vn_id = kwargs.get('vn_id', None) + name = kwargs.get("name", None) + vn_id = kwargs.get("vn_id", None) if vn_id: if name: log.warning( - 'Both the \'vn_id\' and \'name\' arguments were provided. ' - '\'vn_id\' will take precedence.' + "Both the 'vn_id' and 'name' arguments were provided. " + "'vn_id' will take precedence." ) elif name: - vn_id = get_vn_id(kwargs={'name': name}) + vn_id = get_vn_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( - 'The vn_delete function requires a \'name\' or a \'vn_id\' ' - 'to be provided.' + "The vn_delete function requires a 'name' or a 'vn_id' " "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.vn.delete(auth, int(vn_id)) data = { - 'action': 'vn.delete', - 'deleted': response[0], - 'vn_id': response[1], - 'error_code': response[2], + "action": "vn.delete", + "deleted": response[0], + "vn_id": response[1], + "error_code": response[2], } return data def vn_free_ar(call=None, kwargs=None): - ''' + """ Frees a reserved address range from a virtual network. .. versionadded:: 2016.3.0 @@ -4108,54 +4081,54 @@ def vn_free_ar(call=None, kwargs=None): salt-cloud -f vn_free_ar opennebula vn_id=3 ar_id=1 salt-cloud -f vn_free_ar opennebula vn_name=my-vn ar_id=1 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The vn_free_ar function must be called with -f or --function.' + "The vn_free_ar function must be called with -f or --function." ) if kwargs is None: kwargs = {} - vn_id = kwargs.get('vn_id', None) - vn_name = kwargs.get('vn_name', None) - ar_id = kwargs.get('ar_id', None) + vn_id = kwargs.get("vn_id", None) + vn_name = kwargs.get("vn_name", None) + ar_id = kwargs.get("ar_id", None) if ar_id is None: raise SaltCloudSystemExit( - 'The vn_free_ar function requires an \'rn_id\' to be provided.' + "The vn_free_ar function requires an 'rn_id' to be provided." ) if vn_id: if vn_name: log.warning( - 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' - '\'vn_id\' will take precedence.' + "Both the 'vn_id' and 'vn_name' arguments were provided. " + "'vn_id' will take precedence." ) elif vn_name: - vn_id = get_vn_id(kwargs={'name': vn_name}) + vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( - 'The vn_free_ar function requires a \'vn_id\' or a \'vn_name\' to ' - 'be provided.' + "The vn_free_ar function requires a 'vn_id' or a 'vn_name' to " + "be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.vn.free_ar(auth, int(vn_id), int(ar_id)) data = { - 'action': 'vn.free_ar', - 'ar_freed': response[0], - 'resource_id': response[1], - 'error_code': response[2], + "action": "vn.free_ar", + "ar_freed": response[0], + "resource_id": response[1], + "error_code": response[2], } return data def vn_hold(call=None, kwargs=None): - ''' + """ Holds a virtual network lease as used. .. versionadded:: 2016.3.0 @@ -4183,65 +4156,63 @@ def vn_hold(call=None, kwargs=None): salt-cloud -f vn_hold opennebula vn_id=3 path=/path/to/vn_hold_file.txt salt-cloud -f vn_hold opennebula vn_name=my-vn data="LEASES=[IP=192.168.0.5]" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The vn_hold function must be called with -f or --function.' + "The vn_hold function must be called with -f or --function." ) if kwargs is None: kwargs = {} - vn_id = kwargs.get('vn_id', None) - vn_name = kwargs.get('vn_name', None) - path = kwargs.get('path', None) - data = kwargs.get('data', None) + vn_id = kwargs.get("vn_id", None) + vn_name = kwargs.get("vn_name", None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) if vn_id: if vn_name: log.warning( - 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' - '\'vn_id\' will take precedence.' + "Both the 'vn_id' and 'vn_name' arguments were provided. " + "'vn_id' will take precedence." ) elif vn_name: - vn_id = get_vn_id(kwargs={'name': vn_name}) + vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( - 'The vn_hold function requires a \'vn_id\' or a \'vn_name\' to ' - 'be provided.' + "The vn_hold function requires a 'vn_id' or a 'vn_name' to " "be provided." ) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vn_hold function requires either \'data\' or a \'path\' to ' - 'be provided.' + "The vn_hold function requires either 'data' or a 'path' to " "be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.vn.hold(auth, int(vn_id), data) ret = { - 'action': 'vn.hold', - 'held': response[0], - 'resource_id': response[1], - 'error_code': response[2], + "action": "vn.hold", + "held": response[0], + "resource_id": response[1], + "error_code": response[2], } return ret def vn_info(call=None, kwargs=None): - ''' + """ Retrieves information for the virtual network. .. versionadded:: 2016.3.0 @@ -4260,34 +4231,34 @@ def vn_info(call=None, kwargs=None): salt-cloud -f vn_info opennebula vn_id=3 salt-cloud --function vn_info opennebula name=public - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The vn_info function must be called with -f or --function.' + "The vn_info function must be called with -f or --function." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) - vn_id = kwargs.get('vn_id', None) + name = kwargs.get("name", None) + vn_id = kwargs.get("vn_id", None) if vn_id: if name: log.warning( - 'Both the \'vn_id\' and \'name\' arguments were provided. ' - '\'vn_id\' will take precedence.' + "Both the 'vn_id' and 'name' arguments were provided. " + "'vn_id' will take precedence." ) elif name: - vn_id = get_vn_id(kwargs={'name': name}) + vn_id = get_vn_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( - 'The vn_info function requires either a \'name\' or a \'vn_id\' ' - 'to be provided.' + "The vn_info function requires either a 'name' or a 'vn_id' " + "to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.vn.info(auth, int(vn_id)) if response[0] is False: @@ -4295,12 +4266,12 @@ def vn_info(call=None, kwargs=None): else: info = {} tree = _get_xml(response[1]) - info[tree.find('NAME').text] = _xml_to_dict(tree) + info[tree.find("NAME").text] = _xml_to_dict(tree) return info def vn_release(call=None, kwargs=None): - ''' + """ Releases a virtual network lease that was previously on hold. .. versionadded:: 2016.3.0 @@ -4328,65 +4299,65 @@ def vn_release(call=None, kwargs=None): salt-cloud -f vn_release opennebula vn_id=3 path=/path/to/vn_release_file.txt salt-cloud =f vn_release opennebula vn_name=my-vn data="LEASES=[IP=192.168.0.5]" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The vn_reserve function must be called with -f or --function.' + "The vn_reserve function must be called with -f or --function." ) if kwargs is None: kwargs = {} - vn_id = kwargs.get('vn_id', None) - vn_name = kwargs.get('vn_name', None) - path = kwargs.get('path', None) - data = kwargs.get('data', None) + vn_id = kwargs.get("vn_id", None) + vn_name = kwargs.get("vn_name", None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) if vn_id: if vn_name: log.warning( - 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' - '\'vn_id\' will take precedence.' + "Both the 'vn_id' and 'vn_name' arguments were provided. " + "'vn_id' will take precedence." ) elif vn_name: - vn_id = get_vn_id(kwargs={'name': vn_name}) + vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( - 'The vn_release function requires a \'vn_id\' or a \'vn_name\' to ' - 'be provided.' + "The vn_release function requires a 'vn_id' or a 'vn_name' to " + "be provided." ) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vn_release function requires either \'data\' or a \'path\' to ' - 'be provided.' + "The vn_release function requires either 'data' or a 'path' to " + "be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.vn.release(auth, int(vn_id), data) ret = { - 'action': 'vn.release', - 'released': response[0], - 'resource_id': response[1], - 'error_code': response[2], + "action": "vn.release", + "released": response[0], + "resource_id": response[1], + "error_code": response[2], } return ret def vn_reserve(call=None, kwargs=None): - ''' + """ Reserve network addresses. .. versionadded:: 2016.3.0 @@ -4415,57 +4386,57 @@ def vn_reserve(call=None, kwargs=None): salt-cloud -f vn_reserve opennebula vn_id=3 path=/path/to/vn_reserve_file.txt salt-cloud -f vn_reserve opennebula vn_name=my-vn data="SIZE=10 AR_ID=8 NETWORK_ID=1" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The vn_reserve function must be called with -f or --function.' + "The vn_reserve function must be called with -f or --function." ) if kwargs is None: kwargs = {} - vn_id = kwargs.get('vn_id', None) - vn_name = kwargs.get('vn_name', None) - path = kwargs.get('path', None) - data = kwargs.get('data', None) + vn_id = kwargs.get("vn_id", None) + vn_name = kwargs.get("vn_name", None) + path = kwargs.get("path", None) + data = kwargs.get("data", None) if vn_id: if vn_name: log.warning( - 'Both the \'vn_id\' and \'vn_name\' arguments were provided. ' - '\'vn_id\' will take precedence.' + "Both the 'vn_id' and 'vn_name' arguments were provided. " + "'vn_id' will take precedence." ) elif vn_name: - vn_id = get_vn_id(kwargs={'name': vn_name}) + vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( - 'The vn_reserve function requires a \'vn_id\' or a \'vn_name\' to ' - 'be provided.' + "The vn_reserve function requires a 'vn_id' or a 'vn_name' to " + "be provided." ) if data: if path: log.warning( - 'Both the \'data\' and \'path\' arguments were provided. ' - '\'data\' will take precedence.' + "Both the 'data' and 'path' arguments were provided. " + "'data' will take precedence." ) elif path: - with salt.utils.files.fopen(path, mode='r') as rfh: + with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( - 'The vn_reserve function requires a \'path\' to be provided.' + "The vn_reserve function requires a 'path' to be provided." ) server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) response = server.one.vn.reserve(auth, int(vn_id), data) ret = { - 'action': 'vn.reserve', - 'reserved': response[0], - 'resource_id': response[1], - 'error_code': response[2], + "action": "vn.reserve", + "reserved": response[0], + "resource_id": response[1], + "error_code": response[2], } return ret @@ -4473,13 +4444,14 @@ def vn_reserve(call=None, kwargs=None): # Helper Functions + def _get_node(name): - ''' + """ Helper function that returns all information about a named node. name The name of the node for which to get information. - ''' + """ attempts = 10 while attempts >= 0: @@ -4488,8 +4460,9 @@ def _get_node(name): except KeyError: attempts -= 1 log.debug( - 'Failed to get the data for node \'%s\'. Remaining ' - 'attempts: %s', name, attempts + "Failed to get the data for node '%s'. Remaining " "attempts: %s", + name, + attempts, ) # Just a little delay between attempts... @@ -4499,9 +4472,9 @@ def _get_node(name): def _get_xml(xml_str): - ''' + """ Intrepret the data coming from opennebula and raise if it's not XML. - ''' + """ try: xml_data = etree.XML(xml_str) # XMLSyntaxError seems to be only available from lxml, but that is the xml @@ -4509,30 +4482,28 @@ def _get_xml(xml_str): except etree.XMLSyntaxError as err: # opennebula returned invalid XML, which could be an error message, so # log it - raise SaltCloudSystemExit('opennebula returned: {0}'.format(xml_str)) + raise SaltCloudSystemExit("opennebula returned: {0}".format(xml_str)) return xml_data def _get_xml_rpc(): - ''' + """ Uses the OpenNebula cloud provider configurations to connect to the OpenNebula API. Returns the server connection created as well as the user and password values from the cloud provider config file used to make the connection. - ''' + """ vm_ = get_configured_provider() xml_rpc = config.get_cloud_config_value( - 'xml_rpc', vm_, __opts__, search_global=False + "xml_rpc", vm_, __opts__, search_global=False ) - user = config.get_cloud_config_value( - 'user', vm_, __opts__, search_global=False - ) + user = config.get_cloud_config_value("user", vm_, __opts__, search_global=False) password = config.get_cloud_config_value( - 'password', vm_, __opts__, search_global=False + "password", vm_, __opts__, search_global=False ) server = salt.ext.six.moves.xmlrpc_client.ServerProxy(xml_rpc) @@ -4541,56 +4512,56 @@ def _get_xml_rpc(): def _list_nodes(full=False): - ''' + """ Helper function for the list_* query functions - Constructs the appropriate dictionaries to return from the API query. full If performing a full query, such as in list_nodes_full, change this parameter to ``True``. - ''' + """ server, user, password = _get_xml_rpc() - auth = ':'.join([user, password]) + auth = ":".join([user, password]) vm_pool = server.one.vmpool.info(auth, -2, -1, -1, -1)[1] vms = {} for vm in _get_xml(vm_pool): - name = vm.find('NAME').text + name = vm.find("NAME").text vms[name] = {} - cpu_size = vm.find('TEMPLATE').find('CPU').text - memory_size = vm.find('TEMPLATE').find('MEMORY').text + cpu_size = vm.find("TEMPLATE").find("CPU").text + memory_size = vm.find("TEMPLATE").find("MEMORY").text private_ips = [] - for nic in vm.find('TEMPLATE').findall('NIC'): + for nic in vm.find("TEMPLATE").findall("NIC"): try: - private_ips.append(nic.find('IP').text) + private_ips.append(nic.find("IP").text) except Exception: # pylint: disable=broad-except pass - vms[name]['id'] = vm.find('ID').text - if 'TEMPLATE_ID' in vm.find('TEMPLATE'): - vms[name]['image'] = vm.find('TEMPLATE').find('TEMPLATE_ID').text - vms[name]['name'] = name - vms[name]['size'] = {'cpu': cpu_size, 'memory': memory_size} - vms[name]['state'] = vm.find('STATE').text - vms[name]['private_ips'] = private_ips - vms[name]['public_ips'] = [] + vms[name]["id"] = vm.find("ID").text + if "TEMPLATE_ID" in vm.find("TEMPLATE"): + vms[name]["image"] = vm.find("TEMPLATE").find("TEMPLATE_ID").text + vms[name]["name"] = name + vms[name]["size"] = {"cpu": cpu_size, "memory": memory_size} + vms[name]["state"] = vm.find("STATE").text + vms[name]["private_ips"] = private_ips + vms[name]["public_ips"] = [] if full: - vms[vm.find('NAME').text] = _xml_to_dict(vm) + vms[vm.find("NAME").text] = _xml_to_dict(vm) return vms def _xml_to_dict(xml): - ''' + """ Helper function to covert xml into a data dictionary. xml The xml data to convert. - ''' + """ dicts = {} for item in xml: key = item.tag.lower() diff --git a/salt/cloud/clouds/openstack.py b/salt/cloud/clouds/openstack.py index aaec6c53924..ecdf3b8e159 100644 --- a/salt/cloud/clouds/openstack.py +++ b/salt/cloud/clouds/openstack.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Openstack Cloud Driver ====================== @@ -209,7 +209,7 @@ Anything else from the create_server_ docs can be passed through here. .. _create_server: https://docs.openstack.org/shade/latest/user/usage.html#shade.OpenStackCloud.create_server .. _vendor: https://docs.openstack.org/os-client-config/latest/user/vendor-support.html .. _os-client-config: https://docs.openstack.org/os-client-config/latest/user/configuration.html#config-files -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python Libs @@ -219,16 +219,17 @@ import os import pprint import socket +import salt.config as config + # Import Salt Libs import salt.utils.versions -import salt.config as config -from salt.ext import six from salt.exceptions import ( - SaltCloudSystemExit, - SaltCloudExecutionTimeout, - SaltCloudExecutionFailure, SaltCloudConfigError, + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, + SaltCloudSystemExit, ) +from salt.ext import six # Import 3rd-Party Libs try: @@ -236,21 +237,23 @@ try: import shade.openstackcloud import shade.exc import os_client_config + HAS_SHADE = ( - salt.utils.versions._LooseVersion(shade.__version__) >= salt.utils.versions._LooseVersion('1.19.0'), - 'Please install newer version of shade: >= 1.19.0' + salt.utils.versions._LooseVersion(shade.__version__) + >= salt.utils.versions._LooseVersion("1.19.0"), + "Please install newer version of shade: >= 1.19.0", ) except ImportError: - HAS_SHADE = (False, 'Install pypi module shade >= 1.19.0') + HAS_SHADE = (False, "Install pypi module shade >= 1.19.0") log = logging.getLogger(__name__) -__virtualname__ = 'openstack' +__virtualname__ = "openstack" def __virtual__(): - ''' + """ Check for OpenStack dependencies - ''' + """ if get_configured_provider() is False: return False if get_dependencies() is False: @@ -259,52 +262,47 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ provider = config.is_provider_configured( - __opts__, __active_provider_name__ or __virtualname__, - ('auth', 'region_name') + __opts__, __active_provider_name__ or __virtualname__, ("auth", "region_name") ) if provider: return provider return config.is_provider_configured( - __opts__, __active_provider_name__ or __virtualname__, - ('cloud', 'region_name') + __opts__, __active_provider_name__ or __virtualname__, ("cloud", "region_name") ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' + """ if not HAS_SHADE: log.warning('"shade" not found') return False - elif hasattr(HAS_SHADE, '__len__') and not HAS_SHADE[0]: + elif hasattr(HAS_SHADE, "__len__") and not HAS_SHADE[0]: log.warning(HAS_SHADE[1]) return False deps = { - 'shade': HAS_SHADE[0], - 'os_client_config': HAS_SHADE[0], + "shade": HAS_SHADE[0], + "os_client_config": HAS_SHADE[0], } - return config.check_driver_dependencies( - __virtualname__, - deps - ) + return config.check_driver_dependencies(__virtualname__, deps) def preferred_ip(vm_, ips): - ''' + """ Return the preferred Internet protocol. Either 'ipv4' (default) or 'ipv6'. - ''' + """ proto = config.get_cloud_config_value( - 'protocol', vm_, __opts__, default='ipv4', search_global=False + "protocol", vm_, __opts__, default="ipv4", search_global=False ) family = socket.AF_INET - if proto == 'ipv6': + if proto == "ipv6": family = socket.AF_INET6 for ip in ips: try: @@ -316,23 +314,27 @@ def preferred_ip(vm_, ips): def ssh_interface(vm_): - ''' + """ Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. - ''' - return config.get_cloud_config_value('ssh_interface', vm_, __opts__, default='public_ips', search_global=False) + """ + return config.get_cloud_config_value( + "ssh_interface", vm_, __opts__, default="public_ips", search_global=False + ) def get_conn(): - ''' + """ Return a conn object for the passed VM data - ''' + """ if __active_provider_name__ in __context__: return __context__[__active_provider_name__] vm_ = get_configured_provider() - profile = vm_.pop('profile', None) + profile = vm_.pop("profile", None) if profile is not None: - vm_ = __utils__['dictupdate.update'](os_client_config.vendors.get_profile(profile), vm_) + vm_ = __utils__["dictupdate.update"]( + os_client_config.vendors.get_profile(profile), vm_ + ) conn = shade.openstackcloud.OpenStackCloud(cloud_config=None, **vm_) if __active_provider_name__ is not None: __context__[__active_provider_name__] = conn @@ -340,7 +342,7 @@ def get_conn(): def list_nodes(conn=None, call=None): - ''' + """ Return a list of VMs CLI Example @@ -349,21 +351,31 @@ def list_nodes(conn=None, call=None): salt-cloud -f list_nodes myopenstack - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} for node, info in list_nodes_full(conn=conn).items(): - for key in ('id', 'name', 'size', 'state', 'private_ips', 'public_ips', 'floating_ips', 'fixed_ips', 'image'): + for key in ( + "id", + "name", + "size", + "state", + "private_ips", + "public_ips", + "floating_ips", + "fixed_ips", + "image", + ): ret.setdefault(node, {}).setdefault(key, info.get(key)) return ret def list_nodes_min(conn=None, call=None): - ''' + """ Return a list of VMs with minimal information CLI Example @@ -372,34 +384,40 @@ def list_nodes_min(conn=None, call=None): salt-cloud -f list_nodes_min myopenstack - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_min function must be called with -f or --function.' + "The list_nodes_min function must be called with -f or --function." ) if conn is None: conn = get_conn() ret = {} for node in conn.list_servers(bare=True): - ret[node.name] = {'id': node.id, 'state': node.status} + ret[node.name] = {"id": node.id, "state": node.status} return ret -def _get_ips(node, addr_type='public'): +def _get_ips(node, addr_type="public"): ret = [] for _, interface in node.addresses.items(): for addr in interface: - if addr_type in ('floating', 'fixed') and addr_type == addr.get('OS-EXT-IPS:type'): - ret.append(addr['addr']) - elif addr_type == 'public' and __utils__['cloud.is_public_ip'](addr['addr']): - ret.append(addr['addr']) - elif addr_type == 'private' and not __utils__['cloud.is_public_ip'](addr['addr']): - ret.append(addr['addr']) + if addr_type in ("floating", "fixed") and addr_type == addr.get( + "OS-EXT-IPS:type" + ): + ret.append(addr["addr"]) + elif addr_type == "public" and __utils__["cloud.is_public_ip"]( + addr["addr"] + ): + ret.append(addr["addr"]) + elif addr_type == "private" and not __utils__["cloud.is_public_ip"]( + addr["addr"] + ): + ret.append(addr["addr"]) return ret def list_nodes_full(conn=None, call=None): - ''' + """ Return a list of VMs with all the information about them CLI Example @@ -408,33 +426,35 @@ def list_nodes_full(conn=None, call=None): salt-cloud -f list_nodes_full myopenstack - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) if conn is None: conn = get_conn() ret = {} for node in conn.list_servers(detailed=True): ret[node.name] = dict(node) - ret[node.name]['id'] = node.id - ret[node.name]['name'] = node.name - ret[node.name]['size'] = node.flavor.name - ret[node.name]['state'] = node.status - ret[node.name]['private_ips'] = _get_ips(node, 'private') - ret[node.name]['public_ips'] = _get_ips(node, 'public') - ret[node.name]['floating_ips'] = _get_ips(node, 'floating') - ret[node.name]['fixed_ips'] = _get_ips(node, 'fixed') + ret[node.name]["id"] = node.id + ret[node.name]["name"] = node.name + ret[node.name]["size"] = node.flavor.name + ret[node.name]["state"] = node.status + ret[node.name]["private_ips"] = _get_ips(node, "private") + ret[node.name]["public_ips"] = _get_ips(node, "public") + ret[node.name]["floating_ips"] = _get_ips(node, "floating") + ret[node.name]["fixed_ips"] = _get_ips(node, "fixed") if isinstance(node.image, six.string_types): - ret[node.name]['image'] = node.image + ret[node.name]["image"] = node.image else: - ret[node.name]['image'] = getattr(conn.get_image(node.image.id), 'name', node.image.id) + ret[node.name]["image"] = getattr( + conn.get_image(node.image.id), "name", node.image.id + ) return ret def list_nodes_select(conn=None, call=None): - ''' + """ Return a list of VMs with the fields from `query.selection` CLI Example @@ -443,18 +463,18 @@ def list_nodes_select(conn=None, call=None): salt-cloud -f list_nodes_full myopenstack - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_select function must be called with -f or --function.' + "The list_nodes_select function must be called with -f or --function." ) - return __utils__['cloud.list_nodes_select']( - list_nodes(conn, 'function'), __opts__['query.selection'], call, + return __utils__["cloud.list_nodes_select"]( + list_nodes(conn, "function"), __opts__["query.selection"], call, ) def show_instance(name, conn=None, call=None): - ''' + """ Get VM on this OpenStack account name @@ -467,33 +487,33 @@ def show_instance(name, conn=None, call=None): salt-cloud -a show_instance myserver - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) if conn is None: conn = get_conn() node = conn.get_server(name, bare=True) ret = dict(node) - ret['id'] = node.id - ret['name'] = node.name - ret['size'] = conn.get_flavor(node.flavor.id).name - ret['state'] = node.status - ret['private_ips'] = _get_ips(node, 'private') - ret['public_ips'] = _get_ips(node, 'public') - ret['floating_ips'] = _get_ips(node, 'floating') - ret['fixed_ips'] = _get_ips(node, 'fixed') + ret["id"] = node.id + ret["name"] = node.name + ret["size"] = conn.get_flavor(node.flavor.id).name + ret["state"] = node.status + ret["private_ips"] = _get_ips(node, "private") + ret["public_ips"] = _get_ips(node, "public") + ret["floating_ips"] = _get_ips(node, "floating") + ret["fixed_ips"] = _get_ips(node, "fixed") if isinstance(node.image, six.string_types): - ret['image'] = node.image + ret["image"] = node.image else: - ret['image'] = getattr(conn.get_image(node.image.id), 'name', node.image.id) + ret["image"] = getattr(conn.get_image(node.image.id), "name", node.image.id) return ret def avail_images(conn=None, call=None): - ''' + """ List available images for OpenStack CLI Example @@ -503,11 +523,11 @@ def avail_images(conn=None, call=None): salt-cloud -f avail_images myopenstack salt-cloud --list-images myopenstack - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) if conn is None: conn = get_conn() @@ -515,7 +535,7 @@ def avail_images(conn=None, call=None): def avail_sizes(conn=None, call=None): - ''' + """ List available sizes for OpenStack CLI Example @@ -525,11 +545,11 @@ def avail_sizes(conn=None, call=None): salt-cloud -f avail_sizes myopenstack salt-cloud --list-sizes myopenstack - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) if conn is None: conn = get_conn() @@ -537,7 +557,7 @@ def avail_sizes(conn=None, call=None): def list_networks(conn=None, call=None): - ''' + """ List networks for OpenStack CLI Example @@ -546,11 +566,10 @@ def list_networks(conn=None, call=None): salt-cloud -f list_networks myopenstack - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_networks function must be called with ' - '-f or --function' + "The list_networks function must be called with " "-f or --function" ) if conn is None: conn = get_conn() @@ -558,7 +577,7 @@ def list_networks(conn=None, call=None): def list_subnets(conn=None, call=None, kwargs=None): - ''' + """ List subnets in a virtual network network @@ -568,271 +587,265 @@ def list_subnets(conn=None, call=None, kwargs=None): salt-cloud -f list_subnets myopenstack network=salt-net - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_subnets function must be called with ' - '-f or --function.' + "The list_subnets function must be called with " "-f or --function." ) if conn is None: conn = get_conn() - if kwargs is None or (isinstance(kwargs, dict) and 'network' not in kwargs): - raise SaltCloudSystemExit( - 'A `network` must be specified' - ) - return conn.list_subnets(filters={'network': kwargs['network']}) + if kwargs is None or (isinstance(kwargs, dict) and "network" not in kwargs): + raise SaltCloudSystemExit("A `network` must be specified") + return conn.list_subnets(filters={"network": kwargs["network"]}) def _clean_create_kwargs(**kwargs): - ''' + """ Sanatize kwargs to be sent to create_server - ''' + """ VALID_OPTS = { - 'name': six.string_types, - 'image': six.string_types, - 'flavor': six.string_types, - 'auto_ip': bool, - 'ips': list, - 'ip_pool': six.string_types, - 'root_volume': six.string_types, - 'boot_volume': six.string_types, - 'terminate_volume': bool, - 'volumes': list, - 'meta': dict, - 'files': dict, - 'reservation_id': six.string_types, - 'security_groups': list, - 'key_name': six.string_types, - 'availability_zone': six.string_types, - 'block_device_mapping': dict, - 'block_device_mapping_v2': dict, - 'nics': list, - 'scheduler_hints': dict, - 'config_drive': bool, - 'disk_config': six.string_types, # AUTO or MANUAL - 'admin_pass': six.string_types, - 'wait': bool, - 'timeout': int, - 'reuse_ips': bool, - 'network': dict, - 'boot_from_volume': bool, - 'volume_size': int, - 'nat_destination': six.string_types, - 'group': six.string_types, - 'userdata': six.string_types, + "name": six.string_types, + "image": six.string_types, + "flavor": six.string_types, + "auto_ip": bool, + "ips": list, + "ip_pool": six.string_types, + "root_volume": six.string_types, + "boot_volume": six.string_types, + "terminate_volume": bool, + "volumes": list, + "meta": dict, + "files": dict, + "reservation_id": six.string_types, + "security_groups": list, + "key_name": six.string_types, + "availability_zone": six.string_types, + "block_device_mapping": dict, + "block_device_mapping_v2": dict, + "nics": list, + "scheduler_hints": dict, + "config_drive": bool, + "disk_config": six.string_types, # AUTO or MANUAL + "admin_pass": six.string_types, + "wait": bool, + "timeout": int, + "reuse_ips": bool, + "network": dict, + "boot_from_volume": bool, + "volume_size": int, + "nat_destination": six.string_types, + "group": six.string_types, + "userdata": six.string_types, } - extra = kwargs.pop('extra', {}) + extra = kwargs.pop("extra", {}) for key, value in six.iteritems(kwargs.copy()): if key in VALID_OPTS: if isinstance(value, VALID_OPTS[key]): continue - log.error('Error %s: %s is not of type %s', key, value, VALID_OPTS[key]) + log.error("Error %s: %s is not of type %s", key, value, VALID_OPTS[key]) kwargs.pop(key) - return __utils__['dictupdate.update'](kwargs, extra) + return __utils__["dictupdate.update"](kwargs, extra) def request_instance(vm_, conn=None, call=None): - ''' + """ Request an instance to be built - ''' - if call == 'function': + """ + if call == "function": # Technically this function may be called other ways too, but it # definitely cannot be called with --function. raise SaltCloudSystemExit( - 'The request_instance action must be called with -a or --action.' + "The request_instance action must be called with -a or --action." ) kwargs = copy.deepcopy(vm_) - log.info('Creating Cloud VM %s', vm_['name']) - __utils__['cloud.check_name'](vm_['name'], 'a-zA-Z0-9._-') + log.info("Creating Cloud VM %s", vm_["name"]) + __utils__["cloud.check_name"](vm_["name"], "a-zA-Z0-9._-") conn = get_conn() userdata = config.get_cloud_config_value( - 'userdata', vm_, __opts__, search_global=False, default=None + "userdata", vm_, __opts__, search_global=False, default=None ) if userdata is not None and os.path.isfile(userdata): try: - with __utils__['files.fopen'](userdata, 'r') as fp_: - kwargs['userdata'] = __utils__['cloud.userdata_template']( + with __utils__["files.fopen"](userdata, "r") as fp_: + kwargs["userdata"] = __utils__["cloud.userdata_template"]( __opts__, vm_, fp_.read() ) except Exception as exc: # pylint: disable=broad-except - log.exception( - 'Failed to read userdata from %s: %s', userdata, exc) - if 'size' in kwargs: - kwargs['flavor'] = kwargs.pop('size') - kwargs['key_name'] = config.get_cloud_config_value( - 'ssh_key_name', vm_, __opts__, search_global=False, default=None + log.exception("Failed to read userdata from %s: %s", userdata, exc) + if "size" in kwargs: + kwargs["flavor"] = kwargs.pop("size") + kwargs["key_name"] = config.get_cloud_config_value( + "ssh_key_name", vm_, __opts__, search_global=False, default=None ) - kwargs['wait'] = True + kwargs["wait"] = True try: conn.create_server(**_clean_create_kwargs(**kwargs)) except shade.exc.OpenStackCloudException as exc: - log.error('Error creating server %s: %s', vm_['name'], exc) - destroy(vm_['name'], conn=conn, call='action') + log.error("Error creating server %s: %s", vm_["name"], exc) + destroy(vm_["name"], conn=conn, call="action") raise SaltCloudSystemExit(six.text_type(exc)) - return show_instance(vm_['name'], conn=conn, call='action') + return show_instance(vm_["name"], conn=conn, call="action") def create(vm_): - ''' + """ Create a single VM from a data dict - ''' - deploy = config.get_cloud_config_value('deploy', vm_, __opts__) + """ + deploy = config.get_cloud_config_value("deploy", vm_, __opts__) key_filename = config.get_cloud_config_value( - 'ssh_key_file', vm_, __opts__, search_global=False, default=None + "ssh_key_file", vm_, __opts__, search_global=False, default=None ) if key_filename is not None and not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined ssh_key_file \'{0}\' does not exist'.format( - key_filename - ) + "The defined ssh_key_file '{0}' does not exist".format(key_filename) ) - vm_['key_filename'] = key_filename + vm_["key_filename"] = key_filename - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) conn = get_conn() - if 'instance_id' in vm_: + if "instance_id" in vm_: # This was probably created via another process, and doesn't have # things like salt keys created yet, so let's create them now. - if 'pub_key' not in vm_ and 'priv_key' not in vm_: - log.debug('Generating minion keys for \'%s\'', vm_['name']) - vm_['priv_key'], vm_['pub_key'] = __utils__['cloud.gen_keys']( - config.get_cloud_config_value( - 'keysize', - vm_, - __opts__ - ) + if "pub_key" not in vm_ and "priv_key" not in vm_: + log.debug("Generating minion keys for '%s'", vm_["name"]) + vm_["priv_key"], vm_["pub_key"] = __utils__["cloud.gen_keys"]( + config.get_cloud_config_value("keysize", vm_, __opts__) ) else: # Put together all of the information required to request the instance, # and then fire off the request for it - request_instance(conn=conn, call='action', vm_=vm_) - data = show_instance(vm_.get('instance_id', vm_['name']), conn=conn, call='action') - log.debug('VM is now running') + request_instance(conn=conn, call="action", vm_=vm_) + data = show_instance(vm_.get("instance_id", vm_["name"]), conn=conn, call="action") + log.debug("VM is now running") def __query_node(vm_): - data = show_instance(vm_['name'], conn=conn, call='action') - if 'wait_for_metadata' in vm_: - for key, value in six.iteritems(vm_.get('wait_for_metadata', {})): - log.debug('Waiting for metadata: %s=%s', key, value) - if data['metadata'].get(key, None) != value: - log.debug('Metadata is not ready: %s=%s', key, data['metadata'].get(key)) + data = show_instance(vm_["name"], conn=conn, call="action") + if "wait_for_metadata" in vm_: + for key, value in six.iteritems(vm_.get("wait_for_metadata", {})): + log.debug("Waiting for metadata: %s=%s", key, value) + if data["metadata"].get(key, None) != value: + log.debug( + "Metadata is not ready: %s=%s", key, data["metadata"].get(key) + ) return False return preferred_ip(vm_, data[ssh_interface(vm_)]) + try: - ip_address = __utils__['cloud.wait_for_fun']( - __query_node, - vm_=vm_ - ) + ip_address = __utils__["cloud.wait_for_fun"](__query_node, vm_=vm_) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) - log.debug('Using IP address %s', ip_address) + log.debug("Using IP address %s", ip_address) - salt_interface = __utils__['cloud.get_salt_interface'](vm_, __opts__) + salt_interface = __utils__["cloud.get_salt_interface"](vm_, __opts__) salt_ip_address = preferred_ip(vm_, data[salt_interface]) - log.debug('Salt interface set to: %s', salt_ip_address) + log.debug("Salt interface set to: %s", salt_ip_address) if not ip_address: - raise SaltCloudSystemExit('A valid IP address was not found') + raise SaltCloudSystemExit("A valid IP address was not found") - vm_['ssh_host'] = ip_address - vm_['salt_host'] = salt_ip_address + vm_["ssh_host"] = ip_address + vm_["salt_host"] = salt_ip_address - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data) - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) event_data = { - 'name': vm_['name'], - 'profile': vm_['profile'], - 'provider': vm_['driver'], - 'instance_id': data['id'], - 'floating_ips': data['floating_ips'], - 'fixed_ips': data['fixed_ips'], - 'private_ips': data['private_ips'], - 'public_ips': data['public_ips'], + "name": vm_["name"], + "profile": vm_["profile"], + "provider": vm_["driver"], + "instance_id": data["id"], + "floating_ips": data["floating_ips"], + "fixed_ips": data["fixed_ips"], + "private_ips": data["private_ips"], + "public_ips": data["public_ips"], } - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', event_data, list(event_data)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]("created", event_data, list(event_data)), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], + ) + __utils__["cloud.cachedir_index_add"]( + vm_["name"], vm_["profile"], "nova", vm_["driver"] ) - __utils__['cloud.cachedir_index_add'](vm_['name'], vm_['profile'], 'nova', vm_['driver']) return ret def destroy(name, conn=None, call=None): - ''' + """ Delete a single VM - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) if not conn: conn = get_conn() - node = show_instance(name, conn=conn, call='action') - log.info('Destroying VM: %s', name) + node = show_instance(name, conn=conn, call="action") + log.info("Destroying VM: %s", name) ret = conn.delete_server(name) if ret: - log.info('Destroyed VM: %s', name) + log.info("Destroyed VM: %s", name) # Fire destroy action - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('delete_sshkeys', False) is True: - __utils__['cloud.remove_sshkey'](getattr(node, __opts__.get('ssh_interface', 'public_ips'))[0]) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) - __utils__['cloud.cachedir_index_del'](name) + if __opts__.get("delete_sshkeys", False) is True: + __utils__["cloud.remove_sshkey"]( + getattr(node, __opts__.get("ssh_interface", "public_ips"))[0] + ) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) + __utils__["cloud.cachedir_index_del"](name) return True - log.error('Failed to Destroy VM: %s', name) + log.error("Failed to Destroy VM: %s", name) return False def call(conn=None, call=None, kwargs=None): - ''' + """ Call function from shade. func @@ -845,29 +858,26 @@ def call(conn=None, call=None, kwargs=None): salt-cloud -f call myopenstack func=list_images t sujksalt-cloud -f call myopenstack func=create_network name=mysubnet - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The call function must be called with ' - '-f or --function.' + "The call function must be called with " "-f or --function." ) - if 'func' not in kwargs: - raise SaltCloudSystemExit( - 'No `func` argument passed' - ) + if "func" not in kwargs: + raise SaltCloudSystemExit("No `func` argument passed") if conn is None: conn = get_conn() - func = kwargs.pop('func') + func = kwargs.pop("func") for key, value in kwargs.items(): try: - kwargs[key] = __utils__['json.loads'](value) + kwargs[key] = __utils__["json.loads"](value) except ValueError: continue try: return getattr(conn, func)(**kwargs) except shade.exc.OpenStackCloudException as exc: - log.error('Error running %s: %s', func, exc) + log.error("Error running %s: %s", func, exc) raise SaltCloudSystemExit(six.text_type(exc)) diff --git a/salt/cloud/clouds/packet.py b/salt/cloud/clouds/packet.py index 6c3396db262..cf57eaf8d05 100644 --- a/salt/cloud/clouds/packet.py +++ b/salt/cloud/clouds/packet.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Packet Cloud Module Using Packet's Python API Client -=========================================== +==================================================== The Packet cloud module is used to control access to the Packet VPS system. @@ -13,17 +13,18 @@ The Packet profile requires ``size``, ``image``, ``location``, ``project_id`` Optional profile parameters: - - ``storage_size`` - min value is 10, defines Gigabytes of storage that will be attached to device. - - ``storage_tier`` - storage_1 - Standard Plan, storage_2 - Performance Plan - - ``snapshot_count`` - int - - ``snapshot_frequency`` - string - possible values: - - 1min - - 15min - - 1hour - - 1day - - 1week - - 1month - - 1year +- ``storage_size`` - min value is 10, defines Gigabytes of storage that will be attached to device. +- ``storage_tier`` - storage_1 - Standard Plan, storage_2 - Performance Plan +- ``snapshot_count`` - int +- ``snapshot_frequency`` - string - possible values: + + - 1min + - 15min + - 1hour + - 1day + - 1week + - 1month + - 1year This driver requires Packet's client library: https://pypi.python.org/pypi/packet-python @@ -46,36 +47,33 @@ This driver requires Packet's client library: https://pypi.python.org/pypi/packe storage_tier: storage_1 storage_snapshot_count: 1 storage_snapshot_frequency: 15min -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import pprint import time +# Import Salt Libs +import salt.config as config + +# Import Salt-Cloud Libs +import salt.utils.cloud +from salt.cloud.libcloudfuncs import get_image, get_size, script, show_instance +from salt.exceptions import SaltCloudException, SaltCloudSystemExit +from salt.ext.six.moves import range +from salt.utils.functools import namespaced_function + # Import 3rd-party libs try: import packet + HAS_PACKET = True except ImportError: HAS_PACKET = False -# Import Salt Libs -import salt.config as config - -from salt.ext.six.moves import range - -from salt.exceptions import ( - SaltCloudException, - SaltCloudSystemExit -) - -# Import Salt-Cloud Libs -import salt.utils.cloud - -from salt.cloud.libcloudfuncs import get_size, get_image, script, show_instance -from salt.utils.functools import namespaced_function get_size = namespaced_function(get_size, globals()) get_image = namespaced_function(get_image, globals()) @@ -87,16 +85,16 @@ show_instance = namespaced_function(show_instance, globals()) # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'packet' +__virtualname__ = "packet" # Only load this module if the Packet configuration is in place. def __virtual__(): - ''' + """ Check for Packet configs. - ''' + """ if HAS_PACKET is False: - return False, 'The packet python library is not installed' + return False, "The packet python library is not installed" if get_configured_provider() is False: return False @@ -104,18 +102,16 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('token',) + __opts__, __active_provider_name__ or __virtualname__, ("token",) ) def avail_images(call=None): - ''' + """ Return available Packet os images. CLI Example: @@ -124,16 +120,16 @@ def avail_images(call=None): salt-cloud --list-images packet-provider salt-cloud -f avail_images packet-provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The avail_images function must be called with -f or --function.' + "The avail_images function must be called with -f or --function." ) ret = {} vm_ = get_configured_provider() - manager = packet.Manager(auth_token=vm_['token']) + manager = packet.Manager(auth_token=vm_["token"]) ret = {} @@ -144,7 +140,7 @@ def avail_images(call=None): def avail_locations(call=None): - ''' + """ Return available Packet datacenter locations. CLI Example: @@ -153,14 +149,14 @@ def avail_locations(call=None): salt-cloud --list-locations packet-provider salt-cloud -f avail_locations packet-provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The avail_locations function must be called with -f or --function.' + "The avail_locations function must be called with -f or --function." ) vm_ = get_configured_provider() - manager = packet.Manager(auth_token=vm_['token']) + manager = packet.Manager(auth_token=vm_["token"]) ret = {} @@ -171,7 +167,7 @@ def avail_locations(call=None): def avail_sizes(call=None): - ''' + """ Return available Packet sizes. CLI Example: @@ -180,15 +176,15 @@ def avail_sizes(call=None): salt-cloud --list-sizes packet-provider salt-cloud -f avail_sizes packet-provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The avail_locations function must be called with -f or --function.' + "The avail_locations function must be called with -f or --function." ) vm_ = get_configured_provider() - manager = packet.Manager(auth_token=vm_['token']) + manager = packet.Manager(auth_token=vm_["token"]) ret = {} @@ -199,7 +195,7 @@ def avail_sizes(call=None): def avail_projects(call=None): - ''' + """ Return available Packet projects. CLI Example: @@ -207,14 +203,14 @@ def avail_projects(call=None): .. code-block:: bash salt-cloud -f avail_projects packet-provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The avail_projects function must be called with -f or --function.' + "The avail_projects function must be called with -f or --function." ) vm_ = get_configured_provider() - manager = packet.Manager(auth_token=vm_['token']) + manager = packet.Manager(auth_token=vm_["token"]) ret = {} @@ -225,7 +221,7 @@ def avail_projects(call=None): def _wait_for_status(status_type, object_id, status=None, timeout=500, quiet=True): - ''' + """ Wait for a certain status from Packet. status_type device or volume @@ -237,7 +233,7 @@ def _wait_for_status(status_type, object_id, status=None, timeout=500, quiet=Tru The amount of time to wait for a status to update. quiet Log status updates to debug logs when False. Otherwise, logs to info. - ''' + """ if status is None: status = "ok" @@ -245,10 +241,12 @@ def _wait_for_status(status_type, object_id, status=None, timeout=500, quiet=Tru iterations = int(timeout / interval) vm_ = get_configured_provider() - manager = packet.Manager(auth_token=vm_['token']) + manager = packet.Manager(auth_token=vm_["token"]) for i in range(0, iterations): - get_object = getattr(manager, "get_{status_type}".format(status_type=status_type)) + get_object = getattr( + manager, "get_{status_type}".format(status_type=status_type) + ) obj = get_object(object_id) if obj.state == status: @@ -257,8 +255,10 @@ def _wait_for_status(status_type, object_id, status=None, timeout=500, quiet=Tru time.sleep(interval) log.log( logging.INFO if not quiet else logging.DEBUG, - 'Status for Packet %s is \'%s\', waiting for \'%s\'.', - object_id, obj.state, status + "Status for Packet %s is '%s', waiting for '%s'.", + object_id, + obj.state, + status, ) return obj @@ -267,51 +267,60 @@ def _wait_for_status(status_type, object_id, status=None, timeout=500, quiet=Tru def is_profile_configured(vm_): try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or __virtualname__, - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or __virtualname__, + vm_["profile"], + vm_=vm_, + ) + is False + ): return False - alias, driver = __active_provider_name__.split(':') + alias, driver = __active_provider_name__.split(":") - profile_data = __opts__['providers'][alias][driver]['profiles'][vm_['profile']] + profile_data = __opts__["providers"][alias][driver]["profiles"][vm_["profile"]] - if profile_data.get('storage_size') or profile_data.get('storage_tier'): - required_keys = ['storage_size', 'storage_tier'] + if profile_data.get("storage_size") or profile_data.get("storage_tier"): + required_keys = ["storage_size", "storage_tier"] for key in required_keys: if profile_data.get(key) is None: log.error( - 'both storage_size and storage_tier required for ' - 'profile %s. Please check your profile configuration', - vm_['profile'] + "both storage_size and storage_tier required for " + "profile %s. Please check your profile configuration", + vm_["profile"], ) return False locations = avail_locations() for location in locations.values(): - if location['code'] == profile_data['location']: - if 'storage' not in location['features']: + if location["code"] == profile_data["location"]: + if "storage" not in location["features"]: log.error( - 'Chosen location %s for profile %s does not ' - 'support storage feature. Please check your ' - 'profile configuration', - location['code'], vm_['profile'] + "Chosen location %s for profile %s does not " + "support storage feature. Please check your " + "profile configuration", + location["code"], + vm_["profile"], ) return False - if profile_data.get('storage_snapshot_count') or profile_data.get('storage_snapshot_frequency'): - required_keys = ['storage_size', 'storage_tier'] + if profile_data.get("storage_snapshot_count") or profile_data.get( + "storage_snapshot_frequency" + ): + required_keys = ["storage_size", "storage_tier"] for key in required_keys: if profile_data.get(key) is None: log.error( - 'both storage_snapshot_count and ' - 'storage_snapshot_frequency required for profile ' - '%s. Please check your profile configuration', - vm_['profile'] + "both storage_snapshot_count and " + "storage_snapshot_frequency required for profile " + "%s. Please check your profile configuration", + vm_["profile"], ) return False @@ -322,120 +331,132 @@ def is_profile_configured(vm_): def create(vm_): - ''' + """ Create a single Packet VM. - ''' - name = vm_['name'] + """ + name = vm_["name"] if not is_profile_configured(vm_): return False - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(name), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(name), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Packet VM %s', name) + log.info("Creating Packet VM %s", name) - manager = packet.Manager(auth_token=vm_['token']) + manager = packet.Manager(auth_token=vm_["token"]) - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']('requesting', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "requesting", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - device = manager.create_device(project_id=vm_['project_id'], - hostname=name, - plan=vm_['size'], facility=vm_['location'], - operating_system=vm_['image']) + device = manager.create_device( + project_id=vm_["project_id"], + hostname=name, + plan=vm_["size"], + facility=vm_["location"], + operating_system=vm_["image"], + ) - device = _wait_for_status('device', device.id, status="active") + device = _wait_for_status("device", device.id, status="active") if device.state != "active": log.error( - 'Error creating %s on PACKET\n\n' - 'while waiting for initial ready status', - name, exc_info_on_loglevel=logging.DEBUG + "Error creating %s on PACKET\n\n" "while waiting for initial ready status", + name, + exc_info_on_loglevel=logging.DEBUG, ) # Define which ssh_interface to use ssh_interface = _get_ssh_interface(vm_) # Pass the correct IP address to the bootstrap ssh_host key - if ssh_interface == 'private_ips': + if ssh_interface == "private_ips": for ip in device.ip_addresses: - if ip['public'] is False: - vm_['ssh_host'] = ip['address'] + if ip["public"] is False: + vm_["ssh_host"] = ip["address"] break else: for ip in device.ip_addresses: - if ip['public'] is True: - vm_['ssh_host'] = ip['address'] + if ip["public"] is True: + vm_["ssh_host"] = ip["address"] break key_filename = config.get_cloud_config_value( - 'private_key', vm_, __opts__, search_global=False, default=None + "private_key", vm_, __opts__, search_global=False, default=None ) - vm_['key_filename'] = key_filename + vm_["key_filename"] = key_filename - vm_['private_key'] = key_filename + vm_["private_key"] = key_filename # Bootstrap! - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) - ret.update({'device': device.__dict__}) + ret.update({"device": device.__dict__}) - if vm_.get('storage_tier') and vm_.get('storage_size'): + if vm_.get("storage_tier") and vm_.get("storage_size"): # create storage and attach it to device volume = manager.create_volume( - vm_['project_id'], "{0}_storage".format(name), vm_.get('storage_tier'), - vm_.get('storage_size'), vm_.get('location'), snapshot_count=vm_.get('storage_snapshot_count', 0), - snapshot_frequency=vm_.get('storage_snapshot_frequency')) + vm_["project_id"], + "{0}_storage".format(name), + vm_.get("storage_tier"), + vm_.get("storage_size"), + vm_.get("location"), + snapshot_count=vm_.get("storage_snapshot_count", 0), + snapshot_frequency=vm_.get("storage_snapshot_frequency"), + ) volume.attach(device.id) - volume = _wait_for_status('volume', volume.id, status="active") + volume = _wait_for_status("volume", volume.id, status="active") if volume.state != "active": log.error( - 'Error creating %s on PACKET\n\n' - 'while waiting for initial ready status', - name, exc_info_on_loglevel=logging.DEBUG + "Error creating %s on PACKET\n\n" + "while waiting for initial ready status", + name, + exc_info_on_loglevel=logging.DEBUG, ) - ret.update({'volume': volume.__dict__}) + ret.update({"volume": volume.__dict__}) - log.info('Created Cloud VM \'%s\'', name) + log.info("Created Cloud VM '%s'", name) - log.debug( - '\'%s\' VM creation details:\n%s', - name, pprint.pformat(device.__dict__) - ) + log.debug("'%s' VM creation details:\n%s", name, pprint.pformat(device.__dict__)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(name), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(name), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def list_nodes_full(call=None): - ''' + """ List devices, with all available information. CLI Example: @@ -447,10 +468,10 @@ def list_nodes_full(call=None): salt-cloud -f list_nodes_full packet-provider .. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) ret = {} @@ -462,7 +483,7 @@ def list_nodes_full(call=None): def list_nodes_min(call=None): - ''' + """ Return a list of the VMs that are on the provider. Only a list of VM names and their state is returned. This is the minimum amount of information needed to check for existing VMs. @@ -475,57 +496,59 @@ def list_nodes_min(call=None): salt-cloud -f list_nodes_min packet-provider salt-cloud --function list_nodes_min packet-provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_min function must be called with -f or --function.' + "The list_nodes_min function must be called with -f or --function." ) ret = {} for device in get_devices_by_token(): - ret[device.hostname] = {'id': device.id, 'state': device.state} + ret[device.hostname] = {"id": device.id, "state": device.state} return ret def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields. - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full(), __opts__['query.selection'], call, + list_nodes_full(), __opts__["query.selection"], call, ) def get_devices_by_token(): vm_ = get_configured_provider() - manager = packet.Manager(auth_token=vm_['token']) + manager = packet.Manager(auth_token=vm_["token"]) devices = [] - for profile_name in vm_['profiles']: - profile = vm_['profiles'][profile_name] + for profile_name in vm_["profiles"]: + profile = vm_["profiles"][profile_name] - devices.extend(manager.list_devices(profile['project_id'])) + devices.extend(manager.list_devices(profile["project_id"])) return devices def list_nodes(call=None): - ''' + """ Returns a list of devices, keeping only a brief listing. CLI Example: + .. code-block:: bash + salt-cloud -Q salt-cloud --query salt-cloud -f list_nodes packet-provider .. - ''' + """ - if call == 'action': + if call == "action": raise SaltCloudException( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} @@ -537,7 +560,7 @@ def list_nodes(call=None): def destroy(name, call=None): - ''' + """ Destroys a Packet device by name. name @@ -548,24 +571,23 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud -d name - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) vm_ = get_configured_provider() - manager = packet.Manager(auth_token=vm_['token']) + manager = packet.Manager(auth_token=vm_["token"]) nodes = list_nodes_min() @@ -574,31 +596,30 @@ def destroy(name, call=None): for project in manager.list_projects(): for volume in manager.list_volumes(project.id): - if volume.attached_to == node['id']: + if volume.attached_to == node["id"]: volume.detach() volume.delete() break - manager.call_api("devices/{id}".format(id=node['id']), type='DELETE') + manager.call_api("devices/{id}".format(id=node["id"]), type="DELETE") - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return {} def _get_ssh_interface(vm_): - ''' + """ Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. - ''' + """ return config.get_cloud_config_value( - 'ssh_interface', vm_, __opts__, default='public_ips', - search_global=False + "ssh_interface", vm_, __opts__, default="public_ips", search_global=False ) diff --git a/salt/cloud/clouds/parallels.py b/salt/cloud/clouds/parallels.py index c52d2ac7ce4..c66455aeea5 100644 --- a/salt/cloud/clouds/parallels.py +++ b/salt/cloud/clouds/parallels.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Parallels Cloud Module ====================== @@ -18,52 +18,57 @@ Set up the cloud configuration at ``/etc/salt/cloud.providers`` or url: https://api.cloud.xmission.com:4465/paci/v1.0/ driver: parallels -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time -import pprint -import logging -# Import Salt libs -from salt._compat import ElementTree as ET -# pylint: disable=import-error,no-name-in-module -from salt.ext.six.moves.urllib.error import URLError -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -from salt.ext.six.moves.urllib.request import ( - HTTPBasicAuthHandler as _HTTPBasicAuthHandler, - Request as _Request, - urlopen as _urlopen, - build_opener as _build_opener, - install_opener as _install_opener -) -# pylint: enable=import-error,no-name-in-module +import logging +import pprint +import time + +import salt.config as config # Import salt cloud libs import salt.utils.cloud -import salt.config as config + +# Import Salt libs +from salt._compat import ElementTree as ET from salt.exceptions import ( + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, SaltCloudNotFound, SaltCloudSystemExit, - SaltCloudExecutionFailure, - SaltCloudExecutionTimeout ) # Import 3rd-party libs from salt.ext import six +# pylint: disable=import-error,no-name-in-module +from salt.ext.six.moves.urllib.error import URLError +from salt.ext.six.moves.urllib.parse import urlencode as _urlencode +from salt.ext.six.moves.urllib.request import ( + HTTPBasicAuthHandler as _HTTPBasicAuthHandler, +) +from salt.ext.six.moves.urllib.request import Request as _Request +from salt.ext.six.moves.urllib.request import build_opener as _build_opener +from salt.ext.six.moves.urllib.request import install_opener as _install_opener +from salt.ext.six.moves.urllib.request import urlopen as _urlopen + +# pylint: enable=import-error,no-name-in-module + + # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'parallels' +__virtualname__ = "parallels" # Only load in this module if the PARALLELS configurations are in place def __virtual__(): - ''' + """ Check for PARALLELS configurations - ''' + """ if get_configured_provider() is False: return False @@ -71,254 +76,259 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('user', 'password', 'url',) + ("user", "password", "url",), ) def avail_images(call=None): - ''' + """ Return a list of the images that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) - items = query(action='template') + items = query(action="template") ret = {} for item in items: - ret[item.attrib['name']] = item.attrib + ret[item.attrib["name"]] = item.attrib return ret def list_nodes(call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} - items = query(action='ve') + items = query(action="ve") for item in items: - name = item.attrib['name'] - node = show_instance(name, call='action') + name = item.attrib["name"] + node = show_instance(name, call="action") ret[name] = { - 'id': node['id'], - 'image': node['platform']['template-info']['name'], - 'state': node['state'], + "id": node["id"], + "image": node["platform"]["template-info"]["name"], + "state": node["state"], } - if 'private-ip' in node['network']: - ret[name]['private_ips'] = [node['network']['private-ip']] - if 'public-ip' in node['network']: - ret[name]['public_ips'] = [node['network']['public-ip']] + if "private-ip" in node["network"]: + ret[name]["private_ips"] = [node["network"]["private-ip"]] + if "public-ip" in node["network"]: + ret[name]["public_ips"] = [node["network"]["public-ip"]] return ret def list_nodes_full(call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) ret = {} - items = query(action='ve') + items = query(action="ve") for item in items: - name = item.attrib['name'] - node = show_instance(name, call='action') + name = item.attrib["name"] + node = show_instance(name, call="action") ret[name] = node - ret[name]['image'] = node['platform']['template-info']['name'] - if 'private-ip' in node['network']: - ret[name]['private_ips'] = [ - node['network']['private-ip']['address'] - ] - if 'public-ip' in node['network']: - ret[name]['public_ips'] = [ - node['network']['public-ip']['address'] - ] + ret[name]["image"] = node["platform"]["template-info"]["name"] + if "private-ip" in node["network"]: + ret[name]["private_ips"] = [node["network"]["private-ip"]["address"]] + if "public-ip" in node["network"]: + ret[name]["public_ips"] = [node["network"]["public-ip"]["address"]] return ret def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full(), __opts__['query.selection'], call, + list_nodes_full(), __opts__["query.selection"], call, ) def get_image(vm_): - ''' + """ Return the image object to use - ''' + """ images = avail_images() vm_image = config.get_cloud_config_value( - 'image', vm_, __opts__, search_global=False + "image", vm_, __opts__, search_global=False ) for image in images: - if six.text_type(vm_image) in (images[image]['name'], images[image]['id']): - return images[image]['id'] - raise SaltCloudNotFound('The specified image could not be found.') + if six.text_type(vm_image) in (images[image]["name"], images[image]["id"]): + return images[image]["id"] + raise SaltCloudNotFound("The specified image could not be found.") def create_node(vm_): - ''' + """ Build and submit the XML to create a node - ''' + """ # Start the tree - content = ET.Element('ve') + content = ET.Element("ve") # Name of the instance - name = ET.SubElement(content, 'name') - name.text = vm_['name'] + name = ET.SubElement(content, "name") + name.text = vm_["name"] # Description, defaults to name - desc = ET.SubElement(content, 'description') + desc = ET.SubElement(content, "description") desc.text = config.get_cloud_config_value( - 'desc', vm_, __opts__, default=vm_['name'], search_global=False + "desc", vm_, __opts__, default=vm_["name"], search_global=False ) # How many CPU cores, and how fast they are - cpu = ET.SubElement(content, 'cpu') - cpu.attrib['number'] = config.get_cloud_config_value( - 'cpu_number', vm_, __opts__, default='1', search_global=False + cpu = ET.SubElement(content, "cpu") + cpu.attrib["number"] = config.get_cloud_config_value( + "cpu_number", vm_, __opts__, default="1", search_global=False ) - cpu.attrib['power'] = config.get_cloud_config_value( - 'cpu_power', vm_, __opts__, default='1000', search_global=False + cpu.attrib["power"] = config.get_cloud_config_value( + "cpu_power", vm_, __opts__, default="1000", search_global=False ) # How many megabytes of RAM - ram = ET.SubElement(content, 'ram-size') + ram = ET.SubElement(content, "ram-size") ram.text = config.get_cloud_config_value( - 'ram', vm_, __opts__, default='256', search_global=False + "ram", vm_, __opts__, default="256", search_global=False ) # Bandwidth available, in kbps - bandwidth = ET.SubElement(content, 'bandwidth') + bandwidth = ET.SubElement(content, "bandwidth") bandwidth.text = config.get_cloud_config_value( - 'bandwidth', vm_, __opts__, default='100', search_global=False + "bandwidth", vm_, __opts__, default="100", search_global=False ) # How many public IPs will be assigned to this instance - ip_num = ET.SubElement(content, 'no-of-public-ip') + ip_num = ET.SubElement(content, "no-of-public-ip") ip_num.text = config.get_cloud_config_value( - 'ip_num', vm_, __opts__, default='1', search_global=False + "ip_num", vm_, __opts__, default="1", search_global=False ) # Size of the instance disk - disk = ET.SubElement(content, 've-disk') - disk.attrib['local'] = 'true' - disk.attrib['size'] = config.get_cloud_config_value( - 'disk_size', vm_, __opts__, default='10', search_global=False + disk = ET.SubElement(content, "ve-disk") + disk.attrib["local"] = "true" + disk.attrib["size"] = config.get_cloud_config_value( + "disk_size", vm_, __opts__, default="10", search_global=False ) # Attributes for the image vm_image = config.get_cloud_config_value( - 'image', vm_, __opts__, search_global=False + "image", vm_, __opts__, search_global=False ) - image = show_image({'image': vm_image}, call='function') - platform = ET.SubElement(content, 'platform') - template = ET.SubElement(platform, 'template-info') - template.attrib['name'] = vm_image - os_info = ET.SubElement(platform, 'os-info') - os_info.attrib['technology'] = image[vm_image]['technology'] - os_info.attrib['type'] = image[vm_image]['osType'] + image = show_image({"image": vm_image}, call="function") + platform = ET.SubElement(content, "platform") + template = ET.SubElement(platform, "template-info") + template.attrib["name"] = vm_image + os_info = ET.SubElement(platform, "os-info") + os_info.attrib["technology"] = image[vm_image]["technology"] + os_info.attrib["type"] = image[vm_image]["osType"] # Username and password - admin = ET.SubElement(content, 'admin') - admin.attrib['login'] = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, default='root' + admin = ET.SubElement(content, "admin") + admin.attrib["login"] = config.get_cloud_config_value( + "ssh_username", vm_, __opts__, default="root" ) - admin.attrib['password'] = config.get_cloud_config_value( - 'password', vm_, __opts__, search_global=False + admin.attrib["password"] = config.get_cloud_config_value( + "password", vm_, __opts__, search_global=False ) - data = ET.tostring(content, encoding='UTF-8') + data = ET.tostring(content, encoding="UTF-8") - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', data, list(data)), + "kwargs": __utils__["cloud.filter_event"]("requesting", data, list(data)), }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - node = query(action='ve', method='POST', data=data) + node = query(action="ve", method="POST", data=data) return node def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'parallels', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or "parallels", + vm_["profile"], + vm_=vm_, + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) try: data = create_node(vm_) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on PARALLELS\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: \n%s', - vm_['name'], exc, + "Error creating %s on PARALLELS\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: \n%s", + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False - name = vm_['name'] - if not wait_until(name, 'CREATED'): - return {'Error': 'Unable to start {0}, command timed out'.format(name)} - start(vm_['name'], call='action') + name = vm_["name"] + if not wait_until(name, "CREATED"): + return {"Error": "Unable to start {0}, command timed out".format(name)} + start(vm_["name"], call="action") - if not wait_until(name, 'STARTED'): - return {'Error': 'Unable to start {0}, command timed out'.format(name)} + if not wait_until(name, "STARTED"): + return {"Error": "Unable to start {0}, command timed out".format(name)} def __query_node_data(vm_name): - data = show_instance(vm_name, call='action') - if 'public-ip' not in data['network']: + data = show_instance(vm_name, call="action") + if "public-ip" not in data["network"]: # Trigger another iteration return return data @@ -326,63 +336,63 @@ def create(vm_): try: data = salt.utils.cloud.wait_for_ip( __query_node_data, - update_args=(vm_['name'],), + update_args=(vm_["name"],), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=5 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=5 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=5), + "wait_for_ip_interval", vm_, __opts__, default=5 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) - comps = data['network']['public-ip']['address'].split('/') + comps = data["network"]["public-ip"]["address"].split("/") public_ip = comps[0] - vm_['ssh_host'] = public_ip - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + vm_["ssh_host"] = public_ip + ret = __utils__["cloud.bootstrap"](vm_, __opts__) - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return data -def query(action=None, command=None, args=None, method='GET', data=None): - ''' +def query(action=None, command=None, args=None, method="GET", data=None): + """ Make a web call to a Parallels provider - ''' + """ path = config.get_cloud_config_value( - 'url', get_configured_provider(), __opts__, search_global=False + "url", get_configured_provider(), __opts__, search_global=False ) auth_handler = _HTTPBasicAuthHandler() auth_handler.add_password( - realm='Parallels Instance Manager', + realm="Parallels Instance Manager", uri=path, user=config.get_cloud_config_value( - 'user', get_configured_provider(), __opts__, search_global=False + "user", get_configured_provider(), __opts__, search_global=False ), passwd=config.get_cloud_config_value( - 'password', get_configured_provider(), __opts__, - search_global=False - ) + "password", get_configured_provider(), __opts__, search_global=False + ), ) opener = _build_opener(auth_handler) _install_opener(opener) @@ -391,34 +401,34 @@ def query(action=None, command=None, args=None, method='GET', data=None): path += action if command: - path += '/{0}'.format(command) + path += "/{0}".format(command) if not type(args, dict): args = {} - kwargs = {'data': data} - if isinstance(data, six.string_types) and ' timeout: return False - node = show_instance(name, call='action') + node = show_instance(name, call="action") def destroy(name, call=None): - ''' + """ Destroy a node. CLI Example: @@ -518,54 +528,51 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud --destroy mymachine - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - node = show_instance(name, call='action') - if node['state'] == 'STARTED': - stop(name, call='action') - if not wait_until(name, 'STOPPED'): - return { - 'Error': 'Unable to destroy {0}, command timed out'.format( - name - ) - } + node = show_instance(name, call="action") + if node["state"] == "STARTED": + stop(name, call="action") + if not wait_until(name, "STOPPED"): + return {"Error": "Unable to destroy {0}, command timed out".format(name)} - data = query(action='ve', command=name, method='DELETE') + data = query(action="ve", command=name, method="DELETE") - if 'error' in data: - return data['error'] + if "error" in data: + return data["error"] - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) - return {'Destroyed': '{0} was destroyed.'.format(name)} + return {"Destroyed": "{0} was destroyed.".format(name)} def start(name, call=None): - ''' + """ Start a node. CLI Example: @@ -573,22 +580,22 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start mymachine - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) - data = query(action='ve', command='{0}/start'.format(name), method='PUT') + data = query(action="ve", command="{0}/start".format(name), method="PUT") - if 'error' in data: - return data['error'] + if "error" in data: + return data["error"] - return {'Started': '{0} was started.'.format(name)} + return {"Started": "{0} was started.".format(name)} def stop(name, call=None): - ''' + """ Stop a node. CLI Example: @@ -596,15 +603,15 @@ def stop(name, call=None): .. code-block:: bash salt-cloud -a stop mymachine - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) - data = query(action='ve', command='{0}/stop'.format(name), method='PUT') + data = query(action="ve", command="{0}/stop".format(name), method="PUT") - if 'error' in data: - return data['error'] + if "error" in data: + return data["error"] - return {'Stopped': '{0} was stopped.'.format(name)} + return {"Stopped": "{0} was stopped.".format(name)} diff --git a/salt/cloud/clouds/profitbricks.py b/salt/cloud/clouds/profitbricks.py index 79489a1bd68..d2e75dd42c5 100644 --- a/salt/cloud/clouds/profitbricks.py +++ b/salt/cloud/clouds/profitbricks.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" ProfitBricks Cloud Module ========================= @@ -91,40 +91,51 @@ Set ``deploy`` to False if Salt should not be installed on the node. my-profitbricks-profile: deploy: False -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import pprint import time -from salt.utils.versions import LooseVersion + +import salt.config as config # Import salt libs import salt.utils.cloud import salt.utils.files import salt.utils.stringutils -import salt.config as config from salt.exceptions import ( SaltCloudConfigError, - SaltCloudNotFound, SaltCloudExecutionFailure, SaltCloudExecutionTimeout, - SaltCloudSystemExit + SaltCloudNotFound, + SaltCloudSystemExit, ) # Import 3rd-party libs from salt.ext import six +from salt.utils.versions import LooseVersion + try: # pylint: disable=no-name-in-module import profitbricks from profitbricks.client import ( - ProfitBricksService, Server, - NIC, Volume, FirewallRule, IPBlock, - Datacenter, LoadBalancer, LAN, - PBNotFoundError, PBError + ProfitBricksService, + Server, + NIC, + Volume, + FirewallRule, + IPBlock, + Datacenter, + LoadBalancer, + LAN, + PBNotFoundError, + PBError, ) + # pylint: enable=no-name-in-module HAS_PROFITBRICKS = True except ImportError: @@ -133,14 +144,14 @@ except ImportError: # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'profitbricks' +__virtualname__ = "profitbricks" # Only load in this module if the ProfitBricks configurations are in place def __virtual__(): - ''' + """ Check for ProfitBricks configurations. - ''' + """ if get_configured_provider() is False: return False @@ -151,70 +162,63 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('username', 'password', 'datacenter_id') + ("username", "password", "datacenter_id"), ) def version_compatible(version): - ''' + """ Checks profitbricks version - ''' + """ return LooseVersion(profitbricks.API_VERSION) >= LooseVersion(version) def get_dependencies(): - ''' + """ Warn if dependencies are not met. - ''' + """ return config.check_driver_dependencies( - __virtualname__, - {'profitbricks': HAS_PROFITBRICKS} + __virtualname__, {"profitbricks": HAS_PROFITBRICKS} ) def get_conn(): - ''' + """ Return a conn object for the passed VM data - ''' + """ return ProfitBricksService( username=config.get_cloud_config_value( - 'username', - get_configured_provider(), - __opts__, - search_global=False + "username", get_configured_provider(), __opts__, search_global=False ), password=config.get_cloud_config_value( - 'password', - get_configured_provider(), - __opts__, - search_global=False - ) + "password", get_configured_provider(), __opts__, search_global=False + ), ) def avail_locations(call=None): - ''' + """ Return a dict of all available VM locations on the cloud provider with relevant data - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_images function must be called with " + "-f or --function, or with the --list-locations option" ) ret = {} conn = get_conn() - for item in conn.list_locations()['items']: - reg, loc = item['id'].split('/') - location = {'id': item['id']} + for item in conn.list_locations()["items"]: + reg, loc = item["id"].split("/") + location = {"id": item["id"]} if reg not in ret: ret[reg] = {} @@ -224,28 +228,28 @@ def avail_locations(call=None): def avail_images(call=None): - ''' + """ Return a list of the images that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) ret = {} conn = get_conn() - for item in conn.list_images()['items']: - image = {'id': item['id']} - image.update(item['properties']) - ret[image['name']] = image + for item in conn.list_images()["items"]: + image = {"id": item["id"]} + image.update(item["properties"]) + ret[image["name"]] = image return ret def list_images(call=None, kwargs=None): - ''' + """ List all the images with alias by location CLI Example: @@ -253,14 +257,13 @@ def list_images(call=None, kwargs=None): .. code-block:: bash salt-cloud -f list_images my-profitbricks-config location=us/las - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_images function must be called with ' - '-f or --function.' + "The list_images function must be called with " "-f or --function." ) - if not version_compatible('4.0'): + if not version_compatible("4.0"): raise SaltCloudNotFound( "The 'image_alias' feature requires the profitbricks " "SDK v4.0.0 or greater." @@ -269,104 +272,76 @@ def list_images(call=None, kwargs=None): ret = {} conn = get_conn() - if kwargs.get('location') is not None: - item = conn.get_location(kwargs.get('location'), 3) - ret[item['id']] = {'image_alias': item['properties']['imageAliases']} + if kwargs.get("location") is not None: + item = conn.get_location(kwargs.get("location"), 3) + ret[item["id"]] = {"image_alias": item["properties"]["imageAliases"]} return ret - for item in conn.list_locations(3)['items']: - ret[item['id']] = {'image_alias': item['properties']['imageAliases']} + for item in conn.list_locations(3)["items"]: + ret[item["id"]] = {"image_alias": item["properties"]["imageAliases"]} return ret def avail_sizes(call=None): - ''' + """ Return a dict of all available VM sizes on the cloud provider with relevant data. Latest version can be found at: - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) sizes = { - 'Micro Instance': { - 'id': '1', - 'ram': 1024, - 'disk': 50, - 'cores': 1 + "Micro Instance": {"id": "1", "ram": 1024, "disk": 50, "cores": 1}, + "Small Instance": {"id": "2", "ram": 2048, "disk": 50, "cores": 1}, + "Medium Instance": {"id": "3", "ram": 4096, "disk": 50, "cores": 2}, + "Large Instance": {"id": "4", "ram": 7168, "disk": 50, "cores": 4}, + "Extra Large Instance": {"id": "5", "ram": 14336, "disk": 50, "cores": 8}, + "Memory Intensive Instance Medium": { + "id": "6", + "ram": 28672, + "disk": 50, + "cores": 4, }, - 'Small Instance': { - 'id': '2', - 'ram': 2048, - 'disk': 50, - 'cores': 1 + "Memory Intensive Instance Large": { + "id": "7", + "ram": 57344, + "disk": 50, + "cores": 8, }, - 'Medium Instance': { - 'id': '3', - 'ram': 4096, - 'disk': 50, - 'cores': 2 - }, - 'Large Instance': { - 'id': '4', - 'ram': 7168, - 'disk': 50, - 'cores': 4 - }, - 'Extra Large Instance': { - 'id': '5', - 'ram': 14336, - 'disk': 50, - 'cores': 8 - }, - 'Memory Intensive Instance Medium': { - 'id': '6', - 'ram': 28672, - 'disk': 50, - 'cores': 4 - }, - 'Memory Intensive Instance Large': { - 'id': '7', - 'ram': 57344, - 'disk': 50, - 'cores': 8 - } } return sizes def get_size(vm_): - ''' + """ Return the VM's size object - ''' - vm_size = config.get_cloud_config_value('size', vm_, __opts__) + """ + vm_size = config.get_cloud_config_value("size", vm_, __opts__) sizes = avail_sizes() if not vm_size: - return sizes['Small Instance'] + return sizes["Small Instance"] for size in sizes: - combinations = (six.text_type(sizes[size]['id']), six.text_type(size)) + combinations = (six.text_type(sizes[size]["id"]), six.text_type(size)) if vm_size and six.text_type(vm_size) in combinations: return sizes[size] raise SaltCloudNotFound( - 'The specified size, \'{0}\', could not be found.'.format(vm_size) + "The specified size, '{0}', could not be found.".format(vm_size) ) def get_datacenter_id(): - ''' + """ Return datacenter ID from provider configuration - ''' + """ datacenter_id = config.get_cloud_config_value( - 'datacenter_id', - get_configured_provider(), - __opts__, - search_global=False + "datacenter_id", get_configured_provider(), __opts__, search_global=False ) conn = get_conn() @@ -374,36 +349,36 @@ def get_datacenter_id(): try: conn.get_datacenter(datacenter_id=datacenter_id) except PBNotFoundError: - log.error('Failed to get datacenter: %s', datacenter_id) + log.error("Failed to get datacenter: %s", datacenter_id) raise return datacenter_id def list_loadbalancers(call=None): - ''' + """ Return a list of the loadbalancers that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-loadbalancers option' + "The avail_images function must be called with " + "-f or --function, or with the --list-loadbalancers option" ) ret = {} conn = get_conn() datacenter = get_datacenter(conn) - for item in conn.list_loadbalancers(datacenter['id'])['items']: - lb = {'id': item['id']} - lb.update(item['properties']) - ret[lb['name']] = lb + for item in conn.list_loadbalancers(datacenter["id"])["items"]: + lb = {"id": item["id"]} + lb.update(item["properties"]) + ret[lb["name"]] = lb return ret def create_loadbalancer(call=None, kwargs=None): - ''' + """ Creates a loadbalancer within the datacenter from the provider config. CLI Example: @@ -411,10 +386,10 @@ def create_loadbalancer(call=None, kwargs=None): .. code-block:: bash salt-cloud -f create_loadbalancer profitbricks name=mylb - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_address function must be called with -f or --function.' + "The create_address function must be called with -f or --function." ) if kwargs is None: @@ -422,35 +397,33 @@ def create_loadbalancer(call=None, kwargs=None): conn = get_conn() datacenter_id = get_datacenter_id() - loadbalancer = LoadBalancer(name=kwargs.get('name'), - ip=kwargs.get('ip'), - dhcp=kwargs.get('dhcp')) + loadbalancer = LoadBalancer( + name=kwargs.get("name"), ip=kwargs.get("ip"), dhcp=kwargs.get("dhcp") + ) response = conn.create_loadbalancer(datacenter_id, loadbalancer) - _wait_for_completion(conn, response, 60, 'loadbalancer') + _wait_for_completion(conn, response, 60, "loadbalancer") return response def get_datacenter(conn): - ''' + """ Return the datacenter from the config provider datacenter ID - ''' + """ datacenter_id = get_datacenter_id() - for item in conn.list_datacenters()['items']: - if item['id'] == datacenter_id: + for item in conn.list_datacenters()["items"]: + if item["id"] == datacenter_id: return item raise SaltCloudNotFound( - 'The specified datacenter \'{0}\' could not be found.'.format( - datacenter_id - ) + "The specified datacenter '{0}' could not be found.".format(datacenter_id) ) def create_datacenter(call=None, kwargs=None): - ''' + """ Creates a virtual datacenter based on supplied parameters. CLI Example: @@ -459,72 +432,72 @@ def create_datacenter(call=None, kwargs=None): salt-cloud -f create_datacenter profitbricks name=mydatacenter location=us/las description="my description" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_address function must be called with -f or --function.' + "The create_address function must be called with -f or --function." ) if kwargs is None: kwargs = {} - if kwargs.get('name') is None: + if kwargs.get("name") is None: raise SaltCloudExecutionFailure('The "name" parameter is required') - if kwargs.get('location') is None: + if kwargs.get("location") is None: raise SaltCloudExecutionFailure('The "location" parameter is required') conn = get_conn() - datacenter = Datacenter(name=kwargs['name'], - location=kwargs['location'], - description=kwargs.get('description')) + datacenter = Datacenter( + name=kwargs["name"], + location=kwargs["location"], + description=kwargs.get("description"), + ) response = conn.create_datacenter(datacenter) - _wait_for_completion(conn, response, 60, 'create_datacenter') + _wait_for_completion(conn, response, 60, "create_datacenter") return response def get_disk_type(vm_): - ''' + """ Return the type of disk to use. Either 'HDD' (default) or 'SSD'. - ''' + """ return config.get_cloud_config_value( - 'disk_type', vm_, __opts__, default='HDD', - search_global=False + "disk_type", vm_, __opts__, default="HDD", search_global=False ) def get_wait_timeout(vm_): - ''' + """ Return the wait_for_timeout for resource provisioning. - ''' + """ return config.get_cloud_config_value( - 'wait_for_timeout', vm_, __opts__, default=15 * 60, - search_global=False + "wait_for_timeout", vm_, __opts__, default=15 * 60, search_global=False ) def get_image(vm_): - ''' + """ Return the image object to use - ''' - vm_image = config.get_cloud_config_value('image', vm_, __opts__).encode( - 'ascii', 'salt-cloud-force-ascii' + """ + vm_image = config.get_cloud_config_value("image", vm_, __opts__).encode( + "ascii", "salt-cloud-force-ascii" ) images = avail_images() for key in six.iterkeys(images): - if vm_image and vm_image in (images[key]['id'], images[key]['name']): + if vm_image and vm_image in (images[key]["id"], images[key]["name"]): return images[key] raise SaltCloudNotFound( - 'The specified image, \'{0}\', could not be found.'.format(vm_image) + "The specified image, '{0}', could not be found.".format(vm_image) ) def list_datacenters(conn=None, call=None): - ''' + """ List all the data centers CLI Example: @@ -532,11 +505,10 @@ def list_datacenters(conn=None, call=None): .. code-block:: bash salt-cloud -f list_datacenters my-profitbricks-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_datacenters function must be called with ' - '-f or --function.' + "The list_datacenters function must be called with " "-f or --function." ) datacenters = [] @@ -544,21 +516,21 @@ def list_datacenters(conn=None, call=None): if not conn: conn = get_conn() - for item in conn.list_datacenters()['items']: - datacenter = {'id': item['id']} - datacenter.update(item['properties']) - datacenters.append({item['properties']['name']: datacenter}) + for item in conn.list_datacenters()["items"]: + datacenter = {"id": item["id"]} + datacenter.update(item["properties"]) + datacenters.append({item["properties"]["name"]: datacenter}) - return {'Datacenters': datacenters} + return {"Datacenters": datacenters} def list_nodes(conn=None, call=None): - ''' + """ Return a list of VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) if not conn: @@ -570,71 +542,66 @@ def list_nodes(conn=None, call=None): try: nodes = conn.list_servers(datacenter_id=datacenter_id) except PBNotFoundError: - log.error('Failed to get nodes list ' - 'from datacenter: %s', datacenter_id) + log.error("Failed to get nodes list " "from datacenter: %s", datacenter_id) raise - for item in nodes['items']: - node = {'id': item['id']} - node.update(item['properties']) - node['state'] = node.pop('vmState') - ret[node['name']] = node + for item in nodes["items"]: + node = {"id": item["id"]} + node.update(item["properties"]) + node["state"] = node.pop("vmState") + ret[node["name"]] = node return ret def list_nodes_full(conn=None, call=None): - ''' + """ Return a list of the VMs that are on the provider, with all fields - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or ' - '--function.' + "The list_nodes_full function must be called with -f or " "--function." ) if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 ret = {} datacenter_id = get_datacenter_id() nodes = conn.list_servers(datacenter_id=datacenter_id, depth=3) - for item in nodes['items']: - node = {'id': item['id']} - node.update(item['properties']) - node['state'] = node.pop('vmState') - node['public_ips'] = [] - node['private_ips'] = [] - if item['entities']['nics']['items'] > 0: - for nic in item['entities']['nics']['items']: - if nic['properties']['ips']: + for item in nodes["items"]: + node = {"id": item["id"]} + node.update(item["properties"]) + node["state"] = node.pop("vmState") + node["public_ips"] = [] + node["private_ips"] = [] + if item["entities"]["nics"]["items"] > 0: + for nic in item["entities"]["nics"]["items"]: + if nic["properties"]["ips"]: pass - ip_address = nic['properties']['ips'][0] + ip_address = nic["properties"]["ips"][0] if salt.utils.cloud.is_public_ip(ip_address): - node['public_ips'].append(ip_address) + node["public_ips"].append(ip_address) else: - node['private_ips'].append(ip_address) + node["private_ips"].append(ip_address) - ret[node['name']] = node + ret[node["name"]] = node - __utils__['cloud.cache_node_list']( - ret, - __active_provider_name__.split(':')[0], - __opts__ + __utils__["cloud.cache_node_list"]( + ret, __active_provider_name__.split(":")[0], __opts__ ) return ret def reserve_ipblock(call=None, kwargs=None): - ''' + """ Reserve the IP Block - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The reserve_ipblock function must be called with -f or ' - '--function.' + "The reserve_ipblock function must be called with -f or " "--function." ) conn = get_conn() @@ -643,134 +610,125 @@ def reserve_ipblock(call=None, kwargs=None): kwargs = {} ret = {} - ret['ips'] = [] + ret["ips"] = [] - if kwargs.get('location') is None: + if kwargs.get("location") is None: raise SaltCloudExecutionFailure('The "location" parameter is required') - location = kwargs.get('location') + location = kwargs.get("location") size = 1 - if kwargs.get('size') is not None: - size = kwargs.get('size') + if kwargs.get("size") is not None: + size = kwargs.get("size") block = conn.reserve_ipblock(IPBlock(size=size, location=location)) - for item in block['properties']['ips']: - ret['ips'].append(item) + for item in block["properties"]["ips"]: + ret["ips"].append(item) return ret def show_instance(name, call=None): - ''' + """ Show the details from the provider concerning an instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) nodes = list_nodes_full() - __utils__['cloud.cache_node']( - nodes[name], - __active_provider_name__, - __opts__ - ) + __utils__["cloud.cache_node"](nodes[name], __active_provider_name__, __opts__) return nodes[name] def get_node(conn, name): - ''' + """ Return a node for the named VM - ''' + """ datacenter_id = get_datacenter_id() - for item in conn.list_servers(datacenter_id)['items']: - if item['properties']['name'] == name: - node = {'id': item['id']} - node.update(item['properties']) + for item in conn.list_servers(datacenter_id)["items"]: + if item["properties"]["name"] == name: + node = {"id": item["id"]} + node.update(item["properties"]) return node def ssh_interface(vm_): - ''' + """ Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. - ''' + """ return config.get_cloud_config_value( - 'ssh_interface', vm_, __opts__, default='public_ips', - search_global=False + "ssh_interface", vm_, __opts__, default="public_ips", search_global=False ) def _get_nics(vm_): - ''' + """ Create network interfaces on appropriate LANs as defined in cloud profile. - ''' + """ nics = [] - if 'public_lan' in vm_: + if "public_lan" in vm_: firewall_rules = [] # Set LAN to public if it already exists, otherwise create a new # public LAN. - if 'public_firewall_rules' in vm_: - firewall_rules = _get_firewall_rules(vm_['public_firewall_rules']) - nic = NIC(lan=set_public_lan(int(vm_['public_lan'])), - name='public', - firewall_rules=firewall_rules) - if 'public_ips' in vm_: - nic.ips = _get_ip_addresses(vm_['public_ips']) + if "public_firewall_rules" in vm_: + firewall_rules = _get_firewall_rules(vm_["public_firewall_rules"]) + nic = NIC( + lan=set_public_lan(int(vm_["public_lan"])), + name="public", + firewall_rules=firewall_rules, + ) + if "public_ips" in vm_: + nic.ips = _get_ip_addresses(vm_["public_ips"]) nics.append(nic) - if 'private_lan' in vm_: + if "private_lan" in vm_: firewall_rules = [] - if 'private_firewall_rules' in vm_: - firewall_rules = _get_firewall_rules(vm_['private_firewall_rules']) - nic = NIC(lan=int(vm_['private_lan']), - name='private', - firewall_rules=firewall_rules) - if 'private_ips' in vm_: - nic.ips = _get_ip_addresses(vm_['private_ips']) - if 'nat' in vm_ and 'private_ips' not in vm_: - nic.nat = vm_['nat'] + if "private_firewall_rules" in vm_: + firewall_rules = _get_firewall_rules(vm_["private_firewall_rules"]) + nic = NIC( + lan=int(vm_["private_lan"]), name="private", firewall_rules=firewall_rules + ) + if "private_ips" in vm_: + nic.ips = _get_ip_addresses(vm_["private_ips"]) + if "nat" in vm_ and "private_ips" not in vm_: + nic.nat = vm_["nat"] nics.append(nic) return nics def set_public_lan(lan_id): - ''' + """ Enables public Internet access for the specified public_lan. If no public LAN is available, then a new public LAN is created. - ''' + """ conn = get_conn() datacenter_id = get_datacenter_id() try: lan = conn.get_lan(datacenter_id=datacenter_id, lan_id=lan_id) - if not lan['properties']['public']: - conn.update_lan(datacenter_id=datacenter_id, - lan_id=lan_id, - public=True) - return lan['id'] + if not lan["properties"]["public"]: + conn.update_lan(datacenter_id=datacenter_id, lan_id=lan_id, public=True) + return lan["id"] except Exception: # pylint: disable=broad-except - lan = conn.create_lan(datacenter_id, - LAN(public=True, - name='Public LAN')) - return lan['id'] + lan = conn.create_lan(datacenter_id, LAN(public=True, name="Public LAN")) + return lan["id"] def get_public_keys(vm_): - ''' + """ Retrieve list of SSH public keys. - ''' + """ key_filename = config.get_cloud_config_value( - 'ssh_public_key', vm_, __opts__, search_global=False, default=None + "ssh_public_key", vm_, __opts__, search_global=False, default=None ) if key_filename is not None: key_filename = os.path.expanduser(key_filename) if not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined ssh_public_key \'{0}\' does not exist'.format( - key_filename - ) + "The defined ssh_public_key '{0}' does not exist".format(key_filename) ) ssh_keys = [] with salt.utils.files.fopen(key_filename) as rfh: @@ -781,66 +739,64 @@ def get_public_keys(vm_): def get_key_filename(vm_): - ''' + """ Check SSH private key file and return absolute path if exists. - ''' + """ key_filename = config.get_cloud_config_value( - 'ssh_private_key', vm_, __opts__, search_global=False, default=None + "ssh_private_key", vm_, __opts__, search_global=False, default=None ) if key_filename is not None: key_filename = os.path.expanduser(key_filename) if not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined ssh_private_key \'{0}\' does not exist'.format( - key_filename - ) + "The defined ssh_private_key '{0}' does not exist".format(key_filename) ) return key_filename def signal_event(vm_, event, description): - args = __utils__['cloud.filter_event']( - event, - vm_, - ['name', 'profile', 'provider', 'driver'] + args = __utils__["cloud.filter_event"]( + event, vm_, ["name", "profile", "provider", "driver"] ) - __utils__['cloud.fire_event']( - 'event', + __utils__["cloud.fire_event"]( + "event", description, - 'salt/cloud/{0}/creating'.format(vm_['name']), + "salt/cloud/{0}/creating".format(vm_["name"]), args=args, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if (vm_['profile'] and - config.is_profile_configured(__opts__, - (__active_provider_name__ or - 'profitbricks'), - vm_['profile']) is False): + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, (__active_provider_name__ or "profitbricks"), vm_["profile"] + ) + is False + ): return False except AttributeError: pass - if 'image_alias' in vm_ and not version_compatible('4.0'): + if "image_alias" in vm_ and not version_compatible("4.0"): raise SaltCloudNotFound( "The 'image_alias' parameter requires the profitbricks " "SDK v4.0.0 or greater." ) - if 'image' not in vm_ and 'image_alias' not in vm_: - log.error('The image or image_alias parameter is required.') + if "image" not in vm_ and "image_alias" not in vm_: + log.error("The image or image_alias parameter is required.") - signal_event(vm_, 'creating', 'starting create') + signal_event(vm_, "creating", "starting create") data = None datacenter_id = get_datacenter_id() @@ -851,72 +807,79 @@ def create(vm_): # Assemble list of volumes from the cloud profile config. volumes = [_get_system_volume(vm_)] - if 'volumes' in vm_: + if "volumes" in vm_: volumes.extend(_get_data_volumes(vm_)) # Assembla the composite server object. server = _get_server(vm_, volumes, nics) - signal_event(vm_, 'requesting', 'requesting instance') + signal_event(vm_, "requesting", "requesting instance") try: data = conn.create_server(datacenter_id=datacenter_id, server=server) log.info( - 'Create server request ID: %s', - data['requestId'], exc_info_on_loglevel=logging.DEBUG + "Create server request ID: %s", + data["requestId"], + exc_info_on_loglevel=logging.DEBUG, ) - _wait_for_completion(conn, data, get_wait_timeout(vm_), - 'create_server') + _wait_for_completion(conn, data, get_wait_timeout(vm_), "create_server") except PBError as exc: log.error( - 'Error creating %s on ProfitBricks\n\n' - 'The following exception was thrown by the profitbricks library ' - 'when trying to run the initial deployment: \n%s', - vm_['name'], exc, exc_info_on_loglevel=logging.DEBUG + "Error creating %s on ProfitBricks\n\n" + "The following exception was thrown by the profitbricks library " + "when trying to run the initial deployment: \n%s", + vm_["name"], + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False except Exception as exc: # pylint: disable=W0703 log.error( - 'Error creating %s \n\nError: \n%s', - vm_['name'], exc, exc_info_on_loglevel=logging.DEBUG + "Error creating %s \n\nError: \n%s", + vm_["name"], + exc, + exc_info_on_loglevel=logging.DEBUG, ) return False - vm_['server_id'] = data['id'] + vm_["server_id"] = data["id"] def __query_node_data(vm_, data): - ''' + """ Query node data until node becomes available. - ''' + """ running = False try: - data = show_instance(vm_['name'], 'action') + data = show_instance(vm_["name"], "action") if not data: return False log.debug( - 'Loaded node data for %s:\nname: %s\nstate: %s', - vm_['name'], pprint.pformat(data['name']), data['state'] + "Loaded node data for %s:\nname: %s\nstate: %s", + vm_["name"], + pprint.pformat(data["name"]), + data["state"], ) except Exception as err: # pylint: disable=broad-except log.error( - 'Failed to get nodes list: %s', err, + "Failed to get nodes list: %s", + err, # Show the trackback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) # Trigger a failure in the wait for IP function return False - running = data['state'] == 'RUNNING' + running = data["state"] == "RUNNING" if not running: # Still not running, trigger another iteration return - if ssh_interface(vm_) == 'private_lan' and data['private_ips']: - vm_['ssh_host'] = data['private_ips'][0] + if ssh_interface(vm_) == "private_lan" and data["private_ips"]: + vm_["ssh_host"] = data["private_ips"][0] - if ssh_interface(vm_) != 'private_lan' and data['public_ips']: - vm_['ssh_host'] = data['public_ips'][0] + if ssh_interface(vm_) != "private_lan" and data["public_ips"]: + vm_["ssh_host"] = data["public_ips"][0] return data @@ -925,36 +888,38 @@ def create(vm_): __query_node_data, update_args=(vm_, data), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10), + "wait_for_ip_interval", vm_, __opts__, default=10 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc.message)) - log.debug('VM is now running') - log.info('Created Cloud VM %s', vm_) - log.debug('%s VM creation details:\n%s', vm_, pprint.pformat(data)) + log.debug("VM is now running") + log.info("Created Cloud VM %s", vm_) + log.debug("%s VM creation details:\n%s", vm_, pprint.pformat(data)) - signal_event(vm_, 'created', 'created instance') + signal_event(vm_, "created", "created instance") - if 'ssh_host' in vm_: - vm_['key_filename'] = get_key_filename(vm_) - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + if "ssh_host" in vm_: + vm_["key_filename"] = get_key_filename(vm_) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data) return ret else: - raise SaltCloudSystemExit('A valid IP address was not found.') + raise SaltCloudSystemExit("A valid IP address was not found.") def destroy(name, call=None): - ''' + """ destroy a machine by name :param name: name given to the machine @@ -968,20 +933,19 @@ def destroy(name, call=None): salt-cloud -d vm_name - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) datacenter_id = get_datacenter_id() @@ -990,50 +954,41 @@ def destroy(name, call=None): attached_volumes = None delete_volumes = config.get_cloud_config_value( - 'delete_volumes', - get_configured_provider(), - __opts__, - search_global=False + "delete_volumes", get_configured_provider(), __opts__, search_global=False ) # Get volumes before the server is deleted attached_volumes = conn.get_attached_volumes( - datacenter_id=datacenter_id, - server_id=node['id'] + datacenter_id=datacenter_id, server_id=node["id"] ) - conn.delete_server(datacenter_id=datacenter_id, server_id=node['id']) + conn.delete_server(datacenter_id=datacenter_id, server_id=node["id"]) # The server is deleted and now is safe to delete the volumes if delete_volumes: - for vol in attached_volumes['items']: - log.debug('Deleting volume %s', vol['id']) - conn.delete_volume( - datacenter_id=datacenter_id, - volume_id=vol['id'] - ) - log.debug('Deleted volume %s', vol['id']) + for vol in attached_volumes["items"]: + log.debug("Deleting volume %s", vol["id"]) + conn.delete_volume(datacenter_id=datacenter_id, volume_id=vol["id"]) + log.debug("Deleted volume %s", vol["id"]) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir']( - name, - __active_provider_name__.split(':')[0], - __opts__ + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ ) return True def reboot(name, call=None): - ''' + """ reboot a machine by name :param name: name given to the machine :param call: call value in this case is 'action' @@ -1044,18 +999,18 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot vm_name - ''' + """ datacenter_id = get_datacenter_id() conn = get_conn() node = get_node(conn, name) - conn.reboot_server(datacenter_id=datacenter_id, server_id=node['id']) + conn.reboot_server(datacenter_id=datacenter_id, server_id=node["id"]) return True def stop(name, call=None): - ''' + """ stop a machine by name :param name: name given to the machine :param call: call value in this case is 'action' @@ -1066,18 +1021,18 @@ def stop(name, call=None): .. code-block:: bash salt-cloud -a stop vm_name - ''' + """ datacenter_id = get_datacenter_id() conn = get_conn() node = get_node(conn, name) - conn.stop_server(datacenter_id=datacenter_id, server_id=node['id']) + conn.stop_server(datacenter_id=datacenter_id, server_id=node["id"]) return True def start(name, call=None): - ''' + """ start a machine by name :param name: name given to the machine :param call: call value in this case is 'action' @@ -1089,126 +1044,124 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start vm_name - ''' + """ datacenter_id = get_datacenter_id() conn = get_conn() node = get_node(conn, name) - conn.start_server(datacenter_id=datacenter_id, server_id=node['id']) + conn.start_server(datacenter_id=datacenter_id, server_id=node["id"]) return True def _override_size(vm_): - ''' + """ Apply any extra component overrides to VM from the cloud profile. - ''' + """ vm_size = get_size(vm_) - if 'cores' in vm_: - vm_size['cores'] = vm_['cores'] + if "cores" in vm_: + vm_size["cores"] = vm_["cores"] - if 'ram' in vm_: - vm_size['ram'] = vm_['ram'] + if "ram" in vm_: + vm_size["ram"] = vm_["ram"] return vm_size def _get_server(vm_, volumes, nics): - ''' + """ Construct server instance from cloud profile config - ''' + """ # Apply component overrides to the size from the cloud profile config vm_size = _override_size(vm_) # Set the server availability zone from the cloud profile config availability_zone = config.get_cloud_config_value( - 'availability_zone', vm_, __opts__, default=None, - search_global=False + "availability_zone", vm_, __opts__, default=None, search_global=False ) # Assign CPU family from the cloud profile config cpu_family = config.get_cloud_config_value( - 'cpu_family', vm_, __opts__, default=None, - search_global=False + "cpu_family", vm_, __opts__, default=None, search_global=False ) # Contruct server object return Server( - name=vm_['name'], - ram=vm_size['ram'], + name=vm_["name"], + ram=vm_size["ram"], availability_zone=availability_zone, - cores=vm_size['cores'], + cores=vm_size["cores"], cpu_family=cpu_family, create_volumes=volumes, - nics=nics + nics=nics, ) def _get_system_volume(vm_): - ''' + """ Construct VM system volume list from cloud profile config - ''' + """ # Override system volume size if 'disk_size' is defined in cloud profile - disk_size = get_size(vm_)['disk'] - if 'disk_size' in vm_: - disk_size = vm_['disk_size'] + disk_size = get_size(vm_)["disk"] + if "disk_size" in vm_: + disk_size = vm_["disk_size"] # Construct the system volume volume = Volume( - name='{0} Storage'.format(vm_['name']), + name="{0} Storage".format(vm_["name"]), size=disk_size, - disk_type=get_disk_type(vm_) + disk_type=get_disk_type(vm_), ) - if 'image_password' in vm_: - image_password = vm_['image_password'] + if "image_password" in vm_: + image_password = vm_["image_password"] volume.image_password = image_password # Retrieve list of SSH public keys ssh_keys = get_public_keys(vm_) volume.ssh_keys = ssh_keys - if 'image_alias' in vm_.keys(): - volume.image_alias = vm_['image_alias'] + if "image_alias" in vm_.keys(): + volume.image_alias = vm_["image_alias"] else: - volume.image = get_image(vm_)['id'] + volume.image = get_image(vm_)["id"] # Set volume availability zone if defined in the cloud profile - if 'disk_availability_zone' in vm_: - volume.availability_zone = vm_['disk_availability_zone'] + if "disk_availability_zone" in vm_: + volume.availability_zone = vm_["disk_availability_zone"] return volume def _get_data_volumes(vm_): - ''' + """ Construct a list of optional data volumes from the cloud profile - ''' + """ ret = [] - volumes = vm_['volumes'] + volumes = vm_["volumes"] for key, value in six.iteritems(volumes): # Verify the required 'disk_size' property is present in the cloud # profile config - if 'disk_size' not in volumes[key].keys(): + if "disk_size" not in volumes[key].keys(): raise SaltCloudConfigError( - 'The volume \'{0}\' is missing \'disk_size\''.format(key) + "The volume '{0}' is missing 'disk_size'".format(key) ) # Use 'HDD' if no 'disk_type' property is present in cloud profile - if 'disk_type' not in volumes[key].keys(): - volumes[key]['disk_type'] = 'HDD' + if "disk_type" not in volumes[key].keys(): + volumes[key]["disk_type"] = "HDD" # Construct volume object and assign to a list. volume = Volume( name=key, - size=volumes[key]['disk_size'], - disk_type=volumes[key]['disk_type'], - licence_type='OTHER' + size=volumes[key]["disk_size"], + disk_type=volumes[key]["disk_type"], + licence_type="OTHER", ) # Set volume availability zone if defined in the cloud profile - if 'disk_availability_zone' in volumes[key].keys(): - volume.availability_zone = volumes[key]['disk_availability_zone'] + if "disk_availability_zone" in volumes[key].keys(): + volume.availability_zone = volumes[key]["disk_availability_zone"] ret.append(volume) @@ -1216,9 +1169,9 @@ def _get_data_volumes(vm_): def _get_ip_addresses(ip_addresses): - ''' + """ Construct a list of ip address - ''' + """ ret = [] for item in ip_addresses: ret.append(item) @@ -1227,57 +1180,60 @@ def _get_ip_addresses(ip_addresses): def _get_firewall_rules(firewall_rules): - ''' + """ Construct a list of optional firewall rules from the cloud profile. - ''' + """ ret = [] for key, value in six.iteritems(firewall_rules): # Verify the required 'protocol' property is present in the cloud # profile config - if 'protocol' not in firewall_rules[key].keys(): + if "protocol" not in firewall_rules[key].keys(): raise SaltCloudConfigError( - 'The firewall rule \'{0}\' is missing \'protocol\''.format(key) + "The firewall rule '{0}' is missing 'protocol'".format(key) ) - ret.append(FirewallRule( - name=key, - protocol=firewall_rules[key].get('protocol', None), - source_mac=firewall_rules[key].get('source_mac', None), - source_ip=firewall_rules[key].get('source_ip', None), - target_ip=firewall_rules[key].get('target_ip', None), - port_range_start=firewall_rules[key].get('port_range_start', None), - port_range_end=firewall_rules[key].get('port_range_end', None), - icmp_type=firewall_rules[key].get('icmp_type', None), - icmp_code=firewall_rules[key].get('icmp_code', None) - )) + ret.append( + FirewallRule( + name=key, + protocol=firewall_rules[key].get("protocol", None), + source_mac=firewall_rules[key].get("source_mac", None), + source_ip=firewall_rules[key].get("source_ip", None), + target_ip=firewall_rules[key].get("target_ip", None), + port_range_start=firewall_rules[key].get("port_range_start", None), + port_range_end=firewall_rules[key].get("port_range_end", None), + icmp_type=firewall_rules[key].get("icmp_type", None), + icmp_code=firewall_rules[key].get("icmp_code", None), + ) + ) return ret def _wait_for_completion(conn, promise, wait_timeout, msg): - ''' + """ Poll request status until resource is provisioned. - ''' + """ if not promise: return wait_timeout = time.time() + wait_timeout while wait_timeout > time.time(): time.sleep(5) operation_result = conn.get_request( - request_id=promise['requestId'], - status=True) + request_id=promise["requestId"], status=True + ) - if operation_result['metadata']['status'] == "DONE": + if operation_result["metadata"]["status"] == "DONE": return - elif operation_result['metadata']['status'] == "FAILED": + elif operation_result["metadata"]["status"] == "FAILED": raise Exception( "Request: {0}, requestId: {1} failed to complete:\n{2}".format( - msg, six.text_type(promise['requestId']), - operation_result['metadata']['message'] + msg, + six.text_type(promise["requestId"]), + operation_result["metadata"]["message"], ) ) raise Exception( 'Timed out waiting for asynchronous operation {0} "{1}" to complete.'.format( - msg, six.text_type(promise['requestId']) + msg, six.text_type(promise["requestId"]) ) ) diff --git a/salt/cloud/clouds/proxmox.py b/salt/cloud/clouds/proxmox.py index d41f3c4d2a7..4ee12f517a7 100644 --- a/salt/cloud/clouds/proxmox.py +++ b/salt/cloud/clouds/proxmox.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Proxmox Cloud Module ====================== @@ -24,38 +24,42 @@ Set up the cloud configuration at ``/etc/salt/cloud.providers`` or :maintainer: Frank Klaassen :depends: requests >= 2.2.1 :depends: IPy >= 0.81 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time -import pprint + import logging +import pprint import re +import time + +# Import salt cloud libs +import salt.config as config # Import salt libs import salt.utils.cloud import salt.utils.json - -# Import salt cloud libs -import salt.config as config from salt.exceptions import ( - SaltCloudSystemExit, SaltCloudExecutionFailure, - SaltCloudExecutionTimeout + SaltCloudExecutionTimeout, + SaltCloudSystemExit, ) # Import 3rd-party Libs from salt.ext import six from salt.ext.six.moves import range + try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False try: from IPy import IP + HAS_IPY = True except ImportError: HAS_IPY = False @@ -63,13 +67,13 @@ except ImportError: # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'proxmox' +__virtualname__ = "proxmox" def __virtual__(): - ''' + """ Check for PROXMOX configurations - ''' + """ if get_configured_provider() is False: return False @@ -80,28 +84,20 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('user',) + __opts__, __active_provider_name__ or __virtualname__, ("user",) ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - deps = { - 'requests': HAS_REQUESTS, - 'IPy': HAS_IPY - } - return config.check_driver_dependencies( - __virtualname__, - deps - ) + """ + deps = {"requests": HAS_REQUESTS, "IPy": HAS_IPY} + return config.check_driver_dependencies(__virtualname__, deps) url = None @@ -112,89 +108,103 @@ api = None def _authenticate(): - ''' + """ Retrieve CSRF and API tickets for the Proxmox API - ''' + """ global url, ticket, csrf, verify_ssl url = config.get_cloud_config_value( - 'url', get_configured_provider(), __opts__, search_global=False + "url", get_configured_provider(), __opts__, search_global=False + ) + username = ( + config.get_cloud_config_value( + "user", get_configured_provider(), __opts__, search_global=False + ), ) - username = config.get_cloud_config_value( - 'user', get_configured_provider(), __opts__, search_global=False - ), passwd = config.get_cloud_config_value( - 'password', get_configured_provider(), __opts__, search_global=False + "password", 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 + "verify_ssl", + get_configured_provider(), + __opts__, + default=True, + search_global=False, ) - connect_data = {'username': username, 'password': passwd} - full_url = 'https://{0}:8006/api2/json/access/ticket'.format(url) + connect_data = {"username": username, "password": passwd} + full_url = "https://{0}:8006/api2/json/access/ticket".format(url) - returned_data = requests.post( - full_url, verify=verify_ssl, data=connect_data).json() + returned_data = requests.post(full_url, verify=verify_ssl, data=connect_data).json() - ticket = {'PVEAuthCookie': returned_data['data']['ticket']} - csrf = six.text_type(returned_data['data']['CSRFPreventionToken']) + ticket = {"PVEAuthCookie": returned_data["data"]["ticket"]} + csrf = six.text_type(returned_data["data"]["CSRFPreventionToken"]) def query(conn_type, option, post_data=None): - ''' + """ Execute the HTTP request to the API - ''' + """ if ticket is None or csrf is None or url is None: - log.debug('Not authenticated yet, doing that now..') + log.debug("Not authenticated yet, doing that now..") _authenticate() - full_url = 'https://{0}:8006/api2/json/{1}'.format(url, option) + full_url = "https://{0}:8006/api2/json/{1}".format(url, option) - log.debug('%s: %s (%s)', conn_type, full_url, post_data) + log.debug("%s: %s (%s)", conn_type, full_url, post_data) - httpheaders = {'Accept': 'application/json', - 'Content-Type': 'application/x-www-form-urlencoded', - 'User-Agent': 'salt-cloud-proxmox'} + httpheaders = { + "Accept": "application/json", + "Content-Type": "application/x-www-form-urlencoded", + "User-Agent": "salt-cloud-proxmox", + } - if conn_type == 'post': - httpheaders['CSRFPreventionToken'] = csrf - response = requests.post(full_url, verify=verify_ssl, - data=post_data, - cookies=ticket, - headers=httpheaders) - elif conn_type == 'put': - httpheaders['CSRFPreventionToken'] = csrf - response = requests.put(full_url, verify=verify_ssl, - data=post_data, - cookies=ticket, - headers=httpheaders) - elif conn_type == 'delete': - httpheaders['CSRFPreventionToken'] = csrf - response = requests.delete(full_url, verify=verify_ssl, - data=post_data, - cookies=ticket, - headers=httpheaders) - elif conn_type == 'get': - response = requests.get(full_url, verify=verify_ssl, - cookies=ticket) + if conn_type == "post": + httpheaders["CSRFPreventionToken"] = csrf + response = requests.post( + full_url, + verify=verify_ssl, + data=post_data, + cookies=ticket, + headers=httpheaders, + ) + elif conn_type == "put": + httpheaders["CSRFPreventionToken"] = csrf + response = requests.put( + full_url, + verify=verify_ssl, + data=post_data, + cookies=ticket, + headers=httpheaders, + ) + elif conn_type == "delete": + httpheaders["CSRFPreventionToken"] = csrf + response = requests.delete( + full_url, + verify=verify_ssl, + data=post_data, + cookies=ticket, + headers=httpheaders, + ) + elif conn_type == "get": + response = requests.get(full_url, verify=verify_ssl, cookies=ticket) response.raise_for_status() try: returned_data = response.json() - if 'data' not in returned_data: + if "data" not in returned_data: raise SaltCloudExecutionFailure - return returned_data['data'] + return returned_data["data"] except Exception: # pylint: disable=broad-except - log.error('Error in trying to process JSON') + log.error("Error in trying to process JSON") log.error(response) def _get_vm_by_name(name, allDetails=False): - ''' + """ Since Proxmox works based op id's rather than names as identifiers this requires some filtering to retrieve the required information. - ''' + """ vms = get_resources_vms(includeConfig=allDetails) if name in vms: return vms[name] @@ -204,11 +214,13 @@ def _get_vm_by_name(name, allDetails=False): def _get_vm_by_id(vmid, allDetails=False): - ''' + """ Retrieve a VM based on the ID. - ''' - for vm_name, vm_details in six.iteritems(get_resources_vms(includeConfig=allDetails)): - if six.text_type(vm_details['vmid']) == six.text_type(vmid): + """ + for vm_name, vm_details in six.iteritems( + get_resources_vms(includeConfig=allDetails) + ): + if six.text_type(vm_details["vmid"]) == six.text_type(vmid): return vm_details log.info('VM with ID "%s" could not be found.', vmid) @@ -216,102 +228,101 @@ def _get_vm_by_id(vmid, allDetails=False): def _get_next_vmid(): - ''' + """ Proxmox allows the use of alternative ids instead of autoincrementing. Because of that its required to query what the first available ID is. - ''' - return int(query('get', 'cluster/nextid')) + """ + return int(query("get", "cluster/nextid")) def _check_ip_available(ip_addr): - ''' + """ Proxmox VMs refuse to start when the IP is already being used. This function can be used to prevent VMs being created with duplicate IP's or to generate a warning. - ''' + """ for vm_name, vm_details in six.iteritems(get_resources_vms(includeConfig=True)): - vm_config = vm_details['config'] - if ip_addr in vm_config['ip_address'] or vm_config['ip_address'] == ip_addr: + vm_config = vm_details["config"] + if ip_addr in vm_config["ip_address"] or vm_config["ip_address"] == ip_addr: log.debug('IP "%s" is already defined', ip_addr) return False - log.debug('IP \'%s\' is available to be defined', ip_addr) + log.debug("IP '%s' is available to be defined", ip_addr) return True def _parse_proxmox_upid(node, vm_=None): - ''' + """ Upon requesting a task that runs for a longer period of time a UPID is given. This includes information about the job and can be used to lookup information in the log. - ''' + """ ret = {} upid = node # Parse node response - node = node.split(':') - if node[0] == 'UPID': - ret['node'] = six.text_type(node[1]) - ret['pid'] = six.text_type(node[2]) - ret['pstart'] = six.text_type(node[3]) - ret['starttime'] = six.text_type(node[4]) - ret['type'] = six.text_type(node[5]) - ret['vmid'] = six.text_type(node[6]) - ret['user'] = six.text_type(node[7]) + node = node.split(":") + if node[0] == "UPID": + ret["node"] = six.text_type(node[1]) + ret["pid"] = six.text_type(node[2]) + ret["pstart"] = six.text_type(node[3]) + ret["starttime"] = six.text_type(node[4]) + ret["type"] = six.text_type(node[5]) + ret["vmid"] = six.text_type(node[6]) + ret["user"] = six.text_type(node[7]) # include the upid again in case we'll need it again - ret['upid'] = six.text_type(upid) + ret["upid"] = six.text_type(upid) - if vm_ is not None and 'technology' in vm_: - ret['technology'] = six.text_type(vm_['technology']) + if vm_ is not None and "technology" in vm_: + ret["technology"] = six.text_type(vm_["technology"]) return ret def _lookup_proxmox_task(upid): - ''' + """ Retrieve the (latest) logs and retrieve the status for a UPID. This can be used to verify whether a task has completed. - ''' - log.debug('Getting creation status for upid: %s', upid) - tasks = query('get', 'cluster/tasks') + """ + log.debug("Getting creation status for upid: %s", upid) + tasks = query("get", "cluster/tasks") if tasks: for task in tasks: - if task['upid'] == upid: - log.debug('Found upid task: %s', task) + if task["upid"] == upid: + log.debug("Found upid task: %s", task) return task return False def get_resources_nodes(call=None, resFilter=None): - ''' + """ Retrieve all hypervisors (nodes) available on this environment CLI Example: .. code-block:: bash salt-cloud -f get_resources_nodes my-proxmox-config - ''' - log.debug('Getting resource: nodes.. (filter: %s)', resFilter) - resources = query('get', 'cluster/resources') + """ + log.debug("Getting resource: nodes.. (filter: %s)", resFilter) + resources = query("get", "cluster/resources") ret = {} for resource in resources: - if 'type' in resource and resource['type'] == 'node': - name = resource['node'] + if "type" in resource and resource["type"] == "node": + name = resource["node"] ret[name] = resource if resFilter is not None: - log.debug('Filter given: %s, returning requested ' - 'resource: nodes', resFilter) + log.debug("Filter given: %s, returning requested " "resource: nodes", resFilter) return ret[resFilter] - log.debug('Filter not given: %s, returning all resource: nodes', ret) + log.debug("Filter not given: %s, returning all resource: nodes", ret) return ret def get_resources_vms(call=None, resFilter=None, includeConfig=True): - ''' + """ Retrieve all VMs available on this environment CLI Example: @@ -319,40 +330,37 @@ def get_resources_vms(call=None, resFilter=None, includeConfig=True): .. code-block:: bash salt-cloud -f get_resources_vms my-proxmox-config - ''' - log.debug('Getting resource: vms.. (filter: %s)', resFilter) - resources = query('get', 'cluster/resources') + """ + log.debug("Getting resource: vms.. (filter: %s)", resFilter) + resources = query("get", "cluster/resources") ret = {} for resource in resources: - if 'type' in resource and resource['type'] in ['openvz', 'qemu', 'lxc']: - name = resource['name'] + if "type" in resource and resource["type"] in ["openvz", "qemu", "lxc"]: + name = resource["name"] ret[name] = resource if includeConfig: # Requested to include the detailed configuration of a VM - ret[name]['config'] = get_vmconfig( - ret[name]['vmid'], - ret[name]['node'], - ret[name]['type'] + ret[name]["config"] = get_vmconfig( + ret[name]["vmid"], ret[name]["node"], ret[name]["type"] ) if resFilter is not None: - log.debug('Filter given: %s, returning requested ' - 'resource: nodes', resFilter) + log.debug("Filter given: %s, returning requested " "resource: nodes", resFilter) return ret[resFilter] - log.debug('Filter not given: %s, returning all resource: nodes', ret) + log.debug("Filter not given: %s, returning all resource: nodes", ret) return ret def script(vm_): - ''' + """ Return the script deployment object - ''' - script_name = config.get_cloud_config_value('script', vm_, __opts__) + """ + script_name = config.get_cloud_config_value("script", vm_, __opts__) if not script_name: - script_name = 'bootstrap-salt' + script_name = "bootstrap-salt" return salt.utils.cloud.os_script( script_name, @@ -360,12 +368,12 @@ def script(vm_): __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) def avail_locations(call=None): - ''' + """ Return a list of the hypervisors (nodes) which this Proxmox PVE machine manages CLI Example: @@ -373,26 +381,26 @@ def avail_locations(call=None): .. code-block:: bash salt-cloud --list-locations my-proxmox-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) # could also use the get_resources_nodes but speed is ~the same - nodes = query('get', 'nodes') + nodes = query("get", "nodes") ret = {} for node in nodes: - name = node['node'] + name = node["node"] ret[name] = node return ret -def avail_images(call=None, location='local'): - ''' +def avail_images(call=None, location="local"): + """ Return a list of the images that are on the provider CLI Example: @@ -400,22 +408,24 @@ def avail_images(call=None, location='local'): .. code-block:: bash salt-cloud --list-images my-proxmox-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) ret = {} for host_name, host_details in six.iteritems(avail_locations()): - for item in query('get', 'nodes/{0}/storage/{1}/content'.format(host_name, location)): - ret[item['volid']] = item + for item in query( + "get", "nodes/{0}/storage/{1}/content".format(host_name, location) + ): + ret[item["volid"]] = item return ret def list_nodes(call=None): - ''' + """ Return a list of the VMs that are managed by the provider CLI Example: @@ -423,44 +433,47 @@ def list_nodes(call=None): .. code-block:: bash salt-cloud -Q my-proxmox-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} for vm_name, vm_details in six.iteritems(get_resources_vms(includeConfig=True)): - log.debug('VM_Name: %s', vm_name) - log.debug('vm_details: %s', vm_details) + log.debug("VM_Name: %s", vm_name) + log.debug("vm_details: %s", vm_details) # Limit resultset on what Salt-cloud demands: ret[vm_name] = {} - ret[vm_name]['id'] = six.text_type(vm_details['vmid']) - ret[vm_name]['image'] = six.text_type(vm_details['vmid']) - ret[vm_name]['size'] = six.text_type(vm_details['disk']) - ret[vm_name]['state'] = six.text_type(vm_details['status']) + ret[vm_name]["id"] = six.text_type(vm_details["vmid"]) + ret[vm_name]["image"] = six.text_type(vm_details["vmid"]) + ret[vm_name]["size"] = six.text_type(vm_details["disk"]) + ret[vm_name]["state"] = six.text_type(vm_details["status"]) # Figure out which is which to put it in the right column private_ips = [] public_ips = [] - if 'ip_address' in vm_details['config'] and vm_details['config']['ip_address'] != '-': - ips = vm_details['config']['ip_address'].split(' ') + if ( + "ip_address" in vm_details["config"] + and vm_details["config"]["ip_address"] != "-" + ): + ips = vm_details["config"]["ip_address"].split(" ") for ip_ in ips: - if IP(ip_).iptype() == 'PRIVATE': + if IP(ip_).iptype() == "PRIVATE": private_ips.append(six.text_type(ip_)) else: public_ips.append(six.text_type(ip_)) - ret[vm_name]['private_ips'] = private_ips - ret[vm_name]['public_ips'] = public_ips + ret[vm_name]["private_ips"] = private_ips + ret[vm_name]["public_ips"] = public_ips return ret def list_nodes_full(call=None): - ''' + """ Return a list of the VMs that are on the provider CLI Example: @@ -468,17 +481,17 @@ def list_nodes_full(call=None): .. code-block:: bash salt-cloud -F my-proxmox-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) return get_resources_vms(includeConfig=True) def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields CLI Example: @@ -486,62 +499,66 @@ def list_nodes_select(call=None): .. code-block:: bash salt-cloud -S my-proxmox-config - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full(), __opts__['query.selection'], call, + list_nodes_full(), __opts__["query.selection"], call, ) def _stringlist_to_dictionary(input_string): - ''' + """ Convert a stringlist (comma separated settings) to a dictionary The result of the string setting1=value1,setting2=value2 will be a python dictionary: {'setting1':'value1','setting2':'value2'} - ''' + """ return dict(item.strip().split("=") for item in input_string.split(",") if item) def _dictionary_to_stringlist(input_dict): - ''' + """ Convert a dictionary to a stringlist (comma separated settings) The result of the dictionary {'setting1':'value1','setting2':'value2'} will be: setting1=value1,setting2=value2 - ''' - return ','.join('{}={}'.format(k, input_dict[k]) for k in sorted(input_dict.keys())) + """ + return ",".join("{}={}".format(k, input_dict[k]) for k in sorted(input_dict.keys())) def _reconfigure_clone(vm_, vmid): - ''' + """ If we cloned a machine, see if we need to reconfigure any of the options such as net0, ide2, etc. This enables us to have a different cloud-init ISO mounted for each VM that's brought up :param vm_: :return: - ''' - if not vm_.get('technology') == 'qemu': - log.warning('Reconfiguring clones is only available under `qemu`') + """ + if not vm_.get("technology") == "qemu": + log.warning("Reconfiguring clones is only available under `qemu`") return # TODO: Support other settings here too as these are not the only ones that can be modified after a clone operation - log.info('Configuring cloned VM') + log.info("Configuring cloned VM") # Modify the settings for the VM one at a time so we can see any problems with the values # as quickly as possible for setting in vm_: - if re.match(r'^(ide|sata|scsi)(\d+)$', setting): + if re.match(r"^(ide|sata|scsi)(\d+)$", setting): postParams = {setting: vm_[setting]} - query('post', 'nodes/{0}/qemu/{1}/config'.format(vm_['host'], vmid), postParams) + query( + "post", + "nodes/{0}/qemu/{1}/config".format(vm_["host"], vmid), + postParams, + ) - elif re.match(r'^net(\d+)$', setting): + elif re.match(r"^net(\d+)$", setting): # net strings are a list of comma seperated settings. We need to merge the settings so that # the setting in the profile only changes the settings it touches and the other settings # are left alone. An example of why this is necessary is because the MAC address is set # in here and generally you don't want to alter or have to know the MAC address of the new # instance, but you may want to set the VLAN bridge - data = query('get', 'nodes/{0}/qemu/{1}/config'.format(vm_['host'], vmid)) + data = query("get", "nodes/{0}/qemu/{1}/config".format(vm_["host"], vmid)) # Generate a dictionary of settings from the existing string new_setting = {} @@ -554,11 +571,15 @@ def _reconfigure_clone(vm_, vmid): # Convert the dictionary back into a string list postParams = {setting: _dictionary_to_stringlist(new_setting)} - query('post', 'nodes/{0}/qemu/{1}/config'.format(vm_['host'], vmid), postParams) + query( + "post", + "nodes/{0}/qemu/{1}/config".format(vm_["host"], vmid), + postParams, + ) def create(vm_): - ''' + """ Create a single VM from a data dict CLI Example: @@ -566,168 +587,172 @@ def create(vm_): .. code-block:: bash salt-cloud -p proxmox-ubuntu vmhostname - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'proxmox', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "proxmox", vm_["profile"], vm_=vm_ + ) + is False + ): return False except AttributeError: pass ret = {} - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) - if 'use_dns' in vm_ and 'ip_address' not in vm_: - use_dns = vm_['use_dns'] + if "use_dns" in vm_ and "ip_address" not in vm_: + use_dns = vm_["use_dns"] if use_dns: from socket import gethostbyname, gaierror + try: - ip_address = gethostbyname(six.text_type(vm_['name'])) + ip_address = gethostbyname(six.text_type(vm_["name"])) except gaierror: - log.debug('Resolving of %s failed', vm_['name']) + log.debug("Resolving of %s failed", vm_["name"]) else: - vm_['ip_address'] = six.text_type(ip_address) + vm_["ip_address"] = six.text_type(ip_address) try: newid = _get_next_vmid() data = create_node(vm_, newid) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on PROXMOX\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: \n%s', - vm_['name'], exc, + "Error creating %s on PROXMOX\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: \n%s", + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False - ret['creation_data'] = data - name = vm_['name'] # hostname which we know - if 'clone' in vm_ and vm_['clone'] is True: + ret["creation_data"] = data + name = vm_["name"] # hostname which we know + if "clone" in vm_ and vm_["clone"] is True: vmid = newid else: - vmid = data['vmid'] # vmid which we have received - host = data['node'] # host which we have received - nodeType = data['technology'] # VM tech (Qemu / OpenVZ) + vmid = data["vmid"] # vmid which we have received + host = data["node"] # host which we have received + nodeType = data["technology"] # VM tech (Qemu / OpenVZ) # Determine which IP to use in order of preference: - if 'ip_address' in vm_: - ip_address = six.text_type(vm_['ip_address']) - elif 'public_ips' in data: - ip_address = six.text_type(data['public_ips'][0]) # first IP - elif 'private_ips' in data: - ip_address = six.text_type(data['private_ips'][0]) # first IP + if "ip_address" in vm_: + ip_address = six.text_type(vm_["ip_address"]) + elif "public_ips" in data: + ip_address = six.text_type(data["public_ips"][0]) # first IP + elif "private_ips" in data: + ip_address = six.text_type(data["private_ips"][0]) # first IP else: raise SaltCloudExecutionFailure # err.. not a good idea i reckon - log.debug('Using IP address %s', ip_address) + log.debug("Using IP address %s", ip_address) # wait until the vm has been created so we can start it - if not wait_for_created(data['upid'], timeout=300): - return {'Error': 'Unable to create {0}, command timed out'.format(name)} + if not wait_for_created(data["upid"], timeout=300): + return {"Error": "Unable to create {0}, command timed out".format(name)} - if vm_.get('clone') is True: + if vm_.get("clone") is True: _reconfigure_clone(vm_, vmid) # VM has been created. Starting.. - if not start(name, vmid, call='action'): - log.error('Node %s (%s) failed to start!', name, vmid) + if not start(name, vmid, call="action"): + log.error("Node %s (%s) failed to start!", name, vmid) raise SaltCloudExecutionFailure # Wait until the VM has fully started log.debug('Waiting for state "running" for vm %s on %s', vmid, host) - if not wait_for_state(vmid, 'running'): - return {'Error': 'Unable to start {0}, command timed out'.format(name)} + if not wait_for_state(vmid, "running"): + return {"Error": "Unable to start {0}, command timed out".format(name)} ssh_username = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, default='root' - ) - ssh_password = config.get_cloud_config_value( - 'password', vm_, __opts__, + "ssh_username", vm_, __opts__, default="root" ) + ssh_password = config.get_cloud_config_value("password", vm_, __opts__,) - ret['ip_address'] = ip_address - ret['username'] = ssh_username - ret['password'] = ssh_password + ret["ip_address"] = ip_address + ret["username"] = ssh_username + ret["password"] = ssh_password - vm_['ssh_host'] = ip_address - vm_['password'] = ssh_password - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + vm_["ssh_host"] = ip_address + vm_["password"] = ssh_password + ret = __utils__["cloud.bootstrap"](vm_, __opts__) # Report success! - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], ) return ret def _import_api(): - ''' + """ Download https:///pve-docs/api-viewer/apidoc.js Extract content of pveapi var (json formated) Load this json content into global variable "api" - ''' + """ global api - full_url = 'https://{0}:8006/pve-docs/api-viewer/apidoc.js'.format(url) + full_url = "https://{0}:8006/pve-docs/api-viewer/apidoc.js".format(url) returned_data = requests.get(full_url, verify=verify_ssl) - re_filter = re.compile('(?<=pveapi =)(.*)(?=^;)', re.DOTALL | re.MULTILINE) + re_filter = re.compile("(?<=pveapi =)(.*)(?=^;)", re.DOTALL | re.MULTILINE) api_json = re_filter.findall(returned_data.text)[0] api = salt.utils.json.loads(api_json) def _get_properties(path="", method="GET", forced_params=None): - ''' + """ Return the parameter list from api for defined path and HTTP method - ''' + """ if api is None: _import_api() sub = api - path_levels = [level for level in path.split('/') if level != ''] - search_path = '' + path_levels = [level for level in path.split("/") if level != ""] + search_path = "" props = [] parameters = set([] if forced_params is None else forced_params) # Browse all path elements but last for elem in path_levels[:-1]: - search_path += '/' + elem + search_path += "/" + elem # Lookup for a dictionary with path = "requested path" in list" and return its children - sub = next((item for item in sub if item["path"] == search_path))['children'] + sub = next((item for item in sub if item["path"] == search_path))["children"] # Get leaf element in path - search_path += '/' + path_levels[-1] + search_path += "/" + path_levels[-1] sub = next((item for item in sub if item["path"] == search_path)) try: # get list of properties for requested method - props = sub['info'][method]['parameters']['properties'].keys() + props = sub["info"][method]["parameters"]["properties"].keys() except KeyError as exc: log.error('method not found: "%s"', exc) for prop in props: - numerical = re.match(r'(\w+)\[n\]', prop) + numerical = re.match(r"(\w+)\[n\]", prop) # generate (arbitrarily) 10 properties for duplicatable properties identified by: # "prop[n]" if numerical: @@ -739,194 +764,240 @@ def _get_properties(path="", method="GET", forced_params=None): def create_node(vm_, newid): - ''' + """ Build and submit the requestdata to create a new node - ''' + """ newnode = {} - if 'technology' not in vm_: - vm_['technology'] = 'openvz' # default virt tech if none is given + if "technology" not in vm_: + vm_["technology"] = "openvz" # default virt tech if none is given - if vm_['technology'] not in ['qemu', 'openvz', 'lxc']: + if vm_["technology"] not in ["qemu", "openvz", "lxc"]: # Wrong VM type given - log.error('Wrong VM type. Valid options are: qemu, openvz (proxmox3) or lxc (proxmox4)') + log.error( + "Wrong VM type. Valid options are: qemu, openvz (proxmox3) or lxc (proxmox4)" + ) raise SaltCloudExecutionFailure - if 'host' not in vm_: + if "host" not in vm_: # Use globally configured/default location - vm_['host'] = config.get_cloud_config_value( - 'default_host', get_configured_provider(), __opts__, search_global=False + vm_["host"] = config.get_cloud_config_value( + "default_host", get_configured_provider(), __opts__, search_global=False ) - if vm_['host'] is None: + if vm_["host"] is None: # No location given for the profile - log.error('No host given to create this VM on') + log.error("No host given to create this VM on") raise SaltCloudExecutionFailure # Required by both OpenVZ and Qemu (KVM) - vmhost = vm_['host'] - newnode['vmid'] = newid + vmhost = vm_["host"] + newnode["vmid"] = newid - for prop in 'cpuunits', 'description', 'memory', 'onboot': + for prop in "cpuunits", "description", "memory", "onboot": if prop in vm_: # if the property is set, use it for the VM request newnode[prop] = vm_[prop] - if vm_['technology'] == 'openvz': + if vm_["technology"] == "openvz": # OpenVZ related settings, using non-default names: - newnode['hostname'] = vm_['name'] - newnode['ostemplate'] = vm_['image'] + newnode["hostname"] = vm_["name"] + newnode["ostemplate"] = vm_["image"] # optional VZ settings - for prop in 'cpus', 'disk', 'ip_address', 'nameserver', 'password', 'swap', 'poolid', 'storage': + for prop in ( + "cpus", + "disk", + "ip_address", + "nameserver", + "password", + "swap", + "poolid", + "storage", + ): if prop in vm_: # if the property is set, use it for the VM request newnode[prop] = vm_[prop] - elif vm_['technology'] == 'lxc': + elif vm_["technology"] == "lxc": # LXC related settings, using non-default names: - newnode['hostname'] = vm_['name'] - newnode['ostemplate'] = vm_['image'] + newnode["hostname"] = vm_["name"] + newnode["ostemplate"] = vm_["image"] - static_props = ('cpuunits', 'cpulimit', 'rootfs', 'cores', 'description', 'memory', 'onboot', 'net0', - 'password', 'nameserver', 'swap', 'storage', 'rootfs') - for prop in _get_properties('/nodes/{node}/lxc', - 'POST', - static_props): + static_props = ( + "cpuunits", + "cpulimit", + "rootfs", + "cores", + "description", + "memory", + "onboot", + "net0", + "password", + "nameserver", + "swap", + "storage", + "rootfs", + ) + for prop in _get_properties("/nodes/{node}/lxc", "POST", static_props): if prop in vm_: # if the property is set, use it for the VM request newnode[prop] = vm_[prop] - if 'pubkey' in vm_: - newnode['ssh-public-keys'] = vm_['pubkey'] + if "pubkey" in vm_: + newnode["ssh-public-keys"] = vm_["pubkey"] # inform user the "disk" option is not supported for LXC hosts - if 'disk' in vm_: - log.warning('The "disk" option is not supported for LXC hosts and was ignored') + if "disk" in vm_: + log.warning( + 'The "disk" option is not supported for LXC hosts and was ignored' + ) # LXC specific network config # OpenVZ allowed specifying IP and gateway. To ease migration from # Proxmox 3, I've mapped the ip_address and gw to a generic net0 config. # If you need more control, please use the net0 option directly. # This also assumes a /24 subnet. - if 'ip_address' in vm_ and 'net0' not in vm_: - newnode['net0'] = 'bridge=vmbr0,ip=' + vm_['ip_address'] + '/24,name=eth0,type=veth' + if "ip_address" in vm_ and "net0" not in vm_: + newnode["net0"] = ( + "bridge=vmbr0,ip=" + vm_["ip_address"] + "/24,name=eth0,type=veth" + ) # gateway is optional and does not assume a default - if 'gw' in vm_: - newnode['net0'] = newnode['net0'] + ',gw=' + vm_['gw'] + if "gw" in vm_: + newnode["net0"] = newnode["net0"] + ",gw=" + vm_["gw"] - elif vm_['technology'] == 'qemu': + elif vm_["technology"] == "qemu": # optional Qemu settings - static_props = ('acpi', 'cores', 'cpu', 'pool', 'storage', 'sata0', 'ostype', 'ide2', 'net0') - for prop in _get_properties('/nodes/{node}/qemu', - 'POST', - static_props): + static_props = ( + "acpi", + "cores", + "cpu", + "pool", + "storage", + "sata0", + "ostype", + "ide2", + "net0", + ) + for prop in _get_properties("/nodes/{node}/qemu", "POST", static_props): if prop in vm_: # if the property is set, use it for the VM request newnode[prop] = vm_[prop] # The node is ready. Lets request it to be added - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', newnode, list(newnode)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", newnode, list(newnode) + ), }, - sock_dir=__opts__['sock_dir'], + sock_dir=__opts__["sock_dir"], ) - log.debug('Preparing to generate a node using these parameters: %s ', newnode) - if 'clone' in vm_ and vm_['clone'] is True and vm_['technology'] == 'qemu': + log.debug("Preparing to generate a node using these parameters: %s ", newnode) + if "clone" in vm_ and vm_["clone"] is True and vm_["technology"] == "qemu": postParams = {} - postParams['newid'] = newnode['vmid'] + postParams["newid"] = newnode["vmid"] - for prop in 'description', 'format', 'full', 'name': - if 'clone_' + prop in vm_: # if the property is set, use it for the VM request - postParams[prop] = vm_['clone_' + prop] + for prop in "description", "format", "full", "name": + if ( + "clone_" + prop in vm_ + ): # if the property is set, use it for the VM request + postParams[prop] = vm_["clone_" + prop] - node = query('post', 'nodes/{0}/qemu/{1}/clone'.format(vmhost, vm_['clone_from']), postParams) + node = query( + "post", + "nodes/{0}/qemu/{1}/clone".format(vmhost, vm_["clone_from"]), + postParams, + ) else: - node = query('post', 'nodes/{0}/{1}'.format(vmhost, vm_['technology']), newnode) + node = query("post", "nodes/{0}/{1}".format(vmhost, vm_["technology"]), newnode) return _parse_proxmox_upid(node, vm_) def show_instance(name, call=None): - ''' + """ Show the details from Proxmox concerning an instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) nodes = list_nodes_full() - __utils__['cloud.cache_node'](nodes[name], __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](nodes[name], __active_provider_name__, __opts__) return nodes[name] -def get_vmconfig(vmid, node=None, node_type='openvz'): - ''' +def get_vmconfig(vmid, node=None, node_type="openvz"): + """ Get VM configuration - ''' + """ if node is None: # We need to figure out which node this VM is on. for host_name, host_details in six.iteritems(avail_locations()): - for item in query('get', 'nodes/{0}/{1}'.format(host_name, node_type)): - if item['vmid'] == vmid: + for item in query("get", "nodes/{0}/{1}".format(host_name, node_type)): + if item["vmid"] == vmid: node = host_name # If we reached this point, we have all the information we need - data = query('get', 'nodes/{0}/{1}/{2}/config'.format(node, node_type, vmid)) + data = query("get", "nodes/{0}/{1}/{2}/config".format(node, node_type, vmid)) return data def wait_for_created(upid, timeout=300): - ''' + """ Wait until a the vm has been created successfully - ''' + """ start_time = time.time() info = _lookup_proxmox_task(upid) if not info: - log.error('wait_for_created: No task information ' - 'retrieved based on given criteria.') + log.error( + "wait_for_created: No task information " + "retrieved based on given criteria." + ) raise SaltCloudExecutionFailure while True: - if 'status' in info and info['status'] == 'OK': - log.debug('Host has been created!') + if "status" in info and info["status"] == "OK": + log.debug("Host has been created!") return True time.sleep(3) # Little more patience, we're not in a hurry if time.time() - start_time > timeout: - log.debug('Timeout reached while waiting for host to be created') + log.debug("Timeout reached while waiting for host to be created") return False info = _lookup_proxmox_task(upid) def wait_for_state(vmid, state, timeout=300): - ''' + """ Wait until a specific state has been reached on a node - ''' + """ start_time = time.time() node = get_vm_status(vmid=vmid) if not node: - log.error('wait_for_state: No VM retrieved based on given criteria.') + log.error("wait_for_state: No VM retrieved based on given criteria.") raise SaltCloudExecutionFailure while True: - if node['status'] == state: - log.debug('Host %s is now in "%s" state!', node['name'], state) + if node["status"] == state: + log.debug('Host %s is now in "%s" state!', node["name"], state) return True time.sleep(1) if time.time() - start_time > timeout: - log.debug('Timeout reached while waiting for %s to become %s', - node['name'], state) + log.debug( + "Timeout reached while waiting for %s to become %s", node["name"], state + ) return False node = get_vm_status(vmid=vmid) - log.debug('State for %s is: "%s" instead of "%s"', - node['name'], node['status'], state) + log.debug( + 'State for %s is: "%s" instead of "%s"', node["name"], node["status"], state + ) def destroy(name, call=None): - ''' + """ Destroy a node. CLI Example: @@ -934,115 +1005,118 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud --destroy mymachine - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) vmobj = _get_vm_by_name(name) if vmobj is not None: # stop the vm - if get_vm_status(vmid=vmobj['vmid'])['status'] != 'stopped': - stop(name, vmobj['vmid'], 'action') + if get_vm_status(vmid=vmobj["vmid"])["status"] != "stopped": + stop(name, vmobj["vmid"], "action") # wait until stopped - if not wait_for_state(vmobj['vmid'], 'stopped'): - return {'Error': 'Unable to stop {0}, command timed out'.format(name)} + if not wait_for_state(vmobj["vmid"], "stopped"): + return {"Error": "Unable to stop {0}, command timed out".format(name)} # required to wait a bit here, otherwise the VM is sometimes # still locked and destroy fails. time.sleep(3) - query('delete', 'nodes/{0}/{1}'.format( - vmobj['node'], vmobj['id'] - )) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + query("delete", "nodes/{0}/{1}".format(vmobj["node"], vmobj["id"])) + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) - return {'Destroyed': '{0} was destroyed.'.format(name)} + return {"Destroyed": "{0} was destroyed.".format(name)} def set_vm_status(status, name=None, vmid=None): - ''' + """ Convenience function for setting VM status - ''' - log.debug('Set status to %s for %s (%s)', status, name, vmid) + """ + log.debug("Set status to %s for %s (%s)", status, name, vmid) if vmid is not None: - log.debug('set_vm_status: via ID - VMID %s (%s): %s', - vmid, name, status) + log.debug("set_vm_status: via ID - VMID %s (%s): %s", vmid, name, status) vmobj = _get_vm_by_id(vmid) else: - log.debug('set_vm_status: via name - VMID %s (%s): %s', - vmid, name, status) + log.debug("set_vm_status: via name - VMID %s (%s): %s", vmid, name, status) vmobj = _get_vm_by_name(name) - if not vmobj or 'node' not in vmobj or 'type' not in vmobj or 'vmid' not in vmobj: - log.error('Unable to set status %s for %s (%s)', - status, name, vmid) + if not vmobj or "node" not in vmobj or "type" not in vmobj or "vmid" not in vmobj: + log.error("Unable to set status %s for %s (%s)", status, name, vmid) raise SaltCloudExecutionTimeout log.debug("VM_STATUS: Has desired info (%s). Setting status..", vmobj) - data = query('post', 'nodes/{0}/{1}/{2}/status/{3}'.format( - vmobj['node'], vmobj['type'], vmobj['vmid'], status)) + data = query( + "post", + "nodes/{0}/{1}/{2}/status/{3}".format( + vmobj["node"], vmobj["type"], vmobj["vmid"], status + ), + ) result = _parse_proxmox_upid(data, vmobj) if result is not False and result is not None: - log.debug('Set_vm_status action result: %s', result) + log.debug("Set_vm_status action result: %s", result) return True return False def get_vm_status(vmid=None, name=None): - ''' + """ Get the status for a VM, either via the ID or the hostname - ''' + """ if vmid is not None: - log.debug('get_vm_status: VMID %s', vmid) + log.debug("get_vm_status: VMID %s", vmid) vmobj = _get_vm_by_id(vmid) elif name is not None: - log.debug('get_vm_status: name %s', name) + log.debug("get_vm_status: name %s", name) vmobj = _get_vm_by_name(name) else: log.debug("get_vm_status: No ID or NAME given") raise SaltCloudExecutionFailure - log.debug('VM found: %s', vmobj) + log.debug("VM found: %s", vmobj) - if vmobj is not None and 'node' in vmobj: - log.debug("VM_STATUS: Has desired info. Retrieving.. (%s)", - vmobj['name']) - data = query('get', 'nodes/{0}/{1}/{2}/status/current'.format( - vmobj['node'], vmobj['type'], vmobj['vmid'])) + if vmobj is not None and "node" in vmobj: + log.debug("VM_STATUS: Has desired info. Retrieving.. (%s)", vmobj["name"]) + data = query( + "get", + "nodes/{0}/{1}/{2}/status/current".format( + vmobj["node"], vmobj["type"], vmobj["vmid"] + ), + ) return data - log.error('VM or requested status not found..') + log.error("VM or requested status not found..") return False def start(name, vmid=None, call=None): - ''' + """ Start a node. CLI Example: @@ -1050,24 +1124,24 @@ def start(name, vmid=None, call=None): .. code-block:: bash salt-cloud -a start mymachine - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The start action must be called with -a or --action.' + "The start action must be called with -a or --action." ) - log.debug('Start: %s (%s) = Start', name, vmid) - if not set_vm_status('start', name, vmid=vmid): - log.error('Unable to bring VM %s (%s) up..', name, vmid) + log.debug("Start: %s (%s) = Start", name, vmid) + if not set_vm_status("start", name, vmid=vmid): + log.error("Unable to bring VM %s (%s) up..", name, vmid) raise SaltCloudExecutionFailure # xxx: TBD: Check here whether the status was actually changed to 'started' - return {'Started': '{0} was started.'.format(name)} + return {"Started": "{0} was started.".format(name)} def stop(name, vmid=None, call=None): - ''' + """ Stop a node ("pulling the plug"). CLI Example: @@ -1075,23 +1149,21 @@ def stop(name, vmid=None, call=None): .. code-block:: bash salt-cloud -a stop mymachine - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - if not set_vm_status('stop', name, vmid=vmid): - log.error('Unable to bring VM %s (%s) down..', name, vmid) + if not set_vm_status("stop", name, vmid=vmid): + log.error("Unable to bring VM %s (%s) down..", name, vmid) raise SaltCloudExecutionFailure # xxx: TBD: Check here whether the status was actually changed to 'stopped' - return {'Stopped': '{0} was stopped.'.format(name)} + return {"Stopped": "{0} was stopped.".format(name)} def shutdown(name=None, vmid=None, call=None): - ''' + """ Shutdown a node via ACPI. CLI Example: @@ -1099,16 +1171,16 @@ def shutdown(name=None, vmid=None, call=None): .. code-block:: bash salt-cloud -a shutdown mymachine - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The shutdown action must be called with -a or --action.' + "The shutdown action must be called with -a or --action." ) - if not set_vm_status('shutdown', name, vmid=vmid): - log.error('Unable to shut VM %s (%s) down..', name, vmid) + if not set_vm_status("shutdown", name, vmid=vmid): + log.error("Unable to shut VM %s (%s) down..", name, vmid) raise SaltCloudExecutionFailure # xxx: TBD: Check here whether the status was actually changed to 'stopped' - return {'Shutdown': '{0} was shutdown.'.format(name)} + return {"Shutdown": "{0} was shutdown.".format(name)} diff --git a/salt/cloud/clouds/pyrax.py b/salt/cloud/clouds/pyrax.py index c2ddde52ad6..15b80070166 100644 --- a/salt/cloud/clouds/pyrax.py +++ b/salt/cloud/clouds/pyrax.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Pyrax Cloud Module ================== @@ -7,14 +7,15 @@ PLEASE NOTE: This module is currently in early development, and considered to be experimental and unstable. It is not recommended for production use. Unless you are actively developing code in this module, you should use the OpenStack module instead. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import salt.config as config + # Import salt libs import salt.utils.data -import salt.config as config # Import pyrax libraries # This is typically against SaltStack coding styles, @@ -22,14 +23,14 @@ import salt.config as config # in the loader is creating a name clash and making that form fail from salt.utils.openstack import pyrax as suop -__virtualname__ = 'pyrax' +__virtualname__ = "pyrax" # Only load in this module is the PYRAX configurations are in place def __virtual__(): - ''' + """ Check for Pyrax configurations - ''' + """ if get_configured_provider() is False: return False @@ -40,37 +41,34 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('username', 'identity_url', 'compute_region',) + ("username", "identity_url", "compute_region",), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'pyrax': suop.HAS_PYRAX} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"pyrax": suop.HAS_PYRAX}) def get_conn(conn_type): - ''' + """ Return a conn object for the passed VM data - ''' + """ vm_ = get_configured_provider() kwargs = vm_.copy() # pylint: disable=E1103 - kwargs['username'] = vm_['username'] - kwargs['auth_endpoint'] = vm_.get('identity_url', None) - kwargs['region'] = vm_['compute_region'] + kwargs["username"] = vm_["username"] + kwargs["auth_endpoint"] = vm_.get("identity_url", None) + kwargs["region"] = vm_["compute_region"] conn = getattr(suop, conn_type) @@ -78,26 +76,26 @@ def get_conn(conn_type): def queues_exists(call, kwargs): - conn = get_conn('RackspaceQueues') - return conn.exists(kwargs['name']) + conn = get_conn("RackspaceQueues") + return conn.exists(kwargs["name"]) def queues_show(call, kwargs): - conn = get_conn('RackspaceQueues') - return salt.utils.data.simple_types_filter(conn.show(kwargs['name']).__dict__) + conn = get_conn("RackspaceQueues") + return salt.utils.data.simple_types_filter(conn.show(kwargs["name"]).__dict__) def queues_create(call, kwargs): - conn = get_conn('RackspaceQueues') - if conn.create(kwargs['name']): - return salt.utils.data.simple_types_filter(conn.show(kwargs['name']).__dict__) + conn = get_conn("RackspaceQueues") + if conn.create(kwargs["name"]): + return salt.utils.data.simple_types_filter(conn.show(kwargs["name"]).__dict__) else: return {} def queues_delete(call, kwargs): - conn = get_conn('RackspaceQueues') - if conn.delete(kwargs['name']): + conn = get_conn("RackspaceQueues") + if conn.delete(kwargs["name"]): return {} else: - return salt.utils.data.simple_types_filter(conn.show(kwargs['name'].__dict__)) + return salt.utils.data.simple_types_filter(conn.show(kwargs["name"].__dict__)) diff --git a/salt/cloud/clouds/qingcloud.py b/salt/cloud/clouds/qingcloud.py index 319f6d236c6..b388840dd53 100644 --- a/salt/cloud/clouds/qingcloud.py +++ b/salt/cloud/clouds/qingcloud.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" QingCloud Cloud Module ====================== @@ -24,35 +24,38 @@ Set up the cloud configuration at ``/etc/salt/cloud.providers`` or key_filename: /path/to/your.pem :depends: requests -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time -import pprint -import logging -import hmac + import base64 +import hmac +import logging +import pprint +import time from hashlib import sha256 -# Import Salt Libs -from salt.ext import six -from salt.ext.six.moves.urllib.parse import quote as _quote # pylint: disable=import-error,no-name-in-module -from salt.ext.six.moves import range +import salt.config as config import salt.utils.cloud import salt.utils.data import salt.utils.json -import salt.config as config from salt.exceptions import ( + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, SaltCloudNotFound, SaltCloudSystemExit, - SaltCloudExecutionFailure, - SaltCloudExecutionTimeout ) +# Import Salt Libs +from salt.ext import six +from salt.ext.six.moves import range +from salt.ext.six.moves.urllib.parse import quote as _quote + # Import Third Party Libs try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False @@ -61,7 +64,7 @@ except ImportError: # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'qingcloud' +__virtualname__ = "qingcloud" DEFAULT_QINGCLOUD_API_VERSION = 1 DEFAULT_QINGCLOUD_SIGNATURE_VERSION = 1 @@ -69,9 +72,9 @@ DEFAULT_QINGCLOUD_SIGNATURE_VERSION = 1 # Only load in this module if the qingcloud configurations are in place def __virtual__(): - ''' + """ Check for QingCloud configurations. - ''' + """ if get_configured_provider() is False: return False @@ -82,42 +85,39 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('access_key_id', 'secret_access_key', 'zone', 'key_filename') + ("access_key_id", "secret_access_key", "zone", "key_filename"), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'requests': HAS_REQUESTS} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"requests": HAS_REQUESTS}) def _compute_signature(parameters, access_key_secret, method, path): - ''' + """ Generate an API request signature. Detailed document can be found at: https://docs.qingcloud.com/api/common/signature.html - ''' - parameters['signature_method'] = 'HmacSHA256' + """ + parameters["signature_method"] = "HmacSHA256" - string_to_sign = '{0}\n{1}\n'.format(method.upper(), path) + string_to_sign = "{0}\n{1}\n".format(method.upper(), path) keys = sorted(parameters.keys()) pairs = [] for key in keys: - val = six.text_type(parameters[key]).encode('utf-8') - pairs.append(_quote(key, safe='') + '=' + _quote(val, safe='-_~')) - qs = '&'.join(pairs) + val = six.text_type(parameters[key]).encode("utf-8") + pairs.append(_quote(key, safe="") + "=" + _quote(val, safe="-_~")) + qs = "&".join(pairs) string_to_sign += qs h = hmac.new(access_key_secret, digestmod=sha256) @@ -129,24 +129,24 @@ def _compute_signature(parameters, access_key_secret, method, path): def query(params=None): - ''' + """ Make a web call to QingCloud IaaS API. - ''' - path = 'https://api.qingcloud.com/iaas/' + """ + path = "https://api.qingcloud.com/iaas/" access_key_id = config.get_cloud_config_value( - 'access_key_id', get_configured_provider(), __opts__, search_global=False + "access_key_id", get_configured_provider(), __opts__, search_global=False ) access_key_secret = config.get_cloud_config_value( - 'secret_access_key', get_configured_provider(), __opts__, search_global=False + "secret_access_key", get_configured_provider(), __opts__, search_global=False ) # public interface parameters real_parameters = { - 'access_key_id': access_key_id, - 'signature_version': DEFAULT_QINGCLOUD_SIGNATURE_VERSION, - 'time_stamp': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), - 'version': DEFAULT_QINGCLOUD_API_VERSION, + "access_key_id": access_key_id, + "signature_version": DEFAULT_QINGCLOUD_SIGNATURE_VERSION, + "time_stamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), + "version": DEFAULT_QINGCLOUD_API_VERSION, } # include action or function parameters @@ -157,16 +157,16 @@ def query(params=None): if isinstance(value[i - 1], dict): for sk, sv in value[i - 1].items(): if isinstance(sv, dict) or isinstance(sv, list): - sv = salt.utils.json.dumps(sv, separators=(',', ':')) - real_parameters['{0}.{1}.{2}'.format(key, i, sk)] = sv + sv = salt.utils.json.dumps(sv, separators=(",", ":")) + real_parameters["{0}.{1}.{2}".format(key, i, sk)] = sv else: - real_parameters['{0}.{1}'.format(key, i)] = value[i - 1] + real_parameters["{0}.{1}".format(key, i)] = value[i - 1] else: real_parameters[key] = value # Calculate the string for Signature - signature = _compute_signature(real_parameters, access_key_secret, 'GET', '/iaas/') - real_parameters['signature'] = signature + signature = _compute_signature(real_parameters, access_key_secret, "GET", "/iaas/") + real_parameters["signature"] = signature # print('parameters:') # pprint.pprint(real_parameters) @@ -178,11 +178,8 @@ def query(params=None): if request.status_code != 200: raise SaltCloudSystemExit( - 'An error occurred while querying QingCloud. HTTP Code: {0} ' - 'Error: \'{1}\''.format( - request.status_code, - request.text - ) + "An error occurred while querying QingCloud. HTTP Code: {0} " + "Error: '{1}'".format(request.status_code, request.text) ) log.debug(request.url) @@ -193,16 +190,14 @@ def query(params=None): # print('response:') # pprint.pprint(result) - if result['ret_code'] != 0: - raise SaltCloudSystemExit( - pprint.pformat(result.get('message', {})) - ) + if result["ret_code"] != 0: + raise SaltCloudSystemExit(pprint.pformat(result.get("message", {}))) return result def avail_locations(call=None): - ''' + """ Return a dict of all available locations on the provider with relevant data. @@ -211,47 +206,45 @@ def avail_locations(call=None): .. code-block:: bash salt-cloud --list-locations my-qingcloud - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) params = { - 'action': 'DescribeZones', + "action": "DescribeZones", } items = query(params=params) result = {} - for region in items['zone_set']: - result[region['zone_id']] = {} + for region in items["zone_set"]: + result[region["zone_id"]] = {} for key in region: - result[region['zone_id']][key] = six.text_type(region[key]) + result[region["zone_id"]][key] = six.text_type(region[key]) return result def _get_location(vm_=None): - ''' + """ Return the VM's location. Used by create(). - ''' + """ locations = avail_locations() - vm_location = six.text_type(config.get_cloud_config_value( - 'zone', vm_, __opts__, search_global=False - )) + vm_location = six.text_type( + config.get_cloud_config_value("zone", vm_, __opts__, search_global=False) + ) if not vm_location: - raise SaltCloudNotFound('No location specified for this VM.') + raise SaltCloudNotFound("No location specified for this VM.") if vm_location in locations: return vm_location raise SaltCloudNotFound( - 'The specified location, \'{0}\', could not be found.'.format( - vm_location - ) + "The specified location, '{0}', could not be found.".format(vm_location) ) @@ -260,16 +253,16 @@ def _get_specified_zone(kwargs=None, provider=None): provider = get_configured_provider() if isinstance(kwargs, dict): - zone = kwargs.get('zone', None) + zone = kwargs.get("zone", None) if zone is not None: return zone - zone = provider['zone'] + zone = provider["zone"] return zone def avail_images(kwargs=None, call=None): - ''' + """ Return a list of the images that are on the provider. CLI Examples: @@ -278,54 +271,54 @@ def avail_images(kwargs=None, call=None): salt-cloud --list-images my-qingcloud salt-cloud -f avail_images my-qingcloud zone=gd1 - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) if not isinstance(kwargs, dict): kwargs = {} params = { - 'action': 'DescribeImages', - 'provider': 'system', - 'zone': _get_specified_zone(kwargs, get_configured_provider()), + "action": "DescribeImages", + "provider": "system", + "zone": _get_specified_zone(kwargs, get_configured_provider()), } items = query(params=params) result = {} - for image in items['image_set']: - result[image['image_id']] = {} + for image in items["image_set"]: + result[image["image_id"]] = {} for key in image: - result[image['image_id']][key] = image[key] + result[image["image_id"]][key] = image[key] return result def _get_image(vm_): - ''' + """ Return the VM's image. Used by create(). - ''' + """ images = avail_images() - vm_image = six.text_type(config.get_cloud_config_value( - 'image', vm_, __opts__, search_global=False - )) + vm_image = six.text_type( + config.get_cloud_config_value("image", vm_, __opts__, search_global=False) + ) if not vm_image: - raise SaltCloudNotFound('No image specified for this VM.') + raise SaltCloudNotFound("No image specified for this VM.") if vm_image in images: return vm_image raise SaltCloudNotFound( - 'The specified image, \'{0}\', could not be found.'.format(vm_image) + "The specified image, '{0}', could not be found.".format(vm_image) ) def show_image(kwargs, call=None): - ''' + """ Show the details from QingCloud concerning an image. CLI Examples: @@ -335,69 +328,68 @@ def show_image(kwargs, call=None): salt-cloud -f show_image my-qingcloud image=trustysrvx64c salt-cloud -f show_image my-qingcloud image=trustysrvx64c,coreos4 salt-cloud -f show_image my-qingcloud image=trustysrvx64c zone=ap1 - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_images function must be called with ' - '-f or --function' + "The show_images function must be called with " "-f or --function" ) if not isinstance(kwargs, dict): kwargs = {} - images = kwargs['image'] - images = images.split(',') + images = kwargs["image"] + images = images.split(",") params = { - 'action': 'DescribeImages', - 'images': images, - 'zone': _get_specified_zone(kwargs, get_configured_provider()), + "action": "DescribeImages", + "images": images, + "zone": _get_specified_zone(kwargs, get_configured_provider()), } items = query(params=params) - if not items['image_set']: - raise SaltCloudNotFound('The specified image could not be found.') + if not items["image_set"]: + raise SaltCloudNotFound("The specified image could not be found.") result = {} - for image in items['image_set']: - result[image['image_id']] = {} + for image in items["image_set"]: + result[image["image_id"]] = {} for key in image: - result[image['image_id']][key] = image[key] + result[image["image_id"]][key] = image[key] return result # QingCloud doesn't provide an API of geting instance sizes QINGCLOUD_SIZES = { - 'pek2': { - 'c1m1': {'cpu': 1, 'memory': '1G'}, - 'c1m2': {'cpu': 1, 'memory': '2G'}, - 'c1m4': {'cpu': 1, 'memory': '4G'}, - 'c2m2': {'cpu': 2, 'memory': '2G'}, - 'c2m4': {'cpu': 2, 'memory': '4G'}, - 'c2m8': {'cpu': 2, 'memory': '8G'}, - 'c4m4': {'cpu': 4, 'memory': '4G'}, - 'c4m8': {'cpu': 4, 'memory': '8G'}, - 'c4m16': {'cpu': 4, 'memory': '16G'}, + "pek2": { + "c1m1": {"cpu": 1, "memory": "1G"}, + "c1m2": {"cpu": 1, "memory": "2G"}, + "c1m4": {"cpu": 1, "memory": "4G"}, + "c2m2": {"cpu": 2, "memory": "2G"}, + "c2m4": {"cpu": 2, "memory": "4G"}, + "c2m8": {"cpu": 2, "memory": "8G"}, + "c4m4": {"cpu": 4, "memory": "4G"}, + "c4m8": {"cpu": 4, "memory": "8G"}, + "c4m16": {"cpu": 4, "memory": "16G"}, }, - 'pek1': { - 'small_b': {'cpu': 1, 'memory': '1G'}, - 'small_c': {'cpu': 1, 'memory': '2G'}, - 'medium_a': {'cpu': 2, 'memory': '2G'}, - 'medium_b': {'cpu': 2, 'memory': '4G'}, - 'medium_c': {'cpu': 2, 'memory': '8G'}, - 'large_a': {'cpu': 4, 'memory': '4G'}, - 'large_b': {'cpu': 4, 'memory': '8G'}, - 'large_c': {'cpu': 4, 'memory': '16G'}, + "pek1": { + "small_b": {"cpu": 1, "memory": "1G"}, + "small_c": {"cpu": 1, "memory": "2G"}, + "medium_a": {"cpu": 2, "memory": "2G"}, + "medium_b": {"cpu": 2, "memory": "4G"}, + "medium_c": {"cpu": 2, "memory": "8G"}, + "large_a": {"cpu": 4, "memory": "4G"}, + "large_b": {"cpu": 4, "memory": "8G"}, + "large_c": {"cpu": 4, "memory": "16G"}, }, } -QINGCLOUD_SIZES['ap1'] = QINGCLOUD_SIZES['pek2'] -QINGCLOUD_SIZES['gd1'] = QINGCLOUD_SIZES['pek2'] +QINGCLOUD_SIZES["ap1"] = QINGCLOUD_SIZES["pek2"] +QINGCLOUD_SIZES["gd1"] = QINGCLOUD_SIZES["pek2"] def avail_sizes(kwargs=None, call=None): - ''' + """ Return a list of the instance sizes that are on the provider. CLI Examples: @@ -406,11 +398,11 @@ def avail_sizes(kwargs=None, call=None): salt-cloud --list-sizes my-qingcloud salt-cloud -f avail_sizes my-qingcloud zone=pek2 - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) zone = _get_specified_zone(kwargs, get_configured_provider()) @@ -419,62 +411,66 @@ def avail_sizes(kwargs=None, call=None): for size_key in QINGCLOUD_SIZES[zone]: result[size_key] = {} for attribute_key in QINGCLOUD_SIZES[zone][size_key]: - result[size_key][attribute_key] = QINGCLOUD_SIZES[zone][size_key][attribute_key] + result[size_key][attribute_key] = QINGCLOUD_SIZES[zone][size_key][ + attribute_key + ] return result def _get_size(vm_): - ''' + """ Return the VM's size. Used by create(). - ''' + """ sizes = avail_sizes() - vm_size = six.text_type(config.get_cloud_config_value( - 'size', vm_, __opts__, search_global=False - )) + vm_size = six.text_type( + config.get_cloud_config_value("size", vm_, __opts__, search_global=False) + ) if not vm_size: - raise SaltCloudNotFound('No size specified for this instance.') + raise SaltCloudNotFound("No size specified for this instance.") if vm_size in sizes.keys(): return vm_size raise SaltCloudNotFound( - 'The specified size, \'{0}\', could not be found.'.format(vm_size) + "The specified size, '{0}', could not be found.".format(vm_size) ) def _show_normalized_node(full_node): - ''' + """ Normalize the QingCloud instance data. Used by list_nodes()-related functions. - ''' - public_ips = full_node.get('eip', []) + """ + public_ips = full_node.get("eip", []) if public_ips: - public_ip = public_ips['eip_addr'] - public_ips = [public_ip, ] + public_ip = public_ips["eip_addr"] + public_ips = [ + public_ip, + ] private_ips = [] - for vxnet in full_node.get('vxnets', []): - private_ip = vxnet.get('private_ip', None) + for vxnet in full_node.get("vxnets", []): + private_ip = vxnet.get("private_ip", None) if private_ip: private_ips.append(private_ip) normalized_node = { - 'id': full_node['instance_id'], - 'image': full_node['image']['image_id'], - 'size': full_node['instance_type'], - 'state': full_node['status'], - 'private_ips': private_ips, - 'public_ips': public_ips, + "id": full_node["instance_id"], + "image": full_node["image"]["image_id"], + "size": full_node["instance_type"], + "state": full_node["status"], + "private_ips": private_ips, + "public_ips": public_ips, } return normalized_node def list_nodes_full(call=None): - ''' + """ Return a list of the instances that are on the provider. CLI Examples: @@ -482,47 +478,47 @@ def list_nodes_full(call=None): .. code-block:: bash salt-cloud -F my-qingcloud - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) zone = _get_specified_zone() params = { - 'action': 'DescribeInstances', - 'zone': zone, - 'status': ['pending', 'running', 'stopped', 'suspended'], + "action": "DescribeInstances", + "zone": zone, + "status": ["pending", "running", "stopped", "suspended"], } items = query(params=params) - log.debug('Total %s instances found in zone %s', items['total_count'], zone) + log.debug("Total %s instances found in zone %s", items["total_count"], zone) result = {} - if items['total_count'] == 0: + if items["total_count"] == 0: return result - for node in items['instance_set']: + for node in items["instance_set"]: normalized_node = _show_normalized_node(node) node.update(normalized_node) - result[node['instance_id']] = node + result[node["instance_id"]] = node - provider = __active_provider_name__ or 'qingcloud' - if ':' in provider: - comps = provider.split(':') + provider = __active_provider_name__ or "qingcloud" + if ":" in provider: + comps = provider.split(":") provider = comps[0] - __opts__['update_cachedir'] = True - __utils__['cloud.cache_node_list'](result, provider, __opts__) + __opts__["update_cachedir"] = True + __utils__["cloud.cache_node_list"](result, provider, __opts__) return result def list_nodes(call=None): - ''' + """ Return a list of the instances that are on the provider. CLI Examples: @@ -530,10 +526,10 @@ def list_nodes(call=None): .. code-block:: bash salt-cloud -Q my-qingcloud - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) nodes = list_nodes_full() @@ -541,19 +537,19 @@ def list_nodes(call=None): ret = {} for instance_id, full_node in nodes.items(): ret[instance_id] = { - 'id': full_node['id'], - 'image': full_node['image'], - 'size': full_node['size'], - 'state': full_node['state'], - 'public_ips': full_node['public_ips'], - 'private_ips': full_node['private_ips'], + "id": full_node["id"], + "image": full_node["image"], + "size": full_node["size"], + "state": full_node["state"], + "public_ips": full_node["public_ips"], + "private_ips": full_node["private_ips"], } return ret def list_nodes_min(call=None): - ''' + """ Return a list of the instances that are on the provider. Only a list of instances names, and their state, is returned. @@ -562,10 +558,10 @@ def list_nodes_min(call=None): .. code-block:: bash salt-cloud -f list_nodes_min my-qingcloud - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_nodes_min function must be called with -f or --function.' + "The list_nodes_min function must be called with -f or --function." ) nodes = list_nodes_full() @@ -573,15 +569,15 @@ def list_nodes_min(call=None): result = {} for instance_id, full_node in nodes.items(): result[instance_id] = { - 'name': full_node['instance_name'], - 'status': full_node['status'], + "name": full_node["instance_name"], + "status": full_node["status"], } return result def list_nodes_select(call=None): - ''' + """ Return a list of the instances that are on the provider, with selected fields. @@ -590,16 +586,14 @@ def list_nodes_select(call=None): .. code-block:: bash salt-cloud -S my-qingcloud - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full('function'), - __opts__['query.selection'], - call, + list_nodes_full("function"), __opts__["query.selection"], call, ) def show_instance(instance_id, call=None, kwargs=None): - ''' + """ Show the details from QingCloud concerning an instance. CLI Examples: @@ -607,25 +601,25 @@ def show_instance(instance_id, call=None, kwargs=None): .. code-block:: bash salt-cloud -a show_instance i-2f733r5n - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) params = { - 'action': 'DescribeInstances', - 'instances.1': instance_id, - 'zone': _get_specified_zone(kwargs=None, provider=get_configured_provider()), + "action": "DescribeInstances", + "instances.1": instance_id, + "zone": _get_specified_zone(kwargs=None, provider=get_configured_provider()), } items = query(params=params) - if items['total_count'] == 0: + if items["total_count"] == 0: raise SaltCloudNotFound( - 'The specified instance, \'{0}\', could not be found.'.format(instance_id) + "The specified instance, '{0}', could not be found.".format(instance_id) ) - full_node = items['instance_set'][0] + full_node = items["instance_set"][0] normalized_node = _show_normalized_node(full_node) full_node.update(normalized_node) @@ -635,17 +629,17 @@ def show_instance(instance_id, call=None, kwargs=None): def _query_node_data(instance_id): - data = show_instance(instance_id, call='action') + data = show_instance(instance_id, call="action") if not data: return False - if data.get('private_ips', []): + if data.get("private_ips", []): return data def create(vm_): - ''' + """ Create a single instance from a data dict. CLI Examples: @@ -654,117 +648,129 @@ def create(vm_): salt-cloud -p qingcloud-ubuntu-c1m1 hostname1 salt-cloud -m /path/to/mymap.sls -P - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'qingcloud', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or "qingcloud", + vm_["profile"], + vm_=vm_, + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) # params params = { - 'action': 'RunInstances', - 'instance_name': vm_['name'], - 'zone': _get_location(vm_), - 'instance_type': _get_size(vm_), - 'image_id': _get_image(vm_), - 'vxnets.1': vm_['vxnets'], - 'login_mode': vm_['login_mode'], - 'login_keypair': vm_['login_keypair'], + "action": "RunInstances", + "instance_name": vm_["name"], + "zone": _get_location(vm_), + "instance_type": _get_size(vm_), + "image_id": _get_image(vm_), + "vxnets.1": vm_["vxnets"], + "login_mode": vm_["login_mode"], + "login_keypair": vm_["login_keypair"], } - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', params, list(params)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", params, list(params) + ), }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) result = query(params) - new_instance_id = result['instances'][0] + new_instance_id = result["instances"][0] try: data = salt.utils.cloud.wait_for_ip( _query_node_data, update_args=(new_instance_id,), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60 + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10 + "wait_for_ip_interval", vm_, __opts__, default=10 ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) - private_ip = data['private_ips'][0] + private_ip = data["private_ips"][0] - log.debug('VM %s is now running', private_ip) + log.debug("VM %s is now running", private_ip) - vm_['ssh_host'] = private_ip + vm_["ssh_host"] = private_ip # The instance is booted and accessible, let's Salt it! - __utils__['cloud.bootstrap'](vm_, __opts__) + __utils__["cloud.bootstrap"](vm_, __opts__) - log.info('Created Cloud VM \'%s\'', vm_['name']) + log.info("Created Cloud VM '%s'", vm_["name"]) - log.debug('\'%s\' VM creation details:\n%s', vm_['name'], pprint.pformat(data)) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return data def script(vm_): - ''' + """ Return the script deployment object. - ''' + """ deploy_script = salt.utils.cloud.os_script( - config.get_cloud_config_value('script', vm_, __opts__), + config.get_cloud_config_value("script", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) return deploy_script def start(instance_id, call=None): - ''' + """ Start an instance. CLI Examples: @@ -772,18 +778,16 @@ def start(instance_id, call=None): .. code-block:: bash salt-cloud -a start i-2f733r5n - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - log.info('Starting instance %s', instance_id) + log.info("Starting instance %s", instance_id) params = { - 'action': 'StartInstances', - 'zone': _get_specified_zone(provider=get_configured_provider()), - 'instances.1': instance_id, + "action": "StartInstances", + "zone": _get_specified_zone(provider=get_configured_provider()), + "instances.1": instance_id, } result = query(params) @@ -791,7 +795,7 @@ def start(instance_id, call=None): def stop(instance_id, force=False, call=None): - ''' + """ Stop an instance. CLI Examples: @@ -800,19 +804,17 @@ def stop(instance_id, force=False, call=None): salt-cloud -a stop i-2f733r5n salt-cloud -a stop i-2f733r5n force=True - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - log.info('Stopping instance %s', instance_id) + log.info("Stopping instance %s", instance_id) params = { - 'action': 'StopInstances', - 'zone': _get_specified_zone(provider=get_configured_provider()), - 'instances.1': instance_id, - 'force': int(force), + "action": "StopInstances", + "zone": _get_specified_zone(provider=get_configured_provider()), + "instances.1": instance_id, + "force": int(force), } result = query(params) @@ -820,7 +822,7 @@ def stop(instance_id, force=False, call=None): def reboot(instance_id, call=None): - ''' + """ Reboot an instance. CLI Examples: @@ -828,18 +830,16 @@ def reboot(instance_id, call=None): .. code-block:: bash salt-cloud -a reboot i-2f733r5n - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") - log.info('Rebooting instance %s', instance_id) + log.info("Rebooting instance %s", instance_id) params = { - 'action': 'RestartInstances', - 'zone': _get_specified_zone(provider=get_configured_provider()), - 'instances.1': instance_id, + "action": "RestartInstances", + "zone": _get_specified_zone(provider=get_configured_provider()), + "instances.1": instance_id, } result = query(params) @@ -847,7 +847,7 @@ def reboot(instance_id, call=None): def destroy(instance_id, call=None): - ''' + """ Destroy an instance. CLI Example: @@ -856,39 +856,38 @@ def destroy(instance_id, call=None): salt-cloud -a destroy i-2f733r5n salt-cloud -d i-2f733r5n - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - instance_data = show_instance(instance_id, call='action') - name = instance_data['instance_name'] + instance_data = show_instance(instance_id, call="action") + name = instance_data["instance_name"] - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) params = { - 'action': 'TerminateInstances', - 'zone': _get_specified_zone(provider=get_configured_provider()), - 'instances.1': instance_id, + "action": "TerminateInstances", + "zone": _get_specified_zone(provider=get_configured_provider()), + "instances.1": instance_id, } result = query(params) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return result diff --git a/salt/cloud/clouds/saltify.py b/salt/cloud/clouds/saltify.py index 00ed2dc9696..d15c93b5c0e 100644 --- a/salt/cloud/clouds/saltify.py +++ b/salt/cloud/clouds/saltify.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' -.. _`saltify-module`: +""" +.. _saltify-module: Saltify Module ============== @@ -15,18 +15,20 @@ are already installed, but not Salted. Use of this module requires some configuration in cloud profile and provider files as described in the :ref:`Gettting Started with Saltify ` documentation. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time +import salt.client +import salt.config as config +import salt.ext.six as six + # Import salt libs import salt.utils.cloud -import salt.config as config -import salt.client -import salt.ext.six as six from salt._compat import ipaddress from salt.exceptions import SaltCloudException, SaltCloudSystemExit @@ -37,6 +39,7 @@ try: # noinspection PyUnresolvedReferences from impacket.smbconnection import SessionError as smbSessionError from impacket.smb3 import SessionError as smb3SessionError + HAS_IMPACKET = True except ImportError: HAS_IMPACKET = False @@ -44,24 +47,32 @@ except ImportError: try: # noinspection PyUnresolvedReferences from winrm.exceptions import WinRMTransportError + # noinspection PyUnresolvedReferences from requests.exceptions import ( - ConnectionError, ConnectTimeout, ReadTimeout, SSLError, - ProxyError, RetryError, InvalidSchema) + ConnectionError, + ConnectTimeout, + ReadTimeout, + SSLError, + ProxyError, + RetryError, + InvalidSchema, + ) + HAS_WINRM = True except ImportError: HAS_WINRM = False def __virtual__(): - ''' + """ Needs no special configuration - ''' + """ return True def avail_locations(call=None): - ''' + """ This function returns a list of locations available. .. code-block:: bash @@ -69,13 +80,13 @@ def avail_locations(call=None): salt-cloud --list-locations my-cloud-provider [ saltify will always return an empty dictionary ] - ''' + """ return {} def avail_images(call=None): - ''' + """ This function returns a list of images available for this cloud provider. .. code-block:: bash @@ -86,13 +97,13 @@ def avail_images(call=None): ..versionadded:: 2018.3.0 - ''' + """ vm_ = get_configured_provider() - return {'Profiles': [profile for profile in vm_['profiles']]} + return {"Profiles": [profile for profile in vm_["profiles"]]} def avail_sizes(call=None): - ''' + """ This function returns a list of sizes available for this cloud provider. .. code-block:: bash @@ -100,12 +111,12 @@ def avail_sizes(call=None): salt-cloud --list-sizes saltify [ saltify always returns an empty dictionary ] - ''' + """ return {} def list_nodes(call=None): - ''' + """ List the nodes which have salt-cloud:driver:saltify grains. .. code-block:: bash @@ -116,7 +127,7 @@ def list_nodes(call=None): ..versionadded:: 2018.3.0 - ''' + """ nodes = _list_nodes_full(call) return _build_required_items(nodes) @@ -127,7 +138,7 @@ def _build_required_items(nodes): if grains: private_ips = [] public_ips = [] - ips = grains['ipv4'] + grains['ipv6'] + ips = grains["ipv4"] + grains["ipv6"] for adrs in ips: ip_ = ipaddress.ip_address(adrs) if not ip_.is_loopback: @@ -137,19 +148,19 @@ def _build_required_items(nodes): public_ips.append(adrs) ret[name] = { - 'id': grains['id'], - 'image': grains['salt-cloud']['profile'], - 'private_ips': private_ips, - 'public_ips': public_ips, - 'size': '', - 'state': 'running' + "id": grains["id"], + "image": grains["salt-cloud"]["profile"], + "private_ips": private_ips, + "public_ips": public_ips, + "size": "", + "state": "running", } return ret def list_nodes_full(call=None): - ''' + """ Lists complete information for all nodes. .. code-block:: bash @@ -161,13 +172,22 @@ def list_nodes_full(call=None): for 'saltify' minions, returns dict of grains (enhanced). ..versionadded:: 2018.3.0 - ''' + """ ret = _list_nodes_full(call) - for key, grains in ret.items(): # clean up some hyperverbose grains -- everything is too much + for ( + key, + grains, + ) in ret.items(): # clean up some hyperverbose grains -- everything is too much try: - del grains['cpu_flags'], grains['disks'], grains['pythonpath'], grains['dns'], grains['gpus'] + del ( + grains["cpu_flags"], + grains["disks"], + grains["pythonpath"], + grains["dns"], + grains["gpus"], + ) except KeyError: pass # ignore absence of things we are eliminating except TypeError: @@ -182,36 +202,35 @@ def list_nodes_full(call=None): def _list_nodes_full(call=None): - ''' + """ List the nodes, ask all 'saltify' minions, return dict of grains. - ''' + """ local = salt.client.LocalClient() - return local.cmd('salt-cloud:driver:saltify', 'grains.items', '', - tgt_type='grain') + return local.cmd("salt-cloud:driver:saltify", "grains.items", "", tgt_type="grain") def list_nodes_select(call=None): - ''' + """ Return a list of the minions that have salt-cloud grains, with select fields. - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full('function'), __opts__['query.selection'], call, + list_nodes_full("function"), __opts__["query.selection"], call, ) def show_instance(name, call=None): - ''' + """ List the a single node, return dict of grains. - ''' + """ local = salt.client.LocalClient() - ret = local.cmd(name, 'grains.items') + ret = local.cmd(name, "grains.items") ret.update(_build_required_items(ret)) return ret def create(vm_): - ''' + """ if configuration parameter ``deploy`` is ``True``, Provision a single machine, adding its keys to the salt master @@ -244,43 +263,47 @@ def create(vm_): .. code-block:: bash salt-cloud -p mymachine my_new_id - ''' + """ deploy_config = config.get_cloud_config_value( - 'deploy', vm_, __opts__, default=False) + "deploy", vm_, __opts__, default=False + ) if deploy_config: wol_mac = config.get_cloud_config_value( - 'wake_on_lan_mac', vm_, __opts__, default='') + "wake_on_lan_mac", vm_, __opts__, default="" + ) wol_host = config.get_cloud_config_value( - 'wol_sender_node', vm_, __opts__, default='') + "wol_sender_node", vm_, __opts__, default="" + ) if wol_mac and wol_host: good_ping = False local = salt.client.LocalClient() ssh_host = config.get_cloud_config_value( - 'ssh_host', vm_, __opts__, default='') + "ssh_host", vm_, __opts__, default="" + ) if ssh_host: - log.info('trying to ping %s', ssh_host) - count = 'n' if salt.utils.platform.is_windows() else 'c' - cmd = 'ping -{} 1 {}'.format(count, ssh_host) - good_ping = local.cmd(wol_host, 'cmd.retcode', [cmd]) == 0 + log.info("trying to ping %s", ssh_host) + count = "n" if salt.utils.platform.is_windows() else "c" + cmd = "ping -{} 1 {}".format(count, ssh_host) + good_ping = local.cmd(wol_host, "cmd.retcode", [cmd]) == 0 if good_ping: - log.info('successful ping.') + log.info("successful ping.") else: - log.info('sending wake-on-lan to %s using node %s', - wol_mac, wol_host) + log.info("sending wake-on-lan to %s using node %s", wol_mac, wol_host) if isinstance(wol_mac, six.string_types): wol_mac = [wol_mac] # a smart user may have passed more params - ret = local.cmd(wol_host, 'network.wol', wol_mac) - log.info('network.wol returned value %s', ret) + ret = local.cmd(wol_host, "network.wol", wol_mac) + log.info("network.wol returned value %s", ret) if ret and ret[wol_host]: sleep_time = config.get_cloud_config_value( - 'wol_boot_wait', vm_, __opts__, default=30) + "wol_boot_wait", vm_, __opts__, default=30 + ) if sleep_time > 0.0: - log.info('delaying %d seconds for boot', sleep_time) + log.info("delaying %d seconds for boot", sleep_time) time.sleep(sleep_time) - log.info('Provisioning existing machine %s', vm_['name']) - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + log.info("Provisioning existing machine %s", vm_["name"]) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) else: ret = _verify(vm_) @@ -288,112 +311,123 @@ def create(vm_): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or 'saltify', - () + __opts__, __active_provider_name__ or "saltify", () ) def _verify(vm_): - ''' + """ Verify credentials for an existing system - ''' - log.info('Verifying credentials for %s', vm_['name']) + """ + log.info("Verifying credentials for %s", vm_["name"]) - win_installer = config.get_cloud_config_value( - 'win_installer', vm_, __opts__) + win_installer = config.get_cloud_config_value("win_installer", vm_, __opts__) if win_installer: - log.debug('Testing Windows authentication method for %s', vm_['name']) + log.debug("Testing Windows authentication method for %s", vm_["name"]) if not HAS_IMPACKET: - log.error('Impacket library not found') + log.error("Impacket library not found") return False # Test Windows connection kwargs = { - 'host': vm_['ssh_host'], - 'username': config.get_cloud_config_value( - 'win_username', vm_, __opts__, default='Administrator'), - 'password': config.get_cloud_config_value( - 'win_password', vm_, __opts__, default='') + "host": vm_["ssh_host"], + "username": config.get_cloud_config_value( + "win_username", vm_, __opts__, default="Administrator" + ), + "password": config.get_cloud_config_value( + "win_password", vm_, __opts__, default="" + ), } # Test SMB connection try: - log.debug('Testing SMB protocol for %s', vm_['name']) - if __utils__['smb.get_conn'](**kwargs) is False: + log.debug("Testing SMB protocol for %s", vm_["name"]) + if __utils__["smb.get_conn"](**kwargs) is False: return False except (smbSessionError, smb3SessionError) as exc: - log.error('Exception: %s', exc) + log.error("Exception: %s", exc) return False # Test WinRM connection use_winrm = config.get_cloud_config_value( - 'use_winrm', vm_, __opts__, default=False) + "use_winrm", vm_, __opts__, default=False + ) if use_winrm: - log.debug('WinRM protocol requested for %s', vm_['name']) + log.debug("WinRM protocol requested for %s", vm_["name"]) if not HAS_WINRM: - log.error('WinRM library not found') + log.error("WinRM library not found") return False - kwargs['port'] = config.get_cloud_config_value( - 'winrm_port', vm_, __opts__, default=5986) - kwargs['timeout'] = 10 + kwargs["port"] = config.get_cloud_config_value( + "winrm_port", vm_, __opts__, default=5986 + ) + kwargs["timeout"] = 10 try: - log.debug('Testing WinRM protocol for %s', vm_['name']) - return __utils__['cloud.wait_for_winrm'](**kwargs) is not None - except (ConnectionError, ConnectTimeout, ReadTimeout, SSLError, - ProxyError, RetryError, InvalidSchema, WinRMTransportError) as exc: - log.error('Exception: %s', exc) + log.debug("Testing WinRM protocol for %s", vm_["name"]) + return __utils__["cloud.wait_for_winrm"](**kwargs) is not None + except ( + ConnectionError, + ConnectTimeout, + ReadTimeout, + SSLError, + ProxyError, + RetryError, + InvalidSchema, + WinRMTransportError, + ) as exc: + log.error("Exception: %s", exc) return False return True else: - log.debug('Testing SSH authentication method for %s', vm_['name']) + log.debug("Testing SSH authentication method for %s", vm_["name"]) # Test SSH connection kwargs = { - 'host': vm_['ssh_host'], - 'port': config.get_cloud_config_value( - 'ssh_port', vm_, __opts__, default=22 + "host": vm_["ssh_host"], + "port": config.get_cloud_config_value( + "ssh_port", vm_, __opts__, default=22 ), - 'username': config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, default='root' + "username": config.get_cloud_config_value( + "ssh_username", vm_, __opts__, default="root" ), - 'password': config.get_cloud_config_value( - 'password', vm_, __opts__, search_global=False + "password": config.get_cloud_config_value( + "password", vm_, __opts__, search_global=False ), - 'key_filename': config.get_cloud_config_value( - 'key_filename', vm_, __opts__, search_global=False, + "key_filename": config.get_cloud_config_value( + "key_filename", + vm_, + __opts__, + search_global=False, default=config.get_cloud_config_value( - 'ssh_keyfile', vm_, __opts__, search_global=False, - default=None - ) + "ssh_keyfile", vm_, __opts__, search_global=False, default=None + ), ), - 'gateway': vm_.get('gateway', None), - 'maxtries': 1 + "gateway": vm_.get("gateway", None), + "maxtries": 1, } - log.debug('Testing SSH protocol for %s', vm_['name']) + log.debug("Testing SSH protocol for %s", vm_["name"]) try: - return __utils__['cloud.wait_for_passwd'](**kwargs) is True + return __utils__["cloud.wait_for_passwd"](**kwargs) is True except SaltCloudException as exc: - log.error('Exception: %s', exc) + log.error("Exception: %s", exc) return False def destroy(name, call=None): - ''' Destroy a node. + """ Destroy a node. .. versionadded:: 2018.3.0 @@ -412,77 +446,72 @@ def destroy(name, call=None): salt-cloud --destroy mymachine - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a, or --action.' + "The destroy action must be called with -d, --destroy, " "-a, or --action." ) opts = __opts__ - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=opts['sock_dir'], - transport=opts['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=opts["sock_dir"], + transport=opts["transport"], ) vm_ = get_configured_provider() local = salt.client.LocalClient() - my_info = local.cmd(name, 'grains.get', ['salt-cloud']) + my_info = local.cmd(name, "grains.get", ["salt-cloud"]) try: vm_.update(my_info[name]) # get profile name to get config value except (IndexError, TypeError): pass if config.get_cloud_config_value( - 'remove_config_on_destroy', vm_, opts, default=True - ): - ret = local.cmd(name, # prevent generating new keys on restart - 'service.disable', - ['salt-minion']) + "remove_config_on_destroy", vm_, opts, default=True + ): + ret = local.cmd( + name, # prevent generating new keys on restart + "service.disable", + ["salt-minion"], + ) if ret and ret[name]: - log.info('disabled salt-minion service on %s', name) - ret = local.cmd(name, 'config.get', ['conf_file']) + log.info("disabled salt-minion service on %s", name) + ret = local.cmd(name, "config.get", ["conf_file"]) if ret and ret[name]: confile = ret[name] - ret = local.cmd(name, 'file.remove', [confile]) + ret = local.cmd(name, "file.remove", [confile]) if ret and ret[name]: - log.info('removed minion %s configuration file %s', - name, confile) - ret = local.cmd(name, 'config.get', ['pki_dir']) + log.info("removed minion %s configuration file %s", name, confile) + ret = local.cmd(name, "config.get", ["pki_dir"]) if ret and ret[name]: pki_dir = ret[name] - ret = local.cmd(name, 'file.remove', [pki_dir]) + ret = local.cmd(name, "file.remove", [pki_dir]) if ret and ret[name]: - log.info( - 'removed minion %s key files in %s', - name, - pki_dir) + log.info("removed minion %s key files in %s", name, pki_dir) - if config.get_cloud_config_value( - 'shutdown_on_destroy', vm_, opts, default=False - ): - ret = local.cmd(name, 'system.shutdown') + if config.get_cloud_config_value("shutdown_on_destroy", vm_, opts, default=False): + ret = local.cmd(name, "system.shutdown") if ret and ret[name]: - log.info('system.shutdown for minion %s successful', name) + log.info("system.shutdown for minion %s successful", name) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=opts['sock_dir'], - transport=opts['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=opts["sock_dir"], + transport=opts["transport"], ) - return {'Destroyed': '{0} was destroyed.'.format(name)} + return {"Destroyed": "{0} was destroyed.".format(name)} def reboot(name, call=None): - ''' + """ Reboot a saltify minion. ..versionadded:: 2018.3.0 @@ -495,12 +524,12 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot vm_name - ''' + """ - if call != 'action': + if call != "action": raise SaltCloudException( - 'The reboot action must be called with -a or --action.' + "The reboot action must be called with -a or --action." ) local = salt.client.LocalClient() - return local.cmd(name, 'system.reboot') + return local.cmd(name, "system.reboot") diff --git a/salt/cloud/clouds/scaleway.py b/salt/cloud/clouds/scaleway.py index 4baf81c5e29..42f5d516cfd 100644 --- a/salt/cloud/clouds/scaleway.py +++ b/salt/cloud/clouds/scaleway.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Scaleway Cloud Module ===================== @@ -20,39 +20,41 @@ the cloud configuration at ``/etc/salt/cloud.providers`` or token: be8fd96b-04eb-4d39-b6ba-a9edbcf17f12 driver: scaleway -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import pprint import os +import pprint import time +import salt.config as config +import salt.utils.cloud +import salt.utils.json +from salt.exceptions import ( + SaltCloudConfigError, + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, + SaltCloudNotFound, + SaltCloudSystemExit, +) + # Import Salt Libs from salt.ext import six from salt.ext.six.moves import range -import salt.utils.cloud -import salt.utils.json -import salt.config as config -from salt.exceptions import ( - SaltCloudConfigError, - SaltCloudNotFound, - SaltCloudSystemExit, - SaltCloudExecutionFailure, - SaltCloudExecutionTimeout -) log = logging.getLogger(__name__) -__virtualname__ = 'scaleway' +__virtualname__ = "scaleway" # Only load in this module if the Scaleway configurations are in place def __virtual__(): - ''' + """ Check for Scaleway configurations. - ''' + """ if get_configured_provider() is False: return False @@ -60,275 +62,286 @@ def __virtual__(): def get_configured_provider(): - ''' Return the first configured instance. - ''' + """ Return the first configured instance. + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('token',) + __opts__, __active_provider_name__ or __virtualname__, ("token",) ) def avail_images(call=None): - ''' Return a list of the images that are on the provider. - ''' - if call == 'action': + """ Return a list of the images that are on the provider. + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) - items = query(method='images') + items = query(method="images") ret = {} - for image in items['images']: - ret[image['id']] = {} + for image in items["images"]: + ret[image["id"]] = {} for item in image: - ret[image['id']][item] = six.text_type(image[item]) + ret[image["id"]][item] = six.text_type(image[item]) return ret def list_nodes(call=None): - ''' Return a list of the BareMetal servers that are on the provider. - ''' - if call == 'action': + """ Return a list of the BareMetal servers that are on the provider. + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) - items = query(method='servers') + items = query(method="servers") ret = {} - for node in items['servers']: + for node in items["servers"]: public_ips = [] private_ips = [] - image_id = '' + image_id = "" - if node.get('public_ip'): - public_ips = [node['public_ip']['address']] + if node.get("public_ip"): + public_ips = [node["public_ip"]["address"]] - if node.get('private_ip'): - private_ips = [node['private_ip']] + if node.get("private_ip"): + private_ips = [node["private_ip"]] - if node.get('image'): - image_id = node['image']['id'] + if node.get("image"): + image_id = node["image"]["id"] - ret[node['name']] = { - 'id': node['id'], - 'image_id': image_id, - 'public_ips': public_ips, - 'private_ips': private_ips, - 'size': node['volumes']['0']['size'], - 'state': node['state'], + ret[node["name"]] = { + "id": node["id"], + "image_id": image_id, + "public_ips": public_ips, + "private_ips": private_ips, + "size": node["volumes"]["0"]["size"], + "state": node["state"], } return ret def list_nodes_full(call=None): - ''' Return a list of the BareMetal servers that are on the provider. - ''' - if call == 'action': + """ Return a list of the BareMetal servers that are on the provider. + """ + if call == "action": raise SaltCloudSystemExit( - 'list_nodes_full must be called with -f or --function' + "list_nodes_full must be called with -f or --function" ) - items = query(method='servers') + items = query(method="servers") # For each server, iterate on its parameters. ret = {} - for node in items['servers']: - ret[node['name']] = {} + for node in items["servers"]: + ret[node["name"]] = {} for item in node: value = node[item] - ret[node['name']][item] = value + ret[node["name"]][item] = value return ret def list_nodes_select(call=None): - ''' Return a list of the BareMetal servers that are on the provider, with + """ Return a list of the BareMetal servers that are on the provider, with select fields. - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full('function'), __opts__['query.selection'], call, + list_nodes_full("function"), __opts__["query.selection"], call, ) def get_image(server_): - ''' Return the image object to use. - ''' + """ Return the image object to use. + """ images = avail_images() - server_image = six.text_type(config.get_cloud_config_value( - 'image', server_, __opts__, search_global=False - )) + server_image = six.text_type( + config.get_cloud_config_value("image", server_, __opts__, search_global=False) + ) for image in images: - if server_image in (images[image]['name'], images[image]['id']): - return images[image]['id'] + if server_image in (images[image]["name"], images[image]["id"]): + return images[image]["id"] raise SaltCloudNotFound( - 'The specified image, \'{0}\', could not be found.'.format(server_image) + "The specified image, '{0}', could not be found.".format(server_image) ) def create_node(args): - ''' Create a node. - ''' - node = query(method='servers', args=args, http_method='post') + """ Create a node. + """ + node = query(method="servers", args=args, http_method="post") action = query( - method='servers', - server_id=node['server']['id'], - command='action', - args={'action': 'poweron'}, - http_method='post' + method="servers", + server_id=node["server"]["id"], + command="action", + args={"action": "poweron"}, + http_method="post", ) return node def create(server_): - ''' + """ Create a single BareMetal server from a data dict. - ''' + """ try: # Check for required profile parameters before sending any API calls. - if server_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'scaleway', - server_['profile'], - vm_=server_) is False: + if ( + server_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or "scaleway", + server_["profile"], + vm_=server_, + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(server_['name']), - args=__utils__['cloud.filter_event']('creating', server_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(server_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", server_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating a BareMetal server %s', server_['name']) + log.info("Creating a BareMetal server %s", server_["name"]) access_key = config.get_cloud_config_value( - 'access_key', get_configured_provider(), __opts__, search_global=False + "access_key", get_configured_provider(), __opts__, search_global=False ) commercial_type = config.get_cloud_config_value( - 'commercial_type', server_, __opts__, default='C1' + "commercial_type", server_, __opts__, default="C1" ) key_filename = config.get_cloud_config_value( - 'ssh_key_file', server_, __opts__, search_global=False, default=None + "ssh_key_file", server_, __opts__, search_global=False, default=None ) if key_filename is not None and not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined key_filename \'{0}\' does not exist'.format( - key_filename - ) + "The defined key_filename '{0}' does not exist".format(key_filename) ) - ssh_password = config.get_cloud_config_value( - 'ssh_password', server_, __opts__ - ) + ssh_password = config.get_cloud_config_value("ssh_password", server_, __opts__) kwargs = { - 'name': server_['name'], - 'organization': access_key, - 'image': get_image(server_), - 'commercial_type': commercial_type, + "name": server_["name"], + "organization": access_key, + "image": get_image(server_), + "commercial_type": commercial_type, } - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(server_['name']), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(server_["name"]), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', kwargs, list(kwargs)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", kwargs, list(kwargs) + ), }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: ret = create_node(kwargs) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on Scaleway\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: %s', - server_['name'], exc, + "Error creating %s on Scaleway\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: %s", + server_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False def __query_node_data(server_name): - ''' Called to check if the server has a public IP address. - ''' - data = show_instance(server_name, 'action') - if data and data.get('public_ip'): + """ Called to check if the server has a public IP address. + """ + data = show_instance(server_name, "action") + if data and data.get("public_ip"): return data return False try: data = salt.utils.cloud.wait_for_ip( __query_node_data, - update_args=(server_['name'],), + update_args=(server_["name"],), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', server_, __opts__, default=10 * 60), + "wait_for_ip_timeout", server_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', server_, __opts__, default=10), + "wait_for_ip_interval", server_, __opts__, default=10 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! - destroy(server_['name']) + destroy(server_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(six.text_type(exc)) - server_['ssh_host'] = data['public_ip']['address'] - server_['ssh_password'] = ssh_password - server_['key_filename'] = key_filename - ret = __utils__['cloud.bootstrap'](server_, __opts__) + server_["ssh_host"] = data["public_ip"]["address"] + server_["ssh_password"] = ssh_password + server_["key_filename"] = key_filename + ret = __utils__["cloud.bootstrap"](server_, __opts__) ret.update(data) - log.info('Created BareMetal server \'%s\'', server_['name']) + log.info("Created BareMetal server '%s'", server_["name"]) log.debug( - '\'%s\' BareMetal server creation details:\n%s', - server_['name'], pprint.pformat(data) + "'%s' BareMetal server creation details:\n%s", + server_["name"], + pprint.pformat(data), ) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(server_['name']), - args=__utils__['cloud.filter_event']('created', server_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(server_["name"]), + args=__utils__["cloud.filter_event"]( + "created", server_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret -def query(method='servers', server_id=None, command=None, args=None, - http_method='get'): - ''' Make a call to the Scaleway API. - ''' - base_path = six.text_type(config.get_cloud_config_value( - 'api_root', - get_configured_provider(), - __opts__, - search_global=False, - default='https://api.cloud.online.net' - )) +def query(method="servers", server_id=None, command=None, args=None, http_method="get"): + """ Make a call to the Scaleway API. + """ + base_path = six.text_type( + config.get_cloud_config_value( + "api_root", + get_configured_provider(), + __opts__, + search_global=False, + default="https://api.cloud.online.net", + ) + ) - path = '{0}/{1}/'.format(base_path, method) + path = "{0}/{1}/".format(base_path, method) if server_id: - path += '{0}/'.format(server_id) + path += "{0}/".format(server_id) if command: path += command @@ -337,24 +350,25 @@ def query(method='servers', server_id=None, command=None, args=None, args = {} token = config.get_cloud_config_value( - 'token', get_configured_provider(), __opts__, search_global=False + "token", get_configured_provider(), __opts__, search_global=False ) data = salt.utils.json.dumps(args) - request = __utils__["http.query"](path, - method=http_method, - data=data, - headers={'X-Auth-Token': token, - 'User-Agent': "salt-cloud", - 'Content-Type': 'application/json'}) + request = __utils__["http.query"]( + path, + method=http_method, + data=data, + headers={ + "X-Auth-Token": token, + "User-Agent": "salt-cloud", + "Content-Type": "application/json", + }, + ) if request.status_code > 299: raise SaltCloudSystemExit( - 'An error occurred while querying Scaleway. HTTP Code: {0} ' - 'Error: \'{1}\''.format( - request.status_code, - request.text - ) + "An error occurred while querying Scaleway. HTTP Code: {0} " + "Error: '{1}'".format(request.status_code, request.text) ) log.debug(request.url) @@ -367,27 +381,27 @@ def query(method='servers', server_id=None, command=None, args=None, def script(server_): - ''' Return the script deployment object. - ''' + """ Return the script deployment object. + """ return salt.utils.cloud.os_script( - config.get_cloud_config_value('script', server_, __opts__), + config.get_cloud_config_value("script", server_, __opts__), server_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, server_) - ) + ), ) def show_instance(name, call=None): - ''' Show the details from a Scaleway BareMetal server. - ''' - if call != 'action': + """ Show the details from a Scaleway BareMetal server. + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) node = _get_node(name) - __utils__['cloud.cache_node'](node, __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](node, __active_provider_name__, __opts__) return node @@ -397,8 +411,9 @@ def _get_node(name): return list_nodes_full()[name] except KeyError: log.debug( - 'Failed to get the data for node \'%s\'. Remaining ' - 'attempts: %s', name, attempt + "Failed to get the data for node '%s'. Remaining " "attempts: %s", + name, + attempt, ) # Just a little delay between attempts... time.sleep(0.5) @@ -406,46 +421,48 @@ def _get_node(name): def destroy(name, call=None): - ''' Destroy a node. Will check termination protection and warn if enabled. + """ Destroy a node. Will check termination protection and warn if enabled. CLI Example: .. code-block:: bash salt-cloud --destroy mymachine - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - data = show_instance(name, call='action') + data = show_instance(name, call="action") node = query( - method='servers', server_id=data['id'], command='action', - args={'action': 'terminate'}, http_method='post' + method="servers", + server_id=data["id"], + command="action", + args={"action": "terminate"}, + http_method="post", ) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir']( - name, __active_provider_name__.split(':')[0], __opts__ + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ ) return node diff --git a/salt/cloud/clouds/softlayer.py b/salt/cloud/clouds/softlayer.py index b01977d7600..f4e13bd36b2 100644 --- a/salt/cloud/clouds/softlayer.py +++ b/salt/cloud/clouds/softlayer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" SoftLayer Cloud Module ====================== @@ -23,16 +23,18 @@ The SoftLayer Python Library needs to be installed in order to use the SoftLayer salt.cloud modules. See: https://pypi.python.org/pypi/SoftLayer :depends: softlayer -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time +import salt.config as config + # Import salt cloud libs import salt.utils.cloud -import salt.config as config from salt.exceptions import SaltCloudSystemExit # Import 3rd-party libs @@ -41,6 +43,7 @@ from salt.ext import six # Attempt to import softlayer lib try: import SoftLayer + HAS_SLLIBS = True except ImportError: HAS_SLLIBS = False @@ -48,14 +51,14 @@ except ImportError: # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'softlayer' +__virtualname__ = "softlayer" # Only load in this module if the SoftLayer configurations are in place def __virtual__(): - ''' + """ Check for SoftLayer configurations. - ''' + """ if get_configured_provider() is False: return False @@ -66,375 +69,372 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('apikey',) + __opts__, __active_provider_name__ or __virtualname__, ("apikey",) ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'softlayer': HAS_SLLIBS} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"softlayer": HAS_SLLIBS}) def script(vm_): - ''' + """ Return the script deployment object - ''' + """ deploy_script = salt.utils.cloud.os_script( - config.get_cloud_config_value('script', vm_, __opts__), + config.get_cloud_config_value("script", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) return deploy_script -def get_conn(service='SoftLayer_Virtual_Guest'): - ''' +def get_conn(service="SoftLayer_Virtual_Guest"): + """ Return a conn object for the passed VM data - ''' + """ client = SoftLayer.Client( username=config.get_cloud_config_value( - 'user', get_configured_provider(), __opts__, search_global=False + "user", get_configured_provider(), __opts__, search_global=False ), api_key=config.get_cloud_config_value( - 'apikey', get_configured_provider(), __opts__, search_global=False + "apikey", get_configured_provider(), __opts__, search_global=False ), ) return client[service] def avail_locations(call=None): - ''' + """ List all available locations - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) ret = {} conn = get_conn() response = conn.getCreateObjectOptions() - #return response - for datacenter in response['datacenters']: - #return data center - ret[datacenter['template']['datacenter']['name']] = { - 'name': datacenter['template']['datacenter']['name'], + # return response + for datacenter in response["datacenters"]: + # return data center + ret[datacenter["template"]["datacenter"]["name"]] = { + "name": datacenter["template"]["datacenter"]["name"], } return ret def avail_sizes(call=None): - ''' + """ Return a dict of all available VM sizes on the cloud provider with relevant data. This data is provided in three dicts. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) ret = { - 'block devices': {}, - 'memory': {}, - 'processors': {}, + "block devices": {}, + "memory": {}, + "processors": {}, } conn = get_conn() response = conn.getCreateObjectOptions() - for device in response['blockDevices']: - #return device['template']['blockDevices'] - ret['block devices'][device['itemPrice']['item']['description']] = { - 'name': device['itemPrice']['item']['description'], - 'capacity': - device['template']['blockDevices'][0]['diskImage']['capacity'], + for device in response["blockDevices"]: + # return device['template']['blockDevices'] + ret["block devices"][device["itemPrice"]["item"]["description"]] = { + "name": device["itemPrice"]["item"]["description"], + "capacity": device["template"]["blockDevices"][0]["diskImage"]["capacity"], } - for memory in response['memory']: - ret['memory'][memory['itemPrice']['item']['description']] = { - 'name': memory['itemPrice']['item']['description'], - 'maxMemory': memory['template']['maxMemory'], + for memory in response["memory"]: + ret["memory"][memory["itemPrice"]["item"]["description"]] = { + "name": memory["itemPrice"]["item"]["description"], + "maxMemory": memory["template"]["maxMemory"], } - for processors in response['processors']: - ret['processors'][processors['itemPrice']['item']['description']] = { - 'name': processors['itemPrice']['item']['description'], - 'start cpus': processors['template']['startCpus'], + for processors in response["processors"]: + ret["processors"][processors["itemPrice"]["item"]["description"]] = { + "name": processors["itemPrice"]["item"]["description"], + "start cpus": processors["template"]["startCpus"], } return ret def avail_images(call=None): - ''' + """ Return a dict of all available VM images on the cloud provider. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) ret = {} conn = get_conn() response = conn.getCreateObjectOptions() - for image in response['operatingSystems']: - ret[image['itemPrice']['item']['description']] = { - 'name': image['itemPrice']['item']['description'], - 'template': image['template']['operatingSystemReferenceCode'], + for image in response["operatingSystems"]: + ret[image["itemPrice"]["item"]["description"]] = { + "name": image["itemPrice"]["item"]["description"], + "template": image["template"]["operatingSystemReferenceCode"], } return ret def list_custom_images(call=None): - ''' + """ Return a dict of all custom VM images on the cloud provider. - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_vlans function must be called with -f or --function.' + "The list_vlans function must be called with -f or --function." ) ret = {} - conn = get_conn('SoftLayer_Account') + conn = get_conn("SoftLayer_Account") response = conn.getBlockDeviceTemplateGroups() for image in response: - if 'globalIdentifier' not in image: + if "globalIdentifier" not in image: continue - ret[image['name']] = { - 'id': image['id'], - 'name': image['name'], - 'globalIdentifier': image['globalIdentifier'], + ret[image["name"]] = { + "id": image["id"], + "name": image["name"], + "globalIdentifier": image["globalIdentifier"], } - if 'note' in image: - ret[image['name']]['note'] = image['note'] + if "note" in image: + ret[image["name"]]["note"] = image["note"] return ret def get_location(vm_=None): - ''' + """ Return the location to use, in this order: - CLI parameter - VM parameter - Cloud profile setting - ''' + """ return __opts__.get( - 'location', + "location", config.get_cloud_config_value( - 'location', + "location", vm_ or get_configured_provider(), __opts__, - #default=DEFAULT_LOCATION, - search_global=False - ) + # default=DEFAULT_LOCATION, + search_global=False, + ), ) def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'softlayer', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or "softlayer", + vm_["profile"], + vm_=vm_, + ) + is False + ): return False except AttributeError: pass - name = vm_['name'] + name = vm_["name"] hostname = name - domain = config.get_cloud_config_value( - 'domain', vm_, __opts__, default=None - ) + domain = config.get_cloud_config_value("domain", vm_, __opts__, default=None) if domain is None: - SaltCloudSystemExit( - 'A domain name is required for the SoftLayer driver.' - ) + SaltCloudSystemExit("A domain name is required for the SoftLayer driver.") - if vm_.get('use_fqdn'): - name = '.'.join([name, domain]) - vm_['name'] = name + if vm_.get("use_fqdn"): + name = ".".join([name, domain]) + vm_["name"] = name - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(name), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(name), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', name) + log.info("Creating Cloud VM %s", name) conn = get_conn() kwargs = { - 'hostname': hostname, - 'domain': domain, - 'startCpus': vm_['cpu_number'], - 'maxMemory': vm_['ram'], - 'hourlyBillingFlag': vm_['hourly_billing'], + "hostname": hostname, + "domain": domain, + "startCpus": vm_["cpu_number"], + "maxMemory": vm_["ram"], + "hourlyBillingFlag": vm_["hourly_billing"], } local_disk_flag = config.get_cloud_config_value( - 'local_disk', vm_, __opts__, default=False + "local_disk", vm_, __opts__, default=False ) - kwargs['localDiskFlag'] = local_disk_flag + kwargs["localDiskFlag"] = local_disk_flag - if 'image' in vm_: - kwargs['operatingSystemReferenceCode'] = vm_['image'] - kwargs['blockDevices'] = [] - disks = vm_['disk_size'] + if "image" in vm_: + kwargs["operatingSystemReferenceCode"] = vm_["image"] + kwargs["blockDevices"] = [] + disks = vm_["disk_size"] if isinstance(disks, int): disks = [six.text_type(disks)] elif isinstance(disks, six.string_types): - disks = [size.strip() for size in disks.split(',')] + disks = [size.strip() for size in disks.split(",")] count = 0 for disk in disks: # device number '1' is reserved for the SWAP disk if count == 1: count += 1 - block_device = {'device': six.text_type(count), - 'diskImage': {'capacity': six.text_type(disk)}} - kwargs['blockDevices'].append(block_device) + block_device = { + "device": six.text_type(count), + "diskImage": {"capacity": six.text_type(disk)}, + } + kwargs["blockDevices"].append(block_device) count += 1 # Upper bound must be 5 as we're skipping '1' for the SWAP disk ID if count > 5: - log.warning('More that 5 disks were specified for %s .' - 'The first 5 disks will be applied to the VM, ' - 'but the remaining disks will be ignored.\n' - 'Please adjust your cloud configuration to only ' - 'specify a maximum of 5 disks.', name) + log.warning( + "More that 5 disks were specified for %s ." + "The first 5 disks will be applied to the VM, " + "but the remaining disks will be ignored.\n" + "Please adjust your cloud configuration to only " + "specify a maximum of 5 disks.", + name, + ) break - elif 'global_identifier' in vm_: - kwargs['blockDeviceTemplateGroup'] = { - 'globalIdentifier': vm_['global_identifier'] + elif "global_identifier" in vm_: + kwargs["blockDeviceTemplateGroup"] = { + "globalIdentifier": vm_["global_identifier"] } location = get_location(vm_) if location: - kwargs['datacenter'] = {'name': location} + kwargs["datacenter"] = {"name": location} private_vlan = config.get_cloud_config_value( - 'private_vlan', vm_, __opts__, default=False + "private_vlan", vm_, __opts__, default=False ) if private_vlan: - kwargs['primaryBackendNetworkComponent'] = { - 'networkVlan': { - 'id': private_vlan, - } - } + kwargs["primaryBackendNetworkComponent"] = {"networkVlan": {"id": private_vlan}} private_network = config.get_cloud_config_value( - 'private_network', vm_, __opts__, default=False + "private_network", vm_, __opts__, default=False ) if bool(private_network) is True: - kwargs['privateNetworkOnlyFlag'] = 'True' + kwargs["privateNetworkOnlyFlag"] = "True" public_vlan = config.get_cloud_config_value( - 'public_vlan', vm_, __opts__, default=False + "public_vlan", vm_, __opts__, default=False ) if public_vlan: - kwargs['primaryNetworkComponent'] = { - 'networkVlan': { - 'id': public_vlan, - } - } + kwargs["primaryNetworkComponent"] = {"networkVlan": {"id": public_vlan}} public_security_groups = config.get_cloud_config_value( - 'public_security_groups', vm_, __opts__, default=False + "public_security_groups", vm_, __opts__, default=False ) if public_security_groups: - secgroups = [{'securityGroup': {'id': int(sg)}} - for sg in public_security_groups] - pnc = kwargs.get('primaryNetworkComponent', {}) - pnc['securityGroupBindings'] = secgroups - kwargs.update({'primaryNetworkComponent': pnc}) + secgroups = [ + {"securityGroup": {"id": int(sg)}} for sg in public_security_groups + ] + pnc = kwargs.get("primaryNetworkComponent", {}) + pnc["securityGroupBindings"] = secgroups + kwargs.update({"primaryNetworkComponent": pnc}) private_security_groups = config.get_cloud_config_value( - 'private_security_groups', vm_, __opts__, default=False + "private_security_groups", vm_, __opts__, default=False ) if private_security_groups: - secgroups = [{'securityGroup': {'id': int(sg)}} - for sg in private_security_groups] - pbnc = kwargs.get('primaryBackendNetworkComponent', {}) - pbnc['securityGroupBindings'] = secgroups - kwargs.update({'primaryBackendNetworkComponent': pbnc}) + secgroups = [ + {"securityGroup": {"id": int(sg)}} for sg in private_security_groups + ] + pbnc = kwargs.get("primaryBackendNetworkComponent", {}) + pbnc["securityGroupBindings"] = secgroups + kwargs.update({"primaryBackendNetworkComponent": pbnc}) max_net_speed = config.get_cloud_config_value( - 'max_net_speed', vm_, __opts__, default=10 + "max_net_speed", vm_, __opts__, default=10 ) if max_net_speed: - kwargs['networkComponents'] = [{ - 'maxSpeed': int(max_net_speed) - }] + kwargs["networkComponents"] = [{"maxSpeed": int(max_net_speed)}] - post_uri = config.get_cloud_config_value( - 'post_uri', vm_, __opts__, default=None - ) + post_uri = config.get_cloud_config_value("post_uri", vm_, __opts__, default=None) if post_uri: - kwargs['postInstallScriptUri'] = post_uri + kwargs["postInstallScriptUri"] = post_uri dedicated_host_id = config.get_cloud_config_value( - 'dedicated_host_id', vm_, __opts__, default=None + "dedicated_host_id", vm_, __opts__, default=None ) if dedicated_host_id: - kwargs['dedicatedHost'] = {'id': dedicated_host_id} + kwargs["dedicatedHost"] = {"id": dedicated_host_id} - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(name), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(name), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', kwargs, list(kwargs)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", kwargs, list(kwargs) + ), }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: response = conn.createObject(kwargs) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on SoftLayer\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: \n%s', name, exc, + "Error creating %s on SoftLayer\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: \n%s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False - ip_type = 'primaryIpAddress' + ip_type = "primaryIpAddress" private_ssh = config.get_cloud_config_value( - 'private_ssh', vm_, __opts__, default=False + "private_ssh", vm_, __opts__, default=False ) private_wds = config.get_cloud_config_value( - 'private_windows', vm_, __opts__, default=False + "private_windows", vm_, __opts__, default=False ) if private_ssh or private_wds or public_vlan is None: - ip_type = 'primaryBackendIpAddress' + ip_type = "primaryBackendIpAddress" def wait_for_ip(): - ''' + """ Wait for the IP address to become available - ''' + """ nodes = list_nodes_full() if ip_type in nodes[hostname]: return nodes[hostname][ip_type] @@ -444,161 +444,166 @@ def create(vm_): ip_address = salt.utils.cloud.wait_for_fun( wait_for_ip, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) - if config.get_cloud_config_value('deploy', vm_, __opts__) is not True: - return show_instance(hostname, call='action') + if config.get_cloud_config_value("deploy", vm_, __opts__) is not True: + return show_instance(hostname, call="action") SSH_PORT = 22 WINDOWS_DS_PORT = 445 managing_port = SSH_PORT - if config.get_cloud_config_value('windows', vm_, __opts__) or \ - config.get_cloud_config_value('win_installer', vm_, __opts__): + if config.get_cloud_config_value( + "windows", vm_, __opts__ + ) or config.get_cloud_config_value("win_installer", vm_, __opts__): managing_port = WINDOWS_DS_PORT ssh_connect_timeout = config.get_cloud_config_value( - 'ssh_connect_timeout', vm_, __opts__, 15 * 60 + "ssh_connect_timeout", vm_, __opts__, 15 * 60 ) connect_timeout = config.get_cloud_config_value( - 'connect_timeout', vm_, __opts__, ssh_connect_timeout + "connect_timeout", vm_, __opts__, ssh_connect_timeout ) - if not salt.utils.cloud.wait_for_port(ip_address, - port=managing_port, - timeout=connect_timeout): - raise SaltCloudSystemExit( - 'Failed to authenticate against remote ssh' - ) + if not salt.utils.cloud.wait_for_port( + ip_address, port=managing_port, timeout=connect_timeout + ): + raise SaltCloudSystemExit("Failed to authenticate against remote ssh") - pass_conn = get_conn(service='SoftLayer_Account') + pass_conn = get_conn(service="SoftLayer_Account") mask = { - 'virtualGuests': { - 'powerState': '', - 'operatingSystem': { - 'passwords': '' - }, - }, + "virtualGuests": {"powerState": "", "operatingSystem": {"passwords": ""}}, } def get_credentials(): - ''' + """ Wait for the password to become available - ''' - node_info = pass_conn.getVirtualGuests(id=response['id'], mask=mask) + """ + node_info = pass_conn.getVirtualGuests(id=response["id"], mask=mask) for node in node_info: - if node['id'] == response['id'] and \ - 'passwords' in node['operatingSystem'] and \ - node['operatingSystem']['passwords']: - return node['operatingSystem']['passwords'][0]['username'], node['operatingSystem']['passwords'][0]['password'] + if ( + node["id"] == response["id"] + and "passwords" in node["operatingSystem"] + and node["operatingSystem"]["passwords"] + ): + return ( + node["operatingSystem"]["passwords"][0]["username"], + node["operatingSystem"]["passwords"][0]["password"], + ) time.sleep(5) return False username, passwd = salt.utils.cloud.wait_for_fun( # pylint: disable=W0633 get_credentials, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) - response['username'] = username - response['password'] = passwd - response['public_ip'] = ip_address + response["username"] = username + response["password"] = passwd + response["public_ip"] = ip_address ssh_username = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, default=username + "ssh_username", vm_, __opts__, default=username ) - vm_['ssh_host'] = ip_address - vm_['password'] = passwd - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + vm_["ssh_host"] = ip_address + vm_["password"] = passwd + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(response) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(name), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(name), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret -def list_nodes_full(mask='mask[id]', call=None): - ''' +def list_nodes_full(mask="mask[id]", call=None): + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) ret = {} - conn = get_conn(service='SoftLayer_Account') + conn = get_conn(service="SoftLayer_Account") response = conn.getVirtualGuests() for node_id in response: - hostname = node_id['hostname'] + hostname = node_id["hostname"] ret[hostname] = node_id - __utils__['cloud.cache_node_list'](ret, __active_provider_name__.split(':')[0], __opts__) + __utils__["cloud.cache_node_list"]( + ret, __active_provider_name__.split(":")[0], __opts__ + ) return ret def list_nodes(call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} nodes = list_nodes_full() - if 'error' in nodes: + if "error" in nodes: raise SaltCloudSystemExit( - 'An error occurred while listing nodes: {0}'.format( - nodes['error']['Errors']['Error']['Message'] + "An error occurred while listing nodes: {0}".format( + nodes["error"]["Errors"]["Error"]["Message"] ) ) for node in nodes: ret[node] = { - 'id': nodes[node]['hostname'], - 'ram': nodes[node]['maxMemory'], - 'cpus': nodes[node]['maxCpu'], + "id": nodes[node]["hostname"], + "ram": nodes[node]["maxMemory"], + "cpus": nodes[node]["maxCpu"], } - if 'primaryIpAddress' in nodes[node]: - ret[node]['public_ips'] = nodes[node]['primaryIpAddress'] - if 'primaryBackendIpAddress' in nodes[node]: - ret[node]['private_ips'] = nodes[node]['primaryBackendIpAddress'] - if 'status' in nodes[node]: - ret[node]['state'] = six.text_type(nodes[node]['status']['name']) + if "primaryIpAddress" in nodes[node]: + ret[node]["public_ips"] = nodes[node]["primaryIpAddress"] + if "primaryBackendIpAddress" in nodes[node]: + ret[node]["private_ips"] = nodes[node]["primaryBackendIpAddress"] + if "status" in nodes[node]: + ret[node]["state"] = six.text_type(nodes[node]["status"]["name"]) return ret def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full(), __opts__['query.selection'], call, + list_nodes_full(), __opts__["query.selection"], call, ) def show_instance(name, call=None): - ''' + """ Show the details from SoftLayer concerning a guest - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) nodes = list_nodes_full() - __utils__['cloud.cache_node'](nodes[name], __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](nodes[name], __active_provider_name__, __opts__) return nodes[name] def destroy(name, call=None): - ''' + """ Destroy a node. CLI Example: @@ -606,48 +611,49 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud --destroy mymachine - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - node = show_instance(name, call='action') + node = show_instance(name, call="action") conn = get_conn() - response = conn.deleteObject(id=node['id']) + response = conn.deleteObject(id=node["id"]) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) return response def list_vlans(call=None): - ''' + """ List all VLANs associated with the account - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_vlans function must be called with -f or --function.' + "The list_vlans function must be called with -f or --function." ) - conn = get_conn(service='SoftLayer_Account') + conn = get_conn(service="SoftLayer_Account") return conn.getNetworkVlans() diff --git a/salt/cloud/clouds/softlayer_hw.py b/salt/cloud/clouds/softlayer_hw.py index 5f188ffc98d..b5a7b74fb14 100644 --- a/salt/cloud/clouds/softlayer_hw.py +++ b/salt/cloud/clouds/softlayer_hw.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" SoftLayer HW Cloud Module ========================= @@ -23,22 +23,25 @@ The SoftLayer Python Library needs to be installed in order to use the SoftLayer salt.cloud modules. See: https://pypi.python.org/pypi/SoftLayer :depends: softlayer -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import decimal import logging import time -import decimal + +import salt.config as config # Import salt cloud libs import salt.utils.cloud -import salt.config as config from salt.exceptions import SaltCloudSystemExit # Attempt to import softlayer lib try: import SoftLayer + HAS_SLLIBS = True except ImportError: HAS_SLLIBS = False @@ -46,14 +49,14 @@ except ImportError: # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'softlayer_hw' +__virtualname__ = "softlayer_hw" # Only load in this module if the SoftLayer configurations are in place def __virtual__(): - ''' + """ Check for SoftLayer configurations. - ''' + """ if get_configured_provider() is False: return False @@ -64,443 +67,435 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('apikey',) + __opts__, __active_provider_name__ or __virtualname__, ("apikey",) ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' - return config.check_driver_dependencies( - __virtualname__, - {'softlayer': HAS_SLLIBS} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"softlayer": HAS_SLLIBS}) def script(vm_): - ''' + """ Return the script deployment object - ''' + """ deploy_script = salt.utils.cloud.os_script( - config.get_cloud_config_value('script', vm_, __opts__), + config.get_cloud_config_value("script", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) return deploy_script -def get_conn(service='SoftLayer_Hardware'): - ''' +def get_conn(service="SoftLayer_Hardware"): + """ Return a conn object for the passed VM data - ''' + """ client = SoftLayer.Client( username=config.get_cloud_config_value( - 'user', get_configured_provider(), __opts__, search_global=False + "user", get_configured_provider(), __opts__, search_global=False ), api_key=config.get_cloud_config_value( - 'apikey', get_configured_provider(), __opts__, search_global=False + "apikey", get_configured_provider(), __opts__, search_global=False ), ) return client[service] def avail_locations(call=None): - ''' + """ List all available locations - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) ret = {} - conn = get_conn(service='SoftLayer_Product_Package') + conn = get_conn(service="SoftLayer_Product_Package") locations = conn.getLocations(id=50) for location in locations: - ret[location['id']] = { - 'id': location['id'], - 'name': location['name'], - 'location': location['longName'], + ret[location["id"]] = { + "id": location["id"], + "name": location["name"], + "location": location["longName"], } available = conn.getAvailableLocations(id=50) for location in available: - if location.get('isAvailable', 0) is 0: + if location.get("isAvailable", 0) is 0: continue - ret[location['locationId']]['available'] = True + ret[location["locationId"]]["available"] = True return ret def avail_sizes(call=None): - ''' + """ Return a dict of all available VM sizes on the cloud provider with relevant data. This data is provided in three dicts. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) ret = {} - conn = get_conn(service='SoftLayer_Product_Package') + conn = get_conn(service="SoftLayer_Product_Package") for category in conn.getCategories(id=50): - if category['categoryCode'] != 'server_core': + if category["categoryCode"] != "server_core": continue - for group in category['groups']: - for price in group['prices']: - ret[price['id']] = price['item'].copy() - del ret[price['id']]['id'] + for group in category["groups"]: + for price in group["prices"]: + ret[price["id"]] = price["item"].copy() + del ret[price["id"]]["id"] return ret def avail_images(call=None): - ''' + """ Return a dict of all available VM images on the cloud provider. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) ret = {} - conn = get_conn(service='SoftLayer_Product_Package') + conn = get_conn(service="SoftLayer_Product_Package") for category in conn.getCategories(id=50): - if category['categoryCode'] != 'os': + if category["categoryCode"] != "os": continue - for group in category['groups']: - for price in group['prices']: - ret[price['id']] = price['item'].copy() - del ret[price['id']]['id'] + for group in category["groups"]: + for price in group["prices"]: + ret[price["id"]] = price["item"].copy() + del ret[price["id"]]["id"] return ret def get_location(vm_=None): - ''' + """ Return the location to use, in this order: - CLI parameter - VM parameter - Cloud profile setting - ''' + """ return __opts__.get( - 'location', + "location", config.get_cloud_config_value( - 'location', + "location", vm_ or get_configured_provider(), __opts__, - #default=DEFAULT_LOCATION, - search_global=False - ) + # default=DEFAULT_LOCATION, + search_global=False, + ), ) def create(vm_): - ''' + """ Create a single VM from a data dict - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and config.is_profile_configured(__opts__, - __active_provider_name__ or 'softlayer_hw', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or "softlayer_hw", + vm_["profile"], + vm_=vm_, + ) + is False + ): return False except AttributeError: pass - name = vm_['name'] + name = vm_["name"] hostname = name - domain = config.get_cloud_config_value( - 'domain', vm_, __opts__, default=None - ) + domain = config.get_cloud_config_value("domain", vm_, __opts__, default=None) if domain is None: - SaltCloudSystemExit( - 'A domain name is required for the SoftLayer driver.' - ) + SaltCloudSystemExit("A domain name is required for the SoftLayer driver.") - if vm_.get('use_fqdn'): - name = '.'.join([name, domain]) - vm_['name'] = name + if vm_.get("use_fqdn"): + name = ".".join([name, domain]) + vm_["name"] = name - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(name), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(name), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.info('Creating Cloud VM %s', name) - conn = get_conn(service='SoftLayer_Product_Order') + log.info("Creating Cloud VM %s", name) + conn = get_conn(service="SoftLayer_Product_Order") kwargs = { - 'complexType': 'SoftLayer_Container_Product_Order_Hardware_Server', - 'quantity': 1, - 'hardware': [{ - 'hostname': hostname, - 'domain': domain, - }], + "complexType": "SoftLayer_Container_Product_Order_Hardware_Server", + "quantity": 1, + "hardware": [{"hostname": hostname, "domain": domain}], # Baremetal Package - 'packageId': 50, - 'prices': [ + "packageId": 50, + "prices": [ # Size Ex: 1921: 2 x 2.0 GHz Core Bare Metal Instance - 2 GB Ram - {'id': vm_['size']}, + {"id": vm_["size"]}, # HDD Ex: 19: 250GB SATA II - {'id': vm_['hdd']}, + {"id": vm_["hdd"]}, # Image Ex: 13963: CentOS 6.0 - Minimal Install (64 bit) - {'id': vm_['image']}, - + {"id": vm_["image"]}, # The following items are currently required # Reboot / Remote Console - {'id': '905'}, + {"id": "905"}, # 1 IP Address - {'id': '21'}, + {"id": "21"}, # Host Ping Monitoring - {'id': '55'}, + {"id": "55"}, # Email and Ticket Notifications - {'id': '57'}, + {"id": "57"}, # Automated Notification Response - {'id': '58'}, + {"id": "58"}, # Unlimited SSL VPN Users & 1 PPTP VPN User per account - {'id': '420'}, + {"id": "420"}, # Nessus Vulnerability Assessment & Reporting - {'id': '418'}, + {"id": "418"}, ], } optional_products = config.get_cloud_config_value( - 'optional_products', vm_, __opts__, default=[] + "optional_products", vm_, __opts__, default=[] ) for product in optional_products: - kwargs['prices'].append({'id': product}) + kwargs["prices"].append({"id": product}) # Default is 273 (100 Mbps Public & Private Networks) - port_speed = config.get_cloud_config_value( - 'port_speed', vm_, __opts__, default=273 - ) - kwargs['prices'].append({'id': port_speed}) + port_speed = config.get_cloud_config_value("port_speed", vm_, __opts__, default=273) + kwargs["prices"].append({"id": port_speed}) # Default is 1800 (0 GB Bandwidth) - bandwidth = config.get_cloud_config_value( - 'bandwidth', vm_, __opts__, default=1800 - ) - kwargs['prices'].append({'id': bandwidth}) + bandwidth = config.get_cloud_config_value("bandwidth", vm_, __opts__, default=1800) + kwargs["prices"].append({"id": bandwidth}) - post_uri = config.get_cloud_config_value( - 'post_uri', vm_, __opts__, default=None - ) + post_uri = config.get_cloud_config_value("post_uri", vm_, __opts__, default=None) if post_uri: - kwargs['prices'].append({'id': post_uri}) + kwargs["prices"].append({"id": post_uri}) - vlan_id = config.get_cloud_config_value( - 'vlan', vm_, __opts__, default=False - ) + vlan_id = config.get_cloud_config_value("vlan", vm_, __opts__, default=False) if vlan_id: - kwargs['primaryNetworkComponent'] = { - 'networkVlan': { - 'id': vlan_id, - } - } + kwargs["primaryNetworkComponent"] = {"networkVlan": {"id": vlan_id}} location = get_location(vm_) if location: - kwargs['location'] = location + kwargs["location"] = location - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(name), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(name), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', kwargs, list(kwargs)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", kwargs, list(kwargs) + ), }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: response = conn.placeOrder(kwargs) # Leaving the following line in, commented, for easy debugging - #response = conn.verifyOrder(kwargs) + # response = conn.verifyOrder(kwargs) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on SoftLayer\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: \n%s', name, exc, + "Error creating %s on SoftLayer\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: \n%s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False def wait_for_ip(): - ''' + """ Wait for the IP address to become available - ''' + """ nodes = list_nodes_full() - if 'primaryIpAddress' in nodes[hostname]: - return nodes[hostname]['primaryIpAddress'] + if "primaryIpAddress" in nodes[hostname]: + return nodes[hostname]["primaryIpAddress"] time.sleep(1) return False ip_address = salt.utils.cloud.wait_for_fun( wait_for_ip, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) ssh_connect_timeout = config.get_cloud_config_value( # 15 minutes - 'ssh_connect_timeout', vm_, __opts__, 900 + "ssh_connect_timeout", + vm_, + __opts__, + 900, ) - if not salt.utils.cloud.wait_for_port(ip_address, - timeout=ssh_connect_timeout): - raise SaltCloudSystemExit( - 'Failed to authenticate against remote ssh' - ) + if not salt.utils.cloud.wait_for_port(ip_address, timeout=ssh_connect_timeout): + raise SaltCloudSystemExit("Failed to authenticate against remote ssh") - pass_conn = get_conn(service='SoftLayer_Account') + pass_conn = get_conn(service="SoftLayer_Account") mask = { - 'virtualGuests': { - 'powerState': '', - 'operatingSystem': { - 'passwords': '' - }, - }, + "virtualGuests": {"powerState": "", "operatingSystem": {"passwords": ""}}, } def get_passwd(): - ''' + """ Wait for the password to become available - ''' - node_info = pass_conn.getVirtualGuests(id=response['id'], mask=mask) + """ + node_info = pass_conn.getVirtualGuests(id=response["id"], mask=mask) for node in node_info: - if node['id'] == response['id'] \ - and 'passwords' in node['operatingSystem'] \ - and node['operatingSystem']['passwords']: - return node['operatingSystem']['passwords'][0]['password'] + if ( + node["id"] == response["id"] + and "passwords" in node["operatingSystem"] + and node["operatingSystem"]["passwords"] + ): + return node["operatingSystem"]["passwords"][0]["password"] time.sleep(5) return False passwd = salt.utils.cloud.wait_for_fun( get_passwd, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) - response['password'] = passwd - response['public_ip'] = ip_address + response["password"] = passwd + response["public_ip"] = ip_address ssh_username = config.get_cloud_config_value( - 'ssh_username', vm_, __opts__, default='root' + "ssh_username", vm_, __opts__, default="root" ) - vm_['ssh_host'] = ip_address - vm_['password'] = passwd - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + vm_["ssh_host"] = ip_address + vm_["password"] = passwd + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(response) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(name), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(name), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret -def list_nodes_full(mask='mask[id, hostname, primaryIpAddress, \ - primaryBackendIpAddress, processorPhysicalCoreAmount, memoryCount]', - call=None): - ''' +def list_nodes_full( + mask="mask[id, hostname, primaryIpAddress, \ + primaryBackendIpAddress, processorPhysicalCoreAmount, memoryCount]", + call=None, +): + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) ret = {} - conn = get_conn(service='SoftLayer_Account') + conn = get_conn(service="SoftLayer_Account") response = conn.getHardware(mask=mask) for node in response: - ret[node['hostname']] = node - __utils__['cloud.cache_node_list'](ret, __active_provider_name__.split(':')[0], __opts__) + ret[node["hostname"]] = node + __utils__["cloud.cache_node_list"]( + ret, __active_provider_name__.split(":")[0], __opts__ + ) return ret def list_nodes(call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} nodes = list_nodes_full() - if 'error' in nodes: + if "error" in nodes: raise SaltCloudSystemExit( - 'An error occurred while listing nodes: {0}'.format( - nodes['error']['Errors']['Error']['Message'] + "An error occurred while listing nodes: {0}".format( + nodes["error"]["Errors"]["Error"]["Message"] ) ) for node in nodes: ret[node] = { - 'id': nodes[node]['hostname'], - 'ram': nodes[node]['memoryCount'], - 'cpus': nodes[node]['processorPhysicalCoreAmount'], + "id": nodes[node]["hostname"], + "ram": nodes[node]["memoryCount"], + "cpus": nodes[node]["processorPhysicalCoreAmount"], } - if 'primaryIpAddress' in nodes[node]: - ret[node]['public_ips'] = nodes[node]['primaryIpAddress'] - if 'primaryBackendIpAddress' in nodes[node]: - ret[node]['private_ips'] = nodes[node]['primaryBackendIpAddress'] + if "primaryIpAddress" in nodes[node]: + ret[node]["public_ips"] = nodes[node]["primaryIpAddress"] + if "primaryBackendIpAddress" in nodes[node]: + ret[node]["private_ips"] = nodes[node]["primaryBackendIpAddress"] return ret def list_nodes_select(call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full(), __opts__['query.selection'], call, + list_nodes_full(), __opts__["query.selection"], call, ) def show_instance(name, call=None): - ''' + """ Show the details from SoftLayer concerning a guest - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) nodes = list_nodes_full() - __utils__['cloud.cache_node'](nodes[name], __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](nodes[name], __active_provider_name__, __opts__) return nodes[name] def destroy(name, call=None): - ''' + """ Destroy a node. CLI Example: @@ -508,63 +503,64 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud --destroy mymachine - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - node = show_instance(name, call='action') - conn = get_conn(service='SoftLayer_Ticket') + node = show_instance(name, call="action") + conn = get_conn(service="SoftLayer_Ticket") response = conn.createCancelServerTicket( { - 'id': node['id'], - 'reason': 'Salt Cloud Hardware Server Cancellation', - 'content': 'Please cancel this server', - 'cancelAssociatedItems': True, - 'attachmentType': 'HARDWARE', + "id": node["id"], + "reason": "Salt Cloud Hardware Server Cancellation", + "content": "Please cancel this server", + "cancelAssociatedItems": True, + "attachmentType": "HARDWARE", } ) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) return response def list_vlans(call=None): - ''' + """ List all VLANs associated with the account - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_vlans function must be called with -f or --function.' + "The list_vlans function must be called with -f or --function." ) - conn = get_conn(service='SoftLayer_Account') + conn = get_conn(service="SoftLayer_Account") return conn.getNetworkVlans() def show_pricing(kwargs=None, call=None): - ''' + """ Show pricing for a particular profile. This is only an estimate, based on unofficial pricing sources. @@ -583,81 +579,81 @@ def show_pricing(kwargs=None, call=None): salt-cloud -f update_pricing .. versionadded:: 2015.8.0 - ''' - profile = __opts__['profiles'].get(kwargs['profile'], {}) + """ + profile = __opts__["profiles"].get(kwargs["profile"], {}) if not profile: - return {'Error': 'The requested profile was not found'} + return {"Error": "The requested profile was not found"} # Make sure the profile belongs to Softlayer HW - provider = profile.get('provider', '0:0') - comps = provider.split(':') - if len(comps) < 2 or comps[1] != 'softlayer_hw': - return {'Error': 'The requested profile does not belong to Softlayer HW'} + provider = profile.get("provider", "0:0") + comps = provider.split(":") + if len(comps) < 2 or comps[1] != "softlayer_hw": + return {"Error": "The requested profile does not belong to Softlayer HW"} raw = {} ret = {} - ret['per_hour'] = 0 - conn = get_conn(service='SoftLayer_Product_Item_Price') + ret["per_hour"] = 0 + conn = get_conn(service="SoftLayer_Product_Item_Price") for item in profile: - if item in ('profile', 'provider', 'location'): + if item in ("profile", "provider", "location"): continue price = conn.getObject(id=profile[item]) raw[item] = price - ret['per_hour'] += decimal.Decimal(price.get('hourlyRecurringFee', 0)) + ret["per_hour"] += decimal.Decimal(price.get("hourlyRecurringFee", 0)) - ret['per_day'] = ret['per_hour'] * 24 - ret['per_week'] = ret['per_day'] * 7 - ret['per_month'] = ret['per_day'] * 30 - ret['per_year'] = ret['per_week'] * 52 + ret["per_day"] = ret["per_hour"] * 24 + ret["per_week"] = ret["per_day"] * 7 + ret["per_month"] = ret["per_day"] * 30 + ret["per_year"] = ret["per_week"] * 52 - if kwargs.get('raw', False): - ret['_raw'] = raw + if kwargs.get("raw", False): + ret["_raw"] = raw - return {profile['profile']: ret} + return {profile["profile"]: ret} def show_all_prices(call=None, kwargs=None): - ''' + """ Return a dict of all prices on the cloud provider. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The show_all_prices function must be called with -f or --function.' + "The show_all_prices function must be called with -f or --function." ) if kwargs is None: kwargs = {} - conn = get_conn(service='SoftLayer_Product_Package') - if 'code' not in kwargs: + conn = get_conn(service="SoftLayer_Product_Package") + if "code" not in kwargs: return conn.getCategories(id=50) ret = {} for category in conn.getCategories(id=50): - if category['categoryCode'] != kwargs['code']: + if category["categoryCode"] != kwargs["code"]: continue - for group in category['groups']: - for price in group['prices']: - ret[price['id']] = price['item'].copy() - del ret[price['id']]['id'] + for group in category["groups"]: + for price in group["prices"]: + ret[price["id"]] = price["item"].copy() + del ret[price["id"]]["id"] return ret def show_all_categories(call=None): - ''' + """ Return a dict of all available categories on the cloud provider. .. versionadded:: 2016.3.0 - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The show_all_categories function must be called with -f or --function.' + "The show_all_categories function must be called with -f or --function." ) - conn = get_conn(service='SoftLayer_Product_Package') + conn = get_conn(service="SoftLayer_Product_Package") categories = [] for category in conn.getCategories(id=50): - categories.append(category['categoryCode']) + categories.append(category["categoryCode"]) - return {'category_codes': categories} + return {"category_codes": categories} diff --git a/salt/cloud/clouds/tencentcloud.py b/salt/cloud/clouds/tencentcloud.py index 07031a3f41e..e194d5f6b6d 100644 --- a/salt/cloud/clouds/tencentcloud.py +++ b/salt/cloud/clouds/tencentcloud.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Tencent Cloud Cloud Module ============================= @@ -24,26 +24,28 @@ To use this module, set up the cloud configuration at location: ap-guangzhou :depends: tencentcloud-sdk-python -''' +""" # pylint: disable=invalid-name,redefined-builtin,function-redefined,undefined-variable,broad-except,too-many-locals,too-many-branches # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time -import pprint + import logging +import pprint +import time + +import salt.config as config # Import salt cloud libs import salt.utils.cloud import salt.utils.data import salt.utils.json -import salt.config as config from salt.exceptions import ( + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, SaltCloudNotFound, SaltCloudSystemExit, - SaltCloudExecutionFailure, - SaltCloudExecutionTimeout ) # Import 3rd-party libs @@ -53,11 +55,16 @@ from salt.ext.six.moves import range try: # Try import tencentcloud sdk from tencentcloud.common import credential # pylint: disable=no-name-in-module - from tencentcloud.common.profile.client_profile import ClientProfile # pylint: disable=no-name-in-module - from tencentcloud.cvm.v20170312 import cvm_client # pylint: disable=no-name-in-module - from tencentcloud.cvm.v20170312 import models as cvm_models # pylint: disable=no-name-in-module - from tencentcloud.vpc.v20170312 import vpc_client # pylint: disable=no-name-in-module - from tencentcloud.vpc.v20170312 import models as vpc_models # pylint: disable=no-name-in-module + + # pylint: disable=no-name-in-module + from tencentcloud.common.profile.client_profile import ClientProfile + from tencentcloud.cvm.v20170312 import cvm_client + from tencentcloud.cvm.v20170312 import models as cvm_models + from tencentcloud.vpc.v20170312 import vpc_client + from tencentcloud.vpc.v20170312 import models as vpc_models + + # pylint: enable=no-name-in-module + HAS_TENCENTCLOUD_SDK = True except ImportError: HAS_TENCENTCLOUD_SDK = False @@ -66,16 +73,16 @@ except ImportError: log = logging.getLogger(__name__) # The default region -DEFAULT_REGION = 'ap-guangzhou' +DEFAULT_REGION = "ap-guangzhou" # The Tencent Cloud -__virtualname__ = 'tencentcloud' +__virtualname__ = "tencentcloud" def __virtual__(): - ''' + """ Only load in this module if the Tencent Cloud configurations are in place - ''' + """ if get_configured_provider() is False: return False @@ -86,34 +93,31 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('id', 'key') + __opts__, __active_provider_name__ or __virtualname__, ("id", "key") ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' + """ return config.check_driver_dependencies( - __virtualname__, - {'tencentcloud-sdk-python': HAS_TENCENTCLOUD_SDK} + __virtualname__, {"tencentcloud-sdk-python": HAS_TENCENTCLOUD_SDK} ) def get_provider_client(name=None): - ''' + """ Return a new provider client - ''' + """ provider = get_configured_provider() - secretId = provider.get('id') - secretKey = provider.get('key') + secretId = provider.get("id") + secretKey = provider.get("key") region = __get_location(None) cpf = ClientProfile() @@ -125,15 +129,13 @@ def get_provider_client(name=None): elif name == "vpc_client": client = vpc_client.VpcClient(crd, region, cpf) else: - raise SaltCloudSystemExit( - 'Client name {0} is not supported'.format(name) - ) + raise SaltCloudSystemExit("Client name {0} is not supported".format(name)) return client def avail_locations(call=None): - ''' + """ Return Tencent Cloud available region CLI Example: @@ -142,11 +144,11 @@ def avail_locations(call=None): salt-cloud --list-locations my-tencentcloud-config salt-cloud -f avail_locations my-tencentcloud-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) client = get_provider_client("cvm_client") @@ -163,7 +165,7 @@ def avail_locations(call=None): def avail_images(call=None): - ''' + """ Return Tencent Cloud available image CLI Example: @@ -172,18 +174,20 @@ def avail_images(call=None): salt-cloud --list-images my-tencentcloud-config salt-cloud -f avail_images my-tencentcloud-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) - return _get_images(["PUBLIC_IMAGE", "PRIVATE_IMAGE", "IMPORT_IMAGE", "SHARED_IMAGE"]) + return _get_images( + ["PUBLIC_IMAGE", "PRIVATE_IMAGE", "IMPORT_IMAGE", "SHARED_IMAGE"] + ) def avail_sizes(call=None): - ''' + """ Return Tencent Cloud available instance type CLI Example: @@ -192,11 +196,11 @@ def avail_sizes(call=None): salt-cloud --list-sizes my-tencentcloud-config salt-cloud -f avail_sizes my-tencentcloud-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) client = get_provider_client("cvm_client") @@ -212,14 +216,13 @@ def avail_sizes(call=None): "CPU": "{0}-Core".format(typeConfig.CPU), } if typeConfig.GPU: - ret[typeConfig.InstanceType]["GPU"] = "{0}-Core".format( - typeConfig.GPU) + ret[typeConfig.InstanceType]["GPU"] = "{0}-Core".format(typeConfig.GPU) return ret def list_securitygroups(call=None): - ''' + """ Return all Tencent Cloud security groups in current region CLI Example: @@ -227,10 +230,10 @@ def list_securitygroups(call=None): .. code-block:: bash salt-cloud -f list_securitygroups my-tencentcloud-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_securitygroups function must be called with -f or --function.' + "The list_securitygroups function must be called with -f or --function." ) client = get_provider_client("vpc_client") @@ -253,7 +256,7 @@ def list_securitygroups(call=None): def list_custom_images(call=None): - ''' + """ Return all Tencent Cloud images in current region CLI Example: @@ -261,17 +264,17 @@ def list_custom_images(call=None): .. code-block:: bash salt-cloud -f list_custom_images my-tencentcloud-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_custom_images function must be called with -f or --function.' + "The list_custom_images function must be called with -f or --function." ) return _get_images(["PRIVATE_IMAGE", "IMPORT_IMAGE"]) def list_availability_zones(call=None): - ''' + """ Return all Tencent Cloud availability zones in current region CLI Example: @@ -279,10 +282,10 @@ def list_availability_zones(call=None): .. code-block:: bash salt-cloud -f list_availability_zones my-tencentcloud-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_availability_zones function must be called with -f or --function.' + "The list_availability_zones function must be called with -f or --function." ) client = get_provider_client("cvm_client") @@ -293,13 +296,13 @@ def list_availability_zones(call=None): for zone in resp.ZoneSet: if zone.ZoneState != "AVAILABLE": continue - ret[zone.Zone] = zone.ZoneName, + ret[zone.Zone] = (zone.ZoneName,) return ret def list_nodes(call=None): - ''' + """ Return a list of instances that are on the provider CLI Examples: @@ -307,10 +310,10 @@ def list_nodes(call=None): .. code-block:: bash salt-cloud -Q - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) ret = {} @@ -330,7 +333,7 @@ def list_nodes(call=None): def list_nodes_full(call=None): - ''' + """ Return a list of instances that are on the provider, with full details CLI Examples: @@ -338,10 +341,10 @@ def list_nodes_full(call=None): .. code-block:: bash salt-cloud -F - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) ret = {} @@ -349,23 +352,30 @@ def list_nodes_full(call=None): for instance in nodes: instanceAttribute = vars(instance) ret[instance.InstanceName] = instanceAttribute - for k in ["DataDisks", "InternetAccessible", "LoginSettings", - "Placement", "SystemDisk", "Tags", "VirtualPrivateCloud"]: + for k in [ + "DataDisks", + "InternetAccessible", + "LoginSettings", + "Placement", + "SystemDisk", + "Tags", + "VirtualPrivateCloud", + ]: ret[instance.InstanceName][k] = six.text_type(instanceAttribute[k]) - provider = __active_provider_name__ or 'tencentcloud' - if ':' in provider: - comps = provider.split(':') + provider = __active_provider_name__ or "tencentcloud" + if ":" in provider: + comps = provider.split(":") provider = comps[0] - __opts__['update_cachedir'] = True - __utils__['cloud.cache_node_list'](ret, provider, __opts__) + __opts__["update_cachedir"] = True + __utils__["cloud.cache_node_list"](ret, provider, __opts__) return ret def list_nodes_select(call=None): - ''' + """ Return a list of instances that are on the provider, with select fields CLI Examples: @@ -373,14 +383,14 @@ def list_nodes_select(call=None): .. code-block:: bash salt-cloud -S - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full('function'), __opts__['query.selection'], call, + list_nodes_full("function"), __opts__["query.selection"], call, ) def list_nodes_min(call=None): - ''' + """ Return a list of instances that are on the provider, Only names, and their state, is returned. CLI Examples: @@ -388,10 +398,10 @@ def list_nodes_min(call=None): .. code-block:: bash salt-cloud -f list_nodes_min my-tencentcloud-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_min function must be called with -f or --function.' + "The list_nodes_min function must be called with -f or --function." ) ret = {} @@ -406,7 +416,7 @@ def list_nodes_min(call=None): def create(vm_): - ''' + """ Create a single Tencent Cloud instance from a data dict. Tencent Cloud profiles require a ``provider``, ``availability_zone``, ``image`` and ``size``. @@ -430,34 +440,40 @@ def create(vm_): .. code-block:: bash salt-cloud -p tencentcloud-guangzhou-s1 myinstance - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_['profile'] and \ - config.is_profile_configured(__opts__, - __active_provider_name__ or 'tencentcloud', - vm_['profile'], - vm_=vm_) is False: + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, + __active_provider_name__ or "tencentcloud", + vm_["profile"], + vm_=vm_, + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']( - 'creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.debug('Try creating instance: %s', pprint.pformat(vm_)) + log.debug("Try creating instance: %s", pprint.pformat(vm_)) # Init cvm client client = get_provider_client("cvm_client") req = cvm_models.RunInstancesRequest() - req.InstanceName = vm_['name'] + req.InstanceName = vm_["name"] # Required parameters req.InstanceType = __get_size(vm_) @@ -470,22 +486,23 @@ def create(vm_): # Optional parameters req.SecurityGroupIds = __get_securitygroups(vm_) - req.HostName = vm_.get("hostname", vm_['name']) + req.HostName = vm_.get("hostname", vm_["name"]) - req.InstanceChargeType = vm_.get( - "instance_charge_type", "POSTPAID_BY_HOUR") + req.InstanceChargeType = vm_.get("instance_charge_type", "POSTPAID_BY_HOUR") if req.InstanceChargeType == "PREPAID": - period = vm_.get( - "instance_charge_type_prepaid_period", 1) - renewFlag = vm_.get("instance_charge_type_prepaid_renew_flag", - "NOTIFY_AND_MANUAL_RENEW") + period = vm_.get("instance_charge_type_prepaid_period", 1) + renewFlag = vm_.get( + "instance_charge_type_prepaid_renew_flag", "NOTIFY_AND_MANUAL_RENEW" + ) req.InstanceChargePrepaid = {"Period": period, "RenewFlag": renewFlag} allocate_public_ip = vm_.get("allocate_public_ip", False) internet_max_bandwidth_out = vm_.get("internet_max_bandwidth_out", 0) if allocate_public_ip and internet_max_bandwidth_out > 0: - req.InternetAccessible = {"PublicIpAssigned": allocate_public_ip, - "InternetMaxBandwidthOut": internet_max_bandwidth_out} + req.InternetAccessible = { + "PublicIpAssigned": allocate_public_ip, + "InternetMaxBandwidthOut": internet_max_bandwidth_out, + } internet_charge_type = vm_.get("internet_charge_type", "") if internet_charge_type != "": req.InternetAccessible["InternetChargeType"] = internet_charge_type @@ -522,37 +539,35 @@ def create(vm_): if system_disk_type: req.SystemDisk["DiskType"] = system_disk_type - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']( - 'requesting', vm_, list(vm_)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]("requesting", vm_, list(vm_)), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: resp = client.RunInstances(req) if not resp.InstanceIdSet: - raise SaltCloudSystemExit( - "Unexpected error, no instance created" - ) + raise SaltCloudSystemExit("Unexpected error, no instance created") except Exception as exc: log.error( - 'Error creating %s on tencentcloud\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment: %s', - vm_['name'], six.text_type(exc), + "Error creating %s on tencentcloud\n\n" + "The following exception was thrown when trying to " + "run the initial deployment: %s", + vm_["name"], + six.text_type(exc), # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False time.sleep(5) def __query_node_data(vm_name): - data = show_instance(vm_name, call='action') + data = show_instance(vm_name, call="action") if not data: return False if data["InstanceState"] != "RUNNING": @@ -563,15 +578,17 @@ def create(vm_): try: data = salt.utils.cloud.wait_for_ip( __query_node_data, - update_args=(vm_['name'],), + update_args=(vm_["name"],), timeout=config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 + ), interval=config.get_cloud_config_value( - 'wait_for_ip_interval', vm_, __opts__, default=10), + "wait_for_ip_interval", vm_, __opts__, default=10 + ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: - destroy(vm_['name']) + destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: @@ -582,34 +599,34 @@ def create(vm_): elif data["PrivateIpAddresses"]: ssh_ip = data["PrivateIpAddresses"][0] else: - log.error('No available ip: cant connect to salt') + log.error("No available ip: cant connect to salt") return False - log.debug('Instance %s: %s is now running', vm_['name'], ssh_ip) - vm_['ssh_host'] = ssh_ip + log.debug("Instance %s: %s is now running", vm_["name"], ssh_ip) + vm_["ssh_host"] = ssh_ip # The instance is booted and accessible, let's Salt it! - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data) - log.debug('\'%s\' instance creation details:\n%s', - vm_['name'], pprint.pformat(data)) + log.debug("'%s' instance creation details:\n%s", vm_["name"], pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']( - 'created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def start(name, call=None): - ''' + """ Start a Tencent Cloud instance Notice: the instance state must be stopped @@ -618,11 +635,9 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start myinstance - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") node = _get_node(name) @@ -635,7 +650,7 @@ def start(name, call=None): def stop(name, force=False, call=None): - ''' + """ Stop a Tencent Cloud running instance Note: use `force=True` to make force stop @@ -645,11 +660,9 @@ def stop(name, force=False, call=None): salt-cloud -a stop myinstance salt-cloud -a stop myinstance force=True - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") node = _get_node(name) @@ -664,7 +677,7 @@ def stop(name, force=False, call=None): def reboot(name, call=None): - ''' + """ Reboot a Tencent Cloud instance CLI Examples: @@ -672,11 +685,9 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot myinstance - ''' - if call != 'action': - raise SaltCloudSystemExit( - 'The stop action must be called with -a or --action.' - ) + """ + if call != "action": + raise SaltCloudSystemExit("The stop action must be called with -a or --action.") node = _get_node(name) @@ -689,7 +700,7 @@ def reboot(name, call=None): def destroy(name, call=None): - ''' + """ Destroy a Tencent Cloud instance CLI Example: @@ -698,19 +709,19 @@ def destroy(name, call=None): salt-cloud -a destroy myinstance salt-cloud -d myinstance - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, -a or --action.' + "The destroy action must be called with -d, --destroy, -a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) node = _get_node(name) @@ -720,34 +731,34 @@ def destroy(name, call=None): req.InstanceIds = [node.InstanceId] resp = client.TerminateInstances(req) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return resp def script(vm_): - ''' + """ Return the script deployment object - ''' + """ return salt.utils.cloud.os_script( - config.get_cloud_config_value('script', vm_, __opts__), + config.get_cloud_config_value("script", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) def show_image(kwargs, call=None): - ''' + """ Show the details of Tencent Cloud image CLI Examples: @@ -755,19 +766,19 @@ def show_image(kwargs, call=None): .. code-block:: bash salt-cloud -f show_image tencentcloud image=img-31tjrtph - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The show_image function must be called with -f or --function' + "The show_image function must be called with -f or --function" ) if not isinstance(kwargs, dict): kwargs = {} - if 'image' not in kwargs: - raise SaltCloudSystemExit('No image specified.') + if "image" not in kwargs: + raise SaltCloudSystemExit("No image specified.") - image = kwargs['image'] + image = kwargs["image"] client = get_provider_client("cvm_client") req = cvm_models.DescribeImagesRequest() @@ -776,7 +787,7 @@ def show_image(kwargs, call=None): if not resp.ImageSet: raise SaltCloudNotFound( - 'The specified image \'{0}\' could not be found.'.format(image) + "The specified image '{0}' could not be found.".format(image) ) ret = {} @@ -795,7 +806,7 @@ def show_image(kwargs, call=None): def show_instance(name, call=None): - ''' + """ Show the details of Tencent Cloud instance CLI Examples: @@ -803,23 +814,30 @@ def show_instance(name, call=None): .. code-block:: bash salt-cloud -a show_instance myinstance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) node = _get_node(name) ret = vars(node) - for k in ["DataDisks", "InternetAccessible", "LoginSettings", - "Placement", "SystemDisk", "Tags", "VirtualPrivateCloud"]: + for k in [ + "DataDisks", + "InternetAccessible", + "LoginSettings", + "Placement", + "SystemDisk", + "Tags", + "VirtualPrivateCloud", + ]: ret[k] = six.text_type(ret[k]) return ret def show_disk(name, call=None): - ''' + """ Show the disk details of Tencent Cloud instance CLI Examples: @@ -827,10 +845,10 @@ def show_disk(name, call=None): .. code-block:: bash salt-cloud -a show_disk myinstance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_disks action must be called with -a or --action.' + "The show_disks action must be called with -a or --action." ) node = _get_node(name) @@ -858,9 +876,9 @@ def show_disk(name, call=None): def _get_node(name): - ''' + """ Return Tencent Cloud instance detail by name - ''' + """ attempts = 5 while attempts >= 0: try: @@ -872,19 +890,20 @@ def _get_node(name): except Exception as ex: attempts -= 1 log.debug( - 'Failed to get data for node \'%s\': %s. Remaining attempts: %d', name, ex, attempts + "Failed to get data for node '%s': %s. Remaining attempts: %d", + name, + ex, + attempts, ) time.sleep(0.5) - raise SaltCloudNotFound( - 'Failed to get instance info {0}'.format(name) - ) + raise SaltCloudNotFound("Failed to get instance info {0}".format(name)) def _get_nodes(): - ''' + """ Return all list of Tencent Cloud instances - ''' + """ ret = [] offset = 0 limit = 100 @@ -905,9 +924,9 @@ def _get_nodes(): def _get_images(image_type): - ''' + """ Return all list of Tencent Cloud images - ''' + """ client = get_provider_client("cvm_client") req = cvm_models.DescribeImagesRequest() req.Filters = [{"Name": "image-type", "Values": image_type}] @@ -932,42 +951,42 @@ def _get_images(image_type): def __get_image(vm_): - vm_image = six.text_type(config.get_cloud_config_value( - 'image', vm_, __opts__, search_global=False - )) + vm_image = six.text_type( + config.get_cloud_config_value("image", vm_, __opts__, search_global=False) + ) if not vm_image: - raise SaltCloudNotFound('No image specified.') + raise SaltCloudNotFound("No image specified.") images = avail_images() if vm_image in images: return vm_image raise SaltCloudNotFound( - 'The specified image \'{0}\' could not be found.'.format(vm_image) + "The specified image '{0}' could not be found.".format(vm_image) ) def __get_size(vm_): - vm_size = six.text_type(config.get_cloud_config_value( - 'size', vm_, __opts__, search_global=False - )) + vm_size = six.text_type( + config.get_cloud_config_value("size", vm_, __opts__, search_global=False) + ) if not vm_size: - raise SaltCloudNotFound('No size specified.') + raise SaltCloudNotFound("No size specified.") sizes = avail_sizes() if vm_size in sizes: return vm_size raise SaltCloudNotFound( - 'The specified size \'{0}\' could not be found.'.format(vm_size) + "The specified size '{0}' could not be found.".format(vm_size) ) def __get_securitygroups(vm_): vm_securitygroups = config.get_cloud_config_value( - 'securitygroups', vm_, __opts__, search_global=False + "securitygroups", vm_, __opts__, search_global=False ) if not vm_securitygroups: @@ -978,7 +997,7 @@ def __get_securitygroups(vm_): vm_securitygroups[i] = six.text_type(vm_securitygroups[i]) if vm_securitygroups[i] not in securitygroups: raise SaltCloudNotFound( - 'The specified securitygroups \'{0}\' could not be found.'.format( + "The specified securitygroups '{0}' could not be found.".format( vm_securitygroups[i] ) ) @@ -987,43 +1006,47 @@ def __get_securitygroups(vm_): def __get_availability_zone(vm_): - vm_availability_zone = six.text_type(config.get_cloud_config_value( - 'availability_zone', vm_, __opts__, search_global=False - )) + vm_availability_zone = six.text_type( + config.get_cloud_config_value( + "availability_zone", vm_, __opts__, search_global=False + ) + ) if not vm_availability_zone: - raise SaltCloudNotFound('No availability_zone specified.') + raise SaltCloudNotFound("No availability_zone specified.") availability_zones = list_availability_zones() if vm_availability_zone in availability_zones: return vm_availability_zone raise SaltCloudNotFound( - 'The specified availability_zone \'{0}\' could not be found.'.format( + "The specified availability_zone '{0}' could not be found.".format( vm_availability_zone ) ) def __get_location(vm_): - ''' + """ Return the Tencent Cloud region to use, in this order: - CLI parameter - VM parameter - Cloud profile setting - ''' - vm_location = six.text_type(__opts__.get( - 'location', - config.get_cloud_config_value( - 'location', - vm_ or get_configured_provider(), - __opts__, - default=DEFAULT_REGION, - search_global=False + """ + vm_location = six.text_type( + __opts__.get( + "location", + config.get_cloud_config_value( + "location", + vm_ or get_configured_provider(), + __opts__, + default=DEFAULT_REGION, + search_global=False, + ), ) - )) + ) if not vm_location: - raise SaltCloudNotFound('No location specified.') + raise SaltCloudNotFound("No location specified.") return vm_location diff --git a/salt/cloud/clouds/vagrant.py b/salt/cloud/clouds/vagrant.py index 77db32e47ac..861f45e6f55 100644 --- a/salt/cloud/clouds/vagrant.py +++ b/salt/cloud/clouds/vagrant.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Vagrant Cloud Driver ==================== @@ -13,18 +13,20 @@ files as described in the .. versionadded:: 2018.3.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import tempfile +import salt.client +import salt.config as config + # Import salt libs import salt.utils.cloud -import salt.config as config -import salt.client from salt._compat import ipaddress from salt.exceptions import SaltCloudException, SaltCloudSystemExit, SaltInvocationError @@ -33,14 +35,14 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Needs no special configuration - ''' + """ return True def avail_locations(call=None): - r''' + r""" This function returns a list of locations available. CLI Example: @@ -51,22 +53,22 @@ def avail_locations(call=None): # \[ vagrant will always returns an empty dictionary \] - ''' + """ return {} def avail_images(call=None): - '''This function returns a list of images available for this cloud provider. + """This function returns a list of images available for this cloud provider. vagrant will return a list of profiles. salt-cloud --list-images my-cloud-provider - ''' + """ vm_ = get_configured_provider() - return {'Profiles': [profile for profile in vm_['profiles']]} + return {"Profiles": [profile for profile in vm_["profiles"]]} def avail_sizes(call=None): - r''' + r""" This function returns a list of sizes available for this cloud provider. CLI Example: @@ -77,12 +79,12 @@ def avail_sizes(call=None): # \[ vagrant always returns an empty dictionary \] - ''' + """ return {} def list_nodes(call=None): - ''' + """ List the nodes which have salt-cloud:driver:vagrant grains. CLI Example: @@ -90,7 +92,7 @@ def list_nodes(call=None): .. code-block:: bash salt-cloud -Q - ''' + """ nodes = _list_nodes(call) return _build_required_items(nodes) @@ -101,7 +103,7 @@ def _build_required_items(nodes): if grains: private_ips = [] public_ips = [] - ips = grains['ipv4'] + grains['ipv6'] + ips = grains["ipv4"] + grains["ipv6"] for adrs in ips: ip_ = ipaddress.ip_address(adrs) if not ip_.is_loopback: @@ -111,19 +113,19 @@ def _build_required_items(nodes): public_ips.append(adrs) ret[name] = { - 'id': grains['id'], - 'image': grains['salt-cloud']['profile'], - 'private_ips': private_ips, - 'public_ips': public_ips, - 'size': '', - 'state': 'running' + "id": grains["id"], + "image": grains["salt-cloud"]["profile"], + "private_ips": private_ips, + "public_ips": public_ips, + "size": "", + "state": "running", } return ret def list_nodes_full(call=None): - ''' + """ List the nodes, ask all 'vagrant' minions, return dict of grains (enhanced). CLI Example: @@ -131,12 +133,21 @@ def list_nodes_full(call=None): .. code-block:: bash salt-call -F - ''' + """ ret = _list_nodes(call) - for key, grains in ret.items(): # clean up some hyperverbose grains -- everything is too much + for ( + key, + grains, + ) in ret.items(): # clean up some hyperverbose grains -- everything is too much try: - del grains['cpu_flags'], grains['disks'], grains['pythonpath'], grains['dns'], grains['gpus'] + del ( + grains["cpu_flags"], + grains["disks"], + grains["pythonpath"], + grains["dns"], + grains["gpus"], + ) except KeyError: pass # ignore absence of things we are eliminating except TypeError: @@ -149,30 +160,30 @@ def list_nodes_full(call=None): def _list_nodes(call=None): - ''' + """ List the nodes, ask all 'vagrant' minions, return dict of grains. - ''' + """ local = salt.client.LocalClient() - ret = local.cmd('salt-cloud:driver:vagrant', 'grains.items', '', tgt_type='grain') + ret = local.cmd("salt-cloud:driver:vagrant", "grains.items", "", tgt_type="grain") return ret def list_nodes_select(call=None): - ''' + """ Return a list of the minions that have salt-cloud grains, with select fields. - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full('function'), __opts__['query.selection'], call, + list_nodes_full("function"), __opts__["query.selection"], call, ) def show_instance(name, call=None): - ''' + """ List the a single node, return dict of grains. - ''' + """ local = salt.client.LocalClient() - ret = local.cmd(name, 'grains.items', '') + ret = local.cmd(name, "grains.items", "") reqs = _build_required_items(ret) ret[name].update(reqs[name]) return ret @@ -180,11 +191,11 @@ def show_instance(name, call=None): def _get_my_info(name): local = salt.client.LocalClient() - return local.cmd(name, 'grains.get', ['salt-cloud']) + return local.cmd(name, "grains.get", ["salt-cloud"]) def create(vm_): - ''' + """ Provision a single machine CLI Example: @@ -193,76 +204,81 @@ def create(vm_): salt-cloud -p my_profile new_node_1 - ''' - name = vm_['name'] - machine = config.get_cloud_config_value( - 'machine', vm_, __opts__, default='') - vm_['machine'] = machine - host = config.get_cloud_config_value( - 'host', vm_, __opts__, default=NotImplemented) - vm_['cwd'] = config.get_cloud_config_value( - 'cwd', vm_, __opts__, default='/') - vm_['runas'] = config.get_cloud_config_value( - 'vagrant_runas', vm_, __opts__, default=os.getenv('SUDO_USER')) - vm_['timeout'] = config.get_cloud_config_value( - 'vagrant_up_timeout', vm_, __opts__, default=300) - vm_['vagrant_provider'] = config.get_cloud_config_value( - 'vagrant_provider', vm_, __opts__, default='') - vm_['grains'] = {'salt-cloud:vagrant': {'host': host, 'machine': machine}} + """ + name = vm_["name"] + machine = config.get_cloud_config_value("machine", vm_, __opts__, default="") + vm_["machine"] = machine + host = config.get_cloud_config_value("host", vm_, __opts__, default=NotImplemented) + vm_["cwd"] = config.get_cloud_config_value("cwd", vm_, __opts__, default="/") + vm_["runas"] = config.get_cloud_config_value( + "vagrant_runas", vm_, __opts__, default=os.getenv("SUDO_USER") + ) + vm_["timeout"] = config.get_cloud_config_value( + "vagrant_up_timeout", vm_, __opts__, default=300 + ) + vm_["vagrant_provider"] = config.get_cloud_config_value( + "vagrant_provider", vm_, __opts__, default="" + ) + vm_["grains"] = {"salt-cloud:vagrant": {"host": host, "machine": machine}} - log.info('sending \'vagrant.init %s machine=%s\' command to %s', name, machine, host) + log.info("sending 'vagrant.init %s machine=%s' command to %s", name, machine, host) local = salt.client.LocalClient() - ret = local.cmd(host, 'vagrant.init', [name], kwarg={'vm': vm_, 'start': True}) - log.info('response ==> %s', ret[host]) + ret = local.cmd(host, "vagrant.init", [name], kwarg={"vm": vm_, "start": True}) + log.info("response ==> %s", ret[host]) network_mask = config.get_cloud_config_value( - 'network_mask', vm_, __opts__, default='') - if 'ssh_host' not in vm_: - ret = local.cmd(host, - 'vagrant.get_ssh_config', - [name], - kwarg={'network_mask': network_mask, - 'get_private_key': True})[host] + "network_mask", vm_, __opts__, default="" + ) + if "ssh_host" not in vm_: + ret = local.cmd( + host, + "vagrant.get_ssh_config", + [name], + kwarg={"network_mask": network_mask, "get_private_key": True}, + )[host] with tempfile.NamedTemporaryFile() as pks: - if 'private_key' not in vm_ and ret and ret.get('private_key', False): - pks.write(ret['private_key']) + if "private_key" not in vm_ and ret and ret.get("private_key", False): + pks.write(ret["private_key"]) pks.flush() - log.debug('wrote private key to %s', pks.name) - vm_['key_filename'] = pks.name - if 'ssh_host' not in vm_: + log.debug("wrote private key to %s", pks.name) + vm_["key_filename"] = pks.name + if "ssh_host" not in vm_: try: - vm_.setdefault('ssh_username', ret['ssh_username']) - if ret.get('ip_address'): - vm_['ssh_host'] = ret['ip_address'] + vm_.setdefault("ssh_username", ret["ssh_username"]) + if ret.get("ip_address"): + vm_["ssh_host"] = ret["ip_address"] else: # if probe failed or not used, use Vagrant's reported ssh info - vm_['ssh_host'] = ret['ssh_host'] - vm_.setdefault('ssh_port', ret['ssh_port']) + vm_["ssh_host"] = ret["ssh_host"] + vm_.setdefault("ssh_port", ret["ssh_port"]) except (KeyError, TypeError): raise SaltInvocationError( - 'Insufficient SSH addressing information for {}'.format(name)) + "Insufficient SSH addressing information for {}".format(name) + ) - log.info('Provisioning machine %s as node %s using ssh %s', - machine, name, vm_['ssh_host']) - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + log.info( + "Provisioning machine %s as node %s using ssh %s", + machine, + name, + vm_["ssh_host"], + ) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) return ret def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ ret = config.is_provider_configured( - __opts__, - __active_provider_name__ or 'vagrant', - '' - ) + __opts__, __active_provider_name__ or "vagrant", "" + ) return ret # noinspection PyTypeChecker def destroy(name, call=None): - ''' + """ Destroy a node. CLI Example: @@ -270,55 +286,55 @@ def destroy(name, call=None): .. code-block:: bash salt-cloud --destroy mymachine - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a, or --action.' + "The destroy action must be called with -d, --destroy, " "-a, or --action." ) opts = __opts__ - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=opts['sock_dir'], - transport=opts['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=opts["sock_dir"], + transport=opts["transport"], ) my_info = _get_my_info(name) if my_info: - profile_name = my_info[name]['profile'] - profile = opts['profiles'][profile_name] - host = profile['host'] + profile_name = my_info[name]["profile"] + profile = opts["profiles"][profile_name] + host = profile["host"] local = salt.client.LocalClient() - ret = local.cmd(host, 'vagrant.destroy', [name]) + ret = local.cmd(host, "vagrant.destroy", [name]) if ret[host]: - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=opts['sock_dir'], - transport=opts['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=opts["sock_dir"], + transport=opts["transport"], ) - if opts.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir']( - name, __active_provider_name__.split(':')[0], opts) + if opts.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], opts + ) - return {'Destroyed': '{0} was destroyed.'.format(name)} + return {"Destroyed": "{0} was destroyed.".format(name)} else: - return {'Error': 'Error destroying {}'.format(name)} + return {"Error": "Error destroying {}".format(name)} else: - return {'Error': 'No response from {}. Cannot destroy.'.format(name)} + return {"Error": "No response from {}. Cannot destroy.".format(name)} # noinspection PyTypeChecker def reboot(name, call=None): - ''' + """ Reboot a vagrant minion. name @@ -329,14 +345,14 @@ def reboot(name, call=None): .. code-block:: bash salt-cloud -a reboot vm_name - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudException( - 'The reboot action must be called with -a or --action.' + "The reboot action must be called with -a or --action." ) my_info = _get_my_info(name) - profile_name = my_info[name]['profile'] - profile = __opts__['profiles'][profile_name] - host = profile['host'] + profile_name = my_info[name]["profile"] + profile = __opts__["profiles"][profile_name] + host = profile["host"] local = salt.client.LocalClient() - return local.cmd(host, 'vagrant.reboot', [name]) + return local.cmd(host, "vagrant.reboot", [name]) diff --git a/salt/cloud/clouds/virtualbox.py b/salt/cloud/clouds/virtualbox.py index c4115d23fc2..0220f86b9d6 100644 --- a/salt/cloud/clouds/virtualbox.py +++ b/salt/cloud/clouds/virtualbox.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A salt cloud provider that lets you use virtualbox on your machine and act as a cloud. @@ -15,15 +15,17 @@ to create this. Dicts provided by salt: __opts__ : contains the options used to run Salt Cloud, as well as a set of configuration and environment variables -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.config as config + # Import salt libs from salt.exceptions import SaltCloudSystemExit -import salt.config as config # Import Third Party Libs try: @@ -37,8 +39,9 @@ try: vb_stop_vm, treat_machine_dict, vb_start_vm, - vb_wait_for_network_address + vb_wait_for_network_address, ) + HAS_VBOX = True except ImportError: HAS_VBOX = False @@ -46,15 +49,15 @@ except ImportError: log = logging.getLogger(__name__) # The name salt will identify the lib by -__virtualname__ = 'virtualbox' +__virtualname__ = "virtualbox" -#if no clone mode is specified in the virtualbox profile -#then default to 0 which was the old default value +# if no clone mode is specified in the virtualbox profile +# then default to 0 which was the old default value DEFAULT_CLONE_MODE = 0 def __virtual__(): - ''' + """ This function determines whether or not to make this cloud module available upon execution. Most often, it uses get_configured_provider() to determine @@ -65,12 +68,18 @@ def __virtual__(): then that name should be returned instead of True. @return True|False|str - ''' + """ if not HAS_VBOX: - return False, 'The virtualbox driver cannot be loaded: \'vboxapi\' is not installed.' + return ( + False, + "The virtualbox driver cannot be loaded: 'vboxapi' is not installed.", + ) if get_configured_provider() is False: - return False, 'The virtualbox driver cannot be loaded: \'virtualbox\' provider is not configured.' + return ( + False, + "The virtualbox driver cannot be loaded: 'virtualbox' provider is not configured.", + ) # If the name of the driver used does not match the filename, # then that name should be returned instead of True. @@ -84,7 +93,7 @@ def get_configured_provider(): configured = config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - () # keys we need from the provider configuration + (), # keys we need from the provider configuration ) return configured @@ -93,28 +102,26 @@ def map_clonemode(vm_info): """ Convert the virtualbox config file values for clone_mode into the integers the API requires """ - mode_map = { - 'state': 0, - 'child': 1, - 'all': 2 - } + mode_map = {"state": 0, "child": 1, "all": 2} if not vm_info: return DEFAULT_CLONE_MODE - if 'clonemode' not in vm_info: + if "clonemode" not in vm_info: return DEFAULT_CLONE_MODE - if vm_info['clonemode'] in mode_map: - return mode_map[vm_info['clonemode']] + if vm_info["clonemode"] in mode_map: + return mode_map[vm_info["clonemode"]] else: raise SaltCloudSystemExit( - "Illegal clonemode for virtualbox profile. Legal values are: {}".format(','.join(mode_map.keys())) + "Illegal clonemode for virtualbox profile. Legal values are: {}".format( + ",".join(mode_map.keys()) + ) ) def create(vm_info): - ''' + """ Creates a virtual machine from the given VM information This is what is used to request a virtual machine to be created by the @@ -140,88 +147,98 @@ def create(vm_info): @type vm_info dict @return dict of resulting vm. !!!Passwords can and should be included!!! - ''' + """ try: # Check for required profile parameters before sending any API calls. - if vm_info['profile'] and config.is_profile_configured( - __opts__, - __active_provider_name__ or 'virtualbox', - vm_info['profile'] - ) is False: + if ( + vm_info["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "virtualbox", vm_info["profile"] + ) + is False + ): return False except AttributeError: pass vm_name = vm_info["name"] deploy = config.get_cloud_config_value( - 'deploy', vm_info, __opts__, search_global=False, default=True + "deploy", vm_info, __opts__, search_global=False, default=True ) wait_for_ip_timeout = config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_info, __opts__, default=60 + "wait_for_ip_timeout", vm_info, __opts__, default=60 ) boot_timeout = config.get_cloud_config_value( - 'boot_timeout', vm_info, __opts__, default=60 * 1000 - ) - power = config.get_cloud_config_value( - 'power_on', vm_info, __opts__, default=False + "boot_timeout", vm_info, __opts__, default=60 * 1000 ) + power = config.get_cloud_config_value("power_on", vm_info, __opts__, default=False) key_filename = config.get_cloud_config_value( - 'private_key', vm_info, __opts__, search_global=False, default=None + "private_key", vm_info, __opts__, search_global=False, default=None ) clone_mode = map_clonemode(vm_info) - wait_for_pattern = vm_info['waitforpattern'] if 'waitforpattern' in vm_info.keys() else None - interface_index = vm_info['interfaceindex'] if 'interfaceindex' in vm_info.keys() else 0 + wait_for_pattern = ( + vm_info["waitforpattern"] if "waitforpattern" in vm_info.keys() else None + ) + interface_index = ( + vm_info["interfaceindex"] if "interfaceindex" in vm_info.keys() else 0 + ) log.debug("Going to fire event: starting create") - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_info['name']), - args=__utils__['cloud.filter_event']('creating', vm_info, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_info["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_info, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) # to create the virtual machine. request_kwargs = { - 'name': vm_info['name'], - 'clone_from': vm_info['clonefrom'], - 'clone_mode': clone_mode + "name": vm_info["name"], + "clone_from": vm_info["clonefrom"], + "clone_mode": clone_mode, } - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_info['name']), - args=__utils__['cloud.filter_event']('requesting', request_kwargs, list(request_kwargs)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_info["name"]), + args=__utils__["cloud.filter_event"]( + "requesting", request_kwargs, list(request_kwargs) + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) vm_result = vb_clone_vm(**request_kwargs) # Booting and deploying if needed if power: vb_start_vm(vm_name, timeout=boot_timeout) - ips = vb_wait_for_network_address(wait_for_ip_timeout, machine_name=vm_name, wait_for_pattern=wait_for_pattern) + ips = vb_wait_for_network_address( + wait_for_ip_timeout, machine_name=vm_name, wait_for_pattern=wait_for_pattern + ) if ips: ip = ips[interface_index] log.info("[ %s ] IPv4 is: %s", vm_name, ip) # ssh or smb using ip and install salt only if deploy is True if deploy: - vm_info['key_filename'] = key_filename - vm_info['ssh_host'] = ip + vm_info["key_filename"] = key_filename + vm_info["ssh_host"] = ip - res = __utils__['cloud.bootstrap'](vm_info, __opts__) + res = __utils__["cloud.bootstrap"](vm_info, __opts__) vm_result.update(res) - __utils__['cloud.fire_event']( - 'event', - 'created machine', - 'salt/cloud/{0}/created'.format(vm_info['name']), - args=__utils__['cloud.filter_event']('created', vm_result, list(vm_result)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created machine", + "salt/cloud/{0}/created".format(vm_info["name"]), + args=__utils__["cloud.filter_event"]("created", vm_result, list(vm_result)), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) # Passwords should be included in this object!! @@ -250,10 +267,9 @@ def list_nodes_full(kwargs=None, call=None): @return: @rtype: """ - if call == 'action': + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called ' - 'with -f or --function.' + "The list_nodes_full function must be called " "with -f or --function." ) machines = {} @@ -296,10 +312,9 @@ def list_nodes(kwargs=None, call=None): @return: @rtype: """ - if call == 'action': + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called ' - 'with -f or --function.' + "The list_nodes function must be called " "with -f or --function." ) attributes = [ @@ -310,8 +325,8 @@ def list_nodes(kwargs=None, call=None): "private_ips", "public_ips", ] - return __utils__['cloud.list_nodes_select']( - list_nodes_full('function'), attributes, call, + return __utils__["cloud.list_nodes_select"]( + list_nodes_full("function"), attributes, call, ) @@ -319,8 +334,8 @@ def list_nodes_select(call=None): """ Return a list of the VMs that are on the provider, with select fields """ - return __utils__['cloud.list_nodes_select']( - list_nodes_full('function'), __opts__['query.selection'], call, + return __utils__["cloud.list_nodes_select"]( + list_nodes_full("function"), __opts__["query.selection"], call, ) @@ -347,38 +362,38 @@ def destroy(name, call=None): if not vb_machine_exists(name): return "{0} doesn't exist and can't be deleted".format(name) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) vb_destroy_machine(name) - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) def start(name, call=None): - ''' + """ Start a machine. @param name: Machine to start @type name: str @param call: Must be "action" @type call: str - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The instance action must be called with -a or --action.' + "The instance action must be called with -a or --action." ) log.info("Starting machine: %s", name) @@ -396,9 +411,9 @@ def stop(name, call=None): @param call: Must be "action" @type call: str """ - if call != 'action': + if call != "action": raise SaltCloudSystemExit( - 'The instance action must be called with -a or --action.' + "The instance action must be called with -a or --action." ) log.info("Stopping machine: %s", name) @@ -412,17 +427,15 @@ def show_image(kwargs, call=None): """ Show the details of an image """ - if call != 'function': + if call != "function": raise SaltCloudSystemExit( - 'The show_image action must be called with -f or --function.' + "The show_image action must be called with -f or --function." ) - name = kwargs['image'] + name = kwargs["image"] log.info("Showing image %s", name) machine = vb_get_machine(name) - ret = { - machine["name"]: treat_machine_dict(machine) - } + ret = {machine["name"]: treat_machine_dict(machine)} del machine["name"] return ret diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index ff9bbd8a191..0b8cabddd4f 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # pylint: disable=C0302 -''' +""" VMware Cloud Module =================== @@ -112,66 +112,72 @@ cloud configuration at To test the connection for ``my-vmware-config`` specified in the cloud configuration, run :py:func:`test_vcenter_connection` -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from random import randint + +import logging +import os.path import pprint import re -import logging -import time -import os.path import subprocess +import time +from random import randint + +# Import salt cloud libs +import salt.config as config # Import salt libs import salt.utils.cloud import salt.utils.network import salt.utils.stringutils -import salt.utils.xmlutil import salt.utils.vmware +import salt.utils.xmlutil from salt.exceptions import SaltCloudSystemExit -# Import salt cloud libs -import salt.config as config - # Import 3rd-party libs from salt.ext import six + try: # Attempt to import pyVmomi libs from pyVmomi import vim # pylint: disable=no-name-in-module + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False # Disable InsecureRequestWarning generated on python > 2.6 try: - from requests.packages.urllib3 import disable_warnings # pylint: disable=no-name-in-module + from requests.packages.urllib3 import ( + disable_warnings, + ) # pylint: disable=no-name-in-module + disable_warnings() except ImportError: pass -ESX_5_5_NAME_PORTION = 'VMware ESXi 5.5' +ESX_5_5_NAME_PORTION = "VMware ESXi 5.5" SAFE_ESX_5_5_CONTROLLER_KEY_INDEX = 200 -FLATTEN_DISK_FULL_CLONE = 'moveAllDiskBackingsAndDisallowSharing' -COPY_ALL_DISKS_FULL_CLONE = 'moveAllDiskBackingsAndAllowSharing' -CURRENT_STATE_LINKED_CLONE = 'moveChildMostDiskBacking' -QUICK_LINKED_CLONE = 'createNewChildDiskBacking' +FLATTEN_DISK_FULL_CLONE = "moveAllDiskBackingsAndDisallowSharing" +COPY_ALL_DISKS_FULL_CLONE = "moveAllDiskBackingsAndAllowSharing" +CURRENT_STATE_LINKED_CLONE = "moveChildMostDiskBacking" +QUICK_LINKED_CLONE = "createNewChildDiskBacking" -IP_RE = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$' +IP_RE = r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'vmware' +__virtualname__ = "vmware" # Only load in this module if the VMware configurations are in place def __virtual__(): - ''' + """ Check for VMware configuration and if required libs are available. - ''' + """ if get_configured_provider() is False: return False @@ -182,36 +188,33 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( __opts__, __active_provider_name__ or __virtualname__, - ('url', 'user', 'password',) + ("url", "user", "password",), ) def get_dependencies(): - ''' + """ Warn if dependencies aren't met. - ''' + """ deps = { - 'pyVmomi': HAS_PYVMOMI, + "pyVmomi": HAS_PYVMOMI, } - return config.check_driver_dependencies( - __virtualname__, - deps - ) + return config.check_driver_dependencies(__virtualname__, deps) def script(vm_): - ''' + """ Return the script deployment object - ''' - script_name = config.get_cloud_config_value('script', vm_, __opts__) + """ + script_name = config.get_cloud_config_value("script", vm_, __opts__) if not script_name: - script_name = 'bootstrap-salt' + script_name = "bootstrap-salt" return salt.utils.cloud.os_script( script_name, @@ -219,7 +222,7 @@ def script(vm_): __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) @@ -228,37 +231,39 @@ def _str_to_bool(var): return var if isinstance(var, six.string_types): - return True if var.lower() == 'true' else False + return True if var.lower() == "true" else False return None def _get_si(): - ''' + """ Authenticate with vCenter server and return service instance object. - ''' + """ url = config.get_cloud_config_value( - 'url', get_configured_provider(), __opts__, search_global=False + "url", get_configured_provider(), __opts__, search_global=False ) username = config.get_cloud_config_value( - 'user', get_configured_provider(), __opts__, search_global=False + "user", get_configured_provider(), __opts__, search_global=False ) password = config.get_cloud_config_value( - 'password', get_configured_provider(), __opts__, search_global=False + "password", get_configured_provider(), __opts__, search_global=False ) protocol = config.get_cloud_config_value( - 'protocol', get_configured_provider(), __opts__, search_global=False, default='https' + "protocol", + get_configured_provider(), + __opts__, + search_global=False, + default="https", ) port = config.get_cloud_config_value( - 'port', get_configured_provider(), __opts__, search_global=False, default=443 + "port", get_configured_provider(), __opts__, search_global=False, default=443 ) - return salt.utils.vmware.get_service_instance(url, - username, - password, - protocol=protocol, - port=port) + return salt.utils.vmware.get_service_instance( + url, username, password, protocol=protocol, port=port + ) def _edit_existing_hard_disk_helper(disk, size_kb=None, size_gb=None, mode=None): @@ -273,19 +278,21 @@ def _edit_existing_hard_disk_helper(disk, size_kb=None, size_gb=None, mode=None) return disk_spec -def _add_new_hard_disk_helper(disk_label, - size_gb, - unit_number, - controller_key=1000, - thin_provision=False, - eagerly_scrub=False, - datastore=None, - vm_name=None): +def _add_new_hard_disk_helper( + disk_label, + size_gb, + unit_number, + controller_key=1000, + thin_provision=False, + eagerly_scrub=False, + datastore=None, + vm_name=None, +): random_key = randint(-2099, -2000) size_kb = int(size_gb * 1024.0 * 1024.0) disk_spec = vim.vm.device.VirtualDeviceSpec() - disk_spec.fileOperation = 'create' + disk_spec.fileOperation = "create" disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add disk_spec.device = vim.vm.device.VirtualDisk() @@ -297,43 +304,67 @@ def _add_new_hard_disk_helper(disk_label, disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo() disk_spec.device.backing.thinProvisioned = thin_provision disk_spec.device.backing.eagerlyScrub = eagerly_scrub - disk_spec.device.backing.diskMode = 'persistent' + disk_spec.device.backing.diskMode = "persistent" if datastore: - datastore_ref = salt.utils.vmware.get_mor_using_container_view(_get_si(), vim.Datastore, datastore) + datastore_ref = salt.utils.vmware.get_mor_using_container_view( + _get_si(), vim.Datastore, datastore + ) if not datastore_ref: # check if it is a datastore cluster instead - datastore_cluster_ref = salt.utils.vmware.get_mor_using_container_view(_get_si(), vim.StoragePod, datastore) + datastore_cluster_ref = salt.utils.vmware.get_mor_using_container_view( + _get_si(), vim.StoragePod, datastore + ) if not datastore_cluster_ref: # datastore/datastore cluster specified does not exist - raise SaltCloudSystemExit("Specified datastore/datastore cluster ({0}) for disk ({1}) does not exist".format(datastore, disk_label)) + raise SaltCloudSystemExit( + "Specified datastore/datastore cluster ({0}) for disk ({1}) does not exist".format( + datastore, disk_label + ) + ) # datastore cluster has been specified # find datastore with most free space available # # TODO: Get DRS Recommendations instead of finding datastore with most free space - datastore_list = salt.utils.vmware.get_datastores(_get_si(), datastore_cluster_ref, get_all_datastores=True) + datastore_list = salt.utils.vmware.get_datastores( + _get_si(), datastore_cluster_ref, get_all_datastores=True + ) datastore_free_space = 0 for ds_ref in datastore_list: log.trace( - 'Found datastore (%s) with free space (%s) in datastore ' - 'cluster (%s)', ds_ref.name, ds_ref.summary.freeSpace, datastore + "Found datastore (%s) with free space (%s) in datastore " + "cluster (%s)", + ds_ref.name, + ds_ref.summary.freeSpace, + datastore, ) - if ds_ref.summary.accessible and ds_ref.summary.freeSpace > datastore_free_space: + if ( + ds_ref.summary.accessible + and ds_ref.summary.freeSpace > datastore_free_space + ): datastore_free_space = ds_ref.summary.freeSpace datastore_ref = ds_ref if not datastore_ref: # datastore cluster specified does not have any accessible datastores - raise SaltCloudSystemExit("Specified datastore cluster ({0}) for disk ({1}) does not have any accessible datastores available".format(datastore, disk_label)) + raise SaltCloudSystemExit( + "Specified datastore cluster ({0}) for disk ({1}) does not have any accessible datastores available".format( + datastore, disk_label + ) + ) - datastore_path = '[' + six.text_type(datastore_ref.name) + '] ' + vm_name - disk_spec.device.backing.fileName = datastore_path + '/' + disk_label + '.vmdk' + datastore_path = "[" + six.text_type(datastore_ref.name) + "] " + vm_name + disk_spec.device.backing.fileName = datastore_path + "/" + disk_label + ".vmdk" disk_spec.device.backing.datastore = datastore_ref - log.trace('Using datastore (%s) for disk (%s), vm_name (%s)', - datastore_ref.name, disk_label, vm_name) + log.trace( + "Using datastore (%s) for disk (%s), vm_name (%s)", + datastore_ref.name, + disk_label, + vm_name, + ) disk_spec.device.controllerKey = controller_key disk_spec.device.unitNumber = unit_number @@ -342,16 +373,16 @@ def _add_new_hard_disk_helper(disk_label, return disk_spec -def _edit_existing_network_adapter(network_adapter, - new_network_name, - adapter_type, - switch_type, - container_ref=None): +def _edit_existing_network_adapter( + network_adapter, new_network_name, adapter_type, switch_type, container_ref=None +): adapter_type.strip().lower() switch_type.strip().lower() if adapter_type in ["vmxnet", "vmxnet2", "vmxnet3", "e1000", "e1000e"]: - edited_network_adapter = salt.utils.vmware.get_network_adapter_type(adapter_type) + edited_network_adapter = salt.utils.vmware.get_network_adapter_type( + adapter_type + ) if isinstance(network_adapter, type(edited_network_adapter)): edited_network_adapter = network_adapter else: @@ -359,46 +390,52 @@ def _edit_existing_network_adapter(network_adapter, "Changing type of '%s' from '%s' to '%s'", network_adapter.deviceInfo.label, type(network_adapter).__name__.rsplit(".", 1)[1][7:].lower(), - adapter_type + adapter_type, ) else: # If type not specified or does not match, don't change adapter type if adapter_type: log.error( "Cannot change type of '%s' to '%s'. Not changing type", - network_adapter.deviceInfo.label, adapter_type + network_adapter.deviceInfo.label, + adapter_type, ) edited_network_adapter = network_adapter - if switch_type == 'standard': + if switch_type == "standard": network_ref = salt.utils.vmware.get_mor_by_property( - _get_si(), - vim.Network, - new_network_name, - container_ref=container_ref + _get_si(), vim.Network, new_network_name, container_ref=container_ref + ) + edited_network_adapter.backing = ( + vim.vm.device.VirtualEthernetCard.NetworkBackingInfo() ) - edited_network_adapter.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo() edited_network_adapter.backing.deviceName = new_network_name edited_network_adapter.backing.network = network_ref - elif switch_type == 'distributed': + elif switch_type == "distributed": network_ref = salt.utils.vmware.get_mor_by_property( _get_si(), vim.dvs.DistributedVirtualPortgroup, new_network_name, - container_ref=container_ref + container_ref=container_ref, ) dvs_port_connection = vim.dvs.PortConnection( portgroupKey=network_ref.key, - switchUuid=network_ref.config.distributedVirtualSwitch.uuid + switchUuid=network_ref.config.distributedVirtualSwitch.uuid, + ) + edited_network_adapter.backing = ( + vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo() ) - edited_network_adapter.backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo() edited_network_adapter.backing.port = dvs_port_connection else: # If switch type not specified or does not match, show error and return if not switch_type: - err_msg = "The switch type to be used by '{0}' has not been specified".format(network_adapter.deviceInfo.label) + err_msg = "The switch type to be used by '{0}' has not been specified".format( + network_adapter.deviceInfo.label + ) else: - err_msg = "Cannot create '{0}'. Invalid/unsupported switch type '{1}'".format(network_adapter.deviceInfo.label, switch_type) + err_msg = "Cannot create '{0}'. Invalid/unsupported switch type '{1}'".format( + network_adapter.deviceInfo.label, switch_type + ) raise SaltCloudSystemExit(err_msg) edited_network_adapter.key = network_adapter.key @@ -418,12 +455,14 @@ def _edit_existing_network_adapter(network_adapter, return network_spec -def _add_new_network_adapter_helper(network_adapter_label, - network_name, - adapter_type, - switch_type, - mac, - container_ref=None): +def _add_new_network_adapter_helper( + network_adapter_label, + network_name, + adapter_type, + switch_type, + mac, + container_ref=None, +): random_key = randint(-4099, -4000) adapter_type.strip().lower() @@ -437,47 +476,57 @@ def _add_new_network_adapter_helper(network_adapter_label, if not adapter_type: log.debug( "The type of '%s' has not been specified. " - "Creating default type 'vmxnet3'", network_adapter_label + "Creating default type 'vmxnet3'", + network_adapter_label, ) else: log.error( "Cannot create network adapter of type '%s'. " "Creating '%s' of default type 'vmxnet3'", - adapter_type, network_adapter_label + adapter_type, + network_adapter_label, ) network_spec.device = vim.vm.device.VirtualVmxnet3() network_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add - if switch_type == 'standard': - network_spec.device.backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo() + if switch_type == "standard": + network_spec.device.backing = ( + vim.vm.device.VirtualEthernetCard.NetworkBackingInfo() + ) network_spec.device.backing.deviceName = network_name - network_spec.device.backing.network = salt.utils.vmware.get_mor_by_property(_get_si(), - vim.Network, - network_name, - container_ref=container_ref) - elif switch_type == 'distributed': - network_ref = salt.utils.vmware.get_mor_by_property(_get_si(), - vim.dvs.DistributedVirtualPortgroup, - network_name, - container_ref=container_ref) + network_spec.device.backing.network = salt.utils.vmware.get_mor_by_property( + _get_si(), vim.Network, network_name, container_ref=container_ref + ) + elif switch_type == "distributed": + network_ref = salt.utils.vmware.get_mor_by_property( + _get_si(), + vim.dvs.DistributedVirtualPortgroup, + network_name, + container_ref=container_ref, + ) dvs_port_connection = vim.dvs.PortConnection( portgroupKey=network_ref.key, - switchUuid=network_ref.config.distributedVirtualSwitch.uuid + switchUuid=network_ref.config.distributedVirtualSwitch.uuid, + ) + network_spec.device.backing = ( + vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo() ) - network_spec.device.backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo() network_spec.device.backing.port = dvs_port_connection else: # If switch type not specified or does not match, show error and return if not switch_type: - err_msg = "The switch type to be used by '{0}' has not been specified".format(network_adapter_label) + err_msg = "The switch type to be used by '{0}' has not been specified".format( + network_adapter_label + ) else: - err_msg = "Cannot create '{0}'. Invalid/unsupported switch type '{1}'".format(network_adapter_label, - switch_type) + err_msg = "Cannot create '{0}'. Invalid/unsupported switch type '{1}'".format( + network_adapter_label, switch_type + ) raise SaltCloudSystemExit(err_msg) - if mac != '': - network_spec.device.addressType = 'assigned' + if mac != "": + network_spec.device.addressType = "assigned" network_spec.device.macAddress = mac network_spec.device.key = random_key network_spec.device.deviceInfo = vim.Description() @@ -500,12 +549,14 @@ def _edit_existing_scsi_controller(scsi_controller, bus_sharing): return scsi_spec -def _add_new_scsi_controller_helper(scsi_controller_label, - properties, - bus_number): +def _add_new_scsi_controller_helper(scsi_controller_label, properties, bus_number): random_key = randint(-1050, -1000) - adapter_type = properties['type'].strip().lower() if 'type' in properties else None - bus_sharing = properties['bus_sharing'].strip().lower() if 'bus_sharing' in properties else None + adapter_type = properties["type"].strip().lower() if "type" in properties else None + bus_sharing = ( + properties["bus_sharing"].strip().lower() + if "bus_sharing" in properties + else None + ) scsi_spec = vim.vm.device.VirtualDeviceSpec() @@ -521,9 +572,13 @@ def _add_new_scsi_controller_helper(scsi_controller_label, else: # If type not specified or does not match, show error and return if not adapter_type: - err_msg = "The type of '{0}' has not been specified".format(scsi_controller_label) + err_msg = "The type of '{0}' has not been specified".format( + scsi_controller_label + ) else: - err_msg = "Cannot create '{0}'. Invalid/unsupported type '{1}'".format(scsi_controller_label, adapter_type) + err_msg = "Cannot create '{0}'. Invalid/unsupported type '{1}'".format( + scsi_controller_label, adapter_type + ) raise SaltCloudSystemExit(err_msg) scsi_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add @@ -536,23 +591,27 @@ def _add_new_scsi_controller_helper(scsi_controller_label, if bus_sharing == "virtual": # Virtual disks can be shared between virtual machines on the same server - scsi_spec.device.sharedBus = vim.vm.device.VirtualSCSIController.Sharing.virtualSharing + scsi_spec.device.sharedBus = ( + vim.vm.device.VirtualSCSIController.Sharing.virtualSharing + ) elif bus_sharing == "physical": # Virtual disks can be shared between virtual machines on any server - scsi_spec.device.sharedBus = vim.vm.device.VirtualSCSIController.Sharing.physicalSharing + scsi_spec.device.sharedBus = ( + vim.vm.device.VirtualSCSIController.Sharing.physicalSharing + ) else: # Virtual disks cannot be shared between virtual machines - scsi_spec.device.sharedBus = vim.vm.device.VirtualSCSIController.Sharing.noSharing + scsi_spec.device.sharedBus = ( + vim.vm.device.VirtualSCSIController.Sharing.noSharing + ) return scsi_spec -def _add_new_ide_controller_helper(ide_controller_label, - controller_key, - bus_number): - ''' +def _add_new_ide_controller_helper(ide_controller_label, controller_key, bus_number): + """ Helper function for adding new IDE controllers .. versionadded:: 2016.3.0 @@ -564,7 +623,7 @@ def _add_new_ide_controller_helper(ide_controller_label, Returns: created device spec for an IDE controller - ''' + """ if controller_key is None: controller_key = randint(-200, 250) @@ -587,20 +646,22 @@ def _set_cd_or_dvd_backing_type(drive, device_type, mode, iso_path): drive.backing = vim.vm.device.VirtualCdrom.IsoBackingInfo() drive.backing.fileName = iso_path - datastore = iso_path.partition('[')[-1].rpartition(']')[0] - datastore_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.Datastore, datastore) + datastore = iso_path.partition("[")[-1].rpartition("]")[0] + datastore_ref = salt.utils.vmware.get_mor_by_property( + _get_si(), vim.Datastore, datastore + ) if datastore_ref: drive.backing.datastore = datastore_ref - drive.deviceInfo.summary = 'ISO {0}'.format(iso_path) + drive.deviceInfo.summary = "ISO {0}".format(iso_path) elif device_type == "client_device": - if mode == 'passthrough': + if mode == "passthrough": drive.backing = vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo() - drive.deviceInfo.summary = 'Remote Device' - elif mode == 'atapi': + drive.deviceInfo.summary = "Remote Device" + elif mode == "atapi": drive.backing = vim.vm.device.VirtualCdrom.RemoteAtapiBackingInfo() - drive.deviceInfo.summary = 'Remote ATAPI' + drive.deviceInfo.summary = "Remote ATAPI" return drive @@ -616,11 +677,9 @@ def _edit_existing_cd_or_dvd_drive(drive, device_type, mode, iso_path): return drive_spec -def _add_new_cd_or_dvd_drive_helper(drive_label, - controller_key, - device_type, - mode, - iso_path): +def _add_new_cd_or_dvd_drive_helper( + drive_label, controller_key, device_type, mode, iso_path +): random_key = randint(-3025, -3000) device_type.strip().lower() @@ -631,23 +690,29 @@ def _add_new_cd_or_dvd_drive_helper(drive_label, drive_spec.device = vim.vm.device.VirtualCdrom() drive_spec.device.deviceInfo = vim.Description() - if device_type in ['datastore_iso_file', 'client_device']: - drive_spec.device = _set_cd_or_dvd_backing_type(drive_spec.device, device_type, mode, iso_path) + if device_type in ["datastore_iso_file", "client_device"]: + drive_spec.device = _set_cd_or_dvd_backing_type( + drive_spec.device, device_type, mode, iso_path + ) else: # If device_type not specified or does not match, create drive of Client type with Passthough mode if not device_type: log.debug( "The 'device_type' of '%s' has not been specified. " - "Creating default type 'client_device'", drive_label + "Creating default type 'client_device'", + drive_label, ) else: log.error( "Cannot create CD/DVD drive of type '%s'. " "Creating '%s' of default type 'client_device'", - device_type, drive_label + device_type, + drive_label, ) - drive_spec.device.backing = vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo() - drive_spec.device.deviceInfo.summary = 'Remote Device' + drive_spec.device.backing = ( + vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo() + ) + drive_spec.device.deviceInfo.summary = "Remote Device" drive_spec.device.key = random_key drive_spec.device.deviceInfo.label = drive_label @@ -663,15 +728,15 @@ def _set_network_adapter_mapping(adapter_specs): adapter_mapping = vim.vm.customization.AdapterMapping() adapter_mapping.adapter = vim.vm.customization.IPSettings() - if 'domain' in list(adapter_specs.keys()): - domain = adapter_specs['domain'] + if "domain" in list(adapter_specs.keys()): + domain = adapter_specs["domain"] adapter_mapping.adapter.dnsDomain = domain - if 'gateway' in list(adapter_specs.keys()): - gateway = adapter_specs['gateway'] + if "gateway" in list(adapter_specs.keys()): + gateway = adapter_specs["gateway"] adapter_mapping.adapter.gateway = gateway - if 'ip' in list(adapter_specs.keys()): - ip = six.text_type(adapter_specs['ip']) - subnet_mask = six.text_type(adapter_specs['subnet_mask']) + if "ip" in list(adapter_specs.keys()): + ip = six.text_type(adapter_specs["ip"]) + subnet_mask = six.text_type(adapter_specs["subnet_mask"]) adapter_mapping.adapter.ip = vim.vm.customization.FixedIp(ipAddress=ip) adapter_mapping.adapter.subnetMask = subnet_mask else: @@ -683,10 +748,7 @@ def _set_network_adapter_mapping(adapter_specs): def _get_mode_spec(device, mode, disk_spec): if device.backing.diskMode != mode: if not disk_spec: - disk_spec = _edit_existing_hard_disk_helper( - disk=device, - mode=mode - ) + disk_spec = _edit_existing_hard_disk_helper(disk=device, mode=mode) else: disk_spec.device.backing.diskMode = mode return disk_spec @@ -695,17 +757,21 @@ def _get_mode_spec(device, mode, disk_spec): def _get_size_spec(device, size_gb=None, size_kb=None): if size_kb is None and size_gb is not None: size_kb = int(size_gb * 1024.0 * 1024.0) - disk_spec = _edit_existing_hard_disk_helper(disk=device, size_kb=size_kb) if device.capacityInKB < size_kb else None + disk_spec = ( + _edit_existing_hard_disk_helper(disk=device, size_kb=size_kb) + if device.capacityInKB < size_kb + else None + ) return disk_spec def _iter_disk_unit_number(unit_number): - ''' + """ Apparently vmware reserves ID 7 for SCSI controllers, so we cannot specify hard drives for 7. Skip 7 to make sure. - ''' + """ unit_number += 1 if unit_number == 7: unit_number += 1 @@ -731,14 +797,16 @@ def _manage_devices(devices, vm=None, container_ref=None, new_vm_name=None): for device in vm.config.hardware.device: if isinstance(device, vim.vm.device.VirtualDisk): # this is a hard disk - if 'disk' in list(devices.keys()): + if "disk" in list(devices.keys()): # there is atleast one disk specified to be created/configured unit_number = _iter_disk_unit_number(unit_number) existing_disks_label.append(device.deviceInfo.label) - if device.deviceInfo.label in list(devices['disk'].keys()): + if device.deviceInfo.label in list(devices["disk"].keys()): disk_spec = None - if 'size' in devices['disk'][device.deviceInfo.label]: - size_gb = float(devices['disk'][device.deviceInfo.label]['size']) + if "size" in devices["disk"][device.deviceInfo.label]: + size_gb = float( + devices["disk"][device.deviceInfo.label]["size"] + ) size_kb = int(size_gb * 1024.0 * 1024.0) else: # User didn't specify disk size in the cloud @@ -746,189 +814,300 @@ def _manage_devices(devices, vm=None, container_ref=None, new_vm_name=None): size_kb = device.capacityInKB size_gb = size_kb / (1024.0 * 1024.0) log.debug( - 'Virtual disk size for \'%s\' was not ' - 'specified in the cloud profile or map file. ' - 'Using existing virtual disk size of \'%sGB\'', - device.deviceInfo.label, size_gb + "Virtual disk size for '%s' was not " + "specified in the cloud profile or map file. " + "Using existing virtual disk size of '%sGB'", + device.deviceInfo.label, + size_gb, ) if device.capacityInKB > size_kb: raise SaltCloudSystemExit( - 'The specified disk size \'{0}GB\' for \'{1}\' is ' - 'smaller than the disk image size \'{2}GB\'. It must ' - 'be equal to or greater than the disk image'.format( - float(devices['disk'][device.deviceInfo.label]['size']), + "The specified disk size '{0}GB' for '{1}' is " + "smaller than the disk image size '{2}GB'. It must " + "be equal to or greater than the disk image".format( + float( + devices["disk"][device.deviceInfo.label]["size"] + ), device.deviceInfo.label, - float(device.capacityInKB / (1024.0 * 1024.0)) + float(device.capacityInKB / (1024.0 * 1024.0)), ) ) else: disk_spec = _get_size_spec(device=device, size_kb=size_kb) - if 'mode' in devices['disk'][device.deviceInfo.label]: - if devices['disk'][device.deviceInfo.label]['mode'] \ - in [ - 'independent_persistent', - 'independent_nonpersistent', - 'dependent', + if "mode" in devices["disk"][device.deviceInfo.label]: + if devices["disk"][device.deviceInfo.label]["mode"] in [ + "independent_persistent", + "independent_nonpersistent", + "dependent", ]: - mode = devices['disk'][device.deviceInfo.label]['mode'] + mode = devices["disk"][device.deviceInfo.label]["mode"] disk_spec = _get_mode_spec(device, mode, disk_spec) else: - raise SaltCloudSystemExit('Invalid disk' - ' backing mode' - ' specified!') + raise SaltCloudSystemExit( + "Invalid disk" " backing mode" " specified!" + ) if disk_spec is not None: device_specs.append(disk_spec) - elif isinstance(device.backing, ( - vim.vm.device.VirtualEthernetCard.NetworkBackingInfo, - vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo)): + elif isinstance( + device.backing, + ( + vim.vm.device.VirtualEthernetCard.NetworkBackingInfo, + vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo, + ), + ): # this is a network adapter - if 'network' in list(devices.keys()): + if "network" in list(devices.keys()): # there is atleast one network adapter specified to be created/configured existing_network_adapters_label.append(device.deviceInfo.label) - if device.deviceInfo.label in list(devices['network'].keys()): - network_name = devices['network'][device.deviceInfo.label]['name'] - adapter_type = devices['network'][device.deviceInfo.label]['adapter_type'] if 'adapter_type' in devices['network'][device.deviceInfo.label] else '' - switch_type = devices['network'][device.deviceInfo.label]['switch_type'] if 'switch_type' in devices['network'][device.deviceInfo.label] else '' - network_spec = _edit_existing_network_adapter(device, network_name, adapter_type, switch_type, container_ref) - adapter_mapping = _set_network_adapter_mapping(devices['network'][device.deviceInfo.label]) + if device.deviceInfo.label in list(devices["network"].keys()): + network_name = devices["network"][device.deviceInfo.label][ + "name" + ] + adapter_type = ( + devices["network"][device.deviceInfo.label]["adapter_type"] + if "adapter_type" + in devices["network"][device.deviceInfo.label] + else "" + ) + switch_type = ( + devices["network"][device.deviceInfo.label]["switch_type"] + if "switch_type" + in devices["network"][device.deviceInfo.label] + else "" + ) + network_spec = _edit_existing_network_adapter( + device, + network_name, + adapter_type, + switch_type, + container_ref, + ) + adapter_mapping = _set_network_adapter_mapping( + devices["network"][device.deviceInfo.label] + ) device_specs.append(network_spec) nics_map.append(adapter_mapping) - elif hasattr(device, 'scsiCtlrUnitNumber'): + elif hasattr(device, "scsiCtlrUnitNumber"): # this is a SCSI controller - if 'scsi' in list(devices.keys()): + if "scsi" in list(devices.keys()): # there is atleast one SCSI controller specified to be created/configured bus_number += 1 existing_scsi_controllers_label.append(device.deviceInfo.label) - if device.deviceInfo.label in list(devices['scsi'].keys()): + if device.deviceInfo.label in list(devices["scsi"].keys()): # Modify the existing SCSI controller - scsi_controller_properties = devices['scsi'][device.deviceInfo.label] - bus_sharing = scsi_controller_properties['bus_sharing'].strip().lower() if 'bus_sharing' in scsi_controller_properties else None - if bus_sharing and bus_sharing in ['virtual', 'physical', 'no']: - bus_sharing = '{0}Sharing'.format(bus_sharing) + scsi_controller_properties = devices["scsi"][ + device.deviceInfo.label + ] + bus_sharing = ( + scsi_controller_properties["bus_sharing"].strip().lower() + if "bus_sharing" in scsi_controller_properties + else None + ) + if bus_sharing and bus_sharing in ["virtual", "physical", "no"]: + bus_sharing = "{0}Sharing".format(bus_sharing) if bus_sharing != device.sharedBus: # Only edit the SCSI controller if bus_sharing is different - scsi_spec = _edit_existing_scsi_controller(device, bus_sharing) + scsi_spec = _edit_existing_scsi_controller( + device, bus_sharing + ) device_specs.append(scsi_spec) elif isinstance(device, vim.vm.device.VirtualCdrom): # this is a cd/dvd drive - if 'cd' in list(devices.keys()): + if "cd" in list(devices.keys()): # there is atleast one cd/dvd drive specified to be created/configured existing_cd_drives_label.append(device.deviceInfo.label) - if device.deviceInfo.label in list(devices['cd'].keys()): - device_type = devices['cd'][device.deviceInfo.label]['device_type'] if 'device_type' in devices['cd'][device.deviceInfo.label] else '' - mode = devices['cd'][device.deviceInfo.label]['mode'] if 'mode' in devices['cd'][device.deviceInfo.label] else '' - iso_path = devices['cd'][device.deviceInfo.label]['iso_path'] if 'iso_path' in devices['cd'][device.deviceInfo.label] else '' - cd_drive_spec = _edit_existing_cd_or_dvd_drive(device, device_type, mode, iso_path) + if device.deviceInfo.label in list(devices["cd"].keys()): + device_type = ( + devices["cd"][device.deviceInfo.label]["device_type"] + if "device_type" in devices["cd"][device.deviceInfo.label] + else "" + ) + mode = ( + devices["cd"][device.deviceInfo.label]["mode"] + if "mode" in devices["cd"][device.deviceInfo.label] + else "" + ) + iso_path = ( + devices["cd"][device.deviceInfo.label]["iso_path"] + if "iso_path" in devices["cd"][device.deviceInfo.label] + else "" + ) + cd_drive_spec = _edit_existing_cd_or_dvd_drive( + device, device_type, mode, iso_path + ) device_specs.append(cd_drive_spec) elif isinstance(device, vim.vm.device.VirtualIDEController): # this is an IDE controller to add new cd drives to ide_controllers[device.key] = len(device.device) - if 'network' in list(devices.keys()): - network_adapters_to_create = list(set(devices['network'].keys()) - set(existing_network_adapters_label)) + if "network" in list(devices.keys()): + network_adapters_to_create = list( + set(devices["network"].keys()) - set(existing_network_adapters_label) + ) network_adapters_to_create.sort() if network_adapters_to_create: log.debug("Networks adapters to create: %s", network_adapters_to_create) for network_adapter_label in network_adapters_to_create: - network_name = devices['network'][network_adapter_label]['name'] - adapter_type = devices['network'][network_adapter_label]['adapter_type'] if 'adapter_type' in devices['network'][network_adapter_label] else '' - switch_type = devices['network'][network_adapter_label]['switch_type'] if 'switch_type' in devices['network'][network_adapter_label] else '' - mac = devices['network'][network_adapter_label]['mac'] if 'mac' in devices['network'][network_adapter_label] else '' + network_name = devices["network"][network_adapter_label]["name"] + adapter_type = ( + devices["network"][network_adapter_label]["adapter_type"] + if "adapter_type" in devices["network"][network_adapter_label] + else "" + ) + switch_type = ( + devices["network"][network_adapter_label]["switch_type"] + if "switch_type" in devices["network"][network_adapter_label] + else "" + ) + mac = ( + devices["network"][network_adapter_label]["mac"] + if "mac" in devices["network"][network_adapter_label] + else "" + ) # create the network adapter - network_spec = _add_new_network_adapter_helper(network_adapter_label, network_name, adapter_type, switch_type, mac, container_ref) - adapter_mapping = _set_network_adapter_mapping(devices['network'][network_adapter_label]) + network_spec = _add_new_network_adapter_helper( + network_adapter_label, + network_name, + adapter_type, + switch_type, + mac, + container_ref, + ) + adapter_mapping = _set_network_adapter_mapping( + devices["network"][network_adapter_label] + ) device_specs.append(network_spec) nics_map.append(adapter_mapping) - if 'scsi' in list(devices.keys()): - scsi_controllers_to_create = list(set(devices['scsi'].keys()) - set(existing_scsi_controllers_label)) + if "scsi" in list(devices.keys()): + scsi_controllers_to_create = list( + set(devices["scsi"].keys()) - set(existing_scsi_controllers_label) + ) scsi_controllers_to_create.sort() if scsi_controllers_to_create: log.debug("SCSI controllers to create: %s", scsi_controllers_to_create) for scsi_controller_label in scsi_controllers_to_create: # create the SCSI controller - scsi_controller_properties = devices['scsi'][scsi_controller_label] - scsi_spec = _add_new_scsi_controller_helper(scsi_controller_label, scsi_controller_properties, bus_number) + scsi_controller_properties = devices["scsi"][scsi_controller_label] + scsi_spec = _add_new_scsi_controller_helper( + scsi_controller_label, scsi_controller_properties, bus_number + ) device_specs.append(scsi_spec) bus_number += 1 - if 'ide' in list(devices.keys()): - ide_controllers_to_create = list(set(devices['ide'].keys()) - set(existing_ide_controllers_label)) + if "ide" in list(devices.keys()): + ide_controllers_to_create = list( + set(devices["ide"].keys()) - set(existing_ide_controllers_label) + ) ide_controllers_to_create.sort() if ide_controllers_to_create: - log.debug('IDE controllers to create: %s', ide_controllers_to_create) + log.debug("IDE controllers to create: %s", ide_controllers_to_create) # ESX 5.5 (and possibly earlier?) set the IDE controller key themselves, indexed starting at # 200. Rather than doing a create task/get vm/reconfig task dance we query the server and # if it's ESX 5.5 we supply a controller starting at 200 and work out way upwards from there # ESX 6 (and, one assumes, vCenter) does not display this problem and so continues to use # the randomly generated indexes - vcenter_name = get_vcenter_version(call='function') - controller_index = SAFE_ESX_5_5_CONTROLLER_KEY_INDEX if ESX_5_5_NAME_PORTION in vcenter_name else None + vcenter_name = get_vcenter_version(call="function") + controller_index = ( + SAFE_ESX_5_5_CONTROLLER_KEY_INDEX + if ESX_5_5_NAME_PORTION in vcenter_name + else None + ) for ide_controller_label in ide_controllers_to_create: # create the IDE controller - ide_spec = _add_new_ide_controller_helper(ide_controller_label, controller_index, bus_number) + ide_spec = _add_new_ide_controller_helper( + ide_controller_label, controller_index, bus_number + ) device_specs.append(ide_spec) bus_number += 1 if controller_index is not None: controller_index += 1 - if 'disk' in list(devices.keys()): - disks_to_create = list(set(devices['disk'].keys()) - set(existing_disks_label)) + if "disk" in list(devices.keys()): + disks_to_create = list(set(devices["disk"].keys()) - set(existing_disks_label)) disks_to_create.sort() if disks_to_create: log.debug("Hard disks to create: %s", disks_to_create) for disk_label in disks_to_create: # create the disk - size_gb = float(devices['disk'][disk_label]['size']) - thin_provision = bool(devices['disk'][disk_label]['thin_provision']) if 'thin_provision' in devices['disk'][disk_label] else False - eagerly_scrub = bool(devices['disk'][disk_label]['eagerly_scrub']) if 'eagerly_scrub' in devices['disk'][disk_label] else False - datastore = devices['disk'][disk_label].get('datastore', None) + size_gb = float(devices["disk"][disk_label]["size"]) + thin_provision = ( + bool(devices["disk"][disk_label]["thin_provision"]) + if "thin_provision" in devices["disk"][disk_label] + else False + ) + eagerly_scrub = ( + bool(devices["disk"][disk_label]["eagerly_scrub"]) + if "eagerly_scrub" in devices["disk"][disk_label] + else False + ) + datastore = devices["disk"][disk_label].get("datastore", None) disk_spec = _add_new_hard_disk_helper( - disk_label, - size_gb, - unit_number, - thin_provision=thin_provision, - eagerly_scrub=eagerly_scrub, - datastore=datastore, - vm_name=new_vm_name - ) + disk_label, + size_gb, + unit_number, + thin_provision=thin_provision, + eagerly_scrub=eagerly_scrub, + datastore=datastore, + vm_name=new_vm_name, + ) # when creating both SCSI controller and Hard disk at the same time we need the randomly # assigned (temporary) key of the newly created SCSI controller - if 'controller' in devices['disk'][disk_label]: + if "controller" in devices["disk"][disk_label]: for spec in device_specs: - if spec.device.deviceInfo.label == devices['disk'][disk_label]['controller']: + if ( + spec.device.deviceInfo.label + == devices["disk"][disk_label]["controller"] + ): disk_spec.device.controllerKey = spec.device.key break device_specs.append(disk_spec) unit_number = _iter_disk_unit_number(unit_number) - if 'cd' in list(devices.keys()): - cd_drives_to_create = list(set(devices['cd'].keys()) - set(existing_cd_drives_label)) + if "cd" in list(devices.keys()): + cd_drives_to_create = list( + set(devices["cd"].keys()) - set(existing_cd_drives_label) + ) cd_drives_to_create.sort() if cd_drives_to_create: log.debug("CD/DVD drives to create: %s", cd_drives_to_create) for cd_drive_label in cd_drives_to_create: # create the CD/DVD drive - device_type = devices['cd'][cd_drive_label]['device_type'] if 'device_type' in devices['cd'][cd_drive_label] else '' - mode = devices['cd'][cd_drive_label]['mode'] if 'mode' in devices['cd'][cd_drive_label] else '' - iso_path = devices['cd'][cd_drive_label]['iso_path'] if 'iso_path' in devices['cd'][cd_drive_label] else '' + device_type = ( + devices["cd"][cd_drive_label]["device_type"] + if "device_type" in devices["cd"][cd_drive_label] + else "" + ) + mode = ( + devices["cd"][cd_drive_label]["mode"] + if "mode" in devices["cd"][cd_drive_label] + else "" + ) + iso_path = ( + devices["cd"][cd_drive_label]["iso_path"] + if "iso_path" in devices["cd"][cd_drive_label] + else "" + ) controller_key = None # When creating both IDE controller and CD/DVD drive at the same time we need the randomly # assigned (temporary) key of the newly created IDE controller - if 'controller' in devices['cd'][cd_drive_label]: + if "controller" in devices["cd"][cd_drive_label]: for spec in device_specs: - if spec.device.deviceInfo.label == devices['cd'][cd_drive_label]['controller']: + if ( + spec.device.deviceInfo.label + == devices["cd"][cd_drive_label]["controller"] + ): controller_key = spec.device.key ide_controllers[controller_key] = 0 break @@ -942,23 +1121,16 @@ def _manage_devices(devices, vm=None, container_ref=None, new_vm_name=None): log.error( "No more available controllers for '%s'. " "All IDE controllers are currently in use", - cd_drive_label + cd_drive_label, ) else: cd_drive_spec = _add_new_cd_or_dvd_drive_helper( - cd_drive_label, - controller_key, - device_type, - mode, - iso_path - ) + cd_drive_label, controller_key, device_type, mode, iso_path + ) device_specs.append(cd_drive_spec) ide_controllers[controller_key] += 1 - ret = { - 'device_specs': device_specs, - 'nics_map': nics_map - } + ret = {"device_specs": device_specs, "nics_map": nics_map} return ret @@ -970,12 +1142,18 @@ def _wait_for_vmware_tools(vm_ref, max_wait): if time_counter % 5 == 0: log.info( "[ %s ] Waiting for VMware tools to be running [%s s]", - vm_ref.name, time_counter + vm_ref.name, + time_counter, ) - if six.text_type(vm_ref.summary.guest.toolsRunningStatus) == "guestToolsRunning": + if ( + six.text_type(vm_ref.summary.guest.toolsRunningStatus) + == "guestToolsRunning" + ): log.info( "[ %s ] Successfully got VMware tools running on the guest in " - "%s seconds", vm_ref.name, time_counter + "%s seconds", + vm_ref.name, + time_counter, ) return True @@ -983,19 +1161,21 @@ def _wait_for_vmware_tools(vm_ref, max_wait): time_counter += 1 log.warning( "[ %s ] Timeout Reached. VMware tools still not running after waiting " - "for %s seconds", vm_ref.name, max_wait + "for %s seconds", + vm_ref.name, + max_wait, ) return False def _valid_ip(ip_address): - ''' + """ Check if the IP address is valid Return either True or False - ''' + """ # Make sure IP has four octets - octets = ip_address.split('.') + octets = ip_address.split(".") if len(octets) != 4: return False @@ -1036,8 +1216,11 @@ def _wait_for_ip(vm_ref, max_wait): # determine the IP using DNS vm_name = vm_ref.summary.config.name resolved_ips = salt.utils.network.host_to_ips(vm_name) - log.debug("Timeout waiting for VMware tools. The name %s resolved " - "to %s", vm_name, resolved_ips) + log.debug( + "Timeout waiting for VMware tools. The name %s resolved " "to %s", + vm_name, + resolved_ips, + ) if isinstance(resolved_ips, list) and resolved_ips: return resolved_ips[0] return False @@ -1047,13 +1230,15 @@ def _wait_for_ip(vm_ref, max_wait): if time_counter % 5 == 0: log.info( "[ %s ] Waiting to retrieve IPv4 information [%s s]", - vm_ref.name, time_counter + vm_ref.name, + time_counter, ) if vm_ref.summary.guest.ipAddress and _valid_ip(vm_ref.summary.guest.ipAddress): log.info( "[ %s ] Successfully retrieved IPv4 information in %s seconds", - vm_ref.name, time_counter + vm_ref.name, + time_counter, ) return vm_ref.summary.guest.ipAddress for net in vm_ref.guest.net: @@ -1062,186 +1247,234 @@ def _wait_for_ip(vm_ref, max_wait): if _valid_ip(current_ip.ipAddress): log.info( "[ %s ] Successfully retrieved IPv4 information " - "in %s seconds", vm_ref.name, time_counter + "in %s seconds", + vm_ref.name, + time_counter, ) return current_ip.ipAddress time.sleep(1.0 - ((time.time() - starttime) % 1.0)) time_counter += 1 log.warning( "[ %s ] Timeout Reached. Unable to retrieve IPv4 information after " - "waiting for %s seconds", vm_ref.name, max_wait_ip + "waiting for %s seconds", + vm_ref.name, + max_wait_ip, ) return False -def _wait_for_host(host_ref, task_type, sleep_seconds=5, log_level='debug'): +def _wait_for_host(host_ref, task_type, sleep_seconds=5, log_level="debug"): time_counter = 0 starttime = time.time() - while host_ref.runtime.connectionState != 'notResponding': + while host_ref.runtime.connectionState != "notResponding": if time_counter % sleep_seconds == 0: log.log( - logging.INFO if log_level == 'info' else logging.DEBUG, + logging.INFO if log_level == "info" else logging.DEBUG, "[ %s ] Waiting for host %s to finish [%s s]", - host_ref.name, task_type, time_counter + host_ref.name, + task_type, + time_counter, ) time.sleep(1.0 - ((time.time() - starttime) % 1.0)) time_counter += 1 - while host_ref.runtime.connectionState != 'connected': + while host_ref.runtime.connectionState != "connected": if time_counter % sleep_seconds == 0: log.log( - logging.INFO if log_level == 'info' else logging.DEBUG, + logging.INFO if log_level == "info" else logging.DEBUG, "[ %s ] Waiting for host %s to finish [%s s]", - host_ref.name, task_type, time_counter + host_ref.name, + task_type, + time_counter, ) time.sleep(1.0 - ((time.time() - starttime) % 1.0)) time_counter += 1 - if host_ref.runtime.connectionState == 'connected': + if host_ref.runtime.connectionState == "connected": log.log( - logging.INFO if log_level == 'info' else logging.DEBUG, + logging.INFO if log_level == "info" else logging.DEBUG, "[ %s ] Successfully completed host %s in %s seconds", - host_ref.name, task_type, time_counter + host_ref.name, + task_type, + time_counter, ) else: - log.error('Could not connect back to the host system') + log.error("Could not connect back to the host system") def _format_instance_info_select(vm, selection): - def defaultto(machine, section, default='N/A'): - ''' + def defaultto(machine, section, default="N/A"): + """ Return either a named value from a VirtualMachineConfig or a default string "N/A". - ''' + """ return default if section not in machine else machine[section] vm_select_info = {} - if 'id' in selection: - vm_select_info['id'] = vm["name"] + if "id" in selection: + vm_select_info["id"] = vm["name"] - if 'image' in selection: - vm_select_info['image'] = "{0} (Detected)".format( - defaultto(vm, "config.guestFullName")) - - if 'size' in selection: - cpu = defaultto(vm, "config.hardware.numCPU") - ram = "{0} MB".format( - defaultto(vm, "config.hardware.memoryMB") + if "image" in selection: + vm_select_info["image"] = "{0} (Detected)".format( + defaultto(vm, "config.guestFullName") ) - vm_select_info['size'] = "cpu: {0}\nram: {1}".format(cpu, ram) - vm_select_info['size_dict'] = { - 'cpu': cpu, - 'memory': ram, + + if "size" in selection: + cpu = defaultto(vm, "config.hardware.numCPU") + ram = "{0} MB".format(defaultto(vm, "config.hardware.memoryMB")) + vm_select_info["size"] = "cpu: {0}\nram: {1}".format(cpu, ram) + vm_select_info["size_dict"] = { + "cpu": cpu, + "memory": ram, } - if 'state' in selection: - vm_select_info['state'] = six.text_type( + if "state" in selection: + vm_select_info["state"] = six.text_type( defaultto(vm, "summary.runtime.powerState") ) - if 'guest_id' in selection: - vm_select_info['guest_id'] = defaultto(vm, "config.guestId") + if "guest_id" in selection: + vm_select_info["guest_id"] = defaultto(vm, "config.guestId") - if 'hostname' in selection: - vm_select_info['hostname'] = vm["object"].guest.hostName + if "hostname" in selection: + vm_select_info["hostname"] = vm["object"].guest.hostName - if 'path' in selection: - vm_select_info['path'] = defaultto(vm, "config.files.vmPathName") + if "path" in selection: + vm_select_info["path"] = defaultto(vm, "config.files.vmPathName") - if 'tools_status' in selection: - vm_select_info['tools_status'] = six.text_type( + if "tools_status" in selection: + vm_select_info["tools_status"] = six.text_type( defaultto(vm, "guest.toolsStatus") ) - if 'private_ips' in selection or 'networks' in selection: + if "private_ips" in selection or "networks" in selection: network_full_info = {} ip_addresses = [] if "guest.net" in vm: for net in vm["guest.net"]: network_full_info[net.network] = { - 'connected': net.connected, - 'ip_addresses': net.ipAddress, - 'mac_address': net.macAddress + "connected": net.connected, + "ip_addresses": net.ipAddress, + "mac_address": net.macAddress, } ip_addresses.extend(net.ipAddress) - if 'private_ips' in selection: - vm_select_info['private_ips'] = ip_addresses + if "private_ips" in selection: + vm_select_info["private_ips"] = ip_addresses - if 'networks' in selection: - vm_select_info['networks'] = network_full_info + if "networks" in selection: + vm_select_info["networks"] = network_full_info - if any(x in ['devices', 'mac_address', 'mac_addresses'] - for x in selection): + if any(x in ["devices", "mac_address", "mac_addresses"] for x in selection): device_full_info = {} device_mac_addresses = [] if "config.hardware.device" in vm: for device in vm["config.hardware.device"]: device_full_info[device.deviceInfo.label] = {} - if 'devices' in selection: - device_full_info[device.deviceInfo.label]['key'] = device.key, - device_full_info[device.deviceInfo.label]['label'] = device.deviceInfo.label, - device_full_info[device.deviceInfo.label]['summary'] = device.deviceInfo.summary, - device_full_info[device.deviceInfo.label]['type'] = type(device).__name__.rsplit(".", 1)[1] + if "devices" in selection: + device_full_info[device.deviceInfo.label]["key"] = (device.key,) + device_full_info[device.deviceInfo.label]["label"] = ( + device.deviceInfo.label, + ) + device_full_info[device.deviceInfo.label]["summary"] = ( + device.deviceInfo.summary, + ) + device_full_info[device.deviceInfo.label]["type"] = type( + device + ).__name__.rsplit(".", 1)[1] if device.unitNumber: - device_full_info[device.deviceInfo.label]['unitNumber'] = device.unitNumber + device_full_info[device.deviceInfo.label][ + "unitNumber" + ] = device.unitNumber - if hasattr(device, 'connectable') and device.connectable: - device_full_info[device.deviceInfo.label]['startConnected'] = device.connectable.startConnected - device_full_info[device.deviceInfo.label]['allowGuestControl'] = device.connectable.allowGuestControl - device_full_info[device.deviceInfo.label]['connected'] = device.connectable.connected - device_full_info[device.deviceInfo.label]['status'] = device.connectable.status + if hasattr(device, "connectable") and device.connectable: + device_full_info[device.deviceInfo.label][ + "startConnected" + ] = device.connectable.startConnected + device_full_info[device.deviceInfo.label][ + "allowGuestControl" + ] = device.connectable.allowGuestControl + device_full_info[device.deviceInfo.label][ + "connected" + ] = device.connectable.connected + device_full_info[device.deviceInfo.label][ + "status" + ] = device.connectable.status - if hasattr(device, 'controllerKey') and device.controllerKey: - device_full_info[device.deviceInfo.label]['controllerKey'] = device.controllerKey + if hasattr(device, "controllerKey") and device.controllerKey: + device_full_info[device.deviceInfo.label][ + "controllerKey" + ] = device.controllerKey - if hasattr(device, 'addressType'): - device_full_info[device.deviceInfo.label]['addressType'] = device.addressType + if hasattr(device, "addressType"): + device_full_info[device.deviceInfo.label][ + "addressType" + ] = device.addressType - if hasattr(device, 'busNumber'): - device_full_info[device.deviceInfo.label]['busNumber'] = device.busNumber + if hasattr(device, "busNumber"): + device_full_info[device.deviceInfo.label][ + "busNumber" + ] = device.busNumber - if hasattr(device, 'device'): - device_full_info[device.deviceInfo.label]['deviceKeys'] = device.device + if hasattr(device, "device"): + device_full_info[device.deviceInfo.label][ + "deviceKeys" + ] = device.device - if hasattr(device, 'videoRamSizeInKB'): - device_full_info[device.deviceInfo.label]['videoRamSizeInKB'] = device.videoRamSizeInKB + if hasattr(device, "videoRamSizeInKB"): + device_full_info[device.deviceInfo.label][ + "videoRamSizeInKB" + ] = device.videoRamSizeInKB if isinstance(device, vim.vm.device.VirtualDisk): - device_full_info[device.deviceInfo.label]['capacityInKB'] = device.capacityInKB - device_full_info[device.deviceInfo.label]['diskMode'] = device.backing.diskMode - device_full_info[device.deviceInfo.label]['fileName'] = device.backing.fileName + device_full_info[device.deviceInfo.label][ + "capacityInKB" + ] = device.capacityInKB + device_full_info[device.deviceInfo.label][ + "diskMode" + ] = device.backing.diskMode + device_full_info[device.deviceInfo.label][ + "fileName" + ] = device.backing.fileName - if hasattr(device, 'macAddress'): - device_full_info[device.deviceInfo.label]['macAddress'] = device.macAddress + if hasattr(device, "macAddress"): + device_full_info[device.deviceInfo.label][ + "macAddress" + ] = device.macAddress device_mac_addresses.append(device.macAddress) - if 'devices' in selection: - vm_select_info['devices'] = device_full_info + if "devices" in selection: + vm_select_info["devices"] = device_full_info - if 'mac_address' in selection or 'mac_addresses' in selection: - vm_select_info['mac_addresses'] = device_mac_addresses + if "mac_address" in selection or "mac_addresses" in selection: + vm_select_info["mac_addresses"] = device_mac_addresses - if 'storage' in selection: + if "storage" in selection: storage_full_info = { - 'committed': int(vm["summary.storage.committed"]) if "summary.storage.committed" in vm else "N/A", - 'uncommitted': int(vm["summary.storage.uncommitted"]) if "summary.storage.uncommitted" in vm else "N/A", - 'unshared': int(vm["summary.storage.unshared"]) if "summary.storage.unshared" in vm else "N/A" + "committed": int(vm["summary.storage.committed"]) + if "summary.storage.committed" in vm + else "N/A", + "uncommitted": int(vm["summary.storage.uncommitted"]) + if "summary.storage.uncommitted" in vm + else "N/A", + "unshared": int(vm["summary.storage.unshared"]) + if "summary.storage.unshared" in vm + else "N/A", } - vm_select_info['storage'] = storage_full_info + vm_select_info["storage"] = storage_full_info - if 'files' in selection: + if "files" in selection: file_full_info = {} if "layoutEx.file" in vm: for filename in vm["layoutEx.file"]: file_full_info[filename.key] = { - 'key': filename.key, - 'name': filename.name, - 'size': filename.size, - 'type': filename.type + "key": filename.key, + "name": filename.name, + "size": filename.size, + "type": filename.type, } - vm_select_info['files'] = file_full_info + vm_select_info["files"] = file_full_info return vm_select_info @@ -1252,59 +1485,91 @@ def _format_instance_info(vm): if "config.hardware.device" in vm: for device in vm["config.hardware.device"]: device_full_info[device.deviceInfo.label] = { - 'key': device.key, - 'label': device.deviceInfo.label, - 'summary': device.deviceInfo.summary, - 'type': type(device).__name__.rsplit(".", 1)[1] + "key": device.key, + "label": device.deviceInfo.label, + "summary": device.deviceInfo.summary, + "type": type(device).__name__.rsplit(".", 1)[1], } if device.unitNumber: - device_full_info[device.deviceInfo.label]['unitNumber'] = device.unitNumber + device_full_info[device.deviceInfo.label][ + "unitNumber" + ] = device.unitNumber - if hasattr(device, 'connectable') and device.connectable: - device_full_info[device.deviceInfo.label]['startConnected'] = device.connectable.startConnected - device_full_info[device.deviceInfo.label]['allowGuestControl'] = device.connectable.allowGuestControl - device_full_info[device.deviceInfo.label]['connected'] = device.connectable.connected - device_full_info[device.deviceInfo.label]['status'] = device.connectable.status + if hasattr(device, "connectable") and device.connectable: + device_full_info[device.deviceInfo.label][ + "startConnected" + ] = device.connectable.startConnected + device_full_info[device.deviceInfo.label][ + "allowGuestControl" + ] = device.connectable.allowGuestControl + device_full_info[device.deviceInfo.label][ + "connected" + ] = device.connectable.connected + device_full_info[device.deviceInfo.label][ + "status" + ] = device.connectable.status - if hasattr(device, 'controllerKey') and device.controllerKey: - device_full_info[device.deviceInfo.label]['controllerKey'] = device.controllerKey + if hasattr(device, "controllerKey") and device.controllerKey: + device_full_info[device.deviceInfo.label][ + "controllerKey" + ] = device.controllerKey - if hasattr(device, 'addressType'): - device_full_info[device.deviceInfo.label]['addressType'] = device.addressType + if hasattr(device, "addressType"): + device_full_info[device.deviceInfo.label][ + "addressType" + ] = device.addressType - if hasattr(device, 'macAddress'): - device_full_info[device.deviceInfo.label]['macAddress'] = device.macAddress + if hasattr(device, "macAddress"): + device_full_info[device.deviceInfo.label][ + "macAddress" + ] = device.macAddress device_mac_addresses.append(device.macAddress) - if hasattr(device, 'busNumber'): - device_full_info[device.deviceInfo.label]['busNumber'] = device.busNumber + if hasattr(device, "busNumber"): + device_full_info[device.deviceInfo.label][ + "busNumber" + ] = device.busNumber - if hasattr(device, 'device'): - device_full_info[device.deviceInfo.label]['deviceKeys'] = device.device + if hasattr(device, "device"): + device_full_info[device.deviceInfo.label]["deviceKeys"] = device.device - if hasattr(device, 'videoRamSizeInKB'): - device_full_info[device.deviceInfo.label]['videoRamSizeInKB'] = device.videoRamSizeInKB + if hasattr(device, "videoRamSizeInKB"): + device_full_info[device.deviceInfo.label][ + "videoRamSizeInKB" + ] = device.videoRamSizeInKB if isinstance(device, vim.vm.device.VirtualDisk): - device_full_info[device.deviceInfo.label]['capacityInKB'] = device.capacityInKB - device_full_info[device.deviceInfo.label]['diskMode'] = device.backing.diskMode - device_full_info[device.deviceInfo.label]['fileName'] = device.backing.fileName + device_full_info[device.deviceInfo.label][ + "capacityInKB" + ] = device.capacityInKB + device_full_info[device.deviceInfo.label][ + "diskMode" + ] = device.backing.diskMode + device_full_info[device.deviceInfo.label][ + "fileName" + ] = device.backing.fileName storage_full_info = { - 'committed': int(vm["summary.storage.committed"]) if "summary.storage.committed" in vm else "N/A", - 'uncommitted': int(vm["summary.storage.uncommitted"]) if "summary.storage.uncommitted" in vm else "N/A", - 'unshared': int(vm["summary.storage.unshared"]) if "summary.storage.unshared" in vm else "N/A" + "committed": int(vm["summary.storage.committed"]) + if "summary.storage.committed" in vm + else "N/A", + "uncommitted": int(vm["summary.storage.uncommitted"]) + if "summary.storage.uncommitted" in vm + else "N/A", + "unshared": int(vm["summary.storage.unshared"]) + if "summary.storage.unshared" in vm + else "N/A", } file_full_info = {} if "layoutEx.file" in vm: for filename in vm["layoutEx.file"]: file_full_info[filename.key] = { - 'key': filename.key, - 'name': filename.name, - 'size': filename.size, - 'type': filename.type + "key": filename.key, + "name": filename.name, + "size": filename.size, + "type": filename.type, } network_full_info = {} @@ -1312,34 +1577,45 @@ def _format_instance_info(vm): if "guest.net" in vm: for net in vm["guest.net"]: network_full_info[net.network] = { - 'connected': net.connected, - 'ip_addresses': net.ipAddress, - 'mac_address': net.macAddress + "connected": net.connected, + "ip_addresses": net.ipAddress, + "mac_address": net.macAddress, } ip_addresses.extend(net.ipAddress) cpu = vm["config.hardware.numCPU"] if "config.hardware.numCPU" in vm else "N/A" - ram = "{0} MB".format(vm["config.hardware.memoryMB"]) if "config.hardware.memoryMB" in vm else "N/A" + ram = ( + "{0} MB".format(vm["config.hardware.memoryMB"]) + if "config.hardware.memoryMB" in vm + else "N/A" + ) vm_full_info = { - 'id': six.text_type(vm['name']), - 'image': "{0} (Detected)".format(vm["config.guestFullName"]) if "config.guestFullName" in vm else "N/A", - 'size': "cpu: {0}\nram: {1}".format(cpu, ram), - 'size_dict': { - 'cpu': cpu, - 'memory': ram, - }, - 'state': six.text_type(vm["summary.runtime.powerState"]) if "summary.runtime.powerState" in vm else "N/A", - 'private_ips': ip_addresses, - 'public_ips': [], - 'devices': device_full_info, - 'storage': storage_full_info, - 'files': file_full_info, - 'guest_id': six.text_type(vm["config.guestId"]) if "config.guestId" in vm else "N/A", - 'hostname': six.text_type(vm["object"].guest.hostName), - 'mac_addresses': device_mac_addresses, - 'networks': network_full_info, - 'path': six.text_type(vm["config.files.vmPathName"]) if "config.files.vmPathName" in vm else "N/A", - 'tools_status': six.text_type(vm["guest.toolsStatus"]) if "guest.toolsStatus" in vm else "N/A" + "id": six.text_type(vm["name"]), + "image": "{0} (Detected)".format(vm["config.guestFullName"]) + if "config.guestFullName" in vm + else "N/A", + "size": "cpu: {0}\nram: {1}".format(cpu, ram), + "size_dict": {"cpu": cpu, "memory": ram}, + "state": six.text_type(vm["summary.runtime.powerState"]) + if "summary.runtime.powerState" in vm + else "N/A", + "private_ips": ip_addresses, + "public_ips": [], + "devices": device_full_info, + "storage": storage_full_info, + "files": file_full_info, + "guest_id": six.text_type(vm["config.guestId"]) + if "config.guestId" in vm + else "N/A", + "hostname": six.text_type(vm["object"].guest.hostName), + "mac_addresses": device_mac_addresses, + "networks": network_full_info, + "path": six.text_type(vm["config.files.vmPathName"]) + if "config.files.vmPathName" in vm + else "N/A", + "tools_status": six.text_type(vm["guest.toolsStatus"]) + if "guest.toolsStatus" in vm + else "N/A", } return vm_full_info @@ -1350,11 +1626,11 @@ def _get_snapshots(snapshot_list, current_snapshot=None, parent_snapshot_path="" for snapshot in snapshot_list: snapshot_path = "{0}/{1}".format(parent_snapshot_path, snapshot.name) snapshots[snapshot_path] = { - 'name': snapshot.name, - 'description': snapshot.description, - 'created': six.text_type(snapshot.createTime).split('.')[0], - 'state': snapshot.state, - 'path': snapshot_path, + "name": snapshot.name, + "description": snapshot.description, + "created": six.text_type(snapshot.createTime).split(".")[0], + "state": snapshot.state, + "path": snapshot_path, } if current_snapshot and current_snapshot == snapshot.snapshot: @@ -1362,7 +1638,9 @@ def _get_snapshots(snapshot_list, current_snapshot=None, parent_snapshot_path="" # Check if child snapshots exist if snapshot.childSnapshotList: - ret = _get_snapshots(snapshot.childSnapshotList, current_snapshot, snapshot_path) + ret = _get_snapshots( + snapshot.childSnapshotList, current_snapshot, snapshot_path + ) if current_snapshot: return ret snapshots.update(ret) @@ -1398,57 +1676,56 @@ def _get_snapshot_ref_by_name(vm_ref, snapshot_name): def _upg_tools_helper(vm, reboot=False): # Exit if template if vm.config.template: - status = 'VMware tools cannot be updated on a template' + status = "VMware tools cannot be updated on a template" # Exit if VMware tools is already up to date elif vm.guest.toolsStatus == "toolsOk": - status = 'VMware tools is already up to date' + status = "VMware tools is already up to date" # Exit if VM is not powered on elif vm.summary.runtime.powerState != "poweredOn": - status = 'VM must be powered on to upgrade tools' + status = "VM must be powered on to upgrade tools" # Exit if VMware tools is either not running or not installed elif vm.guest.toolsStatus in ["toolsNotRunning", "toolsNotInstalled"]: - status = 'VMware tools is either not running or not installed' + status = "VMware tools is either not running or not installed" # If vmware tools is out of date, check major OS family # Upgrade tools on Linux and Windows guests elif vm.guest.toolsStatus == "toolsOld": - log.info('Upgrading VMware tools on %s', vm.name) + log.info("Upgrading VMware tools on %s", vm.name) try: if vm.guest.guestFamily == "windowsGuest" and not reboot: - log.info('Reboot suppressed on %s', vm.name) + log.info("Reboot suppressed on %s", vm.name) task = vm.UpgradeTools('/S /v"/qn REBOOT=R"') elif vm.guest.guestFamily in ["linuxGuest", "windowsGuest"]: task = vm.UpgradeTools() else: - return 'Only Linux and Windows guests are currently supported' - salt.utils.vmware.wait_for_task(task, - vm.name, - 'tools upgrade', - sleep_seconds=5, - log_level='info') + return "Only Linux and Windows guests are currently supported" + salt.utils.vmware.wait_for_task( + task, vm.name, "tools upgrade", sleep_seconds=5, log_level="info" + ) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while upgrading VMware tools on VM %s: %s', - vm.name, exc, + "Error while upgrading VMware tools on VM %s: %s", + vm.name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'VMware tools upgrade failed' - status = 'VMware tools upgrade succeeded' + return "VMware tools upgrade failed" + status = "VMware tools upgrade succeeded" else: - status = 'VMWare tools could not be upgraded' + status = "VMWare tools could not be upgraded" return status def _get_hba_type(hba_type): - ''' + """ Convert a string representation of a HostHostBusAdapter into an object reference. - ''' + """ if hba_type == "parallel": return vim.host.ParallelScsiHba elif hba_type == "block": @@ -1458,11 +1735,11 @@ def _get_hba_type(hba_type): elif hba_type == "fibre": return vim.host.FibreChannelHba - raise ValueError('Unknown Host Bus Adapter Type') + raise ValueError("Unknown Host Bus Adapter Type") def test_vcenter_connection(kwargs=None, call=None): - ''' + """ Test if the connection can be made to the vCenter server using the specified credentials inside ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/vmware.conf`` @@ -1472,24 +1749,24 @@ def test_vcenter_connection(kwargs=None, call=None): .. code-block:: bash salt-cloud -f test_vcenter_connection my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The test_vcenter_connection function must be called with ' - '-f or --function.' + "The test_vcenter_connection function must be called with " + "-f or --function." ) try: # Get the service instance object _get_si() except Exception as exc: # pylint: disable=broad-except - return 'failed to connect: {0}'.format(exc) + return "failed to connect: {0}".format(exc) - return 'connection successful' + return "connection successful" def get_vcenter_version(kwargs=None, call=None): - ''' + """ Show the vCenter Server version with build number. CLI Example: @@ -1497,11 +1774,10 @@ def get_vcenter_version(kwargs=None, call=None): .. code-block:: bash salt-cloud -f get_vcenter_version my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The get_vcenter_version function must be called with ' - '-f or --function.' + "The get_vcenter_version function must be called with " "-f or --function." ) # Get the inventory @@ -1511,7 +1787,7 @@ def get_vcenter_version(kwargs=None, call=None): def list_datacenters(kwargs=None, call=None): - ''' + """ List all the data centers for this VMware environment CLI Example: @@ -1519,18 +1795,17 @@ def list_datacenters(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_datacenters my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_datacenters function must be called with ' - '-f or --function.' + "The list_datacenters function must be called with " "-f or --function." ) - return {'Datacenters': salt.utils.vmware.list_datacenters(_get_si())} + return {"Datacenters": salt.utils.vmware.list_datacenters(_get_si())} def list_portgroups(kwargs=None, call=None): - ''' + """ List all the distributed virtual portgroups for this VMware environment CLI Example: @@ -1538,18 +1813,17 @@ def list_portgroups(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_portgroups my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_portgroups function must be called with ' - '-f or --function.' + "The list_portgroups function must be called with " "-f or --function." ) - return {'Portgroups': salt.utils.vmware.list_portgroups(_get_si())} + return {"Portgroups": salt.utils.vmware.list_portgroups(_get_si())} def list_clusters(kwargs=None, call=None): - ''' + """ List all the clusters for this VMware environment CLI Example: @@ -1557,18 +1831,17 @@ def list_clusters(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_clusters my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_clusters function must be called with ' - '-f or --function.' + "The list_clusters function must be called with " "-f or --function." ) - return {'Clusters': salt.utils.vmware.list_clusters(_get_si())} + return {"Clusters": salt.utils.vmware.list_clusters(_get_si())} def list_datastore_clusters(kwargs=None, call=None): - ''' + """ List all the datastore clusters for this VMware environment CLI Example: @@ -1576,18 +1849,18 @@ def list_datastore_clusters(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_datastore_clusters my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_datastore_clusters function must be called with ' - '-f or --function.' + "The list_datastore_clusters function must be called with " + "-f or --function." ) - return {'Datastore Clusters': salt.utils.vmware.list_datastore_clusters(_get_si())} + return {"Datastore Clusters": salt.utils.vmware.list_datastore_clusters(_get_si())} def list_datastores(kwargs=None, call=None): - ''' + """ List all the datastores for this VMware environment CLI Example: @@ -1595,18 +1868,17 @@ def list_datastores(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_datastores my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_datastores function must be called with ' - '-f or --function.' + "The list_datastores function must be called with " "-f or --function." ) - return {'Datastores': salt.utils.vmware.list_datastores(_get_si())} + return {"Datastores": salt.utils.vmware.list_datastores(_get_si())} def list_hosts(kwargs=None, call=None): - ''' + """ List all the hosts for this VMware environment CLI Example: @@ -1614,18 +1886,17 @@ def list_hosts(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_hosts my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_hosts function must be called with ' - '-f or --function.' + "The list_hosts function must be called with " "-f or --function." ) - return {'Hosts': salt.utils.vmware.list_hosts(_get_si())} + return {"Hosts": salt.utils.vmware.list_hosts(_get_si())} def list_resourcepools(kwargs=None, call=None): - ''' + """ List all the resource pools for this VMware environment CLI Example: @@ -1633,18 +1904,17 @@ def list_resourcepools(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_resourcepools my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_resourcepools function must be called with ' - '-f or --function.' + "The list_resourcepools function must be called with " "-f or --function." ) - return {'Resource Pools': salt.utils.vmware.list_resourcepools(_get_si())} + return {"Resource Pools": salt.utils.vmware.list_resourcepools(_get_si())} def list_networks(kwargs=None, call=None): - ''' + """ List all the standard networks for this VMware environment CLI Example: @@ -1652,18 +1922,17 @@ def list_networks(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_networks my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_networks function must be called with ' - '-f or --function.' + "The list_networks function must be called with " "-f or --function." ) - return {'Networks': salt.utils.vmware.list_networks(_get_si())} + return {"Networks": salt.utils.vmware.list_networks(_get_si())} def list_nodes_min(kwargs=None, call=None): - ''' + """ Return a list of all VMs and templates that are on the specified provider, with no details CLI Example: @@ -1671,26 +1940,27 @@ def list_nodes_min(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_nodes_min my-vmware-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_min function must be called ' - 'with -f or --function.' + "The list_nodes_min function must be called " "with -f or --function." ) ret = {} vm_properties = ["name"] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: - ret[vm['name']] = {'state': 'Running', 'id': vm['name']} + ret[vm["name"]] = {"state": "Running", "id": vm["name"]} return ret def list_nodes(kwargs=None, call=None): - ''' + """ Return a list of all VMs and templates that are on the specified provider, with basic fields CLI Example: @@ -1707,11 +1977,10 @@ def list_nodes(kwargs=None, call=None): .. code-block:: bash salt-cloud -Q - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called ' - 'with -f or --function.' + "The list_nodes function must be called " "with -f or --function." ) ret = {} @@ -1721,33 +1990,40 @@ def list_nodes(kwargs=None, call=None): "config.guestFullName", "config.hardware.numCPU", "config.hardware.memoryMB", - "summary.runtime.powerState" + "summary.runtime.powerState", ] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: cpu = vm["config.hardware.numCPU"] if "config.hardware.numCPU" in vm else "N/A" - ram = "{0} MB".format(vm["config.hardware.memoryMB"]) if "config.hardware.memoryMB" in vm else "N/A" + ram = ( + "{0} MB".format(vm["config.hardware.memoryMB"]) + if "config.hardware.memoryMB" in vm + else "N/A" + ) vm_info = { - 'id': vm["name"], - 'image': "{0} (Detected)".format(vm["config.guestFullName"]) if "config.guestFullName" in vm else "N/A", - 'size': "cpu: {0}\nram: {1}".format(cpu, ram), - 'size_dict': { - 'cpu': cpu, - 'memory': ram, - }, - 'state': six.text_type(vm["summary.runtime.powerState"]) if "summary.runtime.powerState" in vm else "N/A", - 'private_ips': [vm["guest.ipAddress"]] if "guest.ipAddress" in vm else [], - 'public_ips': [] + "id": vm["name"], + "image": "{0} (Detected)".format(vm["config.guestFullName"]) + if "config.guestFullName" in vm + else "N/A", + "size": "cpu: {0}\nram: {1}".format(cpu, ram), + "size_dict": {"cpu": cpu, "memory": ram}, + "state": six.text_type(vm["summary.runtime.powerState"]) + if "summary.runtime.powerState" in vm + else "N/A", + "private_ips": [vm["guest.ipAddress"]] if "guest.ipAddress" in vm else [], + "public_ips": [], } - ret[vm_info['id']] = vm_info + ret[vm_info["id"]] = vm_info return ret def list_nodes_full(kwargs=None, call=None): - ''' + """ Return a list of all VMs and templates that are on the specified provider, with full details CLI Example: @@ -1764,11 +2040,10 @@ def list_nodes_full(kwargs=None, call=None): .. code-block:: bash salt-cloud -F - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called ' - 'with -f or --function.' + "The list_nodes_full function must be called " "with -f or --function." ) ret = {} @@ -1786,10 +2061,12 @@ def list_nodes_full(kwargs=None, call=None): "config.hardware.numCPU", "config.files.vmPathName", "summary.runtime.powerState", - "guest.toolsStatus" + "guest.toolsStatus", ] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: ret[vm["name"]] = _format_instance_info(vm) @@ -1798,7 +2075,7 @@ def list_nodes_full(kwargs=None, call=None): def list_nodes_select(call=None): - ''' + """ Return a list of all VMs and templates that are on the specified provider, with fields specified under ``query.selection`` in ``/etc/salt/cloud`` @@ -1816,69 +2093,74 @@ def list_nodes_select(call=None): .. code-block:: bash salt-cloud -S - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_select function must be called ' - 'with -f or --function.' + "The list_nodes_select function must be called " "with -f or --function." ) ret = {} vm_properties = [] - selection = __opts__.get('query.selection') + selection = __opts__.get("query.selection") if not selection: - raise SaltCloudSystemExit( - 'query.selection not found in /etc/salt/cloud' - ) + raise SaltCloudSystemExit("query.selection not found in /etc/salt/cloud") - if 'id' in selection: + if "id" in selection: vm_properties.append("name") - if 'image' in selection: + if "image" in selection: vm_properties.append("config.guestFullName") - if 'size' in selection: + if "size" in selection: vm_properties.extend(["config.hardware.numCPU", "config.hardware.memoryMB"]) - if 'state' in selection: + if "state" in selection: vm_properties.append("summary.runtime.powerState") - if 'private_ips' in selection or 'networks' in selection: + if "private_ips" in selection or "networks" in selection: vm_properties.append("guest.net") - if 'devices' in selection or 'mac_address' in selection or 'mac_addresses' in selection: + if ( + "devices" in selection + or "mac_address" in selection + or "mac_addresses" in selection + ): vm_properties.append("config.hardware.device") - if 'storage' in selection: - vm_properties.extend([ - "config.hardware.device", - "summary.storage.committed", - "summary.storage.uncommitted", - "summary.storage.unshared" - ]) + if "storage" in selection: + vm_properties.extend( + [ + "config.hardware.device", + "summary.storage.committed", + "summary.storage.uncommitted", + "summary.storage.unshared", + ] + ) - if 'files' in selection: + if "files" in selection: vm_properties.append("layoutEx.file") - if 'guest_id' in selection: + if "guest_id" in selection: vm_properties.append("config.guestId") - if 'hostname' in selection: + if "hostname" in selection: vm_properties.append("guest.hostName") - if 'path' in selection: + if "path" in selection: vm_properties.append("config.files.vmPathName") - if 'tools_status' in selection: + if "tools_status" in selection: vm_properties.append("guest.toolsStatus") if not vm_properties: return {} - elif 'name' not in vm_properties: + elif "name" not in vm_properties: vm_properties.append("name") - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: ret[vm["name"]] = _format_instance_info_select(vm, selection) @@ -1886,7 +2168,7 @@ def list_nodes_select(call=None): def show_instance(name, call=None): - ''' + """ List all available details of the specified VM CLI Example: @@ -1894,11 +2176,10 @@ def show_instance(name, call=None): .. code-block:: bash salt-cloud -a show_instance vmname - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with ' - '-a or --action.' + "The show_instance action must be called with " "-a or --action." ) vm_properties = [ @@ -1915,20 +2196,22 @@ def show_instance(name, call=None): "config.hardware.numCPU", "config.files.vmPathName", "summary.runtime.powerState", - "guest.toolsStatus" + "guest.toolsStatus", ] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: - if vm['name'] == name: + if vm["name"] == name: return _format_instance_info(vm) return {} def avail_images(call=None): - ''' + """ Return a list of all the templates present in this VMware environment with basic details @@ -1937,11 +2220,11 @@ def avail_images(call=None): .. code-block:: bash salt-cloud --list-images my-vmware-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option.' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option." ) templates = {} @@ -1950,25 +2233,33 @@ def avail_images(call=None): "config.template", "config.guestFullName", "config.hardware.numCPU", - "config.hardware.memoryMB" + "config.hardware.memoryMB", ] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: if "config.template" in vm and vm["config.template"]: templates[vm["name"]] = { - 'name': vm["name"], - 'guest_fullname': vm["config.guestFullName"] if "config.guestFullName" in vm else "N/A", - 'cpus': vm["config.hardware.numCPU"] if "config.hardware.numCPU" in vm else "N/A", - 'ram': vm["config.hardware.memoryMB"] if "config.hardware.memoryMB" in vm else "N/A" + "name": vm["name"], + "guest_fullname": vm["config.guestFullName"] + if "config.guestFullName" in vm + else "N/A", + "cpus": vm["config.hardware.numCPU"] + if "config.hardware.numCPU" in vm + else "N/A", + "ram": vm["config.hardware.memoryMB"] + if "config.hardware.memoryMB" in vm + else "N/A", } return templates def avail_locations(call=None): - ''' + """ Return a list of all the available locations/datacenters in this VMware environment CLI Example: @@ -1976,18 +2267,18 @@ def avail_locations(call=None): .. code-block:: bash salt-cloud --list-locations my-vmware-config - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option.' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option." ) - return list_datacenters(call='function') + return list_datacenters(call="function") def avail_sizes(call=None): - ''' + """ Return a list of all the available sizes in this VMware environment. CLI Example: @@ -2001,23 +2292,23 @@ def avail_sizes(call=None): Since sizes are built into templates, this function will return an empty dictionary. - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option.' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option." ) log.warning( - 'Because sizes are built into templates with VMware, there are no sizes ' - 'to return.' + "Because sizes are built into templates with VMware, there are no sizes " + "to return." ) return {} def list_templates(kwargs=None, call=None): - ''' + """ List all the templates present in this VMware environment CLI Example: @@ -2025,18 +2316,17 @@ def list_templates(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_templates my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_templates function must be called with ' - '-f or --function.' + "The list_templates function must be called with " "-f or --function." ) - return {'Templates': avail_images(call='function')} + return {"Templates": avail_images(call="function")} def list_folders(kwargs=None, call=None): - ''' + """ List all the folders for this VMware environment CLI Example: @@ -2044,18 +2334,17 @@ def list_folders(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_folders my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_folders function must be called with ' - '-f or --function.' + "The list_folders function must be called with " "-f or --function." ) - return {'Folders': salt.utils.vmware.list_folders(_get_si())} + return {"Folders": salt.utils.vmware.list_folders(_get_si())} def list_snapshots(kwargs=None, call=None): - ''' + """ List snapshots either for all VMs and templates or for a specific VM/template in this VMware environment @@ -2074,37 +2363,34 @@ def list_snapshots(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_snapshots my-vmware-config name="vmname" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_snapshots function must be called with ' - '-f or --function.' + "The list_snapshots function must be called with " "-f or --function." ) ret = {} - vm_properties = [ - "name", - "rootSnapshot", - "snapshot" - ] + vm_properties = ["name", "rootSnapshot", "snapshot"] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: if vm["rootSnapshot"]: - if kwargs and kwargs.get('name') == vm["name"]: + if kwargs and kwargs.get("name") == vm["name"]: return {vm["name"]: _get_snapshots(vm["snapshot"].rootSnapshotList)} else: ret[vm["name"]] = _get_snapshots(vm["snapshot"].rootSnapshotList) else: - if kwargs and kwargs.get('name') == vm["name"]: + if kwargs and kwargs.get("name") == vm["name"]: return {} return ret def start(name, call=None): - ''' + """ To start/power on a VM using its name CLI Example: @@ -2112,44 +2398,43 @@ def start(name, call=None): .. code-block:: bash salt-cloud -a start vmname - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The start action must be called with ' - '-a or --action.' + "The start action must be called with " "-a or --action." ) - vm_properties = [ - "name", - "summary.runtime.powerState" - ] + vm_properties = ["name", "summary.runtime.powerState"] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: if vm["name"] == name: if vm["summary.runtime.powerState"] == "poweredOn": - ret = 'already powered on' - log.info('VM %s %s', name, ret) + ret = "already powered on" + log.info("VM %s %s", name, ret) return ret try: - log.info('Starting VM %s', name) + log.info("Starting VM %s", name) task = vm["object"].PowerOn() - salt.utils.vmware.wait_for_task(task, name, 'power on') + salt.utils.vmware.wait_for_task(task, name, "power on") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while powering on VM %s: %s', - name, exc, + "Error while powering on VM %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to power on' + return "failed to power on" - return 'powered on' + return "powered on" def stop(name, soft=False, call=None): - ''' + """ To stop/power off a VM using its name .. note:: @@ -2166,47 +2451,46 @@ def stop(name, soft=False, call=None): salt-cloud -a stop vmname salt-cloud -a stop vmname soft=True - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The stop action must be called with ' - '-a or --action.' + "The stop action must be called with " "-a or --action." ) - vm_properties = [ - "name", - "summary.runtime.powerState" - ] + vm_properties = ["name", "summary.runtime.powerState"] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: if vm["name"] == name: if vm["summary.runtime.powerState"] == "poweredOff": - ret = 'already powered off' - log.info('VM %s %s', name, ret) + ret = "already powered off" + log.info("VM %s %s", name, ret) return ret try: - log.info('Stopping VM %s', name) + log.info("Stopping VM %s", name) if soft: vm["object"].ShutdownGuest() else: task = vm["object"].PowerOff() - salt.utils.vmware.wait_for_task(task, name, 'power off') + salt.utils.vmware.wait_for_task(task, name, "power off") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while powering off VM %s: %s', - name, exc, + "Error while powering off VM %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to power off' + return "failed to power off" - return 'powered off' + return "powered off" def suspend(name, call=None): - ''' + """ To suspend a VM using its name CLI Example: @@ -2214,48 +2498,47 @@ def suspend(name, call=None): .. code-block:: bash salt-cloud -a suspend vmname - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The suspend action must be called with ' - '-a or --action.' + "The suspend action must be called with " "-a or --action." ) - vm_properties = [ - "name", - "summary.runtime.powerState" - ] + vm_properties = ["name", "summary.runtime.powerState"] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: if vm["name"] == name: if vm["summary.runtime.powerState"] == "poweredOff": - ret = 'cannot suspend in powered off state' - log.info('VM %s %s', name, ret) + ret = "cannot suspend in powered off state" + log.info("VM %s %s", name, ret) return ret elif vm["summary.runtime.powerState"] == "suspended": - ret = 'already suspended' - log.info('VM %s %s', name, ret) + ret = "already suspended" + log.info("VM %s %s", name, ret) return ret try: - log.info('Suspending VM %s', name) + log.info("Suspending VM %s", name) task = vm["object"].Suspend() - salt.utils.vmware.wait_for_task(task, name, 'suspend') + salt.utils.vmware.wait_for_task(task, name, "suspend") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while suspending VM %s: %s', - name, exc, + "Error while suspending VM %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to suspend' + return "failed to suspend" - return 'suspended' + return "suspended" def reset(name, soft=False, call=None): - ''' + """ To reset a VM using its name .. note:: @@ -2272,47 +2555,49 @@ def reset(name, soft=False, call=None): salt-cloud -a reset vmname salt-cloud -a reset vmname soft=True - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The reset action must be called with ' - '-a or --action.' + "The reset action must be called with " "-a or --action." ) - vm_properties = [ - "name", - "summary.runtime.powerState" - ] + vm_properties = ["name", "summary.runtime.powerState"] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: if vm["name"] == name: - if vm["summary.runtime.powerState"] == "suspended" or vm["summary.runtime.powerState"] == "poweredOff": - ret = 'cannot reset in suspended/powered off state' - log.info('VM %s %s', name, ret) + if ( + vm["summary.runtime.powerState"] == "suspended" + or vm["summary.runtime.powerState"] == "poweredOff" + ): + ret = "cannot reset in suspended/powered off state" + log.info("VM %s %s", name, ret) return ret try: - log.info('Resetting VM %s', name) + log.info("Resetting VM %s", name) if soft: vm["object"].RebootGuest() else: task = vm["object"].ResetVM_Task() - salt.utils.vmware.wait_for_task(task, name, 'reset') + salt.utils.vmware.wait_for_task(task, name, "reset") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while resetting VM %s: %s', - name, exc, + "Error while resetting VM %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to reset' + return "failed to reset" - return 'reset' + return "reset" def terminate(name, call=None): - ''' + """ To do an immediate power off of a VM using its name. A ``SIGKILL`` is issued to the vmx process of the VM @@ -2321,43 +2606,42 @@ def terminate(name, call=None): .. code-block:: bash salt-cloud -a terminate vmname - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The terminate action must be called with ' - '-a or --action.' + "The terminate action must be called with " "-a or --action." ) - vm_properties = [ - "name", - "summary.runtime.powerState" - ] + vm_properties = ["name", "summary.runtime.powerState"] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: if vm["name"] == name: if vm["summary.runtime.powerState"] == "poweredOff": - ret = 'already powered off' - log.info('VM %s %s', name, ret) + ret = "already powered off" + log.info("VM %s %s", name, ret) return ret try: - log.info('Terminating VM %s', name) + log.info("Terminating VM %s", name) vm["object"].Terminate() except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while terminating VM %s: %s', - name, exc, + "Error while terminating VM %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to terminate' + return "failed to terminate" - return 'terminated' + return "terminated" def destroy(name, call=None): - ''' + """ To destroy a VM from the VMware environment CLI Example: @@ -2367,74 +2651,76 @@ def destroy(name, call=None): salt-cloud -d vmname salt-cloud --destroy vmname salt-cloud -a destroy vmname - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - vm_properties = [ - "name", - "summary.runtime.powerState" - ] + vm_properties = ["name", "summary.runtime.powerState"] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: if vm["name"] == name: if vm["summary.runtime.powerState"] != "poweredOff": # Power off the vm first try: - log.info('Powering Off VM %s', name) + log.info("Powering Off VM %s", name) task = vm["object"].PowerOff() - salt.utils.vmware.wait_for_task(task, name, 'power off') + salt.utils.vmware.wait_for_task(task, name, "power off") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while powering off VM %s: %s', - name, exc, + "Error while powering off VM %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to destroy' + return "failed to destroy" try: - log.info('Destroying VM %s', name) + log.info("Destroying VM %s", name) task = vm["object"].Destroy_Task() - salt.utils.vmware.wait_for_task(task, name, 'destroy') + salt.utils.vmware.wait_for_task(task, name, "destroy") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while destroying VM %s: %s', - name, exc, + "Error while destroying VM %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to destroy' + return "failed to destroy" - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) return True def create(vm_): - ''' + """ To create a single VM in the VMware environment. Sample profile and arguments that can be specified in it can be found @@ -2445,110 +2731,98 @@ def create(vm_): .. code-block:: bash salt-cloud -p vmware-centos6.5 vmname - ''' + """ try: # Check for required profile parameters before sending any API calls. - if (vm_['profile'] and - config.is_profile_configured(__opts__, - __active_provider_name__ or 'vmware', - vm_['profile'], - vm_=vm_) is False): + if ( + vm_["profile"] + and config.is_profile_configured( + __opts__, __active_provider_name__ or "vmware", vm_["profile"], vm_=vm_ + ) + is False + ): return False except AttributeError: pass - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - vm_name = config.get_cloud_config_value( - 'name', vm_, __opts__, default=None - ) - folder = config.get_cloud_config_value( - 'folder', vm_, __opts__, default=None - ) + vm_name = config.get_cloud_config_value("name", vm_, __opts__, default=None) + folder = config.get_cloud_config_value("folder", vm_, __opts__, default=None) datacenter = config.get_cloud_config_value( - 'datacenter', vm_, __opts__, default=None + "datacenter", vm_, __opts__, default=None ) resourcepool = config.get_cloud_config_value( - 'resourcepool', vm_, __opts__, default=None - ) - cluster = config.get_cloud_config_value( - 'cluster', vm_, __opts__, default=None - ) - datastore = config.get_cloud_config_value( - 'datastore', vm_, __opts__, default=None - ) - host = config.get_cloud_config_value( - 'host', vm_, __opts__, default=None - ) - template = config.get_cloud_config_value( - 'template', vm_, __opts__, default=False - ) - num_cpus = config.get_cloud_config_value( - 'num_cpus', vm_, __opts__, default=None + "resourcepool", vm_, __opts__, default=None ) + cluster = config.get_cloud_config_value("cluster", vm_, __opts__, default=None) + datastore = config.get_cloud_config_value("datastore", vm_, __opts__, default=None) + host = config.get_cloud_config_value("host", vm_, __opts__, default=None) + template = config.get_cloud_config_value("template", vm_, __opts__, default=False) + num_cpus = config.get_cloud_config_value("num_cpus", vm_, __opts__, default=None) cores_per_socket = config.get_cloud_config_value( - 'cores_per_socket', vm_, __opts__, default=None - ) - memory = config.get_cloud_config_value( - 'memory', vm_, __opts__, default=None - ) - devices = config.get_cloud_config_value( - 'devices', vm_, __opts__, default=None + "cores_per_socket", vm_, __opts__, default=None ) + memory = config.get_cloud_config_value("memory", vm_, __opts__, default=None) + devices = config.get_cloud_config_value("devices", vm_, __opts__, default=None) extra_config = config.get_cloud_config_value( - 'extra_config', vm_, __opts__, default=None + "extra_config", vm_, __opts__, default=None ) annotation = config.get_cloud_config_value( - 'annotation', vm_, __opts__, default=None - ) - power = config.get_cloud_config_value( - 'power_on', vm_, __opts__, default=True + "annotation", vm_, __opts__, default=None ) + power = config.get_cloud_config_value("power_on", vm_, __opts__, default=True) key_filename = config.get_cloud_config_value( - 'private_key', vm_, __opts__, search_global=False, default=None + "private_key", vm_, __opts__, search_global=False, default=None ) deploy = config.get_cloud_config_value( - 'deploy', vm_, __opts__, search_global=True, default=True + "deploy", vm_, __opts__, search_global=True, default=True ) wait_for_ip_timeout = config.get_cloud_config_value( - 'wait_for_ip_timeout', vm_, __opts__, default=20 * 60 + "wait_for_ip_timeout", vm_, __opts__, default=20 * 60 ) domain = config.get_cloud_config_value( - 'domain', vm_, __opts__, search_global=False, default='local' + "domain", vm_, __opts__, search_global=False, default="local" ) hardware_version = config.get_cloud_config_value( - 'hardware_version', vm_, __opts__, search_global=False, default=None + "hardware_version", vm_, __opts__, search_global=False, default=None ) guest_id = config.get_cloud_config_value( - 'image', vm_, __opts__, search_global=False, default=None + "image", vm_, __opts__, search_global=False, default=None ) customization = config.get_cloud_config_value( - 'customization', vm_, __opts__, search_global=False, default=True + "customization", vm_, __opts__, search_global=False, default=True ) customization_spec = config.get_cloud_config_value( - 'customization_spec', vm_, __opts__, search_global=False, default=None + "customization_spec", vm_, __opts__, search_global=False, default=None ) win_password = config.get_cloud_config_value( - 'win_password', vm_, __opts__, search_global=False, default=None + "win_password", vm_, __opts__, search_global=False, default=None ) win_organization_name = config.get_cloud_config_value( - 'win_organization_name', vm_, __opts__, search_global=False, default='Organization' + "win_organization_name", + vm_, + __opts__, + search_global=False, + default="Organization", ) plain_text = config.get_cloud_config_value( - 'plain_text', vm_, __opts__, search_global=False, default=False + "plain_text", vm_, __opts__, search_global=False, default=False ) win_user_fullname = config.get_cloud_config_value( - 'win_user_fullname', vm_, __opts__, search_global=False, default='Windows User' + "win_user_fullname", vm_, __opts__, search_global=False, default="Windows User" ) win_run_once = config.get_cloud_config_value( - 'win_run_once', vm_, __opts__, search_global=False, default=None + "win_run_once", vm_, __opts__, search_global=False, default=None ) # Get service instance object @@ -2558,31 +2832,28 @@ def create(vm_): # If datacenter is specified, set the container reference to start search from it instead if datacenter: - datacenter_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.Datacenter, datacenter) + datacenter_ref = salt.utils.vmware.get_mor_by_property( + _get_si(), vim.Datacenter, datacenter + ) container_ref = datacenter_ref if datacenter_ref else None - if 'clonefrom' in vm_: + if "clonefrom" in vm_: # If datacenter is specified, set the container reference to start search from it instead if datacenter: datacenter_ref = salt.utils.vmware.get_mor_by_property( - si, - vim.Datacenter, - datacenter - ) + si, vim.Datacenter, datacenter + ) container_ref = datacenter_ref if datacenter_ref else None # Clone VM/template from specified VM/template object_ref = salt.utils.vmware.get_mor_by_property( - si, - vim.VirtualMachine, - vm_['clonefrom'], - container_ref=container_ref - ) + si, vim.VirtualMachine, vm_["clonefrom"], container_ref=container_ref + ) if object_ref: clone_type = "template" if object_ref.config.template else "vm" else: raise SaltCloudSystemExit( - 'The VM/template that you have specified under clonefrom does not exist.' + "The VM/template that you have specified under clonefrom does not exist." ) else: clone_type = None @@ -2591,73 +2862,74 @@ def create(vm_): # Either a cluster, or a resource pool must be specified when cloning from template or creating. if resourcepool: resourcepool_ref = salt.utils.vmware.get_mor_by_property( - si, - vim.ResourcePool, - resourcepool, - container_ref=container_ref - ) + si, vim.ResourcePool, resourcepool, container_ref=container_ref + ) if not resourcepool_ref: log.error("Specified resource pool: '%s' does not exist", resourcepool) if not clone_type or clone_type == "template": - raise SaltCloudSystemExit('You must specify a resource pool that exists.') + raise SaltCloudSystemExit( + "You must specify a resource pool that exists." + ) elif cluster: cluster_ref = salt.utils.vmware.get_mor_by_property( - si, - vim.ClusterComputeResource, - cluster, - container_ref=container_ref - ) + si, vim.ClusterComputeResource, cluster, container_ref=container_ref + ) if not cluster_ref: log.error("Specified cluster: '%s' does not exist", cluster) if not clone_type or clone_type == "template": - raise SaltCloudSystemExit('You must specify a cluster that exists.') + raise SaltCloudSystemExit("You must specify a cluster that exists.") else: resourcepool_ref = cluster_ref.resourcePool elif clone_type == "template": raise SaltCloudSystemExit( - 'You must either specify a cluster or a resource pool when cloning from a template.' + "You must either specify a cluster or a resource pool when cloning from a template." ) elif not clone_type: raise SaltCloudSystemExit( - 'You must either specify a cluster or a resource pool when creating.' + "You must either specify a cluster or a resource pool when creating." ) else: - log.debug("Using resource pool used by the %s %s", clone_type, vm_['clonefrom']) + log.debug("Using resource pool used by the %s %s", clone_type, vm_["clonefrom"]) # Either a datacenter or a folder can be optionally specified when cloning, required when creating. # If not specified when cloning, the existing VM/template\'s parent folder is used. if folder: - folder_parts = folder.split('/') + folder_parts = folder.split("/") search_reference = container_ref for folder_part in folder_parts: if folder_part: folder_ref = salt.utils.vmware.get_mor_by_property( - si, - vim.Folder, - folder_part, - container_ref=search_reference - ) + si, vim.Folder, folder_part, container_ref=search_reference + ) search_reference = folder_ref if not folder_ref: log.error("Specified folder: '%s' does not exist", folder) - log.debug("Using folder in which %s %s is present", clone_type, vm_['clonefrom']) + log.debug( + "Using folder in which %s %s is present", clone_type, vm_["clonefrom"] + ) folder_ref = object_ref.parent elif datacenter: if not datacenter_ref: log.error("Specified datacenter: '%s' does not exist", datacenter) - log.debug("Using datacenter folder in which %s %s is present", clone_type, vm_['clonefrom']) + log.debug( + "Using datacenter folder in which %s %s is present", + clone_type, + vm_["clonefrom"], + ) folder_ref = object_ref.parent else: folder_ref = datacenter_ref.vmFolder elif not clone_type: raise SaltCloudSystemExit( - 'You must either specify a folder or a datacenter when creating not cloning.' + "You must either specify a folder or a datacenter when creating not cloning." ) else: - log.debug("Using folder in which %s %s is present", clone_type, vm_['clonefrom']) + log.debug( + "Using folder in which %s %s is present", clone_type, vm_["clonefrom"] + ) folder_ref = object_ref.parent - if 'clonefrom' in vm_: + if "clonefrom" in vm_: # Create the relocation specs reloc_spec = vim.vm.RelocateSpec() @@ -2668,30 +2940,33 @@ def create(vm_): # If not specified, the current datastore is used. if datastore: datastore_ref = salt.utils.vmware.get_mor_by_property( - si, - vim.Datastore, - datastore, - container_ref=container_ref - ) + si, vim.Datastore, datastore, container_ref=container_ref + ) if datastore_ref: # specific datastore has been specified reloc_spec.datastore = datastore_ref else: - datastore_cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.StoragePod, datastore, container_ref=container_ref) + datastore_cluster_ref = salt.utils.vmware.get_mor_by_property( + si, vim.StoragePod, datastore, container_ref=container_ref + ) if not datastore_cluster_ref: - log.error("Specified datastore/datastore cluster: '%s' does not exist", datastore) - log.debug("Using datastore used by the %s %s", clone_type, vm_['clonefrom']) + log.error( + "Specified datastore/datastore cluster: '%s' does not exist", + datastore, + ) + log.debug( + "Using datastore used by the %s %s", + clone_type, + vm_["clonefrom"], + ) else: log.debug("No datastore/datastore cluster specified") - log.debug("Using datastore used by the %s %s", clone_type, vm_['clonefrom']) + log.debug("Using datastore used by the %s %s", clone_type, vm_["clonefrom"]) if host: host_ref = salt.utils.vmware.get_mor_by_property( - si, - vim.HostSystem, - host, - container_ref=container_ref - ) + si, vim.HostSystem, host, container_ref=container_ref + ) if host_ref: reloc_spec.host = host_ref else: @@ -2699,24 +2974,21 @@ def create(vm_): else: if not datastore: raise SaltCloudSystemExit( - 'You must specify a datastore when creating not cloning.' + "You must specify a datastore when creating not cloning." ) else: datastore_ref = salt.utils.vmware.get_mor_by_property( - si, - vim.Datastore, - datastore - ) + si, vim.Datastore, datastore + ) if not datastore_ref: - raise SaltCloudSystemExit("Specified datastore: '{0}' does not exist".format(datastore)) + raise SaltCloudSystemExit( + "Specified datastore: '{0}' does not exist".format(datastore) + ) if host: host_ref = salt.utils.vmware.get_mor_by_property( - _get_si(), - vim.HostSystem, - host, - container_ref=container_ref - ) + _get_si(), vim.HostSystem, host, container_ref=container_ref + ) if not host_ref: log.error("Specified host: '%s' does not exist", host) @@ -2726,14 +2998,15 @@ def create(vm_): # If the hardware version is specified and if it is different from the current # hardware version, then schedule a hardware version upgrade if hardware_version and object_ref is not None: - hardware_version = 'vmx-{0:02}'.format(hardware_version) + hardware_version = "vmx-{0:02}".format(hardware_version) if hardware_version != object_ref.config.version: log.debug( "Scheduling hardware version upgrade from %s to %s", - object_ref.config.version, hardware_version + object_ref.config.version, + hardware_version, ) scheduled_hardware_upgrade = vim.vm.ScheduledHardwareUpgradeInfo() - scheduled_hardware_upgrade.upgradePolicy = 'always' + scheduled_hardware_upgrade.upgradePolicy = "always" scheduled_hardware_upgrade.versionKey = hardware_version config_spec.scheduledHardwareUpgradeInfo = scheduled_hardware_upgrade else: @@ -2749,24 +3022,25 @@ def create(vm_): if memory: try: - memory_num, memory_unit = re.findall(r"[^\W\d_]+|\d+.\d+|\d+", - memory) + memory_num, memory_unit = re.findall(r"[^\W\d_]+|\d+.\d+|\d+", memory) if memory_unit.lower() == "mb": memory_mb = int(memory_num) elif memory_unit.lower() == "gb": - memory_mb = int(float(memory_num)*1024.0) + memory_mb = int(float(memory_num) * 1024.0) else: err_msg = "Invalid memory type specified: '{0}'".format(memory_unit) log.error(err_msg) - return {'Error': err_msg} + return {"Error": err_msg} except (TypeError, ValueError): memory_mb = int(memory) log.debug("Setting memory to: %s MB", memory_mb) config_spec.memoryMB = memory_mb if devices: - specs = _manage_devices(devices, vm=object_ref, container_ref=container_ref, new_vm_name=vm_name) - config_spec.deviceChange = specs['device_specs'] + specs = _manage_devices( + devices, vm=object_ref, container_ref=container_ref, new_vm_name=vm_name + ) + config_spec.deviceChange = specs["device_specs"] if extra_config: for key, value in six.iteritems(extra_config): @@ -2776,41 +3050,30 @@ def create(vm_): if annotation: config_spec.annotation = six.text_type(annotation) - if 'clonefrom' in vm_: - clone_spec = handle_snapshot( - config_spec, - object_ref, - reloc_spec, - template, - vm_ - ) + if "clonefrom" in vm_: + clone_spec = handle_snapshot(config_spec, object_ref, reloc_spec, template, vm_) if not clone_spec: - clone_spec = build_clonespec(config_spec, - object_ref, - reloc_spec, - template) + clone_spec = build_clonespec(config_spec, object_ref, reloc_spec, template) if customization and customization_spec: - customization_spec = salt.utils.vmware.get_customizationspec_ref(si=si, customization_spec_name=customization_spec) + customization_spec = salt.utils.vmware.get_customizationspec_ref( + si=si, customization_spec_name=customization_spec + ) clone_spec.customization = customization_spec.spec - elif customization and (devices and 'network' in list(devices.keys())): + elif customization and (devices and "network" in list(devices.keys())): global_ip = vim.vm.customization.GlobalIPSettings() - if 'dns_servers' in list(vm_.keys()): - global_ip.dnsServerList = vm_['dns_servers'] + if "dns_servers" in list(vm_.keys()): + global_ip.dnsServerList = vm_["dns_servers"] - non_hostname_chars = re.compile(r'[^\w-]') + non_hostname_chars = re.compile(r"[^\w-]") if re.search(non_hostname_chars, vm_name): - host_name = re.split(non_hostname_chars, - vm_name, - maxsplit=1)[0] - domain_name = re.split(non_hostname_chars, - vm_name, - maxsplit=1)[-1] + host_name = re.split(non_hostname_chars, vm_name, maxsplit=1)[0] + domain_name = re.split(non_hostname_chars, vm_name, maxsplit=1)[-1] else: host_name = vm_name domain_name = domain - if 'Windows' not in object_ref.config.guestFullName: + if "Windows" not in object_ref.config.guestFullName: identity = vim.vm.customization.LinuxPrep() identity.hostName = vim.vm.customization.FixedName(name=host_name) identity.domain = domain_name @@ -2834,89 +3097,103 @@ def create(vm_): custom_spec = vim.vm.customization.Specification( globalIPSettings=global_ip, identity=identity, - nicSettingMap=specs['nics_map'] + nicSettingMap=specs["nics_map"], ) clone_spec.customization = custom_spec if not template: clone_spec.powerOn = power - log.debug('clone_spec set to:\n%s', pprint.pformat(clone_spec)) + log.debug("clone_spec set to:\n%s", pprint.pformat(clone_spec)) else: config_spec.name = vm_name config_spec.files = vim.vm.FileInfo() - config_spec.files.vmPathName = '[{0}] {1}/{1}.vmx'.format(datastore, vm_name) + config_spec.files.vmPathName = "[{0}] {1}/{1}.vmx".format(datastore, vm_name) config_spec.guestId = guest_id - log.debug('config_spec set to:\n%s', pprint.pformat(config_spec)) + log.debug("config_spec set to:\n%s", pprint.pformat(config_spec)) event_kwargs = vm_.copy() - if event_kwargs.get('password'): - del event_kwargs['password'] + if event_kwargs.get("password"): + del event_kwargs["password"] try: - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), - args=__utils__['cloud.filter_event']('requesting', event_kwargs, list(event_kwargs)), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "requesting", event_kwargs, list(event_kwargs) + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if 'clonefrom' in vm_: - log.info("Creating %s from %s(%s)", vm_['name'], clone_type, vm_['clonefrom']) + if "clonefrom" in vm_: + log.info( + "Creating %s from %s(%s)", vm_["name"], clone_type, vm_["clonefrom"] + ) if datastore and not datastore_ref and datastore_cluster_ref: # datastore cluster has been specified so apply Storage DRS recommendations - pod_spec = vim.storageDrs.PodSelectionSpec(storagePod=datastore_cluster_ref) + pod_spec = vim.storageDrs.PodSelectionSpec( + storagePod=datastore_cluster_ref + ) storage_spec = vim.storageDrs.StoragePlacementSpec( - type='clone', + type="clone", vm=object_ref, podSelectionSpec=pod_spec, cloneSpec=clone_spec, cloneName=vm_name, - folder=folder_ref + folder=folder_ref, ) # get recommended datastores - recommended_datastores = si.content.storageResourceManager.RecommendDatastores(storageSpec=storage_spec) + recommended_datastores = si.content.storageResourceManager.RecommendDatastores( + storageSpec=storage_spec + ) # apply storage DRS recommendations - task = si.content.storageResourceManager.ApplyStorageDrsRecommendation_Task(recommended_datastores.recommendations[0].key) - salt.utils.vmware.wait_for_task(task, vm_name, 'apply storage DRS recommendations', 5, 'info') + task = si.content.storageResourceManager.ApplyStorageDrsRecommendation_Task( + recommended_datastores.recommendations[0].key + ) + salt.utils.vmware.wait_for_task( + task, vm_name, "apply storage DRS recommendations", 5, "info" + ) else: # clone the VM/template task = object_ref.Clone(folder_ref, vm_name, clone_spec) - salt.utils.vmware.wait_for_task(task, vm_name, 'clone', 5, 'info') + salt.utils.vmware.wait_for_task(task, vm_name, "clone", 5, "info") else: - log.info('Creating %s', vm_['name']) + log.info("Creating %s", vm_["name"]) if host: task = folder_ref.CreateVM_Task(config_spec, resourcepool_ref, host_ref) else: task = folder_ref.CreateVM_Task(config_spec, resourcepool_ref) - salt.utils.vmware.wait_for_task(task, vm_name, "create", 15, 'info') + salt.utils.vmware.wait_for_task(task, vm_name, "create", 15, "info") except Exception as exc: # pylint: disable=broad-except - err_msg = 'Error creating {0}: {1}'.format(vm_['name'], exc) + err_msg = "Error creating {0}: {1}".format(vm_["name"], exc) log.error( err_msg, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {'Error': err_msg} + return {"Error": err_msg} - new_vm_ref = salt.utils.vmware.get_mor_by_property(si, vim.VirtualMachine, vm_name, container_ref=container_ref) + new_vm_ref = salt.utils.vmware.get_mor_by_property( + si, vim.VirtualMachine, vm_name, container_ref=container_ref + ) # Find how to power on in CreateVM_Task (if possible), for now this will do try: if not clone_type and power: task = new_vm_ref.PowerOn() - salt.utils.vmware.wait_for_task(task, vm_name, 'power', 5, 'info') + salt.utils.vmware.wait_for_task(task, vm_name, "power", 5, "info") except Exception as exc: # pylint: disable=broad-except - log.info('Powering on the VM threw this exception. Ignoring.') + log.info("Powering on the VM threw this exception. Ignoring.") log.info(exc) # If it a template or if it does not need to be powered on then do not wait for the IP @@ -2927,36 +3204,38 @@ def create(vm_): log.info("[ %s ] IPv4 is: %s", vm_name, ip) # ssh or smb using ip and install salt only if deploy is True if deploy: - vm_['key_filename'] = key_filename + vm_["key_filename"] = key_filename # if specified, prefer ssh_host to the discovered ip address - if 'ssh_host' not in vm_: - vm_['ssh_host'] = ip - log.info("[ %s ] Deploying to %s", vm_name, vm_['ssh_host']) + if "ssh_host" not in vm_: + vm_["ssh_host"] = ip + log.info("[ %s ] Deploying to %s", vm_name, vm_["ssh_host"]) - out = __utils__['cloud.bootstrap'](vm_, __opts__) + out = __utils__["cloud.bootstrap"](vm_, __opts__) - data = show_instance(vm_name, call='action') + data = show_instance(vm_name, call="action") if deploy and isinstance(out, dict): - data['deploy_kwargs'] = out.get('deploy_kwargs', {}) + data["deploy_kwargs"] = out.get("deploy_kwargs", {}) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return data def handle_snapshot(config_spec, object_ref, reloc_spec, template, vm_): - ''' + """ Returns a clone spec for cloning from shapshots :rtype vim.vm.CloneSpec - ''' - if 'snapshot' not in vm_: + """ + if "snapshot" not in vm_: return None allowed_types = [ @@ -2967,30 +3246,31 @@ def handle_snapshot(config_spec, object_ref, reloc_spec, template, vm_): ] clone_spec = get_clonespec_for_valid_snapshot( - config_spec, - object_ref, - reloc_spec, - template, - vm_) + config_spec, object_ref, reloc_spec, template, vm_ + ) if not clone_spec: - raise SaltCloudSystemExit('Invalid disk move type specified' - ' supported types are' - ' {0}'.format(' '.join(allowed_types))) + raise SaltCloudSystemExit( + "Invalid disk move type specified" + " supported types are" + " {0}".format(" ".join(allowed_types)) + ) return clone_spec -def get_clonespec_for_valid_snapshot(config_spec, object_ref, reloc_spec, template, vm_): - ''' +def get_clonespec_for_valid_snapshot( + config_spec, object_ref, reloc_spec, template, vm_ +): + """ return clonespec only if values are valid - ''' + """ moving = True - if QUICK_LINKED_CLONE == vm_['snapshot']['disk_move_type']: + if QUICK_LINKED_CLONE == vm_["snapshot"]["disk_move_type"]: reloc_spec.diskMoveType = QUICK_LINKED_CLONE - elif CURRENT_STATE_LINKED_CLONE == vm_['snapshot']['disk_move_type']: + elif CURRENT_STATE_LINKED_CLONE == vm_["snapshot"]["disk_move_type"]: reloc_spec.diskMoveType = CURRENT_STATE_LINKED_CLONE - elif COPY_ALL_DISKS_FULL_CLONE == vm_['snapshot']['disk_move_type']: + elif COPY_ALL_DISKS_FULL_CLONE == vm_["snapshot"]["disk_move_type"]: reloc_spec.diskMoveType = COPY_ALL_DISKS_FULL_CLONE - elif FLATTEN_DISK_FULL_CLONE == vm_['snapshot']['disk_move_type']: + elif FLATTEN_DISK_FULL_CLONE == vm_["snapshot"]["disk_move_type"]: reloc_spec.diskMoveType = FLATTEN_DISK_FULL_CLONE else: moving = False @@ -3002,26 +3282,22 @@ def get_clonespec_for_valid_snapshot(config_spec, object_ref, reloc_spec, templa def build_clonespec(config_spec, object_ref, reloc_spec, template): - ''' + """ Returns the clone spec - ''' + """ if reloc_spec.diskMoveType == QUICK_LINKED_CLONE: return vim.vm.CloneSpec( template=template, location=reloc_spec, config=config_spec, - snapshot=object_ref.snapshot.currentSnapshot + snapshot=object_ref.snapshot.currentSnapshot, ) - return vim.vm.CloneSpec( - template=template, - location=reloc_spec, - config=config_spec - ) + return vim.vm.CloneSpec(template=template, location=reloc_spec, config=config_spec) def create_datacenter(kwargs=None, call=None): - ''' + """ Create a new data center in this VMware environment CLI Example: @@ -3029,32 +3305,33 @@ def create_datacenter(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_datacenter my-vmware-config name="MyNewDatacenter" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_datacenter function must be called with ' - '-f or --function.' + "The create_datacenter function must be called with " "-f or --function." ) - datacenter_name = kwargs.get('name') if kwargs and 'name' in kwargs else None + datacenter_name = kwargs.get("name") if kwargs and "name" in kwargs else None if not datacenter_name: raise SaltCloudSystemExit( - 'You must specify name of the new datacenter to be created.' + "You must specify name of the new datacenter to be created." ) if not datacenter_name or len(datacenter_name) >= 80: raise SaltCloudSystemExit( - 'The datacenter name must be a non empty string of less than 80 characters.' + "The datacenter name must be a non empty string of less than 80 characters." ) # Get the service instance si = _get_si() # Check if datacenter already exists - datacenter_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter_name) + datacenter_ref = salt.utils.vmware.get_mor_by_property( + si, vim.Datacenter, datacenter_name + ) if datacenter_ref: - return {datacenter_name: 'datacenter already exists'} + return {datacenter_name: "datacenter already exists"} folder = si.content.rootFolder @@ -3064,21 +3341,22 @@ def create_datacenter(kwargs=None, call=None): folder.CreateDatacenter(name=datacenter_name) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating datacenter %s: %s', - datacenter_name, exc, + "Error creating datacenter %s: %s", + datacenter_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False log.debug("Created datacenter %s", datacenter_name) - return {datacenter_name: 'created'} + return {datacenter_name: "created"} return False def create_cluster(kwargs=None, call=None): - ''' + """ Create a new cluster under the specified datacenter in this VMware environment CLI Example: @@ -3086,40 +3364,41 @@ def create_cluster(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_cluster my-vmware-config name="myNewCluster" datacenter="datacenterName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_cluster function must be called with ' - '-f or --function.' + "The create_cluster function must be called with " "-f or --function." ) - cluster_name = kwargs.get('name') if kwargs and 'name' in kwargs else None - datacenter = kwargs.get('datacenter') if kwargs and 'datacenter' in kwargs else None + cluster_name = kwargs.get("name") if kwargs and "name" in kwargs else None + datacenter = kwargs.get("datacenter") if kwargs and "datacenter" in kwargs else None if not cluster_name: raise SaltCloudSystemExit( - 'You must specify name of the new cluster to be created.' + "You must specify name of the new cluster to be created." ) if not datacenter: raise SaltCloudSystemExit( - 'You must specify name of the datacenter where the cluster should be created.' + "You must specify name of the datacenter where the cluster should be created." ) # Get the service instance si = _get_si() if not isinstance(datacenter, vim.Datacenter): - datacenter = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter) + datacenter = salt.utils.vmware.get_mor_by_property( + si, vim.Datacenter, datacenter + ) if not datacenter: - raise SaltCloudSystemExit( - 'The specified datacenter does not exist.' - ) + raise SaltCloudSystemExit("The specified datacenter does not exist.") # Check if cluster already exists - cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.ClusterComputeResource, cluster_name) + cluster_ref = salt.utils.vmware.get_mor_by_property( + si, vim.ClusterComputeResource, cluster_name + ) if cluster_ref: - return {cluster_name: 'cluster already exists'} + return {cluster_name: "cluster already exists"} cluster_spec = vim.cluster.ConfigSpecEx() folder = datacenter.hostFolder @@ -3130,24 +3409,24 @@ def create_cluster(kwargs=None, call=None): folder.CreateClusterEx(name=cluster_name, spec=cluster_spec) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating cluster %s: %s', - cluster_name, exc, + "Error creating cluster %s: %s", + cluster_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False log.debug( - "Created cluster %s under datacenter %s", - cluster_name, datacenter.name + "Created cluster %s under datacenter %s", cluster_name, datacenter.name ) - return {cluster_name: 'created'} + return {cluster_name: "created"} return False def rescan_hba(kwargs=None, call=None): - ''' + """ To rescan a specified HBA or all the HBAs on the Host System CLI Example: @@ -3156,46 +3435,46 @@ def rescan_hba(kwargs=None, call=None): salt-cloud -f rescan_hba my-vmware-config host="hostSystemName" salt-cloud -f rescan_hba my-vmware-config hba="hbaDeviceName" host="hostSystemName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The rescan_hba function must be called with ' - '-f or --function.' + "The rescan_hba function must be called with " "-f or --function." ) - hba = kwargs.get('hba') if kwargs and 'hba' in kwargs else None - host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None + hba = kwargs.get("hba") if kwargs and "hba" in kwargs else None + host_name = kwargs.get("host") if kwargs and "host" in kwargs else None if not host_name: - raise SaltCloudSystemExit( - 'You must specify name of the host system.' - ) + raise SaltCloudSystemExit("You must specify name of the host system.") - host_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.HostSystem, host_name) + host_ref = salt.utils.vmware.get_mor_by_property( + _get_si(), vim.HostSystem, host_name + ) try: if hba: - log.info('Rescanning HBA %s on host %s', hba, host_name) + log.info("Rescanning HBA %s on host %s", hba, host_name) host_ref.configManager.storageSystem.RescanHba(hba) - ret = 'rescanned HBA {0}'.format(hba) + ret = "rescanned HBA {0}".format(hba) else: - log.info('Rescanning all HBAs on host %s', host_name) + log.info("Rescanning all HBAs on host %s", host_name) host_ref.configManager.storageSystem.RescanAllHba() - ret = 'rescanned all HBAs' + ret = "rescanned all HBAs" except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while rescaning HBA on host %s: %s', - host_name, exc, + "Error while rescaning HBA on host %s: %s", + host_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {host_name: 'failed to rescan HBA'} + return {host_name: "failed to rescan HBA"} return {host_name: ret} def upgrade_tools_all(call=None): - ''' + """ To upgrade VMware Tools on all virtual machines present in the specified provider @@ -3210,26 +3489,27 @@ def upgrade_tools_all(call=None): .. code-block:: bash salt-cloud -f upgrade_tools_all my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The upgrade_tools_all function must be called with ' - '-f or --function.' + "The upgrade_tools_all function must be called with " "-f or --function." ) ret = {} vm_properties = ["name"] - vm_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.VirtualMachine, vm_properties) + vm_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.VirtualMachine, vm_properties + ) for vm in vm_list: - ret[vm['name']] = _upg_tools_helper(vm['object']) + ret[vm["name"]] = _upg_tools_helper(vm["object"]) return ret def upgrade_tools(name, reboot=False, call=None): - ''' + """ To upgrade VMware Tools on a specified virtual machine. .. note:: @@ -3244,11 +3524,10 @@ def upgrade_tools(name, reboot=False, call=None): salt-cloud -a upgrade_tools vmname salt-cloud -a upgrade_tools vmname reboot=True - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The upgrade_tools action must be called with ' - '-a or --action.' + "The upgrade_tools action must be called with " "-a or --action." ) vm_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.VirtualMachine, name) @@ -3257,7 +3536,7 @@ def upgrade_tools(name, reboot=False, call=None): def list_hosts_by_cluster(kwargs=None, call=None): - ''' + """ List hosts for each cluster; or hosts for a specified cluster in this VMware environment @@ -3276,34 +3555,34 @@ def list_hosts_by_cluster(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_hosts_by_cluster my-vmware-config cluster="clusterName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_hosts_by_cluster function must be called with ' - '-f or --function.' + "The list_hosts_by_cluster function must be called with " + "-f or --function." ) ret = {} - cluster_name = kwargs.get('cluster') if kwargs and 'cluster' in kwargs else None + cluster_name = kwargs.get("cluster") if kwargs and "cluster" in kwargs else None cluster_properties = ["name"] - cluster_list = salt.utils.vmware.get_mors_with_properties(_get_si(), - vim.ClusterComputeResource, - cluster_properties) + cluster_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.ClusterComputeResource, cluster_properties + ) for cluster in cluster_list: - ret[cluster['name']] = [] - for host in cluster['object'].host: + ret[cluster["name"]] = [] + for host in cluster["object"].host: if isinstance(host, vim.HostSystem): - ret[cluster['name']].append(host.name) - if cluster_name and cluster_name == cluster['name']: - return {'Hosts by Cluster': {cluster_name: ret[cluster_name]}} + ret[cluster["name"]].append(host.name) + if cluster_name and cluster_name == cluster["name"]: + return {"Hosts by Cluster": {cluster_name: ret[cluster_name]}} - return {'Hosts by Cluster': ret} + return {"Hosts by Cluster": ret} def list_clusters_by_datacenter(kwargs=None, call=None): - ''' + """ List clusters for each datacenter; or clusters for a specified datacenter in this VMware environment @@ -3322,32 +3601,36 @@ def list_clusters_by_datacenter(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_clusters_by_datacenter my-vmware-config datacenter="datacenterName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_clusters_by_datacenter function must be called with ' - '-f or --function.' + "The list_clusters_by_datacenter function must be called with " + "-f or --function." ) ret = {} - datacenter_name = kwargs.get('datacenter') if kwargs and 'datacenter' in kwargs else None + datacenter_name = ( + kwargs.get("datacenter") if kwargs and "datacenter" in kwargs else None + ) datacenter_properties = ["name"] - datacenter_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.Datacenter, datacenter_properties) + datacenter_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.Datacenter, datacenter_properties + ) for datacenter in datacenter_list: - ret[datacenter['name']] = [] - for cluster in datacenter['object'].hostFolder.childEntity: + ret[datacenter["name"]] = [] + for cluster in datacenter["object"].hostFolder.childEntity: if isinstance(cluster, vim.ClusterComputeResource): - ret[datacenter['name']].append(cluster.name) - if datacenter_name and datacenter_name == datacenter['name']: - return {'Clusters by Datacenter': {datacenter_name: ret[datacenter_name]}} + ret[datacenter["name"]].append(cluster.name) + if datacenter_name and datacenter_name == datacenter["name"]: + return {"Clusters by Datacenter": {datacenter_name: ret[datacenter_name]}} - return {'Clusters by Datacenter': ret} + return {"Clusters by Datacenter": ret} def list_hosts_by_datacenter(kwargs=None, call=None): - ''' + """ List hosts for each datacenter; or hosts for a specified datacenter in this VMware environment @@ -3366,34 +3649,38 @@ def list_hosts_by_datacenter(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_hosts_by_datacenter my-vmware-config datacenter="datacenterName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_hosts_by_datacenter function must be called with ' - '-f or --function.' + "The list_hosts_by_datacenter function must be called with " + "-f or --function." ) ret = {} - datacenter_name = kwargs.get('datacenter') if kwargs and 'datacenter' in kwargs else None + datacenter_name = ( + kwargs.get("datacenter") if kwargs and "datacenter" in kwargs else None + ) datacenter_properties = ["name"] - datacenter_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.Datacenter, datacenter_properties) + datacenter_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.Datacenter, datacenter_properties + ) for datacenter in datacenter_list: - ret[datacenter['name']] = [] - for cluster in datacenter['object'].hostFolder.childEntity: + ret[datacenter["name"]] = [] + for cluster in datacenter["object"].hostFolder.childEntity: if isinstance(cluster, vim.ClusterComputeResource): for host in cluster.host: if isinstance(host, vim.HostSystem): - ret[datacenter['name']].append(host.name) - if datacenter_name and datacenter_name == datacenter['name']: - return {'Hosts by Datacenter': {datacenter_name: ret[datacenter_name]}} + ret[datacenter["name"]].append(host.name) + if datacenter_name and datacenter_name == datacenter["name"]: + return {"Hosts by Datacenter": {datacenter_name: ret[datacenter_name]}} - return {'Hosts by Datacenter': ret} + return {"Hosts by Datacenter": ret} def list_hbas(kwargs=None, call=None): - ''' + """ List all HBAs for each host system; or all HBAs for a specified host system; or HBAs of specified type for each host system; or HBAs of specified type for a specified host system in this VMware environment @@ -3434,55 +3721,53 @@ def list_hbas(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_hbas my-vmware-config host="hostSystemName" type="HBAtype" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_hbas function must be called with ' - '-f or --function.' + "The list_hbas function must be called with " "-f or --function." ) ret = {} - hba_type = kwargs.get('type').lower() if kwargs and 'type' in kwargs else None - host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None - host_properties = [ - "name", - "config.storageDevice.hostBusAdapter" - ] + hba_type = kwargs.get("type").lower() if kwargs and "type" in kwargs else None + host_name = kwargs.get("host") if kwargs and "host" in kwargs else None + host_properties = ["name", "config.storageDevice.hostBusAdapter"] if hba_type and hba_type not in ["parallel", "block", "iscsi", "fibre"]: raise SaltCloudSystemExit( - 'Specified hba type {0} currently not supported.'.format(hba_type) + "Specified hba type {0} currently not supported.".format(hba_type) ) - host_list = salt.utils.vmware.get_mors_with_properties(_get_si(), vim.HostSystem, host_properties) + host_list = salt.utils.vmware.get_mors_with_properties( + _get_si(), vim.HostSystem, host_properties + ) for host in host_list: - ret[host['name']] = {} - for hba in host['config.storageDevice.hostBusAdapter']: + ret[host["name"]] = {} + for hba in host["config.storageDevice.hostBusAdapter"]: hba_spec = { - 'driver': hba.driver, - 'status': hba.status, - 'type': type(hba).__name__.rsplit(".", 1)[1] + "driver": hba.driver, + "status": hba.status, + "type": type(hba).__name__.rsplit(".", 1)[1], } if hba_type: if isinstance(hba, _get_hba_type(hba_type)): - if hba.model in ret[host['name']]: - ret[host['name']][hba.model][hba.device] = hba_spec + if hba.model in ret[host["name"]]: + ret[host["name"]][hba.model][hba.device] = hba_spec else: - ret[host['name']][hba.model] = {hba.device: hba_spec} + ret[host["name"]][hba.model] = {hba.device: hba_spec} else: - if hba.model in ret[host['name']]: - ret[host['name']][hba.model][hba.device] = hba_spec + if hba.model in ret[host["name"]]: + ret[host["name"]][hba.model][hba.device] = hba_spec else: - ret[host['name']][hba.model] = {hba.device: hba_spec} - if host['name'] == host_name: - return {'HBAs by Host': {host_name: ret[host_name]}} + ret[host["name"]][hba.model] = {hba.device: hba_spec} + if host["name"] == host_name: + return {"HBAs by Host": {host_name: ret[host_name]}} - return {'HBAs by Host': ret} + return {"HBAs by Host": ret} def list_dvs(kwargs=None, call=None): - ''' + """ List all the distributed virtual switches for this VMware environment CLI Example: @@ -3490,18 +3775,17 @@ def list_dvs(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_dvs my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_dvs function must be called with ' - '-f or --function.' + "The list_dvs function must be called with " "-f or --function." ) - return {'Distributed Virtual Switches': salt.utils.vmware.list_dvs(_get_si())} + return {"Distributed Virtual Switches": salt.utils.vmware.list_dvs(_get_si())} def list_vapps(kwargs=None, call=None): - ''' + """ List all the vApps for this VMware environment CLI Example: @@ -3509,18 +3793,17 @@ def list_vapps(kwargs=None, call=None): .. code-block:: bash salt-cloud -f list_vapps my-vmware-config - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The list_vapps function must be called with ' - '-f or --function.' + "The list_vapps function must be called with " "-f or --function." ) - return {'vApps': salt.utils.vmware.list_vapps(_get_si())} + return {"vApps": salt.utils.vmware.list_vapps(_get_si())} def enter_maintenance_mode(kwargs=None, call=None): - ''' + """ To put the specified host system in maintenance mode in this VMware environment CLI Example: @@ -3528,42 +3811,43 @@ def enter_maintenance_mode(kwargs=None, call=None): .. code-block:: bash salt-cloud -f enter_maintenance_mode my-vmware-config host="myHostSystemName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The enter_maintenance_mode function must be called with ' - '-f or --function.' + "The enter_maintenance_mode function must be called with " + "-f or --function." ) - host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None + host_name = kwargs.get("host") if kwargs and "host" in kwargs else None - host_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.HostSystem, host_name) + host_ref = salt.utils.vmware.get_mor_by_property( + _get_si(), vim.HostSystem, host_name + ) if not host_name or not host_ref: - raise SaltCloudSystemExit( - 'You must specify a valid name of the host system.' - ) + raise SaltCloudSystemExit("You must specify a valid name of the host system.") if host_ref.runtime.inMaintenanceMode: - return {host_name: 'already in maintenance mode'} + return {host_name: "already in maintenance mode"} try: task = host_ref.EnterMaintenanceMode(timeout=0, evacuatePoweredOffVms=True) - salt.utils.vmware.wait_for_task(task, host_name, 'enter maintenance mode') + salt.utils.vmware.wait_for_task(task, host_name, "enter maintenance mode") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while moving host system %s in maintenance mode: %s', - host_name, exc, + "Error while moving host system %s in maintenance mode: %s", + host_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {host_name: 'failed to enter maintenance mode'} + return {host_name: "failed to enter maintenance mode"} - return {host_name: 'entered maintenance mode'} + return {host_name: "entered maintenance mode"} def exit_maintenance_mode(kwargs=None, call=None): - ''' + """ To take the specified host system out of maintenance mode in this VMware environment CLI Example: @@ -3571,42 +3855,43 @@ def exit_maintenance_mode(kwargs=None, call=None): .. code-block:: bash salt-cloud -f exit_maintenance_mode my-vmware-config host="myHostSystemName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The exit_maintenance_mode function must be called with ' - '-f or --function.' + "The exit_maintenance_mode function must be called with " + "-f or --function." ) - host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None + host_name = kwargs.get("host") if kwargs and "host" in kwargs else None - host_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.HostSystem, host_name) + host_ref = salt.utils.vmware.get_mor_by_property( + _get_si(), vim.HostSystem, host_name + ) if not host_name or not host_ref: - raise SaltCloudSystemExit( - 'You must specify a valid name of the host system.' - ) + raise SaltCloudSystemExit("You must specify a valid name of the host system.") if not host_ref.runtime.inMaintenanceMode: - return {host_name: 'already not in maintenance mode'} + return {host_name: "already not in maintenance mode"} try: task = host_ref.ExitMaintenanceMode(timeout=0) - salt.utils.vmware.wait_for_task(task, host_name, 'exit maintenance mode') + salt.utils.vmware.wait_for_task(task, host_name, "exit maintenance mode") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while moving host system %s out of maintenance mode: %s', - host_name, exc, + "Error while moving host system %s out of maintenance mode: %s", + host_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {host_name: 'failed to exit maintenance mode'} + return {host_name: "failed to exit maintenance mode"} - return {host_name: 'exited maintenance mode'} + return {host_name: "exited maintenance mode"} def create_folder(kwargs=None, call=None): - ''' + """ Create the specified folder path in this VMware environment .. note:: @@ -3632,31 +3917,32 @@ def create_folder(kwargs=None, call=None): salt-cloud -f create_folder my-vmware-config path="/MyDatacenter/host/MyHostFolder" salt-cloud -f create_folder my-vmware-config path="/MyDatacenter/network/MyNetworkFolder" salt-cloud -f create_folder my-vmware-config path="/MyDatacenter/storage/MyStorageFolder" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_folder function must be called with ' - '-f or --function.' + "The create_folder function must be called with " "-f or --function." ) # Get the service instance object si = _get_si() - folder_path = kwargs.get('path') if kwargs and 'path' in kwargs else None + folder_path = kwargs.get("path") if kwargs and "path" in kwargs else None if not folder_path: - raise SaltCloudSystemExit( - 'You must specify a non empty folder path.' - ) + raise SaltCloudSystemExit("You must specify a non empty folder path.") folder_refs = [] - inventory_path = '/' + inventory_path = "/" path_exists = True # Split the path in a list and loop over it to check for its existence - for index, folder_name in enumerate(os.path.normpath(folder_path.strip('/')).split('/')): + for index, folder_name in enumerate( + os.path.normpath(folder_path.strip("/")).split("/") + ): inventory_path = os.path.join(inventory_path, folder_name) - folder_ref = si.content.searchIndex.FindByInventoryPath(inventoryPath=inventory_path) + folder_ref = si.content.searchIndex.FindByInventoryPath( + inventoryPath=inventory_path + ) if isinstance(folder_ref, vim.Folder): # This is a folder that exists so just append and skip it log.debug("Path %s/ exists in the inventory", inventory_path) @@ -3669,21 +3955,23 @@ def create_folder(kwargs=None, call=None): path_exists = False if not folder_refs: # If this is the first folder, create it under the rootFolder - log.debug("Creating folder %s under rootFolder in the inventory", folder_name) + log.debug( + "Creating folder %s under rootFolder in the inventory", folder_name + ) folder_refs.append(si.content.rootFolder.CreateFolder(folder_name)) else: # Create the folder under the parent folder log.debug("Creating path %s/ in the inventory", inventory_path) - folder_refs.append(folder_refs[index-1].CreateFolder(folder_name)) + folder_refs.append(folder_refs[index - 1].CreateFolder(folder_name)) if path_exists: - return {inventory_path: 'specfied path already exists'} + return {inventory_path: "specfied path already exists"} - return {inventory_path: 'created the specified path'} + return {inventory_path: "created the specified path"} def create_snapshot(name, kwargs=None, call=None): - ''' + """ Create a snapshot of the specified virtual machine in this VMware environment @@ -3710,58 +3998,67 @@ def create_snapshot(name, kwargs=None, call=None): salt-cloud -a create_snapshot vmname snapshot_name="mySnapshot" salt-cloud -a create_snapshot vmname snapshot_name="mySnapshot" [description="My snapshot"] [memdump=False] [quiesce=True] - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The create_snapshot action must be called with ' - '-a or --action.' + "The create_snapshot action must be called with " "-a or --action." ) if kwargs is None: kwargs = {} - snapshot_name = kwargs.get('snapshot_name') if kwargs and 'snapshot_name' in kwargs else None + snapshot_name = ( + kwargs.get("snapshot_name") if kwargs and "snapshot_name" in kwargs else None + ) if not snapshot_name: raise SaltCloudSystemExit( - 'You must specify snapshot name for the snapshot to be created.' + "You must specify snapshot name for the snapshot to be created." ) - memdump = _str_to_bool(kwargs.get('memdump', True)) - quiesce = _str_to_bool(kwargs.get('quiesce', False)) + memdump = _str_to_bool(kwargs.get("memdump", True)) + quiesce = _str_to_bool(kwargs.get("quiesce", False)) vm_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.VirtualMachine, name) if vm_ref.summary.runtime.powerState != "poweredOn": - log.debug('VM %s is not powered on. Setting both memdump and quiesce to False', name) + log.debug( + "VM %s is not powered on. Setting both memdump and quiesce to False", name + ) memdump = False quiesce = False if memdump and quiesce: # Either memdump or quiesce should be set to True - log.warning('You can only set either memdump or quiesce to True. Setting quiesce=False') + log.warning( + "You can only set either memdump or quiesce to True. Setting quiesce=False" + ) quiesce = False - desc = kwargs.get('description') if 'description' in kwargs else '' + desc = kwargs.get("description") if "description" in kwargs else "" try: task = vm_ref.CreateSnapshot(snapshot_name, desc, memdump, quiesce) - salt.utils.vmware.wait_for_task(task, name, 'create snapshot', 5, 'info') + salt.utils.vmware.wait_for_task(task, name, "create snapshot", 5, "info") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while creating snapshot of %s: %s', - name, exc, + "Error while creating snapshot of %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to create snapshot' + return "failed to create snapshot" - return {'Snapshot created successfully': _get_snapshots(vm_ref.snapshot.rootSnapshotList, - vm_ref.snapshot.currentSnapshot)} + return { + "Snapshot created successfully": _get_snapshots( + vm_ref.snapshot.rootSnapshotList, vm_ref.snapshot.currentSnapshot + ) + } def revert_to_snapshot(name, kwargs=None, call=None): - ''' + """ Revert virtual machine to its current snapshot. If no snapshot exists, the state of the virtual machine remains unchanged @@ -3783,25 +4080,26 @@ def revert_to_snapshot(name, kwargs=None, call=None): salt-cloud -a revert_to_snapshot vmame [power_off=True] salt-cloud -a revert_to_snapshot vmame snapshot_name="selectedSnapshot" [power_off=True] - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The revert_to_snapshot action must be called with ' - '-a or --action.' + "The revert_to_snapshot action must be called with " "-a or --action." ) if kwargs is None: kwargs = {} - snapshot_name = kwargs.get('snapshot_name') if kwargs and 'snapshot_name' in kwargs else None + snapshot_name = ( + kwargs.get("snapshot_name") if kwargs and "snapshot_name" in kwargs else None + ) - suppress_power_on = _str_to_bool(kwargs.get('power_off', False)) + suppress_power_on = _str_to_bool(kwargs.get("power_off", False)) vm_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.VirtualMachine, name) if not vm_ref.rootSnapshot: - log.error('VM %s does not contain any current snapshots', name) - return 'revert failed' + log.error("VM %s does not contain any current snapshots", name) + return "revert failed" msg = "reverted to current snapshot" @@ -3814,25 +4112,26 @@ def revert_to_snapshot(name, kwargs=None, call=None): msg = "reverted to snapshot {0}".format(snapshot_name) snapshot_ref = _get_snapshot_ref_by_name(vm_ref, snapshot_name) if snapshot_ref is None: - return 'specified snapshot \'{0}\' does not exist'.format(snapshot_name) + return "specified snapshot '{0}' does not exist".format(snapshot_name) task = snapshot_ref.snapshot.Revert(suppressPowerOn=suppress_power_on) - salt.utils.vmware.wait_for_task(task, name, 'revert to snapshot', 5, 'info') + salt.utils.vmware.wait_for_task(task, name, "revert to snapshot", 5, "info") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while reverting VM %s to snapshot: %s', - name, exc, + "Error while reverting VM %s to snapshot: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'revert failed' + return "revert failed" return msg def remove_snapshot(name, kwargs=None, call=None): - ''' + """ Remove a snapshot of the specified virtual machine in this VMware environment CLI Example: @@ -3841,56 +4140,60 @@ def remove_snapshot(name, kwargs=None, call=None): salt-cloud -a remove_snapshot vmname snapshot_name="mySnapshot" salt-cloud -a remove_snapshot vmname snapshot_name="mySnapshot" [remove_children="True"] - ''' + """ - if call != 'action': + if call != "action": raise SaltCloudSystemExit( - 'The create_snapshot action must be called with ' - '-a or --action.' + "The create_snapshot action must be called with " "-a or --action." ) if kwargs is None: kwargs = {} - snapshot_name = kwargs.get('snapshot_name') if kwargs and 'snapshot_name' in kwargs else None - remove_children = _str_to_bool(kwargs.get('remove_children', False)) + snapshot_name = ( + kwargs.get("snapshot_name") if kwargs and "snapshot_name" in kwargs else None + ) + remove_children = _str_to_bool(kwargs.get("remove_children", False)) if not snapshot_name: raise SaltCloudSystemExit( - 'You must specify snapshot name for the snapshot to be deleted.' + "You must specify snapshot name for the snapshot to be deleted." ) vm_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.VirtualMachine, name) if not _get_snapshot_ref_by_name(vm_ref, snapshot_name): raise SaltCloudSystemExit( - 'Сould not find the snapshot with the specified name.' + "Сould not find the snapshot with the specified name." ) try: snap_obj = _get_snapshot_ref_by_name(vm_ref, snapshot_name).snapshot task = snap_obj.RemoveSnapshot_Task(remove_children) - salt.utils.vmware.wait_for_task(task, name, 'remove snapshot', 5, 'info') + salt.utils.vmware.wait_for_task(task, name, "remove snapshot", 5, "info") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while removing snapshot of %s: %s', - name, exc, + "Error while removing snapshot of %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to remove snapshot' + return "failed to remove snapshot" if vm_ref.snapshot: - return {'Snapshot removed successfully': - _get_snapshots(vm_ref.snapshot.rootSnapshotList, - vm_ref.snapshot.currentSnapshot)} + return { + "Snapshot removed successfully": _get_snapshots( + vm_ref.snapshot.rootSnapshotList, vm_ref.snapshot.currentSnapshot + ) + } - return 'Snapshots removed successfully' + return "Snapshots removed successfully" def remove_all_snapshots(name, kwargs=None, call=None): - ''' + """ Remove all the snapshots present for the specified virtual machine. .. note:: @@ -3905,34 +4208,32 @@ def remove_all_snapshots(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a remove_all_snapshots vmname [merge_snapshots=False] - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The remove_all_snapshots action must be called with ' - '-a or --action.' + "The remove_all_snapshots action must be called with " "-a or --action." ) - vm_ref = salt.utils.vmware.get_mor_by_property(_get_si(), - vim.VirtualMachine, - name) + vm_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.VirtualMachine, name) try: task = vm_ref.RemoveAllSnapshots() - salt.utils.vmware.wait_for_task(task, name, 'remove snapshots', 5, 'info') + salt.utils.vmware.wait_for_task(task, name, "remove snapshots", 5, "info") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while removing snapshots on VM %s: %s', - name, exc, + "Error while removing snapshots on VM %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'Failed to remove snapshots' + return "Failed to remove snapshots" - return 'Removed all snapshots' + return "Removed all snapshots" def convert_to_template(name, kwargs=None, call=None): - ''' + """ Convert the specified virtual machine to template. CLI Example: @@ -3940,38 +4241,34 @@ def convert_to_template(name, kwargs=None, call=None): .. code-block:: bash salt-cloud -a convert_to_template vmname - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The convert_to_template action must be called with ' - '-a or --action.' + "The convert_to_template action must be called with " "-a or --action." ) vm_ref = salt.utils.vmware.get_mor_by_property(_get_si(), vim.VirtualMachine, name) if vm_ref.config.template: - raise SaltCloudSystemExit( - '{0} already a template'.format( - name - ) - ) + raise SaltCloudSystemExit("{0} already a template".format(name)) try: vm_ref.MarkAsTemplate() except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while converting VM to template %s: %s', - name, exc, + "Error while converting VM to template %s: %s", + name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return 'failed to convert to teamplate' + return "failed to convert to teamplate" - return '{0} converted to template'.format(name) + return "{0} converted to template".format(name) def add_host(kwargs=None, call=None): - ''' + """ Add a host system to the specified cluster or datacenter in this VMware environment .. note:: @@ -4009,135 +4306,143 @@ def add_host(kwargs=None, call=None): salt-cloud -f add_host my-vmware-config host="myHostSystemName" cluster="myClusterName" salt-cloud -f add_host my-vmware-config host="myHostSystemName" datacenter="myDatacenterName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The add_host function must be called with ' - '-f or --function.' + "The add_host function must be called with " "-f or --function." ) - host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None - cluster_name = kwargs.get('cluster') if kwargs and 'cluster' in kwargs else None - datacenter_name = kwargs.get('datacenter') if kwargs and 'datacenter' in kwargs else None + host_name = kwargs.get("host") if kwargs and "host" in kwargs else None + cluster_name = kwargs.get("cluster") if kwargs and "cluster" in kwargs else None + datacenter_name = ( + kwargs.get("datacenter") if kwargs and "datacenter" in kwargs else None + ) host_user = config.get_cloud_config_value( - 'esxi_host_user', get_configured_provider(), __opts__, search_global=False + "esxi_host_user", get_configured_provider(), __opts__, search_global=False ) host_password = config.get_cloud_config_value( - 'esxi_host_password', get_configured_provider(), __opts__, search_global=False + "esxi_host_password", get_configured_provider(), __opts__, search_global=False ) host_ssl_thumbprint = config.get_cloud_config_value( - 'esxi_host_ssl_thumbprint', get_configured_provider(), __opts__, search_global=False + "esxi_host_ssl_thumbprint", + get_configured_provider(), + __opts__, + search_global=False, ) if not host_user: raise SaltCloudSystemExit( - 'You must specify the ESXi host username in your providers config.' + "You must specify the ESXi host username in your providers config." ) if not host_password: raise SaltCloudSystemExit( - 'You must specify the ESXi host password in your providers config.' + "You must specify the ESXi host password in your providers config." ) if not host_name: raise SaltCloudSystemExit( - 'You must specify either the IP or DNS name of the host system.' + "You must specify either the IP or DNS name of the host system." ) - if (cluster_name and datacenter_name) or not(cluster_name or datacenter_name): + if (cluster_name and datacenter_name) or not (cluster_name or datacenter_name): raise SaltCloudSystemExit( - 'You must specify either the cluster name or the datacenter name.' + "You must specify either the cluster name or the datacenter name." ) # Get the service instance si = _get_si() if cluster_name: - cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.ClusterComputeResource, cluster_name) + cluster_ref = salt.utils.vmware.get_mor_by_property( + si, vim.ClusterComputeResource, cluster_name + ) if not cluster_ref: - raise SaltCloudSystemExit( - 'Specified cluster does not exist.' - ) + raise SaltCloudSystemExit("Specified cluster does not exist.") if datacenter_name: - datacenter_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter_name) + datacenter_ref = salt.utils.vmware.get_mor_by_property( + si, vim.Datacenter, datacenter_name + ) if not datacenter_ref: - raise SaltCloudSystemExit( - 'Specified datacenter does not exist.' - ) + raise SaltCloudSystemExit("Specified datacenter does not exist.") spec = vim.host.ConnectSpec( - hostName=host_name, - userName=host_user, - password=host_password, + hostName=host_name, userName=host_user, password=host_password, ) if host_ssl_thumbprint: spec.sslThumbprint = host_ssl_thumbprint else: - log.warning('SSL thumbprint has not been specified in provider configuration') + log.warning("SSL thumbprint has not been specified in provider configuration") # This smells like a not-so-good idea. A plenty of VMWare VCenters # do not listen to the default port 443. try: - log.debug('Trying to get the SSL thumbprint directly from the host system') - p1 = subprocess.Popen(('echo', '-n'), - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - p2 = subprocess.Popen(('openssl', - 's_client', - '-connect', - '{0}:443'.format(host_name)), - stdin=p1.stdout, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - p3 = subprocess.Popen(('openssl', - 'x509', - '-noout', - '-fingerprint', - '-sha1'), - stdin=p2.stdout, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + log.debug("Trying to get the SSL thumbprint directly from the host system") + p1 = subprocess.Popen( + ("echo", "-n"), stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + p2 = subprocess.Popen( + ("openssl", "s_client", "-connect", "{0}:443".format(host_name)), + stdin=p1.stdout, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + p3 = subprocess.Popen( + ("openssl", "x509", "-noout", "-fingerprint", "-sha1"), + stdin=p2.stdout, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) out = salt.utils.stringutils.to_str(p3.stdout.read()) - ssl_thumbprint = out.split('=')[-1].strip() - log.debug('SSL thumbprint received from the host system: %s', ssl_thumbprint) + ssl_thumbprint = out.split("=")[-1].strip() + log.debug( + "SSL thumbprint received from the host system: %s", ssl_thumbprint + ) spec.sslThumbprint = ssl_thumbprint except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while trying to get SSL thumbprint of host %s: %s', - host_name, exc, + "Error while trying to get SSL thumbprint of host %s: %s", + host_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {host_name: 'failed to add host'} + return {host_name: "failed to add host"} try: if cluster_name: task = cluster_ref.AddHost(spec=spec, asConnected=True) - ret = 'added host system to cluster {0}'.format(cluster_name) + ret = "added host system to cluster {0}".format(cluster_name) if datacenter_name: - task = datacenter_ref.hostFolder.AddStandaloneHost(spec=spec, addConnected=True) - ret = 'added host system to datacenter {0}'.format(datacenter_name) - salt.utils.vmware.wait_for_task(task, host_name, 'add host system', 5, 'info') + task = datacenter_ref.hostFolder.AddStandaloneHost( + spec=spec, addConnected=True + ) + ret = "added host system to datacenter {0}".format(datacenter_name) + salt.utils.vmware.wait_for_task(task, host_name, "add host system", 5, "info") except Exception as exc: # pylint: disable=broad-except if isinstance(exc, vim.fault.SSLVerifyFault): - log.error('Authenticity of the host\'s SSL certificate is not verified') - log.info('Try again after setting the esxi_host_ssl_thumbprint ' - 'to %s in provider configuration', spec.sslThumbprint) + log.error("Authenticity of the host's SSL certificate is not verified") + log.info( + "Try again after setting the esxi_host_ssl_thumbprint " + "to %s in provider configuration", + spec.sslThumbprint, + ) log.error( - 'Error while adding host %s: %s', - host_name, exc, + "Error while adding host %s: %s", + host_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {host_name: 'failed to add host'} + return {host_name: "failed to add host"} return {host_name: ret} def remove_host(kwargs=None, call=None): - ''' + """ Remove the specified host system from this VMware environment CLI Example: @@ -4145,28 +4450,23 @@ def remove_host(kwargs=None, call=None): .. code-block:: bash salt-cloud -f remove_host my-vmware-config host="myHostSystemName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The remove_host function must be called with ' - '-f or --function.' + "The remove_host function must be called with " "-f or --function." ) - host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None + host_name = kwargs.get("host") if kwargs and "host" in kwargs else None if not host_name: - raise SaltCloudSystemExit( - 'You must specify name of the host system.' - ) + raise SaltCloudSystemExit("You must specify name of the host system.") # Get the service instance si = _get_si() host_ref = salt.utils.vmware.get_mor_by_property(si, vim.HostSystem, host_name) if not host_ref: - raise SaltCloudSystemExit( - 'Specified host system does not exist.' - ) + raise SaltCloudSystemExit("Specified host system does not exist.") try: if isinstance(host_ref.parent, vim.ClusterComputeResource): @@ -4175,21 +4475,24 @@ def remove_host(kwargs=None, call=None): else: # This is a standalone host system task = host_ref.parent.Destroy_Task() - salt.utils.vmware.wait_for_task(task, host_name, 'remove host', log_level='info') + salt.utils.vmware.wait_for_task( + task, host_name, "remove host", log_level="info" + ) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while removing host %s: %s', - host_name, exc, + "Error while removing host %s: %s", + host_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {host_name: 'failed to remove host'} + return {host_name: "failed to remove host"} - return {host_name: 'removed host from vcenter'} + return {host_name: "removed host from vcenter"} def connect_host(kwargs=None, call=None): - ''' + """ Connect the specified host system in this VMware environment CLI Example: @@ -4197,49 +4500,45 @@ def connect_host(kwargs=None, call=None): .. code-block:: bash salt-cloud -f connect_host my-vmware-config host="myHostSystemName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The connect_host function must be called with ' - '-f or --function.' + "The connect_host function must be called with " "-f or --function." ) - host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None + host_name = kwargs.get("host") if kwargs and "host" in kwargs else None if not host_name: - raise SaltCloudSystemExit( - 'You must specify name of the host system.' - ) + raise SaltCloudSystemExit("You must specify name of the host system.") # Get the service instance si = _get_si() host_ref = salt.utils.vmware.get_mor_by_property(si, vim.HostSystem, host_name) if not host_ref: - raise SaltCloudSystemExit( - 'Specified host system does not exist.' - ) + raise SaltCloudSystemExit("Specified host system does not exist.") - if host_ref.runtime.connectionState == 'connected': - return {host_name: 'host system already connected'} + if host_ref.runtime.connectionState == "connected": + return {host_name: "host system already connected"} try: task = host_ref.ReconnectHost_Task() - salt.utils.vmware.wait_for_task(task, host_name, 'connect host', 5, 'info') + salt.utils.vmware.wait_for_task(task, host_name, "connect host", 5, "info") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while connecting host %s: %s', - host_name, exc, + "Error while connecting host %s: %s", + host_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {host_name: 'failed to connect host'} + return {host_name: "failed to connect host"} - return {host_name: 'connected host'} + return {host_name: "connected host"} def disconnect_host(kwargs=None, call=None): - ''' + """ Disconnect the specified host system in this VMware environment CLI Example: @@ -4247,49 +4546,47 @@ def disconnect_host(kwargs=None, call=None): .. code-block:: bash salt-cloud -f disconnect_host my-vmware-config host="myHostSystemName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The disconnect_host function must be called with ' - '-f or --function.' + "The disconnect_host function must be called with " "-f or --function." ) - host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None + host_name = kwargs.get("host") if kwargs and "host" in kwargs else None if not host_name: - raise SaltCloudSystemExit( - 'You must specify name of the host system.' - ) + raise SaltCloudSystemExit("You must specify name of the host system.") # Get the service instance si = _get_si() host_ref = salt.utils.vmware.get_mor_by_property(si, vim.HostSystem, host_name) if not host_ref: - raise SaltCloudSystemExit( - 'Specified host system does not exist.' - ) + raise SaltCloudSystemExit("Specified host system does not exist.") - if host_ref.runtime.connectionState == 'disconnected': - return {host_name: 'host system already disconnected'} + if host_ref.runtime.connectionState == "disconnected": + return {host_name: "host system already disconnected"} try: task = host_ref.DisconnectHost_Task() - salt.utils.vmware.wait_for_task(task, host_name, 'disconnect host', log_level='info') + salt.utils.vmware.wait_for_task( + task, host_name, "disconnect host", log_level="info" + ) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while disconnecting host %s: %s', - host_name, exc, + "Error while disconnecting host %s: %s", + host_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {host_name: 'failed to disconnect host'} + return {host_name: "failed to disconnect host"} - return {host_name: 'disconnected host'} + return {host_name: "disconnected host"} def reboot_host(kwargs=None, call=None): - ''' + """ Reboot the specified host system in this VMware environment .. note:: @@ -4303,64 +4600,58 @@ def reboot_host(kwargs=None, call=None): .. code-block:: bash salt-cloud -f reboot_host my-vmware-config host="myHostSystemName" [force=True] - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The reboot_host function must be called with ' - '-f or --function.' + "The reboot_host function must be called with " "-f or --function." ) - host_name = kwargs.get('host') if kwargs and 'host' in kwargs else None - force = _str_to_bool(kwargs.get('force')) if kwargs and 'force' in kwargs else False + host_name = kwargs.get("host") if kwargs and "host" in kwargs else None + force = _str_to_bool(kwargs.get("force")) if kwargs and "force" in kwargs else False if not host_name: - raise SaltCloudSystemExit( - 'You must specify name of the host system.' - ) + raise SaltCloudSystemExit("You must specify name of the host system.") # Get the service instance si = _get_si() host_ref = salt.utils.vmware.get_mor_by_property(si, vim.HostSystem, host_name) if not host_ref: - raise SaltCloudSystemExit( - 'Specified host system does not exist.' - ) + raise SaltCloudSystemExit("Specified host system does not exist.") - if host_ref.runtime.connectionState == 'notResponding': + if host_ref.runtime.connectionState == "notResponding": raise SaltCloudSystemExit( - 'Specified host system cannot be rebooted in it\'s current state (not responding).' + "Specified host system cannot be rebooted in it's current state (not responding)." ) if not host_ref.capability.rebootSupported: - raise SaltCloudSystemExit( - 'Specified host system does not support reboot.' - ) + raise SaltCloudSystemExit("Specified host system does not support reboot.") if not host_ref.runtime.inMaintenanceMode: raise SaltCloudSystemExit( - 'Specified host system is not in maintenance mode. Specify force=True to ' - 'force reboot even if there are virtual machines running or other operations ' - 'in progress.' + "Specified host system is not in maintenance mode. Specify force=True to " + "force reboot even if there are virtual machines running or other operations " + "in progress." ) try: host_ref.RebootHost_Task(force) - _wait_for_host(host_ref, "reboot", 10, 'info') + _wait_for_host(host_ref, "reboot", 10, "info") except Exception as exc: # pylint: disable=broad-except log.error( - 'Error while rebooting host %s: %s', - host_name, exc, + "Error while rebooting host %s: %s", + host_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - return {host_name: 'failed to reboot host'} + return {host_name: "failed to reboot host"} - return {host_name: 'rebooted host'} + return {host_name: "rebooted host"} def create_datastore_cluster(kwargs=None, call=None): - ''' + """ Create a new datastore cluster for the specified datacenter in this VMware environment CLI Example: @@ -4368,54 +4659,59 @@ def create_datastore_cluster(kwargs=None, call=None): .. code-block:: bash salt-cloud -f create_datastore_cluster my-vmware-config name="datastoreClusterName" datacenter="datacenterName" - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'The create_datastore_cluster function must be called with ' - '-f or --function.' + "The create_datastore_cluster function must be called with " + "-f or --function." ) - datastore_cluster_name = kwargs.get('name') if kwargs and 'name' in kwargs else None - datacenter_name = kwargs.get('datacenter') if kwargs and 'datacenter' in kwargs else None + datastore_cluster_name = kwargs.get("name") if kwargs and "name" in kwargs else None + datacenter_name = ( + kwargs.get("datacenter") if kwargs and "datacenter" in kwargs else None + ) if not datastore_cluster_name: raise SaltCloudSystemExit( - 'You must specify name of the new datastore cluster to be created.' + "You must specify name of the new datastore cluster to be created." ) if not datastore_cluster_name or len(datastore_cluster_name) >= 80: raise SaltCloudSystemExit( - 'The datastore cluster name must be a non empty string of less than 80 characters.' + "The datastore cluster name must be a non empty string of less than 80 characters." ) if not datacenter_name: raise SaltCloudSystemExit( - 'You must specify name of the datacenter where the datastore cluster should be created.' + "You must specify name of the datacenter where the datastore cluster should be created." ) # Get the service instance si = _get_si() # Check if datastore cluster already exists - datastore_cluster_ref = salt.utils.vmware.get_mor_by_property(si, vim.StoragePod, datastore_cluster_name) + datastore_cluster_ref = salt.utils.vmware.get_mor_by_property( + si, vim.StoragePod, datastore_cluster_name + ) if datastore_cluster_ref: - return {datastore_cluster_name: 'datastore cluster already exists'} + return {datastore_cluster_name: "datastore cluster already exists"} - datacenter_ref = salt.utils.vmware.get_mor_by_property(si, vim.Datacenter, datacenter_name) + datacenter_ref = salt.utils.vmware.get_mor_by_property( + si, vim.Datacenter, datacenter_name + ) if not datacenter_ref: - raise SaltCloudSystemExit( - 'The specified datacenter does not exist.' - ) + raise SaltCloudSystemExit("The specified datacenter does not exist.") try: datacenter_ref.datastoreFolder.CreateStoragePod(name=datastore_cluster_name) except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating datastore cluster %s: %s', - datastore_cluster_name, exc, + "Error creating datastore cluster %s: %s", + datastore_cluster_name, + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) return False - return {datastore_cluster_name: 'created'} + return {datastore_cluster_name: "created"} diff --git a/salt/cloud/clouds/vultrpy.py b/salt/cloud/clouds/vultrpy.py index 1c474bb0722..0d005f7a22e 100644 --- a/salt/cloud/clouds/vultrpy.py +++ b/salt/cloud/clouds/vultrpy.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Vultr Cloud Module using python-vultr bindings ============================================== @@ -51,35 +51,33 @@ that startup script in a profile like so: size: 13 startup_script_id: 493234 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import pprint + import logging +import pprint import time # Import salt libs import salt.config as config +from salt.exceptions import SaltCloudConfigError, SaltCloudSystemExit from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode # pylint: disable=E0611 -from salt.exceptions import ( - SaltCloudConfigError, - SaltCloudSystemExit -) +from salt.ext.six.moves.urllib.parse import urlencode as _urlencode # Get logging started log = logging.getLogger(__name__) -__virtualname__ = 'vultr' +__virtualname__ = "vultr" DETAILS = {} def __virtual__(): - ''' + """ Set up the Vultr functions and check for configurations - ''' + """ if get_configured_provider() is False: return False @@ -87,171 +85,171 @@ def __virtual__(): def get_configured_provider(): - ''' + """ Return the first configured instance - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or 'vultr', - ('api_key',) + __opts__, __active_provider_name__ or "vultr", ("api_key",) ) def _cache_provider_details(conn=None): - ''' + """ Provide a place to hang onto results of --list-[locations|sizes|images] so we don't have to go out to the API and get them every time. - ''' - DETAILS['avail_locations'] = {} - DETAILS['avail_sizes'] = {} - DETAILS['avail_images'] = {} + """ + DETAILS["avail_locations"] = {} + DETAILS["avail_sizes"] = {} + DETAILS["avail_images"] = {} locations = avail_locations(conn) images = avail_images(conn) sizes = avail_sizes(conn) for key, location in six.iteritems(locations): - DETAILS['avail_locations'][location['name']] = location - DETAILS['avail_locations'][key] = location + DETAILS["avail_locations"][location["name"]] = location + DETAILS["avail_locations"][key] = location for key, image in six.iteritems(images): - DETAILS['avail_images'][image['name']] = image - DETAILS['avail_images'][key] = image + DETAILS["avail_images"][image["name"]] = image + DETAILS["avail_images"][key] = image for key, vm_size in six.iteritems(sizes): - DETAILS['avail_sizes'][vm_size['name']] = vm_size - DETAILS['avail_sizes'][key] = vm_size + DETAILS["avail_sizes"][vm_size["name"]] = vm_size + DETAILS["avail_sizes"][key] = vm_size def avail_locations(conn=None): - ''' + """ return available datacenter locations - ''' - return _query('regions/list') + """ + return _query("regions/list") def avail_scripts(conn=None): - ''' + """ return available startup scripts - ''' - return _query('startupscript/list') + """ + return _query("startupscript/list") def list_scripts(conn=None, call=None): - ''' + """ return list of Startup Scripts - ''' + """ return avail_scripts() def avail_sizes(conn=None): - ''' + """ Return available sizes ("plans" in VultrSpeak) - ''' - return _query('plans/list') + """ + return _query("plans/list") def avail_images(conn=None): - ''' + """ Return available images - ''' - return _query('os/list') + """ + return _query("os/list") def list_nodes(**kwargs): - ''' + """ Return basic data on nodes - ''' + """ ret = {} nodes = list_nodes_full() for node in nodes: ret[node] = {} - for prop in 'id', 'image', 'size', 'state', 'private_ips', 'public_ips': + for prop in "id", "image", "size", "state", "private_ips", "public_ips": ret[node][prop] = nodes[node][prop] return ret def list_nodes_full(**kwargs): - ''' + """ Return all data on nodes - ''' - nodes = _query('server/list') + """ + nodes = _query("server/list") ret = {} for node in nodes: - name = nodes[node]['label'] + name = nodes[node]["label"] ret[name] = nodes[node].copy() - ret[name]['id'] = node - ret[name]['image'] = nodes[node]['os'] - ret[name]['size'] = nodes[node]['VPSPLANID'] - ret[name]['state'] = nodes[node]['status'] - ret[name]['private_ips'] = nodes[node]['internal_ip'] - ret[name]['public_ips'] = nodes[node]['main_ip'] + ret[name]["id"] = node + ret[name]["image"] = nodes[node]["os"] + ret[name]["size"] = nodes[node]["VPSPLANID"] + ret[name]["state"] = nodes[node]["status"] + ret[name]["private_ips"] = nodes[node]["internal_ip"] + ret[name]["public_ips"] = nodes[node]["main_ip"] return ret def list_nodes_select(conn=None, call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' - return __utils__['cloud.list_nodes_select']( - list_nodes_full(), __opts__['query.selection'], call, + """ + return __utils__["cloud.list_nodes_select"]( + list_nodes_full(), __opts__["query.selection"], call, ) def destroy(name): - ''' + """ Remove a node from Vultr - ''' - node = show_instance(name, call='action') - params = {'SUBID': node['SUBID']} - result = _query('server/destroy', method='POST', decode=False, data=_urlencode(params)) + """ + node = show_instance(name, call="action") + params = {"SUBID": node["SUBID"]} + result = _query( + "server/destroy", method="POST", decode=False, data=_urlencode(params) + ) # The return of a destroy call is empty in the case of a success. # Errors are only indicated via HTTP status code. Status code 200 # effetively therefore means "success". - if result.get('body') == '' and result.get('text') == '': + if result.get("body") == "" and result.get("text") == "": return True return result def stop(*args, **kwargs): - ''' + """ Execute a "stop" action on a VM - ''' - return _query('server/halt') + """ + return _query("server/halt") def start(*args, **kwargs): - ''' + """ Execute a "start" action on a VM - ''' - return _query('server/start') + """ + return _query("server/start") def show_instance(name, call=None): - ''' + """ Show the details from the provider concerning an instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) nodes = list_nodes_full() # Find under which cloud service the name is listed, if any if name not in nodes: return {} - __utils__['cloud.cache_node'](nodes[name], __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](nodes[name], __active_provider_name__, __opts__) return nodes[name] def _lookup_vultrid(which_key, availkey, keyname): - ''' + """ Helper function to retrieve a Vultr ID - ''' + """ if DETAILS == {}: _cache_provider_details() @@ -263,188 +261,205 @@ def _lookup_vultrid(which_key, availkey, keyname): def create(vm_): - ''' + """ Create a single VM from a data dict - ''' - if 'driver' not in vm_: - vm_['driver'] = vm_['provider'] + """ + if "driver" not in vm_: + vm_["driver"] = vm_["provider"] private_networking = config.get_cloud_config_value( - 'enable_private_network', vm_, __opts__, search_global=False, default=False, + "enable_private_network", vm_, __opts__, search_global=False, default=False, ) startup_script = config.get_cloud_config_value( - 'startup_script_id', vm_, __opts__, search_global=False, default=None, + "startup_script_id", vm_, __opts__, search_global=False, default=None, ) if startup_script and str(startup_script) not in avail_scripts(): - log.error('Your Vultr account does not have a startup script with ID %s', str(startup_script)) + log.error( + "Your Vultr account does not have a startup script with ID %s", + str(startup_script), + ) return False if private_networking is not None: if not isinstance(private_networking, bool): - raise SaltCloudConfigError("'private_networking' should be a boolean value.") + raise SaltCloudConfigError( + "'private_networking' should be a boolean value." + ) if private_networking is True: - enable_private_network = 'yes' + enable_private_network = "yes" else: - enable_private_network = 'no' + enable_private_network = "no" - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(vm_['name']), - args=__utils__['cloud.filter_event']('creating', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "creating", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - osid = _lookup_vultrid(vm_['image'], 'avail_images', 'OSID') + osid = _lookup_vultrid(vm_["image"], "avail_images", "OSID") if not osid: - log.error('Vultr does not have an image with id or name %s', vm_['image']) + log.error("Vultr does not have an image with id or name %s", vm_["image"]) return False - vpsplanid = _lookup_vultrid(vm_['size'], 'avail_sizes', 'VPSPLANID') + vpsplanid = _lookup_vultrid(vm_["size"], "avail_sizes", "VPSPLANID") if not vpsplanid: - log.error('Vultr does not have a size with id or name %s', vm_['size']) + log.error("Vultr does not have a size with id or name %s", vm_["size"]) return False - dcid = _lookup_vultrid(vm_['location'], 'avail_locations', 'DCID') + dcid = _lookup_vultrid(vm_["location"], "avail_locations", "DCID") if not dcid: - log.error('Vultr does not have a location with id or name %s', vm_['location']) + log.error("Vultr does not have a location with id or name %s", vm_["location"]) return False kwargs = { - 'label': vm_['name'], - 'OSID': osid, - 'VPSPLANID': vpsplanid, - 'DCID': dcid, - 'hostname': vm_['name'], - 'enable_private_network': enable_private_network, + "label": vm_["name"], + "OSID": osid, + "VPSPLANID": vpsplanid, + "DCID": dcid, + "hostname": vm_["name"], + "enable_private_network": enable_private_network, } if startup_script: - kwargs['SCRIPTID'] = startup_script + kwargs["SCRIPTID"] = startup_script - log.info('Creating Cloud VM %s', vm_['name']) + log.info("Creating Cloud VM %s", vm_["name"]) - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(vm_['name']), + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(vm_["name"]), args={ - 'kwargs': __utils__['cloud.filter_event']('requesting', kwargs, list(kwargs)), + "kwargs": __utils__["cloud.filter_event"]( + "requesting", kwargs, list(kwargs) + ), }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'], + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) try: - data = _query('server/create', method='POST', data=_urlencode(kwargs)) - if int(data.get('status', '200')) >= 300: + data = _query("server/create", method="POST", data=_urlencode(kwargs)) + if int(data.get("status", "200")) >= 300: log.error( - 'Error creating %s on Vultr\n\n' - 'Vultr API returned %s\n', vm_['name'], data + "Error creating %s on Vultr\n\n" "Vultr API returned %s\n", + vm_["name"], + data, + ) + log.error( + "Status 412 may mean that you are requesting an\n" + "invalid location, image, or size." ) - log.error('Status 412 may mean that you are requesting an\n' - 'invalid location, image, or size.') - __utils__['cloud.fire_event']( - 'event', - 'instance request failed', - 'salt/cloud/{0}/requesting/failed'.format(vm_['name']), - args={'kwargs': kwargs}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'], + __utils__["cloud.fire_event"]( + "event", + "instance request failed", + "salt/cloud/{0}/requesting/failed".format(vm_["name"]), + args={"kwargs": kwargs}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return False except Exception as exc: # pylint: disable=broad-except log.error( - 'Error creating %s on Vultr\n\n' - 'The following exception was thrown when trying to ' - 'run the initial deployment:\n%s', - vm_['name'], exc, + "Error creating %s on Vultr\n\n" + "The following exception was thrown when trying to " + "run the initial deployment:\n%s", + vm_["name"], + exc, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - __utils__['cloud.fire_event']( - 'event', - 'instance request failed', - 'salt/cloud/{0}/requesting/failed'.format(vm_['name']), - args={'kwargs': kwargs}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'], + __utils__["cloud.fire_event"]( + "event", + "instance request failed", + "salt/cloud/{0}/requesting/failed".format(vm_["name"]), + args={"kwargs": kwargs}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return False def wait_for_hostname(): - ''' + """ Wait for the IP address to become available - ''' - data = show_instance(vm_['name'], call='action') - main_ip = six.text_type(data.get('main_ip', '0')) - if main_ip.startswith('0'): + """ + data = show_instance(vm_["name"], call="action") + main_ip = six.text_type(data.get("main_ip", "0")) + if main_ip.startswith("0"): time.sleep(3) return False - return data['main_ip'] + return data["main_ip"] def wait_for_default_password(): - ''' + """ Wait for the IP address to become available - ''' - data = show_instance(vm_['name'], call='action') + """ + data = show_instance(vm_["name"], call="action") # print("Waiting for default password") # pprint.pprint(data) - if six.text_type(data.get('default_password', '')) == '': + if six.text_type(data.get("default_password", "")) == "": time.sleep(1) return False - return data['default_password'] + return data["default_password"] def wait_for_status(): - ''' + """ Wait for the IP address to become available - ''' - data = show_instance(vm_['name'], call='action') + """ + data = show_instance(vm_["name"], call="action") # print("Waiting for status normal") # pprint.pprint(data) - if six.text_type(data.get('status', '')) != 'active': + if six.text_type(data.get("status", "")) != "active": time.sleep(1) return False - return data['default_password'] + return data["default_password"] def wait_for_server_state(): - ''' + """ Wait for the IP address to become available - ''' - data = show_instance(vm_['name'], call='action') + """ + data = show_instance(vm_["name"], call="action") # print("Waiting for server state ok") # pprint.pprint(data) - if six.text_type(data.get('server_state', '')) != 'ok': + if six.text_type(data.get("server_state", "")) != "ok": time.sleep(1) return False - return data['default_password'] + return data["default_password"] - vm_['ssh_host'] = __utils__['cloud.wait_for_fun']( + vm_["ssh_host"] = __utils__["cloud.wait_for_fun"]( wait_for_hostname, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) - vm_['password'] = __utils__['cloud.wait_for_fun']( + vm_["password"] = __utils__["cloud.wait_for_fun"]( wait_for_default_password, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) - __utils__['cloud.wait_for_fun']( + __utils__["cloud.wait_for_fun"]( wait_for_status, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) - __utils__['cloud.wait_for_fun']( + __utils__["cloud.wait_for_fun"]( wait_for_server_state, timeout=config.get_cloud_config_value( - 'wait_for_fun_timeout', vm_, __opts__, default=15 * 60), + "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 + ), ) - __opts__['hard_timeout'] = config.get_cloud_config_value( - 'hard_timeout', + __opts__["hard_timeout"] = config.get_cloud_config_value( + "hard_timeout", get_configured_provider(), __opts__, search_global=False, @@ -452,55 +467,49 @@ def create(vm_): ) # Bootstrap - ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret = __utils__["cloud.bootstrap"](vm_, __opts__) - ret.update(show_instance(vm_['name'], call='action')) + ret.update(show_instance(vm_["name"], call="action")) - log.info('Created Cloud VM \'%s\'', vm_['name']) - log.debug( - '\'%s\' VM creation details:\n%s', - vm_['name'], pprint.pformat(data) - ) + log.info("Created Cloud VM '%s'", vm_["name"]) + log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(vm_['name']), - args=__utils__['cloud.filter_event']('created', vm_, ['name', 'profile', 'provider', 'driver']), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(vm_["name"]), + args=__utils__["cloud.filter_event"]( + "created", vm_, ["name", "profile", "provider", "driver"] + ), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret -def _query(path, method='GET', data=None, params=None, header_dict=None, decode=True): - ''' +def _query(path, method="GET", data=None, params=None, header_dict=None, decode=True): + """ Perform a query directly against the Vultr REST API - ''' + """ api_key = config.get_cloud_config_value( - 'api_key', - get_configured_provider(), - __opts__, - search_global=False, + "api_key", get_configured_provider(), __opts__, search_global=False, ) management_host = config.get_cloud_config_value( - 'management_host', + "management_host", get_configured_provider(), __opts__, search_global=False, - default='api.vultr.com' + default="api.vultr.com", ) - url = 'https://{management_host}/v1/{path}?api_key={api_key}'.format( - management_host=management_host, - path=path, - api_key=api_key, + url = "https://{management_host}/v1/{path}?api_key={api_key}".format( + management_host=management_host, path=path, api_key=api_key, ) if header_dict is None: header_dict = {} - result = __utils__['http.query']( + result = __utils__["http.query"]( url, method=method, params=params, @@ -509,11 +518,11 @@ def _query(path, method='GET', data=None, params=None, header_dict=None, decode= port=443, text=True, decode=decode, - decode_type='json', - hide_fields=['api_key'], + decode_type="json", + hide_fields=["api_key"], opts=__opts__, ) - if 'dict' in result: - return result['dict'] + if "dict" in result: + return result["dict"] return result diff --git a/salt/cloud/clouds/xen.py b/salt/cloud/clouds/xen.py index 45aee59778a..bb898241cb4 100644 --- a/salt/cloud/clouds/xen.py +++ b/salt/cloud/clouds/xen.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" XenServer Cloud Driver ====================== @@ -57,24 +57,22 @@ Example profile configuration: ipv4_cidr: 10.0.0.215/24 ipv4_gw: 10.0.0.1 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from datetime import datetime + import logging import time +from datetime import datetime # Import salt libs import salt.config as config -from salt.ext import six # Import Salt-Cloud Libs import salt.utils.cloud -from salt.exceptions import ( - SaltCloudSystemExit, - SaltCloudException -) +from salt.exceptions import SaltCloudException, SaltCloudSystemExit +from salt.ext import six # Get logging started log = logging.getLogger(__name__) @@ -86,14 +84,14 @@ try: except ImportError: HAS_XEN_API = False -__virtualname__ = 'xen' +__virtualname__ = "xen" cache = None def __virtual__(): - ''' + """ Only load if Xen configuration and XEN SDK is found. - ''' + """ if get_configured_provider() is False: return False if _get_dependencies() is False: @@ -106,116 +104,108 @@ def __virtual__(): def _get_dependencies(): - ''' + """ Warn if dependencies aren't met. Checks for the XenAPI.py module - ''' - return config.check_driver_dependencies( - __virtualname__, - {'XenAPI': HAS_XEN_API} - ) + """ + return config.check_driver_dependencies(__virtualname__, {"XenAPI": HAS_XEN_API}) def get_configured_provider(): - ''' + """ Return the first configured instance. - ''' + """ return config.is_provider_configured( - __opts__, - __active_provider_name__ or __virtualname__, - ('url',) + __opts__, __active_provider_name__ or __virtualname__, ("url",) ) def _get_session(): - ''' + """ Get a connection to the XenServer host - ''' - api_version = '1.0' - originator = 'salt_cloud_{}_driver'.format(__virtualname__) + """ + api_version = "1.0" + originator = "salt_cloud_{}_driver".format(__virtualname__) url = config.get_cloud_config_value( - 'url', - get_configured_provider(), - __opts__, - search_global=False + "url", get_configured_provider(), __opts__, search_global=False ) user = config.get_cloud_config_value( - 'user', - get_configured_provider(), - __opts__, - search_global=False + "user", get_configured_provider(), __opts__, search_global=False ) password = config.get_cloud_config_value( - 'password', - get_configured_provider(), - __opts__, - search_global=False + "password", get_configured_provider(), __opts__, search_global=False ) ignore_ssl = config.get_cloud_config_value( - 'ignore_ssl', + "ignore_ssl", get_configured_provider(), __opts__, default=False, - search_global=False + search_global=False, ) try: session = XenAPI.Session(url, ignore_ssl=ignore_ssl) log.debug( - 'url: %s user: %s password: %s, originator: %s', - url, user, 'XXX-pw-redacted-XXX', originator + "url: %s user: %s password: %s, originator: %s", + url, + user, + "XXX-pw-redacted-XXX", + originator, ) - session.xenapi.login_with_password( - user, password, api_version, originator) + session.xenapi.login_with_password(user, password, api_version, originator) except XenAPI.Failure as ex: - pool_master_addr = six.text_type(ex.__dict__['details'][1]) - slash_parts = url.split('/') - new_url = '/'.join(slash_parts[:2]) + '/' + pool_master_addr + pool_master_addr = six.text_type(ex.__dict__["details"][1]) + slash_parts = url.split("/") + new_url = "/".join(slash_parts[:2]) + "/" + pool_master_addr session = XenAPI.Session(new_url) log.debug( - 'session is -> url: %s user: %s password: %s, originator:%s', - new_url, user, 'XXX-pw-redacted-XXX', originator + "session is -> url: %s user: %s password: %s, originator:%s", + new_url, + user, + "XXX-pw-redacted-XXX", + originator, ) - session.xenapi.login_with_password( - user, password, api_version, originator) + session.xenapi.login_with_password(user, password, api_version, originator) return session def list_nodes(): - ''' + """ List virtual machines .. code-block:: bash salt-cloud -Q - ''' + """ session = _get_session() vms = session.xenapi.VM.get_all_records() ret = {} for vm in vms: record = session.xenapi.VM.get_record(vm) - if not record['is_a_template'] and not record['is_control_domain']: + if not record["is_a_template"] and not record["is_control_domain"]: try: - base_template_name = record['other_config']['base_template_name'] + base_template_name = record["other_config"]["base_template_name"] except Exception: # pylint: disable=broad-except base_template_name = None log.debug( - 'VM %s, doesnt have base_template_name attribute', - record['name_label'] + "VM %s, doesnt have base_template_name attribute", + record["name_label"], ) - ret[record['name_label']] = {'id': record['uuid'], - 'image': base_template_name, - 'name': record['name_label'], - 'size': record['memory_dynamic_max'], - 'state': record['power_state'], - 'private_ips': get_vm_ip(record['name_label'], session), - 'public_ips': None} + ret[record["name_label"]] = { + "id": record["uuid"], + "image": base_template_name, + "name": record["name_label"], + "size": record["memory_dynamic_max"], + "state": record["power_state"], + "private_ips": get_vm_ip(record["name_label"], session), + "public_ips": None, + } return ret def get_vm_ip(name=None, session=None, call=None): - ''' + """ Get the IP address of the VM .. code-block:: bash @@ -224,13 +214,11 @@ def get_vm_ip(name=None, session=None, call=None): .. note:: Requires xen guest tools to be installed in VM - ''' - if call == 'function': - raise SaltCloudException( - 'This function must be called with -a or --action.' - ) + """ + if call == "function": + raise SaltCloudException("This function must be called with -a or --action.") if session is None: - log.debug('New session being created') + log.debug("New session being created") session = _get_session() vm = _get_vm(name, session=session) ret = None @@ -240,9 +228,8 @@ def get_vm_ip(name=None, session=None, call=None): for vif in vifs: if session.xenapi.VIF.get_ipv4_addresses(vif): cidr = session.xenapi.VIF.get_ipv4_addresses(vif).pop() - ret, subnet = cidr.split('/') - log.debug( - 'VM vif returned for instance: %s ip: %s', name, ret) + ret, subnet = cidr.split("/") + log.debug("VM vif returned for instance: %s ip: %s", name, ret) return ret # -- try to get ip from get tools metrics vgm = session.xenapi.VM.get_guest_metrics(vm) @@ -250,37 +237,34 @@ def get_vm_ip(name=None, session=None, call=None): net = session.xenapi.VM_guest_metrics.get_networks(vgm) if "0/ip" in net.keys(): log.debug( - 'VM guest metrics returned for instance: %s 0/ip: %s', - name, net["0/ip"] + "VM guest metrics returned for instance: %s 0/ip: %s", name, net["0/ip"] ) ret = net["0/ip"] # except Exception as ex: # pylint: disable=broad-except except XenAPI.Failure: - log.info('Could not get vm metrics at this time') + log.info("Could not get vm metrics at this time") return ret -def set_vm_ip(name=None, - ipv4_cidr=None, - ipv4_gw=None, - session=None, - call=None): - ''' +def set_vm_ip(name=None, ipv4_cidr=None, ipv4_gw=None, session=None, call=None): + """ Set the IP address on a virtual interface (vif) - ''' - mode = 'static' + """ + mode = "static" # TODO: Need to add support for IPv6 - if call == 'function': - raise SaltCloudException( - 'The function must be called with -a or --action.') + if call == "function": + raise SaltCloudException("The function must be called with -a or --action.") log.debug( - 'Setting name: %s ipv4_cidr: %s ipv4_gw: %s mode: %s', - name, ipv4_cidr, ipv4_gw, mode + "Setting name: %s ipv4_cidr: %s ipv4_gw: %s mode: %s", + name, + ipv4_cidr, + ipv4_gw, + mode, ) if session is None: - log.debug('New session being created') + log.debug("New session being created") session = _get_session() vm = _get_vm(name, session) # -- try to get ip from vif @@ -289,28 +273,27 @@ def set_vm_ip(name=None, # multiple interface(vif) VMs vifs = session.xenapi.VM.get_VIFs(vm) if vifs is not None: - log.debug('There are %s vifs.', len(vifs)) + log.debug("There are %s vifs.", len(vifs)) for vif in vifs: record = session.xenapi.VIF.get_record(vif) log.debug(record) try: - session.xenapi.VIF.configure_ipv4( - vif, mode, ipv4_cidr, ipv4_gw) + session.xenapi.VIF.configure_ipv4(vif, mode, ipv4_cidr, ipv4_gw) except XenAPI.Failure: - log.info('Static IP assignment could not be performed.') + log.info("Static IP assignment could not be performed.") return True def list_nodes_full(session=None): - ''' + """ List full virtual machines .. code-block:: bash salt-cloud -F - ''' + """ if session is None: session = _get_session() @@ -318,57 +301,55 @@ def list_nodes_full(session=None): vms = session.xenapi.VM.get_all() for vm in vms: record = session.xenapi.VM.get_record(vm) - if not record['is_a_template'] and not record['is_control_domain']: + if not record["is_a_template"] and not record["is_control_domain"]: # deal with cases where the VM doesn't have 'base_template_name' attribute try: - base_template_name = record['other_config']['base_template_name'] + base_template_name = record["other_config"]["base_template_name"] except Exception: # pylint: disable=broad-except base_template_name = None log.debug( - 'VM %s, doesnt have base_template_name attribute', - record['name_label'] + "VM %s, doesnt have base_template_name attribute", + record["name_label"], ) vm_cfg = session.xenapi.VM.get_record(vm) - vm_cfg['id'] = record['uuid'] - vm_cfg['name'] = record['name_label'] - vm_cfg['image'] = base_template_name - vm_cfg['size'] = None - vm_cfg['state'] = record['power_state'] - vm_cfg['private_ips'] = get_vm_ip(record['name_label'], session) - vm_cfg['public_ips'] = None - if 'snapshot_time' in vm_cfg.keys(): - del vm_cfg['snapshot_time'] - ret[record['name_label']] = vm_cfg + vm_cfg["id"] = record["uuid"] + vm_cfg["name"] = record["name_label"] + vm_cfg["image"] = base_template_name + vm_cfg["size"] = None + vm_cfg["state"] = record["power_state"] + vm_cfg["private_ips"] = get_vm_ip(record["name_label"], session) + vm_cfg["public_ips"] = None + if "snapshot_time" in vm_cfg.keys(): + del vm_cfg["snapshot_time"] + ret[record["name_label"]] = vm_cfg - provider = __active_provider_name__ or 'xen' - if ':' in provider: - comps = provider.split(':') + provider = __active_provider_name__ or "xen" + if ":" in provider: + comps = provider.split(":") provider = comps[0] - log.debug('ret: %s', ret) - log.debug('provider: %s', provider) - log.debug('__opts__: %s', __opts__) - __utils__['cloud.cache_node_list'](ret, provider, __opts__) + log.debug("ret: %s", ret) + log.debug("provider: %s", provider) + log.debug("__opts__: %s", __opts__) + __utils__["cloud.cache_node_list"](ret, provider, __opts__) return ret def list_nodes_select(call=None): - ''' + """ Perform a select query on Xen VM instances .. code-block:: bash salt-cloud -S - ''' + """ return salt.utils.cloud.list_nodes_select( - list_nodes_full(), - __opts__['query.selection'], - call, + list_nodes_full(), __opts__["query.selection"], call, ) def vdi_list(call=None, kwargs=None): - ''' + """ Return available Xen VDI images If this function is called with the ``-f`` or ``--function`` then @@ -379,14 +360,13 @@ def vdi_list(call=None, kwargs=None): salt-cloud -f vdi_list myxen terse=True - ''' - if call == 'action': - raise SaltCloudException( - 'This function must be called with -f or --function.') - log.debug('kwargs is %s', kwargs) + """ + if call == "action": + raise SaltCloudException("This function must be called with -f or --function.") + log.debug("kwargs is %s", kwargs) if kwargs is not None: - if 'terse' in kwargs: - if kwargs['terse'] == 'True': + if "terse" in kwargs: + if kwargs["terse"] == "True": terse = True else: terse = False @@ -402,50 +382,50 @@ def vdi_list(call=None, kwargs=None): data = session.xenapi.VDI.get_record(vdi) log.debug(type(terse)) if terse is True: - ret[data.get('name_label')] = { - 'uuid': data.get('uuid'), - 'OpqueRef': vdi} + ret[data.get("name_label")] = {"uuid": data.get("uuid"), "OpqueRef": vdi} else: - data.update({'OpaqueRef': vdi}) - ret[data.get('name_label')] = data + data.update({"OpaqueRef": vdi}) + ret[data.get("name_label")] = data return ret def avail_locations(session=None, call=None): - ''' + """ Return available Xen locations (not implemented) .. code-block:: bash salt-cloud --list-locations myxen - ''' + """ # TODO: need to figure out a good meaning of locations in Xen - if call == 'action': + if call == "action": raise SaltCloudException( - 'The avail_locations function must be called with -f or --function.' + "The avail_locations function must be called with -f or --function." ) return pool_list() def avail_sizes(session=None, call=None): - ''' + """ Return a list of Xen template definitions .. code-block:: bash salt-cloud --list-sizes myxen - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudException( - 'The avail_sizes function must be called with -f or --function.') - return {'STATUS': - 'Sizes are build into templates. Consider running --list-images to see sizes'} + "The avail_sizes function must be called with -f or --function." + ) + return { + "STATUS": "Sizes are build into templates. Consider running --list-images to see sizes" + } def template_list(call=None): - ''' + """ Return available Xen template information. This returns the details of @@ -455,19 +435,19 @@ def template_list(call=None): salt-cloud -f template_list myxen - ''' + """ templates = {} session = _get_session() vms = session.xenapi.VM.get_all() for vm in vms: record = session.xenapi.VM.get_record(vm) - if record['is_a_template']: - templates[record['name_label']] = record + if record["is_a_template"]: + templates[record["name_label"]] = record return templates def show_instance(name, session=None, call=None): - ''' + """ Show information about a specific VM or template .. code-block:: bash @@ -476,48 +456,45 @@ def show_instance(name, session=None, call=None): .. note:: memory is memory_dynamic_max - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The show_instnce function must be called with -a or --action.' + "The show_instnce function must be called with -a or --action." ) - log.debug('show_instance-> name: %s session: %s', name, session) + log.debug("show_instance-> name: %s session: %s", name, session) if session is None: session = _get_session() vm = _get_vm(name, session=session) record = session.xenapi.VM.get_record(vm) - if not record['is_a_template'] and not record['is_control_domain']: + if not record["is_a_template"] and not record["is_control_domain"]: try: - base_template_name = record['other_config']['base_template_name'] + base_template_name = record["other_config"]["base_template_name"] except Exception: # pylint: disable=broad-except base_template_name = None log.debug( - 'VM %s, doesnt have base_template_name attribute', - record['name_label'] + "VM %s, doesnt have base_template_name attribute", record["name_label"] ) - ret = {'id': record['uuid'], - 'image': base_template_name, - 'name': record['name_label'], - 'size': record['memory_dynamic_max'], - 'state': record['power_state'], - 'private_ips': get_vm_ip(name, session), - 'public_ips': None} + ret = { + "id": record["uuid"], + "image": base_template_name, + "name": record["name_label"], + "size": record["memory_dynamic_max"], + "state": record["power_state"], + "private_ips": get_vm_ip(name, session), + "public_ips": None, + } - __utils__['cloud.cache_node']( - ret, - __active_provider_name__, - __opts__ - ) + __utils__["cloud.cache_node"](ret, __active_provider_name__, __opts__) return ret def _determine_resource_pool(session, vm_): - ''' + """ Called by create() used to determine resource pool - ''' - resource_pool = '' - if 'resource_pool' in vm_.keys(): - resource_pool = _get_pool(vm_['resource_pool'], session) + """ + resource_pool = "" + if "resource_pool" in vm_.keys(): + resource_pool = _get_pool(vm_["resource_pool"], session) else: pool = session.xenapi.pool.get_all() if not pool: @@ -526,32 +503,32 @@ def _determine_resource_pool(session, vm_): first_pool = session.xenapi.pool.get_all()[0] resource_pool = first_pool pool_record = session.xenapi.pool.get_record(resource_pool) - log.debug('resource pool: %s', pool_record['name_label']) + log.debug("resource pool: %s", pool_record["name_label"]) return resource_pool def _determine_storage_repo(session, resource_pool, vm_): - ''' + """ Called by create() used to determine storage repo for create - ''' - storage_repo = '' - if 'storage_repo' in vm_.keys(): - storage_repo = _get_sr(vm_['storage_repo'], session) + """ + storage_repo = "" + if "storage_repo" in vm_.keys(): + storage_repo = _get_sr(vm_["storage_repo"], session) else: storage_repo = None if resource_pool: default_sr = session.xenapi.pool.get_default_SR(resource_pool) sr_record = session.xenapi.SR.get_record(default_sr) - log.debug('storage repository: %s', sr_record['name_label']) + log.debug("storage repository: %s", sr_record["name_label"]) storage_repo = default_sr else: storage_repo = None - log.debug('storage repository: %s', storage_repo) + log.debug("storage repository: %s", storage_repo) return storage_repo def create(vm_): - ''' + """ Create a VM in Xen The configuration for this function is read from the profile settings. @@ -560,27 +537,23 @@ def create(vm_): salt-cloud -p some_profile xenvm01 - ''' - name = vm_['name'] + """ + name = vm_["name"] record = {} ret = {} # fire creating event - __utils__['cloud.fire_event']( - 'event', - 'starting create', - 'salt/cloud/{0}/creating'.format(name), - args={ - 'name': name, - 'profile': vm_['profile'], - 'provider': vm_['driver'], - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "starting create", + "salt/cloud/{0}/creating".format(name), + args={"name": name, "profile": vm_["profile"], "provider": vm_["driver"]}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - log.debug('Adding %s to cloud cache.', name) - __utils__['cloud.cachedir_index_add']( - vm_['name'], vm_['profile'], 'xen', vm_['driver'] + log.debug("Adding %s to cloud cache.", name) + __utils__["cloud.cachedir_index_add"]( + vm_["name"], vm_["profile"], "xen", vm_["driver"] ) # connect to xen @@ -593,19 +566,19 @@ def create(vm_): storage_repo = _determine_storage_repo(session, resource_pool, vm_) # build VM - image = vm_.get('image') - clone = vm_.get('clone') + image = vm_.get("image") + clone = vm_.get("clone") if clone is None: clone = True - log.debug('Clone: %s ', clone) + log.debug("Clone: %s ", clone) # fire event to read new vm properties (requesting) - __utils__['cloud.fire_event']( - 'event', - 'requesting instance', - 'salt/cloud/{0}/requesting'.format(name), - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "requesting instance", + "salt/cloud/{0}/requesting".format(name), + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) # create by cloning template @@ -631,127 +604,119 @@ def create(vm_): _set_static_ip(name, session, vm_) # if not deploying salt then exit - deploy = vm_.get('deploy', True) - log.debug('delopy is set to %s', deploy) + deploy = vm_.get("deploy", True) + log.debug("delopy is set to %s", deploy) if deploy: record = session.xenapi.VM.get_record(vm) if record is not None: _deploy_salt_minion(name, session, vm_) else: - log.debug( - 'The Salt minion will not be installed, deploy: %s', - vm_['deploy'] - ) + log.debug("The Salt minion will not be installed, deploy: %s", vm_["deploy"]) record = session.xenapi.VM.get_record(vm) ret = show_instance(name) - ret.update({'extra': record}) + ret.update({"extra": record}) - __utils__['cloud.fire_event']( - 'event', - 'created instance', - 'salt/cloud/{0}/created'.format(name), - args={ - 'name': name, - 'profile': vm_['profile'], - 'provider': vm_['driver'], - }, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "created instance", + "salt/cloud/{0}/created".format(name), + args={"name": name, "profile": vm_["profile"], "provider": vm_["driver"]}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return ret def _deploy_salt_minion(name, session, vm_): - ''' + """ Deploy salt minion during create() - ''' + """ # Get bootstrap values - vm_['ssh_host'] = get_vm_ip(name, session) - vm_['user'] = vm_.get('user', 'root') - vm_['password'] = vm_.get('password', 'p@ssw0rd!') - vm_['provider'] = vm_.get('provider', 'xen') - log.debug('%s has IP of %s', name, vm_['ssh_host']) + vm_["ssh_host"] = get_vm_ip(name, session) + vm_["user"] = vm_.get("user", "root") + vm_["password"] = vm_.get("password", "p@ssw0rd!") + vm_["provider"] = vm_.get("provider", "xen") + log.debug("%s has IP of %s", name, vm_["ssh_host"]) # Bootstrap Salt minion! - if vm_['ssh_host'] is not None: - log.info('Installing Salt minion on %s', name) - boot_ret = __utils__['cloud.bootstrap'](vm_, __opts__) - log.debug('boot return: %s', boot_ret) + if vm_["ssh_host"] is not None: + log.info("Installing Salt minion on %s", name) + boot_ret = __utils__["cloud.bootstrap"](vm_, __opts__) + log.debug("boot return: %s", boot_ret) def _set_static_ip(name, session, vm_): - ''' + """ Set static IP during create() if defined - ''' - ipv4_cidr = '' - ipv4_gw = '' - if 'ipv4_gw' in vm_.keys(): - log.debug('ipv4_gw is found in keys') - ipv4_gw = vm_['ipv4_gw'] - if 'ipv4_cidr' in vm_.keys(): - log.debug('ipv4_cidr is found in keys') - ipv4_cidr = vm_['ipv4_cidr'] - log.debug('attempting to set IP in instance') + """ + ipv4_cidr = "" + ipv4_gw = "" + if "ipv4_gw" in vm_.keys(): + log.debug("ipv4_gw is found in keys") + ipv4_gw = vm_["ipv4_gw"] + if "ipv4_cidr" in vm_.keys(): + log.debug("ipv4_cidr is found in keys") + ipv4_cidr = vm_["ipv4_cidr"] + log.debug("attempting to set IP in instance") set_vm_ip(name, ipv4_cidr, ipv4_gw, session, None) def _wait_for_ip(name, session): - ''' + """ Wait for IP to be available during create() - ''' + """ start_time = datetime.now() status = None while status is None: status = get_vm_ip(name, session) if status is not None: # ignore APIPA address - if status.startswith('169'): + if status.startswith("169"): status = None check_time = datetime.now() delta = check_time - start_time log.debug( - 'Waited %s seconds for %s to report ip address...', - delta.seconds, name + "Waited %s seconds for %s to report ip address...", delta.seconds, name ) if delta.seconds > 180: - log.warning('Timeout getting IP address') + log.warning("Timeout getting IP address") break time.sleep(5) def _run_async_task(task=None, session=None): - ''' + """ Run XenAPI task in asynchronous mode to prevent timeouts - ''' + """ if task is None or session is None: return None task_name = session.xenapi.task.get_name_label(task) - log.debug('Running %s', task_name) - while session.xenapi.task.get_status(task) == 'pending': + log.debug("Running %s", task_name) + while session.xenapi.task.get_status(task) == "pending": progress = round(session.xenapi.task.get_progress(task), 2) * 100 - log.debug('Task progress %.2f%%', progress) + log.debug("Task progress %.2f%%", progress) time.sleep(1) - log.debug('Cleaning up task %s', task_name) + log.debug("Cleaning up task %s", task_name) session.xenapi.task.destroy(task) def _clone_vm(image=None, name=None, session=None): - ''' + """ Create VM by cloning This is faster and should be used if source and target are in the same storage repository - ''' + """ if session is None: session = _get_session() - log.debug('Creating VM %s by cloning %s', name, image) + log.debug("Creating VM %s by cloning %s", name, image) source = _get_vm(image, session) task = session.xenapi.Async.VM.clone(source, name) _run_async_task(task, session) def _copy_vm(template=None, name=None, session=None, sr=None): - ''' + """ Create VM by copy This is slower and should be used if source and target are @@ -761,43 +726,43 @@ def _copy_vm(template=None, name=None, session=None, sr=None): name = string name of new VM session = object reference sr = object reference - ''' + """ if session is None: session = _get_session() - log.debug('Creating VM %s by copying %s', name, template) + log.debug("Creating VM %s by copying %s", name, template) source = _get_vm(template, session) task = session.xenapi.Async.VM.copy(source, name, sr) _run_async_task(task, session) def _provision_vm(name=None, session=None): - ''' + """ Provision vm right after clone/copy - ''' + """ if session is None: session = _get_session() - log.info('Provisioning VM %s', name) + log.info("Provisioning VM %s", name) vm = _get_vm(name, session) task = session.xenapi.Async.VM.provision(vm) _run_async_task(task, session) def start(name, call=None, session=None): - ''' + """ Start a vm .. code-block:: bash salt-cloud -a start xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The show_instnce function must be called with -a or --action.' + "The show_instnce function must be called with -a or --action." ) if session is None: session = _get_session() - log.info('Starting VM %s', name) + log.info("Starting VM %s", name) vm = _get_vm(name, session) task = session.xenapi.Async.VM.start(vm, False, True) _run_async_task(task, session) @@ -805,21 +770,21 @@ def start(name, call=None, session=None): def pause(name, call=None, session=None): - ''' + """ Pause a vm .. code-block:: bash salt-cloud -a pause xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The show_instnce function must be called with -a or --action.' + "The show_instnce function must be called with -a or --action." ) if session is None: session = _get_session() - log.info('Pausing VM %s', name) + log.info("Pausing VM %s", name) vm = _get_vm(name, session) task = session.xenapi.Async.VM.pause(vm) _run_async_task(task, session) @@ -827,21 +792,21 @@ def pause(name, call=None, session=None): def unpause(name, call=None, session=None): - ''' + """ UnPause a vm .. code-block:: bash salt-cloud -a unpause xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The show_instnce function must be called with -a or --action.' + "The show_instnce function must be called with -a or --action." ) if session is None: session = _get_session() - log.info('Unpausing VM %s', name) + log.info("Unpausing VM %s", name) vm = _get_vm(name, session) task = session.xenapi.Async.VM.unpause(vm) _run_async_task(task, session) @@ -849,21 +814,21 @@ def unpause(name, call=None, session=None): def suspend(name, call=None, session=None): - ''' + """ Suspend a vm to disk .. code-block:: bash salt-cloud -a suspend xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The show_instnce function must be called with -a or --action.' + "The show_instnce function must be called with -a or --action." ) if session is None: session = _get_session() - log.info('Suspending VM %s', name) + log.info("Suspending VM %s", name) vm = _get_vm(name, session) task = session.xenapi.Async.VM.suspend(vm) _run_async_task(task, session) @@ -871,21 +836,21 @@ def suspend(name, call=None, session=None): def resume(name, call=None, session=None): - ''' + """ Resume a vm from disk .. code-block:: bash salt-cloud -a resume xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The show_instnce function must be called with -a or --action.' + "The show_instnce function must be called with -a or --action." ) if session is None: session = _get_session() - log.info('Resuming VM %s', name) + log.info("Resuming VM %s", name) vm = _get_vm(name, session) task = session.xenapi.Async.VM.resume(vm, False, True) _run_async_task(task, session) @@ -893,7 +858,7 @@ def resume(name, call=None, session=None): def stop(name, call=None, session=None): - ''' + """ Stop a vm .. code-block:: bash @@ -901,30 +866,30 @@ def stop(name, call=None, session=None): salt-cloud -a stop xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The show_instnce function must be called with -a or --action.' + "The show_instnce function must be called with -a or --action." ) return shutdown(name, call, session) def shutdown(name, call=None, session=None): - ''' + """ Shutdown a vm .. code-block:: bash salt-cloud -a shutdown xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The show_instnce function must be called with -a or --action.' + "The show_instnce function must be called with -a or --action." ) if session is None: session = _get_session() - log.info('Starting VM %s', name) + log.info("Starting VM %s", name) vm = _get_vm(name, session) task = session.xenapi.Async.VM.shutdown(vm) _run_async_task(task, session) @@ -932,35 +897,35 @@ def shutdown(name, call=None, session=None): def reboot(name, call=None, session=None): - ''' + """ Reboot a vm .. code-block:: bash salt-cloud -a reboot xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudException( - 'The show_instnce function must be called with -a or --action.' + "The show_instnce function must be called with -a or --action." ) if session is None: session = _get_session() - log.info('Starting VM %s', name) + log.info("Starting VM %s", name) vm = _get_vm(name, session) power_state = session.xenapi.VM.get_power_state(vm) - if power_state == 'Running': + if power_state == "Running": task = session.xenapi.Async.VM.clean_reboot(vm) _run_async_task(task, session) return show_instance(name) else: - return '{} is not running to be rebooted'.format(name) + return "{} is not running to be rebooted".format(name) def _get_vm(name=None, session=None): - ''' + """ Get XEN vm instance object reference - ''' + """ if session is None: session = _get_session() vms = session.xenapi.VM.get_by_name_label(name) @@ -970,9 +935,9 @@ def _get_vm(name=None, session=None): def _get_sr(name=None, session=None): - ''' + """ Get XEN sr (storage repo) object reference - ''' + """ if session is None: session = _get_session() srs = session.xenapi.SR.get_by_name_label(name) @@ -982,165 +947,162 @@ def _get_sr(name=None, session=None): def _get_pool(name=None, session=None): - ''' + """ Get XEN resource pool object reference - ''' + """ if session is None: session = _get_session() pools = session.xenapi.pool.get_all() for pool in pools: pool_record = session.xenapi.pool.get_record(pool) - if name in pool_record.get('name_label'): + if name in pool_record.get("name_label"): return pool return None def destroy(name=None, call=None): - ''' + """ Destroy Xen VM or template instance .. code-block:: bash salt-cloud -d xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) ret = {} - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) session = _get_session() vm = _get_vm(name) if vm: # get vm record = session.xenapi.VM.get_record(vm) - log.debug('power_state: %s', record['power_state']) + log.debug("power_state: %s", record["power_state"]) # shut down - if record['power_state'] != 'Halted': + if record["power_state"] != "Halted": task = session.xenapi.Async.VM.hard_shutdown(vm) _run_async_task(task, session) # destroy disk (vdi) by reading vdb on vm - ret['vbd'] = destroy_vm_vdis(name, session) + ret["vbd"] = destroy_vm_vdis(name, session) # destroy vm task = session.xenapi.Async.VM.destroy(vm) _run_async_task(task, session) - ret['destroyed'] = True - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + ret["destroyed"] = True + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir']( - name, - __active_provider_name__.split(':')[0], - __opts__ + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ ) - __utils__['cloud.cachedir_index_del'](name) + __utils__["cloud.cachedir_index_del"](name) return ret def sr_list(call=None): - ''' + """ Geta list of storage repositories .. code-block:: bash salt-cloud -f sr_list myxen - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'This function must be called with -f, --function argument.' + "This function must be called with -f, --function argument." ) ret = {} session = _get_session() srs = session.xenapi.SR.get_all() for sr in srs: sr_record = session.xenapi.SR.get_record(sr) - ret[sr_record['name_label']] = sr_record + ret[sr_record["name_label"]] = sr_record return ret def host_list(call=None): - ''' + """ Get a list of Xen Servers .. code-block:: bash salt-cloud -f host_list myxen - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'This function must be called with -f, --function argument.' + "This function must be called with -f, --function argument." ) ret = {} session = _get_session() hosts = session.xenapi.host.get_all() for host in hosts: host_record = session.xenapi.host.get_record(host) - ret[host_record['name_label']] = host_record + ret[host_record["name_label"]] = host_record return ret def pool_list(call=None): - ''' + """ Get a list of Resource Pools .. code-block:: bash salt-cloud -f pool_list myxen - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'This function must be called with -f, --function argument.' + "This function must be called with -f, --function argument." ) ret = {} session = _get_session() pools = session.xenapi.pool.get_all() for pool in pools: pool_record = session.xenapi.pool.get_record(pool) - ret[pool_record['name_label']] = pool_record + ret[pool_record["name_label"]] = pool_record return ret def pif_list(call=None): - ''' + """ Get a list of Resource Pools .. code-block:: bash salt-cloud -f pool_list myxen - ''' - if call != 'function': + """ + if call != "function": raise SaltCloudSystemExit( - 'This function must be called with -f, --function argument.' + "This function must be called with -f, --function argument." ) ret = {} session = _get_session() pifs = session.xenapi.PIF.get_all() for pif in pifs: record = session.xenapi.PIF.get_record(pif) - ret[record['uuid']] = record + ret[record["uuid"]] = record return ret def vif_list(name, call=None, kwargs=None): - ''' + """ Get a list of virtual network interfaces on a VM **requires**: the name of the vm with the vbd definition @@ -1149,13 +1111,13 @@ def vif_list(name, call=None, kwargs=None): salt-cloud -a vif_list xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'This function must be called with -a, --action argument.' + "This function must be called with -a, --action argument." ) if name is None: - return 'A name kwarg is rquired' + return "A name kwarg is rquired" ret = {} data = {} session = _get_session() @@ -1165,14 +1127,14 @@ def vif_list(name, call=None, kwargs=None): x = 0 for vif in vifs: vif_record = session.xenapi.VIF.get_record(vif) - data['vif-{}'.format(x)] = vif_record + data["vif-{}".format(x)] = vif_record x += 1 ret[name] = data return ret def vbd_list(name=None, call=None): - ''' + """ Get a list of VBDs on a VM **requires**: the name of the vm with the vbd definition @@ -1181,13 +1143,13 @@ def vbd_list(name=None, call=None): salt-cloud -a vbd_list xenvm01 - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'This function must be called with -a, --action argument.' + "This function must be called with -a, --action argument." ) if name is None: - return 'A name kwarg is rquired' + return "A name kwarg is rquired" ret = {} data = {} session = _get_session() @@ -1199,14 +1161,14 @@ def vbd_list(name=None, call=None): x = 0 for vbd in vbds: vbd_record = session.xenapi.VBD.get_record(vbd) - data['vbd-{}'.format(x)] = vbd_record + data["vbd-{}".format(x)] = vbd_record x += 1 ret = data return ret def avail_images(call=None): - ''' + """ Get a list of images from Xen If called with the `--list-images` then it returns @@ -1216,23 +1178,23 @@ def avail_images(call=None): salt-cloud --list-images myxen - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'This function must be called with -f, --function argument.' + "This function must be called with -f, --function argument." ) return template_list() def destroy_vm_vdis(name=None, session=None, call=None): - ''' + """ Get virtual block devices on VM .. code-block:: bash salt-cloud -a destroy_vm_vdis xenvm01 - ''' + """ if session is None: session = _get_session() ret = {} @@ -1245,65 +1207,62 @@ def destroy_vm_vdis(name=None, session=None, call=None): x = 0 for vbd in vbds: vbd_record = session.xenapi.VBD.get_record(vbd) - if vbd_record['VDI'] != 'OpaqueRef:NULL': + if vbd_record["VDI"] != "OpaqueRef:NULL": # read vdi on vdb - vdi_record = session.xenapi.VDI.get_record( - vbd_record['VDI']) - if 'iso' not in vdi_record['name_label']: - session.xenapi.VDI.destroy(vbd_record['VDI']) - ret['vdi-{}'.format(x)] = vdi_record['name_label'] + vdi_record = session.xenapi.VDI.get_record(vbd_record["VDI"]) + if "iso" not in vdi_record["name_label"]: + session.xenapi.VDI.destroy(vbd_record["VDI"]) + ret["vdi-{}".format(x)] = vdi_record["name_label"] x += 1 return ret def destroy_template(name=None, call=None, kwargs=None): - ''' + """ Destroy Xen VM or template instance .. code-block:: bash salt-cloud -f destroy_template myxen name=testvm2 - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The destroy_template function must be called with -f.' + "The destroy_template function must be called with -f." ) if kwargs is None: kwargs = {} - name = kwargs.get('name', None) + name = kwargs.get("name", None) session = _get_session() vms = session.xenapi.VM.get_all_records() ret = {} found = False for vm in vms: record = session.xenapi.VM.get_record(vm) - if record['is_a_template']: - if record['name_label'] == name: + if record["is_a_template"]: + if record["name_label"] == name: found = True # log.debug(record['name_label']) session.xenapi.VM.destroy(vm) - ret[name] = {'status': 'destroyed'} + ret[name] = {"status": "destroyed"} if not found: - ret[name] = {'status': 'not found'} + ret[name] = {"status": "not found"} return ret def get_pv_args(name, session=None, call=None): - ''' + """ Get PV arguments for a VM .. code-block:: bash salt-cloud -a get_pv_args xenvm01 - ''' - if call == 'function': - raise SaltCloudException( - 'This function must be called with -a or --action.' - ) + """ + if call == "function": + raise SaltCloudException("This function must be called with -a or --action.") if session is None: - log.debug('New session being created') + log.debug("New session being created") session = _get_session() vm = _get_vm(name, session=session) pv_args = session.xenapi.VM.get_PV_args(vm) @@ -1313,29 +1272,27 @@ def get_pv_args(name, session=None, call=None): def set_pv_args(name, kwargs=None, session=None, call=None): - ''' + """ Set PV arguments for a VM .. code-block:: bash salt-cloud -a set_pv_args xenvm01 pv_args="utf-8 graphical" - ''' - if call == 'function': - raise SaltCloudException( - 'This function must be called with -a or --action.' - ) + """ + if call == "function": + raise SaltCloudException("This function must be called with -a or --action.") if session is None: - log.debug('New session being created') + log.debug("New session being created") session = _get_session() vm = _get_vm(name, session=session) try: - log.debug('Setting PV Args: %s', kwargs['pv_args']) - session.xenapi.VM.set_PV_args(vm, str(kwargs['pv_args'])) + log.debug("Setting PV Args: %s", kwargs["pv_args"]) + session.xenapi.VM.set_PV_args(vm, str(kwargs["pv_args"])) except KeyError: - log.error('No pv_args parameter found.') + log.error("No pv_args parameter found.") return False except XenAPI.Failure: - log.info('Setting PV Args failed.') + log.info("Setting PV Args failed.") return False return True diff --git a/salt/cloud/exceptions.py b/salt/cloud/exceptions.py index e482697b4c2..15e266b0c9a 100644 --- a/salt/cloud/exceptions.py +++ b/salt/cloud/exceptions.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" salt.cloud.exceptions ~~~~~~~~~~~~~~~~~~~~ Salt cloud related exceptions. :codeauthor: Pedro Algarvio (pedro@algarvio.me) -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -15,46 +15,49 @@ from salt.exceptions import SaltException class SaltCloudException(SaltException): - ''' + """ Generic Salt Cloud Exception - ''' + """ class SaltCloudSystemExit(SaltCloudException): - ''' + """ This exception is raised when the execution should be stopped. - ''' + """ + def __init__(self, message, exit_code=salt.defaults.exitcodes.EX_GENERIC): SaltCloudException.__init__(self, message) - self.message = '{0} [WARNING: salt.cloud.exceptions is deprecated. Please migrate to salt.exceptions!]'.format(message) + self.message = "{0} [WARNING: salt.cloud.exceptions is deprecated. Please migrate to salt.exceptions!]".format( + message + ) self.exit_code = exit_code class SaltCloudConfigError(SaltCloudException): - ''' + """ Raised when a configuration setting is not found and should exist. - ''' + """ class SaltCloudNotFound(SaltCloudException): - ''' + """ Raised when some cloud provider function cannot find what's being searched. - ''' + """ class SaltCloudExecutionTimeout(SaltCloudException): - ''' + """ Raised when too much time has passed while querying/waiting for data. - ''' + """ class SaltCloudExecutionFailure(SaltCloudException): - ''' + """ Raised when too much failures have occurred while querying/waiting for data. - ''' + """ class SaltCloudPasswordError(SaltCloudException): - ''' + """ Raise when virtual terminal password input failed - ''' + """ diff --git a/salt/cloud/libcloudfuncs.py b/salt/cloud/libcloudfuncs.py index c46ecc4c0a5..b0dec09ec28 100644 --- a/salt/cloud/libcloudfuncs.py +++ b/salt/cloud/libcloudfuncs.py @@ -1,17 +1,28 @@ # -*- coding: utf-8 -*- -''' +""" The generic libcloud template used to create the connections and deploy the cloud virtual machines -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging + +import salt.client +import salt.config as config + +# Import salt cloud libs +import salt.utils.cloud +import salt.utils.data + +# Import salt libs +import salt.utils.event +from salt.exceptions import SaltCloudNotFound, SaltCloudSystemExit from salt.ext import six from salt.ext.six.moves import zip - # pylint: disable=W0611 # Import libcloud try: @@ -19,14 +30,17 @@ try: import re from libcloud.compute.types import Provider from libcloud.compute.providers import get_driver - from libcloud.compute.deployment import ( - MultiStepDeployment, - ScriptDeployment - ) + from libcloud.compute.deployment import MultiStepDeployment, ScriptDeployment + HAS_LIBCLOUD = True - LIBCLOUD_VERSION_INFO = tuple([ - int(part) for part in libcloud.__version__.replace('-', '.').replace('rc', '.').split('.')[:3] - ]) + LIBCLOUD_VERSION_INFO = tuple( + [ + int(part) + for part in libcloud.__version__.replace("-", ".") + .replace("rc", ".") + .split(".")[:3] + ] + ) except ImportError: HAS_LIBCLOUD = False @@ -34,16 +48,6 @@ except ImportError: # pylint: enable=W0611 -# Import salt libs -import salt.utils.event -import salt.client - -# Import salt cloud libs -import salt.utils.cloud -import salt.utils.data -import salt.config as config -from salt.exceptions import SaltCloudNotFound, SaltCloudSystemExit - # Get logging started log = logging.getLogger(__name__) @@ -52,177 +56,178 @@ LIBCLOUD_MINIMAL_VERSION = (0, 14, 0) def node_state(id_): - ''' + """ Libcloud supported node states - ''' + """ states_int = { - 0: 'RUNNING', - 1: 'REBOOTING', - 2: 'TERMINATED', - 3: 'PENDING', - 4: 'UNKNOWN', - 5: 'STOPPED', - 6: 'SUSPENDED', - 7: 'ERROR', - 8: 'PAUSED'} + 0: "RUNNING", + 1: "REBOOTING", + 2: "TERMINATED", + 3: "PENDING", + 4: "UNKNOWN", + 5: "STOPPED", + 6: "SUSPENDED", + 7: "ERROR", + 8: "PAUSED", + } states_str = { - 'running': 'RUNNING', - 'rebooting': 'REBOOTING', - 'starting': 'STARTING', - 'terminated': 'TERMINATED', - 'pending': 'PENDING', - 'unknown': 'UNKNOWN', - 'stopping': 'STOPPING', - 'stopped': 'STOPPED', - 'suspended': 'SUSPENDED', - 'error': 'ERROR', - 'paused': 'PAUSED', - 'reconfiguring': 'RECONFIGURING' + "running": "RUNNING", + "rebooting": "REBOOTING", + "starting": "STARTING", + "terminated": "TERMINATED", + "pending": "PENDING", + "unknown": "UNKNOWN", + "stopping": "STOPPING", + "stopped": "STOPPED", + "suspended": "SUSPENDED", + "error": "ERROR", + "paused": "PAUSED", + "reconfiguring": "RECONFIGURING", } return states_str[id_] if isinstance(id_, six.string_types) else states_int[id_] def check_libcloud_version(reqver=LIBCLOUD_MINIMAL_VERSION, why=None): - ''' + """ Compare different libcloud versions - ''' + """ if not HAS_LIBCLOUD: return False if not isinstance(reqver, (list, tuple)): raise RuntimeError( - '\'reqver\' needs to passed as a tuple or list, i.e., (0, 14, 0)' + "'reqver' needs to passed as a tuple or list, i.e., (0, 14, 0)" ) try: import libcloud # pylint: disable=redefined-outer-name except ImportError: raise ImportError( - 'salt-cloud requires >= libcloud {0} which is not installed'.format( - '.'.join([six.text_type(num) for num in reqver]) + "salt-cloud requires >= libcloud {0} which is not installed".format( + ".".join([six.text_type(num) for num in reqver]) ) ) if LIBCLOUD_VERSION_INFO >= reqver: return libcloud.__version__ - errormsg = 'Your version of libcloud is {0}. '.format(libcloud.__version__) - errormsg += 'salt-cloud requires >= libcloud {0}'.format( - '.'.join([six.text_type(num) for num in reqver]) + errormsg = "Your version of libcloud is {0}. ".format(libcloud.__version__) + errormsg += "salt-cloud requires >= libcloud {0}".format( + ".".join([six.text_type(num) for num in reqver]) ) if why: - errormsg += ' for {0}'.format(why) - errormsg += '. Please upgrade.' + errormsg += " for {0}".format(why) + errormsg += ". Please upgrade." raise ImportError(errormsg) def get_node(conn, name): - ''' + """ Return a libcloud node for the named VM - ''' + """ nodes = conn.list_nodes() for node in nodes: if node.name == name: - __utils__['cloud.cache_node'](salt.utils.data.simple_types_filter(node.__dict__), __active_provider_name__, __opts__) + __utils__["cloud.cache_node"]( + salt.utils.data.simple_types_filter(node.__dict__), + __active_provider_name__, + __opts__, + ) return node def avail_locations(conn=None, call=None): - ''' + """ Return a dict of all available VM locations on the cloud provider with relevant data - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_locations function must be called with ' - '-f or --function, or with the --list-locations option' + "The avail_locations function must be called with " + "-f or --function, or with the --list-locations option" ) if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 locations = conn.list_locations() ret = {} for img in locations: if isinstance(img.name, six.string_types) and not six.PY3: - img_name = img.name.encode('ascii', 'salt-cloud-force-ascii') + img_name = img.name.encode("ascii", "salt-cloud-force-ascii") else: img_name = str(img.name) # future lint: disable=blacklisted-function ret[img_name] = {} for attr in dir(img): - if attr.startswith('_') or attr == 'driver': + if attr.startswith("_") or attr == "driver": continue attr_value = getattr(img, attr) if isinstance(attr_value, six.string_types) and not six.PY3: - attr_value = attr_value.encode( - 'ascii', 'salt-cloud-force-ascii' - ) + attr_value = attr_value.encode("ascii", "salt-cloud-force-ascii") ret[img_name][attr] = attr_value return ret def avail_images(conn=None, call=None): - ''' + """ Return a dict of all available VM images on the cloud provider with relevant data - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_images function must be called with ' - '-f or --function, or with the --list-images option' + "The avail_images function must be called with " + "-f or --function, or with the --list-images option" ) if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 images = conn.list_images() ret = {} for img in images: if isinstance(img.name, six.string_types) and not six.PY3: - img_name = img.name.encode('ascii', 'salt-cloud-force-ascii') + img_name = img.name.encode("ascii", "salt-cloud-force-ascii") else: img_name = str(img.name) # future lint: disable=blacklisted-function ret[img_name] = {} for attr in dir(img): - if attr.startswith('_') or attr in ('driver', 'get_uuid'): + if attr.startswith("_") or attr in ("driver", "get_uuid"): continue attr_value = getattr(img, attr) if isinstance(attr_value, six.string_types) and not six.PY3: - attr_value = attr_value.encode( - 'ascii', 'salt-cloud-force-ascii' - ) + attr_value = attr_value.encode("ascii", "salt-cloud-force-ascii") ret[img_name][attr] = attr_value return ret def avail_sizes(conn=None, call=None): - ''' + """ Return a dict of all available VM images on the cloud provider with relevant data - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The avail_sizes function must be called with ' - '-f or --function, or with the --list-sizes option' + "The avail_sizes function must be called with " + "-f or --function, or with the --list-sizes option" ) if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 sizes = conn.list_sizes() ret = {} for size in sizes: if isinstance(size.name, six.string_types) and not six.PY3: - size_name = size.name.encode('ascii', 'salt-cloud-force-ascii') + size_name = size.name.encode("ascii", "salt-cloud-force-ascii") else: size_name = str(size.name) # future lint: disable=blacklisted-function ret[size_name] = {} for attr in dir(size): - if attr.startswith('_') or attr in ('driver', 'get_uuid'): + if attr.startswith("_") or attr in ("driver", "get_uuid"): continue try: @@ -231,32 +236,28 @@ def avail_sizes(conn=None, call=None): pass if isinstance(attr_value, six.string_types) and not six.PY3: - attr_value = attr_value.encode( - 'ascii', 'salt-cloud-force-ascii' - ) + attr_value = attr_value.encode("ascii", "salt-cloud-force-ascii") ret[size_name][attr] = attr_value return ret def get_location(conn, vm_): - ''' + """ Return the location object to use - ''' + """ locations = conn.list_locations() - vm_location = config.get_cloud_config_value('location', vm_, __opts__) + vm_location = config.get_cloud_config_value("location", vm_, __opts__) if not six.PY3: - vm_location = vm_location.encode( - 'ascii', 'salt-cloud-force-ascii' - ) + vm_location = vm_location.encode("ascii", "salt-cloud-force-ascii") for img in locations: if isinstance(img.id, six.string_types) and not six.PY3: - img_id = img.id.encode('ascii', 'salt-cloud-force-ascii') + img_id = img.id.encode("ascii", "salt-cloud-force-ascii") else: img_id = str(img.id) # future lint: disable=blacklisted-function if isinstance(img.name, six.string_types) and not six.PY3: - img_name = img.name.encode('ascii', 'salt-cloud-force-ascii') + img_name = img.name.encode("ascii", "salt-cloud-force-ascii") else: img_name = str(img.name) # future lint: disable=blacklisted-function @@ -264,30 +265,28 @@ def get_location(conn, vm_): return img raise SaltCloudNotFound( - 'The specified location, \'{0}\', could not be found.'.format( - vm_location - ) + "The specified location, '{0}', could not be found.".format(vm_location) ) def get_image(conn, vm_): - ''' + """ Return the image object to use - ''' + """ images = conn.list_images() - vm_image = config.get_cloud_config_value('image', vm_, __opts__) + vm_image = config.get_cloud_config_value("image", vm_, __opts__) if not six.PY3: - vm_image = vm_image.encode('ascii', 'salt-cloud-force-ascii') + vm_image = vm_image.encode("ascii", "salt-cloud-force-ascii") for img in images: if isinstance(img.id, six.string_types) and not six.PY3: - img_id = img.id.encode('ascii', 'salt-cloud-force-ascii') + img_id = img.id.encode("ascii", "salt-cloud-force-ascii") else: img_id = str(img.id) # future lint: disable=blacklisted-function if isinstance(img.name, six.string_types) and not six.PY3: - img_name = img.name.encode('ascii', 'salt-cloud-force-ascii') + img_name = img.name.encode("ascii", "salt-cloud-force-ascii") else: img_name = str(img.name) # future lint: disable=blacklisted-function @@ -295,187 +294,189 @@ def get_image(conn, vm_): return img raise SaltCloudNotFound( - 'The specified image, \'{0}\', could not be found.'.format(vm_image) + "The specified image, '{0}', could not be found.".format(vm_image) ) def get_size(conn, vm_): - ''' + """ Return the VM's size object - ''' + """ sizes = conn.list_sizes() - vm_size = config.get_cloud_config_value('size', vm_, __opts__) + vm_size = config.get_cloud_config_value("size", vm_, __opts__) if not vm_size: return sizes[0] for size in sizes: - if vm_size and str(vm_size) in (str(size.id), str(size.name)): # pylint: disable=blacklisted-function + if vm_size and str(vm_size) in ( + str(size.id), + str(size.name), + ): # pylint: disable=blacklisted-function return size raise SaltCloudNotFound( - 'The specified size, \'{0}\', could not be found.'.format(vm_size) + "The specified size, '{0}', could not be found.".format(vm_size) ) def script(vm_): - ''' + """ Return the script deployment object - ''' + """ return ScriptDeployment( salt.utils.cloud.os_script( - config.get_cloud_config_value('os', vm_, __opts__), + config.get_cloud_config_value("os", vm_, __opts__), vm_, __opts__, salt.utils.cloud.salt_config_to_yaml( salt.utils.cloud.minion_config(__opts__, vm_) - ) + ), ) ) def destroy(name, conn=None, call=None): - ''' + """ Delete a single VM - ''' - if call == 'function': + """ + if call == "function": raise SaltCloudSystemExit( - 'The destroy action must be called with -d, --destroy, ' - '-a or --action.' + "The destroy action must be called with -d, --destroy, " "-a or --action." ) - __utils__['cloud.fire_event']( - 'event', - 'destroying instance', - 'salt/cloud/{0}/destroying'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroying instance", + "salt/cloud/{0}/destroying".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 node = get_node(conn, name) - profiles = get_configured_provider()['profiles'] # pylint: disable=E0602 + profiles = get_configured_provider()["profiles"] # pylint: disable=E0602 if node is None: - log.error('Unable to find the VM %s', name) + log.error("Unable to find the VM %s", name) profile = None - if 'metadata' in node.extra and 'profile' in node.extra['metadata']: - profile = node.extra['metadata']['profile'] + if "metadata" in node.extra and "profile" in node.extra["metadata"]: + profile = node.extra["metadata"]["profile"] flush_mine_on_destroy = False - if profile and profile in profiles and 'flush_mine_on_destroy' in profiles[profile]: - flush_mine_on_destroy = profiles[profile]['flush_mine_on_destroy'] + if profile and profile in profiles and "flush_mine_on_destroy" in profiles[profile]: + flush_mine_on_destroy = profiles[profile]["flush_mine_on_destroy"] if flush_mine_on_destroy: - log.info('Clearing Salt Mine: %s', name) + log.info("Clearing Salt Mine: %s", name) mopts_ = salt.config.DEFAULT_MINION_OPTS - conf_path = '/'.join(__opts__['conf_file'].split('/')[:-1]) - mopts_.update( - salt.config.minion_config(os.path.join(conf_path, 'minion')) - ) + conf_path = "/".join(__opts__["conf_file"].split("/")[:-1]) + mopts_.update(salt.config.minion_config(os.path.join(conf_path, "minion"))) client = salt.client.get_local_client(mopts_) - minions = client.cmd(name, 'mine.flush') + minions = client.cmd(name, "mine.flush") - log.info('Clearing Salt Mine: %s, %s', name, flush_mine_on_destroy) - log.info('Destroying VM: %s', name) + log.info("Clearing Salt Mine: %s, %s", name, flush_mine_on_destroy) + log.info("Destroying VM: %s", name) ret = conn.destroy_node(node) if ret: - log.info('Destroyed VM: %s', name) + log.info("Destroyed VM: %s", name) # Fire destroy action - __utils__['cloud.fire_event']( - 'event', - 'destroyed instance', - 'salt/cloud/{0}/destroyed'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "destroyed instance", + "salt/cloud/{0}/destroyed".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) - if __opts__['delete_sshkeys'] is True: - public_ips = getattr(node, __opts__.get('ssh_interface', 'public_ips')) + if __opts__["delete_sshkeys"] is True: + public_ips = getattr(node, __opts__.get("ssh_interface", "public_ips")) if public_ips: salt.utils.cloud.remove_sshkey(public_ips[0]) - private_ips = getattr(node, __opts__.get('ssh_interface', 'private_ips')) + private_ips = getattr(node, __opts__.get("ssh_interface", "private_ips")) if private_ips: salt.utils.cloud.remove_sshkey(private_ips[0]) - if __opts__.get('update_cachedir', False) is True: - __utils__['cloud.delete_minion_cachedir'](name, __active_provider_name__.split(':')[0], __opts__) + if __opts__.get("update_cachedir", False) is True: + __utils__["cloud.delete_minion_cachedir"]( + name, __active_provider_name__.split(":")[0], __opts__ + ) return True - log.error('Failed to Destroy VM: %s', name) + log.error("Failed to Destroy VM: %s", name) return False def reboot(name, conn=None): - ''' + """ Reboot a single VM - ''' + """ if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 node = get_node(conn, name) if node is None: - log.error('Unable to find the VM %s', name) - log.info('Rebooting VM: %s', name) + log.error("Unable to find the VM %s", name) + log.info("Rebooting VM: %s", name) ret = conn.reboot_node(node) if ret: - log.info('Rebooted VM: %s', name) + log.info("Rebooted VM: %s", name) # Fire reboot action - __utils__['cloud.fire_event']( - 'event', - '{0} has been rebooted'.format(name), 'salt-cloud' - 'salt/cloud/{0}/rebooting'.format(name), - args={'name': name}, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'] + __utils__["cloud.fire_event"]( + "event", + "{0} has been rebooted".format(name), + "salt-cloud" "salt/cloud/{0}/rebooting".format(name), + args={"name": name}, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], ) return True - log.error('Failed to reboot VM: %s', name) + log.error("Failed to reboot VM: %s", name) return False def list_nodes(conn=None, call=None): - ''' + """ Return a list of the VMs that are on the provider - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes function must be called with -f or --function.' + "The list_nodes function must be called with -f or --function." ) if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 nodes = conn.list_nodes() ret = {} for node in nodes: ret[node.name] = { - 'id': node.id, - 'image': node.image, - 'name': node.name, - 'private_ips': node.private_ips, - 'public_ips': node.public_ips, - 'size': node.size, - 'state': node_state(node.state) + "id": node.id, + "image": node.image, + "name": node.name, + "private_ips": node.private_ips, + "public_ips": node.public_ips, + "size": node.size, + "state": node_state(node.state), } return ret def list_nodes_full(conn=None, call=None): - ''' + """ Return a list of the VMs that are on the provider, with all fields - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_full function must be called with -f or --function.' + "The list_nodes_full function must be called with -f or --function." ) if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 nodes = conn.list_nodes() ret = {} @@ -484,44 +485,46 @@ def list_nodes_full(conn=None, call=None): for key, value in zip(node.__dict__, six.itervalues(node.__dict__)): pairs[key] = value ret[node.name] = pairs - del ret[node.name]['driver'] + del ret[node.name]["driver"] - __utils__['cloud.cache_node_list'](ret, __active_provider_name__.split(':')[0], __opts__) + __utils__["cloud.cache_node_list"]( + ret, __active_provider_name__.split(":")[0], __opts__ + ) return ret def list_nodes_select(conn=None, call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' + """ if not conn: - conn = get_conn() # pylint: disable=E0602 + conn = get_conn() # pylint: disable=E0602 return salt.utils.cloud.list_nodes_select( - list_nodes_full(conn, 'function'), __opts__['query.selection'], call, + list_nodes_full(conn, "function"), __opts__["query.selection"], call, ) def show_instance(name, call=None): - ''' + """ Show the details from the provider concerning an instance - ''' - if call != 'action': + """ + if call != "action": raise SaltCloudSystemExit( - 'The show_instance action must be called with -a or --action.' + "The show_instance action must be called with -a or --action." ) nodes = list_nodes_full() - __utils__['cloud.cache_node'](nodes[name], __active_provider_name__, __opts__) + __utils__["cloud.cache_node"](nodes[name], __active_provider_name__, __opts__) return nodes[name] def conn_has_method(conn, method_name): - ''' + """ Find if the provided connection object has a specific method - ''' + """ if method_name in dir(conn): return True - log.error('Method \'%s\' not yet supported!', method_name) + log.error("Method '%s' not yet supported!", method_name) return False diff --git a/salt/config/__init__.py b/salt/config/__init__.py index 70b34ec9498..92508ef78f6 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -1,29 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" All salt configuration loading and defaults should be in this module -''' +""" # Import python libs -from __future__ import absolute_import, print_function, unicode_literals, generators +from __future__ import absolute_import, generators, print_function, unicode_literals + +import codecs +import glob +import logging import os import re import sys -import glob import time -import codecs -import logging import types from copy import deepcopy -# pylint: disable=import-error,no-name-in-module -from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlparse -# pylint: enable=import-error,no-name-in-module +import salt.defaults.exitcodes +import salt.exceptions +import salt.syspaths # Import salt libs import salt.utils.data import salt.utils.dictupdate import salt.utils.files +import salt.utils.immutabletypes as immutabletypes import salt.utils.network import salt.utils.path import salt.utils.platform @@ -33,53 +34,55 @@ import salt.utils.validate.path import salt.utils.xdg import salt.utils.yaml import salt.utils.zeromq -import salt.syspaths -import salt.exceptions -import salt.defaults.exitcodes -import salt.utils.immutabletypes as immutabletypes + +# pylint: disable=import-error,no-name-in-module +from salt.ext import six +from salt.ext.six.moves.urllib.parse import urlparse + +# pylint: enable=import-error,no-name-in-module + try: import psutil - if not hasattr(psutil, 'virtual_memory'): - raise ImportError('Version of psutil too old.') + + if not hasattr(psutil, "virtual_memory"): + raise ImportError("Version of psutil too old.") HAS_PSUTIL = True except ImportError: HAS_PSUTIL = False log = logging.getLogger(__name__) -_DFLT_LOG_DATEFMT = '%H:%M:%S' -_DFLT_LOG_DATEFMT_LOGFILE = '%Y-%m-%d %H:%M:%S' -_DFLT_LOG_FMT_CONSOLE = '[%(levelname)-8s] %(message)s' -_DFLT_LOG_FMT_LOGFILE = ( - '%(asctime)s,%(msecs)03d [%(name)-17s:%(lineno)-4d][%(levelname)-8s][%(process)d] %(message)s' -) +_DFLT_LOG_DATEFMT = "%H:%M:%S" +_DFLT_LOG_DATEFMT_LOGFILE = "%Y-%m-%d %H:%M:%S" +_DFLT_LOG_FMT_CONSOLE = "[%(levelname)-8s] %(message)s" +_DFLT_LOG_FMT_LOGFILE = "%(asctime)s,%(msecs)03d [%(name)-17s:%(lineno)-4d][%(levelname)-8s][%(process)d] %(message)s" _DFLT_LOG_FMT_JID = "[JID: %(jid)s]" -_DFLT_REFSPECS = ['+refs/heads/*:refs/remotes/origin/*', '+refs/tags/*:refs/tags/*'] +_DFLT_REFSPECS = ["+refs/heads/*:refs/remotes/origin/*", "+refs/tags/*:refs/tags/*"] DEFAULT_INTERVAL = 60 if salt.utils.platform.is_windows(): # Since an 'ipc_mode' of 'ipc' will never work on Windows due to lack of # support in ZeroMQ, we want the default to be something that has a # chance of working. - _DFLT_IPC_MODE = 'tcp' + _DFLT_IPC_MODE = "tcp" _MASTER_TRIES = -1 # This needs to be SYSTEM in order for salt-master to run as a Service # Otherwise, it will not respond to CLI calls - _MASTER_USER = 'SYSTEM' + _MASTER_USER = "SYSTEM" else: - _DFLT_IPC_MODE = 'ipc' + _DFLT_IPC_MODE = "ipc" _MASTER_TRIES = 1 _MASTER_USER = salt.utils.user.get_user() def _gather_buffer_space(): - ''' + """ Gather some system data and then calculate buffer space. Result is in bytes. - ''' + """ if HAS_PSUTIL and psutil.version_info >= (0, 6, 0): # Oh good, we have psutil. This will be quick. total_mem = psutil.virtual_memory().total @@ -87,1872 +90,1615 @@ def _gather_buffer_space(): # Avoid loading core grains unless absolutely required import platform import salt.grains.core + # We need to load up ``mem_total`` grain. Let's mimic required OS data. - os_data = {'kernel': platform.system()} + os_data = {"kernel": platform.system()} grains = salt.grains.core._memdata(os_data) - total_mem = grains['mem_total'] + total_mem = grains["mem_total"] # Return the higher number between 5% of the system memory and 10MiB return max([total_mem * 0.05, 10 << 20]) # For the time being this will be a fixed calculation # TODO: Allow user configuration -_DFLT_IPC_WBUFFER = _gather_buffer_space() * .5 +_DFLT_IPC_WBUFFER = _gather_buffer_space() * 0.5 # TODO: Reserved for future use -_DFLT_IPC_RBUFFER = _gather_buffer_space() * .5 - -VALID_OPTS = immutabletypes.freeze({ - # The address of the salt master. May be specified as IP address or hostname - 'master': (six.string_types, list), - - # The TCP/UDP port of the master to connect to in order to listen to publications - 'master_port': (six.string_types, int), - - # The behaviour of the minion when connecting to a master. Can specify 'failover', - # 'disable', 'distributed', or 'func'. If 'func' is specified, the 'master' option should be - # set to an exec module function to run to determine the master hostname. If 'disable' is - # specified the minion will run, but will not try to connect to a master. If 'distributed' - # is specified the minion will try to deterministically pick a master based on its' id. - 'master_type': six.string_types, - - # Specify the format in which the master address will be specified. Can - # specify 'default' or 'ip_only'. If 'ip_only' is specified, then the - # master address will not be split into IP and PORT. - 'master_uri_format': six.string_types, - - # The following optiosn refer to the Minion only, and they specify - # the details of the source address / port to be used when connecting to - # the Master. This is useful when dealing withmachines where due to firewall - # rules you are restricted to use a certain IP/port combination only. - 'source_interface_name': six.string_types, - 'source_address': six.string_types, - 'source_ret_port': (six.string_types, int), - 'source_publish_port': (six.string_types, int), - - # The fingerprint of the master key may be specified to increase security. Generate - # a master fingerprint with `salt-key -F master` - 'master_finger': six.string_types, - - # Deprecated in 2019.2.0. Use 'random_master' instead. - # Do not remove! Keep as an alias for usability. - 'master_shuffle': bool, - - # When in multi-master mode, temporarily remove a master from the list if a conenction - # is interrupted and try another master in the list. - 'master_alive_interval': int, - - # When in multi-master failover mode, fail back to the first master in the list if it's back - # online. - 'master_failback': bool, - - # When in multi-master mode, and master_failback is enabled ping the top master with this - # interval. - 'master_failback_interval': int, - - # The name of the signing key-pair - 'master_sign_key_name': six.string_types, - - # Sign the master auth-replies with a cryptographic signature of the masters public key. - 'master_sign_pubkey': bool, - - # Enables verification of the master-public-signature returned by the master in auth-replies. - # Must also set master_sign_pubkey for this to work - 'verify_master_pubkey_sign': bool, - - # If verify_master_pubkey_sign is enabled, the signature is only verified, if the public-key of - # the master changes. If the signature should always be verified, this can be set to True. - 'always_verify_signature': bool, - - # The name of the file in the masters pki-directory that holds the pre-calculated signature of - # the masters public-key - 'master_pubkey_signature': six.string_types, - - # Instead of computing the signature for each auth-reply, use a pre-calculated signature. - # The master_pubkey_signature must also be set for this. - 'master_use_pubkey_signature': bool, - - # Enable master stats eveents to be fired, these events will contain information about - # what commands the master is processing and what the rates are of the executions - 'master_stats': bool, - 'master_stats_event_iter': int, - # The key fingerprint of the higher-level master for the syndic to verify it is talking to the - # intended master - 'syndic_finger': six.string_types, - - # The caching mechanism to use for the PKI key store. Can substantially decrease master publish - # times. Available types: - # 'maint': Runs on a schedule as a part of the maintanence process. - # '': Disable the key cache [default] - 'key_cache': six.string_types, - - # The user under which the daemon should run - 'user': six.string_types, - - # The root directory prepended to these options: pki_dir, cachedir, - # sock_dir, log_file, autosign_file, autoreject_file, extension_modules, - # key_logfile, pidfile: - 'root_dir': six.string_types, - - # The directory used to store public key data - 'pki_dir': six.string_types, - - # A unique identifier for this daemon - 'id': six.string_types, - - # Use a module function to determine the unique identifier. If this is - # set and 'id' is not set, it will allow invocation of a module function - # to determine the value of 'id'. For simple invocations without function - # arguments, this may be a string that is the function name. For - # invocations with function arguments, this may be a dictionary with the - # key being the function name, and the value being an embedded dictionary - # where each key is a function argument name and each value is the - # corresponding argument value. - 'id_function': (dict, six.string_types), - - # The directory to store all cache files. - 'cachedir': six.string_types, - - # Append minion_id to these directories. Helps with - # multiple proxies and minions running on the same machine. - # Allowed elements in the list: pki_dir, cachedir, extension_modules, pidfile - 'append_minionid_config_dirs': list, - - # Flag to cache jobs locally. - 'cache_jobs': bool, - - # The path to the salt configuration file - 'conf_file': six.string_types, - - # The directory containing unix sockets for things like the event bus - 'sock_dir': six.string_types, - - # The pool size of unix sockets, it is necessary to avoid blocking waiting for zeromq and tcp communications. - 'sock_pool_size': int, - - # Specifies how the file server should backup files, if enabled. The backups - # live in the cache dir. - 'backup_mode': six.string_types, - - # A default renderer for all operations on this host - 'renderer': six.string_types, - - # Renderer whitelist. The only renderers from this list are allowed. - 'renderer_whitelist': list, - - # Rendrerer blacklist. Renderers from this list are disalloed even if specified in whitelist. - 'renderer_blacklist': list, - - # A flag indicating that a highstate run should immediately cease if a failure occurs. - 'failhard': bool, - - # A flag to indicate that highstate runs should force refresh the modules prior to execution - 'autoload_dynamic_modules': bool, - - # Force the minion into a single environment when it fetches files from the master - 'saltenv': (type(None), six.string_types), - - # Prevent saltenv from being overridden on the command line - 'lock_saltenv': bool, - - # Force the minion into a single pillar root when it fetches pillar data from the master - 'pillarenv': (type(None), six.string_types), - - # Make the pillarenv always match the effective saltenv - 'pillarenv_from_saltenv': bool, - - # Allows a user to provide an alternate name for top.sls - 'state_top': six.string_types, - - 'state_top_saltenv': (type(None), six.string_types), - - # States to run when a minion starts up - 'startup_states': six.string_types, - - # List of startup states - 'sls_list': list, - - # Configuration for snapper in the state system - 'snapper_states': bool, - 'snapper_states_config': six.string_types, - - # A top file to execute if startup_states == 'top' - 'top_file': six.string_types, - - # Location of the files a minion should look for. Set to 'local' to never ask the master. - 'file_client': six.string_types, - 'local': bool, - - # When using a local file_client, this parameter is used to allow the client to connect to - # a master for remote execution. - 'use_master_when_local': bool, - - # A map of saltenvs and fileserver backend locations - 'file_roots': dict, - - # A map of saltenvs and fileserver backend locations - 'pillar_roots': dict, - - # The external pillars permitted to be used on-demand using pillar.ext - 'on_demand_ext_pillar': list, - - # A map of glob paths to be used - 'decrypt_pillar': list, - - # Delimiter to use in path expressions for decrypt_pillar - 'decrypt_pillar_delimiter': six.string_types, - - # Default renderer for decrypt_pillar - 'decrypt_pillar_default': six.string_types, - - # List of renderers available for decrypt_pillar - 'decrypt_pillar_renderers': list, - - # The type of hashing algorithm to use when doing file comparisons - 'hash_type': six.string_types, - - # Order of preference for optimized .pyc files (PY3 only) - 'optimization_order': list, - - # Refuse to load these modules - 'disable_modules': list, - - # Refuse to load these returners - 'disable_returners': list, - - # Tell the loader to only load modules in this list - 'whitelist_modules': list, - - # A list of additional directories to search for salt modules in - 'module_dirs': list, - - # A list of additional directories to search for salt returners in - 'returner_dirs': list, - - # A list of additional directories to search for salt states in - 'states_dirs': list, - - # A list of additional directories to search for salt grains in - 'grains_dirs': list, - - # A list of additional directories to search for salt renderers in - 'render_dirs': list, - - # A list of additional directories to search for salt outputters in - 'outputter_dirs': list, - - # A list of additional directories to search for salt utilities in. (Used by the loader - # to populate __utils__) - 'utils_dirs': list, - - # salt cloud providers - 'providers': dict, - - # First remove all modules during any sync operation - 'clean_dynamic_modules': bool, - - # A flag indicating that a master should accept any minion connection without any authentication - 'open_mode': bool, - - # Whether or not processes should be forked when needed. The alternative is to use threading. - 'multiprocessing': bool, - - # Maximum number of concurrently active processes at any given point in time - 'process_count_max': int, - - # Whether or not the salt minion should run scheduled mine updates - 'mine_enabled': bool, - - # Whether or not scheduled mine updates should be accompanied by a job return for the job cache - 'mine_return_job': bool, - - # The number of minutes between mine updates. - 'mine_interval': int, - - # The ipc strategy. (i.e., sockets versus tcp, etc) - 'ipc_mode': six.string_types, - - # Enable ipv6 support for daemons - 'ipv6': (type(None), bool), - - # The chunk size to use when streaming files with the file server - 'file_buffer_size': int, - - # The TCP port on which minion events should be published if ipc_mode is TCP - 'tcp_pub_port': int, - - # The TCP port on which minion events should be pulled if ipc_mode is TCP - 'tcp_pull_port': int, - - # The TCP port on which events for the master should be published if ipc_mode is TCP - 'tcp_master_pub_port': int, - - # The TCP port on which events for the master should be pulled if ipc_mode is TCP - 'tcp_master_pull_port': int, - - # The TCP port on which events for the master should pulled and then republished onto - # the event bus on the master - 'tcp_master_publish_pull': int, - - # The TCP port for mworkers to connect to on the master - 'tcp_master_workers': int, - - # The file to send logging data to - 'log_file': six.string_types, - - # The level of verbosity at which to log - 'log_level': six.string_types, - - # The log level to log to a given file - 'log_level_logfile': (type(None), six.string_types), - - # The format to construct dates in log files - 'log_datefmt': six.string_types, - - # The dateformat for a given logfile - 'log_datefmt_logfile': six.string_types, - - # The format for console logs - 'log_fmt_console': six.string_types, - - # The format for a given log file - 'log_fmt_logfile': (tuple, six.string_types), - - # A dictionary of logging levels - 'log_granular_levels': dict, - - # The maximum number of bytes a single log file may contain before - # it is rotated. A value of 0 disables this feature. - # Currently only supported on Windows. On other platforms, use an - # external tool such as 'logrotate' to manage log files. - 'log_rotate_max_bytes': int, - - # The number of backup files to keep when rotating log files. Only - # used if log_rotate_max_bytes is greater than 0. - # Currently only supported on Windows. On other platforms, use an - # external tool such as 'logrotate' to manage log files. - 'log_rotate_backup_count': int, - - # If an event is above this size, it will be trimmed before putting it on the event bus - 'max_event_size': int, - - # Enable old style events to be sent on minion_startup. Change default to False in Sodium release - 'enable_legacy_startup_events': bool, - - # Always execute states with test=True if this flag is set - 'test': bool, - - # Tell the loader to attempt to import *.pyx cython files if cython is available - 'cython_enable': bool, - - # Whether or not to load grains for the GPU - 'enable_gpu_grains': bool, - - # Tell the loader to attempt to import *.zip archives - 'enable_zip_modules': bool, - - # Tell the client to show minions that have timed out - 'show_timeout': bool, - - # Tell the client to display the jid when a job is published - 'show_jid': bool, - - # Ensure that a generated jid is always unique. If this is set, the jid - # format is different due to an underscore and process id being appended - # to the jid. WARNING: A change to the jid format may break external - # applications that depend on the original format. - 'unique_jid': bool, - - # Tells the highstate outputter to show successful states. False will omit successes. - 'state_verbose': bool, - - # Specify the format for state outputs. See highstate outputter for additional details. - 'state_output': six.string_types, - - # Tells the highstate outputter to only report diffs of states that changed - 'state_output_diff': bool, - - # When true, states run in the order defined in an SLS file, unless requisites re-order them - 'state_auto_order': bool, - - # Fire events as state chunks are processed by the state compiler - 'state_events': bool, - - # The number of seconds a minion should wait before retry when attempting authentication - 'acceptance_wait_time': float, - - # The number of seconds a minion should wait before giving up during authentication - 'acceptance_wait_time_max': float, - - # Retry a connection attempt if the master rejects a minion's public key - 'rejected_retry': bool, - - # The interval in which a daemon's main loop should attempt to perform all necessary tasks - # for normal operation - 'loop_interval': float, - - # Perform pre-flight verification steps before daemon startup, such as checking configuration - # files and certain directories. - 'verify_env': bool, - - # The grains dictionary for a minion, containing specific "facts" about the minion - 'grains': dict, - - # Allow a daemon to function even if the key directories are not secured - 'permissive_pki_access': bool, - - # The passphrase of the master's private key - 'key_pass': (type(None), six.string_types), - - # The passphrase of the master's private signing key - 'signing_key_pass': (type(None), six.string_types), - - # The path to a directory to pull in configuration file includes - 'default_include': six.string_types, - - # If a minion is running an esky build of salt, upgrades can be performed using the url - # defined here. See saltutil.update() for additional information - 'update_url': (bool, six.string_types), - - # If using update_url with saltutil.update(), provide a list of services to be restarted - # post-install - 'update_restart_services': list, - - # The number of seconds to sleep between retrying an attempt to resolve the hostname of a - # salt master - 'retry_dns': float, - 'retry_dns_count': (type(None), int), - - # In the case when the resolve of the salt master hostname fails, fall back to localhost - 'resolve_dns_fallback': bool, - - # set the zeromq_reconnect_ivl option on the minion. - # http://lists.zeromq.org/pipermail/zeromq-dev/2011-January/008845.html - 'recon_max': float, - - # If recon_randomize is set, this specifies the lower bound for the randomized period - 'recon_default': float, - - # Tells the minion to choose a bounded, random interval to have zeromq attempt to reconnect - # in the event of a disconnect event - 'recon_randomize': bool, - - 'return_retry_timer': int, - 'return_retry_timer_max': int, - - # Specify one or more returners in which all events will be sent to. Requires that the returners - # in question have an event_return(event) function! - 'event_return': (list, six.string_types), - - # The number of events to queue up in memory before pushing them down the pipe to an event - # returner specified by 'event_return' - 'event_return_queue': int, - - # The number of seconds that events can languish in the queue before we flush them. - # The goal here is to ensure that if the bus is not busy enough to reach a total - # `event_return_queue` events won't get stale. - 'event_return_queue_max_seconds': int, - - # Only forward events to an event returner if it matches one of the tags in this list - 'event_return_whitelist': list, - - # Events matching a tag in this list should never be sent to an event returner. - 'event_return_blacklist': list, - - # default match type for filtering events tags: startswith, endswith, find, regex, fnmatch - 'event_match_type': six.string_types, - - # This pidfile to write out to when a daemon starts - 'pidfile': six.string_types, - - # Used with the SECO range master tops system - 'range_server': six.string_types, - - # The tcp keepalive interval to set on TCP ports. This setting can be used to tune Salt - # connectivity issues in messy network environments with misbehaving firewalls - 'tcp_keepalive': bool, - - # Sets zeromq TCP keepalive idle. May be used to tune issues with minion disconnects - 'tcp_keepalive_idle': float, - - # Sets zeromq TCP keepalive count. May be used to tune issues with minion disconnects - 'tcp_keepalive_cnt': float, - - # Sets zeromq TCP keepalive interval. May be used to tune issues with minion disconnects. - 'tcp_keepalive_intvl': float, - - # The network interface for a daemon to bind to - 'interface': six.string_types, - - # The port for a salt master to broadcast publications on. This will also be the port minions - # connect to to listen for publications. - 'publish_port': int, - - # TODO unknown option! - 'auth_mode': int, - - # listen queue size / backlog - 'zmq_backlog': int, - - # Set the zeromq high water mark on the publisher interface. - # http://api.zeromq.org/3-2:zmq-setsockopt - 'pub_hwm': int, - - # IPC buffer size - # Refs https://github.com/saltstack/salt/issues/34215 - 'ipc_write_buffer': int, - - # The number of MWorker processes for a master to startup. This number needs to scale up as - # the number of connected minions increases. - 'worker_threads': int, - - # The port for the master to listen to returns on. The minion needs to connect to this port - # to send returns. - 'ret_port': int, - - # The number of hours to keep jobs around in the job cache on the master - 'keep_jobs': int, - - # If the returner supports `clean_old_jobs`, then at cleanup time, - # archive the job data before deleting it. - 'archive_jobs': bool, - - # A master-only copy of the file_roots dictionary, used by the state compiler - 'master_roots': dict, - - # Add the proxymodule LazyLoader object to opts. This breaks many things - # but this was the default pre 2015.8.2. This should default to - # False in 2016.3.0 - 'add_proxymodule_to_opts': bool, - - # Merge pillar data into configuration opts. - # As multiple proxies can run on the same server, we may need different - # configuration options for each, while there's one single configuration file. - # The solution is merging the pillar data of each proxy minion into the opts. - 'proxy_merge_pillar_in_opts': bool, - - # Deep merge of pillar data into configuration opts. - # Evaluated only when `proxy_merge_pillar_in_opts` is True. - 'proxy_deep_merge_pillar_in_opts': bool, - - # The strategy used when merging pillar into opts. - # Considered only when `proxy_merge_pillar_in_opts` is True. - 'proxy_merge_pillar_in_opts_strategy': six.string_types, - - # Allow enabling mine details using pillar data. - 'proxy_mines_pillar': bool, - - # In some particular cases, always alive proxies are not beneficial. - # This option can be used in those less dynamic environments: - # the user can request the connection - # always alive, or init-shutdown per command. - 'proxy_always_alive': bool, - - # Poll the connection state with the proxy minion - # If enabled, this option requires the function `alive` - # to be implemented in the proxy module - 'proxy_keep_alive': bool, - - # Frequency of the proxy_keep_alive, in minutes - 'proxy_keep_alive_interval': int, - - # Update intervals - 'roots_update_interval': int, - 'azurefs_update_interval': int, - 'gitfs_update_interval': int, - 'git_pillar_update_interval': int, - 'hgfs_update_interval': int, - 'minionfs_update_interval': int, - 's3fs_update_interval': int, - 'svnfs_update_interval': int, - - # NOTE: git_pillar_base, git_pillar_branch, git_pillar_env, and - # git_pillar_root omitted here because their values could conceivably be - # loaded as non-string types, which is OK because git_pillar will normalize - # them to strings. But rather than include all the possible types they - # could be, we'll just skip type-checking. - 'git_pillar_ssl_verify': bool, - 'git_pillar_global_lock': bool, - 'git_pillar_user': six.string_types, - 'git_pillar_password': six.string_types, - 'git_pillar_insecure_auth': bool, - 'git_pillar_privkey': six.string_types, - 'git_pillar_pubkey': six.string_types, - 'git_pillar_passphrase': six.string_types, - 'git_pillar_refspecs': list, - 'git_pillar_includes': bool, - 'git_pillar_verify_config': bool, - # NOTE: gitfs_base, gitfs_mountpoint, and gitfs_root omitted here because - # their values could conceivably be loaded as non-string types, which is OK - # because gitfs will normalize them to strings. But rather than include all - # the possible types they could be, we'll just skip type-checking. - 'gitfs_remotes': list, - 'gitfs_insecure_auth': bool, - 'gitfs_privkey': six.string_types, - 'gitfs_pubkey': six.string_types, - 'gitfs_passphrase': six.string_types, - 'gitfs_saltenv_whitelist': list, - 'gitfs_saltenv_blacklist': list, - 'gitfs_ssl_verify': bool, - 'gitfs_global_lock': bool, - 'gitfs_saltenv': list, - 'gitfs_ref_types': list, - 'gitfs_refspecs': list, - 'gitfs_disable_saltenv_mapping': bool, - 'hgfs_remotes': list, - 'hgfs_mountpoint': six.string_types, - 'hgfs_root': six.string_types, - 'hgfs_base': six.string_types, - 'hgfs_branch_method': six.string_types, - 'hgfs_saltenv_whitelist': list, - 'hgfs_saltenv_blacklist': list, - 'svnfs_remotes': list, - 'svnfs_mountpoint': six.string_types, - 'svnfs_root': six.string_types, - 'svnfs_trunk': six.string_types, - 'svnfs_branches': six.string_types, - 'svnfs_tags': six.string_types, - 'svnfs_saltenv_whitelist': list, - 'svnfs_saltenv_blacklist': list, - 'minionfs_env': six.string_types, - 'minionfs_mountpoint': six.string_types, - 'minionfs_whitelist': list, - 'minionfs_blacklist': list, - - # Specify a list of external pillar systems to use - 'ext_pillar': list, - - # Reserved for future use to version the pillar structure - 'pillar_version': int, - - # Whether or not a copy of the master opts dict should be rendered into minion pillars - 'pillar_opts': bool, - - # Cache the master pillar to disk to avoid having to pass through the rendering system - 'pillar_cache': bool, - - # Pillar cache TTL, in seconds. Has no effect unless `pillar_cache` is True - 'pillar_cache_ttl': int, - - # Pillar cache backend. Defaults to `disk` which stores caches in the master cache - 'pillar_cache_backend': six.string_types, - - 'pillar_safe_render_error': bool, - - # When creating a pillar, there are several strategies to choose from when - # encountering duplicate values - 'pillar_source_merging_strategy': six.string_types, - - # Recursively merge lists by aggregating them instead of replacing them. - 'pillar_merge_lists': bool, - - # If True, values from included pillar SLS targets will override - 'pillar_includes_override_sls': bool, - - # How to merge multiple top files from multiple salt environments - # (saltenvs); can be 'merge' or 'same' - 'top_file_merging_strategy': six.string_types, - - # The ordering for salt environment merging, when top_file_merging_strategy - # is set to 'same' - 'env_order': list, - - # The salt environment which provides the default top file when - # top_file_merging_strategy is set to 'same'; defaults to 'base' - 'default_top': six.string_types, - - 'ping_on_rotate': bool, - 'peer': dict, - 'preserve_minion_cache': bool, - 'syndic_master': (six.string_types, list), - - # The behaviour of the multimaster syndic when connection to a master of masters failed. Can - # specify 'random' (default) or 'ordered'. If set to 'random' masters will be iterated in random - # order if 'ordered' the configured order will be used. - 'syndic_failover': six.string_types, - 'syndic_forward_all_events': bool, - 'runner_dirs': list, - 'client_acl_verify': bool, - 'publisher_acl': dict, - 'publisher_acl_blacklist': dict, - 'sudo_acl': bool, - 'external_auth': dict, - 'token_expire': int, - 'token_expire_user_override': (bool, dict), - 'file_recv': bool, - 'file_recv_max_size': int, - 'file_ignore_regex': (list, six.string_types), - 'file_ignore_glob': (list, six.string_types), - 'fileserver_backend': list, - 'fileserver_followsymlinks': bool, - 'fileserver_ignoresymlinks': bool, - 'fileserver_limit_traversal': bool, - 'fileserver_verify_config': bool, - - # Optionally apply '*' permissioins to any user. By default '*' is a fallback case that is - # applied only if the user didn't matched by other matchers. - 'permissive_acl': bool, - - # Optionally enables keeping the calculated user's auth list in the token file. - 'keep_acl_in_token': bool, - - # Auth subsystem module to use to get authorized access list for a user. By default it's the - # same module used for external authentication. - 'eauth_acl_module': six.string_types, - - # Subsystem to use to maintain eauth tokens. By default, tokens are stored on the local - # filesystem - 'eauth_tokens': six.string_types, - - # The number of open files a daemon is allowed to have open. Frequently needs to be increased - # higher than the system default in order to account for the way zeromq consumes file handles. - 'max_open_files': int, - - # Automatically accept any key provided to the master. Implies that the key will be preserved - # so that subsequent connections will be authenticated even if this option has later been - # turned off. - 'auto_accept': bool, - 'autosign_timeout': int, - - # A mapping of external systems that can be used to generate topfile data. - 'master_tops': dict, - - # Whether or not matches from master_tops should be executed before or - # after those from the top file(s). - 'master_tops_first': bool, - - # A flag that should be set on a top-level master when it is ordering around subordinate masters - # via the use of a salt syndic - 'order_masters': bool, - - # Whether or not to cache jobs so that they can be examined later on - 'job_cache': bool, - - # Define a returner to be used as an external job caching storage backend - 'ext_job_cache': six.string_types, - - # Specify a returner for the master to use as a backend storage system to cache jobs returns - # that it receives - 'master_job_cache': six.string_types, - - # Specify whether the master should store end times for jobs as returns come in - 'job_cache_store_endtime': bool, - - # The minion data cache is a cache of information about the minions stored on the master. - # This information is primarily the pillar and grains data. The data is cached in the master - # cachedir under the name of the minion and used to predetermine what minions are expected to - # reply from executions. - 'minion_data_cache': bool, - - # The number of seconds between AES key rotations on the master - 'publish_session': int, - - # Defines a salt reactor. See http://docs.saltstack.com/en/latest/topics/reactor/ - 'reactor': list, - - # The TTL for the cache of the reactor configuration - 'reactor_refresh_interval': int, - - # The number of workers for the runner/wheel in the reactor - 'reactor_worker_threads': int, - - # The queue size for workers in the reactor - 'reactor_worker_hwm': int, - - # Defines engines. See https://docs.saltstack.com/en/latest/topics/engines/ - 'engines': list, - - # Whether or not to store runner returns in the job cache - 'runner_returns': bool, - - 'serial': six.string_types, - 'search': six.string_types, - - # A compound target definition. - # See: http://docs.saltstack.com/en/latest/topics/targeting/nodegroups.html - 'nodegroups': (dict, list), - - # List-only nodegroups for salt-ssh. Each group must be formed as either a - # comma-separated list, or a YAML list. - 'ssh_list_nodegroups': dict, - - # By default, salt-ssh uses its own specially-generated RSA key to auth - # against minions. If this is set to True, salt-ssh will look in - # for a key at ~/.ssh/id_rsa, and fall back to using its own specially- - # generated RSA key if that file doesn't exist. - 'ssh_use_home_key': bool, - - # The logfile location for salt-key - 'key_logfile': six.string_types, - - # The upper bound for the random number of seconds that a minion should - # delay when starting in up before it connects to a master. This can be - # used to mitigate a thundering-herd scenario when many minions start up - # at once and attempt to all connect immediately to the master - 'random_startup_delay': int, - - # The source location for the winrepo sls files - # (used by win_pkg.py, minion only) - 'winrepo_source_dir': six.string_types, - - 'winrepo_dir': six.string_types, - 'winrepo_dir_ng': six.string_types, - 'winrepo_cachefile': six.string_types, - # NOTE: winrepo_branch omitted here because its value could conceivably be - # loaded as a non-string type, which is OK because winrepo will normalize - # them to strings. But rather than include all the possible types it could - # be, we'll just skip type-checking. - 'winrepo_cache_expire_max': int, - 'winrepo_cache_expire_min': int, - 'winrepo_remotes': list, - 'winrepo_remotes_ng': list, - 'winrepo_ssl_verify': bool, - 'winrepo_user': six.string_types, - 'winrepo_password': six.string_types, - 'winrepo_insecure_auth': bool, - 'winrepo_privkey': six.string_types, - 'winrepo_pubkey': six.string_types, - 'winrepo_passphrase': six.string_types, - 'winrepo_refspecs': list, - - # Set a hard limit for the amount of memory modules can consume on a minion. - 'modules_max_memory': int, - - # Blacklist specific core grains to be filtered - 'grains_blacklist': list, - - # The number of minutes between the minion refreshing its cache of grains - 'grains_refresh_every': int, - - # Use lspci to gather system data for grains on a minion - 'enable_lspci': bool, - - # The number of seconds for the salt client to wait for additional syndics to - # check in with their lists of expected minions before giving up - 'syndic_wait': int, - - # Override Jinja environment option defaults for all templates except sls templates - 'jinja_env': dict, - - # Set Jinja environment options for sls templates - 'jinja_sls_env': dict, - - # If this is set to True leading spaces and tabs are stripped from the start - # of a line to a block. - 'jinja_lstrip_blocks': bool, - - # If this is set to True the first newline after a Jinja block is removed - 'jinja_trim_blocks': bool, - - # Cache minion ID to file - 'minion_id_caching': bool, - - # Always generate minion id in lowercase. - 'minion_id_lowercase': bool, - - # Remove either a single domain (foo.org), or all (True) from a generated minion id. - 'minion_id_remove_domain': (six.string_types, bool), - - # If set, the master will sign all publications before they are sent out - 'sign_pub_messages': bool, - - # The size of key that should be generated when creating new keys - 'keysize': int, - - # The transport system for this daemon. (i.e. zeromq, tcp, detect, etc) - 'transport': six.string_types, - - # The number of seconds to wait when the client is requesting information about running jobs - 'gather_job_timeout': int, - - # The number of seconds to wait before timing out an authentication request - 'auth_timeout': int, - - # The number of attempts to authenticate to a master before giving up - 'auth_tries': int, - - # The number of attempts to connect to a master before giving up. - # Set this to -1 for unlimited attempts. This allows for a master to have - # downtime and the minion to reconnect to it later when it comes back up. - # In 'failover' mode, it is the number of attempts for each set of masters. - # In this mode, it will cycle through the list of masters for each attempt. - 'master_tries': int, - - # Never give up when trying to authenticate to a master - 'auth_safemode': bool, - - # Selects a random master when starting a minion up in multi-master mode or - # when starting a minion with salt-call. ``master`` must be a list. - 'random_master': bool, - - # An upper bound for the amount of time for a minion to sleep before attempting to - # reauth after a restart. - 'random_reauth_delay': int, - - # The number of seconds for a syndic to poll for new messages that need to be forwarded - 'syndic_event_forward_timeout': float, - - # The length that the syndic event queue must hit before events are popped off and forwarded - 'syndic_jid_forward_cache_hwm': int, - - # Salt SSH configuration - 'ssh_passwd': six.string_types, - 'ssh_port': six.string_types, - 'ssh_sudo': bool, - 'ssh_sudo_user': six.string_types, - 'ssh_timeout': float, - 'ssh_user': six.string_types, - 'ssh_scan_ports': six.string_types, - 'ssh_scan_timeout': float, - 'ssh_identities_only': bool, - 'ssh_log_file': six.string_types, - 'ssh_config_file': six.string_types, - 'ssh_merge_pillar': bool, - - 'cluster_mode': bool, - 'sqlite_queue_dir': six.string_types, - 'queue_dirs': list, - - # Instructs the minion to ping its master(s) every n number of minutes. Used - # primarily as a mitigation technique against minion disconnects. - 'ping_interval': int, - - # Instructs the salt CLI to print a summary of a minion responses before returning - 'cli_summary': bool, - - # The maximum number of minion connections allowed by the master. Can have performance - # implications in large setups. - 'max_minions': int, - - - 'username': (type(None), six.string_types), - 'password': (type(None), six.string_types), - - # Use zmq.SUSCRIBE to limit listening sockets to only process messages bound for them - 'zmq_filtering': bool, - - # Connection caching. Can greatly speed up salt performance. - 'con_cache': bool, - 'rotate_aes_key': bool, - - # Cache ZeroMQ connections. Can greatly improve salt performance. - 'cache_sreqs': bool, - - # Can be set to override the python_shell=False default in the cmd module - 'cmd_safe': bool, - - # Used by salt-api for master requests timeout - 'rest_timeout': int, - - # If set, all minion exec module actions will be rerouted through sudo as this user - 'sudo_user': six.string_types, - - # HTTP connection timeout in seconds. Applied for tornado http fetch functions like cp.get_url - # should be greater than overall download time - 'http_connect_timeout': float, - - # HTTP request timeout in seconds. Applied for tornado http fetch functions like cp.get_url - # should be greater than overall download time - 'http_request_timeout': float, - - # HTTP request max file content size. - 'http_max_body': int, - - # Delay in seconds before executing bootstrap (Salt Cloud) - 'bootstrap_delay': int, - - # If a proxymodule has a function called 'grains', then call it during - # regular grains loading and merge the results with the proxy's grains - # dictionary. Otherwise it is assumed that the module calls the grains - # function in a custom way and returns the data elsewhere - # - # Default to False for 2016.3 and 2016.11. Switch to True for 2017.7.0 - 'proxy_merge_grains_in_module': bool, - - # Command to use to restart salt-minion - 'minion_restart_command': list, - - # Whether or not a minion should send the results of a command back to the master - # Useful when a returner is the source of truth for a job result - 'pub_ret': bool, - - # HTTP proxy settings. Used in tornado fetch functions, apt-key etc - 'proxy_host': six.string_types, - 'proxy_username': six.string_types, - 'proxy_password': six.string_types, - 'proxy_port': int, - # Exclude list of hostnames from proxy - 'no_proxy': list, - - # Minion de-dup jid cache max size - 'minion_jid_queue_hwm': int, - - # Minion data cache driver (one of satl.cache.* modules) - 'cache': six.string_types, - # Enables a fast in-memory cache booster and sets the expiration time. - 'memcache_expire_seconds': int, - # Set a memcache limit in items (bank + key) per cache storage (driver + driver_opts). - 'memcache_max_items': int, - # Each time a cache storage got full cleanup all the expired items not just the oldest one. - 'memcache_full_cleanup': bool, - # Enable collecting the memcache stats and log it on `debug` log level. - 'memcache_debug': bool, - - # Thin and minimal Salt extra modules - 'thin_extra_mods': six.string_types, - 'min_extra_mods': six.string_types, - - # Default returners minion should use. List or comma-delimited string - 'return': (six.string_types, list), - - # TLS/SSL connection options. This could be set to a dictionary containing arguments - # corresponding to python ssl.wrap_socket method. For details see: - # http://www.tornadoweb.org/en/stable/tcpserver.html#tornado.tcpserver.TCPServer - # http://docs.python.org/2/library/ssl.html#ssl.wrap_socket - # Note: to set enum arguments values like `cert_reqs` and `ssl_version` use constant names - # without ssl module prefix: `CERT_REQUIRED` or `PROTOCOL_SSLv23`. - 'ssl': (dict, bool, type(None)), - - # Controls how a multi-function job returns its data. If this is False, - # it will return its data using a dictionary with the function name as - # the key. This is compatible with legacy systems. If this is True, it - # will return its data using an array in the same order as the input - # array of functions to execute. This allows for calling the same - # function multiple times in the same multi-function job. - 'multifunc_ordered': bool, - - # Controls whether beacons are set up before a connection - # to the master is attempted. - 'beacons_before_connect': bool, - - # Controls whether the scheduler is set up before a connection - # to the master is attempted. - 'scheduler_before_connect': bool, - - # Whitelist/blacklist specific modules to be synced - 'extmod_whitelist': dict, - 'extmod_blacklist': dict, - - # django auth - 'django_auth_path': six.string_types, - 'django_auth_settings': six.string_types, - - # Number of times to try to auth with the master on a reconnect with the - # tcp transport - 'tcp_authentication_retries': int, - - # Permit or deny allowing minions to request revoke of its own key - 'allow_minion_key_revoke': bool, - - # File chunk size for salt-cp - 'salt_cp_chunk_size': int, - - # Require that the minion sign messages it posts to the master on the event - # bus - 'minion_sign_messages': bool, - - # Have master drop messages from minions for which their signatures do - # not verify - 'drop_messages_signature_fail': bool, - - # Require that payloads from minions have a 'sig' entry - # (in other words, require that minions have 'minion_sign_messages' - # turned on) - 'require_minion_sign_messages': bool, - - # The list of config entries to be passed to external pillar function as - # part of the extra_minion_data param - # Subconfig entries can be specified by using the ':' notation (e.g. key:subkey) - 'pass_to_ext_pillars': (six.string_types, list), - - # SSDP discovery publisher description. - # Contains publisher configuration and minion mapping. - # Setting it to False disables discovery - 'discovery': (dict, bool), - - # Scheduler should be a dictionary - 'schedule': dict, - - # Whether to fire auth events - 'auth_events': bool, - - # Whether to fire Minion data cache refresh events - 'minion_data_cache_events': bool, - - # Enable calling ssh minions from the salt master - 'enable_ssh_minions': bool, - - # Thorium saltenv - 'thoriumenv': (type(None), six.string_types), - - # Thorium top file location - 'thorium_top': six.string_types, - - # Allow raw_shell option when using the ssh - # client via the Salt API - 'netapi_allow_raw_shell': bool, -}) +_DFLT_IPC_RBUFFER = _gather_buffer_space() * 0.5 + +VALID_OPTS = immutabletypes.freeze( + { + # The address of the salt master. May be specified as IP address or hostname + "master": (six.string_types, list), + # The TCP/UDP port of the master to connect to in order to listen to publications + "master_port": (six.string_types, int), + # The behaviour of the minion when connecting to a master. Can specify 'failover', + # 'disable', 'distributed', or 'func'. If 'func' is specified, the 'master' option should be + # set to an exec module function to run to determine the master hostname. If 'disable' is + # specified the minion will run, but will not try to connect to a master. If 'distributed' + # is specified the minion will try to deterministically pick a master based on its' id. + "master_type": six.string_types, + # Specify the format in which the master address will be specified. Can + # specify 'default' or 'ip_only'. If 'ip_only' is specified, then the + # master address will not be split into IP and PORT. + "master_uri_format": six.string_types, + # The following optiosn refer to the Minion only, and they specify + # the details of the source address / port to be used when connecting to + # the Master. This is useful when dealing withmachines where due to firewall + # rules you are restricted to use a certain IP/port combination only. + "source_interface_name": six.string_types, + "source_address": six.string_types, + "source_ret_port": (six.string_types, int), + "source_publish_port": (six.string_types, int), + # The fingerprint of the master key may be specified to increase security. Generate + # a master fingerprint with `salt-key -F master` + "master_finger": six.string_types, + # Deprecated in 2019.2.0. Use 'random_master' instead. + # Do not remove! Keep as an alias for usability. + "master_shuffle": bool, + # When in multi-master mode, temporarily remove a master from the list if a conenction + # is interrupted and try another master in the list. + "master_alive_interval": int, + # When in multi-master failover mode, fail back to the first master in the list if it's back + # online. + "master_failback": bool, + # When in multi-master mode, and master_failback is enabled ping the top master with this + # interval. + "master_failback_interval": int, + # The name of the signing key-pair + "master_sign_key_name": six.string_types, + # Sign the master auth-replies with a cryptographic signature of the masters public key. + "master_sign_pubkey": bool, + # Enables verification of the master-public-signature returned by the master in auth-replies. + # Must also set master_sign_pubkey for this to work + "verify_master_pubkey_sign": bool, + # If verify_master_pubkey_sign is enabled, the signature is only verified, if the public-key of + # the master changes. If the signature should always be verified, this can be set to True. + "always_verify_signature": bool, + # The name of the file in the masters pki-directory that holds the pre-calculated signature of + # the masters public-key + "master_pubkey_signature": six.string_types, + # Instead of computing the signature for each auth-reply, use a pre-calculated signature. + # The master_pubkey_signature must also be set for this. + "master_use_pubkey_signature": bool, + # Enable master stats eveents to be fired, these events will contain information about + # what commands the master is processing and what the rates are of the executions + "master_stats": bool, + "master_stats_event_iter": int, + # The key fingerprint of the higher-level master for the syndic to verify it is talking to the + # intended master + "syndic_finger": six.string_types, + # The caching mechanism to use for the PKI key store. Can substantially decrease master publish + # times. Available types: + # 'maint': Runs on a schedule as a part of the maintanence process. + # '': Disable the key cache [default] + "key_cache": six.string_types, + # The user under which the daemon should run + "user": six.string_types, + # The root directory prepended to these options: pki_dir, cachedir, + # sock_dir, log_file, autosign_file, autoreject_file, extension_modules, + # key_logfile, pidfile: + "root_dir": six.string_types, + # The directory used to store public key data + "pki_dir": six.string_types, + # A unique identifier for this daemon + "id": six.string_types, + # Use a module function to determine the unique identifier. If this is + # set and 'id' is not set, it will allow invocation of a module function + # to determine the value of 'id'. For simple invocations without function + # arguments, this may be a string that is the function name. For + # invocations with function arguments, this may be a dictionary with the + # key being the function name, and the value being an embedded dictionary + # where each key is a function argument name and each value is the + # corresponding argument value. + "id_function": (dict, six.string_types), + # The directory to store all cache files. + "cachedir": six.string_types, + # Append minion_id to these directories. Helps with + # multiple proxies and minions running on the same machine. + # Allowed elements in the list: pki_dir, cachedir, extension_modules, pidfile + "append_minionid_config_dirs": list, + # Flag to cache jobs locally. + "cache_jobs": bool, + # The path to the salt configuration file + "conf_file": six.string_types, + # The directory containing unix sockets for things like the event bus + "sock_dir": six.string_types, + # The pool size of unix sockets, it is necessary to avoid blocking waiting for zeromq and tcp communications. + "sock_pool_size": int, + # Specifies how the file server should backup files, if enabled. The backups + # live in the cache dir. + "backup_mode": six.string_types, + # A default renderer for all operations on this host + "renderer": six.string_types, + # Renderer whitelist. The only renderers from this list are allowed. + "renderer_whitelist": list, + # Rendrerer blacklist. Renderers from this list are disalloed even if specified in whitelist. + "renderer_blacklist": list, + # A flag indicating that a highstate run should immediately cease if a failure occurs. + "failhard": bool, + # A flag to indicate that highstate runs should force refresh the modules prior to execution + "autoload_dynamic_modules": bool, + # Force the minion into a single environment when it fetches files from the master + "saltenv": (type(None), six.string_types), + # Prevent saltenv from being overridden on the command line + "lock_saltenv": bool, + # Force the minion into a single pillar root when it fetches pillar data from the master + "pillarenv": (type(None), six.string_types), + # Make the pillarenv always match the effective saltenv + "pillarenv_from_saltenv": bool, + # Allows a user to provide an alternate name for top.sls + "state_top": six.string_types, + "state_top_saltenv": (type(None), six.string_types), + # States to run when a minion starts up + "startup_states": six.string_types, + # List of startup states + "sls_list": list, + # Configuration for snapper in the state system + "snapper_states": bool, + "snapper_states_config": six.string_types, + # A top file to execute if startup_states == 'top' + "top_file": six.string_types, + # Location of the files a minion should look for. Set to 'local' to never ask the master. + "file_client": six.string_types, + "local": bool, + # When using a local file_client, this parameter is used to allow the client to connect to + # a master for remote execution. + "use_master_when_local": bool, + # A map of saltenvs and fileserver backend locations + "file_roots": dict, + # A map of saltenvs and fileserver backend locations + "pillar_roots": dict, + # The external pillars permitted to be used on-demand using pillar.ext + "on_demand_ext_pillar": list, + # A map of glob paths to be used + "decrypt_pillar": list, + # Delimiter to use in path expressions for decrypt_pillar + "decrypt_pillar_delimiter": six.string_types, + # Default renderer for decrypt_pillar + "decrypt_pillar_default": six.string_types, + # List of renderers available for decrypt_pillar + "decrypt_pillar_renderers": list, + # The type of hashing algorithm to use when doing file comparisons + "hash_type": six.string_types, + # Order of preference for optimized .pyc files (PY3 only) + "optimization_order": list, + # Refuse to load these modules + "disable_modules": list, + # Refuse to load these returners + "disable_returners": list, + # Tell the loader to only load modules in this list + "whitelist_modules": list, + # A list of additional directories to search for salt modules in + "module_dirs": list, + # A list of additional directories to search for salt returners in + "returner_dirs": list, + # A list of additional directories to search for salt states in + "states_dirs": list, + # A list of additional directories to search for salt grains in + "grains_dirs": list, + # A list of additional directories to search for salt renderers in + "render_dirs": list, + # A list of additional directories to search for salt outputters in + "outputter_dirs": list, + # A list of additional directories to search for salt utilities in. (Used by the loader + # to populate __utils__) + "utils_dirs": list, + # salt cloud providers + "providers": dict, + # First remove all modules during any sync operation + "clean_dynamic_modules": bool, + # A flag indicating that a master should accept any minion connection without any authentication + "open_mode": bool, + # Whether or not processes should be forked when needed. The alternative is to use threading. + "multiprocessing": bool, + # Maximum number of concurrently active processes at any given point in time + "process_count_max": int, + # Whether or not the salt minion should run scheduled mine updates + "mine_enabled": bool, + # Whether or not scheduled mine updates should be accompanied by a job return for the job cache + "mine_return_job": bool, + # The number of minutes between mine updates. + "mine_interval": int, + # The ipc strategy. (i.e., sockets versus tcp, etc) + "ipc_mode": six.string_types, + # Enable ipv6 support for daemons + "ipv6": (type(None), bool), + # The chunk size to use when streaming files with the file server + "file_buffer_size": int, + # The TCP port on which minion events should be published if ipc_mode is TCP + "tcp_pub_port": int, + # The TCP port on which minion events should be pulled if ipc_mode is TCP + "tcp_pull_port": int, + # The TCP port on which events for the master should be published if ipc_mode is TCP + "tcp_master_pub_port": int, + # The TCP port on which events for the master should be pulled if ipc_mode is TCP + "tcp_master_pull_port": int, + # The TCP port on which events for the master should pulled and then republished onto + # the event bus on the master + "tcp_master_publish_pull": int, + # The TCP port for mworkers to connect to on the master + "tcp_master_workers": int, + # The file to send logging data to + "log_file": six.string_types, + # The level of verbosity at which to log + "log_level": six.string_types, + # The log level to log to a given file + "log_level_logfile": (type(None), six.string_types), + # The format to construct dates in log files + "log_datefmt": six.string_types, + # The dateformat for a given logfile + "log_datefmt_logfile": six.string_types, + # The format for console logs + "log_fmt_console": six.string_types, + # The format for a given log file + "log_fmt_logfile": (tuple, six.string_types), + # A dictionary of logging levels + "log_granular_levels": dict, + # The maximum number of bytes a single log file may contain before + # it is rotated. A value of 0 disables this feature. + # Currently only supported on Windows. On other platforms, use an + # external tool such as 'logrotate' to manage log files. + "log_rotate_max_bytes": int, + # The number of backup files to keep when rotating log files. Only + # used if log_rotate_max_bytes is greater than 0. + # Currently only supported on Windows. On other platforms, use an + # external tool such as 'logrotate' to manage log files. + "log_rotate_backup_count": int, + # If an event is above this size, it will be trimmed before putting it on the event bus + "max_event_size": int, + # Enable old style events to be sent on minion_startup. Change default to False in Sodium release + "enable_legacy_startup_events": bool, + # Always execute states with test=True if this flag is set + "test": bool, + # Tell the loader to attempt to import *.pyx cython files if cython is available + "cython_enable": bool, + # Whether or not to load grains for the GPU + "enable_gpu_grains": bool, + # Tell the loader to attempt to import *.zip archives + "enable_zip_modules": bool, + # Tell the client to show minions that have timed out + "show_timeout": bool, + # Tell the client to display the jid when a job is published + "show_jid": bool, + # Ensure that a generated jid is always unique. If this is set, the jid + # format is different due to an underscore and process id being appended + # to the jid. WARNING: A change to the jid format may break external + # applications that depend on the original format. + "unique_jid": bool, + # Tells the highstate outputter to show successful states. False will omit successes. + "state_verbose": bool, + # Specify the format for state outputs. See highstate outputter for additional details. + "state_output": six.string_types, + # Tells the highstate outputter to only report diffs of states that changed + "state_output_diff": bool, + # When true, states run in the order defined in an SLS file, unless requisites re-order them + "state_auto_order": bool, + # Fire events as state chunks are processed by the state compiler + "state_events": bool, + # The number of seconds a minion should wait before retry when attempting authentication + "acceptance_wait_time": float, + # The number of seconds a minion should wait before giving up during authentication + "acceptance_wait_time_max": float, + # Retry a connection attempt if the master rejects a minion's public key + "rejected_retry": bool, + # The interval in which a daemon's main loop should attempt to perform all necessary tasks + # for normal operation + "loop_interval": float, + # Perform pre-flight verification steps before daemon startup, such as checking configuration + # files and certain directories. + "verify_env": bool, + # The grains dictionary for a minion, containing specific "facts" about the minion + "grains": dict, + # Allow a daemon to function even if the key directories are not secured + "permissive_pki_access": bool, + # The passphrase of the master's private key + "key_pass": (type(None), six.string_types), + # The passphrase of the master's private signing key + "signing_key_pass": (type(None), six.string_types), + # The path to a directory to pull in configuration file includes + "default_include": six.string_types, + # If a minion is running an esky build of salt, upgrades can be performed using the url + # defined here. See saltutil.update() for additional information + "update_url": (bool, six.string_types), + # If using update_url with saltutil.update(), provide a list of services to be restarted + # post-install + "update_restart_services": list, + # The number of seconds to sleep between retrying an attempt to resolve the hostname of a + # salt master + "retry_dns": float, + "retry_dns_count": (type(None), int), + # In the case when the resolve of the salt master hostname fails, fall back to localhost + "resolve_dns_fallback": bool, + # set the zeromq_reconnect_ivl option on the minion. + # http://lists.zeromq.org/pipermail/zeromq-dev/2011-January/008845.html + "recon_max": float, + # If recon_randomize is set, this specifies the lower bound for the randomized period + "recon_default": float, + # Tells the minion to choose a bounded, random interval to have zeromq attempt to reconnect + # in the event of a disconnect event + "recon_randomize": bool, + "return_retry_timer": int, + "return_retry_timer_max": int, + # Specify one or more returners in which all events will be sent to. Requires that the returners + # in question have an event_return(event) function! + "event_return": (list, six.string_types), + # The number of events to queue up in memory before pushing them down the pipe to an event + # returner specified by 'event_return' + "event_return_queue": int, + # The number of seconds that events can languish in the queue before we flush them. + # The goal here is to ensure that if the bus is not busy enough to reach a total + # `event_return_queue` events won't get stale. + "event_return_queue_max_seconds": int, + # Only forward events to an event returner if it matches one of the tags in this list + "event_return_whitelist": list, + # Events matching a tag in this list should never be sent to an event returner. + "event_return_blacklist": list, + # default match type for filtering events tags: startswith, endswith, find, regex, fnmatch + "event_match_type": six.string_types, + # This pidfile to write out to when a daemon starts + "pidfile": six.string_types, + # Used with the SECO range master tops system + "range_server": six.string_types, + # The tcp keepalive interval to set on TCP ports. This setting can be used to tune Salt + # connectivity issues in messy network environments with misbehaving firewalls + "tcp_keepalive": bool, + # Sets zeromq TCP keepalive idle. May be used to tune issues with minion disconnects + "tcp_keepalive_idle": float, + # Sets zeromq TCP keepalive count. May be used to tune issues with minion disconnects + "tcp_keepalive_cnt": float, + # Sets zeromq TCP keepalive interval. May be used to tune issues with minion disconnects. + "tcp_keepalive_intvl": float, + # The network interface for a daemon to bind to + "interface": six.string_types, + # The port for a salt master to broadcast publications on. This will also be the port minions + # connect to to listen for publications. + "publish_port": int, + # TODO unknown option! + "auth_mode": int, + # listen queue size / backlog + "zmq_backlog": int, + # Set the zeromq high water mark on the publisher interface. + # http://api.zeromq.org/3-2:zmq-setsockopt + "pub_hwm": int, + # IPC buffer size + # Refs https://github.com/saltstack/salt/issues/34215 + "ipc_write_buffer": int, + # The number of MWorker processes for a master to startup. This number needs to scale up as + # the number of connected minions increases. + "worker_threads": int, + # The port for the master to listen to returns on. The minion needs to connect to this port + # to send returns. + "ret_port": int, + # The number of hours to keep jobs around in the job cache on the master + "keep_jobs": int, + # If the returner supports `clean_old_jobs`, then at cleanup time, + # archive the job data before deleting it. + "archive_jobs": bool, + # A master-only copy of the file_roots dictionary, used by the state compiler + "master_roots": dict, + # Add the proxymodule LazyLoader object to opts. This breaks many things + # but this was the default pre 2015.8.2. This should default to + # False in 2016.3.0 + "add_proxymodule_to_opts": bool, + # Merge pillar data into configuration opts. + # As multiple proxies can run on the same server, we may need different + # configuration options for each, while there's one single configuration file. + # The solution is merging the pillar data of each proxy minion into the opts. + "proxy_merge_pillar_in_opts": bool, + # Deep merge of pillar data into configuration opts. + # Evaluated only when `proxy_merge_pillar_in_opts` is True. + "proxy_deep_merge_pillar_in_opts": bool, + # The strategy used when merging pillar into opts. + # Considered only when `proxy_merge_pillar_in_opts` is True. + "proxy_merge_pillar_in_opts_strategy": six.string_types, + # Allow enabling mine details using pillar data. + "proxy_mines_pillar": bool, + # In some particular cases, always alive proxies are not beneficial. + # This option can be used in those less dynamic environments: + # the user can request the connection + # always alive, or init-shutdown per command. + "proxy_always_alive": bool, + # Poll the connection state with the proxy minion + # If enabled, this option requires the function `alive` + # to be implemented in the proxy module + "proxy_keep_alive": bool, + # Frequency of the proxy_keep_alive, in minutes + "proxy_keep_alive_interval": int, + # Update intervals + "roots_update_interval": int, + "azurefs_update_interval": int, + "gitfs_update_interval": int, + "git_pillar_update_interval": int, + "hgfs_update_interval": int, + "minionfs_update_interval": int, + "s3fs_update_interval": int, + "svnfs_update_interval": int, + # NOTE: git_pillar_base, git_pillar_fallback, git_pillar_branch, + # git_pillar_env, and git_pillar_root omitted here because their values + # could conceivably be loaded as non-string types, which is OK because + # git_pillar will normalize them to strings. But rather than include all the + # possible types they could be, we'll just skip type-checking. + "git_pillar_ssl_verify": bool, + "git_pillar_global_lock": bool, + "git_pillar_user": six.string_types, + "git_pillar_password": six.string_types, + "git_pillar_insecure_auth": bool, + "git_pillar_privkey": six.string_types, + "git_pillar_pubkey": six.string_types, + "git_pillar_passphrase": six.string_types, + "git_pillar_refspecs": list, + "git_pillar_includes": bool, + "git_pillar_verify_config": bool, + # NOTE: gitfs_base, gitfs_fallback, gitfs_mountpoint, and gitfs_root omitted + # here because their values could conceivably be loaded as non-string types, + # which is OK because gitfs will normalize them to strings. But rather than + # include all the possible types they could be, we'll just skip type-checking. + "gitfs_remotes": list, + "gitfs_insecure_auth": bool, + "gitfs_privkey": six.string_types, + "gitfs_pubkey": six.string_types, + "gitfs_passphrase": six.string_types, + "gitfs_saltenv_whitelist": list, + "gitfs_saltenv_blacklist": list, + "gitfs_ssl_verify": bool, + "gitfs_global_lock": bool, + "gitfs_saltenv": list, + "gitfs_ref_types": list, + "gitfs_refspecs": list, + "gitfs_disable_saltenv_mapping": bool, + "hgfs_remotes": list, + "hgfs_mountpoint": six.string_types, + "hgfs_root": six.string_types, + "hgfs_base": six.string_types, + "hgfs_branch_method": six.string_types, + "hgfs_saltenv_whitelist": list, + "hgfs_saltenv_blacklist": list, + "svnfs_remotes": list, + "svnfs_mountpoint": six.string_types, + "svnfs_root": six.string_types, + "svnfs_trunk": six.string_types, + "svnfs_branches": six.string_types, + "svnfs_tags": six.string_types, + "svnfs_saltenv_whitelist": list, + "svnfs_saltenv_blacklist": list, + "minionfs_env": six.string_types, + "minionfs_mountpoint": six.string_types, + "minionfs_whitelist": list, + "minionfs_blacklist": list, + # Specify a list of external pillar systems to use + "ext_pillar": list, + # Reserved for future use to version the pillar structure + "pillar_version": int, + # Whether or not a copy of the master opts dict should be rendered into minion pillars + "pillar_opts": bool, + # Cache the master pillar to disk to avoid having to pass through the rendering system + "pillar_cache": bool, + # Pillar cache TTL, in seconds. Has no effect unless `pillar_cache` is True + "pillar_cache_ttl": int, + # Pillar cache backend. Defaults to `disk` which stores caches in the master cache + "pillar_cache_backend": six.string_types, + "pillar_safe_render_error": bool, + # When creating a pillar, there are several strategies to choose from when + # encountering duplicate values + "pillar_source_merging_strategy": six.string_types, + # Recursively merge lists by aggregating them instead of replacing them. + "pillar_merge_lists": bool, + # If True, values from included pillar SLS targets will override + "pillar_includes_override_sls": bool, + # How to merge multiple top files from multiple salt environments + # (saltenvs); can be 'merge' or 'same' + "top_file_merging_strategy": six.string_types, + # The ordering for salt environment merging, when top_file_merging_strategy + # is set to 'same' + "env_order": list, + # The salt environment which provides the default top file when + # top_file_merging_strategy is set to 'same'; defaults to 'base' + "default_top": six.string_types, + "ping_on_rotate": bool, + "peer": dict, + "preserve_minion_cache": bool, + "syndic_master": (six.string_types, list), + # The behaviour of the multimaster syndic when connection to a master of masters failed. Can + # specify 'random' (default) or 'ordered'. If set to 'random' masters will be iterated in random + # order if 'ordered' the configured order will be used. + "syndic_failover": six.string_types, + "syndic_forward_all_events": bool, + "runner_dirs": list, + "client_acl_verify": bool, + "publisher_acl": dict, + "publisher_acl_blacklist": dict, + "sudo_acl": bool, + "external_auth": dict, + "token_expire": int, + "token_expire_user_override": (bool, dict), + "file_recv": bool, + "file_recv_max_size": int, + "file_ignore_regex": (list, six.string_types), + "file_ignore_glob": (list, six.string_types), + "fileserver_backend": list, + "fileserver_followsymlinks": bool, + "fileserver_ignoresymlinks": bool, + "fileserver_limit_traversal": bool, + "fileserver_verify_config": bool, + # Optionally apply '*' permissioins to any user. By default '*' is a fallback case that is + # applied only if the user didn't matched by other matchers. + "permissive_acl": bool, + # Optionally enables keeping the calculated user's auth list in the token file. + "keep_acl_in_token": bool, + # Auth subsystem module to use to get authorized access list for a user. By default it's the + # same module used for external authentication. + "eauth_acl_module": six.string_types, + # Subsystem to use to maintain eauth tokens. By default, tokens are stored on the local + # filesystem + "eauth_tokens": six.string_types, + # The number of open files a daemon is allowed to have open. Frequently needs to be increased + # higher than the system default in order to account for the way zeromq consumes file handles. + "max_open_files": int, + # Automatically accept any key provided to the master. Implies that the key will be preserved + # so that subsequent connections will be authenticated even if this option has later been + # turned off. + "auto_accept": bool, + "autosign_timeout": int, + # A mapping of external systems that can be used to generate topfile data. + "master_tops": dict, + # Whether or not matches from master_tops should be executed before or + # after those from the top file(s). + "master_tops_first": bool, + # A flag that should be set on a top-level master when it is ordering around subordinate masters + # via the use of a salt syndic + "order_masters": bool, + # Whether or not to cache jobs so that they can be examined later on + "job_cache": bool, + # Define a returner to be used as an external job caching storage backend + "ext_job_cache": six.string_types, + # Specify a returner for the master to use as a backend storage system to cache jobs returns + # that it receives + "master_job_cache": six.string_types, + # Specify whether the master should store end times for jobs as returns come in + "job_cache_store_endtime": bool, + # The minion data cache is a cache of information about the minions stored on the master. + # This information is primarily the pillar and grains data. The data is cached in the master + # cachedir under the name of the minion and used to predetermine what minions are expected to + # reply from executions. + "minion_data_cache": bool, + # The number of seconds between AES key rotations on the master + "publish_session": int, + # Defines a salt reactor. See http://docs.saltstack.com/en/latest/topics/reactor/ + "reactor": list, + # The TTL for the cache of the reactor configuration + "reactor_refresh_interval": int, + # The number of workers for the runner/wheel in the reactor + "reactor_worker_threads": int, + # The queue size for workers in the reactor + "reactor_worker_hwm": int, + # Defines engines. See https://docs.saltstack.com/en/latest/topics/engines/ + "engines": list, + # Whether or not to store runner returns in the job cache + "runner_returns": bool, + "serial": six.string_types, + "search": six.string_types, + # A compound target definition. + # See: http://docs.saltstack.com/en/latest/topics/targeting/nodegroups.html + "nodegroups": (dict, list), + # List-only nodegroups for salt-ssh. Each group must be formed as either a + # comma-separated list, or a YAML list. + "ssh_list_nodegroups": dict, + # By default, salt-ssh uses its own specially-generated RSA key to auth + # against minions. If this is set to True, salt-ssh will look in + # for a key at ~/.ssh/id_rsa, and fall back to using its own specially- + # generated RSA key if that file doesn't exist. + "ssh_use_home_key": bool, + # The logfile location for salt-key + "key_logfile": six.string_types, + # The upper bound for the random number of seconds that a minion should + # delay when starting in up before it connects to a master. This can be + # used to mitigate a thundering-herd scenario when many minions start up + # at once and attempt to all connect immediately to the master + "random_startup_delay": int, + # The source location for the winrepo sls files + # (used by win_pkg.py, minion only) + "winrepo_source_dir": six.string_types, + "winrepo_dir": six.string_types, + "winrepo_dir_ng": six.string_types, + "winrepo_cachefile": six.string_types, + # NOTE: winrepo_branch omitted here because its value could conceivably be + # loaded as a non-string type, which is OK because winrepo will normalize + # them to strings. But rather than include all the possible types it could + # be, we'll just skip type-checking. + "winrepo_cache_expire_max": int, + "winrepo_cache_expire_min": int, + "winrepo_remotes": list, + "winrepo_remotes_ng": list, + "winrepo_ssl_verify": bool, + "winrepo_user": six.string_types, + "winrepo_password": six.string_types, + "winrepo_insecure_auth": bool, + "winrepo_privkey": six.string_types, + "winrepo_pubkey": six.string_types, + "winrepo_passphrase": six.string_types, + "winrepo_refspecs": list, + # Set a hard limit for the amount of memory modules can consume on a minion. + "modules_max_memory": int, + # Blacklist specific core grains to be filtered + "grains_blacklist": list, + # The number of minutes between the minion refreshing its cache of grains + "grains_refresh_every": int, + # Use lspci to gather system data for grains on a minion + "enable_lspci": bool, + # The number of seconds for the salt client to wait for additional syndics to + # check in with their lists of expected minions before giving up + "syndic_wait": int, + # Override Jinja environment option defaults for all templates except sls templates + "jinja_env": dict, + # Set Jinja environment options for sls templates + "jinja_sls_env": dict, + # If this is set to True leading spaces and tabs are stripped from the start + # of a line to a block. + "jinja_lstrip_blocks": bool, + # If this is set to True the first newline after a Jinja block is removed + "jinja_trim_blocks": bool, + # Cache minion ID to file + "minion_id_caching": bool, + # Always generate minion id in lowercase. + "minion_id_lowercase": bool, + # Remove either a single domain (foo.org), or all (True) from a generated minion id. + "minion_id_remove_domain": (six.string_types, bool), + # If set, the master will sign all publications before they are sent out + "sign_pub_messages": bool, + # The size of key that should be generated when creating new keys + "keysize": int, + # The transport system for this daemon. (i.e. zeromq, tcp, detect, etc) + "transport": six.string_types, + # The number of seconds to wait when the client is requesting information about running jobs + "gather_job_timeout": int, + # The number of seconds to wait before timing out an authentication request + "auth_timeout": int, + # The number of attempts to authenticate to a master before giving up + "auth_tries": int, + # The number of attempts to connect to a master before giving up. + # Set this to -1 for unlimited attempts. This allows for a master to have + # downtime and the minion to reconnect to it later when it comes back up. + # In 'failover' mode, it is the number of attempts for each set of masters. + # In this mode, it will cycle through the list of masters for each attempt. + "master_tries": int, + # Never give up when trying to authenticate to a master + "auth_safemode": bool, + # Selects a random master when starting a minion up in multi-master mode or + # when starting a minion with salt-call. ``master`` must be a list. + "random_master": bool, + # An upper bound for the amount of time for a minion to sleep before attempting to + # reauth after a restart. + "random_reauth_delay": int, + # The number of seconds for a syndic to poll for new messages that need to be forwarded + "syndic_event_forward_timeout": float, + # The length that the syndic event queue must hit before events are popped off and forwarded + "syndic_jid_forward_cache_hwm": int, + # Salt SSH configuration + "ssh_passwd": six.string_types, + "ssh_port": six.string_types, + "ssh_sudo": bool, + "ssh_sudo_user": six.string_types, + "ssh_timeout": float, + "ssh_user": six.string_types, + "ssh_scan_ports": six.string_types, + "ssh_scan_timeout": float, + "ssh_identities_only": bool, + "ssh_log_file": six.string_types, + "ssh_config_file": six.string_types, + "ssh_merge_pillar": bool, + "ssh_run_pre_flight": bool, + "cluster_mode": bool, + "sqlite_queue_dir": six.string_types, + "queue_dirs": list, + # Instructs the minion to ping its master(s) every n number of minutes. Used + # primarily as a mitigation technique against minion disconnects. + "ping_interval": int, + # Instructs the salt CLI to print a summary of a minion responses before returning + "cli_summary": bool, + # The maximum number of minion connections allowed by the master. Can have performance + # implications in large setups. + "max_minions": int, + "username": (type(None), six.string_types), + "password": (type(None), six.string_types), + # Use zmq.SUSCRIBE to limit listening sockets to only process messages bound for them + "zmq_filtering": bool, + # Connection caching. Can greatly speed up salt performance. + "con_cache": bool, + "rotate_aes_key": bool, + # Cache ZeroMQ connections. Can greatly improve salt performance. + "cache_sreqs": bool, + # Can be set to override the python_shell=False default in the cmd module + "cmd_safe": bool, + # Used by salt-api for master requests timeout + "rest_timeout": int, + # If set, all minion exec module actions will be rerouted through sudo as this user + "sudo_user": six.string_types, + # HTTP connection timeout in seconds. Applied for tornado http fetch functions like cp.get_url + # should be greater than overall download time + "http_connect_timeout": float, + # HTTP request timeout in seconds. Applied for tornado http fetch functions like cp.get_url + # should be greater than overall download time + "http_request_timeout": float, + # HTTP request max file content size. + "http_max_body": int, + # Delay in seconds before executing bootstrap (Salt Cloud) + "bootstrap_delay": int, + # If a proxymodule has a function called 'grains', then call it during + # regular grains loading and merge the results with the proxy's grains + # dictionary. Otherwise it is assumed that the module calls the grains + # function in a custom way and returns the data elsewhere + # + # Default to False for 2016.3 and 2016.11. Switch to True for 2017.7.0 + "proxy_merge_grains_in_module": bool, + # Command to use to restart salt-minion + "minion_restart_command": list, + # Whether or not a minion should send the results of a command back to the master + # Useful when a returner is the source of truth for a job result + "pub_ret": bool, + # HTTP proxy settings. Used in tornado fetch functions, apt-key etc + "proxy_host": six.string_types, + "proxy_username": six.string_types, + "proxy_password": six.string_types, + "proxy_port": int, + # Exclude list of hostnames from proxy + "no_proxy": list, + # Minion de-dup jid cache max size + "minion_jid_queue_hwm": int, + # Minion data cache driver (one of satl.cache.* modules) + "cache": six.string_types, + # Enables a fast in-memory cache booster and sets the expiration time. + "memcache_expire_seconds": int, + # Set a memcache limit in items (bank + key) per cache storage (driver + driver_opts). + "memcache_max_items": int, + # Each time a cache storage got full cleanup all the expired items not just the oldest one. + "memcache_full_cleanup": bool, + # Enable collecting the memcache stats and log it on `debug` log level. + "memcache_debug": bool, + # Thin and minimal Salt extra modules + "thin_extra_mods": six.string_types, + "min_extra_mods": six.string_types, + # Default returners minion should use. List or comma-delimited string + "return": (six.string_types, list), + # TLS/SSL connection options. This could be set to a dictionary containing arguments + # corresponding to python ssl.wrap_socket method. For details see: + # http://www.tornadoweb.org/en/stable/tcpserver.html#tornado.tcpserver.TCPServer + # http://docs.python.org/2/library/ssl.html#ssl.wrap_socket + # Note: to set enum arguments values like `cert_reqs` and `ssl_version` use constant names + # without ssl module prefix: `CERT_REQUIRED` or `PROTOCOL_SSLv23`. + "ssl": (dict, bool, type(None)), + # Controls how a multi-function job returns its data. If this is False, + # it will return its data using a dictionary with the function name as + # the key. This is compatible with legacy systems. If this is True, it + # will return its data using an array in the same order as the input + # array of functions to execute. This allows for calling the same + # function multiple times in the same multi-function job. + "multifunc_ordered": bool, + # Controls whether beacons are set up before a connection + # to the master is attempted. + "beacons_before_connect": bool, + # Controls whether the scheduler is set up before a connection + # to the master is attempted. + "scheduler_before_connect": bool, + # Whitelist/blacklist specific modules to be synced + "extmod_whitelist": dict, + "extmod_blacklist": dict, + # django auth + "django_auth_path": six.string_types, + "django_auth_settings": six.string_types, + # Number of times to try to auth with the master on a reconnect with the + # tcp transport + "tcp_authentication_retries": int, + # Permit or deny allowing minions to request revoke of its own key + "allow_minion_key_revoke": bool, + # File chunk size for salt-cp + "salt_cp_chunk_size": int, + # Require that the minion sign messages it posts to the master on the event + # bus + "minion_sign_messages": bool, + # Have master drop messages from minions for which their signatures do + # not verify + "drop_messages_signature_fail": bool, + # Require that payloads from minions have a 'sig' entry + # (in other words, require that minions have 'minion_sign_messages' + # turned on) + "require_minion_sign_messages": bool, + # The list of config entries to be passed to external pillar function as + # part of the extra_minion_data param + # Subconfig entries can be specified by using the ':' notation (e.g. key:subkey) + "pass_to_ext_pillars": (six.string_types, list), + # SSDP discovery publisher description. + # Contains publisher configuration and minion mapping. + # Setting it to False disables discovery + "discovery": (dict, bool), + # Scheduler should be a dictionary + "schedule": dict, + # Whether to fire auth events + "auth_events": bool, + # Whether to fire Minion data cache refresh events + "minion_data_cache_events": bool, + # Enable calling ssh minions from the salt master + "enable_ssh_minions": bool, + # Thorium saltenv + "thoriumenv": (type(None), six.string_types), + # Thorium top file location + "thorium_top": six.string_types, + # Allow raw_shell option when using the ssh + # client via the Salt API + "netapi_allow_raw_shell": bool, + } +) # default configurations -DEFAULT_MINION_OPTS = immutabletypes.freeze({ - 'interface': '0.0.0.0', - 'master': 'salt', - 'master_type': 'str', - 'master_uri_format': 'default', - 'source_interface_name': '', - 'source_address': '', - 'source_ret_port': 0, - 'source_publish_port': 0, - 'master_port': 4506, - 'master_finger': '', - 'master_shuffle': False, - 'master_alive_interval': 0, - 'master_failback': False, - 'master_failback_interval': 0, - 'verify_master_pubkey_sign': False, - 'sign_pub_messages': False, - 'always_verify_signature': False, - 'master_sign_key_name': 'master_sign', - 'syndic_finger': '', - 'user': salt.utils.user.get_user(), - 'root_dir': salt.syspaths.ROOT_DIR, - 'pki_dir': os.path.join(salt.syspaths.CONFIG_DIR, 'pki', 'minion'), - 'id': '', - 'id_function': {}, - 'cachedir': os.path.join(salt.syspaths.CACHE_DIR, 'minion'), - 'append_minionid_config_dirs': [], - 'cache_jobs': False, - 'grains_blacklist': [], - 'grains_cache': False, - 'grains_cache_expiration': 300, - 'grains_deep_merge': False, - 'conf_file': os.path.join(salt.syspaths.CONFIG_DIR, 'minion'), - 'sock_dir': os.path.join(salt.syspaths.SOCK_DIR, 'minion'), - 'sock_pool_size': 1, - 'backup_mode': '', - 'renderer': 'jinja|yaml', - 'renderer_whitelist': [], - 'renderer_blacklist': [], - 'random_startup_delay': 0, - 'failhard': False, - 'autoload_dynamic_modules': True, - 'saltenv': None, - 'lock_saltenv': False, - 'pillarenv': None, - 'pillarenv_from_saltenv': False, - 'pillar_opts': False, - 'pillar_source_merging_strategy': 'smart', - 'pillar_merge_lists': False, - 'pillar_includes_override_sls': False, - # ``pillar_cache``, ``pillar_cache_ttl`` and ``pillar_cache_backend`` - # are not used on the minion but are unavoidably in the code path - 'pillar_cache': False, - 'pillar_cache_ttl': 3600, - 'pillar_cache_backend': 'disk', - 'extension_modules': os.path.join(salt.syspaths.CACHE_DIR, 'minion', 'extmods'), - 'state_top': 'top.sls', - 'state_top_saltenv': None, - 'startup_states': '', - 'sls_list': [], - 'start_event_grains': [], - 'top_file': '', - 'thoriumenv': None, - 'thorium_top': 'top.sls', - 'thorium_interval': 0.5, - 'thorium_roots': { - 'base': [salt.syspaths.BASE_THORIUM_ROOTS_DIR], +DEFAULT_MINION_OPTS = immutabletypes.freeze( + { + "interface": "0.0.0.0", + "master": "salt", + "master_type": "str", + "master_uri_format": "default", + "source_interface_name": "", + "source_address": "", + "source_ret_port": 0, + "source_publish_port": 0, + "master_port": 4506, + "master_finger": "", + "master_shuffle": False, + "master_alive_interval": 0, + "master_failback": False, + "master_failback_interval": 0, + "verify_master_pubkey_sign": False, + "sign_pub_messages": False, + "always_verify_signature": False, + "master_sign_key_name": "master_sign", + "syndic_finger": "", + "user": salt.utils.user.get_user(), + "root_dir": salt.syspaths.ROOT_DIR, + "pki_dir": os.path.join(salt.syspaths.CONFIG_DIR, "pki", "minion"), + "id": "", + "id_function": {}, + "cachedir": os.path.join(salt.syspaths.CACHE_DIR, "minion"), + "append_minionid_config_dirs": [], + "cache_jobs": False, + "grains_blacklist": [], + "grains_cache": False, + "grains_cache_expiration": 300, + "grains_deep_merge": False, + "conf_file": os.path.join(salt.syspaths.CONFIG_DIR, "minion"), + "sock_dir": os.path.join(salt.syspaths.SOCK_DIR, "minion"), + "sock_pool_size": 1, + "backup_mode": "", + "renderer": "jinja|yaml", + "renderer_whitelist": [], + "renderer_blacklist": [], + "random_startup_delay": 0, + "failhard": False, + "autoload_dynamic_modules": True, + "saltenv": None, + "lock_saltenv": False, + "pillarenv": None, + "pillarenv_from_saltenv": False, + "pillar_opts": False, + "pillar_source_merging_strategy": "smart", + "pillar_merge_lists": False, + "pillar_includes_override_sls": False, + # ``pillar_cache``, ``pillar_cache_ttl`` and ``pillar_cache_backend`` + # are not used on the minion but are unavoidably in the code path + "pillar_cache": False, + "pillar_cache_ttl": 3600, + "pillar_cache_backend": "disk", + "extension_modules": os.path.join(salt.syspaths.CACHE_DIR, "minion", "extmods"), + "state_top": "top.sls", + "state_top_saltenv": None, + "startup_states": "", + "sls_list": [], + "start_event_grains": [], + "top_file": "", + "thoriumenv": None, + "thorium_top": "top.sls", + "thorium_interval": 0.5, + "thorium_roots": {"base": [salt.syspaths.BASE_THORIUM_ROOTS_DIR]}, + "file_client": "remote", + "local": False, + "use_master_when_local": False, + "file_roots": { + "base": [salt.syspaths.BASE_FILE_ROOTS_DIR, salt.syspaths.SPM_FORMULA_PATH] }, - 'file_client': 'remote', - 'local': False, - 'use_master_when_local': False, - 'file_roots': { - 'base': [salt.syspaths.BASE_FILE_ROOTS_DIR, - salt.syspaths.SPM_FORMULA_PATH] - }, - 'top_file_merging_strategy': 'merge', - 'env_order': [], - 'default_top': 'base', - 'fileserver_limit_traversal': False, - 'file_recv': False, - 'file_recv_max_size': 100, - 'file_ignore_regex': [], - 'file_ignore_glob': [], - 'fileserver_backend': ['roots'], - 'fileserver_followsymlinks': True, - 'fileserver_ignoresymlinks': False, - 'pillar_roots': { - 'base': [salt.syspaths.BASE_PILLAR_ROOTS_DIR, - salt.syspaths.SPM_PILLAR_PATH] - }, - 'on_demand_ext_pillar': ['libvirt', 'virtkey'], - 'decrypt_pillar': [], - 'decrypt_pillar_delimiter': ':', - 'decrypt_pillar_default': 'gpg', - 'decrypt_pillar_renderers': ['gpg'], - - # Update intervals - 'roots_update_interval': DEFAULT_INTERVAL, - 'azurefs_update_interval': DEFAULT_INTERVAL, - 'gitfs_update_interval': DEFAULT_INTERVAL, - 'git_pillar_update_interval': DEFAULT_INTERVAL, - 'hgfs_update_interval': DEFAULT_INTERVAL, - 'minionfs_update_interval': DEFAULT_INTERVAL, - 's3fs_update_interval': DEFAULT_INTERVAL, - 'svnfs_update_interval': DEFAULT_INTERVAL, - - 'git_pillar_base': 'master', - 'git_pillar_branch': 'master', - 'git_pillar_env': '', - 'git_pillar_root': '', - 'git_pillar_ssl_verify': True, - 'git_pillar_global_lock': True, - 'git_pillar_user': '', - 'git_pillar_password': '', - 'git_pillar_insecure_auth': False, - 'git_pillar_privkey': '', - 'git_pillar_pubkey': '', - 'git_pillar_passphrase': '', - 'git_pillar_refspecs': _DFLT_REFSPECS, - 'git_pillar_includes': True, - 'gitfs_remotes': [], - 'gitfs_mountpoint': '', - 'gitfs_root': '', - 'gitfs_base': 'master', - 'gitfs_user': '', - 'gitfs_password': '', - 'gitfs_insecure_auth': False, - 'gitfs_privkey': '', - 'gitfs_pubkey': '', - 'gitfs_passphrase': '', - 'gitfs_saltenv_whitelist': [], - 'gitfs_saltenv_blacklist': [], - 'gitfs_global_lock': True, - 'gitfs_ssl_verify': True, - 'gitfs_saltenv': [], - 'gitfs_ref_types': ['branch', 'tag', 'sha'], - 'gitfs_refspecs': _DFLT_REFSPECS, - 'gitfs_disable_saltenv_mapping': False, - 'unique_jid': False, - 'hash_type': 'sha256', - 'optimization_order': [0, 1, 2], - 'disable_modules': [], - 'disable_returners': [], - 'whitelist_modules': [], - 'module_dirs': [], - 'returner_dirs': [], - 'grains_dirs': [], - 'states_dirs': [], - 'render_dirs': [], - 'outputter_dirs': [], - 'utils_dirs': [], - 'publisher_acl': {}, - 'publisher_acl_blacklist': {}, - 'providers': {}, - 'clean_dynamic_modules': True, - 'open_mode': False, - 'auto_accept': True, - 'autosign_timeout': 120, - 'multiprocessing': True, - 'process_count_max': -1, - 'mine_enabled': True, - 'mine_return_job': False, - 'mine_interval': 60, - 'ipc_mode': _DFLT_IPC_MODE, - 'ipc_write_buffer': _DFLT_IPC_WBUFFER, - 'ipv6': None, - 'file_buffer_size': 262144, - 'tcp_pub_port': 4510, - 'tcp_pull_port': 4511, - 'tcp_authentication_retries': 5, - 'log_file': os.path.join(salt.syspaths.LOGS_DIR, 'minion'), - 'log_level': 'warning', - 'log_level_logfile': None, - 'log_datefmt': _DFLT_LOG_DATEFMT, - 'log_datefmt_logfile': _DFLT_LOG_DATEFMT_LOGFILE, - 'log_fmt_console': _DFLT_LOG_FMT_CONSOLE, - 'log_fmt_logfile': _DFLT_LOG_FMT_LOGFILE, - 'log_fmt_jid': _DFLT_LOG_FMT_JID, - 'log_granular_levels': {}, - 'log_rotate_max_bytes': 0, - 'log_rotate_backup_count': 0, - 'max_event_size': 1048576, - 'enable_legacy_startup_events': True, - 'test': False, - 'ext_job_cache': '', - 'cython_enable': False, - 'enable_gpu_grains': True, - 'enable_zip_modules': False, - 'state_verbose': True, - 'state_output': 'full', - 'state_output_diff': False, - 'state_auto_order': True, - 'state_events': False, - 'state_aggregate': False, - 'snapper_states': False, - 'snapper_states_config': 'root', - 'acceptance_wait_time': 10, - 'acceptance_wait_time_max': 0, - 'rejected_retry': False, - 'loop_interval': 1, - 'verify_env': True, - 'grains': {}, - 'permissive_pki_access': False, - 'default_include': 'minion.d/*.conf', - 'update_url': False, - 'update_restart_services': [], - 'retry_dns': 30, - 'retry_dns_count': None, - 'resolve_dns_fallback': True, - 'recon_max': 10000, - 'recon_default': 1000, - 'recon_randomize': True, - 'return_retry_timer': 5, - 'return_retry_timer_max': 10, - 'random_reauth_delay': 10, - 'winrepo_source_dir': 'salt://win/repo-ng/', - 'winrepo_dir': os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, 'win', 'repo'), - 'winrepo_dir_ng': os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, 'win', 'repo-ng'), - 'winrepo_cachefile': 'winrepo.p', - 'winrepo_cache_expire_max': 21600, - 'winrepo_cache_expire_min': 1800, - 'winrepo_remotes': ['https://github.com/saltstack/salt-winrepo.git'], - 'winrepo_remotes_ng': ['https://github.com/saltstack/salt-winrepo-ng.git'], - 'winrepo_branch': 'master', - 'winrepo_ssl_verify': True, - 'winrepo_user': '', - 'winrepo_password': '', - 'winrepo_insecure_auth': False, - 'winrepo_privkey': '', - 'winrepo_pubkey': '', - 'winrepo_passphrase': '', - 'winrepo_refspecs': _DFLT_REFSPECS, - 'pidfile': os.path.join(salt.syspaths.PIDFILE_DIR, 'salt-minion.pid'), - 'range_server': 'range:80', - 'reactor_refresh_interval': 60, - 'reactor_worker_threads': 10, - 'reactor_worker_hwm': 10000, - 'engines': [], - 'tcp_keepalive': True, - 'tcp_keepalive_idle': 300, - 'tcp_keepalive_cnt': -1, - 'tcp_keepalive_intvl': -1, - 'modules_max_memory': -1, - 'grains_refresh_every': 0, - 'minion_id_caching': True, - 'minion_id_lowercase': False, - 'minion_id_remove_domain': False, - 'keysize': 2048, - 'transport': 'zeromq', - 'auth_timeout': 5, - 'auth_tries': 7, - 'master_tries': _MASTER_TRIES, - 'master_tops_first': False, - 'auth_safemode': False, - 'random_master': False, - 'cluster_mode': False, - 'restart_on_error': False, - 'ping_interval': 0, - 'username': None, - 'password': None, - 'zmq_filtering': False, - 'zmq_monitor': False, - 'cache_sreqs': True, - 'cmd_safe': True, - 'sudo_user': '', - 'http_connect_timeout': 20.0, # tornado default - 20 seconds - 'http_request_timeout': 1 * 60 * 60.0, # 1 hour - 'http_max_body': 100 * 1024 * 1024 * 1024, # 100GB - 'event_match_type': 'startswith', - 'minion_restart_command': [], - 'pub_ret': True, - 'proxy_host': '', - 'proxy_username': '', - 'proxy_password': '', - 'proxy_port': 0, - 'minion_jid_queue_hwm': 100, - 'ssl': None, - 'multifunc_ordered': False, - 'beacons_before_connect': False, - 'scheduler_before_connect': False, - 'cache': 'localfs', - 'salt_cp_chunk_size': 65536, - 'extmod_whitelist': {}, - 'extmod_blacklist': {}, - 'minion_sign_messages': False, - 'discovery': False, - 'schedule': {}, - 'ssh_merge_pillar': True -}) - -DEFAULT_MASTER_OPTS = immutabletypes.freeze({ - 'interface': '0.0.0.0', - 'publish_port': 4505, - 'zmq_backlog': 1000, - 'pub_hwm': 1000, - 'auth_mode': 1, - 'user': _MASTER_USER, - 'worker_threads': 5, - 'sock_dir': os.path.join(salt.syspaths.SOCK_DIR, 'master'), - 'sock_pool_size': 1, - 'ret_port': 4506, - 'timeout': 5, - 'keep_jobs': 24, - 'archive_jobs': False, - 'root_dir': salt.syspaths.ROOT_DIR, - 'pki_dir': os.path.join(salt.syspaths.CONFIG_DIR, 'pki', 'master'), - 'key_cache': '', - 'cachedir': os.path.join(salt.syspaths.CACHE_DIR, 'master'), - 'file_roots': { - 'base': [salt.syspaths.BASE_FILE_ROOTS_DIR, - salt.syspaths.SPM_FORMULA_PATH] - }, - 'master_roots': { - 'base': [salt.syspaths.BASE_MASTER_ROOTS_DIR], - }, - 'pillar_roots': { - 'base': [salt.syspaths.BASE_PILLAR_ROOTS_DIR, - salt.syspaths.SPM_PILLAR_PATH] - }, - 'on_demand_ext_pillar': ['libvirt', 'virtkey'], - 'decrypt_pillar': [], - 'decrypt_pillar_delimiter': ':', - 'decrypt_pillar_default': 'gpg', - 'decrypt_pillar_renderers': ['gpg'], - 'thoriumenv': None, - 'thorium_top': 'top.sls', - 'thorium_interval': 0.5, - 'thorium_roots': { - 'base': [salt.syspaths.BASE_THORIUM_ROOTS_DIR], + "top_file_merging_strategy": "merge", + "env_order": [], + "default_top": "base", + "fileserver_limit_traversal": False, + "file_recv": False, + "file_recv_max_size": 100, + "file_ignore_regex": [], + "file_ignore_glob": [], + "fileserver_backend": ["roots"], + "fileserver_followsymlinks": True, + "fileserver_ignoresymlinks": False, + "pillar_roots": { + "base": [salt.syspaths.BASE_PILLAR_ROOTS_DIR, salt.syspaths.SPM_PILLAR_PATH] }, - 'top_file_merging_strategy': 'merge', - 'env_order': [], - 'saltenv': None, - 'lock_saltenv': False, - 'pillarenv': None, - 'default_top': 'base', - 'file_client': 'local', - 'local': True, + "on_demand_ext_pillar": ["libvirt", "virtkey"], + "decrypt_pillar": [], + "decrypt_pillar_delimiter": ":", + "decrypt_pillar_default": "gpg", + "decrypt_pillar_renderers": ["gpg"], + # Update intervals + "roots_update_interval": DEFAULT_INTERVAL, + "azurefs_update_interval": DEFAULT_INTERVAL, + "gitfs_update_interval": DEFAULT_INTERVAL, + "git_pillar_update_interval": DEFAULT_INTERVAL, + "hgfs_update_interval": DEFAULT_INTERVAL, + "minionfs_update_interval": DEFAULT_INTERVAL, + "s3fs_update_interval": DEFAULT_INTERVAL, + "svnfs_update_interval": DEFAULT_INTERVAL, + "git_pillar_base": "master", + "git_pillar_branch": "master", + "git_pillar_env": "", + "git_pillar_fallback": "", + "git_pillar_root": "", + "git_pillar_ssl_verify": True, + "git_pillar_global_lock": True, + "git_pillar_user": "", + "git_pillar_password": "", + "git_pillar_insecure_auth": False, + "git_pillar_privkey": "", + "git_pillar_pubkey": "", + "git_pillar_passphrase": "", + "git_pillar_refspecs": _DFLT_REFSPECS, + "git_pillar_includes": True, + "gitfs_remotes": [], + "gitfs_mountpoint": "", + "gitfs_root": "", + "gitfs_base": "master", + "gitfs_fallback": "", + "gitfs_user": "", + "gitfs_password": "", + "gitfs_insecure_auth": False, + "gitfs_privkey": "", + "gitfs_pubkey": "", + "gitfs_passphrase": "", + "gitfs_saltenv_whitelist": [], + "gitfs_saltenv_blacklist": [], + "gitfs_global_lock": True, + "gitfs_ssl_verify": True, + "gitfs_saltenv": [], + "gitfs_ref_types": ["branch", "tag", "sha"], + "gitfs_refspecs": _DFLT_REFSPECS, + "gitfs_disable_saltenv_mapping": False, + "unique_jid": False, + "hash_type": "sha256", + "optimization_order": [0, 1, 2], + "disable_modules": [], + "disable_returners": [], + "whitelist_modules": [], + "module_dirs": [], + "returner_dirs": [], + "grains_dirs": [], + "states_dirs": [], + "render_dirs": [], + "outputter_dirs": [], + "utils_dirs": [], + "publisher_acl": {}, + "publisher_acl_blacklist": {}, + "providers": {}, + "clean_dynamic_modules": True, + "open_mode": False, + "auto_accept": True, + "autosign_timeout": 120, + "multiprocessing": True, + "process_count_max": -1, + "mine_enabled": True, + "mine_return_job": False, + "mine_interval": 60, + "ipc_mode": _DFLT_IPC_MODE, + "ipc_write_buffer": _DFLT_IPC_WBUFFER, + "ipv6": None, + "file_buffer_size": 262144, + "tcp_pub_port": 4510, + "tcp_pull_port": 4511, + "tcp_authentication_retries": 5, + "log_file": os.path.join(salt.syspaths.LOGS_DIR, "minion"), + "log_level": "warning", + "log_level_logfile": None, + "log_datefmt": _DFLT_LOG_DATEFMT, + "log_datefmt_logfile": _DFLT_LOG_DATEFMT_LOGFILE, + "log_fmt_console": _DFLT_LOG_FMT_CONSOLE, + "log_fmt_logfile": _DFLT_LOG_FMT_LOGFILE, + "log_fmt_jid": _DFLT_LOG_FMT_JID, + "log_granular_levels": {}, + "log_rotate_max_bytes": 0, + "log_rotate_backup_count": 0, + "max_event_size": 1048576, + "enable_legacy_startup_events": True, + "test": False, + "ext_job_cache": "", + "cython_enable": False, + "enable_gpu_grains": True, + "enable_zip_modules": False, + "state_verbose": True, + "state_output": "full", + "state_output_diff": False, + "state_auto_order": True, + "state_events": False, + "state_aggregate": False, + "snapper_states": False, + "snapper_states_config": "root", + "acceptance_wait_time": 10, + "acceptance_wait_time_max": 0, + "rejected_retry": False, + "loop_interval": 1, + "verify_env": True, + "grains": {}, + "permissive_pki_access": False, + "default_include": "minion.d/*.conf", + "update_url": False, + "update_restart_services": [], + "retry_dns": 30, + "retry_dns_count": None, + "resolve_dns_fallback": True, + "recon_max": 10000, + "recon_default": 1000, + "recon_randomize": True, + "return_retry_timer": 5, + "return_retry_timer_max": 10, + "random_reauth_delay": 10, + "winrepo_source_dir": "salt://win/repo-ng/", + "winrepo_dir": os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, "win", "repo"), + "winrepo_dir_ng": os.path.join( + salt.syspaths.BASE_FILE_ROOTS_DIR, "win", "repo-ng" + ), + "winrepo_cachefile": "winrepo.p", + "winrepo_cache_expire_max": 21600, + "winrepo_cache_expire_min": 1800, + "winrepo_remotes": ["https://github.com/saltstack/salt-winrepo.git"], + "winrepo_remotes_ng": ["https://github.com/saltstack/salt-winrepo-ng.git"], + "winrepo_branch": "master", + "winrepo_ssl_verify": True, + "winrepo_user": "", + "winrepo_password": "", + "winrepo_insecure_auth": False, + "winrepo_privkey": "", + "winrepo_pubkey": "", + "winrepo_passphrase": "", + "winrepo_refspecs": _DFLT_REFSPECS, + "pidfile": os.path.join(salt.syspaths.PIDFILE_DIR, "salt-minion.pid"), + "range_server": "range:80", + "reactor_refresh_interval": 60, + "reactor_worker_threads": 10, + "reactor_worker_hwm": 10000, + "engines": [], + "tcp_keepalive": True, + "tcp_keepalive_idle": 300, + "tcp_keepalive_cnt": -1, + "tcp_keepalive_intvl": -1, + "modules_max_memory": -1, + "grains_refresh_every": 0, + "minion_id_caching": True, + "minion_id_lowercase": False, + "minion_id_remove_domain": False, + "keysize": 2048, + "transport": "zeromq", + "auth_timeout": 5, + "auth_tries": 7, + "master_tries": _MASTER_TRIES, + "master_tops_first": False, + "auth_safemode": False, + "random_master": False, + "cluster_mode": False, + "restart_on_error": False, + "ping_interval": 0, + "username": None, + "password": None, + "zmq_filtering": False, + "zmq_monitor": False, + "cache_sreqs": True, + "cmd_safe": True, + "sudo_user": "", + "http_connect_timeout": 20.0, # tornado default - 20 seconds + "http_request_timeout": 1 * 60 * 60.0, # 1 hour + "http_max_body": 100 * 1024 * 1024 * 1024, # 100GB + "event_match_type": "startswith", + "minion_restart_command": [], + "pub_ret": True, + "proxy_host": "", + "proxy_username": "", + "proxy_password": "", + "proxy_port": 0, + "minion_jid_queue_hwm": 100, + "ssl": None, + "multifunc_ordered": False, + "beacons_before_connect": False, + "scheduler_before_connect": False, + "cache": "localfs", + "salt_cp_chunk_size": 65536, + "extmod_whitelist": {}, + "extmod_blacklist": {}, + "minion_sign_messages": False, + "discovery": False, + "schedule": {}, + "ssh_merge_pillar": True, + } +) - # Update intervals - 'roots_update_interval': DEFAULT_INTERVAL, - 'azurefs_update_interval': DEFAULT_INTERVAL, - 'gitfs_update_interval': DEFAULT_INTERVAL, - 'git_pillar_update_interval': DEFAULT_INTERVAL, - 'hgfs_update_interval': DEFAULT_INTERVAL, - 'minionfs_update_interval': DEFAULT_INTERVAL, - 's3fs_update_interval': DEFAULT_INTERVAL, - 'svnfs_update_interval': DEFAULT_INTERVAL, - - 'git_pillar_base': 'master', - 'git_pillar_branch': 'master', - 'git_pillar_env': '', - 'git_pillar_root': '', - 'git_pillar_ssl_verify': True, - 'git_pillar_global_lock': True, - 'git_pillar_user': '', - 'git_pillar_password': '', - 'git_pillar_insecure_auth': False, - 'git_pillar_privkey': '', - 'git_pillar_pubkey': '', - 'git_pillar_passphrase': '', - 'git_pillar_refspecs': _DFLT_REFSPECS, - 'git_pillar_includes': True, - 'git_pillar_verify_config': True, - 'gitfs_remotes': [], - 'gitfs_mountpoint': '', - 'gitfs_root': '', - 'gitfs_base': 'master', - 'gitfs_user': '', - 'gitfs_password': '', - 'gitfs_insecure_auth': False, - 'gitfs_privkey': '', - 'gitfs_pubkey': '', - 'gitfs_passphrase': '', - 'gitfs_saltenv_whitelist': [], - 'gitfs_saltenv_blacklist': [], - 'gitfs_global_lock': True, - 'gitfs_ssl_verify': True, - 'gitfs_saltenv': [], - 'gitfs_ref_types': ['branch', 'tag', 'sha'], - 'gitfs_refspecs': _DFLT_REFSPECS, - 'gitfs_disable_saltenv_mapping': False, - 'hgfs_remotes': [], - 'hgfs_mountpoint': '', - 'hgfs_root': '', - 'hgfs_base': 'default', - 'hgfs_branch_method': 'branches', - 'hgfs_saltenv_whitelist': [], - 'hgfs_saltenv_blacklist': [], - 'show_timeout': True, - 'show_jid': False, - 'unique_jid': False, - 'svnfs_remotes': [], - 'svnfs_mountpoint': '', - 'svnfs_root': '', - 'svnfs_trunk': 'trunk', - 'svnfs_branches': 'branches', - 'svnfs_tags': 'tags', - 'svnfs_saltenv_whitelist': [], - 'svnfs_saltenv_blacklist': [], - 'max_event_size': 1048576, - 'master_stats': False, - 'master_stats_event_iter': 60, - 'minionfs_env': 'base', - 'minionfs_mountpoint': '', - 'minionfs_whitelist': [], - 'minionfs_blacklist': [], - 'ext_pillar': [], - 'pillar_version': 2, - 'pillar_opts': False, - 'pillar_safe_render_error': True, - 'pillar_source_merging_strategy': 'smart', - 'pillar_merge_lists': False, - 'pillar_includes_override_sls': False, - 'pillar_cache': False, - 'pillar_cache_ttl': 3600, - 'pillar_cache_backend': 'disk', - 'ping_on_rotate': False, - 'peer': {}, - 'preserve_minion_cache': False, - 'syndic_master': 'masterofmasters', - 'syndic_failover': 'random', - 'syndic_forward_all_events': False, - 'syndic_log_file': os.path.join(salt.syspaths.LOGS_DIR, 'syndic'), - 'syndic_pidfile': os.path.join(salt.syspaths.PIDFILE_DIR, 'salt-syndic.pid'), - 'outputter_dirs': [], - 'runner_dirs': [], - 'utils_dirs': [], - 'client_acl_verify': True, - 'publisher_acl': {}, - 'publisher_acl_blacklist': {}, - 'sudo_acl': False, - 'external_auth': {}, - 'token_expire': 43200, - 'token_expire_user_override': False, - 'permissive_acl': False, - 'keep_acl_in_token': False, - 'eauth_acl_module': '', - 'eauth_tokens': 'localfs', - 'extension_modules': os.path.join(salt.syspaths.CACHE_DIR, 'master', 'extmods'), - 'module_dirs': [], - 'file_recv': False, - 'file_recv_max_size': 100, - 'file_buffer_size': 1048576, - 'file_ignore_regex': [], - 'file_ignore_glob': [], - 'fileserver_backend': ['roots'], - 'fileserver_followsymlinks': True, - 'fileserver_ignoresymlinks': False, - 'fileserver_limit_traversal': False, - 'fileserver_verify_config': True, - 'max_open_files': 100000, - 'hash_type': 'sha256', - 'optimization_order': [0, 1, 2], - 'conf_file': os.path.join(salt.syspaths.CONFIG_DIR, 'master'), - 'open_mode': False, - 'auto_accept': False, - 'renderer': 'jinja|yaml', - 'renderer_whitelist': [], - 'renderer_blacklist': [], - 'failhard': False, - 'state_top': 'top.sls', - 'state_top_saltenv': None, - 'master_tops': {}, - 'master_tops_first': False, - 'order_masters': False, - 'job_cache': True, - 'ext_job_cache': '', - 'master_job_cache': 'local_cache', - 'job_cache_store_endtime': False, - 'minion_data_cache': True, - 'enforce_mine_cache': False, - 'ipc_mode': _DFLT_IPC_MODE, - 'ipc_write_buffer': _DFLT_IPC_WBUFFER, - 'ipv6': None, - 'tcp_master_pub_port': 4512, - 'tcp_master_pull_port': 4513, - 'tcp_master_publish_pull': 4514, - 'tcp_master_workers': 4515, - 'log_file': os.path.join(salt.syspaths.LOGS_DIR, 'master'), - 'log_level': 'warning', - 'log_level_logfile': None, - 'log_datefmt': _DFLT_LOG_DATEFMT, - 'log_datefmt_logfile': _DFLT_LOG_DATEFMT_LOGFILE, - 'log_fmt_console': _DFLT_LOG_FMT_CONSOLE, - 'log_fmt_logfile': _DFLT_LOG_FMT_LOGFILE, - 'log_fmt_jid': _DFLT_LOG_FMT_JID, - 'log_granular_levels': {}, - 'log_rotate_max_bytes': 0, - 'log_rotate_backup_count': 0, - 'pidfile': os.path.join(salt.syspaths.PIDFILE_DIR, 'salt-master.pid'), - 'publish_session': 86400, - 'range_server': 'range:80', - 'reactor': [], - 'reactor_refresh_interval': 60, - 'reactor_worker_threads': 10, - 'reactor_worker_hwm': 10000, - 'engines': [], - 'event_return': '', - 'event_return_queue': 0, - 'event_return_whitelist': [], - 'event_return_blacklist': [], - 'event_match_type': 'startswith', - 'runner_returns': True, - 'serial': 'msgpack', - 'test': False, - 'state_verbose': True, - 'state_output': 'full', - 'state_output_diff': False, - 'state_auto_order': True, - 'state_events': False, - 'state_aggregate': False, - 'search': '', - 'loop_interval': 60, - 'nodegroups': {}, - 'ssh_list_nodegroups': {}, - 'ssh_use_home_key': False, - 'cython_enable': False, - 'enable_gpu_grains': False, - # XXX: Remove 'key_logfile' support in 2014.1.0 - 'key_logfile': os.path.join(salt.syspaths.LOGS_DIR, 'key'), - 'verify_env': True, - 'permissive_pki_access': False, - 'key_pass': None, - 'signing_key_pass': None, - 'default_include': 'master.d/*.conf', - 'winrepo_dir': os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, 'win', 'repo'), - 'winrepo_dir_ng': os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, 'win', 'repo-ng'), - 'winrepo_cachefile': 'winrepo.p', - 'winrepo_remotes': ['https://github.com/saltstack/salt-winrepo.git'], - 'winrepo_remotes_ng': ['https://github.com/saltstack/salt-winrepo-ng.git'], - 'winrepo_branch': 'master', - 'winrepo_ssl_verify': True, - 'winrepo_user': '', - 'winrepo_password': '', - 'winrepo_insecure_auth': False, - 'winrepo_privkey': '', - 'winrepo_pubkey': '', - 'winrepo_passphrase': '', - 'winrepo_refspecs': _DFLT_REFSPECS, - 'syndic_wait': 5, - 'jinja_env': {}, - 'jinja_sls_env': {}, - 'jinja_lstrip_blocks': False, - 'jinja_trim_blocks': False, - 'tcp_keepalive': True, - 'tcp_keepalive_idle': 300, - 'tcp_keepalive_cnt': -1, - 'tcp_keepalive_intvl': -1, - 'sign_pub_messages': True, - 'keysize': 2048, - 'transport': 'zeromq', - 'gather_job_timeout': 10, - 'syndic_event_forward_timeout': 0.5, - 'syndic_jid_forward_cache_hwm': 100, - 'regen_thin': False, - 'ssh_passwd': '', - 'ssh_priv_passwd': '', - 'ssh_port': '22', - 'ssh_sudo': False, - 'ssh_sudo_user': '', - 'ssh_timeout': 60, - 'ssh_user': 'root', - 'ssh_scan_ports': '22', - 'ssh_scan_timeout': 0.01, - 'ssh_identities_only': False, - 'ssh_log_file': os.path.join(salt.syspaths.LOGS_DIR, 'ssh'), - 'ssh_config_file': os.path.join(salt.syspaths.HOME_DIR, '.ssh', 'config'), - 'cluster_mode': False, - 'sqlite_queue_dir': os.path.join(salt.syspaths.CACHE_DIR, 'master', 'queues'), - 'queue_dirs': [], - 'cli_summary': False, - 'max_minions': 0, - 'master_sign_key_name': 'master_sign', - 'master_sign_pubkey': False, - 'master_pubkey_signature': 'master_pubkey_signature', - 'master_use_pubkey_signature': False, - 'zmq_filtering': False, - 'zmq_monitor': False, - 'con_cache': False, - 'rotate_aes_key': True, - 'cache_sreqs': True, - 'dummy_pub': False, - 'http_connect_timeout': 20.0, # tornado default - 20 seconds - 'http_request_timeout': 1 * 60 * 60.0, # 1 hour - 'http_max_body': 100 * 1024 * 1024 * 1024, # 100GB - 'python2_bin': 'python2', - 'python3_bin': 'python3', - 'cache': 'localfs', - 'memcache_expire_seconds': 0, - 'memcache_max_items': 1024, - 'memcache_full_cleanup': False, - 'memcache_debug': False, - 'thin_extra_mods': '', - 'min_extra_mods': '', - 'ssl': None, - 'extmod_whitelist': {}, - 'extmod_blacklist': {}, - 'clean_dynamic_modules': True, - 'django_auth_path': '', - 'django_auth_settings': '', - 'allow_minion_key_revoke': True, - 'salt_cp_chunk_size': 98304, - 'require_minion_sign_messages': False, - 'drop_messages_signature_fail': False, - 'discovery': False, - 'schedule': {}, - 'auth_events': True, - 'minion_data_cache_events': True, - 'enable_ssh_minions': False, - 'netapi_allow_raw_shell': False, -}) +DEFAULT_MASTER_OPTS = immutabletypes.freeze( + { + "interface": "0.0.0.0", + "publish_port": 4505, + "zmq_backlog": 1000, + "pub_hwm": 1000, + "auth_mode": 1, + "user": _MASTER_USER, + "worker_threads": 5, + "sock_dir": os.path.join(salt.syspaths.SOCK_DIR, "master"), + "sock_pool_size": 1, + "ret_port": 4506, + "timeout": 5, + "keep_jobs": 24, + "archive_jobs": False, + "root_dir": salt.syspaths.ROOT_DIR, + "pki_dir": os.path.join(salt.syspaths.CONFIG_DIR, "pki", "master"), + "key_cache": "", + "cachedir": os.path.join(salt.syspaths.CACHE_DIR, "master"), + "file_roots": { + "base": [salt.syspaths.BASE_FILE_ROOTS_DIR, salt.syspaths.SPM_FORMULA_PATH] + }, + "master_roots": {"base": [salt.syspaths.BASE_MASTER_ROOTS_DIR]}, + "pillar_roots": { + "base": [salt.syspaths.BASE_PILLAR_ROOTS_DIR, salt.syspaths.SPM_PILLAR_PATH] + }, + "on_demand_ext_pillar": ["libvirt", "virtkey"], + "decrypt_pillar": [], + "decrypt_pillar_delimiter": ":", + "decrypt_pillar_default": "gpg", + "decrypt_pillar_renderers": ["gpg"], + "thoriumenv": None, + "thorium_top": "top.sls", + "thorium_interval": 0.5, + "thorium_roots": {"base": [salt.syspaths.BASE_THORIUM_ROOTS_DIR]}, + "top_file_merging_strategy": "merge", + "env_order": [], + "saltenv": None, + "lock_saltenv": False, + "pillarenv": None, + "default_top": "base", + "file_client": "local", + "local": True, + # Update intervals + "roots_update_interval": DEFAULT_INTERVAL, + "azurefs_update_interval": DEFAULT_INTERVAL, + "gitfs_update_interval": DEFAULT_INTERVAL, + "git_pillar_update_interval": DEFAULT_INTERVAL, + "hgfs_update_interval": DEFAULT_INTERVAL, + "minionfs_update_interval": DEFAULT_INTERVAL, + "s3fs_update_interval": DEFAULT_INTERVAL, + "svnfs_update_interval": DEFAULT_INTERVAL, + "git_pillar_base": "master", + "git_pillar_branch": "master", + "git_pillar_env": "", + "git_pillar_fallback": "", + "git_pillar_root": "", + "git_pillar_ssl_verify": True, + "git_pillar_global_lock": True, + "git_pillar_user": "", + "git_pillar_password": "", + "git_pillar_insecure_auth": False, + "git_pillar_privkey": "", + "git_pillar_pubkey": "", + "git_pillar_passphrase": "", + "git_pillar_refspecs": _DFLT_REFSPECS, + "git_pillar_includes": True, + "git_pillar_verify_config": True, + "gitfs_remotes": [], + "gitfs_mountpoint": "", + "gitfs_root": "", + "gitfs_base": "master", + "gitfs_fallback": "", + "gitfs_user": "", + "gitfs_password": "", + "gitfs_insecure_auth": False, + "gitfs_privkey": "", + "gitfs_pubkey": "", + "gitfs_passphrase": "", + "gitfs_saltenv_whitelist": [], + "gitfs_saltenv_blacklist": [], + "gitfs_global_lock": True, + "gitfs_ssl_verify": True, + "gitfs_saltenv": [], + "gitfs_ref_types": ["branch", "tag", "sha"], + "gitfs_refspecs": _DFLT_REFSPECS, + "gitfs_disable_saltenv_mapping": False, + "hgfs_remotes": [], + "hgfs_mountpoint": "", + "hgfs_root": "", + "hgfs_base": "default", + "hgfs_branch_method": "branches", + "hgfs_saltenv_whitelist": [], + "hgfs_saltenv_blacklist": [], + "show_timeout": True, + "show_jid": False, + "unique_jid": False, + "svnfs_remotes": [], + "svnfs_mountpoint": "", + "svnfs_root": "", + "svnfs_trunk": "trunk", + "svnfs_branches": "branches", + "svnfs_tags": "tags", + "svnfs_saltenv_whitelist": [], + "svnfs_saltenv_blacklist": [], + "max_event_size": 1048576, + "master_stats": False, + "master_stats_event_iter": 60, + "minionfs_env": "base", + "minionfs_mountpoint": "", + "minionfs_whitelist": [], + "minionfs_blacklist": [], + "ext_pillar": [], + "pillar_version": 2, + "pillar_opts": False, + "pillar_safe_render_error": True, + "pillar_source_merging_strategy": "smart", + "pillar_merge_lists": False, + "pillar_includes_override_sls": False, + "pillar_cache": False, + "pillar_cache_ttl": 3600, + "pillar_cache_backend": "disk", + "ping_on_rotate": False, + "peer": {}, + "preserve_minion_cache": False, + "syndic_master": "masterofmasters", + "syndic_failover": "random", + "syndic_forward_all_events": False, + "syndic_log_file": os.path.join(salt.syspaths.LOGS_DIR, "syndic"), + "syndic_pidfile": os.path.join(salt.syspaths.PIDFILE_DIR, "salt-syndic.pid"), + "outputter_dirs": [], + "runner_dirs": [], + "utils_dirs": [], + "client_acl_verify": True, + "publisher_acl": {}, + "publisher_acl_blacklist": {}, + "sudo_acl": False, + "external_auth": {}, + "token_expire": 43200, + "token_expire_user_override": False, + "permissive_acl": False, + "keep_acl_in_token": False, + "eauth_acl_module": "", + "eauth_tokens": "localfs", + "extension_modules": os.path.join(salt.syspaths.CACHE_DIR, "master", "extmods"), + "module_dirs": [], + "file_recv": False, + "file_recv_max_size": 100, + "file_buffer_size": 1048576, + "file_ignore_regex": [], + "file_ignore_glob": [], + "fileserver_backend": ["roots"], + "fileserver_followsymlinks": True, + "fileserver_ignoresymlinks": False, + "fileserver_limit_traversal": False, + "fileserver_verify_config": True, + "max_open_files": 100000, + "hash_type": "sha256", + "optimization_order": [0, 1, 2], + "conf_file": os.path.join(salt.syspaths.CONFIG_DIR, "master"), + "open_mode": False, + "auto_accept": False, + "renderer": "jinja|yaml", + "renderer_whitelist": [], + "renderer_blacklist": [], + "failhard": False, + "state_top": "top.sls", + "state_top_saltenv": None, + "master_tops": {}, + "master_tops_first": False, + "order_masters": False, + "job_cache": True, + "ext_job_cache": "", + "master_job_cache": "local_cache", + "job_cache_store_endtime": False, + "minion_data_cache": True, + "enforce_mine_cache": False, + "ipc_mode": _DFLT_IPC_MODE, + "ipc_write_buffer": _DFLT_IPC_WBUFFER, + "ipv6": None, + "tcp_master_pub_port": 4512, + "tcp_master_pull_port": 4513, + "tcp_master_publish_pull": 4514, + "tcp_master_workers": 4515, + "log_file": os.path.join(salt.syspaths.LOGS_DIR, "master"), + "log_level": "warning", + "log_level_logfile": None, + "log_datefmt": _DFLT_LOG_DATEFMT, + "log_datefmt_logfile": _DFLT_LOG_DATEFMT_LOGFILE, + "log_fmt_console": _DFLT_LOG_FMT_CONSOLE, + "log_fmt_logfile": _DFLT_LOG_FMT_LOGFILE, + "log_fmt_jid": _DFLT_LOG_FMT_JID, + "log_granular_levels": {}, + "log_rotate_max_bytes": 0, + "log_rotate_backup_count": 0, + "pidfile": os.path.join(salt.syspaths.PIDFILE_DIR, "salt-master.pid"), + "publish_session": 86400, + "range_server": "range:80", + "reactor": [], + "reactor_refresh_interval": 60, + "reactor_worker_threads": 10, + "reactor_worker_hwm": 10000, + "engines": [], + "event_return": "", + "event_return_queue": 0, + "event_return_whitelist": [], + "event_return_blacklist": [], + "event_match_type": "startswith", + "runner_returns": True, + "serial": "msgpack", + "test": False, + "state_verbose": True, + "state_output": "full", + "state_output_diff": False, + "state_auto_order": True, + "state_events": False, + "state_aggregate": False, + "search": "", + "loop_interval": 60, + "nodegroups": {}, + "ssh_list_nodegroups": {}, + "ssh_use_home_key": False, + "cython_enable": False, + "enable_gpu_grains": False, + # XXX: Remove 'key_logfile' support in 2014.1.0 + "key_logfile": os.path.join(salt.syspaths.LOGS_DIR, "key"), + "verify_env": True, + "permissive_pki_access": False, + "key_pass": None, + "signing_key_pass": None, + "default_include": "master.d/*.conf", + "winrepo_dir": os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, "win", "repo"), + "winrepo_dir_ng": os.path.join( + salt.syspaths.BASE_FILE_ROOTS_DIR, "win", "repo-ng" + ), + "winrepo_cachefile": "winrepo.p", + "winrepo_remotes": ["https://github.com/saltstack/salt-winrepo.git"], + "winrepo_remotes_ng": ["https://github.com/saltstack/salt-winrepo-ng.git"], + "winrepo_branch": "master", + "winrepo_ssl_verify": True, + "winrepo_user": "", + "winrepo_password": "", + "winrepo_insecure_auth": False, + "winrepo_privkey": "", + "winrepo_pubkey": "", + "winrepo_passphrase": "", + "winrepo_refspecs": _DFLT_REFSPECS, + "syndic_wait": 5, + "jinja_env": {}, + "jinja_sls_env": {}, + "jinja_lstrip_blocks": False, + "jinja_trim_blocks": False, + "tcp_keepalive": True, + "tcp_keepalive_idle": 300, + "tcp_keepalive_cnt": -1, + "tcp_keepalive_intvl": -1, + "sign_pub_messages": True, + "keysize": 2048, + "transport": "zeromq", + "gather_job_timeout": 10, + "syndic_event_forward_timeout": 0.5, + "syndic_jid_forward_cache_hwm": 100, + "regen_thin": False, + "ssh_passwd": "", + "ssh_priv_passwd": "", + "ssh_port": "22", + "ssh_sudo": False, + "ssh_sudo_user": "", + "ssh_timeout": 60, + "ssh_user": "root", + "ssh_scan_ports": "22", + "ssh_scan_timeout": 0.01, + "ssh_identities_only": False, + "ssh_log_file": os.path.join(salt.syspaths.LOGS_DIR, "ssh"), + "ssh_config_file": os.path.join(salt.syspaths.HOME_DIR, ".ssh", "config"), + "cluster_mode": False, + "sqlite_queue_dir": os.path.join(salt.syspaths.CACHE_DIR, "master", "queues"), + "queue_dirs": [], + "cli_summary": False, + "max_minions": 0, + "master_sign_key_name": "master_sign", + "master_sign_pubkey": False, + "master_pubkey_signature": "master_pubkey_signature", + "master_use_pubkey_signature": False, + "zmq_filtering": False, + "zmq_monitor": False, + "con_cache": False, + "rotate_aes_key": True, + "cache_sreqs": True, + "dummy_pub": False, + "http_connect_timeout": 20.0, # tornado default - 20 seconds + "http_request_timeout": 1 * 60 * 60.0, # 1 hour + "http_max_body": 100 * 1024 * 1024 * 1024, # 100GB + "python2_bin": "python2", + "python3_bin": "python3", + "cache": "localfs", + "memcache_expire_seconds": 0, + "memcache_max_items": 1024, + "memcache_full_cleanup": False, + "memcache_debug": False, + "thin_extra_mods": "", + "min_extra_mods": "", + "ssl": None, + "extmod_whitelist": {}, + "extmod_blacklist": {}, + "clean_dynamic_modules": True, + "django_auth_path": "", + "django_auth_settings": "", + "allow_minion_key_revoke": True, + "salt_cp_chunk_size": 98304, + "require_minion_sign_messages": False, + "drop_messages_signature_fail": False, + "discovery": False, + "schedule": {}, + "auth_events": True, + "minion_data_cache_events": True, + "enable_ssh_minions": False, + "netapi_allow_raw_shell": False, + } +) # ----- Salt Proxy Minion Configuration Defaults -----------------------------------> # These are merged with DEFAULT_MINION_OPTS since many of them also apply here. -DEFAULT_PROXY_MINION_OPTS = immutabletypes.freeze({ - 'conf_file': os.path.join(salt.syspaths.CONFIG_DIR, 'proxy'), - 'log_file': os.path.join(salt.syspaths.LOGS_DIR, 'proxy'), - 'add_proxymodule_to_opts': False, - 'proxy_merge_grains_in_module': True, - 'extension_modules': os.path.join(salt.syspaths.CACHE_DIR, 'proxy', 'extmods'), - 'append_minionid_config_dirs': ['cachedir', 'pidfile', 'default_include', 'extension_modules'], - 'default_include': 'proxy.d/*.conf', - - 'proxy_merge_pillar_in_opts': False, - 'proxy_deep_merge_pillar_in_opts': False, - 'proxy_merge_pillar_in_opts_strategy': 'smart', - - 'proxy_mines_pillar': True, - - # By default, proxies will preserve the connection. - # If this option is set to False, - # the connection with the remote dumb device - # is closed after each command request. - 'proxy_always_alive': True, - - 'proxy_keep_alive': True, # by default will try to keep alive the connection - 'proxy_keep_alive_interval': 1, # frequency of the proxy keepalive in minutes - 'pki_dir': os.path.join(salt.syspaths.CONFIG_DIR, 'pki', 'proxy'), - 'cachedir': os.path.join(salt.syspaths.CACHE_DIR, 'proxy'), - 'sock_dir': os.path.join(salt.syspaths.SOCK_DIR, 'proxy'), -}) +DEFAULT_PROXY_MINION_OPTS = immutabletypes.freeze( + { + "conf_file": os.path.join(salt.syspaths.CONFIG_DIR, "proxy"), + "log_file": os.path.join(salt.syspaths.LOGS_DIR, "proxy"), + "add_proxymodule_to_opts": False, + "proxy_merge_grains_in_module": True, + "extension_modules": os.path.join(salt.syspaths.CACHE_DIR, "proxy", "extmods"), + "append_minionid_config_dirs": [ + "cachedir", + "pidfile", + "default_include", + "extension_modules", + ], + "default_include": "proxy.d/*.conf", + "proxy_merge_pillar_in_opts": False, + "proxy_deep_merge_pillar_in_opts": False, + "proxy_merge_pillar_in_opts_strategy": "smart", + "proxy_mines_pillar": True, + # By default, proxies will preserve the connection. + # If this option is set to False, + # the connection with the remote dumb device + # is closed after each command request. + "proxy_always_alive": True, + "proxy_keep_alive": True, # by default will try to keep alive the connection + "proxy_keep_alive_interval": 1, # frequency of the proxy keepalive in minutes + "pki_dir": os.path.join(salt.syspaths.CONFIG_DIR, "pki", "proxy"), + "cachedir": os.path.join(salt.syspaths.CACHE_DIR, "proxy"), + "sock_dir": os.path.join(salt.syspaths.SOCK_DIR, "proxy"), + } +) # ----- Salt Cloud Configuration Defaults -----------------------------------> -DEFAULT_CLOUD_OPTS = immutabletypes.freeze({ - 'verify_env': True, - 'default_include': 'cloud.conf.d/*.conf', - # Global defaults - 'ssh_auth': '', - 'cachedir': os.path.join(salt.syspaths.CACHE_DIR, 'cloud'), - 'keysize': 4096, - 'os': '', - 'script': 'bootstrap-salt', - 'start_action': None, - 'enable_hard_maps': False, - 'delete_sshkeys': False, - # Custom deploy scripts - 'deploy_scripts_search_path': 'cloud.deploy.d', - # Logging defaults - 'log_file': os.path.join(salt.syspaths.LOGS_DIR, 'cloud'), - 'log_level': 'warning', - 'log_level_logfile': None, - 'log_datefmt': _DFLT_LOG_DATEFMT, - 'log_datefmt_logfile': _DFLT_LOG_DATEFMT_LOGFILE, - 'log_fmt_console': _DFLT_LOG_FMT_CONSOLE, - 'log_fmt_logfile': _DFLT_LOG_FMT_LOGFILE, - 'log_fmt_jid': _DFLT_LOG_FMT_JID, - 'log_granular_levels': {}, - 'log_rotate_max_bytes': 0, - 'log_rotate_backup_count': 0, - 'bootstrap_delay': None, - 'cache': 'localfs', -}) +DEFAULT_CLOUD_OPTS = immutabletypes.freeze( + { + "verify_env": True, + "default_include": "cloud.conf.d/*.conf", + # Global defaults + "ssh_auth": "", + "cachedir": os.path.join(salt.syspaths.CACHE_DIR, "cloud"), + "keysize": 4096, + "os": "", + "script": "bootstrap-salt", + "start_action": None, + "enable_hard_maps": False, + "delete_sshkeys": False, + # Custom deploy scripts + "deploy_scripts_search_path": "cloud.deploy.d", + # Logging defaults + "log_file": os.path.join(salt.syspaths.LOGS_DIR, "cloud"), + "log_level": "warning", + "log_level_logfile": None, + "log_datefmt": _DFLT_LOG_DATEFMT, + "log_datefmt_logfile": _DFLT_LOG_DATEFMT_LOGFILE, + "log_fmt_console": _DFLT_LOG_FMT_CONSOLE, + "log_fmt_logfile": _DFLT_LOG_FMT_LOGFILE, + "log_fmt_jid": _DFLT_LOG_FMT_JID, + "log_granular_levels": {}, + "log_rotate_max_bytes": 0, + "log_rotate_backup_count": 0, + "bootstrap_delay": None, + "cache": "localfs", + } +) -DEFAULT_API_OPTS = immutabletypes.freeze({ - # ----- Salt master settings overridden by Salt-API ---------------------> - 'api_pidfile': os.path.join(salt.syspaths.PIDFILE_DIR, 'salt-api.pid'), - 'api_logfile': os.path.join(salt.syspaths.LOGS_DIR, 'api'), - 'rest_timeout': 300, - # <---- Salt master settings overridden by Salt-API ---------------------- -}) +DEFAULT_API_OPTS = immutabletypes.freeze( + { + # ----- Salt master settings overridden by Salt-API ---------------------> + "api_pidfile": os.path.join(salt.syspaths.PIDFILE_DIR, "salt-api.pid"), + "api_logfile": os.path.join(salt.syspaths.LOGS_DIR, "api"), + "rest_timeout": 300, + # <---- Salt master settings overridden by Salt-API ---------------------- + } +) -DEFAULT_SPM_OPTS = immutabletypes.freeze({ - # ----- Salt master settings overridden by SPM ---------------------> - 'spm_conf_file': os.path.join(salt.syspaths.CONFIG_DIR, 'spm'), - 'formula_path': salt.syspaths.SPM_FORMULA_PATH, - 'pillar_path': salt.syspaths.SPM_PILLAR_PATH, - 'reactor_path': salt.syspaths.SPM_REACTOR_PATH, - 'spm_logfile': os.path.join(salt.syspaths.LOGS_DIR, 'spm'), - 'spm_default_include': 'spm.d/*.conf', - # spm_repos_config also includes a .d/ directory - 'spm_repos_config': '/etc/salt/spm.repos', - 'spm_cache_dir': os.path.join(salt.syspaths.CACHE_DIR, 'spm'), - 'spm_build_dir': os.path.join(salt.syspaths.SRV_ROOT_DIR, 'spm_build'), - 'spm_build_exclude': ['CVS', '.hg', '.git', '.svn'], - 'spm_db': os.path.join(salt.syspaths.CACHE_DIR, 'spm', 'packages.db'), - 'cache': 'localfs', - 'spm_repo_dups': 'ignore', - # If set, spm_node_type will be either master or minion, but they should - # NOT be a default - 'spm_node_type': '', - 'spm_share_dir': os.path.join(salt.syspaths.SHARE_DIR, 'spm'), - # <---- Salt master settings overridden by SPM ---------------------- -}) +DEFAULT_SPM_OPTS = immutabletypes.freeze( + { + # ----- Salt master settings overridden by SPM ---------------------> + "spm_conf_file": os.path.join(salt.syspaths.CONFIG_DIR, "spm"), + "formula_path": salt.syspaths.SPM_FORMULA_PATH, + "pillar_path": salt.syspaths.SPM_PILLAR_PATH, + "reactor_path": salt.syspaths.SPM_REACTOR_PATH, + "spm_logfile": os.path.join(salt.syspaths.LOGS_DIR, "spm"), + "spm_default_include": "spm.d/*.conf", + # spm_repos_config also includes a .d/ directory + "spm_repos_config": "/etc/salt/spm.repos", + "spm_cache_dir": os.path.join(salt.syspaths.CACHE_DIR, "spm"), + "spm_build_dir": os.path.join(salt.syspaths.SRV_ROOT_DIR, "spm_build"), + "spm_build_exclude": ["CVS", ".hg", ".git", ".svn"], + "spm_db": os.path.join(salt.syspaths.CACHE_DIR, "spm", "packages.db"), + "cache": "localfs", + "spm_repo_dups": "ignore", + # If set, spm_node_type will be either master or minion, but they should + # NOT be a default + "spm_node_type": "", + "spm_share_dir": os.path.join(salt.syspaths.SHARE_DIR, "spm"), + # <---- Salt master settings overridden by SPM ---------------------- + } +) -VM_CONFIG_DEFAULTS = immutabletypes.freeze({ - 'default_include': 'cloud.profiles.d/*.conf', -}) +VM_CONFIG_DEFAULTS = immutabletypes.freeze( + {"default_include": "cloud.profiles.d/*.conf"} +) -PROVIDER_CONFIG_DEFAULTS = immutabletypes.freeze({ - 'default_include': 'cloud.providers.d/*.conf', -}) +PROVIDER_CONFIG_DEFAULTS = immutabletypes.freeze( + {"default_include": "cloud.providers.d/*.conf"} +) # <---- Salt Cloud Configuration Defaults ------------------------------------ def _normalize_roots(file_roots): - ''' + """ Normalize file or pillar roots. - ''' + """ for saltenv, dirs in six.iteritems(file_roots): normalized_saltenv = six.text_type(saltenv) if normalized_saltenv != saltenv: file_roots[normalized_saltenv] = file_roots.pop(saltenv) if not isinstance(dirs, (list, tuple)): file_roots[normalized_saltenv] = [] - file_roots[normalized_saltenv] = \ - _expand_glob_path(file_roots[normalized_saltenv]) + file_roots[normalized_saltenv] = _expand_glob_path( + file_roots[normalized_saltenv] + ) return file_roots def _validate_pillar_roots(pillar_roots): - ''' + """ If the pillar_roots option has a key that is None then we will error out, just replace it with an empty list - ''' + """ if not isinstance(pillar_roots, dict): - log.warning('The pillar_roots parameter is not properly formatted,' - ' using defaults') - return {'base': _expand_glob_path([salt.syspaths.BASE_PILLAR_ROOTS_DIR])} + log.warning( + "The pillar_roots parameter is not properly formatted," " using defaults" + ) + return {"base": _expand_glob_path([salt.syspaths.BASE_PILLAR_ROOTS_DIR])} return _normalize_roots(pillar_roots) def _validate_file_roots(file_roots): - ''' + """ If the file_roots option has a key that is None then we will error out, just replace it with an empty list - ''' + """ if not isinstance(file_roots, dict): - log.warning('The file_roots parameter is not properly formatted,' - ' using defaults') - return {'base': _expand_glob_path([salt.syspaths.BASE_FILE_ROOTS_DIR])} + log.warning( + "The file_roots parameter is not properly formatted," " using defaults" + ) + return {"base": _expand_glob_path([salt.syspaths.BASE_FILE_ROOTS_DIR])} return _normalize_roots(file_roots) def _expand_glob_path(file_roots): - ''' + """ Applies shell globbing to a set of directories and returns the expanded paths - ''' + """ unglobbed_path = [] for path in file_roots: try: @@ -1966,10 +1712,11 @@ def _expand_glob_path(file_roots): def _validate_opts(opts): - ''' + """ Check that all of the types of values passed into the config are of the right types - ''' + """ + def format_multi_opt(valid_type): try: num_types = len(valid_type) @@ -1978,6 +1725,7 @@ def _validate_opts(opts): # passed. return valid_type.__name__ else: + def get_types(types, type_tuple): for item in type_tuple: if isinstance(item, tuple): @@ -1987,21 +1735,23 @@ def _validate_opts(opts): types.append(item.__name__) except AttributeError: log.warning( - 'Unable to interpret type %s while validating ' - 'configuration', item + "Unable to interpret type %s while validating " + "configuration", + item, ) + types = [] get_types(types, valid_type) - ret = ', '.join(types[:-1]) - ret += ' or ' + types[-1] + ret = ", ".join(types[:-1]) + ret += " or " + types[-1] return ret errors = [] err = ( - 'Config option \'{0}\' with value {1} has an invalid type of {2}, a ' - '{3} is required for this option' + "Config option '{0}' with value {1} has an invalid type of {2}, a " + "{3} is required for this option" ) for key, val in six.iteritems(opts): if key in VALID_OPTS: @@ -2021,10 +1771,10 @@ def _validate_opts(opts): # We don't know what data type sdb will return at run-time so we # simply cannot check it for correctness here at start-time. - if isinstance(val, six.string_types) and val.startswith('sdb://'): + if isinstance(val, six.string_types) and val.startswith("sdb://"): continue - if hasattr(VALID_OPTS[key], '__call__'): + if hasattr(VALID_OPTS[key], "__call__"): try: VALID_OPTS[key](val) if isinstance(val, (list, dict)): @@ -2035,31 +1785,26 @@ def _validate_opts(opts): # pass despite not being the correct type. errors.append( err.format( - key, - val, - type(val).__name__, - VALID_OPTS[key].__name__ + key, val, type(val).__name__, VALID_OPTS[key].__name__ ) ) except (TypeError, ValueError): errors.append( - err.format(key, - val, - type(val).__name__, - VALID_OPTS[key].__name__) + err.format( + key, val, type(val).__name__, VALID_OPTS[key].__name__ + ) ) continue errors.append( - err.format(key, - val, - type(val).__name__, - format_multi_opt(VALID_OPTS[key])) + err.format( + key, val, type(val).__name__, format_multi_opt(VALID_OPTS[key]) + ) ) # Convert list to comma-delimited string for 'return' config option - if isinstance(opts.get('return'), list): - opts['return'] = ','.join(opts['return']) + if isinstance(opts.get("return"), list): + opts["return"] = ",".join(opts["return"]) for error in errors: log.warning(error) @@ -2069,77 +1814,80 @@ def _validate_opts(opts): def _validate_ssh_minion_opts(opts): - ''' + """ Ensure we're not using any invalid ssh_minion_opts. We want to make sure that the ssh_minion_opts does not override any pillar or fileserver options inherited from the master config. To add other items, modify the if statement in the for loop below. - ''' - ssh_minion_opts = opts.get('ssh_minion_opts', {}) + """ + ssh_minion_opts = opts.get("ssh_minion_opts", {}) if not isinstance(ssh_minion_opts, dict): - log.error('Invalidly-formatted ssh_minion_opts') - opts.pop('ssh_minion_opts') + log.error("Invalidly-formatted ssh_minion_opts") + opts.pop("ssh_minion_opts") for opt_name in list(ssh_minion_opts): - if re.match('^[a-z0-9]+fs_', opt_name, flags=re.IGNORECASE) \ - or ('pillar' in opt_name and not 'ssh_merge_pillar' == opt_name) \ - or opt_name in ('fileserver_backend',): + if ( + re.match("^[a-z0-9]+fs_", opt_name, flags=re.IGNORECASE) + or ("pillar" in opt_name and not "ssh_merge_pillar" == opt_name) + or opt_name in ("fileserver_backend",) + ): log.warning( - '\'%s\' is not a valid ssh_minion_opts parameter, ignoring', - opt_name + "'%s' is not a valid ssh_minion_opts parameter, ignoring", opt_name ) ssh_minion_opts.pop(opt_name) def _append_domain(opts): - ''' + """ Append a domain to the existing id if it doesn't already exist - ''' + """ # Domain already exists - if opts['id'].endswith(opts['append_domain']): - return opts['id'] + if opts["id"].endswith(opts["append_domain"]): + return opts["id"] # Trailing dot should mean an FQDN that is terminated, leave it alone. - if opts['id'].endswith('.'): - return opts['id'] - return '{0[id]}.{0[append_domain]}'.format(opts) + if opts["id"].endswith("."): + return opts["id"] + return "{0[id]}.{0[append_domain]}".format(opts) def _read_conf_file(path): - ''' + """ Read in a config file from a given path and process it into a dictionary - ''' - log.debug('Reading configuration from %s', path) - with salt.utils.files.fopen(path, 'r') as conf_file: + """ + log.debug("Reading configuration from %s", path) + with salt.utils.files.fopen(path, "r") as conf_file: try: conf_opts = salt.utils.yaml.safe_load(conf_file) or {} except salt.utils.yaml.YAMLError as err: - message = 'Error parsing configuration file: {0} - {1}'.format(path, err) + message = "Error parsing configuration file: {0} - {1}".format(path, err) log.error(message) raise salt.exceptions.SaltConfigurationError(message) # only interpret documents as a valid conf, not things like strings, # which might have been caused by invalid yaml syntax if not isinstance(conf_opts, dict): - message = 'Error parsing configuration file: {0} - conf ' \ - 'should be a document, not {1}.'.format(path, type(conf_opts)) + message = ( + "Error parsing configuration file: {0} - conf " + "should be a document, not {1}.".format(path, type(conf_opts)) + ) log.error(message) raise salt.exceptions.SaltConfigurationError(message) # allow using numeric ids: convert int to string - if 'id' in conf_opts: - if not isinstance(conf_opts['id'], six.string_types): - conf_opts['id'] = six.text_type(conf_opts['id']) + if "id" in conf_opts: + if not isinstance(conf_opts["id"], six.string_types): + conf_opts["id"] = six.text_type(conf_opts["id"]) else: - conf_opts['id'] = salt.utils.data.decode(conf_opts['id']) + conf_opts["id"] = salt.utils.data.decode(conf_opts["id"]) return conf_opts def _absolute_path(path, relative_to=None): - ''' + """ Return an absolute path. In case ``relative_to`` is passed and ``path`` is not an absolute path, we try to prepend ``relative_to`` to ``path``and if that path exists, return that one - ''' + """ if path and os.path.isabs(path): return path @@ -2147,18 +1895,19 @@ def _absolute_path(path, relative_to=None): _abspath = os.path.join(relative_to, path) if os.path.isfile(_abspath): log.debug( - 'Relative path \'%s\' converted to existing absolute path ' - '\'%s\'', path, _abspath + "Relative path '%s' converted to existing absolute path " "'%s'", + path, + _abspath, ) return _abspath return path def load_config(path, env_var, default_path=None, exit_on_config_errors=True): - ''' + """ Returns configuration dict from parsing either the file described by ``path`` or the environment variable described by ``env_var`` as YAML. - ''' + """ if path is None: # When the passed path is None, we just want the configuration # defaults, not actually loading the whole configuration. @@ -2170,15 +1919,18 @@ def load_config(path, env_var, default_path=None, exit_on_config_errors=True): # argument. Let's issue a warning message that the environ vars won't # work. import inspect + previous_frame = inspect.getframeinfo(inspect.currentframe().f_back) log.warning( "The function '%s()' defined in '%s' is not yet using the " "new 'default_path' argument to `salt.config.load_config()`. " "As such, the '%s' environment variable will be ignored", - previous_frame.function, previous_frame.filename, env_var + previous_frame.function, + previous_frame.filename, + env_var, ) # In this case, maintain old behavior - default_path = DEFAULT_MASTER_OPTS['conf_file'] + default_path = DEFAULT_MASTER_OPTS["conf_file"] # Default to the environment variable path, if it exists env_path = os.environ.get(env_var, path) @@ -2193,11 +1945,11 @@ def load_config(path, env_var, default_path=None, exit_on_config_errors=True): # If the configuration file is missing, attempt to copy the template, # after removing the first header line. if not os.path.isfile(path): - template = '{0}.template'.format(path) + template = "{0}.template".format(path) if os.path.isfile(template): - log.debug('Writing %s based on %s', path, template) - with salt.utils.files.fopen(path, 'w') as out: - with salt.utils.files.fopen(template, 'r') as ifile: + log.debug("Writing %s based on %s", path, template) + with salt.utils.files.fopen(path, "w") as out: + with salt.utils.files.fopen(template, "r") as ifile: ifile.readline() # skip first line out.write(ifile.read()) @@ -2206,22 +1958,22 @@ def load_config(path, env_var, default_path=None, exit_on_config_errors=True): if salt.utils.validate.path.is_readable(path): try: opts = _read_conf_file(path) - opts['conf_file'] = path + opts["conf_file"] = path except salt.exceptions.SaltConfigurationError as error: log.error(error) if exit_on_config_errors: sys.exit(salt.defaults.exitcodes.EX_GENERIC) else: - log.debug('Missing configuration file: %s', path) + log.debug("Missing configuration file: %s", path) return opts def include_config(include, orig_path, verbose, exit_on_config_errors=False): - ''' + """ Parses extra configuration file(s) specified in an include list in the main config file. - ''' + """ # Protect against empty option if not include: return {} @@ -2248,11 +2000,12 @@ def include_config(include, orig_path, verbose, exit_on_config_errors=False): if verbose: log.warning( 'Warning parsing configuration file: "include" path/glob ' - "'%s' matches no files", path + "'%s' matches no files", + path, ) for fn_ in sorted(glob_matches): - log.debug('Including configuration from \'%s\'', fn_) + log.debug("Including configuration from '%s'", fn_) try: opts = _read_conf_file(fn_) except salt.exceptions.SaltConfigurationError as error: @@ -2262,10 +2015,10 @@ def include_config(include, orig_path, verbose, exit_on_config_errors=False): else: # Initialize default config if we wish to skip config errors opts = {} - schedule = opts.get('schedule', {}) - if schedule and 'schedule' in configuration: - configuration['schedule'].update(schedule) - include = opts.get('include', []) + schedule = opts.get("schedule", {}) + if schedule and "schedule" in configuration: + configuration["schedule"].update(schedule) + include = opts.get("include", []) if include: opts.update(include_config(include, fn_, verbose)) @@ -2275,11 +2028,11 @@ def include_config(include, orig_path, verbose, exit_on_config_errors=False): def prepend_root_dir(opts, path_options): - ''' + """ Prepends the options that represent filesystem paths with value of the 'root_dir' option. - ''' - root_dir = os.path.abspath(opts['root_dir']) + """ + root_dir = os.path.abspath(opts["root_dir"]) def_root_dir = salt.syspaths.ROOT_DIR.rstrip(os.sep) for path_option in path_options: if path_option in opts: @@ -2289,11 +2042,10 @@ def prepend_root_dir(opts, path_options): # When running testsuite, salt.syspaths.ROOT_DIR is often empty if path == def_root_dir or path.startswith(def_root_dir + os.sep): # Remove the default root dir prefix - tmp_path_def_root_dir = path[len(def_root_dir):] - if root_dir and (path == root_dir or - path.startswith(root_dir + os.sep)): + tmp_path_def_root_dir = path[len(def_root_dir) :] + if root_dir and (path == root_dir or path.startswith(root_dir + os.sep)): # Remove the root dir prefix - tmp_path_root_dir = path[len(root_dir):] + tmp_path_root_dir = path[len(root_dir) :] if tmp_path_def_root_dir and not tmp_path_root_dir: # Just the default root dir matched path = tmp_path_def_root_dir @@ -2324,27 +2076,28 @@ def prepend_root_dir(opts, path_options): def insert_system_path(opts, paths): - ''' + """ Inserts path into python path taking into consideration 'root_dir' option. - ''' + """ if isinstance(paths, six.string_types): paths = [paths] for path in paths: - path_options = {'path': path, 'root_dir': opts['root_dir']} + path_options = {"path": path, "root_dir": opts["root_dir"]} prepend_root_dir(path_options, path_options) - if (os.path.isdir(path_options['path']) - and path_options['path'] not in sys.path): - sys.path.insert(0, path_options['path']) + if os.path.isdir(path_options["path"]) and path_options["path"] not in sys.path: + sys.path.insert(0, path_options["path"]) -def minion_config(path, - env_var='SALT_MINION_CONFIG', - defaults=None, - cache_minion_id=False, - ignore_config_errors=True, - minion_id=None, - role='minion'): - ''' +def minion_config( + path, + env_var="SALT_MINION_CONFIG", + defaults=None, + cache_minion_id=False, + ignore_config_errors=True, + minion_id=None, + role="minion", +): + """ Reads in the minion configuration file and sets up special options This is useful for Minion-side operations, such as the @@ -2355,47 +2108,57 @@ def minion_config(path, import salt.config minion_opts = salt.config.minion_config('/etc/salt/minion') - ''' + """ if defaults is None: defaults = DEFAULT_MINION_OPTS.copy() if not os.environ.get(env_var, None): # No valid setting was given using the configuration variable. # Lets see is SALT_CONFIG_DIR is of any use - salt_config_dir = os.environ.get('SALT_CONFIG_DIR', None) + salt_config_dir = os.environ.get("SALT_CONFIG_DIR", None) if salt_config_dir: - env_config_file_path = os.path.join(salt_config_dir, 'minion') + env_config_file_path = os.path.join(salt_config_dir, "minion") if salt_config_dir and os.path.isfile(env_config_file_path): # We can get a configuration file using SALT_CONFIG_DIR, let's # update the environment with this information os.environ[env_var] = env_config_file_path - overrides = load_config(path, env_var, DEFAULT_MINION_OPTS['conf_file']) - default_include = overrides.get('default_include', - defaults['default_include']) - include = overrides.get('include', []) + overrides = load_config(path, env_var, DEFAULT_MINION_OPTS["conf_file"]) + default_include = overrides.get("default_include", defaults["default_include"]) + include = overrides.get("include", []) - overrides.update(include_config(default_include, path, verbose=False, - exit_on_config_errors=not ignore_config_errors)) - overrides.update(include_config(include, path, verbose=True, - exit_on_config_errors=not ignore_config_errors)) + overrides.update( + include_config( + default_include, + path, + verbose=False, + exit_on_config_errors=not ignore_config_errors, + ) + ) + overrides.update( + include_config( + include, path, verbose=True, exit_on_config_errors=not ignore_config_errors + ) + ) - opts = apply_minion_config(overrides, defaults, - cache_minion_id=cache_minion_id, - minion_id=minion_id) - opts['__role'] = role + opts = apply_minion_config( + overrides, defaults, cache_minion_id=cache_minion_id, minion_id=minion_id + ) + opts["__role"] = role apply_sdb(opts) _validate_opts(opts) return opts -def proxy_config(path, - env_var='SALT_PROXY_CONFIG', - defaults=None, - cache_minion_id=False, - ignore_config_errors=True, - minion_id=None): - ''' +def proxy_config( + path, + env_var="SALT_PROXY_CONFIG", + defaults=None, + cache_minion_id=False, + ignore_config_errors=True, + minion_id=None, +): + """ Reads in the proxy minion configuration file and sets up special options This is useful for Minion-side operations, such as the @@ -2406,7 +2169,7 @@ def proxy_config(path, import salt.config proxy_opts = salt.config.proxy_config('/etc/salt/proxy') - ''' + """ if defaults is None: defaults = DEFAULT_MINION_OPTS.copy() @@ -2415,38 +2178,48 @@ def proxy_config(path, if not os.environ.get(env_var, None): # No valid setting was given using the configuration variable. # Lets see is SALT_CONFIG_DIR is of any use - salt_config_dir = os.environ.get('SALT_CONFIG_DIR', None) + salt_config_dir = os.environ.get("SALT_CONFIG_DIR", None) if salt_config_dir: - env_config_file_path = os.path.join(salt_config_dir, 'proxy') + env_config_file_path = os.path.join(salt_config_dir, "proxy") if salt_config_dir and os.path.isfile(env_config_file_path): # We can get a configuration file using SALT_CONFIG_DIR, let's # update the environment with this information os.environ[env_var] = env_config_file_path - overrides = load_config(path, env_var, DEFAULT_PROXY_MINION_OPTS['conf_file']) - default_include = overrides.get('default_include', - defaults['default_include']) - include = overrides.get('include', []) + overrides = load_config(path, env_var, DEFAULT_PROXY_MINION_OPTS["conf_file"]) + default_include = overrides.get("default_include", defaults["default_include"]) + include = overrides.get("include", []) - overrides.update(include_config(default_include, path, verbose=False, - exit_on_config_errors=not ignore_config_errors)) - overrides.update(include_config(include, path, verbose=True, - exit_on_config_errors=not ignore_config_errors)) + overrides.update( + include_config( + default_include, + path, + verbose=False, + exit_on_config_errors=not ignore_config_errors, + ) + ) + overrides.update( + include_config( + include, path, verbose=True, exit_on_config_errors=not ignore_config_errors + ) + ) - opts = apply_minion_config(overrides, defaults, - cache_minion_id=cache_minion_id, - minion_id=minion_id) + opts = apply_minion_config( + overrides, defaults, cache_minion_id=cache_minion_id, minion_id=minion_id + ) apply_sdb(opts) _validate_opts(opts) return opts -def syndic_config(master_config_path, - minion_config_path, - master_env_var='SALT_MASTER_CONFIG', - minion_env_var='SALT_MINION_CONFIG', - minion_defaults=None, - master_defaults=None): +def syndic_config( + master_config_path, + minion_config_path, + master_env_var="SALT_MASTER_CONFIG", + minion_env_var="SALT_MINION_CONFIG", + minion_defaults=None, + master_defaults=None, +): if minion_defaults is None: minion_defaults = DEFAULT_MINION_OPTS.copy() @@ -2455,73 +2228,77 @@ def syndic_config(master_config_path, master_defaults = DEFAULT_MASTER_OPTS.copy() opts = {} - master_opts = master_config( - master_config_path, master_env_var, master_defaults - ) - minion_opts = minion_config( - minion_config_path, minion_env_var, minion_defaults - ) - opts['_minion_conf_file'] = master_opts['conf_file'] - opts['_master_conf_file'] = minion_opts['conf_file'] + master_opts = master_config(master_config_path, master_env_var, master_defaults) + minion_opts = minion_config(minion_config_path, minion_env_var, minion_defaults) + opts["_minion_conf_file"] = master_opts["conf_file"] + opts["_master_conf_file"] = minion_opts["conf_file"] opts.update(master_opts) opts.update(minion_opts) syndic_opts = { - '__role': 'syndic', - 'root_dir': opts.get('root_dir', salt.syspaths.ROOT_DIR), - 'pidfile': opts.get('syndic_pidfile', 'salt-syndic.pid'), - 'log_file': opts.get('syndic_log_file', 'salt-syndic.log'), - 'log_level': master_opts['log_level'], - 'id': minion_opts['id'], - 'pki_dir': minion_opts['pki_dir'], - 'master': opts['syndic_master'], - 'interface': master_opts['interface'], - 'master_port': int( + "__role": "syndic", + "root_dir": opts.get("root_dir", salt.syspaths.ROOT_DIR), + "pidfile": opts.get("syndic_pidfile", "salt-syndic.pid"), + "log_file": opts.get("syndic_log_file", "salt-syndic.log"), + "log_level": master_opts["log_level"], + "id": minion_opts["id"], + "pki_dir": minion_opts["pki_dir"], + "master": opts["syndic_master"], + "interface": master_opts["interface"], + "master_port": int( opts.get( # The user has explicitly defined the syndic master port - 'syndic_master_port', + "syndic_master_port", opts.get( # No syndic_master_port, grab master_port from opts - 'master_port', + "master_port", # No master_opts, grab from the provided minion defaults minion_defaults.get( - 'master_port', + "master_port", # Not on the provided minion defaults, load from the # static minion defaults - DEFAULT_MINION_OPTS['master_port'] - ) - ) + DEFAULT_MINION_OPTS["master_port"], + ), + ), ) ), - 'user': opts.get('syndic_user', opts['user']), - 'sock_dir': os.path.join( - opts['cachedir'], opts.get('syndic_sock_dir', opts['sock_dir']) + "user": opts.get("syndic_user", opts["user"]), + "sock_dir": os.path.join( + opts["cachedir"], opts.get("syndic_sock_dir", opts["sock_dir"]) ), - 'sock_pool_size': master_opts['sock_pool_size'], - 'cachedir': master_opts['cachedir'], + "sock_pool_size": master_opts["sock_pool_size"], + "cachedir": master_opts["cachedir"], } opts.update(syndic_opts) # Prepend root_dir to other paths prepend_root_dirs = [ - 'pki_dir', 'cachedir', 'pidfile', 'sock_dir', 'extension_modules', - 'autosign_file', 'autoreject_file', 'token_dir', 'autosign_grains_dir' + "pki_dir", + "cachedir", + "pidfile", + "sock_dir", + "extension_modules", + "autosign_file", + "autoreject_file", + "token_dir", + "autosign_grains_dir", ] - for config_key in ('log_file', 'key_logfile', 'syndic_log_file'): + for config_key in ("log_file", "key_logfile", "syndic_log_file"): # If this is not a URI and instead a local path - if urlparse(opts.get(config_key, '')).scheme == '': + if urlparse(opts.get(config_key, "")).scheme == "": prepend_root_dirs.append(config_key) prepend_root_dir(opts, prepend_root_dirs) return opts def apply_sdb(opts, sdb_opts=None): - ''' + """ Recurse for sdb:// links for opts - ''' + """ # Late load of SDB to keep CLI light import salt.utils.sdb + if sdb_opts is None: sdb_opts = opts - if isinstance(sdb_opts, six.string_types) and sdb_opts.startswith('sdb://'): + if isinstance(sdb_opts, six.string_types) and sdb_opts.startswith("sdb://"): return salt.utils.sdb.sdb_get(sdb_opts, opts) elif isinstance(sdb_opts, dict): for key, value in six.iteritems(sdb_opts): @@ -2538,24 +2315,27 @@ def apply_sdb(opts, sdb_opts=None): # ----- Salt Cloud Configuration Functions ----------------------------------> -def cloud_config(path, env_var='SALT_CLOUD_CONFIG', defaults=None, - master_config_path=None, master_config=None, - providers_config_path=None, providers_config=None, - profiles_config_path=None, profiles_config=None): - ''' +def cloud_config( + path, + env_var="SALT_CLOUD_CONFIG", + defaults=None, + master_config_path=None, + master_config=None, + providers_config_path=None, + providers_config=None, + profiles_config_path=None, + profiles_config=None, +): + """ Read in the Salt Cloud config and return the dict - ''' + """ if path: config_dir = os.path.dirname(path) else: config_dir = salt.syspaths.CONFIG_DIR # Load the cloud configuration - overrides = load_config( - path, - env_var, - os.path.join(config_dir, 'cloud') - ) + overrides = load_config(path, env_var, os.path.join(config_dir, "cloud")) if defaults is None: defaults = DEFAULT_CLOUD_OPTS.copy() @@ -2566,55 +2346,62 @@ def cloud_config(path, env_var='SALT_CLOUD_CONFIG', defaults=None, # Load cloud configuration from any default or provided includes overrides.update( - salt.config.include_config(overrides['default_include'], path, verbose=False) - ) - include = overrides.get('include', []) - overrides.update( - salt.config.include_config(include, path, verbose=True) + salt.config.include_config(overrides["default_include"], path, verbose=False) ) + include = overrides.get("include", []) + overrides.update(salt.config.include_config(include, path, verbose=True)) # The includes have been evaluated, let's see if master, providers and # profiles configuration settings have been included and if not, set the # default value - if 'master_config' in overrides and master_config_path is None: + if "master_config" in overrides and master_config_path is None: # The configuration setting is being specified in the main cloud # configuration file - master_config_path = overrides['master_config'] - elif 'master_config' not in overrides and not master_config \ - and not master_config_path: + master_config_path = overrides["master_config"] + elif ( + "master_config" not in overrides + and not master_config + and not master_config_path + ): # The configuration setting is not being provided in the main cloud # configuration file, and - master_config_path = os.path.join(config_dir, 'master') + master_config_path = os.path.join(config_dir, "master") # Convert relative to absolute paths if necessary master_config_path = _absolute_path(master_config_path, config_dir) - if 'providers_config' in overrides and providers_config_path is None: + if "providers_config" in overrides and providers_config_path is None: # The configuration setting is being specified in the main cloud # configuration file - providers_config_path = overrides['providers_config'] - elif 'providers_config' not in overrides and not providers_config \ - and not providers_config_path: - providers_config_path = os.path.join(config_dir, 'cloud.providers') + providers_config_path = overrides["providers_config"] + elif ( + "providers_config" not in overrides + and not providers_config + and not providers_config_path + ): + providers_config_path = os.path.join(config_dir, "cloud.providers") # Convert relative to absolute paths if necessary providers_config_path = _absolute_path(providers_config_path, config_dir) - if 'profiles_config' in overrides and profiles_config_path is None: + if "profiles_config" in overrides and profiles_config_path is None: # The configuration setting is being specified in the main cloud # configuration file - profiles_config_path = overrides['profiles_config'] - elif 'profiles_config' not in overrides and not profiles_config \ - and not profiles_config_path: - profiles_config_path = os.path.join(config_dir, 'cloud.profiles') + profiles_config_path = overrides["profiles_config"] + elif ( + "profiles_config" not in overrides + and not profiles_config + and not profiles_config_path + ): + profiles_config_path = os.path.join(config_dir, "cloud.profiles") # Convert relative to absolute paths if necessary profiles_config_path = _absolute_path(profiles_config_path, config_dir) # Prepare the deploy scripts search path deploy_scripts_search_path = overrides.get( - 'deploy_scripts_search_path', - defaults.get('deploy_scripts_search_path', 'cloud.deploy.d') + "deploy_scripts_search_path", + defaults.get("deploy_scripts_search_path", "cloud.deploy.d"), ) if isinstance(deploy_scripts_search_path, six.string_types): deploy_scripts_search_path = [deploy_scripts_search_path] @@ -2639,40 +2426,33 @@ def cloud_config(path, env_var='SALT_CLOUD_CONFIG', defaults=None, # Add the built-in scripts directory to the search path (last resort) deploy_scripts_search_path.append( os.path.abspath( - os.path.join( - os.path.dirname(__file__), - '..', - 'cloud', - 'deploy' - ) + os.path.join(os.path.dirname(__file__), "..", "cloud", "deploy") ) ) # Let's make the search path a tuple and add it to the overrides. - overrides.update( - deploy_scripts_search_path=tuple(deploy_scripts_search_path) - ) + overrides.update(deploy_scripts_search_path=tuple(deploy_scripts_search_path)) # Grab data from the 4 sources # 1st - Master config if master_config_path is not None and master_config is not None: raise salt.exceptions.SaltCloudConfigError( - 'Only pass `master_config` or `master_config_path`, not both.' + "Only pass `master_config` or `master_config_path`, not both." ) elif master_config_path is None and master_config is None: master_config = salt.config.master_config( overrides.get( # use the value from the cloud config file - 'master_config', + "master_config", # if not found, use the default path - os.path.join(salt.syspaths.CONFIG_DIR, 'master') + os.path.join(salt.syspaths.CONFIG_DIR, "master"), ) ) elif master_config_path is not None and master_config is None: master_config = salt.config.master_config(master_config_path) # cloud config has a separate cachedir - del master_config['cachedir'] + del master_config["cachedir"] # 2nd - salt-cloud configuration which was loaded before so we could # extract the master configuration file if needed. @@ -2684,59 +2464,56 @@ def cloud_config(path, env_var='SALT_CLOUD_CONFIG', defaults=None, if providers_config_path is not None and providers_config is not None: raise salt.exceptions.SaltCloudConfigError( - 'Only pass `providers_config` or `providers_config_path`, ' - 'not both.' + "Only pass `providers_config` or `providers_config_path`, " "not both." ) elif providers_config_path is None and providers_config is None: providers_config_path = overrides.get( # use the value from the cloud config file - 'providers_config', + "providers_config", # if not found, use the default path - os.path.join(salt.syspaths.CONFIG_DIR, 'cloud.providers') + os.path.join(salt.syspaths.CONFIG_DIR, "cloud.providers"), ) if profiles_config_path is not None and profiles_config is not None: raise salt.exceptions.SaltCloudConfigError( - 'Only pass `profiles_config` or `profiles_config_path`, not both.' + "Only pass `profiles_config` or `profiles_config_path`, not both." ) elif profiles_config_path is None and profiles_config is None: profiles_config_path = overrides.get( # use the value from the cloud config file - 'profiles_config', + "profiles_config", # if not found, use the default path - os.path.join(salt.syspaths.CONFIG_DIR, 'cloud.profiles') + os.path.join(salt.syspaths.CONFIG_DIR, "cloud.profiles"), ) # Apply the salt-cloud configuration opts = apply_cloud_config(overrides, defaults) # 3rd - Include Cloud Providers - if 'providers' in opts: + if "providers" in opts: if providers_config is not None: raise salt.exceptions.SaltCloudConfigError( - 'Do not mix the old cloud providers configuration with ' - 'the passing a pre-configured providers configuration ' - 'dictionary.' + "Do not mix the old cloud providers configuration with " + "the passing a pre-configured providers configuration " + "dictionary." ) if providers_config_path is not None: providers_confd = os.path.join( - os.path.dirname(providers_config_path), - 'cloud.providers.d', '*' + os.path.dirname(providers_config_path), "cloud.providers.d", "*" ) - if (os.path.isfile(providers_config_path) or - glob.glob(providers_confd)): + if os.path.isfile(providers_config_path) or glob.glob(providers_confd): raise salt.exceptions.SaltCloudConfigError( - 'Do not mix the old cloud providers configuration with ' - 'the new one. The providers configuration should now go ' - 'in the file `{0}` or a separate `*.conf` file within ' - '`cloud.providers.d/` which is relative to `{0}`.'.format( - os.path.join(salt.syspaths.CONFIG_DIR, 'cloud.providers') + "Do not mix the old cloud providers configuration with " + "the new one. The providers configuration should now go " + "in the file `{0}` or a separate `*.conf` file within " + "`cloud.providers.d/` which is relative to `{0}`.".format( + os.path.join(salt.syspaths.CONFIG_DIR, "cloud.providers") ) ) # No exception was raised? It's the old configuration alone - providers_config = opts['providers'] + providers_config = opts["providers"] elif providers_config_path is not None: # Load from configuration file, even if that files does not exist since @@ -2744,22 +2521,21 @@ def cloud_config(path, env_var='SALT_CLOUD_CONFIG', defaults=None, providers_config = cloud_providers_config(providers_config_path) # Let's assign back the computed providers configuration - opts['providers'] = providers_config + opts["providers"] = providers_config # 4th - Include VM profiles config if profiles_config is None: # Load profiles configuration from the provided file - profiles_config = vm_profiles_config(profiles_config_path, - providers_config) - opts['profiles'] = profiles_config + profiles_config = vm_profiles_config(profiles_config_path, providers_config) + opts["profiles"] = profiles_config # recurse opts for sdb configs apply_sdb(opts) # prepend root_dir - prepend_root_dirs = ['cachedir'] - if 'log_file' in opts and urlparse(opts['log_file']).scheme == '': - prepend_root_dirs.append(opts['log_file']) + prepend_root_dirs = ["cachedir"] + if "log_file" in opts and urlparse(opts["log_file"]).scheme == "": + prepend_root_dirs.append(opts["log_file"]) prepend_root_dir(opts, prepend_root_dirs) # Return the final options @@ -2767,9 +2543,9 @@ def cloud_config(path, env_var='SALT_CLOUD_CONFIG', defaults=None, def apply_cloud_config(overrides, defaults=None): - ''' + """ Return a cloud config - ''' + """ if defaults is None: defaults = DEFAULT_CLOUD_OPTS.copy() @@ -2779,53 +2555,49 @@ def apply_cloud_config(overrides, defaults=None): # If the user defined providers in salt cloud's main configuration file, we # need to take care for proper and expected format. - if 'providers' in config: + if "providers" in config: # Keep a copy of the defined providers - providers = config['providers'].copy() + providers = config["providers"].copy() # Reset the providers dictionary - config['providers'] = {} + config["providers"] = {} # Populate the providers dictionary for alias, details in six.iteritems(providers): if isinstance(details, list): for detail in details: - if 'driver' not in detail: + if "driver" not in detail: raise salt.exceptions.SaltCloudConfigError( - 'The cloud provider alias \'{0}\' has an entry ' - 'missing the required setting of \'driver\'.'.format( - alias - ) + "The cloud provider alias '{0}' has an entry " + "missing the required setting of 'driver'.".format(alias) ) - driver = detail['driver'] + driver = detail["driver"] - if ':' in driver: + if ":" in driver: # Weird, but... - alias, driver = driver.split(':') + alias, driver = driver.split(":") - if alias not in config['providers']: - config['providers'][alias] = {} + if alias not in config["providers"]: + config["providers"][alias] = {} - detail['provider'] = '{0}:{1}'.format(alias, driver) - config['providers'][alias][driver] = detail + detail["provider"] = "{0}:{1}".format(alias, driver) + config["providers"][alias][driver] = detail elif isinstance(details, dict): - if 'driver' not in details: + if "driver" not in details: raise salt.exceptions.SaltCloudConfigError( - 'The cloud provider alias \'{0}\' has an entry ' - 'missing the required setting of \'driver\''.format( - alias - ) + "The cloud provider alias '{0}' has an entry " + "missing the required setting of 'driver'".format(alias) ) - driver = details['driver'] + driver = details["driver"] - if ':' in driver: + if ":" in driver: # Weird, but... - alias, driver = driver.split(':') - if alias not in config['providers']: - config['providers'][alias] = {} + alias, driver = driver.split(":") + if alias not in config["providers"]: + config["providers"][alias] = {} - details['provider'] = '{0}:{1}'.format(alias, driver) - config['providers'][alias][driver] = details + details["provider"] = "{0}:{1}".format(alias, driver) + config["providers"][alias][driver] = details # Migrate old configuration config = old_to_new(config) @@ -2835,18 +2607,18 @@ def apply_cloud_config(overrides, defaults=None): def old_to_new(opts): providers = ( - 'AWS', - 'CLOUDSTACK', - 'DIGITALOCEAN', - 'EC2', - 'GOGRID', - 'IBMSCE', - 'JOYENT', - 'LINODE', - 'OPENSTACK', - 'PARALLELS' - 'RACKSPACE', - 'SALTIFY' + "AWS", + "CLOUDSTACK", + "DIGITALOCEAN", + "EC2", + "GOGRID", + "IBMSCE", + "JOYENT", + "LINODE", + "OPENSTACK", + "PARALLELS", + "RACKSPACE", + "SALTIFY", ) for provider in providers: @@ -2855,45 +2627,36 @@ def old_to_new(opts): for opt, val in opts.items(): if provider in opt: value = val - name = opt.split('.', 1)[1] + name = opt.split(".", 1)[1] provider_config[name] = value lprovider = provider.lower() if provider_config: - provider_config['provider'] = lprovider - opts.setdefault('providers', {}) + provider_config["provider"] = lprovider + opts.setdefault("providers", {}) # provider alias - opts['providers'][lprovider] = {} + opts["providers"][lprovider] = {} # provider alias, provider driver - opts['providers'][lprovider][lprovider] = provider_config + opts["providers"][lprovider][lprovider] = provider_config return opts -def vm_profiles_config(path, - providers, - env_var='SALT_CLOUDVM_CONFIG', - defaults=None): - ''' +def vm_profiles_config(path, providers, env_var="SALT_CLOUDVM_CONFIG", defaults=None): + """ Read in the salt cloud VM config file - ''' + """ if defaults is None: defaults = VM_CONFIG_DEFAULTS overrides = salt.config.load_config( - path, env_var, os.path.join(salt.syspaths.CONFIG_DIR, 'cloud.profiles') + path, env_var, os.path.join(salt.syspaths.CONFIG_DIR, "cloud.profiles") ) - default_include = overrides.get( - 'default_include', defaults['default_include'] - ) - include = overrides.get('include', []) + default_include = overrides.get("default_include", defaults["default_include"]) + include = overrides.get("include", []) - overrides.update( - salt.config.include_config(default_include, path, verbose=False) - ) - overrides.update( - salt.config.include_config(include, path, verbose=True) - ) + overrides.update(salt.config.include_config(default_include, path, verbose=False)) + overrides.update(salt.config.include_config(include, path, verbose=True)) return apply_vm_profiles_config(providers, overrides, defaults) @@ -2908,99 +2671,110 @@ def apply_vm_profiles_config(providers, overrides, defaults=None): vms = {} for key, val in six.iteritems(config): - if key in ('conf_file', 'include', 'default_include', 'user'): + if key in ("conf_file", "include", "default_include", "user"): continue if not isinstance(val, dict): raise salt.exceptions.SaltCloudConfigError( - 'The VM profiles configuration found in \'{0[conf_file]}\' is ' - 'not in the proper format'.format(config) + "The VM profiles configuration found in '{0[conf_file]}' is " + "not in the proper format".format(config) ) - val['profile'] = key + val["profile"] = key vms[key] = val # Is any VM profile extending data!? for profile, details in six.iteritems(vms.copy()): - if 'extends' not in details: - if ':' in details['provider']: - alias, driver = details['provider'].split(':') + if "extends" not in details: + if ":" in details["provider"]: + alias, driver = details["provider"].split(":") if alias not in providers or driver not in providers[alias]: log.trace( - 'The profile \'%s\' is defining \'%s\' ' - 'as the provider. Since there is no valid ' - 'configuration for that provider, the profile will be ' - 'removed from the available listing', - profile, details['provider'] + "The profile '%s' is defining '%s' " + "as the provider. Since there is no valid " + "configuration for that provider, the profile will be " + "removed from the available listing", + profile, + details["provider"], ) vms.pop(profile) continue - if 'profiles' not in providers[alias][driver]: - providers[alias][driver]['profiles'] = {} - providers[alias][driver]['profiles'][profile] = details + if "profiles" not in providers[alias][driver]: + providers[alias][driver]["profiles"] = {} + providers[alias][driver]["profiles"][profile] = details - if details['provider'] not in providers: + if details["provider"] not in providers: log.trace( - 'The profile \'%s\' is defining \'%s\' as the ' - 'provider. Since there is no valid configuration for ' - 'that provider, the profile will be removed from the ' - 'available listing', profile, details['provider'] + "The profile '%s' is defining '%s' as the " + "provider. Since there is no valid configuration for " + "that provider, the profile will be removed from the " + "available listing", + profile, + details["provider"], ) vms.pop(profile) continue - driver = next(iter(list(providers[details['provider']].keys()))) - providers[details['provider']][driver].setdefault( - 'profiles', {}).update({profile: details}) - details['provider'] = '{0[provider]}:{1}'.format(details, driver) + driver = next(iter(list(providers[details["provider"]].keys()))) + providers[details["provider"]][driver].setdefault("profiles", {}).update( + {profile: details} + ) + details["provider"] = "{0[provider]}:{1}".format(details, driver) vms[profile] = details continue - extends = details.pop('extends') + extends = details.pop("extends") if extends not in vms: log.error( - 'The \'%s\' profile is trying to extend data from \'%s\' ' - 'though \'%s\' is not defined in the salt profiles loaded ' - 'data. Not extending and removing from listing!', - profile, extends, extends + "The '%s' profile is trying to extend data from '%s' " + "though '%s' is not defined in the salt profiles loaded " + "data. Not extending and removing from listing!", + profile, + extends, + extends, ) vms.pop(profile) continue extended = deepcopy(vms.get(extends)) - extended.pop('profile') + extended.pop("profile") # Merge extended configuration with base profile extended = salt.utils.dictupdate.update(extended, details) - if ':' not in extended['provider']: - if extended['provider'] not in providers: + if ":" not in extended["provider"]: + if extended["provider"] not in providers: log.trace( - 'The profile \'%s\' is defining \'%s\' as the ' - 'provider. Since there is no valid configuration for ' - 'that provider, the profile will be removed from the ' - 'available listing', profile, extended['provider'] + "The profile '%s' is defining '%s' as the " + "provider. Since there is no valid configuration for " + "that provider, the profile will be removed from the " + "available listing", + profile, + extended["provider"], ) vms.pop(profile) continue - driver = next(iter(list(providers[extended['provider']].keys()))) - providers[extended['provider']][driver].setdefault( - 'profiles', {}).update({profile: extended}) + driver = next(iter(list(providers[extended["provider"]].keys()))) + providers[extended["provider"]][driver].setdefault("profiles", {}).update( + {profile: extended} + ) - extended['provider'] = '{0[provider]}:{1}'.format(extended, driver) + extended["provider"] = "{0[provider]}:{1}".format(extended, driver) else: - alias, driver = extended['provider'].split(':') + alias, driver = extended["provider"].split(":") if alias not in providers or driver not in providers[alias]: log.trace( - 'The profile \'%s\' is defining \'%s\' as ' - 'the provider. Since there is no valid configuration ' - 'for that provider, the profile will be removed from ' - 'the available listing', profile, extended['provider'] + "The profile '%s' is defining '%s' as " + "the provider. Since there is no valid configuration " + "for that provider, the profile will be removed from " + "the available listing", + profile, + extended["provider"], ) vms.pop(profile) continue - providers[alias][driver].setdefault('profiles', {}).update( + providers[alias][driver].setdefault("profiles", {}).update( {profile: extended} ) @@ -3010,37 +2784,29 @@ def apply_vm_profiles_config(providers, overrides, defaults=None): return vms -def cloud_providers_config(path, - env_var='SALT_CLOUD_PROVIDERS_CONFIG', - defaults=None): - ''' +def cloud_providers_config(path, env_var="SALT_CLOUD_PROVIDERS_CONFIG", defaults=None): + """ Read in the salt cloud providers configuration file - ''' + """ if defaults is None: defaults = PROVIDER_CONFIG_DEFAULTS overrides = salt.config.load_config( - path, env_var, os.path.join(salt.syspaths.CONFIG_DIR, 'cloud.providers') + path, env_var, os.path.join(salt.syspaths.CONFIG_DIR, "cloud.providers") ) - default_include = overrides.get( - 'default_include', defaults['default_include'] - ) - include = overrides.get('include', []) + default_include = overrides.get("default_include", defaults["default_include"]) + include = overrides.get("include", []) - overrides.update( - salt.config.include_config(default_include, path, verbose=False) - ) - overrides.update( - salt.config.include_config(include, path, verbose=True) - ) + overrides.update(salt.config.include_config(default_include, path, verbose=False)) + overrides.update(salt.config.include_config(include, path, verbose=True)) return apply_cloud_providers_config(overrides, defaults) def apply_cloud_providers_config(overrides, defaults=None): - ''' + """ Apply the loaded cloud providers configuration. - ''' + """ if defaults is None: defaults = PROVIDER_CONFIG_DEFAULTS @@ -3050,24 +2816,22 @@ def apply_cloud_providers_config(overrides, defaults=None): # Is the user still using the old format in the new configuration file?! for name, settings in six.iteritems(config.copy()): - if '.' in name: - log.warning( - 'Please switch to the new providers configuration syntax' - ) + if "." in name: + log.warning("Please switch to the new providers configuration syntax") # Let's help out and migrate the data config = old_to_new(config) # old_to_new will migrate the old data into the 'providers' key of # the config dictionary. Let's map it correctly - for prov_name, prov_settings in six.iteritems(config.pop('providers')): + for prov_name, prov_settings in six.iteritems(config.pop("providers")): config[prov_name] = prov_settings break providers = {} ext_count = 0 for key, val in six.iteritems(config): - if key in ('conf_file', 'include', 'default_include', 'user'): + if key in ("conf_file", "include", "default_include", "user"): continue if not isinstance(val, (list, tuple)): @@ -3077,39 +2841,39 @@ def apply_cloud_providers_config(overrides, defaults=None): # we won't be able to properly reference it. handled_providers = set() for details in val: - if 'driver' not in details: - if 'extends' not in details: + if "driver" not in details: + if "extends" not in details: log.error( - 'Please check your cloud providers configuration. ' - 'There\'s no \'driver\' nor \'extends\' definition ' - 'referenced.' + "Please check your cloud providers configuration. " + "There's no 'driver' nor 'extends' definition " + "referenced." ) continue - if details['driver'] in handled_providers: + if details["driver"] in handled_providers: log.error( - 'You can only have one entry per cloud provider. For ' - 'example, if you have a cloud provider configuration ' - 'section named, \'production\', you can only have a ' - 'single entry for EC2, Joyent, Openstack, and so ' - 'forth.' + "You can only have one entry per cloud provider. For " + "example, if you have a cloud provider configuration " + "section named, 'production', you can only have a " + "single entry for EC2, Joyent, Openstack, and so " + "forth." ) raise salt.exceptions.SaltCloudConfigError( - 'The cloud provider alias \'{0}\' has multiple entries ' - 'for the \'{1[driver]}\' driver.'.format(key, details) + "The cloud provider alias '{0}' has multiple entries " + "for the '{1[driver]}' driver.".format(key, details) ) - handled_providers.add(details['driver']) + handled_providers.add(details["driver"]) for entry in val: - if 'driver' not in entry: - entry['driver'] = '-only-extendable-{0}'.format(ext_count) + if "driver" not in entry: + entry["driver"] = "-only-extendable-{0}".format(ext_count) ext_count += 1 if key not in providers: providers[key] = {} - provider = entry['driver'] + provider = entry["driver"] if provider not in providers[key]: providers[key][provider] = entry @@ -3119,70 +2883,63 @@ def apply_cloud_providers_config(overrides, defaults=None): for provider_alias, entries in six.iteritems(providers.copy()): for driver, details in six.iteritems(entries): # Set a holder for the defined profiles - providers[provider_alias][driver]['profiles'] = {} + providers[provider_alias][driver]["profiles"] = {} - if 'extends' not in details: + if "extends" not in details: continue - extends = details.pop('extends') + extends = details.pop("extends") - if ':' in extends: - alias, provider = extends.split(':') + if ":" in extends: + alias, provider = extends.split(":") if alias not in providers: raise salt.exceptions.SaltCloudConfigError( - 'The \'{0}\' cloud provider entry in \'{1}\' is ' - 'trying to extend data from \'{2}\' though ' - '\'{2}\' is not defined in the salt cloud ' - 'providers loaded data.'.format( - details['driver'], - provider_alias, - alias + "The '{0}' cloud provider entry in '{1}' is " + "trying to extend data from '{2}' though " + "'{2}' is not defined in the salt cloud " + "providers loaded data.".format( + details["driver"], provider_alias, alias ) ) if provider not in providers.get(alias): raise salt.exceptions.SaltCloudConfigError( - 'The \'{0}\' cloud provider entry in \'{1}\' is ' - 'trying to extend data from \'{2}:{3}\' though ' - '\'{3}\' is not defined in \'{1}\''.format( - details['driver'], - provider_alias, - alias, - provider + "The '{0}' cloud provider entry in '{1}' is " + "trying to extend data from '{2}:{3}' though " + "'{3}' is not defined in '{1}'".format( + details["driver"], provider_alias, alias, provider ) ) - details['extends'] = '{0}:{1}'.format(alias, provider) + details["extends"] = "{0}:{1}".format(alias, provider) # change provider details '-only-extendable-' to extended # provider name - details['driver'] = provider + details["driver"] = provider elif providers.get(extends): raise salt.exceptions.SaltCloudConfigError( - 'The \'{0}\' cloud provider entry in \'{1}\' is ' - 'trying to extend from \'{2}\' and no provider was ' - 'specified. Not extending!'.format( - details['driver'], provider_alias, extends + "The '{0}' cloud provider entry in '{1}' is " + "trying to extend from '{2}' and no provider was " + "specified. Not extending!".format( + details["driver"], provider_alias, extends ) ) elif extends not in providers: raise salt.exceptions.SaltCloudConfigError( - 'The \'{0}\' cloud provider entry in \'{1}\' is ' - 'trying to extend data from \'{2}\' though \'{2}\' ' - 'is not defined in the salt cloud providers loaded ' - 'data.'.format( - details['driver'], provider_alias, extends - ) + "The '{0}' cloud provider entry in '{1}' is " + "trying to extend data from '{2}' though '{2}' " + "is not defined in the salt cloud providers loaded " + "data.".format(details["driver"], provider_alias, extends) ) else: if driver in providers.get(extends): - details['extends'] = '{0}:{1}'.format(extends, driver) - elif '-only-extendable-' in providers.get(extends): - details['extends'] = '{0}:{1}'.format( - extends, '-only-extendable-{0}'.format(ext_count) + details["extends"] = "{0}:{1}".format(extends, driver) + elif "-only-extendable-" in providers.get(extends): + details["extends"] = "{0}:{1}".format( + extends, "-only-extendable-{0}".format(ext_count) ) else: # We're still not aware of what we're trying to extend # from. Let's try on next iteration - details['extends'] = extends + details["extends"] = extends keep_looping = True if not keep_looping: break @@ -3193,20 +2950,20 @@ def apply_cloud_providers_config(overrides, defaults=None): for alias, entries in six.iteritems(providers.copy()): for driver, details in six.iteritems(entries): - if 'extends' not in details: + if "extends" not in details: # Extends resolved or non existing, continue! continue - if 'extends' in details['extends']: + if "extends" in details["extends"]: # Since there's a nested extends, resolve this one in the # next iteration keep_looping = True continue # Let's get a reference to what we're supposed to extend - extends = details.pop('extends') + extends = details.pop("extends") # Split the setting in (alias, driver) - ext_alias, ext_driver = extends.split(':') + ext_alias, ext_driver = extends.split(":") # Grab a copy of what should be extended extended = providers.get(ext_alias).get(ext_driver).copy() # Merge the data to extend with the details @@ -3214,7 +2971,7 @@ def apply_cloud_providers_config(overrides, defaults=None): # Update the providers dictionary with the merged data providers[alias][driver] = extended # Update name of the driver, now that it's populated with extended information - if driver.startswith('-only-extendable-'): + if driver.startswith("-only-extendable-"): providers[alias][ext_driver] = providers[alias][driver] # Delete driver with old name to maintain dictionary size del providers[alias][driver] @@ -3226,14 +2983,15 @@ def apply_cloud_providers_config(overrides, defaults=None): # extend from for provider_alias, entries in six.iteritems(providers.copy()): for driver, details in six.iteritems(entries.copy()): - if not driver.startswith('-only-extendable-'): + if not driver.startswith("-only-extendable-"): continue log.info( "There's at least one cloud driver under the '%s' " - 'cloud provider alias which does not have the required ' + "cloud provider alias which does not have the required " "'driver' setting. Removing it from the available " - 'providers listing.', provider_alias + "providers listing.", + provider_alias, ) providers[provider_alias].pop(driver) @@ -3244,7 +3002,7 @@ def apply_cloud_providers_config(overrides, defaults=None): def get_cloud_config_value(name, vm_, opts, default=None, search_global=True): - ''' + """ Search and return a setting in a known order: 1. In the virtual machine's configuration @@ -3252,7 +3010,7 @@ def get_cloud_config_value(name, vm_, opts, default=None, search_global=True): 3. In the virtual machine's provider configuration 4. In the salt cloud configuration if global searching is enabled 5. Return the provided default - ''' + """ # As a last resort, return the default value = default @@ -3263,41 +3021,41 @@ def get_cloud_config_value(name, vm_, opts, default=None, search_global=True): if vm_ and name: # Let's get the value from the profile, if present - if 'profile' in vm_ and vm_['profile'] is not None: - if name in opts['profiles'][vm_['profile']]: + if "profile" in vm_ and vm_["profile"] is not None: + if name in opts["profiles"][vm_["profile"]]: if isinstance(value, dict): - value.update(opts['profiles'][vm_['profile']][name].copy()) + value.update(opts["profiles"][vm_["profile"]][name].copy()) else: - value = deepcopy(opts['profiles'][vm_['profile']][name]) + value = deepcopy(opts["profiles"][vm_["profile"]][name]) # Let's get the value from the provider, if present. - if ':' in vm_['driver']: + if ":" in vm_["driver"]: # The provider is defined as : - alias, driver = vm_['driver'].split(':') - if alias in opts['providers'] and \ - driver in opts['providers'][alias]: - details = opts['providers'][alias][driver] + alias, driver = vm_["driver"].split(":") + if alias in opts["providers"] and driver in opts["providers"][alias]: + details = opts["providers"][alias][driver] if name in details: if isinstance(value, dict): value.update(details[name].copy()) else: value = deepcopy(details[name]) - elif len(opts['providers'].get(vm_['driver'], ())) > 1: + elif len(opts["providers"].get(vm_["driver"], ())) > 1: # The provider is NOT defined as : # and there's more than one entry under the alias. # WARN the user!!!! log.error( "The '%s' cloud provider definition has more than one " - 'entry. Your VM configuration should be specifying the ' + "entry. Your VM configuration should be specifying the " "provider as 'driver: %s:'. Since " "it's not, we're returning the first definition which " - 'might not be what you intended.', - vm_['driver'], vm_['driver'] + "might not be what you intended.", + vm_["driver"], + vm_["driver"], ) - if vm_['driver'] in opts['providers']: + if vm_["driver"] in opts["providers"]: # There's only one driver defined for this provider. This is safe. - alias_defs = opts['providers'].get(vm_['driver']) + alias_defs = opts["providers"].get(vm_["driver"]) provider_driver_defs = alias_defs[next(iter(list(alias_defs.keys())))] if name in provider_driver_defs: # The setting name exists in the VM's provider configuration. @@ -3310,7 +3068,7 @@ def get_cloud_config_value(name, vm_, opts, default=None, search_global=True): if name and vm_ and name in vm_: # The setting name exists in VM configuration. if isinstance(vm_[name], types.GeneratorType): - value = next(vm_[name], '') + value = next(vm_[name], "") else: if isinstance(value, dict) and isinstance(vm_[name], dict): value.update(vm_[name].copy()) @@ -3320,33 +3078,38 @@ def get_cloud_config_value(name, vm_, opts, default=None, search_global=True): return value -def is_provider_configured(opts, provider, required_keys=(), log_message=True, aliases=()): - ''' +def is_provider_configured( + opts, provider, required_keys=(), log_message=True, aliases=() +): + """ Check and return the first matching and fully configured cloud provider configuration. - ''' - if ':' in provider: - alias, driver = provider.split(':') - if alias not in opts['providers']: + """ + if ":" in provider: + alias, driver = provider.split(":") + if alias not in opts["providers"]: return False - if driver not in opts['providers'][alias]: + if driver not in opts["providers"][alias]: return False for key in required_keys: - if opts['providers'][alias][driver].get(key, None) is None: + if opts["providers"][alias][driver].get(key, None) is None: if log_message is True: # There's at least one require configuration key which is not # set. log.warning( "The required '%s' configuration setting is missing " "from the '%s' driver, which is configured under the " - "'%s' alias.", key, provider, alias + "'%s' alias.", + key, + provider, + alias, ) return False # If we reached this far, there's a properly configured provider. # Return it! - return opts['providers'][alias][driver] + return opts["providers"][alias][driver] - for alias, drivers in six.iteritems(opts['providers']): + for alias, drivers in six.iteritems(opts["providers"]): for driver, provider_details in six.iteritems(drivers): if driver != provider and driver not in aliases: continue @@ -3362,7 +3125,10 @@ def is_provider_configured(opts, provider, required_keys=(), log_message=True, a log.warning( "The required '%s' configuration setting is " "missing from the '%s' driver, which is configured " - "under the '%s' alias.", key, provider, alias + "under the '%s' alias.", + key, + provider, + alias, ) skip_provider = True break @@ -3378,7 +3144,7 @@ def is_provider_configured(opts, provider, required_keys=(), log_message=True, a def is_profile_configured(opts, provider, profile_name, vm_=None): - ''' + """ Check if the requested profile contains the minimum required parameters for a profile. @@ -3386,47 +3152,74 @@ def is_profile_configured(opts, provider, profile_name, vm_=None): drivers also require size keys. .. versionadded:: 2015.8.0 - ''' + """ # Standard dict keys required by all drivers. - required_keys = ['provider'] - alias, driver = provider.split(':') + required_keys = ["provider"] + alias, driver = provider.split(":") # Most drivers need an image to be specified, but some do not. - non_image_drivers = ['nova', 'virtualbox', 'libvirt', 'softlayer', 'oneandone', 'profitbricks'] + non_image_drivers = [ + "nova", + "virtualbox", + "libvirt", + "softlayer", + "oneandone", + "profitbricks", + ] # Most drivers need a size, but some do not. - non_size_drivers = ['opennebula', 'parallels', 'proxmox', 'scaleway', - 'softlayer', 'softlayer_hw', 'vmware', 'vsphere', - 'virtualbox', 'libvirt', 'oneandone', 'profitbricks'] + non_size_drivers = [ + "opennebula", + "parallels", + "proxmox", + "scaleway", + "softlayer", + "softlayer_hw", + "vmware", + "vsphere", + "virtualbox", + "libvirt", + "oneandone", + "profitbricks", + ] - provider_key = opts['providers'][alias][driver] - profile_key = opts['providers'][alias][driver]['profiles'][profile_name] + provider_key = opts["providers"][alias][driver] + profile_key = opts["providers"][alias][driver]["profiles"][profile_name] # If cloning on Linode, size and image are not necessary. # They are obtained from the to-be-cloned VM. - if driver == 'linode' and profile_key.get('clonefrom', False): - non_image_drivers.append('linode') - non_size_drivers.append('linode') - elif driver == 'gce' and 'sourceImage' in six.text_type(vm_.get('ex_disks_gce_struct')): - non_image_drivers.append('gce') + if driver == "linode" and profile_key.get("clonefrom", False): + non_image_drivers.append("linode") + non_size_drivers.append("linode") + elif driver == "gce" and "sourceImage" in six.text_type( + vm_.get("ex_disks_gce_struct") + ): + non_image_drivers.append("gce") # If cloning on VMware, specifying image is not necessary. - if driver == 'vmware' and 'image' not in list(profile_key.keys()): - non_image_drivers.append('vmware') + if driver == "vmware" and "image" not in list(profile_key.keys()): + non_image_drivers.append("vmware") if driver not in non_image_drivers: - required_keys.append('image') - if driver == 'vmware': - required_keys.append('datastore') - elif driver in ['linode', 'virtualbox']: - required_keys.append('clonefrom') - elif driver == 'nova': - nova_image_keys = ['image', 'block_device_mapping', 'block_device', 'boot_volume'] - if not any([key in provider_key for key in nova_image_keys]) and not any([key in profile_key for key in nova_image_keys]): + required_keys.append("image") + if driver == "vmware": + required_keys.append("datastore") + elif driver in ["linode", "virtualbox"]: + required_keys.append("clonefrom") + elif driver == "nova": + nova_image_keys = [ + "image", + "block_device_mapping", + "block_device", + "boot_volume", + ] + if not any([key in provider_key for key in nova_image_keys]) and not any( + [key in profile_key for key in nova_image_keys] + ): required_keys.extend(nova_image_keys) if driver not in non_size_drivers: - required_keys.append('size') + required_keys.append("size") # Check if required fields are supplied in the provider config. If they # are present, remove it from the required_keys list. @@ -3448,7 +3241,9 @@ def is_profile_configured(opts, provider, profile_name, vm_=None): log.error( "The required '%s' configuration setting is missing from " "the '%s' profile, which is configured under the '%s' alias.", - item, profile_name, alias + item, + profile_name, + alias, ) return False @@ -3456,7 +3251,7 @@ def is_profile_configured(opts, provider, profile_name, vm_=None): def check_driver_dependencies(driver, dependencies): - ''' + """ Check if the driver's dependencies are available. .. versionadded:: 2015.8.0 @@ -3466,25 +3261,29 @@ def check_driver_dependencies(driver, dependencies): dependencies The dictionary of dependencies to check. - ''' + """ ret = True for key, value in six.iteritems(dependencies): if value is False: log.warning( "Missing dependency: '%s'. The %s driver requires " - "'%s' to be installed.", key, driver, key + "'%s' to be installed.", + key, + driver, + key, ) ret = False return ret + # <---- Salt Cloud Configuration Functions ----------------------------------- def _cache_id(minion_id, cache_file): - ''' + """ Helper function, writes minion id to a cache file. - ''' + """ path = os.path.dirname(cache_file) try: if not os.path.isdir(path): @@ -3494,42 +3293,42 @@ def _cache_id(minion_id, cache_file): if os.path.isdir(path): pass else: - log.error('Failed to create dirs to minion_id file: %s', exc) + log.error("Failed to create dirs to minion_id file: %s", exc) try: - with salt.utils.files.fopen(cache_file, 'w') as idf: + with salt.utils.files.fopen(cache_file, "w") as idf: idf.write(minion_id) except (IOError, OSError) as exc: - log.error('Could not cache minion ID: %s', exc) + log.error("Could not cache minion ID: %s", exc) def call_id_function(opts): - ''' + """ Evaluate the function that determines the ID if the 'id_function' option is set and return the result - ''' - if opts.get('id'): - return opts['id'] + """ + if opts.get("id"): + return opts["id"] # Import 'salt.loader' here to avoid a circular dependency import salt.loader as loader - if isinstance(opts['id_function'], six.string_types): - mod_fun = opts['id_function'] + if isinstance(opts["id_function"], six.string_types): + mod_fun = opts["id_function"] fun_kwargs = {} - elif isinstance(opts['id_function'], dict): - mod_fun, fun_kwargs = six.next(six.iteritems(opts['id_function'])) + elif isinstance(opts["id_function"], dict): + mod_fun, fun_kwargs = six.next(six.iteritems(opts["id_function"])) if fun_kwargs is None: fun_kwargs = {} else: - log.error('\'id_function\' option is neither a string nor a dictionary') + log.error("'id_function' option is neither a string nor a dictionary") sys.exit(salt.defaults.exitcodes.EX_GENERIC) # split module and function and try loading the module - mod, fun = mod_fun.split('.') - if not opts.get('grains'): + mod, fun = mod_fun.split(".") + if not opts.get("grains"): # Get grains for use by the module - opts['grains'] = loader.grains(opts) + opts["grains"] = loader.grains(opts) try: id_mod = loader.raw_mod(opts, mod, fun) @@ -3540,44 +3339,45 @@ def call_id_function(opts): if not isinstance(newid, six.string_types) or not newid: log.error( 'Function %s returned value "%s" of type %s instead of string', - mod_fun, newid, type(newid) + mod_fun, + newid, + type(newid), ) sys.exit(salt.defaults.exitcodes.EX_GENERIC) - log.info('Evaluated minion ID from module: %s', mod_fun) + log.info("Evaluated minion ID from module: %s", mod_fun) return newid except TypeError: log.error( - 'Function arguments %s are incorrect for function %s', - fun_kwargs, mod_fun + "Function arguments %s are incorrect for function %s", fun_kwargs, mod_fun ) sys.exit(salt.defaults.exitcodes.EX_GENERIC) except KeyError: - log.error('Failed to load module %s', mod_fun) + log.error("Failed to load module %s", mod_fun) sys.exit(salt.defaults.exitcodes.EX_GENERIC) def remove_domain_from_fqdn(opts, newid): - ''' + """ Depending on the values of `minion_id_remove_domain`, remove all domains or a single domain from a FQDN, effectivly generating a hostname. - ''' - opt_domain = opts.get('minion_id_remove_domain') + """ + opt_domain = opts.get("minion_id_remove_domain") if opt_domain is True: - if '.' in newid: + if "." in newid: # Remove any domain - newid, xdomain = newid.split('.', 1) - log.debug('Removed any domain (%s) from minion id.', xdomain) + newid, xdomain = newid.split(".", 1) + log.debug("Removed any domain (%s) from minion id.", xdomain) else: # Must be string type - if newid.upper().endswith('.' + opt_domain.upper()): + if newid.upper().endswith("." + opt_domain.upper()): # Remove single domain - newid = newid[:-len('.' + opt_domain)] - log.debug('Removed single domain %s from minion id.', opt_domain) + newid = newid[: -len("." + opt_domain)] + log.debug("Removed single domain %s from minion id.", opt_domain) return newid def get_id(opts, cache_minion_id=False): - ''' + """ Guess the id of the minion. If CONFIG_DIR/minion_id exists, use the cached minion ID from that file. @@ -3586,234 +3386,239 @@ def get_id(opts, cache_minion_id=False): Returns two values: the detected ID, and a boolean value noting whether or not an IP address is being used for the ID. - ''' - if opts['root_dir'] is None: + """ + if opts["root_dir"] is None: root_dir = salt.syspaths.ROOT_DIR else: - root_dir = opts['root_dir'] + root_dir = opts["root_dir"] config_dir = salt.syspaths.CONFIG_DIR if config_dir.startswith(salt.syspaths.ROOT_DIR): config_dir = config_dir.split(salt.syspaths.ROOT_DIR, 1)[-1] # Check for cached minion ID - id_cache = os.path.join(root_dir, - config_dir.lstrip(os.path.sep), - 'minion_id') + id_cache = os.path.join(root_dir, config_dir.lstrip(os.path.sep), "minion_id") - if opts.get('minion_id_caching', True): + if opts.get("minion_id_caching", True): try: with salt.utils.files.fopen(id_cache) as idf: name = salt.utils.stringutils.to_unicode(idf.readline().strip()) bname = salt.utils.stringutils.to_bytes(name) if bname.startswith(codecs.BOM): # Remove BOM if exists - name = salt.utils.stringutils.to_str(bname.replace(codecs.BOM, '', 1)) - if name and name != 'localhost': - log.debug('Using cached minion ID from %s: %s', id_cache, name) + name = salt.utils.stringutils.to_str( + bname.replace(codecs.BOM, "", 1) + ) + if name and name != "localhost": + log.debug("Using cached minion ID from %s: %s", id_cache, name) return name, False except (IOError, OSError): pass - if '__role' in opts and opts.get('__role') == 'minion': + if "__role" in opts and opts.get("__role") == "minion": log.debug( - 'Guessing ID. The id can be explicitly set in %s', - os.path.join(salt.syspaths.CONFIG_DIR, 'minion') + "Guessing ID. The id can be explicitly set in %s", + os.path.join(salt.syspaths.CONFIG_DIR, "minion"), ) - if opts.get('id_function'): + if opts.get("id_function"): newid = call_id_function(opts) else: newid = salt.utils.network.generate_minion_id() - if opts.get('minion_id_lowercase'): + if opts.get("minion_id_lowercase"): newid = newid.lower() - log.debug('Changed minion id %s to lowercase.', newid) + log.debug("Changed minion id %s to lowercase.", newid) # Optionally remove one or many domains in a generated minion id - if opts.get('minion_id_remove_domain'): + if opts.get("minion_id_remove_domain"): newid = remove_domain_from_fqdn(opts, newid) - if '__role' in opts and opts.get('__role') == 'minion': - if opts.get('id_function'): + if "__role" in opts and opts.get("__role") == "minion": + if opts.get("id_function"): log.debug( - 'Found minion id from external function %s: %s', - opts['id_function'], newid + "Found minion id from external function %s: %s", + opts["id_function"], + newid, ) else: - log.debug('Found minion id from generate_minion_id(): %s', newid) - if cache_minion_id and opts.get('minion_id_caching', True): + log.debug("Found minion id from generate_minion_id(): %s", newid) + if cache_minion_id and opts.get("minion_id_caching", True): _cache_id(newid, id_cache) is_ipv4 = salt.utils.network.is_ipv4(newid) return newid, is_ipv4 def _update_ssl_config(opts): - ''' + """ Resolves string names to integer constant in ssl configuration. - ''' - if opts['ssl'] in (None, False): - opts['ssl'] = None + """ + if opts["ssl"] in (None, False): + opts["ssl"] = None return - if opts['ssl'] is True: - opts['ssl'] = {} + if opts["ssl"] is True: + opts["ssl"] = {} return import ssl - for key, prefix in (('cert_reqs', 'CERT_'), - ('ssl_version', 'PROTOCOL_')): - val = opts['ssl'].get(key) + + for key, prefix in (("cert_reqs", "CERT_"), ("ssl_version", "PROTOCOL_")): + val = opts["ssl"].get(key) if val is None: continue - if not isinstance(val, six.string_types) or not val.startswith(prefix) or not hasattr(ssl, val): - message = 'SSL option \'{0}\' must be set to one of the following values: \'{1}\'.' \ - .format(key, '\', \''.join([val for val in dir(ssl) if val.startswith(prefix)])) + if ( + not isinstance(val, six.string_types) + or not val.startswith(prefix) + or not hasattr(ssl, val) + ): + message = "SSL option '{0}' must be set to one of the following values: '{1}'.".format( + key, "', '".join([val for val in dir(ssl) if val.startswith(prefix)]) + ) log.error(message) raise salt.exceptions.SaltConfigurationError(message) - opts['ssl'][key] = getattr(ssl, val) + opts["ssl"][key] = getattr(ssl, val) def _adjust_log_file_override(overrides, default_log_file): - ''' + """ Adjusts the log_file based on the log_dir override - ''' - if overrides.get('log_dir'): + """ + if overrides.get("log_dir"): # Adjust log_file if a log_dir override is introduced - if overrides.get('log_file'): - if not os.path.isabs(overrides['log_file']): + if overrides.get("log_file"): + if not os.path.isabs(overrides["log_file"]): # Prepend log_dir if log_file is relative - overrides['log_file'] = os.path.join(overrides['log_dir'], - overrides['log_file']) + overrides["log_file"] = os.path.join( + overrides["log_dir"], overrides["log_file"] + ) else: # Create the log_file override - overrides['log_file'] = \ - os.path.join(overrides['log_dir'], - os.path.basename(default_log_file)) + overrides["log_file"] = os.path.join( + overrides["log_dir"], os.path.basename(default_log_file) + ) -def apply_minion_config(overrides=None, - defaults=None, - cache_minion_id=False, - minion_id=None): - ''' +def apply_minion_config( + overrides=None, defaults=None, cache_minion_id=False, minion_id=None +): + """ Returns minion configurations dict. - ''' + """ if defaults is None: defaults = DEFAULT_MINION_OPTS.copy() if overrides is None: overrides = {} opts = defaults.copy() - opts['__role'] = 'minion' - _adjust_log_file_override(overrides, defaults['log_file']) + opts["__role"] = "minion" + _adjust_log_file_override(overrides, defaults["log_file"]) if overrides: opts.update(overrides) - if 'environment' in opts: - if opts['saltenv'] is not None: + if "environment" in opts: + if opts["saltenv"] is not None: log.warning( - 'The \'saltenv\' and \'environment\' minion config options ' - 'cannot both be used. Ignoring \'environment\' in favor of ' - '\'saltenv\'.', + "The 'saltenv' and 'environment' minion config options " + "cannot both be used. Ignoring 'environment' in favor of " + "'saltenv'.", ) # Set environment to saltenv in case someone's custom module is # refrencing __opts__['environment'] - opts['environment'] = opts['saltenv'] + opts["environment"] = opts["saltenv"] else: log.warning( - 'The \'environment\' minion config option has been renamed ' - 'to \'saltenv\'. Using %s as the \'saltenv\' config value.', - opts['environment'] + "The 'environment' minion config option has been renamed " + "to 'saltenv'. Using %s as the 'saltenv' config value.", + opts["environment"], ) - opts['saltenv'] = opts['environment'] + opts["saltenv"] = opts["environment"] - for idx, val in enumerate(opts['fileserver_backend']): - if val in ('git', 'hg', 'svn', 'minion'): - new_val = val + 'fs' + for idx, val in enumerate(opts["fileserver_backend"]): + if val in ("git", "hg", "svn", "minion"): + new_val = val + "fs" log.debug( - 'Changed %s to %s in minion opts\' fileserver_backend list', - val, new_val + "Changed %s to %s in minion opts' fileserver_backend list", val, new_val ) - opts['fileserver_backend'][idx] = new_val + opts["fileserver_backend"][idx] = new_val - opts['__cli'] = salt.utils.stringutils.to_unicode( - os.path.basename(sys.argv[0]) - ) + opts["__cli"] = salt.utils.stringutils.to_unicode(os.path.basename(sys.argv[0])) # No ID provided. Will getfqdn save us? using_ip_for_id = False - if not opts.get('id'): + if not opts.get("id"): if minion_id: - opts['id'] = minion_id + opts["id"] = minion_id else: - opts['id'], using_ip_for_id = get_id( - opts, - cache_minion_id=cache_minion_id) + opts["id"], using_ip_for_id = get_id(opts, cache_minion_id=cache_minion_id) # it does not make sense to append a domain to an IP based id - if not using_ip_for_id and 'append_domain' in opts: - opts['id'] = _append_domain(opts) + if not using_ip_for_id and "append_domain" in opts: + opts["id"] = _append_domain(opts) - for directory in opts.get('append_minionid_config_dirs', []): - if directory in ('pki_dir', 'cachedir', 'extension_modules'): - newdirectory = os.path.join(opts[directory], opts['id']) + for directory in opts.get("append_minionid_config_dirs", []): + if directory in ("pki_dir", "cachedir", "extension_modules"): + newdirectory = os.path.join(opts[directory], opts["id"]) opts[directory] = newdirectory - elif directory == 'default_include' and directory in opts: + elif directory == "default_include" and directory in opts: include_dir = os.path.dirname(opts[directory]) - new_include_dir = os.path.join(include_dir, - opts['id'], - os.path.basename(opts[directory])) + new_include_dir = os.path.join( + include_dir, opts["id"], os.path.basename(opts[directory]) + ) opts[directory] = new_include_dir # pidfile can be in the list of append_minionid_config_dirs, but pidfile # is the actual path with the filename, not a directory. - if 'pidfile' in opts.get('append_minionid_config_dirs', []): - newpath_list = os.path.split(opts['pidfile']) - opts['pidfile'] = os.path.join(newpath_list[0], 'salt', opts['id'], newpath_list[1]) + if "pidfile" in opts.get("append_minionid_config_dirs", []): + newpath_list = os.path.split(opts["pidfile"]) + opts["pidfile"] = os.path.join( + newpath_list[0], "salt", opts["id"], newpath_list[1] + ) - if len(opts['sock_dir']) > len(opts['cachedir']) + 10: - opts['sock_dir'] = os.path.join(opts['cachedir'], '.salt-unix') + if len(opts["sock_dir"]) > len(opts["cachedir"]) + 10: + opts["sock_dir"] = os.path.join(opts["cachedir"], ".salt-unix") # Enabling open mode requires that the value be set to True, and # nothing else! - opts['open_mode'] = opts['open_mode'] is True - opts['file_roots'] = _validate_file_roots(opts['file_roots']) - opts['pillar_roots'] = _validate_pillar_roots(opts['pillar_roots']) + opts["open_mode"] = opts["open_mode"] is True + opts["file_roots"] = _validate_file_roots(opts["file_roots"]) + opts["pillar_roots"] = _validate_pillar_roots(opts["pillar_roots"]) # Make sure ext_mods gets set if it is an untrue value # (here to catch older bad configs) - opts['extension_modules'] = ( - opts.get('extension_modules') or - os.path.join(opts['cachedir'], 'extmods') + opts["extension_modules"] = opts.get("extension_modules") or os.path.join( + opts["cachedir"], "extmods" ) # Set up the utils_dirs location from the extension_modules location - opts['utils_dirs'] = ( - opts.get('utils_dirs') or - [os.path.join(opts['extension_modules'], 'utils')] - ) + opts["utils_dirs"] = opts.get("utils_dirs") or [ + os.path.join(opts["extension_modules"], "utils") + ] # Insert all 'utils_dirs' directories to the system path - insert_system_path(opts, opts['utils_dirs']) + insert_system_path(opts, opts["utils_dirs"]) # Prepend root_dir to other paths prepend_root_dirs = [ - 'pki_dir', 'cachedir', 'sock_dir', 'extension_modules', 'pidfile', + "pki_dir", + "cachedir", + "sock_dir", + "extension_modules", + "pidfile", ] # These can be set to syslog, so, not actual paths on the system - for config_key in ('log_file', 'key_logfile'): - if urlparse(opts.get(config_key, '')).scheme == '': + for config_key in ("log_file", "key_logfile"): + if urlparse(opts.get(config_key, "")).scheme == "": prepend_root_dirs.append(config_key) prepend_root_dir(opts, prepend_root_dirs) # if there is no beacons option yet, add an empty beacons dict - if 'beacons' not in opts: - opts['beacons'] = {} + if "beacons" not in opts: + opts["beacons"] = {} - if overrides.get('ipc_write_buffer', '') == 'dynamic': - opts['ipc_write_buffer'] = _DFLT_IPC_WBUFFER - if 'ipc_write_buffer' not in overrides: - opts['ipc_write_buffer'] = 0 + if overrides.get("ipc_write_buffer", "") == "dynamic": + opts["ipc_write_buffer"] = _DFLT_IPC_WBUFFER + if "ipc_write_buffer" not in overrides: + opts["ipc_write_buffer"] = 0 # Make sure hash_type is lowercase - opts['hash_type'] = opts['hash_type'].lower() + opts["hash_type"] = opts["hash_type"].lower() # Check and update TLS/SSL configuration _update_ssl_config(opts) @@ -3823,56 +3628,76 @@ def apply_minion_config(overrides=None, def _update_discovery_config(opts): - ''' + """ Update discovery config for all instances. :param opts: :return: - ''' - if opts.get('discovery') not in (None, False): - if opts['discovery'] is True: - opts['discovery'] = {} - discovery_config = {'attempts': 3, 'pause': 5, 'port': 4520, 'match': 'any', 'mapping': {}} - for key in opts['discovery']: + """ + if opts.get("discovery") not in (None, False): + if opts["discovery"] is True: + opts["discovery"] = {} + discovery_config = { + "attempts": 3, + "pause": 5, + "port": 4520, + "match": "any", + "mapping": {}, + } + for key in opts["discovery"]: if key not in discovery_config: - raise salt.exceptions.SaltConfigurationError('Unknown discovery option: {0}'.format(key)) - if opts.get('__role') != 'minion': - for key in ['attempts', 'pause', 'match']: + raise salt.exceptions.SaltConfigurationError( + "Unknown discovery option: {0}".format(key) + ) + if opts.get("__role") != "minion": + for key in ["attempts", "pause", "match"]: del discovery_config[key] - opts['discovery'] = salt.utils.dictupdate.update(discovery_config, opts['discovery'], True, True) + opts["discovery"] = salt.utils.dictupdate.update( + discovery_config, opts["discovery"], True, True + ) -def master_config(path, env_var='SALT_MASTER_CONFIG', defaults=None, exit_on_config_errors=False): - ''' +def master_config( + path, env_var="SALT_MASTER_CONFIG", defaults=None, exit_on_config_errors=False +): + """ Reads in the master configuration file and sets up default options This is useful for running the actual master daemon. For running Master-side client interfaces that need the master opts see :py:func:`salt.client.client_config`. - ''' + """ if defaults is None: defaults = DEFAULT_MASTER_OPTS.copy() if not os.environ.get(env_var, None): # No valid setting was given using the configuration variable. # Lets see is SALT_CONFIG_DIR is of any use - salt_config_dir = os.environ.get('SALT_CONFIG_DIR', None) + salt_config_dir = os.environ.get("SALT_CONFIG_DIR", None) if salt_config_dir: - env_config_file_path = os.path.join(salt_config_dir, 'master') + env_config_file_path = os.path.join(salt_config_dir, "master") if salt_config_dir and os.path.isfile(env_config_file_path): # We can get a configuration file using SALT_CONFIG_DIR, let's # update the environment with this information os.environ[env_var] = env_config_file_path - overrides = load_config(path, env_var, DEFAULT_MASTER_OPTS['conf_file']) - default_include = overrides.get('default_include', - defaults['default_include']) - include = overrides.get('include', []) + overrides = load_config(path, env_var, DEFAULT_MASTER_OPTS["conf_file"]) + default_include = overrides.get("default_include", defaults["default_include"]) + include = overrides.get("include", []) - overrides.update(include_config(default_include, path, verbose=False, - exit_on_config_errors=exit_on_config_errors)) - overrides.update(include_config(include, path, verbose=True, - exit_on_config_errors=exit_on_config_errors)) + overrides.update( + include_config( + default_include, + path, + verbose=False, + exit_on_config_errors=exit_on_config_errors, + ) + ) + overrides.update( + include_config( + include, path, verbose=True, exit_on_config_errors=exit_on_config_errors + ) + ) opts = apply_master_config(overrides, defaults) _validate_ssh_minion_opts(opts) _validate_opts(opts) @@ -3880,166 +3705,165 @@ def master_config(path, env_var='SALT_MASTER_CONFIG', defaults=None, exit_on_con # no nodegroups defined, opts['nodegroups'] will be None. Fix this by # reverting this value to the default, as if 'nodegroups:' was commented # out or not present. - if opts.get('nodegroups') is None: - opts['nodegroups'] = DEFAULT_MASTER_OPTS.get('nodegroups', {}) - if salt.utils.data.is_dictlist(opts['nodegroups']): - opts['nodegroups'] = salt.utils.data.repack_dictlist(opts['nodegroups']) + if opts.get("nodegroups") is None: + opts["nodegroups"] = DEFAULT_MASTER_OPTS.get("nodegroups", {}) + if salt.utils.data.is_dictlist(opts["nodegroups"]): + opts["nodegroups"] = salt.utils.data.repack_dictlist(opts["nodegroups"]) apply_sdb(opts) return opts def apply_master_config(overrides=None, defaults=None): - ''' + """ Returns master configurations dict. - ''' + """ if defaults is None: defaults = DEFAULT_MASTER_OPTS.copy() if overrides is None: overrides = {} opts = defaults.copy() - opts['__role'] = 'master' - _adjust_log_file_override(overrides, defaults['log_file']) + opts["__role"] = "master" + _adjust_log_file_override(overrides, defaults["log_file"]) if overrides: opts.update(overrides) - opts['__cli'] = salt.utils.stringutils.to_unicode( - os.path.basename(sys.argv[0]) - ) + opts["__cli"] = salt.utils.stringutils.to_unicode(os.path.basename(sys.argv[0])) - if 'environment' in opts: - if opts['saltenv'] is not None: + if "environment" in opts: + if opts["saltenv"] is not None: log.warning( - 'The \'saltenv\' and \'environment\' master config options ' - 'cannot both be used. Ignoring \'environment\' in favor of ' - '\'saltenv\'.', + "The 'saltenv' and 'environment' master config options " + "cannot both be used. Ignoring 'environment' in favor of " + "'saltenv'.", ) # Set environment to saltenv in case someone's custom runner is # refrencing __opts__['environment'] - opts['environment'] = opts['saltenv'] + opts["environment"] = opts["saltenv"] else: log.warning( - 'The \'environment\' master config option has been renamed ' - 'to \'saltenv\'. Using %s as the \'saltenv\' config value.', - opts['environment'] + "The 'environment' master config option has been renamed " + "to 'saltenv'. Using %s as the 'saltenv' config value.", + opts["environment"], ) - opts['saltenv'] = opts['environment'] + opts["saltenv"] = opts["environment"] - if six.PY2 and 'rest_cherrypy' in opts: + if six.PY2 and "rest_cherrypy" in opts: # CherryPy is not unicode-compatible - opts['rest_cherrypy'] = salt.utils.data.encode(opts['rest_cherrypy']) + opts["rest_cherrypy"] = salt.utils.data.encode(opts["rest_cherrypy"]) - for idx, val in enumerate(opts['fileserver_backend']): - if val in ('git', 'hg', 'svn', 'minion'): - new_val = val + 'fs' + for idx, val in enumerate(opts["fileserver_backend"]): + if val in ("git", "hg", "svn", "minion"): + new_val = val + "fs" log.debug( - 'Changed %s to %s in master opts\' fileserver_backend list', - val, new_val + "Changed %s to %s in master opts' fileserver_backend list", val, new_val ) - opts['fileserver_backend'][idx] = new_val + opts["fileserver_backend"][idx] = new_val - if len(opts['sock_dir']) > len(opts['cachedir']) + 10: - opts['sock_dir'] = os.path.join(opts['cachedir'], '.salt-unix') + if len(opts["sock_dir"]) > len(opts["cachedir"]) + 10: + opts["sock_dir"] = os.path.join(opts["cachedir"], ".salt-unix") - opts['token_dir'] = os.path.join(opts['cachedir'], 'tokens') - opts['syndic_dir'] = os.path.join(opts['cachedir'], 'syndics') + opts["token_dir"] = os.path.join(opts["cachedir"], "tokens") + opts["syndic_dir"] = os.path.join(opts["cachedir"], "syndics") # Make sure ext_mods gets set if it is an untrue value # (here to catch older bad configs) - opts['extension_modules'] = ( - opts.get('extension_modules') or - os.path.join(opts['cachedir'], 'extmods') + opts["extension_modules"] = opts.get("extension_modules") or os.path.join( + opts["cachedir"], "extmods" ) # Set up the utils_dirs location from the extension_modules location - opts['utils_dirs'] = ( - opts.get('utils_dirs') or - [os.path.join(opts['extension_modules'], 'utils')] - ) + opts["utils_dirs"] = opts.get("utils_dirs") or [ + os.path.join(opts["extension_modules"], "utils") + ] # Insert all 'utils_dirs' directories to the system path - insert_system_path(opts, opts['utils_dirs']) + insert_system_path(opts, opts["utils_dirs"]) - if overrides.get('ipc_write_buffer', '') == 'dynamic': - opts['ipc_write_buffer'] = _DFLT_IPC_WBUFFER - if 'ipc_write_buffer' not in overrides: - opts['ipc_write_buffer'] = 0 + if overrides.get("ipc_write_buffer", "") == "dynamic": + opts["ipc_write_buffer"] = _DFLT_IPC_WBUFFER + if "ipc_write_buffer" not in overrides: + opts["ipc_write_buffer"] = 0 using_ip_for_id = False append_master = False - if not opts.get('id'): - opts['id'], using_ip_for_id = get_id( - opts, - cache_minion_id=None) + if not opts.get("id"): + opts["id"], using_ip_for_id = get_id(opts, cache_minion_id=None) append_master = True # it does not make sense to append a domain to an IP based id - if not using_ip_for_id and 'append_domain' in opts: - opts['id'] = _append_domain(opts) + if not using_ip_for_id and "append_domain" in opts: + opts["id"] = _append_domain(opts) if append_master: - opts['id'] += '_master' + opts["id"] += "_master" # Prepend root_dir to other paths prepend_root_dirs = [ - 'pki_dir', 'cachedir', 'pidfile', 'sock_dir', 'extension_modules', - 'autosign_file', 'autoreject_file', 'token_dir', 'syndic_dir', - 'sqlite_queue_dir', 'autosign_grains_dir' + "pki_dir", + "cachedir", + "pidfile", + "sock_dir", + "extension_modules", + "autosign_file", + "autoreject_file", + "token_dir", + "syndic_dir", + "sqlite_queue_dir", + "autosign_grains_dir", ] # These can be set to syslog, so, not actual paths on the system - for config_key in ('log_file', 'key_logfile', 'ssh_log_file'): - log_setting = opts.get(config_key, '') + for config_key in ("log_file", "key_logfile", "ssh_log_file"): + log_setting = opts.get(config_key, "") if log_setting is None: continue - if urlparse(log_setting).scheme == '': + if urlparse(log_setting).scheme == "": prepend_root_dirs.append(config_key) prepend_root_dir(opts, prepend_root_dirs) # Enabling open mode requires that the value be set to True, and # nothing else! - opts['open_mode'] = opts['open_mode'] is True - opts['auto_accept'] = opts['auto_accept'] is True - opts['file_roots'] = _validate_file_roots(opts['file_roots']) - opts['pillar_roots'] = _validate_file_roots(opts['pillar_roots']) + opts["open_mode"] = opts["open_mode"] is True + opts["auto_accept"] = opts["auto_accept"] is True + opts["file_roots"] = _validate_file_roots(opts["file_roots"]) + opts["pillar_roots"] = _validate_file_roots(opts["pillar_roots"]) - if opts['file_ignore_regex']: + if opts["file_ignore_regex"]: # If file_ignore_regex was given, make sure it's wrapped in a list. # Only keep valid regex entries for improved performance later on. - if isinstance(opts['file_ignore_regex'], six.string_types): - ignore_regex = [opts['file_ignore_regex']] - elif isinstance(opts['file_ignore_regex'], list): - ignore_regex = opts['file_ignore_regex'] + if isinstance(opts["file_ignore_regex"], six.string_types): + ignore_regex = [opts["file_ignore_regex"]] + elif isinstance(opts["file_ignore_regex"], list): + ignore_regex = opts["file_ignore_regex"] - opts['file_ignore_regex'] = [] + opts["file_ignore_regex"] = [] for regex in ignore_regex: try: # Can't store compiled regex itself in opts (breaks # serialization) re.compile(regex) - opts['file_ignore_regex'].append(regex) + opts["file_ignore_regex"].append(regex) except Exception: # pylint: disable=broad-except - log.warning( - 'Unable to parse file_ignore_regex. Skipping: %s', - regex - ) + log.warning("Unable to parse file_ignore_regex. Skipping: %s", regex) - if opts['file_ignore_glob']: + if opts["file_ignore_glob"]: # If file_ignore_glob was given, make sure it's wrapped in a list. - if isinstance(opts['file_ignore_glob'], six.string_types): - opts['file_ignore_glob'] = [opts['file_ignore_glob']] + if isinstance(opts["file_ignore_glob"], six.string_types): + opts["file_ignore_glob"] = [opts["file_ignore_glob"]] # Let's make sure `worker_threads` does not drop below 3 which has proven # to make `salt.modules.publish` not work under the test-suite. - if opts['worker_threads'] < 3 and opts.get('peer', None): + if opts["worker_threads"] < 3 and opts.get("peer", None): log.warning( "The 'worker_threads' setting in '%s' cannot be lower than " - '3. Resetting it to the default value of 3.', opts['conf_file'] + "3. Resetting it to the default value of 3.", + opts["conf_file"], ) - opts['worker_threads'] = 3 + opts["worker_threads"] = 3 - opts.setdefault('pillar_source_merging_strategy', 'smart') + opts.setdefault("pillar_source_merging_strategy", "smart") # Make sure hash_type is lowercase - opts['hash_type'] = opts['hash_type'].lower() + opts["hash_type"] = opts["hash_type"].lower() # Check and update TLS/SSL configuration _update_ssl_config(opts) @@ -4048,8 +3872,8 @@ def apply_master_config(overrides=None, defaults=None): return opts -def client_config(path, env_var='SALT_CLIENT_CONFIG', defaults=None): - ''' +def client_config(path, env_var="SALT_CLIENT_CONFIG", defaults=None): + """ Load Master configuration data Usage: @@ -4066,63 +3890,51 @@ def client_config(path, env_var='SALT_CLIENT_CONFIG', defaults=None): This is useful for master-side operations like :py:class:`~salt.client.LocalClient`. - ''' + """ if defaults is None: defaults = DEFAULT_MASTER_OPTS.copy() xdg_dir = salt.utils.xdg.xdg_config_dir() if os.path.isdir(xdg_dir): client_config_dir = xdg_dir - saltrc_config_file = 'saltrc' + saltrc_config_file = "saltrc" else: - client_config_dir = os.path.expanduser('~') - saltrc_config_file = '.saltrc' + client_config_dir = os.path.expanduser("~") + saltrc_config_file = ".saltrc" # Get the token file path from the provided defaults. If not found, specify # our own, sane, default opts = { - 'token_file': defaults.get( - 'token_file', - os.path.join(client_config_dir, 'salt_token') + "token_file": defaults.get( + "token_file", os.path.join(client_config_dir, "salt_token") ) } # Update options with the master configuration, either from the provided # path, salt's defaults or provided defaults - opts.update( - master_config(path, defaults=defaults) - ) + opts.update(master_config(path, defaults=defaults)) # Update with the users salt dot file or with the environment variable saltrc_config = os.path.join(client_config_dir, saltrc_config_file) - opts.update( - load_config( - saltrc_config, - env_var, - saltrc_config - ) - ) + opts.update(load_config(saltrc_config, env_var, saltrc_config)) # Make sure we have a proper and absolute path to the token file - if 'token_file' in opts: - opts['token_file'] = os.path.abspath( - os.path.expanduser( - opts['token_file'] - ) - ) + if "token_file" in opts: + opts["token_file"] = os.path.abspath(os.path.expanduser(opts["token_file"])) # If the token file exists, read and store the contained token - if os.path.isfile(opts['token_file']): + if os.path.isfile(opts["token_file"]): # Make sure token is still valid - expire = opts.get('token_expire', 43200) - if os.stat(opts['token_file']).st_mtime + expire > time.mktime(time.localtime()): - with salt.utils.files.fopen(opts['token_file']) as fp_: - opts['token'] = fp_.read().strip() + expire = opts.get("token_expire", 43200) + if os.stat(opts["token_file"]).st_mtime + expire > time.mktime( + time.localtime() + ): + with salt.utils.files.fopen(opts["token_file"]) as fp_: + opts["token"] = fp_.read().strip() # On some platforms, like OpenBSD, 0.0.0.0 won't catch a master running on localhost - if opts['interface'] == '0.0.0.0': - opts['interface'] = '127.0.0.1' + if opts["interface"] == "0.0.0.0": + opts["interface"] = "127.0.0.1" # Make sure the master_uri is set - if 'master_uri' not in opts: - opts['master_uri'] = 'tcp://{ip}:{port}'.format( - ip=salt.utils.zeromq.ip_bracket(opts['interface']), - port=opts['ret_port'] + if "master_uri" not in opts: + opts["master_uri"] = "tcp://{ip}:{port}".format( + ip=salt.utils.zeromq.ip_bracket(opts["interface"]), port=opts["ret_port"] ) # Return the client options @@ -4131,10 +3943,10 @@ def client_config(path, env_var='SALT_CLIENT_CONFIG', defaults=None): def api_config(path): - ''' + """ Read in the Salt Master config file and add additional configs that need to be stubbed out for salt-api - ''' + """ # Let's grab a copy of salt-api's required defaults opts = DEFAULT_API_OPTS.copy() @@ -4142,68 +3954,69 @@ def api_config(path): opts.update(client_config(path, defaults=DEFAULT_MASTER_OPTS.copy())) # Let's set the pidfile and log_file values in opts to api settings - opts.update({ - 'pidfile': opts.get('api_pidfile', DEFAULT_API_OPTS['api_pidfile']), - 'log_file': opts.get('api_logfile', DEFAULT_API_OPTS['api_logfile']), - }) + opts.update( + { + "pidfile": opts.get("api_pidfile", DEFAULT_API_OPTS["api_pidfile"]), + "log_file": opts.get("api_logfile", DEFAULT_API_OPTS["api_logfile"]), + } + ) - prepend_root_dir(opts, [ - 'api_pidfile', - 'api_logfile', - 'log_file', - 'pidfile' - ]) + prepend_root_dir(opts, ["api_pidfile", "api_logfile", "log_file", "pidfile"]) return opts def spm_config(path): - ''' + """ Read in the salt master config file and add additional configs that need to be stubbed out for spm .. versionadded:: 2015.8.0 - ''' + """ # Let's grab a copy of salt's master default opts defaults = DEFAULT_MASTER_OPTS.copy() # Let's override them with spm's required defaults defaults.update(DEFAULT_SPM_OPTS) - overrides = load_config(path, 'SPM_CONFIG', DEFAULT_SPM_OPTS['spm_conf_file']) - default_include = overrides.get('spm_default_include', - defaults['spm_default_include']) - include = overrides.get('include', []) + overrides = load_config(path, "SPM_CONFIG", DEFAULT_SPM_OPTS["spm_conf_file"]) + default_include = overrides.get( + "spm_default_include", defaults["spm_default_include"] + ) + include = overrides.get("include", []) overrides.update(include_config(default_include, path, verbose=False)) overrides.update(include_config(include, path, verbose=True)) defaults = apply_master_config(overrides, defaults) defaults = apply_spm_config(overrides, defaults) - return client_config(path, env_var='SPM_CONFIG', defaults=defaults) + return client_config(path, env_var="SPM_CONFIG", defaults=defaults) def apply_spm_config(overrides, defaults): - ''' + """ Returns the spm configurations dict. .. versionadded:: 2015.8.1 - ''' + """ opts = defaults.copy() - _adjust_log_file_override(overrides, defaults['log_file']) + _adjust_log_file_override(overrides, defaults["log_file"]) if overrides: opts.update(overrides) # Prepend root_dir to other paths prepend_root_dirs = [ - 'formula_path', 'pillar_path', 'reactor_path', - 'spm_cache_dir', 'spm_build_dir' + "formula_path", + "pillar_path", + "reactor_path", + "spm_cache_dir", + "spm_build_dir", ] # These can be set to syslog, so, not actual paths on the system - for config_key in ('spm_logfile',): - log_setting = opts.get(config_key, '') + for config_key in ("spm_logfile",): + log_setting = opts.get(config_key, "") if log_setting is None: continue - if urlparse(log_setting).scheme == '': + if urlparse(log_setting).scheme == "": prepend_root_dirs.append(config_key) prepend_root_dir(opts, prepend_root_dirs) diff --git a/salt/config/schemas/__init__.py b/salt/config/schemas/__init__.py index f2091c23eb4..daa2d8c8918 100644 --- a/salt/config/schemas/__init__.py +++ b/salt/config/schemas/__init__.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) salt.config.schemas ~~~~~~~~~~~~~~~~~~~ Salt configuration related schemas for future validation -''' +""" diff --git a/salt/config/schemas/common.py b/salt/config/schemas/common.py index 62c4f3e0ede..d375b70a108 100644 --- a/salt/config/schemas/common.py +++ b/salt/config/schemas/common.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,38 +7,39 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~ Common salt configuration schemas -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import salt libs -from salt.utils.schema import (Schema, - StringItem, - ArrayItem, - OneOfItem) +from salt.utils.schema import ArrayItem, OneOfItem, Schema, StringItem class DefaultIncludeConfig(StringItem): - ''' + """ Per default, the {0}, will automatically include all config files from '{1}/*.conf' ('{1}' is a sub-directory in the same directory as the main {0} config file). - ''' + """ + __target__ = None __confd_directory__ = None - title = 'Include Config' + title = "Include Config" description = __doc__ def __init__(self, default=None, pattern=None, **kwargs): - default = '{0}/*.conf'.format(self.__confd_directory__) - pattern = r'(?:.*)/\*\.conf' - super(DefaultIncludeConfig, self).__init__(default=default, pattern=pattern, **kwargs) + default = "{0}/*.conf".format(self.__confd_directory__) + pattern = r"(?:.*)/\*\.conf" + super(DefaultIncludeConfig, self).__init__( + default=default, pattern=pattern, **kwargs + ) def __validate_attributes__(self): - self.__doc__ = DefaultIncludeConfig.__doc__.format(self.__target__, - self.__confd_directory__) + self.__doc__ = DefaultIncludeConfig.__doc__.format( + self.__target__, self.__confd_directory__ + ) super(DefaultIncludeConfig, self).__validate_attributes__() def __get_description__(self): @@ -46,18 +47,19 @@ class DefaultIncludeConfig(StringItem): class MinionDefaultInclude(DefaultIncludeConfig): - __target__ = 'minion' - __confd_directory__ = 'minion.d' + __target__ = "minion" + __confd_directory__ = "minion.d" class MasterDefaultInclude(DefaultIncludeConfig): - __target__ = 'master' - __confd_directory = 'master.d' + __target__ = "master" + __confd_directory = "master.d" class IncludeConfig(Schema): - title = 'Include Configuration File(s)' - description = 'Include one or more specific configuration files' + title = "Include Configuration File(s)" + description = "Include one or more specific configuration files" - string_or_array = OneOfItem(items=(StringItem(), - ArrayItem(items=StringItem())))(flatten=True) + string_or_array = OneOfItem(items=(StringItem(), ArrayItem(items=StringItem())))( + flatten=True + ) diff --git a/salt/config/schemas/esxcluster.py b/salt/config/schemas/esxcluster.py index d585667d0b3..6b67bbd5aa9 100644 --- a/salt/config/schemas/esxcluster.py +++ b/salt/config/schemas/esxcluster.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu (alexandru.bleotu@morganstanley.com)` @@ -7,207 +7,219 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ESX Cluster configuration schemas -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs -from salt.utils.schema import (Schema, - DefinitionsSchema, - ComplexSchemaItem, - DictItem, - ArrayItem, - IntegerItem, - BooleanItem, - StringItem, - AnyOfItem) +from salt.utils.schema import ( + AnyOfItem, + ArrayItem, + BooleanItem, + ComplexSchemaItem, + DefinitionsSchema, + DictItem, + IntegerItem, + Schema, + StringItem, +) class OptionValueItem(ComplexSchemaItem): - '''Sechma item of the OptionValue''' + """Sechma item of the OptionValue""" - title = 'OptionValue' - key = StringItem(title='Key', required=True) + title = "OptionValue" + key = StringItem(title="Key", required=True) value = AnyOfItem(items=[StringItem(), BooleanItem(), IntegerItem()]) class AdmissionControlPolicyItem(ComplexSchemaItem): - ''' + """ Schema item of the HA admission control policy - ''' + """ - title = 'Admission Control Policy' + title = "Admission Control Policy" cpu_failover_percent = IntegerItem( - title='CPU Failover Percent', - minimum=0, maximum=100) + title="CPU Failover Percent", minimum=0, maximum=100 + ) memory_failover_percent = IntegerItem( - title='Memory Failover Percent', - minimum=0, maximum=100) + title="Memory Failover Percent", minimum=0, maximum=100 + ) class DefaultVmSettingsItem(ComplexSchemaItem): - ''' + """ Schema item of the HA default vm settings - ''' + """ - title = 'Default VM Settings' + title = "Default VM Settings" isolation_response = StringItem( - title='Isolation Response', - enum=['clusterIsolationResponse', 'none', 'powerOff', 'shutdown']) + title="Isolation Response", + enum=["clusterIsolationResponse", "none", "powerOff", "shutdown"], + ) restart_priority = StringItem( - title='Restart Priority', - enum=['clusterRestartPriority', 'disabled', 'high', 'low', 'medium']) + title="Restart Priority", + enum=["clusterRestartPriority", "disabled", "high", "low", "medium"], + ) class HAConfigItem(ComplexSchemaItem): - ''' + """ Schema item of ESX cluster high availability - ''' + """ - title = 'HA Configuration' - description = 'ESX cluster HA configuration json schema item' + title = "HA Configuration" + description = "ESX cluster HA configuration json schema item" enabled = BooleanItem( - title='Enabled', - description='Specifies if HA should be enabled') - admission_control_enabled = BooleanItem( - title='Admission Control Enabled') + title="Enabled", description="Specifies if HA should be enabled" + ) + admission_control_enabled = BooleanItem(title="Admission Control Enabled") admission_control_policy = AdmissionControlPolicyItem() default_vm_settings = DefaultVmSettingsItem() hb_ds_candidate_policy = StringItem( - title='Heartbeat Datastore Candidate Policy', - enum=['allFeasibleDs', 'allFeasibleDsWithUserPreference', - 'userSelectedDs']) - host_monitoring = StringItem(title='Host Monitoring', - choices=['enabled', 'disabled']) + title="Heartbeat Datastore Candidate Policy", + enum=["allFeasibleDs", "allFeasibleDsWithUserPreference", "userSelectedDs"], + ) + host_monitoring = StringItem( + title="Host Monitoring", choices=["enabled", "disabled"] + ) options = ArrayItem(min_items=1, items=OptionValueItem()) vm_monitoring = StringItem( - title='Vm Monitoring', - choices=['vmMonitoringDisabled', 'vmAndAppMonitoring', - 'vmMonitoringOnly']) + title="Vm Monitoring", + choices=["vmMonitoringDisabled", "vmAndAppMonitoring", "vmMonitoringOnly"], + ) class vSANClusterConfigItem(ComplexSchemaItem): - ''' + """ Schema item of the ESX cluster vSAN configuration - ''' + """ - title = 'vSAN Configuration' - description = 'ESX cluster vSAN configurationi item' + title = "vSAN Configuration" + description = "ESX cluster vSAN configurationi item" enabled = BooleanItem( - title='Enabled', - description='Specifies if vSAN should be enabled') + title="Enabled", description="Specifies if vSAN should be enabled" + ) auto_claim_storage = BooleanItem( - title='Auto Claim Storage', - description='Specifies whether the storage of member ESXi hosts should ' - 'be automatically claimed for vSAN') + title="Auto Claim Storage", + description="Specifies whether the storage of member ESXi hosts should " + "be automatically claimed for vSAN", + ) dedup_enabled = BooleanItem( - title='Enabled', - description='Specifies dedup should be enabled') + title="Enabled", description="Specifies dedup should be enabled" + ) compression_enabled = BooleanItem( - title='Enabled', - description='Specifies if compression should be enabled') + title="Enabled", description="Specifies if compression should be enabled" + ) class DRSConfigItem(ComplexSchemaItem): - ''' + """ Schema item of the ESX cluster DRS configuration - ''' + """ - title = 'DRS Configuration' - description = 'ESX cluster DRS configuration item' + title = "DRS Configuration" + description = "ESX cluster DRS configuration item" enabled = BooleanItem( - title='Enabled', - description='Specifies if DRS should be enabled') + title="Enabled", description="Specifies if DRS should be enabled" + ) vmotion_rate = IntegerItem( - title='vMotion rate', - description='Aggressiveness to do automatic vMotions: ' - '1 (least aggressive) - 5 (most aggressive)', + title="vMotion rate", + description="Aggressiveness to do automatic vMotions: " + "1 (least aggressive) - 5 (most aggressive)", minimum=1, - maximum=5) + maximum=5, + ) default_vm_behavior = StringItem( - title='Default VM DRS Behavior', - description='Specifies the default VM DRS behavior', - enum=['fullyAutomated', 'partiallyAutomated', 'manual']) + title="Default VM DRS Behavior", + description="Specifies the default VM DRS behavior", + enum=["fullyAutomated", "partiallyAutomated", "manual"], + ) class ESXClusterConfigSchema(DefinitionsSchema): - ''' + """ Schema of the ESX cluster config - ''' + """ - title = 'ESX Cluster Configuration Schema' - description = 'ESX cluster configuration schema' + title = "ESX Cluster Configuration Schema" + description = "ESX cluster configuration schema" ha = HAConfigItem() vsan = vSANClusterConfigItem() drs = DRSConfigItem() - vm_swap_placement = StringItem(title='VM Swap Placement') + vm_swap_placement = StringItem(title="VM Swap Placement") class ESXClusterEntitySchema(Schema): - '''Schema of the ESX cluster entity''' + """Schema of the ESX cluster entity""" - title = 'ESX Cluster Entity Schema' - description = 'ESX cluster entity schema' + title = "ESX Cluster Entity Schema" + description = "ESX cluster entity schema" - type = StringItem(title='Type', - description='Specifies the entity type', - required=True, - enum=['cluster']) + type = StringItem( + title="Type", + description="Specifies the entity type", + required=True, + enum=["cluster"], + ) - datacenter = StringItem(title='Datacenter', - description='Specifies the cluster datacenter', - required=True, - pattern=r'\w+') + datacenter = StringItem( + title="Datacenter", + description="Specifies the cluster datacenter", + required=True, + pattern=r"\w+", + ) - cluster = StringItem(title='Cluster', - description='Specifies the cluster name', - required=True, - pattern=r'\w+') + cluster = StringItem( + title="Cluster", + description="Specifies the cluster name", + required=True, + pattern=r"\w+", + ) class LicenseSchema(Schema): - ''' + """ Schema item of the ESX cluster vSAN configuration - ''' + """ - title = 'Licenses schema' - description = 'License configuration schema' + title = "Licenses schema" + description = "License configuration schema" licenses = DictItem( - title='Licenses', - description='Dictionary containing the license name to key mapping', + title="Licenses", + description="Dictionary containing the license name to key mapping", required=True, additional_properties=StringItem( - title='License Key', - description='Specifies the license key', - pattern=r'^(\w{5}-\w{5}-\w{5}-\w{5}-\w{5})$')) + title="License Key", + description="Specifies the license key", + pattern=r"^(\w{5}-\w{5}-\w{5}-\w{5}-\w{5})$", + ), + ) class EsxclusterProxySchema(Schema): - ''' + """ Schema of the esxcluster proxy input - ''' + """ - title = 'Esxcluster Proxy Schema' - description = 'Esxcluster proxy schema' + title = "Esxcluster Proxy Schema" + description = "Esxcluster proxy schema" additional_properties = False - proxytype = StringItem(required=True, - enum=['esxcluster']) - vcenter = StringItem(required=True, pattern=r'[^\s]+') + proxytype = StringItem(required=True, enum=["esxcluster"]) + vcenter = StringItem(required=True, pattern=r"[^\s]+") datacenter = StringItem(required=True) cluster = StringItem(required=True) - mechanism = StringItem(required=True, enum=['userpass', 'sspi']) + mechanism = StringItem(required=True, enum=["userpass", "sspi"]) username = StringItem() - passwords = ArrayItem(min_items=1, - items=StringItem(), - unique_items=True) + passwords = ArrayItem(min_items=1, items=StringItem(), unique_items=True) # TODO Should be changed when anyOf is supported for schemas domain = StringItem() principal = StringItem() diff --git a/salt/config/schemas/esxdatacenter.py b/salt/config/schemas/esxdatacenter.py index d31d713d95c..98a0b0ec420 100644 --- a/salt/config/schemas/esxdatacenter.py +++ b/salt/config/schemas/esxdatacenter.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu (alexandru.bleotu@morganstanley.com)` @@ -7,35 +7,29 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ESX Datacenter configuration schemas -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs -from salt.utils.schema import (Schema, - ArrayItem, - IntegerItem, - StringItem) +from salt.utils.schema import ArrayItem, IntegerItem, Schema, StringItem class EsxdatacenterProxySchema(Schema): - ''' + """ Schema of the esxdatacenter proxy input - ''' + """ - title = 'Esxdatacenter Proxy Schema' - description = 'Esxdatacenter proxy schema' + title = "Esxdatacenter Proxy Schema" + description = "Esxdatacenter proxy schema" additional_properties = False - proxytype = StringItem(required=True, - enum=['esxdatacenter']) - vcenter = StringItem(required=True, pattern=r'[^\s]+') + proxytype = StringItem(required=True, enum=["esxdatacenter"]) + vcenter = StringItem(required=True, pattern=r"[^\s]+") datacenter = StringItem(required=True) - mechanism = StringItem(required=True, enum=['userpass', 'sspi']) + mechanism = StringItem(required=True, enum=["userpass", "sspi"]) username = StringItem() - passwords = ArrayItem(min_items=1, - items=StringItem(), - unique_items=True) + passwords = ArrayItem(min_items=1, items=StringItem(), unique_items=True) # TODO Should be changed when anyOf is supported for schemas domain = StringItem() principal = StringItem() diff --git a/salt/config/schemas/esxi.py b/salt/config/schemas/esxi.py index b8bbefa7669..ea6a5335722 100644 --- a/salt/config/schemas/esxi.py +++ b/salt/config/schemas/esxi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu (alexandru.bleotu@morganstanley.com)` @@ -7,211 +7,209 @@ ~~~~~~~~~~~~~~~~~~~~~~~~ ESXi host configuration schemas -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs -from salt.utils.schema import (DefinitionsSchema, - Schema, - ComplexSchemaItem, - ArrayItem, - IntegerItem, - BooleanItem, - StringItem, - OneOfItem) +from salt.utils.schema import ( + ArrayItem, + BooleanItem, + ComplexSchemaItem, + DefinitionsSchema, + IntegerItem, + OneOfItem, + Schema, + StringItem, +) class VMwareScsiAddressItem(StringItem): - pattern = r'vmhba\d+:C\d+:T\d+:L\d+' + pattern = r"vmhba\d+:C\d+:T\d+:L\d+" class DiskGroupDiskScsiAddressItem(ComplexSchemaItem): - ''' + """ Schema item of a ESXi host disk group containing disk SCSI addresses - ''' + """ - title = 'Diskgroup Disk Scsi Address Item' - description = 'ESXi host diskgroup item containing disk SCSI addresses' + title = "Diskgroup Disk Scsi Address Item" + description = "ESXi host diskgroup item containing disk SCSI addresses" cache_scsi_addr = VMwareScsiAddressItem( - title='Cache Disk Scsi Address', - description='Specifies the SCSI address of the cache disk', - required=True) + title="Cache Disk Scsi Address", + description="Specifies the SCSI address of the cache disk", + required=True, + ) capacity_scsi_addrs = ArrayItem( - title='Capacity Scsi Addresses', - description='Array with the SCSI addresses of the capacity disks', + title="Capacity Scsi Addresses", + description="Array with the SCSI addresses of the capacity disks", items=VMwareScsiAddressItem(), - min_items=1) + min_items=1, + ) class DiskGroupDiskIdItem(ComplexSchemaItem): - ''' + """ Schema item of a ESXi host disk group containg disk ids - ''' + """ - title = 'Diskgroup Disk Id Item' - description = 'ESXi host diskgroup item containing disk ids' + title = "Diskgroup Disk Id Item" + description = "ESXi host diskgroup item containing disk ids" cache_id = StringItem( - title='Cache Disk Id', - description='Specifies the id of the cache disk', - pattern=r'[^\s]+') + title="Cache Disk Id", + description="Specifies the id of the cache disk", + pattern=r"[^\s]+", + ) capacity_ids = ArrayItem( - title='Capacity Disk Ids', - description='Array with the ids of the capacity disks', - items=StringItem(pattern=r'[^\s]+'), - min_items=1) + title="Capacity Disk Ids", + description="Array with the ids of the capacity disks", + items=StringItem(pattern=r"[^\s]+"), + min_items=1, + ) class DiskGroupsDiskScsiAddressSchema(DefinitionsSchema): - ''' + """ Schema of ESXi host diskgroups containing disk SCSI addresses - ''' + """ - title = 'Diskgroups Disk Scsi Address Schema' - description = 'ESXi host diskgroup schema containing disk SCSI addresses' + title = "Diskgroups Disk Scsi Address Schema" + description = "ESXi host diskgroup schema containing disk SCSI addresses" diskgroups = ArrayItem( - title='Diskgroups', - description='List of diskgroups in an ESXi host', + title="Diskgroups", + description="List of diskgroups in an ESXi host", min_items=1, items=DiskGroupDiskScsiAddressItem(), - required=True) - erase_disks = BooleanItem( - title='Erase Diskgroup Disks', - required=True) + required=True, + ) + erase_disks = BooleanItem(title="Erase Diskgroup Disks", required=True) class DiskGroupsDiskIdSchema(DefinitionsSchema): - ''' + """ Schema of ESXi host diskgroups containing disk ids - ''' + """ - title = 'Diskgroups Disk Id Schema' - description = 'ESXi host diskgroup schema containing disk ids' + title = "Diskgroups Disk Id Schema" + description = "ESXi host diskgroup schema containing disk ids" diskgroups = ArrayItem( - title='DiskGroups', - description='List of disk groups in an ESXi host', + title="DiskGroups", + description="List of disk groups in an ESXi host", min_items=1, items=DiskGroupDiskIdItem(), - required=True) + required=True, + ) class VmfsDatastoreDiskIdItem(ComplexSchemaItem): - ''' + """ Schema item of a VMFS datastore referencing a backing disk id - ''' + """ - title = 'VMFS Datastore Disk Id Item' - description = 'VMFS datastore item referencing a backing disk id' + title = "VMFS Datastore Disk Id Item" + description = "VMFS datastore item referencing a backing disk id" name = StringItem( - title='Name', - description='Specifies the name of the VMFS datastore', - required=True) + title="Name", + description="Specifies the name of the VMFS datastore", + required=True, + ) backing_disk_id = StringItem( - title='Backing Disk Id', - description=('Specifies the id of the disk backing the VMFS ' - 'datastore'), - pattern=r'[^\s]+', - required=True) + title="Backing Disk Id", + description=("Specifies the id of the disk backing the VMFS " "datastore"), + pattern=r"[^\s]+", + required=True, + ) vmfs_version = IntegerItem( - title='VMFS Version', - description='VMFS version', - enum=[1, 2, 3, 5]) + title="VMFS Version", description="VMFS version", enum=[1, 2, 3, 5] + ) class VmfsDatastoreDiskScsiAddressItem(ComplexSchemaItem): - ''' + """ Schema item of a VMFS datastore referencing a backing disk SCSI address - ''' + """ - title = 'VMFS Datastore Disk Scsi Address Item' - description = 'VMFS datastore item referencing a backing disk SCSI address' + title = "VMFS Datastore Disk Scsi Address Item" + description = "VMFS datastore item referencing a backing disk SCSI address" name = StringItem( - title='Name', - description='Specifies the name of the VMFS datastore', - required=True) + title="Name", + description="Specifies the name of the VMFS datastore", + required=True, + ) backing_disk_scsi_addr = VMwareScsiAddressItem( - title='Backing Disk Scsi Address', - description=('Specifies the SCSI address of the disk backing the VMFS ' - 'datastore'), - required=True) + title="Backing Disk Scsi Address", + description=( + "Specifies the SCSI address of the disk backing the VMFS " "datastore" + ), + required=True, + ) vmfs_version = IntegerItem( - title='VMFS Version', - description='VMFS version', - enum=[1, 2, 3, 5]) + title="VMFS Version", description="VMFS version", enum=[1, 2, 3, 5] + ) class VmfsDatastoreSchema(DefinitionsSchema): - ''' + """ Schema of a VMFS datastore - ''' + """ - title = 'VMFS Datastore Schema' - description = 'Schema of a VMFS datastore' + title = "VMFS Datastore Schema" + description = "Schema of a VMFS datastore" datastore = OneOfItem( - items=[VmfsDatastoreDiskScsiAddressItem(), - VmfsDatastoreDiskIdItem()], - required=True) + items=[VmfsDatastoreDiskScsiAddressItem(), VmfsDatastoreDiskIdItem()], + required=True, + ) class HostCacheSchema(DefinitionsSchema): - ''' + """ Schema of ESXi host cache - ''' + """ - title = 'Host Cache Schema' - description = 'Schema of the ESXi host cache' - enabled = BooleanItem( - title='Enabled', - required=True) + title = "Host Cache Schema" + description = "Schema of the ESXi host cache" + enabled = BooleanItem(title="Enabled", required=True) datastore = VmfsDatastoreDiskScsiAddressItem(required=True) swap_size = StringItem( - title='Host cache swap size (in GB or %)', - pattern=r'(\d+GiB)|(([0-9]|([1-9][0-9])|100)%)', - required=True) - erase_backing_disk = BooleanItem( - title='Erase Backup Disk', - required=True) + title="Host cache swap size (in GB or %)", + pattern=r"(\d+GiB)|(([0-9]|([1-9][0-9])|100)%)", + required=True, + ) + erase_backing_disk = BooleanItem(title="Erase Backup Disk", required=True) class SimpleHostCacheSchema(Schema): - ''' + """ Simplified Schema of ESXi host cache - ''' + """ - title = 'Simple Host Cache Schema' - description = 'Simplified schema of the ESXi host cache' - enabled = BooleanItem( - title='Enabled', - required=True) - datastore_name = StringItem(title='Datastore Name', - required=True) - swap_size_MiB = IntegerItem(title='Host cache swap size in MiB', - minimum=1) + title = "Simple Host Cache Schema" + description = "Simplified schema of the ESXi host cache" + enabled = BooleanItem(title="Enabled", required=True) + datastore_name = StringItem(title="Datastore Name", required=True) + swap_size_MiB = IntegerItem(title="Host cache swap size in MiB", minimum=1) class EsxiProxySchema(Schema): - ''' + """ Schema of the esxi proxy input - ''' + """ - title = 'Esxi Proxy Schema' - description = 'Esxi proxy schema' + title = "Esxi Proxy Schema" + description = "Esxi proxy schema" additional_properties = False - proxytype = StringItem(required=True, - enum=['esxi']) - host = StringItem(pattern=r'[^\s]+') # Used when connecting directly - vcenter = StringItem(pattern=r'[^\s]+') # Used when connecting via a vCenter + proxytype = StringItem(required=True, enum=["esxi"]) + host = StringItem(pattern=r"[^\s]+") # Used when connecting directly + vcenter = StringItem(pattern=r"[^\s]+") # Used when connecting via a vCenter esxi_host = StringItem() username = StringItem() - passwords = ArrayItem(min_items=1, - items=StringItem(), - unique_items=True) - mechanism = StringItem(enum=['userpass', 'sspi']) + passwords = ArrayItem(min_items=1, items=StringItem(), unique_items=True) + mechanism = StringItem(enum=["userpass", "sspi"]) # TODO Should be changed when anyOf is supported for schemas domain = StringItem() principal = StringItem() diff --git a/salt/config/schemas/esxvm.py b/salt/config/schemas/esxvm.py index ba37a8eea4a..76da16ca979 100644 --- a/salt/config/schemas/esxvm.py +++ b/salt/config/schemas/esxvm.py @@ -1,47 +1,51 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Agnes Tevesz (agnes.tevesz@morganstanley.com)` salt.config.schemas.esxvm ~~~~~~~~~~~~~~~~~~~~~~~~~~~ ESX Virtual Machine configuration schemas -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -from salt.utils.schema import (DefinitionsSchema, - ComplexSchemaItem, - ArrayItem, - IntegerItem, - NumberItem, - BooleanItem, - StringItem, - IPv4Item, - AnyOfItem, - NullItem) +from salt.utils.schema import ( + AnyOfItem, + ArrayItem, + BooleanItem, + ComplexSchemaItem, + DefinitionsSchema, + IntegerItem, + IPv4Item, + NullItem, + NumberItem, + StringItem, +) class ESXVirtualMachineSerialBackingItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine Serial Port Backing - ''' - title = 'ESX Virtual Machine Serial Port Backing' - description = 'ESX virtual machine serial port backing' + """ + + title = "ESX Virtual Machine Serial Port Backing" + description = "ESX virtual machine serial port backing" required = True uri = StringItem() - direction = StringItem(enum=('client', 'server')) + direction = StringItem(enum=("client", "server")) filename = StringItem() class ESXVirtualMachineDeviceConnectionItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine Serial Port Connection - ''' - title = 'ESX Virtual Machine Serial Port Connection' - description = 'ESX virtual machine serial port connection' + """ + + title = "ESX Virtual Machine Serial Port Connection" + description = "ESX virtual machine serial port connection" required = True allow_guest_control = BooleanItem(default=True) @@ -49,172 +53,222 @@ class ESXVirtualMachineDeviceConnectionItem(ComplexSchemaItem): class ESXVirtualMachinePlacementSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine Placement - ''' - title = 'ESX Virtual Machine Placement Information' - description = 'ESX virtual machine placement property' + """ + + title = "ESX Virtual Machine Placement Information" + description = "ESX virtual machine placement property" required = True - cluster = StringItem(title='Virtual Machine Cluster', - description='Cluster of the virtual machine if it is placed to a cluster') - host = StringItem(title='Virtual Machine Host', - description='Host of the virtual machine if it is placed to a standalone host') - resourcepool = StringItem(title='Virtual Machine Resource Pool', - description='Resource pool of the virtual machine if it is placed to a resource pool') - folder = StringItem(title='Virtual Machine Folder', - description='Folder of the virtual machine where it should be deployed, default is the datacenter vmFolder') + cluster = StringItem( + title="Virtual Machine Cluster", + description="Cluster of the virtual machine if it is placed to a cluster", + ) + host = StringItem( + title="Virtual Machine Host", + description="Host of the virtual machine if it is placed to a standalone host", + ) + resourcepool = StringItem( + title="Virtual Machine Resource Pool", + description="Resource pool of the virtual machine if it is placed to a resource pool", + ) + folder = StringItem( + title="Virtual Machine Folder", + description="Folder of the virtual machine where it should be deployed, default is the datacenter vmFolder", + ) class ESXVirtualMachineCdDriveClientSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine CD Drive Client - ''' - title = 'ESX Virtual Machine Serial CD Client' - description = 'ESX virtual machine CD/DVD drive client properties' + """ - mode = StringItem(required=True, enum=('passthrough', 'atapi')) + title = "ESX Virtual Machine Serial CD Client" + description = "ESX virtual machine CD/DVD drive client properties" + + mode = StringItem(required=True, enum=("passthrough", "atapi")) class ESXVirtualMachineCdDriveIsoSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine CD Drive ISO - ''' - title = 'ESX Virtual Machine Serial CD ISO' - description = 'ESX virtual machine CD/DVD drive ISO properties' + """ + + title = "ESX Virtual Machine Serial CD ISO" + description = "ESX virtual machine CD/DVD drive ISO properties" path = StringItem(required=True) class ESXVirtualMachineCdDriveSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine CD Drives - ''' - title = 'ESX Virtual Machine Serial CD' - description = 'ESX virtual machine CD/DVD drive properties' + """ - adapter = StringItem(title='Virtual Machine CD/DVD Adapter', - description='Unique adapter name for virtual machine cd/dvd drive', - required=True) + title = "ESX Virtual Machine Serial CD" + description = "ESX virtual machine CD/DVD drive properties" + + adapter = StringItem( + title="Virtual Machine CD/DVD Adapter", + description="Unique adapter name for virtual machine cd/dvd drive", + required=True, + ) controller = StringItem(required=True) - device_type = StringItem(title='Virtual Machine Device Type', - description='CD/DVD drive of the virtual machine if it is placed to a cluster', - required=True, - default='client_device', - enum=('datastore_iso_file', 'client_device')) + device_type = StringItem( + title="Virtual Machine Device Type", + description="CD/DVD drive of the virtual machine if it is placed to a cluster", + required=True, + default="client_device", + enum=("datastore_iso_file", "client_device"), + ) client_device = ESXVirtualMachineCdDriveClientSchemaItem() datastore_iso_file = ESXVirtualMachineCdDriveIsoSchemaItem() connectable = ESXVirtualMachineDeviceConnectionItem() class ESXVirtualMachineSerialSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine Serial Port - ''' - title = 'ESX Virtual Machine Serial Port Configuration' - description = 'ESX virtual machine serial port properties' + """ - type = StringItem(title='Virtual Machine Serial Port Type', - required=True, - enum=('network', 'pipe', 'file', 'device')) - adapter = StringItem(title='Virtual Machine Serial Port Name', - description='Unique adapter name for virtual machine serial port' - 'for creation an arbitrary value should be specified', - required=True) + title = "ESX Virtual Machine Serial Port Configuration" + description = "ESX virtual machine serial port properties" + + type = StringItem( + title="Virtual Machine Serial Port Type", + required=True, + enum=("network", "pipe", "file", "device"), + ) + adapter = StringItem( + title="Virtual Machine Serial Port Name", + description="Unique adapter name for virtual machine serial port" + "for creation an arbitrary value should be specified", + required=True, + ) backing = ESXVirtualMachineSerialBackingItem() connectable = ESXVirtualMachineDeviceConnectionItem() - yield_port = BooleanItem(title='Serial Port Yield', - description='Serial port yield', - default=False) + yield_port = BooleanItem( + title="Serial Port Yield", description="Serial port yield", default=False + ) class ESXVirtualMachineScsiSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine SCSI Controller - ''' - title = 'ESX Virtual Machine SCSI Controller Configuration' - description = 'ESX virtual machine scsi controller properties' + """ + + title = "ESX Virtual Machine SCSI Controller Configuration" + description = "ESX virtual machine scsi controller properties" required = True - adapter = StringItem(title='Virtual Machine SCSI Controller Name', - description='Unique SCSI controller name' - 'for creation an arbitrary value should be specified', - required=True) - type = StringItem(title='Virtual Machine SCSI type', - description='Type of the SCSI controller', - required=True, - enum=('lsilogic', 'lsilogic_sas', 'paravirtual', 'buslogic')) - bus_sharing = StringItem(title='Virtual Machine SCSI bus sharing', - description='Sharing type of the SCSI bus', - required=True, - enum=('virtual_sharing', 'physical_sharing', 'no_sharing')) - bus_number = NumberItem(title='Virtual Machine SCSI bus number', - description='Unique bus number of the SCSI device', - required=True) + adapter = StringItem( + title="Virtual Machine SCSI Controller Name", + description="Unique SCSI controller name" + "for creation an arbitrary value should be specified", + required=True, + ) + type = StringItem( + title="Virtual Machine SCSI type", + description="Type of the SCSI controller", + required=True, + enum=("lsilogic", "lsilogic_sas", "paravirtual", "buslogic"), + ) + bus_sharing = StringItem( + title="Virtual Machine SCSI bus sharing", + description="Sharing type of the SCSI bus", + required=True, + enum=("virtual_sharing", "physical_sharing", "no_sharing"), + ) + bus_number = NumberItem( + title="Virtual Machine SCSI bus number", + description="Unique bus number of the SCSI device", + required=True, + ) class ESXVirtualMachineSataSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine SATA Controller - ''' - title = 'ESX Virtual Machine SATA Controller Configuration' - description = 'ESX virtual machine SATA controller properties' + """ + + title = "ESX Virtual Machine SATA Controller Configuration" + description = "ESX virtual machine SATA controller properties" required = False - adapter = StringItem(title='Virtual Machine SATA Controller Name', - description='Unique SATA controller name' - 'for creation an arbitrary value should be specified', - required=True) - bus_number = NumberItem(title='Virtual Machine SATA bus number', - description='Unique bus number of the SATA device', - required=True) + adapter = StringItem( + title="Virtual Machine SATA Controller Name", + description="Unique SATA controller name" + "for creation an arbitrary value should be specified", + required=True, + ) + bus_number = NumberItem( + title="Virtual Machine SATA bus number", + description="Unique bus number of the SATA device", + required=True, + ) class ESXVirtualMachineDiskSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine Disk - ''' - title = 'ESX Virtual Machine Disk Configuration' - description = 'ESX virtual machine disk properties' + """ + + title = "ESX Virtual Machine Disk Configuration" + description = "ESX virtual machine disk properties" required = True - size = NumberItem(title='Disk size', - description='Size of the disk in GB', - required=True) - unit = StringItem(title='Disk size unit', - description='Unit of the disk size, to VMware a ' - 'GB is the same as GiB = 1024MiB', - required=False, - default='GB', - enum=('KB', 'MB', 'GB')) - adapter = StringItem(title='Virtual Machine Adapter Name', - description='Unique adapter name for virtual machine' - 'for creation an arbitrary value should be specified', - required=True) - filename = StringItem(title='Virtual Machine Disk File', - description='File name of the virtual machine vmdk') - datastore = StringItem(title='Virtual Machine Disk Datastore', - description='Disk datastore where the virtual machine files will be placed', - required=True) - address = StringItem(title='Virtual Machine SCSI Address', - description='Address of the SCSI adapter for the virtual machine', - pattern=r'\d:\d') - thin_provision = BooleanItem(title='Virtual Machine Disk Provision Type', - description='Provision type of the disk', - default=True, - required=False) - eagerly_scrub = AnyOfItem(required=False, - items=[BooleanItem(), NullItem()]) - controller = StringItem(title='Virtual Machine SCSI Adapter', - description='Name of the SCSI adapter where the disk will be connected', - required=True) + size = NumberItem( + title="Disk size", description="Size of the disk in GB", required=True + ) + unit = StringItem( + title="Disk size unit", + description="Unit of the disk size, to VMware a " + "GB is the same as GiB = 1024MiB", + required=False, + default="GB", + enum=("KB", "MB", "GB"), + ) + adapter = StringItem( + title="Virtual Machine Adapter Name", + description="Unique adapter name for virtual machine" + "for creation an arbitrary value should be specified", + required=True, + ) + filename = StringItem( + title="Virtual Machine Disk File", + description="File name of the virtual machine vmdk", + ) + datastore = StringItem( + title="Virtual Machine Disk Datastore", + description="Disk datastore where the virtual machine files will be placed", + required=True, + ) + address = StringItem( + title="Virtual Machine SCSI Address", + description="Address of the SCSI adapter for the virtual machine", + pattern=r"\d:\d", + ) + thin_provision = BooleanItem( + title="Virtual Machine Disk Provision Type", + description="Provision type of the disk", + default=True, + required=False, + ) + eagerly_scrub = AnyOfItem(required=False, items=[BooleanItem(), NullItem()]) + controller = StringItem( + title="Virtual Machine SCSI Adapter", + description="Name of the SCSI adapter where the disk will be connected", + required=True, + ) class ESXVirtualMachineNicMapSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine Nic Map - ''' - title = 'ESX Virtual Machine Nic Configuration' - description = 'ESX Virtual Machine nic properties' + """ + + title = "ESX Virtual Machine Nic Configuration" + description = "ESX Virtual Machine nic properties" required = False domain = StringItem() @@ -224,172 +278,230 @@ class ESXVirtualMachineNicMapSchemaItem(ComplexSchemaItem): class ESXVirtualMachineInterfaceSchemaItem(ComplexSchemaItem): - ''' + """ Configuration Schema Item for ESX Virtual Machine Network Interface - ''' - title = 'ESX Virtual Machine Network Interface Configuration' - description = 'ESX Virtual Machine network adapter properties' + """ + + title = "ESX Virtual Machine Network Interface Configuration" + description = "ESX Virtual Machine network adapter properties" required = True - name = StringItem(title='Virtual Machine Port Group', - description='Specifies the port group name for the virtual machine connection', - required=True) - adapter = StringItem(title='Virtual Machine Network Adapter', - description='Unique name of the network adapter, ' - 'for creation an arbitrary value should be specified', - required=True) - adapter_type = StringItem(title='Virtual Machine Adapter Type', - description='Network adapter type of the virtual machine', - required=True, - enum=('vmxnet', 'vmxnet2', 'vmxnet3', 'e1000', 'e1000e'), - default='vmxnet3') - switch_type = StringItem(title='Virtual Machine Switch Type', - description='Specifies the type of the virtual switch for the virtual machine connection', - required=True, - default='standard', - enum=('standard', 'distributed')) - mac = StringItem(title='Virtual Machine MAC Address', - description='Mac address of the virtual machine', - required=False, - pattern='^([0-9a-f]{1,2}[:]){5}([0-9a-f]{1,2})$') + name = StringItem( + title="Virtual Machine Port Group", + description="Specifies the port group name for the virtual machine connection", + required=True, + ) + adapter = StringItem( + title="Virtual Machine Network Adapter", + description="Unique name of the network adapter, " + "for creation an arbitrary value should be specified", + required=True, + ) + adapter_type = StringItem( + title="Virtual Machine Adapter Type", + description="Network adapter type of the virtual machine", + required=True, + enum=("vmxnet", "vmxnet2", "vmxnet3", "e1000", "e1000e"), + default="vmxnet3", + ) + switch_type = StringItem( + title="Virtual Machine Switch Type", + description="Specifies the type of the virtual switch for the virtual machine connection", + required=True, + default="standard", + enum=("standard", "distributed"), + ) + mac = StringItem( + title="Virtual Machine MAC Address", + description="Mac address of the virtual machine", + required=False, + pattern="^([0-9a-f]{1,2}[:]){5}([0-9a-f]{1,2})$", + ) mapping = ESXVirtualMachineNicMapSchemaItem() connectable = ESXVirtualMachineDeviceConnectionItem() class ESXVirtualMachineMemorySchemaItem(ComplexSchemaItem): - ''' + """ Configurtation Schema Item for ESX Virtual Machine Memory - ''' - title = 'ESX Virtual Machine Memory Configuration' - description = 'ESX Virtual Machine memory property' + """ + + title = "ESX Virtual Machine Memory Configuration" + description = "ESX Virtual Machine memory property" required = True - size = IntegerItem(title='Memory size', - description='Size of the memory', - required=True) + size = IntegerItem( + title="Memory size", description="Size of the memory", required=True + ) - unit = StringItem(title='Memory unit', - description='Unit of the memory, to VMware a ' - 'GB is the same as GiB = 1024MiB', - required=False, - default='MB', - enum=('MB', 'GB')) + unit = StringItem( + title="Memory unit", + description="Unit of the memory, to VMware a " + "GB is the same as GiB = 1024MiB", + required=False, + default="MB", + enum=("MB", "GB"), + ) hotadd = BooleanItem(required=False, default=False) reservation_max = BooleanItem(required=False, default=False) class ESXVirtualMachineCpuSchemaItem(ComplexSchemaItem): - ''' + """ Configurtation Schema Item for ESX Virtual Machine CPU - ''' - title = 'ESX Virtual Machine Memory Configuration' - description = 'ESX Virtual Machine memory property' + """ + + title = "ESX Virtual Machine Memory Configuration" + description = "ESX Virtual Machine memory property" required = True - count = IntegerItem(title='CPU core count', - description='CPU core count', - required=True) - cores_per_socket = IntegerItem(title='CPU cores per socket', - description='CPU cores per socket count', - required=False) - nested = BooleanItem(title='Virtual Machine Nested Property', - description='Nested virtualization support', - default=False) - hotadd = BooleanItem(title='Virtual Machine CPU hot add', - description='CPU hot add', - default=False) - hotremove = BooleanItem(title='Virtual Machine CPU hot remove', - description='CPU hot remove', - default=False) + count = IntegerItem( + title="CPU core count", description="CPU core count", required=True + ) + cores_per_socket = IntegerItem( + title="CPU cores per socket", + description="CPU cores per socket count", + required=False, + ) + nested = BooleanItem( + title="Virtual Machine Nested Property", + description="Nested virtualization support", + default=False, + ) + hotadd = BooleanItem( + title="Virtual Machine CPU hot add", description="CPU hot add", default=False + ) + hotremove = BooleanItem( + title="Virtual Machine CPU hot remove", + description="CPU hot remove", + default=False, + ) class ESXVirtualMachineConfigSchema(DefinitionsSchema): - ''' + """ Configuration Schema for ESX Virtual Machines - ''' - title = 'ESX Virtual Machine Configuration Schema' - description = 'ESX Virtual Machine configuration schema' + """ - vm_name = StringItem(title='Virtual Machine name', - description='Name of the virtual machine', - required=True) + title = "ESX Virtual Machine Configuration Schema" + description = "ESX Virtual Machine configuration schema" + + vm_name = StringItem( + title="Virtual Machine name", + description="Name of the virtual machine", + required=True, + ) cpu = ESXVirtualMachineCpuSchemaItem() memory = ESXVirtualMachineMemorySchemaItem() - image = StringItem(title='Virtual Machine guest OS', - description='Guest OS type', - required=True) - version = StringItem(title='Virtual Machine hardware version', - description='Container hardware version property', - required=True) - interfaces = ArrayItem(items=ESXVirtualMachineInterfaceSchemaItem(), - min_items=1, - required=False, - unique_items=True) - disks = ArrayItem(items=ESXVirtualMachineDiskSchemaItem(), - min_items=1, - required=False, - unique_items=True) - scsi_devices = ArrayItem(items=ESXVirtualMachineScsiSchemaItem(), - min_items=1, - required=False, - unique_items=True) - serial_ports = ArrayItem(items=ESXVirtualMachineSerialSchemaItem(), - min_items=0, - required=False, - unique_items=True) - cd_dvd_drives = ArrayItem(items=ESXVirtualMachineCdDriveSchemaItem(), - min_items=0, - required=False, - unique_items=True) - sata_controllers = ArrayItem(items=ESXVirtualMachineSataSchemaItem(), - min_items=0, - required=False, - unique_items=True) - datacenter = StringItem(title='Virtual Machine Datacenter', - description='Datacenter of the virtual machine', - required=True) - datastore = StringItem(title='Virtual Machine Datastore', - description='Datastore of the virtual machine', - required=True) + image = StringItem( + title="Virtual Machine guest OS", description="Guest OS type", required=True + ) + version = StringItem( + title="Virtual Machine hardware version", + description="Container hardware version property", + required=True, + ) + interfaces = ArrayItem( + items=ESXVirtualMachineInterfaceSchemaItem(), + min_items=1, + required=False, + unique_items=True, + ) + disks = ArrayItem( + items=ESXVirtualMachineDiskSchemaItem(), + min_items=1, + required=False, + unique_items=True, + ) + scsi_devices = ArrayItem( + items=ESXVirtualMachineScsiSchemaItem(), + min_items=1, + required=False, + unique_items=True, + ) + serial_ports = ArrayItem( + items=ESXVirtualMachineSerialSchemaItem(), + min_items=0, + required=False, + unique_items=True, + ) + cd_dvd_drives = ArrayItem( + items=ESXVirtualMachineCdDriveSchemaItem(), + min_items=0, + required=False, + unique_items=True, + ) + sata_controllers = ArrayItem( + items=ESXVirtualMachineSataSchemaItem(), + min_items=0, + required=False, + unique_items=True, + ) + datacenter = StringItem( + title="Virtual Machine Datacenter", + description="Datacenter of the virtual machine", + required=True, + ) + datastore = StringItem( + title="Virtual Machine Datastore", + description="Datastore of the virtual machine", + required=True, + ) placement = ESXVirtualMachinePlacementSchemaItem() - template = BooleanItem(title='Virtual Machine Template', - description='Template to create the virtual machine from', - default=False) - tools = BooleanItem(title='Virtual Machine VMware Tools', - description='Install VMware tools on the guest machine', - default=False) - power_on = BooleanItem(title='Virtual Machine Power', - description='Power on virtual machine afret creation', - default=False) - deploy = BooleanItem(title='Virtual Machine Deploy Salt', - description='Deploy salt after successful installation', - default=False) + template = BooleanItem( + title="Virtual Machine Template", + description="Template to create the virtual machine from", + default=False, + ) + tools = BooleanItem( + title="Virtual Machine VMware Tools", + description="Install VMware tools on the guest machine", + default=False, + ) + power_on = BooleanItem( + title="Virtual Machine Power", + description="Power on virtual machine afret creation", + default=False, + ) + deploy = BooleanItem( + title="Virtual Machine Deploy Salt", + description="Deploy salt after successful installation", + default=False, + ) class ESXVirtualMachineRemoveSchema(DefinitionsSchema): - ''' + """ Remove Schema for ESX Virtual Machines to delete or unregister virtual machines - ''' - name = StringItem(title='Virtual Machine name', - description='Name of the virtual machine', - required=True) - datacenter = StringItem(title='Virtual Machine Datacenter', - description='Datacenter of the virtual machine', - required=True) - placement = AnyOfItem(required=False, - items=[ESXVirtualMachinePlacementSchemaItem(), NullItem()]) - power_off = BooleanItem(title='Power off vm', - description='Power off vm before delete operation', - required=False) + """ + + name = StringItem( + title="Virtual Machine name", + description="Name of the virtual machine", + required=True, + ) + datacenter = StringItem( + title="Virtual Machine Datacenter", + description="Datacenter of the virtual machine", + required=True, + ) + placement = AnyOfItem( + required=False, items=[ESXVirtualMachinePlacementSchemaItem(), NullItem()] + ) + power_off = BooleanItem( + title="Power off vm", + description="Power off vm before delete operation", + required=False, + ) class ESXVirtualMachineDeleteSchema(ESXVirtualMachineRemoveSchema): - ''' + """ Deletion Schema for ESX Virtual Machines - ''' + """ class ESXVirtualMachineUnregisterSchema(ESXVirtualMachineRemoveSchema): - ''' + """ Unregister Schema for ESX Virtual Machines - ''' + """ diff --git a/salt/config/schemas/minion.py b/salt/config/schemas/minion.py index f98e44b7bdf..223706535c4 100644 --- a/salt/config/schemas/minion.py +++ b/salt/config/schemas/minion.py @@ -1,23 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) salt.config.schemas.minion ~~~~~~~~~~~~~~~~~~~~~~~~~~ Minion configuration schema -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals +from salt.config.schemas.common import IncludeConfig, MinionDefaultInclude + # Import salt libs -from salt.utils.schema import (Schema, - IPv4Item, - ) -from salt.config.schemas.common import (MinionDefaultInclude, - IncludeConfig - ) +from salt.utils.schema import IPv4Item, Schema # XXX: THIS IS WAY TOO MINIMAL, BUT EXISTS TO IMPLEMENT salt-ssh @@ -29,7 +26,7 @@ class MinionConfiguration(Schema): # would fail __allow_additional_items__ = True - interface = IPv4Item(title='Interface') + interface = IPv4Item(title="Interface") default_include = MinionDefaultInclude() include = IncludeConfig() diff --git a/salt/config/schemas/ssh.py b/salt/config/schemas/ssh.py index 8f38d6b6594..bd29ff5dde3 100644 --- a/salt/config/schemas/ssh.py +++ b/salt/config/schemas/ssh.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,76 +7,102 @@ ~~~~~~~~~~~~~~~~~~~~~~~ Salt SSH related configuration schemas -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt libs -from salt.utils.schema import (Schema, - StringItem, - IntegerItem, - SecretItem, - PortItem, - BooleanItem, - RequirementsItem, - DictItem, - AnyOfItem - ) from salt.config.schemas.minion import MinionConfiguration +# Import Salt libs +from salt.utils.schema import ( + AnyOfItem, + BooleanItem, + DictItem, + IntegerItem, + PortItem, + RequirementsItem, + Schema, + SecretItem, + StringItem, +) + class RosterEntryConfig(Schema): - ''' + """ Schema definition of a Salt SSH Roster entry - ''' + """ - title = 'Roster Entry' - description = 'Salt SSH roster entry definition' + title = "Roster Entry" + description = "Salt SSH roster entry definition" - host = StringItem(title='Host', - description='The IP address or DNS name of the remote host', - # Pretty naive pattern matching - pattern=r'^((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([A-Za-z0-9][A-Za-z0-9\.\-]{1,255}))$', - min_length=1, - required=True) - port = PortItem(title='Port', - description='The target system\'s ssh port number', - default=22) - user = StringItem(title='User', - description='The user to log in as. Defaults to root', - default='root', - min_length=1, - required=True) - passwd = SecretItem(title='Password', - description='The password to log in with', - min_length=1) - priv = StringItem(title='Private Key', - description='File path to ssh private key, defaults to salt-ssh.rsa', - min_length=1) - priv_passwd = SecretItem(title='Private Key passphrase', - description='Passphrase for private key file', - min_length=1) - passwd_or_priv_requirement = AnyOfItem(items=(RequirementsItem(requirements=['passwd']), - RequirementsItem(requirements=['priv'])))(flatten=True) - sudo = BooleanItem(title='Sudo', - description='run command via sudo. Defaults to False', - default=False) - timeout = IntegerItem(title='Timeout', - description=('Number of seconds to wait for response ' - 'when establishing an SSH connection')) - thin_dir = StringItem(title='Thin Directory', - description=('The target system\'s storage directory for Salt ' - 'components. Defaults to /tmp/salt-.')) - minion_opts = DictItem(title='Minion Options', - description='Dictionary of minion options', - properties=MinionConfiguration()) + host = StringItem( + title="Host", + description="The IP address or DNS name of the remote host", + # Pretty naive pattern matching + pattern=r"^((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([A-Za-z0-9][A-Za-z0-9\.\-]{1,255}))$", + min_length=1, + required=True, + ) + port = PortItem( + title="Port", description="The target system's ssh port number", default=22 + ) + user = StringItem( + title="User", + description="The user to log in as. Defaults to root", + default="root", + min_length=1, + required=True, + ) + passwd = SecretItem( + title="Password", description="The password to log in with", min_length=1 + ) + priv = StringItem( + title="Private Key", + description="File path to ssh private key, defaults to salt-ssh.rsa", + min_length=1, + ) + priv_passwd = SecretItem( + title="Private Key passphrase", + description="Passphrase for private key file", + min_length=1, + ) + passwd_or_priv_requirement = AnyOfItem( + items=( + RequirementsItem(requirements=["passwd"]), + RequirementsItem(requirements=["priv"]), + ) + )(flatten=True) + sudo = BooleanItem( + title="Sudo", + description="run command via sudo. Defaults to False", + default=False, + ) + timeout = IntegerItem( + title="Timeout", + description=( + "Number of seconds to wait for response " + "when establishing an SSH connection" + ), + ) + thin_dir = StringItem( + title="Thin Directory", + description=( + "The target system's storage directory for Salt " + "components. Defaults to /tmp/salt-." + ), + ) + minion_opts = DictItem( + title="Minion Options", + description="Dictionary of minion options", + properties=MinionConfiguration(), + ) class RosterItem(Schema): - title = 'Roster Configuration' - description = 'Roster entries definition' + title = "Roster Configuration" + description = "Roster entries definition" - roster_entries = DictItem( - pattern_properties={ - r'^([^:]+)$': RosterEntryConfig()})(flatten=True) + roster_entries = DictItem(pattern_properties={r"^([^:]+)$": RosterEntryConfig()})( + flatten=True + ) diff --git a/salt/config/schemas/vcenter.py b/salt/config/schemas/vcenter.py index e46149665f5..7db8b67c417 100644 --- a/salt/config/schemas/vcenter.py +++ b/salt/config/schemas/vcenter.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Rod McKenzie (roderick.mckenzie@morganstanley.com)` :codeauthor: :email:`Alexandru Bleotu (alexandru.bleotu@morganstanley.com)` @@ -7,51 +7,49 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ VCenter configuration schemas -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs -from salt.utils.schema import (Schema, - ArrayItem, - IntegerItem, - StringItem) +from salt.utils.schema import ArrayItem, IntegerItem, Schema, StringItem class VCenterEntitySchema(Schema): - ''' + """ Entity Schema for a VCenter. - ''' - title = 'VCenter Entity Schema' - description = 'VCenter entity schema' - type = StringItem(title='Type', - description='Specifies the entity type', - required=True, - enum=['vcenter']) + """ - vcenter = StringItem(title='vCenter', - description='Specifies the vcenter hostname', - required=True) + title = "VCenter Entity Schema" + description = "VCenter entity schema" + type = StringItem( + title="Type", + description="Specifies the entity type", + required=True, + enum=["vcenter"], + ) + + vcenter = StringItem( + title="vCenter", description="Specifies the vcenter hostname", required=True + ) class VCenterProxySchema(Schema): - ''' + """ Schema for the configuration for the proxy to connect to a VCenter. - ''' - title = 'VCenter Proxy Connection Schema' - description = 'Schema that describes the connection to a VCenter' + """ + + title = "VCenter Proxy Connection Schema" + description = "Schema that describes the connection to a VCenter" additional_properties = False - proxytype = StringItem(required=True, - enum=['vcenter']) - vcenter = StringItem(required=True, pattern=r'[^\s]+') - mechanism = StringItem(required=True, enum=['userpass', 'sspi']) + proxytype = StringItem(required=True, enum=["vcenter"]) + vcenter = StringItem(required=True, pattern=r"[^\s]+") + mechanism = StringItem(required=True, enum=["userpass", "sspi"]) username = StringItem() - passwords = ArrayItem(min_items=1, - items=StringItem(), - unique_items=True) + passwords = ArrayItem(min_items=1, items=StringItem(), unique_items=True) domain = StringItem() - principal = StringItem(default='host') - protocol = StringItem(default='https') + principal = StringItem(default="host") + protocol = StringItem(default="https") port = IntegerItem(minimum=1) diff --git a/salt/crypt.py b/salt/crypt.py index 6d45a0d0539..17e6dca7ae1 100644 --- a/salt/crypt.py +++ b/salt/crypt.py @@ -1,64 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" The crypt module manages all of the cryptography functions for minions and masters, encrypting and decrypting payloads, preparing messages, and authenticating peers -''' -# Import python libs +""" + # NOTE: We can't use unicode_literals because this module implicitly uses # the Array class, which has incompatibilities with it. from __future__ import absolute_import, print_function + +import base64 +import binascii +import copy +import getpass +import hashlib +import hmac +import logging + +# Import python libs import os import random -import sys -import copy -import time -import hmac -import base64 -import hashlib -import logging import stat +import sys +import time import traceback -import binascii import weakref -import getpass -import salt.ext.tornado.gen - -# Import third party libs -from salt.ext.six.moves import zip # pylint: disable=import-error,redefined-builtin -from salt.ext import six - -try: - from M2Crypto import RSA, EVP, BIO - HAS_M2 = True -except ImportError: - HAS_M2 = False - -if not HAS_M2: - try: - from Cryptodome.Cipher import AES, PKCS1_OAEP - from Cryptodome.Hash import SHA - from Cryptodome.PublicKey import RSA - from Cryptodome.Signature import PKCS1_v1_5 - import Cryptodome.Random # pylint: disable=W0611 - HAS_CDOME = True - except ImportError: - HAS_CDOME = False - -if not HAS_M2 and not HAS_CDOME: - try: - from Crypto.Cipher import AES, PKCS1_OAEP - from Crypto.Hash import SHA - from Crypto.PublicKey import RSA - from Crypto.Signature import PKCS1_v1_5 - # let this be imported, if possible - import Crypto.Random # pylint: disable=W0611 - except ImportError: - # No need for crypt in local mode - pass # Import salt libs import salt.defaults.exitcodes +import salt.ext.tornado.gen import salt.payload import salt.transport.client import salt.transport.frame @@ -73,32 +43,73 @@ import salt.utils.user import salt.utils.verify import salt.version from salt.exceptions import ( - AuthenticationError, SaltClientError, SaltReqTimeoutError, MasterExit + AuthenticationError, + MasterExit, + SaltClientError, + SaltReqTimeoutError, ) +from salt.ext import six + +# Import third party libs +from salt.ext.six.moves import zip # pylint: disable=import-error,redefined-builtin + +try: + from M2Crypto import RSA, EVP, BIO + + HAS_M2 = True +except ImportError: + HAS_M2 = False + +if not HAS_M2: + try: + from Cryptodome.Cipher import AES, PKCS1_OAEP + from Cryptodome.Hash import SHA + from Cryptodome.PublicKey import RSA + from Cryptodome.Signature import PKCS1_v1_5 + import Cryptodome.Random # pylint: disable=W0611 + + HAS_CDOME = True + except ImportError: + HAS_CDOME = False + +if not HAS_M2 and not HAS_CDOME: + try: + from Crypto.Cipher import AES, PKCS1_OAEP + from Crypto.Hash import SHA + from Crypto.PublicKey import RSA + from Crypto.Signature import PKCS1_v1_5 + + # let this be imported, if possible + import Crypto.Random # pylint: disable=W0611 + except ImportError: + # No need for crypt in local mode + pass + log = logging.getLogger(__name__) def dropfile(cachedir, user=None): - ''' + """ Set an AES dropfile to request the master update the publish session key - ''' - dfn = os.path.join(cachedir, '.dfn') + """ + dfn = os.path.join(cachedir, ".dfn") # set a mask (to avoid a race condition on file creation) and store original. with salt.utils.files.set_umask(0o277): - log.info('Rotating AES key') + log.info("Rotating AES key") if os.path.isfile(dfn): - log.info('AES key rotation already requested') + log.info("AES key rotation already requested") return if os.path.isfile(dfn) and not os.access(dfn, os.W_OK): os.chmod(dfn, stat.S_IRUSR | stat.S_IWUSR) - with salt.utils.files.fopen(dfn, 'wb+') as fp_: - fp_.write(b'') + with salt.utils.files.fopen(dfn, "wb+") as fp_: + fp_.write(b"") os.chmod(dfn, stat.S_IRUSR) if user: try: import pwd + uid = pwd.getpwnam(user).pw_uid os.chown(dfn, uid, -1) except (KeyError, ImportError, OSError, IOError): @@ -106,7 +117,7 @@ def dropfile(cachedir, user=None): def gen_keys(keydir, keyname, keysize, user=None, passphrase=None): - ''' + """ Generate a RSA public keypair for use with salt :param str keydir: The directory to write the keypair to @@ -117,10 +128,10 @@ def gen_keys(keydir, keyname, keysize, user=None, passphrase=None): :rtype: str :return: Path on the filesystem to the RSA private key - ''' + """ base = os.path.join(keydir, keyname) - priv = '{0}.pem'.format(base) - pub = '{0}.pub'.format(base) + priv = "{0}.pem".format(base) + pub = "{0}.pub".format(base) if HAS_M2: gen = RSA.gen_key(keysize, 65537, lambda: None) @@ -134,7 +145,11 @@ def gen_keys(keydir, keyname, keysize, user=None, passphrase=None): # Do not try writing anything, if directory has no permissions. if not os.access(keydir, os.W_OK): - raise IOError('Write access denied to "{0}" for user "{1}".'.format(os.path.abspath(keydir), getpass.getuser())) + raise IOError( + 'Write access denied to "{0}" for user "{1}".'.format( + os.path.abspath(keydir), getpass.getuser() + ) + ) with salt.utils.files.set_umask(0o277): if HAS_M2: @@ -144,20 +159,22 @@ def gen_keys(keydir, keyname, keysize, user=None, passphrase=None): else: gen.save_pem( priv, - cipher='des_ede3_cbc', - callback=lambda x: salt.utils.stringutils.to_bytes(passphrase)) + cipher="des_ede3_cbc", + callback=lambda x: salt.utils.stringutils.to_bytes(passphrase), + ) else: - with salt.utils.files.fopen(priv, 'wb+') as f: - f.write(gen.exportKey('PEM', passphrase)) + with salt.utils.files.fopen(priv, "wb+") as f: + f.write(gen.exportKey("PEM", passphrase)) if HAS_M2: gen.save_pub_key(pub) else: - with salt.utils.files.fopen(pub, 'wb+') as f: - f.write(gen.publickey().exportKey('PEM')) + with salt.utils.files.fopen(pub, "wb+") as f: + f.write(gen.publickey().exportKey("PEM")) os.chmod(priv, 0o400) if user: try: import pwd + uid = pwd.getpwnam(user).pw_uid os.chown(priv, uid, -1) os.chown(pub, uid, -1) @@ -170,14 +187,14 @@ def gen_keys(keydir, keyname, keysize, user=None, passphrase=None): @salt.utils.decorators.memoize def _get_key_with_evict(path, timestamp, passphrase): - ''' + """ Load a private key from disk. `timestamp` above is intended to be the timestamp of the file's last modification. This fn is memoized so if it is called with the same path and timestamp (the file's last modified time) the second time the result is returned from the memoiziation. If the file gets modified then the params are different and the key is loaded from disk. - ''' - log.debug('salt.crypt._get_key_with_evict: Loading private key') + """ + log.debug("salt.crypt._get_key_with_evict: Loading private key") if HAS_M2: key = RSA.load_key(path, lambda x: six.b(passphrase)) else: @@ -187,7 +204,7 @@ def _get_key_with_evict(path, timestamp, passphrase): def get_rsa_key(path, passphrase): - ''' + """ Read a private key off the disk. Poor man's simple cache in effect here, we memoize the result of calling _get_rsa_with_evict. This means the first time _get_key_with_evict is called with a path and a timestamp the result @@ -196,19 +213,19 @@ def get_rsa_key(path, passphrase): cache. If the key DOES change the next time _get_rsa_with_evict is called it is called with different parameters and the fn is run fully to retrieve the key from disk. - ''' - log.debug('salt.crypt.get_rsa_key: Loading private key') + """ + log.debug("salt.crypt.get_rsa_key: Loading private key") return _get_key_with_evict(path, six.text_type(os.path.getmtime(path)), passphrase) def get_rsa_pub_key(path): - ''' + """ Read a public key off the disk. - ''' - log.debug('salt.crypt.get_rsa_pub_key: Loading public key') + """ + log.debug("salt.crypt.get_rsa_pub_key: Loading public key") if HAS_M2: - with salt.utils.files.fopen(path, 'rb') as f: - data = f.read().replace(b'RSA ', b'') + with salt.utils.files.fopen(path, "rb") as f: + data = f.read().replace(b"RSA ", b"") bio = BIO.MemoryBuffer(data) key = RSA.load_pub_key_bio(bio) else: @@ -218,13 +235,13 @@ def get_rsa_pub_key(path): def sign_message(privkey_path, message, passphrase=None): - ''' + """ Use Crypto.Signature.PKCS1_v1_5 to sign a message. Returns the signature. - ''' + """ key = get_rsa_key(privkey_path, passphrase) - log.debug('salt.crypt.sign_message: Signing message.') + log.debug("salt.crypt.sign_message: Signing message.") if HAS_M2: - md = EVP.MessageDigest('sha1') + md = EVP.MessageDigest("sha1") md.update(salt.utils.stringutils.to_bytes(message)) digest = md.final() return key.sign(digest) @@ -234,28 +251,30 @@ def sign_message(privkey_path, message, passphrase=None): def verify_signature(pubkey_path, message, signature): - ''' + """ Use Crypto.Signature.PKCS1_v1_5 to verify the signature on a message. Returns True for valid signature. - ''' - log.debug('salt.crypt.verify_signature: Loading public key') + """ + log.debug("salt.crypt.verify_signature: Loading public key") pubkey = get_rsa_pub_key(pubkey_path) - log.debug('salt.crypt.verify_signature: Verifying signature') + log.debug("salt.crypt.verify_signature: Verifying signature") if HAS_M2: - md = EVP.MessageDigest('sha1') + md = EVP.MessageDigest("sha1") md.update(salt.utils.stringutils.to_bytes(message)) digest = md.final() return pubkey.verify(digest, signature) else: verifier = PKCS1_v1_5.new(pubkey) - return verifier.verify(SHA.new(salt.utils.stringutils.to_bytes(message)), signature) + return verifier.verify( + SHA.new(salt.utils.stringutils.to_bytes(message)), signature + ) def gen_signature(priv_path, pub_path, sign_path, passphrase=None): - ''' + """ creates a signature for the given public-key with the given private key and writes it to sign_path - ''' + """ with salt.utils.files.fopen(pub_path) as fp_: mpub_64 = fp_.read() @@ -265,40 +284,41 @@ def gen_signature(priv_path, pub_path, sign_path, passphrase=None): if os.path.isfile(sign_path): return False log.trace( - 'Calculating signature for %s with %s', - os.path.basename(pub_path), os.path.basename(priv_path) + "Calculating signature for %s with %s", + os.path.basename(pub_path), + os.path.basename(priv_path), ) if os.path.isfile(sign_path): log.trace( - 'Signature file %s already exists, please remove it first and ' - 'try again', sign_path + "Signature file %s already exists, please remove it first and " "try again", + sign_path, ) else: - with salt.utils.files.fopen(sign_path, 'wb+') as sig_f: + with salt.utils.files.fopen(sign_path, "wb+") as sig_f: sig_f.write(salt.utils.stringutils.to_bytes(mpub_sig_64)) - log.trace('Wrote signature to %s', sign_path) + log.trace("Wrote signature to %s", sign_path) return True def private_encrypt(key, message): - ''' + """ Generate an M2Crypto-compatible signature :param Crypto.PublicKey.RSA._RSAobj key: The RSA key object :param str message: The message to sign :rtype: str :return: The signature, or an empty string if the signature operation failed - ''' + """ if HAS_M2: return key.private_encrypt(message, salt.utils.rsax931.RSA_X931_PADDING) else: - signer = salt.utils.rsax931.RSAX931Signer(key.exportKey('PEM')) + signer = salt.utils.rsax931.RSAX931Signer(key.exportKey("PEM")) return signer.sign(message) def public_decrypt(pub, message): - ''' + """ Verify an M2Crypto-compatible signature :param Crypto.PublicKey.RSA._RSAobj key: The RSA public key object @@ -306,68 +326,75 @@ def public_decrypt(pub, message): :rtype: str :return: The message (or digest) recovered from the signature, or an empty string if the verification failed - ''' + """ if HAS_M2: return pub.public_decrypt(message, salt.utils.rsax931.RSA_X931_PADDING) else: - verifier = salt.utils.rsax931.RSAX931Verifier(pub.exportKey('PEM')) + verifier = salt.utils.rsax931.RSAX931Verifier(pub.exportKey("PEM")) return verifier.verify(message) class MasterKeys(dict): - ''' + """ The Master Keys class is used to manage the RSA public key pair used for authentication by the master. It also generates a signing key-pair if enabled with master_sign_key_name. - ''' + """ + def __init__(self, opts): super(MasterKeys, self).__init__() self.opts = opts - self.pub_path = os.path.join(self.opts['pki_dir'], 'master.pub') - self.rsa_path = os.path.join(self.opts['pki_dir'], 'master.pem') + self.pub_path = os.path.join(self.opts["pki_dir"], "master.pub") + self.rsa_path = os.path.join(self.opts["pki_dir"], "master.pem") - key_pass = salt.utils.sdb.sdb_get(self.opts['key_pass'], self.opts) + key_pass = salt.utils.sdb.sdb_get(self.opts["key_pass"], self.opts) self.key = self.__get_keys(passphrase=key_pass) self.pub_signature = None # set names for the signing key-pairs - if opts['master_sign_pubkey']: + if opts["master_sign_pubkey"]: # if only the signature is available, use that - if opts['master_use_pubkey_signature']: - self.sig_path = os.path.join(self.opts['pki_dir'], - opts['master_pubkey_signature']) + if opts["master_use_pubkey_signature"]: + self.sig_path = os.path.join( + self.opts["pki_dir"], opts["master_pubkey_signature"] + ) if os.path.isfile(self.sig_path): with salt.utils.files.fopen(self.sig_path) as fp_: self.pub_signature = fp_.read() log.info( - 'Read %s\'s signature from %s', + "Read %s's signature from %s", os.path.basename(self.pub_path), - self.opts['master_pubkey_signature'] + self.opts["master_pubkey_signature"], ) else: log.error( - 'Signing the master.pub key with a signature is ' - 'enabled but no signature file found at the defined ' - 'location %s', self.sig_path + "Signing the master.pub key with a signature is " + "enabled but no signature file found at the defined " + "location %s", + self.sig_path, ) log.error( - 'The signature-file may be either named differently ' - 'or has to be created with \'salt-key --gen-signature\'' + "The signature-file may be either named differently " + "or has to be created with 'salt-key --gen-signature'" ) sys.exit(1) # create a new signing key-pair to sign the masters # auth-replies when a minion tries to connect else: - key_pass = salt.utils.sdb.sdb_get(self.opts['signing_key_pass'], self.opts) - self.pub_sign_path = os.path.join(self.opts['pki_dir'], - opts['master_sign_key_name'] + '.pub') - self.rsa_sign_path = os.path.join(self.opts['pki_dir'], - opts['master_sign_key_name'] + '.pem') - self.sign_key = self.__get_keys(name=opts['master_sign_key_name']) + key_pass = salt.utils.sdb.sdb_get( + self.opts["signing_key_pass"], self.opts + ) + self.pub_sign_path = os.path.join( + self.opts["pki_dir"], opts["master_sign_key_name"] + ".pub" + ) + self.rsa_sign_path = os.path.join( + self.opts["pki_dir"], opts["master_sign_key_name"] + ".pem" + ) + self.sign_key = self.__get_keys(name=opts["master_sign_key_name"]) # We need __setstate__ and __getstate__ to avoid pickling errors since # some of the member variables correspond to Cython objects which are @@ -375,24 +402,25 @@ class MasterKeys(dict): # These methods are only used when pickling so will not be used on # non-Windows platforms. def __setstate__(self, state): - self.__init__(state['opts']) + self.__init__(state["opts"]) def __getstate__(self): - return {'opts': self.opts} + return {"opts": self.opts} - def __get_keys(self, name='master', passphrase=None): - ''' + def __get_keys(self, name="master", passphrase=None): + """ Returns a key object for a key in the pki-dir - ''' - path = os.path.join(self.opts['pki_dir'], - name + '.pem') + """ + path = os.path.join(self.opts["pki_dir"], name + ".pem") if not os.path.exists(path): - log.info('Generating %s keys: %s', name, self.opts['pki_dir']) - gen_keys(self.opts['pki_dir'], - name, - self.opts['keysize'], - self.opts.get('user'), - passphrase) + log.info("Generating %s keys: %s", name, self.opts["pki_dir"]) + gen_keys( + self.opts["pki_dir"], + name, + self.opts["keysize"], + self.opts.get("user"), + passphrase, + ) if HAS_M2: key_error = RSA.RSAError else: @@ -400,26 +428,27 @@ class MasterKeys(dict): try: key = get_rsa_key(path, passphrase) except key_error as e: - message = 'Unable to read key: {0}; passphrase may be incorrect'.format(path) + message = "Unable to read key: {0}; passphrase may be incorrect".format( + path + ) log.error(message) raise MasterExit(message) - log.debug('Loaded %s key: %s', name, path) + log.debug("Loaded %s key: %s", name, path) return key - def get_pub_str(self, name='master'): - ''' + def get_pub_str(self, name="master"): + """ Return the string representation of a public key in the pki-directory - ''' - path = os.path.join(self.opts['pki_dir'], - name + '.pub') + """ + path = os.path.join(self.opts["pki_dir"], name + ".pub") if not os.path.isfile(path): key = self.__get_keys() if HAS_M2: key.save_pub_key(path) else: - with salt.utils.files.fopen(path, 'wb+') as wfh: - wfh.write(key.publickey().exportKey('PEM')) + with salt.utils.files.fopen(path, "wb+") as wfh: + wfh.write(key.publickey().exportKey("PEM")) with salt.utils.files.fopen(path) as rfh: return rfh.read() @@ -430,17 +459,18 @@ class MasterKeys(dict): return self.pub_sign_path, self.rsa_sign_path def pubkey_signature(self): - ''' + """ returns the base64 encoded signature from the signature file or None if the master has its own signing keys - ''' + """ return self.pub_signature class AsyncAuth(object): - ''' + """ Set up an Async object to maintain authentication with the salt master - ''' + """ + # This class is only a singleton per minion/master pair # mapping of io_loop -> {key -> auth} instance_map = weakref.WeakKeyDictionary() @@ -449,9 +479,9 @@ class AsyncAuth(object): creds_map = {} def __new__(cls, opts, io_loop=None): - ''' + """ Only create one instance of AsyncAuth per __key() - ''' + """ # do we have any mapping for this io_loop io_loop = io_loop or salt.ext.tornado.ioloop.IOLoop.current() if io_loop not in AsyncAuth.instance_map: @@ -461,7 +491,7 @@ class AsyncAuth(object): key = cls.__key(opts) auth = loop_instance_map.get(key) if auth is None: - log.debug('Initializing new AsyncAuth for %s', key) + log.debug("Initializing new AsyncAuth for %s", key) # we need to make a local variable for this, as we are going to store # it in a WeakValueDictionary-- which will remove the item if no one # references it-- this forces a reference while we return to the caller @@ -469,15 +499,16 @@ class AsyncAuth(object): auth.__singleton_init__(opts, io_loop=io_loop) loop_instance_map[key] = auth else: - log.debug('Re-using AsyncAuth for %s', key) + log.debug("Re-using AsyncAuth for %s", key) return auth @classmethod def __key(cls, opts, io_loop=None): - return (opts['pki_dir'], # where the keys are stored - opts['id'], # minion ID - opts['master_uri'], # master ID - ) + return ( + opts["pki_dir"], # where the keys are stored + opts["id"], # minion ID + opts["master_uri"], # master ID + ) # has to remain empty for singletons, since __init__ will *always* be called def __init__(self, opts, io_loop=None): @@ -485,25 +516,27 @@ class AsyncAuth(object): # an init for the singleton instance to call def __singleton_init__(self, opts, io_loop=None): - ''' + """ Init an Auth instance :param dict opts: Options for this server :return: Auth instance :rtype: Auth - ''' + """ self.opts = opts if six.PY2: self.token = Crypticle.generate_key_string() else: - self.token = salt.utils.stringutils.to_bytes(Crypticle.generate_key_string()) + self.token = salt.utils.stringutils.to_bytes( + Crypticle.generate_key_string() + ) self.serial = salt.payload.Serial(self.opts) - self.pub_path = os.path.join(self.opts['pki_dir'], 'minion.pub') - self.rsa_path = os.path.join(self.opts['pki_dir'], 'minion.pem') - if self.opts['__role'] == 'syndic': - self.mpub = 'syndic_master.pub' + self.pub_path = os.path.join(self.opts["pki_dir"], "minion.pub") + self.rsa_path = os.path.join(self.opts["pki_dir"], "minion.pem") + if self.opts["__role"] == "syndic": + self.mpub = "syndic_master.pub" else: - self.mpub = 'minion_master.pub' + self.mpub = "minion_master.pub" if not os.path.isfile(self.pub_path): self.get_keys() @@ -515,7 +548,7 @@ class AsyncAuth(object): if key in AsyncAuth.creds_map: creds = AsyncAuth.creds_map[key] self._creds = creds - self._crypticle = Crypticle(self.opts, creds['aes']) + self._crypticle = Crypticle(self.opts, creds["aes"]) self._authenticate_future = salt.ext.tornado.concurrent.Future() self._authenticate_future.set_result(True) else: @@ -526,7 +559,7 @@ class AsyncAuth(object): result = cls.__new__(cls, copy.deepcopy(self.opts, memo)) memo[id(self)] = result for key in self.__dict__: - if key in ('io_loop',): + if key in ("io_loop",): # The io_loop has a thread Lock which will fail to be deep # copied. Skip it because it will just be recreated on the # new copy. @@ -544,9 +577,11 @@ class AsyncAuth(object): @property def authenticated(self): - return hasattr(self, '_authenticate_future') and \ - self._authenticate_future.done() and \ - self._authenticate_future.exception() is None + return ( + hasattr(self, "_authenticate_future") + and self._authenticate_future.done() + and self._authenticate_future.exception() is None + ) def invalidate(self): if self.authenticated: @@ -556,14 +591,17 @@ class AsyncAuth(object): del AsyncAuth.creds_map[key] def authenticate(self, callback=None): - ''' + """ Ask for this client to reconnect to the origin This function will de-dupe all calls here and return a *single* future for the sign-in-- whis way callers can all assume there aren't others - ''' + """ # if an auth is in flight-- and not done-- just pass that back as the future to wait on - if hasattr(self, '_authenticate_future') and not self._authenticate_future.done(): + if ( + hasattr(self, "_authenticate_future") + and not self._authenticate_future.done() + ): future = self._authenticate_future else: future = salt.ext.tornado.concurrent.Future() @@ -571,16 +609,18 @@ class AsyncAuth(object): self.io_loop.add_callback(self._authenticate) if callback is not None: + def handle_future(future): response = future.result() self.io_loop.add_callback(callback, response) + future.add_done_callback(handle_future) return future @salt.ext.tornado.gen.coroutine def _authenticate(self): - ''' + """ Authenticate with the master, this method breaks the functional paradigm, it will update the master information from a fresh sign in, signing in can occur as often as needed to keep up with the @@ -588,16 +628,16 @@ class AsyncAuth(object): :rtype: Crypticle :returns: A crypticle used for encryption operations - ''' - acceptance_wait_time = self.opts['acceptance_wait_time'] - acceptance_wait_time_max = self.opts['acceptance_wait_time_max'] + """ + acceptance_wait_time = self.opts["acceptance_wait_time"] + acceptance_wait_time_max = self.opts["acceptance_wait_time_max"] if not acceptance_wait_time_max: acceptance_wait_time_max = acceptance_wait_time creds = None - with salt.transport.client.AsyncReqChannel.factory(self.opts, - crypt='clear', - io_loop=self.io_loop) as channel: + with salt.transport.client.AsyncReqChannel.factory( + self.opts, crypt="clear", io_loop=self.io_loop + ) as channel: error = None while True: try: @@ -605,58 +645,71 @@ class AsyncAuth(object): except SaltClientError as exc: error = exc break - if creds == 'retry': - if self.opts.get('detect_mode') is True: - error = SaltClientError('Detect mode is on') + if creds == "retry": + if self.opts.get("detect_mode") is True: + error = SaltClientError("Detect mode is on") break - if self.opts.get('caller'): + if self.opts.get("caller"): # We have a list of masters, so we should break # and try the next one in the list. - if self.opts.get('local_masters', None): - error = SaltClientError('Minion failed to authenticate' - ' with the master, has the ' - 'minion key been accepted?') + if self.opts.get("local_masters", None): + error = SaltClientError( + "Minion failed to authenticate" + " with the master, has the " + "minion key been accepted?" + ) break else: - print('Minion failed to authenticate with the master, ' - 'has the minion key been accepted?') + print( + "Minion failed to authenticate with the master, " + "has the minion key been accepted?" + ) sys.exit(2) if acceptance_wait_time: log.info( - 'Waiting %s seconds before retry.', acceptance_wait_time + "Waiting %s seconds before retry.", acceptance_wait_time ) yield salt.ext.tornado.gen.sleep(acceptance_wait_time) if acceptance_wait_time < acceptance_wait_time_max: acceptance_wait_time += acceptance_wait_time log.debug( - 'Authentication wait time is %s', acceptance_wait_time + "Authentication wait time is %s", acceptance_wait_time ) continue break - if not isinstance(creds, dict) or 'aes' not in creds: - if self.opts.get('detect_mode') is True: - error = SaltClientError('-|RETRY|-') + if not isinstance(creds, dict) or "aes" not in creds: + if self.opts.get("detect_mode") is True: + error = SaltClientError("-|RETRY|-") try: del AsyncAuth.creds_map[self.__key(self.opts)] except KeyError: pass if not error: - error = SaltClientError('Attempt to authenticate with the salt master failed') + error = SaltClientError( + "Attempt to authenticate with the salt master failed" + ) self._authenticate_future.set_exception(error) else: key = self.__key(self.opts) AsyncAuth.creds_map[key] = creds self._creds = creds - self._crypticle = Crypticle(self.opts, creds['aes']) - self._authenticate_future.set_result(True) # mark the sign-in as complete + self._crypticle = Crypticle(self.opts, creds["aes"]) + self._authenticate_future.set_result( + True + ) # mark the sign-in as complete # Notify the bus about creds change - if self.opts.get('auth_events') is True: - with salt.utils.event.get_event(self.opts.get('__role'), opts=self.opts, listen=False) as event: - event.fire_event({'key': key, 'creds': creds}, salt.utils.event.tagify(prefix='auth', suffix='creds')) + if self.opts.get("auth_events") is True: + with salt.utils.event.get_event( + self.opts.get("__role"), opts=self.opts, listen=False + ) as event: + event.fire_event( + {"key": key, "creds": creds}, + salt.utils.event.tagify(prefix="auth", suffix="creds"), + ) @salt.ext.tornado.gen.coroutine def sign_in(self, timeout=60, safe=True, tries=1, channel=None): - ''' + """ Send a sign in request to the master, sets the key information and returns a dict containing the master publish interface to bind to and the decrypted aes key for transport decryption. @@ -670,70 +723,68 @@ class AsyncAuth(object): :return: Return a string on failure indicating the reason for failure. On success, return a dictionary with the publication port and the shared AES key. - ''' + """ auth = {} - auth_timeout = self.opts.get('auth_timeout', None) + auth_timeout = self.opts.get("auth_timeout", None) if auth_timeout is not None: timeout = auth_timeout - auth_safemode = self.opts.get('auth_safemode', None) + auth_safemode = self.opts.get("auth_safemode", None) if auth_safemode is not None: safe = auth_safemode - auth_tries = self.opts.get('auth_tries', None) + auth_tries = self.opts.get("auth_tries", None) if auth_tries is not None: tries = auth_tries - m_pub_fn = os.path.join(self.opts['pki_dir'], self.mpub) + m_pub_fn = os.path.join(self.opts["pki_dir"], self.mpub) - auth['master_uri'] = self.opts['master_uri'] + auth["master_uri"] = self.opts["master_uri"] close_channel = False if not channel: close_channel = True - channel = salt.transport.client.AsyncReqChannel.factory(self.opts, - crypt='clear', - io_loop=self.io_loop) + channel = salt.transport.client.AsyncReqChannel.factory( + self.opts, crypt="clear", io_loop=self.io_loop + ) sign_in_payload = self.minion_sign_in_payload() try: - payload = yield channel.send( - sign_in_payload, - tries=tries, - timeout=timeout - ) + payload = yield channel.send(sign_in_payload, tries=tries, timeout=timeout) except SaltReqTimeoutError as e: if safe: - log.warning('SaltReqTimeoutError: %s', e) - raise salt.ext.tornado.gen.Return('retry') - if self.opts.get('detect_mode') is True: - raise salt.ext.tornado.gen.Return('retry') + log.warning("SaltReqTimeoutError: %s", e) + raise salt.ext.tornado.gen.Return("retry") + if self.opts.get("detect_mode") is True: + raise salt.ext.tornado.gen.Return("retry") else: - raise SaltClientError('Attempt to authenticate with the salt master failed with timeout error') + raise SaltClientError( + "Attempt to authenticate with the salt master failed with timeout error" + ) finally: if close_channel: channel.close() if not isinstance(payload, dict): - log.error('Sign-in attempt failed: %s', payload) + log.error("Sign-in attempt failed: %s", payload) raise salt.ext.tornado.gen.Return(False) - if 'load' in payload: - if 'ret' in payload['load']: - if not payload['load']['ret']: - if self.opts['rejected_retry']: + if "load" in payload: + if "ret" in payload["load"]: + if not payload["load"]["ret"]: + if self.opts["rejected_retry"]: log.error( - 'The Salt Master has rejected this minion\'s public ' - 'key.\nTo repair this issue, delete the public key ' - 'for this minion on the Salt Master.\nThe Salt ' - 'Minion will attempt to to re-authenicate.' + "The Salt Master has rejected this minion's public " + "key.\nTo repair this issue, delete the public key " + "for this minion on the Salt Master.\nThe Salt " + "Minion will attempt to to re-authenicate." ) - raise salt.ext.tornado.gen.Return('retry') + raise salt.ext.tornado.gen.Return("retry") else: log.critical( - 'The Salt Master has rejected this minion\'s public ' - 'key!\nTo repair this issue, delete the public key ' - 'for this minion on the Salt Master and restart this ' - 'minion.\nOr restart the Salt Master in open mode to ' - 'clean out the keys. The Salt Minion will now exit.' + "The Salt Master has rejected this minion's public " + "key!\nTo repair this issue, delete the public key " + "for this minion on the Salt Master and restart this " + "minion.\nOr restart the Salt Master in open mode to " + "clean out the keys. The Salt Minion will now exit." ) # Add a random sleep here for systems that are using a # a service manager to immediately restart the service @@ -741,105 +792,123 @@ class AsyncAuth(object): time.sleep(random.randint(10, 20)) sys.exit(salt.defaults.exitcodes.EX_NOPERM) # has the master returned that its maxed out with minions? - elif payload['load']['ret'] == 'full': - raise salt.ext.tornado.gen.Return('full') + elif payload["load"]["ret"] == "full": + raise salt.ext.tornado.gen.Return("full") else: log.error( - 'The Salt Master has cached the public key for this ' - 'node, this salt minion will wait for %s seconds ' - 'before attempting to re-authenticate', - self.opts['acceptance_wait_time'] + "The Salt Master has cached the public key for this " + "node, this salt minion will wait for %s seconds " + "before attempting to re-authenticate", + self.opts["acceptance_wait_time"], ) - raise salt.ext.tornado.gen.Return('retry') - auth['aes'] = self.verify_master(payload, master_pub='token' in sign_in_payload) - if not auth['aes']: + raise salt.ext.tornado.gen.Return("retry") + auth["aes"] = self.verify_master(payload, master_pub="token" in sign_in_payload) + if not auth["aes"]: log.critical( - 'The Salt Master server\'s public key did not authenticate!\n' - 'The master may need to be updated if it is a version of Salt ' - 'lower than %s, or\n' - 'If you are confident that you are connecting to a valid Salt ' - 'Master, then remove the master public key and restart the ' - 'Salt Minion.\nThe master public key can be found ' - 'at:\n%s', salt.version.__version__, m_pub_fn + "The Salt Master server's public key did not authenticate!\n" + "The master may need to be updated if it is a version of Salt " + "lower than %s, or\n" + "If you are confident that you are connecting to a valid Salt " + "Master, then remove the master public key and restart the " + "Salt Minion.\nThe master public key can be found " + "at:\n%s", + salt.version.__version__, + m_pub_fn, + ) + raise SaltClientError("Invalid master key") + if self.opts.get("syndic_master", False): # Is syndic + syndic_finger = self.opts.get( + "syndic_finger", self.opts.get("master_finger", False) ) - raise SaltClientError('Invalid master key') - if self.opts.get('syndic_master', False): # Is syndic - syndic_finger = self.opts.get('syndic_finger', self.opts.get('master_finger', False)) if syndic_finger: - if salt.utils.crypt.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != syndic_finger: + if ( + salt.utils.crypt.pem_finger( + m_pub_fn, sum_type=self.opts["hash_type"] + ) + != syndic_finger + ): self._finger_fail(syndic_finger, m_pub_fn) else: - if self.opts.get('master_finger', False): - if salt.utils.crypt.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != self.opts['master_finger']: - self._finger_fail(self.opts['master_finger'], m_pub_fn) - auth['publish_port'] = payload['publish_port'] + if self.opts.get("master_finger", False): + if ( + salt.utils.crypt.pem_finger( + m_pub_fn, sum_type=self.opts["hash_type"] + ) + != self.opts["master_finger"] + ): + self._finger_fail(self.opts["master_finger"], m_pub_fn) + auth["publish_port"] = payload["publish_port"] raise salt.ext.tornado.gen.Return(auth) def get_keys(self): - ''' + """ Return keypair object for the minion. :rtype: Crypto.PublicKey.RSA._RSAobj :return: The RSA keypair - ''' + """ # Make sure all key parent directories are accessible - user = self.opts.get('user', 'root') - salt.utils.verify.check_path_traversal(self.opts['pki_dir'], user) + user = self.opts.get("user", "root") + salt.utils.verify.check_path_traversal(self.opts["pki_dir"], user) if not os.path.exists(self.rsa_path): - log.info('Generating keys: %s', self.opts['pki_dir']) - gen_keys(self.opts['pki_dir'], - 'minion', - self.opts['keysize'], - self.opts.get('user')) + log.info("Generating keys: %s", self.opts["pki_dir"]) + gen_keys( + self.opts["pki_dir"], + "minion", + self.opts["keysize"], + self.opts.get("user"), + ) key = get_rsa_key(self.rsa_path, None) - log.debug('Loaded minion key: %s', self.rsa_path) + log.debug("Loaded minion key: %s", self.rsa_path) return key def gen_token(self, clear_tok): - ''' + """ Encrypt a string with the minion private key to verify identity with the master. :param str clear_tok: A plaintext token to encrypt :return: Encrypted token :rtype: str - ''' + """ return private_encrypt(self.get_keys(), clear_tok) def minion_sign_in_payload(self): - ''' + """ Generates the payload used to authenticate with the master server. This payload consists of the passed in id_ and the ssh public key to encrypt the AES key sent back from the master. :return: Payload dictionary :rtype: dict - ''' + """ payload = {} - payload['cmd'] = '_auth' - payload['id'] = self.opts['id'] - if 'autosign_grains' in self.opts: + payload["cmd"] = "_auth" + payload["id"] = self.opts["id"] + if "autosign_grains" in self.opts: autosign_grains = {} - for grain in self.opts['autosign_grains']: - autosign_grains[grain] = self.opts['grains'].get(grain, None) - payload['autosign_grains'] = autosign_grains + for grain in self.opts["autosign_grains"]: + autosign_grains[grain] = self.opts["grains"].get(grain, None) + payload["autosign_grains"] = autosign_grains try: - pubkey_path = os.path.join(self.opts['pki_dir'], self.mpub) + pubkey_path = os.path.join(self.opts["pki_dir"], self.mpub) pub = get_rsa_pub_key(pubkey_path) if HAS_M2: - payload['token'] = pub.public_encrypt(self.token, RSA.pkcs1_oaep_padding) + payload["token"] = pub.public_encrypt( + self.token, RSA.pkcs1_oaep_padding + ) else: cipher = PKCS1_OAEP.new(pub) - payload['token'] = cipher.encrypt(self.token) + payload["token"] = cipher.encrypt(self.token) except Exception: # pylint: disable=broad-except pass with salt.utils.files.fopen(self.pub_path) as f: - payload['pub'] = f.read() + payload["pub"] = f.read() return payload def decrypt_aes(self, payload, master_pub=True): - ''' + """ This function is used to decrypt the AES seed phrase returned from the master server. The seed phrase is decrypted with the SSH RSA host key. @@ -860,123 +929,121 @@ class AsyncAuth(object): :rtype: str :return: The decrypted AES seed key - ''' - if self.opts.get('auth_trb', False): - log.warning('Auth Called: %s', ''.join(traceback.format_stack())) + """ + if self.opts.get("auth_trb", False): + log.warning("Auth Called: %s", "".join(traceback.format_stack())) else: - log.debug('Decrypting the current master AES key') + log.debug("Decrypting the current master AES key") key = self.get_keys() if HAS_M2: - key_str = key.private_decrypt(payload['aes'], - RSA.pkcs1_oaep_padding) + key_str = key.private_decrypt(payload["aes"], RSA.pkcs1_oaep_padding) else: cipher = PKCS1_OAEP.new(key) - key_str = cipher.decrypt(payload['aes']) - if 'sig' in payload: - m_path = os.path.join(self.opts['pki_dir'], self.mpub) + key_str = cipher.decrypt(payload["aes"]) + if "sig" in payload: + m_path = os.path.join(self.opts["pki_dir"], self.mpub) if os.path.exists(m_path): try: mkey = get_rsa_pub_key(m_path) except Exception: # pylint: disable=broad-except - return '', '' + return "", "" digest = hashlib.sha256(key_str).hexdigest() if six.PY3: digest = salt.utils.stringutils.to_bytes(digest) if HAS_M2: - m_digest = public_decrypt(mkey, payload['sig']) + m_digest = public_decrypt(mkey, payload["sig"]) else: - m_digest = public_decrypt(mkey.publickey(), payload['sig']) + m_digest = public_decrypt(mkey.publickey(), payload["sig"]) if m_digest != digest: - return '', '' + return "", "" else: - return '', '' + return "", "" if six.PY3: key_str = salt.utils.stringutils.to_str(key_str) - if '_|-' in key_str: - return key_str.split('_|-') + if "_|-" in key_str: + return key_str.split("_|-") else: - if 'token' in payload: + if "token" in payload: if HAS_M2: - token = key.private_decrypt(payload['token'], - RSA.pkcs1_oaep_padding) + token = key.private_decrypt( + payload["token"], RSA.pkcs1_oaep_padding + ) else: - token = cipher.decrypt(payload['token']) + token = cipher.decrypt(payload["token"]) return key_str, token elif not master_pub: - return key_str, '' - return '', '' + return key_str, "" + return "", "" def verify_pubkey_sig(self, message, sig): - ''' + """ Wraps the verify_signature method so we have additional checks. :rtype: bool :return: Success or failure of public key verification - ''' - if self.opts['master_sign_key_name']: - path = os.path.join(self.opts['pki_dir'], - self.opts['master_sign_key_name'] + '.pub') + """ + if self.opts["master_sign_key_name"]: + path = os.path.join( + self.opts["pki_dir"], self.opts["master_sign_key_name"] + ".pub" + ) if os.path.isfile(path): - res = verify_signature(path, - message, - binascii.a2b_base64(sig)) + res = verify_signature(path, message, binascii.a2b_base64(sig)) else: log.error( - 'Verification public key %s does not exist. You need to ' - 'copy it from the master to the minions pki directory', - os.path.basename(path) + "Verification public key %s does not exist. You need to " + "copy it from the master to the minions pki directory", + os.path.basename(path), ) return False if res: log.debug( - 'Successfully verified signature of master public key ' - 'with verification public key %s', - self.opts['master_sign_key_name'] + '.pub' + "Successfully verified signature of master public key " + "with verification public key %s", + self.opts["master_sign_key_name"] + ".pub", ) return True else: - log.debug('Failed to verify signature of public key') + log.debug("Failed to verify signature of public key") return False else: log.error( - 'Failed to verify the signature of the message because the ' - 'verification key-pairs name is not defined. Please make ' - 'sure that master_sign_key_name is defined.' + "Failed to verify the signature of the message because the " + "verification key-pairs name is not defined. Please make " + "sure that master_sign_key_name is defined." ) return False def verify_signing_master(self, payload): try: - if self.verify_pubkey_sig(payload['pub_key'], - payload['pub_sig']): + if self.verify_pubkey_sig(payload["pub_key"], payload["pub_sig"]): log.info( - 'Received signed and verified master pubkey from master %s', - self.opts['master'] + "Received signed and verified master pubkey from master %s", + self.opts["master"], ) - m_pub_fn = os.path.join(self.opts['pki_dir'], self.mpub) - uid = salt.utils.user.get_uid(self.opts.get('user', None)) - with salt.utils.files.fpopen(m_pub_fn, 'wb+', uid=uid) as wfh: - wfh.write(salt.utils.stringutils.to_bytes(payload['pub_key'])) + m_pub_fn = os.path.join(self.opts["pki_dir"], self.mpub) + uid = salt.utils.user.get_uid(self.opts.get("user", None)) + with salt.utils.files.fpopen(m_pub_fn, "wb+", uid=uid) as wfh: + wfh.write(salt.utils.stringutils.to_bytes(payload["pub_key"])) return True else: log.error( - 'Received signed public-key from master %s but signature ' - 'verification failed!', self.opts['master'] + "Received signed public-key from master %s but signature " + "verification failed!", + self.opts["master"], ) return False except Exception as sign_exc: # pylint: disable=broad-except log.error( - 'There was an error while verifying the masters public-key ' - 'signature' + "There was an error while verifying the masters public-key " "signature" ) raise Exception(sign_exc) def check_auth_deps(self, payload): - ''' + """ Checks if both master and minion either sign (master) and verify (minion). If one side does not, it should fail. @@ -986,31 +1053,35 @@ class AsyncAuth(object): 'publish_port': The TCP port which published the message 'token': The encrypted token used to verify the message. 'pub_key': The RSA public key of the sender. - ''' + """ # master and minion sign and verify - if 'pub_sig' in payload and self.opts['verify_master_pubkey_sign']: + if "pub_sig" in payload and self.opts["verify_master_pubkey_sign"]: return True # master and minion do NOT sign and do NOT verify - elif 'pub_sig' not in payload and not self.opts['verify_master_pubkey_sign']: + elif "pub_sig" not in payload and not self.opts["verify_master_pubkey_sign"]: return True # master signs, but minion does NOT verify - elif 'pub_sig' in payload and not self.opts['verify_master_pubkey_sign']: - log.error('The masters sent its public-key signature, but signature ' - 'verification is not enabled on the minion. Either enable ' - 'signature verification on the minion or disable signing ' - 'the public key on the master!') + elif "pub_sig" in payload and not self.opts["verify_master_pubkey_sign"]: + log.error( + "The masters sent its public-key signature, but signature " + "verification is not enabled on the minion. Either enable " + "signature verification on the minion or disable signing " + "the public key on the master!" + ) return False # master does NOT sign but minion wants to verify - elif 'pub_sig' not in payload and self.opts['verify_master_pubkey_sign']: - log.error('The master did not send its public-key signature, but ' - 'signature verification is enabled on the minion. Either ' - 'disable signature verification on the minion or enable ' - 'signing the public on the master!') + elif "pub_sig" not in payload and self.opts["verify_master_pubkey_sign"]: + log.error( + "The master did not send its public-key signature, but " + "signature verification is enabled on the minion. Either " + "disable signature verification on the minion or enable " + "signing the public on the master!" + ) return False def extract_aes(self, payload, master_pub=True): - ''' + """ Return the AES key received from the master after the minion has been successfully authenticated. @@ -1023,27 +1094,23 @@ class AsyncAuth(object): :rtype: str :return: The shared AES key received from the master. - ''' + """ if master_pub: try: aes, token = self.decrypt_aes(payload, master_pub) if token != self.token: - log.error( - 'The master failed to decrypt the random minion token' - ) - return '' + log.error("The master failed to decrypt the random minion token") + return "" except Exception: # pylint: disable=broad-except - log.error( - 'The master failed to decrypt the random minion token' - ) - return '' + log.error("The master failed to decrypt the random minion token") + return "" return aes else: aes, token = self.decrypt_aes(payload, master_pub) return aes def verify_master(self, payload, master_pub=True): - ''' + """ Verify that the master is the same one that was previously accepted. :param dict payload: The incoming payload. This is a dictionary which may have the following keys: @@ -1057,111 +1124,114 @@ class AsyncAuth(object): :rtype: str :return: An empty string on verification failure. On success, the decrypted AES message in the payload. - ''' - m_pub_fn = os.path.join(self.opts['pki_dir'], self.mpub) + """ + m_pub_fn = os.path.join(self.opts["pki_dir"], self.mpub) m_pub_exists = os.path.isfile(m_pub_fn) - if m_pub_exists and master_pub and not self.opts['open_mode']: + if m_pub_exists and master_pub and not self.opts["open_mode"]: with salt.utils.files.fopen(m_pub_fn) as fp_: local_master_pub = fp_.read() - if payload['pub_key'].replace('\n', '').replace('\r', '') != \ - local_master_pub.replace('\n', '').replace('\r', ''): + if payload["pub_key"].replace("\n", "").replace( + "\r", "" + ) != local_master_pub.replace("\n", "").replace("\r", ""): if not self.check_auth_deps(payload): - return '' + return "" - if self.opts['verify_master_pubkey_sign']: + if self.opts["verify_master_pubkey_sign"]: if self.verify_signing_master(payload): return self.extract_aes(payload, master_pub=False) else: - return '' + return "" else: # This is not the last master we connected to log.error( - 'The master key has changed, the salt master could ' - 'have been subverted, verify salt master\'s public ' - 'key' + "The master key has changed, the salt master could " + "have been subverted, verify salt master's public " + "key" ) - return '' + return "" else: if not self.check_auth_deps(payload): - return '' + return "" # verify the signature of the pubkey even if it has # not changed compared with the one we already have - if self.opts['always_verify_signature']: + if self.opts["always_verify_signature"]: if self.verify_signing_master(payload): return self.extract_aes(payload) else: log.error( - 'The masters public could not be verified. Is the ' - 'verification pubkey %s up to date?', - self.opts['master_sign_key_name'] + '.pub' + "The masters public could not be verified. Is the " + "verification pubkey %s up to date?", + self.opts["master_sign_key_name"] + ".pub", ) - return '' + return "" else: return self.extract_aes(payload) else: if not self.check_auth_deps(payload): - return '' + return "" # verify the masters pubkey signature if the minion # has not received any masters pubkey before - if self.opts['verify_master_pubkey_sign']: + if self.opts["verify_master_pubkey_sign"]: if self.verify_signing_master(payload): return self.extract_aes(payload, master_pub=False) else: - return '' + return "" else: if not m_pub_exists: # the minion has not received any masters pubkey yet, write # the newly received pubkey to minion_master.pub - with salt.utils.files.fopen(m_pub_fn, 'wb+') as fp_: - fp_.write(salt.utils.stringutils.to_bytes(payload['pub_key'])) + with salt.utils.files.fopen(m_pub_fn, "wb+") as fp_: + fp_.write(salt.utils.stringutils.to_bytes(payload["pub_key"])) return self.extract_aes(payload, master_pub=False) def _finger_fail(self, finger, master_key): log.critical( - 'The specified fingerprint in the master configuration ' - 'file:\n%s\nDoes not match the authenticating master\'s ' - 'key:\n%s\nVerify that the configured fingerprint ' - 'matches the fingerprint of the correct master and that ' - 'this minion is not subject to a man-in-the-middle attack.', + "The specified fingerprint in the master configuration " + "file:\n%s\nDoes not match the authenticating master's " + "key:\n%s\nVerify that the configured fingerprint " + "matches the fingerprint of the correct master and that " + "this minion is not subject to a man-in-the-middle attack.", finger, - salt.utils.crypt.pem_finger(master_key, sum_type=self.opts['hash_type']) + salt.utils.crypt.pem_finger(master_key, sum_type=self.opts["hash_type"]), ) sys.exit(42) # TODO: remove, we should just return a sync wrapper of AsyncAuth class SAuth(AsyncAuth): - ''' + """ Set up an object to maintain authentication with the salt master - ''' + """ + # This class is only a singleton per minion/master pair instances = weakref.WeakValueDictionary() def __new__(cls, opts, io_loop=None): - ''' + """ Only create one instance of SAuth per __key() - ''' + """ key = cls.__key(opts) auth = SAuth.instances.get(key) if auth is None: - log.debug('Initializing new SAuth for %s', key) + log.debug("Initializing new SAuth for %s", key) auth = object.__new__(cls) auth.__singleton_init__(opts) SAuth.instances[key] = auth else: - log.debug('Re-using SAuth for %s', key) + log.debug("Re-using SAuth for %s", key) return auth @classmethod def __key(cls, opts, io_loop=None): - return (opts['pki_dir'], # where the keys are stored - opts['id'], # minion ID - opts['master_uri'], # master ID - ) + return ( + opts["pki_dir"], # where the keys are stored + opts["id"], # minion ID + opts["master_uri"], # master ID + ) # has to remain empty for singletons, since __init__ will *always* be called def __init__(self, opts, io_loop=None): @@ -1169,44 +1239,46 @@ class SAuth(AsyncAuth): # an init for the singleton instance to call def __singleton_init__(self, opts, io_loop=None): - ''' + """ Init an Auth instance :param dict opts: Options for this server :return: Auth instance :rtype: Auth - ''' + """ self.opts = opts if six.PY2: self.token = Crypticle.generate_key_string() else: - self.token = salt.utils.stringutils.to_bytes(Crypticle.generate_key_string()) + self.token = salt.utils.stringutils.to_bytes( + Crypticle.generate_key_string() + ) self.serial = salt.payload.Serial(self.opts) - self.pub_path = os.path.join(self.opts['pki_dir'], 'minion.pub') - self.rsa_path = os.path.join(self.opts['pki_dir'], 'minion.pem') - if 'syndic_master' in self.opts: - self.mpub = 'syndic_master.pub' - elif 'alert_master' in self.opts: - self.mpub = 'monitor_master.pub' + self.pub_path = os.path.join(self.opts["pki_dir"], "minion.pub") + self.rsa_path = os.path.join(self.opts["pki_dir"], "minion.pem") + if "syndic_master" in self.opts: + self.mpub = "syndic_master.pub" + elif "alert_master" in self.opts: + self.mpub = "monitor_master.pub" else: - self.mpub = 'minion_master.pub' + self.mpub = "minion_master.pub" if not os.path.isfile(self.pub_path): self.get_keys() @property def creds(self): - if not hasattr(self, '_creds'): + if not hasattr(self, "_creds"): self.authenticate() return self._creds @property def crypticle(self): - if not hasattr(self, '_crypticle'): + if not hasattr(self, "_crypticle"): self.authenticate() return self._crypticle def authenticate(self, _=None): # TODO: remove unused var - ''' + """ Authenticate with the master, this method breaks the functional paradigm, it will update the master information from a fresh sign in, signing in can occur as often as needed to keep up with the @@ -1214,40 +1286,50 @@ class SAuth(AsyncAuth): :rtype: Crypticle :returns: A crypticle used for encryption operations - ''' - acceptance_wait_time = self.opts['acceptance_wait_time'] - acceptance_wait_time_max = self.opts['acceptance_wait_time_max'] + """ + acceptance_wait_time = self.opts["acceptance_wait_time"] + acceptance_wait_time_max = self.opts["acceptance_wait_time_max"] if not acceptance_wait_time_max: acceptance_wait_time_max = acceptance_wait_time - with salt.transport.client.ReqChannel.factory(self.opts, crypt='clear') as channel: + with salt.transport.client.ReqChannel.factory( + self.opts, crypt="clear" + ) as channel: while True: creds = self.sign_in(channel=channel) - if creds == 'retry': - if self.opts.get('caller'): + if creds == "retry": + if self.opts.get("caller"): # We have a list of masters, so we should break # and try the next one in the list. - if self.opts.get('local_masters', None): - error = SaltClientError('Minion failed to authenticate' - ' with the master, has the ' - 'minion key been accepted?') + if self.opts.get("local_masters", None): + error = SaltClientError( + "Minion failed to authenticate" + " with the master, has the " + "minion key been accepted?" + ) break else: - print('Minion failed to authenticate with the master, ' - 'has the minion key been accepted?') + print( + "Minion failed to authenticate with the master, " + "has the minion key been accepted?" + ) sys.exit(2) if acceptance_wait_time: - log.info('Waiting %s seconds before retry.', acceptance_wait_time) + log.info( + "Waiting %s seconds before retry.", acceptance_wait_time + ) time.sleep(acceptance_wait_time) if acceptance_wait_time < acceptance_wait_time_max: acceptance_wait_time += acceptance_wait_time - log.debug('Authentication wait time is %s', acceptance_wait_time) + log.debug( + "Authentication wait time is %s", acceptance_wait_time + ) continue break self._creds = creds - self._crypticle = Crypticle(self.opts, creds['aes']) + self._crypticle = Crypticle(self.opts, creds["aes"]) def sign_in(self, timeout=60, safe=True, tries=1, channel=None): - ''' + """ Send a sign in request to the master, sets the key information and returns a dict containing the master publish interface to bind to and the decrypted aes key for transport decryption. @@ -1261,112 +1343,125 @@ class SAuth(AsyncAuth): :return: Return a string on failure indicating the reason for failure. On success, return a dictionary with the publication port and the shared AES key. - ''' + """ auth = {} - auth_timeout = self.opts.get('auth_timeout', None) + auth_timeout = self.opts.get("auth_timeout", None) if auth_timeout is not None: timeout = auth_timeout - auth_safemode = self.opts.get('auth_safemode', None) + auth_safemode = self.opts.get("auth_safemode", None) if auth_safemode is not None: safe = auth_safemode - auth_tries = self.opts.get('auth_tries', None) + auth_tries = self.opts.get("auth_tries", None) if auth_tries is not None: tries = auth_tries - m_pub_fn = os.path.join(self.opts['pki_dir'], self.mpub) + m_pub_fn = os.path.join(self.opts["pki_dir"], self.mpub) - auth['master_uri'] = self.opts['master_uri'] + auth["master_uri"] = self.opts["master_uri"] close_channel = False if not channel: close_channel = True - channel = salt.transport.client.ReqChannel.factory(self.opts, crypt='clear') + channel = salt.transport.client.ReqChannel.factory(self.opts, crypt="clear") sign_in_payload = self.minion_sign_in_payload() try: - payload = channel.send( - sign_in_payload, - tries=tries, - timeout=timeout - ) + payload = channel.send(sign_in_payload, tries=tries, timeout=timeout) except SaltReqTimeoutError as e: if safe: - log.warning('SaltReqTimeoutError: %s', e) - return 'retry' - raise SaltClientError('Attempt to authenticate with the salt master failed with timeout error') + log.warning("SaltReqTimeoutError: %s", e) + return "retry" + raise SaltClientError( + "Attempt to authenticate with the salt master failed with timeout error" + ) finally: if close_channel: channel.close() - if 'load' in payload: - if 'ret' in payload['load']: - if not payload['load']['ret']: - if self.opts['rejected_retry']: + if "load" in payload: + if "ret" in payload["load"]: + if not payload["load"]["ret"]: + if self.opts["rejected_retry"]: log.error( - 'The Salt Master has rejected this minion\'s public ' - 'key.\nTo repair this issue, delete the public key ' - 'for this minion on the Salt Master.\nThe Salt ' - 'Minion will attempt to to re-authenicate.' + "The Salt Master has rejected this minion's public " + "key.\nTo repair this issue, delete the public key " + "for this minion on the Salt Master.\nThe Salt " + "Minion will attempt to to re-authenicate." ) - return 'retry' + return "retry" else: log.critical( - 'The Salt Master has rejected this minion\'s public ' - 'key!\nTo repair this issue, delete the public key ' - 'for this minion on the Salt Master and restart this ' - 'minion.\nOr restart the Salt Master in open mode to ' - 'clean out the keys. The Salt Minion will now exit.' + "The Salt Master has rejected this minion's public " + "key!\nTo repair this issue, delete the public key " + "for this minion on the Salt Master and restart this " + "minion.\nOr restart the Salt Master in open mode to " + "clean out the keys. The Salt Minion will now exit." ) sys.exit(salt.defaults.exitcodes.EX_NOPERM) # has the master returned that its maxed out with minions? - elif payload['load']['ret'] == 'full': - return 'full' + elif payload["load"]["ret"] == "full": + return "full" else: log.error( - 'The Salt Master has cached the public key for this ' - 'node. If this is the first time connecting to this ' - 'master then this key may need to be accepted using ' - '\'salt-key -a %s\' on the salt master. This salt ' - 'minion will wait for %s seconds before attempting ' - 'to re-authenticate.', - self.opts['id'], self.opts['acceptance_wait_time'] + "The Salt Master has cached the public key for this " + "node. If this is the first time connecting to this " + "master then this key may need to be accepted using " + "'salt-key -a %s' on the salt master. This salt " + "minion will wait for %s seconds before attempting " + "to re-authenticate.", + self.opts["id"], + self.opts["acceptance_wait_time"], ) - return 'retry' - auth['aes'] = self.verify_master(payload, master_pub='token' in sign_in_payload) - if not auth['aes']: + return "retry" + auth["aes"] = self.verify_master(payload, master_pub="token" in sign_in_payload) + if not auth["aes"]: log.critical( - 'The Salt Master server\'s public key did not authenticate!\n' - 'The master may need to be updated if it is a version of Salt ' - 'lower than %s, or\n' - 'If you are confident that you are connecting to a valid Salt ' - 'Master, then remove the master public key and restart the ' - 'Salt Minion.\nThe master public key can be found ' - 'at:\n%s', salt.version.__version__, m_pub_fn + "The Salt Master server's public key did not authenticate!\n" + "The master may need to be updated if it is a version of Salt " + "lower than %s, or\n" + "If you are confident that you are connecting to a valid Salt " + "Master, then remove the master public key and restart the " + "Salt Minion.\nThe master public key can be found " + "at:\n%s", + salt.version.__version__, + m_pub_fn, ) sys.exit(42) - if self.opts.get('syndic_master', False): # Is syndic - syndic_finger = self.opts.get('syndic_finger', self.opts.get('master_finger', False)) + if self.opts.get("syndic_master", False): # Is syndic + syndic_finger = self.opts.get( + "syndic_finger", self.opts.get("master_finger", False) + ) if syndic_finger: - if salt.utils.crypt.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != syndic_finger: + if ( + salt.utils.crypt.pem_finger( + m_pub_fn, sum_type=self.opts["hash_type"] + ) + != syndic_finger + ): self._finger_fail(syndic_finger, m_pub_fn) else: - if self.opts.get('master_finger', False): - if salt.utils.crypt.pem_finger(m_pub_fn, sum_type=self.opts['hash_type']) != self.opts['master_finger']: - self._finger_fail(self.opts['master_finger'], m_pub_fn) - auth['publish_port'] = payload['publish_port'] + if self.opts.get("master_finger", False): + if ( + salt.utils.crypt.pem_finger( + m_pub_fn, sum_type=self.opts["hash_type"] + ) + != self.opts["master_finger"] + ): + self._finger_fail(self.opts["master_finger"], m_pub_fn) + auth["publish_port"] = payload["publish_port"] return auth class Crypticle(object): - ''' + """ Authenticated encryption class Encryption algorithm: AES-CBC Signing algorithm: HMAC-SHA256 - ''' + """ - PICKLE_PAD = b'pickle::' + PICKLE_PAD = b"pickle::" AES_BLOCK_SIZE = 16 SIG_SIZE = hashlib.sha256().digest_size @@ -1381,23 +1476,23 @@ class Crypticle(object): key = os.urandom(key_size // 8 + cls.SIG_SIZE) b64key = base64.b64encode(key) if six.PY3: - b64key = b64key.decode('utf-8') + b64key = b64key.decode("utf-8") # Return data must be a base64-encoded string, not a unicode type - return b64key.replace('\n', '') + return b64key.replace("\n", "") @classmethod def extract_keys(cls, key_string, key_size): if six.PY2: - key = key_string.decode('base64') + key = key_string.decode("base64") else: key = salt.utils.stringutils.to_bytes(base64.b64decode(key_string)) - assert len(key) == key_size / 8 + cls.SIG_SIZE, 'invalid key' - return key[:-cls.SIG_SIZE], key[-cls.SIG_SIZE:] + assert len(key) == key_size / 8 + cls.SIG_SIZE, "invalid key" + return key[: -cls.SIG_SIZE], key[-cls.SIG_SIZE :] def encrypt(self, data): - ''' + """ encrypt data with AES-CBC and sign it with HMAC-SHA256 - ''' + """ aes_key, hmac_key = self.keys pad = self.AES_BLOCK_SIZE - len(data) % self.AES_BLOCK_SIZE if six.PY2: @@ -1406,7 +1501,9 @@ class Crypticle(object): data = data + salt.utils.stringutils.to_bytes(pad * chr(pad)) iv_bytes = os.urandom(self.AES_BLOCK_SIZE) if HAS_M2: - cypher = EVP.Cipher(alg='aes_192_cbc', key=aes_key, iv=iv_bytes, op=1, padding=False) + cypher = EVP.Cipher( + alg="aes_192_cbc", key=aes_key, iv=iv_bytes, op=1, padding=False + ) encr = cypher.update(data) encr += cypher.final() else: @@ -1417,18 +1514,18 @@ class Crypticle(object): return data + sig def decrypt(self, data): - ''' + """ verify HMAC-SHA256 signature and decrypt data with AES-CBC - ''' + """ aes_key, hmac_key = self.keys - sig = data[-self.SIG_SIZE:] - data = data[:-self.SIG_SIZE] + sig = data[-self.SIG_SIZE :] + data = data[: -self.SIG_SIZE] if six.PY3 and not isinstance(data, bytes): data = salt.utils.stringutils.to_bytes(data) mac_bytes = hmac.new(hmac_key, data, hashlib.sha256).digest() if len(mac_bytes) != len(sig): - log.debug('Failed to authenticate message') - raise AuthenticationError('message authentication failed') + log.debug("Failed to authenticate message") + raise AuthenticationError("message authentication failed") result = 0 if six.PY2: @@ -1438,35 +1535,37 @@ class Crypticle(object): for zipped_x, zipped_y in zip(mac_bytes, sig): result |= zipped_x ^ zipped_y if result != 0: - log.debug('Failed to authenticate message') - raise AuthenticationError('message authentication failed') - iv_bytes = data[:self.AES_BLOCK_SIZE] - data = data[self.AES_BLOCK_SIZE:] + log.debug("Failed to authenticate message") + raise AuthenticationError("message authentication failed") + iv_bytes = data[: self.AES_BLOCK_SIZE] + data = data[self.AES_BLOCK_SIZE :] if HAS_M2: - cypher = EVP.Cipher(alg='aes_192_cbc', key=aes_key, iv=iv_bytes, op=0, padding=False) + cypher = EVP.Cipher( + alg="aes_192_cbc", key=aes_key, iv=iv_bytes, op=0, padding=False + ) encr = cypher.update(data) data = encr + cypher.final() else: cypher = AES.new(aes_key, AES.MODE_CBC, iv_bytes) data = cypher.decrypt(data) if six.PY2: - return data[:-ord(data[-1])] + return data[: -ord(data[-1])] else: - return data[:-data[-1]] + return data[: -data[-1]] def dumps(self, obj): - ''' + """ Serialize and encrypt a python object - ''' + """ return self.encrypt(self.PICKLE_PAD + self.serial.dumps(obj)) def loads(self, data, raw=False): - ''' + """ Decrypt and un-serialize a python object - ''' + """ data = self.decrypt(data) # simple integrity check to verify that we got meaningful data if not data.startswith(self.PICKLE_PAD): return {} - load = self.serial.loads(data[len(self.PICKLE_PAD):], raw=raw) + load = self.serial.loads(data[len(self.PICKLE_PAD) :], raw=raw) return load diff --git a/salt/daemons/__init__.py b/salt/daemons/__init__.py index 642100b6633..bb6a646463a 100644 --- a/salt/daemons/__init__.py +++ b/salt/daemons/__init__.py @@ -1,21 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" The daemons package is used to store implementations of the Salt Master and Minion enabling different transports. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + +import logging + # Import Python Libs import sys +# Import Salt Libs +from salt.ext import six + try: from collections.abc import Iterable, Sequence, Mapping except ImportError: + # pylint: disable=no-name-in-module from collections import Iterable, Sequence, Mapping -import logging + # pylint: enable=no-name-in-module -# Import Salt Libs -from salt.ext import six log = logging.getLogger(__name__) @@ -45,8 +50,8 @@ def is_non_string_sequence(obj): return not isinstance(obj, six.string_types) and isinstance(obj, Sequence) -def extract_masters(opts, masters='master', port=None, raise_if_empty=True): - ''' +def extract_masters(opts, masters="master", port=None, raise_if_empty=True): + """ Parses opts and generates a list of master (host,port) addresses. By default looks for list of masters in opts['master'] and uses opts['master_port'] as the default port when otherwise not provided. @@ -135,11 +140,11 @@ def extract_masters(opts, masters='master', port=None, raise_if_empty=True): external: we.example.com - they.example.com - ''' + """ if port is not None: master_port = opts.get(port) else: - master_port = opts.get('master_port') + master_port = opts.get("master_port") try: master_port = int(master_port) except ValueError: @@ -163,30 +168,30 @@ def extract_masters(opts, masters='master', port=None, raise_if_empty=True): if is_non_string_sequence(entries): # multiple master addresses provided for entry in entries: if isinstance(entry, Mapping): # mapping - external = entry.get('external', '') - internal = entry.get('internal', '') + external = entry.get("external", "") + internal = entry.get("internal", "") hostages.append(dict(external=external, internal=internal)) elif isinstance(entry, six.string_types): # string external = entry - internal = '' + internal = "" hostages.append(dict(external=external, internal=internal)) elif isinstance(entries, Mapping): # mapping - external = entries.get('external', '') - internal = entries.get('internal', '') + external = entries.get("external", "") + internal = entries.get("internal", "") hostages.append(dict(external=external, internal=internal)) elif isinstance(entries, six.string_types): # string external = entries - internal = '' + internal = "" hostages.append(dict(external=external, internal=internal)) # now parse each hostname string for host and optional port masters = [] for hostage in hostages: - external = hostage['external'] - internal = hostage['internal'] + external = hostage["external"] + internal = hostage["internal"] if external: external = parse_hostname(external, master_port) if not external: @@ -198,7 +203,7 @@ def extract_masters(opts, masters='master', port=None, raise_if_empty=True): def parse_hostname(hostname, default_port): - ''' + """ Parse hostname string and return a tuple of (host, port) If port missing in hostname string then use default_port If anything is not a valid then return None @@ -211,9 +216,9 @@ def parse_hostname(hostname, default_port): This is problematic since IPV6 addresses may have colons in them. Consequently the use of colon delimited ports is strongly discouraged. An ipv6 address must have at least 2 colons. - ''' + """ try: - host, sep, port = hostname.strip().rpartition(' ') + host, sep, port = hostname.strip().rpartition(" ") if not port: # invalid nothing there return None @@ -221,8 +226,8 @@ def parse_hostname(hostname, default_port): host = port port = default_port # ipv6 must have two or more colons - if host.count(':') == 1: # only one so may be using colon delimited port - host, sep, port = host.rpartition(':') + if host.count(":") == 1: # only one so may be using colon delimited port + host, sep, port = host.rpartition(":") if not host: # colon but not host so invalid return None if not port: # colon but no port so use default diff --git a/salt/daemons/masterapi.py b/salt/daemons/masterapi.py index 073765b8fa1..14c8aa9cc21 100644 --- a/salt/daemons/masterapi.py +++ b/salt/daemons/masterapi.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" This module contains all of the routines needed to set up a master server, this involves preparing the three listeners and the workers needed by the master. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -10,49 +10,48 @@ import fnmatch import logging import os import re -import time import stat +import time # Import salt libs import salt.acl -import salt.crypt +import salt.auth import salt.cache import salt.client +import salt.crypt import salt.exceptions +import salt.fileserver +import salt.key +import salt.minion import salt.payload import salt.pillar -import salt.state import salt.runner -import salt.auth -import salt.wheel -import salt.minion -import salt.key -import salt.fileserver +import salt.state import salt.utils.args import salt.utils.atomicfile import salt.utils.dictupdate import salt.utils.event import salt.utils.files import salt.utils.gitfs -import salt.utils.verify -import salt.utils.mine -import salt.utils.minions import salt.utils.gzip_util import salt.utils.jid +import salt.utils.mine import salt.utils.minions import salt.utils.path import salt.utils.platform import salt.utils.stringutils import salt.utils.user import salt.utils.verify +import salt.wheel from salt.defaults import DEFAULT_TARGET_DELIM -from salt.pillar import git_pillar # Import 3rd-party libs from salt.ext import six +from salt.pillar import git_pillar try: import pwd + HAS_PWD = True except ImportError: # pwd is not available on windows @@ -65,84 +64,79 @@ log = logging.getLogger(__name__) def init_git_pillar(opts): - ''' + """ Clear out the ext pillar caches, used when the master starts - ''' + """ ret = [] - for opts_dict in [x for x in opts.get('ext_pillar', [])]: - if 'git' in opts_dict: + for opts_dict in [x for x in opts.get("ext_pillar", [])]: + if "git" in opts_dict: try: pillar = salt.utils.gitfs.GitPillar( opts, - opts_dict['git'], + opts_dict["git"], per_remote_overrides=git_pillar.PER_REMOTE_OVERRIDES, per_remote_only=git_pillar.PER_REMOTE_ONLY, - global_only=git_pillar.GLOBAL_ONLY) + global_only=git_pillar.GLOBAL_ONLY, + ) ret.append(pillar) except salt.exceptions.FileserverConfigError: - if opts.get('git_pillar_verify_config', True): + if opts.get("git_pillar_verify_config", True): raise else: - log.critical('Could not initialize git_pillar') + log.critical("Could not initialize git_pillar") return ret def clean_fsbackend(opts): - ''' + """ Clean out the old fileserver backends - ''' + """ # Clear remote fileserver backend caches so they get recreated - for backend in ('git', 'hg', 'svn'): - if backend in opts['fileserver_backend']: + for backend in ("git", "hg", "svn"): + if backend in opts["fileserver_backend"]: env_cache = os.path.join( - opts['cachedir'], - '{0}fs'.format(backend), - 'envs.p' + opts["cachedir"], "{0}fs".format(backend), "envs.p" ) if os.path.isfile(env_cache): - log.debug('Clearing %sfs env cache', backend) + log.debug("Clearing %sfs env cache", backend) try: os.remove(env_cache) except OSError as exc: log.critical( - 'Unable to clear env cache file %s: %s', - env_cache, exc + "Unable to clear env cache file %s: %s", env_cache, exc ) file_lists_dir = os.path.join( - opts['cachedir'], - 'file_lists', - '{0}fs'.format(backend) + opts["cachedir"], "file_lists", "{0}fs".format(backend) ) try: file_lists_caches = os.listdir(file_lists_dir) except OSError: continue - for file_lists_cache in fnmatch.filter(file_lists_caches, '*.p'): + for file_lists_cache in fnmatch.filter(file_lists_caches, "*.p"): cache_file = os.path.join(file_lists_dir, file_lists_cache) try: os.remove(cache_file) except OSError as exc: log.critical( - 'Unable to file_lists cache file %s: %s', - cache_file, exc + "Unable to file_lists cache file %s: %s", cache_file, exc ) def clean_expired_tokens(opts): - ''' + """ Clean expired tokens from the master - ''' + """ loadauth = salt.auth.LoadAuth(opts) for tok in loadauth.list_tokens(): token_data = loadauth.get_tok(tok) - if 'expire' not in token_data or token_data.get('expire', 0) < time.time(): + if "expire" not in token_data or token_data.get("expire", 0) < time.time(): loadauth.rm_token(tok) def clean_pub_auth(opts): try: - auth_cache = os.path.join(opts['cachedir'], 'publish_auth') + auth_cache = os.path.join(opts["cachedir"], "publish_auth") if not os.path.exists(auth_cache): return else: @@ -151,25 +145,22 @@ def clean_pub_auth(opts): auth_file_path = os.path.join(dirpath, auth_file) if not os.path.isfile(auth_file_path): continue - if (time.time() - os.path.getmtime(auth_file_path) > - (opts['keep_jobs'] * 3600)): + if time.time() - os.path.getmtime(auth_file_path) > ( + opts["keep_jobs"] * 3600 + ): os.remove(auth_file_path) except (IOError, OSError): - log.error('Unable to delete pub auth file') + log.error("Unable to delete pub auth file") def clean_old_jobs(opts): - ''' + """ Clean out the old jobs from the job cache - ''' + """ # TODO: better way to not require creating the masterminion every time? - mminion = salt.minion.MasterMinion( - opts, - states=False, - rend=False, - ) + mminion = salt.minion.MasterMinion(opts, states=False, rend=False,) # If the master job cache has a clean_old_jobs, call it - fstr = '{0}.clean_old_jobs'.format(opts['master_job_cache']) + fstr = "{0}.clean_old_jobs".format(opts["master_job_cache"]) if fstr in mminion.returners: mminion.returners[fstr]() @@ -181,21 +172,19 @@ def mk_key(opts, user): uid = pwd.getpwnam(user).pw_uid except KeyError: # User doesn't exist in the system - if opts['client_acl_verify']: + if opts["client_acl_verify"]: return None if salt.utils.platform.is_windows(): # The username may contain '\' if it is in Windows # 'DOMAIN\username' format. Fix this for the keyfile path. keyfile = os.path.join( - opts['cachedir'], '.{0}_key'.format(user.replace('\\', '_')) + opts["cachedir"], ".{0}_key".format(user.replace("\\", "_")) ) else: - keyfile = os.path.join( - opts['cachedir'], '.{0}_key'.format(user) - ) + keyfile = os.path.join(opts["cachedir"], ".{0}_key".format(user)) if os.path.exists(keyfile): - log.debug('Removing stale keyfile: %s', keyfile) + log.debug("Removing stale keyfile: %s", keyfile) if salt.utils.platform.is_windows() and not os.access(keyfile, os.W_OK): # Cannot delete read-only files on Windows. os.chmod(keyfile, stat.S_IRUSR | stat.S_IWUSR) @@ -203,7 +192,7 @@ def mk_key(opts, user): key = salt.crypt.Crypticle.generate_key_string() with salt.utils.files.set_umask(0o277): - with salt.utils.files.fopen(keyfile, 'w+') as fp_: + with salt.utils.files.fopen(keyfile, "w+") as fp_: fp_.write(salt.utils.stringutils.to_str(key)) # 600 octal: Read and write access to the owner only. # Write access is necessary since on subsequent runs, if the file @@ -220,73 +209,77 @@ def mk_key(opts, user): def access_keys(opts): - ''' + """ A key needs to be placed in the filesystem with permissions 0400 so clients are required to run as root. - ''' + """ # TODO: Need a way to get all available users for systems not supported by pwd module. # For now users pattern matching will not work for publisher_acl. keys = {} - publisher_acl = opts['publisher_acl'] + publisher_acl = opts["publisher_acl"] acl_users = set(publisher_acl.keys()) - if opts.get('user'): - acl_users.add(opts['user']) + if opts.get("user"): + acl_users.add(opts["user"]) acl_users.add(salt.utils.user.get_user()) for user in acl_users: - log.info('Preparing the %s key for local communication', user) + log.info("Preparing the %s key for local communication", user) key = mk_key(opts, user) if key is not None: keys[user] = key # Check other users matching ACL patterns - if opts['client_acl_verify'] and HAS_PWD: - log.profile('Beginning pwd.getpwall() call in masterapi access_keys function') + if opts["client_acl_verify"] and HAS_PWD: + log.profile("Beginning pwd.getpwall() call in masterapi access_keys function") for user in pwd.getpwall(): user = user.pw_name - if user not in keys and salt.utils.stringutils.check_whitelist_blacklist(user, whitelist=acl_users): + if user not in keys and salt.utils.stringutils.check_whitelist_blacklist( + user, whitelist=acl_users + ): keys[user] = mk_key(opts, user) - log.profile('End pwd.getpwall() call in masterapi access_keys function') + log.profile("End pwd.getpwall() call in masterapi access_keys function") return keys def fileserver_update(fileserver): - ''' + """ Update the fileserver backends, requires that a salt.fileserver.Fileserver object be passed in - ''' + """ try: if not fileserver.servers: log.error( - 'No fileservers loaded, the master will not be able to ' - 'serve files to minions' + "No fileservers loaded, the master will not be able to " + "serve files to minions" ) - raise salt.exceptions.SaltMasterError('No fileserver backends available') + raise salt.exceptions.SaltMasterError("No fileserver backends available") fileserver.update() except Exception as exc: # pylint: disable=broad-except log.error( - 'Exception %s occurred in file server update', exc, - exc_info_on_loglevel=logging.DEBUG + "Exception %s occurred in file server update", + exc, + exc_info_on_loglevel=logging.DEBUG, ) class AutoKey(object): - ''' + """ Implement the methods to run auto key acceptance and rejection - ''' + """ + def __init__(self, opts): self.signing_files = {} self.opts = opts def check_permissions(self, filename): - ''' + """ Check if the specified filename has correct permissions - ''' + """ if salt.utils.platform.is_windows(): return True # After we've ascertained we're not on windows - groups = salt.utils.user.get_gid_list(self.opts['user'], include_default=False) + groups = salt.utils.user.get_gid_list(self.opts["user"], include_default=False) fmode = os.stat(filename) if stat.S_IWOTH & fmode.st_mode: @@ -295,7 +288,7 @@ class AutoKey(object): if stat.S_IWGRP & fmode.st_mode: # if the group has write access only allow with permissive_pki_access - if not self.opts.get('permissive_pki_access', False): + if not self.opts.get("permissive_pki_access", False): return False elif os.getuid() == 0 and fmode.st_gid not in groups: # if salt is root it has to be in the group that has write access @@ -305,34 +298,38 @@ class AutoKey(object): return True def check_signing_file(self, keyid, signing_file): - ''' + """ Check a keyid for membership in a signing file - ''' + """ if not signing_file or not os.path.exists(signing_file): return False if not self.check_permissions(signing_file): - log.warning('Wrong permissions for %s, ignoring content', signing_file) + log.warning("Wrong permissions for %s, ignoring content", signing_file) return False mtime = os.path.getmtime(signing_file) - if self.signing_files.get(signing_file, {}).get('mtime') != mtime: - self.signing_files.setdefault(signing_file, {})['mtime'] = mtime - with salt.utils.files.fopen(signing_file, 'r') as fp_: - self.signing_files[signing_file]['data'] = [ - entry for entry in [line.strip() for line in fp_] if not entry.strip().startswith('#') + if self.signing_files.get(signing_file, {}).get("mtime") != mtime: + self.signing_files.setdefault(signing_file, {})["mtime"] = mtime + with salt.utils.files.fopen(signing_file, "r") as fp_: + self.signing_files[signing_file]["data"] = [ + entry + for entry in [line.strip() for line in fp_] + if not entry.strip().startswith("#") ] - return any(salt.utils.stringutils.expr_match(keyid, line) for line - in self.signing_files[signing_file].get('data', [])) + return any( + salt.utils.stringutils.expr_match(keyid, line) + for line in self.signing_files[signing_file].get("data", []) + ) def check_autosign_dir(self, keyid): - ''' + """ Check a keyid for membership in a autosign directory. - ''' - autosign_dir = os.path.join(self.opts['pki_dir'], 'minions_autosign') + """ + autosign_dir = os.path.join(self.opts["pki_dir"], "minions_autosign") # cleanup expired files - expire_minutes = self.opts.get('autosign_timeout', 120) + expire_minutes = self.opts.get("autosign_timeout", 120) if expire_minutes > 0: min_time = time.time() - (60 * int(expire_minutes)) for root, dirs, filenames in salt.utils.path.os_walk(autosign_dir): @@ -340,7 +337,7 @@ class AutoKey(object): stub_file = os.path.join(autosign_dir, f) mtime = os.path.getmtime(stub_file) if mtime < min_time: - log.warning('Autosign keyid expired %s', stub_file) + log.warning("Autosign keyid expired %s", stub_file) os.remove(stub_file) stub_file = os.path.join(autosign_dir, keyid) @@ -350,13 +347,13 @@ class AutoKey(object): return True def check_autosign_grains(self, autosign_grains): - ''' + """ Check for matching grains in the autosign_grains_dir. - ''' - if not autosign_grains or 'autosign_grains_dir' not in self.opts: + """ + if not autosign_grains or "autosign_grains_dir" not in self.opts: return False - autosign_grains_dir = self.opts['autosign_grains_dir'] + autosign_grains_dir = self.opts["autosign_grains_dir"] for root, dirs, filenames in os.walk(autosign_grains_dir): for grain in filenames: if grain in autosign_grains: @@ -364,36 +361,32 @@ class AutoKey(object): if not self.check_permissions(grain_file): log.warning( - 'Wrong permissions for %s, ignoring content', - grain_file + "Wrong permissions for %s, ignoring content", grain_file ) continue - with salt.utils.files.fopen(grain_file, 'r') as f: + with salt.utils.files.fopen(grain_file, "r") as f: for line in f: line = salt.utils.stringutils.to_unicode(line).strip() - if line.startswith('#'): + if line.startswith("#"): continue if autosign_grains[grain] == line: return True return False def check_autoreject(self, keyid): - ''' + """ Checks if the specified keyid should automatically be rejected. - ''' - return self.check_signing_file( - keyid, - self.opts.get('autoreject_file', None) - ) + """ + return self.check_signing_file(keyid, self.opts.get("autoreject_file", None)) def check_autosign(self, keyid, autosign_grains=None): - ''' + """ Checks if the specified keyid should automatically be signed. - ''' - if self.opts['auto_accept']: + """ + if self.opts["auto_accept"]: return True - if self.check_signing_file(keyid, self.opts.get('autosign_file', None)): + if self.check_signing_file(keyid, self.opts.get("autosign_file", None)): return True if self.check_autosign_dir(keyid): return True @@ -403,18 +396,20 @@ class AutoKey(object): class RemoteFuncs(object): - ''' + """ Funcitons made available to minions, this class includes the raw routines post validation that make up the minion access to the master - ''' + """ + def __init__(self, opts): self.opts = opts self.event = salt.utils.event.get_event( - 'master', - self.opts['sock_dir'], - self.opts['transport'], - opts=self.opts, - listen=False) + "master", + self.opts["sock_dir"], + self.opts["transport"], + opts=self.opts, + listen=False, + ) self.serial = salt.payload.Serial(opts) self.ckminions = salt.utils.minions.CkMinions(opts) # Create the tops dict for loading external top data @@ -422,17 +417,14 @@ class RemoteFuncs(object): # Make a client self.local = salt.client.get_local_client(mopts=self.opts) # Create the master minion to access the external job cache - self.mminion = salt.minion.MasterMinion( - self.opts, - states=False, - rend=False) + self.mminion = salt.minion.MasterMinion(self.opts, states=False, rend=False) self.__setup_fileserver() self.cache = salt.cache.factory(opts) def __setup_fileserver(self): - ''' + """ Set the local file objects from the file server interface - ''' + """ fs_ = salt.fileserver.Fileserver(self.opts) self._serve_file = fs_.serve_file self._file_find = fs_._find_file @@ -444,80 +436,81 @@ class RemoteFuncs(object): self._file_envs = fs_.envs def __verify_minion_publish(self, load): - ''' + """ Verify that the passed information authorized a minion to execute - ''' + """ # Verify that the load is valid - if 'peer' not in self.opts: + if "peer" not in self.opts: return False - if not isinstance(self.opts['peer'], dict): + if not isinstance(self.opts["peer"], dict): return False - if any(key not in load for key in ('fun', 'arg', 'tgt', 'ret', 'id')): + if any(key not in load for key in ("fun", "arg", "tgt", "ret", "id")): return False # If the command will make a recursive publish don't run - if re.match('publish.*', load['fun']): + if re.match("publish.*", load["fun"]): return False # Check the permissions for this minion perms = [] - for match in self.opts['peer']: - if re.match(match, load['id']): + for match in self.opts["peer"]: + if re.match(match, load["id"]): # This is the list of funcs/modules! - if isinstance(self.opts['peer'][match], list): - perms.extend(self.opts['peer'][match]) - if ',' in load['fun']: + if isinstance(self.opts["peer"][match], list): + perms.extend(self.opts["peer"][match]) + if "," in load["fun"]: # 'arg': [['cat', '/proc/cpuinfo'], [], ['foo']] - load['fun'] = load['fun'].split(',') + load["fun"] = load["fun"].split(",") arg_ = [] - for arg in load['arg']: + for arg in load["arg"]: arg_.append(arg.split()) - load['arg'] = arg_ + load["arg"] = arg_ return self.ckminions.auth_check( - perms, - load['fun'], - load['arg'], - load['tgt'], - load.get('tgt_type', 'glob'), - publish_validate=True) + perms, + load["fun"], + load["arg"], + load["tgt"], + load.get("tgt_type", "glob"), + publish_validate=True, + ) def _master_opts(self, load): - ''' + """ Return the master options to the minion - ''' + """ mopts = {} file_roots = {} envs = self._file_envs() for saltenv in envs: if saltenv not in file_roots: file_roots[saltenv] = [] - mopts['file_roots'] = file_roots - mopts['top_file_merging_strategy'] = self.opts['top_file_merging_strategy'] - mopts['env_order'] = self.opts['env_order'] - mopts['default_top'] = self.opts['default_top'] - if load.get('env_only'): + mopts["file_roots"] = file_roots + mopts["top_file_merging_strategy"] = self.opts["top_file_merging_strategy"] + mopts["env_order"] = self.opts["env_order"] + mopts["default_top"] = self.opts["default_top"] + if load.get("env_only"): return mopts - mopts['renderer'] = self.opts['renderer'] - mopts['failhard'] = self.opts['failhard'] - mopts['state_top'] = self.opts['state_top'] - mopts['state_top_saltenv'] = self.opts['state_top_saltenv'] - mopts['nodegroups'] = self.opts['nodegroups'] - mopts['state_auto_order'] = self.opts['state_auto_order'] - mopts['state_events'] = self.opts['state_events'] - mopts['state_aggregate'] = self.opts['state_aggregate'] - mopts['jinja_env'] = self.opts['jinja_env'] - mopts['jinja_sls_env'] = self.opts['jinja_sls_env'] - mopts['jinja_lstrip_blocks'] = self.opts['jinja_lstrip_blocks'] - mopts['jinja_trim_blocks'] = self.opts['jinja_trim_blocks'] + mopts["renderer"] = self.opts["renderer"] + mopts["failhard"] = self.opts["failhard"] + mopts["state_top"] = self.opts["state_top"] + mopts["state_top_saltenv"] = self.opts["state_top_saltenv"] + mopts["nodegroups"] = self.opts["nodegroups"] + mopts["state_auto_order"] = self.opts["state_auto_order"] + mopts["state_events"] = self.opts["state_events"] + mopts["state_aggregate"] = self.opts["state_aggregate"] + mopts["jinja_env"] = self.opts["jinja_env"] + mopts["jinja_sls_env"] = self.opts["jinja_sls_env"] + mopts["jinja_lstrip_blocks"] = self.opts["jinja_lstrip_blocks"] + mopts["jinja_trim_blocks"] = self.opts["jinja_trim_blocks"] return mopts def _master_tops(self, load, skip_verify=False): - ''' + """ Return the results from master_tops if configured - ''' + """ if not skip_verify: - if 'id' not in load: - log.error('Received call for external nodes without an id') + if "id" not in load: + log.error("Received call for external nodes without an id") return {} - if not salt.utils.verify.valid_id(self.opts, load['id']): + if not salt.utils.verify.valid_id(self.opts, load["id"]): return {} # Evaluate all configured master_tops interfaces @@ -525,51 +518,55 @@ class RemoteFuncs(object): grains = {} ret = {} - if 'opts' in load: - opts = load['opts'] - if 'grains' in load['opts']: - grains = load['opts']['grains'] + if "opts" in load: + opts = load["opts"] + if "grains" in load["opts"]: + grains = load["opts"]["grains"] for fun in self.tops: - if fun not in self.opts.get('master_tops', {}): + if fun not in self.opts.get("master_tops", {}): continue try: - ret = salt.utils.dictupdate.merge(ret, self.tops[fun](opts=opts, grains=grains), merge_lists=True) + ret = salt.utils.dictupdate.merge( + ret, self.tops[fun](opts=opts, grains=grains), merge_lists=True + ) except Exception as exc: # pylint: disable=broad-except # If anything happens in the top generation, log it and move on log.error( - 'Top function %s failed with error %s for minion %s', - fun, exc, load['id'] + "Top function %s failed with error %s for minion %s", + fun, + exc, + load["id"], ) return ret def _mine_get(self, load, skip_verify=False): - ''' + """ Gathers the data from the specified minions' mine - ''' + """ if not skip_verify: - if any(key not in load for key in ('id', 'tgt', 'fun')): + if any(key not in load for key in ("id", "tgt", "fun")): return {} - if isinstance(load['fun'], six.string_types): - functions = list(set(load['fun'].split(','))) + if isinstance(load["fun"], six.string_types): + functions = list(set(load["fun"].split(","))) _ret_dict = len(functions) > 1 - elif isinstance(load['fun'], list): - functions = load['fun'] + elif isinstance(load["fun"], list): + functions = load["fun"] _ret_dict = True else: return {} functions_allowed = [] - if 'mine_get' in self.opts: + if "mine_get" in self.opts: # If master side acl defined. - if not isinstance(self.opts['mine_get'], dict): + if not isinstance(self.opts["mine_get"], dict): return {} perms = set() - for match in self.opts['mine_get']: - if re.match(match, load['id']): - if isinstance(self.opts['mine_get'][match], list): - perms.update(self.opts['mine_get'][match]) + for match in self.opts["mine_get"]: + if re.match(match, load["id"]): + if isinstance(self.opts["mine_get"][match], list): + perms.update(self.opts["mine_get"][match]) for fun in functions: if any(re.match(perm, fun) for perm in perms): functions_allowed.append(fun) @@ -579,30 +576,26 @@ class RemoteFuncs(object): functions_allowed = functions ret = {} - if not salt.utils.verify.valid_id(self.opts, load['id']): + if not salt.utils.verify.valid_id(self.opts, load["id"]): return ret - expr_form = load.get('expr_form') + expr_form = load.get("expr_form") # keep both expr_form and tgt_type to ensure # comptability between old versions of salt - if expr_form is not None and 'tgt_type' not in load: + if expr_form is not None and "tgt_type" not in load: match_type = expr_form else: - match_type = load.get('tgt_type', 'glob') - if match_type.lower() == 'pillar': - match_type = 'pillar_exact' - if match_type.lower() == 'compound': - match_type = 'compound_pillar_exact' + match_type = load.get("tgt_type", "glob") + if match_type.lower() == "pillar": + match_type = "pillar_exact" + if match_type.lower() == "compound": + match_type = "compound_pillar_exact" checker = salt.utils.minions.CkMinions(self.opts) - _res = checker.check_minions( - load['tgt'], - match_type, - greedy=False - ) - minions = _res['minions'] + _res = checker.check_minions(load["tgt"], match_type, greedy=False) + minions = _res["minions"] minion_side_acl = {} # Cache minion-side ACL for minion in minions: - mine_data = self.cache.fetch('minions/{0}'.format(minion), 'mine') + mine_data = self.cache.fetch("minions/{0}".format(minion), "mine") if not isinstance(mine_data, dict): continue for function in functions_allowed: @@ -610,25 +603,31 @@ class RemoteFuncs(object): continue mine_entry = mine_data[function] mine_result = mine_data[function] - if isinstance(mine_entry, dict) and salt.utils.mine.MINE_ITEM_ACL_ID in mine_entry: + if ( + isinstance(mine_entry, dict) + and salt.utils.mine.MINE_ITEM_ACL_ID in mine_entry + ): mine_result = mine_entry[salt.utils.mine.MINE_ITEM_ACL_DATA] # Check and fill minion-side ACL cache if function not in minion_side_acl.get(minion, {}): - if 'allow_tgt' in mine_entry: + if "allow_tgt" in mine_entry: # Only determine allowed targets if any have been specified. # This prevents having to add a list of all minions as allowed targets. get_minion = checker.check_minions( - mine_entry['allow_tgt'], - mine_entry.get('allow_tgt_type', 'glob'))['minions'] + mine_entry["allow_tgt"], + mine_entry.get("allow_tgt_type", "glob"), + )["minions"] # the minion in allow_tgt does not exist if not get_minion: continue salt.utils.dictupdate.set_dict_key_value( minion_side_acl, - '{}:{}'.format(minion, function), - get_minion - ) - if salt.utils.mine.minion_side_acl_denied(minion_side_acl, minion, function, load['id']): + "{}:{}".format(minion, function), + get_minion, + ) + if salt.utils.mine.minion_side_acl_denied( + minion_side_acl, minion, function, load["id"] + ): continue if _ret_dict: ret.setdefault(function, {})[minion] = mine_result @@ -638,17 +637,19 @@ class RemoteFuncs(object): return ret def _mine(self, load, skip_verify=False): - ''' + """ Store/update the mine data in cache. - ''' + """ if not skip_verify: - if 'id' not in load or 'data' not in load: + if "id" not in load or "data" not in load: return False - if self.opts.get('minion_data_cache', False) or self.opts.get('enforce_mine_cache', False): - cbank = 'minions/{0}'.format(load['id']) - ckey = 'mine' - new_data = load['data'] - if not load.get('clear', False): + if self.opts.get("minion_data_cache", False) or self.opts.get( + "enforce_mine_cache", False + ): + cbank = "minions/{0}".format(load["id"]) + ckey = "mine" + new_data = load["data"] + if not load.get("clear", False): data = self.cache.fetch(cbank, ckey) if isinstance(data, dict): data.update(new_data) @@ -656,258 +657,265 @@ class RemoteFuncs(object): return True def _mine_delete(self, load): - ''' + """ Allow the minion to delete a specific function from its own mine - ''' - if 'id' not in load or 'fun' not in load: + """ + if "id" not in load or "fun" not in load: return False - if self.opts.get('minion_data_cache', False) or self.opts.get('enforce_mine_cache', False): - cbank = 'minions/{0}'.format(load['id']) - ckey = 'mine' + if self.opts.get("minion_data_cache", False) or self.opts.get( + "enforce_mine_cache", False + ): + cbank = "minions/{0}".format(load["id"]) + ckey = "mine" try: data = self.cache.fetch(cbank, ckey) if not isinstance(data, dict): return False - if load['fun'] in data: - del data[load['fun']] + if load["fun"] in data: + del data[load["fun"]] self.cache.store(cbank, ckey, data) except OSError: return False return True def _mine_flush(self, load, skip_verify=False): - ''' + """ Allow the minion to delete all of its own mine contents - ''' - if not skip_verify and 'id' not in load: + """ + if not skip_verify and "id" not in load: return False - if self.opts.get('minion_data_cache', False) or self.opts.get('enforce_mine_cache', False): - return self.cache.flush('minions/{0}'.format(load['id']), 'mine') + if self.opts.get("minion_data_cache", False) or self.opts.get( + "enforce_mine_cache", False + ): + return self.cache.flush("minions/{0}".format(load["id"]), "mine") return True def _file_recv(self, load): - ''' + """ Allows minions to send files to the master, files are sent to the master file cache - ''' - if any(key not in load for key in ('id', 'path', 'loc')): + """ + if any(key not in load for key in ("id", "path", "loc")): return False - if not self.opts['file_recv'] or os.path.isabs(load['path']): + if not self.opts["file_recv"] or os.path.isabs(load["path"]): return False - if os.path.isabs(load['path']) or '../' in load['path']: + if os.path.isabs(load["path"]) or "../" in load["path"]: # Can overwrite master files!! return False - if not salt.utils.verify.valid_id(self.opts, load['id']): + if not salt.utils.verify.valid_id(self.opts, load["id"]): return False - file_recv_max_size = 1024*1024 * self.opts['file_recv_max_size'] + file_recv_max_size = 1024 * 1024 * self.opts["file_recv_max_size"] - if 'loc' in load and load['loc'] < 0: - log.error('Invalid file pointer: load[loc] < 0') + if "loc" in load and load["loc"] < 0: + log.error("Invalid file pointer: load[loc] < 0") return False - if load.get('size', 0) > file_recv_max_size: - log.error( - 'Exceeding file_recv_max_size limit: %s', - file_recv_max_size - ) + if load.get("size", 0) > file_recv_max_size: + log.error("Exceeding file_recv_max_size limit: %s", file_recv_max_size) return False - if len(load['data']) + load.get('loc', 0) > file_recv_max_size: - log.error( - 'Exceeding file_recv_max_size limit: %s', - file_recv_max_size - ) + if len(load["data"]) + load.get("loc", 0) > file_recv_max_size: + log.error("Exceeding file_recv_max_size limit: %s", file_recv_max_size) return False # Normalize Windows paths - normpath = load['path'] - if ':' in normpath: + normpath = load["path"] + if ":" in normpath: # make sure double backslashes are normalized - normpath = normpath.replace('\\', '/') + normpath = normpath.replace("\\", "/") normpath = os.path.normpath(normpath) cpath = os.path.join( - self.opts['cachedir'], - 'minions', - load['id'], - 'files', - normpath) + self.opts["cachedir"], "minions", load["id"], "files", normpath + ) cdir = os.path.dirname(cpath) if not os.path.isdir(cdir): try: os.makedirs(cdir) except os.error: pass - if os.path.isfile(cpath) and load['loc'] != 0: - mode = 'ab' + if os.path.isfile(cpath) and load["loc"] != 0: + mode = "ab" else: - mode = 'wb' + mode = "wb" with salt.utils.files.fopen(cpath, mode) as fp_: - if load['loc']: - fp_.seek(load['loc']) - fp_.write(salt.utils.stringutils.to_str(load['data'])) + if load["loc"]: + fp_.seek(load["loc"]) + fp_.write(salt.utils.stringutils.to_str(load["data"])) return True def _pillar(self, load): - ''' + """ Return the pillar data for the minion - ''' - if any(key not in load for key in ('id', 'grains')): + """ + if any(key not in load for key in ("id", "grains")): return False - log.debug('Master _pillar using ext: %s', load.get('ext')) + log.debug("Master _pillar using ext: %s", load.get("ext")) pillar = salt.pillar.get_pillar( - self.opts, - load['grains'], - load['id'], - load.get('saltenv', load.get('env')), - load.get('ext'), - self.mminion.functions, - pillar_override=load.get('pillar_override', {})) + self.opts, + load["grains"], + load["id"], + load.get("saltenv", load.get("env")), + load.get("ext"), + self.mminion.functions, + pillar_override=load.get("pillar_override", {}), + ) data = pillar.compile_pillar() - if self.opts.get('minion_data_cache', False): - self.cache.store('minions/{0}'.format(load['id']), - 'data', - {'grains': load['grains'], 'pillar': data}) - if self.opts.get('minion_data_cache_events') is True: - self.event.fire_event({'comment': 'Minion data cache refresh'}, salt.utils.event.tagify(load['id'], 'refresh', 'minion')) + if self.opts.get("minion_data_cache", False): + self.cache.store( + "minions/{0}".format(load["id"]), + "data", + {"grains": load["grains"], "pillar": data}, + ) + if self.opts.get("minion_data_cache_events") is True: + self.event.fire_event( + {"comment": "Minion data cache refresh"}, + salt.utils.event.tagify(load["id"], "refresh", "minion"), + ) return data def _minion_event(self, load): - ''' + """ Receive an event from the minion and fire it on the master event interface - ''' - if 'id' not in load: + """ + if "id" not in load: return False - if 'events' not in load and ('tag' not in load or 'data' not in load): + if "events" not in load and ("tag" not in load or "data" not in load): return False - if 'events' in load: - for event in load['events']: - if 'data' in event: - event_data = event['data'] + if "events" in load: + for event in load["events"]: + if "data" in event: + event_data = event["data"] else: event_data = event - self.event.fire_event(event_data, event['tag']) # old dup event - if load.get('pretag') is not None: - self.event.fire_event(event_data, salt.utils.event.tagify(event['tag'], base=load['pretag'])) + self.event.fire_event(event_data, event["tag"]) # old dup event + if load.get("pretag") is not None: + self.event.fire_event( + event_data, + salt.utils.event.tagify(event["tag"], base=load["pretag"]), + ) else: - tag = load['tag'] + tag = load["tag"] self.event.fire_event(load, tag) return True def _return(self, load): - ''' + """ Handle the return data sent from the minions - ''' + """ # Generate EndTime endtime = salt.utils.jid.jid_to_time(salt.utils.jid.gen_jid(self.opts)) # If the return data is invalid, just ignore it - if any(key not in load for key in ('return', 'jid', 'id')): + if any(key not in load for key in ("return", "jid", "id")): return False - if load['jid'] == 'req': + if load["jid"] == "req": # The minion is returning a standalone job, request a jobid - prep_fstr = '{0}.prep_jid'.format(self.opts['master_job_cache']) - load['jid'] = self.mminion.returners[prep_fstr](nocache=load.get('nocache', False)) + prep_fstr = "{0}.prep_jid".format(self.opts["master_job_cache"]) + load["jid"] = self.mminion.returners[prep_fstr]( + nocache=load.get("nocache", False) + ) # save the load, since we don't have it - saveload_fstr = '{0}.save_load'.format(self.opts['master_job_cache']) - self.mminion.returners[saveload_fstr](load['jid'], load) - log.info('Got return from %s for job %s', load['id'], load['jid']) - self.event.fire_event(load, load['jid']) # old dup event - self.event.fire_event(load, salt.utils.event.tagify([load['jid'], 'ret', load['id']], 'job')) + saveload_fstr = "{0}.save_load".format(self.opts["master_job_cache"]) + self.mminion.returners[saveload_fstr](load["jid"], load) + log.info("Got return from %s for job %s", load["id"], load["jid"]) + self.event.fire_event(load, load["jid"]) # old dup event + self.event.fire_event( + load, salt.utils.event.tagify([load["jid"], "ret", load["id"]], "job") + ) self.event.fire_ret_load(load) - if not self.opts['job_cache'] or self.opts.get('ext_job_cache'): + if not self.opts["job_cache"] or self.opts.get("ext_job_cache"): return - fstr = '{0}.update_endtime'.format(self.opts['master_job_cache']) - if (self.opts.get('job_cache_store_endtime') - and fstr in self.mminion.returners): - self.mminion.returners[fstr](load['jid'], endtime) + fstr = "{0}.update_endtime".format(self.opts["master_job_cache"]) + if self.opts.get("job_cache_store_endtime") and fstr in self.mminion.returners: + self.mminion.returners[fstr](load["jid"], endtime) - fstr = '{0}.returner'.format(self.opts['master_job_cache']) + fstr = "{0}.returner".format(self.opts["master_job_cache"]) self.mminion.returners[fstr](load) def _syndic_return(self, load): - ''' + """ Receive a syndic minion return and format it to look like returns from individual minions. - ''' + """ # Verify the load - if any(key not in load for key in ('return', 'jid', 'id')): + if any(key not in load for key in ("return", "jid", "id")): return None # if we have a load, save it - if 'load' in load: - fstr = '{0}.save_load'.format(self.opts['master_job_cache']) - self.mminion.returners[fstr](load['jid'], load['load']) + if "load" in load: + fstr = "{0}.save_load".format(self.opts["master_job_cache"]) + self.mminion.returners[fstr](load["jid"], load["load"]) # Format individual return loads - for key, item in six.iteritems(load['return']): - ret = {'jid': load['jid'], - 'id': key, - 'return': item} - if 'out' in load: - ret['out'] = load['out'] + for key, item in six.iteritems(load["return"]): + ret = {"jid": load["jid"], "id": key, "return": item} + if "out" in load: + ret["out"] = load["out"] self._return(ret) def minion_runner(self, load): - ''' + """ Execute a runner from a minion, return the runner's function data - ''' - if 'peer_run' not in self.opts: + """ + if "peer_run" not in self.opts: return {} - if not isinstance(self.opts['peer_run'], dict): + if not isinstance(self.opts["peer_run"], dict): return {} - if any(key not in load for key in ('fun', 'arg', 'id')): + if any(key not in load for key in ("fun", "arg", "id")): return {} perms = set() - for match in self.opts['peer_run']: - if re.match(match, load['id']): + for match in self.opts["peer_run"]: + if re.match(match, load["id"]): # This is the list of funcs/modules! - if isinstance(self.opts['peer_run'][match], list): - perms.update(self.opts['peer_run'][match]) + if isinstance(self.opts["peer_run"][match], list): + perms.update(self.opts["peer_run"][match]) good = False for perm in perms: - if re.match(perm, load['fun']): + if re.match(perm, load["fun"]): good = True if not good: # The minion is not who it says it is! # We don't want to listen to it! - log.warning('Minion id %s is not who it says it is!', load['id']) + log.warning("Minion id %s is not who it says it is!", load["id"]) return {} # Prepare the runner object opts = {} opts.update(self.opts) - opts.update({'fun': load['fun'], - 'arg': salt.utils.args.parse_input( - load['arg'], - no_parse=load.get('no_parse', [])), - 'id': load['id'], - 'doc': False, - 'conf_file': self.opts['conf_file']}) + opts.update( + { + "fun": load["fun"], + "arg": salt.utils.args.parse_input( + load["arg"], no_parse=load.get("no_parse", []) + ), + "id": load["id"], + "doc": False, + "conf_file": self.opts["conf_file"], + } + ) runner = salt.runner.Runner(opts) return runner.run() def pub_ret(self, load, skip_verify=False): - ''' + """ Request the return data from a specific jid, only allowed if the requesting minion also initialted the execution. - ''' - if not skip_verify and any(key not in load for key in ('jid', 'id')): + """ + if not skip_verify and any(key not in load for key in ("jid", "id")): return {} else: - auth_cache = os.path.join( - self.opts['cachedir'], - 'publish_auth') + auth_cache = os.path.join(self.opts["cachedir"], "publish_auth") if not os.path.isdir(auth_cache): os.makedirs(auth_cache) - jid_fn = os.path.join(auth_cache, load['jid']) - with salt.utils.files.fopen(jid_fn, 'r') as fp_: - if not load['id'] == salt.utils.stringutils.to_unicode(fp_.read()): + jid_fn = os.path.join(auth_cache, load["jid"]) + with salt.utils.files.fopen(jid_fn, "r") as fp_: + if not load["id"] == salt.utils.stringutils.to_unicode(fp_.read()): return {} - return self.local.get_cache_returns(load['jid']) + return self.local.get_cache_returns(load["jid"]) def minion_pub(self, load): - ''' + """ Publish a command initiated from a minion, this method executes minion restrictions so that the minion publication will only work if it is enabled in the config. @@ -923,47 +931,43 @@ class RemoteFuncs(object): - test.* This configuration will only allow the minion foo.example.com to execute commands from the test module - ''' + """ if not self.__verify_minion_publish(load): return {} # Set up the publication payload pub_load = { - 'fun': load['fun'], - 'arg': salt.utils.args.parse_input( - load['arg'], - no_parse=load.get('no_parse', [])), - 'tgt_type': load.get('tgt_type', 'glob'), - 'tgt': load['tgt'], - 'ret': load['ret'], - 'id': load['id'], + "fun": load["fun"], + "arg": salt.utils.args.parse_input( + load["arg"], no_parse=load.get("no_parse", []) + ), + "tgt_type": load.get("tgt_type", "glob"), + "tgt": load["tgt"], + "ret": load["ret"], + "id": load["id"], } - if 'tgt_type' in load: - if load['tgt_type'].startswith('node'): - if load['tgt'] in self.opts['nodegroups']: - pub_load['tgt'] = self.opts['nodegroups'][load['tgt']] - pub_load['tgt_type'] = 'compound' + if "tgt_type" in load: + if load["tgt_type"].startswith("node"): + if load["tgt"] in self.opts["nodegroups"]: + pub_load["tgt"] = self.opts["nodegroups"][load["tgt"]] + pub_load["tgt_type"] = "compound" else: return {} else: - pub_load['tgt_type'] = load['tgt_type'] + pub_load["tgt_type"] = load["tgt_type"] ret = {} - ret['jid'] = self.local.cmd_async(**pub_load) - _res = self.ckminions.check_minions( - load['tgt'], - pub_load['tgt_type']) - ret['minions'] = _res['minions'] - auth_cache = os.path.join( - self.opts['cachedir'], - 'publish_auth') + ret["jid"] = self.local.cmd_async(**pub_load) + _res = self.ckminions.check_minions(load["tgt"], pub_load["tgt_type"]) + ret["minions"] = _res["minions"] + auth_cache = os.path.join(self.opts["cachedir"], "publish_auth") if not os.path.isdir(auth_cache): os.makedirs(auth_cache) - jid_fn = os.path.join(auth_cache, six.text_type(ret['jid'])) - with salt.utils.files.fopen(jid_fn, 'w+') as fp_: - fp_.write(salt.utils.stringutils.to_str(load['id'])) + jid_fn = os.path.join(auth_cache, six.text_type(ret["jid"])) + with salt.utils.files.fopen(jid_fn, "w+") as fp_: + fp_.write(salt.utils.stringutils.to_str(load["id"])) return ret def minion_publish(self, load): - ''' + """ Publish a command initiated from a minion, this method executes minion restrictions so that the minion publication will only work if it is enabled in the config. @@ -979,82 +983,81 @@ class RemoteFuncs(object): - test.* This configuration will only allow the minion foo.example.com to execute commands from the test module - ''' + """ if not self.__verify_minion_publish(load): return {} # Set up the publication payload pub_load = { - 'fun': load['fun'], - 'arg': salt.utils.args.parse_input( - load['arg'], - no_parse=load.get('no_parse', [])), - 'tgt_type': load.get('tgt_type', 'glob'), - 'tgt': load['tgt'], - 'ret': load['ret'], - 'id': load['id'], + "fun": load["fun"], + "arg": salt.utils.args.parse_input( + load["arg"], no_parse=load.get("no_parse", []) + ), + "tgt_type": load.get("tgt_type", "glob"), + "tgt": load["tgt"], + "ret": load["ret"], + "id": load["id"], } - if 'tmo' in load: + if "tmo" in load: try: - pub_load['timeout'] = int(load['tmo']) + pub_load["timeout"] = int(load["tmo"]) except ValueError: - msg = 'Failed to parse timeout value: {0}'.format( - load['tmo']) + msg = "Failed to parse timeout value: {0}".format(load["tmo"]) log.warning(msg) return {} - if 'timeout' in load: + if "timeout" in load: try: - pub_load['timeout'] = int(load['timeout']) + pub_load["timeout"] = int(load["timeout"]) except ValueError: - msg = 'Failed to parse timeout value: {0}'.format( - load['timeout']) + msg = "Failed to parse timeout value: {0}".format(load["timeout"]) log.warning(msg) return {} - if 'tgt_type' in load: - if load['tgt_type'].startswith('node'): - if load['tgt'] in self.opts['nodegroups']: - pub_load['tgt'] = self.opts['nodegroups'][load['tgt']] - pub_load['tgt_type'] = 'compound' + if "tgt_type" in load: + if load["tgt_type"].startswith("node"): + if load["tgt"] in self.opts["nodegroups"]: + pub_load["tgt"] = self.opts["nodegroups"][load["tgt"]] + pub_load["tgt_type"] = "compound" else: return {} else: - pub_load['tgt_type'] = load['tgt_type'] - pub_load['raw'] = True + pub_load["tgt_type"] = load["tgt_type"] + pub_load["raw"] = True ret = {} for minion in self.local.cmd_iter(**pub_load): - if load.get('form', '') == 'full': + if load.get("form", "") == "full": data = minion - if 'jid' in minion: - ret['__jid__'] = minion['jid'] - data['ret'] = data.pop('return') - ret[minion['id']] = data + if "jid" in minion: + ret["__jid__"] = minion["jid"] + data["ret"] = data.pop("return") + ret[minion["id"]] = data else: - ret[minion['id']] = minion['return'] - if 'jid' in minion: - ret['__jid__'] = minion['jid'] - for key, val in six.iteritems(self.local.get_cache_returns(ret['__jid__'])): + ret[minion["id"]] = minion["return"] + if "jid" in minion: + ret["__jid__"] = minion["jid"] + for key, val in six.iteritems(self.local.get_cache_returns(ret["__jid__"])): if key not in ret: ret[key] = val - if load.get('form', '') != 'full': - ret.pop('__jid__') + if load.get("form", "") != "full": + ret.pop("__jid__") return ret def revoke_auth(self, load): - ''' + """ Allow a minion to request revocation of its own key - ''' - if 'id' not in load: + """ + if "id" not in load: return False keyapi = salt.key.Key(self.opts) - keyapi.delete_key(load['id'], - preserve_minions=load.get('preserve_minion_cache', - False)) + keyapi.delete_key( + load["id"], preserve_minions=load.get("preserve_minion_cache", False) + ) return True class LocalFuncs(object): - ''' + """ Set up methods for use only from the local system - ''' + """ + # The ClearFuncs object encapsulates the functions that can be executed in # the clear: # publish (The publish from the LocalClient) @@ -1065,11 +1068,12 @@ class LocalFuncs(object): self.key = key # Create the event manager self.event = salt.utils.event.get_event( - 'master', - self.opts['sock_dir'], - self.opts['transport'], - opts=self.opts, - listen=False) + "master", + self.opts["sock_dir"], + self.opts["transport"], + opts=self.opts, + listen=False, + ) # Make a client self.local = salt.client.get_local_client(mopts=self.opts) # Make an minion checker object @@ -1077,280 +1081,284 @@ class LocalFuncs(object): # Make an Auth object self.loadauth = salt.auth.LoadAuth(opts) # Stand up the master Minion to access returner data - self.mminion = salt.minion.MasterMinion( - self.opts, - states=False, - rend=False) + self.mminion = salt.minion.MasterMinion(self.opts, states=False, rend=False) # Make a wheel object self.wheel_ = salt.wheel.Wheel(opts) def runner(self, load): - ''' + """ Send a master control function back to the runner system - ''' + """ # All runner opts pass through eauth auth_type, err_name, key = self._prep_auth_info(load) # Authenticate auth_check = self.loadauth.check_authentication(load, auth_type) - error = auth_check.get('error') + error = auth_check.get("error") if error: # Authentication error occurred: do not continue. - return {'error': error} + return {"error": error} # Authorize runner_check = self.ckminions.runner_check( - auth_check.get('auth_list', []), - load['fun'], - load['kwarg'] + auth_check.get("auth_list", []), load["fun"], load["kwarg"] ) - username = auth_check.get('username') + username = auth_check.get("username") if not runner_check: - return {'error': {'name': err_name, - 'message': 'Authentication failure of type "{0}" occurred ' - 'for user {1}.'.format(auth_type, username)}} - elif isinstance(runner_check, dict) and 'error' in runner_check: + return { + "error": { + "name": err_name, + "message": 'Authentication failure of type "{0}" occurred ' + "for user {1}.".format(auth_type, username), + } + } + elif isinstance(runner_check, dict) and "error" in runner_check: # A dictionary with an error name/message was handled by ckminions.runner_check return runner_check # Authorized. Do the job! try: - fun = load.pop('fun') + fun = load.pop("fun") runner_client = salt.runner.RunnerClient(self.opts) - return runner_client.asynchronous(fun, - load.get('kwarg', {}), - username) + return runner_client.asynchronous(fun, load.get("kwarg", {}), username) except Exception as exc: # pylint: disable=broad-except - log.exception('Exception occurred while introspecting %s') - return {'error': {'name': exc.__class__.__name__, - 'args': exc.args, - 'message': six.text_type(exc)}} + log.exception("Exception occurred while introspecting %s") + return { + "error": { + "name": exc.__class__.__name__, + "args": exc.args, + "message": six.text_type(exc), + } + } def wheel(self, load): - ''' + """ Send a master control function back to the wheel system - ''' + """ # All wheel ops pass through eauth auth_type, err_name, key = self._prep_auth_info(load) # Authenticate auth_check = self.loadauth.check_authentication( - load, - auth_type, - key=key, - show_username=True + load, auth_type, key=key, show_username=True ) - error = auth_check.get('error') + error = auth_check.get("error") if error: # Authentication error occurred: do not continue. - return {'error': error} + return {"error": error} # Authorize - username = auth_check.get('username') - if auth_type != 'user': + username = auth_check.get("username") + if auth_type != "user": wheel_check = self.ckminions.wheel_check( - auth_check.get('auth_list', []), - load['fun'], - load['kwarg'] + auth_check.get("auth_list", []), load["fun"], load["kwarg"] ) if not wheel_check: - return {'error': {'name': err_name, - 'message': 'Authentication failure of type "{0}" occurred for ' - 'user {1}.'.format(auth_type, username)}} - elif isinstance(wheel_check, dict) and 'error' in wheel_check: + return { + "error": { + "name": err_name, + "message": 'Authentication failure of type "{0}" occurred for ' + "user {1}.".format(auth_type, username), + } + } + elif isinstance(wheel_check, dict) and "error" in wheel_check: # A dictionary with an error name/message was handled by ckminions.wheel_check return wheel_check # Authenticated. Do the job. jid = salt.utils.jid.gen_jid(self.opts) - fun = load.pop('fun') - tag = salt.utils.event.tagify(jid, prefix='wheel') - data = {'fun': "wheel.{0}".format(fun), - 'jid': jid, - 'tag': tag, - 'user': username} + fun = load.pop("fun") + tag = salt.utils.event.tagify(jid, prefix="wheel") + data = { + "fun": "wheel.{0}".format(fun), + "jid": jid, + "tag": tag, + "user": username, + } try: - self.event.fire_event(data, salt.utils.event.tagify([jid, 'new'], 'wheel')) + self.event.fire_event(data, salt.utils.event.tagify([jid, "new"], "wheel")) ret = self.wheel_.call_func(fun, **load) - data['return'] = ret - data['success'] = True - self.event.fire_event(data, salt.utils.event.tagify([jid, 'ret'], 'wheel')) - return {'tag': tag, - 'data': data} + data["return"] = ret + data["success"] = True + self.event.fire_event(data, salt.utils.event.tagify([jid, "ret"], "wheel")) + return {"tag": tag, "data": data} except Exception as exc: # pylint: disable=broad-except - log.exception('Exception occurred while introspecting %s', fun) - data['return'] = 'Exception occurred in wheel {0}: {1}: {2}'.format( - fun, - exc.__class__.__name__, - exc, - ) - data['success'] = False - self.event.fire_event(data, salt.utils.event.tagify([jid, 'ret'], 'wheel')) - return {'tag': tag, - 'data': data} + log.exception("Exception occurred while introspecting %s", fun) + data["return"] = "Exception occurred in wheel {0}: {1}: {2}".format( + fun, exc.__class__.__name__, exc, + ) + data["success"] = False + self.event.fire_event(data, salt.utils.event.tagify([jid, "ret"], "wheel")) + return {"tag": tag, "data": data} def mk_token(self, load): - ''' + """ Create and return an authentication token, the clear load needs to contain the eauth key and the needed authentication creds. - ''' + """ token = self.loadauth.mk_token(load) if not token: log.warning('Authentication failure of type "eauth" occurred.') - return '' + return "" return token def get_token(self, load): - ''' + """ Return the name associated with a token or False if the token is invalid - ''' - if 'token' not in load: + """ + if "token" not in load: return False - return self.loadauth.get_tok(load['token']) + return self.loadauth.get_tok(load["token"]) def publish(self, load): - ''' + """ This method sends out publications to the minions, it can only be used by the LocalClient. - ''' - extra = load.get('kwargs', {}) + """ + extra = load.get("kwargs", {}) - publisher_acl = salt.acl.PublisherACL(self.opts['publisher_acl_blacklist']) + publisher_acl = salt.acl.PublisherACL(self.opts["publisher_acl_blacklist"]) - if publisher_acl.user_is_blacklisted(load['user']) or \ - publisher_acl.cmd_is_blacklisted(load['fun']): + if publisher_acl.user_is_blacklisted( + load["user"] + ) or publisher_acl.cmd_is_blacklisted(load["fun"]): log.error( - '%s does not have permissions to run %s. Please contact ' - 'your local administrator if you believe this is in error.', - load['user'], load['fun'] + "%s does not have permissions to run %s. Please contact " + "your local administrator if you believe this is in error.", + load["user"], + load["fun"], ) - return {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} + return { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } # Retrieve the minions list - delimiter = load.get('kwargs', {}).get('delimiter', DEFAULT_TARGET_DELIM) + delimiter = load.get("kwargs", {}).get("delimiter", DEFAULT_TARGET_DELIM) _res = self.ckminions.check_minions( - load['tgt'], - load.get('tgt_type', 'glob'), - delimiter + load["tgt"], load.get("tgt_type", "glob"), delimiter ) - minions = _res['minions'] + minions = _res["minions"] # Check for external auth calls and authenticate auth_type, err_name, key = self._prep_auth_info(extra) - if auth_type == 'user': + if auth_type == "user": auth_check = self.loadauth.check_authentication(load, auth_type, key=key) else: auth_check = self.loadauth.check_authentication(extra, auth_type) # Setup authorization list variable and error information - auth_list = auth_check.get('auth_list', []) - error = auth_check.get('error') + auth_list = auth_check.get("auth_list", []) + error = auth_check.get("error") err_msg = 'Authentication failure of type "{0}" occurred.'.format(auth_type) if error: # Authentication error occurred: do not continue. log.warning(err_msg) - return {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} + return { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } # All Token, Eauth, and non-root users must pass the authorization check - if auth_type != 'user' or (auth_type == 'user' and auth_list): + if auth_type != "user" or (auth_type == "user" and auth_list): # Authorize the request authorized = self.ckminions.auth_check( auth_list, - load['fun'], - load['arg'], - load['tgt'], - load.get('tgt_type', 'glob'), + load["fun"], + load["arg"], + load["tgt"], + load.get("tgt_type", "glob"), minions=minions, # always accept find_job - whitelist=['saltutil.find_job'], + whitelist=["saltutil.find_job"], ) if not authorized: # Authorization error occurred. Log warning and do not continue. log.warning(err_msg) - return {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} + return { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } # Perform some specific auth_type tasks after the authorization check - if auth_type == 'token': - username = auth_check.get('username') - load['user'] = username + if auth_type == "token": + username = auth_check.get("username") + load["user"] = username log.debug('Minion tokenized user = "%s"', username) - elif auth_type == 'eauth': + elif auth_type == "eauth": # The username we are attempting to auth with - load['user'] = self.loadauth.load_name(extra) + load["user"] = self.loadauth.load_name(extra) # If we order masters (via a syndic), don't short circuit if no minions # are found - if not self.opts.get('order_masters'): + if not self.opts.get("order_masters"): # Check for no minions if not minions: - return { - 'enc': 'clear', - 'load': { - 'jid': None, - 'minions': minions - } - } + return {"enc": "clear", "load": {"jid": None, "minions": minions}} # Retrieve the jid - if not load['jid']: - fstr = '{0}.prep_jid'.format(self.opts['master_job_cache']) - load['jid'] = self.mminion.returners[fstr](nocache=extra.get('nocache', False)) - self.event.fire_event({'minions': minions}, load['jid']) + if not load["jid"]: + fstr = "{0}.prep_jid".format(self.opts["master_job_cache"]) + load["jid"] = self.mminion.returners[fstr]( + nocache=extra.get("nocache", False) + ) + self.event.fire_event({"minions": minions}, load["jid"]) new_job_load = { - 'jid': load['jid'], - 'tgt_type': load['tgt_type'], - 'tgt': load['tgt'], - 'user': load['user'], - 'fun': load['fun'], - 'arg': salt.utils.args.parse_input( - load['arg'], - no_parse=load.get('no_parse', [])), - 'minions': minions, - } + "jid": load["jid"], + "tgt_type": load["tgt_type"], + "tgt": load["tgt"], + "user": load["user"], + "fun": load["fun"], + "arg": salt.utils.args.parse_input( + load["arg"], no_parse=load.get("no_parse", []) + ), + "minions": minions, + } # Announce the job on the event bus - self.event.fire_event(new_job_load, 'new_job') # old dup event - self.event.fire_event(new_job_load, salt.utils.event.tagify([load['jid'], 'new'], 'job')) + self.event.fire_event(new_job_load, "new_job") # old dup event + self.event.fire_event( + new_job_load, salt.utils.event.tagify([load["jid"], "new"], "job") + ) # Save the invocation information - if self.opts['ext_job_cache']: + if self.opts["ext_job_cache"]: try: - fstr = '{0}.save_load'.format(self.opts['ext_job_cache']) - self.mminion.returners[fstr](load['jid'], load) + fstr = "{0}.save_load".format(self.opts["ext_job_cache"]) + self.mminion.returners[fstr](load["jid"], load) except KeyError: log.critical( - 'The specified returner used for the external job cache ' + "The specified returner used for the external job cache " '"%s" does not have a save_load function!', - self.opts['ext_job_cache'] + self.opts["ext_job_cache"], ) except Exception: # pylint: disable=broad-except log.critical( - 'The specified returner threw a stack trace:', - exc_info=True + "The specified returner threw a stack trace:", exc_info=True ) # always write out to the master job cache try: - fstr = '{0}.save_load'.format(self.opts['master_job_cache']) - self.mminion.returners[fstr](load['jid'], load) + fstr = "{0}.save_load".format(self.opts["master_job_cache"]) + self.mminion.returners[fstr](load["jid"], load) except KeyError: log.critical( - 'The specified returner used for the master job cache ' + "The specified returner used for the master job cache " '"%s" does not have a save_load function!', - self.opts['master_job_cache'] + self.opts["master_job_cache"], ) except Exception: # pylint: disable=broad-except - log.critical( - 'The specified returner threw a stack trace:', - exc_info=True - ) + log.critical("The specified returner threw a stack trace:", exc_info=True) # Altering the contents of the publish load is serious!! Changes here # break compatibility with minion/master versions and even tiny # additions can have serious implications on the performance of the @@ -1360,63 +1368,57 @@ class LocalFuncs(object): # touching this stuff, we can probably do what you want to do another # way that won't have a negative impact. pub_load = { - 'fun': load['fun'], - 'arg': salt.utils.args.parse_input( - load['arg'], - no_parse=load.get('no_parse', [])), - 'tgt': load['tgt'], - 'jid': load['jid'], - 'ret': load['ret'], + "fun": load["fun"], + "arg": salt.utils.args.parse_input( + load["arg"], no_parse=load.get("no_parse", []) + ), + "tgt": load["tgt"], + "jid": load["jid"], + "ret": load["ret"], } - if 'id' in extra: - pub_load['id'] = extra['id'] - if 'tgt_type' in load: - pub_load['tgt_type'] = load['tgt_type'] - if 'to' in load: - pub_load['to'] = load['to'] + if "id" in extra: + pub_load["id"] = extra["id"] + if "tgt_type" in load: + pub_load["tgt_type"] = load["tgt_type"] + if "to" in load: + pub_load["to"] = load["to"] - if 'kwargs' in load: - if 'ret_config' in load['kwargs']: - pub_load['ret_config'] = load['kwargs'].get('ret_config') + if "kwargs" in load: + if "ret_config" in load["kwargs"]: + pub_load["ret_config"] = load["kwargs"].get("ret_config") - if 'metadata' in load['kwargs']: - pub_load['metadata'] = load['kwargs'].get('metadata') + if "metadata" in load["kwargs"]: + pub_load["metadata"] = load["kwargs"].get("metadata") - if 'ret_kwargs' in load['kwargs']: - pub_load['ret_kwargs'] = load['kwargs'].get('ret_kwargs') + if "ret_kwargs" in load["kwargs"]: + pub_load["ret_kwargs"] = load["kwargs"].get("ret_kwargs") - if 'user' in load: + if "user" in load: log.info( - 'User %s Published command %s with jid %s', - load['user'], load['fun'], load['jid'] + "User %s Published command %s with jid %s", + load["user"], + load["fun"], + load["jid"], ) - pub_load['user'] = load['user'] + pub_load["user"] = load["user"] else: - log.info( - 'Published command %s with jid %s', - load['fun'], load['jid'] - ) - log.debug('Published command details %s', pub_load) + log.info("Published command %s with jid %s", load["fun"], load["jid"]) + log.debug("Published command details %s", pub_load) - return {'ret': { - 'jid': load['jid'], - 'minions': minions - }, - 'pub': pub_load - } + return {"ret": {"jid": load["jid"], "minions": minions}, "pub": pub_load} def _prep_auth_info(self, load): key = None - if 'token' in load: - auth_type = 'token' - err_name = 'TokenAuthenticationError' - elif 'eauth' in load: - auth_type = 'eauth' - err_name = 'EauthAuthenticationError' + if "token" in load: + auth_type = "token" + err_name = "TokenAuthenticationError" + elif "eauth" in load: + auth_type = "eauth" + err_name = "EauthAuthenticationError" else: - auth_type = 'user' - err_name = 'UserAuthenticationError' + auth_type = "user" + err_name = "UserAuthenticationError" key = self.key return auth_type, err_name, key diff --git a/salt/defaults/__init__.py b/salt/defaults/__init__.py index 4ae27cabcc2..a83ccc8deaf 100644 --- a/salt/defaults/__init__.py +++ b/salt/defaults/__init__.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Default values, to be imported elsewhere in Salt code Do NOT, import any salt modules (salt.utils, salt.config, etc.) into this file, as this may result in circular imports. -''' +""" # Default delimiter for multi-level traversal in targeting -DEFAULT_TARGET_DELIM = ':' +DEFAULT_TARGET_DELIM = ":" diff --git a/salt/defaults/exitcodes.py b/salt/defaults/exitcodes.py index e2ae783eb08..6f5c505dcba 100644 --- a/salt/defaults/exitcodes.py +++ b/salt/defaults/exitcodes.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Classification of Salt exit codes. These are intended to augment universal exit codes (found in Python's `os` module with the `EX_` prefix or in `sysexits.h`). -''' +""" # Too many situations use "exit 1" - try not to use it when something # else is more appropriate. @@ -29,14 +29,14 @@ EX_AGGREGATE = 20 # These constants are documented here: # https://docs.python.org/2/library/os.html#os.EX_OK -EX_OK = 0 # successful termination -EX_USAGE = 64 # command line usage error -EX_NOUSER = 67 # addressee unknown -EX_UNAVAILABLE = 69 # service unavailable -EX_SOFTWARE = 70 # internal software error -EX_CANTCREAT = 73 # can't create (user) output file -EX_TEMPFAIL = 75 # temp failure; user is invited to retry -EX_NOPERM = 77 # permission denied +EX_OK = 0 # successful termination +EX_USAGE = 64 # command line usage error +EX_NOUSER = 67 # addressee unknown +EX_UNAVAILABLE = 69 # service unavailable +EX_SOFTWARE = 70 # internal software error +EX_CANTCREAT = 73 # can't create (user) output file +EX_TEMPFAIL = 75 # temp failure; user is invited to retry +EX_NOPERM = 77 # permission denied # The Salt specific exit codes are defined below: diff --git a/salt/engines/__init__.py b/salt/engines/__init__.py index 3c97e4ee380..9ab7aa6103b 100644 --- a/salt/engines/__init__.py +++ b/salt/engines/__init__.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Initialize the engines system. This plugin system allows for complex services to be encapsulated within the salt plugin environment -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import multiprocessing + import logging +import multiprocessing # Import salt libs import salt @@ -18,18 +19,18 @@ log = logging.getLogger(__name__) def start_engines(opts, proc_mgr, proxy=None): - ''' + """ Fire up the configured engines! - ''' + """ utils = salt.loader.utils(opts, proxy=proxy) - if opts['__role'] == 'master': + if opts["__role"] == "master": runners = salt.loader.runner(opts, utils=utils) else: runners = [] funcs = salt.loader.minion_mods(opts, utils=utils, proxy=proxy) engines = salt.loader.engines(opts, funcs, runners, utils, proxy=proxy) - engines_opt = opts.get('engines', []) + engines_opt = opts.get("engines", []) if isinstance(engines_opt, dict): engines_opt = [{k: v} for k, v in engines_opt.items()] @@ -47,44 +48,35 @@ def start_engines(opts, proc_mgr, proxy=None): else: engine_opts = None engine_name = None - if engine_opts is not None and 'engine_module' in engine_opts: - fun = '{0}.start'.format(engine_opts['engine_module']) + if engine_opts is not None and "engine_module" in engine_opts: + fun = "{0}.start".format(engine_opts["engine_module"]) engine_name = engine - del engine_opts['engine_module'] + del engine_opts["engine_module"] else: - fun = '{0}.start'.format(engine) + fun = "{0}.start".format(engine) if fun in engines: start_func = engines[fun] if engine_name: - name = '{0}.Engine({1}-{2})'.format(__name__, - start_func.__module__, - engine_name) + name = "{0}.Engine({1}-{2})".format( + __name__, start_func.__module__, engine_name + ) else: - name = '{0}.Engine({1})'.format(__name__, - start_func.__module__) - log.info('Starting Engine %s', name) + name = "{0}.Engine({1})".format(__name__, start_func.__module__) + log.info("Starting Engine %s", name) proc_mgr.add_process( - Engine, - args=( - opts, - fun, - engine_opts, - funcs, - runners, - proxy - ), - name=name - ) + Engine, args=(opts, fun, engine_opts, funcs, runners, proxy), name=name + ) class Engine(SignalHandlingProcess): - ''' + """ Execute the given engine in a new process - ''' + """ + def __init__(self, opts, fun, config, funcs, runners, proxy, **kwargs): - ''' + """ Set up the process executor - ''' + """ super(Engine, self).__init__(**kwargs) self.opts = opts self.config = config @@ -98,51 +90,52 @@ class Engine(SignalHandlingProcess): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['opts'], - state['fun'], - state['config'], - state['funcs'], - state['runners'], - state['proxy'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["opts"], + state["fun"], + state["config"], + state["funcs"], + state["runners"], + state["proxy"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'fun': self.fun, - 'config': self.config, - 'funcs': self.funcs, - 'runners': self.runners, - 'proxy': self.proxy, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "fun": self.fun, + "config": self.config, + "funcs": self.funcs, + "runners": self.runners, + "proxy": self.proxy, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def run(self): - ''' + """ Run the master service! - ''' + """ self.utils = salt.loader.utils(self.opts, proxy=self.proxy) if salt.utils.platform.is_windows(): # Calculate function references since they can't be pickled. - if self.opts['__role'] == 'master': + if self.opts["__role"] == "master": self.runners = salt.loader.runner(self.opts, utils=self.utils) else: self.runners = [] - self.funcs = salt.loader.minion_mods(self.opts, utils=self.utils, proxy=self.proxy) + self.funcs = salt.loader.minion_mods( + self.opts, utils=self.utils, proxy=self.proxy + ) - self.engine = salt.loader.engines(self.opts, - self.funcs, - self.runners, - self.utils, - proxy=self.proxy) + self.engine = salt.loader.engines( + self.opts, self.funcs, self.runners, self.utils, proxy=self.proxy + ) kwargs = self.config or {} try: self.engine[self.fun](**kwargs) except Exception as exc: # pylint: disable=broad-except log.critical( - 'Engine \'%s\' could not be started!', - self.fun.split('.')[0], exc_info=True + "Engine '%s' could not be started!", + self.fun.split(".")[0], + exc_info=True, ) diff --git a/salt/engines/docker_events.py b/salt/engines/docker_events.py index e6cf30e58ee..c5706c433a2 100644 --- a/salt/engines/docker_events.py +++ b/salt/engines/docker_events.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Send events from Docker events :Depends: Docker API >= 1.22 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,13 +10,14 @@ from __future__ import absolute_import, print_function, unicode_literals import logging import traceback -import salt.utils.json import salt.utils.event +import salt.utils.json # pylint: disable=import-error try: import docker import docker.utils + HAS_DOCKER_PY = True except ImportError: HAS_DOCKER_PY = False @@ -27,23 +28,25 @@ log = logging.getLogger(__name__) # pylint: disable=invalid-name CLIENT_TIMEOUT = 60 # Define the module's virtual name -__virtualname__ = 'docker_events' +__virtualname__ = "docker_events" def __virtual__(): - ''' + """ Only load if docker libs are present - ''' + """ if not HAS_DOCKER_PY: - return (False, 'Docker_events engine could not be imported') + return (False, "Docker_events engine could not be imported") return True -def start(docker_url='unix://var/run/docker.sock', - timeout=CLIENT_TIMEOUT, - tag='salt/engines/docker_events', - filters=None): - ''' +def start( + docker_url="unix://var/run/docker.sock", + timeout=CLIENT_TIMEOUT, + tag="salt/engines/docker_events", + filters=None, +): + """ Scan for Docker events and fire events Example Config @@ -65,41 +68,45 @@ def start(docker_url='unix://var/run/docker.sock', them to the Salt event bus. For filter reference, see https://docs.docker.com/engine/reference/commandline/events/ - ''' + """ - if __opts__.get('__role') == 'master': + if __opts__.get("__role") == "master": fire_master = salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir']).fire_event + __opts__, __opts__["sock_dir"] + ).fire_event else: fire_master = None def fire(tag, msg): - ''' + """ How to fire the event - ''' + """ if fire_master: fire_master(msg, tag) else: - __salt__['event.send'](tag, msg) + __salt__["event.send"](tag, msg) try: # docker-py 2.0 renamed this client attribute client = docker.APIClient(base_url=docker_url, timeout=timeout) except AttributeError: + # pylint: disable=not-callable client = docker.Client(base_url=docker_url, timeout=timeout) + # pylint: enable=not-callable try: events = client.events(filters=filters) for event in events: - data = salt.utils.json.loads(event.decode(__salt_system_encoding__, errors='replace')) + data = salt.utils.json.loads( + event.decode(__salt_system_encoding__, errors="replace") + ) # https://github.com/docker/cli/blob/master/cli/command/system/events.go#L109 # https://github.com/docker/engine-api/blob/master/types/events/events.go # Each output includes the event type, actor id, name and action. # status field can be ommited - if data['Action']: - fire('{0}/{1}'.format(tag, data['Action']), data) + if data["Action"]: + fire("{0}/{1}".format(tag, data["Action"]), data) else: - fire('{0}/{1}'.format(tag, data['status']), data) + fire("{0}/{1}".format(tag, data["status"]), data) except Exception: # pylint: disable=broad-except traceback.print_exc() diff --git a/salt/engines/fluent.py b/salt/engines/fluent.py index 233b00a6744..96ba663a520 100644 --- a/salt/engines/fluent.py +++ b/salt/engines/fluent.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An engine that reads messages from the salt event bus and pushes them onto a fluent endpoint. @@ -34,10 +34,11 @@ All arguments are optional :depends: fluent-logger -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -51,17 +52,19 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'fluent' +__virtualname__ = "fluent" def __virtual__(): - return __virtualname__ \ - if sender is not None \ - else (False, 'fluent-logger not installed') + return ( + __virtualname__ + if sender is not None + else (False, "fluent-logger not installed") + ) -def start(host='localhost', port=24224, app='engine'): - ''' +def start(host="localhost", port=24224, app="engine"): + """ Listen to salt events and forward them to fluent args: @@ -69,24 +72,24 @@ def start(host='localhost', port=24224, app='engine'): port (int): Port of fluentd agent. Default is 24224 app (str): Text sent as fluentd tag. Default is "engine". This text is appended to "saltstack." to form a fluentd tag, ex: "saltstack.engine" - ''' - SENDER_NAME = 'saltstack' + """ + SENDER_NAME = "saltstack" sender.setup(SENDER_NAME, host=host, port=port) - if __opts__.get('id').endswith('_master'): + if __opts__.get("id").endswith("_master"): event_bus = salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir'], - listen=True) + __opts__, __opts__["sock_dir"], listen=True + ) else: event_bus = salt.utils.event.get_event( - 'minion', - transport=__opts__['transport'], + "minion", + transport=__opts__["transport"], opts=__opts__, - sock_dir=__opts__['sock_dir'], - listen=True) - log.info('Fluent engine started') + sock_dir=__opts__["sock_dir"], + listen=True, + ) + log.info("Fluent engine started") while True: salt_event = event_bus.get_event_block() diff --git a/salt/engines/http_logstash.py b/salt/engines/http_logstash.py index fdb4c83a55d..c7bc93bae3e 100644 --- a/salt/engines/http_logstash.py +++ b/salt/engines/http_logstash.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" HTTP Logstash engine ========================== @@ -27,23 +27,24 @@ them onto a logstash endpoint via HTTP requests. funs: - probes.results - bgp.config -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python lib import fnmatch +import salt.utils.event + # Import salt libs import salt.utils.http -import salt.utils.event import salt.utils.json # ---------------------------------------------------------------------------------------------------------------------- # module properties # ---------------------------------------------------------------------------------------------------------------------- -_HEADERS = {'Content-Type': 'application/json'} +_HEADERS = {"Content-Type": "application/json"} # ---------------------------------------------------------------------------------------------------------------------- # module properties @@ -55,27 +56,28 @@ _HEADERS = {'Content-Type': 'application/json'} def _logstash(url, data): - ''' + """ Issues HTTP queries to the logstash server. - ''' + """ result = salt.utils.http.query( url, - 'POST', + "POST", header_dict=_HEADERS, data=salt.utils.json.dumps(data), decode=True, status=True, - opts=__opts__ + opts=__opts__, ) return result + # ---------------------------------------------------------------------------------------------------------------------- # main # ---------------------------------------------------------------------------------------------------------------------- def start(url, funs=None, tags=None): - ''' + """ Listen to salt events and forward them to logstash. url @@ -93,17 +95,17 @@ def start(url, funs=None, tags=None): tags: ``None`` A list of pattern to compare the event tag against. By default, this option accepts any event to be submitted to Logstash. - ''' - if __opts__.get('id').endswith('_master'): - instance = 'master' + """ + if __opts__.get("id").endswith("_master"): + instance = "master" else: - instance = 'minion' + instance = "minion" with salt.utils.event.get_event( - instance, - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'], - opts=__opts__, - ) as event_bus: + instance, + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], + opts=__opts__, + ) as event_bus: while True: event = event_bus.get_event(full=True) if event: @@ -111,11 +113,11 @@ def start(url, funs=None, tags=None): if tags and isinstance(tags, list): found_match = False for tag in tags: - if fnmatch.fnmatch(event['tag'], tag): + if fnmatch.fnmatch(event["tag"], tag): found_match = True publish = found_match - if funs and 'fun' in event['data']: - if not event['data']['fun'] in funs: + if funs and "fun" in event["data"]: + if not event["data"]["fun"] in funs: publish = False if publish: - _logstash(url, event['data']) + _logstash(url, event["data"]) diff --git a/salt/engines/ircbot.py b/salt/engines/ircbot.py index 82945e1f20e..aa5d847d58c 100644 --- a/salt/engines/ircbot.py +++ b/salt/engines/ircbot.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" IRC Bot engine .. versionadded:: 2017.7.0 @@ -53,7 +53,7 @@ Example of usage [DEBUG ] Sending event: tag = salt/engines/ircbot/test/tag/ircbot; data = {'_stamp': '2016-11-28T14:34:16.633623', 'data': ['irc', 'is', 'useful']} -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libraries @@ -67,23 +67,36 @@ from collections import namedtuple import salt.ext.tornado.ioloop import salt.ext.tornado.iostream -import logging -log = logging.getLogger(__name__) - # Import salt libraries import salt.utils.event # Import 3rd-party libs from salt.ext import six +log = logging.getLogger(__name__) + + # Nothing listening here Event = namedtuple("Event", "source code line") PrivEvent = namedtuple("PrivEvent", "source nick user host code channel command line") class IRCClient(object): - def __init__(self, nick, host, port=6667, username=None, password=None, channels=None, use_ssl=False, - use_sasl=False, char='!', allow_hosts=False, allow_nicks=False, disable_query=True): + def __init__( + self, + nick, + host, + port=6667, + username=None, + password=None, + channels=None, + use_ssl=False, + use_sasl=False, + char="!", + allow_hosts=False, + allow_nicks=False, + disable_query=True, + ): self.nick = nick self.host = host self.port = port @@ -103,20 +116,28 @@ class IRCClient(object): def _connect(self): _sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) if self.ssl is True: - self._stream = salt.ext.tornado.iostream.SSLIOStream(_sock, ssl_options={'cert_reqs': ssl.CERT_NONE}) + self._stream = salt.ext.tornado.iostream.SSLIOStream( + _sock, ssl_options={"cert_reqs": ssl.CERT_NONE} + ) else: self._stream = salt.ext.tornado.iostream.IOStream(_sock) self._stream.set_close_callback(self.on_closed) self._stream.connect((self.host, self.port), self.on_connect) def read_messages(self): - self._stream.read_until('\r\n', self._message) + self._stream.read_until("\r\n", self._message) @staticmethod def _event(line): - log.debug('Received: %s', line) - search = re.match('^(?:(?P:[^ ]+) )?(?P[^ ]+)(?: (?P.*))?$', line) - source, code, line = search.group('source'), search.group('code'), search.group('line') + log.debug("Received: %s", line) + search = re.match( + "^(?:(?P:[^ ]+) )?(?P[^ ]+)(?: (?P.*))?$", line + ) + source, code, line = ( + search.group("source"), + search.group("code"), + search.group("line"), + ) return Event(source, code, line) def _allow_host(self, host): @@ -132,82 +153,113 @@ class IRCClient(object): return any([re.match(match, nick) for match in self.allow_nicks]) def _privmsg(self, event): - search = re.match('^:(?P[^!]+)!(?P[^@]+)@(?P.*)$', event.source) - nick, user, host = search.group('nick'), search.group('user'), search.group('host') - search = re.match('^(?P[^ ]+) :(?:{0}(?P[^ ]+)(?: (?P.*))?)?$'.format(self.char), event.line) + search = re.match( + "^:(?P[^!]+)!(?P[^@]+)@(?P.*)$", event.source + ) + nick, user, host = ( + search.group("nick"), + search.group("user"), + search.group("host"), + ) + search = re.match( + "^(?P[^ ]+) :(?:{0}(?P[^ ]+)(?: (?P.*))?)?$".format( + self.char + ), + event.line, + ) if search: - channel, command, line = search.group('channel'), search.group('command'), search.group('line') - if self.disable_query is True and not channel.startswith('#'): + channel, command, line = ( + search.group("channel"), + search.group("command"), + search.group("line"), + ) + if self.disable_query is True and not channel.startswith("#"): return if channel == self.nick: channel = nick - privevent = PrivEvent(event.source, nick, user, host, event.code, channel, command, line) - if (self._allow_nick(nick) or self._allow_host(host)) and hasattr(self, '_command_{0}'.format(command)): - getattr(self, '_command_{0}'.format(command))(privevent) + privevent = PrivEvent( + event.source, nick, user, host, event.code, channel, command, line + ) + if (self._allow_nick(nick) or self._allow_host(host)) and hasattr( + self, "_command_{0}".format(command) + ): + getattr(self, "_command_{0}".format(command))(privevent) def _command_echo(self, event): - message = 'PRIVMSG {0} :{1}'.format(event.channel, event.line) + message = "PRIVMSG {0} :{1}".format(event.channel, event.line) self.send_message(message) def _command_ping(self, event): - message = 'PRIVMSG {0} :{1}: pong'.format(event.channel, event.nick) + message = "PRIVMSG {0} :{1}: pong".format(event.channel, event.nick) self.send_message(message) def _command_event(self, event): - if __opts__.get('__role') == 'master': - fire_master = salt.utils.event.get_master_event(__opts__, __opts__['sock_dir']).fire_event + if __opts__.get("__role") == "master": + fire_master = salt.utils.event.get_master_event( + __opts__, __opts__["sock_dir"] + ).fire_event else: fire_master = None def fire(tag, msg): - ''' + """ How to fire the event - ''' + """ if fire_master: fire_master(msg, tag) else: - __salt__['event.send'](tag, msg) + __salt__["event.send"](tag, msg) - args = event.line.split(' ') + args = event.line.split(" ") tag = args[0] if len(args) > 1: - payload = {'data': args[1:]} + payload = {"data": args[1:]} else: - payload = {'data': []} + payload = {"data": []} - fire('salt/engines/ircbot/' + tag, payload) - message = 'PRIVMSG {0} :{1}: TaDa!'.format(event.channel, event.nick) + fire("salt/engines/ircbot/" + tag, payload) + message = "PRIVMSG {0} :{1}: TaDa!".format(event.channel, event.nick) self.send_message(message) def _message(self, raw): - raw = raw.rstrip(b'\r\n').decode('utf-8') + raw = raw.rstrip(b"\r\n").decode("utf-8") event = self._event(raw) if event.code == "PING": - salt.ext.tornado.ioloop.IOLoop.current().spawn_callback(self.send_message, "PONG {0}".format(event.line)) - elif event.code == 'PRIVMSG': - salt.ext.tornado.ioloop.IOLoop.current().spawn_callback(self._privmsg, event) + salt.ext.tornado.ioloop.IOLoop.current().spawn_callback( + self.send_message, "PONG {0}".format(event.line) + ) + elif event.code == "PRIVMSG": + salt.ext.tornado.ioloop.IOLoop.current().spawn_callback( + self._privmsg, event + ) self.read_messages() def join_channel(self, channel): - if not channel.startswith('#'): - channel = '#' + channel - self.send_message('JOIN {0}'.format(channel)) + if not channel.startswith("#"): + channel = "#" + channel + self.send_message("JOIN {0}".format(channel)) def on_connect(self): logging.info("on_connect") if self.sasl is True: - self.send_message('CAP REQ :sasl') - self.send_message('NICK {0}'.format(self.nick)) - self.send_message('USER saltstack 0 * :saltstack') + self.send_message("CAP REQ :sasl") + self.send_message("NICK {0}".format(self.nick)) + self.send_message("USER saltstack 0 * :saltstack") if self.password: if self.sasl is True: - authstring = base64.b64encode("{0}\x00{0}\x00{1}".format(self.username, self.password).encode()) - self.send_message('AUTHENTICATE PLAIN') - self.send_message('AUTHENTICATE {0}'.format(authstring)) - self.send_message('CAP END') + authstring = base64.b64encode( + "{0}\x00{0}\x00{1}".format(self.username, self.password).encode() + ) + self.send_message("AUTHENTICATE PLAIN") + self.send_message("AUTHENTICATE {0}".format(authstring)) + self.send_message("CAP END") else: - self.send_message('PRIVMSG NickServ :IDENTIFY {0} {1}'.format(self.username, self.password)) + self.send_message( + "PRIVMSG NickServ :IDENTIFY {0} {1}".format( + self.username, self.password + ) + ) for channel in self.channels: self.join_channel(channel) self.read_messages() @@ -217,14 +269,26 @@ class IRCClient(object): def send_message(self, line): if isinstance(line, six.string_types): - line = line.encode('utf-8') + line = line.encode("utf-8") log.debug("Sending: %s", line) - self._stream.write(line + b'\r\n') + self._stream.write(line + b"\r\n") -def start(nick, host, port=6667, username=None, password=None, channels=None, use_ssl=False, use_sasl=False, - char='!', allow_hosts=False, allow_nicks=False, disable_query=True): - ''' +def start( + nick, + host, + port=6667, + username=None, + password=None, + channels=None, + use_ssl=False, + use_sasl=False, + char="!", + allow_hosts=False, + allow_nicks=False, + disable_query=True, +): + """ IRC Bot for interacting with salt. nick @@ -279,7 +343,19 @@ def start(nick, host, port=6667, username=None, password=None, channels=None, us /mode +r # do not allow unauthenticated users into the channel It would also be possible to add a password to the irc channel, or only allow invited users to join. - ''' - client = IRCClient(nick, host, port, username, password, channels or [], use_ssl, use_sasl, char, - allow_hosts, allow_nicks, disable_query) + """ + client = IRCClient( + nick, + host, + port, + username, + password, + channels or [], + use_ssl, + use_sasl, + char, + allow_hosts, + allow_nicks, + disable_query, + ) client.io_loop.start() diff --git a/salt/engines/junos_syslog.py b/salt/engines/junos_syslog.py index 8de346c6409..a55b2b8dac7 100644 --- a/salt/engines/junos_syslog.py +++ b/salt/engines/junos_syslog.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Junos Syslog Engine ========================== @@ -84,18 +84,36 @@ Below is a sample syslog event which is received from the junos device: The source for parsing the syslog messages is taken from: https://gist.github.com/leandrosilva/3651640#file-xlog-py -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import re import logging +import re import time +import salt.utils.event as event + +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import range # pylint: disable=redefined-builtin + try: from twisted.internet.protocol import DatagramProtocol from twisted.internet import reactor, threads - from pyparsing import Word, alphas, Suppress, Combine, nums, string, \ - Optional, Regex, LineEnd, StringEnd, delimitedList + from pyparsing import ( + Word, + alphas, + Suppress, + Combine, + nums, + string, + Optional, + Regex, + LineEnd, + StringEnd, + delimitedList, + ) + HAS_TWISTED_AND_PYPARSING = True except ImportError: HAS_TWISTED_AND_PYPARSING = False @@ -104,41 +122,33 @@ except ImportError: class DatagramProtocol(object): pass -import salt.utils.event as event - -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=redefined-builtin # logging.basicConfig(level=logging.DEBUG) log = logging.getLogger(__name__) -__virtualname__ = 'junos_syslog' +__virtualname__ = "junos_syslog" def __virtual__(): - ''' + """ Load only if twisted and pyparsing libs are present. - ''' + """ if not HAS_TWISTED_AND_PYPARSING: - return (False, 'junos_syslog could not be loaded.' - ' Make sure you have twisted and pyparsing python libraries.') + return ( + False, + "junos_syslog could not be loaded." + " Make sure you have twisted and pyparsing python libraries.", + ) return True class _Parser(object): - def __init__(self): ints = Word(nums) EOL = LineEnd().suppress() # ip address of device - ipAddress = Optional( - delimitedList( - ints, - ".", - combine=True) + Suppress( - ":")) + ipAddress = Optional(delimitedList(ints, ".", combine=True) + Suppress(":")) # priority priority = Suppress("<") + ints + Suppress(">") @@ -154,18 +164,24 @@ class _Parser(object): hostname = Word(alphas + nums + "_" + "-" + ".") # daemon - daemon = Word(alphas + nums + "/" + "-" + "_" + ".") + Optional( - Suppress("[") + ints + Suppress("]")) + Suppress(":") + daemon = ( + Word(alphas + nums + "/" + "-" + "_" + ".") + + Optional(Suppress("[") + ints + Suppress("]")) + + Suppress(":") + ) # message message = Regex(".*") # pattern build - self.__pattern = ipAddress + priority + timestamp + \ - hostname + daemon + message + StringEnd() | EOL + self.__pattern = ( + ipAddress + priority + timestamp + hostname + daemon + message + StringEnd() + | EOL + ) - self.__pattern_without_daemon = ipAddress + priority + \ - timestamp + hostname + message + StringEnd() | EOL + self.__pattern_without_daemon = ( + ipAddress + priority + timestamp + hostname + message + StringEnd() | EOL + ) def parse(self, line): try: @@ -182,10 +198,10 @@ class _Parser(object): payload["facility"] = payload["priority"] >> 3 payload["timestamp"] = time.strftime("%Y-%m-%d %H:%M:%S") payload["hostname"] = parsed[4] - payload["daemon"] = 'unknown' + payload["daemon"] = "unknown" payload["message"] = parsed[5] - payload["event"] = 'SYSTEM' - payload['raw'] = line + payload["event"] = "SYSTEM" + payload["raw"] = line return payload elif len(parsed) == 7: payload = {} @@ -196,8 +212,8 @@ class _Parser(object): payload["hostname"] = parsed[4] payload["daemon"] = parsed[5] payload["message"] = parsed[6] - payload["event"] = 'SYSTEM' - obj = re.match(r'(\w+): (.*)', payload["message"]) + payload["event"] = "SYSTEM" + obj = re.match(r"(\w+): (.*)", payload["message"]) if obj: payload["message"] = obj.group(2) payload["raw"] = line @@ -212,8 +228,8 @@ class _Parser(object): payload["daemon"] = parsed[5] payload["pid"] = parsed[6] payload["message"] = parsed[7] - payload["event"] = 'SYSTEM' - obj = re.match(r'(\w+): (.*)', payload["message"]) + payload["event"] = "SYSTEM" + obj = re.match(r"(\w+): (.*)", payload["message"]) if obj: payload["event"] = obj.group(1) payload["message"] = obj.group(2) @@ -230,8 +246,8 @@ class _Parser(object): payload["daemon"] = parsed[6] payload["pid"] = parsed[7] payload["message"] = parsed[8] - payload["event"] = 'SYSTEM' - obj = re.match(r'(\w+): (.*)', payload["message"]) + payload["event"] = "SYSTEM" + obj = re.match(r"(\w+): (.*)", payload["message"]) if obj: payload["event"] = obj.group(1) payload["message"] = obj.group(2) @@ -240,7 +256,6 @@ class _Parser(object): class _SyslogServerFactory(DatagramProtocol): - def __init__(self, options): self.options = options self.obj = _Parser() @@ -254,40 +269,43 @@ class _SyslogServerFactory(DatagramProtocol): "daemon", "pid", "message", - "event"] - if 'topic' in self.options: + "event", + ] + if "topic" in self.options: # self.title = 'jnpr/syslog' # To remove the stray '/', if not removed splitting the topic # won't work properly. Eg: '/jnpr/syslog/event' won't be split # properly if the starting '/' is not stripped - self.options['topic'] = options['topic'].strip('/') - topics = options['topic'].split("/") + self.options["topic"] = options["topic"].strip("/") + topics = options["topic"].split("/") self.title = topics - if len(topics) < 2 or topics[0] != 'jnpr' or topics[1] != 'syslog': + if len(topics) < 2 or topics[0] != "jnpr" or topics[1] != "syslog": log.debug( 'The topic specified in configuration should start with \ - "jnpr/syslog". Using the default topic.') - self.title = ['jnpr', 'syslog', 'hostname', 'event'] + "jnpr/syslog". Using the default topic.' + ) + self.title = ["jnpr", "syslog", "hostname", "event"] else: for i in range(2, len(topics)): if topics[i] not in data: log.debug( - 'Please check the topic specified. \ + "Please check the topic specified. \ Only the following keywords can be specified \ in the topic: hostip, priority, severity, \ facility, timestamp, hostname, daemon, pid, \ - message, event. Using the default topic.') - self.title = ['jnpr', 'syslog', 'hostname', 'event'] + message, event. Using the default topic." + ) + self.title = ["jnpr", "syslog", "hostname", "event"] break # We are done processing the topic. All other arguments are the # filters given by the user. While processing the filters we don't # explicitly ignore the 'topic', but delete it here itself. - del self.options['topic'] + del self.options["topic"] else: - self.title = ['jnpr', 'syslog', 'hostname', 'event'] + self.title = ["jnpr", "syslog", "hostname", "event"] def parseData(self, data, host, port, options): - ''' + """ This function will parse the raw syslog data, dynamically create the topic according to the topic specified by the user (if specified) and decide whether to send the syslog data as an event on the master bus, @@ -300,12 +318,11 @@ class _SyslogServerFactory(DatagramProtocol): :return: The result dictionary which contains the data and the topic, if the event is to be sent on the bus. - ''' + """ data = self.obj.parse(data) - data['hostip'] = host + data["hostip"] = host log.debug( - 'Junos Syslog - received %s from %s, sent from port %s', - data, host, port + "Junos Syslog - received %s from %s, sent from port %s", data, host, port ) send_this_event = True @@ -323,73 +340,68 @@ class _SyslogServerFactory(DatagramProtocol): send_this_event = False break else: - raise Exception( - 'Arguments in config not specified properly') + raise Exception("Arguments in config not specified properly") else: raise Exception( - 'Please check the arguments given to junos engine in the\ - configuration file') + "Please check the arguments given to junos engine in the\ + configuration file" + ) if send_this_event: - if 'event' in data: - topic = 'jnpr/syslog' + if "event" in data: + topic = "jnpr/syslog" for i in range(2, len(self.title)): - topic += '/' + six.text_type(data[self.title[i]]) + topic += "/" + six.text_type(data[self.title[i]]) log.debug( - 'Junos Syslog - sending this event on the bus: %s from %s', - data, host + "Junos Syslog - sending this event on the bus: %s from %s", + data, + host, ) - result = {'send': True, 'data': data, 'topic': topic} + result = {"send": True, "data": data, "topic": topic} return result else: - raise Exception( - 'The incoming event data could not be parsed properly.') + raise Exception("The incoming event data could not be parsed properly.") else: - result = {'send': False} + result = {"send": False} return result def send_event_to_salt(self, result): - ''' + """ This function identifies whether the engine is running on the master or the minion and sends the data to the master event bus accordingly. :param result: It's a dictionary which has the final data and topic. - ''' - if result['send']: - data = result['data'] - topic = result['topic'] + """ + if result["send"]: + data = result["data"] + topic = result["topic"] # If the engine is run on master, get the event bus and send the # parsed event. - if __opts__['__role'] == 'master': - event.get_master_event(__opts__, - __opts__['sock_dir'] - ).fire_event(data, topic) + if __opts__["__role"] == "master": + event.get_master_event(__opts__, __opts__["sock_dir"]).fire_event( + data, topic + ) # If the engine is run on minion, use the fire_master execution # module to send event on the master bus. else: - __salt__['event.fire_master'](data=data, tag=topic) + __salt__["event.fire_master"](data=data, tag=topic) def handle_error(self, err_msg): - ''' + """ Log the error messages. - ''' + """ log.error(err_msg.getErrorMessage) def datagramReceived(self, data, connection_details): (host, port) = connection_details - d = threads.deferToThread( - self.parseData, - data, - host, - port, - self.options) + d = threads.deferToThread(self.parseData, data, host, port, self.options) d.addCallbacks(self.send_event_to_salt, self.handle_error) def start(port=516, **kwargs): - log.info('Starting junos syslog engine (port %s)', port) + log.info("Starting junos syslog engine (port %s)", port) reactor.listenUDP(port, _SyslogServerFactory(kwargs)) reactor.run() diff --git a/salt/engines/libvirt_events.py b/salt/engines/libvirt_events.py index cdb5d1dfe84..c2594e821ae 100644 --- a/salt/engines/libvirt_events.py +++ b/salt/engines/libvirt_events.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" An engine that listens for libvirt events and resends them to the salt event bus. The minimal configuration is the following and will listen to all events on the @@ -63,9 +63,10 @@ A polkit rule like the following one will allow `salt` user to connect to libvir :depends: libvirt 1.0.0+ python binding .. versionadded:: 2019.2.0 -''' +""" + +from __future__ import absolute_import, print_function, unicode_literals -from __future__ import absolute_import, unicode_literals, print_function import logging # Import salt libs @@ -73,6 +74,7 @@ import salt.utils.event # pylint: disable=no-name-in-module,import-error from salt.ext.six.moves.urllib.parse import urlparse + # pylint: enable=no-name-in-module,import-error log = logging.getLogger(__name__) @@ -85,112 +87,119 @@ except ImportError: def __virtual__(): - ''' + """ Only load if libvirt python binding is present - ''' + """ if libvirt is None: - msg = 'libvirt module not found' + msg = "libvirt module not found" elif libvirt.getVersion() < 1000000: - msg = 'libvirt >= 1.0.0 required' + msg = "libvirt >= 1.0.0 required" else: - msg = '' + msg = "" return not bool(msg), msg REGISTER_FUNCTIONS = { - 'domain': 'domainEventRegisterAny', - 'network': 'networkEventRegisterAny', - 'pool': 'storagePoolEventRegisterAny', - 'nodedev': 'nodeDeviceEventRegisterAny', - 'secret': 'secretEventRegisterAny' + "domain": "domainEventRegisterAny", + "network": "networkEventRegisterAny", + "pool": "storagePoolEventRegisterAny", + "nodedev": "nodeDeviceEventRegisterAny", + "secret": "secretEventRegisterAny", } # Handle either BLOCK_JOB or BLOCK_JOB_2, but prefer the latter -if hasattr(libvirt, 'VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2'): - BLOCK_JOB_ID = 'VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2' +if hasattr(libvirt, "VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2"): + BLOCK_JOB_ID = "VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2" else: - BLOCK_JOB_ID = 'VIR_DOMAIN_EVENT_ID_BLOCK_JOB' + BLOCK_JOB_ID = "VIR_DOMAIN_EVENT_ID_BLOCK_JOB" CALLBACK_DEFS = { - 'domain': (('lifecycle', None), - ('reboot', None), - ('rtc_change', None), - ('watchdog', None), - ('graphics', None), - ('io_error', 'VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON'), - ('control_error', None), - ('disk_change', None), - ('tray_change', None), - ('pmwakeup', None), - ('pmsuspend', None), - ('balloon_change', None), - ('pmsuspend_disk', None), - ('device_removed', None), - ('block_job', BLOCK_JOB_ID), - ('tunable', None), - ('agent_lifecycle', None), - ('device_added', None), - ('migration_iteration', None), - ('job_completed', None), - ('device_removal_failed', None), - ('metadata_change', None), - ('block_threshold', None)), - 'network': (('lifecycle', None),), - 'pool': (('lifecycle', None), - ('refresh', None)), - 'nodedev': (('lifecycle', None), - ('update', None)), - 'secret': (('lifecycle', None), - ('value_changed', None)) + "domain": ( + ("lifecycle", None), + ("reboot", None), + ("rtc_change", None), + ("watchdog", None), + ("graphics", None), + ("io_error", "VIR_DOMAIN_EVENT_ID_IO_ERROR_REASON"), + ("control_error", None), + ("disk_change", None), + ("tray_change", None), + ("pmwakeup", None), + ("pmsuspend", None), + ("balloon_change", None), + ("pmsuspend_disk", None), + ("device_removed", None), + ("block_job", BLOCK_JOB_ID), + ("tunable", None), + ("agent_lifecycle", None), + ("device_added", None), + ("migration_iteration", None), + ("job_completed", None), + ("device_removal_failed", None), + ("metadata_change", None), + ("block_threshold", None), + ), + "network": (("lifecycle", None),), + "pool": (("lifecycle", None), ("refresh", None)), + "nodedev": (("lifecycle", None), ("update", None)), + "secret": (("lifecycle", None), ("value_changed", None)), } def _compute_subprefix(attr): - ''' + """ Get the part before the first '_' or the end of attr including the potential '_' - ''' - return ''.join((attr.split('_')[0], '_' if len(attr.split('_')) > 1 else '')) + """ + return "".join((attr.split("_")[0], "_" if len(attr.split("_")) > 1 else "")) def _get_libvirt_enum_string(prefix, value): - ''' + """ Convert the libvirt enum integer value into a human readable string. :param prefix: start of the libvirt attribute to look for. :param value: integer to convert to string - ''' - attributes = [attr[len(prefix):] for attr in libvirt.__dict__ if attr.startswith(prefix)] + """ + attributes = [ + attr[len(prefix) :] for attr in libvirt.__dict__ if attr.startswith(prefix) + ] # Filter out the values starting with a common base as they match another enum prefixes = [_compute_subprefix(p) for p in attributes] counts = {p: prefixes.count(p) for p in prefixes} - sub_prefixes = [p for p, count in counts.items() if count > 1 or (p.endswith('_') and p[:-1] in prefixes)] - filtered = [attr for attr in attributes if _compute_subprefix(attr) not in sub_prefixes] + sub_prefixes = [ + p + for p, count in counts.items() + if count > 1 or (p.endswith("_") and p[:-1] in prefixes) + ] + filtered = [ + attr for attr in attributes if _compute_subprefix(attr) not in sub_prefixes + ] for candidate in filtered: - if value == getattr(libvirt, ''.join((prefix, candidate))): - name = candidate.lower().replace('_', ' ') + if value == getattr(libvirt, "".join((prefix, candidate))): + name = candidate.lower().replace("_", " ") return name - return 'unknown' + return "unknown" def _get_domain_event_detail(event, detail): - ''' + """ Convert event and detail numeric values into a tuple of human readable strings - ''' - event_name = _get_libvirt_enum_string('VIR_DOMAIN_EVENT_', event) - if event_name == 'unknown': - return event_name, 'unknown' + """ + event_name = _get_libvirt_enum_string("VIR_DOMAIN_EVENT_", event) + if event_name == "unknown": + return event_name, "unknown" - prefix = 'VIR_DOMAIN_EVENT_{0}_'.format(event_name.upper()) + prefix = "VIR_DOMAIN_EVENT_{0}_".format(event_name.upper()) detail_name = _get_libvirt_enum_string(prefix, detail) return event_name, detail_name def _salt_send_event(opaque, conn, data): - ''' + """ Convenience function adding common data to the event and sending it on the salt event bus. @@ -198,10 +207,10 @@ def _salt_send_event(opaque, conn, data): This is a dict with 'prefix', 'object' and 'event' keys. :param conn: libvirt connection :param data: additional event data dict to send - ''' - tag_prefix = opaque['prefix'] - object_type = opaque['object'] - event_type = opaque['event'] + """ + tag_prefix = opaque["prefix"] + object_type = opaque["object"] + event_type = opaque["event"] # Prepare the connection URI to fit in the tag # qemu+ssh://user@host:1234/system -> qemu+ssh/user@host:1234/system @@ -209,30 +218,28 @@ def _salt_send_event(opaque, conn, data): uri_tag = [uri.scheme] if uri.netloc: uri_tag.append(uri.netloc) - path = uri.path.strip('/') + path = uri.path.strip("/") if path: uri_tag.append(path) uri_str = "/".join(uri_tag) # Append some common data - all_data = { - 'uri': conn.getURI() - } + all_data = {"uri": conn.getURI()} all_data.update(data) - tag = '/'.join((tag_prefix, uri_str, object_type, event_type)) + tag = "/".join((tag_prefix, uri_str, object_type, event_type)) # Actually send the event in salt - if __opts__.get('__role') == 'master': - salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir']).fire_event(all_data, tag) + if __opts__.get("__role") == "master": + salt.utils.event.get_master_event(__opts__, __opts__["sock_dir"]).fire_event( + all_data, tag + ) else: - __salt__['event.send'](tag, all_data) + __salt__["event.send"](tag, all_data) def _salt_send_domain_event(opaque, conn, domain, event, event_data): - ''' + """ Helper function send a salt event for a libvirt domain. :param opaque: the opaque data that is passed to the callback. @@ -241,375 +248,428 @@ def _salt_send_domain_event(opaque, conn, domain, event, event_data): :param domain: name of the domain related to the event :param event: name of the event :param event_data: additional event data dict to send - ''' + """ data = { - 'domain': { - 'name': domain.name(), - 'id': domain.ID(), - 'uuid': domain.UUIDString() + "domain": { + "name": domain.name(), + "id": domain.ID(), + "uuid": domain.UUIDString(), }, - 'event': event + "event": event, } data.update(event_data) _salt_send_event(opaque, conn, data) def _domain_event_lifecycle_cb(conn, domain, event, detail, opaque): - ''' + """ Domain lifecycle events handler - ''' + """ event_str, detail_str = _get_domain_event_detail(event, detail) - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'event': event_str, - 'detail': detail_str - }) + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + {"event": event_str, "detail": detail_str}, + ) def _domain_event_reboot_cb(conn, domain, opaque): - ''' + """ Domain reboot events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], {}) + """ + _salt_send_domain_event(opaque, conn, domain, opaque["event"], {}) def _domain_event_rtc_change_cb(conn, domain, utcoffset, opaque): - ''' + """ Domain RTC change events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'utcoffset': utcoffset - }) + """ + _salt_send_domain_event( + opaque, conn, domain, opaque["event"], {"utcoffset": utcoffset} + ) def _domain_event_watchdog_cb(conn, domain, action, opaque): - ''' + """ Domain watchdog events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'action': _get_libvirt_enum_string('VIR_DOMAIN_EVENT_WATCHDOG_', action) - }) + """ + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + {"action": _get_libvirt_enum_string("VIR_DOMAIN_EVENT_WATCHDOG_", action)}, + ) def _domain_event_io_error_cb(conn, domain, srcpath, devalias, action, reason, opaque): - ''' + """ Domain I/O Error events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'srcPath': srcpath, - 'dev': devalias, - 'action': _get_libvirt_enum_string('VIR_DOMAIN_EVENT_IO_ERROR_', action), - 'reason': reason - }) + """ + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + { + "srcPath": srcpath, + "dev": devalias, + "action": _get_libvirt_enum_string("VIR_DOMAIN_EVENT_IO_ERROR_", action), + "reason": reason, + }, + ) -def _domain_event_graphics_cb(conn, domain, phase, local, remote, auth, subject, opaque): - ''' +def _domain_event_graphics_cb( + conn, domain, phase, local, remote, auth, subject, opaque +): + """ Domain graphics events handler - ''' - prefix = 'VIR_DOMAIN_EVENT_GRAPHICS_' + """ + prefix = "VIR_DOMAIN_EVENT_GRAPHICS_" def get_address(addr): - ''' + """ transform address structure into event data piece - ''' - return {'family': _get_libvirt_enum_string('{0}_ADDRESS_'.format(prefix), addr['family']), - 'node': addr['node'], - 'service': addr['service']} + """ + return { + "family": _get_libvirt_enum_string( + "{0}_ADDRESS_".format(prefix), addr["family"] + ), + "node": addr["node"], + "service": addr["service"], + } - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'phase': _get_libvirt_enum_string(prefix, phase), - 'local': get_address(local), - 'remote': get_address(remote), - 'authScheme': auth, - 'subject': [{'type': item[0], 'name': item[1]} for item in subject] - }) + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + { + "phase": _get_libvirt_enum_string(prefix, phase), + "local": get_address(local), + "remote": get_address(remote), + "authScheme": auth, + "subject": [{"type": item[0], "name": item[1]} for item in subject], + }, + ) def _domain_event_control_error_cb(conn, domain, opaque): - ''' + """ Domain control error events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], {}) + """ + _salt_send_domain_event(opaque, conn, domain, opaque["event"], {}) def _domain_event_disk_change_cb(conn, domain, old_src, new_src, dev, reason, opaque): - ''' + """ Domain disk change events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'oldSrcPath': old_src, - 'newSrcPath': new_src, - 'dev': dev, - 'reason': _get_libvirt_enum_string('VIR_DOMAIN_EVENT_DISK_', reason) - }) + """ + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + { + "oldSrcPath": old_src, + "newSrcPath": new_src, + "dev": dev, + "reason": _get_libvirt_enum_string("VIR_DOMAIN_EVENT_DISK_", reason), + }, + ) def _domain_event_tray_change_cb(conn, domain, dev, reason, opaque): - ''' + """ Domain tray change events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'dev': dev, - 'reason': _get_libvirt_enum_string('VIR_DOMAIN_EVENT_TRAY_CHANGE_', reason) - }) + """ + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + { + "dev": dev, + "reason": _get_libvirt_enum_string("VIR_DOMAIN_EVENT_TRAY_CHANGE_", reason), + }, + ) def _domain_event_pmwakeup_cb(conn, domain, reason, opaque): - ''' + """ Domain wakeup events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'reason': 'unknown' # currently unused - }) + """ + _salt_send_domain_event( + opaque, conn, domain, opaque["event"], {"reason": "unknown"} # currently unused + ) def _domain_event_pmsuspend_cb(conn, domain, reason, opaque): - ''' + """ Domain suspend events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'reason': 'unknown' # currently unused - }) + """ + _salt_send_domain_event( + opaque, conn, domain, opaque["event"], {"reason": "unknown"} # currently unused + ) def _domain_event_balloon_change_cb(conn, domain, actual, opaque): - ''' + """ Domain balloon change events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'actual': actual - }) + """ + _salt_send_domain_event(opaque, conn, domain, opaque["event"], {"actual": actual}) def _domain_event_pmsuspend_disk_cb(conn, domain, reason, opaque): - ''' + """ Domain disk suspend events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'reason': 'unknown' # currently unused - }) + """ + _salt_send_domain_event( + opaque, conn, domain, opaque["event"], {"reason": "unknown"} # currently unused + ) def _domain_event_block_job_cb(conn, domain, disk, job_type, status, opaque): - ''' + """ Domain block job events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'disk': disk, - 'type': _get_libvirt_enum_string('VIR_DOMAIN_BLOCK_JOB_TYPE_', job_type), - 'status': _get_libvirt_enum_string('VIR_DOMAIN_BLOCK_JOB_', status) - }) + """ + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + { + "disk": disk, + "type": _get_libvirt_enum_string("VIR_DOMAIN_BLOCK_JOB_TYPE_", job_type), + "status": _get_libvirt_enum_string("VIR_DOMAIN_BLOCK_JOB_", status), + }, + ) def _domain_event_device_removed_cb(conn, domain, dev, opaque): - ''' + """ Domain device removal events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'dev': dev - }) + """ + _salt_send_domain_event(opaque, conn, domain, opaque["event"], {"dev": dev}) def _domain_event_tunable_cb(conn, domain, params, opaque): - ''' + """ Domain tunable events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'params': params - }) + """ + _salt_send_domain_event(opaque, conn, domain, opaque["event"], {"params": params}) # pylint: disable=invalid-name def _domain_event_agent_lifecycle_cb(conn, domain, state, reason, opaque): - ''' + """ Domain agent lifecycle events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'state': _get_libvirt_enum_string('VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_', state), - 'reason': _get_libvirt_enum_string('VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_', reason) - }) + """ + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + { + "state": _get_libvirt_enum_string( + "VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_STATE_", state + ), + "reason": _get_libvirt_enum_string( + "VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_", reason + ), + }, + ) def _domain_event_device_added_cb(conn, domain, dev, opaque): - ''' + """ Domain device addition events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'dev': dev - }) + """ + _salt_send_domain_event(opaque, conn, domain, opaque["event"], {"dev": dev}) # pylint: disable=invalid-name def _domain_event_migration_iteration_cb(conn, domain, iteration, opaque): - ''' + """ Domain migration iteration events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'iteration': iteration - }) + """ + _salt_send_domain_event( + opaque, conn, domain, opaque["event"], {"iteration": iteration} + ) def _domain_event_job_completed_cb(conn, domain, params, opaque): - ''' + """ Domain job completion events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'params': params - }) + """ + _salt_send_domain_event(opaque, conn, domain, opaque["event"], {"params": params}) def _domain_event_device_removal_failed_cb(conn, domain, dev, opaque): - ''' + """ Domain device removal failure events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'dev': dev - }) + """ + _salt_send_domain_event(opaque, conn, domain, opaque["event"], {"dev": dev}) def _domain_event_metadata_change_cb(conn, domain, mtype, nsuri, opaque): - ''' + """ Domain metadata change events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'type': _get_libvirt_enum_string('VIR_DOMAIN_METADATA_', mtype), - 'nsuri': nsuri - }) + """ + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + { + "type": _get_libvirt_enum_string("VIR_DOMAIN_METADATA_", mtype), + "nsuri": nsuri, + }, + ) -def _domain_event_block_threshold_cb(conn, domain, dev, path, threshold, excess, opaque): - ''' +def _domain_event_block_threshold_cb( + conn, domain, dev, path, threshold, excess, opaque +): + """ Domain block threshold events handler - ''' - _salt_send_domain_event(opaque, conn, domain, opaque['event'], { - 'dev': dev, - 'path': path, - 'threshold': threshold, - 'excess': excess - }) + """ + _salt_send_domain_event( + opaque, + conn, + domain, + opaque["event"], + {"dev": dev, "path": path, "threshold": threshold, "excess": excess}, + ) def _network_event_lifecycle_cb(conn, net, event, detail, opaque): - ''' + """ Network lifecycle events handler - ''' + """ - _salt_send_event(opaque, conn, { - 'network': { - 'name': net.name(), - 'uuid': net.UUIDString() + _salt_send_event( + opaque, + conn, + { + "network": {"name": net.name(), "uuid": net.UUIDString()}, + "event": _get_libvirt_enum_string("VIR_NETWORK_EVENT_", event), + "detail": "unknown", # currently unused }, - 'event': _get_libvirt_enum_string('VIR_NETWORK_EVENT_', event), - 'detail': 'unknown' # currently unused - }) + ) def _pool_event_lifecycle_cb(conn, pool, event, detail, opaque): - ''' + """ Storage pool lifecycle events handler - ''' - _salt_send_event(opaque, conn, { - 'pool': { - 'name': pool.name(), - 'uuid': pool.UUIDString() + """ + _salt_send_event( + opaque, + conn, + { + "pool": {"name": pool.name(), "uuid": pool.UUIDString()}, + "event": _get_libvirt_enum_string("VIR_STORAGE_POOL_EVENT_", event), + "detail": "unknown", # currently unused }, - 'event': _get_libvirt_enum_string('VIR_STORAGE_POOL_EVENT_', event), - 'detail': 'unknown' # currently unused - }) + ) def _pool_event_refresh_cb(conn, pool, opaque): - ''' + """ Storage pool refresh events handler - ''' - _salt_send_event(opaque, conn, { - 'pool': { - 'name': pool.name(), - 'uuid': pool.UUIDString() + """ + _salt_send_event( + opaque, + conn, + { + "pool": {"name": pool.name(), "uuid": pool.UUIDString()}, + "event": opaque["event"], }, - 'event': opaque['event'] - }) + ) def _nodedev_event_lifecycle_cb(conn, dev, event, detail, opaque): - ''' + """ Node device lifecycle events handler - ''' - _salt_send_event(opaque, conn, { - 'nodedev': { - 'name': dev.name() + """ + _salt_send_event( + opaque, + conn, + { + "nodedev": {"name": dev.name()}, + "event": _get_libvirt_enum_string("VIR_NODE_DEVICE_EVENT_", event), + "detail": "unknown", # currently unused }, - 'event': _get_libvirt_enum_string('VIR_NODE_DEVICE_EVENT_', event), - 'detail': 'unknown' # currently unused - }) + ) def _nodedev_event_update_cb(conn, dev, opaque): - ''' + """ Node device update events handler - ''' - _salt_send_event(opaque, conn, { - 'nodedev': { - 'name': dev.name() - }, - 'event': opaque['event'] - }) + """ + _salt_send_event( + opaque, conn, {"nodedev": {"name": dev.name()}, "event": opaque["event"]} + ) def _secret_event_lifecycle_cb(conn, secret, event, detail, opaque): - ''' + """ Secret lifecycle events handler - ''' - _salt_send_event(opaque, conn, { - 'secret': { - 'uuid': secret.UUIDString() + """ + _salt_send_event( + opaque, + conn, + { + "secret": {"uuid": secret.UUIDString()}, + "event": _get_libvirt_enum_string("VIR_SECRET_EVENT_", event), + "detail": "unknown", # currently unused }, - 'event': _get_libvirt_enum_string('VIR_SECRET_EVENT_', event), - 'detail': 'unknown' # currently unused - }) + ) def _secret_event_value_changed_cb(conn, secret, opaque): - ''' + """ Secret value change events handler - ''' - _salt_send_event(opaque, conn, { - 'secret': { - 'uuid': secret.UUIDString() - }, - 'event': opaque['event'] - }) + """ + _salt_send_event( + opaque, + conn, + {"secret": {"uuid": secret.UUIDString()}, "event": opaque["event"]}, + ) def _cleanup(cnx): - ''' + """ Close the libvirt connection :param cnx: libvirt connection - ''' - log.debug('Closing libvirt connection: %s', cnx.getURI()) + """ + log.debug("Closing libvirt connection: %s", cnx.getURI()) cnx.close() def _callbacks_cleanup(cnx, callback_ids): - ''' + """ Unregister all the registered callbacks :param cnx: libvirt connection :param callback_ids: dictionary mapping a libvirt object type to an ID list of callbacks to deregister - ''' + """ for obj, ids in callback_ids.items(): register_name = REGISTER_FUNCTIONS[obj] - deregister_name = register_name.replace('Reg', 'Dereg') + deregister_name = register_name.replace("Reg", "Dereg") deregister = getattr(cnx, deregister_name) for callback_id in ids: deregister(callback_id) def _register_callback(cnx, tag_prefix, obj, event, real_id): - ''' + """ Helper function registering a callback :param cnx: libvirt connection @@ -620,10 +680,10 @@ def _register_callback(cnx, tag_prefix, obj, event, real_id): :param real_id: the libvirt name of an alternative event id to use or None :rtype integer value needed to deregister the callback - ''' + """ libvirt_name = real_id if real_id is None: - libvirt_name = 'VIR_{0}_EVENT_ID_{1}'.format(obj, event).upper() + libvirt_name = "VIR_{0}_EVENT_ID_{1}".format(obj, event).upper() if not hasattr(libvirt, libvirt_name): log.warning('Skipping "%s/%s" events: libvirt too old', obj, event) @@ -633,34 +693,34 @@ def _register_callback(cnx, tag_prefix, obj, event, real_id): callback_name = "_{0}_event_{1}_cb".format(obj, event) callback = globals().get(callback_name, None) if callback is None: - log.error('Missing function %s in engine', callback_name) + log.error("Missing function %s in engine", callback_name) return None register = getattr(cnx, REGISTER_FUNCTIONS[obj]) - return register(None, libvirt_id, callback, - {'prefix': tag_prefix, - 'object': obj, - 'event': event}) + return register( + None, + libvirt_id, + callback, + {"prefix": tag_prefix, "object": obj, "event": event}, + ) def _append_callback_id(ids, obj, callback_id): - ''' + """ Helper function adding a callback ID to the IDs dict. The callback ids dict maps an object to event callback ids. :param ids: dict of callback IDs to update :param obj: one of the keys of REGISTER_FUNCTIONS :param callback_id: the result of _register_callback - ''' + """ if obj not in ids: ids[obj] = [] ids[obj].append(callback_id) -def start(uri=None, - tag_prefix='salt/engines/libvirt_events', - filters=None): - ''' +def start(uri=None, tag_prefix="salt/engines/libvirt_events", filters=None): + """ Listen to libvirt events and forward them to salt. :param uri: libvirt URI to listen on. @@ -668,14 +728,14 @@ def start(uri=None, :param tag_prefix: the begining of the salt event tag to use. Defaults to 'salt/engines/libvirt_events' :param filters: the list of event of listen on. Defaults to 'all' - ''' + """ if filters is None: - filters = ['all'] + filters = ["all"] try: libvirt.virEventRegisterDefaultImpl() cnx = libvirt.openReadOnly(uri) - log.debug('Opened libvirt uri: %s', cnx.getURI()) + log.debug("Opened libvirt uri: %s", cnx.getURI()) callback_ids = {} all_filters = "all" in filters @@ -683,17 +743,20 @@ def start(uri=None, for obj, event_defs in CALLBACK_DEFS.items(): for event, real_id in event_defs: event_filter = "/".join((obj, event)) - if event_filter not in filters and obj not in filters and not all_filters: + if ( + event_filter not in filters + and obj not in filters + and not all_filters + ): continue - registered_id = _register_callback(cnx, tag_prefix, - obj, event, real_id) + registered_id = _register_callback(cnx, tag_prefix, obj, event, real_id) if registered_id: _append_callback_id(callback_ids, obj, registered_id) exit_loop = False while not exit_loop: exit_loop = libvirt.virEventRunDefaultImpl() < 0 - log.debug('=== in the loop exit_loop %s ===', exit_loop) + log.debug("=== in the loop exit_loop %s ===", exit_loop) except Exception as err: # pylint: disable=broad-except log.exception(err) diff --git a/salt/engines/logentries.py b/salt/engines/logentries.py index 65e7ad0a567..83f622eac4d 100644 --- a/salt/engines/logentries.py +++ b/salt/engines/logentries.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An engine that sends events to the Logentries logging service. :maintainer: Jimmy Tang (jimmy_tang@rapid7.com) @@ -41,8 +41,17 @@ To test this engine salt '*' test.ping cmd.run uptime -''' +""" from __future__ import absolute_import, print_function, unicode_literals + +import logging +import random + +# Import Python libs +import socket +import time +import uuid + # Import Salt libs import salt.utils.event import salt.utils.json @@ -50,6 +59,7 @@ import salt.utils.json # Import third party libs try: import certifi + HAS_CERTIFI = True except ImportError: HAS_CERTIFI = False @@ -58,16 +68,11 @@ except ImportError: # encrypted tcp connection try: import ssl + HAS_SSL = True except ImportError: # for systems without TLS support. HAS_SSL = False -# Import Python libs -import socket -import random -import time -import uuid -import logging log = logging.getLogger(__name__) @@ -77,11 +82,9 @@ def __virtual__(): class PlainTextSocketAppender(object): - def __init__(self, - verbose=True, - LE_API='data.logentries.com', - LE_PORT=80, - LE_TLS_PORT=443): + def __init__( + self, verbose=True, LE_API="data.logentries.com", LE_PORT=80, LE_TLS_PORT=443 + ): self.LE_API = LE_API self.LE_PORT = LE_PORT @@ -89,10 +92,12 @@ class PlainTextSocketAppender(object): self.MIN_DELAY = 0.1 self.MAX_DELAY = 10 # Error message displayed when an incorrect Token has been detected - self.INVALID_TOKEN = ("\n\nIt appears the LOGENTRIES_TOKEN " - "parameter you entered is incorrect!\n\n") + self.INVALID_TOKEN = ( + "\n\nIt appears the LOGENTRIES_TOKEN " + "parameter you entered is incorrect!\n\n" + ) # Encoded unicode line separator - self.LINE_SEP = salt.utils.stringutils.to_str('\u2028') + self.LINE_SEP = salt.utils.stringutils.to_str("\u2028") self.verbose = verbose self._conn = None @@ -111,7 +116,7 @@ class PlainTextSocketAppender(object): return except Exception: # pylint: disable=broad-except if self.verbose: - log.warning('Unable to connect to Logentries') + log.warning("Unable to connect to Logentries") root_delay *= 2 if root_delay > self.MAX_DELAY: @@ -130,7 +135,9 @@ class PlainTextSocketAppender(object): def put(self, data): # Replace newlines with Unicode line separator for multi-line events - multiline = data.replace('\n', self.LINE_SEP) + str('\n') # future lint: disable=blacklisted-function + multiline = data.replace("\n", self.LINE_SEP) + str( + "\n" + ) # future lint: disable=blacklisted-function # Send data, reconnect if needed while True: try: @@ -145,6 +152,7 @@ class PlainTextSocketAppender(object): try: import ssl + HAS_SSL = True except ImportError: # for systems without TLS support. SocketAppender = PlainTextSocketAppender @@ -160,11 +168,11 @@ else: certfile=None, server_side=False, cert_reqs=ssl.CERT_REQUIRED, - ssl_version=getattr( - ssl, 'PROTOCOL_TLSv1_2', ssl.PROTOCOL_TLSv1), + ssl_version=getattr(ssl, "PROTOCOL_TLSv1_2", ssl.PROTOCOL_TLSv1), ca_certs=certifi.where(), do_handshake_on_connect=True, - suppress_ragged_eofs=True, ) + suppress_ragged_eofs=True, + ) sock.connect((self.LE_API, self.LE_TLS_PORT)) self._conn = sock @@ -172,34 +180,36 @@ else: def event_bus_context(opts): - if opts.get('id').endswith('_master'): + if opts.get("id").endswith("_master"): event_bus = salt.utils.event.get_master_event( - opts, - opts['sock_dir'], - listen=True) + opts, opts["sock_dir"], listen=True + ) else: event_bus = salt.utils.event.get_event( - 'minion', - transport=opts['transport'], + "minion", + transport=opts["transport"], opts=opts, - sock_dir=opts['sock_dir'], - listen=True) + sock_dir=opts["sock_dir"], + listen=True, + ) return event_bus -def start(endpoint='data.logentries.com', - port=10000, - token=None, - tag='salt/engines/logentries'): - ''' +def start( + endpoint="data.logentries.com", + port=10000, + token=None, + tag="salt/engines/logentries", +): + """ Listen to salt events and forward them to Logentries - ''' + """ with event_bus_context(__opts__) as event_bus: - log.debug('Logentries engine started') + log.debug("Logentries engine started") try: val = uuid.UUID(token) except ValueError: - log.warning('Not a valid logentries token') + log.warning("Not a valid logentries token") appender = SocketAppender(verbose=False, LE_API=endpoint, LE_PORT=port) appender.reopen_connection() @@ -208,11 +218,13 @@ def start(endpoint='data.logentries.com', event = event_bus.get_event() if event: # future lint: disable=blacklisted-function - msg = str(' ').join(( - salt.utils.stringutils.to_str(token), - salt.utils.stringutils.to_str(tag), - salt.utils.json.dumps(event) - )) + msg = str(" ").join( + ( + salt.utils.stringutils.to_str(token), + salt.utils.stringutils.to_str(tag), + salt.utils.json.dumps(event), + ) + ) # future lint: enable=blacklisted-function appender.put(msg) diff --git a/salt/engines/logstash_engine.py b/salt/engines/logstash_engine.py index 78a7c21d539..c699e0dfc4a 100644 --- a/salt/engines/logstash_engine.py +++ b/salt/engines/logstash_engine.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An engine that reads messages from the salt event bus and pushes them onto a logstash endpoint. @@ -18,10 +18,11 @@ them onto a logstash endpoint. proto: tcp :depends: logstash -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -35,47 +36,49 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'logstash' +__virtualname__ = "logstash" def __virtual__(): - return __virtualname__ \ - if logstash is not None \ - else (False, 'python-logstash not installed') + return ( + __virtualname__ + if logstash is not None + else (False, "python-logstash not installed") + ) def event_bus_context(opts): - if opts.get('id').endswith('_master'): + if opts.get("id").endswith("_master"): event_bus = salt.utils.event.get_master_event( - opts, - opts['sock_dir'], - listen=True) + opts, opts["sock_dir"], listen=True + ) else: event_bus = salt.utils.event.get_event( - 'minion', - transport=opts['transport'], + "minion", + transport=opts["transport"], opts=opts, - sock_dir=opts['sock_dir'], - listen=True) + sock_dir=opts["sock_dir"], + listen=True, + ) return event_bus -def start(host, port=5959, tag='salt/engine/logstash', proto='udp'): - ''' +def start(host, port=5959, tag="salt/engine/logstash", proto="udp"): + """ Listen to salt events and forward them to logstash - ''' + """ - if proto == 'tcp': + if proto == "tcp": logstashHandler = logstash.TCPLogstashHandler - elif proto == 'udp': + elif proto == "udp": logstashHandler = logstash.UDPLogstashHandler - logstash_logger = logging.getLogger('python-logstash-logger') + logstash_logger = logging.getLogger("python-logstash-logger") logstash_logger.setLevel(logging.INFO) logstash_logger.addHandler(logstashHandler(host, port, version=1)) with event_bus_context(__opts__) as event_bus: - log.debug('Logstash engine started') + log.debug("Logstash engine started") while True: event = event_bus.get_event() if event: diff --git a/salt/engines/napalm_syslog.py b/salt/engines/napalm_syslog.py index f0cc02b7b98..550e84aa0a9 100644 --- a/salt/engines/napalm_syslog.py +++ b/salt/engines/napalm_syslog.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM syslog engine ==================== @@ -167,12 +167,17 @@ by the user in their environment and the complete OpenConfig object under the variable name ``openconfig_structure``. Inside the Jinja template, the user can process the object from ``openconfig_structure`` and define the bussiness logic as required. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python stdlib import logging +# Import salt libs +import salt.utils.event as event +import salt.utils.network +import salt.utils.stringutils + # Import third party libraries from salt.utils.zeromq import zmq @@ -180,22 +185,19 @@ try: # pylint: disable=W0611 import napalm_logs import napalm_logs.utils + # pylint: enable=W0611 HAS_NAPALM_LOGS = True except ImportError: HAS_NAPALM_LOGS = False -# Import salt libs -import salt.utils.event as event -import salt.utils.network -import salt.utils.stringutils # ---------------------------------------------------------------------------------------------------------------------- # module properties # ---------------------------------------------------------------------------------------------------------------------- log = logging.getLogger(__name__) -__virtualname__ = 'napalm_syslog' +__virtualname__ = "napalm_syslog" # ---------------------------------------------------------------------------------------------------------------------- # helpers @@ -203,12 +205,15 @@ __virtualname__ = 'napalm_syslog' def __virtual__(): - ''' + """ Load only if napalm-logs is installed. - ''' + """ if not HAS_NAPALM_LOGS or not zmq: - return (False, 'napalm_syslog could not be loaded. \ - Please install napalm-logs library amd ZeroMQ.') + return ( + False, + "napalm_syslog could not be loaded. \ + Please install napalm-logs library amd ZeroMQ.", + ) return True @@ -217,48 +222,41 @@ def _zmq(address, port, **kwargs): socket = context.socket(zmq.SUB) if salt.utils.network.is_ipv6(address): socket.ipv6 = True - socket.connect('tcp://{addr}:{port}'.format( - addr=address, - port=port) - ) - socket.setsockopt(zmq.SUBSCRIBE, b'') + socket.connect("tcp://{addr}:{port}".format(addr=address, port=port)) + socket.setsockopt(zmq.SUBSCRIBE, b"") return socket.recv -def _get_transport_recv(name='zmq', - address='0.0.0.0', - port=49017, - **kwargs): +def _get_transport_recv(name="zmq", address="0.0.0.0", port=49017, **kwargs): if name not in TRANSPORT_FUN_MAP: - log.error('Invalid transport: %s. Falling back to ZeroMQ.', name) - name = 'zmq' + log.error("Invalid transport: %s. Falling back to ZeroMQ.", name) + name = "zmq" return TRANSPORT_FUN_MAP[name](address, port, **kwargs) -TRANSPORT_FUN_MAP = { - 'zmq': _zmq, - 'zeromq': _zmq -} +TRANSPORT_FUN_MAP = {"zmq": _zmq, "zeromq": _zmq} # ---------------------------------------------------------------------------------------------------------------------- # main # ---------------------------------------------------------------------------------------------------------------------- -def start(transport='zmq', - address='0.0.0.0', - port=49017, - auth_address='0.0.0.0', - auth_port=49018, - disable_security=False, - certificate=None, - os_whitelist=None, - os_blacklist=None, - error_whitelist=None, - error_blacklist=None, - host_whitelist=None, - host_blacklist=None): - ''' +def start( + transport="zmq", + address="0.0.0.0", + port=49017, + auth_address="0.0.0.0", + auth_port=49018, + disable_security=False, + certificate=None, + os_whitelist=None, + os_blacklist=None, + error_whitelist=None, + error_blacklist=None, + host_whitelist=None, + host_blacklist=None, +): + """ Listen to napalm-logs and publish events into the Salt event bus. transport: ``zmq`` @@ -304,75 +302,73 @@ def start(transport='zmq', host_blacklist: ``None`` List of hosts of IPs to be ignored. - ''' + """ if not disable_security: if not certificate: - log.critical('Please use a certificate, or disable the security.') + log.critical("Please use a certificate, or disable the security.") return - auth = napalm_logs.utils.ClientAuth(certificate, - address=auth_address, - port=auth_port) + auth = napalm_logs.utils.ClientAuth( + certificate, address=auth_address, port=auth_port + ) - transport_recv_fun = _get_transport_recv(name=transport, - address=address, - port=port) + transport_recv_fun = _get_transport_recv(name=transport, address=address, port=port) if not transport_recv_fun: - log.critical('Unable to start the engine', exc_info=True) + log.critical("Unable to start the engine", exc_info=True) return master = False - if __opts__['__role'] == 'master': + if __opts__["__role"] == "master": master = True while True: - log.debug('Waiting for napalm-logs to send anything...') + log.debug("Waiting for napalm-logs to send anything...") raw_object = transport_recv_fun() - log.debug('Received from napalm-logs:') + log.debug("Received from napalm-logs:") log.debug(raw_object) if not disable_security: dict_object = auth.decrypt(raw_object) else: dict_object = napalm_logs.utils.unserialize(raw_object) try: - event_os = dict_object['os'] + event_os = dict_object["os"] if os_blacklist or os_whitelist: valid_os = salt.utils.stringutils.check_whitelist_blacklist( - event_os, - whitelist=os_whitelist, - blacklist=os_blacklist) + event_os, whitelist=os_whitelist, blacklist=os_blacklist + ) if not valid_os: - log.info('Ignoring NOS %s as per whitelist/blacklist', event_os) + log.info("Ignoring NOS %s as per whitelist/blacklist", event_os) continue - event_error = dict_object['error'] + event_error = dict_object["error"] if error_blacklist or error_whitelist: valid_error = salt.utils.stringutils.check_whitelist_blacklist( - event_error, - whitelist=error_whitelist, - blacklist=error_blacklist) + event_error, whitelist=error_whitelist, blacklist=error_blacklist + ) if not valid_error: - log.info('Ignoring error %s as per whitelist/blacklist', event_error) + log.info( + "Ignoring error %s as per whitelist/blacklist", event_error + ) continue - event_host = dict_object.get('host') or dict_object.get('ip') + event_host = dict_object.get("host") or dict_object.get("ip") if host_blacklist or host_whitelist: valid_host = salt.utils.stringutils.check_whitelist_blacklist( - event_host, - whitelist=host_whitelist, - blacklist=host_blacklist) + event_host, whitelist=host_whitelist, blacklist=host_blacklist + ) if not valid_host: - log.info('Ignoring messages from %s as per whitelist/blacklist', event_host) + log.info( + "Ignoring messages from %s as per whitelist/blacklist", + event_host, + ) continue - tag = 'napalm/syslog/{os}/{error}/{host}'.format( - os=event_os, - error=event_error, - host=event_host + tag = "napalm/syslog/{os}/{error}/{host}".format( + os=event_os, error=event_error, host=event_host ) except KeyError as kerr: - log.warning('Missing keys from the napalm-logs object:', exc_info=True) + log.warning("Missing keys from the napalm-logs object:", exc_info=True) log.warning(dict_object) continue # jump to the next object in the queue - log.debug('Sending event %s', tag) + log.debug("Sending event %s", tag) log.debug(raw_object) if master: - event.get_master_event(__opts__, - __opts__['sock_dir'] - ).fire_event(dict_object, tag) + event.get_master_event(__opts__, __opts__["sock_dir"]).fire_event( + dict_object, tag + ) else: - __salt__['event.send'](tag, dict_object) + __salt__["event.send"](tag, dict_object) diff --git a/salt/engines/reactor.py b/salt/engines/reactor.py index af3c90f64ee..9f8c71884e1 100644 --- a/salt/engines/reactor.py +++ b/salt/engines/reactor.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Setup Reactor Example Config in Master or Minion config @@ -16,21 +16,20 @@ Example Config in Master or Minion config - 'salt/cloud/*/destroyed': - /srv/reactor/destroy/*.sls -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals - # Import salt libs import salt.utils.reactor def start(refresh_interval=None, worker_threads=None, worker_hwm=None): if refresh_interval is not None: - __opts__['reactor_refresh_interval'] = refresh_interval + __opts__["reactor_refresh_interval"] = refresh_interval if worker_threads is not None: - __opts__['reactor_worker_threads'] = worker_threads + __opts__["reactor_worker_threads"] = worker_threads if worker_hwm is not None: - __opts__['reactor_worker_hwm'] = worker_hwm + __opts__["reactor_worker_hwm"] = worker_hwm salt.utils.reactor.Reactor(__opts__).run() diff --git a/salt/engines/redis_sentinel.py b/salt/engines/redis_sentinel.py index ec1c276c141..a89464b2690 100644 --- a/salt/engines/redis_sentinel.py +++ b/salt/engines/redis_sentinel.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An engine that reads messages from the redis sentinel pubsub and sends reactor events based on the channels they are subscribed to. @@ -23,10 +23,11 @@ events based on the channels they are subscribed to. - '-odown' :depends: redis -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -42,64 +43,77 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'redis' +__virtualname__ = "redis" log = logging.getLogger(__name__) def __virtual__(): - return __virtualname__ \ - if redis is not None \ - else (False, 'redis python module is not installed') + return ( + __virtualname__ + if redis is not None + else (False, "redis python module is not installed") + ) class Listener(object): def __init__(self, host=None, port=None, channels=None, tag=None): if host is None: - host = 'localhost' + host = "localhost" if port is None: port = 26379 if channels is None: - channels = ['*'] + channels = ["*"] if tag is None: - tag = 'salt/engine/redis_sentinel' + tag = "salt/engine/redis_sentinel" super(Listener, self).__init__() self.tag = tag self.redis = redis.StrictRedis(host=host, port=port, decode_responses=True) self.pubsub = self.redis.pubsub() self.pubsub.psubscribe(channels) - self.fire_master = salt.utils.event.get_master_event(__opts__, __opts__['sock_dir']).fire_event + self.fire_master = salt.utils.event.get_master_event( + __opts__, __opts__["sock_dir"] + ).fire_event def work(self, item): - ret = {'channel': item['channel']} - if isinstance(item['data'], six.integer_types): - ret['code'] = item['data'] - elif item['channel'] == '+switch-master': - ret.update(dict(list(zip( - ('master', 'old_host', 'old_port', 'new_host', 'new_port'), item['data'].split(' ') - )))) - elif item['channel'] in ('+odown', '-odown'): - ret.update(dict(list(zip( - ('master', 'host', 'port'), item['data'].split(' ')[1:] - )))) + ret = {"channel": item["channel"]} + if isinstance(item["data"], six.integer_types): + ret["code"] = item["data"] + elif item["channel"] == "+switch-master": + ret.update( + dict( + list( + zip( + ("master", "old_host", "old_port", "new_host", "new_port"), + item["data"].split(" "), + ) + ) + ) + ) + elif item["channel"] in ("+odown", "-odown"): + ret.update( + dict(list(zip(("master", "host", "port"), item["data"].split(" ")[1:]))) + ) else: ret = { - 'channel': item['channel'], - 'data': item['data'], + "channel": item["channel"], + "data": item["data"], } - self.fire_master(ret, '{0}/{1}'.format(self.tag, item['channel'])) + self.fire_master(ret, "{0}/{1}".format(self.tag, item["channel"])) def run(self): - log.debug('Start Listener') + log.debug("Start Listener") for item in self.pubsub.listen(): - log.debug('Item: %s', item) + log.debug("Item: %s", item) self.work(item) def start(hosts, channels, tag=None): if tag is None: - tag = 'salt/engine/redis_sentinel' + tag = "salt/engine/redis_sentinel" local = salt.client.LocalClient() - ips = local.cmd(hosts['matching'], 'network.ip_addrs', [hosts['interface']]).values() - client = Listener(host=ips.pop()[0], port=hosts['port'], channels=channels, tag=tag) + ips = local.cmd( + hosts["matching"], "network.ip_addrs", [hosts["interface"]] + ).values() + client = Listener(host=ips.pop()[0], port=hosts["port"], channels=channels, tag=tag) client.run() diff --git a/salt/engines/script.py b/salt/engines/script.py index aa61210e949..b46772cd3b2 100644 --- a/salt/engines/script.py +++ b/salt/engines/script.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Send events based on a script's stdout Example Config @@ -18,48 +18,50 @@ Script engine configs: output: Any available saltstack deserializer interval: How often in seconds to execute the command -''' +""" from __future__ import absolute_import, print_function + import logging import shlex -import time import subprocess +import time + +import salt.loader # import salt libs import salt.utils.event import salt.utils.process -import salt.loader from salt.exceptions import CommandExecutionError - from salt.ext import six - log = logging.getLogger(__name__) def _read_stdout(proc): - ''' + """ Generator that returns stdout - ''' + """ for line in iter(proc.stdout.readline, ""): yield line def _get_serializer(output): - ''' + """ Helper to return known serializer based on pass output argument - ''' + """ serializers = salt.loader.serializers(__opts__) try: return getattr(serializers, output) except AttributeError: - raise CommandExecutionError('Unknown serializer `{}` found for output option'.format(output)) + raise CommandExecutionError( + "Unknown serializer `{}` found for output option".format(output) + ) -def start(cmd, output='json', interval=1): - ''' +def start(cmd, output="json", interval=1): + """ Parse stdout of a command and generate an event The script engine will scrap stdout of the @@ -73,17 +75,19 @@ def start(cmd, output='json', interval=1): Given the following json output from a script: - { "tag" : "lots/of/tacos", - "data" : { "toppings" : "cilantro" } - } + .. code-block:: json + + { "tag" : "lots/of/tacos", + "data" : { "toppings" : "cilantro" } + } This will fire the event 'lots/of/tacos' on the event bus with the data obj as is. :param cmd: The command to execute :param output: How to deserialize stdout of the script - :param interval: How often to execute the script. - ''' + :param interval: How often to execute the script + """ try: cmd = shlex.split(cmd) except AttributeError: @@ -92,19 +96,19 @@ def start(cmd, output='json', interval=1): serializer = _get_serializer(output) - if __opts__.get('__role') == 'master': + if __opts__.get("__role") == "master": fire_master = salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir']).fire_event + __opts__, __opts__["sock_dir"] + ).fire_event else: - fire_master = __salt__['event.send'] + fire_master = __salt__["event.send"] while True: try: - proc = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + proc = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) log.debug("Starting script with pid %d", proc.pid) @@ -112,11 +116,11 @@ def start(cmd, output='json', interval=1): log.debug(raw_event) event = serializer.deserialize(raw_event) - tag = event.get('tag', None) - data = event.get('data', {}) + tag = event.get("tag", None) + data = event.get("data", {}) - if data and 'id' not in data: - data['id'] = __opts__['id'] + if data and "id" not in data: + data["id"] = __opts__["id"] if tag: log.info("script engine firing event with tag %s", tag) diff --git a/salt/engines/slack.py b/salt/engines/slack.py index e0094b1a7a3..46256b99296 100644 --- a/salt/engines/slack.py +++ b/salt/engines/slack.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An engine that reads messages from Slack and can act on them .. versionadded: 2016.3.0 @@ -144,30 +144,24 @@ must be quoted, or else PyYAML will fail to load the configuration. commands: - '*' -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals + import ast import datetime import itertools import logging -import time import re +import time import traceback -log = logging.getLogger(__name__) - -try: - import slackclient - HAS_SLACKCLIENT = True -except ImportError: - HAS_SLACKCLIENT = False - # Import salt libs import salt.client import salt.loader import salt.minion +import salt.output import salt.runner import salt.utils.args import salt.utils.event @@ -175,15 +169,24 @@ import salt.utils.http import salt.utils.json import salt.utils.slack import salt.utils.yaml -import salt.output from salt.ext import six -__virtualname__ = 'slack' +log = logging.getLogger(__name__) + +try: + import slackclient + + HAS_SLACKCLIENT = True +except ImportError: + HAS_SLACKCLIENT = False + + +__virtualname__ = "slack" def __virtual__(): if not HAS_SLACKCLIENT: - return (False, 'The \'slackclient\' Python module could not be loaded') + return (False, "The 'slackclient' Python module could not be loaded") return __virtualname__ @@ -195,56 +198,52 @@ class SlackClient(object): self.slack_connect = self.sc.rtm_connect() def get_slack_users(self, token): - ''' + """ Get all users from Slack - ''' + """ - ret = salt.utils.slack.query(function='users', - api_key=token, - opts=__opts__) + ret = salt.utils.slack.query(function="users", api_key=token, opts=__opts__) users = {} - if 'message' in ret: - for item in ret['message']: - if 'is_bot' in item: - if not item['is_bot']: - users[item['name']] = item['id'] - users[item['id']] = item['name'] + if "message" in ret: + for item in ret["message"]: + if "is_bot" in item: + if not item["is_bot"]: + users[item["name"]] = item["id"] + users[item["id"]] = item["name"] return users def get_slack_channels(self, token): - ''' + """ Get all channel names from Slack - ''' + """ ret = salt.utils.slack.query( - function='rooms', + function="rooms", api_key=token, # These won't be honored until https://github.com/saltstack/salt/pull/41187/files is merged - opts={ - 'exclude_archived': True, - 'exclude_members': True - }) + opts={"exclude_archived": True, "exclude_members": True}, + ) channels = {} - if 'message' in ret: - for item in ret['message']: - channels[item['id']] = item['name'] + if "message" in ret: + for item in ret["message"]: + channels[item["id"]] = item["name"] return channels def get_config_groups(self, groups_conf, groups_pillar_name): - ''' + """ get info from groups in config, and from the named pillar todo: add specification for the minion to use to recover pillar - ''' + """ # Get groups # Default to returning something that'll never match ret_groups = { - 'default': { - 'users': set(), - 'commands': set(), - 'aliases': {}, - 'default_target': {}, - 'targets': {} + "default": { + "users": set(), + "commands": set(), + "aliases": {}, + "default_target": {}, + "targets": {}, } } @@ -257,36 +256,50 @@ class SlackClient(object): # First obtain group lists from pillars, then in case there is any overlap, iterate over the groups # that come from pillars. The configuration in files on disk/from startup # will override any configs from pillars. They are meant to be complementary not to provide overrides. - log.debug('use_groups %s', use_groups) + log.debug("use_groups %s", use_groups) try: - groups_gen = itertools.chain(self._groups_from_pillar(groups_pillar_name).items(), use_groups.items()) + groups_gen = itertools.chain( + self._groups_from_pillar(groups_pillar_name).items(), use_groups.items() + ) except AttributeError: - log.warning('Failed to get groups from %s: %s or from config: %s', + log.warning( + "Failed to get groups from %s: %s or from config: %s", groups_pillar_name, self._groups_from_pillar(groups_pillar_name), - use_groups + use_groups, ) groups_gen = [] for name, config in groups_gen: - log.info('Trying to get %s and %s to be useful', name, config) - ret_groups.setdefault(name, { - 'users': set(), 'commands': set(), 'aliases': {}, - 'default_target': {}, 'targets': {} - }) + log.info("Trying to get %s and %s to be useful", name, config) + ret_groups.setdefault( + name, + { + "users": set(), + "commands": set(), + "aliases": {}, + "default_target": {}, + "targets": {}, + }, + ) try: - ret_groups[name]['users'].update(set(config.get('users', []))) - ret_groups[name]['commands'].update(set(config.get('commands', []))) - ret_groups[name]['aliases'].update(config.get('aliases', {})) - ret_groups[name]['default_target'].update(config.get('default_target', {})) - ret_groups[name]['targets'].update(config.get('targets', {})) + ret_groups[name]["users"].update(set(config.get("users", []))) + ret_groups[name]["commands"].update(set(config.get("commands", []))) + ret_groups[name]["aliases"].update(config.get("aliases", {})) + ret_groups[name]["default_target"].update( + config.get("default_target", {}) + ) + ret_groups[name]["targets"].update(config.get("targets", {})) except (IndexError, AttributeError): - log.warning("Couldn't use group %s. Check that targets is a dictionary and not a list", name) + log.warning( + "Couldn't use group %s. Check that targets is a dictionary and not a list", + name, + ) - log.debug('Got the groups: %s', ret_groups) + log.debug("Got the groups: %s", ret_groups) return ret_groups def _groups_from_pillar(self, pillar_name): - ''' + """ pillar_prefix is the pillar.get syntax for the pillar to be queried. Group name is gotten via the equivalent of using ``salt['pillar.get']('{}:{}'.format(pillar_prefix, group_name))`` @@ -295,36 +308,36 @@ class SlackClient(object): returns a dictionary (unless the pillar is mis-formatted) XXX: instead of using Caller, make the minion to use configurable so there could be some restrictions placed on what pillars can be used. - ''' - if pillar_name and __opts__['__role'] == 'minion': - pillar_groups = __salt__['pillar.get'](pillar_name, {}) - log.debug('Got pillar groups %s from pillar %s', pillar_groups, pillar_name) - log.debug('pillar groups is %s', pillar_groups) - log.debug('pillar groups type is %s', type(pillar_groups)) + """ + if pillar_name and __opts__["__role"] == "minion": + pillar_groups = __salt__["pillar.get"](pillar_name, {}) + log.debug("Got pillar groups %s from pillar %s", pillar_groups, pillar_name) + log.debug("pillar groups is %s", pillar_groups) + log.debug("pillar groups type is %s", type(pillar_groups)) else: pillar_groups = {} return pillar_groups def fire(self, tag, msg): - ''' + """ This replaces a function in main called 'fire' It fires an event into the salt bus. - ''' - if __opts__.get('__role') == 'master': + """ + if __opts__.get("__role") == "master": fire_master = salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir']).fire_master + __opts__, __opts__["sock_dir"] + ).fire_master else: fire_master = None if fire_master: fire_master(msg, tag) else: - __salt__['event.send'](tag, msg) + __salt__["event.send"](tag, msg) def can_user_run(self, user, command, groups): - ''' + """ Break out the permissions into the following: Check whether a user is in any group, including whether a group has the '*' membership @@ -345,42 +358,51 @@ class SlackClient(object): On failure it returns an empty tuple - ''' - log.info('%s wants to run %s with groups %s', user, command, groups) + """ + log.info("%s wants to run %s with groups %s", user, command, groups) for key, val in groups.items(): - if user not in val['users']: - if '*' not in val['users']: + if user not in val["users"]: + if "*" not in val["users"]: continue # this doesn't grant permissions, pass - if (command not in val['commands']) and (command not in val.get('aliases', {}).keys()): - if '*' not in val['commands']: + if (command not in val["commands"]) and ( + command not in val.get("aliases", {}).keys() + ): + if "*" not in val["commands"]: continue # again, pass - log.info('Slack user %s permitted to run %s', user, command) - return (key, val,) # matched this group, return the group - log.info('Slack user %s denied trying to run %s', user, command) + log.info("Slack user %s permitted to run %s", user, command) + return ( + key, + val, + ) # matched this group, return the group + log.info("Slack user %s denied trying to run %s", user, command) return () def commandline_to_list(self, cmdline_str, trigger_string): - ''' + """ cmdline_str is the string of the command line trigger_string is the trigger string, to be removed - ''' - cmdline = salt.utils.args.shlex_split(cmdline_str[len(trigger_string):]) + """ + cmdline = salt.utils.args.shlex_split(cmdline_str[len(trigger_string) :]) # Remove slack url parsing # Translate target= # to target=host.domain.net cmdlist = [] for cmditem in cmdline: - pattern = r'(?P.*)(<.*\|)(?P.*)(>)(?P.*)' + pattern = r"(?P.*)(<.*\|)(?P.*)(>)(?P.*)" mtch = re.match(pattern, cmditem) if mtch: - origtext = mtch.group('begin') + mtch.group('url') + mtch.group('remainder') + origtext = ( + mtch.group("begin") + mtch.group("url") + mtch.group("remainder") + ) cmdlist.append(origtext) else: cmdlist.append(cmditem) return cmdlist - def control_message_target(self, slack_user_name, text, loaded_groups, trigger_string): - '''Returns a tuple of (target, cmdline,) for the response + def control_message_target( + self, slack_user_name, text, loaded_groups, trigger_string + ): + """Returns a tuple of (target, cmdline,) for the response Raises IndexError if a user can't be looked up from all_slack_users @@ -393,13 +415,17 @@ class SlackClient(object): The cmdline that is returned is the actual list that should be processed by salt, and not the alias. - ''' + """ # Trim the trigger string from the front # cmdline = _text[1:].split(' ', 1) cmdline = self.commandline_to_list(text, trigger_string) permitted_group = self.can_user_run(slack_user_name, cmdline[0], loaded_groups) - log.debug('slack_user_name is %s and the permitted group is %s', slack_user_name, permitted_group) + log.debug( + "slack_user_name is %s and the permitted group is %s", + slack_user_name, + permitted_group, + ) if not permitted_group: return (False, None, cmdline[0]) @@ -407,8 +433,10 @@ class SlackClient(object): return (False, None, cmdline[0]) # maybe there are aliases, so check on that - if cmdline[0] in permitted_group[1].get('aliases', {}).keys(): - use_cmdline = self.commandline_to_list(permitted_group[1]['aliases'][cmdline[0]].get('cmd', ''), '') + if cmdline[0] in permitted_group[1].get("aliases", {}).keys(): + use_cmdline = self.commandline_to_list( + permitted_group[1]["aliases"][cmdline[0]].get("cmd", ""), "" + ) # Include any additional elements from cmdline use_cmdline.extend(cmdline[1:]) else: @@ -417,36 +445,40 @@ class SlackClient(object): # Remove target and tgt_type from commandline # that is sent along to Salt - use_cmdline = [item for item - in use_cmdline - if all(not item.startswith(x) for x in ('target', 'tgt_type'))] + use_cmdline = [ + item + for item in use_cmdline + if all(not item.startswith(x) for x in ("target", "tgt_type")) + ] return (True, target, use_cmdline) def message_text(self, m_data): - ''' + """ Raises ValueError if a value doesn't work out, and TypeError if this isn't a message type - ''' - if m_data.get('type') != 'message': - raise TypeError('This is not a message') + """ + if m_data.get("type") != "message": + raise TypeError("This is not a message") # Edited messages have text in message - _text = m_data.get('text', None) or m_data.get('message', {}).get('text', None) + _text = m_data.get("text", None) or m_data.get("message", {}).get("text", None) try: - log.info('Message is %s', _text) # this can violate the ascii codec + log.info("Message is %s", _text) # this can violate the ascii codec except UnicodeEncodeError as uee: - log.warning('Got a message that I could not log. The reason is: %s', uee) + log.warning("Got a message that I could not log. The reason is: %s", uee) # Convert UTF to string _text = salt.utils.json.dumps(_text) _text = salt.utils.yaml.safe_load(_text) if not _text: - raise ValueError('_text has no value') + raise ValueError("_text has no value") return _text - def generate_triggered_messages(self, token, trigger_string, groups, groups_pillar_name): - ''' + def generate_triggered_messages( + self, token, trigger_string, groups, groups_pillar_name + ): + """ slack_token = string trigger_string = string input_valid_users = set @@ -472,44 +504,50 @@ class SlackClient(object): its own processing and check back for more data later. This relies on the caller sleeping between checks, otherwise this could flood - ''' - all_slack_users = self.get_slack_users(token) # re-checks this if we have an negative lookup result - all_slack_channels = self.get_slack_channels(token) # re-checks this if we have an negative lookup result + """ + all_slack_users = self.get_slack_users( + token + ) # re-checks this if we have an negative lookup result + all_slack_channels = self.get_slack_channels( + token + ) # re-checks this if we have an negative lookup result def just_data(m_data): - '''Always try to return the user and channel anyway''' - if 'user' not in m_data: - if 'message' in m_data and 'user' in m_data['message']: - log.debug('Message was edited, ' - 'so we look for user in ' - 'the original message.') - user_id = m_data['message']['user'] - elif 'comment' in m_data and 'user' in m_data['comment']: - log.debug('Comment was added, ' - 'so we look for user in ' - 'the comment.') - user_id = m_data['comment']['user'] + """Always try to return the user and channel anyway""" + if "user" not in m_data: + if "message" in m_data and "user" in m_data["message"]: + log.debug( + "Message was edited, " + "so we look for user in " + "the original message." + ) + user_id = m_data["message"]["user"] + elif "comment" in m_data and "user" in m_data["comment"]: + log.debug( + "Comment was added, " "so we look for user in " "the comment." + ) + user_id = m_data["comment"]["user"] else: - user_id = m_data.get('user') - channel_id = m_data.get('channel') - if channel_id.startswith('D'): # private chate with bot user - channel_name = 'private chat' + user_id = m_data.get("user") + channel_id = m_data.get("channel") + if channel_id.startswith("D"): # private chate with bot user + channel_name = "private chat" else: channel_name = all_slack_channels.get(channel_id) data = { - 'message_data': m_data, - 'user_id': user_id, - 'user_name': all_slack_users.get(user_id), - 'channel_name': channel_name + "message_data": m_data, + "user_id": user_id, + "user_name": all_slack_users.get(user_id), + "channel_name": channel_name, } - if not data['user_name']: + if not data["user_name"]: all_slack_users.clear() all_slack_users.update(self.get_slack_users(token)) - data['user_name'] = all_slack_users.get(user_id) - if not data['channel_name']: + data["user_name"] = all_slack_users.get(user_id) + if not data["channel_name"]: all_slack_channels.clear() all_slack_channels.update(self.get_slack_channels(token)) - data['channel_name'] = all_slack_channels.get(channel_id) + data["channel_name"] = all_slack_channels.get(channel_id) return data for sleeps in (5, 10, 30, 60): @@ -517,59 +555,78 @@ class SlackClient(object): break else: # see https://api.slack.com/docs/rate-limits - log.warning('Slack connection is invalid. Server: %s, sleeping %s', self.sc.server, sleeps) - time.sleep(sleeps) # respawning too fast makes the slack API unhappy about the next reconnection + log.warning( + "Slack connection is invalid. Server: %s, sleeping %s", + self.sc.server, + sleeps, + ) + time.sleep( + sleeps + ) # respawning too fast makes the slack API unhappy about the next reconnection else: - raise UserWarning('Connection to slack is still invalid, giving up: {}'.format(self.slack_connect)) # Boom! + raise UserWarning( + "Connection to slack is still invalid, giving up: {}".format( + self.slack_connect + ) + ) # Boom! while True: msg = self.sc.rtm_read() for m_data in msg: try: msg_text = self.message_text(m_data) except (ValueError, TypeError) as msg_err: - log.debug('Got an error from trying to get the message text %s', msg_err) - yield {'message_data': m_data} # Not a message type from the API? + log.debug( + "Got an error from trying to get the message text %s", msg_err + ) + yield {"message_data": m_data} # Not a message type from the API? continue # Find the channel object from the channel name - channel = self.sc.server.channels.find(m_data['channel']) + channel = self.sc.server.channels.find(m_data["channel"]) data = just_data(m_data) if msg_text.startswith(trigger_string): loaded_groups = self.get_config_groups(groups, groups_pillar_name) - if not data.get('user_name'): + if not data.get("user_name"): log.error( - 'The user %s can not be looked up via slack. What has happened here?', - m_data.get('user') + "The user %s can not be looked up via slack. What has happened here?", + m_data.get("user"), ) - channel.send_message('The user {} can not be looked up via slack. Not running {}'.format( - data['user_id'], msg_text)) - yield {'message_data': m_data} + channel.send_message( + "The user {} can not be looked up via slack. Not running {}".format( + data["user_id"], msg_text + ) + ) + yield {"message_data": m_data} continue (allowed, target, cmdline) = self.control_message_target( - data['user_name'], msg_text, loaded_groups, trigger_string) - log.debug('Got target: %s, cmdline: %s', target, cmdline) + data["user_name"], msg_text, loaded_groups, trigger_string + ) + log.debug("Got target: %s, cmdline: %s", target, cmdline) if allowed: yield { - 'message_data': m_data, - 'channel': m_data['channel'], - 'user': data['user_id'], - 'user_name': data['user_name'], - 'cmdline': cmdline, - 'target': target + "message_data": m_data, + "channel": m_data["channel"], + "user": data["user_id"], + "user_name": data["user_name"], + "cmdline": cmdline, + "target": target, } continue else: - channel.send_message('{0} is not allowed to use command {1}.'.format( - data['user_name'], cmdline)) + channel.send_message( + "{0} is not allowed to use command {1}.".format( + data["user_name"], cmdline + ) + ) yield data continue else: yield data continue - yield {'done': True} + yield {"done": True} def get_target(self, permitted_group, cmdline, alias_cmdline): - ''' + """ When we are permitted to run a command on a target, look to see what the default targeting is for that group, and for that specific command (if provided). @@ -595,86 +652,91 @@ class SlackClient(object): Run each of them through ``get_configured_target(('foo', f), 'pillar.get')`` and confirm a valid target - ''' + """ # Default to targeting all minions with a type of glob - null_target = {'target': '*', 'tgt_type': 'glob'} + null_target = {"target": "*", "tgt_type": "glob"} def check_cmd_against_group(cmd): - ''' + """ Validate cmd against the group to return the target, or a null target - ''' + """ name, group_config = permitted_group - target = group_config.get('default_target') + target = group_config.get("default_target") if not target: # Empty, None, or False target = null_target - if group_config.get('targets'): - if group_config['targets'].get(cmd): - target = group_config['targets'][cmd] - if not target.get('target'): - log.debug('Group %s is not configured to have a target for cmd %s.', name, cmd) + if group_config.get("targets"): + if group_config["targets"].get(cmd): + target = group_config["targets"][cmd] + if not target.get("target"): + log.debug( + "Group %s is not configured to have a target for cmd %s.", name, cmd + ) return target for this_cl in cmdline, alias_cmdline: _, kwargs = self.parse_args_and_kwargs(this_cl) - if 'target' in kwargs: - log.debug('target is in kwargs %s.', kwargs) - if 'tgt_type' in kwargs: - log.debug('tgt_type is in kwargs %s.', kwargs) - return {'target': kwargs['target'], 'tgt_type': kwargs['tgt_type']} - return {'target': kwargs['target'], 'tgt_type': 'glob'} + if "target" in kwargs: + log.debug("target is in kwargs %s.", kwargs) + if "tgt_type" in kwargs: + log.debug("tgt_type is in kwargs %s.", kwargs) + return {"target": kwargs["target"], "tgt_type": kwargs["tgt_type"]} + return {"target": kwargs["target"], "tgt_type": "glob"} for this_cl in cmdline, alias_cmdline: checked = check_cmd_against_group(this_cl[0]) - log.debug('this cmdline has target %s.', this_cl) - if checked.get('target'): + log.debug("this cmdline has target %s.", this_cl) + if checked.get("target"): return checked return null_target - def format_return_text(self, data, function, **kwargs): # pylint: disable=unused-argument - ''' + def format_return_text( + self, data, function, **kwargs + ): # pylint: disable=unused-argument + """ Print out YAML using the block mode - ''' + """ # emulate the yaml_out output formatter. It relies on a global __opts__ object which # we can't obviously pass in try: try: - outputter = data[next(iter(data))].get('out') + outputter = data[next(iter(data))].get("out") except (StopIteration, AttributeError): outputter = None return salt.output.string_format( - {x: y['return'] for x, y in six.iteritems(data)}, + {x: y["return"] for x, y in six.iteritems(data)}, out=outputter, opts=__opts__, ) except Exception as exc: # pylint: disable=broad-except import pprint + log.exception( - 'Exception encountered when trying to serialize %s', - pprint.pformat(data) + "Exception encountered when trying to serialize %s", + pprint.pformat(data), ) - return 'Got an error trying to serialze/clean up the response' + return "Got an error trying to serialze/clean up the response" def parse_args_and_kwargs(self, cmdline): - ''' + """ cmdline: list returns tuple of: args (list), kwargs (dict) - ''' + """ # Parse args and kwargs args = [] kwargs = {} if len(cmdline) > 1: for item in cmdline[1:]: - if '=' in item: - (key, value) = item.split('=', 1) + if "=" in item: + (key, value) = item.split("=", 1) kwargs[key] = value else: args.append(item) return (args, kwargs) def get_jobs_from_runner(self, outstanding_jids): - ''' + """ Given a list of job_ids, return a dictionary of those job_ids that have completed and their results. @@ -683,39 +745,41 @@ class SlackClient(object): completed. returns a dictionary of job id: result - ''' + """ # Can't use the runner because of https://github.com/saltstack/salt/issues/40671 runner = salt.runner.RunnerClient(__opts__) - source = __opts__.get('ext_job_cache') + source = __opts__.get("ext_job_cache") if not source: - source = __opts__.get('master_job_cache') + source = __opts__.get("master_job_cache") results = {} for jid in outstanding_jids: # results[jid] = runner.cmd('jobs.lookup_jid', [jid]) - if self.master_minion.returners['{}.get_jid'.format(source)](jid): - job_result = runner.cmd('jobs.list_job', [jid]) - jid_result = job_result.get('Result', {}) - jid_function = job_result.get('Function', {}) + if self.master_minion.returners["{}.get_jid".format(source)](jid): + job_result = runner.cmd("jobs.list_job", [jid]) + jid_result = job_result.get("Result", {}) + jid_function = job_result.get("Function", {}) # emulate lookup_jid's return, which is just minion:return results[jid] = { - 'data': salt.utils.json.loads(salt.utils.json.dumps(jid_result)), - 'function': jid_function + "data": salt.utils.json.loads(salt.utils.json.dumps(jid_result)), + "function": jid_function, } return results - def run_commands_from_slack_async(self, message_generator, fire_all, tag, control, interval=1): - ''' + def run_commands_from_slack_async( + self, message_generator, fire_all, tag, control, interval=1 + ): + """ Pull any pending messages from the message_generator, sending each one to either the event bus, the command_async or both, depending on the values of fire_all and command - ''' + """ outstanding = {} # set of job_id that we need to check for while True: - log.trace('Sleeping for interval of %s', interval) + log.trace("Sleeping for interval of %s", interval) time.sleep(interval) # Drain the slack messages, up to 10 messages at a clip count = 0 @@ -723,59 +787,84 @@ class SlackClient(object): # The message_generator yields dicts. Leave this loop # on a dict that looks like {'done': True} or when we've done it # 10 times without taking a break. - log.trace('Got a message from the generator: %s', msg.keys()) + log.trace("Got a message from the generator: %s", msg.keys()) if count > 10: - log.warning('Breaking in getting messages because count is exceeded') + log.warning( + "Breaking in getting messages because count is exceeded" + ) break if not msg: count += 1 - log.warning('Skipping an empty message.') + log.warning("Skipping an empty message.") continue # This one is a dud, get the next message - if msg.get('done'): - log.trace('msg is done') + if msg.get("done"): + log.trace("msg is done") break if fire_all: - log.debug('Firing message to the bus with tag: %s', tag) - log.debug('%s %s', tag, msg) - self.fire('{0}/{1}'.format(tag, msg['message_data'].get('type')), msg) - if control and (len(msg) > 1) and msg.get('cmdline'): - channel = self.sc.server.channels.find(msg['channel']) + log.debug("Firing message to the bus with tag: %s", tag) + log.debug("%s %s", tag, msg) + self.fire( + "{0}/{1}".format(tag, msg["message_data"].get("type")), msg + ) + if control and (len(msg) > 1) and msg.get("cmdline"): + channel = self.sc.server.channels.find(msg["channel"]) jid = self.run_command_async(msg) - log.debug('Submitted a job and got jid: %s', jid) - outstanding[jid] = msg # record so we can return messages to the caller - channel.send_message("@{}'s job is submitted as salt jid {}".format(msg['user_name'], jid)) + log.debug("Submitted a job and got jid: %s", jid) + outstanding[ + jid + ] = msg # record so we can return messages to the caller + channel.send_message( + "@{}'s job is submitted as salt jid {}".format( + msg["user_name"], jid + ) + ) count += 1 start_time = time.time() - job_status = self.get_jobs_from_runner(outstanding.keys()) # dict of job_ids:results are returned - log.trace('Getting %s jobs status took %s seconds', len(job_status), time.time() - start_time) + job_status = self.get_jobs_from_runner( + outstanding.keys() + ) # dict of job_ids:results are returned + log.trace( + "Getting %s jobs status took %s seconds", + len(job_status), + time.time() - start_time, + ) for jid in job_status: - result = job_status[jid]['data'] - function = job_status[jid]['function'] + result = job_status[jid]["data"] + function = job_status[jid]["function"] if result: - log.debug('ret to send back is %s', result) + log.debug("ret to send back is %s", result) # formatting function? this_job = outstanding[jid] - channel = self.sc.server.channels.find(this_job['channel']) + channel = self.sc.server.channels.find(this_job["channel"]) return_text = self.format_return_text(result, function) return_prefix = "@{}'s job `{}` (id: {}) (target: {}) returned".format( - this_job['user_name'], this_job['cmdline'], jid, this_job['target']) + this_job["user_name"], + this_job["cmdline"], + jid, + this_job["target"], + ) channel.send_message(return_prefix) ts = time.time() - st = datetime.datetime.fromtimestamp(ts).strftime('%Y%m%d%H%M%S%f') - filename = 'salt-results-{0}.yaml'.format(st) + st = datetime.datetime.fromtimestamp(ts).strftime("%Y%m%d%H%M%S%f") + filename = "salt-results-{0}.yaml".format(st) r = self.sc.api_call( - 'files.upload', channels=channel.id, filename=filename, - content=return_text) + "files.upload", + channels=channel.id, + filename=filename, + content=return_text, + ) # Handle unicode return - log.debug('Got back %s via the slack client', r) + log.debug("Got back %s via the slack client", r) resp = salt.utils.yaml.safe_load(salt.utils.json.dumps(r)) - if 'ok' in resp and resp['ok'] is False: - this_job['channel'].send_message('Error: {0}'.format(resp['error'])) + if "ok" in resp and resp["ok"] is False: + this_job["channel"].send_message( + "Error: {0}".format(resp["error"]) + ) del outstanding[jid] def run_command_async(self, msg): - ''' + """ :type message_generator: generator of dict :param message_generator: Generates messages from slack that should be run @@ -788,62 +877,74 @@ class SlackClient(object): :type interval: int :param interval: time to wait between ending a loop and beginning the next - ''' - log.debug('Going to run a command asynchronous') + """ + log.debug("Going to run a command asynchronous") runner_functions = sorted(salt.runner.Runner(__opts__).functions) # Parse args and kwargs - cmd = msg['cmdline'][0] + cmd = msg["cmdline"][0] - args, kwargs = self.parse_args_and_kwargs(msg['cmdline']) + args, kwargs = self.parse_args_and_kwargs(msg["cmdline"]) # Check for pillar string representation of dict and convert it to dict - if 'pillar' in kwargs: - kwargs.update(pillar=ast.literal_eval(kwargs['pillar'])) + if "pillar" in kwargs: + kwargs.update(pillar=ast.literal_eval(kwargs["pillar"])) # Check for target. Otherwise assume None - target = msg['target']['target'] + target = msg["target"]["target"] # Check for tgt_type. Otherwise assume glob - tgt_type = msg['target']['tgt_type'] - log.debug('target_type is: %s', tgt_type) + tgt_type = msg["target"]["tgt_type"] + log.debug("target_type is: %s", tgt_type) if cmd in runner_functions: runner = salt.runner.RunnerClient(__opts__) - log.debug('Command %s will run via runner_functions', cmd) + log.debug("Command %s will run via runner_functions", cmd) # pylint is tripping # pylint: disable=missing-whitespace-after-comma - job_id_dict = runner.asynchronous(cmd, {'args': args, 'kwargs': kwargs}) - job_id = job_id_dict['jid'] + job_id_dict = runner.asynchronous(cmd, {"args": args, "kwargs": kwargs}) + job_id = job_id_dict["jid"] # Default to trying to run as a client module. else: local = salt.client.LocalClient() - log.debug('Command %s will run via local.cmd_async, targeting %s', cmd, target) - log.debug('Running %s, %s, %s, %s, %s', target, cmd, args, kwargs, tgt_type) + log.debug( + "Command %s will run via local.cmd_async, targeting %s", cmd, target + ) + log.debug("Running %s, %s, %s, %s, %s", target, cmd, args, kwargs, tgt_type) # according to https://github.com/saltstack/salt-api/issues/164, tgt_type has changed to expr_form - job_id = local.cmd_async(six.text_type(target), cmd, arg=args, kwarg=kwargs, tgt_type=six.text_type(tgt_type)) - log.info('ret from local.cmd_async is %s', job_id) + job_id = local.cmd_async( + six.text_type(target), + cmd, + arg=args, + kwarg=kwargs, + tgt_type=six.text_type(tgt_type), + ) + log.info("ret from local.cmd_async is %s", job_id) return job_id -def start(token, - control=False, - trigger='!', - groups=None, - groups_pillar_name=None, - fire_all=False, - tag='salt/engines/slack'): - ''' +def start( + token, + control=False, + trigger="!", + groups=None, + groups_pillar_name=None, + fire_all=False, + tag="salt/engines/slack", +): + """ Listen to slack events and forward them to salt, new version - ''' + """ - if (not token) or (not token.startswith('xoxb')): + if (not token) or (not token.startswith("xoxb")): time.sleep(2) # don't respawn too quickly - log.error('Slack bot token not found, bailing...') - raise UserWarning('Slack Engine bot token not configured') + log.error("Slack bot token not found, bailing...") + raise UserWarning("Slack Engine bot token not configured") try: client = SlackClient(token=token) - message_generator = client.generate_triggered_messages(token, trigger, groups, groups_pillar_name) + message_generator = client.generate_triggered_messages( + token, trigger, groups, groups_pillar_name + ) client.run_commands_from_slack_async(message_generator, fire_all, tag, control) except Exception: # pylint: disable=broad-except - raise Exception('{}'.format(traceback.format_exc())) + raise Exception("{}".format(traceback.format_exc())) diff --git a/salt/engines/sqs_events.py b/salt/engines/sqs_events.py index 16f16406e5e..6fadd5c90e6 100644 --- a/salt/engines/sqs_events.py +++ b/salt/engines/sqs_events.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An engine that continuously reads messages from SQS and fires them as events. Note that long polling is utilized to avoid excessive CPU usage. @@ -71,30 +71,35 @@ Additionally you can define cross account sqs: queue: prod owner_acct_id: 111111111111 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time +import salt.utils.event + # Import salt libs import salt.utils.json -import salt.utils.event +from salt.ext import six # Import third party libs try: import boto.sqs + HAS_BOTO = True except ImportError: HAS_BOTO = False -from salt.ext import six - def __virtual__(): if not HAS_BOTO: - return (False, 'Cannot import engine sqs_events because the required boto module is missing') + return ( + False, + "Cannot import engine sqs_events because the required boto module is missing", + ) else: return True @@ -103,64 +108,75 @@ log = logging.getLogger(__name__) def _get_sqs_conn(profile, region=None, key=None, keyid=None): - ''' + """ Get a boto connection to SQS. - ''' + """ if profile: if isinstance(profile, six.string_types): _profile = __opts__[profile] elif isinstance(profile, dict): _profile = profile - key = _profile.get('key', None) - keyid = _profile.get('keyid', None) - region = _profile.get('region', None) + key = _profile.get("key", None) + keyid = _profile.get("keyid", None) + region = _profile.get("region", None) if not region: - region = __opts__.get('sqs.region', 'us-east-1') + region = __opts__.get("sqs.region", "us-east-1") if not key: - key = __opts__.get('sqs.key', None) + key = __opts__.get("sqs.key", None) if not keyid: - keyid = __opts__.get('sqs.keyid', None) + keyid = __opts__.get("sqs.keyid", None) try: - conn = boto.sqs.connect_to_region(region, aws_access_key_id=keyid, - aws_secret_access_key=key) + conn = boto.sqs.connect_to_region( + region, aws_access_key_id=keyid, aws_secret_access_key=key + ) except boto.exception.NoAuthHandlerFound: - log.error('No authentication credentials found when attempting to' - ' make sqs_event engine connection to AWS.') + log.error( + "No authentication credentials found when attempting to" + " make sqs_event engine connection to AWS." + ) return None return conn -def _process_queue(q, q_name, fire_master, tag='salt/engine/sqs', owner_acct_id=None, message_format=None): +def _process_queue( + q, + q_name, + fire_master, + tag="salt/engine/sqs", + owner_acct_id=None, + message_format=None, +): if not q: log.warning( - 'failure connecting to queue: %s, waiting 10 seconds.', - ':'.join([_f for _f in (six.text_type(owner_acct_id), q_name) if _f]) + "failure connecting to queue: %s, waiting 10 seconds.", + ":".join([_f for _f in (six.text_type(owner_acct_id), q_name) if _f]), ) time.sleep(10) else: msgs = q.get_messages(wait_time_seconds=20) for msg in msgs: if message_format == "json": - fire_master(tag=tag, data={'message': salt.utils.json.loads(msg.get_body())}) + fire_master( + tag=tag, data={"message": salt.utils.json.loads(msg.get_body())} + ) else: - fire_master(tag=tag, data={'message': msg.get_body()}) + fire_master(tag=tag, data={"message": msg.get_body()}) msg.delete() -def start(queue, profile=None, tag='salt/engine/sqs', owner_acct_id=None): - ''' +def start(queue, profile=None, tag="salt/engine/sqs", owner_acct_id=None): + """ Listen to sqs and fire message on event bus - ''' - if __opts__.get('__role') == 'master': + """ + if __opts__.get("__role") == "master": fire_master = salt.utils.event.get_master_event( - __opts__, - __opts__['sock_dir'], - listen=False).fire_event + __opts__, __opts__["sock_dir"], listen=False + ).fire_event else: - fire_master = __salt__['event.send'] + fire_master = __salt__["event.send"] - message_format = __opts__.get('sqs.message_format', None) + message_format = __opts__.get("sqs.message_format", None) sqs = _get_sqs_conn(profile) q = None @@ -169,4 +185,11 @@ def start(queue, profile=None, tag='salt/engine/sqs', owner_acct_id=None): q = sqs.get_queue(queue, owner_acct_id=owner_acct_id) q.set_message_class(boto.sqs.message.RawMessage) - _process_queue(q, queue, fire_master, tag=tag, owner_acct_id=owner_acct_id, message_format=message_format) + _process_queue( + q, + queue, + fire_master, + tag=tag, + owner_acct_id=owner_acct_id, + message_format=message_format, + ) diff --git a/salt/engines/stalekey.py b/salt/engines/stalekey.py index 18e9b0bd45b..b216b3fe01c 100644 --- a/salt/engines/stalekey.py +++ b/salt/engines/stalekey.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An engine that uses presence detection to keep track of which minions have been recently connected and remove their keys if they have not been connected for a certain period of time. @@ -19,12 +19,13 @@ Requires that the :conf_master:`minion_data_cache` option be enabled. interval: 3600 expire: 86400 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import time -import logging # Import salt libs import salt.config @@ -41,31 +42,31 @@ log = logging.getLogger(__name__) def __virtual__(): - if not __opts__.get('minion_data_cache'): - return (False, 'stalekey engine requires minion_data_cache to be enabled') + if not __opts__.get("minion_data_cache"): + return (False, "stalekey engine requires minion_data_cache to be enabled") return True def _get_keys(): keys = salt.key.get_key(__opts__) minions = keys.all_keys() - return minions['minions'] + return minions["minions"] def start(interval=3600, expire=604800): ck = salt.utils.minions.CkMinions(__opts__) - presence_file = '{0}/presence.p'.format(__opts__['cachedir']) + presence_file = "{0}/presence.p".format(__opts__["cachedir"]) wheel = salt.wheel.WheelClient(__opts__) while True: - log.debug('Checking for present minions') + log.debug("Checking for present minions") minions = {} if os.path.exists(presence_file): try: - with salt.utils.files.fopen(presence_file, 'r') as f: + with salt.utils.files.fopen(presence_file, "r") as f: minions = salt.utils.msgpack.load(f) except IOError as e: - log.error('Could not open presence file %s: %s', presence_file, e) + log.error("Could not open presence file %s: %s", presence_file, e) time.sleep(interval) continue @@ -83,7 +84,7 @@ def start(interval=3600, expire=604800): elif m in present: minions[m] = now - log.debug('Finished checking for present minions') + log.debug("Finished checking for present minions") # Delete old keys stale_keys = [] for m, seen in six.iteritems(minions): @@ -92,13 +93,13 @@ def start(interval=3600, expire=604800): if stale_keys: for k in stale_keys: - log.info('Removing stale key for %s', k) - wheel.cmd('key.delete', stale_keys) + log.info("Removing stale key for %s", k) + wheel.cmd("key.delete", stale_keys) del minions[k] try: - with salt.utils.files.fopen(presence_file, 'w') as f: + with salt.utils.files.fopen(presence_file, "w") as f: salt.utils.msgpack.dump(minions, f) except IOError as e: - log.error('Could not write to presence file %s: %s', presence_file, e) + log.error("Could not write to presence file %s: %s", presence_file, e) time.sleep(interval) diff --git a/salt/engines/test.py b/salt/engines/test.py index e91dbe59612..3aefbfad70f 100644 --- a/salt/engines/test.py +++ b/salt/engines/test.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" A simple test engine, not intended for real use but as an example -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -15,26 +16,26 @@ log = logging.getLogger(__name__) def event_bus_context(opts): - if opts['__role'] == 'master': + if opts["__role"] == "master": event_bus = salt.utils.event.get_master_event( - opts, - opts['sock_dir'], - listen=True) + opts, opts["sock_dir"], listen=True + ) else: event_bus = salt.utils.event.get_event( - 'minion', - transport=opts['transport'], + "minion", + transport=opts["transport"], opts=opts, - sock_dir=opts['sock_dir'], - listen=True) - log.debug('test engine started') + sock_dir=opts["sock_dir"], + listen=True, + ) + log.debug("test engine started") return event_bus def start(): - ''' + """ Listen to events and write them to a log file - ''' + """ with event_bus_context(__opts__) as event_bus: while True: event = event_bus.get_event() diff --git a/salt/engines/thorium.py b/salt/engines/thorium.py index 241fef63ded..406bec187c2 100644 --- a/salt/engines/thorium.py +++ b/salt/engines/thorium.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Manage the Thorium complex event reaction system -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -9,13 +9,8 @@ import salt.thorium def start(grains=False, grain_keys=None, pillar=False, pillar_keys=None): - ''' + """ Execute the Thorium runtime - ''' - state = salt.thorium.ThorState( - __opts__, - grains, - grain_keys, - pillar, - pillar_keys) + """ + state = salt.thorium.ThorState(__opts__, grains, grain_keys, pillar, pillar_keys) state.start_runtime() diff --git a/salt/engines/webhook.py b/salt/engines/webhook.py index 5cb269ae35b..a2ab964a3a0 100644 --- a/salt/engines/webhook.py +++ b/salt/engines/webhook.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Send events from webhook api -''' +""" from __future__ import absolute_import, print_function, unicode_literals import salt.ext.tornado.httpserver @@ -13,7 +13,7 @@ import salt.utils.event def start(address=None, port=5000, ssl_crt=None, ssl_key=None): - ''' + """ Api to listen for webhooks to send to the reactor. Implement the webhook behavior in an engine. @@ -50,37 +50,43 @@ def start(address=None, port=5000, ssl_crt=None, ssl_key=None): .. note: For making an unsigned key, use the following command `salt-call --local tls.create_self_signed_cert` - ''' - if __opts__.get('__role') == 'master': - fire_master = salt.utils.event.get_master_event(__opts__, __opts__['sock_dir']).fire_event + """ + if __opts__.get("__role") == "master": + fire_master = salt.utils.event.get_master_event( + __opts__, __opts__["sock_dir"] + ).fire_event else: fire_master = None def fire(tag, msg): - ''' + """ How to fire the event - ''' + """ if fire_master: fire_master(msg, tag) else: - __salt__['event.send'](tag, msg) + __salt__["event.send"](tag, msg) - class WebHook(salt.ext.tornado.web.RequestHandler): # pylint: disable=abstract-method + class WebHook( + salt.ext.tornado.web.RequestHandler + ): # pylint: disable=abstract-method def post(self, tag): # pylint: disable=arguments-differ body = self.request.body headers = self.request.headers payload = { - 'headers': headers if isinstance(headers, dict) else dict(headers), - 'body': body, + "headers": headers if isinstance(headers, dict) else dict(headers), + "body": body, } - fire('salt/engines/hook/' + tag, payload) + fire("salt/engines/hook/" + tag, payload) - application = salt.ext.tornado.web.Application([(r"/(.*)", WebHook), ]) + application = salt.ext.tornado.web.Application([(r"/(.*)", WebHook)]) ssl_options = None if all([ssl_crt, ssl_key]): ssl_options = {"certfile": ssl_crt, "keyfile": ssl_key} io_loop = salt.ext.tornado.ioloop.IOLoop(make_current=False) io_loop.make_current() - http_server = salt.ext.tornado.httpserver.HTTPServer(application, ssl_options=ssl_options) + http_server = salt.ext.tornado.httpserver.HTTPServer( + application, ssl_options=ssl_options + ) http_server.listen(port, address=address) io_loop.start() diff --git a/salt/exceptions.py b/salt/exceptions.py index 58cb8b92041..5c255c6d280 100644 --- a/salt/exceptions.py +++ b/salt/exceptions.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" This module is a central location for all salt exceptions -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -17,117 +17,125 @@ log = logging.getLogger(__name__) def _nested_output(obj): - ''' + """ Serialize obj and format for output - ''' + """ # Explicit late import to avoid circular import from salt.output import nested + nested.__opts__ = {} ret = nested.output(obj).rstrip() return ret def get_error_message(error): - ''' + """ Get human readable message from Python Exception - ''' - return error.args[0] if error.args else '' + """ + return error.args[0] if error.args else "" class SaltException(Exception): - ''' + """ Base exception class; all Salt-specific exceptions should subclass this - ''' - def __init__(self, message=''): + """ + + def __init__(self, message=""): # Avoid circular import import salt.utils.stringutils + if not isinstance(message, six.string_types): message = six.text_type(message) - if six.PY3 or isinstance(message, unicode): # pylint: disable=incompatible-py3-code,undefined-variable - super(SaltException, self).__init__( - salt.utils.stringutils.to_str(message) - ) + # pylint: disable=incompatible-py3-code,undefined-variable + if six.PY3 or isinstance(message, unicode): + super(SaltException, self).__init__(salt.utils.stringutils.to_str(message)) self.message = self.strerror = message + # pylint: enable=incompatible-py3-code,undefined-variable elif isinstance(message, str): super(SaltException, self).__init__(message) - self.message = self.strerror = \ - salt.utils.stringutils.to_unicode(message) + self.message = self.strerror = salt.utils.stringutils.to_unicode(message) else: # Some non-string input was passed. Run the parent dunder init with # a str version, and convert the passed value to unicode for the # message/strerror attributes. - super(SaltException, self).__init__(str(message)) # future lint: blacklisted-function - self.message = self.strerror = unicode(message) # pylint: disable=incompatible-py3-code,undefined-variable + # futurdisable lint: blacklisteenable-function + super(SaltException, self).__init__(str(message)) + # future lint: blacklisteenable-function + # pylint: disable=incompatible-py3-code,undefined-variable + self.message = self.strerror = unicode(message) + # pylint: enable=incompatible-py3-code,undefined-variable def __unicode__(self): return self.strerror def pack(self): - ''' + """ Pack this exception into a serializable dictionary that is safe for transport via msgpack - ''' + """ if six.PY3: - return {'message': six.text_type(self), 'args': self.args} + return {"message": six.text_type(self), "args": self.args} return dict(message=self.__unicode__(), args=self.args) class SaltClientError(SaltException): - ''' + """ Problem reading the master root key - ''' + """ class SaltMasterError(SaltException): - ''' + """ Problem reading the master root key - ''' + """ class SaltNoMinionsFound(SaltException): - ''' + """ An attempt to retrieve a list of minions failed - ''' + """ class SaltSyndicMasterError(SaltException): - ''' + """ Problem while proxying a request in the syndication master - ''' + """ class SaltMasterUnresolvableError(SaltException): - ''' + """ Problem resolving the name of the Salt master - ''' + """ class MasterExit(SystemExit): - ''' + """ Rise when the master exits - ''' + """ class AuthenticationError(SaltException): - ''' + """ If sha256 signature fails during decryption - ''' + """ class CommandNotFoundError(SaltException): - ''' + """ Used in modules or grains when a required binary is not available - ''' + """ class CommandExecutionError(SaltException): - ''' + """ Used when a module runs a command which returns an error and wants to show the user the output gracefully instead of dying - ''' - def __init__(self, message='', info=None): + """ + + def __init__(self, message="", info=None): # Avoid circular import import salt.utils.stringutils + try: exc_str_prefix = salt.utils.stringutils.to_unicode(message) except TypeError: @@ -139,16 +147,18 @@ class CommandExecutionError(SaltException): try: exc_str_prefix = six.text_type(message) except UnicodeDecodeError: - exc_str_prefix = salt.utils.stringutils.to_unicode(str(message)) # future lint: disable=blacklisted-function + exc_str_prefix = salt.utils.stringutils.to_unicode( + str(message) + ) # future lint: disable=blacklisted-function self.error = exc_str_prefix self.info = info if self.info: if exc_str_prefix: - if exc_str_prefix[-1] not in '.?!': - exc_str_prefix += '.' - exc_str_prefix += ' ' + if exc_str_prefix[-1] not in ".?!": + exc_str_prefix += "." + exc_str_prefix += " " - exc_str_prefix += 'Additional info follows:\n\n' + exc_str_prefix += "Additional info follows:\n\n" # NOTE: exc_str will be passed to the parent class' constructor and # become self.strerror. exc_str = exc_str_prefix + _nested_output(self.info) @@ -159,10 +169,11 @@ class CommandExecutionError(SaltException): # this information would be redundant). if isinstance(self.info, dict): info_without_changes = copy.deepcopy(self.info) - info_without_changes.pop('changes', None) + info_without_changes.pop("changes", None) if info_without_changes: - self.strerror_without_changes = \ - exc_str_prefix + _nested_output(info_without_changes) + self.strerror_without_changes = exc_str_prefix + _nested_output( + info_without_changes + ) else: # 'changes' was the only key in the info dictionary. We no # longer have any additional info to display. Use the @@ -180,40 +191,43 @@ class CommandExecutionError(SaltException): class LoaderError(SaltException): - ''' + """ Problems loading the right renderer - ''' + """ class PublishError(SaltException): - ''' + """ Problems encountered when trying to publish a command - ''' + """ class MinionError(SaltException): - ''' + """ Minion problems reading uris such as salt:// or http:// - ''' + """ class FileserverConfigError(SaltException): - ''' + """ Used when invalid fileserver settings are detected - ''' + """ class FileLockError(SaltException): - ''' + """ Used when an error occurs obtaining a file lock - ''' - def __init__(self, message, time_start=None, *args, **kwargs): # pylint: disable=keyword-arg-before-vararg + """ + + def __init__( + self, message, time_start=None, *args, **kwargs + ): # pylint: disable=keyword-arg-before-vararg super(FileLockError, self).__init__(message, *args, **kwargs) if time_start is None: log.warning( - 'time_start should be provided when raising a FileLockError. ' - 'Defaulting to current time as a fallback, but this may ' - 'result in an inaccurate timeout.' + "time_start should be provided when raising a FileLockError. " + "Defaulting to current time as a fallback, but this may " + "result in an inaccurate timeout." ) self.time_start = time.time() else: @@ -221,7 +235,7 @@ class FileLockError(SaltException): class GitLockError(SaltException): - ''' + """ Raised when an uncaught error occurs in the midst of obtaining an update/checkout lock in salt.utils.gitfs. @@ -229,47 +243,52 @@ class GitLockError(SaltException): class is *not* as subclass of OSError. This is done intentionally, so that this exception class can be caught in a try/except without being caught as an OSError. - ''' + """ + def __init__(self, errno, message, *args, **kwargs): super(GitLockError, self).__init__(message, *args, **kwargs) self.errno = errno class GitRemoteError(SaltException): - ''' + """ Used by GitFS to denote a problem with the existence of the "origin" remote or part of its configuration - ''' + """ class SaltInvocationError(SaltException, TypeError): - ''' + """ Used when the wrong number of arguments are sent to modules or invalid arguments are specified on the command line - ''' + """ class PkgParseError(SaltException): - ''' + """ Used when of the pkg modules cannot correctly parse the output from the CLI tool (pacman, yum, apt, aptitude, etc) - ''' + """ class SaltRenderError(SaltException): - ''' + """ Used when a renderer needs to raise an explicit error. If a line number and buffer string are passed, get_context will be invoked to get the location of the error. - ''' - def __init__(self, - message, - line_num=None, - buf='', - marker=' <======================', - trace=None): + """ + + def __init__( + self, + message, + line_num=None, + buf="", + marker=" <======================", + trace=None, + ): # Avoid circular import import salt.utils.stringutils + self.error = message try: exc_str = salt.utils.stringutils.to_unicode(message) @@ -282,131 +301,136 @@ class SaltRenderError(SaltException): try: exc_str = six.text_type(message) except UnicodeDecodeError: - exc_str = salt.utils.stringutils.to_unicode(str(message)) # future lint: disable=blacklisted-function + exc_str = salt.utils.stringutils.to_unicode( + str(message) + ) # future lint: disable=blacklisted-function self.line_num = line_num self.buffer = buf - self.context = '' + self.context = "" if trace: - exc_str += '\n{0}\n'.format(trace) + exc_str += "\n{0}\n".format(trace) if self.line_num and self.buffer: # Avoid circular import import salt.utils.templates + self.context = salt.utils.stringutils.get_context( - self.buffer, - self.line_num, - marker=marker + self.buffer, self.line_num, marker=marker ) - exc_str += '; line {0}\n\n{1}'.format( - self.line_num, - salt.utils.stringutils.to_unicode(self.context), + exc_str += "; line {0}\n\n{1}".format( + self.line_num, salt.utils.stringutils.to_unicode(self.context), ) super(SaltRenderError, self).__init__(exc_str) class SaltClientTimeout(SaltException): - ''' + """ Thrown when a job sent through one of the Client interfaces times out Takes the ``jid`` as a parameter - ''' - def __init__(self, message, jid=None, *args, **kwargs): # pylint: disable=keyword-arg-before-vararg + """ + + def __init__( + self, message, jid=None, *args, **kwargs + ): # pylint: disable=keyword-arg-before-vararg super(SaltClientTimeout, self).__init__(message, *args, **kwargs) self.jid = jid class SaltCacheError(SaltException): - ''' + """ Thrown when a problem was encountered trying to read or write from the salt cache - ''' + """ class TimeoutError(SaltException): - ''' + """ Thrown when an opration cannot be completet within a given time limit. - ''' + """ class SaltReqTimeoutError(SaltException): - ''' + """ Thrown when a salt master request call fails to return within the timeout - ''' + """ class TimedProcTimeoutError(SaltException): - ''' + """ Thrown when a timed subprocess does not terminate within the timeout, or if the specified timeout is not an int or a float - ''' + """ class EauthAuthenticationError(SaltException): - ''' + """ Thrown when eauth authentication fails - ''' + """ class TokenAuthenticationError(SaltException): - ''' + """ Thrown when token authentication fails - ''' + """ class SaltDeserializationError(SaltException): - ''' + """ Thrown when salt cannot deserialize data. - ''' + """ class AuthorizationError(SaltException): - ''' + """ Thrown when runner or wheel execution fails due to permissions - ''' + """ class SaltDaemonNotRunning(SaltException): - ''' + """ Throw when a running master/minion/syndic is not running but is needed to perform the requested operation (e.g., eauth). - ''' + """ class SaltRunnerError(SaltException): - ''' + """ Problem in runner - ''' + """ class SaltWheelError(SaltException): - ''' + """ Problem in wheel - ''' + """ class SaltConfigurationError(SaltException): - ''' + """ Configuration error - ''' + """ class SaltSystemExit(SystemExit): - ''' + """ This exception is raised when an unsolvable problem is found. There's nothing else to do, salt should just exit. - ''' + """ + def __init__(self, code=0, msg=None): SystemExit.__init__(self, msg) class SaltCloudException(SaltException): - ''' + """ Generic Salt Cloud Exception - ''' + """ class SaltCloudSystemExit(SaltCloudException): - ''' + """ This exception is raised when the execution should be stopped. - ''' + """ + def __init__(self, message, exit_code=salt.defaults.exitcodes.EX_GENERIC): super(SaltCloudSystemExit, self).__init__(message) self.message = message @@ -414,167 +438,167 @@ class SaltCloudSystemExit(SaltCloudException): class SaltCloudConfigError(SaltCloudException): - ''' + """ Raised when a configuration setting is not found and should exist. - ''' + """ class SaltCloudNotFound(SaltCloudException): - ''' + """ Raised when some cloud provider function cannot find what's being searched. - ''' + """ class SaltCloudExecutionTimeout(SaltCloudException): - ''' + """ Raised when too much time has passed while querying/waiting for data. - ''' + """ class SaltCloudExecutionFailure(SaltCloudException): - ''' + """ Raised when too much failures have occurred while querying/waiting for data. - ''' + """ class SaltCloudPasswordError(SaltCloudException): - ''' + """ Raise when virtual terminal password input failed - ''' + """ class NotImplemented(SaltException): - ''' + """ Used when a module runs a command which returns an error and wants to show the user the output gracefully instead of dying - ''' + """ class TemplateError(SaltException): - ''' + """ Used when a custom error is triggered in a template - ''' + """ class ArgumentValueError(CommandExecutionError): - ''' + """ Used when an invalid argument was passed to a command execution - ''' + """ class CheckError(CommandExecutionError): - ''' + """ Used when a check fails - ''' + """ # Validation related exceptions class InvalidConfigError(CommandExecutionError): - ''' + """ Used when the config is invalid - ''' + """ class InvalidEntityError(CommandExecutionError): - ''' + """ Used when an entity fails validation - ''' + """ # VMware related exceptions class VMwareSaltError(CommandExecutionError): - ''' + """ Used when a VMware object cannot be retrieved - ''' + """ class VMwareRuntimeError(VMwareSaltError): - ''' + """ Used when a runtime error is encountered when communicating with the vCenter - ''' + """ class VMwareConnectionError(VMwareSaltError): - ''' + """ Used when the client fails to connect to a either a VMware vCenter server or to a ESXi host - ''' + """ class VMwareObjectRetrievalError(VMwareSaltError): - ''' + """ Used when a VMware object cannot be retrieved - ''' + """ class VMwareObjectNotFoundError(VMwareSaltError): - ''' + """ Used when a VMware object was not found - ''' + """ class VMwareObjectExistsError(VMwareSaltError): - ''' + """ Used when a VMware object already exists - ''' + """ class VMwareMultipleObjectsError(VMwareObjectRetrievalError): - ''' + """ Used when multiple objects were retrieved (and one was expected) - ''' + """ class VMwareNotFoundError(VMwareSaltError): - ''' + """ Used when a VMware object was not found - ''' + """ class VMwareApiError(VMwareSaltError): - ''' + """ Used when representing a generic VMware API error - ''' + """ class VMwareFileNotFoundError(VMwareApiError): - ''' + """ Used when representing a generic VMware error if a file not found - ''' + """ class VMwareSystemError(VMwareSaltError): - ''' + """ Used when representing a generic VMware system error - ''' + """ class VMwarePowerOnError(VMwareSaltError): - ''' + """ Used when error occurred during power on - ''' + """ class VMwareVmRegisterError(VMwareSaltError): - ''' + """ Used when a configuration parameter is incorrect - ''' + """ class VMwareVmCreationError(VMwareSaltError): - ''' + """ Used when a configuration parameter is incorrect - ''' + """ class MissingSmb(SaltException): - ''' + """ Raised when no smb library is found. - ''' + """ class LoggingRuntimeError(RuntimeError): - ''' + """ Raised when we encounter an error while logging - ''' + """ diff --git a/salt/executors/__init__.py b/salt/executors/__init__.py index 8e0f6918c6a..ccb0e8706d1 100644 --- a/salt/executors/__init__.py +++ b/salt/executors/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Executors Directory -''' +""" diff --git a/salt/executors/direct_call.py b/salt/executors/direct_call.py index 76aae3ffa90..d3d32d9621b 100644 --- a/salt/executors/direct_call.py +++ b/salt/executors/direct_call.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Direct call executor module -''' +""" from __future__ import absolute_import, print_function, unicode_literals def execute(opts, data, func, args, kwargs): - ''' + """ Directly calls the given function with arguments - ''' + """ return func(*args, **kwargs) diff --git a/salt/executors/docker.py b/salt/executors/docker.py index 1360c8fb3e4..3e97945f190 100644 --- a/salt/executors/docker.py +++ b/salt/executors/docker.py @@ -1,46 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" Docker executor module .. versionadded: 2019.2.0 Used with the docker proxy minion. -''' +""" from __future__ import absolute_import, unicode_literals - -__virtualname__ = 'docker' +__virtualname__ = "docker" DOCKER_MOD_MAP = { - 'state.sls': 'docker.sls', - 'state.apply': 'docker.apply', - 'state.highstate': 'docker.highstate', + "state.sls": "docker.sls", + "state.apply": "docker.apply", + "state.highstate": "docker.highstate", } def __virtual__(): - if 'proxy' not in __opts__: - return False, 'Docker executor is only meant to be used with Docker Proxy Minions' - if __opts__.get('proxy', {}).get('proxytype') != __virtualname__: - return False, 'Proxytype does not match: {0}'.format(__virtualname__) + if "proxy" not in __opts__: + return ( + False, + "Docker executor is only meant to be used with Docker Proxy Minions", + ) + if __opts__.get("proxy", {}).get("proxytype") != __virtualname__: + return False, "Proxytype does not match: {0}".format(__virtualname__) return True def execute(opts, data, func, args, kwargs): - ''' + """ Directly calls the given function with arguments - ''' - if data['fun'] == 'saltutil.find_job': - return __executors__['direct_call.execute'](opts, data, func, args, kwargs) - if data['fun'] in DOCKER_MOD_MAP: - return __executors__['direct_call.execute'](opts, data, __salt__[DOCKER_MOD_MAP[data['fun']]], [opts['proxy']['name']] + args, kwargs) - return __salt__['docker.call'](opts['proxy']['name'], data['fun'], *args, **kwargs) + """ + if data["fun"] == "saltutil.find_job": + return __executors__["direct_call.execute"](opts, data, func, args, kwargs) + if data["fun"] in DOCKER_MOD_MAP: + return __executors__["direct_call.execute"]( + opts, + data, + __salt__[DOCKER_MOD_MAP[data["fun"]]], + [opts["proxy"]["name"]] + args, + kwargs, + ) + return __salt__["docker.call"](opts["proxy"]["name"], data["fun"], *args, **kwargs) def allow_missing_func(function): # pylint: disable=unused-argument - ''' + """ Allow all calls to be passed through to docker container. The docker call will use direct_call, which will return back if the module was unable to be run. - ''' + """ return True diff --git a/salt/executors/splay.py b/salt/executors/splay.py index 8c815a1fdd7..d30826559f3 100644 --- a/salt/executors/splay.py +++ b/salt/executors/splay.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Splay function calls across targeted minions -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import time + import logging +import time import salt.utils.stringutils @@ -22,15 +23,15 @@ def __init__(opts): def _get_hash(): - ''' + """ Jenkins One-At-A-Time Hash Function More Info: http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time - ''' + """ # Using bitmask to emulate rollover behavior of C unsigned 32 bit int - bitmask = 0xffffffff + bitmask = 0xFFFFFFFF h = 0 - for i in bytearray(salt.utils.stringutils.to_bytes(__grains__['id'])): + for i in bytearray(salt.utils.stringutils.to_bytes(__grains__["id"])): h = (h + i) & bitmask h = (h + (h << 10)) & bitmask h = (h ^ (h >> 6)) & bitmask @@ -47,7 +48,7 @@ def _calc_splay(splaytime): def execute(opts, data, func, args, kwargs): - ''' + """ Splay a salt function call execution time across minions over a number of seconds (default: 300) @@ -69,14 +70,14 @@ def execute(opts, data, func, args, kwargs): # With specified splaytime (5 minutes) and timeout with 10 second buffer salt -t 310 --module-executors='[splay, direct_call]' --executor-opts='{splaytime: 300}' '*' pkg.version cowsay - ''' - if 'executor_opts' in data and 'splaytime' in data['executor_opts']: - splaytime = data['executor_opts']['splaytime'] + """ + if "executor_opts" in data and "splaytime" in data["executor_opts"]: + splaytime = data["executor_opts"]["splaytime"] else: - splaytime = opts.get('splaytime', _DEFAULT_SPLAYTIME) + splaytime = opts.get("splaytime", _DEFAULT_SPLAYTIME) if splaytime <= 0: - raise ValueError('splaytime must be a positive integer') - fun_name = data.get('fun') + raise ValueError("splaytime must be a positive integer") + fun_name = data.get("fun") my_delay = _calc_splay(splaytime) log.debug("Splay is sleeping %s secs on %s", my_delay, fun_name) diff --git a/salt/executors/sudo.py b/salt/executors/sudo.py index 258aa8ead1c..fb5d3123569 100644 --- a/salt/executors/sudo.py +++ b/salt/executors/sudo.py @@ -1,29 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Sudo executor module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.syspaths + # Import salt libs import salt.utils.json import salt.utils.path -import salt.syspaths - from salt.ext import six from salt.ext.six.moves import shlex_quote as _cmd_quote -__virtualname__ = 'sudo' +__virtualname__ = "sudo" def __virtual__(): - if salt.utils.path.which('sudo') and __opts__.get('sudo_user'): + if salt.utils.path.which("sudo") and __opts__.get("sudo_user"): return __virtualname__ return False def execute(opts, data, func, args, kwargs): - ''' + """ Allow for the calling of execution modules via sudo. This module is invoked by the minion if the ``sudo_user`` minion config is @@ -50,30 +50,35 @@ def execute(opts, data, func, args, kwargs): sudo -u saltdev salt-call cmd.run 'cat /etc/sudoers' being run on ``sudo_minion``. - ''' - cmd = ['sudo', - '-u', opts.get('sudo_user'), - 'salt-call', - '--out', 'json', - '--metadata', - '-c', opts.get('config_dir'), - '--', - data.get('fun')] - if data['fun'] in ('state.sls', 'state.highstate', 'state.apply'): - kwargs['concurrent'] = True + """ + cmd = [ + "sudo", + "-u", + opts.get("sudo_user"), + "salt-call", + "--out", + "json", + "--metadata", + "-c", + opts.get("config_dir"), + "--", + data.get("fun"), + ] + if data["fun"] in ("state.sls", "state.highstate", "state.apply"): + kwargs["concurrent"] = True for arg in args: cmd.append(_cmd_quote(six.text_type(arg))) for key in kwargs: - cmd.append(_cmd_quote('{0}={1}'.format(key, kwargs[key]))) + cmd.append(_cmd_quote("{0}={1}".format(key, kwargs[key]))) - cmd_ret = __salt__['cmd.run_all'](cmd, use_vt=True, python_shell=False) + cmd_ret = __salt__["cmd.run_all"](cmd, use_vt=True, python_shell=False) - if cmd_ret['retcode'] == 0: - cmd_meta = salt.utils.json.loads(cmd_ret['stdout'])['local'] - ret = cmd_meta['return'] - __context__['retcode'] = cmd_meta.get('retcode', 0) + if cmd_ret["retcode"] == 0: + cmd_meta = salt.utils.json.loads(cmd_ret["stdout"])["local"] + ret = cmd_meta["return"] + __context__["retcode"] = cmd_meta.get("retcode", 0) else: - ret = cmd_ret['stderr'] - __context__['retcode'] = cmd_ret['retcode'] + ret = cmd_ret["stderr"] + __context__["retcode"] = cmd_ret["retcode"] return ret diff --git a/salt/ext/tornado/gen.py b/salt/ext/tornado/gen.py index 6cb19730bf1..72f422ce28f 100644 --- a/salt/ext/tornado/gen.py +++ b/salt/ext/tornado/gen.py @@ -115,13 +115,13 @@ try: # py35+ from collections.abc import Generator as GeneratorType # type: ignore except ImportError: - from backports_abc import Generator as GeneratorType # type: ignore + from salt.ext.backports_abc import Generator as GeneratorType # type: ignore try: # py35+ from inspect import isawaitable # type: ignore except ImportError: - from backports_abc import isawaitable + from salt.ext.backports_abc import isawaitable except ImportError: if 'APPENGINE_RUNTIME' not in os.environ: raise diff --git a/salt/fileclient.py b/salt/fileclient.py index f154e4fd433..d606bea99e6 100644 --- a/salt/fileclient.py +++ b/salt/fileclient.py @@ -1,29 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" Classes that manage file clients -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import contextlib import errno +import ftplib import logging import os -import string import shutil -import ftplib -from salt.ext.tornado.httputil import parse_response_start_line, HTTPHeaders, HTTPInputError -import salt.utils.atomicfile +import string -# Import salt libs -from salt.exceptions import ( - CommandExecutionError, MinionError -) import salt.client +import salt.ext.six.moves.BaseHTTPServer as BaseHTTPServer +import salt.fileserver import salt.loader import salt.payload import salt.transport.client -import salt.fileserver +import salt.utils.atomicfile import salt.utils.data import salt.utils.files import salt.utils.gzip_util @@ -35,13 +31,21 @@ import salt.utils.stringutils import salt.utils.templates import salt.utils.url import salt.utils.versions -from salt.utils.openstack.swift import SaltSwift + +# Import salt libs +from salt.exceptions import CommandExecutionError, MinionError # pylint: disable=no-name-in-module,import-error from salt.ext import six -import salt.ext.six.moves.BaseHTTPServer as BaseHTTPServer from salt.ext.six.moves.urllib.error import HTTPError, URLError from salt.ext.six.moves.urllib.parse import urlparse, urlunparse +from salt.ext.tornado.httputil import ( + HTTPHeaders, + HTTPInputError, + parse_response_start_line, +) +from salt.utils.openstack.swift import SaltSwift + # pylint: enable=no-name-in-module,import-error log = logging.getLogger(__name__) @@ -49,26 +53,24 @@ MAX_FILENAME_LENGTH = 255 def get_file_client(opts, pillar=False): - ''' + """ Read in the ``file_client`` option and return the correct type of file server - ''' - client = opts.get('file_client', 'remote') - if pillar and client == 'local': - client = 'pillar' - return { - 'remote': RemoteClient, - 'local': FSClient, - 'pillar': PillarClient, - }.get(client, RemoteClient)(opts) + """ + client = opts.get("file_client", "remote") + if pillar and client == "local": + client = "pillar" + return {"remote": RemoteClient, "local": FSClient, "pillar": PillarClient}.get( + client, RemoteClient + )(opts) def decode_dict_keys_to_str(src): - ''' + """ Convert top level keys from bytes to strings if possible. This is necessary because Python 3 makes a distinction between these types. - ''' + """ if not six.PY3 or not isinstance(src, dict): return src @@ -84,9 +86,10 @@ def decode_dict_keys_to_str(src): class Client(object): - ''' + """ Base class for Salt file interactions - ''' + """ + def __init__(self, opts): self.opts = opts self.utils = salt.loader.utils(self.opts) @@ -100,24 +103,24 @@ class Client(object): def __setstate__(self, state): # This will polymorphically call __init__ # in the derived class. - self.__init__(state['opts']) + self.__init__(state["opts"]) def __getstate__(self): - return {'opts': self.opts} + return {"opts": self.opts} def _check_proto(self, path): - ''' + """ Make sure that this path is intended for the salt master and trim it - ''' - if not path.startswith('salt://'): - raise MinionError('Unsupported path: {0}'.format(path)) + """ + if not path.startswith("salt://"): + raise MinionError("Unsupported path: {0}".format(path)) file_path, saltenv = salt.utils.url.parse(path) return file_path def _file_local_list(self, dest): - ''' + """ Helper util to return a list of files in a directory - ''' + """ if os.path.isdir(dest): destdir = dest else: @@ -133,15 +136,12 @@ class Client(object): return filelist @contextlib.contextmanager - def _cache_loc(self, path, saltenv='base', cachedir=None): - ''' + def _cache_loc(self, path, saltenv="base", cachedir=None): + """ Return the local location to cache the file, cache dirs will be made - ''' + """ cachedir = self.get_cachedir(cachedir) - dest = salt.utils.path.join(cachedir, - 'files', - saltenv, - path) + dest = salt.utils.path.join(cachedir, "files", saltenv, path) destdir = os.path.dirname(dest) with salt.utils.files.set_umask(0o077): # remove destdir if it is a regular file to avoid an OSError when @@ -160,88 +160,91 @@ class Client(object): def get_cachedir(self, cachedir=None): if cachedir is None: - cachedir = self.opts['cachedir'] + cachedir = self.opts["cachedir"] elif not os.path.isabs(cachedir): - cachedir = os.path.join(self.opts['cachedir'], cachedir) + cachedir = os.path.join(self.opts["cachedir"], cachedir) return cachedir - def get_file(self, - path, - dest='', - makedirs=False, - saltenv='base', - gzip=None, - cachedir=None): - ''' + def get_file( + self, path, dest="", makedirs=False, saltenv="base", gzip=None, cachedir=None + ): + """ Copies a file from the local files or master depending on implementation - ''' + """ raise NotImplementedError - def file_list_emptydirs(self, saltenv='base', prefix=''): - ''' + def file_list_emptydirs(self, saltenv="base", prefix=""): + """ List the empty dirs - ''' + """ raise NotImplementedError - def cache_file(self, path, saltenv='base', cachedir=None, source_hash=None): - ''' + def cache_file(self, path, saltenv="base", cachedir=None, source_hash=None): + """ Pull a file down from the file server and store it in the minion file cache - ''' + """ return self.get_url( - path, '', True, saltenv, cachedir=cachedir, source_hash=source_hash) + path, "", True, saltenv, cachedir=cachedir, source_hash=source_hash + ) - def cache_files(self, paths, saltenv='base', cachedir=None): - ''' + def cache_files(self, paths, saltenv="base", cachedir=None): + """ Download a list of files stored on the master and put them in the minion file cache - ''' + """ ret = [] if isinstance(paths, six.string_types): - paths = paths.split(',') + paths = paths.split(",") for path in paths: ret.append(self.cache_file(path, saltenv, cachedir=cachedir)) return ret - def cache_master(self, saltenv='base', cachedir=None): - ''' + def cache_master(self, saltenv="base", cachedir=None): + """ Download and cache all files on a master in a specified environment - ''' + """ ret = [] for path in self.file_list(saltenv): ret.append( - self.cache_file( - salt.utils.url.create(path), saltenv, cachedir=cachedir) + self.cache_file(salt.utils.url.create(path), saltenv, cachedir=cachedir) ) return ret - def cache_dir(self, path, saltenv='base', include_empty=False, - include_pat=None, exclude_pat=None, cachedir=None): - ''' + def cache_dir( + self, + path, + saltenv="base", + include_empty=False, + include_pat=None, + exclude_pat=None, + cachedir=None, + ): + """ Download all of the files in a subdir of the master - ''' + """ ret = [] path = self._check_proto(salt.utils.data.decode(path)) # We want to make sure files start with this *directory*, use # '/' explicitly because the master (that's generating the # list of files) only runs on POSIX - if not path.endswith('/'): - path = path + '/' + if not path.endswith("/"): + path = path + "/" - log.info( - 'Caching directory \'%s\' for environment \'%s\'', path, saltenv - ) + log.info("Caching directory '%s' for environment '%s'", path, saltenv) # go through the list of all files finding ones that are in # the target directory and caching them for fn_ in self.file_list(saltenv): fn_ = salt.utils.data.decode(fn_) if fn_.strip() and fn_.startswith(path): if salt.utils.stringutils.check_include_exclude( - fn_, include_pat, exclude_pat): + fn_, include_pat, exclude_pat + ): fn_ = self.cache_file( - salt.utils.url.create(fn_), saltenv, cachedir=cachedir) + salt.utils.url.create(fn_), saltenv, cachedir=cachedir + ) if fn_: ret.append(fn_) @@ -257,22 +260,21 @@ class Client(object): # prefix = separated[0] cachedir = self.get_cachedir(cachedir) - dest = salt.utils.path.join(cachedir, 'files', saltenv) + dest = salt.utils.path.join(cachedir, "files", saltenv) for fn_ in self.file_list_emptydirs(saltenv): fn_ = salt.utils.data.decode(fn_) if fn_.startswith(path): - minion_dir = '{0}/{1}'.format(dest, fn_) + minion_dir = "{0}/{1}".format(dest, fn_) if not os.path.isdir(minion_dir): os.makedirs(minion_dir) ret.append(minion_dir) return ret def cache_local_file(self, path, **kwargs): - ''' + """ Cache a local file on the minion in the localfiles cache - ''' - dest = os.path.join(self.opts['cachedir'], 'localfiles', - path.lstrip('/')) + """ + dest = os.path.join(self.opts["cachedir"], "localfiles", path.lstrip("/")) destdir = os.path.dirname(dest) if not os.path.isdir(destdir): @@ -281,41 +283,41 @@ class Client(object): shutil.copyfile(path, dest) return dest - def file_local_list(self, saltenv='base'): - ''' + def file_local_list(self, saltenv="base"): + """ List files in the local minion files and localfiles caches - ''' - filesdest = os.path.join(self.opts['cachedir'], 'files', saltenv) - localfilesdest = os.path.join(self.opts['cachedir'], 'localfiles') + """ + filesdest = os.path.join(self.opts["cachedir"], "files", saltenv) + localfilesdest = os.path.join(self.opts["cachedir"], "localfiles") fdest = self._file_local_list(filesdest) ldest = self._file_local_list(localfilesdest) return sorted(fdest.union(ldest)) - def file_list(self, saltenv='base', prefix=''): - ''' + def file_list(self, saltenv="base", prefix=""): + """ This function must be overwritten - ''' + """ return [] - def dir_list(self, saltenv='base', prefix=''): - ''' + def dir_list(self, saltenv="base", prefix=""): + """ This function must be overwritten - ''' + """ return [] - def symlink_list(self, saltenv='base', prefix=''): - ''' + def symlink_list(self, saltenv="base", prefix=""): + """ This function must be overwritten - ''' + """ return {} - def is_cached(self, path, saltenv='base', cachedir=None): - ''' + def is_cached(self, path, saltenv="base", cachedir=None): + """ Returns the full path to a file if it is cached locally on the minion otherwise returns a blank string - ''' - if path.startswith('salt://'): + """ + if path.startswith("salt://"): path, senv = salt.utils.url.parse(path) if senv: saltenv = senv @@ -324,69 +326,70 @@ class Client(object): # also strip escape character '|' localsfilesdest = os.path.join( - self.opts['cachedir'], 'localfiles', path.lstrip('|/')) + self.opts["cachedir"], "localfiles", path.lstrip("|/") + ) filesdest = os.path.join( - self.opts['cachedir'], 'files', saltenv, path.lstrip('|/')) + self.opts["cachedir"], "files", saltenv, path.lstrip("|/") + ) extrndest = self._extrn_path(path, saltenv, cachedir=cachedir) if os.path.exists(filesdest): return salt.utils.url.escape(filesdest) if escaped else filesdest elif os.path.exists(localsfilesdest): - return salt.utils.url.escape(localsfilesdest) \ - if escaped \ - else localsfilesdest + return ( + salt.utils.url.escape(localsfilesdest) if escaped else localsfilesdest + ) elif os.path.exists(extrndest): return extrndest - return '' + return "" def list_states(self, saltenv): - ''' + """ Return a list of all available sls modules on the master for a given environment - ''' + """ states = set() for path in self.file_list(saltenv): if salt.utils.platform.is_windows(): - path = path.replace('\\', '/') - if path.endswith('.sls'): + path = path.replace("\\", "/") + if path.endswith(".sls"): # is an sls module! - if path.endswith('/init.sls'): - states.add(path.replace('/', '.')[:-9]) + if path.endswith("/init.sls"): + states.add(path.replace("/", ".")[:-9]) else: - states.add(path.replace('/', '.')[:-4]) + states.add(path.replace("/", ".")[:-4]) return sorted(states) def get_state(self, sls, saltenv, cachedir=None): - ''' + """ Get a state file from the master and store it in the local minion cache; return the location of the file - ''' - if '.' in sls: - sls = sls.replace('.', '/') - sls_url = salt.utils.url.create(sls + '.sls') - init_url = salt.utils.url.create(sls + '/init.sls') + """ + if "." in sls: + sls = sls.replace(".", "/") + sls_url = salt.utils.url.create(sls + ".sls") + init_url = salt.utils.url.create(sls + "/init.sls") for path in [sls_url, init_url]: dest = self.cache_file(path, saltenv, cachedir=cachedir) if dest: - return {'source': path, 'dest': dest} + return {"source": path, "dest": dest} return {} - def get_dir(self, path, dest='', saltenv='base', gzip=None, - cachedir=None): - ''' + def get_dir(self, path, dest="", saltenv="base", gzip=None, cachedir=None): + """ Get a directory recursively from the salt-master - ''' + """ ret = [] # Strip trailing slash - path = self._check_proto(path).rstrip('/') + path = self._check_proto(path).rstrip("/") # Break up the path into a list containing the bottom-level directory # (the one being recursively copied) and the directories preceding it - separated = path.rsplit('/', 1) + separated = path.rsplit("/", 1) if len(separated) != 2: # No slashes in path. (This means all files in saltenv will be # copied) - prefix = '' + prefix = "" else: prefix = separated[0] @@ -395,19 +398,21 @@ class Client(object): # Prevent files in "salt://foobar/" (or salt://foo.sh) from # matching a path of "salt://foo" try: - if fn_[len(path)] != '/': + if fn_[len(path)] != "/": continue except IndexError: continue # Remove the leading directories from path to derive # the relative path on the minion. - minion_relpath = fn_[len(prefix):].lstrip('/') + minion_relpath = fn_[len(prefix) :].lstrip("/") ret.append( - self.get_file( - salt.utils.url.create(fn_), - '{0}/{1}'.format(dest, minion_relpath), - True, saltenv, gzip - ) + self.get_file( + salt.utils.url.create(fn_), + "{0}/{1}".format(dest, minion_relpath), + True, + saltenv, + gzip, + ) ) # Replicate empty dirs from master try: @@ -415,14 +420,14 @@ class Client(object): # Prevent an empty dir "salt://foobar/" from matching a path of # "salt://foo" try: - if fn_[len(path)] != '/': + if fn_[len(path)] != "/": continue except IndexError: continue # Remove the leading directories from path to derive # the relative path on the minion. - minion_relpath = fn_[len(prefix):].lstrip('/') - minion_mkdir = '{0}/{1}'.format(dest, minion_relpath) + minion_relpath = fn_[len(prefix) :].lstrip("/") + minion_mkdir = "{0}/{1}".format(dest, minion_relpath) if not os.path.isdir(minion_mkdir): os.makedirs(minion_mkdir) ret.append(minion_mkdir) @@ -431,23 +436,33 @@ class Client(object): ret.sort() return ret - def get_url(self, url, dest, makedirs=False, saltenv='base', - no_cache=False, cachedir=None, source_hash=None): - ''' + def get_url( + self, + url, + dest, + makedirs=False, + saltenv="base", + no_cache=False, + cachedir=None, + source_hash=None, + ): + """ Get a single file from a URL. - ''' + """ url_data = urlparse(url) url_scheme = url_data.scheme - url_path = os.path.join( - url_data.netloc, url_data.path).rstrip(os.sep) + url_path = os.path.join(url_data.netloc, url_data.path).rstrip(os.sep) # If dest is a directory, rewrite dest with filename - if dest is not None \ - and (os.path.isdir(dest) or dest.endswith(('/', '\\'))): - if url_data.query or len(url_data.path) > 1 and not url_data.path.endswith('/'): - strpath = url.split('/')[-1] + if dest is not None and (os.path.isdir(dest) or dest.endswith(("/", "\\"))): + if ( + url_data.query + or len(url_data.path) > 1 + and not url_data.path.endswith("/") + ): + strpath = url.split("/")[-1] else: - strpath = 'index.html' + strpath = "index.html" if salt.utils.platform.is_windows(): strpath = salt.utils.path.sanitize_win_path(strpath) @@ -455,25 +470,25 @@ class Client(object): dest = os.path.join(dest, strpath) if url_scheme and url_scheme.lower() in string.ascii_lowercase: - url_path = ':'.join((url_scheme, url_path)) - url_scheme = 'file' + url_path = ":".join((url_scheme, url_path)) + url_scheme = "file" - if url_scheme in ('file', ''): + if url_scheme in ("file", ""): # Local filesystem if not os.path.isabs(url_path): raise CommandExecutionError( - 'Path \'{0}\' is not absolute'.format(url_path) + "Path '{0}' is not absolute".format(url_path) ) if dest is None: - with salt.utils.files.fopen(url_path, 'rb') as fp_: + with salt.utils.files.fopen(url_path, "rb") as fp_: data = fp_.read() return data return url_path - if url_scheme == 'salt': + if url_scheme == "salt": result = self.get_file(url, dest, makedirs, saltenv, cachedir=cachedir) if result and dest is None: - with salt.utils.files.fopen(result, 'rb') as fp_: + with salt.utils.files.fopen(result, "rb") as fp_: data = fp_.read() return data return result @@ -484,17 +499,20 @@ class Client(object): if makedirs: os.makedirs(destdir) else: - return '' + return "" elif not no_cache: dest = self._extrn_path(url, saltenv, cachedir=cachedir) if source_hash is not None: try: - source_hash = source_hash.split('=')[-1] + source_hash = source_hash.split("=")[-1] form = salt.utils.files.HASHES_REVMAP[len(source_hash)] if salt.utils.hashutils.get_hash(dest, form) == source_hash: log.debug( - 'Cached copy of %s (%s) matches source_hash %s, ' - 'skipping download', url, dest, source_hash + "Cached copy of %s (%s) matches source_hash %s, " + "skipping download", + url, + dest, + source_hash, ) return dest except (AttributeError, KeyError, IOError, OSError): @@ -503,37 +521,41 @@ class Client(object): if not os.path.isdir(destdir): os.makedirs(destdir) - if url_data.scheme == 's3': + if url_data.scheme == "s3": try: + def s3_opt(key, default=None): - ''' + """ Get value of s3. from Minion config or from Pillar - ''' - if 's3.' + key in self.opts: - return self.opts['s3.' + key] + """ + if "s3." + key in self.opts: + return self.opts["s3." + key] try: - return self.opts['pillar']['s3'][key] + return self.opts["pillar"]["s3"][key] except (KeyError, TypeError): return default - self.utils['s3.query'](method='GET', - bucket=url_data.netloc, - path=url_data.path[1:], - return_bin=False, - local_file=dest, - action=None, - key=s3_opt('key'), - keyid=s3_opt('keyid'), - service_url=s3_opt('service_url'), - verify_ssl=s3_opt('verify_ssl', True), - location=s3_opt('location'), - path_style=s3_opt('path_style', False), - https_enable=s3_opt('https_enable', True)) + + self.utils["s3.query"]( + method="GET", + bucket=url_data.netloc, + path=url_data.path[1:], + return_bin=False, + local_file=dest, + action=None, + key=s3_opt("key"), + keyid=s3_opt("keyid"), + service_url=s3_opt("service_url"), + verify_ssl=s3_opt("verify_ssl", True), + location=s3_opt("location"), + path_style=s3_opt("path_style", False), + https_enable=s3_opt("https_enable", True), + ) return dest except Exception as exc: # pylint: disable=broad-except raise MinionError( - 'Could not fetch from {0}. Exception: {1}'.format(url, exc) + "Could not fetch from {0}. Exception: {1}".format(url, exc) ) - if url_data.scheme == 'ftp': + if url_data.scheme == "ftp": try: ftp = ftplib.FTP() ftp_port = url_data.port @@ -541,50 +563,61 @@ class Client(object): ftp_port = 21 ftp.connect(url_data.hostname, ftp_port) ftp.login(url_data.username, url_data.password) - remote_file_path = url_data.path.lstrip('/') - with salt.utils.files.fopen(dest, 'wb') as fp_: - ftp.retrbinary('RETR {0}'.format(remote_file_path), fp_.write) + remote_file_path = url_data.path.lstrip("/") + with salt.utils.files.fopen(dest, "wb") as fp_: + ftp.retrbinary("RETR {0}".format(remote_file_path), fp_.write) ftp.quit() return dest except Exception as exc: # pylint: disable=broad-except - raise MinionError('Could not retrieve {0} from FTP server. Exception: {1}'.format(url, exc)) + raise MinionError( + "Could not retrieve {0} from FTP server. Exception: {1}".format( + url, exc + ) + ) - if url_data.scheme == 'swift': + if url_data.scheme == "swift": try: + def swift_opt(key, default): - ''' + """ Get value of from Minion config or from Pillar - ''' + """ if key in self.opts: return self.opts[key] try: - return self.opts['pillar'][key] + return self.opts["pillar"][key] except (KeyError, TypeError): return default - swift_conn = SaltSwift(swift_opt('keystone.user', None), - swift_opt('keystone.tenant', None), - swift_opt('keystone.auth_url', None), - swift_opt('keystone.password', None)) + swift_conn = SaltSwift( + swift_opt("keystone.user", None), + swift_opt("keystone.tenant", None), + swift_opt("keystone.auth_url", None), + swift_opt("keystone.password", None), + ) - swift_conn.get_object(url_data.netloc, - url_data.path[1:], - dest) + swift_conn.get_object(url_data.netloc, url_data.path[1:], dest) return dest except Exception: # pylint: disable=broad-except - raise MinionError('Could not fetch from {0}'.format(url)) + raise MinionError("Could not fetch from {0}".format(url)) get_kwargs = {} - if url_data.username is not None \ - and url_data.scheme in ('http', 'https'): + if url_data.username is not None and url_data.scheme in ("http", "https"): netloc = url_data.netloc - at_sign_pos = netloc.rfind('@') + at_sign_pos = netloc.rfind("@") if at_sign_pos != -1: - netloc = netloc[at_sign_pos + 1:] + netloc = netloc[at_sign_pos + 1 :] fixed_url = urlunparse( - (url_data.scheme, netloc, url_data.path, - url_data.params, url_data.query, url_data.fragment)) - get_kwargs['auth'] = (url_data.username, url_data.password) + ( + url_data.scheme, + netloc, + url_data.path, + url_data.params, + url_data.query, + url_data.fragment, + ) + ) + get_kwargs["auth"] = (url_data.username, url_data.password) else: fixed_url = url @@ -620,7 +653,7 @@ class Client(object): def on_header(hdr): if write_body[1] is not False and write_body[2] is None: - if not hdr.strip() and 'Content-Type' not in write_body[1]: + if not hdr.strip() and "Content-Type" not in write_body[1]: # If write_body[0] is True, then we are not following a # redirect (initial response was a 200 OK). So there is # no need to reset write_body[0]. @@ -634,16 +667,18 @@ class Client(object): # Try to find out what content type encoding is used if # this is a text file write_body[1].parse_line(hdr) # pylint: disable=no-member - if 'Content-Type' in write_body[1]: - content_type = write_body[1].get('Content-Type') # pylint: disable=no-member - if not content_type.startswith('text'): + if "Content-Type" in write_body[1]: + content_type = write_body[1].get( + "Content-Type" + ) # pylint: disable=no-member + if not content_type.startswith("text"): write_body[1] = write_body[2] = False else: - encoding = 'utf-8' - fields = content_type.split(';') + encoding = "utf-8" + fields = content_type.split(";") for field in fields: - if 'encoding' in field: - encoding = field.split('encoding=')[-1] + if "encoding" in field: + encoding = field.split("encoding=")[-1] write_body[2] = encoding # We have found our encoding. Stop processing headers. write_body[1] = False @@ -676,11 +711,14 @@ class Client(object): if write_body[2]: chunk = chunk.decode(write_body[2]) result.append(chunk) + else: - dest_tmp = u"{0}.part".format(dest) + dest_tmp = "{0}.part".format(dest) # We need an open filehandle to use in the on_chunk callback, # that's why we're not using a with clause here. - destfp = salt.utils.files.fopen(dest_tmp, 'wb') # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + destfp = salt.utils.files.fopen(dest_tmp, "wb") + # pylint: enable=resource-leakage def on_chunk(chunk): if write_body[0]: @@ -696,64 +734,64 @@ class Client(object): opts=self.opts, **get_kwargs ) - if 'handle' not in query: - raise MinionError('Error: {0} reading {1}'.format(query['error'], url)) + if "handle" not in query: + raise MinionError("Error: {0} reading {1}".format(query["error"], url)) if no_cache: if write_body[2]: - return ''.join(result) - return b''.join(result) + return "".join(result) + return b"".join(result) else: destfp.close() destfp = None salt.utils.files.rename(dest_tmp, dest) return dest except HTTPError as exc: - raise MinionError('HTTP error {0} reading {1}: {3}'.format( - exc.code, - url, - *BaseHTTPServer.BaseHTTPRequestHandler.responses[exc.code])) + raise MinionError( + "HTTP error {0} reading {1}: {3}".format( + exc.code, + url, + *BaseHTTPServer.BaseHTTPRequestHandler.responses[exc.code] + ) + ) except URLError as exc: - raise MinionError('Error reading {0}: {1}'.format(url, exc.reason)) + raise MinionError("Error reading {0}: {1}".format(url, exc.reason)) finally: if destfp is not None: destfp.close() def get_template( - self, - url, - dest, - template='jinja', - makedirs=False, - saltenv='base', - cachedir=None, - **kwargs): - ''' + self, + url, + dest, + template="jinja", + makedirs=False, + saltenv="base", + cachedir=None, + **kwargs + ): + """ Cache a file then process it as a template - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") - kwargs['saltenv'] = saltenv + kwargs["saltenv"] = saltenv url_data = urlparse(url) sfn = self.cache_file(url, saltenv, cachedir=cachedir) if not sfn or not os.path.exists(sfn): - return '' + return "" if template in salt.utils.templates.TEMPLATE_REGISTRY: - data = salt.utils.templates.TEMPLATE_REGISTRY[template]( - sfn, - **kwargs - ) + data = salt.utils.templates.TEMPLATE_REGISTRY[template](sfn, **kwargs) else: log.error( - 'Attempted to render template with unavailable engine %s', - template + "Attempted to render template with unavailable engine %s", template ) - return '' - if not data['result']: + return "" + if not data["result"]: # Failed to render the template - log.error('Failed to render template with error: %s', data['data']) - return '' + log.error("Failed to render template with error: %s", data["data"]) + return "" if not dest: # No destination passed, set the dest as an extrn_files cache dest = self._extrn_path(url, saltenv, cachedir=cachedir) @@ -765,15 +803,15 @@ class Client(object): if makedirs: os.makedirs(destdir) else: - salt.utils.files.safe_rm(data['data']) - return '' - shutil.move(data['data'], dest) + salt.utils.files.safe_rm(data["data"]) + return "" + shutil.move(data["data"], dest) return dest def _extrn_path(self, url, saltenv, cachedir=None): - ''' + """ Return the extrn_filepath for a given url - ''' + """ url_data = urlparse(url) if salt.utils.platform.is_windows(): netloc = salt.utils.path.sanitize_win_path(url_data.netloc) @@ -781,144 +819,137 @@ class Client(object): netloc = url_data.netloc # Strip user:pass from URLs - netloc = netloc.split('@')[-1] + netloc = netloc.split("@")[-1] if cachedir is None: - cachedir = self.opts['cachedir'] + cachedir = self.opts["cachedir"] elif not os.path.isabs(cachedir): - cachedir = os.path.join(self.opts['cachedir'], cachedir) + cachedir = os.path.join(self.opts["cachedir"], cachedir) if url_data.query: - file_name = '-'.join([url_data.path, url_data.query]) + file_name = "-".join([url_data.path, url_data.query]) else: file_name = url_data.path if len(file_name) > MAX_FILENAME_LENGTH: file_name = salt.utils.hashutils.sha256_digest(file_name) - return salt.utils.path.join( - cachedir, - 'extrn_files', - saltenv, - netloc, - file_name - ) + return salt.utils.path.join(cachedir, "extrn_files", saltenv, netloc, file_name) class PillarClient(Client): - ''' + """ Used by pillar to handle fileclient requests - ''' - def _find_file(self, path, saltenv='base'): - ''' + """ + + def _find_file(self, path, saltenv="base"): + """ Locate the file path - ''' - fnd = {'path': '', - 'rel': ''} + """ + fnd = {"path": "", "rel": ""} if salt.utils.url.is_escaped(path): # The path arguments are escaped path = salt.utils.url.unescape(path) - for root in self.opts['pillar_roots'].get(saltenv, []): + for root in self.opts["pillar_roots"].get(saltenv, []): full = os.path.join(root, path) if os.path.isfile(full): - fnd['path'] = full - fnd['rel'] = path + fnd["path"] = full + fnd["rel"] = path return fnd return fnd - def get_file(self, - path, - dest='', - makedirs=False, - saltenv='base', - gzip=None, - cachedir=None): - ''' + def get_file( + self, path, dest="", makedirs=False, saltenv="base", gzip=None, cachedir=None + ): + """ Copies a file from the local files directory into :param:`dest` gzip compression settings are ignored for local files - ''' + """ path = self._check_proto(path) fnd = self._find_file(path, saltenv) - fnd_path = fnd.get('path') + fnd_path = fnd.get("path") if not fnd_path: - return '' + return "" return fnd_path - def file_list(self, saltenv='base', prefix=''): - ''' + def file_list(self, saltenv="base", prefix=""): + """ Return a list of files in the given environment with optional relative prefix path to limit directory traversal - ''' + """ ret = [] - prefix = prefix.strip('/') - for path in self.opts['pillar_roots'].get(saltenv, []): + prefix = prefix.strip("/") + for path in self.opts["pillar_roots"].get(saltenv, []): for root, dirs, files in salt.utils.path.os_walk( os.path.join(path, prefix), followlinks=True ): # Don't walk any directories that match file_ignore_regex or glob - dirs[:] = [d for d in dirs if not salt.fileserver.is_file_ignored(self.opts, d)] + dirs[:] = [ + d for d in dirs if not salt.fileserver.is_file_ignored(self.opts, d) + ] for fname in files: relpath = os.path.relpath(os.path.join(root, fname), path) ret.append(salt.utils.data.decode(relpath)) return ret - def file_list_emptydirs(self, saltenv='base', prefix=''): - ''' + def file_list_emptydirs(self, saltenv="base", prefix=""): + """ List the empty dirs in the pillar_roots with optional relative prefix path to limit directory traversal - ''' + """ ret = [] - prefix = prefix.strip('/') - for path in self.opts['pillar_roots'].get(saltenv, []): + prefix = prefix.strip("/") + for path in self.opts["pillar_roots"].get(saltenv, []): for root, dirs, files in salt.utils.path.os_walk( os.path.join(path, prefix), followlinks=True ): # Don't walk any directories that match file_ignore_regex or glob - dirs[:] = [d for d in dirs if not salt.fileserver.is_file_ignored(self.opts, d)] + dirs[:] = [ + d for d in dirs if not salt.fileserver.is_file_ignored(self.opts, d) + ] if not dirs and not files: ret.append(salt.utils.data.decode(os.path.relpath(root, path))) return ret - def dir_list(self, saltenv='base', prefix=''): - ''' + def dir_list(self, saltenv="base", prefix=""): + """ List the dirs in the pillar_roots with optional relative prefix path to limit directory traversal - ''' + """ ret = [] - prefix = prefix.strip('/') - for path in self.opts['pillar_roots'].get(saltenv, []): + prefix = prefix.strip("/") + for path in self.opts["pillar_roots"].get(saltenv, []): for root, dirs, files in salt.utils.path.os_walk( os.path.join(path, prefix), followlinks=True ): ret.append(salt.utils.data.decode(os.path.relpath(root, path))) return ret - def __get_file_path(self, path, saltenv='base'): - ''' + def __get_file_path(self, path, saltenv="base"): + """ Return either a file path or the result of a remote find_file call. - ''' + """ try: path = self._check_proto(path) except MinionError as err: # Local file path if not os.path.isfile(path): log.warning( - 'specified file %s is not present to generate hash: %s', - path, err + "specified file %s is not present to generate hash: %s", path, err ) return None else: return path return self._find_file(path, saltenv) - def hash_file(self, path, saltenv='base'): - ''' + def hash_file(self, path, saltenv="base"): + """ Return the hash of a file, to get the hash of a file in the pillar_roots prepend the path with salt:// otherwise, prepend the file with / for a local file. - ''' + """ ret = {} fnd = self.__get_file_path(path, saltenv) if fnd is None: @@ -926,25 +957,25 @@ class PillarClient(Client): try: # Remote file path (self._find_file() invoked) - fnd_path = fnd['path'] + fnd_path = fnd["path"] except TypeError: # Local file path fnd_path = fnd - hash_type = self.opts.get('hash_type', 'md5') - ret['hsum'] = salt.utils.hashutils.get_hash(fnd_path, form=hash_type) - ret['hash_type'] = hash_type + hash_type = self.opts.get("hash_type", "md5") + ret["hsum"] = salt.utils.hashutils.get_hash(fnd_path, form=hash_type) + ret["hash_type"] = hash_type return ret - def hash_and_stat_file(self, path, saltenv='base'): - ''' + def hash_and_stat_file(self, path, saltenv="base"): + """ Return the hash of a file, to get the hash of a file in the pillar_roots prepend the path with salt:// otherwise, prepend the file with / for a local file. Additionally, return the stat result of the file, or None if no stat results were found. - ''' + """ ret = {} fnd = self.__get_file_path(path, saltenv) if fnd is None: @@ -952,8 +983,8 @@ class PillarClient(Client): try: # Remote file path (self._find_file() invoked) - fnd_path = fnd['path'] - fnd_stat = fnd.get('stat') + fnd_path = fnd["path"] + fnd_stat = fnd.get("stat") except TypeError: # Local file path fnd_path = fnd @@ -962,34 +993,34 @@ class PillarClient(Client): except Exception: # pylint: disable=broad-except fnd_stat = None - hash_type = self.opts.get('hash_type', 'md5') - ret['hsum'] = salt.utils.hashutils.get_hash(fnd_path, form=hash_type) - ret['hash_type'] = hash_type + hash_type = self.opts.get("hash_type", "md5") + ret["hsum"] = salt.utils.hashutils.get_hash(fnd_path, form=hash_type) + ret["hash_type"] = hash_type return ret, fnd_stat - def list_env(self, saltenv='base'): - ''' + def list_env(self, saltenv="base"): + """ Return a list of the files in the file server's specified environment - ''' + """ return self.file_list(saltenv) def master_opts(self): - ''' + """ Return the master opts data - ''' + """ return self.opts def envs(self): - ''' + """ Return the available environments - ''' + """ ret = [] - for saltenv in self.opts['pillar_roots']: + for saltenv in self.opts["pillar_roots"]: ret.append(saltenv) return ret def master_tops(self): - ''' + """ Originally returned information via the external_nodes subsystem. External_nodes was deprecated and removed in 2014.1.6 in favor of master_tops (which had been around since pre-0.17). @@ -1000,27 +1031,28 @@ class PillarClient(Client): if 'external_nodes' not in opts: return {} So since external_nodes is gone now, we are just returning the empty dict. - ''' + """ return {} class RemoteClient(Client): - ''' + """ Interact with the salt master file server. - ''' + """ + def __init__(self, opts): Client.__init__(self, opts) self._closing = False self.channel = salt.transport.client.ReqChannel.factory(self.opts) - if hasattr(self.channel, 'auth'): + if hasattr(self.channel, "auth"): self.auth = self.channel.auth else: - self.auth = '' + self.auth = "" def _refresh_channel(self): - ''' + """ Reset the channel, in the event of an interruption - ''' + """ # Close the previous channel self.channel.close() # Instantiate a new one @@ -1030,6 +1062,7 @@ class RemoteClient(Client): # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 def destroy(self): @@ -1045,19 +1078,15 @@ class RemoteClient(Client): if channel is not None: channel.close() - def get_file(self, - path, - dest='', - makedirs=False, - saltenv='base', - gzip=None, - cachedir=None): - ''' + def get_file( + self, path, dest="", makedirs=False, saltenv="base", gzip=None, cachedir=None + ): + """ Get a single file from the salt-master path must be a salt server location, aka, salt://path/to/file, if dest is omitted, then the downloaded file will be placed in the minion cache - ''' + """ path, senv = salt.utils.url.split_env(path) if senv: saltenv = senv @@ -1074,20 +1103,18 @@ class RemoteClient(Client): # Check if file exists on server, before creating files and # directories - if hash_server == '': - log.debug( - 'Could not find file \'%s\' in saltenv \'%s\'', - path, saltenv - ) + if hash_server == "": + log.debug("Could not find file '%s' in saltenv '%s'", path, saltenv) return False # If dest is a directory, rewrite dest with filename - if dest is not None \ - and (os.path.isdir(dest) or dest.endswith(('/', '\\'))): + if dest is not None and (os.path.isdir(dest) or dest.endswith(("/", "\\"))): dest = os.path.join(dest, os.path.basename(path)) log.debug( - 'In saltenv \'%s\', \'%s\' is a directory. Changing dest to ' - '\'%s\'', saltenv, os.path.dirname(dest), dest + "In saltenv '%s', '%s' is a directory. Changing dest to " "'%s'", + saltenv, + os.path.dirname(dest), + dest, ) # Hash compare local copy with master and skip download @@ -1097,22 +1124,24 @@ class RemoteClient(Client): rel_path = self._check_proto(path) log.debug( - 'In saltenv \'%s\', looking at rel_path \'%s\' to resolve ' - '\'%s\'', saltenv, rel_path, path + "In saltenv '%s', looking at rel_path '%s' to resolve " "'%s'", + saltenv, + rel_path, + path, ) - with self._cache_loc( - rel_path, saltenv, cachedir=cachedir) as cache_dest: + with self._cache_loc(rel_path, saltenv, cachedir=cachedir) as cache_dest: dest2check = cache_dest log.debug( - 'In saltenv \'%s\', ** considering ** path \'%s\' to resolve ' - '\'%s\'', saltenv, dest2check, path + "In saltenv '%s', ** considering ** path '%s' to resolve " "'%s'", + saltenv, + dest2check, + path, ) if dest2check and os.path.isfile(dest2check): if not salt.utils.platform.is_windows(): - hash_local, stat_local = \ - self.hash_and_stat_file(dest2check, saltenv) + hash_local, stat_local = self.hash_and_stat_file(dest2check, saltenv) try: mode_local = stat_local[0] except (IndexError, TypeError): @@ -1125,18 +1154,15 @@ class RemoteClient(Client): return dest2check log.debug( - 'Fetching file from saltenv \'%s\', ** attempting ** \'%s\'', - saltenv, path + "Fetching file from saltenv '%s', ** attempting ** '%s'", saltenv, path ) d_tries = 0 transport_tries = 0 path = self._check_proto(path) - load = {'path': path, - 'saltenv': saltenv, - 'cmd': '_serve_file'} + load = {"path": path, "saltenv": saltenv, "cmd": "_serve_file"} if gzip: gzip = int(gzip) - load['gzip'] = gzip + load["gzip"] = gzip fn_ = None if dest: @@ -1152,15 +1178,17 @@ class RemoteClient(Client): return False # We need an open filehandle here, that's why we're not using a # with clause: - fn_ = salt.utils.files.fopen(dest, 'wb+') # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + fn_ = salt.utils.files.fopen(dest, "wb+") + # pylint: enable=resource-leakage else: - log.debug('No dest file found') + log.debug("No dest file found") while True: if not fn_: - load['loc'] = 0 + load["loc"] = 0 else: - load['loc'] = fn_.tell() + load["loc"] = fn_.tell() data = self.channel.send(load, raw=True) if six.PY3: # Sometimes the source is local (eg when using @@ -1170,43 +1198,47 @@ class RemoteClient(Client): # strings for the top-level keys to simplify things. data = decode_dict_keys_to_str(data) try: - if not data['data']: - if not fn_ and data['dest']: + if not data["data"]: + if not fn_ and data["dest"]: # This is a 0 byte file on the master with self._cache_loc( - data['dest'], - saltenv, - cachedir=cachedir) as cache_dest: + data["dest"], saltenv, cachedir=cachedir + ) as cache_dest: dest = cache_dest - with salt.utils.files.fopen(cache_dest, 'wb+') as ofile: - ofile.write(data['data']) - if 'hsum' in data and d_tries < 3: + with salt.utils.files.fopen(cache_dest, "wb+") as ofile: + ofile.write(data["data"]) + if "hsum" in data and d_tries < 3: # Master has prompted a file verification, if the # verification fails, re-download the file. Try 3 times d_tries += 1 - hsum = salt.utils.hashutils.get_hash(dest, salt.utils.stringutils.to_str(data.get('hash_type', b'md5'))) - if hsum != data['hsum']: + hsum = salt.utils.hashutils.get_hash( + dest, + salt.utils.stringutils.to_str( + data.get("hash_type", b"md5") + ), + ) + if hsum != data["hsum"]: log.warning( - 'Bad download of file %s, attempt %d of 3', - path, d_tries + "Bad download of file %s, attempt %d of 3", + path, + d_tries, ) continue break if not fn_: with self._cache_loc( - data['dest'], - saltenv, - cachedir=cachedir) as cache_dest: + data["dest"], saltenv, cachedir=cachedir + ) as cache_dest: dest = cache_dest # If a directory was formerly cached at this path, then # remove it to avoid a traceback trying to write the file if os.path.isdir(dest): salt.utils.files.rm_rf(dest) - fn_ = salt.utils.atomicfile.atomic_open(dest, 'wb+') - if data.get('gzip', None): - data = salt.utils.gzip_util.uncompress(data['data']) + fn_ = salt.utils.atomicfile.atomic_open(dest, "wb+") + if data.get("gzip", None): + data = salt.utils.gzip_util.uncompress(data["data"]) else: - data = data['data'] + data = data["data"] if six.PY3 and isinstance(data, str): data = data.encode() fn_.write(data) @@ -1218,110 +1250,112 @@ class RemoteClient(Client): data_type = six.text_type(type(data)) transport_tries += 1 log.warning( - 'Data transport is broken, got: %s, type: %s, ' - 'exception: %s, attempt %d of 3', - data, data_type, exc, transport_tries + "Data transport is broken, got: %s, type: %s, " + "exception: %s, attempt %d of 3", + data, + data_type, + exc, + transport_tries, ) self._refresh_channel() if transport_tries > 3: log.error( - 'Data transport is broken, got: %s, type: %s, ' - 'exception: %s, retry attempts exhausted', - data, data_type, exc + "Data transport is broken, got: %s, type: %s, " + "exception: %s, retry attempts exhausted", + data, + data_type, + exc, ) break if fn_: fn_.close() - log.info( - 'Fetching file from saltenv \'%s\', ** done ** \'%s\'', - saltenv, path - ) + log.info("Fetching file from saltenv '%s', ** done ** '%s'", saltenv, path) else: log.debug( - 'In saltenv \'%s\', we are ** missing ** the file \'%s\'', - saltenv, path + "In saltenv '%s', we are ** missing ** the file '%s'", saltenv, path ) return dest - def file_list(self, saltenv='base', prefix=''): - ''' + def file_list(self, saltenv="base", prefix=""): + """ List the files on the master - ''' - load = {'saltenv': saltenv, - 'prefix': prefix, - 'cmd': '_file_list'} - return salt.utils.data.decode(self.channel.send(load)) if six.PY2 \ + """ + load = {"saltenv": saltenv, "prefix": prefix, "cmd": "_file_list"} + return ( + salt.utils.data.decode(self.channel.send(load)) + if six.PY2 else self.channel.send(load) + ) - def file_list_emptydirs(self, saltenv='base', prefix=''): - ''' + def file_list_emptydirs(self, saltenv="base", prefix=""): + """ List the empty dirs on the master - ''' - load = {'saltenv': saltenv, - 'prefix': prefix, - 'cmd': '_file_list_emptydirs'} - return salt.utils.data.decode(self.channel.send(load)) if six.PY2 \ + """ + load = {"saltenv": saltenv, "prefix": prefix, "cmd": "_file_list_emptydirs"} + return ( + salt.utils.data.decode(self.channel.send(load)) + if six.PY2 else self.channel.send(load) + ) - def dir_list(self, saltenv='base', prefix=''): - ''' + def dir_list(self, saltenv="base", prefix=""): + """ List the dirs on the master - ''' - load = {'saltenv': saltenv, - 'prefix': prefix, - 'cmd': '_dir_list'} - return salt.utils.data.decode(self.channel.send(load)) if six.PY2 \ + """ + load = {"saltenv": saltenv, "prefix": prefix, "cmd": "_dir_list"} + return ( + salt.utils.data.decode(self.channel.send(load)) + if six.PY2 else self.channel.send(load) + ) - def symlink_list(self, saltenv='base', prefix=''): - ''' + def symlink_list(self, saltenv="base", prefix=""): + """ List symlinked files and dirs on the master - ''' - load = {'saltenv': saltenv, - 'prefix': prefix, - 'cmd': '_symlink_list'} - return salt.utils.data.decode(self.channel.send(load)) if six.PY2 \ + """ + load = {"saltenv": saltenv, "prefix": prefix, "cmd": "_symlink_list"} + return ( + salt.utils.data.decode(self.channel.send(load)) + if six.PY2 else self.channel.send(load) + ) - def __hash_and_stat_file(self, path, saltenv='base'): - ''' + def __hash_and_stat_file(self, path, saltenv="base"): + """ Common code for hashing and stating files - ''' + """ try: path = self._check_proto(path) except MinionError as err: if not os.path.isfile(path): log.warning( - 'specified file %s is not present to generate hash: %s', - path, err + "specified file %s is not present to generate hash: %s", path, err ) return {}, None else: ret = {} - hash_type = self.opts.get('hash_type', 'md5') - ret['hsum'] = salt.utils.hashutils.get_hash(path, form=hash_type) - ret['hash_type'] = hash_type + hash_type = self.opts.get("hash_type", "md5") + ret["hsum"] = salt.utils.hashutils.get_hash(path, form=hash_type) + ret["hash_type"] = hash_type return ret - load = {'path': path, - 'saltenv': saltenv, - 'cmd': '_file_hash'} + load = {"path": path, "saltenv": saltenv, "cmd": "_file_hash"} return self.channel.send(load) - def hash_file(self, path, saltenv='base'): - ''' + def hash_file(self, path, saltenv="base"): + """ Return the hash of a file, to get the hash of a file on the salt master file server prepend the path with salt:// otherwise, prepend the file with / for a local file. - ''' + """ return self.__hash_and_stat_file(path, saltenv) - def hash_and_stat_file(self, path, saltenv='base'): - ''' + def hash_and_stat_file(self, path, saltenv="base"): + """ The same as hash_file, but also return the file's mode, or None if no mode data is present. - ''' + """ hash_result = self.hash_file(path, saltenv) try: path = self._check_proto(path) @@ -1333,67 +1367,75 @@ class RemoteClient(Client): return hash_result, list(os.stat(path)) except Exception: # pylint: disable=broad-except return hash_result, None - load = {'path': path, - 'saltenv': saltenv, - 'cmd': '_file_find'} + load = {"path": path, "saltenv": saltenv, "cmd": "_file_find"} fnd = self.channel.send(load) try: - stat_result = fnd.get('stat') + stat_result = fnd.get("stat") except AttributeError: stat_result = None return hash_result, stat_result - def list_env(self, saltenv='base'): - ''' + def list_env(self, saltenv="base"): + """ Return a list of the files in the file server's specified environment - ''' - load = {'saltenv': saltenv, - 'cmd': '_file_list'} - return salt.utils.data.decode(self.channel.send(load)) if six.PY2 \ + """ + load = {"saltenv": saltenv, "cmd": "_file_list"} + return ( + salt.utils.data.decode(self.channel.send(load)) + if six.PY2 else self.channel.send(load) + ) def envs(self): - ''' + """ Return a list of available environments - ''' - load = {'cmd': '_file_envs'} - return salt.utils.data.decode(self.channel.send(load)) if six.PY2 \ + """ + load = {"cmd": "_file_envs"} + return ( + salt.utils.data.decode(self.channel.send(load)) + if six.PY2 else self.channel.send(load) + ) def master_opts(self): - ''' + """ Return the master opts data - ''' - load = {'cmd': '_master_opts'} - return salt.utils.data.decode(self.channel.send(load)) if six.PY2 \ + """ + load = {"cmd": "_master_opts"} + return ( + salt.utils.data.decode(self.channel.send(load)) + if six.PY2 else self.channel.send(load) + ) def master_tops(self): - ''' + """ Return the metadata derived from the master_tops system - ''' + """ log.debug( - 'The _ext_nodes master function has been renamed to _master_tops. ' - 'To ensure compatibility when using older Salt masters we will ' - 'continue to invoke the function as _ext_nodes until the ' - 'Magnesium release.' + "The _ext_nodes master function has been renamed to _master_tops. " + "To ensure compatibility when using older Salt masters we will " + "continue to invoke the function as _ext_nodes until the " + "Magnesium release." ) # TODO: Change back to _master_tops # for Magnesium release - load = {'cmd': '_ext_nodes', - 'id': self.opts['id'], - 'opts': self.opts} + load = {"cmd": "_ext_nodes", "id": self.opts["id"], "opts": self.opts} if self.auth: - load['tok'] = self.auth.gen_token(b'salt') - return salt.utils.data.decode(self.channel.send(load)) if six.PY2 \ + load["tok"] = self.auth.gen_token(b"salt") + return ( + salt.utils.data.decode(self.channel.send(load)) + if six.PY2 else self.channel.send(load) + ) class FSClient(RemoteClient): - ''' + """ A local client that uses the RemoteClient but substitutes the channel for the FSChan object - ''' + """ + def __init__(self, opts): # pylint: disable=W0231 Client.__init__(self, opts) # pylint: disable=W0233 self._closing = False @@ -1407,9 +1449,10 @@ LocalClient = FSClient class DumbAuth(object): - ''' + """ The dumbauth class is used to stub out auth calls fired from the FSClient subsystem - ''' + """ + def gen_token(self, clear_tok): return clear_tok diff --git a/salt/fileserver/__init__.py b/salt/fileserver/__init__.py index 919987e2fcc..35b6c6e17b9 100644 --- a/salt/fileserver/__init__.py +++ b/salt/fileserver/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" File server pluggable modules and generic backend functions -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -21,25 +21,28 @@ import salt.utils.files import salt.utils.path import salt.utils.url import salt.utils.versions + +# Import 3rd-party libs +from salt.ext import six from salt.utils.args import get_function_argspec as _argspec from salt.utils.decorators import ensure_unicode_args try: from collections.abc import Sequence except ImportError: + # pylint: disable=no-name-in-module from collections import Sequence -# Import 3rd-party libs -from salt.ext import six + # pylint: enable=no-name-in-module log = logging.getLogger(__name__) def _unlock_cache(w_lock): - ''' + """ Unlock a FS file/dir based lock - ''' + """ if not os.path.exists(w_lock): return try: @@ -48,7 +51,7 @@ def _unlock_cache(w_lock): elif os.path.isfile(w_lock): os.unlink(w_lock) except (OSError, IOError) as exc: - log.trace('Error removing lockfile %s: %s', w_lock, exc) + log.trace("Error removing lockfile %s: %s", w_lock, exc) def _lock_cache(w_lock): @@ -59,16 +62,16 @@ def _lock_cache(w_lock): raise return False else: - log.trace('Lockfile %s created', w_lock) + log.trace("Lockfile %s created", w_lock) return True def wait_lock(lk_fn, dest, wait_timeout=0): - ''' + """ If the write lock is there, check to see if the file is actually being written. If there is no change in the file size after a short sleep, remove the lock and move forward. - ''' + """ if not os.path.exists(lk_fn): return False if not os.path.exists(dest): @@ -102,7 +105,7 @@ def wait_lock(lk_fn, dest, wait_timeout=0): if timeout: if time.time() > timeout: raise ValueError( - 'Timeout({0}s) for {1} (lock: {2}) elapsed'.format( + "Timeout({0}s) for {1} (lock: {2}) elapsed".format( wait_timeout, dest, lk_fn ) ) @@ -110,11 +113,11 @@ def wait_lock(lk_fn, dest, wait_timeout=0): def check_file_list_cache(opts, form, list_cache, w_lock): - ''' + """ Checks the cache file to see if there is a new enough file list cache, and returns the match (if found, along with booleans used by the fileserver backend to determine if the cache needs to be refreshed/written). - ''' + """ refresh_cache = False save_cache = True serial = salt.payload.Serial(opts) @@ -138,27 +141,35 @@ def check_file_list_cache(opts, form, list_cache, w_lock): file_mtime = int(cache_stat.st_mtime) if file_mtime > current_time: log.debug( - 'Cache file modified time is in the future, ignoring. ' - 'file=%s mtime=%s current_time=%s', - list_cache, current_time, file_mtime + "Cache file modified time is in the future, ignoring. " + "file=%s mtime=%s current_time=%s", + list_cache, + current_time, + file_mtime, ) age = 0 else: age = current_time - file_mtime else: # if filelist does not exists yet, mark it as expired - age = opts.get('fileserver_list_cache_time', 20) + 1 + age = opts.get("fileserver_list_cache_time", 20) + 1 if age < 0: # Cache is from the future! Warn and mark cache invalid. - log.warning('The file list_cache was created in the future!') - if 0 <= age < opts.get('fileserver_list_cache_time', 20): + log.warning("The file list_cache was created in the future!") + if 0 <= age < opts.get("fileserver_list_cache_time", 20): # Young enough! Load this sucker up! - with salt.utils.files.fopen(list_cache, 'rb') as fp_: + with salt.utils.files.fopen(list_cache, "rb") as fp_: log.debug( "Returning file list from cache: age=%s cache_time=%s %s", - age, opts.get('fileserver_list_cache_time', 20), list_cache + age, + opts.get("fileserver_list_cache_time", 20), + list_cache, + ) + return ( + salt.utils.data.decode(serial.load(fp_).get(form, [])), + False, + False, ) - return salt.utils.data.decode(serial.load(fp_).get(form, [])), False, False elif _lock_cache(w_lock): # Set the w_lock and go refresh_cache = True @@ -174,27 +185,27 @@ def check_file_list_cache(opts, form, list_cache, w_lock): def write_file_list_cache(opts, data, list_cache, w_lock): - ''' + """ Checks the cache file to see if there is a new enough file list cache, and returns the match (if found, along with booleans used by the fileserver backend to determine if the cache needs to be refreshed/written). - ''' + """ serial = salt.payload.Serial(opts) - with salt.utils.files.fopen(list_cache, 'w+b') as fp_: + with salt.utils.files.fopen(list_cache, "w+b") as fp_: fp_.write(serial.dumps(data)) _unlock_cache(w_lock) - log.trace('Lockfile %s removed', w_lock) + log.trace("Lockfile %s removed", w_lock) def check_env_cache(opts, env_cache): - ''' + """ Returns cached env names, if present. Otherwise returns None. - ''' + """ if not os.path.isfile(env_cache): return None try: - with salt.utils.files.fopen(env_cache, 'rb') as fp_: - log.trace('Returning env cache data from %s', env_cache) + with salt.utils.files.fopen(env_cache, "rb") as fp_: + log.trace("Returning env cache data from %s", env_cache) serial = salt.payload.Serial(opts) return salt.utils.data.decode(serial.load(fp_)) except (IOError, OSError): @@ -203,9 +214,9 @@ def check_env_cache(opts, env_cache): def generate_mtime_map(opts, path_map): - ''' + """ Generate a dict of filename -> mtime - ''' + """ file_map = {} for saltenv, path_list in six.iteritems(path_map): for path in path_list: @@ -221,17 +232,16 @@ def generate_mtime_map(opts, path_map): except (OSError, IOError): # skip dangling symlinks log.info( - 'Failed to get mtime on %s, dangling symlink?', - file_path + "Failed to get mtime on %s, dangling symlink?", file_path ) continue return file_map def diff_mtime_map(map1, map2): - ''' + """ Is there a change to the mtime map? return a boolean - ''' + """ # check if the mtimes are the same if sorted(map1) != sorted(map2): return True @@ -247,12 +257,12 @@ def diff_mtime_map(map1, map2): def reap_fileserver_cache_dir(cache_base, find_func): - ''' + """ Remove unused cache items assuming the cache directory follows a directory convention: cache_base -> saltenv -> relpath - ''' + """ for saltenv in os.listdir(cache_base): env_base = os.path.join(cache_base, saltenv) for root, dirs, files in salt.utils.path.os_walk(env_base): @@ -269,49 +279,44 @@ def reap_fileserver_cache_dir(cache_base, find_func): file_path = os.path.join(root, file_) file_rel_path = os.path.relpath(file_path, env_base) try: - filename, _, hash_type = file_rel_path.rsplit('.', 2) + filename, _, hash_type = file_rel_path.rsplit(".", 2) except ValueError: log.warning( - 'Found invalid hash file [%s] when attempting to reap ' - 'cache directory', file_ + "Found invalid hash file [%s] when attempting to reap " + "cache directory", + file_, ) continue # do we have the file? ret = find_func(filename, saltenv=saltenv) # if we don't actually have the file, lets clean up the cache # object - if ret['path'] == '': + if ret["path"] == "": os.unlink(file_path) def is_file_ignored(opts, fname): - ''' + """ If file_ignore_regex or file_ignore_glob were given in config, compare the given file path against all of them and return True on the first match. - ''' - if opts['file_ignore_regex']: - for regex in opts['file_ignore_regex']: + """ + if opts["file_ignore_regex"]: + for regex in opts["file_ignore_regex"]: if re.search(regex, fname): - log.debug( - 'File matching file_ignore_regex. Skipping: %s', - fname - ) + log.debug("File matching file_ignore_regex. Skipping: %s", fname) return True - if opts['file_ignore_glob']: - for glob in opts['file_ignore_glob']: + if opts["file_ignore_glob"]: + for glob in opts["file_ignore_glob"]: if fnmatch.fnmatch(fname, glob): - log.debug( - 'File matching file_ignore_glob. Skipping: %s', - fname - ) + log.debug("File matching file_ignore_glob. Skipping: %s", fname) return True return False -def clear_lock(clear_func, role, remote=None, lock_type='update'): - ''' +def clear_lock(clear_func, role, remote=None, lock_type="update"): + """ Function to allow non-fileserver functions to clear update locks clear_func @@ -332,36 +337,37 @@ def clear_lock(clear_func, role, remote=None, lock_type='update'): Which type of lock to clear Returns the return data from ``clear_func``. - ''' - msg = 'Clearing {0} lock for {1} remotes'.format(lock_type, role) + """ + msg = "Clearing {0} lock for {1} remotes".format(lock_type, role) if remote: - msg += ' matching {0}'.format(remote) + msg += " matching {0}".format(remote) log.debug(msg) return clear_func(remote=remote, lock_type=lock_type) class Fileserver(object): - ''' + """ Create a fileserver wrapper object that wraps the fileserver functions and iterates over them to execute the desired function within the scope of the desired fileserver backend. - ''' + """ + def __init__(self, opts): self.opts = opts - self.servers = salt.loader.fileserver(opts, opts['fileserver_backend']) + self.servers = salt.loader.fileserver(opts, opts["fileserver_backend"]) def backends(self, back=None): - ''' + """ Return the backend list - ''' + """ if not back: - back = self.opts['fileserver_backend'] + back = self.opts["fileserver_backend"] else: if not isinstance(back, list): try: - back = back.split(',') + back = back.split(",") except AttributeError: - back = six.text_type(back).split(',') + back = six.text_type(back).split(",") if isinstance(back, Sequence): # The test suite uses an ImmutableList type (based on @@ -379,86 +385,84 @@ class Fileserver(object): # .keys() attribute rather than on the LazyDict itself. server_funcs = self.servers.keys() try: - subtract_only = all((x.startswith('-') for x in back)) + subtract_only = all((x.startswith("-") for x in back)) except AttributeError: pass else: if subtract_only: # Only subtracting backends from enabled ones - ret = self.opts['fileserver_backend'] + ret = self.opts["fileserver_backend"] for sub in back: - if '{0}.envs'.format(sub[1:]) in server_funcs: + if "{0}.envs".format(sub[1:]) in server_funcs: ret.remove(sub[1:]) - elif '{0}.envs'.format(sub[1:-2]) in server_funcs: + elif "{0}.envs".format(sub[1:-2]) in server_funcs: ret.remove(sub[1:-2]) return ret for sub in back: - if '{0}.envs'.format(sub) in server_funcs: + if "{0}.envs".format(sub) in server_funcs: ret.append(sub) - elif '{0}.envs'.format(sub[:-2]) in server_funcs: + elif "{0}.envs".format(sub[:-2]) in server_funcs: ret.append(sub[:-2]) return ret def master_opts(self, load): - ''' + """ Simplify master opts - ''' + """ return self.opts def update_opts(self): # This fix func monkey patching by pillar for name, func in self.servers.items(): try: - if '__opts__' in func.__globals__: - func.__globals__['__opts__'].update(self.opts) + if "__opts__" in func.__globals__: + func.__globals__["__opts__"].update(self.opts) except AttributeError: pass def clear_cache(self, back=None): - ''' + """ Clear the cache of all of the fileserver backends that support the clear_cache function or the named backend(s) only. - ''' + """ back = self.backends(back) cleared = [] errors = [] for fsb in back: - fstr = '{0}.clear_cache'.format(fsb) + fstr = "{0}.clear_cache".format(fsb) if fstr in self.servers: - log.debug('Clearing %s fileserver cache', fsb) + log.debug("Clearing %s fileserver cache", fsb) failed = self.servers[fstr]() if failed: errors.extend(failed) else: cleared.append( - 'The {0} fileserver cache was successfully cleared' - .format(fsb) + "The {0} fileserver cache was successfully cleared".format(fsb) ) return cleared, errors def lock(self, back=None, remote=None): - ''' + """ ``remote`` can either be a dictionary containing repo configuration information, or a pattern. If the latter, then remotes for which the URL matches the pattern will be locked. - ''' + """ back = self.backends(back) locked = [] errors = [] for fsb in back: - fstr = '{0}.lock'.format(fsb) + fstr = "{0}.lock".format(fsb) if fstr in self.servers: - msg = 'Setting update lock for {0} remotes'.format(fsb) + msg = "Setting update lock for {0} remotes".format(fsb) if remote: if not isinstance(remote, six.string_types): errors.append( - 'Badly formatted remote pattern \'{0}\'' - .format(remote) + "Badly formatted remote pattern '{0}'".format(remote) ) continue else: - msg += ' matching {0}'.format(remote) + msg += " matching {0}".format(remote) log.debug(msg) good, bad = self.servers[fstr](remote=remote) locked.extend(good) @@ -466,7 +470,7 @@ class Fileserver(object): return locked, errors def clear_lock(self, back=None, remote=None): - ''' + """ Clear the update lock for the enabled fileserver backends back @@ -476,59 +480,59 @@ class Fileserver(object): remote If specified, then any remotes which contain the passed string will have their lock cleared. - ''' + """ back = self.backends(back) cleared = [] errors = [] for fsb in back: - fstr = '{0}.clear_lock'.format(fsb) + fstr = "{0}.clear_lock".format(fsb) if fstr in self.servers: - good, bad = clear_lock(self.servers[fstr], - fsb, - remote=remote) + good, bad = clear_lock(self.servers[fstr], fsb, remote=remote) cleared.extend(good) errors.extend(bad) return cleared, errors def update(self, back=None): - ''' + """ Update all of the enabled fileserver backends which support the update function, or - ''' + """ back = self.backends(back) for fsb in back: - fstr = '{0}.update'.format(fsb) + fstr = "{0}.update".format(fsb) if fstr in self.servers: - log.debug('Updating %s fileserver cache', fsb) + log.debug("Updating %s fileserver cache", fsb) self.servers[fstr]() def update_intervals(self, back=None): - ''' + """ Return the update intervals for all of the enabled fileserver backends which support variable update intervals. - ''' + """ back = self.backends(back) ret = {} for fsb in back: - fstr = '{0}.update_intervals'.format(fsb) + fstr = "{0}.update_intervals".format(fsb) if fstr in self.servers: ret[fsb] = self.servers[fstr]() return ret def envs(self, back=None, sources=False): - ''' + """ Return the environments for the named backend or all backends - ''' + """ back = self.backends(back) ret = set() if sources: ret = {} for fsb in back: - fstr = '{0}.envs'.format(fsb) - kwargs = {'ignore_cache': True} \ - if 'ignore_cache' in _argspec(self.servers[fstr]).args \ - and self.opts['__role'] == 'minion' \ + fstr = "{0}.envs".format(fsb) + kwargs = ( + {"ignore_cache": True} + if "ignore_cache" in _argspec(self.servers[fstr]).args + and self.opts["__role"] == "minion" else {} + ) if sources: ret[fsb] = self.servers[fstr](**kwargs) else: @@ -538,370 +542,369 @@ class Fileserver(object): return list(ret) def file_envs(self, load=None): - ''' + """ Return environments for all backends for requests from fileclient - ''' + """ if load is None: load = {} - load.pop('cmd', None) + load.pop("cmd", None) return self.envs(**load) def init(self, back=None): - ''' + """ Initialize the backend, only do so if the fs supports an init function - ''' + """ back = self.backends(back) for fsb in back: - fstr = '{0}.init'.format(fsb) + fstr = "{0}.init".format(fsb) if fstr in self.servers: self.servers[fstr]() def _find_file(self, load): - ''' + """ Convenience function for calls made using the RemoteClient - ''' - path = load.get('path') + """ + path = load.get("path") if not path: - return {'path': '', - 'rel': ''} - tgt_env = load.get('saltenv', 'base') + return {"path": "", "rel": ""} + tgt_env = load.get("saltenv", "base") return self.find_file(path, tgt_env) def file_find(self, load): - ''' + """ Convenience function for calls made using the LocalClient - ''' - path = load.get('path') + """ + path = load.get("path") if not path: - return {'path': '', - 'rel': ''} - tgt_env = load.get('saltenv', 'base') + return {"path": "", "rel": ""} + tgt_env = load.get("saltenv", "base") return self.find_file(path, tgt_env) def find_file(self, path, saltenv, back=None): - ''' + """ Find the path and return the fnd structure, this structure is passed to other backend interfaces. - ''' + """ path = salt.utils.stringutils.to_unicode(path) saltenv = salt.utils.stringutils.to_unicode(saltenv) back = self.backends(back) kwargs = {} - fnd = {'path': '', - 'rel': ''} + fnd = {"path": "", "rel": ""} if os.path.isabs(path): return fnd - if '../' in path: + if "../" in path: return fnd if salt.utils.url.is_escaped(path): # don't attempt to find URL query arguments in the path path = salt.utils.url.unescape(path) else: - if '?' in path: - hcomps = path.split('?') + if "?" in path: + hcomps = path.split("?") path = hcomps[0] - comps = hcomps[1].split('&') + comps = hcomps[1].split("&") for comp in comps: - if '=' not in comp: + if "=" not in comp: # Invalid option, skip it continue - args = comp.split('=', 1) + args = comp.split("=", 1) kwargs[args[0]] = args[1] - if 'env' in kwargs: + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') - if 'saltenv' in kwargs: - saltenv = kwargs.pop('saltenv') + kwargs.pop("env") + if "saltenv" in kwargs: + saltenv = kwargs.pop("saltenv") if not isinstance(saltenv, six.string_types): saltenv = six.text_type(saltenv) for fsb in back: - fstr = '{0}.find_file'.format(fsb) + fstr = "{0}.find_file".format(fsb) if fstr in self.servers: fnd = self.servers[fstr](path, saltenv, **kwargs) - if fnd.get('path'): - fnd['back'] = fsb + if fnd.get("path"): + fnd["back"] = fsb return fnd return fnd def serve_file(self, load): - ''' + """ Serve up a chunk of a file - ''' - ret = {'data': '', - 'dest': ''} + """ + ret = {"data": "", "dest": ""} - if 'env' in load: + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if 'path' not in load or 'loc' not in load or 'saltenv' not in load: + if "path" not in load or "loc" not in load or "saltenv" not in load: return ret - if not isinstance(load['saltenv'], six.string_types): - load['saltenv'] = six.text_type(load['saltenv']) + if not isinstance(load["saltenv"], six.string_types): + load["saltenv"] = six.text_type(load["saltenv"]) - fnd = self.find_file(load['path'], load['saltenv']) - if not fnd.get('back'): + fnd = self.find_file(load["path"], load["saltenv"]) + if not fnd.get("back"): return ret - fstr = '{0}.serve_file'.format(fnd['back']) + fstr = "{0}.serve_file".format(fnd["back"]) if fstr in self.servers: return self.servers[fstr](load, fnd) return ret def __file_hash_and_stat(self, load): - ''' + """ Common code for hashing and stating files - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if 'path' not in load or 'saltenv' not in load: - return '', None - if not isinstance(load['saltenv'], six.string_types): - load['saltenv'] = six.text_type(load['saltenv']) + if "path" not in load or "saltenv" not in load: + return "", None + if not isinstance(load["saltenv"], six.string_types): + load["saltenv"] = six.text_type(load["saltenv"]) - fnd = self.find_file(salt.utils.stringutils.to_unicode(load['path']), - load['saltenv']) - if not fnd.get('back'): - return '', None - stat_result = fnd.get('stat', None) - fstr = '{0}.file_hash'.format(fnd['back']) + fnd = self.find_file( + salt.utils.stringutils.to_unicode(load["path"]), load["saltenv"] + ) + if not fnd.get("back"): + return "", None + stat_result = fnd.get("stat", None) + fstr = "{0}.file_hash".format(fnd["back"]) if fstr in self.servers: return self.servers[fstr](load, fnd), stat_result - return '', None + return "", None def file_hash(self, load): - ''' + """ Return the hash of a given file - ''' + """ try: return self.__file_hash_and_stat(load)[0] except (IndexError, TypeError): - return '' + return "" def file_hash_and_stat(self, load): - ''' + """ Return the hash and stat result of a given file - ''' + """ try: return self.__file_hash_and_stat(load) except (IndexError, TypeError): - return '', None + return "", None def clear_file_list_cache(self, load): - ''' + """ Deletes the file_lists cache files - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - saltenv = load.get('saltenv', []) + saltenv = load.get("saltenv", []) if saltenv is not None: if not isinstance(saltenv, list): try: - saltenv = [x.strip() for x in saltenv.split(',')] + saltenv = [x.strip() for x in saltenv.split(",")] except AttributeError: - saltenv = [x.strip() for x in six.text_type(saltenv).split(',')] + saltenv = [x.strip() for x in six.text_type(saltenv).split(",")] for idx, val in enumerate(saltenv): if not isinstance(val, six.string_types): saltenv[idx] = six.text_type(val) ret = {} - fsb = self.backends(load.pop('fsbackend', None)) - list_cachedir = os.path.join(self.opts['cachedir'], 'file_lists') + fsb = self.backends(load.pop("fsbackend", None)) + list_cachedir = os.path.join(self.opts["cachedir"], "file_lists") try: file_list_backends = os.listdir(list_cachedir) except OSError as exc: if exc.errno == errno.ENOENT: - log.debug('No file list caches found') + log.debug("No file list caches found") return {} else: log.error( - 'Failed to get list of saltenvs for which the master has ' - 'cached file lists: %s', exc + "Failed to get list of saltenvs for which the master has " + "cached file lists: %s", + exc, ) for back in file_list_backends: # Account for the fact that the file_list cache directory for gitfs # is 'git', hgfs is 'hg', etc. - back_virtualname = re.sub('fs$', '', back) + back_virtualname = re.sub("fs$", "", back) try: cache_files = os.listdir(os.path.join(list_cachedir, back)) except OSError as exc: log.error( - 'Failed to find file list caches for saltenv \'%s\': %s', - back, exc + "Failed to find file list caches for saltenv '%s': %s", back, exc ) continue for cache_file in cache_files: try: - cache_saltenv, extension = cache_file.rsplit('.', 1) + cache_saltenv, extension = cache_file.rsplit(".", 1) except ValueError: # Filename has no dot in it. Not a cache file, ignore. continue - if extension != 'p': + if extension != "p": # Filename does not end in ".p". Not a cache file, ignore. continue - elif back_virtualname not in fsb or \ - (saltenv is not None and cache_saltenv not in saltenv): + elif back_virtualname not in fsb or ( + saltenv is not None and cache_saltenv not in saltenv + ): log.debug( - 'Skipping %s file list cache for saltenv \'%s\'', - back, cache_saltenv + "Skipping %s file list cache for saltenv '%s'", + back, + cache_saltenv, ) continue try: os.remove(os.path.join(list_cachedir, back, cache_file)) except OSError as exc: if exc.errno != errno.ENOENT: - log.error('Failed to remove %s: %s', - exc.filename, exc.strerror) + log.error("Failed to remove %s: %s", exc.filename, exc.strerror) else: ret.setdefault(back, []).append(cache_saltenv) log.debug( - 'Removed %s file list cache for saltenv \'%s\'', - cache_saltenv, back + "Removed %s file list cache for saltenv '%s'", + cache_saltenv, + back, ) return ret @ensure_unicode_args def file_list(self, load): - ''' + """ Return a list of files from the dominant environment - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") ret = set() - if 'saltenv' not in load: + if "saltenv" not in load: return [] - if not isinstance(load['saltenv'], six.string_types): - load['saltenv'] = six.text_type(load['saltenv']) + if not isinstance(load["saltenv"], six.string_types): + load["saltenv"] = six.text_type(load["saltenv"]) - for fsb in self.backends(load.pop('fsbackend', None)): - fstr = '{0}.file_list'.format(fsb) + for fsb in self.backends(load.pop("fsbackend", None)): + fstr = "{0}.file_list".format(fsb) if fstr in self.servers: ret.update(self.servers[fstr](load)) # some *fs do not handle prefix. Ensure it is filtered - prefix = load.get('prefix', '').strip('/') - if prefix != '': + prefix = load.get("prefix", "").strip("/") + if prefix != "": ret = [f for f in ret if f.startswith(prefix)] return sorted(ret) @ensure_unicode_args def file_list_emptydirs(self, load): - ''' + """ List all emptydirs in the given environment - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") ret = set() - if 'saltenv' not in load: + if "saltenv" not in load: return [] - if not isinstance(load['saltenv'], six.string_types): - load['saltenv'] = six.text_type(load['saltenv']) + if not isinstance(load["saltenv"], six.string_types): + load["saltenv"] = six.text_type(load["saltenv"]) for fsb in self.backends(None): - fstr = '{0}.file_list_emptydirs'.format(fsb) + fstr = "{0}.file_list_emptydirs".format(fsb) if fstr in self.servers: ret.update(self.servers[fstr](load)) # some *fs do not handle prefix. Ensure it is filtered - prefix = load.get('prefix', '').strip('/') - if prefix != '': + prefix = load.get("prefix", "").strip("/") + if prefix != "": ret = [f for f in ret if f.startswith(prefix)] return sorted(ret) @ensure_unicode_args def dir_list(self, load): - ''' + """ List all directories in the given environment - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") ret = set() - if 'saltenv' not in load: + if "saltenv" not in load: return [] - if not isinstance(load['saltenv'], six.string_types): - load['saltenv'] = six.text_type(load['saltenv']) + if not isinstance(load["saltenv"], six.string_types): + load["saltenv"] = six.text_type(load["saltenv"]) - for fsb in self.backends(load.pop('fsbackend', None)): - fstr = '{0}.dir_list'.format(fsb) + for fsb in self.backends(load.pop("fsbackend", None)): + fstr = "{0}.dir_list".format(fsb) if fstr in self.servers: ret.update(self.servers[fstr](load)) # some *fs do not handle prefix. Ensure it is filtered - prefix = load.get('prefix', '').strip('/') - if prefix != '': + prefix = load.get("prefix", "").strip("/") + if prefix != "": ret = [f for f in ret if f.startswith(prefix)] return sorted(ret) @ensure_unicode_args def symlink_list(self, load): - ''' + """ Return a list of symlinked files and dirs - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") ret = {} - if 'saltenv' not in load: + if "saltenv" not in load: return {} - if not isinstance(load['saltenv'], six.string_types): - load['saltenv'] = six.text_type(load['saltenv']) + if not isinstance(load["saltenv"], six.string_types): + load["saltenv"] = six.text_type(load["saltenv"]) - for fsb in self.backends(load.pop('fsbackend', None)): - symlstr = '{0}.symlink_list'.format(fsb) + for fsb in self.backends(load.pop("fsbackend", None)): + symlstr = "{0}.symlink_list".format(fsb) if symlstr in self.servers: ret = self.servers[symlstr](load) # some *fs do not handle prefix. Ensure it is filtered - prefix = load.get('prefix', '').strip('/') - if prefix != '': - ret = dict([ - (x, y) for x, y in six.iteritems(ret) if x.startswith(prefix) - ]) + prefix = load.get("prefix", "").strip("/") + if prefix != "": + ret = dict([(x, y) for x, y in six.iteritems(ret) if x.startswith(prefix)]) return ret class FSChan(object): - ''' + """ A class that mimics the transport channels allowing for local access to to the fileserver class class structure - ''' + """ + def __init__(self, opts, **kwargs): self.opts = opts self.kwargs = kwargs self.fs = Fileserver(self.opts) self.fs.init() - if self.opts.get('file_client', 'remote') == 'local': - if '__fs_update' not in self.opts: + if self.opts.get("file_client", "remote") == "local": + if "__fs_update" not in self.opts: self.fs.update() - self.opts['__fs_update'] = True + self.opts["__fs_update"] = True else: self.fs.update() - self.cmd_stub = {'master_tops': {}, - 'ext_nodes': {}} + self.cmd_stub = {"master_tops": {}, "ext_nodes": {}} - def send(self, load, tries=None, timeout=None, raw=False): # pylint: disable=unused-argument - ''' + def send( + self, load, tries=None, timeout=None, raw=False + ): # pylint: disable=unused-argument + """ Emulate the channel send method, the tries and timeout are not used - ''' - if 'cmd' not in load: - log.error('Malformed request, no cmd: %s', load) + """ + if "cmd" not in load: + log.error("Malformed request, no cmd: %s", load) return {} - cmd = load['cmd'].lstrip('_') + cmd = load["cmd"].lstrip("_") if cmd in self.cmd_stub: return self.cmd_stub[cmd] if not hasattr(self.fs, cmd): - log.error('Malformed request, invalid cmd: %s', load) + log.error("Malformed request, invalid cmd: %s", load) return {} return getattr(self.fs, cmd)(load) diff --git a/salt/fileserver/azurefs.py b/salt/fileserver/azurefs.py index 2b5fd62a8a4..dfa87a31ef1 100644 --- a/salt/fileserver/azurefs.py +++ b/salt/fileserver/azurefs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The backend for serving files from the Azure blob storage service. .. versionadded:: 2015.8.0 @@ -44,10 +44,11 @@ permissions. .. note:: Do not include the leading ? for sas_token if generated from the web -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import logging import os @@ -61,36 +62,37 @@ import salt.utils.hashutils import salt.utils.json import salt.utils.path import salt.utils.stringutils + +# Import third party libs +from salt.ext import six from salt.utils.versions import LooseVersion try: import azure.storage - if LooseVersion(azure.storage.__version__) < LooseVersion('0.20.0'): - raise ImportError('azure.storage.__version__ must be >= 0.20.0') + + if LooseVersion(azure.storage.__version__) < LooseVersion("0.20.0"): + raise ImportError("azure.storage.__version__ must be >= 0.20.0") HAS_AZURE = True except (ImportError, AttributeError): HAS_AZURE = False -# Import third party libs -from salt.ext import six - -__virtualname__ = 'azurefs' +__virtualname__ = "azurefs" log = logging.getLogger() def __virtual__(): - ''' + """ Only load if defined in fileserver_backend and azure.storage is present - ''' - if __virtualname__ not in __opts__['fileserver_backend']: + """ + if __virtualname__ not in __opts__["fileserver_backend"]: return False if not HAS_AZURE: return False - if 'azurefs' not in __opts__: + if "azurefs" not in __opts__: return False if not _validate_config(): @@ -99,20 +101,18 @@ def __virtual__(): return True -def find_file(path, saltenv='base', **kwargs): - ''' +def find_file(path, saltenv="base", **kwargs): + """ Search the environment for the relative path - ''' - fnd = {'path': '', - 'rel': ''} - for container in __opts__.get('azurefs', []): - if container.get('saltenv', 'base') != saltenv: + """ + fnd = {"path": "", "rel": ""} + for container in __opts__.get("azurefs", []): + if container.get("saltenv", "base") != saltenv: continue full = os.path.join(_get_container_path(container), path) - if os.path.isfile(full) and not salt.fileserver.is_file_ignored( - __opts__, path): - fnd['path'] = full - fnd['rel'] = path + if os.path.isfile(full) and not salt.fileserver.is_file_ignored(__opts__, path): + fnd["path"] = full + fnd["rel"] = path try: # Converting the stat result to a list, the elements of the # list correspond to the following stat_result params: @@ -126,7 +126,7 @@ def find_file(path, saltenv='base', **kwargs): # 7 => st_atime=1468284229 # 8 => st_mtime=1456338235 # 9 => st_ctime=1456338235 - fnd['stat'] = list(os.stat(full)) + fnd["stat"] = list(os.stat(full)) except Exception: # pylint: disable=broad-except pass return fnd @@ -134,49 +134,48 @@ def find_file(path, saltenv='base', **kwargs): def envs(): - ''' + """ Each container configuration can have an environment setting, or defaults to base - ''' + """ saltenvs = [] - for container in __opts__.get('azurefs', []): - saltenvs.append(container.get('saltenv', 'base')) + for container in __opts__.get("azurefs", []): + saltenvs.append(container.get("saltenv", "base")) # Remove duplicates return list(set(saltenvs)) def serve_file(load, fnd): - ''' + """ Return a chunk from a file based on the data received - ''' - ret = {'data': '', - 'dest': ''} - required_load_keys = ('path', 'loc', 'saltenv') + """ + ret = {"data": "", "dest": ""} + required_load_keys = ("path", "loc", "saltenv") if not all(x in load for x in required_load_keys): log.debug( - 'Not all of the required keys present in payload. Missing: %s', - ', '.join(required_load_keys.difference(load)) + "Not all of the required keys present in payload. Missing: %s", + ", ".join(required_load_keys.difference(load)), ) return ret - if not fnd['path']: + if not fnd["path"]: return ret - ret['dest'] = fnd['rel'] - gzip = load.get('gzip', None) - fpath = os.path.normpath(fnd['path']) - with salt.utils.files.fopen(fpath, 'rb') as fp_: - fp_.seek(load['loc']) - data = fp_.read(__opts__['file_buffer_size']) + ret["dest"] = fnd["rel"] + gzip = load.get("gzip", None) + fpath = os.path.normpath(fnd["path"]) + with salt.utils.files.fopen(fpath, "rb") as fp_: + fp_.seek(load["loc"]) + data = fp_.read(__opts__["file_buffer_size"]) if data and six.PY3 and not salt.utils.files.is_binary(fpath): data = data.decode(__salt_system_encoding__) if gzip and data: data = salt.utils.gzip_util.compress(data, gzip) - ret['gzip'] = gzip - ret['data'] = data + ret["gzip"] = gzip + ret["data"] = data return ret def update(): - ''' + """ Update caches of the storage containers. Compares the md5 of the files on disk to the md5 of the blobs in the @@ -184,8 +183,8 @@ def update(): Also processes deletions by walking the container caches and comparing with the list of blobs in the container - ''' - for container in __opts__['azurefs']: + """ + for container in __opts__["azurefs"]: path = _get_container_path(container) try: if not os.path.exists(path): @@ -194,14 +193,14 @@ def update(): shutil.rmtree(path) os.makedirs(path) except Exception as exc: # pylint: disable=broad-except - log.exception('Error occurred creating cache directory for azurefs') + log.exception("Error occurred creating cache directory for azurefs") continue blob_service = _get_container_service(container) - name = container['container_name'] + name = container["container_name"] try: blob_list = blob_service.list_blobs(name) except Exception as exc: # pylint: disable=broad-except - log.exception('Error occurred fetching blob list for azurefs') + log.exception("Error occurred fetching blob list for azurefs") continue # Walk the cache directory searching for deletions @@ -212,7 +211,7 @@ def update(): fname = os.path.join(root, f) relpath = os.path.relpath(fname, path) if relpath not in blob_set: - salt.fileserver.wait_lock(fname + '.lk', fname) + salt.fileserver.wait_lock(fname + ".lk", fname) try: os.unlink(fname) except Exception: # pylint: disable=broad-except @@ -226,7 +225,9 @@ def update(): if os.path.exists(fname): # File exists, check the hashes source_md5 = blob.properties.content_settings.content_md5 - local_md5 = base64.b64encode(salt.utils.hashutils.get_hash(fname, 'md5').decode('hex')) + local_md5 = base64.b64encode( + salt.utils.hashutils.get_hash(fname, "md5").decode("hex") + ) if local_md5 != source_md5: update = True else: @@ -236,15 +237,15 @@ def update(): if not os.path.exists(os.path.dirname(fname)): os.makedirs(os.path.dirname(fname)) # Lock writes - lk_fn = fname + '.lk' + lk_fn = fname + ".lk" salt.fileserver.wait_lock(lk_fn, fname) - with salt.utils.files.fopen(lk_fn, 'w'): + with salt.utils.files.fopen(lk_fn, "w"): pass try: blob_service.get_blob_to_path(name, blob.name, fname) except Exception as exc: # pylint: disable=broad-except - log.exception('Error occurred fetching blob from azurefs') + log.exception("Error occurred fetching blob from azurefs") continue # Unlock writes @@ -254,79 +255,82 @@ def update(): pass # Write out file list - container_list = path + '.list' - lk_fn = container_list + '.lk' + container_list = path + ".list" + lk_fn = container_list + ".lk" salt.fileserver.wait_lock(lk_fn, container_list) - with salt.utils.files.fopen(lk_fn, 'w'): + with salt.utils.files.fopen(lk_fn, "w"): pass - with salt.utils.files.fopen(container_list, 'w') as fp_: + with salt.utils.files.fopen(container_list, "w") as fp_: salt.utils.json.dump(blob_names, fp_) try: os.unlink(lk_fn) except Exception: # pylint: disable=broad-except pass try: - hash_cachedir = os.path.join(__opts__['cachedir'], 'azurefs', 'hashes') + hash_cachedir = os.path.join(__opts__["cachedir"], "azurefs", "hashes") shutil.rmtree(hash_cachedir) except Exception: # pylint: disable=broad-except - log.exception('Problem occurred trying to invalidate hash cach for azurefs') + log.exception("Problem occurred trying to invalidate hash cach for azurefs") def file_hash(load, fnd): - ''' + """ Return a file hash based on the hash type set in the master config - ''' - if not all(x in load for x in ('path', 'saltenv')): - return '', None - ret = {'hash_type': __opts__['hash_type']} - relpath = fnd['rel'] - path = fnd['path'] - hash_cachedir = os.path.join(__opts__['cachedir'], 'azurefs', 'hashes') - hashdest = salt.utils.path.join(hash_cachedir, - load['saltenv'], - '{0}.hash.{1}'.format(relpath, - __opts__['hash_type'])) + """ + if not all(x in load for x in ("path", "saltenv")): + return "", None + ret = {"hash_type": __opts__["hash_type"]} + relpath = fnd["rel"] + path = fnd["path"] + hash_cachedir = os.path.join(__opts__["cachedir"], "azurefs", "hashes") + hashdest = salt.utils.path.join( + hash_cachedir, + load["saltenv"], + "{0}.hash.{1}".format(relpath, __opts__["hash_type"]), + ) if not os.path.isfile(hashdest): if not os.path.exists(os.path.dirname(hashdest)): os.makedirs(os.path.dirname(hashdest)) - ret['hsum'] = salt.utils.hashutils.get_hash(path, __opts__['hash_type']) - with salt.utils.files.fopen(hashdest, 'w+') as fp_: - fp_.write(salt.utils.stringutils.to_str(ret['hsum'])) + ret["hsum"] = salt.utils.hashutils.get_hash(path, __opts__["hash_type"]) + with salt.utils.files.fopen(hashdest, "w+") as fp_: + fp_.write(salt.utils.stringutils.to_str(ret["hsum"])) return ret else: - with salt.utils.files.fopen(hashdest, 'rb') as fp_: - ret['hsum'] = salt.utils.stringutils.to_unicode(fp_.read()) + with salt.utils.files.fopen(hashdest, "rb") as fp_: + ret["hsum"] = salt.utils.stringutils.to_unicode(fp_.read()) return ret def file_list(load): - ''' + """ Return a list of all files in a specified environment - ''' + """ ret = set() try: - for container in __opts__['azurefs']: - if container.get('saltenv', 'base') != load['saltenv']: + for container in __opts__["azurefs"]: + if container.get("saltenv", "base") != load["saltenv"]: continue - container_list = _get_container_path(container) + '.list' - lk = container_list + '.lk' + container_list = _get_container_path(container) + ".list" + lk = container_list + ".lk" salt.fileserver.wait_lock(lk, container_list, 5) if not os.path.exists(container_list): continue - with salt.utils.files.fopen(container_list, 'r') as fp_: + with salt.utils.files.fopen(container_list, "r") as fp_: ret.update(set(salt.utils.json.load(fp_))) except Exception as exc: # pylint: disable=broad-except - log.error('azurefs: an error ocurred retrieving file lists. ' - 'It should be resolved next time the fileserver ' - 'updates. Please do not manually modify the azurefs ' - 'cache directory.') + log.error( + "azurefs: an error ocurred retrieving file lists. " + "It should be resolved next time the fileserver " + "updates. Please do not manually modify the azurefs " + "cache directory." + ) return list(ret) def dir_list(load): - ''' + """ Return a list of all directories in a specified environment - ''' + """ ret = set() files = file_list(load) for f in files: @@ -339,53 +343,61 @@ def dir_list(load): def _get_container_path(container): - ''' + """ Get the cache path for the container in question Cache paths are generate by combining the account name, container name, and saltenv, separated by underscores - ''' - root = os.path.join(__opts__['cachedir'], 'azurefs') - container_dir = '{0}_{1}_{2}'.format(container.get('account_name', ''), - container.get('container_name', ''), - container.get('saltenv', 'base')) + """ + root = os.path.join(__opts__["cachedir"], "azurefs") + container_dir = "{0}_{1}_{2}".format( + container.get("account_name", ""), + container.get("container_name", ""), + container.get("saltenv", "base"), + ) return os.path.join(root, container_dir) def _get_container_service(container): - ''' + """ Get the azure block blob service for the container in question Try account_key, sas_token, and no auth in that order - ''' - if 'account_key' in container: - account = azure.storage.CloudStorageAccount(container['account_name'], account_key=container['account_key']) - elif 'sas_token' in container: - account = azure.storage.CloudStorageAccount(container['account_name'], sas_token=container['sas_token']) + """ + if "account_key" in container: + account = azure.storage.CloudStorageAccount( + container["account_name"], account_key=container["account_key"] + ) + elif "sas_token" in container: + account = azure.storage.CloudStorageAccount( + container["account_name"], sas_token=container["sas_token"] + ) else: - account = azure.storage.CloudStorageAccount(container['account_name']) + account = azure.storage.CloudStorageAccount(container["account_name"]) blob_service = account.create_block_blob_service() return blob_service def _validate_config(): - ''' + """ Validate azurefs config, return False if it doesn't validate - ''' - if not isinstance(__opts__['azurefs'], list): - log.error('azurefs configuration is not formed as a list, skipping azurefs') + """ + if not isinstance(__opts__["azurefs"], list): + log.error("azurefs configuration is not formed as a list, skipping azurefs") return False - for container in __opts__['azurefs']: + for container in __opts__["azurefs"]: if not isinstance(container, dict): log.error( - 'One or more entries in the azurefs configuration list are ' - 'not formed as a dict. Skipping azurefs: %s', container + "One or more entries in the azurefs configuration list are " + "not formed as a dict. Skipping azurefs: %s", + container, ) return False - if 'account_name' not in container or 'container_name' not in container: + if "account_name" not in container or "container_name" not in container: log.error( - 'An azurefs container configuration is missing either an ' - 'account_name or a container_name: %s', container + "An azurefs container configuration is missing either an " + "account_name or a container_name: %s", + container, ) return False return True diff --git a/salt/fileserver/gitfs.py b/salt/fileserver/gitfs.py index 494ca375611..fdf8661e391 100644 --- a/salt/fileserver/gitfs.py +++ b/salt/fileserver/gitfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Git Fileserver Backend With this backend, branches and tags in a remote git repository are exposed to @@ -46,50 +46,59 @@ Walkthrough `. .. _pygit2: https://github.com/libgit2/pygit2 .. _libgit2: https://libgit2.github.com/ .. _GitPython: https://github.com/gitpython-developers/GitPython -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -PER_REMOTE_OVERRIDES = ( - 'base', 'mountpoint', 'root', 'ssl_verify', - 'saltenv_whitelist', 'saltenv_blacklist', - 'refspecs', 'disable_saltenv_mapping', - 'ref_types', 'update_interval', -) -PER_REMOTE_ONLY = ('all_saltenvs', 'name', 'saltenv') - -# Auth support (auth params can be global or per-remote, too) -AUTH_PROVIDERS = ('pygit2',) -AUTH_PARAMS = ('user', 'password', 'pubkey', 'privkey', 'passphrase', - 'insecure_auth') - # Import salt libs import salt.utils.gitfs from salt.exceptions import FileserverConfigError +PER_REMOTE_OVERRIDES = ( + "base", + "fallback", + "mountpoint", + "root", + "ssl_verify", + "saltenv_whitelist", + "saltenv_blacklist", + "refspecs", + "disable_saltenv_mapping", + "ref_types", + "update_interval", +) +PER_REMOTE_ONLY = ("all_saltenvs", "name", "saltenv") + +# Auth support (auth params can be global or per-remote, too) +AUTH_PROVIDERS = ("pygit2",) +AUTH_PARAMS = ("user", "password", "pubkey", "privkey", "passphrase", "insecure_auth") + + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'gitfs' +__virtualname__ = "gitfs" def _gitfs(init_remotes=True): return salt.utils.gitfs.GitFS( __opts__, - __opts__['gitfs_remotes'], + __opts__["gitfs_remotes"], per_remote_overrides=PER_REMOTE_OVERRIDES, per_remote_only=PER_REMOTE_ONLY, - init_remotes=init_remotes) + init_remotes=init_remotes, + ) def __virtual__(): - ''' + """ Only load if the desired provider module is present and gitfs is enabled properly in the master config file. - ''' - if __virtualname__ not in __opts__['fileserver_backend']: + """ + if __virtualname__ not in __opts__["fileserver_backend"]: return False try: _gitfs(init_remotes=False) @@ -102,106 +111,106 @@ def __virtual__(): def clear_cache(): - ''' + """ Completely clear gitfs cache - ''' + """ return _gitfs(init_remotes=False).clear_cache() -def clear_lock(remote=None, lock_type='update'): - ''' +def clear_lock(remote=None, lock_type="update"): + """ Clear update.lk - ''' + """ return _gitfs().clear_lock(remote=remote, lock_type=lock_type) def lock(remote=None): - ''' + """ Place an update.lk ``remote`` can either be a dictionary containing repo configuration information, or a pattern. If the latter, then remotes for which the URL matches the pattern will be locked. - ''' + """ return _gitfs().lock(remote=remote) def update(remotes=None): - ''' + """ Execute a git fetch on all of the repos - ''' + """ _gitfs().update(remotes) def update_intervals(): - ''' + """ Returns the update intervals for each configured remote - ''' + """ return _gitfs().update_intervals() def envs(ignore_cache=False): - ''' + """ Return a list of refs that can be used as environments - ''' + """ return _gitfs().envs(ignore_cache=ignore_cache) -def find_file(path, tgt_env='base', **kwargs): # pylint: disable=W0613 - ''' +def find_file(path, tgt_env="base", **kwargs): # pylint: disable=W0613 + """ Find the first file to match the path and ref, read the file out of git and send the path to the newly cached file - ''' + """ return _gitfs().find_file(path, tgt_env=tgt_env, **kwargs) def init(): - ''' + """ Initialize remotes. This is only used by the master's pre-flight checks, and is not invoked by GitFS. - ''' + """ _gitfs() def serve_file(load, fnd): - ''' + """ Return a chunk from a file based on the data received - ''' + """ return _gitfs().serve_file(load, fnd) def file_hash(load, fnd): - ''' + """ Return a file hash, the hash type is set in the master config file - ''' + """ return _gitfs().file_hash(load, fnd) def file_list(load): - ''' + """ Return a list of all files on the file server in a specified environment (specified as a key within the load dict). - ''' + """ return _gitfs().file_list(load) def file_list_emptydirs(load): # pylint: disable=W0613 - ''' + """ Return a list of all empty directories on the master - ''' + """ # Cannot have empty dirs in git return [] def dir_list(load): - ''' + """ Return a list of all directories on the master - ''' + """ return _gitfs().dir_list(load) def symlink_list(load): - ''' + """ Return a dict of all symlinks based on a given path in the repo - ''' + """ return _gitfs().symlink_list(load) diff --git a/salt/fileserver/hgfs.py b/salt/fileserver/hgfs.py index 97f8b7b952e..0f954355e6f 100644 --- a/salt/fileserver/hgfs.py +++ b/salt/fileserver/hgfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Mercurial Fileserver Backend To enable, add ``hgfs`` to the :conf_master:`fileserver_backend` option in the @@ -34,10 +34,11 @@ will set the desired branch method. Possible values are: ``branches``, :depends: - mercurial - python bindings for mercurial (``python-hglib``) -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import errno import fnmatch @@ -47,20 +48,8 @@ import logging import os import shutil from datetime import datetime -from salt.exceptions import FileserverConfigError -VALID_BRANCH_METHODS = ('branches', 'bookmarks', 'mixed') -PER_REMOTE_OVERRIDES = ('base', 'branch_method', 'mountpoint', 'root') - -# Import third party libs -from salt.ext import six -# pylint: disable=import-error -try: - import hglib - HAS_HG = True -except ImportError: - HAS_HG = False -# pylint: enable=import-error +import salt.fileserver # Import salt libs import salt.utils.data @@ -70,38 +59,58 @@ import salt.utils.hashutils import salt.utils.stringutils import salt.utils.url import salt.utils.versions -import salt.fileserver +from salt.exceptions import FileserverConfigError + +# Import third party libs +from salt.ext import six from salt.utils.event import tagify +VALID_BRANCH_METHODS = ("branches", "bookmarks", "mixed") +PER_REMOTE_OVERRIDES = ("base", "branch_method", "mountpoint", "root") + + +# pylint: disable=import-error +try: + import hglib + + HAS_HG = True +except ImportError: + HAS_HG = False +# pylint: enable=import-error + + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'hg' +__virtualname__ = "hg" def __virtual__(): - ''' + """ Only load if mercurial is available - ''' - if __virtualname__ not in __opts__['fileserver_backend']: + """ + if __virtualname__ not in __opts__["fileserver_backend"]: return False if not HAS_HG: - log.error('Mercurial fileserver backend is enabled in configuration ' - 'but could not be loaded, is hglib installed?') - return False - if __opts__['hgfs_branch_method'] not in VALID_BRANCH_METHODS: log.error( - 'Invalid hgfs_branch_method \'%s\'. Valid methods are: %s', - __opts__['hgfs_branch_method'], VALID_BRANCH_METHODS + "Mercurial fileserver backend is enabled in configuration " + "but could not be loaded, is hglib installed?" + ) + return False + if __opts__["hgfs_branch_method"] not in VALID_BRANCH_METHODS: + log.error( + "Invalid hgfs_branch_method '%s'. Valid methods are: %s", + __opts__["hgfs_branch_method"], + VALID_BRANCH_METHODS, ) return False return __virtualname__ def _all_branches(repo): - ''' + """ Returns all branches for the specified repo - ''' + """ # repo.branches() returns a list of 3-tuples consisting of # (branch name, rev #, nodeid) # Example: [('default', 4, '7c96229269fa')] @@ -109,9 +118,9 @@ def _all_branches(repo): def _get_branch(repo, name): - ''' + """ Find the requested branch in the specified repo - ''' + """ try: return [x for x in _all_branches(repo) if x[0] == name][0] except IndexError: @@ -119,9 +128,9 @@ def _get_branch(repo, name): def _all_bookmarks(repo): - ''' + """ Returns all bookmarks for the specified repo - ''' + """ # repo.bookmarks() returns a tuple containing the following: # 1. A list of 3-tuples consisting of (bookmark name, rev #, nodeid) # 2. The index of the current bookmark (-1 if no current one) @@ -130,9 +139,9 @@ def _all_bookmarks(repo): def _get_bookmark(repo, name): - ''' + """ Find the requested bookmark in the specified repo - ''' + """ try: return [x for x in _all_bookmarks(repo) if x[0] == name][0] except IndexError: @@ -140,21 +149,21 @@ def _get_bookmark(repo, name): def _all_tags(repo): - ''' + """ Returns all tags for the specified repo - ''' + """ # repo.tags() returns a list of 4-tuples consisting of # (tag name, rev #, nodeid, islocal) # Example: [('1.0', 3, '3be15e71b31a', False), # ('tip', 4, '7c96229269fa', False)] # Avoid returning the special 'tip' tag. - return [x for x in repo.tags() if x[0] != 'tip'] + return [x for x in repo.tags() if x[0] != "tip"] def _get_tag(repo, name): - ''' + """ Find the requested tag in the specified repo - ''' + """ try: return [x for x in _all_tags(repo) if x[0] == name][0] except IndexError: @@ -162,83 +171,88 @@ def _get_tag(repo, name): def _get_ref(repo, name): - ''' + """ Return ref tuple if ref is in the repo. - ''' - if name == 'base': - name = repo['base'] - if name == repo['base'] or name in envs(): - if repo['branch_method'] == 'branches': - return _get_branch(repo['repo'], name) \ - or _get_tag(repo['repo'], name) - elif repo['branch_method'] == 'bookmarks': - return _get_bookmark(repo['repo'], name) \ - or _get_tag(repo['repo'], name) - elif repo['branch_method'] == 'mixed': - return _get_branch(repo['repo'], name) \ - or _get_bookmark(repo['repo'], name) \ - or _get_tag(repo['repo'], name) + """ + if name == "base": + name = repo["base"] + if name == repo["base"] or name in envs(): + if repo["branch_method"] == "branches": + return _get_branch(repo["repo"], name) or _get_tag(repo["repo"], name) + elif repo["branch_method"] == "bookmarks": + return _get_bookmark(repo["repo"], name) or _get_tag(repo["repo"], name) + elif repo["branch_method"] == "mixed": + return ( + _get_branch(repo["repo"], name) + or _get_bookmark(repo["repo"], name) + or _get_tag(repo["repo"], name) + ) return False def _failhard(): - ''' + """ Fatal fileserver configuration issue, raise an exception - ''' - raise FileserverConfigError( - 'Failed to load hg fileserver backend' - ) + """ + raise FileserverConfigError("Failed to load hg fileserver backend") def init(): - ''' + """ Return a list of hglib objects for the various hgfs remotes - ''' - bp_ = os.path.join(__opts__['cachedir'], 'hgfs') + """ + bp_ = os.path.join(__opts__["cachedir"], "hgfs") new_remote = False repos = [] per_remote_defaults = {} for param in PER_REMOTE_OVERRIDES: - per_remote_defaults[param] = \ - six.text_type(__opts__['hgfs_{0}'.format(param)]) + per_remote_defaults[param] = six.text_type(__opts__["hgfs_{0}".format(param)]) - for remote in __opts__['hgfs_remotes']: + for remote in __opts__["hgfs_remotes"]: repo_conf = copy.deepcopy(per_remote_defaults) if isinstance(remote, dict): repo_url = next(iter(remote)) per_remote_conf = dict( - [(key, six.text_type(val)) for key, val in - six.iteritems(salt.utils.data.repack_dictlist(remote[repo_url]))] + [ + (key, six.text_type(val)) + for key, val in six.iteritems( + salt.utils.data.repack_dictlist(remote[repo_url]) + ) + ] ) if not per_remote_conf: log.error( - 'Invalid per-remote configuration for hgfs remote %s. If ' - 'no per-remote parameters are being specified, there may ' - 'be a trailing colon after the URL, which should be ' - 'removed. Check the master configuration file.', repo_url + "Invalid per-remote configuration for hgfs remote %s. If " + "no per-remote parameters are being specified, there may " + "be a trailing colon after the URL, which should be " + "removed. Check the master configuration file.", + repo_url, ) _failhard() - branch_method = \ - per_remote_conf.get('branch_method', - per_remote_defaults['branch_method']) + branch_method = per_remote_conf.get( + "branch_method", per_remote_defaults["branch_method"] + ) if branch_method not in VALID_BRANCH_METHODS: log.error( - 'Invalid branch_method \'%s\' for remote %s. Valid ' - 'branch methods are: %s. This remote will be ignored.', - branch_method, repo_url, ', '.join(VALID_BRANCH_METHODS) + "Invalid branch_method '%s' for remote %s. Valid " + "branch methods are: %s. This remote will be ignored.", + branch_method, + repo_url, + ", ".join(VALID_BRANCH_METHODS), ) _failhard() per_remote_errors = False - for param in (x for x in per_remote_conf - if x not in PER_REMOTE_OVERRIDES): + for param in (x for x in per_remote_conf if x not in PER_REMOTE_OVERRIDES): log.error( - 'Invalid configuration parameter \'%s\' for remote %s. ' - 'Valid parameters are: %s. See the documentation for ' - 'further information.', - param, repo_url, ', '.join(PER_REMOTE_OVERRIDES) + "Invalid configuration parameter '%s' for remote %s. " + "Valid parameters are: %s. See the documentation for " + "further information.", + param, + repo_url, + ", ".join(PER_REMOTE_OVERRIDES), ) per_remote_errors = True if per_remote_errors: @@ -250,20 +264,21 @@ def init(): if not isinstance(repo_url, six.string_types): log.error( - 'Invalid hgfs remote %s. Remotes must be strings, you may ' - 'need to enclose the URL in quotes', repo_url + "Invalid hgfs remote %s. Remotes must be strings, you may " + "need to enclose the URL in quotes", + repo_url, ) _failhard() try: - repo_conf['mountpoint'] = salt.utils.url.strip_proto( - repo_conf['mountpoint'] + repo_conf["mountpoint"] = salt.utils.url.strip_proto( + repo_conf["mountpoint"] ) except TypeError: # mountpoint not specified pass - hash_type = getattr(hashlib, __opts__.get('hash_type', 'md5')) + hash_type = getattr(hashlib, __opts__.get("hash_type", "md5")) repo_hash = hash_type(repo_url).hexdigest() rp_ = os.path.join(bp_, repo_hash) if not os.path.isdir(rp_): @@ -277,21 +292,24 @@ def init(): repo = hglib.open(rp_) except hglib.error.ServerError: log.error( - 'Cache path %s (corresponding remote: %s) exists but is not ' - 'a valid mercurial repository. You will need to manually ' - 'delete this directory on the master to continue to use this ' - 'hgfs remote.', rp_, repo_url + "Cache path %s (corresponding remote: %s) exists but is not " + "a valid mercurial repository. You will need to manually " + "delete this directory on the master to continue to use this " + "hgfs remote.", + rp_, + repo_url, ) _failhard() except Exception as exc: # pylint: disable=broad-except log.error( - 'Exception \'%s\' encountered while initializing hgfs ' - 'remote %s', exc, repo_url + "Exception '%s' encountered while initializing hgfs " "remote %s", + exc, + repo_url, ) _failhard() try: - refs = repo.config(names='paths') + refs = repo.config(names="paths") except hglib.error.CommandError: refs = None @@ -300,52 +318,52 @@ def init(): # this way to support both older and newer hglib. if not refs: # Write an hgrc defining the remote URL - hgconfpath = os.path.join(rp_, '.hg', 'hgrc') - with salt.utils.files.fopen(hgconfpath, 'w+') as hgconfig: - hgconfig.write('[paths]\n') + hgconfpath = os.path.join(rp_, ".hg", "hgrc") + with salt.utils.files.fopen(hgconfpath, "w+") as hgconfig: + hgconfig.write("[paths]\n") hgconfig.write( - salt.utils.stringutils.to_str( - 'default = {0}\n'.format(repo_url) - ) + salt.utils.stringutils.to_str("default = {0}\n".format(repo_url)) ) - repo_conf.update({ - 'repo': repo, - 'url': repo_url, - 'hash': repo_hash, - 'cachedir': rp_, - 'lockfile': os.path.join(__opts__['cachedir'], - 'hgfs', - '{0}.update.lk'.format(repo_hash)) - }) + repo_conf.update( + { + "repo": repo, + "url": repo_url, + "hash": repo_hash, + "cachedir": rp_, + "lockfile": os.path.join( + __opts__["cachedir"], "hgfs", "{0}.update.lk".format(repo_hash) + ), + } + ) repos.append(repo_conf) repo.close() if new_remote: - remote_map = os.path.join(__opts__['cachedir'], 'hgfs/remote_map.txt') + remote_map = os.path.join(__opts__["cachedir"], "hgfs/remote_map.txt") try: - with salt.utils.files.fopen(remote_map, 'w+') as fp_: - timestamp = datetime.now().strftime('%d %b %Y %H:%M:%S.%f') - fp_.write('# hgfs_remote map as of {0}\n'.format(timestamp)) + with salt.utils.files.fopen(remote_map, "w+") as fp_: + timestamp = datetime.now().strftime("%d %b %Y %H:%M:%S.%f") + fp_.write("# hgfs_remote map as of {0}\n".format(timestamp)) for repo in repos: fp_.write( salt.utils.stringutils.to_str( - '{0} = {1}\n'.format(repo['hash'], repo['url']) + "{0} = {1}\n".format(repo["hash"], repo["url"]) ) ) except OSError: pass else: - log.info('Wrote new hgfs_remote map to %s', remote_map) + log.info("Wrote new hgfs_remote map to %s", remote_map) return repos def _clear_old_remotes(): - ''' + """ Remove cache directories for remotes no longer configured - ''' - bp_ = os.path.join(__opts__['cachedir'], 'hgfs') + """ + bp_ = os.path.join(__opts__["cachedir"], "hgfs") try: cachedir_ls = os.listdir(bp_) except OSError: @@ -354,12 +372,12 @@ def _clear_old_remotes(): # Remove actively-used remotes from list for repo in repos: try: - cachedir_ls.remove(repo['hash']) + cachedir_ls.remove(repo["hash"]) except ValueError: pass to_remove = [] for item in cachedir_ls: - if item in ('hash', 'refs'): + if item in ("hash", "refs"): continue path = os.path.join(bp_, item) if os.path.isdir(path): @@ -370,66 +388,66 @@ def _clear_old_remotes(): try: shutil.rmtree(rdir) except OSError as exc: - log.error( - 'Unable to remove old hgfs remote cachedir %s: %s', - rdir, exc - ) + log.error("Unable to remove old hgfs remote cachedir %s: %s", rdir, exc) failed.append(rdir) else: - log.debug('hgfs removed old cachedir %s', rdir) + log.debug("hgfs removed old cachedir %s", rdir) for fdir in failed: to_remove.remove(fdir) return bool(to_remove), repos def clear_cache(): - ''' + """ Completely clear hgfs cache - ''' - fsb_cachedir = os.path.join(__opts__['cachedir'], 'hgfs') - list_cachedir = os.path.join(__opts__['cachedir'], 'file_lists/hgfs') + """ + fsb_cachedir = os.path.join(__opts__["cachedir"], "hgfs") + list_cachedir = os.path.join(__opts__["cachedir"], "file_lists/hgfs") errors = [] for rdir in (fsb_cachedir, list_cachedir): if os.path.exists(rdir): try: shutil.rmtree(rdir) except OSError as exc: - errors.append('Unable to delete {0}: {1}'.format(rdir, exc)) + errors.append("Unable to delete {0}: {1}".format(rdir, exc)) return errors def clear_lock(remote=None): - ''' + """ Clear update.lk ``remote`` can either be a dictionary containing repo configuration information, or a pattern. If the latter, then remotes for which the URL matches the pattern will be locked. - ''' + """ + def _do_clear_lock(repo): def _add_error(errlist, repo, exc): - msg = ('Unable to remove update lock for {0} ({1}): {2} ' - .format(repo['url'], repo['lockfile'], exc)) + msg = "Unable to remove update lock for {0} ({1}): {2} ".format( + repo["url"], repo["lockfile"], exc + ) log.debug(msg) errlist.append(msg) + success = [] failed = [] - if os.path.exists(repo['lockfile']): + if os.path.exists(repo["lockfile"]): try: - os.remove(repo['lockfile']) + os.remove(repo["lockfile"]) except OSError as exc: if exc.errno == errno.EISDIR: # Somehow this path is a directory. Should never happen # unless some wiseguy manually creates a directory at this # path, but just in case, handle it. try: - shutil.rmtree(repo['lockfile']) + shutil.rmtree(repo["lockfile"]) except OSError as exc: _add_error(failed, repo, exc) else: _add_error(failed, repo, exc) else: - msg = 'Removed lock for {0}'.format(repo['url']) + msg = "Removed lock for {0}".format(repo["url"]) log.debug(msg) success.append(msg) return success, failed @@ -442,11 +460,11 @@ def clear_lock(remote=None): for repo in init(): if remote: try: - if not fnmatch.fnmatch(repo['url'], remote): + if not fnmatch.fnmatch(repo["url"], remote): continue except TypeError: # remote was non-string, try again - if not fnmatch.fnmatch(repo['url'], six.text_type(remote)): + if not fnmatch.fnmatch(repo["url"], six.text_type(remote)): continue success, failed = _do_clear_lock(repo) cleared.extend(success) @@ -455,27 +473,29 @@ def clear_lock(remote=None): def lock(remote=None): - ''' + """ Place an update.lk ``remote`` can either be a dictionary containing repo configuration information, or a pattern. If the latter, then remotes for which the URL matches the pattern will be locked. - ''' + """ + def _do_lock(repo): success = [] failed = [] - if not os.path.exists(repo['lockfile']): + if not os.path.exists(repo["lockfile"]): try: - with salt.utils.files.fopen(repo['lockfile'], 'w'): + with salt.utils.files.fopen(repo["lockfile"], "w"): pass except (IOError, OSError) as exc: - msg = ('Unable to set update lock for {0} ({1}): {2} ' - .format(repo['url'], repo['lockfile'], exc)) + msg = "Unable to set update lock for {0} ({1}): {2} ".format( + repo["url"], repo["lockfile"], exc + ) log.debug(msg) failed.append(msg) else: - msg = 'Set lock for {0}'.format(repo['url']) + msg = "Set lock for {0}".format(repo["url"]) log.debug(msg) success.append(msg) return success, failed @@ -488,11 +508,11 @@ def lock(remote=None): for repo in init(): if remote: try: - if not fnmatch.fnmatch(repo['url'], remote): + if not fnmatch.fnmatch(repo["url"], remote): continue except TypeError: # remote was non-string, try again - if not fnmatch.fnmatch(repo['url'], six.text_type(remote)): + if not fnmatch.fnmatch(repo["url"], six.text_type(remote)): continue success, failed = _do_lock(repo) locked.extend(success) @@ -502,73 +522,75 @@ def lock(remote=None): def update(): - ''' + """ Execute an hg pull on all of the repos - ''' + """ # data for the fileserver event - data = {'changed': False, - 'backend': 'hgfs'} + data = {"changed": False, "backend": "hgfs"} # _clear_old_remotes runs init(), so use the value from there to avoid a # second init() - data['changed'], repos = _clear_old_remotes() + data["changed"], repos = _clear_old_remotes() for repo in repos: - if os.path.exists(repo['lockfile']): + if os.path.exists(repo["lockfile"]): log.warning( - 'Update lockfile is present for hgfs remote %s, skipping. ' - 'If this warning persists, it is possible that the update ' - 'process was interrupted. Removing %s or running ' - '\'salt-run fileserver.clear_lock hgfs\' will allow updates ' - 'to continue for this remote.', repo['url'], repo['lockfile'] + "Update lockfile is present for hgfs remote %s, skipping. " + "If this warning persists, it is possible that the update " + "process was interrupted. Removing %s or running " + "'salt-run fileserver.clear_lock hgfs' will allow updates " + "to continue for this remote.", + repo["url"], + repo["lockfile"], ) continue _, errors = lock(repo) if errors: log.error( - 'Unable to set update lock for hgfs remote %s, skipping.', - repo['url'] + "Unable to set update lock for hgfs remote %s, skipping.", repo["url"] ) continue - log.debug('hgfs is fetching from %s', repo['url']) - repo['repo'].open() - curtip = repo['repo'].tip() + log.debug("hgfs is fetching from %s", repo["url"]) + repo["repo"].open() + curtip = repo["repo"].tip() try: - repo['repo'].pull() + repo["repo"].pull() except Exception as exc: # pylint: disable=broad-except log.error( - 'Exception %s caught while updating hgfs remote %s', - exc, repo['url'], exc_info_on_loglevel=logging.DEBUG + "Exception %s caught while updating hgfs remote %s", + exc, + repo["url"], + exc_info_on_loglevel=logging.DEBUG, ) else: - newtip = repo['repo'].tip() + newtip = repo["repo"].tip() if curtip[1] != newtip[1]: - data['changed'] = True - repo['repo'].close() + data["changed"] = True + repo["repo"].close() clear_lock(repo) - env_cache = os.path.join(__opts__['cachedir'], 'hgfs/envs.p') - if data.get('changed', False) is True or not os.path.isfile(env_cache): + env_cache = os.path.join(__opts__["cachedir"], "hgfs/envs.p") + if data.get("changed", False) is True or not os.path.isfile(env_cache): env_cachedir = os.path.dirname(env_cache) if not os.path.exists(env_cachedir): os.makedirs(env_cachedir) new_envs = envs(ignore_cache=True) serial = salt.payload.Serial(__opts__) - with salt.utils.files.fopen(env_cache, 'wb+') as fp_: + with salt.utils.files.fopen(env_cache, "wb+") as fp_: fp_.write(serial.dumps(new_envs)) - log.trace('Wrote env cache data to %s', env_cache) + log.trace("Wrote env cache data to %s", env_cache) # if there is a change, fire an event - if __opts__.get('fileserver_events', False): + if __opts__.get("fileserver_events", False): with salt.utils.event.get_event( - 'master', - __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=False) as event: - event.fire_event(data, tagify(['hgfs', 'update'], prefix='fileserver')) + "master", + __opts__["sock_dir"], + __opts__["transport"], + opts=__opts__, + listen=False, + ) as event: + event.fire_event(data, tagify(["hgfs", "update"], prefix="fileserver")) try: salt.fileserver.reap_fileserver_cache_dir( - os.path.join(__opts__['cachedir'], 'hgfs/hash'), - find_file + os.path.join(__opts__["cachedir"], "hgfs/hash"), find_file ) except (IOError, OSError): # Hash file won't exist if no files have yet been served up @@ -576,69 +598,65 @@ def update(): def _env_is_exposed(env): - ''' + """ Check if an environment is exposed by comparing it against a whitelist and blacklist. - ''' + """ return salt.utils.stringutils.check_whitelist_blacklist( env, - whitelist=__opts__['hgfs_saltenv_whitelist'], - blacklist=__opts__['hgfs_saltenv_blacklist'], + whitelist=__opts__["hgfs_saltenv_whitelist"], + blacklist=__opts__["hgfs_saltenv_blacklist"], ) def envs(ignore_cache=False): - ''' + """ Return a list of refs that can be used as environments - ''' + """ if not ignore_cache: - env_cache = os.path.join(__opts__['cachedir'], 'hgfs/envs.p') + env_cache = os.path.join(__opts__["cachedir"], "hgfs/envs.p") cache_match = salt.fileserver.check_env_cache(__opts__, env_cache) if cache_match is not None: return cache_match ret = set() for repo in init(): - repo['repo'].open() - if repo['branch_method'] in ('branches', 'mixed'): - for branch in _all_branches(repo['repo']): + repo["repo"].open() + if repo["branch_method"] in ("branches", "mixed"): + for branch in _all_branches(repo["repo"]): branch_name = branch[0] - if branch_name == repo['base']: - branch_name = 'base' + if branch_name == repo["base"]: + branch_name = "base" ret.add(branch_name) - if repo['branch_method'] in ('bookmarks', 'mixed'): - for bookmark in _all_bookmarks(repo['repo']): + if repo["branch_method"] in ("bookmarks", "mixed"): + for bookmark in _all_bookmarks(repo["repo"]): bookmark_name = bookmark[0] - if bookmark_name == repo['base']: - bookmark_name = 'base' + if bookmark_name == repo["base"]: + bookmark_name = "base" ret.add(bookmark_name) - ret.update([x[0] for x in _all_tags(repo['repo'])]) - repo['repo'].close() + ret.update([x[0] for x in _all_tags(repo["repo"])]) + repo["repo"].close() return [x for x in sorted(ret) if _env_is_exposed(x)] -def find_file(path, tgt_env='base', **kwargs): # pylint: disable=W0613 - ''' +def find_file(path, tgt_env="base", **kwargs): # pylint: disable=W0613 + """ Find the first file to match the path and ref, read the file out of hg and send the path to the newly cached file - ''' - fnd = {'path': '', - 'rel': ''} + """ + fnd = {"path": "", "rel": ""} if os.path.isabs(path) or tgt_env not in envs(): return fnd - dest = os.path.join(__opts__['cachedir'], 'hgfs/refs', tgt_env, path) - hashes_glob = os.path.join(__opts__['cachedir'], - 'hgfs/hash', - tgt_env, - '{0}.hash.*'.format(path)) - blobshadest = os.path.join(__opts__['cachedir'], - 'hgfs/hash', - tgt_env, - '{0}.hash.blob_sha1'.format(path)) - lk_fn = os.path.join(__opts__['cachedir'], - 'hgfs/hash', - tgt_env, - '{0}.lk'.format(path)) + dest = os.path.join(__opts__["cachedir"], "hgfs/refs", tgt_env, path) + hashes_glob = os.path.join( + __opts__["cachedir"], "hgfs/hash", tgt_env, "{0}.hash.*".format(path) + ) + blobshadest = os.path.join( + __opts__["cachedir"], "hgfs/hash", tgt_env, "{0}.hash.blob_sha1".format(path) + ) + lk_fn = os.path.join( + __opts__["cachedir"], "hgfs/hash", tgt_env, "{0}.lk".format(path) + ) destdir = os.path.dirname(dest) hashdir = os.path.dirname(blobshadest) if not os.path.isdir(destdir): @@ -657,50 +675,47 @@ def find_file(path, tgt_env='base', **kwargs): # pylint: disable=W0613 os.makedirs(hashdir) for repo in init(): - if repo['mountpoint'] \ - and not path.startswith(repo['mountpoint'] + os.path.sep): + if repo["mountpoint"] and not path.startswith(repo["mountpoint"] + os.path.sep): continue - repo_path = path[len(repo['mountpoint']):].lstrip(os.path.sep) - if repo['root']: - repo_path = os.path.join(repo['root'], repo_path) + repo_path = path[len(repo["mountpoint"]) :].lstrip(os.path.sep) + if repo["root"]: + repo_path = os.path.join(repo["root"], repo_path) - repo['repo'].open() + repo["repo"].open() ref = _get_ref(repo, tgt_env) if not ref: # Branch or tag not found in repo, try the next - repo['repo'].close() + repo["repo"].close() continue salt.fileserver.wait_lock(lk_fn, dest) if os.path.isfile(blobshadest) and os.path.isfile(dest): - with salt.utils.files.fopen(blobshadest, 'r') as fp_: + with salt.utils.files.fopen(blobshadest, "r") as fp_: sha = fp_.read() if sha == ref[2]: - fnd['rel'] = path - fnd['path'] = dest - repo['repo'].close() + fnd["rel"] = path + fnd["path"] = dest + repo["repo"].close() return fnd try: - repo['repo'].cat( - ['path:{0}'.format(repo_path)], rev=ref[2], output=dest - ) + repo["repo"].cat(["path:{0}".format(repo_path)], rev=ref[2], output=dest) except hglib.error.CommandError: - repo['repo'].close() + repo["repo"].close() continue - with salt.utils.files.fopen(lk_fn, 'w'): + with salt.utils.files.fopen(lk_fn, "w"): pass for filename in glob.glob(hashes_glob): try: os.remove(filename) except Exception: # pylint: disable=broad-except pass - with salt.utils.files.fopen(blobshadest, 'w+') as fp_: + with salt.utils.files.fopen(blobshadest, "w+") as fp_: fp_.write(ref[2]) try: os.remove(lk_fn) except (OSError, IOError): pass - fnd['rel'] = path - fnd['path'] = dest + fnd["rel"] = path + fnd["path"] = dest try: # Converting the stat result to a list, the elements of the # list correspond to the following stat_result params: @@ -714,183 +729,180 @@ def find_file(path, tgt_env='base', **kwargs): # pylint: disable=W0613 # 7 => st_atime=1468284229 # 8 => st_mtime=1456338235 # 9 => st_ctime=1456338235 - fnd['stat'] = list(os.stat(dest)) + fnd["stat"] = list(os.stat(dest)) except Exception: # pylint: disable=broad-except pass - repo['repo'].close() + repo["repo"].close() return fnd return fnd def serve_file(load, fnd): - ''' + """ Return a chunk from a file based on the data received - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - ret = {'data': '', - 'dest': ''} - if not all(x in load for x in ('path', 'loc', 'saltenv')): + ret = {"data": "", "dest": ""} + if not all(x in load for x in ("path", "loc", "saltenv")): return ret - if not fnd['path']: + if not fnd["path"]: return ret - ret['dest'] = fnd['rel'] - gzip = load.get('gzip', None) - fpath = os.path.normpath(fnd['path']) - with salt.utils.files.fopen(fpath, 'rb') as fp_: - fp_.seek(load['loc']) - data = fp_.read(__opts__['file_buffer_size']) + ret["dest"] = fnd["rel"] + gzip = load.get("gzip", None) + fpath = os.path.normpath(fnd["path"]) + with salt.utils.files.fopen(fpath, "rb") as fp_: + fp_.seek(load["loc"]) + data = fp_.read(__opts__["file_buffer_size"]) if data and six.PY3 and not salt.utils.files.is_binary(fpath): data = data.decode(__salt_system_encoding__) if gzip and data: data = salt.utils.gzip_util.compress(data, gzip) - ret['gzip'] = gzip - ret['data'] = data + ret["gzip"] = gzip + ret["data"] = data return ret def file_hash(load, fnd): - ''' + """ Return a file hash, the hash type is set in the master config file - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if not all(x in load for x in ('path', 'saltenv')): - return '' - ret = {'hash_type': __opts__['hash_type']} - relpath = fnd['rel'] - path = fnd['path'] - hashdest = os.path.join(__opts__['cachedir'], - 'hgfs/hash', - load['saltenv'], - '{0}.hash.{1}'.format(relpath, - __opts__['hash_type'])) + if not all(x in load for x in ("path", "saltenv")): + return "" + ret = {"hash_type": __opts__["hash_type"]} + relpath = fnd["rel"] + path = fnd["path"] + hashdest = os.path.join( + __opts__["cachedir"], + "hgfs/hash", + load["saltenv"], + "{0}.hash.{1}".format(relpath, __opts__["hash_type"]), + ) if not os.path.isfile(hashdest): - ret['hsum'] = salt.utils.hashutils.get_hash(path, __opts__['hash_type']) - with salt.utils.files.fopen(hashdest, 'w+') as fp_: - fp_.write(ret['hsum']) + ret["hsum"] = salt.utils.hashutils.get_hash(path, __opts__["hash_type"]) + with salt.utils.files.fopen(hashdest, "w+") as fp_: + fp_.write(ret["hsum"]) return ret else: - with salt.utils.files.fopen(hashdest, 'rb') as fp_: - ret['hsum'] = salt.utils.stringutils.to_unicode(fp_.read()) + with salt.utils.files.fopen(hashdest, "rb") as fp_: + ret["hsum"] = salt.utils.stringutils.to_unicode(fp_.read()) return ret def _file_lists(load, form): - ''' + """ Return a dict containing the file lists for files and dirs - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - list_cachedir = os.path.join(__opts__['cachedir'], 'file_lists/hgfs') + list_cachedir = os.path.join(__opts__["cachedir"], "file_lists/hgfs") if not os.path.isdir(list_cachedir): try: os.makedirs(list_cachedir) except os.error: - log.critical('Unable to make cachedir %s', list_cachedir) + log.critical("Unable to make cachedir %s", list_cachedir) return [] - list_cache = os.path.join(list_cachedir, '{0}.p'.format(load['saltenv'])) - w_lock = os.path.join(list_cachedir, '.{0}.w'.format(load['saltenv'])) - cache_match, refresh_cache, save_cache = \ - salt.fileserver.check_file_list_cache( - __opts__, form, list_cache, w_lock - ) + list_cache = os.path.join(list_cachedir, "{0}.p".format(load["saltenv"])) + w_lock = os.path.join(list_cachedir, ".{0}.w".format(load["saltenv"])) + cache_match, refresh_cache, save_cache = salt.fileserver.check_file_list_cache( + __opts__, form, list_cache, w_lock + ) if cache_match is not None: return cache_match if refresh_cache: ret = {} - ret['files'] = _get_file_list(load) - ret['dirs'] = _get_dir_list(load) + ret["files"] = _get_file_list(load) + ret["dirs"] = _get_dir_list(load) if save_cache: - salt.fileserver.write_file_list_cache( - __opts__, ret, list_cache, w_lock - ) + salt.fileserver.write_file_list_cache(__opts__, ret, list_cache, w_lock) return ret.get(form, []) # Shouldn't get here, but if we do, this prevents a TypeError return [] def file_list(load): - ''' + """ Return a list of all files on the file server in a specified environment - ''' - return _file_lists(load, 'files') + """ + return _file_lists(load, "files") def _get_file_list(load): - ''' + """ Get a list of all files on the file server in a specified environment - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if 'saltenv' not in load or load['saltenv'] not in envs(): + if "saltenv" not in load or load["saltenv"] not in envs(): return [] ret = set() for repo in init(): - repo['repo'].open() - ref = _get_ref(repo, load['saltenv']) + repo["repo"].open() + ref = _get_ref(repo, load["saltenv"]) if ref: - manifest = repo['repo'].manifest(rev=ref[1]) + manifest = repo["repo"].manifest(rev=ref[1]) for tup in manifest: - relpath = os.path.relpath(tup[4], repo['root']) + relpath = os.path.relpath(tup[4], repo["root"]) # Don't add files outside the hgfs_root - if not relpath.startswith('../'): - ret.add(os.path.join(repo['mountpoint'], relpath)) - repo['repo'].close() + if not relpath.startswith("../"): + ret.add(os.path.join(repo["mountpoint"], relpath)) + repo["repo"].close() return sorted(ret) def file_list_emptydirs(load): # pylint: disable=W0613 - ''' + """ Return a list of all empty directories on the master - ''' + """ # Cannot have empty dirs in hg return [] def dir_list(load): - ''' + """ Return a list of all directories on the master - ''' - return _file_lists(load, 'dirs') + """ + return _file_lists(load, "dirs") def _get_dir_list(load): - ''' + """ Get a list of all directories on the master - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if 'saltenv' not in load or load['saltenv'] not in envs(): + if "saltenv" not in load or load["saltenv"] not in envs(): return [] ret = set() for repo in init(): - repo['repo'].open() - ref = _get_ref(repo, load['saltenv']) + repo["repo"].open() + ref = _get_ref(repo, load["saltenv"]) if ref: - manifest = repo['repo'].manifest(rev=ref[1]) + manifest = repo["repo"].manifest(rev=ref[1]) for tup in manifest: filepath = tup[4] - split = filepath.rsplit('/', 1) + split = filepath.rsplit("/", 1) while len(split) > 1: - relpath = os.path.relpath(split[0], repo['root']) + relpath = os.path.relpath(split[0], repo["root"]) # Don't add '.' - if relpath != '.': + if relpath != ".": # Don't add files outside the hgfs_root - if not relpath.startswith('../'): - ret.add(os.path.join(repo['mountpoint'], relpath)) - split = split[0].rsplit('/', 1) - repo['repo'].close() - if repo['mountpoint']: - ret.add(repo['mountpoint']) + if not relpath.startswith("../"): + ret.add(os.path.join(repo["mountpoint"], relpath)) + split = split[0].rsplit("/", 1) + repo["repo"].close() + if repo["mountpoint"]: + ret.add(repo["mountpoint"]) return sorted(ret) diff --git a/salt/fileserver/minionfs.py b/salt/fileserver/minionfs.py index 53603503a5e..1013981dcf6 100644 --- a/salt/fileserver/minionfs.py +++ b/salt/fileserver/minionfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Fileserver backend which serves files pushed to the Master The :mod:`cp.push ` function allows Minions to push files @@ -26,12 +26,13 @@ Other minionfs settings include: :conf_master:`minionfs_whitelist`, .. seealso:: :ref:`tutorial-minionfs` -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging # Import salt libs import salt.fileserver @@ -50,76 +51,74 @@ log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'minionfs' +__virtualname__ = "minionfs" def __virtual__(): - ''' + """ Only load if file_recv is enabled - ''' - if __virtualname__ not in __opts__['fileserver_backend']: + """ + if __virtualname__ not in __opts__["fileserver_backend"]: return False - return __virtualname__ if __opts__['file_recv'] else False + return __virtualname__ if __opts__["file_recv"] else False def _is_exposed(minion): - ''' + """ Check if the minion is exposed, based on the whitelist and blacklist - ''' + """ return salt.utils.stringutils.check_whitelist_blacklist( minion, - whitelist=__opts__['minionfs_whitelist'], - blacklist=__opts__['minionfs_blacklist'] + whitelist=__opts__["minionfs_whitelist"], + blacklist=__opts__["minionfs_blacklist"], ) -def find_file(path, tgt_env='base', **kwargs): # pylint: disable=W0613 - ''' +def find_file(path, tgt_env="base", **kwargs): # pylint: disable=W0613 + """ Search the environment for the relative path - ''' - fnd = {'path': '', 'rel': ''} + """ + fnd = {"path": "", "rel": ""} if os.path.isabs(path): return fnd if tgt_env not in envs(): return fnd - if os.path.basename(path) == 'top.sls': + if os.path.basename(path) == "top.sls": log.debug( - 'minionfs will NOT serve top.sls ' - 'for security reasons (path requested: %s)', path + "minionfs will NOT serve top.sls " + "for security reasons (path requested: %s)", + path, ) return fnd - mountpoint = salt.utils.url.strip_proto(__opts__['minionfs_mountpoint']) + mountpoint = salt.utils.url.strip_proto(__opts__["minionfs_mountpoint"]) # Remove the mountpoint to get the "true" path - path = path[len(mountpoint):].lstrip(os.path.sep) + path = path[len(mountpoint) :].lstrip(os.path.sep) try: minion, pushed_file = path.split(os.sep, 1) except ValueError: return fnd if not _is_exposed(minion): return fnd - full = os.path.join( - __opts__['cachedir'], 'minions', minion, 'files', pushed_file - ) - if os.path.isfile(full) \ - and not salt.fileserver.is_file_ignored(__opts__, full): - fnd['path'] = full - fnd['rel'] = path - fnd['stat'] = list(os.stat(full)) + full = os.path.join(__opts__["cachedir"], "minions", minion, "files", pushed_file) + if os.path.isfile(full) and not salt.fileserver.is_file_ignored(__opts__, full): + fnd["path"] = full + fnd["rel"] = path + fnd["stat"] = list(os.stat(full)) return fnd return fnd def envs(): - ''' + """ Returns the one environment specified for minionfs in the master configuration. - ''' - return [__opts__['minionfs_env']] + """ + return [__opts__["minionfs_env"]] def serve_file(load, fnd): - ''' + """ Return a chunk from a file based on the data received CLI Example: @@ -129,54 +128,54 @@ def serve_file(load, fnd): # Push the file to the master $ salt 'source-minion' cp.push /path/to/the/file $ salt 'destination-minion' cp.get_file salt://source-minion/path/to/the/file /destination/file - ''' - ret = {'data': '', 'dest': ''} - if not fnd['path']: + """ + ret = {"data": "", "dest": ""} + if not fnd["path"]: return ret - ret['dest'] = fnd['rel'] - gzip = load.get('gzip', None) - fpath = os.path.normpath(fnd['path']) + ret["dest"] = fnd["rel"] + gzip = load.get("gzip", None) + fpath = os.path.normpath(fnd["path"]) # AP # May I sleep here to slow down serving of big files? # How many threads are serving files? - with salt.utils.files.fopen(fpath, 'rb') as fp_: - fp_.seek(load['loc']) - data = fp_.read(__opts__['file_buffer_size']) + with salt.utils.files.fopen(fpath, "rb") as fp_: + fp_.seek(load["loc"]) + data = fp_.read(__opts__["file_buffer_size"]) if data and six.PY3 and not salt.utils.files.is_binary(fpath): data = data.decode(__salt_system_encoding__) if gzip and data: data = salt.utils.gzip_util.compress(data, gzip) - ret['gzip'] = gzip - ret['data'] = data + ret["gzip"] = gzip + ret["data"] = data return ret def update(): - ''' + """ When we are asked to update (regular interval) lets reap the cache - ''' + """ try: salt.fileserver.reap_fileserver_cache_dir( - os.path.join(__opts__['cachedir'], 'minionfs/hash'), - find_file) + os.path.join(__opts__["cachedir"], "minionfs/hash"), find_file + ) except os.error: # Hash file won't exist if no files have yet been served up pass def file_hash(load, fnd): - ''' + """ Return a file hash, the hash type is set in the master config file - ''' - path = fnd['path'] + """ + path = fnd["path"] ret = {} - if 'env' in load: + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if load['saltenv'] not in envs(): + if load["saltenv"] not in envs(): return {} # if the file doesn't exist, we can't get a hash @@ -184,79 +183,80 @@ def file_hash(load, fnd): return ret # set the hash_type as it is determined by config-- so mechanism won't change that - ret['hash_type'] = __opts__['hash_type'] + ret["hash_type"] = __opts__["hash_type"] # check if the hash is cached # cache file's contents should be "hash:mtime" cache_path = os.path.join( - __opts__['cachedir'], - 'minionfs', - 'hash', - load['saltenv'], - '{0}.hash.{1}'.format(fnd['rel'], __opts__['hash_type']) + __opts__["cachedir"], + "minionfs", + "hash", + load["saltenv"], + "{0}.hash.{1}".format(fnd["rel"], __opts__["hash_type"]), ) # if we have a cache, serve that if the mtime hasn't changed if os.path.exists(cache_path): try: - with salt.utils.files.fopen(cache_path, 'rb') as fp_: + with salt.utils.files.fopen(cache_path, "rb") as fp_: try: - hsum, mtime = salt.utils.stringutils.to_unicode(fp_.read()).split(':') + hsum, mtime = salt.utils.stringutils.to_unicode(fp_.read()).split( + ":" + ) except ValueError: log.debug( - 'Fileserver attempted to read incomplete cache file. ' - 'Retrying.' + "Fileserver attempted to read incomplete cache file. " + "Retrying." ) file_hash(load, fnd) return ret if os.path.getmtime(path) == mtime: # check if mtime changed - ret['hsum'] = hsum + ret["hsum"] = hsum return ret # Can't use Python select() because we need Windows support except os.error: log.debug( - 'Fileserver encountered lock when reading cache file. ' - 'Retrying.' + "Fileserver encountered lock when reading cache file. " "Retrying." ) file_hash(load, fnd) return ret # if we don't have a cache entry-- lets make one - ret['hsum'] = salt.utils.hashutils.get_hash(path, __opts__['hash_type']) + ret["hsum"] = salt.utils.hashutils.get_hash(path, __opts__["hash_type"]) cache_dir = os.path.dirname(cache_path) # make cache directory if it doesn't exist if not os.path.exists(cache_dir): os.makedirs(cache_dir) # save the cache object "hash:mtime" - cache_object = '{0}:{1}'.format(ret['hsum'], os.path.getmtime(path)) - with salt.utils.files.flopen(cache_path, 'w') as fp_: + cache_object = "{0}:{1}".format(ret["hsum"], os.path.getmtime(path)) + with salt.utils.files.flopen(cache_path, "w") as fp_: fp_.write(cache_object) return ret def file_list(load): - ''' + """ Return a list of all files on the file server in a specified environment - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if load['saltenv'] not in envs(): + if load["saltenv"] not in envs(): return [] - mountpoint = salt.utils.url.strip_proto(__opts__['minionfs_mountpoint']) - prefix = load.get('prefix', '').strip('/') + mountpoint = salt.utils.url.strip_proto(__opts__["minionfs_mountpoint"]) + prefix = load.get("prefix", "").strip("/") if mountpoint and prefix.startswith(mountpoint + os.path.sep): - prefix = prefix[len(mountpoint + os.path.sep):] + prefix = prefix[len(mountpoint + os.path.sep) :] - minions_cache_dir = os.path.join(__opts__['cachedir'], 'minions') + minions_cache_dir = os.path.join(__opts__["cachedir"], "minions") minion_dirs = os.listdir(minions_cache_dir) # If the prefix is not an empty string, then get the minion id from it. The # minion ID will be the part before the first slash, so if there is no # slash, this is an invalid path. if prefix: - tgt_minion, _, prefix = prefix.partition('/') + tgt_minion, _, prefix = prefix.partition("/") if not prefix: # No minion ID in path return [] @@ -264,8 +264,7 @@ def file_list(load): # pushed files if tgt_minion not in minion_dirs: log.warning( - 'No files found in minionfs cache for minion ID \'%s\'', - tgt_minion + "No files found in minionfs cache for minion ID '%s'", tgt_minion ) return [] minion_dirs = [tgt_minion] @@ -274,11 +273,11 @@ def file_list(load): for minion in minion_dirs: if not _is_exposed(minion): continue - minion_files_dir = os.path.join(minions_cache_dir, minion, 'files') + minion_files_dir = os.path.join(minions_cache_dir, minion, "files") if not os.path.isdir(minion_files_dir): log.debug( - 'minionfs: could not find files directory under %s!', - os.path.join(minions_cache_dir, minion) + "minionfs: could not find files directory under %s!", + os.path.join(minions_cache_dir, minion), ) continue walk_dir = os.path.join(minion_files_dir, prefix) @@ -288,10 +287,8 @@ def file_list(load): # Ignore links for security reasons if os.path.islink(os.path.join(root, fname)): continue - relpath = os.path.relpath( - os.path.join(root, fname), minion_files_dir - ) - if relpath.startswith('../'): + relpath = os.path.relpath(os.path.join(root, fname), minion_files_dir) + if relpath.startswith("../"): continue rel_fn = os.path.join(mountpoint, minion, relpath) if not salt.fileserver.is_file_ignored(__opts__, rel_fn): @@ -300,11 +297,11 @@ def file_list(load): # There should be no emptydirs -#def file_list_emptydirs(load): +# def file_list_emptydirs(load): def dir_list(load): - ''' + """ Return a list of all directories on the master CLI Example: @@ -316,26 +313,26 @@ def dir_list(load): destination-minion: - source-minion/absolute - source-minion/absolute/path - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if load['saltenv'] not in envs(): + if load["saltenv"] not in envs(): return [] - mountpoint = salt.utils.url.strip_proto(__opts__['minionfs_mountpoint']) - prefix = load.get('prefix', '').strip('/') + mountpoint = salt.utils.url.strip_proto(__opts__["minionfs_mountpoint"]) + prefix = load.get("prefix", "").strip("/") if mountpoint and prefix.startswith(mountpoint + os.path.sep): - prefix = prefix[len(mountpoint + os.path.sep):] + prefix = prefix[len(mountpoint + os.path.sep) :] - minions_cache_dir = os.path.join(__opts__['cachedir'], 'minions') + minions_cache_dir = os.path.join(__opts__["cachedir"], "minions") minion_dirs = os.listdir(minions_cache_dir) # If the prefix is not an empty string, then get the minion id from it. The # minion ID will be the part before the first slash, so if there is no # slash, this is an invalid path. if prefix: - tgt_minion, _, prefix = prefix.partition('/') + tgt_minion, _, prefix = prefix.partition("/") if not prefix: # No minion ID in path return [] @@ -343,8 +340,7 @@ def dir_list(load): # pushed files if tgt_minion not in minion_dirs: log.warning( - 'No files found in minionfs cache for minion ID \'%s\'', - tgt_minion + "No files found in minionfs cache for minion ID '%s'", tgt_minion ) return [] minion_dirs = [tgt_minion] @@ -353,11 +349,11 @@ def dir_list(load): for minion in os.listdir(minions_cache_dir): if not _is_exposed(minion): continue - minion_files_dir = os.path.join(minions_cache_dir, minion, 'files') + minion_files_dir = os.path.join(minions_cache_dir, minion, "files") if not os.path.isdir(minion_files_dir): log.warning( - 'minionfs: could not find files directory under %s!', - os.path.join(minions_cache_dir, minion) + "minionfs: could not find files directory under %s!", + os.path.join(minions_cache_dir, minion), ) continue walk_dir = os.path.join(minion_files_dir, prefix) @@ -366,7 +362,7 @@ def dir_list(load): relpath = os.path.relpath(root, minion_files_dir) # Ensure that the current directory and directories outside of # the minion dir do not end up in return list - if relpath in ('.', '..') or relpath.startswith('../'): + if relpath in (".", "..") or relpath.startswith("../"): continue ret.append(os.path.join(mountpoint, minion, relpath)) return ret diff --git a/salt/fileserver/roots.py b/salt/fileserver/roots.py index 160e2d99a09..66227999b3d 100644 --- a/salt/fileserver/roots.py +++ b/salt/fileserver/roots.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The default file server backend This fileserver backend serves files from the Master's local filesystem. If @@ -14,13 +14,14 @@ be in the :conf_master:`fileserver_backend` list to enable this backend. Fileserver environments are defined using the :conf_master:`file_roots` configuration option. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import errno +import logging + # Import python libs import os -import errno -import logging # Import salt libs import salt.fileserver @@ -37,28 +38,29 @@ from salt.ext import six log = logging.getLogger(__name__) -def find_file(path, saltenv='base', **kwargs): - ''' +def find_file(path, saltenv="base", **kwargs): + """ Search the environment for the relative path. - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") path = os.path.normpath(path) - fnd = {'path': '', - 'rel': ''} + fnd = {"path": "", "rel": ""} if os.path.isabs(path): return fnd - if saltenv not in __opts__['file_roots']: - if '__env__' in __opts__['file_roots']: - log.debug("salt environment '%s' maps to __env__ file_roots directory", saltenv) - saltenv = '__env__' + if saltenv not in __opts__["file_roots"]: + if "__env__" in __opts__["file_roots"]: + log.debug( + "salt environment '%s' maps to __env__ file_roots directory", saltenv + ) + saltenv = "__env__" else: return fnd def _add_file_stat(fnd): - ''' + """ Stat the file and, assuming no errors were found, convert the stat result to a list of values and add to the return dict. @@ -75,16 +77,16 @@ def find_file(path, saltenv='base', **kwargs): 7 => st_atime=1468284229 8 => st_mtime=1456338235 9 => st_ctime=1456338235 - ''' + """ try: - fnd['stat'] = list(os.stat(fnd['path'])) + fnd["stat"] = list(os.stat(fnd["path"])) except Exception: # pylint: disable=broad-except pass return fnd - if 'index' in kwargs: + if "index" in kwargs: try: - root = __opts__['file_roots'][saltenv][int(kwargs['index'])] + root = __opts__["file_roots"][saltenv][int(kwargs["index"])] except IndexError: # An invalid index was passed return fnd @@ -93,141 +95,137 @@ def find_file(path, saltenv='base', **kwargs): return fnd full = os.path.join(root, path) if os.path.isfile(full) and not salt.fileserver.is_file_ignored(__opts__, full): - fnd['path'] = full - fnd['rel'] = path + fnd["path"] = full + fnd["rel"] = path return _add_file_stat(fnd) return fnd - for root in __opts__['file_roots'][saltenv]: + for root in __opts__["file_roots"][saltenv]: full = os.path.join(root, path) if os.path.isfile(full) and not salt.fileserver.is_file_ignored(__opts__, full): - fnd['path'] = full - fnd['rel'] = path + fnd["path"] = full + fnd["rel"] = path return _add_file_stat(fnd) return fnd def envs(): - ''' + """ Return the file server environments - ''' - return sorted(__opts__['file_roots']) + """ + return sorted(__opts__["file_roots"]) def serve_file(load, fnd): - ''' + """ Return a chunk from a file based on the data received - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - ret = {'data': '', - 'dest': ''} - if 'path' not in load or 'loc' not in load or 'saltenv' not in load: + ret = {"data": "", "dest": ""} + if "path" not in load or "loc" not in load or "saltenv" not in load: return ret - if not fnd['path']: + if not fnd["path"]: return ret - ret['dest'] = fnd['rel'] - gzip = load.get('gzip', None) - fpath = os.path.normpath(fnd['path']) - with salt.utils.files.fopen(fpath, 'rb') as fp_: - fp_.seek(load['loc']) - data = fp_.read(__opts__['file_buffer_size']) + ret["dest"] = fnd["rel"] + gzip = load.get("gzip", None) + fpath = os.path.normpath(fnd["path"]) + with salt.utils.files.fopen(fpath, "rb") as fp_: + fp_.seek(load["loc"]) + data = fp_.read(__opts__["file_buffer_size"]) if gzip and data: data = salt.utils.gzip_util.compress(data, gzip) - ret['gzip'] = gzip - ret['data'] = data + ret["gzip"] = gzip + ret["data"] = data return ret def update(): - ''' + """ When we are asked to update (regular interval) lets reap the cache - ''' + """ try: salt.fileserver.reap_fileserver_cache_dir( - os.path.join(__opts__['cachedir'], 'roots', 'hash'), - find_file + os.path.join(__opts__["cachedir"], "roots", "hash"), find_file ) except (IOError, OSError): # Hash file won't exist if no files have yet been served up pass - mtime_map_path = os.path.join(__opts__['cachedir'], 'roots', 'mtime_map') + mtime_map_path = os.path.join(__opts__["cachedir"], "roots", "mtime_map") # data to send on event - data = {'changed': False, - 'files': {'changed': []}, - 'backend': 'roots'} + data = {"changed": False, "files": {"changed": []}, "backend": "roots"} # generate the new map - new_mtime_map = salt.fileserver.generate_mtime_map(__opts__, __opts__['file_roots']) + new_mtime_map = salt.fileserver.generate_mtime_map(__opts__, __opts__["file_roots"]) old_mtime_map = {} # if you have an old map, load that if os.path.exists(mtime_map_path): - with salt.utils.files.fopen(mtime_map_path, 'rb') as fp_: + with salt.utils.files.fopen(mtime_map_path, "rb") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) try: - file_path, mtime = line.replace('\n', '').split(':', 1) + file_path, mtime = line.replace("\n", "").split(":", 1) old_mtime_map[file_path] = mtime if mtime != new_mtime_map.get(file_path, mtime): - data['files']['changed'].append(file_path) + data["files"]["changed"].append(file_path) except ValueError: # Document the invalid entry in the log log.warning( - 'Skipped invalid cache mtime entry in %s: %s', - mtime_map_path, line + "Skipped invalid cache mtime entry in %s: %s", + mtime_map_path, + line, ) # compare the maps, set changed to the return value - data['changed'] = salt.fileserver.diff_mtime_map(old_mtime_map, new_mtime_map) + data["changed"] = salt.fileserver.diff_mtime_map(old_mtime_map, new_mtime_map) # compute files that were removed and added old_files = set(old_mtime_map.keys()) new_files = set(new_mtime_map.keys()) - data['files']['removed'] = list(old_files - new_files) - data['files']['added'] = list(new_files - old_files) + data["files"]["removed"] = list(old_files - new_files) + data["files"]["added"] = list(new_files - old_files) # write out the new map mtime_map_path_dir = os.path.dirname(mtime_map_path) if not os.path.exists(mtime_map_path_dir): os.makedirs(mtime_map_path_dir) - with salt.utils.files.fopen(mtime_map_path, 'wb') as fp_: + with salt.utils.files.fopen(mtime_map_path, "wb") as fp_: for file_path, mtime in six.iteritems(new_mtime_map): fp_.write( - salt.utils.stringutils.to_bytes( - '{0}:{1}\n'.format(file_path, mtime) - ) + salt.utils.stringutils.to_bytes("{0}:{1}\n".format(file_path, mtime)) ) - if __opts__.get('fileserver_events', False): + if __opts__.get("fileserver_events", False): # if there is a change, fire an event with salt.utils.event.get_event( - 'master', - __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=False) as event: + "master", + __opts__["sock_dir"], + __opts__["transport"], + opts=__opts__, + listen=False, + ) as event: event.fire_event( - data, - salt.utils.event.tagify(['roots', 'update'], prefix='fileserver')) + data, salt.utils.event.tagify(["roots", "update"], prefix="fileserver") + ) def file_hash(load, fnd): - ''' + """ Return a file hash, the hash type is set in the master config file - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if 'path' not in load or 'saltenv' not in load: - return '' - path = fnd['path'] - saltenv = load['saltenv'] - if saltenv not in __opts__['file_roots'] and '__env__' in __opts__['file_roots']: - saltenv = '__env__' + if "path" not in load or "saltenv" not in load: + return "" + path = fnd["path"] + saltenv = load["saltenv"] + if saltenv not in __opts__["file_roots"] and "__env__" in __opts__["file_roots"]: + saltenv = "__env__" ret = {} # if the file doesn't exist, we can't get a hash @@ -235,24 +233,29 @@ def file_hash(load, fnd): return ret # set the hash_type as it is determined by config-- so mechanism won't change that - ret['hash_type'] = __opts__['hash_type'] + ret["hash_type"] = __opts__["hash_type"] # check if the hash is cached # cache file's contents should be "hash:mtime" - cache_path = os.path.join(__opts__['cachedir'], - 'roots', - 'hash', - saltenv, - '{0}.hash.{1}'.format(fnd['rel'], - __opts__['hash_type'])) + cache_path = os.path.join( + __opts__["cachedir"], + "roots", + "hash", + saltenv, + "{0}.hash.{1}".format(fnd["rel"], __opts__["hash_type"]), + ) # if we have a cache, serve that if the mtime hasn't changed if os.path.exists(cache_path): try: - with salt.utils.files.fopen(cache_path, 'rb') as fp_: + with salt.utils.files.fopen(cache_path, "rb") as fp_: try: - hsum, mtime = salt.utils.stringutils.to_unicode(fp_.read()).split(':') + hsum, mtime = salt.utils.stringutils.to_unicode(fp_.read()).split( + ":" + ) except ValueError: - log.debug('Fileserver attempted to read incomplete cache file. Retrying.') + log.debug( + "Fileserver attempted to read incomplete cache file. Retrying." + ) # Delete the file since its incomplete (either corrupted or incomplete) try: os.unlink(cache_path) @@ -261,9 +264,12 @@ def file_hash(load, fnd): return file_hash(load, fnd) if str(os.path.getmtime(path)) == mtime: # check if mtime changed - ret['hsum'] = hsum + ret["hsum"] = hsum return ret - except (os.error, IOError): # Can't use Python select() because we need Windows support + except ( + os.error, + IOError, + ): # Can't use Python select() because we need Windows support log.debug("Fileserver encountered lock when reading cache file. Retrying.") # Delete the file since its incomplete (either corrupted or incomplete) try: @@ -273,7 +279,7 @@ def file_hash(load, fnd): return file_hash(load, fnd) # if we don't have a cache entry-- lets make one - ret['hsum'] = salt.utils.hashutils.get_hash(path, __opts__['hash_type']) + ret["hsum"] = salt.utils.hashutils.get_hash(path, __opts__["hash_type"]) cache_dir = os.path.dirname(cache_path) # make cache directory if it doesn't exist if not os.path.exists(cache_dir): @@ -287,79 +293,79 @@ def file_hash(load, fnd): else: raise # save the cache object "hash:mtime" - cache_object = '{0}:{1}'.format(ret['hsum'], os.path.getmtime(path)) - with salt.utils.files.flopen(cache_path, 'w') as fp_: + cache_object = "{0}:{1}".format(ret["hsum"], os.path.getmtime(path)) + with salt.utils.files.flopen(cache_path, "w") as fp_: fp_.write(cache_object) return ret def _file_lists(load, form): - ''' + """ Return a dict containing the file lists for files, dirs, emtydirs and symlinks - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - saltenv = load['saltenv'] - if saltenv not in __opts__['file_roots']: - if '__env__' in __opts__['file_roots']: - log.debug("salt environment '%s' maps to __env__ file_roots directory", saltenv) - saltenv = '__env__' + saltenv = load["saltenv"] + if saltenv not in __opts__["file_roots"]: + if "__env__" in __opts__["file_roots"]: + log.debug( + "salt environment '%s' maps to __env__ file_roots directory", saltenv + ) + saltenv = "__env__" else: return [] - list_cachedir = os.path.join(__opts__['cachedir'], 'file_lists', 'roots') + list_cachedir = os.path.join(__opts__["cachedir"], "file_lists", "roots") if not os.path.isdir(list_cachedir): try: os.makedirs(list_cachedir) except os.error: - log.critical('Unable to make cachedir %s', list_cachedir) + log.critical("Unable to make cachedir %s", list_cachedir) return [] - list_cache = os.path.join(list_cachedir, '{0}.p'.format(salt.utils.files.safe_filename_leaf(saltenv))) - w_lock = os.path.join(list_cachedir, '.{0}.w'.format(salt.utils.files.safe_filename_leaf(saltenv))) - cache_match, refresh_cache, save_cache = \ - salt.fileserver.check_file_list_cache( - __opts__, form, list_cache, w_lock - ) + list_cache = os.path.join( + list_cachedir, "{0}.p".format(salt.utils.files.safe_filename_leaf(saltenv)) + ) + w_lock = os.path.join( + list_cachedir, ".{0}.w".format(salt.utils.files.safe_filename_leaf(saltenv)) + ) + cache_match, refresh_cache, save_cache = salt.fileserver.check_file_list_cache( + __opts__, form, list_cache, w_lock + ) if cache_match is not None: return cache_match if refresh_cache: - ret = { - 'files': set(), - 'dirs': set(), - 'empty_dirs': set(), - 'links': {} - } + ret = {"files": set(), "dirs": set(), "empty_dirs": set(), "links": {}} def _add_to(tgt, fs_root, parent_dir, items): - ''' + """ Add the files to the target set - ''' + """ + def _translate_sep(path): - ''' + """ Translate path separators for Windows masterless minions - ''' - return path.replace('\\', '/') if os.path.sep == '\\' else path + """ + return path.replace("\\", "/") if os.path.sep == "\\" else path for item in items: abs_path = os.path.join(parent_dir, item) - log.trace('roots: Processing %s', abs_path) + log.trace("roots: Processing %s", abs_path) is_link = salt.utils.path.islink(abs_path) log.trace( - 'roots: %s is %sa link', - abs_path, 'not ' if not is_link else '' + "roots: %s is %sa link", abs_path, "not " if not is_link else "" ) - if is_link and __opts__['fileserver_ignoresymlinks']: + if is_link and __opts__["fileserver_ignoresymlinks"]: continue rel_path = _translate_sep(os.path.relpath(abs_path, fs_root)) - log.trace('roots: %s relative path is %s', abs_path, rel_path) + log.trace("roots: %s relative path is %s", abs_path, rel_path) if salt.fileserver.is_file_ignored(__opts__, rel_path): continue tgt.add(rel_path) try: if not os.listdir(abs_path): - ret['empty_dirs'].add(rel_path) + ret["empty_dirs"].add(rel_path) except Exception: # pylint: disable=broad-except # Generic exception because running os.listdir() on a # non-directory path raises an OSError on *NIX and a @@ -368,57 +374,51 @@ def _file_lists(load, form): if is_link: link_dest = salt.utils.path.readlink(abs_path) log.trace( - 'roots: %s symlink destination is %s', - abs_path, link_dest + "roots: %s symlink destination is %s", abs_path, link_dest ) - if salt.utils.platform.is_windows() \ - and link_dest.startswith('\\\\'): + if salt.utils.platform.is_windows() and link_dest.startswith( + "\\\\" + ): # Symlink points to a network path. Since you can't # join UNC and non-UNC paths, just assume the original # path. log.trace( - 'roots: %s is a UNC path, using %s instead', - link_dest, abs_path + "roots: %s is a UNC path, using %s instead", + link_dest, + abs_path, ) link_dest = abs_path - if link_dest.startswith('..'): + if link_dest.startswith(".."): joined = os.path.join(abs_path, link_dest) else: - joined = os.path.join( - os.path.dirname(abs_path), link_dest - ) + joined = os.path.join(os.path.dirname(abs_path), link_dest) rel_dest = _translate_sep( os.path.relpath( os.path.realpath(os.path.normpath(joined)), - os.path.realpath(fs_root) + os.path.realpath(fs_root), ) ) - log.trace( - 'roots: %s relative path is %s', - abs_path, rel_dest - ) - if not rel_dest.startswith('..'): + log.trace("roots: %s relative path is %s", abs_path, rel_dest) + if not rel_dest.startswith(".."): # Only count the link if it does not point # outside of the root dir of the fileserver # (i.e. the "path" variable) - ret['links'][rel_path] = link_dest + ret["links"][rel_path] = link_dest - for path in __opts__['file_roots'][saltenv]: + for path in __opts__["file_roots"][saltenv]: for root, dirs, files in salt.utils.path.os_walk( - path, - followlinks=__opts__['fileserver_followsymlinks']): - _add_to(ret['dirs'], path, root, dirs) - _add_to(ret['files'], path, root, files) + path, followlinks=__opts__["fileserver_followsymlinks"] + ): + _add_to(ret["dirs"], path, root, dirs) + _add_to(ret["files"], path, root, files) - ret['files'] = sorted(ret['files']) - ret['dirs'] = sorted(ret['dirs']) - ret['empty_dirs'] = sorted(ret['empty_dirs']) + ret["files"] = sorted(ret["files"]) + ret["dirs"] = sorted(ret["dirs"]) + ret["empty_dirs"] = sorted(ret["empty_dirs"]) if save_cache: try: - salt.fileserver.write_file_list_cache( - __opts__, ret, list_cache, w_lock - ) + salt.fileserver.write_file_list_cache(__opts__, ret, list_cache, w_lock) except NameError: # Catch msgpack error in salt-ssh pass @@ -428,45 +428,48 @@ def _file_lists(load, form): def file_list(load): - ''' + """ Return a list of all files on the file server in a specified environment - ''' - return _file_lists(load, 'files') + """ + return _file_lists(load, "files") def file_list_emptydirs(load): - ''' + """ Return a list of all empty directories on the master - ''' - return _file_lists(load, 'empty_dirs') + """ + return _file_lists(load, "empty_dirs") def dir_list(load): - ''' + """ Return a list of all directories on the master - ''' - return _file_lists(load, 'dirs') + """ + return _file_lists(load, "dirs") def symlink_list(load): - ''' + """ Return a dict of all symlinks based on a given path on the Master - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") ret = {} - if load['saltenv'] not in __opts__['file_roots'] and '__env__' not in __opts__['file_roots']: + if ( + load["saltenv"] not in __opts__["file_roots"] + and "__env__" not in __opts__["file_roots"] + ): return ret - if 'prefix' in load: - prefix = load['prefix'].strip('/') + if "prefix" in load: + prefix = load["prefix"].strip("/") else: - prefix = '' + prefix = "" - symlinks = _file_lists(load, 'links') - return dict([(key, val) - for key, val in six.iteritems(symlinks) - if key.startswith(prefix)]) + symlinks = _file_lists(load, "links") + return dict( + [(key, val) for key, val in six.iteritems(symlinks) if key.startswith(prefix)] + ) diff --git a/salt/fileserver/s3fs.py b/salt/fileserver/s3fs.py index 09f1b4e227e..6b94b0c4d32 100644 --- a/salt/fileserver/s3fs.py +++ b/salt/fileserver/s3fs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Amazon S3 Fileserver Backend .. versionadded:: 0.16.0 @@ -76,15 +76,16 @@ structure:: More info here: https://docs.aws.amazon.com/cli/latest/topic/s3-config.html -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import datetime -import os -import time -import pickle import logging +import os +import pickle +import time # Import salt libs import salt.fileserver as fs @@ -99,6 +100,7 @@ import salt.utils.versions from salt.ext import six from salt.ext.six.moves import filter from salt.ext.six.moves.urllib.parse import quote as _quote + # pylint: enable=import-error,no-name-in-module,redefined-builtin log = logging.getLogger(__name__) @@ -108,10 +110,10 @@ S3_SYNC_ON_UPDATE = True # sync cache on update rather than jit def envs(): - ''' + """ Return a list of directories within the bucket that can be used as environments. - ''' + """ # update and grab the envs from the metadata keys metadata = _init() @@ -119,40 +121,43 @@ def envs(): def update(): - ''' + """ Update the cache file for the bucket. - ''' + """ metadata = _init() if S3_SYNC_ON_UPDATE: # sync the buckets to the local cache - log.info('Syncing local cache from S3...') + log.info("Syncing local cache from S3...") for saltenv, env_meta in six.iteritems(metadata): for bucket_files in _find_files(env_meta): for bucket, files in six.iteritems(bucket_files): for file_path in files: - cached_file_path = _get_cached_file_name(bucket, saltenv, file_path) - log.info('%s - %s : %s', bucket, saltenv, file_path) + cached_file_path = _get_cached_file_name( + bucket, saltenv, file_path + ) + log.info("%s - %s : %s", bucket, saltenv, file_path) # load the file from S3 if it's not in the cache or it's old - _get_file_from_s3(metadata, saltenv, bucket, file_path, cached_file_path) + _get_file_from_s3( + metadata, saltenv, bucket, file_path, cached_file_path + ) - log.info('Sync local cache from S3 completed.') + log.info("Sync local cache from S3 completed.") -def find_file(path, saltenv='base', **kwargs): - ''' +def find_file(path, saltenv="base", **kwargs): + """ Look through the buckets cache file for a match. If the field is found, it is retrieved from S3 only if its cached version is missing, or if the MD5 does not match. - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") - fnd = {'bucket': None, - 'path': None} + fnd = {"bucket": None, "path": None} metadata = _init() if not metadata or saltenv not in metadata: @@ -167,105 +172,102 @@ def find_file(path, saltenv='base', **kwargs): for bucket in env_files: for bucket_name, files in six.iteritems(bucket): if path in files and not fs.is_file_ignored(__opts__, path): - fnd['bucket'] = bucket_name - fnd['path'] = path + fnd["bucket"] = bucket_name + fnd["path"] = path break else: continue # only executes if we didn't break break - if not fnd['path'] or not fnd['bucket']: + if not fnd["path"] or not fnd["bucket"]: return fnd - cached_file_path = _get_cached_file_name(fnd['bucket'], saltenv, path) + cached_file_path = _get_cached_file_name(fnd["bucket"], saltenv, path) # jit load the file from S3 if it's not in the cache or it's old - _get_file_from_s3(metadata, saltenv, fnd['bucket'], path, cached_file_path) + _get_file_from_s3(metadata, saltenv, fnd["bucket"], path, cached_file_path) return fnd def file_hash(load, fnd): - ''' + """ Return an MD5 file hash - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") ret = {} - if 'saltenv' not in load: + if "saltenv" not in load: return ret - if 'path' not in fnd or 'bucket' not in fnd or not fnd['path']: + if "path" not in fnd or "bucket" not in fnd or not fnd["path"]: return ret cached_file_path = _get_cached_file_name( - fnd['bucket'], - load['saltenv'], - fnd['path']) + fnd["bucket"], load["saltenv"], fnd["path"] + ) if os.path.isfile(cached_file_path): - ret['hsum'] = salt.utils.hashutils.get_hash(cached_file_path) - ret['hash_type'] = 'md5' + ret["hsum"] = salt.utils.hashutils.get_hash(cached_file_path) + ret["hash_type"] = "md5" return ret def serve_file(load, fnd): - ''' + """ Return a chunk from a file based on the data received - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - ret = {'data': '', - 'dest': ''} + ret = {"data": "", "dest": ""} - if 'path' not in load or 'loc' not in load or 'saltenv' not in load: + if "path" not in load or "loc" not in load or "saltenv" not in load: return ret - if 'path' not in fnd or 'bucket' not in fnd: + if "path" not in fnd or "bucket" not in fnd: return ret - gzip = load.get('gzip', None) + gzip = load.get("gzip", None) # get the saltenv/path file from the cache cached_file_path = _get_cached_file_name( - fnd['bucket'], - load['saltenv'], - fnd['path']) + fnd["bucket"], load["saltenv"], fnd["path"] + ) - ret['dest'] = _trim_env_off_path([fnd['path']], load['saltenv'])[0] + ret["dest"] = _trim_env_off_path([fnd["path"]], load["saltenv"])[0] - with salt.utils.files.fopen(cached_file_path, 'rb') as fp_: - fp_.seek(load['loc']) - data = fp_.read(__opts__['file_buffer_size']) + with salt.utils.files.fopen(cached_file_path, "rb") as fp_: + fp_.seek(load["loc"]) + data = fp_.read(__opts__["file_buffer_size"]) if data and six.PY3 and not salt.utils.files.is_binary(cached_file_path): data = data.decode(__salt_system_encoding__) if gzip and data: data = salt.utils.gzip_util.compress(data, gzip) - ret['gzip'] = gzip - ret['data'] = data + ret["gzip"] = gzip + ret["data"] = data return ret def file_list(load): - ''' + """ Return a list of all files on the file server in a specified environment - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") ret = [] - if 'saltenv' not in load: + if "saltenv" not in load: return ret - saltenv = load['saltenv'] + saltenv = load["saltenv"] metadata = _init() if not metadata or saltenv not in metadata: @@ -279,9 +281,9 @@ def file_list(load): def file_list_emptydirs(load): - ''' + """ Return a list of all empty directories on the master - ''' + """ # TODO - implement this _init() @@ -289,19 +291,19 @@ def file_list_emptydirs(load): def dir_list(load): - ''' + """ Return a list of all directories on the master - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") ret = [] - if 'saltenv' not in load: + if "saltenv" not in load: return ret - saltenv = load['saltenv'] + saltenv = load["saltenv"] metadata = _init() if not metadata or saltenv not in metadata: @@ -319,37 +321,38 @@ def dir_list(load): def _get_s3_key(): - ''' + """ Get AWS keys from pillar or config - ''' + """ - key = __opts__['s3.key'] if 's3.key' in __opts__ else None - keyid = __opts__['s3.keyid'] if 's3.keyid' in __opts__ else None - service_url = __opts__['s3.service_url'] \ - if 's3.service_url' in __opts__ \ - else None - verify_ssl = __opts__['s3.verify_ssl'] \ - if 's3.verify_ssl' in __opts__ \ - else None - kms_keyid = __opts__['aws.kmw.keyid'] if 'aws.kms.keyid' in __opts__ else None - location = __opts__['s3.location'] \ - if 's3.location' in __opts__ \ - else None - path_style = __opts__['s3.path_style'] \ - if 's3.path_style' in __opts__ \ - else None - https_enable = __opts__['s3.https_enable'] \ - if 's3.https_enable' in __opts__ \ - else None + key = __opts__["s3.key"] if "s3.key" in __opts__ else None + keyid = __opts__["s3.keyid"] if "s3.keyid" in __opts__ else None + service_url = __opts__["s3.service_url"] if "s3.service_url" in __opts__ else None + verify_ssl = __opts__["s3.verify_ssl"] if "s3.verify_ssl" in __opts__ else None + kms_keyid = __opts__["aws.kmw.keyid"] if "aws.kms.keyid" in __opts__ else None + location = __opts__["s3.location"] if "s3.location" in __opts__ else None + path_style = __opts__["s3.path_style"] if "s3.path_style" in __opts__ else None + https_enable = ( + __opts__["s3.https_enable"] if "s3.https_enable" in __opts__ else None + ) - return key, keyid, service_url, verify_ssl, kms_keyid, location, path_style, https_enable + return ( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + path_style, + https_enable, + ) def _init(): - ''' + """ Connect to S3 and download the metadata for each file in all buckets specified and cache the data to disk. - ''' + """ cache_file = _get_buckets_cache_filename() exp = time.time() - S3_CACHE_EXPIRE @@ -369,18 +372,18 @@ def _init(): def _get_cache_dir(): - ''' + """ Return the path to the s3cache dir - ''' + """ # Or is that making too many assumptions? - return os.path.join(__opts__['cachedir'], 's3cache') + return os.path.join(__opts__["cachedir"], "s3cache") def _get_cached_file_name(bucket_name, saltenv, path): - ''' + """ Return the cached file name for a bucket path file - ''' + """ file_path = os.path.join(_get_cache_dir(), saltenv, bucket_name, path) @@ -392,53 +395,66 @@ def _get_cached_file_name(bucket_name, saltenv, path): def _get_buckets_cache_filename(): - ''' + """ Return the filename of the cache for bucket contents. Create the path if it does not exist. - ''' + """ cache_dir = _get_cache_dir() if not os.path.exists(cache_dir): os.makedirs(cache_dir) - return os.path.join(cache_dir, 'buckets_files.cache') + return os.path.join(cache_dir, "buckets_files.cache") def _refresh_buckets_cache_file(cache_file): - ''' + """ Retrieve the content of all buckets and cache the metadata to the buckets cache file - ''' + """ - log.debug('Refreshing buckets cache file') + log.debug("Refreshing buckets cache file") - key, keyid, service_url, verify_ssl, kms_keyid, location, path_style, https_enable = _get_s3_key() + ( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + path_style, + https_enable, + ) = _get_s3_key() metadata = {} # helper s3 query function def __get_s3_meta(bucket, key=key, keyid=keyid): - ret, marker = [], '' + ret, marker = [], "" while True: - tmp = __utils__['s3.query'](key=key, - keyid=keyid, - kms_keyid=keyid, - bucket=bucket, - service_url=service_url, - verify_ssl=verify_ssl, - location=location, - return_bin=False, - path_style=path_style, - https_enable=https_enable, - params={'marker': marker}) + tmp = __utils__["s3.query"]( + key=key, + keyid=keyid, + kms_keyid=keyid, + bucket=bucket, + service_url=service_url, + verify_ssl=verify_ssl, + location=location, + return_bin=False, + path_style=path_style, + https_enable=https_enable, + params={"marker": marker}, + ) headers = [] for header in tmp: - if 'Key' in header: + if "Key" in header: break headers.append(header) ret.extend(tmp) - if all([header.get('IsTruncated', 'false') == 'false' for header in headers]): + if all( + [header.get("IsTruncated", "false") == "false" for header in headers] + ): break - marker = tmp[-1]['Key'] + marker = tmp[-1]["Key"] return ret if _is_env_per_bucket(): @@ -454,32 +470,37 @@ def _refresh_buckets_cache_file(cache_file): continue # grab only the files/dirs - bucket_files[bucket_name] = [k for k in s3_meta if 'Key' in k] + bucket_files[bucket_name] = [k for k in s3_meta if "Key" in k] bucket_files_list.append(bucket_files) # check to see if we added any keys, otherwise investigate possible error conditions if not bucket_files[bucket_name]: meta_response = {} for k in s3_meta: - if 'Code' in k or 'Message' in k: + if "Code" in k or "Message" in k: # assumes no duplicate keys, consisdent with current erro response. meta_response.update(k) # attempt use of human readable output first. try: - log.warning("'%s' response for bucket '%s'", meta_response['Message'], bucket_name) + log.warning( + "'%s' response for bucket '%s'", + meta_response["Message"], + bucket_name, + ) continue except KeyError: # no human readable error message provided - if 'Code' in meta_response: + if "Code" in meta_response: log.warning( "'%s' response for bucket '%s'", - meta_response['Code'], bucket_name + meta_response["Code"], + bucket_name, ) continue else: log.warning( - 'S3 Error! Do you have any files ' - 'in your S3 bucket?') + "S3 Error! Do you have any files " "in your S3 bucket?" + ) return {} metadata[saltenv] = bucket_files_list @@ -494,40 +515,45 @@ def _refresh_buckets_cache_file(cache_file): continue # pull out the environment dirs (e.g. the root dirs) - files = [k for k in s3_meta if 'Key' in k] + files = [k for k in s3_meta if "Key" in k] # check to see if we added any keys, otherwise investigate possible error conditions if not files: meta_response = {} for k in s3_meta: - if 'Code' in k or 'Message' in k: + if "Code" in k or "Message" in k: # assumes no duplicate keys, consisdent with current erro response. meta_response.update(k) # attempt use of human readable output first. try: - log.warning("'%s' response for bucket '%s'", meta_response['Message'], bucket_name) + log.warning( + "'%s' response for bucket '%s'", + meta_response["Message"], + bucket_name, + ) continue except KeyError: # no human readable error message provided - if 'Code' in meta_response: + if "Code" in meta_response: log.warning( "'%s' response for bucket '%s'", - meta_response['Code'], bucket_name + meta_response["Code"], + bucket_name, ) continue else: log.warning( - 'S3 Error! Do you have any files ' - 'in your S3 bucket?') + "S3 Error! Do you have any files " "in your S3 bucket?" + ) return {} - environments = [(os.path.dirname(k['Key']).split('/', 1))[0] for k in files] + environments = [(os.path.dirname(k["Key"]).split("/", 1))[0] for k in files] environments = set(environments) # pull out the files for the environment for saltenv in environments: # grab only files/dirs that match this saltenv - env_files = [k for k in files if k['Key'].startswith(saltenv)] + env_files = [k for k in files if k["Key"].startswith(saltenv)] if saltenv not in metadata: metadata[saltenv] = [] @@ -545,43 +571,49 @@ def _refresh_buckets_cache_file(cache_file): if os.path.isfile(cache_file): os.remove(cache_file) - log.debug('Writing buckets cache file') + log.debug("Writing buckets cache file") - with salt.utils.files.fopen(cache_file, 'w') as fp_: + with salt.utils.files.fopen(cache_file, "w") as fp_: pickle.dump(metadata, fp_) return metadata def _read_buckets_cache_file(cache_file): - ''' + """ Return the contents of the buckets cache file - ''' + """ - log.debug('Reading buckets cache file') + log.debug("Reading buckets cache file") - with salt.utils.files.fopen(cache_file, 'rb') as fp_: + with salt.utils.files.fopen(cache_file, "rb") as fp_: try: data = pickle.load(fp_) - except (pickle.UnpicklingError, AttributeError, EOFError, ImportError, - IndexError, KeyError): + except ( + pickle.UnpicklingError, + AttributeError, + EOFError, + ImportError, + IndexError, + KeyError, + ): data = None return data def _find_files(metadata): - ''' + """ Looks for all the files in the S3 bucket cache metadata - ''' + """ ret = [] found = {} for bucket_dict in metadata: for bucket_name, data in six.iteritems(bucket_dict): - filepaths = [k['Key'] for k in data] - filepaths = [k for k in filepaths if not k.endswith('/')] + filepaths = [k["Key"] for k in data] + filepaths = [k for k in filepaths if not k.endswith("/")] if bucket_name not in found: found[bucket_name] = True ret.append({bucket_name: filepaths}) @@ -594,12 +626,12 @@ def _find_files(metadata): def _find_dirs(metadata): - ''' + """ Looks for all the directories in the S3 bucket cache metadata. Supports trailing '/' keys (as created by S3 console) as well as directories discovered in the path of file keys. - ''' + """ ret = [] found = {} @@ -607,10 +639,10 @@ def _find_dirs(metadata): for bucket_dict in metadata: for bucket_name, data in six.iteritems(bucket_dict): dirpaths = set() - for path in [k['Key'] for k in data]: - prefix = '' - for part in path.split('/')[:-1]: - directory = prefix + part + '/' + for path in [k["Key"] for k in data]: + prefix = "" + for part in path.split("/")[:-1]: + directory = prefix + part + "/" dirpaths.add(directory) prefix = directory if bucket_name not in found: @@ -626,50 +658,59 @@ def _find_dirs(metadata): def _find_file_meta(metadata, bucket_name, saltenv, path): - ''' + """ Looks for a file's metadata in the S3 bucket cache file - ''' + """ env_meta = metadata[saltenv] if saltenv in metadata else {} bucket_meta = {} for bucket in env_meta: if bucket_name in bucket: bucket_meta = bucket[bucket_name] - files_meta = list(list(filter((lambda k: 'Key' in k), bucket_meta))) + files_meta = list(list(filter((lambda k: "Key" in k), bucket_meta))) for item_meta in files_meta: - if 'Key' in item_meta and item_meta['Key'] == path: + if "Key" in item_meta and item_meta["Key"] == path: try: # Get rid of quotes surrounding md5 - item_meta['ETag'] = item_meta['ETag'].strip('"') + item_meta["ETag"] = item_meta["ETag"].strip('"') except KeyError: pass return item_meta def _get_buckets(): - ''' + """ Return the configuration buckets - ''' + """ - return __opts__['s3.buckets'] if 's3.buckets' in __opts__ else {} + return __opts__["s3.buckets"] if "s3.buckets" in __opts__ else {} def _get_file_from_s3(metadata, saltenv, bucket_name, path, cached_file_path): - ''' + """ Checks the local cache for the file, if it's old or missing go grab the file from S3 and update the cache - ''' - key, keyid, service_url, verify_ssl, kms_keyid, location, path_style, https_enable = _get_s3_key() + """ + ( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + path_style, + https_enable, + ) = _get_s3_key() # check the local cache... if os.path.isfile(cached_file_path): file_meta = _find_file_meta(metadata, bucket_name, saltenv, path) if file_meta: - file_etag = file_meta['ETag'] + file_etag = file_meta["ETag"] - if file_etag.find('-') == -1: + if file_etag.find("-") == -1: file_md5 = file_etag - cached_md5 = salt.utils.hashutils.get_hash(cached_file_path, 'md5') + cached_md5 = salt.utils.hashutils.get_hash(cached_file_path, "md5") # hashes match we have a cache hit if cached_md5 == file_md5: @@ -678,20 +719,26 @@ def _get_file_from_s3(metadata, saltenv, bucket_name, path, cached_file_path): cached_file_stat = os.stat(cached_file_path) cached_file_size = cached_file_stat.st_size cached_file_mtime = datetime.datetime.fromtimestamp( - cached_file_stat.st_mtime) + cached_file_stat.st_mtime + ) cached_file_lastmod = datetime.datetime.strptime( - file_meta['LastModified'], '%Y-%m-%dT%H:%M:%S.%fZ') - if (cached_file_size == int(file_meta['Size']) and - cached_file_mtime > cached_file_lastmod): - log.debug('cached file size equal to metadata size and ' - 'cached file mtime later than metadata last ' - 'modification time.') - ret = __utils__['s3.query']( + file_meta["LastModified"], "%Y-%m-%dT%H:%M:%S.%fZ" + ) + if ( + cached_file_size == int(file_meta["Size"]) + and cached_file_mtime > cached_file_lastmod + ): + log.debug( + "cached file size equal to metadata size and " + "cached file mtime later than metadata last " + "modification time." + ) + ret = __utils__["s3.query"]( key=key, keyid=keyid, kms_keyid=keyid, - method='HEAD', + method="HEAD", bucket=bucket_name, service_url=service_url, verify_ssl=verify_ssl, @@ -700,28 +747,33 @@ def _get_file_from_s3(metadata, saltenv, bucket_name, path, cached_file_path): local_file=cached_file_path, full_headers=True, path_style=path_style, - https_enable=https_enable + https_enable=https_enable, ) if ret is not None: - for header_name, header_value in ret['headers'].items(): + for header_name, header_value in ret["headers"].items(): name = header_name.strip() value = header_value.strip() - if six.text_type(name).lower() == 'last-modified': + if six.text_type(name).lower() == "last-modified": s3_file_mtime = datetime.datetime.strptime( - value, '%a, %d %b %Y %H:%M:%S %Z') - elif six.text_type(name).lower() == 'content-length': + value, "%a, %d %b %Y %H:%M:%S %Z" + ) + elif six.text_type(name).lower() == "content-length": s3_file_size = int(value) - if (cached_file_size == s3_file_size and - cached_file_mtime > s3_file_mtime): + if ( + cached_file_size == s3_file_size + and cached_file_mtime > s3_file_mtime + ): log.info( - '%s - %s : %s skipped download since cached file size ' - 'equal to and mtime after s3 values', - bucket_name, saltenv, path + "%s - %s : %s skipped download since cached file size " + "equal to and mtime after s3 values", + bucket_name, + saltenv, + path, ) return # ... or get the file from S3 - __utils__['s3.query']( + __utils__["s3.query"]( key=key, keyid=keyid, kms_keyid=keyid, @@ -737,9 +789,9 @@ def _get_file_from_s3(metadata, saltenv, bucket_name, path, cached_file_path): def _trim_env_off_path(paths, saltenv, trim_slash=False): - ''' + """ Return a list of file paths with the saltenv directory removed - ''' + """ env_len = None if _is_env_per_bucket() else len(saltenv) + 1 slash_len = -1 if trim_slash else None @@ -747,10 +799,10 @@ def _trim_env_off_path(paths, saltenv, trim_slash=False): def _is_env_per_bucket(): - ''' + """ Return the configuration mode, either buckets per environment or a list of buckets that have environment dirs in their root - ''' + """ buckets = _get_buckets() if isinstance(buckets, dict): @@ -758,4 +810,4 @@ def _is_env_per_bucket(): elif isinstance(buckets, list): return False else: - raise ValueError('Incorrect s3.buckets type given in config') + raise ValueError("Incorrect s3.buckets type given in config") diff --git a/salt/fileserver/svnfs.py b/salt/fileserver/svnfs.py index 19de8a844c1..fec88572a8d 100644 --- a/salt/fileserver/svnfs.py +++ b/salt/fileserver/svnfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Subversion Fileserver Backend After enabling this backend, branches and tags in a remote subversion @@ -29,10 +29,11 @@ This backend assumes a standard svn layout with directories for ``branches``, :conf_master:`svnfs_mountpoint` was also added. Finally, support for per-remote configuration parameters was added. See the :conf_master:`documentation ` for more information. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import errno import fnmatch @@ -41,21 +42,8 @@ import logging import os import shutil from datetime import datetime -from salt.exceptions import FileserverConfigError -PER_REMOTE_OVERRIDES = ('mountpoint', 'root', 'trunk', 'branches', 'tags') - -# Import third party libs -from salt.ext import six -# pylint: disable=import-error -HAS_SVN = False -try: - import pysvn - HAS_SVN = True - CLIENT = pysvn.Client() -except ImportError: - pass -# pylint: enable=import-error +import salt.fileserver # Import salt libs import salt.utils.data @@ -66,105 +54,128 @@ import salt.utils.path import salt.utils.stringutils import salt.utils.url import salt.utils.versions -import salt.fileserver +from salt.exceptions import FileserverConfigError + +# Import third party libs +from salt.ext import six from salt.utils.event import tagify +PER_REMOTE_OVERRIDES = ("mountpoint", "root", "trunk", "branches", "tags") + + +# pylint: disable=import-error +HAS_SVN = False +try: + import pysvn + + HAS_SVN = True + CLIENT = pysvn.Client() +except ImportError: + pass +# pylint: enable=import-error + + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'svn' +__virtualname__ = "svn" def __virtual__(): - ''' + """ Only load if subversion is available - ''' - if __virtualname__ not in __opts__['fileserver_backend']: + """ + if __virtualname__ not in __opts__["fileserver_backend"]: return False if not HAS_SVN: - log.error('Subversion fileserver backend is enabled in configuration ' - 'but could not be loaded, is pysvn installed?') + log.error( + "Subversion fileserver backend is enabled in configuration " + "but could not be loaded, is pysvn installed?" + ) return False errors = [] - for param in ('svnfs_trunk', 'svnfs_branches', 'svnfs_tags'): + for param in ("svnfs_trunk", "svnfs_branches", "svnfs_tags"): if os.path.isabs(__opts__[param]): errors.append( - 'Master configuration parameter \'{0}\' (value: {1}) cannot ' - 'be an absolute path'.format(param, __opts__[param]) + "Master configuration parameter '{0}' (value: {1}) cannot " + "be an absolute path".format(param, __opts__[param]) ) if errors: for error in errors: log.error(error) - log.error('Subversion fileserver backed will be disabled') + log.error("Subversion fileserver backed will be disabled") return False return __virtualname__ def _rev(repo): - ''' + """ Returns revision ID of repo - ''' + """ try: - repo_info = dict(six.iteritems(CLIENT.info(repo['repo']))) - except (pysvn._pysvn.ClientError, TypeError, - KeyError, AttributeError) as exc: + repo_info = dict(six.iteritems(CLIENT.info(repo["repo"]))) + except (pysvn._pysvn.ClientError, TypeError, KeyError, AttributeError) as exc: log.error( - 'Error retrieving revision ID for svnfs remote %s ' - '(cachedir: %s): %s', - repo['url'], repo['repo'], exc + "Error retrieving revision ID for svnfs remote %s " "(cachedir: %s): %s", + repo["url"], + repo["repo"], + exc, ) else: - return repo_info['revision'].number + return repo_info["revision"].number return None def _failhard(): - ''' + """ Fatal fileserver configuration issue, raise an exception - ''' - raise FileserverConfigError( - 'Failed to load svn fileserver backend' - ) + """ + raise FileserverConfigError("Failed to load svn fileserver backend") def init(): - ''' + """ Return the list of svn remotes and their configuration information - ''' - bp_ = os.path.join(__opts__['cachedir'], 'svnfs') + """ + bp_ = os.path.join(__opts__["cachedir"], "svnfs") new_remote = False repos = [] per_remote_defaults = {} for param in PER_REMOTE_OVERRIDES: - per_remote_defaults[param] = \ - six.text_type(__opts__['svnfs_{0}'.format(param)]) + per_remote_defaults[param] = six.text_type(__opts__["svnfs_{0}".format(param)]) - for remote in __opts__['svnfs_remotes']: + for remote in __opts__["svnfs_remotes"]: repo_conf = copy.deepcopy(per_remote_defaults) if isinstance(remote, dict): repo_url = next(iter(remote)) per_remote_conf = dict( - [(key, six.text_type(val)) for key, val in - six.iteritems(salt.utils.data.repack_dictlist(remote[repo_url]))] + [ + (key, six.text_type(val)) + for key, val in six.iteritems( + salt.utils.data.repack_dictlist(remote[repo_url]) + ) + ] ) if not per_remote_conf: log.error( - 'Invalid per-remote configuration for remote %s. If no ' - 'per-remote parameters are being specified, there may be ' - 'a trailing colon after the URL, which should be removed. ' - 'Check the master configuration file.', repo_url + "Invalid per-remote configuration for remote %s. If no " + "per-remote parameters are being specified, there may be " + "a trailing colon after the URL, which should be removed. " + "Check the master configuration file.", + repo_url, ) _failhard() per_remote_errors = False - for param in (x for x in per_remote_conf - if x not in PER_REMOTE_OVERRIDES): + for param in (x for x in per_remote_conf if x not in PER_REMOTE_OVERRIDES): log.error( - 'Invalid configuration parameter \'%s\' for remote %s. ' - 'Valid parameters are: %s. See the documentation for ' - 'further information.', - param, repo_url, ', '.join(PER_REMOTE_OVERRIDES) + "Invalid configuration parameter '%s' for remote %s. " + "Valid parameters are: %s. See the documentation for " + "further information.", + param, + repo_url, + ", ".join(PER_REMOTE_OVERRIDES), ) per_remote_errors = True if per_remote_errors: @@ -176,20 +187,21 @@ def init(): if not isinstance(repo_url, six.string_types): log.error( - 'Invalid svnfs remote %s. Remotes must be strings, you may ' - 'need to enclose the URL in quotes', repo_url + "Invalid svnfs remote %s. Remotes must be strings, you may " + "need to enclose the URL in quotes", + repo_url, ) _failhard() try: - repo_conf['mountpoint'] = salt.utils.url.strip_proto( - repo_conf['mountpoint'] + repo_conf["mountpoint"] = salt.utils.url.strip_proto( + repo_conf["mountpoint"] ) except TypeError: # mountpoint not specified pass - hash_type = getattr(hashlib, __opts__.get('hash_type', 'md5')) + hash_type = getattr(hashlib, __opts__.get("hash_type", "md5")) repo_hash = hash_type(repo_url).hexdigest() rp_ = os.path.join(bp_, repo_hash) if not os.path.isdir(rp_): @@ -202,10 +214,7 @@ def init(): repos.append(rp_) new_remote = True except pysvn._pysvn.ClientError as exc: - log.error( - 'Failed to initialize svnfs remote \'%s\': %s', - repo_url, exc - ) + log.error("Failed to initialize svnfs remote '%s': %s", repo_url, exc) _failhard() else: # Confirm that there is an svn checkout at the necessary path by @@ -214,49 +223,51 @@ def init(): CLIENT.status(rp_) except pysvn._pysvn.ClientError as exc: log.error( - 'Cache path %s (corresponding remote: %s) exists but is ' - 'not a valid subversion checkout. You will need to ' - 'manually delete this directory on the master to continue ' - 'to use this svnfs remote.', rp_, repo_url + "Cache path %s (corresponding remote: %s) exists but is " + "not a valid subversion checkout. You will need to " + "manually delete this directory on the master to continue " + "to use this svnfs remote.", + rp_, + repo_url, ) _failhard() - repo_conf.update({ - 'repo': rp_, - 'url': repo_url, - 'hash': repo_hash, - 'cachedir': rp_, - 'lockfile': os.path.join(rp_, 'update.lk') - }) + repo_conf.update( + { + "repo": rp_, + "url": repo_url, + "hash": repo_hash, + "cachedir": rp_, + "lockfile": os.path.join(rp_, "update.lk"), + } + ) repos.append(repo_conf) if new_remote: - remote_map = os.path.join(__opts__['cachedir'], 'svnfs/remote_map.txt') + remote_map = os.path.join(__opts__["cachedir"], "svnfs/remote_map.txt") try: - with salt.utils.files.fopen(remote_map, 'w+') as fp_: - timestamp = datetime.now().strftime('%d %b %Y %H:%M:%S.%f') - fp_.write('# svnfs_remote map as of {0}\n'.format(timestamp)) + with salt.utils.files.fopen(remote_map, "w+") as fp_: + timestamp = datetime.now().strftime("%d %b %Y %H:%M:%S.%f") + fp_.write("# svnfs_remote map as of {0}\n".format(timestamp)) for repo_conf in repos: fp_.write( salt.utils.stringutils.to_str( - '{0} = {1}\n'.format( - repo_conf['hash'], repo_conf['url'] - ) + "{0} = {1}\n".format(repo_conf["hash"], repo_conf["url"]) ) ) except OSError: pass else: - log.info('Wrote new svnfs_remote map to %s', remote_map) + log.info("Wrote new svnfs_remote map to %s", remote_map) return repos def _clear_old_remotes(): - ''' + """ Remove cache directories for remotes no longer configured - ''' - bp_ = os.path.join(__opts__['cachedir'], 'svnfs') + """ + bp_ = os.path.join(__opts__["cachedir"], "svnfs") try: cachedir_ls = os.listdir(bp_) except OSError: @@ -265,12 +276,12 @@ def _clear_old_remotes(): # Remove actively-used remotes from list for repo in repos: try: - cachedir_ls.remove(repo['hash']) + cachedir_ls.remove(repo["hash"]) except ValueError: pass to_remove = [] for item in cachedir_ls: - if item in ('hash', 'refs'): + if item in ("hash", "refs"): continue path = os.path.join(bp_, item) if os.path.isdir(path): @@ -282,65 +293,67 @@ def _clear_old_remotes(): shutil.rmtree(rdir) except OSError as exc: log.error( - 'Unable to remove old svnfs remote cachedir %s: %s', - rdir, exc + "Unable to remove old svnfs remote cachedir %s: %s", rdir, exc ) failed.append(rdir) else: - log.debug('svnfs removed old cachedir %s', rdir) + log.debug("svnfs removed old cachedir %s", rdir) for fdir in failed: to_remove.remove(fdir) return bool(to_remove), repos def clear_cache(): - ''' + """ Completely clear svnfs cache - ''' - fsb_cachedir = os.path.join(__opts__['cachedir'], 'svnfs') - list_cachedir = os.path.join(__opts__['cachedir'], 'file_lists/svnfs') + """ + fsb_cachedir = os.path.join(__opts__["cachedir"], "svnfs") + list_cachedir = os.path.join(__opts__["cachedir"], "file_lists/svnfs") errors = [] for rdir in (fsb_cachedir, list_cachedir): if os.path.exists(rdir): try: shutil.rmtree(rdir) except OSError as exc: - errors.append('Unable to delete {0}: {1}'.format(rdir, exc)) + errors.append("Unable to delete {0}: {1}".format(rdir, exc)) return errors def clear_lock(remote=None): - ''' + """ Clear update.lk ``remote`` can either be a dictionary containing repo configuration information, or a pattern. If the latter, then remotes for which the URL matches the pattern will be locked. - ''' + """ + def _do_clear_lock(repo): def _add_error(errlist, repo, exc): - msg = ('Unable to remove update lock for {0} ({1}): {2} ' - .format(repo['url'], repo['lockfile'], exc)) + msg = "Unable to remove update lock for {0} ({1}): {2} ".format( + repo["url"], repo["lockfile"], exc + ) log.debug(msg) errlist.append(msg) + success = [] failed = [] - if os.path.exists(repo['lockfile']): + if os.path.exists(repo["lockfile"]): try: - os.remove(repo['lockfile']) + os.remove(repo["lockfile"]) except OSError as exc: if exc.errno == errno.EISDIR: # Somehow this path is a directory. Should never happen # unless some wiseguy manually creates a directory at this # path, but just in case, handle it. try: - shutil.rmtree(repo['lockfile']) + shutil.rmtree(repo["lockfile"]) except OSError as exc: _add_error(failed, repo, exc) else: _add_error(failed, repo, exc) else: - msg = 'Removed lock for {0}'.format(repo['url']) + msg = "Removed lock for {0}".format(repo["url"]) log.debug(msg) success.append(msg) return success, failed @@ -353,11 +366,11 @@ def clear_lock(remote=None): for repo in init(): if remote: try: - if remote not in repo['url']: + if remote not in repo["url"]: continue except TypeError: # remote was non-string, try again - if six.text_type(remote) not in repo['url']: + if six.text_type(remote) not in repo["url"]: continue success, failed = _do_clear_lock(repo) cleared.extend(success) @@ -366,27 +379,29 @@ def clear_lock(remote=None): def lock(remote=None): - ''' + """ Place an update.lk ``remote`` can either be a dictionary containing repo configuration information, or a pattern. If the latter, then remotes for which the URL matches the pattern will be locked. - ''' + """ + def _do_lock(repo): success = [] failed = [] - if not os.path.exists(repo['lockfile']): + if not os.path.exists(repo["lockfile"]): try: - with salt.utils.files.fopen(repo['lockfile'], 'w+') as fp_: - fp_.write('') + with salt.utils.files.fopen(repo["lockfile"], "w+") as fp_: + fp_.write("") except (IOError, OSError) as exc: - msg = ('Unable to set update lock for {0} ({1}): {2} ' - .format(repo['url'], repo['lockfile'], exc)) + msg = "Unable to set update lock for {0} ({1}): {2} ".format( + repo["url"], repo["lockfile"], exc + ) log.debug(msg) failed.append(msg) else: - msg = 'Set lock for {0}'.format(repo['url']) + msg = "Set lock for {0}".format(repo["url"]) log.debug(msg) success.append(msg) return success, failed @@ -399,11 +414,11 @@ def lock(remote=None): for repo in init(): if remote: try: - if not fnmatch.fnmatch(repo['url'], remote): + if not fnmatch.fnmatch(repo["url"], remote): continue except TypeError: # remote was non-string, try again - if not fnmatch.fnmatch(repo['url'], six.text_type(remote)): + if not fnmatch.fnmatch(repo["url"], six.text_type(remote)): continue success, failed = _do_lock(repo) locked.extend(success) @@ -413,40 +428,42 @@ def lock(remote=None): def update(): - ''' + """ Execute an svn update on all of the repos - ''' + """ # data for the fileserver event - data = {'changed': False, - 'backend': 'svnfs'} + data = {"changed": False, "backend": "svnfs"} # _clear_old_remotes runs init(), so use the value from there to avoid a # second init() - data['changed'], repos = _clear_old_remotes() + data["changed"], repos = _clear_old_remotes() for repo in repos: - if os.path.exists(repo['lockfile']): + if os.path.exists(repo["lockfile"]): log.warning( - 'Update lockfile is present for svnfs remote %s, skipping. ' - 'If this warning persists, it is possible that the update ' - 'process was interrupted. Removing %s or running ' - '\'salt-run fileserver.clear_lock svnfs\' will allow updates ' - 'to continue for this remote.', repo['url'], repo['lockfile'] + "Update lockfile is present for svnfs remote %s, skipping. " + "If this warning persists, it is possible that the update " + "process was interrupted. Removing %s or running " + "'salt-run fileserver.clear_lock svnfs' will allow updates " + "to continue for this remote.", + repo["url"], + repo["lockfile"], ) continue _, errors = lock(repo) if errors: log.error( - 'Unable to set update lock for svnfs remote %s, skipping.', - repo['url'] + "Unable to set update lock for svnfs remote %s, skipping.", repo["url"] ) continue - log.debug('svnfs is fetching from %s', repo['url']) + log.debug("svnfs is fetching from %s", repo["url"]) old_rev = _rev(repo) try: - CLIENT.update(repo['repo']) + CLIENT.update(repo["repo"]) except pysvn._pysvn.ClientError as exc: log.error( - 'Error updating svnfs remote %s (cachedir: %s): %s', - repo['url'], repo['cachedir'], exc + "Error updating svnfs remote %s (cachedir: %s): %s", + repo["url"], + repo["cachedir"], + exc, ) new_rev = _rev(repo) @@ -454,34 +471,34 @@ def update(): # There were problems getting the revision ID continue if new_rev != old_rev: - data['changed'] = True + data["changed"] = True clear_lock(repo) - env_cache = os.path.join(__opts__['cachedir'], 'svnfs/envs.p') - if data.get('changed', False) is True or not os.path.isfile(env_cache): + env_cache = os.path.join(__opts__["cachedir"], "svnfs/envs.p") + if data.get("changed", False) is True or not os.path.isfile(env_cache): env_cachedir = os.path.dirname(env_cache) if not os.path.exists(env_cachedir): os.makedirs(env_cachedir) new_envs = envs(ignore_cache=True) serial = salt.payload.Serial(__opts__) - with salt.utils.files.fopen(env_cache, 'wb+') as fp_: + with salt.utils.files.fopen(env_cache, "wb+") as fp_: fp_.write(serial.dumps(new_envs)) - log.trace('Wrote env cache data to %s', env_cache) + log.trace("Wrote env cache data to %s", env_cache) # if there is a change, fire an event - if __opts__.get('fileserver_events', False): + if __opts__.get("fileserver_events", False): with salt.utils.event.get_event( - 'master', - __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=False) as event: - event.fire_event(data, tagify(['svnfs', 'update'], prefix='fileserver')) + "master", + __opts__["sock_dir"], + __opts__["transport"], + opts=__opts__, + listen=False, + ) as event: + event.fire_event(data, tagify(["svnfs", "update"], prefix="fileserver")) try: salt.fileserver.reap_fileserver_cache_dir( - os.path.join(__opts__['cachedir'], 'svnfs/hash'), - find_file + os.path.join(__opts__["cachedir"], "svnfs/hash"), find_file ) except (IOError, OSError): # Hash file won't exist if no files have yet been served up @@ -489,93 +506,95 @@ def update(): def _env_is_exposed(env): - ''' + """ Check if an environment is exposed by comparing it against a whitelist and blacklist. - ''' + """ return salt.utils.stringutils.check_whitelist_blacklist( env, - whitelist=__opts__['svnfs_saltenv_whitelist'], - blacklist=__opts__['svnfs_saltenv_blacklist'], + whitelist=__opts__["svnfs_saltenv_whitelist"], + blacklist=__opts__["svnfs_saltenv_blacklist"], ) def envs(ignore_cache=False): - ''' + """ Return a list of refs that can be used as environments - ''' + """ if not ignore_cache: - env_cache = os.path.join(__opts__['cachedir'], 'svnfs/envs.p') + env_cache = os.path.join(__opts__["cachedir"], "svnfs/envs.p") cache_match = salt.fileserver.check_env_cache(__opts__, env_cache) if cache_match is not None: return cache_match ret = set() for repo in init(): - trunk = os.path.join(repo['repo'], repo['trunk']) + trunk = os.path.join(repo["repo"], repo["trunk"]) if os.path.isdir(trunk): # Add base as the env for trunk - ret.add('base') + ret.add("base") else: log.error( - 'svnfs trunk path \'%s\' does not exist in repo %s, no base ' - 'environment will be provided by this remote', - repo['trunk'], repo['url'] + "svnfs trunk path '%s' does not exist in repo %s, no base " + "environment will be provided by this remote", + repo["trunk"], + repo["url"], ) - branches = os.path.join(repo['repo'], repo['branches']) + branches = os.path.join(repo["repo"], repo["branches"]) if os.path.isdir(branches): ret.update(os.listdir(branches)) else: log.error( - 'svnfs branches path \'%s\' does not exist in repo %s', - repo['branches'], repo['url'] + "svnfs branches path '%s' does not exist in repo %s", + repo["branches"], + repo["url"], ) - tags = os.path.join(repo['repo'], repo['tags']) + tags = os.path.join(repo["repo"], repo["tags"]) if os.path.isdir(tags): ret.update(os.listdir(tags)) else: log.error( - 'svnfs tags path \'%s\' does not exist in repo %s', - repo['tags'], repo['url'] + "svnfs tags path '%s' does not exist in repo %s", + repo["tags"], + repo["url"], ) return [x for x in sorted(ret) if _env_is_exposed(x)] def _env_root(repo, saltenv): - ''' + """ Return the root of the directory corresponding to the desired environment, or None if the environment was not found. - ''' + """ # If 'base' is desired, look for the trunk - if saltenv == 'base': - trunk = os.path.join(repo['repo'], repo['trunk']) + if saltenv == "base": + trunk = os.path.join(repo["repo"], repo["trunk"]) if os.path.isdir(trunk): return trunk else: return None # Check branches - branches = os.path.join(repo['repo'], repo['branches']) + branches = os.path.join(repo["repo"], repo["branches"]) if os.path.isdir(branches) and saltenv in os.listdir(branches): return os.path.join(branches, saltenv) # Check tags - tags = os.path.join(repo['repo'], repo['tags']) + tags = os.path.join(repo["repo"], repo["tags"]) if os.path.isdir(tags) and saltenv in os.listdir(tags): return os.path.join(tags, saltenv) return None -def find_file(path, tgt_env='base', **kwargs): # pylint: disable=W0613 - ''' +def find_file(path, tgt_env="base", **kwargs): # pylint: disable=W0613 + """ Find the first file to match the path and ref. This operates similarly to the roots file sever but with assumptions of the directory structure based on svn standard practices. - ''' - fnd = {'path': '', - 'rel': ''} + """ + fnd = {"path": "", "rel": ""} if os.path.isabs(path) or tgt_env not in envs(): return fnd @@ -584,17 +603,16 @@ def find_file(path, tgt_env='base', **kwargs): # pylint: disable=W0613 if env_root is None: # Environment not found, try the next repo continue - if repo['mountpoint'] \ - and not path.startswith(repo['mountpoint'] + os.path.sep): + if repo["mountpoint"] and not path.startswith(repo["mountpoint"] + os.path.sep): continue - repo_path = path[len(repo['mountpoint']):].lstrip(os.path.sep) - if repo['root']: - repo_path = os.path.join(repo['root'], repo_path) + repo_path = path[len(repo["mountpoint"]) :].lstrip(os.path.sep) + if repo["root"]: + repo_path = os.path.join(repo["root"], repo_path) full = os.path.join(env_root, repo_path) if os.path.isfile(full): - fnd['rel'] = path - fnd['path'] = full + fnd["rel"] = path + fnd["path"] = full try: # Converting the stat result to a list, the elements of the # list correspond to the following stat_result params: @@ -608,7 +626,7 @@ def find_file(path, tgt_env='base', **kwargs): # pylint: disable=W0613 # 7 => st_atime=1468284229 # 8 => st_mtime=1456338235 # 9 => st_ctime=1456338235 - fnd['stat'] = list(os.stat(full)) + fnd["stat"] = list(os.stat(full)) except Exception: # pylint: disable=broad-except pass return fnd @@ -616,176 +634,165 @@ def find_file(path, tgt_env='base', **kwargs): # pylint: disable=W0613 def serve_file(load, fnd): - ''' + """ Return a chunk from a file based on the data received - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - ret = {'data': '', - 'dest': ''} - if not all(x in load for x in ('path', 'loc', 'saltenv')): + ret = {"data": "", "dest": ""} + if not all(x in load for x in ("path", "loc", "saltenv")): return ret - if not fnd['path']: + if not fnd["path"]: return ret - ret['dest'] = fnd['rel'] - gzip = load.get('gzip', None) - fpath = os.path.normpath(fnd['path']) - with salt.utils.files.fopen(fpath, 'rb') as fp_: - fp_.seek(load['loc']) - data = fp_.read(__opts__['file_buffer_size']) + ret["dest"] = fnd["rel"] + gzip = load.get("gzip", None) + fpath = os.path.normpath(fnd["path"]) + with salt.utils.files.fopen(fpath, "rb") as fp_: + fp_.seek(load["loc"]) + data = fp_.read(__opts__["file_buffer_size"]) if data and six.PY3 and not salt.utils.files.is_binary(fpath): data = data.decode(__salt_system_encoding__) if gzip and data: data = salt.utils.gzip_util.compress(data, gzip) - ret['gzip'] = gzip - ret['data'] = data + ret["gzip"] = gzip + ret["data"] = data return ret def file_hash(load, fnd): - ''' + """ Return a file hash, the hash type is set in the master config file - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if not all(x in load for x in ('path', 'saltenv')): - return '' - saltenv = load['saltenv'] - if saltenv == 'base': - saltenv = 'trunk' + if not all(x in load for x in ("path", "saltenv")): + return "" + saltenv = load["saltenv"] + if saltenv == "base": + saltenv = "trunk" ret = {} - relpath = fnd['rel'] - path = fnd['path'] + relpath = fnd["rel"] + path = fnd["path"] # If the file doesn't exist, we can't get a hash if not path or not os.path.isfile(path): return ret # Set the hash_type as it is determined by config - ret['hash_type'] = __opts__['hash_type'] + ret["hash_type"] = __opts__["hash_type"] # Check if the hash is cached # Cache file's contents should be "hash:mtime" - cache_path = os.path.join(__opts__['cachedir'], - 'svnfs', - 'hash', - saltenv, - '{0}.hash.{1}'.format(relpath, - __opts__['hash_type'])) + cache_path = os.path.join( + __opts__["cachedir"], + "svnfs", + "hash", + saltenv, + "{0}.hash.{1}".format(relpath, __opts__["hash_type"]), + ) # If we have a cache, serve that if the mtime hasn't changed if os.path.exists(cache_path): - with salt.utils.files.fopen(cache_path, 'rb') as fp_: - hsum, mtime = fp_.read().split(':') + with salt.utils.files.fopen(cache_path, "rb") as fp_: + hsum, mtime = fp_.read().split(":") if os.path.getmtime(path) == mtime: # check if mtime changed - ret['hsum'] = hsum + ret["hsum"] = hsum return ret # if we don't have a cache entry-- lets make one - ret['hsum'] = salt.utils.hashutils.get_hash(path, __opts__['hash_type']) + ret["hsum"] = salt.utils.hashutils.get_hash(path, __opts__["hash_type"]) cache_dir = os.path.dirname(cache_path) # make cache directory if it doesn't exist if not os.path.exists(cache_dir): os.makedirs(cache_dir) # save the cache object "hash:mtime" - with salt.utils.files.fopen(cache_path, 'w') as fp_: - fp_.write('{0}:{1}'.format(ret['hsum'], os.path.getmtime(path))) + with salt.utils.files.fopen(cache_path, "w") as fp_: + fp_.write("{0}:{1}".format(ret["hsum"], os.path.getmtime(path))) return ret def _file_lists(load, form): - ''' + """ Return a dict containing the file lists for files, dirs, emptydirs and symlinks - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if 'saltenv' not in load or load['saltenv'] not in envs(): + if "saltenv" not in load or load["saltenv"] not in envs(): return [] - list_cachedir = os.path.join(__opts__['cachedir'], 'file_lists/svnfs') + list_cachedir = os.path.join(__opts__["cachedir"], "file_lists/svnfs") if not os.path.isdir(list_cachedir): try: os.makedirs(list_cachedir) except os.error: - log.critical('Unable to make cachedir %s', list_cachedir) + log.critical("Unable to make cachedir %s", list_cachedir) return [] - list_cache = os.path.join(list_cachedir, '{0}.p'.format(load['saltenv'])) - w_lock = os.path.join(list_cachedir, '.{0}.w'.format(load['saltenv'])) - cache_match, refresh_cache, save_cache = \ - salt.fileserver.check_file_list_cache( - __opts__, form, list_cache, w_lock - ) + list_cache = os.path.join(list_cachedir, "{0}.p".format(load["saltenv"])) + w_lock = os.path.join(list_cachedir, ".{0}.w".format(load["saltenv"])) + cache_match, refresh_cache, save_cache = salt.fileserver.check_file_list_cache( + __opts__, form, list_cache, w_lock + ) if cache_match is not None: return cache_match if refresh_cache: - ret = { - 'files': set(), - 'dirs': set(), - 'empty_dirs': set() - } + ret = {"files": set(), "dirs": set(), "empty_dirs": set()} for repo in init(): - env_root = _env_root(repo, load['saltenv']) + env_root = _env_root(repo, load["saltenv"]) if env_root is None: # Environment not found, try the next repo continue - if repo['root']: - env_root = \ - os.path.join(env_root, repo['root']).rstrip(os.path.sep) + if repo["root"]: + env_root = os.path.join(env_root, repo["root"]).rstrip(os.path.sep) if not os.path.isdir(env_root): # svnfs root (global or per-remote) does not exist in env continue for root, dirs, files in salt.utils.path.os_walk(env_root): relpath = os.path.relpath(root, env_root) - dir_rel_fn = os.path.join(repo['mountpoint'], relpath) - if relpath != '.': - ret['dirs'].add(dir_rel_fn) + dir_rel_fn = os.path.join(repo["mountpoint"], relpath) + if relpath != ".": + ret["dirs"].add(dir_rel_fn) if not dirs and not files: - ret['empty_dirs'].add(dir_rel_fn) + ret["empty_dirs"].add(dir_rel_fn) for fname in files: - rel_fn = os.path.relpath( - os.path.join(root, fname), - env_root - ) - ret['files'].add(os.path.join(repo['mountpoint'], rel_fn)) - if repo['mountpoint']: - ret['dirs'].add(repo['mountpoint']) + rel_fn = os.path.relpath(os.path.join(root, fname), env_root) + ret["files"].add(os.path.join(repo["mountpoint"], rel_fn)) + if repo["mountpoint"]: + ret["dirs"].add(repo["mountpoint"]) # Convert all compiled sets to lists for key in ret: ret[key] = sorted(ret[key]) if save_cache: - salt.fileserver.write_file_list_cache( - __opts__, ret, list_cache, w_lock - ) + salt.fileserver.write_file_list_cache(__opts__, ret, list_cache, w_lock) return ret.get(form, []) # Shouldn't get here, but if we do, this prevents a TypeError return [] def file_list(load): - ''' + """ Return a list of all files on the file server in a specified environment - ''' - return _file_lists(load, 'files') + """ + return _file_lists(load, "files") def file_list_emptydirs(load): - ''' + """ Return a list of all empty directories on the master - ''' - return _file_lists(load, 'empty_dirs') + """ + return _file_lists(load, "empty_dirs") def dir_list(load): - ''' + """ Return a list of all directories on the master - ''' - return _file_lists(load, 'dirs') + """ + return _file_lists(load, "dirs") diff --git a/salt/grains/__init__.py b/salt/grains/__init__.py index 7a1d4801a91..cf708331ef7 100644 --- a/salt/grains/__init__.py +++ b/salt/grains/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Grains plugin directory -''' +""" diff --git a/salt/grains/chronos.py b/salt/grains/chronos.py index 3b5add6895b..60478a76938 100644 --- a/salt/grains/chronos.py +++ b/salt/grains/chronos.py @@ -1,39 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Generate chronos proxy minion grains. .. versionadded:: 2015.8.2 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals - # Import Salt libs import salt.utils.http import salt.utils.platform -__proxyenabled__ = ['chronos'] -__virtualname__ = 'chronos' + +__proxyenabled__ = ["chronos"] +__virtualname__ = "chronos" def __virtual__(): - if not salt.utils.platform.is_proxy() or 'proxy' not in __opts__: + if not salt.utils.platform.is_proxy() or "proxy" not in __opts__: return False else: return __virtualname__ def kernel(): - return {'kernel': 'chronos'} + return {"kernel": "chronos"} def os(): - return {'os': 'chronos'} + return {"os": "chronos"} def os_family(): - return {'os_family': 'chronos'} + return {"os_family": "chronos"} def os_data(): - return {'os_data': 'chronos'} + return {"os_data": "chronos"} diff --git a/salt/grains/cimc.py b/salt/grains/cimc.py index 62f891ebee9..080b124a6b6 100644 --- a/salt/grains/cimc.py +++ b/salt/grains/cimc.py @@ -1,28 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" Generate baseline proxy minion grains for cimc hosts. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.proxy.cimc + # Import Salt Libs import salt.utils.platform -import salt.proxy.cimc -__proxyenabled__ = ['cimc'] -__virtualname__ = 'cimc' +__proxyenabled__ = ["cimc"] +__virtualname__ = "cimc" log = logging.getLogger(__file__) -GRAINS_CACHE = {'os_family': 'Cisco UCS'} +GRAINS_CACHE = {"os_family": "Cisco UCS"} def __virtual__(): try: - if salt.utils.platform.is_proxy() and __opts__['proxy']['proxytype'] == 'cimc': + if salt.utils.platform.is_proxy() and __opts__["proxy"]["proxytype"] == "cimc": return __virtualname__ except KeyError: pass @@ -33,6 +35,6 @@ def __virtual__(): def cimc(proxy=None): if not proxy: return {} - if proxy['cimc.initialized']() is False: + if proxy["cimc.initialized"]() is False: return {} - return {'cimc': proxy['cimc.grains']()} + return {"cimc": proxy["cimc.grains"]()} diff --git a/salt/grains/core.py b/salt/grains/core.py index 9b244def9c9..e7b9a1f564f 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The static grains, these are the core, or built in grains. When grains are loaded they are not loaded in the same way that modules are @@ -8,53 +8,36 @@ return a dict which will be applied to the main grains dict. This module will always be executed first, so that any grains loaded here in the core module can be overwritten just by returning dict keys with the same value as those returned here -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime +import locale +import logging import os +import platform +import re import socket import sys -import re -import platform -import logging -import locale -import uuid -from errno import EACCES, EPERM -import datetime -import warnings import time - -# pylint: disable=import-error -try: - import dateutil.tz - _DATEUTIL_TZ = True -except ImportError: - _DATEUTIL_TZ = False - -__proxyenabled__ = ['*'] -__FQDN__ = None +import uuid +import warnings +from errno import EACCES, EPERM # Extend the default list of supported distros. This will be used for the # /etc/DISTRO-release checking that is part of linux_distribution() from platform import _supported_dists -_supported_dists += ('arch', 'mageia', 'meego', 'vmware', 'bluewhite64', - 'slamd64', 'ovs', 'system', 'mint', 'oracle', 'void') - -# linux_distribution deprecated in py3.7 -try: - from platform import linux_distribution as _deprecated_linux_distribution - - def linux_distribution(**kwargs): - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - return _deprecated_linux_distribution(**kwargs) -except ImportError: - from distro import linux_distribution # Import salt libs import salt.exceptions import salt.log + +# Solve the Chicken and egg problem where grains need to run before any +# of the modules are loaded and are generally available for any usage. +import salt.modules.cmdmod +import salt.modules.smbios import salt.utils.dns import salt.utils.files import salt.utils.network @@ -65,20 +48,56 @@ import salt.utils.stringutils from salt.ext import six from salt.ext.six.moves import range +# pylint: disable=import-error +try: + import dateutil.tz + + _DATEUTIL_TZ = True +except ImportError: + _DATEUTIL_TZ = False + +__proxyenabled__ = ["*"] +__FQDN__ = None + + +_supported_dists += ( + "arch", + "mageia", + "meego", + "vmware", + "bluewhite64", + "slamd64", + "ovs", + "system", + "mint", + "oracle", + "void", +) + +# linux_distribution deprecated in py3.7 +try: + from platform import linux_distribution as _deprecated_linux_distribution + + def linux_distribution(**kwargs): + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + return _deprecated_linux_distribution(**kwargs) + + +except ImportError: + from distro import linux_distribution + + if salt.utils.platform.is_windows(): import salt.utils.win_osinfo -# Solve the Chicken and egg problem where grains need to run before any -# of the modules are loaded and are generally available for any usage. -import salt.modules.cmdmod -import salt.modules.smbios __salt__ = { - 'cmd.run': salt.modules.cmdmod._run_quiet, - 'cmd.retcode': salt.modules.cmdmod._retcode_quiet, - 'cmd.run_all': salt.modules.cmdmod._run_all_quiet, - 'smbios.records': salt.modules.smbios.records, - 'smbios.get': salt.modules.smbios.get, + "cmd.run": salt.modules.cmdmod._run_quiet, + "cmd.retcode": salt.modules.cmdmod._retcode_quiet, + "cmd.run_all": salt.modules.cmdmod._run_all_quiet, + "smbios.records": salt.modules.smbios.records, + "smbios.get": salt.modules.smbios.get, } log = logging.getLogger(__name__) @@ -91,15 +110,15 @@ if salt.utils.platform.is_windows(): import salt.utils.winapi import win32api import salt.utils.win_reg + HAS_WMI = True except ImportError: log.exception( - 'Unable to import Python wmi module, some core grains ' - 'will be missing' + "Unable to import Python wmi module, some core grains " "will be missing" ) HAS_UNAME = True -if not hasattr(os, 'uname'): +if not hasattr(os, "uname"): HAS_UNAME = False _INTERFACES = {} @@ -110,61 +129,62 @@ NO_DATA = 4 def _windows_cpudata(): - ''' + """ Return some CPU information on Windows minions - ''' + """ # Provides: # num_cpus # cpu_model grains = {} - if 'NUMBER_OF_PROCESSORS' in os.environ: + if "NUMBER_OF_PROCESSORS" in os.environ: # Cast to int so that the logic isn't broken when used as a # conditional in templating. Also follows _linux_cpudata() try: - grains['num_cpus'] = int(os.environ['NUMBER_OF_PROCESSORS']) + grains["num_cpus"] = int(os.environ["NUMBER_OF_PROCESSORS"]) except ValueError: - grains['num_cpus'] = 1 - grains['cpu_model'] = salt.utils.win_reg.read_value( + grains["num_cpus"] = 1 + grains["cpu_model"] = salt.utils.win_reg.read_value( hive="HKEY_LOCAL_MACHINE", key="HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", - vname="ProcessorNameString").get('vdata') + vname="ProcessorNameString", + ).get("vdata") return grains def _linux_cpudata(): - ''' + """ Return some CPU information for Linux minions - ''' + """ # Provides: # num_cpus # cpu_model # cpu_flags grains = {} - cpuinfo = '/proc/cpuinfo' + cpuinfo = "/proc/cpuinfo" # Parse over the cpuinfo file if os.path.isfile(cpuinfo): - with salt.utils.files.fopen(cpuinfo, 'r') as _fp: + with salt.utils.files.fopen(cpuinfo, "r") as _fp: for line in _fp: - comps = line.split(':') + comps = line.split(":") if not len(comps) > 1: continue key = comps[0].strip() val = comps[1].strip() - if key == 'processor': - grains['num_cpus'] = int(val) + 1 + if key == "processor": + grains["num_cpus"] = int(val) + 1 # head -2 /proc/cpuinfo # vendor_id : IBM/S390 # # processors : 2 - elif key == '# processors': - grains['num_cpus'] = int(val) - elif key == 'vendor_id': - grains['cpu_model'] = val - elif key == 'model name': - grains['cpu_model'] = val - elif key == 'flags': - grains['cpu_flags'] = val.split() - elif key == 'Features': - grains['cpu_flags'] = val.split() + elif key == "# processors": + grains["num_cpus"] = int(val) + elif key == "vendor_id": + grains["cpu_model"] = val + elif key == "model name": + grains["cpu_model"] = val + elif key == "flags": + grains["cpu_flags"] = val.split() + elif key == "Features": + grains["cpu_flags"] = val.split() # ARM support - /proc/cpuinfo # # Processor : ARMv6-compatible processor rev 7 (v6l) @@ -179,588 +199,626 @@ def _linux_cpudata(): # Hardware : BCM2708 # Revision : 0002 # Serial : 00000000 - elif key == 'Processor': - grains['cpu_model'] = val.split('-')[0] - grains['num_cpus'] = 1 - if 'num_cpus' not in grains: - grains['num_cpus'] = 0 - if 'cpu_model' not in grains: - grains['cpu_model'] = 'Unknown' - if 'cpu_flags' not in grains: - grains['cpu_flags'] = [] + elif key == "Processor": + grains["cpu_model"] = val.split("-")[0] + grains["num_cpus"] = 1 + if "num_cpus" not in grains: + grains["num_cpus"] = 0 + if "cpu_model" not in grains: + grains["cpu_model"] = "Unknown" + if "cpu_flags" not in grains: + grains["cpu_flags"] = [] return grains def _linux_gpu_data(): - ''' + """ num_gpus: int gpus: - vendor: nvidia|amd|ati|... model: string - ''' - if __opts__.get('enable_lspci', True) is False: + """ + if __opts__.get("enable_lspci", True) is False: return {} - if __opts__.get('enable_gpu_grains', True) is False: + if __opts__.get("enable_gpu_grains", True) is False: return {} - lspci = salt.utils.path.which('lspci') + lspci = salt.utils.path.which("lspci") if not lspci: log.debug( - 'The `lspci` binary is not available on the system. GPU grains ' - 'will not be available.' + "The `lspci` binary is not available on the system. GPU grains " + "will not be available." ) return {} # dominant gpu vendors to search for (MUST be lowercase for matching below) - known_vendors = ['nvidia', 'amd', 'ati', 'intel', 'cirrus logic', 'vmware', 'matrox', 'aspeed'] - gpu_classes = ('vga compatible controller', '3d controller') + known_vendors = [ + "nvidia", + "amd", + "ati", + "intel", + "cirrus logic", + "vmware", + "matrox", + "aspeed", + ] + gpu_classes = ("vga compatible controller", "3d controller") devs = [] try: - lspci_out = __salt__['cmd.run']('{0} -vmm'.format(lspci)) + lspci_out = __salt__["cmd.run"]("{0} -vmm".format(lspci)) cur_dev = {} error = False # Add a blank element to the lspci_out.splitlines() list, # otherwise the last device is not evaluated as a cur_dev and ignored. lspci_list = lspci_out.splitlines() - lspci_list.append('') + lspci_list.append("") for line in lspci_list: # check for record-separating empty lines - if line == '': - if cur_dev.get('Class', '').lower() in gpu_classes: + if line == "": + if cur_dev.get("Class", "").lower() in gpu_classes: devs.append(cur_dev) cur_dev = {} continue - if re.match(r'^\w+:\s+.*', line): - key, val = line.split(':', 1) + if re.match(r"^\w+:\s+.*", line): + key, val = line.split(":", 1) cur_dev[key.strip()] = val.strip() else: error = True - log.debug('Unexpected lspci output: \'%s\'', line) + log.debug("Unexpected lspci output: '%s'", line) if error: log.warning( - 'Error loading grains, unexpected linux_gpu_data output, ' - 'check that you have a valid shell configured and ' - 'permissions to run lspci command' + "Error loading grains, unexpected linux_gpu_data output, " + "check that you have a valid shell configured and " + "permissions to run lspci command" ) except OSError: pass gpus = [] for gpu in devs: - vendor_strings = gpu['Vendor'].lower().split() + vendor_strings = gpu["Vendor"].lower().split() # default vendor to 'unknown', overwrite if we match a known one - vendor = 'unknown' + vendor = "unknown" for name in known_vendors: # search for an 'expected' vendor name in the list of strings if name in vendor_strings: vendor = name break - gpus.append({'vendor': vendor, 'model': gpu['Device']}) + gpus.append({"vendor": vendor, "model": gpu["Device"]}) grains = {} - grains['num_gpus'] = len(gpus) - grains['gpus'] = gpus + grains["num_gpus"] = len(gpus) + grains["gpus"] = gpus return grains def _netbsd_gpu_data(): - ''' + """ num_gpus: int gpus: - vendor: nvidia|amd|ati|... model: string - ''' - known_vendors = ['nvidia', 'amd', 'ati', 'intel', 'cirrus logic', 'vmware', 'matrox', 'aspeed'] + """ + known_vendors = [ + "nvidia", + "amd", + "ati", + "intel", + "cirrus logic", + "vmware", + "matrox", + "aspeed", + ] gpus = [] try: - pcictl_out = __salt__['cmd.run']('pcictl pci0 list') + pcictl_out = __salt__["cmd.run"]("pcictl pci0 list") for line in pcictl_out.splitlines(): for vendor in known_vendors: vendor_match = re.match( - r'[0-9:]+ ({0}) (.+) \(VGA .+\)'.format(vendor), - line, - re.IGNORECASE + r"[0-9:]+ ({0}) (.+) \(VGA .+\)".format(vendor), line, re.IGNORECASE ) if vendor_match: - gpus.append({'vendor': vendor_match.group(1), 'model': vendor_match.group(2)}) + gpus.append( + { + "vendor": vendor_match.group(1), + "model": vendor_match.group(2), + } + ) except OSError: pass grains = {} - grains['num_gpus'] = len(gpus) - grains['gpus'] = gpus + grains["num_gpus"] = len(gpus) + grains["gpus"] = gpus return grains def _osx_gpudata(): - ''' + """ num_gpus: int gpus: - vendor: nvidia|amd|ati|... model: string - ''' + """ gpus = [] try: - pcictl_out = __salt__['cmd.run']('system_profiler SPDisplaysDataType') + pcictl_out = __salt__["cmd.run"]("system_profiler SPDisplaysDataType") for line in pcictl_out.splitlines(): - fieldname, _, fieldval = line.partition(': ') + fieldname, _, fieldval = line.partition(": ") if fieldname.strip() == "Chipset Model": - vendor, _, model = fieldval.partition(' ') + vendor, _, model = fieldval.partition(" ") vendor = vendor.lower() - gpus.append({'vendor': vendor, 'model': model}) + gpus.append({"vendor": vendor, "model": model}) except OSError: pass grains = {} - grains['num_gpus'] = len(gpus) - grains['gpus'] = gpus + grains["num_gpus"] = len(gpus) + grains["gpus"] = gpus return grains def _bsd_cpudata(osdata): - ''' + """ Return CPU information for BSD-like systems - ''' + """ # Provides: # cpuarch # num_cpus # cpu_model # cpu_flags - sysctl = salt.utils.path.which('sysctl') - arch = salt.utils.path.which('arch') + sysctl = salt.utils.path.which("sysctl") + arch = salt.utils.path.which("arch") cmds = {} if sysctl: - cmds.update({ - 'num_cpus': '{0} -n hw.ncpu'.format(sysctl), - 'cpuarch': '{0} -n hw.machine'.format(sysctl), - 'cpu_model': '{0} -n hw.model'.format(sysctl), - }) + cmds.update( + { + "num_cpus": "{0} -n hw.ncpu".format(sysctl), + "cpuarch": "{0} -n hw.machine".format(sysctl), + "cpu_model": "{0} -n hw.model".format(sysctl), + } + ) - if arch and osdata['kernel'] == 'OpenBSD': - cmds['cpuarch'] = '{0} -s'.format(arch) + if arch and osdata["kernel"] == "OpenBSD": + cmds["cpuarch"] = "{0} -s".format(arch) - if osdata['kernel'] == 'Darwin': - cmds['cpu_model'] = '{0} -n machdep.cpu.brand_string'.format(sysctl) - cmds['cpu_flags'] = '{0} -n machdep.cpu.features'.format(sysctl) + if osdata["kernel"] == "Darwin": + cmds["cpu_model"] = "{0} -n machdep.cpu.brand_string".format(sysctl) + cmds["cpu_flags"] = "{0} -n machdep.cpu.features".format(sysctl) - grains = dict([(k, __salt__['cmd.run'](v)) for k, v in six.iteritems(cmds)]) + grains = dict([(k, __salt__["cmd.run"](v)) for k, v in six.iteritems(cmds)]) - if 'cpu_flags' in grains and isinstance(grains['cpu_flags'], six.string_types): - grains['cpu_flags'] = grains['cpu_flags'].split(' ') + if "cpu_flags" in grains and isinstance(grains["cpu_flags"], six.string_types): + grains["cpu_flags"] = grains["cpu_flags"].split(" ") - if osdata['kernel'] == 'NetBSD': - grains['cpu_flags'] = [] - for line in __salt__['cmd.run']('cpuctl identify 0').splitlines(): - cpu_match = re.match(r'cpu[0-9]:\ features[0-9]?\ .+<(.+)>', line) + if osdata["kernel"] == "NetBSD": + grains["cpu_flags"] = [] + for line in __salt__["cmd.run"]("cpuctl identify 0").splitlines(): + cpu_match = re.match(r"cpu[0-9]:\ features[0-9]?\ .+<(.+)>", line) if cpu_match: - flag = cpu_match.group(1).split(',') - grains['cpu_flags'].extend(flag) + flag = cpu_match.group(1).split(",") + grains["cpu_flags"].extend(flag) - if osdata['kernel'] == 'FreeBSD' and os.path.isfile('/var/run/dmesg.boot'): - grains['cpu_flags'] = [] + if osdata["kernel"] == "FreeBSD" and os.path.isfile("/var/run/dmesg.boot"): + grains["cpu_flags"] = [] # TODO: at least it needs to be tested for BSD other then FreeBSD - with salt.utils.files.fopen('/var/run/dmesg.boot', 'r') as _fp: + with salt.utils.files.fopen("/var/run/dmesg.boot", "r") as _fp: cpu_here = False for line in _fp: - if line.startswith('CPU: '): + if line.startswith("CPU: "): cpu_here = True # starts CPU descr continue if cpu_here: - if not line.startswith(' '): + if not line.startswith(" "): break # game over - if 'Features' in line: - start = line.find('<') - end = line.find('>') + if "Features" in line: + start = line.find("<") + end = line.find(">") if start > 0 and end > 0: - flag = line[start + 1:end].split(',') - grains['cpu_flags'].extend(flag) + flag = line[start + 1 : end].split(",") + grains["cpu_flags"].extend(flag) try: - grains['num_cpus'] = int(grains['num_cpus']) + grains["num_cpus"] = int(grains["num_cpus"]) except ValueError: - grains['num_cpus'] = 1 + grains["num_cpus"] = 1 return grains def _sunos_cpudata(): - ''' + """ Return the CPU information for Solaris-like systems - ''' + """ # Provides: # cpuarch # num_cpus # cpu_model # cpu_flags grains = {} - grains['cpu_flags'] = [] + grains["cpu_flags"] = [] - grains['cpuarch'] = __salt__['cmd.run']('isainfo -k') - psrinfo = '/usr/sbin/psrinfo 2>/dev/null' - grains['num_cpus'] = len(__salt__['cmd.run'](psrinfo, python_shell=True).splitlines()) - kstat_info = 'kstat -p cpu_info:*:*:brand' - for line in __salt__['cmd.run'](kstat_info).splitlines(): - match = re.match(r'(\w+:\d+:\w+\d+:\w+)\s+(.+)', line) + grains["cpuarch"] = __salt__["cmd.run"]("isainfo -k") + psrinfo = "/usr/sbin/psrinfo 2>/dev/null" + grains["num_cpus"] = len( + __salt__["cmd.run"](psrinfo, python_shell=True).splitlines() + ) + kstat_info = "kstat -p cpu_info:*:*:brand" + for line in __salt__["cmd.run"](kstat_info).splitlines(): + match = re.match(r"(\w+:\d+:\w+\d+:\w+)\s+(.+)", line) if match: - grains['cpu_model'] = match.group(2) - isainfo = 'isainfo -n -v' - for line in __salt__['cmd.run'](isainfo).splitlines(): - match = re.match(r'^\s+(.+)', line) + grains["cpu_model"] = match.group(2) + isainfo = "isainfo -n -v" + for line in __salt__["cmd.run"](isainfo).splitlines(): + match = re.match(r"^\s+(.+)", line) if match: cpu_flags = match.group(1).split() - grains['cpu_flags'].extend(cpu_flags) + grains["cpu_flags"].extend(cpu_flags) return grains def _aix_cpudata(): - ''' + """ Return CPU information for AIX systems - ''' + """ # Provides: # cpuarch # num_cpus # cpu_model # cpu_flags grains = {} - cmd = salt.utils.path.which('prtconf') + cmd = salt.utils.path.which("prtconf") if cmd: - data = __salt__['cmd.run']('{0}'.format(cmd)) + os.linesep - for dest, regstring in (('cpuarch', r'(?im)^\s*Processor\s+Type:\s+(\S+)'), - ('cpu_flags', r'(?im)^\s*Processor\s+Version:\s+(\S+)'), - ('cpu_model', r'(?im)^\s*Processor\s+Implementation\s+Mode:\s+(.*)'), - ('num_cpus', r'(?im)^\s*Number\s+Of\s+Processors:\s+(\S+)')): + data = __salt__["cmd.run"]("{0}".format(cmd)) + os.linesep + for dest, regstring in ( + ("cpuarch", r"(?im)^\s*Processor\s+Type:\s+(\S+)"), + ("cpu_flags", r"(?im)^\s*Processor\s+Version:\s+(\S+)"), + ("cpu_model", r"(?im)^\s*Processor\s+Implementation\s+Mode:\s+(.*)"), + ("num_cpus", r"(?im)^\s*Number\s+Of\s+Processors:\s+(\S+)"), + ): for regex in [re.compile(r) for r in [regstring]]: res = regex.search(data) if res and len(res.groups()) >= 1: - grains[dest] = res.group(1).strip().replace("'", '') + grains[dest] = res.group(1).strip().replace("'", "") else: - log.error('The \'prtconf\' binary was not found in $PATH.') + log.error("The 'prtconf' binary was not found in $PATH.") return grains def _linux_memdata(): - ''' + """ Return the memory information for Linux-like systems - ''' - grains = {'mem_total': 0, 'swap_total': 0} + """ + grains = {"mem_total": 0, "swap_total": 0} - meminfo = '/proc/meminfo' + meminfo = "/proc/meminfo" if os.path.isfile(meminfo): - with salt.utils.files.fopen(meminfo, 'r') as ifile: + with salt.utils.files.fopen(meminfo, "r") as ifile: for line in ifile: - comps = line.rstrip('\n').split(':') + comps = line.rstrip("\n").split(":") if not len(comps) > 1: continue - if comps[0].strip() == 'MemTotal': + if comps[0].strip() == "MemTotal": # Use floor division to force output to be an integer - grains['mem_total'] = int(comps[1].split()[0]) // 1024 - if comps[0].strip() == 'SwapTotal': + grains["mem_total"] = int(comps[1].split()[0]) // 1024 + if comps[0].strip() == "SwapTotal": # Use floor division to force output to be an integer - grains['swap_total'] = int(comps[1].split()[0]) // 1024 + grains["swap_total"] = int(comps[1].split()[0]) // 1024 return grains def _osx_memdata(): - ''' + """ Return the memory information for BSD-like systems - ''' - grains = {'mem_total': 0, 'swap_total': 0} + """ + grains = {"mem_total": 0, "swap_total": 0} - sysctl = salt.utils.path.which('sysctl') + sysctl = salt.utils.path.which("sysctl") if sysctl: - mem = __salt__['cmd.run']('{0} -n hw.memsize'.format(sysctl)) - swap_total = __salt__['cmd.run']('{0} -n vm.swapusage'.format(sysctl)).split()[2].replace(',', '.') - if swap_total.endswith('K'): - _power = 2**10 - elif swap_total.endswith('M'): - _power = 2**20 - elif swap_total.endswith('G'): - _power = 2**30 + mem = __salt__["cmd.run"]("{0} -n hw.memsize".format(sysctl)) + swap_total = ( + __salt__["cmd.run"]("{0} -n vm.swapusage".format(sysctl)) + .split()[2] + .replace(",", ".") + ) + if swap_total.endswith("K"): + _power = 2 ** 10 + elif swap_total.endswith("M"): + _power = 2 ** 20 + elif swap_total.endswith("G"): + _power = 2 ** 30 swap_total = float(swap_total[:-1]) * _power - grains['mem_total'] = int(mem) // 1024 // 1024 - grains['swap_total'] = int(swap_total) // 1024 // 1024 + grains["mem_total"] = int(mem) // 1024 // 1024 + grains["swap_total"] = int(swap_total) // 1024 // 1024 return grains def _bsd_memdata(osdata): - ''' + """ Return the memory information for BSD-like systems - ''' - grains = {'mem_total': 0, 'swap_total': 0} + """ + grains = {"mem_total": 0, "swap_total": 0} - sysctl = salt.utils.path.which('sysctl') + sysctl = salt.utils.path.which("sysctl") if sysctl: - mem = __salt__['cmd.run']('{0} -n hw.physmem'.format(sysctl)) - if osdata['kernel'] == 'NetBSD' and mem.startswith('-'): - mem = __salt__['cmd.run']('{0} -n hw.physmem64'.format(sysctl)) - grains['mem_total'] = int(mem) // 1024 // 1024 + mem = __salt__["cmd.run"]("{0} -n hw.physmem".format(sysctl)) + if osdata["kernel"] == "NetBSD" and mem.startswith("-"): + mem = __salt__["cmd.run"]("{0} -n hw.physmem64".format(sysctl)) + grains["mem_total"] = int(mem) // 1024 // 1024 - if osdata['kernel'] in ['OpenBSD', 'NetBSD']: - swapctl = salt.utils.path.which('swapctl') - swap_data = __salt__['cmd.run']('{0} -sk'.format(swapctl)) - if swap_data == 'no swap devices configured': + if osdata["kernel"] in ["OpenBSD", "NetBSD"]: + swapctl = salt.utils.path.which("swapctl") + swap_data = __salt__["cmd.run"]("{0} -sk".format(swapctl)) + if swap_data == "no swap devices configured": swap_total = 0 else: - swap_total = swap_data.split(' ')[1] + swap_total = swap_data.split(" ")[1] else: - swap_total = __salt__['cmd.run']('{0} -n vm.swap_total'.format(sysctl)) - grains['swap_total'] = int(swap_total) // 1024 // 1024 + swap_total = __salt__["cmd.run"]("{0} -n vm.swap_total".format(sysctl)) + grains["swap_total"] = int(swap_total) // 1024 // 1024 return grains def _sunos_memdata(): - ''' + """ Return the memory information for SunOS-like systems - ''' - grains = {'mem_total': 0, 'swap_total': 0} + """ + grains = {"mem_total": 0, "swap_total": 0} - prtconf = '/usr/sbin/prtconf 2>/dev/null' - for line in __salt__['cmd.run'](prtconf, python_shell=True).splitlines(): - comps = line.split(' ') - if comps[0].strip() == 'Memory' and comps[1].strip() == 'size:': - grains['mem_total'] = int(comps[2].strip()) + prtconf = "/usr/sbin/prtconf 2>/dev/null" + for line in __salt__["cmd.run"](prtconf, python_shell=True).splitlines(): + comps = line.split(" ") + if comps[0].strip() == "Memory" and comps[1].strip() == "size:": + grains["mem_total"] = int(comps[2].strip()) - swap_cmd = salt.utils.path.which('swap') - swap_data = __salt__['cmd.run']('{0} -s'.format(swap_cmd)).split() + swap_cmd = salt.utils.path.which("swap") + swap_data = __salt__["cmd.run"]("{0} -s".format(swap_cmd)).split() try: swap_avail = int(swap_data[-2][:-1]) swap_used = int(swap_data[-4][:-1]) swap_total = (swap_avail + swap_used) // 1024 except ValueError: swap_total = None - grains['swap_total'] = swap_total + grains["swap_total"] = swap_total return grains def _aix_memdata(): - ''' + """ Return the memory information for AIX systems - ''' - grains = {'mem_total': 0, 'swap_total': 0} - prtconf = salt.utils.path.which('prtconf') + """ + grains = {"mem_total": 0, "swap_total": 0} + prtconf = salt.utils.path.which("prtconf") if prtconf: - for line in __salt__['cmd.run'](prtconf, python_shell=True).splitlines(): - comps = [x for x in line.strip().split(' ') if x] - if len(comps) > 2 and 'Memory' in comps[0] and 'Size' in comps[1]: - grains['mem_total'] = int(comps[2]) + for line in __salt__["cmd.run"](prtconf, python_shell=True).splitlines(): + comps = [x for x in line.strip().split(" ") if x] + if len(comps) > 2 and "Memory" in comps[0] and "Size" in comps[1]: + grains["mem_total"] = int(comps[2]) break else: - log.error('The \'prtconf\' binary was not found in $PATH.') + log.error("The 'prtconf' binary was not found in $PATH.") - swap_cmd = salt.utils.path.which('swap') + swap_cmd = salt.utils.path.which("swap") if swap_cmd: - swap_data = __salt__['cmd.run']('{0} -s'.format(swap_cmd)).split() + swap_data = __salt__["cmd.run"]("{0} -s".format(swap_cmd)).split() try: swap_total = (int(swap_data[-2]) + int(swap_data[-6])) * 4 except ValueError: swap_total = None - grains['swap_total'] = swap_total + grains["swap_total"] = swap_total else: - log.error('The \'swap\' binary was not found in $PATH.') + log.error("The 'swap' binary was not found in $PATH.") return grains def _windows_memdata(): - ''' + """ Return the memory information for Windows systems - ''' - grains = {'mem_total': 0} + """ + grains = {"mem_total": 0} # get the Total Physical memory as reported by msinfo32 - tot_bytes = win32api.GlobalMemoryStatusEx()['TotalPhys'] + tot_bytes = win32api.GlobalMemoryStatusEx()["TotalPhys"] # return memory info in gigabytes - grains['mem_total'] = int(tot_bytes / (1024 ** 2)) + grains["mem_total"] = int(tot_bytes / (1024 ** 2)) return grains def _memdata(osdata): - ''' + """ Gather information about the system memory - ''' + """ # Provides: # mem_total # swap_total, for supported systems. - grains = {'mem_total': 0} - if osdata['kernel'] == 'Linux': + grains = {"mem_total": 0} + if osdata["kernel"] == "Linux": grains.update(_linux_memdata()) - elif osdata['kernel'] in ('FreeBSD', 'OpenBSD', 'NetBSD'): + elif osdata["kernel"] in ("FreeBSD", "OpenBSD", "NetBSD"): grains.update(_bsd_memdata(osdata)) - elif osdata['kernel'] == 'Darwin': + elif osdata["kernel"] == "Darwin": grains.update(_osx_memdata()) - elif osdata['kernel'] == 'SunOS': + elif osdata["kernel"] == "SunOS": grains.update(_sunos_memdata()) - elif osdata['kernel'] == 'AIX': + elif osdata["kernel"] == "AIX": grains.update(_aix_memdata()) - elif osdata['kernel'] == 'Windows' and HAS_WMI: + elif osdata["kernel"] == "Windows" and HAS_WMI: grains.update(_windows_memdata()) return grains def _aix_get_machine_id(): - ''' + """ Parse the output of lsattr -El sys0 for os_uuid - ''' + """ grains = {} - cmd = salt.utils.path.which('lsattr') + cmd = salt.utils.path.which("lsattr") if cmd: - data = __salt__['cmd.run']('{0} -El sys0'.format(cmd)) + os.linesep - uuid_regexes = [re.compile(r'(?im)^\s*os_uuid\s+(\S+)\s+(.*)')] + data = __salt__["cmd.run"]("{0} -El sys0".format(cmd)) + os.linesep + uuid_regexes = [re.compile(r"(?im)^\s*os_uuid\s+(\S+)\s+(.*)")] for regex in uuid_regexes: res = regex.search(data) if res and len(res.groups()) >= 1: - grains['machine_id'] = res.group(1).strip() + grains["machine_id"] = res.group(1).strip() break else: - log.error('The \'lsattr\' binary was not found in $PATH.') + log.error("The 'lsattr' binary was not found in $PATH.") return grains def _windows_virtual(osdata): - ''' + """ Returns what type of virtual hardware is under the hood, kvm or physical - ''' + """ # Provides: # virtual # virtual_subtype grains = dict() - if osdata['kernel'] != 'Windows': + if osdata["kernel"] != "Windows": return grains - grains['virtual'] = osdata.get('virtual', 'physical') + grains["virtual"] = osdata.get("virtual", "physical") # It is possible that the 'manufacturer' and/or 'productname' grains # exist but have a value of None. - manufacturer = osdata.get('manufacturer', '') + manufacturer = osdata.get("manufacturer", "") if manufacturer is None: - manufacturer = '' - productname = osdata.get('productname', '') + manufacturer = "" + productname = osdata.get("productname", "") if productname is None: - productname = '' + productname = "" - if 'QEMU' in manufacturer: + if "QEMU" in manufacturer: # FIXME: Make this detect between kvm or qemu - grains['virtual'] = 'kvm' - if 'Bochs' in manufacturer: - grains['virtual'] = 'kvm' + grains["virtual"] = "kvm" + if "Bochs" in manufacturer: + grains["virtual"] = "kvm" # Product Name: (oVirt) www.ovirt.org # Red Hat Community virtualization Project based on kvm - elif 'oVirt' in productname: - grains['virtual'] = 'kvm' - grains['virtual_subtype'] = 'oVirt' + elif "oVirt" in productname: + grains["virtual"] = "kvm" + grains["virtual_subtype"] = "oVirt" # Red Hat Enterprise Virtualization - elif 'RHEV Hypervisor' in productname: - grains['virtual'] = 'kvm' - grains['virtual_subtype'] = 'rhev' + elif "RHEV Hypervisor" in productname: + grains["virtual"] = "kvm" + grains["virtual_subtype"] = "rhev" # Product Name: VirtualBox - elif 'VirtualBox' in productname: - grains['virtual'] = 'VirtualBox' + elif "VirtualBox" in productname: + grains["virtual"] = "VirtualBox" # Product Name: VMware Virtual Platform - elif 'VMware Virtual Platform' in productname: - grains['virtual'] = 'VMware' + elif "VMware Virtual Platform" in productname: + grains["virtual"] = "VMware" # Manufacturer: Microsoft Corporation # Product Name: Virtual Machine - elif 'Microsoft' in manufacturer and \ - 'Virtual Machine' in productname: - grains['virtual'] = 'VirtualPC' + elif "Microsoft" in manufacturer and "Virtual Machine" in productname: + grains["virtual"] = "VirtualPC" # Manufacturer: Parallels Software International Inc. - elif 'Parallels Software' in manufacturer: - grains['virtual'] = 'Parallels' + elif "Parallels Software" in manufacturer: + grains["virtual"] = "Parallels" # Apache CloudStack - elif 'CloudStack KVM Hypervisor' in productname: - grains['virtual'] = 'kvm' - grains['virtual_subtype'] = 'cloudstack' + elif "CloudStack KVM Hypervisor" in productname: + grains["virtual"] = "kvm" + grains["virtual_subtype"] = "cloudstack" return grains def _virtual(osdata): - ''' + """ Returns what type of virtual hardware is under the hood, kvm or physical - ''' + """ # This is going to be a monster, if you are running a vm you can test this # grain with please submit patches! # Provides: # virtual # virtual_subtype - grains = {'virtual': osdata.get('virtual', 'physical')} + grains = {"virtual": osdata.get("virtual", "physical")} # Skip the below loop on platforms which have none of the desired cmds # This is a temporary measure until we can write proper virtual hardware # detection. - skip_cmds = ('AIX',) + skip_cmds = ("AIX",) # list of commands to be executed to determine the 'virtual' grain - _cmds = ['systemd-detect-virt', 'virt-what', 'dmidecode'] + _cmds = ["systemd-detect-virt", "virt-what", "dmidecode"] # test first for virt-what, which covers most of the desired functionality # on most platforms - if not salt.utils.platform.is_windows() and osdata['kernel'] not in skip_cmds: - if salt.utils.path.which('virt-what'): - _cmds = ['virt-what'] + if not salt.utils.platform.is_windows() and osdata["kernel"] not in skip_cmds: + if salt.utils.path.which("virt-what"): + _cmds = ["virt-what"] # Check if enable_lspci is True or False - if __opts__.get('enable_lspci', True) is True: + if __opts__.get("enable_lspci", True) is True: # /proc/bus/pci does not exists, lspci will fail - if os.path.exists('/proc/bus/pci'): - _cmds += ['lspci'] + if os.path.exists("/proc/bus/pci"): + _cmds += ["lspci"] # Add additional last resort commands - if osdata['kernel'] in skip_cmds: + if osdata["kernel"] in skip_cmds: _cmds = () # Quick backout for BrandZ (Solaris LX Branded zones) # Don't waste time trying other commands to detect the virtual grain - if HAS_UNAME and osdata['kernel'] == 'Linux' and 'BrandZ virtual linux' in os.uname(): - grains['virtual'] = 'zone' + if ( + HAS_UNAME + and osdata["kernel"] == "Linux" + and "BrandZ virtual linux" in os.uname() + ): + grains["virtual"] = "zone" return grains failed_commands = set() for command in _cmds: args = [] - if osdata['kernel'] == 'Darwin': - command = 'system_profiler' - args = ['SPDisplaysDataType'] - elif osdata['kernel'] == 'SunOS': - virtinfo = salt.utils.path.which('virtinfo') + if osdata["kernel"] == "Darwin": + command = "system_profiler" + args = ["SPDisplaysDataType"] + elif osdata["kernel"] == "SunOS": + virtinfo = salt.utils.path.which("virtinfo") if virtinfo: try: - ret = __salt__['cmd.run_all']('{0} -a'.format(virtinfo)) + ret = __salt__["cmd.run_all"]("{0} -a".format(virtinfo)) except salt.exceptions.CommandExecutionError: if salt.log.is_logging_configured(): failed_commands.add(virtinfo) else: - if ret['stdout'].endswith('not supported'): - command = 'prtdiag' + if ret["stdout"].endswith("not supported"): + command = "prtdiag" else: - command = 'virtinfo' + command = "virtinfo" else: - command = 'prtdiag' + command = "prtdiag" cmd = salt.utils.path.which(command) if not cmd: continue - cmd = '{0} {1}'.format(cmd, ' '.join(args)) + cmd = "{0} {1}".format(cmd, " ".join(args)) try: - ret = __salt__['cmd.run_all'](cmd) + ret = __salt__["cmd.run_all"](cmd) - if ret['retcode'] > 0: + if ret["retcode"] > 0: if salt.log.is_logging_configured(): # systemd-detect-virt always returns > 0 on non-virtualized # systems # prtdiag only works in the global zone, skip if it fails - if salt.utils.platform.is_windows() or 'systemd-detect-virt' in cmd or 'prtdiag' in cmd: + if ( + salt.utils.platform.is_windows() + or "systemd-detect-virt" in cmd + or "prtdiag" in cmd + ): continue failed_commands.add(command) continue @@ -771,329 +829,345 @@ def _virtual(osdata): failed_commands.add(command) continue - output = ret['stdout'] + output = ret["stdout"] if command == "system_profiler": macoutput = output.lower() - if '0x1ab8' in macoutput: - grains['virtual'] = 'Parallels' - if 'parallels' in macoutput: - grains['virtual'] = 'Parallels' - if 'vmware' in macoutput: - grains['virtual'] = 'VMware' - if '0x15ad' in macoutput: - grains['virtual'] = 'VMware' - if 'virtualbox' in macoutput: - grains['virtual'] = 'VirtualBox' + if "0x1ab8" in macoutput: + grains["virtual"] = "Parallels" + if "parallels" in macoutput: + grains["virtual"] = "Parallels" + if "vmware" in macoutput: + grains["virtual"] = "VMware" + if "0x15ad" in macoutput: + grains["virtual"] = "VMware" + if "virtualbox" in macoutput: + grains["virtual"] = "VirtualBox" # Break out of the loop so the next log message is not issued break - elif command == 'systemd-detect-virt': - if output in ('qemu', 'kvm', 'oracle', 'xen', 'bochs', 'chroot', 'uml', 'systemd-nspawn'): - grains['virtual'] = output + elif command == "systemd-detect-virt": + if output in ( + "qemu", + "kvm", + "oracle", + "xen", + "bochs", + "chroot", + "uml", + "systemd-nspawn", + ): + grains["virtual"] = output break - elif 'vmware' in output: - grains['virtual'] = 'VMware' + elif "vmware" in output: + grains["virtual"] = "VMware" break - elif 'microsoft' in output: - grains['virtual'] = 'VirtualPC' + elif "microsoft" in output: + grains["virtual"] = "VirtualPC" break - elif 'lxc' in output: - grains['virtual'] = 'LXC' + elif "lxc" in output: + grains["virtual"] = "LXC" break - elif 'systemd-nspawn' in output: - grains['virtual'] = 'LXC' + elif "systemd-nspawn" in output: + grains["virtual"] = "LXC" break - elif command == 'virt-what': + elif command == "virt-what": try: output = output.splitlines()[-1] except IndexError: pass - if output in ('kvm', 'qemu', 'uml', 'xen', 'lxc'): - grains['virtual'] = output + if output in ("kvm", "qemu", "uml", "xen", "lxc"): + grains["virtual"] = output break - elif 'vmware' in output: - grains['virtual'] = 'VMware' + elif "vmware" in output: + grains["virtual"] = "VMware" break - elif 'parallels' in output: - grains['virtual'] = 'Parallels' + elif "parallels" in output: + grains["virtual"] = "Parallels" break - elif 'hyperv' in output: - grains['virtual'] = 'HyperV' + elif "hyperv" in output: + grains["virtual"] = "HyperV" break - elif command == 'dmidecode': + elif command == "dmidecode": # Product Name: VirtualBox - if 'Vendor: QEMU' in output: + if "Vendor: QEMU" in output: # FIXME: Make this detect between kvm or qemu - grains['virtual'] = 'kvm' - if 'Manufacturer: QEMU' in output: - grains['virtual'] = 'kvm' - if 'Vendor: Bochs' in output: - grains['virtual'] = 'kvm' - if 'Manufacturer: Bochs' in output: - grains['virtual'] = 'kvm' - if 'BHYVE' in output: - grains['virtual'] = 'bhyve' + grains["virtual"] = "kvm" + if "Manufacturer: QEMU" in output: + grains["virtual"] = "kvm" + if "Vendor: Bochs" in output: + grains["virtual"] = "kvm" + if "Manufacturer: Bochs" in output: + grains["virtual"] = "kvm" + if "BHYVE" in output: + grains["virtual"] = "bhyve" # Product Name: (oVirt) www.ovirt.org # Red Hat Community virtualization Project based on kvm - elif 'Manufacturer: oVirt' in output: - grains['virtual'] = 'kvm' - grains['virtual_subtype'] = 'ovirt' + elif "Manufacturer: oVirt" in output: + grains["virtual"] = "kvm" + grains["virtual_subtype"] = "ovirt" # Red Hat Enterprise Virtualization - elif 'Product Name: RHEV Hypervisor' in output: - grains['virtual'] = 'kvm' - grains['virtual_subtype'] = 'rhev' - elif 'VirtualBox' in output: - grains['virtual'] = 'VirtualBox' + elif "Product Name: RHEV Hypervisor" in output: + grains["virtual"] = "kvm" + grains["virtual_subtype"] = "rhev" + elif "VirtualBox" in output: + grains["virtual"] = "VirtualBox" # Product Name: VMware Virtual Platform - elif 'VMware' in output: - grains['virtual'] = 'VMware' + elif "VMware" in output: + grains["virtual"] = "VMware" # Manufacturer: Microsoft Corporation # Product Name: Virtual Machine - elif ': Microsoft' in output and 'Virtual Machine' in output: - grains['virtual'] = 'VirtualPC' + elif ": Microsoft" in output and "Virtual Machine" in output: + grains["virtual"] = "VirtualPC" # Manufacturer: Parallels Software International Inc. - elif 'Parallels Software' in output: - grains['virtual'] = 'Parallels' - elif 'Manufacturer: Google' in output: - grains['virtual'] = 'kvm' + elif "Parallels Software" in output: + grains["virtual"] = "Parallels" + elif "Manufacturer: Google" in output: + grains["virtual"] = "kvm" # Proxmox KVM - elif 'Vendor: SeaBIOS' in output: - grains['virtual'] = 'kvm' + elif "Vendor: SeaBIOS" in output: + grains["virtual"] = "kvm" # Break out of the loop, lspci parsing is not necessary break - elif command == 'lspci': + elif command == "lspci": # dmidecode not available or the user does not have the necessary # permissions model = output.lower() - if 'vmware' in model: - grains['virtual'] = 'VMware' + if "vmware" in model: + grains["virtual"] = "VMware" # 00:04.0 System peripheral: InnoTek Systemberatung GmbH # VirtualBox Guest Service - elif 'virtualbox' in model: - grains['virtual'] = 'VirtualBox' - elif 'qemu' in model: - grains['virtual'] = 'kvm' - elif 'virtio' in model: - grains['virtual'] = 'kvm' + elif "virtualbox" in model: + grains["virtual"] = "VirtualBox" + elif "qemu" in model: + grains["virtual"] = "kvm" + elif "virtio" in model: + grains["virtual"] = "kvm" # Break out of the loop so the next log message is not issued break - elif command == 'prtdiag': + elif command == "prtdiag": model = output.lower().split("\n")[0] - if 'vmware' in model: - grains['virtual'] = 'VMware' - elif 'virtualbox' in model: - grains['virtual'] = 'VirtualBox' - elif 'qemu' in model: - grains['virtual'] = 'kvm' - elif 'joyent smartdc hvm' in model: - grains['virtual'] = 'kvm' + if "vmware" in model: + grains["virtual"] = "VMware" + elif "virtualbox" in model: + grains["virtual"] = "VirtualBox" + elif "qemu" in model: + grains["virtual"] = "kvm" + elif "joyent smartdc hvm" in model: + grains["virtual"] = "kvm" break - elif command == 'virtinfo': - grains['virtual'] = 'LDOM' + elif command == "virtinfo": + grains["virtual"] = "LDOM" break - choices = ('Linux', 'HP-UX') + choices = ("Linux", "HP-UX") isdir = os.path.isdir - sysctl = salt.utils.path.which('sysctl') - if osdata['kernel'] in choices: - if os.path.isdir('/proc'): + sysctl = salt.utils.path.which("sysctl") + if osdata["kernel"] in choices: + if os.path.isdir("/proc"): try: - self_root = os.stat('/') - init_root = os.stat('/proc/1/root/.') + self_root = os.stat("/") + init_root = os.stat("/proc/1/root/.") if self_root != init_root: - grains['virtual_subtype'] = 'chroot' + grains["virtual_subtype"] = "chroot" except (IOError, OSError): pass - if isdir('/proc/vz'): - if os.path.isfile('/proc/vz/version'): - grains['virtual'] = 'openvzhn' - elif os.path.isfile('/proc/vz/veinfo'): - grains['virtual'] = 'openvzve' + if isdir("/proc/vz"): + if os.path.isfile("/proc/vz/version"): + grains["virtual"] = "openvzhn" + elif os.path.isfile("/proc/vz/veinfo"): + grains["virtual"] = "openvzve" # a posteriori, it's expected for these to have failed: - failed_commands.discard('lspci') - failed_commands.discard('dmidecode') + failed_commands.discard("lspci") + failed_commands.discard("dmidecode") # Provide additional detection for OpenVZ - if os.path.isfile('/proc/self/status'): - with salt.utils.files.fopen('/proc/self/status') as status_file: - vz_re = re.compile(r'^envID:\s+(\d+)$') + if os.path.isfile("/proc/self/status"): + with salt.utils.files.fopen("/proc/self/status") as status_file: + vz_re = re.compile(r"^envID:\s+(\d+)$") for line in status_file: - vz_match = vz_re.match(line.rstrip('\n')) + vz_match = vz_re.match(line.rstrip("\n")) if vz_match and int(vz_match.groups()[0]) != 0: - grains['virtual'] = 'openvzve' + grains["virtual"] = "openvzve" elif vz_match and int(vz_match.groups()[0]) == 0: - grains['virtual'] = 'openvzhn' - if isdir('/proc/sys/xen') or \ - isdir('/sys/bus/xen') or isdir('/proc/xen'): - if os.path.isfile('/proc/xen/xsd_kva'): + grains["virtual"] = "openvzhn" + if isdir("/proc/sys/xen") or isdir("/sys/bus/xen") or isdir("/proc/xen"): + if os.path.isfile("/proc/xen/xsd_kva"): # Tested on CentOS 5.3 / 2.6.18-194.26.1.el5xen # Tested on CentOS 5.4 / 2.6.18-164.15.1.el5xen - grains['virtual_subtype'] = 'Xen Dom0' + grains["virtual_subtype"] = "Xen Dom0" else: - if osdata.get('productname', '') == 'HVM domU': + if osdata.get("productname", "") == "HVM domU": # Requires dmidecode! - grains['virtual_subtype'] = 'Xen HVM DomU' - elif os.path.isfile('/proc/xen/capabilities') and \ - os.access('/proc/xen/capabilities', os.R_OK): - with salt.utils.files.fopen('/proc/xen/capabilities') as fhr: - if 'control_d' not in fhr.read(): + grains["virtual_subtype"] = "Xen HVM DomU" + elif os.path.isfile("/proc/xen/capabilities") and os.access( + "/proc/xen/capabilities", os.R_OK + ): + with salt.utils.files.fopen("/proc/xen/capabilities") as fhr: + if "control_d" not in fhr.read(): # Tested on CentOS 5.5 / 2.6.18-194.3.1.el5xen - grains['virtual_subtype'] = 'Xen PV DomU' + grains["virtual_subtype"] = "Xen PV DomU" else: # Shouldn't get to this, but just in case - grains['virtual_subtype'] = 'Xen Dom0' + grains["virtual_subtype"] = "Xen Dom0" # Tested on Fedora 10 / 2.6.27.30-170.2.82 with xen # Tested on Fedora 15 / 2.6.41.4-1 without running xen - elif isdir('/sys/bus/xen'): - if 'xen:' in __salt__['cmd.run']('dmesg').lower(): - grains['virtual_subtype'] = 'Xen PV DomU' - elif os.path.isfile('/sys/bus/xen/drivers/xenconsole'): + elif isdir("/sys/bus/xen"): + if "xen:" in __salt__["cmd.run"]("dmesg").lower(): + grains["virtual_subtype"] = "Xen PV DomU" + elif os.path.isfile("/sys/bus/xen/drivers/xenconsole"): # An actual DomU will have the xenconsole driver - grains['virtual_subtype'] = 'Xen PV DomU' + grains["virtual_subtype"] = "Xen PV DomU" # If a Dom0 or DomU was detected, obviously this is xen - if 'dom' in grains.get('virtual_subtype', '').lower(): - grains['virtual'] = 'xen' + if "dom" in grains.get("virtual_subtype", "").lower(): + grains["virtual"] = "xen" # Check container type after hypervisors, to avoid variable overwrite on containers running in virtual environment. - if os.path.isfile('/proc/1/cgroup'): + if os.path.isfile("/proc/1/cgroup"): try: - with salt.utils.files.fopen('/proc/1/cgroup', 'r') as fhr: + with salt.utils.files.fopen("/proc/1/cgroup", "r") as fhr: fhr_contents = fhr.read() - if ':/lxc/' in fhr_contents: - grains['virtual'] = 'container' - grains['virtual_subtype'] = 'LXC' - elif ':/kubepods/' in fhr_contents: - grains['virtual_subtype'] = 'kubernetes' - elif ':/libpod_parent/' in fhr_contents: - grains['virtual_subtype'] = 'libpod' + if ":/lxc/" in fhr_contents: + grains["virtual"] = "container" + grains["virtual_subtype"] = "LXC" + elif ":/kubepods/" in fhr_contents: + grains["virtual_subtype"] = "kubernetes" + elif ":/libpod_parent/" in fhr_contents: + grains["virtual_subtype"] = "libpod" else: - if any(x in fhr_contents - for x in (':/system.slice/docker', ':/docker/', - ':/docker-ce/')): - grains['virtual'] = 'container' - grains['virtual_subtype'] = 'Docker' + if any( + x in fhr_contents + for x in (":/system.slice/docker", ":/docker/", ":/docker-ce/") + ): + grains["virtual"] = "container" + grains["virtual_subtype"] = "Docker" except IOError: pass - if os.path.isfile('/proc/cpuinfo'): - with salt.utils.files.fopen('/proc/cpuinfo', 'r') as fhr: - if 'QEMU Virtual CPU' in fhr.read(): - grains['virtual'] = 'kvm' - if os.path.isfile('/sys/devices/virtual/dmi/id/product_name'): + if os.path.isfile("/proc/cpuinfo"): + with salt.utils.files.fopen("/proc/cpuinfo", "r") as fhr: + if "QEMU Virtual CPU" in fhr.read(): + grains["virtual"] = "kvm" + if os.path.isfile("/sys/devices/virtual/dmi/id/product_name"): try: - with salt.utils.files.fopen('/sys/devices/virtual/dmi/id/product_name', 'r') as fhr: - output = salt.utils.stringutils.to_unicode(fhr.read(), errors='replace') - if 'VirtualBox' in output: - grains['virtual'] = 'VirtualBox' - elif 'RHEV Hypervisor' in output: - grains['virtual'] = 'kvm' - grains['virtual_subtype'] = 'rhev' - elif 'oVirt Node' in output: - grains['virtual'] = 'kvm' - grains['virtual_subtype'] = 'ovirt' - elif 'Google' in output: - grains['virtual'] = 'gce' - elif 'BHYVE' in output: - grains['virtual'] = 'bhyve' + with salt.utils.files.fopen( + "/sys/devices/virtual/dmi/id/product_name", "r" + ) as fhr: + output = salt.utils.stringutils.to_unicode( + fhr.read(), errors="replace" + ) + if "VirtualBox" in output: + grains["virtual"] = "VirtualBox" + elif "RHEV Hypervisor" in output: + grains["virtual"] = "kvm" + grains["virtual_subtype"] = "rhev" + elif "oVirt Node" in output: + grains["virtual"] = "kvm" + grains["virtual_subtype"] = "ovirt" + elif "Google" in output: + grains["virtual"] = "gce" + elif "BHYVE" in output: + grains["virtual"] = "bhyve" except IOError: pass - elif osdata['kernel'] == 'FreeBSD': - kenv = salt.utils.path.which('kenv') + elif osdata["kernel"] == "FreeBSD": + kenv = salt.utils.path.which("kenv") if kenv: - product = __salt__['cmd.run']( - '{0} smbios.system.product'.format(kenv) - ) - maker = __salt__['cmd.run']( - '{0} smbios.system.maker'.format(kenv) - ) - if product.startswith('VMware'): - grains['virtual'] = 'VMware' - if product.startswith('VirtualBox'): - grains['virtual'] = 'VirtualBox' - if maker.startswith('Xen'): - grains['virtual_subtype'] = '{0} {1}'.format(maker, product) - grains['virtual'] = 'xen' - if maker.startswith('Microsoft') and product.startswith('Virtual'): - grains['virtual'] = 'VirtualPC' - if maker.startswith('OpenStack'): - grains['virtual'] = 'OpenStack' - if maker.startswith('Bochs'): - grains['virtual'] = 'kvm' + product = __salt__["cmd.run"]("{0} smbios.system.product".format(kenv)) + maker = __salt__["cmd.run"]("{0} smbios.system.maker".format(kenv)) + if product.startswith("VMware"): + grains["virtual"] = "VMware" + if product.startswith("VirtualBox"): + grains["virtual"] = "VirtualBox" + if maker.startswith("Xen"): + grains["virtual_subtype"] = "{0} {1}".format(maker, product) + grains["virtual"] = "xen" + if maker.startswith("Microsoft") and product.startswith("Virtual"): + grains["virtual"] = "VirtualPC" + if maker.startswith("OpenStack"): + grains["virtual"] = "OpenStack" + if maker.startswith("Bochs"): + grains["virtual"] = "kvm" if sysctl: - hv_vendor = __salt__['cmd.run']('{0} -n hw.hv_vendor'.format(sysctl)) - model = __salt__['cmd.run']('{0} -n hw.model'.format(sysctl)) - jail = __salt__['cmd.run']( - '{0} -n security.jail.jailed'.format(sysctl) - ) - if 'bhyve' in hv_vendor: - grains['virtual'] = 'bhyve' - elif 'QEMU Virtual CPU' in model: - grains['virtual'] = 'kvm' - if jail == '1': - grains['virtual_subtype'] = 'jail' - elif osdata['kernel'] == 'OpenBSD': - if 'manufacturer' in osdata: - if osdata['manufacturer'] in ['QEMU', 'Red Hat', 'Joyent']: - grains['virtual'] = 'kvm' - if osdata['manufacturer'] == 'OpenBSD': - grains['virtual'] = 'vmm' - elif osdata['kernel'] == 'SunOS': - if grains['virtual'] == 'LDOM': + hv_vendor = __salt__["cmd.run"]("{0} -n hw.hv_vendor".format(sysctl)) + model = __salt__["cmd.run"]("{0} -n hw.model".format(sysctl)) + jail = __salt__["cmd.run"]("{0} -n security.jail.jailed".format(sysctl)) + if "bhyve" in hv_vendor: + grains["virtual"] = "bhyve" + elif "QEMU Virtual CPU" in model: + grains["virtual"] = "kvm" + if jail == "1": + grains["virtual_subtype"] = "jail" + elif osdata["kernel"] == "OpenBSD": + if "manufacturer" in osdata: + if osdata["manufacturer"] in ["QEMU", "Red Hat", "Joyent"]: + grains["virtual"] = "kvm" + if osdata["manufacturer"] == "OpenBSD": + grains["virtual"] = "vmm" + elif osdata["kernel"] == "SunOS": + if grains["virtual"] == "LDOM": roles = [] - for role in ('control', 'io', 'root', 'service'): - subtype_cmd = '{0} -c current get -H -o value {1}-role'.format(cmd, role) - ret = __salt__['cmd.run_all']('{0}'.format(subtype_cmd)) - if ret['stdout'] == 'true': + for role in ("control", "io", "root", "service"): + subtype_cmd = "{0} -c current get -H -o value {1}-role".format( + cmd, role + ) + ret = __salt__["cmd.run_all"]("{0}".format(subtype_cmd)) + if ret["stdout"] == "true": roles.append(role) if roles: - grains['virtual_subtype'] = roles + grains["virtual_subtype"] = roles else: # Check if it's a "regular" zone. (i.e. Solaris 10/11 zone) - zonename = salt.utils.path.which('zonename') + zonename = salt.utils.path.which("zonename") if zonename: - zone = __salt__['cmd.run']('{0}'.format(zonename)) - if zone != 'global': - grains['virtual'] = 'zone' + zone = __salt__["cmd.run"]("{0}".format(zonename)) + if zone != "global": + grains["virtual"] = "zone" # Check if it's a branded zone (i.e. Solaris 8/9 zone) - if isdir('/.SUNWnative'): - grains['virtual'] = 'zone' - elif osdata['kernel'] == 'NetBSD': + if isdir("/.SUNWnative"): + grains["virtual"] = "zone" + elif osdata["kernel"] == "NetBSD": if sysctl: - if 'QEMU Virtual CPU' in __salt__['cmd.run']( - '{0} -n machdep.cpu_brand'.format(sysctl)): - grains['virtual'] = 'kvm' - elif 'invalid' not in __salt__['cmd.run']( - '{0} -n machdep.xen.suspend'.format(sysctl)): - grains['virtual'] = 'Xen PV DomU' - elif 'VMware' in __salt__['cmd.run']( - '{0} -n machdep.dmi.system-vendor'.format(sysctl)): - grains['virtual'] = 'VMware' + if "QEMU Virtual CPU" in __salt__["cmd.run"]( + "{0} -n machdep.cpu_brand".format(sysctl) + ): + grains["virtual"] = "kvm" + elif "invalid" not in __salt__["cmd.run"]( + "{0} -n machdep.xen.suspend".format(sysctl) + ): + grains["virtual"] = "Xen PV DomU" + elif "VMware" in __salt__["cmd.run"]( + "{0} -n machdep.dmi.system-vendor".format(sysctl) + ): + grains["virtual"] = "VMware" # NetBSD has Xen dom0 support - elif __salt__['cmd.run']( - '{0} -n machdep.idle-mechanism'.format(sysctl)) == 'xen': - if os.path.isfile('/var/run/xenconsoled.pid'): - grains['virtual_subtype'] = 'Xen Dom0' + elif ( + __salt__["cmd.run"]("{0} -n machdep.idle-mechanism".format(sysctl)) + == "xen" + ): + if os.path.isfile("/var/run/xenconsoled.pid"): + grains["virtual_subtype"] = "Xen Dom0" # If we have a virtual_subtype, we're virtual, but maybe we couldn't # figure out what specific virtual type we were? - if grains.get('virtual_subtype') and grains['virtual'] == 'physical': - grains['virtual'] = 'virtual' + if grains.get("virtual_subtype") and grains["virtual"] == "physical": + grains["virtual"] = "virtual" for command in failed_commands: log.info( "Although '%s' was found in path, the current user " - 'cannot execute it. Grains output might not be ' - 'accurate.', command + "cannot execute it. Grains output might not be " + "accurate.", + command, ) return grains def _virtual_hv(osdata): - ''' + """ Returns detailed hypervisor information from sysfs Currently this seems to be used only by Xen - ''' + """ grains = {} # Bail early if we're not running on Xen try: - if 'xen' not in osdata['virtual']: + if "xen" not in osdata["virtual"]: return grains except KeyError: return grains @@ -1101,40 +1175,50 @@ def _virtual_hv(osdata): # Try to get the exact hypervisor version from sysfs try: version = {} - for fn in ('major', 'minor', 'extra'): - with salt.utils.files.fopen('/sys/hypervisor/version/{}'.format(fn), 'r') as fhr: + for fn in ("major", "minor", "extra"): + with salt.utils.files.fopen( + "/sys/hypervisor/version/{}".format(fn), "r" + ) as fhr: version[fn] = salt.utils.stringutils.to_unicode(fhr.read().strip()) - grains['virtual_hv_version'] = '{}.{}{}'.format(version['major'], version['minor'], version['extra']) - grains['virtual_hv_version_info'] = [version['major'], version['minor'], version['extra']] + grains["virtual_hv_version"] = "{}.{}{}".format( + version["major"], version["minor"], version["extra"] + ) + grains["virtual_hv_version_info"] = [ + version["major"], + version["minor"], + version["extra"], + ] except (IOError, OSError, KeyError): pass # Try to read and decode the supported feature set of the hypervisor # Based on https://github.com/brendangregg/Misc/blob/master/xen/xen-features.py # Table data from include/xen/interface/features.h - xen_feature_table = {0: 'writable_page_tables', - 1: 'writable_descriptor_tables', - 2: 'auto_translated_physmap', - 3: 'supervisor_mode_kernel', - 4: 'pae_pgdir_above_4gb', - 5: 'mmu_pt_update_preserve_ad', - 7: 'gnttab_map_avail_bits', - 8: 'hvm_callback_vector', - 9: 'hvm_safe_pvclock', - 10: 'hvm_pirqs', - 11: 'dom0', - 12: 'grant_map_identity', - 13: 'memory_op_vnode_supported', - 14: 'ARM_SMCCC_supported'} + xen_feature_table = { + 0: "writable_page_tables", + 1: "writable_descriptor_tables", + 2: "auto_translated_physmap", + 3: "supervisor_mode_kernel", + 4: "pae_pgdir_above_4gb", + 5: "mmu_pt_update_preserve_ad", + 7: "gnttab_map_avail_bits", + 8: "hvm_callback_vector", + 9: "hvm_safe_pvclock", + 10: "hvm_pirqs", + 11: "dom0", + 12: "grant_map_identity", + 13: "memory_op_vnode_supported", + 14: "ARM_SMCCC_supported", + } try: - with salt.utils.files.fopen('/sys/hypervisor/properties/features', 'r') as fhr: + with salt.utils.files.fopen("/sys/hypervisor/properties/features", "r") as fhr: features = salt.utils.stringutils.to_unicode(fhr.read().strip()) enabled_features = [] for bit, feat in six.iteritems(xen_feature_table): if int(features, 16) & (1 << bit): enabled_features.append(feat) - grains['virtual_hv_features'] = features - grains['virtual_hv_features_list'] = enabled_features + grains["virtual_hv_features"] = features + grains["virtual_hv_features_list"] = enabled_features except (IOError, OSError, KeyError): pass @@ -1142,45 +1226,44 @@ def _virtual_hv(osdata): def _ps(osdata): - ''' + """ Return the ps grain - ''' + """ grains = {} - bsd_choices = ('FreeBSD', 'NetBSD', 'OpenBSD', 'MacOS') - if osdata['os'] in bsd_choices: - grains['ps'] = 'ps auxwww' - elif osdata['os_family'] == 'Solaris': - grains['ps'] = '/usr/ucb/ps auxwww' - elif osdata['os'] == 'Windows': - grains['ps'] = 'tasklist.exe' - elif osdata.get('virtual', '') == 'openvzhn': - grains['ps'] = ( - 'ps -fH -p $(grep -l \"^envID:[[:space:]]*0\\$\" ' - '/proc/[0-9]*/status | sed -e \"s=/proc/\\([0-9]*\\)/.*=\\1=\") ' - '| awk \'{ $7=\"\"; print }\'' + bsd_choices = ("FreeBSD", "NetBSD", "OpenBSD", "MacOS") + if osdata["os"] in bsd_choices: + grains["ps"] = "ps auxwww" + elif osdata["os_family"] == "Solaris": + grains["ps"] = "/usr/ucb/ps auxwww" + elif osdata["os"] == "Windows": + grains["ps"] = "tasklist.exe" + elif osdata.get("virtual", "") == "openvzhn": + grains["ps"] = ( + 'ps -fH -p $(grep -l "^envID:[[:space:]]*0\\$" ' + '/proc/[0-9]*/status | sed -e "s=/proc/\\([0-9]*\\)/.*=\\1=") ' + "| awk '{ $7=\"\"; print }'" ) - elif osdata['os_family'] == 'AIX': - grains['ps'] = '/usr/bin/ps auxww' - elif osdata['os_family'] == 'NILinuxRT': - grains['ps'] = 'ps -o user,pid,ppid,tty,time,comm' + elif osdata["os_family"] == "AIX": + grains["ps"] = "/usr/bin/ps auxww" + elif osdata["os_family"] == "NILinuxRT": + grains["ps"] = "ps -o user,pid,ppid,tty,time,comm" else: - grains['ps'] = 'ps -efHww' + grains["ps"] = "ps -efHww" return grains def _clean_value(key, val): - ''' + """ Clean out well-known bogus values. If it isn't clean (for example has value 'None'), return None. Otherwise, return the original value. NOTE: This logic also exists in the smbios module. This function is for use when not using smbios to retrieve the value. - ''' - if (val is None or not val or - re.match('none', val, flags=re.IGNORECASE)): + """ + if val is None or not val or re.match("none", val, flags=re.IGNORECASE): return None - elif 'uuid' in key: + elif "uuid" in key: # Try each version (1-5) of RFC4122 to check if it's actually a UUID for uuidver in range(1, 5): try: @@ -1188,69 +1271,83 @@ def _clean_value(key, val): return val except ValueError: continue - log.trace('HW %s value %s is an invalid UUID', key, val.replace('\n', ' ')) + log.trace("HW %s value %s is an invalid UUID", key, val.replace("\n", " ")) return None - elif re.search('serial|part|version', key): + elif re.search("serial|part|version", key): # 'To be filled by O.E.M. # 'Not applicable' etc. # 'Not specified' etc. # 0000000, 1234567 etc. # begone! - if (re.match(r'^[0]+$', val) or - re.match(r'[0]?1234567[8]?[9]?[0]?', val) or - re.search(r'sernum|part[_-]?number|specified|filled|applicable', val, flags=re.IGNORECASE)): + if ( + re.match(r"^[0]+$", val) + or re.match(r"[0]?1234567[8]?[9]?[0]?", val) + or re.search( + r"sernum|part[_-]?number|specified|filled|applicable", + val, + flags=re.IGNORECASE, + ) + ): return None - elif re.search('asset|manufacturer', key): + elif re.search("asset|manufacturer", key): # AssetTag0. Manufacturer04. Begone. - if re.search(r'manufacturer|to be filled|available|asset|^no(ne|t)', val, flags=re.IGNORECASE): + if re.search( + r"manufacturer|to be filled|available|asset|^no(ne|t)", + val, + flags=re.IGNORECASE, + ): return None else: # map unspecified, undefined, unknown & whatever to None - if (re.search(r'to be filled', val, flags=re.IGNORECASE) or - re.search(r'un(known|specified)|no(t|ne)? (asset|provided|defined|available|present|specified)', - val, flags=re.IGNORECASE)): + if re.search(r"to be filled", val, flags=re.IGNORECASE) or re.search( + r"un(known|specified)|no(t|ne)? (asset|provided|defined|available|present|specified)", + val, + flags=re.IGNORECASE, + ): return None return val def _windows_os_release_grain(caption, product_type): - ''' + """ helper function for getting the osrelease grain :return: - ''' + """ # This creates the osrelease grain based on the Windows Operating # System Product Name. As long as Microsoft maintains a similar format # this should be future proof - version = 'Unknown' - release = '' - if 'Server' in caption: - for item in caption.split(' '): + version = "Unknown" + release = "" + if "Server" in caption: + for item in caption.split(" "): # If it's all digits, then it's version - if re.match(r'\d+', item): + if re.match(r"\d+", item): version = item # If it starts with R and then numbers, it's the release # ie: R2 - if re.match(r'^R\d+$', item): + if re.match(r"^R\d+$", item): release = item - os_release = '{0}Server{1}'.format(version, release) + os_release = "{0}Server{1}".format(version, release) else: - for item in caption.split(' '): + for item in caption.split(" "): # If it's a number, decimal number, Thin or Vista, then it's the # version - if re.match(r'^(\d+(\.\d+)?)|Thin|Vista|XP$', item): + if re.match(r"^(\d+(\.\d+)?)|Thin|Vista|XP$", item): version = item os_release = version # If the version is still Unknown, revert back to the old way of getting # the os_release # https://github.com/saltstack/salt/issues/52339 - if os_release in ['Unknown']: + if os_release in ["Unknown"]: os_release = platform.release() - server = {'Vista': '2008Server', - '7': '2008ServerR2', - '8': '2012Server', - '8.1': '2012ServerR2', - '10': '2016Server'} + server = { + "Vista": "2008Server", + "7": "2008ServerR2", + "8": "2012Server", + "8.1": "2012ServerR2", + "10": "2016Server", + } # Starting with Python 2.7.12 and 3.5.2 the `platform.uname()` # function started reporting the Desktop version instead of the @@ -1265,9 +1362,9 @@ def _windows_os_release_grain(caption, product_type): def _windows_platform_data(): - ''' + """ Use the platform module for as much as we can. - ''' + """ # Provides: # kernelrelease # kernelversion @@ -1302,145 +1399,151 @@ def _windows_platform_data(): timeinfo = wmi_c.Win32_TimeZone()[0] # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394072(v=vs.85).aspx - motherboard = {'product': None, - 'serial': None} + motherboard = {"product": None, "serial": None} try: motherboardinfo = wmi_c.Win32_BaseBoard()[0] - motherboard['product'] = motherboardinfo.Product - motherboard['serial'] = motherboardinfo.SerialNumber + motherboard["product"] = motherboardinfo.Product + motherboard["serial"] = motherboardinfo.SerialNumber except IndexError: - log.debug('Motherboard info not available on this system') + log.debug("Motherboard info not available on this system") kernel_version = platform.version() info = salt.utils.win_osinfo.get_os_version_info() net_info = salt.utils.win_osinfo.get_join_info() service_pack = None - if info['ServicePackMajor'] > 0: - service_pack = ''.join(['SP', six.text_type(info['ServicePackMajor'])]) + if info["ServicePackMajor"] > 0: + service_pack = "".join(["SP", six.text_type(info["ServicePackMajor"])]) - os_release = _windows_os_release_grain(caption=osinfo.Caption, - product_type=osinfo.ProductType) + os_release = _windows_os_release_grain( + caption=osinfo.Caption, product_type=osinfo.ProductType + ) grains = { - 'kernelrelease': _clean_value('kernelrelease', osinfo.Version), - 'kernelversion': _clean_value('kernelversion', kernel_version), - 'osversion': _clean_value('osversion', osinfo.Version), - 'osrelease': _clean_value('osrelease', os_release), - 'osservicepack': _clean_value('osservicepack', service_pack), - 'osmanufacturer': _clean_value('osmanufacturer', osinfo.Manufacturer), - 'manufacturer': _clean_value('manufacturer', systeminfo.Manufacturer), - 'productname': _clean_value('productname', systeminfo.Model), + "kernelrelease": _clean_value("kernelrelease", osinfo.Version), + "kernelversion": _clean_value("kernelversion", kernel_version), + "osversion": _clean_value("osversion", osinfo.Version), + "osrelease": _clean_value("osrelease", os_release), + "osservicepack": _clean_value("osservicepack", service_pack), + "osmanufacturer": _clean_value("osmanufacturer", osinfo.Manufacturer), + "manufacturer": _clean_value("manufacturer", systeminfo.Manufacturer), + "productname": _clean_value("productname", systeminfo.Model), # bios name had a bunch of whitespace appended to it in my testing # 'PhoenixBIOS 4.0 Release 6.0 ' - 'biosversion': _clean_value('biosversion', biosinfo.Name.strip()), - 'serialnumber': _clean_value('serialnumber', biosinfo.SerialNumber), - 'osfullname': _clean_value('osfullname', osinfo.Caption), - 'timezone': _clean_value('timezone', timeinfo.Description), - 'windowsdomain': _clean_value('windowsdomain', net_info['Domain']), - 'windowsdomaintype': _clean_value('windowsdomaintype', net_info['DomainType']), - 'motherboard': { - 'productname': _clean_value('motherboard.productname', motherboard['product']), - 'serialnumber': _clean_value('motherboard.serialnumber', motherboard['serial']), - } + "biosversion": _clean_value("biosversion", biosinfo.Name.strip()), + "serialnumber": _clean_value("serialnumber", biosinfo.SerialNumber), + "osfullname": _clean_value("osfullname", osinfo.Caption), + "timezone": _clean_value("timezone", timeinfo.Description), + "windowsdomain": _clean_value("windowsdomain", net_info["Domain"]), + "windowsdomaintype": _clean_value( + "windowsdomaintype", net_info["DomainType"] + ), + "motherboard": { + "productname": _clean_value( + "motherboard.productname", motherboard["product"] + ), + "serialnumber": _clean_value( + "motherboard.serialnumber", motherboard["serial"] + ), + }, } # test for virtualized environments # I only had VMware available so the rest are unvalidated - if 'VRTUAL' in biosinfo.Version: # (not a typo) - grains['virtual'] = 'HyperV' - elif 'A M I' in biosinfo.Version: - grains['virtual'] = 'VirtualPC' - elif 'VMware' in systeminfo.Model: - grains['virtual'] = 'VMware' - elif 'VirtualBox' in systeminfo.Model: - grains['virtual'] = 'VirtualBox' - elif 'Xen' in biosinfo.Version: - grains['virtual'] = 'Xen' - if 'HVM domU' in systeminfo.Model: - grains['virtual_subtype'] = 'HVM domU' - elif 'OpenStack' in systeminfo.Model: - grains['virtual'] = 'OpenStack' - elif 'AMAZON' in biosinfo.Version: - grains['virtual'] = 'EC2' + if "VRTUAL" in biosinfo.Version: # (not a typo) + grains["virtual"] = "HyperV" + elif "A M I" in biosinfo.Version: + grains["virtual"] = "VirtualPC" + elif "VMware" in systeminfo.Model: + grains["virtual"] = "VMware" + elif "VirtualBox" in systeminfo.Model: + grains["virtual"] = "VirtualBox" + elif "Xen" in biosinfo.Version: + grains["virtual"] = "Xen" + if "HVM domU" in systeminfo.Model: + grains["virtual_subtype"] = "HVM domU" + elif "OpenStack" in systeminfo.Model: + grains["virtual"] = "OpenStack" + elif "AMAZON" in biosinfo.Version: + grains["virtual"] = "EC2" return grains def _osx_platform_data(): - ''' + """ Additional data for macOS systems Returns: A dictionary containing values for the following: - model_name - boot_rom_version - smc_version - system_serialnumber - ''' - cmd = 'system_profiler SPHardwareDataType' - hardware = __salt__['cmd.run'](cmd) + """ + cmd = "system_profiler SPHardwareDataType" + hardware = __salt__["cmd.run"](cmd) grains = {} for line in hardware.splitlines(): - field_name, _, field_val = line.partition(': ') + field_name, _, field_val = line.partition(": ") if field_name.strip() == "Model Name": - key = 'model_name' + key = "model_name" grains[key] = _clean_value(key, field_val) if field_name.strip() == "Boot ROM Version": - key = 'boot_rom_version' + key = "boot_rom_version" grains[key] = _clean_value(key, field_val) if field_name.strip() == "SMC Version (system)": - key = 'smc_version' + key = "smc_version" grains[key] = _clean_value(key, field_val) if field_name.strip() == "Serial Number (system)": - key = 'system_serialnumber' + key = "system_serialnumber" grains[key] = _clean_value(key, field_val) return grains def id_(): - ''' + """ Return the id - ''' - return {'id': __opts__.get('id', '')} + """ + return {"id": __opts__.get("id", "")} -_REPLACE_LINUX_RE = re.compile(r'\W(?:gnu/)?linux', re.IGNORECASE) +_REPLACE_LINUX_RE = re.compile(r"\W(?:gnu/)?linux", re.IGNORECASE) # This maps (at most) the first ten characters (no spaces, lowercased) of # 'osfullname' to the 'os' grain that Salt traditionally uses. # Please see os_data() and _supported_dists. # If your system is not detecting properly it likely needs an entry here. _OS_NAME_MAP = { - 'redhatente': 'RedHat', - 'gentoobase': 'Gentoo', - 'archarm': 'Arch ARM', - 'arch': 'Arch', - 'debian': 'Debian', - 'raspbian': 'Raspbian', - 'fedoraremi': 'Fedora', - 'chapeau': 'Chapeau', - 'korora': 'Korora', - 'amazonami': 'Amazon', - 'alt': 'ALT', - 'enterprise': 'OEL', - 'oracleserv': 'OEL', - 'cloudserve': 'CloudLinux', - 'cloudlinux': 'CloudLinux', - 'pidora': 'Fedora', - 'scientific': 'ScientificLinux', - 'synology': 'Synology', - 'nilrt': 'NILinuxRT', - 'poky': 'Poky', - 'manjaro': 'Manjaro', - 'manjarolin': 'Manjaro', - 'univention': 'Univention', - 'antergos': 'Antergos', - 'sles': 'SUSE', - 'void': 'Void', - 'slesexpand': 'RES', - 'linuxmint': 'Mint', - 'neon': 'KDE neon', + "redhatente": "RedHat", + "gentoobase": "Gentoo", + "archarm": "Arch ARM", + "arch": "Arch", + "debian": "Debian", + "raspbian": "Raspbian", + "fedoraremi": "Fedora", + "chapeau": "Chapeau", + "korora": "Korora", + "amazonami": "Amazon", + "alt": "ALT", + "enterprise": "OEL", + "oracleserv": "OEL", + "cloudserve": "CloudLinux", + "cloudlinux": "CloudLinux", + "pidora": "Fedora", + "scientific": "ScientificLinux", + "synology": "Synology", + "nilrt": "NILinuxRT", + "poky": "Poky", + "manjaro": "Manjaro", + "manjarolin": "Manjaro", + "univention": "Univention", + "antergos": "Antergos", + "sles": "SUSE", + "void": "Void", + "slesexpand": "RES", + "linuxmint": "Mint", + "neon": "KDE neon", } # Map the 'os' grain to the 'os_family' grain @@ -1448,71 +1551,71 @@ _OS_NAME_MAP = { # post-_OS_NAME_MAP. If your system is having trouble with detection, please # make sure that the 'os' grain is capitalized and working correctly first. _OS_FAMILY_MAP = { - 'Ubuntu': 'Debian', - 'Fedora': 'RedHat', - 'Chapeau': 'RedHat', - 'Korora': 'RedHat', - 'FedBerry': 'RedHat', - 'CentOS': 'RedHat', - 'GoOSe': 'RedHat', - 'Scientific': 'RedHat', - 'Amazon': 'RedHat', - 'CloudLinux': 'RedHat', - 'OVS': 'RedHat', - 'OEL': 'RedHat', - 'XCP': 'RedHat', - 'XCP-ng': 'RedHat', - 'XenServer': 'RedHat', - 'RES': 'RedHat', - 'Sangoma': 'RedHat', - 'Mandrake': 'Mandriva', - 'ESXi': 'VMware', - 'Mint': 'Debian', - 'VMwareESX': 'VMware', - 'Bluewhite64': 'Bluewhite', - 'Slamd64': 'Slackware', - 'SLES': 'Suse', - 'SUSE Enterprise Server': 'Suse', - 'SUSE Enterprise Server': 'Suse', - 'SLED': 'Suse', - 'openSUSE': 'Suse', - 'SUSE': 'Suse', - 'openSUSE Leap': 'Suse', - 'openSUSE Tumbleweed': 'Suse', - 'SLES_SAP': 'Suse', - 'Solaris': 'Solaris', - 'SmartOS': 'Solaris', - 'OmniOS': 'Solaris', - 'OpenIndiana Development': 'Solaris', - 'OpenIndiana': 'Solaris', - 'OpenSolaris Development': 'Solaris', - 'OpenSolaris': 'Solaris', - 'Oracle Solaris': 'Solaris', - 'Arch ARM': 'Arch', - 'Manjaro': 'Arch', - 'Antergos': 'Arch', - 'ALT': 'RedHat', - 'Trisquel': 'Debian', - 'GCEL': 'Debian', - 'Linaro': 'Debian', - 'elementary OS': 'Debian', - 'elementary': 'Debian', - 'Univention': 'Debian', - 'ScientificLinux': 'RedHat', - 'Raspbian': 'Debian', - 'Devuan': 'Debian', - 'antiX': 'Debian', - 'Kali': 'Debian', - 'neon': 'Debian', - 'Cumulus': 'Debian', - 'Deepin': 'Debian', - 'NILinuxRT': 'NILinuxRT', - 'KDE neon': 'Debian', - 'Void': 'Void', - 'IDMS': 'Debian', - 'Funtoo': 'Gentoo', - 'AIX': 'AIX', - 'TurnKey': 'Debian', + "Ubuntu": "Debian", + "Fedora": "RedHat", + "Chapeau": "RedHat", + "Korora": "RedHat", + "FedBerry": "RedHat", + "CentOS": "RedHat", + "GoOSe": "RedHat", + "Scientific": "RedHat", + "Amazon": "RedHat", + "CloudLinux": "RedHat", + "OVS": "RedHat", + "OEL": "RedHat", + "XCP": "RedHat", + "XCP-ng": "RedHat", + "XenServer": "RedHat", + "RES": "RedHat", + "Sangoma": "RedHat", + "Mandrake": "Mandriva", + "ESXi": "VMware", + "Mint": "Debian", + "VMwareESX": "VMware", + "Bluewhite64": "Bluewhite", + "Slamd64": "Slackware", + "SLES": "Suse", + "SUSE Enterprise Server": "Suse", + "SUSE Enterprise Server": "Suse", + "SLED": "Suse", + "openSUSE": "Suse", + "SUSE": "Suse", + "openSUSE Leap": "Suse", + "openSUSE Tumbleweed": "Suse", + "SLES_SAP": "Suse", + "Solaris": "Solaris", + "SmartOS": "Solaris", + "OmniOS": "Solaris", + "OpenIndiana Development": "Solaris", + "OpenIndiana": "Solaris", + "OpenSolaris Development": "Solaris", + "OpenSolaris": "Solaris", + "Oracle Solaris": "Solaris", + "Arch ARM": "Arch", + "Manjaro": "Arch", + "Antergos": "Arch", + "ALT": "RedHat", + "Trisquel": "Debian", + "GCEL": "Debian", + "Linaro": "Debian", + "elementary OS": "Debian", + "elementary": "Debian", + "Univention": "Debian", + "ScientificLinux": "RedHat", + "Raspbian": "Debian", + "Devuan": "Debian", + "antiX": "Debian", + "Kali": "Debian", + "neon": "Debian", + "Cumulus": "Debian", + "Deepin": "Debian", + "NILinuxRT": "NILinuxRT", + "KDE neon": "Debian", + "Void": "Void", + "IDMS": "Debian", + "Funtoo": "Gentoo", + "AIX": "AIX", + "TurnKey": "Debian", } # Matches any possible format: @@ -1522,36 +1625,41 @@ _OS_FAMILY_MAP = { # DISTRIB_RELEASE='10.10' # DISTRIB_CODENAME='squeeze' # DISTRIB_DESCRIPTION='Ubuntu 10.10' -_LSB_REGEX = re.compile(( - '^(DISTRIB_(?:ID|RELEASE|CODENAME|DESCRIPTION))=(?:\'|")?' - '([\\w\\s\\.\\-_]+)(?:\'|")?' -)) +_LSB_REGEX = re.compile( + ( + "^(DISTRIB_(?:ID|RELEASE|CODENAME|DESCRIPTION))=(?:'|\")?" + "([\\w\\s\\.\\-_]+)(?:'|\")?" + ) +) def _linux_bin_exists(binary): - ''' + """ Does a binary exist in linux (depends on which, type, or whereis) - ''' - for search_cmd in ('which', 'type -ap'): + """ + for search_cmd in ("which", "type -ap"): try: - return __salt__['cmd.retcode']( - '{0} {1}'.format(search_cmd, binary) - ) == 0 + return __salt__["cmd.retcode"]("{0} {1}".format(search_cmd, binary)) == 0 except salt.exceptions.CommandExecutionError: pass try: - return len(__salt__['cmd.run_all']( - 'whereis -b {0}'.format(binary) - )['stdout'].split()) > 1 + return ( + len( + __salt__["cmd.run_all"]("whereis -b {0}".format(binary))[ + "stdout" + ].split() + ) + > 1 + ) except salt.exceptions.CommandExecutionError: return False def _get_interfaces(): - ''' + """ Provide a dict of the connected interfaces and their ip addresses - ''' + """ global _INTERFACES if not _INTERFACES: @@ -1562,40 +1670,40 @@ def _get_interfaces(): def _parse_lsb_release(): ret = {} try: - log.trace('Attempting to parse /etc/lsb-release') - with salt.utils.files.fopen('/etc/lsb-release') as ifile: + log.trace("Attempting to parse /etc/lsb-release") + with salt.utils.files.fopen("/etc/lsb-release") as ifile: for line in ifile: try: - key, value = _LSB_REGEX.match(line.rstrip('\n')).groups()[:2] + key, value = _LSB_REGEX.match(line.rstrip("\n")).groups()[:2] except AttributeError: pass else: # Adds lsb_distrib_{id,release,codename,description} - ret['lsb_{0}'.format(key.lower())] = value.rstrip() + ret["lsb_{0}".format(key.lower())] = value.rstrip() except (IOError, OSError) as exc: - log.trace('Failed to parse /etc/lsb-release: %s', exc) + log.trace("Failed to parse /etc/lsb-release: %s", exc) return ret def _parse_os_release(*os_release_files): - ''' + """ Parse os-release and return a parameter dictionary See http://www.freedesktop.org/software/systemd/man/os-release.html for specification of the file format. - ''' + """ ret = {} for filename in os_release_files: try: with salt.utils.files.fopen(filename) as ifile: - regex = re.compile('^([\\w]+)=(?:\'|")?(.*?)(?:\'|")?$') + regex = re.compile("^([\\w]+)=(?:'|\")?(.*?)(?:'|\")?$") for line in ifile: match = regex.match(line.strip()) if match: # Shell special characters ("$", quotes, backslash, # backtick) are escaped with backslashes ret[match.group(1)] = re.sub( - r'\\([$"\'\\`])', r'\1', match.group(2) + r'\\([$"\'\\`])', r"\1", match.group(2) ) break except (IOError, OSError): @@ -1605,7 +1713,7 @@ def _parse_os_release(*os_release_files): def _parse_cpe_name(cpe): - ''' + """ Parse CPE_NAME data from the os-release Info: https://csrc.nist.gov/projects/security-content-automation-protocol/scap-specifications/cpe @@ -1616,38 +1724,42 @@ def _parse_cpe_name(cpe): :param cpe: :return: - ''' + """ part = { - 'o': 'operating system', - 'h': 'hardware', - 'a': 'application', + "o": "operating system", + "h": "hardware", + "a": "application", } ret = {} - cpe = (cpe or '').split(':') - if len(cpe) > 4 and cpe[0] == 'cpe': - if cpe[1].startswith('/'): # WFN to URI - ret['vendor'], ret['product'], ret['version'] = cpe[2:5] - ret['phase'] = cpe[5] if len(cpe) > 5 else None - ret['part'] = part.get(cpe[1][1:]) - elif len(cpe) == 6 and cpe[1] == '2.3': # WFN to a string - ret['vendor'], ret['product'], ret['version'] = [x if x != '*' else None for x in cpe[3:6]] - ret['phase'] = None - ret['part'] = part.get(cpe[2]) - elif len(cpe) > 7 and len(cpe) <= 13 and cpe[1] == '2.3': # WFN to a string - ret['vendor'], ret['product'], ret['version'], ret['phase'] = [x if x != '*' else None for x in cpe[3:7]] - ret['part'] = part.get(cpe[2]) + cpe = (cpe or "").split(":") + if len(cpe) > 4 and cpe[0] == "cpe": + if cpe[1].startswith("/"): # WFN to URI + ret["vendor"], ret["product"], ret["version"] = cpe[2:5] + ret["phase"] = cpe[5] if len(cpe) > 5 else None + ret["part"] = part.get(cpe[1][1:]) + elif len(cpe) == 6 and cpe[1] == "2.3": # WFN to a string + ret["vendor"], ret["product"], ret["version"] = [ + x if x != "*" else None for x in cpe[3:6] + ] + ret["phase"] = None + ret["part"] = part.get(cpe[2]) + elif len(cpe) > 7 and len(cpe) <= 13 and cpe[1] == "2.3": # WFN to a string + ret["vendor"], ret["product"], ret["version"], ret["phase"] = [ + x if x != "*" else None for x in cpe[3:7] + ] + ret["part"] = part.get(cpe[2]) return ret def os_data(): - ''' + """ Return grains pertaining to the operating system - ''' + """ grains = { - 'num_gpus': 0, - 'gpus': [], - } + "num_gpus": 0, + "gpus": [], + } # Windows Server 2008 64-bit # ('Windows', 'MINIONNAME', '2008ServerR2', '6.1.7601', 'AMD64', @@ -1657,79 +1769,83 @@ def os_data(): # '#83-Ubuntu SMP Wed Jan 4 11:26:59 UTC 2012', 'x86_64', '') # pylint: disable=unpacking-non-sequence - (grains['kernel'], grains['nodename'], - grains['kernelrelease'], grains['kernelversion'], grains['cpuarch'], _) = platform.uname() + ( + grains["kernel"], + grains["nodename"], + grains["kernelrelease"], + grains["kernelversion"], + grains["cpuarch"], + _, + ) = platform.uname() # pylint: enable=unpacking-non-sequence if salt.utils.platform.is_proxy(): - grains['kernel'] = 'proxy' - grains['kernelrelease'] = 'proxy' - grains['kernelversion'] = 'proxy' - grains['osrelease'] = 'proxy' - grains['os'] = 'proxy' - grains['os_family'] = 'proxy' - grains['osfullname'] = 'proxy' + grains["kernel"] = "proxy" + grains["kernelrelease"] = "proxy" + grains["kernelversion"] = "proxy" + grains["osrelease"] = "proxy" + grains["os"] = "proxy" + grains["os_family"] = "proxy" + grains["osfullname"] = "proxy" elif salt.utils.platform.is_windows(): - grains['os'] = 'Windows' - grains['os_family'] = 'Windows' + grains["os"] = "Windows" + grains["os_family"] = "Windows" grains.update(_memdata(grains)) grains.update(_windows_platform_data()) grains.update(_windows_cpudata()) grains.update(_windows_virtual(grains)) grains.update(_ps(grains)) - if 'Server' in grains['osrelease']: - osrelease_info = grains['osrelease'].split('Server', 1) - osrelease_info[1] = osrelease_info[1].lstrip('R') + if "Server" in grains["osrelease"]: + osrelease_info = grains["osrelease"].split("Server", 1) + osrelease_info[1] = osrelease_info[1].lstrip("R") else: - osrelease_info = grains['osrelease'].split('.') + osrelease_info = grains["osrelease"].split(".") for idx, value in enumerate(osrelease_info): if not value.isdigit(): continue osrelease_info[idx] = int(value) - grains['osrelease_info'] = tuple(osrelease_info) + grains["osrelease_info"] = tuple(osrelease_info) - grains['osfinger'] = '{os}-{ver}'.format( - os=grains['os'], - ver=grains['osrelease']) + grains["osfinger"] = "{os}-{ver}".format( + os=grains["os"], ver=grains["osrelease"] + ) - grains['init'] = 'Windows' + grains["init"] = "Windows" return grains elif salt.utils.platform.is_linux(): # Add SELinux grain, if you have it - if _linux_bin_exists('selinuxenabled'): - log.trace('Adding selinux grains') - grains['selinux'] = {} - grains['selinux']['enabled'] = __salt__['cmd.retcode']( - 'selinuxenabled' - ) == 0 - if _linux_bin_exists('getenforce'): - grains['selinux']['enforced'] = __salt__['cmd.run']( - 'getenforce' + if _linux_bin_exists("selinuxenabled"): + log.trace("Adding selinux grains") + grains["selinux"] = {} + grains["selinux"]["enabled"] = ( + __salt__["cmd.retcode"]("selinuxenabled") == 0 + ) + if _linux_bin_exists("getenforce"): + grains["selinux"]["enforced"] = __salt__["cmd.run"]( + "getenforce" ).strip() # Add systemd grain, if you have it - if _linux_bin_exists('systemctl') and _linux_bin_exists('localectl'): - log.trace('Adding systemd grains') - grains['systemd'] = {} - systemd_info = __salt__['cmd.run']( - 'systemctl --version' - ).splitlines() - grains['systemd']['version'] = systemd_info[0].split()[1] - grains['systemd']['features'] = systemd_info[1] + if _linux_bin_exists("systemctl") and _linux_bin_exists("localectl"): + log.trace("Adding systemd grains") + grains["systemd"] = {} + systemd_info = __salt__["cmd.run"]("systemctl --version").splitlines() + grains["systemd"]["version"] = systemd_info[0].split()[1] + grains["systemd"]["features"] = systemd_info[1] # Add init grain - grains['init'] = 'unknown' - log.trace('Adding init grain') + grains["init"] = "unknown" + log.trace("Adding init grain") try: - os.stat('/run/systemd/system') - grains['init'] = 'systemd' + os.stat("/run/systemd/system") + grains["init"] = "systemd" except (OSError, IOError): try: - with salt.utils.files.fopen('/proc/1/cmdline') as fhr: - init_cmdline = fhr.read().replace('\x00', ' ').split() + with salt.utils.files.fopen("/proc/1/cmdline") as fhr: + init_cmdline = fhr.read().replace("\x00", " ").split() except (IOError, OSError): pass else: @@ -1738,53 +1854,52 @@ def os_data(): except IndexError: # Emtpy init_cmdline init_bin = None - log.warning('Unable to fetch data from /proc/1/cmdline') - if init_bin is not None and init_bin.endswith('bin/init'): - supported_inits = (b'upstart', b'sysvinit', b'systemd') + log.warning("Unable to fetch data from /proc/1/cmdline") + if init_bin is not None and init_bin.endswith("bin/init"): + supported_inits = (b"upstart", b"sysvinit", b"systemd") edge_len = max(len(x) for x in supported_inits) - 1 try: - buf_size = __opts__['file_buffer_size'] + buf_size = __opts__["file_buffer_size"] except KeyError: # Default to the value of file_buffer_size for the minion buf_size = 262144 try: - with salt.utils.files.fopen(init_bin, 'rb') as fp_: - edge = b'' + with salt.utils.files.fopen(init_bin, "rb") as fp_: + edge = b"" buf = fp_.read(buf_size).lower() while buf: buf = edge + buf for item in supported_inits: if item in buf: if six.PY3: - item = item.decode('utf-8') - grains['init'] = item - buf = b'' + item = item.decode("utf-8") + grains["init"] = item + buf = b"" break edge = buf[-edge_len:] buf = fp_.read(buf_size).lower() except (IOError, OSError) as exc: log.error( - 'Unable to read from init_bin (%s): %s', - init_bin, exc + "Unable to read from init_bin (%s): %s", init_bin, exc ) - elif salt.utils.path.which('supervisord') in init_cmdline: - grains['init'] = 'supervisord' - elif salt.utils.path.which('dumb-init') in init_cmdline: + elif salt.utils.path.which("supervisord") in init_cmdline: + grains["init"] = "supervisord" + elif salt.utils.path.which("dumb-init") in init_cmdline: # https://github.com/Yelp/dumb-init - grains['init'] = 'dumb-init' - elif salt.utils.path.which('tini') in init_cmdline: + grains["init"] = "dumb-init" + elif salt.utils.path.which("tini") in init_cmdline: # https://github.com/krallin/tini - grains['init'] = 'tini' - elif init_cmdline == ['runit']: - grains['init'] = 'runit' - elif '/sbin/my_init' in init_cmdline: + grains["init"] = "tini" + elif init_cmdline == ["runit"]: + grains["init"] = "runit" + elif "/sbin/my_init" in init_cmdline: # Phusion Base docker container use runit for srv mgmt, but # my_init as pid1 - grains['init'] = 'runit' + grains["init"] = "runit" else: log.debug( - 'Could not determine init system from command line: (%s)', - ' '.join(init_cmdline) + "Could not determine init system from command line: (%s)", + " ".join(init_cmdline), ) # Add lsb grains on any distro with lsb-release. Note that this import @@ -1792,14 +1907,14 @@ def os_data(): # does not install the python package for the python interpreter used by # Salt (i.e. python2 or python3) try: - log.trace('Getting lsb_release distro information') + log.trace("Getting lsb_release distro information") import lsb_release # pylint: disable=import-error + release = lsb_release.get_distro_information() for key, value in six.iteritems(release): key = key.lower() - lsb_param = 'lsb_{0}{1}'.format( - '' if key.startswith('distrib_') else 'distrib_', - key + lsb_param = "lsb_{0}{1}".format( + "" if key.startswith("distrib_") else "distrib_", key ) grains[lsb_param] = value # Catch a NameError to workaround possible breakage in lsb_release @@ -1807,76 +1922,81 @@ def os_data(): except (ImportError, NameError): # if the python library isn't available, try to parse # /etc/lsb-release using regex - log.trace('lsb_release python bindings not available') + log.trace("lsb_release python bindings not available") grains.update(_parse_lsb_release()) - if grains.get('lsb_distrib_description', '').lower().startswith('antergos'): + if grains.get("lsb_distrib_description", "").lower().startswith("antergos"): # Antergos incorrectly configures their /etc/lsb-release, # setting the DISTRIB_ID to "Arch". This causes the "os" grain # to be incorrectly set to "Arch". - grains['osfullname'] = 'Antergos Linux' - elif 'lsb_distrib_id' not in grains: - log.trace( - 'Failed to get lsb_distrib_id, trying to parse os-release' - ) - os_release = _parse_os_release('/etc/os-release', '/usr/lib/os-release') + grains["osfullname"] = "Antergos Linux" + elif "lsb_distrib_id" not in grains: + log.trace("Failed to get lsb_distrib_id, trying to parse os-release") + os_release = _parse_os_release("/etc/os-release", "/usr/lib/os-release") if os_release: - if 'NAME' in os_release: - grains['lsb_distrib_id'] = os_release['NAME'].strip() - if 'VERSION_ID' in os_release: - grains['lsb_distrib_release'] = os_release['VERSION_ID'] - if 'VERSION_CODENAME' in os_release: - grains['lsb_distrib_codename'] = os_release['VERSION_CODENAME'] - elif 'PRETTY_NAME' in os_release: - codename = os_release['PRETTY_NAME'] + if "NAME" in os_release: + grains["lsb_distrib_id"] = os_release["NAME"].strip() + if "VERSION_ID" in os_release: + grains["lsb_distrib_release"] = os_release["VERSION_ID"] + if "VERSION_CODENAME" in os_release: + grains["lsb_distrib_codename"] = os_release["VERSION_CODENAME"] + elif "PRETTY_NAME" in os_release: + codename = os_release["PRETTY_NAME"] # https://github.com/saltstack/salt/issues/44108 - if os_release['ID'] == 'debian': - codename_match = re.search(r'\((\w+)\)$', codename) + if os_release["ID"] == "debian": + codename_match = re.search(r"\((\w+)\)$", codename) if codename_match: codename = codename_match.group(1) - grains['lsb_distrib_codename'] = codename - if 'CPE_NAME' in os_release: - cpe = _parse_cpe_name(os_release['CPE_NAME']) + grains["lsb_distrib_codename"] = codename + if "CPE_NAME" in os_release: + cpe = _parse_cpe_name(os_release["CPE_NAME"]) if not cpe: - log.error('Broken CPE_NAME format in /etc/os-release!') - elif cpe.get('vendor', '').lower() in ['suse', 'opensuse']: - grains['os'] = "SUSE" + log.error("Broken CPE_NAME format in /etc/os-release!") + elif cpe.get("vendor", "").lower() in ["suse", "opensuse"]: + grains["os"] = "SUSE" # openSUSE `osfullname` grain normalization if os_release.get("NAME") == "openSUSE Leap": - grains['osfullname'] = "Leap" + grains["osfullname"] = "Leap" elif os_release.get("VERSION") == "Tumbleweed": - grains['osfullname'] = os_release["VERSION"] + grains["osfullname"] = os_release["VERSION"] # Override VERSION_ID, if CPE_NAME around - if cpe.get('version') and cpe.get('vendor') == 'opensuse': # Keep VERSION_ID for SLES - grains['lsb_distrib_release'] = cpe['version'] + if ( + cpe.get("version") and cpe.get("vendor") == "opensuse" + ): # Keep VERSION_ID for SLES + grains["lsb_distrib_release"] = cpe["version"] - elif os.path.isfile('/etc/SuSE-release'): - log.trace('Parsing distrib info from /etc/SuSE-release') - grains['lsb_distrib_id'] = 'SUSE' - version = '' - patch = '' - with salt.utils.files.fopen('/etc/SuSE-release') as fhr: + elif os.path.isfile("/etc/SuSE-release"): + log.trace("Parsing distrib info from /etc/SuSE-release") + grains["lsb_distrib_id"] = "SUSE" + version = "" + patch = "" + with salt.utils.files.fopen("/etc/SuSE-release") as fhr: for line in fhr: - if 'enterprise' in line.lower(): - grains['lsb_distrib_id'] = 'SLES' - grains['lsb_distrib_codename'] = re.sub(r'\(.+\)', '', line).strip() - elif 'version' in line.lower(): - version = re.sub(r'[^0-9]', '', line) - elif 'patchlevel' in line.lower(): - patch = re.sub(r'[^0-9]', '', line) - grains['lsb_distrib_release'] = version + if "enterprise" in line.lower(): + grains["lsb_distrib_id"] = "SLES" + grains["lsb_distrib_codename"] = re.sub( + r"\(.+\)", "", line + ).strip() + elif "version" in line.lower(): + version = re.sub(r"[^0-9]", "", line) + elif "patchlevel" in line.lower(): + patch = re.sub(r"[^0-9]", "", line) + grains["lsb_distrib_release"] = version if patch: - grains['lsb_distrib_release'] += '.' + patch - patchstr = 'SP' + patch - if grains['lsb_distrib_codename'] and patchstr not in grains['lsb_distrib_codename']: - grains['lsb_distrib_codename'] += ' ' + patchstr - if not grains.get('lsb_distrib_codename'): - grains['lsb_distrib_codename'] = 'n.a' - elif os.path.isfile('/etc/altlinux-release'): - log.trace('Parsing distrib info from /etc/altlinux-release') + grains["lsb_distrib_release"] += "." + patch + patchstr = "SP" + patch + if ( + grains["lsb_distrib_codename"] + and patchstr not in grains["lsb_distrib_codename"] + ): + grains["lsb_distrib_codename"] += " " + patchstr + if not grains.get("lsb_distrib_codename"): + grains["lsb_distrib_codename"] = "n.a" + elif os.path.isfile("/etc/altlinux-release"): + log.trace("Parsing distrib info from /etc/altlinux-release") # ALT Linux - grains['lsb_distrib_id'] = 'altlinux' - with salt.utils.files.fopen('/etc/altlinux-release') as ifile: + grains["lsb_distrib_id"] = "altlinux" + with salt.utils.files.fopen("/etc/altlinux-release") as ifile: # This file is symlinked to from: # /etc/fedora-release # /etc/redhat-release @@ -1884,221 +2004,236 @@ def os_data(): for line in ifile: # ALT Linux Sisyphus (unstable) comps = line.split() - if comps[0] == 'ALT': - grains['lsb_distrib_release'] = comps[2] - grains['lsb_distrib_codename'] = \ - comps[3].replace('(', '').replace(')', '') - elif os.path.isfile('/etc/centos-release'): - log.trace('Parsing distrib info from /etc/centos-release') + if comps[0] == "ALT": + grains["lsb_distrib_release"] = comps[2] + grains["lsb_distrib_codename"] = ( + comps[3].replace("(", "").replace(")", "") + ) + elif os.path.isfile("/etc/centos-release"): + log.trace("Parsing distrib info from /etc/centos-release") # CentOS Linux - grains['lsb_distrib_id'] = 'CentOS' - with salt.utils.files.fopen('/etc/centos-release') as ifile: + grains["lsb_distrib_id"] = "CentOS" + with salt.utils.files.fopen("/etc/centos-release") as ifile: for line in ifile: # Need to pull out the version and codename # in the case of custom content in /etc/centos-release - find_release = re.compile(r'\d+\.\d+') - find_codename = re.compile(r'(?<=\()(.*?)(?=\))') + find_release = re.compile(r"\d+\.\d+") + find_codename = re.compile(r"(?<=\()(.*?)(?=\))") release = find_release.search(line) codename = find_codename.search(line) if release is not None: - grains['lsb_distrib_release'] = release.group() + grains["lsb_distrib_release"] = release.group() if codename is not None: - grains['lsb_distrib_codename'] = codename.group() - elif os.path.isfile('/etc.defaults/VERSION') \ - and os.path.isfile('/etc.defaults/synoinfo.conf'): - grains['osfullname'] = 'Synology' + grains["lsb_distrib_codename"] = codename.group() + elif os.path.isfile("/etc.defaults/VERSION") and os.path.isfile( + "/etc.defaults/synoinfo.conf" + ): + grains["osfullname"] = "Synology" log.trace( - 'Parsing Synology distrib info from /etc/.defaults/VERSION' + "Parsing Synology distrib info from /etc/.defaults/VERSION" ) - with salt.utils.files.fopen('/etc.defaults/VERSION', 'r') as fp_: + with salt.utils.files.fopen("/etc.defaults/VERSION", "r") as fp_: synoinfo = {} for line in fp_: try: - key, val = line.rstrip('\n').split('=') + key, val = line.rstrip("\n").split("=") except ValueError: continue - if key in ('majorversion', 'minorversion', - 'buildnumber'): + if key in ("majorversion", "minorversion", "buildnumber"): synoinfo[key] = val.strip('"') if len(synoinfo) != 3: log.warning( - 'Unable to determine Synology version info. ' - 'Please report this, as it is likely a bug.' + "Unable to determine Synology version info. " + "Please report this, as it is likely a bug." ) else: - grains['osrelease'] = ( - '{majorversion}.{minorversion}-{buildnumber}' - .format(**synoinfo) + grains[ + "osrelease" + ] = "{majorversion}.{minorversion}-{buildnumber}".format( + **synoinfo ) # Use the already intelligent platform module to get distro info # (though apparently it's not intelligent enough to strip quotes) log.trace( - 'Getting OS name, release, and codename from ' - 'platform.linux_distribution()' + "Getting OS name, release, and codename from " + "platform.linux_distribution()" ) - (osname, osrelease, oscodename) = \ - [x.strip('"').strip("'") for x in - linux_distribution(supported_dists=_supported_dists)] + (osname, osrelease, oscodename) = [ + x.strip('"').strip("'") + for x in linux_distribution(supported_dists=_supported_dists) + ] # Try to assign these three names based on the lsb info, they tend to # be more accurate than what python gets from /etc/DISTRO-release. # It's worth noting that Ubuntu has patched their Python distribution # so that linux_distribution() does the /etc/lsb-release parsing, but # we do it anyway here for the sake for full portability. - if 'osfullname' not in grains: + if "osfullname" not in grains: # If NI Linux RT distribution, set the grains['osfullname'] to 'nilrt' - if grains.get('lsb_distrib_id', '').lower().startswith('nilrt'): - grains['osfullname'] = 'nilrt' + if grains.get("lsb_distrib_id", "").lower().startswith("nilrt"): + grains["osfullname"] = "nilrt" else: - grains['osfullname'] = grains.get('lsb_distrib_id', osname).strip() - if 'osrelease' not in grains: + grains["osfullname"] = grains.get("lsb_distrib_id", osname).strip() + if "osrelease" not in grains: # NOTE: This is a workaround for CentOS 7 os-release bug # https://bugs.centos.org/view.php?id=8359 # /etc/os-release contains no minor distro release number so we fall back to parse # /etc/centos-release file instead. # Commit introducing this comment should be reverted after the upstream bug is released. - if 'CentOS Linux 7' in grains.get('lsb_distrib_codename', ''): - grains.pop('lsb_distrib_release', None) - grains['osrelease'] = grains.get('lsb_distrib_release', osrelease).strip() - grains['oscodename'] = grains.get('lsb_distrib_codename', '').strip() or oscodename - if 'Red Hat' in grains['oscodename']: - grains['oscodename'] = oscodename - distroname = _REPLACE_LINUX_RE.sub('', grains['osfullname']).strip() + if "CentOS Linux 7" in grains.get("lsb_distrib_codename", ""): + grains.pop("lsb_distrib_release", None) + grains["osrelease"] = grains.get("lsb_distrib_release", osrelease).strip() + grains["oscodename"] = ( + grains.get("lsb_distrib_codename", "").strip() or oscodename + ) + if "Red Hat" in grains["oscodename"]: + grains["oscodename"] = oscodename + distroname = _REPLACE_LINUX_RE.sub("", grains["osfullname"]).strip() # return the first ten characters with no spaces, lowercased - shortname = distroname.replace(' ', '').lower()[:10] + shortname = distroname.replace(" ", "").lower()[:10] # this maps the long names from the /etc/DISTRO-release files to the # traditional short names that Salt has used. - if 'os' not in grains: - grains['os'] = _OS_NAME_MAP.get(shortname, distroname) + if "os" not in grains: + grains["os"] = _OS_NAME_MAP.get(shortname, distroname) grains.update(_linux_cpudata()) grains.update(_linux_gpu_data()) - elif grains['kernel'] == 'SunOS': + elif grains["kernel"] == "SunOS": if salt.utils.platform.is_smartos(): # See https://github.com/joyent/smartos-live/issues/224 if HAS_UNAME: uname_v = os.uname()[3] # format: joyent_20161101T004406Z else: uname_v = os.name - uname_v = uname_v[uname_v.index('_')+1:] - grains['os'] = grains['osfullname'] = 'SmartOS' + uname_v = uname_v[uname_v.index("_") + 1 :] + grains["os"] = grains["osfullname"] = "SmartOS" # store a parsed version of YYYY.MM.DD as osrelease - grains['osrelease'] = ".".join([ - uname_v.split('T')[0][0:4], - uname_v.split('T')[0][4:6], - uname_v.split('T')[0][6:8], - ]) + grains["osrelease"] = ".".join( + [ + uname_v.split("T")[0][0:4], + uname_v.split("T")[0][4:6], + uname_v.split("T")[0][6:8], + ] + ) # store a untouched copy of the timestamp in osrelease_stamp - grains['osrelease_stamp'] = uname_v - elif os.path.isfile('/etc/release'): - with salt.utils.files.fopen('/etc/release', 'r') as fp_: + grains["osrelease_stamp"] = uname_v + elif os.path.isfile("/etc/release"): + with salt.utils.files.fopen("/etc/release", "r") as fp_: rel_data = fp_.read() try: release_re = re.compile( - r'((?:Open|Oracle )?Solaris|OpenIndiana|OmniOS) (Development)?' - r'\s*(\d+\.?\d*|v\d+)\s?[A-Z]*\s?(r\d+|\d+\/\d+|oi_\S+|snv_\S+)?' + r"((?:Open|Oracle )?Solaris|OpenIndiana|OmniOS) (Development)?" + r"\s*(\d+\.?\d*|v\d+)\s?[A-Z]*\s?(r\d+|\d+\/\d+|oi_\S+|snv_\S+)?" ) - osname, development, osmajorrelease, osminorrelease = release_re.search(rel_data).groups() + ( + osname, + development, + osmajorrelease, + osminorrelease, + ) = release_re.search(rel_data).groups() except AttributeError: # Set a blank osrelease grain and fallback to 'Solaris' # as the 'os' grain. - grains['os'] = grains['osfullname'] = 'Solaris' - grains['osrelease'] = '' + grains["os"] = grains["osfullname"] = "Solaris" + grains["osrelease"] = "" else: if development is not None: - osname = ' '.join((osname, development)) + osname = " ".join((osname, development)) if HAS_UNAME: uname_v = os.uname()[3] else: uname_v = os.name - grains['os'] = grains['osfullname'] = osname - if osname in ['Oracle Solaris'] and uname_v.startswith(osmajorrelease): + grains["os"] = grains["osfullname"] = osname + if osname in ["Oracle Solaris"] and uname_v.startswith( + osmajorrelease + ): # Oracla Solars 11 and up have minor version in uname - grains['osrelease'] = uname_v - elif osname in ['OmniOS']: + grains["osrelease"] = uname_v + elif osname in ["OmniOS"]: # OmniOS osrelease = [] osrelease.append(osmajorrelease[1:]) osrelease.append(osminorrelease[1:]) - grains['osrelease'] = ".".join(osrelease) - grains['osrelease_stamp'] = uname_v + grains["osrelease"] = ".".join(osrelease) + grains["osrelease_stamp"] = uname_v else: # Sun Solaris 10 and earlier/comparable osrelease = [] osrelease.append(osmajorrelease) if osminorrelease: osrelease.append(osminorrelease) - grains['osrelease'] = ".".join(osrelease) - grains['osrelease_stamp'] = uname_v + grains["osrelease"] = ".".join(osrelease) + grains["osrelease_stamp"] = uname_v grains.update(_sunos_cpudata()) - elif grains['kernel'] == 'VMkernel': - grains['os'] = 'ESXi' - elif grains['kernel'] == 'Darwin': - osrelease = __salt__['cmd.run']('sw_vers -productVersion') - osname = __salt__['cmd.run']('sw_vers -productName') - osbuild = __salt__['cmd.run']('sw_vers -buildVersion') - grains['os'] = 'MacOS' - grains['os_family'] = 'MacOS' - grains['osfullname'] = "{0} {1}".format(osname, osrelease) - grains['osrelease'] = osrelease - grains['osbuild'] = osbuild - grains['init'] = 'launchd' + elif grains["kernel"] == "VMkernel": + grains["os"] = "ESXi" + elif grains["kernel"] == "Darwin": + osrelease = __salt__["cmd.run"]("sw_vers -productVersion") + osname = __salt__["cmd.run"]("sw_vers -productName") + osbuild = __salt__["cmd.run"]("sw_vers -buildVersion") + grains["os"] = "MacOS" + grains["os_family"] = "MacOS" + grains["osfullname"] = "{0} {1}".format(osname, osrelease) + grains["osrelease"] = osrelease + grains["osbuild"] = osbuild + grains["init"] = "launchd" grains.update(_bsd_cpudata(grains)) grains.update(_osx_gpudata()) grains.update(_osx_platform_data()) - elif grains['kernel'] == 'AIX': - osrelease = __salt__['cmd.run']('oslevel') - osrelease_techlevel = __salt__['cmd.run']('oslevel -r') - osname = __salt__['cmd.run']('uname') - grains['os'] = 'AIX' - grains['osfullname'] = osname - grains['osrelease'] = osrelease - grains['osrelease_techlevel'] = osrelease_techlevel + elif grains["kernel"] == "AIX": + osrelease = __salt__["cmd.run"]("oslevel") + osrelease_techlevel = __salt__["cmd.run"]("oslevel -r") + osname = __salt__["cmd.run"]("uname") + grains["os"] = "AIX" + grains["osfullname"] = osname + grains["osrelease"] = osrelease + grains["osrelease_techlevel"] = osrelease_techlevel grains.update(_aix_cpudata()) else: - grains['os'] = grains['kernel'] - if grains['kernel'] == 'FreeBSD': - grains['osfullname'] = grains['os'] + grains["os"] = grains["kernel"] + if grains["kernel"] == "FreeBSD": + grains["osfullname"] = grains["os"] try: - grains['osrelease'] = __salt__['cmd.run']('freebsd-version -u').split('-')[0] + grains["osrelease"] = __salt__["cmd.run"]("freebsd-version -u").split("-")[ + 0 + ] except salt.exceptions.CommandExecutionError: # freebsd-version was introduced in 10.0. # derive osrelease from kernelversion prior to that - grains['osrelease'] = grains['kernelrelease'].split('-')[0] + grains["osrelease"] = grains["kernelrelease"].split("-")[0] grains.update(_bsd_cpudata(grains)) - if grains['kernel'] in ('OpenBSD', 'NetBSD'): + if grains["kernel"] in ("OpenBSD", "NetBSD"): grains.update(_bsd_cpudata(grains)) - grains['osrelease'] = grains['kernelrelease'].split('-')[0] - if grains['kernel'] == 'NetBSD': + grains["osrelease"] = grains["kernelrelease"].split("-")[0] + if grains["kernel"] == "NetBSD": grains.update(_netbsd_gpu_data()) - if not grains['os']: - grains['os'] = 'Unknown {0}'.format(grains['kernel']) - grains['os_family'] = 'Unknown' + if not grains["os"]: + grains["os"] = "Unknown {0}".format(grains["kernel"]) + grains["os_family"] = "Unknown" else: # this assigns family names based on the os name # family defaults to the os name if not found - grains['os_family'] = _OS_FAMILY_MAP.get(grains['os'], - grains['os']) + grains["os_family"] = _OS_FAMILY_MAP.get(grains["os"], grains["os"]) # Build the osarch grain. This grain will be used for platform-specific # considerations such as package management. Fall back to the CPU # architecture. - if grains.get('os_family') == 'Debian': - osarch = __salt__['cmd.run']('dpkg --print-architecture').strip() - elif grains.get('os_family') in ['RedHat', 'Suse']: + if grains.get("os_family") == "Debian": + osarch = __salt__["cmd.run"]("dpkg --print-architecture").strip() + elif grains.get("os_family") in ["RedHat", "Suse"]: osarch = salt.utils.pkg.rpm.get_osarch() - elif grains.get('os_family') in ('NILinuxRT', 'Poky'): + elif grains.get("os_family") in ("NILinuxRT", "Poky"): archinfo = {} - for line in __salt__['cmd.run']('opkg print-architecture').splitlines(): - if line.startswith('arch'): + for line in __salt__["cmd.run"]("opkg print-architecture").splitlines(): + if line.startswith("arch"): _, arch, priority = line.split() archinfo[arch.strip()] = int(priority.strip()) # Return osarch in priority order (higher to lower) osarch = sorted(archinfo, key=archinfo.get, reverse=True) else: - osarch = grains['cpuarch'] - grains['osarch'] = osarch + osarch = grains["cpuarch"] + grains["osarch"] = osarch grains.update(_memdata(grains)) @@ -2110,68 +2245,78 @@ def os_data(): grains.update(_virtual_hv(grains)) grains.update(_ps(grains)) - if grains.get('osrelease', ''): - osrelease_info = grains['osrelease'].split('.') + if grains.get("osrelease", ""): + osrelease_info = grains["osrelease"].split(".") for idx, value in enumerate(osrelease_info): if not value.isdigit(): continue osrelease_info[idx] = int(value) - grains['osrelease_info'] = tuple(osrelease_info) + grains["osrelease_info"] = tuple(osrelease_info) try: - grains['osmajorrelease'] = int(grains['osrelease_info'][0]) + grains["osmajorrelease"] = int(grains["osrelease_info"][0]) except (IndexError, TypeError, ValueError): log.debug( - 'Unable to derive osmajorrelease from osrelease_info \'%s\'. ' - 'The osmajorrelease grain will not be set.', - grains['osrelease_info'] + "Unable to derive osmajorrelease from osrelease_info '%s'. " + "The osmajorrelease grain will not be set.", + grains["osrelease_info"], ) - os_name = grains['os' if grains.get('os') in ( - 'Debian', 'FreeBSD', 'OpenBSD', 'NetBSD', 'Mac', 'Raspbian') else 'osfullname'] - grains['osfinger'] = '{0}-{1}'.format( - os_name, grains['osrelease'] if os_name in ('Ubuntu',) else grains['osrelease_info'][0]) + os_name = grains[ + "os" + if grains.get("os") + in ("Debian", "FreeBSD", "OpenBSD", "NetBSD", "Mac", "Raspbian") + else "osfullname" + ] + grains["osfinger"] = "{0}-{1}".format( + os_name, + grains["osrelease"] + if os_name in ("Ubuntu",) + else grains["osrelease_info"][0], + ) return grains def locale_info(): - ''' + """ Provides defaultlanguage defaultencoding - ''' + """ grains = {} - grains['locale_info'] = {} + grains["locale_info"] = {} if salt.utils.platform.is_proxy(): return grains try: ( - grains['locale_info']['defaultlanguage'], - grains['locale_info']['defaultencoding'] + grains["locale_info"]["defaultlanguage"], + grains["locale_info"]["defaultencoding"], ) = locale.getdefaultlocale() except Exception: # pylint: disable=broad-except # locale.getdefaultlocale can ValueError!! Catch anything else it # might do, per #2205 - grains['locale_info']['defaultlanguage'] = 'unknown' - grains['locale_info']['defaultencoding'] = 'unknown' - grains['locale_info']['detectedencoding'] = __salt_system_encoding__ + grains["locale_info"]["defaultlanguage"] = "unknown" + grains["locale_info"]["defaultencoding"] = "unknown" + grains["locale_info"]["detectedencoding"] = __salt_system_encoding__ - grains['locale_info']['timezone'] = 'unknown' + grains["locale_info"]["timezone"] = "unknown" if _DATEUTIL_TZ: try: - grains['locale_info']['timezone'] = datetime.datetime.now(dateutil.tz.tzlocal()).tzname() + grains["locale_info"]["timezone"] = datetime.datetime.now( + dateutil.tz.tzlocal() + ).tzname() except UnicodeDecodeError: # Because the method 'tzname' is not a part of salt the decoding error cant be fixed. # The error is in datetime in the python2 lib if salt.utils.platform.is_windows(): - grains['locale_info']['timezone'] = time.tzname[0].decode('mbcs') + grains["locale_info"]["timezone"] = time.tzname[0].decode("mbcs") return grains def hostname(): - ''' + """ Return fqdn, hostname, domainname .. note:: @@ -2179,7 +2324,7 @@ def hostname(): instead of the Windows domain to which the host is joined. It may also be empty if not a part of any domain. Refer to the ``windowsdomain`` grain instead - ''' + """ # This is going to need some work # Provides: # fqdn @@ -2192,7 +2337,7 @@ def hostname(): if salt.utils.platform.is_proxy(): return grains - grains['localhost'] = socket.gethostname() + grains["localhost"] = socket.gethostname() if __FQDN__ is None: __FQDN__ = salt.utils.network.get_fqhostname() @@ -2201,46 +2346,50 @@ def hostname(): # In this case we punt and log a message at error level, but force the # hostname and domain to be localhost.localdomain # Otherwise we would stacktrace below - if __FQDN__ is None: # still! - log.error('Having trouble getting a hostname. Does this machine have its hostname and domain set properly?') - __FQDN__ = 'localhost.localdomain' + if __FQDN__ is None: # still! + log.error( + "Having trouble getting a hostname. Does this machine have its hostname and domain set properly?" + ) + __FQDN__ = "localhost.localdomain" - grains['fqdn'] = __FQDN__ - (grains['host'], grains['domain']) = grains['fqdn'].partition('.')[::2] + grains["fqdn"] = __FQDN__ + (grains["host"], grains["domain"]) = grains["fqdn"].partition(".")[::2] return grains def append_domain(): - ''' + """ Return append_domain if set - ''' + """ grain = {} if salt.utils.platform.is_proxy(): return grain - if 'append_domain' in __opts__: - grain['append_domain'] = __opts__['append_domain'] + if "append_domain" in __opts__: + grain["append_domain"] = __opts__["append_domain"] return grain def fqdns(): - ''' + """ Return all known FQDNs for the system by enumerating all interfaces and then trying to reverse resolve them (excluding 'lo' interface). - ''' + """ # Provides: # fqdns grains = {} fqdns = set() - addresses = salt.utils.network.ip_addrs(include_loopback=False, - interface_data=_INTERFACES) - addresses.extend(salt.utils.network.ip_addrs6(include_loopback=False, - interface_data=_INTERFACES)) - err_message = 'An exception occurred resolving address \'%s\': %s' + addresses = salt.utils.network.ip_addrs( + include_loopback=False, interface_data=_INTERFACES + ) + addresses.extend( + salt.utils.network.ip_addrs6(include_loopback=False, interface_data=_INTERFACES) + ) + err_message = "An exception occurred resolving address '%s': %s" for ip in addresses: try: fqdns.add(socket.getfqdn(socket.gethostbyaddr(ip)[0])) @@ -2253,25 +2402,25 @@ def fqdns(): except (socket.error, socket.gaierror, socket.timeout) as err: log.error(err_message, ip, err) - grains['fqdns'] = sorted(list(fqdns)) + grains["fqdns"] = sorted(list(fqdns)) return grains def ip_fqdn(): - ''' + """ Return ip address and FQDN grains - ''' + """ if salt.utils.platform.is_proxy(): return {} ret = {} - ret['ipv4'] = salt.utils.network.ip_addrs(include_loopback=True) - ret['ipv6'] = salt.utils.network.ip_addrs6(include_loopback=True) + ret["ipv4"] = salt.utils.network.ip_addrs(include_loopback=True) + ret["ipv6"] = salt.utils.network.ip_addrs6(include_loopback=True) - _fqdn = hostname()['fqdn'] - for socket_type, ipv_num in ((socket.AF_INET, '4'), (socket.AF_INET6, '6')): - key = 'fqdn_ip' + ipv_num - if not ret['ipv' + ipv_num]: + _fqdn = hostname()["fqdn"] + for socket_type, ipv_num in ((socket.AF_INET, "4"), (socket.AF_INET6, "6")): + key = "fqdn_ip" + ipv_num + if not ret["ipv" + ipv_num]: ret[key] = [] else: try: @@ -2280,12 +2429,15 @@ def ip_fqdn(): ret[key] = list(set(item[4][0] for item in info)) except socket.error: timediff = datetime.datetime.utcnow() - start_time - if timediff.seconds > 5 and __opts__['__role'] == 'master': + if timediff.seconds > 5 and __opts__["__role"] == "master": log.warning( 'Unable to find IPv%s record for "%s" causing a %s ' - 'second timeout when rendering grains. Set the dns or ' - '/etc/hosts for IPv%s to clear this.', - ipv_num, _fqdn, timediff, ipv_num + "second timeout when rendering grains. Set the dns or " + "/etc/hosts for IPv%s to clear this.", + ipv_num, + _fqdn, + timediff, + ipv_num, ) ret[key] = [] @@ -2293,10 +2445,10 @@ def ip_fqdn(): def ip_interfaces(): - ''' + """ Provide a dict of the connected interfaces and their ip addresses The addresses will be passed as a list for each interface - ''' + """ # Provides: # ip_interfaces @@ -2307,24 +2459,24 @@ def ip_interfaces(): ifaces = _get_interfaces() for face in ifaces: iface_ips = [] - for inet in ifaces[face].get('inet', []): - if 'address' in inet: - iface_ips.append(inet['address']) - for inet in ifaces[face].get('inet6', []): - if 'address' in inet: - iface_ips.append(inet['address']) - for secondary in ifaces[face].get('secondary', []): - if 'address' in secondary: - iface_ips.append(secondary['address']) + for inet in ifaces[face].get("inet", []): + if "address" in inet: + iface_ips.append(inet["address"]) + for inet in ifaces[face].get("inet6", []): + if "address" in inet: + iface_ips.append(inet["address"]) + for secondary in ifaces[face].get("secondary", []): + if "address" in secondary: + iface_ips.append(secondary["address"]) ret[face] = iface_ips - return {'ip_interfaces': ret} + return {"ip_interfaces": ret} def ip4_interfaces(): - ''' + """ Provide a dict of the connected interfaces and their ip4 addresses The addresses will be passed as a list for each interface - ''' + """ # Provides: # ip_interfaces @@ -2335,21 +2487,21 @@ def ip4_interfaces(): ifaces = _get_interfaces() for face in ifaces: iface_ips = [] - for inet in ifaces[face].get('inet', []): - if 'address' in inet: - iface_ips.append(inet['address']) - for secondary in ifaces[face].get('secondary', []): - if 'address' in secondary: - iface_ips.append(secondary['address']) + for inet in ifaces[face].get("inet", []): + if "address" in inet: + iface_ips.append(inet["address"]) + for secondary in ifaces[face].get("secondary", []): + if "address" in secondary: + iface_ips.append(secondary["address"]) ret[face] = iface_ips - return {'ip4_interfaces': ret} + return {"ip4_interfaces": ret} def ip6_interfaces(): - ''' + """ Provide a dict of the connected interfaces and their ip6 addresses The addresses will be passed as a list for each interface - ''' + """ # Provides: # ip_interfaces @@ -2360,159 +2512,166 @@ def ip6_interfaces(): ifaces = _get_interfaces() for face in ifaces: iface_ips = [] - for inet in ifaces[face].get('inet6', []): - if 'address' in inet: - iface_ips.append(inet['address']) - for secondary in ifaces[face].get('secondary', []): - if 'address' in secondary: - iface_ips.append(secondary['address']) + for inet in ifaces[face].get("inet6", []): + if "address" in inet: + iface_ips.append(inet["address"]) + for secondary in ifaces[face].get("secondary", []): + if "address" in secondary: + iface_ips.append(secondary["address"]) ret[face] = iface_ips - return {'ip6_interfaces': ret} + return {"ip6_interfaces": ret} def hwaddr_interfaces(): - ''' + """ Provide a dict of the connected interfaces and their hw addresses (Mac Address) - ''' + """ # Provides: # hwaddr_interfaces ret = {} ifaces = _get_interfaces() for face in ifaces: - if 'hwaddr' in ifaces[face]: - ret[face] = ifaces[face]['hwaddr'] - return {'hwaddr_interfaces': ret} + if "hwaddr" in ifaces[face]: + ret[face] = ifaces[face]["hwaddr"] + return {"hwaddr_interfaces": ret} def dns(): - ''' + """ Parse the resolver configuration file .. versionadded:: 2016.3.0 - ''' + """ # Provides: # dns - if salt.utils.platform.is_windows() or 'proxyminion' in __opts__: + if salt.utils.platform.is_windows() or "proxyminion" in __opts__: return {} resolv = salt.utils.dns.parse_resolv() - for key in ('nameservers', 'ip4_nameservers', 'ip6_nameservers', - 'sortlist'): + for key in ("nameservers", "ip4_nameservers", "ip6_nameservers", "sortlist"): if key in resolv: resolv[key] = [six.text_type(i) for i in resolv[key]] - return {'dns': resolv} if resolv else {} + return {"dns": resolv} if resolv else {} def get_machine_id(): - ''' + """ Provide the machine-id for machine/virtualization combination - ''' + """ # Provides: # machine-id - if platform.system() == 'AIX': + if platform.system() == "AIX": return _aix_get_machine_id() - locations = ['/etc/machine-id', '/var/lib/dbus/machine-id'] + locations = ["/etc/machine-id", "/var/lib/dbus/machine-id"] existing_locations = [loc for loc in locations if os.path.exists(loc)] if not existing_locations: return {} else: with salt.utils.files.fopen(existing_locations[0]) as machineid: - return {'machine_id': machineid.read().strip()} + return {"machine_id": machineid.read().strip()} def cwd(): - ''' + """ Current working directory - ''' - return {'cwd': os.getcwd()} + """ + return {"cwd": os.getcwd()} def path(): - ''' + """ Return the path - ''' + """ # Provides: # path - return {'path': os.environ.get('PATH', '').strip()} + # systempath + _path = salt.utils.stringutils.to_unicode(os.environ.get("PATH", "").strip()) + return { + "path": _path, + "systempath": _path.split(os.path.pathsep), + } def pythonversion(): - ''' + """ Return the Python version - ''' + """ # Provides: # pythonversion - return {'pythonversion': list(sys.version_info)} + return {"pythonversion": list(sys.version_info)} def pythonpath(): - ''' + """ Return the Python path - ''' + """ # Provides: # pythonpath - return {'pythonpath': sys.path} + return {"pythonpath": sys.path} def pythonexecutable(): - ''' + """ Return the python executable in use - ''' + """ # Provides: # pythonexecutable - return {'pythonexecutable': sys.executable} + return {"pythonexecutable": sys.executable} def saltpath(): - ''' + """ Return the path of the salt module - ''' + """ # Provides: # saltpath salt_path = os.path.abspath(os.path.join(__file__, os.path.pardir)) - return {'saltpath': os.path.dirname(salt_path)} + return {"saltpath": os.path.dirname(salt_path)} def saltversion(): - ''' + """ Return the version of salt - ''' + """ # Provides: # saltversion from salt.version import __version__ - return {'saltversion': __version__} + + return {"saltversion": __version__} def zmqversion(): - ''' + """ Return the zeromq version - ''' + """ # Provides: # zmqversion try: import zmq - return {'zmqversion': zmq.zmq_version()} # pylint: disable=no-member + + return {"zmqversion": zmq.zmq_version()} # pylint: disable=no-member except ImportError: return {} def saltversioninfo(): - ''' + """ Return the version_info of salt .. versionadded:: 0.17.0 - ''' + """ # Provides: # saltversioninfo from salt.version import __version_info__ - return {'saltversioninfo': list(__version_info__)} + + return {"saltversioninfo": list(__version_info__)} def _hw_data(osdata): - ''' + """ Get system specific hardware data from dmidecode Provides @@ -2524,232 +2683,244 @@ def _hw_data(osdata): uuid .. versionadded:: 0.9.5 - ''' + """ if salt.utils.platform.is_proxy(): return {} grains = {} - if osdata['kernel'] == 'Linux' and os.path.exists('/sys/class/dmi/id'): + if osdata["kernel"] == "Linux" and os.path.exists("/sys/class/dmi/id"): # On many Linux distributions basic firmware information is available via sysfs # requires CONFIG_DMIID to be enabled in the Linux kernel configuration sysfs_firmware_info = { - 'biosversion': 'bios_version', - 'productname': 'product_name', - 'manufacturer': 'sys_vendor', - 'biosreleasedate': 'bios_date', - 'uuid': 'product_uuid', - 'serialnumber': 'product_serial' + "biosversion": "bios_version", + "productname": "product_name", + "manufacturer": "sys_vendor", + "biosreleasedate": "bios_date", + "uuid": "product_uuid", + "serialnumber": "product_serial", } for key, fw_file in sysfs_firmware_info.items(): - contents_file = os.path.join('/sys/class/dmi/id', fw_file) + contents_file = os.path.join("/sys/class/dmi/id", fw_file) if os.path.exists(contents_file): try: - with salt.utils.files.fopen(contents_file, 'r') as ifile: - grains[key] = salt.utils.stringutils.to_unicode(ifile.read().strip(), errors='replace') - if key == 'uuid': - grains['uuid'] = grains['uuid'].lower() + with salt.utils.files.fopen(contents_file, "r") as ifile: + grains[key] = salt.utils.stringutils.to_unicode( + ifile.read().strip(), errors="replace" + ) + if key == "uuid": + grains["uuid"] = grains["uuid"].lower() except (IOError, OSError) as err: # PermissionError is new to Python 3, but corresponds to the EACESS and # EPERM error numbers. Use those instead here for PY2 compatibility. if err.errno == EACCES or err.errno == EPERM: # Skip the grain if non-root user has no access to the file. pass - elif salt.utils.path.which_bin(['dmidecode', 'smbios']) is not None and not ( - salt.utils.platform.is_smartos() or - ( # SunOS on SPARC - 'smbios: failed to load SMBIOS: System does not export an SMBIOS table' - osdata['kernel'] == 'SunOS' and - osdata['cpuarch'].startswith('sparc') - )): + elif salt.utils.path.which_bin(["dmidecode", "smbios"]) is not None and not ( + salt.utils.platform.is_smartos() + or ( # SunOS on SPARC - 'smbios: failed to load SMBIOS: System does not export an SMBIOS table' + osdata["kernel"] == "SunOS" and osdata["cpuarch"].startswith("sparc") + ) + ): # On SmartOS (possibly SunOS also) smbios only works in the global zone # smbios is also not compatible with linux's smbios (smbios -s = print summarized) grains = { - 'biosversion': __salt__['smbios.get']('bios-version'), - 'productname': __salt__['smbios.get']('system-product-name'), - 'manufacturer': __salt__['smbios.get']('system-manufacturer'), - 'biosreleasedate': __salt__['smbios.get']('bios-release-date'), - 'uuid': __salt__['smbios.get']('system-uuid') + "biosversion": __salt__["smbios.get"]("bios-version"), + "productname": __salt__["smbios.get"]("system-product-name"), + "manufacturer": __salt__["smbios.get"]("system-manufacturer"), + "biosreleasedate": __salt__["smbios.get"]("bios-release-date"), + "uuid": __salt__["smbios.get"]("system-uuid"), } grains = dict([(key, val) for key, val in grains.items() if val is not None]) - uuid = __salt__['smbios.get']('system-uuid') + uuid = __salt__["smbios.get"]("system-uuid") if uuid is not None: - grains['uuid'] = uuid.lower() - for serial in ('system-serial-number', 'chassis-serial-number', 'baseboard-serial-number'): - serial = __salt__['smbios.get'](serial) + grains["uuid"] = uuid.lower() + for serial in ( + "system-serial-number", + "chassis-serial-number", + "baseboard-serial-number", + ): + serial = __salt__["smbios.get"](serial) if serial is not None: - grains['serialnumber'] = serial + grains["serialnumber"] = serial break - elif salt.utils.path.which_bin(['fw_printenv']) is not None: + elif salt.utils.path.which_bin(["fw_printenv"]) is not None: # ARM Linux devices expose UBOOT env variables via fw_printenv hwdata = { - 'manufacturer': 'manufacturer', - 'serialnumber': 'serial#', - 'productname': 'DeviceDesc', + "manufacturer": "manufacturer", + "serialnumber": "serial#", + "productname": "DeviceDesc", } for grain_name, cmd_key in six.iteritems(hwdata): - result = __salt__['cmd.run_all']('fw_printenv {0}'.format(cmd_key)) - if result['retcode'] == 0: - uboot_keyval = result['stdout'].split('=') + result = __salt__["cmd.run_all"]("fw_printenv {0}".format(cmd_key)) + if result["retcode"] == 0: + uboot_keyval = result["stdout"].split("=") grains[grain_name] = _clean_value(grain_name, uboot_keyval[1]) - elif osdata['kernel'] == 'FreeBSD': + elif osdata["kernel"] == "FreeBSD": # On FreeBSD /bin/kenv (already in base system) # can be used instead of dmidecode - kenv = salt.utils.path.which('kenv') + kenv = salt.utils.path.which("kenv") if kenv: # In theory, it will be easier to add new fields to this later fbsd_hwdata = { - 'biosversion': 'smbios.bios.version', - 'manufacturer': 'smbios.system.maker', - 'serialnumber': 'smbios.system.serial', - 'productname': 'smbios.system.product', - 'biosreleasedate': 'smbios.bios.reldate', - 'uuid': 'smbios.system.uuid', + "biosversion": "smbios.bios.version", + "manufacturer": "smbios.system.maker", + "serialnumber": "smbios.system.serial", + "productname": "smbios.system.product", + "biosreleasedate": "smbios.bios.reldate", + "uuid": "smbios.system.uuid", } for key, val in six.iteritems(fbsd_hwdata): - value = __salt__['cmd.run']('{0} {1}'.format(kenv, val)) + value = __salt__["cmd.run"]("{0} {1}".format(kenv, val)) grains[key] = _clean_value(key, value) - elif osdata['kernel'] == 'OpenBSD': - sysctl = salt.utils.path.which('sysctl') - hwdata = {'biosversion': 'hw.version', - 'manufacturer': 'hw.vendor', - 'productname': 'hw.product', - 'serialnumber': 'hw.serialno', - 'uuid': 'hw.uuid'} + elif osdata["kernel"] == "OpenBSD": + sysctl = salt.utils.path.which("sysctl") + hwdata = { + "biosversion": "hw.version", + "manufacturer": "hw.vendor", + "productname": "hw.product", + "serialnumber": "hw.serialno", + "uuid": "hw.uuid", + } for key, oid in six.iteritems(hwdata): - value = __salt__['cmd.run']('{0} -n {1}'.format(sysctl, oid)) - if not value.endswith(' value is not available'): + value = __salt__["cmd.run"]("{0} -n {1}".format(sysctl, oid)) + if not value.endswith(" value is not available"): grains[key] = _clean_value(key, value) - elif osdata['kernel'] == 'NetBSD': - sysctl = salt.utils.path.which('sysctl') + elif osdata["kernel"] == "NetBSD": + sysctl = salt.utils.path.which("sysctl") nbsd_hwdata = { - 'biosversion': 'machdep.dmi.board-version', - 'manufacturer': 'machdep.dmi.system-vendor', - 'serialnumber': 'machdep.dmi.system-serial', - 'productname': 'machdep.dmi.system-product', - 'biosreleasedate': 'machdep.dmi.bios-date', - 'uuid': 'machdep.dmi.system-uuid', + "biosversion": "machdep.dmi.board-version", + "manufacturer": "machdep.dmi.system-vendor", + "serialnumber": "machdep.dmi.system-serial", + "productname": "machdep.dmi.system-product", + "biosreleasedate": "machdep.dmi.bios-date", + "uuid": "machdep.dmi.system-uuid", } for key, oid in six.iteritems(nbsd_hwdata): - result = __salt__['cmd.run_all']('{0} -n {1}'.format(sysctl, oid)) - if result['retcode'] == 0: - grains[key] = _clean_value(key, result['stdout']) - elif osdata['kernel'] == 'Darwin': - grains['manufacturer'] = 'Apple Inc.' - sysctl = salt.utils.path.which('sysctl') - hwdata = {'productname': 'hw.model'} + result = __salt__["cmd.run_all"]("{0} -n {1}".format(sysctl, oid)) + if result["retcode"] == 0: + grains[key] = _clean_value(key, result["stdout"]) + elif osdata["kernel"] == "Darwin": + grains["manufacturer"] = "Apple Inc." + sysctl = salt.utils.path.which("sysctl") + hwdata = {"productname": "hw.model"} for key, oid in hwdata.items(): - value = __salt__['cmd.run']('{0} -b {1}'.format(sysctl, oid)) - if not value.endswith(' is invalid'): + value = __salt__["cmd.run"]("{0} -b {1}".format(sysctl, oid)) + if not value.endswith(" is invalid"): grains[key] = _clean_value(key, value) - elif osdata['kernel'] == 'SunOS' and osdata['cpuarch'].startswith('sparc'): + elif osdata["kernel"] == "SunOS" and osdata["cpuarch"].startswith("sparc"): # Depending on the hardware model, commands can report different bits # of information. With that said, consolidate the output from various # commands and attempt various lookups. data = "" - for (cmd, args) in (('/usr/sbin/prtdiag', '-v'), ('/usr/sbin/prtconf', '-vp'), ('/usr/sbin/virtinfo', '-a')): + for (cmd, args) in ( + ("/usr/sbin/prtdiag", "-v"), + ("/usr/sbin/prtconf", "-vp"), + ("/usr/sbin/virtinfo", "-a"), + ): if salt.utils.path.which(cmd): # Also verifies that cmd is executable - data += __salt__['cmd.run']('{0} {1}'.format(cmd, args)) - data += '\n' + data += __salt__["cmd.run"]("{0} {1}".format(cmd, args)) + data += "\n" sn_regexes = [ - re.compile(r) for r in [ - r'(?im)^\s*Chassis\s+Serial\s+Number\n-+\n(\S+)', # prtdiag - r'(?im)^\s*chassis-sn:\s*(\S+)', # prtconf - r'(?im)^\s*Chassis\s+Serial#:\s*(\S+)', # virtinfo + re.compile(r) + for r in [ + r"(?im)^\s*Chassis\s+Serial\s+Number\n-+\n(\S+)", # prtdiag + r"(?im)^\s*chassis-sn:\s*(\S+)", # prtconf + r"(?im)^\s*Chassis\s+Serial#:\s*(\S+)", # virtinfo ] ] obp_regexes = [ - re.compile(r) for r in [ - r'(?im)^\s*System\s+PROM\s+revisions.*\nVersion\n-+\nOBP\s+(\S+)\s+(\S+)', # prtdiag - r'(?im)^\s*version:\s*\'OBP\s+(\S+)\s+(\S+)', # prtconf + re.compile(r) + for r in [ + r"(?im)^\s*System\s+PROM\s+revisions.*\nVersion\n-+\nOBP\s+(\S+)\s+(\S+)", # prtdiag + r"(?im)^\s*version:\s*\'OBP\s+(\S+)\s+(\S+)", # prtconf ] ] fw_regexes = [ - re.compile(r) for r in [ - r'(?im)^\s*Sun\s+System\s+Firmware\s+(\S+)\s+(\S+)', # prtdiag - ] + re.compile(r) + for r in [r"(?im)^\s*Sun\s+System\s+Firmware\s+(\S+)\s+(\S+)"] # prtdiag ] uuid_regexes = [ - re.compile(r) for r in [ - r'(?im)^\s*Domain\s+UUID:\s*(\S+)', # virtinfo - ] + re.compile(r) for r in [r"(?im)^\s*Domain\s+UUID:\s*(\S+)"] # virtinfo ] manufacture_regexes = [ - re.compile(r) for r in [ - r'(?im)^\s*System\s+Configuration:\s*(.*)(?=sun)', # prtdiag - ] + re.compile(r) + for r in [r"(?im)^\s*System\s+Configuration:\s*(.*)(?=sun)"] # prtdiag ] product_regexes = [ - re.compile(r) for r in [ - r'(?im)^\s*System\s+Configuration:\s*.*?sun\d\S+[^\S\r\n]*(.*)', # prtdiag - r'(?im)^[^\S\r\n]*banner-name:[^\S\r\n]*(.*)', # prtconf - r'(?im)^[^\S\r\n]*product-name:[^\S\r\n]*(.*)', # prtconf + re.compile(r) + for r in [ + r"(?im)^\s*System\s+Configuration:\s*.*?sun\d\S+[^\S\r\n]*(.*)", # prtdiag + r"(?im)^[^\S\r\n]*banner-name:[^\S\r\n]*(.*)", # prtconf + r"(?im)^[^\S\r\n]*product-name:[^\S\r\n]*(.*)", # prtconf ] ] sn_regexes = [ - re.compile(r) for r in [ - r'(?im)Chassis\s+Serial\s+Number\n-+\n(\S+)', # prtdiag - r'(?i)Chassis\s+Serial#:\s*(\S+)', # virtinfo - r'(?i)chassis-sn:\s*(\S+)', # prtconf + re.compile(r) + for r in [ + r"(?im)Chassis\s+Serial\s+Number\n-+\n(\S+)", # prtdiag + r"(?i)Chassis\s+Serial#:\s*(\S+)", # virtinfo + r"(?i)chassis-sn:\s*(\S+)", # prtconf ] ] obp_regexes = [ - re.compile(r) for r in [ - r'(?im)System\s+PROM\s+revisions.*\nVersion\n-+\nOBP\s+(\S+)\s+(\S+)', # prtdiag - r'(?im)version:\s*\'OBP\s+(\S+)\s+(\S+)', # prtconf + re.compile(r) + for r in [ + r"(?im)System\s+PROM\s+revisions.*\nVersion\n-+\nOBP\s+(\S+)\s+(\S+)", # prtdiag + r"(?im)version:\s*\'OBP\s+(\S+)\s+(\S+)", # prtconf ] ] fw_regexes = [ - re.compile(r) for r in [ - r'(?i)Sun\s+System\s+Firmware\s+(\S+)\s+(\S+)', # prtdiag - ] + re.compile(r) + for r in [r"(?i)Sun\s+System\s+Firmware\s+(\S+)\s+(\S+)"] # prtdiag ] uuid_regexes = [ - re.compile(r) for r in [ - r'(?i)Domain\s+UUID:\s+(\S+)', # virtinfo - ] + re.compile(r) for r in [r"(?i)Domain\s+UUID:\s+(\S+)"] # virtinfo ] for regex in sn_regexes: res = regex.search(data) if res and len(res.groups()) >= 1: - grains['serialnumber'] = res.group(1).strip().replace("'", "") + grains["serialnumber"] = res.group(1).strip().replace("'", "") break for regex in obp_regexes: res = regex.search(data) if res and len(res.groups()) >= 1: - obp_rev, obp_date = res.groups()[0:2] # Limit the number in case we found the data in multiple places - grains['biosversion'] = obp_rev.strip().replace("'", "") - grains['biosreleasedate'] = obp_date.strip().replace("'", "") + obp_rev, obp_date = res.groups()[ + 0:2 + ] # Limit the number in case we found the data in multiple places + grains["biosversion"] = obp_rev.strip().replace("'", "") + grains["biosreleasedate"] = obp_date.strip().replace("'", "") for regex in fw_regexes: res = regex.search(data) if res and len(res.groups()) >= 1: fw_rev, fw_date = res.groups()[0:2] - grains['systemfirmware'] = fw_rev.strip().replace("'", "") - grains['systemfirmwaredate'] = fw_date.strip().replace("'", "") + grains["systemfirmware"] = fw_rev.strip().replace("'", "") + grains["systemfirmwaredate"] = fw_date.strip().replace("'", "") break for regex in uuid_regexes: res = regex.search(data) if res and len(res.groups()) >= 1: - grains['uuid'] = res.group(1).strip().replace("'", "") + grains["uuid"] = res.group(1).strip().replace("'", "") break for regex in manufacture_regexes: res = regex.search(data) if res and len(res.groups()) >= 1: - grains['manufacture'] = res.group(1).strip().replace("'", "") + grains["manufacture"] = res.group(1).strip().replace("'", "") break for regex in product_regexes: @@ -2757,80 +2928,85 @@ def _hw_data(osdata): if res and len(res.groups()) >= 1: t_productname = res.group(1).strip().replace("'", "") if t_productname: - grains['product'] = t_productname - grains['productname'] = t_productname + grains["product"] = t_productname + grains["productname"] = t_productname break - elif osdata['kernel'] == 'AIX': - cmd = salt.utils.path.which('prtconf') + elif osdata["kernel"] == "AIX": + cmd = salt.utils.path.which("prtconf") if cmd: - data = __salt__['cmd.run']('{0}'.format(cmd)) + os.linesep - for dest, regstring in (('serialnumber', r'(?im)^\s*Machine\s+Serial\s+Number:\s+(\S+)'), - ('systemfirmware', r'(?im)^\s*Firmware\s+Version:\s+(.*)')): + data = __salt__["cmd.run"]("{0}".format(cmd)) + os.linesep + for dest, regstring in ( + ("serialnumber", r"(?im)^\s*Machine\s+Serial\s+Number:\s+(\S+)"), + ("systemfirmware", r"(?im)^\s*Firmware\s+Version:\s+(.*)"), + ): for regex in [re.compile(r) for r in [regstring]]: res = regex.search(data) if res and len(res.groups()) >= 1: - grains[dest] = res.group(1).strip().replace("'", '') + grains[dest] = res.group(1).strip().replace("'", "") - product_regexes = [re.compile(r'(?im)^\s*System\s+Model:\s+(\S+)')] + product_regexes = [re.compile(r"(?im)^\s*System\s+Model:\s+(\S+)")] for regex in product_regexes: res = regex.search(data) if res and len(res.groups()) >= 1: - grains['manufacturer'], grains['productname'] = res.group(1).strip().replace("'", "").split(",") + grains["manufacturer"], grains["productname"] = ( + res.group(1).strip().replace("'", "").split(",") + ) break else: - log.error('The \'prtconf\' binary was not found in $PATH.') + log.error("The 'prtconf' binary was not found in $PATH.") return grains def get_server_id(): - ''' + """ Provides an integer based on the FQDN of a machine. Useful as server-id in MySQL replication or anywhere else you'll need an ID like this. - ''' + """ # Provides: # server_id if salt.utils.platform.is_proxy(): return {} - id_ = __opts__.get('id', '') + id_ = __opts__.get("id", "") id_hash = None py_ver = sys.version_info[:2] if py_ver >= (3, 3): # Python 3.3 enabled hash randomization, so we need to shell out to get # a reliable hash. - id_hash = __salt__['cmd.run']( - [sys.executable, '-c', 'print(hash("{0}"))'.format(id_)], - env={'PYTHONHASHSEED': '0'} + id_hash = __salt__["cmd.run"]( + [sys.executable, "-c", 'print(hash("{0}"))'.format(id_)], + env={"PYTHONHASHSEED": "0"}, ) try: id_hash = int(id_hash) except (TypeError, ValueError): log.debug( - 'Failed to hash the ID to get the server_id grain. Result of ' - 'hash command: %s', id_hash + "Failed to hash the ID to get the server_id grain. Result of " + "hash command: %s", + id_hash, ) id_hash = None if id_hash is None: # Python < 3.3 or error encountered above id_hash = hash(id_) - return {'server_id': abs(id_hash % (2 ** 31))} + return {"server_id": abs(id_hash % (2 ** 31))} def get_master(): - ''' + """ Provides the minion with the name of its master. This is useful in states to target other services running on the master. - ''' + """ # Provides: # master - return {'master': __opts__.get('master', '')} + return {"master": __opts__.get("master", "")} def default_gateway(): - ''' + """ Populates grains which describe whether a server has a default gateway configured or not. Uses `ip -4 route show` and `ip -6 route show` and greps for a `default` at the beginning of any line. Assuming the standard @@ -2847,28 +3023,28 @@ def default_gateway(): ip4_gw: True # ip/True/False if default ipv4 gateway ip6_gw: True # ip/True/False if default ipv6 gateway ip_gw: True # True if either of the above is True, False otherwise - ''' + """ grains = {} - ip_bin = salt.utils.path.which('ip') + ip_bin = salt.utils.path.which("ip") if not ip_bin: return {} - grains['ip_gw'] = False - grains['ip4_gw'] = False - grains['ip6_gw'] = False - for ip_version in ('4', '6'): + grains["ip_gw"] = False + grains["ip4_gw"] = False + grains["ip6_gw"] = False + for ip_version in ("4", "6"): try: - out = __salt__['cmd.run']([ip_bin, '-' + ip_version, 'route', 'show']) + out = __salt__["cmd.run"]([ip_bin, "-" + ip_version, "route", "show"]) for line in out.splitlines(): - if line.startswith('default'): - grains['ip_gw'] = True - grains['ip{0}_gw'.format(ip_version)] = True + if line.startswith("default"): + grains["ip_gw"] = True + grains["ip{0}_gw".format(ip_version)] = True try: via, gw_ip = line.split()[1:3] except ValueError: pass else: - if via == 'via': - grains['ip{0}_gw'.format(ip_version)] = gw_ip + if via == "via": + grains["ip{0}_gw".format(ip_version)] = gw_ip break except Exception: # pylint: disable=broad-except continue diff --git a/salt/grains/disks.py b/salt/grains/disks.py index 38fb7755d85..aa72262348b 100644 --- a/salt/grains/disks.py +++ b/salt/grains/disks.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Detect disks -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -9,27 +9,27 @@ import glob import logging import re +# Solve the Chicken and egg problem where grains need to run before any +# of the modules are loaded and are generally available for any usage. +import salt.modules.cmdmod + # Import salt libs import salt.utils.files import salt.utils.path import salt.utils.platform -# Solve the Chicken and egg problem where grains need to run before any -# of the modules are loaded and are generally available for any usage. -import salt.modules.cmdmod - __salt__ = { - 'cmd.run': salt.modules.cmdmod._run_quiet, - 'cmd.run_all': salt.modules.cmdmod._run_all_quiet + "cmd.run": salt.modules.cmdmod._run_quiet, + "cmd.run_all": salt.modules.cmdmod._run_all_quiet, } log = logging.getLogger(__name__) def disks(): - ''' + """ Return list of disk devices - ''' + """ if salt.utils.platform.is_freebsd(): return _freebsd_geom() elif salt.utils.platform.is_linux(): @@ -37,45 +37,45 @@ def disks(): elif salt.utils.platform.is_windows(): return _windows_disks() else: - log.trace('Disk grain does not support OS') + log.trace("Disk grain does not support OS") class _geomconsts(object): - GEOMNAME = 'Geom name' - MEDIASIZE = 'Mediasize' - SECTORSIZE = 'Sectorsize' - STRIPESIZE = 'Stripesize' - STRIPEOFFSET = 'Stripeoffset' - DESCR = 'descr' # model - LUNID = 'lunid' - LUNNAME = 'lunname' - IDENT = 'ident' # serial - ROTATIONRATE = 'rotationrate' # RPM or 0 for non-rotating + GEOMNAME = "Geom name" + MEDIASIZE = "Mediasize" + SECTORSIZE = "Sectorsize" + STRIPESIZE = "Stripesize" + STRIPEOFFSET = "Stripeoffset" + DESCR = "descr" # model + LUNID = "lunid" + LUNNAME = "lunname" + IDENT = "ident" # serial + ROTATIONRATE = "rotationrate" # RPM or 0 for non-rotating # Preserve the API where possible with Salt < 2016.3 _aliases = { - DESCR: 'device_model', - IDENT: 'serial_number', - ROTATIONRATE: 'media_RPM', - LUNID: 'WWN', + DESCR: "device_model", + IDENT: "serial_number", + ROTATIONRATE: "media_RPM", + LUNID: "WWN", } _datatypes = { - MEDIASIZE: ('re_int', r'(\d+)'), - SECTORSIZE: 'try_int', - STRIPESIZE: 'try_int', - STRIPEOFFSET: 'try_int', - ROTATIONRATE: 'try_int', + MEDIASIZE: ("re_int", r"(\d+)"), + SECTORSIZE: "try_int", + STRIPESIZE: "try_int", + STRIPEOFFSET: "try_int", + ROTATIONRATE: "try_int", } def _datavalue(datatype, data): - if datatype == 'try_int': + if datatype == "try_int": try: return int(data) except ValueError: return None - elif datatype is tuple and datatype[0] == 're_int': + elif datatype is tuple and datatype[0] == "re_int": search = re.search(datatype[1], data) if search: try: @@ -87,37 +87,39 @@ def _datavalue(datatype, data): return data -_geom_attribs = [_geomconsts.__dict__[key] for key in - _geomconsts.__dict__ if not key.startswith('_')] +_geom_attribs = [ + _geomconsts.__dict__[key] for key in _geomconsts.__dict__ if not key.startswith("_") +] def _freebsd_geom(): - geom = salt.utils.path.which('geom') - ret = {'disks': {}, 'SSDs': []} + geom = salt.utils.path.which("geom") + ret = {"disks": {}, "SSDs": []} - devices = __salt__['cmd.run']('{0} disk list'.format(geom)) - devices = devices.split('\n\n') + devices = __salt__["cmd.run"]("{0} disk list".format(geom)) + devices = devices.split("\n\n") def parse_geom_attribs(device): tmp = {} - for line in device.split('\n'): + for line in device.split("\n"): for attrib in _geom_attribs: - search = re.search(r'{0}:\s(.*)'.format(attrib), line) + search = re.search(r"{0}:\s(.*)".format(attrib), line) if search: - value = _datavalue(_geomconsts._datatypes.get(attrib), - search.group(1)) + value = _datavalue( + _geomconsts._datatypes.get(attrib), search.group(1) + ) tmp[attrib] = value if attrib in _geomconsts._aliases: tmp[_geomconsts._aliases[attrib]] = value name = tmp.pop(_geomconsts.GEOMNAME) - if name.startswith('cd'): + if name.startswith("cd"): return - ret['disks'][name] = tmp + ret["disks"][name] = tmp if tmp.get(_geomconsts.ROTATIONRATE) == 0: - log.trace('Device %s reports itself as an SSD', device) - ret['SSDs'].append(name) + log.trace("Device %s reports itself as an SSD", device) + ret["SSDs"].append(name) for device in devices: parse_geom_attribs(device) @@ -126,26 +128,27 @@ def _freebsd_geom(): def _linux_disks(): - ''' + """ Return list of disk devices and work out if they are SSD or HDD. - ''' - ret = {'disks': [], 'SSDs': []} + """ + ret = {"disks": [], "SSDs": []} - for entry in glob.glob('/sys/block/*/queue/rotational'): + for entry in glob.glob("/sys/block/*/queue/rotational"): try: with salt.utils.files.fopen(entry) as entry_fp: - device = entry.split('/')[3] + device = entry.split("/")[3] flag = entry_fp.read(1) - if flag == '0': - ret['SSDs'].append(device) - log.trace('Device %s reports itself as an SSD', device) - elif flag == '1': - ret['disks'].append(device) - log.trace('Device %s reports itself as an HDD', device) + if flag == "0": + ret["SSDs"].append(device) + log.trace("Device %s reports itself as an SSD", device) + elif flag == "1": + ret["disks"].append(device) + log.trace("Device %s reports itself as an HDD", device) else: log.trace( - 'Unable to identify device %s as an SSD or HDD. It does ' - 'not report 0 or 1', device + "Unable to identify device %s as an SSD or HDD. It does " + "not report 0 or 1", + device, ) except IOError: pass @@ -153,39 +156,41 @@ def _linux_disks(): def _windows_disks(): - wmic = salt.utils.path.which('wmic') + wmic = salt.utils.path.which("wmic") - namespace = r'\\root\microsoft\windows\storage' - path = 'MSFT_PhysicalDisk' - get = 'DeviceID,MediaType' + namespace = r"\\root\microsoft\windows\storage" + path = "MSFT_PhysicalDisk" + get = "DeviceID,MediaType" - ret = {'disks': [], 'SSDs': []} + ret = {"disks": [], "SSDs": []} - cmdret = __salt__['cmd.run_all']( - '{0} /namespace:{1} path {2} get {3} /format:table'.format( - wmic, namespace, path, get)) + cmdret = __salt__["cmd.run_all"]( + "{0} /namespace:{1} path {2} get {3} /format:table".format( + wmic, namespace, path, get + ) + ) - if cmdret['retcode'] != 0: - log.trace('Disk grain does not support this version of Windows') + if cmdret["retcode"] != 0: + log.trace("Disk grain does not support this version of Windows") else: - for line in cmdret['stdout'].splitlines(): + for line in cmdret["stdout"].splitlines(): info = line.split() if len(info) != 2 or not info[0].isdigit() or not info[1].isdigit(): continue - device = r'\\.\PhysicalDrive{0}'.format(info[0]) + device = r"\\.\PhysicalDrive{0}".format(info[0]) mediatype = info[1] - if mediatype == '3': - log.trace('Device %s reports itself as an HDD', device) - ret['disks'].append(device) - elif mediatype == '4': - log.trace('Device %s reports itself as an SSD', device) - ret['SSDs'].append(device) - ret['disks'].append(device) - elif mediatype == '5': - log.trace('Device %s reports itself as an SCM', device) - ret['disks'].append(device) + if mediatype == "3": + log.trace("Device %s reports itself as an HDD", device) + ret["disks"].append(device) + elif mediatype == "4": + log.trace("Device %s reports itself as an SSD", device) + ret["SSDs"].append(device) + ret["disks"].append(device) + elif mediatype == "5": + log.trace("Device %s reports itself as an SCM", device) + ret["disks"].append(device) else: - log.trace('Device %s reports itself as Unspecified', device) - ret['disks'].append(device) + log.trace("Device %s reports itself as Unspecified", device) + ret["disks"].append(device) return ret diff --git a/salt/grains/esxi.py b/salt/grains/esxi.py index 95805622039..7291fd2d62b 100644 --- a/salt/grains/esxi.py +++ b/salt/grains/esxi.py @@ -1,22 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" Generate baseline proxy minion grains for ESXi hosts. .. versionadded:: 2015.8.4 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.modules.vsphere +import salt.utils.platform + # Import Salt Libs from salt.exceptions import SaltSystemExit -import salt.utils.platform -import salt.modules.vsphere -__proxyenabled__ = ['esxi'] -__virtualname__ = 'esxi' +__proxyenabled__ = ["esxi"] +__virtualname__ = "esxi" log = logging.getLogger(__file__) @@ -26,7 +28,7 @@ GRAINS_CACHE = {} def __virtual__(): try: - if salt.utils.platform.is_proxy() and __opts__['proxy']['proxytype'] == 'esxi': + if salt.utils.platform.is_proxy() and __opts__["proxy"]["proxytype"] == "esxi": return __virtualname__ except KeyError: pass @@ -39,7 +41,7 @@ def esxi(): def kernel(): - return {'kernel': 'proxy'} + return {"kernel": "proxy"} def os(): @@ -47,29 +49,29 @@ def os(): GRAINS_CACHE.update(_grains()) try: - return {'os': GRAINS_CACHE.get('fullName')} + return {"os": GRAINS_CACHE.get("fullName")} except AttributeError: - return {'os': 'Unknown'} + return {"os": "Unknown"} def os_family(): - return {'os_family': 'proxy'} + return {"os_family": "proxy"} def _find_credentials(host): - ''' + """ Cycle through all the possible credentials and return the first one that works. - ''' - user_names = [__pillar__['proxy'].get('username', 'root')] - passwords = __pillar__['proxy']['passwords'] + """ + user_names = [__pillar__["proxy"].get("username", "root")] + passwords = __pillar__["proxy"]["passwords"] for user in user_names: for password in passwords: try: # Try to authenticate with the given user/password combination - ret = salt.modules.vsphere.system_info(host=host, - username=user, - password=password) + ret = salt.modules.vsphere.system_info( + host=host, username=user, password=password + ) except SaltSystemExit: # If we can't authenticate, continue on to try the next password. continue @@ -77,24 +79,28 @@ def _find_credentials(host): if ret: return user, password # We've reached the end of the list without successfully authenticating. - raise SaltSystemExit('Cannot complete login due to an incorrect user name or password.') + raise SaltSystemExit( + "Cannot complete login due to an incorrect user name or password." + ) def _grains(): - ''' + """ Get the grains from the proxied device. - ''' + """ try: - host = __pillar__['proxy']['host'] + host = __pillar__["proxy"]["host"] if host: username, password = _find_credentials(host) - protocol = __pillar__['proxy'].get('protocol') - port = __pillar__['proxy'].get('port') - ret = salt.modules.vsphere.system_info(host=host, - username=username, - password=password, - protocol=protocol, - port=port) + protocol = __pillar__["proxy"].get("protocol") + port = __pillar__["proxy"].get("port") + ret = salt.modules.vsphere.system_info( + host=host, + username=username, + password=password, + protocol=protocol, + port=port, + ) GRAINS_CACHE.update(ret) except KeyError: pass diff --git a/salt/grains/extra.py b/salt/grains/extra.py index 9ce644b7664..2fdbe6526ac 100644 --- a/salt/grains/extra.py +++ b/salt/grains/extra.py @@ -2,73 +2,64 @@ from __future__ import absolute_import, print_function, unicode_literals -# Import python libs -import os - # Import third party libs import logging +# Import python libs +import os + # Import salt libs import salt.utils.data import salt.utils.files import salt.utils.platform import salt.utils.yaml -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] log = logging.getLogger(__name__) def shell(): - ''' + """ Return the default shell to use on this system - ''' + """ # Provides: # shell if salt.utils.platform.is_windows(): - env_var = 'COMSPEC' - default = r'C:\Windows\system32\cmd.exe' + env_var = "COMSPEC" + default = r"C:\Windows\system32\cmd.exe" else: - env_var = 'SHELL' - default = '/bin/sh' + env_var = "SHELL" + default = "/bin/sh" - return {'shell': os.environ.get(env_var, default)} + return {"shell": os.environ.get(env_var, default)} def config(): - ''' + """ Return the grains set in the grains file - ''' - if 'conf_file' not in __opts__: + """ + if "conf_file" not in __opts__: return {} - if os.path.isdir(__opts__['conf_file']): + if os.path.isdir(__opts__["conf_file"]): if salt.utils.platform.is_proxy(): gfn = os.path.join( - __opts__['conf_file'], - 'proxy.d', - __opts__['id'], - 'grains' - ) + __opts__["conf_file"], "proxy.d", __opts__["id"], "grains" + ) else: - gfn = os.path.join( - __opts__['conf_file'], - 'grains' - ) + gfn = os.path.join(__opts__["conf_file"], "grains") else: if salt.utils.platform.is_proxy(): gfn = os.path.join( - os.path.dirname(__opts__['conf_file']), - 'proxy.d', - __opts__['id'], - 'grains' - ) + os.path.dirname(__opts__["conf_file"]), + "proxy.d", + __opts__["id"], + "grains", + ) else: - gfn = os.path.join( - os.path.dirname(__opts__['conf_file']), - 'grains' - ) + gfn = os.path.join(os.path.dirname(__opts__["conf_file"]), "grains") if os.path.isfile(gfn): - log.debug('Loading static grains from %s', gfn) - with salt.utils.files.fopen(gfn, 'rb') as fp_: + log.debug("Loading static grains from %s", gfn) + with salt.utils.files.fopen(gfn, "rb") as fp_: try: return salt.utils.data.decode(salt.utils.yaml.safe_load(fp_)) except Exception: # pylint: disable=broad-except diff --git a/salt/grains/fibre_channel.py b/salt/grains/fibre_channel.py index 5396bbde7a9..7c2c7490797 100644 --- a/salt/grains/fibre_channel.py +++ b/salt/grains/fibre_channel.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Grains for Fibre Channel WWN's. On Windows this runs a PowerShell command that queries WMI to get the Fibre Channel WWN's available. @@ -10,7 +10,7 @@ To enable these grains set ``fibre_channel_grains: True``. .. code-block:: yaml fibre_channel_grains: True -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -19,29 +19,29 @@ import logging # Import Salt libs import salt.modules.cmdmod -import salt.utils.platform import salt.utils.files +import salt.utils.platform -__virtualname__ = 'fibre_channel' +__virtualname__ = "fibre_channel" # Get logging started log = logging.getLogger(__name__) def __virtual__(): - if __opts__.get('fibre_channel_grains', False) is False: + if __opts__.get("fibre_channel_grains", False) is False: return False else: return __virtualname__ def _linux_wwns(): - ''' + """ Return Fibre Channel port WWNs from a Linux host. - ''' + """ ret = [] - for fc_file in glob.glob('/sys/class/fc_host/*/port_name'): - with salt.utils.files.fopen(fc_file, 'r') as _wwn: + for fc_file in glob.glob("/sys/class/fc_host/*/port_name"): + with salt.utils.files.fopen(fc_file, "r") as _wwn: content = _wwn.read() for line in content.splitlines(): ret.append(line.rstrip()[2:]) @@ -49,14 +49,16 @@ def _linux_wwns(): def _windows_wwns(): - ''' + """ Return Fibre Channel port WWNs from a Windows host. - ''' - ps_cmd = r'Get-WmiObject -ErrorAction Stop ' \ - r'-class MSFC_FibrePortHBAAttributes ' \ - r'-namespace "root\WMI" | ' \ - r'Select -Expandproperty Attributes | ' \ - r'%{($_.PortWWN | % {"{0:x2}" -f $_}) -join ""}' + """ + ps_cmd = ( + r"Get-WmiObject -ErrorAction Stop " + r"-class MSFC_FibrePortHBAAttributes " + r'-namespace "root\WMI" | ' + r"Select -Expandproperty Attributes | " + r'%{($_.PortWWN | % {"{0:x2}" -f $_}) -join ""}' + ) ret = [] cmd_ret = salt.modules.cmdmod.powershell(ps_cmd) for line in cmd_ret: @@ -65,12 +67,12 @@ def _windows_wwns(): def fibre_channel_wwns(): - ''' + """ Return list of fiber channel HBA WWNs - ''' - grains = {'fc_wwn': False} + """ + grains = {"fc_wwn": False} if salt.utils.platform.is_linux(): - grains['fc_wwn'] = _linux_wwns() + grains["fc_wwn"] = _linux_wwns() elif salt.utils.platform.is_windows(): - grains['fc_wwn'] = _windows_wwns() + grains["fc_wwn"] = _windows_wwns() return grains diff --git a/salt/grains/fx2.py b/salt/grains/fx2.py index 9bb8522200b..eaf8d24e1dc 100644 --- a/salt/grains/fx2.py +++ b/salt/grains/fx2.py @@ -1,21 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Generate baseline proxy minion grains for Dell FX2 chassis. The challenge is that most of Salt isn't bootstrapped yet, so we need to repeat a bunch of things that would normally happen in proxy/fx2.py--just enough to get data from the chassis to include in grains. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging -import salt.proxy.fx2 + import salt.modules.cmdmod import salt.modules.dracr +import salt.proxy.fx2 import salt.utils.platform -__proxyenabled__ = ['fx2'] +__proxyenabled__ = ["fx2"] -__virtualname__ = 'fx2' +__virtualname__ = "fx2" logger = logging.getLogger(__file__) @@ -24,61 +26,74 @@ GRAINS_CACHE = {} def __virtual__(): - if salt.utils.platform.is_proxy() and 'proxy' in __opts__ and __opts__['proxy'].get('proxytype') == 'fx2': + if ( + salt.utils.platform.is_proxy() + and "proxy" in __opts__ + and __opts__["proxy"].get("proxytype") == "fx2" + ): return __virtualname__ return False def _find_credentials(): - ''' + """ Cycle through all the possible credentials and return the first one that works - ''' + """ usernames = [] - usernames.append(__pillar__['proxy'].get('admin_username', 'root')) - if 'fallback_admin_username' in __pillar__.get('proxy'): - usernames.append(__pillar__['proxy'].get('fallback_admin_username')) + usernames.append(__pillar__["proxy"].get("admin_username", "root")) + if "fallback_admin_username" in __pillar__.get("proxy"): + usernames.append(__pillar__["proxy"].get("fallback_admin_username")) for user in usernames: - for pwd in __pillar__['proxy']['passwords']: + for pwd in __pillar__["proxy"]["passwords"]: r = salt.modules.dracr.get_chassis_name( - host=__pillar__['proxy']['host'], + host=__pillar__["proxy"]["host"], admin_username=user, - admin_password=pwd) + admin_password=pwd, + ) # Retcode will be present if the chassis_name call failed try: - if r.get('retcode', None) is None: - __opts__['proxy']['admin_username'] = user - __opts__['proxy']['admin_password'] = pwd + if r.get("retcode", None) is None: + __opts__["proxy"]["admin_username"] = user + __opts__["proxy"]["admin_password"] = pwd return (user, pwd) except AttributeError: # Then the above was a string, and we can return the username # and password - __opts__['proxy']['admin_username'] = user - __opts__['proxy']['admin_password'] = pwd + __opts__["proxy"]["admin_username"] = user + __opts__["proxy"]["admin_password"] = pwd return (user, pwd) - logger.debug('grains fx2.find_credentials found no valid credentials, using Dell default') - return ('root', 'calvin') + logger.debug( + "grains fx2.find_credentials found no valid credentials, using Dell default" + ) + return ("root", "calvin") def _grains(): - ''' + """ Get the grains from the proxied device - ''' + """ (username, password) = _find_credentials() - r = salt.modules.dracr.system_info(host=__pillar__['proxy']['host'], - admin_username=username, - admin_password=password) + r = salt.modules.dracr.system_info( + host=__pillar__["proxy"]["host"], + admin_username=username, + admin_password=password, + ) - if r.get('retcode', 0) == 0: + if r.get("retcode", 0) == 0: GRAINS_CACHE = r else: GRAINS_CACHE = {} - GRAINS_CACHE.update(salt.modules.dracr.inventory(host=__pillar__['proxy']['host'], - admin_username=username, - admin_password=password)) + GRAINS_CACHE.update( + salt.modules.dracr.inventory( + host=__pillar__["proxy"]["host"], + admin_username=username, + admin_password=password, + ) + ) return GRAINS_CACHE @@ -88,7 +103,7 @@ def fx2(): def kernel(): - return {'kernel': 'proxy'} + return {"kernel": "proxy"} def location(): @@ -96,14 +111,16 @@ def location(): GRAINS_CACHE.update(_grains()) try: - return {'location': GRAINS_CACHE.get('Chassis Information').get('Chassis Location')} + return { + "location": GRAINS_CACHE.get("Chassis Information").get("Chassis Location") + } except AttributeError: - return {'location': 'Unknown'} + return {"location": "Unknown"} def os_family(): - return {'os_family': 'proxy'} + return {"os_family": "proxy"} def os_data(): - return {'os_data': 'Unknown'} + return {"os_data": "Unknown"} diff --git a/salt/grains/iscsi.py b/salt/grains/iscsi.py index 56b6601439a..af62bb94859 100644 --- a/salt/grains/iscsi.py +++ b/salt/grains/iscsi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Grains for iSCSI Qualified Names (IQN). .. versionadded:: 2018.3.0 @@ -9,7 +9,7 @@ To enable these grains set `iscsi_grains: True`. .. code-block:: yaml iscsi_grains: True -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -22,47 +22,47 @@ import salt.utils.files import salt.utils.path import salt.utils.platform -__virtualname__ = 'iscsi' +__virtualname__ = "iscsi" # Get logging started log = logging.getLogger(__name__) def __virtual__(): - if __opts__.get('iscsi_grains', False) is False: + if __opts__.get("iscsi_grains", False) is False: return False else: return __virtualname__ def iscsi_iqn(): - ''' + """ Return iSCSI IQN - ''' + """ grains = {} - grains['iscsi_iqn'] = False + grains["iscsi_iqn"] = False if salt.utils.platform.is_linux(): - grains['iscsi_iqn'] = _linux_iqn() + grains["iscsi_iqn"] = _linux_iqn() elif salt.utils.platform.is_windows(): - grains['iscsi_iqn'] = _windows_iqn() + grains["iscsi_iqn"] = _windows_iqn() elif salt.utils.platform.is_aix(): - grains['iscsi_iqn'] = _aix_iqn() + grains["iscsi_iqn"] = _aix_iqn() return grains def _linux_iqn(): - ''' + """ Return iSCSI IQN from a Linux host. - ''' + """ ret = [] - initiator = '/etc/iscsi/initiatorname.iscsi' + initiator = "/etc/iscsi/initiatorname.iscsi" try: - with salt.utils.files.fopen(initiator, 'r') as _iscsi: + with salt.utils.files.fopen(initiator, "r") as _iscsi: for line in _iscsi: line = line.strip() - if line.startswith('InitiatorName='): - ret.append(line.split('=', 1)[1]) + if line.startswith("InitiatorName="): + ret.append(line.split("=", 1)[1]) except IOError as ex: if ex.errno != errno.ENOENT: log.debug("Error while accessing '%s': %s", initiator, ex) @@ -71,12 +71,12 @@ def _linux_iqn(): def _aix_iqn(): - ''' + """ Return iSCSI IQN from an AIX host. - ''' + """ ret = [] - aix_cmd = 'lsattr -E -l iscsi0 | grep initiator_name' + aix_cmd = "lsattr -E -l iscsi0 | grep initiator_name" aix_ret = salt.modules.cmdmod.run(aix_cmd) if aix_ret[0].isalpha(): @@ -88,26 +88,27 @@ def _aix_iqn(): def _windows_iqn(): - ''' + """ Return iSCSI IQN from a Windows host. - ''' + """ ret = [] - wmic = salt.utils.path.which('wmic') + wmic = salt.utils.path.which("wmic") if not wmic: return ret - namespace = r'\\root\WMI' - path = 'MSiSCSIInitiator_MethodClass' - get = 'iSCSINodeName' + namespace = r"\\root\WMI" + path = "MSiSCSIInitiator_MethodClass" + get = "iSCSINodeName" cmd_ret = salt.modules.cmdmod.run_all( - '{0} /namespace:{1} path {2} get {3} /format:table' - ''.format(wmic, namespace, path, get)) + "{0} /namespace:{1} path {2} get {3} /format:table" + "".format(wmic, namespace, path, get) + ) - for line in cmd_ret['stdout'].splitlines(): - if line.startswith('iqn.'): + for line in cmd_ret["stdout"].splitlines(): + if line.startswith("iqn."): line = line.rstrip() ret.append(line.rstrip()) diff --git a/salt/grains/junos.py b/salt/grains/junos.py index 5da49f9a795..d2d2b5377b2 100644 --- a/salt/grains/junos.py +++ b/salt/grains/junos.py @@ -1,55 +1,56 @@ # -*- coding: utf-8 -*- -''' +""" Grains for junos. NOTE this is a little complicated--junos can only be accessed via salt-proxy-minion.Thus, some grains make sense to get them from the minion (PYTHONPATH), but others don't (ip_interfaces) -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs from salt.ext import six -__proxyenabled__ = ['junos'] -__virtualname__ = 'junos' +__proxyenabled__ = ["junos"] +__virtualname__ = "junos" # Get looging started log = logging.getLogger(__name__) def __virtual__(): - if 'proxy' not in __opts__: + if "proxy" not in __opts__: return False else: return __virtualname__ def _remove_complex_types(dictionary): - ''' + """ Linode-python is now returning some complex types that are not serializable by msgpack. Kill those. - ''' + """ for k, v in six.iteritems(dictionary): if isinstance(v, dict): dictionary[k] = _remove_complex_types(v) - elif hasattr(v, 'to_eng_string'): + elif hasattr(v, "to_eng_string"): dictionary[k] = v.to_eng_string() return dictionary def defaults(): - return {'os': 'proxy', 'kernel': 'unknown', 'osrelease': 'proxy'} + return {"os": "proxy", "kernel": "unknown", "osrelease": "proxy"} def facts(proxy=None): - if proxy is None or proxy['junos.initialized']() is False: + if proxy is None or proxy["junos.initialized"]() is False: return {} - return {'junos_facts': proxy['junos.get_serialized_facts']()} + return {"junos_facts": proxy["junos.get_serialized_facts"]()} def os_family(): - return {'os_family': 'junos'} + return {"os_family": "junos"} diff --git a/salt/grains/marathon.py b/salt/grains/marathon.py index fd9c73c3245..0afcf781212 100644 --- a/salt/grains/marathon.py +++ b/salt/grains/marathon.py @@ -1,49 +1,53 @@ # -*- coding: utf-8 -*- -''' +""" Generate marathon proxy minion grains. .. versionadded:: 2015.8.2 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import salt.utils.http import salt.utils.platform -__proxyenabled__ = ['marathon'] -__virtualname__ = 'marathon' + +__proxyenabled__ = ["marathon"] +__virtualname__ = "marathon" def __virtual__(): - if salt.utils.platform.is_proxy() and 'proxy' in __opts__ and __opts__['proxy'].get('proxytype') == 'marathon': + if ( + salt.utils.platform.is_proxy() + and "proxy" in __opts__ + and __opts__["proxy"].get("proxytype") == "marathon" + ): return __virtualname__ return False def kernel(): - return {'kernel': 'marathon'} + return {"kernel": "marathon"} def os(): - return {'os': 'marathon'} + return {"os": "marathon"} def os_family(): - return {'os_family': 'marathon'} + return {"os_family": "marathon"} def os_data(): - return {'os_data': 'marathon'} + return {"os_data": "marathon"} def marathon(): response = salt.utils.http.query( - "{0}/v2/info".format(__opts__['proxy'].get( - 'base_url', - "http://locahost:8080", - )), - decode_type='json', + "{0}/v2/info".format( + __opts__["proxy"].get("base_url", "http://locahost:8080",) + ), + decode_type="json", decode=True, ) - if not response or 'dict' not in response: - return {'marathon': None} - return {'marathon': response['dict']} + if not response or "dict" not in response: + return {"marathon": None} + return {"marathon": response["dict"]} diff --git a/salt/grains/mdadm.py b/salt/grains/mdadm.py index 182aedc9273..f5e0fc704a5 100644 --- a/salt/grains/mdadm.py +++ b/salt/grains/mdadm.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Detect MDADM RAIDs -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -14,25 +14,25 @@ log = logging.getLogger(__name__) def mdadm(): - ''' + """ Return list of mdadm devices - ''' + """ devices = set() try: - with salt.utils.files.fopen('/proc/mdstat', 'r') as mdstat: + with salt.utils.files.fopen("/proc/mdstat", "r") as mdstat: for line in mdstat: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('Personalities : '): + if line.startswith("Personalities : "): continue - if line.startswith('unused devices:'): + if line.startswith("unused devices:"): continue - if ' : ' in line: - devices.add(line.split(' : ')[0]) + if " : " in line: + devices.add(line.split(" : ")[0]) except IOError: return {} devices = sorted(devices) if devices: - log.trace('mdadm devices detected: %s', ', '.join(devices)) + log.trace("mdadm devices detected: %s", ", ".join(devices)) - return {'mdadm': devices} + return {"mdadm": devices} diff --git a/salt/grains/mdata.py b/salt/grains/mdata.py index 37a56072ce9..1deda096c24 100644 --- a/salt/grains/mdata.py +++ b/salt/grains/mdata.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" SmartOS Metadata grain provider :maintainer: Jorge Schrauwen @@ -9,12 +9,17 @@ SmartOS Metadata grain provider .. versionadded:: nitrogen -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging + +# Solve the Chicken and egg problem where grains need to run before any +# of the modules are loaded and are generally available for any usage. +import salt.modules.cmdmod # Import salt libs import salt.utils.dictupdate @@ -22,120 +27,129 @@ import salt.utils.json import salt.utils.path import salt.utils.platform -# Solve the Chicken and egg problem where grains need to run before any -# of the modules are loaded and are generally available for any usage. -import salt.modules.cmdmod - -__virtualname__ = 'mdata' +__virtualname__ = "mdata" __salt__ = { - 'cmd.run': salt.modules.cmdmod.run, + "cmd.run": salt.modules.cmdmod.run, } log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Figure out if we need to be loaded - ''' + """ ## collect mdata grains in a SmartOS zone if salt.utils.platform.is_smartos_zone(): return __virtualname__ ## collect mdata grains in a LX zone - if salt.utils.platform.is_linux() and 'BrandZ virtual linux' in os.uname(): + if salt.utils.platform.is_linux() and "BrandZ virtual linux" in os.uname(): return __virtualname__ return False def _user_mdata(mdata_list=None, mdata_get=None): - ''' + """ User Metadata - ''' + """ grains = {} if not mdata_list: - mdata_list = salt.utils.path.which('mdata-list') + mdata_list = salt.utils.path.which("mdata-list") if not mdata_get: - mdata_get = salt.utils.path.which('mdata-get') + mdata_get = salt.utils.path.which("mdata-get") if not mdata_list or not mdata_get: return grains - for mdata_grain in __salt__['cmd.run'](mdata_list, ignore_retcode=True).splitlines(): - mdata_value = __salt__['cmd.run']('{0} {1}'.format(mdata_get, mdata_grain), ignore_retcode=True) + for mdata_grain in __salt__["cmd.run"]( + mdata_list, ignore_retcode=True + ).splitlines(): + mdata_value = __salt__["cmd.run"]( + "{0} {1}".format(mdata_get, mdata_grain), ignore_retcode=True + ) - if not mdata_grain.startswith('sdc:'): - if 'mdata' not in grains: - grains['mdata'] = {} + if not mdata_grain.startswith("sdc:"): + if "mdata" not in grains: + grains["mdata"] = {} - log.debug('found mdata entry %s with value %s', mdata_grain, mdata_value) - mdata_grain = mdata_grain.replace('-', '_') - mdata_grain = mdata_grain.replace(':', '_') - grains['mdata'][mdata_grain] = mdata_value + log.debug("found mdata entry %s with value %s", mdata_grain, mdata_value) + mdata_grain = mdata_grain.replace("-", "_") + mdata_grain = mdata_grain.replace(":", "_") + grains["mdata"][mdata_grain] = mdata_value return grains def _sdc_mdata(mdata_list=None, mdata_get=None): - ''' + """ SDC Metadata specified by there specs https://eng.joyent.com/mdata/datadict.html - ''' + """ grains = {} sdc_text_keys = [ - 'uuid', - 'server_uuid', - 'datacenter_name', - 'hostname', - 'dns_domain', + "uuid", + "server_uuid", + "datacenter_name", + "hostname", + "dns_domain", ] sdc_json_keys = [ - 'resolvers', - 'nics', - 'routes', + "resolvers", + "nics", + "routes", ] if not mdata_list: - mdata_list = salt.utils.path.which('mdata-list') + mdata_list = salt.utils.path.which("mdata-list") if not mdata_get: - mdata_get = salt.utils.path.which('mdata-get') + mdata_get = salt.utils.path.which("mdata-get") if not mdata_list or not mdata_get: return grains - for mdata_grain in sdc_text_keys+sdc_json_keys: - mdata_value = __salt__['cmd.run']('{0} sdc:{1}'.format(mdata_get, mdata_grain), ignore_retcode=True) + for mdata_grain in sdc_text_keys + sdc_json_keys: + mdata_value = __salt__["cmd.run"]( + "{0} sdc:{1}".format(mdata_get, mdata_grain), ignore_retcode=True + ) - if not mdata_value.startswith('No metadata for '): - if 'mdata' not in grains: - grains['mdata'] = {} - if 'sdc' not in grains['mdata']: - grains['mdata']['sdc'] = {} + if not mdata_value.startswith("No metadata for "): + if "mdata" not in grains: + grains["mdata"] = {} + if "sdc" not in grains["mdata"]: + grains["mdata"]["sdc"] = {} - log.debug('found mdata entry sdc:%s with value %s', mdata_grain, mdata_value) - mdata_grain = mdata_grain.replace('-', '_') - mdata_grain = mdata_grain.replace(':', '_') + log.debug( + "found mdata entry sdc:%s with value %s", mdata_grain, mdata_value + ) + mdata_grain = mdata_grain.replace("-", "_") + mdata_grain = mdata_grain.replace(":", "_") if mdata_grain in sdc_json_keys: - grains['mdata']['sdc'][mdata_grain] = salt.utils.json.loads(mdata_value) + grains["mdata"]["sdc"][mdata_grain] = salt.utils.json.loads(mdata_value) else: - grains['mdata']['sdc'][mdata_grain] = mdata_value + grains["mdata"]["sdc"][mdata_grain] = mdata_value return grains def mdata(): - ''' + """ Provide grains from the SmartOS metadata - ''' + """ grains = {} - mdata_list = salt.utils.path.which('mdata-list') - mdata_get = salt.utils.path.which('mdata-get') + mdata_list = salt.utils.path.which("mdata-list") + mdata_get = salt.utils.path.which("mdata-get") - grains = salt.utils.dictupdate.update(grains, _user_mdata(mdata_list, mdata_get), merge_lists=True) - grains = salt.utils.dictupdate.update(grains, _sdc_mdata(mdata_list, mdata_get), merge_lists=True) + grains = salt.utils.dictupdate.update( + grains, _user_mdata(mdata_list, mdata_get), merge_lists=True + ) + grains = salt.utils.dictupdate.update( + grains, _sdc_mdata(mdata_list, mdata_get), merge_lists=True + ) return grains + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/grains/metadata.py b/salt/grains/metadata.py index 11512b47805..676153ff32e 100644 --- a/salt/grains/metadata.py +++ b/salt/grains/metadata.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Grains from cloud metadata servers at 169.254.169.254 .. versionadded:: 2017.7.0 @@ -13,7 +13,7 @@ metadata server set `metadata_server_grains: True`. metadata_server_grains: True -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -27,56 +27,60 @@ import salt.utils.http as http import salt.utils.json import salt.utils.stringutils - # metadata server information -IP = '169.254.169.254' -HOST = 'http://{0}/'.format(IP) +IP = "169.254.169.254" +HOST = "http://{0}/".format(IP) def __virtual__(): - if __opts__.get('metadata_server_grains', False) is False: + if __opts__.get("metadata_server_grains", False) is False: return False sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(.1) + sock.settimeout(0.1) result = sock.connect_ex((IP, 80)) if result != 0: return False - if http.query(os.path.join(HOST, 'latest/'), status=True).get('status') != 200: + if http.query(os.path.join(HOST, "latest/"), status=True).get("status") != 200: return False return True def _search(prefix="latest/"): - ''' + """ Recursively look up all grains in the metadata server - ''' + """ ret = {} linedata = http.query(os.path.join(HOST, prefix), headers=True) - if 'body' not in linedata: + if "body" not in linedata: return ret - body = salt.utils.stringutils.to_unicode(linedata['body']) - if linedata['headers'].get('Content-Type', 'text/plain') == 'application/octet-stream': + body = salt.utils.stringutils.to_unicode(linedata["body"]) + if ( + linedata["headers"].get("Content-Type", "text/plain") + == "application/octet-stream" + ): return body - for line in body.split('\n'): - if line.endswith('/'): + for line in body.split("\n"): + if line.endswith("/"): ret[line[:-1]] = _search(prefix=os.path.join(prefix, line)) - elif prefix == 'latest/': + elif prefix == "latest/": # (gtmanfred) The first level should have a forward slash since # they have stuff underneath. This will not be doubled up though, # because lines ending with a slash are checked first. - ret[line] = _search(prefix=os.path.join(prefix, line + '/')) - elif line.endswith(('dynamic', 'meta-data')): + ret[line] = _search(prefix=os.path.join(prefix, line + "/")) + elif line.endswith(("dynamic", "meta-data")): ret[line] = _search(prefix=os.path.join(prefix, line)) - elif '=' in line: - key, value = line.split('=') + elif "=" in line: + key, value = line.split("=") ret[value] = _search(prefix=os.path.join(prefix, key)) else: - retdata = http.query(os.path.join(HOST, prefix, line)).get('body', None) + retdata = http.query(os.path.join(HOST, prefix, line)).get("body", None) # (gtmanfred) This try except block is slightly faster than # checking if the string starts with a curly brace if isinstance(retdata, six.binary_type): try: - ret[line] = salt.utils.json.loads(salt.utils.stringutils.to_unicode(retdata)) + ret[line] = salt.utils.json.loads( + salt.utils.stringutils.to_unicode(retdata) + ) except ValueError: ret[line] = salt.utils.stringutils.to_unicode(retdata) else: diff --git a/salt/grains/minion_process.py b/salt/grains/minion_process.py index b1120de617f..0dc325422cc 100644 --- a/salt/grains/minion_process.py +++ b/salt/grains/minion_process.py @@ -1,67 +1,69 @@ # -*- coding: utf-8 -*- -''' +""" Set grains describing the minion process. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import os +import salt.utils.platform + # Import salt libs import salt.utils.user -import salt.utils.platform def _uid(): - ''' + """ Grain for the minion User ID - ''' + """ return salt.utils.user.get_uid() def _username(): - ''' + """ Grain for the minion username - ''' + """ return salt.utils.user.get_user() def _gid(): - ''' + """ Grain for the minion Group ID - ''' + """ return salt.utils.user.get_gid() def _groupname(): - ''' + """ Grain for the minion groupname - ''' + """ try: - return salt.utils.user.get_default_group(_username()) or '' + return salt.utils.user.get_default_group(_username()) or "" except KeyError: - return '' + return "" def _pid(): - ''' + """ Return the current process pid - ''' + """ return os.getpid() def grains(): - ''' + """ Return the grains dictionary - ''' + """ ret = { - 'username': _username(), - 'groupname': _groupname(), - 'pid': _pid(), + "username": _username(), + "groupname": _groupname(), + "pid": _pid(), } if not salt.utils.platform.is_windows(): - ret['gid'] = _gid() - ret['uid'] = _uid() + ret["gid"] = _gid() + ret["uid"] = _uid() return ret diff --git a/salt/grains/napalm.py b/salt/grains/napalm.py index d61ad9a38e0..45987c17766 100644 --- a/salt/grains/napalm.py +++ b/salt/grains/napalm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM Grains ============= @@ -14,23 +14,25 @@ Dependencies - :mod:`NAPALM proxy module ` .. versionadded:: 2016.11.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging -log = logging.getLogger(__name__) # Salt lib import salt.utils.dns import salt.utils.napalm +log = logging.getLogger(__name__) + + # ---------------------------------------------------------------------------------------------------------------------- # grains properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'napalm' -__proxyenabled__ = ['napalm'] +__virtualname__ = "napalm" +__proxyenabled__ = ["napalm"] # ---------------------------------------------------------------------------------------------------------------------- # global variables @@ -40,8 +42,8 @@ GRAINS_CACHE = {} DEVICE_CACHE = {} _FORBIDDEN_OPT_ARGS = [ - 'secret', # used by IOS to enter in enable mode - 'enable_password' # used by EOS + "secret", # used by IOS to enter in enable mode + "enable_password", # used by EOS ] # ---------------------------------------------------------------------------------------------------------------------- @@ -50,45 +52,42 @@ _FORBIDDEN_OPT_ARGS = [ def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helpers # ---------------------------------------------------------------------------------------------------------------------- def _retrieve_grains_cache(proxy=None): - ''' + """ Retrieves the grains from the network device if not cached already. - ''' + """ global GRAINS_CACHE if not GRAINS_CACHE: if proxy and salt.utils.napalm.is_proxy(__opts__): # if proxy var passed and is NAPALM-type proxy minion - GRAINS_CACHE = proxy['napalm.get_grains']() + GRAINS_CACHE = proxy["napalm.get_grains"]() elif not proxy and salt.utils.napalm.is_minion(__opts__): # if proxy var not passed and is running in a straight minion - GRAINS_CACHE = salt.utils.napalm.call( - DEVICE_CACHE, - 'get_facts', - **{} - ) + GRAINS_CACHE = salt.utils.napalm.call(DEVICE_CACHE, "get_facts", **{}) return GRAINS_CACHE def _retrieve_device_cache(proxy=None): - ''' + """ Loads the network device details if not cached already. - ''' + """ global DEVICE_CACHE if not DEVICE_CACHE: if proxy and salt.utils.napalm.is_proxy(__opts__): # if proxy var passed and is NAPALM-type proxy minion - if 'napalm.get_device' in proxy: - DEVICE_CACHE = proxy['napalm.get_device']() + if "napalm.get_device" in proxy: + DEVICE_CACHE = proxy["napalm.get_device"]() elif not proxy and salt.utils.napalm.is_minion(__opts__): # if proxy var not passed and is running in a straight minion DEVICE_CACHE = salt.utils.napalm.get_device(__opts__) @@ -96,28 +95,29 @@ def _retrieve_device_cache(proxy=None): def _get_grain(name, proxy=None): - ''' + """ Retrieves the grain value from the cached dictionary. - ''' + """ grains = _retrieve_grains_cache(proxy=proxy) - if grains.get('result', False) and grains.get('out', {}): - return grains.get('out').get(name) + if grains.get("result", False) and grains.get("out", {}): + return grains.get("out").get(name) def _get_device_grain(name, proxy=None): - ''' + """ Retrieves device-specific grains. - ''' + """ device = _retrieve_device_cache(proxy=proxy) return device.get(name.upper()) + # ---------------------------------------------------------------------------------------------------------------------- # actual grains # ---------------------------------------------------------------------------------------------------------------------- def getos(proxy=None): - ''' + """ Returns the Operating System name running on the network device. Example: junos, iosxr, eos, ios etc. @@ -127,12 +127,12 @@ def getos(proxy=None): .. code-block:: bash salt -G 'os:junos' test.ping - ''' - return {'os': _get_device_grain('driver_name', proxy=proxy)} + """ + return {"os": _get_device_grain("driver_name", proxy=proxy)} def version(proxy=None): - ''' + """ Returns the OS version. Example: 13.3R6.5, 6.0.2 etc. @@ -155,12 +155,12 @@ def version(proxy=None): MX480 edge01.muc01: MX240 - ''' - return {'version': _get_grain('os_version', proxy=proxy)} + """ + return {"version": _get_grain("os_version", proxy=proxy)} def model(proxy=None): - ''' + """ Returns the network device chassis model. Example: MX480, ASR-9904-AC etc. @@ -170,12 +170,12 @@ def model(proxy=None): .. code-block:: bash salt -G 'model:MX480' net.traceroute 8.8.8.8 - ''' - return {'model': _get_grain('model', proxy=proxy)} + """ + return {"model": _get_grain("model", proxy=proxy)} def serial(proxy=None): - ''' + """ Returns the chassis serial number. Example: FOX1234W00F @@ -198,12 +198,12 @@ def serial(proxy=None): FOXW00F003 edge01.mrs01: FOXW00F004 - ''' - return {'serial': _get_grain('serial_number', proxy=proxy)} + """ + return {"serial": _get_grain("serial_number", proxy=proxy)} def vendor(proxy=None): - ''' + """ Returns the network device vendor. Example: juniper, cisco, arista etc. @@ -213,12 +213,12 @@ def vendor(proxy=None): .. code-block:: bash salt -G 'vendor:cisco' net.cli "shut" - ''' - return {'vendor': _get_grain('vendor', proxy=proxy)} + """ + return {"vendor": _get_grain("vendor", proxy=proxy)} def uptime(proxy=None): - ''' + """ Returns the uptime in seconds. CLI Example - select all devices started/restarted within the last hour: @@ -226,12 +226,12 @@ def uptime(proxy=None): .. code-block:: bash salt -G 'uptime<3600' test.ping - ''' - return {'uptime': _get_grain('uptime', proxy=proxy)} + """ + return {"uptime": _get_grain("uptime", proxy=proxy)} def interfaces(proxy=None): - ''' + """ Returns the complete interfaces list of the network device. Example: ['lc-0/0/0', 'pfe-0/0/0', 'xe-1/3/0', 'lo0', 'irb', 'demux0', 'fxp0'] @@ -258,12 +258,12 @@ def interfaces(proxy=None): True edge01.kix01: True - ''' - return {'interfaces': _get_grain('interface_list', proxy=proxy)} + """ + return {"interfaces": _get_grain("interface_list", proxy=proxy)} def username(proxy=None): - ''' + """ Return the username. .. versionadded:: 2017.7.0 @@ -282,15 +282,15 @@ def username(proxy=None): True device2: True - ''' + """ if proxy and salt.utils.napalm.is_proxy(__opts__): # only if proxy will override the username # otherwise will use the default Salt grains - return {'username': _get_device_grain('username', proxy=proxy)} + return {"username": _get_device_grain("username", proxy=proxy)} def hostname(proxy=None): - ''' + """ Return the hostname as configured on the network device. CLI Example: @@ -309,12 +309,12 @@ def hostname(proxy=None): edge01.bjm01 device3: edge01.flw01 - ''' - return {'hostname': _get_grain('hostname', proxy=proxy)} + """ + return {"hostname": _get_grain("hostname", proxy=proxy)} def host(proxy=None): - ''' + """ This grain is set by the NAPALM grain module only when running in a proxy minion. When Salt is installed directly on the network device, @@ -349,15 +349,15 @@ def host(proxy=None): ip-172-31-11-193.us-east-2.compute.internal device3: ip-172-31-2-181.us-east-2.compute.internal - ''' + """ if proxy and salt.utils.napalm.is_proxy(__opts__): # this grain is set only when running in a proxy minion # otherwise will use the default Salt grains - return {'host': _get_device_grain('hostname', proxy=proxy)} + return {"host": _get_device_grain("hostname", proxy=proxy)} def host_dns(proxy=None): - ''' + """ Return the DNS information of the host. This grain is a dictionary having two keys: @@ -401,29 +401,24 @@ def host_dns(proxy=None): - 172.31.8.167 AAAA: - fd0f:9fd6:5fab::1 - ''' - if not __opts__.get('napalm_host_dns_grain', False): + """ + if not __opts__.get("napalm_host_dns_grain", False): return device_host = host(proxy=proxy) if device_host: - device_host_value = device_host['host'] - host_dns_ret = { - 'host_dns': { - 'A': [], - 'AAAA': [] - } - } - dns_a = salt.utils.dns.lookup(device_host_value, 'A') + device_host_value = device_host["host"] + host_dns_ret = {"host_dns": {"A": [], "AAAA": []}} + dns_a = salt.utils.dns.lookup(device_host_value, "A") if dns_a: - host_dns_ret['host_dns']['A'] = dns_a - dns_aaaa = salt.utils.dns.lookup(device_host_value, 'AAAA') + host_dns_ret["host_dns"]["A"] = dns_a + dns_aaaa = salt.utils.dns.lookup(device_host_value, "AAAA") if dns_aaaa: - host_dns_ret['host_dns']['AAAA'] = dns_aaaa + host_dns_ret["host_dns"]["AAAA"] = dns_aaaa return host_dns_ret def optional_args(proxy=None): - ''' + """ Return the connection optional args. .. note:: @@ -446,9 +441,9 @@ def optional_args(proxy=None): True device2: True - ''' - opt_args = _get_device_grain('optional_args', proxy=proxy) or {} + """ + opt_args = _get_device_grain("optional_args", proxy=proxy) or {} if opt_args and _FORBIDDEN_OPT_ARGS: for arg in _FORBIDDEN_OPT_ARGS: opt_args.pop(arg, None) - return {'optional_args': opt_args} + return {"optional_args": opt_args} diff --git a/salt/grains/nvme.py b/salt/grains/nvme.py index 697c8562cd6..27e86f7320d 100644 --- a/salt/grains/nvme.py +++ b/salt/grains/nvme.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Grains for NVMe Qualified Names (NQN). .. versionadded:: 3000 @@ -9,7 +9,7 @@ To enable these grains set `nvme_grains: True`. .. code-block:: yaml nvme_grains: True -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -21,41 +21,41 @@ import salt.utils.files import salt.utils.path import salt.utils.platform -__virtualname__ = 'nvme' +__virtualname__ = "nvme" # Get logging started log = logging.getLogger(__name__) def __virtual__(): - if __opts__.get('nvme_grains', False) is False: + if __opts__.get("nvme_grains", False) is False: return False return __virtualname__ def nvme_nqn(): - ''' + """ Return NVMe NQN - ''' + """ grains = {} - grains['nvme_nqn'] = False + grains["nvme_nqn"] = False if salt.utils.platform.is_linux(): - grains['nvme_nqn'] = _linux_nqn() + grains["nvme_nqn"] = _linux_nqn() return grains def _linux_nqn(): - ''' + """ Return NVMe NQN from a Linux host. - ''' + """ ret = [] - initiator = '/etc/nvme/hostnqn' + initiator = "/etc/nvme/hostnqn" try: - with salt.utils.files.fopen(initiator, 'r') as _nvme: + with salt.utils.files.fopen(initiator, "r") as _nvme: for line in _nvme: line = line.strip() - if line.startswith('nqn.'): + if line.startswith("nqn."): ret.append(line) except IOError as ex: if ex.errno != errno.ENOENT: diff --git a/salt/grains/nxos.py b/salt/grains/nxos.py index c009f09861b..c02f8abdd13 100644 --- a/salt/grains/nxos.py +++ b/salt/grains/nxos.py @@ -1,29 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Grains for Cisco NX OS Switches Proxy minions .. versionadded: 2016.11.0 For documentation on setting up the nxos proxy minion look in the documentation for :mod:`salt.proxy.nxos`. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Libs -import salt.utils.platform +import logging + import salt.modules.nxos -import logging +# Import Salt Libs +import salt.utils.platform + log = logging.getLogger(__name__) -__proxyenabled__ = ['nxos'] -__virtualname__ = 'nxos' +__proxyenabled__ = ["nxos"] +__virtualname__ = "nxos" def __virtual__(): try: - if salt.utils.platform.is_proxy() and __opts__['proxy']['proxytype'] == 'nxos': + if salt.utils.platform.is_proxy() and __opts__["proxy"]["proxytype"] == "nxos": return __virtualname__ except KeyError: pass @@ -34,6 +36,6 @@ def __virtual__(): def proxy_functions(proxy=None): if proxy is None: return {} - if proxy['nxos.initialized']() is False: + if proxy["nxos.initialized"]() is False: return {} - return {'nxos': proxy['nxos.grains']()} + return {"nxos": proxy["nxos.grains"]()} diff --git a/salt/grains/opts.py b/salt/grains/opts.py index b2ffe689b04..78f6c394a2c 100644 --- a/salt/grains/opts.py +++ b/salt/grains/opts.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Simple grain to merge the opts into the grains directly if the grain_opts configuration value is set -''' +""" from __future__ import absolute_import, print_function, unicode_literals def opts(): - ''' + """ Return the minion configuration settings - ''' - if __opts__.get('grain_opts', False) or \ - (isinstance(__pillar__, dict) and __pillar__.get('grain_opts', False)): + """ + if __opts__.get("grain_opts", False) or ( + isinstance(__pillar__, dict) and __pillar__.get("grain_opts", False) + ): return __opts__ return {} diff --git a/salt/grains/panos.py b/salt/grains/panos.py index 0c9f7aada9c..aead7896426 100644 --- a/salt/grains/panos.py +++ b/salt/grains/panos.py @@ -1,28 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" Generate baseline proxy minion grains for panos hosts. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.proxy.panos + # Import Salt Libs import salt.utils.platform -import salt.proxy.panos -__proxyenabled__ = ['panos'] -__virtualname__ = 'panos' +__proxyenabled__ = ["panos"] +__virtualname__ = "panos" log = logging.getLogger(__file__) -GRAINS_CACHE = {'os_family': 'panos'} +GRAINS_CACHE = {"os_family": "panos"} def __virtual__(): try: - if salt.utils.platform.is_proxy() and __opts__['proxy']['proxytype'] == 'panos': + if salt.utils.platform.is_proxy() and __opts__["proxy"]["proxytype"] == "panos": return __virtualname__ except KeyError: pass @@ -33,6 +35,6 @@ def __virtual__(): def panos(proxy=None): if not proxy: return {} - if proxy['panos.initialized']() is False: + if proxy["panos.initialized"]() is False: return {} - return {'panos': proxy['panos.grains']()} + return {"panos": proxy["panos.grains"]()} diff --git a/salt/grains/philips_hue.py b/salt/grains/philips_hue.py index 9ca6b1e2d3d..c52cedf4783 100644 --- a/salt/grains/philips_hue.py +++ b/salt/grains/philips_hue.py @@ -14,39 +14,39 @@ # See the License for the specific language governing permissions and # limitations under the License. -''' +""" Static grains for the Philips HUE lamps .. versionadded:: 2015.8.3 -''' +""" -__proxyenabled__ = ['philips_hue'] +__proxyenabled__ = ["philips_hue"] -__virtualname__ = 'hue' +__virtualname__ = "hue" def __virtual__(): - if 'proxy' not in __opts__: + if "proxy" not in __opts__: return False else: return __virtualname__ def kernel(): - return {'kernel': 'RTOS'} + return {"kernel": "RTOS"} def os(): - return {'os': 'FreeRTOS'} + return {"os": "FreeRTOS"} def os_family(): - return {'os_family': 'RTOS'} + return {"os_family": "RTOS"} def vendor(): - return {'vendor': 'Philips'} + return {"vendor": "Philips"} def product(): - return {'product': 'HUE'} + return {"product": "HUE"} diff --git a/salt/grains/rest_sample.py b/salt/grains/rest_sample.py index 4ff9e3a124a..02ee95b2102 100644 --- a/salt/grains/rest_sample.py +++ b/salt/grains/rest_sample.py @@ -1,18 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Generate baseline proxy minion grains -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import salt.utils.platform -__proxyenabled__ = ['rest_sample'] +__proxyenabled__ = ["rest_sample"] -__virtualname__ = 'rest_sample' +__virtualname__ = "rest_sample" def __virtual__(): try: - if salt.utils.platform.is_proxy() and __opts__['proxy']['proxytype'] == 'rest_sample': + if ( + salt.utils.platform.is_proxy() + and __opts__["proxy"]["proxytype"] == "rest_sample" + ): return __virtualname__ except KeyError: pass @@ -21,31 +25,31 @@ def __virtual__(): def kernel(): - return {'kernel': 'proxy'} + return {"kernel": "proxy"} def proxy_functions(proxy): - ''' + """ The loader will execute functions with one argument and pass a reference to the proxymodules LazyLoader object. However, grains sometimes get called before the LazyLoader object is setup so `proxy` might be None. - ''' + """ if proxy: - return {'proxy_functions': proxy['rest_sample.fns']()} + return {"proxy_functions": proxy["rest_sample.fns"]()} def os(): - return {'os': 'RestExampleOS'} + return {"os": "RestExampleOS"} def location(): - return {'location': 'In this darn virtual machine. Let me out!'} + return {"location": "In this darn virtual machine. Let me out!"} def os_family(): - return {'os_family': 'proxy'} + return {"os_family": "proxy"} def os_data(): - return {'os_data': 'funkyHttp release 1.0.a.4.g'} + return {"os_data": "funkyHttp release 1.0.a.4.g"} diff --git a/salt/grains/smartos.py b/salt/grains/smartos.py index 6a92265c40b..8681ed9f38d 100644 --- a/salt/grains/smartos.py +++ b/salt/grains/smartos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" SmartOS grain provider :maintainer: Jorge Schrauwen @@ -9,13 +9,18 @@ SmartOS grain provider .. versionadded:: nitrogen -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os import re -import logging + +# Solve the Chicken and egg problem where grains need to run before any +# of the modules are loaded and are generally available for any usage. +import salt.modules.cmdmod # Import salt libs import salt.utils.dictupdate @@ -25,31 +30,27 @@ import salt.utils.platform import salt.utils.stringutils from salt.ext.six.moves import zip -# Solve the Chicken and egg problem where grains need to run before any -# of the modules are loaded and are generally available for any usage. -import salt.modules.cmdmod - -__virtualname__ = 'smartos' +__virtualname__ = "smartos" __salt__ = { - 'cmd.run': salt.modules.cmdmod.run, + "cmd.run": salt.modules.cmdmod.run, } log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load when we are on SmartOS - ''' + """ if salt.utils.platform.is_smartos(): return __virtualname__ return False def _smartos_computenode_data(): - ''' + """ Return useful information from a SmartOS compute node - ''' + """ # Provides: # vms_total # vms_running @@ -63,137 +64,149 @@ def _smartos_computenode_data(): # collect vm data vms = {} - for vm in __salt__['cmd.run']('vmadm list -p -o uuid,alias,state,type').split("\n"): - vm = dict(list(zip(['uuid', 'alias', 'state', 'type'], vm.split(':')))) - vms[vm['uuid']] = vm - del vms[vm['uuid']]['uuid'] + for vm in __salt__["cmd.run"]("vmadm list -p -o uuid,alias,state,type").split("\n"): + vm = dict(list(zip(["uuid", "alias", "state", "type"], vm.split(":")))) + vms[vm["uuid"]] = vm + del vms[vm["uuid"]]["uuid"] # set vm grains - grains['computenode_vms_total'] = len(vms) - grains['computenode_vms_running'] = 0 - grains['computenode_vms_stopped'] = 0 - grains['computenode_vms_type'] = {'KVM': 0, 'LX': 0, 'OS': 0} + grains["computenode_vms_total"] = len(vms) + grains["computenode_vms_running"] = 0 + grains["computenode_vms_stopped"] = 0 + grains["computenode_vms_type"] = {"KVM": 0, "LX": 0, "OS": 0} for vm in vms: - if vms[vm]['state'].lower() == 'running': - grains['computenode_vms_running'] += 1 - elif vms[vm]['state'].lower() == 'stopped': - grains['computenode_vms_stopped'] += 1 + if vms[vm]["state"].lower() == "running": + grains["computenode_vms_running"] += 1 + elif vms[vm]["state"].lower() == "stopped": + grains["computenode_vms_stopped"] += 1 - if vms[vm]['type'] not in grains['computenode_vms_type']: + if vms[vm]["type"] not in grains["computenode_vms_type"]: # NOTE: be prepared for when bhyve gets its own type - grains['computenode_vms_type'][vms[vm]['type']] = 0 - grains['computenode_vms_type'][vms[vm]['type']] += 1 + grains["computenode_vms_type"][vms[vm]["type"]] = 0 + grains["computenode_vms_type"][vms[vm]["type"]] += 1 # sysinfo derived grains - sysinfo = salt.utils.json.loads(__salt__['cmd.run']('sysinfo')) - grains['computenode_sdc_version'] = sysinfo['SDC Version'] - grains['computenode_vm_capable'] = sysinfo['VM Capable'] - if sysinfo['VM Capable']: - grains['computenode_vm_hw_virt'] = sysinfo['CPU Virtualization'] + sysinfo = salt.utils.json.loads(__salt__["cmd.run"]("sysinfo")) + grains["computenode_sdc_version"] = sysinfo["SDC Version"] + grains["computenode_vm_capable"] = sysinfo["VM Capable"] + if sysinfo["VM Capable"]: + grains["computenode_vm_hw_virt"] = sysinfo["CPU Virtualization"] # sysinfo derived smbios grains - grains['manufacturer'] = sysinfo['Manufacturer'] - grains['productname'] = sysinfo['Product'] - grains['uuid'] = sysinfo['UUID'] + grains["manufacturer"] = sysinfo["Manufacturer"] + grains["productname"] = sysinfo["Product"] + grains["uuid"] = sysinfo["UUID"] return grains def _smartos_zone_data(): - ''' + """ Return useful information from a SmartOS zone - ''' + """ # Provides: # zoneid # zonename # imageversion grains = { - 'zoneid': __salt__['cmd.run']('zoneadm list -p | awk -F: \'{ print $1 }\'', python_shell=True), - 'zonename': __salt__['cmd.run']('zonename'), - 'imageversion': 'Unknown', + "zoneid": __salt__["cmd.run"]( + "zoneadm list -p | awk -F: '{ print $1 }'", python_shell=True + ), + "zonename": __salt__["cmd.run"]("zonename"), + "imageversion": "Unknown", } - imageversion = re.compile('Image:\\s(.+)') - if os.path.isfile('/etc/product'): - with salt.utils.files.fopen('/etc/product', 'r') as fp_: + imageversion = re.compile("Image:\\s(.+)") + if os.path.isfile("/etc/product"): + with salt.utils.files.fopen("/etc/product", "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) match = imageversion.match(line) if match: - grains['imageversion'] = match.group(1) + grains["imageversion"] = match.group(1) return grains def _smartos_zone_pkgsrc_data(): - ''' + """ SmartOS zone pkgsrc information - ''' + """ # Provides: # pkgsrcversion # pkgsrcpath grains = { - 'pkgsrcversion': 'Unknown', - 'pkgsrcpath': 'Unknown', + "pkgsrcversion": "Unknown", + "pkgsrcpath": "Unknown", } - pkgsrcversion = re.compile('^release:\\s(.+)') - if os.path.isfile('/etc/pkgsrc_version'): - with salt.utils.files.fopen('/etc/pkgsrc_version', 'r') as fp_: + pkgsrcversion = re.compile("^release:\\s(.+)") + if os.path.isfile("/etc/pkgsrc_version"): + with salt.utils.files.fopen("/etc/pkgsrc_version", "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) match = pkgsrcversion.match(line) if match: - grains['pkgsrcversion'] = match.group(1) + grains["pkgsrcversion"] = match.group(1) - pkgsrcpath = re.compile('PKG_PATH=(.+)') - if os.path.isfile('/opt/local/etc/pkg_install.conf'): - with salt.utils.files.fopen('/opt/local/etc/pkg_install.conf', 'r') as fp_: + pkgsrcpath = re.compile("PKG_PATH=(.+)") + if os.path.isfile("/opt/local/etc/pkg_install.conf"): + with salt.utils.files.fopen("/opt/local/etc/pkg_install.conf", "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) match = pkgsrcpath.match(line) if match: - grains['pkgsrcpath'] = match.group(1) + grains["pkgsrcpath"] = match.group(1) return grains def _smartos_zone_pkgin_data(): - ''' + """ SmartOS zone pkgsrc information - ''' + """ # Provides: # pkgin_repositories grains = { - 'pkgin_repositories': [], + "pkgin_repositories": [], } - pkginrepo = re.compile('^(?:https|http|ftp|file)://.*$') - if os.path.isfile('/opt/local/etc/pkgin/repositories.conf'): - with salt.utils.files.fopen('/opt/local/etc/pkgin/repositories.conf', 'r') as fp_: + pkginrepo = re.compile("^(?:https|http|ftp|file)://.*$") + if os.path.isfile("/opt/local/etc/pkgin/repositories.conf"): + with salt.utils.files.fopen( + "/opt/local/etc/pkgin/repositories.conf", "r" + ) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) if pkginrepo.match(line): - grains['pkgin_repositories'].append(line) + grains["pkgin_repositories"].append(line) return grains def smartos(): - ''' + """ Provide grains for SmartOS - ''' + """ grains = {} if salt.utils.platform.is_smartos_zone(): - grains = salt.utils.dictupdate.update(grains, _smartos_zone_data(), merge_lists=True) - grains = salt.utils.dictupdate.update(grains, _smartos_zone_pkgsrc_data(), merge_lists=True) - grains = salt.utils.dictupdate.update(grains, _smartos_zone_pkgin_data(), merge_lists=True) + grains = salt.utils.dictupdate.update( + grains, _smartos_zone_data(), merge_lists=True + ) + grains = salt.utils.dictupdate.update( + grains, _smartos_zone_pkgsrc_data(), merge_lists=True + ) + grains = salt.utils.dictupdate.update( + grains, _smartos_zone_pkgin_data(), merge_lists=True + ) elif salt.utils.platform.is_smartos_globalzone(): - grains = salt.utils.dictupdate.update(grains, _smartos_computenode_data(), merge_lists=True) + grains = salt.utils.dictupdate.update( + grains, _smartos_computenode_data(), merge_lists=True + ) return grains diff --git a/salt/grains/ssh_sample.py b/salt/grains/ssh_sample.py index e8c04081e92..1985a14cb61 100644 --- a/salt/grains/ssh_sample.py +++ b/salt/grains/ssh_sample.py @@ -1,18 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Generate baseline proxy minion grains -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import salt.utils.platform -__proxyenabled__ = ['ssh_sample'] +__proxyenabled__ = ["ssh_sample"] -__virtualname__ = 'ssh_sample' +__virtualname__ = "ssh_sample" def __virtual__(): try: - if salt.utils.platform.is_proxy() and __opts__['proxy']['proxytype'] == 'ssh_sample': + if ( + salt.utils.platform.is_proxy() + and __opts__["proxy"]["proxytype"] == "ssh_sample" + ): return __virtualname__ except KeyError: pass @@ -21,22 +25,22 @@ def __virtual__(): def kernel(): - return {'kernel': 'proxy'} + return {"kernel": "proxy"} def proxy_functions(proxy): - ''' + """ The loader will execute functions with one argument and pass a reference to the proxymodules LazyLoader object. However, grains sometimes get called before the LazyLoader object is setup so `proxy` might be None. - ''' - return {'proxy_functions': proxy['ssh_sample.fns']()} + """ + return {"proxy_functions": proxy["ssh_sample.fns"]()} def location(): - return {'location': 'At the other end of an SSH Tunnel!!'} + return {"location": "At the other end of an SSH Tunnel!!"} def os_data(): - return {'os_data': 'DumbShell Endpoint release 4.09.g'} + return {"os_data": "DumbShell Endpoint release 4.09.g"} diff --git a/salt/grains/zfs.py b/salt/grains/zfs.py index fec70dfe8d0..d351611eb2a 100644 --- a/salt/grains/zfs.py +++ b/salt/grains/zfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" ZFS grain provider :maintainer: Jorge Schrauwen @@ -9,80 +9,81 @@ ZFS grain provider .. versionadded:: 2018.3.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging +# Solve the Chicken and egg problem where grains need to run before any +# of the modules are loaded and are generally available for any usage. +import salt.modules.cmdmod + # Import salt libs import salt.utils.dictupdate import salt.utils.path import salt.utils.platform - -# Solve the Chicken and egg problem where grains need to run before any -# of the modules are loaded and are generally available for any usage. -import salt.modules.cmdmod import salt.utils.zfs -__virtualname__ = 'zfs' +__virtualname__ = "zfs" __salt__ = { - 'cmd.run': salt.modules.cmdmod.run, + "cmd.run": salt.modules.cmdmod.run, } __utils__ = { - 'zfs.is_supported': salt.utils.zfs.is_supported, - 'zfs.has_feature_flags': salt.utils.zfs.has_feature_flags, - 'zfs.zpool_command': salt.utils.zfs.zpool_command, - 'zfs.to_size': salt.utils.zfs.to_size, + "zfs.is_supported": salt.utils.zfs.is_supported, + "zfs.has_feature_flags": salt.utils.zfs.has_feature_flags, + "zfs.zpool_command": salt.utils.zfs.zpool_command, + "zfs.to_size": salt.utils.zfs.to_size, } log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Load zfs grains - ''' + """ # NOTE: we always load this grain so we can properly export # at least the zfs_support grain # except for Windows... don't try to load this on Windows (#51703) if salt.utils.platform.is_windows(): - return False, 'ZFS: Not available on Windows' + return False, "ZFS: Not available on Windows" return __virtualname__ def _zfs_pool_data(): - ''' + """ Provide grains about zpools - ''' + """ grains = {} # collect zpool data - zpool_list_cmd = __utils__['zfs.zpool_command']( - 'list', - flags=['-H'], - opts={'-o': 'name,size'}, + zpool_list_cmd = __utils__["zfs.zpool_command"]( + "list", flags=["-H"], opts={"-o": "name,size"}, ) - for zpool in __salt__['cmd.run'](zpool_list_cmd, ignore_retcode=True).splitlines(): - if 'zpool' not in grains: - grains['zpool'] = {} + for zpool in __salt__["cmd.run"](zpool_list_cmd, ignore_retcode=True).splitlines(): + if "zpool" not in grains: + grains["zpool"] = {} zpool = zpool.split() - grains['zpool'][zpool[0]] = __utils__['zfs.to_size'](zpool[1], False) + grains["zpool"][zpool[0]] = __utils__["zfs.to_size"](zpool[1], False) # return grain data return grains def zfs(): - ''' + """ Provide grains for zfs/zpool - ''' + """ grains = {} - grains['zfs_support'] = __utils__['zfs.is_supported']() - grains['zfs_feature_flags'] = __utils__['zfs.has_feature_flags']() - if grains['zfs_support']: - grains = salt.utils.dictupdate.update(grains, _zfs_pool_data(), merge_lists=True) + grains["zfs_support"] = __utils__["zfs.is_supported"]() + grains["zfs_feature_flags"] = __utils__["zfs.has_feature_flags"]() + if grains["zfs_support"]: + grains = salt.utils.dictupdate.update( + grains, _zfs_pool_data(), merge_lists=True + ) return grains + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/key.py b/salt/key.py index 6cc4c33a755..6d6cf5ded4a 100644 --- a/salt/key.py +++ b/salt/key.py @@ -1,15 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" The Salt Key backend API and interface used by the CLI. The Key class can be used to manage salt keys directly without interfacing with the CLI. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import shutil + import fnmatch import logging +import os +import shutil # Import salt libs import salt.cache @@ -33,6 +34,7 @@ import salt.utils.user # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six from salt.ext.six.moves import input, zip_longest + # pylint: enable=import-error,no-name-in-module,redefined-builtin log = logging.getLogger(__name__) @@ -43,100 +45,108 @@ def get_key(opts): class KeyCLI(object): - ''' + """ Manage key CLI operations - ''' - CLI_KEY_MAP = {'list': 'list_status', - 'delete': 'delete_key', - 'gen_signature': 'gen_keys_signature', - 'print': 'key_str', - } + """ + + CLI_KEY_MAP = { + "list": "list_status", + "delete": "delete_key", + "gen_signature": "gen_keys_signature", + "print": "key_str", + } def __init__(self, opts): self.opts = opts self.client = salt.wheel.WheelClient(opts) self.key = Key # instantiate the key object for masterless mode - if not opts.get('eauth'): + if not opts.get("eauth"): self.key = self.key(opts) self.auth = None def _update_opts(self): # get the key command - for cmd in ('gen_keys', - 'gen_signature', - 'list', - 'list_all', - 'print', - 'print_all', - 'accept', - 'accept_all', - 'reject', - 'reject_all', - 'delete', - 'delete_all', - 'finger', - 'finger_all', - 'list_all'): # last is default + for cmd in ( + "gen_keys", + "gen_signature", + "list", + "list_all", + "print", + "print_all", + "accept", + "accept_all", + "reject", + "reject_all", + "delete", + "delete_all", + "finger", + "finger_all", + "list_all", + ): # last is default if self.opts[cmd]: break # set match if needed - if not cmd.startswith('gen_'): - if cmd == 'list_all': - self.opts['match'] = 'all' - elif cmd.endswith('_all'): - self.opts['match'] = '*' + if not cmd.startswith("gen_"): + if cmd == "list_all": + self.opts["match"] = "all" + elif cmd.endswith("_all"): + self.opts["match"] = "*" else: - self.opts['match'] = self.opts[cmd] - if cmd.startswith('accept'): - self.opts['include_rejected'] = self.opts['include_all'] or self.opts['include_rejected'] - self.opts['include_accepted'] = False - elif cmd.startswith('reject'): - self.opts['include_accepted'] = self.opts['include_all'] or self.opts['include_accepted'] - self.opts['include_rejected'] = False - elif cmd == 'gen_keys': - self.opts['keydir'] = self.opts['gen_keys_dir'] - self.opts['keyname'] = self.opts['gen_keys'] + self.opts["match"] = self.opts[cmd] + if cmd.startswith("accept"): + self.opts["include_rejected"] = ( + self.opts["include_all"] or self.opts["include_rejected"] + ) + self.opts["include_accepted"] = False + elif cmd.startswith("reject"): + self.opts["include_accepted"] = ( + self.opts["include_all"] or self.opts["include_accepted"] + ) + self.opts["include_rejected"] = False + elif cmd == "gen_keys": + self.opts["keydir"] = self.opts["gen_keys_dir"] + self.opts["keyname"] = self.opts["gen_keys"] # match is set to opts, now we can forget about *_all commands - self.opts['fun'] = cmd.replace('_all', '') + self.opts["fun"] = cmd.replace("_all", "") def _init_auth(self): if self.auth: return low = {} - skip_perm_errors = self.opts['eauth'] != '' + skip_perm_errors = self.opts["eauth"] != "" - if self.opts['eauth']: - if 'token' in self.opts: + if self.opts["eauth"]: + if "token" in self.opts: try: - with salt.utils.files.fopen(os.path.join(self.opts['cachedir'], '.root_key'), 'r') as fp_: - low['key'] = \ - salt.utils.stringutils.to_unicode(fp_.readline()) + with salt.utils.files.fopen( + os.path.join(self.opts["cachedir"], ".root_key"), "r" + ) as fp_: + low["key"] = salt.utils.stringutils.to_unicode(fp_.readline()) except IOError: - low['token'] = self.opts['token'] + low["token"] = self.opts["token"] # # If using eauth and a token hasn't already been loaded into # low, prompt the user to enter auth credentials - if 'token' not in low and 'key' not in low and self.opts['eauth']: + if "token" not in low and "key" not in low and self.opts["eauth"]: # This is expensive. Don't do it unless we need to. resolver = salt.auth.Resolver(self.opts) - res = resolver.cli(self.opts['eauth']) - if self.opts['mktoken'] and res: - tok = resolver.token_cli( - self.opts['eauth'], - res - ) + res = resolver.cli(self.opts["eauth"]) + if self.opts["mktoken"] and res: + tok = resolver.token_cli(self.opts["eauth"], res) if tok: - low['token'] = tok.get('token', '') + low["token"] = tok.get("token", "") if not res: - log.error('Authentication failed') + log.error("Authentication failed") return {} low.update(res) - low['eauth'] = self.opts['eauth'] + low["eauth"] = self.opts["eauth"] else: - low['user'] = salt.utils.user.get_specific_user() - low['key'] = salt.utils.master.get_master_key(low['user'], self.opts, skip_perm_errors) + low["user"] = salt.utils.user.get_specific_user() + low["key"] = salt.utils.master.get_master_key( + low["user"], self.opts, skip_perm_errors + ) self.auth = low @@ -147,8 +157,9 @@ class KeyCLI(object): if argspec.args: # Iterate in reverse order to ensure we get the correct default # value for the positional argument. - for arg, default in zip_longest(reversed(argspec.args), - reversed(argspec.defaults or ())): + for arg, default in zip_longest( + reversed(argspec.args), reversed(argspec.defaults or ()) + ): args.append(self.opts.get(arg, default)) # Reverse the args so that they are in the correct order args = args[::-1] @@ -156,31 +167,32 @@ class KeyCLI(object): if argspec.keywords is None: kwargs = {} else: - args, kwargs = salt.minion.load_args_and_kwargs( - fun, - args) + args, kwargs = salt.minion.load_args_and_kwargs(fun, args) return args, kwargs def _run_cmd(self, cmd, args=None): - if not self.opts.get('eauth'): + if not self.opts.get("eauth"): cmd = self.CLI_KEY_MAP.get(cmd, cmd) fun = getattr(self.key, cmd) args, kwargs = self._get_args_kwargs(fun, args) ret = fun(*args, **kwargs) - if (isinstance(ret, dict) and 'local' in ret and - cmd not in ('finger', 'finger_all')): - ret.pop('local', None) + if ( + isinstance(ret, dict) + and "local" in ret + and cmd not in ("finger", "finger_all") + ): + ret.pop("local", None) return ret - fstr = 'key.{0}'.format(cmd) + fstr = "key.{0}".format(cmd) fun = self.client.functions[fstr] args, kwargs = self._get_args_kwargs(fun, args) low = { - 'fun': fstr, - 'arg': args, - 'kwarg': kwargs, - } + "fun": fstr, + "arg": args, + "kwarg": kwargs, + } self._init_auth() low.update(self.auth) @@ -188,292 +200,305 @@ class KeyCLI(object): # Execute the key request! ret = self.client.cmd_sync(low) - ret = ret['data']['return'] - if (isinstance(ret, dict) and 'local' in ret and - cmd not in ('finger', 'finger_all')): - ret.pop('local', None) + ret = ret["data"]["return"] + if ( + isinstance(ret, dict) + and "local" in ret + and cmd not in ("finger", "finger_all") + ): + ret.pop("local", None) return ret def _filter_ret(self, cmd, ret): - if cmd.startswith('delete'): + if cmd.startswith("delete"): return ret keys = {} if self.key.PEND in ret: keys[self.key.PEND] = ret[self.key.PEND] - if self.opts['include_accepted'] and bool(ret.get(self.key.ACC)): + if self.opts["include_accepted"] and bool(ret.get(self.key.ACC)): keys[self.key.ACC] = ret[self.key.ACC] - if self.opts['include_rejected'] and bool(ret.get(self.key.REJ)): + if self.opts["include_rejected"] and bool(ret.get(self.key.REJ)): keys[self.key.REJ] = ret[self.key.REJ] - if self.opts['include_denied'] and bool(ret.get(self.key.DEN)): + if self.opts["include_denied"] and bool(ret.get(self.key.DEN)): keys[self.key.DEN] = ret[self.key.DEN] return keys def _print_no_match(self, cmd, match): - statuses = ['unaccepted'] - if self.opts['include_accepted']: - statuses.append('accepted') - if self.opts['include_rejected']: - statuses.append('rejected') - if self.opts['include_denied']: - statuses.append('denied') + statuses = ["unaccepted"] + if self.opts["include_accepted"]: + statuses.append("accepted") + if self.opts["include_rejected"]: + statuses.append("rejected") + if self.opts["include_denied"]: + statuses.append("denied") if len(statuses) == 1: stat_str = statuses[0] else: - stat_str = '{0} or {1}'.format(', '.join(statuses[:-1]), statuses[-1]) - msg = 'The key glob \'{0}\' does not match any {1} keys.'.format(match, stat_str) + stat_str = "{0} or {1}".format(", ".join(statuses[:-1]), statuses[-1]) + msg = "The key glob '{0}' does not match any {1} keys.".format(match, stat_str) print(msg) def run(self): - ''' + """ Run the logic for saltkey - ''' + """ self._update_opts() - cmd = self.opts['fun'] + cmd = self.opts["fun"] veri = None ret = None try: - if cmd in ('accept', 'reject', 'delete'): - ret = self._run_cmd('name_match') + if cmd in ("accept", "reject", "delete"): + ret = self._run_cmd("name_match") if not isinstance(ret, dict): - salt.output.display_output(ret, 'key', opts=self.opts) + salt.output.display_output(ret, "key", opts=self.opts) return ret ret = self._filter_ret(cmd, ret) if not ret: - self._print_no_match(cmd, self.opts['match']) + self._print_no_match(cmd, self.opts["match"]) return - print('The following keys are going to be {0}ed:'.format(cmd.rstrip('e'))) - salt.output.display_output(ret, 'key', opts=self.opts) + print( + "The following keys are going to be {0}ed:".format(cmd.rstrip("e")) + ) + salt.output.display_output(ret, "key", opts=self.opts) - if not self.opts.get('yes', False): + if not self.opts.get("yes", False): try: - if cmd.startswith('delete'): - veri = input('Proceed? [N/y] ') + if cmd.startswith("delete"): + veri = input("Proceed? [N/y] ") if not veri: - veri = 'n' + veri = "n" else: - veri = input('Proceed? [n/Y] ') + veri = input("Proceed? [n/Y] ") if not veri: - veri = 'y' + veri = "y" except KeyboardInterrupt: raise SystemExit("\nExiting on CTRL-c") # accept/reject/delete the same keys we're printed to the user - self.opts['match_dict'] = ret - self.opts.pop('match', None) + self.opts["match_dict"] = ret + self.opts.pop("match", None) list_ret = ret - if veri is None or veri.lower().startswith('y'): + if veri is None or veri.lower().startswith("y"): ret = self._run_cmd(cmd) - if cmd in ('accept', 'reject', 'delete'): - if cmd == 'delete': + if cmd in ("accept", "reject", "delete"): + if cmd == "delete": ret = list_ret for minions in ret.values(): for minion in minions: - print('Key for minion {0} {1}ed.'.format(minion, - cmd.rstrip('e'))) + print( + "Key for minion {0} {1}ed.".format( + minion, cmd.rstrip("e") + ) + ) elif isinstance(ret, dict): - salt.output.display_output(ret, 'key', opts=self.opts) + salt.output.display_output(ret, "key", opts=self.opts) else: - salt.output.display_output({'return': ret}, 'key', opts=self.opts) + salt.output.display_output({"return": ret}, "key", opts=self.opts) except salt.exceptions.SaltException as exc: - ret = '{0}'.format(exc) - if not self.opts.get('quiet', False): - salt.output.display_output(ret, 'nested', self.opts) + ret = "{0}".format(exc) + if not self.opts.get("quiet", False): + salt.output.display_output(ret, "nested", self.opts) return ret class Key(object): - ''' + """ The object that encapsulates saltkey actions - ''' - ACC = 'minions' - PEND = 'minions_pre' - REJ = 'minions_rejected' - DEN = 'minions_denied' + """ + + ACC = "minions" + PEND = "minions_pre" + REJ = "minions_rejected" + DEN = "minions_denied" def __init__(self, opts, io_loop=None): self.opts = opts - kind = self.opts.get('__role', '') # application kind + kind = self.opts.get("__role", "") # application kind if kind not in salt.utils.kinds.APPL_KINDS: emsg = "Invalid application kind = '{0}'.".format(kind) log.error(emsg) raise ValueError(emsg) self.event = salt.utils.event.get_event( - kind, - opts['sock_dir'], - opts['transport'], - opts=opts, - listen=False, - io_loop=io_loop - ) + kind, + opts["sock_dir"], + opts["transport"], + opts=opts, + listen=False, + io_loop=io_loop, + ) - self.passphrase = salt.utils.sdb.sdb_get(self.opts.get('signing_key_pass'), self.opts) + self.passphrase = salt.utils.sdb.sdb_get( + self.opts.get("signing_key_pass"), self.opts + ) def _check_minions_directories(self): - ''' + """ Return the minion keys directory paths - ''' - minions_accepted = os.path.join(self.opts['pki_dir'], self.ACC) - minions_pre = os.path.join(self.opts['pki_dir'], self.PEND) - minions_rejected = os.path.join(self.opts['pki_dir'], - self.REJ) + """ + minions_accepted = os.path.join(self.opts["pki_dir"], self.ACC) + minions_pre = os.path.join(self.opts["pki_dir"], self.PEND) + minions_rejected = os.path.join(self.opts["pki_dir"], self.REJ) - minions_denied = os.path.join(self.opts['pki_dir'], - self.DEN) + minions_denied = os.path.join(self.opts["pki_dir"], self.DEN) return minions_accepted, minions_pre, minions_rejected, minions_denied - def _get_key_attrs(self, keydir, keyname, - keysize, user): + def _get_key_attrs(self, keydir, keyname, keysize, user): if not keydir: - if 'gen_keys_dir' in self.opts: - keydir = self.opts['gen_keys_dir'] + if "gen_keys_dir" in self.opts: + keydir = self.opts["gen_keys_dir"] else: - keydir = self.opts['pki_dir'] + keydir = self.opts["pki_dir"] if not keyname: - if 'gen_keys' in self.opts: - keyname = self.opts['gen_keys'] + if "gen_keys" in self.opts: + keyname = self.opts["gen_keys"] else: - keyname = 'minion' + keyname = "minion" if not keysize: - keysize = self.opts['keysize'] + keysize = self.opts["keysize"] return keydir, keyname, keysize, user def gen_keys(self, keydir=None, keyname=None, keysize=None, user=None): - ''' + """ Generate minion RSA public keypair - ''' - keydir, keyname, keysize, user = self._get_key_attrs(keydir, keyname, - keysize, user) + """ + keydir, keyname, keysize, user = self._get_key_attrs( + keydir, keyname, keysize, user + ) salt.crypt.gen_keys(keydir, keyname, keysize, user, self.passphrase) - return salt.utils.crypt.pem_finger(os.path.join(keydir, keyname + '.pub')) + return salt.utils.crypt.pem_finger(os.path.join(keydir, keyname + ".pub")) def gen_signature(self, privkey, pubkey, sig_path): - ''' + """ Generate master public-key-signature - ''' - return salt.crypt.gen_signature(privkey, - pubkey, - sig_path, - self.passphrase) + """ + return salt.crypt.gen_signature(privkey, pubkey, sig_path, self.passphrase) - def gen_keys_signature(self, priv, pub, signature_path, auto_create=False, keysize=None): - ''' + def gen_keys_signature( + self, priv, pub, signature_path, auto_create=False, keysize=None + ): + """ Generate master public-key-signature - ''' + """ # check given pub-key if pub: if not os.path.isfile(pub): - return 'Public-key {0} does not exist'.format(pub) + return "Public-key {0} does not exist".format(pub) # default to master.pub else: - mpub = self.opts['pki_dir'] + '/' + 'master.pub' + mpub = self.opts["pki_dir"] + "/" + "master.pub" if os.path.isfile(mpub): pub = mpub # check given priv-key if priv: if not os.path.isfile(priv): - return 'Private-key {0} does not exist'.format(priv) + return "Private-key {0} does not exist".format(priv) # default to master_sign.pem else: - mpriv = self.opts['pki_dir'] + '/' + 'master_sign.pem' + mpriv = self.opts["pki_dir"] + "/" + "master_sign.pem" if os.path.isfile(mpriv): priv = mpriv if not priv: if auto_create: log.debug( - 'Generating new signing key-pair .%s.* in %s', - self.opts['master_sign_key_name'], self.opts['pki_dir'] + "Generating new signing key-pair .%s.* in %s", + self.opts["master_sign_key_name"], + self.opts["pki_dir"], + ) + salt.crypt.gen_keys( + self.opts["pki_dir"], + self.opts["master_sign_key_name"], + keysize or self.opts["keysize"], + self.opts.get("user"), + self.passphrase, ) - salt.crypt.gen_keys(self.opts['pki_dir'], - self.opts['master_sign_key_name'], - keysize or self.opts['keysize'], - self.opts.get('user'), - self.passphrase) - priv = self.opts['pki_dir'] + '/' + self.opts['master_sign_key_name'] + '.pem' + priv = ( + self.opts["pki_dir"] + + "/" + + self.opts["master_sign_key_name"] + + ".pem" + ) else: - return 'No usable private-key found' + return "No usable private-key found" if not pub: - return 'No usable public-key found' + return "No usable public-key found" - log.debug('Using public-key %s', pub) - log.debug('Using private-key %s', priv) + log.debug("Using public-key %s", pub) + log.debug("Using private-key %s", priv) if signature_path: if not os.path.isdir(signature_path): - log.debug('target directory %s does not exist', signature_path) + log.debug("target directory %s does not exist", signature_path) else: - signature_path = self.opts['pki_dir'] + signature_path = self.opts["pki_dir"] - sign_path = signature_path + '/' + self.opts['master_pubkey_signature'] + sign_path = signature_path + "/" + self.opts["master_pubkey_signature"] skey = get_key(self.opts) return skey.gen_signature(priv, pub, sign_path) def check_minion_cache(self, preserve_minions=None): - ''' + """ Check the minion cache to make sure that old minion data is cleared Optionally, pass in a list of minions which should have their caches preserved. To preserve all caches, set __opts__['preserve_minion_cache'] - ''' + """ if preserve_minions is None: preserve_minions = [] keys = self.list_keys() minions = [] for key, val in six.iteritems(keys): minions.extend(val) - if not self.opts.get('preserve_minion_cache', False): - m_cache = os.path.join(self.opts['cachedir'], self.ACC) + if not self.opts.get("preserve_minion_cache", False): + m_cache = os.path.join(self.opts["cachedir"], self.ACC) if os.path.isdir(m_cache): for minion in os.listdir(m_cache): if minion not in minions and minion not in preserve_minions: try: shutil.rmtree(os.path.join(m_cache, minion)) except (OSError, IOError) as ex: - log.warning('Key: Delete cache for %s got OSError/IOError: %s \n', - minion, - ex) + log.warning( + "Key: Delete cache for %s got OSError/IOError: %s \n", + minion, + ex, + ) continue cache = salt.cache.factory(self.opts) clist = cache.list(self.ACC) if clist: for minion in clist: if minion not in minions and minion not in preserve_minions: - cache.flush('{0}/{1}'.format(self.ACC, minion)) + cache.flush("{0}/{1}".format(self.ACC, minion)) def check_master(self): - ''' + """ Log if the master is not running :rtype: bool :return: Whether or not the master is running - ''' - if not os.path.exists( - os.path.join( - self.opts['sock_dir'], - 'publish_pull.ipc' - ) - ): + """ + if not os.path.exists(os.path.join(self.opts["sock_dir"], "publish_pull.ipc")): return False return True def name_match(self, match, full=False): - ''' + """ Accept a glob which to match the of a key and return the key's location - ''' + """ if full: matches = self.all_keys() else: matches = self.list_keys() ret = {} - if ',' in match and isinstance(match, six.string_types): - match = match.split(',') + if "," in match and isinstance(match, six.string_types): + match = match.split(",") for status, keys in six.iteritems(matches): for key in salt.utils.data.sorted_ignorecase(keys): if isinstance(match, list): @@ -490,10 +515,10 @@ class Key(object): return ret def dict_match(self, match_dict): - ''' + """ Accept a dictionary of keys and return the current state of the specified keys - ''' + """ ret = {} cur_keys = self.list_keys() for status, keys in six.iteritems(match_dict): @@ -504,21 +529,21 @@ class Key(object): return ret def local_keys(self): - ''' + """ Return a dict of local keys - ''' - ret = {'local': []} - for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(self.opts['pki_dir'])): - if fn_.endswith('.pub') or fn_.endswith('.pem'): - path = os.path.join(self.opts['pki_dir'], fn_) + """ + ret = {"local": []} + for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(self.opts["pki_dir"])): + if fn_.endswith(".pub") or fn_.endswith(".pem"): + path = os.path.join(self.opts["pki_dir"], fn_) if os.path.isfile(path): - ret['local'].append(fn_) + ret["local"].append(fn_) return ret def list_keys(self): - ''' + """ Return a dict of managed keys and what the key status are - ''' + """ key_dirs = self._check_minions_directories() ret = {} @@ -529,7 +554,7 @@ class Key(object): ret[os.path.basename(dir_)] = [] try: for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(dir_)): - if not fn_.startswith('.'): + if not fn_.startswith("."): if os.path.isfile(os.path.join(dir_, fn_)): ret[os.path.basename(dir_)].append( salt.utils.stringutils.to_unicode(fn_) @@ -540,80 +565,80 @@ class Key(object): return ret def all_keys(self): - ''' + """ Merge managed keys with local keys - ''' + """ keys = self.list_keys() keys.update(self.local_keys()) return keys def list_status(self, match): - ''' + """ Return a dict of managed keys under a named status - ''' + """ acc, pre, rej, den = self._check_minions_directories() ret = {} - if match.startswith('acc'): + if match.startswith("acc"): ret[os.path.basename(acc)] = [] for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(acc)): - if not fn_.startswith('.'): + if not fn_.startswith("."): if os.path.isfile(os.path.join(acc, fn_)): ret[os.path.basename(acc)].append(fn_) - elif match.startswith('pre') or match.startswith('un'): + elif match.startswith("pre") or match.startswith("un"): ret[os.path.basename(pre)] = [] for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(pre)): - if not fn_.startswith('.'): + if not fn_.startswith("."): if os.path.isfile(os.path.join(pre, fn_)): ret[os.path.basename(pre)].append(fn_) - elif match.startswith('rej'): + elif match.startswith("rej"): ret[os.path.basename(rej)] = [] for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(rej)): - if not fn_.startswith('.'): + if not fn_.startswith("."): if os.path.isfile(os.path.join(rej, fn_)): ret[os.path.basename(rej)].append(fn_) - elif match.startswith('den') and den is not None: + elif match.startswith("den") and den is not None: ret[os.path.basename(den)] = [] for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(den)): - if not fn_.startswith('.'): + if not fn_.startswith("."): if os.path.isfile(os.path.join(den, fn_)): ret[os.path.basename(den)].append(fn_) - elif match.startswith('all'): + elif match.startswith("all"): return self.all_keys() return ret def key_str(self, match): - ''' + """ Return the specified public key or keys based on a glob - ''' + """ ret = {} for status, keys in six.iteritems(self.name_match(match)): ret[status] = {} for key in salt.utils.data.sorted_ignorecase(keys): - path = os.path.join(self.opts['pki_dir'], status, key) - with salt.utils.files.fopen(path, 'r') as fp_: - ret[status][key] = \ - salt.utils.stringutils.to_unicode(fp_.read()) + path = os.path.join(self.opts["pki_dir"], status, key) + with salt.utils.files.fopen(path, "r") as fp_: + ret[status][key] = salt.utils.stringutils.to_unicode(fp_.read()) return ret def key_str_all(self): - ''' + """ Return all managed key strings - ''' + """ ret = {} for status, keys in six.iteritems(self.list_keys()): ret[status] = {} for key in salt.utils.data.sorted_ignorecase(keys): - path = os.path.join(self.opts['pki_dir'], status, key) - with salt.utils.files.fopen(path, 'r') as fp_: - ret[status][key] = \ - salt.utils.stringutils.to_unicode(fp_.read()) + path = os.path.join(self.opts["pki_dir"], status, key) + with salt.utils.files.fopen(path, "r") as fp_: + ret[status][key] = salt.utils.stringutils.to_unicode(fp_.read()) return ret - def accept(self, match=None, match_dict=None, include_rejected=False, include_denied=False): - ''' + def accept( + self, match=None, match_dict=None, include_rejected=False, include_denied=False + ): + """ Accept public keys. If "match" is passed, it is evaluated as a glob. Pre-gathered matches can also be passed via "match_dict". - ''' + """ if match is not None: matches = self.name_match(match) elif match_dict is not None and isinstance(match_dict, dict): @@ -629,64 +654,41 @@ class Key(object): for key in matches.get(keydir, []): try: shutil.move( - os.path.join( - self.opts['pki_dir'], - keydir, - key), - os.path.join( - self.opts['pki_dir'], - self.ACC, - key) - ) - eload = {'result': True, - 'act': 'accept', - 'id': key} - self.event.fire_event(eload, - salt.utils.event.tagify(prefix='key')) + os.path.join(self.opts["pki_dir"], keydir, key), + os.path.join(self.opts["pki_dir"], self.ACC, key), + ) + eload = {"result": True, "act": "accept", "id": key} + self.event.fire_event(eload, salt.utils.event.tagify(prefix="key")) except (IOError, OSError): pass - return ( - self.name_match(match) if match is not None - else self.dict_match(matches) - ) + return self.name_match(match) if match is not None else self.dict_match(matches) def accept_all(self): - ''' + """ Accept all keys in pre - ''' + """ keys = self.list_keys() for key in keys[self.PEND]: try: shutil.move( - os.path.join( - self.opts['pki_dir'], - self.PEND, - key), - os.path.join( - self.opts['pki_dir'], - self.ACC, - key) - ) - eload = {'result': True, - 'act': 'accept', - 'id': key} - self.event.fire_event(eload, - salt.utils.event.tagify(prefix='key')) + os.path.join(self.opts["pki_dir"], self.PEND, key), + os.path.join(self.opts["pki_dir"], self.ACC, key), + ) + eload = {"result": True, "act": "accept", "id": key} + self.event.fire_event(eload, salt.utils.event.tagify(prefix="key")) except (IOError, OSError): pass return self.list_keys() - def delete_key(self, - match=None, - match_dict=None, - preserve_minions=None, - revoke_auth=False): - ''' + def delete_key( + self, match=None, match_dict=None, preserve_minions=None, revoke_auth=False + ): + """ Delete public keys. If "match" is passed, it is evaluated as a glob. Pre-gathered matches can also be passed via "match_dict". To preserve the master caches of minions who are matched, set preserve_minions - ''' + """ if match is not None: matches = self.name_match(match) elif match_dict is not None and isinstance(match_dict, dict): @@ -697,81 +699,75 @@ class Key(object): for key in keys: try: if revoke_auth: - if self.opts.get('rotate_aes_key') is False: - print('Immediate auth revocation specified but AES key rotation not allowed. ' - 'Minion will not be disconnected until the master AES key is rotated.') + if self.opts.get("rotate_aes_key") is False: + print( + "Immediate auth revocation specified but AES key rotation not allowed. " + "Minion will not be disconnected until the master AES key is rotated." + ) else: try: client = salt.client.get_local_client(mopts=self.opts) - client.cmd_async(key, 'saltutil.revoke_auth') + client.cmd_async(key, "saltutil.revoke_auth") except salt.exceptions.SaltClientError: - print('Cannot contact Salt master. ' - 'Connection for {0} will remain up until ' - 'master AES key is rotated or auth is revoked ' - 'with \'saltutil.revoke_auth\'.'.format(key)) - os.remove(os.path.join(self.opts['pki_dir'], status, key)) - eload = {'result': True, - 'act': 'delete', - 'id': key} - self.event.fire_event(eload, - salt.utils.event.tagify(prefix='key')) + print( + "Cannot contact Salt master. " + "Connection for {0} will remain up until " + "master AES key is rotated or auth is revoked " + "with 'saltutil.revoke_auth'.".format(key) + ) + os.remove(os.path.join(self.opts["pki_dir"], status, key)) + eload = {"result": True, "act": "delete", "id": key} + self.event.fire_event(eload, salt.utils.event.tagify(prefix="key")) except (OSError, IOError): pass - if self.opts.get('preserve_minions') is True: - self.check_minion_cache(preserve_minions=matches.get('minions', [])) + if self.opts.get("preserve_minions") is True: + self.check_minion_cache(preserve_minions=matches.get("minions", [])) else: self.check_minion_cache() - if self.opts.get('rotate_aes_key'): - salt.crypt.dropfile(self.opts['cachedir'], self.opts['user']) - return ( - self.name_match(match) if match is not None - else self.dict_match(matches) - ) + if self.opts.get("rotate_aes_key"): + salt.crypt.dropfile(self.opts["cachedir"], self.opts["user"]) + return self.name_match(match) if match is not None else self.dict_match(matches) def delete_den(self): - ''' + """ Delete all denied keys - ''' + """ keys = self.list_keys() for status, keys in six.iteritems(self.list_keys()): for key in keys[self.DEN]: try: - os.remove(os.path.join(self.opts['pki_dir'], status, key)) - eload = {'result': True, - 'act': 'delete', - 'id': key} - self.event.fire_event(eload, - salt.utils.event.tagify(prefix='key')) + os.remove(os.path.join(self.opts["pki_dir"], status, key)) + eload = {"result": True, "act": "delete", "id": key} + self.event.fire_event(eload, salt.utils.event.tagify(prefix="key")) except (OSError, IOError): pass self.check_minion_cache() return self.list_keys() def delete_all(self): - ''' + """ Delete all keys - ''' + """ for status, keys in six.iteritems(self.list_keys()): for key in keys: try: - os.remove(os.path.join(self.opts['pki_dir'], status, key)) - eload = {'result': True, - 'act': 'delete', - 'id': key} - self.event.fire_event(eload, - salt.utils.event.tagify(prefix='key')) + os.remove(os.path.join(self.opts["pki_dir"], status, key)) + eload = {"result": True, "act": "delete", "id": key} + self.event.fire_event(eload, salt.utils.event.tagify(prefix="key")) except (OSError, IOError): pass self.check_minion_cache() - if self.opts.get('rotate_aes_key'): - salt.crypt.dropfile(self.opts['cachedir'], self.opts['user']) + if self.opts.get("rotate_aes_key"): + salt.crypt.dropfile(self.opts["cachedir"], self.opts["user"]) return self.list_keys() - def reject(self, match=None, match_dict=None, include_accepted=False, include_denied=False): - ''' + def reject( + self, match=None, match_dict=None, include_accepted=False, include_denied=False + ): + """ Reject public keys. If "match" is passed, it is evaluated as a glob. Pre-gathered matches can also be passed via "match_dict". - ''' + """ if match is not None: matches = self.name_match(match) elif match_dict is not None and isinstance(match_dict, dict): @@ -787,92 +783,71 @@ class Key(object): for key in matches.get(keydir, []): try: shutil.move( - os.path.join( - self.opts['pki_dir'], - keydir, - key), - os.path.join( - self.opts['pki_dir'], - self.REJ, - key) - ) - eload = {'result': True, - 'act': 'reject', - 'id': key} - self.event.fire_event(eload, - salt.utils.event.tagify(prefix='key')) + os.path.join(self.opts["pki_dir"], keydir, key), + os.path.join(self.opts["pki_dir"], self.REJ, key), + ) + eload = {"result": True, "act": "reject", "id": key} + self.event.fire_event(eload, salt.utils.event.tagify(prefix="key")) except (IOError, OSError): pass self.check_minion_cache() - if self.opts.get('rotate_aes_key'): - salt.crypt.dropfile(self.opts['cachedir'], self.opts['user']) - return ( - self.name_match(match) if match is not None - else self.dict_match(matches) - ) + if self.opts.get("rotate_aes_key"): + salt.crypt.dropfile(self.opts["cachedir"], self.opts["user"]) + return self.name_match(match) if match is not None else self.dict_match(matches) def reject_all(self): - ''' + """ Reject all keys in pre - ''' + """ keys = self.list_keys() for key in keys[self.PEND]: try: shutil.move( - os.path.join( - self.opts['pki_dir'], - self.PEND, - key), - os.path.join( - self.opts['pki_dir'], - self.REJ, - key) - ) - eload = {'result': True, - 'act': 'reject', - 'id': key} - self.event.fire_event(eload, - salt.utils.event.tagify(prefix='key')) + os.path.join(self.opts["pki_dir"], self.PEND, key), + os.path.join(self.opts["pki_dir"], self.REJ, key), + ) + eload = {"result": True, "act": "reject", "id": key} + self.event.fire_event(eload, salt.utils.event.tagify(prefix="key")) except (IOError, OSError): pass self.check_minion_cache() - if self.opts.get('rotate_aes_key'): - salt.crypt.dropfile(self.opts['cachedir'], self.opts['user']) + if self.opts.get("rotate_aes_key"): + salt.crypt.dropfile(self.opts["cachedir"], self.opts["user"]) return self.list_keys() def finger(self, match, hash_type=None): - ''' + """ Return the fingerprint for a specified key - ''' + """ if hash_type is None: - hash_type = __opts__['hash_type'] + hash_type = __opts__["hash_type"] matches = self.name_match(match, True) ret = {} for status, keys in six.iteritems(matches): ret[status] = {} for key in keys: - if status == 'local': - path = os.path.join(self.opts['pki_dir'], key) + if status == "local": + path = os.path.join(self.opts["pki_dir"], key) else: - path = os.path.join(self.opts['pki_dir'], status, key) + path = os.path.join(self.opts["pki_dir"], status, key) ret[status][key] = salt.utils.crypt.pem_finger(path, sum_type=hash_type) return ret def finger_all(self, hash_type=None): - ''' + """ Return fingerprints for all keys - ''' + """ if hash_type is None: - hash_type = __opts__['hash_type'] + hash_type = __opts__["hash_type"] ret = {} for status, keys in six.iteritems(self.all_keys()): ret[status] = {} for key in keys: - if status == 'local': - path = os.path.join(self.opts['pki_dir'], key) + if status == "local": + path = os.path.join(self.opts["pki_dir"], key) else: - path = os.path.join(self.opts['pki_dir'], status, key) + path = os.path.join(self.opts["pki_dir"], status, key) ret[status][key] = salt.utils.crypt.pem_finger(path, sum_type=hash_type) return ret diff --git a/salt/loader.py b/salt/loader.py index 428fb338c96..939edca45dd 100644 --- a/salt/loader.py +++ b/salt/loader.py @@ -1,21 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" The Salt loader is the core to Salt's plugin system, the loader scans directories for python loadable code and organizes the code into the plugin interfaces used by Salt. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import functools +import inspect +import logging import os import re import sys -import time -import logging -import inspect import tempfile -import functools import threading +import time import traceback import types from zipimport import zipimporter @@ -33,31 +34,37 @@ import salt.utils.files import salt.utils.lazy import salt.utils.odict import salt.utils.platform -import salt.utils.versions import salt.utils.stringutils +import salt.utils.versions from salt.exceptions import LoaderError -from salt.template import check_render_pipe_str -from salt.utils.decorators import Depends # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import reload_module +from salt.template import check_render_pipe_str +from salt.utils.decorators import Depends if sys.version_info[:2] >= (3, 5): import importlib.machinery # pylint: disable=no-name-in-module,import-error import importlib.util # pylint: disable=no-name-in-module,import-error + USE_IMPORTLIB = True else: import imp + USE_IMPORTLIB = False try: from collections.abc import MutableMapping except ImportError: + # pylint: disable=no-name-in-module from collections import MutableMapping + # pylint: enable=no-name-in-module + try: import pkg_resources + HAS_PKG_RESOURCES = True except ImportError: HAS_PKG_RESOURCES = False @@ -65,7 +72,7 @@ except ImportError: log = logging.getLogger(__name__) SALT_BASE_PATH = os.path.abspath(salt.syspaths.INSTALL_DIR) -LOADED_BASE_NAME = 'salt.loaded' +LOADED_BASE_NAME = "salt.loaded" if USE_IMPORTLIB: # pylint: disable=no-member @@ -75,22 +82,23 @@ if USE_IMPORTLIB: MODULE_KIND_PKG_DIRECTORY = 5 SUFFIXES = [] for suffix in importlib.machinery.EXTENSION_SUFFIXES: - SUFFIXES.append((suffix, 'rb', MODULE_KIND_EXTENSION)) + SUFFIXES.append((suffix, "rb", MODULE_KIND_EXTENSION)) for suffix in importlib.machinery.SOURCE_SUFFIXES: - SUFFIXES.append((suffix, 'rb', MODULE_KIND_SOURCE)) + SUFFIXES.append((suffix, "rb", MODULE_KIND_SOURCE)) for suffix in importlib.machinery.BYTECODE_SUFFIXES: - SUFFIXES.append((suffix, 'rb', MODULE_KIND_COMPILED)) + SUFFIXES.append((suffix, "rb", MODULE_KIND_COMPILED)) MODULE_KIND_MAP = { MODULE_KIND_SOURCE: importlib.machinery.SourceFileLoader, MODULE_KIND_COMPILED: importlib.machinery.SourcelessFileLoader, - MODULE_KIND_EXTENSION: importlib.machinery.ExtensionFileLoader + MODULE_KIND_EXTENSION: importlib.machinery.ExtensionFileLoader, } # pylint: enable=no-member else: SUFFIXES = imp.get_suffixes() -PY3_PRE_EXT = \ - re.compile(r'\.cpython-{0}{1}(\.opt-[1-9])?'.format(*sys.version_info[:2])) +PY3_PRE_EXT = re.compile( + r"\.cpython-{0}{1}(\.opt-[1-9])?".format(*sys.version_info[:2]) +) # Because on the cloud drivers we do `from salt.cloud.libcloudfuncs import *` # which simplifies code readability, it adds some unsupported functions into @@ -98,9 +106,9 @@ PY3_PRE_EXT = \ # We list un-supported functions here. These will be removed from the loaded. # TODO: remove the need for this cross-module code. Maybe use NotImplemented LIBCLOUD_FUNCS_NOT_SUPPORTED = ( - 'parallels.avail_sizes', - 'parallels.avail_locations', - 'proxmox.avail_sizes', + "parallels.avail_sizes", + "parallels.avail_locations", + "proxmox.avail_sizes", ) # Will be set to pyximport module at runtime if cython is enabled in config. @@ -108,25 +116,19 @@ pyximport = None def static_loader( - opts, - ext_type, - tag, - pack=None, - int_type=None, - ext_dirs=True, - ext_type_dirs=None, - base_path=None, - filter_name=None, - ): + opts, + ext_type, + tag, + pack=None, + int_type=None, + ext_dirs=True, + ext_type_dirs=None, + base_path=None, + filter_name=None, +): funcs = LazyLoader( _module_dirs( - opts, - ext_type, - tag, - int_type, - ext_dirs, - ext_type_dirs, - base_path, + opts, ext_type, tag, int_type, ext_dirs, ext_type_dirs, base_path, ), opts, tag=tag, @@ -142,57 +144,65 @@ def static_loader( def _format_entrypoint_target(ep): - ''' + """ Makes a string describing the target of an EntryPoint object. Base strongly on EntryPoint.__str__(). - ''' + """ s = ep.module_name if ep.attrs: - s += ':' + '.'.join(ep.attrs) + s += ":" + ".".join(ep.attrs) return s def _module_dirs( - opts, - ext_type, - tag=None, - int_type=None, - ext_dirs=True, - ext_type_dirs=None, - base_path=None, - ): + opts, + ext_type, + tag=None, + int_type=None, + ext_dirs=True, + ext_type_dirs=None, + base_path=None, +): if tag is None: tag = ext_type sys_types = os.path.join(base_path or SALT_BASE_PATH, int_type or ext_type) - ext_types = os.path.join(opts['extension_modules'], ext_type) + ext_types = os.path.join(opts["extension_modules"], ext_type) ext_type_types = [] if ext_dirs: if ext_type_dirs is None: - ext_type_dirs = '{0}_dirs'.format(tag) + ext_type_dirs = "{0}_dirs".format(tag) if ext_type_dirs in opts: ext_type_types.extend(opts[ext_type_dirs]) if HAS_PKG_RESOURCES and ext_type_dirs: - for entry_point in pkg_resources.iter_entry_points('salt.loader', ext_type_dirs): + for entry_point in pkg_resources.iter_entry_points( + "salt.loader", ext_type_dirs + ): try: loaded_entry_point = entry_point.load() for path in loaded_entry_point(): ext_type_types.append(path) except Exception as exc: # pylint: disable=broad-except - log.error("Error getting module directories from %s: %s", _format_entrypoint_target(entry_point), exc) - log.debug("Full backtrace for module directories error", exc_info=True) + log.error( + "Error getting module directories from %s: %s", + _format_entrypoint_target(entry_point), + exc, + ) + log.debug( + "Full backtrace for module directories error", exc_info=True + ) cli_module_dirs = [] # The dirs can be any module dir, or a in-tree _{ext_type} dir - for _dir in opts.get('module_dirs', []): + for _dir in opts.get("module_dirs", []): # Prepend to the list to match cli argument ordering maybe_dir = os.path.join(_dir, ext_type) if os.path.isdir(maybe_dir): cli_module_dirs.insert(0, maybe_dir) continue - maybe_dir = os.path.join(_dir, '_{0}'.format(ext_type)) + maybe_dir = os.path.join(_dir, "_{0}".format(ext_type)) if os.path.isdir(maybe_dir): cli_module_dirs.insert(0, maybe_dir) @@ -200,16 +210,17 @@ def _module_dirs( def minion_mods( - opts, - context=None, - utils=None, - whitelist=None, - initial_load=False, - loaded_base_name=None, - notify=False, - static_modules=None, - proxy=None): - ''' + opts, + context=None, + utils=None, + whitelist=None, + initial_load=False, + loaded_base_name=None, + notify=False, + static_modules=None, + proxy=None, +): + """ Load execution modules Returns a dictionary of execution modules appropriate for the current @@ -242,26 +253,26 @@ def minion_mods( __utils__ = salt.loader.utils(__opts__) __salt__ = salt.loader.minion_mods(__opts__, utils=__utils__) __salt__['test.ping']() - ''' + """ # TODO Publish documentation for module whitelisting if not whitelist: - whitelist = opts.get('whitelist_modules', None) + whitelist = opts.get("whitelist_modules", None) ret = LazyLoader( - _module_dirs(opts, 'modules', 'module'), + _module_dirs(opts, "modules", "module"), opts, - tag='module', - pack={'__context__': context, '__utils__': utils, '__proxy__': proxy}, + tag="module", + pack={"__context__": context, "__utils__": utils, "__proxy__": proxy}, whitelist=whitelist, loaded_base_name=loaded_base_name, static_modules=static_modules, ) - ret.pack['__salt__'] = ret + ret.pack["__salt__"] = ret # Load any provider overrides from the configuration file providers option # Note: Providers can be pkg, service, user or group - not to be confused # with cloud providers. - providers = opts.get('providers', False) + providers = opts.get("providers", False) if providers and isinstance(providers, dict): for mod in providers: # sometimes providers opts is not to diverge modules but @@ -273,18 +284,18 @@ def minion_mods( else: if funcs: for func in funcs: - f_key = '{0}{1}'.format(mod, func[func.rindex('.'):]) + f_key = "{0}{1}".format(mod, func[func.rindex(".") :]) ret[f_key] = funcs[func] if notify: - with salt.utils.event.get_event('minion', opts=opts, listen=False) as evt: - evt.fire_event({'complete': True}, tag='/salt/minion/minion_mod_complete') + with salt.utils.event.get_event("minion", opts=opts, listen=False) as evt: + evt.fire_event({"complete": True}, tag="/salt/minion/minion_mod_complete") return ret -def raw_mod(opts, name, functions, mod='modules'): - ''' +def raw_mod(opts, name, functions, mod="modules"): + """ Returns a single module loaded raw and bypassing the __virtual__ function .. code-block:: python @@ -295,13 +306,13 @@ def raw_mod(opts, name, functions, mod='modules'): __opts__ = salt.config.minion_config('/etc/salt/minion') testmod = salt.loader.raw_mod(__opts__, 'test', None) testmod['test.ping']() - ''' + """ loader = LazyLoader( - _module_dirs(opts, mod, 'module'), + _module_dirs(opts, mod, "module"), opts, - tag='rawmodule', + tag="rawmodule", virtual_enable=False, - pack={'__salt__': functions}, + pack={"__salt__": functions}, ) # if we don't have the module, return an empty dict if name not in loader.file_mapping: @@ -312,233 +323,209 @@ def raw_mod(opts, name, functions, mod='modules'): def metaproxy(opts): - ''' + """ Return functions used in the meta proxy - ''' + """ - return LazyLoader( - _module_dirs(opts, 'metaproxy'), - opts, - tag='metaproxy' - ) + return LazyLoader(_module_dirs(opts, "metaproxy"), opts, tag="metaproxy") def matchers(opts): - ''' + """ Return the matcher services plugins - ''' - return LazyLoader( - _module_dirs(opts, 'matchers'), - opts, - tag='matchers' - ) + """ + return LazyLoader(_module_dirs(opts, "matchers"), opts, tag="matchers") def engines(opts, functions, runners, utils, proxy=None): - ''' + """ Return the master services plugins - ''' - pack = {'__salt__': functions, - '__runners__': runners, - '__proxy__': proxy, - '__utils__': utils} - return LazyLoader( - _module_dirs(opts, 'engines'), - opts, - tag='engines', - pack=pack, - ) + """ + pack = { + "__salt__": functions, + "__runners__": runners, + "__proxy__": proxy, + "__utils__": utils, + } + return LazyLoader(_module_dirs(opts, "engines"), opts, tag="engines", pack=pack,) def proxy(opts, functions=None, returners=None, whitelist=None, utils=None): - ''' + """ Returns the proxy module for this salt-proxy-minion - ''' + """ ret = LazyLoader( - _module_dirs(opts, 'proxy'), + _module_dirs(opts, "proxy"), opts, - tag='proxy', - pack={'__salt__': functions, '__ret__': returners, '__utils__': utils}, + tag="proxy", + pack={"__salt__": functions, "__ret__": returners, "__utils__": utils}, ) - ret.pack['__proxy__'] = ret + ret.pack["__proxy__"] = ret return ret def returners(opts, functions, whitelist=None, context=None, proxy=None): - ''' + """ Returns the returner modules - ''' + """ return LazyLoader( - _module_dirs(opts, 'returners', 'returner'), + _module_dirs(opts, "returners", "returner"), opts, - tag='returner', + tag="returner", whitelist=whitelist, - pack={'__salt__': functions, '__context__': context, '__proxy__': proxy or {}}, + pack={"__salt__": functions, "__context__": context, "__proxy__": proxy or {}}, ) def utils(opts, whitelist=None, context=None, proxy=proxy): - ''' + """ Returns the utility modules - ''' + """ return LazyLoader( - _module_dirs(opts, 'utils', ext_type_dirs='utils_dirs'), + _module_dirs(opts, "utils", ext_type_dirs="utils_dirs"), opts, - tag='utils', + tag="utils", whitelist=whitelist, - pack={'__context__': context, '__proxy__': proxy or {}}, + pack={"__context__": context, "__proxy__": proxy or {}}, ) def pillars(opts, functions, context=None): - ''' + """ Returns the pillars modules - ''' - ret = LazyLoader(_module_dirs(opts, 'pillar'), - opts, - tag='pillar', - pack={'__salt__': functions, - '__context__': context, - '__utils__': utils(opts)}) - ret.pack['__ext_pillar__'] = ret - return FilterDictWrapper(ret, '.ext_pillar') + """ + ret = LazyLoader( + _module_dirs(opts, "pillar"), + opts, + tag="pillar", + pack={"__salt__": functions, "__context__": context, "__utils__": utils(opts)}, + ) + ret.pack["__ext_pillar__"] = ret + return FilterDictWrapper(ret, ".ext_pillar") def tops(opts): - ''' + """ Returns the tops modules - ''' - if 'master_tops' not in opts: + """ + if "master_tops" not in opts: return {} - whitelist = list(opts['master_tops'].keys()) + whitelist = list(opts["master_tops"].keys()) ret = LazyLoader( - _module_dirs(opts, 'tops', 'top'), - opts, - tag='top', - whitelist=whitelist, + _module_dirs(opts, "tops", "top"), opts, tag="top", whitelist=whitelist, ) - return FilterDictWrapper(ret, '.top') + return FilterDictWrapper(ret, ".top") def wheels(opts, whitelist=None, context=None): - ''' + """ Returns the wheels modules - ''' + """ if context is None: context = {} return LazyLoader( - _module_dirs(opts, 'wheel'), + _module_dirs(opts, "wheel"), opts, - tag='wheel', + tag="wheel", whitelist=whitelist, - pack={'__context__': context}, + pack={"__context__": context}, ) def outputters(opts): - ''' + """ Returns the outputters modules :param dict opts: The Salt options dictionary :returns: LazyLoader instance, with only outputters present in the keyspace - ''' + """ ret = LazyLoader( - _module_dirs(opts, 'output', ext_type_dirs='outputter_dirs'), + _module_dirs(opts, "output", ext_type_dirs="outputter_dirs"), opts, - tag='output', + tag="output", ) - wrapped_ret = FilterDictWrapper(ret, '.output') + wrapped_ret = FilterDictWrapper(ret, ".output") # TODO: this name seems terrible... __salt__ should always be execution mods - ret.pack['__salt__'] = wrapped_ret + ret.pack["__salt__"] = wrapped_ret return wrapped_ret def serializers(opts): - ''' + """ Returns the serializers modules :param dict opts: The Salt options dictionary :returns: LazyLoader instance, with only serializers present in the keyspace - ''' - return LazyLoader( - _module_dirs(opts, 'serializers'), - opts, - tag='serializers', - ) + """ + return LazyLoader(_module_dirs(opts, "serializers"), opts, tag="serializers",) def eauth_tokens(opts): - ''' + """ Returns the tokens modules :param dict opts: The Salt options dictionary :returns: LazyLoader instance, with only token backends present in the keyspace - ''' - return LazyLoader( - _module_dirs(opts, 'tokens'), - opts, - tag='tokens', - ) + """ + return LazyLoader(_module_dirs(opts, "tokens"), opts, tag="tokens",) def auth(opts, whitelist=None): - ''' + """ Returns the auth modules :param dict opts: The Salt options dictionary :returns: LazyLoader - ''' + """ return LazyLoader( - _module_dirs(opts, 'auth'), + _module_dirs(opts, "auth"), opts, - tag='auth', + tag="auth", whitelist=whitelist, - pack={'__salt__': minion_mods(opts)}, + pack={"__salt__": minion_mods(opts)}, ) def fileserver(opts, backends): - ''' + """ Returns the file server modules - ''' - return LazyLoader(_module_dirs(opts, 'fileserver'), - opts, - tag='fileserver', - whitelist=backends, - pack={'__utils__': utils(opts)}) + """ + return LazyLoader( + _module_dirs(opts, "fileserver"), + opts, + tag="fileserver", + whitelist=backends, + pack={"__utils__": utils(opts)}, + ) def roster(opts, runner=None, utils=None, whitelist=None): - ''' + """ Returns the roster modules - ''' + """ return LazyLoader( - _module_dirs(opts, 'roster'), + _module_dirs(opts, "roster"), opts, - tag='roster', + tag="roster", whitelist=whitelist, - pack={ - '__runner__': runner, - '__utils__': utils, - }, + pack={"__runner__": runner, "__utils__": utils}, ) def thorium(opts, functions, runners): - ''' + """ Load the thorium runtime modules - ''' - pack = {'__salt__': functions, '__runner__': runners, '__context__': {}} - ret = LazyLoader(_module_dirs(opts, 'thorium'), - opts, - tag='thorium', - pack=pack) - ret.pack['__thorium__'] = ret + """ + pack = {"__salt__": functions, "__runner__": runners, "__context__": {}} + ret = LazyLoader(_module_dirs(opts, "thorium"), opts, tag="thorium", pack=pack) + ret.pack["__thorium__"] = ret return ret -def states(opts, functions, utils, serializers, whitelist=None, proxy=None, context=None): - ''' +def states( + opts, functions, utils, serializers, whitelist=None, proxy=None, context=None +): + """ Returns the state modules :param dict opts: The Salt options dictionary @@ -552,118 +539,119 @@ def states(opts, functions, utils, serializers, whitelist=None, proxy=None, cont __opts__ = salt.config.minion_config('/etc/salt/minion') statemods = salt.loader.states(__opts__, None, None) - ''' + """ if context is None: context = {} ret = LazyLoader( - _module_dirs(opts, 'states'), + _module_dirs(opts, "states"), opts, - tag='states', - pack={'__salt__': functions, '__proxy__': proxy or {}}, + tag="states", + pack={"__salt__": functions, "__proxy__": proxy or {}}, whitelist=whitelist, ) - ret.pack['__states__'] = ret - ret.pack['__utils__'] = utils - ret.pack['__serializers__'] = serializers - ret.pack['__context__'] = context + ret.pack["__states__"] = ret + ret.pack["__utils__"] = utils + ret.pack["__serializers__"] = serializers + ret.pack["__context__"] = context return ret def beacons(opts, functions, context=None, proxy=None): - ''' + """ Load the beacon modules :param dict opts: The Salt options dictionary :param dict functions: A dictionary of minion modules, with module names as keys and funcs as values. - ''' + """ return LazyLoader( - _module_dirs(opts, 'beacons'), + _module_dirs(opts, "beacons"), opts, - tag='beacons', - pack={'__context__': context, '__salt__': functions, '__proxy__': proxy or {}}, + tag="beacons", + pack={"__context__": context, "__salt__": functions, "__proxy__": proxy or {}}, virtual_funcs=[], ) def log_handlers(opts): - ''' + """ Returns the custom logging handler modules :param dict opts: The Salt options dictionary - ''' + """ ret = LazyLoader( _module_dirs( opts, - 'log_handlers', - int_type='handlers', - base_path=os.path.join(SALT_BASE_PATH, 'log'), + "log_handlers", + int_type="handlers", + base_path=os.path.join(SALT_BASE_PATH, "log"), ), opts, - tag='log_handlers', + tag="log_handlers", ) - return FilterDictWrapper(ret, '.setup_handlers') + return FilterDictWrapper(ret, ".setup_handlers") def ssh_wrapper(opts, functions=None, context=None): - ''' + """ Returns the custom logging handler modules - ''' + """ return LazyLoader( _module_dirs( opts, - 'wrapper', - base_path=os.path.join(SALT_BASE_PATH, os.path.join('client', 'ssh')), + "wrapper", + base_path=os.path.join(SALT_BASE_PATH, os.path.join("client", "ssh")), ), opts, - tag='wrapper', + tag="wrapper", pack={ - '__salt__': functions, - '__grains__': opts.get('grains', {}), - '__pillar__': opts.get('pillar', {}), - '__context__': context, - }, + "__salt__": functions, + "__grains__": opts.get("grains", {}), + "__pillar__": opts.get("pillar", {}), + "__context__": context, + }, ) def render(opts, functions, states=None, proxy=None, context=None): - ''' + """ Returns the render modules - ''' + """ if context is None: context = {} - pack = {'__salt__': functions, - '__grains__': opts.get('grains', {}), - '__context__': context} + pack = { + "__salt__": functions, + "__grains__": opts.get("grains", {}), + "__context__": context, + } if states: - pack['__states__'] = states - pack['__proxy__'] = proxy or {} + pack["__states__"] = states + pack["__proxy__"] = proxy or {} ret = LazyLoader( - _module_dirs( - opts, - 'renderers', - 'render', - ext_type_dirs='render_dirs', - ), + _module_dirs(opts, "renderers", "render", ext_type_dirs="render_dirs",), opts, - tag='render', + tag="render", pack=pack, ) - rend = FilterDictWrapper(ret, '.render') + rend = FilterDictWrapper(ret, ".render") - if not check_render_pipe_str(opts['renderer'], rend, opts['renderer_blacklist'], opts['renderer_whitelist']): - err = ('The renderer {0} is unavailable, this error is often because ' - 'the needed software is unavailable'.format(opts['renderer'])) + if not check_render_pipe_str( + opts["renderer"], rend, opts["renderer_blacklist"], opts["renderer_whitelist"] + ): + err = ( + "The renderer {0} is unavailable, this error is often because " + "the needed software is unavailable".format(opts["renderer"]) + ) log.critical(err) raise LoaderError(err) return rend def grain_funcs(opts, proxy=None): - ''' + """ Returns the grain functions .. code-block:: python @@ -673,18 +661,13 @@ def grain_funcs(opts, proxy=None): __opts__ = salt.config.minion_config('/etc/salt/minion') grainfuncs = salt.loader.grain_funcs(__opts__) - ''' + """ ret = LazyLoader( - _module_dirs( - opts, - 'grains', - 'grain', - ext_type_dirs='grains_dirs', - ), + _module_dirs(opts, "grains", "grain", ext_type_dirs="grains_dirs",), opts, - tag='grains', + tag="grains", ) - ret.pack['__utils__'] = utils(opts, proxy=proxy) + ret.pack["__utils__"] = utils(opts, proxy=proxy) return ret @@ -692,43 +675,46 @@ def _format_cached_grains(cached_grains): """ Returns cached grains with fixed types, like tuples. """ - if cached_grains.get('osrelease_info'): - osrelease_info = cached_grains['osrelease_info'] + if cached_grains.get("osrelease_info"): + osrelease_info = cached_grains["osrelease_info"] if isinstance(osrelease_info, list): - cached_grains['osrelease_info'] = tuple(osrelease_info) + cached_grains["osrelease_info"] = tuple(osrelease_info) return cached_grains def _load_cached_grains(opts, cfn): - ''' + """ Returns the grains cached in cfn, or None if the cache is too old or is corrupted. - ''' + """ if not os.path.isfile(cfn): - log.debug('Grains cache file does not exist.') + log.debug("Grains cache file does not exist.") return None grains_cache_age = int(time.time() - os.path.getmtime(cfn)) - if grains_cache_age > opts.get('grains_cache_expiration', 300): + if grains_cache_age > opts.get("grains_cache_expiration", 300): log.debug( - 'Grains cache last modified %s seconds ago and cache ' - 'expiration is set to %s. Grains cache expired. ' - 'Refreshing.', - grains_cache_age, opts.get('grains_cache_expiration', 300) + "Grains cache last modified %s seconds ago and cache " + "expiration is set to %s. Grains cache expired. " + "Refreshing.", + grains_cache_age, + opts.get("grains_cache_expiration", 300), ) return None - if opts.get('refresh_grains_cache', False): - log.debug('refresh_grains_cache requested, Refreshing.') + if opts.get("refresh_grains_cache", False): + log.debug("refresh_grains_cache requested, Refreshing.") return None - log.debug('Retrieving grains from cache') + log.debug("Retrieving grains from cache") try: serial = salt.payload.Serial(opts) - with salt.utils.files.fopen(cfn, 'rb') as fp_: - cached_grains = salt.utils.data.decode(serial.load(fp_), preserve_tuples=True) + with salt.utils.files.fopen(cfn, "rb") as fp_: + cached_grains = salt.utils.data.decode( + serial.load(fp_), preserve_tuples=True + ) if not cached_grains: - log.debug('Cached grains are empty, cache might be corrupted. Refreshing.') + log.debug("Cached grains are empty, cache might be corrupted. Refreshing.") return None return _format_cached_grains(cached_grains) @@ -737,7 +723,7 @@ def _load_cached_grains(opts, cfn): def grains(opts, force_refresh=False, proxy=None): - ''' + """ Return the functions for the dynamic grains and the values for the static grains. @@ -754,57 +740,58 @@ def grains(opts, force_refresh=False, proxy=None): __opts__ = salt.config.minion_config('/etc/salt/minion') __grains__ = salt.loader.grains(__opts__) print __grains__['id'] - ''' + """ # Need to re-import salt.config, somehow it got lost when a minion is starting import salt.config + # if we have no grains, lets try loading from disk (TODO: move to decorator?) - cfn = os.path.join( - opts['cachedir'], - 'grains.cache.p' - ) - if not force_refresh and opts.get('grains_cache', False): + cfn = os.path.join(opts["cachedir"], "grains.cache.p") + if not force_refresh and opts.get("grains_cache", False): cached_grains = _load_cached_grains(opts, cfn) if cached_grains: return cached_grains else: - log.debug('Grains refresh requested. Refreshing grains.') + log.debug("Grains refresh requested. Refreshing grains.") - if opts.get('skip_grains', False): + if opts.get("skip_grains", False): return {} - grains_deep_merge = opts.get('grains_deep_merge', False) is True - if 'conf_file' in opts: + grains_deep_merge = opts.get("grains_deep_merge", False) is True + if "conf_file" in opts: pre_opts = {} - pre_opts.update(salt.config.load_config( - opts['conf_file'], 'SALT_MINION_CONFIG', - salt.config.DEFAULT_MINION_OPTS['conf_file'] - )) - default_include = pre_opts.get( - 'default_include', opts['default_include'] + pre_opts.update( + salt.config.load_config( + opts["conf_file"], + "SALT_MINION_CONFIG", + salt.config.DEFAULT_MINION_OPTS["conf_file"], + ) ) - include = pre_opts.get('include', []) - pre_opts.update(salt.config.include_config( - default_include, opts['conf_file'], verbose=False - )) - pre_opts.update(salt.config.include_config( - include, opts['conf_file'], verbose=True - )) - if 'grains' in pre_opts: - opts['grains'] = pre_opts['grains'] + default_include = pre_opts.get("default_include", opts["default_include"]) + include = pre_opts.get("include", []) + pre_opts.update( + salt.config.include_config( + default_include, opts["conf_file"], verbose=False + ) + ) + pre_opts.update( + salt.config.include_config(include, opts["conf_file"], verbose=True) + ) + if "grains" in pre_opts: + opts["grains"] = pre_opts["grains"] else: - opts['grains'] = {} + opts["grains"] = {} else: - opts['grains'] = {} + opts["grains"] = {} grains_data = {} - blist = opts.get('grains_blacklist', []) + blist = opts.get("grains_blacklist", []) funcs = grain_funcs(opts, proxy=proxy) if force_refresh: # if we refresh, lets reload grain modules funcs.clear() # Run core grains for key in funcs: - if not key.startswith('core.'): + if not key.startswith("core."): continue - log.trace('Loading %s grain', key) + log.trace("Loading %s grain", key) ret = funcs[key]() if not isinstance(ret, dict): continue @@ -813,7 +800,7 @@ def grains(opts, force_refresh=False, proxy=None): for block in blist: if salt.utils.stringutils.expr_match(key, block): del ret[key] - log.trace('Filtering %s grain', key) + log.trace("Filtering %s grain", key) if not ret: continue if grains_deep_merge: @@ -823,7 +810,7 @@ def grains(opts, force_refresh=False, proxy=None): # Run the rest of the grains for key in funcs: - if key.startswith('core.') or key == '_errors': + if key.startswith("core.") or key == "_errors": continue try: # Grains are loaded too early to take advantage of the injected @@ -832,21 +819,25 @@ def grains(opts, force_refresh=False, proxy=None): # one parameter. Then the grains can have access to the # proxymodule for retrieving information from the connected # device. - log.trace('Loading %s grain', key) + log.trace("Loading %s grain", key) parameters = salt.utils.args.get_function_argspec(funcs[key]).args kwargs = {} - if 'proxy' in parameters: - kwargs['proxy'] = proxy - if 'grains' in parameters: - kwargs['grains'] = grains_data + if "proxy" in parameters: + kwargs["proxy"] = proxy + if "grains" in parameters: + kwargs["grains"] = grains_data ret = funcs[key](**kwargs) except Exception: # pylint: disable=broad-except if salt.utils.platform.is_proxy(): - log.info('The following CRITICAL message may not be an error; the proxy may not be completely established yet.') + log.info( + "The following CRITICAL message may not be an error; the proxy may not be completely established yet." + ) log.critical( - 'Failed to load grains defined in grain file %s in ' - 'function %s, error:\n', key, funcs[key], - exc_info=True + "Failed to load grains defined in grain file %s in " + "function %s, error:\n", + key, + funcs[key], + exc_info=True, ) continue if not isinstance(ret, dict): @@ -856,7 +847,7 @@ def grains(opts, force_refresh=False, proxy=None): for block in blist: if salt.utils.stringutils.expr_match(key, block): del ret[key] - log.trace('Filtering %s grain', key) + log.trace("Filtering %s grain", key) if not ret: continue if grains_deep_merge: @@ -864,44 +855,48 @@ def grains(opts, force_refresh=False, proxy=None): else: grains_data.update(ret) - if opts.get('proxy_merge_grains_in_module', True) and proxy: + if opts.get("proxy_merge_grains_in_module", True) and proxy: try: - proxytype = proxy.opts['proxy']['proxytype'] - if proxytype + '.grains' in proxy: - if proxytype + '.initialized' in proxy and proxy[proxytype + '.initialized'](): + proxytype = proxy.opts["proxy"]["proxytype"] + if proxytype + ".grains" in proxy: + if ( + proxytype + ".initialized" in proxy + and proxy[proxytype + ".initialized"]() + ): try: - proxytype = proxy.opts['proxy']['proxytype'] - ret = proxy[proxytype + '.grains']() + proxytype = proxy.opts["proxy"]["proxytype"] + ret = proxy[proxytype + ".grains"]() if grains_deep_merge: salt.utils.dictupdate.update(grains_data, ret) else: grains_data.update(ret) except Exception: # pylint: disable=broad-except - log.critical('Failed to run proxy\'s grains function!', - exc_info=True + log.critical( + "Failed to run proxy's grains function!", exc_info=True ) except KeyError: pass - grains_data.update(opts['grains']) + grains_data.update(opts["grains"]) # Write cache if enabled - if opts.get('grains_cache', False): + if opts.get("grains_cache", False): with salt.utils.files.set_umask(0o077): try: if salt.utils.platform.is_windows(): # Late import import salt.modules.cmdmod + # Make sure cache file isn't read-only salt.modules.cmdmod._run_quiet('attrib -R "{0}"'.format(cfn)) - with salt.utils.files.fopen(cfn, 'w+b') as fp_: + with salt.utils.files.fopen(cfn, "w+b") as fp_: try: serial = salt.payload.Serial(opts) serial.dump(grains_data, fp_) except TypeError as e: - log.error('Failed to serialize grains cache: %s', e) + log.error("Failed to serialize grains cache: %s", e) raise # re-throw for cleanup except Exception as e: # pylint: disable=broad-except - log.error('Unable to write to grains cache file %s: %s', cfn, e) + log.error("Unable to write to grains cache file %s: %s", cfn, e) # Based on the original exception, the file may or may not have been # created. If it was, we will remove it now, as the exception means # the serialized data is not to be trusted, no matter what the @@ -910,176 +905,170 @@ def grains(opts, force_refresh=False, proxy=None): os.unlink(cfn) if grains_deep_merge: - salt.utils.dictupdate.update(grains_data, opts['grains']) + salt.utils.dictupdate.update(grains_data, opts["grains"]) else: - grains_data.update(opts['grains']) + grains_data.update(opts["grains"]) return salt.utils.data.decode(grains_data, preserve_tuples=True) # TODO: get rid of? Does anyone use this? You should use raw() instead def call(fun, **kwargs): - ''' + """ Directly call a function inside a loader directory - ''' - args = kwargs.get('args', []) - dirs = kwargs.get('dirs', []) + """ + args = kwargs.get("args", []) + dirs = kwargs.get("dirs", []) funcs = LazyLoader( - [os.path.join(SALT_BASE_PATH, 'modules')] + dirs, + [os.path.join(SALT_BASE_PATH, "modules")] + dirs, None, - tag='modules', + tag="modules", virtual_enable=False, ) return funcs[fun](*args) def runner(opts, utils=None, context=None, whitelist=None): - ''' + """ Directly call a function inside a loader directory - ''' + """ if utils is None: utils = {} if context is None: context = {} ret = LazyLoader( - _module_dirs(opts, 'runners', 'runner', ext_type_dirs='runner_dirs'), + _module_dirs(opts, "runners", "runner", ext_type_dirs="runner_dirs"), opts, - tag='runners', - pack={'__utils__': utils, '__context__': context}, + tag="runners", + pack={"__utils__": utils, "__context__": context}, whitelist=whitelist, ) # TODO: change from __salt__ to something else, we overload __salt__ too much - ret.pack['__salt__'] = ret + ret.pack["__salt__"] = ret return ret def queues(opts): - ''' + """ Directly call a function inside a loader directory - ''' + """ return LazyLoader( - _module_dirs(opts, 'queues', 'queue', ext_type_dirs='queue_dirs'), + _module_dirs(opts, "queues", "queue", ext_type_dirs="queue_dirs"), opts, - tag='queues', + tag="queues", ) def sdb(opts, functions=None, whitelist=None, utils=None): - ''' + """ Make a very small database call - ''' + """ if utils is None: utils = {} return LazyLoader( - _module_dirs(opts, 'sdb'), + _module_dirs(opts, "sdb"), opts, - tag='sdb', + tag="sdb", pack={ - '__sdb__': functions, - '__opts__': opts, - '__utils__': utils, - '__salt__': minion_mods(opts, utils=utils), + "__sdb__": functions, + "__opts__": opts, + "__utils__": utils, + "__salt__": minion_mods(opts, utils=utils), }, whitelist=whitelist, ) def pkgdb(opts): - ''' + """ Return modules for SPM's package database .. versionadded:: 2015.8.0 - ''' + """ return LazyLoader( - _module_dirs( - opts, - 'pkgdb', - base_path=os.path.join(SALT_BASE_PATH, 'spm') - ), + _module_dirs(opts, "pkgdb", base_path=os.path.join(SALT_BASE_PATH, "spm")), opts, - tag='pkgdb' + tag="pkgdb", ) def pkgfiles(opts): - ''' + """ Return modules for SPM's file handling .. versionadded:: 2015.8.0 - ''' + """ return LazyLoader( - _module_dirs( - opts, - 'pkgfiles', - base_path=os.path.join(SALT_BASE_PATH, 'spm') - ), + _module_dirs(opts, "pkgfiles", base_path=os.path.join(SALT_BASE_PATH, "spm")), opts, - tag='pkgfiles' + tag="pkgfiles", ) def clouds(opts): - ''' + """ Return the cloud functions - ''' + """ # Let's bring __active_provider_name__, defaulting to None, to all cloud # drivers. This will get temporarily updated/overridden with a context # manager when needed. functions = LazyLoader( - _module_dirs(opts, - 'clouds', - 'cloud', - base_path=os.path.join(SALT_BASE_PATH, 'cloud'), - int_type='clouds'), + _module_dirs( + opts, + "clouds", + "cloud", + base_path=os.path.join(SALT_BASE_PATH, "cloud"), + int_type="clouds", + ), opts, - tag='clouds', - pack={'__utils__': salt.loader.utils(opts), - '__active_provider_name__': None}, + tag="clouds", + pack={"__utils__": salt.loader.utils(opts), "__active_provider_name__": None}, ) for funcname in LIBCLOUD_FUNCS_NOT_SUPPORTED: log.trace( - '\'%s\' has been marked as not supported. Removing from the ' - 'list of supported cloud functions', funcname + "'%s' has been marked as not supported. Removing from the " + "list of supported cloud functions", + funcname, ) functions.pop(funcname, None) return functions def netapi(opts): - ''' + """ Return the network api functions - ''' - return LazyLoader( - _module_dirs(opts, 'netapi'), - opts, - tag='netapi', - ) + """ + return LazyLoader(_module_dirs(opts, "netapi"), opts, tag="netapi",) def executors(opts, functions=None, context=None, proxy=None): - ''' + """ Returns the executor modules - ''' + """ executors = LazyLoader( - _module_dirs(opts, 'executors', 'executor'), + _module_dirs(opts, "executors", "executor"), opts, - tag='executor', - pack={'__salt__': functions, '__context__': context or {}, '__proxy__': proxy or {}}, + tag="executor", + pack={ + "__salt__": functions, + "__context__": context or {}, + "__proxy__": proxy or {}, + }, ) - executors.pack['__executors__'] = executors + executors.pack["__executors__"] = executors return executors def cache(opts, serial): - ''' + """ Returns the returner modules - ''' + """ return LazyLoader( - _module_dirs(opts, 'cache', 'cache'), + _module_dirs(opts, "cache", "cache"), opts, - tag='cache', - pack={'__opts__': opts, '__context__': {'serial': serial}}, + tag="cache", + pack={"__opts__": opts, "__context__": {"serial": serial}}, ) @@ -1087,7 +1076,7 @@ def _generate_module(name): if name in sys.modules: return - code = "'''Salt loaded {0} parent module'''".format(name.split('.')[-1]) + code = "'''Salt loaded {0} parent module'''".format(name.split(".")[-1]) # ModuleType can't accept a unicode type on PY2 module = types.ModuleType(str(name)) # future lint: disable=blacklisted-function exec(code, module.__dict__) @@ -1096,17 +1085,18 @@ def _generate_module(name): def _mod_type(module_path): if module_path.startswith(SALT_BASE_PATH): - return 'int' - return 'ext' + return "int" + return "ext" # TODO: move somewhere else? class FilterDictWrapper(MutableMapping): - ''' + """ Create a dict which wraps another dict with a specific key suffix on get This is to replace "filter_load" - ''' + """ + def __init__(self, d, suffix): self._dict = d self.suffix = suffix @@ -1126,11 +1116,11 @@ class FilterDictWrapper(MutableMapping): def __iter__(self): for key in self._dict: if key.endswith(self.suffix): - yield key.replace(self.suffix, '') + yield key.replace(self.suffix, "") class LazyLoader(salt.utils.lazy.LazyDict): - ''' + """ A pseduo-dictionary which has a set of keys which are the name of the module and function, delimited by a dot. When the value of the key is accessed, the function is then loaded @@ -1154,33 +1144,34 @@ class LazyLoader(salt.utils.lazy.LazyDict): # TODO: - move modules_max_memory into here - singletons (per tag) - ''' + """ mod_dict_class = salt.utils.odict.OrderedDict - def __init__(self, - module_dirs, - opts=None, - tag='module', - loaded_base_name=None, - mod_type_check=None, - pack=None, - whitelist=None, - virtual_enable=True, - static_modules=None, - proxy=None, - virtual_funcs=None, - ): # pylint: disable=W0231 - ''' + def __init__( + self, + module_dirs, + opts=None, + tag="module", + loaded_base_name=None, + mod_type_check=None, + pack=None, + whitelist=None, + virtual_enable=True, + static_modules=None, + proxy=None, + virtual_funcs=None, + ): # pylint: disable=W0231 + """ In pack, if any of the values are None they will be replaced with an empty context-specific dict - ''' + """ self.inject_globals = {} self.pack = {} if pack is None else pack if opts is None: opts = {} - threadsafety = not opts.get('multiprocessing') + threadsafety = not opts.get("multiprocessing") self.context_dict = salt.utils.context.ContextDict(threadsafe=threadsafety) self.opts = self.__prep_mod_opts(opts) @@ -1189,13 +1180,15 @@ class LazyLoader(salt.utils.lazy.LazyDict): self.loaded_base_name = loaded_base_name or LOADED_BASE_NAME self.mod_type_check = mod_type_check or _mod_type - if '__context__' not in self.pack: - self.pack['__context__'] = None + if "__context__" not in self.pack: + self.pack["__context__"] = None for k, v in six.iteritems(self.pack): if v is None: # if the value of a pack is None, lets make an empty dict self.context_dict.setdefault(k, {}) - self.pack[k] = salt.utils.context.NamespacedDictWrapper(self.context_dict, k) + self.pack[k] = salt.utils.context.NamespacedDictWrapper( + self.context_dict, k + ) self.whitelist = whitelist self.virtual_enable = virtual_enable @@ -1213,11 +1206,8 @@ class LazyLoader(salt.utils.lazy.LazyDict): self.disabled = set( self.opts.get( - 'disable_{0}{1}'.format( - self.tag, - '' if self.tag[-1] == 's' else 's' - ), - [] + "disable_{0}{1}".format(self.tag, "" if self.tag[-1] == "s" else "s"), + [], ) ) @@ -1225,7 +1215,7 @@ class LazyLoader(salt.utils.lazy.LazyDict): self.suffix_map = {} # A list to determine precedence of extensions # Prefer packages (directories) over modules (single files)! - self.suffix_order = [''] + self.suffix_order = [""] for (suffix, mode, kind) in SUFFIXES: self.suffix_map[suffix] = (suffix, mode, kind) self.suffix_order.append(suffix) @@ -1235,16 +1225,16 @@ class LazyLoader(salt.utils.lazy.LazyDict): super(LazyLoader, self).__init__() # late init the lazy loader # create all of the import namespaces - _generate_module('{0}.int'.format(self.loaded_base_name)) - _generate_module('{0}.int.{1}'.format(self.loaded_base_name, tag)) - _generate_module('{0}.ext'.format(self.loaded_base_name)) - _generate_module('{0}.ext.{1}'.format(self.loaded_base_name, tag)) + _generate_module("{0}.int".format(self.loaded_base_name)) + _generate_module("{0}.int.{1}".format(self.loaded_base_name, tag)) + _generate_module("{0}.ext".format(self.loaded_base_name)) + _generate_module("{0}.ext.{1}".format(self.loaded_base_name, tag)) def __getitem__(self, item): - ''' + """ Override the __getitem__ in order to decorate the returned function if we need to last-minute inject globals - ''' + """ func = super(LazyLoader, self).__getitem__(item) if self.inject_globals: return global_injector_decorator(self.inject_globals)(func) @@ -1252,11 +1242,11 @@ class LazyLoader(salt.utils.lazy.LazyDict): return func def __getattr__(self, mod_name): - ''' + """ Allow for "direct" attribute access-- this allows jinja templates to access things like `salt.test.ping()` - ''' - if mod_name in ('__getstate__', '__setstate__'): + """ + if mod_name in ("__getstate__", "__setstate__"): return object.__getattribute__(self, mod_name) # if we have an attribute named that, lets return it. @@ -1279,48 +1269,52 @@ class LazyLoader(salt.utils.lazy.LazyDict): raise AttributeError(mod_name) def missing_fun_string(self, function_name): - ''' + """ Return the error string for a missing function. This can range from "not available' to "__virtual__" returned False - ''' - mod_name = function_name.split('.')[0] + """ + mod_name = function_name.split(".")[0] if mod_name in self.loaded_modules: - return '\'{0}\' is not available.'.format(function_name) + return "'{0}' is not available.".format(function_name) else: try: reason = self.missing_modules[mod_name] except KeyError: - return '\'{0}\' is not available.'.format(function_name) + return "'{0}' is not available.".format(function_name) else: if reason is not None: - return '\'{0}\' __virtual__ returned False: {1}'.format(mod_name, reason) + return "'{0}' __virtual__ returned False: {1}".format( + mod_name, reason + ) else: - return '\'{0}\' __virtual__ returned False'.format(mod_name) + return "'{0}' __virtual__ returned False".format(mod_name) def _refresh_file_mapping(self): - ''' + """ refresh the mapping of the FS on disk - ''' + """ # map of suffix to description for imp - if self.opts.get('cython_enable', True) is True: + if self.opts.get("cython_enable", True) is True: try: global pyximport - pyximport = __import__('pyximport') # pylint: disable=import-error + pyximport = __import__("pyximport") # pylint: disable=import-error pyximport.install() # add to suffix_map so file_mapping will pick it up - self.suffix_map['.pyx'] = tuple() + self.suffix_map[".pyx"] = tuple() except ImportError: - log.info('Cython is enabled in the options but not present ' - 'in the system path. Skipping Cython modules.') + log.info( + "Cython is enabled in the options but not present " + "in the system path. Skipping Cython modules." + ) # Allow for zipimport of modules - if self.opts.get('enable_zip_modules', True) is True: - self.suffix_map['.zip'] = tuple() + if self.opts.get("enable_zip_modules", True) is True: + self.suffix_map[".zip"] = tuple() # allow for module dirs if USE_IMPORTLIB: - self.suffix_map[''] = ('', '', MODULE_KIND_PKG_DIRECTORY) + self.suffix_map[""] = ("", "", MODULE_KIND_PKG_DIRECTORY) else: - self.suffix_map[''] = ('', '', imp.PKG_DIRECTORY) + self.suffix_map[""] = ("", "", imp.PKG_DIRECTORY) # create mapping of filename (without suffix) to (path, suffix) # The files are added in order of priority, so order *must* be retained. @@ -1329,29 +1323,29 @@ class LazyLoader(salt.utils.lazy.LazyDict): opt_match = [] def _replace_pre_ext(obj): - ''' + """ Hack so we can get the optimization level that we replaced (if any) out of the re.sub call below. We use a list here because it is a persistent data structure that we will be able to access after re.sub is called. - ''' + """ opt_match.append(obj) - return '' + return "" for mod_dir in self.module_dirs: try: # Make sure we have a sorted listdir in order to have # expectable override results - files = sorted( - x for x in os.listdir(mod_dir) if x != '__pycache__' - ) + files = sorted(x for x in os.listdir(mod_dir) if x != "__pycache__") except OSError: continue # Next mod_dir if six.PY3: try: pycache_files = [ - os.path.join('__pycache__', x) for x in - sorted(os.listdir(os.path.join(mod_dir, '__pycache__'))) + os.path.join("__pycache__", x) + for x in sorted( + os.listdir(os.path.join(mod_dir, "__pycache__")) + ) ] except OSError: pass @@ -1361,7 +1355,7 @@ class LazyLoader(salt.utils.lazy.LazyDict): for filename in files: try: dirname, basename = os.path.split(filename) - if basename.startswith('_'): + if basename.startswith("_"): # skip private modules # log messages omitted for obviousness continue # Next filename @@ -1369,21 +1363,22 @@ class LazyLoader(salt.utils.lazy.LazyDict): if six.PY3: f_noext = PY3_PRE_EXT.sub(_replace_pre_ext, f_noext) try: - opt_level = int( - opt_match.pop().group(1).rsplit('-', 1)[-1] - ) + opt_level = int(opt_match.pop().group(1).rsplit("-", 1)[-1]) except (AttributeError, IndexError, ValueError): # No regex match or no optimization level matched opt_level = 0 try: - opt_index = self.opts['optimization_order'].index(opt_level) + opt_index = self.opts["optimization_order"].index(opt_level) except KeyError: log.trace( - 'Disallowed optimization level %d for module ' - 'name \'%s\', skipping. Add %d to the ' - '\'optimization_order\' config option if you ' - 'do not want to ignore this optimization ' - 'level.', opt_level, f_noext, opt_level + "Disallowed optimization level %d for module " + "name '%s', skipping. Add %d to the " + "'optimization_order' config option if you " + "do not want to ignore this optimization " + "level.", + opt_level, + f_noext, + opt_level, ) continue else: @@ -1395,19 +1390,18 @@ class LazyLoader(salt.utils.lazy.LazyDict): continue # Next filename if f_noext in self.disabled: log.trace( - 'Skipping %s, it is disabled by configuration', - filename + "Skipping %s, it is disabled by configuration", filename ) continue # Next filename fpath = os.path.join(mod_dir, filename) # if its a directory, lets allow us to load that - if ext == '': + if ext == "": # is there something __init__? subfiles = os.listdir(fpath) for suffix in self.suffix_order: - if '' == suffix: + if "" == suffix: continue # Next suffix (__init__ must have a suffix) - init_file = '__init__{0}'.format(suffix) + init_file = "__init__{0}".format(suffix) if init_file in subfiles: break else: @@ -1419,25 +1413,27 @@ class LazyLoader(salt.utils.lazy.LazyDict): except KeyError: pass else: - if '' in (curr_ext, ext) and curr_ext != ext: + if "" in (curr_ext, ext) and curr_ext != ext: log.error( - 'Module/package collision: \'%s\' and \'%s\'', + "Module/package collision: '%s' and '%s'", fpath, - self.file_mapping[f_noext][0] + self.file_mapping[f_noext][0], ) - if six.PY3 and ext == '.pyc' and curr_ext == '.pyc': + if six.PY3 and ext == ".pyc" and curr_ext == ".pyc": # Check the optimization level if opt_index >= curr_opt_index: # Module name match, but a higher-priority # optimization level was already matched, skipping. continue - elif not curr_ext or self.suffix_order.index(ext) >= self.suffix_order.index(curr_ext): + elif not curr_ext or self.suffix_order.index( + ext + ) >= self.suffix_order.index(curr_ext): # Match found but a higher-priorty match already # exists, so skip this. continue - if six.PY3 and not dirname and ext == '.pyc': + if six.PY3 and not dirname and ext == ".pyc": # On Python 3, we should only load .pyc files from the # __pycache__ subdirectory (i.e. when dirname is not an # empty string). @@ -1449,13 +1445,13 @@ class LazyLoader(salt.utils.lazy.LazyDict): except OSError: continue for smod in self.static_modules: - f_noext = smod.split('.')[-1] - self.file_mapping[f_noext] = (smod, '.o', 0) + f_noext = smod.split(".")[-1] + self.file_mapping[f_noext] = (smod, ".o", 0) def clear(self): - ''' + """ Clear the dict - ''' + """ with self._lock: super(LazyLoader, self).clear() # clear the lazy loader self.loaded_files = set() @@ -1463,33 +1459,37 @@ class LazyLoader(salt.utils.lazy.LazyDict): self.loaded_modules = {} # if we have been loaded before, lets clear the file mapping since # we obviously want a re-do - if hasattr(self, 'opts'): + if hasattr(self, "opts"): self._refresh_file_mapping() self.initial_load = False def __prep_mod_opts(self, opts): - ''' + """ Strip out of the opts any logger instance - ''' - if '__grains__' not in self.pack: - self.context_dict['grains'] = opts.get('grains', {}) - self.pack['__grains__'] = salt.utils.context.NamespacedDictWrapper(self.context_dict, 'grains') + """ + if "__grains__" not in self.pack: + self.context_dict["grains"] = opts.get("grains", {}) + self.pack["__grains__"] = salt.utils.context.NamespacedDictWrapper( + self.context_dict, "grains" + ) - if '__pillar__' not in self.pack: - self.context_dict['pillar'] = opts.get('pillar', {}) - self.pack['__pillar__'] = salt.utils.context.NamespacedDictWrapper(self.context_dict, 'pillar') + if "__pillar__" not in self.pack: + self.context_dict["pillar"] = opts.get("pillar", {}) + self.pack["__pillar__"] = salt.utils.context.NamespacedDictWrapper( + self.context_dict, "pillar" + ) mod_opts = {} for key, val in list(opts.items()): - if key == 'logger': + if key == "logger": continue mod_opts[key] = val return mod_opts def _iter_files(self, mod_name): - ''' + """ Iterate over all file_mapping files in order of closeness to mod_name - ''' + """ # do we have an exact match? if mod_name in self.file_mapping: yield mod_name @@ -1506,14 +1506,15 @@ class LazyLoader(salt.utils.lazy.LazyDict): def _reload_submodules(self, mod): submodules = ( - getattr(mod, sname) for sname in dir(mod) if - isinstance(getattr(mod, sname), mod.__class__) + getattr(mod, sname) + for sname in dir(mod) + if isinstance(getattr(mod, sname), mod.__class__) ) # reload only custom "sub"modules for submodule in submodules: # it is a submodule if the name is in a namespace under mod - if submodule.__name__.startswith(mod.__name__ + '.'): + if submodule.__name__.startswith(mod.__name__ + "."): reload_module(submodule) self._reload_submodules(submodule) @@ -1524,46 +1525,58 @@ class LazyLoader(salt.utils.lazy.LazyDict): fpath_dirname = os.path.dirname(fpath) try: sys.path.append(fpath_dirname) - if suffix == '.pyx': + if suffix == ".pyx": mod = pyximport.load_module(name, fpath, tempfile.gettempdir()) - elif suffix == '.o': + elif suffix == ".o": top_mod = __import__(fpath, globals(), locals(), []) - comps = fpath.split('.') + comps = fpath.split(".") if len(comps) < 2: mod = top_mod else: mod = top_mod for subname in comps[1:]: mod = getattr(mod, subname) - elif suffix == '.zip': + elif suffix == ".zip": mod = zipimporter(fpath).load_module(name) else: desc = self.suffix_map[suffix] # if it is a directory, we don't open a file try: - mod_namespace = '.'.join(( - self.loaded_base_name, - self.mod_type_check(fpath), - self.tag, - name)) + mod_namespace = ".".join( + ( + self.loaded_base_name, + self.mod_type_check(fpath), + self.tag, + name, + ) + ) except TypeError: - mod_namespace = '{0}.{1}.{2}.{3}'.format( + mod_namespace = "{0}.{1}.{2}.{3}".format( self.loaded_base_name, self.mod_type_check(fpath), self.tag, - name) - if suffix == '': + name, + ) + if suffix == "": if USE_IMPORTLIB: # pylint: disable=no-member # Package directory, look for __init__ loader_details = [ - (importlib.machinery.SourceFileLoader, importlib.machinery.SOURCE_SUFFIXES), - (importlib.machinery.SourcelessFileLoader, importlib.machinery.BYTECODE_SUFFIXES), - (importlib.machinery.ExtensionFileLoader, importlib.machinery.EXTENSION_SUFFIXES), + ( + importlib.machinery.SourceFileLoader, + importlib.machinery.SOURCE_SUFFIXES, + ), + ( + importlib.machinery.SourcelessFileLoader, + importlib.machinery.BYTECODE_SUFFIXES, + ), + ( + importlib.machinery.ExtensionFileLoader, + importlib.machinery.EXTENSION_SUFFIXES, + ), ] file_finder = importlib.machinery.FileFinder( - fpath_dirname, - *loader_details + fpath_dirname, *loader_details ) spec = file_finder.find_spec(mod_namespace) if spec is None: @@ -1598,8 +1611,8 @@ class LazyLoader(salt.utils.lazy.LazyDict): # with the magic dunders we pack into the loaded # modules, most notably with salt-ssh's __opts__. mod = spec.loader.load_module() - #mod = importlib.util.module_from_spec(spec) - #spec.loader.exec_module(mod) + # mod = importlib.util.module_from_spec(spec) + # spec.loader.exec_module(mod) # pylint: enable=no-member sys.modules[mod_namespace] = mod else: @@ -1608,20 +1621,22 @@ class LazyLoader(salt.utils.lazy.LazyDict): except IOError: raise except ImportError as exc: - if 'magic number' in six.text_type(exc): - error_msg = 'Failed to import {0} {1}. Bad magic number. If migrating from Python2 to Python3, remove all .pyc files and try again.'.format(self.tag, name) + if "magic number" in six.text_type(exc): + error_msg = "Failed to import {0} {1}. Bad magic number. If migrating from Python2 to Python3, remove all .pyc files and try again.".format( + self.tag, name + ) log.warning(error_msg) self.missing_modules[name] = error_msg - log.debug( - 'Failed to import %s %s:\n', - self.tag, name, exc_info=True - ) + log.debug("Failed to import %s %s:\n", self.tag, name, exc_info=True) self.missing_modules[name] = exc return False except Exception as error: # pylint: disable=broad-except log.error( - 'Failed to import %s %s, this is due most likely to a ' - 'syntax error:\n', self.tag, name, exc_info=True + "Failed to import %s %s, this is due most likely to a " + "syntax error:\n", + self.tag, + name, + exc_info=True, ) self.missing_modules[name] = error return False @@ -1631,22 +1646,24 @@ class LazyLoader(salt.utils.lazy.LazyDict): except Exception: # pylint: disable=broad-except pass else: - tgt_fn = os.path.join('salt', 'utils', 'process.py') - if fn_.endswith(tgt_fn) and '_handle_signals' in caller: + tgt_fn = os.path.join("salt", "utils", "process.py") + if fn_.endswith(tgt_fn) and "_handle_signals" in caller: # Race conditon, SIGTERM or SIGINT received while loader # was in process of loading a module. Call sys.exit to # ensure that the process is killed. sys.exit(salt.defaults.exitcodes.EX_OK) log.error( - 'Failed to import %s %s as the module called exit()\n', - self.tag, name, exc_info=True + "Failed to import %s %s as the module called exit()\n", + self.tag, + name, + exc_info=True, ) self.missing_modules[name] = error return False finally: sys.path.remove(fpath_dirname) - if hasattr(mod, '__opts__'): + if hasattr(mod, "__opts__"): mod.__opts__.update(self.opts) else: mod.__opts__ = self.opts @@ -1655,20 +1672,23 @@ class LazyLoader(salt.utils.lazy.LazyDict): for p_name, p_value in six.iteritems(self.pack): setattr(mod, p_name, p_value) - module_name = mod.__name__.rsplit('.', 1)[-1] + module_name = mod.__name__.rsplit(".", 1)[-1] # Call a module's initialization method if it exists - module_init = getattr(mod, '__init__', None) + module_init = getattr(mod, "__init__", None) if inspect.isfunction(module_init): try: module_init(self.opts) except TypeError as e: log.error(e) except Exception: # pylint: disable=broad-except - err_string = '__init__ failed' + err_string = "__init__ failed" log.debug( - 'Error loading %s.%s: %s', - self.tag, module_name, err_string, exc_info=True + "Error loading %s.%s: %s", + self.tag, + module_name, + err_string, + exc_info=True, ) self.missing_modules[module_name] = err_string self.missing_modules[name] = err_string @@ -1677,14 +1697,17 @@ class LazyLoader(salt.utils.lazy.LazyDict): # if virtual modules are enabled, we need to look for the # __virtual__() function inside that module and run it. if self.virtual_enable: - virtual_funcs_to_process = ['__virtual__'] + self.virtual_funcs + virtual_funcs_to_process = ["__virtual__"] + self.virtual_funcs for virtual_func in virtual_funcs_to_process: - virtual_ret, module_name, virtual_err, virtual_aliases = \ - self._process_virtual(mod, module_name, virtual_func) + ( + virtual_ret, + module_name, + virtual_err, + virtual_aliases, + ) = self._process_virtual(mod, module_name, virtual_func) if virtual_err is not None: log.trace( - 'Error loading %s.%s: %s', - self.tag, module_name, virtual_err + "Error loading %s.%s: %s", self.tag, module_name, virtual_err ) # if _process_virtual returned a non-True value then we are @@ -1702,32 +1725,33 @@ class LazyLoader(salt.utils.lazy.LazyDict): # containing the names of the proxy types that the module supports. # # Render modules and state modules are OK though - if 'proxy' in self.opts: - if self.tag in ['grains', 'proxy']: - if not hasattr(mod, '__proxyenabled__') or \ - (self.opts['proxy']['proxytype'] not in mod.__proxyenabled__ and - '*' not in mod.__proxyenabled__): - err_string = 'not a proxy_minion enabled module' + if "proxy" in self.opts: + if self.tag in ["grains", "proxy"]: + if not hasattr(mod, "__proxyenabled__") or ( + self.opts["proxy"]["proxytype"] not in mod.__proxyenabled__ + and "*" not in mod.__proxyenabled__ + ): + err_string = "not a proxy_minion enabled module" self.missing_modules[module_name] = err_string self.missing_modules[name] = err_string return False - if getattr(mod, '__load__', False) is not False: + if getattr(mod, "__load__", False) is not False: log.info( - 'The functions from module \'%s\' are being loaded from the ' - 'provided __load__ attribute', module_name + "The functions from module '%s' are being loaded from the " + "provided __load__ attribute", + module_name, ) # If we had another module by the same virtual name, we should put any # new functions under the existing dictionary. mod_names = [module_name] + list(virtual_aliases) - mod_dict = dict(( - (x, self.loaded_modules.get(x, self.mod_dict_class())) - for x in mod_names - )) + mod_dict = dict( + ((x, self.loaded_modules.get(x, self.mod_dict_class())) for x in mod_names) + ) - for attr in getattr(mod, '__load__', dir(mod)): - if attr.startswith('_'): + for attr in getattr(mod, "__load__", dir(mod)): + if attr.startswith("_"): # private functions are skipped continue func = getattr(mod, attr) @@ -1741,12 +1765,12 @@ class LazyLoader(salt.utils.lazy.LazyDict): # # It default's of course to the found callable attribute name # if no alias is defined. - funcname = getattr(mod, '__func_alias__', {}).get(attr, attr) + funcname = getattr(mod, "__func_alias__", {}).get(attr, attr) for tgt_mod in mod_names: try: - full_funcname = '.'.join((tgt_mod, funcname)) + full_funcname = ".".join((tgt_mod, funcname)) except TypeError: - full_funcname = '{0}.{1}'.format(tgt_mod, funcname) + full_funcname = "{0}.{1}".format(tgt_mod, funcname) # Save many references for lookups # Careful not to overwrite existing (higher priority) functions if full_funcname not in self._dict: @@ -1761,8 +1785,8 @@ class LazyLoader(salt.utils.lazy.LazyDict): Depends.enforce_dependencies(self._dict, self.tag, name) except RuntimeError as exc: log.info( - 'Depends.enforce_dependencies() failed for the following ' - 'reason: %s', exc + "Depends.enforce_dependencies() failed for the following " "reason: %s", + exc, ) for tgt_mod in mod_names: @@ -1770,15 +1794,15 @@ class LazyLoader(salt.utils.lazy.LazyDict): return True def _load(self, key): - ''' + """ Load a single item if you have it - ''' + """ # if the key doesn't have a '.' then it isn't valid for this mod dict if not isinstance(key, six.string_types): - raise KeyError('The key must be a string.') - if '.' not in key: - raise KeyError('The key \'{0}\' should contain a \'.\''.format(key)) - mod_name, _ = key.split('.', 1) + raise KeyError("The key must be a string.") + if "." not in key: + raise KeyError("The key '{0}' should contain a '.'".format(key)) + mod_name, _ = key.split(".", 1) with self._lock: # It is possible that the key is in the dictionary after # acquiring the lock due to another thread loading it. @@ -1787,8 +1811,11 @@ class LazyLoader(salt.utils.lazy.LazyDict): # if the modulename isn't in the whitelist, don't bother if self.whitelist and mod_name not in self.whitelist: log.error( - 'Failed to load function %s because its module (%s) is ' - 'not in the whitelist: %s', key, mod_name, self.whitelist + "Failed to load function %s because its module (%s) is " + "not in the whitelist: %s", + key, + mod_name, + self.whitelist, ) raise KeyError(key) @@ -1823,9 +1850,9 @@ class LazyLoader(salt.utils.lazy.LazyDict): return ret def _load_all(self): - ''' + """ Load all of them - ''' + """ with self._lock: for name in self.file_mapping: if name in self.loaded_files or name in self.missing_modules: @@ -1840,16 +1867,16 @@ class LazyLoader(salt.utils.lazy.LazyDict): self._load_all() def _apply_outputter(self, func, mod): - ''' + """ Apply the __outputter__ variable to the functions - ''' - if hasattr(mod, '__outputter__'): + """ + if hasattr(mod, "__outputter__"): outp = mod.__outputter__ if func.__name__ in outp: func.__outputter__ = outp[func.__name__] - def _process_virtual(self, mod, module_name, virtual_func='__virtual__'): - ''' + def _process_virtual(self, mod, module_name, virtual_func="__virtual__"): + """ Given a loaded module and its default name determine its virtual name This function returns a tuple. The first value will be either True or @@ -1861,7 +1888,7 @@ class LazyLoader(salt.utils.lazy.LazyDict): The default name can be calculated as follows:: module_name = mod.__name__.rsplit('.', 1)[-1] - ''' + """ # The __virtual__ function will return either a True or False value. # If it returns a True value it can also set a module level attribute @@ -1874,30 +1901,33 @@ class LazyLoader(salt.utils.lazy.LazyDict): # namespace collisions. And finally it allows modules to return False # if they are not intended to run on the given platform or are missing # dependencies. - virtual_aliases = getattr(mod, '__virtual_aliases__', tuple()) + virtual_aliases = getattr(mod, "__virtual_aliases__", tuple()) try: error_reason = None - if hasattr(mod, '__virtual__') and inspect.isfunction(mod.__virtual__): + if hasattr(mod, "__virtual__") and inspect.isfunction(mod.__virtual__): try: start = time.time() virtual = getattr(mod, virtual_func)() if isinstance(virtual, tuple): error_reason = virtual[1] virtual = virtual[0] - if self.opts.get('virtual_timer', False): + if self.opts.get("virtual_timer", False): end = time.time() - start - msg = 'Virtual function took {0} seconds for {1}'.format( - end, module_name) + msg = "Virtual function took {0} seconds for {1}".format( + end, module_name + ) log.warning(msg) except Exception as exc: # pylint: disable=broad-except error_reason = ( - 'Exception raised when processing __virtual__ function' - ' for {0}. Module will not be loaded: {1}'.format( - mod.__name__, exc)) + "Exception raised when processing __virtual__ function" + " for {0}. Module will not be loaded: {1}".format( + mod.__name__, exc + ) + ) log.error(error_reason, exc_info_on_loglevel=logging.DEBUG) virtual = None # Get the module's virtual name - virtualname = getattr(mod, '__virtualname__', virtual) + virtualname = getattr(mod, "__virtualname__", virtual) if not virtual: # if __virtual__() evaluates to False then the module # wasn't meant for this platform or it's not supposed to @@ -1907,10 +1937,12 @@ class LazyLoader(salt.utils.lazy.LazyDict): # improperly loaded if virtual is None: log.warning( - '%s.__virtual__() is wrongly returning `None`. ' - 'It should either return `True`, `False` or a new ' - 'name. If you\'re the developer of the module ' - '\'%s\', please fix this.', mod.__name__, module_name + "%s.__virtual__() is wrongly returning `None`. " + "It should either return `True`, `False` or a new " + "name. If you're the developer of the module " + "'%s', please fix this.", + mod.__name__, + module_name, ) return (False, module_name, error_reason, virtual_aliases) @@ -1921,18 +1953,20 @@ class LazyLoader(salt.utils.lazy.LazyDict): if virtual is not True and module_name != virtual: # The module is renaming itself. Updating the module name # with the new name - log.trace('Loaded %s as virtual %s', module_name, virtual) + log.trace("Loaded %s as virtual %s", module_name, virtual) if virtualname != virtual: # The __virtualname__ attribute does not match what's # being returned by the __virtual__() function. This # should be considered an error. log.error( - 'The module \'%s\' is showing some bad usage. Its ' - '__virtualname__ attribute is set to \'%s\' yet the ' - '__virtual__() function is returning \'%s\'. These ' - 'values should match!', - mod.__name__, virtualname, virtual + "The module '%s' is showing some bad usage. Its " + "__virtualname__ attribute is set to '%s' yet the " + "__virtual__() function is returning '%s'. These " + "values should match!", + mod.__name__, + virtualname, + virtual, ) module_name = virtualname @@ -1948,14 +1982,16 @@ class LazyLoader(salt.utils.lazy.LazyDict): # in incomplete grains sets, these can be safely ignored # and logged to debug, still, it includes the traceback to # help debugging. - log.debug('KeyError when loading %s', module_name, exc_info=True) + log.debug("KeyError when loading %s", module_name, exc_info=True) except Exception: # pylint: disable=broad-except # If the module throws an exception during __virtual__() # then log the information and continue to the next. log.error( - 'Failed to read the virtual function for %s: %s', - self.tag, module_name, exc_info=True + "Failed to read the virtual function for %s: %s", + self.tag, + module_name, + exc_info=True, ) return (False, module_name, error_reason, virtual_aliases) @@ -1963,17 +1999,20 @@ class LazyLoader(salt.utils.lazy.LazyDict): def global_injector_decorator(inject_globals): - ''' + """ Decorator used by the LazyLoader to inject globals into a function at execute time. globals Dictionary with global variables to inject - ''' + """ + def inner_decorator(f): @functools.wraps(f) def wrapper(*args, **kwargs): with salt.utils.context.func_globals_inject(f, **inject_globals): return f(*args, **kwargs) + return wrapper + return inner_decorator diff --git a/salt/log/__init__.py b/salt/log/__init__.py index 2fbd2760794..e2514c1d0fb 100644 --- a/salt/log/__init__.py +++ b/salt/log/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -8,7 +8,7 @@ This is where Salt's logging gets set up. Currently, the required imports are made to assure backwards compatibility. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import severals classes/functions from salt.log.setup for backwards @@ -20,8 +20,8 @@ from salt.log.setup import ( is_logfile_configured, is_logging_configured, is_temp_logging_configured, - setup_temp_logger, + set_logger_level, setup_console_logger, setup_logfile_logger, - set_logger_level, + setup_temp_logger, ) diff --git a/salt/log/handlers/__init__.py b/salt/log/handlers/__init__.py index de262b97215..fe7002bd0b1 100644 --- a/salt/log/handlers/__init__.py +++ b/salt/log/handlers/__init__.py @@ -1,32 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" salt.log.handlers ~~~~~~~~~~~~~~~~~ .. versionadded:: 0.17.0 Custom logging handlers to be used in salt. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs -from salt._logging.handlers import TemporaryLoggingHandler -from salt._logging.handlers import StreamHandler -from salt._logging.handlers import FileHandler -from salt._logging.handlers import SysLogHandler -from salt._logging.handlers import RotatingFileHandler -from salt._logging.handlers import WatchedFileHandler -from salt._logging.handlers import QueueHandler -#from salt.utils.versions import warn_until_date -#warn_until_date( +from salt._logging.handlers import ( + FileHandler, + QueueHandler, + RotatingFileHandler, + StreamHandler, + SysLogHandler, + TemporaryLoggingHandler, + WatchedFileHandler, +) + +# from salt.utils.versions import warn_until_date +# warn_until_date( # '20220101', # 'Please stop using \'{name}\' and instead use \'salt._logging.handlers\'. ' # '\'{name}\' will go away after {{date}}.'.format( # name=__name__ # ) -#) +# ) NullHandler = logging.NullHandler diff --git a/salt/log/handlers/fluent_mod.py b/salt/log/handlers/fluent_mod.py index 78587f2d62f..3da4463396a 100644 --- a/salt/log/handlers/fluent_mod.py +++ b/salt/log/handlers/fluent_mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Fluent Logging Handler ====================== @@ -71,52 +71,54 @@ .. _fluentd: http://www.fluentd.org .. _`fluent-logger-python`: https://github.com/fluent/fluent-logger-python -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime import logging import logging.handlers -import time -import datetime import socket import threading +import time import types -# Import salt libs -from salt.log.setup import LOG_LEVELS -from salt.log.mixins import NewStyleClassMixIn import salt.utils.msgpack import salt.utils.network # Import Third party libs from salt.ext import six +from salt.log.mixins import NewStyleClassMixIn + +# Import salt libs +from salt.log.setup import LOG_LEVELS log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'fluent' +__virtualname__ = "fluent" _global_sender = None # Python logger's idea of "level" is wildly at variance with # Graylog's (and, incidentally, the rest of the civilized world). syslog_levels = { - 'EMERG': 0, - 'ALERT': 2, - 'CRIT': 2, - 'ERR': 3, - 'WARNING': 4, - 'NOTICE': 5, - 'INFO': 6, - 'DEBUG': 7 + "EMERG": 0, + "ALERT": 2, + "CRIT": 2, + "ERR": 3, + "WARNING": 4, + "NOTICE": 5, + "INFO": 6, + "DEBUG": 7, } def setup(tag, **kwargs): - host = kwargs.get('host', 'localhost') - port = kwargs.get('port', 24224) + host = kwargs.get("host", "localhost") + port = kwargs.get("port", 24224) global _global_sender _global_sender = FluentSender(tag, host=host, port=port) @@ -127,11 +129,11 @@ def get_global_sender(): def __virtual__(): - if not any(['fluent_handler' in __opts__]): + if not any(["fluent_handler" in __opts__]): log.trace( - 'The required configuration section, \'fluent_handler\', ' - 'was not found the in the configuration. Not loading the fluent ' - 'logging handlers module.' + "The required configuration section, 'fluent_handler', " + "was not found the in the configuration. Not loading the fluent " + "logging handlers module." ) return False return __virtualname__ @@ -140,35 +142,41 @@ def __virtual__(): def setup_handlers(): host = port = None - if 'fluent_handler' in __opts__: - host = __opts__['fluent_handler'].get('host', None) - port = __opts__['fluent_handler'].get('port', None) - payload_type = __opts__['fluent_handler'].get('payload_type', None) + if "fluent_handler" in __opts__: + host = __opts__["fluent_handler"].get("host", None) + port = __opts__["fluent_handler"].get("port", None) + payload_type = __opts__["fluent_handler"].get("payload_type", None) # in general, you want the value of tag to ALSO be a member of tags - tags = __opts__['fluent_handler'].get('tags', ['salt']) - tag = tags[0] if tags else 'salt' - if payload_type == 'graylog': + tags = __opts__["fluent_handler"].get("tags", ["salt"]) + tag = tags[0] if tags else "salt" + if payload_type == "graylog": version = 0 - elif payload_type == 'gelf': + elif payload_type == "gelf": # We only support version 1.1 (the latest) of GELF... version = 1.1 else: # Default to logstash for backwards compat - payload_type = 'logstash' - version = __opts__['fluent_handler'].get('version', 1) + payload_type = "logstash" + version = __opts__["fluent_handler"].get("version", 1) if host is None and port is None: log.debug( - 'The required \'fluent_handler\' configuration keys, ' - '\'host\' and/or \'port\', are not properly configured. Not ' - 'enabling the fluent logging handler.' + "The required 'fluent_handler' configuration keys, " + "'host' and/or 'port', are not properly configured. Not " + "enabling the fluent logging handler." ) else: - formatter = MessageFormatter(payload_type=payload_type, version=version, tags=tags) + formatter = MessageFormatter( + payload_type=payload_type, version=version, tags=tags + ) fluent_handler = FluentHandler(tag, host=host, port=port) fluent_handler.setFormatter(formatter) fluent_handler.setLevel( - LOG_LEVELS[__opts__['fluent_handler'].get('log_level', __opts__.get('log_level', 'error'))] + LOG_LEVELS[ + __opts__["fluent_handler"].get( + "log_level", __opts__.get("log_level", "error") + ) + ] ) yield fluent_handler @@ -180,175 +188,247 @@ class MessageFormatter(logging.Formatter, NewStyleClassMixIn): def __init__(self, payload_type, version, tags, msg_type=None, msg_path=None): self.payload_type = payload_type self.version = version - self.tag = tags[0] if tags else 'salt' # 'salt' for backwards compat + self.tag = tags[0] if tags else "salt" # 'salt' for backwards compat self.tags = tags self.msg_path = msg_path if msg_path else payload_type self.msg_type = msg_type if msg_type else payload_type - format_func = 'format_{0}_v{1}'.format(payload_type, version).replace('.', '_') + format_func = "format_{0}_v{1}".format(payload_type, version).replace(".", "_") self.format = getattr(self, format_func) super(MessageFormatter, self).__init__(fmt=None, datefmt=None) def formatTime(self, record, datefmt=None): - if self.payload_type == 'gelf': # GELF uses epoch times + if self.payload_type == "gelf": # GELF uses epoch times return record.created - return datetime.datetime.utcfromtimestamp(record.created).isoformat()[:-3] + 'Z' + return datetime.datetime.utcfromtimestamp(record.created).isoformat()[:-3] + "Z" def format_graylog_v0(self, record): - ''' + """ Graylog 'raw' format is essentially the raw record, minimally munged to provide the bare minimum that td-agent requires to accept and route the event. This is well suited to a config where the client td-agents log directly to Graylog. - ''' + """ message_dict = { - 'message': record.getMessage(), - 'timestamp': self.formatTime(record), + "message": record.getMessage(), + "timestamp": self.formatTime(record), # Graylog uses syslog levels, not whatever it is Python does... - 'level': syslog_levels.get(record.levelname, 'ALERT'), - 'tag': self.tag + "level": syslog_levels.get(record.levelname, "ALERT"), + "tag": self.tag, } if record.exc_info: exc_info = self.formatException(record.exc_info) - message_dict.update({'full_message': exc_info}) + message_dict.update({"full_message": exc_info}) # Add any extra attributes to the message field for key, value in six.iteritems(record.__dict__): - if key in ('args', 'asctime', 'bracketlevel', 'bracketname', 'bracketprocess', - 'created', 'exc_info', 'exc_text', 'id', 'levelname', 'levelno', 'msecs', - 'msecs', 'message', 'msg', 'relativeCreated', 'version'): + if key in ( + "args", + "asctime", + "bracketlevel", + "bracketname", + "bracketprocess", + "created", + "exc_info", + "exc_text", + "id", + "levelname", + "levelno", + "msecs", + "msecs", + "message", + "msg", + "relativeCreated", + "version", + ): # These are already handled above or explicitly pruned. continue - if isinstance(value, (six.string_types, bool, dict, float, int, list, types.NoneType)): # pylint: disable=W1699 + # pylint: disable=incompatible-py3-code + if isinstance( + value, (six.string_types, bool, dict, float, int, list, types.NoneType) + ): val = value + # pylint: enable=incompatible-py3-code else: val = repr(value) - message_dict.update({'{0}'.format(key): val}) + message_dict.update({"{0}".format(key): val}) return message_dict def format_gelf_v1_1(self, record): - ''' + """ If your agent is (or can be) configured to forward pre-formed GELF to Graylog with ZERO fluent processing, this function is for YOU, pal... - ''' + """ message_dict = { - 'version': self.version, - 'host': salt.utils.network.get_fqhostname(), - 'short_message': record.getMessage(), - 'timestamp': self.formatTime(record), - 'level': syslog_levels.get(record.levelname, 'ALERT'), - "_tag": self.tag + "version": self.version, + "host": salt.utils.network.get_fqhostname(), + "short_message": record.getMessage(), + "timestamp": self.formatTime(record), + "level": syslog_levels.get(record.levelname, "ALERT"), + "_tag": self.tag, } if record.exc_info: exc_info = self.formatException(record.exc_info) - message_dict.update({'full_message': exc_info}) + message_dict.update({"full_message": exc_info}) # Add any extra attributes to the message field for key, value in six.iteritems(record.__dict__): - if key in ('args', 'asctime', 'bracketlevel', 'bracketname', 'bracketprocess', - 'created', 'exc_info', 'exc_text', 'id', 'levelname', 'levelno', 'msecs', - 'msecs', 'message', 'msg', 'relativeCreated', 'version'): + if key in ( + "args", + "asctime", + "bracketlevel", + "bracketname", + "bracketprocess", + "created", + "exc_info", + "exc_text", + "id", + "levelname", + "levelno", + "msecs", + "msecs", + "message", + "msg", + "relativeCreated", + "version", + ): # These are already handled above or explicitly avoided. continue - if isinstance(value, (six.string_types, bool, dict, float, int, list, types.NoneType)): # pylint: disable=W1699 + # pylint: disable=incompatible-py3-code + if isinstance( + value, (six.string_types, bool, dict, float, int, list, types.NoneType) + ): val = value + # pylint: enable=incompatible-py3-code else: val = repr(value) # GELF spec require "non-standard" fields to be prefixed with '_' (underscore). - message_dict.update({'_{0}'.format(key): val}) + message_dict.update({"_{0}".format(key): val}) return message_dict def format_logstash_v0(self, record): - ''' + """ Messages are formatted in logstash's expected format. - ''' + """ host = salt.utils.network.get_fqhostname() message_dict = { - '@timestamp': self.formatTime(record), - '@fields': { - 'levelname': record.levelname, - 'logger': record.name, - 'lineno': record.lineno, - 'pathname': record.pathname, - 'process': record.process, - 'threadName': record.threadName, - 'funcName': record.funcName, - 'processName': record.processName + "@timestamp": self.formatTime(record), + "@fields": { + "levelname": record.levelname, + "logger": record.name, + "lineno": record.lineno, + "pathname": record.pathname, + "process": record.process, + "threadName": record.threadName, + "funcName": record.funcName, + "processName": record.processName, }, - '@message': record.getMessage(), - '@source': '{0}://{1}/{2}'.format( - self.msg_type, - host, - self.msg_path - ), - '@source_host': host, - '@source_path': self.msg_path, - '@tags': self.tags, - '@type': self.msg_type, + "@message": record.getMessage(), + "@source": "{0}://{1}/{2}".format(self.msg_type, host, self.msg_path), + "@source_host": host, + "@source_path": self.msg_path, + "@tags": self.tags, + "@type": self.msg_type, } if record.exc_info: - message_dict['@fields']['exc_info'] = self.formatException( - record.exc_info - ) + message_dict["@fields"]["exc_info"] = self.formatException(record.exc_info) # Add any extra attributes to the message field for key, value in six.iteritems(record.__dict__): - if key in ('args', 'asctime', 'created', 'exc_info', 'exc_text', - 'filename', 'funcName', 'id', 'levelname', 'levelno', - 'lineno', 'module', 'msecs', 'msecs', 'message', 'msg', - 'name', 'pathname', 'process', 'processName', - 'relativeCreated', 'thread', 'threadName'): + if key in ( + "args", + "asctime", + "created", + "exc_info", + "exc_text", + "filename", + "funcName", + "id", + "levelname", + "levelno", + "lineno", + "module", + "msecs", + "msecs", + "message", + "msg", + "name", + "pathname", + "process", + "processName", + "relativeCreated", + "thread", + "threadName", + ): # These are already handled above or not handled at all continue if value is None: - message_dict['@fields'][key] = value + message_dict["@fields"][key] = value continue if isinstance(value, (six.string_types, bool, dict, float, int, list)): - message_dict['@fields'][key] = value + message_dict["@fields"][key] = value continue - message_dict['@fields'][key] = repr(value) + message_dict["@fields"][key] = repr(value) return message_dict def format_logstash_v1(self, record): - ''' + """ Messages are formatted in logstash's expected format. - ''' + """ message_dict = { - '@version': 1, - '@timestamp': self.formatTime(record), - 'host': salt.utils.network.get_fqhostname(), - 'levelname': record.levelname, - 'logger': record.name, - 'lineno': record.lineno, - 'pathname': record.pathname, - 'process': record.process, - 'threadName': record.threadName, - 'funcName': record.funcName, - 'processName': record.processName, - 'message': record.getMessage(), - 'tags': self.tags, - 'type': self.msg_type + "@version": 1, + "@timestamp": self.formatTime(record), + "host": salt.utils.network.get_fqhostname(), + "levelname": record.levelname, + "logger": record.name, + "lineno": record.lineno, + "pathname": record.pathname, + "process": record.process, + "threadName": record.threadName, + "funcName": record.funcName, + "processName": record.processName, + "message": record.getMessage(), + "tags": self.tags, + "type": self.msg_type, } if record.exc_info: - message_dict['exc_info'] = self.formatException( - record.exc_info - ) + message_dict["exc_info"] = self.formatException(record.exc_info) # Add any extra attributes to the message field for key, value in six.iteritems(record.__dict__): - if key in ('args', 'asctime', 'created', 'exc_info', 'exc_text', - 'filename', 'funcName', 'id', 'levelname', 'levelno', - 'lineno', 'module', 'msecs', 'msecs', 'message', 'msg', - 'name', 'pathname', 'process', 'processName', - 'relativeCreated', 'thread', 'threadName'): + if key in ( + "args", + "asctime", + "created", + "exc_info", + "exc_text", + "filename", + "funcName", + "id", + "levelname", + "levelno", + "lineno", + "module", + "msecs", + "msecs", + "message", + "msg", + "name", + "pathname", + "process", + "processName", + "relativeCreated", + "thread", + "threadName", + ): # These are already handled above or not handled at all continue @@ -365,20 +445,16 @@ class MessageFormatter(logging.Formatter, NewStyleClassMixIn): class FluentHandler(logging.Handler): - ''' + """ Logging Handler for fluent. - ''' - def __init__(self, - tag, - host='localhost', - port=24224, - timeout=3.0, - verbose=False): + """ + + def __init__(self, tag, host="localhost", port=24224, timeout=3.0, verbose=False): self.tag = tag - self.sender = FluentSender(tag, - host=host, port=port, - timeout=timeout, verbose=verbose) + self.sender = FluentSender( + tag, host=host, port=port, timeout=timeout, verbose=verbose + ) logging.Handler.__init__(self) def emit(self, record): @@ -395,13 +471,15 @@ class FluentHandler(logging.Handler): class FluentSender(object): - def __init__(self, - tag, - host='localhost', - port=24224, - bufmax=1 * 1024 * 1024, - timeout=3.0, - verbose=False): + def __init__( + self, + tag, + host="localhost", + port=24224, + bufmax=1 * 1024 * 1024, + timeout=3.0, + verbose=False, + ): self.tag = tag self.host = host @@ -430,7 +508,7 @@ class FluentSender(object): def _make_packet(self, label, timestamp, data): if label: - tag = '.'.join((self.tag, label)) + tag = ".".join((self.tag, label)) else: tag = self.tag packet = (tag, timestamp, data) @@ -472,10 +550,10 @@ class FluentSender(object): def _reconnect(self): if not self.socket: - if self.host.startswith('unix://'): + if self.host.startswith("unix://"): sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.settimeout(self.timeout) - sock.connect(self.host[len('unix://'):]) + sock.connect(self.host[len("unix://") :]) else: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(self.timeout) diff --git a/salt/log/handlers/log4mongo_mod.py b/salt/log/handlers/log4mongo_mod.py index 2a75b89418d..a54bd497148 100644 --- a/salt/log/handlers/log4mongo_mod.py +++ b/salt/log/handlers/log4mongo_mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Log4Mongo Logging Handler ========================= @@ -33,11 +33,12 @@ This work was inspired by the Salt logging handlers for LogStash and Sentry and by the log4mongo Python implementation. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import socket + import logging +import socket # Import salt libs from salt.ext import six @@ -47,11 +48,12 @@ from salt.log.setup import LOG_LEVELS # Import third party libs try: from log4mongo.handlers import MongoHandler, MongoFormatter + HAS_MONGO = True except ImportError: HAS_MONGO = False -__virtualname__ = 'mongo' +__virtualname__ = "mongo" def __virtual__(): @@ -64,38 +66,32 @@ class FormatterWithHost(logging.Formatter, NewStyleClassMixIn): def format(self, record): mongoformatter = MongoFormatter() document = mongoformatter.format(record) - document['hostname'] = socket.gethostname() + document["hostname"] = socket.gethostname() return document def setup_handlers(): - handler_id = 'log4mongo_handler' + handler_id = "log4mongo_handler" if handler_id in __opts__: config_fields = { - 'host': 'host', - 'port': 'port', - 'database_name': 'database_name', - 'collection': 'collection', - 'username': 'username', - 'password': 'password', - 'write_concern': 'w' + "host": "host", + "port": "port", + "database_name": "database_name", + "collection": "collection", + "username": "username", + "password": "password", + "write_concern": "w", } config_opts = {} for config_opt, arg_name in six.iteritems(config_fields): config_opts[arg_name] = __opts__[handler_id].get(config_opt) - config_opts['level'] = LOG_LEVELS[ - __opts__[handler_id].get( - 'log_level', - __opts__.get('log_level', 'error') - ) + config_opts["level"] = LOG_LEVELS[ + __opts__[handler_id].get("log_level", __opts__.get("log_level", "error")) ] - handler = MongoHandler( - formatter=FormatterWithHost(), - **config_opts - ) + handler = MongoHandler(formatter=FormatterWithHost(), **config_opts) yield handler else: yield False diff --git a/salt/log/handlers/logstash_mod.py b/salt/log/handlers/logstash_mod.py index 5f003446a23..8fdba7a5f46 100644 --- a/salt/log/handlers/logstash_mod.py +++ b/salt/log/handlers/logstash_mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Logstash Logging Handler ======================== @@ -153,24 +153,27 @@ .. _`ZeroMQ input`: http://logstash.net/docs/latest/inputs/zeromq .. _`high water mark`: http://api.zeromq.org/3-2:zmq-setsockopt -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + +import datetime import logging import logging.handlers -import datetime +import os -# Import salt libs -from salt.log.setup import LOG_LEVELS -from salt.log.mixins import NewStyleClassMixIn import salt.utils.json import salt.utils.network import salt.utils.stringutils # Import Third party libs from salt.ext import six +from salt.log.mixins import NewStyleClassMixIn + +# Import salt libs +from salt.log.setup import LOG_LEVELS + try: import zmq except ImportError: @@ -179,17 +182,18 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'logstash' +__virtualname__ = "logstash" def __virtual__(): - if not any(['logstash_udp_handler' in __opts__, - 'logstash_zmq_handler' in __opts__]): + if not any( + ["logstash_udp_handler" in __opts__, "logstash_zmq_handler" in __opts__] + ): log.trace( - 'None of the required configuration sections, ' - '\'logstash_udp_handler\' and \'logstash_zmq_handler\', ' - 'were found in the configuration. Not loading the Logstash ' - 'logging handlers module.' + "None of the required configuration sections, " + "'logstash_udp_handler' and 'logstash_zmq_handler', " + "were found in the configuration. Not loading the Logstash " + "logging handlers module." ) return False return __virtualname__ @@ -198,17 +202,17 @@ def __virtual__(): def setup_handlers(): host = port = address = None - if 'logstash_udp_handler' in __opts__: - host = __opts__['logstash_udp_handler'].get('host', None) - port = __opts__['logstash_udp_handler'].get('port', None) - version = __opts__['logstash_udp_handler'].get('version', 0) - msg_type = __opts__['logstash_udp_handler'].get('msg_type', 'logstash') + if "logstash_udp_handler" in __opts__: + host = __opts__["logstash_udp_handler"].get("host", None) + port = __opts__["logstash_udp_handler"].get("port", None) + version = __opts__["logstash_udp_handler"].get("version", 0) + msg_type = __opts__["logstash_udp_handler"].get("msg_type", "logstash") if host is None and port is None: log.debug( - 'The required \'logstash_udp_handler\' configuration keys, ' - '\'host\' and/or \'port\', are not properly configured. Not ' - 'configuring the logstash UDP logging handler.' + "The required 'logstash_udp_handler' configuration keys, " + "'host' and/or 'port', are not properly configured. Not " + "configuring the logstash UDP logging handler." ) else: logstash_formatter = LogstashFormatter(msg_type=msg_type, version=version) @@ -216,30 +220,30 @@ def setup_handlers(): udp_handler.setFormatter(logstash_formatter) udp_handler.setLevel( LOG_LEVELS[ - __opts__['logstash_udp_handler'].get( - 'log_level', + __opts__["logstash_udp_handler"].get( + "log_level", # Not set? Get the main salt log_level setting on the # configuration file __opts__.get( - 'log_level', + "log_level", # Also not set?! Default to 'error' - 'error' - ) + "error", + ), ) ] ) yield udp_handler - if 'logstash_zmq_handler' in __opts__: - address = __opts__['logstash_zmq_handler'].get('address', None) - zmq_hwm = __opts__['logstash_zmq_handler'].get('hwm', 1000) - version = __opts__['logstash_zmq_handler'].get('version', 0) + if "logstash_zmq_handler" in __opts__: + address = __opts__["logstash_zmq_handler"].get("address", None) + zmq_hwm = __opts__["logstash_zmq_handler"].get("hwm", 1000) + version = __opts__["logstash_zmq_handler"].get("version", 0) if address is None: log.debug( - 'The required \'logstash_zmq_handler\' configuration key, ' - '\'address\', is not properly configured. Not ' - 'configuring the logstash ZMQ logging handler.' + "The required 'logstash_zmq_handler' configuration key, " + "'address', is not properly configured. Not " + "configuring the logstash ZMQ logging handler." ) else: logstash_formatter = LogstashFormatter(version=version) @@ -247,15 +251,15 @@ def setup_handlers(): zmq_handler.setFormatter(logstash_formatter) zmq_handler.setLevel( LOG_LEVELS[ - __opts__['logstash_zmq_handler'].get( - 'log_level', + __opts__["logstash_zmq_handler"].get( + "log_level", # Not set? Get the main salt log_level setting on the # configuration file __opts__.get( - 'log_level', + "log_level", # Also not set?! Default to 'error' - 'error' - ) + "error", + ), ) ] ) @@ -266,98 +270,130 @@ def setup_handlers(): class LogstashFormatter(logging.Formatter, NewStyleClassMixIn): - def __init__(self, msg_type='logstash', msg_path='logstash', version=0): + def __init__(self, msg_type="logstash", msg_path="logstash", version=0): self.msg_path = msg_path self.msg_type = msg_type self.version = version - self.format = getattr(self, 'format_v{0}'.format(version)) + self.format = getattr(self, "format_v{0}".format(version)) super(LogstashFormatter, self).__init__(fmt=None, datefmt=None) def formatTime(self, record, datefmt=None): - return datetime.datetime.utcfromtimestamp(record.created).isoformat()[:-3] + 'Z' + return datetime.datetime.utcfromtimestamp(record.created).isoformat()[:-3] + "Z" def format_v0(self, record): host = salt.utils.network.get_fqhostname() message_dict = { - '@timestamp': self.formatTime(record), - '@fields': { - 'levelname': record.levelname, - 'logger': record.name, - 'lineno': record.lineno, - 'pathname': record.pathname, - 'process': record.process, - 'threadName': record.threadName, - 'funcName': record.funcName, - 'processName': record.processName + "@timestamp": self.formatTime(record), + "@fields": { + "levelname": record.levelname, + "logger": record.name, + "lineno": record.lineno, + "pathname": record.pathname, + "process": record.process, + "threadName": record.threadName, + "funcName": record.funcName, + "processName": record.processName, }, - '@message': record.getMessage(), - '@source': '{0}://{1}/{2}'.format( - self.msg_type, - host, - self.msg_path - ), - '@source_host': host, - '@source_path': self.msg_path, - '@tags': ['salt'], - '@type': self.msg_type, + "@message": record.getMessage(), + "@source": "{0}://{1}/{2}".format(self.msg_type, host, self.msg_path), + "@source_host": host, + "@source_path": self.msg_path, + "@tags": ["salt"], + "@type": self.msg_type, } if record.exc_info: - message_dict['@fields']['exc_info'] = self.formatException( - record.exc_info - ) + message_dict["@fields"]["exc_info"] = self.formatException(record.exc_info) # Add any extra attributes to the message field for key, value in six.iteritems(record.__dict__): - if key in ('args', 'asctime', 'created', 'exc_info', 'exc_text', - 'filename', 'funcName', 'id', 'levelname', 'levelno', - 'lineno', 'module', 'msecs', 'msecs', 'message', 'msg', - 'name', 'pathname', 'process', 'processName', - 'relativeCreated', 'thread', 'threadName'): + if key in ( + "args", + "asctime", + "created", + "exc_info", + "exc_text", + "filename", + "funcName", + "id", + "levelname", + "levelno", + "lineno", + "module", + "msecs", + "msecs", + "message", + "msg", + "name", + "pathname", + "process", + "processName", + "relativeCreated", + "thread", + "threadName", + ): # These are already handled above or not handled at all continue if value is None: - message_dict['@fields'][key] = value + message_dict["@fields"][key] = value continue if isinstance(value, (six.string_types, bool, dict, float, int, list)): - message_dict['@fields'][key] = value + message_dict["@fields"][key] = value continue - message_dict['@fields'][key] = repr(value) + message_dict["@fields"][key] = repr(value) return salt.utils.json.dumps(message_dict) def format_v1(self, record): message_dict = { - '@version': 1, - '@timestamp': self.formatTime(record), - 'host': salt.utils.network.get_fqhostname(), - 'levelname': record.levelname, - 'logger': record.name, - 'lineno': record.lineno, - 'pathname': record.pathname, - 'process': record.process, - 'threadName': record.threadName, - 'funcName': record.funcName, - 'processName': record.processName, - 'message': record.getMessage(), - 'tags': ['salt'], - 'type': self.msg_type + "@version": 1, + "@timestamp": self.formatTime(record), + "host": salt.utils.network.get_fqhostname(), + "levelname": record.levelname, + "logger": record.name, + "lineno": record.lineno, + "pathname": record.pathname, + "process": record.process, + "threadName": record.threadName, + "funcName": record.funcName, + "processName": record.processName, + "message": record.getMessage(), + "tags": ["salt"], + "type": self.msg_type, } if record.exc_info: - message_dict['exc_info'] = self.formatException( - record.exc_info - ) + message_dict["exc_info"] = self.formatException(record.exc_info) # Add any extra attributes to the message field for key, value in six.iteritems(record.__dict__): - if key in ('args', 'asctime', 'created', 'exc_info', 'exc_text', - 'filename', 'funcName', 'id', 'levelname', 'levelno', - 'lineno', 'module', 'msecs', 'msecs', 'message', 'msg', - 'name', 'pathname', 'process', 'processName', - 'relativeCreated', 'thread', 'threadName'): + if key in ( + "args", + "asctime", + "created", + "exc_info", + "exc_text", + "filename", + "funcName", + "id", + "levelname", + "levelno", + "lineno", + "module", + "msecs", + "msecs", + "message", + "msg", + "name", + "pathname", + "process", + "processName", + "relativeCreated", + "thread", + "threadName", + ): # These are already handled above or not handled at all continue @@ -374,18 +410,18 @@ class LogstashFormatter(logging.Formatter, NewStyleClassMixIn): class DatagramLogstashHandler(logging.handlers.DatagramHandler): - ''' + """ Logstash UDP logging handler. - ''' + """ def makePickle(self, record): return salt.utils.stringutils.to_bytes(self.format(record)) class ZMQLogstashHander(logging.Handler, NewStyleClassMixIn): - ''' + """ Logstash ZMQ logging handler. - ''' + """ def __init__(self, address, level=logging.NOTSET, zmq_hwm=1000): super(ZMQLogstashHander, self).__init__(level=level) @@ -397,7 +433,7 @@ class ZMQLogstashHander(logging.Handler, NewStyleClassMixIn): @property def publisher(self): current_pid = os.getpid() - if not getattr(self, '_publisher') or self._pid != current_pid: + if not getattr(self, "_publisher") or self._pid != current_pid: # We forked? Multiprocessing? Recreate!!! self._pid = current_pid self._context = zmq.Context() @@ -423,10 +459,10 @@ class ZMQLogstashHander(logging.Handler, NewStyleClassMixIn): def close(self): if self._context is not None: # One second to send any queued messages - if hasattr(self._context, 'destroy'): + if hasattr(self._context, "destroy"): self._context.destroy(1 * 1000) else: - if getattr(self, '_publisher', None) is not None: + if getattr(self, "_publisher", None) is not None: self._publisher.setsockopt(zmq.LINGER, 1 * 1000) self._publisher.close() diff --git a/salt/log/handlers/sentry_mod.py b/salt/log/handlers/sentry_mod.py index 5f9974c19f2..15b444a52f9 100644 --- a/salt/log/handlers/sentry_mod.py +++ b/salt/log/handlers/sentry_mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Sentry Logging Handler ====================== @@ -84,7 +84,7 @@ .. _`Sentry`: https://getsentry.com .. _`Raven`: https://raven.readthedocs.io .. _`Raven client documentation`: https://raven.readthedocs.io/en/latest/config/index.html#client-arguments -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -99,6 +99,7 @@ from salt.log import LOG_LEVELS try: import raven from raven.handlers.logging import SentryHandler + HAS_RAVEN = True except ImportError: HAS_RAVEN = False @@ -108,7 +109,7 @@ __grains__ = {} __salt__ = {} # Define the module's virtual name -__virtualname__ = 'sentry' +__virtualname__ = "sentry" def __virtual__(): @@ -118,38 +119,40 @@ def __virtual__(): def setup_handlers(): - ''' + """ sets up the sentry handler - ''' + """ __grains__ = salt.loader.grains(__opts__) __salt__ = salt.loader.minion_mods(__opts__) - if 'sentry_handler' not in __opts__: - log.debug('No \'sentry_handler\' key was found in the configuration') + if "sentry_handler" not in __opts__: + log.debug("No 'sentry_handler' key was found in the configuration") return False options = {} - dsn = get_config_value('dsn') + dsn = get_config_value("dsn") if dsn is not None: try: # support raven ver 5.5.0 from raven.transport import TransportRegistry, default_transports from raven.utils.urlparse import urlparse + transport_registry = TransportRegistry(default_transports) url = urlparse(dsn) if not transport_registry.supported_scheme(url.scheme): - raise ValueError('Unsupported Sentry DSN scheme: {0}'.format(url.scheme)) + raise ValueError( + "Unsupported Sentry DSN scheme: {0}".format(url.scheme) + ) except ValueError as exc: - log.info( - 'Raven failed to parse the configuration provided DSN: %s', exc - ) + log.info("Raven failed to parse the configuration provided DSN: %s", exc) if not dsn: - for key in ('project', 'servers', 'public_key', 'secret_key'): + for key in ("project", "servers", "public_key", "secret_key"): config_value = get_config_value(key) if config_value is None and key not in options: log.debug( - 'The required \'sentry_handler\' configuration key, ' - '\'%s\', is not properly configured. Not configuring ' - 'the sentry logging handler.', key + "The required 'sentry_handler' configuration key, " + "'%s', is not properly configured. Not configuring " + "the sentry logging handler.", + key, ) return elif config_value is None: @@ -157,67 +160,60 @@ def setup_handlers(): options[key] = config_value # site: An optional, arbitrary string to identify this client installation. - options.update({ - # site: An optional, arbitrary string to identify this client - # installation - 'site': get_config_value('site'), - - # name: This will override the server_name value for this installation. - # Defaults to socket.gethostname() - 'name': get_config_value('name'), - - # exclude_paths: Extending this allow you to ignore module prefixes - # when sentry attempts to discover which function an error comes from - 'exclude_paths': get_config_value('exclude_paths', ()), - - # include_paths: For example, in Django this defaults to your list of - # INSTALLED_APPS, and is used for drilling down where an exception is - # located - 'include_paths': get_config_value('include_paths', ()), - - # list_max_length: The maximum number of items a list-like container - # should store. - 'list_max_length': get_config_value('list_max_length'), - - # string_max_length: The maximum characters of a string that should be - # stored. - 'string_max_length': get_config_value('string_max_length'), - - # auto_log_stacks: Should Raven automatically log frame stacks - # (including locals) all calls as it would for exceptions. - 'auto_log_stacks': get_config_value('auto_log_stacks'), - - # timeout: If supported, the timeout value for sending messages to - # remote. - 'timeout': get_config_value('timeout', 1), - - # processors: A list of processors to apply to events before sending - # them to the Sentry server. Useful for sending additional global state - # data or sanitizing data that you want to keep off of the server. - 'processors': get_config_value('processors'), - - # dsn: Ensure the DSN is passed into the client - 'dsn': dsn - }) + options.update( + { + # site: An optional, arbitrary string to identify this client + # installation + "site": get_config_value("site"), + # name: This will override the server_name value for this installation. + # Defaults to socket.gethostname() + "name": get_config_value("name"), + # exclude_paths: Extending this allow you to ignore module prefixes + # when sentry attempts to discover which function an error comes from + "exclude_paths": get_config_value("exclude_paths", ()), + # include_paths: For example, in Django this defaults to your list of + # INSTALLED_APPS, and is used for drilling down where an exception is + # located + "include_paths": get_config_value("include_paths", ()), + # list_max_length: The maximum number of items a list-like container + # should store. + "list_max_length": get_config_value("list_max_length"), + # string_max_length: The maximum characters of a string that should be + # stored. + "string_max_length": get_config_value("string_max_length"), + # auto_log_stacks: Should Raven automatically log frame stacks + # (including locals) all calls as it would for exceptions. + "auto_log_stacks": get_config_value("auto_log_stacks"), + # timeout: If supported, the timeout value for sending messages to + # remote. + "timeout": get_config_value("timeout", 1), + # processors: A list of processors to apply to events before sending + # them to the Sentry server. Useful for sending additional global state + # data or sanitizing data that you want to keep off of the server. + "processors": get_config_value("processors"), + # dsn: Ensure the DSN is passed into the client + "dsn": dsn, + } + ) client = raven.Client(**options) - context = get_config_value('context') + context = get_config_value("context") context_dict = {} if context is not None: for tag in context: try: tag_value = __grains__[tag] except KeyError: - log.debug('Sentry tag \'%s\' not found in grains.', tag) + log.debug("Sentry tag '%s' not found in grains.", tag) continue if tag_value: context_dict[tag] = tag_value if context_dict: - client.context.merge({'tags': context_dict}) + client.context.merge({"tags": context_dict}) try: handler = SentryHandler(client) - exclude_patterns = get_config_value('exclude_patterns', None) + exclude_patterns = get_config_value("exclude_patterns", None) if exclude_patterns: filter_regexes = [re.compile(pattern) for pattern in exclude_patterns] @@ -229,14 +225,14 @@ def setup_handlers(): handler.addFilter(FilterExcludedMessages()) - handler.setLevel(LOG_LEVELS[get_config_value('log_level', 'error')]) + handler.setLevel(LOG_LEVELS[get_config_value("log_level", "error")]) return handler except ValueError as exc: - log.debug('Failed to setup the sentry logging handler', exc_info=True) + log.debug("Failed to setup the sentry logging handler", exc_info=True) def get_config_value(name, default=None): - ''' + """ returns a configuration option for the sentry_handler - ''' - return __opts__['sentry_handler'].get(name, default) + """ + return __opts__["sentry_handler"].get(name, default) diff --git a/salt/log/mixins.py b/salt/log/mixins.py index 6e8645d96dd..47477c9964b 100644 --- a/salt/log/mixins.py +++ b/salt/log/mixins.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -9,25 +9,28 @@ .. versionadded:: 0.17.0 Some mix-in classes to be used in salt's logging -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs # pylint: disable=unused-import -from salt._logging.mixins import NewStyleClassMixin as NewStyleClassMixIn -from salt._logging.mixins import LoggingProfileMixin as LoggingProfileMixIn -from salt._logging.mixins import LoggingTraceMixin as LoggingTraceMixIn +from salt._logging.mixins import ( + ExcInfoOnLogLevelFormatMixin as ExcInfoOnLogLevelFormatMixIn, +) from salt._logging.mixins import LoggingGarbageMixin as LoggingGarbageMixIn from salt._logging.mixins import LoggingMixinMeta as LoggingMixInMeta -from salt._logging.mixins import ExcInfoOnLogLevelFormatMixin as ExcInfoOnLogLevelFormatMixIn +from salt._logging.mixins import LoggingProfileMixin as LoggingProfileMixIn +from salt._logging.mixins import LoggingTraceMixin as LoggingTraceMixIn +from salt._logging.mixins import NewStyleClassMixin as NewStyleClassMixIn + # pylint: enable=unused-import -#from salt.utils.versions import warn_until_date -#warn_until_date( +# from salt.utils.versions import warn_until_date +# warn_until_date( # '20220101', # 'Please stop using \'{name}\' and instead use \'salt._logging.mixins\'. ' # '\'{name}\' will go away after {{date}}.'.format( # name=__name__ # ) -#) +# ) diff --git a/salt/log/setup.py b/salt/log/setup.py index 6f9fa63cda2..7d02e315e3d 100644 --- a/salt/log/setup.py +++ b/salt/log/setup.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -11,42 +11,52 @@ This module should be imported as soon as possible, preferably the first module salt or any salt depending library imports so any new logging logger instance uses our ``salt.log.setup.SaltLoggingClass``. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys -import time -import types -import socket + import logging import logging.handlers -import traceback import multiprocessing - -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlparse # pylint: disable=import-error,no-name-in-module +import os +import socket +import sys +import time +import traceback +import types # Import salt libs # pylint: disable=unused-import -from salt._logging import (LOG_COLORS, - LOG_LEVELS, - LOG_VALUES_TO_LEVELS, - SORTED_LEVEL_NAMES) -from salt._logging.impl import (SaltLogRecord, - SaltColorLogRecord, - LOGGING_NULL_HANDLER, - LOGGING_STORE_HANDLER, - LOGGING_TEMP_HANDLER) +from salt._logging import ( + LOG_COLORS, + LOG_LEVELS, + LOG_VALUES_TO_LEVELS, + SORTED_LEVEL_NAMES, +) +from salt._logging.handlers import ( + FileHandler, + QueueHandler, + RotatingFileHandler, + StreamHandler, + SysLogHandler, + WatchedFileHandler, +) +from salt._logging.impl import ( + LOGGING_NULL_HANDLER, + LOGGING_STORE_HANDLER, + LOGGING_TEMP_HANDLER, + SaltColorLogRecord, + SaltLogRecord, +) from salt._logging.impl import set_log_record_factory as setLogRecordFactory -from salt._logging.handlers import (StreamHandler, - SysLogHandler, - FileHandler, - WatchedFileHandler, - RotatingFileHandler, - QueueHandler) + +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves.urllib.parse import ( # pylint: disable=import-error,no-name-in-module + urlparse, +) + # pylint: enable=unused-import __CONSOLE_CONFIGURED = False @@ -61,7 +71,7 @@ __MP_LOGGING_QUEUE = None __MP_LOGGING_LEVEL = logging.GARBAGE __MP_LOGGING_QUEUE_PROCESS = None __MP_LOGGING_QUEUE_HANDLER = None -__MP_IN_MAINPROCESS = multiprocessing.current_process().name == 'MainProcess' +__MP_IN_MAINPROCESS = multiprocessing.current_process().name == "MainProcess" __MP_MAINPROCESS_ID = None @@ -94,34 +104,32 @@ def is_extended_logging_configured(): class SaltLogQueueHandler(QueueHandler): - ''' + """ Subclassed just to differentiate when debugging - ''' + """ def getLogger(name): # pylint: disable=C0103 - ''' + """ This function is just a helper, an alias to: logging.getLogger(name) Although you might find it useful, there's no reason why you should not be using the aliased method. - ''' + """ return logging.getLogger(name) -def setup_temp_logger(log_level='error'): - ''' +def setup_temp_logger(log_level="error"): + """ Setup the temporary console logger - ''' + """ if is_temp_logging_configured(): - logging.getLogger(__name__).warning( - 'Temporary logging is already configured' - ) + logging.getLogger(__name__).warning("Temporary logging is already configured") return if log_level is None: - log_level = 'warning' + log_level = "warning" level = LOG_LEVELS.get(log_level.lower(), logging.ERROR) @@ -130,7 +138,7 @@ def setup_temp_logger(log_level='error'): if handler in (LOGGING_NULL_HANDLER, LOGGING_STORE_HANDLER): continue - if not hasattr(handler, 'stream'): + if not hasattr(handler, "stream"): # Not a stream handler, continue continue @@ -142,9 +150,7 @@ def setup_temp_logger(log_level='error'): handler.setLevel(level) # Set the default temporary console formatter config - formatter = logging.Formatter( - '[%(levelname)-8s] %(message)s', datefmt='%H:%M:%S' - ) + formatter = logging.Formatter("[%(levelname)-8s] %(message)s", datefmt="%H:%M:%S") handler.setFormatter(formatter) logging.root.addHandler(handler) @@ -153,8 +159,7 @@ def setup_temp_logger(log_level='error'): LOGGING_NULL_HANDLER.sync_with_handlers([handler]) else: logging.getLogger(__name__).debug( - 'LOGGING_NULL_HANDLER is already None, can\'t sync messages ' - 'with it' + "LOGGING_NULL_HANDLER is already None, can't sync messages " "with it" ) # Remove the temporary null logging handler @@ -164,19 +169,19 @@ def setup_temp_logger(log_level='error'): __TEMP_LOGGING_CONFIGURED = True -def setup_console_logger(log_level='error', log_format=None, date_format=None): - ''' +def setup_console_logger(log_level="error", log_format=None, date_format=None): + """ Setup the console logger - ''' + """ if is_console_configured(): - logging.getLogger(__name__).warning('Console logging already configured') + logging.getLogger(__name__).warning("Console logging already configured") return # Remove the temporary logging handler __remove_temp_logging_handler() if log_level is None: - log_level = 'warning' + log_level = "warning" level = LOG_LEVELS.get(log_level.lower(), logging.ERROR) @@ -187,7 +192,7 @@ def setup_console_logger(log_level='error', log_format=None, date_format=None): if handler is LOGGING_STORE_HANDLER: continue - if not hasattr(handler, 'stream'): + if not hasattr(handler, "stream"): # Not a stream handler, continue continue @@ -200,9 +205,9 @@ def setup_console_logger(log_level='error', log_format=None, date_format=None): # Set the default console formatter config if not log_format: - log_format = '[%(levelname)-8s] %(message)s' + log_format = "[%(levelname)-8s] %(message)s" if not date_format: - date_format = '%H:%M:%S' + date_format = "%H:%M:%S" formatter = logging.Formatter(log_format, datefmt=date_format) @@ -215,9 +220,15 @@ def setup_console_logger(log_level='error', log_format=None, date_format=None): __LOGGING_CONSOLE_HANDLER = handler -def setup_logfile_logger(log_path, log_level='error', log_format=None, - date_format=None, max_bytes=0, backup_count=0): - ''' +def setup_logfile_logger( + log_path, + log_level="error", + log_format=None, + date_format=None, + max_bytes=0, + backup_count=0, +): + """ Setup the logfile logger Since version 0.10.6 we support logging to syslog, some examples: @@ -241,15 +252,15 @@ def setup_logfile_logger(log_path, log_level='error', log_format=None, The preferred way to do remote logging is setup a local syslog, point salt's logging to the local syslog(unix socket is much faster) and then have the local syslog forward the log messages to the remote syslog. - ''' + """ if is_logfile_configured(): - logging.getLogger(__name__).warning('Logfile logging already configured') + logging.getLogger(__name__).warning("Logfile logging already configured") return if log_path is None: logging.getLogger(__name__).warning( - 'log_path setting is set to `None`. Nothing else to do' + "log_path setting is set to `None`. Nothing else to do" ) return @@ -257,7 +268,7 @@ def setup_logfile_logger(log_path, log_level='error', log_format=None, __remove_temp_logging_handler() if log_level is None: - log_level = 'warning' + log_level = "warning" level = LOG_LEVELS.get(log_level.lower(), logging.ERROR) @@ -265,77 +276,71 @@ def setup_logfile_logger(log_path, log_level='error', log_format=None, root_logger = logging.getLogger() - if parsed_log_path.scheme in ('tcp', 'udp', 'file'): + if parsed_log_path.scheme in ("tcp", "udp", "file"): syslog_opts = { - 'facility': SysLogHandler.LOG_USER, - 'socktype': socket.SOCK_DGRAM + "facility": SysLogHandler.LOG_USER, + "socktype": socket.SOCK_DGRAM, } - if parsed_log_path.scheme == 'file' and parsed_log_path.path: + if parsed_log_path.scheme == "file" and parsed_log_path.path: facility_name = parsed_log_path.path.split(os.sep)[-1].upper() - if not facility_name.startswith('LOG_'): + if not facility_name.startswith("LOG_"): # The user is not specifying a syslog facility - facility_name = 'LOG_USER' # Syslog default - syslog_opts['address'] = parsed_log_path.path + facility_name = "LOG_USER" # Syslog default + syslog_opts["address"] = parsed_log_path.path else: # The user has set a syslog facility, let's update the path to # the logging socket - syslog_opts['address'] = os.sep.join( + syslog_opts["address"] = os.sep.join( parsed_log_path.path.split(os.sep)[:-1] ) elif parsed_log_path.path: # In case of udp or tcp with a facility specified facility_name = parsed_log_path.path.lstrip(os.sep).upper() - if not facility_name.startswith('LOG_'): + if not facility_name.startswith("LOG_"): # Logging facilities start with LOG_ if this is not the case # fail right now! raise RuntimeError( - 'The syslog facility \'{0}\' is not known'.format( - facility_name - ) + "The syslog facility '{0}' is not known".format(facility_name) ) else: # This is the case of udp or tcp without a facility specified - facility_name = 'LOG_USER' # Syslog default + facility_name = "LOG_USER" # Syslog default - facility = getattr( - SysLogHandler, facility_name, None - ) + facility = getattr(SysLogHandler, facility_name, None) if facility is None: # This python syslog version does not know about the user provided # facility name raise RuntimeError( - 'The syslog facility \'{0}\' is not known'.format( - facility_name - ) + "The syslog facility '{0}' is not known".format(facility_name) ) - syslog_opts['facility'] = facility + syslog_opts["facility"] = facility - if parsed_log_path.scheme == 'tcp': + if parsed_log_path.scheme == "tcp": # tcp syslog support was only added on python versions >= 2.7 if sys.version_info < (2, 7): raise RuntimeError( - 'Python versions lower than 2.7 do not support logging ' - 'to syslog using tcp sockets' + "Python versions lower than 2.7 do not support logging " + "to syslog using tcp sockets" ) - syslog_opts['socktype'] = socket.SOCK_STREAM + syslog_opts["socktype"] = socket.SOCK_STREAM - if parsed_log_path.scheme in ('tcp', 'udp'): - syslog_opts['address'] = ( + if parsed_log_path.scheme in ("tcp", "udp"): + syslog_opts["address"] = ( parsed_log_path.hostname, - parsed_log_path.port or logging.handlers.SYSLOG_UDP_PORT + parsed_log_path.port or logging.handlers.SYSLOG_UDP_PORT, ) - if sys.version_info < (2, 7) or parsed_log_path.scheme == 'file': + if sys.version_info < (2, 7) or parsed_log_path.scheme == "file": # There's not socktype support on python versions lower than 2.7 - syslog_opts.pop('socktype', None) + syslog_opts.pop("socktype", None) try: # Et voilá! Finally our syslog handler instance handler = SysLogHandler(**syslog_opts) except socket.error as err: logging.getLogger(__name__).error( - 'Failed to setup the Syslog logging handler: %s', err + "Failed to setup the Syslog logging handler: %s", err ) shutdown_multiprocessing_logging_listener() sys.exit(2) @@ -344,13 +349,13 @@ def setup_logfile_logger(log_path, log_level='error', log_format=None, log_dir = os.path.dirname(log_path) if not os.path.exists(log_dir): logging.getLogger(__name__).info( - 'Log directory not found, trying to create it: %s', log_dir + "Log directory not found, trying to create it: %s", log_dir ) try: os.makedirs(log_dir, mode=0o700) except OSError as ose: logging.getLogger(__name__).warning( - 'Failed to create directory for log file: %s (%s)', log_dir, ose + "Failed to create directory for log file: %s (%s)", log_dir, ose ) return try: @@ -359,17 +364,22 @@ def setup_logfile_logger(log_path, log_level='error', log_format=None, # user is not using plain ASCII, their system should be ready to # handle UTF-8. if max_bytes > 0: - handler = RotatingFileHandler(log_path, - mode='a', - maxBytes=max_bytes, - backupCount=backup_count, - encoding='utf-8', - delay=0) + handler = RotatingFileHandler( + log_path, + mode="a", + maxBytes=max_bytes, + backupCount=backup_count, + encoding="utf-8", + delay=0, + ) else: - handler = WatchedFileHandler(log_path, mode='a', encoding='utf-8', delay=0) + handler = WatchedFileHandler( + log_path, mode="a", encoding="utf-8", delay=0 + ) except (IOError, OSError): logging.getLogger(__name__).warning( - 'Failed to open log file, do you have permission to write to %s?', log_path + "Failed to open log file, do you have permission to write to %s?", + log_path, ) # Do not proceed with any more configuration since it will fail, we # have the console logging already setup and the user should see @@ -380,9 +390,9 @@ def setup_logfile_logger(log_path, log_level='error', log_format=None, # Set the default console formatter config if not log_format: - log_format = '%(asctime)s [%(name)-15s][%(levelname)-8s] %(message)s' + log_format = "%(asctime)s [%(name)-15s][%(levelname)-8s] %(message)s" if not date_format: - date_format = '%Y-%m-%d %H:%M:%S' + date_format = "%Y-%m-%d %H:%M:%S" formatter = logging.Formatter(log_format, datefmt=date_format) @@ -396,9 +406,9 @@ def setup_logfile_logger(log_path, log_level='error', log_format=None, def setup_extended_logging(opts): - ''' + """ Setup any additional logging handlers, internal or external - ''' + """ if is_extended_logging_configured() is True: # Don't re-configure external loggers return @@ -417,7 +427,7 @@ def setup_extended_logging(opts): additional_handlers = [] for name, get_handlers_func in six.iteritems(providers): - logging.getLogger(__name__).info('Processing `log_handlers.%s`', name) + logging.getLogger(__name__).info("Processing `log_handlers.%s`", name) # Keep a reference to the logging handlers count before getting the # possible additional ones. initial_handlers_count = len(logging.root.handlers) @@ -429,9 +439,10 @@ def setup_extended_logging(opts): # A false return value means not configuring any logging handler on # purpose logging.getLogger(__name__).info( - 'The `log_handlers.%s.setup_handlers()` function returned ' - '`False` which means no logging handler was configured on ' - 'purpose. Continuing...', name + "The `log_handlers.%s.setup_handlers()` function returned " + "`False` which means no logging handler was configured on " + "purpose. Continuing...", + name, ) continue else: @@ -439,19 +450,19 @@ def setup_extended_logging(opts): handlers = [handlers] for handler in handlers: - if not handler and \ - len(logging.root.handlers) == initial_handlers_count: + if not handler and len(logging.root.handlers) == initial_handlers_count: logging.getLogger(__name__).info( - 'The `log_handlers.%s`, did not return any handlers ' - 'and the global handlers count did not increase. This ' - 'could be a sign of `log_handlers.%s` not working as ' - 'supposed', name, name + "The `log_handlers.%s`, did not return any handlers " + "and the global handlers count did not increase. This " + "could be a sign of `log_handlers.%s` not working as " + "supposed", + name, + name, ) continue logging.getLogger(__name__).debug( - 'Adding the \'%s\' provided logging handler: \'%s\'', - name, handler + "Adding the '%s' provided logging handler: '%s'", name, handler ) additional_handlers.append(handler) logging.root.addHandler(handler) @@ -466,8 +477,7 @@ def setup_extended_logging(opts): LOGGING_STORE_HANDLER.sync_with_handlers(additional_handlers) else: logging.getLogger(__name__).debug( - 'LOGGING_STORE_HANDLER is already None, can\'t sync messages ' - 'with it' + "LOGGING_STORE_HANDLER is already None, can't sync messages " "with it" ) # Remove the temporary queue logging handler @@ -515,20 +525,18 @@ def set_multiprocessing_logging_level(log_level): def set_multiprocessing_logging_level_by_opts(opts): - ''' + """ This will set the multiprocessing logging level to the lowest logging level of all the types of logging that are configured. - ''' + """ global __MP_LOGGING_LEVEL log_levels = [ - LOG_LEVELS.get(opts.get('log_level', '').lower(), logging.ERROR), - LOG_LEVELS.get(opts.get('log_level_logfile', '').lower(), logging.ERROR) + LOG_LEVELS.get(opts.get("log_level", "").lower(), logging.ERROR), + LOG_LEVELS.get(opts.get("log_level_logfile", "").lower(), logging.ERROR), ] - for level in six.itervalues(opts.get('log_granular_levels', {})): - log_levels.append( - LOG_LEVELS.get(level.lower(), logging.ERROR) - ) + for level in six.itervalues(opts.get("log_granular_levels", {})): + log_levels.append(LOG_LEVELS.get(level.lower(), logging.ERROR)) __MP_LOGGING_LEVEL = min(log_levels) @@ -552,7 +560,7 @@ def setup_multiprocessing_logging_listener(opts, queue=None): __MP_MAINPROCESS_ID = os.getpid() __MP_LOGGING_QUEUE_PROCESS = multiprocessing.Process( target=__process_multiprocessing_logging_queue, - args=(opts, queue or get_multiprocessing_logging_queue(),) + args=(opts, queue or get_multiprocessing_logging_queue(),), ) __MP_LOGGING_QUEUE_PROCESS.daemon = True __MP_LOGGING_QUEUE_PROCESS.start() @@ -560,10 +568,10 @@ def setup_multiprocessing_logging_listener(opts, queue=None): def setup_multiprocessing_logging(queue=None): - ''' + """ This code should be called from within a running multiprocessing process instance. - ''' + """ from salt.utils.platform import is_windows global __MP_LOGGING_CONFIGURED @@ -594,15 +602,19 @@ def setup_multiprocessing_logging(queue=None): __remove_queue_logging_handler() # Let's add a queue handler to the logging root handlers - __MP_LOGGING_QUEUE_HANDLER = SaltLogQueueHandler(queue or get_multiprocessing_logging_queue()) + __MP_LOGGING_QUEUE_HANDLER = SaltLogQueueHandler( + queue or get_multiprocessing_logging_queue() + ) logging.root.addHandler(__MP_LOGGING_QUEUE_HANDLER) # Set the logging root level to the lowest needed level to get all # desired messages. log_level = get_multiprocessing_logging_level() logging.root.setLevel(log_level) logging.getLogger(__name__).debug( - 'Multiprocessing queue logging configured for the process running ' - 'under PID: %s at log level %s', os.getpid(), log_level + "Multiprocessing queue logging configured for the process running " + "under PID: %s at log level %s", + os.getpid(), + log_level, ) # The above logging call will create, in some situations, a futex wait # lock condition, probably due to the multiprocessing Queue's internal @@ -695,15 +707,17 @@ def shutdown_multiprocessing_logging_listener(daemonizing=False): return if __MP_LOGGING_QUEUE_PROCESS.is_alive(): - logging.getLogger(__name__).debug('Stopping the multiprocessing logging queue listener') + logging.getLogger(__name__).debug( + "Stopping the multiprocessing logging queue listener" + ) try: # Sent None sentinel to stop the logging processing queue __MP_LOGGING_QUEUE.put(None) # Let's join the multiprocessing logging handle thread time.sleep(0.5) - logging.getLogger(__name__).debug('closing multiprocessing queue') + logging.getLogger(__name__).debug("closing multiprocessing queue") __MP_LOGGING_QUEUE.close() - logging.getLogger(__name__).debug('joining multiprocessing queue thread') + logging.getLogger(__name__).debug("joining multiprocessing queue thread") __MP_LOGGING_QUEUE.join_thread() __MP_LOGGING_QUEUE = None __MP_LOGGING_QUEUE_PROCESS.join(1) @@ -717,22 +731,24 @@ def shutdown_multiprocessing_logging_listener(daemonizing=False): __MP_LOGGING_QUEUE_PROCESS.terminate() __MP_LOGGING_QUEUE_PROCESS = None __MP_LOGGING_LISTENER_CONFIGURED = False - logging.getLogger(__name__).debug('Stopped the multiprocessing logging queue listener') + logging.getLogger(__name__).debug( + "Stopped the multiprocessing logging queue listener" + ) -def set_logger_level(logger_name, log_level='error'): - ''' +def set_logger_level(logger_name, log_level="error"): + """ Tweak a specific logger's logging level - ''' + """ logging.getLogger(logger_name).setLevel( LOG_LEVELS.get(log_level.lower(), logging.ERROR) ) def patch_python_logging_handlers(): - ''' + """ Patch the python logging handlers with out mixed-in classes - ''' + """ logging.StreamHandler = StreamHandler logging.FileHandler = FileHandler logging.handlers.SysLogHandler = SysLogHandler @@ -745,32 +761,35 @@ def patch_python_logging_handlers(): def __process_multiprocessing_logging_queue(opts, queue): # Avoid circular import import salt.utils.process - salt.utils.process.appendproctitle('MultiprocessingLoggingQueue') + + salt.utils.process.appendproctitle("MultiprocessingLoggingQueue") # Assign UID/GID of user to proc if set from salt.utils.verify import check_user - user = opts.get('user') + + user = opts.get("user") if user: check_user(user) from salt.utils.platform import is_windows + if is_windows(): # On Windows, creating a new process doesn't fork (copy the parent # process image). Due to this, we need to setup all of our logging # inside this process. setup_temp_logger() setup_console_logger( - log_level=opts.get('log_level'), - log_format=opts.get('log_fmt_console'), - date_format=opts.get('log_datefmt_console') + log_level=opts.get("log_level"), + log_format=opts.get("log_fmt_console"), + date_format=opts.get("log_datefmt_console"), ) setup_logfile_logger( - opts.get('log_file'), - log_level=opts.get('log_level_logfile'), - log_format=opts.get('log_fmt_logfile'), - date_format=opts.get('log_datefmt_logfile'), - max_bytes=opts.get('log_rotate_max_bytes', 0), - backup_count=opts.get('log_rotate_backup_count', 0) + opts.get("log_file"), + log_level=opts.get("log_level_logfile"), + log_format=opts.get("log_fmt_logfile"), + date_format=opts.get("log_datefmt_logfile"), + max_bytes=opts.get("log_rotate_max_bytes", 0), + backup_count=opts.get("log_rotate_backup_count", 0), ) setup_extended_logging(opts) while True: @@ -787,16 +806,18 @@ def __process_multiprocessing_logging_queue(opts, queue): break except Exception as exc: # pylint: disable=broad-except logging.getLogger(__name__).warning( - 'An exception occurred in the multiprocessing logging ' - 'queue thread: %r', exc, exc_info_on_loglevel=logging.DEBUG + "An exception occurred in the multiprocessing logging " + "queue thread: %r", + exc, + exc_info_on_loglevel=logging.DEBUG, ) def __remove_null_logging_handler(): - ''' + """ This function will run once the temporary logging has been configured. It just removes the NullHandler from the logging handlers. - ''' + """ global LOGGING_NULL_HANDLER if LOGGING_NULL_HANDLER is None: # Already removed @@ -813,10 +834,10 @@ def __remove_null_logging_handler(): def __remove_queue_logging_handler(): - ''' + """ This function will run once the additional loggers have been synchronized. It just removes the QueueLoggingHandler from the logging handlers. - ''' + """ global LOGGING_STORE_HANDLER if LOGGING_STORE_HANDLER is None: # Already removed @@ -833,10 +854,10 @@ def __remove_queue_logging_handler(): def __remove_temp_logging_handler(): - ''' + """ This function will run once logging has been configured. It just removes the temporary stream Handler from the logging handlers. - ''' + """ if is_logging_configured(): # In this case, the temporary logging handler has been removed, return! return @@ -861,9 +882,9 @@ def __remove_temp_logging_handler(): def __global_logging_exception_handler(exc_type, exc_value, exc_traceback): - ''' + """ This function will log all un-handled python exceptions. - ''' + """ if exc_type.__name__ == "KeyboardInterrupt": # Do not log the exception or display the traceback on Keyboard Interrupt # Stop the logging queue listener thread @@ -872,13 +893,13 @@ def __global_logging_exception_handler(exc_type, exc_value, exc_traceback): else: # Log the exception logging.getLogger(__name__).error( - 'An un-handled exception was caught by salt\'s global exception ' - 'handler:\n%s: %s\n%s', + "An un-handled exception was caught by salt's global exception " + "handler:\n%s: %s\n%s", exc_type.__name__, exc_value, - ''.join(traceback.format_exception( - exc_type, exc_value, exc_traceback - )).strip() + "".join( + traceback.format_exception(exc_type, exc_value, exc_traceback) + ).strip(), ) # Call the original sys.excepthook sys.__excepthook__(exc_type, exc_value, exc_traceback) diff --git a/salt/master.py b/salt/master.py index fb2e0c35bf0..359ffc7de81 100644 --- a/salt/master.py +++ b/salt/master.py @@ -1,53 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" This module contains all of the routines needed to set up a master server, this involves preparing the three listeners and the workers needed by the master. -''' +""" # Import python libs -from __future__ import absolute_import, with_statement, print_function, unicode_literals +from __future__ import absolute_import, print_function, unicode_literals, with_statement + +import collections import copy import ctypes import functools +import logging +import multiprocessing import os import re -import sys -import time import signal import stat -import logging -import collections -import multiprocessing +import sys import threading -import salt.serializers.msgpack +import time -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext import six -from salt.ext.six.moves import range -from salt.utils.zeromq import zmq, ZMQDefaultLoop, install_zmq, ZMQ_VERSION_INFO -# pylint: enable=import-error,no-name-in-module,redefined-builtin - -import salt.ext.tornado.gen # pylint: disable=F0401 +import salt.acl +import salt.auth +import salt.client +import salt.client.ssh.client # Import salt libs import salt.crypt -import salt.client -import salt.client.ssh.client -import salt.exceptions -import salt.payload -import salt.pillar -import salt.state -import salt.runner -import salt.auth -import salt.wheel -import salt.minion -import salt.key -import salt.acl -import salt.engines import salt.daemons.masterapi import salt.defaults.exitcodes -import salt.transport.server +import salt.engines +import salt.exceptions +import salt.ext.tornado.gen # pylint: disable=F0401 +import salt.key import salt.log.setup +import salt.minion +import salt.payload +import salt.pillar +import salt.runner +import salt.serializers.msgpack +import salt.state +import salt.transport.server import salt.utils.args import salt.utils.atomicfile import salt.utils.crypt @@ -67,17 +61,31 @@ import salt.utils.stringutils import salt.utils.user import salt.utils.verify import salt.utils.zeromq +import salt.wheel from salt.config import DEFAULT_INTERVAL from salt.defaults import DEFAULT_TARGET_DELIM + +# pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext import six +from salt.ext.six.moves import range +from salt.ext.tornado.stack_context import StackContext from salt.transport import iter_transport_opts +from salt.utils.ctx import RequestContext from salt.utils.debug import ( - enable_sigusr1_handler, enable_sigusr2_handler, inspect_stack + enable_sigusr1_handler, + enable_sigusr2_handler, + inspect_stack, ) from salt.utils.event import tagify from salt.utils.odict import OrderedDict +from salt.utils.zeromq import ZMQ_VERSION_INFO, ZMQDefaultLoop, install_zmq, zmq + +# pylint: enable=import-error,no-name-in-module,redefined-builtin + try: import resource + HAS_RESOURCE = True except ImportError: # resource is not available on windows @@ -86,29 +94,30 @@ except ImportError: # Import halite libs try: import halite # pylint: disable=import-error + HAS_HALITE = True except ImportError: HAS_HALITE = False -from salt.ext.tornado.stack_context import StackContext -from salt.utils.ctx import RequestContext - log = logging.getLogger(__name__) class SMaster(object): - ''' + """ Create a simple salt-master, this will generate the top-level master - ''' - secrets = {} # mapping of key -> {'secret': multiprocessing type, 'reload': FUNCTION} + """ + + secrets = ( + {} + ) # mapping of key -> {'secret': multiprocessing type, 'reload': FUNCTION} def __init__(self, opts): - ''' + """ Create a salt master server instance :param dict opts: The salt options dictionary - ''' + """ self.opts = opts self.master_key = salt.crypt.MasterKeys(self.opts) self.key = self.__prep_key() @@ -119,39 +128,42 @@ class SMaster(object): # These methods are only used when pickling so will not be used on # non-Windows platforms. def __setstate__(self, state): - self.opts = state['opts'] - self.master_key = state['master_key'] - self.key = state['key'] - SMaster.secrets = state['secrets'] + self.opts = state["opts"] + self.master_key = state["master_key"] + self.key = state["key"] + SMaster.secrets = state["secrets"] def __getstate__(self): - return {'opts': self.opts, - 'master_key': self.master_key, - 'key': self.key, - 'secrets': SMaster.secrets} + return { + "opts": self.opts, + "master_key": self.master_key, + "key": self.key, + "secrets": SMaster.secrets, + } def __prep_key(self): - ''' + """ A key needs to be placed in the filesystem with permissions 0400 so clients are required to run as root. - ''' + """ return salt.daemons.masterapi.access_keys(self.opts) class Maintenance(salt.utils.process.SignalHandlingProcess): - ''' + """ A generalized maintenance process which performs maintenance routines. - ''' + """ + def __init__(self, opts, **kwargs): - ''' + """ Create a maintenance instance :param dict opts: The salt options - ''' + """ super(Maintenance, self).__init__(**kwargs) self.opts = opts # How often do we perform the maintenance tasks - self.loop_interval = int(self.opts['loop_interval']) + self.loop_interval = int(self.opts["loop_interval"]) # Track key rotation intervals self.rotate = int(time.time()) # A serializer for general maint operations @@ -162,47 +174,49 @@ class Maintenance(salt.utils.process.SignalHandlingProcess): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['opts'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["opts"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def _post_fork_init(self): - ''' + """ Some things need to be init'd after the fork has completed The easiest example is that one of these module types creates a thread in the parent process, then once the fork happens you'll start getting errors like "WARNING: Mixing fork() and threads detected; memory leaked." - ''' + """ # Load Runners ropts = dict(self.opts) - ropts['quiet'] = True + ropts["quiet"] = True runner_client = salt.runner.RunnerClient(ropts) # Load Returners self.returners = salt.loader.returners(self.opts, {}) # Init Scheduler - self.schedule = salt.utils.schedule.Schedule(self.opts, - runner_client.functions_dict(), - returners=self.returners) + self.schedule = salt.utils.schedule.Schedule( + self.opts, runner_client.functions_dict(), returners=self.returners + ) self.ckminions = salt.utils.minions.CkMinions(self.opts) # Make Event bus for firing - self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) + self.event = salt.utils.event.get_master_event( + self.opts, self.opts["sock_dir"], listen=False + ) # Init any values needed by the git ext pillar self.git_pillar = salt.daemons.masterapi.init_git_pillar(self.opts) self.presence_events = False - if self.opts.get('presence_events', False): + if self.opts.get("presence_events", False): tcp_only = True for transport, _ in iter_transport_opts(self.opts): - if transport != 'tcp': + if transport != "tcp": tcp_only = False if not tcp_only: # For a TCP only transport, the presence events will be @@ -210,13 +224,13 @@ class Maintenance(salt.utils.process.SignalHandlingProcess): self.presence_events = True def run(self): - ''' + """ This is the general passive maintenance process controller for the Salt master. This is where any data that needs to be cleanly maintained from the master is maintained. - ''' + """ salt.utils.process.appendproctitle(self.__class__.__name__) # init things that need to be done after the process is forked @@ -226,7 +240,7 @@ class Maintenance(salt.utils.process.SignalHandlingProcess): last = int(time.time()) last_git_pillar_update = last - git_pillar_update_interval = self.opts.get('git_pillar_update_interval', 0) + git_pillar_update_interval = self.opts.get("git_pillar_update_interval", 0) old_present = set() while True: now = int(time.time()) @@ -246,36 +260,42 @@ class Maintenance(salt.utils.process.SignalHandlingProcess): time.sleep(self.loop_interval) def handle_key_cache(self): - ''' + """ Evaluate accepted keys and create a msgpack file which contains a list - ''' - if self.opts['key_cache'] == 'sched': + """ + if self.opts["key_cache"] == "sched": keys = [] - #TODO DRY from CKMinions - if self.opts['transport'] in ('zeromq', 'tcp'): - acc = 'minions' + # TODO DRY from CKMinions + if self.opts["transport"] in ("zeromq", "tcp"): + acc = "minions" else: - acc = 'accepted' + acc = "accepted" - for fn_ in os.listdir(os.path.join(self.opts['pki_dir'], acc)): - if not fn_.startswith('.') and os.path.isfile(os.path.join(self.opts['pki_dir'], acc, fn_)): + for fn_ in os.listdir(os.path.join(self.opts["pki_dir"], acc)): + if not fn_.startswith(".") and os.path.isfile( + os.path.join(self.opts["pki_dir"], acc, fn_) + ): keys.append(fn_) - log.debug('Writing master key cache') + log.debug("Writing master key cache") # Write a temporary file securely if six.PY2: - with salt.utils.atomicfile.atomic_open(os.path.join(self.opts['pki_dir'], acc, '.key_cache')) as cache_file: + with salt.utils.atomicfile.atomic_open( + os.path.join(self.opts["pki_dir"], acc, ".key_cache") + ) as cache_file: self.serial.dump(keys, cache_file) else: - with salt.utils.atomicfile.atomic_open(os.path.join(self.opts['pki_dir'], acc, '.key_cache'), mode='wb') as cache_file: + with salt.utils.atomicfile.atomic_open( + os.path.join(self.opts["pki_dir"], acc, ".key_cache"), mode="wb" + ) as cache_file: self.serial.dump(keys, cache_file) def handle_key_rotate(self, now): - ''' + """ Rotate the AES key rotation - ''' + """ to_rotate = False - dfn = os.path.join(self.opts['cachedir'], '.dfn') + dfn = os.path.join(self.opts["cachedir"], ".dfn") try: stats = os.stat(dfn) # Basic Windows permissions don't distinguish between @@ -287,44 +307,46 @@ class Maintenance(salt.utils.process.SignalHandlingProcess): elif stats.st_mode == 0o100400: to_rotate = True else: - log.error('Found dropfile with incorrect permissions, ignoring...') + log.error("Found dropfile with incorrect permissions, ignoring...") os.remove(dfn) except os.error: pass - if self.opts.get('publish_session'): - if now - self.rotate >= self.opts['publish_session']: + if self.opts.get("publish_session"): + if now - self.rotate >= self.opts["publish_session"]: to_rotate = True if to_rotate: - log.info('Rotating master AES key') + log.info("Rotating master AES key") for secret_key, secret_map in six.iteritems(SMaster.secrets): # should be unnecessary-- since no one else should be modifying - with secret_map['secret'].get_lock(): - secret_map['secret'].value = salt.utils.stringutils.to_bytes(secret_map['reload']()) - self.event.fire_event({'rotate_{0}_key'.format(secret_key): True}, tag='key') + with secret_map["secret"].get_lock(): + secret_map["secret"].value = salt.utils.stringutils.to_bytes( + secret_map["reload"]() + ) + self.event.fire_event( + {"rotate_{0}_key".format(secret_key): True}, tag="key" + ) self.rotate = now - if self.opts.get('ping_on_rotate'): + if self.opts.get("ping_on_rotate"): # Ping all minions to get them to pick up the new key - log.debug('Pinging all connected minions ' - 'due to key rotation') + log.debug("Pinging all connected minions " "due to key rotation") salt.utils.master.ping_all_connected_minions(self.opts) def handle_git_pillar(self): - ''' + """ Update git pillar - ''' + """ try: for pillar in self.git_pillar: pillar.fetch_remotes() except Exception as exc: # pylint: disable=broad-except - log.error('Exception caught while updating git_pillar', - exc_info=True) + log.error("Exception caught while updating git_pillar", exc_info=True) def handle_schedule(self): - ''' + """ Evaluate the scheduler - ''' + """ try: self.schedule.eval() # Check if scheduler requires lower loop interval than @@ -332,13 +354,13 @@ class Maintenance(salt.utils.process.SignalHandlingProcess): if self.schedule.loop_interval < self.loop_interval: self.loop_interval = self.schedule.loop_interval except Exception as exc: # pylint: disable=broad-except - log.error('Exception %s occurred in scheduled job', exc) + log.error("Exception %s occurred in scheduled job", exc) self.schedule.cleanup_subprocesses() def handle_presence(self, old_present): - ''' + """ Fire presence events if enabled - ''' + """ # On the first run it may need more time for the EventPublisher # to come up and be ready. Set the timeout to account for this. if self.presence_events and self.event.connect_pull(timeout=3): @@ -347,25 +369,26 @@ class Maintenance(salt.utils.process.SignalHandlingProcess): lost = old_present.difference(present) if new or lost: # Fire new minions present event - data = {'new': list(new), - 'lost': list(lost)} - self.event.fire_event(data, tagify('change', 'presence')) - data = {'present': list(present)} - self.event.fire_event(data, tagify('present', 'presence')) + data = {"new": list(new), "lost": list(lost)} + self.event.fire_event(data, tagify("change", "presence")) + data = {"present": list(present)} + self.event.fire_event(data, tagify("present", "presence")) old_present.clear() old_present.update(present) class FileserverUpdate(salt.utils.process.SignalHandlingProcess): - ''' + """ A process from which to update any dynamic fileserver backends - ''' + """ + def __init__(self, opts, **kwargs): super(FileserverUpdate, self).__init__(**kwargs) self.opts = opts self.update_threads = {} # Avoid circular import import salt.fileserver + self.fileserver = salt.fileserver.Fileserver(self.opts) self.fill_buckets() @@ -374,32 +397,29 @@ class FileserverUpdate(salt.utils.process.SignalHandlingProcess): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['opts'], - log_queue=state['log_queue'], + state["opts"], log_queue=state["log_queue"], ) def __getstate__(self): - return {'opts': self.opts, - 'log_queue': self.log_queue, + return { + "opts": self.opts, + "log_queue": self.log_queue, } def fill_buckets(self): - ''' + """ Get the configured backends and the intervals for any backend which supports them, and set up the update "buckets". There will be one bucket for each thing being updated at a given interval. - ''' + """ update_intervals = self.fileserver.update_intervals() self.buckets = {} for backend in self.fileserver.backends(): - fstr = '{0}.update'.format(backend) + fstr = "{0}.update".format(backend) try: update_func = self.fileserver.servers[fstr] except KeyError: - log.debug( - 'No update function for the %s filserver backend', - backend - ) + log.debug("No update function for the %s filserver backend", backend) continue if backend in update_intervals: # Variable intervals are supported for this backend @@ -408,8 +428,9 @@ class FileserverUpdate(salt.utils.process.SignalHandlingProcess): # Don't allow an interval of 0 interval = DEFAULT_INTERVAL log.debug( - 'An update_interval of 0 is not supported, ' - 'falling back to %s', interval + "An update_interval of 0 is not supported, " + "falling back to %s", + interval, ) i_ptr = self.buckets.setdefault(interval, OrderedDict()) # Backend doesn't technically need to be present in the @@ -424,50 +445,58 @@ class FileserverUpdate(salt.utils.process.SignalHandlingProcess): # nothing to pass to the backend's update func, so we'll just # set the value to None. try: - interval_key = '{0}_update_interval'.format(backend) + interval_key = "{0}_update_interval".format(backend) interval = self.opts[interval_key] except KeyError: interval = DEFAULT_INTERVAL log.warning( - '%s key missing from configuration. Falling back to ' - 'default interval of %d seconds', - interval_key, interval + "%s key missing from configuration. Falling back to " + "default interval of %d seconds", + interval_key, + interval, ) - self.buckets.setdefault( - interval, OrderedDict())[(backend, update_func)] = None + self.buckets.setdefault(interval, OrderedDict())[ + (backend, update_func) + ] = None def update_fileserver(self, interval, backends): - ''' + """ Threading target which handles all updates for a given wait interval - ''' + """ + def _do_update(): log.debug( - 'Performing fileserver updates for items with an update ' - 'interval of %d', interval + "Performing fileserver updates for items with an update " + "interval of %d", + interval, ) for backend, update_args in six.iteritems(backends): backend_name, update_func = backend try: if update_args: log.debug( - 'Updating %s fileserver cache for the following ' - 'targets: %s', backend_name, update_args + "Updating %s fileserver cache for the following " + "targets: %s", + backend_name, + update_args, ) args = (update_args,) else: - log.debug('Updating %s fileserver cache', backend_name) + log.debug("Updating %s fileserver cache", backend_name) args = () update_func(*args) except Exception as exc: # pylint: disable=broad-except log.exception( - 'Uncaught exception while updating %s fileserver ' - 'cache', backend_name + "Uncaught exception while updating %s fileserver " "cache", + backend_name, ) log.debug( - 'Completed fileserver updates for items with an update ' - 'interval of %d, waiting %d seconds', interval, interval + "Completed fileserver updates for items with an update " + "interval of %d, waiting %d seconds", + interval, + interval, ) condition = threading.Condition() @@ -478,17 +507,16 @@ class FileserverUpdate(salt.utils.process.SignalHandlingProcess): _do_update() def run(self): - ''' + """ Start the update threads - ''' + """ salt.utils.process.appendproctitle(self.__class__.__name__) # Clean out the fileserver backend cache salt.daemons.masterapi.clean_fsbackend(self.opts) for interval in self.buckets: self.update_threads[interval] = threading.Thread( - target=self.update_fileserver, - args=(interval, self.buckets[interval]), + target=self.update_fileserver, args=(interval, self.buckets[interval]), ) self.update_threads[interval].start() @@ -498,21 +526,22 @@ class FileserverUpdate(salt.utils.process.SignalHandlingProcess): class Master(SMaster): - ''' + """ The salt master server - ''' + """ + def __init__(self, opts): - ''' + """ Create a salt master server instance :param dict: The salt options - ''' + """ if zmq and ZMQ_VERSION_INFO < (3, 2): log.warning( - 'You have a version of ZMQ less than ZMQ 3.2! There are ' - 'known connection keep-alive issues with ZMQ < 3.2 which ' - 'may result in loss of contact with minions. Please ' - 'upgrade your ZMQ!' + "You have a version of ZMQ less than ZMQ 3.2! There are " + "known connection keep-alive issues with ZMQ < 3.2 which " + "may result in loss of contact with minions. Please " + "upgrade your ZMQ!" ) SMaster.__init__(self, opts) @@ -526,64 +555,67 @@ class Master(SMaster): # hard limit,but raising to anything above soft limit fails... mof_h = mof_s log.info( - 'Current values for max open files soft/hard setting: %s/%s', - mof_s, mof_h + "Current values for max open files soft/hard setting: %s/%s", mof_s, mof_h ) # Let's grab, from the configuration file, the value to raise max open # files to - mof_c = self.opts['max_open_files'] + mof_c = self.opts["max_open_files"] if mof_c > mof_h: # The configured value is higher than what's allowed log.info( - 'The value for the \'max_open_files\' setting, %s, is higher ' - 'than the highest value the user running salt is allowed to ' - 'set (%s). Defaulting to %s.', mof_c, mof_h, mof_h + "The value for the 'max_open_files' setting, %s, is higher " + "than the highest value the user running salt is allowed to " + "set (%s). Defaulting to %s.", + mof_c, + mof_h, + mof_h, ) mof_c = mof_h if mof_s < mof_c: # There's room to raise the value. Raise it! - log.info('Raising max open files value to %s', mof_c) + log.info("Raising max open files value to %s", mof_c) resource.setrlimit(resource.RLIMIT_NOFILE, (mof_c, mof_h)) try: mof_s, mof_h = resource.getrlimit(resource.RLIMIT_NOFILE) log.info( - 'New values for max open files soft/hard values: %s/%s', - mof_s, mof_h + "New values for max open files soft/hard values: %s/%s", + mof_s, + mof_h, ) except ValueError: # https://github.com/saltstack/salt/issues/1991#issuecomment-13025595 # A user under macOS reported that our 100000 default value is # still too high. log.critical( - 'Failed to raise max open files setting to %s. If this ' - 'value is too low, the salt-master will most likely fail ' - 'to run properly.', mof_c + "Failed to raise max open files setting to %s. If this " + "value is too low, the salt-master will most likely fail " + "to run properly.", + mof_c, ) def _pre_flight(self): - ''' + """ Run pre flight checks. If anything in this method fails then the master should not start up. - ''' + """ errors = [] critical_errors = [] try: - os.chdir('/') + os.chdir("/") except OSError as err: - errors.append( - 'Cannot change to root directory ({0})'.format(err) - ) + errors.append("Cannot change to root directory ({0})".format(err)) - if self.opts.get('fileserver_verify_config', True): + if self.opts.get("fileserver_verify_config", True): # Avoid circular import import salt.fileserver + fileserver = salt.fileserver.Fileserver(self.opts) if not fileserver.servers: errors.append( - 'Failed to load fileserver backends, the configured backends ' - 'are: {0}'.format(', '.join(self.opts['fileserver_backend'])) + "Failed to load fileserver backends, the configured backends " + "are: {0}".format(", ".join(self.opts["fileserver_backend"])) ) else: # Run init() for all backends which support the function, to @@ -591,46 +623,50 @@ class Master(SMaster): try: fileserver.init() except salt.exceptions.FileserverConfigError as exc: - critical_errors.append('{0}'.format(exc)) + critical_errors.append("{0}".format(exc)) - if not self.opts['fileserver_backend']: - errors.append('No fileserver backends are configured') + if not self.opts["fileserver_backend"]: + errors.append("No fileserver backends are configured") # Check to see if we need to create a pillar cache dir - if self.opts['pillar_cache'] and not os.path.isdir(os.path.join(self.opts['cachedir'], 'pillar_cache')): + if self.opts["pillar_cache"] and not os.path.isdir( + os.path.join(self.opts["cachedir"], "pillar_cache") + ): try: with salt.utils.files.set_umask(0o077): - os.mkdir(os.path.join(self.opts['cachedir'], 'pillar_cache')) + os.mkdir(os.path.join(self.opts["cachedir"], "pillar_cache")) except OSError: pass - if self.opts.get('git_pillar_verify_config', True): + if self.opts.get("git_pillar_verify_config", True): try: git_pillars = [ - x for x in self.opts.get('ext_pillar', []) - if 'git' in x - and not isinstance(x['git'], six.string_types) + x + for x in self.opts.get("ext_pillar", []) + if "git" in x and not isinstance(x["git"], six.string_types) ] except TypeError: git_pillars = [] critical_errors.append( - 'Invalid ext_pillar configuration. It is likely that the ' - 'external pillar type was not specified for one or more ' - 'external pillars.' + "Invalid ext_pillar configuration. It is likely that the " + "external pillar type was not specified for one or more " + "external pillars." ) if git_pillars: try: new_opts = copy.deepcopy(self.opts) import salt.pillar.git_pillar + for repo in git_pillars: - new_opts['ext_pillar'] = [repo] + new_opts["ext_pillar"] = [repo] try: git_pillar = salt.utils.gitfs.GitPillar( new_opts, - repo['git'], + repo["git"], per_remote_overrides=salt.pillar.git_pillar.PER_REMOTE_OVERRIDES, per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY, - global_only=salt.pillar.git_pillar.GLOBAL_ONLY) + global_only=salt.pillar.git_pillar.GLOBAL_ONLY, + ) except salt.exceptions.FileserverConfigError as exc: critical_errors.append(exc.strerror) finally: @@ -641,15 +677,15 @@ class Master(SMaster): log.error(error) for error in critical_errors: log.critical(error) - log.critical('Master failed pre flight checks, exiting\n') + log.critical("Master failed pre flight checks, exiting\n") sys.exit(salt.defaults.exitcodes.EX_GENERIC) def start(self): - ''' + """ Turn on the master server components - ''' + """ self._pre_flight() - log.info('salt-master is starting as user \'%s\'', salt.utils.user.get_user()) + log.info("salt-master is starting as user '%s'", salt.utils.user.get_user()) enable_sigusr1_handler() enable_sigusr2_handler() @@ -663,105 +699,119 @@ class Master(SMaster): # Setup the secrets here because the PubServerChannel may need # them as well. - SMaster.secrets['aes'] = { - 'secret': multiprocessing.Array( + SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( ctypes.c_char, salt.utils.stringutils.to_bytes( salt.crypt.Crypticle.generate_key_string() - ) + ), ), - 'reload': salt.crypt.Crypticle.generate_key_string + "reload": salt.crypt.Crypticle.generate_key_string, } - log.info('Creating master process manager') + log.info("Creating master process manager") # Since there are children having their own ProcessManager we should wait for kill more time. self.process_manager = salt.utils.process.ProcessManager(wait_for_kill=5) pub_channels = [] - log.info('Creating master publisher process') + log.info("Creating master publisher process") log_queue = salt.log.setup.get_multiprocessing_logging_queue() for _, opts in iter_transport_opts(self.opts): chan = salt.transport.server.PubServerChannel.factory(opts) - chan.pre_fork(self.process_manager, kwargs={'log_queue': log_queue}) + chan.pre_fork(self.process_manager, kwargs={"log_queue": log_queue}) pub_channels.append(chan) - log.info('Creating master event publisher process') - self.process_manager.add_process(salt.utils.event.EventPublisher, args=(self.opts,)) + log.info("Creating master event publisher process") + self.process_manager.add_process( + salt.utils.event.EventPublisher, args=(self.opts,) + ) - if self.opts.get('reactor'): - if isinstance(self.opts['engines'], list): + if self.opts.get("reactor"): + if isinstance(self.opts["engines"], list): rine = False - for item in self.opts['engines']: - if 'reactor' in item: + for item in self.opts["engines"]: + if "reactor" in item: rine = True break if not rine: - self.opts['engines'].append({'reactor': {}}) + self.opts["engines"].append({"reactor": {}}) else: - if 'reactor' not in self.opts['engines']: - log.info('Enabling the reactor engine') - self.opts['engines']['reactor'] = {} + if "reactor" not in self.opts["engines"]: + log.info("Enabling the reactor engine") + self.opts["engines"]["reactor"] = {} salt.engines.start_engines(self.opts, self.process_manager) # must be after channels - log.info('Creating master maintenance process') + log.info("Creating master maintenance process") self.process_manager.add_process(Maintenance, args=(self.opts,)) - if self.opts.get('event_return'): - log.info('Creating master event return process') - self.process_manager.add_process(salt.utils.event.EventReturn, args=(self.opts,)) + if self.opts.get("event_return"): + log.info("Creating master event return process") + self.process_manager.add_process( + salt.utils.event.EventReturn, args=(self.opts,) + ) - ext_procs = self.opts.get('ext_processes', []) + ext_procs = self.opts.get("ext_processes", []) for proc in ext_procs: - log.info('Creating ext_processes process: %s', proc) + log.info("Creating ext_processes process: %s", proc) try: - mod = '.'.join(proc.split('.')[:-1]) - cls = proc.split('.')[-1] + mod = ".".join(proc.split(".")[:-1]) + cls = proc.split(".")[-1] _tmp = __import__(mod, globals(), locals(), [cls], -1) cls = _tmp.__getattribute__(cls) self.process_manager.add_process(cls, args=(self.opts,)) except Exception: # pylint: disable=broad-except - log.error('Error creating ext_processes process: %s', proc) + log.error("Error creating ext_processes process: %s", proc) - if HAS_HALITE and 'halite' in self.opts: - log.info('Creating master halite process') - self.process_manager.add_process(Halite, args=(self.opts['halite'],)) + if HAS_HALITE and "halite" in self.opts: + log.info("Creating master halite process") + self.process_manager.add_process(Halite, args=(self.opts["halite"],)) # TODO: remove, or at least push into the transport stuff (pre-fork probably makes sense there) - if self.opts['con_cache']: - log.info('Creating master concache process') - self.process_manager.add_process(salt.utils.master.ConnectedCache, args=(self.opts,)) + if self.opts["con_cache"]: + log.info("Creating master concache process") + self.process_manager.add_process( + salt.utils.master.ConnectedCache, args=(self.opts,) + ) # workaround for issue #16315, race condition - log.debug('Sleeping for two seconds to let concache rest') + log.debug("Sleeping for two seconds to let concache rest") time.sleep(2) - log.info('Creating master request server process') + log.info("Creating master request server process") kwargs = {} if salt.utils.platform.is_windows(): - kwargs['log_queue'] = log_queue - kwargs['log_queue_level'] = salt.log.setup.get_multiprocessing_logging_level() - kwargs['secrets'] = SMaster.secrets + kwargs["log_queue"] = log_queue + kwargs[ + "log_queue_level" + ] = salt.log.setup.get_multiprocessing_logging_level() + kwargs["secrets"] = SMaster.secrets self.process_manager.add_process( ReqServer, args=(self.opts, self.key, self.master_key), kwargs=kwargs, - name='ReqServer') + name="ReqServer", + ) - self.process_manager.add_process( - FileserverUpdate, - args=(self.opts,)) + self.process_manager.add_process(FileserverUpdate, args=(self.opts,)) # Fire up SSDP discovery publisher - if self.opts['discovery']: + if self.opts["discovery"]: if salt.utils.ssdp.SSDPDiscoveryServer.is_available(): - self.process_manager.add_process(salt.utils.ssdp.SSDPDiscoveryServer( - port=self.opts['discovery']['port'], - listen_ip=self.opts['interface'], - answer={'mapping': self.opts['discovery'].get('mapping', {})}).run) + self.process_manager.add_process( + salt.utils.ssdp.SSDPDiscoveryServer( + port=self.opts["discovery"]["port"], + listen_ip=self.opts["interface"], + answer={ + "mapping": self.opts["discovery"].get("mapping", {}) + }, + ).run + ) else: - log.error('Unable to load SSDP: asynchronous IO is not available.') + log.error("Unable to load SSDP: asynchronous IO is not available.") if sys.version_info.major == 2: - log.error('You are using Python 2, please install "trollius" module to enable SSDP discovery.') + log.error( + 'You are using Python 2, please install "trollius" module to enable SSDP discovery.' + ) # Install the SIGINT/SIGTERM handlers if not done so far if signal.getsignal(signal.SIGINT) is signal.SIG_DFL: @@ -785,15 +835,16 @@ class Master(SMaster): class Halite(salt.utils.process.SignalHandlingProcess): - ''' + """ Manage the Halite server - ''' + """ + def __init__(self, hopts, **kwargs): - ''' + """ Create a halite instance :param dict hopts: The halite options - ''' + """ super(Halite, self).__init__(**kwargs) self.hopts = hopts @@ -802,33 +853,34 @@ class Halite(salt.utils.process.SignalHandlingProcess): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['hopts'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["hopts"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'hopts': self.hopts, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "hopts": self.hopts, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def run(self): - ''' + """ Fire up halite! - ''' + """ salt.utils.process.appendproctitle(self.__class__.__name__) halite.start(self.hopts) class ReqServer(salt.utils.process.SignalHandlingProcess): - ''' + """ Starts up the master request server, minions send results to this interface. - ''' + """ + def __init__(self, opts, key, mkey, secrets=None, **kwargs): - ''' + """ Create a request server :param dict opts: The salt options dictionary @@ -837,7 +889,7 @@ class ReqServer(salt.utils.process.SignalHandlingProcess): :rtype: ReqServer :returns: Request server - ''' + """ super(ReqServer, self).__init__(**kwargs) self.opts = opts self.master_key = mkey @@ -850,22 +902,22 @@ class ReqServer(salt.utils.process.SignalHandlingProcess): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['opts'], - state['key'], - state['mkey'], - secrets=state['secrets'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["opts"], + state["key"], + state["mkey"], + secrets=state["secrets"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'key': self.key, - 'mkey': self.master_key, - 'secrets': self.secrets, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "key": self.key, + "mkey": self.master_key, + "secrets": self.secrets, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument @@ -873,9 +925,9 @@ class ReqServer(salt.utils.process.SignalHandlingProcess): super(ReqServer, self)._handle_signals(signum, sigframe) def __bind(self): - ''' + """ Binds the reply server - ''' + """ if self.log_queue is not None: salt.log.setup.set_multiprocessing_logging_queue(self.log_queue) if self.log_queue_level is not None: @@ -884,7 +936,7 @@ class ReqServer(salt.utils.process.SignalHandlingProcess): if self.secrets is not None: SMaster.secrets = self.secrets - dfn = os.path.join(self.opts['cachedir'], '.dfn') + dfn = os.path.join(self.opts["cachedir"], ".dfn") if os.path.isfile(dfn): try: if salt.utils.platform.is_windows() and not os.access(dfn, os.W_OK): @@ -895,8 +947,9 @@ class ReqServer(salt.utils.process.SignalHandlingProcess): pass # Wait for kill should be less then parent's ProcessManager. - self.process_manager = salt.utils.process.ProcessManager(name='ReqServer_ProcessManager', - wait_for_kill=1) + self.process_manager = salt.utils.process.ProcessManager( + name="ReqServer_ProcessManager", wait_for_kill=1 + ) req_channels = [] tcp_only = True @@ -904,45 +957,45 @@ class ReqServer(salt.utils.process.SignalHandlingProcess): chan = salt.transport.server.ReqServerChannel.factory(opts) chan.pre_fork(self.process_manager) req_channels.append(chan) - if transport != 'tcp': + if transport != "tcp": tcp_only = False kwargs = {} if salt.utils.platform.is_windows(): - kwargs['log_queue'] = self.log_queue - kwargs['log_queue_level'] = self.log_queue_level + kwargs["log_queue"] = self.log_queue + kwargs["log_queue_level"] = self.log_queue_level # Use one worker thread if only the TCP transport is set up on # Windows and we are using Python 2. There is load balancer # support on Windows for the TCP transport when using Python 3. - if tcp_only and six.PY2 and int(self.opts['worker_threads']) != 1: - log.warning('TCP transport supports only 1 worker on Windows ' - 'when using Python 2.') - self.opts['worker_threads'] = 1 + if tcp_only and six.PY2 and int(self.opts["worker_threads"]) != 1: + log.warning( + "TCP transport supports only 1 worker on Windows " + "when using Python 2." + ) + self.opts["worker_threads"] = 1 # Reset signals to default ones before adding processes to the process # manager. We don't want the processes being started to inherit those # signal handlers with salt.utils.process.default_signals(signal.SIGINT, signal.SIGTERM): - for ind in range(int(self.opts['worker_threads'])): - name = 'MWorker-{0}'.format(ind) - self.process_manager.add_process(MWorker, - args=(self.opts, - self.master_key, - self.key, - req_channels, - name), - kwargs=kwargs, - name=name) + for ind in range(int(self.opts["worker_threads"])): + name = "MWorker-{0}".format(ind) + self.process_manager.add_process( + MWorker, + args=(self.opts, self.master_key, self.key, req_channels, name), + kwargs=kwargs, + name=name, + ) self.process_manager.run() def run(self): - ''' + """ Start up the ReqServer - ''' + """ self.__bind() def destroy(self, signum=signal.SIGTERM): - if hasattr(self, 'process_manager'): + if hasattr(self, "process_manager"): self.process_manager.stop_restarting() self.process_manager.send_signal_to_processes(signum) self.process_manager.kill_children() @@ -950,22 +1003,18 @@ class ReqServer(salt.utils.process.SignalHandlingProcess): # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 class MWorker(salt.utils.process.SignalHandlingProcess): - ''' + """ The worker multiprocess instance to manage the backend operations for the salt master. - ''' - def __init__(self, - opts, - mkey, - key, - req_channels, - name, - **kwargs): - ''' + """ + + def __init__(self, opts, mkey, key, req_channels, name, **kwargs): + """ Create a salt master worker process :param dict opts: The salt options @@ -974,8 +1023,8 @@ class MWorker(salt.utils.process.SignalHandlingProcess): :rtype: MWorker :return: Master worker - ''' - kwargs['name'] = name + """ + kwargs["name"] = name self.name = name super(MWorker, self).__init__(**kwargs) self.opts = opts @@ -984,7 +1033,7 @@ class MWorker(salt.utils.process.SignalHandlingProcess): self.mkey = mkey self.key = key self.k_mtime = 0 - self.stats = collections.defaultdict(lambda: {'mean': 0, 'runs': 0}) + self.stats = collections.defaultdict(lambda: {"mean": 0, "runs": 0}) self.stat_clock = time.time() # We need __setstate__ and __getstate__ to also pickle 'SMaster.secrets'. @@ -994,43 +1043,44 @@ class MWorker(salt.utils.process.SignalHandlingProcess): # non-Windows platforms. def __setstate__(self, state): super(MWorker, self).__init__( - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + log_queue=state["log_queue"], log_queue_level=state["log_queue_level"] ) - self.opts = state['opts'] - self.req_channels = state['req_channels'] - self.mkey = state['mkey'] - self.key = state['key'] - self.k_mtime = state['k_mtime'] - SMaster.secrets = state['secrets'] + self.opts = state["opts"] + self.req_channels = state["req_channels"] + self.mkey = state["mkey"] + self.key = state["key"] + self.k_mtime = state["k_mtime"] + SMaster.secrets = state["secrets"] def __getstate__(self): return { - 'opts': self.opts, - 'req_channels': self.req_channels, - 'mkey': self.mkey, - 'key': self.key, - 'k_mtime': self.k_mtime, - 'secrets': SMaster.secrets, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "req_channels": self.req_channels, + "mkey": self.mkey, + "key": self.key, + "k_mtime": self.k_mtime, + "secrets": SMaster.secrets, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def _handle_signals(self, signum, sigframe): - for channel in getattr(self, 'req_channels', ()): + for channel in getattr(self, "req_channels", ()): channel.close() super(MWorker, self)._handle_signals(signum, sigframe) def __bind(self): - ''' + """ Bind to the local port - ''' + """ # using ZMQIOLoop since we *might* need zmq in there install_zmq() self.io_loop = ZMQDefaultLoop() self.io_loop.make_current() for req_channel in self.req_channels: - req_channel.post_fork(self._handle_payload, io_loop=self.io_loop) # TODO: cleaner? Maybe lazily? + req_channel.post_fork( + self._handle_payload, io_loop=self.io_loop + ) # TODO: cleaner? Maybe lazily? try: self.io_loop.start() except (KeyboardInterrupt, SystemExit): @@ -1039,7 +1089,7 @@ class MWorker(salt.utils.process.SignalHandlingProcess): @salt.ext.tornado.gen.coroutine def _handle_payload(self, payload): - ''' + """ The _handle_payload method is the key method used to figure out what needs to be done with communication to the server @@ -1058,86 +1108,91 @@ class MWorker(salt.utils.process.SignalHandlingProcess): 'user': 'root'}} :param dict payload: The payload route to the appropriate handler - ''' - key = payload['enc'] - load = payload['load'] - ret = {'aes': self._handle_aes, - 'clear': self._handle_clear}[key](load) + """ + key = payload["enc"] + load = payload["load"] + ret = {"aes": self._handle_aes, "clear": self._handle_clear}[key](load) raise salt.ext.tornado.gen.Return(ret) def _post_stats(self, start, cmd): - ''' + """ Calculate the master stats and fire events with stat info - ''' + """ end = time.time() duration = end - start - self.stats[cmd]['mean'] = (self.stats[cmd]['mean'] * (self.stats[cmd]['runs'] - 1) + duration) / self.stats[cmd]['runs'] - if end - self.stat_clock > self.opts['master_stats_event_iter']: + self.stats[cmd]["mean"] = ( + self.stats[cmd]["mean"] * (self.stats[cmd]["runs"] - 1) + duration + ) / self.stats[cmd]["runs"] + if end - self.stat_clock > self.opts["master_stats_event_iter"]: # Fire the event with the stats and wipe the tracker - self.aes_funcs.event.fire_event({'time': end - self.stat_clock, 'worker': self.name, 'stats': self.stats}, tagify(self.name, 'stats')) - self.stats = collections.defaultdict(lambda: {'mean': 0, 'runs': 0}) + self.aes_funcs.event.fire_event( + { + "time": end - self.stat_clock, + "worker": self.name, + "stats": self.stats, + }, + tagify(self.name, "stats"), + ) + self.stats = collections.defaultdict(lambda: {"mean": 0, "runs": 0}) self.stat_clock = end def _handle_clear(self, load): - ''' + """ Process a cleartext command :param dict load: Cleartext payload :return: The result of passing the load to a function in ClearFuncs corresponding to the command specified in the load's 'cmd' key. - ''' - log.trace('Clear payload received with command %s', load['cmd']) - cmd = load['cmd'] - if cmd.startswith('__'): + """ + log.trace("Clear payload received with command %s", load["cmd"]) + cmd = load["cmd"] + if cmd.startswith("__"): return False - if self.opts['master_stats']: + if self.opts["master_stats"]: start = time.time() - self.stats[cmd]['runs'] += 1 - ret = getattr(self.clear_funcs, cmd)(load), {'fun': 'send_clear'} - if self.opts['master_stats']: + self.stats[cmd]["runs"] += 1 + ret = getattr(self.clear_funcs, cmd)(load), {"fun": "send_clear"} + if self.opts["master_stats"]: self._post_stats(start, cmd) return ret def _handle_aes(self, data): - ''' + """ Process a command sent via an AES key :param str load: Encrypted payload :return: The result of passing the load to a function in AESFuncs corresponding to the command specified in the load's 'cmd' key. - ''' - if 'cmd' not in data: - log.error('Received malformed command %s', data) + """ + if "cmd" not in data: + log.error("Received malformed command %s", data) return {} - cmd = data['cmd'] - log.trace('AES payload received with command %s', data['cmd']) - if cmd.startswith('__'): + cmd = data["cmd"] + log.trace("AES payload received with command %s", data["cmd"]) + if cmd.startswith("__"): return False - if self.opts['master_stats']: + if self.opts["master_stats"]: start = time.time() - self.stats[cmd]['runs'] += 1 + self.stats[cmd]["runs"] += 1 def run_func(data): - return self.aes_funcs.run_func(data['cmd'], data) + return self.aes_funcs.run_func(data["cmd"], data) - with StackContext(functools.partial(RequestContext, - {'data': data, - 'opts': self.opts})): + with StackContext( + functools.partial(RequestContext, {"data": data, "opts": self.opts}) + ): ret = run_func(data) - if self.opts['master_stats']: + if self.opts["master_stats"]: self._post_stats(start, cmd) return ret def run(self): - ''' + """ Start a Master Worker - ''' + """ salt.utils.process.appendproctitle(self.name) - self.clear_funcs = ClearFuncs( - self.opts, - self.key, - ) + self.clear_funcs = ClearFuncs(self.opts, self.key,) self.aes_funcs = AESFuncs(self.opts) salt.utils.crypt.reinit_crypto() self.__bind() @@ -1145,42 +1200,43 @@ class MWorker(salt.utils.process.SignalHandlingProcess): # TODO: rename? No longer tied to "AES", just "encrypted" or "private" requests class AESFuncs(object): - ''' + """ Set up functions that are available when the load is encrypted with AES - ''' + """ + # The AES Functions: # def __init__(self, opts): - ''' + """ Create a new AESFuncs :param dict opts: The salt options :rtype: AESFuncs :returns: Instance for handling AES operations - ''' + """ self.opts = opts - self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) + self.event = salt.utils.event.get_master_event( + self.opts, self.opts["sock_dir"], listen=False + ) self.serial = salt.payload.Serial(opts) self.ckminions = salt.utils.minions.CkMinions(opts) # Make a client - self.local = salt.client.get_local_client(self.opts['conf_file']) + self.local = salt.client.get_local_client(self.opts["conf_file"]) # Create the master minion to access the external job cache self.mminion = salt.minion.MasterMinion( - self.opts, - states=False, - rend=False, - ignore_config_errors=True + self.opts, states=False, rend=False, ignore_config_errors=True ) self.__setup_fileserver() self.masterapi = salt.daemons.masterapi.RemoteFuncs(opts) def __setup_fileserver(self): - ''' + """ Set the local file objects from the file server interface - ''' + """ # Avoid circular import import salt.fileserver + self.fs_ = salt.fileserver.Fileserver(self.opts) self._serve_file = self.fs_.serve_file self._file_find = self.fs_._find_file @@ -1193,7 +1249,7 @@ class AESFuncs(object): self._file_envs = self.fs_.file_envs def __verify_minion(self, id_, token): - ''' + """ Take a minion id and a string signed with the minion private key The string needs to verify as 'salt' with the minion public key @@ -1202,36 +1258,37 @@ class AESFuncs(object): :rtype: bool :return: Boolean indicating whether or not the token can be verified. - ''' + """ if not salt.utils.verify.valid_id(self.opts, id_): return False - pub_path = os.path.join(self.opts['pki_dir'], 'minions', id_) + pub_path = os.path.join(self.opts["pki_dir"], "minions", id_) try: pub = salt.crypt.get_rsa_pub_key(pub_path) except (IOError, OSError): log.warning( - 'Salt minion claiming to be %s attempted to communicate with ' - 'master, but key could not be read and verification was denied.', - id_ + "Salt minion claiming to be %s attempted to communicate with " + "master, but key could not be read and verification was denied.", + id_, ) return False except (ValueError, IndexError, TypeError) as err: log.error('Unable to load public key "%s": %s', pub_path, err) try: - if salt.crypt.public_decrypt(pub, token) == b'salt': + if salt.crypt.public_decrypt(pub, token) == b"salt": return True except ValueError as err: - log.error('Unable to decrypt token: %s', err) + log.error("Unable to decrypt token: %s", err) log.error( - 'Salt minion claiming to be %s has attempted to communicate with ' - 'the master and could not be verified', id_ + "Salt minion claiming to be %s has attempted to communicate with " + "the master and could not be verified", + id_, ) return False def verify_minion(self, id_, token): - ''' + """ Take a minion id and a string signed with the minion private key The string needs to verify as 'salt' with the minion public key @@ -1240,63 +1297,67 @@ class AESFuncs(object): :rtype: bool :return: Boolean indicating whether or not the token can be verified. - ''' + """ return self.__verify_minion(id_, token) def __verify_minion_publish(self, clear_load): - ''' + """ Verify that the passed information authorized a minion to execute :param dict clear_load: A publication load from a minion :rtype: bool :return: A boolean indicating if the minion is allowed to publish the command in the load - ''' + """ # Verify that the load is valid - if 'peer' not in self.opts: + if "peer" not in self.opts: return False - if not isinstance(self.opts['peer'], dict): + if not isinstance(self.opts["peer"], dict): return False - if any(key not in clear_load for key in ('fun', 'arg', 'tgt', 'ret', 'tok', 'id')): + if any( + key not in clear_load for key in ("fun", "arg", "tgt", "ret", "tok", "id") + ): return False # If the command will make a recursive publish don't run - if clear_load['fun'].startswith('publish.'): + if clear_load["fun"].startswith("publish."): return False # Check the permissions for this minion - if not self.__verify_minion(clear_load['id'], clear_load['tok']): + if not self.__verify_minion(clear_load["id"], clear_load["tok"]): # The minion is not who it says it is! # We don't want to listen to it! log.warning( - 'Minion id %s is not who it says it is and is attempting ' - 'to issue a peer command', clear_load['id'] + "Minion id %s is not who it says it is and is attempting " + "to issue a peer command", + clear_load["id"], ) return False - clear_load.pop('tok') + clear_load.pop("tok") perms = [] - for match in self.opts['peer']: - if re.match(match, clear_load['id']): + for match in self.opts["peer"]: + if re.match(match, clear_load["id"]): # This is the list of funcs/modules! - if isinstance(self.opts['peer'][match], list): - perms.extend(self.opts['peer'][match]) - if ',' in clear_load['fun']: + if isinstance(self.opts["peer"][match], list): + perms.extend(self.opts["peer"][match]) + if "," in clear_load["fun"]: # 'arg': [['cat', '/proc/cpuinfo'], [], ['foo']] - clear_load['fun'] = clear_load['fun'].split(',') + clear_load["fun"] = clear_load["fun"].split(",") arg_ = [] - for arg in clear_load['arg']: + for arg in clear_load["arg"]: arg_.append(arg.split()) - clear_load['arg'] = arg_ + clear_load["arg"] = arg_ # finally, check the auth of the load return self.ckminions.auth_check( perms, - clear_load['fun'], - clear_load['arg'], - clear_load['tgt'], - clear_load.get('tgt_type', 'glob'), - publish_validate=True) + clear_load["fun"], + clear_load["arg"], + clear_load["tgt"], + clear_load.get("tgt_type", "glob"), + publish_validate=True, + ) def __verify_load(self, load, verify_keys): - ''' + """ A utility function to perform common verification steps. :param dict load: A payload received from a minion @@ -1307,35 +1368,37 @@ class AESFuncs(object): :rtype: dict :return: The original load (except for the token) if the load can be verified. False if the load is invalid. - ''' + """ if any(key not in load for key in verify_keys): return False - if 'tok' not in load: + if "tok" not in load: log.error( - 'Received incomplete call from %s for \'%s\', missing \'%s\'', - load['id'], inspect_stack()['co_name'], 'tok' + "Received incomplete call from %s for '%s', missing '%s'", + load["id"], + inspect_stack()["co_name"], + "tok", ) return False - if not self.__verify_minion(load['id'], load['tok']): + if not self.__verify_minion(load["id"], load["tok"]): # The minion is not who it says it is! # We don't want to listen to it! - log.warning('Minion id %s is not who it says it is!', load['id']) + log.warning("Minion id %s is not who it says it is!", load["id"]) return False - if 'tok' in load: - load.pop('tok') + if "tok" in load: + load.pop("tok") return load def _master_tops(self, load): - ''' + """ Return the results from an external node classifier if one is specified :param dict load: A payload received from a minion :return: The results from an external node classifier - ''' - load = self.__verify_load(load, ('id', 'tok')) + """ + load = self.__verify_load(load, ("id", "tok")) if load is False: return {} return self.masterapi._master_tops(load, skip_verify=True) @@ -1344,138 +1407,142 @@ class AESFuncs(object): _ext_nodes = _master_tops def _master_opts(self, load): - ''' + """ Return the master options to the minion :param dict load: A payload received from a minion :rtype: dict :return: The master options - ''' + """ mopts = {} file_roots = {} envs = self._file_envs() for saltenv in envs: if saltenv not in file_roots: file_roots[saltenv] = [] - mopts['file_roots'] = file_roots - mopts['top_file_merging_strategy'] = self.opts['top_file_merging_strategy'] - mopts['env_order'] = self.opts['env_order'] - mopts['default_top'] = self.opts['default_top'] - if load.get('env_only'): + mopts["file_roots"] = file_roots + mopts["top_file_merging_strategy"] = self.opts["top_file_merging_strategy"] + mopts["env_order"] = self.opts["env_order"] + mopts["default_top"] = self.opts["default_top"] + if load.get("env_only"): return mopts - mopts['renderer'] = self.opts['renderer'] - mopts['failhard'] = self.opts['failhard'] - mopts['state_top'] = self.opts['state_top'] - mopts['state_top_saltenv'] = self.opts['state_top_saltenv'] - mopts['nodegroups'] = self.opts['nodegroups'] - mopts['state_auto_order'] = self.opts['state_auto_order'] - mopts['state_events'] = self.opts['state_events'] - mopts['state_aggregate'] = self.opts['state_aggregate'] - mopts['jinja_env'] = self.opts['jinja_env'] - mopts['jinja_sls_env'] = self.opts['jinja_sls_env'] - mopts['jinja_lstrip_blocks'] = self.opts['jinja_lstrip_blocks'] - mopts['jinja_trim_blocks'] = self.opts['jinja_trim_blocks'] + mopts["renderer"] = self.opts["renderer"] + mopts["failhard"] = self.opts["failhard"] + mopts["state_top"] = self.opts["state_top"] + mopts["state_top_saltenv"] = self.opts["state_top_saltenv"] + mopts["nodegroups"] = self.opts["nodegroups"] + mopts["state_auto_order"] = self.opts["state_auto_order"] + mopts["state_events"] = self.opts["state_events"] + mopts["state_aggregate"] = self.opts["state_aggregate"] + mopts["jinja_env"] = self.opts["jinja_env"] + mopts["jinja_sls_env"] = self.opts["jinja_sls_env"] + mopts["jinja_lstrip_blocks"] = self.opts["jinja_lstrip_blocks"] + mopts["jinja_trim_blocks"] = self.opts["jinja_trim_blocks"] return mopts def _mine_get(self, load): - ''' + """ Gathers the data from the specified minions' mine :param dict load: A payload received from a minion :rtype: dict :return: Mine data from the specified minions - ''' - load = self.__verify_load(load, ('id', 'tgt', 'fun', 'tok')) + """ + load = self.__verify_load(load, ("id", "tgt", "fun", "tok")) if load is False: return {} else: return self.masterapi._mine_get(load, skip_verify=True) def _mine(self, load): - ''' + """ Store the mine data :param dict load: A payload received from a minion :rtype: bool :return: True if the data has been stored in the mine - ''' - load = self.__verify_load(load, ('id', 'data', 'tok')) + """ + load = self.__verify_load(load, ("id", "data", "tok")) if load is False: return {} return self.masterapi._mine(load, skip_verify=True) def _mine_delete(self, load): - ''' + """ Allow the minion to delete a specific function from its own mine :param dict load: A payload received from a minion :rtype: bool :return: Boolean indicating whether or not the given function was deleted from the mine - ''' - load = self.__verify_load(load, ('id', 'fun', 'tok')) + """ + load = self.__verify_load(load, ("id", "fun", "tok")) if load is False: return {} else: return self.masterapi._mine_delete(load) def _mine_flush(self, load): - ''' + """ Allow the minion to delete all of its own mine contents :param dict load: A payload received from a minion - ''' - load = self.__verify_load(load, ('id', 'tok')) + """ + load = self.__verify_load(load, ("id", "tok")) if load is False: return {} else: return self.masterapi._mine_flush(load, skip_verify=True) def _file_recv(self, load): - ''' + """ Allows minions to send files to the master, files are sent to the master file cache - ''' - if any(key not in load for key in ('id', 'path', 'loc')): + """ + if any(key not in load for key in ("id", "path", "loc")): return False - if not isinstance(load['path'], list): + if not isinstance(load["path"], list): return False - if not self.opts['file_recv']: + if not self.opts["file_recv"]: return False - if not salt.utils.verify.valid_id(self.opts, load['id']): + if not salt.utils.verify.valid_id(self.opts, load["id"]): return False - file_recv_max_size = 1024*1024 * self.opts['file_recv_max_size'] + file_recv_max_size = 1024 * 1024 * self.opts["file_recv_max_size"] - if 'loc' in load and load['loc'] < 0: - log.error('Invalid file pointer: load[loc] < 0') + if "loc" in load and load["loc"] < 0: + log.error("Invalid file pointer: load[loc] < 0") return False - if len(load['data']) + load.get('loc', 0) > file_recv_max_size: + if len(load["data"]) + load.get("loc", 0) > file_recv_max_size: log.error( - 'file_recv_max_size limit of %d MB exceeded! %s will be ' - 'truncated. To successfully push this file, adjust ' - 'file_recv_max_size to an integer (in MB) large enough to ' - 'accommodate it.', file_recv_max_size, load['path'] + "file_recv_max_size limit of %d MB exceeded! %s will be " + "truncated. To successfully push this file, adjust " + "file_recv_max_size to an integer (in MB) large enough to " + "accommodate it.", + file_recv_max_size, + load["path"], ) return False - if 'tok' not in load: + if "tok" not in load: log.error( - 'Received incomplete call from %s for \'%s\', missing \'%s\'', - load['id'], inspect_stack()['co_name'], 'tok' + "Received incomplete call from %s for '%s', missing '%s'", + load["id"], + inspect_stack()["co_name"], + "tok", ) return False - if not self.__verify_minion(load['id'], load['tok']): + if not self.__verify_minion(load["id"], load["tok"]): # The minion is not who it says it is! # We don't want to listen to it! - log.warning('Minion id %s is not who it says it is!', load['id']) + log.warning("Minion id %s is not who it says it is!", load["id"]) return {} - load.pop('tok') + load.pop("tok") # Join path - sep_path = os.sep.join(load['path']) + sep_path = os.sep.join(load["path"]) # Path normalization should have been done by the sending # minion but we can't guarantee it. Re-do it here. @@ -1483,21 +1550,19 @@ class AESFuncs(object): # Ensure that this safety check is done after the path # have been normalized. - if os.path.isabs(normpath) or '../' in load['path']: + if os.path.isabs(normpath) or "../" in load["path"]: # Can overwrite master files!! return False cpath = os.path.join( - self.opts['cachedir'], - 'minions', - load['id'], - 'files', - normpath) + self.opts["cachedir"], "minions", load["id"], "files", normpath + ) # One last safety check here - if not os.path.normpath(cpath).startswith(self.opts['cachedir']): + if not os.path.normpath(cpath).startswith(self.opts["cachedir"]): log.warning( - 'Attempt to write received file outside of master cache ' - 'directory! Requested path: %s. Access denied.', cpath + "Attempt to write received file outside of master cache " + "directory! Requested path: %s. Access denied.", + cpath, ) return False cdir = os.path.dirname(cpath) @@ -1506,60 +1571,65 @@ class AESFuncs(object): os.makedirs(cdir) except os.error: pass - if os.path.isfile(cpath) and load['loc'] != 0: - mode = 'ab' + if os.path.isfile(cpath) and load["loc"] != 0: + mode = "ab" else: - mode = 'wb' + mode = "wb" with salt.utils.files.fopen(cpath, mode) as fp_: - if load['loc']: - fp_.seek(load['loc']) + if load["loc"]: + fp_.seek(load["loc"]) - fp_.write(salt.utils.stringutils.to_bytes(load['data'])) + fp_.write(salt.utils.stringutils.to_bytes(load["data"])) return True def _pillar(self, load): - ''' + """ Return the pillar data for the minion :param dict load: Minion payload :rtype: dict :return: The pillar data for the minion - ''' - if any(key not in load for key in ('id', 'grains')): + """ + if any(key not in load for key in ("id", "grains")): return False - if not salt.utils.verify.valid_id(self.opts, load['id']): + if not salt.utils.verify.valid_id(self.opts, load["id"]): return False - load['grains']['id'] = load['id'] + load["grains"]["id"] = load["id"] pillar = salt.pillar.get_pillar( self.opts, - load['grains'], - load['id'], - load.get('saltenv', load.get('env')), - ext=load.get('ext'), - pillar_override=load.get('pillar_override', {}), - pillarenv=load.get('pillarenv'), - extra_minion_data=load.get('extra_minion_data')) + load["grains"], + load["id"], + load.get("saltenv", load.get("env")), + ext=load.get("ext"), + pillar_override=load.get("pillar_override", {}), + pillarenv=load.get("pillarenv"), + extra_minion_data=load.get("extra_minion_data"), + ) data = pillar.compile_pillar() self.fs_.update_opts() - if self.opts.get('minion_data_cache', False): - self.masterapi.cache.store('minions/{0}'.format(load['id']), - 'data', - {'grains': load['grains'], - 'pillar': data}) - if self.opts.get('minion_data_cache_events') is True: - self.event.fire_event({'Minion data cache refresh': load['id']}, tagify(load['id'], 'refresh', 'minion')) + if self.opts.get("minion_data_cache", False): + self.masterapi.cache.store( + "minions/{0}".format(load["id"]), + "data", + {"grains": load["grains"], "pillar": data}, + ) + if self.opts.get("minion_data_cache_events") is True: + self.event.fire_event( + {"Minion data cache refresh": load["id"]}, + tagify(load["id"], "refresh", "minion"), + ) return data def _minion_event(self, load): - ''' + """ Receive an event from the minion and fire it on the master event interface :param dict load: The minion payload - ''' - load = self.__verify_load(load, ('id', 'tok')) + """ + load = self.__verify_load(load, ("id", "tok")) if load is False: return {} # Route to master event bus @@ -1568,38 +1638,33 @@ class AESFuncs(object): self._handle_minion_event(load) def _handle_minion_event(self, load): - ''' + """ Act on specific events from minions - ''' - id_ = load['id'] - if load.get('tag', '') == '_salt_error': + """ + id_ = load["id"] + if load.get("tag", "") == "_salt_error": log.error( - 'Received minion error from [%s]: %s', - id_, load['data']['message'] + "Received minion error from [%s]: %s", id_, load["data"]["message"] ) - for event in load.get('events', []): - event_data = event.get('data', {}) - if 'minions' in event_data: - jid = event_data.get('jid') + for event in load.get("events", []): + event_data = event.get("data", {}) + if "minions" in event_data: + jid = event_data.get("jid") if not jid: continue - minions = event_data['minions'] + minions = event_data["minions"] try: salt.utils.job.store_minions( - self.opts, - jid, - minions, - mminion=self.mminion, - syndic_id=id_) + self.opts, jid, minions, mminion=self.mminion, syndic_id=id_ + ) except (KeyError, salt.exceptions.SaltCacheError) as exc: log.error( - 'Could not add minion(s) %s for job %s: %s', - minions, jid, exc + "Could not add minion(s) %s for job %s: %s", minions, jid, exc ) def _return(self, load): - ''' + """ Handle the return data sent from the minions. Takes the return, verifies it and fires it on the master event bus. @@ -1607,100 +1672,110 @@ class AESFuncs(object): end of the event bus but could be heard by any listener on the bus. :param dict load: The minion payload - ''' - if self.opts['require_minion_sign_messages'] and 'sig' not in load: + """ + if self.opts["require_minion_sign_messages"] and "sig" not in load: log.critical( - '_return: Master is requiring minions to sign their ' - 'messages, but there is no signature in this payload from ' - '%s.', load['id'] + "_return: Master is requiring minions to sign their " + "messages, but there is no signature in this payload from " + "%s.", + load["id"], ) return False - if 'sig' in load: - log.trace('Verifying signed event publish from minion') - sig = load.pop('sig') - this_minion_pubkey = os.path.join(self.opts['pki_dir'], 'minions/{0}'.format(load['id'])) + if "sig" in load: + log.trace("Verifying signed event publish from minion") + sig = load.pop("sig") + this_minion_pubkey = os.path.join( + self.opts["pki_dir"], "minions/{0}".format(load["id"]) + ) serialized_load = salt.serializers.msgpack.serialize(load) - if not salt.crypt.verify_signature(this_minion_pubkey, serialized_load, sig): - log.info('Failed to verify event signature from minion %s.', load['id']) - if self.opts['drop_messages_signature_fail']: + if not salt.crypt.verify_signature( + this_minion_pubkey, serialized_load, sig + ): + log.info("Failed to verify event signature from minion %s.", load["id"]) + if self.opts["drop_messages_signature_fail"]: log.critical( - 'drop_messages_signature_fail is enabled, dropping ' - 'message from %s', load['id'] + "drop_messages_signature_fail is enabled, dropping " + "message from %s", + load["id"], ) return False else: - log.info('But \'drop_message_signature_fail\' is disabled, so message is still accepted.') - load['sig'] = sig + log.info( + "But 'drop_message_signature_fail' is disabled, so message is still accepted." + ) + load["sig"] = sig try: salt.utils.job.store_job( - self.opts, load, event=self.event, mminion=self.mminion) + self.opts, load, event=self.event, mminion=self.mminion + ) except salt.exceptions.SaltCacheError: - log.error('Could not store job information for load: %s', load) + log.error("Could not store job information for load: %s", load) def _syndic_return(self, load): - ''' + """ Receive a syndic minion return and format it to look like returns from individual minions. :param dict load: The minion payload - ''' - loads = load.get('load') + """ + loads = load.get("load") if not isinstance(loads, list): loads = [load] # support old syndics not aggregating returns for load in loads: # Verify the load - if any(key not in load for key in ('return', 'jid', 'id')): + if any(key not in load for key in ("return", "jid", "id")): continue # if we have a load, save it - if load.get('load'): - fstr = '{0}.save_load'.format(self.opts['master_job_cache']) - self.mminion.returners[fstr](load['jid'], load['load']) + if load.get("load"): + fstr = "{0}.save_load".format(self.opts["master_job_cache"]) + self.mminion.returners[fstr](load["jid"], load["load"]) # Register the syndic - syndic_cache_path = os.path.join(self.opts['cachedir'], 'syndics', load['id']) + syndic_cache_path = os.path.join( + self.opts["cachedir"], "syndics", load["id"] + ) if not os.path.exists(syndic_cache_path): path_name = os.path.split(syndic_cache_path)[0] if not os.path.exists(path_name): os.makedirs(path_name) - with salt.utils.files.fopen(syndic_cache_path, 'w') as wfh: - wfh.write('') + with salt.utils.files.fopen(syndic_cache_path, "w") as wfh: + wfh.write("") # Format individual return loads - for key, item in six.iteritems(load['return']): - ret = {'jid': load['jid'], - 'id': key} + for key, item in six.iteritems(load["return"]): + ret = {"jid": load["jid"], "id": key} ret.update(item) - if 'master_id' in load: - ret['master_id'] = load['master_id'] - if 'fun' in load: - ret['fun'] = load['fun'] - if 'arg' in load: - ret['fun_args'] = load['arg'] - if 'out' in load: - ret['out'] = load['out'] - if 'sig' in load: - ret['sig'] = load['sig'] + if "master_id" in load: + ret["master_id"] = load["master_id"] + if "fun" in load: + ret["fun"] = load["fun"] + if "arg" in load: + ret["fun_args"] = load["arg"] + if "out" in load: + ret["out"] = load["out"] + if "sig" in load: + ret["sig"] = load["sig"] self._return(ret) def minion_runner(self, clear_load): - ''' + """ Execute a runner from a minion, return the runner's function data :param dict clear_load: The minion payload :rtype: dict :return: The runner function data - ''' - load = self.__verify_load(clear_load, ('fun', 'arg', 'id', 'tok')) + """ + load = self.__verify_load(clear_load, ("fun", "arg", "id", "tok")) if load is False: return {} else: return self.masterapi.minion_runner(clear_load) def pub_ret(self, load): - ''' + """ Request the return data from a specific jid, only allowed if the requesting minion also initialted the execution. @@ -1708,25 +1783,23 @@ class AESFuncs(object): :rtype: dict :return: Return data corresponding to a given JID - ''' - load = self.__verify_load(load, ('jid', 'id', 'tok')) + """ + load = self.__verify_load(load, ("jid", "id", "tok")) if load is False: return {} # Check that this minion can access this data - auth_cache = os.path.join( - self.opts['cachedir'], - 'publish_auth') + auth_cache = os.path.join(self.opts["cachedir"], "publish_auth") if not os.path.isdir(auth_cache): os.makedirs(auth_cache) - jid_fn = os.path.join(auth_cache, six.text_type(load['jid'])) - with salt.utils.files.fopen(jid_fn, 'r') as fp_: - if not load['id'] == fp_.read(): + jid_fn = os.path.join(auth_cache, six.text_type(load["jid"])) + with salt.utils.files.fopen(jid_fn, "r") as fp_: + if not load["id"] == fp_.read(): return {} # Grab the latest and return - return self.local.get_cache_returns(load['jid']) + return self.local.get_cache_returns(load["jid"]) def minion_pub(self, clear_load): - ''' + """ Publish a command initiated from a minion, this method executes minion restrictions so that the minion publication will only work if it is enabled in the config. @@ -1754,14 +1827,14 @@ class AESFuncs(object): execute commands from the test module. :param dict clear_load: The minion pay - ''' + """ if not self.__verify_minion_publish(clear_load): return {} else: return self.masterapi.minion_pub(clear_load) def minion_publish(self, clear_load): - ''' + """ Publish a command initiated from a minion, this method executes minion restrictions so that the minion publication will only work if it is enabled in the config. @@ -1789,14 +1862,14 @@ class AESFuncs(object): execute commands from the test module. :param dict clear_load: The minion payload - ''' + """ if not self.__verify_minion_publish(clear_load): return {} else: return self.masterapi.minion_publish(clear_load) def revoke_auth(self, load): - ''' + """ Allow a minion to request revocation of its own key :param dict load: The minion payload @@ -1806,13 +1879,14 @@ class AESFuncs(object): :rtype: bool :return: True if key was revoked, False if not - ''' - load = self.__verify_load(load, ('id', 'tok')) + """ + load = self.__verify_load(load, ("id", "tok")) - if not self.opts.get('allow_minion_key_revoke', False): + if not self.opts.get("allow_minion_key_revoke", False): log.warning( - 'Minion %s requested key revoke, but allow_minion_key_revoke ' - 'is set to False', load['id'] + "Minion %s requested key revoke, but allow_minion_key_revoke " + "is set to False", + load["id"], ) return load @@ -1822,52 +1896,53 @@ class AESFuncs(object): return self.masterapi.revoke_auth(load) def run_func(self, func, load): - ''' + """ Wrapper for running functions executed with AES encryption :param function func: The function to run :return: The result of the master function that was called - ''' + """ # Don't honor private functions - if func.startswith('__'): + if func.startswith("__"): # TODO: return some error? Seems odd to return {} - return {}, {'fun': 'send'} + return {}, {"fun": "send"} # Run the func if hasattr(self, func): try: start = time.time() ret = getattr(self, func)(load) log.trace( - 'Master function call %s took %s seconds', - func, time.time() - start + "Master function call %s took %s seconds", func, time.time() - start ) except Exception: # pylint: disable=broad-except - ret = '' - log.error('Error in function %s:\n', func, exc_info=True) + ret = "" + log.error("Error in function %s:\n", func, exc_info=True) else: log.error( - 'Received function %s which is unavailable on the master, ' - 'returning False', func + "Received function %s which is unavailable on the master, " + "returning False", + func, ) - return False, {'fun': 'send'} + return False, {"fun": "send"} # Don't encrypt the return value for the _return func # (we don't care about the return value, so why encrypt it?) - if func == '_return': - return ret, {'fun': 'send'} - if func == '_pillar' and 'id' in load: - if load.get('ver') != '2' and self.opts['pillar_version'] == 1: + if func == "_return": + return ret, {"fun": "send"} + if func == "_pillar" and "id" in load: + if load.get("ver") != "2" and self.opts["pillar_version"] == 1: # Authorized to return old pillar proto - return ret, {'fun': 'send'} - return ret, {'fun': 'send_private', 'key': 'pillar', 'tgt': load['id']} + return ret, {"fun": "send"} + return ret, {"fun": "send_private", "key": "pillar", "tgt": load["id"]} # Encrypt the return - return ret, {'fun': 'send'} + return ret, {"fun": "send"} class ClearFuncs(object): - ''' + """ Set up functions that are safe to execute when commands sent to the master without encryption and authentication - ''' + """ + # The ClearFuncs object encapsulates the functions that can be executed in # the clear: # publish (The publish from the LocalClient) @@ -1876,19 +1951,18 @@ class ClearFuncs(object): self.opts = opts self.key = key # Create the event manager - self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) + self.event = salt.utils.event.get_master_event( + self.opts, self.opts["sock_dir"], listen=False + ) # Make a client - self.local = salt.client.get_local_client(self.opts['conf_file']) + self.local = salt.client.get_local_client(self.opts["conf_file"]) # Make an minion checker object self.ckminions = salt.utils.minions.CkMinions(opts) # Make an Auth object self.loadauth = salt.auth.LoadAuth(opts) # Stand up the master Minion to access returner data self.mminion = salt.minion.MasterMinion( - self.opts, - states=False, - rend=False, - ignore_config_errors=True + self.opts, states=False, rend=False, ignore_config_errors=True ) # Make a wheel object self.wheel_ = salt.wheel.Wheel(opts) @@ -1896,33 +1970,37 @@ class ClearFuncs(object): self.masterapi = salt.daemons.masterapi.LocalFuncs(opts, key) def runner(self, clear_load): - ''' + """ Send a master control function back to the runner system - ''' + """ # All runner ops pass through eauth auth_type, err_name, key, sensitive_load_keys = self._prep_auth_info(clear_load) # Authenticate auth_check = self.loadauth.check_authentication(clear_load, auth_type, key=key) - error = auth_check.get('error') + error = auth_check.get("error") if error: # Authentication error occurred: do not continue. - return {'error': error} + return {"error": error} # Authorize - username = auth_check.get('username') - if auth_type != 'user': + username = auth_check.get("username") + if auth_type != "user": runner_check = self.ckminions.runner_check( - auth_check.get('auth_list', []), - clear_load['fun'], - clear_load.get('kwarg', {}) + auth_check.get("auth_list", []), + clear_load["fun"], + clear_load.get("kwarg", {}), ) if not runner_check: - return {'error': {'name': err_name, - 'message': 'Authentication failure of type "{0}" occurred for ' - 'user {1}.'.format(auth_type, username)}} - elif isinstance(runner_check, dict) and 'error' in runner_check: + return { + "error": { + "name": err_name, + "message": 'Authentication failure of type "{0}" occurred for ' + "user {1}.".format(auth_type, username), + } + } + elif isinstance(runner_check, dict) and "error" in runner_check: # A dictionary with an error name/message was handled by ckminions.runner_check return runner_check @@ -1930,54 +2008,62 @@ class ClearFuncs(object): for item in sensitive_load_keys: clear_load.pop(item, None) else: - if 'user' in clear_load: - username = clear_load['user'] + if "user" in clear_load: + username = clear_load["user"] if salt.auth.AuthUser(username).is_sudo(): - username = self.opts.get('user', 'root') + username = self.opts.get("user", "root") else: username = salt.utils.user.get_user() # Authorized. Do the job! try: - fun = clear_load.pop('fun') + fun = clear_load.pop("fun") runner_client = salt.runner.RunnerClient(self.opts) - return runner_client.asynchronous(fun, - clear_load.get('kwarg', {}), - username) + return runner_client.asynchronous( + fun, clear_load.get("kwarg", {}), username + ) except Exception as exc: # pylint: disable=broad-except - log.error('Exception occurred while introspecting %s: %s', fun, exc) - return {'error': {'name': exc.__class__.__name__, - 'args': exc.args, - 'message': six.text_type(exc)}} + log.error("Exception occurred while introspecting %s: %s", fun, exc) + return { + "error": { + "name": exc.__class__.__name__, + "args": exc.args, + "message": six.text_type(exc), + } + } def wheel(self, clear_load): - ''' + """ Send a master control function back to the wheel system - ''' + """ # All wheel ops pass through eauth auth_type, err_name, key, sensitive_load_keys = self._prep_auth_info(clear_load) # Authenticate auth_check = self.loadauth.check_authentication(clear_load, auth_type, key=key) - error = auth_check.get('error') + error = auth_check.get("error") if error: # Authentication error occurred: do not continue. - return {'error': error} + return {"error": error} # Authorize - username = auth_check.get('username') - if auth_type != 'user': + username = auth_check.get("username") + if auth_type != "user": wheel_check = self.ckminions.wheel_check( - auth_check.get('auth_list', []), - clear_load['fun'], - clear_load.get('kwarg', {}) + auth_check.get("auth_list", []), + clear_load["fun"], + clear_load.get("kwarg", {}), ) if not wheel_check: - return {'error': {'name': err_name, - 'message': 'Authentication failure of type "{0}" occurred for ' - 'user {1}.'.format(auth_type, username)}} - elif isinstance(wheel_check, dict) and 'error' in wheel_check: + return { + "error": { + "name": err_name, + "message": 'Authentication failure of type "{0}" occurred for ' + "user {1}.".format(auth_type, username), + } + } + elif isinstance(wheel_check, dict) and "error" in wheel_check: # A dictionary with an error name/message was handled by ckminions.wheel_check return wheel_check @@ -1985,156 +2071,179 @@ class ClearFuncs(object): for item in sensitive_load_keys: clear_load.pop(item, None) else: - if 'user' in clear_load: - username = clear_load['user'] + if "user" in clear_load: + username = clear_load["user"] if salt.auth.AuthUser(username).is_sudo(): - username = self.opts.get('user', 'root') + username = self.opts.get("user", "root") else: username = salt.utils.user.get_user() # Authorized. Do the job! try: jid = salt.utils.jid.gen_jid(self.opts) - fun = clear_load.pop('fun') - tag = tagify(jid, prefix='wheel') - data = {'fun': "wheel.{0}".format(fun), - 'jid': jid, - 'tag': tag, - 'user': username} + fun = clear_load.pop("fun") + tag = tagify(jid, prefix="wheel") + data = { + "fun": "wheel.{0}".format(fun), + "jid": jid, + "tag": tag, + "user": username, + } - self.event.fire_event(data, tagify([jid, 'new'], 'wheel')) + self.event.fire_event(data, tagify([jid, "new"], "wheel")) ret = self.wheel_.call_func(fun, full_return=True, **clear_load) - data['return'] = ret['return'] - data['success'] = ret['success'] - self.event.fire_event(data, tagify([jid, 'ret'], 'wheel')) - return {'tag': tag, - 'data': data} + data["return"] = ret["return"] + data["success"] = ret["success"] + self.event.fire_event(data, tagify([jid, "ret"], "wheel")) + return {"tag": tag, "data": data} except Exception as exc: # pylint: disable=broad-except - log.error('Exception occurred while introspecting %s: %s', fun, exc) - data['return'] = 'Exception occurred in wheel {0}: {1}: {2}'.format( - fun, - exc.__class__.__name__, - exc, + log.error("Exception occurred while introspecting %s: %s", fun, exc) + data["return"] = "Exception occurred in wheel {0}: {1}: {2}".format( + fun, exc.__class__.__name__, exc, ) - data['success'] = False - self.event.fire_event(data, tagify([jid, 'ret'], 'wheel')) - return {'tag': tag, - 'data': data} + data["success"] = False + self.event.fire_event(data, tagify([jid, "ret"], "wheel")) + return {"tag": tag, "data": data} def mk_token(self, clear_load): - ''' + """ Create and return an authentication token, the clear load needs to contain the eauth key and the needed authentication creds. - ''' + """ token = self.loadauth.mk_token(clear_load) if not token: log.warning('Authentication failure of type "eauth" occurred.') - return '' + return "" return token def get_token(self, clear_load): - ''' + """ Return the name associated with a token or False if the token is invalid - ''' - if 'token' not in clear_load: + """ + if "token" not in clear_load: return False - return self.loadauth.get_tok(clear_load['token']) + return self.loadauth.get_tok(clear_load["token"]) def publish(self, clear_load): - ''' + """ This method sends out publications to the minions, it can only be used by the LocalClient. - ''' - extra = clear_load.get('kwargs', {}) + """ + extra = clear_load.get("kwargs", {}) - publisher_acl = salt.acl.PublisherACL(self.opts['publisher_acl_blacklist']) + publisher_acl = salt.acl.PublisherACL(self.opts["publisher_acl_blacklist"]) - if publisher_acl.user_is_blacklisted(clear_load['user']) or \ - publisher_acl.cmd_is_blacklisted(clear_load['fun']): + if publisher_acl.user_is_blacklisted( + clear_load["user"] + ) or publisher_acl.cmd_is_blacklisted(clear_load["fun"]): log.error( - '%s does not have permissions to run %s. Please contact ' - 'your local administrator if you believe this is in ' - 'error.\n', clear_load['user'], clear_load['fun'] + "%s does not have permissions to run %s. Please contact " + "your local administrator if you believe this is in " + "error.\n", + clear_load["user"], + clear_load["fun"], ) - return {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} + return { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } # Retrieve the minions list - delimiter = clear_load.get('kwargs', {}).get('delimiter', DEFAULT_TARGET_DELIM) + delimiter = clear_load.get("kwargs", {}).get("delimiter", DEFAULT_TARGET_DELIM) _res = self.ckminions.check_minions( - clear_load['tgt'], - clear_load.get('tgt_type', 'glob'), - delimiter + clear_load["tgt"], clear_load.get("tgt_type", "glob"), delimiter ) - minions = _res.get('minions', list()) - missing = _res.get('missing', list()) - ssh_minions = _res.get('ssh_minions', False) + minions = _res.get("minions", list()) + missing = _res.get("missing", list()) + ssh_minions = _res.get("ssh_minions", False) # Check for external auth calls and authenticate auth_type, err_name, key, sensitive_load_keys = self._prep_auth_info(extra) - if auth_type == 'user': - auth_check = self.loadauth.check_authentication(clear_load, auth_type, key=key) + if auth_type == "user": + auth_check = self.loadauth.check_authentication( + clear_load, auth_type, key=key + ) else: auth_check = self.loadauth.check_authentication(extra, auth_type) # Setup authorization list variable and error information - auth_list = auth_check.get('auth_list', []) + auth_list = auth_check.get("auth_list", []) err_msg = 'Authentication failure of type "{0}" occurred.'.format(auth_type) - if auth_check.get('error'): + if auth_check.get("error"): # Authentication error occurred: do not continue. log.warning(err_msg) - return {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} + return { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } # All Token, Eauth, and non-root users must pass the authorization check - if auth_type != 'user' or (auth_type == 'user' and auth_list): + if auth_type != "user" or (auth_type == "user" and auth_list): # Authorize the request authorized = self.ckminions.auth_check( auth_list, - clear_load['fun'], - clear_load['arg'], - clear_load['tgt'], - clear_load.get('tgt_type', 'glob'), + clear_load["fun"], + clear_load["arg"], + clear_load["tgt"], + clear_load.get("tgt_type", "glob"), minions=minions, # always accept find_job - whitelist=['saltutil.find_job'], + whitelist=["saltutil.find_job"], ) if not authorized: # Authorization error occurred. Do not continue. - if auth_type == 'eauth' and not auth_list and 'username' in extra and 'eauth' in extra: - log.debug('Auth configuration for eauth "%s" and user "%s" is empty', extra['eauth'], extra['username']) + if ( + auth_type == "eauth" + and not auth_list + and "username" in extra + and "eauth" in extra + ): + log.debug( + 'Auth configuration for eauth "%s" and user "%s" is empty', + extra["eauth"], + extra["username"], + ) log.warning(err_msg) - return {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} + return { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } # Perform some specific auth_type tasks after the authorization check - if auth_type == 'token': - username = auth_check.get('username') - clear_load['user'] = username + if auth_type == "token": + username = auth_check.get("username") + clear_load["user"] = username log.debug('Minion tokenized user = "%s"', username) - elif auth_type == 'eauth': + elif auth_type == "eauth": # The username we are attempting to auth with - clear_load['user'] = self.loadauth.load_name(extra) + clear_load["user"] = self.loadauth.load_name(extra) # If we order masters (via a syndic), don't short circuit if no minions # are found - if not self.opts.get('order_masters'): + if not self.opts.get("order_masters"): # Check for no minions if not minions: return { - 'enc': 'clear', - 'load': { - 'jid': None, - 'minions': minions, - 'error': 'Master could not resolve minions for target {0}'.format(clear_load['tgt']) - } + "enc": "clear", + "load": { + "jid": None, + "minions": minions, + "error": "Master could not resolve minions for target {0}".format( + clear_load["tgt"] + ), + }, } jid = self._prep_jid(clear_load, extra) if jid is None: - return {'enc': 'clear', - 'load': {'error': 'Master failed to assign jid'}} + return {"enc": "clear", "load": {"error": "Master failed to assign jid"}} payload = self._prep_pub(minions, jid, clear_load, extra, missing) # Send it! @@ -2142,156 +2251,152 @@ class ClearFuncs(object): self._send_pub(payload) return { - 'enc': 'clear', - 'load': { - 'jid': clear_load['jid'], - 'minions': minions, - 'missing': missing - } + "enc": "clear", + "load": {"jid": clear_load["jid"], "minions": minions, "missing": missing}, } def _prep_auth_info(self, clear_load): sensitive_load_keys = [] key = None - if 'token' in clear_load: - auth_type = 'token' - err_name = 'TokenAuthenticationError' - sensitive_load_keys = ['token'] - elif 'eauth' in clear_load: - auth_type = 'eauth' - err_name = 'EauthAuthenticationError' - sensitive_load_keys = ['username', 'password'] + if "token" in clear_load: + auth_type = "token" + err_name = "TokenAuthenticationError" + sensitive_load_keys = ["token"] + elif "eauth" in clear_load: + auth_type = "eauth" + err_name = "EauthAuthenticationError" + sensitive_load_keys = ["username", "password"] else: - auth_type = 'user' - err_name = 'UserAuthenticationError' + auth_type = "user" + err_name = "UserAuthenticationError" key = self.key return auth_type, err_name, key, sensitive_load_keys def _prep_jid(self, clear_load, extra): - ''' + """ Return a jid for this publication - ''' + """ # the jid in clear_load can be None, '', or something else. this is an # attempt to clean up the value before passing to plugins - passed_jid = clear_load['jid'] if clear_load.get('jid') else None - nocache = extra.get('nocache', False) + passed_jid = clear_load["jid"] if clear_load.get("jid") else None + nocache = extra.get("nocache", False) # Retrieve the jid - fstr = '{0}.prep_jid'.format(self.opts['master_job_cache']) + fstr = "{0}.prep_jid".format(self.opts["master_job_cache"]) try: # Retrieve the jid - jid = self.mminion.returners[fstr](nocache=nocache, - passed_jid=passed_jid) + jid = self.mminion.returners[fstr](nocache=nocache, passed_jid=passed_jid) except (KeyError, TypeError): # The returner is not present msg = ( - 'Failed to allocate a jid. The requested returner \'{0}\' ' - 'could not be loaded.'.format(fstr.split('.')[0]) + "Failed to allocate a jid. The requested returner '{0}' " + "could not be loaded.".format(fstr.split(".")[0]) ) log.error(msg) - return {'error': msg} + return {"error": msg} return jid def _send_pub(self, load): - ''' + """ Take a load and send it across the network to connected minions - ''' + """ for transport, opts in iter_transport_opts(self.opts): chan = salt.transport.server.PubServerChannel.factory(opts) chan.publish(load) @property def ssh_client(self): - if not hasattr(self, '_ssh_client'): + if not hasattr(self, "_ssh_client"): self._ssh_client = salt.client.ssh.client.SSHClient(mopts=self.opts) return self._ssh_client def _send_ssh_pub(self, load, ssh_minions=False): - ''' + """ Take a load and send it across the network to ssh minions - ''' - if self.opts['enable_ssh_minions'] is True and ssh_minions is True: - log.debug('Send payload to ssh minions') + """ + if self.opts["enable_ssh_minions"] is True and ssh_minions is True: + log.debug("Send payload to ssh minions") threading.Thread(target=self.ssh_client.cmd, kwargs=load).start() def _prep_pub(self, minions, jid, clear_load, extra, missing): - ''' + """ Take a given load and perform the necessary steps to prepare a publication. TODO: This is really only bound by temporal cohesion and thus should be refactored even further. - ''' - clear_load['jid'] = jid - delimiter = clear_load.get('kwargs', {}).get('delimiter', DEFAULT_TARGET_DELIM) + """ + clear_load["jid"] = jid + delimiter = clear_load.get("kwargs", {}).get("delimiter", DEFAULT_TARGET_DELIM) # TODO Error reporting over the master event bus - self.event.fire_event({'minions': minions}, clear_load['jid']) + self.event.fire_event({"minions": minions}, clear_load["jid"]) new_job_load = { - 'jid': clear_load['jid'], - 'tgt_type': clear_load['tgt_type'], - 'tgt': clear_load['tgt'], - 'user': clear_load['user'], - 'fun': clear_load['fun'], - 'arg': clear_load['arg'], - 'minions': minions, - 'missing': missing, - } + "jid": clear_load["jid"], + "tgt_type": clear_load["tgt_type"], + "tgt": clear_load["tgt"], + "user": clear_load["user"], + "fun": clear_load["fun"], + "arg": clear_load["arg"], + "minions": minions, + "missing": missing, + } # Announce the job on the event bus - self.event.fire_event(new_job_load, tagify([clear_load['jid'], 'new'], 'job')) + self.event.fire_event(new_job_load, tagify([clear_load["jid"], "new"], "job")) - if self.opts['ext_job_cache']: - fstr = '{0}.save_load'.format(self.opts['ext_job_cache']) + if self.opts["ext_job_cache"]: + fstr = "{0}.save_load".format(self.opts["ext_job_cache"]) save_load_func = True # Get the returner's save_load arg_spec. try: - arg_spec = salt.utils.args.get_function_argspec(self.mminion.returners[fstr]) + arg_spec = salt.utils.args.get_function_argspec( + self.mminion.returners[fstr] + ) # Check if 'minions' is included in returner's save_load arg_spec. # This may be missing in custom returners, which we should warn about. - if 'minions' not in arg_spec.args: + if "minions" not in arg_spec.args: log.critical( - 'The specified returner used for the external job cache ' - '\'%s\' does not have a \'minions\' kwarg in the returner\'s ' - 'save_load function.', self.opts['ext_job_cache'] + "The specified returner used for the external job cache " + "'%s' does not have a 'minions' kwarg in the returner's " + "save_load function.", + self.opts["ext_job_cache"], ) except (AttributeError, KeyError): save_load_func = False log.critical( - 'The specified returner used for the external job cache ' + "The specified returner used for the external job cache " '"%s" does not have a save_load function!', - self.opts['ext_job_cache'] + self.opts["ext_job_cache"], ) if save_load_func: try: - self.mminion.returners[fstr](clear_load['jid'], clear_load, minions=minions) + self.mminion.returners[fstr]( + clear_load["jid"], clear_load, minions=minions + ) except Exception: # pylint: disable=broad-except log.critical( - 'The specified returner threw a stack trace:\n', - exc_info=True + "The specified returner threw a stack trace:\n", exc_info=True ) # always write out to the master job caches try: - fstr = '{0}.save_load'.format(self.opts['master_job_cache']) - self.mminion.returners[fstr](clear_load['jid'], clear_load, minions) + fstr = "{0}.save_load".format(self.opts["master_job_cache"]) + self.mminion.returners[fstr](clear_load["jid"], clear_load, minions) except KeyError: log.critical( - 'The specified returner used for the master job cache ' + "The specified returner used for the master job cache " '"%s" does not have a save_load function!', - self.opts['master_job_cache'] + self.opts["master_job_cache"], ) except Exception: # pylint: disable=broad-except - log.critical( - 'The specified returner threw a stack trace:\n', - exc_info=True - ) + log.critical("The specified returner threw a stack trace:\n", exc_info=True) # Set up the payload - payload = {'enc': 'aes'} + payload = {"enc": "aes"} # Altering the contents of the publish load is serious!! Changes here # break compatibility with minion/master versions and even tiny # additions can have serious implications on the performance of the @@ -2301,61 +2406,62 @@ class ClearFuncs(object): # touching this stuff, we can probably do what you want to do another # way that won't have a negative impact. load = { - 'fun': clear_load['fun'], - 'arg': clear_load['arg'], - 'tgt': clear_load['tgt'], - 'jid': clear_load['jid'], - 'ret': clear_load['ret'], + "fun": clear_load["fun"], + "arg": clear_load["arg"], + "tgt": clear_load["tgt"], + "jid": clear_load["jid"], + "ret": clear_load["ret"], } # if you specified a master id, lets put that in the load - if 'master_id' in self.opts: - load['master_id'] = self.opts['master_id'] + if "master_id" in self.opts: + load["master_id"] = self.opts["master_id"] # if someone passed us one, use that - if 'master_id' in extra: - load['master_id'] = extra['master_id'] + if "master_id" in extra: + load["master_id"] = extra["master_id"] # Only add the delimiter to the pub data if it is non-default if delimiter != DEFAULT_TARGET_DELIM: - load['delimiter'] = delimiter + load["delimiter"] = delimiter - if 'id' in extra: - load['id'] = extra['id'] - if 'tgt_type' in clear_load: - load['tgt_type'] = clear_load['tgt_type'] - if 'to' in clear_load: - load['to'] = clear_load['to'] + if "id" in extra: + load["id"] = extra["id"] + if "tgt_type" in clear_load: + load["tgt_type"] = clear_load["tgt_type"] + if "to" in clear_load: + load["to"] = clear_load["to"] - if 'kwargs' in clear_load: - if 'ret_config' in clear_load['kwargs']: - load['ret_config'] = clear_load['kwargs'].get('ret_config') + if "kwargs" in clear_load: + if "ret_config" in clear_load["kwargs"]: + load["ret_config"] = clear_load["kwargs"].get("ret_config") - if 'metadata' in clear_load['kwargs']: - load['metadata'] = clear_load['kwargs'].get('metadata') + if "metadata" in clear_load["kwargs"]: + load["metadata"] = clear_load["kwargs"].get("metadata") - if 'module_executors' in clear_load['kwargs']: - load['module_executors'] = clear_load['kwargs'].get('module_executors') + if "module_executors" in clear_load["kwargs"]: + load["module_executors"] = clear_load["kwargs"].get("module_executors") - if 'executor_opts' in clear_load['kwargs']: - load['executor_opts'] = clear_load['kwargs'].get('executor_opts') + if "executor_opts" in clear_load["kwargs"]: + load["executor_opts"] = clear_load["kwargs"].get("executor_opts") - if 'ret_kwargs' in clear_load['kwargs']: - load['ret_kwargs'] = clear_load['kwargs'].get('ret_kwargs') + if "ret_kwargs" in clear_load["kwargs"]: + load["ret_kwargs"] = clear_load["kwargs"].get("ret_kwargs") - if 'user' in clear_load: + if "user" in clear_load: log.info( - 'User %s Published command %s with jid %s', - clear_load['user'], clear_load['fun'], clear_load['jid'] + "User %s Published command %s with jid %s", + clear_load["user"], + clear_load["fun"], + clear_load["jid"], ) - load['user'] = clear_load['user'] + load["user"] = clear_load["user"] else: log.info( - 'Published command %s with jid %s', - clear_load['fun'], clear_load['jid'] + "Published command %s with jid %s", clear_load["fun"], clear_load["jid"] ) - log.debug('Published command details %s', load) + log.debug("Published command details %s", load) return load def ping(self, clear_load): - ''' + """ Send the load back to the sender. - ''' + """ return clear_load diff --git a/salt/matchers/__init__.py b/salt/matchers/__init__.py index 22f81456180..01bff648670 100644 --- a/salt/matchers/__init__.py +++ b/salt/matchers/__init__.py @@ -1,43 +1,45 @@ # -*- coding: utf-8 -*- -''' +""" Salt package -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import warnings # All salt related deprecation warnings should be shown once each! warnings.filterwarnings( - 'once', # Show once - '', # No deprecation message match + "once", # Show once + "", # No deprecation message match DeprecationWarning, # This filter is for DeprecationWarnings - r'^(salt|salt\.(.*))$' # Match module(s) 'salt' and 'salt.' + r"^(salt|salt\.(.*))$", # Match module(s) 'salt' and 'salt.' ) # While we are supporting Python2.6, hide nested with-statements warnings warnings.filterwarnings( - 'ignore', - 'With-statements now directly support multiple context managers', - DeprecationWarning + "ignore", + "With-statements now directly support multiple context managers", + DeprecationWarning, ) # Filter the backports package UserWarning about being re-imported warnings.filterwarnings( - 'ignore', - '^Module backports was already imported from (.*), but (.*) is being added to sys.path$', - UserWarning + "ignore", + "^Module backports was already imported from (.*), but (.*) is being added to sys.path$", + UserWarning, ) def __define_global_system_encoding_variable__(): import sys + # This is the most trustworthy source of the system encoding, though, if # salt is being imported after being daemonized, this information is lost # and reset to None encoding = None - if not sys.platform.startswith('win') and sys.stdin is not None: + if not sys.platform.startswith("win") and sys.stdin is not None: # On linux we can rely on sys.stdin for the encoding since it # most commonly matches the filesystem encoding. This however # does not apply to windows @@ -48,6 +50,7 @@ def __define_global_system_encoding_variable__(): # encoding. MS Windows has problems with this and reports the wrong # encoding import locale + try: encoding = locale.getdefaultlocale()[-1] except ValueError: @@ -63,16 +66,16 @@ def __define_global_system_encoding_variable__(): # the way back to ascii encoding = sys.getdefaultencoding() if not encoding: - if sys.platform.startswith('darwin'): + if sys.platform.startswith("darwin"): # Mac OS X uses UTF-8 - encoding = 'utf-8' - elif sys.platform.startswith('win'): + encoding = "utf-8" + elif sys.platform.startswith("win"): # Windows uses a configurable encoding; on Windows, Python uses the name “mbcs” # to refer to whatever the currently configured encoding is. - encoding = 'mbcs' + encoding = "mbcs" else: # On linux default to ascii as a last resort - encoding = 'ascii' + encoding = "ascii" # We can't use six.moves.builtins because these builtins get deleted sooner # than expected. See: @@ -83,7 +86,7 @@ def __define_global_system_encoding_variable__(): import builtins # pylint: disable=import-error # Define the detected encoding as a built-in variable for ease of use - setattr(builtins, '__salt_system_encoding__', encoding) + setattr(builtins, "__salt_system_encoding__", encoding) # This is now garbage collectable del sys diff --git a/salt/matchers/cache_match.py b/salt/matchers/cache_match.py index d34c2a2670a..3ad0bff4007 100644 --- a/salt/matchers/cache_match.py +++ b/salt/matchers/cache_match.py @@ -1,34 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" This is the default cache matcher function. It only exists for the master, this is why there is only a ``mmatch()`` but not ``match()``. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging -import salt.utils.data # pylint: disable=3rd-party-module-not-gated +import salt.utils.data # pylint: disable=3rd-party-module-not-gated import salt.utils.minions # pylint: disable=3rd-party-module-not-gated log = logging.getLogger(__name__) -def mmatch(expr, - delimiter, - greedy, - search_type, - regex_match=False, - exact_match=False, - opts=None): - ''' +def mmatch( + expr, + delimiter, + greedy, + search_type, + regex_match=False, + exact_match=False, + opts=None, +): + """ Helper function to search for minions in master caches If 'greedy' return accepted minions that matched by the condition or absent in the cache. If not 'greedy' return the only minions have cache data and matched by the condition. - ''' + """ if not opts: opts = __opts__ ckminions = salt.utils.minions.CkMinions(opts) - return ckminions._check_cache_minions(expr, delimiter, greedy, - search_type, regex_match=regex_match, - exact_match=exact_match) + return ckminions._check_cache_minions( + expr, + delimiter, + greedy, + search_type, + regex_match=regex_match, + exact_match=exact_match, + ) diff --git a/salt/matchers/compound_match.py b/salt/matchers/compound_match.py index 678844f194a..fabc7ec4cdd 100644 --- a/salt/matchers/compound_match.py +++ b/salt/matchers/compound_match.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" This is the default compound matcher function. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging -from salt.ext import six # pylint: disable=3rd-party-module-not-gated + import salt.loader import salt.utils.minions # pylint: disable=3rd-party-module-not-gated +from salt.ext import six # pylint: disable=3rd-party-module-not-gated HAS_RANGE = False try: import seco.range # pylint: disable=unused-import + HAS_RANGE = True except ImportError: pass @@ -20,32 +22,34 @@ log = logging.getLogger(__name__) def match(tgt, opts=None): - ''' + """ Runs the compound target check - ''' + """ if not opts: opts = __opts__ - nodegroups = opts.get('nodegroups', {}) + nodegroups = opts.get("nodegroups", {}) matchers = salt.loader.matchers(opts) - minion_id = opts.get('minion_id', opts['id']) + minion_id = opts.get("minion_id", opts["id"]) if not isinstance(tgt, six.string_types) and not isinstance(tgt, (list, tuple)): - log.error('Compound target received that is neither string, list nor tuple') + log.error("Compound target received that is neither string, list nor tuple") return False - log.debug('compound_match: %s ? %s', minion_id, tgt) - ref = {'G': 'grain', - 'P': 'grain_pcre', - 'I': 'pillar', - 'J': 'pillar_pcre', - 'L': 'list', - 'N': None, # Nodegroups should already be expanded - 'S': 'ipcidr', - 'E': 'pcre'} + log.debug("compound_match: %s ? %s", minion_id, tgt) + ref = { + "G": "grain", + "P": "grain_pcre", + "I": "pillar", + "J": "pillar_pcre", + "L": "list", + "N": None, # Nodegroups should already be expanded + "S": "ipcidr", + "E": "pcre", + } if HAS_RANGE: - ref['R'] = 'range' + ref["R"] = "range" results = [] - opers = ['and', 'or', 'not', '(', ')'] + opers = ["and", "or", "not", "(", ")"] if isinstance(tgt, six.string_types): words = tgt.split() @@ -60,56 +64,62 @@ def match(tgt, opts=None): # Easy check first if word in opers: if results: - if results[-1] == '(' and word in ('and', 'or'): + if results[-1] == "(" and word in ("and", "or"): log.error('Invalid beginning operator after "(": %s', word) return False - if word == 'not': - if not results[-1] in ('and', 'or', '('): - results.append('and') + if word == "not": + if not results[-1] in ("and", "or", "("): + results.append("and") results.append(word) else: # seq start with binary oper, fail - if word not in ['(', 'not']: - log.error('Invalid beginning operator: %s', word) + if word not in ["(", "not"]: + log.error("Invalid beginning operator: %s", word) return False results.append(word) - elif target_info and target_info['engine']: - if 'N' == target_info['engine']: + elif target_info and target_info["engine"]: + if "N" == target_info["engine"]: # if we encounter a node group, just evaluate it in-place - decomposed = salt.utils.minions.nodegroup_comp(target_info['pattern'], nodegroups) + decomposed = salt.utils.minions.nodegroup_comp( + target_info["pattern"], nodegroups + ) if decomposed: words = decomposed + words continue - engine = ref.get(target_info['engine']) + engine = ref.get(target_info["engine"]) if not engine: # If an unknown engine is called at any time, fail out log.error( - 'Unrecognized target engine "%s" for target ' - 'expression "%s"', target_info['engine'], word + 'Unrecognized target engine "%s" for target ' 'expression "%s"', + target_info["engine"], + word, ) return False - engine_args = [target_info['pattern']] - engine_kwargs = {'opts': opts} - if target_info['delimiter']: - engine_kwargs['delimiter'] = target_info['delimiter'] + engine_args = [target_info["pattern"]] + engine_kwargs = {"opts": opts} + if target_info["delimiter"]: + engine_kwargs["delimiter"] = target_info["delimiter"] results.append( - six.text_type(matchers['{0}_match.match'.format(engine)](*engine_args, **engine_kwargs)) + six.text_type( + matchers["{0}_match.match".format(engine)]( + *engine_args, **engine_kwargs + ) + ) ) else: # The match is not explicitly defined, evaluate it as a glob - results.append(six.text_type(matchers['glob_match.match'](word, opts))) + results.append(six.text_type(matchers["glob_match.match"](word, opts))) - results = ' '.join(results) + results = " ".join(results) log.debug('compound_match %s ? "%s" => "%s"', minion_id, tgt, results) try: return eval(results) # pylint: disable=W0123 except Exception: # pylint: disable=broad-except - log.error( - 'Invalid compound target: %s for results: %s', tgt, results) + log.error("Invalid compound target: %s for results: %s", tgt, results) return False return False diff --git a/salt/matchers/compound_pillar_exact_match.py b/salt/matchers/compound_pillar_exact_match.py index 21346e804cd..1d9112671ab 100644 --- a/salt/matchers/compound_pillar_exact_match.py +++ b/salt/matchers/compound_pillar_exact_match.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" This is the default pillar exact matcher for compound matches. There is no minion-side equivalent for this, so consequently there is no ``match()`` function below, only an ``mmatch()`` -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging @@ -15,12 +15,11 @@ log = logging.getLogger(__name__) def mmatch(expr, delimiter, greedy, opts=None): - ''' + """ Return the minions found by looking via pillar - ''' + """ if not opts: opts = __opts__ ckminions = salt.utils.minions.CkMinions(opts) - return ckminions._check_compound_minions(expr, delimiter, greedy, - pillar_exact=True) + return ckminions._check_compound_minions(expr, delimiter, greedy, pillar_exact=True) diff --git a/salt/matchers/confirm_top.py b/salt/matchers/confirm_top.py index dfca546a11b..05581d8df98 100644 --- a/salt/matchers/confirm_top.py +++ b/salt/matchers/confirm_top.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" The matcher subsystem needs a function called 'confirm_top', which takes the data passed to a top file environment and determines if that data matches this minion. -''' +""" from __future__ import absolute_import + import logging import salt.loader @@ -13,23 +14,22 @@ log = logging.getLogger(__file__) def confirm_top(match, data, nodegroups=None): - ''' + """ Takes the data passed to a top file environment and determines if the data matches this minion - ''' - matcher = 'compound' + """ + matcher = "compound" if not data: - log.error('Received bad data when setting the match from the top ' - 'file') + log.error("Received bad data when setting the match from the top " "file") return False for item in data: if isinstance(item, dict): - if 'match' in item: - matcher = item['match'] + if "match" in item: + matcher = item["match"] matchers = salt.loader.matchers(__opts__) - funcname = matcher + '_match.match' - if matcher == 'nodegroup': + funcname = matcher + "_match.match" + if matcher == "nodegroup": return matchers[funcname](match, nodegroups) else: m = matchers[funcname] diff --git a/salt/matchers/data_match.py b/salt/matchers/data_match.py index 3109627a6dd..20b10b9aa25 100644 --- a/salt/matchers/data_match.py +++ b/salt/matchers/data_match.py @@ -1,34 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" This is the default data matcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import fnmatch import logging -from salt.ext import six # pylint: disable=3rd-party-module-not-gated +import salt.loader # pylint: disable=3rd-party-module-not-gated import salt.utils.data # pylint: disable=3rd-party-module-not-gated import salt.utils.minions # pylint: disable=3rd-party-module-not-gated import salt.utils.network # pylint: disable=3rd-party-module-not-gated -import salt.loader # pylint: disable=3rd-party-module-not-gated +from salt.ext import six # pylint: disable=3rd-party-module-not-gated log = logging.getLogger(__name__) def match(tgt, functions=None, opts=None): - ''' + """ Match based on the local data store on the minion - ''' + """ if not opts: opts = __opts__ if functions is None: utils = salt.loader.utils(opts) functions = salt.loader.minion_mods(opts, utils=utils) - comps = tgt.split(':') + comps = tgt.split(":") if len(comps) < 2: return False - val = functions['data.getval'](comps[0]) + val = functions["data.getval"](comps[0]) if val is None: # The value is not defined return False @@ -42,7 +42,4 @@ def match(tgt, functions=None, opts=None): if comps[1] in val: return True return False - return bool(fnmatch.fnmatch( - val, - comps[1], - )) + return bool(fnmatch.fnmatch(val, comps[1],)) diff --git a/salt/matchers/glob_match.py b/salt/matchers/glob_match.py index 1934585d68c..88740289693 100644 --- a/salt/matchers/glob_match.py +++ b/salt/matchers/glob_match.py @@ -1,20 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" This is the default glob matcher function. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import fnmatch + from salt.ext import six # pylint: disable=3rd-party-module-not-gated def match(tgt, opts=None): - ''' + """ Returns true if the passed glob matches the id - ''' + """ if not opts: opts = __opts__ - minion_id = opts.get('minion_id', opts['id']) + minion_id = opts.get("minion_id", opts["id"]) if not isinstance(tgt, six.string_types): return False diff --git a/salt/matchers/grain_match.py b/salt/matchers/grain_match.py index 5318023bc84..99a7c3ec44d 100644 --- a/salt/matchers/grain_match.py +++ b/salt/matchers/grain_match.py @@ -1,30 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" This is the default grains matcher function. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging -from salt.defaults import DEFAULT_TARGET_DELIM # pylint: disable=3rd-party-module-not-gated import salt.utils.data # pylint: disable=3rd-party-module-not-gated +from salt.defaults import ( # pylint: disable=3rd-party-module-not-gated + DEFAULT_TARGET_DELIM, +) log = logging.getLogger(__name__) def match(tgt, delimiter=DEFAULT_TARGET_DELIM, opts=None): - ''' + """ Reads in the grains glob match - ''' + """ if not opts: opts = __opts__ - log.debug('grains target: %s', tgt) + log.debug("grains target: %s", tgt) if delimiter not in tgt: - log.error('Got insufficient arguments for grains match ' - 'statement from master') + log.error( + "Got insufficient arguments for grains match " "statement from master" + ) return False - return salt.utils.data.subdict_match( - opts['grains'], tgt, delimiter=delimiter - ) + return salt.utils.data.subdict_match(opts["grains"], tgt, delimiter=delimiter) diff --git a/salt/matchers/grain_pcre_match.py b/salt/matchers/grain_pcre_match.py index b429f4e29b5..de8921859ad 100644 --- a/salt/matchers/grain_pcre_match.py +++ b/salt/matchers/grain_pcre_match.py @@ -1,28 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" This is the default grains PCRE matcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging -from salt.defaults import DEFAULT_TARGET_DELIM # pylint: disable=3rd-party-module-not-gated import salt.utils.data # pylint: disable=3rd-party-module-not-gated +from salt.defaults import ( # pylint: disable=3rd-party-module-not-gated + DEFAULT_TARGET_DELIM, +) log = logging.getLogger(__name__) def match(tgt, delimiter=DEFAULT_TARGET_DELIM, opts=None): - ''' + """ Matches a grain based on regex - ''' + """ if not opts: opts = __opts__ - log.debug('grains pcre target: %s', tgt) + log.debug("grains pcre target: %s", tgt) if delimiter not in tgt: - log.error('Got insufficient arguments for grains pcre match ' - 'statement from master') + log.error( + "Got insufficient arguments for grains pcre match " "statement from master" + ) return False return salt.utils.data.subdict_match( - opts['grains'], tgt, delimiter=delimiter, regex_match=True) + opts["grains"], tgt, delimiter=delimiter, regex_match=True + ) diff --git a/salt/matchers/ipcidr_match.py b/salt/matchers/ipcidr_match.py index 0c5d71e802d..ca0ac5a4077 100644 --- a/salt/matchers/ipcidr_match.py +++ b/salt/matchers/ipcidr_match.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" This is the default ipcidr matcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging -from salt.ext import six # pylint: disable=3rd-party-module-not-gated import salt.utils.network # pylint: disable=3rd-party-module-not-gated +from salt.ext import six # pylint: disable=3rd-party-module-not-gated if six.PY3: import ipaddress @@ -18,9 +18,9 @@ log = logging.getLogger(__name__) def match(tgt, opts=None): - ''' + """ Matches based on IP address or CIDR notation - ''' + """ if not opts: opts = __opts__ @@ -32,11 +32,11 @@ def match(tgt, opts=None): # Target is a network? tgt = ipaddress.ip_network(tgt) except: # pylint: disable=bare-except - log.error('Invalid IP/CIDR target: %s', tgt) + log.error("Invalid IP/CIDR target: %s", tgt) return [] - proto = 'ipv{0}'.format(tgt.version) + proto = "ipv{0}".format(tgt.version) - grains = opts['grains'] + grains = opts["grains"] if proto not in grains: match = False diff --git a/salt/matchers/list_match.py b/salt/matchers/list_match.py index 01d9ada71b2..b15f125ca5c 100644 --- a/salt/matchers/list_match.py +++ b/salt/matchers/list_match.py @@ -1,33 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" This is the default list matcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging log = logging.getLogger(__name__) def match(tgt, opts=None): - ''' + """ Determines if this host is on the list - ''' + """ if not opts: opts = __opts__ try: - if ',' + opts['id'] + ',' in tgt \ - or tgt.startswith(opts['id'] + ',') \ - or tgt.endswith(',' + opts['id']): + if ( + "," + opts["id"] + "," in tgt + or tgt.startswith(opts["id"] + ",") + or tgt.endswith("," + opts["id"]) + ): return True # tgt is a string, which we know because the if statement above did not # cause one of the exceptions being caught. Therefore, look for an # exact match. (e.g. salt -L foo test.ping) - return opts['id'] == tgt + return opts["id"] == tgt except (AttributeError, TypeError): # tgt is not a string, maybe it's a sequence type? try: - return opts['id'] in tgt + return opts["id"] in tgt except Exception: # pylint: disable=broad-except # tgt was likely some invalid type return False @@ -36,7 +39,8 @@ def match(tgt, opts=None): # above. If we do, it is because something above changed, and should be # considered as a bug. Log a warning to help us catch this. log.warning( - 'List matcher unexpectedly did not return, for target %s, ' - 'this is probably a bug.', tgt + "List matcher unexpectedly did not return, for target %s, " + "this is probably a bug.", + tgt, ) return False diff --git a/salt/matchers/nodegroup_match.py b/salt/matchers/nodegroup_match.py index 1e5fb04c220..146b1de23b9 100644 --- a/salt/matchers/nodegroup_match.py +++ b/salt/matchers/nodegroup_match.py @@ -1,30 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" This is the default nodegroup matcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import salt.utils.minions # pylint: disable=3rd-party-module-not-gated -import salt.loader import logging +import salt.loader +import salt.utils.minions # pylint: disable=3rd-party-module-not-gated + log = logging.getLogger(__name__) def match(tgt, nodegroups=None, opts=None): - ''' + """ This is a compatibility matcher and is NOT called when using nodegroups for remote execution, but is called when the nodegroups matcher is used in states - ''' + """ if not opts: opts = __opts__ if not nodegroups: - log.debug('Nodegroup matcher called with no nodegroups.') + log.debug("Nodegroup matcher called with no nodegroups.") return False if tgt in nodegroups: matchers = salt.loader.matchers(opts) - return matchers['compound_match.match']( + return matchers["compound_match.match"]( salt.utils.minions.nodegroup_comp(tgt, nodegroups) ) return False diff --git a/salt/matchers/pcre_match.py b/salt/matchers/pcre_match.py index 4133128d632..c94bcc607d8 100644 --- a/salt/matchers/pcre_match.py +++ b/salt/matchers/pcre_match.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" This is the default pcre matcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import re def match(tgt, opts=None): - ''' + """ Returns true if the passed pcre regex matches - ''' + """ if not opts: - return bool(re.match(tgt, __opts__['id'])) + return bool(re.match(tgt, __opts__["id"])) else: - return bool(re.match(tgt, opts['id'])) + return bool(re.match(tgt, opts["id"])) diff --git a/salt/matchers/pillar_exact_match.py b/salt/matchers/pillar_exact_match.py index 564f5c04ffe..ffb4c55f464 100644 --- a/salt/matchers/pillar_exact_match.py +++ b/salt/matchers/pillar_exact_match.py @@ -1,32 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" This is the default pillar exact matcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + import salt.utils.data # pylint: disable=3rd-party-module-not-gated log = logging.getLogger(__name__) -def match(tgt, delimiter=':', opts=None): - ''' +def match(tgt, delimiter=":", opts=None): + """ Reads in the pillar match, no globbing, no PCRE - ''' + """ if not opts: opts = __opts__ - log.debug('pillar target: %s', tgt) + log.debug("pillar target: %s", tgt) if delimiter not in tgt: - log.error('Got insufficient arguments for pillar match ' - 'statement from master') + log.error( + "Got insufficient arguments for pillar match " "statement from master" + ) return False - if 'pillar' in opts: - pillar = opts['pillar'] - elif 'ext_pillar' in opts: - log.info('No pillar found, fallback to ext_pillar') - pillar = opts['ext_pillar'] + if "pillar" in opts: + pillar = opts["pillar"] + elif "ext_pillar" in opts: + log.info("No pillar found, fallback to ext_pillar") + pillar = opts["ext_pillar"] return salt.utils.data.subdict_match( pillar, tgt, delimiter=delimiter, exact_match=True diff --git a/salt/matchers/pillar_match.py b/salt/matchers/pillar_match.py index 32d681130fe..c69f49ce6a0 100644 --- a/salt/matchers/pillar_match.py +++ b/salt/matchers/pillar_match.py @@ -1,34 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" This is the default pillar matcher function. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging -from salt.defaults import DEFAULT_TARGET_DELIM # pylint: disable=3rd-party-module-not-gated + import salt.utils.data # pylint: disable=3rd-party-module-not-gated +from salt.defaults import ( # pylint: disable=3rd-party-module-not-gated + DEFAULT_TARGET_DELIM, +) log = logging.getLogger(__name__) def match(tgt, delimiter=DEFAULT_TARGET_DELIM, opts=None): - ''' + """ Reads in the pillar glob match - ''' + """ if not opts: opts = __opts__ - log.debug('pillar target: %s', tgt) + log.debug("pillar target: %s", tgt) if delimiter not in tgt: - log.error('Got insufficient arguments for pillar match ' - 'statement from master') + log.error( + "Got insufficient arguments for pillar match " "statement from master" + ) return False - if 'pillar' in opts: - pillar = opts['pillar'] - elif 'ext_pillar' in opts: - log.info('No pillar found, fallback to ext_pillar') - pillar = opts['ext_pillar'] + if "pillar" in opts: + pillar = opts["pillar"] + elif "ext_pillar" in opts: + log.info("No pillar found, fallback to ext_pillar") + pillar = opts["ext_pillar"] - return salt.utils.data.subdict_match( - pillar, tgt, delimiter=delimiter - ) + return salt.utils.data.subdict_match(pillar, tgt, delimiter=delimiter) diff --git a/salt/matchers/pillar_pcre_match.py b/salt/matchers/pillar_pcre_match.py index 3fbf0e6f941..27d9b25aad9 100644 --- a/salt/matchers/pillar_pcre_match.py +++ b/salt/matchers/pillar_pcre_match.py @@ -1,33 +1,37 @@ # -*- coding: utf-8 -*- -''' +""" This is the default pillar PCRE matcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging -from salt.defaults import DEFAULT_TARGET_DELIM # pylint: disable=3rd-party-module-not-gated + import salt.utils.data # pylint: disable=3rd-party-module-not-gated +from salt.defaults import ( # pylint: disable=3rd-party-module-not-gated + DEFAULT_TARGET_DELIM, +) log = logging.getLogger(__name__) def match(tgt, delimiter=DEFAULT_TARGET_DELIM, opts=None): - ''' + """ Reads in the pillar pcre match - ''' + """ if not opts: opts = __opts__ - log.debug('pillar PCRE target: %s', tgt) + log.debug("pillar PCRE target: %s", tgt) if delimiter not in tgt: - log.error('Got insufficient arguments for pillar PCRE match ' - 'statement from master') + log.error( + "Got insufficient arguments for pillar PCRE match " "statement from master" + ) return False - if 'pillar' in opts: - pillar = opts['pillar'] - elif 'ext_pillar' in opts: - log.info('No pillar found, fallback to ext_pillar') - pillar = opts['ext_pillar'] + if "pillar" in opts: + pillar = opts["pillar"] + elif "ext_pillar" in opts: + log.info("No pillar found, fallback to ext_pillar") + pillar = opts["ext_pillar"] return salt.utils.data.subdict_match( pillar, tgt, delimiter=delimiter, regex_match=True diff --git a/salt/matchers/range_match.py b/salt/matchers/range_match.py index d37d9b790a1..8ec1f443607 100644 --- a/salt/matchers/range_match.py +++ b/salt/matchers/range_match.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" This is the default range matcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging @@ -9,6 +9,7 @@ import logging HAS_RANGE = False try: import seco.range + HAS_RANGE = True except ImportError: pass @@ -17,16 +18,16 @@ log = logging.getLogger(__name__) def match(tgt, opts=None): - ''' + """ Matches based on range cluster - ''' + """ if not opts: opts = __opts__ if HAS_RANGE: - range_ = seco.range.Range(opts['range_server']) + range_ = seco.range.Range(opts["range_server"]) try: - return opts['grains']['fqdn'] in range_.expand(tgt) + return opts["grains"]["fqdn"] in range_.expand(tgt) except seco.range.RangeException as exc: - log.debug('Range exception in compound match: %s', exc) + log.debug("Range exception in compound match: %s", exc) return False return False diff --git a/salt/metaproxy/__init__.py b/salt/metaproxy/__init__.py index 2ada1ab598b..70e6568dd29 100644 --- a/salt/metaproxy/__init__.py +++ b/salt/metaproxy/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Metaproxy Directory -''' +""" diff --git a/salt/metaproxy/proxy.py b/salt/metaproxy/proxy.py index cc59d477c9f..07e0ff033ba 100644 --- a/salt/metaproxy/proxy.py +++ b/salt/metaproxy/proxy.py @@ -2,29 +2,38 @@ # # Proxy minion metaproxy modules # -from __future__ import absolute_import, print_function, with_statement, unicode_literals +from __future__ import absolute_import, print_function, unicode_literals, with_statement + +import logging import os import signal import sys -import types -import logging import threading import traceback +import types # Import Salt Libs # pylint: disable=3rd-party-module-not-gated import salt +import salt.beacons +import salt.cli.daemons import salt.client import salt.crypt -import salt.loader -import salt.beacons +import salt.defaults.exitcodes import salt.engines +import salt.ext.tornado.gen # pylint: disable=F0401 +import salt.ext.tornado.ioloop # pylint: disable=F0401 +import salt.loader +import salt.log.setup +import salt.minion import salt.payload import salt.pillar +import salt.serializers.msgpack import salt.syspaths import salt.utils.args import salt.utils.context import salt.utils.data +import salt.utils.dictupdate import salt.utils.error import salt.utils.event import salt.utils.files @@ -38,15 +47,7 @@ import salt.utils.schedule import salt.utils.ssdp import salt.utils.user import salt.utils.zeromq -import salt.defaults.exitcodes -import salt.cli.daemons -import salt.log.setup -import salt.serializers.msgpack -import salt.minion -import salt.defaults.exitcodes - -import salt.utils.dictupdate -from salt.utils.event import tagify +from salt.defaults import DEFAULT_TARGET_DELIM from salt.exceptions import ( CommandExecutionError, CommandNotFoundError, @@ -56,14 +57,8 @@ from salt.exceptions import ( from salt.ext import six from salt.ext.six.moves import range from salt.minion import ProxyMinion - -from salt.defaults import DEFAULT_TARGET_DELIM -from salt.utils.process import (default_signals, - SignalHandlingProcess) - - -import salt.ext.tornado.gen # pylint: disable=F0401 -import salt.ext.tornado.ioloop # pylint: disable=F0401 +from salt.utils.event import tagify +from salt.utils.process import SignalHandlingProcess, default_signals log = logging.getLogger(__name__) @@ -71,55 +66,69 @@ log = logging.getLogger(__name__) def post_master_init(self, master): log.debug("subclassed LazyLoaded _post_master_init") if self.connected: - self.opts['master'] = master + self.opts["master"] = master - self.opts['pillar'] = yield salt.pillar.get_async_pillar( + self.opts["pillar"] = yield salt.pillar.get_async_pillar( self.opts, - self.opts['grains'], - self.opts['id'], - saltenv=self.opts['saltenv'], - pillarenv=self.opts.get('pillarenv'), + self.opts["grains"], + self.opts["id"], + saltenv=self.opts["saltenv"], + pillarenv=self.opts.get("pillarenv"), ).compile_pillar() - if 'proxy' not in self.opts['pillar'] and 'proxy' not in self.opts: - errmsg = 'No proxy key found in pillar or opts for id ' + self.opts['id'] + '. ' + \ - 'Check your pillar/opts configuration and contents. Salt-proxy aborted.' + if "proxy" not in self.opts["pillar"] and "proxy" not in self.opts: + errmsg = ( + "No proxy key found in pillar or opts for id " + + self.opts["id"] + + ". " + + "Check your pillar/opts configuration and contents. Salt-proxy aborted." + ) log.error(errmsg) self._running = False raise SaltSystemExit(code=-1, msg=errmsg) - if 'proxy' not in self.opts: - self.opts['proxy'] = self.opts['pillar']['proxy'] + if "proxy" not in self.opts: + self.opts["proxy"] = self.opts["pillar"]["proxy"] - if self.opts.get('proxy_merge_pillar_in_opts'): + if self.opts.get("proxy_merge_pillar_in_opts"): # Override proxy opts with pillar data when the user required. - self.opts = salt.utils.dictupdate.merge(self.opts, - self.opts['pillar'], - strategy=self.opts.get('proxy_merge_pillar_in_opts_strategy'), - merge_lists=self.opts.get('proxy_deep_merge_pillar_in_opts', False)) - elif self.opts.get('proxy_mines_pillar'): + self.opts = salt.utils.dictupdate.merge( + self.opts, + self.opts["pillar"], + strategy=self.opts.get("proxy_merge_pillar_in_opts_strategy"), + merge_lists=self.opts.get("proxy_deep_merge_pillar_in_opts", False), + ) + elif self.opts.get("proxy_mines_pillar"): # Even when not required, some details such as mine configuration # should be merged anyway whenever possible. - if 'mine_interval' in self.opts['pillar']: - self.opts['mine_interval'] = self.opts['pillar']['mine_interval'] - if 'mine_functions' in self.opts['pillar']: - general_proxy_mines = self.opts.get('mine_functions', []) - specific_proxy_mines = self.opts['pillar']['mine_functions'] + if "mine_interval" in self.opts["pillar"]: + self.opts["mine_interval"] = self.opts["pillar"]["mine_interval"] + if "mine_functions" in self.opts["pillar"]: + general_proxy_mines = self.opts.get("mine_functions", []) + specific_proxy_mines = self.opts["pillar"]["mine_functions"] try: - self.opts['mine_functions'] = general_proxy_mines + specific_proxy_mines + self.opts["mine_functions"] = general_proxy_mines + specific_proxy_mines except TypeError as terr: - log.error('Unable to merge mine functions from the pillar in the opts, for proxy {}'.format( - self.opts['id'])) + log.error( + "Unable to merge mine functions from the pillar in the opts, for proxy {}".format( + self.opts["id"] + ) + ) - fq_proxyname = self.opts['proxy']['proxytype'] + fq_proxyname = self.opts["proxy"]["proxytype"] # Need to load the modules so they get all the dunder variables - self.functions, self.returners, self.function_errors, self.executors = self._load_modules() + ( + self.functions, + self.returners, + self.function_errors, + self.executors, + ) = self._load_modules() # we can then sync any proxymodules down from the master # we do a sync_all here in case proxy code was installed by # SPM or was manually placed in /srv/salt/_modules etc. - self.functions['saltutil.sync_all'](saltenv=self.opts['saltenv']) + self.functions["saltutil.sync_all"](saltenv=self.opts["saltenv"]) # Pull in the utils self.utils = salt.loader.utils(self.opts) @@ -128,15 +137,20 @@ def post_master_init(self, master): self.proxy = salt.loader.proxy(self.opts, utils=self.utils) # And re-load the modules so the __proxy__ variable gets injected - self.functions, self.returners, self.function_errors, self.executors = self._load_modules() - self.functions.pack['__proxy__'] = self.proxy - self.proxy.pack['__salt__'] = self.functions - self.proxy.pack['__ret__'] = self.returners - self.proxy.pack['__pillar__'] = self.opts['pillar'] + ( + self.functions, + self.returners, + self.function_errors, + self.executors, + ) = self._load_modules() + self.functions.pack["__proxy__"] = self.proxy + self.proxy.pack["__salt__"] = self.functions + self.proxy.pack["__ret__"] = self.returners + self.proxy.pack["__pillar__"] = self.opts["pillar"] # Reload utils as well (chicken and egg, __utils__ needs __proxy__ and __proxy__ needs __utils__ self.utils = salt.loader.utils(self.opts, proxy=self.proxy) - self.proxy.pack['__utils__'] = self.utils + self.proxy.pack["__utils__"] = self.utils # Reload all modules so all dunder variables are injected self.proxy.reload_modules() @@ -144,124 +158,156 @@ def post_master_init(self, master): # Start engines here instead of in the Minion superclass __init__ # This is because we need to inject the __proxy__ variable but # it is not setup until now. - self.io_loop.spawn_callback(salt.engines.start_engines, self.opts, - self.process_manager, proxy=self.proxy) + self.io_loop.spawn_callback( + salt.engines.start_engines, self.opts, self.process_manager, proxy=self.proxy + ) - if ('{0}.init'.format(fq_proxyname) not in self.proxy - or '{0}.shutdown'.format(fq_proxyname) not in self.proxy): - errmsg = 'Proxymodule {0} is missing an init() or a shutdown() or both. '.format(fq_proxyname) + \ - 'Check your proxymodule. Salt-proxy aborted.' + if ( + "{0}.init".format(fq_proxyname) not in self.proxy + or "{0}.shutdown".format(fq_proxyname) not in self.proxy + ): + errmsg = ( + "Proxymodule {0} is missing an init() or a shutdown() or both. ".format( + fq_proxyname + ) + + "Check your proxymodule. Salt-proxy aborted." + ) log.error(errmsg) self._running = False raise SaltSystemExit(code=-1, msg=errmsg) - self.module_executors = self.proxy.get('{0}.module_executors'.format(fq_proxyname), lambda: [])() - proxy_init_fn = self.proxy[fq_proxyname + '.init'] + self.module_executors = self.proxy.get( + "{0}.module_executors".format(fq_proxyname), lambda: [] + )() + proxy_init_fn = self.proxy[fq_proxyname + ".init"] proxy_init_fn(self.opts) - self.opts['grains'] = salt.loader.grains(self.opts, proxy=self.proxy) + self.opts["grains"] = salt.loader.grains(self.opts, proxy=self.proxy) self.serial = salt.payload.Serial(self.opts) self.mod_opts = self._prep_mod_opts() self.matchers = salt.loader.matchers(self.opts) self.beacons = salt.beacons.Beacon(self.opts, self.functions) - uid = salt.utils.user.get_uid(user=self.opts.get('user', None)) - self.proc_dir = salt.minion.get_proc_dir(self.opts['cachedir'], uid=uid) + uid = salt.utils.user.get_uid(user=self.opts.get("user", None)) + self.proc_dir = salt.minion.get_proc_dir(self.opts["cachedir"], uid=uid) - if self.connected and self.opts['pillar']: + if self.connected and self.opts["pillar"]: # The pillar has changed due to the connection to the master. # Reload the functions so that they can use the new pillar data. - self.functions, self.returners, self.function_errors, self.executors = self._load_modules() - if hasattr(self, 'schedule'): + ( + self.functions, + self.returners, + self.function_errors, + self.executors, + ) = self._load_modules() + if hasattr(self, "schedule"): self.schedule.functions = self.functions self.schedule.returners = self.returners - if not hasattr(self, 'schedule'): + if not hasattr(self, "schedule"): self.schedule = salt.utils.schedule.Schedule( self.opts, self.functions, self.returners, - cleanup=[salt.minion.master_event(type='alive')], - proxy=self.proxy) + cleanup=[salt.minion.master_event(type="alive")], + proxy=self.proxy, + ) # add default scheduling jobs to the minions scheduler - if self.opts['mine_enabled'] and 'mine.update' in self.functions: - self.schedule.add_job({ - '__mine_interval': - { - 'function': 'mine.update', - 'minutes': self.opts['mine_interval'], - 'jid_include': True, - 'maxrunning': 2, - 'run_on_start': True, - 'return_job': self.opts.get('mine_return_job', False) + if self.opts["mine_enabled"] and "mine.update" in self.functions: + self.schedule.add_job( + { + "__mine_interval": { + "function": "mine.update", + "minutes": self.opts["mine_interval"], + "jid_include": True, + "maxrunning": 2, + "run_on_start": True, + "return_job": self.opts.get("mine_return_job", False), } - }, persist=True) - log.info('Added mine.update to scheduler') + }, + persist=True, + ) + log.info("Added mine.update to scheduler") else: - self.schedule.delete_job('__mine_interval', persist=True) + self.schedule.delete_job("__mine_interval", persist=True) # add master_alive job if enabled - if (self.opts['transport'] != 'tcp' and - self.opts['master_alive_interval'] > 0): - self.schedule.add_job({ - salt.minion.master_event(type='alive', master=self.opts['master']): - { - 'function': 'status.master', - 'seconds': self.opts['master_alive_interval'], - 'jid_include': True, - 'maxrunning': 1, - 'return_job': False, - 'kwargs': {'master': self.opts['master'], - 'connected': True} + if self.opts["transport"] != "tcp" and self.opts["master_alive_interval"] > 0: + self.schedule.add_job( + { + salt.minion.master_event(type="alive", master=self.opts["master"]): { + "function": "status.master", + "seconds": self.opts["master_alive_interval"], + "jid_include": True, + "maxrunning": 1, + "return_job": False, + "kwargs": {"master": self.opts["master"], "connected": True}, } - }, persist=True) - if self.opts['master_failback'] and \ - 'master_list' in self.opts and \ - self.opts['master'] != self.opts['master_list'][0]: - self.schedule.add_job({ - salt.minion.master_event(type='failback'): - { - 'function': 'status.ping_master', - 'seconds': self.opts['master_failback_interval'], - 'jid_include': True, - 'maxrunning': 1, - 'return_job': False, - 'kwargs': {'master': self.opts['master_list'][0]} + }, + persist=True, + ) + if ( + self.opts["master_failback"] + and "master_list" in self.opts + and self.opts["master"] != self.opts["master_list"][0] + ): + self.schedule.add_job( + { + salt.minion.master_event(type="failback"): { + "function": "status.ping_master", + "seconds": self.opts["master_failback_interval"], + "jid_include": True, + "maxrunning": 1, + "return_job": False, + "kwargs": {"master": self.opts["master_list"][0]}, } - }, persist=True) + }, + persist=True, + ) else: - self.schedule.delete_job(salt.minion.master_event(type='failback'), persist=True) + self.schedule.delete_job( + salt.minion.master_event(type="failback"), persist=True + ) else: - self.schedule.delete_job(salt.minion.master_event(type='alive', master=self.opts['master']), persist=True) - self.schedule.delete_job(salt.minion.master_event(type='failback'), persist=True) + self.schedule.delete_job( + salt.minion.master_event(type="alive", master=self.opts["master"]), + persist=True, + ) + self.schedule.delete_job( + salt.minion.master_event(type="failback"), persist=True + ) # proxy keepalive - proxy_alive_fn = fq_proxyname+'.alive' - if (proxy_alive_fn in self.proxy - and 'status.proxy_reconnect' in self.functions - and self.opts.get('proxy_keep_alive', True)): + proxy_alive_fn = fq_proxyname + ".alive" + if ( + proxy_alive_fn in self.proxy + and "status.proxy_reconnect" in self.functions + and self.opts.get("proxy_keep_alive", True) + ): # if `proxy_keep_alive` is either not specified, either set to False does not retry reconnecting - self.schedule.add_job({ - '__proxy_keepalive': - { - 'function': 'status.proxy_reconnect', - 'minutes': self.opts.get('proxy_keep_alive_interval', 1), # by default, check once per minute - 'jid_include': True, - 'maxrunning': 1, - 'return_job': False, - 'kwargs': { - 'proxy_name': fq_proxyname - } + self.schedule.add_job( + { + "__proxy_keepalive": { + "function": "status.proxy_reconnect", + "minutes": self.opts.get( + "proxy_keep_alive_interval", 1 + ), # by default, check once per minute + "jid_include": True, + "maxrunning": 1, + "return_job": False, + "kwargs": {"proxy_name": fq_proxyname}, } - }, persist=True) + }, + persist=True, + ) self.schedule.enable_schedule() else: - self.schedule.delete_job('__proxy_keepalive', persist=True) + self.schedule.delete_job("__proxy_keepalive", persist=True) # Sync the grains here so the proxy can communicate them to the master - self.functions['saltutil.sync_grains'](saltenv='base') - self.grains_cache = self.opts['grains'] + self.functions["saltutil.sync_grains"](saltenv="base") + self.grains_cache = self.opts["grains"] self.ready = True @@ -270,11 +316,14 @@ def target(cls, minion_instance, opts, data, connected): if not minion_instance: minion_instance = cls(opts) minion_instance.connected = connected - if not hasattr(minion_instance, 'functions'): + if not hasattr(minion_instance, "functions"): # Need to load the modules so they get all the dunder variables - functions, returners, function_errors, executors = ( - minion_instance._load_modules(grains=opts['grains']) - ) + ( + functions, + returners, + function_errors, + executors, + ) = minion_instance._load_modules(grains=opts["grains"]) minion_instance.functions = functions minion_instance.returners = returners minion_instance.function_errors = function_errors @@ -284,117 +333,152 @@ def target(cls, minion_instance, opts, data, connected): minion_instance.utils = salt.loader.utils(minion_instance.opts) # Then load the proxy module - minion_instance.proxy = salt.loader.proxy(minion_instance.opts, utils=minion_instance.utils) + minion_instance.proxy = salt.loader.proxy( + minion_instance.opts, utils=minion_instance.utils + ) # And re-load the modules so the __proxy__ variable gets injected - functions, returners, function_errors, executors = ( - minion_instance._load_modules(grains=opts['grains']) - ) + ( + functions, + returners, + function_errors, + executors, + ) = minion_instance._load_modules(grains=opts["grains"]) minion_instance.functions = functions minion_instance.returners = returners minion_instance.function_errors = function_errors minion_instance.executors = executors - minion_instance.functions.pack['__proxy__'] = minion_instance.proxy - minion_instance.proxy.pack['__salt__'] = minion_instance.functions - minion_instance.proxy.pack['__ret__'] = minion_instance.returners - minion_instance.proxy.pack['__pillar__'] = minion_instance.opts['pillar'] + minion_instance.functions.pack["__proxy__"] = minion_instance.proxy + minion_instance.proxy.pack["__salt__"] = minion_instance.functions + minion_instance.proxy.pack["__ret__"] = minion_instance.returners + minion_instance.proxy.pack["__pillar__"] = minion_instance.opts["pillar"] # Reload utils as well (chicken and egg, __utils__ needs __proxy__ and __proxy__ needs __utils__ - minion_instance.utils = salt.loader.utils(minion_instance.opts, proxy=minion_instance.proxy) - minion_instance.proxy.pack['__utils__'] = minion_instance.utils + minion_instance.utils = salt.loader.utils( + minion_instance.opts, proxy=minion_instance.proxy + ) + minion_instance.proxy.pack["__utils__"] = minion_instance.utils # Reload all modules so all dunder variables are injected minion_instance.proxy.reload_modules() - fq_proxyname = opts['proxy']['proxytype'] + fq_proxyname = opts["proxy"]["proxytype"] - minion_instance.module_executors = minion_instance.proxy.get('{0}.module_executors'.format(fq_proxyname), lambda: [])() + minion_instance.module_executors = minion_instance.proxy.get( + "{0}.module_executors".format(fq_proxyname), lambda: [] + )() - proxy_init_fn = minion_instance.proxy[fq_proxyname + '.init'] + proxy_init_fn = minion_instance.proxy[fq_proxyname + ".init"] proxy_init_fn(opts) - if not hasattr(minion_instance, 'serial'): + if not hasattr(minion_instance, "serial"): minion_instance.serial = salt.payload.Serial(opts) - if not hasattr(minion_instance, 'proc_dir'): - uid = salt.utils.user.get_uid(user=opts.get('user', None)) - minion_instance.proc_dir = ( - salt.minion.get_proc_dir(opts['cachedir'], uid=uid) + if not hasattr(minion_instance, "proc_dir"): + uid = salt.utils.user.get_uid(user=opts.get("user", None)) + minion_instance.proc_dir = salt.minion.get_proc_dir( + opts["cachedir"], uid=uid ) with salt.ext.tornado.stack_context.StackContext(minion_instance.ctx): - if isinstance(data['fun'], tuple) or isinstance(data['fun'], list): + if isinstance(data["fun"], tuple) or isinstance(data["fun"], list): ProxyMinion._thread_multi_return(minion_instance, opts, data) else: ProxyMinion._thread_return(minion_instance, opts, data) def thread_return(cls, minion_instance, opts, data): - ''' + """ This method should be used as a threading target, start the actual minion side execution. - ''' - fn_ = os.path.join(minion_instance.proc_dir, data['jid']) + """ + fn_ = os.path.join(minion_instance.proc_dir, data["jid"]) - salt.utils.process.appendproctitle('{0}._thread_return {1}'.format(cls.__name__, data['jid'])) + salt.utils.process.appendproctitle( + "{0}._thread_return {1}".format(cls.__name__, data["jid"]) + ) - sdata = {'pid': os.getpid()} + sdata = {"pid": os.getpid()} sdata.update(data) - log.info('Starting a new job with PID %s', sdata['pid']) - with salt.utils.files.fopen(fn_, 'w+b') as fp_: + log.info("Starting a new job with PID %s", sdata["pid"]) + with salt.utils.files.fopen(fn_, "w+b") as fp_: fp_.write(minion_instance.serial.dumps(sdata)) - ret = {'success': False} - function_name = data['fun'] - executors = data.get('module_executors') or \ - getattr(minion_instance, 'module_executors', []) or \ - opts.get('module_executors', ['direct_call']) - allow_missing_funcs = any([ - minion_instance.executors['{0}.allow_missing_func'.format(executor)](function_name) - for executor in executors - if '{0}.allow_missing_func'.format(executor) in minion_instance.executors - ]) + ret = {"success": False} + function_name = data["fun"] + executors = ( + data.get("module_executors") + or getattr(minion_instance, "module_executors", []) + or opts.get("module_executors", ["direct_call"]) + ) + allow_missing_funcs = any( + [ + minion_instance.executors["{0}.allow_missing_func".format(executor)]( + function_name + ) + for executor in executors + if "{0}.allow_missing_func".format(executor) in minion_instance.executors + ] + ) if function_name in minion_instance.functions or allow_missing_funcs is True: try: minion_blackout_violation = False - if minion_instance.connected and minion_instance.opts['pillar'].get('minion_blackout', False): - whitelist = minion_instance.opts['pillar'].get('minion_blackout_whitelist', []) + if minion_instance.connected and minion_instance.opts["pillar"].get( + "minion_blackout", False + ): + whitelist = minion_instance.opts["pillar"].get( + "minion_blackout_whitelist", [] + ) # this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist - if function_name != 'saltutil.refresh_pillar' and function_name not in whitelist: + if ( + function_name != "saltutil.refresh_pillar" + and function_name not in whitelist + ): minion_blackout_violation = True # use minion_blackout_whitelist from grains if it exists - if minion_instance.opts['grains'].get('minion_blackout', False): - whitelist = minion_instance.opts['grains'].get('minion_blackout_whitelist', []) - if function_name != 'saltutil.refresh_pillar' and function_name not in whitelist: + if minion_instance.opts["grains"].get("minion_blackout", False): + whitelist = minion_instance.opts["grains"].get( + "minion_blackout_whitelist", [] + ) + if ( + function_name != "saltutil.refresh_pillar" + and function_name not in whitelist + ): minion_blackout_violation = True if minion_blackout_violation: - raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' ' - 'to False in pillar or grains to resume operations. Only ' - 'saltutil.refresh_pillar allowed in blackout mode.') + raise SaltInvocationError( + "Minion in blackout mode. Set 'minion_blackout' " + "to False in pillar or grains to resume operations. Only " + "saltutil.refresh_pillar allowed in blackout mode." + ) if function_name in minion_instance.functions: func = minion_instance.functions[function_name] - args, kwargs = salt.minion.load_args_and_kwargs( - func, - data['arg'], - data) + args, kwargs = salt.minion.load_args_and_kwargs(func, data["arg"], data) else: # only run if function_name is not in minion_instance.functions and allow_missing_funcs is True func = function_name - args, kwargs = data['arg'], data - minion_instance.functions.pack['__context__']['retcode'] = 0 + args, kwargs = data["arg"], data + minion_instance.functions.pack["__context__"]["retcode"] = 0 if isinstance(executors, six.string_types): executors = [executors] elif not isinstance(executors, list) or not executors: - raise SaltInvocationError("Wrong executors specification: {0}. String or non-empty list expected". - format(executors)) - if opts.get('sudo_user', '') and executors[-1] != 'sudo': - executors[-1] = 'sudo' # replace the last one with sudo - log.trace('Executors list %s', executors) # pylint: disable=no-member + raise SaltInvocationError( + "Wrong executors specification: {0}. String or non-empty list expected".format( + executors + ) + ) + if opts.get("sudo_user", "") and executors[-1] != "sudo": + executors[-1] = "sudo" # replace the last one with sudo + log.trace("Executors list %s", executors) # pylint: disable=no-member for name in executors: - fname = '{0}.execute'.format(name) + fname = "{0}.execute".format(name) if fname not in minion_instance.executors: - raise SaltInvocationError("Executor '{0}' is not available".format(name)) - return_data = minion_instance.executors[fname](opts, data, func, args, kwargs) + raise SaltInvocationError( + "Executor '{0}' is not available".format(name) + ) + return_data = minion_instance.executors[fname]( + opts, data, func, args, kwargs + ) if return_data is not None: break @@ -408,261 +492,264 @@ def thread_return(cls, minion_instance, opts, data): if not iret: iret = [] iret.append(single) - tag = tagify([data['jid'], 'prog', opts['id'], six.text_type(ind)], 'job') - event_data = {'return': single} + tag = tagify( + [data["jid"], "prog", opts["id"], six.text_type(ind)], "job" + ) + event_data = {"return": single} minion_instance._fire_master(event_data, tag) ind += 1 - ret['return'] = iret + ret["return"] = iret else: - ret['return'] = return_data + ret["return"] = return_data - retcode = minion_instance.functions.pack['__context__'].get( - 'retcode', - salt.defaults.exitcodes.EX_OK + retcode = minion_instance.functions.pack["__context__"].get( + "retcode", salt.defaults.exitcodes.EX_OK ) if retcode == salt.defaults.exitcodes.EX_OK: # No nonzero retcode in __context__ dunder. Check if return # is a dictionary with a "result" or "success" key. try: - func_result = all(return_data.get(x, True) - for x in ('result', 'success')) + func_result = all( + return_data.get(x, True) for x in ("result", "success") + ) except Exception: # pylint: disable=broad-except # return data is not a dict func_result = True if not func_result: retcode = salt.defaults.exitcodes.EX_GENERIC - ret['retcode'] = retcode - ret['success'] = retcode == salt.defaults.exitcodes.EX_OK + ret["retcode"] = retcode + ret["success"] = retcode == salt.defaults.exitcodes.EX_OK except CommandNotFoundError as exc: - msg = 'Command required for \'{0}\' not found'.format( - function_name - ) + msg = "Command required for '{0}' not found".format(function_name) log.debug(msg, exc_info=True) - ret['return'] = '{0}: {1}'.format(msg, exc) - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + ret["return"] = "{0}: {1}".format(msg, exc) + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC except CommandExecutionError as exc: log.error( - 'A command in \'%s\' had a problem: %s', - function_name, exc, - exc_info_on_loglevel=logging.DEBUG + "A command in '%s' had a problem: %s", + function_name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) - ret['return'] = 'ERROR: {0}'.format(exc) - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + ret["return"] = "ERROR: {0}".format(exc) + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC except SaltInvocationError as exc: log.error( - 'Problem executing \'%s\': %s', - function_name, exc, - exc_info_on_loglevel=logging.DEBUG + "Problem executing '%s': %s", + function_name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) - ret['return'] = 'ERROR executing \'{0}\': {1}'.format( - function_name, exc - ) - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + ret["return"] = "ERROR executing '{0}': {1}".format(function_name, exc) + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC except TypeError as exc: - msg = 'Passed invalid arguments to {0}: {1}\n{2}'.format( - function_name, exc, func.__doc__ or '' + msg = "Passed invalid arguments to {0}: {1}\n{2}".format( + function_name, exc, func.__doc__ or "" ) log.warning(msg, exc_info_on_loglevel=logging.DEBUG) - ret['return'] = msg - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + ret["return"] = msg + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC except Exception: # pylint: disable=broad-except - msg = 'The minion function caused an exception' + msg = "The minion function caused an exception" log.warning(msg, exc_info_on_loglevel=True) - salt.utils.error.fire_exception(salt.exceptions.MinionError(msg), opts, job=data) - ret['return'] = '{0}: {1}'.format(msg, traceback.format_exc()) - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + salt.utils.error.fire_exception( + salt.exceptions.MinionError(msg), opts, job=data + ) + ret["return"] = "{0}: {1}".format(msg, traceback.format_exc()) + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC else: - docs = minion_instance.functions['sys.doc']('{0}*'.format(function_name)) + docs = minion_instance.functions["sys.doc"]("{0}*".format(function_name)) if docs: - docs[function_name] = minion_instance.functions.missing_fun_string(function_name) - ret['return'] = docs + docs[function_name] = minion_instance.functions.missing_fun_string( + function_name + ) + ret["return"] = docs else: - ret['return'] = minion_instance.functions.missing_fun_string(function_name) - mod_name = function_name.split('.')[0] + ret["return"] = minion_instance.functions.missing_fun_string(function_name) + mod_name = function_name.split(".")[0] if mod_name in minion_instance.function_errors: - ret['return'] += ' Possible reasons: \'{0}\''.format( + ret["return"] += " Possible reasons: '{0}'".format( minion_instance.function_errors[mod_name] ) - ret['success'] = False - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC - ret['out'] = 'nested' + ret["success"] = False + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC + ret["out"] = "nested" - ret['jid'] = data['jid'] - ret['fun'] = data['fun'] - ret['fun_args'] = data['arg'] - if 'master_id' in data: - ret['master_id'] = data['master_id'] - if 'metadata' in data: - if isinstance(data['metadata'], dict): - ret['metadata'] = data['metadata'] + ret["jid"] = data["jid"] + ret["fun"] = data["fun"] + ret["fun_args"] = data["arg"] + if "master_id" in data: + ret["master_id"] = data["master_id"] + if "metadata" in data: + if isinstance(data["metadata"], dict): + ret["metadata"] = data["metadata"] else: - log.warning('The metadata parameter must be a dictionary. Ignoring.') + log.warning("The metadata parameter must be a dictionary. Ignoring.") if minion_instance.connected: - minion_instance._return_pub( - ret, - timeout=minion_instance._return_retry_timer() - ) + minion_instance._return_pub(ret, timeout=minion_instance._return_retry_timer()) # Add default returners from minion config # Should have been coverted to comma-delimited string already - if isinstance(opts.get('return'), six.string_types): - if data['ret']: - data['ret'] = ','.join((data['ret'], opts['return'])) + if isinstance(opts.get("return"), six.string_types): + if data["ret"]: + data["ret"] = ",".join((data["ret"], opts["return"])) else: - data['ret'] = opts['return'] + data["ret"] = opts["return"] - log.debug('minion return: %s', ret) + log.debug("minion return: %s", ret) # TODO: make a list? Seems odd to split it this late :/ - if data['ret'] and isinstance(data['ret'], six.string_types): - if 'ret_config' in data: - ret['ret_config'] = data['ret_config'] - if 'ret_kwargs' in data: - ret['ret_kwargs'] = data['ret_kwargs'] - ret['id'] = opts['id'] - for returner in set(data['ret'].split(',')): + if data["ret"] and isinstance(data["ret"], six.string_types): + if "ret_config" in data: + ret["ret_config"] = data["ret_config"] + if "ret_kwargs" in data: + ret["ret_kwargs"] = data["ret_kwargs"] + ret["id"] = opts["id"] + for returner in set(data["ret"].split(",")): try: - returner_str = '{0}.returner'.format(returner) + returner_str = "{0}.returner".format(returner) if returner_str in minion_instance.returners: minion_instance.returners[returner_str](ret) else: - returner_err = minion_instance.returners.missing_fun_string(returner_str) + returner_err = minion_instance.returners.missing_fun_string( + returner_str + ) log.error( - 'Returner %s could not be loaded: %s', - returner_str, returner_err + "Returner %s could not be loaded: %s", + returner_str, + returner_err, ) except Exception as exc: # pylint: disable=broad-except - log.exception( - 'The return failed for job %s: %s', data['jid'], exc - ) + log.exception("The return failed for job %s: %s", data["jid"], exc) def thread_multi_return(cls, minion_instance, opts, data): - ''' + """ This method should be used as a threading target, start the actual minion side execution. - ''' - fn_ = os.path.join(minion_instance.proc_dir, data['jid']) + """ + fn_ = os.path.join(minion_instance.proc_dir, data["jid"]) - salt.utils.process.appendproctitle('{0}._thread_multi_return {1}'.format(cls.__name__, data['jid'])) + salt.utils.process.appendproctitle( + "{0}._thread_multi_return {1}".format(cls.__name__, data["jid"]) + ) - sdata = {'pid': os.getpid()} + sdata = {"pid": os.getpid()} sdata.update(data) - log.info('Starting a new job with PID %s', sdata['pid']) - with salt.utils.files.fopen(fn_, 'w+b') as fp_: + log.info("Starting a new job with PID %s", sdata["pid"]) + with salt.utils.files.fopen(fn_, "w+b") as fp_: fp_.write(minion_instance.serial.dumps(sdata)) - multifunc_ordered = opts.get('multifunc_ordered', False) - num_funcs = len(data['fun']) + multifunc_ordered = opts.get("multifunc_ordered", False) + num_funcs = len(data["fun"]) if multifunc_ordered: ret = { - 'return': [None] * num_funcs, - 'retcode': [None] * num_funcs, - 'success': [False] * num_funcs + "return": [None] * num_funcs, + "retcode": [None] * num_funcs, + "success": [False] * num_funcs, } else: - ret = { - 'return': {}, - 'retcode': {}, - 'success': {} - } + ret = {"return": {}, "retcode": {}, "success": {}} for ind in range(0, num_funcs): if not multifunc_ordered: - ret['success'][data['fun'][ind]] = False + ret["success"][data["fun"][ind]] = False try: minion_blackout_violation = False - if minion_instance.connected and minion_instance.opts['pillar'].get('minion_blackout', False): - whitelist = minion_instance.opts['pillar'].get('minion_blackout_whitelist', []) + if minion_instance.connected and minion_instance.opts["pillar"].get( + "minion_blackout", False + ): + whitelist = minion_instance.opts["pillar"].get( + "minion_blackout_whitelist", [] + ) # this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist - if data['fun'][ind] != 'saltutil.refresh_pillar' and data['fun'][ind] not in whitelist: + if ( + data["fun"][ind] != "saltutil.refresh_pillar" + and data["fun"][ind] not in whitelist + ): minion_blackout_violation = True - elif minion_instance.opts['grains'].get('minion_blackout', False): - whitelist = minion_instance.opts['grains'].get('minion_blackout_whitelist', []) - if data['fun'][ind] != 'saltutil.refresh_pillar' and data['fun'][ind] not in whitelist: + elif minion_instance.opts["grains"].get("minion_blackout", False): + whitelist = minion_instance.opts["grains"].get( + "minion_blackout_whitelist", [] + ) + if ( + data["fun"][ind] != "saltutil.refresh_pillar" + and data["fun"][ind] not in whitelist + ): minion_blackout_violation = True if minion_blackout_violation: - raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' ' - 'to False in pillar or grains to resume operations. Only ' - 'saltutil.refresh_pillar allowed in blackout mode.') + raise SaltInvocationError( + "Minion in blackout mode. Set 'minion_blackout' " + "to False in pillar or grains to resume operations. Only " + "saltutil.refresh_pillar allowed in blackout mode." + ) - func = minion_instance.functions[data['fun'][ind]] + func = minion_instance.functions[data["fun"][ind]] args, kwargs = salt.minion.load_args_and_kwargs( - func, - data['arg'][ind], - data) - minion_instance.functions.pack['__context__']['retcode'] = 0 - key = ind if multifunc_ordered else data['fun'][ind] - ret['return'][key] = func(*args, **kwargs) - retcode = minion_instance.functions.pack['__context__'].get( - 'retcode', - 0 + func, data["arg"][ind], data ) + minion_instance.functions.pack["__context__"]["retcode"] = 0 + key = ind if multifunc_ordered else data["fun"][ind] + ret["return"][key] = func(*args, **kwargs) + retcode = minion_instance.functions.pack["__context__"].get("retcode", 0) if retcode == 0: # No nonzero retcode in __context__ dunder. Check if return # is a dictionary with a "result" or "success" key. try: - func_result = all(ret['return'][key].get(x, True) - for x in ('result', 'success')) + func_result = all( + ret["return"][key].get(x, True) for x in ("result", "success") + ) except Exception: # pylint: disable=broad-except # return data is not a dict func_result = True if not func_result: retcode = 1 - ret['retcode'][key] = retcode - ret['success'][key] = retcode == 0 + ret["retcode"][key] = retcode + ret["success"][key] = retcode == 0 except Exception as exc: # pylint: disable=broad-except trb = traceback.format_exc() - log.warning('The minion function caused an exception: %s', exc) + log.warning("The minion function caused an exception: %s", exc) if multifunc_ordered: - ret['return'][ind] = trb + ret["return"][ind] = trb else: - ret['return'][data['fun'][ind]] = trb - ret['jid'] = data['jid'] - ret['fun'] = data['fun'] - ret['fun_args'] = data['arg'] - if 'metadata' in data: - ret['metadata'] = data['metadata'] + ret["return"][data["fun"][ind]] = trb + ret["jid"] = data["jid"] + ret["fun"] = data["fun"] + ret["fun_args"] = data["arg"] + if "metadata" in data: + ret["metadata"] = data["metadata"] if minion_instance.connected: - minion_instance._return_pub( - ret, - timeout=minion_instance._return_retry_timer() - ) - if data['ret']: - if 'ret_config' in data: - ret['ret_config'] = data['ret_config'] - if 'ret_kwargs' in data: - ret['ret_kwargs'] = data['ret_kwargs'] - for returner in set(data['ret'].split(',')): - ret['id'] = opts['id'] + minion_instance._return_pub(ret, timeout=minion_instance._return_retry_timer()) + if data["ret"]: + if "ret_config" in data: + ret["ret_config"] = data["ret_config"] + if "ret_kwargs" in data: + ret["ret_kwargs"] = data["ret_kwargs"] + for returner in set(data["ret"].split(",")): + ret["id"] = opts["id"] try: - minion_instance.returners['{0}.returner'.format( - returner - )](ret) + minion_instance.returners["{0}.returner".format(returner)](ret) except Exception as exc: # pylint: disable=broad-except - log.error( - 'The return failed for job %s: %s', - data['jid'], exc - ) + log.error("The return failed for job %s: %s", data["jid"], exc) def handle_payload(self, payload): - if payload is not None and payload['enc'] == 'aes': - if self._target_load(payload['load']): + if payload is not None and payload["enc"] == "aes": + if self._target_load(payload["load"]): - self._handle_decoded_payload(payload['load']) - elif self.opts['zmq_filtering']: + self._handle_decoded_payload(payload["load"]) + elif self.opts["zmq_filtering"]: # In the filtering enabled case, we'd like to know when minion sees something it shouldnt log.trace( - 'Broadcast message received not for this minion, Load: %s', - payload['load'] + "Broadcast message received not for this minion, Load: %s", + payload["load"], ) # If it's not AES, and thus has not been verified, we do nothing. # In the future, we could add support for some clearfuncs, but @@ -670,46 +757,54 @@ def handle_payload(self, payload): def handle_decoded_payload(self, data): - ''' + """ Override this method if you wish to handle the decoded data differently. - ''' + """ # Ensure payload is unicode. Disregard failure to decode binary blobs. if six.PY2: data = salt.utils.data.decode(data, keep=True) - if 'user' in data: + if "user" in data: log.info( - 'User %s Executing command %s with jid %s', - data['user'], data['fun'], data['jid'] + "User %s Executing command %s with jid %s", + data["user"], + data["fun"], + data["jid"], ) else: - log.info( - 'Executing command %s with jid %s', - data['fun'], data['jid'] - ) - log.debug('Command details %s', data) + log.info("Executing command %s with jid %s", data["fun"], data["jid"]) + log.debug("Command details %s", data) # Don't duplicate jobs - log.trace('Started JIDs: %s', self.jid_queue) + log.trace("Started JIDs: %s", self.jid_queue) if self.jid_queue is not None: - if data['jid'] in self.jid_queue: + if data["jid"] in self.jid_queue: return else: - self.jid_queue.append(data['jid']) - if len(self.jid_queue) > self.opts['minion_jid_queue_hwm']: + self.jid_queue.append(data["jid"]) + if len(self.jid_queue) > self.opts["minion_jid_queue_hwm"]: self.jid_queue.pop(0) - if isinstance(data['fun'], six.string_types): - if data['fun'] == 'sys.reload_modules': - self.functions, self.returners, self.function_errors, self.executors = self._load_modules() + if isinstance(data["fun"], six.string_types): + if data["fun"] == "sys.reload_modules": + ( + self.functions, + self.returners, + self.function_errors, + self.executors, + ) = self._load_modules() self.schedule.functions = self.functions self.schedule.returners = self.returners - process_count_max = self.opts.get('process_count_max') + process_count_max = self.opts.get("process_count_max") if process_count_max > 0: process_count = len(salt.utils.minion.running(self.opts)) while process_count >= process_count_max: - log.warning("Maximum number of processes reached while executing jid {0}, waiting...".format(data['jid'])) + log.warning( + "Maximum number of processes reached while executing jid {0}, waiting...".format( + data["jid"] + ) + ) yield salt.ext.tornado.gen.sleep(10) process_count = len(salt.utils.minion.running(self.opts)) @@ -718,23 +813,23 @@ def handle_decoded_payload(self, data): # python needs to be able to reconstruct the reference on the other # side. instance = self - multiprocessing_enabled = self.opts.get('multiprocessing', True) + multiprocessing_enabled = self.opts.get("multiprocessing", True) if multiprocessing_enabled: - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): # let python reconstruct the minion on the other side if we're # running on windows instance = None with default_signals(signal.SIGINT, signal.SIGTERM): process = SignalHandlingProcess( target=self._target, - name='ProcessPayload', - args=(instance, self.opts, data, self.connected) + name="ProcessPayload", + args=(instance, self.opts, data, self.connected), ) else: process = threading.Thread( target=self._target, args=(instance, self.opts, data, self.connected), - name=data['jid'] + name=data["jid"], ) if multiprocessing_enabled: @@ -744,14 +839,13 @@ def handle_decoded_payload(self, data): process.start() else: process.start() - process.name = '{}-Job-{}'.format(process.name, data['jid']) + process.name = "{}-Job-{}".format(process.name, data["jid"]) self.subprocess_list.add(process) def target_load(self, load): # Verify that the publication is valid - if 'tgt' not in load or 'jid' not in load or 'fun' not in load \ - or 'arg' not in load: + if "tgt" not in load or "jid" not in load or "fun" not in load or "arg" not in load: return False # Verify that the publication applies to this minion @@ -760,18 +854,18 @@ def target_load(self, load): # a "salt -G 'grain_key:grain_val' test.ping" will invoke some # pre-processing on the master and this minion should not see the # publication if the master does not determine that it should. - if 'tgt_type' in load: - match_func = self.matchers.get('{0}_match.match'.format(load['tgt_type']), None) + if "tgt_type" in load: + match_func = self.matchers.get("{0}_match.match".format(load["tgt_type"]), None) if match_func is None: return False - if load['tgt_type'] in ('grain', 'grain_pcre', 'pillar'): - delimiter = load.get('delimiter', DEFAULT_TARGET_DELIM) - if not match_func(load['tgt'], delimiter=delimiter): + if load["tgt_type"] in ("grain", "grain_pcre", "pillar"): + delimiter = load.get("delimiter", DEFAULT_TARGET_DELIM) + if not match_func(load["tgt"], delimiter=delimiter): return False - elif not match_func(load['tgt']): + elif not match_func(load["tgt"]): return False else: - if not self.matchers['glob_match.match'](load['tgt']): + if not self.matchers["glob_match.match"](load["tgt"]): return False return True diff --git a/salt/minion.py b/salt/minion.py index 4e4ac5e2e1b..b0808cbbd2c 100644 --- a/salt/minion.py +++ b/salt/minion.py @@ -1,77 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" Routines to set up a minion -''' +""" # Import python libs -from __future__ import absolute_import, print_function, with_statement, unicode_literals -import functools -import os -import sys -import copy -import time -import types -import signal -import random -import logging -import threading -import traceback +from __future__ import absolute_import, print_function, unicode_literals, with_statement + import contextlib +import copy +import functools +import logging import multiprocessing +import os +import random +import signal +import sys +import threading +import time +import traceback +import types +from binascii import crc32 from random import randint, shuffle from stat import S_IMODE -import salt.serializers.msgpack -from binascii import crc32 -# Import Salt Libs -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext import six -from salt._compat import ipaddress -from salt.utils.network import parse_host_port -from salt.ext.six.moves import range -from salt.template import SLS_ENCODING -from salt.utils.zeromq import zmq, ZMQDefaultLoop, install_zmq, ZMQ_VERSION_INFO -import salt.transport.client -import salt.defaults.exitcodes -import salt.utils.crypt - -from salt.utils.ctx import RequestContext - -# pylint: enable=no-name-in-module,redefined-builtin -import salt.ext.tornado - -HAS_PSUTIL = False -try: - import salt.utils.psutil_compat as psutil - HAS_PSUTIL = True -except ImportError: - pass - -HAS_RESOURCE = False -try: - import resource - HAS_RESOURCE = True -except ImportError: - pass - -try: - import salt.utils.win_functions - HAS_WIN_FUNCTIONS = True -except ImportError: - HAS_WIN_FUNCTIONS = False -# pylint: enable=import-error # Import salt libs import salt +import salt.beacons +import salt.cli.daemons import salt.client import salt.crypt -import salt.loader -import salt.beacons +import salt.defaults.exitcodes import salt.engines + +# pylint: enable=no-name-in-module,redefined-builtin +import salt.ext.tornado +import salt.ext.tornado.gen # pylint: disable=F0401 +import salt.ext.tornado.ioloop # pylint: disable=F0401 +import salt.loader +import salt.log.setup import salt.payload import salt.pillar +import salt.serializers.msgpack import salt.syspaths +import salt.transport.client import salt.utils.args import salt.utils.context +import salt.utils.crypt import salt.utils.data +import salt.utils.dictupdate import salt.utils.error import salt.utils.event import salt.utils.files @@ -85,34 +60,58 @@ import salt.utils.schedule import salt.utils.ssdp import salt.utils.user import salt.utils.zeromq -import salt.defaults.exitcodes -import salt.cli.daemons -import salt.log.setup - -import salt.utils.dictupdate +from salt._compat import ipaddress from salt.config import DEFAULT_MINION_OPTS from salt.defaults import DEFAULT_TARGET_DELIM -from salt.utils.debug import enable_sigusr1_handler -from salt.utils.event import tagify -from salt.utils.odict import OrderedDict -from salt.utils.process import (default_signals, - SignalHandlingProcess, - ProcessManager) from salt.exceptions import ( CommandExecutionError, CommandNotFoundError, - SaltInvocationError, - SaltReqTimeoutError, SaltClientError, - SaltSystemExit, SaltDaemonNotRunning, SaltException, - SaltMasterUnresolvableError + SaltInvocationError, + SaltMasterUnresolvableError, + SaltReqTimeoutError, + SaltSystemExit, ) +# Import Salt Libs +# pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext import six +from salt.ext.six.moves import range +from salt.template import SLS_ENCODING +from salt.utils.ctx import RequestContext +from salt.utils.debug import enable_sigusr1_handler +from salt.utils.event import tagify +from salt.utils.network import parse_host_port +from salt.utils.odict import OrderedDict +from salt.utils.process import ProcessManager, SignalHandlingProcess, default_signals +from salt.utils.zeromq import ZMQ_VERSION_INFO, ZMQDefaultLoop, install_zmq, zmq + +HAS_PSUTIL = False +try: + import salt.utils.psutil_compat as psutil + + HAS_PSUTIL = True +except ImportError: + pass + +HAS_RESOURCE = False +try: + import resource + + HAS_RESOURCE = True +except ImportError: + pass + +try: + import salt.utils.win_functions + + HAS_WIN_FUNCTIONS = True +except ImportError: + HAS_WIN_FUNCTIONS = False +# pylint: enable=import-error -import salt.ext.tornado.gen # pylint: disable=F0401 -import salt.ext.tornado.ioloop # pylint: disable=F0401 log = logging.getLogger(__name__) @@ -126,116 +125,129 @@ log = logging.getLogger(__name__) def resolve_dns(opts, fallback=True): - ''' + """ Resolves the master_ip and master_uri options - ''' + """ ret = {} check_dns = True - if (opts.get('file_client', 'remote') == 'local' and - not opts.get('use_master_when_local', False)): + if opts.get("file_client", "remote") == "local" and not opts.get( + "use_master_when_local", False + ): check_dns = False # Since salt.log is imported below, salt.utils.network needs to be imported here as well import salt.utils.network if check_dns is True: try: - if opts['master'] == '': + if opts["master"] == "": raise SaltSystemExit - ret['master_ip'] = salt.utils.network.dns_check( - opts['master'], - int(opts['master_port']), - True, - opts['ipv6']) + ret["master_ip"] = salt.utils.network.dns_check( + opts["master"], int(opts["master_port"]), True, opts["ipv6"] + ) except SaltClientError: - retry_dns_count = opts.get('retry_dns_count', None) - if opts['retry_dns']: + retry_dns_count = opts.get("retry_dns_count", None) + if opts["retry_dns"]: while True: if retry_dns_count is not None: if retry_dns_count == 0: raise SaltMasterUnresolvableError retry_dns_count -= 1 import salt.log - msg = ('Master hostname: \'{0}\' not found or not responsive. ' - 'Retrying in {1} seconds').format(opts['master'], opts['retry_dns']) + + msg = ( + "Master hostname: '{0}' not found or not responsive. " + "Retrying in {1} seconds" + ).format(opts["master"], opts["retry_dns"]) if salt.log.setup.is_console_configured(): log.error(msg) else: - print('WARNING: {0}'.format(msg)) - time.sleep(opts['retry_dns']) + print("WARNING: {0}".format(msg)) + time.sleep(opts["retry_dns"]) try: - ret['master_ip'] = salt.utils.network.dns_check( - opts['master'], - int(opts['master_port']), - True, - opts['ipv6']) + ret["master_ip"] = salt.utils.network.dns_check( + opts["master"], int(opts["master_port"]), True, opts["ipv6"] + ) break except SaltClientError: pass else: if fallback: - ret['master_ip'] = '127.0.0.1' + ret["master_ip"] = "127.0.0.1" else: raise except SaltSystemExit: - unknown_str = 'unknown address' - master = opts.get('master', unknown_str) - if master == '': + unknown_str = "unknown address" + master = opts.get("master", unknown_str) + if master == "": master = unknown_str - if opts.get('__role') == 'syndic': - err = 'Master address: \'{0}\' could not be resolved. Invalid or unresolveable address. ' \ - 'Set \'syndic_master\' value in minion config.'.format(master) + if opts.get("__role") == "syndic": + err = ( + "Master address: '{0}' could not be resolved. Invalid or unresolveable address. " + "Set 'syndic_master' value in minion config.".format(master) + ) else: - err = 'Master address: \'{0}\' could not be resolved. Invalid or unresolveable address. ' \ - 'Set \'master\' value in minion config.'.format(master) + err = ( + "Master address: '{0}' could not be resolved. Invalid or unresolveable address. " + "Set 'master' value in minion config.".format(master) + ) log.error(err) raise SaltSystemExit(code=42, msg=err) else: - ret['master_ip'] = '127.0.0.1' + ret["master_ip"] = "127.0.0.1" - if 'master_ip' in ret and 'master_ip' in opts: - if ret['master_ip'] != opts['master_ip']: + if "master_ip" in ret and "master_ip" in opts: + if ret["master_ip"] != opts["master_ip"]: log.warning( - 'Master ip address changed from %s to %s', - opts['master_ip'], ret['master_ip'] + "Master ip address changed from %s to %s", + opts["master_ip"], + ret["master_ip"], ) - if opts['source_interface_name']: - log.trace('Custom source interface required: %s', opts['source_interface_name']) + if opts["source_interface_name"]: + log.trace("Custom source interface required: %s", opts["source_interface_name"]) interfaces = salt.utils.network.interfaces() - log.trace('The following interfaces are available on this Minion:') + log.trace("The following interfaces are available on this Minion:") log.trace(interfaces) - if opts['source_interface_name'] in interfaces: - if interfaces[opts['source_interface_name']]['up']: - addrs = interfaces[opts['source_interface_name']]['inet'] if not opts['ipv6'] else\ - interfaces[opts['source_interface_name']]['inet6'] - ret['source_ip'] = addrs[0]['address'] - log.debug('Using %s as source IP address', ret['source_ip']) + if opts["source_interface_name"] in interfaces: + if interfaces[opts["source_interface_name"]]["up"]: + addrs = ( + interfaces[opts["source_interface_name"]]["inet"] + if not opts["ipv6"] + else interfaces[opts["source_interface_name"]]["inet6"] + ) + ret["source_ip"] = addrs[0]["address"] + log.debug("Using %s as source IP address", ret["source_ip"]) else: - log.warning('The interface %s is down so it cannot be used as source to connect to the Master', - opts['source_interface_name']) + log.warning( + "The interface %s is down so it cannot be used as source to connect to the Master", + opts["source_interface_name"], + ) else: - log.warning('%s is not a valid interface. Ignoring.', opts['source_interface_name']) - elif opts['source_address']: - ret['source_ip'] = salt.utils.network.dns_check( - opts['source_address'], - int(opts['source_ret_port']), - True, - opts['ipv6']) - log.debug('Using %s as source IP address', ret['source_ip']) - if opts['source_ret_port']: - ret['source_ret_port'] = int(opts['source_ret_port']) - log.debug('Using %d as source port for the ret server', ret['source_ret_port']) - if opts['source_publish_port']: - ret['source_publish_port'] = int(opts['source_publish_port']) - log.debug('Using %d as source port for the master pub', ret['source_publish_port']) - ret['master_uri'] = 'tcp://{ip}:{port}'.format( - ip=ret['master_ip'], port=opts['master_port']) - log.debug('Master URI: %s', ret['master_uri']) + log.warning( + "%s is not a valid interface. Ignoring.", opts["source_interface_name"] + ) + elif opts["source_address"]: + ret["source_ip"] = salt.utils.network.dns_check( + opts["source_address"], int(opts["source_ret_port"]), True, opts["ipv6"] + ) + log.debug("Using %s as source IP address", ret["source_ip"]) + if opts["source_ret_port"]: + ret["source_ret_port"] = int(opts["source_ret_port"]) + log.debug("Using %d as source port for the ret server", ret["source_ret_port"]) + if opts["source_publish_port"]: + ret["source_publish_port"] = int(opts["source_publish_port"]) + log.debug( + "Using %d as source port for the master pub", ret["source_publish_port"] + ) + ret["master_uri"] = "tcp://{ip}:{port}".format( + ip=ret["master_ip"], port=opts["master_port"] + ) + log.debug("Master URI: %s", ret["master_uri"]) return ret def prep_ip_port(opts): - ''' + """ parse host:port values from opts['master'] and return valid: master: ip address or hostname as a string master_port: (optional) master returner port as integer @@ -245,24 +257,24 @@ def prep_ip_port(opts): - master: '127.0.0.1:1234' -> {'master': '127.0.0.1', 'master_port' :1234} - master: '[::1]:1234' -> {'master': '::1', 'master_port': 1234} - master: 'fe80::a00:27ff:fedc:ba98' -> {'master': 'fe80::a00:27ff:fedc:ba98'} - ''' + """ ret = {} # Use given master IP if "ip_only" is set or if master_ip is an ipv6 address without # a port specified. The is_ipv6 check returns False if brackets are used in the IP # definition such as master: '[::1]:1234'. - if opts['master_uri_format'] == 'ip_only': - ret['master'] = ipaddress.ip_address(opts['master']) + if opts["master_uri_format"] == "ip_only": + ret["master"] = ipaddress.ip_address(opts["master"]) else: - host, port = parse_host_port(opts['master']) - ret = {'master': host} + host, port = parse_host_port(opts["master"]) + ret = {"master": host} if port: - ret.update({'master_port': port}) + ret.update({"master_port": port}) return ret def get_proc_dir(cachedir, **kwargs): - ''' + """ Given the cache directory, return the directory that process data is stored in, creating it if it doesn't exist. The following optional Keyword Arguments are handled: @@ -276,14 +288,14 @@ def get_proc_dir(cachedir, **kwargs): gid: the gid to set, if not set, or it is None or -1 no changes are made. Same applies if the directory is already owned by this gid. Must be int. Works only on unix/unix like systems. - ''' - fn_ = os.path.join(cachedir, 'proc') - mode = kwargs.pop('mode', None) + """ + fn_ = os.path.join(cachedir, "proc") + mode = kwargs.pop("mode", None) if mode is None: mode = {} else: - mode = {'mode': mode} + mode = {"mode": mode} if not os.path.isdir(fn_): # proc_dir is not present, create it with mode settings @@ -295,35 +307,36 @@ def get_proc_dir(cachedir, **kwargs): # dir mode. So lets check if mode needs to be changed. if mode: mode_part = S_IMODE(d_stat.st_mode) - if mode_part != mode['mode']: - os.chmod(fn_, (d_stat.st_mode ^ mode_part) | mode['mode']) + if mode_part != mode["mode"]: + os.chmod(fn_, (d_stat.st_mode ^ mode_part) | mode["mode"]) - if hasattr(os, 'chown'): + if hasattr(os, "chown"): # only on unix/unix like systems - uid = kwargs.pop('uid', -1) - gid = kwargs.pop('gid', -1) + uid = kwargs.pop("uid", -1) + gid = kwargs.pop("gid", -1) # if uid and gid are both -1 then go ahead with # no changes at all - if (d_stat.st_uid != uid or d_stat.st_gid != gid) and \ - [i for i in (uid, gid) if i != -1]: + if (d_stat.st_uid != uid or d_stat.st_gid != gid) and [ + i for i in (uid, gid) if i != -1 + ]: os.chown(fn_, uid, gid) return fn_ def load_args_and_kwargs(func, args, data=None, ignore_invalid=False): - ''' + """ Detect the args and kwargs that need to be passed to a function call, and check them against what was passed. - ''' + """ argspec = salt.utils.args.get_function_argspec(func) _args = [] _kwargs = {} invalid_kwargs = [] for arg in args: - if isinstance(arg, dict) and arg.pop('__kwarg__', False) is True: + if isinstance(arg, dict) and arg.pop("__kwarg__", False) is True: # if the arg is a dict with __kwarg__ == True, then its a kwarg for key, val in six.iteritems(arg): if argspec.keywords or key in argspec.args: @@ -334,11 +347,13 @@ def load_args_and_kwargs(func, args, data=None, ignore_invalid=False): # **kwargs not in argspec and parsed argument name not in # list of positional arguments. This keyword argument is # invalid. - invalid_kwargs.append('{0}={1}'.format(key, val)) + invalid_kwargs.append("{0}={1}".format(key, val)) continue else: - string_kwarg = salt.utils.args.parse_input([arg], condition=False)[1] # pylint: disable=W0632 + string_kwarg = salt.utils.args.parse_input([arg], condition=False)[ + 1 + ] # pylint: disable=W0632 if string_kwarg: if argspec.keywords or next(six.iterkeys(string_kwarg)) in argspec.args: # Function supports **kwargs or is a positional argument to @@ -349,7 +364,7 @@ def load_args_and_kwargs(func, args, data=None, ignore_invalid=False): # list of positional arguments. This keyword argument is # invalid. for key, val in six.iteritems(string_kwarg): - invalid_kwargs.append('{0}={1}'.format(key, val)) + invalid_kwargs.append("{0}={1}".format(key, val)) else: _args.append(arg) @@ -359,68 +374,70 @@ def load_args_and_kwargs(func, args, data=None, ignore_invalid=False): if argspec.keywords and isinstance(data, dict): # this function accepts **kwargs, pack in the publish data for key, val in six.iteritems(data): - _kwargs['__pub_{0}'.format(key)] = val + _kwargs["__pub_{0}".format(key)] = val return _args, _kwargs def eval_master_func(opts): - ''' + """ Evaluate master function if master type is 'func' and save it result in opts['master'] - ''' - if '__master_func_evaluated' not in opts: + """ + if "__master_func_evaluated" not in opts: # split module and function and try loading the module - mod_fun = opts['master'] - mod, fun = mod_fun.split('.') + mod_fun = opts["master"] + mod, fun = mod_fun.split(".") try: master_mod = salt.loader.raw_mod(opts, mod, fun) if not master_mod: raise KeyError # we take whatever the module returns as master address - opts['master'] = master_mod[mod_fun]() + opts["master"] = master_mod[mod_fun]() # Check for valid types - if not isinstance(opts['master'], (six.string_types, list)): + if not isinstance(opts["master"], (six.string_types, list)): raise TypeError - opts['__master_func_evaluated'] = True + opts["__master_func_evaluated"] = True except KeyError: - log.error('Failed to load module %s', mod_fun) + log.error("Failed to load module %s", mod_fun) sys.exit(salt.defaults.exitcodes.EX_GENERIC) except TypeError: - log.error('%s returned from %s is not a string', opts['master'], mod_fun) + log.error("%s returned from %s is not a string", opts["master"], mod_fun) sys.exit(salt.defaults.exitcodes.EX_GENERIC) - log.info('Evaluated master from module: %s', mod_fun) + log.info("Evaluated master from module: %s", mod_fun) def master_event(type, master=None): - ''' + """ Centralized master event function which will return event type based on event_map - ''' - event_map = {'connected': '__master_connected', - 'disconnected': '__master_disconnected', - 'failback': '__master_failback', - 'alive': '__master_alive'} + """ + event_map = { + "connected": "__master_connected", + "disconnected": "__master_disconnected", + "failback": "__master_failback", + "alive": "__master_alive", + } - if type == 'alive' and master is not None: - return '{0}_{1}'.format(event_map.get(type), master) + if type == "alive" and master is not None: + return "{0}_{1}".format(event_map.get(type), master) return event_map.get(type, None) def service_name(): - ''' + """ Return the proper service name based on platform - ''' - return 'salt_minion' if 'bsd' in sys.platform else 'salt-minion' + """ + return "salt_minion" if "bsd" in sys.platform else "salt-minion" class MinionBase(object): def __init__(self, opts): self.opts = opts - self.beacons_leader = opts.get('beacons_leader', True) + self.beacons_leader = opts.get("beacons_leader", True) def gen_modules(self, initial_load=False, context=None): - ''' + """ Tell the minion to reload the execution modules CLI Example: @@ -428,75 +445,83 @@ class MinionBase(object): .. code-block:: bash salt '*' sys.reload_modules - ''' + """ if initial_load: - self.opts['pillar'] = salt.pillar.get_pillar( + self.opts["pillar"] = salt.pillar.get_pillar( self.opts, - self.opts['grains'], - self.opts['id'], - self.opts['saltenv'], - pillarenv=self.opts.get('pillarenv'), + self.opts["grains"], + self.opts["id"], + self.opts["saltenv"], + pillarenv=self.opts.get("pillarenv"), ).compile_pillar() self.utils = salt.loader.utils(self.opts, context=context) - self.functions = salt.loader.minion_mods(self.opts, utils=self.utils, context=context) + self.functions = salt.loader.minion_mods( + self.opts, utils=self.utils, context=context + ) self.serializers = salt.loader.serializers(self.opts) - self.returners = salt.loader.returners(self.opts, functions=self.functions, context=context) - self.proxy = salt.loader.proxy(self.opts, functions=self.functions, returners=self.returners) + self.returners = salt.loader.returners( + self.opts, functions=self.functions, context=context + ) + self.proxy = salt.loader.proxy( + self.opts, functions=self.functions, returners=self.returners + ) # TODO: remove self.function_errors = {} # Keep the funcs clean - self.states = salt.loader.states(self.opts, - functions=self.functions, - utils=self.utils, - serializers=self.serializers, - context=context) - self.rend = salt.loader.render(self.opts, functions=self.functions, context=context) -# self.matcher = Matcher(self.opts, self.functions) + self.states = salt.loader.states( + self.opts, + functions=self.functions, + utils=self.utils, + serializers=self.serializers, + context=context, + ) + self.rend = salt.loader.render( + self.opts, functions=self.functions, context=context + ) + # self.matcher = Matcher(self.opts, self.functions) self.matchers = salt.loader.matchers(self.opts) - self.functions['sys.reload_modules'] = self.gen_modules - self.executors = salt.loader.executors(self.opts, - functions=self.functions, - proxy=self.proxy, - context=context) + self.functions["sys.reload_modules"] = self.gen_modules + self.executors = salt.loader.executors( + self.opts, functions=self.functions, proxy=self.proxy, context=context + ) @staticmethod def process_schedule(minion, loop_interval): try: - if hasattr(minion, 'schedule'): + if hasattr(minion, "schedule"): minion.schedule.eval() else: - log.error('Minion scheduler not initialized. Scheduled jobs will not be run.') + log.error( + "Minion scheduler not initialized. Scheduled jobs will not be run." + ) return # Check if scheduler requires lower loop interval than # the loop_interval setting if minion.schedule.loop_interval < loop_interval: loop_interval = minion.schedule.loop_interval - log.debug( - 'Overriding loop_interval because of scheduled jobs.' - ) + log.debug("Overriding loop_interval because of scheduled jobs.") except Exception as exc: # pylint: disable=broad-except - log.error('Exception %s occurred in scheduled job', exc) + log.error("Exception %s occurred in scheduled job", exc) return loop_interval def process_beacons(self, functions): - ''' + """ Evaluate all of the configured beacons, grab the config again in case the pillar or grains changed - ''' - if 'config.merge' in functions: - b_conf = functions['config.merge']('beacons', self.opts['beacons'], omit_opts=True) + """ + if "config.merge" in functions: + b_conf = functions["config.merge"]( + "beacons", self.opts["beacons"], omit_opts=True + ) if b_conf: - return self.beacons.process(b_conf, self.opts['grains']) # pylint: disable=no-member + return self.beacons.process( + b_conf, self.opts["grains"] + ) # pylint: disable=no-member return [] @salt.ext.tornado.gen.coroutine - def eval_master(self, - opts, - timeout=60, - safe=True, - failed=False, - failback=False): - ''' + def eval_master(self, opts, timeout=60, safe=True, failed=False, failback=False): + """ Evaluates and returns a tuple of the current master address and the pub_channel. In standard mode, just creates a pub_channel with the given master address. @@ -510,10 +535,10 @@ class MinionBase(object): phase (for example from the minions main event-loop when a master connection loss was detected), 'failed' should be set to True. The current (possibly failed) master will then be removed from the list of masters. - ''' + """ # return early if we are not connecting to a master - if opts['master_type'] == 'disable': - log.warning('Master is set to disable, skipping connection') + if opts["master_type"] == "disable": + log.warning("Master is set to disable, skipping connection") self.connected = False raise salt.ext.tornado.gen.Return((None, None)) @@ -522,52 +547,67 @@ class MinionBase(object): self._discover_masters() # check if master_type was altered from its default - if opts['master_type'] != 'str' and opts['__role'] != 'syndic': + if opts["master_type"] != "str" and opts["__role"] != "syndic": # check for a valid keyword - if opts['master_type'] == 'func': + if opts["master_type"] == "func": eval_master_func(opts) # if failover or distributed is set, master has to be of type list - elif opts['master_type'] in ('failover', 'distributed'): - if isinstance(opts['master'], list): + elif opts["master_type"] in ("failover", "distributed"): + if isinstance(opts["master"], list): log.info( - 'Got list of available master addresses: %s', - opts['master'] + "Got list of available master addresses: %s", opts["master"] ) - if opts['master_type'] == 'distributed': - master_len = len(opts['master']) + if opts["master_type"] == "distributed": + master_len = len(opts["master"]) if master_len > 1: - secondary_masters = opts['master'][1:] - master_idx = crc32(opts['id']) % master_len + secondary_masters = opts["master"][1:] + master_idx = crc32(opts["id"]) % master_len try: - preferred_masters = opts['master'] - preferred_masters[0] = opts['master'][master_idx] - preferred_masters[1:] = [m for m in opts['master'] if m != preferred_masters[0]] - opts['master'] = preferred_masters - log.info('Distributed to the master at \'%s\'.', opts['master'][0]) + preferred_masters = opts["master"] + preferred_masters[0] = opts["master"][master_idx] + preferred_masters[1:] = [ + m + for m in opts["master"] + if m != preferred_masters[0] + ] + opts["master"] = preferred_masters + log.info( + "Distributed to the master at '%s'.", + opts["master"][0], + ) except (KeyError, AttributeError, TypeError): - log.warning('Failed to distribute to a specific master.') + log.warning( + "Failed to distribute to a specific master." + ) else: - log.warning('master_type = distributed needs more than 1 master.') + log.warning( + "master_type = distributed needs more than 1 master." + ) - if opts['master_shuffle']: + if opts["master_shuffle"]: log.warning( - 'Use of \'master_shuffle\' detected. \'master_shuffle\' is deprecated in favor ' - 'of \'random_master\'. Please update your minion config file.' + "Use of 'master_shuffle' detected. 'master_shuffle' is deprecated in favor " + "of 'random_master'. Please update your minion config file." ) - opts['random_master'] = opts['master_shuffle'] + opts["random_master"] = opts["master_shuffle"] - opts['auth_tries'] = 0 - if opts['master_failback'] and opts['master_failback_interval'] == 0: - opts['master_failback_interval'] = opts['master_alive_interval'] + opts["auth_tries"] = 0 + if ( + opts["master_failback"] + and opts["master_failback_interval"] == 0 + ): + opts["master_failback_interval"] = opts["master_alive_interval"] # if opts['master'] is a str and we have never created opts['master_list'] - elif isinstance(opts['master'], six.string_types) and ('master_list' not in opts): + elif isinstance(opts["master"], six.string_types) and ( + "master_list" not in opts + ): # We have a string, but a list was what was intended. Convert. # See issue 23611 for details - opts['master'] = [opts['master']] - elif opts['__role'] == 'syndic': - log.info('Syndic setting master_syndic to \'%s\'', opts['master']) + opts["master"] = [opts["master"]] + elif opts["__role"] == "syndic": + log.info("Syndic setting master_syndic to '%s'", opts["master"]) # if failed=True, the minion was previously connected # we're probably called from the minions main-event-loop @@ -576,121 +616,132 @@ class MinionBase(object): elif failed: if failback: # failback list of masters to original config - opts['master'] = opts['master_list'] + opts["master"] = opts["master_list"] else: log.info( - 'Moving possibly failed master %s to the end of ' - 'the list of masters', opts['master'] + "Moving possibly failed master %s to the end of " + "the list of masters", + opts["master"], ) - if opts['master'] in opts['local_masters']: + if opts["master"] in opts["local_masters"]: # create new list of master with the possibly failed # one moved to the end - failed_master = opts['master'] - opts['master'] = [x for x in opts['local_masters'] if opts['master'] != x] - opts['master'].append(failed_master) + failed_master = opts["master"] + opts["master"] = [ + x for x in opts["local_masters"] if opts["master"] != x + ] + opts["master"].append(failed_master) else: - opts['master'] = opts['master_list'] + opts["master"] = opts["master_list"] else: - msg = ('master_type set to \'failover\' but \'master\' ' - 'is not of type list but of type ' - '{0}'.format(type(opts['master']))) + msg = ( + "master_type set to 'failover' but 'master' " + "is not of type list but of type " + "{0}".format(type(opts["master"])) + ) log.error(msg) sys.exit(salt.defaults.exitcodes.EX_GENERIC) # If failover is set, minion have to failover on DNS errors instead of retry DNS resolve. # See issue 21082 for details - if opts['retry_dns'] and opts['master_type'] == 'failover': - msg = ('\'master_type\' set to \'failover\' but \'retry_dns\' is not 0. ' - 'Setting \'retry_dns\' to 0 to failover to the next master on DNS errors.') + if opts["retry_dns"] and opts["master_type"] == "failover": + msg = ( + "'master_type' set to 'failover' but 'retry_dns' is not 0. " + "Setting 'retry_dns' to 0 to failover to the next master on DNS errors." + ) log.critical(msg) - opts['retry_dns'] = 0 + opts["retry_dns"] = 0 else: - msg = ('Invalid keyword \'{0}\' for variable ' - '\'master_type\''.format(opts['master_type'])) + msg = "Invalid keyword '{0}' for variable " "'master_type'".format( + opts["master_type"] + ) log.error(msg) sys.exit(salt.defaults.exitcodes.EX_GENERIC) # FIXME: if SMinion don't define io_loop, it can't switch master see #29088 # Specify kwargs for the channel factory so that SMinion doesn't need to define an io_loop # (The channel factories will set a default if the kwarg isn't passed) - factory_kwargs = {'timeout': timeout, 'safe': safe} - if getattr(self, 'io_loop', None): - factory_kwargs['io_loop'] = self.io_loop # pylint: disable=no-member + factory_kwargs = {"timeout": timeout, "safe": safe} + if getattr(self, "io_loop", None): + factory_kwargs["io_loop"] = self.io_loop # pylint: disable=no-member - tries = opts.get('master_tries', 1) + tries = opts.get("master_tries", 1) attempts = 0 # if we have a list of masters, loop through them and be # happy with the first one that allows us to connect - if isinstance(opts['master'], list): + if isinstance(opts["master"], list): conn = False last_exc = None - opts['master_uri_list'] = [] - opts['local_masters'] = copy.copy(opts['master']) + opts["master_uri_list"] = [] + opts["local_masters"] = copy.copy(opts["master"]) # shuffle the masters and then loop through them - if opts['random_master']: + if opts["random_master"]: # master_failback is only used when master_type is set to failover - if opts['master_type'] == 'failover' and opts['master_failback']: - secondary_masters = opts['local_masters'][1:] + if opts["master_type"] == "failover" and opts["master_failback"]: + secondary_masters = opts["local_masters"][1:] shuffle(secondary_masters) - opts['local_masters'][1:] = secondary_masters + opts["local_masters"][1:] = secondary_masters else: - shuffle(opts['local_masters']) + shuffle(opts["local_masters"]) # This sits outside of the connection loop below because it needs to set # up a list of master URIs regardless of which masters are available # to connect _to_. This is primarily used for masterless mode, when # we need a list of master URIs to fire calls back to. - for master in opts['local_masters']: - opts['master'] = master + for master in opts["local_masters"]: + opts["master"] = master opts.update(prep_ip_port(opts)) - opts['master_uri_list'].append(resolve_dns(opts)['master_uri']) + opts["master_uri_list"].append(resolve_dns(opts)["master_uri"]) pub_channel = None while True: if attempts != 0: # Give up a little time between connection attempts # to allow the IOLoop to run any other scheduled tasks. - yield salt.ext.tornado.gen.sleep(opts['acceptance_wait_time']) + yield salt.ext.tornado.gen.sleep(opts["acceptance_wait_time"]) attempts += 1 if tries > 0: - log.debug( - 'Connecting to master. Attempt %s of %s', - attempts, tries - ) + log.debug("Connecting to master. Attempt %s of %s", attempts, tries) else: log.debug( - 'Connecting to master. Attempt %s (infinite attempts)', - attempts + "Connecting to master. Attempt %s (infinite attempts)", attempts ) - for master in opts['local_masters']: - opts['master'] = master + for master in opts["local_masters"]: + opts["master"] = master opts.update(prep_ip_port(opts)) opts.update(resolve_dns(opts)) # on first run, update self.opts with the whole master list # to enable a minion to re-use old masters if they get fixed - if 'master_list' not in opts: - opts['master_list'] = copy.copy(opts['local_masters']) + if "master_list" not in opts: + opts["master_list"] = copy.copy(opts["local_masters"]) self.opts = opts - pub_channel = salt.transport.client.AsyncPubChannel.factory(opts, **factory_kwargs) + pub_channel = salt.transport.client.AsyncPubChannel.factory( + opts, **factory_kwargs + ) try: yield pub_channel.connect() conn = True break except SaltClientError as exc: last_exc = exc - if exc.strerror.startswith('Could not access'): + if exc.strerror.startswith("Could not access"): msg = ( - 'Failed to initiate connection with Master ' - '%s: check ownership/permissions. Error ' - 'message: %s', opts['master'], exc + "Failed to initiate connection with Master " + "%s: check ownership/permissions. Error " + "message: %s", + opts["master"], + exc, ) else: - msg = ('Master %s could not be reached, trying next ' - 'next master (if any)', opts['master']) + msg = ( + "Master %s could not be reached, trying next " + "next master (if any)", + opts["master"], + ) log.info(msg) pub_channel.close() pub_channel = None @@ -700,10 +751,10 @@ class MinionBase(object): if attempts == tries: # Exhausted all attempts. Return exception. self.connected = False - self.opts['master'] = copy.copy(self.opts['local_masters']) + self.opts["master"] = copy.copy(self.opts["local_masters"]) log.error( - 'No master could be reached or all masters ' - 'denied the minion\'s connection attempt.' + "No master could be reached or all masters " + "denied the minion's connection attempt." ) if pub_channel: pub_channel.close() @@ -711,52 +762,54 @@ class MinionBase(object): # should already be set. raise last_exc # pylint: disable=E0702 else: - self.tok = pub_channel.auth.gen_token(b'salt') + self.tok = pub_channel.auth.gen_token(b"salt") self.connected = True - raise salt.ext.tornado.gen.Return((opts['master'], pub_channel)) + raise salt.ext.tornado.gen.Return((opts["master"], pub_channel)) # single master sign in else: - if opts['random_master']: - log.warning('random_master is True but there is only one master specified. Ignoring.') + if opts["random_master"]: + log.warning( + "random_master is True but there is only one master specified. Ignoring." + ) pub_channel = None while True: if attempts != 0: # Give up a little time between connection attempts # to allow the IOLoop to run any other scheduled tasks. - yield salt.ext.tornado.gen.sleep(opts['acceptance_wait_time']) + yield salt.ext.tornado.gen.sleep(opts["acceptance_wait_time"]) attempts += 1 if tries > 0: - log.debug( - 'Connecting to master. Attempt %s of %s', - attempts, tries - ) + log.debug("Connecting to master. Attempt %s of %s", attempts, tries) else: log.debug( - 'Connecting to master. Attempt %s (infinite attempts)', - attempts + "Connecting to master. Attempt %s (infinite attempts)", attempts ) opts.update(prep_ip_port(opts)) opts.update(resolve_dns(opts)) try: - if self.opts['transport'] == 'detect': - self.opts['detect_mode'] = True - for trans in ('zeromq', 'tcp'): - if trans == 'zeromq' and not zmq: + if self.opts["transport"] == "detect": + self.opts["detect_mode"] = True + for trans in ("zeromq", "tcp"): + if trans == "zeromq" and not zmq: continue - self.opts['transport'] = trans - pub_channel = salt.transport.client.AsyncPubChannel.factory(self.opts, **factory_kwargs) + self.opts["transport"] = trans + pub_channel = salt.transport.client.AsyncPubChannel.factory( + self.opts, **factory_kwargs + ) yield pub_channel.connect() if not pub_channel.auth.authenticated: continue - del self.opts['detect_mode'] + del self.opts["detect_mode"] break else: - pub_channel = salt.transport.client.AsyncPubChannel.factory(self.opts, **factory_kwargs) + pub_channel = salt.transport.client.AsyncPubChannel.factory( + self.opts, **factory_kwargs + ) yield pub_channel.connect() - self.tok = pub_channel.auth.gen_token(b'salt') + self.tok = pub_channel.auth.gen_token(b"salt") self.connected = True - raise salt.ext.tornado.gen.Return((opts['master'], pub_channel)) + raise salt.ext.tornado.gen.Return((opts["master"], pub_channel)) except SaltClientError: if attempts == tries: # Exhausted all attempts. Return exception. @@ -766,138 +819,157 @@ class MinionBase(object): six.reraise(*sys.exc_info()) def _discover_masters(self): - ''' + """ Discover master(s) and decide where to connect, if SSDP is around. This modifies the configuration on the fly. :return: - ''' - if self.opts['master'] == DEFAULT_MINION_OPTS['master'] and self.opts['discovery'] is not False: + """ + if ( + self.opts["master"] == DEFAULT_MINION_OPTS["master"] + and self.opts["discovery"] is not False + ): master_discovery_client = salt.utils.ssdp.SSDPDiscoveryClient() masters = {} - for att in range(self.opts['discovery'].get('attempts', 3)): + for att in range(self.opts["discovery"].get("attempts", 3)): try: att += 1 - log.info('Attempting %s time(s) to discover masters', att) + log.info("Attempting %s time(s) to discover masters", att) masters.update(master_discovery_client.discover()) if not masters: - time.sleep(self.opts['discovery'].get('pause', 5)) + time.sleep(self.opts["discovery"].get("pause", 5)) else: break except Exception as err: # pylint: disable=broad-except - log.error('SSDP discovery failure: %s', err) + log.error("SSDP discovery failure: %s", err) break if masters: - policy = self.opts.get('discovery', {}).get('match', 'any') - if policy not in ['any', 'all']: - log.error('SSDP configuration matcher failure: unknown value "%s". ' - 'Should be "any" or "all"', policy) + policy = self.opts.get("discovery", {}).get("match", "any") + if policy not in ["any", "all"]: + log.error( + 'SSDP configuration matcher failure: unknown value "%s". ' + 'Should be "any" or "all"', + policy, + ) else: - mapping = self.opts['discovery'].get('mapping', {}) + mapping = self.opts["discovery"].get("mapping", {}) for addr, mappings in masters.items(): for proto_data in mappings: - cnt = len([key for key, value in mapping.items() - if proto_data.get('mapping', {}).get(key) == value]) - if policy == 'any' and bool(cnt) or cnt == len(mapping): - self.opts['master'] = proto_data['master'] + cnt = len( + [ + key + for key, value in mapping.items() + if proto_data.get("mapping", {}).get(key) == value + ] + ) + if policy == "any" and bool(cnt) or cnt == len(mapping): + self.opts["master"] = proto_data["master"] return def _return_retry_timer(self): - ''' + """ Based on the minion configuration, either return a randomized timer or just return the value of the return_retry_timer. - ''' - msg = 'Minion return retry timer set to %s seconds' - if self.opts.get('return_retry_timer_max'): + """ + msg = "Minion return retry timer set to %s seconds" + if self.opts.get("return_retry_timer_max"): try: - random_retry = randint(self.opts['return_retry_timer'], self.opts['return_retry_timer_max']) + random_retry = randint( + self.opts["return_retry_timer"], self.opts["return_retry_timer_max"] + ) retry_msg = msg % random_retry - log.debug('%s (randomized)', msg % random_retry) + log.debug("%s (randomized)", msg % random_retry) return random_retry except ValueError: # Catch wiseguys using negative integers here log.error( - 'Invalid value (return_retry_timer: %s or ' - 'return_retry_timer_max: %s). Both must be positive ' - 'integers.', - self.opts['return_retry_timer'], - self.opts['return_retry_timer_max'], + "Invalid value (return_retry_timer: %s or " + "return_retry_timer_max: %s). Both must be positive " + "integers.", + self.opts["return_retry_timer"], + self.opts["return_retry_timer_max"], ) - log.debug(msg, DEFAULT_MINION_OPTS['return_retry_timer']) - return DEFAULT_MINION_OPTS['return_retry_timer'] + log.debug(msg, DEFAULT_MINION_OPTS["return_retry_timer"]) + return DEFAULT_MINION_OPTS["return_retry_timer"] else: - log.debug(msg, self.opts.get('return_retry_timer')) - return self.opts.get('return_retry_timer') + log.debug(msg, self.opts.get("return_retry_timer")) + return self.opts.get("return_retry_timer") class SMinion(MinionBase): - ''' + """ Create an object that has loaded all of the minion module functions, grains, modules, returners etc. The SMinion allows developers to generate all of the salt minion functions and present them with these functions for general use. - ''' + """ + def __init__(self, opts, context=None): # Late setup of the opts grains, so we can log from the grains module import salt.loader - opts['grains'] = salt.loader.grains(opts) + + opts["grains"] = salt.loader.grains(opts) super(SMinion, self).__init__(opts) # Clean out the proc directory (default /var/cache/salt/minion/proc) - if (self.opts.get('file_client', 'remote') == 'remote' - or self.opts.get('use_master_when_local', False)): + if self.opts.get("file_client", "remote") == "remote" or self.opts.get( + "use_master_when_local", False + ): install_zmq() io_loop = ZMQDefaultLoop.current() - io_loop.run_sync( - lambda: self.eval_master(self.opts, failed=True) - ) + io_loop.run_sync(lambda: self.eval_master(self.opts, failed=True)) self.gen_modules(initial_load=True, context=context or {}) # If configured, cache pillar data on the minion - if self.opts['file_client'] == 'remote' and self.opts.get('minion_pillar_cache', False): + if self.opts["file_client"] == "remote" and self.opts.get( + "minion_pillar_cache", False + ): import salt.utils.yaml - pdir = os.path.join(self.opts['cachedir'], 'pillar') + + pdir = os.path.join(self.opts["cachedir"], "pillar") if not os.path.isdir(pdir): os.makedirs(pdir, 0o700) - ptop = os.path.join(pdir, 'top.sls') - if self.opts['saltenv'] is not None: - penv = self.opts['saltenv'] + ptop = os.path.join(pdir, "top.sls") + if self.opts["saltenv"] is not None: + penv = self.opts["saltenv"] else: - penv = 'base' - cache_top = {penv: {self.opts['id']: ['cache']}} - with salt.utils.files.fopen(ptop, 'wb') as fp_: + penv = "base" + cache_top = {penv: {self.opts["id"]: ["cache"]}} + with salt.utils.files.fopen(ptop, "wb") as fp_: salt.utils.yaml.safe_dump(cache_top, fp_, encoding=SLS_ENCODING) os.chmod(ptop, 0o600) - cache_sls = os.path.join(pdir, 'cache.sls') - with salt.utils.files.fopen(cache_sls, 'wb') as fp_: - salt.utils.yaml.safe_dump(self.opts['pillar'], fp_, encoding=SLS_ENCODING) + cache_sls = os.path.join(pdir, "cache.sls") + with salt.utils.files.fopen(cache_sls, "wb") as fp_: + salt.utils.yaml.safe_dump( + self.opts["pillar"], fp_, encoding=SLS_ENCODING + ) os.chmod(cache_sls, 0o600) class MasterMinion(object): - ''' + """ Create a fully loaded minion function object for generic use on the master. What makes this class different is that the pillar is omitted, otherwise everything else is loaded cleanly. - ''' + """ + def __init__( - self, - opts, - returners=True, - states=True, - rend=True, - matcher=True, - whitelist=None, - ignore_config_errors=True): + self, + opts, + returners=True, + states=True, + rend=True, + matcher=True, + whitelist=None, + ignore_config_errors=True, + ): self.opts = salt.config.minion_config( - opts['conf_file'], - ignore_config_errors=ignore_config_errors, - role='master' + opts["conf_file"], ignore_config_errors=ignore_config_errors, role="master" ) self.opts.update(opts) self.whitelist = whitelist - self.opts['grains'] = salt.loader.grains(opts) - self.opts['pillar'] = {} + self.opts["grains"] = salt.loader.grains(opts) + self.opts["pillar"] = {} self.mk_returners = returners self.mk_states = states self.mk_rend = rend @@ -905,7 +977,7 @@ class MasterMinion(object): self.gen_modules(initial_load=True) def gen_modules(self, initial_load=False): - ''' + """ Tell the minion to reload the execution modules CLI Example: @@ -913,59 +985,64 @@ class MasterMinion(object): .. code-block:: bash salt '*' sys.reload_modules - ''' + """ self.utils = salt.loader.utils(self.opts) self.functions = salt.loader.minion_mods( self.opts, utils=self.utils, whitelist=self.whitelist, - initial_load=initial_load) + initial_load=initial_load, + ) self.serializers = salt.loader.serializers(self.opts) if self.mk_returners: self.returners = salt.loader.returners(self.opts, self.functions) if self.mk_states: - self.states = salt.loader.states(self.opts, - self.functions, - self.utils, - self.serializers) + self.states = salt.loader.states( + self.opts, self.functions, self.utils, self.serializers + ) if self.mk_rend: self.rend = salt.loader.render(self.opts, self.functions) if self.mk_matcher: self.matchers = salt.loader.matchers(self.opts) - self.functions['sys.reload_modules'] = self.gen_modules + self.functions["sys.reload_modules"] = self.gen_modules class MinionManager(MinionBase): - ''' + """ Create a multi minion interface, this creates as many minions as are defined in the master option and binds each minion object to a respective master. - ''' + """ + def __init__(self, opts): super(MinionManager, self).__init__(opts) - self.auth_wait = self.opts['acceptance_wait_time'] - self.max_auth_wait = self.opts['acceptance_wait_time_max'] + self.auth_wait = self.opts["acceptance_wait_time"] + self.max_auth_wait = self.opts["acceptance_wait_time_max"] self.minions = [] self.jid_queue = [] install_zmq() self.io_loop = ZMQDefaultLoop.current() - self.process_manager = ProcessManager(name='MultiMinionProcessManager') - self.io_loop.spawn_callback(self.process_manager.run, **{'asynchronous': True}) # Tornado backward compat + self.process_manager = ProcessManager(name="MultiMinionProcessManager") + self.io_loop.spawn_callback( + self.process_manager.run, **{"asynchronous": True} + ) # Tornado backward compat # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 def _bind(self): # start up the event publisher, so we can see events during startup self.event_publisher = salt.utils.event.AsyncEventPublisher( - self.opts, - io_loop=self.io_loop, + self.opts, io_loop=self.io_loop, ) - self.event = salt.utils.event.get_event('minion', opts=self.opts, io_loop=self.io_loop) - self.event.subscribe('') + self.event = salt.utils.event.get_event( + "minion", opts=self.opts, io_loop=self.io_loop + ) + self.event.subscribe("") self.event.set_event_handler(self.handle_event) @salt.ext.tornado.gen.coroutine @@ -973,66 +1050,71 @@ class MinionManager(MinionBase): for minion in self.minions: minion.handle_event(package) - def _create_minion_object(self, opts, timeout, safe, - io_loop=None, loaded_base_name=None, - jid_queue=None): - ''' + def _create_minion_object( + self, opts, timeout, safe, io_loop=None, loaded_base_name=None, jid_queue=None + ): + """ Helper function to return the correct type of object - ''' - return Minion(opts, - timeout, - safe, - io_loop=io_loop, - loaded_base_name=loaded_base_name, - jid_queue=jid_queue) + """ + return Minion( + opts, + timeout, + safe, + io_loop=io_loop, + loaded_base_name=loaded_base_name, + jid_queue=jid_queue, + ) def _check_minions(self): - ''' + """ Check the size of self.minions and raise an error if it's empty - ''' + """ if not self.minions: - err = ('Minion unable to successfully connect to ' - 'a Salt Master.') + err = "Minion unable to successfully connect to " "a Salt Master." log.error(err) def _spawn_minions(self, timeout=60): - ''' + """ Spawn all the coroutines which will sign in to masters - ''' - masters = self.opts['master'] - if (self.opts['master_type'] in ('failover', 'distributed')) or not isinstance(self.opts['master'], list): + """ + masters = self.opts["master"] + if (self.opts["master_type"] in ("failover", "distributed")) or not isinstance( + self.opts["master"], list + ): masters = [masters] beacons_leader = True for master in masters: s_opts = copy.deepcopy(self.opts) - s_opts['master'] = master - s_opts['multimaster'] = True - s_opts['beacons_leader'] = beacons_leader + s_opts["master"] = master + s_opts["multimaster"] = True + s_opts["beacons_leader"] = beacons_leader if beacons_leader: beacons_leader = False - minion = self._create_minion_object(s_opts, - s_opts['auth_timeout'], - False, - io_loop=self.io_loop, - loaded_base_name='salt.loader.{0}'.format(s_opts['master']), - jid_queue=self.jid_queue) + minion = self._create_minion_object( + s_opts, + s_opts["auth_timeout"], + False, + io_loop=self.io_loop, + loaded_base_name="salt.loader.{0}".format(s_opts["master"]), + jid_queue=self.jid_queue, + ) self.io_loop.spawn_callback(self._connect_minion, minion) self.io_loop.call_later(timeout, self._check_minions) @salt.ext.tornado.gen.coroutine def _connect_minion(self, minion): - ''' + """ Create a minion, and asynchronously connect it to a master - ''' + """ last = 0 # never have we signed in - auth_wait = minion.opts['acceptance_wait_time'] + auth_wait = minion.opts["acceptance_wait_time"] failed = False while True: try: - if minion.opts.get('beacons_before_connect', False): + if minion.opts.get("beacons_before_connect", False): minion.setup_beacons(before_connect=True) - if minion.opts.get('scheduler_before_connect', False): + if minion.opts.get("scheduler_before_connect", False): minion.setup_scheduler(before_connect=True) yield minion.connect_master(failed=failed) minion.tune_in(start=False) @@ -1041,34 +1123,38 @@ class MinionManager(MinionBase): except SaltClientError as exc: failed = True log.error( - 'Error while bringing up minion for multi-master. Is ' - 'master at %s responding?', minion.opts['master'] + "Error while bringing up minion for multi-master. Is " + "master at %s responding?", + minion.opts["master"], ) last = time.time() if auth_wait < self.max_auth_wait: auth_wait += self.auth_wait yield salt.ext.tornado.gen.sleep(auth_wait) # TODO: log? except SaltMasterUnresolvableError: - err = 'Master address: \'{0}\' could not be resolved. Invalid or unresolveable address. ' \ - 'Set \'master\' value in minion config.'.format(minion.opts['master']) + err = ( + "Master address: '{0}' could not be resolved. Invalid or unresolveable address. " + "Set 'master' value in minion config.".format(minion.opts["master"]) + ) log.error(err) break except Exception as e: # pylint: disable=broad-except failed = True log.critical( - 'Unexpected error while connecting to %s', - minion.opts['master'], exc_info=True + "Unexpected error while connecting to %s", + minion.opts["master"], + exc_info=True, ) # Multi Master Tune In def tune_in(self): - ''' + """ Bind to the masters This loop will attempt to create connections to masters it hasn't connected to yet, but once the initial connection is made it is up to ZMQ to do the reconnect (don't know of an API to get the state here in salt) - ''' + """ self._bind() # Fire off all the minion coroutines @@ -1098,14 +1184,23 @@ class MinionManager(MinionBase): class Minion(MinionBase): - ''' + """ This class instantiates a minion, runs connections for a minion, and loads all of the functions into the minion - ''' - def __init__(self, opts, timeout=60, safe=True, loaded_base_name=None, io_loop=None, jid_queue=None): # pylint: disable=W0231 - ''' + """ + + def __init__( + self, + opts, + timeout=60, + safe=True, + loaded_base_name=None, + io_loop=None, + jid_queue=None, + ): # pylint: disable=W0231 + """ Pass in the options dict - ''' + """ # this means that the parent class doesn't know *which* master we connect to super(Minion, self).__init__(opts) self.timeout = timeout @@ -1133,49 +1228,51 @@ class Minion(MinionBase): if zmq: if ZMQ_VERSION_INFO < (3, 2): log.warning( - 'You have a version of ZMQ less than ZMQ 3.2! There are ' - 'known connection keep-alive issues with ZMQ < 3.2 which ' - 'may result in loss of contact with minions. Please ' - 'upgrade your ZMQ!' + "You have a version of ZMQ less than ZMQ 3.2! There are " + "known connection keep-alive issues with ZMQ < 3.2 which " + "may result in loss of contact with minions. Please " + "upgrade your ZMQ!" ) # Late setup of the opts grains, so we can log from the grains # module. If this is a proxy, however, we need to init the proxymodule # before we can get the grains. We do this for proxies in the # post_master_init if not salt.utils.platform.is_proxy(): - self.opts['grains'] = salt.loader.grains(opts) + self.opts["grains"] = salt.loader.grains(opts) else: - if self.opts.get('beacons_before_connect', False): + if self.opts.get("beacons_before_connect", False): log.warning( - '\'beacons_before_connect\' is not supported ' - 'for proxy minions. Setting to False' + "'beacons_before_connect' is not supported " + "for proxy minions. Setting to False" ) - self.opts['beacons_before_connect'] = False - if self.opts.get('scheduler_before_connect', False): + self.opts["beacons_before_connect"] = False + if self.opts.get("scheduler_before_connect", False): log.warning( - '\'scheduler_before_connect\' is not supported ' - 'for proxy minions. Setting to False' + "'scheduler_before_connect' is not supported " + "for proxy minions. Setting to False" ) - self.opts['scheduler_before_connect'] = False + self.opts["scheduler_before_connect"] = False - log.info('Creating minion process manager') + log.info("Creating minion process manager") - if self.opts['random_startup_delay']: - sleep_time = random.randint(0, self.opts['random_startup_delay']) + if self.opts["random_startup_delay"]: + sleep_time = random.randint(0, self.opts["random_startup_delay"]) log.info( - 'Minion sleeping for %s seconds due to configured ' - 'startup_delay between 0 and %s seconds', - sleep_time, self.opts['random_startup_delay'] + "Minion sleeping for %s seconds due to configured " + "startup_delay between 0 and %s seconds", + sleep_time, + self.opts["random_startup_delay"], ) time.sleep(sleep_time) - self.process_manager = ProcessManager(name='MinionProcessManager') - self.io_loop.spawn_callback(self.process_manager.run, **{'asynchronous': True}) + self.process_manager = ProcessManager(name="MinionProcessManager") + self.io_loop.spawn_callback(self.process_manager.run, **{"asynchronous": True}) # We don't have the proxy setup yet, so we can't start engines # Engines need to be able to access __proxy__ if not salt.utils.platform.is_proxy(): - self.io_loop.spawn_callback(salt.engines.start_engines, self.opts, - self.process_manager) + self.io_loop.spawn_callback( + salt.engines.start_engines, self.opts, self.process_manager + ) # Install the SIGINT/SIGTERM handlers if not done so far if signal.getsignal(signal.SIGINT) is signal.SIG_DFL: @@ -1197,9 +1294,9 @@ class Minion(MinionBase): sys.exit(0) def sync_connect_master(self, timeout=None, failed=False): - ''' + """ Block until we are connected to a master - ''' + """ self._sync_connect_master_success = False log.debug("sync_connect_master") @@ -1225,20 +1322,22 @@ class Minion(MinionBase): # This needs to be re-raised to preserve restart_on_error behavior. raise six.reraise(*future_exception) if timeout and self._sync_connect_master_success is False: - raise SaltDaemonNotRunning('Failed to connect to the salt-master') + raise SaltDaemonNotRunning("Failed to connect to the salt-master") @salt.ext.tornado.gen.coroutine def connect_master(self, failed=False): - ''' + """ Return a future which will complete when you are connected to a master - ''' - master, self.pub_channel = yield self.eval_master(self.opts, self.timeout, self.safe, failed) + """ + master, self.pub_channel = yield self.eval_master( + self.opts, self.timeout, self.safe, failed + ) yield self._post_master_init(master) # TODO: better name... @salt.ext.tornado.gen.coroutine def _post_master_init(self, master): - ''' + """ Function to finish init after connecting to a master This is primarily loading modules, pillars, etc. (since they need @@ -1250,107 +1349,124 @@ class Minion(MinionBase): Minions and ProxyMinions need significantly different post master setups, which is why the differences are not factored out into separate helper functions. - ''' + """ if self.connected: - self.opts['master'] = master + self.opts["master"] = master # Initialize pillar before loader to make pillar accessible in modules async_pillar = salt.pillar.get_async_pillar( self.opts, - self.opts['grains'], - self.opts['id'], - self.opts['saltenv'], - pillarenv=self.opts.get('pillarenv') + self.opts["grains"], + self.opts["id"], + self.opts["saltenv"], + pillarenv=self.opts.get("pillarenv"), ) - self.opts['pillar'] = yield async_pillar.compile_pillar() + self.opts["pillar"] = yield async_pillar.compile_pillar() async_pillar.destroy() if not self.ready: self._setup_core() - elif self.connected and self.opts['pillar']: + elif self.connected and self.opts["pillar"]: # The pillar has changed due to the connection to the master. # Reload the functions so that they can use the new pillar data. - self.functions, self.returners, self.function_errors, self.executors = self._load_modules() - if hasattr(self, 'schedule'): + ( + self.functions, + self.returners, + self.function_errors, + self.executors, + ) = self._load_modules() + if hasattr(self, "schedule"): self.schedule.functions = self.functions self.schedule.returners = self.returners - if not hasattr(self, 'schedule'): + if not hasattr(self, "schedule"): self.schedule = salt.utils.schedule.Schedule( self.opts, self.functions, self.returners, - cleanup=[master_event(type='alive')]) + cleanup=[master_event(type="alive")], + ) # add default scheduling jobs to the minions scheduler - if self.opts['mine_enabled'] and 'mine.update' in self.functions: - self.schedule.add_job({ - '__mine_interval': + if self.opts["mine_enabled"] and "mine.update" in self.functions: + self.schedule.add_job( { - 'function': 'mine.update', - 'minutes': self.opts['mine_interval'], - 'jid_include': True, - 'maxrunning': 2, - 'run_on_start': True, - 'return_job': self.opts.get('mine_return_job', False) - } - }, persist=True) - log.info('Added mine.update to scheduler') + "__mine_interval": { + "function": "mine.update", + "minutes": self.opts["mine_interval"], + "jid_include": True, + "maxrunning": 2, + "run_on_start": True, + "return_job": self.opts.get("mine_return_job", False), + } + }, + persist=True, + ) + log.info("Added mine.update to scheduler") else: - self.schedule.delete_job('__mine_interval', persist=True) + self.schedule.delete_job("__mine_interval", persist=True) # add master_alive job if enabled - if (self.opts['transport'] != 'tcp' and - self.opts['master_alive_interval'] > 0 and - self.connected): - self.schedule.add_job({ - master_event(type='alive', master=self.opts['master']): + if ( + self.opts["transport"] != "tcp" + and self.opts["master_alive_interval"] > 0 + and self.connected + ): + self.schedule.add_job( { - 'function': 'status.master', - 'seconds': self.opts['master_alive_interval'], - 'jid_include': True, - 'maxrunning': 1, - 'return_job': False, - 'kwargs': {'master': self.opts['master'], - 'connected': True} - } - }, persist=True) - if self.opts['master_failback'] and \ - 'master_list' in self.opts and \ - self.opts['master'] != self.opts['master_list'][0]: - self.schedule.add_job({ - master_event(type='failback'): - { - 'function': 'status.ping_master', - 'seconds': self.opts['master_failback_interval'], - 'jid_include': True, - 'maxrunning': 1, - 'return_job': False, - 'kwargs': {'master': self.opts['master_list'][0]} + master_event(type="alive", master=self.opts["master"]): { + "function": "status.master", + "seconds": self.opts["master_alive_interval"], + "jid_include": True, + "maxrunning": 1, + "return_job": False, + "kwargs": {"master": self.opts["master"], "connected": True}, } - }, persist=True) + }, + persist=True, + ) + if ( + self.opts["master_failback"] + and "master_list" in self.opts + and self.opts["master"] != self.opts["master_list"][0] + ): + self.schedule.add_job( + { + master_event(type="failback"): { + "function": "status.ping_master", + "seconds": self.opts["master_failback_interval"], + "jid_include": True, + "maxrunning": 1, + "return_job": False, + "kwargs": {"master": self.opts["master_list"][0]}, + } + }, + persist=True, + ) else: - self.schedule.delete_job(master_event(type='failback'), persist=True) + self.schedule.delete_job(master_event(type="failback"), persist=True) else: - self.schedule.delete_job(master_event(type='alive', master=self.opts['master']), persist=True) - self.schedule.delete_job(master_event(type='failback'), persist=True) + self.schedule.delete_job( + master_event(type="alive", master=self.opts["master"]), persist=True + ) + self.schedule.delete_job(master_event(type="failback"), persist=True) def _prep_mod_opts(self): - ''' + """ Returns a copy of the opts with key bits stripped out - ''' + """ mod_opts = {} for key, val in six.iteritems(self.opts): - if key == 'logger': + if key == "logger": continue mod_opts[key] = val return mod_opts def _load_modules(self, force_refresh=False, notify=False, grains=None, opts=None): - ''' + """ Return the functions and the returners loaded up from the loader module - ''' + """ opt_in = True if not opts: opts = self.opts @@ -1359,43 +1475,54 @@ class Minion(MinionBase): # a memory limit on module imports # this feature ONLY works on *nix like OSs (resource module doesn't work on windows) modules_max_memory = False - if opts.get('modules_max_memory', -1) > 0 and HAS_PSUTIL and HAS_RESOURCE: + if opts.get("modules_max_memory", -1) > 0 and HAS_PSUTIL and HAS_RESOURCE: log.debug( - 'modules_max_memory set, enforcing a maximum of %s', - opts['modules_max_memory'] + "modules_max_memory set, enforcing a maximum of %s", + opts["modules_max_memory"], ) modules_max_memory = True old_mem_limit = resource.getrlimit(resource.RLIMIT_AS) rss, vms = psutil.Process(os.getpid()).memory_info()[:2] - mem_limit = rss + vms + opts['modules_max_memory'] + mem_limit = rss + vms + opts["modules_max_memory"] resource.setrlimit(resource.RLIMIT_AS, (mem_limit, mem_limit)) - elif opts.get('modules_max_memory', -1) > 0: + elif opts.get("modules_max_memory", -1) > 0: if not HAS_PSUTIL: - log.error('Unable to enforce modules_max_memory because psutil is missing') + log.error( + "Unable to enforce modules_max_memory because psutil is missing" + ) if not HAS_RESOURCE: - log.error('Unable to enforce modules_max_memory because resource is missing') + log.error( + "Unable to enforce modules_max_memory because resource is missing" + ) # This might be a proxy minion - if hasattr(self, 'proxy'): + if hasattr(self, "proxy"): proxy = self.proxy else: proxy = None if grains is None: - opts['grains'] = salt.loader.grains(opts, force_refresh, proxy=proxy) + opts["grains"] = salt.loader.grains(opts, force_refresh, proxy=proxy) self.utils = salt.loader.utils(opts, proxy=proxy) - if opts.get('multimaster', False): + if opts.get("multimaster", False): s_opts = copy.deepcopy(opts) - functions = salt.loader.minion_mods(s_opts, utils=self.utils, proxy=proxy, - loaded_base_name=self.loaded_base_name, notify=notify) + functions = salt.loader.minion_mods( + s_opts, + utils=self.utils, + proxy=proxy, + loaded_base_name=self.loaded_base_name, + notify=notify, + ) else: - functions = salt.loader.minion_mods(opts, utils=self.utils, notify=notify, proxy=proxy) + functions = salt.loader.minion_mods( + opts, utils=self.utils, notify=notify, proxy=proxy + ) returners = salt.loader.returners(opts, functions, proxy=proxy) errors = {} - if '_errors' in functions: - errors = functions['_errors'] - functions.pop('_errors') + if "_errors" in functions: + errors = functions["_errors"] + functions.pop("_errors") # we're done, reset the limits! if modules_max_memory is True: @@ -1410,11 +1537,13 @@ class Minion(MinionBase): def _send_req_sync(self, load, timeout): - if self.opts['minion_sign_messages']: - log.trace('Signing event to be published onto the bus.') - minion_privkey_path = os.path.join(self.opts['pki_dir'], 'minion.pem') - sig = salt.crypt.sign_message(minion_privkey_path, salt.serializers.msgpack.serialize(load)) - load['sig'] = sig + if self.opts["minion_sign_messages"]: + log.trace("Signing event to be published onto the bus.") + minion_privkey_path = os.path.join(self.opts["pki_dir"], "minion.pem") + sig = salt.crypt.sign_message( + minion_privkey_path, salt.serializers.msgpack.serialize(load) + ) + load["sig"] = sig with salt.transport.client.ReqChannel.factory(self.opts) as channel: return channel.send(load, timeout=timeout) @@ -1422,102 +1551,136 @@ class Minion(MinionBase): @salt.ext.tornado.gen.coroutine def _send_req_async(self, load, timeout): - if self.opts['minion_sign_messages']: - log.trace('Signing event to be published onto the bus.') - minion_privkey_path = os.path.join(self.opts['pki_dir'], 'minion.pem') - sig = salt.crypt.sign_message(minion_privkey_path, salt.serializers.msgpack.serialize(load)) - load['sig'] = sig + if self.opts["minion_sign_messages"]: + log.trace("Signing event to be published onto the bus.") + minion_privkey_path = os.path.join(self.opts["pki_dir"], "minion.pem") + sig = salt.crypt.sign_message( + minion_privkey_path, salt.serializers.msgpack.serialize(load) + ) + load["sig"] = sig with salt.transport.client.AsyncReqChannel.factory(self.opts) as channel: ret = yield channel.send(load, timeout=timeout) raise salt.ext.tornado.gen.Return(ret) - def _fire_master(self, data=None, tag=None, events=None, pretag=None, timeout=60, sync=True, timeout_handler=None, include_startup_grains=False): - ''' + def _fire_master( + self, + data=None, + tag=None, + events=None, + pretag=None, + timeout=60, + sync=True, + timeout_handler=None, + include_startup_grains=False, + ): + """ Fire an event on the master, or drop message if unable to send. - ''' - load = {'id': self.opts['id'], - 'cmd': '_minion_event', - 'pretag': pretag, - 'tok': self.tok} + """ + load = { + "id": self.opts["id"], + "cmd": "_minion_event", + "pretag": pretag, + "tok": self.tok, + } if events: - load['events'] = events + load["events"] = events elif data and tag: - load['data'] = data - load['tag'] = tag + load["data"] = data + load["tag"] = tag elif not data and tag: - load['data'] = {} - load['tag'] = tag + load["data"] = {} + load["tag"] = tag else: return if include_startup_grains: grains_to_add = dict( - [(k, v) for k, v in six.iteritems(self.opts.get('grains', {})) if k in self.opts['start_event_grains']]) - load['grains'] = grains_to_add + [ + (k, v) + for k, v in six.iteritems(self.opts.get("grains", {})) + if k in self.opts["start_event_grains"] + ] + ) + load["grains"] = grains_to_add if sync: try: self._send_req_sync(load, timeout) except salt.exceptions.SaltReqTimeoutError: - log.info('fire_master failed: master could not be contacted. Request timed out.') + log.info( + "fire_master failed: master could not be contacted. Request timed out." + ) return False except Exception: # pylint: disable=broad-except - log.info('fire_master failed: %s', traceback.format_exc()) + log.info("fire_master failed: %s", traceback.format_exc()) return False else: if timeout_handler is None: + def handle_timeout(*_): - log.info('fire_master failed: master could not be contacted. Request timed out.') + log.info( + "fire_master failed: master could not be contacted. Request timed out." + ) return True + timeout_handler = handle_timeout with salt.ext.tornado.stack_context.ExceptionStackContext(timeout_handler): - self._send_req_async(load, timeout, callback=lambda f: None) # pylint: disable=unexpected-keyword-arg + # pylint: disable=unexpected-keyword-arg + self._send_req_async(load, timeout, callback=lambda f: None) + # pylint: enable=unexpected-keyword-arg return True @salt.ext.tornado.gen.coroutine def _handle_decoded_payload(self, data): - ''' + """ Override this method if you wish to handle the decoded data differently. - ''' + """ # Ensure payload is unicode. Disregard failure to decode binary blobs. if six.PY2: data = salt.utils.data.decode(data, keep=True) - if 'user' in data: + if "user" in data: log.info( - 'User %s Executing command %s with jid %s', - data['user'], data['fun'], data['jid'] + "User %s Executing command %s with jid %s", + data["user"], + data["fun"], + data["jid"], ) else: - log.info( - 'Executing command %s with jid %s', - data['fun'], data['jid'] - ) - log.debug('Command details %s', data) + log.info("Executing command %s with jid %s", data["fun"], data["jid"]) + log.debug("Command details %s", data) # Don't duplicate jobs - log.trace('Started JIDs: %s', self.jid_queue) + log.trace("Started JIDs: %s", self.jid_queue) if self.jid_queue is not None: - if data['jid'] in self.jid_queue: + if data["jid"] in self.jid_queue: return else: - self.jid_queue.append(data['jid']) - if len(self.jid_queue) > self.opts['minion_jid_queue_hwm']: + self.jid_queue.append(data["jid"]) + if len(self.jid_queue) > self.opts["minion_jid_queue_hwm"]: self.jid_queue.pop(0) - if isinstance(data['fun'], six.string_types): - if data['fun'] == 'sys.reload_modules': - self.functions, self.returners, self.function_errors, self.executors = self._load_modules() + if isinstance(data["fun"], six.string_types): + if data["fun"] == "sys.reload_modules": + ( + self.functions, + self.returners, + self.function_errors, + self.executors, + ) = self._load_modules() self.schedule.functions = self.functions self.schedule.returners = self.returners - process_count_max = self.opts.get('process_count_max') + process_count_max = self.opts.get("process_count_max") if process_count_max > 0: process_count = len(salt.utils.minion.running(self.opts)) while process_count >= process_count_max: - log.warning("Maximum number of processes reached while executing jid %s, waiting...", data['jid']) + log.warning( + "Maximum number of processes reached while executing jid %s, waiting...", + data["jid"], + ) yield salt.ext.tornado.gen.sleep(10) process_count = len(salt.utils.minion.running(self.opts)) @@ -1526,24 +1689,26 @@ class Minion(MinionBase): # python needs to be able to reconstruct the reference on the other # side. instance = self - multiprocessing_enabled = self.opts.get('multiprocessing', True) + multiprocessing_enabled = self.opts.get("multiprocessing", True) if multiprocessing_enabled: - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): # let python reconstruct the minion on the other side if we're # running on windows instance = None with default_signals(signal.SIGINT, signal.SIGTERM): process = SignalHandlingProcess( target=self._target, - name='ProcessPayload', - args=(instance, self.opts, data, self.connected) + name="ProcessPayload", + args=(instance, self.opts, data, self.connected), + ) + process._after_fork_methods.append( + (salt.utils.crypt.reinit_crypto, [], {}) ) - process._after_fork_methods.append((salt.utils.crypt.reinit_crypto, [], {})) else: process = threading.Thread( target=self._target, args=(instance, self.opts, data, self.connected), - name=data['jid'] + name=data["jid"], ) if multiprocessing_enabled: @@ -1553,13 +1718,13 @@ class Minion(MinionBase): process.start() else: process.start() - process.name = '{}-Job-{}'.format(process.name, data['jid']) + process.name = "{}-Job-{}".format(process.name, data["jid"]) self.subprocess_list.add(process) def ctx(self): - ''' + """ Return a single context manager for the minion's data - ''' + """ if six.PY2: return contextlib.nested( self.functions.context_dict.clone(), @@ -1578,102 +1743,131 @@ class Minion(MinionBase): if not minion_instance: minion_instance = cls(opts) minion_instance.connected = connected - if not hasattr(minion_instance, 'functions'): - functions, returners, function_errors, executors = ( - minion_instance._load_modules(grains=opts['grains']) - ) + if not hasattr(minion_instance, "functions"): + ( + functions, + returners, + function_errors, + executors, + ) = minion_instance._load_modules(grains=opts["grains"]) minion_instance.functions = functions minion_instance.returners = returners minion_instance.function_errors = function_errors minion_instance.executors = executors - if not hasattr(minion_instance, 'serial'): + if not hasattr(minion_instance, "serial"): minion_instance.serial = salt.payload.Serial(opts) - if not hasattr(minion_instance, 'proc_dir'): - uid = salt.utils.user.get_uid(user=opts.get('user', None)) - minion_instance.proc_dir = ( - get_proc_dir(opts['cachedir'], uid=uid) - ) + if not hasattr(minion_instance, "proc_dir"): + uid = salt.utils.user.get_uid(user=opts.get("user", None)) + minion_instance.proc_dir = get_proc_dir(opts["cachedir"], uid=uid) def run_func(minion_instance, opts, data): - if isinstance(data['fun'], tuple) or isinstance(data['fun'], list): + if isinstance(data["fun"], tuple) or isinstance(data["fun"], list): return Minion._thread_multi_return(minion_instance, opts, data) else: return Minion._thread_return(minion_instance, opts, data) - with salt.ext.tornado.stack_context.StackContext(functools.partial(RequestContext, - {'data': data, 'opts': opts})): + with salt.ext.tornado.stack_context.StackContext( + functools.partial(RequestContext, {"data": data, "opts": opts}) + ): with salt.ext.tornado.stack_context.StackContext(minion_instance.ctx): run_func(minion_instance, opts, data) @classmethod def _thread_return(cls, minion_instance, opts, data): - ''' + """ This method should be used as a threading target, start the actual minion side execution. - ''' + """ minion_instance.gen_modules() - fn_ = os.path.join(minion_instance.proc_dir, data['jid']) + fn_ = os.path.join(minion_instance.proc_dir, data["jid"]) - salt.utils.process.appendproctitle('{0}._thread_return {1}'.format(cls.__name__, data['jid'])) + salt.utils.process.appendproctitle( + "{0}._thread_return {1}".format(cls.__name__, data["jid"]) + ) - sdata = {'pid': os.getpid()} + sdata = {"pid": os.getpid()} sdata.update(data) - log.info('Starting a new job %s with PID %s', data['jid'], sdata['pid']) - with salt.utils.files.fopen(fn_, 'w+b') as fp_: + log.info("Starting a new job %s with PID %s", data["jid"], sdata["pid"]) + with salt.utils.files.fopen(fn_, "w+b") as fp_: fp_.write(minion_instance.serial.dumps(sdata)) - ret = {'success': False} - function_name = data['fun'] - executors = data.get('module_executors') or \ - getattr(minion_instance, 'module_executors', []) or \ - opts.get('module_executors', ['direct_call']) - allow_missing_funcs = any([ - minion_instance.executors['{0}.allow_missing_func'.format(executor)](function_name) - for executor in executors - if '{0}.allow_missing_func'.format(executor) in minion_instance.executors - ]) + ret = {"success": False} + function_name = data["fun"] + executors = ( + data.get("module_executors") + or getattr(minion_instance, "module_executors", []) + or opts.get("module_executors", ["direct_call"]) + ) + allow_missing_funcs = any( + [ + minion_instance.executors["{0}.allow_missing_func".format(executor)]( + function_name + ) + for executor in executors + if "{0}.allow_missing_func".format(executor) + in minion_instance.executors + ] + ) if function_name in minion_instance.functions or allow_missing_funcs is True: try: minion_blackout_violation = False - if minion_instance.connected and minion_instance.opts['pillar'].get('minion_blackout', False): - whitelist = minion_instance.opts['pillar'].get('minion_blackout_whitelist', []) + if minion_instance.connected and minion_instance.opts["pillar"].get( + "minion_blackout", False + ): + whitelist = minion_instance.opts["pillar"].get( + "minion_blackout_whitelist", [] + ) # this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist - if function_name != 'saltutil.refresh_pillar' and function_name not in whitelist: + if ( + function_name != "saltutil.refresh_pillar" + and function_name not in whitelist + ): minion_blackout_violation = True # use minion_blackout_whitelist from grains if it exists - if minion_instance.opts['grains'].get('minion_blackout', False): - whitelist = minion_instance.opts['grains'].get('minion_blackout_whitelist', []) - if function_name != 'saltutil.refresh_pillar' and function_name not in whitelist: + if minion_instance.opts["grains"].get("minion_blackout", False): + whitelist = minion_instance.opts["grains"].get( + "minion_blackout_whitelist", [] + ) + if ( + function_name != "saltutil.refresh_pillar" + and function_name not in whitelist + ): minion_blackout_violation = True if minion_blackout_violation: - raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' ' - 'to False in pillar or grains to resume operations. Only ' - 'saltutil.refresh_pillar allowed in blackout mode.') + raise SaltInvocationError( + "Minion in blackout mode. Set 'minion_blackout' " + "to False in pillar or grains to resume operations. Only " + "saltutil.refresh_pillar allowed in blackout mode." + ) if function_name in minion_instance.functions: func = minion_instance.functions[function_name] - args, kwargs = load_args_and_kwargs( - func, - data['arg'], - data) + args, kwargs = load_args_and_kwargs(func, data["arg"], data) else: # only run if function_name is not in minion_instance.functions and allow_missing_funcs is True func = function_name - args, kwargs = data['arg'], data - minion_instance.functions.pack['__context__']['retcode'] = 0 + args, kwargs = data["arg"], data + minion_instance.functions.pack["__context__"]["retcode"] = 0 if isinstance(executors, six.string_types): executors = [executors] elif not isinstance(executors, list) or not executors: - raise SaltInvocationError("Wrong executors specification: {0}. String or non-empty list expected". - format(executors)) - if opts.get('sudo_user', '') and executors[-1] != 'sudo': - executors[-1] = 'sudo' # replace the last one with sudo - log.trace('Executors list %s', executors) # pylint: disable=no-member + raise SaltInvocationError( + "Wrong executors specification: {0}. String or non-empty list expected".format( + executors + ) + ) + if opts.get("sudo_user", "") and executors[-1] != "sudo": + executors[-1] = "sudo" # replace the last one with sudo + log.trace("Executors list %s", executors) # pylint: disable=no-member for name in executors: - fname = '{0}.execute'.format(name) + fname = "{0}.execute".format(name) if fname not in minion_instance.executors: - raise SaltInvocationError("Executor '{0}' is not available".format(name)) - return_data = minion_instance.executors[fname](opts, data, func, args, kwargs) + raise SaltInvocationError( + "Executor '{0}' is not available".format(name) + ) + return_data = minion_instance.executors[fname]( + opts, data, func, args, kwargs + ) if return_data is not None: break @@ -1687,258 +1881,268 @@ class Minion(MinionBase): if not iret: iret = [] iret.append(single) - tag = tagify([data['jid'], 'prog', opts['id'], six.text_type(ind)], 'job') - event_data = {'return': single} + tag = tagify( + [data["jid"], "prog", opts["id"], six.text_type(ind)], "job" + ) + event_data = {"return": single} minion_instance._fire_master(event_data, tag) ind += 1 - ret['return'] = iret + ret["return"] = iret else: - ret['return'] = return_data + ret["return"] = return_data - retcode = minion_instance.functions.pack['__context__'].get( - 'retcode', - salt.defaults.exitcodes.EX_OK + retcode = minion_instance.functions.pack["__context__"].get( + "retcode", salt.defaults.exitcodes.EX_OK ) if retcode == salt.defaults.exitcodes.EX_OK: # No nonzero retcode in __context__ dunder. Check if return # is a dictionary with a "result" or "success" key. try: - func_result = all(return_data.get(x, True) - for x in ('result', 'success')) + func_result = all( + return_data.get(x, True) for x in ("result", "success") + ) except Exception: # pylint: disable=broad-except # return data is not a dict func_result = True if not func_result: retcode = salt.defaults.exitcodes.EX_GENERIC - ret['retcode'] = retcode - ret['success'] = retcode == salt.defaults.exitcodes.EX_OK + ret["retcode"] = retcode + ret["success"] = retcode == salt.defaults.exitcodes.EX_OK except CommandNotFoundError as exc: - msg = 'Command required for \'{0}\' not found'.format( - function_name - ) + msg = "Command required for '{0}' not found".format(function_name) log.debug(msg, exc_info=True) - ret['return'] = '{0}: {1}'.format(msg, exc) - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + ret["return"] = "{0}: {1}".format(msg, exc) + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC except CommandExecutionError as exc: log.error( - 'A command in \'%s\' had a problem: %s', - function_name, exc, - exc_info_on_loglevel=logging.DEBUG + "A command in '%s' had a problem: %s", + function_name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) - ret['return'] = 'ERROR: {0}'.format(exc) - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + ret["return"] = "ERROR: {0}".format(exc) + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC except SaltInvocationError as exc: log.error( - 'Problem executing \'%s\': %s', - function_name, exc, - exc_info_on_loglevel=logging.DEBUG + "Problem executing '%s': %s", + function_name, + exc, + exc_info_on_loglevel=logging.DEBUG, ) - ret['return'] = 'ERROR executing \'{0}\': {1}'.format( - function_name, exc - ) - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + ret["return"] = "ERROR executing '{0}': {1}".format(function_name, exc) + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC except TypeError as exc: - msg = 'Passed invalid arguments to {0}: {1}\n{2}'.format( - function_name, exc, func.__doc__ or '' + msg = "Passed invalid arguments to {0}: {1}\n{2}".format( + function_name, exc, func.__doc__ or "" ) log.warning(msg, exc_info_on_loglevel=logging.DEBUG) - ret['return'] = msg - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + ret["return"] = msg + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC except Exception: # pylint: disable=broad-except - msg = 'The minion function caused an exception' + msg = "The minion function caused an exception" log.warning(msg, exc_info_on_loglevel=True) - salt.utils.error.fire_exception(salt.exceptions.MinionError(msg), opts, job=data) - ret['return'] = '{0}: {1}'.format(msg, traceback.format_exc()) - ret['out'] = 'nested' - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC + salt.utils.error.fire_exception( + salt.exceptions.MinionError(msg), opts, job=data + ) + ret["return"] = "{0}: {1}".format(msg, traceback.format_exc()) + ret["out"] = "nested" + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC else: - docs = minion_instance.functions['sys.doc']('{0}*'.format(function_name)) + docs = minion_instance.functions["sys.doc"]("{0}*".format(function_name)) if docs: - docs[function_name] = minion_instance.functions.missing_fun_string(function_name) - ret['return'] = docs + docs[function_name] = minion_instance.functions.missing_fun_string( + function_name + ) + ret["return"] = docs else: - ret['return'] = minion_instance.functions.missing_fun_string(function_name) - mod_name = function_name.split('.')[0] + ret["return"] = minion_instance.functions.missing_fun_string( + function_name + ) + mod_name = function_name.split(".")[0] if mod_name in minion_instance.function_errors: - ret['return'] += ' Possible reasons: \'{0}\''.format( + ret["return"] += " Possible reasons: '{0}'".format( minion_instance.function_errors[mod_name] ) - ret['success'] = False - ret['retcode'] = salt.defaults.exitcodes.EX_GENERIC - ret['out'] = 'nested' + ret["success"] = False + ret["retcode"] = salt.defaults.exitcodes.EX_GENERIC + ret["out"] = "nested" - ret['jid'] = data['jid'] - ret['fun'] = data['fun'] - ret['fun_args'] = data['arg'] - if 'master_id' in data: - ret['master_id'] = data['master_id'] - if 'metadata' in data: - if isinstance(data['metadata'], dict): - ret['metadata'] = data['metadata'] + ret["jid"] = data["jid"] + ret["fun"] = data["fun"] + ret["fun_args"] = data["arg"] + if "master_id" in data: + ret["master_id"] = data["master_id"] + if "metadata" in data: + if isinstance(data["metadata"], dict): + ret["metadata"] = data["metadata"] else: - log.warning('The metadata parameter must be a dictionary. Ignoring.') + log.warning("The metadata parameter must be a dictionary. Ignoring.") if minion_instance.connected: minion_instance._return_pub( - ret, - timeout=minion_instance._return_retry_timer() + ret, timeout=minion_instance._return_retry_timer() ) # Add default returners from minion config # Should have been coverted to comma-delimited string already - if isinstance(opts.get('return'), six.string_types): - if data['ret']: - data['ret'] = ','.join((data['ret'], opts['return'])) + if isinstance(opts.get("return"), six.string_types): + if data["ret"]: + data["ret"] = ",".join((data["ret"], opts["return"])) else: - data['ret'] = opts['return'] + data["ret"] = opts["return"] - log.debug('minion return: %s', ret) + log.debug("minion return: %s", ret) # TODO: make a list? Seems odd to split it this late :/ - if data['ret'] and isinstance(data['ret'], six.string_types): - if 'ret_config' in data: - ret['ret_config'] = data['ret_config'] - if 'ret_kwargs' in data: - ret['ret_kwargs'] = data['ret_kwargs'] - ret['id'] = opts['id'] - for returner in set(data['ret'].split(',')): + if data["ret"] and isinstance(data["ret"], six.string_types): + if "ret_config" in data: + ret["ret_config"] = data["ret_config"] + if "ret_kwargs" in data: + ret["ret_kwargs"] = data["ret_kwargs"] + ret["id"] = opts["id"] + for returner in set(data["ret"].split(",")): try: - returner_str = '{0}.returner'.format(returner) + returner_str = "{0}.returner".format(returner) if returner_str in minion_instance.returners: minion_instance.returners[returner_str](ret) else: - returner_err = minion_instance.returners.missing_fun_string(returner_str) + returner_err = minion_instance.returners.missing_fun_string( + returner_str + ) log.error( - 'Returner %s could not be loaded: %s', - returner_str, returner_err + "Returner %s could not be loaded: %s", + returner_str, + returner_err, ) except Exception as exc: # pylint: disable=broad-except - log.exception( - 'The return failed for job %s: %s', data['jid'], exc - ) + log.exception("The return failed for job %s: %s", data["jid"], exc) @classmethod def _thread_multi_return(cls, minion_instance, opts, data): - ''' + """ This method should be used as a threading target, start the actual minion side execution. - ''' + """ minion_instance.gen_modules() - fn_ = os.path.join(minion_instance.proc_dir, data['jid']) + fn_ = os.path.join(minion_instance.proc_dir, data["jid"]) - salt.utils.process.appendproctitle('{0}._thread_multi_return {1}'.format(cls.__name__, data['jid'])) + salt.utils.process.appendproctitle( + "{0}._thread_multi_return {1}".format(cls.__name__, data["jid"]) + ) - sdata = {'pid': os.getpid()} + sdata = {"pid": os.getpid()} sdata.update(data) - log.info('Starting a new job with PID %s', sdata['pid']) - with salt.utils.files.fopen(fn_, 'w+b') as fp_: + log.info("Starting a new job with PID %s", sdata["pid"]) + with salt.utils.files.fopen(fn_, "w+b") as fp_: fp_.write(minion_instance.serial.dumps(sdata)) - multifunc_ordered = opts.get('multifunc_ordered', False) - num_funcs = len(data['fun']) + multifunc_ordered = opts.get("multifunc_ordered", False) + num_funcs = len(data["fun"]) if multifunc_ordered: ret = { - 'return': [None] * num_funcs, - 'retcode': [None] * num_funcs, - 'success': [False] * num_funcs + "return": [None] * num_funcs, + "retcode": [None] * num_funcs, + "success": [False] * num_funcs, } else: - ret = { - 'return': {}, - 'retcode': {}, - 'success': {} - } + ret = {"return": {}, "retcode": {}, "success": {}} for ind in range(0, num_funcs): if not multifunc_ordered: - ret['success'][data['fun'][ind]] = False + ret["success"][data["fun"][ind]] = False try: minion_blackout_violation = False - if minion_instance.connected and minion_instance.opts['pillar'].get('minion_blackout', False): - whitelist = minion_instance.opts['pillar'].get('minion_blackout_whitelist', []) + if minion_instance.connected and minion_instance.opts["pillar"].get( + "minion_blackout", False + ): + whitelist = minion_instance.opts["pillar"].get( + "minion_blackout_whitelist", [] + ) # this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist - if data['fun'][ind] != 'saltutil.refresh_pillar' and data['fun'][ind] not in whitelist: + if ( + data["fun"][ind] != "saltutil.refresh_pillar" + and data["fun"][ind] not in whitelist + ): minion_blackout_violation = True - elif minion_instance.opts['grains'].get('minion_blackout', False): - whitelist = minion_instance.opts['grains'].get('minion_blackout_whitelist', []) - if data['fun'][ind] != 'saltutil.refresh_pillar' and data['fun'][ind] not in whitelist: + elif minion_instance.opts["grains"].get("minion_blackout", False): + whitelist = minion_instance.opts["grains"].get( + "minion_blackout_whitelist", [] + ) + if ( + data["fun"][ind] != "saltutil.refresh_pillar" + and data["fun"][ind] not in whitelist + ): minion_blackout_violation = True if minion_blackout_violation: - raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' ' - 'to False in pillar or grains to resume operations. Only ' - 'saltutil.refresh_pillar allowed in blackout mode.') + raise SaltInvocationError( + "Minion in blackout mode. Set 'minion_blackout' " + "to False in pillar or grains to resume operations. Only " + "saltutil.refresh_pillar allowed in blackout mode." + ) - func = minion_instance.functions[data['fun'][ind]] + func = minion_instance.functions[data["fun"][ind]] - args, kwargs = load_args_and_kwargs( - func, - data['arg'][ind], - data) - minion_instance.functions.pack['__context__']['retcode'] = 0 - key = ind if multifunc_ordered else data['fun'][ind] - ret['return'][key] = func(*args, **kwargs) - retcode = minion_instance.functions.pack['__context__'].get( - 'retcode', - 0 + args, kwargs = load_args_and_kwargs(func, data["arg"][ind], data) + minion_instance.functions.pack["__context__"]["retcode"] = 0 + key = ind if multifunc_ordered else data["fun"][ind] + ret["return"][key] = func(*args, **kwargs) + retcode = minion_instance.functions.pack["__context__"].get( + "retcode", 0 ) if retcode == 0: # No nonzero retcode in __context__ dunder. Check if return # is a dictionary with a "result" or "success" key. try: - func_result = all(ret['return'][key].get(x, True) - for x in ('result', 'success')) + func_result = all( + ret["return"][key].get(x, True) + for x in ("result", "success") + ) except Exception: # pylint: disable=broad-except # return data is not a dict func_result = True if not func_result: retcode = 1 - ret['retcode'][key] = retcode - ret['success'][key] = retcode == 0 + ret["retcode"][key] = retcode + ret["success"][key] = retcode == 0 except Exception as exc: # pylint: disable=broad-except trb = traceback.format_exc() - log.warning('The minion function caused an exception: %s', exc) + log.warning("The minion function caused an exception: %s", exc) if multifunc_ordered: - ret['return'][ind] = trb + ret["return"][ind] = trb else: - ret['return'][data['fun'][ind]] = trb - ret['jid'] = data['jid'] - ret['fun'] = data['fun'] - ret['fun_args'] = data['arg'] - if 'metadata' in data: - ret['metadata'] = data['metadata'] + ret["return"][data["fun"][ind]] = trb + ret["jid"] = data["jid"] + ret["fun"] = data["fun"] + ret["fun_args"] = data["arg"] + if "metadata" in data: + ret["metadata"] = data["metadata"] if minion_instance.connected: minion_instance._return_pub( - ret, - timeout=minion_instance._return_retry_timer() + ret, timeout=minion_instance._return_retry_timer() ) - if data['ret']: - if 'ret_config' in data: - ret['ret_config'] = data['ret_config'] - if 'ret_kwargs' in data: - ret['ret_kwargs'] = data['ret_kwargs'] - for returner in set(data['ret'].split(',')): - ret['id'] = opts['id'] + if data["ret"]: + if "ret_config" in data: + ret["ret_config"] = data["ret_config"] + if "ret_kwargs" in data: + ret["ret_kwargs"] = data["ret_kwargs"] + for returner in set(data["ret"].split(",")): + ret["id"] = opts["id"] try: - minion_instance.returners['{0}.returner'.format( - returner - )](ret) + minion_instance.returners["{0}.returner".format(returner)](ret) except Exception as exc: # pylint: disable=broad-except - log.error( - 'The return failed for job %s: %s', - data['jid'], exc - ) + log.error("The return failed for job %s: %s", data["jid"], exc) - def _return_pub(self, ret, ret_cmd='_return', timeout=60, sync=True): - ''' + def _return_pub(self, ret, ret_cmd="_return", timeout=60, sync=True): + """ Return the data from the executed command to the master server - ''' - jid = ret.get('jid', ret.get('__jid__')) - fun = ret.get('fun', ret.get('__fun__')) - if self.opts['multiprocessing']: + """ + jid = ret.get("jid", ret.get("__jid__")) + fun = ret.get("fun", ret.get("__fun__")) + if self.opts["multiprocessing"]: fn_ = os.path.join(self.proc_dir, jid) if os.path.isfile(fn_): try: @@ -1946,38 +2150,36 @@ class Minion(MinionBase): except (OSError, IOError): # The file is gone already pass - log.info('Returning information for job: %s', jid) - log.trace('Return data: %s', ret) - if ret_cmd == '_syndic_return': - load = {'cmd': ret_cmd, - 'id': self.opts['uid'], - 'jid': jid, - 'fun': fun, - 'arg': ret.get('arg'), - 'tgt': ret.get('tgt'), - 'tgt_type': ret.get('tgt_type'), - 'load': ret.get('__load__')} - if '__master_id__' in ret: - load['master_id'] = ret['__master_id__'] - load['return'] = {} + log.info("Returning information for job: %s", jid) + log.trace("Return data: %s", ret) + if ret_cmd == "_syndic_return": + load = { + "cmd": ret_cmd, + "id": self.opts["uid"], + "jid": jid, + "fun": fun, + "arg": ret.get("arg"), + "tgt": ret.get("tgt"), + "tgt_type": ret.get("tgt_type"), + "load": ret.get("__load__"), + } + if "__master_id__" in ret: + load["master_id"] = ret["__master_id__"] + load["return"] = {} for key, value in six.iteritems(ret): - if key.startswith('__'): + if key.startswith("__"): continue - load['return'][key] = value + load["return"][key] = value else: - load = {'cmd': ret_cmd, - 'id': self.opts['id']} + load = {"cmd": ret_cmd, "id": self.opts["id"]} for key, value in six.iteritems(ret): load[key] = value - if 'out' in ret: - if isinstance(ret['out'], six.string_types): - load['out'] = ret['out'] + if "out" in ret: + if isinstance(ret["out"], six.string_types): + load["out"] = ret["out"] else: - log.error( - 'Invalid outputter %s. This is likely a bug.', - ret['out'] - ) + log.error("Invalid outputter %s. This is likely a bug.", ret["out"]) else: try: oput = self.functions[fun].__outputter__ @@ -1985,22 +2187,23 @@ class Minion(MinionBase): pass else: if isinstance(oput, six.string_types): - load['out'] = oput - if self.opts['cache_jobs']: + load["out"] = oput + if self.opts["cache_jobs"]: # Local job cache has been enabled - if ret['jid'] == 'req': - ret['jid'] = salt.utils.jid.gen_jid(self.opts) - salt.utils.minion.cache_jobs(self.opts, ret['jid'], ret) + if ret["jid"] == "req": + ret["jid"] = salt.utils.jid.gen_jid(self.opts) + salt.utils.minion.cache_jobs(self.opts, ret["jid"], ret) - if not self.opts['pub_ret']: - return '' + if not self.opts["pub_ret"]: + return "" def timeout_handler(*_): log.warning( - 'The minion failed to return the job information for job %s. ' - 'This is often due to the master being shut down or ' - 'overloaded. If the master is running, consider increasing ' - 'the worker_threads value.', jid + "The minion failed to return the job information for job %s. " + "This is often due to the master being shut down or " + "overloaded. If the master is running, consider increasing " + "the worker_threads value.", + jid, ) return True @@ -2009,25 +2212,29 @@ class Minion(MinionBase): ret_val = self._send_req_sync(load, timeout=timeout) except SaltReqTimeoutError: timeout_handler() - return '' + return "" else: with salt.ext.tornado.stack_context.ExceptionStackContext(timeout_handler): - ret_val = self._send_req_async(load, timeout=timeout, callback=lambda f: None) # pylint: disable=unexpected-keyword-arg + # pylint: disable=unexpected-keyword-arg + ret_val = self._send_req_async( + load, timeout=timeout, callback=lambda f: None + ) + # pylint: enable=unexpected-keyword-arg - log.trace('ret_val = %s', ret_val) # pylint: disable=no-member + log.trace("ret_val = %s", ret_val) # pylint: disable=no-member return ret_val - def _return_pub_multi(self, rets, ret_cmd='_return', timeout=60, sync=True): - ''' + def _return_pub_multi(self, rets, ret_cmd="_return", timeout=60, sync=True): + """ Return the data from the executed command to the master server - ''' + """ if not isinstance(rets, list): rets = [rets] jids = {} for ret in rets: - jid = ret.get('jid', ret.get('__jid__')) - fun = ret.get('fun', ret.get('__fun__')) - if self.opts['multiprocessing']: + jid = ret.get("jid", ret.get("__jid__")) + fun = ret.get("fun", ret.get("__fun__")) + if self.opts["multiprocessing"]: fn_ = os.path.join(self.proc_dir, jid) if os.path.isfile(fn_): try: @@ -2035,37 +2242,38 @@ class Minion(MinionBase): except (OSError, IOError): # The file is gone already pass - log.info('Returning information for job: %s', jid) + log.info("Returning information for job: %s", jid) load = jids.setdefault(jid, {}) - if ret_cmd == '_syndic_return': + if ret_cmd == "_syndic_return": if not load: - load.update({'id': self.opts['id'], - 'jid': jid, - 'fun': fun, - 'arg': ret.get('arg'), - 'tgt': ret.get('tgt'), - 'tgt_type': ret.get('tgt_type'), - 'load': ret.get('__load__'), - 'return': {}}) - if '__master_id__' in ret: - load['master_id'] = ret['__master_id__'] + load.update( + { + "id": self.opts["id"], + "jid": jid, + "fun": fun, + "arg": ret.get("arg"), + "tgt": ret.get("tgt"), + "tgt_type": ret.get("tgt_type"), + "load": ret.get("__load__"), + "return": {}, + } + ) + if "__master_id__" in ret: + load["master_id"] = ret["__master_id__"] for key, value in six.iteritems(ret): - if key.startswith('__'): + if key.startswith("__"): continue - load['return'][key] = value + load["return"][key] = value else: - load.update({'id': self.opts['id']}) + load.update({"id": self.opts["id"]}) for key, value in six.iteritems(ret): load[key] = value - if 'out' in ret: - if isinstance(ret['out'], six.string_types): - load['out'] = ret['out'] + if "out" in ret: + if isinstance(ret["out"], six.string_types): + load["out"] = ret["out"] else: - log.error( - 'Invalid outputter %s. This is likely a bug.', - ret['out'] - ) + log.error("Invalid outputter %s. This is likely a bug.", ret["out"]) else: try: oput = self.functions[fun].__outputter__ @@ -2073,20 +2281,20 @@ class Minion(MinionBase): pass else: if isinstance(oput, six.string_types): - load['out'] = oput - if self.opts['cache_jobs']: + load["out"] = oput + if self.opts["cache_jobs"]: # Local job cache has been enabled - salt.utils.minion.cache_jobs(self.opts, load['jid'], ret) + salt.utils.minion.cache_jobs(self.opts, load["jid"], ret) - load = {'cmd': ret_cmd, - 'load': list(six.itervalues(jids))} + load = {"cmd": ret_cmd, "load": list(six.itervalues(jids))} def timeout_handler(*_): log.warning( - 'The minion failed to return the job information for job %s. ' - 'This is often due to the master being shut down or ' - 'overloaded. If the master is running, consider increasing ' - 'the worker_threads value.', jid + "The minion failed to return the job information for job %s. " + "This is often due to the master being shut down or " + "overloaded. If the master is running, consider increasing " + "the worker_threads value.", + jid, ) return True @@ -2095,362 +2303,391 @@ class Minion(MinionBase): ret_val = self._send_req_sync(load, timeout=timeout) except SaltReqTimeoutError: timeout_handler() - return '' + return "" else: with salt.ext.tornado.stack_context.ExceptionStackContext(timeout_handler): - ret_val = self._send_req_async(load, timeout=timeout, callback=lambda f: None) # pylint: disable=unexpected-keyword-arg + # pylint: disable=unexpected-keyword-arg + ret_val = self._send_req_async( + load, timeout=timeout, callback=lambda f: None + ) + # pylint: enable=unexpected-keyword-arg - log.trace('ret_val = %s', ret_val) # pylint: disable=no-member + log.trace("ret_val = %s", ret_val) # pylint: disable=no-member return ret_val def _state_run(self): - ''' + """ Execute a state run based on information set in the minion config file - ''' - if self.opts['startup_states']: - if self.opts.get('master_type', 'str') == 'disable' and \ - self.opts.get('file_client', 'remote') == 'remote': + """ + if self.opts["startup_states"]: + if ( + self.opts.get("master_type", "str") == "disable" + and self.opts.get("file_client", "remote") == "remote" + ): log.warning( - 'Cannot run startup_states when \'master_type\' is set ' - 'to \'disable\' and \'file_client\' is set to ' - '\'remote\'. Skipping.' + "Cannot run startup_states when 'master_type' is set " + "to 'disable' and 'file_client' is set to " + "'remote'. Skipping." ) else: - data = {'jid': 'req', 'ret': self.opts.get('ext_job_cache', '')} - if self.opts['startup_states'] == 'sls': - data['fun'] = 'state.sls' - data['arg'] = [self.opts['sls_list']] - elif self.opts['startup_states'] == 'top': - data['fun'] = 'state.top' - data['arg'] = [self.opts['top_file']] + data = {"jid": "req", "ret": self.opts.get("ext_job_cache", "")} + if self.opts["startup_states"] == "sls": + data["fun"] = "state.sls" + data["arg"] = [self.opts["sls_list"]] + elif self.opts["startup_states"] == "top": + data["fun"] = "state.top" + data["arg"] = [self.opts["top_file"]] else: - data['fun'] = 'state.highstate' - data['arg'] = [] + data["fun"] = "state.highstate" + data["arg"] = [] self._handle_decoded_payload(data) def _refresh_grains_watcher(self, refresh_interval_in_minutes): - ''' + """ Create a loop that will fire a pillar refresh to inform a master about a change in the grains of this minion :param refresh_interval_in_minutes: :return: None - ''' - if '__update_grains' not in self.opts.get('schedule', {}): - if 'schedule' not in self.opts: - self.opts['schedule'] = {} - self.opts['schedule'].update({ - '__update_grains': - { - 'function': 'event.fire', - 'args': [{}, 'grains_refresh'], - 'minutes': refresh_interval_in_minutes + """ + if "__update_grains" not in self.opts.get("schedule", {}): + if "schedule" not in self.opts: + self.opts["schedule"] = {} + self.opts["schedule"].update( + { + "__update_grains": { + "function": "event.fire", + "args": [{}, "grains_refresh"], + "minutes": refresh_interval_in_minutes, } - }) + } + ) def _fire_master_minion_start(self): include_grains = False - if self.opts['start_event_grains']: + if self.opts["start_event_grains"]: include_grains = True # Send an event to the master that the minion is live - if self.opts['enable_legacy_startup_events']: + if self.opts["enable_legacy_startup_events"]: # Old style event. Defaults to False in Sodium release. self._fire_master( - 'Minion {0} started at {1}'.format( - self.opts['id'], - time.asctime() - ), - 'minion_start', - include_startup_grains=include_grains + "Minion {0} started at {1}".format(self.opts["id"], time.asctime()), + "minion_start", + include_startup_grains=include_grains, ) # send name spaced event self._fire_master( - 'Minion {0} started at {1}'.format( - self.opts['id'], - time.asctime() - ), - tagify([self.opts['id'], 'start'], 'minion'), - include_startup_grains=include_grains + "Minion {0} started at {1}".format(self.opts["id"], time.asctime()), + tagify([self.opts["id"], "start"], "minion"), + include_startup_grains=include_grains, ) def module_refresh(self, force_refresh=False, notify=False): - ''' + """ Refresh the functions and returners. - ''' - log.debug('Refreshing modules. Notify=%s', notify) - self.functions, self.returners, _, self.executors = self._load_modules(force_refresh, notify=notify) + """ + log.debug("Refreshing modules. Notify=%s", notify) + self.functions, self.returners, _, self.executors = self._load_modules( + force_refresh, notify=notify + ) self.schedule.functions = self.functions self.schedule.returners = self.returners def beacons_refresh(self): - ''' + """ Refresh the functions and returners. - ''' + """ if not self.beacons_leader: return - log.debug('Refreshing beacons.') + log.debug("Refreshing beacons.") self.beacons = salt.beacons.Beacon(self.opts, self.functions) def matchers_refresh(self): - ''' + """ Refresh the matchers - ''' - log.debug('Refreshing matchers.') + """ + log.debug("Refreshing matchers.") self.matchers = salt.loader.matchers(self.opts) # TODO: only allow one future in flight at a time? @salt.ext.tornado.gen.coroutine def pillar_refresh(self, force_refresh=False): - ''' + """ Refresh the pillar - ''' + """ self.module_refresh(force_refresh) if self.connected: - log.debug('Refreshing pillar') + log.debug("Refreshing pillar") async_pillar = salt.pillar.get_async_pillar( self.opts, - self.opts['grains'], - self.opts['id'], - self.opts['saltenv'], - pillarenv=self.opts.get('pillarenv'), + self.opts["grains"], + self.opts["id"], + self.opts["saltenv"], + pillarenv=self.opts.get("pillarenv"), ) try: - self.opts['pillar'] = yield async_pillar.compile_pillar() + self.opts["pillar"] = yield async_pillar.compile_pillar() except SaltClientError: # Do not exit if a pillar refresh fails. - log.error('Pillar data could not be refreshed. ' - 'One or more masters may be down!') + log.error( + "Pillar data could not be refreshed. " + "One or more masters may be down!" + ) finally: async_pillar.destroy() self.matchers_refresh() self.beacons_refresh() - evt = salt.utils.event.get_event('minion', opts=self.opts) - evt.fire_event({'complete': True}, tag='/salt/minion/minion_pillar_refresh_complete') + evt = salt.utils.event.get_event("minion", opts=self.opts) + evt.fire_event( + {"complete": True}, tag="/salt/minion/minion_pillar_refresh_complete" + ) def manage_schedule(self, tag, data): - ''' + """ Refresh the functions and returners. - ''' - func = data.get('func', None) - name = data.get('name', None) - schedule = data.get('schedule', None) - where = data.get('where', None) - persist = data.get('persist', None) + """ + func = data.get("func", None) + name = data.get("name", None) + schedule = data.get("schedule", None) + where = data.get("where", None) + persist = data.get("persist", None) - if func == 'delete': + if func == "delete": self.schedule.delete_job(name, persist) - elif func == 'add': + elif func == "add": self.schedule.add_job(schedule, persist) - elif func == 'modify': + elif func == "modify": self.schedule.modify_job(name, schedule, persist) - elif func == 'enable': + elif func == "enable": self.schedule.enable_schedule(persist) - elif func == 'disable': + elif func == "disable": self.schedule.disable_schedule(persist) - elif func == 'enable_job': + elif func == "enable_job": self.schedule.enable_job(name, persist) - elif func == 'run_job': + elif func == "run_job": self.schedule.run_job(name) - elif func == 'disable_job': + elif func == "disable_job": self.schedule.disable_job(name, persist) - elif func == 'postpone_job': + elif func == "postpone_job": self.schedule.postpone_job(name, data) - elif func == 'skip_job': + elif func == "skip_job": self.schedule.skip_job(name, data) - elif func == 'reload': + elif func == "reload": self.schedule.reload(schedule) - elif func == 'list': + elif func == "list": self.schedule.list(where) - elif func == 'save_schedule': + elif func == "save_schedule": self.schedule.save_schedule() - elif func == 'get_next_fire_time': + elif func == "get_next_fire_time": self.schedule.get_next_fire_time(name) def manage_beacons(self, tag, data): - ''' + """ Manage Beacons - ''' + """ if not self.beacons_leader: return - func = data.get('func', None) - name = data.get('name', None) - beacon_data = data.get('beacon_data', None) - include_pillar = data.get('include_pillar', None) - include_opts = data.get('include_opts', None) + func = data.get("func", None) + name = data.get("name", None) + beacon_data = data.get("beacon_data", None) + include_pillar = data.get("include_pillar", None) + include_opts = data.get("include_opts", None) - if func == 'add': + if func == "add": self.beacons.add_beacon(name, beacon_data) - elif func == 'modify': + elif func == "modify": self.beacons.modify_beacon(name, beacon_data) - elif func == 'delete': + elif func == "delete": self.beacons.delete_beacon(name) - elif func == 'enable': + elif func == "enable": self.beacons.enable_beacons() - elif func == 'disable': + elif func == "disable": self.beacons.disable_beacons() - elif func == 'enable_beacon': + elif func == "enable_beacon": self.beacons.enable_beacon(name) - elif func == 'disable_beacon': + elif func == "disable_beacon": self.beacons.disable_beacon(name) - elif func == 'list': - self.beacons.list_beacons(include_opts=include_opts, - include_pillar=include_pillar) - elif func == 'list_available': + elif func == "list": + self.beacons.list_beacons( + include_opts=include_opts, include_pillar=include_pillar + ) + elif func == "list_available": self.beacons.list_available_beacons() - elif func == 'validate_beacon': + elif func == "validate_beacon": self.beacons.validate_beacon(name, beacon_data) - elif func == 'reset': + elif func == "reset": self.beacons.reset() def environ_setenv(self, tag, data): - ''' + """ Set the salt-minion main process environment according to the data contained in the minion event data - ''' - environ = data.get('environ', None) + """ + environ = data.get("environ", None) if environ is None: return False - false_unsets = data.get('false_unsets', False) - clear_all = data.get('clear_all', False) + false_unsets = data.get("false_unsets", False) + clear_all = data.get("clear_all", False) import salt.modules.environ as mod_environ + return mod_environ.setenv(environ, false_unsets, clear_all) def _pre_tune(self): - ''' + """ Set the minion running flag and issue the appropriate warnings if the minion cannot be started or is already running - ''' + """ if self._running is None: self._running = True elif self._running is False: log.error( - 'This %s was scheduled to stop. Not running %s.tune_in()', - self.__class__.__name__, self.__class__.__name__ + "This %s was scheduled to stop. Not running %s.tune_in()", + self.__class__.__name__, + self.__class__.__name__, ) return elif self._running is True: log.error( - 'This %s is already running. Not running %s.tune_in()', - self.__class__.__name__, self.__class__.__name__ + "This %s is already running. Not running %s.tune_in()", + self.__class__.__name__, + self.__class__.__name__, ) return try: log.info( - '%s is starting as user \'%s\'', - self.__class__.__name__, salt.utils.user.get_user() + "%s is starting as user '%s'", + self.__class__.__name__, + salt.utils.user.get_user(), ) except Exception as err: # pylint: disable=broad-except # Only windows is allowed to fail here. See #3189. Log as debug in # that case. Else, error. log.log( salt.utils.platform.is_windows() and logging.DEBUG or logging.ERROR, - 'Failed to get the user who is starting %s', + "Failed to get the user who is starting %s", self.__class__.__name__, - exc_info=err + exc_info=err, ) def _mine_send(self, tag, data): - ''' + """ Send mine data to the master - ''' + """ with salt.transport.client.ReqChannel.factory(self.opts) as channel: - data['tok'] = self.tok + data["tok"] = self.tok try: ret = channel.send(data) return ret except SaltReqTimeoutError: - log.warning('Unable to send mine data to master.') + log.warning("Unable to send mine data to master.") return None @salt.ext.tornado.gen.coroutine def handle_event(self, package): - ''' + """ Handle an event from the epull_sock (all local minion events) - ''' + """ if not self.ready: raise salt.ext.tornado.gen.Return() tag, data = salt.utils.event.SaltEvent.unpack(package) - log.debug( - 'Minion of \'%s\' is handling event tag \'%s\'', - self.opts['master'], tag - ) - if tag.startswith('module_refresh'): + log.debug("Minion of '%s' is handling event tag '%s'", self.opts["master"], tag) + if tag.startswith("module_refresh"): self.module_refresh( - force_refresh=data.get('force_refresh', False), - notify=data.get('notify', False) + force_refresh=data.get("force_refresh", False), + notify=data.get("notify", False), ) - elif tag.startswith('pillar_refresh'): - yield self.pillar_refresh( - force_refresh=data.get('force_refresh', False) - ) - elif tag.startswith('beacons_refresh'): + elif tag.startswith("pillar_refresh"): + yield self.pillar_refresh(force_refresh=data.get("force_refresh", False)) + elif tag.startswith("beacons_refresh"): self.beacons_refresh() - elif tag.startswith('matchers_refresh'): + elif tag.startswith("matchers_refresh"): self.matchers_refresh() - elif tag.startswith('manage_schedule'): + elif tag.startswith("manage_schedule"): self.manage_schedule(tag, data) - elif tag.startswith('manage_beacons'): + elif tag.startswith("manage_beacons"): self.manage_beacons(tag, data) - elif tag.startswith('grains_refresh'): - if (data.get('force_refresh', False) or - self.grains_cache != self.opts['grains']): + elif tag.startswith("grains_refresh"): + if ( + data.get("force_refresh", False) + or self.grains_cache != self.opts["grains"] + ): self.pillar_refresh(force_refresh=True) - self.grains_cache = self.opts['grains'] - elif tag.startswith('environ_setenv'): + self.grains_cache = self.opts["grains"] + elif tag.startswith("environ_setenv"): self.environ_setenv(tag, data) - elif tag.startswith('_minion_mine'): + elif tag.startswith("_minion_mine"): self._mine_send(tag, data) - elif tag.startswith('fire_master'): + elif tag.startswith("fire_master"): if self.connected: - log.debug('Forwarding master event tag=%s', data['tag']) - self._fire_master(data['data'], data['tag'], data['events'], data['pretag'], sync=False) - elif tag.startswith(master_event(type='disconnected')) or tag.startswith(master_event(type='failback')): + log.debug("Forwarding master event tag=%s", data["tag"]) + self._fire_master( + data["data"], + data["tag"], + data["events"], + data["pretag"], + sync=False, + ) + elif tag.startswith(master_event(type="disconnected")) or tag.startswith( + master_event(type="failback") + ): # if the master disconnect event is for a different master, raise an exception - if tag.startswith(master_event(type='disconnected')) and data['master'] != self.opts['master']: + if ( + tag.startswith(master_event(type="disconnected")) + and data["master"] != self.opts["master"] + ): # not mine master, ignore raise salt.ext.tornado.gen.Return() - if tag.startswith(master_event(type='failback')): + if tag.startswith(master_event(type="failback")): # if the master failback event is not for the top master, raise an exception - if data['master'] != self.opts['master_list'][0]: - raise SaltException('Bad master \'{0}\' when mine failback is \'{1}\''.format( - data['master'], self.opts['master'])) + if data["master"] != self.opts["master_list"][0]: + raise SaltException( + "Bad master '{0}' when mine failback is '{1}'".format( + data["master"], self.opts["master"] + ) + ) # if the master failback event is for the current master, raise an exception - elif data['master'] == self.opts['master'][0]: - raise SaltException('Already connected to \'{0}\''.format(data['master'])) + elif data["master"] == self.opts["master"][0]: + raise SaltException( + "Already connected to '{0}'".format(data["master"]) + ) if self.connected: # we are not connected anymore self.connected = False - log.info('Connection to master %s lost', self.opts['master']) + log.info("Connection to master %s lost", self.opts["master"]) - if self.opts['master_type'] != 'failover': + if self.opts["master_type"] != "failover": # modify the scheduled job to fire on reconnect - if self.opts['transport'] != 'tcp': + if self.opts["transport"] != "tcp": schedule = { - 'function': 'status.master', - 'seconds': self.opts['master_alive_interval'], - 'jid_include': True, - 'maxrunning': 1, - 'return_job': False, - 'kwargs': {'master': self.opts['master'], - 'connected': False} + "function": "status.master", + "seconds": self.opts["master_alive_interval"], + "jid_include": True, + "maxrunning": 1, + "return_job": False, + "kwargs": { + "master": self.opts["master"], + "connected": False, + }, } - self.schedule.modify_job(name=master_event(type='alive', master=self.opts['master']), - schedule=schedule) + self.schedule.modify_job( + name=master_event(type="alive", master=self.opts["master"]), + schedule=schedule, + ) else: # delete the scheduled job to don't interfere with the failover process - if self.opts['transport'] != 'tcp': - self.schedule.delete_job(name=master_event(type='alive')) + if self.opts["transport"] != "tcp": + self.schedule.delete_job(name=master_event(type="alive")) - log.info('Trying to tune in to next master from master-list') + log.info("Trying to tune in to next master from master-list") - if hasattr(self, 'pub_channel'): + if hasattr(self, "pub_channel"): self.pub_channel.on_recv(None) - if hasattr(self.pub_channel, 'auth'): + if hasattr(self.pub_channel, "auth"): self.pub_channel.auth.invalidate() - if hasattr(self.pub_channel, 'close'): + if hasattr(self.pub_channel, "close"): self.pub_channel.close() del self.pub_channel @@ -2458,113 +2695,141 @@ class Minion(MinionBase): # will be True again on successful master authentication try: master, self.pub_channel = yield self.eval_master( - opts=self.opts, - failed=True, - failback=tag.startswith(master_event(type='failback'))) + opts=self.opts, + failed=True, + failback=tag.startswith(master_event(type="failback")), + ) except SaltClientError: pass if self.connected: - self.opts['master'] = master + self.opts["master"] = master # re-init the subsystems to work with the new master log.info( - 'Re-initialising subsystems for new master %s', - self.opts['master'] + "Re-initialising subsystems for new master %s", + self.opts["master"], ) # put the current schedule into the new loaders - self.opts['schedule'] = self.schedule.option('schedule') - self.functions, self.returners, self.function_errors, self.executors = self._load_modules() + self.opts["schedule"] = self.schedule.option("schedule") + ( + self.functions, + self.returners, + self.function_errors, + self.executors, + ) = self._load_modules() # make the schedule to use the new 'functions' loader self.schedule.functions = self.functions self.pub_channel.on_recv(self._handle_payload) self._fire_master_minion_start() - log.info('Minion is ready to receive requests!') + log.info("Minion is ready to receive requests!") # update scheduled job to run with the new master addr - if self.opts['transport'] != 'tcp': + if self.opts["transport"] != "tcp": schedule = { - 'function': 'status.master', - 'seconds': self.opts['master_alive_interval'], - 'jid_include': True, - 'maxrunning': 1, - 'return_job': False, - 'kwargs': {'master': self.opts['master'], - 'connected': True} + "function": "status.master", + "seconds": self.opts["master_alive_interval"], + "jid_include": True, + "maxrunning": 1, + "return_job": False, + "kwargs": { + "master": self.opts["master"], + "connected": True, + }, } - self.schedule.modify_job(name=master_event(type='alive', master=self.opts['master']), - schedule=schedule) + self.schedule.modify_job( + name=master_event( + type="alive", master=self.opts["master"] + ), + schedule=schedule, + ) - if self.opts['master_failback'] and 'master_list' in self.opts: - if self.opts['master'] != self.opts['master_list'][0]: + if ( + self.opts["master_failback"] + and "master_list" in self.opts + ): + if self.opts["master"] != self.opts["master_list"][0]: schedule = { - 'function': 'status.ping_master', - 'seconds': self.opts['master_failback_interval'], - 'jid_include': True, - 'maxrunning': 1, - 'return_job': False, - 'kwargs': {'master': self.opts['master_list'][0]} + "function": "status.ping_master", + "seconds": self.opts[ + "master_failback_interval" + ], + "jid_include": True, + "maxrunning": 1, + "return_job": False, + "kwargs": { + "master": self.opts["master_list"][0] + }, } - self.schedule.modify_job(name=master_event(type='failback'), - schedule=schedule) + self.schedule.modify_job( + name=master_event(type="failback"), + schedule=schedule, + ) else: - self.schedule.delete_job(name=master_event(type='failback'), persist=True) + self.schedule.delete_job( + name=master_event(type="failback"), persist=True + ) else: self.restart = True self.io_loop.stop() - elif tag.startswith(master_event(type='connected')): + elif tag.startswith(master_event(type="connected")): # handle this event only once. otherwise it will pollute the log # also if master type is failover all the reconnection work is done # by `disconnected` event handler and this event must never happen, # anyway check it to be sure - if not self.connected and self.opts['master_type'] != 'failover': - log.info('Connection to master %s re-established', self.opts['master']) + if not self.connected and self.opts["master_type"] != "failover": + log.info("Connection to master %s re-established", self.opts["master"]) self.connected = True # modify the __master_alive job to only fire, # if the connection is lost again - if self.opts['transport'] != 'tcp': + if self.opts["transport"] != "tcp": schedule = { - 'function': 'status.master', - 'seconds': self.opts['master_alive_interval'], - 'jid_include': True, - 'maxrunning': 1, - 'return_job': False, - 'kwargs': {'master': self.opts['master'], - 'connected': True} + "function": "status.master", + "seconds": self.opts["master_alive_interval"], + "jid_include": True, + "maxrunning": 1, + "return_job": False, + "kwargs": {"master": self.opts["master"], "connected": True}, } - self.schedule.modify_job(name=master_event(type='alive', master=self.opts['master']), - schedule=schedule) - elif tag.startswith('__schedule_return'): - # reporting current connection with master - if data['schedule'].startswith(master_event(type='alive', master='')): - if data['return']: - log.debug( - 'Connected to master %s', - data['schedule'].split(master_event(type='alive', master=''))[1] + self.schedule.modify_job( + name=master_event(type="alive", master=self.opts["master"]), + schedule=schedule, ) - self._return_pub(data, ret_cmd='_return', sync=False) - elif tag.startswith('_salt_error'): + elif tag.startswith("__schedule_return"): + # reporting current connection with master + if data["schedule"].startswith(master_event(type="alive", master="")): + if data["return"]: + log.debug( + "Connected to master %s", + data["schedule"].split(master_event(type="alive", master=""))[ + 1 + ], + ) + self._return_pub(data, ret_cmd="_return", sync=False) + elif tag.startswith("_salt_error"): if self.connected: - log.debug('Forwarding salt error event tag=%s', tag) + log.debug("Forwarding salt error event tag=%s", tag) self._fire_master(data, tag, sync=False) - elif tag.startswith('salt/auth/creds'): - key = tuple(data['key']) + elif tag.startswith("salt/auth/creds"): + key = tuple(data["key"]) log.debug( - 'Updating auth data for %s: %s -> %s', - key, salt.crypt.AsyncAuth.creds_map.get(key), data['creds'] + "Updating auth data for %s: %s -> %s", + key, + salt.crypt.AsyncAuth.creds_map.get(key), + data["creds"], ) - salt.crypt.AsyncAuth.creds_map[tuple(data['key'])] = data['creds'] - elif tag.startswith('__beacons_return'): + salt.crypt.AsyncAuth.creds_map[tuple(data["key"])] = data["creds"] + elif tag.startswith("__beacons_return"): if self.connected: - log.debug('Firing beacons to master') - self._fire_master(events=data['beacons']) + log.debug("Firing beacons to master") + self._fire_master(events=data["beacons"]) def cleanup_subprocesses(self): - ''' + """ Clean up subprocesses and spawned threads. - ''' + """ # Add an extra fallback in case a forked process leaks through multiprocessing.active_children() self.subprocess_list.cleanup() @@ -2572,36 +2837,41 @@ class Minion(MinionBase): self.schedule.cleanup_subprocesses() def _setup_core(self): - ''' + """ Set up the core minion attributes. This is safe to call multiple times. - ''' + """ if not self.ready: # First call. Initialize. - self.functions, self.returners, self.function_errors, self.executors = self._load_modules() + ( + self.functions, + self.returners, + self.function_errors, + self.executors, + ) = self._load_modules() self.serial = salt.payload.Serial(self.opts) self.mod_opts = self._prep_mod_opts() -# self.matcher = Matcher(self.opts, self.functions) + # self.matcher = Matcher(self.opts, self.functions) self.matchers = salt.loader.matchers(self.opts) if self.beacons_leader: self.beacons = salt.beacons.Beacon(self.opts, self.functions) - uid = salt.utils.user.get_uid(user=self.opts.get('user', None)) - self.proc_dir = get_proc_dir(self.opts['cachedir'], uid=uid) - self.grains_cache = self.opts['grains'] + uid = salt.utils.user.get_uid(user=self.opts.get("user", None)) + self.proc_dir = get_proc_dir(self.opts["cachedir"], uid=uid) + self.grains_cache = self.opts["grains"] self.ready = True def setup_beacons(self, before_connect=False): - ''' + """ Set up the beacons. This is safe to call multiple times. - ''' + """ # In multimaster configuration the only one minion shall execute beacons if not self.beacons_leader: return self._setup_core() - loop_interval = self.opts['loop_interval'] - if 'beacons' not in self.periodic_callbacks: + loop_interval = self.opts["loop_interval"] + if "beacons" not in self.periodic_callbacks: self.beacons = salt.beacons.Beacon(self.opts, self.functions) def handle_beacons(): @@ -2610,51 +2880,53 @@ class Minion(MinionBase): try: beacons = self.process_beacons(self.functions) except Exception: # pylint: disable=broad-except - log.critical('The beacon errored: ', exc_info=True) + log.critical("The beacon errored: ", exc_info=True) if beacons: - event = salt.utils.event.get_event('minion', - opts=self.opts, - listen=False) - event.fire_event({'beacons': beacons}, '__beacons_return') + event = salt.utils.event.get_event( + "minion", opts=self.opts, listen=False + ) + event.fire_event({"beacons": beacons}, "__beacons_return") event.destroy() if before_connect: # Make sure there is a chance for one iteration to occur before connect handle_beacons() - self.add_periodic_callback('beacons', handle_beacons) + self.add_periodic_callback("beacons", handle_beacons) def setup_scheduler(self, before_connect=False): - ''' + """ Set up the scheduler. This is safe to call multiple times. - ''' + """ self._setup_core() - loop_interval = self.opts['loop_interval'] + loop_interval = self.opts["loop_interval"] - if 'schedule' not in self.periodic_callbacks: - if 'schedule' not in self.opts: - self.opts['schedule'] = {} - if not hasattr(self, 'schedule'): + if "schedule" not in self.periodic_callbacks: + if "schedule" not in self.opts: + self.opts["schedule"] = {} + if not hasattr(self, "schedule"): self.schedule = salt.utils.schedule.Schedule( self.opts, self.functions, self.returners, utils=self.utils, - cleanup=[master_event(type='alive')]) + cleanup=[master_event(type="alive")], + ) try: - if self.opts['grains_refresh_every']: # In minutes, not seconds! + if self.opts["grains_refresh_every"]: # In minutes, not seconds! log.debug( - 'Enabling the grains refresher. Will run every %d minute(s).', - self.opts['grains_refresh_every'] + "Enabling the grains refresher. Will run every %d minute(s).", + self.opts["grains_refresh_every"], ) - self._refresh_grains_watcher(abs(self.opts['grains_refresh_every'])) + self._refresh_grains_watcher(abs(self.opts["grains_refresh_every"])) except Exception as exc: # pylint: disable=broad-except log.error( - 'Exception occurred in attempt to initialize grain refresh ' - 'routine during minion tune-in: %s', exc + "Exception occurred in attempt to initialize grain refresh " + "routine during minion tune-in: %s", + exc, ) # TODO: actually listen to the return and change period @@ -2665,13 +2937,13 @@ class Minion(MinionBase): # Make sure there is a chance for one iteration to occur before connect handle_schedule() - self.add_periodic_callback('schedule', handle_schedule) + self.add_periodic_callback("schedule", handle_schedule) def add_periodic_callback(self, name, method, interval=1): - ''' + """ Add a periodic callback to the event loop and call its start method. If a callback by the given name exists this method returns False - ''' + """ if name in self.periodic_callbacks: return False self.periodic_callbacks[name] = salt.ext.tornado.ioloop.PeriodicCallback( @@ -2681,10 +2953,10 @@ class Minion(MinionBase): return True def remove_periodic_callback(self, name): - ''' + """ Remove a periodic callback. If a callback by the given name does not exist this method returns False - ''' + """ callback = self.periodic_callbacks.pop(name, None) if callback is None: return False @@ -2693,23 +2965,23 @@ class Minion(MinionBase): # Main Minion Tune In def tune_in(self, start=True): - ''' + """ Lock onto the publisher. This is the main event loop for the minion :rtype : None - ''' + """ self._pre_tune() - log.debug('Minion \'%s\' trying to tune in', self.opts['id']) + log.debug("Minion '%s' trying to tune in", self.opts["id"]) if start: - if self.opts.get('beacons_before_connect', False): + if self.opts.get("beacons_before_connect", False): self.setup_beacons(before_connect=True) - if self.opts.get('scheduler_before_connect', False): + if self.opts.get("scheduler_before_connect", False): self.setup_scheduler(before_connect=True) self.sync_connect_master() if self.connected: self._fire_master_minion_start() - log.info('Minion is ready to receive requests!') + log.info("Minion is ready to receive requests!") # Make sure to gracefully handle SIGUSR1 enable_sigusr1_handler() @@ -2723,59 +2995,74 @@ class Minion(MinionBase): self.setup_beacons() self.setup_scheduler() - self.add_periodic_callback('cleanup', self.cleanup_subprocesses) + self.add_periodic_callback("cleanup", self.cleanup_subprocesses) # schedule the stuff that runs every interval - ping_interval = self.opts.get('ping_interval', 0) * 60 + ping_interval = self.opts.get("ping_interval", 0) * 60 if ping_interval > 0 and self.connected: + def ping_master(): try: + def ping_timeout_handler(*_): - if self.opts.get('auth_safemode', False): - log.error('** Master Ping failed. Attempting to restart minion**') - delay = self.opts.get('random_reauth_delay', 5) - log.info('delaying random_reauth_delay %ss', delay) + if self.opts.get("auth_safemode", False): + log.error( + "** Master Ping failed. Attempting to restart minion**" + ) + delay = self.opts.get("random_reauth_delay", 5) + log.info("delaying random_reauth_delay %ss", delay) try: - self.functions['service.restart'](service_name()) + self.functions["service.restart"](service_name()) except KeyError: # Probably no init system (running in docker?) log.warning( - 'ping_interval reached without response ' - 'from the master, but service.restart ' - 'could not be run to restart the minion ' - 'daemon. ping_interval requires that the ' - 'minion is running under an init system.' + "ping_interval reached without response " + "from the master, but service.restart " + "could not be run to restart the minion " + "daemon. ping_interval requires that the " + "minion is running under an init system." ) - self._fire_master('ping', 'minion_ping', sync=False, timeout_handler=ping_timeout_handler) + self._fire_master( + "ping", + "minion_ping", + sync=False, + timeout_handler=ping_timeout_handler, + ) except Exception: # pylint: disable=broad-except - log.warning('Attempt to ping master failed.', exc_on_loglevel=logging.DEBUG) - self.remove_periodic_callback('ping') - self.add_periodic_callback('ping', ping_master, ping_interval) + log.warning( + "Attempt to ping master failed.", exc_on_loglevel=logging.DEBUG + ) + + self.remove_periodic_callback("ping") + self.add_periodic_callback("ping", ping_master, ping_interval) # add handler to subscriber - if hasattr(self, 'pub_channel') and self.pub_channel is not None: + if hasattr(self, "pub_channel") and self.pub_channel is not None: self.pub_channel.on_recv(self._handle_payload) - elif self.opts.get('master_type') != 'disable': - log.error('No connection to master found. Scheduled jobs will not run.') + elif self.opts.get("master_type") != "disable": + log.error("No connection to master found. Scheduled jobs will not run.") if start: try: self.io_loop.start() if self.restart: self.destroy() - except (KeyboardInterrupt, RuntimeError): # A RuntimeError can be re-raised by Tornado on shutdown + except ( + KeyboardInterrupt, + RuntimeError, + ): # A RuntimeError can be re-raised by Tornado on shutdown self.destroy() def _handle_payload(self, payload): - if payload is not None and payload['enc'] == 'aes': - if self._target_load(payload['load']): - self._handle_decoded_payload(payload['load']) - elif self.opts['zmq_filtering']: + if payload is not None and payload["enc"] == "aes": + if self._target_load(payload["load"]): + self._handle_decoded_payload(payload["load"]) + elif self.opts["zmq_filtering"]: # In the filtering enabled case, we'd like to know when minion sees something it shouldnt log.trace( - 'Broadcast message received not for this minion, Load: %s', - payload['load'] + "Broadcast message received not for this minion, Load: %s", + payload["load"], ) # If it's not AES, and thus has not been verified, we do nothing. # In the future, we could add support for some clearfuncs, but @@ -2783,8 +3070,12 @@ class Minion(MinionBase): def _target_load(self, load): # Verify that the publication is valid - if 'tgt' not in load or 'jid' not in load or 'fun' not in load \ - or 'arg' not in load: + if ( + "tgt" not in load + or "jid" not in load + or "fun" not in load + or "arg" not in load + ): return False # Verify that the publication applies to this minion @@ -2794,58 +3085,62 @@ class Minion(MinionBase): # pre-processing on the master and this minion should not see the # publication if the master does not determine that it should. - if 'tgt_type' in load: - match_func = self.matchers.get('{0}_match.match'.format(load['tgt_type']), None) + if "tgt_type" in load: + match_func = self.matchers.get( + "{0}_match.match".format(load["tgt_type"]), None + ) if match_func is None: return False - if load['tgt_type'] in ('grain', 'grain_pcre', 'pillar'): - delimiter = load.get('delimiter', DEFAULT_TARGET_DELIM) - if not match_func(load['tgt'], delimiter=delimiter): + if load["tgt_type"] in ("grain", "grain_pcre", "pillar"): + delimiter = load.get("delimiter", DEFAULT_TARGET_DELIM) + if not match_func(load["tgt"], delimiter=delimiter): return False - elif not match_func(load['tgt']): + elif not match_func(load["tgt"]): return False else: - if not self.matchers['glob_match.match'](load['tgt']): + if not self.matchers["glob_match.match"](load["tgt"]): return False return True def destroy(self): - ''' + """ Tear down the minion - ''' + """ if self._running is False: return self._running = False - if hasattr(self, 'schedule'): + if hasattr(self, "schedule"): del self.schedule - if hasattr(self, 'pub_channel') and self.pub_channel is not None: + if hasattr(self, "pub_channel") and self.pub_channel is not None: self.pub_channel.on_recv(None) - if hasattr(self.pub_channel, 'close'): + if hasattr(self.pub_channel, "close"): self.pub_channel.close() del self.pub_channel - if hasattr(self, 'periodic_callbacks'): + if hasattr(self, "periodic_callbacks"): for cb in six.itervalues(self.periodic_callbacks): cb.stop() # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 class Syndic(Minion): - ''' + """ Make a Syndic minion, this minion will use the minion keys on the master to authenticate with a higher level master. - ''' + """ + def __init__(self, opts, **kwargs): - self._syndic_interface = opts.get('interface') + self._syndic_interface = opts.get("interface") self._syndic = True # force auth_safemode True because Syndic don't support autorestart - opts['auth_safemode'] = True - opts['loop_interval'] = 1 + opts["auth_safemode"] = True + opts["loop_interval"] = 1 super(Syndic, self).__init__(opts, **kwargs) self.mminion = salt.minion.MasterMinion(opts) self.jid_forward_cache = set() @@ -2854,96 +3149,94 @@ class Syndic(Minion): self.pub_future = None def _handle_decoded_payload(self, data): - ''' + """ Override this method if you wish to handle the decoded data differently. - ''' + """ # TODO: even do this?? - data['to'] = int(data.get('to', self.opts['timeout'])) - 1 + data["to"] = int(data.get("to", self.opts["timeout"])) - 1 # Only forward the command if it didn't originate from ourselves - if data.get('master_id', 0) != self.opts.get('master_id', 1): + if data.get("master_id", 0) != self.opts.get("master_id", 1): self.syndic_cmd(data) def syndic_cmd(self, data): - ''' + """ Take the now clear load and forward it on to the client cmd - ''' + """ # Set up default tgt_type - if 'tgt_type' not in data: - data['tgt_type'] = 'glob' + if "tgt_type" not in data: + data["tgt_type"] = "glob" kwargs = {} # optionally add a few fields to the publish data - for field in ('master_id', # which master the job came from - 'user', # which user ran the job - ): + for field in ( + "master_id", # which master the job came from + "user", # which user ran the job + ): if field in data: kwargs[field] = data[field] def timeout_handler(*args): - log.warning('Unable to forward pub data: %s', args[1]) + log.warning("Unable to forward pub data: %s", args[1]) return True with salt.ext.tornado.stack_context.ExceptionStackContext(timeout_handler): - self.local.pub_async(data['tgt'], - data['fun'], - data['arg'], - data['tgt_type'], - data['ret'], - data['jid'], - data['to'], - io_loop=self.io_loop, - callback=lambda _: None, - **kwargs) + self.local.pub_async( + data["tgt"], + data["fun"], + data["arg"], + data["tgt_type"], + data["ret"], + data["jid"], + data["to"], + io_loop=self.io_loop, + callback=lambda _: None, + **kwargs + ) def fire_master_syndic_start(self): # Send an event to the master that the minion is live - if self.opts['enable_legacy_startup_events']: + if self.opts["enable_legacy_startup_events"]: # Old style event. Defaults to false in Sodium release. self._fire_master( - 'Syndic {0} started at {1}'.format( - self.opts['id'], - time.asctime() - ), - 'syndic_start', + "Syndic {0} started at {1}".format(self.opts["id"], time.asctime()), + "syndic_start", sync=False, ) self._fire_master( - 'Syndic {0} started at {1}'.format( - self.opts['id'], - time.asctime() - ), - tagify([self.opts['id'], 'start'], 'syndic'), + "Syndic {0} started at {1}".format(self.opts["id"], time.asctime()), + tagify([self.opts["id"], "start"], "syndic"), sync=False, ) # TODO: clean up docs def tune_in_no_block(self): - ''' + """ Executes the tune_in sequence but omits extra logging and the management of the event bus assuming that these are handled outside the tune_in sequence - ''' + """ # Instantiate the local client self.local = salt.client.get_local_client( - self.opts['_minion_conf_file'], io_loop=self.io_loop) + self.opts["_minion_conf_file"], io_loop=self.io_loop + ) # add handler to subscriber self.pub_channel.on_recv(self._process_cmd_socket) def _process_cmd_socket(self, payload): - if payload is not None and payload['enc'] == 'aes': - log.trace('Handling payload') - self._handle_decoded_payload(payload['load']) + if payload is not None and payload["enc"] == "aes": + log.trace("Handling payload") + self._handle_decoded_payload(payload["load"]) # If it's not AES, and thus has not been verified, we do nothing. # In the future, we could add support for some clearfuncs, but # the syndic currently has no need. @salt.ext.tornado.gen.coroutine def reconnect(self): - if hasattr(self, 'pub_channel'): + if hasattr(self, "pub_channel"): self.pub_channel.on_recv(None) - if hasattr(self.pub_channel, 'close'): + if hasattr(self.pub_channel, "close"): self.pub_channel.close() del self.pub_channel @@ -2952,29 +3245,29 @@ class Syndic(Minion): master, self.pub_channel = yield self.eval_master(opts=self.opts) if self.connected: - self.opts['master'] = master + self.opts["master"] = master self.pub_channel.on_recv(self._process_cmd_socket) - log.info('Minion is ready to receive requests!') + log.info("Minion is ready to receive requests!") raise salt.ext.tornado.gen.Return(self) def destroy(self): - ''' + """ Tear down the syndic minion - ''' + """ # We borrowed the local clients poller so give it back before # it's destroyed. Reset the local poller reference. super(Syndic, self).destroy() - if hasattr(self, 'local'): + if hasattr(self, "local"): del self.local - if hasattr(self, 'forward_events'): + if hasattr(self, "forward_events"): self.forward_events.stop() # TODO: need a way of knowing if the syndic connection is busted class SyndicManager(MinionBase): - ''' + """ Make a MultiMaster syndic minion, this minion will handle relaying jobs and returns from all minions connected to it to the list of masters it is connected to. @@ -2991,21 +3284,22 @@ class SyndicManager(MinionBase): calls (with varying timeouts along the way) this daemon does not handle failure well, it will (under most circumstances) stall the daemon for ~15s trying to forward events to the down master - ''' + """ + # time to connect to upstream master SYNDIC_CONNECT_TIMEOUT = 5 SYNDIC_EVENT_TIMEOUT = 5 def __init__(self, opts, io_loop=None): - opts['loop_interval'] = 1 + opts["loop_interval"] = 1 super(SyndicManager, self).__init__(opts) self.mminion = salt.minion.MasterMinion(opts) # sync (old behavior), cluster (only returns and publishes) - self.syndic_mode = self.opts.get('syndic_mode', 'sync') - self.syndic_failover = self.opts.get('syndic_failover', 'random') + self.syndic_mode = self.opts.get("syndic_mode", "sync") + self.syndic_failover = self.opts.get("syndic_failover", "random") - self.auth_wait = self.opts['acceptance_wait_time'] - self.max_auth_wait = self.opts['acceptance_wait_time_max'] + self.auth_wait = self.opts["acceptance_wait_time"] + self.max_auth_wait = self.opts["acceptance_wait_time_max"] self._has_master = threading.Event() self.jid_forward_cache = set() @@ -3027,37 +3321,35 @@ class SyndicManager(MinionBase): self.pub_futures = {} def _spawn_syndics(self): - ''' + """ Spawn all the coroutines which will sign in the syndics - ''' + """ self._syndics = OrderedDict() # mapping of opts['master'] -> syndic - masters = self.opts['master'] + masters = self.opts["master"] if not isinstance(masters, list): masters = [masters] for master in masters: s_opts = copy.copy(self.opts) - s_opts['master'] = master + s_opts["master"] = master self._syndics[master] = self._connect_syndic(s_opts) @salt.ext.tornado.gen.coroutine def _connect_syndic(self, opts): - ''' + """ Create a syndic, and asynchronously connect it to a master - ''' + """ last = 0 # never have we signed in - auth_wait = opts['acceptance_wait_time'] + auth_wait = opts["acceptance_wait_time"] failed = False while True: - log.debug( - 'Syndic attempting to connect to %s', - opts['master'] - ) + log.debug("Syndic attempting to connect to %s", opts["master"]) try: - syndic = Syndic(opts, - timeout=self.SYNDIC_CONNECT_TIMEOUT, - safe=False, - io_loop=self.io_loop, - ) + syndic = Syndic( + opts, + timeout=self.SYNDIC_CONNECT_TIMEOUT, + safe=False, + io_loop=self.io_loop, + ) yield syndic.connect_master(failed=failed) # set up the syndic to handle publishes (specifically not event forwarding) syndic.tune_in_no_block() @@ -3065,16 +3357,14 @@ class SyndicManager(MinionBase): # Send an event to the master that the minion is live syndic.fire_master_syndic_start() - log.info( - 'Syndic successfully connected to %s', - opts['master'] - ) + log.info("Syndic successfully connected to %s", opts["master"]) break except SaltClientError as exc: failed = True log.error( - 'Error while bringing up syndic for multi-syndic. Is the ' - 'master at %s responding?', opts['master'] + "Error while bringing up syndic for multi-syndic. Is the " + "master at %s responding?", + opts["master"], ) last = time.time() if auth_wait < self.max_auth_wait: @@ -3085,16 +3375,17 @@ class SyndicManager(MinionBase): except Exception: # pylint: disable=broad-except failed = True log.critical( - 'Unexpected error while connecting to %s', - opts['master'], exc_info=True + "Unexpected error while connecting to %s", + opts["master"], + exc_info=True, ) raise salt.ext.tornado.gen.Return(syndic) def _mark_master_dead(self, master): - ''' + """ Mark a master as dead. This will start the sign-in routine - ''' + """ # if its connected, mark it dead if self._syndics[master].done(): syndic = self._syndics[master].result() # pylint: disable=no-member @@ -3102,14 +3393,14 @@ class SyndicManager(MinionBase): else: # TODO: debug? log.info( - 'Attempting to mark %s as dead, although it is already ' - 'marked dead', master + "Attempting to mark %s as dead, although it is already " "marked dead", + master, ) def _call_syndic(self, func, args=(), kwargs=None, master_id=None): - ''' + """ Wrapper to call a given func on a syndic, best effort to get the one you asked for - ''' + """ if kwargs is None: kwargs = {} successful = False @@ -3117,8 +3408,9 @@ class SyndicManager(MinionBase): for master, syndic_future in self.iter_master_options(master_id): if not syndic_future.done() or syndic_future.exception(): log.error( - 'Unable to call %s on %s, that syndic is not connected', - func, master + "Unable to call %s on %s, that syndic is not connected", + func, + master, ) continue @@ -3126,24 +3418,22 @@ class SyndicManager(MinionBase): getattr(syndic_future.result(), func)(*args, **kwargs) successful = True except SaltClientError: - log.error( - 'Unable to call %s on %s, trying another...', - func, master - ) + log.error("Unable to call %s on %s, trying another...", func, master) self._mark_master_dead(master) if not successful: - log.critical('Unable to call %s on any masters!', func) + log.critical("Unable to call %s on any masters!", func) def _return_pub_syndic(self, values, master_id=None): - ''' + """ Wrapper to call the '_return_pub_multi' a syndic, best effort to get the one you asked for - ''' - func = '_return_pub_multi' + """ + func = "_return_pub_multi" for master, syndic_future in self.iter_master_options(master_id): if not syndic_future.done() or syndic_future.exception(): log.error( - 'Unable to call %s on %s, that syndic is not connected', - func, master + "Unable to call %s on %s, that syndic is not connected", + func, + master, ) continue @@ -3159,29 +3449,27 @@ class SyndicManager(MinionBase): elif future.exception(): # Previous execution on this master returned an error log.error( - 'Unable to call %s on %s, trying another...', - func, master + "Unable to call %s on %s, trying another...", func, master ) self._mark_master_dead(master) del self.pub_futures[master] # Add not sent data to the delayed list and try the next master self.delayed.extend(data) continue - future = getattr(syndic_future.result(), func)(values, - '_syndic_return', - timeout=self._return_retry_timer(), - sync=False) + future = getattr(syndic_future.result(), func)( + values, "_syndic_return", timeout=self._return_retry_timer(), sync=False + ) self.pub_futures[master] = (future, values) return True # Loop done and didn't exit: wasn't sent, try again later return False def iter_master_options(self, master_id=None): - ''' + """ Iterate (in order) over your options for master - ''' + """ masters = list(self._syndics.keys()) - if self.opts['syndic_failover'] == 'random': + if self.opts["syndic_failover"] == "random": shuffle(masters) if master_id not in self._syndics: master_id = masters.pop(0) @@ -3204,16 +3492,17 @@ class SyndicManager(MinionBase): # Syndic Tune In def tune_in(self): - ''' + """ Lock onto the publisher. This is the main event loop for the syndic - ''' + """ self._spawn_syndics() # Instantiate the local client self.local = salt.client.get_local_client( - self.opts['_minion_conf_file'], io_loop=self.io_loop) - self.local.event.subscribe('') + self.opts["_minion_conf_file"], io_loop=self.io_loop + ) + self.local.event.subscribe("") - log.debug('SyndicManager \'%s\' trying to tune in', self.opts['id']) + log.debug("SyndicManager '%s' trying to tune in", self.opts["id"]) # register the event sub to the poller self.job_rets = {} @@ -3223,9 +3512,9 @@ class SyndicManager(MinionBase): self.io_loop.add_future(future, self.reconnect_event_bus) # forward events every syndic_event_forward_timeout - self.forward_events = salt.ext.tornado.ioloop.PeriodicCallback(self._forward_events, - self.opts['syndic_event_forward_timeout'] * 1000, - ) + self.forward_events = salt.ext.tornado.ioloop.PeriodicCallback( + self._forward_events, self.opts["syndic_event_forward_timeout"] * 1000, + ) self.forward_events.start() # Make sure to gracefully handle SIGUSR1 @@ -3236,67 +3525,76 @@ class SyndicManager(MinionBase): def _process_event(self, raw): # TODO: cleanup: Move down into event class mtag, data = self.local.event.unpack(raw, self.local.event.serial) - log.trace('Got event %s', mtag) # pylint: disable=no-member + log.trace("Got event %s", mtag) # pylint: disable=no-member - tag_parts = mtag.split('/') - if len(tag_parts) >= 4 and tag_parts[1] == 'job' and \ - salt.utils.jid.is_jid(tag_parts[2]) and tag_parts[3] == 'ret' and \ - 'return' in data: - if 'jid' not in data: + tag_parts = mtag.split("/") + if ( + len(tag_parts) >= 4 + and tag_parts[1] == "job" + and salt.utils.jid.is_jid(tag_parts[2]) + and tag_parts[3] == "ret" + and "return" in data + ): + if "jid" not in data: # Not a job return return - if self.syndic_mode == 'cluster' and data.get('master_id', 0) == self.opts.get('master_id', 1): - log.debug('Return received with matching master_id, not forwarding') + if self.syndic_mode == "cluster" and data.get( + "master_id", 0 + ) == self.opts.get("master_id", 1): + log.debug("Return received with matching master_id, not forwarding") return - master = data.get('master_id') + master = data.get("master_id") jdict = self.job_rets.setdefault(master, {}).setdefault(mtag, {}) if not jdict: - jdict['__fun__'] = data.get('fun') - jdict['__jid__'] = data['jid'] - jdict['__load__'] = {} - fstr = '{0}.get_load'.format(self.opts['master_job_cache']) + jdict["__fun__"] = data.get("fun") + jdict["__jid__"] = data["jid"] + jdict["__load__"] = {} + fstr = "{0}.get_load".format(self.opts["master_job_cache"]) # Only need to forward each load once. Don't hit the disk # for every minion return! - if data['jid'] not in self.jid_forward_cache: - jdict['__load__'].update( - self.mminion.returners[fstr](data['jid']) - ) - self.jid_forward_cache.add(data['jid']) - if len(self.jid_forward_cache) > self.opts['syndic_jid_forward_cache_hwm']: + if data["jid"] not in self.jid_forward_cache: + jdict["__load__"].update(self.mminion.returners[fstr](data["jid"])) + self.jid_forward_cache.add(data["jid"]) + if ( + len(self.jid_forward_cache) + > self.opts["syndic_jid_forward_cache_hwm"] + ): # Pop the oldest jid from the cache tmp = sorted(list(self.jid_forward_cache)) tmp.pop(0) self.jid_forward_cache = set(tmp) if master is not None: # __'s to make sure it doesn't print out on the master cli - jdict['__master_id__'] = master + jdict["__master_id__"] = master ret = {} - for key in 'return', 'retcode', 'success': + for key in "return", "retcode", "success": if key in data: ret[key] = data[key] - jdict[data['id']] = ret + jdict[data["id"]] = ret else: # TODO: config to forward these? If so we'll have to keep track of who # has seen them # if we are the top level masters-- don't forward all the minion events - if self.syndic_mode == 'sync': + if self.syndic_mode == "sync": # Add generic event aggregation here - if 'retcode' not in data: - self.raw_events.append({'data': data, 'tag': mtag}) + if "retcode" not in data: + self.raw_events.append({"data": data, "tag": mtag}) def _forward_events(self): - log.trace('Forwarding events') # pylint: disable=no-member + log.trace("Forwarding events") # pylint: disable=no-member if self.raw_events: events = self.raw_events self.raw_events = [] - self._call_syndic('_fire_master', - kwargs={'events': events, - 'pretag': tagify(self.opts['id'], base='syndic'), - 'timeout': self._return_retry_timer(), - 'sync': False, - }, - ) + self._call_syndic( + "_fire_master", + kwargs={ + "events": events, + "pretag": tagify(self.opts["id"], base="syndic"), + "timeout": self._return_retry_timer(), + "sync": False, + }, + ) if self.delayed: res = self._return_pub_syndic(self.delayed) if res: @@ -3309,47 +3607,54 @@ class SyndicManager(MinionBase): class ProxyMinionManager(MinionManager): - ''' + """ Create the multi-minion interface but for proxy minions - ''' - def _create_minion_object(self, opts, timeout, safe, - io_loop=None, loaded_base_name=None, - jid_queue=None): - ''' + """ + + def _create_minion_object( + self, opts, timeout, safe, io_loop=None, loaded_base_name=None, jid_queue=None + ): + """ Helper function to return the correct type of object - ''' - return ProxyMinion(opts, - timeout, - safe, - io_loop=io_loop, - loaded_base_name=loaded_base_name, - jid_queue=jid_queue) + """ + return ProxyMinion( + opts, + timeout, + safe, + io_loop=io_loop, + loaded_base_name=loaded_base_name, + jid_queue=jid_queue, + ) def _metaproxy_call(opts, fn_name): metaproxy = salt.loader.metaproxy(opts) try: - metaproxy_name = opts['metaproxy'] + metaproxy_name = opts["metaproxy"] except KeyError: - metaproxy_name = 'proxy' - errmsg = 'No metaproxy key found in opts for id ' + opts['id'] + '. ' + \ - 'Defaulting to standard proxy minion' + metaproxy_name = "proxy" + errmsg = ( + "No metaproxy key found in opts for id " + + opts["id"] + + ". " + + "Defaulting to standard proxy minion" + ) log.trace(errmsg) - metaproxy_fn = metaproxy_name + '.' + fn_name + metaproxy_fn = metaproxy_name + "." + fn_name return metaproxy[metaproxy_fn] class ProxyMinion(Minion): - ''' + """ This class instantiates a 'proxy' minion--a minion that does not manipulate the host it runs on, but instead manipulates a device that cannot run a minion. - ''' + """ # TODO: better name... @salt.ext.tornado.gen.coroutine def _post_master_init(self, master): - ''' + """ Function to finish init after connecting to a master This is primarily loading modules, pillars, etc. (since they need @@ -3361,52 +3666,53 @@ class ProxyMinion(Minion): ProxyMinions need a significantly different post master setup, which is why the differences are not factored out into separate helper functions. - ''' - mp_call = _metaproxy_call(self.opts, 'post_master_init') + """ + mp_call = _metaproxy_call(self.opts, "post_master_init") return mp_call(self, master) def _target_load(self, load): - ''' + """ Verify that the publication is valid and applies to this minion - ''' - mp_call = _metaproxy_call(self.opts, 'target_load') + """ + mp_call = _metaproxy_call(self.opts, "target_load") return mp_call(self, load) def _handle_payload(self, payload): - mp_call = _metaproxy_call(self.opts, 'handle_payload') + mp_call = _metaproxy_call(self.opts, "handle_payload") return mp_call(self, payload) @salt.ext.tornado.gen.coroutine def _handle_decoded_payload(self, data): - mp_call = _metaproxy_call(self.opts, 'handle_decoded_payload') + mp_call = _metaproxy_call(self.opts, "handle_decoded_payload") return mp_call(self, data) @classmethod def _target(cls, minion_instance, opts, data, connected): - mp_call = _metaproxy_call(opts, 'target') + mp_call = _metaproxy_call(opts, "target") return mp_call(cls, minion_instance, opts, data, connected) @classmethod def _thread_return(cls, minion_instance, opts, data): - mp_call = _metaproxy_call(opts, 'thread_return') + mp_call = _metaproxy_call(opts, "thread_return") return mp_call(cls, minion_instance, opts, data) @classmethod def _thread_multi_return(cls, minion_instance, opts, data): - mp_call = _metaproxy_call(opts, 'thread_multi_return') + mp_call = _metaproxy_call(opts, "thread_multi_return") return mp_call(cls, minion_instance, opts, data) class SProxyMinion(SMinion): - ''' + """ Create an object that has loaded all of the minion module functions, grains, modules, returners etc. The SProxyMinion allows developers to generate all of the salt minion functions and present them with these functions for general use. - ''' + """ + def gen_modules(self, initial_load=False, context=None): - ''' + """ Tell the minion to reload the execution modules CLI Example: @@ -3414,84 +3720,88 @@ class SProxyMinion(SMinion): .. code-block:: bash salt '*' sys.reload_modules - ''' - self.opts['grains'] = salt.loader.grains(self.opts) - self.opts['pillar'] = salt.pillar.get_pillar( + """ + self.opts["grains"] = salt.loader.grains(self.opts) + self.opts["pillar"] = salt.pillar.get_pillar( self.opts, - self.opts['grains'], - self.opts['id'], - saltenv=self.opts['saltenv'], - pillarenv=self.opts.get('pillarenv'), + self.opts["grains"], + self.opts["id"], + saltenv=self.opts["saltenv"], + pillarenv=self.opts.get("pillarenv"), ).compile_pillar() - if 'proxy' not in self.opts['pillar'] and 'proxy' not in self.opts: + if "proxy" not in self.opts["pillar"] and "proxy" not in self.opts: errmsg = ( 'No "proxy" configuration key found in pillar or opts ' - 'dictionaries for id {id}. Check your pillar/options ' - 'configuration and contents. Salt-proxy aborted.' - ).format(id=self.opts['id']) + "dictionaries for id {id}. Check your pillar/options " + "configuration and contents. Salt-proxy aborted." + ).format(id=self.opts["id"]) log.error(errmsg) self._running = False raise SaltSystemExit(code=salt.defaults.exitcodes.EX_GENERIC, msg=errmsg) - if 'proxy' not in self.opts: - self.opts['proxy'] = self.opts['pillar']['proxy'] + if "proxy" not in self.opts: + self.opts["proxy"] = self.opts["pillar"]["proxy"] # Then load the proxy module self.proxy = salt.loader.proxy(self.opts) self.utils = salt.loader.utils(self.opts, proxy=self.proxy, context=context) - self.functions = salt.loader.minion_mods(self.opts, - utils=self.utils, - notify=False, - proxy=self.proxy, - context=context) - self.returners = salt.loader.returners(self.opts, - functions=self.functions, - proxy=self.proxy, - context=context) + self.functions = salt.loader.minion_mods( + self.opts, utils=self.utils, notify=False, proxy=self.proxy, context=context + ) + self.returners = salt.loader.returners( + self.opts, functions=self.functions, proxy=self.proxy, context=context + ) self.matchers = salt.loader.matchers(self.opts) - self.functions['sys.reload_modules'] = self.gen_modules - self.executors = salt.loader.executors(self.opts, - functions=self.functions, - proxy=self.proxy, - context=context) + self.functions["sys.reload_modules"] = self.gen_modules + self.executors = salt.loader.executors( + self.opts, functions=self.functions, proxy=self.proxy, context=context + ) - fq_proxyname = self.opts['proxy']['proxytype'] + fq_proxyname = self.opts["proxy"]["proxytype"] # we can then sync any proxymodules down from the master # we do a sync_all here in case proxy code was installed by # SPM or was manually placed in /srv/salt/_modules etc. - self.functions['saltutil.sync_all'](saltenv=self.opts['saltenv']) + self.functions["saltutil.sync_all"](saltenv=self.opts["saltenv"]) - self.functions.pack['__proxy__'] = self.proxy - self.proxy.pack['__salt__'] = self.functions - self.proxy.pack['__ret__'] = self.returners - self.proxy.pack['__pillar__'] = self.opts['pillar'] + self.functions.pack["__proxy__"] = self.proxy + self.proxy.pack["__salt__"] = self.functions + self.proxy.pack["__ret__"] = self.returners + self.proxy.pack["__pillar__"] = self.opts["pillar"] # Reload utils as well (chicken and egg, __utils__ needs __proxy__ and __proxy__ needs __utils__ self.utils = salt.loader.utils(self.opts, proxy=self.proxy, context=context) - self.proxy.pack['__utils__'] = self.utils + self.proxy.pack["__utils__"] = self.utils # Reload all modules so all dunder variables are injected self.proxy.reload_modules() - if ('{0}.init'.format(fq_proxyname) not in self.proxy - or '{0}.shutdown'.format(fq_proxyname) not in self.proxy): - errmsg = 'Proxymodule {0} is missing an init() or a shutdown() or both. '.format(fq_proxyname) + \ - 'Check your proxymodule. Salt-proxy aborted.' + if ( + "{0}.init".format(fq_proxyname) not in self.proxy + or "{0}.shutdown".format(fq_proxyname) not in self.proxy + ): + errmsg = ( + "Proxymodule {0} is missing an init() or a shutdown() or both. ".format( + fq_proxyname + ) + + "Check your proxymodule. Salt-proxy aborted." + ) log.error(errmsg) self._running = False raise SaltSystemExit(code=salt.defaults.exitcodes.EX_GENERIC, msg=errmsg) - self.module_executors = self.proxy.get('{0}.module_executors'.format(fq_proxyname), lambda: [])() - proxy_init_fn = self.proxy[fq_proxyname + '.init'] + self.module_executors = self.proxy.get( + "{0}.module_executors".format(fq_proxyname), lambda: [] + )() + proxy_init_fn = self.proxy[fq_proxyname + ".init"] proxy_init_fn(self.opts) - self.opts['grains'] = salt.loader.grains(self.opts, proxy=self.proxy) + self.opts["grains"] = salt.loader.grains(self.opts, proxy=self.proxy) # Sync the grains here so the proxy can communicate them to the master - self.functions['saltutil.sync_grains'](saltenv='base') - self.grains_cache = self.opts['grains'] + self.functions["saltutil.sync_grains"](saltenv="base") + self.grains_cache = self.opts["grains"] self.ready = True diff --git a/salt/modules/__init__.py b/salt/modules/__init__.py index 0d649bc1522..cb0f27fee8a 100644 --- a/salt/modules/__init__.py +++ b/salt/modules/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Execution Module Directory -''' +""" diff --git a/salt/modules/acme.py b/salt/modules/acme.py index 76552281f55..5fb0062dab8 100644 --- a/salt/modules/acme.py +++ b/salt/modules/acme.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" ACME / Let's Encrypt module =========================== @@ -33,11 +33,12 @@ plugin credentials file needs to be passed in using the Make sure the appropriate certbot plugin for the wanted DNS provider is installed before using this module. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import logging + import datetime +import logging import os # Import salt libs @@ -46,56 +47,65 @@ from salt.exceptions import SaltInvocationError log = logging.getLogger(__name__) -LEA = salt.utils.path.which_bin(['certbot', 'letsencrypt', - 'certbot-auto', 'letsencrypt-auto', - '/opt/letsencrypt/letsencrypt-auto']) -LE_LIVE = '/etc/letsencrypt/live/' +LEA = salt.utils.path.which_bin( + [ + "certbot", + "letsencrypt", + "certbot-auto", + "letsencrypt-auto", + "/opt/letsencrypt/letsencrypt-auto", + ] +) +LE_LIVE = "/etc/letsencrypt/live/" def __virtual__(): - ''' + """ Only work when letsencrypt-auto is installed - ''' - return LEA is not None, 'The ACME execution module cannot be loaded: letsencrypt-auto not installed.' + """ + return ( + LEA is not None, + "The ACME execution module cannot be loaded: letsencrypt-auto not installed.", + ) def _cert_file(name, cert_type): - ''' + """ Return expected path of a Let's Encrypt live cert - ''' - return os.path.join(LE_LIVE, name, '{0}.pem'.format(cert_type)) + """ + return os.path.join(LE_LIVE, name, "{0}.pem".format(cert_type)) def _expires(name): - ''' + """ Return the expiry date of a cert :rtype: datetime :return: Expiry date - ''' - cert_file = _cert_file(name, 'cert') + """ + cert_file = _cert_file(name, "cert") # Use the salt module if available - if 'tls.cert_info' in __salt__: - expiry = __salt__['tls.cert_info'](cert_file).get('not_after', 0) + if "tls.cert_info" in __salt__: + expiry = __salt__["tls.cert_info"](cert_file).get("not_after", 0) # Cobble it together using the openssl binary else: - openssl_cmd = 'openssl x509 -in {0} -noout -enddate'.format(cert_file) + openssl_cmd = "openssl x509 -in {0} -noout -enddate".format(cert_file) # No %e format on my Linux'es here strptime_sux_cmd = 'date --date="$({0} | cut -d= -f2)" +%s'.format(openssl_cmd) - expiry = float(__salt__['cmd.shell'](strptime_sux_cmd, output_loglevel='quiet')) + expiry = float(__salt__["cmd.shell"](strptime_sux_cmd, output_loglevel="quiet")) # expiry = datetime.datetime.strptime(expiry.split('=', 1)[-1], '%b %e %H:%M:%S %Y %Z') return datetime.datetime.fromtimestamp(expiry) def _renew_by(name, window=None): - ''' + """ Date before a certificate should be renewed :param str name: Common Name of the certificate (DNS name of certificate) :param int window: days before expiry date to renew :rtype: datetime :return: First renewal date - ''' + """ expiry = _expires(name) if window is not None: expiry = expiry - datetime.timedelta(days=window) @@ -103,26 +113,28 @@ def _renew_by(name, window=None): return expiry -def cert(name, - aliases=None, - email=None, - webroot=None, - test_cert=False, - renew=None, - keysize=None, - server=None, - owner='root', - group='root', - mode='0640', - certname=None, - preferred_challenges=None, - tls_sni_01_port=None, - tls_sni_01_address=None, - http_01_port=None, - http_01_address=None, - dns_plugin=None, - dns_plugin_credentials=None): - ''' +def cert( + name, + aliases=None, + email=None, + webroot=None, + test_cert=False, + renew=None, + keysize=None, + server=None, + owner="root", + group="root", + mode="0640", + certname=None, + preferred_challenges=None, + tls_sni_01_port=None, + tls_sni_01_address=None, + http_01_port=None, + http_01_address=None, + dns_plugin=None, + dns_plugin_credentials=None, +): + """ Obtain/renew a certificate from an ACME CA, probably Let's Encrypt. :param name: Common Name of the certificate (DNS name of certificate) @@ -167,103 +179,123 @@ def cert(name, salt 'gitlab.example.com' acme.cert dev.example.com "[gitlab.example.com]" test_cert=True \ renew=14 webroot=/opt/gitlab/embedded/service/gitlab-rails/public - ''' + """ - cmd = [LEA, 'certonly', '--non-interactive', '--agree-tos'] + cmd = [LEA, "certonly", "--non-interactive", "--agree-tos"] - supported_dns_plugins = ['cloudflare'] + supported_dns_plugins = ["cloudflare"] - cert_file = _cert_file(name, 'cert') - if not __salt__['file.file_exists'](cert_file): - log.debug('Certificate %s does not exist (yet)', cert_file) + cert_file = _cert_file(name, "cert") + if not __salt__["file.file_exists"](cert_file): + log.debug("Certificate %s does not exist (yet)", cert_file) renew = False elif needs_renewal(name, renew): - log.debug('Certificate %s will be renewed', cert_file) - cmd.append('--renew-by-default') + log.debug("Certificate %s will be renewed", cert_file) + cmd.append("--renew-by-default") renew = True if server: - cmd.append('--server {0}'.format(server)) + cmd.append("--server {0}".format(server)) if certname: - cmd.append('--cert-name {0}'.format(certname)) + cmd.append("--cert-name {0}".format(certname)) if test_cert: if server: - return {'result': False, 'comment': 'Use either server or test_cert, not both'} - cmd.append('--test-cert') + return { + "result": False, + "comment": "Use either server or test_cert, not both", + } + cmd.append("--test-cert") if webroot: - cmd.append('--authenticator webroot') + cmd.append("--authenticator webroot") if webroot is not True: - cmd.append('--webroot-path {0}'.format(webroot)) + cmd.append("--webroot-path {0}".format(webroot)) elif dns_plugin in supported_dns_plugins: - if dns_plugin == 'cloudflare': - cmd.append('--dns-cloudflare') - cmd.append('--dns-cloudflare-credentials {0}'.format(dns_plugin_credentials)) + if dns_plugin == "cloudflare": + cmd.append("--dns-cloudflare") + cmd.append( + "--dns-cloudflare-credentials {0}".format(dns_plugin_credentials) + ) else: - return {'result': False, 'comment': 'DNS plugin \'{0}\' is not supported'.format(dns_plugin)} + return { + "result": False, + "comment": "DNS plugin '{0}' is not supported".format(dns_plugin), + } else: - cmd.append('--authenticator standalone') + cmd.append("--authenticator standalone") if email: - cmd.append('--email {0}'.format(email)) + cmd.append("--email {0}".format(email)) if keysize: - cmd.append('--rsa-key-size {0}'.format(keysize)) + cmd.append("--rsa-key-size {0}".format(keysize)) - cmd.append('--domains {0}'.format(name)) + cmd.append("--domains {0}".format(name)) if aliases is not None: for dns in aliases: - cmd.append('--domains {0}'.format(dns)) + cmd.append("--domains {0}".format(dns)) if preferred_challenges: - cmd.append('--preferred-challenges {}'.format(preferred_challenges)) + cmd.append("--preferred-challenges {}".format(preferred_challenges)) if tls_sni_01_port: - cmd.append('--tls-sni-01-port {}'.format(tls_sni_01_port)) + cmd.append("--tls-sni-01-port {}".format(tls_sni_01_port)) if tls_sni_01_address: - cmd.append('--tls-sni-01-address {}'.format(tls_sni_01_address)) + cmd.append("--tls-sni-01-address {}".format(tls_sni_01_address)) if http_01_port: - cmd.append('--http-01-port {}'.format(http_01_port)) + cmd.append("--http-01-port {}".format(http_01_port)) if http_01_address: - cmd.append('--http-01-address {}'.format(http_01_address)) + cmd.append("--http-01-address {}".format(http_01_address)) - res = __salt__['cmd.run_all'](' '.join(cmd)) + res = __salt__["cmd.run_all"](" ".join(cmd)) - if res['retcode'] != 0: - if 'expand' in res['stderr']: - cmd.append('--expand') - res = __salt__['cmd.run_all'](' '.join(cmd)) - if res['retcode'] != 0: - return {'result': False, - 'comment': ('Certificate {0} renewal failed with:\n{1}' - ''.format(name, res['stderr']))} + if res["retcode"] != 0: + if "expand" in res["stderr"]: + cmd.append("--expand") + res = __salt__["cmd.run_all"](" ".join(cmd)) + if res["retcode"] != 0: + return { + "result": False, + "comment": ( + "Certificate {0} renewal failed with:\n{1}" + "".format(name, res["stderr"]) + ), + } else: - return {'result': False, - 'comment': ('Certificate {0} renewal failed with:\n{1}' - ''.format(name, res['stderr']))} + return { + "result": False, + "comment": ( + "Certificate {0} renewal failed with:\n{1}" + "".format(name, res["stderr"]) + ), + } - if 'no action taken' in res['stdout']: - comment = 'Certificate {0} unchanged'.format(cert_file) + if "no action taken" in res["stdout"]: + comment = "Certificate {0} unchanged".format(cert_file) result = None elif renew: - comment = 'Certificate {0} renewed'.format(name) + comment = "Certificate {0} renewed".format(name) result = True else: - comment = 'Certificate {0} obtained'.format(name) + comment = "Certificate {0} obtained".format(name) result = True - ret = {'comment': comment, 'not_after': expires(name), 'changes': {}, 'result': result} - ret, _ = __salt__['file.check_perms'](_cert_file(name, 'privkey'), - ret, - owner, group, mode, - follow_symlinks=True) + ret = { + "comment": comment, + "not_after": expires(name), + "changes": {}, + "result": result, + } + ret, _ = __salt__["file.check_perms"]( + _cert_file(name, "privkey"), ret, owner, group, mode, follow_symlinks=True + ) return ret def certs(): - ''' + """ Return a list of active certificates CLI example: @@ -271,12 +303,14 @@ def certs(): .. code-block:: bash salt 'vhost.example.com' acme.certs - ''' - return [item for item in __salt__['file.readdir'](LE_LIVE)[2:] if os.path.isdir(item)] + """ + return [ + item for item in __salt__["file.readdir"](LE_LIVE)[2:] if os.path.isdir(item) + ] def info(name): - ''' + """ Return information about a certificate :param str name: CommonName of certificate @@ -291,28 +325,28 @@ def info(name): .. code-block:: bash salt 'gitlab.example.com' acme.info dev.example.com - ''' + """ if not has(name): return {} - cert_file = _cert_file(name, 'cert') + cert_file = _cert_file(name, "cert") # Use the tls salt module if available - if 'tls.cert_info' in __salt__: - cert_info = __salt__['tls.cert_info'](cert_file) + if "tls.cert_info" in __salt__: + cert_info = __salt__["tls.cert_info"](cert_file) # Strip out the extensions object contents; # these trip over our poor state output # and they serve no real purpose here anyway - cert_info['extensions'] = cert_info['extensions'].keys() - elif 'x509.read_certificate' in __salt__: - cert_info = __salt__['x509.read_certificate'](cert_file) + cert_info["extensions"] = cert_info["extensions"].keys() + elif "x509.read_certificate" in __salt__: + cert_info = __salt__["x509.read_certificate"](cert_file) else: # Cobble it together using the openssl binary - openssl_cmd = 'openssl x509 -in {0} -noout -text'.format(cert_file) - cert_info = {'text': __salt__['cmd.run'](openssl_cmd, output_loglevel='quiet')} + openssl_cmd = "openssl x509 -in {0} -noout -text".format(cert_file) + cert_info = {"text": __salt__["cmd.run"](openssl_cmd, output_loglevel="quiet")} return cert_info def expires(name): - ''' + """ The expiry date of a certificate in ISO format :param str name: CommonName of certificate @@ -324,12 +358,12 @@ def expires(name): .. code-block:: bash salt 'gitlab.example.com' acme.expires dev.example.com - ''' + """ return _expires(name).isoformat() def has(name): - ''' + """ Test if a certificate is in the Let's Encrypt Live directory :param str name: CommonName of certificate @@ -341,24 +375,24 @@ def has(name): if __salt__['acme.has']('dev.example.com'): log.info('That is one nice certificate you have there!') - ''' - return __salt__['file.file_exists'](_cert_file(name, 'cert')) + """ + return __salt__["file.file_exists"](_cert_file(name, "cert")) def renew_by(name, window=None): - ''' + """ Date in ISO format when a certificate should first be renewed :param str name: CommonName of certificate :param int window: number of days before expiry when renewal should take place :rtype: str :return: Date of certificate renewal in ISO format. - ''' + """ return _renew_by(name, window).isoformat() def needs_renewal(name, window=None): - ''' + """ Check if a certificate needs renewal :param str name: CommonName of certificate @@ -374,11 +408,13 @@ def needs_renewal(name, window=None): __salt__['acme.cert']('dev.example.com', **kwargs) else: log.info('Your certificate is still good') - ''' + """ if window: - if str(window).lower in ('force', 'true'): + if str(window).lower() in ("force", "true"): return True - if not (isinstance(window, int) or (hasattr(window, 'isdigit') and window.isdigit())): + if not ( + isinstance(window, int) or (hasattr(window, "isdigit") and window.isdigit()) + ): raise SaltInvocationError( 'The argument "window", if provided, must be one of the following : ' 'True (boolean), "force" or "Force" (str) or a numerical value in days.' diff --git a/salt/modules/aix_group.py b/salt/modules/aix_group.py index 18229107c5f..e1587152122 100644 --- a/salt/modules/aix_group.py +++ b/salt/modules/aix_group.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage groups on Solaris .. important:: @@ -7,13 +7,12 @@ Manage groups on Solaris minion, and it is using a different module (or gives an error similar to *'group.info' is not available*), see :ref:`here `. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging - log = logging.getLogger(__name__) @@ -23,21 +22,24 @@ except ImportError: pass # Define the module's virtual name -__virtualname__ = 'group' +__virtualname__ = "group" def __virtual__(): - ''' + """ Set the group module if the kernel is AIX - ''' - if __grains__['kernel'] == 'AIX': + """ + if __grains__["kernel"] == "AIX": return __virtualname__ - return (False, 'The aix_group execution module failed to load: ' - 'only available on AIX systems.') + return ( + False, + "The aix_group execution module failed to load: " + "only available on AIX systems.", + ) def add(name, gid=None, system=False, root=None): - ''' + """ Add the specified group CLI Example: @@ -45,23 +47,23 @@ def add(name, gid=None, system=False, root=None): .. code-block:: bash salt '*' group.add foo 3456 - ''' - cmd = 'mkgroup ' + """ + cmd = "mkgroup " if system and root is not None: - cmd += '-a ' + cmd += "-a " if gid: - cmd += 'id={0} '.format(gid) + cmd += "id={0} ".format(gid) cmd += name - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - return not ret['retcode'] + return not ret["retcode"] def delete(name): - ''' + """ Remove the named group CLI Example: @@ -69,14 +71,14 @@ def delete(name): .. code-block:: bash salt '*' group.delete foo - ''' - ret = __salt__['cmd.run_all']('rmgroup {0}'.format(name), python_shell=False) + """ + ret = __salt__["cmd.run_all"]("rmgroup {0}".format(name), python_shell=False) - return not ret['retcode'] + return not ret["retcode"] def info(name): - ''' + """ Return information about a group CLI Example: @@ -84,20 +86,22 @@ def info(name): .. code-block:: bash salt '*' group.info foo - ''' + """ try: grinfo = grp.getgrnam(name) except KeyError: return {} else: - return {'name': grinfo.gr_name, - 'passwd': grinfo.gr_passwd, - 'gid': grinfo.gr_gid, - 'members': grinfo.gr_mem} + return { + "name": grinfo.gr_name, + "passwd": grinfo.gr_passwd, + "gid": grinfo.gr_gid, + "members": grinfo.gr_mem, + } def getent(refresh=False): - ''' + """ Return info on all groups CLI Example: @@ -105,20 +109,20 @@ def getent(refresh=False): .. code-block:: bash salt '*' group.getent - ''' - if 'group.getent' in __context__ and not refresh: - return __context__['group.getent'] + """ + if "group.getent" in __context__ and not refresh: + return __context__["group.getent"] ret = [] for grinfo in grp.getgrall(): ret.append(info(grinfo.gr_name)) - __context__['group.getent'] = ret + __context__["group.getent"] = ret return ret def chgid(name, gid): - ''' + """ Change the gid for a named group CLI Example: @@ -126,20 +130,20 @@ def chgid(name, gid): .. code-block:: bash salt '*' group.chgid foo 4376 - ''' - pre_gid = __salt__['file.group_to_gid'](name) + """ + pre_gid = __salt__["file.group_to_gid"](name) if gid == pre_gid: return True - cmd = 'chgroup id={0} {1}'.format(gid, name) - __salt__['cmd.run'](cmd, python_shell=False) - post_gid = __salt__['file.group_to_gid'](name) + cmd = "chgroup id={0} {1}".format(gid, name) + __salt__["cmd.run"](cmd, python_shell=False) + post_gid = __salt__["file.group_to_gid"](name) if post_gid != pre_gid: return post_gid == gid return False def adduser(name, username, root=None): - ''' + """ Add a user in the group. CLI Example: @@ -150,16 +154,16 @@ def adduser(name, username, root=None): Verifies if a valid username 'bar' as a member of an existing group 'foo', if not then adds it. - ''' - cmd = 'chgrpmem -m + {0} {1}'.format(username, name) + """ + cmd = "chgrpmem -m + {0} {1}".format(username, name) - retcode = __salt__['cmd.retcode'](cmd, python_shell=False) + retcode = __salt__["cmd.retcode"](cmd, python_shell=False) return not retcode def deluser(name, username, root=None): - ''' + """ Remove a user from the group. CLI Example: @@ -170,13 +174,13 @@ def deluser(name, username, root=None): Removes a member user 'bar' from a group 'foo'. If group is not present then returns True. - ''' - grp_info = __salt__['group.info'](name) + """ + grp_info = __salt__["group.info"](name) try: - if username in grp_info['members']: - cmd = 'chgrpmem -m - {0} {1}'.format(username, name) - ret = __salt__['cmd.run'](cmd, python_shell=False) - return not ret['retcode'] + if username in grp_info["members"]: + cmd = "chgrpmem -m - {0} {1}".format(username, name) + ret = __salt__["cmd.run"](cmd, python_shell=False) + return not ret["retcode"] else: return True except Exception: # pylint: disable=broad-except @@ -184,7 +188,7 @@ def deluser(name, username, root=None): def members(name, members_list, root=None): - ''' + """ Replaces members of the group with a provided list. CLI Example: @@ -193,8 +197,8 @@ def members(name, members_list, root=None): Replaces a membership list for a local group 'foo'. foo:x:1234:user1,user2,user3,... - ''' - cmd = 'chgrpmem -m = {0} {1}'.format(members_list, name) - retcode = __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "chgrpmem -m = {0} {1}".format(members_list, name) + retcode = __salt__["cmd.retcode"](cmd, python_shell=False) return not retcode diff --git a/salt/modules/aix_shadow.py b/salt/modules/aix_shadow.py index dd6e9c9d4c5..be0955e3e2e 100644 --- a/salt/modules/aix_shadow.py +++ b/salt/modules/aix_shadow.py @@ -1,37 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Manage account locks on AIX systems .. versionadded:: 2018.3.0 :depends: none -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python librarie import logging - log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'shadow' +__virtualname__ = "shadow" def __virtual__(): - ''' + """ Only load if kernel is AIX - ''' - if __grains__['kernel'] == 'AIX': + """ + if __grains__["kernel"] == "AIX": return __virtualname__ - return (False, 'The aix_shadow execution module failed to load: ' - 'only available on AIX systems.') + return ( + False, + "The aix_shadow execution module failed to load: " + "only available on AIX systems.", + ) def login_failures(user): - ''' + """ Query for all accounts which have 3 or more login failures. CLI Example: @@ -39,15 +41,15 @@ def login_failures(user): .. code-block:: bash salt shadow.login_failures ALL - ''' + """ - cmd = 'lsuser -a unsuccessful_login_count {0}'.format(user) + cmd = "lsuser -a unsuccessful_login_count {0}".format(user) cmd += " | grep -E 'unsuccessful_login_count=([3-9]|[0-9][0-9]+)'" - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=True) + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=True) ret = [] - lines = out['stdout'].splitlines() + lines = out["stdout"].splitlines() for line in lines: ret.append(line.split()[0]) @@ -55,7 +57,7 @@ def login_failures(user): def locked(user): - ''' + """ Query for all accounts which are flagged as locked. CLI Example: @@ -63,15 +65,15 @@ def locked(user): .. code-block:: bash salt shadow.locked ALL - ''' + """ - cmd = 'lsuser -a account_locked {0}'.format(user) + cmd = "lsuser -a account_locked {0}".format(user) cmd += ' | grep "account_locked=true"' - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=True) + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=True) ret = [] - lines = out['stdout'].splitlines() + lines = out["stdout"].splitlines() for line in lines: ret.append(line.split()[0]) @@ -79,7 +81,7 @@ def locked(user): def unlock(user): - ''' + """ Unlock user for locked account CLI Example: @@ -87,10 +89,14 @@ def unlock(user): .. code-block:: bash salt shadow.unlock user - ''' + """ - cmd = 'chuser account_locked=false {0} | ' \ - 'chsec -f /etc/security/lastlog -a "unsuccessful_login_count=0" -s {0}'.format(user) - ret = __salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=True) + cmd = ( + "chuser account_locked=false {0} | " + 'chsec -f /etc/security/lastlog -a "unsuccessful_login_count=0" -s {0}'.format( + user + ) + ) + ret = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=True) return ret diff --git a/salt/modules/aixpkg.py b/salt/modules/aixpkg.py index 4f9852b504d..fba8d785849 100644 --- a/salt/modules/aixpkg.py +++ b/salt/modules/aixpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Package support for AIX .. important:: @@ -7,13 +7,13 @@ Package support for AIX rpm packages on a minion, and it is using a different module (or gives an error similar to *'pkg.install' is not available*), see :ref:`here `. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import copy import logging - +import os # Import salt libs import salt.utils.data @@ -22,52 +22,49 @@ import salt.utils.path import salt.utils.pkg from salt.exceptions import CommandExecutionError - log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Set the virtual pkg module if the os is AIX - ''' - if __grains__['os_family'] == 'AIX': + """ + if __grains__["os_family"] == "AIX": return __virtualname__ - return (False, - 'Did not load AIX module on non-AIX OS.') + return (False, "Did not load AIX module on non-AIX OS.") def _check_pkg(target): - ''' + """ Return name, version and if rpm package for specified target - ''' + """ ret = {} - cmd = ['/usr/bin/lslpp', '-Lc', target] - lines = __salt__['cmd.run']( - cmd, - python_shell=False).splitlines() + cmd = ["/usr/bin/lslpp", "-Lc", target] + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() - name = '' - version_num = '' + name = "" + version_num = "" rpmpkg = False for line in lines: - if line.startswith('#'): + if line.startswith("#"): continue - comps = line.split(':') + comps = line.split(":") if len(comps) < 7: raise CommandExecutionError( - 'Error occurred finding fileset/package', - info={'errors': comps[1].strip()}) + "Error occurred finding fileset/package", + info={"errors": comps[1].strip()}, + ) # handle first matching line - if 'R' in comps[6]: + if "R" in comps[6]: name = comps[0] rpmpkg = True else: - name = comps[1] # use fileset rather than rpm package + name = comps[1] # use fileset rather than rpm package version_num = comps[2] break @@ -76,15 +73,15 @@ def _check_pkg(target): def _is_installed_rpm(name): - ''' + """ Returns True if the rpm package is installed. Otherwise returns False. - ''' - cmd = ['/usr/bin/rpm', '-q', name] - return __salt__['cmd.retcode'](cmd) == 0 + """ + cmd = ["/usr/bin/rpm", "-q", name] + return __salt__["cmd.retcode"](cmd) == 0 def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the filesets/rpm packages currently installed as a dict: .. code-block:: python @@ -96,21 +93,21 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ ret = {} versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return ret - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy( - __context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret # cmd returns information colon delimited in a single linei, format @@ -124,41 +121,36 @@ def list_pkgs(versions_as_list=False, **kwargs): # # where Type codes: F -- Installp Fileset, P -- Product, C -- Component, # T -- Feature, R -- RPM Package - cmd = '/usr/bin/lslpp -Lc' - lines = __salt__['cmd.run']( - cmd, - python_shell=False).splitlines() + cmd = "/usr/bin/lslpp -Lc" + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() for line in lines: - if line.startswith('#'): + if line.startswith("#"): continue - comps = line.split(':') + comps = line.split(":") if len(comps) < 7: continue - if 'R' in comps[6]: + if "R" in comps[6]: name = comps[0] else: - name = comps[1] # use fileset rather than rpm package + name = comps[1] # use fileset rather than rpm package version_num = comps[2] - __salt__['pkg_resource.add_pkg']( - ret, - name, - version_num) + __salt__["pkg_resource.add_pkg"](ret, name, version_num) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def version(*names, **kwargs): - ''' + """ Common interface for obtaining the version of installed fileset/rpm package. CLI Example: @@ -167,12 +159,12 @@ def version(*names, **kwargs): salt '*' pkg.version vim salt '*' pkg.version foo bar baz - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def _is_installed(name, **kwargs): - ''' + """ Returns True if the fileset/rpm package is installed. Otherwise returns False. CLI Example: @@ -180,13 +172,13 @@ def _is_installed(name, **kwargs): .. code-block:: bash salt '*' pkg._is_installed bash - ''' - cmd = ['/usr/bin/lslpp', '-Lc', name] - return __salt__['cmd.retcode'](cmd) == 0 + """ + cmd = ["/usr/bin/lslpp", "-Lc", name] + return __salt__["cmd.retcode"](cmd) == 0 def install(name=None, refresh=False, pkgs=None, version=None, test=False, **kwargs): - ''' + """ Install the named fileset(s)/rpm package(s). name @@ -225,14 +217,15 @@ def install(name=None, refresh=False, pkgs=None, version=None, test=False, **kwa salt '*' pkg.install /stage/middleware/AIX/Xlc/usr/sys/inst.images/xlC.rte salt '*' pkg.install /stage/middleware/AIX/Firefox/ppc-AIX53/Firefox.base salt '*' pkg.install pkgs='["foo", "bar"]' - ''' + """ targets = salt.utils.args.split_input(pkgs) if pkgs else [name] if not targets: return {} if pkgs: - log.debug('Removing these fileset(s)/rpm package(s) {0}: {1}' - .format(name, targets)) + log.debug( + "Removing these fileset(s)/rpm package(s) {0}: {1}".format(name, targets) + ) # Get a list of the currently installed pkgs. old = list_pkgs() @@ -241,54 +234,51 @@ def install(name=None, refresh=False, pkgs=None, version=None, test=False, **kwa errors = [] for target in targets: filename = os.path.basename(target) - if filename.endswith('.rpm'): - if _is_installed_rpm(filename.split('.aix')[0]): + if filename.endswith(".rpm"): + if _is_installed_rpm(filename.split(".aix")[0]): continue - cmdflags = ' -Uivh ' + cmdflags = " -Uivh " if test: - cmdflags += ' --test' + cmdflags += " --test" - cmd = ['/usr/bin/rpm', cmdflags, target] - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace') + cmd = ["/usr/bin/rpm", cmdflags, target] + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace") else: if _is_installed(target): continue - cmd = '/usr/sbin/installp -acYXg' + cmd = "/usr/sbin/installp -acYXg" if test: - cmd += 'p' - cmd += ' -d ' + cmd += "p" + cmd += " -d " dirpath = os.path.dirname(target) - cmd += dirpath +' '+ filename - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace') + cmd += dirpath + " " + filename + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace") - if 0 != out['retcode']: - errors.append(out['stderr']) + if 0 != out["retcode"]: + errors.append(out["stderr"]) # Get a list of the packages after the uninstall - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problems encountered installing filesets(s)/package(s)', - info={ - 'changes': ret, - 'errors': errors - } + "Problems encountered installing filesets(s)/package(s)", + info={"changes": ret, "errors": errors}, ) # No error occurred if test: - return 'Test succeeded.' + return "Test succeeded." return ret def remove(name=None, pkgs=None, **kwargs): - ''' + """ Remove specified fileset(s)/rpm package(s). name @@ -314,14 +304,15 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove xlC.rte salt '*' pkg.remove Firefox.base.adt salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ targets = salt.utils.args.split_input(pkgs) if pkgs else [name] if not targets: return {} if pkgs: - log.debug('Removing these fileset(s)/rpm package(s) {0}: {1}' - .format(name, targets)) + log.debug( + "Removing these fileset(s)/rpm package(s) {0}: {1}".format(name, targets) + ) errors = [] @@ -334,35 +325,32 @@ def remove(name=None, pkgs=None, **kwargs): named, versionpkg, rpmpkg = _check_pkg(target) except CommandExecutionError as exc: if exc.info: - errors.append(exc.info['errors']) + errors.append(exc.info["errors"]) continue if rpmpkg: - cmd = ['/usr/bin/rpm', '-e', named] - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace') + cmd = ["/usr/bin/rpm", "-e", named] + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace") else: - cmd = ['/usr/sbin/installp', '-u', named] - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace') + cmd = ["/usr/sbin/installp", "-u", named] + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace") # Get a list of the packages after the uninstall - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problems encountered removing filesets(s)/package(s)', - info={ - 'changes': ret, - 'errors': errors - } + "Problems encountered removing filesets(s)/package(s)", + info={"changes": ret, "errors": errors}, ) return ret def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named fileset/rpm package available for upgrade or installation. If more than one fileset/rpm package name is specified, a dict of name/version pairs is returned. @@ -381,14 +369,14 @@ def latest_version(*names, **kwargs): NOTE: Repositories are not presently supported for AIX. This function will always return an empty string for a given fileset/rpm package. - ''' - kwargs.pop('refresh', True) + """ + kwargs.pop("refresh", True) ret = {} if not names: - return '' + return "" for name in names: - ret[name] = '' + ret[name] = "" # Return a string if only one package name passed if len(names) == 1: @@ -397,11 +385,13 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def upgrade_available(name): - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -409,5 +399,5 @@ def upgrade_available(name): .. code-block:: bash salt '*' pkg.upgrade_available - ''' - return latest_version(name) != '' + """ + return latest_version(name) != "" diff --git a/salt/modules/aliases.py b/salt/modules/aliases.py index 33922803a2c..d05fb70dd67 100644 --- a/salt/modules/aliases.py +++ b/salt/modules/aliases.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Manage the information in the aliases file -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -20,37 +20,37 @@ from salt.exceptions import SaltInvocationError from salt.ext import six __outputter__ = { - 'rm_alias': 'txt', - 'has_target': 'txt', - 'get_target': 'txt', - 'set_target': 'txt', - 'list_aliases': 'yaml', + "rm_alias": "txt", + "has_target": "txt", + "get_target": "txt", + "set_target": "txt", + "list_aliases": "yaml", } -__ALIAS_RE = re.compile(r'([^:#]*)\s*:?\s*([^#]*?)(\s+#.*|$)') +__ALIAS_RE = re.compile(r"([^:#]*)\s*:?\s*([^#]*?)(\s+#.*|$)") def __get_aliases_filename(): - ''' + """ Return the path to the appropriate aliases file - ''' - return os.path.realpath(__salt__['config.option']('aliases.file')) + """ + return os.path.realpath(__salt__["config.option"]("aliases.file")) def __parse_aliases(): - ''' + """ Parse the aliases file, and return a list of line components: [ (alias1, target1, comment1), (alias2, target2, comment2), ] - ''' + """ afn = __get_aliases_filename() ret = [] if not os.path.isfile(afn): return ret - with salt.utils.files.fopen(afn, 'r') as ifile: + with salt.utils.files.fopen(afn, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) match = __ALIAS_RE.match(line) @@ -62,16 +62,16 @@ def __parse_aliases(): def __write_aliases_file(lines): - ''' + """ Write a new copy of the aliases file. Lines is a list of lines as returned by __parse_aliases. - ''' + """ afn = __get_aliases_filename() adir = os.path.dirname(afn) out = tempfile.NamedTemporaryFile(dir=adir, delete=False) - if not __opts__.get('integration.test', False): + if not __opts__.get("integration.test", False): if os.path.isfile(afn): afn_st = os.stat(afn) os.chmod(out.name, stat.S_IMODE(afn_st.st_mode)) @@ -82,15 +82,13 @@ def __write_aliases_file(lines): for (line_alias, line_target, line_comment) in lines: if isinstance(line_target, list): - line_target = ', '.join(line_target) + line_target = ", ".join(line_target) if not line_comment: - line_comment = '' + line_comment = "" if line_alias and line_target: - write_line = '{0}: {1}{2}\n'.format( - line_alias, line_target, line_comment - ) + write_line = "{0}: {1}{2}\n".format(line_alias, line_target, line_comment) else: - write_line = '{0}\n'.format(line_comment) + write_line = "{0}\n".format(line_comment) if six.PY3: write_line = write_line.encode(__salt_system_encoding__) out.write(write_line) @@ -99,15 +97,15 @@ def __write_aliases_file(lines): os.rename(out.name, afn) # Search $PATH for the newalises command - newaliases = salt.utils.path.which('newaliases') + newaliases = salt.utils.path.which("newaliases") if newaliases is not None: - __salt__['cmd.run'](newaliases) + __salt__["cmd.run"](newaliases) return True def list_aliases(): - ''' + """ Return the aliases found in the aliases file in this format:: {'alias': 'target'} @@ -117,13 +115,13 @@ def list_aliases(): .. code-block:: bash salt '*' aliases.list_aliases - ''' + """ ret = dict((alias, target) for alias, target, comment in __parse_aliases() if alias) return ret def get_target(alias): - ''' + """ Return the target associated with an alias CLI Example: @@ -131,15 +129,15 @@ def get_target(alias): .. code-block:: bash salt '*' aliases.get_target alias - ''' + """ aliases = list_aliases() if alias in aliases: return aliases[alias] - return '' + return "" def has_target(alias, target): - ''' + """ Return true if the alias/target is set CLI Example: @@ -147,19 +145,19 @@ def has_target(alias, target): .. code-block:: bash salt '*' aliases.has_target alias target - ''' - if target == '': - raise SaltInvocationError('target can not be an empty string') + """ + if target == "": + raise SaltInvocationError("target can not be an empty string") aliases = list_aliases() if alias not in aliases: return False if isinstance(target, list): - target = ', '.join(target) + target = ", ".join(target) return target == aliases[alias] def set_target(alias, target): - ''' + """ Set the entry in the aliases file for the given alias, this will overwrite any previous entry for the given alias or create a new one if it does not exist. @@ -169,13 +167,13 @@ def set_target(alias, target): .. code-block:: bash salt '*' aliases.set_target alias target - ''' + """ - if alias == '': - raise SaltInvocationError('alias can not be an empty string') + if alias == "": + raise SaltInvocationError("alias can not be an empty string") - if target == '': - raise SaltInvocationError('target can not be an empty string') + if target == "": + raise SaltInvocationError("target can not be an empty string") if get_target(alias) == target: return True @@ -191,14 +189,14 @@ def set_target(alias, target): else: out.append((line_alias, line_target, line_comment)) if not ovr: - out.append((alias, target, '')) + out.append((alias, target, "")) __write_aliases_file(out) return True def rm_alias(alias): - ''' + """ Remove an entry from the aliases file CLI Example: @@ -206,7 +204,7 @@ def rm_alias(alias): .. code-block:: bash salt '*' aliases.rm_alias alias - ''' + """ if not get_target(alias): return True diff --git a/salt/modules/alternatives.py b/salt/modules/alternatives.py index 606d493efc5..ed66a3b6e42 100644 --- a/salt/modules/alternatives.py +++ b/salt/modules/alternatives.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Support for Alternatives system :codeauthor: Radek Rada -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging # Import Salt libs import salt.utils.files @@ -17,41 +18,38 @@ import salt.utils.path # Import 3rd-party libs from salt.ext import six - __outputter__ = { - 'display': 'txt', - 'install': 'txt', - 'remove': 'txt', + "display": "txt", + "install": "txt", + "remove": "txt", } log = logging.getLogger(__name__) # Don't shadow built-in's. -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Only if alternatives dir is available - ''' - if os.path.isdir('/etc/alternatives'): + """ + if os.path.isdir("/etc/alternatives"): return True - return (False, 'Cannot load alternatives module: /etc/alternatives dir not found') + return (False, "Cannot load alternatives module: /etc/alternatives dir not found") def _get_cmd(): - ''' + """ Alteratives commands and differ across distributions - ''' - if __grains__['os_family'] == 'RedHat': - return 'alternatives' - return 'update-alternatives' + """ + if __grains__["os_family"] == "RedHat": + return "alternatives" + return "update-alternatives" def display(name): - ''' + """ Display alternatives settings for defined command name CLI Example: @@ -59,16 +57,16 @@ def display(name): .. code-block:: bash salt '*' alternatives.display editor - ''' - cmd = [_get_cmd(), '--display', name] - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out['retcode'] > 0 and out['stderr'] != '': - return out['stderr'] - return out['stdout'] + """ + cmd = [_get_cmd(), "--display", name] + out = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=True) + if out["retcode"] > 0 and out["stderr"] != "": + return out["stderr"] + return out["stdout"] def show_link(name): - ''' + """ Display master link for the alternative .. versionadded:: 2015.8.13,2016.3.4,2016.11.0 @@ -78,34 +76,35 @@ def show_link(name): .. code-block:: bash salt '*' alternatives.show_link editor - ''' + """ - if __grains__['os_family'] == 'RedHat': - path = '/var/lib/' - elif __grains__['os_family'] == 'Suse': - path = '/var/lib/rpm/' + if __grains__["os_family"] == "RedHat": + path = "/var/lib/" + elif __grains__["os_family"] == "Suse": + path = "/var/lib/rpm/" else: - path = '/var/lib/dpkg/' + path = "/var/lib/dpkg/" - path += 'alternatives/{0}'.format(name) + path += "alternatives/{0}".format(name) try: - with salt.utils.files.fopen(path, 'rb') as r_file: + with salt.utils.files.fopen(path, "rb") as r_file: contents = salt.utils.stringutils.to_unicode(r_file.read()) - return contents.splitlines(True)[1].rstrip('\n') + return contents.splitlines(True)[1].rstrip("\n") except OSError: - log.error('alternatives: %s does not exist', name) + log.error("alternatives: %s does not exist", name) except (IOError, IndexError) as exc: # pylint: disable=duplicate-except log.error( - 'alternatives: unable to get master link for %s. ' - 'Exception: %s', name, exc + "alternatives: unable to get master link for %s. " "Exception: %s", + name, + exc, ) return False def show_current(name): - ''' + """ Display the current highest-priority alternative for a given alternatives link @@ -114,16 +113,16 @@ def show_current(name): .. code-block:: bash salt '*' alternatives.show_current editor - ''' + """ try: return _read_link(name) except OSError: - log.error('alternative: %s does not exist', name) + log.error("alternative: %s does not exist", name) return False def check_exists(name, path): - ''' + """ Check if the given path is an alternative for a name. .. versionadded:: 2015.8.4 @@ -133,18 +132,18 @@ def check_exists(name, path): .. code-block:: bash salt '*' alternatives.check_exists name path - ''' - cmd = [_get_cmd(), '--display', name] - out = __salt__['cmd.run_all'](cmd, python_shell=False) + """ + cmd = [_get_cmd(), "--display", name] + out = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=True) - if out['retcode'] > 0 and out['stderr'] != '': + if out["retcode"] > 0 and out["stderr"] != "": return False - return any((line.startswith(path) for line in out['stdout'].splitlines())) + return any((line.startswith(path) for line in out["stdout"].splitlines())) def check_installed(name, path): - ''' + """ Check if the current highest-priority match for a given alternatives link is set to the desired path @@ -153,7 +152,7 @@ def check_installed(name, path): .. code-block:: bash salt '*' alternatives.check_installed name path - ''' + """ try: return _read_link(name) == path except OSError: @@ -161,7 +160,7 @@ def check_installed(name, path): def install(name, link, path, priority): - ''' + """ Install symbolic links determining default commands CLI Example: @@ -169,16 +168,16 @@ def install(name, link, path, priority): .. code-block:: bash salt '*' alternatives.install editor /usr/bin/editor /usr/bin/emacs23 50 - ''' - cmd = [_get_cmd(), '--install', link, name, path, six.text_type(priority)] - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out['retcode'] > 0 and out['stderr'] != '': - return out['stderr'] - return out['stdout'] + """ + cmd = [_get_cmd(), "--install", link, name, path, six.text_type(priority)] + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out["retcode"] > 0 and out["stderr"] != "": + return out["stderr"] + return out["stdout"] def remove(name, path): - ''' + """ Remove symbolic links determining the default commands. CLI Example: @@ -186,16 +185,16 @@ def remove(name, path): .. code-block:: bash salt '*' alternatives.remove name path - ''' - cmd = [_get_cmd(), '--remove', name, path] - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out['retcode'] > 0: - return out['stderr'] - return out['stdout'] + """ + cmd = [_get_cmd(), "--remove", name, path] + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out["retcode"] > 0: + return out["stderr"] + return out["stdout"] def auto(name): - ''' + """ Trigger alternatives to set the path for as specified by priority. @@ -204,16 +203,16 @@ def auto(name): .. code-block:: bash salt '*' alternatives.auto name - ''' - cmd = [_get_cmd(), '--auto', name] - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out['retcode'] > 0: - return out['stderr'] - return out['stdout'] + """ + cmd = [_get_cmd(), "--auto", name] + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out["retcode"] > 0: + return out["stderr"] + return out["stdout"] def set_(name, path): - ''' + """ Manually set the alternative for . CLI Example: @@ -221,19 +220,19 @@ def set_(name, path): .. code-block:: bash salt '*' alternatives.set name path - ''' - cmd = [_get_cmd(), '--set', name, path] - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out['retcode'] > 0: - return out['stderr'] - return out['stdout'] + """ + cmd = [_get_cmd(), "--set", name, path] + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out["retcode"] > 0: + return out["stderr"] + return out["stdout"] def _read_link(name): - ''' + """ Read the link from /etc/alternatives Throws an OSError if the link does not exist - ''' - alt_link_path = '/etc/alternatives/{0}'.format(name) + """ + alt_link_path = "/etc/alternatives/{0}".format(name) return salt.utils.path.readlink(alt_link_path) diff --git a/salt/modules/ansiblegate.py b/salt/modules/ansiblegate.py index 6b903c2b941..44a07f58f38 100644 --- a/salt/modules/ansiblegate.py +++ b/salt/modules/ansiblegate.py @@ -15,7 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -''' +""" Ansible Support =============== @@ -26,25 +26,26 @@ configuration in /etc/salt/minion.d/ as follows: The timeout is how many seconds Salt should wait for any Ansible module to respond. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import json -import os -import sys -import logging -import importlib -import fnmatch -import subprocess -import salt.utils.json -from salt.exceptions import LoaderError, CommandExecutionError -from salt.utils.decorators import depends +import fnmatch +import importlib +import json +import logging +import os +import subprocess +import sys + import salt.utils.decorators.path +import salt.utils.json import salt.utils.platform import salt.utils.timed_subprocess import salt.utils.yaml +from salt.exceptions import CommandExecutionError, LoaderError from salt.ext import six +from salt.utils.decorators import depends try: import ansible @@ -53,23 +54,24 @@ try: except ImportError: ansible = None -__virtualname__ = 'ansible' +__virtualname__ = "ansible" log = logging.getLogger(__name__) class AnsibleModuleResolver(object): - ''' + """ This class is to resolve all available modules in Ansible. - ''' + """ + def __init__(self, opts): self.opts = opts self._modules_map = {} def _get_modules_map(self, path=None): - ''' + """ Get installed Ansible modules :return: - ''' + """ paths = {} root = ansible.modules.__path__[0] if not path: @@ -81,104 +83,127 @@ class AnsibleModuleResolver(object): if os.path.isdir(p_el_path): paths.update(self._get_modules_map(p_el_path)) else: - if (any(p_el.startswith(elm) for elm in ['__', '.']) or - not p_el.endswith('.py') or - p_el in ansible.constants.IGNORE_FILES): + if ( + any(p_el.startswith(elm) for elm in ["__", "."]) + or not p_el.endswith(".py") + or p_el in ansible.constants.IGNORE_FILES + ): continue - p_el_path = p_el_path.replace(root, '').split('.')[0] - als_name = p_el_path.replace('.', '').replace('/', '', 1).replace('/', '.') + p_el_path = p_el_path.replace(root, "").split(".")[0] + als_name = ( + p_el_path.replace(".", "").replace("/", "", 1).replace("/", ".") + ) paths[als_name] = p_el_path return paths def load_module(self, module): - ''' + """ Introspect Ansible module. :param module: :return: - ''' + """ m_ref = self._modules_map.get(module) if m_ref is None: raise LoaderError('Module "{0}" was not found'.format(module)) - mod = importlib.import_module('ansible.modules{0}'.format( - '.'.join([elm.split('.')[0] for elm in m_ref.split(os.path.sep)]))) + mod = importlib.import_module( + "ansible.modules{0}".format( + ".".join([elm.split(".")[0] for elm in m_ref.split(os.path.sep)]) + ) + ) return mod def get_modules_list(self, pattern=None): - ''' + """ Return module map references. :return: - ''' - if pattern and '*' not in pattern: - pattern = '*{0}*'.format(pattern) + """ + if pattern and "*" not in pattern: + pattern = "*{0}*".format(pattern) modules = [] for m_name, m_path in self._modules_map.items(): - m_path = m_path.split('.')[0] - m_name = '.'.join([elm for elm in m_path.split(os.path.sep) if elm]) + m_path = m_path.split(".")[0] + m_name = ".".join([elm for elm in m_path.split(os.path.sep) if elm]) if pattern and fnmatch.fnmatch(m_name, pattern) or not pattern: modules.append(m_name) return sorted(modules) def resolve(self): - log.debug('Resolving Ansible modules') + log.debug("Resolving Ansible modules") self._modules_map = self._get_modules_map() return self def install(self): - log.debug('Installing Ansible modules') + log.debug("Installing Ansible modules") return self class AnsibleModuleCaller(object): DEFAULT_TIMEOUT = 1200 # seconds (20 minutes) - OPT_TIMEOUT_KEY = 'ansible_timeout' + OPT_TIMEOUT_KEY = "ansible_timeout" def __init__(self, resolver): self._resolver = resolver - self.timeout = self._resolver.opts.get(self.OPT_TIMEOUT_KEY, self.DEFAULT_TIMEOUT) + self.timeout = self._resolver.opts.get( + self.OPT_TIMEOUT_KEY, self.DEFAULT_TIMEOUT + ) def call(self, module, *args, **kwargs): - ''' + """ Call an Ansible module by invoking it. :param module: the name of the module. :param args: Arguments to the module :param kwargs: keywords to the module :return: - ''' + """ module = self._resolver.load_module(module) - if not hasattr(module, 'main'): - raise CommandExecutionError('This module is not callable ' - '(see "ansible.help {0}")'.format(module.__name__.replace('ansible.modules.', - ''))) + if not hasattr(module, "main"): + raise CommandExecutionError( + "This module is not callable " + '(see "ansible.help {0}")'.format( + module.__name__.replace("ansible.modules.", "") + ) + ) if args: - kwargs['_raw_params'] = ' '.join(args) - js_args = str('{{"ANSIBLE_MODULE_ARGS": {args}}}') # future lint: disable=blacklisted-function + kwargs["_raw_params"] = " ".join(args) + js_args = str( + '{{"ANSIBLE_MODULE_ARGS": {args}}}' + ) # future lint: disable=blacklisted-function js_args = js_args.format(args=salt.utils.json.dumps(kwargs)) proc_out = salt.utils.timed_subprocess.TimedProc( ["echo", "{0}".format(js_args)], - stdout=subprocess.PIPE, timeout=self.timeout) + stdout=subprocess.PIPE, + timeout=self.timeout, + ) proc_out.run() proc_exc = salt.utils.timed_subprocess.TimedProc( - ['python', module.__file__], - stdin=proc_out.stdout, stdout=subprocess.PIPE, timeout=self.timeout) + ["python", module.__file__], + stdin=proc_out.stdout, + stdout=subprocess.PIPE, + timeout=self.timeout, + ) proc_exc.run() try: out = salt.utils.json.loads(proc_exc.stdout) except ValueError as ex: - out = {'Error': (proc_exc.stderr and (proc_exc.stderr + '.') or six.text_type(ex))} + out = { + "Error": ( + proc_exc.stderr and (proc_exc.stderr + ".") or six.text_type(ex) + ) + } if proc_exc.stdout: - out['Given JSON output'] = proc_exc.stdout + out["Given JSON output"] = proc_exc.stdout return out - if 'invocation' in out: - del out['invocation'] + if "invocation" in out: + del out["invocation"] - out['timeout'] = self.timeout + out["timeout"] = self.timeout return out @@ -188,38 +213,41 @@ _caller = None def _set_callables(modules): - ''' + """ Set all Ansible modules callables :return: - ''' + """ + def _set_function(cmd_name, doc): - ''' + """ Create a Salt function for the Ansible module. - ''' + """ + def _cmd(*args, **kw): - ''' + """ Call an Ansible module as a function from the Salt. - ''' + """ kwargs = {} - if kw.get('__pub_arg'): - for _kw in kw.get('__pub_arg', []): + if kw.get("__pub_arg"): + for _kw in kw.get("__pub_arg", []): if isinstance(_kw, dict): kwargs = _kw break return _caller.call(cmd_name, *args, **kwargs) + _cmd.__doc__ = doc return _cmd for mod in modules: - setattr(sys.modules[__name__], mod, _set_function(mod, 'Available')) + setattr(sys.modules[__name__], mod, _set_function(mod, "Available")) def __virtual__(): - ''' + """ Ansible module caller. :return: - ''' + """ if salt.utils.platform.is_windows(): return False, "The ansiblegate module isn't supported on Windows" ret = ansible is not None @@ -233,25 +261,29 @@ def __virtual__(): return __virtualname__ -@depends('ansible') +@depends("ansible") def help(module=None, *args): - ''' + """ Display help on Ansible standard module. :param module: :return: - ''' + """ if not module: - raise CommandExecutionError('Please tell me what module you want to have helped with. ' - 'Or call "ansible.list" to know what is available.') + raise CommandExecutionError( + "Please tell me what module you want to have helped with. " + 'Or call "ansible.list" to know what is available.' + ) try: module = _resolver.load_module(module) except (ImportError, LoaderError) as err: - raise CommandExecutionError('Module "{0}" is currently not functional on your system.'.format(module)) + raise CommandExecutionError( + 'Module "{0}" is currently not functional on your system.'.format(module) + ) doc = {} ret = {} - for docset in module.DOCUMENTATION.split('---'): + for docset in module.DOCUMENTATION.split("---"): try: docset = salt.utils.yaml.safe_load(docset) if docset: @@ -259,11 +291,15 @@ def help(module=None, *args): except Exception as err: # pylint: disable=broad-except log.error("Error parsing doc section: %s", err) if not args: - if 'description' in doc: - description = doc.get('description') or '' - del doc['description'] - ret['Description'] = description - ret['Available sections on module "{}"'.format(module.__name__.replace('ansible.modules.', ''))] = doc.keys() + if "description" in doc: + description = doc.get("description") or "" + del doc["description"] + ret["Description"] = description + ret[ + 'Available sections on module "{}"'.format( + module.__name__.replace("ansible.modules.", "") + ) + ] = doc.keys() else: for arg in args: info = doc.get(arg) @@ -273,22 +309,37 @@ def help(module=None, *args): return ret -@depends('ansible') +@depends("ansible") def list(pattern=None): - ''' + """ Lists available modules. :return: - ''' + """ return _resolver.get_modules_list(pattern=pattern) -@salt.utils.decorators.path.which('ansible-playbook') -def playbooks(playbook, rundir=None, check=False, diff=False, extra_vars=None, - flush_cache=False, forks=5, inventory=None, limit=None, - list_hosts=False, list_tags=False, list_tasks=False, - module_path=None, skip_tags=None, start_at_task=None, - syntax_check=False, tags=None, playbook_kwargs=None): - ''' +@salt.utils.decorators.path.which("ansible-playbook") +def playbooks( + playbook, + rundir=None, + check=False, + diff=False, + extra_vars=None, + flush_cache=False, + forks=5, + inventory=None, + limit=None, + list_hosts=False, + list_tags=False, + list_tasks=False, + module_path=None, + skip_tags=None, + start_at_task=None, + syntax_check=False, + tags=None, + playbook_kwargs=None, +): + """ Run Ansible Playbooks :param playbook: Which playbook to run. @@ -326,56 +377,56 @@ def playbooks(playbook, rundir=None, check=False, diff=False, extra_vars=None, .. code-block:: bash salt 'ansiblehost' ansible.playbook playbook=/srv/playbooks/play.yml - ''' - command = ['ansible-playbook', playbook] + """ + command = ["ansible-playbook", playbook] if check: - command.append('--check') + command.append("--check") if diff: - command.append('--diff') + command.append("--diff") if isinstance(extra_vars, dict): command.append("--extra-vars='{0}'".format(json.dumps(extra_vars))) - elif isinstance(extra_vars, six.text_type) and extra_vars.startswith('@'): - command.append('--extra-vars={0}'.format(extra_vars)) + elif isinstance(extra_vars, six.text_type) and extra_vars.startswith("@"): + command.append("--extra-vars={0}".format(extra_vars)) if flush_cache: - command.append('--flush-cache') + command.append("--flush-cache") if inventory: - command.append('--inventory={0}'.format(inventory)) + command.append("--inventory={0}".format(inventory)) if limit: - command.append('--limit={0}'.format(limit)) + command.append("--limit={0}".format(limit)) if list_hosts: - command.append('--list-hosts') + command.append("--list-hosts") if list_tags: - command.append('--list-tags') + command.append("--list-tags") if list_tasks: - command.append('--list-tasks') + command.append("--list-tasks") if module_path: - command.append('--module-path={0}'.format(module_path)) + command.append("--module-path={0}".format(module_path)) if skip_tags: - command.append('--skip-tags={0}'.format(skip_tags)) + command.append("--skip-tags={0}".format(skip_tags)) if start_at_task: - command.append('--start-at-task={0}'.format(start_at_task)) + command.append("--start-at-task={0}".format(start_at_task)) if syntax_check: - command.append('--syntax-check') + command.append("--syntax-check") if tags: - command.append('--tags={0}'.format(tags)) + command.append("--tags={0}".format(tags)) if playbook_kwargs: for key, value in six.iteritems(playbook_kwargs): - key = key.replace('_', '-') + key = key.replace("_", "-") if value is True: - command.append('--{0}'.format(key)) + command.append("--{0}".format(key)) elif isinstance(value, six.text_type): - command.append('--{0}={1}'.format(key, value)) + command.append("--{0}={1}".format(key, value)) elif isinstance(value, dict): - command.append('--{0}={1}'.format(key, json.dumps(value))) - command.append('--forks={0}'.format(forks)) + command.append("--{0}={1}".format(key, json.dumps(value))) + command.append("--forks={0}".format(forks)) cmd_kwargs = { - 'env': {'ANSIBLE_STDOUT_CALLBACK': 'json', 'ANSIBLE_RETRY_FILES_ENABLED': '0'}, - 'cwd': rundir, - 'cmd': ' '.join(command) + "env": {"ANSIBLE_STDOUT_CALLBACK": "json", "ANSIBLE_RETRY_FILES_ENABLED": "0"}, + "cwd": rundir, + "cmd": " ".join(command), } - ret = __salt__['cmd.run_all'](**cmd_kwargs) - log.debug('Ansible Playbook Return: %s', ret) - retdata = json.loads(ret['stdout']) - if ret['retcode']: - __context__['retcode'] = ret['retcode'] + ret = __salt__["cmd.run_all"](**cmd_kwargs) + log.debug("Ansible Playbook Return: %s", ret) + retdata = json.loads(ret["stdout"]) + if ret["retcode"]: + __context__["retcode"] = ret["retcode"] return retdata diff --git a/salt/modules/apache.py b/salt/modules/apache.py index 83df8a09483..a8d716fe6e9 100644 --- a/salt/modules/apache.py +++ b/salt/modules/apache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for Apache .. note:: @@ -7,26 +7,19 @@ Support for Apache all implementations of Apache. Debian-specific functions have been moved into deb_apache.py, but will still load under the ``apache`` namespace when a Debian-based system is detected. -''' +""" # Import python libs -from __future__ import absolute_import, generators, print_function, with_statement, unicode_literals -import re -import logging - -# Import 3rd-party libs -# pylint: disable=import-error,no-name-in-module -from salt.ext import six -from salt.ext.six.moves import cStringIO -from salt.ext.six.moves.urllib.error import URLError -from salt.ext.six.moves.urllib.request import ( - HTTPBasicAuthHandler as _HTTPBasicAuthHandler, - HTTPDigestAuthHandler as _HTTPDigestAuthHandler, - urlopen as _urlopen, - build_opener as _build_opener, - install_opener as _install_opener +from __future__ import ( + absolute_import, + generators, + print_function, + unicode_literals, + with_statement, ) -# pylint: enable=import-error,no-name-in-module + +import logging +import re # Import salt libs import salt.utils.data @@ -35,35 +28,56 @@ import salt.utils.path import salt.utils.stringutils from salt.exceptions import SaltException +# Import 3rd-party libs +# pylint: disable=import-error,no-name-in-module +from salt.ext import six +from salt.ext.six.moves import cStringIO +from salt.ext.six.moves.urllib.error import URLError +from salt.ext.six.moves.urllib.request import ( + HTTPBasicAuthHandler as _HTTPBasicAuthHandler, +) +from salt.ext.six.moves.urllib.request import ( + HTTPDigestAuthHandler as _HTTPDigestAuthHandler, +) +from salt.ext.six.moves.urllib.request import build_opener as _build_opener +from salt.ext.six.moves.urllib.request import install_opener as _install_opener +from salt.ext.six.moves.urllib.request import urlopen as _urlopen + +# pylint: enable=import-error,no-name-in-module + + log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if apache is installed - ''' + """ cmd = _detect_os() if salt.utils.path.which(cmd): - return 'apache' - return (False, 'The apache execution module cannot be loaded: apache is not installed.') + return "apache" + return ( + False, + "The apache execution module cannot be loaded: apache is not installed.", + ) def _detect_os(): - ''' + """ Apache commands and paths differ depending on packaging - ''' + """ # TODO: Add pillar support for the apachectl location - os_family = __grains__['os_family'] - if os_family == 'RedHat': - return 'apachectl' - elif os_family == 'Debian' or os_family == 'Suse': - return 'apache2ctl' + os_family = __grains__["os_family"] + if os_family == "RedHat": + return "apachectl" + elif os_family == "Debian" or os_family == "Suse": + return "apache2ctl" else: - return 'apachectl' + return "apachectl" def version(): - ''' + """ Return server version (``apachectl -v``) CLI Example: @@ -71,15 +85,15 @@ def version(): .. code-block:: bash salt '*' apache.version - ''' - cmd = '{0} -v'.format(_detect_os()) - out = __salt__['cmd.run'](cmd).splitlines() - ret = out[0].split(': ') + """ + cmd = "{0} -v".format(_detect_os()) + out = __salt__["cmd.run"](cmd).splitlines() + ret = out[0].split(": ") return ret[1] def fullversion(): - ''' + """ Return server version (``apachectl -V``) CLI Example: @@ -87,28 +101,28 @@ def fullversion(): .. code-block:: bash salt '*' apache.fullversion - ''' - cmd = '{0} -V'.format(_detect_os()) + """ + cmd = "{0} -V".format(_detect_os()) ret = {} - ret['compiled_with'] = [] - out = __salt__['cmd.run'](cmd).splitlines() + ret["compiled_with"] = [] + out = __salt__["cmd.run"](cmd).splitlines() # Example # -D APR_HAS_MMAP - define_re = re.compile(r'^\s+-D\s+') + define_re = re.compile(r"^\s+-D\s+") for line in out: - if ': ' in line: - comps = line.split(': ') + if ": " in line: + comps = line.split(": ") if not comps: continue - ret[comps[0].strip().lower().replace(' ', '_')] = comps[1].strip() - elif ' -D' in line: - cwith = define_re.sub('', line) - ret['compiled_with'].append(cwith) + ret[comps[0].strip().lower().replace(" ", "_")] = comps[1].strip() + elif " -D" in line: + cwith = define_re.sub("", line) + ret["compiled_with"].append(cwith) return ret def modules(): - ''' + """ Return list of static and shared modules (``apachectl -M``) CLI Example: @@ -116,25 +130,25 @@ def modules(): .. code-block:: bash salt '*' apache.modules - ''' - cmd = '{0} -M'.format(_detect_os()) + """ + cmd = "{0} -M".format(_detect_os()) ret = {} - ret['static'] = [] - ret['shared'] = [] - out = __salt__['cmd.run'](cmd).splitlines() + ret["static"] = [] + ret["shared"] = [] + out = __salt__["cmd.run"](cmd).splitlines() for line in out: comps = line.split() if not comps: continue - if '(static)' in line: - ret['static'].append(comps[0]) - if '(shared)' in line: - ret['shared'].append(comps[0]) + if "(static)" in line: + ret["static"].append(comps[0]) + if "(shared)" in line: + ret["shared"].append(comps[0]) return ret def servermods(): - ''' + """ Return list of modules compiled into the server (``apachectl -l``) CLI Example: @@ -142,20 +156,20 @@ def servermods(): .. code-block:: bash salt '*' apache.servermods - ''' - cmd = '{0} -l'.format(_detect_os()) + """ + cmd = "{0} -l".format(_detect_os()) ret = [] - out = __salt__['cmd.run'](cmd).splitlines() + out = __salt__["cmd.run"](cmd).splitlines() for line in out: if not line: continue - if '.c' in line: + if ".c" in line: ret.append(line.strip()) return ret def directives(): - ''' + """ Return list of directives together with expected arguments and places where the directive is valid (``apachectl -L``) @@ -164,22 +178,22 @@ def directives(): .. code-block:: bash salt '*' apache.directives - ''' - cmd = '{0} -L'.format(_detect_os()) + """ + cmd = "{0} -L".format(_detect_os()) ret = {} - out = __salt__['cmd.run'](cmd) - out = out.replace('\n\t', '\t') + out = __salt__["cmd.run"](cmd) + out = out.replace("\n\t", "\t") for line in out.splitlines(): if not line: continue - comps = line.split('\t') - desc = '\n'.join(comps[1:]) + comps = line.split("\t") + desc = "\n".join(comps[1:]) ret[comps[0]] = desc return ret def vhosts(): - ''' + """ Show the settings as parsed from the config file (currently only shows the virtualhost settings) (``apachectl -S``). Because each additional virtual host adds to the execution @@ -191,41 +205,33 @@ def vhosts(): .. code-block:: bash salt -t 10 '*' apache.vhosts - ''' - cmd = '{0} -S'.format(_detect_os()) + """ + cmd = "{0} -S".format(_detect_os()) ret = {} - namevhost = '' - out = __salt__['cmd.run'](cmd) + namevhost = "" + out = __salt__["cmd.run"](cmd) for line in out.splitlines(): if not line: continue comps = line.split() - if 'is a NameVirtualHost' in line: + if "is a NameVirtualHost" in line: namevhost = comps[0] ret[namevhost] = {} else: - if comps[0] == 'default': - ret[namevhost]['default'] = {} - ret[namevhost]['default']['vhost'] = comps[2] - ret[namevhost]['default']['conf'] = re.sub( - r'\(|\)', - '', - comps[3] - ) - if comps[0] == 'port': + if comps[0] == "default": + ret[namevhost]["default"] = {} + ret[namevhost]["default"]["vhost"] = comps[2] + ret[namevhost]["default"]["conf"] = re.sub(r"\(|\)", "", comps[3]) + if comps[0] == "port": ret[namevhost][comps[3]] = {} - ret[namevhost][comps[3]]['vhost'] = comps[3] - ret[namevhost][comps[3]]['conf'] = re.sub( - r'\(|\)', - '', - comps[4] - ) - ret[namevhost][comps[3]]['port'] = comps[1] + ret[namevhost][comps[3]]["vhost"] = comps[3] + ret[namevhost][comps[3]]["conf"] = re.sub(r"\(|\)", "", comps[4]) + ret[namevhost][comps[3]]["port"] = comps[1] return ret def signal(signal=None): - ''' + """ Signals httpd to start, restart, or stop. CLI Example: @@ -233,36 +239,36 @@ def signal(signal=None): .. code-block:: bash salt '*' apache.signal restart - ''' - no_extra_args = ('configtest', 'status', 'fullstatus') - valid_signals = ('start', 'stop', 'restart', 'graceful', 'graceful-stop') + """ + no_extra_args = ("configtest", "status", "fullstatus") + valid_signals = ("start", "stop", "restart", "graceful", "graceful-stop") if signal not in valid_signals and signal not in no_extra_args: return # Make sure you use the right arguments if signal in valid_signals: - arguments = ' -k {0}'.format(signal) + arguments = " -k {0}".format(signal) else: - arguments = ' {0}'.format(signal) + arguments = " {0}".format(signal) cmd = _detect_os() + arguments - out = __salt__['cmd.run_all'](cmd) + out = __salt__["cmd.run_all"](cmd) # A non-zero return code means fail - if out['retcode'] and out['stderr']: - ret = out['stderr'].strip() + if out["retcode"] and out["stderr"]: + ret = out["stderr"].strip() # 'apachectl configtest' returns 'Syntax OK' to stderr - elif out['stderr']: - ret = out['stderr'].strip() - elif out['stdout']: - ret = out['stdout'].strip() + elif out["stderr"]: + ret = out["stderr"].strip() + elif out["stdout"]: + ret = out["stdout"].strip() # No output for something like: apachectl graceful else: ret = 'Command: "{0}" completed successfully!'.format(cmd) return ret -def useradd(pwfile, user, password, opts=''): - ''' +def useradd(pwfile, user, password, opts=""): + """ Add HTTP user using the ``htpasswd`` command. If the ``htpasswd`` file does not exist, it will be created. Valid options that can be passed are: @@ -280,12 +286,12 @@ def useradd(pwfile, user, password, opts=''): salt '*' apache.useradd /etc/httpd/htpasswd larry badpassword salt '*' apache.useradd /etc/httpd/htpasswd larry badpass opts=ns - ''' - return __salt__['webutil.useradd'](pwfile, user, password, opts) + """ + return __salt__["webutil.useradd"](pwfile, user, password, opts) def userdel(pwfile, user): - ''' + """ Delete HTTP user from the specified ``htpasswd`` file. CLI Example: @@ -293,12 +299,12 @@ def userdel(pwfile, user): .. code-block:: bash salt '*' apache.userdel /etc/httpd/htpasswd larry - ''' - return __salt__['webutil.userdel'](pwfile, user) + """ + return __salt__["webutil.userdel"](pwfile, user) -def server_status(profile='default'): - ''' +def server_status(profile="default"): + """ Get Information from the Apache server-status handler .. note:: @@ -327,43 +333,32 @@ def server_status(profile='default'): salt '*' apache.server_status salt '*' apache.server_status other-profile - ''' + """ ret = { - 'Scoreboard': { - '_': 0, - 'S': 0, - 'R': 0, - 'W': 0, - 'K': 0, - 'D': 0, - 'C': 0, - 'L': 0, - 'G': 0, - 'I': 0, - '.': 0, + "Scoreboard": { + "_": 0, + "S": 0, + "R": 0, + "W": 0, + "K": 0, + "D": 0, + "C": 0, + "L": 0, + "G": 0, + "I": 0, + ".": 0, }, } # Get configuration from pillar - url = __salt__['config.get']( - 'apache.server-status:{0}:url'.format(profile), - 'http://localhost/server-status' + url = __salt__["config.get"]( + "apache.server-status:{0}:url".format(profile), "http://localhost/server-status" ) - user = __salt__['config.get']( - 'apache.server-status:{0}:user'.format(profile), - '' - ) - passwd = __salt__['config.get']( - 'apache.server-status:{0}:pass'.format(profile), - '' - ) - realm = __salt__['config.get']( - 'apache.server-status:{0}:realm'.format(profile), - '' - ) - timeout = __salt__['config.get']( - 'apache.server-status:{0}:timeout'.format(profile), - 5 + user = __salt__["config.get"]("apache.server-status:{0}:user".format(profile), "") + passwd = __salt__["config.get"]("apache.server-status:{0}:pass".format(profile), "") + realm = __salt__["config.get"]("apache.server-status:{0}:realm".format(profile), "") + timeout = __salt__["config.get"]( + "apache.server-status:{0}:timeout".format(profile), 5 ) # create authentication handler if configuration exists @@ -375,21 +370,21 @@ def server_status(profile='default'): _install_opener(_build_opener(basic, digest)) # get http data - url += '?auto' + url += "?auto" try: response = _urlopen(url, timeout=timeout).read().splitlines() except URLError: - return 'error' + return "error" # parse the data for line in response: - splt = line.split(':', 1) + splt = line.split(":", 1) splt[0] = splt[0].strip() splt[1] = splt[1].strip() - if splt[0] == 'Scoreboard': + if splt[0] == "Scoreboard": for c in splt[1]: - ret['Scoreboard'][c] += 1 + ret["Scoreboard"][c] += 1 else: if splt[1].isdigit(): ret[splt[0]] = int(splt[1]) @@ -401,56 +396,58 @@ def server_status(profile='default'): def _parse_config(conf, slot=None): - ''' + """ Recursively goes through config structure and builds final Apache configuration :param conf: defined config structure :param slot: name of section container if needed - ''' + """ ret = cStringIO() if isinstance(conf, six.string_types): if slot: - print('{0} {1}'.format(slot, conf), file=ret, end='') + print("{0} {1}".format(slot, conf), file=ret, end="") else: - print('{0}'.format(conf), file=ret, end='') + print("{0}".format(conf), file=ret, end="") elif isinstance(conf, list): is_section = False for item in conf: - if 'this' in item: + if "this" in item: is_section = True - slot_this = six.text_type(item['this']) + slot_this = six.text_type(item["this"]) if is_section: - print('<{0} {1}>'.format(slot, slot_this), file=ret) + print("<{0} {1}>".format(slot, slot_this), file=ret) for item in conf: for key, val in item.items(): - if key != 'this': + if key != "this": print(_parse_config(val, six.text_type(key)), file=ret) - print(''.format(slot), file=ret) + print("".format(slot), file=ret) else: for value in conf: print(_parse_config(value, six.text_type(slot)), file=ret) elif isinstance(conf, dict): try: - print('<{0} {1}>'.format(slot, conf['this']), file=ret) + print("<{0} {1}>".format(slot, conf["this"]), file=ret) except KeyError: - raise SaltException('Apache section container "<{0}>" expects attribute. ' - 'Specify it using key "this".'.format(slot)) + raise SaltException( + 'Apache section container "<{0}>" expects attribute. ' + 'Specify it using key "this".'.format(slot) + ) for key, value in six.iteritems(conf): - if key != 'this': + if key != "this": if isinstance(value, six.string_types): - print('{0} {1}'.format(key, value), file=ret) + print("{0} {1}".format(key, value), file=ret) elif isinstance(value, list): print(_parse_config(value, key), file=ret) elif isinstance(value, dict): print(_parse_config(value, key), file=ret) - print(''.format(slot), file=ret) + print("".format(slot), file=ret) ret.seek(0) return ret.read() def config(name, config, edit=True): - ''' + """ Create VirtualHost configuration files name @@ -468,7 +465,7 @@ def config(name, config, edit=True): .. code-block:: bash salt '*' apache.config /etc/httpd/conf.d/ports.conf config="[{'Listen': '22'}]" - ''' + """ configs = [] for entry in config: @@ -476,9 +473,9 @@ def config(name, config, edit=True): configs.append(_parse_config(entry[key], key)) # Python auto-correct line endings - configstext = '\n'.join(salt.utils.data.decode(configs)) + configstext = "\n".join(salt.utils.data.decode(configs)) if edit: - with salt.utils.files.fopen(name, 'w') as configfile: - configfile.write('# This file is managed by Salt.\n') + with salt.utils.files.fopen(name, "w") as configfile: + configfile.write("# This file is managed by Salt.\n") configfile.write(salt.utils.stringutils.to_str(configstext)) return configstext diff --git a/salt/modules/apcups.py b/salt/modules/apcups.py index b6b972add20..2792604d895 100644 --- a/salt/modules/apcups.py +++ b/salt/modules/apcups.py @@ -1,46 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" Module for apcupsd -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging +import salt.utils.decorators as decorators + # Import Salt libs import salt.utils.path -import salt.utils.decorators as decorators log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'apcups' +__virtualname__ = "apcups" @decorators.memoize def _check_apcaccess(): - ''' + """ Looks to see if apcaccess is present on the system - ''' - return salt.utils.path.which('apcaccess') + """ + return salt.utils.path.which("apcaccess") def __virtual__(): - ''' + """ Provides apcupsd only if apcaccess is present - ''' + """ if _check_apcaccess(): return __virtualname__ return ( False, - '{0} module can only be loaded on when apcupsd is installed'.format( + "{0} module can only be loaded on when apcupsd is installed".format( __virtualname__ - ) + ), ) def status(): - ''' + """ Return apcaccess output CLI Example: @@ -48,24 +49,24 @@ def status(): .. code-block:: bash salt '*' apcups.status - ''' + """ ret = {} apcaccess = _check_apcaccess() - res = __salt__['cmd.run_all'](apcaccess) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](apcaccess) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = 'Something with wrong executing apcaccess, is apcupsd running?' + ret["Error"] = "Something with wrong executing apcaccess, is apcupsd running?" return ret - for line in res['stdout'].splitlines(): - line = line.split(':') + for line in res["stdout"].splitlines(): + line = line.split(":") ret[line[0].strip()] = line[1].strip() return ret def status_load(): - ''' + """ Return load CLI Example: @@ -73,18 +74,18 @@ def status_load(): .. code-block:: bash salt '*' apcups.status_load - ''' + """ data = status() - if 'LOADPCT' in data: - load = data['LOADPCT'].split() - if load[1].lower() == 'percent': + if "LOADPCT" in data: + load = data["LOADPCT"].split() + if load[1].lower() == "percent": return float(load[0]) - return {'Error': 'Load not available.'} + return {"Error": "Load not available."} def status_charge(): - ''' + """ Return battery charge CLI Example: @@ -92,18 +93,18 @@ def status_charge(): .. code-block:: bash salt '*' apcups.status_charge - ''' + """ data = status() - if 'BCHARGE' in data: - charge = data['BCHARGE'].split() - if charge[1].lower() == 'percent': + if "BCHARGE" in data: + charge = data["BCHARGE"].split() + if charge[1].lower() == "percent": return float(charge[0]) - return {'Error': 'Load not available.'} + return {"Error": "Load not available."} def status_battery(): - ''' + """ Return true if running on battery power CLI Example: @@ -111,12 +112,12 @@ def status_battery(): .. code-block:: bash salt '*' apcups.status_battery - ''' + """ data = status() - if 'TONBATT' in data: - return not data['TONBATT'] == '0 Seconds' + if "TONBATT" in data: + return not data["TONBATT"] == "0 Seconds" - return {'Error': 'Battery status not available.'} + return {"Error": "Battery status not available."} # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/apf.py b/salt/modules/apf.py index 8fa574bece1..9a5c1e4707d 100644 --- a/salt/modules/apf.py +++ b/salt/modules/apf.py @@ -1,166 +1,166 @@ # -*- coding: utf-8 -*- -''' +""" Support for Advanced Policy Firewall (APF) ========================================== :maintainer: Mostafa Hussein :maturity: new :depends: python-iptables :platform: Linux -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -try: - import iptc - IPTC_IMPORTED = True -except ImportError: - IPTC_IMPORTED = False - # Import Salt Libs import salt.utils.path from salt.exceptions import CommandExecutionError +try: + import iptc + + IPTC_IMPORTED = True +except ImportError: + IPTC_IMPORTED = False + def __virtual__(): - ''' + """ Only load if apf exists on the system - ''' - if salt.utils.path.which('apf') is None: - return (False, - 'The apf execution module cannot be loaded: apf unavailable.') + """ + if salt.utils.path.which("apf") is None: + return (False, "The apf execution module cannot be loaded: apf unavailable.") elif not IPTC_IMPORTED: - return (False, - 'The apf execution module cannot be loaded: python-iptables is missing.') + return ( + False, + "The apf execution module cannot be loaded: python-iptables is missing.", + ) else: return True def __apf_cmd(cmd): - ''' + """ Return the apf location - ''' - apf_cmd = '{0} {1}'.format(salt.utils.path.which('apf'), cmd) - out = __salt__['cmd.run_all'](apf_cmd) + """ + apf_cmd = "{0} {1}".format(salt.utils.path.which("apf"), cmd) + out = __salt__["cmd.run_all"](apf_cmd) - if out['retcode'] != 0: - if not out['stderr']: - msg = out['stdout'] + if out["retcode"] != 0: + if not out["stderr"]: + msg = out["stdout"] else: - msg = out['stderr'] - raise CommandExecutionError( - 'apf failed: {0}'.format(msg) - ) - return out['stdout'] + msg = out["stderr"] + raise CommandExecutionError("apf failed: {0}".format(msg)) + return out["stdout"] def _status_apf(): - ''' + """ Return True if apf is running otherwise return False - ''' + """ status = 0 table = iptc.Table(iptc.Table.FILTER) for chain in table.chains: - if 'sanity' in chain.name.lower(): + if "sanity" in chain.name.lower(): status = 1 return True if status else False def running(): - ''' + """ Check apf status CLI Example: .. code-block:: bash salt '*' apf.running - ''' + """ return True if _status_apf() else False def disable(): - ''' + """ Stop (flush) all firewall rules CLI Example: .. code-block:: bash salt '*' apf.disable - ''' + """ if _status_apf(): - return __apf_cmd('-f') + return __apf_cmd("-f") def enable(): - ''' + """ Load all firewall rules CLI Example: .. code-block:: bash salt '*' apf.enable - ''' + """ if not _status_apf(): - return __apf_cmd('-s') + return __apf_cmd("-s") def reload(): - ''' + """ Stop (flush) & reload firewall rules CLI Example: .. code-block:: bash salt '*' apf.reload - ''' + """ if not _status_apf(): - return __apf_cmd('-r') + return __apf_cmd("-r") def refresh(): - ''' + """ Refresh & resolve dns names in trust rules CLI Example: .. code-block:: bash salt '*' apf.refresh - ''' - return __apf_cmd('-e') + """ + return __apf_cmd("-e") def allow(ip, port=None): - ''' + """ Add host (IP/FQDN) to allow_hosts.rules and immediately load new rule into firewall CLI Example: .. code-block:: bash salt '*' apf.allow 127.0.0.1 - ''' + """ if port is None: - return __apf_cmd('-a {0}'.format(ip)) + return __apf_cmd("-a {0}".format(ip)) def deny(ip): - ''' + """ Add host (IP/FQDN) to deny_hosts.rules and immediately load new rule into firewall CLI Example: .. code-block:: bash salt '*' apf.deny 1.2.3.4 - ''' - return __apf_cmd('-d {0}'.format(ip)) + """ + return __apf_cmd("-d {0}".format(ip)) def remove(ip): - ''' + """ Remove host from [glob]*_hosts.rules and immediately remove rule from firewall CLI Example: .. code-block:: bash salt '*' apf.remove 1.2.3.4 - ''' - return __apf_cmd('-u {0}'.format(ip)) + """ + return __apf_cmd("-u {0}".format(ip)) diff --git a/salt/modules/apkpkg.py b/salt/modules/apkpkg.py index 2e9a2a952e7..42720674e6b 100644 --- a/salt/modules/apkpkg.py +++ b/salt/modules/apkpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for apk .. important:: @@ -10,7 +10,7 @@ Support for apk .. versionadded: 2017.7.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -20,55 +20,55 @@ import logging # Import salt libs import salt.utils.data import salt.utils.itertools - from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Confirm this module is running on an Alpine Linux distribution - ''' - if __grains__.get('os_family', False) == 'Alpine': + """ + if __grains__.get("os_family", False) == "Alpine": return __virtualname__ return (False, "Module apk only works on Alpine Linux based systems") -#def autoremove(list_only=False, purge=False): + +# def autoremove(list_only=False, purge=False): # return 'Not available' -#def hold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 +# def hold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 # return 'Not available' -#def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 +# def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 # return 'Not available' -#def upgrade_available(name): +# def upgrade_available(name): # return 'Not available' -#def version_cmp(pkg1, pkg2, ignore_epoch=False): +# def version_cmp(pkg1, pkg2, ignore_epoch=False): # return 'Not available' -#def list_repos(): +# def list_repos(): # return 'Not available' -#def get_repo(repo, **kwargs): +# def get_repo(repo, **kwargs): # return 'Not available' -#def del_repo(repo, **kwargs): +# def del_repo(repo, **kwargs): # return 'Not available' -#def del_repo_key(name=None, **kwargs): +# def del_repo_key(name=None, **kwargs): # return 'Not available' -#def mod_repo(repo, saltenv='base', **kwargs): +# def mod_repo(repo, saltenv='base', **kwargs): # return 'Not available' -#def expand_repo_def(**kwargs): +# def expand_repo_def(**kwargs): # return 'Not available' -#def get_selections(pattern=None, state=None): +# def get_selections(pattern=None, state=None): # return 'Not available' -#def set_selections(path=None, selection=None, clear=False, saltenv='base'): +# def set_selections(path=None, selection=None, clear=False, saltenv='base'): # return 'Not available' -#def info_installed(*names): +# def info_installed(*names): # return 'Not available' def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -79,12 +79,12 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def refresh_db(): - ''' + """ Updates the package list - ``True``: Database updated successfully @@ -95,30 +95,28 @@ def refresh_db(): .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ ret = {} - cmd = ['apk', 'update'] - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - if call['retcode'] == 0: + cmd = ["apk", "update"] + call = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if call["retcode"] == 0: errors = [] ret = True else: - errors = [call['stdout']] + errors = [call["stdout"]] ret = False if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed in a dict:: {'': ''} @@ -129,38 +127,39 @@ def list_pkgs(versions_as_list=False, **kwargs): salt '*' pkg.list_pkgs salt '*' pkg.list_pkgs versions_as_list=True - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret - cmd = ['apk', 'info', '-v'] + cmd = ["apk", "info", "-v"] ret = {} - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): - pkg_version = '-'.join(line.split('-')[-2:]) - pkg_name = '-'.join(line.split('-')[:-2]) - __salt__['pkg_resource.add_pkg'](ret, pkg_name, pkg_version) + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): + pkg_version = "-".join(line.split("-")[-2:]) + pkg_name = "-".join(line.split("-")[:-2]) + __salt__["pkg_resource.add_pkg"](ret, pkg_name, pkg_version) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -175,15 +174,15 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version salt '*' pkg.latest_version ... - ''' - refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) + """ + refresh = salt.utils.data.is_true(kwargs.pop("refresh", True)) if len(names) == 0: - return '' + return "" ret = {} for name in names: - ret[name] = '' + ret[name] = "" pkgs = list_pkgs() # Refresh before looking for the latest version available @@ -191,15 +190,13 @@ def latest_version(*names, **kwargs): refresh_db() # Upgrade check - cmd = ['apk', 'upgrade', '-s'] - out = __salt__['cmd.run_stdout'](cmd, - output_loglevel='trace', - python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): + cmd = ["apk", "upgrade", "-s"] + out = __salt__["cmd.run_stdout"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): try: - name = line.split(' ')[2] - _oldversion = line.split(' ')[3].strip('(') - newversion = line.split(' ')[5].strip(')') + name = line.split(" ")[2] + _oldversion = line.split(" ")[3].strip("(") + newversion = line.split(" ")[5].strip(")") if name in names: ret[name] = newversion except (ValueError, IndexError): @@ -209,17 +206,17 @@ def latest_version(*names, **kwargs): for pkg in ret: if not ret[pkg]: installed = pkgs.get(pkg) - cmd = ['apk', 'search', pkg] - out = __salt__['cmd.run_stdout'](cmd, - output_loglevel='trace', - python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): + cmd = ["apk", "search", pkg] + out = __salt__["cmd.run_stdout"]( + cmd, output_loglevel="trace", python_shell=False + ) + for line in salt.utils.itertools.split(out, "\n"): try: - pkg_version = '-'.join(line.split('-')[-2:]) - pkg_name = '-'.join(line.split('-')[:-2]) + pkg_version = "-".join(line.split("-")[-2:]) + pkg_name = "-".join(line.split("-")[:-2]) if pkg == pkg_name: if installed == pkg_version: - ret[pkg] = '' + ret[pkg] = "" else: ret[pkg] = pkg_version except ValueError: @@ -232,12 +229,8 @@ def latest_version(*names, **kwargs): # TODO: Support specific version installation -def install(name=None, - refresh=False, - pkgs=None, - sources=None, - **kwargs): - ''' +def install(name=None, refresh=False, pkgs=None, sources=None, **kwargs): + """ Install the passed package, add refresh=True to update the apk database. name @@ -288,25 +281,22 @@ def install(name=None, {'': {'old': '', 'new': ''}} - ''' + """ refreshdb = salt.utils.data.is_true(refresh) pkg_to_install = [] old = list_pkgs() if name and not (pkgs or sources): - if ',' in name: - pkg_to_install = name.split(',') + if "," in name: + pkg_to_install = name.split(",") else: pkg_to_install = [name] if pkgs: # We don't support installing specific version for now # so transform the dict in list ignoring version provided - pkgs = [ - next(iter(p)) for p in pkgs - if isinstance(p, dict) - ] + pkgs = [next(iter(p)) for p in pkgs if isinstance(p, dict)] pkg_to_install.extend(pkgs) if not pkg_to_install: @@ -315,49 +305,47 @@ def install(name=None, if refreshdb: refresh_db() - cmd = ['apk', 'add'] + cmd = ["apk", "add"] # Switch in update mode if a package is already installed for _pkg in pkg_to_install: if old.get(_pkg): - cmd.append('-u') + cmd.append("-u") break cmd.extend(pkg_to_install) - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False - ) + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def purge(name=None, pkgs=None, **kwargs): - ''' + """ Alias to remove - ''' + """ return remove(name=name, pkgs=pkgs, purge=True) -def remove(name=None, pkgs=None, purge=False, **kwargs): # pylint: disable=unused-argument - ''' +def remove( + name=None, pkgs=None, purge=False, **kwargs +): # pylint: disable=unused-argument + """ Remove packages using ``apk del``. name @@ -379,13 +367,13 @@ def remove(name=None, pkgs=None, purge=False, **kwargs): # pylint: disable=unus salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ old = list_pkgs() pkg_to_remove = [] if name: - if ',' in name: - pkg_to_remove = name.split(',') + if "," in name: + pkg_to_remove = name.split(",") else: pkg_to_remove = [name] @@ -396,37 +384,33 @@ def remove(name=None, pkgs=None, purge=False, **kwargs): # pylint: disable=unus return {} if purge: - cmd = ['apk', 'del', '--purge'] + cmd = ["apk", "del", "--purge"] else: - cmd = ['apk', 'del'] + cmd = ["apk", "del"] cmd.extend(pkg_to_remove) - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False - ) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def upgrade(name=None, pkgs=None, refresh=True): - ''' + """ Upgrades all packages via ``apk upgrade`` or a specific package if name or pkgs is specified. Name is ignored if pkgs is specified @@ -440,11 +424,12 @@ def upgrade(name=None, pkgs=None, refresh=True): .. code-block:: bash salt '*' pkg.upgrade - ''' - ret = {'changes': {}, - 'result': True, - 'comment': '', - } + """ + ret = { + "changes": {}, + "result": True, + "comment": "", + } if salt.utils.data.is_true(refresh): refresh_db() @@ -454,8 +439,8 @@ def upgrade(name=None, pkgs=None, refresh=True): pkg_to_upgrade = [] if name and not pkgs: - if ',' in name: - pkg_to_upgrade = name.split(',') + if "," in name: + pkg_to_upgrade = name.split(",") else: pkg_to_upgrade = [name] @@ -463,30 +448,29 @@ def upgrade(name=None, pkgs=None, refresh=True): pkg_to_upgrade.extend(pkgs) if pkg_to_upgrade: - cmd = ['apk', 'add', '-u'] + cmd = ["apk", "add", "-u"] cmd.extend(pkg_to_upgrade) else: - cmd = ['apk', 'upgrade'] + cmd = ["apk", "upgrade"] - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False, - redirect_stderr=True) + call = __salt__["cmd.run_all"]( + cmd, output_loglevel="trace", python_shell=False, redirect_stderr=True + ) - if call['retcode'] != 0: - ret['result'] = False - if call['stdout']: - ret['comment'] = call['stdout'] + if call["retcode"] != 0: + ret["result"] = False + if call["stdout"]: + ret["comment"] = call["stdout"] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() - ret['changes'] = salt.utils.data.compare_dicts(old, new) + ret["changes"] = salt.utils.data.compare_dicts(old, new) return ret def list_upgrades(refresh=True): - ''' + """ List all available package upgrades. CLI Example: @@ -494,38 +478,36 @@ def list_upgrades(refresh=True): .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ ret = {} if salt.utils.data.is_true(refresh): refresh_db() - cmd = ['apk', 'upgrade', '-s'] - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + cmd = ["apk", "upgrade", "-s"] + call = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] - if 'stdout' in call: - comment += call['stdout'] + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] + if "stdout" in call: + comment += call["stdout"] raise CommandExecutionError(comment) else: - out = call['stdout'] + out = call["stdout"] for line in out.splitlines(): - if 'Upgrading' in line: - name = line.split(' ')[2] - _oldversion = line.split(' ')[3].strip('(') - newversion = line.split(' ')[5].strip(')') + if "Upgrading" in line: + name = line.split(" ")[2] + _oldversion = line.split(" ")[3].strip("(") + newversion = line.split(" ")[5].strip(")") ret[name] = newversion return ret def file_list(*packages): - ''' + """ List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -537,12 +519,12 @@ def file_list(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' + """ return file_dict(*packages) def file_dict(*packages): - ''' + """ List the files that belong to a package, grouped by package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -554,34 +536,32 @@ def file_dict(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' + """ errors = [] ret = {} - cmd_files = ['apk', 'info', '-L'] + cmd_files = ["apk", "info", "-L"] if not packages: - return 'Package name should be provided' + return "Package name should be provided" for package in packages: files = [] cmd = cmd_files[:] cmd.append(package) - out = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - for line in out['stdout'].splitlines(): - if line.endswith('contains:'): + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + for line in out["stdout"].splitlines(): + if line.endswith("contains:"): continue else: files.append(line) if files: ret[package] = files - return {'errors': errors, 'packages': ret} + return {"errors": errors, "packages": ret} def owner(*paths): - ''' + """ Return the name of the package that owns the file. Multiple file paths can be passed. Like :mod:`pkg.version (file|key)s)[\w\s]+:$' - list_pattern = r'^\s+-\s+(?P.*)$' + type_pattern = r"^List\s+[\w\s]+(?P(file|key)s)[\w\s]+:$" + list_pattern = r"^\s+-\s+(?P.*)$" current_block = None for line in cmd_ret.splitlines(): if current_block: match = re.search(list_pattern, line) if match: - package_type = 'deleted_{}'.format(current_block) - ret[package_type].append(match.group('package')) + package_type = "deleted_{}".format(current_block) + ret[package_type].append(match.group("package")) else: current_block = None # Intentionally not using an else here, in case of a situation where @@ -500,8 +548,8 @@ def cleanup_db(config_path=_DEFAULT_CONFIG_PATH, dry_run=False): if not current_block: match = re.search(type_pattern, line) if match: - current_block = match.group('package_type') + current_block = match.group("package_type") - log.debug('Package keys identified for deletion: %s', len(ret['deleted_keys'])) - log.debug('Package files identified for deletion: %s', len(ret['deleted_files'])) + log.debug("Package keys identified for deletion: %s", len(ret["deleted_keys"])) + log.debug("Package files identified for deletion: %s", len(ret["deleted_files"])) return ret diff --git a/salt/modules/aptpkg.py b/salt/modules/aptpkg.py index b5503f0b10d..7cf4130cce4 100644 --- a/salt/modules/aptpkg.py +++ b/salt/modules/aptpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for APT (Advanced Packaging Tool) .. important:: @@ -9,32 +9,24 @@ Support for APT (Advanced Packaging Tool) `. For repository management, the ``python-apt`` package must be installed. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import copy +import datetime +import fnmatch +import logging import os import re -import logging import time -import fnmatch -import datetime - - -# Import third party libs -# pylint: disable=no-name-in-module,import-error,redefined-builtin -from salt.ext import six -from salt.ext.six.moves.urllib.error import HTTPError -from salt.ext.six.moves.urllib.request import Request as _Request, urlopen as _urlopen -# pylint: enable=no-name-in-module,import-error,redefined-builtin # Import salt libs import salt.config import salt.syspaths -from salt.modules.cmdmod import _parse_env import salt.utils.args import salt.utils.data +import salt.utils.environment import salt.utils.files import salt.utils.functools import salt.utils.itertools @@ -46,10 +38,18 @@ import salt.utils.stringutils import salt.utils.systemd import salt.utils.versions import salt.utils.yaml -import salt.utils.environment -from salt.exceptions import ( - CommandExecutionError, MinionError, SaltInvocationError -) +from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError + +# Import third party libs +# pylint: disable=no-name-in-module,import-error,redefined-builtin +from salt.ext import six +from salt.ext.six.moves.urllib.error import HTTPError +from salt.ext.six.moves.urllib.request import Request as _Request +from salt.ext.six.moves.urllib.request import urlopen as _urlopen +from salt.modules.cmdmod import _parse_env + +# pylint: enable=no-name-in-module,import-error,redefined-builtin + log = logging.getLogger(__name__) @@ -58,38 +58,41 @@ try: import apt.cache import apt.debfile from aptsources import sourceslist + HAS_APT = True except ImportError: HAS_APT = False try: import apt_pkg + HAS_APTPKG = True except ImportError: HAS_APTPKG = False try: import softwareproperties.ppa + HAS_SOFTWAREPROPERTIES = True except ImportError: HAS_SOFTWAREPROPERTIES = False # pylint: enable=import-error APT_LISTS_PATH = "/var/lib/apt/lists" -PKG_ARCH_SEPARATOR = ':' +PKG_ARCH_SEPARATOR = ":" # Source format for urllib fallback on PPA handling -LP_SRC_FORMAT = 'deb http://ppa.launchpad.net/{0}/{1}/ubuntu {2} main' -LP_PVT_SRC_FORMAT = 'deb https://{0}private-ppa.launchpad.net/{1}/{2}/ubuntu' \ - ' {3} main' +LP_SRC_FORMAT = "deb http://ppa.launchpad.net/{0}/{1}/ubuntu {2} main" +LP_PVT_SRC_FORMAT = ( + "deb https://{0}private-ppa.launchpad.net/{1}/{2}/ubuntu" " {3} main" +) -_MODIFY_OK = frozenset(['uri', 'comps', 'architectures', 'disabled', - 'file', 'dist']) +_MODIFY_OK = frozenset(["uri", "comps", "architectures", "disabled", "file", "dist"]) DPKG_ENV_VARS = { - 'APT_LISTBUGS_FRONTEND': 'none', - 'APT_LISTCHANGES_FRONTEND': 'none', - 'DEBIAN_FRONTEND': 'noninteractive', - 'UCF_FORCE_CONFFOLD': '1', + "APT_LISTBUGS_FRONTEND": "none", + "APT_LISTCHANGES_FRONTEND": "none", + "DEBIAN_FRONTEND": "noninteractive", + "UCF_FORCE_CONFFOLD": "1", } if six.PY2: # Ensure no unicode in env vars on PY2, as it causes problems with @@ -97,37 +100,37 @@ if six.PY2: DPKG_ENV_VARS = salt.utils.data.encode(DPKG_ENV_VARS) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Confirm this module is on a Debian-based system - ''' + """ # If your minion is running an OS which is Debian-based but does not have # an "os_family" grain of Debian, then the proper fix is NOT to check for # the minion's "os_family" grain here in the __virtual__. The correct fix # is to add the value from the minion's "os" grain to the _OS_FAMILY_MAP # dict in salt/grains/core.py, so that we assign the correct "os_family" # grain to the minion. - if __grains__.get('os_family') == 'Debian': + if __grains__.get("os_family") == "Debian": return __virtualname__ - return False, 'The pkg module could not be loaded: unsupported OS family' + return False, "The pkg module could not be loaded: unsupported OS family" def __init__(opts): - ''' + """ For Debian and derivative systems, set up a few env variables to keep apt happy and non-interactive. - ''' + """ if __virtual__() == __virtualname__: # Export these puppies so they persist os.environ.update(DPKG_ENV_VARS) def _get_ppa_info_from_launchpad(owner_name, ppa_name): - ''' + """ Idea from softwareproperties.ppa. Uses urllib2 which sacrifices server cert verification. @@ -136,61 +139,70 @@ def _get_ppa_info_from_launchpad(owner_name, ppa_name): :param owner_name: :param ppa_name: :return: - ''' + """ - lp_url = 'https://launchpad.net/api/1.0/~{0}/+archive/{1}'.format( - owner_name, ppa_name) - request = _Request(lp_url, headers={'Accept': 'application/json'}) + lp_url = "https://launchpad.net/api/1.0/~{0}/+archive/{1}".format( + owner_name, ppa_name + ) + request = _Request(lp_url, headers={"Accept": "application/json"}) lp_page = _urlopen(request) return salt.utils.json.load(lp_page) def _reconstruct_ppa_name(owner_name, ppa_name): - ''' + """ Stringify PPA name from args. - ''' - return 'ppa:{0}/{1}'.format(owner_name, ppa_name) + """ + return "ppa:{0}/{1}".format(owner_name, ppa_name) def _check_apt(): - ''' + """ Abort if python-apt is not installed - ''' + """ if not HAS_APT: - raise CommandExecutionError( - 'Error: \'python-apt\' package not installed' - ) + raise CommandExecutionError("Error: 'python-apt' package not installed") def _call_apt(args, scope=True, **kwargs): - ''' + """ Call apt* utilities. - ''' + """ cmd = [] - if scope and salt.utils.systemd.has_scope(__context__) and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) + if ( + scope + and salt.utils.systemd.has_scope(__context__) + and __salt__["config.get"]("systemd.scope", True) + ): + cmd.extend( + ["systemd-run", "--scope", "--description", '"{0}"'.format(__name__)] + ) cmd.extend(args) - params = {'output_loglevel': 'trace', - 'python_shell': False, - 'env': salt.utils.environment.get_module_environment(globals())} + params = { + "output_loglevel": "trace", + "python_shell": False, + "env": salt.utils.environment.get_module_environment(globals()), + } params.update(kwargs) - return __salt__['cmd.run_all'](cmd, **params) + return __salt__["cmd.run_all"](cmd, **params) def _warn_software_properties(repo): - ''' + """ Warn of missing python-software-properties package. - ''' - log.warning('The \'python-software-properties\' package is not installed. ' - 'For more accurate support of PPA repositories, you should ' - 'install this package.') - log.warning('Best guess at ppa format: %s', repo) + """ + log.warning( + "The 'python-software-properties' package is not installed. " + "For more accurate support of PPA repositories, you should " + "install this package." + ) + log.warning("Best guess at ppa format: %s", repo) def normalize_name(name): - ''' + """ Strips the architecture from the specified package name, if necessary. CLI Example: @@ -198,7 +210,7 @@ def normalize_name(name): .. code-block:: bash salt '*' pkg.normalize_name zsh:amd64 - ''' + """ try: name, arch = name.rsplit(PKG_ARCH_SEPARATOR, 1) except ValueError: @@ -207,7 +219,7 @@ def normalize_name(name): def parse_arch(name): - ''' + """ Parse name and architecture from the specified package name. CLI Example: @@ -215,19 +227,16 @@ def parse_arch(name): .. code-block:: bash salt '*' pkg.parse_arch zsh:amd64 - ''' + """ try: _name, _arch = name.rsplit(PKG_ARCH_SEPARATOR, 1) except ValueError: _name, _arch = name, None - return { - 'name': _name, - 'arch': _arch - } + return {"name": _name, "arch": _arch} def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -251,44 +260,43 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version fromrepo=unstable salt '*' pkg.latest_version ... - ''' - refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) - show_installed = salt.utils.data.is_true(kwargs.pop('show_installed', False)) - if 'repo' in kwargs: + """ + refresh = salt.utils.data.is_true(kwargs.pop("refresh", True)) + show_installed = salt.utils.data.is_true(kwargs.pop("show_installed", False)) + if "repo" in kwargs: raise SaltInvocationError( - 'The \'repo\' argument is invalid, use \'fromrepo\' instead' + "The 'repo' argument is invalid, use 'fromrepo' instead" ) - fromrepo = kwargs.pop('fromrepo', None) - cache_valid_time = kwargs.pop('cache_valid_time', 0) + fromrepo = kwargs.pop("fromrepo", None) + cache_valid_time = kwargs.pop("cache_valid_time", 0) if len(names) == 0: - return '' + return "" ret = {} # Initialize the dict with empty strings for name in names: - ret[name] = '' + ret[name] = "" pkgs = list_pkgs(versions_as_list=True) - repo = ['-o', 'APT::Default-Release={0}'.format(fromrepo)] \ - if fromrepo else None + repo = ["-o", "APT::Default-Release={0}".format(fromrepo)] if fromrepo else None # Refresh before looking for the latest version available if refresh: refresh_db(cache_valid_time) for name in names: - cmd = ['apt-cache', '-q', 'policy', name] + cmd = ["apt-cache", "-q", "policy", name] if repo is not None: cmd.extend(repo) out = _call_apt(cmd, scope=False) - candidate = '' - for line in salt.utils.itertools.split(out['stdout'], '\n'): - if 'Candidate' in line: + candidate = "" + for line in salt.utils.itertools.split(out["stdout"], "\n"): + if "Candidate" in line: comps = line.split() if len(comps) >= 2: candidate = comps[-1] - if candidate.lower() == '(none)': - candidate = '' + if candidate.lower() == "(none)": + candidate = "" break installed = pkgs.get(name, []) @@ -301,11 +309,12 @@ def latest_version(*names, **kwargs): # to the install candidate, then the candidate is an upgrade, so # add it to the return dict if not any( - (salt.utils.versions.compare(ver1=x, - oper='>=', - ver2=candidate, - cmp_func=version_cmp) - for x in installed) + ( + salt.utils.versions.compare( + ver1=x, oper=">=", ver2=candidate, cmp_func=version_cmp + ) + for x in installed + ) ): ret[name] = candidate @@ -316,11 +325,13 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -331,12 +342,12 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def refresh_db(cache_valid_time=0, failhard=False): - ''' + """ Updates the APT database to latest packages based upon repositories Returns a dict, with the keys being package databases and the values being @@ -365,7 +376,7 @@ def refresh_db(cache_valid_time=0, failhard=False): .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) failhard = salt.utils.data.is_true(failhard) @@ -376,59 +387,70 @@ def refresh_db(cache_valid_time=0, failhard=False): try: latest_update = os.stat(APT_LISTS_PATH).st_mtime now = time.time() - log.debug("now: %s, last update time: %s, expire after: %s seconds", now, latest_update, cache_valid_time) + log.debug( + "now: %s, last update time: %s, expire after: %s seconds", + now, + latest_update, + cache_valid_time, + ) if latest_update + cache_valid_time > now: return ret except TypeError as exp: - log.warning("expected integer for cache_valid_time parameter, failed with: %s", exp) + log.warning( + "expected integer for cache_valid_time parameter, failed with: %s", exp + ) except IOError as exp: log.warning("could not stat cache directory due to: %s", exp) - call = _call_apt(['apt-get', '-q', 'update'], scope=False) - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] + call = _call_apt(["apt-get", "-q", "update"], scope=False) + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] raise CommandExecutionError(comment) else: - out = call['stdout'] + out = call["stdout"] for line in out.splitlines(): cols = line.split() if not cols: continue - ident = ' '.join(cols[1:]) - if 'Get' in cols[0]: + ident = " ".join(cols[1:]) + if "Get" in cols[0]: # Strip filesize from end of line - ident = re.sub(r' \[.+B\]$', '', ident) + ident = re.sub(r" \[.+B\]$", "", ident) ret[ident] = True - elif 'Ign' in cols[0]: + elif "Ign" in cols[0]: ret[ident] = False - elif 'Hit' in cols[0]: + elif "Hit" in cols[0]: ret[ident] = None - elif 'Err' in cols[0]: + elif "Err" in cols[0]: ret[ident] = False error_repos.append(ident) if failhard and error_repos: - raise CommandExecutionError('Error getting repos: {0}'.format(', '.join(error_repos))) + raise CommandExecutionError( + "Error getting repos: {0}".format(", ".join(error_repos)) + ) return ret -def install(name=None, - refresh=False, - fromrepo=None, - skip_verify=False, - debconf=None, - pkgs=None, - sources=None, - reinstall=False, - downloadonly=False, - ignore_epoch=False, - **kwargs): - ''' +def install( + name=None, + refresh=False, + fromrepo=None, + skip_verify=False, + debconf=None, + pkgs=None, + sources=None, + reinstall=False, + downloadonly=False, + ignore_epoch=False, + **kwargs +): + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -567,16 +589,14 @@ def install(name=None, {'': {'old': '', 'new': ''}} - ''' + """ _refresh_db = False if salt.utils.data.is_true(refresh): _refresh_db = True - if 'version' in kwargs and kwargs['version']: + if "version" in kwargs and kwargs["version"]: _refresh_db = False - _latest_version = latest_version(name, - refresh=False, - show_installed=True) - _version = kwargs.get('version') + _latest_version = latest_version(name, refresh=False, show_installed=True) + _version = kwargs.get("version") # If the versions don't match, refresh is True, otherwise no need # to refresh if not _latest_version == _version: @@ -587,9 +607,9 @@ def install(name=None, for pkg in pkgs: if isinstance(pkg, dict): _name = next(six.iterkeys(pkg)) - _latest_version = latest_version(_name, - refresh=False, - show_installed=True) + _latest_version = latest_version( + _name, refresh=False, show_installed=True + ) _version = pkg[_name] # If the versions don't match, refresh is True, otherwise # no need to refresh @@ -600,17 +620,17 @@ def install(name=None, _refresh_db = True if debconf: - __salt__['debconf.set_file'](debconf) + __salt__["debconf.set_file"](debconf) try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: raise CommandExecutionError(exc) # Support old "repo" argument - repo = kwargs.get('repo', '') + repo = kwargs.get("repo", "") if not fromrepo and repo: fromrepo = repo @@ -624,71 +644,74 @@ def install(name=None, downgrade = [] to_reinstall = {} errors = [] - if pkg_type == 'repository': + if pkg_type == "repository": pkg_params_items = list(six.iteritems(pkg_params)) - has_comparison = [x for x, y in pkg_params_items - if y is not None - and (y.startswith('<') or y.startswith('>'))] - _available = list_repo_pkgs(*has_comparison, byrepo=False, **kwargs) \ - if has_comparison else {} + has_comparison = [ + x + for x, y in pkg_params_items + if y is not None and (y.startswith("<") or y.startswith(">")) + ] + _available = ( + list_repo_pkgs(*has_comparison, byrepo=False, **kwargs) + if has_comparison + else {} + ) # Build command prefix - cmd_prefix.extend(['apt-get', '-q', '-y']) - if kwargs.get('force_yes', False): - cmd_prefix.append('--force-yes') - if 'force_conf_new' in kwargs and kwargs['force_conf_new']: - cmd_prefix.extend(['-o', 'DPkg::Options::=--force-confnew']) + cmd_prefix.extend(["apt-get", "-q", "-y"]) + if kwargs.get("force_yes", False): + cmd_prefix.append("--force-yes") + if "force_conf_new" in kwargs and kwargs["force_conf_new"]: + cmd_prefix.extend(["-o", "DPkg::Options::=--force-confnew"]) else: - cmd_prefix.extend(['-o', 'DPkg::Options::=--force-confold']) - cmd_prefix += ['-o', 'DPkg::Options::=--force-confdef'] - if 'install_recommends' in kwargs: - if not kwargs['install_recommends']: - cmd_prefix.append('--no-install-recommends') + cmd_prefix.extend(["-o", "DPkg::Options::=--force-confold"]) + cmd_prefix += ["-o", "DPkg::Options::=--force-confdef"] + if "install_recommends" in kwargs: + if not kwargs["install_recommends"]: + cmd_prefix.append("--no-install-recommends") else: - cmd_prefix.append('--install-recommends') - if 'only_upgrade' in kwargs and kwargs['only_upgrade']: - cmd_prefix.append('--only-upgrade') + cmd_prefix.append("--install-recommends") + if "only_upgrade" in kwargs and kwargs["only_upgrade"]: + cmd_prefix.append("--only-upgrade") if skip_verify: - cmd_prefix.append('--allow-unauthenticated') + cmd_prefix.append("--allow-unauthenticated") if fromrepo: - cmd_prefix.extend(['-t', fromrepo]) - cmd_prefix.append('install') + cmd_prefix.extend(["-t", fromrepo]) + cmd_prefix.append("install") else: pkg_params_items = [] for pkg_source in pkg_params: - if 'lowpkg.bin_pkg_info' in __salt__: - deb_info = __salt__['lowpkg.bin_pkg_info'](pkg_source) + if "lowpkg.bin_pkg_info" in __salt__: + deb_info = __salt__["lowpkg.bin_pkg_info"](pkg_source) else: deb_info = None if deb_info is None: log.error( - 'pkg.install: Unable to get deb information for %s. ' - 'Version comparisons will be unavailable.', pkg_source + "pkg.install: Unable to get deb information for %s. " + "Version comparisons will be unavailable.", + pkg_source, ) pkg_params_items.append([pkg_source]) else: pkg_params_items.append( - [deb_info['name'], pkg_source, deb_info['version']] + [deb_info["name"], pkg_source, deb_info["version"]] ) # Build command prefix - if 'force_conf_new' in kwargs and kwargs['force_conf_new']: - cmd_prefix.extend(['dpkg', '-i', '--force-confnew']) + if "force_conf_new" in kwargs and kwargs["force_conf_new"]: + cmd_prefix.extend(["dpkg", "-i", "--force-confnew"]) else: - cmd_prefix.extend(['dpkg', '-i', '--force-confold']) + cmd_prefix.extend(["dpkg", "-i", "--force-confold"]) if skip_verify: - cmd_prefix.append('--force-bad-verify') + cmd_prefix.append("--force-bad-verify") if HAS_APT: _resolve_deps(name, pkg_params, **kwargs) for pkg_item_list in pkg_params_items: - if pkg_type == 'repository': + if pkg_type == "repository": pkgname, version_num = pkg_item_list - if name \ - and pkgs is None \ - and kwargs.get('version') \ - and len(pkg_params) == 1: + if name and pkgs is None and kwargs.get("version") and len(pkg_params) == 1: # Only use the 'version' param if 'name' was not specified as a # comma-separated list - version_num = kwargs['version'] + version_num = kwargs["version"] else: try: pkgname, pkgpath, version_num = pkg_item_list @@ -698,7 +721,7 @@ def install(name=None, version_num = None if version_num is None: - if pkg_type == 'repository': + if pkg_type == "repository": if reinstall and pkgname in old: to_reinstall[pkgname] = pkgname else: @@ -710,10 +733,10 @@ def install(name=None, # and version_num is not None, then we can assume that pkgname is # not None, since the only way version_num is not None is if DEB # metadata parsing was successful. - if pkg_type == 'repository': + if pkg_type == "repository": # Remove leading equals sign(s) to keep from building a pkgstr # with multiple equals (which would be invalid) - version_num = version_num.lstrip('=') + version_num = version_num.lstrip("=") if pkgname in has_comparison: candidates = _available.get(pkgname, []) target = salt.utils.pkg.match_version( @@ -724,37 +747,38 @@ def install(name=None, ) if target is None: errors.append( - 'No version matching \'{0}{1}\' could be found ' - '(available: {2})'.format( + "No version matching '{0}{1}' could be found " + "(available: {2})".format( pkgname, version_num, - ', '.join(candidates) if candidates else None + ", ".join(candidates) if candidates else None, ) ) continue else: version_num = target - pkgstr = '{0}={1}'.format(pkgname, version_num) + pkgstr = "{0}={1}".format(pkgname, version_num) else: pkgstr = pkgpath - cver = old.get(pkgname, '') - if reinstall and cver \ - and salt.utils.versions.compare(ver1=version_num, - oper='==', - ver2=cver, - cmp_func=version_cmp): + cver = old.get(pkgname, "") + if ( + reinstall + and cver + and salt.utils.versions.compare( + ver1=version_num, oper="==", ver2=cver, cmp_func=version_cmp + ) + ): to_reinstall[pkgname] = pkgstr - elif not cver or salt.utils.versions.compare(ver1=version_num, - oper='>=', - ver2=cver, - cmp_func=version_cmp): + elif not cver or salt.utils.versions.compare( + ver1=version_num, oper=">=", ver2=cver, cmp_func=version_cmp + ): targets.append(pkgstr) else: downgrade.append(pkgstr) if fromrepo and not sources: - log.info('Targeting repo \'%s\'', fromrepo) + log.info("Targeting repo '%s'", fromrepo) cmds = [] all_pkgs = [] @@ -766,9 +790,9 @@ def install(name=None, if downgrade: cmd = copy.deepcopy(cmd_prefix) - if pkg_type == 'repository' and '--force-yes' not in cmd: + if pkg_type == "repository" and "--force-yes" not in cmd: # Downgrading requires --force-yes. Insert this before 'install' - cmd.insert(-1, '--force-yes') + cmd.insert(-1, "--force-yes") cmd.extend(downgrade) cmds.append(cmd) @@ -779,26 +803,26 @@ def install(name=None, all_pkgs.extend(to_reinstall) cmd = copy.deepcopy(cmd_prefix) if not sources: - cmd.append('--reinstall') + cmd.append("--reinstall") cmd.extend([x for x in six.itervalues(to_reinstall)]) cmds.append(cmd) if not cmds: ret = {} else: - cache_valid_time = kwargs.pop('cache_valid_time', 0) + cache_valid_time = kwargs.pop("cache_valid_time", 0) if _refresh_db: refresh_db(cache_valid_time) - env = _parse_env(kwargs.get('env')) + env = _parse_env(kwargs.get("env")) env.update(DPKG_ENV_VARS.copy()) - hold_pkgs = get_selections(state='hold').get('hold', []) + hold_pkgs = get_selections(state="hold").get("hold", []) # all_pkgs contains the argument to be passed to apt-get install, which # when a specific version is requested will be in the format # name=version. Strip off the '=' if present so we can compare the # held package names against the pacakges we are trying to install. - targeted_names = [x.split('=')[0] for x in all_pkgs] + targeted_names = [x.split("=")[0] for x in all_pkgs] to_unhold = [x for x in hold_pkgs if x in targeted_names] if to_unhold: @@ -806,81 +830,87 @@ def install(name=None, for cmd in cmds: out = _call_apt(cmd) - if out['retcode'] != 0 and out['stderr']: - errors.append(out['stderr']) + if out["retcode"] != 0 and out["stderr"]: + errors.append(out["stderr"]) - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) for pkgname in to_reinstall: if pkgname not in ret or pkgname in old: - ret.update({pkgname: {'old': old.get(pkgname, ''), - 'new': new.get(pkgname, '')}}) + ret.update( + { + pkgname: { + "old": old.get(pkgname, ""), + "new": new.get(pkgname, ""), + } + } + ) if to_unhold: hold(pkgs=to_unhold) if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) return ret -def _uninstall(action='remove', name=None, pkgs=None, **kwargs): - ''' +def _uninstall(action="remove", name=None, pkgs=None, **kwargs): + """ remove and purge do identical things but with different apt-get commands, this function performs the common logic. - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) old = list_pkgs() old_removed = list_pkgs(removed=True) targets = [x for x in pkg_params if x in old] - if action == 'purge': + if action == "purge": targets.extend([x for x in pkg_params if x in old_removed]) if not targets: return {} - cmd = ['apt-get', '-q', '-y', action] + cmd = ["apt-get", "-q", "-y", action] cmd.extend(targets) - env = _parse_env(kwargs.get('env')) + env = _parse_env(kwargs.get("env")) env.update(DPKG_ENV_VARS.copy()) out = _call_apt(cmd, env=env) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() new_removed = list_pkgs(removed=True) changes = salt.utils.data.compare_dicts(old, new) - if action == 'purge': + if action == "purge": ret = { - 'removed': salt.utils.data.compare_dicts(old_removed, new_removed), - 'installed': changes + "removed": salt.utils.data.compare_dicts(old_removed, new_removed), + "installed": changes, } else: ret = changes if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def autoremove(list_only=False, purge=False): - ''' + """ .. versionadded:: 2015.5.0 Remove packages not required by another package using ``apt-get @@ -902,40 +932,40 @@ def autoremove(list_only=False, purge=False): salt '*' pkg.autoremove salt '*' pkg.autoremove list_only=True salt '*' pkg.autoremove purge=True - ''' + """ cmd = [] if list_only: ret = [] - cmd.extend(['apt-get', '--assume-no']) + cmd.extend(["apt-get", "--assume-no"]) if purge: - cmd.append('--purge') - cmd.append('autoremove') - out = _call_apt(cmd, ignore_retcode=True)['stdout'] + cmd.append("--purge") + cmd.append("autoremove") + out = _call_apt(cmd, ignore_retcode=True)["stdout"] found = False for line in out.splitlines(): if found is True: - if line.startswith(' '): + if line.startswith(" "): ret.extend(line.split()) else: found = False - elif 'The following packages will be REMOVED:' in line: + elif "The following packages will be REMOVED:" in line: found = True ret.sort() return ret else: old = list_pkgs() - cmd.extend(['apt-get', '--assume-yes']) + cmd.extend(["apt-get", "--assume-yes"]) if purge: - cmd.append('--purge') - cmd.append('autoremove') + cmd.append("--purge") + cmd.append("autoremove") _call_apt(cmd, ignore_retcode=True) - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() return salt.utils.data.compare_dicts(old, new) def remove(name=None, pkgs=None, **kwargs): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -974,12 +1004,12 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' - return _uninstall(action='remove', name=name, pkgs=pkgs, **kwargs) + """ + return _uninstall(action="remove", name=name, pkgs=pkgs, **kwargs) def purge(name=None, pkgs=None, **kwargs): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -1018,12 +1048,12 @@ def purge(name=None, pkgs=None, **kwargs): salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' - return _uninstall(action='purge', name=name, pkgs=pkgs, **kwargs) + """ + return _uninstall(action="purge", name=name, pkgs=pkgs, **kwargs) def upgrade(refresh=True, dist_upgrade=False, **kwargs): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -1077,43 +1107,50 @@ def upgrade(refresh=True, dist_upgrade=False, **kwargs): .. code-block:: bash salt '*' pkg.upgrade - ''' - cache_valid_time = kwargs.pop('cache_valid_time', 0) + """ + cache_valid_time = kwargs.pop("cache_valid_time", 0) if salt.utils.data.is_true(refresh): refresh_db(cache_valid_time) old = list_pkgs() - if 'force_conf_new' in kwargs and kwargs['force_conf_new']: - force_conf = '--force-confnew' + if "force_conf_new" in kwargs and kwargs["force_conf_new"]: + force_conf = "--force-confnew" else: - force_conf = '--force-confold' - cmd = ['apt-get', '-q', '-y', '-o', 'DPkg::Options::={0}'.format(force_conf), - '-o', 'DPkg::Options::=--force-confdef'] + force_conf = "--force-confold" + cmd = [ + "apt-get", + "-q", + "-y", + "-o", + "DPkg::Options::={0}".format(force_conf), + "-o", + "DPkg::Options::=--force-confdef", + ] - if kwargs.get('force_yes', False): - cmd.append('--force-yes') - if kwargs.get('skip_verify', False): - cmd.append('--allow-unauthenticated') - if kwargs.get('download_only', False) or kwargs.get('downloadonly', False): - cmd.append('--download-only') + if kwargs.get("force_yes", False): + cmd.append("--force-yes") + if kwargs.get("skip_verify", False): + cmd.append("--allow-unauthenticated") + if kwargs.get("download_only", False) or kwargs.get("downloadonly", False): + cmd.append("--download-only") - cmd.append('dist-upgrade' if dist_upgrade else 'upgrade') + cmd.append("dist-upgrade" if dist_upgrade else "upgrade") result = _call_apt(cmd, env=DPKG_ENV_VARS.copy()) - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret def hold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 - ''' + """ .. versionadded:: 2014.7.0 Set package in 'hold' state, meaning it will not be upgraded. @@ -1135,15 +1172,11 @@ def hold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 .. code-block:: bash salt '*' pkg.hold pkgs='["foo", "bar"]' - ''' + """ if not name and not pkgs and not sources: - raise SaltInvocationError( - 'One of name, pkgs, or sources must be specified.' - ) + raise SaltInvocationError("One of name, pkgs, or sources must be specified.") if pkgs and sources: - raise SaltInvocationError( - 'Only one of pkgs or sources can be specified.' - ) + raise SaltInvocationError("Only one of pkgs or sources can be specified.") targets = [] if pkgs: @@ -1159,34 +1192,29 @@ def hold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 if isinstance(target, dict): target = next(iter(target)) - ret[target] = {'name': target, - 'changes': {}, - 'result': False, - 'comment': ''} + ret[target] = {"name": target, "changes": {}, "result": False, "comment": ""} - state = get_selections(pattern=target, state='hold') + state = get_selections(pattern=target, state="hold") if not state: - ret[target]['comment'] = ('Package {0} not currently held.' - .format(target)) - elif not salt.utils.data.is_true(state.get('hold', False)): - if 'test' in __opts__ and __opts__['test']: + ret[target]["comment"] = "Package {0} not currently held.".format(target) + elif not salt.utils.data.is_true(state.get("hold", False)): + if "test" in __opts__ and __opts__["test"]: ret[target].update(result=None) - ret[target]['comment'] = ('Package {0} is set to be held.' - .format(target)) + ret[target]["comment"] = "Package {0} is set to be held.".format(target) else: - result = set_selections(selection={'hold': [target]}) + result = set_selections(selection={"hold": [target]}) ret[target].update(changes=result[target], result=True) - ret[target]['comment'] = ('Package {0} is now being held.' - .format(target)) + ret[target]["comment"] = "Package {0} is now being held.".format(target) else: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is already set to be held.' - .format(target)) + ret[target]["comment"] = "Package {0} is already set to be held.".format( + target + ) return ret def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 - ''' + """ .. versionadded:: 2014.7.0 Set package current in 'hold' state to install state, @@ -1209,15 +1237,11 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06 .. code-block:: bash salt '*' pkg.unhold pkgs='["foo", "bar"]' - ''' + """ if not name and not pkgs and not sources: - raise SaltInvocationError( - 'One of name, pkgs, or sources must be specified.' - ) + raise SaltInvocationError("One of name, pkgs, or sources must be specified.") if pkgs and sources: - raise SaltInvocationError( - 'Only one of pkgs or sources can be specified.' - ) + raise SaltInvocationError("Only one of pkgs or sources can be specified.") targets = [] if pkgs: @@ -1233,37 +1257,35 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06 if isinstance(target, dict): target = next(iter(target)) - ret[target] = {'name': target, - 'changes': {}, - 'result': False, - 'comment': ''} + ret[target] = {"name": target, "changes": {}, "result": False, "comment": ""} state = get_selections(pattern=target) if not state: - ret[target]['comment'] = ('Package {0} does not have a state.' - .format(target)) - elif salt.utils.data.is_true(state.get('hold', False)): - if 'test' in __opts__ and __opts__['test']: + ret[target]["comment"] = "Package {0} does not have a state.".format(target) + elif salt.utils.data.is_true(state.get("hold", False)): + if "test" in __opts__ and __opts__["test"]: ret[target].update(result=None) - ret[target]['comment'] = ('Package {0} is set not to be ' - 'held.'.format(target)) + ret[target]["comment"] = "Package {0} is set not to be " "held.".format( + target + ) else: - result = set_selections(selection={'install': [target]}) + result = set_selections(selection={"install": [target]}) ret[target].update(changes=result[target], result=True) - ret[target]['comment'] = ('Package {0} is no longer being ' - 'held.'.format(target)) + ret[target][ + "comment" + ] = "Package {0} is no longer being " "held.".format(target) else: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is already set not to be ' - 'held.'.format(target)) + ret[target][ + "comment" + ] = "Package {0} is already set not to be " "held.".format(target) return ret -def list_pkgs(versions_as_list=False, - removed=False, - purge_desired=False, - **kwargs): # pylint: disable=W0613 - ''' +def list_pkgs( + versions_as_list=False, removed=False, purge_desired=False, **kwargs +): # pylint: disable=W0613 + """ List the packages currently installed in a dict:: {'': ''} @@ -1290,125 +1312,120 @@ def list_pkgs(versions_as_list=False, salt '*' pkg.list_pkgs salt '*' pkg.list_pkgs versions_as_list=True - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) removed = salt.utils.data.is_true(removed) purge_desired = salt.utils.data.is_true(purge_desired) - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if removed: - ret = copy.deepcopy(__context__['pkg.list_pkgs']['removed']) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]["removed"]) else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']['purge_desired']) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]["purge_desired"]) if not purge_desired: - ret.update(__context__['pkg.list_pkgs']['installed']) + ret.update(__context__["pkg.list_pkgs"]["installed"]) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret - ret = {'installed': {}, 'removed': {}, 'purge_desired': {}} - cmd = ['dpkg-query', '--showformat', - '${Status} ${Package} ${Version} ${Architecture}\n', '-W'] + ret = {"installed": {}, "removed": {}, "purge_desired": {}} + cmd = [ + "dpkg-query", + "--showformat", + "${Status} ${Package} ${Version} ${Architecture}\n", + "-W", + ] - out = __salt__['cmd.run_stdout']( - cmd, - output_loglevel='trace', - python_shell=False) + out = __salt__["cmd.run_stdout"](cmd, output_loglevel="trace", python_shell=False) # Typical lines of output: # install ok installed zsh 4.3.17-1ubuntu1 amd64 # deinstall ok config-files mc 3:4.8.1-2ubuntu1 amd64 for line in out.splitlines(): cols = line.split() try: - linetype, status, name, version_num, arch = \ - [cols[x] for x in (0, 2, 3, 4, 5)] + linetype, status, name, version_num, arch = [ + cols[x] for x in (0, 2, 3, 4, 5) + ] except (ValueError, IndexError): continue - if __grains__.get('cpuarch', '') == 'x86_64': - osarch = __grains__.get('osarch', '') - if arch != 'all' and osarch == 'amd64' and osarch != arch: - name += ':{0}'.format(arch) + if __grains__.get("cpuarch", "") == "x86_64": + osarch = __grains__.get("osarch", "") + if arch != "all" and osarch == "amd64" and osarch != arch: + name += ":{0}".format(arch) if cols: - if ('install' in linetype or 'hold' in linetype) and \ - 'installed' in status: - __salt__['pkg_resource.add_pkg'](ret['installed'], - name, - version_num) - elif 'deinstall' in linetype: - __salt__['pkg_resource.add_pkg'](ret['removed'], - name, - version_num) - elif 'purge' in linetype and status == 'installed': - __salt__['pkg_resource.add_pkg'](ret['purge_desired'], - name, - version_num) + if ("install" in linetype or "hold" in linetype) and "installed" in status: + __salt__["pkg_resource.add_pkg"](ret["installed"], name, version_num) + elif "deinstall" in linetype: + __salt__["pkg_resource.add_pkg"](ret["removed"], name, version_num) + elif "purge" in linetype and status == "installed": + __salt__["pkg_resource.add_pkg"]( + ret["purge_desired"], name, version_num + ) - for pkglist_type in ('installed', 'removed', 'purge_desired'): - __salt__['pkg_resource.sort_pkglist'](ret[pkglist_type]) + for pkglist_type in ("installed", "removed", "purge_desired"): + __salt__["pkg_resource.sort_pkglist"](ret[pkglist_type]) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if removed: - ret = ret['removed'] + ret = ret["removed"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']['purge_desired']) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]["purge_desired"]) if not purge_desired: - ret.update(__context__['pkg.list_pkgs']['installed']) + ret.update(__context__["pkg.list_pkgs"]["installed"]) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def _get_upgradable(dist_upgrade=True, **kwargs): - ''' + """ Utility function to get upgradable packages Sample return data: { 'pkgname': '1.2.3-45', ... } - ''' + """ - cmd = ['apt-get', '--just-print'] + cmd = ["apt-get", "--just-print"] if dist_upgrade: - cmd.append('dist-upgrade') + cmd.append("dist-upgrade") else: - cmd.append('upgrade') + cmd.append("upgrade") try: - cmd.extend(['-o', 'APT::Default-Release={0}'.format(kwargs['fromrepo'])]) + cmd.extend(["-o", "APT::Default-Release={0}".format(kwargs["fromrepo"])]) except KeyError: pass call = _call_apt(cmd) - if call['retcode'] != 0: - msg = 'Failed to get upgrades' - for key in ('stderr', 'stdout'): + if call["retcode"] != 0: + msg = "Failed to get upgrades" + for key in ("stderr", "stdout"): if call[key]: - msg += ': ' + call[key] + msg += ": " + call[key] break raise CommandExecutionError(msg) else: - out = call['stdout'] + out = call["stdout"] # rexp parses lines that look like the following: # Conf libxfont1 (1:1.4.5-1 Debian:testing [i386]) - rexp = re.compile('(?m)^Conf ' - '([^ ]+) ' # Package name - r'\(([^ ]+)') # Version - keys = ['name', 'version'] + rexp = re.compile("(?m)^Conf " "([^ ]+) " r"\(([^ ]+)") # Package name # Version + keys = ["name", "version"] _get = lambda l, k: l[keys.index(k)] upgrades = rexp.findall(out) ret = {} for line in upgrades: - name = _get(line, 'name') - version_num = _get(line, 'version') + name = _get(line, "name") + version_num = _get(line, "version") ret[name] = version_num return ret def list_upgrades(refresh=True, dist_upgrade=True, **kwargs): - ''' + """ List all available package upgrades. refresh @@ -1431,15 +1448,15 @@ def list_upgrades(refresh=True, dist_upgrade=True, **kwargs): .. code-block:: bash salt '*' pkg.list_upgrades - ''' - cache_valid_time = kwargs.pop('cache_valid_time', 0) + """ + cache_valid_time = kwargs.pop("cache_valid_time", 0) if salt.utils.data.is_true(refresh): refresh_db(cache_valid_time) return _get_upgradable(dist_upgrade, **kwargs) def upgrade_available(name): - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -1447,12 +1464,12 @@ def upgrade_available(name): .. code-block:: bash salt '*' pkg.upgrade_available - ''' - return latest_version(name) != '' + """ + return latest_version(name) != "" def version_cmp(pkg1, pkg2, ignore_epoch=False): - ''' + """ Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem making the comparison. @@ -1467,9 +1484,12 @@ def version_cmp(pkg1, pkg2, ignore_epoch=False): .. code-block:: bash salt '*' pkg.version_cmp '0.2.4-0ubuntu1' '0.2.4.1-0ubuntu1' - ''' - normalize = lambda x: six.text_type(x).split(':', 1)[-1] \ - if ignore_epoch else six.text_type(x) + """ + normalize = ( + lambda x: six.text_type(x).split(":", 1)[-1] + if ignore_epoch + else six.text_type(x) + ) # both apt_pkg.version_compare and _cmd_quote need string arguments. pkg1 = normalize(pkg1) pkg2 = normalize(pkg2) @@ -1494,12 +1514,11 @@ def version_cmp(pkg1, pkg2, ignore_epoch=False): # Try to use shell version in case of errors w/python bindings pass try: - for oper, ret in (('lt', -1), ('eq', 0), ('gt', 1)): - cmd = ['dpkg', '--compare-versions', pkg1, oper, pkg2] - retcode = __salt__['cmd.retcode'](cmd, - output_loglevel='trace', - python_shell=False, - ignore_retcode=True) + for oper, ret in (("lt", -1), ("eq", 0), ("gt", 1)): + cmd = ["dpkg", "--compare-versions", pkg1, oper, pkg2] + retcode = __salt__["cmd.retcode"]( + cmd, output_loglevel="trace", python_shell=False, ignore_retcode=True + ) if retcode == 0: return ret except Exception as exc: # pylint: disable=broad-except @@ -1508,43 +1527,49 @@ def version_cmp(pkg1, pkg2, ignore_epoch=False): def _split_repo_str(repo): - ''' + """ Return APT source entry as a tuple. - ''' + """ split = sourceslist.SourceEntry(repo) return split.type, split.architectures, split.uri, split.dist, split.comps def _consolidate_repo_sources(sources): - ''' + """ Consolidate APT sources. - ''' + """ if not isinstance(sources, sourceslist.SourcesList): raise TypeError( - '\'{0}\' not a \'{1}\''.format( - type(sources), - sourceslist.SourcesList - ) + "'{0}' not a '{1}'".format(type(sources), sourceslist.SourcesList) ) consolidated = {} delete_files = set() - base_file = sourceslist.SourceEntry('').file + base_file = sourceslist.SourceEntry("").file repos = [s for s in sources.list if not s.invalid] for repo in repos: - repo.uri = repo.uri.rstrip('/') + repo.uri = repo.uri.rstrip("/") # future lint: disable=blacklisted-function - key = str((getattr(repo, 'architectures', []), - repo.disabled, repo.type, repo.uri, repo.dist)) + key = str( + ( + getattr(repo, "architectures", []), + repo.disabled, + repo.type, + repo.uri, + repo.dist, + ) + ) # future lint: enable=blacklisted-function if key in consolidated: combined = consolidated[key] combined_comps = set(repo.comps).union(set(combined.comps)) consolidated[key].comps = list(combined_comps) else: - consolidated[key] = sourceslist.SourceEntry(salt.utils.pkg.deb.strip_uri(repo.line)) + consolidated[key] = sourceslist.SourceEntry( + salt.utils.pkg.deb.strip_uri(repo.line) + ) if repo.file != base_file: delete_files.add(repo.file) @@ -1560,7 +1585,7 @@ def _consolidate_repo_sources(sources): def list_repo_pkgs(*args, **kwargs): # pylint: disable=unused-import - ''' + """ .. versionadded:: 2017.7.0 Returns all available packages. Optionally, package names (and name globs) @@ -1588,21 +1613,21 @@ def list_repo_pkgs(*args, **kwargs): # pylint: disable=unused-import salt '*' pkg.list_repo_pkgs salt '*' pkg.list_repo_pkgs foo bar baz - ''' + """ if args: # Get only information about packages in args - cmd = ['apt-cache', 'show'] + [arg for arg in args] + cmd = ["apt-cache", "show"] + [arg for arg in args] else: # Get information about all available packages - cmd = ['apt-cache', 'dump'] + cmd = ["apt-cache", "dump"] out = _call_apt(cmd, scope=False, ignore_retcode=True) ret = {} pkg_name = None skip_pkg = False - new_pkg = re.compile('^Package: (.+)') - for line in salt.utils.itertools.split(out['stdout'], '\n'): + new_pkg = re.compile("^Package: (.+)") + for line in salt.utils.itertools.split(out["stdout"], "\n"): if not line.strip(): continue try: @@ -1614,14 +1639,14 @@ def list_repo_pkgs(*args, **kwargs): # pylint: disable=unused-import pkg_name = cur_pkg continue comps = line.strip().split(None, 1) - if comps[0] == 'Version:': + if comps[0] == "Version:": ret.setdefault(pkg_name, []).append(comps[1]) return ret def list_repos(): - ''' + """ Lists all repos in the sources.list (and sources.lists.d) files CLI Example: @@ -1630,7 +1655,7 @@ def list_repos(): salt '*' pkg.list_repos salt '*' pkg.list_repos disabled=True - ''' + """ _check_apt() repos = {} sources = sourceslist.SourcesList() @@ -1638,20 +1663,20 @@ def list_repos(): if source.invalid: continue repo = {} - repo['file'] = source.file - repo['comps'] = getattr(source, 'comps', []) - repo['disabled'] = source.disabled - repo['dist'] = source.dist - repo['type'] = source.type - repo['uri'] = source.uri.rstrip('/') - repo['line'] = salt.utils.pkg.deb.strip_uri(source.line.strip()) - repo['architectures'] = getattr(source, 'architectures', []) + repo["file"] = source.file + repo["comps"] = getattr(source, "comps", []) + repo["disabled"] = source.disabled + repo["dist"] = source.dist + repo["type"] = source.type + repo["uri"] = source.uri.rstrip("/") + repo["line"] = salt.utils.pkg.deb.strip_uri(source.line.strip()) + repo["architectures"] = getattr(source, "architectures", []) repos.setdefault(source.uri, []).append(repo) return repos def get_repo(repo, **kwargs): - ''' + """ Display a repo from the sources.list / sources.list.d The repo passed in needs to be a complete repo entry. @@ -1661,33 +1686,31 @@ def get_repo(repo, **kwargs): .. code-block:: bash salt '*' pkg.get_repo "myrepo definition" - ''' + """ _check_apt() - ppa_auth = kwargs.get('ppa_auth', None) + ppa_auth = kwargs.get("ppa_auth", None) # we have to be clever about this since the repo definition formats # are a bit more "loose" than in some other distributions - if repo.startswith('ppa:') and __grains__['os'] in ('Ubuntu', 'Mint', 'neon'): + if repo.startswith("ppa:") and __grains__["os"] in ("Ubuntu", "Mint", "neon"): # This is a PPA definition meaning special handling is needed # to derive the name. - dist = __grains__['lsb_distrib_codename'] - owner_name, ppa_name = repo[4:].split('/') + dist = __grains__["lsb_distrib_codename"] + owner_name, ppa_name = repo[4:].split("/") if ppa_auth: - auth_info = '{0}@'.format(ppa_auth) - repo = LP_PVT_SRC_FORMAT.format(auth_info, owner_name, - ppa_name, dist) + auth_info = "{0}@".format(ppa_auth) + repo = LP_PVT_SRC_FORMAT.format(auth_info, owner_name, ppa_name, dist) else: if HAS_SOFTWAREPROPERTIES: try: - if hasattr(softwareproperties.ppa, 'PPAShortcutHandler'): - repo = softwareproperties.ppa.PPAShortcutHandler( - repo).expand(dist)[0] + if hasattr(softwareproperties.ppa, "PPAShortcutHandler"): + repo = softwareproperties.ppa.PPAShortcutHandler(repo).expand( + dist + )[0] else: - repo = softwareproperties.ppa.expand_ppa_line( - repo, - dist)[0] + repo = softwareproperties.ppa.expand_ppa_line(repo, dist)[0] except NameError as name_error: raise CommandExecutionError( - 'Could not find ppa {0}: {1}'.format(repo, name_error) + "Could not find ppa {0}: {1}".format(repo, name_error) ) else: repo = LP_SRC_FORMAT.format(owner_name, ppa_name, dist) @@ -1696,37 +1719,45 @@ def get_repo(repo, **kwargs): if repos: try: - repo_type, repo_architectures, repo_uri, repo_dist, repo_comps = _split_repo_str(repo) + ( + repo_type, + repo_architectures, + repo_uri, + repo_dist, + repo_comps, + ) = _split_repo_str(repo) if ppa_auth: - uri_match = re.search('(http[s]?://)(.+)', repo_uri) + uri_match = re.search("(http[s]?://)(.+)", repo_uri) if uri_match: if not uri_match.group(2).startswith(ppa_auth): - repo_uri = '{0}{1}@{2}'.format(uri_match.group(1), - ppa_auth, - uri_match.group(2)) + repo_uri = "{0}{1}@{2}".format( + uri_match.group(1), ppa_auth, uri_match.group(2) + ) except SyntaxError: raise CommandExecutionError( - 'Error: repo \'{0}\' is not a well formatted definition' - .format(repo) + "Error: repo '{0}' is not a well formatted definition".format(repo) ) for source in six.itervalues(repos): for sub in source: - if (sub['type'] == repo_type and + if ( + sub["type"] == repo_type + and # strip trailing '/' from repo_uri, it's valid in definition # but not valid when compared to persisted source - sub['uri'].rstrip('/') == repo_uri.rstrip('/') and - sub['dist'] == repo_dist): + sub["uri"].rstrip("/") == repo_uri.rstrip("/") + and sub["dist"] == repo_dist + ): if not repo_comps: return sub for comp in repo_comps: - if comp in sub.get('comps', []): + if comp in sub.get("comps", []): return sub return {} def del_repo(repo, **kwargs): - ''' + """ Delete a repo from the sources.list / sources.list.d If the .list file is in the sources.list.d directory @@ -1741,25 +1772,24 @@ def del_repo(repo, **kwargs): .. code-block:: bash salt '*' pkg.del_repo "myrepo definition" - ''' + """ _check_apt() is_ppa = False - if repo.startswith('ppa:') and __grains__['os'] in ('Ubuntu', 'Mint', 'neon'): + if repo.startswith("ppa:") and __grains__["os"] in ("Ubuntu", "Mint", "neon"): # This is a PPA definition meaning special handling is needed # to derive the name. is_ppa = True - dist = __grains__['lsb_distrib_codename'] + dist = __grains__["lsb_distrib_codename"] if not HAS_SOFTWAREPROPERTIES: _warn_software_properties(repo) - owner_name, ppa_name = repo[4:].split('/') - if 'ppa_auth' in kwargs: - auth_info = '{0}@'.format(kwargs['ppa_auth']) - repo = LP_PVT_SRC_FORMAT.format(auth_info, dist, owner_name, - ppa_name) + owner_name, ppa_name = repo[4:].split("/") + if "ppa_auth" in kwargs: + auth_info = "{0}@".format(kwargs["ppa_auth"]) + repo = LP_PVT_SRC_FORMAT.format(auth_info, dist, owner_name, ppa_name) else: repo = LP_SRC_FORMAT.format(owner_name, ppa_name, dist) else: - if hasattr(softwareproperties.ppa, 'PPAShortcutHandler'): + if hasattr(softwareproperties.ppa, "PPAShortcutHandler"): repo = softwareproperties.ppa.PPAShortcutHandler(repo).expand(dist)[0] else: repo = softwareproperties.ppa.expand_ppa_line(repo, dist)[0] @@ -1769,22 +1799,25 @@ def del_repo(repo, **kwargs): if repos: deleted_from = dict() try: - repo_type, \ - repo_architectures, \ - repo_uri, \ - repo_dist, \ - repo_comps = _split_repo_str(repo) + ( + repo_type, + repo_architectures, + repo_uri, + repo_dist, + repo_comps, + ) = _split_repo_str(repo) except SyntaxError: raise SaltInvocationError( - 'Error: repo \'{0}\' not a well formatted definition' - .format(repo) + "Error: repo '{0}' not a well formatted definition".format(repo) ) for source in repos: - if (source.type == repo_type - and source.architectures == repo_architectures - and source.uri == repo_uri - and source.dist == repo_dist): + if ( + source.type == repo_type + and source.architectures == repo_architectures + and source.uri == repo_uri + and source.dist == repo_dist + ): s_comps = set(source.comps) r_comps = set(repo_comps) @@ -1799,8 +1832,13 @@ def del_repo(repo, **kwargs): # PPAs are special and can add deb-src where expand_ppa_line # doesn't always reflect this. Lets just cleanup here for good # measure - if (is_ppa and repo_type == 'deb' and source.type == 'deb-src' and - source.uri == repo_uri and source.dist == repo_dist): + if ( + is_ppa + and repo_type == "deb" + and source.type == "deb-src" + and source.uri == repo_uri + and source.dist == repo_dist + ): s_comps = set(source.comps) r_comps = set(repo_comps) @@ -1814,16 +1852,15 @@ def del_repo(repo, **kwargs): pass sources.save() if deleted_from: - ret = '' + ret = "" for source in sources: if source.file in deleted_from: deleted_from[source.file] += 1 for repo_file, count in six.iteritems(deleted_from): - msg = 'Repo \'{0}\' has been removed from {1}.\n' - if count == 0 and 'sources.list.d/' in repo_file: + msg = "Repo '{0}' has been removed from {1}.\n" + if count == 0 and "sources.list.d/" in repo_file: if os.path.isfile(repo_file): - msg = ('File {1} containing repo \'{0}\' has been ' - 'removed.') + msg = "File {1} containing repo '{0}' has been " "removed." try: os.remove(repo_file) except OSError: @@ -1834,12 +1871,12 @@ def del_repo(repo, **kwargs): return ret raise CommandExecutionError( - 'Repo {0} doesn\'t exist in the sources.list(s)'.format(repo) + "Repo {0} doesn't exist in the sources.list(s)".format(repo) ) def _convert_if_int(value): - ''' + """ .. versionadded:: 2017.7.0 Convert to an int if necessary. @@ -1848,7 +1885,7 @@ def _convert_if_int(value): :return: The converted or passed value. :rtype: bool|int|str - ''' + """ try: value = int(str(value)) # future lint: disable=blacklisted-function except ValueError: @@ -1857,7 +1894,7 @@ def _convert_if_int(value): def get_repo_keys(): - ''' + """ .. versionadded:: 2017.7.0 List known repo key details. @@ -1870,66 +1907,76 @@ def get_repo_keys(): .. code-block:: bash salt '*' pkg.get_repo_keys - ''' + """ ret = dict() repo_keys = list() # The double usage of '--with-fingerprint' is necessary in order to # retrieve the fingerprint of the subkey. - cmd = ['apt-key', 'adv', '--batch', '--list-public-keys', '--with-fingerprint', - '--with-fingerprint', '--with-colons', '--fixed-list-mode'] + cmd = [ + "apt-key", + "adv", + "--batch", + "--list-public-keys", + "--with-fingerprint", + "--with-fingerprint", + "--with-colons", + "--fixed-list-mode", + ] cmd_ret = _call_apt(cmd, scope=False) - if cmd_ret['retcode'] != 0: - log.error(cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + log.error(cmd_ret["stderr"]) return ret - lines = [line for line in cmd_ret['stdout'].splitlines() if line.strip()] + lines = [line for line in cmd_ret["stdout"].splitlines() if line.strip()] # Reference for the meaning of each item in the colon-separated # record can be found here: https://goo.gl/KIZbvp for line in lines: - items = [_convert_if_int(item.strip()) if item.strip() else None for item in line.split(':')] + items = [ + _convert_if_int(item.strip()) if item.strip() else None + for item in line.split(":") + ] key_props = dict() if len(items) < 2: - log.debug('Skipping line: %s', line) + log.debug("Skipping line: %s", line) continue - if items[0] in ('pub', 'sub'): - key_props.update({ - 'algorithm': items[3], - 'bits': items[2], - 'capability': items[11], - 'date_creation': items[5], - 'date_expiration': items[6], - 'keyid': items[4], - 'validity': items[1] - }) + if items[0] in ("pub", "sub"): + key_props.update( + { + "algorithm": items[3], + "bits": items[2], + "capability": items[11], + "date_creation": items[5], + "date_expiration": items[6], + "keyid": items[4], + "validity": items[1], + } + ) - if items[0] == 'pub': + if items[0] == "pub": repo_keys.append(key_props) else: - repo_keys[-1]['subkey'] = key_props - elif items[0] == 'fpr': - if repo_keys[-1].get('subkey', False): - repo_keys[-1]['subkey'].update({'fingerprint': items[9]}) + repo_keys[-1]["subkey"] = key_props + elif items[0] == "fpr": + if repo_keys[-1].get("subkey", False): + repo_keys[-1]["subkey"].update({"fingerprint": items[9]}) else: - repo_keys[-1].update({'fingerprint': items[9]}) - elif items[0] == 'uid': - repo_keys[-1].update({ - 'uid': items[9], - 'uid_hash': items[7] - }) + repo_keys[-1].update({"fingerprint": items[9]}) + elif items[0] == "uid": + repo_keys[-1].update({"uid": items[9], "uid_hash": items[7]}) for repo_key in repo_keys: - ret[repo_key['keyid']] = repo_key + ret[repo_key["keyid"]] = repo_key return ret -def add_repo_key(path=None, text=None, keyserver=None, keyid=None, saltenv='base'): - ''' +def add_repo_key(path=None, text=None, keyserver=None, keyid=None, saltenv="base"): + """ .. versionadded:: 2017.7.0 Add a repo key using ``apt-key add``. @@ -1952,55 +1999,59 @@ def add_repo_key(path=None, text=None, keyserver=None, keyid=None, saltenv='base salt '*' pkg.add_repo_key text="'$KEY1'" salt '*' pkg.add_repo_key keyserver='keyserver.example' keyid='0000AAAA' - ''' - cmd = ['apt-key'] + """ + cmd = ["apt-key"] kwargs = {} current_repo_keys = get_repo_keys() if path: - cached_source_path = __salt__['cp.cache_file'](path, saltenv) + cached_source_path = __salt__["cp.cache_file"](path, saltenv) if not cached_source_path: - log.error('Unable to get cached copy of file: %s', path) + log.error("Unable to get cached copy of file: %s", path) return False - cmd.extend(['add', cached_source_path]) + cmd.extend(["add", cached_source_path]) elif text: - log.debug('Received value: %s', text) + log.debug("Received value: %s", text) - cmd.extend(['add', '-']) - kwargs.update({'stdin': text}) + cmd.extend(["add", "-"]) + kwargs.update({"stdin": text}) elif keyserver: if not keyid: - error_msg = 'No keyid or keyid too short for keyserver: {0}'.format(keyserver) + error_msg = "No keyid or keyid too short for keyserver: {0}".format( + keyserver + ) raise SaltInvocationError(error_msg) - cmd.extend(['adv', '--batch', '--keyserver', keyserver, '--recv', keyid]) + cmd.extend(["adv", "--batch", "--keyserver", keyserver, "--recv", keyid]) elif keyid: - error_msg = 'No keyserver specified for keyid: {0}'.format(keyid) + error_msg = "No keyserver specified for keyid: {0}".format(keyid) raise SaltInvocationError(error_msg) else: - raise TypeError('{0}() takes at least 1 argument (0 given)'.format(add_repo_key.__name__)) + raise TypeError( + "{0}() takes at least 1 argument (0 given)".format(add_repo_key.__name__) + ) # If the keyid is provided or determined, check it against the existing # repo key ids to determine whether it needs to be imported. if keyid: for current_keyid in current_repo_keys: - if current_keyid[-(len(keyid)):] == keyid: + if current_keyid[-(len(keyid)) :] == keyid: log.debug("The keyid '%s' already present: %s", keyid, current_keyid) return True cmd_ret = _call_apt(cmd, **kwargs) - if cmd_ret['retcode'] == 0: + if cmd_ret["retcode"] == 0: return True - log.error('Unable to add repo key: %s', cmd_ret['stderr']) + log.error("Unable to add repo key: %s", cmd_ret["stderr"]) return False def del_repo_key(name=None, **kwargs): - ''' + """ .. versionadded:: 2015.8.0 Remove a repo key using ``apt-key del`` @@ -2026,36 +2077,31 @@ def del_repo_key(name=None, **kwargs): salt '*' pkg.del_repo_key keyid=0123ABCD salt '*' pkg.del_repo_key name='ppa:foo/bar' keyid_ppa=True - ''' - if kwargs.get('keyid_ppa', False): - if isinstance(name, six.string_types) and name.startswith('ppa:'): - owner_name, ppa_name = name[4:].split('/') - ppa_info = _get_ppa_info_from_launchpad( - owner_name, ppa_name) - keyid = ppa_info['signing_key_fingerprint'][-8:] + """ + if kwargs.get("keyid_ppa", False): + if isinstance(name, six.string_types) and name.startswith("ppa:"): + owner_name, ppa_name = name[4:].split("/") + ppa_info = _get_ppa_info_from_launchpad(owner_name, ppa_name) + keyid = ppa_info["signing_key_fingerprint"][-8:] else: - raise SaltInvocationError( - 'keyid_ppa requires that a PPA be passed' - ) + raise SaltInvocationError("keyid_ppa requires that a PPA be passed") else: - if 'keyid' in kwargs: - keyid = kwargs.get('keyid') + if "keyid" in kwargs: + keyid = kwargs.get("keyid") else: - raise SaltInvocationError( - 'keyid or keyid_ppa and PPA name must be passed' - ) + raise SaltInvocationError("keyid or keyid_ppa and PPA name must be passed") - result = _call_apt(['apt-key', 'del', keyid], scope=False) - if result['retcode'] != 0: - msg = 'Failed to remove keyid {0}' - if result['stderr']: - msg += ': {0}'.format(result['stderr']) + result = _call_apt(["apt-key", "del", keyid], scope=False) + if result["retcode"] != 0: + msg = "Failed to remove keyid {0}" + if result["stderr"]: + msg += ": {0}".format(result["stderr"]) raise CommandExecutionError(msg) return keyid -def mod_repo(repo, saltenv='base', **kwargs): - ''' +def mod_repo(repo, saltenv="base", **kwargs): + """ Modify one or more values for a repo. If the repo does not exist, it will be created, so long as the definition is well formed. For Ubuntu the ``ppa:/repo`` format is acceptable. ``ppa:`` format can only be @@ -2117,22 +2163,21 @@ def mod_repo(repo, saltenv='base', **kwargs): salt '*' pkg.mod_repo 'myrepo definition' uri=http://new/uri salt '*' pkg.mod_repo 'myrepo definition' comps=main,universe - ''' - if 'refresh_db' in kwargs: - refresh = kwargs['refresh_db'] + """ + if "refresh_db" in kwargs: + refresh = kwargs["refresh_db"] else: - refresh = kwargs.get('refresh', True) + refresh = kwargs.get("refresh", True) _check_apt() # to ensure no one sets some key values that _shouldn't_ be changed on the # object itself, this is just a white-list of "ok" to set properties - if repo.startswith('ppa:'): - if __grains__['os'] in ('Ubuntu', 'Mint', 'neon'): + if repo.startswith("ppa:"): + if __grains__["os"] in ("Ubuntu", "Mint", "neon"): # secure PPAs cannot be supported as of the time of this code # implementation via apt-add-repository. The code path for # secure PPAs should be the same as urllib method - if salt.utils.path.which('apt-add-repository') \ - and 'ppa_auth' not in kwargs: + if salt.utils.path.which("apt-add-repository") and "ppa_auth" not in kwargs: repo_info = get_repo(repo) if repo_info: return {repo: repo_info} @@ -2140,21 +2185,20 @@ def mod_repo(repo, saltenv='base', **kwargs): env = None http_proxy_url = _get_http_proxy_url() if http_proxy_url: - env = {'http_proxy': http_proxy_url, - 'https_proxy': http_proxy_url} - if float(__grains__['osrelease']) < 12.04: - cmd = ['apt-add-repository', repo] + env = { + "http_proxy": http_proxy_url, + "https_proxy": http_proxy_url, + } + if float(__grains__["osrelease"]) < 12.04: + cmd = ["apt-add-repository", repo] else: - cmd = ['apt-add-repository', '-y', repo] + cmd = ["apt-add-repository", "-y", repo] out = _call_apt(cmd, env=env, scope=False, **kwargs) - if out['retcode']: + if out["retcode"]: raise CommandExecutionError( - 'Unable to add PPA \'{0}\'. \'{1}\' exited with ' - 'status {2!s}: \'{3}\' '.format( - repo[4:], - cmd, - out['retcode'], - out['stderr'] + "Unable to add PPA '{0}'. '{1}' exited with " + "status {2!s}: '{3}' ".format( + repo[4:], cmd, out["retcode"], out["stderr"] ) ) # explicit refresh when a repo is modified. @@ -2165,77 +2209,79 @@ def mod_repo(repo, saltenv='base', **kwargs): if not HAS_SOFTWAREPROPERTIES: _warn_software_properties(repo) else: - log.info('Falling back to urllib method for private PPA') + log.info("Falling back to urllib method for private PPA") # fall back to urllib style try: - owner_name, ppa_name = repo[4:].split('/', 1) + owner_name, ppa_name = repo[4:].split("/", 1) except ValueError: raise CommandExecutionError( - 'Unable to get PPA info from argument. ' + "Unable to get PPA info from argument. " 'Expected format "/" ' - '(e.g. saltstack/salt) not found. Received ' - '\'{0}\' instead.'.format(repo[4:]) + "(e.g. saltstack/salt) not found. Received " + "'{0}' instead.".format(repo[4:]) ) - dist = __grains__['lsb_distrib_codename'] + dist = __grains__["lsb_distrib_codename"] # ppa has a lot of implicit arguments. Make them explicit. # These will defer to any user-defined variants - kwargs['dist'] = dist - ppa_auth = '' - if 'file' not in kwargs: - filename = '/etc/apt/sources.list.d/{0}-{1}-{2}.list' - kwargs['file'] = filename.format(owner_name, ppa_name, - dist) + kwargs["dist"] = dist + ppa_auth = "" + if "file" not in kwargs: + filename = "/etc/apt/sources.list.d/{0}-{1}-{2}.list" + kwargs["file"] = filename.format(owner_name, ppa_name, dist) try: launchpad_ppa_info = _get_ppa_info_from_launchpad( - owner_name, ppa_name) - if 'ppa_auth' not in kwargs: - kwargs['keyid'] = launchpad_ppa_info[ - 'signing_key_fingerprint'] + owner_name, ppa_name + ) + if "ppa_auth" not in kwargs: + kwargs["keyid"] = launchpad_ppa_info["signing_key_fingerprint"] else: - if 'keyid' not in kwargs: - error_str = 'Private PPAs require a ' \ - 'keyid to be specified: {0}/{1}' + if "keyid" not in kwargs: + error_str = ( + "Private PPAs require a " + "keyid to be specified: {0}/{1}" + ) raise CommandExecutionError( error_str.format(owner_name, ppa_name) ) except HTTPError as exc: raise CommandExecutionError( - 'Launchpad does not know about {0}/{1}: {2}'.format( - owner_name, ppa_name, exc) + "Launchpad does not know about {0}/{1}: {2}".format( + owner_name, ppa_name, exc + ) ) except IndexError as exc: raise CommandExecutionError( - 'Launchpad knows about {0}/{1} but did not ' - 'return a fingerprint. Please set keyid ' - 'manually: {2}'.format(owner_name, ppa_name, exc) + "Launchpad knows about {0}/{1} but did not " + "return a fingerprint. Please set keyid " + "manually: {2}".format(owner_name, ppa_name, exc) ) - if 'keyserver' not in kwargs: - kwargs['keyserver'] = 'keyserver.ubuntu.com' - if 'ppa_auth' in kwargs: - if not launchpad_ppa_info['private']: + if "keyserver" not in kwargs: + kwargs["keyserver"] = "keyserver.ubuntu.com" + if "ppa_auth" in kwargs: + if not launchpad_ppa_info["private"]: raise CommandExecutionError( - 'PPA is not private but auth credentials ' - 'passed: {0}'.format(repo) + "PPA is not private but auth credentials " + "passed: {0}".format(repo) ) # assign the new repo format to the "repo" variable # so we can fall through to the "normal" mechanism # here. - if 'ppa_auth' in kwargs: - ppa_auth = '{0}@'.format(kwargs['ppa_auth']) - repo = LP_PVT_SRC_FORMAT.format(ppa_auth, owner_name, - ppa_name, dist) + if "ppa_auth" in kwargs: + ppa_auth = "{0}@".format(kwargs["ppa_auth"]) + repo = LP_PVT_SRC_FORMAT.format( + ppa_auth, owner_name, ppa_name, dist + ) else: repo = LP_SRC_FORMAT.format(owner_name, ppa_name, dist) else: raise CommandExecutionError( - 'cannot parse "ppa:" style repo definitions: {0}' - .format(repo) + 'cannot parse "ppa:" style repo definitions: {0}'.format(repo) ) sources = sourceslist.SourcesList() - if kwargs.get('consolidate', False): + if kwargs.get("consolidate", False): # attempt to de-dup and consolidate all sources # down to entries in sources.list # this option makes it easier to keep the sources @@ -2252,122 +2298,148 @@ def mod_repo(repo, saltenv='base', **kwargs): repos = [s for s in sources if not s.invalid] mod_source = None try: - repo_type, \ - repo_architectures, \ - repo_uri, \ - repo_dist, \ - repo_comps = _split_repo_str(repo) + ( + repo_type, + repo_architectures, + repo_uri, + repo_dist, + repo_comps, + ) = _split_repo_str(repo) except SyntaxError: raise SyntaxError( - 'Error: repo \'{0}\' not a well formatted definition'.format(repo) + "Error: repo '{0}' not a well formatted definition".format(repo) ) full_comp_list = set(repo_comps) - no_proxy = __salt__['config.option']('no_proxy') + no_proxy = __salt__["config.option"]("no_proxy") - if 'keyid' in kwargs: - keyid = kwargs.pop('keyid', None) - keyserver = kwargs.pop('keyserver', None) + if "keyid" in kwargs: + keyid = kwargs.pop("keyid", None) + keyserver = kwargs.pop("keyserver", None) if not keyid or not keyserver: - error_str = 'both keyserver and keyid options required.' + error_str = "both keyserver and keyid options required." raise NameError(error_str) if not isinstance(keyid, list): keyid = [keyid] for key in keyid: - if isinstance(key, int): # yaml can make this an int, we need the hex version + if isinstance( + key, int + ): # yaml can make this an int, we need the hex version key = hex(key) - cmd = ['apt-key', 'export', key] - output = __salt__['cmd.run_stdout'](cmd, python_shell=False, **kwargs) - imported = output.startswith('-----BEGIN PGP') + cmd = ["apt-key", "export", key] + output = __salt__["cmd.run_stdout"](cmd, python_shell=False, **kwargs) + imported = output.startswith("-----BEGIN PGP") if keyserver: if not imported: http_proxy_url = _get_http_proxy_url() if http_proxy_url and keyserver not in no_proxy: - cmd = ['apt-key', 'adv', '--batch', '--keyserver-options', 'http-proxy={0}'.format(http_proxy_url), - '--keyserver', keyserver, '--logger-fd', '1', '--recv-keys', key] + cmd = [ + "apt-key", + "adv", + "--batch", + "--keyserver-options", + "http-proxy={0}".format(http_proxy_url), + "--keyserver", + keyserver, + "--logger-fd", + "1", + "--recv-keys", + key, + ] else: - cmd = ['apt-key', 'adv', '--batch', '--keyserver', keyserver, - '--logger-fd', '1', '--recv-keys', key] + cmd = [ + "apt-key", + "adv", + "--batch", + "--keyserver", + keyserver, + "--logger-fd", + "1", + "--recv-keys", + key, + ] ret = _call_apt(cmd, scope=False, **kwargs) - if ret['retcode'] != 0: + if ret["retcode"] != 0: raise CommandExecutionError( - 'Error: key retrieval failed: {0}'.format(ret['stdout']) + "Error: key retrieval failed: {0}".format(ret["stdout"]) ) - elif 'key_url' in kwargs: - key_url = kwargs['key_url'] - fn_ = __salt__['cp.cache_file'](key_url, saltenv) + elif "key_url" in kwargs: + key_url = kwargs["key_url"] + fn_ = __salt__["cp.cache_file"](key_url, saltenv) if not fn_: + raise CommandExecutionError("Error: file not found: {0}".format(key_url)) + cmd = ["apt-key", "add", fn_] + out = __salt__["cmd.run_stdout"](cmd, python_shell=False, **kwargs) + if not out.upper().startswith("OK"): raise CommandExecutionError( - 'Error: file not found: {0}'.format(key_url) - ) - cmd = ['apt-key', 'add', fn_] - out = __salt__['cmd.run_stdout'](cmd, python_shell=False, **kwargs) - if not out.upper().startswith('OK'): - raise CommandExecutionError( - 'Error: failed to add key from {0}'.format(key_url) + "Error: failed to add key from {0}".format(key_url) ) - elif 'key_text' in kwargs: - key_text = kwargs['key_text'] - cmd = ['apt-key', 'add', '-'] - out = __salt__['cmd.run_stdout'](cmd, stdin=key_text, - python_shell=False, **kwargs) - if not out.upper().startswith('OK'): + elif "key_text" in kwargs: + key_text = kwargs["key_text"] + cmd = ["apt-key", "add", "-"] + out = __salt__["cmd.run_stdout"]( + cmd, stdin=key_text, python_shell=False, **kwargs + ) + if not out.upper().startswith("OK"): raise CommandExecutionError( - 'Error: failed to add key:\n{0}'.format(key_text) + "Error: failed to add key:\n{0}".format(key_text) ) - if 'comps' in kwargs: - kwargs['comps'] = kwargs['comps'].split(',') - full_comp_list |= set(kwargs['comps']) + if "comps" in kwargs: + kwargs["comps"] = kwargs["comps"].split(",") + full_comp_list |= set(kwargs["comps"]) else: - kwargs['comps'] = list(full_comp_list) + kwargs["comps"] = list(full_comp_list) - if 'architectures' in kwargs: - kwargs['architectures'] = kwargs['architectures'].split(',') + if "architectures" in kwargs: + kwargs["architectures"] = kwargs["architectures"].split(",") else: - kwargs['architectures'] = repo_architectures + kwargs["architectures"] = repo_architectures - if 'disabled' in kwargs: - kwargs['disabled'] = salt.utils.data.is_true(kwargs['disabled']) - elif 'enabled' in kwargs: - kwargs['disabled'] = not salt.utils.data.is_true(kwargs['enabled']) + if "disabled" in kwargs: + kwargs["disabled"] = salt.utils.data.is_true(kwargs["disabled"]) + elif "enabled" in kwargs: + kwargs["disabled"] = not salt.utils.data.is_true(kwargs["enabled"]) - kw_type = kwargs.get('type') - kw_dist = kwargs.get('dist') + kw_type = kwargs.get("type") + kw_dist = kwargs.get("dist") for source in repos: # This series of checks will identify the starting source line # and the resulting source line. The idea here is to ensure # we are not returning bogus data because the source line # has already been modified on a previous run. - repo_matches = source.type == repo_type and source.uri == repo_uri and source.dist == repo_dist + repo_matches = ( + source.type == repo_type + and source.uri == repo_uri + and source.dist == repo_dist + ) kw_matches = source.dist == kw_dist and source.type == kw_type if repo_matches or kw_matches: for comp in full_comp_list: - if comp in getattr(source, 'comps', []): + if comp in getattr(source, "comps", []): mod_source = source if not source.comps: mod_source = source - if kwargs['architectures'] != source.architectures: + if kwargs["architectures"] != source.architectures: mod_source = source if mod_source: break - if 'comments' in kwargs: - kwargs['comments'] = \ - salt.utils.pkg.deb.combine_comments(kwargs['comments']) + if "comments" in kwargs: + kwargs["comments"] = salt.utils.pkg.deb.combine_comments(kwargs["comments"]) if not mod_source: mod_source = sourceslist.SourceEntry(repo) - if 'comments' in kwargs: - mod_source.comment = kwargs['comments'] + if "comments" in kwargs: + mod_source.comment = kwargs["comments"] sources.list.append(mod_source) - elif 'comments' in kwargs: - mod_source.comment = kwargs['comments'] + elif "comments" in kwargs: + mod_source.comment = kwargs["comments"] for key in kwargs: if key in _MODIFY_OK and hasattr(mod_source, key): @@ -2378,19 +2450,19 @@ def mod_repo(repo, saltenv='base', **kwargs): refresh_db() return { repo: { - 'architectures': getattr(mod_source, 'architectures', []), - 'comps': mod_source.comps, - 'disabled': mod_source.disabled, - 'file': mod_source.file, - 'type': mod_source.type, - 'uri': mod_source.uri, - 'line': mod_source.line + "architectures": getattr(mod_source, "architectures", []), + "comps": mod_source.comps, + "disabled": mod_source.disabled, + "file": mod_source.file, + "type": mod_source.type, + "uri": mod_source.uri, + "line": mod_source.line, } } def file_list(*packages): - ''' + """ List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -2402,12 +2474,12 @@ def file_list(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' - return __salt__['lowpkg.file_list'](*packages) + """ + return __salt__["lowpkg.file_list"](*packages) def file_dict(*packages): - ''' + """ List the files that belong to a package, grouped by package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -2419,74 +2491,75 @@ def file_dict(*packages): salt '*' pkg.file_dict httpd salt '*' pkg.file_dict httpd postfix salt '*' pkg.file_dict - ''' - return __salt__['lowpkg.file_dict'](*packages) + """ + return __salt__["lowpkg.file_dict"](*packages) def expand_repo_def(**kwargs): - ''' + """ Take a repository definition and expand it to the full pkg repository dict that can be used for comparison. This is a helper function to make the Debian/Ubuntu apt sources sane for comparison in the pkgrepo states. This is designed to be called from pkgrepo states and will have little use being called on the CLI. - ''' - if 'repo' not in kwargs: - raise SaltInvocationError('missing \'repo\' argument') + """ + if "repo" not in kwargs: + raise SaltInvocationError("missing 'repo' argument") _check_apt() sanitized = {} - repo = salt.utils.pkg.deb.strip_uri(kwargs['repo']) - if repo.startswith('ppa:') and __grains__['os'] in ('Ubuntu', 'Mint', 'neon'): - dist = __grains__['lsb_distrib_codename'] - owner_name, ppa_name = repo[4:].split('/', 1) - if 'ppa_auth' in kwargs: - auth_info = '{0}@'.format(kwargs['ppa_auth']) - repo = LP_PVT_SRC_FORMAT.format(auth_info, owner_name, ppa_name, - dist) + repo = salt.utils.pkg.deb.strip_uri(kwargs["repo"]) + if repo.startswith("ppa:") and __grains__["os"] in ("Ubuntu", "Mint", "neon"): + dist = __grains__["lsb_distrib_codename"] + owner_name, ppa_name = repo[4:].split("/", 1) + if "ppa_auth" in kwargs: + auth_info = "{0}@".format(kwargs["ppa_auth"]) + repo = LP_PVT_SRC_FORMAT.format(auth_info, owner_name, ppa_name, dist) else: if HAS_SOFTWAREPROPERTIES: - if hasattr(softwareproperties.ppa, 'PPAShortcutHandler'): - repo = softwareproperties.ppa.PPAShortcutHandler(repo).expand(dist)[0] + if hasattr(softwareproperties.ppa, "PPAShortcutHandler"): + repo = softwareproperties.ppa.PPAShortcutHandler(repo).expand(dist)[ + 0 + ] else: repo = softwareproperties.ppa.expand_ppa_line(repo, dist)[0] else: repo = LP_SRC_FORMAT.format(owner_name, ppa_name, dist) - if 'file' not in kwargs: - filename = '/etc/apt/sources.list.d/{0}-{1}-{2}.list' - kwargs['file'] = filename.format(owner_name, ppa_name, dist) + if "file" not in kwargs: + filename = "/etc/apt/sources.list.d/{0}-{1}-{2}.list" + kwargs["file"] = filename.format(owner_name, ppa_name, dist) source_entry = sourceslist.SourceEntry(repo) - for list_args in ('architectures', 'comps'): + for list_args in ("architectures", "comps"): if list_args in kwargs: - kwargs[list_args] = kwargs[list_args].split(',') + kwargs[list_args] = kwargs[list_args].split(",") for kwarg in _MODIFY_OK: if kwarg in kwargs: setattr(source_entry, kwarg, kwargs[kwarg]) - sanitized['file'] = source_entry.file - sanitized['comps'] = getattr(source_entry, 'comps', []) - sanitized['disabled'] = source_entry.disabled - sanitized['dist'] = source_entry.dist - sanitized['type'] = source_entry.type - sanitized['uri'] = source_entry.uri.rstrip('/') - sanitized['line'] = source_entry.line.strip() - sanitized['architectures'] = getattr(source_entry, 'architectures', []) + sanitized["file"] = source_entry.file + sanitized["comps"] = getattr(source_entry, "comps", []) + sanitized["disabled"] = source_entry.disabled + sanitized["dist"] = source_entry.dist + sanitized["type"] = source_entry.type + sanitized["uri"] = source_entry.uri.rstrip("/") + sanitized["line"] = source_entry.line.strip() + sanitized["architectures"] = getattr(source_entry, "architectures", []) return sanitized def _parse_selections(dpkgselection): - ''' + """ Parses the format from ``dpkg --get-selections`` and return a format that pkg.get_selections and pkg.set_selections work with. - ''' + """ ret = {} if isinstance(dpkgselection, six.string_types): - dpkgselection = dpkgselection.split('\n') + dpkgselection = dpkgselection.split("\n") for line in dpkgselection: if line: _pkg, _state = line.split() @@ -2498,7 +2571,7 @@ def _parse_selections(dpkgselection): def get_selections(pattern=None, state=None): - ''' + """ View package state from the dpkg database. Returns a dict of dicts containing the state, and package names: @@ -2521,13 +2594,13 @@ def get_selections(pattern=None, state=None): salt '*' pkg.get_selections 'python-*' salt '*' pkg.get_selections state=hold salt '*' pkg.get_selections 'openssh*' state=hold - ''' + """ ret = {} - cmd = ['dpkg', '--get-selections'] - cmd.append(pattern if pattern else '*') - stdout = __salt__['cmd.run_stdout'](cmd, - output_loglevel='trace', - python_shell=False) + cmd = ["dpkg", "--get-selections"] + cmd.append(pattern if pattern else "*") + stdout = __salt__["cmd.run_stdout"]( + cmd, output_loglevel="trace", python_shell=False + ) ret = _parse_selections(stdout) if state: return {state: ret.get(state, [])} @@ -2540,8 +2613,8 @@ def get_selections(pattern=None, state=None): # above, but override that if explicitly specified # TODO: handle path to selection file from local fs as well as from salt file # server -def set_selections(path=None, selection=None, clear=False, saltenv='base'): - ''' +def set_selections(path=None, selection=None, clear=False, saltenv="base"): + """ Change package state in the dpkg database. The state can be any one of, documented in ``dpkg(1)``: @@ -2579,46 +2652,49 @@ def set_selections(path=None, selection=None, clear=False, saltenv='base'): salt '*' pkg.set_selections selection='{"hold": ["openssh-server", "openssh-client"]}' salt '*' pkg.set_selections salt://path/to/file salt '*' pkg.set_selections salt://path/to/file clear=True - ''' + """ ret = {} if not path and not selection: return ret if path and selection: - err = ('The \'selection\' and \'path\' arguments to ' - 'pkg.set_selections are mutually exclusive, and cannot be ' - 'specified together') + err = ( + "The 'selection' and 'path' arguments to " + "pkg.set_selections are mutually exclusive, and cannot be " + "specified together" + ) raise SaltInvocationError(err) if isinstance(selection, six.string_types): try: selection = salt.utils.yaml.safe_load(selection) - except (salt.utils.yaml.parser.ParserError, salt.utils.yaml.scanner.ScannerError) as exc: - raise SaltInvocationError( - 'Improperly-formatted selection: {0}'.format(exc) - ) + except ( + salt.utils.yaml.parser.ParserError, + salt.utils.yaml.scanner.ScannerError, + ) as exc: + raise SaltInvocationError("Improperly-formatted selection: {0}".format(exc)) if path: - path = __salt__['cp.cache_file'](path, saltenv) - with salt.utils.files.fopen(path, 'r') as ifile: - content = [salt.utils.stringutils.to_unicode(x) - for x in ifile.readlines()] + path = __salt__["cp.cache_file"](path, saltenv) + with salt.utils.files.fopen(path, "r") as ifile: + content = [salt.utils.stringutils.to_unicode(x) for x in ifile.readlines()] selection = _parse_selections(content) if selection: - valid_states = ('install', 'hold', 'deinstall', 'purge') + valid_states = ("install", "hold", "deinstall", "purge") bad_states = [x for x in selection if x not in valid_states] if bad_states: raise SaltInvocationError( - 'Invalid state(s): {0}'.format(', '.join(bad_states)) + "Invalid state(s): {0}".format(", ".join(bad_states)) ) if clear: - cmd = ['dpkg', '--clear-selections'] - if not __opts__['test']: + cmd = ["dpkg", "--clear-selections"] + if not __opts__["test"]: result = _call_apt(cmd, scope=False) - if result['retcode'] != 0: - err = ('Running dpkg --clear-selections failed: ' - '{0}'.format(result['stderr'])) + if result["retcode"] != 0: + err = "Running dpkg --clear-selections failed: " "{0}".format( + result["stderr"] + ) log.error(err) raise CommandExecutionError(err) @@ -2630,30 +2706,26 @@ def set_selections(path=None, selection=None, clear=False, saltenv='base'): for _pkg in _pkgs: if _state == sel_revmap.get(_pkg): continue - cmd = ['dpkg', '--set-selections'] - cmd_in = '{0} {1}'.format(_pkg, _state) - if not __opts__['test']: + cmd = ["dpkg", "--set-selections"] + cmd_in = "{0} {1}".format(_pkg, _state) + if not __opts__["test"]: result = _call_apt(cmd, scope=False, stdin=cmd_in) - if result['retcode'] != 0: - log.error( - 'failed to set state %s for package %s', - _state, _pkg - ) + if result["retcode"] != 0: + log.error("failed to set state %s for package %s", _state, _pkg) else: - ret[_pkg] = {'old': sel_revmap.get(_pkg), - 'new': _state} + ret[_pkg] = {"old": sel_revmap.get(_pkg), "new": _state} return ret def _resolve_deps(name, pkgs, **kwargs): - ''' + """ Installs missing dependencies and marks them as auto installed so they are removed when no more manually installed packages depend on them. .. versionadded:: 2014.7.0 :depends: - python-apt module - ''' + """ missing_deps = [] for pkg_file in pkgs: deb = apt.debfile.DebPackage(filename=pkg_file, cache=apt.Cache()) @@ -2661,37 +2733,29 @@ def _resolve_deps(name, pkgs, **kwargs): missing_deps.extend(deb.missing_deps) if missing_deps: - cmd = ['apt-get', '-q', '-y'] - cmd = cmd + ['-o', 'DPkg::Options::=--force-confold'] - cmd = cmd + ['-o', 'DPkg::Options::=--force-confdef'] - cmd.append('install') + cmd = ["apt-get", "-q", "-y"] + cmd = cmd + ["-o", "DPkg::Options::=--force-confold"] + cmd = cmd + ["-o", "DPkg::Options::=--force-confdef"] + cmd.append("install") cmd.extend(missing_deps) - ret = __salt__['cmd.retcode']( - cmd, - env=kwargs.get('env'), - python_shell=False - ) + ret = __salt__["cmd.retcode"](cmd, env=kwargs.get("env"), python_shell=False) if ret != 0: raise CommandExecutionError( - 'Error: unable to resolve dependencies for: {0}'.format(name) + "Error: unable to resolve dependencies for: {0}".format(name) ) else: try: - cmd = ['apt-mark', 'auto'] + missing_deps - __salt__['cmd.run']( - cmd, - env=kwargs.get('env'), - python_shell=False - ) + cmd = ["apt-mark", "auto"] + missing_deps + __salt__["cmd.run"](cmd, env=kwargs.get("env"), python_shell=False) except MinionError as exc: raise CommandExecutionError(exc) return def owner(*paths): - ''' + """ .. versionadded:: 2014.7.0 Return the name of the package that owns the file. Multiple file paths can @@ -2708,25 +2772,25 @@ def owner(*paths): salt '*' pkg.owner /usr/bin/apachectl salt '*' pkg.owner /usr/bin/apachectl /usr/bin/basename - ''' + """ if not paths: - return '' + return "" ret = {} for path in paths: - cmd = ['dpkg', '-S', path] - output = __salt__['cmd.run_stdout'](cmd, - output_loglevel='trace', - python_shell=False) - ret[path] = output.split(':')[0] - if 'no path found' in ret[path].lower(): - ret[path] = '' + cmd = ["dpkg", "-S", path] + output = __salt__["cmd.run_stdout"]( + cmd, output_loglevel="trace", python_shell=False + ) + ret[path] = output.split(":")[0] + if "no path found" in ret[path].lower(): + ret[path] = "" if len(ret) == 1: return next(six.itervalues(ret)) return ret def show(*names, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Runs an ``apt-cache show`` on the passed package names, and returns the @@ -2752,14 +2816,14 @@ def show(*names, **kwargs): salt myminion pkg.show gawk salt myminion pkg.show 'nginx-*' salt myminion pkg.show 'nginx-*' filter=description,provides - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - refresh = kwargs.pop('refresh', False) + refresh = kwargs.pop("refresh", False) filter_ = salt.utils.args.split_input( - kwargs.pop('filter', []), + kwargs.pop("filter", []), lambda x: six.text_type(x) - if not isinstance(x, six.string_types) - else x.lower() + if not isinstance(x, six.string_types) + else x.lower(), ) if kwargs: salt.utils.args.invalid_kwargs(kwargs) @@ -2770,26 +2834,25 @@ def show(*names, **kwargs): if not names: return {} - result = _call_apt(['apt-cache', 'show'] + list(names), scope=False) + result = _call_apt(["apt-cache", "show"] + list(names), scope=False) def _add(ret, pkginfo): - name = pkginfo.pop('Package', None) - version = pkginfo.pop('Version', None) + name = pkginfo.pop("Package", None) + version = pkginfo.pop("Version", None) if name is not None and version is not None: ret.setdefault(name, {}).setdefault(version, {}).update(pkginfo) def _check_filter(key): key = key.lower() - return True if key in ('package', 'version') or not filter_ \ - else key in filter_ + return True if key in ("package", "version") or not filter_ else key in filter_ ret = {} pkginfo = {} - for line in salt.utils.itertools.split(result['stdout'], '\n'): + for line in salt.utils.itertools.split(result["stdout"], "\n"): line = line.strip() if line: try: - key, val = [x.strip() for x in line.split(':', 1)] + key, val = [x.strip() for x in line.split(":", 1)] except ValueError: pass else: @@ -2810,7 +2873,7 @@ def show(*names, **kwargs): def info_installed(*names, **kwargs): - ''' + """ Return the information of the named package(s) installed on the system. .. versionadded:: 2015.8.1 @@ -2831,31 +2894,31 @@ def info_installed(*names, **kwargs): salt '*' pkg.info_installed salt '*' pkg.info_installed ... salt '*' pkg.info_installed failhard=false - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - failhard = kwargs.pop('failhard', True) + failhard = kwargs.pop("failhard", True) if kwargs: salt.utils.args.invalid_kwargs(kwargs) ret = dict() - for pkg_name, pkg_nfo in __salt__['lowpkg.info'](*names, failhard=failhard).items(): + for pkg_name, pkg_nfo in __salt__["lowpkg.info"](*names, failhard=failhard).items(): t_nfo = dict() - if pkg_nfo.get('status', 'ii')[1] != 'i': - continue # return only packages that are really installed + if pkg_nfo.get("status", "ii")[1] != "i": + continue # return only packages that are really installed # Translate dpkg-specific keys to a common structure for key, value in pkg_nfo.items(): - if key == 'package': - t_nfo['name'] = value - elif key == 'origin': - t_nfo['vendor'] = value - elif key == 'section': - t_nfo['group'] = value - elif key == 'maintainer': - t_nfo['packager'] = value - elif key == 'homepage': - t_nfo['url'] = value - elif key == 'status': - continue # only installed pkgs are returned, no need for status + if key == "package": + t_nfo["name"] = value + elif key == "origin": + t_nfo["vendor"] = value + elif key == "section": + t_nfo["group"] = value + elif key == "maintainer": + t_nfo["packager"] = value + elif key == "homepage": + t_nfo["url"] = value + elif key == "status": + continue # only installed pkgs are returned, no need for status else: t_nfo[key] = value @@ -2865,38 +2928,32 @@ def info_installed(*names, **kwargs): def _get_http_proxy_url(): - ''' + """ Returns the http_proxy_url if proxy_username, proxy_password, proxy_host, and proxy_port config values are set. Returns a string. - ''' - http_proxy_url = '' - host = __salt__['config.option']('proxy_host') - port = __salt__['config.option']('proxy_port') - username = __salt__['config.option']('proxy_username') - password = __salt__['config.option']('proxy_password') + """ + http_proxy_url = "" + host = __salt__["config.option"]("proxy_host") + port = __salt__["config.option"]("proxy_port") + username = __salt__["config.option"]("proxy_username") + password = __salt__["config.option"]("proxy_password") # Set http_proxy_url for use in various internet facing actions...eg apt-key adv if host and port: if username and password: - http_proxy_url = 'http://{0}:{1}@{2}:{3}'.format( - username, - password, - host, - port + http_proxy_url = "http://{0}:{1}@{2}:{3}".format( + username, password, host, port ) else: - http_proxy_url = 'http://{0}:{1}'.format( - host, - port - ) + http_proxy_url = "http://{0}:{1}".format(host, port) return http_proxy_url def list_downloaded(root=None, **kwargs): - ''' + """ .. versionadded:: 3000? List prefetched packages downloaded by apt in the local disk. @@ -2909,21 +2966,23 @@ def list_downloaded(root=None, **kwargs): .. code-block:: bash salt '*' pkg.list_downloaded - ''' - CACHE_DIR = '/var/cache/apt' + """ + CACHE_DIR = "/var/cache/apt" if root: CACHE_DIR = os.path.join(root, os.path.relpath(CACHE_DIR, os.path.sep)) ret = {} for root, dirnames, filenames in salt.utils.path.os_walk(CACHE_DIR): - for filename in fnmatch.filter(filenames, '*.deb'): + for filename in fnmatch.filter(filenames, "*.deb"): package_path = os.path.join(root, filename) - pkg_info = __salt__['lowpkg.bin_pkg_info'](package_path) + pkg_info = __salt__["lowpkg.bin_pkg_info"](package_path) pkg_timestamp = int(os.path.getctime(package_path)) - ret.setdefault(pkg_info['name'], {})[pkg_info['version']] = { - 'path': package_path, - 'size': os.path.getsize(package_path), - 'creation_date_time_t': pkg_timestamp, - 'creation_date_time': datetime.datetime.utcfromtimestamp(pkg_timestamp).isoformat(), + ret.setdefault(pkg_info["name"], {})[pkg_info["version"]] = { + "path": package_path, + "size": os.path.getsize(package_path), + "creation_date_time_t": pkg_timestamp, + "creation_date_time": datetime.datetime.utcfromtimestamp( + pkg_timestamp + ).isoformat(), } return ret diff --git a/salt/modules/archive.py b/salt/modules/archive.py index 483aadb6ac0..86a112c109a 100644 --- a/salt/modules/archive.py +++ b/salt/modules/archive.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" A module to wrap (non-Windows) archive calls .. versionadded:: 2014.1.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import contextlib # For < 2.7 compat import copy import errno @@ -17,22 +18,7 @@ import stat import subprocess import tarfile import zipfile -try: - from shlex import quote as _quote # pylint: disable=E0611 -except ImportError: - from pipes import quote as _quote -# Import third party libs -from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=no-name-in-module -try: - import rarfile - HAS_RARFILE = True -except ImportError: - HAS_RARFILE = False - -# Import salt libs -from salt.exceptions import SaltInvocationError, CommandExecutionError import salt.utils.decorators import salt.utils.decorators.path import salt.utils.files @@ -41,29 +27,49 @@ import salt.utils.platform import salt.utils.stringutils import salt.utils.templates +# Import salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError + +# Import third party libs +from salt.ext import six +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse + +try: + from shlex import quote as _quote # pylint: disable=E0611 +except ImportError: + from pipes import quote as _quote + + +try: + import rarfile + + HAS_RARFILE = True +except ImportError: + HAS_RARFILE = False + + if salt.utils.platform.is_windows(): import win32file # TODO: Check that the passed arguments are correct # Don't shadow built-in's. -__func_alias__ = { - 'zip_': 'zip', - 'list_': 'list' -} +__func_alias__ = {"zip_": "zip", "list_": "list"} log = logging.getLogger(__name__) -def list_(name, - archive_format=None, - options=None, - strip_components=None, - clean=False, - verbose=False, - saltenv='base', - source_hash=None): - ''' +def list_( + name, + archive_format=None, + options=None, + strip_components=None, + clean=False, + verbose=False, + saltenv="base", + source_hash=None, +): + """ .. versionadded:: 2016.11.0 .. versionchanged:: 2016.11.2 The rarfile_ Python module is now supported for listing the contents of @@ -172,25 +178,28 @@ def list_(name, salt '*' archive.list https://domain.tld/myfile.zip salt '*' archive.list https://domain.tld/myfile.zip source_hash=f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 salt '*' archive.list ftp://10.1.2.3/foo.rar - ''' + """ + def _list_tar(name, cached, decompress_cmd, failhard=False): - ''' + """ List the contents of a tar archive. - ''' + """ dirs = [] files = [] links = [] try: - open_kwargs = {'name': cached} \ - if not isinstance(cached, subprocess.Popen) \ - else {'fileobj': cached.stdout, 'mode': 'r|'} + open_kwargs = ( + {"name": cached} + if not isinstance(cached, subprocess.Popen) + else {"fileobj": cached.stdout, "mode": "r|"} + ) with contextlib.closing(tarfile.open(**open_kwargs)) as tar_archive: for member in tar_archive.getmembers(): _member = salt.utils.data.decode(member.name) if member.issym(): links.append(_member) elif member.isdir(): - dirs.append(_member + '/') + dirs.append(_member + "/") else: files.append(_member) return dirs, files, links @@ -201,49 +210,55 @@ def list_(name, stderr = cached.communicate()[1] if cached.returncode != 0: raise CommandExecutionError( - 'Failed to decompress {0}'.format(name), - info={'error': stderr} + "Failed to decompress {0}".format(name), + info={"error": stderr}, ) else: - if not salt.utils.path.which('tar'): - raise CommandExecutionError('\'tar\' command not available') + if not salt.utils.path.which("tar"): + raise CommandExecutionError("'tar' command not available") if decompress_cmd is not None: # Guard against shell injection try: - decompress_cmd = ' '.join( + decompress_cmd = " ".join( [_quote(x) for x in shlex.split(decompress_cmd)] ) except AttributeError: - raise CommandExecutionError('Invalid CLI options') + raise CommandExecutionError("Invalid CLI options") else: - if salt.utils.path.which('xz') \ - and __salt__['cmd.retcode'](['xz', '-t', cached], - python_shell=False, - ignore_retcode=True) == 0: - decompress_cmd = 'xz --decompress --stdout' + if ( + salt.utils.path.which("xz") + and __salt__["cmd.retcode"]( + ["xz", "-t", cached], + python_shell=False, + ignore_retcode=True, + ) + == 0 + ): + decompress_cmd = "xz --decompress --stdout" if decompress_cmd: decompressed = subprocess.Popen( - '{0} {1}'.format(decompress_cmd, _quote(cached)), + "{0} {1}".format(decompress_cmd, _quote(cached)), shell=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stderr=subprocess.PIPE, + ) return _list_tar(name, decompressed, None, True) raise CommandExecutionError( - 'Unable to list contents of {0}. If this is an XZ-compressed tar ' - 'archive, install XZ Utils to enable listing its contents. If it ' - 'is compressed using something other than XZ, it may be necessary ' - 'to specify CLI options to decompress the archive. See the ' - 'documentation for details.'.format(name) + "Unable to list contents of {0}. If this is an XZ-compressed tar " + "archive, install XZ Utils to enable listing its contents. If it " + "is compressed using something other than XZ, it may be necessary " + "to specify CLI options to decompress the archive. See the " + "documentation for details.".format(name) ) def _list_zip(name, cached): - ''' + """ List the contents of a zip archive. Password-protected ZIP archives can still be listed by zipfile, so there is no reason to invoke the unzip command. - ''' + """ dirs = set() files = [] links = [] @@ -252,7 +267,7 @@ def list_(name, for member in zip_archive.infolist(): path = member.filename if salt.utils.platform.is_windows(): - if path.endswith('/'): + if path.endswith("/"): # zipfile.ZipInfo objects on windows use forward # slash at end of the directory name. dirs.add(path) @@ -272,58 +287,57 @@ def list_(name, # ZIP files created on Windows do not add entries # to the archive for directories. So, we'll need to # manually add them. - dirname = ''.join(path.rpartition('/')[:2]) + dirname = "".join(path.rpartition("/")[:2]) if dirname: dirs.add(dirname) if dirname in files: files.remove(dirname) return list(dirs), files, links except zipfile.BadZipfile: - raise CommandExecutionError('{0} is not a ZIP file'.format(name)) + raise CommandExecutionError("{0} is not a ZIP file".format(name)) def _list_rar(name, cached): - ''' + """ List the contents of a rar archive. - ''' + """ dirs = [] files = [] if HAS_RARFILE: with rarfile.RarFile(cached) as rf: for member in rf.infolist(): - path = member.filename.replace('\\', '/') + path = member.filename.replace("\\", "/") if member.isdir(): - dirs.append(path + '/') + dirs.append(path + "/") else: files.append(path) else: - if not salt.utils.path.which('rar'): + if not salt.utils.path.which("rar"): raise CommandExecutionError( - 'rar command not available, is it installed?' + "rar command not available, is it installed?" ) - output = __salt__['cmd.run']( - ['rar', 'lt', name], - python_shell=False, - ignore_retcode=False) - matches = re.findall(r'Name:\s*([^\n]+)\s*Type:\s*([^\n]+)', output) + output = __salt__["cmd.run"]( + ["rar", "lt", name], python_shell=False, ignore_retcode=False + ) + matches = re.findall(r"Name:\s*([^\n]+)\s*Type:\s*([^\n]+)", output) for path, type_ in matches: - if type_ == 'Directory': - dirs.append(path + '/') + if type_ == "Directory": + dirs.append(path + "/") else: files.append(path) if not dirs and not files: raise CommandExecutionError( - 'Failed to list {0}, is it a rar file? If so, the ' - 'installed version of rar may be too old to list data in ' - 'a parsable format. Installing the rarfile Python module ' - 'may be an easier workaround if newer rar is not readily ' - 'available.'.format(name), - info={'error': output} + "Failed to list {0}, is it a rar file? If so, the " + "installed version of rar may be too old to list data in " + "a parsable format. Installing the rarfile Python module " + "may be an easier workaround if newer rar is not readily " + "available.".format(name), + info={"error": output}, ) return dirs, files, [] - cached = __salt__['cp.cache_file'](name, saltenv, source_hash=source_hash) + cached = __salt__["cp.cache_file"](name, saltenv, source_hash=source_hash) if not cached: - raise CommandExecutionError('Failed to cache {0}'.format(name)) + raise CommandExecutionError("Failed to cache {0}".format(name)) try: if strip_components: @@ -334,23 +348,23 @@ def list_(name, if strip_components <= 0: raise CommandExecutionError( - '\'strip_components\' must be a positive integer' + "'strip_components' must be a positive integer" ) parsed = _urlparse(name) path = parsed.path or parsed.netloc def _unsupported_format(archive_format): - ''' + """ Raise the proper exception message for the given archive format. - ''' + """ if archive_format is None: raise CommandExecutionError( - 'Unable to guess archive format, please pass an ' - '\'archive_format\' argument.' + "Unable to guess archive format, please pass an " + "'archive_format' argument." ) raise CommandExecutionError( - 'Unsupported archive format \'{0}\''.format(archive_format) + "Unsupported archive format '{0}'".format(archive_format) ) if not archive_format: @@ -359,36 +373,34 @@ def list_(name, _unsupported_format(archive_format) archive_format = guessed_format - func = locals().get('_list_' + archive_format) - if not hasattr(func, '__call__'): + func = locals().get("_list_" + archive_format) + if not hasattr(func, "__call__"): _unsupported_format(archive_format) - args = (options,) if archive_format == 'tar' else () + args = (options,) if archive_format == "tar" else () try: dirs, files, links = func(name, cached, *args) except (IOError, OSError) as exc: raise CommandExecutionError( - 'Failed to list contents of {0}: {1}'.format( - name, exc.__str__() - ) + "Failed to list contents of {0}: {1}".format(name, exc.__str__()) ) except CommandExecutionError as exc: raise except Exception as exc: # pylint: disable=broad-except raise CommandExecutionError( - 'Uncaught exception \'{0}\' when listing contents of {1}' - .format(exc, name) + "Uncaught exception '{0}' when listing contents of {1}".format( + exc, name + ) ) if clean: try: os.remove(cached) - log.debug('Cleaned cached archive %s', cached) + log.debug("Cleaned cached archive %s", cached) except OSError as exc: if exc.errno != errno.ENOENT: log.warning( - 'Failed to clean cached archive %s: %s', - cached, exc.__str__() + "Failed to clean cached archive %s: %s", cached, exc.__str__() ) if strip_components: @@ -398,28 +410,28 @@ def list_(name, # Strip off the specified number of directory # boundaries, and grab what comes after the last # stripped path separator. - item[index] = item[index].split( - os.sep, strip_components)[strip_components] + item[index] = item[index].split(os.sep, strip_components)[ + strip_components + ] except IndexError: # Path is excluded by strip_components because it is not # deep enough. Set this to an empty string so it can # be removed in the generator expression below. - item[index] = '' + item[index] = "" # Remove all paths which were excluded item[:] = (x for x in item if x) item.sort() if verbose: - ret = {'dirs': sorted(salt.utils.data.decode_list(dirs)), - 'files': sorted(salt.utils.data.decode_list(files)), - 'links': sorted(salt.utils.data.decode_list(links))} - ret['top_level_dirs'] = [x for x in ret['dirs'] - if x.count('/') == 1] - ret['top_level_files'] = [x for x in ret['files'] - if x.count('/') == 0] - ret['top_level_links'] = [x for x in ret['links'] - if x.count('/') == 0] + ret = { + "dirs": sorted(salt.utils.data.decode_list(dirs)), + "files": sorted(salt.utils.data.decode_list(files)), + "links": sorted(salt.utils.data.decode_list(links)), + } + ret["top_level_dirs"] = [x for x in ret["dirs"] if x.count("/") == 1] + ret["top_level_files"] = [x for x in ret["files"] if x.count("/") == 0] + ret["top_level_links"] = [x for x in ret["links"] if x.count("/") == 0] else: ret = sorted(dirs + files + links) return ret @@ -428,19 +440,19 @@ def list_(name, # Reraise with cache path in the error so that the user can examine the # cached archive for troubleshooting purposes. info = exc.info or {} - info['archive location'] = cached + info["archive location"] = cached raise CommandExecutionError(exc.error, info=info) -_glob_wildcards = re.compile('[*?[]') +_glob_wildcards = re.compile("[*?[]") def _glob(pathname): - ''' + """ In case ``pathname`` contains glob wildcards, performs expansion and returns the possibly empty list of matching pathnames. Otherwise returns a list that contains only ``pathname`` itself. - ''' + """ if _glob_wildcards.search(pathname) is None: return [pathname] else: @@ -448,24 +460,21 @@ def _glob(pathname): def _expand_sources(sources): - ''' + """ Expands a user-provided specification of source files into a list of paths. - ''' + """ if sources is None: return [] if isinstance(sources, six.string_types): - sources = [x.strip() for x in sources.split(',')] + sources = [x.strip() for x in sources.split(",")] elif isinstance(sources, (float, six.integer_types)): sources = [six.text_type(sources)] - return [path - for source in sources - for path in _glob(source)] + return [path for source in sources for path in _glob(source)] -@salt.utils.decorators.path.which('tar') -def tar(options, tarfile, sources=None, dest=None, - cwd=None, template=None, runas=None): - ''' +@salt.utils.decorators.path.which("tar") +def tar(options, tarfile, sources=None, dest=None, cwd=None, template=None, runas=None): + """ .. note:: This function has changed for version 0.17.0. In prior versions, the @@ -527,32 +536,30 @@ def tar(options, tarfile, sources=None, dest=None, salt '*' archive.tar cjvf /tmp/tarfile.tar.bz2 '/tmp/file_*' # Unpack a tarfile salt '*' archive.tar xf foo.tar dest=/target/directory - ''' + """ if not options: # Catch instances were people pass an empty string for the "options" # argument. Someone would have to be really silly to do this, but we # should at least let them know of their silliness. - raise SaltInvocationError('Tar options can not be empty') + raise SaltInvocationError("Tar options can not be empty") - cmd = ['tar'] + cmd = ["tar"] if options: cmd.extend(options.split()) - cmd.extend(['{0}'.format(tarfile)]) + cmd.extend(["{0}".format(tarfile)]) cmd.extend(_expand_sources(sources)) if dest: - cmd.extend(['-C', '{0}'.format(dest)]) + cmd.extend(["-C", "{0}".format(dest)]) - return __salt__['cmd.run'](cmd, - cwd=cwd, - template=template, - runas=runas, - python_shell=False).splitlines() + return __salt__["cmd.run"]( + cmd, cwd=cwd, template=template, runas=runas, python_shell=False + ).splitlines() -@salt.utils.decorators.path.which('gzip') +@salt.utils.decorators.path.which("gzip") def gzip(sourcefile, template=None, runas=None, options=None): - ''' + """ Uses the gzip command to create gzip files template : None @@ -578,21 +585,20 @@ def gzip(sourcefile, template=None, runas=None, options=None): # Create /tmp/sourcefile.txt.gz salt '*' archive.gzip /tmp/sourcefile.txt salt '*' archive.gzip /tmp/sourcefile.txt options='-9 --verbose' - ''' - cmd = ['gzip'] + """ + cmd = ["gzip"] if options: cmd.append(options) - cmd.append('{0}'.format(sourcefile)) + cmd.append("{0}".format(sourcefile)) - return __salt__['cmd.run'](cmd, - template=template, - runas=runas, - python_shell=False).splitlines() + return __salt__["cmd.run"]( + cmd, template=template, runas=runas, python_shell=False + ).splitlines() -@salt.utils.decorators.path.which('gunzip') +@salt.utils.decorators.path.which("gunzip") def gunzip(gzipfile, template=None, runas=None, options=None): - ''' + """ Uses the gunzip command to unpack gzip files template : None @@ -618,21 +624,20 @@ def gunzip(gzipfile, template=None, runas=None, options=None): # Create /tmp/sourcefile.txt salt '*' archive.gunzip /tmp/sourcefile.txt.gz salt '*' archive.gunzip /tmp/sourcefile.txt options='--verbose' - ''' - cmd = ['gunzip'] + """ + cmd = ["gunzip"] if options: cmd.append(options) - cmd.append('{0}'.format(gzipfile)) + cmd.append("{0}".format(gzipfile)) - return __salt__['cmd.run'](cmd, - template=template, - runas=runas, - python_shell=False).splitlines() + return __salt__["cmd.run"]( + cmd, template=template, runas=runas, python_shell=False + ).splitlines() -@salt.utils.decorators.path.which('zip') +@salt.utils.decorators.path.which("zip") def cmd_zip(zip_file, sources, template=None, cwd=None, runas=None): - ''' + """ .. versionadded:: 2015.5.0 In versions 2014.7.x and earlier, this function was known as ``archive.zip``. @@ -688,20 +693,18 @@ def cmd_zip(zip_file, sources, template=None, cwd=None, runas=None): salt '*' archive.cmd_zip /tmp/zipfile.zip /tmp/sourcefile1,/tmp/sourcefile2 # Globbing for sources (2017.7.0 and later) salt '*' archive.cmd_zip /tmp/zipfile.zip '/tmp/sourcefile*' - ''' - cmd = ['zip', '-r'] - cmd.append('{0}'.format(zip_file)) + """ + cmd = ["zip", "-r"] + cmd.append("{0}".format(zip_file)) cmd.extend(_expand_sources(sources)) - return __salt__['cmd.run'](cmd, - cwd=cwd, - template=template, - runas=runas, - python_shell=False).splitlines() + return __salt__["cmd.run"]( + cmd, cwd=cwd, template=template, runas=runas, python_shell=False + ).splitlines() -@salt.utils.decorators.depends('zipfile', fallback_function=cmd_zip) +@salt.utils.decorators.depends("zipfile", fallback_function=cmd_zip) def zip_(zip_file, sources, template=None, cwd=None, runas=None, zip64=False): - ''' + """ Uses the ``zipfile`` Python module to create zip files .. versionchanged:: 2015.5.0 @@ -761,15 +764,13 @@ def zip_(zip_file, sources, template=None, cwd=None, runas=None, zip64=False): salt '*' archive.zip /tmp/zipfile.zip /tmp/sourcefile1,/tmp/sourcefile2 # Globbing for sources (2017.7.0 and later) salt '*' archive.zip /tmp/zipfile.zip '/tmp/sourcefile*' - ''' + """ if runas: euid = os.geteuid() egid = os.getegid() - uinfo = __salt__['user.info'](runas) + uinfo = __salt__["user.info"](runas) if not uinfo: - raise SaltInvocationError( - 'User \'{0}\' does not exist'.format(runas) - ) + raise SaltInvocationError("User '{0}' does not exist".format(runas)) zip_file, sources = _render_filenames(zip_file, sources, None, template) sources = _expand_sources(sources) @@ -777,35 +778,35 @@ def zip_(zip_file, sources, template=None, cwd=None, runas=None, zip64=False): if not cwd: for src in sources: if not os.path.isabs(src): - raise SaltInvocationError( - 'Relative paths require the \'cwd\' parameter' - ) + raise SaltInvocationError("Relative paths require the 'cwd' parameter") else: - err_msg = 'cwd must be absolute' + err_msg = "cwd must be absolute" try: if not os.path.isabs(cwd): raise SaltInvocationError(err_msg) except AttributeError: raise SaltInvocationError(err_msg) - if runas and (euid != uinfo['uid'] or egid != uinfo['gid']): + if runas and (euid != uinfo["uid"] or egid != uinfo["gid"]): # Change the egid first, as changing it after the euid will fail # if the runas user is non-privileged. - os.setegid(uinfo['gid']) - os.seteuid(uinfo['uid']) + os.setegid(uinfo["gid"]) + os.seteuid(uinfo["uid"]) try: exc = None archived_files = [] - with contextlib.closing(zipfile.ZipFile(zip_file, 'w', zipfile.ZIP_DEFLATED, zip64)) as zfile: + with contextlib.closing( + zipfile.ZipFile(zip_file, "w", zipfile.ZIP_DEFLATED, zip64) + ) as zfile: for src in sources: if cwd: src = os.path.join(cwd, src) if os.path.exists(src): if os.path.isabs(src): - rel_root = '/' + rel_root = "/" else: - rel_root = cwd if cwd is not None else '/' + rel_root = cwd if cwd is not None else "/" if os.path.isdir(src): for dir_name, sub_dirs, files in salt.utils.path.os_walk(src): if cwd and dir_name.startswith(cwd): @@ -813,7 +814,7 @@ def zip_(zip_file, sources, template=None, cwd=None, runas=None, zip64=False): else: arc_dir = os.path.relpath(dir_name, rel_root) if arc_dir: - archived_files.append(arc_dir + '/') + archived_files.append(arc_dir + "/") zfile.write(dir_name, arc_dir) for filename in files: abs_name = os.path.join(dir_name, filename) @@ -839,27 +840,29 @@ def zip_(zip_file, sources, template=None, cwd=None, runas=None, zip64=False): # permission errors in writing to minion log. if exc == zipfile.LargeZipFile: raise CommandExecutionError( - 'Resulting zip file too large, would require ZIP64 support' - 'which has not been enabled. Rerun command with zip64=True' + "Resulting zip file too large, would require ZIP64 support" + "which has not been enabled. Rerun command with zip64=True" ) else: raise CommandExecutionError( - 'Exception encountered creating zipfile: {0}'.format(exc) + "Exception encountered creating zipfile: {0}".format(exc) ) return archived_files -@salt.utils.decorators.path.which('unzip') -def cmd_unzip(zip_file, - dest, - excludes=None, - options=None, - template=None, - runas=None, - trim_output=False, - password=None): - ''' +@salt.utils.decorators.path.which("unzip") +def cmd_unzip( + zip_file, + dest, + excludes=None, + options=None, + template=None, + runas=None, + trim_output=False, + password=None, +): + """ .. versionadded:: 2015.5.0 In versions 2014.7.x and earlier, this function was known as ``archive.unzip``. @@ -926,47 +929,50 @@ def cmd_unzip(zip_file, .. code-block:: bash salt '*' archive.cmd_unzip /tmp/zipfile.zip /home/strongbad/ excludes=file_1,file_2 - ''' + """ if isinstance(excludes, six.string_types): - excludes = [x.strip() for x in excludes.split(',')] + excludes = [x.strip() for x in excludes.split(",")] elif isinstance(excludes, (float, six.integer_types)): excludes = [six.text_type(excludes)] - cmd = ['unzip'] + cmd = ["unzip"] if password: - cmd.extend(['-P', password]) + cmd.extend(["-P", password]) if options: cmd.extend(shlex.split(options)) - cmd.extend(['{0}'.format(zip_file), '-d', '{0}'.format(dest)]) + cmd.extend(["{0}".format(zip_file), "-d", "{0}".format(dest)]) if excludes is not None: - cmd.append('-x') + cmd.append("-x") cmd.extend(excludes) - result = __salt__['cmd.run_all']( + result = __salt__["cmd.run_all"]( cmd, template=template, runas=runas, python_shell=False, redirect_stderr=True, - output_loglevel='quiet' if password else 'debug') + output_loglevel="quiet" if password else "debug", + ) - if result['retcode'] != 0: - raise CommandExecutionError(result['stdout']) + if result["retcode"] != 0: + raise CommandExecutionError(result["stdout"]) - return _trim_files(result['stdout'].splitlines(), trim_output) + return _trim_files(result["stdout"].splitlines(), trim_output) -def unzip(zip_file, - dest, - excludes=None, - options=None, - template=None, - runas=None, - trim_output=False, - password=None, - extract_perms=True): - ''' +def unzip( + zip_file, + dest, + excludes=None, + options=None, + template=None, + runas=None, + trim_output=False, + password=None, + extract_perms=True, +): + """ Uses the ``zipfile`` Python module to unpack zip files .. versionchanged:: 2015.5.0 @@ -1044,25 +1050,23 @@ def unzip(zip_file, .. code-block:: bash salt '*' archive.unzip /tmp/zipfile.zip /home/strongbad/ password='BadPassword' - ''' + """ if not excludes: excludes = [] if runas: euid = os.geteuid() egid = os.getegid() - uinfo = __salt__['user.info'](runas) + uinfo = __salt__["user.info"](runas) if not uinfo: - raise SaltInvocationError( - "User '{0}' does not exist".format(runas) - ) + raise SaltInvocationError("User '{0}' does not exist".format(runas)) zip_file, dest = _render_filenames(zip_file, dest, None, template) - if runas and (euid != uinfo['uid'] or egid != uinfo['gid']): + if runas and (euid != uinfo["uid"] or egid != uinfo["gid"]): # Change the egid first, as changing it after the euid will fail # if the runas user is non-privileged. - os.setegid(uinfo['gid']) - os.seteuid(uinfo['uid']) + os.setegid(uinfo["gid"]) + os.seteuid(uinfo["uid"]) try: # Define cleaned_files here so that an exception will not prevent this @@ -1073,7 +1077,7 @@ def unzip(zip_file, files = zfile.namelist() if isinstance(excludes, six.string_types): - excludes = [x.strip() for x in excludes.split(',')] + excludes = [x.strip() for x in excludes.split(",")] elif isinstance(excludes, (float, six.integer_types)): excludes = [six.text_type(excludes)] @@ -1093,14 +1097,16 @@ def unzip(zip_file, perm = zfile.getinfo(target).external_attr >> 16 if perm == 0: umask_ = salt.utils.files.get_umask() - if target.endswith('/'): + if target.endswith("/"): perm = 0o777 & ~umask_ else: perm = 0o666 & ~umask_ os.chmod(os.path.join(dest, target), perm) else: win32_attr = zfile.getinfo(target).external_attr & 0xFF - win32file.SetFileAttributes(os.path.join(dest, target), win32_attr) + win32file.SetFileAttributes( + os.path.join(dest, target), win32_attr + ) except Exception as exc: # pylint: disable=broad-except if runas: os.seteuid(euid) @@ -1108,7 +1114,7 @@ def unzip(zip_file, # Wait to raise the exception until euid/egid are restored to avoid # permission errors in writing to minion log. raise CommandExecutionError( - 'Exception encountered unpacking zipfile: {0}'.format(exc) + "Exception encountered unpacking zipfile: {0}".format(exc) ) finally: # Restore the euid/egid @@ -1119,8 +1125,8 @@ def unzip(zip_file, return _trim_files(cleaned_files, trim_output) -def is_encrypted(name, clean=False, saltenv='base', source_hash=None): - ''' +def is_encrypted(name, clean=False, saltenv="base", source_hash=None): + """ .. versionadded:: 2016.11.0 Returns ``True`` if the zip archive is password-protected, ``False`` if @@ -1161,12 +1167,12 @@ def is_encrypted(name, clean=False, saltenv='base', source_hash=None): salt '*' archive.is_encrypted https://domain.tld/myfile.zip clean=True salt '*' archive.is_encrypted https://domain.tld/myfile.zip source_hash=f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 salt '*' archive.is_encrypted ftp://10.1.2.3/foo.zip - ''' - cached = __salt__['cp.cache_file'](name, saltenv, source_hash=source_hash) + """ + cached = __salt__["cp.cache_file"](name, saltenv, source_hash=source_hash) if not cached: - raise CommandExecutionError('Failed to cache {0}'.format(name)) + raise CommandExecutionError("Failed to cache {0}".format(name)) - archive_info = {'archive location': cached} + archive_info = {"archive location": cached} try: with contextlib.closing(zipfile.ZipFile(cached)) as zip_archive: zip_archive.testzip() @@ -1174,8 +1180,7 @@ def is_encrypted(name, clean=False, saltenv='base', source_hash=None): ret = True except zipfile.BadZipfile: raise CommandExecutionError( - '{0} is not a ZIP file'.format(name), - info=archive_info + "{0} is not a ZIP file".format(name), info=archive_info ) except Exception as exc: # pylint: disable=broad-except raise CommandExecutionError(exc.__str__(), info=archive_info) @@ -1185,19 +1190,18 @@ def is_encrypted(name, clean=False, saltenv='base', source_hash=None): if clean: try: os.remove(cached) - log.debug('Cleaned cached archive %s', cached) + log.debug("Cleaned cached archive %s", cached) except OSError as exc: if exc.errno != errno.ENOENT: log.warning( - 'Failed to clean cached archive %s: %s', - cached, exc.__str__() + "Failed to clean cached archive %s: %s", cached, exc.__str__() ) return ret -@salt.utils.decorators.path.which('rar') +@salt.utils.decorators.path.which("rar") def rar(rarfile, sources, template=None, cwd=None, runas=None): - ''' + """ Uses `rar for Linux`_ to create rar files .. _`rar for Linux`: http://www.rarlab.com/ @@ -1236,19 +1240,17 @@ def rar(rarfile, sources, template=None, cwd=None, runas=None): salt '*' archive.rar /tmp/rarfile.rar /tmp/sourcefile1,/tmp/sourcefile2 # Globbing for sources (2017.7.0 and later) salt '*' archive.rar /tmp/rarfile.rar '/tmp/sourcefile*' - ''' - cmd = ['rar', 'a', '-idp', '{0}'.format(rarfile)] + """ + cmd = ["rar", "a", "-idp", "{0}".format(rarfile)] cmd.extend(_expand_sources(sources)) - return __salt__['cmd.run'](cmd, - cwd=cwd, - template=template, - runas=runas, - python_shell=False).splitlines() + return __salt__["cmd.run"]( + cmd, cwd=cwd, template=template, runas=runas, python_shell=False + ).splitlines() -@salt.utils.decorators.path.which_bin(('unrar', 'rar')) +@salt.utils.decorators.path.which_bin(("unrar", "rar")) def unrar(rarfile, dest, excludes=None, template=None, runas=None, trim_output=False): - ''' + """ Uses `rar for Linux`_ to unpack rar files .. _`rar for Linux`: http://www.rarlab.com/ @@ -1277,71 +1279,70 @@ def unrar(rarfile, dest, excludes=None, template=None, runas=None, trim_output=F salt '*' archive.unrar /tmp/rarfile.rar /home/strongbad/ excludes=file_1,file_2 - ''' + """ if isinstance(excludes, six.string_types): - excludes = [entry.strip() for entry in excludes.split(',')] + excludes = [entry.strip() for entry in excludes.split(",")] - cmd = [salt.utils.path.which_bin(('unrar', 'rar')), - 'x', '-idp', '{0}'.format(rarfile)] + cmd = [ + salt.utils.path.which_bin(("unrar", "rar")), + "x", + "-idp", + "{0}".format(rarfile), + ] if excludes is not None: for exclude in excludes: - cmd.extend(['-x', '{0}'.format(exclude)]) - cmd.append('{0}'.format(dest)) - files = __salt__['cmd.run'](cmd, - template=template, - runas=runas, - python_shell=False).splitlines() + cmd.extend(["-x", "{0}".format(exclude)]) + cmd.append("{0}".format(dest)) + files = __salt__["cmd.run"]( + cmd, template=template, runas=runas, python_shell=False + ).splitlines() return _trim_files(files, trim_output) def _render_filenames(filenames, zip_file, saltenv, template): - ''' + """ Process markup in the :param:`filenames` and :param:`zipfile` variables (NOT the files under the paths they ultimately point to) according to the markup format provided by :param:`template`. - ''' + """ if not template: return (filenames, zip_file) # render the path as a template using path_template_engine as the engine if template not in salt.utils.templates.TEMPLATE_REGISTRY: raise CommandExecutionError( - 'Attempted to render file paths with unavailable engine ' - '{0}'.format(template) + "Attempted to render file paths with unavailable engine " + "{0}".format(template) ) kwargs = {} - kwargs['salt'] = __salt__ - kwargs['pillar'] = __pillar__ - kwargs['grains'] = __grains__ - kwargs['opts'] = __opts__ - kwargs['saltenv'] = saltenv + kwargs["salt"] = __salt__ + kwargs["pillar"] = __pillar__ + kwargs["grains"] = __grains__ + kwargs["opts"] = __opts__ + kwargs["saltenv"] = saltenv def _render(contents): - ''' + """ Render :param:`contents` into a literal pathname by writing it to a temp file, rendering that file, and returning the result. - ''' + """ # write out path to temp file tmp_path_fn = salt.utils.files.mkstemp() - with salt.utils.files.fopen(tmp_path_fn, 'w+') as fp_: + with salt.utils.files.fopen(tmp_path_fn, "w+") as fp_: fp_.write(salt.utils.stringutils.to_str(contents)) data = salt.utils.templates.TEMPLATE_REGISTRY[template]( - tmp_path_fn, - to_str=True, - **kwargs + tmp_path_fn, to_str=True, **kwargs ) salt.utils.files.safe_rm(tmp_path_fn) - if not data['result']: + if not data["result"]: # Failed to render the template raise CommandExecutionError( - 'Failed to render file path with error: {0}'.format( - data['data'] - ) + "Failed to render file path with error: {0}".format(data["data"]) ) else: - return data['data'] + return data["data"] filenames = _render(filenames) zip_file = _render(zip_file) @@ -1349,14 +1350,17 @@ def _render_filenames(filenames, zip_file, saltenv, template): def _trim_files(files, trim_output): - ''' + """ Trim the file list for output. - ''' + """ count = 100 if not isinstance(trim_output, bool): count = trim_output - if not(isinstance(trim_output, bool) and trim_output is False) and len(files) > count: + if ( + not (isinstance(trim_output, bool) and trim_output is False) + and len(files) > count + ): files = files[:count] files.append("List trimmed after {0} files.".format(count)) diff --git a/salt/modules/arista_pyeapi.py b/salt/modules/arista_pyeapi.py index 1dcf4fe1271..3095a467f86 100644 --- a/salt/modules/arista_pyeapi.py +++ b/salt/modules/arista_pyeapi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Arista pyeapi ============= @@ -88,21 +88,23 @@ outside a ``pyeapi`` Proxy, e.g.: Remember that the above applies only when not running in a ``pyeapi`` Proxy Minion. If you want to use the :mod:`pyeapi Proxy `, please follow the documentation notes for a proper setup. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python stdlib import difflib import logging +from salt.exceptions import CommandExecutionError + # Import Salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError from salt.utils.args import clean_kwargs # Import third party libs try: import pyeapi + HAS_PYEAPI = True except ImportError: HAS_PYEAPI = False @@ -111,10 +113,10 @@ except ImportError: # execution module properties # ----------------------------------------------------------------------------- -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] # Any Proxy Minion should be able to execute these -__virtualname__ = 'pyeapi' +__virtualname__ = "pyeapi" # The Execution Module will be identified as ``pyeapi`` # ----------------------------------------------------------------------------- @@ -124,14 +126,14 @@ __virtualname__ = 'pyeapi' log = logging.getLogger(__name__) PYEAPI_INIT_KWARGS = [ - 'transport', - 'host', - 'username', - 'password', - 'enablepwd', - 'port', - 'timeout', - 'return_node' + "transport", + "host", + "username", + "password", + "enablepwd", + "port", + "timeout", + "return_node", ] # ----------------------------------------------------------------------------- @@ -140,39 +142,46 @@ PYEAPI_INIT_KWARGS = [ def __virtual__(): - ''' + """ Execution module available only if pyeapi is installed. - ''' + """ if not HAS_PYEAPI: - return False, 'The pyeapi execution module requires pyeapi library to be installed: ``pip install pyeapi``' + return ( + False, + "The pyeapi execution module requires pyeapi library to be installed: ``pip install pyeapi``", + ) return __virtualname__ + # ----------------------------------------------------------------------------- # helper functions # ----------------------------------------------------------------------------- def _prepare_connection(**kwargs): - ''' + """ Prepare the connection with the remote network device, and clean up the key value pairs, removing the args used for the connection init. - ''' - pyeapi_kwargs = __salt__['config.get']('pyeapi', {}) + """ + pyeapi_kwargs = __salt__["config.get"]("pyeapi", {}) pyeapi_kwargs.update(kwargs) # merge the CLI args with the opts/pillar - init_kwargs, fun_kwargs = __utils__['args.prepare_kwargs'](pyeapi_kwargs, PYEAPI_INIT_KWARGS) - if 'transport' not in init_kwargs: - init_kwargs['transport'] = 'https' + init_kwargs, fun_kwargs = __utils__["args.prepare_kwargs"]( + pyeapi_kwargs, PYEAPI_INIT_KWARGS + ) + if "transport" not in init_kwargs: + init_kwargs["transport"] = "https" conn = pyeapi.client.connect(**init_kwargs) - node = pyeapi.client.Node(conn, enablepwd=init_kwargs.get('enablepwd')) + node = pyeapi.client.Node(conn, enablepwd=init_kwargs.get("enablepwd")) return node, fun_kwargs + # ----------------------------------------------------------------------------- # callable functions # ----------------------------------------------------------------------------- def get_connection(**kwargs): - ''' + """ Return the connection object to the pyeapi Node. .. warning:: @@ -192,16 +201,16 @@ def get_connection(**kwargs): username='example', password='example') show_ver = conn.run_commands(['show version', 'show interfaces']) - ''' + """ kwargs = clean_kwargs(**kwargs) - if 'pyeapi.conn' in __proxy__: - return __proxy__['pyeapi.conn']() + if "pyeapi.conn" in __proxy__: + return __proxy__["pyeapi.conn"]() conn, kwargs = _prepare_connection(**kwargs) return conn def call(method, *args, **kwargs): - ''' + """ Invoke an arbitrary pyeapi method. method @@ -269,17 +278,17 @@ def call(method, *args, **kwargs): .. code-block:: bash salt '*' pyeapi.call run_commands "['show version']" - ''' + """ kwargs = clean_kwargs(**kwargs) - if 'pyeapi.call' in __proxy__: - return __proxy__['pyeapi.call'](method, *args, **kwargs) + if "pyeapi.call" in __proxy__: + return __proxy__["pyeapi.call"](method, *args, **kwargs) conn, kwargs = _prepare_connection(**kwargs) ret = getattr(conn, method)(*args, **kwargs) return ret def run_commands(*commands, **kwargs): - ''' + """ Sends the commands over the transport to the device. This function sends the commands to the device using the nodes @@ -374,30 +383,30 @@ def run_commands(*commands, **kwargs): 52:54:00:3f:e6:d0 version: 4.18.1F - ''' - encoding = kwargs.pop('encoding', 'json') - send_enable = kwargs.pop('send_enable', True) - output = call('run_commands', - commands, - encoding=encoding, - send_enable=send_enable, - **kwargs) - if encoding == 'text': + """ + encoding = kwargs.pop("encoding", "json") + send_enable = kwargs.pop("send_enable", True) + output = call( + "run_commands", commands, encoding=encoding, send_enable=send_enable, **kwargs + ) + if encoding == "text": ret = [] for res in output: - ret.append(res['output']) + ret.append(res["output"]) return ret return output -def config(commands=None, - config_file=None, - template_engine='jinja', - context=None, - defaults=None, - saltenv='base', - **kwargs): - ''' +def config( + commands=None, + config_file=None, + template_engine="jinja", + context=None, + defaults=None, + saltenv="base", + **kwargs +): + """ Configures the node with the specified commands. This method is used to send configuration commands to the node. It @@ -501,41 +510,38 @@ def config(commands=None, salt '*' pyeapi.config commands="['ntp server 1.2.3.4', 'ntp server 5.6.7.8']" salt '*' pyeapi.config config_file=salt://config.txt salt '*' pyeapi.config config_file=https://bit.ly/2LGLcDy context="{'servers': ['1.2.3.4']}" - ''' + """ initial_config = get_config(as_string=True, **kwargs) if config_file: - file_str = __salt__['cp.get_file_str'](config_file, saltenv=saltenv) + file_str = __salt__["cp.get_file_str"](config_file, saltenv=saltenv) if file_str is False: - raise CommandExecutionError('Source file {} not found'.format(config_file)) - log.debug('Fetched from %s', config_file) + raise CommandExecutionError("Source file {} not found".format(config_file)) + log.debug("Fetched from %s", config_file) log.debug(file_str) elif commands: if isinstance(commands, (six.string_types, six.text_type)): commands = [commands] - file_str = '\n'.join(commands) + file_str = "\n".join(commands) # unify all the commands in a single file, to render them in a go if template_engine: - file_str = __salt__['file.apply_template_on_contents'](file_str, - template_engine, - context, - defaults, - saltenv) - log.debug('Rendered:') + file_str = __salt__["file.apply_template_on_contents"]( + file_str, template_engine, context, defaults, saltenv + ) + log.debug("Rendered:") log.debug(file_str) # whatever the source of the commands would be, split them line by line commands = [line for line in file_str.splitlines() if line.strip()] # push the commands one by one, removing empty lines - configured = call('config', commands, **kwargs) + configured = call("config", commands, **kwargs) current_config = get_config(as_string=True, **kwargs) - diff = difflib.unified_diff(initial_config.splitlines(1)[4:], current_config.splitlines(1)[4:]) - return ''.join([x.replace('\r', '') for x in diff]) + diff = difflib.unified_diff( + initial_config.splitlines(1)[4:], current_config.splitlines(1)[4:] + ) + return "".join([x.replace("\r", "") for x in diff]) -def get_config(config='running-config', - params=None, - as_string=False, - **kwargs): - ''' +def get_config(config="running-config", params=None, as_string=False, **kwargs): + """ Retrieves the config from the device. This method will retrieve the config from the node as either a string @@ -612,16 +618,14 @@ def get_config(config='running-config', salt '*' pyeapi.get_config salt '*' pyeapi.get_config params='section snmp-server' salt '*' pyeapi.get_config config='startup-config' - ''' - return call('get_config', - config=config, - params=params, - as_string=as_string, - **kwargs) + """ + return call( + "get_config", config=config, params=params, as_string=as_string, **kwargs + ) -def section(regex, config='running-config', **kwargs): - ''' +def section(regex, config="running-config", **kwargs): + """ Return a section of the config. regex @@ -689,5 +693,5 @@ def section(regex, config='running-config', **kwargs): .. code-block:: bash salt '*' - ''' - return call('section', regex, config=config, **kwargs) + """ + return call("section", regex, config=config, **kwargs) diff --git a/salt/modules/artifactory.py b/salt/modules/artifactory.py index 01019a87f37..e2daf7ef9bf 100644 --- a/salt/modules/artifactory.py +++ b/salt/modules/artifactory.py @@ -1,46 +1,69 @@ # -*- coding: utf-8 -*- -''' +""" Module for fetching artifacts from Artifactory -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import base64 import logging +import os + +import salt.ext.six.moves.http_client # pylint: disable=import-error,redefined-builtin,no-name-in-module # Import Salt libs import salt.utils.files import salt.utils.stringutils -import salt.ext.six.moves.http_client # pylint: disable=import-error,redefined-builtin,no-name-in-module -from salt.ext.six.moves import urllib # pylint: disable=no-name-in-module -from salt.ext.six.moves.urllib.error import HTTPError, URLError # pylint: disable=no-name-in-module from salt.exceptions import CommandExecutionError +from salt.ext.six.moves import urllib # pylint: disable=no-name-in-module +from salt.ext.six.moves.urllib.error import ( # pylint: disable=no-name-in-module + HTTPError, + URLError, +) # Import 3rd party libs try: from salt._compat import ElementTree as ET + HAS_ELEMENT_TREE = True except ImportError: HAS_ELEMENT_TREE = False log = logging.getLogger(__name__) -__virtualname__ = 'artifactory' +__virtualname__ = "artifactory" def __virtual__(): - ''' + """ Only load if elementtree xml library is available. - ''' + """ if not HAS_ELEMENT_TREE: - return (False, 'Cannot load {0} module: ElementTree library unavailable'.format(__virtualname__)) + return ( + False, + "Cannot load {0} module: ElementTree library unavailable".format( + __virtualname__ + ), + ) else: return True -def get_latest_snapshot(artifactory_url, repository, group_id, artifact_id, packaging, target_dir='/tmp', target_file=None, classifier=None, username=None, password=None, use_literal_group_id=False): - ''' +def get_latest_snapshot( + artifactory_url, + repository, + group_id, + artifact_id, + packaging, + target_dir="/tmp", + target_file=None, + classifier=None, + username=None, + password=None, + use_literal_group_id=False, +): + """ Gets latest snapshot of the given artifact artifactory_url @@ -63,23 +86,64 @@ def get_latest_snapshot(artifactory_url, repository, group_id, artifact_id, pack Artifactory username. Optional parameter. password Artifactory password. Optional parameter. - ''' - log.debug("======================== MODULE FUNCTION: artifactory.get_latest_snapshot, artifactory_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, target_dir=%s, classifier=%s)", - artifactory_url, repository, group_id, artifact_id, packaging, target_dir, classifier) + """ + log.debug( + "======================== MODULE FUNCTION: artifactory.get_latest_snapshot, artifactory_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, target_dir=%s, classifier=%s)", + artifactory_url, + repository, + group_id, + artifact_id, + packaging, + target_dir, + classifier, + ) headers = {} if username and password: - headers['Authorization'] = 'Basic {0}'.format(base64.encodestring('{0}:{1}'.format(username, password)).replace('\n', '')) - artifact_metadata = _get_artifact_metadata(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, headers=headers, use_literal_group_id=use_literal_group_id) - version = artifact_metadata['latest_version'] - snapshot_url, file_name = _get_snapshot_url(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, packaging=packaging, classifier=classifier, headers=headers, use_literal_group_id=use_literal_group_id) + headers["Authorization"] = "Basic {0}".format( + base64.encodestring("{0}:{1}".format(username, password)).replace("\n", "") + ) + artifact_metadata = _get_artifact_metadata( + artifactory_url=artifactory_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + headers=headers, + use_literal_group_id=use_literal_group_id, + ) + version = artifact_metadata["latest_version"] + snapshot_url, file_name = _get_snapshot_url( + artifactory_url=artifactory_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + version=version, + packaging=packaging, + classifier=classifier, + headers=headers, + use_literal_group_id=use_literal_group_id, + ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(snapshot_url, target_file, headers) -def get_snapshot(artifactory_url, repository, group_id, artifact_id, packaging, version, snapshot_version=None, target_dir='/tmp', target_file=None, classifier=None, username=None, password=None, use_literal_group_id=False): - ''' +def get_snapshot( + artifactory_url, + repository, + group_id, + artifact_id, + packaging, + version, + snapshot_version=None, + target_dir="/tmp", + target_file=None, + classifier=None, + username=None, + password=None, + use_literal_group_id=False, +): + """ Gets snapshot of the desired version of the artifact artifactory_url @@ -104,20 +168,54 @@ def get_snapshot(artifactory_url, repository, group_id, artifact_id, packaging, Artifactory username. Optional parameter. password Artifactory password. Optional parameter. - ''' - log.debug('======================== MODULE FUNCTION: artifactory.get_snapshot(artifactory_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)', - artifactory_url, repository, group_id, artifact_id, packaging, version, target_dir, classifier) + """ + log.debug( + "======================== MODULE FUNCTION: artifactory.get_snapshot(artifactory_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)", + artifactory_url, + repository, + group_id, + artifact_id, + packaging, + version, + target_dir, + classifier, + ) headers = {} if username and password: - headers['Authorization'] = 'Basic {0}'.format(base64.encodestring('{0}:{1}'.format(username, password)).replace('\n', '')) - snapshot_url, file_name = _get_snapshot_url(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, packaging=packaging, snapshot_version=snapshot_version, classifier=classifier, headers=headers, use_literal_group_id=use_literal_group_id) + headers["Authorization"] = "Basic {0}".format( + base64.encodestring("{0}:{1}".format(username, password)).replace("\n", "") + ) + snapshot_url, file_name = _get_snapshot_url( + artifactory_url=artifactory_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + version=version, + packaging=packaging, + snapshot_version=snapshot_version, + classifier=classifier, + headers=headers, + use_literal_group_id=use_literal_group_id, + ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(snapshot_url, target_file, headers) -def get_latest_release(artifactory_url, repository, group_id, artifact_id, packaging, target_dir='/tmp', target_file=None, classifier=None, username=None, password=None, use_literal_group_id=False): - ''' +def get_latest_release( + artifactory_url, + repository, + group_id, + artifact_id, + packaging, + target_dir="/tmp", + target_file=None, + classifier=None, + username=None, + password=None, + use_literal_group_id=False, +): + """ Gets the latest release of the artifact artifactory_url @@ -140,21 +238,59 @@ def get_latest_release(artifactory_url, repository, group_id, artifact_id, packa Artifactory username. Optional parameter. password Artifactory password. Optional parameter. - ''' - log.debug('======================== MODULE FUNCTION: artifactory.get_latest_release(artifactory_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, target_dir=%s, classifier=%s)', - artifactory_url, repository, group_id, artifact_id, packaging, target_dir, classifier) + """ + log.debug( + "======================== MODULE FUNCTION: artifactory.get_latest_release(artifactory_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, target_dir=%s, classifier=%s)", + artifactory_url, + repository, + group_id, + artifact_id, + packaging, + target_dir, + classifier, + ) headers = {} if username and password: - headers['Authorization'] = 'Basic {0}'.format(base64.encodestring('{0}:{1}'.format(username, password)).replace('\n', '')) - version = __find_latest_version(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, headers=headers) - release_url, file_name = _get_release_url(repository, group_id, artifact_id, packaging, version, artifactory_url, classifier, use_literal_group_id) + headers["Authorization"] = "Basic {0}".format( + base64.encodestring("{0}:{1}".format(username, password)).replace("\n", "") + ) + version = __find_latest_version( + artifactory_url=artifactory_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + headers=headers, + ) + release_url, file_name = _get_release_url( + repository, + group_id, + artifact_id, + packaging, + version, + artifactory_url, + classifier, + use_literal_group_id, + ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(release_url, target_file, headers) -def get_release(artifactory_url, repository, group_id, artifact_id, packaging, version, target_dir='/tmp', target_file=None, classifier=None, username=None, password=None, use_literal_group_id=False): - ''' +def get_release( + artifactory_url, + repository, + group_id, + artifact_id, + packaging, + version, + target_dir="/tmp", + target_file=None, + classifier=None, + username=None, + password=None, + use_literal_group_id=False, +): + """ Gets the specified release of the artifact artifactory_url @@ -179,13 +315,33 @@ def get_release(artifactory_url, repository, group_id, artifact_id, packaging, v Artifactory username. Optional parameter. password Artifactory password. Optional parameter. - ''' - log.debug('======================== MODULE FUNCTION: artifactory.get_release(artifactory_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)', - artifactory_url, repository, group_id, artifact_id, packaging, version, target_dir, classifier) + """ + log.debug( + "======================== MODULE FUNCTION: artifactory.get_release(artifactory_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)", + artifactory_url, + repository, + group_id, + artifact_id, + packaging, + version, + target_dir, + classifier, + ) headers = {} if username and password: - headers['Authorization'] = 'Basic {0}'.format(base64.encodestring('{0}:{1}'.format(username, password)).replace('\n', '')) - release_url, file_name = _get_release_url(repository, group_id, artifact_id, packaging, version, artifactory_url, classifier, use_literal_group_id) + headers["Authorization"] = "Basic {0}".format( + base64.encodestring("{0}:{1}".format(username, password)).replace("\n", "") + ) + release_url, file_name = _get_release_url( + repository, + group_id, + artifact_id, + packaging, + version, + artifactory_url, + classifier, + use_literal_group_id, + ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(release_url, target_file, headers) @@ -197,160 +353,241 @@ def __resolve_target_file(file_name, target_dir, target_file=None): return target_file -def _get_snapshot_url(artifactory_url, repository, group_id, artifact_id, version, packaging, snapshot_version=None, classifier=None, headers=None, use_literal_group_id=False): +def _get_snapshot_url( + artifactory_url, + repository, + group_id, + artifact_id, + version, + packaging, + snapshot_version=None, + classifier=None, + headers=None, + use_literal_group_id=False, +): if headers is None: headers = {} has_classifier = classifier is not None and classifier != "" if snapshot_version is None: try: - snapshot_version_metadata = _get_snapshot_version_metadata(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers) - if packaging not in snapshot_version_metadata['snapshot_versions']: - error_message = '''Cannot find requested packaging '{packaging}' in the snapshot version metadata. + snapshot_version_metadata = _get_snapshot_version_metadata( + artifactory_url=artifactory_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + version=version, + headers=headers, + ) + if packaging not in snapshot_version_metadata["snapshot_versions"]: + error_message = """Cannot find requested packaging '{packaging}' in the snapshot version metadata. artifactory_url: {artifactory_url} repository: {repository} group_id: {group_id} artifact_id: {artifact_id} packaging: {packaging} classifier: {classifier} - version: {version}'''.format( - artifactory_url=artifactory_url, - repository=repository, - group_id=group_id, - artifact_id=artifact_id, - packaging=packaging, - classifier=classifier, - version=version) + version: {version}""".format( + artifactory_url=artifactory_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + packaging=packaging, + classifier=classifier, + version=version, + ) raise ArtifactoryError(error_message) - packaging_with_classifier = packaging if not has_classifier else packaging + ':' + classifier - if has_classifier and packaging_with_classifier not in snapshot_version_metadata['snapshot_versions']: - error_message = '''Cannot find requested classifier '{classifier}' in the snapshot version metadata. + packaging_with_classifier = ( + packaging if not has_classifier else packaging + ":" + classifier + ) + if ( + has_classifier + and packaging_with_classifier + not in snapshot_version_metadata["snapshot_versions"] + ): + error_message = """Cannot find requested classifier '{classifier}' in the snapshot version metadata. artifactory_url: {artifactory_url} repository: {repository} group_id: {group_id} artifact_id: {artifact_id} packaging: {packaging} classifier: {classifier} - version: {version}'''.format( - artifactory_url=artifactory_url, - repository=repository, - group_id=group_id, - artifact_id=artifact_id, - packaging=packaging, - classifier=classifier, - version=version) + version: {version}""".format( + artifactory_url=artifactory_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + packaging=packaging, + classifier=classifier, + version=version, + ) raise ArtifactoryError(error_message) - snapshot_version = snapshot_version_metadata['snapshot_versions'][packaging_with_classifier] + snapshot_version = snapshot_version_metadata["snapshot_versions"][ + packaging_with_classifier + ] except CommandExecutionError as err: - log.error('Could not fetch maven-metadata.xml. Assuming snapshot_version=%s.', version) + log.error( + "Could not fetch maven-metadata.xml. Assuming snapshot_version=%s.", + version, + ) snapshot_version = version group_url = __get_group_id_subpath(group_id, use_literal_group_id) - file_name = '{artifact_id}-{snapshot_version}{classifier}.{packaging}'.format( + file_name = "{artifact_id}-{snapshot_version}{classifier}.{packaging}".format( artifact_id=artifact_id, snapshot_version=snapshot_version, packaging=packaging, - classifier=__get_classifier_url(classifier)) + classifier=__get_classifier_url(classifier), + ) - snapshot_url = '{artifactory_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}'.format( - artifactory_url=artifactory_url, - repository=repository, - group_url=group_url, - artifact_id=artifact_id, - version=version, - file_name=file_name) - log.debug('snapshot_url=%s', snapshot_url) + snapshot_url = "{artifactory_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}".format( + artifactory_url=artifactory_url, + repository=repository, + group_url=group_url, + artifact_id=artifact_id, + version=version, + file_name=file_name, + ) + log.debug("snapshot_url=%s", snapshot_url) return snapshot_url, file_name -def _get_release_url(repository, group_id, artifact_id, packaging, version, artifactory_url, classifier=None, use_literal_group_id=False): +def _get_release_url( + repository, + group_id, + artifact_id, + packaging, + version, + artifactory_url, + classifier=None, + use_literal_group_id=False, +): group_url = __get_group_id_subpath(group_id, use_literal_group_id) # for released versions the suffix for the file is same as version - file_name = '{artifact_id}-{version}{classifier}.{packaging}'.format( + file_name = "{artifact_id}-{version}{classifier}.{packaging}".format( artifact_id=artifact_id, version=version, packaging=packaging, - classifier=__get_classifier_url(classifier)) + classifier=__get_classifier_url(classifier), + ) - release_url = '{artifactory_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}'.format( - artifactory_url=artifactory_url, - repository=repository, - group_url=group_url, - artifact_id=artifact_id, - version=version, - file_name=file_name) - log.debug('release_url=%s', release_url) + release_url = "{artifactory_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}".format( + artifactory_url=artifactory_url, + repository=repository, + group_url=group_url, + artifact_id=artifact_id, + version=version, + file_name=file_name, + ) + log.debug("release_url=%s", release_url) return release_url, file_name -def _get_artifact_metadata_url(artifactory_url, repository, group_id, artifact_id, use_literal_group_id=False): +def _get_artifact_metadata_url( + artifactory_url, repository, group_id, artifact_id, use_literal_group_id=False +): group_url = __get_group_id_subpath(group_id, use_literal_group_id) # for released versions the suffix for the file is same as version - artifact_metadata_url = '{artifactory_url}/{repository}/{group_url}/{artifact_id}/maven-metadata.xml'.format( - artifactory_url=artifactory_url, - repository=repository, - group_url=group_url, - artifact_id=artifact_id) - log.debug('artifact_metadata_url=%s', artifact_metadata_url) + artifact_metadata_url = "{artifactory_url}/{repository}/{group_url}/{artifact_id}/maven-metadata.xml".format( + artifactory_url=artifactory_url, + repository=repository, + group_url=group_url, + artifact_id=artifact_id, + ) + log.debug("artifact_metadata_url=%s", artifact_metadata_url) return artifact_metadata_url -def _get_artifact_metadata_xml(artifactory_url, repository, group_id, artifact_id, headers, use_literal_group_id=False): +def _get_artifact_metadata_xml( + artifactory_url, + repository, + group_id, + artifact_id, + headers, + use_literal_group_id=False, +): artifact_metadata_url = _get_artifact_metadata_url( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, - use_literal_group_id=use_literal_group_id + use_literal_group_id=use_literal_group_id, ) try: request = urllib.request.Request(artifact_metadata_url, None, headers) artifact_metadata_xml = urllib.request.urlopen(request).read() except (HTTPError, URLError) as err: - message = 'Could not fetch data from url: {0}. ERROR: {1}'.format( - artifact_metadata_url, - err + message = "Could not fetch data from url: {0}. ERROR: {1}".format( + artifact_metadata_url, err ) raise CommandExecutionError(message) - log.debug('artifact_metadata_xml=%s', artifact_metadata_xml) + log.debug("artifact_metadata_xml=%s", artifact_metadata_xml) return artifact_metadata_xml -def _get_artifact_metadata(artifactory_url, repository, group_id, artifact_id, headers, use_literal_group_id=False): - metadata_xml = _get_artifact_metadata_xml(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, headers=headers, use_literal_group_id=use_literal_group_id) +def _get_artifact_metadata( + artifactory_url, + repository, + group_id, + artifact_id, + headers, + use_literal_group_id=False, +): + metadata_xml = _get_artifact_metadata_xml( + artifactory_url=artifactory_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + headers=headers, + use_literal_group_id=use_literal_group_id, + ) root = ET.fromstring(metadata_xml) - assert group_id == root.find('groupId').text - assert artifact_id == root.find('artifactId').text - latest_version = root.find('versioning').find('latest').text - return { - 'latest_version': latest_version - } + assert group_id == root.find("groupId").text + assert artifact_id == root.find("artifactId").text + latest_version = root.find("versioning").find("latest").text + return {"latest_version": latest_version} # functions for handling snapshots -def _get_snapshot_version_metadata_url(artifactory_url, repository, group_id, artifact_id, version, use_literal_group_id=False): +def _get_snapshot_version_metadata_url( + artifactory_url, + repository, + group_id, + artifact_id, + version, + use_literal_group_id=False, +): group_url = __get_group_id_subpath(group_id, use_literal_group_id) # for released versions the suffix for the file is same as version - snapshot_version_metadata_url = '{artifactory_url}/{repository}/{group_url}/{artifact_id}/{version}/maven-metadata.xml'.format( - artifactory_url=artifactory_url, - repository=repository, - group_url=group_url, - artifact_id=artifact_id, - version=version) - log.debug('snapshot_version_metadata_url=%s', snapshot_version_metadata_url) + snapshot_version_metadata_url = "{artifactory_url}/{repository}/{group_url}/{artifact_id}/{version}/maven-metadata.xml".format( + artifactory_url=artifactory_url, + repository=repository, + group_url=group_url, + artifact_id=artifact_id, + version=version, + ) + log.debug("snapshot_version_metadata_url=%s", snapshot_version_metadata_url) return snapshot_version_metadata_url -def _get_snapshot_version_metadata_xml(artifactory_url, repository, group_id, artifact_id, version, headers, use_literal_group_id=False): +def _get_snapshot_version_metadata_xml( + artifactory_url, + repository, + group_id, + artifact_id, + version, + headers, + use_literal_group_id=False, +): snapshot_version_metadata_url = _get_snapshot_version_metadata_url( artifactory_url=artifactory_url, @@ -358,141 +595,161 @@ def _get_snapshot_version_metadata_xml(artifactory_url, repository, group_id, ar group_id=group_id, artifact_id=artifact_id, version=version, - use_literal_group_id=use_literal_group_id + use_literal_group_id=use_literal_group_id, ) try: request = urllib.request.Request(snapshot_version_metadata_url, None, headers) snapshot_version_metadata_xml = urllib.request.urlopen(request).read() except (HTTPError, URLError) as err: - message = 'Could not fetch data from url: {0}. ERROR: {1}'.format( - snapshot_version_metadata_url, - err + message = "Could not fetch data from url: {0}. ERROR: {1}".format( + snapshot_version_metadata_url, err ) raise CommandExecutionError(message) - log.debug('snapshot_version_metadata_xml=%s', snapshot_version_metadata_xml) + log.debug("snapshot_version_metadata_xml=%s", snapshot_version_metadata_xml) return snapshot_version_metadata_xml -def _get_snapshot_version_metadata(artifactory_url, repository, group_id, artifact_id, version, headers): - metadata_xml = _get_snapshot_version_metadata_xml(artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers) +def _get_snapshot_version_metadata( + artifactory_url, repository, group_id, artifact_id, version, headers +): + metadata_xml = _get_snapshot_version_metadata_xml( + artifactory_url=artifactory_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + version=version, + headers=headers, + ) metadata = ET.fromstring(metadata_xml) - assert group_id == metadata.find('groupId').text - assert artifact_id == metadata.find('artifactId').text - assert version == metadata.find('version').text + assert group_id == metadata.find("groupId").text + assert artifact_id == metadata.find("artifactId").text + assert version == metadata.find("version").text - snapshot_versions = metadata.find('versioning').find('snapshotVersions') + snapshot_versions = metadata.find("versioning").find("snapshotVersions") extension_version_dict = {} for snapshot_version in snapshot_versions: - extension = snapshot_version.find('extension').text - value = snapshot_version.find('value').text - if snapshot_version.find('classifier') is not None: - classifier = snapshot_version.find('classifier').text - extension_version_dict[extension + ':' + classifier] = value + extension = snapshot_version.find("extension").text + value = snapshot_version.find("value").text + if snapshot_version.find("classifier") is not None: + classifier = snapshot_version.find("classifier").text + extension_version_dict[extension + ":" + classifier] = value else: extension_version_dict[extension] = value - return { - 'snapshot_versions': extension_version_dict - } + return {"snapshot_versions": extension_version_dict} -def __get_latest_version_url(artifactory_url, repository, group_id, artifact_id, use_literal_group_id=False): +def __get_latest_version_url( + artifactory_url, repository, group_id, artifact_id, use_literal_group_id=False +): group_url = __get_group_id_subpath(group_id, use_literal_group_id) # for released versions the suffix for the file is same as version - latest_version_url = '{artifactory_url}/api/search/latestVersion?g={group_url}&a={artifact_id}&repos={repository}'.format( - artifactory_url=artifactory_url, - repository=repository, - group_url=group_url, - artifact_id=artifact_id) - log.debug('latest_version_url=%s', latest_version_url) + latest_version_url = "{artifactory_url}/api/search/latestVersion?g={group_url}&a={artifact_id}&repos={repository}".format( + artifactory_url=artifactory_url, + repository=repository, + group_url=group_url, + artifact_id=artifact_id, + ) + log.debug("latest_version_url=%s", latest_version_url) return latest_version_url -def __find_latest_version(artifactory_url, repository, group_id, artifact_id, headers, use_literal_group_id=False): +def __find_latest_version( + artifactory_url, + repository, + group_id, + artifact_id, + headers, + use_literal_group_id=False, +): latest_version_url = __get_latest_version_url( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, - use_literal_group_id=use_literal_group_id + use_literal_group_id=use_literal_group_id, ) try: request = urllib.request.Request(latest_version_url, None, headers) version = urllib.request.urlopen(request).read() except (HTTPError, URLError) as err: - message = 'Could not fetch data from url: {0}. ERROR: {1}'.format( - latest_version_url, - err + message = "Could not fetch data from url: {0}. ERROR: {1}".format( + latest_version_url, err ) raise CommandExecutionError(message) log.debug("Response of: %s", version) - if version is None or version == '': - raise ArtifactoryError('Unable to find release version') + if version is None or version == "": + raise ArtifactoryError("Unable to find release version") return version def __save_artifact(artifact_url, target_file, headers): log.debug("__save_artifact(%s, %s)", artifact_url, target_file) - result = { - 'status': False, - 'changes': {}, - 'comment': '' - } + result = {"status": False, "changes": {}, "comment": ""} if os.path.isfile(target_file): log.debug("File %s already exists, checking checksum...", target_file) checksum_url = artifact_url + ".sha1" - checksum_success, artifact_sum, checksum_comment = __download(checksum_url, headers) + checksum_success, artifact_sum, checksum_comment = __download( + checksum_url, headers + ) if checksum_success: log.debug("Downloaded SHA1 SUM: %s", artifact_sum) - file_sum = __salt__['file.get_hash'](path=target_file, form='sha1') + file_sum = __salt__["file.get_hash"](path=target_file, form="sha1") log.debug("Target file (%s) SHA1 SUM: %s", target_file, file_sum) if artifact_sum == file_sum: - result['status'] = True - result['target_file'] = target_file - result['comment'] = 'File {0} already exists, checksum matches with Artifactory.\n' \ - 'Checksum URL: {1}'.format(target_file, checksum_url) + result["status"] = True + result["target_file"] = target_file + result["comment"] = ( + "File {0} already exists, checksum matches with Artifactory.\n" + "Checksum URL: {1}".format(target_file, checksum_url) + ) return result else: - result['comment'] = 'File {0} already exists, checksum does not match with Artifactory!\n'\ - 'Checksum URL: {1}'.format(target_file, checksum_url) + result["comment"] = ( + "File {0} already exists, checksum does not match with Artifactory!\n" + "Checksum URL: {1}".format(target_file, checksum_url) + ) else: - result['status'] = False - result['comment'] = checksum_comment + result["status"] = False + result["comment"] = checksum_comment return result - log.debug('Downloading: %s -> %s', artifact_url, target_file) + log.debug("Downloading: %s -> %s", artifact_url, target_file) try: request = urllib.request.Request(artifact_url, None, headers) f = urllib.request.urlopen(request) with salt.utils.files.fopen(target_file, "wb") as local_file: local_file.write(salt.utils.stringutils.to_bytes(f.read())) - result['status'] = True - result['comment'] = __append_comment(('Artifact downloaded from URL: {0}'.format(artifact_url)), result['comment']) - result['changes']['downloaded_file'] = target_file - result['target_file'] = target_file + result["status"] = True + result["comment"] = __append_comment( + ("Artifact downloaded from URL: {0}".format(artifact_url)), + result["comment"], + ) + result["changes"]["downloaded_file"] = target_file + result["target_file"] = target_file except (HTTPError, URLError) as e: - result['status'] = False - result['comment'] = __get_error_comment(e, artifact_url) + result["status"] = False + result["comment"] = __get_error_comment(e, artifact_url) return result def __get_group_id_subpath(group_id, use_literal_group_id=False): if not use_literal_group_id: - group_url = group_id.replace('.', '/') + group_url = group_id.replace(".", "/") return group_url return group_id @@ -503,7 +760,7 @@ def __get_classifier_url(classifier): def __download(request_url, headers): - log.debug('Downloading content from %s', request_url) + log.debug("Downloading content from %s", request_url) success = False content = None @@ -521,22 +778,27 @@ def __download(request_url, headers): def __get_error_comment(http_error, request_url): if http_error.code == salt.ext.six.moves.http_client.NOT_FOUND: - comment = 'HTTP Error 404. Request URL: ' + request_url + comment = "HTTP Error 404. Request URL: " + request_url elif http_error.code == salt.ext.six.moves.http_client.CONFLICT: - comment = 'HTTP Error 409: Conflict. Requested URL: {0}. \n' \ - 'This error may be caused by reading snapshot artifact from non-snapshot repository.'.format(request_url) + comment = ( + "HTTP Error 409: Conflict. Requested URL: {0}. \n" + "This error may be caused by reading snapshot artifact from non-snapshot repository.".format( + request_url + ) + ) else: - comment = 'HTTP Error {err_code}. Request URL: {url}'.format(err_code=http_error.code, url=request_url) + comment = "HTTP Error {err_code}. Request URL: {url}".format( + err_code=http_error.code, url=request_url + ) return comment -def __append_comment(new_comment, current_comment=''): - return current_comment+'\n'+new_comment +def __append_comment(new_comment, current_comment=""): + return current_comment + "\n" + new_comment class ArtifactoryError(Exception): - def __init__(self, value): super(ArtifactoryError, self).__init__() self.value = value diff --git a/salt/modules/at.py b/salt/modules/at.py index bd2d45a4fff..119a8dbac1f 100644 --- a/salt/modules/at.py +++ b/salt/modules/at.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Wrapper module for at(1) Also, a 'tag' feature has been added to more @@ -8,60 +8,61 @@ easily tag jobs. :platform: linux,openbsd,freebsd .. versionchanged:: 2017.7.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import datetime + # Import python libs import re import time -import datetime - -# Import 3rd-party libs -# pylint: disable=import-error,redefined-builtin -from salt.ext.six.moves import map -# pylint: enable=import-error,redefined-builtin -from salt.exceptions import CommandNotFoundError -from salt.ext import six # Import salt libs import salt.utils.data import salt.utils.path import salt.utils.platform +# pylint: enable=import-error,redefined-builtin +from salt.exceptions import CommandNotFoundError +from salt.ext import six + +# Import 3rd-party libs +# pylint: disable=import-error,redefined-builtin +from salt.ext.six.moves import map + # OS Families that should work (Ubuntu and Debian are the default) # TODO: Refactor some of this module to remove the checks for binaries # Tested on OpenBSD 5.0 -BSD = ('OpenBSD', 'FreeBSD') +BSD = ("OpenBSD", "FreeBSD") -__virtualname__ = 'at' +__virtualname__ = "at" def __virtual__(): - ''' + """ Most everything has the ability to support at(1) - ''' + """ if salt.utils.platform.is_windows() or salt.utils.platform.is_sunos(): - return (False, 'The at module could not be loaded: unsupported platform') - if salt.utils.path.which('at') is None: - return (False, 'The at module could not be loaded: at command not found') + return (False, "The at module could not be loaded: unsupported platform") + if salt.utils.path.which("at") is None: + return (False, "The at module could not be loaded: at command not found") return __virtualname__ def _cmd(binary, *args): - ''' + """ Wrapper to run at(1) or return None. - ''' + """ binary = salt.utils.path.which(binary) if not binary: - raise CommandNotFoundError('{0}: command not found'.format(binary)) + raise CommandNotFoundError("{0}: command not found".format(binary)) cmd = [binary] + list(args) - return __salt__['cmd.run_stdout']([binary] + list(args), - python_shell=False) + return __salt__["cmd.run_stdout"]([binary] + list(args), python_shell=False) def atq(tag=None): - ''' + """ List all queued and running jobs or only those with an optional 'tag'. @@ -72,78 +73,89 @@ def atq(tag=None): salt '*' at.atq salt '*' at.atq [tag] salt '*' at.atq [job number] - ''' + """ jobs = [] # Shim to produce output similar to what __virtual__() should do # but __salt__ isn't available in __virtual__() # Tested on CentOS 5.8 - if __grains__['os_family'] == 'RedHat': - output = _cmd('at', '-l') + if __grains__["os_family"] == "RedHat": + output = _cmd("at", "-l") else: - output = _cmd('atq') + output = _cmd("atq") if output is None: - return '\'at.atq\' is not available.' + return "'at.atq' is not available." # No jobs so return - if output == '': - return {'jobs': jobs} + if output == "": + return {"jobs": jobs} # Jobs created with at.at() will use the following # comment to denote a tagged job. - job_kw_regex = re.compile(r'^### SALT: (\w+)') + job_kw_regex = re.compile(r"^### SALT: (\w+)") # Split each job into a dictionary and handle # pulling out tags or only listing jobs with a certain # tag for line in output.splitlines(): - job_tag = '' + job_tag = "" # Redhat/CentOS - if __grains__['os_family'] == 'RedHat': - job, spec = line.split('\t') + if __grains__["os_family"] == "RedHat": + job, spec = line.split("\t") specs = spec.split() - elif __grains__['os'] == 'OpenBSD': - if line.startswith(' Rank'): + elif __grains__["os"] == "OpenBSD": + if line.startswith(" Rank"): continue else: tmp = line.split() - timestr = ' '.join(tmp[1:5]) + timestr = " ".join(tmp[1:5]) job = tmp[6] - specs = datetime.datetime(*(time.strptime(timestr, '%b %d, %Y ' - '%H:%M')[0:5])).isoformat().split('T') + specs = ( + datetime.datetime( + *(time.strptime(timestr, "%b %d, %Y " "%H:%M")[0:5]) + ) + .isoformat() + .split("T") + ) specs.append(tmp[7]) specs.append(tmp[5]) - elif __grains__['os'] == 'FreeBSD': - if line.startswith('Date'): + elif __grains__["os"] == "FreeBSD": + if line.startswith("Date"): continue else: tmp = line.split() - timestr = ' '.join(tmp[1:6]) + timestr = " ".join(tmp[1:6]) job = tmp[8] - specs = datetime.datetime(*(time.strptime(timestr, - '%b %d %H:%M:%S %Z %Y')[0:5])).isoformat().split('T') + specs = ( + datetime.datetime( + *(time.strptime(timestr, "%b %d %H:%M:%S %Z %Y")[0:5]) + ) + .isoformat() + .split("T") + ) specs.append(tmp[7]) specs.append(tmp[6]) else: - job, spec = line.split('\t') + job, spec = line.split("\t") tmp = spec.split() - timestr = ' '.join(tmp[0:5]) - specs = datetime.datetime(*(time.strptime(timestr) - [0:5])).isoformat().split('T') + timestr = " ".join(tmp[0:5]) + specs = ( + datetime.datetime(*(time.strptime(timestr)[0:5])).isoformat().split("T") + ) specs.append(tmp[5]) specs.append(tmp[6]) # Search for any tags - atc_out = _cmd('at', '-c', job) + atc_out = _cmd("at", "-c", job) for line in atc_out.splitlines(): tmp = job_kw_regex.match(line) if tmp: job_tag = tmp.groups()[0] - if __grains__['os'] in BSD: + if __grains__["os"] in BSD: job = six.text_type(job) else: job = int(job) @@ -154,17 +166,33 @@ def atq(tag=None): # If I don't wrap job in an int(), it fails on salt but works on # salt-call. With the int(), it fails with salt-call but not salt. if tag == job_tag or tag == job: - jobs.append({'job': job, 'date': specs[0], 'time': specs[1], - 'queue': specs[2], 'user': specs[3], 'tag': job_tag}) + jobs.append( + { + "job": job, + "date": specs[0], + "time": specs[1], + "queue": specs[2], + "user": specs[3], + "tag": job_tag, + } + ) else: - jobs.append({'job': job, 'date': specs[0], 'time': specs[1], - 'queue': specs[2], 'user': specs[3], 'tag': job_tag}) + jobs.append( + { + "job": job, + "date": specs[0], + "time": specs[1], + "queue": specs[2], + "user": specs[3], + "tag": job_tag, + } + ) - return {'jobs': jobs} + return {"jobs": jobs} def atrm(*args): - ''' + """ Remove jobs from the queue. CLI Example: @@ -174,41 +202,51 @@ def atrm(*args): salt '*' at.atrm .. salt '*' at.atrm all salt '*' at.atrm all [tag] - ''' + """ # Need to do this here also since we use atq() - if not salt.utils.path.which('at'): - return '\'at.atrm\' is not available.' + if not salt.utils.path.which("at"): + return "'at.atrm' is not available." if not args: - return {'jobs': {'removed': [], 'tag': None}} + return {"jobs": {"removed": [], "tag": None}} # Convert all to strings args = salt.utils.data.stringify(args) - if args[0] == 'all': + if args[0] == "all": if len(args) > 1: - opts = list(list(map(str, [j['job'] for j in atq(args[1])['jobs']]))) - ret = {'jobs': {'removed': opts, 'tag': args[1]}} + opts = list(list(map(str, [j["job"] for j in atq(args[1])["jobs"]]))) + ret = {"jobs": {"removed": opts, "tag": args[1]}} else: - opts = list(list(map(str, [j['job'] for j in atq()['jobs']]))) - ret = {'jobs': {'removed': opts, 'tag': None}} + opts = list(list(map(str, [j["job"] for j in atq()["jobs"]]))) + ret = {"jobs": {"removed": opts, "tag": None}} else: - opts = list(list(map(str, [i['job'] for i in atq()['jobs'] - if six.text_type(i['job']) in args]))) - ret = {'jobs': {'removed': opts, 'tag': None}} + opts = list( + list( + map( + str, + [ + i["job"] + for i in atq()["jobs"] + if six.text_type(i["job"]) in args + ], + ) + ) + ) + ret = {"jobs": {"removed": opts, "tag": None}} # Shim to produce output similar to what __virtual__() should do # but __salt__ isn't available in __virtual__() - output = _cmd('at', '-d', ' '.join(opts)) + output = _cmd("at", "-d", " ".join(opts)) if output is None: - return '\'at.atrm\' is not available.' + return "'at.atrm' is not available." return ret def at(*args, **kwargs): # pylint: disable=C0103 - ''' + """ Add a job to the queue. The 'timespec' follows the format documented in the @@ -221,50 +259,50 @@ def at(*args, **kwargs): # pylint: disable=C0103 salt '*' at.at [tag=] [runas=] salt '*' at.at 12:05am '/sbin/reboot' tag=reboot salt '*' at.at '3:05am +3 days' 'bin/myscript' tag=nightly runas=jim - ''' + """ if len(args) < 2: - return {'jobs': []} + return {"jobs": []} # Shim to produce output similar to what __virtual__() should do # but __salt__ isn't available in __virtual__() - binary = salt.utils.path.which('at') + binary = salt.utils.path.which("at") if not binary: - return '\'at.at\' is not available.' + return "'at.at' is not available." - if 'tag' in kwargs: - stdin = '### SALT: {0}\n{1}'.format(kwargs['tag'], ' '.join(args[1:])) + if "tag" in kwargs: + stdin = "### SALT: {0}\n{1}".format(kwargs["tag"], " ".join(args[1:])) else: - stdin = ' '.join(args[1:]) + stdin = " ".join(args[1:]) cmd = [binary, args[0]] - cmd_kwargs = {'stdin': stdin, 'python_shell': False} - if 'runas' in kwargs: - cmd_kwargs['runas'] = kwargs['runas'] - output = __salt__['cmd.run'](cmd, **cmd_kwargs) + cmd_kwargs = {"stdin": stdin, "python_shell": False} + if "runas" in kwargs: + cmd_kwargs["runas"] = kwargs["runas"] + output = __salt__["cmd.run"](cmd, **cmd_kwargs) if output is None: - return '\'at.at\' is not available.' + return "'at.at' is not available." - if output.endswith('Garbled time'): - return {'jobs': [], 'error': 'invalid timespec'} + if output.endswith("Garbled time"): + return {"jobs": [], "error": "invalid timespec"} - if output.startswith('warning: commands'): + if output.startswith("warning: commands"): output = output.splitlines()[1] - if output.startswith('commands will be executed'): + if output.startswith("commands will be executed"): output = output.splitlines()[1] output = output.split()[1] - if __grains__['os'] in BSD: + if __grains__["os"] in BSD: return atq(six.text_type(output)) else: return atq(int(output)) def atc(jobid): - ''' + """ Print the at(1) script that will run for the passed job id. This is mostly for debugging so the output will just be text. @@ -274,87 +312,87 @@ def atc(jobid): .. code-block:: bash salt '*' at.atc - ''' + """ # Shim to produce output similar to what __virtual__() should do # but __salt__ isn't available in __virtual__() - output = _cmd('at', '-c', six.text_type(jobid)) + output = _cmd("at", "-c", six.text_type(jobid)) if output is None: - return '\'at.atc\' is not available.' - elif output == '': - return {'error': 'invalid job id \'{0}\''.format(jobid)} + return "'at.atc' is not available." + elif output == "": + return {"error": "invalid job id '{0}'".format(jobid)} return output def _atq(**kwargs): - ''' + """ Return match jobs list - ''' + """ jobs = [] - runas = kwargs.get('runas', None) - tag = kwargs.get('tag', None) - hour = kwargs.get('hour', None) - minute = kwargs.get('minute', None) - day = kwargs.get('day', None) - month = kwargs.get('month', None) - year = kwargs.get('year', None) + runas = kwargs.get("runas", None) + tag = kwargs.get("tag", None) + hour = kwargs.get("hour", None) + minute = kwargs.get("minute", None) + day = kwargs.get("day", None) + month = kwargs.get("month", None) + year = kwargs.get("year", None) if year and len(six.text_type(year)) == 2: - year = '20{0}'.format(year) + year = "20{0}".format(year) - jobinfo = atq()['jobs'] + jobinfo = atq()["jobs"] if not jobinfo: - return {'jobs': jobs} + return {"jobs": jobs} for job in jobinfo: if not runas: pass - elif runas == job['user']: + elif runas == job["user"]: pass else: continue if not tag: pass - elif tag == job['tag']: + elif tag == job["tag"]: pass else: continue if not hour: pass - elif '{0:02d}'.format(int(hour)) == job['time'].split(':')[0]: + elif "{0:02d}".format(int(hour)) == job["time"].split(":")[0]: pass else: continue if not minute: pass - elif '{0:02d}'.format(int(minute)) == job['time'].split(':')[1]: + elif "{0:02d}".format(int(minute)) == job["time"].split(":")[1]: pass else: continue if not day: pass - elif '{0:02d}'.format(int(day)) == job['date'].split('-')[2]: + elif "{0:02d}".format(int(day)) == job["date"].split("-")[2]: pass else: continue if not month: pass - elif '{0:02d}'.format(int(month)) == job['date'].split('-')[1]: + elif "{0:02d}".format(int(month)) == job["date"].split("-")[1]: pass else: continue if not year: pass - elif year == job['date'].split('-')[0]: + elif year == job["date"].split("-")[0]: pass else: continue @@ -362,14 +400,14 @@ def _atq(**kwargs): jobs.append(job) if not jobs: - note = 'No match jobs or time format error' - return {'jobs': jobs, 'note': note} + note = "No match jobs or time format error" + return {"jobs": jobs, "note": note} - return {'jobs': jobs} + return {"jobs": jobs} def jobcheck(**kwargs): - ''' + """ Check the job from queue. The kwargs dict include 'hour minute day month year tag runas' Other parameters will be ignored. @@ -380,9 +418,9 @@ def jobcheck(**kwargs): salt '*' at.jobcheck runas=jam day=13 salt '*' at.jobcheck day=13 month=12 year=13 tag=rose - ''' + """ if not kwargs: - return {'error': 'You have given a condition'} + return {"error": "You have given a condition"} return _atq(**kwargs) diff --git a/salt/modules/at_solaris.py b/salt/modules/at_solaris.py index 30afef8c07c..86059b35e07 100644 --- a/salt/modules/at_solaris.py +++ b/salt/modules/at_solaris.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Wrapper for at(1) on Solaris-like systems .. note:: @@ -11,45 +11,48 @@ Wrapper for at(1) on Solaris-like systems :platform: solaris,illumos,smartso .. versionadded:: 2017.7.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import datetime +import logging + # Import python libs import re import time -import datetime -import logging - -# Import 3rd-party libs -# pylint: disable=import-error,redefined-builtin -from salt.ext.six.moves import map -from salt.ext import six # Import salt libs import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils +from salt.ext import six + +# Import 3rd-party libs +# pylint: disable=import-error,redefined-builtin +from salt.ext.six.moves import map log = logging.getLogger(__name__) -__virtualname__ = 'at' +__virtualname__ = "at" def __virtual__(): - ''' + """ We only deal with Solaris' specific version of at - ''' + """ if not salt.utils.platform.is_sunos(): - return (False, 'The at module could not be loaded: unsupported platform') - if not salt.utils.path.which('at') or \ - not salt.utils.path.which('atq') or \ - not salt.utils.path.which('atrm'): - return (False, 'The at module could not be loaded: at command not found') + return (False, "The at module could not be loaded: unsupported platform") + if ( + not salt.utils.path.which("at") + or not salt.utils.path.which("atq") + or not salt.utils.path.which("atrm") + ): + return (False, "The at module could not be loaded: at command not found") return __virtualname__ def atq(tag=None): - ''' + """ List all queued and running jobs or only those with an optional 'tag'. @@ -60,39 +63,41 @@ def atq(tag=None): salt '*' at.atq salt '*' at.atq [tag] salt '*' at.atq [job number] - ''' + """ jobs = [] - res = __salt__['cmd.run_all']('atq') + res = __salt__["cmd.run_all"]("atq") - if res['retcode'] > 0: - return {'error': res['stderr']} + if res["retcode"] > 0: + return {"error": res["stderr"]} # No jobs so return - if res['stdout'] == 'no files in queue.': - return {'jobs': jobs} + if res["stdout"] == "no files in queue.": + return {"jobs": jobs} # Jobs created with at.at() will use the following # comment to denote a tagged job. - job_kw_regex = re.compile(r'^### SALT: (\w+)') + job_kw_regex = re.compile(r"^### SALT: (\w+)") # Split each job into a dictionary and handle # pulling out tags or only listing jobs with a certain # tag - for line in res['stdout'].splitlines(): - job_tag = '' + for line in res["stdout"].splitlines(): + job_tag = "" # skip header - if line.startswith(' Rank'): + if line.startswith(" Rank"): continue # parse job output tmp = line.split() - timestr = ' '.join(tmp[1:5]) + timestr = " ".join(tmp[1:5]) job = tmp[6] - specs = datetime.datetime( - *(time.strptime(timestr, '%b %d, %Y %H:%M')[0:5]) - ).isoformat().split('T') + specs = ( + datetime.datetime(*(time.strptime(timestr, "%b %d, %Y %H:%M")[0:5])) + .isoformat() + .split("T") + ) specs.append(tmp[7]) specs.append(tmp[5]) @@ -100,11 +105,9 @@ def atq(tag=None): job = six.text_type(job) # search for any tags - atjob_file = '/var/spool/cron/atjobs/{job}'.format( - job=job - ) - if __salt__['file.file_exists'](atjob_file): - with salt.utils.files.fopen(atjob_file, 'r') as atjob: + atjob_file = "/var/spool/cron/atjobs/{job}".format(job=job) + if __salt__["file.file_exists"](atjob_file): + with salt.utils.files.fopen(atjob_file, "r") as atjob: for line in atjob: line = salt.utils.stringutils.to_unicode(line) tmp = job_kw_regex.match(line) @@ -113,17 +116,33 @@ def atq(tag=None): # filter on tags if not tag: - jobs.append({'job': job, 'date': specs[0], 'time': specs[1], - 'queue': specs[2], 'user': specs[3], 'tag': job_tag}) + jobs.append( + { + "job": job, + "date": specs[0], + "time": specs[1], + "queue": specs[2], + "user": specs[3], + "tag": job_tag, + } + ) elif tag and tag in [job_tag, job]: - jobs.append({'job': job, 'date': specs[0], 'time': specs[1], - 'queue': specs[2], 'user': specs[3], 'tag': job_tag}) + jobs.append( + { + "job": job, + "date": specs[0], + "time": specs[1], + "queue": specs[2], + "user": specs[3], + "tag": job_tag, + } + ) - return {'jobs': jobs} + return {"jobs": jobs} def atrm(*args): - ''' + """ Remove jobs from the queue. CLI Example: @@ -133,43 +152,42 @@ def atrm(*args): salt '*' at.atrm .. salt '*' at.atrm all salt '*' at.atrm all [tag] - ''' + """ if not args: - return {'jobs': {'removed': [], 'tag': None}} + return {"jobs": {"removed": [], "tag": None}} - if args[0] == 'all': + if args[0] == "all": if len(args) > 1: - opts = list(list(map(str, [j['job'] for j in atq(args[1])['jobs']]))) - ret = {'jobs': {'removed': opts, 'tag': args[1]}} + opts = list(list(map(str, [j["job"] for j in atq(args[1])["jobs"]]))) + ret = {"jobs": {"removed": opts, "tag": args[1]}} else: - opts = list(list(map(str, [j['job'] for j in atq()['jobs']]))) - ret = {'jobs': {'removed': opts, 'tag': None}} + opts = list(list(map(str, [j["job"] for j in atq()["jobs"]]))) + ret = {"jobs": {"removed": opts, "tag": None}} else: - opts = list(list(map(str, [i['job'] for i in atq()['jobs'] - if i['job'] in args]))) - ret = {'jobs': {'removed': opts, 'tag': None}} + opts = list( + list(map(str, [i["job"] for i in atq()["jobs"] if i["job"] in args])) + ) + ret = {"jobs": {"removed": opts, "tag": None}} # call atrm for each job in ret['jobs']['removed'] - for job in ret['jobs']['removed']: - res_job = __salt__['cmd.run_all']('atrm {job}'.format( - job=job - )) - if res_job['retcode'] > 0: - if 'failed' not in ret['jobs']: - ret['jobs']['failed'] = {} - ret['jobs']['failed'][job] = res_job['stderr'] + for job in ret["jobs"]["removed"]: + res_job = __salt__["cmd.run_all"]("atrm {job}".format(job=job)) + if res_job["retcode"] > 0: + if "failed" not in ret["jobs"]: + ret["jobs"]["failed"] = {} + ret["jobs"]["failed"][job] = res_job["stderr"] # remove failed from list - if 'failed' in ret['jobs']: - for job in ret['jobs']['failed']: - ret['jobs']['removed'].remove(job) + if "failed" in ret["jobs"]: + for job in ret["jobs"]["failed"]: + ret["jobs"]["removed"].remove(job) return ret def at(*args, **kwargs): # pylint: disable=C0103 - ''' + """ Add a job to the queue. The 'timespec' follows the format documented in the @@ -182,38 +200,38 @@ def at(*args, **kwargs): # pylint: disable=C0103 salt '*' at.at [tag=] [runas=] salt '*' at.at 12:05am '/sbin/reboot' tag=reboot salt '*' at.at '3:05am +3 days' 'bin/myscript' tag=nightly runas=jim - ''' + """ # check args if len(args) < 2: - return {'jobs': []} + return {"jobs": []} # build job - if 'tag' in kwargs: - stdin = '### SALT: {0}\n{1}'.format(kwargs['tag'], ' '.join(args[1:])) + if "tag" in kwargs: + stdin = "### SALT: {0}\n{1}".format(kwargs["tag"], " ".join(args[1:])) else: - stdin = ' '.join(args[1:]) + stdin = " ".join(args[1:]) - cmd_kwargs = {'stdin': stdin, 'python_shell': False} - if 'runas' in kwargs: - cmd_kwargs['runas'] = kwargs['runas'] - res = __salt__['cmd.run_all']('at "{timespec}"'.format( - timespec=args[0] - ), **cmd_kwargs) + cmd_kwargs = {"stdin": stdin, "python_shell": False} + if "runas" in kwargs: + cmd_kwargs["runas"] = kwargs["runas"] + res = __salt__["cmd.run_all"]( + 'at "{timespec}"'.format(timespec=args[0]), **cmd_kwargs + ) # verify job creation - if res['retcode'] > 0: - if 'bad time specification' in res['stderr']: - return {'jobs': [], 'error': 'invalid timespec'} - return {'jobs': [], 'error': res['stderr']} + if res["retcode"] > 0: + if "bad time specification" in res["stderr"]: + return {"jobs": [], "error": "invalid timespec"} + return {"jobs": [], "error": res["stderr"]} else: - jobid = res['stderr'].splitlines()[1] + jobid = res["stderr"].splitlines()[1] jobid = six.text_type(jobid.split()[1]) return atq(jobid) def atc(jobid): - ''' + """ Print the at(1) script that will run for the passed job id. This is mostly for debugging so the output will just be text. @@ -223,87 +241,86 @@ def atc(jobid): .. code-block:: bash salt '*' at.atc - ''' + """ - atjob_file = '/var/spool/cron/atjobs/{job}'.format( - job=jobid - ) - if __salt__['file.file_exists'](atjob_file): - with salt.utils.files.fopen(atjob_file, 'r') as rfh: - return ''.join([salt.utils.stringutils.to_unicode(x) - for x in rfh.readlines()]) + atjob_file = "/var/spool/cron/atjobs/{job}".format(job=jobid) + if __salt__["file.file_exists"](atjob_file): + with salt.utils.files.fopen(atjob_file, "r") as rfh: + return "".join( + [salt.utils.stringutils.to_unicode(x) for x in rfh.readlines()] + ) else: - return {'error': 'invalid job id \'{0}\''.format(jobid)} + return {"error": "invalid job id '{0}'".format(jobid)} def _atq(**kwargs): - ''' + """ Return match jobs list - ''' + """ jobs = [] - runas = kwargs.get('runas', None) - tag = kwargs.get('tag', None) - hour = kwargs.get('hour', None) - minute = kwargs.get('minute', None) - day = kwargs.get('day', None) - month = kwargs.get('month', None) - year = kwargs.get('year', None) + runas = kwargs.get("runas", None) + tag = kwargs.get("tag", None) + hour = kwargs.get("hour", None) + minute = kwargs.get("minute", None) + day = kwargs.get("day", None) + month = kwargs.get("month", None) + year = kwargs.get("year", None) if year and len(six.text_type(year)) == 2: - year = '20{0}'.format(year) + year = "20{0}".format(year) - jobinfo = atq()['jobs'] + jobinfo = atq()["jobs"] if not jobinfo: - return {'jobs': jobs} + return {"jobs": jobs} for job in jobinfo: if not runas: pass - elif runas == job['user']: + elif runas == job["user"]: pass else: continue if not tag: pass - elif tag == job['tag']: + elif tag == job["tag"]: pass else: continue if not hour: pass - elif '{0:02d}'.format(int(hour)) == job['time'].split(':')[0]: + elif "{0:02d}".format(int(hour)) == job["time"].split(":")[0]: pass else: continue if not minute: pass - elif '{0:02d}'.format(int(minute)) == job['time'].split(':')[1]: + elif "{0:02d}".format(int(minute)) == job["time"].split(":")[1]: pass else: continue if not day: pass - elif '{0:02d}'.format(int(day)) == job['date'].split('-')[2]: + elif "{0:02d}".format(int(day)) == job["date"].split("-")[2]: pass else: continue if not month: pass - elif '{0:02d}'.format(int(month)) == job['date'].split('-')[1]: + elif "{0:02d}".format(int(month)) == job["date"].split("-")[1]: pass else: continue if not year: pass - elif year == job['date'].split('-')[0]: + elif year == job["date"].split("-")[0]: pass else: continue @@ -311,14 +328,14 @@ def _atq(**kwargs): jobs.append(job) if not jobs: - note = 'No match jobs or time format error' - return {'jobs': jobs, 'note': note} + note = "No match jobs or time format error" + return {"jobs": jobs, "note": note} - return {'jobs': jobs} + return {"jobs": jobs} def jobcheck(**kwargs): - ''' + """ Check the job from queue. The kwargs dict include 'hour minute day month year tag runas' Other parameters will be ignored. @@ -329,10 +346,10 @@ def jobcheck(**kwargs): salt '*' at.jobcheck runas=jam day=13 salt '*' at.jobcheck day=13 month=12 year=13 tag=rose - ''' + """ if not kwargs: - return {'error': 'You have given a condition'} + return {"error": "You have given a condition"} return _atq(**kwargs) diff --git a/salt/modules/augeas_cfg.py b/salt/modules/augeas_cfg.py index dd679816b20..5b5feafd2e7 100644 --- a/salt/modules/augeas_cfg.py +++ b/salt/modules/augeas_cfg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manages configuration files via augeas This module requires the ``augeas`` Python module. @@ -22,110 +22,113 @@ This module requires the ``augeas`` Python module. For affected Debian/Ubuntu hosts, installing ``libpython2.7`` has been known to resolve the issue. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os import re -import logging -from salt.ext.six.moves import zip -from salt.ext import six - -# Make sure augeas python interface is installed -HAS_AUGEAS = False -try: - from augeas import Augeas as _Augeas # pylint: disable=no-name-in-module - HAS_AUGEAS = True -except ImportError: - pass # Import salt libs import salt.utils.args import salt.utils.data import salt.utils.stringutils from salt.exceptions import SaltInvocationError +from salt.ext import six +from salt.ext.six.moves import zip + +# Make sure augeas python interface is installed +HAS_AUGEAS = False +try: + from augeas import Augeas as _Augeas # pylint: disable=no-name-in-module + + HAS_AUGEAS = True +except ImportError: + pass + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'augeas' +__virtualname__ = "augeas" METHOD_MAP = { - 'set': 'set', - 'setm': 'setm', - 'mv': 'move', - 'move': 'move', - 'ins': 'insert', - 'insert': 'insert', - 'rm': 'remove', - 'remove': 'remove', + "set": "set", + "setm": "setm", + "mv": "move", + "move": "move", + "ins": "insert", + "insert": "insert", + "rm": "remove", + "remove": "remove", } def __virtual__(): - ''' + """ Only run this module if the augeas python module is installed - ''' + """ if HAS_AUGEAS: return __virtualname__ - return (False, 'Cannot load augeas_cfg module: augeas python module not installed') + return (False, "Cannot load augeas_cfg module: augeas python module not installed") def _recurmatch(path, aug): - ''' + """ Recursive generator providing the infrastructure for augtools print behavior. This function is based on test_augeas.py from Harald Hoyer in the python-augeas repository - ''' + """ if path: - clean_path = path.rstrip('/*') + clean_path = path.rstrip("/*") yield (clean_path, aug.get(path)) - for i in aug.match(clean_path + '/*'): - i = i.replace('!', '\\!') # escape some dirs + for i in aug.match(clean_path + "/*"): + i = i.replace("!", "\\!") # escape some dirs for _match in _recurmatch(i, aug): yield _match def _lstrip_word(word, prefix): - ''' + """ Return a copy of the string after the specified prefix was removed from the beginning of the string - ''' + """ if six.text_type(word).startswith(prefix): - return six.text_type(word)[len(prefix):] + return six.text_type(word)[len(prefix) :] return word def _check_load_paths(load_path): - ''' + """ Checks the validity of the load_path, returns a sanitized version with invalid paths removed. - ''' + """ if load_path is None or not isinstance(load_path, six.string_types): return None _paths = [] - for _path in load_path.split(':'): + for _path in load_path.split(":"): if os.path.isabs(_path) and os.path.isdir(_path): _paths.append(_path) else: - log.info('Invalid augeas_cfg load_path entry: %s removed', _path) + log.info("Invalid augeas_cfg load_path entry: %s removed", _path) if len(_paths) == 0: return None - return ':'.join(_paths) + return ":".join(_paths) def execute(context=None, lens=None, commands=(), load_path=None): - ''' + """ Execute Augeas commands .. versionadded:: 2014.7.0 @@ -152,29 +155,29 @@ def execute(context=None, lens=None, commands=(), load_path=None): A colon-spearated list of directories that modules should be searched in. This is in addition to the standard load path and the directories in AUGEAS_LENS_LIB. - ''' - ret = {'retval': False} + """ + ret = {"retval": False} arg_map = { - 'set': (1, 2), - 'setm': (2, 3), - 'move': (2,), - 'insert': (3,), - 'remove': (1,), + "set": (1, 2), + "setm": (2, 3), + "move": (2,), + "insert": (3,), + "remove": (1,), } def make_path(path): - ''' + """ Return correct path - ''' + """ if not context: return path - if path.lstrip('/'): + if path.lstrip("/"): if path.startswith(context): return path - path = path.lstrip('/') + path = path.lstrip("/") return os.path.join(context, path) else: return context @@ -185,17 +188,17 @@ def execute(context=None, lens=None, commands=(), load_path=None): aug = _Augeas(flags=flags, loadpath=load_path) if lens and context: - aug.add_transform(lens, re.sub('^/files', '', context)) + aug.add_transform(lens, re.sub("^/files", "", context)) aug.load() for command in commands: try: # first part up to space is always the # command name (i.e.: set, move) - cmd, arg = command.split(' ', 1) + cmd, arg = command.split(" ", 1) if cmd not in METHOD_MAP: - ret['error'] = 'Command {0} is not supported (yet)'.format(cmd) + ret["error"] = "Command {0} is not supported (yet)".format(cmd) return ret method = METHOD_MAP[cmd] @@ -204,65 +207,67 @@ def execute(context=None, lens=None, commands=(), load_path=None): parts = salt.utils.args.shlex_split(arg) if len(parts) not in nargs: - err = '{0} takes {1} args: {2}'.format(method, nargs, parts) + err = "{0} takes {1} args: {2}".format(method, nargs, parts) raise ValueError(err) - if method == 'set': + if method == "set": path = make_path(parts[0]) value = parts[1] if len(parts) == 2 else None - args = {'path': path, 'value': value} - elif method == 'setm': + args = {"path": path, "value": value} + elif method == "setm": base = make_path(parts[0]) sub = parts[1] value = parts[2] if len(parts) == 3 else None - args = {'base': base, 'sub': sub, 'value': value} - elif method == 'move': + args = {"base": base, "sub": sub, "value": value} + elif method == "move": path = make_path(parts[0]) dst = parts[1] - args = {'src': path, 'dst': dst} - elif method == 'insert': + args = {"src": path, "dst": dst} + elif method == "insert": label, where, path = parts - if where not in ('before', 'after'): + if where not in ("before", "after"): raise ValueError( - 'Expected "before" or "after", not {0}'.format(where)) + 'Expected "before" or "after", not {0}'.format(where) + ) path = make_path(path) - args = { - 'path': path, - 'label': label, - 'before': where == 'before'} - elif method == 'remove': + args = {"path": path, "label": label, "before": where == "before"} + elif method == "remove": path = make_path(parts[0]) - args = {'path': path} + args = {"path": path} except ValueError as err: log.error(err) # if command.split fails arg will not be set - if 'arg' not in locals(): + if "arg" not in locals(): arg = command - ret['error'] = 'Invalid formatted command, ' \ - 'see debug log for details: {0}'.format(arg) + ret["error"] = ( + "Invalid formatted command, " + "see debug log for details: {0}".format(arg) + ) return ret args = salt.utils.data.decode(args, to_str=True) - log.debug('%s: %s', method, args) + log.debug("%s: %s", method, args) func = getattr(aug, method) func(**args) try: aug.save() - ret['retval'] = True + ret["retval"] = True except IOError as err: - ret['error'] = six.text_type(err) + ret["error"] = six.text_type(err) - if lens and not lens.endswith('.lns'): - ret['error'] += '\nLenses are normally configured as "name.lns". ' \ - 'Did you mean "{0}.lns"?'.format(lens) + if lens and not lens.endswith(".lns"): + ret["error"] += ( + '\nLenses are normally configured as "name.lns". ' + 'Did you mean "{0}.lns"?'.format(lens) + ) aug.close() return ret -def get(path, value='', load_path=None): - ''' +def get(path, value="", load_path=None): + """ Get a value for a specific augeas path CLI Example: @@ -283,31 +288,31 @@ def get(path, value='', load_path=None): A colon-spearated list of directories that modules should be searched in. This is in addition to the standard load path and the directories in AUGEAS_LENS_LIB. - ''' + """ load_path = _check_load_paths(load_path) aug = _Augeas(loadpath=load_path) ret = {} - path = path.rstrip('/') + path = path.rstrip("/") if value: - path += '/{0}'.format(value.strip('/')) + path += "/{0}".format(value.strip("/")) try: _match = aug.match(path) except RuntimeError as err: - return {'error': six.text_type(err)} + return {"error": six.text_type(err)} if _match: ret[path] = aug.get(path) else: - ret[path] = '' # node does not exist + ret[path] = "" # node does not exist return ret def setvalue(*args): - ''' + """ Set a value for a specific augeas path CLI Example: @@ -342,57 +347,55 @@ def setvalue(*args): Ensures that the following line is present in /etc/sudoers:: %wheel ALL = PASSWD : ALL , NOPASSWD : /usr/bin/apt-get , /usr/bin/aptitude - ''' + """ load_path = None - load_paths = [x for x in args if six.text_type(x).startswith('load_path=')] + load_paths = [x for x in args if six.text_type(x).startswith("load_path=")] if load_paths: if len(load_paths) > 1: - raise SaltInvocationError( - 'Only one \'load_path=\' value is permitted' - ) + raise SaltInvocationError("Only one 'load_path=' value is permitted") else: - load_path = load_paths[0].split('=', 1)[1] + load_path = load_paths[0].split("=", 1)[1] load_path = _check_load_paths(load_path) aug = _Augeas(loadpath=load_path) - ret = {'retval': False} + ret = {"retval": False} tuples = [ - x for x in args - if not six.text_type(x).startswith('prefix=') and - not six.text_type(x).startswith('load_path=')] - prefix = [x for x in args if six.text_type(x).startswith('prefix=')] + x + for x in args + if not six.text_type(x).startswith("prefix=") + and not six.text_type(x).startswith("load_path=") + ] + prefix = [x for x in args if six.text_type(x).startswith("prefix=")] if prefix: if len(prefix) > 1: - raise SaltInvocationError( - 'Only one \'prefix=\' value is permitted' - ) + raise SaltInvocationError("Only one 'prefix=' value is permitted") else: - prefix = prefix[0].split('=', 1)[1] + prefix = prefix[0].split("=", 1)[1] if len(tuples) % 2 != 0: - raise SaltInvocationError('Uneven number of path/value arguments') + raise SaltInvocationError("Uneven number of path/value arguments") tuple_iter = iter(tuples) for path, value in zip(tuple_iter, tuple_iter): target_path = path if prefix: - target_path = os.path.join(prefix.rstrip('/'), path.lstrip('/')) + target_path = os.path.join(prefix.rstrip("/"), path.lstrip("/")) try: aug.set(target_path, six.text_type(value)) except ValueError as err: - ret['error'] = 'Multiple values: {0}'.format(err) + ret["error"] = "Multiple values: {0}".format(err) try: aug.save() - ret['retval'] = True + ret["retval"] = True except IOError as err: - ret['error'] = six.text_type(err) + ret["error"] = six.text_type(err) return ret -def match(path, value='', load_path=None): - ''' +def match(path, value="", load_path=None): + """ Get matches for path expression CLI Example: @@ -413,7 +416,7 @@ def match(path, value='', load_path=None): A colon-spearated list of directories that modules should be searched in. This is in addition to the standard load path and the directories in AUGEAS_LENS_LIB. - ''' + """ load_path = _check_load_paths(load_path) aug = _Augeas(loadpath=load_path) @@ -433,7 +436,7 @@ def match(path, value='', load_path=None): def remove(path, load_path=None): - ''' + """ Get matches for path expression CLI Example: @@ -452,28 +455,28 @@ def remove(path, load_path=None): A colon-spearated list of directories that modules should be searched in. This is in addition to the standard load path and the directories in AUGEAS_LENS_LIB. - ''' + """ load_path = _check_load_paths(load_path) aug = _Augeas(loadpath=load_path) - ret = {'retval': False} + ret = {"retval": False} try: count = aug.remove(path) aug.save() if count == -1: - ret['error'] = 'Invalid node' + ret["error"] = "Invalid node" else: - ret['retval'] = True + ret["retval"] = True except (RuntimeError, IOError) as err: - ret['error'] = six.text_type(err) + ret["error"] = six.text_type(err) - ret['count'] = count + ret["count"] = count return ret def ls(path, load_path=None): # pylint: disable=C0103 - ''' + """ List the direct children of a node CLI Example: @@ -491,9 +494,10 @@ def ls(path, load_path=None): # pylint: disable=C0103 A colon-spearated list of directories that modules should be searched in. This is in addition to the standard load path and the directories in AUGEAS_LENS_LIB. - ''' + """ + def _match(path): - ''' Internal match function ''' + """ Internal match function """ try: matches = aug.match(salt.utils.stringutils.to_str(path)) except RuntimeError: @@ -508,23 +512,23 @@ def ls(path, load_path=None): # pylint: disable=C0103 aug = _Augeas(loadpath=load_path) - path = path.rstrip('/') + '/' - match_path = path + '*' + path = path.rstrip("/") + "/" + match_path = path + "*" matches = _match(match_path) ret = {} for key, value in six.iteritems(matches): name = _lstrip_word(key, path) - if _match(key + '/*'): - ret[name + '/'] = value # has sub nodes, e.g. directory + if _match(key + "/*"): + ret[name + "/"] = value # has sub nodes, e.g. directory else: ret[name] = value return ret def tree(path, load_path=None): - ''' + """ Returns recursively the complete tree of a node CLI Example: @@ -542,11 +546,11 @@ def tree(path, load_path=None): A colon-spearated list of directories that modules should be searched in. This is in addition to the standard load path and the directories in AUGEAS_LENS_LIB. - ''' + """ load_path = _check_load_paths(load_path) aug = _Augeas(loadpath=load_path) - path = path.rstrip('/') + '/' + path = path.rstrip("/") + "/" match_path = path return dict([i for i in _recurmatch(match_path, aug)]) diff --git a/salt/modules/aws_sqs.py b/salt/modules/aws_sqs.py index 6204921e890..a6c6d351f32 100644 --- a/salt/modules/aws_sqs.py +++ b/salt/modules/aws_sqs.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Support for the Amazon Simple Queue Service. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -14,25 +15,25 @@ from salt.ext import six log = logging.getLogger(__name__) -_OUTPUT = '--output json' +_OUTPUT = "--output json" def __virtual__(): - if salt.utils.path.which('aws'): + if salt.utils.path.which("aws"): # awscli is installed, load the module return True - return (False, 'The module aws_sqs could not be loaded: aws command not found') + return (False, "The module aws_sqs could not be loaded: aws command not found") def _region(region): - ''' + """ Return the region argument. - ''' - return ' --region {r}'.format(r=region) + """ + return " --region {r}".format(r=region) def _run_aws(cmd, region, opts, user, **kwargs): - ''' + """ Runs the given command against AWS. cmd Command to run @@ -44,32 +45,29 @@ def _run_aws(cmd, region, opts, user, **kwargs): Pass in from salt kwargs Key-value arguments to pass to the command - ''' + """ # These args need a specific key value that aren't # valid python parameter keys - receipthandle = kwargs.pop('receipthandle', None) + receipthandle = kwargs.pop("receipthandle", None) if receipthandle: - kwargs['receipt-handle'] = receipthandle - num = kwargs.pop('num', None) + kwargs["receipt-handle"] = receipthandle + num = kwargs.pop("num", None) if num: - kwargs['max-number-of-messages'] = num + kwargs["max-number-of-messages"] = num - _formatted_args = [ - '--{0} "{1}"'.format(k, v) for k, v in six.iteritems(kwargs)] + _formatted_args = ['--{0} "{1}"'.format(k, v) for k, v in six.iteritems(kwargs)] - cmd = 'aws sqs {cmd} {args} {region} {out}'.format( - cmd=cmd, - args=' '.join(_formatted_args), - region=_region(region), - out=_OUTPUT) + cmd = "aws sqs {cmd} {args} {region} {out}".format( + cmd=cmd, args=" ".join(_formatted_args), region=_region(region), out=_OUTPUT + ) - rtn = __salt__['cmd.run'](cmd, runas=user, python_shell=False) + rtn = __salt__["cmd.run"](cmd, runas=user, python_shell=False) - return salt.utils.json.loads(rtn) if rtn else '' + return salt.utils.json.loads(rtn) if rtn else "" def receive_message(queue, region, num=1, opts=None, user=None): - ''' + """ Receive one or more messages from a queue in a region queue @@ -96,24 +94,23 @@ def receive_message(queue, region, num=1, opts=None, user=None): .. versionadded:: 2014.7.0 - ''' + """ ret = { - 'Messages': None, - } + "Messages": None, + } queues = list_queues(region, opts, user) url_map = _parse_queue_list(queues) if queue not in url_map: log.info('"%s" queue does not exist.', queue) return ret - out = _run_aws('receive-message', region, opts, user, queue=url_map[queue], - num=num) - ret['Messages'] = out['Messages'] + out = _run_aws("receive-message", region, opts, user, queue=url_map[queue], num=num) + ret["Messages"] = out["Messages"] return ret def delete_message(queue, region, receipthandle, opts=None, user=None): - ''' + """ Delete one or more messages from a queue in a region queue @@ -140,20 +137,26 @@ def delete_message(queue, region, receipthandle, opts=None, user=None): .. versionadded:: 2014.7.0 - ''' + """ queues = list_queues(region, opts, user) url_map = _parse_queue_list(queues) if queue not in url_map: log.info('"%s" queue does not exist.', queue) return False - out = _run_aws('delete-message', region, opts, user, - receipthandle=receipthandle, queue=url_map[queue],) + out = _run_aws( + "delete-message", + region, + opts, + user, + receipthandle=receipthandle, + queue=url_map[queue], + ) return True def list_queues(region, opts=None, user=None): - ''' + """ List the queues in the selected region. region @@ -169,18 +172,18 @@ def list_queues(region, opts=None, user=None): salt '*' aws_sqs.list_queues - ''' - out = _run_aws('list-queues', region, opts, user) + """ + out = _run_aws("list-queues", region, opts, user) ret = { - 'retcode': 0, - 'stdout': out['QueueUrls'], + "retcode": 0, + "stdout": out["QueueUrls"], } return ret def create_queue(name, region, opts=None, user=None): - ''' + """ Creates a queue with the correct name. name @@ -199,23 +202,21 @@ def create_queue(name, region, opts=None, user=None): salt '*' aws_sqs.create_queue - ''' + """ - create = {'queue-name': name} - out = _run_aws( - 'create-queue', region=region, opts=opts, - user=user, **create) + create = {"queue-name": name} + out = _run_aws("create-queue", region=region, opts=opts, user=user, **create) ret = { - 'retcode': 0, - 'stdout': out['QueueUrl'], - 'stderr': '', + "retcode": 0, + "stdout": out["QueueUrl"], + "stderr": "", } return ret def delete_queue(name, region, opts=None, user=None): - ''' + """ Deletes a queue in the region. name @@ -233,40 +234,35 @@ def delete_queue(name, region, opts=None, user=None): salt '*' aws_sqs.delete_queue - ''' + """ queues = list_queues(region, opts, user) url_map = _parse_queue_list(queues) logger = logging.getLogger(__name__) - logger.debug('map %s', six.text_type(url_map)) + logger.debug("map %s", six.text_type(url_map)) if name in url_map: - delete = {'queue-url': url_map[name]} + delete = {"queue-url": url_map[name]} - rtn = _run_aws( - 'delete-queue', - region=region, - opts=opts, - user=user, - **delete) + rtn = _run_aws("delete-queue", region=region, opts=opts, user=user, **delete) success = True - err = '' - out = '{0} deleted'.format(name) + err = "" + out = "{0} deleted".format(name) else: - out = '' + out = "" err = "Delete failed" success = False ret = { - 'retcode': 0 if success else 1, - 'stdout': out, - 'stderr': err, + "retcode": 0 if success else 1, + "stdout": out, + "stderr": err, } return ret def queue_exists(name, region, opts=None, user=None): - ''' + """ Returns True or False on whether the queue exists in the region name @@ -285,15 +281,15 @@ def queue_exists(name, region, opts=None, user=None): salt '*' aws_sqs.queue_exists - ''' + """ output = list_queues(region, opts, user) return name in _parse_queue_list(output) def _parse_queue_list(list_output): - ''' + """ Parse the queue to get a dict of name -> URL - ''' - queues = dict((q.split('/')[-1], q) for q in list_output['stdout']) + """ + queues = dict((q.split("/")[-1], q) for q in list_output["stdout"]) return queues diff --git a/salt/modules/azurearm_compute.py b/salt/modules/azurearm_compute.py index 79c86d9e495..f66fac2c506 100644 --- a/salt/modules/azurearm_compute.py +++ b/salt/modules/azurearm_compute.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure (ARM) Compute Execution Module .. versionadded:: 2019.2.0 @@ -44,10 +44,11 @@ Azure (ARM) Compute Execution Module * ``AZURE_US_GOV_CLOUD`` * ``AZURE_GERMAN_CLOUD`` -''' +""" # Python libs from __future__ import absolute_import + import logging # Azure libs @@ -56,11 +57,12 @@ try: import azure.mgmt.compute.models # pylint: disable=unused-import from msrest.exceptions import SerializationError from msrestazure.azure_exceptions import CloudError + HAS_LIBS = True except ImportError: pass -__virtualname__ = 'azurearm_compute' +__virtualname__ = "azurearm_compute" log = logging.getLogger(__name__) @@ -69,16 +71,18 @@ def __virtual__(): if not HAS_LIBS: return ( False, - 'The following dependencies are required to use the AzureARM modules: ' - 'Microsoft Azure SDK for Python >= 2.0rc6, ' - 'MS REST Azure (msrestazure) >= 0.4' + "The following dependencies are required to use the AzureARM modules: " + "Microsoft Azure SDK for Python >= 2.0rc6, " + "MS REST Azure (msrestazure) >= 0.4", ) return __virtualname__ -def availability_set_create_or_update(name, resource_group, **kwargs): # pylint: disable=invalid-name - ''' +def availability_set_create_or_update( + name, resource_group, **kwargs +): # pylint: disable=invalid-name + """ .. versionadded:: 2019.2.0 Create or update an availability set. @@ -94,59 +98,61 @@ def availability_set_create_or_update(name, resource_group, **kwargs): # pylint salt-call azurearm_compute.availability_set_create_or_update testset testgroup - ''' - if 'location' not in kwargs: - rg_props = __salt__['azurearm_resource.resource_group_get']( + """ + if "location" not in kwargs: + rg_props = __salt__["azurearm_resource.resource_group_get"]( resource_group, **kwargs ) - if 'error' in rg_props: - log.error( - 'Unable to determine location from resource group specified.' - ) + if "error" in rg_props: + log.error("Unable to determine location from resource group specified.") return False - kwargs['location'] = rg_props['location'] + kwargs["location"] = rg_props["location"] - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) # Use VM names to link to the IDs of existing VMs. - if isinstance(kwargs.get('virtual_machines'), list): + if isinstance(kwargs.get("virtual_machines"), list): vm_list = [] - for vm_name in kwargs.get('virtual_machines'): - vm_instance = __salt__['azurearm_compute.virtual_machine_get']( - name=vm_name, - resource_group=resource_group, - **kwargs + for vm_name in kwargs.get("virtual_machines"): + vm_instance = __salt__["azurearm_compute.virtual_machine_get"]( + name=vm_name, resource_group=resource_group, **kwargs ) - if 'error' not in vm_instance: - vm_list.append({'id': str(vm_instance['id'])}) - kwargs['virtual_machines'] = vm_list + if "error" not in vm_instance: + vm_list.append({"id": str(vm_instance["id"])}) + kwargs["virtual_machines"] = vm_list try: - setmodel = __utils__['azurearm.create_object_model']('compute', 'AvailabilitySet', **kwargs) + setmodel = __utils__["azurearm.create_object_model"]( + "compute", "AvailabilitySet", **kwargs + ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: av_set = compconn.availability_sets.create_or_update( resource_group_name=resource_group, availability_set_name=name, - parameters=setmodel + parameters=setmodel, ) result = av_set.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def availability_set_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete an availability set. @@ -162,24 +168,23 @@ def availability_set_delete(name, resource_group, **kwargs): salt-call azurearm_compute.availability_set_delete testset testgroup - ''' + """ result = False - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: compconn.availability_sets.delete( - resource_group_name=resource_group, - availability_set_name=name + resource_group_name=resource_group, availability_set_name=name ) result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) return result def availability_set_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get a dictionary representing an availability set's properties. @@ -195,24 +200,23 @@ def availability_set_get(name, resource_group, **kwargs): salt-call azurearm_compute.availability_set_get testset testgroup - ''' - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + """ + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: av_set = compconn.availability_sets.get( - resource_group_name=resource_group, - availability_set_name=name + resource_group_name=resource_group, availability_set_name=name ) result = av_set.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result def availability_sets_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all availability sets within a resource group. @@ -226,27 +230,27 @@ def availability_sets_list(resource_group, **kwargs): salt-call azurearm_compute.availability_sets_list testgroup - ''' + """ result = {} - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: - avail_sets = __utils__['azurearm.paged_object_to_list']( - compconn.availability_sets.list( - resource_group_name=resource_group - ) + avail_sets = __utils__["azurearm.paged_object_to_list"]( + compconn.availability_sets.list(resource_group_name=resource_group) ) for avail_set in avail_sets: - result[avail_set['name']] = avail_set + result[avail_set["name"]] = avail_set except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result -def availability_sets_list_available_sizes(name, resource_group, **kwargs): # pylint: disable=invalid-name - ''' +def availability_sets_list_available_sizes( + name, resource_group, **kwargs +): # pylint: disable=invalid-name + """ .. versionadded:: 2019.2.0 List all available virtual machine sizes that can be used to @@ -264,28 +268,29 @@ def availability_sets_list_available_sizes(name, resource_group, **kwargs): # p salt-call azurearm_compute.availability_sets_list_available_sizes testset testgroup - ''' + """ result = {} - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: - sizes = __utils__['azurearm.paged_object_to_list']( + sizes = __utils__["azurearm.paged_object_to_list"]( compconn.availability_sets.list_available_sizes( - resource_group_name=resource_group, - availability_set_name=name + resource_group_name=resource_group, availability_set_name=name ) ) for size in sizes: - result[size['name']] = size + result[size["name"]] = size except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result -def virtual_machine_capture(name, destination_name, resource_group, prefix='capture-', overwrite=False, **kwargs): - ''' +def virtual_machine_capture( + name, destination_name, resource_group, prefix="capture-", overwrite=False, **kwargs +): + """ .. versionadded:: 2019.2.0 Captures the VM by copying virtual hard disks of the VM and outputs @@ -308,13 +313,13 @@ def virtual_machine_capture(name, destination_name, resource_group, prefix='capt salt-call azurearm_compute.virtual_machine_capture testvm testcontainer testgroup - ''' + """ # pylint: disable=invalid-name VirtualMachineCaptureParameters = getattr( - azure.mgmt.compute.models, 'VirtualMachineCaptureParameters' + azure.mgmt.compute.models, "VirtualMachineCaptureParameters" ) - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: # pylint: disable=invalid-name vm = compconn.virtual_machines.capture( @@ -323,21 +328,21 @@ def virtual_machine_capture(name, destination_name, resource_group, prefix='capt parameters=VirtualMachineCaptureParameters( vhd_prefix=prefix, destination_container_name=destination_name, - overwrite_vhds=overwrite - ) + overwrite_vhds=overwrite, + ), ) vm.wait() vm_result = vm.result() result = vm_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result def virtual_machine_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Retrieves information about the model view or the instance view of a @@ -354,27 +359,27 @@ def virtual_machine_get(name, resource_group, **kwargs): salt-call azurearm_compute.virtual_machine_get testvm testgroup - ''' - expand = kwargs.get('expand') + """ + expand = kwargs.get("expand") - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: # pylint: disable=invalid-name vm = compconn.virtual_machines.get( - resource_group_name=resource_group, - vm_name=name, - expand=expand + resource_group_name=resource_group, vm_name=name, expand=expand ) result = vm.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result -def virtual_machine_convert_to_managed_disks(name, resource_group, **kwargs): # pylint: disable=invalid-name - ''' +def virtual_machine_convert_to_managed_disks( + name, resource_group, **kwargs +): # pylint: disable=invalid-name + """ .. versionadded:: 2019.2.0 Converts virtual machine disks from blob-based to managed disks. Virtual @@ -391,26 +396,25 @@ def virtual_machine_convert_to_managed_disks(name, resource_group, **kwargs): # salt-call azurearm_compute.virtual_machine_convert_to_managed_disks testvm testgroup - ''' - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + """ + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: # pylint: disable=invalid-name vm = compconn.virtual_machines.convert_to_managed_disks( - resource_group_name=resource_group, - vm_name=name + resource_group_name=resource_group, vm_name=name ) vm.wait() vm_result = vm.result() result = vm_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result def virtual_machine_deallocate(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Power off a virtual machine and deallocate compute resources. @@ -426,26 +430,25 @@ def virtual_machine_deallocate(name, resource_group, **kwargs): salt-call azurearm_compute.virtual_machine_deallocate testvm testgroup - ''' - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + """ + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: # pylint: disable=invalid-name vm = compconn.virtual_machines.deallocate( - resource_group_name=resource_group, - vm_name=name + resource_group_name=resource_group, vm_name=name ) vm.wait() vm_result = vm.result() result = vm_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result def virtual_machine_generalize(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Set the state of a virtual machine to 'generalized'. @@ -461,23 +464,22 @@ def virtual_machine_generalize(name, resource_group, **kwargs): salt-call azurearm_compute.virtual_machine_generalize testvm testgroup - ''' + """ result = False - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: compconn.virtual_machines.generalize( - resource_group_name=resource_group, - vm_name=name + resource_group_name=resource_group, vm_name=name ) result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) return result def virtual_machines_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all virtual machines within a resource group. @@ -491,26 +493,24 @@ def virtual_machines_list(resource_group, **kwargs): salt-call azurearm_compute.virtual_machines_list testgroup - ''' + """ result = {} - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: - vms = __utils__['azurearm.paged_object_to_list']( - compconn.virtual_machines.list( - resource_group_name=resource_group - ) + vms = __utils__["azurearm.paged_object_to_list"]( + compconn.virtual_machines.list(resource_group_name=resource_group) ) for vm in vms: # pylint: disable=invalid-name - result[vm['name']] = vm + result[vm["name"]] = vm except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result def virtual_machines_list_all(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all virtual machines within a subscription. @@ -521,24 +521,26 @@ def virtual_machines_list_all(**kwargs): salt-call azurearm_compute.virtual_machines_list_all - ''' + """ result = {} - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: - vms = __utils__['azurearm.paged_object_to_list']( + vms = __utils__["azurearm.paged_object_to_list"]( compconn.virtual_machines.list_all() ) for vm in vms: # pylint: disable=invalid-name - result[vm['name']] = vm + result[vm["name"]] = vm except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result -def virtual_machines_list_available_sizes(name, resource_group, **kwargs): # pylint: disable=invalid-name - ''' +def virtual_machines_list_available_sizes( + name, resource_group, **kwargs +): # pylint: disable=invalid-name + """ .. versionadded:: 2019.2.0 Lists all available virtual machine sizes to which the specified virtual @@ -555,27 +557,26 @@ def virtual_machines_list_available_sizes(name, resource_group, **kwargs): # py salt-call azurearm_compute.virtual_machines_list_available_sizes testvm testgroup - ''' + """ result = {} - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: - sizes = __utils__['azurearm.paged_object_to_list']( + sizes = __utils__["azurearm.paged_object_to_list"]( compconn.virtual_machines.list_available_sizes( - resource_group_name=resource_group, - vm_name=name + resource_group_name=resource_group, vm_name=name ) ) for size in sizes: - result[size['name']] = size + result[size["name"]] = size except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result def virtual_machine_power_off(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Power off (stop) a virtual machine. @@ -591,26 +592,25 @@ def virtual_machine_power_off(name, resource_group, **kwargs): salt-call azurearm_compute.virtual_machine_power_off testvm testgroup - ''' - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + """ + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: # pylint: disable=invalid-name vm = compconn.virtual_machines.power_off( - resource_group_name=resource_group, - vm_name=name + resource_group_name=resource_group, vm_name=name ) vm.wait() vm_result = vm.result() result = vm_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result def virtual_machine_restart(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Restart a virtual machine. @@ -626,26 +626,25 @@ def virtual_machine_restart(name, resource_group, **kwargs): salt-call azurearm_compute.virtual_machine_restart testvm testgroup - ''' - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + """ + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: # pylint: disable=invalid-name vm = compconn.virtual_machines.restart( - resource_group_name=resource_group, - vm_name=name + resource_group_name=resource_group, vm_name=name ) vm.wait() vm_result = vm.result() result = vm_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result def virtual_machine_start(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Power on (start) a virtual machine. @@ -661,26 +660,25 @@ def virtual_machine_start(name, resource_group, **kwargs): salt-call azurearm_compute.virtual_machine_start testvm testgroup - ''' - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + """ + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: # pylint: disable=invalid-name vm = compconn.virtual_machines.start( - resource_group_name=resource_group, - vm_name=name + resource_group_name=resource_group, vm_name=name ) vm.wait() vm_result = vm.result() result = vm_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result def virtual_machine_redeploy(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Redeploy a virtual machine. @@ -696,19 +694,18 @@ def virtual_machine_redeploy(name, resource_group, **kwargs): salt-call azurearm_compute.virtual_machine_redeploy testvm testgroup - ''' - compconn = __utils__['azurearm.get_client']('compute', **kwargs) + """ + compconn = __utils__["azurearm.get_client"]("compute", **kwargs) try: # pylint: disable=invalid-name vm = compconn.virtual_machines.redeploy( - resource_group_name=resource_group, - vm_name=name + resource_group_name=resource_group, vm_name=name ) vm.wait() vm_result = vm.result() result = vm_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('compute', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("compute", str(exc), **kwargs) + result = {"error": str(exc)} return result diff --git a/salt/modules/azurearm_dns.py b/salt/modules/azurearm_dns.py index e8f9e2e88c7..dcd84df8e2d 100644 --- a/salt/modules/azurearm_dns.py +++ b/salt/modules/azurearm_dns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure (ARM) DNS Execution Module .. versionadded:: 3000 @@ -18,37 +18,43 @@ Azure (ARM) DNS Execution Module * `azure-mgmt-web `_ >= 0.32.0 * `azure-storage `_ >= 0.34.3 * `msrestazure `_ >= 0.4.21 + :platform: linux +:configuration: + This module requires Azure Resource Manager credentials to be passed as keyword arguments + to every function in order to work properly. -:configuration: This module requires Azure Resource Manager credentials to be passed as keyword arguments -to every function in order to work properly. - - Required provider parameters: +Required provider parameters: if using username and password: - * ``subscription_id`` - * ``username`` - * ``password`` + + * ``subscription_id`` + * ``username`` + * ``password`` if using a service principal: - * ``subscription_id`` - * ``tenant`` - * ``client_id`` - * ``secret`` - Optional provider parameters: + * ``subscription_id`` + * ``tenant`` + * ``client_id`` + * ``secret`` + +Optional provider parameters: **cloud_environment**: Used to point the cloud driver to different API endpoints, such as Azure GovCloud. - Possible values: - * ``AZURE_PUBLIC_CLOUD`` (default) - * ``AZURE_CHINA_CLOUD`` - * ``AZURE_US_GOV_CLOUD`` - * ``AZURE_GERMAN_CLOUD`` -''' + Possible values: + + * ``AZURE_PUBLIC_CLOUD`` (default) + * ``AZURE_CHINA_CLOUD`` + * ``AZURE_US_GOV_CLOUD`` + * ``AZURE_GERMAN_CLOUD`` + +""" # Python libs from __future__ import absolute_import + import logging # Azure libs @@ -57,11 +63,12 @@ try: import azure.mgmt.dns.models # pylint: disable=unused-import from msrest.exceptions import SerializationError from msrestazure.azure_exceptions import CloudError + HAS_LIBS = True except ImportError: pass -__virtualname__ = 'azurearm_dns' +__virtualname__ = "azurearm_dns" log = logging.getLogger(__name__) @@ -70,16 +77,16 @@ def __virtual__(): if not HAS_LIBS: return ( False, - 'The following dependencies are required to use the AzureARM modules: ' - 'Microsoft Azure SDK for Python >= 2.0rc6, ' - 'MS REST Azure (msrestazure) >= 0.4' + "The following dependencies are required to use the AzureARM modules: " + "Microsoft Azure SDK for Python >= 2.0rc6, " + "MS REST Azure (msrestazure) >= 0.4", ) return __virtualname__ def record_set_create_or_update(name, zone_name, resource_group, record_type, **kwargs): - ''' + """ .. versionadded:: 3000 Creates or updates a record set within a DNS zone. @@ -90,9 +97,10 @@ def record_set_create_or_update(name, zone_name, resource_group, record_type, ** :param resource_group: The name of the resource group. - :param record_type: The type of DNS record in this record set. Record sets of type SOA can be - updated but not created (they are created when the DNS zone is created). - Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' + :param record_type: + The type of DNS record in this record set. Record sets of type SOA can be + updated but not created (they are created when the DNS zone is created). + Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' CLI Example: @@ -101,13 +109,17 @@ def record_set_create_or_update(name, zone_name, resource_group, record_type, ** salt-call azurearm_dns.record_set_create_or_update myhost myzone testgroup A arecords='[{ipv4_address: 10.0.0.1}]' ttl=300 - ''' - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + """ + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) try: - record_set_model = __utils__['azurearm.create_object_model']('dns', 'RecordSet', **kwargs) + record_set_model = __utils__["azurearm.create_object_model"]( + "dns", "RecordSet", **kwargs + ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: @@ -117,21 +129,23 @@ def record_set_create_or_update(name, zone_name, resource_group, record_type, ** resource_group_name=resource_group, record_type=record_type, parameters=record_set_model, - if_match=kwargs.get('if_match'), - if_none_match=kwargs.get('if_none_match') + if_match=kwargs.get("if_match"), + if_none_match=kwargs.get("if_none_match"), ) result = record_set.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def record_set_delete(name, zone_name, resource_group, record_type, **kwargs): - ''' + """ .. versionadded:: 3000 Deletes a record set from a DNS zone. This operation cannot be undone. @@ -142,9 +156,10 @@ def record_set_delete(name, zone_name, resource_group, record_type, **kwargs): :param resource_group: The name of the resource group. - :param record_type: The type of DNS record in this record set. Record sets of type SOA cannot be - deleted (they are deleted when the DNS zone is deleted). - Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' + :param record_type: + The type of DNS record in this record set. Record sets of type SOA cannot be + deleted (they are deleted when the DNS zone is deleted). + Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' CLI Example: @@ -152,26 +167,26 @@ def record_set_delete(name, zone_name, resource_group, record_type, **kwargs): salt-call azurearm_dns.record_set_delete myhost myzone testgroup A - ''' + """ result = False - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) try: record_set = dnsconn.record_sets.delete( relative_record_set_name=name, zone_name=zone_name, resource_group_name=resource_group, record_type=record_type, - if_match=kwargs.get('if_match') + if_match=kwargs.get("if_match"), ) result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) return result def record_set_get(name, zone_name, resource_group, record_type, **kwargs): - ''' + """ .. versionadded:: 3000 Get a dictionary representing a record set's properties. @@ -182,8 +197,9 @@ def record_set_get(name, zone_name, resource_group, record_type, **kwargs): :param resource_group: The name of the resource group. - :param record_type: The type of DNS record in this record set. - Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' + :param record_type: + The type of DNS record in this record set. + Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' CLI Example: @@ -191,26 +207,28 @@ def record_set_get(name, zone_name, resource_group, record_type, **kwargs): salt-call azurearm_dns.record_set_get '@' myzone testgroup SOA - ''' - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + """ + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) try: record_set = dnsconn.record_sets.get( relative_record_set_name=name, zone_name=zone_name, resource_group_name=resource_group, - record_type=record_type + record_type=record_type, ) result = record_set.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) + result = {"error": str(exc)} return result -def record_sets_list_by_type(zone_name, resource_group, record_type, top=None, recordsetnamesuffix=None, **kwargs): - ''' +def record_sets_list_by_type( + zone_name, resource_group, record_type, top=None, recordsetnamesuffix=None, **kwargs +): + """ .. versionadded:: 3000 Lists the record sets of a specified type in a DNS zone. @@ -219,14 +237,17 @@ def record_sets_list_by_type(zone_name, resource_group, record_type, top=None, r :param resource_group: The name of the resource group. - :param record_type: The type of record sets to enumerate. - Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' + :param record_type: + The type of record sets to enumerate. + Possible values include: 'A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SRV', 'TXT' - :param top: The maximum number of record sets to return. If not specified, - returns up to 100 record sets. + :param top: + The maximum number of record sets to return. If not specified, + returns up to 100 record sets. - :param recordsetnamesuffix: The suffix label of the record set name that has - to be used to filter the record set enumerations. + :param recordsetnamesuffix: + The suffix label of the record set name that has + to be used to filter the record set enumerations. CLI Example: @@ -234,31 +255,33 @@ def record_sets_list_by_type(zone_name, resource_group, record_type, top=None, r salt-call azurearm_dns.record_sets_list_by_type myzone testgroup SOA - ''' + """ result = {} - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) try: - record_sets = __utils__['azurearm.paged_object_to_list']( + record_sets = __utils__["azurearm.paged_object_to_list"]( dnsconn.record_sets.list_by_type( zone_name=zone_name, resource_group_name=resource_group, record_type=record_type, top=top, - recordsetnamesuffix=recordsetnamesuffix + recordsetnamesuffix=recordsetnamesuffix, ) ) for record_set in record_sets: - result[record_set['name']] = record_set + result[record_set["name"]] = record_set except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) + result = {"error": str(exc)} return result -def record_sets_list_by_dns_zone(zone_name, resource_group, top=None, recordsetnamesuffix=None, **kwargs): - ''' +def record_sets_list_by_dns_zone( + zone_name, resource_group, top=None, recordsetnamesuffix=None, **kwargs +): + """ .. versionadded:: 3000 Lists all record sets in a DNS zone. @@ -267,11 +290,13 @@ def record_sets_list_by_dns_zone(zone_name, resource_group, top=None, recordsetn :param resource_group: The name of the resource group. - :param top: The maximum number of record sets to return. If not specified, - returns up to 100 record sets. + :param top: + The maximum number of record sets to return. If not specified, + returns up to 100 record sets. - :param recordsetnamesuffix: The suffix label of the record set name that has - to be used to filter the record set enumerations. + :param recordsetnamesuffix: + The suffix label of the record set name that has + to be used to filter the record set enumerations. CLI Example: @@ -279,30 +304,30 @@ def record_sets_list_by_dns_zone(zone_name, resource_group, top=None, recordsetn salt-call azurearm_dns.record_sets_list_by_dns_zone myzone testgroup - ''' + """ result = {} - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) try: - record_sets = __utils__['azurearm.paged_object_to_list']( + record_sets = __utils__["azurearm.paged_object_to_list"]( dnsconn.record_sets.list_by_dns_zone( zone_name=zone_name, resource_group_name=resource_group, top=top, - recordsetnamesuffix=recordsetnamesuffix + recordsetnamesuffix=recordsetnamesuffix, ) ) for record_set in record_sets: - result[record_set['name']] = record_set + result[record_set["name"]] = record_set except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) + result = {"error": str(exc)} return result def zone_create_or_update(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 3000 Creates or updates a DNS zone. Does not modify DNS records within the zone. @@ -317,23 +342,29 @@ def zone_create_or_update(name, resource_group, **kwargs): salt-call azurearm_dns.zone_create_or_update myzone testgroup - ''' + """ # DNS zones are global objects - kwargs['location'] = 'global' + kwargs["location"] = "global" - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) # Convert list of ID strings to list of dictionaries with id key. - if isinstance(kwargs.get('registration_virtual_networks'), list): - kwargs['registration_virtual_networks'] = [{'id': vnet} for vnet in kwargs['registration_virtual_networks']] + if isinstance(kwargs.get("registration_virtual_networks"), list): + kwargs["registration_virtual_networks"] = [ + {"id": vnet} for vnet in kwargs["registration_virtual_networks"] + ] - if isinstance(kwargs.get('resolution_virtual_networks'), list): - kwargs['resolution_virtual_networks'] = [{'id': vnet} for vnet in kwargs['resolution_virtual_networks']] + if isinstance(kwargs.get("resolution_virtual_networks"), list): + kwargs["resolution_virtual_networks"] = [ + {"id": vnet} for vnet in kwargs["resolution_virtual_networks"] + ] try: - zone_model = __utils__['azurearm.create_object_model']('dns', 'Zone', **kwargs) + zone_model = __utils__["azurearm.create_object_model"]("dns", "Zone", **kwargs) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: @@ -341,21 +372,23 @@ def zone_create_or_update(name, resource_group, **kwargs): zone_name=name, resource_group_name=resource_group, parameters=zone_model, - if_match=kwargs.get('if_match'), - if_none_match=kwargs.get('if_none_match') + if_match=kwargs.get("if_match"), + if_none_match=kwargs.get("if_none_match"), ) result = zone.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def zone_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 3000 Delete a DNS zone within a resource group. @@ -370,25 +403,25 @@ def zone_delete(name, resource_group, **kwargs): salt-call azurearm_dns.zone_delete myzone testgroup - ''' + """ result = False - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) try: zone = dnsconn.zones.delete( zone_name=name, resource_group_name=resource_group, - if_match=kwargs.get('if_match') + if_match=kwargs.get("if_match"), ) zone.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) return result def zone_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 3000 Get a dictionary representing a DNS zone's properties, but not the @@ -404,32 +437,30 @@ def zone_get(name, resource_group, **kwargs): salt-call azurearm_dns.zone_get myzone testgroup - ''' - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + """ + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) try: - zone = dnsconn.zones.get( - zone_name=name, - resource_group_name=resource_group - ) + zone = dnsconn.zones.get(zone_name=name, resource_group_name=resource_group) result = zone.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) + result = {"error": str(exc)} return result def zones_list_by_resource_group(resource_group, top=None, **kwargs): - ''' + """ .. versionadded:: 3000 Lists the DNS zones in a resource group. :param resource_group: The name of the resource group. - :param top: The maximum number of DNS zones to return. If not specified, - returns up to 100 zones. + :param top: + The maximum number of DNS zones to return. If not specified, + returns up to 100 zones. CLI Example: @@ -437,34 +468,34 @@ def zones_list_by_resource_group(resource_group, top=None, **kwargs): salt-call azurearm_dns.zones_list_by_resource_group testgroup - ''' + """ result = {} - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) try: - zones = __utils__['azurearm.paged_object_to_list']( + zones = __utils__["azurearm.paged_object_to_list"]( dnsconn.zones.list_by_resource_group( - resource_group_name=resource_group, - top=top + resource_group_name=resource_group, top=top ) ) for zone in zones: - result[zone['name']] = zone + result[zone["name"]] = zone except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) + result = {"error": str(exc)} return result def zones_list(top=None, **kwargs): - ''' + """ .. versionadded:: 3000 Lists the DNS zones in all resource groups in a subscription. - :param top: The maximum number of DNS zones to return. If not specified, - returns up to 100 zones. + :param top: + The maximum number of DNS zones to return. If not specified, + eturns up to 100 zones. CLI Example: @@ -472,16 +503,16 @@ def zones_list(top=None, **kwargs): salt-call azurearm_dns.zones_list - ''' + """ result = {} - dnsconn = __utils__['azurearm.get_client']('dns', **kwargs) + dnsconn = __utils__["azurearm.get_client"]("dns", **kwargs) try: - zones = __utils__['azurearm.paged_object_to_list'](dnsconn.zones.list(top=top)) + zones = __utils__["azurearm.paged_object_to_list"](dnsconn.zones.list(top=top)) for zone in zones: - result[zone['name']] = zone + result[zone["name"]] = zone except CloudError as exc: - __utils__['azurearm.log_cloud_error']('dns', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("dns", str(exc), **kwargs) + result = {"error": str(exc)} return result diff --git a/salt/modules/azurearm_network.py b/salt/modules/azurearm_network.py index e5f027ececb..53278d20402 100644 --- a/salt/modules/azurearm_network.py +++ b/salt/modules/azurearm_network.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure (ARM) Network Execution Module .. versionadded:: 2019.2.0 @@ -44,10 +44,11 @@ Azure (ARM) Network Execution Module * ``AZURE_US_GOV_CLOUD`` * ``AZURE_GERMAN_CLOUD`` -''' +""" # Python libs from __future__ import absolute_import + import logging # Salt libs @@ -60,11 +61,12 @@ try: import azure.mgmt.network.models # pylint: disable=unused-import from msrest.exceptions import SerializationError from msrestazure.azure_exceptions import CloudError + HAS_LIBS = True except ImportError: pass -__virtualname__ = 'azurearm_network' +__virtualname__ = "azurearm_network" log = logging.getLogger(__name__) @@ -73,16 +75,16 @@ def __virtual__(): if not HAS_LIBS: return ( False, - 'The following dependencies are required to use the AzureARM modules: ' - 'Microsoft Azure SDK for Python >= 2.0rc6, ' - 'MS REST Azure (msrestazure) >= 0.4' + "The following dependencies are required to use the AzureARM modules: " + "Microsoft Azure SDK for Python >= 2.0rc6, " + "MS REST Azure (msrestazure) >= 0.4", ) return __virtualname__ def check_dns_name_availability(name, region, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Check whether a domain name in the current zone is available for use. @@ -97,24 +99,24 @@ def check_dns_name_availability(name, region, **kwargs): salt-call azurearm_network.check_dns_name_availability testdnsname westus - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: check_dns_name = netconn.check_dns_name_availability( - location=region, - domain_name_label=name + location=region, domain_name_label=name ) result = check_dns_name.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result -def check_ip_address_availability(ip_address, virtual_network, resource_group, - **kwargs): - ''' +def check_ip_address_availability( + ip_address, virtual_network, resource_group, **kwargs +): + """ .. versionadded:: 2019.2.0 Check that a private ip address is available within the specified @@ -134,23 +136,24 @@ def check_ip_address_availability(ip_address, virtual_network, resource_group, salt-call azurearm_network.check_ip_address_availability 10.0.0.4 testnet testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: check_ip = netconn.virtual_networks.check_ip_address_availability( resource_group_name=resource_group, virtual_network_name=virtual_network, - ip_address=ip_address) + ip_address=ip_address, + ) result = check_ip.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def default_security_rule_get(name, security_group, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a default security rule within a security group. @@ -169,37 +172,33 @@ def default_security_rule_get(name, security_group, resource_group, **kwargs): salt-call azurearm_network.default_security_rule_get DenyAllOutBound testnsg testgroup - ''' + """ result = {} default_rules = default_security_rules_list( - security_group=security_group, - resource_group=resource_group, - **kwargs + security_group=security_group, resource_group=resource_group, **kwargs ) - if isinstance(default_rules, dict) and 'error' in default_rules: + if isinstance(default_rules, dict) and "error" in default_rules: return default_rules try: for default_rule in default_rules: - if default_rule['name'] == name: + if default_rule["name"] == name: result = default_rule if not result: result = { - 'error': 'Unable to find {0} in {1}!'.format(name, security_group) + "error": "Unable to find {0} in {1}!".format(name, security_group) } except KeyError as exc: - log.error( - 'Unable to find {0} in {1}!'.format(name, security_group) - ) - result = {'error': str(exc)} + log.error("Unable to find {0} in {1}!".format(name, security_group)) + result = {"error": str(exc)} return result def default_security_rules_list(security_group, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List default security rules within a security group. @@ -215,31 +214,27 @@ def default_security_rules_list(security_group, resource_group, **kwargs): salt-call azurearm_network.default_security_rules_list testnsg testgroup - ''' + """ result = {} secgroup = network_security_group_get( - security_group=security_group, - resource_group=resource_group, - **kwargs + security_group=security_group, resource_group=resource_group, **kwargs ) - if 'error' in secgroup: + if "error" in secgroup: return secgroup try: - result = secgroup['default_security_rules'] + result = secgroup["default_security_rules"] except KeyError as exc: - log.error( - 'No default security rules found for {0}!'.format(security_group) - ) - result = {'error': str(exc)} + log.error("No default security rules found for {0}!".format(security_group)) + result = {"error": str(exc)} return result def security_rules_list(security_group, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List security rules within a network security group. @@ -255,27 +250,40 @@ def security_rules_list(security_group, resource_group, **kwargs): salt-call azurearm_network.security_rules_list testnsg testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: secrules = netconn.security_rules.list( network_security_group_name=security_group, - resource_group_name=resource_group + resource_group_name=resource_group, ) - result = __utils__['azurearm.paged_object_to_list'](secrules) + result = __utils__["azurearm.paged_object_to_list"](secrules) except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result -def security_rule_create_or_update(name, access, direction, priority, protocol, security_group, resource_group, - source_address_prefix=None, destination_address_prefix=None, source_port_range=None, - destination_port_range=None, source_address_prefixes=None, - destination_address_prefixes=None, source_port_ranges=None, - destination_port_ranges=None, **kwargs): - ''' +def security_rule_create_or_update( + name, + access, + direction, + priority, + protocol, + security_group, + resource_group, + source_address_prefix=None, + destination_address_prefix=None, + source_port_range=None, + destination_port_range=None, + source_address_prefixes=None, + destination_address_prefixes=None, + source_port_ranges=None, + destination_port_ranges=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Create or update a security rule within a specified network security group. @@ -342,32 +350,34 @@ def security_rule_create_or_update(name, access, direction, priority, protocol, source_address_prefix='*' destination_address_prefix=internet source_port_range='*' \ destination_port_range='1-1024' - ''' + """ exclusive_params = [ - ('source_port_ranges', 'source_port_range'), - ('source_address_prefixes', 'source_address_prefix'), - ('destination_port_ranges', 'destination_port_range'), - ('destination_address_prefixes', 'destination_address_prefix'), + ("source_port_ranges", "source_port_range"), + ("source_address_prefixes", "source_address_prefix"), + ("destination_port_ranges", "destination_port_range"), + ("destination_address_prefixes", "destination_address_prefix"), ] for params in exclusive_params: # pylint: disable=eval-used if not eval(params[0]) and not eval(params[1]): log.error( - 'Either the {0} or {1} parameter must be provided!'.format(params[0], params[1]) + "Either the {0} or {1} parameter must be provided!".format( + params[0], params[1] + ) ) return False # pylint: disable=eval-used if eval(params[0]): # pylint: disable=exec-used - exec('{0} = None'.format(params[1])) + exec("{0} = None".format(params[1])) - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - rulemodel = __utils__['azurearm.create_object_model']( - 'network', - 'SecurityRule', + rulemodel = __utils__["azurearm.create_object_model"]( + "network", + "SecurityRule", name=name, access=access, direction=direction, @@ -384,7 +394,9 @@ def security_rule_create_or_update(name, access, direction, priority, protocol, **kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: @@ -392,23 +404,24 @@ def security_rule_create_or_update(name, access, direction, priority, protocol, resource_group_name=resource_group, network_security_group_name=security_group, security_rule_name=name, - security_rule_parameters=rulemodel + security_rule_parameters=rulemodel, ) secrule.wait() secrule_result = secrule.result() result = secrule_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result -def security_rule_delete(security_rule, security_group, resource_group, - **kwargs): - ''' +def security_rule_delete(security_rule, security_group, resource_group, **kwargs): + """ .. versionadded:: 2019.2.0 Delete a security rule within a specified security group. @@ -427,25 +440,25 @@ def security_rule_delete(security_rule, security_group, resource_group, salt-call azurearm_network.security_rule_delete testrule1 testnsg testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: secrule = netconn.security_rules.delete( network_security_group_name=security_group, resource_group_name=resource_group, - security_rule_name=security_rule + security_rule_name=security_rule, ) secrule.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def security_rule_get(security_rule, security_group, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get a security rule within a specified network security group. @@ -464,24 +477,26 @@ def security_rule_get(security_rule, security_group, resource_group, **kwargs): salt-call azurearm_network.security_rule_get testrule1 testnsg testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: secrule = netconn.security_rules.get( network_security_group_name=security_group, resource_group_name=resource_group, - security_rule_name=security_rule + security_rule_name=security_rule, ) result = secrule.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result -def network_security_group_create_or_update(name, resource_group, **kwargs): # pylint: disable=invalid-name - ''' +def network_security_group_create_or_update( + name, resource_group, **kwargs +): # pylint: disable=invalid-name + """ .. versionadded:: 2019.2.0 Create or update a network security group. @@ -497,47 +512,51 @@ def network_security_group_create_or_update(name, resource_group, **kwargs): # salt-call azurearm_network.network_security_group_create_or_update testnsg testgroup - ''' - if 'location' not in kwargs: - rg_props = __salt__['azurearm_resource.resource_group_get']( + """ + if "location" not in kwargs: + rg_props = __salt__["azurearm_resource.resource_group_get"]( resource_group, **kwargs ) - if 'error' in rg_props: - log.error( - 'Unable to determine location from resource group specified.' - ) + if "error" in rg_props: + log.error("Unable to determine location from resource group specified.") return False - kwargs['location'] = rg_props['location'] + kwargs["location"] = rg_props["location"] - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - secgroupmodel = __utils__['azurearm.create_object_model']('network', 'NetworkSecurityGroup', **kwargs) + secgroupmodel = __utils__["azurearm.create_object_model"]( + "network", "NetworkSecurityGroup", **kwargs + ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: secgroup = netconn.network_security_groups.create_or_update( resource_group_name=resource_group, network_security_group_name=name, - parameters=secgroupmodel + parameters=secgroupmodel, ) secgroup.wait() secgroup_result = secgroup.result() result = secgroup_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def network_security_group_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a network security group within a resource group. @@ -553,24 +572,23 @@ def network_security_group_delete(name, resource_group, **kwargs): salt-call azurearm_network.network_security_group_delete testnsg testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: secgroup = netconn.network_security_groups.delete( - resource_group_name=resource_group, - network_security_group_name=name + resource_group_name=resource_group, network_security_group_name=name ) secgroup.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def network_security_group_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a network security group within a resource group. @@ -586,23 +604,22 @@ def network_security_group_get(name, resource_group, **kwargs): salt-call azurearm_network.network_security_group_get testnsg testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: secgroup = netconn.network_security_groups.get( - resource_group_name=resource_group, - network_security_group_name=name + resource_group_name=resource_group, network_security_group_name=name ) result = secgroup.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def network_security_groups_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all network security groups within a resource group. @@ -616,26 +633,24 @@ def network_security_groups_list(resource_group, **kwargs): salt-call azurearm_network.network_security_groups_list testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - secgroups = __utils__['azurearm.paged_object_to_list']( - netconn.network_security_groups.list( - resource_group_name=resource_group - ) + secgroups = __utils__["azurearm.paged_object_to_list"]( + netconn.network_security_groups.list(resource_group_name=resource_group) ) for secgroup in secgroups: - result[secgroup['name']] = secgroup + result[secgroup["name"]] = secgroup except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def network_security_groups_list_all(**kwargs): # pylint: disable=invalid-name - ''' + """ .. versionadded:: 2019.2.0 List all network security groups within a subscription. @@ -646,24 +661,24 @@ def network_security_groups_list_all(**kwargs): # pylint: disable=invalid-name salt-call azurearm_network.network_security_groups_list_all - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - secgroups = __utils__['azurearm.paged_object_to_list']( + secgroups = __utils__["azurearm.paged_object_to_list"]( netconn.network_security_groups.list_all() ) for secgroup in secgroups: - result[secgroup['name']] = secgroup + result[secgroup["name"]] = secgroup except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def subnets_list(virtual_network, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all subnets within a virtual network. @@ -679,28 +694,27 @@ def subnets_list(virtual_network, resource_group, **kwargs): salt-call azurearm_network.subnets_list testnet testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - subnets = __utils__['azurearm.paged_object_to_list']( + subnets = __utils__["azurearm.paged_object_to_list"]( netconn.subnets.list( - resource_group_name=resource_group, - virtual_network_name=virtual_network + resource_group_name=resource_group, virtual_network_name=virtual_network ) ) for subnet in subnets: - result[subnet['name']] = subnet + result[subnet["name"]] = subnet except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def subnet_get(name, virtual_network, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific subnet. @@ -719,25 +733,27 @@ def subnet_get(name, virtual_network, resource_group, **kwargs): salt-call azurearm_network.subnet_get testsubnet testnet testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: subnet = netconn.subnets.get( resource_group_name=resource_group, virtual_network_name=virtual_network, - subnet_name=name + subnet_name=name, ) result = subnet.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result -def subnet_create_or_update(name, address_prefix, virtual_network, resource_group, **kwargs): - ''' +def subnet_create_or_update( + name, address_prefix, virtual_network, resource_group, **kwargs +): + """ .. versionadded:: 2019.2.0 Create or update a subnet. @@ -759,39 +775,39 @@ def subnet_create_or_update(name, address_prefix, virtual_network, resource_grou salt-call azurearm_network.subnet_create_or_update testsubnet \ '10.0.0.0/24' testnet testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) # Use NSG name to link to the ID of an existing NSG. - if kwargs.get('network_security_group'): + if kwargs.get("network_security_group"): nsg = network_security_group_get( - name=kwargs['network_security_group'], + name=kwargs["network_security_group"], resource_group=resource_group, **kwargs ) - if 'error' not in nsg: - kwargs['network_security_group'] = {'id': str(nsg['id'])} + if "error" not in nsg: + kwargs["network_security_group"] = {"id": str(nsg["id"])} # Use Route Table name to link to the ID of an existing Route Table. - if kwargs.get('route_table'): + if kwargs.get("route_table"): rt_table = route_table_get( - name=kwargs['route_table'], - resource_group=resource_group, - **kwargs + name=kwargs["route_table"], resource_group=resource_group, **kwargs ) - if 'error' not in rt_table: - kwargs['route_table'] = {'id': str(rt_table['id'])} + if "error" not in rt_table: + kwargs["route_table"] = {"id": str(rt_table["id"])} try: - snetmodel = __utils__['azurearm.create_object_model']( - 'network', - 'Subnet', + snetmodel = __utils__["azurearm.create_object_model"]( + "network", + "Subnet", address_prefix=address_prefix, resource_group=resource_group, **kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: @@ -805,16 +821,18 @@ def subnet_create_or_update(name, address_prefix, virtual_network, resource_grou sn_result = subnet.result() result = sn_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def subnet_delete(name, virtual_network, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a subnet. @@ -833,25 +851,25 @@ def subnet_delete(name, virtual_network, resource_group, **kwargs): salt-call azurearm_network.subnet_delete testsubnet testnet testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: subnet = netconn.subnets.delete( resource_group_name=resource_group, virtual_network_name=virtual_network, - subnet_name=name + subnet_name=name, ) subnet.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def virtual_networks_list_all(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all virtual networks within a subscription. @@ -862,23 +880,25 @@ def virtual_networks_list_all(**kwargs): salt-call azurearm_network.virtual_networks_list_all - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - vnets = __utils__['azurearm.paged_object_to_list'](netconn.virtual_networks.list_all()) + vnets = __utils__["azurearm.paged_object_to_list"]( + netconn.virtual_networks.list_all() + ) for vnet in vnets: - result[vnet['name']] = vnet + result[vnet["name"]] = vnet except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def virtual_networks_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all virtual networks within a resource group. @@ -892,31 +912,26 @@ def virtual_networks_list(resource_group, **kwargs): salt-call azurearm_network.virtual_networks_list testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - vnets = __utils__['azurearm.paged_object_to_list']( - netconn.virtual_networks.list( - resource_group_name=resource_group - ) + vnets = __utils__["azurearm.paged_object_to_list"]( + netconn.virtual_networks.list(resource_group_name=resource_group) ) for vnet in vnets: - result[vnet['name']] = vnet + result[vnet["name"]] = vnet except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result # pylint: disable=invalid-name -def virtual_network_create_or_update(name, - address_prefixes, - resource_group, - **kwargs): - ''' +def virtual_network_create_or_update(name, address_prefixes, resource_group, **kwargs): + """ .. versionadded:: 2019.2.0 Create or update a virtual network. @@ -937,62 +952,62 @@ def virtual_network_create_or_update(name, salt-call azurearm_network.virtual_network_create_or_update \ testnet ['10.0.0.0/16'] testgroup - ''' - if 'location' not in kwargs: - rg_props = __salt__['azurearm_resource.resource_group_get']( + """ + if "location" not in kwargs: + rg_props = __salt__["azurearm_resource.resource_group_get"]( resource_group, **kwargs ) - if 'error' in rg_props: - log.error( - 'Unable to determine location from resource group specified.' - ) + if "error" in rg_props: + log.error("Unable to determine location from resource group specified.") return False - kwargs['location'] = rg_props['location'] + kwargs["location"] = rg_props["location"] if not isinstance(address_prefixes, list): - log.error( - 'Address prefixes must be specified as a list!' - ) + log.error("Address prefixes must be specified as a list!") return False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) - address_space = {'address_prefixes': address_prefixes} - dhcp_options = {'dns_servers': kwargs.get('dns_servers')} + address_space = {"address_prefixes": address_prefixes} + dhcp_options = {"dns_servers": kwargs.get("dns_servers")} try: - vnetmodel = __utils__['azurearm.create_object_model']( - 'network', - 'VirtualNetwork', + vnetmodel = __utils__["azurearm.create_object_model"]( + "network", + "VirtualNetwork", address_space=address_space, dhcp_options=dhcp_options, **kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: vnet = netconn.virtual_networks.create_or_update( virtual_network_name=name, resource_group_name=resource_group, - parameters=vnetmodel + parameters=vnetmodel, ) vnet.wait() vnet_result = vnet.result() result = vnet_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def virtual_network_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a virtual network. @@ -1008,24 +1023,23 @@ def virtual_network_delete(name, resource_group, **kwargs): salt-call azurearm_network.virtual_network_delete testnet testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: vnet = netconn.virtual_networks.delete( - virtual_network_name=name, - resource_group_name=resource_group + virtual_network_name=name, resource_group_name=resource_group ) vnet.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def virtual_network_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific virtual network. @@ -1041,23 +1055,22 @@ def virtual_network_get(name, resource_group, **kwargs): salt-call azurearm_network.virtual_network_get testnet testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: vnet = netconn.virtual_networks.get( - virtual_network_name=name, - resource_group_name=resource_group + virtual_network_name=name, resource_group_name=resource_group ) result = vnet.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def load_balancers_list_all(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all load balancers within a subscription. @@ -1068,23 +1081,25 @@ def load_balancers_list_all(**kwargs): salt-call azurearm_network.load_balancers_list_all - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - load_balancers = __utils__['azurearm.paged_object_to_list'](netconn.load_balancers.list_all()) + load_balancers = __utils__["azurearm.paged_object_to_list"]( + netconn.load_balancers.list_all() + ) for load_balancer in load_balancers: - result[load_balancer['name']] = load_balancer + result[load_balancer["name"]] = load_balancer except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def load_balancers_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all load balancers within a resource group. @@ -1098,27 +1113,25 @@ def load_balancers_list(resource_group, **kwargs): salt-call azurearm_network.load_balancers_list testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - load_balancers = __utils__['azurearm.paged_object_to_list']( - netconn.load_balancers.list( - resource_group_name=resource_group - ) + load_balancers = __utils__["azurearm.paged_object_to_list"]( + netconn.load_balancers.list(resource_group_name=resource_group) ) for load_balancer in load_balancers: - result[load_balancer['name']] = load_balancer + result[load_balancer["name"]] = load_balancer except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def load_balancer_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific load balancer. @@ -1134,23 +1147,22 @@ def load_balancer_get(name, resource_group, **kwargs): salt-call azurearm_network.load_balancer_get testlb testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: load_balancer = netconn.load_balancers.get( - load_balancer_name=name, - resource_group_name=resource_group + load_balancer_name=name, resource_group_name=resource_group ) result = load_balancer.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def load_balancer_create_or_update(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Create or update a load balancer within a specified resource group. @@ -1166,165 +1178,179 @@ def load_balancer_create_or_update(name, resource_group, **kwargs): salt-call azurearm_network.load_balancer_create_or_update testlb testgroup - ''' - if 'location' not in kwargs: - rg_props = __salt__['azurearm_resource.resource_group_get']( + """ + if "location" not in kwargs: + rg_props = __salt__["azurearm_resource.resource_group_get"]( resource_group, **kwargs ) - if 'error' in rg_props: - log.error( - 'Unable to determine location from resource group specified.' - ) + if "error" in rg_props: + log.error("Unable to determine location from resource group specified.") return False - kwargs['location'] = rg_props['location'] + kwargs["location"] = rg_props["location"] - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) - if isinstance(kwargs.get('frontend_ip_configurations'), list): - for idx in range(0, len(kwargs['frontend_ip_configurations'])): + if isinstance(kwargs.get("frontend_ip_configurations"), list): + for idx in range(0, len(kwargs["frontend_ip_configurations"])): # Use Public IP Address name to link to the ID of an existing Public IP - if 'public_ip_address' in kwargs['frontend_ip_configurations'][idx]: + if "public_ip_address" in kwargs["frontend_ip_configurations"][idx]: pub_ip = public_ip_address_get( - name=kwargs['frontend_ip_configurations'][idx]['public_ip_address'], + name=kwargs["frontend_ip_configurations"][idx]["public_ip_address"], resource_group=resource_group, **kwargs ) - if 'error' not in pub_ip: - kwargs['frontend_ip_configurations'][idx]['public_ip_address'] = {'id': str(pub_ip['id'])} + if "error" not in pub_ip: + kwargs["frontend_ip_configurations"][idx]["public_ip_address"] = { + "id": str(pub_ip["id"]) + } # Use Subnet name to link to the ID of an existing Subnet - elif 'subnet' in kwargs['frontend_ip_configurations'][idx]: - vnets = virtual_networks_list( - resource_group=resource_group, - **kwargs - ) - if 'error' not in vnets: + elif "subnet" in kwargs["frontend_ip_configurations"][idx]: + vnets = virtual_networks_list(resource_group=resource_group, **kwargs) + if "error" not in vnets: for vnet in vnets: subnets = subnets_list( virtual_network=vnet, resource_group=resource_group, **kwargs ) - if kwargs['frontend_ip_configurations'][idx]['subnet'] in subnets: - kwargs['frontend_ip_configurations'][idx]['subnet'] = { - 'id': str(subnets[kwargs['frontend_ip_configurations'][idx]['subnet']]['id']) + if ( + kwargs["frontend_ip_configurations"][idx]["subnet"] + in subnets + ): + kwargs["frontend_ip_configurations"][idx]["subnet"] = { + "id": str( + subnets[ + kwargs["frontend_ip_configurations"][idx][ + "subnet" + ] + ]["id"] + ) } break - id_url = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/loadBalancers/{2}/{3}/{4}' + id_url = "/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/loadBalancers/{2}/{3}/{4}" - if isinstance(kwargs.get('load_balancing_rules'), list): - for idx in range(0, len(kwargs['load_balancing_rules'])): + if isinstance(kwargs.get("load_balancing_rules"), list): + for idx in range(0, len(kwargs["load_balancing_rules"])): # Link to sub-objects which might be created at the same time as the load balancer - if 'frontend_ip_configuration' in kwargs['load_balancing_rules'][idx]: - kwargs['load_balancing_rules'][idx]['frontend_ip_configuration'] = { - 'id': id_url.format( - kwargs.get('subscription_id'), + if "frontend_ip_configuration" in kwargs["load_balancing_rules"][idx]: + kwargs["load_balancing_rules"][idx]["frontend_ip_configuration"] = { + "id": id_url.format( + kwargs.get("subscription_id"), resource_group, name, - 'frontendIPConfigurations', - kwargs['load_balancing_rules'][idx]['frontend_ip_configuration'] + "frontendIPConfigurations", + kwargs["load_balancing_rules"][idx][ + "frontend_ip_configuration" + ], ) } - if 'backend_address_pool' in kwargs['load_balancing_rules'][idx]: - kwargs['load_balancing_rules'][idx]['backend_address_pool'] = { - 'id': id_url.format( - kwargs.get('subscription_id'), + if "backend_address_pool" in kwargs["load_balancing_rules"][idx]: + kwargs["load_balancing_rules"][idx]["backend_address_pool"] = { + "id": id_url.format( + kwargs.get("subscription_id"), resource_group, name, - 'backendAddressPools', - kwargs['load_balancing_rules'][idx]['backend_address_pool'] + "backendAddressPools", + kwargs["load_balancing_rules"][idx]["backend_address_pool"], ) } - if 'probe' in kwargs['load_balancing_rules'][idx]: - kwargs['load_balancing_rules'][idx]['probe'] = { - 'id': id_url.format( - kwargs.get('subscription_id'), + if "probe" in kwargs["load_balancing_rules"][idx]: + kwargs["load_balancing_rules"][idx]["probe"] = { + "id": id_url.format( + kwargs.get("subscription_id"), resource_group, name, - 'probes', - kwargs['load_balancing_rules'][idx]['probe'] + "probes", + kwargs["load_balancing_rules"][idx]["probe"], ) } - if isinstance(kwargs.get('inbound_nat_rules'), list): - for idx in range(0, len(kwargs['inbound_nat_rules'])): + if isinstance(kwargs.get("inbound_nat_rules"), list): + for idx in range(0, len(kwargs["inbound_nat_rules"])): # Link to sub-objects which might be created at the same time as the load balancer - if 'frontend_ip_configuration' in kwargs['inbound_nat_rules'][idx]: - kwargs['inbound_nat_rules'][idx]['frontend_ip_configuration'] = { - 'id': id_url.format( - kwargs.get('subscription_id'), + if "frontend_ip_configuration" in kwargs["inbound_nat_rules"][idx]: + kwargs["inbound_nat_rules"][idx]["frontend_ip_configuration"] = { + "id": id_url.format( + kwargs.get("subscription_id"), resource_group, name, - 'frontendIPConfigurations', - kwargs['inbound_nat_rules'][idx]['frontend_ip_configuration'] + "frontendIPConfigurations", + kwargs["inbound_nat_rules"][idx]["frontend_ip_configuration"], ) } - if isinstance(kwargs.get('inbound_nat_pools'), list): - for idx in range(0, len(kwargs['inbound_nat_pools'])): + if isinstance(kwargs.get("inbound_nat_pools"), list): + for idx in range(0, len(kwargs["inbound_nat_pools"])): # Link to sub-objects which might be created at the same time as the load balancer - if 'frontend_ip_configuration' in kwargs['inbound_nat_pools'][idx]: - kwargs['inbound_nat_pools'][idx]['frontend_ip_configuration'] = { - 'id': id_url.format( - kwargs.get('subscription_id'), + if "frontend_ip_configuration" in kwargs["inbound_nat_pools"][idx]: + kwargs["inbound_nat_pools"][idx]["frontend_ip_configuration"] = { + "id": id_url.format( + kwargs.get("subscription_id"), resource_group, name, - 'frontendIPConfigurations', - kwargs['inbound_nat_pools'][idx]['frontend_ip_configuration'] + "frontendIPConfigurations", + kwargs["inbound_nat_pools"][idx]["frontend_ip_configuration"], ) } - if isinstance(kwargs.get('outbound_nat_rules'), list): - for idx in range(0, len(kwargs['outbound_nat_rules'])): + if isinstance(kwargs.get("outbound_nat_rules"), list): + for idx in range(0, len(kwargs["outbound_nat_rules"])): # Link to sub-objects which might be created at the same time as the load balancer - if 'frontend_ip_configuration' in kwargs['outbound_nat_rules'][idx]: - kwargs['outbound_nat_rules'][idx]['frontend_ip_configuration'] = { - 'id': id_url.format( - kwargs.get('subscription_id'), + if "frontend_ip_configuration" in kwargs["outbound_nat_rules"][idx]: + kwargs["outbound_nat_rules"][idx]["frontend_ip_configuration"] = { + "id": id_url.format( + kwargs.get("subscription_id"), resource_group, name, - 'frontendIPConfigurations', - kwargs['outbound_nat_rules'][idx]['frontend_ip_configuration'] + "frontendIPConfigurations", + kwargs["outbound_nat_rules"][idx]["frontend_ip_configuration"], ) } - if 'backend_address_pool' in kwargs['outbound_nat_rules'][idx]: - kwargs['outbound_nat_rules'][idx]['backend_address_pool'] = { - 'id': id_url.format( - kwargs.get('subscription_id'), + if "backend_address_pool" in kwargs["outbound_nat_rules"][idx]: + kwargs["outbound_nat_rules"][idx]["backend_address_pool"] = { + "id": id_url.format( + kwargs.get("subscription_id"), resource_group, name, - 'backendAddressPools', - kwargs['outbound_nat_rules'][idx]['backend_address_pool'] + "backendAddressPools", + kwargs["outbound_nat_rules"][idx]["backend_address_pool"], ) } try: - lbmodel = __utils__['azurearm.create_object_model']('network', 'LoadBalancer', **kwargs) + lbmodel = __utils__["azurearm.create_object_model"]( + "network", "LoadBalancer", **kwargs + ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: load_balancer = netconn.load_balancers.create_or_update( resource_group_name=resource_group, load_balancer_name=name, - parameters=lbmodel + parameters=lbmodel, ) load_balancer.wait() lb_result = load_balancer.result() result = lb_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def load_balancer_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a load balancer. @@ -1340,24 +1366,23 @@ def load_balancer_delete(name, resource_group, **kwargs): salt-call azurearm_network.load_balancer_delete testlb testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: load_balancer = netconn.load_balancers.delete( - load_balancer_name=name, - resource_group_name=resource_group + load_balancer_name=name, resource_group_name=resource_group ) load_balancer.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def usages_list(location, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List subscription network usage for a location. @@ -1370,19 +1395,21 @@ def usages_list(location, **kwargs): salt-call azurearm_network.usages_list westus - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - result = __utils__['azurearm.paged_object_to_list'](netconn.usages.list(location)) + result = __utils__["azurearm.paged_object_to_list"]( + netconn.usages.list(location) + ) except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def network_interface_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a network interface. @@ -1398,25 +1425,24 @@ def network_interface_delete(name, resource_group, **kwargs): salt-call azurearm_network.network_interface_delete test-iface0 testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: nic = netconn.network_interfaces.delete( - network_interface_name=name, - resource_group_name=resource_group + network_interface_name=name, resource_group_name=resource_group ) nic.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def network_interface_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific network interface. @@ -1432,25 +1458,25 @@ def network_interface_get(name, resource_group, **kwargs): salt-call azurearm_network.network_interface_get test-iface0 testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: nic = netconn.network_interfaces.get( - network_interface_name=name, - resource_group_name=resource_group + network_interface_name=name, resource_group_name=resource_group ) result = nic.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result # pylint: disable=invalid-name -def network_interface_create_or_update(name, ip_configurations, subnet, virtual_network, - resource_group, **kwargs): - ''' +def network_interface_create_or_update( + name, ip_configurations, subnet, virtual_network, resource_group, **kwargs +): + """ .. versionadded:: 2019.2.0 Create or update a network interface within a specified resource group. @@ -1475,40 +1501,36 @@ def network_interface_create_or_update(name, ip_configurations, subnet, virtual_ salt-call azurearm_network.network_interface_create_or_update test-iface0 [{'name': 'testipconfig1'}] \ testsubnet testnet testgroup - ''' - if 'location' not in kwargs: - rg_props = __salt__['azurearm_resource.resource_group_get']( + """ + if "location" not in kwargs: + rg_props = __salt__["azurearm_resource.resource_group_get"]( resource_group, **kwargs ) - if 'error' in rg_props: - log.error( - 'Unable to determine location from resource group specified.' - ) + if "error" in rg_props: + log.error("Unable to determine location from resource group specified.") return False - kwargs['location'] = rg_props['location'] + kwargs["location"] = rg_props["location"] - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) # Use NSG name to link to the ID of an existing NSG. - if kwargs.get('network_security_group'): + if kwargs.get("network_security_group"): nsg = network_security_group_get( - name=kwargs['network_security_group'], + name=kwargs["network_security_group"], resource_group=resource_group, **kwargs ) - if 'error' not in nsg: - kwargs['network_security_group'] = {'id': str(nsg['id'])} + if "error" not in nsg: + kwargs["network_security_group"] = {"id": str(nsg["id"])} # Use VM name to link to the ID of an existing VM. - if kwargs.get('virtual_machine'): - vm_instance = __salt__['azurearm_compute.virtual_machine_get']( - name=kwargs['virtual_machine'], - resource_group=resource_group, - **kwargs + if kwargs.get("virtual_machine"): + vm_instance = __salt__["azurearm_compute.virtual_machine_get"]( + name=kwargs["virtual_machine"], resource_group=resource_group, **kwargs ) - if 'error' not in vm_instance: - kwargs['virtual_machine'] = {'id': str(vm_instance['id'])} + if "error" not in vm_instance: + kwargs["virtual_machine"] = {"id": str(vm_instance["id"])} # Loop through IP Configurations and build each dictionary to pass to model creation. if isinstance(ip_configurations, list): @@ -1518,60 +1540,67 @@ def network_interface_create_or_update(name, ip_configurations, subnet, virtual_ resource_group=resource_group, **kwargs ) - if 'error' not in subnet: - subnet = {'id': str(subnet['id'])} + if "error" not in subnet: + subnet = {"id": str(subnet["id"])} for ipconfig in ip_configurations: - if 'name' in ipconfig: - ipconfig['subnet'] = subnet - if isinstance(ipconfig.get('application_gateway_backend_address_pools'), list): + if "name" in ipconfig: + ipconfig["subnet"] = subnet + if isinstance( + ipconfig.get("application_gateway_backend_address_pools"), list + ): # TODO: Add ID lookup for referenced object names pass - if isinstance(ipconfig.get('load_balancer_backend_address_pools'), list): + if isinstance( + ipconfig.get("load_balancer_backend_address_pools"), list + ): # TODO: Add ID lookup for referenced object names pass - if isinstance(ipconfig.get('load_balancer_inbound_nat_rules'), list): + if isinstance( + ipconfig.get("load_balancer_inbound_nat_rules"), list + ): # TODO: Add ID lookup for referenced object names pass - if ipconfig.get('public_ip_address'): + if ipconfig.get("public_ip_address"): pub_ip = public_ip_address_get( - name=ipconfig['public_ip_address'], + name=ipconfig["public_ip_address"], resource_group=resource_group, **kwargs ) - if 'error' not in pub_ip: - ipconfig['public_ip_address'] = {'id': str(pub_ip['id'])} + if "error" not in pub_ip: + ipconfig["public_ip_address"] = {"id": str(pub_ip["id"])} try: - nicmodel = __utils__['azurearm.create_object_model']( - 'network', - 'NetworkInterface', - ip_configurations=ip_configurations, - **kwargs + nicmodel = __utils__["azurearm.create_object_model"]( + "network", "NetworkInterface", ip_configurations=ip_configurations, **kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: interface = netconn.network_interfaces.create_or_update( resource_group_name=resource_group, network_interface_name=name, - parameters=nicmodel + parameters=nicmodel, ) interface.wait() nic_result = interface.result() result = nic_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def network_interfaces_list_all(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all network interfaces within a subscription. @@ -1582,23 +1611,25 @@ def network_interfaces_list_all(**kwargs): salt-call azurearm_network.network_interfaces_list_all - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - nics = __utils__['azurearm.paged_object_to_list'](netconn.network_interfaces.list_all()) + nics = __utils__["azurearm.paged_object_to_list"]( + netconn.network_interfaces.list_all() + ) for nic in nics: - result[nic['name']] = nic + result[nic["name"]] = nic except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def network_interfaces_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all network interfaces within a resource group. @@ -1612,28 +1643,26 @@ def network_interfaces_list(resource_group, **kwargs): salt-call azurearm_network.network_interfaces_list testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - nics = __utils__['azurearm.paged_object_to_list']( - netconn.network_interfaces.list( - resource_group_name=resource_group - ) + nics = __utils__["azurearm.paged_object_to_list"]( + netconn.network_interfaces.list(resource_group_name=resource_group) ) for nic in nics: - result[nic['name']] = nic + result[nic["name"]] = nic except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result # pylint: disable=invalid-name def network_interface_get_effective_route_table(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get all route tables for a specific network interface. @@ -1649,27 +1678,28 @@ def network_interface_get_effective_route_table(name, resource_group, **kwargs): salt-call azurearm_network.network_interface_get_effective_route_table test-iface0 testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: nic = netconn.network_interfaces.get_effective_route_table( - network_interface_name=name, - resource_group_name=resource_group + network_interface_name=name, resource_group_name=resource_group ) nic.wait() tables = nic.result() tables = tables.as_dict() - result = tables['value'] + result = tables["value"] except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result # pylint: disable=invalid-name -def network_interface_list_effective_network_security_groups(name, resource_group, **kwargs): - ''' +def network_interface_list_effective_network_security_groups( + name, resource_group, **kwargs +): + """ .. versionadded:: 2019.2.0 Get all network security groups applied to a specific network interface. @@ -1685,30 +1715,28 @@ def network_interface_list_effective_network_security_groups(name, resource_grou salt-call azurearm_network.network_interface_list_effective_network_security_groups test-iface0 testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: nic = netconn.network_interfaces.list_effective_network_security_groups( - network_interface_name=name, - resource_group_name=resource_group + network_interface_name=name, resource_group_name=resource_group ) nic.wait() groups = nic.result() groups = groups.as_dict() - result = groups['value'] + result = groups["value"] except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result # pylint: disable=invalid-name -def list_virtual_machine_scale_set_vm_network_interfaces(scale_set, - vm_index, - resource_group, - **kwargs): - ''' +def list_virtual_machine_scale_set_vm_network_interfaces( + scale_set, vm_index, resource_group, **kwargs +): + """ .. versionadded:: 2019.2.0 Get information about all network interfaces in a specific virtual machine within a scale set. @@ -1726,30 +1754,32 @@ def list_virtual_machine_scale_set_vm_network_interfaces(scale_set, salt-call azurearm_network.list_virtual_machine_scale_set_vm_network_interfaces testset testvm testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - nics = __utils__['azurearm.paged_object_to_list']( + nics = __utils__["azurearm.paged_object_to_list"]( netconn.network_interfaces.list_virtual_machine_scale_set_vm_network_interfaces( virtual_machine_scale_set_name=scale_set, virtualmachine_index=vm_index, - resource_group_name=resource_group + resource_group_name=resource_group, ) ) for nic in nics: - result[nic['name']] = nic + result[nic["name"]] = nic except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result # pylint: disable=invalid-name -def list_virtual_machine_scale_set_network_interfaces(scale_set, resource_group, **kwargs): - ''' +def list_virtual_machine_scale_set_network_interfaces( + scale_set, resource_group, **kwargs +): + """ .. versionadded:: 2019.2.0 Get information about all network interfaces within a scale set. @@ -1765,29 +1795,31 @@ def list_virtual_machine_scale_set_network_interfaces(scale_set, resource_group, salt-call azurearm_network.list_virtual_machine_scale_set_vm_network_interfaces testset testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - nics = __utils__['azurearm.paged_object_to_list']( + nics = __utils__["azurearm.paged_object_to_list"]( netconn.network_interfaces.list_virtual_machine_scale_set_network_interfaces( virtual_machine_scale_set_name=scale_set, - resource_group_name=resource_group + resource_group_name=resource_group, ) ) for nic in nics: - result[nic['name']] = nic + result[nic["name"]] = nic except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result # pylint: disable=invalid-name -def get_virtual_machine_scale_set_network_interface(name, scale_set, vm_index, resource_group, **kwargs): - ''' +def get_virtual_machine_scale_set_network_interface( + name, scale_set, vm_index, resource_group, **kwargs +): + """ .. versionadded:: 2019.2.0 Get information about a specfic network interface within a scale set. @@ -1807,29 +1839,29 @@ def get_virtual_machine_scale_set_network_interface(name, scale_set, vm_index, r salt-call azurearm_network.get_virtual_machine_scale_set_network_interface test-iface0 testset testvm testgroup - ''' - expand = kwargs.get('expand') + """ + expand = kwargs.get("expand") - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: nic = netconn.network_interfaces.list_virtual_machine_scale_set_vm_network_interfaces( network_interface_name=name, virtual_machine_scale_set_name=scale_set, virtualmachine_index=vm_index, resource_group_name=resource_group, - exapnd=expand + exapnd=expand, ) result = nic.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def public_ip_address_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a public IP address. @@ -1845,24 +1877,23 @@ def public_ip_address_delete(name, resource_group, **kwargs): salt-call azurearm_network.public_ip_address_delete test-pub-ip testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: pub_ip = netconn.public_ip_addresses.delete( - public_ip_address_name=name, - resource_group_name=resource_group + public_ip_address_name=name, resource_group_name=resource_group ) pub_ip.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def public_ip_address_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific public IP address. @@ -1878,27 +1909,27 @@ def public_ip_address_get(name, resource_group, **kwargs): salt-call azurearm_network.public_ip_address_get test-pub-ip testgroup - ''' - expand = kwargs.get('expand') + """ + expand = kwargs.get("expand") - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: pub_ip = netconn.public_ip_addresses.get( public_ip_address_name=name, resource_group_name=resource_group, - expand=expand + expand=expand, ) result = pub_ip.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def public_ip_address_create_or_update(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Create or update a public IP address within a specified resource group. @@ -1914,47 +1945,51 @@ def public_ip_address_create_or_update(name, resource_group, **kwargs): salt-call azurearm_network.public_ip_address_create_or_update test-ip-0 testgroup - ''' - if 'location' not in kwargs: - rg_props = __salt__['azurearm_resource.resource_group_get']( + """ + if "location" not in kwargs: + rg_props = __salt__["azurearm_resource.resource_group_get"]( resource_group, **kwargs ) - if 'error' in rg_props: - log.error( - 'Unable to determine location from resource group specified.' - ) + if "error" in rg_props: + log.error("Unable to determine location from resource group specified.") return False - kwargs['location'] = rg_props['location'] + kwargs["location"] = rg_props["location"] - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - pub_ip_model = __utils__['azurearm.create_object_model']('network', 'PublicIPAddress', **kwargs) + pub_ip_model = __utils__["azurearm.create_object_model"]( + "network", "PublicIPAddress", **kwargs + ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: ip = netconn.public_ip_addresses.create_or_update( resource_group_name=resource_group, public_ip_address_name=name, - parameters=pub_ip_model + parameters=pub_ip_model, ) ip.wait() ip_result = ip.result() result = ip_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def public_ip_addresses_list_all(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all public IP addresses within a subscription. @@ -1965,23 +2000,25 @@ def public_ip_addresses_list_all(**kwargs): salt-call azurearm_network.public_ip_addresses_list_all - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - pub_ips = __utils__['azurearm.paged_object_to_list'](netconn.public_ip_addresses.list_all()) + pub_ips = __utils__["azurearm.paged_object_to_list"]( + netconn.public_ip_addresses.list_all() + ) for ip in pub_ips: - result[ip['name']] = ip + result[ip["name"]] = ip except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def public_ip_addresses_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all public IP addresses within a resource group. @@ -1995,27 +2032,25 @@ def public_ip_addresses_list(resource_group, **kwargs): salt-call azurearm_network.public_ip_addresses_list testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - pub_ips = __utils__['azurearm.paged_object_to_list']( - netconn.public_ip_addresses.list( - resource_group_name=resource_group - ) + pub_ips = __utils__["azurearm.paged_object_to_list"]( + netconn.public_ip_addresses.list(resource_group_name=resource_group) ) for ip in pub_ips: - result[ip['name']] = ip + result[ip["name"]] = ip except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def route_filter_rule_delete(name, route_filter, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a route filter rule. @@ -2033,25 +2068,25 @@ def route_filter_rule_delete(name, route_filter, resource_group, **kwargs): salt-call azurearm_network.route_filter_rule_delete test-rule test-filter testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: rule = netconn.route_filter_rules.delete( resource_group_name=resource_group, route_filter_name=route_filter, - rule_name=name + rule_name=name, ) rule.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def route_filter_rule_get(name, route_filter, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific route filter rule. @@ -2069,26 +2104,28 @@ def route_filter_rule_get(name, route_filter, resource_group, **kwargs): salt-call azurearm_network.route_filter_rule_get test-rule test-filter testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: rule = netconn.route_filter_rules.get( resource_group_name=resource_group, route_filter_name=route_filter, - rule_name=name + rule_name=name, ) result = rule.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result -def route_filter_rule_create_or_update(name, access, communities, route_filter, resource_group, **kwargs): - ''' +def route_filter_rule_create_or_update( + name, access, communities, route_filter, resource_group, **kwargs +): + """ .. versionadded:: 2019.2.0 Create or update a rule within a specified route filter. @@ -2111,37 +2148,35 @@ def route_filter_rule_create_or_update(name, access, communities, route_filter, salt-call azurearm_network.route_filter_rule_create_or_update \ test-rule allow "['12076:51006']" test-filter testgroup - ''' + """ if not isinstance(communities, list): - log.error( - 'The communities parameter must be a list of strings!' - ) + log.error("The communities parameter must be a list of strings!") return False - if 'location' not in kwargs: - rg_props = __salt__['azurearm_resource.resource_group_get']( + if "location" not in kwargs: + rg_props = __salt__["azurearm_resource.resource_group_get"]( resource_group, **kwargs ) - if 'error' in rg_props: - log.error( - 'Unable to determine location from resource group specified.' - ) + if "error" in rg_props: + log.error("Unable to determine location from resource group specified.") return False - kwargs['location'] = rg_props['location'] + kwargs["location"] = rg_props["location"] - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - rule_model = __utils__['azurearm.create_object_model']( - 'network', - 'RouteFilterRule', + rule_model = __utils__["azurearm.create_object_model"]( + "network", + "RouteFilterRule", access=access, communities=communities, **kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: @@ -2149,25 +2184,27 @@ def route_filter_rule_create_or_update(name, access, communities, route_filter, resource_group_name=resource_group, route_filter_name=route_filter, rule_name=name, - route_filter_rule_parameters=rule_model + route_filter_rule_parameters=rule_model, ) rule.wait() rule_result = rule.result() result = rule_result.as_dict() except CloudError as exc: message = str(exc) - if kwargs.get('subscription_id') == str(message).strip(): - message = 'Subscription not authorized for this operation!' - __utils__['azurearm.log_cloud_error']('network', message, **kwargs) - result = {'error': message} + if kwargs.get("subscription_id") == str(message).strip(): + message = "Subscription not authorized for this operation!" + __utils__["azurearm.log_cloud_error"]("network", message, **kwargs) + result = {"error": message} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def route_filter_rules_list(route_filter, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all routes within a route filter. @@ -2183,28 +2220,27 @@ def route_filter_rules_list(route_filter, resource_group, **kwargs): salt-call azurearm_network.route_filter_rules_list test-filter testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - rules = __utils__['azurearm.paged_object_to_list']( + rules = __utils__["azurearm.paged_object_to_list"]( netconn.route_filter_rules.list_by_route_filter( - resource_group_name=resource_group, - route_filter_name=route_filter + resource_group_name=resource_group, route_filter_name=route_filter ) ) for rule in rules: - result[rule['name']] = rule + result[rule["name"]] = rule except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def route_filter_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a route filter. @@ -2220,24 +2256,23 @@ def route_filter_delete(name, resource_group, **kwargs): salt-call azurearm_network.route_filter_delete test-filter testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: route_filter = netconn.route_filters.delete( - route_filter_name=name, - resource_group_name=resource_group + route_filter_name=name, resource_group_name=resource_group ) route_filter.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def route_filter_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific route filter. @@ -2253,27 +2288,25 @@ def route_filter_get(name, resource_group, **kwargs): salt-call azurearm_network.route_filter_get test-filter testgroup - ''' - expand = kwargs.get('expand') + """ + expand = kwargs.get("expand") - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: route_filter = netconn.route_filters.get( - route_filter_name=name, - resource_group_name=resource_group, - expand=expand + route_filter_name=name, resource_group_name=resource_group, expand=expand ) result = route_filter.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def route_filter_create_or_update(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Create or update a route filter within a specified resource group. @@ -2289,47 +2322,51 @@ def route_filter_create_or_update(name, resource_group, **kwargs): salt-call azurearm_network.route_filter_create_or_update test-filter testgroup - ''' - if 'location' not in kwargs: - rg_props = __salt__['azurearm_resource.resource_group_get']( + """ + if "location" not in kwargs: + rg_props = __salt__["azurearm_resource.resource_group_get"]( resource_group, **kwargs ) - if 'error' in rg_props: - log.error( - 'Unable to determine location from resource group specified.' - ) + if "error" in rg_props: + log.error("Unable to determine location from resource group specified.") return False - kwargs['location'] = rg_props['location'] + kwargs["location"] = rg_props["location"] - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - rt_filter_model = __utils__['azurearm.create_object_model']('network', 'RouteFilter', **kwargs) + rt_filter_model = __utils__["azurearm.create_object_model"]( + "network", "RouteFilter", **kwargs + ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: rt_filter = netconn.route_filters.create_or_update( resource_group_name=resource_group, route_filter_name=name, - route_filter_parameters=rt_filter_model + route_filter_parameters=rt_filter_model, ) rt_filter.wait() rt_result = rt_filter.result() result = rt_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def route_filters_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all route filters within a resource group. @@ -2343,27 +2380,27 @@ def route_filters_list(resource_group, **kwargs): salt-call azurearm_network.route_filters_list testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - filters = __utils__['azurearm.paged_object_to_list']( + filters = __utils__["azurearm.paged_object_to_list"]( netconn.route_filters.list_by_resource_group( resource_group_name=resource_group ) ) for route_filter in filters: - result[route_filter['name']] = route_filter + result[route_filter["name"]] = route_filter except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def route_filters_list_all(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all route filters within a subscription. @@ -2374,23 +2411,25 @@ def route_filters_list_all(**kwargs): salt-call azurearm_network.route_filters_list_all - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - filters = __utils__['azurearm.paged_object_to_list'](netconn.route_filters.list()) + filters = __utils__["azurearm.paged_object_to_list"]( + netconn.route_filters.list() + ) for route_filter in filters: - result[route_filter['name']] = route_filter + result[route_filter["name"]] = route_filter except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def route_delete(name, route_table, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a route from a route table. @@ -2408,25 +2447,25 @@ def route_delete(name, route_table, resource_group, **kwargs): salt-call azurearm_network.route_delete test-rt test-rt-table testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: route = netconn.routes.delete( resource_group_name=resource_group, route_table_name=route_table, - route_name=name + route_name=name, ) route.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def route_get(name, route_table, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific route. @@ -2444,27 +2483,34 @@ def route_get(name, route_table, resource_group, **kwargs): salt-call azurearm_network.route_get test-rt test-rt-table testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: route = netconn.routes.get( resource_group_name=resource_group, route_table_name=route_table, - route_name=name + route_name=name, ) result = route.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result -def route_create_or_update(name, address_prefix, next_hop_type, route_table, resource_group, - next_hop_ip_address=None, **kwargs): - ''' +def route_create_or_update( + name, + address_prefix, + next_hop_type, + route_table, + resource_group, + next_hop_ip_address=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Create or update a route within a specified route table. @@ -2490,20 +2536,22 @@ def route_create_or_update(name, address_prefix, next_hop_type, route_table, res salt-call azurearm_network.route_create_or_update test-rt '10.0.0.0/8' test-rt-table testgroup - ''' - netconn = __utils__['azurearm.get_client']('network', **kwargs) + """ + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - rt_model = __utils__['azurearm.create_object_model']( - 'network', - 'Route', + rt_model = __utils__["azurearm.create_object_model"]( + "network", + "Route", address_prefix=address_prefix, next_hop_type=next_hop_type, next_hop_ip_address=next_hop_ip_address, **kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: @@ -2511,22 +2559,24 @@ def route_create_or_update(name, address_prefix, next_hop_type, route_table, res resource_group_name=resource_group, route_table_name=route_table, route_name=name, - route_parameters=rt_model + route_parameters=rt_model, ) route.wait() rt_result = route.result() result = rt_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def routes_list(route_table, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all routes within a route table. @@ -2542,28 +2592,27 @@ def routes_list(route_table, resource_group, **kwargs): salt-call azurearm_network.routes_list test-rt-table testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - routes = __utils__['azurearm.paged_object_to_list']( + routes = __utils__["azurearm.paged_object_to_list"]( netconn.routes.list( - resource_group_name=resource_group, - route_table_name=route_table + resource_group_name=resource_group, route_table_name=route_table ) ) for route in routes: - result[route['name']] = route + result[route["name"]] = route except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def route_table_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a route table. @@ -2579,24 +2628,23 @@ def route_table_delete(name, resource_group, **kwargs): salt-call azurearm_network.route_table_delete test-rt-table testgroup - ''' + """ result = False - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: table = netconn.route_tables.delete( - route_table_name=name, - resource_group_name=resource_group + route_table_name=name, resource_group_name=resource_group ) table.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) return result def route_table_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific route table. @@ -2612,27 +2660,25 @@ def route_table_get(name, resource_group, **kwargs): salt-call azurearm_network.route_table_get test-rt-table testgroup - ''' - expand = kwargs.get('expand') + """ + expand = kwargs.get("expand") - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: table = netconn.route_tables.get( - route_table_name=name, - resource_group_name=resource_group, - expand=expand + route_table_name=name, resource_group_name=resource_group, expand=expand ) result = table.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def route_table_create_or_update(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Create or update a route table within a specified resource group. @@ -2648,47 +2694,51 @@ def route_table_create_or_update(name, resource_group, **kwargs): salt-call azurearm_network.route_table_create_or_update test-rt-table testgroup - ''' - if 'location' not in kwargs: - rg_props = __salt__['azurearm_resource.resource_group_get']( + """ + if "location" not in kwargs: + rg_props = __salt__["azurearm_resource.resource_group_get"]( resource_group, **kwargs ) - if 'error' in rg_props: - log.error( - 'Unable to determine location from resource group specified.' - ) + if "error" in rg_props: + log.error("Unable to determine location from resource group specified.") return False - kwargs['location'] = rg_props['location'] + kwargs["location"] = rg_props["location"] - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - rt_tbl_model = __utils__['azurearm.create_object_model']('network', 'RouteTable', **kwargs) + rt_tbl_model = __utils__["azurearm.create_object_model"]( + "network", "RouteTable", **kwargs + ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: table = netconn.route_tables.create_or_update( resource_group_name=resource_group, route_table_name=name, - parameters=rt_tbl_model + parameters=rt_tbl_model, ) table.wait() tbl_result = table.result() result = tbl_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def route_tables_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all route tables within a resource group. @@ -2702,27 +2752,25 @@ def route_tables_list(resource_group, **kwargs): salt-call azurearm_network.route_tables_list testgroup - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - tables = __utils__['azurearm.paged_object_to_list']( - netconn.route_tables.list( - resource_group_name=resource_group - ) + tables = __utils__["azurearm.paged_object_to_list"]( + netconn.route_tables.list(resource_group_name=resource_group) ) for table in tables: - result[table['name']] = table + result[table["name"]] = table except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result def route_tables_list_all(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all route tables within a subscription. @@ -2733,16 +2781,18 @@ def route_tables_list_all(**kwargs): salt-call azurearm_network.route_tables_list_all - ''' + """ result = {} - netconn = __utils__['azurearm.get_client']('network', **kwargs) + netconn = __utils__["azurearm.get_client"]("network", **kwargs) try: - tables = __utils__['azurearm.paged_object_to_list'](netconn.route_tables.list_all()) + tables = __utils__["azurearm.paged_object_to_list"]( + netconn.route_tables.list_all() + ) for table in tables: - result[table['name']] = table + result[table["name"]] = table except CloudError as exc: - __utils__['azurearm.log_cloud_error']('network', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("network", str(exc), **kwargs) + result = {"error": str(exc)} return result diff --git a/salt/modules/azurearm_resource.py b/salt/modules/azurearm_resource.py index 6716d508d1b..2b45ebfd4ce 100644 --- a/salt/modules/azurearm_resource.py +++ b/salt/modules/azurearm_resource.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure (ARM) Resource Execution Module .. versionadded:: 2019.2.0 @@ -44,10 +44,11 @@ Azure (ARM) Resource Execution Module * ``AZURE_US_GOV_CLOUD`` * ``AZURE_GERMAN_CLOUD`` -''' +""" # Python libs from __future__ import absolute_import + import logging # Salt Libs @@ -59,11 +60,12 @@ try: import azure.mgmt.resource.resources.models # pylint: disable=unused-import from msrest.exceptions import SerializationError from msrestazure.azure_exceptions import CloudError + HAS_LIBS = True except ImportError: pass -__virtualname__ = 'azurearm_resource' +__virtualname__ = "azurearm_resource" log = logging.getLogger(__name__) @@ -72,16 +74,16 @@ def __virtual__(): if not HAS_LIBS: return ( False, - 'The following dependencies are required to use the AzureARM modules: ' - 'Microsoft Azure SDK for Python >= 2.0rc6, ' - 'MS REST Azure (msrestazure) >= 0.4' + "The following dependencies are required to use the AzureARM modules: " + "Microsoft Azure SDK for Python >= 2.0rc6, " + "MS REST Azure (msrestazure) >= 0.4", ) return __virtualname__ def resource_groups_list(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all resource groups within a subscription. @@ -92,23 +94,25 @@ def resource_groups_list(**kwargs): salt-call azurearm_resource.resource_groups_list - ''' + """ result = {} - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: - groups = __utils__['azurearm.paged_object_to_list'](resconn.resource_groups.list()) + groups = __utils__["azurearm.paged_object_to_list"]( + resconn.resource_groups.list() + ) for group in groups: - result[group['name']] = group + result[group["name"]] = group except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def resource_group_check_existence(name, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Check for the existence of a named resource group in the current subscription. @@ -121,20 +125,20 @@ def resource_group_check_existence(name, **kwargs): salt-call azurearm_resource.resource_group_check_existence testgroup - ''' + """ result = False - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: result = resconn.resource_groups.check_existence(name) except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) return result def resource_group_get(name, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get a dictionary representing a resource group's properties. @@ -147,22 +151,24 @@ def resource_group_get(name, **kwargs): salt-call azurearm_resource.resource_group_get testgroup - ''' + """ result = {} - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: group = resconn.resource_groups.get(name) result = group.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result -def resource_group_create_or_update(name, location, **kwargs): # pylint: disable=invalid-name - ''' +def resource_group_create_or_update( + name, location, **kwargs +): # pylint: disable=invalid-name + """ .. versionadded:: 2019.2.0 Create or update a resource group in a given location. @@ -178,26 +184,26 @@ def resource_group_create_or_update(name, location, **kwargs): # pylint: disabl salt-call azurearm_resource.resource_group_create_or_update testgroup westus - ''' + """ result = {} - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) resource_group_params = { - 'location': location, - 'managed_by': kwargs.get('managed_by'), - 'tags': kwargs.get('tags'), + "location": location, + "managed_by": kwargs.get("managed_by"), + "tags": kwargs.get("tags"), } try: group = resconn.resource_groups.create_or_update(name, resource_group_params) result = group.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def resource_group_delete(name, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a resource group from the subscription. @@ -210,21 +216,21 @@ def resource_group_delete(name, **kwargs): salt-call azurearm_resource.resource_group_delete testgroup - ''' + """ result = False - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: group = resconn.resource_groups.delete(name) group.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) return result def deployment_operation_get(operation, deployment, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get a deployment operation within a deployment. @@ -242,25 +248,25 @@ def deployment_operation_get(operation, deployment, resource_group, **kwargs): salt-call azurearm_resource.deployment_operation_get XXXXX testdeploy testgroup - ''' - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + """ + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: operation = resconn.deployment_operations.get( resource_group_name=resource_group, deployment_name=deployment, - operation_id=operation + operation_id=operation, ) result = operation.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def deployment_operations_list(name, resource_group, result_limit=10, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all deployment operations within a deployment. @@ -279,29 +285,29 @@ def deployment_operations_list(name, resource_group, result_limit=10, **kwargs): salt-call azurearm_resource.deployment_operations_list testdeploy testgroup - ''' + """ result = {} - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: - operations = __utils__['azurearm.paged_object_to_list']( + operations = __utils__["azurearm.paged_object_to_list"]( resconn.deployment_operations.list( resource_group_name=resource_group, deployment_name=name, - top=result_limit + top=result_limit, ) ) for oper in operations: - result[oper['operation_id']] = oper + result[oper["operation_id"]] = oper except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def deployment_delete(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a deployment. @@ -317,24 +323,23 @@ def deployment_delete(name, resource_group, **kwargs): salt-call azurearm_resource.deployment_delete testdeploy testgroup - ''' + """ result = False - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: deploy = resconn.deployments.delete( - deployment_name=name, - resource_group_name=resource_group + deployment_name=name, resource_group_name=resource_group ) deploy.wait() result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) return result def deployment_check_existence(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Check the existence of a deployment. @@ -350,25 +355,31 @@ def deployment_check_existence(name, resource_group, **kwargs): salt-call azurearm_resource.deployment_check_existence testdeploy testgroup - ''' + """ result = False - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: result = resconn.deployments.check_existence( - deployment_name=name, - resource_group_name=resource_group + deployment_name=name, resource_group_name=resource_group ) except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) return result -def deployment_create_or_update(name, resource_group, deploy_mode='incremental', - debug_setting='none', deploy_params=None, - parameters_link=None, deploy_template=None, - template_link=None, **kwargs): - ''' +def deployment_create_or_update( + name, + resource_group, + deploy_mode="incremental", + debug_setting="none", + deploy_params=None, + parameters_link=None, + deploy_template=None, + template_link=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Deploys resources to a resource group. @@ -411,69 +422,69 @@ def deployment_create_or_update(name, resource_group, deploy_mode='incremental', salt-call azurearm_resource.deployment_create_or_update testdeploy testgroup - ''' - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + """ + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) - prop_kwargs = {'mode': deploy_mode} - prop_kwargs['debug_setting'] = {'detail_level': debug_setting} + prop_kwargs = {"mode": deploy_mode} + prop_kwargs["debug_setting"] = {"detail_level": debug_setting} if deploy_params: - prop_kwargs['parameters'] = deploy_params + prop_kwargs["parameters"] = deploy_params else: if isinstance(parameters_link, dict): - prop_kwargs['parameters_link'] = parameters_link + prop_kwargs["parameters_link"] = parameters_link else: - prop_kwargs['parameters_link'] = {'uri': parameters_link} + prop_kwargs["parameters_link"] = {"uri": parameters_link} if deploy_template: - prop_kwargs['template'] = deploy_template + prop_kwargs["template"] = deploy_template else: if isinstance(template_link, dict): - prop_kwargs['template_link'] = template_link + prop_kwargs["template_link"] = template_link else: - prop_kwargs['template_link'] = {'uri': template_link} + prop_kwargs["template_link"] = {"uri": template_link} deploy_kwargs = kwargs.copy() deploy_kwargs.update(prop_kwargs) try: - deploy_model = __utils__['azurearm.create_object_model']( - 'resource', - 'DeploymentProperties', - **deploy_kwargs + deploy_model = __utils__["azurearm.create_object_model"]( + "resource", "DeploymentProperties", **deploy_kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: validate = deployment_validate( - name=name, - resource_group=resource_group, - **deploy_kwargs + name=name, resource_group=resource_group, **deploy_kwargs ) - if 'error' in validate: + if "error" in validate: result = validate else: deploy = resconn.deployments.create_or_update( deployment_name=name, resource_group_name=resource_group, - properties=deploy_model + properties=deploy_model, ) deploy.wait() deploy_result = deploy.result() result = deploy_result.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def deployment_get(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific deployment. @@ -489,23 +500,22 @@ def deployment_get(name, resource_group, **kwargs): salt-call azurearm_resource.deployment_get testdeploy testgroup - ''' - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + """ + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: deploy = resconn.deployments.get( - deployment_name=name, - resource_group_name=resource_group + deployment_name=name, resource_group_name=resource_group ) result = deploy.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def deployment_cancel(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Cancel a deployment if in 'Accepted' or 'Running' state. @@ -521,29 +531,32 @@ def deployment_cancel(name, resource_group, **kwargs): salt-call azurearm_resource.deployment_cancel testdeploy testgroup - ''' - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + """ + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: resconn.deployments.cancel( - deployment_name=name, - resource_group_name=resource_group + deployment_name=name, resource_group_name=resource_group ) - result = {'result': True} + result = {"result": True} except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = { - 'error': str(exc), - 'result': False - } + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc), "result": False} return result -def deployment_validate(name, resource_group, deploy_mode=None, - debug_setting=None, deploy_params=None, - parameters_link=None, deploy_template=None, - template_link=None, **kwargs): - ''' +def deployment_validate( + name, + resource_group, + deploy_mode=None, + debug_setting=None, + deploy_params=None, + parameters_link=None, + deploy_template=None, + template_link=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Validates whether the specified template is syntactically correct @@ -587,39 +600,39 @@ def deployment_validate(name, resource_group, deploy_mode=None, salt-call azurearm_resource.deployment_validate testdeploy testgroup - ''' - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + """ + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) - prop_kwargs = {'mode': deploy_mode} - prop_kwargs['debug_setting'] = {'detail_level': debug_setting} + prop_kwargs = {"mode": deploy_mode} + prop_kwargs["debug_setting"] = {"detail_level": debug_setting} if deploy_params: - prop_kwargs['parameters'] = deploy_params + prop_kwargs["parameters"] = deploy_params else: if isinstance(parameters_link, dict): - prop_kwargs['parameters_link'] = parameters_link + prop_kwargs["parameters_link"] = parameters_link else: - prop_kwargs['parameters_link'] = {'uri': parameters_link} + prop_kwargs["parameters_link"] = {"uri": parameters_link} if deploy_template: - prop_kwargs['template'] = deploy_template + prop_kwargs["template"] = deploy_template else: if isinstance(template_link, dict): - prop_kwargs['template_link'] = template_link + prop_kwargs["template_link"] = template_link else: - prop_kwargs['template_link'] = {'uri': template_link} + prop_kwargs["template_link"] = {"uri": template_link} deploy_kwargs = kwargs.copy() deploy_kwargs.update(prop_kwargs) try: - deploy_model = __utils__['azurearm.create_object_model']( - 'resource', - 'DeploymentProperties', - **deploy_kwargs + deploy_model = __utils__["azurearm.create_object_model"]( + "resource", "DeploymentProperties", **deploy_kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: @@ -630,20 +643,22 @@ def deployment_validate(name, resource_group, deploy_mode=None, deploy = resconn.deployments.validate( deployment_name=name, resource_group_name=resource_group, - properties=deploy_model + properties=deploy_model, ) result = deploy.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def deployment_export_template(name, resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Exports the template used for the specified deployment. @@ -659,23 +674,22 @@ def deployment_export_template(name, resource_group, **kwargs): salt-call azurearm_resource.deployment_export_template testdeploy testgroup - ''' - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + """ + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: deploy = resconn.deployments.export_template( - deployment_name=name, - resource_group_name=resource_group + deployment_name=name, resource_group_name=resource_group ) result = deploy.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def deployments_list(resource_group, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all deployments within a resource group. @@ -686,27 +700,27 @@ def deployments_list(resource_group, **kwargs): salt-call azurearm_resource.deployments_list testgroup - ''' + """ result = {} - resconn = __utils__['azurearm.get_client']('resource', **kwargs) + resconn = __utils__["azurearm.get_client"]("resource", **kwargs) try: - deployments = __utils__['azurearm.paged_object_to_list']( + deployments = __utils__["azurearm.paged_object_to_list"]( resconn.deployments.list_by_resource_group( resource_group_name=resource_group ) ) for deploy in deployments: - result[deploy['name']] = deploy + result[deploy["name"]] = deploy except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def subscriptions_list_locations(subscription_id=None, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all locations for a subscription. @@ -719,33 +733,33 @@ def subscriptions_list_locations(subscription_id=None, **kwargs): salt-call azurearm_resource.subscriptions_list_locations XXXXXXXX - ''' + """ result = {} if not subscription_id: - subscription_id = kwargs.get('subscription_id') - elif not kwargs.get('subscription_id'): - kwargs['subscription_id'] = subscription_id + subscription_id = kwargs.get("subscription_id") + elif not kwargs.get("subscription_id"): + kwargs["subscription_id"] = subscription_id - subconn = __utils__['azurearm.get_client']('subscription', **kwargs) + subconn = __utils__["azurearm.get_client"]("subscription", **kwargs) try: - locations = __utils__['azurearm.paged_object_to_list']( + locations = __utils__["azurearm.paged_object_to_list"]( subconn.subscriptions.list_locations( - subscription_id=kwargs['subscription_id'] + subscription_id=kwargs["subscription_id"] ) ) for loc in locations: - result[loc['name']] = loc + result[loc["name"]] = loc except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def subscription_get(subscription_id=None, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a subscription. @@ -758,30 +772,30 @@ def subscription_get(subscription_id=None, **kwargs): salt-call azurearm_resource.subscription_get XXXXXXXX - ''' + """ result = {} if not subscription_id: - subscription_id = kwargs.get('subscription_id') - elif not kwargs.get('subscription_id'): - kwargs['subscription_id'] = subscription_id + subscription_id = kwargs.get("subscription_id") + elif not kwargs.get("subscription_id"): + kwargs["subscription_id"] = subscription_id - subconn = __utils__['azurearm.get_client']('subscription', **kwargs) + subconn = __utils__["azurearm.get_client"]("subscription", **kwargs) try: subscription = subconn.subscriptions.get( - subscription_id=kwargs.get('subscription_id') + subscription_id=kwargs.get("subscription_id") ) result = subscription.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def subscriptions_list(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all subscriptions for a tenant. @@ -792,23 +806,23 @@ def subscriptions_list(**kwargs): salt-call azurearm_resource.subscriptions_list - ''' + """ result = {} - subconn = __utils__['azurearm.get_client']('subscription', **kwargs) + subconn = __utils__["azurearm.get_client"]("subscription", **kwargs) try: - subs = __utils__['azurearm.paged_object_to_list'](subconn.subscriptions.list()) + subs = __utils__["azurearm.paged_object_to_list"](subconn.subscriptions.list()) for sub in subs: - result[sub['subscription_id']] = sub + result[sub["subscription_id"]] = sub except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def tenants_list(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all tenants for your account. @@ -819,23 +833,23 @@ def tenants_list(**kwargs): salt-call azurearm_resource.tenants_list - ''' + """ result = {} - subconn = __utils__['azurearm.get_client']('subscription', **kwargs) + subconn = __utils__["azurearm.get_client"]("subscription", **kwargs) try: - tenants = __utils__['azurearm.paged_object_to_list'](subconn.tenants.list()) + tenants = __utils__["azurearm.paged_object_to_list"](subconn.tenants.list()) for tenant in tenants: - result[tenant['tenant_id']] = tenant + result[tenant["tenant_id"]] = tenant except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def policy_assignment_delete(name, scope, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a policy assignment. @@ -851,24 +865,23 @@ def policy_assignment_delete(name, scope, **kwargs): salt-call azurearm_resource.policy_assignment_delete testassign \ /subscriptions/bc75htn-a0fhsi-349b-56gh-4fghti-f84852 - ''' + """ result = False - polconn = __utils__['azurearm.get_client']('policy', **kwargs) + polconn = __utils__["azurearm.get_client"]("policy", **kwargs) try: # pylint: disable=unused-variable policy = polconn.policy_assignments.delete( - policy_assignment_name=name, - scope=scope + policy_assignment_name=name, scope=scope ) result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) return result def policy_assignment_create(name, scope, definition_name, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Create a policy assignment. @@ -886,8 +899,8 @@ def policy_assignment_create(name, scope, definition_name, **kwargs): salt-call azurearm_resource.policy_assignment_create testassign \ /subscriptions/bc75htn-a0fhsi-349b-56gh-4fghti-f84852 testpolicy - ''' - polconn = __utils__['azurearm.get_client']('policy', **kwargs) + """ + polconn = __utils__["azurearm.get_client"]("policy", **kwargs) # "get" doesn't work for built-in policies per https://github.com/Azure/azure-cli/issues/692 # Uncomment this section when the ticket above is resolved. @@ -900,53 +913,59 @@ def policy_assignment_create(name, scope, definition_name, **kwargs): # Delete this section when the ticket above is resolved. # BEGIN - definition_list = policy_definitions_list( - **kwargs - ) + definition_list = policy_definitions_list(**kwargs) if definition_name in definition_list: definition = definition_list[definition_name] else: - definition = {'error': 'The policy definition named "{0}" could not be found.'.format(definition_name)} + definition = { + "error": 'The policy definition named "{0}" could not be found.'.format( + definition_name + ) + } # END - if 'error' not in definition: - definition_id = str(definition['id']) + if "error" not in definition: + definition_id = str(definition["id"]) - prop_kwargs = {'policy_definition_id': definition_id} + prop_kwargs = {"policy_definition_id": definition_id} policy_kwargs = kwargs.copy() policy_kwargs.update(prop_kwargs) try: - policy_model = __utils__['azurearm.create_object_model']( - 'resource.policy', - 'PolicyAssignment', - **policy_kwargs + policy_model = __utils__["azurearm.create_object_model"]( + "resource.policy", "PolicyAssignment", **policy_kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: policy = polconn.policy_assignments.create( - scope=scope, - policy_assignment_name=name, - parameters=policy_model + scope=scope, policy_assignment_name=name, parameters=policy_model ) result = policy.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } else: - result = {'error': 'The policy definition named "{0}" could not be found.'.format(definition_name)} + result = { + "error": 'The policy definition named "{0}" could not be found.'.format( + definition_name + ) + } return result def policy_assignment_get(name, scope, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific policy assignment. @@ -962,23 +981,24 @@ def policy_assignment_get(name, scope, **kwargs): salt-call azurearm_resource.policy_assignment_get testassign \ /subscriptions/bc75htn-a0fhsi-349b-56gh-4fghti-f84852 - ''' - polconn = __utils__['azurearm.get_client']('policy', **kwargs) + """ + polconn = __utils__["azurearm.get_client"]("policy", **kwargs) try: policy = polconn.policy_assignments.get( - policy_assignment_name=name, - scope=scope + policy_assignment_name=name, scope=scope ) result = policy.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result -def policy_assignments_list_for_resource_group(resource_group, **kwargs): # pylint: disable=invalid-name - ''' +def policy_assignments_list_for_resource_group( + resource_group, **kwargs +): # pylint: disable=invalid-name + """ .. versionadded:: 2019.2.0 List all policy assignments for a resource group. @@ -991,28 +1011,27 @@ def policy_assignments_list_for_resource_group(resource_group, **kwargs): # pyl salt-call azurearm_resource.policy_assignments_list_for_resource_group testgroup - ''' + """ result = {} - polconn = __utils__['azurearm.get_client']('policy', **kwargs) + polconn = __utils__["azurearm.get_client"]("policy", **kwargs) try: - policy_assign = __utils__['azurearm.paged_object_to_list']( + policy_assign = __utils__["azurearm.paged_object_to_list"]( polconn.policy_assignments.list_for_resource_group( - resource_group_name=resource_group, - filter=kwargs.get('filter') + resource_group_name=resource_group, filter=kwargs.get("filter") ) ) for assign in policy_assign: - result[assign['name']] = assign + result[assign["name"]] = assign except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def policy_assignments_list(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all policy assignments for a subscription. @@ -1023,23 +1042,27 @@ def policy_assignments_list(**kwargs): salt-call azurearm_resource.policy_assignments_list - ''' + """ result = {} - polconn = __utils__['azurearm.get_client']('policy', **kwargs) + polconn = __utils__["azurearm.get_client"]("policy", **kwargs) try: - policy_assign = __utils__['azurearm.paged_object_to_list'](polconn.policy_assignments.list()) + policy_assign = __utils__["azurearm.paged_object_to_list"]( + polconn.policy_assignments.list() + ) for assign in policy_assign: - result[assign['name']] = assign + result[assign["name"]] = assign except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result -def policy_definition_create_or_update(name, policy_rule, **kwargs): # pylint: disable=invalid-name - ''' +def policy_definition_create_or_update( + name, policy_rule, **kwargs +): # pylint: disable=invalid-name + """ .. versionadded:: 2019.2.0 Create or update a policy definition. @@ -1055,46 +1078,49 @@ def policy_definition_create_or_update(name, policy_rule, **kwargs): # pylint: salt-call azurearm_resource.policy_definition_create_or_update testpolicy '{...rule definition..}' - ''' + """ if not isinstance(policy_rule, dict): - result = {'error': 'The policy rule must be a dictionary!'} + result = {"error": "The policy rule must be a dictionary!"} return result - polconn = __utils__['azurearm.get_client']('policy', **kwargs) + polconn = __utils__["azurearm.get_client"]("policy", **kwargs) # Convert OrderedDict to dict - prop_kwargs = {'policy_rule': salt.utils.json.loads(salt.utils.json.dumps(policy_rule))} + prop_kwargs = { + "policy_rule": salt.utils.json.loads(salt.utils.json.dumps(policy_rule)) + } policy_kwargs = kwargs.copy() policy_kwargs.update(prop_kwargs) try: - policy_model = __utils__['azurearm.create_object_model']( - 'resource.policy', - 'PolicyDefinition', - **policy_kwargs + policy_model = __utils__["azurearm.create_object_model"]( + "resource.policy", "PolicyDefinition", **policy_kwargs ) except TypeError as exc: - result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be built. ({0})".format(str(exc)) + } return result try: policy = polconn.policy_definitions.create_or_update( - policy_definition_name=name, - parameters=policy_model + policy_definition_name=name, parameters=policy_model ) result = policy.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} except SerializationError as exc: - result = {'error': 'The object model could not be parsed. ({0})'.format(str(exc))} + result = { + "error": "The object model could not be parsed. ({0})".format(str(exc)) + } return result def policy_definition_delete(name, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Delete a policy definition. @@ -1107,23 +1133,21 @@ def policy_definition_delete(name, **kwargs): salt-call azurearm_resource.policy_definition_delete testpolicy - ''' + """ result = False - polconn = __utils__['azurearm.get_client']('policy', **kwargs) + polconn = __utils__["azurearm.get_client"]("policy", **kwargs) try: # pylint: disable=unused-variable - policy = polconn.policy_definitions.delete( - policy_definition_name=name - ) + policy = polconn.policy_definitions.delete(policy_definition_name=name) result = True except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) return result def policy_definition_get(name, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Get details about a specific policy definition. @@ -1136,22 +1160,20 @@ def policy_definition_get(name, **kwargs): salt-call azurearm_resource.policy_definition_get testpolicy - ''' - polconn = __utils__['azurearm.get_client']('policy', **kwargs) + """ + polconn = __utils__["azurearm.get_client"]("policy", **kwargs) try: - policy_def = polconn.policy_definitions.get( - policy_definition_name=name - ) + policy_def = polconn.policy_definitions.get(policy_definition_name=name) result = policy_def.as_dict() except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result def policy_definitions_list(hide_builtin=False, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 List all policy definitions for a subscription. @@ -1164,17 +1186,19 @@ def policy_definitions_list(hide_builtin=False, **kwargs): salt-call azurearm_resource.policy_definitions_list - ''' + """ result = {} - polconn = __utils__['azurearm.get_client']('policy', **kwargs) + polconn = __utils__["azurearm.get_client"]("policy", **kwargs) try: - policy_defs = __utils__['azurearm.paged_object_to_list'](polconn.policy_definitions.list()) + policy_defs = __utils__["azurearm.paged_object_to_list"]( + polconn.policy_definitions.list() + ) for policy in policy_defs: - if not (hide_builtin and policy['policy_type'] == 'BuiltIn'): - result[policy['name']] = policy + if not (hide_builtin and policy["policy_type"] == "BuiltIn"): + result[policy["name"]] = policy except CloudError as exc: - __utils__['azurearm.log_cloud_error']('resource', str(exc), **kwargs) - result = {'error': str(exc)} + __utils__["azurearm.log_cloud_error"]("resource", str(exc), **kwargs) + result = {"error": str(exc)} return result diff --git a/salt/modules/bamboohr.py b/salt/modules/bamboohr.py index 2817722f1ae..87854fafc95 100644 --- a/salt/modules/bamboohr.py +++ b/salt/modules/bamboohr.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for BambooHR .. versionadded:: 2015.8.0 @@ -11,39 +11,43 @@ Requires a ``subdomain`` and an ``apikey`` in ``/etc/salt/minion``: bamboohr: apikey: 012345678901234567890 subdomain: mycompany -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs import salt.utils.http import salt.utils.yaml -from salt.ext import six from salt._compat import ElementTree as ET +from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if apache is installed - ''' + """ if _apikey(): return True - return (False, 'The API key was not specified. Please specify it using the "apikey" config.') + return ( + False, + 'The API key was not specified. Please specify it using the "apikey" config.', + ) def _apikey(): - ''' + """ Get the API key - ''' - return __opts__.get('bamboohr', {}).get('apikey', None) + """ + return __opts__.get("bamboohr", {}).get("apikey", None) -def list_employees(order_by='id'): - ''' +def list_employees(order_by="id"): + """ Show all employees for this company. CLI Example: @@ -62,17 +66,17 @@ def list_employees(order_by='id'): salt myminion bamboohr.list_employees order_by=id salt myminion bamboohr.list_employees order_by=displayName salt myminion bamboohr.list_employees order_by=workEmail - ''' + """ ret = {} - status, result = _query(action='employees', command='directory') + status, result = _query(action="employees", command="directory") root = ET.fromstring(result) directory = root.getchildren() for cat in directory: - if cat.tag != 'employees': + if cat.tag != "employees": continue for item in cat: emp_id = item.items()[0][1] - emp_ret = {'id': emp_id} + emp_ret = {"id": emp_id} for details in item.getchildren(): emp_ret[details.items()[0][1]] = details.text ret[emp_ret[order_by]] = emp_ret @@ -80,7 +84,7 @@ def list_employees(order_by='id'): def show_employee(emp_id, fields=None): - ''' + """ Show all employees for this company. CLI Example: @@ -114,44 +118,42 @@ def show_employee(emp_id, fields=None): A list of available fields can be found at http://www.bamboohr.com/api/documentation/employees.php - ''' + """ ret = {} if fields is None: - fields = ','.join(( - 'canUploadPhoto', - 'department', - 'displayName', - 'firstName', - 'id', - 'jobTitle', - 'lastName', - 'location', - 'mobilePhone', - 'nickname', - 'photoUploaded', - 'photoUrl', - 'workEmail', - 'workPhone', - 'workPhoneExtension', - )) + fields = ",".join( + ( + "canUploadPhoto", + "department", + "displayName", + "firstName", + "id", + "jobTitle", + "lastName", + "location", + "mobilePhone", + "nickname", + "photoUploaded", + "photoUrl", + "workEmail", + "workPhone", + "workPhoneExtension", + ) + ) - status, result = _query( - action='employees', - command=emp_id, - args={'fields': fields} - ) + status, result = _query(action="employees", command=emp_id, args={"fields": fields}) root = ET.fromstring(result) items = root.getchildren() - ret = {'id': emp_id} + ret = {"id": emp_id} for item in items: ret[item.items()[0][1]] = item.text return ret def update_employee(emp_id, key=None, value=None, items=None): - ''' + """ Update one or more items for this employee. Specifying an empty value will clear it for that employee. @@ -161,31 +163,28 @@ def update_employee(emp_id, key=None, value=None, items=None): salt myminion bamboohr.update_employee 1138 nickname '' salt myminion bamboohr.update_employee 1138 items='{"nickname": "Curly"} salt myminion bamboohr.update_employee 1138 items='{"nickname": ""} - ''' + """ if items is None: if key is None or value is None: - return {'Error': 'At least one key/value pair is required'} + return {"Error": "At least one key/value pair is required"} items = {key: value} elif isinstance(items, six.string_types): items = salt.utils.yaml.safe_load(items) - xml_items = '' + xml_items = "" for pair in items: xml_items += '{1}'.format(pair, items[pair]) - xml_items = '{0}'.format(xml_items) + xml_items = "{0}".format(xml_items) status, result = _query( - action='employees', - command=emp_id, - data=xml_items, - method='POST', + action="employees", command=emp_id, data=xml_items, method="POST", ) - return show_employee(emp_id, ','.join(items.keys())) + return show_employee(emp_id, ",".join(items.keys())) -def list_users(order_by='id'): - ''' +def list_users(order_by="id"): + """ Show all users for this company. CLI Example: @@ -203,9 +202,9 @@ def list_users(order_by='id'): salt myminion bamboohr.list_users order_by=id salt myminion bamboohr.list_users order_by=email - ''' + """ ret = {} - status, result = _query(action='meta', command='users') + status, result = _query(action="meta", command="users") root = ET.fromstring(result) users = root.getchildren() for user in users: @@ -213,7 +212,7 @@ def list_users(order_by='id'): user_ret = {} for item in user.items(): user_ret[item[0]] = item[1] - if item[0] == 'id': + if item[0] == "id": user_id = item[1] for item in user.getchildren(): user_ret[item.tag] = item.text @@ -222,50 +221,44 @@ def list_users(order_by='id'): def list_meta_fields(): - ''' + """ Show all meta data fields for this company. CLI Example: salt myminion bamboohr.list_meta_fields - ''' + """ ret = {} - status, result = _query(action='meta', command='fields') + status, result = _query(action="meta", command="fields") root = ET.fromstring(result) fields = root.getchildren() for field in fields: field_id = None - field_ret = {'name': field.text} + field_ret = {"name": field.text} for item in field.items(): field_ret[item[0]] = item[1] - if item[0] == 'id': + if item[0] == "id": field_id = item[1] ret[field_id] = field_ret return ret -def _query(action=None, - command=None, - args=None, - method='GET', - data=None): - ''' +def _query(action=None, command=None, args=None, method="GET", data=None): + """ Make a web call to BambooHR The password can be any random text, so we chose Salty text. - ''' - subdomain = __opts__.get('bamboohr', {}).get('subdomain', None) - path = 'https://api.bamboohr.com/api/gateway.php/{0}/v1/'.format( - subdomain - ) + """ + subdomain = __opts__.get("bamboohr", {}).get("subdomain", None) + path = "https://api.bamboohr.com/api/gateway.php/{0}/v1/".format(subdomain) if action: path += action if command: - path += '/{0}'.format(command) + path += "/{0}".format(command) - log.debug('BambooHR URL: %s', path) + log.debug("BambooHR URL: %s", path) if not isinstance(args, dict): args = {} @@ -275,7 +268,7 @@ def _query(action=None, path, method, username=_apikey(), - password='saltypork', + password="saltypork", params=args, data=data, decode=False, @@ -283,6 +276,6 @@ def _query(action=None, status=True, opts=__opts__, ) - log.debug('BambooHR Response Status Code: %s', result['status']) + log.debug("BambooHR Response Status Code: %s", result["status"]) - return [result['status'], result['text']] + return [result["status"], result["text"]] diff --git a/salt/modules/bcache.py b/salt/modules/bcache.py index f487c745b9f..d545e1ad5d4 100644 --- a/salt/modules/bcache.py +++ b/salt/modules/bcache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing BCache sets BCache is a block-level caching mechanism similar to ZFS L2ARC/ZIL, dm-cache and fscache. @@ -14,48 +14,48 @@ This module needs the bcache userspace tools to function. .. versionadded: 2016.3.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os -import time import re - -from salt.ext import six +import time # Import salt libs import salt.utils.path +from salt.ext import six log = logging.getLogger(__name__) LOG = { - 'trace': logging.TRACE, - 'debug': logging.DEBUG, - 'info': logging.INFO, - 'warn': logging.WARNING, - 'error': logging.ERROR, - 'crit': logging.CRITICAL, + "trace": logging.TRACE, + "debug": logging.DEBUG, + "info": logging.INFO, + "warn": logging.WARNING, + "error": logging.ERROR, + "crit": logging.CRITICAL, } __func_alias__ = { - 'attach_': 'attach', - 'config_': 'config', - 'super_': 'super', + "attach_": "attach", + "config_": "config", + "super_": "super", } -HAS_BLKDISCARD = salt.utils.path.which('blkdiscard') is not None +HAS_BLKDISCARD = salt.utils.path.which("blkdiscard") is not None def __virtual__(): - ''' + """ Only work when make-bcache is installed - ''' - return salt.utils.path.which('make-bcache') is not None + """ + return salt.utils.path.which("make-bcache") is not None def uuid(dev=None): - ''' + """ Return the bcache UUID of a block device. If no device is given, the Cache UUID is returned. @@ -67,20 +67,20 @@ def uuid(dev=None): salt '*' bcache.uuid /dev/sda salt '*' bcache.uuid bcache0 - ''' + """ try: if dev is None: # take the only directory in /sys/fs/bcache and return its basename - return list(salt.utils.path.os_walk('/sys/fs/bcache/'))[0][1][0] + return list(salt.utils.path.os_walk("/sys/fs/bcache/"))[0][1][0] else: # basename of the /sys/block/{dev}/bcache/cache symlink target - return os.path.basename(_bcsys(dev, 'cache')) + return os.path.basename(_bcsys(dev, "cache")) except Exception: # pylint: disable=broad-except return False def attach_(dev=None): - ''' + """ Attach a backing devices to a cache set If no dev is given, all backing devices will be attached. @@ -93,16 +93,16 @@ def attach_(dev=None): :return: bool or None if nuttin' happened - ''' + """ cache = uuid() if not cache: - log.error('No cache to attach %s to', dev) + log.error("No cache to attach %s to", dev) return False if dev is None: res = {} for dev, data in status(alldevs=True).items(): - if 'cache' in data: + if "cache" in data: res[dev] = attach_(dev) if res: @@ -113,23 +113,31 @@ def attach_(dev=None): bcache = uuid(dev) if bcache: if bcache == cache: - log.info('%s is already attached to bcache %s, doing nothing', dev, cache) + log.info("%s is already attached to bcache %s, doing nothing", dev, cache) return None elif not detach(dev): return False - log.debug('Attaching %s to bcache %s', dev, cache) + log.debug("Attaching %s to bcache %s", dev, cache) - if not _bcsys(dev, 'attach', cache, - 'error', 'Error attaching {0} to bcache {1}'.format(dev, cache)): + if not _bcsys( + dev, + "attach", + cache, + "error", + "Error attaching {0} to bcache {1}".format(dev, cache), + ): return False - return _wait(lambda: uuid(dev) == cache, - 'error', '{0} received attach to bcache {1}, but did not comply'.format(dev, cache)) + return _wait( + lambda: uuid(dev) == cache, + "error", + "{0} received attach to bcache {1}, but did not comply".format(dev, cache), + ) def detach(dev=None): - ''' + """ Detach a backing device(s) from a cache set If no dev is given, all backing devices will be attached. @@ -143,11 +151,11 @@ def detach(dev=None): salt '*' bcache.detach sdc salt '*' bcache.detach bcache1 - ''' + """ if dev is None: res = {} for dev, data in status(alldevs=True).items(): - if 'cache' in data: + if "cache" in data: res[dev] = detach(dev) if res: @@ -155,14 +163,19 @@ def detach(dev=None): else: return None - log.debug('Detaching %s', dev) - if not _bcsys(dev, 'detach', 'goaway', 'error', 'Error detaching {0}'.format(dev)): + log.debug("Detaching %s", dev) + if not _bcsys(dev, "detach", "goaway", "error", "Error detaching {0}".format(dev)): return False - return _wait(lambda: uuid(dev) is False, 'error', '{0} received detach, but did not comply'.format(dev), 300) + return _wait( + lambda: uuid(dev) is False, + "error", + "{0} received detach, but did not comply".format(dev), + 300, + ) def start(): - ''' + """ Trigger a start of the full bcache system through udev. CLI example: @@ -171,16 +184,20 @@ def start(): salt '*' bcache.start - ''' - if not _run_all('udevadm trigger', 'error', 'Error starting bcache: %s'): + """ + if not _run_all("udevadm trigger", "error", "Error starting bcache: %s"): return False - elif not _wait(lambda: uuid() is not False, 'warn', 'Bcache system started, but no active cache set found.'): + elif not _wait( + lambda: uuid() is not False, + "warn", + "Bcache system started, but no active cache set found.", + ): return False return True def stop(dev=None): - ''' + """ Stop a bcache device If no device is given, all backing devices will be detached from the cache, which will subsequently be stopped. @@ -194,28 +211,33 @@ def stop(dev=None): salt '*' bcache.stop - ''' + """ if dev is not None: - log.warning('Stopping %s, device will only reappear after reregistering!', dev) - if not _bcsys(dev, 'stop', 'goaway', 'error', 'Error stopping {0}'.format(dev)): + log.warning("Stopping %s, device will only reappear after reregistering!", dev) + if not _bcsys(dev, "stop", "goaway", "error", "Error stopping {0}".format(dev)): return False - return _wait(lambda: _sysfs_attr(_bcpath(dev)) is False, 'error', 'Device {0} did not stop'.format(dev), 300) + return _wait( + lambda: _sysfs_attr(_bcpath(dev)) is False, + "error", + "Device {0} did not stop".format(dev), + 300, + ) else: cache = uuid() if not cache: - log.warning('bcache already stopped?') + log.warning("bcache already stopped?") return None if not _alltrue(detach()): return False - elif not _fssys('stop', 'goaway', 'error', 'Error stopping cache'): + elif not _fssys("stop", "goaway", "error", "Error stopping cache"): return False - return _wait(lambda: uuid() is False, 'error', 'Cache did not stop', 300) + return _wait(lambda: uuid() is False, "error", "Cache did not stop", 300) -def back_make(dev, cache_mode='writeback', force=False, attach=True, bucket_size=None): - ''' +def back_make(dev, cache_mode="writeback", force=False, attach=True, bucket_size=None): + """ Create a backing device for attachment to a set. Because the block size must be the same, a cache set already needs to exist. @@ -230,16 +252,18 @@ def back_make(dev, cache_mode='writeback', force=False, attach=True, bucket_size :param force: Overwrite existing bcaches :param attach: Immediately attach the backing device to the set :param bucket_size: Size of a bucket (see kernel doc) - ''' + """ # pylint: disable=too-many-return-statements cache = uuid() if not cache: - log.error('No bcache set found') + log.error("No bcache set found") return False elif _sysfs_attr(_bcpath(dev)): if not force: - log.error('%s already contains a bcache. Wipe it manually or use force', dev) + log.error( + "%s already contains a bcache. Wipe it manually or use force", dev + ) return False elif uuid(dev) and not detach(dev): return False @@ -247,22 +271,31 @@ def back_make(dev, cache_mode='writeback', force=False, attach=True, bucket_size return False dev = _devpath(dev) - block_size = _size_map(_fssys('block_size')) + block_size = _size_map(_fssys("block_size")) # You might want to override, we pick the cache set's as sane default if bucket_size is None: - bucket_size = _size_map(_fssys('bucket_size')) + bucket_size = _size_map(_fssys("bucket_size")) - cmd = 'make-bcache --block {0} --bucket {1} --{2} --bdev {3}'.format(block_size, bucket_size, cache_mode, dev) + cmd = "make-bcache --block {0} --bucket {1} --{2} --bdev {3}".format( + block_size, bucket_size, cache_mode, dev + ) if force: - cmd += ' --wipe-bcache' + cmd += " --wipe-bcache" - if not _run_all(cmd, 'error', 'Error creating backing device {0}: %s'.format(dev)): + if not _run_all(cmd, "error", "Error creating backing device {0}: %s".format(dev)): return False - elif not _sysfs_attr('fs/bcache/register', _devpath(dev), - 'error', 'Error registering backing device {0}'.format(dev)): + elif not _sysfs_attr( + "fs/bcache/register", + _devpath(dev), + "error", + "Error registering backing device {0}".format(dev), + ): return False - elif not _wait(lambda: _sysfs_attr(_bcpath(dev)) is not False, - 'error', 'Backing device {0} did not register'.format(dev)): + elif not _wait( + lambda: _sysfs_attr(_bcpath(dev)) is not False, + "error", + "Backing device {0} did not register".format(dev), + ): return False elif attach: return attach_(dev) @@ -270,8 +303,10 @@ def back_make(dev, cache_mode='writeback', force=False, attach=True, bucket_size return True -def cache_make(dev, reserved=None, force=False, block_size=None, bucket_size=None, attach=True): - ''' +def cache_make( + dev, reserved=None, force=False, block_size=None, bucket_size=None, attach=True +): + """ Create BCache cache on a block device. If blkdiscard is available the entire device will be properly cleared in advance. @@ -290,7 +325,7 @@ def cache_make(dev, reserved=None, force=False, block_size=None, bucket_size=Non :param block_size: Block size of the cache; defaults to devices' logical block size :param force: Overwrite existing BCache sets :param attach: Attach all existing backend devices immediately - ''' + """ # TODO: multiple devs == md jbod # pylint: disable=too-many-return-statements @@ -298,19 +333,21 @@ def cache_make(dev, reserved=None, force=False, block_size=None, bucket_size=Non cache = uuid() if cache: if not force: - log.error('BCache cache %s is already on the system', cache) + log.error("BCache cache %s is already on the system", cache) return False cache = _bdev() dev = _devbase(dev) - udev = __salt__['udev.env'](dev) + udev = __salt__["udev.env"](dev) - if ('ID_FS_TYPE' in udev or (udev.get('DEVTYPE', None) != 'partition' and 'ID_PART_TABLE_TYPE' in udev)) \ - and not force: - log.error('%s already contains data, wipe first or force', dev) + if ( + "ID_FS_TYPE" in udev + or (udev.get("DEVTYPE", None) != "partition" and "ID_PART_TABLE_TYPE" in udev) + ) and not force: + log.error("%s already contains data, wipe first or force", dev) return False - elif reserved is not None and udev.get('DEVTYPE', None) != 'disk': - log.error('Need a partitionable blockdev for reserved to work') + elif reserved is not None and udev.get("DEVTYPE", None) != "disk": + log.error("Need a partitionable blockdev for reserved to work") return False _, block, bucket = _sizes(dev) @@ -336,25 +373,36 @@ def cache_make(dev, reserved=None, force=False, block_size=None, bucket_size=Non return False if reserved: - cmd = 'parted -m -s -a optimal -- ' \ - '/dev/{0} mklabel gpt mkpart bcache-reserved 1M {1} mkpart bcache {1} 100%'.format(dev, reserved) + cmd = ( + "parted -m -s -a optimal -- " + "/dev/{0} mklabel gpt mkpart bcache-reserved 1M {1} mkpart bcache {1} 100%".format( + dev, reserved + ) + ) # if wipe was incomplete & part layout remains the same, # this is one condition set where udev would make it accidentally popup again - if not _run_all(cmd, 'error', 'Error creating bcache partitions on {0}: %s'.format(dev)): + if not _run_all( + cmd, "error", "Error creating bcache partitions on {0}: %s".format(dev) + ): return False - dev = '{0}2'.format(dev) + dev = "{0}2".format(dev) # ---------------- Finally, create a cache ---------------- - cmd = 'make-bcache --cache /dev/{0} --block {1} --wipe-bcache'.format(dev, block_size) + cmd = "make-bcache --cache /dev/{0} --block {1} --wipe-bcache".format( + dev, block_size + ) # Actually bucket_size should always have a value, but for testing 0 is possible as well if bucket_size: - cmd += ' --bucket {0}'.format(bucket_size) + cmd += " --bucket {0}".format(bucket_size) - if not _run_all(cmd, 'error', 'Error creating cache {0}: %s'.format(dev)): + if not _run_all(cmd, "error", "Error creating cache {0}: %s".format(dev)): return False - elif not _wait(lambda: uuid() is not False, - 'error', 'Cache {0} seemingly created OK, but FS did not activate'.format(dev)): + elif not _wait( + lambda: uuid() is not False, + "error", + "Cache {0} seemingly created OK, but FS did not activate".format(dev), + ): return False if attach: @@ -364,7 +412,7 @@ def cache_make(dev, reserved=None, force=False, block_size=None, bucket_size=Non def config_(dev=None, **kwargs): - ''' + """ Show or update config of a bcache device. If no device is given, operate on the cache set itself. @@ -379,25 +427,31 @@ def config_(dev=None, **kwargs): salt '*' bcache.config bcache1 cache_mode=writeback writeback_percent=15 :return: config or True/False - ''' + """ if dev is None: spath = _fspath() else: spath = _bcpath(dev) # filter out 'hidden' kwargs added by our favourite orchestration system - updates = dict([(key, val) for key, val in kwargs.items() if not key.startswith('__')]) + updates = dict( + [(key, val) for key, val in kwargs.items() if not key.startswith("__")] + ) if updates: endres = 0 for key, val in updates.items(): - endres += _sysfs_attr([spath, key], val, - 'warn', 'Failed to update {0} with {1}'.format(os.path.join(spath, key), val)) + endres += _sysfs_attr( + [spath, key], + val, + "warn", + "Failed to update {0} with {1}".format(os.path.join(spath, key), val), + ) return endres > 0 else: result = {} data = _sysfs_parse(spath, config=True, internals=True, options=True) - for key in ('other_ro', 'inter_ro'): + for key in ("other_ro", "inter_ro"): if key in data: del data[key] @@ -408,7 +462,7 @@ def config_(dev=None, **kwargs): def status(stats=False, config=False, internals=False, superblock=False, alldevs=False): - ''' + """ Show the full status of the BCache system and optionally all its involved devices CLI example: @@ -423,15 +477,17 @@ def status(stats=False, config=False, internals=False, superblock=False, alldevs :param config: include settings :param internals: include internals :param superblock: include superblock - ''' + """ bdevs = [] - for _, links, _ in salt.utils.path.os_walk('/sys/block/'): + for _, links, _ in salt.utils.path.os_walk("/sys/block/"): for block in links: - if 'bcache' in block: + if "bcache" in block: continue - for spath, sdirs, _ in salt.utils.path.os_walk('/sys/block/{0}'.format(block), followlinks=False): - if 'bcache' in sdirs: + for spath, sdirs, _ in salt.utils.path.os_walk( + "/sys/block/{0}".format(block), followlinks=False + ): + if "bcache" in sdirs: bdevs.append(os.path.basename(spath)) statii = {} for bcache in bdevs: @@ -444,9 +500,9 @@ def status(stats=False, config=False, internals=False, superblock=False, alldevs for dev in statii: if dev != cdev: # it's a backing dev - if statii[dev]['cache'] == cuuid: + if statii[dev]["cache"] == cuuid: count += 1 - statii[cdev]['attached_backing_devices'] = count + statii[cdev]["attached_backing_devices"] = count if not alldevs: statii = statii[cdev] @@ -455,7 +511,7 @@ def status(stats=False, config=False, internals=False, superblock=False, alldevs def device(dev, stats=False, config=False, internals=False, superblock=False): - ''' + """ Check the state of a single bcache device CLI example: @@ -469,106 +525,114 @@ def device(dev, stats=False, config=False, internals=False, superblock=False): :param settings: include all settings :param internals: include all internals :param superblock: include superblock info - ''' + """ result = {} - if not _sysfs_attr(_bcpath(dev), None, 'error', '{0} is not a bcache fo any kind'.format(dev)): + if not _sysfs_attr( + _bcpath(dev), None, "error", "{0} is not a bcache fo any kind".format(dev) + ): return False - elif _bcsys(dev, 'set'): + elif _bcsys(dev, "set"): # ---------------- It's the cache itself ---------------- - result['uuid'] = uuid() - base_attr = ['block_size', 'bucket_size', 'cache_available_percent', 'cache_replacement_policy', 'congested'] + result["uuid"] = uuid() + base_attr = [ + "block_size", + "bucket_size", + "cache_available_percent", + "cache_replacement_policy", + "congested", + ] # ---------------- Parse through both the blockdev & the FS ---------------- result.update(_sysfs_parse(_bcpath(dev), base_attr, stats, config, internals)) result.update(_sysfs_parse(_fspath(), base_attr, stats, config, internals)) - result.update(result.pop('base')) + result.update(result.pop("base")) else: # ---------------- It's a backing device ---------------- back_uuid = uuid(dev) if back_uuid is not None: - result['cache'] = back_uuid + result["cache"] = back_uuid try: - result['dev'] = os.path.basename(_bcsys(dev, 'dev')) + result["dev"] = os.path.basename(_bcsys(dev, "dev")) except Exception: # pylint: disable=broad-except pass - result['bdev'] = _bdev(dev) + result["bdev"] = _bdev(dev) - base_attr = ['cache_mode', 'running', 'state', 'writeback_running'] + base_attr = ["cache_mode", "running", "state", "writeback_running"] base_path = _bcpath(dev) result.update(_sysfs_parse(base_path, base_attr, stats, config, internals)) - result.update(result.pop('base')) + result.update(result.pop("base")) # ---------------- Modifications ---------------- - state = [result['state']] - if result.pop('running'): - state.append('running') + state = [result["state"]] + if result.pop("running"): + state.append("running") else: - state.append('stopped') - if 'writeback_running' in result: - if result.pop('writeback_running'): - state.append('writeback_running') + state.append("stopped") + if "writeback_running" in result: + if result.pop("writeback_running"): + state.append("writeback_running") else: - state.append('writeback_stopped') - result['state'] = state + state.append("writeback_stopped") + result["state"] = state # ---------------- Statistics ---------------- - if 'stats' in result: - replre = r'(stats|cache)_' - statres = result['stats'] - for attr in result['stats']: - if '/' not in attr: - key = re.sub(replre, '', attr) + if "stats" in result: + replre = r"(stats|cache)_" + statres = result["stats"] + for attr in result["stats"]: + if "/" not in attr: + key = re.sub(replre, "", attr) statres[key] = statres.pop(attr) else: - stat, key = attr.split('/', 1) - stat = re.sub(replre, '', stat) - key = re.sub(replre, '', key) + stat, key = attr.split("/", 1) + stat = re.sub(replre, "", stat) + key = re.sub(replre, "", key) if stat not in statres: statres[stat] = {} statres[stat][key] = statres.pop(attr) - result['stats'] = statres + result["stats"] = statres # ---------------- Internals ---------------- if internals: - interres = result.pop('inter_ro', {}) - interres.update(result.pop('inter_rw', {})) + interres = result.pop("inter_ro", {}) + interres.update(result.pop("inter_rw", {})) if interres: for key in interres: - if key.startswith('internal'): - nkey = re.sub(r'internal[s/]*', '', key) + if key.startswith("internal"): + nkey = re.sub(r"internal[s/]*", "", key) interres[nkey] = interres.pop(key) key = nkey - if key.startswith(('btree', 'writeback')): - mkey, skey = re.split(r'_', key, maxsplit=1) + if key.startswith(("btree", "writeback")): + mkey, skey = re.split(r"_", key, maxsplit=1) if mkey not in interres: interres[mkey] = {} interres[mkey][skey] = interres.pop(key) - result['internals'] = interres + result["internals"] = interres # ---------------- Config ---------------- if config: - configres = result['config'] + configres = result["config"] for key in configres: - if key.startswith('writeback'): - mkey, skey = re.split(r'_', key, maxsplit=1) + if key.startswith("writeback"): + mkey, skey = re.split(r"_", key, maxsplit=1) if mkey not in configres: configres[mkey] = {} configres[mkey][skey] = configres.pop(key) - result['config'] = configres + result["config"] = configres # ---------------- Superblock ---------------- if superblock: - result['superblock'] = super_(dev) + result["superblock"] = super_(dev) return result def super_(dev): - ''' + """ Read out BCache SuperBlock CLI example: @@ -578,11 +642,15 @@ def super_(dev): salt '*' bcache.device bcache0 salt '*' bcache.device /dev/sdc - ''' + """ dev = _devpath(dev) ret = {} - res = _run_all('bcache-super-show {0}'.format(dev), 'error', 'Error reading superblock on {0}: %s'.format(dev)) + res = _run_all( + "bcache-super-show {0}".format(dev), + "error", + "Error reading superblock on {0}: %s".format(dev), + ) if not res: return False @@ -591,13 +659,13 @@ def super_(dev): if not line: continue - key, val = [val.strip() for val in re.split(r'[\s]+', line, maxsplit=1)] + key, val = [val.strip() for val in re.split(r"[\s]+", line, maxsplit=1)] if not (key and val): continue mval = None - if ' ' in val: - rval, mval = [val.strip() for val in re.split(r'[\s]+', val, maxsplit=1)] + if " " in val: + rval, mval = [val.strip() for val in re.split(r"[\s]+", val, maxsplit=1)] mval = mval[1:-1] else: rval = val @@ -608,12 +676,12 @@ def super_(dev): try: rval = float(rval) except Exception: # pylint: disable=broad-except - if rval == 'yes': + if rval == "yes": rval = True - elif rval == 'no': + elif rval == "no": rval = False - pkey, key = re.split(r'\.', key, maxsplit=1) + pkey, key = re.split(r"\.", key, maxsplit=1) if pkey not in ret: ret[pkey] = {} @@ -624,75 +692,76 @@ def super_(dev): return ret + # -------------------------------- HELPER FUNCTIONS -------------------------------- def _devbase(dev): - ''' + """ Basename of just about any dev - ''' + """ dev = os.path.realpath(os.path.expandvars(dev)) dev = os.path.basename(dev) return dev def _devpath(dev): - ''' + """ Return /dev name of just about any dev :return: /dev/devicename - ''' - return os.path.join('/dev', _devbase(dev)) + """ + return os.path.join("/dev", _devbase(dev)) def _syspath(dev): - ''' + """ Full SysFS path of a device - ''' + """ dev = _devbase(dev) - dev = re.sub(r'^([vhs][a-z]+)([0-9]+)', r'\1/\1\2', dev) + dev = re.sub(r"^([vhs][a-z]+)([0-9]+)", r"\1/\1\2", dev) # name = re.sub(r'^([a-z]+)(?GPT) writes stuff at the end of a dev as well - cmd += ' seek={0}'.format((size/1024**2) - blocks) - endres += _run_all(cmd, 'warn', wipe_failmsg) + cmd += " seek={0}".format((size / 1024 ** 2) - blocks) + endres += _run_all(cmd, "warn", wipe_failmsg) - elif wiper == 'blkdiscard': - cmd = 'blkdiscard /dev/{0}'.format(dev) - endres += _run_all(cmd, 'warn', wipe_failmsg) + elif wiper == "blkdiscard": + cmd = "blkdiscard /dev/{0}".format(dev) + endres += _run_all(cmd, "warn", wipe_failmsg) # TODO: fix annoying bug failing blkdiscard by trying to discard 1 sector past blkdev endres = 1 @@ -925,10 +1026,10 @@ def _wipe(dev): def _wait(lfunc, log_lvl=None, log_msg=None, tries=10): - ''' + """ Wait for lfunc to be True :return: True if lfunc succeeded within tries, False if it didn't - ''' + """ i = 0 while i < tries: time.sleep(1) @@ -943,20 +1044,20 @@ def _wait(lfunc, log_lvl=None, log_msg=None, tries=10): def _run_all(cmd, log_lvl=None, log_msg=None, exitcode=0): - ''' + """ Simple wrapper around cmd.run_all log_msg can contain {0} for stderr :return: True or stdout, False if retcode wasn't exitcode - ''' - res = __salt__['cmd.run_all'](cmd) - if res['retcode'] == exitcode: - if res['stdout']: - return res['stdout'] + """ + res = __salt__["cmd.run_all"](cmd) + if res["retcode"] == exitcode: + if res["stdout"]: + return res["stdout"] else: return True if log_lvl is not None: - log.log(LOG[log_lvl], log_msg, res['stderr']) + log.log(LOG[log_lvl], log_msg, res["stderr"]) return False diff --git a/salt/modules/beacons.py b/salt/modules/beacons.py index 461901d56db..24a241d825d 100644 --- a/salt/modules/beacons.py +++ b/salt/modules/beacons.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing the Salt beacons on a minion .. versionadded:: 2015.8.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import difflib import logging import os @@ -23,17 +24,11 @@ from salt.ext.six.moves import map log = logging.getLogger(__name__) default_event_wait = 60 -__func_alias__ = { - 'list_': 'list', - 'reload_': 'reload' -} +__func_alias__ = {"list_": "list", "reload_": "reload"} -def list_(return_yaml=True, - include_pillar=True, - include_opts=True, - **kwargs): - ''' +def list_(return_yaml=True, include_pillar=True, include_opts=True, **kwargs): + """ List the beacons currently configured on the minion :param return_yaml: Whether to return YAML formatted output, @@ -53,41 +48,48 @@ def list_(return_yaml=True, salt '*' beacons.list - ''' + """ beacons = None try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'func': 'list', - 'include_pillar': include_pillar, - 'include_opts': include_opts}, - 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]( + { + "func": "list", + "include_pillar": include_pillar, + "include_opts": include_opts, + }, + "manage_beacons", + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacons_list_complete', - wait=kwargs.get('timeout', default_event_wait)) - log.debug('event_ret %s', event_ret) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] + tag="/salt/minion/minion_beacons_list_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + log.debug("event_ret %s", event_ret) + if event_ret and event_ret["complete"]: + beacons = event_ret["beacons"] except KeyError: # Effectively a no-op, since we can't really return without an event system ret = {} - ret['result'] = False - ret['comment'] = 'Event module not available. Beacon add failed.' + ret["result"] = False + ret["comment"] = "Event module not available. Beacon add failed." return ret if beacons: if return_yaml: - tmp = {'beacons': beacons} + tmp = {"beacons": beacons} return salt.utils.yaml.safe_dump(tmp, default_flow_style=False) else: return beacons else: - return {'beacons': {}} + return {"beacons": {}} def list_available(return_yaml=True, **kwargs): - ''' + """ List the beacons currently available on the minion :param return_yaml: Whether to return YAML formatted output, default @@ -100,37 +102,40 @@ def list_available(return_yaml=True, **kwargs): salt '*' beacons.list_available - ''' + """ beacons = None try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'func': 'list_available'}, 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]({"func": "list_available"}, "manage_beacons") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacons_list_available_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] + tag="/salt/minion/minion_beacons_list_available_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + if event_ret and event_ret["complete"]: + beacons = event_ret["beacons"] except KeyError: # Effectively a no-op, since we can't really return without an event system ret = {} - ret['result'] = False - ret['comment'] = 'Event module not available. Beacon add failed.' + ret["result"] = False + ret["comment"] = "Event module not available. Beacon add failed." return ret if beacons: if return_yaml: - tmp = {'beacons': beacons} + tmp = {"beacons": beacons} return salt.utils.yaml.safe_dump(tmp, default_flow_style=False) else: return beacons else: - return {'beacons': {}} + return {"beacons": {}} def add(name, beacon_data, **kwargs): - ''' + """ Add a beacon on the minion :param name: Name of the beacon to configure @@ -143,84 +148,98 @@ def add(name, beacon_data, **kwargs): salt '*' beacons.add ps "[{'processes': {'salt-master': 'stopped', 'apache2': 'stopped'}}]" - ''' - ret = {'comment': 'Failed to add beacon {0}.'.format(name), - 'result': False} + """ + ret = {"comment": "Failed to add beacon {0}.".format(name), "result": False} if name in list_(return_yaml=False, **kwargs): - ret['comment'] = 'Beacon {0} is already configured.'.format(name) + ret["comment"] = "Beacon {0} is already configured.".format(name) return ret # Check to see if a beacon_module is specified, if so, verify it is # valid and available beacon type. - if any('beacon_module' in key for key in beacon_data): - res = next(value for value in beacon_data if 'beacon_module' in value) - beacon_name = res['beacon_module'] + if any("beacon_module" in key for key in beacon_data): + res = next(value for value in beacon_data if "beacon_module" in value) + beacon_name = res["beacon_module"] else: beacon_name = name if beacon_name not in list_available(return_yaml=False): - ret['comment'] = 'Beacon "{0}" is not available.'.format(beacon_name) + ret["comment"] = 'Beacon "{0}" is not available.'.format(beacon_name) return ret - if 'test' in kwargs and kwargs['test']: - ret['result'] = True - ret['comment'] = 'Beacon: {0} would be added.'.format(name) + if "test" in kwargs and kwargs["test"]: + ret["result"] = True + ret["comment"] = "Beacon: {0} would be added.".format(name) else: try: # Attempt to load the beacon module so we have access to the validate function - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'name': name, - 'beacon_data': beacon_data, - 'func': 'validate_beacon'}, - 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]( + { + "name": name, + "beacon_data": beacon_data, + "func": "validate_beacon", + }, + "manage_beacons", + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacon_validation_complete', - wait=kwargs.get('timeout', default_event_wait)) - valid = event_ret['valid'] - vcomment = event_ret['vcomment'] + tag="/salt/minion/minion_beacon_validation_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + valid = event_ret["valid"] + vcomment = event_ret["vcomment"] if not valid: - ret['result'] = False - ret['comment'] = ('Beacon {0} configuration invalid, ' - 'not adding.\n{1}'.format(name, vcomment)) + ret["result"] = False + ret["comment"] = ( + "Beacon {0} configuration invalid, " + "not adding.\n{1}".format(name, vcomment) + ) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacon add failed.' + ret["comment"] = "Event module not available. Beacon add failed." try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'name': name, - 'beacon_data': beacon_data, - 'func': 'add'}, 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]( + {"name": name, "beacon_data": beacon_data, "func": "add"}, + "manage_beacons", + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacon_add_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] + tag="/salt/minion/minion_beacon_add_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + if event_ret and event_ret["complete"]: + beacons = event_ret["beacons"] if name in beacons and beacons[name] == beacon_data: - ret['result'] = True - ret['comment'] = 'Added beacon: {0}.'.format(name) + ret["result"] = True + ret["comment"] = "Added beacon: {0}.".format(name) elif event_ret: - ret['result'] = False - ret['comment'] = event_ret['comment'] + ret["result"] = False + ret["comment"] = event_ret["comment"] else: - ret['result'] = False - ret['comment'] = 'Did not receive the beacon add complete event before the timeout of {}s'.format( - kwargs.get('timeout', default_event_wait) + ret["result"] = False + ret[ + "comment" + ] = "Did not receive the beacon add complete event before the timeout of {}s".format( + kwargs.get("timeout", default_event_wait) ) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacon add failed.' + ret["comment"] = "Event module not available. Beacon add failed." return ret def modify(name, beacon_data, **kwargs): - ''' + """ Modify an existing beacon :param name: Name of the beacon to configure @@ -232,99 +251,125 @@ def modify(name, beacon_data, **kwargs): .. code-block:: bash salt '*' beacons.modify ps "[{'salt-master': 'stopped'}, {'apache2': 'stopped'}]" - ''' + """ - ret = {'comment': '', - 'result': True} + ret = {"comment": "", "result": True} current_beacons = list_(return_yaml=False, **kwargs) if name not in current_beacons: - ret['comment'] = 'Beacon {0} is not configured.'.format(name) + ret["comment"] = "Beacon {0} is not configured.".format(name) return ret - if 'test' in kwargs and kwargs['test']: - ret['result'] = True - ret['comment'] = 'Beacon: {0} would be added.'.format(name) + if "test" in kwargs and kwargs["test"]: + ret["result"] = True + ret["comment"] = "Beacon: {0} would be added.".format(name) else: try: # Attempt to load the beacon module so we have access to the validate function - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'name': name, - 'beacon_data': beacon_data, - 'func': 'validate_beacon'}, - 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]( + { + "name": name, + "beacon_data": beacon_data, + "func": "validate_beacon", + }, + "manage_beacons", + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacon_validation_complete', - wait=kwargs.get('timeout', default_event_wait)) - valid = event_ret['valid'] - vcomment = event_ret['vcomment'] + tag="/salt/minion/minion_beacon_validation_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + valid = event_ret["valid"] + vcomment = event_ret["vcomment"] if not valid: - ret['result'] = False - ret['comment'] = ('Beacon {0} configuration invalid, ' - 'not adding.\n{1}'.format(name, vcomment)) + ret["result"] = False + ret["comment"] = ( + "Beacon {0} configuration invalid, " + "not adding.\n{1}".format(name, vcomment) + ) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacon modify failed.' + ret["comment"] = "Event module not available. Beacon modify failed." if not valid: - ret['result'] = False - ret['comment'] = ('Beacon {0} configuration invalid, ' - 'not modifying.\n{1}'.format(name, vcomment)) + ret["result"] = False + ret["comment"] = ( + "Beacon {0} configuration invalid, " + "not modifying.\n{1}".format(name, vcomment) + ) return ret _current = current_beacons[name] _new = beacon_data if _new == _current: - ret['comment'] = 'Job {0} in correct state'.format(name) + ret["comment"] = "Job {0} in correct state".format(name) return ret _current_lines = [] for _item in _current: - _current_lines.extend(['{0}:{1}\n'.format(key, value) - for (key, value) in six.iteritems(_item)]) + _current_lines.extend( + [ + "{0}:{1}\n".format(key, value) + for (key, value) in six.iteritems(_item) + ] + ) _new_lines = [] for _item in _new: - _new_lines.extend(['{0}:{1}\n'.format(key, value) - for (key, value) in six.iteritems(_item)]) + _new_lines.extend( + [ + "{0}:{1}\n".format(key, value) + for (key, value) in six.iteritems(_item) + ] + ) _diff = difflib.unified_diff(_current_lines, _new_lines) - ret['changes'] = {} - ret['changes']['diff'] = ''.join(_diff) + ret["changes"] = {} + ret["changes"]["diff"] = "".join(_diff) try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'name': name, 'beacon_data': beacon_data, 'func': 'modify'}, 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]( + {"name": name, "beacon_data": beacon_data, "func": "modify"}, + "manage_beacons", + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacon_modify_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] + tag="/salt/minion/minion_beacon_modify_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + if event_ret and event_ret["complete"]: + beacons = event_ret["beacons"] if name in beacons and beacons[name] == beacon_data: - ret['result'] = True - ret['comment'] = 'Modified beacon: {0}.'.format(name) + ret["result"] = True + ret["comment"] = "Modified beacon: {0}.".format(name) elif event_ret: - ret['result'] = False - ret['comment'] = event_ret['comment'] + ret["result"] = False + ret["comment"] = event_ret["comment"] else: - ret['result'] = False - ret['comment'] = 'Did not receive the beacon modify complete event before the timeout of {}s'.format( - kwargs.get('timeout', default_event_wait) + ret["result"] = False + ret[ + "comment" + ] = "Did not receive the beacon modify complete event before the timeout of {}s".format( + kwargs.get("timeout", default_event_wait) ) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacon add failed.' + ret["comment"] = "Event module not available. Beacon add failed." return ret def delete(name, **kwargs): - ''' + """ Delete a beacon item :param name: Name of the beacon to delete @@ -338,44 +383,50 @@ def delete(name, **kwargs): salt '*' beacons.delete load - ''' + """ - ret = {'comment': 'Failed to delete beacon {0}.'.format(name), - 'result': False} + ret = {"comment": "Failed to delete beacon {0}.".format(name), "result": False} - if 'test' in kwargs and kwargs['test']: - ret['result'] = True - ret['comment'] = 'Beacon: {0} would be deleted.'.format(name) + if "test" in kwargs and kwargs["test"]: + ret["result"] = True + ret["comment"] = "Beacon: {0} would be deleted.".format(name) else: try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'name': name, 'func': 'delete'}, 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]( + {"name": name, "func": "delete"}, "manage_beacons" + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacon_delete_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] + tag="/salt/minion/minion_beacon_delete_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + if event_ret and event_ret["complete"]: + beacons = event_ret["beacons"] if name not in beacons: - ret['result'] = True - ret['comment'] = 'Deleted beacon: {0}.'.format(name) + ret["result"] = True + ret["comment"] = "Deleted beacon: {0}.".format(name) return ret elif event_ret: - ret['result'] = False - ret['comment'] = event_ret['comment'] + ret["result"] = False + ret["comment"] = event_ret["comment"] else: - ret['result'] = False - ret['comment'] = 'Did not receive the beacon delete complete event before the timeout of {}s'.format( - kwargs.get('timeout', default_event_wait) + ret["result"] = False + ret[ + "comment" + ] = "Did not receive the beacon delete complete event before the timeout of {}s".format( + kwargs.get("timeout", default_event_wait) ) except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacon add failed.' + ret["comment"] = "Event module not available. Beacon add failed." return ret def save(**kwargs): - ''' + """ Save all beacons on the minion :return: Boolean and status message on success or failure of save. @@ -385,36 +436,38 @@ def save(**kwargs): .. code-block:: bash salt '*' beacons.save - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} beacons = list_(return_yaml=False, include_pillar=False, **kwargs) # move this file into an configurable opt - sfn = os.path.join(os.path.dirname(__opts__['conf_file']), - os.path.dirname(__opts__['default_include']), - 'beacons.conf') + sfn = os.path.join( + os.path.dirname(__opts__["conf_file"]), + os.path.dirname(__opts__["default_include"]), + "beacons.conf", + ) if beacons: - tmp = {'beacons': beacons} + tmp = {"beacons": beacons} yaml_out = salt.utils.yaml.safe_dump(tmp, default_flow_style=False) else: - yaml_out = '' + yaml_out = "" try: - with salt.utils.files.fopen(sfn, 'w+') as fp_: + with salt.utils.files.fopen(sfn, "w+") as fp_: fp_.write(yaml_out) - ret['comment'] = 'Beacons saved to {0}.'.format(sfn) + ret["comment"] = "Beacons saved to {0}.".format(sfn) except (IOError, OSError): - ret['comment'] = 'Unable to write to beacons file at {0}. Check ' \ - 'permissions.'.format(sfn) - ret['result'] = False + ret[ + "comment" + ] = "Unable to write to beacons file at {0}. Check " "permissions.".format(sfn) + ret["result"] = False return ret def enable(**kwargs): - ''' + """ Enable all beacons on the minion Returns: @@ -425,43 +478,47 @@ def enable(**kwargs): .. code-block:: bash salt '*' beacons.enable - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Beacons would be enabled.' + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Beacons would be enabled." else: try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'func': 'enable'}, 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]({"func": "enable"}, "manage_beacons") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacons_enabled_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] - if 'enabled' in beacons and beacons['enabled']: - ret['result'] = True - ret['comment'] = 'Enabled beacons on minion.' + tag="/salt/minion/minion_beacons_enabled_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + if event_ret and event_ret["complete"]: + beacons = event_ret["beacons"] + if "enabled" in beacons and beacons["enabled"]: + ret["result"] = True + ret["comment"] = "Enabled beacons on minion." elif event_ret: - ret['result'] = False - ret['comment'] = 'Failed to enable beacons on minion.' + ret["result"] = False + ret["comment"] = "Failed to enable beacons on minion." else: - ret['result'] = False - ret['comment'] = 'Did not receive the beacon enabled complete event before the timeout of {}s'.format( - kwargs.get('timeout', default_event_wait) + ret["result"] = False + ret[ + "comment" + ] = "Did not receive the beacon enabled complete event before the timeout of {}s".format( + kwargs.get("timeout", default_event_wait) ) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacons enable job failed.' + ret["comment"] = "Event module not available. Beacons enable job failed." return ret def disable(**kwargs): - ''' + """ Disable all beacons jobs on the minion :return: Boolean and status message on success or failure of disable. @@ -471,39 +528,43 @@ def disable(**kwargs): .. code-block:: bash salt '*' beacons.disable - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Beacons would be disabled.' + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Beacons would be disabled." else: try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'func': 'disable'}, 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]({"func": "disable"}, "manage_beacons") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacons_disabled_complete', - wait=kwargs.get('timeout', default_event_wait)) - log.debug('event_ret %s', event_ret) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] - if 'enabled' in beacons and not beacons['enabled']: - ret['result'] = True - ret['comment'] = 'Disabled beacons on minion.' + tag="/salt/minion/minion_beacons_disabled_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + log.debug("event_ret %s", event_ret) + if event_ret and event_ret["complete"]: + beacons = event_ret["beacons"] + if "enabled" in beacons and not beacons["enabled"]: + ret["result"] = True + ret["comment"] = "Disabled beacons on minion." elif event_ret: - ret['result'] = False - ret['comment'] = 'Failed to disable beacons on minion.' + ret["result"] = False + ret["comment"] = "Failed to disable beacons on minion." else: - ret['result'] = False - ret['comment'] = 'Did not receive the beacon disabled complete event before the timeout of {}s'.format( - kwargs.get('timeout', default_event_wait) + ret["result"] = False + ret[ + "comment" + ] = "Did not receive the beacon disabled complete event before the timeout of {}s".format( + kwargs.get("timeout", default_event_wait) ) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacons enable job failed.' + ret["comment"] = "Event module not available. Beacons enable job failed." return ret @@ -518,7 +579,7 @@ def _get_beacon_config_dict(beacon_config): def enable_beacon(name, **kwargs): - ''' + """ Enable beacon on the minion :name: Name of the beacon to enable. @@ -529,59 +590,72 @@ def enable_beacon(name, **kwargs): .. code-block:: bash salt '*' beacons.enable_beacon ps - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Beacon name is required.' - ret['result'] = False + ret["comment"] = "Beacon name is required." + ret["result"] = False return ret - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Beacon {0} would be enabled.'.format(name) + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Beacon {0} would be enabled.".format(name) else: _beacons = list_(return_yaml=False, **kwargs) if name not in _beacons: - ret['comment'] = 'Beacon {0} is not currently configured.'.format(name) - ret['result'] = False + ret["comment"] = "Beacon {0} is not currently configured.".format(name) + ret["result"] = False return ret try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'func': 'enable_beacon', 'name': name}, 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]( + {"func": "enable_beacon", "name": name}, "manage_beacons" + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacon_enabled_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] + tag="/salt/minion/minion_beacon_enabled_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + if event_ret and event_ret["complete"]: + beacons = event_ret["beacons"] beacon_config_dict = _get_beacon_config_dict(beacons[name]) - if 'enabled' in beacon_config_dict and beacon_config_dict['enabled']: - ret['result'] = True - ret['comment'] = 'Enabled beacon {0} on minion.'.format(name) + if ( + "enabled" in beacon_config_dict + and beacon_config_dict["enabled"] + ): + ret["result"] = True + ret["comment"] = "Enabled beacon {0} on minion.".format( + name + ) else: - ret['result'] = False - ret['comment'] = 'Failed to enable beacon {0} on minion.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "Failed to enable beacon {0} on minion.".format(name) elif event_ret: - ret['result'] = False - ret['comment'] = event_ret['comment'] + ret["result"] = False + ret["comment"] = event_ret["comment"] else: - ret['result'] = False - ret['comment'] = 'Did not receive the beacon enabled complete event before the timeout of {}s'.format( - kwargs.get('timeout', default_event_wait) + ret["result"] = False + ret[ + "comment" + ] = "Did not receive the beacon enabled complete event before the timeout of {}s".format( + kwargs.get("timeout", default_event_wait) ) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacon enable job failed.' + ret["comment"] = "Event module not available. Beacon enable job failed." return ret def disable_beacon(name, **kwargs): - ''' + """ Disable beacon on the minion :name: Name of the beacon to disable. @@ -592,59 +666,70 @@ def disable_beacon(name, **kwargs): .. code-block:: bash salt '*' beacons.disable_beacon ps - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Beacon name is required.' - ret['result'] = False + ret["comment"] = "Beacon name is required." + ret["result"] = False return ret - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Beacons would be enabled.' + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Beacons would be enabled." else: _beacons = list_(return_yaml=False, **kwargs) if name not in _beacons: - ret['comment'] = 'Beacon {0} is not currently configured.'.format(name) - ret['result'] = False + ret["comment"] = "Beacon {0} is not currently configured.".format(name) + ret["result"] = False return ret try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'func': 'disable_beacon', 'name': name}, 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]( + {"func": "disable_beacon", "name": name}, "manage_beacons" + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacon_disabled_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - beacons = event_ret['beacons'] + tag="/salt/minion/minion_beacon_disabled_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + if event_ret and event_ret["complete"]: + beacons = event_ret["beacons"] beacon_config_dict = _get_beacon_config_dict(beacons[name]) - if 'enabled' in beacon_config_dict and not beacon_config_dict['enabled']: - ret['result'] = True - ret['comment'] = 'Disabled beacon {0} on minion.'.format(name) + if ( + "enabled" in beacon_config_dict + and not beacon_config_dict["enabled"] + ): + ret["result"] = True + ret["comment"] = "Disabled beacon {0} on minion.".format( + name + ) else: - ret['result'] = False - ret['comment'] = 'Failed to disable beacon on minion.' + ret["result"] = False + ret["comment"] = "Failed to disable beacon on minion." elif event_ret: - ret['result'] = False - ret['comment'] = event_ret['comment'] + ret["result"] = False + ret["comment"] = event_ret["comment"] else: - ret['result'] = False - ret['comment'] = 'Did not receive the beacon disabled complete event before the timeout of {}s'.format( - kwargs.get('timeout', default_event_wait) + ret["result"] = False + ret[ + "comment" + ] = "Did not receive the beacon disabled complete event before the timeout of {}s".format( + kwargs.get("timeout", default_event_wait) ) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacon disable job failed.' + ret["comment"] = "Event module not available. Beacon disable job failed." return ret def reset(**kwargs): - ''' + """ Resest beacon configuration on the minion CLI Example: @@ -652,32 +737,34 @@ def reset(**kwargs): .. code-block:: bash salt '*' beacons.reset - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} - if kwargs.get('test'): - ret['comment'] = 'Beacons would be reset.' + if kwargs.get("test"): + ret["comment"] = "Beacons would be reset." else: try: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - res = __salt__['event.fire']({'func': 'reset'}, 'manage_beacons') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + res = __salt__["event.fire"]({"func": "reset"}, "manage_beacons") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_beacon_reset_complete', - wait=kwargs.get('timeout', default_event_wait)) - if event_ret and event_ret['complete']: - ret['result'] = True - ret['comment'] = 'Beacon configuration reset.' + tag="/salt/minion/minion_beacon_reset_complete", + wait=kwargs.get("timeout", default_event_wait), + ) + if event_ret and event_ret["complete"]: + ret["result"] = True + ret["comment"] = "Beacon configuration reset." else: - ret['result'] = False + ret["result"] = False if ret is not None: - ret['comment'] = event_ret['comment'] + ret["comment"] = event_ret["comment"] else: - ret['comment'] = 'Beacon reset event never received' + ret["comment"] = "Beacon reset event never received" return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Beacon disable job failed.' + ret["comment"] = "Event module not available. Beacon disable job failed." return ret diff --git a/salt/modules/bigip.py b/salt/modules/bigip.py index 3737bf2ad95..2b54e4d27c5 100644 --- a/salt/modules/bigip.py +++ b/salt/modules/bigip.py @@ -1,102 +1,114 @@ # -*- coding: utf-8 -*- -''' +""" An execution module which can manipulate an f5 bigip via iControl REST :maturity: develop :platform: f5_bigip_11.6 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +# Import salt libs +import salt.exceptions import salt.utils.json +# Import 3rd-party libs +from salt.ext import six + # Import third party libs try: import requests import requests.exceptions + HAS_LIBS = True except ImportError: HAS_LIBS = False -# Import 3rd-party libs -from salt.ext import six - -# Import salt libs -import salt.exceptions # Define the module's virtual name -__virtualname__ = 'bigip' +__virtualname__ = "bigip" def __virtual__(): - ''' + """ Only return if requests is installed - ''' + """ if HAS_LIBS: return __virtualname__ - return (False, 'The bigip execution module cannot be loaded: ' - 'python requests library not available.') + return ( + False, + "The bigip execution module cannot be loaded: " + "python requests library not available.", + ) -BIG_IP_URL_BASE = 'https://{host}/mgmt/tm' +BIG_IP_URL_BASE = "https://{host}/mgmt/tm" def _build_session(username, password, trans_label=None): - ''' + """ Create a session to be used when connecting to iControl REST. - ''' + """ bigip = requests.session() bigip.auth = (username, password) bigip.verify = False - bigip.headers.update({'Content-Type': 'application/json'}) + bigip.headers.update({"Content-Type": "application/json"}) if trans_label: - #pull the trans id from the grain - trans_id = __salt__['grains.get']('bigip_f5_trans:{label}'.format(label=trans_label)) + # pull the trans id from the grain + trans_id = __salt__["grains.get"]( + "bigip_f5_trans:{label}".format(label=trans_label) + ) if trans_id: - bigip.headers.update({'X-F5-REST-Coordination-Id': trans_id}) + bigip.headers.update({"X-F5-REST-Coordination-Id": trans_id}) else: - bigip.headers.update({'X-F5-REST-Coordination-Id': None}) + bigip.headers.update({"X-F5-REST-Coordination-Id": None}) return bigip def _load_response(response): - ''' + """ Load the response from json data, return the dictionary or raw text - ''' + """ try: data = salt.utils.json.loads(response.text) except ValueError: data = response.text - ret = {'code': response.status_code, 'content': data} + ret = {"code": response.status_code, "content": data} return ret def _load_connection_error(hostname, error): - ''' + """ Format and Return a connection error - ''' + """ - ret = {'code': None, 'content': 'Error: Unable to connect to the bigip device: {host}\n{error}'.format(host=hostname, error=error)} + ret = { + "code": None, + "content": "Error: Unable to connect to the bigip device: {host}\n{error}".format( + host=hostname, error=error + ), + } return ret def _loop_payload(params): - ''' + """ Pass in a dictionary of parameters, loop through them and build a payload containing, parameters who's values are not None. - ''' + """ - #construct the payload + # construct the payload payload = {} - #set the payload + # set the payload for param, value in six.iteritems(params): if value is not None: payload[param] = value @@ -105,22 +117,22 @@ def _loop_payload(params): def _build_list(option_value, item_kind): - ''' + """ pass in an option to check for a list of items, create a list of dictionary of items to set for this option - ''' - #specify profiles if provided + """ + # specify profiles if provided if option_value is not None: items = [] - #if user specified none, return an empty list - if option_value == 'none': + # if user specified none, return an empty list + if option_value == "none": return items - #was a list already passed in? + # was a list already passed in? if not isinstance(option_value, list): - values = option_value.split(',') + values = option_value.split(",") else: values = option_value @@ -130,97 +142,105 @@ def _build_list(option_value, item_kind): items.append(value) # other times it's picky and likes key value pairs... else: - items.append({'kind': item_kind, 'name': value}) + items.append({"kind": item_kind, "name": value}) return items return None def _determine_toggles(payload, toggles): - ''' + """ BigIP can't make up its mind if it likes yes / no or true or false. Figure out what it likes to hear without confusing the user. - ''' + """ for toggle, definition in six.iteritems(toggles): - #did the user specify anything? - if definition['value'] is not None: - #test for yes_no toggle - if (definition['value'] is True or definition['value'] == 'yes') and definition['type'] == 'yes_no': - payload[toggle] = 'yes' - elif (definition['value'] is False or definition['value'] == 'no') and definition['type'] == 'yes_no': - payload[toggle] = 'no' + # did the user specify anything? + if definition["value"] is not None: + # test for yes_no toggle + if ( + definition["value"] is True or definition["value"] == "yes" + ) and definition["type"] == "yes_no": + payload[toggle] = "yes" + elif ( + definition["value"] is False or definition["value"] == "no" + ) and definition["type"] == "yes_no": + payload[toggle] = "no" - #test for true_false toggle - if (definition['value'] is True or definition['value'] == 'yes') and definition['type'] == 'true_false': + # test for true_false toggle + if ( + definition["value"] is True or definition["value"] == "yes" + ) and definition["type"] == "true_false": payload[toggle] = True - elif (definition['value'] is False or definition['value'] == 'no') and definition['type'] == 'true_false': + elif ( + definition["value"] is False or definition["value"] == "no" + ) and definition["type"] == "true_false": payload[toggle] = False return payload def _set_value(value): - ''' + """ A function to detect if user is trying to pass a dictionary or list. parse it and return a dictionary list or a string - ''' - #don't continue if already an acceptable data-type + """ + # don't continue if already an acceptable data-type if isinstance(value, bool) or isinstance(value, dict) or isinstance(value, list): return value - #check if json - if value.startswith('j{') and value.endswith('}j'): + # check if json + if value.startswith("j{") and value.endswith("}j"): - value = value.replace('j{', '{') - value = value.replace('}j', '}') + value = value.replace("j{", "{") + value = value.replace("}j", "}") try: return salt.utils.json.loads(value) except Exception: # pylint: disable=broad-except raise salt.exceptions.CommandExecutionError - #detect list of dictionaries - if '|' in value and r'\|' not in value: - values = value.split('|') + # detect list of dictionaries + if "|" in value and r"\|" not in value: + values = value.split("|") items = [] for value in values: items.append(_set_value(value)) return items - #parse out dictionary if detected - if ':' in value and r'\:' not in value: + # parse out dictionary if detected + if ":" in value and r"\:" not in value: options = {} - #split out pairs - key_pairs = value.split(',') + # split out pairs + key_pairs = value.split(",") for key_pair in key_pairs: - k = key_pair.split(':')[0] - v = key_pair.split(':')[1] + k = key_pair.split(":")[0] + v = key_pair.split(":")[1] options[k] = v return options - #try making a list - elif ',' in value and r'\,' not in value: - value_items = value.split(',') + # try making a list + elif "," in value and r"\," not in value: + value_items = value.split(",") return value_items - #just return a string + # just return a string else: - #remove escape chars if added - if r'\|' in value: - value = value.replace(r'\|', '|') + # remove escape chars if added + if r"\|" in value: + value = value.replace(r"\|", "|") - if r'\:' in value: - value = value.replace(r'\:', ':') + if r"\:" in value: + value = value.replace(r"\:", ":") - if r'\,' in value: - value = value.replace(r'\,', ',') + if r"\," in value: + value = value.replace(r"\,", ",") return value def start_transaction(hostname, username, password, label): - ''' + """ A function to connect to a bigip device and start a new transaction. hostname @@ -237,39 +257,40 @@ def start_transaction(hostname, username, password, label): salt '*' bigip.start_transaction bigip admin admin my_transaction - ''' + """ - #build the session + # build the session bigip_session = _build_session(username, password) payload = {} - #post to REST to get trans id + # post to REST to get trans id try: response = bigip_session.post( - BIG_IP_URL_BASE.format(host=hostname) + '/transaction', - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + "/transaction", + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) - #extract the trans_id + # extract the trans_id data = _load_response(response) - if data['code'] == 200: + if data["code"] == 200: - trans_id = data['content']['transId'] + trans_id = data["content"]["transId"] - __salt__['grains.setval']('bigip_f5_trans', {label: trans_id}) + __salt__["grains.setval"]("bigip_f5_trans", {label: trans_id}) - return 'Transaction: {trans_id} - has successfully been stored in the grain: bigip_f5_trans:{label}'.format(trans_id=trans_id, - label=label) + return "Transaction: {trans_id} - has successfully been stored in the grain: bigip_f5_trans:{label}".format( + trans_id=trans_id, label=label + ) else: return data def list_transaction(hostname, username, password, label): - ''' + """ A function to connect to a bigip device and list an existing transaction. hostname @@ -286,29 +307,34 @@ def list_transaction(hostname, username, password, label): salt '*' bigip.list_transaction bigip admin admin my_transaction - ''' + """ - #build the session + # build the session bigip_session = _build_session(username, password) - #pull the trans id from the grain - trans_id = __salt__['grains.get']('bigip_f5_trans:{label}'.format(label=label)) + # pull the trans id from the grain + trans_id = __salt__["grains.get"]("bigip_f5_trans:{label}".format(label=label)) if trans_id: - #post to REST to get trans id + # post to REST to get trans id try: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/transaction/{trans_id}/commands'.format(trans_id=trans_id)) + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + + "/transaction/{trans_id}/commands".format(trans_id=trans_id) + ) return _load_response(response) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) else: - return 'Error: the label for this transaction was not defined as a grain. Begin a new transaction using the' \ - ' bigip.start_transaction function' + return ( + "Error: the label for this transaction was not defined as a grain. Begin a new transaction using the" + " bigip.start_transaction function" + ) def commit_transaction(hostname, username, password, label): - ''' + """ A function to connect to a bigip device and commit an existing transaction. hostname @@ -324,35 +350,38 @@ def commit_transaction(hostname, username, password, label): CLI Example:: salt '*' bigip.commit_transaction bigip admin admin my_transaction - ''' + """ - #build the session + # build the session bigip_session = _build_session(username, password) - #pull the trans id from the grain - trans_id = __salt__['grains.get']('bigip_f5_trans:{label}'.format(label=label)) + # pull the trans id from the grain + trans_id = __salt__["grains.get"]("bigip_f5_trans:{label}".format(label=label)) if trans_id: payload = {} - payload['state'] = 'VALIDATING' + payload["state"] = "VALIDATING" - #patch to REST to get trans id + # patch to REST to get trans id try: response = bigip_session.patch( - BIG_IP_URL_BASE.format(host=hostname) + '/transaction/{trans_id}'.format(trans_id=trans_id), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/transaction/{trans_id}".format(trans_id=trans_id), + data=salt.utils.json.dumps(payload), ) return _load_response(response) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) else: - return 'Error: the label for this transaction was not defined as a grain. Begin a new transaction using the' \ - ' bigip.start_transaction function' + return ( + "Error: the label for this transaction was not defined as a grain. Begin a new transaction using the" + " bigip.start_transaction function" + ) def delete_transaction(hostname, username, password, label): - ''' + """ A function to connect to a bigip device and delete an existing transaction. hostname @@ -368,29 +397,34 @@ def delete_transaction(hostname, username, password, label): CLI Example:: salt '*' bigip.delete_transaction bigip admin admin my_transaction - ''' + """ - #build the session + # build the session bigip_session = _build_session(username, password) - #pull the trans id from the grain - trans_id = __salt__['grains.get']('bigip_f5_trans:{label}'.format(label=label)) + # pull the trans id from the grain + trans_id = __salt__["grains.get"]("bigip_f5_trans:{label}".format(label=label)) if trans_id: - #patch to REST to get trans id + # patch to REST to get trans id try: - response = bigip_session.delete(BIG_IP_URL_BASE.format(host=hostname)+'/transaction/{trans_id}'.format(trans_id=trans_id)) + response = bigip_session.delete( + BIG_IP_URL_BASE.format(host=hostname) + + "/transaction/{trans_id}".format(trans_id=trans_id) + ) return _load_response(response) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) else: - return 'Error: the label for this transaction was not defined as a grain. Begin a new transaction using the' \ - ' bigip.start_transaction function' + return ( + "Error: the label for this transaction was not defined as a grain. Begin a new transaction using the" + " bigip.start_transaction function" + ) def list_node(hostname, username, password, name=None, trans_label=None): - ''' + """ A function to connect to a bigip device and list all nodes or a specific node. @@ -410,17 +444,22 @@ def list_node(hostname, username, password, name=None, trans_label=None): CLI Example:: salt '*' bigip.list_node bigip admin admin my-node - ''' + """ - #build sessions + # build sessions bigip_session = _build_session(username, password, trans_label) - #get to REST + # get to REST try: if name: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/node/{name}'.format(name=name)) + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/node/{name}".format(name=name) + ) else: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/node') + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + "/ltm/node" + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -428,7 +467,7 @@ def list_node(hostname, username, password, name=None, trans_label=None): def create_node(hostname, username, password, name, address, trans_label=None): - ''' + """ A function to connect to a bigip device and create a node. hostname @@ -448,39 +487,45 @@ def create_node(hostname, username, password, name, address, trans_label=None): CLI Example:: salt '*' bigip.create_node bigip admin admin 10.1.1.2 - ''' + """ - #build session + # build session bigip_session = _build_session(username, password, trans_label) - #construct the payload + # construct the payload payload = {} - payload['name'] = name - payload['address'] = address + payload["name"] = name + payload["address"] = address - #post to REST + # post to REST try: response = bigip_session.post( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/node', - data=salt.utils.json.dumps(payload)) + BIG_IP_URL_BASE.format(host=hostname) + "/ltm/node", + data=salt.utils.json.dumps(payload), + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) return _load_response(response) -def modify_node(hostname, username, password, name, - connection_limit=None, - description=None, - dynamic_ratio=None, - logging=None, - monitor=None, - rate_limit=None, - ratio=None, - session=None, - state=None, - trans_label=None): - ''' +def modify_node( + hostname, + username, + password, + name, + connection_limit=None, + description=None, + dynamic_ratio=None, + logging=None, + monitor=None, + rate_limit=None, + ratio=None, + session=None, + state=None, + trans_label=None, +): + """ A function to connect to a bigip device and modify an existing node. hostname @@ -516,32 +561,33 @@ def modify_node(hostname, username, password, name, CLI Example:: salt '*' bigip.modify_node bigip admin admin 10.1.1.2 ratio=2 logging=enabled - ''' + """ params = { - 'connection-limit': connection_limit, - 'description': description, - 'dynamic-ratio': dynamic_ratio, - 'logging': logging, - 'monitor': monitor, - 'rate-limit': rate_limit, - 'ratio': ratio, - 'session': session, - 'state': state, + "connection-limit": connection_limit, + "description": description, + "dynamic-ratio": dynamic_ratio, + "logging": logging, + "monitor": monitor, + "rate-limit": rate_limit, + "ratio": ratio, + "session": session, + "state": state, } - #build session + # build session bigip_session = _build_session(username, password, trans_label) - #build payload + # build payload payload = _loop_payload(params) - payload['name'] = name + payload["name"] = name - #put to REST + # put to REST try: response = bigip_session.put( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/node/{name}'.format(name=name), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/node/{name}".format(name=name), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -550,7 +596,7 @@ def modify_node(hostname, username, password, name, def delete_node(hostname, username, password, name, trans_label=None): - ''' + """ A function to connect to a bigip device and delete a specific node. hostname @@ -568,25 +614,27 @@ def delete_node(hostname, username, password, name, trans_label=None): CLI Example:: salt '*' bigip.delete_node bigip admin admin my-node - ''' + """ - #build session + # build session bigip_session = _build_session(username, password, trans_label) - #delete to REST + # delete to REST try: - response = bigip_session.delete(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/node/{name}'.format(name=name)) + response = bigip_session.delete( + BIG_IP_URL_BASE.format(host=hostname) + "/ltm/node/{name}".format(name=name) + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) - if _load_response(response) == '': + if _load_response(response) == "": return True else: return _load_response(response) def list_pool(hostname, username, password, name=None): - ''' + """ A function to connect to a bigip device and list all pools or a specific pool. hostname @@ -602,47 +650,58 @@ def list_pool(hostname, username, password, name=None): CLI Example:: salt '*' bigip.list_pool bigip admin admin my-pool - ''' + """ - #build sessions + # build sessions bigip_session = _build_session(username, password) - #get to REST + # get to REST try: if name: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/pool/{name}/?expandSubcollections=true'.format(name=name)) + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/pool/{name}/?expandSubcollections=true".format(name=name) + ) else: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/pool') + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + "/ltm/pool" + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) return _load_response(response) -def create_pool(hostname, username, password, name, members=None, - allow_nat=None, - allow_snat=None, - description=None, - gateway_failsafe_device=None, - ignore_persisted_weight=None, - ip_tos_to_client=None, - ip_tos_to_server=None, - link_qos_to_client=None, - link_qos_to_server=None, - load_balancing_mode=None, - min_active_members=None, - min_up_members=None, - min_up_members_action=None, - min_up_members_checking=None, - monitor=None, - profiles=None, - queue_depth_limit=None, - queue_on_connection_limit=None, - queue_time_limit=None, - reselect_tries=None, - service_down_action=None, - slow_ramp_time=None): - ''' +def create_pool( + hostname, + username, + password, + name, + members=None, + allow_nat=None, + allow_snat=None, + description=None, + gateway_failsafe_device=None, + ignore_persisted_weight=None, + ip_tos_to_client=None, + ip_tos_to_server=None, + link_qos_to_client=None, + link_qos_to_server=None, + load_balancing_mode=None, + min_active_members=None, + min_up_members=None, + min_up_members_action=None, + min_up_members_checking=None, + monitor=None, + profiles=None, + queue_depth_limit=None, + queue_on_connection_limit=None, + queue_time_limit=None, + reselect_tries=None, + service_down_action=None, + slow_ramp_time=None, +): + """ A function to connect to a bigip device and create a pool. hostname @@ -715,57 +774,57 @@ def create_pool(hostname, username, password, name, members=None, CLI Example:: salt '*' bigip.create_pool bigip admin admin my-pool 10.1.1.1:80,10.1.1.2:80,10.1.1.3:80 monitor=http - ''' + """ params = { - 'description': description, - 'gateway-failsafe-device': gateway_failsafe_device, - 'ignore-persisted-weight': ignore_persisted_weight, - 'ip-tos-to-client': ip_tos_to_client, - 'ip-tos-to-server': ip_tos_to_server, - 'link-qos-to-client': link_qos_to_client, - 'link-qos-to-server': link_qos_to_server, - 'load-balancing-mode': load_balancing_mode, - 'min-active-members': min_active_members, - 'min-up-members': min_up_members, - 'min-up-members-action': min_up_members_action, - 'min-up-members-checking': min_up_members_checking, - 'monitor': monitor, - 'profiles': profiles, - 'queue-on-connection-limit': queue_on_connection_limit, - 'queue-depth-limit': queue_depth_limit, - 'queue-time-limit': queue_time_limit, - 'reselect-tries': reselect_tries, - 'service-down-action': service_down_action, - 'slow-ramp-time': slow_ramp_time + "description": description, + "gateway-failsafe-device": gateway_failsafe_device, + "ignore-persisted-weight": ignore_persisted_weight, + "ip-tos-to-client": ip_tos_to_client, + "ip-tos-to-server": ip_tos_to_server, + "link-qos-to-client": link_qos_to_client, + "link-qos-to-server": link_qos_to_server, + "load-balancing-mode": load_balancing_mode, + "min-active-members": min_active_members, + "min-up-members": min_up_members, + "min-up-members-action": min_up_members_action, + "min-up-members-checking": min_up_members_checking, + "monitor": monitor, + "profiles": profiles, + "queue-on-connection-limit": queue_on_connection_limit, + "queue-depth-limit": queue_depth_limit, + "queue-time-limit": queue_time_limit, + "reselect-tries": reselect_tries, + "service-down-action": service_down_action, + "slow-ramp-time": slow_ramp_time, } # some options take yes no others take true false. Figure out when to use which without # confusing the end user toggles = { - 'allow-nat': {'type': 'yes_no', 'value': allow_nat}, - 'allow-snat': {'type': 'yes_no', 'value': allow_snat} + "allow-nat": {"type": "yes_no", "value": allow_nat}, + "allow-snat": {"type": "yes_no", "value": allow_snat}, } - #build payload + # build payload payload = _loop_payload(params) - payload['name'] = name + payload["name"] = name - #determine toggles + # determine toggles payload = _determine_toggles(payload, toggles) - #specify members if provided + # specify members if provided if members is not None: - payload['members'] = _build_list(members, 'ltm:pool:members') + payload["members"] = _build_list(members, "ltm:pool:members") - #build session + # build session bigip_session = _build_session(username, password) - #post to REST + # post to REST try: response = bigip_session.post( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/pool', - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + "/ltm/pool", + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -773,30 +832,35 @@ def create_pool(hostname, username, password, name, members=None, return _load_response(response) -def modify_pool(hostname, username, password, name, - allow_nat=None, - allow_snat=None, - description=None, - gateway_failsafe_device=None, - ignore_persisted_weight=None, - ip_tos_to_client=None, - ip_tos_to_server=None, - link_qos_to_client=None, - link_qos_to_server=None, - load_balancing_mode=None, - min_active_members=None, - min_up_members=None, - min_up_members_action=None, - min_up_members_checking=None, - monitor=None, - profiles=None, - queue_depth_limit=None, - queue_on_connection_limit=None, - queue_time_limit=None, - reselect_tries=None, - service_down_action=None, - slow_ramp_time=None): - ''' +def modify_pool( + hostname, + username, + password, + name, + allow_nat=None, + allow_snat=None, + description=None, + gateway_failsafe_device=None, + ignore_persisted_weight=None, + ip_tos_to_client=None, + ip_tos_to_server=None, + link_qos_to_client=None, + link_qos_to_server=None, + load_balancing_mode=None, + min_active_members=None, + min_up_members=None, + min_up_members_action=None, + min_up_members_checking=None, + monitor=None, + profiles=None, + queue_depth_limit=None, + queue_on_connection_limit=None, + queue_time_limit=None, + reselect_tries=None, + service_down_action=None, + slow_ramp_time=None, +): + """ A function to connect to a bigip device and modify an existing pool. hostname @@ -866,53 +930,54 @@ def modify_pool(hostname, username, password, name, CLI Example:: salt '*' bigip.modify_pool bigip admin admin my-pool 10.1.1.1:80,10.1.1.2:80,10.1.1.3:80 min_active_members=1 - ''' + """ params = { - 'description': description, - 'gateway-failsafe-device': gateway_failsafe_device, - 'ignore-persisted-weight': ignore_persisted_weight, - 'ip-tos-to-client': ip_tos_to_client, - 'ip-tos-to-server': ip_tos_to_server, - 'link-qos-to-client': link_qos_to_client, - 'link-qos-to-server': link_qos_to_server, - 'load-balancing-mode': load_balancing_mode, - 'min-active-members': min_active_members, - 'min-up-members': min_up_members, - 'min-up_members-action': min_up_members_action, - 'min-up-members-checking': min_up_members_checking, - 'monitor': monitor, - 'profiles': profiles, - 'queue-on-connection-limit': queue_on_connection_limit, - 'queue-depth-limit': queue_depth_limit, - 'queue-time-limit': queue_time_limit, - 'reselect-tries': reselect_tries, - 'service-down-action': service_down_action, - 'slow-ramp-time': slow_ramp_time + "description": description, + "gateway-failsafe-device": gateway_failsafe_device, + "ignore-persisted-weight": ignore_persisted_weight, + "ip-tos-to-client": ip_tos_to_client, + "ip-tos-to-server": ip_tos_to_server, + "link-qos-to-client": link_qos_to_client, + "link-qos-to-server": link_qos_to_server, + "load-balancing-mode": load_balancing_mode, + "min-active-members": min_active_members, + "min-up-members": min_up_members, + "min-up_members-action": min_up_members_action, + "min-up-members-checking": min_up_members_checking, + "monitor": monitor, + "profiles": profiles, + "queue-on-connection-limit": queue_on_connection_limit, + "queue-depth-limit": queue_depth_limit, + "queue-time-limit": queue_time_limit, + "reselect-tries": reselect_tries, + "service-down-action": service_down_action, + "slow-ramp-time": slow_ramp_time, } # some options take yes no others take true false. Figure out when to use which without # confusing the end user toggles = { - 'allow-nat': {'type': 'yes_no', 'value': allow_nat}, - 'allow-snat': {'type': 'yes_no', 'value': allow_snat} + "allow-nat": {"type": "yes_no", "value": allow_nat}, + "allow-snat": {"type": "yes_no", "value": allow_snat}, } - #build payload + # build payload payload = _loop_payload(params) - payload['name'] = name + payload["name"] = name - #determine toggles + # determine toggles payload = _determine_toggles(payload, toggles) - #build session + # build session bigip_session = _build_session(username, password) - #post to REST + # post to REST try: response = bigip_session.put( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/pool/{name}'.format(name=name), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/pool/{name}".format(name=name), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -921,7 +986,7 @@ def modify_pool(hostname, username, password, name, def delete_pool(hostname, username, password, name): - ''' + """ A function to connect to a bigip device and delete a specific pool. hostname @@ -936,25 +1001,27 @@ def delete_pool(hostname, username, password, name): CLI Example:: salt '*' bigip.delete_node bigip admin admin my-pool - ''' + """ - #build session + # build session bigip_session = _build_session(username, password) - #delete to REST + # delete to REST try: - response = bigip_session.delete(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/pool/{name}'.format(name=name)) + response = bigip_session.delete( + BIG_IP_URL_BASE.format(host=hostname) + "/ltm/pool/{name}".format(name=name) + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) - if _load_response(response) == '': + if _load_response(response) == "": return True else: return _load_response(response) def replace_pool_members(hostname, username, password, name, members): - ''' + """ A function to connect to a bigip device and replace members of an existing pool with new members. hostname @@ -972,47 +1039,48 @@ def replace_pool_members(hostname, username, password, name, members): CLI Example:: salt '*' bigip.replace_pool_members bigip admin admin my-pool 10.2.2.1:80,10.2.2.2:80,10.2.2.3:80 - ''' + """ payload = {} - payload['name'] = name - #specify members if provided + payload["name"] = name + # specify members if provided if members is not None: if isinstance(members, six.string_types): - members = members.split(',') + members = members.split(",") pool_members = [] for member in members: - #check to see if already a dictionary ( for states) + # check to see if already a dictionary ( for states) if isinstance(member, dict): - #check for state alternative name 'member_state', replace with state - if 'member_state' in member.keys(): - member['state'] = member.pop('member_state') + # check for state alternative name 'member_state', replace with state + if "member_state" in member.keys(): + member["state"] = member.pop("member_state") - #replace underscore with dash + # replace underscore with dash for key in member: - new_key = key.replace('_', '-') + new_key = key.replace("_", "-") member[new_key] = member.pop(key) pool_members.append(member) - #parse string passed via execution command (for executions) + # parse string passed via execution command (for executions) else: - pool_members.append({'name': member, 'address': member.split(':')[0]}) + pool_members.append({"name": member, "address": member.split(":")[0]}) - payload['members'] = pool_members + payload["members"] = pool_members - #build session + # build session bigip_session = _build_session(username, password) - #put to REST + # put to REST try: response = bigip_session.put( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/pool/{name}'.format(name=name), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/pool/{name}".format(name=name), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -1021,7 +1089,7 @@ def replace_pool_members(hostname, username, password, name, members): def add_pool_member(hostname, username, password, name, member): - ''' + """ A function to connect to a bigip device and add a new member to an existing pool. hostname @@ -1041,34 +1109,35 @@ def add_pool_member(hostname, username, password, name, member): .. code-block:: bash salt '*' bigip.add_pool_members bigip admin admin my-pool 10.2.2.1:80 - ''' + """ # for states if isinstance(member, dict): - #check for state alternative name 'member_state', replace with state - if 'member_state' in member.keys(): - member['state'] = member.pop('member_state') + # check for state alternative name 'member_state', replace with state + if "member_state" in member.keys(): + member["state"] = member.pop("member_state") - #replace underscore with dash + # replace underscore with dash for key in member: - new_key = key.replace('_', '-') + new_key = key.replace("_", "-") member[new_key] = member.pop(key) payload = member # for execution else: - payload = {'name': member, 'address': member.split(':')[0]} + payload = {"name": member, "address": member.split(":")[0]} - #build session + # build session bigip_session = _build_session(username, password) - #post to REST + # post to REST try: response = bigip_session.post( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/pool/{name}/members'.format(name=name), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/pool/{name}/members".format(name=name), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -1076,20 +1145,26 @@ def add_pool_member(hostname, username, password, name, member): return _load_response(response) -def modify_pool_member(hostname, username, password, name, member, - connection_limit=None, - description=None, - dynamic_ratio=None, - inherit_profile=None, - logging=None, - monitor=None, - priority_group=None, - profiles=None, - rate_limit=None, - ratio=None, - session=None, - state=None): - ''' +def modify_pool_member( + hostname, + username, + password, + name, + member, + connection_limit=None, + description=None, + dynamic_ratio=None, + inherit_profile=None, + logging=None, + monitor=None, + priority_group=None, + profiles=None, + rate_limit=None, + ratio=None, + session=None, + state=None, +): + """ A function to connect to a bigip device and modify an existing member of a pool. hostname @@ -1130,34 +1205,35 @@ def modify_pool_member(hostname, username, password, name, member, CLI Example:: salt '*' bigip.modify_pool_member bigip admin admin my-pool 10.2.2.1:80 state=use-down session=user-disabled - ''' + """ params = { - 'connection-limit': connection_limit, - 'description': description, - 'dynamic-ratio': dynamic_ratio, - 'inherit-profile': inherit_profile, - 'logging': logging, - 'monitor': monitor, - 'priority-group': priority_group, - 'profiles': profiles, - 'rate-limit': rate_limit, - 'ratio': ratio, - 'session': session, - 'state': state + "connection-limit": connection_limit, + "description": description, + "dynamic-ratio": dynamic_ratio, + "inherit-profile": inherit_profile, + "logging": logging, + "monitor": monitor, + "priority-group": priority_group, + "profiles": profiles, + "rate-limit": rate_limit, + "ratio": ratio, + "session": session, + "state": state, } - #build session + # build session bigip_session = _build_session(username, password) - #build payload + # build payload payload = _loop_payload(params) - #put to REST + # put to REST try: response = bigip_session.put( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/pool/{name}/members/{member}'.format(name=name, member=member), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/pool/{name}/members/{member}".format(name=name, member=member), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -1166,7 +1242,7 @@ def modify_pool_member(hostname, username, password, name, member, def delete_pool_member(hostname, username, password, name, member): - ''' + """ A function to connect to a bigip device and delete a specific pool. hostname @@ -1183,25 +1259,28 @@ def delete_pool_member(hostname, username, password, name, member): CLI Example:: salt '*' bigip.delete_pool_member bigip admin admin my-pool 10.2.2.2:80 - ''' + """ - #build session + # build session bigip_session = _build_session(username, password) - #delete to REST + # delete to REST try: - response = bigip_session.delete(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/pool/{name}/members/{member}'.format(name=name, member=member)) + response = bigip_session.delete( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/pool/{name}/members/{member}".format(name=name, member=member) + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) - if _load_response(response) == '': + if _load_response(response) == "": return True else: return _load_response(response) def list_virtual(hostname, username, password, name=None): - ''' + """ A function to connect to a bigip device and list all virtuals or a specific virtual. hostname @@ -1217,63 +1296,74 @@ def list_virtual(hostname, username, password, name=None): CLI Example:: salt '*' bigip.list_virtual bigip admin admin my-virtual - ''' + """ - #build sessions + # build sessions bigip_session = _build_session(username, password) - #get to REST + # get to REST try: if name: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/virtual/{name}/?expandSubcollections=true'.format(name=name)) + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/virtual/{name}/?expandSubcollections=true".format(name=name) + ) else: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/virtual') + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + "/ltm/virtual" + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) return _load_response(response) -def create_virtual(hostname, username, password, name, destination, - pool=None, - address_status=None, - auto_lasthop=None, - bwc_policy=None, - cmp_enabled=None, - connection_limit=None, - dhcp_relay=None, - description=None, - fallback_persistence=None, - flow_eviction_policy=None, - gtm_score=None, - ip_forward=None, - ip_protocol=None, - internal=None, - twelve_forward=None, - last_hop_pool=None, - mask=None, - mirror=None, - nat64=None, - persist=None, - profiles=None, - policies=None, - rate_class=None, - rate_limit=None, - rate_limit_mode=None, - rate_limit_dst=None, - rate_limit_src=None, - rules=None, - related_rules=None, - reject=None, - source=None, - source_address_translation=None, - source_port=None, - state=None, - traffic_classes=None, - translate_address=None, - translate_port=None, - vlans=None): - r''' +def create_virtual( + hostname, + username, + password, + name, + destination, + pool=None, + address_status=None, + auto_lasthop=None, + bwc_policy=None, + cmp_enabled=None, + connection_limit=None, + dhcp_relay=None, + description=None, + fallback_persistence=None, + flow_eviction_policy=None, + gtm_score=None, + ip_forward=None, + ip_protocol=None, + internal=None, + twelve_forward=None, + last_hop_pool=None, + mask=None, + mirror=None, + nat64=None, + persist=None, + profiles=None, + policies=None, + rate_class=None, + rate_limit=None, + rate_limit_mode=None, + rate_limit_dst=None, + rate_limit_src=None, + rules=None, + related_rules=None, + reject=None, + source=None, + source_address_translation=None, + source_port=None, + state=None, + traffic_classes=None, + translate_address=None, + translate_port=None, + vlans=None, +): + r""" A function to connect to a bigip device and create a virtual server. hostname @@ -1383,139 +1473,145 @@ def create_virtual(hostname, username, password, name, destination, traffic_classes=my-class,other-class \ vlans=enabled:external,internal - ''' + """ params = { - 'pool': pool, - 'auto-lasthop': auto_lasthop, - 'bwc-policy': bwc_policy, - 'connection-limit': connection_limit, - 'description': description, - 'fallback-persistence': fallback_persistence, - 'flow-eviction-policy': flow_eviction_policy, - 'gtm-score': gtm_score, - 'ip-protocol': ip_protocol, - 'last-hop-pool': last_hop_pool, - 'mask': mask, - 'mirror': mirror, - 'nat64': nat64, - 'persist': persist, - 'rate-class': rate_class, - 'rate-limit': rate_limit, - 'rate-limit-mode': rate_limit_mode, - 'rate-limit-dst': rate_limit_dst, - 'rate-limit-src': rate_limit_src, - 'source': source, - 'source-port': source_port, - 'translate-address': translate_address, - 'translate-port': translate_port + "pool": pool, + "auto-lasthop": auto_lasthop, + "bwc-policy": bwc_policy, + "connection-limit": connection_limit, + "description": description, + "fallback-persistence": fallback_persistence, + "flow-eviction-policy": flow_eviction_policy, + "gtm-score": gtm_score, + "ip-protocol": ip_protocol, + "last-hop-pool": last_hop_pool, + "mask": mask, + "mirror": mirror, + "nat64": nat64, + "persist": persist, + "rate-class": rate_class, + "rate-limit": rate_limit, + "rate-limit-mode": rate_limit_mode, + "rate-limit-dst": rate_limit_dst, + "rate-limit-src": rate_limit_src, + "source": source, + "source-port": source_port, + "translate-address": translate_address, + "translate-port": translate_port, } # some options take yes no others take true false. Figure out when to use which without # confusing the end user toggles = { - 'address-status': {'type': 'yes_no', 'value': address_status}, - 'cmp-enabled': {'type': 'yes_no', 'value': cmp_enabled}, - 'dhcp-relay': {'type': 'true_false', 'value': dhcp_relay}, - 'reject': {'type': 'true_false', 'value': reject}, - '12-forward': {'type': 'true_false', 'value': twelve_forward}, - 'internal': {'type': 'true_false', 'value': internal}, - 'ip-forward': {'type': 'true_false', 'value': ip_forward} + "address-status": {"type": "yes_no", "value": address_status}, + "cmp-enabled": {"type": "yes_no", "value": cmp_enabled}, + "dhcp-relay": {"type": "true_false", "value": dhcp_relay}, + "reject": {"type": "true_false", "value": reject}, + "12-forward": {"type": "true_false", "value": twelve_forward}, + "internal": {"type": "true_false", "value": internal}, + "ip-forward": {"type": "true_false", "value": ip_forward}, } - #build session + # build session bigip_session = _build_session(username, password) - #build payload + # build payload payload = _loop_payload(params) - payload['name'] = name - payload['destination'] = destination + payload["name"] = name + payload["destination"] = destination - #determine toggles + # determine toggles payload = _determine_toggles(payload, toggles) - #specify profiles if provided + # specify profiles if provided if profiles is not None: - payload['profiles'] = _build_list(profiles, 'ltm:virtual:profile') + payload["profiles"] = _build_list(profiles, "ltm:virtual:profile") - #specify persist if provided + # specify persist if provided if persist is not None: - payload['persist'] = _build_list(persist, 'ltm:virtual:persist') + payload["persist"] = _build_list(persist, "ltm:virtual:persist") - #specify policies if provided + # specify policies if provided if policies is not None: - payload['policies'] = _build_list(policies, 'ltm:virtual:policy') + payload["policies"] = _build_list(policies, "ltm:virtual:policy") - #specify rules if provided + # specify rules if provided if rules is not None: - payload['rules'] = _build_list(rules, None) + payload["rules"] = _build_list(rules, None) - #specify related-rules if provided + # specify related-rules if provided if related_rules is not None: - payload['related-rules'] = _build_list(related_rules, None) + payload["related-rules"] = _build_list(related_rules, None) - #handle source-address-translation + # handle source-address-translation if source_address_translation is not None: - #check to see if this is already a dictionary first + # check to see if this is already a dictionary first if isinstance(source_address_translation, dict): - payload['source-address-translation'] = source_address_translation - elif source_address_translation == 'none': - payload['source-address-translation'] = {'pool': 'none', 'type': 'none'} - elif source_address_translation == 'automap': - payload['source-address-translation'] = {'pool': 'none', 'type': 'automap'} - elif source_address_translation == 'lsn': - payload['source-address-translation'] = {'pool': 'none', 'type': 'lsn'} - elif source_address_translation.startswith('snat'): - snat_pool = source_address_translation.split(':')[1] - payload['source-address-translation'] = {'pool': snat_pool, 'type': 'snat'} + payload["source-address-translation"] = source_address_translation + elif source_address_translation == "none": + payload["source-address-translation"] = {"pool": "none", "type": "none"} + elif source_address_translation == "automap": + payload["source-address-translation"] = {"pool": "none", "type": "automap"} + elif source_address_translation == "lsn": + payload["source-address-translation"] = {"pool": "none", "type": "lsn"} + elif source_address_translation.startswith("snat"): + snat_pool = source_address_translation.split(":")[1] + payload["source-address-translation"] = {"pool": snat_pool, "type": "snat"} - #specify related-rules if provided + # specify related-rules if provided if traffic_classes is not None: - payload['traffic-classes'] = _build_list(traffic_classes, None) + payload["traffic-classes"] = _build_list(traffic_classes, None) - #handle vlans + # handle vlans if vlans is not None: - #ceck to see if vlans is a dictionary (used when state makes use of function) + # ceck to see if vlans is a dictionary (used when state makes use of function) if isinstance(vlans, dict): try: - payload['vlans'] = vlans['vlan_ids'] - if vlans['enabled']: - payload['vlans-enabled'] = True - elif vlans['disabled']: - payload['vlans-disabled'] = True + payload["vlans"] = vlans["vlan_ids"] + if vlans["enabled"]: + payload["vlans-enabled"] = True + elif vlans["disabled"]: + payload["vlans-disabled"] = True except Exception: # pylint: disable=broad-except - return 'Error: Unable to Parse vlans dictionary: \n\tvlans={vlans}'.format(vlans=vlans) - elif vlans == 'none': - payload['vlans'] = 'none' - elif vlans == 'default': - payload['vlans'] = 'default' - elif isinstance(vlans, six.string_types) and (vlans.startswith('enabled') or vlans.startswith('disabled')): + return "Error: Unable to Parse vlans dictionary: \n\tvlans={vlans}".format( + vlans=vlans + ) + elif vlans == "none": + payload["vlans"] = "none" + elif vlans == "default": + payload["vlans"] = "default" + elif isinstance(vlans, six.string_types) and ( + vlans.startswith("enabled") or vlans.startswith("disabled") + ): try: - vlans_setting = vlans.split(':')[0] - payload['vlans'] = vlans.split(':')[1].split(',') - if vlans_setting == 'disabled': - payload['vlans-disabled'] = True - elif vlans_setting == 'enabled': - payload['vlans-enabled'] = True + vlans_setting = vlans.split(":")[0] + payload["vlans"] = vlans.split(":")[1].split(",") + if vlans_setting == "disabled": + payload["vlans-disabled"] = True + elif vlans_setting == "enabled": + payload["vlans-enabled"] = True except Exception: # pylint: disable=broad-except - return 'Error: Unable to Parse vlans option: \n\tvlans={vlans}'.format(vlans=vlans) + return "Error: Unable to Parse vlans option: \n\tvlans={vlans}".format( + vlans=vlans + ) else: - return 'Error: vlans must be a dictionary or string.' + return "Error: vlans must be a dictionary or string." - #determine state + # determine state if state is not None: - if state == 'enabled': - payload['enabled'] = True - elif state == 'disabled': - payload['disabled'] = True + if state == "enabled": + payload["enabled"] = True + elif state == "disabled": + payload["disabled"] = True - #post to REST + # post to REST try: response = bigip_session.post( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/virtual', - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + "/ltm/virtual", + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -1523,47 +1619,52 @@ def create_virtual(hostname, username, password, name, destination, return _load_response(response) -def modify_virtual(hostname, username, password, name, - destination=None, - pool=None, - address_status=None, - auto_lasthop=None, - bwc_policy=None, - cmp_enabled=None, - connection_limit=None, - dhcp_relay=None, - description=None, - fallback_persistence=None, - flow_eviction_policy=None, - gtm_score=None, - ip_forward=None, - ip_protocol=None, - internal=None, - twelve_forward=None, - last_hop_pool=None, - mask=None, - mirror=None, - nat64=None, - persist=None, - profiles=None, - policies=None, - rate_class=None, - rate_limit=None, - rate_limit_mode=None, - rate_limit_dst=None, - rate_limit_src=None, - rules=None, - related_rules=None, - reject=None, - source=None, - source_address_translation=None, - source_port=None, - state=None, - traffic_classes=None, - translate_address=None, - translate_port=None, - vlans=None): - ''' +def modify_virtual( + hostname, + username, + password, + name, + destination=None, + pool=None, + address_status=None, + auto_lasthop=None, + bwc_policy=None, + cmp_enabled=None, + connection_limit=None, + dhcp_relay=None, + description=None, + fallback_persistence=None, + flow_eviction_policy=None, + gtm_score=None, + ip_forward=None, + ip_protocol=None, + internal=None, + twelve_forward=None, + last_hop_pool=None, + mask=None, + mirror=None, + nat64=None, + persist=None, + profiles=None, + policies=None, + rate_class=None, + rate_limit=None, + rate_limit_mode=None, + rate_limit_dst=None, + rate_limit_src=None, + rules=None, + related_rules=None, + reject=None, + source=None, + source_address_translation=None, + source_port=None, + state=None, + traffic_classes=None, + translate_address=None, + translate_port=None, + vlans=None, +): + """ A function to connect to a bigip device and modify an existing virtual server. hostname @@ -1662,132 +1763,137 @@ def modify_virtual(hostname, username, password, name, salt '*' bigip.modify_virtual bigip admin admin my-virtual source_address_translation=none salt '*' bigip.modify_virtual bigip admin admin my-virtual rules=my-rule,my-other-rule - ''' + """ params = { - 'destination': destination, - 'pool': pool, - 'auto-lasthop': auto_lasthop, - 'bwc-policy': bwc_policy, - 'connection-limit': connection_limit, - 'description': description, - 'fallback-persistence': fallback_persistence, - 'flow-eviction-policy': flow_eviction_policy, - 'gtm-score': gtm_score, - 'ip-protocol': ip_protocol, - 'last-hop-pool': last_hop_pool, - 'mask': mask, - 'mirror': mirror, - 'nat64': nat64, - 'persist': persist, - 'rate-class': rate_class, - 'rate-limit': rate_limit, - 'rate-limit-mode': rate_limit_mode, - 'rate-limit-dst': rate_limit_dst, - 'rate-limit-src': rate_limit_src, - 'source': source, - 'source-port': source_port, - 'translate-address': translate_address, - 'translate-port': translate_port + "destination": destination, + "pool": pool, + "auto-lasthop": auto_lasthop, + "bwc-policy": bwc_policy, + "connection-limit": connection_limit, + "description": description, + "fallback-persistence": fallback_persistence, + "flow-eviction-policy": flow_eviction_policy, + "gtm-score": gtm_score, + "ip-protocol": ip_protocol, + "last-hop-pool": last_hop_pool, + "mask": mask, + "mirror": mirror, + "nat64": nat64, + "persist": persist, + "rate-class": rate_class, + "rate-limit": rate_limit, + "rate-limit-mode": rate_limit_mode, + "rate-limit-dst": rate_limit_dst, + "rate-limit-src": rate_limit_src, + "source": source, + "source-port": source_port, + "translate-address": translate_address, + "translate-port": translate_port, } # some options take yes no others take true false. Figure out when to use which without # confusing the end user toggles = { - 'address-status': {'type': 'yes_no', 'value': address_status}, - 'cmp-enabled': {'type': 'yes_no', 'value': cmp_enabled}, - 'dhcp-relay': {'type': 'true_false', 'value': dhcp_relay}, - 'reject': {'type': 'true_false', 'value': reject}, - '12-forward': {'type': 'true_false', 'value': twelve_forward}, - 'internal': {'type': 'true_false', 'value': internal}, - 'ip-forward': {'type': 'true_false', 'value': ip_forward} + "address-status": {"type": "yes_no", "value": address_status}, + "cmp-enabled": {"type": "yes_no", "value": cmp_enabled}, + "dhcp-relay": {"type": "true_false", "value": dhcp_relay}, + "reject": {"type": "true_false", "value": reject}, + "12-forward": {"type": "true_false", "value": twelve_forward}, + "internal": {"type": "true_false", "value": internal}, + "ip-forward": {"type": "true_false", "value": ip_forward}, } - #build session + # build session bigip_session = _build_session(username, password) - #build payload + # build payload payload = _loop_payload(params) - payload['name'] = name + payload["name"] = name - #determine toggles + # determine toggles payload = _determine_toggles(payload, toggles) - #specify profiles if provided + # specify profiles if provided if profiles is not None: - payload['profiles'] = _build_list(profiles, 'ltm:virtual:profile') + payload["profiles"] = _build_list(profiles, "ltm:virtual:profile") - #specify persist if provided + # specify persist if provided if persist is not None: - payload['persist'] = _build_list(persist, 'ltm:virtual:persist') + payload["persist"] = _build_list(persist, "ltm:virtual:persist") - #specify policies if provided + # specify policies if provided if policies is not None: - payload['policies'] = _build_list(policies, 'ltm:virtual:policy') + payload["policies"] = _build_list(policies, "ltm:virtual:policy") - #specify rules if provided + # specify rules if provided if rules is not None: - payload['rules'] = _build_list(rules, None) + payload["rules"] = _build_list(rules, None) - #specify related-rules if provided + # specify related-rules if provided if related_rules is not None: - payload['related-rules'] = _build_list(related_rules, None) + payload["related-rules"] = _build_list(related_rules, None) - #handle source-address-translation + # handle source-address-translation if source_address_translation is not None: - if source_address_translation == 'none': - payload['source-address-translation'] = {'pool': 'none', 'type': 'none'} - elif source_address_translation == 'automap': - payload['source-address-translation'] = {'pool': 'none', 'type': 'automap'} - elif source_address_translation == 'lsn': - payload['source-address-translation'] = {'pool': 'none', 'type': 'lsn'} - elif source_address_translation.startswith('snat'): - snat_pool = source_address_translation.split(':')[1] - payload['source-address-translation'] = {'pool': snat_pool, 'type': 'snat'} + if source_address_translation == "none": + payload["source-address-translation"] = {"pool": "none", "type": "none"} + elif source_address_translation == "automap": + payload["source-address-translation"] = {"pool": "none", "type": "automap"} + elif source_address_translation == "lsn": + payload["source-address-translation"] = {"pool": "none", "type": "lsn"} + elif source_address_translation.startswith("snat"): + snat_pool = source_address_translation.split(":")[1] + payload["source-address-translation"] = {"pool": snat_pool, "type": "snat"} - #specify related-rules if provided + # specify related-rules if provided if traffic_classes is not None: - payload['traffic-classes'] = _build_list(traffic_classes, None) + payload["traffic-classes"] = _build_list(traffic_classes, None) - #handle vlans + # handle vlans if vlans is not None: - #ceck to see if vlans is a dictionary (used when state makes use of function) + # ceck to see if vlans is a dictionary (used when state makes use of function) if isinstance(vlans, dict): try: - payload['vlans'] = vlans['vlan_ids'] - if vlans['enabled']: - payload['vlans-enabled'] = True - elif vlans['disabled']: - payload['vlans-disabled'] = True + payload["vlans"] = vlans["vlan_ids"] + if vlans["enabled"]: + payload["vlans-enabled"] = True + elif vlans["disabled"]: + payload["vlans-disabled"] = True except Exception: # pylint: disable=broad-except - return 'Error: Unable to Parse vlans dictionary: \n\tvlans={vlans}'.format(vlans=vlans) - elif vlans == 'none': - payload['vlans'] = 'none' - elif vlans == 'default': - payload['vlans'] = 'default' - elif vlans.startswith('enabled') or vlans.startswith('disabled'): + return "Error: Unable to Parse vlans dictionary: \n\tvlans={vlans}".format( + vlans=vlans + ) + elif vlans == "none": + payload["vlans"] = "none" + elif vlans == "default": + payload["vlans"] = "default" + elif vlans.startswith("enabled") or vlans.startswith("disabled"): try: - vlans_setting = vlans.split(':')[0] - payload['vlans'] = vlans.split(':')[1].split(',') - if vlans_setting == 'disabled': - payload['vlans-disabled'] = True - elif vlans_setting == 'enabled': - payload['vlans-enabled'] = True + vlans_setting = vlans.split(":")[0] + payload["vlans"] = vlans.split(":")[1].split(",") + if vlans_setting == "disabled": + payload["vlans-disabled"] = True + elif vlans_setting == "enabled": + payload["vlans-enabled"] = True except Exception: # pylint: disable=broad-except - return 'Error: Unable to Parse vlans option: \n\tvlans={vlans}'.format(vlans=vlans) + return "Error: Unable to Parse vlans option: \n\tvlans={vlans}".format( + vlans=vlans + ) - #determine state + # determine state if state is not None: - if state == 'enabled': - payload['enabled'] = True - elif state == 'disabled': - payload['disabled'] = True + if state == "enabled": + payload["enabled"] = True + elif state == "disabled": + payload["disabled"] = True - #put to REST + # put to REST try: response = bigip_session.put( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/virtual/{name}'.format(name=name), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/virtual/{name}".format(name=name), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -1796,7 +1902,7 @@ def modify_virtual(hostname, username, password, name, def delete_virtual(hostname, username, password, name): - ''' + """ A function to connect to a bigip device and delete a specific virtual. hostname @@ -1811,25 +1917,30 @@ def delete_virtual(hostname, username, password, name): CLI Example:: salt '*' bigip.delete_virtual bigip admin admin my-virtual - ''' + """ - #build session + # build session bigip_session = _build_session(username, password) - #delete to REST + # delete to REST try: - response = bigip_session.delete(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/virtual/{name}'.format(name=name)) + response = bigip_session.delete( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/virtual/{name}".format(name=name) + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) - if _load_response(response) == '': + if _load_response(response) == "": return True else: return _load_response(response) -def list_monitor(hostname, username, password, monitor_type, name=None, ): - ''' +def list_monitor( + hostname, username, password, monitor_type, name=None, +): + """ A function to connect to a bigip device and list an existing monitor. If no name is provided than all monitors of the specified type will be listed. @@ -1848,17 +1959,25 @@ def list_monitor(hostname, username, password, monitor_type, name=None, ): salt '*' bigip.list_monitor bigip admin admin http my-http-monitor - ''' + """ - #build sessions + # build sessions bigip_session = _build_session(username, password) - #get to REST + # get to REST try: if name: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/monitor/{type}/{name}?expandSubcollections=true'.format(type=monitor_type, name=name)) + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/monitor/{type}/{name}?expandSubcollections=true".format( + type=monitor_type, name=name + ) + ) else: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/monitor/{type}'.format(type=monitor_type)) + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/monitor/{type}".format(type=monitor_type) + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -1866,7 +1985,7 @@ def list_monitor(hostname, username, password, monitor_type, name=None, ): def create_monitor(hostname, username, password, monitor_type, name, **kwargs): - ''' + """ A function to connect to a bigip device and create a monitor. hostname @@ -1886,28 +2005,29 @@ def create_monitor(hostname, username, password, monitor_type, name, **kwargs): CLI Example:: salt '*' bigip.create_monitor bigip admin admin http my-http-monitor timeout=10 interval=5 - ''' + """ - #build session + # build session bigip_session = _build_session(username, password) - #construct the payload + # construct the payload payload = {} - payload['name'] = name + payload["name"] = name - #there's a ton of different monitors and a ton of options for each type of monitor. - #this logic relies that the end user knows which options are meant for which monitor types + # there's a ton of different monitors and a ton of options for each type of monitor. + # this logic relies that the end user knows which options are meant for which monitor types for key, value in six.iteritems(kwargs): - if not key.startswith('__'): - if key not in ['hostname', 'username', 'password', 'type']: - key = key.replace('_', '-') + if not key.startswith("__"): + if key not in ["hostname", "username", "password", "type"]: + key = key.replace("_", "-") payload[key] = value - #post to REST + # post to REST try: response = bigip_session.post( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/monitor/{type}'.format(type=monitor_type), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/monitor/{type}".format(type=monitor_type), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -1916,7 +2036,7 @@ def create_monitor(hostname, username, password, monitor_type, name, **kwargs): def modify_monitor(hostname, username, password, monitor_type, name, **kwargs): - ''' + """ A function to connect to a bigip device and modify an existing monitor. hostname @@ -1937,27 +2057,28 @@ def modify_monitor(hostname, username, password, monitor_type, name, **kwargs): salt '*' bigip.modify_monitor bigip admin admin http my-http-monitor timout=16 interval=6 - ''' + """ - #build session + # build session bigip_session = _build_session(username, password) - #construct the payload + # construct the payload payload = {} - #there's a ton of different monitors and a ton of options for each type of monitor. - #this logic relies that the end user knows which options are meant for which monitor types + # there's a ton of different monitors and a ton of options for each type of monitor. + # this logic relies that the end user knows which options are meant for which monitor types for key, value in six.iteritems(kwargs): - if not key.startswith('__'): - if key not in ['hostname', 'username', 'password', 'type', 'name']: - key = key.replace('_', '-') + if not key.startswith("__"): + if key not in ["hostname", "username", "password", "type", "name"]: + key = key.replace("_", "-") payload[key] = value - #put to REST + # put to REST try: response = bigip_session.put( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/monitor/{type}/{name}'.format(type=monitor_type, name=name), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/monitor/{type}/{name}".format(type=monitor_type, name=name), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -1966,7 +2087,7 @@ def modify_monitor(hostname, username, password, monitor_type, name, **kwargs): def delete_monitor(hostname, username, password, monitor_type, name): - ''' + """ A function to connect to a bigip device and delete an existing monitor. hostname @@ -1984,25 +2105,30 @@ def delete_monitor(hostname, username, password, monitor_type, name): salt '*' bigip.delete_monitor bigip admin admin http my-http-monitor - ''' + """ - #build sessions + # build sessions bigip_session = _build_session(username, password) - #delete to REST + # delete to REST try: - response = bigip_session.delete(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/monitor/{type}/{name}'.format(type=monitor_type, name=name)) + response = bigip_session.delete( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/monitor/{type}/{name}".format(type=monitor_type, name=name) + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) - if _load_response(response) == '': + if _load_response(response) == "": return True else: return _load_response(response) -def list_profile(hostname, username, password, profile_type, name=None, ): - ''' +def list_profile( + hostname, username, password, profile_type, name=None, +): + """ A function to connect to a bigip device and list an existing profile. If no name is provided than all profiles of the specified type will be listed. @@ -2021,17 +2147,25 @@ def list_profile(hostname, username, password, profile_type, name=None, ): salt '*' bigip.list_profile bigip admin admin http my-http-profile - ''' + """ - #build sessions + # build sessions bigip_session = _build_session(username, password) - #get to REST + # get to REST try: if name: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/profile/{type}/{name}?expandSubcollections=true'.format(type=profile_type, name=name)) + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/profile/{type}/{name}?expandSubcollections=true".format( + type=profile_type, name=name + ) + ) else: - response = bigip_session.get(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/profile/{type}'.format(type=profile_type)) + response = bigip_session.get( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/profile/{type}".format(type=profile_type) + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -2039,7 +2173,7 @@ def list_profile(hostname, username, password, profile_type, name=None, ): def create_profile(hostname, username, password, profile_type, name, **kwargs): - r''' + r""" A function to connect to a bigip device and create a profile. hostname @@ -2086,32 +2220,35 @@ def create_profile(hostname, username, password, profile_type, name, **kwargs): salt '*' bigip.create_profile bigip admin admin http my-http-profile defaultsFrom='/Common/http' \ enforcement=maxHeaderCount:3200,maxRequests:10 - ''' + """ - #build session + # build session bigip_session = _build_session(username, password) - #construct the payload + # construct the payload payload = {} - payload['name'] = name + payload["name"] = name - #there's a ton of different profiles and a ton of options for each type of profile. - #this logic relies that the end user knows which options are meant for which profile types + # there's a ton of different profiles and a ton of options for each type of profile. + # this logic relies that the end user knows which options are meant for which profile types for key, value in six.iteritems(kwargs): - if not key.startswith('__'): - if key not in ['hostname', 'username', 'password', 'profile_type']: - key = key.replace('_', '-') + if not key.startswith("__"): + if key not in ["hostname", "username", "password", "profile_type"]: + key = key.replace("_", "-") try: payload[key] = _set_value(value) except salt.exceptions.CommandExecutionError: - return 'Error: Unable to Parse JSON data for parameter: {key}\n{value}'.format(key=key, value=value) + return "Error: Unable to Parse JSON data for parameter: {key}\n{value}".format( + key=key, value=value + ) - #post to REST + # post to REST try: response = bigip_session.post( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/profile/{type}'.format(type=profile_type), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/profile/{type}".format(type=profile_type), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -2120,7 +2257,7 @@ def create_profile(hostname, username, password, profile_type, name, **kwargs): def modify_profile(hostname, username, password, profile_type, name, **kwargs): - r''' + r""" A function to connect to a bigip device and create a profile. A function to connect to a bigip device and create a profile. @@ -2174,32 +2311,35 @@ def modify_profile(hostname, username, password, profile_type, name, **kwargs): salt '*' bigip.modify_profile bigip admin admin client-ssl my-client-ssl-1 retainCertificate=false \ ciphers='DEFAULT\:!SSLv3' cert_key_chain='j{ "default": { "cert": "default.crt", "chain": "default.crt", "key": "default.key" } }j' - ''' + """ - #build session + # build session bigip_session = _build_session(username, password) - #construct the payload + # construct the payload payload = {} - payload['name'] = name + payload["name"] = name - #there's a ton of different profiles and a ton of options for each type of profile. - #this logic relies that the end user knows which options are meant for which profile types + # there's a ton of different profiles and a ton of options for each type of profile. + # this logic relies that the end user knows which options are meant for which profile types for key, value in six.iteritems(kwargs): - if not key.startswith('__'): - if key not in ['hostname', 'username', 'password', 'profile_type']: - key = key.replace('_', '-') + if not key.startswith("__"): + if key not in ["hostname", "username", "password", "profile_type"]: + key = key.replace("_", "-") try: payload[key] = _set_value(value) except salt.exceptions.CommandExecutionError: - return 'Error: Unable to Parse JSON data for parameter: {key}\n{value}'.format(key=key, value=value) + return "Error: Unable to Parse JSON data for parameter: {key}\n{value}".format( + key=key, value=value + ) - #put to REST + # put to REST try: response = bigip_session.put( - BIG_IP_URL_BASE.format(host=hostname) + '/ltm/profile/{type}/{name}'.format(type=profile_type, name=name), - data=salt.utils.json.dumps(payload) + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/profile/{type}/{name}".format(type=profile_type, name=name), + data=salt.utils.json.dumps(payload), ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) @@ -2208,7 +2348,7 @@ def modify_profile(hostname, username, password, profile_type, name, **kwargs): def delete_profile(hostname, username, password, profile_type, name): - ''' + """ A function to connect to a bigip device and delete an existing profile. hostname @@ -2226,18 +2366,21 @@ def delete_profile(hostname, username, password, profile_type, name): salt '*' bigip.delete_profile bigip admin admin http my-http-profile - ''' + """ - #build sessions + # build sessions bigip_session = _build_session(username, password) - #delete to REST + # delete to REST try: - response = bigip_session.delete(BIG_IP_URL_BASE.format(host=hostname)+'/ltm/profile/{type}/{name}'.format(type=profile_type, name=name)) + response = bigip_session.delete( + BIG_IP_URL_BASE.format(host=hostname) + + "/ltm/profile/{type}/{name}".format(type=profile_type, name=name) + ) except requests.exceptions.ConnectionError as e: return _load_connection_error(hostname, e) - if _load_response(response) == '': + if _load_response(response) == "": return True else: return _load_response(response) diff --git a/salt/modules/bluez_bluetooth.py b/salt/modules/bluez_bluetooth.py index 7f10425fefe..ac1d2a29008 100644 --- a/salt/modules/bluez_bluetooth.py +++ b/salt/modules/bluez_bluetooth.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for Bluetooth (using BlueZ in Linux). The following packages are required packages for this module: @@ -8,45 +8,48 @@ The following packages are required packages for this module: bluez-libs >= 5.7 bluez-utils >= 5.7 pybluez >= 0.18 -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import 3rd-party libs -# pylint: disable=import-error -from salt.ext.six.moves import shlex_quote as _cmd_quote -# pylint: enable=import-error - # Import salt libs import salt.utils.validate.net from salt.exceptions import CommandExecutionError +# Import 3rd-party libs +# pylint: disable=import-error +from salt.ext.six.moves import shlex_quote as _cmd_quote + +# pylint: enable=import-error + HAS_PYBLUEZ = False try: import bluetooth # pylint: disable=import-error + HAS_PYBLUEZ = True except ImportError: pass -__func_alias__ = { - 'address_': 'address' -} +__func_alias__ = {"address_": "address"} # Define the module's virtual name -__virtualname__ = 'bluetooth' +__virtualname__ = "bluetooth" def __virtual__(): - ''' + """ Only load the module if bluetooth is installed - ''' + """ if HAS_PYBLUEZ: return __virtualname__ - return (False, 'The bluetooth execution module cannot be loaded: bluetooth not installed.') + return ( + False, + "The bluetooth execution module cannot be loaded: bluetooth not installed.", + ) def version(): - ''' + """ Return Bluez version from bluetoothd -v CLI Example: @@ -54,20 +57,20 @@ def version(): .. code-block:: bash salt '*' bluetoothd.version - ''' - cmd = 'bluetoothctl -v' - out = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "bluetoothctl -v" + out = __salt__["cmd.run"](cmd).splitlines() bluez_version = out[0] - pybluez_version = '<= 0.18 (Unknown, but installed)' + pybluez_version = "<= 0.18 (Unknown, but installed)" try: pybluez_version = bluetooth.__version__ except Exception as exc: # pylint: disable=broad-except pass - return {'Bluez': bluez_version, 'PyBluez': pybluez_version} + return {"Bluez": bluez_version, "PyBluez": pybluez_version} def address_(): - ''' + """ Get the many addresses of the Bluetooth adapter CLI Example: @@ -75,31 +78,31 @@ def address_(): .. code-block:: bash salt '*' bluetooth.address - ''' + """ ret = {} - cmd = 'hciconfig' - out = __salt__['cmd.run'](cmd).splitlines() - dev = '' + cmd = "hciconfig" + out = __salt__["cmd.run"](cmd).splitlines() + dev = "" for line in out: - if line.startswith('hci'): - comps = line.split(':') + if line.startswith("hci"): + comps = line.split(":") dev = comps[0] ret[dev] = { - 'device': dev, - 'path': '/sys/class/bluetooth/{0}'.format(dev), + "device": dev, + "path": "/sys/class/bluetooth/{0}".format(dev), } - if 'BD Address' in line: + if "BD Address" in line: comps = line.split() - ret[dev]['address'] = comps[2] - if 'DOWN' in line: - ret[dev]['power'] = 'off' - if 'UP RUNNING' in line: - ret[dev]['power'] = 'on' + ret[dev]["address"] = comps[2] + if "DOWN" in line: + ret[dev]["power"] = "off" + if "UP RUNNING" in line: + ret[dev]["power"] = "on" return ret def power(dev, mode): - ''' + """ Power a bluetooth device on or off CLI Examples: @@ -108,26 +111,26 @@ def power(dev, mode): salt '*' bluetooth.power hci0 on salt '*' bluetooth.power hci0 off - ''' + """ if dev not in address_(): - raise CommandExecutionError('Invalid dev passed to bluetooth.power') + raise CommandExecutionError("Invalid dev passed to bluetooth.power") - if mode == 'on' or mode is True: - state = 'up' - mode = 'on' + if mode == "on" or mode is True: + state = "up" + mode = "on" else: - state = 'down' - mode = 'off' - cmd = 'hciconfig {0} {1}'.format(dev, state) - __salt__['cmd.run'](cmd).splitlines() + state = "down" + mode = "off" + cmd = "hciconfig {0} {1}".format(dev, state) + __salt__["cmd.run"](cmd).splitlines() info = address_() - if info[dev]['power'] == mode: + if info[dev]["power"] == mode: return True return False def discoverable(dev): - ''' + """ Enable this bluetooth device to be discoverable. CLI Example: @@ -135,23 +138,21 @@ def discoverable(dev): .. code-block:: bash salt '*' bluetooth.discoverable hci0 - ''' + """ if dev not in address_(): - raise CommandExecutionError( - 'Invalid dev passed to bluetooth.discoverable' - ) + raise CommandExecutionError("Invalid dev passed to bluetooth.discoverable") - cmd = 'hciconfig {0} iscan'.format(dev) - __salt__['cmd.run'](cmd).splitlines() - cmd = 'hciconfig {0}'.format(dev) - out = __salt__['cmd.run'](cmd) - if 'UP RUNNING ISCAN' in out: + cmd = "hciconfig {0} iscan".format(dev) + __salt__["cmd.run"](cmd).splitlines() + cmd = "hciconfig {0}".format(dev) + out = __salt__["cmd.run"](cmd) + if "UP RUNNING ISCAN" in out: return True return False def noscan(dev): - ''' + """ Turn off scanning modes on this device. CLI Example: @@ -159,21 +160,21 @@ def noscan(dev): .. code-block:: bash salt '*' bluetooth.noscan hci0 - ''' + """ if dev not in address_(): - raise CommandExecutionError('Invalid dev passed to bluetooth.noscan') + raise CommandExecutionError("Invalid dev passed to bluetooth.noscan") - cmd = 'hciconfig {0} noscan'.format(dev) - __salt__['cmd.run'](cmd).splitlines() - cmd = 'hciconfig {0}'.format(dev) - out = __salt__['cmd.run'](cmd) - if 'SCAN' in out: + cmd = "hciconfig {0} noscan".format(dev) + __salt__["cmd.run"](cmd).splitlines() + cmd = "hciconfig {0}".format(dev) + out = __salt__["cmd.run"](cmd) + if "SCAN" in out: return False return True def scan(): - ''' + """ Scan for bluetooth devices in the area CLI Example: @@ -181,7 +182,7 @@ def scan(): .. code-block:: bash salt '*' bluetooth.scan - ''' + """ ret = [] devices = bluetooth.discover_devices(lookup_names=True) for device in devices: @@ -190,7 +191,7 @@ def scan(): def block(bdaddr): - ''' + """ Block a specific bluetooth device by BD Address CLI Example: @@ -198,18 +199,16 @@ def block(bdaddr): .. code-block:: bash salt '*' bluetooth.block DE:AD:BE:EF:CA:FE - ''' + """ if not salt.utils.validate.net.mac(bdaddr): - raise CommandExecutionError( - 'Invalid BD address passed to bluetooth.block' - ) + raise CommandExecutionError("Invalid BD address passed to bluetooth.block") - cmd = 'hciconfig {0} block'.format(bdaddr) - __salt__['cmd.run'](cmd).splitlines() + cmd = "hciconfig {0} block".format(bdaddr) + __salt__["cmd.run"](cmd).splitlines() def unblock(bdaddr): - ''' + """ Unblock a specific bluetooth device by BD Address CLI Example: @@ -217,18 +216,16 @@ def unblock(bdaddr): .. code-block:: bash salt '*' bluetooth.unblock DE:AD:BE:EF:CA:FE - ''' + """ if not salt.utils.validate.net.mac(bdaddr): - raise CommandExecutionError( - 'Invalid BD address passed to bluetooth.unblock' - ) + raise CommandExecutionError("Invalid BD address passed to bluetooth.unblock") - cmd = 'hciconfig {0} unblock'.format(bdaddr) - __salt__['cmd.run'](cmd).splitlines() + cmd = "hciconfig {0} unblock".format(bdaddr) + __salt__["cmd.run"](cmd).splitlines() def pair(address, key): - ''' + """ Pair the bluetooth adapter with a device CLI Example: @@ -242,29 +239,27 @@ def pair(address, key): TODO: This function is currently broken, as the bluez-simple-agent program no longer ships with BlueZ >= 5.0. It needs to be refactored. - ''' + """ if not salt.utils.validate.net.mac(address): - raise CommandExecutionError( - 'Invalid BD address passed to bluetooth.pair' - ) + raise CommandExecutionError("Invalid BD address passed to bluetooth.pair") try: int(key) except Exception: # pylint: disable=broad-except raise CommandExecutionError( - 'bluetooth.pair requires a numerical key to be used' + "bluetooth.pair requires a numerical key to be used" ) addy = address_() - cmd = 'echo {0} | bluez-simple-agent {1} {2}'.format( - _cmd_quote(addy['device']), _cmd_quote(address), _cmd_quote(key) + cmd = "echo {0} | bluez-simple-agent {1} {2}".format( + _cmd_quote(addy["device"]), _cmd_quote(address), _cmd_quote(key) ) - out = __salt__['cmd.run'](cmd, python_shell=True).splitlines() + out = __salt__["cmd.run"](cmd, python_shell=True).splitlines() return out def unpair(address): - ''' + """ Unpair the bluetooth adapter from a device CLI Example: @@ -277,19 +272,17 @@ def unpair(address): TODO: This function is currently broken, as the bluez-simple-agent program no longer ships with BlueZ >= 5.0. It needs to be refactored. - ''' + """ if not salt.utils.validate.net.mac(address): - raise CommandExecutionError( - 'Invalid BD address passed to bluetooth.unpair' - ) + raise CommandExecutionError("Invalid BD address passed to bluetooth.unpair") - cmd = 'bluez-test-device remove {0}'.format(address) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "bluez-test-device remove {0}".format(address) + out = __salt__["cmd.run"](cmd).splitlines() return out def start(): - ''' + """ Start the bluetooth service. CLI Example: @@ -297,13 +290,13 @@ def start(): .. code-block:: bash salt '*' bluetooth.start - ''' - out = __salt__['service.start']('bluetooth') + """ + out = __salt__["service.start"]("bluetooth") return out def stop(): - ''' + """ Stop the bluetooth service. CLI Example: @@ -311,6 +304,6 @@ def stop(): .. code-block:: bash salt '*' bluetooth.stop - ''' - out = __salt__['service.stop']('bluetooth') + """ + out = __salt__["service.stop"]("bluetooth") return out diff --git a/salt/modules/boto3_elasticache.py b/salt/modules/boto3_elasticache.py index 0cd48f5b273..8d20a9c6ee8 100644 --- a/salt/modules/boto3_elasticache.py +++ b/salt/modules/boto3_elasticache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Execution module for Amazon Elasticache using boto3 =================================================== @@ -41,56 +41,61 @@ Execution module for Amazon Elasticache using boto3 region: us-east-1 :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time -# Import Salt libs -from salt.exceptions import SaltInvocationError, CommandExecutionError import salt.utils.compat import salt.utils.versions +# Import Salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) # Import third party libs try: - #pylint: disable=unused-import + # pylint: disable=unused-import import botocore import boto3 - #pylint: enable=unused-import - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + # pylint: enable=unused-import + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO3 = True except ImportError: HAS_BOTO3 = False def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ return salt.utils.versions.check_boto_reqs() def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO3: - __utils__['boto3.assign_funcs'](__name__, 'elasticache', - get_conn_funcname='_get_conn', - cache_id_funcname='_cache_id', - exactly_one_funcname=None) + __utils__["boto3.assign_funcs"]( + __name__, + "elasticache", + get_conn_funcname="_get_conn", + cache_id_funcname="_cache_id", + exactly_one_funcname=None, + ) -def _collect_results(func, item, args, marker='Marker'): +def _collect_results(func, item, args, marker="Marker"): ret = [] - Marker = args[marker] if marker in args else '' + Marker = args[marker] if marker in args else "" while Marker is not None: r = func(**args) ret += r.get(item) @@ -99,18 +104,30 @@ def _collect_results(func, item, args, marker='Marker'): return ret -def _describe_resource(name=None, name_param=None, res_type=None, info_node=None, conn=None, - region=None, key=None, keyid=None, profile=None, **args): +def _describe_resource( + name=None, + name_param=None, + res_type=None, + info_node=None, + conn=None, + region=None, + key=None, + keyid=None, + profile=None, + **args +): if conn is None: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - func = 'describe_'+res_type+'s' + func = "describe_" + res_type + "s" f = getattr(conn, func) except (AttributeError, KeyError) as e: - raise SaltInvocationError("No function '{0}()' found: {1}".format(func, e.message)) + raise SaltInvocationError( + "No function '{0}()' found: {1}".format(func, e.message) + ) # Undocumented, but you can't pass 'Marker' if searching for a specific resource... - args.update({name_param: name} if name else {'Marker': ''}) - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args.update({name_param: name} if name else {"Marker": ""}) + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: return _collect_results(f, info_node, args) except botocore.exceptions.ClientError as e: @@ -118,170 +135,233 @@ def _describe_resource(name=None, name_param=None, res_type=None, info_node=None return None -def _delete_resource(name, name_param, desc, res_type, wait=0, status_param=None, - status_gone='deleted', region=None, key=None, keyid=None, profile=None, - **args): - ''' +def _delete_resource( + name, + name_param, + desc, + res_type, + wait=0, + status_param=None, + status_gone="deleted", + region=None, + key=None, + keyid=None, + profile=None, + **args +): + """ Delete a generic Elasticache resource. - ''' + """ try: wait = int(wait) except Exception: # pylint: disable=broad-except - raise SaltInvocationError("Bad value ('{0}') passed for 'wait' param - must be an " - "int or boolean.".format(wait)) + raise SaltInvocationError( + "Bad value ('{0}') passed for 'wait' param - must be an " + "int or boolean.".format(wait) + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if name_param in args: log.info( "'name: %s' param being overridden by explicitly provided '%s: %s'", - name, name_param, args[name_param] + name, + name_param, + args[name_param], ) name = args[name_param] else: args[name_param] = name - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: - func = 'delete_'+res_type + func = "delete_" + res_type f = getattr(conn, func) if wait: - func = 'describe_'+res_type+'s' + func = "describe_" + res_type + "s" s = globals()[func] except (AttributeError, KeyError) as e: - raise SaltInvocationError("No function '{0}()' found: {1}".format(func, e.message)) + raise SaltInvocationError( + "No function '{0}()' found: {1}".format(func, e.message) + ) try: f(**args) if not wait: - log.info('%s %s deletion requested.', desc.title(), name) + log.info("%s %s deletion requested.", desc.title(), name) return True - log.info('Waiting up to %s seconds for %s %s to be deleted.', wait, desc, name) + log.info("Waiting up to %s seconds for %s %s to be deleted.", wait, desc, name) orig_wait = wait while wait > 0: r = s(name=name, conn=conn) if not r or (r and r[0].get(status_param) == status_gone): - log.info('%s %s deleted.', desc.title(), name) + log.info("%s %s deleted.", desc.title(), name) return True sleep = wait if wait % 60 == wait else 60 - log.info('Sleeping %s seconds for %s %s to be deleted.', - sleep, desc, name) + log.info("Sleeping %s seconds for %s %s to be deleted.", sleep, desc, name) time.sleep(sleep) wait -= sleep - log.error('%s %s not deleted after %s seconds!', desc.title(), name, orig_wait) + log.error("%s %s not deleted after %s seconds!", desc.title(), name, orig_wait) return False except botocore.exceptions.ClientError as e: - log.error('Failed to delete %s %s: %s', desc, name, e) + log.error("Failed to delete %s %s: %s", desc, name, e) return False -def _create_resource(name, name_param=None, desc=None, res_type=None, wait=0, status_param=None, - status_good='available', region=None, key=None, keyid=None, profile=None, - **args): +def _create_resource( + name, + name_param=None, + desc=None, + res_type=None, + wait=0, + status_param=None, + status_good="available", + region=None, + key=None, + keyid=None, + profile=None, + **args +): try: wait = int(wait) except Exception: # pylint: disable=broad-except - raise SaltInvocationError("Bad value ('{0}') passed for 'wait' param - must be an " - "int or boolean.".format(wait)) + raise SaltInvocationError( + "Bad value ('{0}') passed for 'wait' param - must be an " + "int or boolean.".format(wait) + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if name_param in args: log.info( "'name: %s' param being overridden by explicitly provided '%s: %s'", - name, name_param, args[name_param] + name, + name_param, + args[name_param], ) name = args[name_param] else: args[name_param] = name - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: - func = 'create_'+res_type + func = "create_" + res_type f = getattr(conn, func) if wait: - func = 'describe_'+res_type+'s' + func = "describe_" + res_type + "s" s = globals()[func] except (AttributeError, KeyError) as e: - raise SaltInvocationError("No function '{0}()' found: {1}".format(func, e.message)) + raise SaltInvocationError( + "No function '{0}()' found: {1}".format(func, e.message) + ) try: f(**args) if not wait: - log.info('%s %s created.', desc.title(), name) + log.info("%s %s created.", desc.title(), name) return True - log.info('Waiting up to %s seconds for %s %s to be become available.', - wait, desc, name) + log.info( + "Waiting up to %s seconds for %s %s to be become available.", + wait, + desc, + name, + ) orig_wait = wait while wait > 0: r = s(name=name, conn=conn) if r and r[0].get(status_param) == status_good: - log.info('%s %s created and available.', desc.title(), name) + log.info("%s %s created and available.", desc.title(), name) return True sleep = wait if wait % 60 == wait else 60 - log.info('Sleeping %s seconds for %s %s to become available.', - sleep, desc, name) + log.info( + "Sleeping %s seconds for %s %s to become available.", sleep, desc, name + ) time.sleep(sleep) wait -= sleep - log.error('%s %s not available after %s seconds!', - desc.title(), name, orig_wait) + log.error( + "%s %s not available after %s seconds!", desc.title(), name, orig_wait + ) return False except botocore.exceptions.ClientError as e: - msg = 'Failed to create {0} {1}: {2}'.format(desc, name, e) + msg = "Failed to create {0} {1}: {2}".format(desc, name, e) log.error(msg) return False -def _modify_resource(name, name_param=None, desc=None, res_type=None, wait=0, status_param=None, - status_good='available', region=None, key=None, keyid=None, profile=None, - **args): +def _modify_resource( + name, + name_param=None, + desc=None, + res_type=None, + wait=0, + status_param=None, + status_good="available", + region=None, + key=None, + keyid=None, + profile=None, + **args +): try: wait = int(wait) except Exception: # pylint: disable=broad-except - raise SaltInvocationError("Bad value ('{0}') passed for 'wait' param - must be an " - "int or boolean.".format(wait)) + raise SaltInvocationError( + "Bad value ('{0}') passed for 'wait' param - must be an " + "int or boolean.".format(wait) + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if name_param in args: log.info( "'name: %s' param being overridden by explicitly provided '%s: %s'", - name, name_param, args[name_param] + name, + name_param, + args[name_param], ) name = args[name_param] else: args[name_param] = name - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: - func = 'modify_'+res_type + func = "modify_" + res_type f = getattr(conn, func) if wait: - func = 'describe_'+res_type+'s' + func = "describe_" + res_type + "s" s = globals()[func] except (AttributeError, KeyError) as e: - raise SaltInvocationError("No function '{0}()' found: {1}".format(func, e.message)) + raise SaltInvocationError( + "No function '{0}()' found: {1}".format(func, e.message) + ) try: f(**args) if not wait: - log.info('%s %s modification requested.', desc.title(), name) + log.info("%s %s modification requested.", desc.title(), name) return True - log.info('Waiting up to %s seconds for %s %s to be become available.', - wait, desc, name) + log.info( + "Waiting up to %s seconds for %s %s to be become available.", + wait, + desc, + name, + ) orig_wait = wait while wait > 0: r = s(name=name, conn=conn) if r and r[0].get(status_param) == status_good: - log.info('%s %s modified and available.', desc.title(), name) + log.info("%s %s modified and available.", desc.title(), name) return True sleep = wait if wait % 60 == wait else 60 - log.info('Sleeping %s seconds for %s %s to become available.', - sleep, desc, name) + log.info( + "Sleeping %s seconds for %s %s to become available.", sleep, desc, name + ) time.sleep(sleep) wait -= sleep - log.error('%s %s not available after %s seconds!', - desc.title(), name, orig_wait) + log.error( + "%s %s not available after %s seconds!", desc.title(), name, orig_wait + ) return False except botocore.exceptions.ClientError as e: - msg = 'Failed to modify {0} {1}: {2}'.format(desc, name, e) + msg = "Failed to modify {0} {1}: {2}".format(desc, name, e) log.error(msg) return False -def describe_cache_clusters(name=None, conn=None, region=None, key=None, - keyid=None, profile=None, **args): - ''' +def describe_cache_clusters( + name=None, conn=None, region=None, key=None, keyid=None, profile=None, **args +): + """ Return details about all (or just one) Elasticache cache clusters. Example: @@ -290,14 +370,25 @@ def describe_cache_clusters(name=None, conn=None, region=None, key=None, salt myminion boto3_elasticache.describe_cache_clusters salt myminion boto3_elasticache.describe_cache_clusters myelasticache - ''' - return _describe_resource(name=name, name_param='CacheClusterId', res_type='cache_cluster', - info_node='CacheClusters', conn=conn, region=region, key=key, - keyid=keyid, profile=profile, **args) + """ + return _describe_resource( + name=name, + name_param="CacheClusterId", + res_type="cache_cluster", + info_node="CacheClusters", + conn=conn, + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def cache_cluster_exists(name, conn=None, region=None, key=None, keyid=None, profile=None): - ''' +def cache_cluster_exists( + name, conn=None, region=None, key=None, keyid=None, profile=None +): + """ Check to see if a cache cluster exists. Example: @@ -305,13 +396,25 @@ def cache_cluster_exists(name, conn=None, region=None, key=None, keyid=None, pro .. code-block:: bash salt myminion boto3_elasticache.cache_cluster_exists myelasticache - ''' - return bool(describe_cache_clusters(name=name, conn=conn, region=region, key=key, keyid=keyid, profile=profile)) + """ + return bool( + describe_cache_clusters( + name=name, conn=conn, region=region, key=key, keyid=keyid, profile=profile + ) + ) -def create_cache_cluster(name, wait=600, security_groups=None, - region=None, key=None, keyid=None, profile=None, **args): - ''' +def create_cache_cluster( + name, + wait=600, + security_groups=None, + region=None, + key=None, + keyid=None, + profile=None, + **args +): + """ Create a cache cluster. Example: @@ -324,24 +427,43 @@ def create_cache_cluster(name, wait=600, security_groups=None, NumCacheNodes=1 \ SecurityGroupIds='[sg-11223344]' \ CacheSubnetGroupName=myCacheSubnetGroup - ''' + """ if security_groups: if not isinstance(security_groups, list): security_groups = [security_groups] - sgs = __salt__['boto_secgroup.convert_to_group_ids'](groups=security_groups, region=region, - key=key, keyid=keyid, profile=profile) - if 'SecurityGroupIds' not in args: - args['SecurityGroupIds'] = [] - args['SecurityGroupIds'] += sgs - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - return _create_resource(name, name_param='CacheClusterId', desc='cache cluster', - res_type='cache_cluster', wait=wait, status_param='CacheClusterStatus', - region=region, key=key, keyid=keyid, profile=profile, **args) + sgs = __salt__["boto_secgroup.convert_to_group_ids"]( + groups=security_groups, region=region, key=key, keyid=keyid, profile=profile + ) + if "SecurityGroupIds" not in args: + args["SecurityGroupIds"] = [] + args["SecurityGroupIds"] += sgs + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + return _create_resource( + name, + name_param="CacheClusterId", + desc="cache cluster", + res_type="cache_cluster", + wait=wait, + status_param="CacheClusterStatus", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def modify_cache_cluster(name, wait=600, security_groups=None, region=None, - key=None, keyid=None, profile=None, **args): - ''' +def modify_cache_cluster( + name, + wait=600, + security_groups=None, + region=None, + key=None, + keyid=None, + profile=None, + **args +): + """ Update a cache cluster in place. Notes: {ApplyImmediately: False} is pretty danged silly in the context of salt. @@ -359,23 +481,36 @@ def modify_cache_cluster(name, wait=600, security_groups=None, region=None, salt myminion boto3_elasticache.create_cache_cluster name=myCacheCluster \ NotificationTopicStatus=inactive - ''' + """ if security_groups: if not isinstance(security_groups, list): security_groups = [security_groups] - sgs = __salt__['boto_secgroup.convert_to_group_ids'](groups=security_groups, region=region, - key=key, keyid=keyid, profile=profile) - if 'SecurityGroupIds' not in args: - args['SecurityGroupIds'] = [] - args['SecurityGroupIds'] += sgs - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - return _modify_resource(name, name_param='CacheClusterId', desc='cache cluster', - res_type='cache_cluster', wait=wait, status_param='CacheClusterStatus', - region=region, key=key, keyid=keyid, profile=profile, **args) + sgs = __salt__["boto_secgroup.convert_to_group_ids"]( + groups=security_groups, region=region, key=key, keyid=keyid, profile=profile + ) + if "SecurityGroupIds" not in args: + args["SecurityGroupIds"] = [] + args["SecurityGroupIds"] += sgs + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + return _modify_resource( + name, + name_param="CacheClusterId", + desc="cache cluster", + res_type="cache_cluster", + wait=wait, + status_param="CacheClusterStatus", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def delete_cache_cluster(name, wait=600, region=None, key=None, keyid=None, profile=None, **args): - ''' +def delete_cache_cluster( + name, wait=600, region=None, key=None, keyid=None, profile=None, **args +): + """ Delete a cache cluster. Example: @@ -383,15 +518,26 @@ def delete_cache_cluster(name, wait=600, region=None, key=None, keyid=None, prof .. code-block:: bash salt myminion boto3_elasticache.delete myelasticache - ''' - return _delete_resource(name, name_param='CacheClusterId', desc='cache cluster', - res_type='cache_cluster', wait=wait, - status_param='CacheClusterStatus', - region=region, key=key, keyid=keyid, profile=profile, **args) + """ + return _delete_resource( + name, + name_param="CacheClusterId", + desc="cache cluster", + res_type="cache_cluster", + wait=wait, + status_param="CacheClusterStatus", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def describe_replication_groups(name=None, conn=None, region=None, key=None, keyid=None, profile=None): - ''' +def describe_replication_groups( + name=None, conn=None, region=None, key=None, keyid=None, profile=None +): + """ Return details about all (or just one) Elasticache replication groups. Example: @@ -400,14 +546,22 @@ def describe_replication_groups(name=None, conn=None, region=None, key=None, key salt myminion boto3_elasticache.describe_replication_groups salt myminion boto3_elasticache.describe_replication_groups myelasticache - ''' - return _describe_resource(name=name, name_param='ReplicationGroupId', - res_type='replication_group', info_node='ReplicationGroups', - conn=conn, region=region, key=key, keyid=keyid, profile=profile) + """ + return _describe_resource( + name=name, + name_param="ReplicationGroupId", + res_type="replication_group", + info_node="ReplicationGroups", + conn=conn, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) def replication_group_exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if a replication group exists. Example: @@ -415,14 +569,25 @@ def replication_group_exists(name, region=None, key=None, keyid=None, profile=No .. code-block:: bash salt myminion boto3_elasticache.replication_group_exists myelasticache - ''' - return bool(describe_replication_groups(name=name, region=region, key=key, keyid=keyid, - profile=profile)) + """ + return bool( + describe_replication_groups( + name=name, region=region, key=key, keyid=keyid, profile=profile + ) + ) -def create_replication_group(name, wait=600, security_groups=None, region=None, key=None, keyid=None, - profile=None, **args): - ''' +def create_replication_group( + name, + wait=600, + security_groups=None, + region=None, + key=None, + keyid=None, + profile=None, + **args +): + """ Create a replication group. Params are extensive and variable - see http://boto3.readthedocs.io/en/latest/reference/services/elasticache.html?#ElastiCache.Client.create_replication_group @@ -435,24 +600,43 @@ def create_replication_group(name, wait=600, security_groups=None, region=None, salt myminion boto3_elasticache.create_replication_group \ name=myelasticache \ ReplicationGroupDescription=description - ''' + """ if security_groups: if not isinstance(security_groups, list): security_groups = [security_groups] - sgs = __salt__['boto_secgroup.convert_to_group_ids'](groups=security_groups, region=region, - key=key, keyid=keyid, profile=profile) - if 'SecurityGroupIds' not in args: - args['SecurityGroupIds'] = [] - args['SecurityGroupIds'] += sgs - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - return _create_resource(name, name_param='ReplicationGroupId', desc='replication group', - res_type='replication_group', wait=wait, status_param='Status', - region=region, key=key, keyid=keyid, profile=profile, **args) + sgs = __salt__["boto_secgroup.convert_to_group_ids"]( + groups=security_groups, region=region, key=key, keyid=keyid, profile=profile + ) + if "SecurityGroupIds" not in args: + args["SecurityGroupIds"] = [] + args["SecurityGroupIds"] += sgs + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + return _create_resource( + name, + name_param="ReplicationGroupId", + desc="replication group", + res_type="replication_group", + wait=wait, + status_param="Status", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def modify_replication_group(name, wait=600, security_groups=None, region=None, key=None, keyid=None, - profile=None, **args): - ''' +def modify_replication_group( + name, + wait=600, + security_groups=None, + region=None, + key=None, + keyid=None, + profile=None, + **args +): + """ Modify a replication group. Example: @@ -462,23 +646,36 @@ def modify_replication_group(name, wait=600, security_groups=None, region=None, salt myminion boto3_elasticache.modify_replication_group \ name=myelasticache \ ReplicationGroupDescription=newDescription - ''' + """ if security_groups: if not isinstance(security_groups, list): security_groups = [security_groups] - sgs = __salt__['boto_secgroup.convert_to_group_ids'](groups=security_groups, region=region, - key=key, keyid=keyid, profile=profile) - if 'SecurityGroupIds' not in args: - args['SecurityGroupIds'] = [] - args['SecurityGroupIds'] += sgs - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - return _modify_resource(name, name_param='ReplicationGroupId', desc='replication group', - res_type='replication_group', wait=wait, status_param='Status', - region=region, key=key, keyid=keyid, profile=profile, **args) + sgs = __salt__["boto_secgroup.convert_to_group_ids"]( + groups=security_groups, region=region, key=key, keyid=keyid, profile=profile + ) + if "SecurityGroupIds" not in args: + args["SecurityGroupIds"] = [] + args["SecurityGroupIds"] += sgs + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + return _modify_resource( + name, + name_param="ReplicationGroupId", + desc="replication group", + res_type="replication_group", + wait=wait, + status_param="Status", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def delete_replication_group(name, wait=600, region=None, key=None, keyid=None, profile=None, **args): - ''' +def delete_replication_group( + name, wait=600, region=None, key=None, keyid=None, profile=None, **args +): + """ Delete an ElastiCache replication group, optionally taking a snapshot first. Example: @@ -486,14 +683,26 @@ def delete_replication_group(name, wait=600, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto3_elasticache.delete_replication_group my-replication-group - ''' - return _delete_resource(name, name_param='ReplicationGroupId', desc='replication group', - res_type='replication_group', wait=wait, status_param='Status', - region=region, key=key, keyid=keyid, profile=profile, **args) + """ + return _delete_resource( + name, + name_param="ReplicationGroupId", + desc="replication group", + res_type="replication_group", + wait=wait, + status_param="Status", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def describe_cache_subnet_groups(name=None, conn=None, region=None, key=None, keyid=None, profile=None): - ''' +def describe_cache_subnet_groups( + name=None, conn=None, region=None, key=None, keyid=None, profile=None +): + """ Return details about all (or just one) Elasticache replication groups. Example: @@ -501,14 +710,22 @@ def describe_cache_subnet_groups(name=None, conn=None, region=None, key=None, ke .. code-block:: bash salt myminion boto3_elasticache.describe_cache_subnet_groups region=us-east-1 - ''' - return _describe_resource(name=name, name_param='CacheSubnetGroupName', - res_type='cache_subnet_group', info_node='CacheSubnetGroups', - conn=conn, region=region, key=key, keyid=keyid, profile=profile) + """ + return _describe_resource( + name=name, + name_param="CacheSubnetGroupName", + res_type="cache_subnet_group", + info_node="CacheSubnetGroups", + conn=conn, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) def cache_subnet_group_exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if an ElastiCache subnet group exists. Example: @@ -516,12 +733,16 @@ def cache_subnet_group_exists(name, region=None, key=None, keyid=None, profile=N .. code-block:: bash salt myminion boto3_elasticache.cache_subnet_group_exists my-subnet-group - ''' - return bool(describe_cache_subnet_groups(name=name, region=region, key=key, keyid=keyid, profile=profile)) + """ + return bool( + describe_cache_subnet_groups( + name=name, region=region, key=key, keyid=keyid, profile=profile + ) + ) def list_cache_subnet_groups(region=None, key=None, keyid=None, profile=None): - ''' + """ Return a list of all cache subnet group names Example: @@ -529,13 +750,17 @@ def list_cache_subnet_groups(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto3_elasticache.list_cache_subnet_groups region=us-east-1 - ''' - return [g['CacheSubnetGroupName'] for g in - describe_cache_subnet_groups(None, region, key, keyid, profile)] + """ + return [ + g["CacheSubnetGroupName"] + for g in describe_cache_subnet_groups(None, region, key, keyid, profile) + ] -def create_cache_subnet_group(name, subnets=None, region=None, key=None, keyid=None, profile=None, **args): - ''' +def create_cache_subnet_group( + name, subnets=None, region=None, key=None, keyid=None, profile=None, **args +): + """ Create an ElastiCache subnet group Example: @@ -545,35 +770,52 @@ def create_cache_subnet_group(name, subnets=None, region=None, key=None, keyid=N salt myminion boto3_elasticache.create_cache_subnet_group name=my-subnet-group \ CacheSubnetGroupDescription="description" \ subnets='[myVPCSubnet1,myVPCSubnet2]' - ''' + """ if subnets: - if 'SubnetIds' not in args: - args['SubnetIds'] = [] + if "SubnetIds" not in args: + args["SubnetIds"] = [] if not isinstance(subnets, list): subnets = [subnets] for subnet in subnets: - if subnet.startswith('subnet-'): + if subnet.startswith("subnet-"): # Moderately safe assumption... :) Will be caught further down if incorrect. - args['SubnetIds'] += [subnet] + args["SubnetIds"] += [subnet] continue - sn = __salt__['boto_vpc.describe_subnets'](subnet_names=subnet, region=region, key=key, - keyid=keyid, profile=profile).get('subnets') + sn = __salt__["boto_vpc.describe_subnets"]( + subnet_names=subnet, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("subnets") if not sn: raise SaltInvocationError( - 'Could not resolve Subnet Name {0} to an ID.'.format(subnet)) + "Could not resolve Subnet Name {0} to an ID.".format(subnet) + ) if len(sn) == 1: - args['SubnetIds'] += [sn[0]['id']] + args["SubnetIds"] += [sn[0]["id"]] elif len(sn) > 1: raise CommandExecutionError( - 'Subnet Name {0} returned more than one ID.'.format(subnet)) - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - return _create_resource(name, name_param='CacheSubnetGroupName', desc='cache subnet group', - res_type='cache_subnet_group', - region=region, key=key, keyid=keyid, profile=profile, **args) + "Subnet Name {0} returned more than one ID.".format(subnet) + ) + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + return _create_resource( + name, + name_param="CacheSubnetGroupName", + desc="cache subnet group", + res_type="cache_subnet_group", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def modify_cache_subnet_group(name, subnets=None, region=None, key=None, keyid=None, profile=None, **args): - ''' +def modify_cache_subnet_group( + name, subnets=None, region=None, key=None, keyid=None, profile=None, **args +): + """ Modify an ElastiCache subnet group Example: @@ -583,35 +825,51 @@ def modify_cache_subnet_group(name, subnets=None, region=None, key=None, keyid=N salt myminion boto3_elasticache.modify_cache_subnet_group \ name=my-subnet-group \ subnets='[myVPCSubnet3]' - ''' + """ if subnets: - if 'SubnetIds' not in args: - args['SubnetIds'] = [] + if "SubnetIds" not in args: + args["SubnetIds"] = [] if not isinstance(subnets, list): subnets = [subnets] for subnet in subnets: - sn = __salt__['boto_vpc.describe_subnets'](subnet_names=subnet, - region=region, key=key, keyid=keyid, - profile=profile).get('subnets') + sn = __salt__["boto_vpc.describe_subnets"]( + subnet_names=subnet, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("subnets") if len(sn) == 1: - args['SubnetIds'] += [sn[0]['id']] + args["SubnetIds"] += [sn[0]["id"]] elif len(sn) > 1: raise CommandExecutionError( - 'Subnet Name {0} returned more than one ID.'.format(subnet)) - elif subnet.startswith('subnet-'): + "Subnet Name {0} returned more than one ID.".format(subnet) + ) + elif subnet.startswith("subnet-"): # Moderately safe assumption... :) Will be caught later if incorrect. - args['SubnetIds'] += [subnet] + args["SubnetIds"] += [subnet] else: raise SaltInvocationError( - 'Could not resolve Subnet Name {0} to an ID.'.format(subnet)) - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - return _modify_resource(name, name_param='CacheSubnetGroupName', desc='cache subnet group', - res_type='cache_subnet_group', - region=region, key=key, keyid=keyid, profile=profile, **args) + "Could not resolve Subnet Name {0} to an ID.".format(subnet) + ) + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + return _modify_resource( + name, + name_param="CacheSubnetGroupName", + desc="cache subnet group", + res_type="cache_subnet_group", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def delete_cache_subnet_group(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def delete_cache_subnet_group( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ Delete an ElastiCache subnet group. Example: @@ -619,14 +877,24 @@ def delete_cache_subnet_group(name, region=None, key=None, keyid=None, profile=N .. code-block:: bash salt myminion boto3_elasticache.delete_subnet_group my-subnet-group region=us-east-1 - ''' - return _delete_resource(name, name_param='CacheSubnetGroupName', - desc='cache subnet group', res_type='cache_subnet_group', - region=region, key=key, keyid=keyid, profile=profile, **args) + """ + return _delete_resource( + name, + name_param="CacheSubnetGroupName", + desc="cache subnet group", + res_type="cache_subnet_group", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def describe_cache_security_groups(name=None, conn=None, region=None, key=None, keyid=None, profile=None): - ''' +def describe_cache_security_groups( + name=None, conn=None, region=None, key=None, keyid=None, profile=None +): + """ Return details about all (or just one) Elasticache cache clusters. Example: @@ -635,15 +903,22 @@ def describe_cache_security_groups(name=None, conn=None, region=None, key=None, salt myminion boto3_elasticache.describe_cache_security_groups salt myminion boto3_elasticache.describe_cache_security_groups mycachesecgrp - ''' - return _describe_resource(name=name, name_param='CacheSecurityGroupName', - res_type='cache_security_group', - info_node='CacheSecurityGroups', conn=conn, region=region, key=key, - keyid=keyid, profile=profile) + """ + return _describe_resource( + name=name, + name_param="CacheSecurityGroupName", + res_type="cache_security_group", + info_node="CacheSecurityGroups", + conn=conn, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) def cache_security_group_exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if an ElastiCache security group exists. Example: @@ -651,12 +926,18 @@ def cache_security_group_exists(name, region=None, key=None, keyid=None, profile .. code-block:: bash salt myminion boto3_elasticache.cache_security_group_exists mysecuritygroup - ''' - return bool(describe_cache_security_groups(name=name, region=region, key=key, keyid=keyid, profile=profile)) + """ + return bool( + describe_cache_security_groups( + name=name, region=region, key=key, keyid=keyid, profile=profile + ) + ) -def create_cache_security_group(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def create_cache_security_group( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ Create a cache security group. Example: @@ -664,14 +945,24 @@ def create_cache_security_group(name, region=None, key=None, keyid=None, profile .. code-block:: bash salt myminion boto3_elasticache.create_cache_security_group mycachesecgrp Description='My Cache Security Group' - ''' - return _create_resource(name, name_param='CacheSecurityGroupName', desc='cache security group', - res_type='cache_security_group', - region=region, key=key, keyid=keyid, profile=profile, **args) + """ + return _create_resource( + name, + name_param="CacheSecurityGroupName", + desc="cache security group", + res_type="cache_security_group", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def delete_cache_security_group(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def delete_cache_security_group( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ Delete a cache security group. Example: @@ -679,14 +970,24 @@ def delete_cache_security_group(name, region=None, key=None, keyid=None, profile .. code-block:: bash salt myminion boto3_elasticache.delete_cache_security_group myelasticachesg - ''' - return _delete_resource(name, name_param='CacheSecurityGroupName', - desc='cache security group', res_type='cache_security_group', - region=region, key=key, keyid=keyid, profile=profile, **args) + """ + return _delete_resource( + name, + name_param="CacheSecurityGroupName", + desc="cache security group", + res_type="cache_security_group", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def authorize_cache_security_group_ingress(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def authorize_cache_security_group_ingress( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ Authorize network ingress from an ec2 security group to a cache security group. Example: @@ -697,30 +998,36 @@ def authorize_cache_security_group_ingress(name, region=None, key=None, keyid=No mycachesecgrp \ EC2SecurityGroupName=someEC2sg \ EC2SecurityGroupOwnerId=SOMEOWNERID - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if 'CacheSecurityGroupName' in args: + if "CacheSecurityGroupName" in args: log.info( "'name: %s' param being overridden by explicitly provided " "'CacheSecurityGroupName: %s'", - name, args['CacheSecurityGroupName'] + name, + args["CacheSecurityGroupName"], ) - name = args['CacheSecurityGroupName'] + name = args["CacheSecurityGroupName"] else: - args['CacheSubnetGroupName'] = name - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args["CacheSubnetGroupName"] = name + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: conn.authorize_cache_security_group_ingress(**args) - log.info('Authorized %s to cache security group %s.', - args['EC2SecurityGroupName'], name) + log.info( + "Authorized %s to cache security group %s.", + args["EC2SecurityGroupName"], + name, + ) return True except botocore.exceptions.ClientError as e: - log.error('Failed to update security group %s: %s', name, e) + log.error("Failed to update security group %s: %s", name, e) return False -def revoke_cache_security_group_ingress(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def revoke_cache_security_group_ingress( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ Revoke network ingress from an ec2 security group to a cache security group. @@ -732,30 +1039,36 @@ def revoke_cache_security_group_ingress(name, region=None, key=None, keyid=None, mycachesecgrp \ EC2SecurityGroupName=someEC2sg \ EC2SecurityGroupOwnerId=SOMEOWNERID - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if 'CacheSecurityGroupName' in args: + if "CacheSecurityGroupName" in args: log.info( "'name: %s' param being overridden by explicitly provided " "'CacheSecurityGroupName: %s'", - name, args['CacheSecurityGroupName'] + name, + args["CacheSecurityGroupName"], ) - name = args['CacheSecurityGroupName'] + name = args["CacheSecurityGroupName"] else: - args['CacheSubnetGroupName'] = name - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args["CacheSubnetGroupName"] = name + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: conn.revoke_cache_security_group_ingress(**args) - log.info('Revoked %s from cache security group %s.', - args['EC2SecurityGroupName'], name) + log.info( + "Revoked %s from cache security group %s.", + args["EC2SecurityGroupName"], + name, + ) return True except botocore.exceptions.ClientError as e: - log.error('Failed to update security group %s: %s', name, e) + log.error("Failed to update security group %s: %s", name, e) return False -def list_tags_for_resource(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def list_tags_for_resource( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ List tags on an Elasticache resource. Note that this function is essentially useless as it requires a full AWS ARN for the @@ -771,29 +1084,31 @@ def list_tags_for_resource(name, region=None, key=None, keyid=None, profile=None salt myminion boto3_elasticache.list_tags_for_resource \ name'=arn:aws:elasticache:us-west-2:0123456789:snapshot:mySnapshot' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if 'ResourceName' in args: + if "ResourceName" in args: log.info( "'name: %s' param being overridden by explicitly provided " - "'ResourceName: %s'", name, args['ResourceName'] + "'ResourceName: %s'", + name, + args["ResourceName"], ) - name = args['ResourceName'] + name = args["ResourceName"] else: - args['ResourceName'] = name - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args["ResourceName"] = name + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: r = conn.list_tags_for_resource(**args) - if r and 'Taglist' in r: - return r['TagList'] + if r and "Taglist" in r: + return r["TagList"] return [] except botocore.exceptions.ClientError as e: - log.error('Failed to list tags for resource %s: %s', name, e) + log.error("Failed to list tags for resource %s: %s", name, e) return [] def add_tags_to_resource(name, region=None, key=None, keyid=None, profile=None, **args): - ''' + """ Add tags to an Elasticache resource. Note that this function is essentially useless as it requires a full AWS ARN for the @@ -810,28 +1125,32 @@ def add_tags_to_resource(name, region=None, key=None, keyid=None, profile=None, salt myminion boto3_elasticache.add_tags_to_resource \ name'=arn:aws:elasticache:us-west-2:0123456789:snapshot:mySnapshot' \ Tags="[{'Key': 'TeamOwner', 'Value': 'infrastructure'}]" - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if 'ResourceName' in args: + if "ResourceName" in args: log.info( "'name: %s' param being overridden by explicitly provided " - "'ResourceName: %s'", name, args['ResourceName'] + "'ResourceName: %s'", + name, + args["ResourceName"], ) - name = args['ResourceName'] + name = args["ResourceName"] else: - args['ResourceName'] = name - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args["ResourceName"] = name + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: conn.add_tags_to_resource(**args) - log.info('Added tags %s to %s.', args['Tags'], name) + log.info("Added tags %s to %s.", args["Tags"], name) return True except botocore.exceptions.ClientError as e: - log.error('Failed to add tags to %s: %s', name, e) + log.error("Failed to add tags to %s: %s", name, e) return False -def remove_tags_from_resource(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def remove_tags_from_resource( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ Remove tags from an Elasticache resource. Note that this function is essentially useless as it requires a full AWS ARN for the @@ -848,28 +1167,30 @@ def remove_tags_from_resource(name, region=None, key=None, keyid=None, profile=N salt myminion boto3_elasticache.remove_tags_from_resource \ name'=arn:aws:elasticache:us-west-2:0123456789:snapshot:mySnapshot' \ TagKeys="['TeamOwner']" - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if 'ResourceName' in args: + if "ResourceName" in args: log.info( "'name: %s' param being overridden by explicitly provided " - "'ResourceName: %s'", name, args['ResourceName'] + "'ResourceName: %s'", + name, + args["ResourceName"], ) - name = args['ResourceName'] + name = args["ResourceName"] else: - args['ResourceName'] = name - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args["ResourceName"] = name + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: conn.remove_tags_from_resource(**args) - log.info('Added tags %s to %s.', args['Tags'], name) + log.info("Added tags %s to %s.", args["Tags"], name) return True except botocore.exceptions.ClientError as e: - log.error('Failed to add tags to %s: %s', name, e) + log.error("Failed to add tags to %s: %s", name, e) return False def copy_snapshot(name, region=None, key=None, keyid=None, profile=None, **args): - ''' + """ Make a copy of an existing snapshot. Example: @@ -878,29 +1199,32 @@ def copy_snapshot(name, region=None, key=None, keyid=None, profile=None, **args) salt myminion boto3_elasticache.copy_snapshot name=mySnapshot \ TargetSnapshotName=copyOfMySnapshot - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if 'SourceSnapshotName' in args: + if "SourceSnapshotName" in args: log.info( "'name: %s' param being overridden by explicitly provided " - "'SourceSnapshotName: %s'", name, args['SourceSnapshotName'] + "'SourceSnapshotName: %s'", + name, + args["SourceSnapshotName"], ) - name = args['SourceSnapshotName'] + name = args["SourceSnapshotName"] else: - args['SourceSnapshotName'] = name - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) + args["SourceSnapshotName"] = name + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) try: conn.copy_snapshot(**args) - log.info('Snapshot %s copied to %s.', name, args['TargetSnapshotName']) + log.info("Snapshot %s copied to %s.", name, args["TargetSnapshotName"]) return True except botocore.exceptions.ClientError as e: - log.error('Failed to copy snapshot %s: %s', name, e) + log.error("Failed to copy snapshot %s: %s", name, e) return False -def describe_cache_parameter_groups(name=None, conn=None, region=None, key=None, keyid=None, - profile=None): - ''' +def describe_cache_parameter_groups( + name=None, conn=None, region=None, key=None, keyid=None, profile=None +): + """ Return details about all (or just one) Elasticache cache clusters. Example: @@ -909,14 +1233,24 @@ def describe_cache_parameter_groups(name=None, conn=None, region=None, key=None, salt myminion boto3_elasticache.describe_cache_parameter_groups salt myminion boto3_elasticache.describe_cache_parameter_groups myParameterGroup - ''' - return _describe_resource(name=name, name_param='CacheParameterGroupName', - res_type='cache_parameter_group', info_node='CacheParameterGroups', - conn=conn, region=region, key=key, keyid=keyid, profile=profile) + """ + return _describe_resource( + name=name, + name_param="CacheParameterGroupName", + res_type="cache_parameter_group", + info_node="CacheParameterGroups", + conn=conn, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def create_cache_parameter_group(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def create_cache_parameter_group( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ Create a cache parameter group. Example: @@ -927,14 +1261,24 @@ def create_cache_parameter_group(name, region=None, key=None, keyid=None, profil name=myParamGroup \ CacheParameterGroupFamily=redis2.8 \ Description="My Parameter Group" - ''' - return _create_resource(name, name_param='CacheParameterGroupName', - desc='cache parameter group', res_type='cache_parameter_group', - region=region, key=key, keyid=keyid, profile=profile, **args) + """ + return _create_resource( + name, + name_param="CacheParameterGroupName", + desc="cache parameter group", + res_type="cache_parameter_group", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) -def delete_cache_parameter_group(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def delete_cache_parameter_group( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ Delete a cache parameter group. Example: @@ -942,7 +1286,15 @@ def delete_cache_parameter_group(name, region=None, key=None, keyid=None, profil .. code-block:: bash salt myminion boto3_elasticache.delete_cache_parameter_group myParamGroup - ''' - return _delete_resource(name, name_param='CacheParameterGroupName', - desc='cache parameter group', res_type='cache_parameter_group', - region=region, key=key, keyid=keyid, profile=profile, **args) + """ + return _delete_resource( + name, + name_param="CacheParameterGroupName", + desc="cache parameter group", + res_type="cache_parameter_group", + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) diff --git a/salt/modules/boto3_elasticsearch.py b/salt/modules/boto3_elasticsearch.py index b50c128e834..7644afefdb7 100644 --- a/salt/modules/boto3_elasticsearch.py +++ b/salt/modules/boto3_elasticsearch.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Elasticsearch Service .. versionadded:: Natrium @@ -46,20 +46,22 @@ Connection module for Amazon Elasticsearch Service :codeauthor: Herbert Buurman :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt libs -from salt.ext import six import salt.utils.compat import salt.utils.json import salt.utils.versions from salt.exceptions import SaltInvocationError + +# Import Salt libs +from salt.ext import six from salt.utils.decorators import depends # Import third party libs @@ -69,9 +71,11 @@ try: # pylint: disable=unused-import import boto3 import botocore + # pylint: enable=unused-import from botocore.exceptions import ClientError, ParamValidationError, WaiterError - logging.getLogger('boto3').setLevel(logging.INFO) + + logging.getLogger("boto3").setLevel(logging.INFO) except ImportError: pass @@ -79,25 +83,29 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' - return salt.utils.versions.check_boto_reqs(boto3_ver='1.2.7') + """ + return salt.utils.versions.check_boto_reqs(boto3_ver="1.2.7") def __init__(opts): _ = opts salt.utils.compat.pack_dunder(__name__) - __utils__['boto3.assign_funcs'](__name__, 'es') + __utils__["boto3.assign_funcs"](__name__, "es") def add_tags( - domain_name=None, - arn=None, - tags=None, - region=None, key=None, keyid=None, profile=None): - ''' + domain_name=None, + arn=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Attaches tags to an existing Elasticsearch domain. Tags are a set of case-sensitive key value pairs. An Elasticsearch domain may have up to 10 tags. @@ -118,39 +126,53 @@ def add_tags( .. code-block:: bash salt myminion boto3_elasticsearch.add_tags domain_name=mydomain tags='{"foo": "bar", "baz": "qux"}' - ''' + """ if not any((arn, domain_name)): - raise SaltInvocationError('At least one of domain_name or arn must be specified.') - ret = {'result': False} + raise SaltInvocationError( + "At least one of domain_name or arn must be specified." + ) + ret = {"result": False} if arn is None: res = describe_elasticsearch_domain( domain_name=domain_name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in res: + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in res: ret.update(res) - elif not res['result']: - ret.update({'error': 'The domain with name "{}" does not exist.'.format(domain_name)}) + elif not res["result"]: + ret.update( + { + "error": 'The domain with name "{}" does not exist.'.format( + domain_name + ) + } + ) else: - arn = res['response'].get('ARN') + arn = res["response"].get("ARN") if arn: boto_params = { - 'ARN': arn, - 'TagList': [{'Key': k, 'Value': value} for k, value in six.iteritems(tags or {})] + "ARN": arn, + "TagList": [ + {"Key": k, "Value": value} for k, value in six.iteritems(tags or {}) + ], } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.add_tags(**boto_params) - ret['result'] = True + ret["result"] = True except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.12.21') +@depends("botocore", version="1.12.21") def cancel_elasticsearch_service_software_update( - domain_name, - region=None, keyid=None, key=None, profile=None): - ''' + domain_name, region=None, keyid=None, key=None, profile=None +): + """ Cancels a scheduled service software update for an Amazon ES domain. You can only perform this operation before the AutomatedUpdateDate and when the UpdateStatus is in the PENDING_UPDATE state. @@ -165,34 +187,38 @@ def cancel_elasticsearch_service_software_update( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.cancel_elasticsearch_service_software_update(DomainName=domain_name) - ret['result'] = True - res['response'] = res['ServiceSoftwareOptions'] + ret["result"] = True + res["response"] = res["ServiceSoftwareOptions"] except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret def create_elasticsearch_domain( - domain_name, - elasticsearch_version=None, - elasticsearch_cluster_config=None, - ebs_options=None, - access_policies=None, - snapshot_options=None, - vpc_options=None, - cognito_options=None, - encryption_at_rest_options=None, - node_to_node_encryption_options=None, - advanced_options=None, - log_publishing_options=None, - blocking=False, - region=None, key=None, keyid=None, profile=None): - ''' + domain_name, + elasticsearch_version=None, + elasticsearch_cluster_config=None, + ebs_options=None, + access_policies=None, + snapshot_options=None, + vpc_options=None, + cognito_options=None, + encryption_at_rest_options=None, + node_to_node_encryption_options=None, + advanced_options=None, + log_publishing_options=None, + blocking=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, create a domain. :param str domain_name: The name of the Elasticsearch domain that you are creating. @@ -319,43 +345,48 @@ def create_elasticsearch_domain( "Condition": {"IpAddress": {"aws:SourceIp": ["127.0.0.1"]}}}]}' \\ snapshot_options='{"AutomatedSnapshotStartHour": 0}' \\ advanced_options='{"rest.action.multi.allow_explicit_index": "true"}' - ''' - boto_kwargs = salt.utils.data.filter_falsey({ - 'DomainName': domain_name, - 'ElasticsearchVersion': six.text_type(elasticsearch_version or ''), - 'ElasticsearchClusterConfig': elasticsearch_cluster_config, - 'EBSOptions': ebs_options, - 'AccessPolicies': (salt.utils.json.dumps(access_policies) - if isinstance(access_policies, dict) - else access_policies), - 'SnapshotOptions': snapshot_options, - 'VPCOptions': vpc_options, - 'CognitoOptions': cognito_options, - 'EncryptionAtRestOptions': encryption_at_rest_options, - 'NodeToNodeEncryptionOptions': node_to_node_encryption_options, - 'AdvancedOptions': advanced_options, - 'LogPublishingOptions': log_publishing_options, - }) - ret = {'result': False} + """ + boto_kwargs = salt.utils.data.filter_falsey( + { + "DomainName": domain_name, + "ElasticsearchVersion": six.text_type(elasticsearch_version or ""), + "ElasticsearchClusterConfig": elasticsearch_cluster_config, + "EBSOptions": ebs_options, + "AccessPolicies": ( + salt.utils.json.dumps(access_policies) + if isinstance(access_policies, dict) + else access_policies + ), + "SnapshotOptions": snapshot_options, + "VPCOptions": vpc_options, + "CognitoOptions": cognito_options, + "EncryptionAtRestOptions": encryption_at_rest_options, + "NodeToNodeEncryptionOptions": node_to_node_encryption_options, + "AdvancedOptions": advanced_options, + "LogPublishingOptions": log_publishing_options, + } + ) + ret = {"result": False} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) res = conn.create_elasticsearch_domain(**boto_kwargs) - if res and 'DomainStatus' in res: - ret['result'] = True - ret['response'] = res['DomainStatus'] + if res and "DomainStatus" in res: + ret["result"] = True + ret["response"] = res["DomainStatus"] if blocking: - waiter = __utils__['boto3_elasticsearch.get_waiter'](conn, waiter='ESDomainAvailable') + waiter = __utils__["boto3_elasticsearch.get_waiter"]( + conn, waiter="ESDomainAvailable" + ) waiter.wait(DomainName=domain_name) except (ParamValidationError, ClientError, WaiterError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret def delete_elasticsearch_domain( - domain_name, - blocking=False, - region=None, key=None, keyid=None, profile=None): - ''' + domain_name, blocking=False, region=None, key=None, keyid=None, profile=None +): + """ Permanently deletes the specified Elasticsearch domain and all of its data. Once a domain is deleted, it cannot be recovered. @@ -369,24 +400,25 @@ def delete_elasticsearch_domain( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_elasticsearch_domain(DomainName=domain_name) - ret['result'] = True + ret["result"] = True if blocking: - waiter = __utils__['boto3_elasticsearch.get_waiter'](conn, waiter='ESDomainDeleted') + waiter = __utils__["boto3_elasticsearch.get_waiter"]( + conn, waiter="ESDomainDeleted" + ) waiter.wait(DomainName=domain_name) except (ParamValidationError, ClientError, WaiterError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.7.30') -def delete_elasticsearch_service_role( - region=None, keyid=None, key=None, profile=None): - ''' +@depends("botocore", version="1.7.30") +def delete_elasticsearch_service_role(region=None, keyid=None, key=None, profile=None): + """ Deletes the service-linked role that Elasticsearch Service uses to manage and maintain VPC domains. Role deletion will fail if any existing VPC domains use the role. You must delete any such Elasticsearch domains before deleting the role. @@ -397,21 +429,21 @@ def delete_elasticsearch_service_role( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) conn.delete_elasticsearch_service_role() - ret['result'] = True + ret["result"] = True except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret def describe_elasticsearch_domain( - domain_name, - region=None, keyid=None, key=None, profile=None): - ''' + domain_name, region=None, keyid=None, key=None, profile=None +): + """ Given a domain name gets its status description. :param str domain_name: The name of the domain to get the status of. @@ -423,23 +455,23 @@ def describe_elasticsearch_domain( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) res = conn.describe_elasticsearch_domain(DomainName=domain_name) - if res and 'DomainStatus' in res: - ret['result'] = True - ret['response'] = res['DomainStatus'] + if res and "DomainStatus" in res: + ret["result"] = True + ret["response"] = res["DomainStatus"] except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret def describe_elasticsearch_domain_config( - domain_name, - region=None, keyid=None, key=None, profile=None): - ''' + domain_name, region=None, keyid=None, key=None, profile=None +): + """ Provides cluster configuration information about the specified Elasticsearch domain, such as the state, creation date, update version, and update date for cluster options. @@ -452,23 +484,23 @@ def describe_elasticsearch_domain_config( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) res = conn.describe_elasticsearch_domain_config(DomainName=domain_name) - if res and 'DomainConfig' in res: - ret['result'] = True - ret['response'] = res['DomainConfig'] + if res and "DomainConfig" in res: + ret["result"] = True + ret["response"] = res["DomainConfig"] except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret def describe_elasticsearch_domains( - domain_names, - region=None, keyid=None, key=None, profile=None): - ''' + domain_names, region=None, keyid=None, key=None, profile=None +): + """ Returns domain configuration information about the specified Elasticsearch domains, including the domain ID, domain endpoint, and domain ARN. @@ -486,26 +518,30 @@ def describe_elasticsearch_domains( .. code-block:: bash salt myminion boto3_elasticsearch.describe_elasticsearch_domains '["domain_a", "domain_b"]' - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.describe_elasticsearch_domains(DomainNames=domain_names) - if res and 'DomainStatusList' in res: - ret['result'] = True - ret['response'] = res['DomainStatusList'] + if res and "DomainStatusList" in res: + ret["result"] = True + ret["response"] = res["DomainStatusList"] except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.5.18') +@depends("botocore", version="1.5.18") def describe_elasticsearch_instance_type_limits( - instance_type, - elasticsearch_version, - domain_name=None, - region=None, keyid=None, key=None, profile=None): - ''' + instance_type, + elasticsearch_version, + domain_name=None, + region=None, + keyid=None, + key=None, + profile=None, +): + """ Describe Elasticsearch Limits for a given InstanceType and ElasticsearchVersion. When modifying existing Domain, specify the `` DomainName `` to know what Limits are supported for modifying. @@ -532,29 +568,35 @@ def describe_elasticsearch_instance_type_limits( salt myminion boto3_elasticsearch.describe_elasticsearch_instance_type_limits \\ instance_type=r3.8xlarge.elasticsearch \\ elasticsearch_version='6.2' - ''' - ret = {'result': False} - boto_params = salt.utils.data.filter_falsey({ - 'DomainName': domain_name, - 'InstanceType': instance_type, - 'ElasticsearchVersion': six.text_type(elasticsearch_version), - }) + """ + ret = {"result": False} + boto_params = salt.utils.data.filter_falsey( + { + "DomainName": domain_name, + "InstanceType": instance_type, + "ElasticsearchVersion": six.text_type(elasticsearch_version), + } + ) try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.describe_elasticsearch_instance_type_limits(**boto_params) - if res and 'LimitsByRole' in res: - ret['result'] = True - ret['response'] = res['LimitsByRole'] + if res and "LimitsByRole" in res: + ret["result"] = True + ret["response"] = res["LimitsByRole"] except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.10.15') +@depends("botocore", version="1.10.15") def describe_reserved_elasticsearch_instance_offerings( - reserved_elasticsearch_instance_offering_id=None, - region=None, keyid=None, key=None, profile=None): - ''' + reserved_elasticsearch_instance_offering_id=None, + region=None, + keyid=None, + key=None, + profile=None, +): + """ Lists available reserved Elasticsearch instance offerings. :param str reserved_elasticsearch_instance_offering_id: The offering identifier @@ -568,31 +610,35 @@ def describe_reserved_elasticsearch_instance_offerings( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) boto_params = { - 'ReservedElasticsearchInstanceOfferingId': reserved_elasticsearch_instance_offering_id + "ReservedElasticsearchInstanceOfferingId": reserved_elasticsearch_instance_offering_id } res = [] for page in conn.get_paginator( - 'describe_reserved_elasticsearch_instance_offerings' - ).paginate(**boto_params): - res.extend(page['ReservedElasticsearchInstanceOfferings']) + "describe_reserved_elasticsearch_instance_offerings" + ).paginate(**boto_params): + res.extend(page["ReservedElasticsearchInstanceOfferings"]) if res: - ret['result'] = True - ret['response'] = res + ret["result"] = True + ret["response"] = res except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.10.15') +@depends("botocore", version="1.10.15") def describe_reserved_elasticsearch_instances( - reserved_elasticsearch_instance_id=None, - region=None, keyid=None, key=None, profile=None): - ''' + reserved_elasticsearch_instance_id=None, + region=None, + keyid=None, + key=None, + profile=None, +): + """ Returns information about reserved Elasticsearch instances for this account. :param str reserved_elasticsearch_instance_id: The reserved instance identifier @@ -610,31 +656,31 @@ def describe_reserved_elasticsearch_instances( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) boto_params = { - 'ReservedElasticsearchInstanceId': reserved_elasticsearch_instance_id, + "ReservedElasticsearchInstanceId": reserved_elasticsearch_instance_id, } res = [] for page in conn.get_paginator( - 'describe_reserved_elasticsearch_instances' - ).paginate(**boto_params): - res.extend(page['ReservedElasticsearchInstances']) + "describe_reserved_elasticsearch_instances" + ).paginate(**boto_params): + res.extend(page["ReservedElasticsearchInstances"]) if res: - ret['result'] = True - ret['response'] = res + ret["result"] = True + ret["response"] = res except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.10.77') +@depends("botocore", version="1.10.77") def get_compatible_elasticsearch_versions( - domain_name=None, - region=None, keyid=None, key=None, profile=None): - ''' + domain_name=None, region=None, keyid=None, key=None, profile=None +): + """ Returns a list of upgrade compatible Elastisearch versions. You can optionally pass a ``domain_name`` to get all upgrade compatible Elasticsearch versions for that specific domain. @@ -648,27 +694,23 @@ def get_compatible_elasticsearch_versions( .. versionadded:: Natrium - ''' - ret = {'result': False} - boto_params = salt.utils.data.filter_falsey({ - 'DomainName': domain_name, - }) + """ + ret = {"result": False} + boto_params = salt.utils.data.filter_falsey({"DomainName": domain_name}) try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.get_compatible_elasticsearch_versions(**boto_params) - if res and 'CompatibleElasticsearchVersions' in res: - ret['result'] = True - ret['response'] = res['CompatibleElasticsearchVersions'] + if res and "CompatibleElasticsearchVersions" in res: + ret["result"] = True + ret["response"] = res["CompatibleElasticsearchVersions"] except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.10.77') -def get_upgrade_history( - domain_name, - region=None, keyid=None, key=None, profile=None): - ''' +@depends("botocore", version="1.10.77") +def get_upgrade_history(domain_name, region=None, keyid=None, key=None, profile=None): + """ Retrieves the complete history of the last 10 upgrades that were performed on the domain. :param str domain_name: The name of an Elasticsearch domain. Domain names are @@ -683,27 +725,25 @@ def get_upgrade_history( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) - boto_params = {'DomainName': domain_name} + boto_params = {"DomainName": domain_name} res = [] - for page in conn.get_paginator('get_upgrade_history').paginate(**boto_params): - res.extend(page['UpgradeHistories']) + for page in conn.get_paginator("get_upgrade_history").paginate(**boto_params): + res.extend(page["UpgradeHistories"]) if res: - ret['result'] = True - ret['response'] = res + ret["result"] = True + ret["response"] = res except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.10.77') -def get_upgrade_status( - domain_name, - region=None, keyid=None, key=None, profile=None): - ''' +@depends("botocore", version="1.10.77") +def get_upgrade_status(domain_name, region=None, keyid=None, key=None, profile=None): + """ Retrieves the latest status of the last upgrade or upgrade eligibility check that was performed on the domain. @@ -719,23 +759,22 @@ def get_upgrade_status( .. versionadded:: Natrium - ''' - ret = {'result': False} - boto_params = {'DomainName': domain_name} + """ + ret = {"result": False} + boto_params = {"DomainName": domain_name} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.get_upgrade_status(**boto_params) - ret['result'] = True - ret['response'] = res - del res['ResponseMetadata'] + ret["result"] = True + ret["response"] = res + del res["ResponseMetadata"] except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -def list_domain_names( - region=None, keyid=None, key=None, profile=None): - ''' +def list_domain_names(region=None, keyid=None, key=None, profile=None): + """ Returns the name of all Elasticsearch domains owned by the current user's account. :rtype: dict @@ -745,25 +784,29 @@ def list_domain_names( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.list_domain_names() - if res and 'DomainNames' in res: - ret['result'] = True - ret['response'] = [item['DomainName'] for item in res['DomainNames']] + if res and "DomainNames" in res: + ret["result"] = True + ret["response"] = [item["DomainName"] for item in res["DomainNames"]] except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.5.18') +@depends("botocore", version="1.5.18") def list_elasticsearch_instance_types( - elasticsearch_version, - domain_name=None, - region=None, keyid=None, key=None, profile=None): - ''' + elasticsearch_version, + domain_name=None, + region=None, + keyid=None, + key=None, + profile=None, +): + """ List all Elasticsearch instance types that are supported for given ElasticsearchVersion. :param str elasticsearch_version: Version of Elasticsearch for which list of @@ -779,29 +822,32 @@ def list_elasticsearch_instance_types( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) - boto_params = salt.utils.data.filter_falsey({ - 'ElasticsearchVersion': six.text_type(elasticsearch_version), - 'DomainName': domain_name, - }) + boto_params = salt.utils.data.filter_falsey( + { + "ElasticsearchVersion": six.text_type(elasticsearch_version), + "DomainName": domain_name, + } + ) res = [] - for page in conn.get_paginator('list_elasticsearch_instance_types').paginate(**boto_params): - res.extend(page['ElasticsearchInstanceTypes']) + for page in conn.get_paginator("list_elasticsearch_instance_types").paginate( + **boto_params + ): + res.extend(page["ElasticsearchInstanceTypes"]) if res: - ret['result'] = True - ret['response'] = res + ret["result"] = True + ret["response"] = res except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.5.18') -def list_elasticsearch_versions( - region=None, keyid=None, key=None, profile=None): - ''' +@depends("botocore", version="1.5.18") +def list_elasticsearch_versions(region=None, keyid=None, key=None, profile=None): + """ List all supported Elasticsearch versions. :rtype: dict @@ -811,26 +857,25 @@ def list_elasticsearch_versions( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = [] - for page in conn.get_paginator('list_elasticsearch_versions').paginate(): - res.extend(page['ElasticsearchVersions']) + for page in conn.get_paginator("list_elasticsearch_versions").paginate(): + res.extend(page["ElasticsearchVersions"]) if res: - ret['result'] = True - ret['response'] = res + ret["result"] = True + ret["response"] = res except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret def list_tags( - domain_name=None, - arn=None, - region=None, key=None, keyid=None, profile=None): - ''' + domain_name=None, arn=None, region=None, key=None, keyid=None, profile=None +): + """ Returns all tags for the given Elasticsearch domain. :rtype: dict @@ -840,38 +885,56 @@ def list_tags( .. versionadded:: Natrium - ''' + """ if not any((arn, domain_name)): - raise SaltInvocationError('At least one of domain_name or arn must be specified.') - ret = {'result': False} + raise SaltInvocationError( + "At least one of domain_name or arn must be specified." + ) + ret = {"result": False} if arn is None: res = describe_elasticsearch_domain( domain_name=domain_name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in res: + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in res: ret.update(res) - elif not res['result']: - ret.update({'error': 'The domain with name "{}" does not exist.'.format(domain_name)}) + elif not res["result"]: + ret.update( + { + "error": 'The domain with name "{}" does not exist.'.format( + domain_name + ) + } + ) else: - arn = res['response'].get('ARN') + arn = res["response"].get("ARN") if arn: try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) res = conn.list_tags(ARN=arn) - ret['result'] = True - ret['response'] = {item['Key']: item['Value'] for item in res.get('TagList', [])} + ret["result"] = True + ret["response"] = { + item["Key"]: item["Value"] for item in res.get("TagList", []) + } except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.10.15') +@depends("botocore", version="1.10.15") def purchase_reserved_elasticsearch_instance_offering( - reserved_elasticsearch_instance_offering_id, - reservation_name, - instance_count=None, - region=None, keyid=None, key=None, profile=None): - ''' + reserved_elasticsearch_instance_offering_id, + reservation_name, + instance_count=None, + region=None, + keyid=None, + key=None, + profile=None, +): + """ Allows you to purchase reserved Elasticsearch instances. :param str reserved_elasticsearch_instance_offering_id: The ID of the reserved @@ -886,30 +949,36 @@ def purchase_reserved_elasticsearch_instance_offering( .. versionadded:: Natrium - ''' - ret = {'result': False} - boto_params = salt.utils.data.filter_falsey({ - 'ReservedElasticsearchInstanceOfferingId': reserved_elasticsearch_instance_offering_id, - 'ReservationName': reservation_name, - 'InstanceCount': instance_count, - }) + """ + ret = {"result": False} + boto_params = salt.utils.data.filter_falsey( + { + "ReservedElasticsearchInstanceOfferingId": reserved_elasticsearch_instance_offering_id, + "ReservationName": reservation_name, + "InstanceCount": instance_count, + } + ) try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.purchase_reserved_elasticsearch_instance_offering(**boto_params) if res: - ret['result'] = True - ret['response'] = res + ret["result"] = True + ret["response"] = res except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret def remove_tags( - tag_keys, - domain_name=None, - arn=None, - region=None, key=None, keyid=None, profile=None): - ''' + tag_keys, + domain_name=None, + arn=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Removes the specified set of tags from the specified Elasticsearch domain. :param list tag_keys: List with tag keys you want to remove from the Elasticsearch domain. @@ -928,36 +997,47 @@ def remove_tags( .. code-block:: bash salt myminion boto3_elasticsearch.remove_tags '["foo", "bar"]' domain_name=my_domain - ''' + """ if not any((arn, domain_name)): - raise SaltInvocationError('At least one of domain_name or arn must be specified.') - ret = {'result': False} + raise SaltInvocationError( + "At least one of domain_name or arn must be specified." + ) + ret = {"result": False} if arn is None: res = describe_elasticsearch_domain( domain_name=domain_name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in res: + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in res: ret.update(res) - elif not res['result']: - ret.update({'error': 'The domain with name "{}" does not exist.'.format(domain_name)}) + elif not res["result"]: + ret.update( + { + "error": 'The domain with name "{}" does not exist.'.format( + domain_name + ) + } + ) else: - arn = res['response'].get('ARN') + arn = res["response"].get("ARN") if arn: try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.remove_tags(ARN=arn, - TagKeys=tag_keys) - ret['result'] = True + conn.remove_tags(ARN=arn, TagKeys=tag_keys) + ret["result"] = True except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.12.21') +@depends("botocore", version="1.12.21") def start_elasticsearch_service_software_update( - domain_name, - region=None, keyid=None, key=None, profile=None): - ''' + domain_name, region=None, keyid=None, key=None, profile=None +): + """ Schedules a service software update for an Amazon ES domain. :param str domain_name: The name of the domain that you want to update to the @@ -970,33 +1050,37 @@ def start_elasticsearch_service_software_update( .. versionadded:: Natrium - ''' - ret = {'result': False} - boto_params = {'DomainName': domain_name} + """ + ret = {"result": False} + boto_params = {"DomainName": domain_name} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.start_elasticsearch_service_software_update(**boto_params) - if res and 'ServiceSoftwareOptions' in res: - ret['result'] = True - ret['response'] = res['ServiceSoftwareOptions'] + if res and "ServiceSoftwareOptions" in res: + ret["result"] = True + ret["response"] = res["ServiceSoftwareOptions"] except (ParamValidationError, ClientError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret def update_elasticsearch_domain_config( - domain_name, - elasticsearch_cluster_config=None, - ebs_options=None, - vpc_options=None, - access_policies=None, - snapshot_options=None, - cognito_options=None, - advanced_options=None, - log_publishing_options=None, - blocking=False, - region=None, key=None, keyid=None, profile=None): - ''' + domain_name, + elasticsearch_cluster_config=None, + ebs_options=None, + vpc_options=None, + access_policies=None, + snapshot_options=None, + cognito_options=None, + advanced_options=None, + log_publishing_options=None, + blocking=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Modifies the cluster configuration of the specified Elasticsearch domain, for example setting the instance type and the number of instances. @@ -1102,45 +1186,55 @@ def update_elasticsearch_domain_config( "Condition": {"IpAddress": {"aws:SourceIp": ["127.0.0.1"]}}}]}' \\ snapshot_options='{"AutomatedSnapshotStartHour": 0}' \\ advanced_options='{"rest.action.multi.allow_explicit_index": "true"}' - ''' - ret = {'result': False} - boto_kwargs = salt.utils.data.filter_falsey({ - 'DomainName': domain_name, - 'ElasticsearchClusterConfig': elasticsearch_cluster_config, - 'EBSOptions': ebs_options, - 'SnapshotOptions': snapshot_options, - 'VPCOptions': vpc_options, - 'CognitoOptions': cognito_options, - 'AdvancedOptions': advanced_options, - 'AccessPolicies': (salt.utils.json.dumps(access_policies) - if isinstance(access_policies, dict) - else access_policies), - 'LogPublishingOptions': log_publishing_options, - }) + """ + ret = {"result": False} + boto_kwargs = salt.utils.data.filter_falsey( + { + "DomainName": domain_name, + "ElasticsearchClusterConfig": elasticsearch_cluster_config, + "EBSOptions": ebs_options, + "SnapshotOptions": snapshot_options, + "VPCOptions": vpc_options, + "CognitoOptions": cognito_options, + "AdvancedOptions": advanced_options, + "AccessPolicies": ( + salt.utils.json.dumps(access_policies) + if isinstance(access_policies, dict) + else access_policies + ), + "LogPublishingOptions": log_publishing_options, + } + ) try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.update_elasticsearch_domain_config(**boto_kwargs) - if not res or 'DomainConfig' not in res: - log.warning('Domain was not updated') + if not res or "DomainConfig" not in res: + log.warning("Domain was not updated") else: - ret['result'] = True - ret['response'] = res['DomainConfig'] + ret["result"] = True + ret["response"] = res["DomainConfig"] if blocking: - waiter = __utils__['boto3_elasticsearch.get_waiter'](conn, waiter='ESDomainAvailable') + waiter = __utils__["boto3_elasticsearch.get_waiter"]( + conn, waiter="ESDomainAvailable" + ) waiter.wait(DomainName=domain_name) except (ParamValidationError, ClientError, WaiterError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.10.77') +@depends("botocore", version="1.10.77") def upgrade_elasticsearch_domain( - domain_name, - target_version, - perform_check_only=None, - blocking=False, - region=None, keyid=None, key=None, profile=None): - ''' + domain_name, + target_version, + perform_check_only=None, + blocking=False, + region=None, + keyid=None, + key=None, + profile=None, +): + """ Allows you to either upgrade your domain or perform an Upgrade eligibility check to a compatible Elasticsearch version. @@ -1170,31 +1264,33 @@ def upgrade_elasticsearch_domain( salt myminion boto3_elasticsearch.upgrade_elasticsearch_domain mydomain \\ target_version='6.7' \\ perform_check_only=True - ''' - ret = {'result': False} - boto_params = salt.utils.data.filter_falsey({ - 'DomainName': domain_name, - 'TargetVersion': six.text_type(target_version), - 'PerformCheckOnly': perform_check_only, - }) + """ + ret = {"result": False} + boto_params = salt.utils.data.filter_falsey( + { + "DomainName": domain_name, + "TargetVersion": six.text_type(target_version), + "PerformCheckOnly": perform_check_only, + } + ) try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) res = conn.upgrade_elasticsearch_domain(**boto_params) if res: - ret['result'] = True - ret['response'] = res + ret["result"] = True + ret["response"] = res if blocking: - waiter = __utils__['boto3_elasticsearch.get_waiter'](conn, waiter='ESUpgradeFinished') + waiter = __utils__["boto3_elasticsearch.get_waiter"]( + conn, waiter="ESUpgradeFinished" + ) waiter.wait(DomainName=domain_name) except (ParamValidationError, ClientError, WaiterError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -def exists( - domain_name, - region=None, key=None, keyid=None, profile=None): - ''' +def exists(domain_name, region=None, key=None, keyid=None, profile=None): + """ Given a domain name, check to see if the given domain exists. :param str domain_name: The name of the domain to check. @@ -1205,22 +1301,20 @@ def exists( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.describe_elasticsearch_domain(DomainName=domain_name) - ret['result'] = True + ret["result"] = True except (ParamValidationError, ClientError) as exp: - if exp.response.get('Error', {}).get('Code') != 'ResourceNotFoundException': - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + if exp.response.get("Error", {}).get("Code") != "ResourceNotFoundException": + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -def wait_for_upgrade( - domain_name, - region=None, keyid=None, key=None, profile=None): - ''' +def wait_for_upgrade(domain_name, region=None, keyid=None, key=None, profile=None): + """ Block until an upgrade-in-progress for domain ``name`` is finished. :param str name: The name of the domain to wait for. @@ -1231,24 +1325,25 @@ def wait_for_upgrade( .. versionadded:: Natrium - ''' - ret = {'result': False} + """ + ret = {"result": False} try: conn = _get_conn(region=region, keyid=keyid, key=key, profile=profile) - waiter = __utils__['boto3_elasticsearch.get_waiter'](conn, waiter='ESUpgradeFinished') + waiter = __utils__["boto3_elasticsearch.get_waiter"]( + conn, waiter="ESUpgradeFinished" + ) waiter.wait(DomainName=domain_name) - ret['result'] = True + ret["result"] = True except (ParamValidationError, ClientError, WaiterError) as exp: - ret.update({'error': __utils__['boto3.get_error'](exp)['message']}) + ret.update({"error": __utils__["boto3.get_error"](exp)["message"]}) return ret -@depends('botocore', version='1.10.77') +@depends("botocore", version="1.10.77") def check_upgrade_eligibility( - domain_name, - elasticsearch_version, - region=None, keyid=None, key=None, profile=None): - ''' + domain_name, elasticsearch_version, region=None, keyid=None, key=None, profile=None +): + """ Helper function to determine in one call if an Elasticsearch domain can be upgraded to the specified Elasticsearch version. @@ -1281,20 +1376,21 @@ def check_upgrade_eligibility( .. code-block:: bash salt myminion boto3_elasticsearch.check_upgrade_eligibility mydomain '6.7' - ''' - ret = {'result': False} + """ + ret = {"result": False} # Check if the desired version is in the list of compatible versions res = get_compatible_elasticsearch_versions( - domain_name, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: + domain_name, region=region, keyid=keyid, key=key, profile=profile + ) + if "error" in res: return res - compatible_versions = res['response'][0]['TargetVersions'] + compatible_versions = res["response"][0]["TargetVersions"] if six.text_type(elasticsearch_version) not in compatible_versions: - ret['result'] = True - ret['response'] = False - ret['error'] = ('Desired version "{}" not in compatible versions: {}.' - ''.format(elasticsearch_version, compatible_versions)) + ret["result"] = True + ret["response"] = False + ret["error"] = 'Desired version "{}" not in compatible versions: {}.' "".format( + elasticsearch_version, compatible_versions + ) return ret # Check if the domain is eligible to upgrade to the desired version res = upgrade_elasticsearch_domain( @@ -1302,14 +1398,24 @@ def check_upgrade_eligibility( elasticsearch_version, perform_check_only=True, blocking=True, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: + region=region, + keyid=keyid, + key=key, + profile=profile, + ) + if "error" in res: return res - res = wait_for_upgrade(domain_name, region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: + res = wait_for_upgrade( + domain_name, region=region, keyid=keyid, key=key, profile=profile + ) + if "error" in res: return res - res = get_upgrade_status(domain_name, region=region, keyid=keyid, key=key, profile=profile) - ret['result'] = True - ret['response'] = (res['response']['UpgradeStep'] == 'PRE_UPGRADE_CHECK' and - res['response']['StepStatus'] == 'SUCCEEDED') + res = get_upgrade_status( + domain_name, region=region, keyid=keyid, key=key, profile=profile + ) + ret["result"] = True + ret["response"] = ( + res["response"]["UpgradeStep"] == "PRE_UPGRADE_CHECK" + and res["response"]["StepStatus"] == "SUCCEEDED" + ) return ret diff --git a/salt/modules/boto3_route53.py b/salt/modules/boto3_route53.py index f0a12667853..1efbe507727 100644 --- a/salt/modules/boto3_route53.py +++ b/salt/modules/boto3_route53.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Execution module for Amazon Route53 written against Boto 3 .. versionadded:: 2017.7.0 @@ -43,68 +43,73 @@ Execution module for Amazon Route53 written against Boto 3 None as well. :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602,W0106 +# pylint: disable=E0602,W0106 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import time import re +import time # Import Salt libs import salt.utils.compat import salt.utils.versions -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six +from salt.ext.six.moves import range + log = logging.getLogger(__name__) # pylint: disable=W1699 # Import third party libs try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto3 - #pylint: enable=unused-import + + # pylint: enable=unused-import from botocore.exceptions import ClientError - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO3 = True except ImportError: HAS_BOTO3 = False def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ return salt.utils.versions.check_boto_reqs() def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO3: - __utils__['boto3.assign_funcs'](__name__, 'route53') + __utils__["boto3.assign_funcs"](__name__, "route53") -def _collect_results(func, item, args, marker='Marker', nextmarker='NextMarker'): +def _collect_results(func, item, args, marker="Marker", nextmarker="NextMarker"): ret = [] - Marker = args.get(marker, '') + Marker = args.get(marker, "") tries = 10 while Marker is not None: try: r = func(**args) except ClientError as e: - if tries and e.response.get('Error', {}).get('Code') == 'Throttling': + if tries and e.response.get("Error", {}).get("Code") == "Throttling": # Rate limited - retry - log.debug('Throttled by AWS API.') + log.debug("Throttled by AWS API.") time.sleep(3) tries -= 1 continue - log.error('Could not collect results from %s(): %s', func, e) + log.error("Could not collect results from %s(): %s", func, e) return [] i = r.get(item, []) if item else r - i.pop('ResponseMetadata', None) if isinstance(i, dict) else None + i.pop("ResponseMetadata", None) if isinstance(i, dict) else None ret += i if isinstance(i, list) else [i] Marker = r.get(nextmarker) args.update({marker: Marker}) @@ -112,26 +117,33 @@ def _collect_results(func, item, args, marker='Marker', nextmarker='NextMarker') def _wait_for_sync(change, conn, tries=10, sleep=20): - for retry in range(1, tries+1): - log.info('Getting route53 status (attempt %s)', retry) - status = 'wait' + for retry in range(1, tries + 1): + log.info("Getting route53 status (attempt %s)", retry) + status = "wait" try: - status = conn.get_change(Id=change)['ChangeInfo']['Status'] + status = conn.get_change(Id=change)["ChangeInfo"]["Status"] except ClientError as e: - if e.response.get('Error', {}).get('Code') == 'Throttling': - log.debug('Throttled by AWS API.') + if e.response.get("Error", {}).get("Code") == "Throttling": + log.debug("Throttled by AWS API.") else: six.reraise(*sys.exc_info()) - if status == 'INSYNC': + if status == "INSYNC": return True time.sleep(sleep) - log.error('Timed out waiting for Route53 INSYNC status.') + log.error("Timed out waiting for Route53 INSYNC status.") return False -def find_hosted_zone(Id=None, Name=None, PrivateZone=None, - region=None, key=None, keyid=None, profile=None): - ''' +def find_hosted_zone( + Id=None, + Name=None, + PrivateZone=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Find a hosted zone with the given characteristics. Id @@ -164,28 +176,35 @@ def find_hosted_zone(Id=None, Name=None, PrivateZone=None, salt myminion boto3_route53.find_hosted_zone Name=salt.org. \ profile='{"region": "us-east-1", "keyid": "A12345678AB", "key": "xblahblahblah"}' - ''' + """ if not _exactly_one((Id, Name)): - raise SaltInvocationError('Exactly one of either Id or Name is required.') + raise SaltInvocationError("Exactly one of either Id or Name is required.") if PrivateZone is not None and not isinstance(PrivateZone, bool): - raise SaltInvocationError('If set, PrivateZone must be a bool (e.g. True / False).') + raise SaltInvocationError( + "If set, PrivateZone must be a bool (e.g. True / False)." + ) if Id: ret = get_hosted_zone(Id, region=region, key=key, keyid=keyid, profile=profile) else: - ret = get_hosted_zones_by_domain(Name, region=region, key=key, keyid=keyid, profile=profile) + ret = get_hosted_zones_by_domain( + Name, region=region, key=key, keyid=keyid, profile=profile + ) if PrivateZone is not None: - ret = [m for m in ret if m['HostedZone']['Config']['PrivateZone'] is PrivateZone] + ret = [ + m for m in ret if m["HostedZone"]["Config"]["PrivateZone"] is PrivateZone + ] if len(ret) > 1: log.error( - 'Request matched more than one Hosted Zone (%s). Refine your ' - 'criteria and try again.', [z['HostedZone']['Id'] for z in ret] + "Request matched more than one Hosted Zone (%s). Refine your " + "criteria and try again.", + [z["HostedZone"]["Id"] for z in ret], ) ret = [] return ret def get_hosted_zone(Id, region=None, key=None, keyid=None, profile=None): - ''' + """ Return detailed info about the given zone. Id @@ -209,14 +228,14 @@ def get_hosted_zone(Id, region=None, key=None, keyid=None, profile=None): salt myminion boto3_route53.get_hosted_zone Z1234567690 \ profile='{"region": "us-east-1", "keyid": "A12345678AB", "key": "xblahblahblah"}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - args = {'Id': Id} + args = {"Id": Id} return _collect_results(conn.get_hosted_zone, None, args) def get_hosted_zones_by_domain(Name, region=None, key=None, keyid=None, profile=None): - ''' + """ Find any zones with the given domain name and return detailed info about them. Note that this can return multiple Route53 zones, since a domain name can be used in both public and private zones. @@ -242,18 +261,25 @@ def get_hosted_zones_by_domain(Name, region=None, key=None, keyid=None, profile= salt myminion boto3_route53.get_hosted_zones_by_domain salt.org. \ profile='{"region": "us-east-1", "keyid": "A12345678AB", "key": "xblahblahblah"}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - zones = [z for z in _collect_results(conn.list_hosted_zones, 'HostedZones', {}) - if z['Name'] == aws_encode(Name)] + zones = [ + z + for z in _collect_results(conn.list_hosted_zones, "HostedZones", {}) + if z["Name"] == aws_encode(Name) + ] ret = [] for z in zones: - ret += get_hosted_zone(Id=z['Id'], region=region, key=key, keyid=keyid, profile=profile) + ret += get_hosted_zone( + Id=z["Id"], region=region, key=key, keyid=keyid, profile=profile + ) return ret -def list_hosted_zones(DelegationSetId=None, region=None, key=None, keyid=None, profile=None): - ''' +def list_hosted_zones( + DelegationSetId=None, region=None, key=None, keyid=None, profile=None +): + """ Return detailed info about all zones in the bound account. DelegationSetId @@ -278,16 +304,27 @@ def list_hosted_zones(DelegationSetId=None, region=None, key=None, keyid=None, p salt myminion boto3_route53.describe_hosted_zones \ profile='{"region": "us-east-1", "keyid": "A12345678AB", "key": "xblahblahblah"}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - args = {'DelegationSetId': DelegationSetId} if DelegationSetId else {} - return _collect_results(conn.list_hosted_zones, 'HostedZones', args) + args = {"DelegationSetId": DelegationSetId} if DelegationSetId else {} + return _collect_results(conn.list_hosted_zones, "HostedZones", args) -def create_hosted_zone(Name, VPCId=None, VPCName=None, VPCRegion=None, CallerReference=None, - Comment='', PrivateZone=False, DelegationSetId=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_hosted_zone( + Name, + VPCId=None, + VPCName=None, + VPCRegion=None, + CallerReference=None, + Comment="", + PrivateZone=False, + DelegationSetId=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a new Route53 Hosted Zone. Returns a Python data structure with information about the newly created Hosted Zone. @@ -343,78 +380,103 @@ def create_hosted_zone(Name, VPCId=None, VPCName=None, VPCRegion=None, CallerRef CLI Example:: salt myminion boto3_route53.create_hosted_zone example.org. - ''' - if not Name.endswith('.'): - raise SaltInvocationError('Domain must be fully-qualified, complete with trailing period.') + """ + if not Name.endswith("."): + raise SaltInvocationError( + "Domain must be fully-qualified, complete with trailing period." + ) Name = aws_encode(Name) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - deets = find_hosted_zone(Name=Name, PrivateZone=PrivateZone, - region=region, key=key, keyid=keyid, profile=profile) + deets = find_hosted_zone( + Name=Name, + PrivateZone=PrivateZone, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if deets: log.info( - 'Route 53 hosted zone %s already exists. You may want to pass ' - 'e.g. \'PrivateZone=True\' or similar...', Name + "Route 53 hosted zone %s already exists. You may want to pass " + "e.g. 'PrivateZone=True' or similar...", + Name, ) return None args = { - 'Name': Name, - 'CallerReference': CallerReference, - 'HostedZoneConfig': { - 'Comment': Comment, - 'PrivateZone': PrivateZone - } - } - args.update({'DelegationSetId': DelegationSetId}) if DelegationSetId else None + "Name": Name, + "CallerReference": CallerReference, + "HostedZoneConfig": {"Comment": Comment, "PrivateZone": PrivateZone}, + } + args.update({"DelegationSetId": DelegationSetId}) if DelegationSetId else None if PrivateZone: if not _exactly_one((VPCName, VPCId)): - raise SaltInvocationError('Either VPCName or VPCId is required when creating a ' - 'private zone.') - vpcs = __salt__['boto_vpc.describe_vpcs']( - vpc_id=VPCId, name=VPCName, region=region, key=key, - keyid=keyid, profile=profile).get('vpcs', []) + raise SaltInvocationError( + "Either VPCName or VPCId is required when creating a " "private zone." + ) + vpcs = __salt__["boto_vpc.describe_vpcs"]( + vpc_id=VPCId, + name=VPCName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("vpcs", []) if VPCRegion and vpcs: - vpcs = [v for v in vpcs if v['region'] == VPCRegion] + vpcs = [v for v in vpcs if v["region"] == VPCRegion] if not vpcs: - log.error('Private zone requested but no VPC matching given criteria found.') + log.error( + "Private zone requested but no VPC matching given criteria found." + ) return None if len(vpcs) > 1: log.error( - 'Private zone requested but multiple VPCs matching given ' - 'criteria found: %s.', [v['id'] for v in vpcs] + "Private zone requested but multiple VPCs matching given " + "criteria found: %s.", + [v["id"] for v in vpcs], ) return None vpc = vpcs[0] if VPCName: - VPCId = vpc['id'] + VPCId = vpc["id"] if not VPCRegion: - VPCRegion = vpc['region'] - args.update({'VPC': {'VPCId': VPCId, 'VPCRegion': VPCRegion}}) + VPCRegion = vpc["region"] + args.update({"VPC": {"VPCId": VPCId, "VPCRegion": VPCRegion}}) else: if any((VPCId, VPCName, VPCRegion)): - log.info('Options VPCId, VPCName, and VPCRegion are ignored when creating ' - 'non-private zones.') + log.info( + "Options VPCId, VPCName, and VPCRegion are ignored when creating " + "non-private zones." + ) tries = 10 while tries: try: r = conn.create_hosted_zone(**args) - r.pop('ResponseMetadata', None) - if _wait_for_sync(r['ChangeInfo']['Id'], conn): + r.pop("ResponseMetadata", None) + if _wait_for_sync(r["ChangeInfo"]["Id"], conn): return [r] return [] except ClientError as e: - if tries and e.response.get('Error', {}).get('Code') == 'Throttling': - log.debug('Throttled by AWS API.') + if tries and e.response.get("Error", {}).get("Code") == "Throttling": + log.debug("Throttled by AWS API.") time.sleep(3) tries -= 1 continue - log.error('Failed to create hosted zone %s: %s', Name, e) + log.error("Failed to create hosted zone %s: %s", Name, e) return [] return [] -def update_hosted_zone_comment(Id=None, Name=None, Comment=None, PrivateZone=None, - region=None, key=None, keyid=None, profile=None): - ''' +def update_hosted_zone_comment( + Id=None, + Name=None, + Comment=None, + PrivateZone=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Update the comment on an existing Route 53 hosted zone. Id @@ -433,39 +495,53 @@ def update_hosted_zone_comment(Id=None, Name=None, Comment=None, PrivateZone=Non salt myminion boto3_route53.update_hosted_zone_comment Name=example.org. \ Comment="This is an example comment for an example zone" - ''' + """ if not _exactly_one((Id, Name)): - raise SaltInvocationError('Exactly one of either Id or Name is required.') + raise SaltInvocationError("Exactly one of either Id or Name is required.") conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if Name: - args = {'Name': Name, 'PrivateZone': PrivateZone, 'region': region, - 'key': key, 'keyid': keyid, 'profile': profile} + args = { + "Name": Name, + "PrivateZone": PrivateZone, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } zone = find_hosted_zone(**args) if not zone: log.error("Couldn't resolve domain name %s to a hosted zone ID.", Name) return [] - Id = zone[0]['HostedZone']['Id'] + Id = zone[0]["HostedZone"]["Id"] tries = 10 while tries: try: r = conn.update_hosted_zone_comment(Id=Id, Comment=Comment) - r.pop('ResponseMetadata', None) + r.pop("ResponseMetadata", None) return [r] except ClientError as e: - if tries and e.response.get('Error', {}).get('Code') == 'Throttling': - log.debug('Throttled by AWS API.') + if tries and e.response.get("Error", {}).get("Code") == "Throttling": + log.debug("Throttled by AWS API.") time.sleep(3) tries -= 1 continue - log.error('Failed to update comment on hosted zone %s: %s', - Name or Id, e) + log.error("Failed to update comment on hosted zone %s: %s", Name or Id, e) return [] -def associate_vpc_with_hosted_zone(HostedZoneId=None, Name=None, VPCId=None, - VPCName=None, VPCRegion=None, Comment=None, - region=None, key=None, keyid=None, profile=None): - ''' +def associate_vpc_with_hosted_zone( + HostedZoneId=None, + Name=None, + VPCId=None, + VPCName=None, + VPCRegion=None, + Comment=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Associates an Amazon VPC with a private hosted zone. To perform the association, the VPC and the private hosted zone must already exist. You can't @@ -510,67 +586,93 @@ def associate_vpc_with_hosted_zone(HostedZoneId=None, Name=None, VPCId=None, Name=example.org. VPCName=myVPC \ VPCRegion=us-east-1 Comment="Whoo-hoo! I added another VPC." - ''' + """ if not _exactly_one((HostedZoneId, Name)): - raise SaltInvocationError('Exactly one of either HostedZoneId or Name is required.') + raise SaltInvocationError( + "Exactly one of either HostedZoneId or Name is required." + ) if not _exactly_one((VPCId, VPCName)): - raise SaltInvocationError('Exactly one of either VPCId or VPCName is required.') + raise SaltInvocationError("Exactly one of either VPCId or VPCName is required.") if Name: # {'PrivateZone': True} because you can only associate VPCs with private hosted zones. - args = {'Name': Name, 'PrivateZone': True, 'region': region, - 'key': key, 'keyid': keyid, 'profile': profile} + args = { + "Name": Name, + "PrivateZone": True, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } zone = find_hosted_zone(**args) if not zone: log.error( - "Couldn't resolve domain name %s to a private hosted zone ID.", - Name + "Couldn't resolve domain name %s to a private hosted zone ID.", Name ) return False - HostedZoneId = zone[0]['HostedZone']['Id'] - vpcs = __salt__['boto_vpc.describe_vpcs'](vpc_id=VPCId, name=VPCName, region=region, key=key, - keyid=keyid, profile=profile).get('vpcs', []) + HostedZoneId = zone[0]["HostedZone"]["Id"] + vpcs = __salt__["boto_vpc.describe_vpcs"]( + vpc_id=VPCId, name=VPCName, region=region, key=key, keyid=keyid, profile=profile + ).get("vpcs", []) if VPCRegion and vpcs: - vpcs = [v for v in vpcs if v['region'] == VPCRegion] + vpcs = [v for v in vpcs if v["region"] == VPCRegion] if not vpcs: - log.error('No VPC matching the given criteria found.') + log.error("No VPC matching the given criteria found.") return False if len(vpcs) > 1: - log.error('Multiple VPCs matching the given criteria found: %s.', - ', '.join([v['id'] for v in vpcs])) + log.error( + "Multiple VPCs matching the given criteria found: %s.", + ", ".join([v["id"] for v in vpcs]), + ) return False vpc = vpcs[0] if VPCName: - VPCId = vpc['id'] + VPCId = vpc["id"] if not VPCRegion: - VPCRegion = vpc['region'] - args = {'HostedZoneId': HostedZoneId, 'VPC': {'VPCId': VPCId, 'VPCRegion': VPCRegion}} - args.update({'Comment': Comment}) if Comment is not None else None + VPCRegion = vpc["region"] + args = { + "HostedZoneId": HostedZoneId, + "VPC": {"VPCId": VPCId, "VPCRegion": VPCRegion}, + } + args.update({"Comment": Comment}) if Comment is not None else None conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) tries = 10 while tries: try: r = conn.associate_vpc_with_hosted_zone(**args) - return _wait_for_sync(r['ChangeInfo']['Id'], conn) + return _wait_for_sync(r["ChangeInfo"]["Id"], conn) except ClientError as e: - if e.response.get('Error', {}).get('Code') == 'ConflictingDomainExists': - log.debug('VPC Association already exists.') + if e.response.get("Error", {}).get("Code") == "ConflictingDomainExists": + log.debug("VPC Association already exists.") # return True since the current state is the desired one return True - if tries and e.response.get('Error', {}).get('Code') == 'Throttling': - log.debug('Throttled by AWS API.') + if tries and e.response.get("Error", {}).get("Code") == "Throttling": + log.debug("Throttled by AWS API.") time.sleep(3) tries -= 1 continue - log.error('Failed to associate VPC %s with hosted zone %s: %s', - VPCName or VPCId, Name or HostedZoneId, e) + log.error( + "Failed to associate VPC %s with hosted zone %s: %s", + VPCName or VPCId, + Name or HostedZoneId, + e, + ) return False -def disassociate_vpc_from_hosted_zone(HostedZoneId=None, Name=None, VPCId=None, - VPCName=None, VPCRegion=None, Comment=None, - region=None, key=None, keyid=None, profile=None): - ''' +def disassociate_vpc_from_hosted_zone( + HostedZoneId=None, + Name=None, + VPCId=None, + VPCName=None, + VPCRegion=None, + Comment=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Disassociates an Amazon VPC from a private hosted zone. You can't disassociate the last VPC from a private hosted zone. You also can't convert a @@ -608,75 +710,95 @@ def disassociate_vpc_from_hosted_zone(HostedZoneId=None, Name=None, VPCId=None, Name=example.org. VPCName=myVPC \ VPCRegion=us-east-1 Comment="Whoops! Don't wanna talk to this-here zone no more." - ''' + """ if not _exactly_one((HostedZoneId, Name)): - raise SaltInvocationError('Exactly one of either HostedZoneId or Name is required.') + raise SaltInvocationError( + "Exactly one of either HostedZoneId or Name is required." + ) if not _exactly_one((VPCId, VPCName)): - raise SaltInvocationError('Exactly one of either VPCId or VPCName is required.') + raise SaltInvocationError("Exactly one of either VPCId or VPCName is required.") if Name: # {'PrivateZone': True} because you can only associate VPCs with private hosted zones. - args = {'Name': Name, 'PrivateZone': True, 'region': region, - 'key': key, 'keyid': keyid, 'profile': profile} + args = { + "Name": Name, + "PrivateZone": True, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } zone = find_hosted_zone(**args) if not zone: - log.error("Couldn't resolve domain name %s to a private hosted zone ID.", Name) + log.error( + "Couldn't resolve domain name %s to a private hosted zone ID.", Name + ) return False - HostedZoneId = zone[0]['HostedZone']['Id'] - vpcs = __salt__['boto_vpc.describe_vpcs'](vpc_id=VPCId, name=VPCName, region=region, key=key, - keyid=keyid, profile=profile).get('vpcs', []) + HostedZoneId = zone[0]["HostedZone"]["Id"] + vpcs = __salt__["boto_vpc.describe_vpcs"]( + vpc_id=VPCId, name=VPCName, region=region, key=key, keyid=keyid, profile=profile + ).get("vpcs", []) if VPCRegion and vpcs: - vpcs = [v for v in vpcs if v['region'] == VPCRegion] + vpcs = [v for v in vpcs if v["region"] == VPCRegion] if not vpcs: - log.error('No VPC matching the given criteria found.') + log.error("No VPC matching the given criteria found.") return False if len(vpcs) > 1: - log.error('Multiple VPCs matching the given criteria found: %s.', - ', '.join([v['id'] for v in vpcs])) + log.error( + "Multiple VPCs matching the given criteria found: %s.", + ", ".join([v["id"] for v in vpcs]), + ) return False vpc = vpcs[0] if VPCName: - VPCId = vpc['id'] + VPCId = vpc["id"] if not VPCRegion: - VPCRegion = vpc['region'] - args = ({'HostedZoneId': HostedZoneId, 'VPC': {'VPCId': VPCId, 'VPCRegion': VPCRegion}}) - args.update({'Comment': Comment}) if Comment is not None else None + VPCRegion = vpc["region"] + args = { + "HostedZoneId": HostedZoneId, + "VPC": {"VPCId": VPCId, "VPCRegion": VPCRegion}, + } + args.update({"Comment": Comment}) if Comment is not None else None conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) tries = 10 while tries: try: r = conn.disassociate_vpc_from_hosted_zone(**args) - return _wait_for_sync(r['ChangeInfo']['Id'], conn) + return _wait_for_sync(r["ChangeInfo"]["Id"], conn) except ClientError as e: - if e.response.get('Error', {}).get('Code') == 'VPCAssociationNotFound': - log.debug('No VPC Association exists.') + if e.response.get("Error", {}).get("Code") == "VPCAssociationNotFound": + log.debug("No VPC Association exists.") # return True since the current state is the desired one return True - if tries and e.response.get('Error', {}).get('Code') == 'Throttling': - log.debug('Throttled by AWS API.') + if tries and e.response.get("Error", {}).get("Code") == "Throttling": + log.debug("Throttled by AWS API.") time.sleep(3) tries -= 1 continue - log.error('Failed to associate VPC %s with hosted zone %s: %s', - VPCName or VPCId, Name or HostedZoneId, e) + log.error( + "Failed to associate VPC %s with hosted zone %s: %s", + VPCName or VPCId, + Name or HostedZoneId, + e, + ) return False -#def create_vpc_association_authorization(*args, **kwargs): +# def create_vpc_association_authorization(*args, **kwargs): # ''' # unimplemented # ''' # pass -#def delete_vpc_association_authorization(*args, **kwargs): +# def delete_vpc_association_authorization(*args, **kwargs): # ''' # unimplemented # ''' # pass -#def list_vpc_association_authorizations(*args, **kwargs): +# def list_vpc_association_authorizations(*args, **kwargs): # ''' # unimplemented # ''' @@ -684,45 +806,54 @@ def disassociate_vpc_from_hosted_zone(HostedZoneId=None, Name=None, VPCId=None, def delete_hosted_zone(Id, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete a Route53 hosted zone. CLI Example:: salt myminion boto3_route53.delete_hosted_zone Z1234567890 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: r = conn.delete_hosted_zone(Id=Id) - return _wait_for_sync(r['ChangeInfo']['Id'], conn) + return _wait_for_sync(r["ChangeInfo"]["Id"], conn) except ClientError as e: - log.error('Failed to delete hosted zone %s: %s', Id, e) + log.error("Failed to delete hosted zone %s: %s", Id, e) return False -def delete_hosted_zone_by_domain(Name, PrivateZone=None, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_hosted_zone_by_domain( + Name, PrivateZone=None, region=None, key=None, keyid=None, profile=None +): + """ Delete a Route53 hosted zone by domain name, and PrivateZone status if provided. CLI Example:: salt myminion boto3_route53.delete_hosted_zone_by_domain example.org. - ''' - args = {'Name': Name, 'PrivateZone': PrivateZone, - 'region': region, 'key': key, 'keyid': keyid, 'profile': profile} + """ + args = { + "Name": Name, + "PrivateZone": PrivateZone, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } # Be extra pedantic in the service of safety - if public/private is not provided and the domain # name resolves to both, fail and require them to declare it explicitly. zone = find_hosted_zone(**args) if not zone: log.error("Couldn't resolve domain name %s to a hosted zone ID.", Name) return False - Id = zone[0]['HostedZone']['Id'] - return delete_hosted_zone(Id=Id, region=region, key=key, keyid=keyid, profile=profile) + Id = zone[0]["HostedZone"]["Id"] + return delete_hosted_zone( + Id=Id, region=region, key=key, keyid=keyid, profile=profile + ) def aws_encode(x): - ''' + """ An implementation of the encoding required to suport AWS's domain name rules defined here__: @@ -739,41 +870,56 @@ def aws_encode(x): .. __: https://pypi.org/project/idna - ''' + """ ret = None try: - x.encode('ascii') - ret = re.sub(r'\\x([a-f0-8]{2})', - _hexReplace, x.encode('unicode_escape')) + x.encode("ascii") + ret = re.sub(r"\\x([a-f0-8]{2})", _hexReplace, x.encode("unicode_escape")) except UnicodeEncodeError: - ret = x.encode('idna') + ret = x.encode("idna") except Exception as e: # pylint: disable=broad-except - log.error("Couldn't encode %s using either 'unicode_escape' or 'idna' codecs", x) + log.error( + "Couldn't encode %s using either 'unicode_escape' or 'idna' codecs", x + ) raise CommandExecutionError(e) - log.debug('AWS-encoded result for %s: %s', x, ret) + log.debug("AWS-encoded result for %s: %s", x, ret) return ret def _aws_encode_changebatch(o): - ''' + """ helper method to process a change batch & encode the bits which need encoding. - ''' + """ change_idx = 0 - while change_idx < len(o['Changes']): - o['Changes'][change_idx]['ResourceRecordSet']['Name'] = aws_encode(o['Changes'][change_idx]['ResourceRecordSet']['Name']) - if 'ResourceRecords' in o['Changes'][change_idx]['ResourceRecordSet']: + while change_idx < len(o["Changes"]): + o["Changes"][change_idx]["ResourceRecordSet"]["Name"] = aws_encode( + o["Changes"][change_idx]["ResourceRecordSet"]["Name"] + ) + if "ResourceRecords" in o["Changes"][change_idx]["ResourceRecordSet"]: rr_idx = 0 - while rr_idx < len(o['Changes'][change_idx]['ResourceRecordSet']['ResourceRecords']): - o['Changes'][change_idx]['ResourceRecordSet']['ResourceRecords'][rr_idx]['Value'] = aws_encode(o['Changes'][change_idx]['ResourceRecordSet']['ResourceRecords'][rr_idx]['Value']) + while rr_idx < len( + o["Changes"][change_idx]["ResourceRecordSet"]["ResourceRecords"] + ): + o["Changes"][change_idx]["ResourceRecordSet"]["ResourceRecords"][ + rr_idx + ]["Value"] = aws_encode( + o["Changes"][change_idx]["ResourceRecordSet"]["ResourceRecords"][ + rr_idx + ]["Value"] + ) rr_idx += 1 - if 'AliasTarget' in o['Changes'][change_idx]['ResourceRecordSet']: - o['Changes'][change_idx]['ResourceRecordSet']['AliasTarget']['DNSName'] = aws_encode(o['Changes'][change_idx]['ResourceRecordSet']['AliasTarget']['DNSName']) + if "AliasTarget" in o["Changes"][change_idx]["ResourceRecordSet"]: + o["Changes"][change_idx]["ResourceRecordSet"]["AliasTarget"][ + "DNSName" + ] = aws_encode( + o["Changes"][change_idx]["ResourceRecordSet"]["AliasTarget"]["DNSName"] + ) change_idx += 1 return o def _aws_decode(x): - ''' + """ An implementation of the decoding required to suport AWS's domain name rules defined here__: @@ -792,29 +938,37 @@ def _aws_decode(x): We look for the existance of any escape codes which give us a clue that we're received an escaped unicode string; or we assume it's idna encoded and then decode as necessary. - ''' - if '\\' in x: - return x.decode('unicode_escape') - return x.decode('idna') + """ + if "\\" in x: + return x.decode("unicode_escape") + return x.decode("idna") def _hexReplace(x): - ''' + """ Converts a hex code to a base 16 int then the octal of it, minus the leading zero. This is necessary because x.encode('unicode_escape') automatically assumes you want a hex string, which AWS will accept but doesn't result in what you really want unless it's an octal escape sequence - ''' + """ c = int(x.group(1), 16) - return '\\' + str(oct(c))[1:] + return "\\" + str(oct(c))[1:] -def get_resource_records(HostedZoneId=None, Name=None, StartRecordName=None, - StartRecordType=None, PrivateZone=None, - region=None, key=None, keyid=None, profile=None): - ''' +def get_resource_records( + HostedZoneId=None, + Name=None, + StartRecordName=None, + StartRecordType=None, + PrivateZone=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get all resource records from a given zone matching the provided StartRecordName (if given) or all records in the zone (if not), optionally filtered by a specific StartRecordType. This will return any and all RRs matching, regardless of their special AWS flavors (weighted, geolocation, alias, @@ -832,19 +986,25 @@ def get_resource_records(HostedZoneId=None, Name=None, StartRecordName=None, CLI example:: salt myminion boto3_route53.get_records test.example.org example.org A - ''' + """ if not _exactly_one((HostedZoneId, Name)): - raise SaltInvocationError('Exactly one of either HostedZoneId or Name must ' - 'be provided.') + raise SaltInvocationError( + "Exactly one of either HostedZoneId or Name must " "be provided." + ) if Name: - args = {'Name': Name, 'region': region, 'key': key, 'keyid': keyid, - 'profile': profile} - args.update({'PrivateZone': PrivateZone}) if PrivateZone is not None else None + args = { + "Name": Name, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } + args.update({"PrivateZone": PrivateZone}) if PrivateZone is not None else None zone = find_hosted_zone(**args) if not zone: log.error("Couldn't resolve domain name %s to a hosted zone ID.", Name) return [] - HostedZoneId = zone[0]['HostedZone']['Id'] + HostedZoneId = zone[0]["HostedZone"]["Id"] conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) ret = [] @@ -855,34 +1015,42 @@ def get_resource_records(HostedZoneId=None, Name=None, StartRecordName=None, while True: if done: return ret - args = {'HostedZoneId': HostedZoneId} - args.update({'StartRecordName': aws_encode(next_rr_name)}) if next_rr_name else None + args = {"HostedZoneId": HostedZoneId} + args.update( + {"StartRecordName": aws_encode(next_rr_name)} + ) if next_rr_name else None # Grrr, can't specify type unless name is set... We'll do this via filtering later instead - args.update({'StartRecordType': next_rr_type}) if next_rr_name and next_rr_type else None - args.update({'StartRecordIdentifier': next_rr_id}) if next_rr_id else None + args.update( + {"StartRecordType": next_rr_type} + ) if next_rr_name and next_rr_type else None + args.update({"StartRecordIdentifier": next_rr_id}) if next_rr_id else None try: r = conn.list_resource_record_sets(**args) - rrs = r['ResourceRecordSets'] - next_rr_name = r.get('NextRecordName') - next_rr_type = r.get('NextRecordType') - next_rr_id = r.get('NextRecordIdentifier') + rrs = r["ResourceRecordSets"] + next_rr_name = r.get("NextRecordName") + next_rr_type = r.get("NextRecordType") + next_rr_id = r.get("NextRecordIdentifier") for rr in rrs: - rr['Name'] = _aws_decode(rr['Name']) + rr["Name"] = _aws_decode(rr["Name"]) # now iterate over the ResourceRecords and replace any encoded # value strings with the decoded versions - if 'ResourceRecords' in rr: + if "ResourceRecords" in rr: x = 0 - while x < len(rr['ResourceRecords']): - if 'Value' in rr['ResourceRecords'][x]: - rr['ResourceRecords'][x]['Value'] = _aws_decode(rr['ResourceRecords'][x]['Value']) + while x < len(rr["ResourceRecords"]): + if "Value" in rr["ResourceRecords"][x]: + rr["ResourceRecords"][x]["Value"] = _aws_decode( + rr["ResourceRecords"][x]["Value"] + ) x += 1 # or if we are an AliasTarget then decode the DNSName - if 'AliasTarget' in rr: - rr['AliasTarget']['DNSName'] = _aws_decode(rr['AliasTarget']['DNSName']) - if StartRecordName and rr['Name'] != StartRecordName: + if "AliasTarget" in rr: + rr["AliasTarget"]["DNSName"] = _aws_decode( + rr["AliasTarget"]["DNSName"] + ) + if StartRecordName and rr["Name"] != StartRecordName: done = True break - if StartRecordType and rr['Type'] != StartRecordType: + if StartRecordType and rr["Type"] != StartRecordType: if StartRecordName: done = True break @@ -894,17 +1062,24 @@ def get_resource_records(HostedZoneId=None, Name=None, StartRecordName=None, done = True except ClientError as e: # Try forever on a simple thing like this... - if e.response.get('Error', {}).get('Code') == 'Throttling': - log.debug('Throttled by AWS API.') + if e.response.get("Error", {}).get("Code") == "Throttling": + log.debug("Throttled by AWS API.") time.sleep(3) continue six.reraise(*sys.exc_info()) -def change_resource_record_sets(HostedZoneId=None, Name=None, - PrivateZone=None, ChangeBatch=None, - region=None, key=None, keyid=None, profile=None): - ''' +def change_resource_record_sets( + HostedZoneId=None, + Name=None, + PrivateZone=None, + ChangeBatch=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ See the `AWS Route53 API docs`__ as well as the `Boto3 documentation`__ for all the details... .. __: https://docs.aws.amazon.com/Route53/latest/APIReference/API_ChangeResourceRecordSets.html @@ -969,34 +1144,49 @@ def change_resource_record_sets(HostedZoneId=None, Name=None, salt myminion boto3_route53.change_resource_record_sets DomainName=example.org. \ keyid=A1234567890ABCDEF123 key=xblahblahblah \ ChangeBatch="{'Changes': [{'Action': 'UPSERT', 'ResourceRecordSet': $foo}]}" - ''' + """ if not _exactly_one((HostedZoneId, Name)): - raise SaltInvocationError('Exactly one of either HostZoneId or Name must be provided.') + raise SaltInvocationError( + "Exactly one of either HostZoneId or Name must be provided." + ) if Name: - args = {'Name': Name, 'region': region, 'key': key, 'keyid': keyid, - 'profile': profile} - args.update({'PrivateZone': PrivateZone}) if PrivateZone is not None else None + args = { + "Name": Name, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } + args.update({"PrivateZone": PrivateZone}) if PrivateZone is not None else None zone = find_hosted_zone(**args) if not zone: log.error("Couldn't resolve domain name %s to a hosted zone ID.", Name) return [] - HostedZoneId = zone[0]['HostedZone']['Id'] + HostedZoneId = zone[0]["HostedZone"]["Id"] - args = {'HostedZoneId': HostedZoneId, 'ChangeBatch': _aws_encode_changebatch(ChangeBatch)} + args = { + "HostedZoneId": HostedZoneId, + "ChangeBatch": _aws_encode_changebatch(ChangeBatch), + } conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) tries = 20 # A bit more headroom while tries: try: r = conn.change_resource_record_sets(**args) - return _wait_for_sync(r['ChangeInfo']['Id'], conn, 30) # And a little extra time here + return _wait_for_sync( + r["ChangeInfo"]["Id"], conn, 30 + ) # And a little extra time here except ClientError as e: - if tries and e.response.get('Error', {}).get('Code') == 'Throttling': - log.debug('Throttled by AWS API.') + if tries and e.response.get("Error", {}).get("Code") == "Throttling": + log.debug("Throttled by AWS API.") time.sleep(3) tries -= 1 continue - log.error('Failed to apply requested changes to the hosted zone %s: %s', - (Name or HostedZoneId), six.text_type(e)) + log.error( + "Failed to apply requested changes to the hosted zone %s: %s", + (Name or HostedZoneId), + six.text_type(e), + ) raise e return False diff --git a/salt/modules/boto3_sns.py b/salt/modules/boto3_sns.py index 14f9fdd3670..6866219807b 100644 --- a/salt/modules/boto3_sns.py +++ b/salt/modules/boto3_sns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon SNS :configuration: This module accepts explicit sns credentials but can also @@ -38,12 +38,13 @@ Connection module for Amazon SNS region: us-east-1 :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -52,273 +53,327 @@ import salt.utils.versions log = logging.getLogger(__name__) # Import third party libs -#pylint: disable=unused-import +# pylint: disable=unused-import try: import botocore import boto3 import jmespath - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False -#pylint: enable=unused-import +# pylint: enable=unused-import def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' + """ has_boto_reqs = salt.utils.versions.check_boto_reqs() if has_boto_reqs is True: - __utils__['boto3.assign_funcs'](__name__, 'sns') + __utils__["boto3.assign_funcs"](__name__, "sns") return has_boto_reqs def list_topics(region=None, key=None, keyid=None, profile=None): - ''' + """ Returns a list of the requester's topics CLI example:: salt myminion boto3_sns.list_topics - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) res = {} - NextToken = '' + NextToken = "" while NextToken is not None: ret = conn.list_topics(NextToken=NextToken) - NextToken = ret.get('NextToken', None) - arns = jmespath.search('Topics[*].TopicArn', ret) + NextToken = ret.get("NextToken", None) + arns = jmespath.search("Topics[*].TopicArn", ret) for t in arns: - short_name = t.split(':')[-1] + short_name = t.split(":")[-1] res[short_name] = t return res def describe_topic(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Returns details about a specific SNS topic, specified by name or ARN. CLI example:: salt my_favorite_client boto3_sns.describe_topic a_sns_topic_of_my_choice - ''' + """ topics = list_topics(region=region, key=key, keyid=keyid, profile=profile) ret = {} for topic, arn in topics.items(): if name in (topic, arn): - ret = {'TopicArn': arn} - ret['Subscriptions'] = list_subscriptions_by_topic(arn, region=region, key=key, - keyid=keyid, profile=profile) - ret['Attributes'] = get_topic_attributes(arn, region=region, key=key, keyid=keyid, - profile=profile) + ret = {"TopicArn": arn} + ret["Subscriptions"] = list_subscriptions_by_topic( + arn, region=region, key=key, keyid=keyid, profile=profile + ) + ret["Attributes"] = get_topic_attributes( + arn, region=region, key=key, keyid=keyid, profile=profile + ) return ret def topic_exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if an SNS topic exists. CLI example:: salt myminion boto3_sns.topic_exists mytopic region=us-east-1 - ''' + """ topics = list_topics(region=region, key=key, keyid=keyid, profile=profile) return name in list(topics.values() + topics.keys()) def create_topic(Name, region=None, key=None, keyid=None, profile=None): - ''' + """ Create an SNS topic. CLI example:: salt myminion boto3_sns.create_topic mytopic region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: ret = conn.create_topic(Name=Name) - log.info('SNS topic %s created with ARN %s', Name, ret['TopicArn']) - return ret['TopicArn'] + log.info("SNS topic %s created with ARN %s", Name, ret["TopicArn"]) + return ret["TopicArn"] except botocore.exceptions.ClientError as e: - log.error('Failed to create SNS topic %s: %s', Name, e) + log.error("Failed to create SNS topic %s: %s", Name, e) return None except KeyError: - log.error('Failed to create SNS topic %s', Name) + log.error("Failed to create SNS topic %s", Name) return None def delete_topic(TopicArn, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete an SNS topic. CLI example:: salt myminion boto3_sns.delete_topic mytopic region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.delete_topic(TopicArn=TopicArn) - log.info('SNS topic %s deleted', TopicArn) + log.info("SNS topic %s deleted", TopicArn) return True except botocore.exceptions.ClientError as e: - log.error('Failed to delete SNS topic %s: %s', name, e) + log.error("Failed to delete SNS topic %s: %s", name, e) return False def get_topic_attributes(TopicArn, region=None, key=None, keyid=None, profile=None): - ''' + """ Returns all of the properties of a topic. Topic properties returned might differ based on the authorization of the user. CLI example:: salt myminion boto3_sns.get_topic_attributes someTopic region=us-west-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - return conn.get_topic_attributes(TopicArn=TopicArn).get('Attributes') + return conn.get_topic_attributes(TopicArn=TopicArn).get("Attributes") except botocore.exceptions.ClientError as e: - log.error('Failed to garner attributes for SNS topic %s: %s', TopicArn, e) + log.error("Failed to garner attributes for SNS topic %s: %s", TopicArn, e) return None -def set_topic_attributes(TopicArn, AttributeName, AttributeValue, region=None, key=None, keyid=None, - profile=None): - ''' +def set_topic_attributes( + TopicArn, + AttributeName, + AttributeValue, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Set an attribute of a topic to a new value. CLI example:: salt myminion boto3_sns.set_topic_attributes someTopic DisplayName myDisplayNameValue - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - conn.set_topic_attributes(TopicArn=TopicArn, AttributeName=AttributeName, - AttributeValue=AttributeValue) - log.debug('Set attribute %s=%s on SNS topic %s', - AttributeName, AttributeValue, TopicArn) + conn.set_topic_attributes( + TopicArn=TopicArn, + AttributeName=AttributeName, + AttributeValue=AttributeValue, + ) + log.debug( + "Set attribute %s=%s on SNS topic %s", + AttributeName, + AttributeValue, + TopicArn, + ) return True except botocore.exceptions.ClientError as e: - log.error('Failed to set attribute %s=%s for SNS topic %s: %s', - AttributeName, AttributeValue, TopicArn, e) + log.error( + "Failed to set attribute %s=%s for SNS topic %s: %s", + AttributeName, + AttributeValue, + TopicArn, + e, + ) return False -def list_subscriptions_by_topic(TopicArn, region=None, key=None, keyid=None, profile=None): - ''' +def list_subscriptions_by_topic( + TopicArn, region=None, key=None, keyid=None, profile=None +): + """ Returns a list of the subscriptions to a specific topic CLI example:: salt myminion boto3_sns.list_subscriptions_by_topic mytopic region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - NextToken = '' + NextToken = "" res = [] try: while NextToken is not None: - ret = conn.list_subscriptions_by_topic(TopicArn=TopicArn, NextToken=NextToken) - NextToken = ret.get('NextToken', None) - subs = ret.get('Subscriptions', []) + ret = conn.list_subscriptions_by_topic( + TopicArn=TopicArn, NextToken=NextToken + ) + NextToken = ret.get("NextToken", None) + subs = ret.get("Subscriptions", []) res += subs except botocore.exceptions.ClientError as e: - log.error('Failed to list subscriptions for SNS topic %s: %s', TopicArn, e) + log.error("Failed to list subscriptions for SNS topic %s: %s", TopicArn, e) return None return res def list_subscriptions(region=None, key=None, keyid=None, profile=None): - ''' + """ Returns a list of the requester's topics CLI example:: salt myminion boto3_sns.list_subscriptions region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - NextToken = '' + NextToken = "" res = [] try: while NextToken is not None: ret = conn.list_subscriptions(NextToken=NextToken) - NextToken = ret.get('NextToken', None) - subs = ret.get('Subscriptions', []) + NextToken = ret.get("NextToken", None) + subs = ret.get("Subscriptions", []) res += subs except botocore.exceptions.ClientError as e: - log.error('Failed to list SNS subscriptions: %s', e) + log.error("Failed to list SNS subscriptions: %s", e) return None return res -def get_subscription_attributes(SubscriptionArn, region=None, key=None, keyid=None, profile=None): - ''' +def get_subscription_attributes( + SubscriptionArn, region=None, key=None, keyid=None, profile=None +): + """ Returns all of the properties of a subscription. CLI example:: salt myminion boto3_sns.get_subscription_attributes somesubscription region=us-west-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: ret = conn.get_subscription_attributes(SubscriptionArn=SubscriptionArn) - return ret['Attributes'] + return ret["Attributes"] except botocore.exceptions.ClientError as e: - log.error('Failed to list attributes for SNS subscription %s: %s', - SubscriptionArn, e) + log.error( + "Failed to list attributes for SNS subscription %s: %s", SubscriptionArn, e + ) return None except KeyError: - log.error('Failed to list attributes for SNS subscription %s', - SubscriptionArn) + log.error("Failed to list attributes for SNS subscription %s", SubscriptionArn) return None -def set_subscription_attributes(SubscriptionArn, AttributeName, AttributeValue, region=None, - key=None, keyid=None, profile=None): - ''' +def set_subscription_attributes( + SubscriptionArn, + AttributeName, + AttributeValue, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Set an attribute of a subscription to a new value. CLI example:: salt myminion boto3_sns.set_subscription_attributes someSubscription RawMessageDelivery jsonStringValue - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - conn.set_subscription_attributes(SubscriptionArn=SubscriptionArn, - AttributeName=AttributeName, AttributeValue=AttributeValue) - log.debug('Set attribute %s=%s on SNS subscription %s', - AttributeName, AttributeValue, SubscriptionArn) + conn.set_subscription_attributes( + SubscriptionArn=SubscriptionArn, + AttributeName=AttributeName, + AttributeValue=AttributeValue, + ) + log.debug( + "Set attribute %s=%s on SNS subscription %s", + AttributeName, + AttributeValue, + SubscriptionArn, + ) return True except botocore.exceptions.ClientError as e: - log.error('Failed to set attribute %s=%s for SNS subscription %s: %s', - AttributeName, AttributeValue, SubscriptionArn, e) + log.error( + "Failed to set attribute %s=%s for SNS subscription %s: %s", + AttributeName, + AttributeValue, + SubscriptionArn, + e, + ) return False -def subscribe(TopicArn, Protocol, Endpoint, region=None, key=None, keyid=None, profile=None): - ''' +def subscribe( + TopicArn, Protocol, Endpoint, region=None, key=None, keyid=None, profile=None +): + """ Subscribe to a Topic. CLI example:: salt myminion boto3_sns.subscribe mytopic https https://www.example.com/sns-endpoint - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: ret = conn.subscribe(TopicArn=TopicArn, Protocol=Protocol, Endpoint=Endpoint) - log.info('Subscribed %s %s to topic %s with SubscriptionArn %s', - Protocol, Endpoint, TopicArn, ret['SubscriptionArn']) - return ret['SubscriptionArn'] + log.info( + "Subscribed %s %s to topic %s with SubscriptionArn %s", + Protocol, + Endpoint, + TopicArn, + ret["SubscriptionArn"], + ) + return ret["SubscriptionArn"] except botocore.exceptions.ClientError as e: - log.error('Failed to create subscription to SNS topic %s: %s', TopicArn, e) + log.error("Failed to create subscription to SNS topic %s: %s", TopicArn, e) return None except KeyError: - log.error('Failed to create subscription to SNS topic %s', TopicArn) + log.error("Failed to create subscription to SNS topic %s", TopicArn) return None def unsubscribe(SubscriptionArn, region=None, key=None, keyid=None, profile=None): - ''' + """ Unsubscribe a specific SubscriptionArn of a topic. CLI Example: @@ -326,19 +381,18 @@ def unsubscribe(SubscriptionArn, region=None, key=None, keyid=None, profile=None .. code-block:: bash salt myminion boto3_sns.unsubscribe my_subscription_arn region=us-east-1 - ''' + """ subs = list_subscriptions(region=region, key=key, keyid=keyid, profile=profile) - sub = [s for s in subs if s.get('SubscriptionArn') == SubscriptionArn] + sub = [s for s in subs if s.get("SubscriptionArn") == SubscriptionArn] if not sub: - log.error('Subscription ARN %s not found', SubscriptionArn) + log.error("Subscription ARN %s not found", SubscriptionArn) return False - TopicArn = sub[0]['TopicArn'] + TopicArn = sub[0]["TopicArn"] conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.unsubscribe(SubscriptionArn=SubscriptionArn) - log.info('Deleted subscription %s from SNS topic %s', - SubscriptionArn, TopicArn) + log.info("Deleted subscription %s from SNS topic %s", SubscriptionArn, TopicArn) return True except botocore.exceptions.ClientError as e: - log.error('Failed to delete subscription %s: %s', SubscriptionArn, e) + log.error("Failed to delete subscription %s: %s", SubscriptionArn, e) return False diff --git a/salt/modules/boto_apigateway.py b/salt/modules/boto_apigateway.py index eb855418e0e..661f09a8b38 100644 --- a/salt/modules/boto_apigateway.py +++ b/salt/modules/boto_apigateway.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon APIGateway .. versionadded:: 2016.11.0 @@ -74,21 +74,23 @@ Connection module for Amazon APIGateway error: message: error message -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -import datetime -# Import Salt libs -from salt.ext import six +import datetime +import logging + import salt.utils.compat import salt.utils.json import salt.utils.versions +# Import Salt libs +from salt.ext import six + log = logging.getLogger(__name__) # Import third party libs @@ -98,11 +100,13 @@ try: # pylint: disable=unused-import import boto import boto3 + # pylint: enable=unused-import from botocore.exceptions import ClientError from botocore import __version__ as found_botocore_version - logging.getLogger('boto').setLevel(logging.CRITICAL) - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -110,84 +114,90 @@ except ImportError: def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # the boto_apigateway execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 return salt.utils.versions.check_boto_reqs( - boto_ver='2.8.0', - boto3_ver='1.2.1', - botocore_ver='1.4.49' + boto_ver="2.8.0", boto3_ver="1.2.1", botocore_ver="1.4.49" ) def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 'apigateway') + __utils__["boto3.assign_funcs"](__name__, "apigateway") def _convert_datetime_str(response): - ''' + """ modify any key-value pair where value is a datetime object to a string. - ''' + """ if response: - return dict([(k, '{0}'.format(v)) if isinstance(v, datetime.date) else (k, v) for k, v in six.iteritems(response)]) + return dict( + [ + (k, "{0}".format(v)) if isinstance(v, datetime.date) else (k, v) + for k, v in six.iteritems(response) + ] + ) return None def _filter_apis(name, apis): - ''' + """ Return list of api items matching the given name. - ''' - return [api for api in apis if api['name'] == name] + """ + return [api for api in apis if api["name"] == name] def _filter_apis_desc(desc, apis): - ''' + """ Return list of api items matching the given description. - ''' - return [api for api in apis if api['description'] == desc] + """ + return [api for api in apis if api["description"] == desc] def _multi_call(function, contentkey, *args, **kwargs): - ''' + """ Retrieve full list of values for the contentkey from a boto3 ApiGateway client function that may be paged via 'position' - ''' + """ ret = function(*args, **kwargs) - position = ret.get('position') + position = ret.get("position") while position: more = function(*args, position=position, **kwargs) ret[contentkey].extend(more[contentkey]) - position = more.get('position') + position = more.get("position") return ret.get(contentkey) -def _find_apis_by_name(name, description=None, - region=None, key=None, keyid=None, profile=None): - ''' +def _find_apis_by_name( + name, description=None, region=None, key=None, keyid=None, profile=None +): + """ get and return list of matching rest api information by the given name and desc. If rest api name evaluates to False, return all apis w/o filtering the name. - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - apis = _multi_call(conn.get_rest_apis, 'items') + apis = _multi_call(conn.get_rest_apis, "items") if name: apis = _filter_apis(name, apis) if description is not None: apis = _filter_apis_desc(description, apis) - return {'restapi': [_convert_datetime_str(api) for api in apis]} + return {"restapi": [_convert_datetime_str(api) for api in apis]} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe_apis(name=None, description=None, region=None, key=None, keyid=None, profile=None): - ''' +def describe_apis( + name=None, description=None, region=None, key=None, keyid=None, profile=None +): + """ Returns all rest apis in the defined region. If optional parameter name is included, returns all rest apis matching the name in the defined region. @@ -201,18 +211,30 @@ def describe_apis(name=None, description=None, region=None, key=None, keyid=None salt myminion boto_apigateway.describe_apis name='api name' description='desc str' - ''' + """ if name: - return _find_apis_by_name(name, description=description, - region=region, key=key, keyid=keyid, profile=profile) + return _find_apis_by_name( + name, + description=description, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) else: - return _find_apis_by_name('', description=description, - region=region, key=key, keyid=keyid, profile=profile) + return _find_apis_by_name( + "", + description=description, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) def api_exists(name, description=None, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if the given Rest API Name and optionally description exists. CLI Example: @@ -221,15 +243,22 @@ def api_exists(name, description=None, region=None, key=None, keyid=None, profil salt myminion boto_apigateway.exists myapi_name - ''' - apis = _find_apis_by_name(name, description=description, - region=region, key=key, keyid=keyid, profile=profile) - return {'exists': bool(apis.get('restapi'))} + """ + apis = _find_apis_by_name( + name, + description=description, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + return {"exists": bool(apis.get("restapi"))} -def create_api(name, description, cloneFrom=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_api( + name, description, cloneFrom=None, region=None, key=None, keyid=None, profile=None +): + """ Create a new REST API Service with the given name Returns {created: True} if the rest api was created and returns @@ -241,21 +270,23 @@ def create_api(name, description, cloneFrom=None, salt myminion boto_apigateway.create_api myapi_name api_description - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if cloneFrom: - api = conn.create_rest_api(name=name, description=description, cloneFrom=cloneFrom) + api = conn.create_rest_api( + name=name, description=description, cloneFrom=cloneFrom + ) else: api = conn.create_rest_api(name=name, description=description) api = _convert_datetime_str(api) - return {'created': True, 'restapi': api} if api else {'created': False} + return {"created": True, "restapi": api} if api else {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} def delete_api(name, description=None, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete all REST API Service with the given name and an optional API description Returns {deleted: True, count: deleted_count} if apis were deleted, and @@ -269,24 +300,24 @@ def delete_api(name, description=None, region=None, key=None, keyid=None, profil salt myminion boto_apigateway.delete_api myapi_name description='api description' - ''' + """ try: conn_params = dict(region=region, key=key, keyid=keyid, profile=profile) r = _find_apis_by_name(name, description=description, **conn_params) - apis = r.get('restapi') + apis = r.get("restapi") if apis: conn = _get_conn(**conn_params) for api in apis: - conn.delete_rest_api(restApiId=api['id']) - return {'deleted': True, 'count': len(apis)} + conn.delete_rest_api(restApiId=api["id"]) + return {"deleted": True, "count": len(apis)} else: - return {'deleted': False} + return {"deleted": False} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} def describe_api_resources(restApiId, region=None, key=None, keyid=None, profile=None): - ''' + """ Given rest api id, return all resources for this api. CLI Example: @@ -295,20 +326,23 @@ def describe_api_resources(restApiId, region=None, key=None, keyid=None, profile salt myminion boto_apigateway.describe_api_resources myapi_id - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - resources = sorted(_multi_call(conn.get_resources, 'items', restApiId=restApiId), - key=lambda k: k['path']) + resources = sorted( + _multi_call(conn.get_resources, "items", restApiId=restApiId), + key=lambda k: k["path"], + ) - return {'resources': resources} + return {"resources": resources} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe_api_resource(restApiId, path, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_resource( + restApiId, path, region=None, key=None, keyid=None, profile=None +): + """ Given rest api id, and an absolute resource path, returns the resource id for the given path. @@ -318,20 +352,23 @@ def describe_api_resource(restApiId, path, salt myminion boto_apigateway.describe_api_resource myapi_id resource_path - ''' - r = describe_api_resources(restApiId, region=region, key=key, keyid=keyid, profile=profile) - resources = r.get('resources') + """ + r = describe_api_resources( + restApiId, region=region, key=key, keyid=keyid, profile=profile + ) + resources = r.get("resources") if resources is None: return r for resource in resources: - if resource['path'] == path: - return {'resource': resource} - return {'resource': None} + if resource["path"] == path: + return {"resource": resource} + return {"resource": None} -def create_api_resources(restApiId, path, - region=None, key=None, keyid=None, profile=None): - ''' +def create_api_resources( + restApiId, path, region=None, key=None, keyid=None, profile=None +): + """ Given rest api id, and an absolute resource path, create all the resources and return all resources in the resourcepath, returns False on failure. @@ -341,35 +378,44 @@ def create_api_resources(restApiId, path, salt myminion boto_apigateway.create_api_resources myapi_id resource_path - ''' - path_parts = path.split('/') + """ + path_parts = path.split("/") created = [] - current_path = '' + current_path = "" try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) for path_part in path_parts: - if current_path == '/': - current_path = '{0}{1}'.format(current_path, path_part) + if current_path == "/": + current_path = "{0}{1}".format(current_path, path_part) else: - current_path = '{0}/{1}'.format(current_path, path_part) - r = describe_api_resource(restApiId, current_path, - region=region, key=key, keyid=keyid, profile=profile) - resource = r.get('resource') + current_path = "{0}/{1}".format(current_path, path_part) + r = describe_api_resource( + restApiId, + current_path, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + resource = r.get("resource") if not resource: - resource = conn.create_resource(restApiId=restApiId, parentId=created[-1]['id'], pathPart=path_part) + resource = conn.create_resource( + restApiId=restApiId, parentId=created[-1]["id"], pathPart=path_part + ) created.append(resource) if created: - return {'created': True, 'restApiId': restApiId, 'resources': created} + return {"created": True, "restApiId": restApiId, "resources": created} else: - return {'created': False, 'error': 'unexpected error.'} + return {"created": False, "error": "unexpected error."} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete_api_resources(restApiId, path, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_api_resources( + restApiId, path, region=None, key=None, keyid=None, profile=None +): + """ Given restApiId and an absolute resource path, delete the resources starting from the absolute resource path. If resourcepath is the root resource '/', the function will return False. Returns False on failure. @@ -380,25 +426,28 @@ def delete_api_resources(restApiId, path, salt myminion boto_apigateway.delete_api_resources myapi_id, resource_path - ''' - if path == '/': - return {'deleted': False, 'error': 'use delete_api to remove the root resource'} + """ + if path == "/": + return {"deleted": False, "error": "use delete_api to remove the root resource"} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - r = describe_api_resource(restApiId, path, region=region, key=key, keyid=keyid, profile=profile) - resource = r.get('resource') + r = describe_api_resource( + restApiId, path, region=region, key=key, keyid=keyid, profile=profile + ) + resource = r.get("resource") if resource: - conn.delete_resource(restApiId=restApiId, resourceId=resource['id']) - return {'deleted': True} + conn.delete_resource(restApiId=restApiId, resourceId=resource["id"]) + return {"deleted": True} else: - return {'deleted': False, 'error': 'no resource found by {0}'.format(path)} + return {"deleted": False, "error": "no resource found by {0}".format(path)} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def describe_api_resource_method(restApiId, resourcePath, httpMethod, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_resource_method( + restApiId, resourcePath, httpMethod, region=None, key=None, keyid=None, profile=None +): + """ Given rest api id, resource path, and http method (must be one of DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT), return the method for the api/resource path if defined. Return False if method is not defined. @@ -409,23 +458,26 @@ def describe_api_resource_method(restApiId, resourcePath, httpMethod, salt myminion boto_apigateway.describe_api_resource_method myapi_id resource_path httpmethod - ''' - r = describe_api_resource(restApiId, resourcePath, - region=region, key=key, keyid=keyid, profile=profile) - resource = r.get('resource') + """ + r = describe_api_resource( + restApiId, resourcePath, region=region, key=key, keyid=keyid, profile=profile + ) + resource = r.get("resource") if not resource: - return {'error': 'no such resource'} + return {"error": "no such resource"} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - method = conn.get_method(restApiId=restApiId, resourceId=resource['id'], httpMethod=httpMethod) - return {'method': method} + method = conn.get_method( + restApiId=restApiId, resourceId=resource["id"], httpMethod=httpMethod + ) + return {"method": method} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def describe_api_key(apiKey, region=None, key=None, keyid=None, profile=None): - ''' + """ Gets info about the given api key CLI Example: @@ -434,17 +486,17 @@ def describe_api_key(apiKey, region=None, key=None, keyid=None, profile=None): salt myminion boto_apigateway.describe_api_key apigw_api_key - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) response = conn.get_api_key(apiKey=apiKey) - return {'apiKey': _convert_datetime_str(response)} + return {"apiKey": _convert_datetime_str(response)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def describe_api_keys(region=None, key=None, keyid=None, profile=None): - ''' + """ Gets information about the defined API Keys. Return list of apiKeys. CLI Example: @@ -453,19 +505,27 @@ def describe_api_keys(region=None, key=None, keyid=None, profile=None): salt myminion boto_apigateway.describe_api_keys - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - apikeys = _multi_call(conn.get_api_keys, 'items') + apikeys = _multi_call(conn.get_api_keys, "items") - return {'apiKeys': [_convert_datetime_str(apikey) for apikey in apikeys]} + return {"apiKeys": [_convert_datetime_str(apikey) for apikey in apikeys]} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create_api_key(name, description, enabled=True, stageKeys=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_api_key( + name, + description, + enabled=True, + stageKeys=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an API key given name and description. An optional enabled argument can be provided. If provided, the @@ -485,24 +545,25 @@ def create_api_key(name, description, enabled=True, stageKeys=None, salt myminion boto_apigateway.create_api_key name description \\ stageKeys='[{"restApiId": "id", "stageName": "stagename"}]' - ''' + """ try: stageKeys = list() if stageKeys is None else stageKeys conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = conn.create_api_key(name=name, description=description, - enabled=enabled, stageKeys=stageKeys) + response = conn.create_api_key( + name=name, description=description, enabled=enabled, stageKeys=stageKeys + ) if not response: - return {'created': False} + return {"created": False} - return {'created': True, 'apiKey': _convert_datetime_str(response)} + return {"created": True, "apiKey": _convert_datetime_str(response)} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} def delete_api_key(apiKey, region=None, key=None, keyid=None, profile=None): - ''' + """ Deletes a given apiKey CLI Example: @@ -511,51 +572,56 @@ def delete_api_key(apiKey, region=None, key=None, keyid=None, profile=None): salt myminion boto_apigateway.delete_api_key apikeystring - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_api_key(apiKey=apiKey) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} def _api_key_patch_replace(conn, apiKey, path, value): - ''' + """ the replace patch operation on an ApiKey resource - ''' - response = conn.update_api_key(apiKey=apiKey, - patchOperations=[{'op': 'replace', 'path': path, 'value': value}]) + """ + response = conn.update_api_key( + apiKey=apiKey, patchOperations=[{"op": "replace", "path": path, "value": value}] + ) return response def _api_key_patchops(op, pvlist): - ''' + """ helper function to return patchOperations object - ''' - return [{'op': op, 'path': p, 'value': v} for (p, v) in pvlist] + """ + return [{"op": op, "path": p, "value": v} for (p, v) in pvlist] def _api_key_patch_add(conn, apiKey, pvlist): - ''' + """ the add patch operation for a list of (path, value) tuples on an ApiKey resource list path - ''' - response = conn.update_api_key(apiKey=apiKey, - patchOperations=_api_key_patchops('add', pvlist)) + """ + response = conn.update_api_key( + apiKey=apiKey, patchOperations=_api_key_patchops("add", pvlist) + ) return response def _api_key_patch_remove(conn, apiKey, pvlist): - ''' + """ the remove patch operation for a list of (path, value) tuples on an ApiKey resource list path - ''' - response = conn.update_api_key(apiKey=apiKey, - patchOperations=_api_key_patchops('remove', pvlist)) + """ + response = conn.update_api_key( + apiKey=apiKey, patchOperations=_api_key_patchops("remove", pvlist) + ) return response -def update_api_key_description(apiKey, description, region=None, key=None, keyid=None, profile=None): - ''' +def update_api_key_description( + apiKey, description, region=None, key=None, keyid=None, profile=None +): + """ update the given apiKey with the given description. CLI Example: @@ -564,17 +630,17 @@ def update_api_key_description(apiKey, description, region=None, key=None, keyid salt myminion boto_apigateway.update_api_key_description api_key description - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = _api_key_patch_replace(conn, apiKey, '/description', description) - return {'updated': True, 'apiKey': _convert_datetime_str(response)} + response = _api_key_patch_replace(conn, apiKey, "/description", description) + return {"updated": True, "apiKey": _convert_datetime_str(response)} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} def enable_api_key(apiKey, region=None, key=None, keyid=None, profile=None): - ''' + """ enable the given apiKey. CLI Example: @@ -583,17 +649,17 @@ def enable_api_key(apiKey, region=None, key=None, keyid=None, profile=None): salt myminion boto_apigateway.enable_api_key api_key - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = _api_key_patch_replace(conn, apiKey, '/enabled', 'True') - return {'apiKey': _convert_datetime_str(response)} + response = _api_key_patch_replace(conn, apiKey, "/enabled", "True") + return {"apiKey": _convert_datetime_str(response)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def disable_api_key(apiKey, region=None, key=None, keyid=None, profile=None): - ''' + """ disable the given apiKey. CLI Example: @@ -602,17 +668,19 @@ def disable_api_key(apiKey, region=None, key=None, keyid=None, profile=None): salt myminion boto_apigateway.enable_api_key api_key - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = _api_key_patch_replace(conn, apiKey, '/enabled', 'False') - return {'apiKey': _convert_datetime_str(response)} + response = _api_key_patch_replace(conn, apiKey, "/enabled", "False") + return {"apiKey": _convert_datetime_str(response)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def associate_api_key_stagekeys(apiKey, stagekeyslist, region=None, key=None, keyid=None, profile=None): - ''' +def associate_api_key_stagekeys( + apiKey, stagekeyslist, region=None, key=None, keyid=None, profile=None +): + """ associate the given stagekeyslist to the given apiKey. CLI Example: @@ -622,18 +690,20 @@ def associate_api_key_stagekeys(apiKey, stagekeyslist, region=None, key=None, ke salt myminion boto_apigateway.associate_stagekeys_api_key \\ api_key '["restapi id/stage name", ...]' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - pvlist = [('/stages', stagekey) for stagekey in stagekeyslist] + pvlist = [("/stages", stagekey) for stagekey in stagekeyslist] response = _api_key_patch_add(conn, apiKey, pvlist) - return {'associated': True, 'apiKey': _convert_datetime_str(response)} + return {"associated": True, "apiKey": _convert_datetime_str(response)} except ClientError as e: - return {'associated': False, 'error': __utils__['boto3.get_error'](e)} + return {"associated": False, "error": __utils__["boto3.get_error"](e)} -def disassociate_api_key_stagekeys(apiKey, stagekeyslist, region=None, key=None, keyid=None, profile=None): - ''' +def disassociate_api_key_stagekeys( + apiKey, stagekeyslist, region=None, key=None, keyid=None, profile=None +): + """ disassociate the given stagekeyslist to the given apiKey. CLI Example: @@ -643,18 +713,20 @@ def disassociate_api_key_stagekeys(apiKey, stagekeyslist, region=None, key=None, salt myminion boto_apigateway.disassociate_stagekeys_api_key \\ api_key '["restapi id/stage name", ...]' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - pvlist = [('/stages', stagekey) for stagekey in stagekeyslist] + pvlist = [("/stages", stagekey) for stagekey in stagekeyslist] response = _api_key_patch_remove(conn, apiKey, pvlist) - return {'disassociated': True} + return {"disassociated": True} except ClientError as e: - return {'disassociated': False, 'error': __utils__['boto3.get_error'](e)} + return {"disassociated": False, "error": __utils__["boto3.get_error"](e)} -def describe_api_deployments(restApiId, region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_deployments( + restApiId, region=None, key=None, keyid=None, profile=None +): + """ Gets information about the defined API Deployments. Return list of api deployments. CLI Example: @@ -663,7 +735,7 @@ def describe_api_deployments(restApiId, region=None, key=None, keyid=None, profi salt myminion boto_apigateway.describe_api_deployments restApiId - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) deployments = [] @@ -671,18 +743,26 @@ def describe_api_deployments(restApiId, region=None, key=None, keyid=None, profi while True: if _deployments: - deployments = deployments + _deployments['items'] - if 'position' not in _deployments: + deployments = deployments + _deployments["items"] + if "position" not in _deployments: break - _deployments = conn.get_deployments(restApiId=restApiId, position=_deployments['position']) + _deployments = conn.get_deployments( + restApiId=restApiId, position=_deployments["position"] + ) - return {'deployments': [_convert_datetime_str(deployment) for deployment in deployments]} + return { + "deployments": [ + _convert_datetime_str(deployment) for deployment in deployments + ] + } except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe_api_deployment(restApiId, deploymentId, region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_deployment( + restApiId, deploymentId, region=None, key=None, keyid=None, profile=None +): + """ Get API deployment for a given restApiId and deploymentId. CLI Example: @@ -691,18 +771,19 @@ def describe_api_deployment(restApiId, deploymentId, region=None, key=None, keyi salt myminion boto_apigateway.describe_api_deployent restApiId deploymentId - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) deployment = conn.get_deployment(restApiId=restApiId, deploymentId=deploymentId) - return {'deployment': _convert_datetime_str(deployment)} + return {"deployment": _convert_datetime_str(deployment)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def activate_api_deployment(restApiId, stageName, deploymentId, - region=None, key=None, keyid=None, profile=None): - ''' +def activate_api_deployment( + restApiId, stageName, deploymentId, region=None, key=None, keyid=None, profile=None +): + """ Activates previously deployed deployment for a given stage CLI Example: @@ -711,22 +792,35 @@ def activate_api_deployment(restApiId, stageName, deploymentId, salt myminion boto_apigateway.activate_api_deployent restApiId stagename deploymentId - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = conn.update_stage(restApiId=restApiId, stageName=stageName, - patchOperations=[{'op': 'replace', - 'path': '/deploymentId', - 'value': deploymentId}]) - return {'set': True, 'response': _convert_datetime_str(response)} + response = conn.update_stage( + restApiId=restApiId, + stageName=stageName, + patchOperations=[ + {"op": "replace", "path": "/deploymentId", "value": deploymentId} + ], + ) + return {"set": True, "response": _convert_datetime_str(response)} except ClientError as e: - return {'set': False, 'error': __utils__['boto3.get_error'](e)} + return {"set": False, "error": __utils__["boto3.get_error"](e)} -def create_api_deployment(restApiId, stageName, stageDescription='', description='', cacheClusterEnabled=False, - cacheClusterSize='0.5', variables=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_api_deployment( + restApiId, + stageName, + stageDescription="", + description="", + cacheClusterEnabled=False, + cacheClusterSize="0.5", + variables=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates a new API deployment. CLI Example: @@ -736,22 +830,29 @@ def create_api_deployment(restApiId, stageName, stageDescription='', description salt myminion boto_apigateway.create_api_deployent restApiId stagename stageDescription='' \\ description='' cacheClusterEnabled=True|False cacheClusterSize=0.5 variables='{"name": "value"}' - ''' + """ try: variables = dict() if variables is None else variables conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - deployment = conn.create_deployment(restApiId=restApiId, stageName=stageName, - stageDescription=stageDescription, description=description, - cacheClusterEnabled=cacheClusterEnabled, cacheClusterSize=cacheClusterSize, - variables=variables) - return {'created': True, 'deployment': _convert_datetime_str(deployment)} + deployment = conn.create_deployment( + restApiId=restApiId, + stageName=stageName, + stageDescription=stageDescription, + description=description, + cacheClusterEnabled=cacheClusterEnabled, + cacheClusterSize=cacheClusterSize, + variables=variables, + ) + return {"created": True, "deployment": _convert_datetime_str(deployment)} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete_api_deployment(restApiId, deploymentId, region=None, key=None, keyid=None, profile=None): - ''' +def delete_api_deployment( + restApiId, deploymentId, region=None, key=None, keyid=None, profile=None +): + """ Deletes API deployment for a given restApiId and deploymentID CLI Example: @@ -760,17 +861,19 @@ def delete_api_deployment(restApiId, deploymentId, region=None, key=None, keyid= salt myminion boto_apigateway.delete_api_deployent restApiId deploymentId - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_deployment(restApiId=restApiId, deploymentId=deploymentId) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def overwrite_api_stage_variables(restApiId, stageName, variables, region=None, key=None, keyid=None, profile=None): - ''' +def overwrite_api_stage_variables( + restApiId, stageName, variables, region=None, key=None, keyid=None, profile=None +): + """ Overwrite the stage variables for the given restApiId and stage name with the given variables, variables must be in the form of a dictionary. Overwrite will always remove all the existing stage variables associated with the given restApiId and stage name, follow by the adding of all the @@ -782,41 +885,46 @@ def overwrite_api_stage_variables(restApiId, stageName, variables, region=None, salt myminion boto_apigateway.overwrite_api_stage_variables restApiId stageName variables='{"name": "value"}' - ''' + """ try: - res = describe_api_stage(restApiId, stageName, region=region, key=key, keyid=keyid, profile=profile) - if res.get('error'): - return {'overwrite': False, 'error': res.get('error')} + res = describe_api_stage( + restApiId, stageName, region=region, key=key, keyid=keyid, profile=profile + ) + if res.get("error"): + return {"overwrite": False, "error": res.get("error")} conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) # remove all existing variables that are not in the given variables, # followed by adding of the variables - stage = res.get('stage') - old_vars = stage.get('variables', {}) + stage = res.get("stage") + old_vars = stage.get("variables", {}) patch_ops = [] for old_var in old_vars: if old_var not in variables: - patch_ops.append(dict(op='remove', - path='/variables/{0}'.format(old_var), - value='')) + patch_ops.append( + dict(op="remove", path="/variables/{0}".format(old_var), value="") + ) for var, val in six.iteritems(variables): if var not in old_vars or old_vars[var] != val: - patch_ops.append(dict(op='replace', - path='/variables/{0}'.format(var), - value=val)) + patch_ops.append( + dict(op="replace", path="/variables/{0}".format(var), value=val) + ) if patch_ops: - stage = conn.update_stage(restApiId=restApiId, stageName=stageName, - patchOperations=patch_ops) + stage = conn.update_stage( + restApiId=restApiId, stageName=stageName, patchOperations=patch_ops + ) - return {'overwrite': True, 'stage': _convert_datetime_str(stage)} + return {"overwrite": True, "stage": _convert_datetime_str(stage)} except ClientError as e: - return {'overwrite': False, 'error': __utils__['boto3.get_error'](e)} + return {"overwrite": False, "error": __utils__["boto3.get_error"](e)} -def describe_api_stage(restApiId, stageName, region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_stage( + restApiId, stageName, region=None, key=None, keyid=None, profile=None +): + """ Get API stage for a given apiID and stage name CLI Example: @@ -825,17 +933,19 @@ def describe_api_stage(restApiId, stageName, region=None, key=None, keyid=None, salt myminion boto_apigateway.describe_api_stage restApiId stageName - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) stage = conn.get_stage(restApiId=restApiId, stageName=stageName) - return {'stage': _convert_datetime_str(stage)} + return {"stage": _convert_datetime_str(stage)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe_api_stages(restApiId, deploymentId, region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_stages( + restApiId, deploymentId, region=None, key=None, keyid=None, profile=None +): + """ Get all API stages for a given apiID and deploymentID CLI Example: @@ -844,19 +954,29 @@ def describe_api_stages(restApiId, deploymentId, region=None, key=None, keyid=No salt myminion boto_apigateway.describe_api_stages restApiId deploymentId - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) stages = conn.get_stages(restApiId=restApiId, deploymentId=deploymentId) - return {'stages': [_convert_datetime_str(stage) for stage in stages['item']]} + return {"stages": [_convert_datetime_str(stage) for stage in stages["item"]]} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create_api_stage(restApiId, stageName, deploymentId, description='', - cacheClusterEnabled=False, cacheClusterSize='0.5', variables=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_api_stage( + restApiId, + stageName, + deploymentId, + description="", + cacheClusterEnabled=False, + cacheClusterSize="0.5", + variables=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates a new API stage for a given restApiId and deploymentId. CLI Example: @@ -866,21 +986,29 @@ def create_api_stage(restApiId, stageName, deploymentId, description='', salt myminion boto_apigateway.create_api_stage restApiId stagename deploymentId \\ description='' cacheClusterEnabled=True|False cacheClusterSize='0.5' variables='{"name": "value"}' - ''' + """ try: variables = dict() if variables is None else variables conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - stage = conn.create_stage(restApiId=restApiId, stageName=stageName, deploymentId=deploymentId, - description=description, cacheClusterEnabled=cacheClusterEnabled, - cacheClusterSize=cacheClusterSize, variables=variables) - return {'created': True, 'stage': _convert_datetime_str(stage)} + stage = conn.create_stage( + restApiId=restApiId, + stageName=stageName, + deploymentId=deploymentId, + description=description, + cacheClusterEnabled=cacheClusterEnabled, + cacheClusterSize=cacheClusterSize, + variables=variables, + ) + return {"created": True, "stage": _convert_datetime_str(stage)} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete_api_stage(restApiId, stageName, region=None, key=None, keyid=None, profile=None): - ''' +def delete_api_stage( + restApiId, stageName, region=None, key=None, keyid=None, profile=None +): + """ Deletes stage identified by stageName from API identified by restApiId CLI Example: @@ -889,17 +1017,19 @@ def delete_api_stage(restApiId, stageName, region=None, key=None, keyid=None, pr salt myminion boto_apigateway.delete_api_stage restApiId stageName - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_stage(restApiId=restApiId, stageName=stageName) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def flush_api_stage_cache(restApiId, stageName, region=None, key=None, keyid=None, profile=None): - ''' +def flush_api_stage_cache( + restApiId, stageName, region=None, key=None, keyid=None, profile=None +): + """ Flushes cache for the stage identified by stageName from API identified by restApiId CLI Example: @@ -908,19 +1038,29 @@ def flush_api_stage_cache(restApiId, stageName, region=None, key=None, keyid=Non salt myminion boto_apigateway.flush_api_stage_cache restApiId stageName - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.flush_stage_cache(restApiId=restApiId, stageName=stageName) - return {'flushed': True} + return {"flushed": True} except ClientError as e: - return {'flushed': False, 'error': __utils__['boto3.get_error'](e)} + return {"flushed": False, "error": __utils__["boto3.get_error"](e)} -def create_api_method(restApiId, resourcePath, httpMethod, authorizationType, - apiKeyRequired=False, requestParameters=None, requestModels=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_api_method( + restApiId, + resourcePath, + httpMethod, + authorizationType, + apiKeyRequired=False, + requestParameters=None, + requestModels=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates API method for a resource in the given API CLI Example: @@ -930,27 +1070,43 @@ def create_api_method(restApiId, resourcePath, httpMethod, authorizationType, salt myminion boto_apigateway.create_api_method restApiId resourcePath, httpMethod, authorizationType, \\ apiKeyRequired=False, requestParameters='{"name", "value"}', requestModels='{"content-type", "value"}' - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: - requestParameters = dict() if requestParameters is None else requestParameters + requestParameters = ( + dict() if requestParameters is None else requestParameters + ) requestModels = dict() if requestModels is None else requestModels conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - method = conn.put_method(restApiId=restApiId, resourceId=resource['id'], httpMethod=httpMethod, - authorizationType=str(authorizationType), apiKeyRequired=apiKeyRequired, # future lint: disable=blacklisted-function - requestParameters=requestParameters, requestModels=requestModels) - return {'created': True, 'method': method} - return {'created': False, 'error': 'Failed to create method'} + method = conn.put_method( + restApiId=restApiId, + resourceId=resource["id"], + httpMethod=httpMethod, + authorizationType=str(authorizationType), + apiKeyRequired=apiKeyRequired, # future lint: disable=blacklisted-function + requestParameters=requestParameters, + requestModels=requestModels, + ) + return {"created": True, "method": method} + return {"created": False, "error": "Failed to create method"} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def describe_api_method(restApiId, resourcePath, httpMethod, region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_method( + restApiId, resourcePath, httpMethod, region=None, key=None, keyid=None, profile=None +): + """ Get API method for a resource in the given API CLI Example: @@ -959,21 +1115,31 @@ def describe_api_method(restApiId, resourcePath, httpMethod, region=None, key=No salt myminion boto_apigateway.describe_api_method restApiId resourcePath httpMethod - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - method = conn.get_method(restApiId=restApiId, resourceId=resource['id'], httpMethod=httpMethod) - return {'method': _convert_datetime_str(method)} - return {'error': 'get API method failed: no such resource'} + method = conn.get_method( + restApiId=restApiId, resourceId=resource["id"], httpMethod=httpMethod + ) + return {"method": _convert_datetime_str(method)} + return {"error": "get API method failed: no such resource"} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def delete_api_method(restApiId, resourcePath, httpMethod, region=None, key=None, keyid=None, profile=None): - ''' +def delete_api_method( + restApiId, resourcePath, httpMethod, region=None, key=None, keyid=None, profile=None +): + """ Delete API method for a resource in the given API CLI Example: @@ -982,22 +1148,40 @@ def delete_api_method(restApiId, resourcePath, httpMethod, region=None, key=None salt myminion boto_apigateway.delete_api_method restApiId resourcePath httpMethod - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.delete_method(restApiId=restApiId, resourceId=resource['id'], httpMethod=httpMethod) - return {'deleted': True} - return {'deleted': False, 'error': 'get API method failed: no such resource'} + conn.delete_method( + restApiId=restApiId, resourceId=resource["id"], httpMethod=httpMethod + ) + return {"deleted": True} + return {"deleted": False, "error": "get API method failed: no such resource"} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def create_api_method_response(restApiId, resourcePath, httpMethod, statusCode, responseParameters=None, - responseModels=None, region=None, key=None, keyid=None, profile=None): - ''' +def create_api_method_response( + restApiId, + resourcePath, + httpMethod, + statusCode, + responseParameters=None, + responseModels=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create API method response for a method on a given resource in the given API CLI Example: @@ -1007,27 +1191,48 @@ def create_api_method_response(restApiId, resourcePath, httpMethod, statusCode, salt myminion boto_apigateway.create_api_method_response restApiId resourcePath httpMethod \\ statusCode responseParameters='{"name", "True|False"}' responseModels='{"content-type", "model"}' - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: - responseParameters = dict() if responseParameters is None else responseParameters + responseParameters = ( + dict() if responseParameters is None else responseParameters + ) responseModels = dict() if responseModels is None else responseModels conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = conn.put_method_response(restApiId=restApiId, resourceId=resource['id'], - httpMethod=httpMethod, statusCode=str(statusCode), # future lint: disable=blacklisted-function - responseParameters=responseParameters, responseModels=responseModels) - return {'created': True, 'response': response} - return {'created': False, 'error': 'no such resource'} + response = conn.put_method_response( + restApiId=restApiId, + resourceId=resource["id"], + httpMethod=httpMethod, + statusCode=str(statusCode), # future lint: disable=blacklisted-function + responseParameters=responseParameters, + responseModels=responseModels, + ) + return {"created": True, "response": response} + return {"created": False, "error": "no such resource"} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete_api_method_response(restApiId, resourcePath, httpMethod, statusCode, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_api_method_response( + restApiId, + resourcePath, + httpMethod, + statusCode, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Delete API method response for a resource in the given API CLI Example: @@ -1036,23 +1241,41 @@ def delete_api_method_response(restApiId, resourcePath, httpMethod, statusCode, salt myminion boto_apigateway.delete_api_method_response restApiId resourcePath httpMethod statusCode - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.delete_method_response(restApiId=restApiId, resourceId=resource['id'], - httpMethod=httpMethod, statusCode=str(statusCode)) # future lint: disable=blacklisted-function - return {'deleted': True} - return {'deleted': False, 'error': 'no such resource'} + conn.delete_method_response( + restApiId=restApiId, + resourceId=resource["id"], + httpMethod=httpMethod, + statusCode=str(statusCode), + ) # future lint: disable=blacklisted-function + return {"deleted": True} + return {"deleted": False, "error": "no such resource"} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def describe_api_method_response(restApiId, resourcePath, httpMethod, statusCode, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_method_response( + restApiId, + resourcePath, + httpMethod, + statusCode, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get API method response for a resource in the given API CLI Example: @@ -1061,22 +1284,32 @@ def describe_api_method_response(restApiId, resourcePath, httpMethod, statusCode salt myminion boto_apigateway.describe_api_method_response restApiId resourcePath httpMethod statusCode - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = conn.get_method_response(restApiId=restApiId, resourceId=resource['id'], - httpMethod=httpMethod, statusCode=str(statusCode)) # future lint: disable=blacklisted-function - return {'response': _convert_datetime_str(response)} - return {'error': 'no such resource'} + response = conn.get_method_response( + restApiId=restApiId, + resourceId=resource["id"], + httpMethod=httpMethod, + statusCode=str(statusCode), + ) # future lint: disable=blacklisted-function + return {"response": _convert_datetime_str(response)} + return {"error": "no such resource"} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def describe_api_models(restApiId, region=None, key=None, keyid=None, profile=None): - ''' + """ Get all models for a given API CLI Example: @@ -1085,17 +1318,19 @@ def describe_api_models(restApiId, region=None, key=None, keyid=None, profile=No salt myminion boto_apigateway.describe_api_models restApiId - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - models = _multi_call(conn.get_models, 'items', restApiId=restApiId) - return {'models': [_convert_datetime_str(model) for model in models]} + models = _multi_call(conn.get_models, "items", restApiId=restApiId) + return {"models": [_convert_datetime_str(model) for model in models]} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe_api_model(restApiId, modelName, flatten=True, region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_model( + restApiId, modelName, flatten=True, region=None, key=None, keyid=None, profile=None +): + """ Get a model by name for a given API CLI Example: @@ -1104,17 +1339,21 @@ def describe_api_model(restApiId, modelName, flatten=True, region=None, key=None salt myminion boto_apigateway.describe_api_model restApiId modelName [True] - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - model = conn.get_model(restApiId=restApiId, modelName=modelName, flatten=flatten) - return {'model': _convert_datetime_str(model)} + model = conn.get_model( + restApiId=restApiId, modelName=modelName, flatten=flatten + ) + return {"model": _convert_datetime_str(model)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def api_model_exists(restApiId, modelName, region=None, key=None, keyid=None, profile=None): - ''' +def api_model_exists( + restApiId, modelName, region=None, key=None, keyid=None, profile=None +): + """ Check to see if the given modelName exists in the given restApiId CLI Example: @@ -1122,23 +1361,30 @@ def api_model_exists(restApiId, modelName, region=None, key=None, keyid=None, pr .. code-block:: bash salt myminion boto_apigateway.api_model_exists restApiId modelName - ''' - r = describe_api_model(restApiId, modelName, region=region, key=key, keyid=keyid, profile=profile) + """ + r = describe_api_model( + restApiId, modelName, region=region, key=key, keyid=keyid, profile=profile + ) - return {'exists': bool(r.get('model'))} + return {"exists": bool(r.get("model"))} def _api_model_patch_replace(conn, restApiId, modelName, path, value): - ''' + """ the replace patch operation on a Model resource - ''' - response = conn.update_model(restApiId=restApiId, modelName=modelName, - patchOperations=[{'op': 'replace', 'path': path, 'value': value}]) + """ + response = conn.update_model( + restApiId=restApiId, + modelName=modelName, + patchOperations=[{"op": "replace", "path": path, "value": value}], + ) return response -def update_api_model_schema(restApiId, modelName, schema, region=None, key=None, keyid=None, profile=None): - ''' +def update_api_model_schema( + restApiId, modelName, schema, region=None, key=None, keyid=None, profile=None +): + """ update the schema (in python dictionary format) for the given model in the given restApiId CLI Example: @@ -1147,18 +1393,24 @@ def update_api_model_schema(restApiId, modelName, schema, region=None, key=None, salt myminion boto_apigateway.update_api_model_schema restApiId modelName schema - ''' + """ try: - schema_json = salt.utils.json.dumps(schema) if isinstance(schema, dict) else schema + schema_json = ( + salt.utils.json.dumps(schema) if isinstance(schema, dict) else schema + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = _api_model_patch_replace(conn, restApiId, modelName, '/schema', schema_json) - return {'updated': True, 'model': _convert_datetime_str(response)} + response = _api_model_patch_replace( + conn, restApiId, modelName, "/schema", schema_json + ) + return {"updated": True, "model": _convert_datetime_str(response)} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def delete_api_model(restApiId, modelName, region=None, key=None, keyid=None, profile=None): - ''' +def delete_api_model( + restApiId, modelName, region=None, key=None, keyid=None, profile=None +): + """ Delete a model identified by name in a given API CLI Example: @@ -1167,18 +1419,27 @@ def delete_api_model(restApiId, modelName, region=None, key=None, keyid=None, pr salt myminion boto_apigateway.delete_api_model restApiId modelName - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_model(restApiId=restApiId, modelName=modelName) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def create_api_model(restApiId, modelName, modelDescription, schema, contentType='application/json', - region=None, key=None, keyid=None, profile=None): - ''' +def create_api_model( + restApiId, + modelName, + modelDescription, + schema, + contentType="application/json", + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a new model in a given API with a given schema, currently only contentType supported is 'application/json' @@ -1188,19 +1449,28 @@ def create_api_model(restApiId, modelName, modelDescription, schema, contentType salt myminion boto_apigateway.create_api_model restApiId modelName modelDescription '' 'content-type' - ''' + """ try: - schema_json = salt.utils.json.dumps(schema) if isinstance(schema, dict) else schema + schema_json = ( + salt.utils.json.dumps(schema) if isinstance(schema, dict) else schema + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - model = conn.create_model(restApiId=restApiId, name=modelName, description=modelDescription, - schema=schema_json, contentType=contentType) - return {'created': True, 'model': _convert_datetime_str(model)} + model = conn.create_model( + restApiId=restApiId, + name=modelName, + description=modelDescription, + schema=schema_json, + contentType=contentType, + ) + return {"created": True, "model": _convert_datetime_str(model)} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def describe_api_integration(restApiId, resourcePath, httpMethod, region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_integration( + restApiId, resourcePath, httpMethod, region=None, key=None, keyid=None, profile=None +): + """ Get an integration for a given method in a given API CLI Example: @@ -1209,22 +1479,38 @@ def describe_api_integration(restApiId, resourcePath, httpMethod, region=None, k salt myminion boto_apigateway.describe_api_integration restApiId resourcePath httpMethod - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - integration = conn.get_integration(restApiId=restApiId, resourceId=resource['id'], httpMethod=httpMethod) - return {'integration': _convert_datetime_str(integration)} - return {'error': 'no such resource'} + integration = conn.get_integration( + restApiId=restApiId, resourceId=resource["id"], httpMethod=httpMethod + ) + return {"integration": _convert_datetime_str(integration)} + return {"error": "no such resource"} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe_api_integration_response(restApiId, resourcePath, httpMethod, statusCode, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_api_integration_response( + restApiId, + resourcePath, + httpMethod, + statusCode, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get an integration response for a given method in a given API CLI Example: @@ -1233,22 +1519,34 @@ def describe_api_integration_response(restApiId, resourcePath, httpMethod, statu salt myminion boto_apigateway.describe_api_integration_response restApiId resourcePath httpMethod statusCode - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = conn.get_integration_response(restApiId=restApiId, resourceId=resource['id'], - httpMethod=httpMethod, statusCode=statusCode) - return {'response': _convert_datetime_str(response)} - return {'error': 'no such resource'} + response = conn.get_integration_response( + restApiId=restApiId, + resourceId=resource["id"], + httpMethod=httpMethod, + statusCode=statusCode, + ) + return {"response": _convert_datetime_str(response)} + return {"error": "no such resource"} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def delete_api_integration(restApiId, resourcePath, httpMethod, region=None, key=None, keyid=None, profile=None): - ''' +def delete_api_integration( + restApiId, resourcePath, httpMethod, region=None, key=None, keyid=None, profile=None +): + """ Deletes an integration for a given method in a given API CLI Example: @@ -1257,22 +1555,38 @@ def delete_api_integration(restApiId, resourcePath, httpMethod, region=None, key salt myminion boto_apigateway.delete_api_integration restApiId resourcePath httpMethod - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.delete_integration(restApiId=restApiId, resourceId=resource['id'], httpMethod=httpMethod) - return {'deleted': True} - return {'deleted': False, 'error': 'no such resource'} + conn.delete_integration( + restApiId=restApiId, resourceId=resource["id"], httpMethod=httpMethod + ) + return {"deleted": True} + return {"deleted": False, "error": "no such resource"} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def delete_api_integration_response(restApiId, resourcePath, httpMethod, statusCode, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_api_integration_response( + restApiId, + resourcePath, + httpMethod, + statusCode, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Deletes an integration response for a given method in a given API CLI Example: @@ -1281,38 +1595,60 @@ def delete_api_integration_response(restApiId, resourcePath, httpMethod, statusC salt myminion boto_apigateway.delete_api_integration_response restApiId resourcePath httpMethod statusCode - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.delete_integration_response(restApiId=restApiId, resourceId=resource['id'], - httpMethod=httpMethod, statusCode=statusCode) - return {'deleted': True} - return {'deleted': False, 'error': 'no such resource'} + conn.delete_integration_response( + restApiId=restApiId, + resourceId=resource["id"], + httpMethod=httpMethod, + statusCode=statusCode, + ) + return {"deleted": True} + return {"deleted": False, "error": "no such resource"} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} def _get_role_arn(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Helper function to get an ARN if name does not look like an ARN. - ''' - if name.startswith('arn:aws:iam:'): + """ + if name.startswith("arn:aws:iam:"): return name - account_id = __salt__['boto_iam.get_account_id']( + account_id = __salt__["boto_iam.get_account_id"]( region=region, key=key, keyid=keyid, profile=profile ) - return 'arn:aws:iam::{0}:role/{1}'.format(account_id, name) + return "arn:aws:iam::{0}:role/{1}".format(account_id, name) -def create_api_integration(restApiId, resourcePath, httpMethod, integrationType, integrationHttpMethod, - uri, credentials, requestParameters=None, requestTemplates=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_api_integration( + restApiId, + resourcePath, + httpMethod, + integrationType, + integrationHttpMethod, + uri, + credentials, + requestParameters=None, + requestTemplates=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates an integration for a given method in a given API. If integrationType is MOCK, uri and credential parameters will be ignored. @@ -1328,34 +1664,61 @@ def create_api_integration(restApiId, resourcePath, httpMethod, integrationType, salt myminion boto_apigateway.create_api_integration restApiId resourcePath httpMethod \\ integrationType integrationHttpMethod uri credentials ['{}' ['{}']] - ''' + """ try: - credentials = _get_role_arn(credentials, region=region, key=key, keyid=keyid, profile=profile) - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + credentials = _get_role_arn( + credentials, region=region, key=key, keyid=keyid, profile=profile + ) + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: - requestParameters = dict() if requestParameters is None else requestParameters + requestParameters = ( + dict() if requestParameters is None else requestParameters + ) requestTemplates = dict() if requestTemplates is None else requestTemplates conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if httpMethod.lower() == 'options': + if httpMethod.lower() == "options": uri = "" credentials = "" - integration = conn.put_integration(restApiId=restApiId, resourceId=resource['id'], httpMethod=httpMethod, - type=integrationType, integrationHttpMethod=integrationHttpMethod, - uri=uri, credentials=credentials, requestParameters=requestParameters, - requestTemplates=requestTemplates) - return {'created': True, 'integration': integration} - return {'created': False, 'error': 'no such resource'} + integration = conn.put_integration( + restApiId=restApiId, + resourceId=resource["id"], + httpMethod=httpMethod, + type=integrationType, + integrationHttpMethod=integrationHttpMethod, + uri=uri, + credentials=credentials, + requestParameters=requestParameters, + requestTemplates=requestTemplates, + ) + return {"created": True, "integration": integration} + return {"created": False, "error": "no such resource"} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def create_api_integration_response(restApiId, resourcePath, httpMethod, statusCode, selectionPattern, - responseParameters=None, responseTemplates=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_api_integration_response( + restApiId, + resourcePath, + httpMethod, + statusCode, + selectionPattern, + responseParameters=None, + responseTemplates=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates an integration response for a given method in a given API CLI Example: @@ -1365,35 +1728,51 @@ def create_api_integration_response(restApiId, resourcePath, httpMethod, statusC salt myminion boto_apigateway.create_api_integration_response restApiId resourcePath httpMethod \\ statusCode selectionPattern ['{}' ['{}']] - ''' + """ try: - resource = describe_api_resource(restApiId, resourcePath, region=region, - key=key, keyid=keyid, profile=profile).get('resource') + resource = describe_api_resource( + restApiId, + resourcePath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("resource") if resource: - responseParameters = dict() if responseParameters is None else responseParameters - responseTemplates = dict() if responseTemplates is None else responseTemplates + responseParameters = ( + dict() if responseParameters is None else responseParameters + ) + responseTemplates = ( + dict() if responseTemplates is None else responseTemplates + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - response = conn.put_integration_response(restApiId=restApiId, resourceId=resource['id'], - httpMethod=httpMethod, statusCode=statusCode, - selectionPattern=selectionPattern, - responseParameters=responseParameters, - responseTemplates=responseTemplates) - return {'created': True, 'response': response} - return {'created': False, 'error': 'no such resource'} + response = conn.put_integration_response( + restApiId=restApiId, + resourceId=resource["id"], + httpMethod=httpMethod, + statusCode=statusCode, + selectionPattern=selectionPattern, + responseParameters=responseParameters, + responseTemplates=responseTemplates, + ) + return {"created": True, "response": response} + return {"created": False, "error": "no such resource"} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} def _filter_plans(attr, name, plans): - ''' + """ Helper to return list of usage plan items matching the given attribute value. - ''' + """ return [plan for plan in plans if plan[attr] == name] -def describe_usage_plans(name=None, plan_id=None, region=None, key=None, keyid=None, profile=None): - ''' +def describe_usage_plans( + name=None, plan_id=None, region=None, key=None, keyid=None, profile=None +): + """ Returns a list of existing usage plans, optionally filtered to match a given plan name .. versionadded:: 2017.7.0 @@ -1406,46 +1785,63 @@ def describe_usage_plans(name=None, plan_id=None, region=None, key=None, keyid=N salt myminion boto_apigateway.describe_usage_plans name='usage plan name' salt myminion boto_apigateway.describe_usage_plans plan_id='usage plan id' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - plans = _multi_call(conn.get_usage_plans, 'items') + plans = _multi_call(conn.get_usage_plans, "items") if name: - plans = _filter_plans('name', name, plans) + plans = _filter_plans("name", name, plans) if plan_id: - plans = _filter_plans('id', plan_id, plans) + plans = _filter_plans("id", plan_id, plans) - return {'plans': [_convert_datetime_str(plan) for plan in plans]} + return {"plans": [_convert_datetime_str(plan) for plan in plans]} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def _validate_throttle(throttle): - ''' + """ Helper to verify that throttling parameters are valid - ''' + """ if throttle is not None: if not isinstance(throttle, dict): - raise TypeError('throttle must be a dictionary, provided value: {0}'.format(throttle)) + raise TypeError( + "throttle must be a dictionary, provided value: {0}".format(throttle) + ) def _validate_quota(quota): - ''' + """ Helper to verify that quota parameters are valid - ''' + """ if quota is not None: if not isinstance(quota, dict): - raise TypeError('quota must be a dictionary, provided value: {0}'.format(quota)) - periods = ['DAY', 'WEEK', 'MONTH'] - if 'period' not in quota or quota['period'] not in periods: - raise ValueError('quota must have a valid period specified, valid values are {0}'.format(','.join(periods))) - if 'limit' not in quota: - raise ValueError('quota limit must have a valid value') + raise TypeError( + "quota must be a dictionary, provided value: {0}".format(quota) + ) + periods = ["DAY", "WEEK", "MONTH"] + if "period" not in quota or quota["period"] not in periods: + raise ValueError( + "quota must have a valid period specified, valid values are {0}".format( + ",".join(periods) + ) + ) + if "limit" not in quota: + raise ValueError("quota limit must have a valid value") -def create_usage_plan(name, description=None, throttle=None, quota=None, region=None, key=None, keyid=None, profile=None): - ''' +def create_usage_plan( + name, + description=None, + throttle=None, + quota=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates a new usage plan with throttling and quotas optionally applied .. versionadded:: 2017.7.0 @@ -1480,30 +1876,32 @@ def create_usage_plan(name, description=None, throttle=None, quota=None, region= salt myminion boto_apigateway.create_usage_plan name='usage plan name' throttle='{"rateLimit": 10.0, "burstLimit": 10}' - ''' + """ try: _validate_throttle(throttle) _validate_quota(quota) values = dict(name=name) if description: - values['description'] = description + values["description"] = description if throttle: - values['throttle'] = throttle + values["throttle"] = throttle if quota: - values['quota'] = quota + values["quota"] = quota conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) res = conn.create_usage_plan(**values) - return {'created': True, 'result': res} + return {"created": True, "result": res} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} except (TypeError, ValueError) as e: - return {'error': six.text_type(e)} + return {"error": six.text_type(e)} -def update_usage_plan(plan_id, throttle=None, quota=None, region=None, key=None, keyid=None, profile=None): - ''' +def update_usage_plan( + plan_id, throttle=None, quota=None, region=None, key=None, keyid=None, profile=None +): + """ Updates an existing usage plan with throttling and quotas .. versionadded:: 2017.7.0 @@ -1538,7 +1936,7 @@ def update_usage_plan(plan_id, throttle=None, quota=None, region=None, key=None, salt myminion boto_apigateway.update_usage_plan plan_id='usage plan id' throttle='{"rateLimit": 10.0, "burstLimit": 10}' - ''' + """ try: _validate_throttle(throttle) _validate_quota(quota) @@ -1548,36 +1946,63 @@ def update_usage_plan(plan_id, throttle=None, quota=None, region=None, key=None, patchOperations = [] if throttle is None: - patchOperations.append({'op': 'remove', 'path': '/throttle'}) + patchOperations.append({"op": "remove", "path": "/throttle"}) else: - if 'rateLimit' in throttle: - patchOperations.append({'op': 'replace', 'path': '/throttle/rateLimit', 'value': str(throttle['rateLimit'])}) # future lint: disable=blacklisted-function - if 'burstLimit' in throttle: - patchOperations.append({'op': 'replace', 'path': '/throttle/burstLimit', 'value': str(throttle['burstLimit'])}) # future lint: disable=blacklisted-function + if "rateLimit" in throttle: + patchOperations.append( + { + "op": "replace", + "path": "/throttle/rateLimit", + "value": str(throttle["rateLimit"]), + } + ) # future lint: disable=blacklisted-function + if "burstLimit" in throttle: + patchOperations.append( + { + "op": "replace", + "path": "/throttle/burstLimit", + "value": str(throttle["burstLimit"]), + } + ) # future lint: disable=blacklisted-function if quota is None: - patchOperations.append({'op': 'remove', 'path': '/quota'}) + patchOperations.append({"op": "remove", "path": "/quota"}) else: - patchOperations.append({'op': 'replace', 'path': '/quota/period', 'value': str(quota['period'])}) # future lint: disable=blacklisted-function - patchOperations.append({'op': 'replace', 'path': '/quota/limit', 'value': str(quota['limit'])}) # future lint: disable=blacklisted-function - if 'offset' in quota: - patchOperations.append({'op': 'replace', 'path': '/quota/offset', 'value': str(quota['offset'])}) # future lint: disable=blacklisted-function + patchOperations.append( + { + "op": "replace", + "path": "/quota/period", + "value": str(quota["period"]), + } + ) # future lint: disable=blacklisted-function + patchOperations.append( + {"op": "replace", "path": "/quota/limit", "value": str(quota["limit"])} + ) # future lint: disable=blacklisted-function + if "offset" in quota: + patchOperations.append( + { + "op": "replace", + "path": "/quota/offset", + "value": str(quota["offset"]), + } + ) # future lint: disable=blacklisted-function if patchOperations: - res = conn.update_usage_plan(usagePlanId=plan_id, - patchOperations=patchOperations) - return {'updated': True, 'result': res} + res = conn.update_usage_plan( + usagePlanId=plan_id, patchOperations=patchOperations + ) + return {"updated": True, "result": res} - return {'updated': False} + return {"updated": False} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} except (TypeError, ValueError) as e: - return {'error': six.text_type(e)} + return {"error": six.text_type(e)} def delete_usage_plan(plan_id, region=None, key=None, keyid=None, profile=None): - ''' + """ Deletes usage plan identified by plan_id .. versionadded:: 2017.7.0 @@ -1588,23 +2013,27 @@ def delete_usage_plan(plan_id, region=None, key=None, keyid=None, profile=None): salt myminion boto_apigateway.delete_usage_plan plan_id='usage plan id' - ''' + """ try: - existing = describe_usage_plans(plan_id=plan_id, region=region, key=key, keyid=keyid, profile=profile) + existing = describe_usage_plans( + plan_id=plan_id, region=region, key=key, keyid=keyid, profile=profile + ) # don't attempt to delete the usage plan if it does not exist - if 'error' in existing: - return {'error': existing['error']} + if "error" in existing: + return {"error": existing["error"]} - if 'plans' in existing and existing['plans']: + if "plans" in existing and existing["plans"]: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) res = conn.delete_usage_plan(usagePlanId=plan_id) - return {'deleted': True, 'usagePlanId': plan_id} + return {"deleted": True, "usagePlanId": plan_id} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def _update_usage_plan_apis(plan_id, apis, op, region=None, key=None, keyid=None, profile=None): - ''' +def _update_usage_plan_apis( + plan_id, apis, op, region=None, key=None, keyid=None, profile=None +): + """ Helper function that updates the usage plan identified by plan_id by adding or removing it to each of the stages, specified by apis parameter. apis @@ -1618,29 +2047,34 @@ def _update_usage_plan_apis(plan_id, apis, op, region=None, key=None, keyid=None op 'add' or 'remove' - ''' + """ try: patchOperations = [] for api in apis: - patchOperations.append({ - 'op': op, - 'path': '/apiStages', - 'value': '{0}:{1}'.format(api['apiId'], api['stage']) - }) + patchOperations.append( + { + "op": op, + "path": "/apiStages", + "value": "{0}:{1}".format(api["apiId"], api["stage"]), + } + ) res = None if patchOperations: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - res = conn.update_usage_plan(usagePlanId=plan_id, - patchOperations=patchOperations) - return {'success': True, 'result': res} + res = conn.update_usage_plan( + usagePlanId=plan_id, patchOperations=patchOperations + ) + return {"success": True, "result": res} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} except Exception as e: # pylint: disable=broad-except - return {'error': e} + return {"error": e} -def attach_usage_plan_to_apis(plan_id, apis, region=None, key=None, keyid=None, profile=None): - ''' +def attach_usage_plan_to_apis( + plan_id, apis, region=None, key=None, keyid=None, profile=None +): + """ Attaches given usage plan to each of the apis provided in a list of apiId and stage values .. versionadded:: 2017.7.0 @@ -1660,12 +2094,16 @@ def attach_usage_plan_to_apis(plan_id, apis, region=None, key=None, keyid=None, salt myminion boto_apigateway.attach_usage_plan_to_apis plan_id='usage plan id' apis='[{"apiId": "some id 1", "stage": "some stage 1"}]' - ''' - return _update_usage_plan_apis(plan_id, apis, 'add', region=region, key=key, keyid=keyid, profile=profile) + """ + return _update_usage_plan_apis( + plan_id, apis, "add", region=region, key=key, keyid=keyid, profile=profile + ) -def detach_usage_plan_from_apis(plan_id, apis, region=None, key=None, keyid=None, profile=None): - ''' +def detach_usage_plan_from_apis( + plan_id, apis, region=None, key=None, keyid=None, profile=None +): + """ Detaches given usage plan from each of the apis provided in a list of apiId and stage value .. versionadded:: 2017.7.0 @@ -1685,5 +2123,7 @@ def detach_usage_plan_from_apis(plan_id, apis, region=None, key=None, keyid=None salt myminion boto_apigateway.detach_usage_plan_to_apis plan_id='usage plan id' apis='[{"apiId": "some id 1", "stage": "some stage 1"}]' - ''' - return _update_usage_plan_apis(plan_id, apis, 'remove', region=region, key=key, keyid=keyid, profile=profile) + """ + return _update_usage_plan_apis( + plan_id, apis, "remove", region=region, key=key, keyid=keyid, profile=profile + ) diff --git a/salt/modules/boto_asg.py b/salt/modules/boto_asg.py index 0df3b080666..6baebe2404c 100644 --- a/salt/modules/boto_asg.py +++ b/salt/modules/boto_asg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Autoscale Groups .. versionadded:: 2014.7.0 @@ -41,37 +41,18 @@ Connection module for Amazon Autoscale Groups :depends: boto :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import datetime +import email.mime.multipart import logging import sys import time -import email.mime.multipart - -log = logging.getLogger(__name__) -DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" - -# Import third party libs -from salt.ext import six -try: - import boto - import boto.ec2 - import boto.ec2.instance - import boto.ec2.blockdevicemapping as blockdevicemapping - import boto.ec2.autoscale as autoscale - logging.getLogger('boto').setLevel(logging.CRITICAL) - import boto3 # pylint: disable=unused-import - from botocore.exceptions import ClientError - logging.getLogger('boto3').setLevel(logging.CRITICAL) - HAS_BOTO = True -except ImportError: - HAS_BOTO = False - # Import Salt libs import salt.utils.compat @@ -79,35 +60,63 @@ import salt.utils.json import salt.utils.odict as odict import salt.utils.versions +# Import third party libs +from salt.ext import six + +log = logging.getLogger(__name__) +DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" + + +try: + import boto + import boto.ec2 + import boto.ec2.instance + import boto.ec2.blockdevicemapping as blockdevicemapping + import boto.ec2.autoscale as autoscale + + logging.getLogger("boto").setLevel(logging.CRITICAL) + import boto3 # pylint: disable=unused-import + from botocore.exceptions import ClientError + + logging.getLogger("boto3").setLevel(logging.CRITICAL) + HAS_BOTO = True +except ImportError: + HAS_BOTO = False + def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' + """ has_boto_reqs = salt.utils.versions.check_boto_reqs() if has_boto_reqs is True: - __utils__['boto.assign_funcs'](__name__, 'asg', module='ec2.autoscale', pack=__salt__) - setattr(sys.modules[__name__], '_get_ec2_conn', - __utils__['boto.get_connection_func']('ec2')) + __utils__["boto.assign_funcs"]( + __name__, "asg", module="ec2.autoscale", pack=__salt__ + ) + setattr( + sys.modules[__name__], + "_get_ec2_conn", + __utils__["boto.get_connection_func"]("ec2"), + ) return has_boto_reqs def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs']( - __name__, 'autoscaling', - get_conn_funcname='_get_conn_autoscaling_boto3') + __utils__["boto3.assign_funcs"]( + __name__, "autoscaling", get_conn_funcname="_get_conn_autoscaling_boto3" + ) def exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if an autoscale group exists. CLI example:: salt myminion boto_asg.exists myasg region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 while True: @@ -116,12 +125,12 @@ def exists(name, region=None, key=None, keyid=None, profile=None): if _conn: return True else: - msg = 'The autoscale group does not exist in region {0}'.format(region) + msg = "The autoscale group does not exist in region {0}".format(region) log.debug(msg) return False except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue @@ -130,13 +139,13 @@ def exists(name, region=None, key=None, keyid=None, profile=None): def get_config(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get the configuration for an autoscale group. CLI example:: salt myminion boto_asg.get_config myasg region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 while True: @@ -147,30 +156,41 @@ def get_config(name, region=None, key=None, keyid=None, profile=None): else: return {} ret = odict.OrderedDict() - attrs = ['name', 'availability_zones', 'default_cooldown', - 'desired_capacity', 'health_check_period', - 'health_check_type', 'launch_config_name', 'load_balancers', - 'max_size', 'min_size', 'placement_group', - 'vpc_zone_identifier', 'tags', 'termination_policies', - 'suspended_processes'] + attrs = [ + "name", + "availability_zones", + "default_cooldown", + "desired_capacity", + "health_check_period", + "health_check_type", + "launch_config_name", + "load_balancers", + "max_size", + "min_size", + "placement_group", + "vpc_zone_identifier", + "tags", + "termination_policies", + "suspended_processes", + ] for attr in attrs: # Tags are objects, so we need to turn them into dicts. - if attr == 'tags': + if attr == "tags": _tags = [] for tag in asg.tags: _tag = odict.OrderedDict() - _tag['key'] = tag.key - _tag['value'] = tag.value - _tag['propagate_at_launch'] = tag.propagate_at_launch + _tag["key"] = tag.key + _tag["value"] = tag.value + _tag["propagate_at_launch"] = tag.propagate_at_launch _tags.append(_tag) - ret['tags'] = _tags + ret["tags"] = _tags # Boto accepts a string or list as input for vpc_zone_identifier, # but always returns a comma separated list. We require lists in # states. - elif attr == 'vpc_zone_identifier': - ret[attr] = getattr(asg, attr).split(',') + elif attr == "vpc_zone_identifier": + ret[attr] = getattr(asg, attr).split(",") # convert SuspendedProcess objects to names - elif attr == 'suspended_processes': + elif attr == "suspended_processes": suspended_processes = getattr(asg, attr) ret[attr] = sorted([x.process_name for x in suspended_processes]) else: @@ -180,34 +200,38 @@ def get_config(name, region=None, key=None, keyid=None, profile=None): ret["scaling_policies"] = [] for policy in policies: ret["scaling_policies"].append( - dict([ - ("name", policy.name), - ("adjustment_type", policy.adjustment_type), - ("scaling_adjustment", policy.scaling_adjustment), - ("min_adjustment_step", policy.min_adjustment_step), - ("cooldown", policy.cooldown) - ]) + dict( + [ + ("name", policy.name), + ("adjustment_type", policy.adjustment_type), + ("scaling_adjustment", policy.scaling_adjustment), + ("min_adjustment_step", policy.min_adjustment_step), + ("cooldown", policy.cooldown), + ] + ) ) # scheduled actions actions = conn.get_all_scheduled_actions(as_group=name) - ret['scheduled_actions'] = {} + ret["scheduled_actions"] = {} for action in actions: end_time = None if action.end_time: end_time = action.end_time.isoformat() - ret['scheduled_actions'][action.name] = dict([ - ("min_size", action.min_size), - ("max_size", action.max_size), - # AWS bug - ("desired_capacity", int(action.desired_capacity)), - ("start_time", action.start_time.isoformat()), - ("end_time", end_time), - ("recurrence", action.recurrence) - ]) + ret["scheduled_actions"][action.name] = dict( + [ + ("min_size", action.min_size), + ("max_size", action.max_size), + # AWS bug + ("desired_capacity", int(action.desired_capacity)), + ("start_time", action.start_time.isoformat()), + ("end_time", end_time), + ("recurrence", action.recurrence), + ] + ) return ret except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue @@ -215,21 +239,38 @@ def get_config(name, region=None, key=None, keyid=None, profile=None): return {} -def create(name, launch_config_name, availability_zones, min_size, max_size, - desired_capacity=None, load_balancers=None, default_cooldown=None, - health_check_type=None, health_check_period=None, - placement_group=None, vpc_zone_identifier=None, tags=None, - termination_policies=None, suspended_processes=None, - scaling_policies=None, scheduled_actions=None, region=None, - notification_arn=None, notification_types=None, - key=None, keyid=None, profile=None): - ''' +def create( + name, + launch_config_name, + availability_zones, + min_size, + max_size, + desired_capacity=None, + load_balancers=None, + default_cooldown=None, + health_check_type=None, + health_check_period=None, + placement_group=None, + vpc_zone_identifier=None, + tags=None, + termination_policies=None, + suspended_processes=None, + scaling_policies=None, + scheduled_actions=None, + region=None, + notification_arn=None, + notification_types=None, + key=None, + keyid=None, + profile=None, +): + """ Create an autoscale group. CLI example:: salt myminion boto_asg.create myasg mylc '["us-east-1a", "us-east-1e"]' 1 10 load_balancers='["myelb", "myelb2"]' tags='[{"key": "Name", value="myasg", "propagate_at_launch": True}]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(availability_zones, six.string_types): availability_zones = salt.utils.json.loads(availability_zones) @@ -244,18 +285,22 @@ def create(name, launch_config_name, availability_zones, min_size, max_size, if tags: for tag in tags: try: - key = tag.get('key') + key = tag.get("key") except KeyError: - log.error('Tag missing key.') + log.error("Tag missing key.") return False try: - value = tag.get('value') + value = tag.get("value") except KeyError: - log.error('Tag missing value.') + log.error("Tag missing value.") return False - propagate_at_launch = tag.get('propagate_at_launch', False) - _tag = autoscale.Tag(key=key, value=value, resource_id=name, - propagate_at_launch=propagate_at_launch) + propagate_at_launch = tag.get("propagate_at_launch", False) + _tag = autoscale.Tag( + key=key, + value=value, + resource_id=name, + propagate_at_launch=propagate_at_launch, + ) _tags.append(_tag) if isinstance(termination_policies, six.string_types): termination_policies = salt.utils.json.loads(termination_policies) @@ -267,17 +312,22 @@ def create(name, launch_config_name, availability_zones, min_size, max_size, while True: try: _asg = autoscale.AutoScalingGroup( - name=name, launch_config=launch_config_name, + name=name, + launch_config=launch_config_name, availability_zones=availability_zones, - min_size=min_size, max_size=max_size, - desired_capacity=desired_capacity, load_balancers=load_balancers, + min_size=min_size, + max_size=max_size, + desired_capacity=desired_capacity, + load_balancers=load_balancers, default_cooldown=default_cooldown, health_check_type=health_check_type, health_check_period=health_check_period, - placement_group=placement_group, tags=_tags, + placement_group=placement_group, + tags=_tags, vpc_zone_identifier=vpc_zone_identifier, termination_policies=termination_policies, - suspended_processes=suspended_processes) + suspended_processes=suspended_processes, + ) conn.create_auto_scaling_group(_asg) # create scaling policies _create_scaling_policies(conn, name, scaling_policies) @@ -285,40 +335,60 @@ def create(name, launch_config_name, availability_zones, min_size, max_size, _create_scheduled_actions(conn, name, scheduled_actions) # create notifications if notification_arn and notification_types: - conn.put_notification_configuration(_asg, notification_arn, notification_types) - log.info('Created ASG %s', name) + conn.put_notification_configuration( + _asg, notification_arn, notification_types + ) + log.info("Created ASG %s", name) return True except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue log.error(e) - msg = 'Failed to create ASG %s', name + msg = "Failed to create ASG %s", name log.error(msg) return False -def update(name, launch_config_name, availability_zones, min_size, max_size, - desired_capacity=None, load_balancers=None, default_cooldown=None, - health_check_type=None, health_check_period=None, - placement_group=None, vpc_zone_identifier=None, tags=None, - termination_policies=None, suspended_processes=None, - scaling_policies=None, scheduled_actions=None, - notification_arn=None, notification_types=None, - region=None, key=None, keyid=None, profile=None): - ''' +def update( + name, + launch_config_name, + availability_zones, + min_size, + max_size, + desired_capacity=None, + load_balancers=None, + default_cooldown=None, + health_check_type=None, + health_check_period=None, + placement_group=None, + vpc_zone_identifier=None, + tags=None, + termination_policies=None, + suspended_processes=None, + scaling_policies=None, + scheduled_actions=None, + notification_arn=None, + notification_types=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Update an autoscale group. CLI example:: salt myminion boto_asg.update myasg mylc '["us-east-1a", "us-east-1e"]' 1 10 load_balancers='["myelb", "myelb2"]' tags='[{"key": "Name", value="myasg", "propagate_at_launch": True}]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn3 = _get_conn_autoscaling_boto3(region=region, key=key, keyid=keyid, - profile=profile) + conn3 = _get_conn_autoscaling_boto3( + region=region, key=key, keyid=keyid, profile=profile + ) if not conn: return False, "failed to connect to AWS" if isinstance(availability_zones, six.string_types): @@ -338,33 +408,40 @@ def update(name, launch_config_name, availability_zones, min_size, max_size, # Massage our tagset into add / remove lists # Use a boto3 call here b/c the boto2 call doeesn't implement filters - current_tags = conn3.describe_tags(Filters=[{'Name': 'auto-scaling-group', - 'Values': [name]}]).get('Tags', []) - current_tags = [{'key': t['Key'], - 'value': t['Value'], - 'resource_id': t['ResourceId'], - 'propagate_at_launch': t.get('PropagateAtLaunch', False)} - for t in current_tags] + current_tags = conn3.describe_tags( + Filters=[{"Name": "auto-scaling-group", "Values": [name]}] + ).get("Tags", []) + current_tags = [ + { + "key": t["Key"], + "value": t["Value"], + "resource_id": t["ResourceId"], + "propagate_at_launch": t.get("PropagateAtLaunch", False), + } + for t in current_tags + ] add_tags = [] desired_tags = [] if tags: - tags = __utils__['boto3.ordered'](tags) + tags = __utils__["boto3.ordered"](tags) for tag in tags: try: - key = tag.get('key') + key = tag.get("key") except KeyError: - log.error('Tag missing key.') + log.error("Tag missing key.") return False, "Tag {0} missing key".format(tag) try: - value = tag.get('value') + value = tag.get("value") except KeyError: - log.error('Tag missing value.') + log.error("Tag missing value.") return False, "Tag {0} missing value".format(tag) - propagate_at_launch = tag.get('propagate_at_launch', False) - _tag = {'key': key, - 'value': value, - 'resource_id': name, - 'propagate_at_launch': propagate_at_launch} + propagate_at_launch = tag.get("propagate_at_launch", False) + _tag = { + "key": key, + "value": value, + "resource_id": name, + "propagate_at_launch": propagate_at_launch, + } if _tag not in current_tags: add_tags.append(_tag) desired_tags.append(_tag) @@ -375,26 +452,33 @@ def update(name, launch_config_name, availability_zones, min_size, max_size, try: _asg = autoscale.AutoScalingGroup( connection=conn, - name=name, launch_config=launch_config_name, + name=name, + launch_config=launch_config_name, availability_zones=availability_zones, - min_size=min_size, max_size=max_size, - desired_capacity=desired_capacity, load_balancers=load_balancers, + min_size=min_size, + max_size=max_size, + desired_capacity=desired_capacity, + load_balancers=load_balancers, default_cooldown=default_cooldown, health_check_type=health_check_type, health_check_period=health_check_period, - placement_group=placement_group, tags=add_tags, + placement_group=placement_group, + tags=add_tags, vpc_zone_identifier=vpc_zone_identifier, - termination_policies=termination_policies) + termination_policies=termination_policies, + ) if notification_arn and notification_types: - conn.put_notification_configuration(_asg, notification_arn, notification_types) + conn.put_notification_configuration( + _asg, notification_arn, notification_types + ) _asg.update() # Seems the update call doesn't handle tags, so we'll need to update # that separately. if add_tags: - log.debug('Adding/updating tags from ASG: %s', add_tags) + log.debug("Adding/updating tags from ASG: %s", add_tags) conn.create_or_update_tags([autoscale.Tag(**t) for t in add_tags]) if delete_tags: - log.debug('Deleting tags from ASG: %s', delete_tags) + log.debug("Deleting tags from ASG: %s", delete_tags) conn.delete_tags([autoscale.Tag(**t) for t in delete_tags]) # update doesn't handle suspended_processes either # Resume all processes @@ -403,7 +487,7 @@ def update(name, launch_config_name, availability_zones, min_size, max_size, # list suspends all; don't do that. if suspended_processes is not None and len(suspended_processes) > 0: _asg.suspend_processes(suspended_processes) - log.info('Updated ASG %s', name) + log.info("Updated ASG %s", name) # ### scaling policies # delete all policies, then recreate them for policy in conn.get_all_policies(as_group=name): @@ -416,21 +500,21 @@ def update(name, launch_config_name, availability_zones, min_size, max_size, scheduled_action.name, autoscale_group=name ) _create_scheduled_actions(conn, name, scheduled_actions) - return True, '' + return True, "" except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue log.error(e) - msg = 'Failed to update ASG {0}'.format(name) + msg = "Failed to update ASG {0}".format(name) log.error(msg) return False, six.text_type(e) def _create_scaling_policies(conn, as_name, scaling_policies): - 'helper function to create scaling policies' + "helper function to create scaling policies" if scaling_policies: for policy in scaling_policies: policy = autoscale.policy.ScalingPolicy( @@ -439,64 +523,71 @@ def _create_scaling_policies(conn, as_name, scaling_policies): adjustment_type=policy["adjustment_type"], scaling_adjustment=policy["scaling_adjustment"], min_adjustment_step=policy.get("min_adjustment_step", None), - cooldown=policy["cooldown"]) + cooldown=policy["cooldown"], + ) conn.create_scaling_policy(policy) def _create_scheduled_actions(conn, as_name, scheduled_actions): - ''' + """ Helper function to create scheduled actions - ''' + """ if scheduled_actions: for name, action in six.iteritems(scheduled_actions): - if 'start_time' in action and isinstance(action['start_time'], six.string_types): - action['start_time'] = datetime.datetime.strptime( - action['start_time'], DATE_FORMAT + if "start_time" in action and isinstance( + action["start_time"], six.string_types + ): + action["start_time"] = datetime.datetime.strptime( + action["start_time"], DATE_FORMAT ) - if 'end_time' in action and isinstance(action['end_time'], six.string_types): - action['end_time'] = datetime.datetime.strptime( - action['end_time'], DATE_FORMAT + if "end_time" in action and isinstance( + action["end_time"], six.string_types + ): + action["end_time"] = datetime.datetime.strptime( + action["end_time"], DATE_FORMAT ) - conn.create_scheduled_group_action(as_name, name, - desired_capacity=action.get('desired_capacity'), - min_size=action.get('min_size'), - max_size=action.get('max_size'), - start_time=action.get('start_time'), - end_time=action.get('end_time'), - recurrence=action.get('recurrence') + conn.create_scheduled_group_action( + as_name, + name, + desired_capacity=action.get("desired_capacity"), + min_size=action.get("min_size"), + max_size=action.get("max_size"), + start_time=action.get("start_time"), + end_time=action.get("end_time"), + recurrence=action.get("recurrence"), ) def delete(name, force=False, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete an autoscale group. CLI example:: salt myminion boto_asg.delete myasg region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 while True: try: conn.delete_auto_scaling_group(name, force) - msg = 'Deleted autoscale group {0}.'.format(name) + msg = "Deleted autoscale group {0}.".format(name) log.info(msg) return True except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue log.error(e) - msg = 'Failed to delete autoscale group {0}'.format(name) + msg = "Failed to delete autoscale group {0}".format(name) log.error(msg) return False def get_cloud_init_mime(cloud_init): - ''' + """ Get a mime multipart encoded string from a cloud-init dict. Currently supports boothooks, scripts and cloud-config. @@ -505,36 +596,36 @@ def get_cloud_init_mime(cloud_init): .. code-block:: bash salt myminion boto.get_cloud_init_mime - ''' + """ if isinstance(cloud_init, six.string_types): cloud_init = salt.utils.json.loads(cloud_init) _cloud_init = email.mime.multipart.MIMEMultipart() - if 'boothooks' in cloud_init: - for script_name, script in six.iteritems(cloud_init['boothooks']): - _script = email.mime.text.MIMEText(script, 'cloud-boothook') + if "boothooks" in cloud_init: + for script_name, script in six.iteritems(cloud_init["boothooks"]): + _script = email.mime.text.MIMEText(script, "cloud-boothook") _cloud_init.attach(_script) - if 'scripts' in cloud_init: - for script_name, script in six.iteritems(cloud_init['scripts']): - _script = email.mime.text.MIMEText(script, 'x-shellscript') + if "scripts" in cloud_init: + for script_name, script in six.iteritems(cloud_init["scripts"]): + _script = email.mime.text.MIMEText(script, "x-shellscript") _cloud_init.attach(_script) - if 'cloud-config' in cloud_init: - cloud_config = cloud_init['cloud-config'] + if "cloud-config" in cloud_init: + cloud_config = cloud_init["cloud-config"] _cloud_config = email.mime.text.MIMEText( salt.utils.yaml.safe_dump(cloud_config, default_flow_style=False), - 'cloud-config') + "cloud-config", + ) _cloud_init.attach(_cloud_config) return _cloud_init.as_string() -def launch_configuration_exists(name, region=None, key=None, keyid=None, - profile=None): - ''' +def launch_configuration_exists(name, region=None, key=None, keyid=None, profile=None): + """ Check for a launch configuration's existence. CLI example:: salt myminion boto_asg.launch_configuration_exists mylc - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 while True: @@ -543,12 +634,14 @@ def launch_configuration_exists(name, region=None, key=None, keyid=None, if lc: return True else: - msg = 'The launch configuration does not exist in region {0}'.format(region) + msg = "The launch configuration does not exist in region {0}".format( + region + ) log.debug(msg) return False except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue @@ -556,23 +649,22 @@ def launch_configuration_exists(name, region=None, key=None, keyid=None, return False -def get_all_launch_configurations(region=None, key=None, keyid=None, - profile=None): - ''' +def get_all_launch_configurations(region=None, key=None, keyid=None, profile=None): + """ Fetch and return all Launch Configuration with details. CLI example:: salt myminion boto_asg.get_all_launch_configurations - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 while True: try: return conn.get_all_launch_configurations() except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue @@ -580,28 +672,28 @@ def get_all_launch_configurations(region=None, key=None, keyid=None, return [] -def list_launch_configurations(region=None, key=None, keyid=None, - profile=None): - ''' +def list_launch_configurations(region=None, key=None, keyid=None, profile=None): + """ List all Launch Configurations. CLI example:: salt myminion boto_asg.list_launch_configurations - ''' + """ ret = get_all_launch_configurations(region, key, keyid, profile) return [r.name for r in ret] -def describe_launch_configuration(name, region=None, key=None, keyid=None, - profile=None): - ''' +def describe_launch_configuration( + name, region=None, key=None, keyid=None, profile=None +): + """ Dump details of a given launch configuration. CLI example:: salt myminion boto_asg.describe_launch_configuration mylc - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 while True: @@ -610,12 +702,14 @@ def describe_launch_configuration(name, region=None, key=None, keyid=None, if lc: return lc[0] else: - msg = 'The launch configuration does not exist in region {0}'.format(region) + msg = "The launch configuration does not exist in region {0}".format( + region + ) log.debug(msg) return None except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue @@ -623,26 +717,39 @@ def describe_launch_configuration(name, region=None, key=None, keyid=None, return None -def create_launch_configuration(name, image_id, key_name=None, - vpc_id=None, vpc_name=None, - security_groups=None, user_data=None, - instance_type='m1.small', kernel_id=None, - ramdisk_id=None, block_device_mappings=None, - instance_monitoring=False, spot_price=None, - instance_profile_name=None, - ebs_optimized=False, - associate_public_ip_address=None, - volume_type=None, delete_on_termination=True, - iops=None, use_block_device_types=False, - region=None, key=None, keyid=None, - profile=None): - ''' +def create_launch_configuration( + name, + image_id, + key_name=None, + vpc_id=None, + vpc_name=None, + security_groups=None, + user_data=None, + instance_type="m1.small", + kernel_id=None, + ramdisk_id=None, + block_device_mappings=None, + instance_monitoring=False, + spot_price=None, + instance_profile_name=None, + ebs_optimized=False, + associate_public_ip_address=None, + volume_type=None, + delete_on_termination=True, + iops=None, + use_block_device_types=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a launch configuration. CLI example:: salt myminion boto_asg.create_launch_configuration mylc image_id=ami-0b9c9f62 key_name='mykey' security_groups='["mygroup"]' instance_type='c3.2xlarge' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(security_groups, six.string_types): security_groups = salt.utils.json.loads(security_groups) @@ -664,72 +771,84 @@ def create_launch_configuration(name, image_id, key_name=None, # within the default VPC. If a security group id is already part of the list, # convert_to_group_ids leaves that entry without attempting a lookup on it. if security_groups and (vpc_id or vpc_name): - security_groups = __salt__['boto_secgroup.convert_to_group_ids']( - security_groups, - vpc_id=vpc_id, vpc_name=vpc_name, - region=region, key=key, keyid=keyid, - profile=profile - ) + security_groups = __salt__["boto_secgroup.convert_to_group_ids"]( + security_groups, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) lc = autoscale.LaunchConfiguration( - name=name, image_id=image_id, key_name=key_name, - security_groups=security_groups, user_data=user_data, - instance_type=instance_type, kernel_id=kernel_id, - ramdisk_id=ramdisk_id, block_device_mappings=_bdms, - instance_monitoring=instance_monitoring, spot_price=spot_price, + name=name, + image_id=image_id, + key_name=key_name, + security_groups=security_groups, + user_data=user_data, + instance_type=instance_type, + kernel_id=kernel_id, + ramdisk_id=ramdisk_id, + block_device_mappings=_bdms, + instance_monitoring=instance_monitoring, + spot_price=spot_price, instance_profile_name=instance_profile_name, ebs_optimized=ebs_optimized, associate_public_ip_address=associate_public_ip_address, - volume_type=volume_type, delete_on_termination=delete_on_termination, - iops=iops, use_block_device_types=use_block_device_types) + volume_type=volume_type, + delete_on_termination=delete_on_termination, + iops=iops, + use_block_device_types=use_block_device_types, + ) retries = 30 while True: try: conn.create_launch_configuration(lc) - log.info('Created LC %s', name) + log.info("Created LC %s", name) return True except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue log.error(e) - msg = 'Failed to create LC {0}'.format(name) + msg = "Failed to create LC {0}".format(name) log.error(msg) return False -def delete_launch_configuration(name, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_launch_configuration(name, region=None, key=None, keyid=None, profile=None): + """ Delete a launch configuration. CLI example:: salt myminion boto_asg.delete_launch_configuration mylc - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 while True: try: conn.delete_launch_configuration(name) - log.info('Deleted LC %s', name) + log.info("Deleted LC %s", name) return True except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue log.error(e) - msg = 'Failed to delete LC {0}'.format(name) + msg = "Failed to delete LC {0}".format(name) log.error(msg) return False -def get_scaling_policy_arn(as_group, scaling_policy_name, region=None, - key=None, keyid=None, profile=None): - ''' +def get_scaling_policy_arn( + as_group, scaling_policy_name, region=None, key=None, keyid=None, profile=None +): + """ Return the arn for a scaling policy in a specific autoscale group or None if not found. Mainly used as a helper method for boto_cloudwatch_alarm, for linking alarms to scaling policies. @@ -737,7 +856,7 @@ def get_scaling_policy_arn(as_group, scaling_policy_name, region=None, CLI Example:: salt '*' boto_asg.get_scaling_policy_arn mygroup mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 while retries > 0: @@ -747,20 +866,20 @@ def get_scaling_policy_arn(as_group, scaling_policy_name, region=None, for policy in policies: if policy.name == scaling_policy_name: return policy.policy_arn - log.error('Could not convert: %s', as_group) + log.error("Could not convert: %s", as_group) return None except boto.exception.BotoServerError as e: - if e.error_code != 'Throttling': + if e.error_code != "Throttling": raise - log.debug('Throttled by API, will retry in 5 seconds') + log.debug("Throttled by API, will retry in 5 seconds") time.sleep(5) - log.error('Maximum number of retries exceeded') + log.error("Maximum number of retries exceeded") return None def get_all_groups(region=None, key=None, keyid=None, profile=None): - ''' + """ Return all AutoScale Groups visible in the account (as a list of boto.ec2.autoscale.group.AutoScalingGroup). @@ -772,12 +891,12 @@ def get_all_groups(region=None, key=None, keyid=None, profile=None): salt-call boto_asg.get_all_groups region=us-east-1 --output yaml - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 while True: try: - next_token = '' + next_token = "" asgs = [] while next_token is not None: ret = conn.get_all_groups(next_token=next_token) @@ -785,8 +904,8 @@ def get_all_groups(region=None, key=None, keyid=None, profile=None): next_token = ret.next_token return asgs except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue @@ -795,7 +914,7 @@ def get_all_groups(region=None, key=None, keyid=None, profile=None): def list_groups(region=None, key=None, keyid=None, profile=None): - ''' + """ Return all AutoScale Groups visible in the account (as a list of names). @@ -807,21 +926,32 @@ def list_groups(region=None, key=None, keyid=None, profile=None): salt-call boto_asg.list_groups region=us-east-1 - ''' - return [a.name for a in get_all_groups(region=region, key=key, keyid=keyid, profile=profile)] + """ + return [ + a.name + for a in get_all_groups(region=region, key=key, keyid=keyid, profile=profile) + ] -def get_instances(name, lifecycle_state="InService", health_status="Healthy", - attribute="private_ip_address", attributes=None, region=None, - key=None, keyid=None, profile=None): - ''' +def get_instances( + name, + lifecycle_state="InService", + health_status="Healthy", + attribute="private_ip_address", + attributes=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ return attribute of all instances in the named autoscale group. CLI example:: salt-call boto_asg.get_instances my_autoscale_group_name - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) ec2_conn = _get_ec2_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 @@ -830,15 +960,17 @@ def get_instances(name, lifecycle_state="InService", health_status="Healthy", asgs = conn.get_all_groups(names=[name]) break except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, retrying in 5 seconds...') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, retrying in 5 seconds...") time.sleep(5) retries -= 1 continue log.error(e) return False if len(asgs) != 1: - log.debug("name '%s' returns multiple ASGs: %s", name, [asg.name for asg in asgs]) + log.debug( + "name '%s' returns multiple ASGs: %s", name, [asg.name for asg in asgs] + ) return False asg = asgs[0] instance_ids = [] @@ -852,23 +984,40 @@ def get_instances(name, lifecycle_state="InService", health_status="Healthy", # get full instance info, so that we can return the attribute instances = ec2_conn.get_only_instances(instance_ids=instance_ids) if attributes: - return [[_convert_attribute(instance, attr) for attr in attributes] for instance in instances] + return [ + [_convert_attribute(instance, attr) for attr in attributes] + for instance in instances + ] else: # properly handle case when not all instances have the requested attribute - return [_convert_attribute(instance, attribute) for instance in instances if getattr(instance, attribute)] + return [ + _convert_attribute(instance, attribute) + for instance in instances + if getattr(instance, attribute) + ] def _convert_attribute(instance, attribute): if attribute == "tags": tags = dict(getattr(instance, attribute)) - return {key.encode("utf-8"): value.encode("utf-8") for key, value in six.iteritems(tags)} + return { + key.encode("utf-8"): value.encode("utf-8") + for key, value in six.iteritems(tags) + } return getattr(instance, attribute).encode("ascii") -def enter_standby(name, instance_ids, should_decrement_desired_capacity=False, - region=None, key=None, keyid=None, profile=None): - ''' +def enter_standby( + name, + instance_ids, + should_decrement_desired_capacity=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Switch desired instances to StandBy mode .. versionadded:: 2016.11.0 @@ -877,24 +1026,36 @@ def enter_standby(name, instance_ids, should_decrement_desired_capacity=False, salt-call boto_asg.enter_standby my_autoscale_group_name '["i-xxxxxx"]' - ''' - conn = _get_conn_autoscaling_boto3(region=region, key=key, keyid=keyid, profile=profile) + """ + conn = _get_conn_autoscaling_boto3( + region=region, key=key, keyid=keyid, profile=profile + ) try: response = conn.enter_standby( InstanceIds=instance_ids, AutoScalingGroupName=name, - ShouldDecrementDesiredCapacity=should_decrement_desired_capacity) + ShouldDecrementDesiredCapacity=should_decrement_desired_capacity, + ) except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'exists': False} - return {'error': err} - return all(activity['StatusCode'] != 'Failed' for activity in response['Activities']) + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"exists": False} + return {"error": err} + return all( + activity["StatusCode"] != "Failed" for activity in response["Activities"] + ) -def exit_standby(name, instance_ids, should_decrement_desired_capacity=False, - region=None, key=None, keyid=None, profile=None): - ''' +def exit_standby( + name, + instance_ids, + should_decrement_desired_capacity=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Exit desired instances from StandBy mode .. versionadded:: 2016.11.0 @@ -903,16 +1064,19 @@ def exit_standby(name, instance_ids, should_decrement_desired_capacity=False, salt-call boto_asg.exit_standby my_autoscale_group_name '["i-xxxxxx"]' - ''' + """ conn = _get_conn_autoscaling_boto3( - region=region, key=key, keyid=keyid, profile=profile) + region=region, key=key, keyid=keyid, profile=profile + ) try: response = conn.exit_standby( - InstanceIds=instance_ids, - AutoScalingGroupName=name) + InstanceIds=instance_ids, AutoScalingGroupName=name + ) except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'exists': False} - return {'error': err} - return all(activity['StatusCode'] != 'Failed' for activity in response['Activities']) + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"exists": False} + return {"error": err} + return all( + activity["StatusCode"] != "Failed" for activity in response["Activities"] + ) diff --git a/salt/modules/boto_cfn.py b/salt/modules/boto_cfn.py index bb7bfcc33b9..cfe9ebc167d 100644 --- a/salt/modules/boto_cfn.py +++ b/salt/modules/boto_cfn.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Cloud Formation .. versionadded:: 2015.5.0 @@ -28,48 +28,54 @@ Connection module for Amazon Cloud Formation cfn.region: us-east-1 :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.versions + # Import Salt libs from salt.ext import six -import salt.utils.versions log = logging.getLogger(__name__) # Import third party libs # pylint: disable=import-error try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import boto.cloudformation - #pylint: enable=unused-import + + # pylint: enable=unused-import from boto.exception import BotoServerError - logging.getLogger('boto').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' + """ return salt.utils.versions.check_boto_reqs(check_boto3=False) def __init__(opts): if HAS_BOTO: - __utils__['boto.assign_funcs'](__name__, 'cfn', module='cloudformation', pack=__salt__) + __utils__["boto.assign_funcs"]( + __name__, "cfn", module="cloudformation", pack=__salt__ + ) def exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if a stack exists. CLI Example: @@ -77,21 +83,21 @@ def exists(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_cfn.exists mystack region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: # Returns an object if stack exists else an exception exists = conn.describe_stacks(name) - log.debug('Stack %s exists.', name) + log.debug("Stack %s exists.", name) return True except BotoServerError as e: - log.debug('boto_cfn.exists raised an exception', exc_info=True) + log.debug("boto_cfn.exists raised an exception", exc_info=True) return False def describe(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Describe a stack. .. versionadded:: 2015.8.0 @@ -101,7 +107,7 @@ def describe(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_cfn.describe mystack region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -109,34 +115,55 @@ def describe(name, region=None, key=None, keyid=None, profile=None): r = conn.describe_stacks(name) if r: stack = r[0] - log.debug('Found VPC: %s', stack.stack_id) - keys = ('stack_id', 'description', 'stack_status', 'stack_status_reason', 'tags') + log.debug("Found VPC: %s", stack.stack_id) + keys = ( + "stack_id", + "description", + "stack_status", + "stack_status_reason", + "tags", + ) ret = dict([(k, getattr(stack, k)) for k in keys if hasattr(stack, k)]) - o = getattr(stack, 'outputs') - p = getattr(stack, 'parameters') + o = getattr(stack, "outputs") + p = getattr(stack, "parameters") outputs = {} parameters = {} for i in o: outputs[i.key] = i.value - ret['outputs'] = outputs + ret["outputs"] = outputs for j in p: parameters[j.key] = j.value - ret['parameters'] = parameters + ret["parameters"] = parameters - return {'stack': ret} + return {"stack": ret} - log.debug('Stack %s exists.', name) + log.debug("Stack %s exists.", name) return True except BotoServerError as e: - log.warning('Could not describe stack %s.\n%s', name, e) + log.warning("Could not describe stack %s.\n%s", name, e) return False -def create(name, template_body=None, template_url=None, parameters=None, notification_arns=None, disable_rollback=None, - timeout_in_minutes=None, capabilities=None, tags=None, on_failure=None, stack_policy_body=None, - stack_policy_url=None, region=None, key=None, keyid=None, profile=None): - ''' +def create( + name, + template_body=None, + template_url=None, + parameters=None, + notification_arns=None, + disable_rollback=None, + timeout_in_minutes=None, + capabilities=None, + tags=None, + on_failure=None, + stack_policy_body=None, + stack_policy_url=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a CFN stack. CLI Example: @@ -145,24 +172,52 @@ def create(name, template_body=None, template_url=None, parameters=None, notific salt myminion boto_cfn.create mystack template_url='https://s3.amazonaws.com/bucket/template.cft' \ region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - return conn.create_stack(name, template_body, template_url, parameters, notification_arns, disable_rollback, - timeout_in_minutes, capabilities, tags, on_failure, stack_policy_body, stack_policy_url) + return conn.create_stack( + name, + template_body, + template_url, + parameters, + notification_arns, + disable_rollback, + timeout_in_minutes, + capabilities, + tags, + on_failure, + stack_policy_body, + stack_policy_url, + ) except BotoServerError as e: - msg = 'Failed to create stack {0}.\n{1}'.format(name, e) + msg = "Failed to create stack {0}.\n{1}".format(name, e) log.error(msg) log.debug(e) return False -def update_stack(name, template_body=None, template_url=None, parameters=None, notification_arns=None, - disable_rollback=False, timeout_in_minutes=None, capabilities=None, tags=None, - use_previous_template=None, stack_policy_during_update_body=None, stack_policy_during_update_url=None, - stack_policy_body=None, stack_policy_url=None, region=None, key=None, keyid=None, profile=None): - ''' +def update_stack( + name, + template_body=None, + template_url=None, + parameters=None, + notification_arns=None, + disable_rollback=False, + timeout_in_minutes=None, + capabilities=None, + tags=None, + use_previous_template=None, + stack_policy_during_update_body=None, + stack_policy_during_update_url=None, + stack_policy_body=None, + stack_policy_url=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Update a CFN stack. .. versionadded:: 2015.8.0 @@ -173,25 +228,37 @@ def update_stack(name, template_body=None, template_url=None, parameters=None, n salt myminion boto_cfn.update_stack mystack template_url='https://s3.amazonaws.com/bucket/template.cft' \ region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - update = conn.update_stack(name, template_body, template_url, parameters, notification_arns, - disable_rollback, timeout_in_minutes, capabilities, tags, use_previous_template, - stack_policy_during_update_body, stack_policy_during_update_url, - stack_policy_body, stack_policy_url) - log.debug('Updated result is : %s.', update) + update = conn.update_stack( + name, + template_body, + template_url, + parameters, + notification_arns, + disable_rollback, + timeout_in_minutes, + capabilities, + tags, + use_previous_template, + stack_policy_during_update_body, + stack_policy_during_update_url, + stack_policy_body, + stack_policy_url, + ) + log.debug("Updated result is : %s.", update) return update except BotoServerError as e: - msg = 'Failed to update stack {0}.'.format(name) + msg = "Failed to update stack {0}.".format(name) log.debug(e) log.error(msg) return six.text_type(e) def delete(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete a CFN stack. CLI Example: @@ -199,20 +266,20 @@ def delete(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_cfn.delete mystack region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: return conn.delete_stack(name) except BotoServerError as e: - msg = 'Failed to create stack {0}.'.format(name) + msg = "Failed to create stack {0}.".format(name) log.error(msg) log.debug(e) return six.text_type(e) def get_template(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if attributes are set on a CFN stack. CLI Example: @@ -220,22 +287,29 @@ def get_template(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_cfn.get_template mystack - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: template = conn.get_template(name) - log.info('Retrieved template for stack %s', name) + log.info("Retrieved template for stack %s", name) return template except BotoServerError as e: log.debug(e) - msg = 'Template {0} does not exist'.format(name) + msg = "Template {0} does not exist".format(name) log.error(msg) return six.text_type(e) -def validate_template(template_body=None, template_url=None, region=None, key=None, keyid=None, profile=None): - ''' +def validate_template( + template_body=None, + template_url=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Validate cloudformation template .. versionadded:: 2015.8.0 @@ -245,7 +319,7 @@ def validate_template(template_body=None, template_url=None, region=None, key=No .. code-block:: bash salt myminion boto_cfn.validate_template mystack-template - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -253,6 +327,6 @@ def validate_template(template_body=None, template_url=None, region=None, key=No return conn.validate_template(template_body, template_url) except BotoServerError as e: log.debug(e) - msg = 'Error while trying to validate template {0}.'.format(template_body) + msg = "Error while trying to validate template {0}.".format(template_body) log.error(msg) return six.text_type(e) diff --git a/salt/modules/boto_cloudfront.py b/salt/modules/boto_cloudfront.py index ea04e8d6b0b..1469cae3801 100644 --- a/salt/modules/boto_cloudfront.py +++ b/salt/modules/boto_cloudfront.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon CloudFront .. versionadded:: 2018.3.0 @@ -46,26 +46,28 @@ Connection module for Amazon CloudFront keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs region: us-east-1 -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.ext.six as six -from salt.utils.odict import OrderedDict import salt.utils.versions +from salt.utils.odict import OrderedDict # Import third party libs try: # pylint: disable=unused-import import boto3 import botocore + # pylint: enable=unused-import - logging.getLogger('boto3').setLevel(logging.CRITICAL) + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -74,46 +76,39 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto3 libraries exist. - ''' + """ has_boto_reqs = salt.utils.versions.check_boto_reqs() if has_boto_reqs is True: - __utils__['boto3.assign_funcs'](__name__, 'cloudfront') + __utils__["boto3.assign_funcs"](__name__, "cloudfront") return has_boto_reqs def _list_distributions( - conn, - name=None, - region=None, - key=None, - keyid=None, - profile=None, + conn, name=None, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Private function that returns an iterator over all CloudFront distributions. The caller is responsible for all boto-related error handling. name (Optional) Only yield the distribution with the given name - ''' - for dl_ in conn.get_paginator('list_distributions').paginate(): - distribution_list = dl_['DistributionList'] - if 'Items' not in distribution_list: + """ + for dl_ in conn.get_paginator("list_distributions").paginate(): + distribution_list = dl_["DistributionList"] + if "Items" not in distribution_list: # If there are no items, AWS omits the `Items` key for some reason continue - for partial_dist in distribution_list['Items']: - tags = conn.list_tags_for_resource(Resource=partial_dist['ARN']) - tags = dict( - (kv['Key'], kv['Value']) for kv in tags['Tags']['Items'] - ) + for partial_dist in distribution_list["Items"]: + tags = conn.list_tags_for_resource(Resource=partial_dist["ARN"]) + tags = dict((kv["Key"], kv["Value"]) for kv in tags["Tags"]["Items"]) - id_ = partial_dist['Id'] - if 'Name' not in tags: - log.warning('CloudFront distribution %s has no Name tag.', id_) + id_ = partial_dist["Id"] + if "Name" not in tags: + log.warning("CloudFront distribution %s has no Name tag.", id_) continue - distribution_name = tags.pop('Name', None) + distribution_name = tags.pop("Name", None) if name is not None and distribution_name != name: continue @@ -126,7 +121,7 @@ def _list_distributions( # Hence, we must call get_distribution() to get the full object, # and we cache these objects to help lessen API calls. distribution = _cache_id( - 'cloudfront', + "cloudfront", sub_resource=distribution_name, region=region, key=key, @@ -139,12 +134,12 @@ def _list_distributions( dist_with_etag = conn.get_distribution(Id=id_) distribution = { - 'distribution': dist_with_etag['Distribution'], - 'etag': dist_with_etag['ETag'], - 'tags': tags, + "distribution": dist_with_etag["Distribution"], + "etag": dist_with_etag["ETag"], + "tags": tags, } _cache_id( - 'cloudfront', + "cloudfront", sub_resource=distribution_name, resource_id=distribution, region=region, @@ -156,7 +151,7 @@ def _list_distributions( def get_distribution(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get information about a CloudFront distribution (configuration, tags) with a given name. name @@ -181,9 +176,9 @@ def get_distribution(name, region=None, key=None, keyid=None, profile=None): salt myminion boto_cloudfront.get_distribution name=mydistribution profile=awsprofile - ''' + """ distribution = _cache_id( - 'cloudfront', + "cloudfront", sub_resource=name, region=region, key=key, @@ -191,17 +186,12 @@ def get_distribution(name, region=None, key=None, keyid=None, profile=None): profile=profile, ) if distribution: - return {'result': distribution} + return {"result": distribution} conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: for _, dist in _list_distributions( - conn, - name=name, - region=region, - key=key, - keyid=keyid, - profile=profile, + conn, name=name, region=region, key=key, keyid=keyid, profile=profile, ): # _list_distributions should only return the one distribution # that we want (with the given name). @@ -210,16 +200,16 @@ def get_distribution(name, region=None, key=None, keyid=None, profile=None): # return the first one over and over again, # so only the first result is useful. if distribution is not None: - msg = 'More than one distribution found with name {0}' - return {'error': msg.format(name)} + msg = "More than one distribution found with name {0}" + return {"error": msg.format(name)} distribution = dist except botocore.exceptions.ClientError as err: - return {'error': __utils__['boto3.get_error'](err)} + return {"error": __utils__["boto3.get_error"](err)} if not distribution: - return {'result': None} + return {"result": None} _cache_id( - 'cloudfront', + "cloudfront", sub_resource=name, resource_id=distribution, region=region, @@ -227,11 +217,11 @@ def get_distribution(name, region=None, key=None, keyid=None, profile=None): keyid=keyid, profile=profile, ) - return {'result': distribution} + return {"result": distribution} def export_distributions(region=None, key=None, keyid=None, profile=None): - ''' + """ Get details of all CloudFront distributions. Produces results that can be used to create an SLS file. @@ -242,51 +232,37 @@ def export_distributions(region=None, key=None, keyid=None, profile=None): salt-call boto_cloudfront.export_distributions --out=txt |\ sed "s/local: //" > cloudfront_distributions.sls - ''' + """ results = OrderedDict() conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: for name, distribution in _list_distributions( - conn, - region=region, - key=key, - keyid=keyid, - profile=profile, + conn, region=region, key=key, keyid=keyid, profile=profile, ): - config = distribution['distribution']['DistributionConfig'] - tags = distribution['tags'] + config = distribution["distribution"]["DistributionConfig"] + tags = distribution["tags"] distribution_sls_data = [ - {'name': name}, - {'config': config}, - {'tags': tags}, + {"name": name}, + {"config": config}, + {"tags": tags}, ] - results['Manage CloudFront distribution {0}'.format(name)] = { - 'boto_cloudfront.present': distribution_sls_data, + results["Manage CloudFront distribution {0}".format(name)] = { + "boto_cloudfront.present": distribution_sls_data, } except botocore.exceptions.ClientError as err: # Raise an exception, as this is meant to be user-invoked at the CLI # as opposed to being called from execution or state modules six.reraise(*sys.exc_info()) - dumper = __utils__['yaml.get_dumper']('IndentedSafeOrderedDumper') - return __utils__['yaml.dump']( - results, - default_flow_style=False, - Dumper=dumper, - ) + dumper = __utils__["yaml.get_dumper"]("IndentedSafeOrderedDumper") + return __utils__["yaml.dump"](results, default_flow_style=False, Dumper=dumper,) def create_distribution( - name, - config, - tags=None, - region=None, - key=None, - keyid=None, - profile=None, + name, config, tags=None, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Create a CloudFront distribution with the given name, config, and (optionally) tags. name @@ -317,28 +293,23 @@ def create_distribution( salt myminion boto_cloudfront.create_distribution name=mydistribution profile=awsprofile \ config='{"Comment":"partial configuration","Enabled":true}' - ''' + """ if tags is None: tags = {} - if 'Name' in tags: + if "Name" in tags: # Be lenient and silently accept if names match, else error - if tags['Name'] != name: - return {'error': 'Must not pass `Name` in `tags` but as `name`'} - tags['Name'] = name - tags = { - 'Items': [{'Key': k, 'Value': v} for k, v in six.iteritems(tags)] - } + if tags["Name"] != name: + return {"error": "Must not pass `Name` in `tags` but as `name`"} + tags["Name"] = name + tags = {"Items": [{"Key": k, "Value": v} for k, v in six.iteritems(tags)]} conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.create_distribution_with_tags( - DistributionConfigWithTags={ - 'DistributionConfig': config, - 'Tags': tags, - }, + DistributionConfigWithTags={"DistributionConfig": config, "Tags": tags}, ) _cache_id( - 'cloudfront', + "cloudfront", sub_resource=name, invalidate=True, region=region, @@ -347,21 +318,15 @@ def create_distribution( profile=profile, ) except botocore.exceptions.ClientError as err: - return {'error': __utils__['boto3.get_error'](err)} + return {"error": __utils__["boto3.get_error"](err)} - return {'result': True} + return {"result": True} def update_distribution( - name, - config, - tags=None, - region=None, - key=None, - keyid=None, - profile=None, + name, config, tags=None, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Update the config (and optionally tags) for the CloudFront distribution with the given name. name @@ -392,61 +357,53 @@ def update_distribution( salt myminion boto_cloudfront.update_distribution name=mydistribution profile=awsprofile \ config='{"Comment":"partial configuration","Enabled":true}' - ''' + """ distribution_ret = get_distribution( - name, - region=region, - key=key, - keyid=keyid, - profile=profile + name, region=region, key=key, keyid=keyid, profile=profile ) - if 'error' in distribution_ret: + if "error" in distribution_ret: return distribution_ret - dist_with_tags = distribution_ret['result'] + dist_with_tags = distribution_ret["result"] - current_distribution = dist_with_tags['distribution'] - current_config = current_distribution['DistributionConfig'] - current_tags = dist_with_tags['tags'] - etag = dist_with_tags['etag'] + current_distribution = dist_with_tags["distribution"] + current_config = current_distribution["DistributionConfig"] + current_tags = dist_with_tags["tags"] + etag = dist_with_tags["etag"] - config_diff = __utils__['dictdiffer.deep_diff'](current_config, config) + config_diff = __utils__["dictdiffer.deep_diff"](current_config, config) if tags: - tags_diff = __utils__['dictdiffer.deep_diff'](current_tags, tags) + tags_diff = __utils__["dictdiffer.deep_diff"](current_tags, tags) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - if 'old' in config_diff or 'new' in config_diff: + if "old" in config_diff or "new" in config_diff: conn.update_distribution( - DistributionConfig=config, - Id=current_distribution['Id'], - IfMatch=etag, + DistributionConfig=config, Id=current_distribution["Id"], IfMatch=etag, ) if tags: - arn = current_distribution['ARN'] - if 'new' in tags_diff: + arn = current_distribution["ARN"] + if "new" in tags_diff: tags_to_add = { - 'Items': [ - {'Key': k, 'Value': v} - for k, v in six.iteritems(tags_diff['new']) + "Items": [ + {"Key": k, "Value": v} + for k, v in six.iteritems(tags_diff["new"]) ], } conn.tag_resource( - Resource=arn, - Tags=tags_to_add, + Resource=arn, Tags=tags_to_add, ) - if 'old' in tags_diff: + if "old" in tags_diff: tags_to_remove = { - 'Items': list(tags_diff['old'].keys()), + "Items": list(tags_diff["old"].keys()), } conn.untag_resource( - Resource=arn, - TagKeys=tags_to_remove, + Resource=arn, TagKeys=tags_to_remove, ) except botocore.exceptions.ClientError as err: - return {'error': __utils__['boto3.get_error'](err)} + return {"error": __utils__["boto3.get_error"](err)} finally: _cache_id( - 'cloudfront', + "cloudfront", sub_resource=name, invalidate=True, region=region, @@ -455,4 +412,4 @@ def update_distribution( profile=profile, ) - return {'result': True} + return {"result": True} diff --git a/salt/modules/boto_cloudtrail.py b/salt/modules/boto_cloudtrail.py index c7ac67d08d5..331f82dd534 100644 --- a/salt/modules/boto_cloudtrail.py +++ b/salt/modules/boto_cloudtrail.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon CloudTrail .. versionadded:: 2016.3.0 @@ -45,18 +45,20 @@ The dependencies listed above can be installed via package or pip. key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs region: us-east-1 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.compat +import salt.utils.versions + # Import Salt libs from salt.ext import six -import salt.utils.compat -import salt.utils.versions log = logging.getLogger(__name__) @@ -64,12 +66,14 @@ log = logging.getLogger(__name__) # pylint: disable=import-error try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import boto3 - #pylint: enable=unused-import + + # pylint: enable=unused-import from botocore.exceptions import ClientError - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -77,27 +81,24 @@ except ImportError: def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # the boto_lambda execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 - return salt.utils.versions.check_boto_reqs( - boto3_ver='1.2.5' - ) + return salt.utils.versions.check_boto_reqs(boto3_ver="1.2.5") def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 'cloudtrail') + __utils__["boto3.assign_funcs"](__name__, "cloudtrail") -def exists(Name, - region=None, key=None, keyid=None, profile=None): - ''' +def exists(Name, region=None, key=None, keyid=None, profile=None): + """ Given a trail name, check to see if the given trail exists. Returns True if the given trail exists and returns False if the given @@ -109,30 +110,36 @@ def exists(Name, salt myminion boto_cloudtrail.exists mytrail - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.get_trail_status(Name=Name) - return {'exists': True} + return {"exists": True} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'TrailNotFoundException': - return {'exists': False} - return {'error': err} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "TrailNotFoundException": + return {"exists": False} + return {"error": err} -def create(Name, - S3BucketName, S3KeyPrefix=None, - SnsTopicName=None, - IncludeGlobalServiceEvents=None, - IsMultiRegionTrail=None, - EnableLogFileValidation=None, - CloudWatchLogsLogGroupArn=None, - CloudWatchLogsRoleArn=None, - KmsKeyId=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create( + Name, + S3BucketName, + S3KeyPrefix=None, + SnsTopicName=None, + IncludeGlobalServiceEvents=None, + IsMultiRegionTrail=None, + EnableLogFileValidation=None, + CloudWatchLogsLogGroupArn=None, + CloudWatchLogsRoleArn=None, + KmsKeyId=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, create a trail. Returns {created: true} if the trail was created and returns @@ -144,34 +151,37 @@ def create(Name, salt myminion boto_cloudtrail.create my_trail my_bucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} - for arg in ('S3KeyPrefix', 'SnsTopicName', 'IncludeGlobalServiceEvents', - 'IsMultiRegionTrail', - 'EnableLogFileValidation', 'CloudWatchLogsLogGroupArn', - 'CloudWatchLogsRoleArn', 'KmsKeyId'): + for arg in ( + "S3KeyPrefix", + "SnsTopicName", + "IncludeGlobalServiceEvents", + "IsMultiRegionTrail", + "EnableLogFileValidation", + "CloudWatchLogsLogGroupArn", + "CloudWatchLogsRoleArn", + "KmsKeyId", + ): if locals()[arg] is not None: kwargs[arg] = locals()[arg] - trail = conn.create_trail(Name=Name, - S3BucketName=S3BucketName, - **kwargs) + trail = conn.create_trail(Name=Name, S3BucketName=S3BucketName, **kwargs) if trail: - log.info('The newly created trail name is %s', trail['Name']) + log.info("The newly created trail name is %s", trail["Name"]) - return {'created': True, 'name': trail['Name']} + return {"created": True, "name": trail["Name"]} else: - log.warning('Trail was not created') - return {'created': False} + log.warning("Trail was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete(Name, - region=None, key=None, keyid=None, profile=None): - ''' +def delete(Name, region=None, key=None, keyid=None, profile=None): + """ Given a trail name, delete it. Returns {deleted: true} if the trail was deleted and returns @@ -183,19 +193,18 @@ def delete(Name, salt myminion boto_cloudtrail.delete mytrail - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_trail(Name=Name) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def describe(Name, - region=None, key=None, keyid=None, profile=None): - ''' +def describe(Name, region=None, key=None, keyid=None, profile=None): + """ Given a trail name describe its properties. Returns a dictionary of interesting properties. @@ -206,32 +215,39 @@ def describe(Name, salt myminion boto_cloudtrail.describe mytrail - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) trails = conn.describe_trails(trailNameList=[Name]) - if trails and len(trails.get('trailList', [])) > 0: - keys = ('Name', 'S3BucketName', 'S3KeyPrefix', - 'SnsTopicName', 'IncludeGlobalServiceEvents', - 'IsMultiRegionTrail', - 'HomeRegion', 'TrailARN', - 'LogFileValidationEnabled', 'CloudWatchLogsLogGroupArn', - 'CloudWatchLogsRoleArn', 'KmsKeyId') - trail = trails['trailList'].pop() - return {'trail': dict([(k, trail.get(k)) for k in keys])} + if trails and len(trails.get("trailList", [])) > 0: + keys = ( + "Name", + "S3BucketName", + "S3KeyPrefix", + "SnsTopicName", + "IncludeGlobalServiceEvents", + "IsMultiRegionTrail", + "HomeRegion", + "TrailARN", + "LogFileValidationEnabled", + "CloudWatchLogsLogGroupArn", + "CloudWatchLogsRoleArn", + "KmsKeyId", + ) + trail = trails["trailList"].pop() + return {"trail": dict([(k, trail.get(k)) for k in keys])} else: - return {'trail': None} + return {"trail": None} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'TrailNotFoundException': - return {'trail': None} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "TrailNotFoundException": + return {"trail": None} + return {"error": __utils__["boto3.get_error"](e)} -def status(Name, - region=None, key=None, keyid=None, profile=None): - ''' +def status(Name, region=None, key=None, keyid=None, profile=None): + """ Given a trail name describe its properties. Returns a dictionary of interesting properties. @@ -242,36 +258,43 @@ def status(Name, salt myminion boto_cloudtrail.describe mytrail - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) trail = conn.get_trail_status(Name=Name) if trail: - keys = ('IsLogging', 'LatestDeliveryError', 'LatestNotificationError', - 'LatestDeliveryTime', 'LatestNotificationTime', - 'StartLoggingTime', 'StopLoggingTime', - 'LatestCloudWatchLogsDeliveryError', - 'LatestCloudWatchLogsDeliveryTime', - 'LatestDigestDeliveryTime', 'LatestDigestDeliveryError', - 'LatestDeliveryAttemptTime', - 'LatestNotificationAttemptTime', - 'LatestNotificationAttemptSucceeded', - 'LatestDeliveryAttemptSucceeded', - 'TimeLoggingStarted', - 'TimeLoggingStopped') - return {'trail': dict([(k, trail.get(k)) for k in keys])} + keys = ( + "IsLogging", + "LatestDeliveryError", + "LatestNotificationError", + "LatestDeliveryTime", + "LatestNotificationTime", + "StartLoggingTime", + "StopLoggingTime", + "LatestCloudWatchLogsDeliveryError", + "LatestCloudWatchLogsDeliveryTime", + "LatestDigestDeliveryTime", + "LatestDigestDeliveryError", + "LatestDeliveryAttemptTime", + "LatestNotificationAttemptTime", + "LatestNotificationAttemptSucceeded", + "LatestDeliveryAttemptSucceeded", + "TimeLoggingStarted", + "TimeLoggingStopped", + ) + return {"trail": dict([(k, trail.get(k)) for k in keys])} else: - return {'trail': None} + return {"trail": None} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'TrailNotFoundException': - return {'trail': None} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "TrailNotFoundException": + return {"trail": None} + return {"error": __utils__["boto3.get_error"](e)} def list(region=None, key=None, keyid=None, profile=None): - ''' + """ List all trails Returns list of trails @@ -284,28 +307,34 @@ def list(region=None, key=None, keyid=None, profile=None): - {...} - {...} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) trails = conn.describe_trails() - if not bool(trails.get('trailList')): - log.warning('No trails found') - return {'trails': trails.get('trailList', [])} + if not bool(trails.get("trailList")): + log.warning("No trails found") + return {"trails": trails.get("trailList", [])} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def update(Name, - S3BucketName, S3KeyPrefix=None, - SnsTopicName=None, - IncludeGlobalServiceEvents=None, - IsMultiRegionTrail=None, - EnableLogFileValidation=None, - CloudWatchLogsLogGroupArn=None, - CloudWatchLogsRoleArn=None, - KmsKeyId=None, - region=None, key=None, keyid=None, profile=None): - ''' +def update( + Name, + S3BucketName, + S3KeyPrefix=None, + SnsTopicName=None, + IncludeGlobalServiceEvents=None, + IsMultiRegionTrail=None, + EnableLogFileValidation=None, + CloudWatchLogsLogGroupArn=None, + CloudWatchLogsRoleArn=None, + KmsKeyId=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, update a trail. Returns {created: true} if the trail was created and returns @@ -317,34 +346,37 @@ def update(Name, salt myminion boto_cloudtrail.update my_trail my_bucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} - for arg in ('S3KeyPrefix', 'SnsTopicName', 'IncludeGlobalServiceEvents', - 'IsMultiRegionTrail', - 'EnableLogFileValidation', 'CloudWatchLogsLogGroupArn', - 'CloudWatchLogsRoleArn', 'KmsKeyId'): + for arg in ( + "S3KeyPrefix", + "SnsTopicName", + "IncludeGlobalServiceEvents", + "IsMultiRegionTrail", + "EnableLogFileValidation", + "CloudWatchLogsLogGroupArn", + "CloudWatchLogsRoleArn", + "KmsKeyId", + ): if locals()[arg] is not None: kwargs[arg] = locals()[arg] - trail = conn.update_trail(Name=Name, - S3BucketName=S3BucketName, - **kwargs) + trail = conn.update_trail(Name=Name, S3BucketName=S3BucketName, **kwargs) if trail: - log.info('The updated trail name is %s', trail['Name']) + log.info("The updated trail name is %s", trail["Name"]) - return {'updated': True, 'name': trail['Name']} + return {"updated": True, "name": trail["Name"]} else: - log.warning('Trail was not created') - return {'updated': False} + log.warning("Trail was not created") + return {"updated": False} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def start_logging(Name, - region=None, key=None, keyid=None, profile=None): - ''' +def start_logging(Name, region=None, key=None, keyid=None, profile=None): + """ Start logging for a trail Returns {started: true} if the trail was started and returns @@ -356,19 +388,18 @@ def start_logging(Name, salt myminion boto_cloudtrail.start_logging my_trail - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.start_logging(Name=Name) - return {'started': True} + return {"started": True} except ClientError as e: - return {'started': False, 'error': __utils__['boto3.get_error'](e)} + return {"started": False, "error": __utils__["boto3.get_error"](e)} -def stop_logging(Name, - region=None, key=None, keyid=None, profile=None): - ''' +def stop_logging(Name, region=None, key=None, keyid=None, profile=None): + """ Stop logging for a trail Returns {stopped: true} if the trail was stopped and returns @@ -380,33 +411,32 @@ def stop_logging(Name, salt myminion boto_cloudtrail.stop_logging my_trail - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.stop_logging(Name=Name) - return {'stopped': True} + return {"stopped": True} except ClientError as e: - return {'stopped': False, 'error': __utils__['boto3.get_error'](e)} + return {"stopped": False, "error": __utils__["boto3.get_error"](e)} def _get_trail_arn(name, region=None, key=None, keyid=None, profile=None): - if name.startswith('arn:aws:cloudtrail:'): + if name.startswith("arn:aws:cloudtrail:"): return name - account_id = __salt__['boto_iam.get_account_id']( + account_id = __salt__["boto_iam.get_account_id"]( region=region, key=key, keyid=keyid, profile=profile ) - if profile and 'region' in profile: - region = profile['region'] + if profile and "region" in profile: + region = profile["region"] if region is None: - region = 'us-east-1' - return 'arn:aws:cloudtrail:{0}:{1}:trail/{2}'.format(region, account_id, name) + region = "us-east-1" + return "arn:aws:cloudtrail:{0}:{1}:trail/{2}".format(region, account_id, name) -def add_tags(Name, - region=None, key=None, keyid=None, profile=None, **kwargs): - ''' +def add_tags(Name, region=None, key=None, keyid=None, profile=None, **kwargs): + """ Add tags to a trail Returns {tagged: true} if the trail was tagged and returns @@ -418,26 +448,28 @@ def add_tags(Name, salt myminion boto_cloudtrail.add_tags my_trail tag_a=tag_value tag_b=tag_value - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) tagslist = [] for k, v in six.iteritems(kwargs): - if six.text_type(k).startswith('__'): + if six.text_type(k).startswith("__"): continue - tagslist.append({'Key': six.text_type(k), 'Value': six.text_type(v)}) - conn.add_tags(ResourceId=_get_trail_arn(Name, - region=region, key=key, keyid=keyid, - profile=profile), TagsList=tagslist) - return {'tagged': True} + tagslist.append({"Key": six.text_type(k), "Value": six.text_type(v)}) + conn.add_tags( + ResourceId=_get_trail_arn( + Name, region=region, key=key, keyid=keyid, profile=profile + ), + TagsList=tagslist, + ) + return {"tagged": True} except ClientError as e: - return {'tagged': False, 'error': __utils__['boto3.get_error'](e)} + return {"tagged": False, "error": __utils__["boto3.get_error"](e)} -def remove_tags(Name, - region=None, key=None, keyid=None, profile=None, **kwargs): - ''' +def remove_tags(Name, region=None, key=None, keyid=None, profile=None, **kwargs): + """ Remove tags from a trail Returns {tagged: true} if the trail was tagged and returns @@ -449,26 +481,28 @@ def remove_tags(Name, salt myminion boto_cloudtrail.remove_tags my_trail tag_a=tag_value tag_b=tag_value - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) tagslist = [] for k, v in six.iteritems(kwargs): - if six.text_type(k).startswith('__'): + if six.text_type(k).startswith("__"): continue - tagslist.append({'Key': six.text_type(k), 'Value': six.text_type(v)}) - conn.remove_tags(ResourceId=_get_trail_arn(Name, - region=region, key=key, keyid=keyid, - profile=profile), TagsList=tagslist) - return {'tagged': True} + tagslist.append({"Key": six.text_type(k), "Value": six.text_type(v)}) + conn.remove_tags( + ResourceId=_get_trail_arn( + Name, region=region, key=key, keyid=keyid, profile=profile + ), + TagsList=tagslist, + ) + return {"tagged": True} except ClientError as e: - return {'tagged': False, 'error': __utils__['boto3.get_error'](e)} + return {"tagged": False, "error": __utils__["boto3.get_error"](e)} -def list_tags(Name, - region=None, key=None, keyid=None, profile=None): - ''' +def list_tags(Name, region=None, key=None, keyid=None, profile=None): + """ List tags of a trail Returns: @@ -482,18 +516,16 @@ def list_tags(Name, salt myminion boto_cloudtrail.list_tags my_trail - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - rid = _get_trail_arn(Name, - region=region, key=key, keyid=keyid, - profile=profile) + rid = _get_trail_arn(Name, region=region, key=key, keyid=keyid, profile=profile) ret = conn.list_tags(ResourceIdList=[rid]) - tlist = ret.get('ResourceTagList', []).pop().get('TagsList') + tlist = ret.get("ResourceTagList", []).pop().get("TagsList") tagdict = {} for tag in tlist: - tagdict[tag.get('Key')] = tag.get('Value') - return {'tags': tagdict} + tagdict[tag.get("Key")] = tag.get("Value") + return {"tags": tagdict} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} diff --git a/salt/modules/boto_cloudwatch.py b/salt/modules/boto_cloudwatch.py index e32e606b8ac..91a60327eff 100644 --- a/salt/modules/boto_cloudwatch.py +++ b/salt/modules/boto_cloudwatch.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon CloudWatch .. versionadded:: 2014.7.0 @@ -40,21 +40,22 @@ Connection module for Amazon CloudWatch region: us-east-1 :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging + +import salt.utils.json +import salt.utils.odict as odict +import salt.utils.versions import yaml # pylint: disable=blacklisted-import # Import Salt libs from salt.ext import six -import salt.utils.json -import salt.utils.odict as odict -import salt.utils.versions log = logging.getLogger(__name__) @@ -64,32 +65,33 @@ try: import boto.ec2.cloudwatch import boto.ec2.cloudwatch.listelement import boto.ec2.cloudwatch.dimension - logging.getLogger('boto').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' + """ has_boto_reqs = salt.utils.versions.check_boto_reqs(check_boto3=False) if has_boto_reqs is True: - __utils__['boto.assign_funcs'](__name__, 'cloudwatch', - module='ec2.cloudwatch', - pack=__salt__) + __utils__["boto.assign_funcs"]( + __name__, "cloudwatch", module="ec2.cloudwatch", pack=__salt__ + ) return has_boto_reqs def get_alarm(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get alarm details. Also can be used to check to see if an alarm exists. CLI example:: salt myminion boto_cloudwatch.get_alarm myalarm region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) alarms = conn.describe_alarms(alarm_names=[name]) @@ -101,32 +103,36 @@ def get_alarm(name, region=None, key=None, keyid=None, profile=None): def _safe_dump(data): - ''' + """ this presenter magic makes yaml.safe_dump work with the objects returned from boto.describe_alarms() - ''' - custom_dumper = __utils__['yaml.get_dumper']('SafeOrderedDumper') + """ + custom_dumper = __utils__["yaml.get_dumper"]("SafeOrderedDumper") def boto_listelement_presenter(dumper, data): return dumper.represent_list(list(data)) - yaml.add_representer(boto.ec2.cloudwatch.listelement.ListElement, - boto_listelement_presenter, - Dumper=custom_dumper) + yaml.add_representer( + boto.ec2.cloudwatch.listelement.ListElement, + boto_listelement_presenter, + Dumper=custom_dumper, + ) def dimension_presenter(dumper, data): return dumper.represent_dict(dict(data)) - yaml.add_representer(boto.ec2.cloudwatch.dimension.Dimension, - dimension_presenter, Dumper=custom_dumper) + yaml.add_representer( + boto.ec2.cloudwatch.dimension.Dimension, + dimension_presenter, + Dumper=custom_dumper, + ) - return __utils__['yaml.dump'](data, Dumper=custom_dumper) + return __utils__["yaml.dump"](data, Dumper=custom_dumper) -def get_all_alarms(region=None, prefix=None, key=None, keyid=None, - profile=None): - ''' +def get_all_alarms(region=None, prefix=None, key=None, keyid=None, profile=None): + """ Get all alarm details. Produces results that can be used to create an sls file. @@ -156,7 +162,7 @@ def get_all_alarms(region=None, prefix=None, key=None, keyid=None, CLI example:: salt myminion boto_cloudwatch.get_all_alarms region=us-east-1 --out=txt - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) alarms = conn.describe_alarms() @@ -170,19 +176,32 @@ def get_all_alarms(region=None, prefix=None, key=None, keyid=None, name = prefix + alarm["name"] del alarm["name"] alarm_sls = [{"name": name}, {"attributes": alarm}] - results["manage alarm " + name] = {"boto_cloudwatch_alarm.present": - alarm_sls} + results["manage alarm " + name] = {"boto_cloudwatch_alarm.present": alarm_sls} return _safe_dump(results) def create_or_update_alarm( - connection=None, name=None, metric=None, namespace=None, - statistic=None, comparison=None, threshold=None, period=None, - evaluation_periods=None, unit=None, description='', - dimensions=None, alarm_actions=None, - insufficient_data_actions=None, ok_actions=None, - region=None, key=None, keyid=None, profile=None): - ''' + connection=None, + name=None, + metric=None, + namespace=None, + statistic=None, + comparison=None, + threshold=None, + period=None, + evaluation_periods=None, + unit=None, + description="", + dimensions=None, + alarm_actions=None, + insufficient_data_actions=None, + ok_actions=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create or update a cloudwatch alarm. Params are the same as: @@ -207,7 +226,7 @@ def create_or_update_alarm( CLI example: salt myminion boto_cloudwatch.create_alarm name=myalarm ... region=us-east-1 - ''' + """ # clean up argument types, so that CLI works if threshold: threshold = float(threshold) @@ -218,7 +237,10 @@ def create_or_update_alarm( if isinstance(dimensions, six.string_types): dimensions = salt.utils.json.loads(dimensions) if not isinstance(dimensions, dict): - log.error("could not parse dimensions argument: must be json encoding of a dict: '%s'", dimensions) + log.error( + "could not parse dimensions argument: must be json encoding of a dict: '%s'", + dimensions, + ) return False if isinstance(alarm_actions, six.string_types): alarm_actions = alarm_actions.split(",") @@ -229,23 +251,21 @@ def create_or_update_alarm( # convert provided action names into ARN's if alarm_actions: - alarm_actions = convert_to_arn(alarm_actions, - region=region, - key=key, - keyid=keyid, - profile=profile) + alarm_actions = convert_to_arn( + alarm_actions, region=region, key=key, keyid=keyid, profile=profile + ) if insufficient_data_actions: - insufficient_data_actions = convert_to_arn(insufficient_data_actions, - region=region, - key=key, - keyid=keyid, - profile=profile) + insufficient_data_actions = convert_to_arn( + insufficient_data_actions, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if ok_actions: - ok_actions = convert_to_arn(ok_actions, - region=region, - key=key, - keyid=keyid, - profile=profile) + ok_actions = convert_to_arn( + ok_actions, region=region, key=key, keyid=keyid, profile=profile + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -264,22 +284,22 @@ def create_or_update_alarm( dimensions=dimensions, alarm_actions=alarm_actions, insufficient_data_actions=insufficient_data_actions, - ok_actions=ok_actions + ok_actions=ok_actions, ) conn.create_alarm(alarm) - log.info('Created/updated alarm %s', name) + log.info("Created/updated alarm %s", name) return True def convert_to_arn(arns, region=None, key=None, keyid=None, profile=None): - ''' + """ Convert a list of strings into actual arns. Converts convenience names such as 'scaling_policy:...' CLI Example:: salt '*' convert_to_arn 'scaling_policy:' - ''' + """ results = [] for arn in arns: if arn.startswith("scaling_policy:"): @@ -290,37 +310,49 @@ def convert_to_arn(arns, region=None, key=None, keyid=None, profile=None): if policy_arn: results.append(policy_arn) else: - log.error('Could not convert: %s', arn) + log.error("Could not convert: %s", arn) else: results.append(arn) return results def delete_alarm(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete a cloudwatch alarm CLI example to delete a queue:: salt myminion boto_cloudwatch.delete_alarm myalarm region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_alarms([name]) - log.info('Deleted alarm %s', name) + log.info("Deleted alarm %s", name) return True def _metric_alarm_to_dict(alarm): - ''' + """ Convert a boto.ec2.cloudwatch.alarm.MetricAlarm into a dict. Convenience for pretty printing. - ''' + """ d = odict.OrderedDict() - fields = ['name', 'metric', 'namespace', 'statistic', 'comparison', - 'threshold', 'period', 'evaluation_periods', 'unit', - 'description', 'dimensions', 'alarm_actions', - 'insufficient_data_actions', 'ok_actions'] + fields = [ + "name", + "metric", + "namespace", + "statistic", + "comparison", + "threshold", + "period", + "evaluation_periods", + "unit", + "description", + "dimensions", + "alarm_actions", + "insufficient_data_actions", + "ok_actions", + ] for f in fields: if hasattr(alarm, f): d[f] = getattr(alarm, f) diff --git a/salt/modules/boto_cloudwatch_event.py b/salt/modules/boto_cloudwatch_event.py index 529401df029..49daad93137 100644 --- a/salt/modules/boto_cloudwatch_event.py +++ b/salt/modules/boto_cloudwatch_event.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon CloudWatch Events .. versionadded:: 2016.11.0 @@ -40,9 +40,9 @@ Connection module for Amazon CloudWatch Events region: us-east-1 :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 from __future__ import absolute_import, print_function, unicode_literals @@ -53,41 +53,42 @@ import logging import salt.utils.compat import salt.utils.json import salt.utils.versions +from salt.ext import six log = logging.getLogger(__name__) # Import third party libs # pylint: disable=import-error try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import boto3 - #pylint: enable=unused-import + + # pylint: enable=unused-import from botocore.exceptions import ClientError - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError as e: HAS_BOTO = False # pylint: enable=import-error -from salt.ext import six - def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' + """ return salt.utils.versions.check_boto_reqs() def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 'events') + __utils__["boto3.assign_funcs"](__name__, "events") def exists(Name, region=None, key=None, keyid=None, profile=None): - ''' + """ Given a rule name, check to see if the given rule exists. Returns True if the given rule exists and returns False if the given @@ -96,30 +97,35 @@ def exists(Name, region=None, key=None, keyid=None, profile=None): CLI example:: salt myminion boto_cloudwatch_event.exists myevent region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: events = conn.list_rules(NamePrefix=Name) if len(events) == 0: - return {'exists': False} - for rule in events.get('Rules', []): - if rule.get('Name', None) == Name: - return {'exists': True} - return {'exists': False} + return {"exists": False} + for rule in events.get("Rules", []): + if rule.get("Name", None) == Name: + return {"exists": True} + return {"exists": False} except ClientError as e: - err = __utils__['boto3.get_error'](e) - return {'error': err} + err = __utils__["boto3.get_error"](e) + return {"error": err} -def create_or_update(Name, - ScheduleExpression=None, - EventPattern=None, - Description=None, - RoleArn=None, - State=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_or_update( + Name, + ScheduleExpression=None, + EventPattern=None, + Description=None, + RoleArn=None, + State=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, create an event rule. Returns {created: true} if the rule was created and returns @@ -131,31 +137,34 @@ def create_or_update(Name, salt myminion boto_cloudwatch_event.create_or_update my_rule - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} - for arg in ('ScheduleExpression', 'EventPattern', 'State', - 'Description', 'RoleArn'): + for arg in ( + "ScheduleExpression", + "EventPattern", + "State", + "Description", + "RoleArn", + ): if locals()[arg] is not None: kwargs[arg] = locals()[arg] - rule = conn.put_rule(Name=Name, - **kwargs) + rule = conn.put_rule(Name=Name, **kwargs) if rule: - log.info('The newly created event rule is %s', rule.get('RuleArn')) + log.info("The newly created event rule is %s", rule.get("RuleArn")) - return {'created': True, 'arn': rule.get('RuleArn')} + return {"created": True, "arn": rule.get("RuleArn")} else: - log.warning('Event rule was not created') - return {'created': False} + log.warning("Event rule was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete(Name, - region=None, key=None, keyid=None, profile=None): - ''' +def delete(Name, region=None, key=None, keyid=None, profile=None): + """ Given a rule name, delete it. Returns {deleted: true} if the rule was deleted and returns @@ -167,19 +176,18 @@ def delete(Name, salt myminion boto_cloudwatch_event.delete myrule - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_rule(Name=Name) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def describe(Name, - region=None, key=None, keyid=None, profile=None): - ''' +def describe(Name, region=None, key=None, keyid=None, profile=None): + """ Given a rule name describe its properties. Returns a dictionary of interesting properties. @@ -190,52 +198,56 @@ def describe(Name, salt myminion boto_cloudwatch_event.describe myrule - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) rule = conn.describe_rule(Name=Name) if rule: - keys = ('Name', 'Arn', 'EventPattern', - 'ScheduleExpression', 'State', - 'Description', - 'RoleArn') - return {'rule': dict([(k, rule.get(k)) for k in keys])} + keys = ( + "Name", + "Arn", + "EventPattern", + "ScheduleExpression", + "State", + "Description", + "RoleArn", + ) + return {"rule": dict([(k, rule.get(k)) for k in keys])} else: - return {'rule': None} + return {"rule": None} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'RuleNotFoundException': - return {'error': "Rule {0} not found".format(Rule)} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "RuleNotFoundException": + return {"error": "Rule {0} not found".format(Rule)} + return {"error": __utils__["boto3.get_error"](e)} def list_rules(region=None, key=None, keyid=None, profile=None): - ''' + """ List, with details, all Cloudwatch Event rules visible in the current scope. CLI example:: salt myminion boto_cloudwatch_event.list_rules region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: ret = [] - NextToken = '' + NextToken = "" while NextToken is not None: - args = {'NextToken': NextToken} if NextToken else {} + args = {"NextToken": NextToken} if NextToken else {} r = conn.list_rules(**args) - ret += r.get('Rules', []) - NextToken = r.get('NextToken') + ret += r.get("Rules", []) + NextToken = r.get("NextToken") return ret except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def list_targets(Rule, - region=None, key=None, keyid=None, profile=None): - ''' +def list_targets(Rule, region=None, key=None, keyid=None, profile=None): + """ Given a rule name list the targets of that rule. Returns a dictionary of interesting properties. @@ -246,29 +258,27 @@ def list_targets(Rule, salt myminion boto_cloudwatch_event.list_targets myrule - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) targets = conn.list_targets_by_rule(Rule=Rule) ret = [] - if targets and 'Targets' in targets: - keys = ('Id', 'Arn', 'Input', - 'InputPath') - for target in targets.get('Targets'): + if targets and "Targets" in targets: + keys = ("Id", "Arn", "Input", "InputPath") + for target in targets.get("Targets"): ret.append(dict([(k, target.get(k)) for k in keys if k in target])) - return {'targets': ret} + return {"targets": ret} else: - return {'targets': None} + return {"targets": None} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'RuleNotFoundException': - return {'error': "Rule {0} not found".format(Rule)} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "RuleNotFoundException": + return {"error": "Rule {0} not found".format(Rule)} + return {"error": __utils__["boto3.get_error"](e)} -def put_targets(Rule, Targets, - region=None, key=None, keyid=None, profile=None): - ''' +def put_targets(Rule, Targets, region=None, key=None, keyid=None, profile=None): + """ Add the given targets to the given rule Returns a dictionary describing any failures. @@ -279,26 +289,25 @@ def put_targets(Rule, Targets, salt myminion boto_cloudwatch_event.put_targets myrule [{'Id': 'target1', 'Arn': 'arn:***'}] - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(Targets, six.string_types): Targets = salt.utils.json.loads(Targets) failures = conn.put_targets(Rule=Rule, Targets=Targets) - if failures and failures.get('FailedEntryCount', 0) > 0: - return {'failures': failures.get('FailedEntries')} + if failures and failures.get("FailedEntryCount", 0) > 0: + return {"failures": failures.get("FailedEntries")} else: - return {'failures': None} + return {"failures": None} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'RuleNotFoundException': - return {'error': "Rule {0} not found".format(Rule)} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "RuleNotFoundException": + return {"error": "Rule {0} not found".format(Rule)} + return {"error": __utils__["boto3.get_error"](e)} -def remove_targets(Rule, Ids, - region=None, key=None, keyid=None, profile=None): - ''' +def remove_targets(Rule, Ids, region=None, key=None, keyid=None, profile=None): + """ Given a rule name remove the named targets from the target list Returns a dictionary describing any failures. @@ -309,18 +318,18 @@ def remove_targets(Rule, Ids, salt myminion boto_cloudwatch_event.remove_targets myrule ['Target1'] - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(Ids, six.string_types): Ids = salt.utils.json.loads(Ids) failures = conn.remove_targets(Rule=Rule, Ids=Ids) - if failures and failures.get('FailedEntryCount', 0) > 0: - return {'failures': failures.get('FailedEntries', 1)} + if failures and failures.get("FailedEntryCount", 0) > 0: + return {"failures": failures.get("FailedEntries", 1)} else: - return {'failures': None} + return {"failures": None} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'RuleNotFoundException': - return {'error': "Rule {0} not found".format(Rule)} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "RuleNotFoundException": + return {"error": "Rule {0} not found".format(Rule)} + return {"error": __utils__["boto3.get_error"](e)} diff --git a/salt/modules/boto_cognitoidentity.py b/salt/modules/boto_cognitoidentity.py index 63377cad658..017bcbdc981 100644 --- a/salt/modules/boto_cognitoidentity.py +++ b/salt/modules/boto_cognitoidentity.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon CognitoIdentity .. versionadded:: 2016.11.0 @@ -72,12 +72,13 @@ Connection module for Amazon CognitoIdentity :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -93,10 +94,12 @@ try: # pylint: disable=unused-import import boto import boto3 + # pylint: enable=unused-import from botocore.exceptions import ClientError - logging.getLogger('boto').setLevel(logging.CRITICAL) - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -104,47 +107,54 @@ except ImportError: def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # the boto_cognitoidentity execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 - return salt.utils.versions.check_boto_reqs( - boto_ver='2.8.0', - boto3_ver='1.2.1' - ) + return salt.utils.versions.check_boto_reqs(boto_ver="2.8.0", boto3_ver="1.2.1") def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 'cognito-identity') + __utils__["boto3.assign_funcs"](__name__, "cognito-identity") def _find_identity_pool_ids(name, pool_id, conn): - ''' + """ Given identity pool name (or optionally a pool_id and name will be ignored), find and return list of matching identity pool id's. - ''' + """ ids = [] if pool_id is None: - for pools in __utils__['boto3.paged_call'](conn.list_identity_pools, - marker_flag='NextToken', marker_arg='NextToken', MaxResults=25): - for pool in pools['IdentityPools']: - if pool['IdentityPoolName'] == name: - ids.append(pool['IdentityPoolId']) + for pools in __utils__["boto3.paged_call"]( + conn.list_identity_pools, + marker_flag="NextToken", + marker_arg="NextToken", + MaxResults=25, + ): + for pool in pools["IdentityPools"]: + if pool["IdentityPoolName"] == name: + ids.append(pool["IdentityPoolId"]) else: ids.append(pool_id) return ids -def describe_identity_pools(IdentityPoolName, IdentityPoolId=None, - region=None, key=None, keyid=None, profile=None): +def describe_identity_pools( + IdentityPoolName, + IdentityPoolId=None, + region=None, + key=None, + keyid=None, + profile=None, +): - ''' + """ Given an identity pool name, (optionally if an identity pool id is given, the given name will be ignored) @@ -157,7 +167,7 @@ def describe_identity_pools(IdentityPoolName, IdentityPoolId=None, salt myminion boto_cognitoidentity.describe_identity_pools my_id_pool_name salt myminion boto_cognitoidentity.describe_identity_pools '' IdentityPoolId=my_id_pool_id - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -167,22 +177,27 @@ def describe_identity_pools(IdentityPoolName, IdentityPoolId=None, results = [] for pool_id in ids: response = conn.describe_identity_pool(IdentityPoolId=pool_id) - response.pop('ResponseMetadata', None) + response.pop("ResponseMetadata", None) results.append(response) - return {'identity_pools': results} + return {"identity_pools": results} else: - return {'identity_pools': None} + return {"identity_pools": None} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create_identity_pool(IdentityPoolName, - AllowUnauthenticatedIdentities=False, - SupportedLoginProviders=None, - DeveloperProviderName=None, - OpenIdConnectProviderARNs=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_identity_pool( + IdentityPoolName, + AllowUnauthenticatedIdentities=False, + SupportedLoginProviders=None, + DeveloperProviderName=None, + OpenIdConnectProviderARNs=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates a new identity pool. All parameters except for IdentityPoolName is optional. SupportedLoginProviders should be a dictionary mapping provider names to provider app IDs. OpenIdConnectProviderARNs should be a list of OpenID Connect provider ARNs. @@ -196,31 +211,43 @@ def create_identity_pool(IdentityPoolName, salt myminion boto_cognitoidentity.create_identity_pool my_id_pool_name \ DeveloperProviderName=custom_developer_provider - ''' - SupportedLoginProviders = dict() if SupportedLoginProviders is None else SupportedLoginProviders - OpenIdConnectProviderARNs = list() if OpenIdConnectProviderARNs is None else OpenIdConnectProviderARNs + """ + SupportedLoginProviders = ( + dict() if SupportedLoginProviders is None else SupportedLoginProviders + ) + OpenIdConnectProviderARNs = ( + list() if OpenIdConnectProviderARNs is None else OpenIdConnectProviderARNs + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - request_params = dict(IdentityPoolName=IdentityPoolName, - AllowUnauthenticatedIdentities=AllowUnauthenticatedIdentities, - SupportedLoginProviders=SupportedLoginProviders, - OpenIdConnectProviderARNs=OpenIdConnectProviderARNs) + request_params = dict( + IdentityPoolName=IdentityPoolName, + AllowUnauthenticatedIdentities=AllowUnauthenticatedIdentities, + SupportedLoginProviders=SupportedLoginProviders, + OpenIdConnectProviderARNs=OpenIdConnectProviderARNs, + ) if DeveloperProviderName: - request_params['DeveloperProviderName'] = DeveloperProviderName + request_params["DeveloperProviderName"] = DeveloperProviderName response = conn.create_identity_pool(**request_params) - response.pop('ResponseMetadata', None) + response.pop("ResponseMetadata", None) - return {'created': True, 'identity_pool': response} + return {"created": True, "identity_pool": response} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete_identity_pools(IdentityPoolName, IdentityPoolId=None, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_identity_pools( + IdentityPoolName, + IdentityPoolId=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given an identity pool name, (optionally if an identity pool id is given, the given name will be ignored) @@ -234,7 +261,7 @@ def delete_identity_pools(IdentityPoolName, IdentityPoolId=None, salt myminion boto_cognitoidentity.delete_identity_pools my_id_pool_name salt myminion boto_cognitoidentity.delete_identity_pools '' IdentityPoolId=my_id_pool_id - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -245,16 +272,22 @@ def delete_identity_pools(IdentityPoolName, IdentityPoolId=None, for pool_id in ids: conn.delete_identity_pool(IdentityPoolId=pool_id) count += 1 - return {'deleted': True, 'count': count} + return {"deleted": True, "count": count} else: - return {'deleted': False, 'count': count} + return {"deleted": False, "count": count} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def get_identity_pool_roles(IdentityPoolName, IdentityPoolId=None, - region=None, key=None, keyid=None, profile=None): - ''' +def get_identity_pool_roles( + IdentityPoolName, + IdentityPoolId=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given an identity pool name, (optionally if an identity pool id if given, the given name will be ignored) @@ -267,7 +300,7 @@ def get_identity_pool_roles(IdentityPoolName, IdentityPoolId=None, salt myminion boto_cognitoidentity.get_identity_pool_roles my_id_pool_name salt myminion boto_cognitoidentity.get_identity_pool_roles '' IdentityPoolId=my_id_pool_id - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -277,31 +310,38 @@ def get_identity_pool_roles(IdentityPoolName, IdentityPoolId=None, results = [] for pool_id in ids: response = conn.get_identity_pool_roles(IdentityPoolId=pool_id) - response.pop('ResponseMetadata', None) + response.pop("ResponseMetadata", None) results.append(response) - return {'identity_pool_roles': results} + return {"identity_pool_roles": results} else: - return {'identity_pool_roles': None} + return {"identity_pool_roles": None} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def _get_role_arn(name, **conn_params): - ''' + """ Helper function to turn a name into an arn string, returns None if not able to resolve - ''' - if name.startswith('arn:aws:iam'): + """ + if name.startswith("arn:aws:iam"): return name - role = __salt__['boto_iam.describe_role'](name, **conn_params) - rolearn = role.get('arn') if role else None + role = __salt__["boto_iam.describe_role"](name, **conn_params) + rolearn = role.get("arn") if role else None return rolearn -def set_identity_pool_roles(IdentityPoolId, AuthenticatedRole=None, UnauthenticatedRole=None, - region=None, key=None, keyid=None, profile=None): - ''' +def set_identity_pool_roles( + IdentityPoolId, + AuthenticatedRole=None, + UnauthenticatedRole=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given an identity pool id, set the given AuthenticatedRole and UnauthenticatedRole (the Role can be an iam arn, or a role name) If AuthenticatedRole or UnauthenticatedRole is not given, the authenticated and/or the unauthenticated role associated previously with the pool will be @@ -321,7 +361,7 @@ def set_identity_pool_roles(IdentityPoolId, AuthenticatedRole=None, Unauthentica salt myminion boto_cognitoidentity.set_identity_pool_roles my_id_pool_id \ UnauthenticatedRole=my_unauth_role # this will set the unauth role and clear the auth role - ''' + """ conn_params = dict(region=region, key=key, keyid=keyid, profile=profile) conn = _get_conn(**conn_params) @@ -329,36 +369,49 @@ def set_identity_pool_roles(IdentityPoolId, AuthenticatedRole=None, Unauthentica if AuthenticatedRole: role_arn = _get_role_arn(AuthenticatedRole, **conn_params) if role_arn is None: - return {'set': False, 'error': 'invalid AuthenticatedRole {0}'.format(AuthenticatedRole)} + return { + "set": False, + "error": "invalid AuthenticatedRole {0}".format(AuthenticatedRole), + } AuthenticatedRole = role_arn if UnauthenticatedRole: role_arn = _get_role_arn(UnauthenticatedRole, **conn_params) if role_arn is None: - return {'set': False, 'error': 'invalid UnauthenticatedRole {0}'.format(UnauthenticatedRole)} + return { + "set": False, + "error": "invalid UnauthenticatedRole {0}".format( + UnauthenticatedRole + ), + } UnauthenticatedRole = role_arn Roles = dict() if AuthenticatedRole: - Roles['authenticated'] = AuthenticatedRole + Roles["authenticated"] = AuthenticatedRole if UnauthenticatedRole: - Roles['unauthenticated'] = UnauthenticatedRole + Roles["unauthenticated"] = UnauthenticatedRole conn.set_identity_pool_roles(IdentityPoolId=IdentityPoolId, Roles=Roles) - return {'set': True, 'roles': Roles} + return {"set": True, "roles": Roles} except ClientError as e: - return {'set': False, 'error': __utils__['boto3.get_error'](e)} + return {"set": False, "error": __utils__["boto3.get_error"](e)} -def update_identity_pool(IdentityPoolId, - IdentityPoolName=None, - AllowUnauthenticatedIdentities=False, - SupportedLoginProviders=None, - DeveloperProviderName=None, - OpenIdConnectProviderARNs=None, - region=None, key=None, keyid=None, profile=None): - ''' +def update_identity_pool( + IdentityPoolId, + IdentityPoolName=None, + AllowUnauthenticatedIdentities=False, + SupportedLoginProviders=None, + DeveloperProviderName=None, + OpenIdConnectProviderARNs=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Updates the given IdentityPoolId's properties. All parameters except for IdentityPoolId, is optional. SupportedLoginProviders should be a dictionary mapping provider names to provider app IDs. OpenIdConnectProviderARNs should be a list of OpenID Connect provider @@ -379,44 +432,53 @@ def update_identity_pool(IdentityPoolId, salt myminion boto_cognitoidentity.update_identity_pool my_id_pool_id my_id_pool_name \ DeveloperProviderName=custom_developer_provider - ''' + """ conn_params = dict(region=region, key=key, keyid=keyid, profile=profile) - response = describe_identity_pools('', IdentityPoolId=IdentityPoolId, **conn_params) - error = response.get('error') + response = describe_identity_pools("", IdentityPoolId=IdentityPoolId, **conn_params) + error = response.get("error") if error is None: - error = 'No matching pool' if response.get('identity_pools') is None else None + error = "No matching pool" if response.get("identity_pools") is None else None if error: - return {'updated': False, 'error': error} + return {"updated": False, "error": error} - id_pool = response.get('identity_pools')[0] + id_pool = response.get("identity_pools")[0] request_params = id_pool.copy() # IdentityPoolName and AllowUnauthenticatedIdentities are required for the call to update_identity_pool - if IdentityPoolName is not None and IdentityPoolName != request_params.get('IdentityPoolName'): - request_params['IdentityPoolName'] = IdentityPoolName + if IdentityPoolName is not None and IdentityPoolName != request_params.get( + "IdentityPoolName" + ): + request_params["IdentityPoolName"] = IdentityPoolName - if AllowUnauthenticatedIdentities != request_params.get('AllowUnauthenticatedIdentities'): - request_params['AllowUnauthenticatedIdentities'] = AllowUnauthenticatedIdentities + if AllowUnauthenticatedIdentities != request_params.get( + "AllowUnauthenticatedIdentities" + ): + request_params[ + "AllowUnauthenticatedIdentities" + ] = AllowUnauthenticatedIdentities - current_val = request_params.pop('SupportedLoginProviders', None) + current_val = request_params.pop("SupportedLoginProviders", None) if SupportedLoginProviders is not None and SupportedLoginProviders != current_val: - request_params['SupportedLoginProviders'] = SupportedLoginProviders + request_params["SupportedLoginProviders"] = SupportedLoginProviders # we can only set DeveloperProviderName one time per AWS. - current_val = request_params.pop('DeveloperProviderName', None) + current_val = request_params.pop("DeveloperProviderName", None) if current_val is None and DeveloperProviderName is not None: - request_params['DeveloperProviderName'] = DeveloperProviderName + request_params["DeveloperProviderName"] = DeveloperProviderName - current_val = request_params.pop('OpenIdConnectProviderARNs', None) - if OpenIdConnectProviderARNs is not None and OpenIdConnectProviderARNs != current_val: - request_params['OpenIdConnectProviderARNs'] = OpenIdConnectProviderARNs + current_val = request_params.pop("OpenIdConnectProviderARNs", None) + if ( + OpenIdConnectProviderARNs is not None + and OpenIdConnectProviderARNs != current_val + ): + request_params["OpenIdConnectProviderARNs"] = OpenIdConnectProviderARNs conn = _get_conn(**conn_params) try: response = conn.update_identity_pool(**request_params) - response.pop('ResponseMetadata', None) + response.pop("ResponseMetadata", None) - return {'updated': True, 'identity_pool': response} + return {"updated": True, "identity_pool": response} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} diff --git a/salt/modules/boto_datapipeline.py b/salt/modules/boto_datapipeline.py index cc62541b79c..42a421c3836 100644 --- a/salt/modules/boto_datapipeline.py +++ b/salt/modules/boto_datapipeline.py @@ -1,41 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Data Pipeline .. versionadded:: 2016.3.0 :depends: boto3 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.versions + # Import Salt libs from salt.ext import six -import salt.utils.versions log = logging.getLogger(__name__) try: import boto3 import botocore.exceptions + boto3.set_stream_logger(level=logging.CRITICAL) - logging.getLogger('botocore').setLevel(logging.CRITICAL) + logging.getLogger("botocore").setLevel(logging.CRITICAL) HAS_BOTO3 = True except ImportError: HAS_BOTO3 = False def __virtual__(): - ''' + """ Only load if boto3 libraries exists. - ''' + """ return salt.utils.versions.check_boto_reqs(check_boto=False) def activate_pipeline(pipeline_id, region=None, key=None, keyid=None, profile=None): - ''' + """ Start processing pipeline tasks. This function is idempotent. CLI example: @@ -43,20 +46,21 @@ def activate_pipeline(pipeline_id, region=None, key=None, keyid=None, profile=No .. code-block:: bash salt myminion boto_datapipeline.activate_pipeline my_pipeline_id - ''' + """ client = _get_client(region, key, keyid, profile) r = {} try: client.activate_pipeline(pipelineId=pipeline_id) - r['result'] = True + r["result"] = True except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - r['error'] = six.text_type(e) + r["error"] = six.text_type(e) return r -def create_pipeline(name, unique_id, description='', region=None, key=None, keyid=None, - profile=None): - ''' +def create_pipeline( + name, unique_id, description="", region=None, key=None, keyid=None, profile=None +): + """ Create a new, empty pipeline. This function is idempotent. CLI example: @@ -64,23 +68,21 @@ def create_pipeline(name, unique_id, description='', region=None, key=None, keyi .. code-block:: bash salt myminion boto_datapipeline.create_pipeline my_name my_unique_id - ''' + """ client = _get_client(region, key, keyid, profile) r = {} try: response = client.create_pipeline( - name=name, - uniqueId=unique_id, - description=description, + name=name, uniqueId=unique_id, description=description, ) - r['result'] = response['pipelineId'] + r["result"] = response["pipelineId"] except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - r['error'] = six.text_type(e) + r["error"] = six.text_type(e) return r def delete_pipeline(pipeline_id, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete a pipeline, its pipeline definition, and its run history. This function is idempotent. CLI example: @@ -88,19 +90,19 @@ def delete_pipeline(pipeline_id, region=None, key=None, keyid=None, profile=None .. code-block:: bash salt myminion boto_datapipeline.delete_pipeline my_pipeline_id - ''' + """ client = _get_client(region, key, keyid, profile) r = {} try: client.delete_pipeline(pipelineId=pipeline_id) - r['result'] = True + r["result"] = True except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - r['error'] = six.text_type(e) + r["error"] = six.text_type(e) return r def describe_pipelines(pipeline_ids, region=None, key=None, keyid=None, profile=None): - ''' + """ Retrieve metadata about one or more pipelines. CLI example: @@ -108,19 +110,20 @@ def describe_pipelines(pipeline_ids, region=None, key=None, keyid=None, profile= .. code-block:: bash salt myminion boto_datapipeline.describe_pipelines ['my_pipeline_id'] - ''' + """ client = _get_client(region, key, keyid, profile) r = {} try: - r['result'] = client.describe_pipelines(pipelineIds=pipeline_ids) + r["result"] = client.describe_pipelines(pipelineIds=pipeline_ids) except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - r['error'] = six.text_type(e) + r["error"] = six.text_type(e) return r -def get_pipeline_definition(pipeline_id, version='latest', region=None, key=None, keyid=None, - profile=None): - ''' +def get_pipeline_definition( + pipeline_id, version="latest", region=None, key=None, keyid=None, profile=None +): + """ Get the definition of the specified pipeline. CLI example: @@ -128,21 +131,20 @@ def get_pipeline_definition(pipeline_id, version='latest', region=None, key=None .. code-block:: bash salt myminion boto_datapipeline.get_pipeline_definition my_pipeline_id - ''' + """ client = _get_client(region, key, keyid, profile) r = {} try: - r['result'] = client.get_pipeline_definition( - pipelineId=pipeline_id, - version=version, + r["result"] = client.get_pipeline_definition( + pipelineId=pipeline_id, version=version, ) except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - r['error'] = six.text_type(e) + r["error"] = six.text_type(e) return r def list_pipelines(region=None, key=None, keyid=None, profile=None): - ''' + """ Get a list of pipeline ids and names for all pipelines. CLI Example: @@ -150,22 +152,22 @@ def list_pipelines(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_datapipeline.list_pipelines profile=myprofile - ''' + """ client = _get_client(region, key, keyid, profile) r = {} try: - paginator = client.get_paginator('list_pipelines') + paginator = client.get_paginator("list_pipelines") pipelines = [] for page in paginator.paginate(): - pipelines += page['pipelineIdList'] - r['result'] = pipelines + pipelines += page["pipelineIdList"] + r["result"] = pipelines except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - r['error'] = six.text_type(e) + r["error"] = six.text_type(e) return r def pipeline_id_from_name(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get the pipeline id, if it exists, for the given name. CLI example: @@ -173,23 +175,31 @@ def pipeline_id_from_name(name, region=None, key=None, keyid=None, profile=None) .. code-block:: bash salt myminion boto_datapipeline.pipeline_id_from_name my_pipeline_name - ''' + """ r = {} result_pipelines = list_pipelines() - if 'error' in result_pipelines: + if "error" in result_pipelines: return result_pipelines - for pipeline in result_pipelines['result']: - if pipeline['name'] == name: - r['result'] = pipeline['id'] + for pipeline in result_pipelines["result"]: + if pipeline["name"] == name: + r["result"] = pipeline["id"] return r - r['error'] = 'No pipeline found with name={0}'.format(name) + r["error"] = "No pipeline found with name={0}".format(name) return r -def put_pipeline_definition(pipeline_id, pipeline_objects, parameter_objects=None, - parameter_values=None, region=None, key=None, keyid=None, profile=None): - ''' +def put_pipeline_definition( + pipeline_id, + pipeline_objects, + parameter_objects=None, + parameter_values=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Add tasks, schedules, and preconditions to the specified pipeline. This function is idempotent and will replace an existing definition. @@ -198,7 +208,7 @@ def put_pipeline_definition(pipeline_id, pipeline_objects, parameter_objects=Non .. code-block:: bash salt myminion boto_datapipeline.put_pipeline_definition my_pipeline_id my_pipeline_objects - ''' + """ parameter_objects = parameter_objects or [] parameter_values = parameter_values or [] client = _get_client(region, key, keyid, profile) @@ -210,48 +220,46 @@ def put_pipeline_definition(pipeline_id, pipeline_objects, parameter_objects=Non parameterObjects=parameter_objects, parameterValues=parameter_values, ) - if response['errored']: - r['error'] = response['validationErrors'] + if response["errored"]: + r["error"] = response["validationErrors"] else: - r['result'] = response + r["result"] = response except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: - r['error'] = six.text_type(e) + r["error"] = six.text_type(e) return r def _get_client(region, key, keyid, profile): - ''' + """ Get a boto connection to Data Pipeline. - ''' + """ session = _get_session(region, key, keyid, profile) if not session: log.error("Failed to get datapipeline client.") return None - return session.client('datapipeline') + return session.client("datapipeline") def _get_session(region, key, keyid, profile): - ''' + """ Get a boto3 session - ''' + """ if profile: if isinstance(profile, six.string_types): - _profile = __salt__['config.option'](profile) + _profile = __salt__["config.option"](profile) elif isinstance(profile, dict): _profile = profile - key = _profile.get('key', None) - keyid = _profile.get('keyid', None) - region = _profile.get('region', None) + key = _profile.get("key", None) + keyid = _profile.get("keyid", None) + region = _profile.get("region", None) - if not region and __salt__['config.option']('datapipeline.region'): - region = __salt__['config.option']('datapipeline.region') + if not region and __salt__["config.option"]("datapipeline.region"): + region = __salt__["config.option"]("datapipeline.region") if not region: - region = 'us-east-1' + region = "us-east-1" return boto3.session.Session( - region_name=region, - aws_secret_access_key=key, - aws_access_key_id=keyid, + region_name=region, aws_secret_access_key=key, aws_access_key_id=keyid, ) diff --git a/salt/modules/boto_dynamodb.py b/salt/modules/boto_dynamodb.py index ad1c0256e46..a2fd2251f16 100644 --- a/salt/modules/boto_dynamodb.py +++ b/salt/modules/boto_dynamodb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon DynamoDB .. versionadded:: 2015.5.0 @@ -40,54 +40,74 @@ Connection module for Amazon DynamoDB region: us-east-1 :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time -logger = logging.getLogger(__name__) -logging.getLogger('boto').setLevel(logging.INFO) +import salt.utils.versions +from salt.exceptions import SaltInvocationError # Import third party libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -from salt.exceptions import SaltInvocationError -import salt.utils.versions + +logger = logging.getLogger(__name__) +logging.getLogger("boto").setLevel(logging.INFO) + try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import boto.dynamodb2 - #pylint: enable=unused-import + + # pylint: enable=unused-import from boto.dynamodb2.fields import HashKey, RangeKey - from boto.dynamodb2.fields import AllIndex, GlobalAllIndex, GlobalIncludeIndex, GlobalKeysOnlyIndex + from boto.dynamodb2.fields import ( + AllIndex, + GlobalAllIndex, + GlobalIncludeIndex, + GlobalKeysOnlyIndex, + ) from boto.dynamodb2.table import Table from boto.exception import JSONResponseError + HAS_BOTO = True except ImportError: HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' + """ has_boto_reqs = salt.utils.versions.check_boto_reqs(check_boto3=False) if has_boto_reqs is True: - __utils__['boto.assign_funcs'](__name__, 'dynamodb2', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "dynamodb2", pack=__salt__) return has_boto_reqs -def create_table(table_name, region=None, key=None, keyid=None, profile=None, - read_capacity_units=None, write_capacity_units=None, - hash_key=None, hash_key_data_type=None, range_key=None, - range_key_data_type=None, local_indexes=None, - global_indexes=None): - ''' +def create_table( + table_name, + region=None, + key=None, + keyid=None, + profile=None, + read_capacity_units=None, + write_capacity_units=None, + hash_key=None, + hash_key_data_type=None, + range_key=None, + range_key_data_type=None, + local_indexes=None, + global_indexes=None, +): + """ Creates a DynamoDB table. CLI Example: @@ -102,10 +122,10 @@ def create_table(table_name, region=None, key=None, keyid=None, profile=None, range_key_data_type=N / read_capacity_units=1 / write_capacity_units=1 - ''' + """ schema = [] primary_index_fields = [] - primary_index_name = '' + primary_index_name = "" if hash_key: hash_key_obj = HashKey(hash_key, data_type=hash_key_data_type) schema.append(hash_key_obj) @@ -115,13 +135,10 @@ def create_table(table_name, region=None, key=None, keyid=None, profile=None, range_key_obj = RangeKey(range_key, data_type=range_key_data_type) schema.append(range_key_obj) primary_index_fields.append(range_key_obj) - primary_index_name += '_' + primary_index_name += "_" primary_index_name += range_key - primary_index_name += '_index' - throughput = { - 'read': read_capacity_units, - 'write': write_capacity_units - } + primary_index_name += "_index" + throughput = {"read": read_capacity_units, "write": write_capacity_units} local_table_indexes = [] if local_indexes: for index in local_indexes: @@ -129,9 +146,7 @@ def create_table(table_name, region=None, key=None, keyid=None, profile=None, global_table_indexes = [] if global_indexes: for index in global_indexes: - global_table_indexes.append( - extract_index(index, global_index=True) - ) + global_table_indexes.append(extract_index(index, global_index=True)) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -141,28 +156,22 @@ def create_table(table_name, region=None, key=None, keyid=None, profile=None, throughput=throughput, indexes=local_table_indexes, global_indexes=global_table_indexes, - connection=conn + connection=conn, ) # Table creation can take several seconds to propagate. # We will check MAX_ATTEMPTS times. MAX_ATTEMPTS = 30 for i in range(MAX_ATTEMPTS): - if exists( - table_name, - region, - key, - keyid, - profile - ): + if exists(table_name, region, key, keyid, profile): return True else: - time.sleep(1) # sleep for one second and try again + time.sleep(1) # sleep for one second and try again return False def exists(table_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if a table exists. CLI Example: @@ -170,12 +179,12 @@ def exists(table_name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_dynamodb.exists table_name region=us-east-1 - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.describe_table(table_name) except JSONResponseError as e: - if e.error_code == 'ResourceNotFoundException': + if e.error_code == "ResourceNotFoundException": return False raise @@ -183,7 +192,7 @@ def exists(table_name, region=None, key=None, keyid=None, profile=None): def delete(table_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete a DynamoDB table. CLI Example: @@ -191,7 +200,7 @@ def delete(table_name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_dynamodb.delete table_name region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) table = Table(table_name, connection=conn) @@ -204,27 +213,35 @@ def delete(table_name, region=None, key=None, keyid=None, profile=None): if not exists(table_name, region, key, keyid, profile): return True else: - time.sleep(1) # sleep for one second and try again + time.sleep(1) # sleep for one second and try again return False -def update(table_name, throughput=None, global_indexes=None, - region=None, key=None, keyid=None, profile=None): - ''' +def update( + table_name, + throughput=None, + global_indexes=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Update a DynamoDB table. CLI example:: salt myminion boto_dynamodb.update table_name region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) table = Table(table_name, connection=conn) return table.update(throughput=throughput, global_indexes=global_indexes) -def create_global_secondary_index(table_name, global_index, region=None, - key=None, keyid=None, profile=None): - ''' +def create_global_secondary_index( + table_name, global_index, region=None, key=None, keyid=None, profile=None +): + """ Creates a single global secondary index on a DynamoDB table. CLI Example: @@ -232,15 +249,16 @@ def create_global_secondary_index(table_name, global_index, region=None, salt myminion boto_dynamodb.create_global_secondary_index table_name / index_name - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) table = Table(table_name, connection=conn) return table.create_global_secondary_index(global_index) -def update_global_secondary_index(table_name, global_indexes, region=None, - key=None, keyid=None, profile=None): - ''' +def update_global_secondary_index( + table_name, global_indexes, region=None, key=None, keyid=None, profile=None +): + """ Updates the throughput of the given global secondary indexes. CLI Example: @@ -248,106 +266,104 @@ def update_global_secondary_index(table_name, global_indexes, region=None, salt myminion boto_dynamodb.update_global_secondary_index table_name / indexes - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) table = Table(table_name, connection=conn) return table.update_global_secondary_index(global_indexes) def describe(table_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Describe a DynamoDB table. CLI example:: salt myminion boto_dynamodb.describe table_name region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) table = Table(table_name, connection=conn) return table.describe() def extract_index(index_data, global_index=False): - ''' + """ Instantiates and returns an AllIndex object given a valid index configuration CLI Example: salt myminion boto_dynamodb.extract_index index - ''' + """ parsed_data = {} keys = [] for key, value in six.iteritems(index_data): for item in value: for field, data in six.iteritems(item): - if field == 'hash_key': - parsed_data['hash_key'] = data - elif field == 'hash_key_data_type': - parsed_data['hash_key_data_type'] = data - elif field == 'range_key': - parsed_data['range_key'] = data - elif field == 'range_key_data_type': - parsed_data['range_key_data_type'] = data - elif field == 'name': - parsed_data['name'] = data - elif field == 'read_capacity_units': - parsed_data['read_capacity_units'] = data - elif field == 'write_capacity_units': - parsed_data['write_capacity_units'] = data - elif field == 'includes': - parsed_data['includes'] = data - elif field == 'keys_only': - parsed_data['keys_only'] = True + if field == "hash_key": + parsed_data["hash_key"] = data + elif field == "hash_key_data_type": + parsed_data["hash_key_data_type"] = data + elif field == "range_key": + parsed_data["range_key"] = data + elif field == "range_key_data_type": + parsed_data["range_key_data_type"] = data + elif field == "name": + parsed_data["name"] = data + elif field == "read_capacity_units": + parsed_data["read_capacity_units"] = data + elif field == "write_capacity_units": + parsed_data["write_capacity_units"] = data + elif field == "includes": + parsed_data["includes"] = data + elif field == "keys_only": + parsed_data["keys_only"] = True - if parsed_data['hash_key']: + if parsed_data["hash_key"]: keys.append( HashKey( - parsed_data['hash_key'], - data_type=parsed_data['hash_key_data_type'] + parsed_data["hash_key"], data_type=parsed_data["hash_key_data_type"] ) ) - if parsed_data.get('range_key'): + if parsed_data.get("range_key"): keys.append( RangeKey( - parsed_data['range_key'], - data_type=parsed_data['range_key_data_type'] + parsed_data["range_key"], data_type=parsed_data["range_key_data_type"] ) ) if ( - global_index and - parsed_data['read_capacity_units'] and - parsed_data['write_capacity_units']): - parsed_data['throughput'] = { - 'read': parsed_data['read_capacity_units'], - 'write': parsed_data['write_capacity_units'] + global_index + and parsed_data["read_capacity_units"] + and parsed_data["write_capacity_units"] + ): + parsed_data["throughput"] = { + "read": parsed_data["read_capacity_units"], + "write": parsed_data["write_capacity_units"], } - if parsed_data['name'] and len(keys) > 0: + if parsed_data["name"] and len(keys) > 0: if global_index: - if parsed_data.get('keys_only') and parsed_data.get('includes'): - raise SaltInvocationError('Only one type of GSI projection can be used.') - - if parsed_data.get('includes'): - return GlobalIncludeIndex( - parsed_data['name'], - parts=keys, - throughput=parsed_data['throughput'], - includes=parsed_data['includes'] + if parsed_data.get("keys_only") and parsed_data.get("includes"): + raise SaltInvocationError( + "Only one type of GSI projection can be used." ) - elif parsed_data.get('keys_only'): - return GlobalKeysOnlyIndex( - parsed_data['name'], + + if parsed_data.get("includes"): + return GlobalIncludeIndex( + parsed_data["name"], parts=keys, - throughput=parsed_data['throughput'], + throughput=parsed_data["throughput"], + includes=parsed_data["includes"], + ) + elif parsed_data.get("keys_only"): + return GlobalKeysOnlyIndex( + parsed_data["name"], + parts=keys, + throughput=parsed_data["throughput"], ) else: return GlobalAllIndex( - parsed_data['name'], + parsed_data["name"], parts=keys, - throughput=parsed_data['throughput'] + throughput=parsed_data["throughput"], ) else: - return AllIndex( - parsed_data['name'], - parts=keys - ) + return AllIndex(parsed_data["name"], parts=keys) diff --git a/salt/modules/boto_ec2.py b/salt/modules/boto_ec2.py index 27566ffb8b0..30081f3448d 100644 --- a/salt/modules/boto_ec2.py +++ b/salt/modules/boto_ec2.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon EC2 .. versionadded:: 2015.8.0 @@ -39,12 +39,13 @@ as a passed in dict, or as a string to pull from pillars or minion config: :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time @@ -53,18 +54,23 @@ import salt.utils.compat import salt.utils.data import salt.utils.json import salt.utils.versions +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six from salt.ext.six.moves import map -from salt.exceptions import SaltInvocationError, CommandExecutionError # Import third party libs try: # pylint: disable=unused-import import boto import boto.ec2 + # pylint: enable=unused-import from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType - from boto.ec2.networkinterface import NetworkInterfaceSpecification, NetworkInterfaceCollection + from boto.ec2.networkinterface import ( + NetworkInterfaceSpecification, + NetworkInterfaceCollection, + ) + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -74,31 +80,31 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # the boto_ec2 execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 has_boto_reqs = salt.utils.versions.check_boto_reqs( - boto_ver='2.8.0', - check_boto3=False + boto_ver="2.8.0", check_boto3=False ) if has_boto_reqs is True: - __utils__['boto.assign_funcs'](__name__, 'ec2', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "ec2", pack=__salt__) return has_boto_reqs def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto.assign_funcs'](__name__, 'ec2') + __utils__["boto.assign_funcs"](__name__, "ec2") -def _get_all_eip_addresses(addresses=None, allocation_ids=None, region=None, - key=None, keyid=None, profile=None): - ''' +def _get_all_eip_addresses( + addresses=None, allocation_ids=None, region=None, key=None, keyid=None, profile=None +): + """ Get all EIP's associated with the current credentials. addresses @@ -110,19 +116,22 @@ def _get_all_eip_addresses(addresses=None, allocation_ids=None, region=None, returns (list) - The requested Addresses as a list of :class:`boto.ec2.address.Address` - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - return conn.get_all_addresses(addresses=addresses, allocation_ids=allocation_ids) + return conn.get_all_addresses( + addresses=addresses, allocation_ids=allocation_ids + ) except boto.exception.BotoServerError as e: log.error(e) return [] -def get_all_eip_addresses(addresses=None, allocation_ids=None, region=None, - key=None, keyid=None, profile=None): - ''' +def get_all_eip_addresses( + addresses=None, allocation_ids=None, region=None, key=None, keyid=None, profile=None +): + """ Get public addresses of some, or all EIPs associated with the current account. addresses @@ -142,14 +151,19 @@ def get_all_eip_addresses(addresses=None, allocation_ids=None, region=None, salt-call boto_ec2.get_all_eip_addresses .. versionadded:: 2016.3.0 - ''' - return [x.public_ip for x in _get_all_eip_addresses(addresses, allocation_ids, region, - key, keyid, profile)] + """ + return [ + x.public_ip + for x in _get_all_eip_addresses( + addresses, allocation_ids, region, key, keyid, profile + ) + ] -def get_unassociated_eip_address(domain='standard', region=None, key=None, - keyid=None, profile=None): - ''' +def get_unassociated_eip_address( + domain="standard", region=None, key=None, keyid=None, profile=None +): + """ Return the first unassociated EIP domain @@ -163,40 +177,49 @@ def get_unassociated_eip_address(domain='standard', region=None, key=None, salt-call boto_ec2.get_unassociated_eip_address .. versionadded:: 2016.3.0 - ''' + """ eip = None - for address in get_all_eip_addresses(region=region, key=key, keyid=keyid, - profile=profile): - address_info = get_eip_address_info(addresses=address, region=region, - key=key, keyid=keyid, - profile=profile)[0] - if address_info['instance_id']: - log.debug('%s is already associated with the instance %s', - address, address_info['instance_id']) + for address in get_all_eip_addresses( + region=region, key=key, keyid=keyid, profile=profile + ): + address_info = get_eip_address_info( + addresses=address, region=region, key=key, keyid=keyid, profile=profile + )[0] + if address_info["instance_id"]: + log.debug( + "%s is already associated with the instance %s", + address, + address_info["instance_id"], + ) continue - if address_info['network_interface_id']: - log.debug('%s is already associated with the network interface %s', - address, address_info['network_interface_id']) + if address_info["network_interface_id"]: + log.debug( + "%s is already associated with the network interface %s", + address, + address_info["network_interface_id"], + ) continue - if address_info['domain'] == domain: + if address_info["domain"] == domain: log.debug( "The first unassociated EIP address in the domain '%s' is %s", - domain, address + domain, + address, ) eip = address break if not eip: - log.debug('No unassociated Elastic IP found!') + log.debug("No unassociated Elastic IP found!") return eip -def get_eip_address_info(addresses=None, allocation_ids=None, region=None, key=None, - keyid=None, profile=None): - ''' +def get_eip_address_info( + addresses=None, allocation_ids=None, region=None, key=None, keyid=None, profile=None +): + """ Get 'interesting' info about some, or all EIPs associated with the current account. addresses @@ -216,24 +239,37 @@ def get_eip_address_info(addresses=None, allocation_ids=None, region=None, key=N salt-call boto_ec2.get_eip_address_info addresses=52.4.2.15 .. versionadded:: 2016.3.0 - ''' - if type(addresses) == (type('string')): + """ + if type(addresses) == (type("string")): addresses = [addresses] - if type(allocation_ids) == (type('string')): + if type(allocation_ids) == (type("string")): allocation_ids = [allocation_ids] - ret = _get_all_eip_addresses(addresses=addresses, allocation_ids=allocation_ids, - region=region, key=key, keyid=keyid, profile=profile) + ret = _get_all_eip_addresses( + addresses=addresses, + allocation_ids=allocation_ids, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) - interesting = ['allocation_id', 'association_id', 'domain', 'instance_id', - 'network_interface_id', 'network_interface_owner_id', 'public_ip', - 'private_ip_address'] + interesting = [ + "allocation_id", + "association_id", + "domain", + "instance_id", + "network_interface_id", + "network_interface_owner_id", + "public_ip", + "private_ip_address", + ] return [dict([(x, getattr(address, x)) for x in interesting]) for address in ret] def allocate_eip_address(domain=None, region=None, key=None, keyid=None, profile=None): - ''' + """ Allocate a new Elastic IP address and associate it with your account. domain @@ -253,9 +289,11 @@ def allocate_eip_address(domain=None, region=None, key=None, keyid=None, profile salt-call boto_ec2.allocate_eip_address domain=vpc .. versionadded:: 2016.3.0 - ''' - if domain and domain != 'vpc': - raise SaltInvocationError('The only permitted value for the \'domain\' param is \'vpc\'.') + """ + if domain and domain != "vpc": + raise SaltInvocationError( + "The only permitted value for the 'domain' param is 'vpc'." + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -265,16 +303,24 @@ def allocate_eip_address(domain=None, region=None, key=None, keyid=None, profile log.error(e) return False - interesting = ['allocation_id', 'association_id', 'domain', 'instance_id', - 'network_interface_id', 'network_interface_owner_id', 'public_ip', - 'private_ip_address'] + interesting = [ + "allocation_id", + "association_id", + "domain", + "instance_id", + "network_interface_id", + "network_interface_owner_id", + "public_ip", + "private_ip_address", + ] return dict([(x, getattr(address, x)) for x in interesting]) -def release_eip_address(public_ip=None, allocation_id=None, region=None, key=None, - keyid=None, profile=None): - ''' +def release_eip_address( + public_ip=None, allocation_id=None, region=None, key=None, keyid=None, profile=None +): + """ Free an Elastic IP address. Pass either a public IP address to release an EC2 Classic EIP, or an AllocationId to release a VPC EIP. @@ -293,10 +339,11 @@ def release_eip_address(public_ip=None, allocation_id=None, region=None, key=Non salt myminion boto_ec2.release_eip_address allocation_id=eipalloc-ef382c8a .. versionadded:: 2016.3.0 - ''' + """ if not salt.utils.data.exactly_one((public_ip, allocation_id)): - raise SaltInvocationError("Exactly one of 'public_ip' OR " - "'allocation_id' must be provided") + raise SaltInvocationError( + "Exactly one of 'public_ip' OR " "'allocation_id' must be provided" + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -307,12 +354,21 @@ def release_eip_address(public_ip=None, allocation_id=None, region=None, key=Non return False -def associate_eip_address(instance_id=None, instance_name=None, public_ip=None, - allocation_id=None, network_interface_id=None, - network_interface_name=None, private_ip_address=None, - allow_reassociation=False, region=None, key=None, - keyid=None, profile=None): - ''' +def associate_eip_address( + instance_id=None, + instance_name=None, + public_ip=None, + allocation_id=None, + network_interface_id=None, + network_interface_name=None, + private_ip_address=None, + allow_reassociation=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Associate an Elastic IP address with a currently running instance or a network interface. This requires exactly one of either 'public_ip' or 'allocation_id', depending on whether you’re associating a VPC address or a plain EC2 address. @@ -344,58 +400,71 @@ def associate_eip_address(instance_id=None, instance_name=None, public_ip=None, salt myminion boto_ec2.associate_eip_address instance_name=bubba.ho.tep allocation_id=eipalloc-ef382c8a .. versionadded:: 2016.3.0 - ''' - if not salt.utils.data.exactly_one((instance_id, instance_name, - network_interface_id, - network_interface_name)): - raise SaltInvocationError("Exactly one of 'instance_id', " - "'instance_name', 'network_interface_id', " - "'network_interface_name' must be provided") + """ + if not salt.utils.data.exactly_one( + (instance_id, instance_name, network_interface_id, network_interface_name) + ): + raise SaltInvocationError( + "Exactly one of 'instance_id', " + "'instance_name', 'network_interface_id', " + "'network_interface_name' must be provided" + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if instance_name: try: - instance_id = get_id(name=instance_name, region=region, key=key, - keyid=keyid, profile=profile) + instance_id = get_id( + name=instance_name, region=region, key=key, keyid=keyid, profile=profile + ) except boto.exception.BotoServerError as e: log.error(e) return False if not instance_id: log.error( "Given instance_name '%s' cannot be mapped to an instance_id", - instance_name + instance_name, ) return False if network_interface_name: try: network_interface_id = get_network_interface_id( - network_interface_name, region=region, key=key, keyid=keyid, - profile=profile) + network_interface_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) except boto.exception.BotoServerError as e: log.error(e) return False if not network_interface_id: - log.error("Given network_interface_name '%s' cannot be mapped to " - "an network_interface_id", network_interface_name) + log.error( + "Given network_interface_name '%s' cannot be mapped to " + "an network_interface_id", + network_interface_name, + ) return False try: - return conn.associate_address(instance_id=instance_id, - public_ip=public_ip, - allocation_id=allocation_id, - network_interface_id=network_interface_id, - private_ip_address=private_ip_address, - allow_reassociation=allow_reassociation) + return conn.associate_address( + instance_id=instance_id, + public_ip=public_ip, + allocation_id=allocation_id, + network_interface_id=network_interface_id, + private_ip_address=private_ip_address, + allow_reassociation=allow_reassociation, + ) except boto.exception.BotoServerError as e: log.error(e) return False -def disassociate_eip_address(public_ip=None, association_id=None, region=None, - key=None, keyid=None, profile=None): - ''' +def disassociate_eip_address( + public_ip=None, association_id=None, region=None, key=None, keyid=None, profile=None +): + """ Disassociate an Elastic IP address from a currently running instance. This requires exactly one of either 'association_id' or 'public_ip', depending on whether you’re dealing with a VPC or EC2 Classic address. @@ -415,7 +484,7 @@ def disassociate_eip_address(public_ip=None, association_id=None, region=None, salt myminion boto_ec2.disassociate_eip_address association_id=eipassoc-e3ba2d16 .. versionadded:: 2016.3.0 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -425,11 +494,18 @@ def disassociate_eip_address(public_ip=None, association_id=None, region=None, return False -def assign_private_ip_addresses(network_interface_name=None, network_interface_id=None, - private_ip_addresses=None, secondary_private_ip_address_count=None, - allow_reassignment=False, region=None, key=None, - keyid=None, profile=None): - ''' +def assign_private_ip_addresses( + network_interface_name=None, + network_interface_id=None, + private_ip_addresses=None, + secondary_private_ip_address_count=None, + allow_reassignment=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Assigns one or more secondary private IP addresses to a network interface. network_interface_id @@ -454,41 +530,57 @@ def assign_private_ip_addresses(network_interface_name=None, network_interface_i salt myminion boto_ec2.assign_private_ip_addresses network_interface_name=my_eni secondary_private_ip_address_count=2 .. versionadded:: 2017.7.0 - ''' - if not salt.utils.data.exactly_one((network_interface_name, - network_interface_id)): - raise SaltInvocationError("Exactly one of 'network_interface_name', " - "'network_interface_id' must be provided") + """ + if not salt.utils.data.exactly_one((network_interface_name, network_interface_id)): + raise SaltInvocationError( + "Exactly one of 'network_interface_name', " + "'network_interface_id' must be provided" + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if network_interface_name: try: network_interface_id = get_network_interface_id( - network_interface_name, region=region, key=key, keyid=keyid, - profile=profile) + network_interface_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) except boto.exception.BotoServerError as e: log.error(e) return False if not network_interface_id: - log.error("Given network_interface_name '%s' cannot be mapped to " - "an network_interface_id", network_interface_name) + log.error( + "Given network_interface_name '%s' cannot be mapped to " + "an network_interface_id", + network_interface_name, + ) return False try: - return conn.assign_private_ip_addresses(network_interface_id=network_interface_id, - private_ip_addresses=private_ip_addresses, - secondary_private_ip_address_count=secondary_private_ip_address_count, - allow_reassignment=allow_reassignment) + return conn.assign_private_ip_addresses( + network_interface_id=network_interface_id, + private_ip_addresses=private_ip_addresses, + secondary_private_ip_address_count=secondary_private_ip_address_count, + allow_reassignment=allow_reassignment, + ) except boto.exception.BotoServerError as e: log.error(e) return False -def unassign_private_ip_addresses(network_interface_name=None, network_interface_id=None, - private_ip_addresses=None, region=None, - key=None, keyid=None, profile=None): - ''' +def unassign_private_ip_addresses( + network_interface_name=None, + network_interface_id=None, + private_ip_addresses=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Unassigns one or more secondary private IP addresses from a network interface network_interface_id @@ -508,37 +600,47 @@ def unassign_private_ip_addresses(network_interface_name=None, network_interface salt myminion boto_ec2.unassign_private_ip_addresses network_interface_name=my_eni private_ip_addresses=private_ip .. versionadded:: 2017.7.0 - ''' - if not salt.utils.data.exactly_one((network_interface_name, - network_interface_id)): - raise SaltInvocationError("Exactly one of 'network_interface_name', " - "'network_interface_id' must be provided") + """ + if not salt.utils.data.exactly_one((network_interface_name, network_interface_id)): + raise SaltInvocationError( + "Exactly one of 'network_interface_name', " + "'network_interface_id' must be provided" + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if network_interface_name: try: network_interface_id = get_network_interface_id( - network_interface_name, region=region, key=key, keyid=keyid, - profile=profile) + network_interface_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) except boto.exception.BotoServerError as e: log.error(e) return False if not network_interface_id: - log.error("Given network_interface_name '%s' cannot be mapped to " - "an network_interface_id", network_interface_name) + log.error( + "Given network_interface_name '%s' cannot be mapped to " + "an network_interface_id", + network_interface_name, + ) return False try: - return conn.unassign_private_ip_addresses(network_interface_id=network_interface_id, - private_ip_addresses=private_ip_addresses) + return conn.unassign_private_ip_addresses( + network_interface_id=network_interface_id, + private_ip_addresses=private_ip_addresses, + ) except boto.exception.BotoServerError as e: log.error(e) return False def get_zones(region=None, key=None, keyid=None, profile=None): - ''' + """ Get a list of AZs for the configured region. CLI Example: @@ -546,17 +648,26 @@ def get_zones(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_ec2.get_zones - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) return [z.name for z in conn.get_all_zones()] -def find_instances(instance_id=None, name=None, tags=None, region=None, - key=None, keyid=None, profile=None, return_objs=False, - in_states=None, filters=None): +def find_instances( + instance_id=None, + name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + return_objs=False, + in_states=None, + filters=None, +): - ''' + """ Given instance properties, find and return matching instance ids CLI Examples: @@ -568,35 +679,38 @@ def find_instances(instance_id=None, name=None, tags=None, region=None, salt myminion boto_ec2.find_instances tags='{"mytag": "value"}' salt myminion boto_ec2.find_instances filters='{"vpc-id": "vpc-12345678"}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if instance_id: - filter_parameters['instance_ids'] = [instance_id] + filter_parameters["instance_ids"] = [instance_id] if name: - filter_parameters['filters']['tag:Name'] = name + filter_parameters["filters"]["tag:Name"] = name if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value if filters: - filter_parameters['filters'].update(filters) + filter_parameters["filters"].update(filters) reservations = conn.get_all_reservations(**filter_parameters) instances = [i for r in reservations for i in r.instances] - log.debug('The filters criteria %s matched the following ' - 'instances:%s', filter_parameters, instances) + log.debug( + "The filters criteria %s matched the following " "instances:%s", + filter_parameters, + instances, + ) if in_states: instances = [i for i in instances if i.state in in_states] log.debug( - 'Limiting instance matches to those in the requested states: %s', - instances + "Limiting instance matches to those in the requested states: %s", + instances, ) if instances: if return_objs: @@ -609,10 +723,21 @@ def find_instances(instance_id=None, name=None, tags=None, region=None, return [] -def create_image(ami_name, instance_id=None, instance_name=None, tags=None, region=None, - key=None, keyid=None, profile=None, description=None, no_reboot=False, - dry_run=False, filters=None): - ''' +def create_image( + ami_name, + instance_id=None, + instance_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + description=None, + no_reboot=False, + dry_run=False, + filters=None, +): + """ Given instance properties that define exactly one instance, create AMI and return AMI-id. CLI Examples: @@ -622,32 +747,53 @@ def create_image(ami_name, instance_id=None, instance_name=None, tags=None, regi salt myminion boto_ec2.create_image ami_name instance_name=myinstance salt myminion boto_ec2.create_image another_ami_name tags='{"mytag": "value"}' description='this is my ami' - ''' + """ - instances = find_instances(instance_id=instance_id, name=instance_name, tags=tags, - region=region, key=key, keyid=keyid, profile=profile, - return_objs=True, filters=filters) + instances = find_instances( + instance_id=instance_id, + name=instance_name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + return_objs=True, + filters=filters, + ) if not instances: - log.error('Source instance not found') + log.error("Source instance not found") return False if len(instances) > 1: - log.error('Multiple instances found, must match exactly only one instance to create an image from') + log.error( + "Multiple instances found, must match exactly only one instance to create an image from" + ) return False instance = instances[0] try: - return instance.create_image(ami_name, description=description, - no_reboot=no_reboot, dry_run=dry_run) + return instance.create_image( + ami_name, description=description, no_reboot=no_reboot, dry_run=dry_run + ) except boto.exception.BotoServerError as exc: log.error(exc) return False -def find_images(ami_name=None, executable_by=None, owners=None, image_ids=None, tags=None, - region=None, key=None, keyid=None, profile=None, return_objs=False): +def find_images( + ami_name=None, + executable_by=None, + owners=None, + image_ids=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + return_objs=False, +): - ''' + """ Given image properties, find and return matching AMI ids CLI Examples: @@ -656,26 +802,29 @@ def find_images(ami_name=None, executable_by=None, owners=None, image_ids=None, salt myminion boto_ec2.find_images tags='{"mytag": "value"}' - ''' + """ retries = 30 conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) while retries: try: - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if image_ids: - filter_parameters['image_ids'] = [image_ids] + filter_parameters["image_ids"] = [image_ids] if executable_by: - filter_parameters['executable_by'] = [executable_by] + filter_parameters["executable_by"] = [executable_by] if owners: - filter_parameters['owners'] = [owners] + filter_parameters["owners"] = [owners] if ami_name: - filter_parameters['filters']['name'] = ami_name + filter_parameters["filters"]["name"] = ami_name if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value images = conn.get_all_images(**filter_parameters) - log.debug('The filters criteria %s matched the following ' - 'images:%s', filter_parameters, images) + log.debug( + "The filters criteria %s matched the following " "images:%s", + filter_parameters, + images, + ) if images: if return_objs: return images @@ -683,19 +832,26 @@ def find_images(ami_name=None, executable_by=None, owners=None, image_ids=None, else: return False except boto.exception.BotoServerError as exc: - if exc.error_code == 'Throttling': + if exc.error_code == "Throttling": log.debug("Throttled by AWS API, will retry in 5 seconds...") time.sleep(5) retries -= 1 continue - log.error('Failed to convert AMI name `%s` to an AMI ID: %s', ami_name, exc) + log.error("Failed to convert AMI name `%s` to an AMI ID: %s", ami_name, exc) return False return False -def terminate(instance_id=None, name=None, region=None, - key=None, keyid=None, profile=None, filters=None): - ''' +def terminate( + instance_id=None, + name=None, + region=None, + key=None, + keyid=None, + profile=None, + filters=None, +): + """ Terminate the instance described by instance_id or name. CLI Example: @@ -704,11 +860,17 @@ def terminate(instance_id=None, name=None, region=None, salt myminion boto_ec2.terminate name=myinstance salt myminion boto_ec2.terminate instance_id=i-a46b9f - ''' - instances = find_instances(instance_id=instance_id, name=name, - region=region, key=key, keyid=keyid, - profile=profile, return_objs=True, - filters=filters) + """ + instances = find_instances( + instance_id=instance_id, + name=name, + region=region, + key=key, + keyid=keyid, + profile=profile, + return_objs=True, + filters=filters, + ) if instances in (False, None, []): return instances @@ -716,14 +878,22 @@ def terminate(instance_id=None, name=None, region=None, instances[0].terminate() return True else: - log.warning('Refusing to terminate multiple instances at once') + log.warning("Refusing to terminate multiple instances at once") return False -def get_id(name=None, tags=None, region=None, key=None, - keyid=None, profile=None, in_states=None, filters=None): +def get_id( + name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + in_states=None, + filters=None, +): - ''' + """ Given instance properties, return the instance id if it exists. CLI Example: @@ -732,25 +902,32 @@ def get_id(name=None, tags=None, region=None, key=None, salt myminion boto_ec2.get_id myinstance - ''' - instance_ids = find_instances(name=name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile, in_states=in_states, - filters=filters) + """ + instance_ids = find_instances( + name=name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + in_states=in_states, + filters=filters, + ) if instance_ids: log.info("Instance ids: %s", " ".join(instance_ids)) if len(instance_ids) == 1: return instance_ids[0] else: - raise CommandExecutionError('Found more than one instance ' - 'matching the criteria.') + raise CommandExecutionError( + "Found more than one instance " "matching the criteria." + ) else: - log.warning('Could not find instance.') + log.warning("Could not find instance.") return None -def get_tags(instance_id=None, keyid=None, key=None, profile=None, - region=None): - ''' +def get_tags(instance_id=None, keyid=None, key=None, profile=None, region=None): + """ Given an instance_id, return a list of tags associated with that instance. returns @@ -761,7 +938,7 @@ def get_tags(instance_id=None, keyid=None, key=None, profile=None, .. code-block:: bash salt myminion boto_ec2.get_tags instance_id - ''' + """ tags = [] client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) result = client.get_all_tags(filters={"resource-id": instance_id}) @@ -773,9 +950,18 @@ def get_tags(instance_id=None, keyid=None, key=None, profile=None, return tags -def exists(instance_id=None, name=None, tags=None, region=None, key=None, - keyid=None, profile=None, in_states=None, filters=None): - ''' +def exists( + instance_id=None, + name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + in_states=None, + filters=None, +): + """ Given an instance id, check to see if the given instance id exists. Returns True if the given instance with the given id, name, or tags @@ -786,20 +972,28 @@ def exists(instance_id=None, name=None, tags=None, region=None, key=None, .. code-block:: bash salt myminion boto_ec2.exists myinstance - ''' - instances = find_instances(instance_id=instance_id, name=name, tags=tags, - region=region, key=key, keyid=keyid, - profile=profile, in_states=in_states, filters=filters) + """ + instances = find_instances( + instance_id=instance_id, + name=name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + in_states=in_states, + filters=filters, + ) if instances: - log.info('Instance exists.') + log.info("Instance exists.") return True else: - log.warning('Instance does not exist.') + log.warning("Instance does not exist.") return False def _to_blockdev_map(thing): - ''' + """ Convert a string, or a json payload, or a dict in the right format, into a boto.ec2.blockdevicemapping.BlockDeviceMapping as needed by instance_present(). The following YAML is a direct @@ -821,7 +1015,7 @@ def _to_blockdev_map(thing): size: 20 volume_type: gp2 - ''' + """ if not thing: return None if isinstance(thing, BlockDeviceMapping): @@ -829,41 +1023,73 @@ def _to_blockdev_map(thing): if isinstance(thing, six.string_types): thing = salt.utils.json.loads(thing) if not isinstance(thing, dict): - log.error("Can't convert '%s' of type %s to a " - "boto.ec2.blockdevicemapping.BlockDeviceMapping", thing, type(thing)) + log.error( + "Can't convert '%s' of type %s to a " + "boto.ec2.blockdevicemapping.BlockDeviceMapping", + thing, + type(thing), + ) return None bdm = BlockDeviceMapping() for d, t in six.iteritems(thing): - bdt = BlockDeviceType(ephemeral_name=t.get('ephemeral_name'), - no_device=t.get('no_device', False), - volume_id=t.get('volume_id'), - snapshot_id=t.get('snapshot_id'), - status=t.get('status'), - attach_time=t.get('attach_time'), - delete_on_termination=t.get('delete_on_termination', False), - size=t.get('size'), - volume_type=t.get('volume_type'), - iops=t.get('iops'), - encrypted=t.get('encrypted')) + bdt = BlockDeviceType( + ephemeral_name=t.get("ephemeral_name"), + no_device=t.get("no_device", False), + volume_id=t.get("volume_id"), + snapshot_id=t.get("snapshot_id"), + status=t.get("status"), + attach_time=t.get("attach_time"), + delete_on_termination=t.get("delete_on_termination", False), + size=t.get("size"), + volume_type=t.get("volume_type"), + iops=t.get("iops"), + encrypted=t.get("encrypted"), + ) bdm[d] = bdt return bdm -def run(image_id, name=None, tags=None, key_name=None, security_groups=None, - user_data=None, instance_type='m1.small', placement=None, - kernel_id=None, ramdisk_id=None, monitoring_enabled=None, vpc_id=None, - vpc_name=None, subnet_id=None, subnet_name=None, private_ip_address=None, - block_device_map=None, disable_api_termination=None, - instance_initiated_shutdown_behavior=None, placement_group=None, - client_token=None, security_group_ids=None, security_group_names=None, - additional_info=None, tenancy=None, instance_profile_arn=None, - instance_profile_name=None, ebs_optimized=None, - network_interface_id=None, network_interface_name=None, - region=None, key=None, keyid=None, profile=None, network_interfaces=None): - #TODO: support multi-instance reservations - ''' +def run( + image_id, + name=None, + tags=None, + key_name=None, + security_groups=None, + user_data=None, + instance_type="m1.small", + placement=None, + kernel_id=None, + ramdisk_id=None, + monitoring_enabled=None, + vpc_id=None, + vpc_name=None, + subnet_id=None, + subnet_name=None, + private_ip_address=None, + block_device_map=None, + disable_api_termination=None, + instance_initiated_shutdown_behavior=None, + placement_group=None, + client_token=None, + security_group_ids=None, + security_group_names=None, + additional_info=None, + tenancy=None, + instance_profile_arn=None, + instance_profile_name=None, + ebs_optimized=None, + network_interface_id=None, + network_interface_name=None, + region=None, + key=None, + keyid=None, + profile=None, + network_interfaces=None, +): + # TODO: support multi-instance reservations + """ Create and start an EC2 instance. Returns True if the instance was created; otherwise False. @@ -980,109 +1206,133 @@ def run(image_id, name=None, tags=None, key_name=None, security_groups=None, network_interface_name (string) - Name of the network interface to attach to the instance - ''' + """ if all((subnet_id, subnet_name)): - raise SaltInvocationError('Only one of subnet_name or subnet_id may be ' - 'provided.') + raise SaltInvocationError( + "Only one of subnet_name or subnet_id may be " "provided." + ) if subnet_name: - r = __salt__['boto_vpc.get_resource_id']('subnet', subnet_name, - region=region, key=key, - keyid=keyid, profile=profile) - if 'id' not in r: - log.warning('Couldn\'t resolve subnet name %s.', subnet_name) + r = __salt__["boto_vpc.get_resource_id"]( + "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile + ) + if "id" not in r: + log.warning("Couldn't resolve subnet name %s.", subnet_name) return False - subnet_id = r['id'] + subnet_id = r["id"] if all((security_group_ids, security_group_names)): - raise SaltInvocationError('Only one of security_group_ids or ' - 'security_group_names may be provided.') + raise SaltInvocationError( + "Only one of security_group_ids or " "security_group_names may be provided." + ) if security_group_names: security_group_ids = [] for sgn in security_group_names: - r = __salt__['boto_secgroup.get_group_id'](sgn, vpc_name=vpc_name, - region=region, key=key, - keyid=keyid, profile=profile) + r = __salt__["boto_secgroup.get_group_id"]( + sgn, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not r: - log.warning('Couldn\'t resolve security group name %s', sgn) + log.warning("Couldn't resolve security group name %s", sgn) return False security_group_ids += [r] - network_interface_args = list(map(int, [network_interface_id is not None, - network_interface_name is not None, - network_interfaces is not None])) + network_interface_args = list( + map( + int, + [ + network_interface_id is not None, + network_interface_name is not None, + network_interfaces is not None, + ], + ) + ) if sum(network_interface_args) > 1: - raise SaltInvocationError('Only one of network_interface_id, ' - 'network_interface_name or ' - 'network_interfaces may be provided.') + raise SaltInvocationError( + "Only one of network_interface_id, " + "network_interface_name or " + "network_interfaces may be provided." + ) if network_interface_name: - result = get_network_interface_id(network_interface_name, - region=region, key=key, - keyid=keyid, - profile=profile) - network_interface_id = result['result'] + result = get_network_interface_id( + network_interface_name, region=region, key=key, keyid=keyid, profile=profile + ) + network_interface_id = result["result"] if not network_interface_id: log.warning( "Given network_interface_name '%s' cannot be mapped to an " - "network_interface_id", network_interface_name + "network_interface_id", + network_interface_name, ) if network_interface_id: interface = NetworkInterfaceSpecification( - network_interface_id=network_interface_id, - device_index=0) + network_interface_id=network_interface_id, device_index=0 + ) else: interface = NetworkInterfaceSpecification( - subnet_id=subnet_id, - groups=security_group_ids, - device_index=0) + subnet_id=subnet_id, groups=security_group_ids, device_index=0 + ) if network_interfaces: - interfaces_specs = [NetworkInterfaceSpecification(**x) for x in network_interfaces] + interfaces_specs = [ + NetworkInterfaceSpecification(**x) for x in network_interfaces + ] interfaces = NetworkInterfaceCollection(*interfaces_specs) else: interfaces = NetworkInterfaceCollection(interface) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - reservation = conn.run_instances(image_id, key_name=key_name, security_groups=security_groups, - user_data=user_data, instance_type=instance_type, - placement=placement, kernel_id=kernel_id, ramdisk_id=ramdisk_id, - monitoring_enabled=monitoring_enabled, - private_ip_address=private_ip_address, - block_device_map=_to_blockdev_map(block_device_map), - disable_api_termination=disable_api_termination, - instance_initiated_shutdown_behavior=instance_initiated_shutdown_behavior, - placement_group=placement_group, client_token=client_token, - additional_info=additional_info, - tenancy=tenancy, instance_profile_arn=instance_profile_arn, - instance_profile_name=instance_profile_name, ebs_optimized=ebs_optimized, - network_interfaces=interfaces) + reservation = conn.run_instances( + image_id, + key_name=key_name, + security_groups=security_groups, + user_data=user_data, + instance_type=instance_type, + placement=placement, + kernel_id=kernel_id, + ramdisk_id=ramdisk_id, + monitoring_enabled=monitoring_enabled, + private_ip_address=private_ip_address, + block_device_map=_to_blockdev_map(block_device_map), + disable_api_termination=disable_api_termination, + instance_initiated_shutdown_behavior=instance_initiated_shutdown_behavior, + placement_group=placement_group, + client_token=client_token, + additional_info=additional_info, + tenancy=tenancy, + instance_profile_arn=instance_profile_arn, + instance_profile_name=instance_profile_name, + ebs_optimized=ebs_optimized, + network_interfaces=interfaces, + ) if not reservation: - log.warning('Instance could not be reserved') + log.warning("Instance could not be reserved") return False instance = reservation.instances[0] - status = 'pending' - while status == 'pending': + status = "pending" + while status == "pending": time.sleep(5) status = instance.update() - if status == 'running': + if status == "running": if name: - instance.add_tag('Name', name) + instance.add_tag("Name", name) if tags: instance.add_tags(tags) - return {'instance_id': instance.id} + return {"instance_id": instance.id} else: - log.warning( - 'Instance could not be started -- status is "%s"', - status - ) + log.warning('Instance could not be started -- status is "%s"', status) def get_key(key_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if a key exists. Returns fingerprint and name if it does and False if it doesn't CLI Example: @@ -1090,7 +1340,7 @@ def get_key(key_name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_ec2.get_key mykey - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -1104,9 +1354,8 @@ def get_key(key_name, region=None, key=None, keyid=None, profile=None): return False -def create_key(key_name, save_path, region=None, key=None, keyid=None, - profile=None): - ''' +def create_key(key_name, save_path, region=None, key=None, keyid=None, profile=None): + """ Creates a key and saves it to a given path. Returns the private key. @@ -1115,7 +1364,7 @@ def create_key(key_name, save_path, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_ec2.create_key mykey /root/ - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -1128,9 +1377,10 @@ def create_key(key_name, save_path, region=None, key=None, keyid=None, return False -def import_key(key_name, public_key_material, region=None, key=None, - keyid=None, profile=None): - ''' +def import_key( + key_name, public_key_material, region=None, key=None, keyid=None, profile=None +): + """ Imports the public key from an RSA key pair that you created with a third-party tool. Supported formats: - OpenSSH public key format (e.g., the format in ~/.ssh/authorized_keys) @@ -1144,7 +1394,7 @@ def import_key(key_name, public_key_material, region=None, key=None, .. code-block:: bash salt myminion boto_ec2.import mykey publickey - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -1157,7 +1407,7 @@ def import_key(key_name, public_key_material, region=None, key=None, def delete_key(key_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Deletes a key. Always returns True CLI Example: @@ -1165,7 +1415,7 @@ def delete_key(key_name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_ec2.delete_key mykey - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -1177,9 +1427,10 @@ def delete_key(key_name, region=None, key=None, keyid=None, profile=None): return False -def get_keys(keynames=None, filters=None, region=None, key=None, - keyid=None, profile=None): - ''' +def get_keys( + keynames=None, filters=None, region=None, key=None, keyid=None, profile=None +): + """ Gets all keys or filters them by name and returns a list. keynames (list):: A list of the names of keypairs to retrieve. If not provided, all key pairs will be returned. @@ -1194,7 +1445,7 @@ def get_keys(keynames=None, filters=None, region=None, key=None, .. code-block:: bash salt myminion boto_ec2.get_keys - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -1210,9 +1461,17 @@ def get_keys(keynames=None, filters=None, region=None, key=None, return False -def get_attribute(attribute, instance_name=None, instance_id=None, region=None, key=None, - keyid=None, profile=None, filters=None): - ''' +def get_attribute( + attribute, + instance_name=None, + instance_id=None, + region=None, + key=None, + keyid=None, + profile=None, + filters=None, +): + """ Get an EC2 instance attribute. CLI Example: @@ -1235,28 +1494,52 @@ def get_attribute(attribute, instance_name=None, instance_id=None, region=None, * groupSet * ebsOptimized * sriovNetSupport - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - attribute_list = ['instanceType', 'kernel', 'ramdisk', 'userData', 'disableApiTermination', - 'instanceInitiatedShutdownBehavior', 'rootDeviceName', 'blockDeviceMapping', 'productCodes', - 'sourceDestCheck', 'groupSet', 'ebsOptimized', 'sriovNetSupport'] + attribute_list = [ + "instanceType", + "kernel", + "ramdisk", + "userData", + "disableApiTermination", + "instanceInitiatedShutdownBehavior", + "rootDeviceName", + "blockDeviceMapping", + "productCodes", + "sourceDestCheck", + "groupSet", + "ebsOptimized", + "sriovNetSupport", + ] if not any((instance_name, instance_id)): - raise SaltInvocationError('At least one of the following must be specified: ' - 'instance_name or instance_id.') + raise SaltInvocationError( + "At least one of the following must be specified: " + "instance_name or instance_id." + ) if instance_name and instance_id: - raise SaltInvocationError('Both instance_name and instance_id can not be specified in the same command.') + raise SaltInvocationError( + "Both instance_name and instance_id can not be specified in the same command." + ) if attribute not in attribute_list: - raise SaltInvocationError('Attribute must be one of: {0}.'.format(attribute_list)) + raise SaltInvocationError( + "Attribute must be one of: {0}.".format(attribute_list) + ) try: if instance_name: - instances = find_instances(name=instance_name, region=region, key=key, keyid=keyid, profile=profile, - filters=filters) + instances = find_instances( + name=instance_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + filters=filters, + ) if len(instances) > 1: - log.error('Found more than one EC2 instance matching the criteria.') + log.error("Found more than one EC2 instance matching the criteria.") return False elif len(instances) < 1: - log.error('Found no EC2 instance matching the criteria.') + log.error("Found no EC2 instance matching the criteria.") return False instance_id = instances[0] instance_attribute = conn.get_instance_attribute(instance_id, attribute) @@ -1268,9 +1551,18 @@ def get_attribute(attribute, instance_name=None, instance_id=None, region=None, return False -def set_attribute(attribute, attribute_value, instance_name=None, instance_id=None, region=None, key=None, keyid=None, - profile=None, filters=None): - ''' +def set_attribute( + attribute, + attribute_value, + instance_name=None, + instance_id=None, + region=None, + key=None, + keyid=None, + profile=None, + filters=None, +): + """ Set an EC2 instance attribute. Returns whether the operation succeeded or not. @@ -1294,26 +1586,54 @@ def set_attribute(attribute, attribute_value, instance_name=None, instance_id=No * groupSet * ebsOptimized * sriovNetSupport - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - attribute_list = ['instanceType', 'kernel', 'ramdisk', 'userData', 'disableApiTermination', - 'instanceInitiatedShutdownBehavior', 'rootDeviceName', 'blockDeviceMapping', 'productCodes', - 'sourceDestCheck', 'groupSet', 'ebsOptimized', 'sriovNetSupport'] + attribute_list = [ + "instanceType", + "kernel", + "ramdisk", + "userData", + "disableApiTermination", + "instanceInitiatedShutdownBehavior", + "rootDeviceName", + "blockDeviceMapping", + "productCodes", + "sourceDestCheck", + "groupSet", + "ebsOptimized", + "sriovNetSupport", + ] if not any((instance_name, instance_id)): - raise SaltInvocationError('At least one of the following must be specified: instance_name or instance_id.') + raise SaltInvocationError( + "At least one of the following must be specified: instance_name or instance_id." + ) if instance_name and instance_id: - raise SaltInvocationError('Both instance_name and instance_id can not be specified in the same command.') + raise SaltInvocationError( + "Both instance_name and instance_id can not be specified in the same command." + ) if attribute not in attribute_list: - raise SaltInvocationError('Attribute must be one of: {0}.'.format(attribute_list)) + raise SaltInvocationError( + "Attribute must be one of: {0}.".format(attribute_list) + ) try: if instance_name: - instances = find_instances(name=instance_name, region=region, key=key, keyid=keyid, profile=profile, - filters=filters) + instances = find_instances( + name=instance_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + filters=filters, + ) if len(instances) != 1: - raise CommandExecutionError('Found more than one EC2 instance matching the criteria.') + raise CommandExecutionError( + "Found more than one EC2 instance matching the criteria." + ) instance_id = instances[0] - attribute = conn.modify_instance_attribute(instance_id, attribute, attribute_value) + attribute = conn.modify_instance_attribute( + instance_id, attribute, attribute_value + ) if not attribute: return False return attribute @@ -1322,9 +1642,8 @@ def set_attribute(attribute, attribute_value, instance_name=None, instance_id=No return False -def get_network_interface_id(name, region=None, key=None, keyid=None, - profile=None): - ''' +def get_network_interface_id(name, region=None, key=None, keyid=None, profile=None): + """ Get an Elastic Network Interface id from its name tag. .. versionadded:: 2016.3.0 @@ -1334,26 +1653,32 @@ def get_network_interface_id(name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_ec2.get_network_interface_id name=my_eni - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: - enis = conn.get_all_network_interfaces(filters={'tag:Name': name}) + enis = conn.get_all_network_interfaces(filters={"tag:Name": name}) if not enis: - r['error'] = {'message': 'No ENIs found.'} + r["error"] = {"message": "No ENIs found."} elif len(enis) > 1: - r['error'] = {'message': 'Name specified is tagged on multiple ENIs.'} + r["error"] = {"message": "Name specified is tagged on multiple ENIs."} else: eni = enis[0] - r['result'] = eni.id + r["result"] = eni.id except boto.exception.EC2ResponseError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def get_network_interface(name=None, network_interface_id=None, region=None, - key=None, keyid=None, profile=None): - ''' +def get_network_interface( + name=None, + network_interface_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get an Elastic Network Interface. .. versionadded:: 2016.3.0 @@ -1363,17 +1688,17 @@ def get_network_interface(name=None, network_interface_id=None, region=None, .. code-block:: bash salt myminion boto_ec2.get_network_interface name=my_eni - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} result = _get_network_interface(conn, name, network_interface_id) - if 'error' in result: - if result['error']['message'] == 'No ENIs found.': - r['result'] = None + if "error" in result: + if result["error"]["message"] == "No ENIs found.": + r["result"] = None return r return result - eni = result['result'] - r['result'] = _describe_network_interface(eni) + eni = result["result"] + r["result"] = _describe_network_interface(eni) return r @@ -1381,59 +1706,91 @@ def _get_network_interface(conn, name=None, network_interface_id=None): r = {} if not (name or network_interface_id): raise SaltInvocationError( - 'Either name or network_interface_id must be provided.' + "Either name or network_interface_id must be provided." ) try: if network_interface_id: enis = conn.get_all_network_interfaces([network_interface_id]) else: - enis = conn.get_all_network_interfaces(filters={'tag:Name': name}) + enis = conn.get_all_network_interfaces(filters={"tag:Name": name}) if not enis: - r['error'] = {'message': 'No ENIs found.'} + r["error"] = {"message": "No ENIs found."} elif len(enis) > 1: - r['error'] = {'message': 'Name specified is tagged on multiple ENIs.'} + r["error"] = {"message": "Name specified is tagged on multiple ENIs."} else: eni = enis[0] - r['result'] = eni + r["result"] = eni except boto.exception.EC2ResponseError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r def _describe_network_interface(eni): r = {} - for attr in ['status', 'description', 'availability_zone', 'requesterId', - 'requester_managed', 'mac_address', 'private_ip_address', - 'vpc_id', 'id', 'source_dest_check', 'owner_id', 'tags', - 'subnet_id', 'associationId', 'publicDnsName', 'owner_id', - 'ipOwnerId', 'publicIp', 'allocationId']: + for attr in [ + "status", + "description", + "availability_zone", + "requesterId", + "requester_managed", + "mac_address", + "private_ip_address", + "vpc_id", + "id", + "source_dest_check", + "owner_id", + "tags", + "subnet_id", + "associationId", + "publicDnsName", + "owner_id", + "ipOwnerId", + "publicIp", + "allocationId", + ]: if hasattr(eni, attr): r[attr] = getattr(eni, attr) - r['region'] = eni.region.name - r['groups'] = [] + r["region"] = eni.region.name + r["groups"] = [] for group in eni.groups: - r['groups'].append({'name': group.name, 'id': group.id}) - r['private_ip_addresses'] = [] + r["groups"].append({"name": group.name, "id": group.id}) + r["private_ip_addresses"] = [] for address in eni.private_ip_addresses: - r['private_ip_addresses'].append( - {'private_ip_address': address.private_ip_address, - 'primary': address.primary} + r["private_ip_addresses"].append( + { + "private_ip_address": address.private_ip_address, + "primary": address.primary, + } ) - r['attachment'] = {} - for attr in ['status', 'attach_time', 'device_index', - 'delete_on_termination', 'instance_id', - 'instance_owner_id', 'id']: + r["attachment"] = {} + for attr in [ + "status", + "attach_time", + "device_index", + "delete_on_termination", + "instance_id", + "instance_owner_id", + "id", + ]: if hasattr(eni.attachment, attr): - r['attachment'][attr] = getattr(eni.attachment, attr) + r["attachment"][attr] = getattr(eni.attachment, attr) return r -def create_network_interface(name, subnet_id=None, subnet_name=None, - private_ip_address=None, description=None, - groups=None, region=None, key=None, keyid=None, - profile=None): - ''' +def create_network_interface( + name, + subnet_id=None, + subnet_name=None, + private_ip_address=None, + description=None, + groups=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an Elastic Network Interface. .. versionadded:: 2016.3.0 @@ -1443,58 +1800,62 @@ def create_network_interface(name, subnet_id=None, subnet_name=None, .. code-block:: bash salt myminion boto_ec2.create_network_interface my_eni subnet-12345 description=my_eni groups=['my_group'] - ''' + """ if not salt.utils.data.exactly_one((subnet_id, subnet_name)): - raise SaltInvocationError('One (but not both) of subnet_id or ' - 'subnet_name must be provided.') + raise SaltInvocationError( + "One (but not both) of subnet_id or " "subnet_name must be provided." + ) if subnet_name: - resource = __salt__['boto_vpc.get_resource_id']('subnet', subnet_name, - region=region, key=key, - keyid=keyid, - profile=profile) - if 'id' not in resource: - log.warning('Couldn\'t resolve subnet name %s.', subnet_name) + resource = __salt__["boto_vpc.get_resource_id"]( + "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile + ) + if "id" not in resource: + log.warning("Couldn't resolve subnet name %s.", subnet_name) return False - subnet_id = resource['id'] + subnet_id = resource["id"] conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} result = _get_network_interface(conn, name) - if 'result' in result: - r['error'] = {'message': 'An ENI with this Name tag already exists.'} + if "result" in result: + r["error"] = {"message": "An ENI with this Name tag already exists."} return r - vpc_id = __salt__['boto_vpc.get_subnet_association']( + vpc_id = __salt__["boto_vpc.get_subnet_association"]( [subnet_id], region=region, key=key, keyid=keyid, profile=profile ) - vpc_id = vpc_id.get('vpc_id') + vpc_id = vpc_id.get("vpc_id") if not vpc_id: - msg = 'subnet_id {0} does not map to a valid vpc id.'.format(subnet_id) - r['error'] = {'message': msg} + msg = "subnet_id {0} does not map to a valid vpc id.".format(subnet_id) + r["error"] = {"message": msg} return r - _groups = __salt__['boto_secgroup.convert_to_group_ids']( - groups, vpc_id=vpc_id, region=region, key=key, - keyid=keyid, profile=profile + _groups = __salt__["boto_secgroup.convert_to_group_ids"]( + groups, vpc_id=vpc_id, region=region, key=key, keyid=keyid, profile=profile ) try: eni = conn.create_network_interface( subnet_id, private_ip_address=private_ip_address, description=description, - groups=_groups + groups=_groups, ) - eni.add_tag('Name', name) + eni.add_tag("Name", name) except boto.exception.EC2ResponseError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r - r['result'] = _describe_network_interface(eni) + r["result"] = _describe_network_interface(eni) return r def delete_network_interface( - name=None, network_interface_id=None, region=None, key=None, - keyid=None, profile=None): - ''' + name=None, + network_interface_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an Elastic Network Interface. .. versionadded:: 2016.3.0 @@ -1504,34 +1865,42 @@ def delete_network_interface( .. code-block:: bash salt myminion boto_ec2.create_network_interface my_eni subnet-12345 description=my_eni groups=['my_group'] - ''' + """ if not (name or network_interface_id): raise SaltInvocationError( - 'Either name or network_interface_id must be provided.' + "Either name or network_interface_id must be provided." ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} result = _get_network_interface(conn, name, network_interface_id) - if 'error' in result: + if "error" in result: return result - eni = result['result'] + eni = result["result"] try: info = _describe_network_interface(eni) - network_interface_id = info['id'] + network_interface_id = info["id"] except KeyError: - r['error'] = {'message': 'ID not found for this network interface.'} + r["error"] = {"message": "ID not found for this network interface."} return r try: - r['result'] = conn.delete_network_interface(network_interface_id) + r["result"] = conn.delete_network_interface(network_interface_id) except boto.exception.EC2ResponseError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def attach_network_interface(device_index, name=None, network_interface_id=None, - instance_name=None, instance_id=None, - region=None, key=None, keyid=None, profile=None): - ''' +def attach_network_interface( + device_index, + name=None, + network_interface_id=None, + instance_name=None, + instance_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Attach an Elastic Network Interface. .. versionadded:: 2016.3.0 @@ -1541,7 +1910,7 @@ def attach_network_interface(device_index, name=None, network_interface_id=None, .. code-block:: bash salt myminion boto_ec2.attach_network_interface my_eni instance_name=salt-master device_index=0 - ''' + """ if not salt.utils.data.exactly_one((name, network_interface_id)): raise SaltInvocationError( "Exactly one (but not both) of 'name' or 'network_interface_id' " @@ -1557,37 +1926,45 @@ def attach_network_interface(device_index, name=None, network_interface_id=None, conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} result = _get_network_interface(conn, name, network_interface_id) - if 'error' in result: + if "error" in result: return result - eni = result['result'] + eni = result["result"] try: info = _describe_network_interface(eni) - network_interface_id = info['id'] + network_interface_id = info["id"] except KeyError: - r['error'] = {'message': 'ID not found for this network interface.'} + r["error"] = {"message": "ID not found for this network interface."} return r if instance_name: try: - instance_id = get_id(name=instance_name, region=region, key=key, - keyid=keyid, profile=profile) + instance_id = get_id( + name=instance_name, region=region, key=key, keyid=keyid, profile=profile + ) except boto.exception.BotoServerError as e: log.error(e) return False try: - r['result'] = conn.attach_network_interface( + r["result"] = conn.attach_network_interface( network_interface_id, instance_id, device_index ) except boto.exception.EC2ResponseError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r def detach_network_interface( - name=None, network_interface_id=None, attachment_id=None, - force=False, region=None, key=None, keyid=None, profile=None): - ''' + name=None, + network_interface_id=None, + attachment_id=None, + force=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Detach an Elastic Network Interface. .. versionadded:: 2016.3.0 @@ -1597,36 +1974,42 @@ def detach_network_interface( .. code-block:: bash salt myminion boto_ec2.detach_network_interface my_eni - ''' + """ if not (name or network_interface_id or attachment_id): raise SaltInvocationError( - 'Either name or network_interface_id or attachment_id must be' - ' provided.' + "Either name or network_interface_id or attachment_id must be" " provided." ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} if not attachment_id: result = _get_network_interface(conn, name, network_interface_id) - if 'error' in result: + if "error" in result: return result - eni = result['result'] + eni = result["result"] info = _describe_network_interface(eni) try: - attachment_id = info['attachment']['id'] + attachment_id = info["attachment"]["id"] except KeyError: - r['error'] = {'message': 'Attachment id not found for this ENI.'} + r["error"] = {"message": "Attachment id not found for this ENI."} return r try: - r['result'] = conn.detach_network_interface(attachment_id, force) + r["result"] = conn.detach_network_interface(attachment_id, force) except boto.exception.EC2ResponseError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r def modify_network_interface_attribute( - name=None, network_interface_id=None, attr=None, - value=None, region=None, key=None, keyid=None, profile=None): - ''' + name=None, + network_interface_id=None, + attr=None, + value=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Modify an attribute of an Elastic Network Interface. .. versionadded:: 2016.3.0 @@ -1636,67 +2019,77 @@ def modify_network_interface_attribute( .. code-block:: bash salt myminion boto_ec2.modify_network_interface_attribute my_eni attr=description value='example description' - ''' + """ if not (name or network_interface_id): raise SaltInvocationError( - 'Either name or network_interface_id must be provided.' + "Either name or network_interface_id must be provided." ) if attr is None and value is None: - raise SaltInvocationError( - 'attr and value must be provided.' - ) + raise SaltInvocationError("attr and value must be provided.") r = {} conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) result = _get_network_interface(conn, name, network_interface_id) - if 'error' in result: + if "error" in result: return result - eni = result['result'] + eni = result["result"] info = _describe_network_interface(eni) - network_interface_id = info['id'] + network_interface_id = info["id"] # munge attr into what the API requires - if attr == 'groups': - _attr = 'groupSet' - elif attr == 'source_dest_check': - _attr = 'sourceDestCheck' - elif attr == 'delete_on_termination': - _attr = 'deleteOnTermination' + if attr == "groups": + _attr = "groupSet" + elif attr == "source_dest_check": + _attr = "sourceDestCheck" + elif attr == "delete_on_termination": + _attr = "deleteOnTermination" else: _attr = attr _value = value - if info.get('vpc_id') and _attr == 'groupSet': - _value = __salt__['boto_secgroup.convert_to_group_ids']( - value, vpc_id=info.get('vpc_id'), region=region, key=key, - keyid=keyid, profile=profile + if info.get("vpc_id") and _attr == "groupSet": + _value = __salt__["boto_secgroup.convert_to_group_ids"]( + value, + vpc_id=info.get("vpc_id"), + region=region, + key=key, + keyid=keyid, + profile=profile, ) if not _value: - r['error'] = { - 'message': ('Security groups do not map to valid security' - ' group ids') + r["error"] = { + "message": ("Security groups do not map to valid security" " group ids") } return r _attachment_id = None - if _attr == 'deleteOnTermination': + if _attr == "deleteOnTermination": try: - _attachment_id = info['attachment']['id'] + _attachment_id = info["attachment"]["id"] except KeyError: - r['error'] = { - 'message': ('No attachment id found for this ENI. The ENI must' - ' be attached before delete_on_termination can be' - ' modified') + r["error"] = { + "message": ( + "No attachment id found for this ENI. The ENI must" + " be attached before delete_on_termination can be" + " modified" + ) } return r try: - r['result'] = conn.modify_network_interface_attribute( + r["result"] = conn.modify_network_interface_attribute( network_interface_id, _attr, _value, attachment_id=_attachment_id ) except boto.exception.EC2ResponseError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def get_all_volumes(volume_ids=None, filters=None, return_objs=False, - region=None, key=None, keyid=None, profile=None): - ''' +def get_all_volumes( + volume_ids=None, + filters=None, + return_objs=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get a list of all EBS volumes, optionally filtered by provided 'filters' param .. versionadded:: 2016.11.0 @@ -1738,7 +2131,7 @@ def get_all_volumes(volume_ids=None, filters=None, return_objs=False, salt-call boto_ec2.get_all_volumes filters='{"tag:Name": "myVolume01"}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -1749,9 +2142,16 @@ def get_all_volumes(volume_ids=None, filters=None, return_objs=False, return [] -def set_volumes_tags(tag_maps, authoritative=False, dry_run=False, - region=None, key=None, keyid=None, profile=None): - ''' +def set_volumes_tags( + tag_maps, + authoritative=False, + dry_run=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ .. versionadded:: 2016.11.0 tag_maps (list) @@ -1802,82 +2202,114 @@ def set_volumes_tags(tag_maps, authoritative=False, dry_run=False, returns (dict) A dict describing status and any changes. - ''' - ret = {'success': True, 'comment': '', 'changes': {}} - running_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped') + """ + ret = {"success": True, "comment": "", "changes": {}} + running_states = ("pending", "rebooting", "running", "stopping", "stopped") ### First creeate a dictionary mapping all changes for a given volume to its volume ID... tag_sets = {} for tm in tag_maps: - filters = dict(tm.get('filters', {})) - tags = dict(tm.get('tags', {})) - args = {'return_objs': True, 'region': region, 'key': key, 'keyid': keyid, 'profile': profile} + filters = dict(tm.get("filters", {})) + tags = dict(tm.get("tags", {})) + args = { + "return_objs": True, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } new_filters = {} - log.debug('got filters: %s', filters) + log.debug("got filters: %s", filters) instance_id = None - in_states = tm.get('in_states', running_states) + in_states = tm.get("in_states", running_states) try: for k, v in filters.items(): - if k == 'volume_ids': - args['volume_ids'] = v - elif k == 'instance_name': - instance_id = get_id(name=v, in_states=in_states, region=region, key=key, - keyid=keyid, profile=profile) + if k == "volume_ids": + args["volume_ids"] = v + elif k == "instance_name": + instance_id = get_id( + name=v, + in_states=in_states, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not instance_id: msg = "Couldn't resolve instance Name {0} to an ID.".format(v) raise CommandExecutionError(msg) - new_filters['attachment.instance_id'] = instance_id + new_filters["attachment.instance_id"] = instance_id else: new_filters[k] = v except CommandExecutionError as e: log.warning(e) continue # Hmme, abort or do what we can...? Guess the latter for now. - args['filters'] = new_filters + args["filters"] = new_filters volumes = get_all_volumes(**args) - log.debug('got volume list: %s', volumes) + log.debug("got volume list: %s", volumes) for vol in volumes: - tag_sets.setdefault(vol.id.replace('-', '_'), {'vol': vol, 'tags': tags.copy()})['tags'].update(tags.copy()) - log.debug('tag_sets after munging: %s', tag_sets) + tag_sets.setdefault( + vol.id.replace("-", "_"), {"vol": vol, "tags": tags.copy()} + )["tags"].update(tags.copy()) + log.debug("tag_sets after munging: %s", tag_sets) ### ...then loop through all those volume->tag pairs and apply them. - changes = {'old': {}, 'new': {}} + changes = {"old": {}, "new": {}} for volume in tag_sets.values(): - vol, tags = volume['vol'], volume['tags'] - log.debug('current tags on vol.id %s: %s', vol.id, dict(getattr(vol, 'tags', {}))) - curr = set(dict(getattr(vol, 'tags', {})).keys()) - log.debug('requested tags on vol.id %s: %s', vol.id, tags) + vol, tags = volume["vol"], volume["tags"] + log.debug( + "current tags on vol.id %s: %s", vol.id, dict(getattr(vol, "tags", {})) + ) + curr = set(dict(getattr(vol, "tags", {})).keys()) + log.debug("requested tags on vol.id %s: %s", vol.id, tags) req = set(tags.keys()) add = list(req - curr) update = [r for r in (req & curr) if vol.tags[r] != tags[r]] remove = list(curr - req) if add or update or (authoritative and remove): - changes['old'][vol.id] = dict(getattr(vol, 'tags', {})) - changes['new'][vol.id] = tags + changes["old"][vol.id] = dict(getattr(vol, "tags", {})) + changes["new"][vol.id] = tags else: - log.debug('No changes needed for vol.id %s', vol.id) + log.debug("No changes needed for vol.id %s", vol.id) if add: d = dict((k, tags[k]) for k in add) - log.debug('New tags for vol.id %s: %s', vol.id, d) + log.debug("New tags for vol.id %s: %s", vol.id, d) if update: d = dict((k, tags[k]) for k in update) - log.debug('Updated tags for vol.id %s: %s', vol.id, d) + log.debug("Updated tags for vol.id %s: %s", vol.id, d) if not dry_run: - if not create_tags(vol.id, tags, region=region, key=key, keyid=keyid, profile=profile): - ret['success'] = False - ret['comment'] = "Failed to set tags on vol.id {0}: {1}".format(vol.id, tags) + if not create_tags( + vol.id, tags, region=region, key=key, keyid=keyid, profile=profile + ): + ret["success"] = False + ret["comment"] = "Failed to set tags on vol.id {0}: {1}".format( + vol.id, tags + ) return ret if authoritative: if remove: - log.debug('Removed tags for vol.id %s: %s', vol.id, remove) - if not delete_tags(vol.id, remove, region=region, key=key, keyid=keyid, profile=profile): - ret['success'] = False - ret['comment'] = "Failed to remove tags on vol.id {0}: {1}".format(vol.id, remove) + log.debug("Removed tags for vol.id %s: %s", vol.id, remove) + if not delete_tags( + vol.id, + remove, + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + ret["success"] = False + ret[ + "comment" + ] = "Failed to remove tags on vol.id {0}: {1}".format( + vol.id, remove + ) return ret - ret['changes'].update(changes) if changes['old'] or changes['new'] else None # pylint: disable=W0106 + if changes["old"] or changes["new"]: + ret["changes"].update(changes) return ret def get_all_tags(filters=None, region=None, key=None, keyid=None, profile=None): - ''' + """ Describe all tags matching the filter criteria, or all tags in the account otherwise. .. versionadded:: 2018.3.0 @@ -1894,7 +2326,7 @@ def get_all_tags(filters=None, region=None, key=None, keyid=None, profile=None): salt-call boto_ec2.get_all_tags '{"tag:Name": myInstanceNameTag, resource-type: instance}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: ret = conn.get_all_tags(filters) @@ -1910,7 +2342,7 @@ def get_all_tags(filters=None, region=None, key=None, keyid=None, profile=None): def create_tags(resource_ids, tags, region=None, key=None, keyid=None, profile=None): - ''' + """ Create new metadata tags for the specified resource ids. .. versionadded:: 2016.11.0 @@ -1929,7 +2361,7 @@ def create_tags(resource_ids, tags, region=None, key=None, keyid=None, profile=N salt-call boto_ec2.create_tags vol-12345678 '{"Name": "myVolume01"}' - ''' + """ if not isinstance(resource_ids, list): resource_ids = [resource_ids] @@ -1943,7 +2375,7 @@ def create_tags(resource_ids, tags, region=None, key=None, keyid=None, profile=N def delete_tags(resource_ids, tags, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete metadata tags for the specified resource ids. .. versionadded:: 2016.11.0 @@ -1966,7 +2398,7 @@ def delete_tags(resource_ids, tags, region=None, key=None, keyid=None, profile=N salt-call boto_ec2.delete_tags vol-12345678 '{"Name": "myVolume01"}' salt-call boto_ec2.delete_tags vol-12345678 '["Name","MountPoint"]' - ''' + """ if not isinstance(resource_ids, list): resource_ids = [resource_ids] @@ -1979,9 +2411,18 @@ def delete_tags(resource_ids, tags, region=None, key=None, keyid=None, profile=N return False -def detach_volume(volume_id, instance_id=None, device=None, force=False, - wait_for_detachement=False, region=None, key=None, keyid=None, profile=None): - ''' +def detach_volume( + volume_id, + instance_id=None, + device=None, + force=False, + wait_for_detachement=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Detach an EBS volume from an EC2 instance. .. versionadded:: 2016.11.0 @@ -2010,11 +2451,15 @@ def detach_volume(volume_id, instance_id=None, device=None, force=False, salt-call boto_ec2.detach_volume vol-12345678 i-87654321 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: ret = conn.detach_volume(volume_id, instance_id, device, force) - if ret and wait_for_detachement and not _wait_for_volume_available(conn, volume_id): + if ( + ret + and wait_for_detachement + and not _wait_for_volume_available(conn, volume_id) + ): timeout_msg = 'Timed out waiting for the volume status "available".' log.error(timeout_msg) return False @@ -2024,9 +2469,17 @@ def detach_volume(volume_id, instance_id=None, device=None, force=False, return False -def delete_volume(volume_id, instance_id=None, device=None, force=False, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_volume( + volume_id, + instance_id=None, + device=None, + force=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Detach an EBS volume from an EC2 instance. .. versionadded:: 2016.11.0 @@ -2045,7 +2498,7 @@ def delete_volume(volume_id, instance_id=None, device=None, force=False, salt-call boto_ec2.delete_volume vol-12345678 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: return conn.delete_volume(volume_id) @@ -2070,15 +2523,16 @@ def _wait_for_volume_available(conn, volume_id, retries=5, interval=5): if len(vols) != 1: return False vol = vols[0] - if vol.status == 'available': + if vol.status == "available": return True if i > retries: return False -def attach_volume(volume_id, instance_id, device, - region=None, key=None, keyid=None, profile=None): - ''' +def attach_volume( + volume_id, instance_id, device, region=None, key=None, keyid=None, profile=None +): + """ Attach an EBS volume to an EC2 instance. .. @@ -2098,7 +2552,7 @@ def attach_volume(volume_id, instance_id, device, salt-call boto_ec2.attach_volume vol-12345678 i-87654321 /dev/sdh - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: return conn.attach_volume(volume_id, instance_id, device) @@ -2107,10 +2561,21 @@ def attach_volume(volume_id, instance_id, device, return False -def create_volume(zone_name, size=None, snapshot_id=None, volume_type=None, - iops=None, encrypted=False, kms_key_id=None, wait_for_creation=False, - region=None, key=None, keyid=None, profile=None): - ''' +def create_volume( + zone_name, + size=None, + snapshot_id=None, + volume_type=None, + iops=None, + encrypted=False, + kms_key_id=None, + wait_for_creation=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an EBS volume to an availability zone. .. @@ -2147,23 +2612,27 @@ def create_volume(zone_name, size=None, snapshot_id=None, volume_type=None, salt-call boto_ec2.create_volume us-east-1a size=10 salt-call boto_ec2.create_volume us-east-1a snapshot_id=snap-0123abcd - ''' + """ if size is None and snapshot_id is None: - raise SaltInvocationError( - 'Size must be provided if not created from snapshot.' - ) + raise SaltInvocationError("Size must be provided if not created from snapshot.") ret = {} conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - vol = conn.create_volume(size=size, zone=zone_name, snapshot=snapshot_id, - volume_type=volume_type, iops=iops, encrypted=encrypted, - kms_key_id=kms_key_id) + vol = conn.create_volume( + size=size, + zone=zone_name, + snapshot=snapshot_id, + volume_type=volume_type, + iops=iops, + encrypted=encrypted, + kms_key_id=kms_key_id, + ) if wait_for_creation and not _wait_for_volume_available(conn, vol.id): timeout_msg = 'Timed out waiting for the volume status "available".' log.error(timeout_msg) - ret['error'] = timeout_msg + ret["error"] = timeout_msg else: - ret['result'] = vol.id + ret["result"] = vol.id except boto.exception.BotoServerError as error: - ret['error'] = __utils__['boto.get_error'](error) + ret["error"] = __utils__["boto.get_error"](error) return ret diff --git a/salt/modules/boto_efs.py b/salt/modules/boto_efs.py index 64930a6dd36..225403d5fbf 100644 --- a/salt/modules/boto_efs.py +++ b/salt/modules/boto_efs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon EFS .. versionadded:: 2017.7.0 @@ -46,47 +46,43 @@ Connection module for Amazon EFS region: us-east-1 :depends: boto3 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +# Import salt libs +import salt.utils.versions # Import 3rd-party libs from salt.ext import six + try: import boto3 + HAS_BOTO3 = True except ImportError: HAS_BOTO3 = False -# Import salt libs -import salt.utils.versions log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto3 libraries exist and if boto3 libraries are greater than a given version. - ''' - return salt.utils.versions.check_boto_reqs( - boto3_ver='1.0.0', - check_boto=False - ) + """ + return salt.utils.versions.check_boto_reqs(boto3_ver="1.0.0", check_boto=False) -def _get_conn(key=None, - keyid=None, - profile=None, - region=None, - **kwargs): - ''' +def _get_conn(key=None, keyid=None, profile=None, region=None, **kwargs): + """ Create a boto3 client connection to EFS - ''' + """ client = None if profile: if isinstance(profile, six.string_types): @@ -97,39 +93,41 @@ def _get_conn(key=None, elif key or keyid or region: profile = {} if key: - profile['key'] = key + profile["key"] = key if keyid: - profile['keyid'] = keyid + profile["keyid"] = keyid if region: - profile['region'] = region + profile["region"] = region if isinstance(profile, dict): - if 'region' in profile: - profile['region_name'] = profile['region'] - profile.pop('region', None) - if 'key' in profile: - profile['aws_secret_access_key'] = profile['key'] - profile.pop('key', None) - if 'keyid' in profile: - profile['aws_access_key_id'] = profile['keyid'] - profile.pop('keyid', None) + if "region" in profile: + profile["region_name"] = profile["region"] + profile.pop("region", None) + if "key" in profile: + profile["aws_secret_access_key"] = profile["key"] + profile.pop("key", None) + if "keyid" in profile: + profile["aws_access_key_id"] = profile["keyid"] + profile.pop("keyid", None) - client = boto3.client('efs', **profile) + client = boto3.client("efs", **profile) else: - client = boto3.client('efs') + client = boto3.client("efs") return client -def create_file_system(name, - performance_mode='generalPurpose', - keyid=None, - key=None, - profile=None, - region=None, - creation_token=None, - **kwargs): - ''' +def create_file_system( + name, + performance_mode="generalPurpose", + keyid=None, + key=None, + profile=None, + region=None, + creation_token=None, + **kwargs +): + """ Creates a new, empty file system. name @@ -151,7 +149,7 @@ def create_file_system(name, .. code-block:: bash salt 'my-minion' boto_efs.create_file_system efs-name generalPurpose - ''' + """ if creation_token is None: creation_token = name @@ -160,28 +158,31 @@ def create_file_system(name, client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) - response = client.create_file_system(CreationToken=creation_token, - PerformanceMode=performance_mode) + response = client.create_file_system( + CreationToken=creation_token, PerformanceMode=performance_mode + ) - if 'FileSystemId' in response: - client.create_tags(FileSystemId=response['FileSystemId'], Tags=tags) + if "FileSystemId" in response: + client.create_tags(FileSystemId=response["FileSystemId"], Tags=tags) - if 'Name' in response: - response['Name'] = name + if "Name" in response: + response["Name"] = name return response -def create_mount_target(filesystemid, - subnetid, - ipaddress=None, - securitygroups=None, - keyid=None, - key=None, - profile=None, - region=None, - **kwargs): - ''' +def create_mount_target( + filesystemid, + subnetid, + ipaddress=None, + securitygroups=None, + keyid=None, + key=None, + profile=None, + region=None, + **kwargs +): + """ Creates a mount target for a file system. You can then mount the file system on EC2 instances via the mount target. @@ -217,37 +218,34 @@ def create_mount_target(filesystemid, .. code-block:: bash salt 'my-minion' boto_efs.create_mount_target filesystemid subnetid - ''' + """ client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) if ipaddress is None and securitygroups is None: - return client.create_mount_target(FileSystemId=filesystemid, - SubnetId=subnetid) + return client.create_mount_target(FileSystemId=filesystemid, SubnetId=subnetid) if ipaddress is None: - return client.create_mount_target(FileSystemId=filesystemid, - SubnetId=subnetid, - SecurityGroups=securitygroups) + return client.create_mount_target( + FileSystemId=filesystemid, SubnetId=subnetid, SecurityGroups=securitygroups + ) if securitygroups is None: - return client.create_mount_target(FileSystemId=filesystemid, - SubnetId=subnetid, - IpAddress=ipaddress) + return client.create_mount_target( + FileSystemId=filesystemid, SubnetId=subnetid, IpAddress=ipaddress + ) - return client.create_mount_target(FileSystemId=filesystemid, - SubnetId=subnetid, - IpAddress=ipaddress, - SecurityGroups=securitygroups) + return client.create_mount_target( + FileSystemId=filesystemid, + SubnetId=subnetid, + IpAddress=ipaddress, + SecurityGroups=securitygroups, + ) -def create_tags(filesystemid, - tags, - keyid=None, - key=None, - profile=None, - region=None, - **kwargs): - ''' +def create_tags( + filesystemid, tags, keyid=None, key=None, profile=None, region=None, **kwargs +): + """ Creates or overwrites tags associated with a file system. Each tag is a key-value pair. If a tag key specified in the request already exists on the file system, this operation overwrites @@ -264,24 +262,21 @@ def create_tags(filesystemid, .. code-block:: bash salt 'my-minion' boto_efs.create_tags - ''' + """ client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) new_tags = [] for k, v in six.iteritems(tags): - new_tags.append({'Key': k, 'Value': v}) + new_tags.append({"Key": k, "Value": v}) client.create_tags(FileSystemId=filesystemid, Tags=new_tags) -def delete_file_system(filesystemid, - keyid=None, - key=None, - profile=None, - region=None, - **kwargs): - ''' +def delete_file_system( + filesystemid, keyid=None, key=None, profile=None, region=None, **kwargs +): + """ Deletes a file system, permanently severing access to its contents. Upon return, the file system no longer exists and you can't access any contents of the deleted file system. You can't delete a file system @@ -296,20 +291,17 @@ def delete_file_system(filesystemid, .. code-block:: bash salt 'my-minion' boto_efs.delete_file_system filesystemid - ''' + """ client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) client.delete_file_system(FileSystemId=filesystemid) -def delete_mount_target(mounttargetid, - keyid=None, - key=None, - profile=None, - region=None, - **kwargs): - ''' +def delete_mount_target( + mounttargetid, keyid=None, key=None, profile=None, region=None, **kwargs +): + """ Deletes the specified mount target. This operation forcibly breaks any mounts of the file system via the @@ -330,21 +322,17 @@ def delete_mount_target(mounttargetid, .. code-block:: bash salt 'my-minion' boto_efs.delete_mount_target mounttargetid - ''' + """ client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) client.delete_mount_target(MountTargetId=mounttargetid) -def delete_tags(filesystemid, - tags, - keyid=None, - key=None, - profile=None, - region=None, - **kwargs): - ''' +def delete_tags( + filesystemid, tags, keyid=None, key=None, profile=None, region=None, **kwargs +): + """ Deletes the specified tags from a file system. filesystemid @@ -358,21 +346,23 @@ def delete_tags(filesystemid, .. code-block:: bash salt 'my-minion' boto_efs.delete_tags - ''' + """ client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) client.delete_tags(FileSystemId=filesystemid, Tags=tags) -def get_file_systems(filesystemid=None, - keyid=None, - key=None, - profile=None, - region=None, - creation_token=None, - **kwargs): - ''' +def get_file_systems( + filesystemid=None, + keyid=None, + key=None, + profile=None, + region=None, + creation_token=None, + **kwargs +): + """ Get all EFS properties or a specific instance property if filesystemid is specified @@ -393,14 +383,15 @@ def get_file_systems(filesystemid=None, .. code-block:: bash salt 'my-minion' boto_efs.get_file_systems efs-id - ''' + """ result = None client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) if filesystemid and creation_token: - response = client.describe_file_systems(FileSystemId=filesystemid, - CreationToken=creation_token) + response = client.describe_file_systems( + FileSystemId=filesystemid, CreationToken=creation_token + ) result = response["FileSystems"] elif filesystemid: response = client.describe_file_systems(FileSystemId=filesystemid) @@ -414,21 +405,22 @@ def get_file_systems(filesystemid=None, result = response["FileSystems"] while "NextMarker" in response: - response = client.describe_file_systems( - Marker=response["NextMarker"]) + response = client.describe_file_systems(Marker=response["NextMarker"]) result.extend(response["FileSystems"]) return result -def get_mount_targets(filesystemid=None, - mounttargetid=None, - keyid=None, - key=None, - profile=None, - region=None, - **kwargs): - ''' +def get_mount_targets( + filesystemid=None, + mounttargetid=None, + keyid=None, + key=None, + profile=None, + region=None, + **kwargs +): + """ Get all the EFS mount point properties for a specific filesystemid or the properties for a specific mounttargetid. One or the other must be specified @@ -449,7 +441,7 @@ def get_mount_targets(filesystemid=None, .. code-block:: bash salt 'my-minion' boto_efs.get_mount_targets - ''' + """ result = None client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) @@ -458,8 +450,9 @@ def get_mount_targets(filesystemid=None, response = client.describe_mount_targets(FileSystemId=filesystemid) result = response["MountTargets"] while "NextMarker" in response: - response = client.describe_mount_targets(FileSystemId=filesystemid, - Marker=response["NextMarker"]) + response = client.describe_mount_targets( + FileSystemId=filesystemid, Marker=response["NextMarker"] + ) result.extend(response["MountTargets"]) elif mounttargetid: response = client.describe_mount_targets(MountTargetId=mounttargetid) @@ -468,13 +461,8 @@ def get_mount_targets(filesystemid=None, return result -def get_tags(filesystemid, - keyid=None, - key=None, - profile=None, - region=None, - **kwargs): - ''' +def get_tags(filesystemid, keyid=None, key=None, profile=None, region=None, **kwargs): + """ Return the tags associated with an EFS instance. filesystemid @@ -488,27 +476,30 @@ def get_tags(filesystemid, .. code-block:: bash salt 'my-minion' boto_efs.get_tags efs-id - ''' + """ client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) response = client.describe_tags(FileSystemId=filesystemid) result = response["Tags"] while "NextMarker" in response: - response = client.describe_tags(FileSystemId=filesystemid, - Marker=response["NextMarker"]) + response = client.describe_tags( + FileSystemId=filesystemid, Marker=response["NextMarker"] + ) result.extend(response["Tags"]) return result -def set_security_groups(mounttargetid, - securitygroup, - keyid=None, - key=None, - profile=None, - region=None, - **kwargs): - ''' +def set_security_groups( + mounttargetid, + securitygroup, + keyid=None, + key=None, + profile=None, + region=None, + **kwargs +): + """ Modifies the set of security groups in effect for a mount target mounttargetid @@ -522,8 +513,9 @@ def set_security_groups(mounttargetid, .. code-block:: bash salt 'my-minion' boto_efs.set_security_groups my-mount-target-id my-sec-group - ''' + """ client = _get_conn(key=key, keyid=keyid, profile=profile, region=region) - client.modify_mount_target_security_groups(MountTargetId=mounttargetid, - SecurityGroups=securitygroup) + client.modify_mount_target_security_groups( + MountTargetId=mounttargetid, SecurityGroups=securitygroup + ) diff --git a/salt/modules/boto_elasticache.py b/salt/modules/boto_elasticache.py index 8f9cdf8c7fc..f2008da77dc 100644 --- a/salt/modules/boto_elasticache.py +++ b/salt/modules/boto_elasticache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Elasticache .. versionadded:: 2014.7.0 @@ -40,9 +40,9 @@ Connection module for Amazon Elasticache region: us-east-1 :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 from __future__ import absolute_import, print_function, unicode_literals @@ -50,11 +50,12 @@ from __future__ import absolute_import, print_function, unicode_literals import logging import time -# Import Salt libs -from salt.ext import six -from salt.exceptions import SaltInvocationError import salt.utils.odict as odict import salt.utils.versions +from salt.exceptions import SaltInvocationError + +# Import Salt libs +from salt.ext import six log = logging.getLogger(__name__) @@ -63,35 +64,35 @@ try: # pylint: disable=unused-import import boto import boto.elasticache + # pylint: enable=unused-import import boto.utils - logging.getLogger('boto').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' - has_boto_reqs = salt.utils.versions.check_boto_reqs( - check_boto3=False - ) + """ + has_boto_reqs = salt.utils.versions.check_boto_reqs(check_boto3=False) if has_boto_reqs is True: - __utils__['boto.assign_funcs'](__name__, 'elasticache', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "elasticache", pack=__salt__) return has_boto_reqs def exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if a cache cluster exists. CLI example:: salt myminion boto_elasticache.exists myelasticache - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -103,13 +104,13 @@ def exists(name, region=None, key=None, keyid=None, profile=None): def group_exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if a replication group exists. CLI example:: salt myminion boto_elasticache.group_exists myelasticache - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -120,73 +121,82 @@ def group_exists(name, region=None, key=None, keyid=None, profile=None): return False -def create_replication_group(name, primary_cluster_id, replication_group_description, - wait=None, region=None, key=None, - keyid=None, profile=None): - ''' +def create_replication_group( + name, + primary_cluster_id, + replication_group_description, + wait=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create replication group. CLI example:: salt myminion boto_elasticache.create_replication_group myelasticache myprimarycluster description - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return None try: - cc = conn.create_replication_group(name, primary_cluster_id, - replication_group_description) + cc = conn.create_replication_group( + name, primary_cluster_id, replication_group_description + ) if not wait: - log.info('Created cache cluster %s.', name) + log.info("Created cache cluster %s.", name) return True while True: time.sleep(3) config = describe_replication_group(name, region, key, keyid, profile) if not config: return True - if config['status'] == 'available': + if config["status"] == "available": return True except boto.exception.BotoServerError as e: - msg = 'Failed to create replication group {0}.'.format(name) + msg = "Failed to create replication group {0}.".format(name) log.error(msg) log.debug(e) return {} def delete_replication_group(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete an ElastiCache replication group. CLI example:: salt myminion boto_elasticache.delete_replication_group my-replication-group \ region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return False try: conn.delete_replication_group(name) - msg = 'Deleted ElastiCache replication group {0}.'.format(name) + msg = "Deleted ElastiCache replication group {0}.".format(name) log.info(msg) return True except boto.exception.BotoServerError as e: log.debug(e) - msg = 'Failed to delete ElastiCache replication group {0}'.format(name) + msg = "Failed to delete ElastiCache replication group {0}".format(name) log.error(msg) return False -def describe_replication_group(name, region=None, key=None, keyid=None, - profile=None, parameter=None): - ''' +def describe_replication_group( + name, region=None, key=None, keyid=None, profile=None, parameter=None +): + """ Get replication group information. CLI example:: salt myminion boto_elasticache.describe_replication_group mygroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: @@ -194,46 +204,52 @@ def describe_replication_group(name, region=None, key=None, keyid=None, try: cc = conn.describe_replication_groups(name) except boto.exception.BotoServerError as e: - msg = 'Failed to get config for cache cluster {0}.'.format(name) + msg = "Failed to get config for cache cluster {0}.".format(name) log.error(msg) log.debug(e) return {} ret = odict.OrderedDict() - cc = cc['DescribeReplicationGroupsResponse']['DescribeReplicationGroupsResult'] - cc = cc['ReplicationGroups'][0] + cc = cc["DescribeReplicationGroupsResponse"]["DescribeReplicationGroupsResult"] + cc = cc["ReplicationGroups"][0] - attrs = ['status', 'description', 'primary_endpoint', - 'member_clusters', 'replication_group_id', - 'pending_modified_values', 'primary_cluster_id', - 'node_groups'] + attrs = [ + "status", + "description", + "primary_endpoint", + "member_clusters", + "replication_group_id", + "pending_modified_values", + "primary_cluster_id", + "node_groups", + ] for key, val in six.iteritems(cc): _key = boto.utils.pythonize_name(key) - if _key == 'status': + if _key == "status": if val: ret[_key] = val else: ret[_key] = None - if _key == 'description': + if _key == "description": if val: ret[_key] = val else: ret[_key] = None - if _key == 'replication_group_id': + if _key == "replication_group_id": if val: ret[_key] = val else: ret[_key] = None - if _key == 'member_clusters': + if _key == "member_clusters": if val: ret[_key] = val else: ret[_key] = None - if _key == 'node_groups': + if _key == "node_groups": if val: ret[_key] = val else: ret[_key] = None - if _key == 'pending_modified_values': + if _key == "pending_modified_values": if val: ret[_key] = val else: @@ -242,105 +258,115 @@ def describe_replication_group(name, region=None, key=None, keyid=None, def get_config(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get the configuration for a cache cluster. CLI example:: salt myminion boto_elasticache.get_config myelasticache - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return None try: - cc = conn.describe_cache_clusters(name, - show_cache_node_info=True) + cc = conn.describe_cache_clusters(name, show_cache_node_info=True) except boto.exception.BotoServerError as e: - msg = 'Failed to get config for cache cluster {0}.'.format(name) + msg = "Failed to get config for cache cluster {0}.".format(name) log.error(msg) log.debug(e) return {} - cc = cc['DescribeCacheClustersResponse']['DescribeCacheClustersResult'] - cc = cc['CacheClusters'][0] + cc = cc["DescribeCacheClustersResponse"]["DescribeCacheClustersResult"] + cc = cc["CacheClusters"][0] ret = odict.OrderedDict() - attrs = ['engine', 'cache_parameter_group', 'cache_cluster_id', - 'cache_security_groups', 'replication_group_id', - 'auto_minor_version_upgrade', 'num_cache_nodes', - 'preferred_availability_zone', 'security_groups', - 'cache_subnet_group_name', 'engine_version', 'cache_node_type', - 'notification_configuration', 'preferred_maintenance_window', - 'configuration_endpoint', 'cache_cluster_status', 'cache_nodes'] + attrs = [ + "engine", + "cache_parameter_group", + "cache_cluster_id", + "cache_security_groups", + "replication_group_id", + "auto_minor_version_upgrade", + "num_cache_nodes", + "preferred_availability_zone", + "security_groups", + "cache_subnet_group_name", + "engine_version", + "cache_node_type", + "notification_configuration", + "preferred_maintenance_window", + "configuration_endpoint", + "cache_cluster_status", + "cache_nodes", + ] for key, val in six.iteritems(cc): _key = boto.utils.pythonize_name(key) if _key not in attrs: continue - if _key == 'cache_parameter_group': + if _key == "cache_parameter_group": if val: - ret[_key] = val['CacheParameterGroupName'] + ret[_key] = val["CacheParameterGroupName"] else: ret[_key] = None - elif _key == 'cache_nodes': + elif _key == "cache_nodes": if val: ret[_key] = [k for k in val] else: ret[_key] = [] - elif _key == 'cache_security_groups': + elif _key == "cache_security_groups": if val: - ret[_key] = [k['CacheSecurityGroupName'] for k in val] + ret[_key] = [k["CacheSecurityGroupName"] for k in val] else: ret[_key] = [] - elif _key == 'configuration_endpoint': + elif _key == "configuration_endpoint": if val: - ret['port'] = val['Port'] - ret['address'] = val['Address'] + ret["port"] = val["Port"] + ret["address"] = val["Address"] else: - ret['port'] = None - ret['address'] = None - elif _key == 'notification_configuration': + ret["port"] = None + ret["address"] = None + elif _key == "notification_configuration": if val: - ret['notification_topic_arn'] = val['TopicArn'] + ret["notification_topic_arn"] = val["TopicArn"] else: - ret['notification_topic_arn'] = None + ret["notification_topic_arn"] = None else: ret[_key] = val return ret def get_node_host(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get hostname from cache node CLI example:: salt myminion boto_elasticache.get_node_host myelasticache - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return None try: - cc = conn.describe_cache_clusters(name, - show_cache_node_info=True) + cc = conn.describe_cache_clusters(name, show_cache_node_info=True) except boto.exception.BotoServerError as e: - msg = 'Failed to get config for cache cluster {0}.'.format(name) + msg = "Failed to get config for cache cluster {0}.".format(name) log.error(msg) log.debug(e) return {} - cc = cc['DescribeCacheClustersResponse']['DescribeCacheClustersResult'] - host = cc['CacheClusters'][0]['CacheNodes'][0]['Endpoint']['Address'] + cc = cc["DescribeCacheClustersResponse"]["DescribeCacheClustersResult"] + host = cc["CacheClusters"][0]["CacheNodes"][0]["Endpoint"]["Address"] return host def get_group_host(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get hostname from replication cache group CLI example:: salt myminion boto_elasticache.get_group_host myelasticachegroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: @@ -348,74 +374,82 @@ def get_group_host(name, region=None, key=None, keyid=None, profile=None): try: cc = conn.describe_replication_groups(name) except boto.exception.BotoServerError as e: - msg = 'Failed to get config for cache cluster {0}.'.format(name) + msg = "Failed to get config for cache cluster {0}.".format(name) log.error(msg) log.debug(e) return {} - cc = cc['DescribeReplicationGroupsResponse']['DescribeReplicationGroupsResult'] - cc = cc['ReplicationGroups'][0]['NodeGroups'][0]['PrimaryEndpoint'] - host = cc['Address'] + cc = cc["DescribeReplicationGroupsResponse"]["DescribeReplicationGroupsResult"] + cc = cc["ReplicationGroups"][0]["NodeGroups"][0]["PrimaryEndpoint"] + host = cc["Address"] return host -def get_all_cache_subnet_groups(name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def get_all_cache_subnet_groups( + name=None, region=None, key=None, keyid=None, profile=None +): + """ Return a list of all cache subnet groups with details CLI example:: salt myminion boto_elasticache.get_all_subnet_groups region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - marker = '' + marker = "" groups = [] while marker is not None: - ret = conn.describe_cache_subnet_groups(cache_subnet_group_name=name, - marker=marker) - trimmed = ret.get('DescribeCacheSubnetGroupsResponse', - {}).get('DescribeCacheSubnetGroupsResult', {}) - groups += trimmed.get('CacheSubnetGroups', []) - marker = trimmed.get('Marker', None) + ret = conn.describe_cache_subnet_groups( + cache_subnet_group_name=name, marker=marker + ) + trimmed = ret.get("DescribeCacheSubnetGroupsResponse", {}).get( + "DescribeCacheSubnetGroupsResult", {} + ) + groups += trimmed.get("CacheSubnetGroups", []) + marker = trimmed.get("Marker", None) if not groups: - log.debug('No ElastiCache subnet groups found.') + log.debug("No ElastiCache subnet groups found.") return groups except boto.exception.BotoServerError as e: log.error(e) return [] -def list_cache_subnet_groups(name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def list_cache_subnet_groups( + name=None, region=None, key=None, keyid=None, profile=None +): + """ Return a list of all cache subnet group names CLI example:: salt myminion boto_elasticache.list_subnet_groups region=us-east-1 - ''' - return [g['CacheSubnetGroupName'] for g in - get_all_cache_subnet_groups(name, region, key, keyid, profile)] + """ + return [ + g["CacheSubnetGroupName"] + for g in get_all_cache_subnet_groups(name, region, key, keyid, profile) + ] -def subnet_group_exists(name, tags=None, region=None, key=None, keyid=None, profile=None): - ''' +def subnet_group_exists( + name, tags=None, region=None, key=None, keyid=None, profile=None +): + """ Check to see if an ElastiCache subnet group exists. CLI example:: salt myminion boto_elasticache.subnet_group_exists my-param-group \ region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return False try: ec = conn.describe_cache_subnet_groups(cache_subnet_group_name=name) if not ec: - msg = ('ElastiCache subnet group does not exist in region {0}'.format(region)) + msg = "ElastiCache subnet group does not exist in region {0}".format(region) log.debug(msg) return False return True @@ -424,9 +458,18 @@ def subnet_group_exists(name, tags=None, region=None, key=None, keyid=None, prof return False -def create_subnet_group(name, description, subnet_ids=None, subnet_names=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_subnet_group( + name, + description, + subnet_ids=None, + subnet_names=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an ElastiCache subnet group CLI example to create an ElastiCache subnet group:: @@ -434,10 +477,11 @@ def create_subnet_group(name, description, subnet_ids=None, subnet_names=None, t salt myminion boto_elasticache.create_subnet_group my-subnet-group \ "group description" subnet_ids='[subnet-12345678, subnet-87654321]' \ region=us-east-1 - ''' + """ if not _exactly_one((subnet_ids, subnet_names)): - raise SaltInvocationError("Exactly one of either 'subnet_ids' or " - "'subnet_names' must be provided.") + raise SaltInvocationError( + "Exactly one of either 'subnet_ids' or " "'subnet_names' must be provided." + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return False @@ -446,279 +490,316 @@ def create_subnet_group(name, description, subnet_ids=None, subnet_names=None, t if subnet_names: subnet_ids = [] for n in subnet_names: - r = __salt__['boto_vpc.get_resource_id']('subnet', n, - region=region, key=key, - keyid=keyid, profile=profile) - if 'id' not in r: - log.error('Couldn\'t resolve subnet name %s to an ID.', subnet_name) + r = __salt__["boto_vpc.get_resource_id"]( + "subnet", n, region=region, key=key, keyid=keyid, profile=profile + ) + if "id" not in r: + log.error("Couldn't resolve subnet name %s to an ID.", subnet_name) return False - subnet_ids += [r['id']] + subnet_ids += [r["id"]] try: ec = conn.create_cache_subnet_group(name, description, subnet_ids) if not ec: - msg = 'Failed to create ElastiCache subnet group {0}'.format(name) + msg = "Failed to create ElastiCache subnet group {0}".format(name) log.error(msg) return False - log.info('Created ElastiCache subnet group %s', name) + log.info("Created ElastiCache subnet group %s", name) return True except boto.exception.BotoServerError as e: log.debug(e) - msg = 'Failed to create ElastiCache subnet group {0}'.format(name) + msg = "Failed to create ElastiCache subnet group {0}".format(name) log.error(msg) return False -def get_cache_subnet_group(name, region=None, key=None, keyid=None, - profile=None): - ''' +def get_cache_subnet_group(name, region=None, key=None, keyid=None, profile=None): + """ Get information about a cache subnet group. CLI example:: salt myminion boto_elasticache.get_cache_subnet_group mycache_subnet_group - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: csg = conn.describe_cache_subnet_groups(name) - csg = csg['DescribeCacheSubnetGroupsResponse'] - csg = csg['DescribeCacheSubnetGroupsResult']['CacheSubnetGroups'][0] + csg = csg["DescribeCacheSubnetGroupsResponse"] + csg = csg["DescribeCacheSubnetGroupsResult"]["CacheSubnetGroups"][0] except boto.exception.BotoServerError as e: - msg = 'Failed to get cache subnet group {0}.'.format(name) + msg = "Failed to get cache subnet group {0}.".format(name) log.error(msg) log.debug(e) return False except (IndexError, TypeError, KeyError): - msg = 'Failed to get cache subnet group {0} (2).'.format(name) + msg = "Failed to get cache subnet group {0} (2).".format(name) log.error(msg) return False ret = {} for key, val in six.iteritems(csg): - if key == 'CacheSubnetGroupName': - ret['cache_subnet_group_name'] = val - elif key == 'CacheSubnetGroupDescription': - ret['cache_subnet_group_description'] = val - elif key == 'VpcId': - ret['vpc_id'] = val - elif key == 'Subnets': - ret['subnets'] = [] + if key == "CacheSubnetGroupName": + ret["cache_subnet_group_name"] = val + elif key == "CacheSubnetGroupDescription": + ret["cache_subnet_group_description"] = val + elif key == "VpcId": + ret["vpc_id"] = val + elif key == "Subnets": + ret["subnets"] = [] for subnet in val: _subnet = {} - _subnet['subnet_id'] = subnet['SubnetIdentifier'] - _az = subnet['SubnetAvailabilityZone']['Name'] - _subnet['subnet_availability_zone'] = _az - ret['subnets'].append(_subnet) + _subnet["subnet_id"] = subnet["SubnetIdentifier"] + _az = subnet["SubnetAvailabilityZone"]["Name"] + _subnet["subnet_availability_zone"] = _az + ret["subnets"].append(_subnet) else: ret[key] = val return ret def delete_subnet_group(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete an ElastiCache subnet group. CLI example:: salt myminion boto_elasticache.delete_subnet_group my-subnet-group \ region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return False try: conn.delete_cache_subnet_group(name) - msg = 'Deleted ElastiCache subnet group {0}.'.format(name) + msg = "Deleted ElastiCache subnet group {0}.".format(name) log.info(msg) return True except boto.exception.BotoServerError as e: log.debug(e) - msg = 'Failed to delete ElastiCache subnet group {0}'.format(name) + msg = "Failed to delete ElastiCache subnet group {0}".format(name) log.error(msg) return False -def create(name, num_cache_nodes=None, engine=None, cache_node_type=None, - replication_group_id=None, engine_version=None, - cache_parameter_group_name=None, cache_subnet_group_name=None, - cache_security_group_names=None, security_group_ids=None, - snapshot_arns=None, preferred_availability_zone=None, - preferred_maintenance_window=None, port=None, - notification_topic_arn=None, auto_minor_version_upgrade=None, - wait=None, region=None, key=None, keyid=None, profile=None): - ''' +def create( + name, + num_cache_nodes=None, + engine=None, + cache_node_type=None, + replication_group_id=None, + engine_version=None, + cache_parameter_group_name=None, + cache_subnet_group_name=None, + cache_security_group_names=None, + security_group_ids=None, + snapshot_arns=None, + preferred_availability_zone=None, + preferred_maintenance_window=None, + port=None, + notification_topic_arn=None, + auto_minor_version_upgrade=None, + wait=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a cache cluster. CLI example:: salt myminion boto_elasticache.create myelasticache 1 redis cache.t1.micro cache_security_group_names='["myelasticachesg"]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.create_cache_cluster( - name, num_cache_nodes, cache_node_type, engine, - replication_group_id, engine_version, cache_parameter_group_name, - cache_subnet_group_name, cache_security_group_names, - security_group_ids, snapshot_arns, preferred_availability_zone, - preferred_maintenance_window, port, notification_topic_arn, - auto_minor_version_upgrade) + name, + num_cache_nodes, + cache_node_type, + engine, + replication_group_id, + engine_version, + cache_parameter_group_name, + cache_subnet_group_name, + cache_security_group_names, + security_group_ids, + snapshot_arns, + preferred_availability_zone, + preferred_maintenance_window, + port, + notification_topic_arn, + auto_minor_version_upgrade, + ) if not wait: - log.info('Created cache cluster %s.', name) + log.info("Created cache cluster %s.", name) return True while True: time.sleep(3) config = get_config(name, region, key, keyid, profile) if not config: return True - if config['cache_cluster_status'] == 'available': + if config["cache_cluster_status"] == "available": return True - log.info('Created cache cluster %s.', name) + log.info("Created cache cluster %s.", name) except boto.exception.BotoServerError as e: - msg = 'Failed to create cache cluster {0}.'.format(name) + msg = "Failed to create cache cluster {0}.".format(name) log.error(msg) log.debug(e) return False def delete(name, wait=False, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete a cache cluster. CLI example:: salt myminion boto_elasticache.delete myelasticache - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.delete_cache_cluster(name) if not wait: - log.info('Deleted cache cluster %s.', name) + log.info("Deleted cache cluster %s.", name) return True while True: config = get_config(name, region, key, keyid, profile) if not config: return True - if config['cache_cluster_status'] == 'deleting': + if config["cache_cluster_status"] == "deleting": return True time.sleep(2) - log.info('Deleted cache cluster %s.', name) + log.info("Deleted cache cluster %s.", name) return True except boto.exception.BotoServerError as e: - msg = 'Failed to delete cache cluster {0}.'.format(name) + msg = "Failed to delete cache cluster {0}.".format(name) log.error(msg) log.debug(e) return False -def create_cache_security_group(name, description, region=None, key=None, - keyid=None, profile=None): - ''' +def create_cache_security_group( + name, description, region=None, key=None, keyid=None, profile=None +): + """ Create a cache security group. CLI example:: salt myminion boto_elasticache.create_cache_security_group myelasticachesg 'My Cache Security Group' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) created = conn.create_cache_security_group(name, description) if created: - log.info('Created cache security group %s.', name) + log.info("Created cache security group %s.", name) return True else: - msg = 'Failed to create cache security group {0}.'.format(name) + msg = "Failed to create cache security group {0}.".format(name) log.error(msg) return False -def delete_cache_security_group(name, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_cache_security_group(name, region=None, key=None, keyid=None, profile=None): + """ Delete a cache security group. CLI example:: salt myminion boto_elasticache.delete_cache_security_group myelasticachesg 'My Cache Security Group' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) deleted = conn.delete_cache_security_group(name) if deleted: - log.info('Deleted cache security group %s.', name) + log.info("Deleted cache security group %s.", name) return True else: - msg = 'Failed to delete cache security group {0}.'.format(name) + msg = "Failed to delete cache security group {0}.".format(name) log.error(msg) return False -def authorize_cache_security_group_ingress(name, ec2_security_group_name, - ec2_security_group_owner_id, - region=None, key=None, keyid=None, - profile=None): - ''' +def authorize_cache_security_group_ingress( + name, + ec2_security_group_name, + ec2_security_group_owner_id, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Authorize network ingress from an ec2 security group to a cache security group. CLI example:: salt myminion boto_elasticache.authorize_cache_security_group_ingress myelasticachesg myec2sg 879879 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: added = conn.authorize_cache_security_group_ingress( - name, ec2_security_group_name, ec2_security_group_owner_id) + name, ec2_security_group_name, ec2_security_group_owner_id + ) if added: - msg = 'Added {0} to cache security group {1}.' + msg = "Added {0} to cache security group {1}." msg = msg.format(name, ec2_security_group_name) log.info(msg) return True else: - msg = 'Failed to add {0} to cache security group {1}.' + msg = "Failed to add {0} to cache security group {1}." msg = msg.format(name, ec2_security_group_name) log.error(msg) return False except boto.exception.EC2ResponseError as e: log.debug(e) - msg = 'Failed to add {0} to cache security group {1}.' + msg = "Failed to add {0} to cache security group {1}." msg = msg.format(name, ec2_security_group_name) log.error(msg) return False -def revoke_cache_security_group_ingress(name, ec2_security_group_name, - ec2_security_group_owner_id, - region=None, key=None, keyid=None, - profile=None): - ''' +def revoke_cache_security_group_ingress( + name, + ec2_security_group_name, + ec2_security_group_owner_id, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Revoke network ingress from an ec2 security group to a cache security group. CLI example:: salt myminion boto_elasticache.revoke_cache_security_group_ingress myelasticachesg myec2sg 879879 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: removed = conn.revoke_cache_security_group_ingress( - name, ec2_security_group_name, ec2_security_group_owner_id) + name, ec2_security_group_name, ec2_security_group_owner_id + ) if removed: - msg = 'Removed {0} from cache security group {1}.' + msg = "Removed {0} from cache security group {1}." msg = msg.format(name, ec2_security_group_name) log.info(msg) return True else: - msg = 'Failed to remove {0} from cache security group {1}.' + msg = "Failed to remove {0} from cache security group {1}." msg = msg.format(name, ec2_security_group_name) log.error(msg) return False except boto.exception.EC2ResponseError as e: log.debug(e) - msg = 'Failed to remove {0} from cache security group {1}.' + msg = "Failed to remove {0} from cache security group {1}." msg = msg.format(name, ec2_security_group_name) log.error(msg) return False diff --git a/salt/modules/boto_elasticsearch_domain.py b/salt/modules/boto_elasticsearch_domain.py index a2ef782231c..c07cf5685df 100644 --- a/salt/modules/boto_elasticsearch_domain.py +++ b/salt/modules/boto_elasticsearch_domain.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Elasticsearch Service .. versionadded:: 2016.11.0 @@ -70,34 +70,38 @@ Connection module for Amazon Elasticsearch Service :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt libs -from salt.ext import six import salt.utils.compat import salt.utils.json import salt.utils.versions from salt.exceptions import SaltInvocationError +# Import Salt libs +from salt.ext import six + log = logging.getLogger(__name__) # Import third party libs # pylint: disable=import-error try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import boto3 - #pylint: enable=unused-import + + # pylint: enable=unused-import from botocore.exceptions import ClientError - logging.getLogger('boto').setLevel(logging.CRITICAL) - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -105,28 +109,24 @@ except ImportError: def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # the boto_lambda execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 - return salt.utils.versions.check_boto_reqs( - boto_ver='2.8.0', - boto3_ver='1.4.0' - ) + return salt.utils.versions.check_boto_reqs(boto_ver="2.8.0", boto3_ver="1.4.0") def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 'es') + __utils__["boto3.assign_funcs"](__name__, "es") -def exists(DomainName, - region=None, key=None, keyid=None, profile=None): - ''' +def exists(DomainName, region=None, key=None, keyid=None, profile=None): + """ Given a domain name, check to see if the given domain exists. Returns True if the given domain exists and returns False if the given @@ -138,21 +138,20 @@ def exists(DomainName, salt myminion boto_elasticsearch_domain.exists mydomain - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: domain = conn.describe_elasticsearch_domain(DomainName=DomainName) - return {'exists': True} + return {"exists": True} except ClientError as e: - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'exists': False} - return {'error': __utils__['boto3.get_error'](e)} + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"exists": False} + return {"error": __utils__["boto3.get_error"](e)} -def status(DomainName, - region=None, key=None, keyid=None, profile=None): - ''' +def status(DomainName, region=None, key=None, keyid=None, profile=None): + """ Given a domain name describe its status. Returns a dictionary of interesting properties. @@ -163,27 +162,36 @@ def status(DomainName, salt myminion boto_elasticsearch_domain.status mydomain - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: domain = conn.describe_elasticsearch_domain(DomainName=DomainName) - if domain and 'DomainStatus' in domain: - domain = domain.get('DomainStatus', {}) - keys = ('Endpoint', 'Created', 'Deleted', - 'DomainName', 'DomainId', 'EBSOptions', 'SnapshotOptions', - 'AccessPolicies', 'Processing', 'AdvancedOptions', 'ARN', - 'ElasticsearchVersion') - return {'domain': dict([(k, domain.get(k)) for k in keys if k in domain])} + if domain and "DomainStatus" in domain: + domain = domain.get("DomainStatus", {}) + keys = ( + "Endpoint", + "Created", + "Deleted", + "DomainName", + "DomainId", + "EBSOptions", + "SnapshotOptions", + "AccessPolicies", + "Processing", + "AdvancedOptions", + "ARN", + "ElasticsearchVersion", + ) + return {"domain": dict([(k, domain.get(k)) for k in keys if k in domain])} else: - return {'domain': None} + return {"domain": None} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe(DomainName, - region=None, key=None, keyid=None, profile=None): - ''' +def describe(DomainName, region=None, key=None, keyid=None, profile=None): + """ Given a domain name describe its properties. Returns a dictionary of interesting properties. @@ -194,27 +202,45 @@ def describe(DomainName, salt myminion boto_elasticsearch_domain.describe mydomain - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: domain = conn.describe_elasticsearch_domain_config(DomainName=DomainName) - if domain and 'DomainConfig' in domain: - domain = domain['DomainConfig'] - keys = ('ElasticsearchClusterConfig', 'EBSOptions', 'AccessPolicies', - 'SnapshotOptions', 'AdvancedOptions') - return {'domain': dict([(k, domain.get(k, {}).get('Options')) for k in keys if k in domain])} + if domain and "DomainConfig" in domain: + domain = domain["DomainConfig"] + keys = ( + "ElasticsearchClusterConfig", + "EBSOptions", + "AccessPolicies", + "SnapshotOptions", + "AdvancedOptions", + ) + return { + "domain": dict( + [(k, domain.get(k, {}).get("Options")) for k in keys if k in domain] + ) + } else: - return {'domain': None} + return {"domain": None} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create(DomainName, ElasticsearchClusterConfig=None, EBSOptions=None, - AccessPolicies=None, SnapshotOptions=None, AdvancedOptions=None, - region=None, key=None, keyid=None, profile=None, - ElasticsearchVersion=None): - ''' +def create( + DomainName, + ElasticsearchClusterConfig=None, + EBSOptions=None, + AccessPolicies=None, + SnapshotOptions=None, + AdvancedOptions=None, + region=None, + key=None, + keyid=None, + profile=None, + ElasticsearchVersion=None, +): + """ Given a valid config, create a domain. Returns {created: true} if the domain was created and returns @@ -234,38 +260,48 @@ def create(DomainName, ElasticsearchClusterConfig=None, EBSOptions=None, "Condition": {"IpAddress": {"aws:SourceIp": ["127.0.0.1"]}}}]} \\ {"AutomatedSnapshotStartHour": 0} \\ {"rest.action.multi.allow_explicit_index": "true"} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} - for k in ('ElasticsearchClusterConfig', 'EBSOptions', - 'AccessPolicies', 'SnapshotOptions', 'AdvancedOptions', - 'ElasticsearchVersion'): + for k in ( + "ElasticsearchClusterConfig", + "EBSOptions", + "AccessPolicies", + "SnapshotOptions", + "AdvancedOptions", + "ElasticsearchVersion", + ): if locals()[k] is not None: val = locals()[k] if isinstance(val, six.string_types): try: val = salt.utils.json.loads(val) except ValueError as e: - return {'updated': False, 'error': 'Error parsing {0}: {1}'.format(k, e.message)} + return { + "updated": False, + "error": "Error parsing {0}: {1}".format(k, e.message), + } kwargs[k] = val - if 'AccessPolicies' in kwargs: - kwargs['AccessPolicies'] = salt.utils.json.dumps(kwargs['AccessPolicies']) - if 'ElasticsearchVersion' in kwargs: - kwargs['ElasticsearchVersion'] = six.text_type(kwargs['ElasticsearchVersion']) + if "AccessPolicies" in kwargs: + kwargs["AccessPolicies"] = salt.utils.json.dumps(kwargs["AccessPolicies"]) + if "ElasticsearchVersion" in kwargs: + kwargs["ElasticsearchVersion"] = six.text_type( + kwargs["ElasticsearchVersion"] + ) domain = conn.create_elasticsearch_domain(DomainName=DomainName, **kwargs) - if domain and 'DomainStatus' in domain: - return {'created': True} + if domain and "DomainStatus" in domain: + return {"created": True} else: - log.warning('Domain was not created') - return {'created': False} + log.warning("Domain was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} def delete(DomainName, region=None, key=None, keyid=None, profile=None): - ''' + """ Given a domain name, delete it. Returns {deleted: true} if the domain was deleted and returns @@ -277,20 +313,29 @@ def delete(DomainName, region=None, key=None, keyid=None, profile=None): salt myminion boto_elasticsearch_domain.delete mydomain - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_elasticsearch_domain(DomainName=DomainName) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def update(DomainName, ElasticsearchClusterConfig=None, EBSOptions=None, - AccessPolicies=None, SnapshotOptions=None, AdvancedOptions=None, - region=None, key=None, keyid=None, profile=None): - ''' +def update( + DomainName, + ElasticsearchClusterConfig=None, + EBSOptions=None, + AccessPolicies=None, + SnapshotOptions=None, + AdvancedOptions=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Update the named domain to the configuration. Returns {updated: true} if the domain was updated and returns @@ -311,35 +356,46 @@ def update(DomainName, ElasticsearchClusterConfig=None, EBSOptions=None, {"AutomatedSnapshotStartHour": 0} \\ {"rest.action.multi.allow_explicit_index": "true"} - ''' + """ call_args = {} - for k in ('ElasticsearchClusterConfig', 'EBSOptions', - 'AccessPolicies', 'SnapshotOptions', 'AdvancedOptions'): + for k in ( + "ElasticsearchClusterConfig", + "EBSOptions", + "AccessPolicies", + "SnapshotOptions", + "AdvancedOptions", + ): if locals()[k] is not None: val = locals()[k] if isinstance(val, six.string_types): try: val = salt.utils.json.loads(val) except ValueError as e: - return {'updated': False, 'error': 'Error parsing {0}: {1}'.format(k, e.message)} + return { + "updated": False, + "error": "Error parsing {0}: {1}".format(k, e.message), + } call_args[k] = val - if 'AccessPolicies' in call_args: - call_args['AccessPolicies'] = salt.utils.json.dumps(call_args['AccessPolicies']) + if "AccessPolicies" in call_args: + call_args["AccessPolicies"] = salt.utils.json.dumps(call_args["AccessPolicies"]) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - domain = conn.update_elasticsearch_domain_config(DomainName=DomainName, **call_args) - if not domain or 'DomainConfig' not in domain: - log.warning('Domain was not updated') - return {'updated': False} - return {'updated': True} + domain = conn.update_elasticsearch_domain_config( + DomainName=DomainName, **call_args + ) + if not domain or "DomainConfig" not in domain: + log.warning("Domain was not updated") + return {"updated": False} + return {"updated": True} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def add_tags(DomainName=None, ARN=None, - region=None, key=None, keyid=None, profile=None, **kwargs): - ''' +def add_tags( + DomainName=None, ARN=None, region=None, key=None, keyid=None, profile=None, **kwargs +): + """ Add tags to a domain Returns {tagged: true} if the domain was tagged and returns @@ -351,38 +407,45 @@ def add_tags(DomainName=None, ARN=None, salt myminion boto_elasticsearch_domain.add_tags mydomain tag_a=tag_value tag_b=tag_value - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) tagslist = [] for k, v in six.iteritems(kwargs): - if six.text_type(k).startswith('__'): + if six.text_type(k).startswith("__"): continue - tagslist.append({'Key': six.text_type(k), 'Value': six.text_type(v)}) + tagslist.append({"Key": six.text_type(k), "Value": six.text_type(v)}) if ARN is None: if DomainName is None: - raise SaltInvocationError('One (but not both) of ARN or ' - 'domain must be specified.') - domaindata = status(DomainName=DomainName, - region=region, key=key, keyid=keyid, - profile=profile) - if not domaindata or 'domain' not in domaindata: - log.warning('Domain tags not updated') - return {'tagged': False} - ARN = domaindata.get('domain', {}).get('ARN') + raise SaltInvocationError( + "One (but not both) of ARN or " "domain must be specified." + ) + domaindata = status( + DomainName=DomainName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not domaindata or "domain" not in domaindata: + log.warning("Domain tags not updated") + return {"tagged": False} + ARN = domaindata.get("domain", {}).get("ARN") elif DomainName is not None: - raise SaltInvocationError('One (but not both) of ARN or ' - 'domain must be specified.') + raise SaltInvocationError( + "One (but not both) of ARN or " "domain must be specified." + ) conn.add_tags(ARN=ARN, TagList=tagslist) - return {'tagged': True} + return {"tagged": True} except ClientError as e: - return {'tagged': False, 'error': __utils__['boto3.get_error'](e)} + return {"tagged": False, "error": __utils__["boto3.get_error"](e)} -def remove_tags(TagKeys, DomainName=None, ARN=None, - region=None, key=None, keyid=None, profile=None): - ''' +def remove_tags( + TagKeys, DomainName=None, ARN=None, region=None, key=None, keyid=None, profile=None +): + """ Remove tags from a trail Returns {tagged: true} if the trail was tagged and returns @@ -394,34 +457,40 @@ def remove_tags(TagKeys, DomainName=None, ARN=None, salt myminion boto_cloudtrail.remove_tags my_trail tag_a=tag_value tag_b=tag_value - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if ARN is None: if DomainName is None: - raise SaltInvocationError('One (but not both) of ARN or ' - 'domain must be specified.') - domaindata = status(DomainName=DomainName, - region=region, key=key, keyid=keyid, - profile=profile) - if not domaindata or 'domain' not in domaindata: - log.warning('Domain tags not updated') - return {'tagged': False} - ARN = domaindata.get('domain', {}).get('ARN') + raise SaltInvocationError( + "One (but not both) of ARN or " "domain must be specified." + ) + domaindata = status( + DomainName=DomainName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not domaindata or "domain" not in domaindata: + log.warning("Domain tags not updated") + return {"tagged": False} + ARN = domaindata.get("domain", {}).get("ARN") elif DomainName is not None: - raise SaltInvocationError('One (but not both) of ARN or ' - 'domain must be specified.') - conn.remove_tags(ARN=domaindata.get('domain', {}).get('ARN'), - TagKeys=TagKeys) - return {'tagged': True} + raise SaltInvocationError( + "One (but not both) of ARN or " "domain must be specified." + ) + conn.remove_tags(ARN=domaindata.get("domain", {}).get("ARN"), TagKeys=TagKeys) + return {"tagged": True} except ClientError as e: - return {'tagged': False, 'error': __utils__['boto3.get_error'](e)} + return {"tagged": False, "error": __utils__["boto3.get_error"](e)} -def list_tags(DomainName=None, ARN=None, - region=None, key=None, keyid=None, profile=None): - ''' +def list_tags( + DomainName=None, ARN=None, region=None, key=None, keyid=None, profile=None +): + """ List tags of a trail Returns: @@ -435,30 +504,36 @@ def list_tags(DomainName=None, ARN=None, salt myminion boto_cloudtrail.list_tags my_trail - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if ARN is None: if DomainName is None: - raise SaltInvocationError('One (but not both) of ARN or ' - 'domain must be specified.') - domaindata = status(DomainName=DomainName, - region=region, key=key, keyid=keyid, - profile=profile) - if not domaindata or 'domain' not in domaindata: - log.warning('Domain tags not updated') - return {'tagged': False} - ARN = domaindata.get('domain', {}).get('ARN') + raise SaltInvocationError( + "One (but not both) of ARN or " "domain must be specified." + ) + domaindata = status( + DomainName=DomainName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not domaindata or "domain" not in domaindata: + log.warning("Domain tags not updated") + return {"tagged": False} + ARN = domaindata.get("domain", {}).get("ARN") elif DomainName is not None: - raise SaltInvocationError('One (but not both) of ARN or ' - 'domain must be specified.') + raise SaltInvocationError( + "One (but not both) of ARN or " "domain must be specified." + ) ret = conn.list_tags(ARN=ARN) log.warning(ret) - tlist = ret.get('TagList', []) + tlist = ret.get("TagList", []) tagdict = {} for tag in tlist: - tagdict[tag.get('Key')] = tag.get('Value') - return {'tags': tagdict} + tagdict[tag.get("Key")] = tag.get("Value") + return {"tags": tagdict} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} diff --git a/salt/modules/boto_elb.py b/salt/modules/boto_elb.py index 66455a3c0b6..b6b08e78bd9 100644 --- a/salt/modules/boto_elb.py +++ b/salt/modules/boto_elb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon ELB .. versionadded:: 2014.7.0 @@ -40,7 +40,7 @@ Connection module for Amazon ELB region: us-east-1 :depends: boto >= 2.33.0 -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 @@ -50,8 +50,6 @@ from __future__ import absolute_import, print_function, unicode_literals import logging import time -log = logging.getLogger(__name__) - # Import Salt libs import salt.utils.json import salt.utils.odict as odict @@ -59,6 +57,10 @@ import salt.utils.versions # Import third party libs from salt.ext import six + +log = logging.getLogger(__name__) + + try: import boto import boto.ec2 # pylint: enable=unused-import @@ -67,28 +69,28 @@ try: from boto.ec2.elb.attributes import ConnectionDrainingAttribute from boto.ec2.elb.attributes import ConnectionSettingAttribute from boto.ec2.elb.attributes import CrossZoneLoadBalancingAttribute - logging.getLogger('boto').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' + """ # connection settings were added in 2.33.0 has_boto_reqs = salt.utils.versions.check_boto_reqs( - boto_ver='2.33.0', - check_boto3=False + boto_ver="2.33.0", check_boto3=False ) if has_boto_reqs is True: - __utils__['boto.assign_funcs'](__name__, 'elb', module='ec2.elb', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "elb", module="ec2.elb", pack=__salt__) return has_boto_reqs def exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if an ELB exists. CLI example: @@ -96,7 +98,7 @@ def exists(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_elb.exists myelb region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -104,7 +106,7 @@ def exists(name, region=None, key=None, keyid=None, profile=None): if elb: return True else: - log.debug('The load balancer does not exist in region %s', region) + log.debug("The load balancer does not exist in region %s", region) return False except boto.exception.BotoServerError as error: log.warning(error) @@ -112,7 +114,7 @@ def exists(name, region=None, key=None, keyid=None, profile=None): def get_all_elbs(region=None, key=None, keyid=None, profile=None): - ''' + """ Return all load balancers associated with an account CLI example: @@ -120,7 +122,7 @@ def get_all_elbs(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_elb.get_all_elbs region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -131,7 +133,7 @@ def get_all_elbs(region=None, key=None, keyid=None, profile=None): def list_elbs(region=None, key=None, keyid=None, profile=None): - ''' + """ Return names of all load balancers associated with an account CLI example: @@ -139,14 +141,16 @@ def list_elbs(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_elb.list_elbs region=us-east-1 - ''' + """ - return [e.name for e in get_all_elbs(region=region, key=key, keyid=keyid, - profile=profile)] + return [ + e.name + for e in get_all_elbs(region=region, key=key, keyid=keyid, profile=profile) + ] def get_elb_config(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get an ELB configuration. CLI example: @@ -154,7 +158,7 @@ def get_elb_config(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_elb.exists myelb region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 @@ -163,55 +167,55 @@ def get_elb_config(name, region=None, key=None, keyid=None, profile=None): lb = conn.get_all_load_balancers(load_balancer_names=[name]) lb = lb[0] ret = {} - ret['availability_zones'] = lb.availability_zones + ret["availability_zones"] = lb.availability_zones listeners = [] for _listener in lb.listeners: listener_dict = {} - listener_dict['elb_port'] = _listener.load_balancer_port - listener_dict['elb_protocol'] = _listener.protocol - listener_dict['instance_port'] = _listener.instance_port - listener_dict['instance_protocol'] = _listener.instance_protocol - listener_dict['policies'] = _listener.policy_names + listener_dict["elb_port"] = _listener.load_balancer_port + listener_dict["elb_protocol"] = _listener.protocol + listener_dict["instance_port"] = _listener.instance_port + listener_dict["instance_protocol"] = _listener.instance_protocol + listener_dict["policies"] = _listener.policy_names if _listener.ssl_certificate_id: - listener_dict['certificate'] = _listener.ssl_certificate_id + listener_dict["certificate"] = _listener.ssl_certificate_id listeners.append(listener_dict) - ret['listeners'] = listeners + ret["listeners"] = listeners backends = [] for _backend in lb.backends: bs_dict = {} - bs_dict['instance_port'] = _backend.instance_port - bs_dict['policies'] = [p.policy_name for p in _backend.policies] + bs_dict["instance_port"] = _backend.instance_port + bs_dict["policies"] = [p.policy_name for p in _backend.policies] backends.append(bs_dict) - ret['backends'] = backends - ret['subnets'] = lb.subnets - ret['security_groups'] = lb.security_groups - ret['scheme'] = lb.scheme - ret['dns_name'] = lb.dns_name - ret['tags'] = _get_all_tags(conn, name) + ret["backends"] = backends + ret["subnets"] = lb.subnets + ret["security_groups"] = lb.security_groups + ret["scheme"] = lb.scheme + ret["dns_name"] = lb.dns_name + ret["tags"] = _get_all_tags(conn, name) lb_policy_lists = [ lb.policies.app_cookie_stickiness_policies, lb.policies.lb_cookie_stickiness_policies, - lb.policies.other_policies - ] + lb.policies.other_policies, + ] policies = [] for policy_list in lb_policy_lists: policies += [p.policy_name for p in policy_list] - ret['policies'] = policies + ret["policies"] = policies return ret except boto.exception.BotoServerError as error: - if error.error_code == 'Throttling': - log.debug('Throttled by AWS API, will retry in 5 seconds.') + if error.error_code == "Throttling": + log.debug("Throttled by AWS API, will retry in 5 seconds.") time.sleep(5) retries -= 1 continue - log.error('Error fetching config for ELB %s: %s', name, error.message) + log.error("Error fetching config for ELB %s: %s", name, error.message) log.error(error) return {} return {} def listener_dict_to_tuple(listener): - ''' + """ Convert an ELB listener dict into a listener tuple used by certain parts of the AWS ELB API. @@ -220,24 +224,36 @@ def listener_dict_to_tuple(listener): .. code-block:: bash salt myminion boto_elb.listener_dict_to_tuple '{"elb_port":80,"instance_port":80,"elb_protocol":"HTTP"}' - ''' + """ # We define all listeners as complex listeners. - if 'instance_protocol' not in listener: - instance_protocol = listener['elb_protocol'].upper() + if "instance_protocol" not in listener: + instance_protocol = listener["elb_protocol"].upper() else: - instance_protocol = listener['instance_protocol'].upper() - listener_tuple = [listener['elb_port'], listener['instance_port'], - listener['elb_protocol'], instance_protocol] - if 'certificate' in listener: - listener_tuple.append(listener['certificate']) + instance_protocol = listener["instance_protocol"].upper() + listener_tuple = [ + listener["elb_port"], + listener["instance_port"], + listener["elb_protocol"], + instance_protocol, + ] + if "certificate" in listener: + listener_tuple.append(listener["certificate"]) return tuple(listener_tuple) -def create(name, availability_zones, listeners, subnets=None, - security_groups=None, scheme='internet-facing', - region=None, key=None, keyid=None, - profile=None): - ''' +def create( + name, + availability_zones, + listeners, + subnets=None, + security_groups=None, + scheme="internet-facing", + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an ELB CLI example to create an ELB: @@ -245,7 +261,7 @@ def create(name, availability_zones, listeners, subnets=None, .. code-block:: bash salt myminion boto_elb.create myelb '["us-east-1a", "us-east-1e"]' '{"elb_port": 443, "elb_protocol": "HTTPS", ...}' region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if exists(name, region, key, keyid, profile): return True @@ -260,24 +276,33 @@ def create(name, availability_zones, listeners, subnets=None, _complex_listeners.append(listener_dict_to_tuple(listener)) try: - lb = conn.create_load_balancer(name=name, zones=availability_zones, subnets=subnets, - security_groups=security_groups, scheme=scheme, - complex_listeners=_complex_listeners) + lb = conn.create_load_balancer( + name=name, + zones=availability_zones, + subnets=subnets, + security_groups=security_groups, + scheme=scheme, + complex_listeners=_complex_listeners, + ) if lb: - log.info('Created ELB %s', name) + log.info("Created ELB %s", name) return True else: - log.error('Failed to create ELB %s', name) + log.error("Failed to create ELB %s", name) return False except boto.exception.BotoServerError as error: - log.error('Failed to create ELB %s: %s: %s', - name, error.error_code, error.message, - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to create ELB %s: %s: %s", + name, + error.error_code, + error.message, + exc_info_on_loglevel=logging.DEBUG, + ) return False def delete(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete an ELB. CLI example to delete an ELB: @@ -285,24 +310,22 @@ def delete(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_elb.delete myelb region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not exists(name, region, key, keyid, profile): return True try: conn.delete_load_balancer(name) - log.info('Deleted ELB %s.', name) + log.info("Deleted ELB %s.", name) return True except boto.exception.BotoServerError as error: - log.error('Failed to delete ELB %s', name, - exc_info_on_loglevel=logging.DEBUG) + log.error("Failed to delete ELB %s", name, exc_info_on_loglevel=logging.DEBUG) return False -def create_listeners(name, listeners, region=None, key=None, keyid=None, - profile=None): - ''' +def create_listeners(name, listeners, region=None, key=None, keyid=None, profile=None): + """ Create listeners on an ELB. CLI example: @@ -310,7 +333,7 @@ def create_listeners(name, listeners, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_elb.create_listeners myelb '[["HTTPS", "HTTP", 443, 80, "arn:aws:iam::11 11111:server-certificate/mycert"]]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(listeners, six.string_types): @@ -321,17 +344,20 @@ def create_listeners(name, listeners, region=None, key=None, keyid=None, _complex_listeners.append(listener_dict_to_tuple(listener)) try: conn.create_load_balancer_listeners(name, [], _complex_listeners) - log.info('Created ELB listeners on %s', name) + log.info("Created ELB listeners on %s", name) return True except boto.exception.BotoServerError as error: - log.error('Failed to create ELB listeners on %s: %s', name, error, - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to create ELB listeners on %s: %s", + name, + error, + exc_info_on_loglevel=logging.DEBUG, + ) return False -def delete_listeners(name, ports, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_listeners(name, ports, region=None, key=None, keyid=None, profile=None): + """ Delete listeners on an ELB. CLI example: @@ -339,24 +365,29 @@ def delete_listeners(name, ports, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_elb.delete_listeners myelb '[80,443]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(ports, six.string_types): ports = salt.utils.json.loads(ports) try: conn.delete_load_balancer_listeners(name, ports) - log.info('Deleted ELB listeners on %s', name) + log.info("Deleted ELB listeners on %s", name) return True except boto.exception.BotoServerError as error: - log.error('Failed to delete ELB listeners on %s: %s', name, error, - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to delete ELB listeners on %s: %s", + name, + error, + exc_info_on_loglevel=logging.DEBUG, + ) return False -def apply_security_groups(name, security_groups, region=None, key=None, - keyid=None, profile=None): - ''' +def apply_security_groups( + name, security_groups, region=None, key=None, keyid=None, profile=None +): + """ Apply security groups to ELB. CLI example: @@ -364,25 +395,25 @@ def apply_security_groups(name, security_groups, region=None, key=None, .. code-block:: bash salt myminion boto_elb.apply_security_groups myelb '["mysecgroup1"]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(security_groups, six.string_types): security_groups = salt.utils.json.loads(security_groups) try: conn.apply_security_groups_to_lb(name, security_groups) - log.info('Applied security_groups on ELB %s', name) + log.info("Applied security_groups on ELB %s", name) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to appply security_groups on ELB %s: %s', - name, e.message) + log.error("Failed to appply security_groups on ELB %s: %s", name, e.message) return False -def enable_availability_zones(name, availability_zones, region=None, key=None, - keyid=None, profile=None): - ''' +def enable_availability_zones( + name, availability_zones, region=None, key=None, keyid=None, profile=None +): + """ Enable availability zones for ELB. CLI example: @@ -390,23 +421,24 @@ def enable_availability_zones(name, availability_zones, region=None, key=None, .. code-block:: bash salt myminion boto_elb.enable_availability_zones myelb '["us-east-1a"]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(availability_zones, six.string_types): availability_zones = salt.utils.json.loads(availability_zones) try: conn.enable_availability_zones(name, availability_zones) - log.info('Enabled availability_zones on ELB %s', name) + log.info("Enabled availability_zones on ELB %s", name) return True except boto.exception.BotoServerError as error: - log.error('Failed to enable availability_zones on ELB %s: %s', name, error) + log.error("Failed to enable availability_zones on ELB %s: %s", name, error) return False -def disable_availability_zones(name, availability_zones, region=None, key=None, - keyid=None, profile=None): - ''' +def disable_availability_zones( + name, availability_zones, region=None, key=None, keyid=None, profile=None +): + """ Disable availability zones for ELB. CLI example: @@ -414,24 +446,27 @@ def disable_availability_zones(name, availability_zones, region=None, key=None, .. code-block:: bash salt myminion boto_elb.disable_availability_zones myelb '["us-east-1a"]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(availability_zones, six.string_types): availability_zones = salt.utils.json.loads(availability_zones) try: conn.disable_availability_zones(name, availability_zones) - log.info('Disabled availability_zones on ELB %s', name) + log.info("Disabled availability_zones on ELB %s", name) return True except boto.exception.BotoServerError as error: - log.error('Failed to disable availability_zones on ELB %s: %s', - name, error, exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to disable availability_zones on ELB %s: %s", + name, + error, + exc_info_on_loglevel=logging.DEBUG, + ) return False -def attach_subnets(name, subnets, region=None, key=None, keyid=None, - profile=None): - ''' +def attach_subnets(name, subnets, region=None, key=None, keyid=None, profile=None): + """ Attach ELB to subnets. CLI example: @@ -439,24 +474,27 @@ def attach_subnets(name, subnets, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_elb.attach_subnets myelb '["mysubnet"]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(subnets, six.string_types): subnets = salt.utils.json.loads(subnets) try: conn.attach_lb_to_subnets(name, subnets) - log.info('Attached ELB %s on subnets.', name) + log.info("Attached ELB %s on subnets.", name) return True except boto.exception.BotoServerError as error: - log.error('Failed to attach ELB %s on subnets: %s', name, error, - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to attach ELB %s on subnets: %s", + name, + error, + exc_info_on_loglevel=logging.DEBUG, + ) return False -def detach_subnets(name, subnets, region=None, key=None, keyid=None, - profile=None): - ''' +def detach_subnets(name, subnets, region=None, key=None, keyid=None, profile=None): + """ Detach ELB from subnets. CLI example: @@ -464,23 +502,27 @@ def detach_subnets(name, subnets, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_elb.detach_subnets myelb '["mysubnet"]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(subnets, six.string_types): subnets = salt.utils.json.loads(subnets) try: conn.detach_lb_from_subnets(name, subnets) - log.info('Detached ELB %s from subnets.', name) + log.info("Detached ELB %s from subnets.", name) return True except boto.exception.BotoServerError as error: - log.error('Failed to detach ELB %s from subnets: %s', name, error, - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to detach ELB %s from subnets: %s", + name, + error, + exc_info_on_loglevel=logging.DEBUG, + ) return False def get_attributes(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if attributes are set on an ELB. CLI example: @@ -488,7 +530,7 @@ def get_attributes(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_elb.get_attributes myelb - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 @@ -496,37 +538,36 @@ def get_attributes(name, region=None, key=None, keyid=None, profile=None): try: lbattrs = conn.get_all_lb_attributes(name) ret = odict.OrderedDict() - ret['access_log'] = odict.OrderedDict() - ret['cross_zone_load_balancing'] = odict.OrderedDict() - ret['connection_draining'] = odict.OrderedDict() - ret['connecting_settings'] = odict.OrderedDict() + ret["access_log"] = odict.OrderedDict() + ret["cross_zone_load_balancing"] = odict.OrderedDict() + ret["connection_draining"] = odict.OrderedDict() + ret["connecting_settings"] = odict.OrderedDict() al = lbattrs.access_log czlb = lbattrs.cross_zone_load_balancing cd = lbattrs.connection_draining cs = lbattrs.connecting_settings - ret['access_log']['enabled'] = al.enabled - ret['access_log']['s3_bucket_name'] = al.s3_bucket_name - ret['access_log']['s3_bucket_prefix'] = al.s3_bucket_prefix - ret['access_log']['emit_interval'] = al.emit_interval - ret['cross_zone_load_balancing']['enabled'] = czlb.enabled - ret['connection_draining']['enabled'] = cd.enabled - ret['connection_draining']['timeout'] = cd.timeout - ret['connecting_settings']['idle_timeout'] = cs.idle_timeout + ret["access_log"]["enabled"] = al.enabled + ret["access_log"]["s3_bucket_name"] = al.s3_bucket_name + ret["access_log"]["s3_bucket_prefix"] = al.s3_bucket_prefix + ret["access_log"]["emit_interval"] = al.emit_interval + ret["cross_zone_load_balancing"]["enabled"] = czlb.enabled + ret["connection_draining"]["enabled"] = cd.enabled + ret["connection_draining"]["timeout"] = cd.timeout + ret["connecting_settings"]["idle_timeout"] = cs.idle_timeout return ret except boto.exception.BotoServerError as e: - if e.error_code == 'Throttling': + if e.error_code == "Throttling": log.debug("Throttled by AWS API, will retry in 5 seconds...") time.sleep(5) retries -= 1 continue - log.error('ELB %s does not exist: %s', name, e.message) + log.error("ELB %s does not exist: %s", name, e.message) return {} return {} -def set_attributes(name, attributes, region=None, key=None, keyid=None, - profile=None): - ''' +def set_attributes(name, attributes, region=None, key=None, keyid=None, profile=None): + """ Set attributes on an ELB. name (string) @@ -565,66 +606,67 @@ def set_attributes(name, attributes, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_elb.set_attributes myelb '{"access_log": {"enabled": "true", "s3_bucket_name": "mybucket", "s3_bucket_prefix": "mylogs/", "emit_interval": "5"}}' region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - al = attributes.get('access_log', {}) - czlb = attributes.get('cross_zone_load_balancing', {}) - cd = attributes.get('connection_draining', {}) - cs = attributes.get('connecting_settings', {}) + al = attributes.get("access_log", {}) + czlb = attributes.get("cross_zone_load_balancing", {}) + cd = attributes.get("connection_draining", {}) + cs = attributes.get("connecting_settings", {}) if not al and not czlb and not cd and not cs: - log.error('No supported attributes for ELB.') + log.error("No supported attributes for ELB.") return False if al: _al = AccessLogAttribute() - _al.enabled = al.get('enabled', False) + _al.enabled = al.get("enabled", False) if not _al.enabled: - msg = 'Access log attribute configured, but enabled config missing' + msg = "Access log attribute configured, but enabled config missing" log.error(msg) return False - _al.s3_bucket_name = al.get('s3_bucket_name', None) - _al.s3_bucket_prefix = al.get('s3_bucket_prefix', None) - _al.emit_interval = al.get('emit_interval', None) - added_attr = conn.modify_lb_attribute(name, 'accessLog', _al) + _al.s3_bucket_name = al.get("s3_bucket_name", None) + _al.s3_bucket_prefix = al.get("s3_bucket_prefix", None) + _al.emit_interval = al.get("emit_interval", None) + added_attr = conn.modify_lb_attribute(name, "accessLog", _al) if added_attr: - log.info('Added access_log attribute to %s elb.', name) + log.info("Added access_log attribute to %s elb.", name) else: - log.error('Failed to add access_log attribute to %s elb.', name) + log.error("Failed to add access_log attribute to %s elb.", name) return False if czlb: _czlb = CrossZoneLoadBalancingAttribute() - _czlb.enabled = czlb['enabled'] - added_attr = conn.modify_lb_attribute(name, 'crossZoneLoadBalancing', - _czlb.enabled) + _czlb.enabled = czlb["enabled"] + added_attr = conn.modify_lb_attribute( + name, "crossZoneLoadBalancing", _czlb.enabled + ) if added_attr: - log.info('Added cross_zone_load_balancing attribute to %s elb.', name) + log.info("Added cross_zone_load_balancing attribute to %s elb.", name) else: - log.error('Failed to add cross_zone_load_balancing attribute.') + log.error("Failed to add cross_zone_load_balancing attribute.") return False if cd: _cd = ConnectionDrainingAttribute() - _cd.enabled = cd['enabled'] - _cd.timeout = cd.get('timeout', 300) - added_attr = conn.modify_lb_attribute(name, 'connectionDraining', _cd) + _cd.enabled = cd["enabled"] + _cd.timeout = cd.get("timeout", 300) + added_attr = conn.modify_lb_attribute(name, "connectionDraining", _cd) if added_attr: - log.info('Added connection_draining attribute to %s elb.', name) + log.info("Added connection_draining attribute to %s elb.", name) else: - log.error('Failed to add connection_draining attribute.') + log.error("Failed to add connection_draining attribute.") return False if cs: _cs = ConnectionSettingAttribute() - _cs.idle_timeout = cs.get('idle_timeout', 60) - added_attr = conn.modify_lb_attribute(name, 'connectingSettings', _cs) + _cs.idle_timeout = cs.get("idle_timeout", 60) + added_attr = conn.modify_lb_attribute(name, "connectingSettings", _cs) if added_attr: - log.info('Added connecting_settings attribute to %s elb.', name) + log.info("Added connecting_settings attribute to %s elb.", name) else: - log.error('Failed to add connecting_settings attribute.') + log.error("Failed to add connecting_settings attribute.") return False return True def get_health_check(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get the health check configured for this ELB. CLI example: @@ -632,7 +674,7 @@ def get_health_check(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_elb.get_health_check myelb - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 @@ -642,26 +684,26 @@ def get_health_check(name, region=None, key=None, keyid=None, profile=None): lb = lb[0] ret = odict.OrderedDict() hc = lb.health_check - ret['interval'] = hc.interval - ret['target'] = hc.target - ret['healthy_threshold'] = hc.healthy_threshold - ret['timeout'] = hc.timeout - ret['unhealthy_threshold'] = hc.unhealthy_threshold + ret["interval"] = hc.interval + ret["target"] = hc.target + ret["healthy_threshold"] = hc.healthy_threshold + ret["timeout"] = hc.timeout + ret["unhealthy_threshold"] = hc.unhealthy_threshold return ret except boto.exception.BotoServerError as e: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, will retry in 5 seconds.') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, will retry in 5 seconds.") time.sleep(5) retries -= 1 continue - log.error('ELB %s not found.', name, - exc_info_on_logleve=logging.DEBUG) + log.error("ELB %s not found.", name, exc_info_on_logleve=logging.DEBUG) return {} -def set_health_check(name, health_check, region=None, key=None, keyid=None, - profile=None): - ''' +def set_health_check( + name, health_check, region=None, key=None, keyid=None, profile=None +): + """ Set attributes on an ELB. CLI example to set attributes on an ELB: @@ -669,7 +711,7 @@ def set_health_check(name, health_check, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_elb.set_health_check myelb '{"target": "HTTP:80/"}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 @@ -677,21 +719,22 @@ def set_health_check(name, health_check, region=None, key=None, keyid=None, while True: try: conn.configure_health_check(name, hc) - log.info('Configured health check on ELB %s', name) + log.info("Configured health check on ELB %s", name) return True except boto.exception.BotoServerError as error: - if retries and e.code == 'Throttling': - log.debug('Throttled by AWS API, will retry in 5 seconds.') + if retries and e.code == "Throttling": + log.debug("Throttled by AWS API, will retry in 5 seconds.") time.sleep(5) retries -= 1 continue - log.exception('Failed to configure health check on ELB %s', name) + log.exception("Failed to configure health check on ELB %s", name) return False -def register_instances(name, instances, region=None, key=None, keyid=None, - profile=None): - ''' +def register_instances( + name, instances, region=None, key=None, keyid=None, profile=None +): + """ Register instances with an ELB. Instances is either a string instance id or a list of string instance id's. @@ -706,7 +749,7 @@ def register_instances(name, instances, region=None, key=None, keyid=None, salt myminion boto_elb.register_instances myelb instance_id salt myminion boto_elb.register_instances myelb "[instance_id,instance_id]" - ''' + """ # convert instances to list type, enabling consistent use of instances # variable throughout the register_instances method if isinstance(instances, six.string_types) or isinstance(instances, six.text_type): @@ -718,23 +761,24 @@ def register_instances(name, instances, region=None, key=None, keyid=None, except boto.exception.BotoServerError as error: log.warning(error) return False - registered_instance_ids = [instance.id for instance in - registered_instances] + registered_instance_ids = [instance.id for instance in registered_instances] # register_failues is a set that will contain any instances that were not # able to be registered with the given ELB register_failures = set(instances).difference(set(registered_instance_ids)) if register_failures: - log.warning('Instance(s): %s not registered with ELB %s.', - list(register_failures), name) + log.warning( + "Instance(s): %s not registered with ELB %s.", list(register_failures), name + ) register_result = False else: register_result = True return register_result -def deregister_instances(name, instances, region=None, key=None, keyid=None, - profile=None): - ''' +def deregister_instances( + name, instances, region=None, key=None, keyid=None, profile=None +): + """ Deregister instances with an ELB. Instances is either a string instance id or a list of string instance id's. @@ -750,7 +794,7 @@ def deregister_instances(name, instances, region=None, key=None, keyid=None, salt myminion boto_elb.deregister_instances myelb instance_id salt myminion boto_elb.deregister_instances myelb "[instance_id, instance_id]" - ''' + """ # convert instances to list type, enabling consistent use of instances # variable throughout the deregister_instances method if isinstance(instances, six.string_types) or isinstance(instances, six.text_type): @@ -764,24 +808,26 @@ def deregister_instances(name, instances, region=None, key=None, keyid=None, # boto returns error.error_code == 'InvalidInstance' # deregister_instances returns "None" because the instances are # effectively deregistered from ELB - if error.error_code == 'InvalidInstance': + if error.error_code == "InvalidInstance": log.warning( - 'One or more of instance(s) %s are not part of ELB %s. ' - 'deregister_instances not performed.', instances, name + "One or more of instance(s) %s are not part of ELB %s. " + "deregister_instances not performed.", + instances, + name, ) return None else: log.warning(error) return False - registered_instance_ids = [instance.id for instance in - registered_instances] + registered_instance_ids = [instance.id for instance in registered_instances] # deregister_failures is a set that will contain any instances that were # unable to be deregistered from the given ELB deregister_failures = set(instances).intersection(set(registered_instance_ids)) if deregister_failures: log.warning( - 'Instance(s): %s not deregistered from ELB %s.', - list(deregister_failures), name + "Instance(s): %s not deregistered from ELB %s.", + list(deregister_failures), + name, ) deregister_result = False else: @@ -789,9 +835,10 @@ def deregister_instances(name, instances, region=None, key=None, keyid=None, return deregister_result -def set_instances(name, instances, test=False, region=None, key=None, keyid=None, - profile=None): - ''' +def set_instances( + name, instances, test=False, region=None, key=None, keyid=None, profile=None +): + """ Set the instances assigned to an ELB to exactly the list given CLI example: @@ -799,16 +846,24 @@ def set_instances(name, instances, test=False, region=None, key=None, keyid=None .. code-block:: bash salt myminion boto_elb.set_instances myelb region=us-east-1 instances="[instance_id,instance_id]" - ''' + """ ret = True - current = set([i['instance_id'] for i in get_instance_health(name, region, key, keyid, profile)]) + current = set( + [ + i["instance_id"] + for i in get_instance_health(name, region, key, keyid, profile) + ] + ) desired = set(instances) add = desired - current remove = current - desired if test: return bool(add or remove) if remove: - if deregister_instances(name, list(remove), region, key, keyid, profile) is False: + if ( + deregister_instances(name, list(remove), region, key, keyid, profile) + is False + ): ret = False if add: if register_instances(name, list(add), region, key, keyid, profile) is False: @@ -816,8 +871,10 @@ def set_instances(name, instances, test=False, region=None, key=None, keyid=None return ret -def get_instance_health(name, region=None, key=None, keyid=None, profile=None, instances=None): - ''' +def get_instance_health( + name, region=None, key=None, keyid=None, profile=None, instances=None +): + """ Get a list of instances and their health state CLI example: @@ -826,27 +883,38 @@ def get_instance_health(name, region=None, key=None, keyid=None, profile=None, i salt myminion boto_elb.get_instance_health myelb salt myminion boto_elb.get_instance_health myelb region=us-east-1 instances="[instance_id,instance_id]" - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: instance_states = conn.describe_instance_health(name, instances) ret = [] for _instance in instance_states: - ret.append({'instance_id': _instance.instance_id, - 'description': _instance.description, - 'state': _instance.state, - 'reason_code': _instance.reason_code - }) + ret.append( + { + "instance_id": _instance.instance_id, + "description": _instance.description, + "state": _instance.state, + "reason_code": _instance.reason_code, + } + ) return ret except boto.exception.BotoServerError as error: log.debug(error) return [] -def create_policy(name, policy_name, policy_type, policy, region=None, - key=None, keyid=None, profile=None): - ''' +def create_policy( + name, + policy_name, + policy_type, + policy, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an ELB policy. .. versionadded:: 2016.3.0 @@ -856,7 +924,7 @@ def create_policy(name, policy_name, policy_type, policy, region=None, .. code-block:: bash salt myminion boto_elb.create_policy myelb mypolicy LBCookieStickinessPolicyType '{"CookieExpirationPeriod": 3600}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not exists(name, region, key, keyid, profile): @@ -864,21 +932,24 @@ def create_policy(name, policy_name, policy_type, policy, region=None, try: success = conn.create_lb_policy(name, policy_name, policy_type, policy) if success: - log.info('Created policy %s on ELB %s', policy_name, name) + log.info("Created policy %s on ELB %s", policy_name, name) return True else: - log.error('Failed to create policy %s on ELB %s', policy_name, name) + log.error("Failed to create policy %s on ELB %s", policy_name, name) return False except boto.exception.BotoServerError as e: - log.error('Failed to create policy %s on ELB %s: %s', - policy_name, name, e.message, - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to create policy %s on ELB %s: %s", + policy_name, + name, + e.message, + exc_info_on_loglevel=logging.DEBUG, + ) return False -def delete_policy(name, policy_name, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_policy(name, policy_name, region=None, key=None, keyid=None, profile=None): + """ Delete an ELB policy. .. versionadded:: 2016.3.0 @@ -888,25 +959,30 @@ def delete_policy(name, policy_name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_elb.delete_policy myelb mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not exists(name, region, key, keyid, profile): return True try: conn.delete_lb_policy(name, policy_name) - log.info('Deleted policy %s on ELB %s', policy_name, name) + log.info("Deleted policy %s on ELB %s", policy_name, name) return True except boto.exception.BotoServerError as e: - log.error('Failed to delete policy %s on ELB %s: %s', - policy_name, name, e.message, - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to delete policy %s on ELB %s: %s", + policy_name, + name, + e.message, + exc_info_on_loglevel=logging.DEBUG, + ) return False -def set_listener_policy(name, port, policies=None, region=None, key=None, - keyid=None, profile=None): - ''' +def set_listener_policy( + name, port, policies=None, region=None, key=None, keyid=None, profile=None +): + """ Set the policies of an ELB listener. .. versionadded:: 2016.3.0 @@ -916,7 +992,7 @@ def set_listener_policy(name, port, policies=None, region=None, key=None, .. code-block:: Bash salt myminion boto_elb.set_listener_policy myelb 443 "[policy1,policy2]" - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not exists(name, region, key, keyid, profile): @@ -925,24 +1001,30 @@ def set_listener_policy(name, port, policies=None, region=None, key=None, policies = [] try: conn.set_lb_policies_of_listener(name, port, policies) - log.info('Set policies %s on ELB %s listener %s', policies, name, port) + log.info("Set policies %s on ELB %s listener %s", policies, name, port) except boto.exception.BotoServerError as e: - log.info('Failed to set policy %s on ELB %s listener %s: %s', - policies, name, port, e.message, - exc_info_on_loglevel=logging.DEBUG) + log.info( + "Failed to set policy %s on ELB %s listener %s: %s", + policies, + name, + port, + e.message, + exc_info_on_loglevel=logging.DEBUG, + ) return False return True -def set_backend_policy(name, port, policies=None, region=None, key=None, - keyid=None, profile=None): - ''' +def set_backend_policy( + name, port, policies=None, region=None, key=None, keyid=None, profile=None +): + """ Set the policies of an ELB backend server. CLI example: salt myminion boto_elb.set_backend_policy myelb 443 "[policy1,policy2]" - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not exists(name, region, key, keyid, profile): @@ -951,18 +1033,22 @@ def set_backend_policy(name, port, policies=None, region=None, key=None, policies = [] try: conn.set_lb_policies_of_backend_server(name, port, policies) - log.info('Set policies %s on ELB %s backend server %s', - policies, name, port) + log.info("Set policies %s on ELB %s backend server %s", policies, name, port) except boto.exception.BotoServerError as e: - log.info('Failed to set policy %s on ELB %s backend server %s: %s', - policies, name, port, e.message, - exc_info_on_loglevel=logging.DEBUG) + log.info( + "Failed to set policy %s on ELB %s backend server %s: %s", + policies, + name, + port, + e.message, + exc_info_on_loglevel=logging.DEBUG, + ) return False return True def set_tags(name, tags, region=None, key=None, keyid=None, profile=None): - ''' + """ Add the tags on an ELB .. versionadded:: 2016.3.0 @@ -978,7 +1064,7 @@ def set_tags(name, tags, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_elb.set_tags my-elb-name "{'Tag1': 'Value', 'Tag2': 'Another Value'}" - ''' + """ if exists(name, region, key, keyid, profile): conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -989,7 +1075,7 @@ def set_tags(name, tags, region=None, key=None, keyid=None, profile=None): def delete_tags(name, tags, region=None, key=None, keyid=None, profile=None): - ''' + """ Add the tags on an ELB name @@ -1003,7 +1089,7 @@ def delete_tags(name, tags, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_elb.delete_tags my-elb-name ['TagToRemove1', 'TagToRemove2'] - ''' + """ if exists(name, region, key, keyid, profile): conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) ret = _remove_tags(conn, name, tags) @@ -1013,21 +1099,21 @@ def delete_tags(name, tags, region=None, key=None, keyid=None, profile=None): def _build_tag_param_list(params, tags): - ''' + """ helper function to build a tag parameter list to send - ''' + """ keys = sorted(tags.keys()) i = 1 for key in keys: value = tags[key] - params['Tags.member.{0}.Key'.format(i)] = key + params["Tags.member.{0}.Key".format(i)] = key if value is not None: - params['Tags.member.{0}.Value'.format(i)] = value + params["Tags.member.{0}.Value".format(i)] = value i += 1 def _get_all_tags(conn, load_balancer_names=None): - ''' + """ Retrieve all the metadata tags associated with your ELB(s). :type load_balancer_names: list @@ -1035,17 +1121,18 @@ def _get_all_tags(conn, load_balancer_names=None): :rtype: list :return: A list of :class:`boto.ec2.elb.tag.Tag` objects - ''' + """ params = {} if load_balancer_names: - conn.build_list_params(params, load_balancer_names, - 'LoadBalancerNames.member.%d') + conn.build_list_params( + params, load_balancer_names, "LoadBalancerNames.member.%d" + ) tags = conn.get_object( - 'DescribeTags', + "DescribeTags", params, - __utils__['boto_elb_tag.get_tag_descriptions'](), - verb='POST' + __utils__["boto_elb_tag.get_tag_descriptions"](), + verb="POST", ) if tags[load_balancer_names]: return tags[load_balancer_names] @@ -1054,7 +1141,7 @@ def _get_all_tags(conn, load_balancer_names=None): def _add_tags(conn, load_balancer_names, tags): - ''' + """ Create new metadata tags for the specified resource ids. :type load_balancer_names: list @@ -1065,16 +1152,15 @@ def _add_tags(conn, load_balancer_names, tags): If you want to create only a tag name, the value for that tag should be the empty string (e.g. ''). - ''' + """ params = {} - conn.build_list_params(params, load_balancer_names, - 'LoadBalancerNames.member.%d') + conn.build_list_params(params, load_balancer_names, "LoadBalancerNames.member.%d") _build_tag_param_list(params, tags) - return conn.get_status('AddTags', params, verb='POST') + return conn.get_status("AddTags", params, verb="POST") def _remove_tags(conn, load_balancer_names, tags): - ''' + """ Delete metadata tags for the specified resource ids. :type load_balancer_names: list @@ -1083,10 +1169,8 @@ def _remove_tags(conn, load_balancer_names, tags): :type tags: list :param tags: A list containing just tag names for the tags to be deleted. - ''' + """ params = {} - conn.build_list_params(params, load_balancer_names, - 'LoadBalancerNames.member.%d') - conn.build_list_params(params, tags, - 'Tags.member.%d.Key') - return conn.get_status('RemoveTags', params, verb='POST') + conn.build_list_params(params, load_balancer_names, "LoadBalancerNames.member.%d") + conn.build_list_params(params, tags, "Tags.member.%d.Key") + return conn.get_status("RemoveTags", params, verb="POST") diff --git a/salt/modules/boto_elbv2.py b/salt/modules/boto_elbv2.py index 8d316c4fb9a..e8bac5e666c 100644 --- a/salt/modules/boto_elbv2.py +++ b/salt/modules/boto_elbv2.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon ALB .. versionadded:: 2017.7.0 @@ -36,7 +36,7 @@ Connection module for Amazon ALB :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 @@ -45,53 +45,59 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging -log = logging.getLogger(__name__) +import salt.utils.versions # Import Salt libs from salt.ext import six -import salt.utils.versions + +log = logging.getLogger(__name__) + # Import third-party libs try: # pylint: disable=unused-import import boto3 import botocore + # pylint: enable=unused-import # TODO Version check using salt.utils.versions from botocore.exceptions import ClientError - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto3 libraries exist. - ''' + """ has_boto_reqs = salt.utils.versions.check_boto_reqs() if has_boto_reqs is True: - __utils__['boto3.assign_funcs'](__name__, 'elbv2') + __utils__["boto3.assign_funcs"](__name__, "elbv2") return has_boto_reqs -def create_target_group(name, - protocol, - port, - vpc_id, - region=None, - key=None, - keyid=None, - profile=None, - health_check_protocol='HTTP', - health_check_port='traffic-port', - health_check_path='/', - health_check_interval_seconds=30, - health_check_timeout_seconds=5, - healthy_threshold_count=5, - unhealthy_threshold_count=2): - ''' +def create_target_group( + name, + protocol, + port, + vpc_id, + region=None, + key=None, + keyid=None, + profile=None, + health_check_protocol="HTTP", + health_check_port="traffic-port", + health_check_path="/", + health_check_interval_seconds=30, + health_check_timeout_seconds=5, + healthy_threshold_count=5, + unhealthy_threshold_count=2, +): + """ Create target group if not present. name @@ -133,42 +139,46 @@ def create_target_group(name, .. code-block:: bash salt myminion boto_elbv2.create_target_group learn1give1 protocol=HTTP port=54006 vpc_id=vpc-deadbeef - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if target_group_exists(name, region, key, keyid, profile): return True try: - alb = conn.create_target_group(Name=name, Protocol=protocol, Port=port, - VpcId=vpc_id, HealthCheckProtocol=health_check_protocol, - HealthCheckPort=health_check_port, - HealthCheckPath=health_check_path, - HealthCheckIntervalSeconds=health_check_interval_seconds, - HealthCheckTimeoutSeconds=health_check_timeout_seconds, - HealthyThresholdCount=healthy_threshold_count, - UnhealthyThresholdCount=unhealthy_threshold_count) + alb = conn.create_target_group( + Name=name, + Protocol=protocol, + Port=port, + VpcId=vpc_id, + HealthCheckProtocol=health_check_protocol, + HealthCheckPort=health_check_port, + HealthCheckPath=health_check_path, + HealthCheckIntervalSeconds=health_check_interval_seconds, + HealthCheckTimeoutSeconds=health_check_timeout_seconds, + HealthyThresholdCount=healthy_threshold_count, + UnhealthyThresholdCount=unhealthy_threshold_count, + ) if alb: - log.info('Created ALB %s: %s', name, alb['TargetGroups'][0]['TargetGroupArn']) + log.info( + "Created ALB %s: %s", name, alb["TargetGroups"][0]["TargetGroupArn"] + ) return True else: - log.error('Failed to create ALB %s', name) + log.error("Failed to create ALB %s", name) return False except ClientError as error: log.error( - 'Failed to create ALB %s: %s: %s', - name, error.response['Error']['Code'], - error.response['Error']['Message'], - exc_info_on_loglevel=logging.DEBUG + "Failed to create ALB %s: %s: %s", + name, + error.response["Error"]["Code"], + error.response["Error"]["Message"], + exc_info_on_loglevel=logging.DEBUG, ) -def delete_target_group(name, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def delete_target_group(name, region=None, key=None, keyid=None, profile=None): + """ Delete target group. name @@ -182,36 +192,33 @@ def delete_target_group(name, .. code-block:: bash salt myminion boto_elbv2.delete_target_group arn:aws:elasticloadbalancing:us-west-2:644138682826:targetgroup/learn1give1-api/414788a16b5cf163 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not target_group_exists(name, region, key, keyid, profile): return True try: - if name.startswith('arn:aws:elasticloadbalancing'): + if name.startswith("arn:aws:elasticloadbalancing"): conn.delete_target_group(TargetGroupArn=name) - log.info('Deleted target group %s', name) + log.info("Deleted target group %s", name) else: tg_info = conn.describe_target_groups(Names=[name]) - if len(tg_info['TargetGroups']) != 1: + if len(tg_info["TargetGroups"]) != 1: return False - arn = tg_info['TargetGroups'][0]['TargetGroupArn'] + arn = tg_info["TargetGroups"][0]["TargetGroupArn"] conn.delete_target_group(TargetGroupArn=arn) - log.info('Deleted target group %s ARN %s', name, arn) + log.info("Deleted target group %s ARN %s", name, arn) return True except ClientError as error: - log.error('Failed to delete target group %s', name, - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to delete target group %s", name, exc_info_on_loglevel=logging.DEBUG + ) return False -def target_group_exists(name, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def target_group_exists(name, region=None, key=None, keyid=None, profile=None): + """ Check to see if an target group exists. CLI example: @@ -219,31 +226,28 @@ def target_group_exists(name, .. code-block:: bash salt myminion boto_elbv2.target_group_exists arn:aws:elasticloadbalancing:us-west-2:644138682826:targetgroup/learn1give1-api/414788a16b5cf163 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - if name.startswith('arn:aws:elasticloadbalancing'): + if name.startswith("arn:aws:elasticloadbalancing"): alb = conn.describe_target_groups(TargetGroupArns=[name]) else: alb = conn.describe_target_groups(Names=[name]) if alb: return True else: - log.warning('The target group does not exist in region %s', region) + log.warning("The target group does not exist in region %s", region) return False except ClientError as error: - log.warning('target_group_exists check for %s returned: %s', name, error) + log.warning("target_group_exists check for %s returned: %s", name, error) return False -def describe_target_health(name, - targets=None, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def describe_target_health( + name, targets=None, region=None, key=None, keyid=None, profile=None +): + """ Get the curret health check status for targets in a target group. CLI example: @@ -251,7 +255,7 @@ def describe_target_health(name, .. code-block:: bash salt myminion boto_elbv2.describe_target_health arn:aws:elasticloadbalancing:us-west-2:644138682826:targetgroup/learn1give1-api/414788a16b5cf163 targets=["i-isdf23ifjf"] - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -259,12 +263,14 @@ def describe_target_health(name, targetsdict = [] for target in targets: targetsdict.append({"Id": target}) - instances = conn.describe_target_health(TargetGroupArn=name, Targets=targetsdict) + instances = conn.describe_target_health( + TargetGroupArn=name, Targets=targetsdict + ) else: instances = conn.describe_target_health(TargetGroupArn=name) ret = {} - for instance in instances['TargetHealthDescriptions']: - ret.update({instance['Target']['Id']: instance['TargetHealth']['State']}) + for instance in instances["TargetHealthDescriptions"]: + ret.update({instance["Target"]["Id"]: instance["TargetHealth"]["State"]}) return ret except ClientError as error: @@ -272,13 +278,8 @@ def describe_target_health(name, return {} -def register_targets(name, - targets, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def register_targets(name, targets, region=None, key=None, keyid=None, profile=None): + """ Register targets to a target froup of an ALB. ``targets`` is either a instance id string or a list of instance id's. @@ -293,7 +294,7 @@ def register_targets(name, salt myminion boto_elbv2.register_targets myelb instance_id salt myminion boto_elbv2.register_targets myelb "[instance_id,instance_id]" - ''' + """ targetsdict = [] if isinstance(targets, six.string_types) or isinstance(targets, six.text_type): targetsdict.append({"Id": targets}) @@ -303,7 +304,9 @@ def register_targets(name, conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - registered_targets = conn.register_targets(TargetGroupArn=name, Targets=targetsdict) + registered_targets = conn.register_targets( + TargetGroupArn=name, Targets=targetsdict + ) if registered_targets: return True return False @@ -312,13 +315,8 @@ def register_targets(name, return False -def deregister_targets(name, - targets, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def deregister_targets(name, targets, region=None, key=None, keyid=None, profile=None): + """ Deregister targets to a target froup of an ALB. ``targets`` is either a instance id string or a list of instance id's. @@ -333,7 +331,7 @@ def deregister_targets(name, salt myminion boto_elbv2.deregister_targets myelb instance_id salt myminion boto_elbv2.deregister_targets myelb "[instance_id,instance_id]" - ''' + """ targetsdict = [] if isinstance(targets, six.string_types) or isinstance(targets, six.text_type): targetsdict.append({"Id": targets}) @@ -343,7 +341,9 @@ def deregister_targets(name, conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - registered_targets = conn.deregister_targets(TargetGroupArn=name, Targets=targetsdict) + registered_targets = conn.deregister_targets( + TargetGroupArn=name, Targets=targetsdict + ) if registered_targets: return True return False diff --git a/salt/modules/boto_iam.py b/salt/modules/boto_iam.py index 375db1f75bd..81f43c30dbc 100644 --- a/salt/modules/boto_iam.py +++ b/salt/modules/boto_iam.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon IAM .. versionadded:: 2014.7.0 @@ -33,32 +33,36 @@ Connection module for Amazon IAM region: us-east-1 :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time -# Import salt libs -from salt.ext import six import salt.utils.compat import salt.utils.json import salt.utils.odict as odict import salt.utils.versions +# Import salt libs +from salt.ext import six + # Import third party libs # pylint: disable=unused-import -from salt.ext.six.moves.urllib.parse import unquote as _unquote # pylint: disable=no-name-in-module +from salt.ext.six.moves.urllib.parse import unquote as _unquote + try: import boto import boto.iam import boto3 import botocore - logging.getLogger('boto').setLevel(logging.CRITICAL) - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -68,23 +72,20 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' - return salt.utils.versions.check_boto_reqs( - check_boto3=False - ) + """ + return salt.utils.versions.check_boto_reqs(check_boto3=False) def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto.assign_funcs'](__name__, 'iam', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "iam", pack=__salt__) -def instance_profile_exists(name, region=None, key=None, keyid=None, - profile=None): - ''' +def instance_profile_exists(name, region=None, key=None, keyid=None, profile=None): + """ Check to see if an instance profile exists. CLI Example: @@ -92,7 +93,7 @@ def instance_profile_exists(name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.instance_profile_exists myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -104,9 +105,8 @@ def instance_profile_exists(name, region=None, key=None, keyid=None, return False -def create_instance_profile(name, region=None, key=None, keyid=None, - profile=None): - ''' +def create_instance_profile(name, region=None, key=None, keyid=None, profile=None): + """ Create an instance profile. CLI Example: @@ -114,7 +114,7 @@ def create_instance_profile(name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.create_instance_profile myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if instance_profile_exists(name, region, key, keyid, profile): @@ -123,17 +123,16 @@ def create_instance_profile(name, region=None, key=None, keyid=None, # This call returns an instance profile if successful and an exception # if not. It's annoying. conn.create_instance_profile(name) - log.info('Created %s instance profile.', name) + log.info("Created %s instance profile.", name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to create %s instance profile.', name) + log.error("Failed to create %s instance profile.", name) return False return True -def delete_instance_profile(name, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_instance_profile(name, region=None, key=None, keyid=None, profile=None): + """ Delete an instance profile. CLI Example: @@ -141,23 +140,23 @@ def delete_instance_profile(name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.delete_instance_profile myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not instance_profile_exists(name, region, key, keyid, profile): return True try: conn.delete_instance_profile(name) - log.info('Deleted %s instance profile.', name) + log.info("Deleted %s instance profile.", name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to delete %s instance profile.', name) + log.error("Failed to delete %s instance profile.", name) return False return True def role_exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if an IAM role exists. CLI Example: @@ -165,7 +164,7 @@ def role_exists(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.role_exists myirole - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.get_role(name) @@ -175,7 +174,7 @@ def role_exists(name, region=None, key=None, keyid=None, profile=None): def describe_role(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get information for a role. CLI Example: @@ -183,34 +182,33 @@ def describe_role(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.describe_role myirole - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.get_role(name) if not info: return False role = info.get_role_response.get_role_result.role - role['assume_role_policy_document'] = salt.utils.json.loads(_unquote( - role.assume_role_policy_document - )) + role["assume_role_policy_document"] = salt.utils.json.loads( + _unquote(role.assume_role_policy_document) + ) # If Sid wasn't defined by the user, boto will still return a Sid in # each policy. To properly check idempotently, let's remove the Sid # from the return if it's not actually set. - for policy_key, policy in role['assume_role_policy_document'].items(): - if policy_key == 'Statement': + for policy_key, policy in role["assume_role_policy_document"].items(): + if policy_key == "Statement": for val in policy: - if 'Sid' in val and not val['Sid']: - del val['Sid'] + if "Sid" in val and not val["Sid"]: + del val["Sid"] return role except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to get %s information.', name) + log.error("Failed to get %s information.", name) return False -def create_user(user_name, path=None, region=None, key=None, keyid=None, - profile=None): - ''' +def create_user(user_name, path=None, region=None, key=None, keyid=None, profile=None): + """ Create a user. .. versionadded:: 2015.8.0 @@ -220,25 +218,32 @@ def create_user(user_name, path=None, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.create_user myuser - ''' + """ if not path: - path = '/' + path = "/" if get_user(user_name, region, key, keyid, profile): return True conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.create_user(user_name, path) - log.info('Created IAM user : %s.', user_name) + log.info("Created IAM user : %s.", user_name) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to create IAM user %s.', user_name) + log.error("Failed to create IAM user %s.", user_name) return False -def get_all_access_keys(user_name, marker=None, max_items=None, - region=None, key=None, keyid=None, profile=None): - ''' +def get_all_access_keys( + user_name, + marker=None, + max_items=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get all access keys from a user. .. versionadded:: 2015.8.0 @@ -248,18 +253,18 @@ def get_all_access_keys(user_name, marker=None, max_items=None, .. code-block:: bash salt myminion boto_iam.get_all_access_keys myuser - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: return conn.get_all_access_keys(user_name, marker, max_items) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to get access keys for IAM user %s.', user_name) + log.error("Failed to get access keys for IAM user %s.", user_name) return six.text_type(e) def create_access_key(user_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Create access key id for a user. .. versionadded:: 2015.8.0 @@ -269,19 +274,20 @@ def create_access_key(user_name, region=None, key=None, keyid=None, profile=None .. code-block:: bash salt myminion boto_iam.create_access_key myuser - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: return conn.create_access_key(user_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to create access key.') + log.error("Failed to create access key.") return six.text_type(e) -def delete_access_key(access_key_id, user_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def delete_access_key( + access_key_id, user_name=None, region=None, key=None, keyid=None, profile=None +): + """ Delete access key id from a user. .. versionadded:: 2015.8.0 @@ -291,19 +297,18 @@ def delete_access_key(access_key_id, user_name=None, region=None, key=None, .. code-block:: bash salt myminion boto_iam.delete_access_key myuser - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: return conn.delete_access_key(access_key_id, user_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to delete access key id %s.', access_key_id) + log.error("Failed to delete access key id %s.", access_key_id) return six.text_type(e) -def delete_user(user_name, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_user(user_name, region=None, key=None, keyid=None, profile=None): + """ Delete a user. .. versionadded:: 2015.8.0 @@ -313,22 +318,22 @@ def delete_user(user_name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.delete_user myuser - ''' + """ if not get_user(user_name, region, key, keyid, profile): return True conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.delete_user(user_name) - log.info('Deleted IAM user : %s .', user_name) + log.info("Deleted IAM user : %s .", user_name) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to delete IAM user %s', user_name) + log.error("Failed to delete IAM user %s", user_name) return six.text_type(e) def get_user(user_name=None, region=None, key=None, keyid=None, profile=None): - ''' + """ Get user information. .. versionadded:: 2015.8.0 @@ -338,7 +343,7 @@ def get_user(user_name=None, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.get_user myuser - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.get_user(user_name) @@ -347,13 +352,14 @@ def get_user(user_name=None, region=None, key=None, keyid=None, profile=None): return info except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to get IAM user %s info.', user_name) + log.error("Failed to get IAM user %s info.", user_name) return False -def create_group(group_name, path=None, region=None, key=None, keyid=None, - profile=None): - ''' +def create_group( + group_name, path=None, region=None, key=None, keyid=None, profile=None +): + """ Create a group. .. versionadded:: 2015.8.0 @@ -363,25 +369,24 @@ def create_group(group_name, path=None, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.create_group group - ''' + """ if not path: - path = '/' - if get_group(group_name, region=region, key=key, keyid=keyid, - profile=profile): + path = "/" + if get_group(group_name, region=region, key=key, keyid=keyid, profile=profile): return True conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.create_group(group_name, path) - log.info('Created IAM group : %s.', group_name) + log.info("Created IAM group : %s.", group_name) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to create IAM group %s.', group_name) + log.error("Failed to create IAM group %s.", group_name) return False def get_group(group_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get group information. .. versionadded:: 2015.8.0 @@ -391,21 +396,21 @@ def get_group(group_name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.get_group mygroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.get_group(group_name, max_items=1) if not info: return False - return info['get_group_response']['get_group_result']['group'] + return info["get_group_response"]["get_group_result"]["group"] except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to get IAM group %s info.', group_name) + log.error("Failed to get IAM group %s info.", group_name) return False def get_group_members(group_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get group information. .. versionadded:: 2016.3.0 @@ -415,7 +420,7 @@ def get_group_members(group_name, region=None, key=None, keyid=None, profile=Non .. code-block:: bash salt myminion boto_iam.get_group mygroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: marker = None @@ -425,23 +430,26 @@ def get_group_members(group_name, region=None, key=None, keyid=None, profile=Non info = conn.get_group(group_name, marker=marker, max_items=1000) if not info: return False - truncated = bool(info['get_group_response']['get_group_result']['is_truncated']) - if truncated and 'marker' in info['get_group_response']['get_group_result']: - marker = info['get_group_response']['get_group_result']['marker'] + truncated = bool( + info["get_group_response"]["get_group_result"]["is_truncated"] + ) + if truncated and "marker" in info["get_group_response"]["get_group_result"]: + marker = info["get_group_response"]["get_group_result"]["marker"] else: marker = None truncated = False - users += info['get_group_response']['get_group_result']['users'] + users += info["get_group_response"]["get_group_result"]["users"] return users except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to get members for IAM group %s.', group_name) + log.error("Failed to get members for IAM group %s.", group_name) return False -def add_user_to_group(user_name, group_name, region=None, key=None, keyid=None, - profile=None): - ''' +def add_user_to_group( + user_name, group_name, region=None, key=None, keyid=None, profile=None +): + """ Add user to group. .. versionadded:: 2015.8.0 @@ -451,13 +459,14 @@ def add_user_to_group(user_name, group_name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.add_user_to_group myuser mygroup - ''' + """ user = get_user(user_name, region, key, keyid, profile) if not user: - log.error('Username : %s does not exist.', user_name) + log.error("Username : %s does not exist.", user_name) return False - if user_exists_in_group(user_name, group_name, region=region, key=key, - keyid=keyid, profile=profile): + if user_exists_in_group( + user_name, group_name, region=region, key=key, keyid=keyid, profile=profile + ): return True conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -467,13 +476,14 @@ def add_user_to_group(user_name, group_name, region=None, key=None, keyid=None, return info except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to add IAM user %s to group %s.', user_name, group_name) + log.error("Failed to add IAM user %s to group %s.", user_name, group_name) return False -def user_exists_in_group(user_name, group_name, region=None, key=None, keyid=None, - profile=None): - ''' +def user_exists_in_group( + user_name, group_name, region=None, key=None, keyid=None, profile=None +): + """ Check if user exists in group. .. versionadded:: 2015.8.0 @@ -483,23 +493,25 @@ def user_exists_in_group(user_name, group_name, region=None, key=None, keyid=Non .. code-block:: bash salt myminion boto_iam.user_exists_in_group myuser mygroup - ''' + """ # TODO this should probably use boto.iam.get_groups_for_user users = get_group_members( - group_name=group_name, region=region, key=key, keyid=keyid, - profile=profile + group_name=group_name, region=region, key=key, keyid=keyid, profile=profile ) if users: for _user in users: - if user_name == _user['user_name']: - log.debug('IAM user %s is already in IAM group %s.', user_name, group_name) + if user_name == _user["user_name"]: + log.debug( + "IAM user %s is already in IAM group %s.", user_name, group_name + ) return True return False -def remove_user_from_group(group_name, user_name, region=None, key=None, keyid=None, - profile=None): - ''' +def remove_user_from_group( + group_name, user_name, region=None, key=None, keyid=None, profile=None +): + """ Remove user from group. .. versionadded:: 2015.8.0 @@ -509,13 +521,14 @@ def remove_user_from_group(group_name, user_name, region=None, key=None, keyid=N .. code-block:: bash salt myminion boto_iam.remove_user_from_group mygroup myuser - ''' + """ user = get_user(user_name, region, key, keyid, profile) if not user: - log.error('IAM user %s does not exist.', user_name) + log.error("IAM user %s does not exist.", user_name) return False - if not user_exists_in_group(user_name, group_name, region=region, key=key, - keyid=keyid, profile=profile): + if not user_exists_in_group( + user_name, group_name, region=region, key=key, keyid=keyid, profile=profile + ): return True conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -525,13 +538,20 @@ def remove_user_from_group(group_name, user_name, region=None, key=None, keyid=N return info except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to remove IAM user %s from group %s', user_name, group_name) + log.error("Failed to remove IAM user %s from group %s", user_name, group_name) return False -def put_group_policy(group_name, policy_name, policy_json, region=None, key=None, - keyid=None, profile=None): - ''' +def put_group_policy( + group_name, + policy_name, + policy_json, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Adds or updates the specified policy document for the specified group. .. versionadded:: 2015.8.0 @@ -541,31 +561,30 @@ def put_group_policy(group_name, policy_name, policy_json, region=None, key=None .. code-block:: bash salt myminion boto_iam.put_group_policy mygroup policyname policyrules - ''' - group = get_group(group_name, region=region, key=key, keyid=keyid, - profile=profile) + """ + group = get_group(group_name, region=region, key=key, keyid=keyid, profile=profile) if not group: - log.error('Group %s does not exist', group_name) + log.error("Group %s does not exist", group_name) return False conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: if not isinstance(policy_json, six.string_types): policy_json = salt.utils.json.dumps(policy_json) - created = conn.put_group_policy(group_name, policy_name, - policy_json) + created = conn.put_group_policy(group_name, policy_name, policy_json) if created: - log.info('Created policy for IAM group %s.', group_name) + log.info("Created policy for IAM group %s.", group_name) return True - log.error('Could not create policy for IAM group %s', group_name) + log.error("Could not create policy for IAM group %s", group_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to create policy for IAM group %s', group_name) + log.error("Failed to create policy for IAM group %s", group_name) return False -def delete_group_policy(group_name, policy_name, region=None, key=None, - keyid=None, profile=None): - ''' +def delete_group_policy( + group_name, policy_name, region=None, key=None, keyid=None, profile=None +): + """ Delete a group policy. CLI Example:: @@ -573,28 +592,31 @@ def delete_group_policy(group_name, policy_name, region=None, key=None, .. code-block:: bash salt myminion boto_iam.delete_group_policy mygroup mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return False - _policy = get_group_policy( - group_name, policy_name, region, key, keyid, profile - ) + _policy = get_group_policy(group_name, policy_name, region, key, keyid, profile) if not _policy: return True try: conn.delete_group_policy(group_name, policy_name) - log.info('Successfully deleted policy %s for IAM group %s.', policy_name, group_name) + log.info( + "Successfully deleted policy %s for IAM group %s.", policy_name, group_name + ) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to delete policy %s for IAM group %s.', policy_name, group_name) + log.error( + "Failed to delete policy %s for IAM group %s.", policy_name, group_name + ) return False -def get_group_policy(group_name, policy_name, region=None, key=None, - keyid=None, profile=None): - ''' +def get_group_policy( + group_name, policy_name, region=None, key=None, keyid=None, profile=None +): + """ Retrieves the specified policy document for the specified group. .. versionadded:: 2015.8.0 @@ -604,11 +626,11 @@ def get_group_policy(group_name, policy_name, region=None, key=None, .. code-block:: bash salt myminion boto_iam.get_group_policy mygroup policyname - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.get_group_policy(group_name, policy_name) - log.debug('info for group policy is : %s', info) + log.debug("info for group policy is : %s", info) if not info: return False info = info.get_group_policy_response.get_group_policy_result.policy_document @@ -617,13 +639,12 @@ def get_group_policy(group_name, policy_name, region=None, key=None, return info except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to get IAM group %s info.', group_name) + log.error("Failed to get IAM group %s info.", group_name) return False -def get_all_groups(path_prefix='/', region=None, key=None, keyid=None, - profile=None): - ''' +def get_all_groups(path_prefix="/", region=None, key=None, keyid=None, profile=None): + """ Get and return all IAM group details, starting at the optional path. .. versionadded:: 2016.3.0 @@ -631,27 +652,26 @@ def get_all_groups(path_prefix='/', region=None, key=None, keyid=None, CLI Example: salt-call boto_iam.get_all_groups - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return None _groups = conn.get_all_groups(path_prefix=path_prefix) groups = _groups.list_groups_response.list_groups_result.groups - marker = getattr( - _groups.list_groups_response.list_groups_result, 'marker', None - ) + marker = getattr(_groups.list_groups_response.list_groups_result, "marker", None) while marker: _groups = conn.get_all_groups(path_prefix=path_prefix, marker=marker) groups = groups + _groups.list_groups_response.list_groups_result.groups marker = getattr( - _groups.list_groups_response.list_groups_result, 'marker', None + _groups.list_groups_response.list_groups_result, "marker", None ) return groups -def get_all_instance_profiles(path_prefix='/', region=None, key=None, - keyid=None, profile=None): - ''' +def get_all_instance_profiles( + path_prefix="/", region=None, key=None, keyid=None, profile=None +): + """ Get and return all IAM instance profiles, starting at the optional path. .. versionadded:: 2016.11.0 @@ -659,23 +679,23 @@ def get_all_instance_profiles(path_prefix='/', region=None, key=None, CLI Example: salt-call boto_iam.get_all_instance_profiles - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) marker = False profiles = [] while marker is not None: marker = marker if marker else None - p = conn.list_instance_profiles(path_prefix=path_prefix, - marker=marker) + p = conn.list_instance_profiles(path_prefix=path_prefix, marker=marker) res = p.list_instance_profiles_response.list_instance_profiles_result profiles += res.instance_profiles - marker = getattr(res, 'marker', None) + marker = getattr(res, "marker", None) return profiles -def list_instance_profiles(path_prefix='/', region=None, key=None, - keyid=None, profile=None): - ''' +def list_instance_profiles( + path_prefix="/", region=None, key=None, keyid=None, profile=None +): + """ List all IAM instance profiles, starting at the optional path. .. versionadded:: 2016.11.0 @@ -683,14 +703,13 @@ def list_instance_profiles(path_prefix='/', region=None, key=None, CLI Example: salt-call boto_iam.list_instance_profiles - ''' + """ p = get_all_instance_profiles(path_prefix, region, key, keyid, profile) - return [i['instance_profile_name'] for i in p] + return [i["instance_profile_name"] for i in p] -def get_all_group_policies(group_name, region=None, key=None, keyid=None, - profile=None): - ''' +def get_all_group_policies(group_name, region=None, key=None, keyid=None, profile=None): + """ Get a list of policy names from a group. CLI Example: @@ -698,7 +717,7 @@ def get_all_group_policies(group_name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.get_all_group_policies mygroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return False @@ -711,9 +730,8 @@ def get_all_group_policies(group_name, region=None, key=None, keyid=None, return [] -def delete_group(group_name, region=None, key=None, - keyid=None, profile=None): - ''' +def delete_group(group_name, region=None, key=None, keyid=None, profile=None): + """ Delete a group policy. CLI Example:: @@ -721,28 +739,27 @@ def delete_group(group_name, region=None, key=None, .. code-block:: bash salt myminion boto_iam.delete_group mygroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return False - _group = get_group( - group_name, region, key, keyid, profile - ) + _group = get_group(group_name, region, key, keyid, profile) if not _group: return True try: conn.delete_group(group_name) - log.info('Successfully deleted IAM group %s.', group_name) + log.info("Successfully deleted IAM group %s.", group_name) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to delete IAM group %s.', group_name) + log.error("Failed to delete IAM group %s.", group_name) return False -def create_login_profile(user_name, password, region=None, key=None, - keyid=None, profile=None): - ''' +def create_login_profile( + user_name, password, region=None, key=None, keyid=None, profile=None +): + """ Creates a login profile for the specified user, give the user the ability to access AWS services and the AWS Management Console. @@ -753,28 +770,27 @@ def create_login_profile(user_name, password, region=None, key=None, .. code-block:: bash salt myminion boto_iam.create_login_profile user_name password - ''' + """ user = get_user(user_name, region, key, keyid, profile) if not user: - log.error('IAM user %s does not exist', user_name) + log.error("IAM user %s does not exist", user_name) return False conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.create_login_profile(user_name, password) - log.info('Created profile for IAM user %s.', user_name) + log.info("Created profile for IAM user %s.", user_name) return info except boto.exception.BotoServerError as e: log.debug(e) - if 'Conflict' in e: - log.info('Profile already exists for IAM user %s.', user_name) - return 'Conflict' - log.error('Failed to update profile for IAM user %s.', user_name) + if "Conflict" in e: + log.info("Profile already exists for IAM user %s.", user_name) + return "Conflict" + log.error("Failed to update profile for IAM user %s.", user_name) return False -def delete_login_profile(user_name, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_login_profile(user_name, region=None, key=None, keyid=None, profile=None): + """ Deletes a login profile for the specified user. .. versionadded:: 2016.3.0 @@ -784,28 +800,27 @@ def delete_login_profile(user_name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.delete_login_profile user_name - ''' + """ user = get_user(user_name, region, key, keyid, profile) if not user: - log.error('IAM user %s does not exist', user_name) + log.error("IAM user %s does not exist", user_name) return False conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.delete_login_profile(user_name) - log.info('Deleted login profile for IAM user %s.', user_name) + log.info("Deleted login profile for IAM user %s.", user_name) return True except boto.exception.BotoServerError as e: log.debug(e) - if 'Not Found' in e: - log.info('Login profile already deleted for IAM user %s.', user_name) + if "Not Found" in e: + log.info("Login profile already deleted for IAM user %s.", user_name) return True - log.error('Failed to delete login profile for IAM user %s.', user_name) + log.error("Failed to delete login profile for IAM user %s.", user_name) return False -def get_all_mfa_devices(user_name, region=None, key=None, keyid=None, - profile=None): - ''' +def get_all_mfa_devices(user_name, region=None, key=None, keyid=None, profile=None): + """ Get all MFA devices associated with an IAM user. .. versionadded:: 2016.3.0 @@ -815,28 +830,31 @@ def get_all_mfa_devices(user_name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.get_all_mfa_devices user_name - ''' + """ user = get_user(user_name, region, key, keyid, profile) if not user: - log.error('IAM user %s does not exist', user_name) + log.error("IAM user %s does not exist", user_name) return False conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: result = conn.get_all_mfa_devices(user_name) - devices = result['list_mfa_devices_response']['list_mfa_devices_result']['mfa_devices'] + devices = result["list_mfa_devices_response"]["list_mfa_devices_result"][ + "mfa_devices" + ] return devices except boto.exception.BotoServerError as e: log.debug(e) - if 'Not Found' in e: - log.info('Could not find IAM user %s.', user_name) + if "Not Found" in e: + log.info("Could not find IAM user %s.", user_name) return [] - log.error('Failed to get all MFA devices for IAM user %s.', user_name) + log.error("Failed to get all MFA devices for IAM user %s.", user_name) return False -def deactivate_mfa_device(user_name, serial, region=None, key=None, keyid=None, - profile=None): - ''' +def deactivate_mfa_device( + user_name, serial, region=None, key=None, keyid=None, profile=None +): + """ Deactivates the specified MFA device and removes it from association with the user. @@ -847,27 +865,31 @@ def deactivate_mfa_device(user_name, serial, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.deactivate_mfa_device user_name serial_num - ''' + """ user = get_user(user_name, region, key, keyid, profile) if not user: - log.error('IAM user %s does not exist', user_name) + log.error("IAM user %s does not exist", user_name) return False conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.deactivate_mfa_device(user_name, serial) - log.info('Deactivated MFA device %s for IAM user %s.', serial, user_name) + log.info("Deactivated MFA device %s for IAM user %s.", serial, user_name) return True except boto.exception.BotoServerError as e: log.debug(e) - if 'Not Found' in e: - log.info('MFA device %s not associated with IAM user %s.', serial, user_name) + if "Not Found" in e: + log.info( + "MFA device %s not associated with IAM user %s.", serial, user_name + ) return True - log.error('Failed to deactivate MFA device %s for IAM user %s.', serial, user_name) + log.error( + "Failed to deactivate MFA device %s for IAM user %s.", serial, user_name + ) return False def delete_virtual_mfa_device(serial, region=None, key=None, keyid=None, profile=None): - ''' + """ Deletes the specified virtual MFA device. CLI Example: @@ -875,31 +897,37 @@ def delete_virtual_mfa_device(serial, region=None, key=None, keyid=None, profile .. code-block:: bash salt myminion boto_iam.delete_virtual_mfa_device serial_num - ''' - conn = __utils__['boto3.get_connection_func']('iam')() + """ + conn = __utils__["boto3.get_connection_func"]("iam")() try: conn.delete_virtual_mfa_device(SerialNumber=serial) - log.info('Deleted virtual MFA device %s.', serial) + log.info("Deleted virtual MFA device %s.", serial) return True except botocore.exceptions.ClientError as e: log.debug(e) - if 'NoSuchEntity' in six.text_type(e): - log.info('Virtual MFA device %s not found.', serial) + if "NoSuchEntity" in six.text_type(e): + log.info("Virtual MFA device %s not found.", serial) return True - log.error('Failed to delete virtual MFA device %s.', serial) + log.error("Failed to delete virtual MFA device %s.", serial) return False -def update_account_password_policy(allow_users_to_change_password=None, - hard_expiry=None, max_password_age=None, - minimum_password_length=None, - password_reuse_prevention=None, - require_lowercase_characters=None, - require_numbers=None, require_symbols=None, - require_uppercase_characters=None, - region=None, key=None, keyid=None, - profile=None): - ''' +def update_account_password_policy( + allow_users_to_change_password=None, + hard_expiry=None, + max_password_age=None, + minimum_password_length=None, + password_reuse_prevention=None, + require_lowercase_characters=None, + require_numbers=None, + require_symbols=None, + require_uppercase_characters=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Update the password policy for the AWS account. .. versionadded:: 2015.8.0 @@ -909,27 +937,31 @@ def update_account_password_policy(allow_users_to_change_password=None, .. code-block:: bash salt myminion boto_iam.update_account_password_policy True - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - conn.update_account_password_policy(allow_users_to_change_password, - hard_expiry, max_password_age, - minimum_password_length, - password_reuse_prevention, - require_lowercase_characters, - require_numbers, require_symbols, - require_uppercase_characters) - log.info('The password policy has been updated.') + conn.update_account_password_policy( + allow_users_to_change_password, + hard_expiry, + max_password_age, + minimum_password_length, + password_reuse_prevention, + require_lowercase_characters, + require_numbers, + require_symbols, + require_uppercase_characters, + ) + log.info("The password policy has been updated.") return True except boto.exception.BotoServerError as e: log.debug(e) - msg = 'Failed to update the password policy' + msg = "Failed to update the password policy" log.error(msg) return False def get_account_policy(region=None, key=None, keyid=None, profile=None): - ''' + """ Get account policy for the AWS account. .. versionadded:: 2015.8.0 @@ -939,21 +971,30 @@ def get_account_policy(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.get_account_policy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.get_account_password_policy() - return info.get_account_password_policy_response.get_account_password_policy_result.password_policy + return ( + info.get_account_password_policy_response.get_account_password_policy_result.password_policy + ) except boto.exception.BotoServerError as e: log.debug(e) - msg = 'Failed to update the password policy.' + msg = "Failed to update the password policy." log.error(msg) return False -def create_role(name, policy_document=None, path=None, region=None, key=None, - keyid=None, profile=None): - ''' +def create_role( + name, + policy_document=None, + path=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an instance role. CLI Example: @@ -961,7 +1002,7 @@ def create_role(name, policy_document=None, path=None, region=None, key=None, .. code-block:: bash salt myminion boto_iam.create_role myrole - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if role_exists(name, region, key, keyid, profile): @@ -969,18 +1010,17 @@ def create_role(name, policy_document=None, path=None, region=None, key=None, if not policy_document: policy_document = None try: - conn.create_role(name, assume_role_policy_document=policy_document, - path=path) - log.info('Created IAM role %s.', name) + conn.create_role(name, assume_role_policy_document=policy_document, path=path) + log.info("Created IAM role %s.", name) return True except boto.exception.BotoServerError as e: log.error(e) - log.error('Failed to create IAM role %s.', name) + log.error("Failed to create IAM role %s.", name) return False def delete_role(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete an IAM role. CLI Example: @@ -988,23 +1028,23 @@ def delete_role(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.delete_role myirole - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not role_exists(name, region, key, keyid, profile): return True try: conn.delete_role(name) - log.info('Deleted %s IAM role.', name) + log.info("Deleted %s IAM role.", name) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to delete %s IAM role.', name) + log.error("Failed to delete %s IAM role.", name) return False def profile_associated(role_name, profile_name, region, key, keyid, profile): - ''' + """ Check to see if an instance profile is associated with an IAM role. CLI Example: @@ -1012,7 +1052,7 @@ def profile_associated(role_name, profile_name, region, key, keyid, profile): .. code-block:: bash salt myminion boto_iam.profile_associated myirole myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) # The IAM module of boto doesn't return objects. Instead you need to grab @@ -1031,9 +1071,10 @@ def profile_associated(role_name, profile_name, region, key, keyid, profile): return False -def associate_profile_to_role(profile_name, role_name, region=None, key=None, - keyid=None, profile=None): - ''' +def associate_profile_to_role( + profile_name, role_name, region=None, key=None, keyid=None, profile=None +): + """ Associate an instance profile with an IAM role. CLI Example: @@ -1041,32 +1082,41 @@ def associate_profile_to_role(profile_name, role_name, region=None, key=None, .. code-block:: bash salt myminion boto_iam.associate_profile_to_role myirole myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not role_exists(role_name, region, key, keyid, profile): - log.error('IAM role %s does not exist.', role_name) + log.error("IAM role %s does not exist.", role_name) return False if not instance_profile_exists(profile_name, region, key, keyid, profile): - log.error('Instance profile %s does not exist.', profile_name) + log.error("Instance profile %s does not exist.", profile_name) return False - associated = profile_associated(role_name, profile_name, region, key, keyid, profile) + associated = profile_associated( + role_name, profile_name, region, key, keyid, profile + ) if associated: return True else: try: conn.add_role_to_instance_profile(profile_name, role_name) - log.info('Added %s instance profile to IAM role %s.', profile_name, role_name) + log.info( + "Added %s instance profile to IAM role %s.", profile_name, role_name + ) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to add %s instance profile to IAM role %s', profile_name, role_name) + log.error( + "Failed to add %s instance profile to IAM role %s", + profile_name, + role_name, + ) return False -def disassociate_profile_from_role(profile_name, role_name, region=None, - key=None, keyid=None, profile=None): - ''' +def disassociate_profile_from_role( + profile_name, role_name, region=None, key=None, keyid=None, profile=None +): + """ Disassociate an instance profile from an IAM role. CLI Example: @@ -1074,32 +1124,39 @@ def disassociate_profile_from_role(profile_name, role_name, region=None, .. code-block:: bash salt myminion boto_iam.disassociate_profile_from_role myirole myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not role_exists(role_name, region, key, keyid, profile): - log.error('IAM role %s does not exist.', role_name) + log.error("IAM role %s does not exist.", role_name) return False if not instance_profile_exists(profile_name, region, key, keyid, profile): - log.error('Instance profile %s does not exist.', profile_name) + log.error("Instance profile %s does not exist.", profile_name) return False - associated = profile_associated(role_name, profile_name, region, key, keyid, profile) + associated = profile_associated( + role_name, profile_name, region, key, keyid, profile + ) if not associated: return True else: try: conn.remove_role_from_instance_profile(profile_name, role_name) - log.info('Removed %s instance profile from IAM role %s.', profile_name, role_name) + log.info( + "Removed %s instance profile from IAM role %s.", profile_name, role_name + ) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to remove %s instance profile from IAM role %s.', profile_name, role_name) + log.error( + "Failed to remove %s instance profile from IAM role %s.", + profile_name, + role_name, + ) return False -def list_role_policies(role_name, region=None, key=None, keyid=None, - profile=None): - ''' +def list_role_policies(role_name, region=None, key=None, keyid=None, profile=None): + """ Get a list of policy names from a role. CLI Example: @@ -1107,7 +1164,7 @@ def list_role_policies(role_name, region=None, key=None, keyid=None, .. code-block:: bash salt myminion boto_iam.list_role_policies myirole - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -1119,9 +1176,10 @@ def list_role_policies(role_name, region=None, key=None, keyid=None, return [] -def get_role_policy(role_name, policy_name, region=None, key=None, - keyid=None, profile=None): - ''' +def get_role_policy( + role_name, policy_name, region=None, key=None, keyid=None, profile=None +): + """ Get a role policy. CLI Example: @@ -1129,7 +1187,7 @@ def get_role_policy(role_name, policy_name, region=None, key=None, .. code-block:: bash salt myminion boto_iam.get_role_policy myirole mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: @@ -1144,9 +1202,10 @@ def get_role_policy(role_name, policy_name, region=None, key=None, return {} -def create_role_policy(role_name, policy_name, policy, region=None, key=None, - keyid=None, profile=None): - ''' +def create_role_policy( + role_name, policy_name, policy, region=None, key=None, keyid=None, profile=None +): + """ Create or modify a role policy. CLI Example: @@ -1154,36 +1213,38 @@ def create_role_policy(role_name, policy_name, policy, region=None, key=None, .. code-block:: bash salt myminion boto_iam.create_role_policy myirole mypolicy '{"MyPolicy": "Statement": [{"Action": ["sqs:*"], "Effect": "Allow", "Resource": ["arn:aws:sqs:*:*:*"], "Sid": "MyPolicySqs1"}]}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) _policy = get_role_policy(role_name, policy_name, region, key, keyid, profile) - mode = 'create' + mode = "create" if _policy: if _policy == policy: return True - mode = 'modify' + mode = "modify" if isinstance(policy, six.string_types): policy = salt.utils.json.loads(policy, object_pairs_hook=odict.OrderedDict) try: _policy = salt.utils.json.dumps(policy) conn.put_role_policy(role_name, policy_name, _policy) - if mode == 'create': - msg = 'Successfully added policy %s to IAM role %s.' + if mode == "create": + msg = "Successfully added policy %s to IAM role %s." else: - msg = 'Successfully modified policy %s for IAM role %s.' + msg = "Successfully modified policy %s for IAM role %s." log.info(msg, policy_name, role_name) return True except boto.exception.BotoServerError as e: log.error(e) - log.error('Failed to %s policy %s for IAM role %s.', - mode, policy_name, role_name) + log.error( + "Failed to %s policy %s for IAM role %s.", mode, policy_name, role_name + ) return False -def delete_role_policy(role_name, policy_name, region=None, key=None, - keyid=None, profile=None): - ''' +def delete_role_policy( + role_name, policy_name, region=None, key=None, keyid=None, profile=None +): + """ Delete a role policy. CLI Example: @@ -1191,7 +1252,7 @@ def delete_role_policy(role_name, policy_name, region=None, key=None, .. code-block:: bash salt myminion boto_iam.delete_role_policy myirole mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) _policy = get_role_policy(role_name, policy_name, region, key, keyid, profile) @@ -1199,19 +1260,20 @@ def delete_role_policy(role_name, policy_name, region=None, key=None, return True try: conn.delete_role_policy(role_name, policy_name) - log.info('Successfully deleted policy %s for IAM role %s.', - policy_name, role_name) + log.info( + "Successfully deleted policy %s for IAM role %s.", policy_name, role_name + ) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to delete policy %s for IAM role %s.', - policy_name, role_name) + log.error("Failed to delete policy %s for IAM role %s.", policy_name, role_name) return False -def update_assume_role_policy(role_name, policy_document, region=None, - key=None, keyid=None, profile=None): - ''' +def update_assume_role_policy( + role_name, policy_document, region=None, key=None, keyid=None, profile=None +): + """ Update an assume role policy for a role. .. versionadded:: 2015.8.0 @@ -1221,25 +1283,26 @@ def update_assume_role_policy(role_name, policy_document, region=None, .. code-block:: bash salt myminion boto_iam.update_assume_role_policy myrole '{"Statement":"..."}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(policy_document, six.string_types): - policy_document = salt.utils.json.loads(policy_document, - object_pairs_hook=odict.OrderedDict) + policy_document = salt.utils.json.loads( + policy_document, object_pairs_hook=odict.OrderedDict + ) try: _policy_document = salt.utils.json.dumps(policy_document) conn.update_assume_role_policy(role_name, _policy_document) - log.info('Successfully updated assume role policy for IAM role %s.', role_name) + log.info("Successfully updated assume role policy for IAM role %s.", role_name) return True except boto.exception.BotoServerError as e: log.error(e) - log.error('Failed to update assume role policy for IAM role %s.', role_name) + log.error("Failed to update assume role policy for IAM role %s.", role_name) return False def build_policy(region=None, key=None, keyid=None, profile=None): - ''' + """ Build a default assume role policy. .. versionadded:: 2015.8.0 @@ -1249,11 +1312,11 @@ def build_policy(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.build_policy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if hasattr(conn, 'build_policy'): + if hasattr(conn, "build_policy"): policy = salt.utils.json.loads(conn.build_policy()) - elif hasattr(conn, '_build_policy'): + elif hasattr(conn, "_build_policy"): policy = salt.utils.json.loads(conn._build_policy()) else: return {} @@ -1262,20 +1325,21 @@ def build_policy(region=None, key=None, keyid=None, profile=None): # into strings, so let's do the same here. for key, policy_val in policy.items(): for statement in policy_val: - if (isinstance(statement['Action'], list) - and len(statement['Action']) == 1): - statement['Action'] = statement['Action'][0] - if (isinstance(statement['Principal']['Service'], list) - and len(statement['Principal']['Service']) == 1): - statement['Principal']['Service'] = statement['Principal']['Service'][0] + if isinstance(statement["Action"], list) and len(statement["Action"]) == 1: + statement["Action"] = statement["Action"][0] + if ( + isinstance(statement["Principal"]["Service"], list) + and len(statement["Principal"]["Service"]) == 1 + ): + statement["Principal"]["Service"] = statement["Principal"]["Service"][0] # build_policy doesn't add a version field, which AWS is going to set to a # default value, when we get it back, so let's set it. - policy['Version'] = '2008-10-17' + policy["Version"] = "2008-10-17" return policy def get_account_id(region=None, key=None, keyid=None, profile=None): - ''' + """ Get a the AWS account id associated with the used credentials. CLI Example: @@ -1283,39 +1347,36 @@ def get_account_id(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.get_account_id - ''' - cache_key = 'boto_iam.account_id' + """ + cache_key = "boto_iam.account_id" if cache_key not in __context__: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: ret = conn.get_user() # The get_user call returns an user ARN: # arn:aws:iam::027050522557:user/salt-test - arn = ret['get_user_response']['get_user_result']['user']['arn'] - account_id = arn.split(':')[4] + arn = ret["get_user_response"]["get_user_result"]["user"]["arn"] + account_id = arn.split(":")[4] except boto.exception.BotoServerError: # If call failed, then let's try to get the ARN from the metadata - timeout = boto.config.getfloat( - 'Boto', 'metadata_service_timeout', 1.0 - ) - attempts = boto.config.getint( - 'Boto', 'metadata_service_num_attempts', 1 - ) + timeout = boto.config.getfloat("Boto", "metadata_service_timeout", 1.0) + attempts = boto.config.getint("Boto", "metadata_service_num_attempts", 1) identity = boto.utils.get_instance_identity( timeout=timeout, num_retries=attempts ) try: - account_id = identity['document']['accountId'] + account_id = identity["document"]["accountId"] except KeyError: - log.error('Failed to get account id from instance_identity in' - ' boto_iam.get_account_id.') + log.error( + "Failed to get account id from instance_identity in" + " boto_iam.get_account_id." + ) __context__[cache_key] = account_id return __context__[cache_key] -def get_all_roles(path_prefix=None, region=None, key=None, keyid=None, - profile=None): - ''' +def get_all_roles(path_prefix=None, region=None, key=None, keyid=None, profile=None): + """ Get and return all IAM role details, starting at the optional path. .. versionadded:: 2016.3.0 @@ -1323,27 +1384,22 @@ def get_all_roles(path_prefix=None, region=None, key=None, keyid=None, CLI Example: salt-call boto_iam.get_all_roles - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return None _roles = conn.list_roles(path_prefix=path_prefix) roles = _roles.list_roles_response.list_roles_result.roles - marker = getattr( - _roles.list_roles_response.list_roles_result, 'marker', None - ) + marker = getattr(_roles.list_roles_response.list_roles_result, "marker", None) while marker: _roles = conn.list_roles(path_prefix=path_prefix, marker=marker) roles = roles + _roles.list_roles_response.list_roles_result.roles - marker = getattr( - _roles.list_roles_response.list_roles_result, 'marker', None - ) + marker = getattr(_roles.list_roles_response.list_roles_result, "marker", None) return roles -def get_all_users(path_prefix='/', region=None, key=None, keyid=None, - profile=None): - ''' +def get_all_users(path_prefix="/", region=None, key=None, keyid=None, profile=None): + """ Get and return all IAM user details, starting at the optional path. .. versionadded:: 2016.3.0 @@ -1351,26 +1407,30 @@ def get_all_users(path_prefix='/', region=None, key=None, keyid=None, CLI Example: salt-call boto_iam.get_all_users - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return None _users = conn.get_all_users(path_prefix=path_prefix) users = _users.list_users_response.list_users_result.users - marker = getattr( - _users.list_users_response.list_users_result, 'marker', None - ) + marker = getattr(_users.list_users_response.list_users_result, "marker", None) while marker: _users = conn.get_all_users(path_prefix=path_prefix, marker=marker) users = users + _users.list_users_response.list_users_result.users - marker = getattr( - _users.list_users_response.list_users_result, 'marker', None - ) + marker = getattr(_users.list_users_response.list_users_result, "marker", None) return users -def get_all_user_policies(user_name, marker=None, max_items=None, region=None, key=None, keyid=None, profile=None): - ''' +def get_all_user_policies( + user_name, + marker=None, + max_items=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get all user policies. .. versionadded:: 2015.8.0 @@ -1380,7 +1440,7 @@ def get_all_user_policies(user_name, marker=None, max_items=None, region=None, k .. code-block:: bash salt myminion boto_iam.get_all_user_policies myuser - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.get_all_user_policies(user_name, marker, max_items) @@ -1390,12 +1450,14 @@ def get_all_user_policies(user_name, marker=None, max_items=None, region=None, k return _list.policy_names except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to get policies for user %s.', user_name) + log.error("Failed to get policies for user %s.", user_name) return False -def get_user_policy(user_name, policy_name, region=None, key=None, keyid=None, profile=None): - ''' +def get_user_policy( + user_name, policy_name, region=None, key=None, keyid=None, profile=None +): + """ Retrieves the specified policy document for the specified user. .. versionadded:: 2015.8.0 @@ -1405,11 +1467,11 @@ def get_user_policy(user_name, policy_name, region=None, key=None, keyid=None, p .. code-block:: bash salt myminion boto_iam.get_user_policy myuser mypolicyname - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.get_user_policy(user_name, policy_name) - log.debug('Info for IAM user %s policy %s: %s.', user_name, policy_name, info) + log.debug("Info for IAM user %s policy %s: %s.", user_name, policy_name, info) if not info: return False info = info.get_user_policy_response.get_user_policy_result.policy_document @@ -1418,12 +1480,14 @@ def get_user_policy(user_name, policy_name, region=None, key=None, keyid=None, p return info except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to get policy %s for IAM user %s.', policy_name, user_name) + log.error("Failed to get policy %s for IAM user %s.", policy_name, user_name) return False -def put_user_policy(user_name, policy_name, policy_json, region=None, key=None, keyid=None, profile=None): - ''' +def put_user_policy( + user_name, policy_name, policy_json, region=None, key=None, keyid=None, profile=None +): + """ Adds or updates the specified policy document for the specified user. .. versionadded:: 2015.8.0 @@ -1433,29 +1497,30 @@ def put_user_policy(user_name, policy_name, policy_json, region=None, key=None, .. code-block:: bash salt myminion boto_iam.put_user_policy myuser policyname policyrules - ''' + """ user = get_user(user_name, region, key, keyid, profile) if not user: - log.error('IAM user %s does not exist', user_name) + log.error("IAM user %s does not exist", user_name) return False conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: if not isinstance(policy_json, six.string_types): policy_json = salt.utils.json.dumps(policy_json) - created = conn.put_user_policy(user_name, policy_name, - policy_json) + created = conn.put_user_policy(user_name, policy_name, policy_json) if created: - log.info('Created policy %s for IAM user %s.', policy_name, user_name) + log.info("Created policy %s for IAM user %s.", policy_name, user_name) return True - log.error('Could not create policy %s for IAM user %s.', policy_name, user_name) + log.error("Could not create policy %s for IAM user %s.", policy_name, user_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to create policy %s for IAM user %s.', policy_name, user_name) + log.error("Failed to create policy %s for IAM user %s.", policy_name, user_name) return False -def delete_user_policy(user_name, policy_name, region=None, key=None, keyid=None, profile=None): - ''' +def delete_user_policy( + user_name, policy_name, region=None, key=None, keyid=None, profile=None +): + """ Delete a user policy. CLI Example: @@ -1463,28 +1528,37 @@ def delete_user_policy(user_name, policy_name, region=None, key=None, keyid=None .. code-block:: bash salt myminion boto_iam.delete_user_policy myuser mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return False - _policy = get_user_policy( - user_name, policy_name, region, key, keyid, profile - ) + _policy = get_user_policy(user_name, policy_name, region, key, keyid, profile) if not _policy: return True try: conn.delete_user_policy(user_name, policy_name) - log.info('Successfully deleted policy %s for IAM user %s.', policy_name, user_name) + log.info( + "Successfully deleted policy %s for IAM user %s.", policy_name, user_name + ) return True except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to delete policy %s for IAM user %s.', policy_name, user_name) + log.error("Failed to delete policy %s for IAM user %s.", policy_name, user_name) return False -def upload_server_cert(cert_name, cert_body, private_key, cert_chain=None, path=None, - region=None, key=None, keyid=None, profile=None): - ''' +def upload_server_cert( + cert_name, + cert_body, + private_key, + cert_chain=None, + path=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Upload a certificate to Amazon. .. versionadded:: 2015.8.0 @@ -1505,7 +1579,7 @@ def upload_server_cert(cert_name, cert_body, private_key, cert_chain=None, path= :param keyid: The keyid to be used in order to connect :param profile: The profile that contains a dict of region, key, keyid :return: True / False - ''' + """ exists = get_server_certificate(cert_name, region, key, keyid, profile) if exists: @@ -1513,16 +1587,16 @@ def upload_server_cert(cert_name, cert_body, private_key, cert_chain=None, path= conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.upload_server_cert(cert_name, cert_body, private_key, cert_chain) - log.info('Created certificate %s.', cert_name) + log.info("Created certificate %s.", cert_name) return info except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to failed to create certificate %s.', cert_name) + log.error("Failed to failed to create certificate %s.", cert_name) return False def get_server_certificate(cert_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Returns certificate information from Amazon .. versionadded:: 2015.8.0 @@ -1532,7 +1606,7 @@ def get_server_certificate(cert_name, region=None, key=None, keyid=None, profile .. code-block:: bash salt myminion boto_iam.get_server_certificate mycert_name - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: info = conn.get_server_certificate(cert_name) @@ -1541,12 +1615,12 @@ def get_server_certificate(cert_name, region=None, key=None, keyid=None, profile return info except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to get certificate %s information.', cert_name) + log.error("Failed to get certificate %s information.", cert_name) return False def delete_server_cert(cert_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Deletes a certificate from Amazon. .. versionadded:: 2015.8.0 @@ -1556,19 +1630,18 @@ def delete_server_cert(cert_name, region=None, key=None, keyid=None, profile=Non .. code-block:: bash salt myminion boto_iam.delete_server_cert mycert_name - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: return conn.delete_server_cert(cert_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to delete certificate %s.', cert_name) + log.error("Failed to delete certificate %s.", cert_name) return False -def export_users(path_prefix='/', region=None, key=None, keyid=None, - profile=None): - ''' +def export_users(path_prefix="/", region=None, key=None, keyid=None, profile=None): + """ Get all IAM user details. Produces results that can be used to create an sls file. @@ -1577,7 +1650,7 @@ def export_users(path_prefix='/', region=None, key=None, keyid=None, CLI Example: salt-call boto_iam.export_users --out=txt | sed "s/local: //" > iam_users.sls - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return None @@ -1586,34 +1659,35 @@ def export_users(path_prefix='/', region=None, key=None, keyid=None, for user in users: name = user.user_name _policies = conn.get_all_user_policies(name, max_items=100) - _policies = _policies.list_user_policies_response.list_user_policies_result.policy_names + _policies = ( + _policies.list_user_policies_response.list_user_policies_result.policy_names + ) policies = {} for policy_name in _policies: _policy = conn.get_user_policy(name, policy_name) - _policy = salt.utils.json.loads(_unquote( + _policy = salt.utils.json.loads( + _unquote( _policy.get_user_policy_response.get_user_policy_result.policy_document - )) + ) + ) policies[policy_name] = _policy user_sls = [] user_sls.append({"name": name}) user_sls.append({"policies": policies}) user_sls.append({"path": user.path}) results["manage user " + name] = {"boto_iam.user_present": user_sls} - return __utils__['yaml.safe_dump']( - results, - default_flow_style=False, - indent=2) + return __utils__["yaml.safe_dump"](results, default_flow_style=False, indent=2) -def export_roles(path_prefix='/', region=None, key=None, keyid=None, profile=None): - ''' +def export_roles(path_prefix="/", region=None, key=None, keyid=None, profile=None): + """ Get all IAM role details. Produces results that can be used to create an sls file. CLI Example: salt-call boto_iam.export_roles --out=txt | sed "s/local: //" > iam_roles.sls - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: return None @@ -1622,39 +1696,43 @@ def export_roles(path_prefix='/', region=None, key=None, keyid=None, profile=Non for role in roles: name = role.role_name _policies = conn.list_role_policies(name, max_items=100) - _policies = _policies.list_role_policies_response.list_role_policies_result.policy_names + _policies = ( + _policies.list_role_policies_response.list_role_policies_result.policy_names + ) policies = {} for policy_name in _policies: _policy = conn.get_role_policy(name, policy_name) - _policy = salt.utils.json.loads(_unquote( - _policy.get_role_policy_response.get_role_policy_result.policy_document - )) + _policy = salt.utils.json.loads( + _unquote( + _policy.get_role_policy_response.get_role_policy_result.policy_document + ) + ) policies[policy_name] = _policy role_sls = [] role_sls.append({"name": name}) role_sls.append({"policies": policies}) - role_sls.append({'policy_document': salt.utils.json.loads(_unquote(role.assume_role_policy_document))}) + role_sls.append( + { + "policy_document": salt.utils.json.loads( + _unquote(role.assume_role_policy_document) + ) + } + ) role_sls.append({"path": role.path}) results["manage role " + name] = {"boto_iam_role.present": role_sls} - return __utils__['yaml.safe_dump']( - results, - default_flow_style=False, - indent=2) + return __utils__["yaml.safe_dump"](results, default_flow_style=False, indent=2) def _get_policy_arn(name, region=None, key=None, keyid=None, profile=None): - if name.startswith('arn:aws:iam:'): + if name.startswith("arn:aws:iam:"): return name - account_id = get_account_id( - region=region, key=key, keyid=keyid, profile=profile - ) - return 'arn:aws:iam::{0}:policy/{1}'.format(account_id, name) + account_id = get_account_id(region=region, key=key, keyid=keyid, profile=profile) + return "arn:aws:iam::{0}:policy/{1}".format(account_id, name) -def policy_exists(policy_name, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_exists(policy_name, region=None, key=None, keyid=None, profile=None): + """ Check to see if policy exists. CLI Example: @@ -1662,20 +1740,22 @@ def policy_exists(policy_name, .. code-block:: bash salt myminion boto_iam.instance_profile_exists myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - conn.get_policy(_get_policy_arn(policy_name, - region=region, key=key, keyid=keyid, profile=profile)) + conn.get_policy( + _get_policy_arn( + policy_name, region=region, key=key, keyid=keyid, profile=profile + ) + ) return True except boto.exception.BotoServerError: return False -def get_policy(policy_name, - region=None, key=None, keyid=None, profile=None): - ''' +def get_policy(policy_name, region=None, key=None, keyid=None, profile=None): + """ Check to see if policy exists. CLI Example: @@ -1683,20 +1763,31 @@ def get_policy(policy_name, .. code-block:: bash salt myminion boto_iam.instance_profile_exists myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - ret = conn.get_policy(_get_policy_arn(policy_name, - region=region, key=key, keyid=keyid, profile=profile)) - return ret.get('get_policy_response', {}).get('get_policy_result', {}) + ret = conn.get_policy( + _get_policy_arn( + policy_name, region=region, key=key, keyid=keyid, profile=profile + ) + ) + return ret.get("get_policy_response", {}).get("get_policy_result", {}) except boto.exception.BotoServerError: return None -def create_policy(policy_name, policy_document, path=None, description=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_policy( + policy_name, + policy_document, + path=None, + description=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a policy. CLI Example: @@ -1704,30 +1795,29 @@ def create_policy(policy_name, policy_document, path=None, description=None, .. code-block:: bash salt myminios boto_iam.create_policy mypolicy '{"Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["s3:Get*", "s3:List*"], "Resource": ["arn:aws:s3:::my-bucket/shared/*"]},]}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not isinstance(policy_document, six.string_types): policy_document = salt.utils.json.dumps(policy_document) params = {} - for arg in 'path', 'description': + for arg in "path", "description": if locals()[arg] is not None: params[arg] = locals()[arg] if policy_exists(policy_name, region, key, keyid, profile): return True try: conn.create_policy(policy_name, policy_document, **params) - log.info('Created IAM policy %s.', policy_name) + log.info("Created IAM policy %s.", policy_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to create IAM policy %s.', policy_name) + log.error("Failed to create IAM policy %s.", policy_name) return False return True -def delete_policy(policy_name, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_policy(policy_name, region=None, key=None, keyid=None, profile=None): + """ Delete a policy. CLI Example: @@ -1735,7 +1825,7 @@ def delete_policy(policy_name, .. code-block:: bash salt myminion boto_iam.delete_policy mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) @@ -1743,17 +1833,17 @@ def delete_policy(policy_name, return True try: conn.delete_policy(policy_arn) - log.info('Deleted %s policy.', policy_name) + log.info("Deleted %s policy.", policy_name) except boto.exception.BotoServerError as e: - aws = __utils__['boto.get_error'](e) + aws = __utils__["boto.get_error"](e) log.debug(aws) - log.error('Failed to delete %s policy: %s.', policy_name, aws.get('message')) + log.error("Failed to delete %s policy: %s.", policy_name, aws.get("message")) return False return True def list_policies(region=None, key=None, keyid=None, profile=None): - ''' + """ List policies. CLI Example: @@ -1761,24 +1851,29 @@ def list_policies(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.list_policies - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: policies = [] - for ret in __utils__['boto.paged_call'](conn.list_policies): - policies.append(ret.get('list_policies_response', {}).get('list_policies_result', {}).get('policies')) + for ret in __utils__["boto.paged_call"](conn.list_policies): + policies.append( + ret.get("list_policies_response", {}) + .get("list_policies_result", {}) + .get("policies") + ) return policies except boto.exception.BotoServerError as e: log.debug(e) - msg = 'Failed to list policy versions.' + msg = "Failed to list policy versions." log.error(msg) return [] -def policy_version_exists(policy_name, version_id, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_version_exists( + policy_name, version_id, region=None, key=None, keyid=None, profile=None +): + """ Check to see if policy exists. CLI Example: @@ -1786,7 +1881,7 @@ def policy_version_exists(policy_name, version_id, .. code-block:: bash salt myminion boto_iam.instance_profile_exists myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) @@ -1797,9 +1892,10 @@ def policy_version_exists(policy_name, version_id, return False -def get_policy_version(policy_name, version_id, - region=None, key=None, keyid=None, profile=None): - ''' +def get_policy_version( + policy_name, version_id, region=None, key=None, keyid=None, profile=None +): + """ Check to see if policy exists. CLI Example: @@ -1807,22 +1903,37 @@ def get_policy_version(policy_name, version_id, .. code-block:: bash salt myminion boto_iam.instance_profile_exists myiprofile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - ret = conn.get_policy_version(_get_policy_arn(policy_name, - region=region, key=key, keyid=keyid, profile=profile), version_id) - retval = ret.get('get_policy_version_response', {}).get('get_policy_version_result', {}).get('policy_version', {}) - retval['document'] = _unquote(retval.get('document')) - return {'policy_version': retval} + ret = conn.get_policy_version( + _get_policy_arn( + policy_name, region=region, key=key, keyid=keyid, profile=profile + ), + version_id, + ) + retval = ( + ret.get("get_policy_version_response", {}) + .get("get_policy_version_result", {}) + .get("policy_version", {}) + ) + retval["document"] = _unquote(retval.get("document")) + return {"policy_version": retval} except boto.exception.BotoServerError: return None -def create_policy_version(policy_name, policy_document, set_as_default=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_policy_version( + policy_name, + policy_document, + set_as_default=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a policy version. CLI Example: @@ -1830,30 +1941,36 @@ def create_policy_version(policy_name, policy_document, set_as_default=None, .. code-block:: bash salt myminios boto_iam.create_policy_version mypolicy '{"Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["s3:Get*", "s3:List*"], "Resource": ["arn:aws:s3:::my-bucket/shared/*"]},]}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not isinstance(policy_document, six.string_types): policy_document = salt.utils.json.dumps(policy_document) params = {} - for arg in ('set_as_default',): + for arg in ("set_as_default",): if locals()[arg] is not None: params[arg] = locals()[arg] policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) try: ret = conn.create_policy_version(policy_arn, policy_document, **params) - vid = ret.get('create_policy_version_response', {}).get('create_policy_version_result', {}).get('policy_version', {}).get('version_id') - log.info('Created IAM policy %s version %s.', policy_name, vid) - return {'created': True, 'version_id': vid} + vid = ( + ret.get("create_policy_version_response", {}) + .get("create_policy_version_result", {}) + .get("policy_version", {}) + .get("version_id") + ) + log.info("Created IAM policy %s version %s.", policy_name, vid) + return {"created": True, "version_id": vid} except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to create IAM policy %s version %s.', policy_name, vid) - return {'created': False, 'error': __utils__['boto.get_error'](e)} + log.error("Failed to create IAM policy %s version %s.", policy_name, vid) + return {"created": False, "error": __utils__["boto.get_error"](e)} -def delete_policy_version(policy_name, version_id, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_policy_version( + policy_name, version_id, region=None, key=None, keyid=None, profile=None +): + """ Delete a policy version. CLI Example: @@ -1861,7 +1978,7 @@ def delete_policy_version(policy_name, version_id, .. code-block:: bash salt myminion boto_iam.delete_policy_version mypolicy v1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) @@ -1869,19 +1986,22 @@ def delete_policy_version(policy_name, version_id, return True try: conn.delete_policy_version(policy_arn, version_id) - log.info('Deleted IAM policy %s version %s.', policy_name, version_id) + log.info("Deleted IAM policy %s version %s.", policy_name, version_id) except boto.exception.BotoServerError as e: - aws = __utils__['boto.get_error'](e) + aws = __utils__["boto.get_error"](e) log.debug(aws) - log.error('Failed to delete IAM policy %s version %s: %s', - policy_name, version_id, aws.get('message')) + log.error( + "Failed to delete IAM policy %s version %s: %s", + policy_name, + version_id, + aws.get("message"), + ) return False return True -def list_policy_versions(policy_name, - region=None, key=None, keyid=None, profile=None): - ''' +def list_policy_versions(policy_name, region=None, key=None, keyid=None, profile=None): + """ List versions of a policy. CLI Example: @@ -1889,22 +2009,27 @@ def list_policy_versions(policy_name, .. code-block:: bash salt myminion boto_iam.list_policy_versions mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) try: ret = conn.list_policy_versions(policy_arn) - return ret.get('list_policy_versions_response', {}).get('list_policy_versions_result', {}).get('versions') + return ( + ret.get("list_policy_versions_response", {}) + .get("list_policy_versions_result", {}) + .get("versions") + ) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to list versions for IAM policy %s.', policy_name) + log.error("Failed to list versions for IAM policy %s.", policy_name) return [] -def set_default_policy_version(policy_name, version_id, - region=None, key=None, keyid=None, profile=None): - ''' +def set_default_policy_version( + policy_name, version_id, region=None, key=None, keyid=None, profile=None +): + """ Set the default version of a policy. CLI Example: @@ -1912,25 +2037,30 @@ def set_default_policy_version(policy_name, version_id, .. code-block:: bash salt myminion boto_iam.set_default_policy_version mypolicy v1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) try: conn.set_default_policy_version(policy_arn, version_id) - log.info('Set %s policy to version %s.', policy_name, version_id) + log.info("Set %s policy to version %s.", policy_name, version_id) except boto.exception.BotoServerError as e: - aws = __utils__['boto.get_error'](e) + aws = __utils__["boto.get_error"](e) log.debug(aws) - log.error('Failed to set %s policy to version %s: %s', - policy_name, version_id, aws.get('message')) + log.error( + "Failed to set %s policy to version %s: %s", + policy_name, + version_id, + aws.get("message"), + ) return False return True -def attach_user_policy(policy_name, user_name, - region=None, key=None, keyid=None, profile=None): - ''' +def attach_user_policy( + policy_name, user_name, region=None, key=None, keyid=None, profile=None +): + """ Attach a managed policy to a user. CLI Example: @@ -1938,23 +2068,24 @@ def attach_user_policy(policy_name, user_name, .. code-block:: bash salt myminion boto_iam.attach_user_policy mypolicy myuser - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) try: conn.attach_user_policy(policy_arn, user_name) - log.info('Attached policy %s to IAM user %s.', policy_name, user_name) + log.info("Attached policy %s to IAM user %s.", policy_name, user_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to attach %s policy to IAM user %s.', policy_name, user_name) + log.error("Failed to attach %s policy to IAM user %s.", policy_name, user_name) return False return True -def detach_user_policy(policy_name, user_name, - region=None, key=None, keyid=None, profile=None): - ''' +def detach_user_policy( + policy_name, user_name, region=None, key=None, keyid=None, profile=None +): + """ Detach a managed policy to a user. CLI Example: @@ -1962,23 +2093,26 @@ def detach_user_policy(policy_name, user_name, .. code-block:: bash salt myminion boto_iam.detach_user_policy mypolicy myuser - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) try: conn.detach_user_policy(policy_arn, user_name) - log.info('Detached %s policy from IAM user %s.', policy_name, user_name) + log.info("Detached %s policy from IAM user %s.", policy_name, user_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to detach %s policy from IAM user %s.', policy_name, user_name) + log.error( + "Failed to detach %s policy from IAM user %s.", policy_name, user_name + ) return False return True -def attach_group_policy(policy_name, group_name, - region=None, key=None, keyid=None, profile=None): - ''' +def attach_group_policy( + policy_name, group_name, region=None, key=None, keyid=None, profile=None +): + """ Attach a managed policy to a group. CLI Example: @@ -1986,23 +2120,26 @@ def attach_group_policy(policy_name, group_name, .. code-block:: bash salt myminion boto_iam.attach_group_policy mypolicy mygroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) try: conn.attach_group_policy(policy_arn, group_name) - log.info('Attached policy %s to IAM group %s.', policy_name, group_name) + log.info("Attached policy %s to IAM group %s.", policy_name, group_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to attach policy %s to IAM group %s.', policy_name, group_name) + log.error( + "Failed to attach policy %s to IAM group %s.", policy_name, group_name + ) return False return True -def detach_group_policy(policy_name, group_name, - region=None, key=None, keyid=None, profile=None): - ''' +def detach_group_policy( + policy_name, group_name, region=None, key=None, keyid=None, profile=None +): + """ Detach a managed policy to a group. CLI Example: @@ -2010,23 +2147,26 @@ def detach_group_policy(policy_name, group_name, .. code-block:: bash salt myminion boto_iam.detach_group_policy mypolicy mygroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) try: conn.detach_group_policy(policy_arn, group_name) - log.info('Detached policy %s from IAM group %s.', policy_name, group_name) + log.info("Detached policy %s from IAM group %s.", policy_name, group_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to detach policy %s from IAM group %s.', policy_name, group_name) + log.error( + "Failed to detach policy %s from IAM group %s.", policy_name, group_name + ) return False return True -def attach_role_policy(policy_name, role_name, - region=None, key=None, keyid=None, profile=None): - ''' +def attach_role_policy( + policy_name, role_name, region=None, key=None, keyid=None, profile=None +): + """ Attach a managed policy to a role. CLI Example: @@ -2034,23 +2174,24 @@ def attach_role_policy(policy_name, role_name, .. code-block:: bash salt myminion boto_iam.attach_role_policy mypolicy myrole - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) try: conn.attach_role_policy(policy_arn, role_name) - log.info('Attached policy %s to IAM role %s.', policy_name, role_name) + log.info("Attached policy %s to IAM role %s.", policy_name, role_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to attach policy %s to IAM role %s.', policy_name, role_name) + log.error("Failed to attach policy %s to IAM role %s.", policy_name, role_name) return False return True -def detach_role_policy(policy_name, role_name, - region=None, key=None, keyid=None, profile=None): - ''' +def detach_role_policy( + policy_name, role_name, region=None, key=None, keyid=None, profile=None +): + """ Detach a managed policy to a role. CLI Example: @@ -2058,23 +2199,32 @@ def detach_role_policy(policy_name, role_name, .. code-block:: bash salt myminion boto_iam.detach_role_policy mypolicy myrole - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy_arn = _get_policy_arn(policy_name, region, key, keyid, profile) try: conn.detach_role_policy(policy_arn, role_name) - log.info('Detached policy %s from IAM role %s.', policy_name, role_name) + log.info("Detached policy %s from IAM role %s.", policy_name, role_name) except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to detach policy %s from IAM role %s.', policy_name, role_name) + log.error( + "Failed to detach policy %s from IAM role %s.", policy_name, role_name + ) return False return True -def list_entities_for_policy(policy_name, path_prefix=None, entity_filter=None, - region=None, key=None, keyid=None, profile=None): - ''' +def list_entities_for_policy( + policy_name, + path_prefix=None, + entity_filter=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ List entities that a policy is attached to. CLI Example: @@ -2082,12 +2232,12 @@ def list_entities_for_policy(policy_name, path_prefix=None, entity_filter=None, .. code-block:: bash salt myminion boto_iam.list_entities_for_policy mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) retries = 30 params = {} - for arg in ('path_prefix', 'entity_filter'): + for arg in ("path_prefix", "entity_filter"): if locals()[arg] is not None: params[arg] = locals()[arg] @@ -2095,28 +2245,43 @@ def list_entities_for_policy(policy_name, path_prefix=None, entity_filter=None, while retries: try: allret = { - 'policy_groups': [], - 'policy_users': [], - 'policy_roles': [], + "policy_groups": [], + "policy_users": [], + "policy_roles": [], } - for ret in __utils__['boto.paged_call'](conn.list_entities_for_policy, policy_arn=policy_arn, **params): + for ret in __utils__["boto.paged_call"]( + conn.list_entities_for_policy, policy_arn=policy_arn, **params + ): for k, v in six.iteritems(allret): - v.extend(ret.get('list_entities_for_policy_response', {}).get('list_entities_for_policy_result', {}).get(k)) + v.extend( + ret.get("list_entities_for_policy_response", {}) + .get("list_entities_for_policy_result", {}) + .get(k) + ) return allret except boto.exception.BotoServerError as e: - if e.error_code == 'Throttling': + if e.error_code == "Throttling": log.debug("Throttled by AWS API, will retry in 5 seconds...") time.sleep(5) retries -= 1 continue - log.error('Failed to list entities for IAM policy %s: %s', policy_name, e.message) + log.error( + "Failed to list entities for IAM policy %s: %s", policy_name, e.message + ) return {} return {} -def list_attached_user_policies(user_name, path_prefix=None, entity_filter=None, - region=None, key=None, keyid=None, profile=None): - ''' +def list_attached_user_policies( + user_name, + path_prefix=None, + entity_filter=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ List entities attached to the given user. CLI Example: @@ -2124,30 +2289,45 @@ def list_attached_user_policies(user_name, path_prefix=None, entity_filter=None, .. code-block:: bash salt myminion boto_iam.list_entities_for_policy mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - params = {'UserName': user_name} + params = {"UserName": user_name} if path_prefix is not None: - params['PathPrefix'] = path_prefix + params["PathPrefix"] = path_prefix policies = [] try: # Using conn.get_response is a bit of a hack, but it avoids having to # rewrite this whole module based on boto3 - for ret in __utils__['boto.paged_call'](conn.get_response, 'ListAttachedUserPolicies', params, list_marker='AttachedPolicies'): - policies.extend(ret.get('list_attached_user_policies_response', {}).get('list_attached_user_policies_result', {} - ).get('attached_policies', [])) + for ret in __utils__["boto.paged_call"]( + conn.get_response, + "ListAttachedUserPolicies", + params, + list_marker="AttachedPolicies", + ): + policies.extend( + ret.get("list_attached_user_policies_response", {}) + .get("list_attached_user_policies_result", {}) + .get("attached_policies", []) + ) return policies except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to list attached policies for IAM user %s.', user_name) + log.error("Failed to list attached policies for IAM user %s.", user_name) return [] -def list_attached_group_policies(group_name, path_prefix=None, entity_filter=None, - region=None, key=None, keyid=None, profile=None): - ''' +def list_attached_group_policies( + group_name, + path_prefix=None, + entity_filter=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ List entities attached to the given group. CLI Example: @@ -2155,30 +2335,45 @@ def list_attached_group_policies(group_name, path_prefix=None, entity_filter=Non .. code-block:: bash salt myminion boto_iam.list_entities_for_policy mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - params = {'GroupName': group_name} + params = {"GroupName": group_name} if path_prefix is not None: - params['PathPrefix'] = path_prefix + params["PathPrefix"] = path_prefix policies = [] try: # Using conn.get_response is a bit of a hack, but it avoids having to # rewrite this whole module based on boto3 - for ret in __utils__['boto.paged_call'](conn.get_response, 'ListAttachedGroupPolicies', params, list_marker='AttachedPolicies'): - policies.extend(ret.get('list_attached_group_policies_response', {}).get('list_attached_group_policies_result', {} - ).get('attached_policies', [])) + for ret in __utils__["boto.paged_call"]( + conn.get_response, + "ListAttachedGroupPolicies", + params, + list_marker="AttachedPolicies", + ): + policies.extend( + ret.get("list_attached_group_policies_response", {}) + .get("list_attached_group_policies_result", {}) + .get("attached_policies", []) + ) return policies except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to list attached policies for IAM group %s.', group_name) + log.error("Failed to list attached policies for IAM group %s.", group_name) return [] -def list_attached_role_policies(role_name, path_prefix=None, entity_filter=None, - region=None, key=None, keyid=None, profile=None): - ''' +def list_attached_role_policies( + role_name, + path_prefix=None, + entity_filter=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ List entities attached to the given role. CLI Example: @@ -2186,29 +2381,39 @@ def list_attached_role_policies(role_name, path_prefix=None, entity_filter=None, .. code-block:: bash salt myminion boto_iam.list_entities_for_policy mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - params = {'RoleName': role_name} + params = {"RoleName": role_name} if path_prefix is not None: - params['PathPrefix'] = path_prefix + params["PathPrefix"] = path_prefix policies = [] try: # Using conn.get_response is a bit of a hack, but it avoids having to # rewrite this whole module based on boto3 - for ret in __utils__['boto.paged_call'](conn.get_response, 'ListAttachedRolePolicies', params, list_marker='AttachedPolicies'): - policies.extend(ret.get('list_attached_role_policies_response', {}).get('list_attached_role_policies_result', {} - ).get('attached_policies', [])) + for ret in __utils__["boto.paged_call"]( + conn.get_response, + "ListAttachedRolePolicies", + params, + list_marker="AttachedPolicies", + ): + policies.extend( + ret.get("list_attached_role_policies_response", {}) + .get("list_attached_role_policies_result", {}) + .get("attached_policies", []) + ) return policies except boto.exception.BotoServerError as e: log.debug(e) - log.error('Failed to list attached policies for IAM role %s.', role_name) + log.error("Failed to list attached policies for IAM role %s.", role_name) return [] -def create_saml_provider(name, saml_metadata_document, region=None, key=None, keyid=None, profile=None): - ''' +def create_saml_provider( + name, saml_metadata_document, region=None, key=None, keyid=None, profile=None +): + """ Create SAML provider CLI Example: @@ -2216,21 +2421,21 @@ def create_saml_provider(name, saml_metadata_document, region=None, key=None, ke .. code-block:: bash salt myminion boto_iam.create_saml_provider my_saml_provider_name saml_metadata_document - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.create_saml_provider(saml_metadata_document, name) - log.info('Successfully created %s SAML provider.', name) + log.info("Successfully created %s SAML provider.", name) return True except boto.exception.BotoServerError as e: - aws = __utils__['boto.get_error'](e) + aws = __utils__["boto.get_error"](e) log.debug(aws) - log.error('Failed to create SAML provider %s.', name) + log.error("Failed to create SAML provider %s.", name) return False def get_saml_provider_arn(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get SAML provider CLI Example: @@ -2238,23 +2443,27 @@ def get_saml_provider_arn(name, region=None, key=None, keyid=None, profile=None) .. code-block:: bash salt myminion boto_iam.get_saml_provider_arn my_saml_provider_name - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: response = conn.list_saml_providers() - for saml_provider in response.list_saml_providers_response.list_saml_providers_result.saml_provider_list: - if saml_provider['arn'].endswith(':saml-provider/' + name): - return saml_provider['arn'] + for ( + saml_provider + ) in ( + response.list_saml_providers_response.list_saml_providers_result.saml_provider_list + ): + if saml_provider["arn"].endswith(":saml-provider/" + name): + return saml_provider["arn"] return False except boto.exception.BotoServerError as e: - aws = __utils__['boto.get_error'](e) + aws = __utils__["boto.get_error"](e) log.debug(aws) - log.error('Failed to get ARN of SAML provider %s.', name) + log.error("Failed to get ARN of SAML provider %s.", name) return False def delete_saml_provider(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete SAML provider CLI Example: @@ -2262,25 +2471,27 @@ def delete_saml_provider(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.delete_saml_provider my_saml_provider_name - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - saml_provider_arn = get_saml_provider_arn(name, region=region, key=key, keyid=keyid, profile=profile) + saml_provider_arn = get_saml_provider_arn( + name, region=region, key=key, keyid=keyid, profile=profile + ) if not saml_provider_arn: - log.info('SAML provider %s not found.', name) + log.info("SAML provider %s not found.", name) return True conn.delete_saml_provider(saml_provider_arn) - log.info('Successfully deleted SAML provider %s.', name) + log.info("Successfully deleted SAML provider %s.", name) return True except boto.exception.BotoServerError as e: - aws = __utils__['boto.get_error'](e) + aws = __utils__["boto.get_error"](e) log.debug(aws) - log.error('Failed to delete SAML provider %s.', name) + log.error("Failed to delete SAML provider %s.", name) return False def list_saml_providers(region=None, key=None, keyid=None, profile=None): - ''' + """ List SAML providers. CLI Example: @@ -2288,22 +2499,24 @@ def list_saml_providers(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.list_saml_providers - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: providers = [] info = conn.list_saml_providers() - for arn in info['list_saml_providers_response']['list_saml_providers_result']['saml_provider_list']: - providers.append(arn['arn'].rsplit('/', 1)[1]) + for arn in info["list_saml_providers_response"]["list_saml_providers_result"][ + "saml_provider_list" + ]: + providers.append(arn["arn"].rsplit("/", 1)[1]) return providers except boto.exception.BotoServerError as e: - log.debug(__utils__['boto.get_error'](e)) - log.error('Failed to get list of SAML providers.') + log.debug(__utils__["boto.get_error"](e)) + log.error("Failed to get list of SAML providers.") return False def get_saml_provider(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get SAML provider document. CLI Example: @@ -2311,19 +2524,23 @@ def get_saml_provider(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_iam.get_saml_provider arn - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: provider = conn.get_saml_provider(name) - return provider['get_saml_provider_response']['get_saml_provider_result']['saml_metadata_document'] + return provider["get_saml_provider_response"]["get_saml_provider_result"][ + "saml_metadata_document" + ] except boto.exception.BotoServerError as e: - log.debug(__utils__['boto.get_error'](e)) - log.error('Failed to get SAML provider document %s.', name) + log.debug(__utils__["boto.get_error"](e)) + log.error("Failed to get SAML provider document %s.", name) return False -def update_saml_provider(name, saml_metadata_document, region=None, key=None, keyid=None, profile=None): - ''' +def update_saml_provider( + name, saml_metadata_document, region=None, key=None, keyid=None, profile=None +): + """ Update SAML provider. CLI Example: @@ -2331,17 +2548,19 @@ def update_saml_provider(name, saml_metadata_document, region=None, key=None, ke .. code-block:: bash salt myminion boto_iam.update_saml_provider my_saml_provider_name saml_metadata_document - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - saml_provider_arn = get_saml_provider_arn(name, region=region, key=key, keyid=keyid, profile=profile) + saml_provider_arn = get_saml_provider_arn( + name, region=region, key=key, keyid=keyid, profile=profile + ) if not saml_provider_arn: - log.info('SAML provider %s not found.', name) + log.info("SAML provider %s not found.", name) return False if conn.update_saml_provider(name, saml_metadata_document): return True return False except boto.exception.BotoServerError as e: - log.debug(__utils__['boto.get_error'](e)) - log.error('Failed to update SAML provider %s.', name) + log.debug(__utils__["boto.get_error"](e)) + log.error("Failed to update SAML provider %s.", name) return False diff --git a/salt/modules/boto_iot.py b/salt/modules/boto_iot.py index 977eb434714..3cc8a979afa 100644 --- a/salt/modules/boto_iot.py +++ b/salt/modules/boto_iot.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon IoT .. versionadded:: 2016.3.0 @@ -45,33 +45,38 @@ The dependencies listed above can be installed via package or pip. key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs region: us-east-1 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging + import datetime +import logging # Import Salt libs import salt.utils.compat import salt.utils.json import salt.utils.versions -log = logging.getLogger(__name__) - # Import third party libs from salt.ext.six import string_types + +log = logging.getLogger(__name__) + + # pylint: disable=import-error try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import boto3 - #pylint: enable=unused-import + + # pylint: enable=unused-import from botocore.exceptions import ClientError from botocore import __version__ as found_botocore_version - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -79,28 +84,24 @@ except ImportError: def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # the boto_lambda execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 - return salt.utils.versions.check_boto_reqs( - boto3_ver='1.2.1', - botocore_ver='1.4.41' - ) + return salt.utils.versions.check_boto_reqs(boto3_ver="1.2.1", botocore_ver="1.4.41") def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 'iot') + __utils__["boto3.assign_funcs"](__name__, "iot") -def thing_type_exists(thingTypeName, - region=None, key=None, keyid=None, profile=None): - ''' +def thing_type_exists(thingTypeName, region=None, key=None, keyid=None, profile=None): + """ Given a thing type name, check to see if the given thing type exists Returns True if the given thing type exists and returns False if the @@ -114,25 +115,24 @@ def thing_type_exists(thingTypeName, salt myminion boto_iot.thing_type_exists mythingtype - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) res = conn.describe_thing_type(thingTypeName=thingTypeName) - if res.get('thingTypeName'): - return {'exists': True} + if res.get("thingTypeName"): + return {"exists": True} else: - return {'exists': False} + return {"exists": False} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'exists': False} - return {'error': err} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"exists": False} + return {"error": err} -def describe_thing_type(thingTypeName, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_thing_type(thingTypeName, region=None, key=None, keyid=None, profile=None): + """ Given a thing type name describe its properties. Returns a dictionary of interesting properties. @@ -145,32 +145,38 @@ def describe_thing_type(thingTypeName, salt myminion boto_iot.describe_thing_type mythingtype - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) res = conn.describe_thing_type(thingTypeName=thingTypeName) if res: - res.pop('ResponseMetadata', None) - thingTypeMetadata = res.get('thingTypeMetadata') + res.pop("ResponseMetadata", None) + thingTypeMetadata = res.get("thingTypeMetadata") if thingTypeMetadata: - for dtype in ('creationDate', 'deprecationDate'): + for dtype in ("creationDate", "deprecationDate"): dval = thingTypeMetadata.get(dtype) if dval and isinstance(dval, datetime.date): - thingTypeMetadata[dtype] = '{0}'.format(dval) - return {'thing_type': res} + thingTypeMetadata[dtype] = "{0}".format(dval) + return {"thing_type": res} else: - return {'thing_type': None} + return {"thing_type": None} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'thing_type': None} - return {'error': err} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"thing_type": None} + return {"error": err} -def create_thing_type(thingTypeName, thingTypeDescription, - searchableAttributesList, region=None, key=None, - keyid=None, profile=None): - ''' +def create_thing_type( + thingTypeName, + thingTypeDescription, + searchableAttributesList, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, create a thing type. Returns {created: true} if the thing type was created and returns @@ -185,33 +191,35 @@ def create_thing_type(thingTypeName, thingTypeDescription, salt myminion boto_iot.create_thing_type mythingtype \\ thingtype_description_string '["searchable_attr_1", "searchable_attr_2"]' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) thingTypeProperties = dict( thingTypeDescription=thingTypeDescription, - searchableAttributes=searchableAttributesList + searchableAttributes=searchableAttributesList, ) thingtype = conn.create_thing_type( - thingTypeName=thingTypeName, - thingTypeProperties=thingTypeProperties + thingTypeName=thingTypeName, thingTypeProperties=thingTypeProperties ) if thingtype: - log.info('The newly created thing type ARN is %s', thingtype['thingTypeArn']) + log.info( + "The newly created thing type ARN is %s", thingtype["thingTypeArn"] + ) - return {'created': True, 'thingTypeArn': thingtype['thingTypeArn']} + return {"created": True, "thingTypeArn": thingtype["thingTypeArn"]} else: - log.warning('thing type was not created') - return {'created': False} + log.warning("thing type was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def deprecate_thing_type(thingTypeName, undoDeprecate=False, - region=None, key=None, keyid=None, profile=None): - ''' +def deprecate_thing_type( + thingTypeName, undoDeprecate=False, region=None, key=None, keyid=None, profile=None +): + """ Given a thing type name, deprecate it when undoDeprecate is False and undeprecate it when undoDeprecate is True. @@ -226,23 +234,21 @@ def deprecate_thing_type(thingTypeName, undoDeprecate=False, salt myminion boto_iot.deprecate_thing_type mythingtype - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.deprecate_thing_type( - thingTypeName=thingTypeName, - undoDeprecate=undoDeprecate + thingTypeName=thingTypeName, undoDeprecate=undoDeprecate ) deprecated = True if undoDeprecate is False else False - return {'deprecated': deprecated} + return {"deprecated": deprecated} except ClientError as e: - return {'deprecated': False, 'error': __utils__['boto3.get_error'](e)} + return {"deprecated": False, "error": __utils__["boto3.get_error"](e)} -def delete_thing_type(thingTypeName, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_thing_type(thingTypeName, region=None, key=None, keyid=None, profile=None): + """ Given a thing type name, delete it. Returns {deleted: true} if the thing type was deleted and returns @@ -256,22 +262,21 @@ def delete_thing_type(thingTypeName, salt myminion boto_iot.delete_thing_type mythingtype - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_thing_type(thingTypeName=thingTypeName) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'deleted': True} - return {'deleted': False, 'error': err} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"deleted": True} + return {"deleted": False, "error": err} -def policy_exists(policyName, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_exists(policyName, region=None, key=None, keyid=None, profile=None): + """ Given a policy name, check to see if the given policy exists. Returns True if the given policy exists and returns False if the given @@ -283,22 +288,23 @@ def policy_exists(policyName, salt myminion boto_iot.policy_exists mypolicy - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.get_policy(policyName=policyName) - return {'exists': True} + return {"exists": True} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'exists': False} - return {'error': err} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"exists": False} + return {"error": err} -def create_policy(policyName, policyDocument, - region=None, key=None, keyid=None, profile=None): - ''' +def create_policy( + policyName, policyDocument, region=None, key=None, keyid=None, profile=None +): + """ Given a valid config, create a policy. Returns {created: true} if the policy was created and returns @@ -314,28 +320,30 @@ def create_policy(policyName, policyDocument, "Action":["iot:Publish"],\\ "Resource":["arn:::::topic/foo/bar"]}]}' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not isinstance(policyDocument, string_types): policyDocument = salt.utils.json.dumps(policyDocument) - policy = conn.create_policy(policyName=policyName, - policyDocument=policyDocument) + policy = conn.create_policy( + policyName=policyName, policyDocument=policyDocument + ) if policy: - log.info('The newly created policy version is %s', policy['policyVersionId']) + log.info( + "The newly created policy version is %s", policy["policyVersionId"] + ) - return {'created': True, 'versionId': policy['policyVersionId']} + return {"created": True, "versionId": policy["policyVersionId"]} else: - log.warning('Policy was not created') - return {'created': False} + log.warning("Policy was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete_policy(policyName, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_policy(policyName, region=None, key=None, keyid=None, profile=None): + """ Given a policy name, delete it. Returns {deleted: true} if the policy was deleted and returns @@ -347,19 +355,18 @@ def delete_policy(policyName, salt myminion boto_iot.delete_policy mypolicy - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_policy(policyName=policyName) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def describe_policy(policyName, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_policy(policyName, region=None, key=None, keyid=None, profile=None): + """ Given a policy name describe its properties. Returns a dictionary of interesting properties. @@ -370,27 +377,27 @@ def describe_policy(policyName, salt myminion boto_iot.describe_policy mypolicy - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policy = conn.get_policy(policyName=policyName) if policy: - keys = ('policyName', 'policyArn', 'policyDocument', - 'defaultVersionId') - return {'policy': dict([(k, policy.get(k)) for k in keys])} + keys = ("policyName", "policyArn", "policyDocument", "defaultVersionId") + return {"policy": dict([(k, policy.get(k)) for k in keys])} else: - return {'policy': None} + return {"policy": None} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'policy': None} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"policy": None} + return {"error": __utils__["boto3.get_error"](e)} -def policy_version_exists(policyName, policyVersionId, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_version_exists( + policyName, policyVersionId, region=None, key=None, keyid=None, profile=None +): + """ Given a policy name and version ID, check to see if the given policy version exists. Returns True if the given policy version exists and returns False if the given @@ -402,23 +409,31 @@ def policy_version_exists(policyName, policyVersionId, salt myminion boto_iot.policy_version_exists mypolicy versionid - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - policy = conn.get_policy_version(policyName=policyName, - policyversionId=policyVersionId) - return {'exists': bool(policy)} + policy = conn.get_policy_version( + policyName=policyName, policyversionId=policyVersionId + ) + return {"exists": bool(policy)} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'exists': False} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"exists": False} + return {"error": __utils__["boto3.get_error"](e)} -def create_policy_version(policyName, policyDocument, setAsDefault=False, - region=None, key=None, keyid=None, profile=None): - ''' +def create_policy_version( + policyName, + policyDocument, + setAsDefault=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, create a new version of a policy. Returns {created: true} if the policy version was created and returns @@ -431,29 +446,34 @@ def create_policy_version(policyName, policyDocument, setAsDefault=False, salt myminion boto_iot.create_policy_version my_policy \\ '{"Statement":[{"Effect":"Allow","Action":["iot:Publish"],"Resource":["arn:::::topic/foo/bar"]}]}' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not isinstance(policyDocument, string_types): policyDocument = salt.utils.json.dumps(policyDocument) - policy = conn.create_policy_version(policyName=policyName, - policyDocument=policyDocument, - setAsDefault=setAsDefault) + policy = conn.create_policy_version( + policyName=policyName, + policyDocument=policyDocument, + setAsDefault=setAsDefault, + ) if policy: - log.info('The newly created policy version is %s', policy['policyVersionId']) + log.info( + "The newly created policy version is %s", policy["policyVersionId"] + ) - return {'created': True, 'name': policy['policyVersionId']} + return {"created": True, "name": policy["policyVersionId"]} else: - log.warning('Policy version was not created') - return {'created': False} + log.warning("Policy version was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete_policy_version(policyName, policyVersionId, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_policy_version( + policyName, policyVersionId, region=None, key=None, keyid=None, profile=None +): + """ Given a policy name and version, delete it. Returns {deleted: true} if the policy version was deleted and returns @@ -465,20 +485,22 @@ def delete_policy_version(policyName, policyVersionId, salt myminion boto_iot.delete_policy_version mypolicy version - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.delete_policy_version(policyName=policyName, - policyVersionId=policyVersionId) - return {'deleted': True} + conn.delete_policy_version( + policyName=policyName, policyVersionId=policyVersionId + ) + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def describe_policy_version(policyName, policyVersionId, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_policy_version( + policyName, policyVersionId, region=None, key=None, keyid=None, profile=None +): + """ Given a policy name and version describe its properties. Returns a dictionary of interesting properties. @@ -489,27 +511,33 @@ def describe_policy_version(policyName, policyVersionId, salt myminion boto_iot.describe_policy_version mypolicy version - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - policy = conn.get_policy_version(policyName=policyName, - policyVersionId=policyVersionId) + policy = conn.get_policy_version( + policyName=policyName, policyVersionId=policyVersionId + ) if policy: - keys = ('policyName', 'policyArn', 'policyDocument', - 'policyVersionId', 'isDefaultVersion') - return {'policy': dict([(k, policy.get(k)) for k in keys])} + keys = ( + "policyName", + "policyArn", + "policyDocument", + "policyVersionId", + "isDefaultVersion", + ) + return {"policy": dict([(k, policy.get(k)) for k in keys])} else: - return {'policy': None} + return {"policy": None} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'policy': None} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"policy": None} + return {"error": __utils__["boto3.get_error"](e)} def list_policies(region=None, key=None, keyid=None, profile=None): - ''' + """ List all policies Returns list of policies @@ -528,24 +556,23 @@ def list_policies(region=None, key=None, keyid=None, profile=None): - {...} - {...} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) policies = [] - for ret in __utils__['boto3.paged_call'](conn.list_policies, - marker_flag='nextMarker', - marker_arg='marker'): - policies.extend(ret['policies']) + for ret in __utils__["boto3.paged_call"]( + conn.list_policies, marker_flag="nextMarker", marker_arg="marker" + ): + policies.extend(ret["policies"]) if not bool(policies): - log.warning('No policies found') - return {'policies': policies} + log.warning("No policies found") + return {"policies": policies} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def list_policy_versions(policyName, - region=None, key=None, keyid=None, profile=None): - ''' +def list_policy_versions(policyName, region=None, key=None, keyid=None, profile=None): + """ List the versions available for the given policy. CLI Example: @@ -562,25 +589,28 @@ def list_policy_versions(policyName, - {...} - {...} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) vers = [] - for ret in __utils__['boto3.paged_call'](conn.list_policy_versions, - marker_flag='nextMarker', - marker_arg='marker', - policyName=policyName): - vers.extend(ret['policyVersions']) + for ret in __utils__["boto3.paged_call"]( + conn.list_policy_versions, + marker_flag="nextMarker", + marker_arg="marker", + policyName=policyName, + ): + vers.extend(ret["policyVersions"]) if not bool(vers): - log.warning('No versions found') - return {'policyVersions': vers} + log.warning("No versions found") + return {"policyVersions": vers} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def set_default_policy_version(policyName, policyVersionId, - region=None, key=None, keyid=None, profile=None): - ''' +def set_default_policy_version( + policyName, policyVersionId, region=None, key=None, keyid=None, profile=None +): + """ Sets the specified version of the specified policy as the policy's default (operative) version. This action affects all certificates that the policy is attached to. @@ -595,19 +625,19 @@ def set_default_policy_version(policyName, policyVersionId, salt myminion boto_iot.set_default_policy_version mypolicy versionid - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.set_default_policy_version(policyName=policyName, - policyVersionId=str(policyVersionId)) # future lint: disable=blacklisted-function - return {'changed': True} + conn.set_default_policy_version( + policyName=policyName, policyVersionId=str(policyVersionId) + ) # future lint: disable=blacklisted-function + return {"changed": True} except ClientError as e: - return {'changed': False, 'error': __utils__['boto3.get_error'](e)} + return {"changed": False, "error": __utils__["boto3.get_error"](e)} -def list_principal_policies(principal, - region=None, key=None, keyid=None, profile=None): - ''' +def list_principal_policies(principal, region=None, key=None, keyid=None, profile=None): + """ List the policies attached to the given principal. CLI Example: @@ -624,25 +654,28 @@ def list_principal_policies(principal, - {...} - {...} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) vers = [] - for ret in __utils__['boto3.paged_call'](conn.list_principal_policies, - principal=principal, - marker_flag='nextMarker', - marker_arg='marker'): - vers.extend(ret['policies']) + for ret in __utils__["boto3.paged_call"]( + conn.list_principal_policies, + principal=principal, + marker_flag="nextMarker", + marker_arg="marker", + ): + vers.extend(ret["policies"]) if not bool(vers): - log.warning('No policies found') - return {'policies': vers} + log.warning("No policies found") + return {"policies": vers} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def attach_principal_policy(policyName, principal, - region=None, key=None, keyid=None, profile=None): - ''' +def attach_principal_policy( + policyName, principal, region=None, key=None, keyid=None, profile=None +): + """ Attach the specified policy to the specified principal (certificate or other credential.) @@ -656,19 +689,19 @@ def attach_principal_policy(policyName, principal, salt myminion boto_iot.attach_principal_policy mypolicy mycognitoID - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.attach_principal_policy(policyName=policyName, - principal=principal) - return {'attached': True} + conn.attach_principal_policy(policyName=policyName, principal=principal) + return {"attached": True} except ClientError as e: - return {'attached': False, 'error': __utils__['boto3.get_error'](e)} + return {"attached": False, "error": __utils__["boto3.get_error"](e)} -def detach_principal_policy(policyName, principal, - region=None, key=None, keyid=None, profile=None): - ''' +def detach_principal_policy( + policyName, principal, region=None, key=None, keyid=None, profile=None +): + """ Detach the specified policy from the specified principal (certificate or other credential.) @@ -681,19 +714,17 @@ def detach_principal_policy(policyName, principal, salt myminion boto_iot.detach_principal_policy mypolicy mycognitoID - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.detach_principal_policy(policyName=policyName, - principal=principal) - return {'detached': True} + conn.detach_principal_policy(policyName=policyName, principal=principal) + return {"detached": True} except ClientError as e: - return {'detached': False, 'error': __utils__['boto3.get_error'](e)} + return {"detached": False, "error": __utils__["boto3.get_error"](e)} -def topic_rule_exists(ruleName, - region=None, key=None, keyid=None, profile=None): - ''' +def topic_rule_exists(ruleName, region=None, key=None, keyid=None, profile=None): + """ Given a rule name, check to see if the given rule exists. Returns True if the given rule exists and returns False if the given @@ -705,28 +736,36 @@ def topic_rule_exists(ruleName, salt myminion boto_iot.topic_rule_exists myrule - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) rule = conn.get_topic_rule(ruleName=ruleName) - return {'exists': True} + return {"exists": True} except ClientError as e: # Nonexistent rules show up as unauthorized exceptions. It's unclear how # to distinguish this from a real authorization exception. In practical # use, it's more useful to assume lack of existence than to assume a # genuine authorization problem; authorization problems should not be # the common case. - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'UnauthorizedException': - return {'exists': False} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "UnauthorizedException": + return {"exists": False} + return {"error": __utils__["boto3.get_error"](e)} -def create_topic_rule(ruleName, sql, actions, description, - ruleDisabled=False, - region=None, key=None, keyid=None, profile=None): - ''' +def create_topic_rule( + ruleName, + sql, + actions, + description, + ruleDisabled=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, create a topic rule. Returns {created: true} if the rule was created and returns @@ -740,26 +779,36 @@ def create_topic_rule(ruleName, sql, actions, description, '[{"lambda":{"functionArn":"arn:::::something"}},{"sns":{\\ "targetArn":"arn:::::something","roleArn":"arn:::::something"}}]' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.create_topic_rule(ruleName=ruleName, - topicRulePayload={ - 'sql': sql, - 'description': description, - 'actions': actions, - 'ruleDisabled': ruleDisabled - }) - return {'created': True} + conn.create_topic_rule( + ruleName=ruleName, + topicRulePayload={ + "sql": sql, + "description": description, + "actions": actions, + "ruleDisabled": ruleDisabled, + }, + ) + return {"created": True} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def replace_topic_rule(ruleName, sql, actions, description, - ruleDisabled=False, - region=None, key=None, keyid=None, profile=None): - ''' +def replace_topic_rule( + ruleName, + sql, + actions, + description, + ruleDisabled=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, replace a topic rule with the new values. Returns {created: true} if the rule was created and returns @@ -773,25 +822,26 @@ def replace_topic_rule(ruleName, sql, actions, description, '[{"lambda":{"functionArn":"arn:::::something"}},{"sns":{\\ "targetArn":"arn:::::something","roleArn":"arn:::::something"}}]' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.replace_topic_rule(ruleName=ruleName, - topicRulePayload={ - 'sql': sql, - 'description': description, - 'actions': actions, - 'ruleDisabled': ruleDisabled - }) - return {'replaced': True} + conn.replace_topic_rule( + ruleName=ruleName, + topicRulePayload={ + "sql": sql, + "description": description, + "actions": actions, + "ruleDisabled": ruleDisabled, + }, + ) + return {"replaced": True} except ClientError as e: - return {'replaced': False, 'error': __utils__['boto3.get_error'](e)} + return {"replaced": False, "error": __utils__["boto3.get_error"](e)} -def delete_topic_rule(ruleName, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_topic_rule(ruleName, region=None, key=None, keyid=None, profile=None): + """ Given a rule name, delete it. Returns {deleted: true} if the rule was deleted and returns @@ -803,19 +853,18 @@ def delete_topic_rule(ruleName, salt myminion boto_iot.delete_rule myrule - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_topic_rule(ruleName=ruleName) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def describe_topic_rule(ruleName, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_topic_rule(ruleName, region=None, key=None, keyid=None, profile=None): + """ Given a topic rule name describe its properties. Returns a dictionary of interesting properties. @@ -826,25 +875,25 @@ def describe_topic_rule(ruleName, salt myminion boto_iot.describe_topic_rule myrule - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) rule = conn.get_topic_rule(ruleName=ruleName) - if rule and 'rule' in rule: - rule = rule['rule'] - keys = ('ruleName', 'sql', 'description', - 'actions', 'ruleDisabled') - return {'rule': dict([(k, rule.get(k)) for k in keys])} + if rule and "rule" in rule: + rule = rule["rule"] + keys = ("ruleName", "sql", "description", "actions", "ruleDisabled") + return {"rule": dict([(k, rule.get(k)) for k in keys])} else: - return {'rule': None} + return {"rule": None} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def list_topic_rules(topic=None, ruleDisabled=None, - region=None, key=None, keyid=None, profile=None): - ''' +def list_topic_rules( + topic=None, ruleDisabled=None, region=None, key=None, keyid=None, profile=None +): + """ List all rules (for a given topic, if specified) Returns list of rules @@ -863,22 +912,24 @@ def list_topic_rules(topic=None, ruleDisabled=None, - {...} - {...} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} if topic is not None: - kwargs['topic'] = topic + kwargs["topic"] = topic if ruleDisabled is not None: - kwargs['ruleDisabled'] = ruleDisabled + kwargs["ruleDisabled"] = ruleDisabled rules = [] - for ret in __utils__['boto3.paged_call'](conn.list_topic_rules, - marker_flag='nextToken', - marker_arg='nextToken', - **kwargs): - rules.extend(ret['rules']) + for ret in __utils__["boto3.paged_call"]( + conn.list_topic_rules, + marker_flag="nextToken", + marker_arg="nextToken", + **kwargs + ): + rules.extend(ret["rules"]) if not bool(rules): - log.warning('No rules found') - return {'rules': rules} + log.warning("No rules found") + return {"rules": rules} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} diff --git a/salt/modules/boto_kinesis.py b/salt/modules/boto_kinesis.py index a78973d9fcb..44426ebd1ba 100644 --- a/salt/modules/boto_kinesis.py +++ b/salt/modules/boto_kinesis.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Kinesis .. versionadded:: 2017.7.0 @@ -41,27 +41,30 @@ Connection module for Amazon Kinesis :depends: boto3 -''' +""" # keep lint from choking on _get_conn # pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import time import random import sys +import time + +import salt.utils.versions # Import Salt libs from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -import salt.utils.versions # Import third party libs # pylint: disable=unused-import try: import boto3 import botocore - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -69,60 +72,66 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'boto_kinesis' +__virtualname__ = "boto_kinesis" def __virtual__(): - ''' + """ Only load if boto3 libraries exist. - ''' + """ has_boto_reqs = salt.utils.versions.check_boto_reqs() if has_boto_reqs is True: - __utils__['boto3.assign_funcs'](__name__, 'kinesis') + __utils__["boto3.assign_funcs"](__name__, "kinesis") return __virtualname__ return has_boto_reqs def _get_basic_stream(stream_name, conn): - ''' + """ Stream info from AWS, via describe_stream Only returns the first "page" of shards (up to 100); use _get_full_stream() for all shards. CLI example:: salt myminion boto_kinesis._get_basic_stream my_stream existing_conn - ''' + """ return _execute_with_retries(conn, "describe_stream", StreamName=stream_name) def _get_full_stream(stream_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Get complete stream info from AWS, via describe_stream, including all shards. CLI example:: salt myminion boto_kinesis._get_full_stream my_stream region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} - stream = _get_basic_stream(stream_name, conn)['result'] + stream = _get_basic_stream(stream_name, conn)["result"] full_stream = stream # iterate through if there are > 100 shards (max that AWS will return from describe_stream) while stream["StreamDescription"]["HasMoreShards"]: - stream = _execute_with_retries(conn, - "describe_stream", - StreamName=stream_name, - ExclusiveStartShardId=stream["StreamDescription"]["Shards"][-1]["ShardId"]) - stream = stream['result'] - full_stream["StreamDescription"]["Shards"] += stream["StreamDescription"]["Shards"] + stream = _execute_with_retries( + conn, + "describe_stream", + StreamName=stream_name, + ExclusiveStartShardId=stream["StreamDescription"]["Shards"][-1]["ShardId"], + ) + stream = stream["result"] + full_stream["StreamDescription"]["Shards"] += stream["StreamDescription"][ + "Shards" + ] - r['result'] = full_stream + r["result"] = full_stream return r -def get_stream_when_active(stream_name, region=None, key=None, keyid=None, profile=None): - ''' +def get_stream_when_active( + stream_name, region=None, key=None, keyid=None, profile=None +): + """ Get complete stream info from AWS, returning only when the stream is in the ACTIVE state. Continues to retry when stream is updating or creating. If the stream is deleted during retries, the loop will catch the error and break. @@ -130,7 +139,7 @@ def get_stream_when_active(stream_name, region=None, key=None, keyid=None, profi CLI example:: salt myminion boto_kinesis.get_stream_when_active my_stream region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) stream_status = None @@ -142,148 +151,159 @@ def get_stream_when_active(stream_name, region=None, key=None, keyid=None, profi time.sleep(_jittered_backoff(attempt, max_retry_delay)) attempt += 1 stream_response = _get_basic_stream(stream_name, conn) - if 'error' in stream_response: + if "error" in stream_response: return stream_response - stream_status = stream_response['result']["StreamDescription"]["StreamStatus"] + stream_status = stream_response["result"]["StreamDescription"]["StreamStatus"] # now it's active, get the full stream if necessary - if stream_response['result']["StreamDescription"]["HasMoreShards"]: + if stream_response["result"]["StreamDescription"]["HasMoreShards"]: stream_response = _get_full_stream(stream_name, region, key, keyid, profile) return stream_response def exists(stream_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check if the stream exists. Returns False and the error if it does not. CLI example:: salt myminion boto_kinesis.exists my_stream region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} stream = _get_basic_stream(stream_name, conn) - if 'error' in stream: - r['result'] = False - r['error'] = stream['error'] + if "error" in stream: + r["result"] = False + r["error"] = stream["error"] else: - r['result'] = True + r["result"] = True return r -def create_stream(stream_name, num_shards, region=None, key=None, keyid=None, profile=None): - ''' +def create_stream( + stream_name, num_shards, region=None, key=None, keyid=None, profile=None +): + """ Create a stream with name stream_name and initial number of shards num_shards. CLI example:: salt myminion boto_kinesis.create_stream my_stream N region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - r = _execute_with_retries(conn, - "create_stream", - ShardCount=num_shards, - StreamName=stream_name) - if 'error' not in r: - r['result'] = True + r = _execute_with_retries( + conn, "create_stream", ShardCount=num_shards, StreamName=stream_name + ) + if "error" not in r: + r["result"] = True return r def delete_stream(stream_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete the stream with name stream_name. This cannot be undone! All data will be lost!! CLI example:: salt myminion boto_kinesis.delete_stream my_stream region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - r = _execute_with_retries(conn, - "delete_stream", - StreamName=stream_name) - if 'error' not in r: - r['result'] = True + r = _execute_with_retries(conn, "delete_stream", StreamName=stream_name) + if "error" not in r: + r["result"] = True return r -def increase_stream_retention_period(stream_name, retention_hours, - region=None, key=None, keyid=None, profile=None): - ''' +def increase_stream_retention_period( + stream_name, retention_hours, region=None, key=None, keyid=None, profile=None +): + """ Increase stream retention period to retention_hours CLI example:: salt myminion boto_kinesis.increase_stream_retention_period my_stream N region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - r = _execute_with_retries(conn, - "increase_stream_retention_period", - StreamName=stream_name, - RetentionPeriodHours=retention_hours) - if 'error' not in r: - r['result'] = True + r = _execute_with_retries( + conn, + "increase_stream_retention_period", + StreamName=stream_name, + RetentionPeriodHours=retention_hours, + ) + if "error" not in r: + r["result"] = True return r -def decrease_stream_retention_period(stream_name, retention_hours, - region=None, key=None, keyid=None, profile=None): - ''' +def decrease_stream_retention_period( + stream_name, retention_hours, region=None, key=None, keyid=None, profile=None +): + """ Decrease stream retention period to retention_hours CLI example:: salt myminion boto_kinesis.decrease_stream_retention_period my_stream N region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - r = _execute_with_retries(conn, - "decrease_stream_retention_period", - StreamName=stream_name, - RetentionPeriodHours=retention_hours) - if 'error' not in r: - r['result'] = True + r = _execute_with_retries( + conn, + "decrease_stream_retention_period", + StreamName=stream_name, + RetentionPeriodHours=retention_hours, + ) + if "error" not in r: + r["result"] = True return r -def enable_enhanced_monitoring(stream_name, metrics, - region=None, key=None, keyid=None, profile=None): - ''' +def enable_enhanced_monitoring( + stream_name, metrics, region=None, key=None, keyid=None, profile=None +): + """ Enable enhanced monitoring for the specified shard-level metrics on stream stream_name CLI example:: salt myminion boto_kinesis.enable_enhanced_monitoring my_stream ["metrics", "to", "enable"] region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - r = _execute_with_retries(conn, - "enable_enhanced_monitoring", - StreamName=stream_name, - ShardLevelMetrics=metrics) + r = _execute_with_retries( + conn, + "enable_enhanced_monitoring", + StreamName=stream_name, + ShardLevelMetrics=metrics, + ) - if 'error' not in r: - r['result'] = True + if "error" not in r: + r["result"] = True return r -def disable_enhanced_monitoring(stream_name, metrics, - region=None, key=None, keyid=None, profile=None): - ''' +def disable_enhanced_monitoring( + stream_name, metrics, region=None, key=None, keyid=None, profile=None +): + """ Disable enhanced monitoring for the specified shard-level metrics on stream stream_name CLI example:: salt myminion boto_kinesis.disable_enhanced_monitoring my_stream ["metrics", "to", "disable"] region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - r = _execute_with_retries(conn, - "disable_enhanced_monitoring", - StreamName=stream_name, - ShardLevelMetrics=metrics) + r = _execute_with_retries( + conn, + "disable_enhanced_monitoring", + StreamName=stream_name, + ShardLevelMetrics=metrics, + ) - if 'error' not in r: - r['result'] = True + if "error" not in r: + r["result"] = True return r @@ -308,15 +328,18 @@ def get_info_for_reshard(stream_details): continue stream_details["OpenShards"].append(shard) shard["HashKeyRange"]["StartingHashKey"] = long_int( - shard["HashKeyRange"]["StartingHashKey"]) + shard["HashKeyRange"]["StartingHashKey"] + ) shard["HashKeyRange"]["EndingHashKey"] = long_int( - shard["HashKeyRange"]["EndingHashKey"]) + shard["HashKeyRange"]["EndingHashKey"] + ) if shard["HashKeyRange"]["StartingHashKey"] < min_hash_key: min_hash_key = shard["HashKeyRange"]["StartingHashKey"] if shard["HashKeyRange"]["EndingHashKey"] > max_hash_key: max_hash_key = shard["HashKeyRange"]["EndingHashKey"] - stream_details["OpenShards"].sort(key=lambda shard: long_int( - shard["HashKeyRange"]["StartingHashKey"])) + stream_details["OpenShards"].sort( + key=lambda shard: long_int(shard["HashKeyRange"]["StartingHashKey"]) + ) return min_hash_key, max_hash_key, stream_details @@ -338,8 +361,15 @@ def long_int(hash_key): return int(hash_key) -def reshard(stream_name, desired_size, force=False, - region=None, key=None, keyid=None, profile=None): +def reshard( + stream_name, + desired_size, + force=False, + region=None, + key=None, + keyid=None, + profile=None, +): """ Reshard a kinesis stream. Each call to this function will wait until the stream is ACTIVE, then make a single split or merge operation. This function decides where to split or merge @@ -357,14 +387,18 @@ def reshard(stream_name, desired_size, force=False, r = {} stream_response = get_stream_when_active(stream_name, region, key, keyid, profile) - if 'error' in stream_response: + if "error" in stream_response: return stream_response - stream_details = stream_response['result']["StreamDescription"] + stream_details = stream_response["result"]["StreamDescription"] min_hash_key, max_hash_key, stream_details = get_info_for_reshard(stream_details) - log.debug("found %s open shards, min_hash_key %s max_hash_key %s", - len(stream_details["OpenShards"]), min_hash_key, max_hash_key) + log.debug( + "found %s open shards, min_hash_key %s max_hash_key %s", + len(stream_details["OpenShards"]), + min_hash_key, + max_hash_key, + ) # find the first open shard that doesn't match the desired pattern. When we find it, # either split or merge (depending on if it's too big or too small), and then return. @@ -380,26 +414,32 @@ def reshard(stream_name, desired_size, force=False, # this weird math matches what AWS does when you create a kinesis stream # with an initial number of shards. expected_starting_hash_key = ( - max_hash_key - min_hash_key) / desired_size * shard_num + shard_num - expected_ending_hash_key = ( - max_hash_key - min_hash_key) / desired_size * (shard_num + 1) + shard_num + max_hash_key - min_hash_key + ) / desired_size * shard_num + shard_num + expected_ending_hash_key = (max_hash_key - min_hash_key) / desired_size * ( + shard_num + 1 + ) + shard_num # fix an off-by-one at the end if expected_ending_hash_key > max_hash_key: expected_ending_hash_key = max_hash_key log.debug( "Shard %s (%s) should start at %s: %s", - shard_num, shard_id, expected_starting_hash_key, - starting_hash_key == expected_starting_hash_key + shard_num, + shard_id, + expected_starting_hash_key, + starting_hash_key == expected_starting_hash_key, ) log.debug( "Shard %s (%s) should end at %s: %s", - shard_num, shard_id, expected_ending_hash_key, - ending_hash_key == expected_ending_hash_key + shard_num, + shard_id, + expected_ending_hash_key, + ending_hash_key == expected_ending_hash_key, ) if starting_hash_key != expected_starting_hash_key: - r['error'] = "starting hash keys mismatch, don't know what to do!" + r["error"] = "starting hash keys mismatch, don't know what to do!" return r if ending_hash_key == expected_ending_hash_key: @@ -408,49 +448,69 @@ def reshard(stream_name, desired_size, force=False, if ending_hash_key > expected_ending_hash_key + 1: # split at expected_ending_hash_key if force: - log.debug("%s should end at %s, actual %s, splitting", - shard_id, expected_ending_hash_key, ending_hash_key) - r = _execute_with_retries(conn, - "split_shard", - StreamName=stream_name, - ShardToSplit=shard_id, - NewStartingHashKey=str(expected_ending_hash_key + 1)) # future lint: disable=blacklisted-function + log.debug( + "%s should end at %s, actual %s, splitting", + shard_id, + expected_ending_hash_key, + ending_hash_key, + ) + r = _execute_with_retries( + conn, + "split_shard", + StreamName=stream_name, + ShardToSplit=shard_id, + NewStartingHashKey=str(expected_ending_hash_key + 1), + ) # future lint: disable=blacklisted-function else: - log.debug("%s should end at %s, actual %s would split", - shard_id, expected_ending_hash_key, ending_hash_key) + log.debug( + "%s should end at %s, actual %s would split", + shard_id, + expected_ending_hash_key, + ending_hash_key, + ) - if 'error' not in r: - r['result'] = True + if "error" not in r: + r["result"] = True return r else: # merge next_shard_id = _get_next_open_shard(stream_details, shard_id) if not next_shard_id: - r['error'] = "failed to find next shard after {0}".format(shard_id) + r["error"] = "failed to find next shard after {0}".format(shard_id) return r if force: - log.debug("%s should continue past %s, merging with %s", - shard_id, ending_hash_key, next_shard_id) - r = _execute_with_retries(conn, - "merge_shards", - StreamName=stream_name, - ShardToMerge=shard_id, - AdjacentShardToMerge=next_shard_id) + log.debug( + "%s should continue past %s, merging with %s", + shard_id, + ending_hash_key, + next_shard_id, + ) + r = _execute_with_retries( + conn, + "merge_shards", + StreamName=stream_name, + ShardToMerge=shard_id, + AdjacentShardToMerge=next_shard_id, + ) else: - log.debug("%s should continue past %s, would merge with %s", - shard_id, ending_hash_key, next_shard_id) + log.debug( + "%s should continue past %s, would merge with %s", + shard_id, + ending_hash_key, + next_shard_id, + ) - if 'error' not in r: - r['result'] = True + if "error" not in r: + r["result"] = True return r log.debug("No split or merge action necessary") - r['result'] = False + r["result"] = False return r def list_streams(region=None, key=None, keyid=None, profile=None): - ''' + """ Return a list of all streams visible to the current account CLI example: @@ -458,29 +518,35 @@ def list_streams(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_kinesis.list_streams - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) streams = [] - exclusive_start_stream_name = '' + exclusive_start_stream_name = "" while exclusive_start_stream_name is not None: - args = {'ExclusiveStartStreamName': exclusive_start_stream_name} if exclusive_start_stream_name else {} - ret = _execute_with_retries(conn, 'list_streams', **args) - if 'error' in ret: + args = ( + {"ExclusiveStartStreamName": exclusive_start_stream_name} + if exclusive_start_stream_name + else {} + ) + ret = _execute_with_retries(conn, "list_streams", **args) + if "error" in ret: return ret - ret = ret['result'] if ret and ret.get('result') else {} - streams += ret.get('StreamNames', []) - exclusive_start_stream_name = streams[-1] if ret.get('HasMoreStreams', False) in (True, 'true') else None - return {'result': streams} + ret = ret["result"] if ret and ret.get("result") else {} + streams += ret.get("StreamNames", []) + exclusive_start_stream_name = ( + streams[-1] if ret.get("HasMoreStreams", False) in (True, "true") else None + ) + return {"result": streams} def _get_next_open_shard(stream_details, shard_id): - ''' + """ Return the next open shard after shard_id CLI example:: salt myminion boto_kinesis._get_next_open_shard existing_stream_details shard_id - ''' + """ found = False for shard in stream_details["OpenShards"]: current_shard_id = shard["ShardId"] @@ -492,7 +558,7 @@ def _get_next_open_shard(stream_details, shard_id): def _execute_with_retries(conn, function, **kwargs): - ''' + """ Retry if we're rate limited by AWS or blocked by another call. Give up and return error message if resource not found or argument is invalid. @@ -514,7 +580,7 @@ def _execute_with_retries(conn, function, **kwargs): salt myminion boto_kinesis._execute_with_retries existing_conn function_name function_kwargs - ''' + """ r = {} max_attempts = 18 max_retry_delay = 10 @@ -522,33 +588,38 @@ def _execute_with_retries(conn, function, **kwargs): log.info("attempt: %s function: %s", attempt, function) try: fn = getattr(conn, function) - r['result'] = fn(**kwargs) + r["result"] = fn(**kwargs) return r except botocore.exceptions.ClientError as e: - error_code = e.response['Error']['Code'] - if "LimitExceededException" in error_code or "ResourceInUseException" in error_code: + error_code = e.response["Error"]["Code"] + if ( + "LimitExceededException" in error_code + or "ResourceInUseException" in error_code + ): # could be rate limited by AWS or another command is blocking, # retry with exponential backoff log.debug("Retrying due to AWS exception", exc_info=True) time.sleep(_jittered_backoff(attempt, max_retry_delay)) else: # ResourceNotFoundException or InvalidArgumentException - r['error'] = e.response['Error'] - log.error(r['error']) - r['result'] = None + r["error"] = e.response["Error"] + log.error(r["error"]) + r["result"] = None return r - r['error'] = "Tried to execute function {0} {1} times, but was unable".format(function, max_attempts) - log.error(r['error']) + r["error"] = "Tried to execute function {0} {1} times, but was unable".format( + function, max_attempts + ) + log.error(r["error"]) return r def _jittered_backoff(attempt, max_retry_delay): - ''' + """ Basic exponential backoff CLI example:: salt myminion boto_kinesis._jittered_backoff current_attempt_number max_delay_in_seconds - ''' + """ return min(random.random() * (2 ** attempt), max_retry_delay) diff --git a/salt/modules/boto_kms.py b/salt/modules/boto_kms.py index a5996d7f79f..db58cfbe867 100644 --- a/salt/modules/boto_kms.py +++ b/salt/modules/boto_kms.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon KMS .. versionadded:: 2015.8.0 @@ -32,7 +32,7 @@ Connection module for Amazon KMS region: us-east-1 :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 @@ -41,10 +41,11 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging +import salt.serializers.json + # Import Salt libs import salt.utils.compat import salt.utils.odict as odict -import salt.serializers.json import salt.utils.versions log = logging.getLogger(__name__) @@ -54,114 +55,134 @@ try: # pylint: disable=unused-import import boto import boto.kms + # pylint: enable=unused-import - logging.getLogger('boto').setLevel(logging.CRITICAL) + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except (ImportError, AttributeError): HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' - return salt.utils.versions.check_boto_reqs( - boto_ver='2.38.0', - check_boto3=False - ) + """ + return salt.utils.versions.check_boto_reqs(boto_ver="2.38.0", check_boto3=False) def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto.assign_funcs'](__name__, 'kms', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "kms", pack=__salt__) -def create_alias(alias_name, target_key_id, region=None, key=None, keyid=None, - profile=None): - ''' +def create_alias( + alias_name, target_key_id, region=None, key=None, keyid=None, profile=None +): + """ Create a display name for a key. CLI example:: salt myminion boto_kms.create_alias 'alias/mykey' key_id - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: conn.create_alias(alias_name, target_key_id) - r['result'] = True + r["result"] = True except boto.exception.BotoServerError as e: - r['result'] = False - r['error'] = __utils__['boto.get_error'](e) + r["result"] = False + r["error"] = __utils__["boto.get_error"](e) return r -def create_grant(key_id, grantee_principal, retiring_principal=None, - operations=None, constraints=None, grant_tokens=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_grant( + key_id, + grantee_principal, + retiring_principal=None, + operations=None, + constraints=None, + grant_tokens=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Adds a grant to a key to specify who can access the key and under what conditions. CLI example:: salt myminion boto_kms.create_grant 'alias/mykey' 'arn:aws:iam::1111111:/role/myrole' operations='["Encrypt","Decrypt"]' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if key_id.startswith('alias/'): + if key_id.startswith("alias/"): key_id = _get_key_id(key_id) r = {} try: - r['grant'] = conn.create_grant( + r["grant"] = conn.create_grant( key_id, grantee_principal, retiring_principal=retiring_principal, operations=operations, constraints=constraints, - grant_tokens=grant_tokens + grant_tokens=grant_tokens, ) except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def create_key(policy=None, description=None, key_usage=None, region=None, - key=None, keyid=None, profile=None): - ''' +def create_key( + policy=None, + description=None, + key_usage=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates a master key. CLI example:: salt myminion boto_kms.create_key '{"Statement":...}' "My master key" - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} _policy = salt.serializers.json.serialize(policy) try: key_metadata = conn.create_key( - _policy, - description=description, - key_usage=key_usage + _policy, description=description, key_usage=key_usage ) - r['key_metadata'] = key_metadata['KeyMetadata'] + r["key_metadata"] = key_metadata["KeyMetadata"] except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def decrypt(ciphertext_blob, encryption_context=None, grant_tokens=None, - region=None, key=None, keyid=None, profile=None): - ''' +def decrypt( + ciphertext_blob, + encryption_context=None, + grant_tokens=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Decrypt ciphertext. CLI example:: salt myminion boto_kms.decrypt encrypted_ciphertext - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} @@ -169,158 +190,162 @@ def decrypt(ciphertext_blob, encryption_context=None, grant_tokens=None, plaintext = conn.decrypt( ciphertext_blob, encryption_context=encryption_context, - grant_tokens=grant_tokens + grant_tokens=grant_tokens, ) - r['plaintext'] = plaintext['Plaintext'] + r["plaintext"] = plaintext["Plaintext"] except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r def key_exists(key_id, region=None, key=None, keyid=None, profile=None): - ''' + """ Check for the existence of a key. CLI example:: salt myminion boto_kms.key_exists 'alias/mykey' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: key = conn.describe_key(key_id) # TODO: add to context cache - r['result'] = True + r["result"] = True except boto.exception.BotoServerError as e: if isinstance(e, boto.kms.exceptions.NotFoundException): - r['result'] = False + r["result"] = False return r - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r def _get_key_id(alias, region=None, key=None, keyid=None, profile=None): - ''' + """ From an alias, get a key_id. - ''' - key_metadata = describe_key( - alias, region, key, keyid, profile - )['key_metadata'] - return key_metadata['KeyId'] + """ + key_metadata = describe_key(alias, region, key, keyid, profile)["key_metadata"] + return key_metadata["KeyId"] def describe_key(key_id, region=None, key=None, keyid=None, profile=None): - ''' + """ Get detailed information about a key. CLI example:: salt myminion boto_kms.describe_key 'alias/mykey' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: key = conn.describe_key(key_id) # TODO: add to context cache - r['key_metadata'] = key['KeyMetadata'] + r["key_metadata"] = key["KeyMetadata"] except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r def disable_key(key_id, region=None, key=None, keyid=None, profile=None): - ''' + """ Mark key as disabled. CLI example:: salt myminion boto_kms.disable_key 'alias/mykey' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: key = conn.disable_key(key_id) - r['result'] = True + r["result"] = True except boto.exception.BotoServerError as e: - r['result'] = False - r['error'] = __utils__['boto.get_error'](e) + r["result"] = False + r["error"] = __utils__["boto.get_error"](e) return r -def disable_key_rotation(key_id, region=None, key=None, keyid=None, - profile=None): - ''' +def disable_key_rotation(key_id, region=None, key=None, keyid=None, profile=None): + """ Disable key rotation for specified key. CLI example:: salt myminion boto_kms.disable_key_rotation 'alias/mykey' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: key = conn.disable_key_rotation(key_id) - r['result'] = True + r["result"] = True except boto.exception.BotoServerError as e: - r['result'] = False - r['error'] = __utils__['boto.get_error'](e) + r["result"] = False + r["error"] = __utils__["boto.get_error"](e) return r def enable_key(key_id, region=None, key=None, keyid=None, profile=None): - ''' + """ Mark key as enabled. CLI example:: salt myminion boto_kms.enable_key 'alias/mykey' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: key = conn.enable_key(key_id) - r['result'] = True + r["result"] = True except boto.exception.BotoServerError as e: - r['result'] = False - r['error'] = __utils__['boto.get_error'](e) + r["result"] = False + r["error"] = __utils__["boto.get_error"](e) return r -def enable_key_rotation(key_id, region=None, key=None, keyid=None, - profile=None): - ''' +def enable_key_rotation(key_id, region=None, key=None, keyid=None, profile=None): + """ Disable key rotation for specified key. CLI example:: salt myminion boto_kms.enable_key_rotation 'alias/mykey' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: key = conn.enable_key_rotation(key_id) - r['result'] = True + r["result"] = True except boto.exception.BotoServerError as e: - r['result'] = False - r['error'] = __utils__['boto.get_error'](e) + r["result"] = False + r["error"] = __utils__["boto.get_error"](e) return r -def encrypt(key_id, plaintext, encryption_context=None, grant_tokens=None, - region=None, key=None, keyid=None, profile=None): - ''' +def encrypt( + key_id, + plaintext, + encryption_context=None, + grant_tokens=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Encrypt plaintext into cipher text using specified key. CLI example:: salt myminion boto_kms.encrypt 'alias/mykey' 'myplaindata' '{"aws:username":"myuser"}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} @@ -329,24 +354,32 @@ def encrypt(key_id, plaintext, encryption_context=None, grant_tokens=None, key_id, plaintext, encryption_context=encryption_context, - grant_tokens=grant_tokens + grant_tokens=grant_tokens, ) - r['ciphertext'] = ciphertext['CiphertextBlob'] + r["ciphertext"] = ciphertext["CiphertextBlob"] except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def generate_data_key(key_id, encryption_context=None, number_of_bytes=None, - key_spec=None, grant_tokens=None, region=None, key=None, - keyid=None, profile=None): - ''' +def generate_data_key( + key_id, + encryption_context=None, + number_of_bytes=None, + key_spec=None, + grant_tokens=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Generate a secure data key. CLI example:: salt myminion boto_kms.generate_data_key 'alias/mykey' number_of_bytes=1024 key_spec=AES_128 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} @@ -356,25 +389,32 @@ def generate_data_key(key_id, encryption_context=None, number_of_bytes=None, encryption_context=encryption_context, number_of_bytes=number_of_bytes, key_spec=key_spec, - grant_tokens=grant_tokens + grant_tokens=grant_tokens, ) - r['data_key'] = data_key + r["data_key"] = data_key except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r def generate_data_key_without_plaintext( - key_id, encryption_context=None, number_of_bytes=None, key_spec=None, - grant_tokens=None, region=None, key=None, keyid=None, profile=None - ): - ''' + key_id, + encryption_context=None, + number_of_bytes=None, + key_spec=None, + grant_tokens=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Generate a secure data key without a plaintext copy of the key. CLI example:: salt myminion boto_kms.generate_data_key_without_plaintext 'alias/mykey' number_of_bytes=1024 key_spec=AES_128 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} @@ -384,229 +424,231 @@ def generate_data_key_without_plaintext( encryption_context=encryption_context, number_of_bytes=number_of_bytes, key_spec=key_spec, - grant_tokens=grant_tokens + grant_tokens=grant_tokens, ) - r['data_key'] = data_key + r["data_key"] = data_key except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def generate_random(number_of_bytes=None, region=None, key=None, keyid=None, - profile=None): - ''' +def generate_random( + number_of_bytes=None, region=None, key=None, keyid=None, profile=None +): + """ Generate a random string. CLI example:: salt myminion boto_kms.generate_random number_of_bytes=1024 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: random = conn.generate_random(number_of_bytes) - r['random'] = random['Plaintext'] + r["random"] = random["Plaintext"] except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def get_key_policy(key_id, policy_name, region=None, key=None, keyid=None, - profile=None): - ''' +def get_key_policy( + key_id, policy_name, region=None, key=None, keyid=None, profile=None +): + """ Get the policy for the specified key. CLI example:: salt myminion boto_kms.get_key_policy 'alias/mykey' mypolicy - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: key_policy = conn.get_key_policy(key_id, policy_name) - r['key_policy'] = salt.serializers.json.deserialize( - key_policy['Policy'], - object_pairs_hook=odict.OrderedDict + r["key_policy"] = salt.serializers.json.deserialize( + key_policy["Policy"], object_pairs_hook=odict.OrderedDict ) except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def get_key_rotation_status(key_id, region=None, key=None, keyid=None, - profile=None): - ''' +def get_key_rotation_status(key_id, region=None, key=None, keyid=None, profile=None): + """ Get status of whether or not key rotation is enabled for a key. CLI example:: salt myminion boto_kms.get_key_rotation_status 'alias/mykey' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: key_rotation_status = conn.get_key_rotation_status(key_id) - r['result'] = key_rotation_status['KeyRotationEnabled'] + r["result"] = key_rotation_status["KeyRotationEnabled"] except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def list_grants(key_id, limit=None, marker=None, region=None, key=None, - keyid=None, profile=None): - ''' +def list_grants( + key_id, limit=None, marker=None, region=None, key=None, keyid=None, profile=None +): + """ List grants for the specified key. CLI example:: salt myminion boto_kms.list_grants 'alias/mykey' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if key_id.startswith('alias/'): + if key_id.startswith("alias/"): key_id = _get_key_id(key_id) r = {} try: _grants = [] next_marker = None while True: - grants = conn.list_grants( - key_id, - limit=limit, - marker=next_marker - ) - for grant in grants['Grants']: + grants = conn.list_grants(key_id, limit=limit, marker=next_marker) + for grant in grants["Grants"]: _grants.append(grant) - if 'NextMarker' in grants: - next_marker = grants['NextMarker'] + if "NextMarker" in grants: + next_marker = grants["NextMarker"] else: break - r['grants'] = _grants + r["grants"] = _grants except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def list_key_policies(key_id, limit=None, marker=None, region=None, key=None, - keyid=None, profile=None): - ''' +def list_key_policies( + key_id, limit=None, marker=None, region=None, key=None, keyid=None, profile=None +): + """ List key_policies for the specified key. CLI example:: salt myminion boto_kms.list_key_policies 'alias/mykey' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if key_id.startswith('alias/'): + if key_id.startswith("alias/"): key_id = _get_key_id(key_id) r = {} try: - key_policies = conn.list_key_policies( - key_id, - limit=limit, - marker=marker - ) + key_policies = conn.list_key_policies(key_id, limit=limit, marker=marker) # TODO: handle limit, marker and truncation automatically. - r['key_policies'] = key_policies['PolicyNames'] + r["key_policies"] = key_policies["PolicyNames"] except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def put_key_policy(key_id, policy_name, policy, region=None, key=None, - keyid=None, profile=None): - ''' +def put_key_policy( + key_id, policy_name, policy, region=None, key=None, keyid=None, profile=None +): + """ Attach a key policy to the specified key. CLI example:: salt myminion boto_kms.put_key_policy 'alias/mykey' default '{"Statement":...}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: - conn.put_key_policy(key_id, policy_name, salt.serializers.json.serialize(policy)) - r['result'] = True + conn.put_key_policy( + key_id, policy_name, salt.serializers.json.serialize(policy) + ) + r["result"] = True except boto.exception.BotoServerError as e: - r['result'] = False - r['error'] = __utils__['boto.get_error'](e) + r["result"] = False + r["error"] = __utils__["boto.get_error"](e) return r -def re_encrypt(ciphertext_blob, destination_key_id, - source_encryption_context=None, - destination_encryption_context=None, grant_tokens=None, - region=None, key=None, keyid=None, profile=None): - ''' +def re_encrypt( + ciphertext_blob, + destination_key_id, + source_encryption_context=None, + destination_encryption_context=None, + grant_tokens=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Reencrypt encrypted data with a new master key. CLI example:: salt myminion boto_kms.re_encrypt 'encrypted_data' 'alias/mynewkey' default '{"Statement":...}' - ''' - conn = _get_conn( - region=region, - key=key, - keyid=keyid, - profile=profile - ) + """ + conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: ciphertext = conn.re_encrypt( - ciphertext_blob, destination_key_id, source_encryption_context, - destination_encryption_context, grant_tokens + ciphertext_blob, + destination_key_id, + source_encryption_context, + destination_encryption_context, + grant_tokens, ) - r['ciphertext'] = ciphertext + r["ciphertext"] = ciphertext except boto.exception.BotoServerError as e: - r['error'] = __utils__['boto.get_error'](e) + r["error"] = __utils__["boto.get_error"](e) return r -def revoke_grant(key_id, grant_id, region=None, key=None, keyid=None, - profile=None): - ''' +def revoke_grant(key_id, grant_id, region=None, key=None, keyid=None, profile=None): + """ Revoke a grant from a key. CLI example:: salt myminion boto_kms.revoke_grant 'alias/mykey' 8u89hf-j09j... - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if key_id.startswith('alias/'): + if key_id.startswith("alias/"): key_id = _get_key_id(key_id) r = {} try: conn.revoke_grant(key_id, grant_id) - r['result'] = True + r["result"] = True except boto.exception.BotoServerError as e: - r['result'] = False - r['error'] = __utils__['boto.get_error'](e) + r["result"] = False + r["error"] = __utils__["boto.get_error"](e) return r -def update_key_description(key_id, description, region=None, key=None, - keyid=None, profile=None): - ''' +def update_key_description( + key_id, description, region=None, key=None, keyid=None, profile=None +): + """ Update a key's description. CLI example:: salt myminion boto_kms.update_key_description 'alias/mykey' 'My key' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = {} try: conn.update_key_description(key_id, description) - r['result'] = True + r["result"] = True except boto.exception.BotoServerError as e: - r['result'] = False - r['error'] = __utils__['boto.get_error'](e) + r["result"] = False + r["error"] = __utils__["boto.get_error"](e) return r diff --git a/salt/modules/boto_lambda.py b/salt/modules/boto_lambda.py index f28ec96ae84..96e601141ea 100644 --- a/salt/modules/boto_lambda.py +++ b/salt/modules/boto_lambda.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Lambda .. versionadded:: 2016.3.0 @@ -74,23 +74,25 @@ as a passed in dict, or as a string to pull from pillars or minion config: error: message: error message -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -import time -import random -# Import Salt libs -from salt.ext import six +import logging +import random +import time + import salt.utils.compat import salt.utils.files import salt.utils.json import salt.utils.versions from salt.exceptions import SaltInvocationError + +# Import Salt libs +from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error log = logging.getLogger(__name__) @@ -102,11 +104,13 @@ try: # pylint: disable=unused-import import boto import boto3 + # pylint: enable=unused-import from botocore.exceptions import ClientError from botocore import __version__ as found_botocore_version - logging.getLogger('boto').setLevel(logging.CRITICAL) - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -114,44 +118,40 @@ except ImportError: def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # the boto_lambda execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 # botocore version >= 1.5.2 is required due to lambda environment variables return salt.utils.versions.check_boto_reqs( - boto_ver='2.8.0', - boto3_ver='1.2.5', - botocore_ver='1.5.2' + boto_ver="2.8.0", boto3_ver="1.2.5", botocore_ver="1.5.2" ) def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 'lambda') + __utils__["boto3.assign_funcs"](__name__, "lambda") -def _find_function(name, - region=None, key=None, keyid=None, profile=None): - ''' +def _find_function(name, region=None, key=None, keyid=None, profile=None): + """ Given function name, find and return matching Lambda information. - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - for funcs in __utils__['boto3.paged_call'](conn.list_functions): - for func in funcs['Functions']: - if func['FunctionName'] == name: + for funcs in __utils__["boto3.paged_call"](conn.list_functions): + for func in funcs["Functions"]: + if func["FunctionName"] == name: return func return None -def function_exists(FunctionName, region=None, key=None, - keyid=None, profile=None): - ''' +def function_exists(FunctionName, region=None, key=None, keyid=None, profile=None): + """ Given a function name, check to see if the given function name exists. Returns True if the given function exists and returns False if the given @@ -163,32 +163,33 @@ def function_exists(FunctionName, region=None, key=None, salt myminion boto_lambda.function_exists myfunction - ''' + """ try: - func = _find_function(FunctionName, - region=region, key=key, keyid=keyid, profile=profile) - return {'exists': bool(func)} + func = _find_function( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + ) + return {"exists": bool(func)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def _get_role_arn(name, region=None, key=None, keyid=None, profile=None): - if name.startswith('arn:aws:iam:'): + if name.startswith("arn:aws:iam:"): return name - account_id = __salt__['boto_iam.get_account_id']( + account_id = __salt__["boto_iam.get_account_id"]( region=region, key=key, keyid=keyid, profile=profile ) - if profile and 'region' in profile: - region = profile['region'] + if profile and "region" in profile: + region = profile["region"] if region is None: - region = 'us-east-1' - return 'arn:aws:iam::{0}:role/{1}'.format(account_id, name) + region = "us-east-1" + return "arn:aws:iam::{0}:role/{1}".format(account_id, name) def _filedata(infile): - with salt.utils.files.fopen(infile, 'rb') as f: + with salt.utils.files.fopen(infile, "rb") as f: return f.read() @@ -198,23 +199,47 @@ def _resolve_vpcconfig(conf, region=None, key=None, keyid=None, profile=None): if not conf: return None if not isinstance(conf, dict): - raise SaltInvocationError('VpcConfig must be a dict.') - sns = [__salt__['boto_vpc.get_resource_id']('subnet', s, region=region, key=key, - keyid=keyid, profile=profile).get('id') for s in conf.pop('SubnetNames', [])] - sgs = [__salt__['boto_secgroup.get_group_id'](s, region=region, key=key, keyid=keyid, - profile=profile) for s in conf.pop('SecurityGroupNames', [])] - conf.setdefault('SubnetIds', []).extend(sns) - conf.setdefault('SecurityGroupIds', []).extend(sgs) + raise SaltInvocationError("VpcConfig must be a dict.") + sns = [ + __salt__["boto_vpc.get_resource_id"]( + "subnet", s, region=region, key=key, keyid=keyid, profile=profile + ).get("id") + for s in conf.pop("SubnetNames", []) + ] + sgs = [ + __salt__["boto_secgroup.get_group_id"]( + s, region=region, key=key, keyid=keyid, profile=profile + ) + for s in conf.pop("SecurityGroupNames", []) + ] + conf.setdefault("SubnetIds", []).extend(sns) + conf.setdefault("SecurityGroupIds", []).extend(sgs) return conf -def create_function(FunctionName, Runtime, Role, Handler, ZipFile=None, - S3Bucket=None, S3Key=None, S3ObjectVersion=None, - Description="", Timeout=3, MemorySize=128, Publish=False, - WaitForRole=False, RoleRetries=5, - region=None, key=None, keyid=None, profile=None, - VpcConfig=None, Environment=None): - ''' +def create_function( + FunctionName, + Runtime, + Role, + Handler, + ZipFile=None, + S3Bucket=None, + S3Key=None, + S3ObjectVersion=None, + Description="", + Timeout=3, + MemorySize=128, + Publish=False, + WaitForRole=False, + RoleRetries=5, + region=None, + key=None, + keyid=None, + profile=None, + VpcConfig=None, + Environment=None, +): + """ .. versionadded:: 2017.7.0 Given a valid config, create a function. @@ -240,68 +265,90 @@ def create_function(FunctionName, Runtime, Role, Handler, ZipFile=None, salt myminion boto_lamba.create_function my_function python2.7 my_role my_file.my_function my_function.zip - ''' + """ - role_arn = _get_role_arn(Role, region=region, key=key, - keyid=keyid, profile=profile) + role_arn = _get_role_arn(Role, region=region, key=key, keyid=keyid, profile=profile) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if ZipFile: if S3Bucket or S3Key or S3ObjectVersion: - raise SaltInvocationError('Either ZipFile must be specified, or ' - 'S3Bucket and S3Key must be provided.') + raise SaltInvocationError( + "Either ZipFile must be specified, or " + "S3Bucket and S3Key must be provided." + ) code = { - 'ZipFile': _filedata(ZipFile), + "ZipFile": _filedata(ZipFile), } else: if not S3Bucket or not S3Key: - raise SaltInvocationError('Either ZipFile must be specified, or ' - 'S3Bucket and S3Key must be provided.') + raise SaltInvocationError( + "Either ZipFile must be specified, or " + "S3Bucket and S3Key must be provided." + ) code = { - 'S3Bucket': S3Bucket, - 'S3Key': S3Key, + "S3Bucket": S3Bucket, + "S3Key": S3Key, } if S3ObjectVersion: - code['S3ObjectVersion'] = S3ObjectVersion + code["S3ObjectVersion"] = S3ObjectVersion kwargs = {} if VpcConfig is not None: - kwargs['VpcConfig'] = _resolve_vpcconfig(VpcConfig, region=region, key=key, keyid=keyid, profile=profile) + kwargs["VpcConfig"] = _resolve_vpcconfig( + VpcConfig, region=region, key=key, keyid=keyid, profile=profile + ) if Environment is not None: - kwargs['Environment'] = Environment + kwargs["Environment"] = Environment if WaitForRole: retrycount = RoleRetries else: retrycount = 1 for retry in range(retrycount, 0, -1): try: - func = conn.create_function(FunctionName=FunctionName, Runtime=Runtime, Role=role_arn, Handler=Handler, - Code=code, Description=Description, Timeout=Timeout, MemorySize=MemorySize, - Publish=Publish, **kwargs) + func = conn.create_function( + FunctionName=FunctionName, + Runtime=Runtime, + Role=role_arn, + Handler=Handler, + Code=code, + Description=Description, + Timeout=Timeout, + MemorySize=MemorySize, + Publish=Publish, + **kwargs + ) except ClientError as e: - if retry > 1 and e.response.get('Error', {}).get('Code') == 'InvalidParameterValueException': + if ( + retry > 1 + and e.response.get("Error", {}).get("Code") + == "InvalidParameterValueException" + ): log.info( - 'Function not created but IAM role may not have propagated, will retry') + "Function not created but IAM role may not have propagated, will retry" + ) # exponential backoff - time.sleep((2 ** (RoleRetries - retry)) + - (random.randint(0, 1000) / 1000)) + time.sleep( + (2 ** (RoleRetries - retry)) + (random.randint(0, 1000) / 1000) + ) continue else: raise else: break if func: - log.info('The newly created function name is %s', func['FunctionName']) + log.info("The newly created function name is %s", func["FunctionName"]) - return {'created': True, 'name': func['FunctionName']} + return {"created": True, "name": func["FunctionName"]} else: - log.warning('Function was not created') - return {'created': False} + log.warning("Function was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete_function(FunctionName, Qualifier=None, region=None, key=None, keyid=None, profile=None): - ''' +def delete_function( + FunctionName, Qualifier=None, region=None, key=None, keyid=None, profile=None +): + """ Given a function name and optional version qualifier, delete it. Returns {deleted: true} if the function was deleted and returns @@ -313,23 +360,21 @@ def delete_function(FunctionName, Qualifier=None, region=None, key=None, keyid=N salt myminion boto_lambda.delete_function myfunction - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if Qualifier: - conn.delete_function( - FunctionName=FunctionName, Qualifier=Qualifier) + conn.delete_function(FunctionName=FunctionName, Qualifier=Qualifier) else: conn.delete_function(FunctionName=FunctionName) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def describe_function(FunctionName, region=None, key=None, - keyid=None, profile=None): - ''' +def describe_function(FunctionName, region=None, key=None, keyid=None, profile=None): + """ Given a function name describe its properties. Returns a dictionary of interesting properties. @@ -340,28 +385,52 @@ def describe_function(FunctionName, region=None, key=None, salt myminion boto_lambda.describe_function myfunction - ''' + """ try: - func = _find_function(FunctionName, - region=region, key=key, keyid=keyid, profile=profile) + func = _find_function( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + ) if func: - keys = ('FunctionName', 'Runtime', 'Role', 'Handler', 'CodeSha256', - 'CodeSize', 'Description', 'Timeout', 'MemorySize', - 'FunctionArn', 'LastModified', 'VpcConfig', 'Environment') - return {'function': dict([(k, func.get(k)) for k in keys])} + keys = ( + "FunctionName", + "Runtime", + "Role", + "Handler", + "CodeSha256", + "CodeSize", + "Description", + "Timeout", + "MemorySize", + "FunctionArn", + "LastModified", + "VpcConfig", + "Environment", + ) + return {"function": dict([(k, func.get(k)) for k in keys])} else: - return {'function': None} + return {"function": None} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def update_function_config(FunctionName, Role=None, Handler=None, - Description=None, Timeout=None, MemorySize=None, - region=None, key=None, keyid=None, profile=None, - VpcConfig=None, WaitForRole=False, RoleRetries=5, - Environment=None): - ''' +def update_function_config( + FunctionName, + Role=None, + Handler=None, + Description=None, + Timeout=None, + MemorySize=None, + region=None, + key=None, + keyid=None, + profile=None, + VpcConfig=None, + WaitForRole=False, + RoleRetries=5, + Environment=None, +): + """ .. versionadded:: 2017.7.0 Update the named lambda function to the configuration. @@ -387,24 +456,28 @@ def update_function_config(FunctionName, Role=None, Handler=None, salt myminion boto_lamba.update_function_config my_function my_role my_file.my_function "my lambda function" - ''' + """ args = dict(FunctionName=FunctionName) - options = {'Handler': Handler, - 'Description': Description, - 'Timeout': Timeout, - 'MemorySize': MemorySize, - 'VpcConfig': VpcConfig, - 'Environment': Environment} + options = { + "Handler": Handler, + "Description": Description, + "Timeout": Timeout, + "MemorySize": MemorySize, + "VpcConfig": VpcConfig, + "Environment": Environment, + } conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) for val, var in six.iteritems(options): if var: args[val] = var if Role: - args['Role'] = _get_role_arn(Role, region, key, keyid, profile) + args["Role"] = _get_role_arn(Role, region, key, keyid, profile) if VpcConfig: - args['VpcConfig'] = _resolve_vpcconfig(VpcConfig, region=region, key=key, keyid=keyid, profile=profile) + args["VpcConfig"] = _resolve_vpcconfig( + VpcConfig, region=region, key=key, keyid=keyid, profile=profile + ) try: if WaitForRole: retrycount = RoleRetries @@ -414,33 +487,60 @@ def update_function_config(FunctionName, Role=None, Handler=None, try: r = conn.update_function_configuration(**args) except ClientError as e: - if retry > 1 and e.response.get('Error', {}).get('Code') == 'InvalidParameterValueException': + if ( + retry > 1 + and e.response.get("Error", {}).get("Code") + == "InvalidParameterValueException" + ): log.info( - 'Function not updated but IAM role may not have propagated, will retry') + "Function not updated but IAM role may not have propagated, will retry" + ) # exponential backoff - time.sleep((2 ** (RoleRetries - retry)) + - (random.randint(0, 1000) / 1000)) + time.sleep( + (2 ** (RoleRetries - retry)) + (random.randint(0, 1000) / 1000) + ) continue else: raise else: break if r: - keys = ('FunctionName', 'Runtime', 'Role', 'Handler', 'CodeSha256', - 'CodeSize', 'Description', 'Timeout', 'MemorySize', - 'FunctionArn', 'LastModified', 'VpcConfig', 'Environment') - return {'updated': True, 'function': dict([(k, r.get(k)) for k in keys])} + keys = ( + "FunctionName", + "Runtime", + "Role", + "Handler", + "CodeSha256", + "CodeSize", + "Description", + "Timeout", + "MemorySize", + "FunctionArn", + "LastModified", + "VpcConfig", + "Environment", + ) + return {"updated": True, "function": dict([(k, r.get(k)) for k in keys])} else: - log.warning('Function was not updated') - return {'updated': False} + log.warning("Function was not updated") + return {"updated": False} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def update_function_code(FunctionName, ZipFile=None, S3Bucket=None, S3Key=None, - S3ObjectVersion=None, Publish=False, - region=None, key=None, keyid=None, profile=None): - ''' +def update_function_code( + FunctionName, + ZipFile=None, + S3Bucket=None, + S3Key=None, + S3ObjectVersion=None, + Publish=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Upload the given code to the named lambda function. Returns {updated: true} if the function was updated and returns @@ -452,45 +552,72 @@ def update_function_code(FunctionName, ZipFile=None, S3Bucket=None, S3Key=None, salt myminion boto_lamba.update_function_code my_function ZipFile=function.zip - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: if ZipFile: if S3Bucket or S3Key or S3ObjectVersion: - raise SaltInvocationError('Either ZipFile must be specified, or ' - 'S3Bucket and S3Key must be provided.') - r = conn.update_function_code(FunctionName=FunctionName, - ZipFile=_filedata(ZipFile), - Publish=Publish) + raise SaltInvocationError( + "Either ZipFile must be specified, or " + "S3Bucket and S3Key must be provided." + ) + r = conn.update_function_code( + FunctionName=FunctionName, ZipFile=_filedata(ZipFile), Publish=Publish + ) else: if not S3Bucket or not S3Key: - raise SaltInvocationError('Either ZipFile must be specified, or ' - 'S3Bucket and S3Key must be provided.') + raise SaltInvocationError( + "Either ZipFile must be specified, or " + "S3Bucket and S3Key must be provided." + ) args = { - 'S3Bucket': S3Bucket, - 'S3Key': S3Key, + "S3Bucket": S3Bucket, + "S3Key": S3Key, } if S3ObjectVersion: - args['S3ObjectVersion'] = S3ObjectVersion - r = conn.update_function_code(FunctionName=FunctionName, - Publish=Publish, **args) + args["S3ObjectVersion"] = S3ObjectVersion + r = conn.update_function_code( + FunctionName=FunctionName, Publish=Publish, **args + ) if r: - keys = ('FunctionName', 'Runtime', 'Role', 'Handler', 'CodeSha256', - 'CodeSize', 'Description', 'Timeout', 'MemorySize', - 'FunctionArn', 'LastModified', 'VpcConfig', 'Environment') - return {'updated': True, 'function': dict([(k, r.get(k)) for k in keys])} + keys = ( + "FunctionName", + "Runtime", + "Role", + "Handler", + "CodeSha256", + "CodeSize", + "Description", + "Timeout", + "MemorySize", + "FunctionArn", + "LastModified", + "VpcConfig", + "Environment", + ) + return {"updated": True, "function": dict([(k, r.get(k)) for k in keys])} else: - log.warning('Function was not updated') - return {'updated': False} + log.warning("Function was not updated") + return {"updated": False} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def add_permission(FunctionName, StatementId, Action, Principal, SourceArn=None, - SourceAccount=None, Qualifier=None, - region=None, key=None, keyid=None, profile=None): - ''' +def add_permission( + FunctionName, + StatementId, + Action, + Principal, + SourceArn=None, + SourceAccount=None, + Qualifier=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Add a permission to a lambda function. Returns {added: true} if the permission was added and returns @@ -504,25 +631,38 @@ def add_permission(FunctionName, StatementId, Action, Principal, SourceArn=None, s3.amazonaws.com aws:arn::::bucket-name \\ aws-account-id - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} - for key in ('SourceArn', 'SourceAccount', 'Qualifier'): + for key in ("SourceArn", "SourceAccount", "Qualifier"): if locals()[key] is not None: - kwargs[key] = str(locals()[key]) # future lint: disable=blacklisted-function - conn.add_permission(FunctionName=FunctionName, StatementId=StatementId, - Action=Action, Principal=str(Principal), # future lint: disable=blacklisted-function - **kwargs) - return {'updated': True} + kwargs[key] = str( + locals()[key] + ) # future lint: disable=blacklisted-function + conn.add_permission( + FunctionName=FunctionName, + StatementId=StatementId, + Action=Action, + Principal=str(Principal), # future lint: disable=blacklisted-function + **kwargs + ) + return {"updated": True} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def remove_permission(FunctionName, StatementId, Qualifier=None, - region=None, key=None, keyid=None, profile=None): - ''' +def remove_permission( + FunctionName, + StatementId, + Qualifier=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Remove a permission from a lambda function. Returns {removed: true} if the permission was removed and returns @@ -534,23 +674,25 @@ def remove_permission(FunctionName, StatementId, Qualifier=None, salt myminion boto_lamba.remove_permission my_function my_id - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} if Qualifier is not None: - kwargs['Qualifier'] = Qualifier - conn.remove_permission(FunctionName=FunctionName, StatementId=StatementId, - **kwargs) - return {'updated': True} + kwargs["Qualifier"] = Qualifier + conn.remove_permission( + FunctionName=FunctionName, StatementId=StatementId, **kwargs + ) + return {"updated": True} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def get_permissions(FunctionName, Qualifier=None, - region=None, key=None, keyid=None, profile=None): - ''' +def get_permissions( + FunctionName, Qualifier=None, region=None, key=None, keyid=None, profile=None +): + """ Get resource permissions for the given lambda function Returns dictionary of permissions, by statement ID @@ -562,51 +704,50 @@ def get_permissions(FunctionName, Qualifier=None, salt myminion boto_lamba.get_permissions my_function permissions: {...} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} if Qualifier is not None: - kwargs['Qualifier'] = Qualifier + kwargs["Qualifier"] = Qualifier # The get_policy call is not symmetric with add/remove_permissions. So # massage it until it is, for better ease of use. - policy = conn.get_policy(FunctionName=FunctionName, - **kwargs) - policy = policy.get('Policy', {}) + policy = conn.get_policy(FunctionName=FunctionName, **kwargs) + policy = policy.get("Policy", {}) if isinstance(policy, six.string_types): policy = salt.utils.json.loads(policy) if policy is None: policy = {} permissions = {} - for statement in policy.get('Statement', []): - condition = statement.get('Condition', {}) - principal = statement.get('Principal', {}) - if 'AWS' in principal: - principal = principal['AWS'].split(':')[4] + for statement in policy.get("Statement", []): + condition = statement.get("Condition", {}) + principal = statement.get("Principal", {}) + if "AWS" in principal: + principal = principal["AWS"].split(":")[4] else: - principal = principal.get('Service') + principal = principal.get("Service") permission = { - 'Action': statement.get('Action'), - 'Principal': principal, + "Action": statement.get("Action"), + "Principal": principal, } - if 'ArnLike' in condition: - permission['SourceArn'] = condition[ - 'ArnLike'].get('AWS:SourceArn') - if 'StringEquals' in condition: - permission['SourceAccount'] = condition[ - 'StringEquals'].get('AWS:SourceAccount') - permissions[statement.get('Sid')] = permission - return {'permissions': permissions} + if "ArnLike" in condition: + permission["SourceArn"] = condition["ArnLike"].get("AWS:SourceArn") + if "StringEquals" in condition: + permission["SourceAccount"] = condition["StringEquals"].get( + "AWS:SourceAccount" + ) + permissions[statement.get("Sid")] = permission + return {"permissions": permissions} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'ResourceNotFoundException': - return {'permissions': None} - return {'permissions': None, 'error': err} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "ResourceNotFoundException": + return {"permissions": None} + return {"permissions": None, "error": err} def list_functions(region=None, key=None, keyid=None, profile=None): - ''' + """ List all Lambda functions visible in the current scope. CLI Example: @@ -615,18 +756,19 @@ def list_functions(region=None, key=None, keyid=None, profile=None): salt myminion boto_lambda.list_functions - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) ret = [] - for funcs in __utils__['boto3.paged_call'](conn.list_functions): - ret += funcs['Functions'] + for funcs in __utils__["boto3.paged_call"](conn.list_functions): + ret += funcs["Functions"] return ret -def list_function_versions(FunctionName, - region=None, key=None, keyid=None, profile=None): - ''' +def list_function_versions( + FunctionName, region=None, key=None, keyid=None, profile=None +): + """ List the versions available for the given function. Returns list of function versions @@ -639,23 +781,32 @@ def list_function_versions(FunctionName, - {...} - {...} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) vers = [] - for ret in __utils__['boto3.paged_call'](conn.list_versions_by_function, - FunctionName=FunctionName): - vers.extend(ret['Versions']) + for ret in __utils__["boto3.paged_call"]( + conn.list_versions_by_function, FunctionName=FunctionName + ): + vers.extend(ret["Versions"]) if not bool(vers): - log.warning('No versions found') - return {'Versions': vers} + log.warning("No versions found") + return {"Versions": vers} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create_alias(FunctionName, Name, FunctionVersion, Description="", - region=None, key=None, keyid=None, profile=None): - ''' +def create_alias( + FunctionName, + Name, + FunctionVersion, + Description="", + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, create an alias to a function. Returns {created: true} if the alias was created and returns @@ -667,24 +818,28 @@ def create_alias(FunctionName, Name, FunctionVersion, Description="", salt myminion boto_lamba.create_alias my_function my_alias $LATEST "An alias" - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - alias = conn.create_alias(FunctionName=FunctionName, Name=Name, - FunctionVersion=FunctionVersion, Description=Description) + alias = conn.create_alias( + FunctionName=FunctionName, + Name=Name, + FunctionVersion=FunctionVersion, + Description=Description, + ) if alias: - log.info('The newly created alias name is %s', alias['Name']) + log.info("The newly created alias name is %s", alias["Name"]) - return {'created': True, 'name': alias['Name']} + return {"created": True, "name": alias["Name"]} else: - log.warning('Alias was not created') - return {'created': False} + log.warning("Alias was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} def delete_alias(FunctionName, Name, region=None, key=None, keyid=None, profile=None): - ''' + """ Given a function name and alias name, delete the alias. Returns {deleted: true} if the alias was deleted and returns @@ -696,39 +851,43 @@ def delete_alias(FunctionName, Name, region=None, key=None, keyid=None, profile= salt myminion boto_lambda.delete_alias myfunction myalias - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_alias(FunctionName=FunctionName, Name=Name) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def _find_alias(FunctionName, Name, FunctionVersion=None, - region=None, key=None, keyid=None, profile=None): - ''' +def _find_alias( + FunctionName, + Name, + FunctionVersion=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given function name and alias name, find and return matching alias information. - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - args = { - 'FunctionName': FunctionName - } + args = {"FunctionName": FunctionName} if FunctionVersion: - args['FunctionVersion'] = FunctionVersion + args["FunctionVersion"] = FunctionVersion - for aliases in __utils__['boto3.paged_call'](conn.list_aliases, **args): - for alias in aliases.get('Aliases'): - if alias['Name'] == Name: + for aliases in __utils__["boto3.paged_call"](conn.list_aliases, **args): + for alias in aliases.get("Aliases"): + if alias["Name"] == Name: return alias return None -def alias_exists(FunctionName, Name, region=None, key=None, - keyid=None, profile=None): - ''' +def alias_exists(FunctionName, Name, region=None, key=None, keyid=None, profile=None): + """ Given a function name and alias name, check to see if the given alias exists. Returns True if the given alias exists and returns False if the given @@ -740,19 +899,19 @@ def alias_exists(FunctionName, Name, region=None, key=None, salt myminion boto_lambda.alias_exists myfunction myalias - ''' + """ try: - alias = _find_alias(FunctionName, Name, - region=region, key=key, keyid=keyid, profile=profile) - return {'exists': bool(alias)} + alias = _find_alias( + FunctionName, Name, region=region, key=key, keyid=keyid, profile=profile + ) + return {"exists": bool(alias)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe_alias(FunctionName, Name, region=None, key=None, - keyid=None, profile=None): - ''' +def describe_alias(FunctionName, Name, region=None, key=None, keyid=None, profile=None): + """ Given a function name and alias name describe the properties of the alias. Returns a dictionary of interesting properties. @@ -763,23 +922,32 @@ def describe_alias(FunctionName, Name, region=None, key=None, salt myminion boto_lambda.describe_alias myalias - ''' + """ try: - alias = _find_alias(FunctionName, Name, - region=region, key=key, keyid=keyid, profile=profile) + alias = _find_alias( + FunctionName, Name, region=region, key=key, keyid=keyid, profile=profile + ) if alias: - keys = ('AliasArn', 'Name', 'FunctionVersion', 'Description') - return {'alias': dict([(k, alias.get(k)) for k in keys])} + keys = ("AliasArn", "Name", "FunctionVersion", "Description") + return {"alias": dict([(k, alias.get(k)) for k in keys])} else: - return {'alias': None} + return {"alias": None} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def update_alias(FunctionName, Name, FunctionVersion=None, Description=None, - region=None, key=None, keyid=None, profile=None): - ''' +def update_alias( + FunctionName, + Name, + FunctionVersion=None, + Description=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Update the named alias to the configuration. Returns {updated: true} if the alias was updated and returns @@ -791,30 +959,38 @@ def update_alias(FunctionName, Name, FunctionVersion=None, Description=None, salt myminion boto_lamba.update_alias my_lambda my_alias $LATEST - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) args = {} if FunctionVersion: - args['FunctionVersion'] = FunctionVersion + args["FunctionVersion"] = FunctionVersion if Description: - args['Description'] = Description + args["Description"] = Description r = conn.update_alias(FunctionName=FunctionName, Name=Name, **args) if r: - keys = ('Name', 'FunctionVersion', 'Description') - return {'updated': True, 'alias': dict([(k, r.get(k)) for k in keys])} + keys = ("Name", "FunctionVersion", "Description") + return {"updated": True, "alias": dict([(k, r.get(k)) for k in keys])} else: - log.warning('Alias was not updated') - return {'updated': False} + log.warning("Alias was not updated") + return {"updated": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def create_event_source_mapping(EventSourceArn, FunctionName, StartingPosition, - Enabled=True, BatchSize=100, - region=None, key=None, keyid=None, profile=None): - ''' +def create_event_source_mapping( + EventSourceArn, + FunctionName, + StartingPosition, + Enabled=True, + BatchSize=100, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Identifies a stream as an event source for a Lambda function. It can be either an Amazon Kinesis stream or an Amazon DynamoDB stream. AWS Lambda invokes the specified function when records are posted to the stream. @@ -828,28 +1004,31 @@ def create_event_source_mapping(EventSourceArn, FunctionName, StartingPosition, salt myminion boto_lamba.create_event_source_mapping arn::::eventsource myfunction LATEST - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - obj = conn.create_event_source_mapping(EventSourceArn=EventSourceArn, - FunctionName=FunctionName, - Enabled=Enabled, - BatchSize=BatchSize, - StartingPosition=StartingPosition) + obj = conn.create_event_source_mapping( + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + Enabled=Enabled, + BatchSize=BatchSize, + StartingPosition=StartingPosition, + ) if obj: - log.info('The newly created event source mapping ID is %s', obj['UUID']) + log.info("The newly created event source mapping ID is %s", obj["UUID"]) - return {'created': True, 'id': obj['UUID']} + return {"created": True, "id": obj["UUID"]} else: - log.warning('Event source mapping was not created') - return {'created': False} + log.warning("Event source mapping was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def get_event_source_mapping_ids(EventSourceArn, FunctionName, - region=None, key=None, keyid=None, profile=None): - ''' +def get_event_source_mapping_ids( + EventSourceArn, FunctionName, region=None, key=None, keyid=None, profile=None +): + """ Given an event source and function name, return a list of mapping IDs CLI Example: @@ -858,40 +1037,66 @@ def get_event_source_mapping_ids(EventSourceArn, FunctionName, salt myminion boto_lambda.get_event_source_mapping_ids arn:::: myfunction - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: mappings = [] - for maps in __utils__['boto3.paged_call'](conn.list_event_source_mappings, - EventSourceArn=EventSourceArn, - FunctionName=FunctionName): - mappings.extend([mapping['UUID'] - for mapping in maps['EventSourceMappings']]) + for maps in __utils__["boto3.paged_call"]( + conn.list_event_source_mappings, + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + ): + mappings.extend( + [mapping["UUID"] for mapping in maps["EventSourceMappings"]] + ) return mappings except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def _get_ids(UUID=None, EventSourceArn=None, FunctionName=None, - region=None, key=None, keyid=None, profile=None): +def _get_ids( + UUID=None, + EventSourceArn=None, + FunctionName=None, + region=None, + key=None, + keyid=None, + profile=None, +): if UUID: if EventSourceArn or FunctionName: - raise SaltInvocationError('Either UUID must be specified, or ' - 'EventSourceArn and FunctionName must be provided.') + raise SaltInvocationError( + "Either UUID must be specified, or " + "EventSourceArn and FunctionName must be provided." + ) return [UUID] else: if not EventSourceArn or not FunctionName: - raise SaltInvocationError('Either UUID must be specified, or ' - 'EventSourceArn and FunctionName must be provided.') - return get_event_source_mapping_ids(EventSourceArn=EventSourceArn, - FunctionName=FunctionName, - region=region, key=key, keyid=keyid, profile=profile) + raise SaltInvocationError( + "Either UUID must be specified, or " + "EventSourceArn and FunctionName must be provided." + ) + return get_event_source_mapping_ids( + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def delete_event_source_mapping(UUID=None, EventSourceArn=None, FunctionName=None, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_event_source_mapping( + UUID=None, + EventSourceArn=None, + FunctionName=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given an event source mapping ID or an event source ARN and FunctionName, delete the event source mapping @@ -904,22 +1109,27 @@ def delete_event_source_mapping(UUID=None, EventSourceArn=None, FunctionName=Non salt myminion boto_lambda.delete_event_source_mapping 260c423d-e8b5-4443-8d6a-5e91b9ecd0fa - ''' - ids = _get_ids(UUID, EventSourceArn=EventSourceArn, - FunctionName=FunctionName) + """ + ids = _get_ids(UUID, EventSourceArn=EventSourceArn, FunctionName=FunctionName) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) for id in ids: conn.delete_event_source_mapping(UUID=id) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def event_source_mapping_exists(UUID=None, EventSourceArn=None, - FunctionName=None, - region=None, key=None, keyid=None, profile=None): - ''' +def event_source_mapping_exists( + UUID=None, + EventSourceArn=None, + FunctionName=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given an event source mapping ID or an event source ARN and FunctionName, check whether the mapping exists. @@ -932,22 +1142,32 @@ def event_source_mapping_exists(UUID=None, EventSourceArn=None, salt myminion boto_lambda.alias_exists myfunction myalias - ''' + """ - desc = describe_event_source_mapping(UUID=UUID, - EventSourceArn=EventSourceArn, - FunctionName=FunctionName, - region=region, key=key, - keyid=keyid, profile=profile) - if 'error' in desc: + desc = describe_event_source_mapping( + UUID=UUID, + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in desc: return desc - return {'exists': bool(desc.get('event_source_mapping'))} + return {"exists": bool(desc.get("event_source_mapping"))} -def describe_event_source_mapping(UUID=None, EventSourceArn=None, - FunctionName=None, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_event_source_mapping( + UUID=None, + EventSourceArn=None, + FunctionName=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given an event source mapping ID or an event source ARN and FunctionName, obtain the current settings of that mapping. @@ -959,32 +1179,45 @@ def describe_event_source_mapping(UUID=None, EventSourceArn=None, salt myminion boto_lambda.describe_event_source_mapping uuid - ''' + """ - ids = _get_ids(UUID, EventSourceArn=EventSourceArn, - FunctionName=FunctionName) + ids = _get_ids(UUID, EventSourceArn=EventSourceArn, FunctionName=FunctionName) if len(ids) < 1: - return {'event_source_mapping': None} + return {"event_source_mapping": None} UUID = ids[0] try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) desc = conn.get_event_source_mapping(UUID=UUID) if desc: - keys = ('UUID', 'BatchSize', 'EventSourceArn', - 'FunctionArn', 'LastModified', 'LastProcessingResult', - 'State', 'StateTransitionReason') - return {'event_source_mapping': dict([(k, desc.get(k)) for k in keys])} + keys = ( + "UUID", + "BatchSize", + "EventSourceArn", + "FunctionArn", + "LastModified", + "LastProcessingResult", + "State", + "StateTransitionReason", + ) + return {"event_source_mapping": dict([(k, desc.get(k)) for k in keys])} else: - return {'event_source_mapping': None} + return {"event_source_mapping": None} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def update_event_source_mapping(UUID, - FunctionName=None, Enabled=None, BatchSize=None, - region=None, key=None, keyid=None, profile=None): - ''' +def update_event_source_mapping( + UUID, + FunctionName=None, + Enabled=None, + BatchSize=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Update the event source mapping identified by the UUID. Returns {updated: true} if the alias was updated and returns @@ -996,25 +1229,35 @@ def update_event_source_mapping(UUID, salt myminion boto_lamba.update_event_source_mapping uuid FunctionName=new_function - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) args = {} if FunctionName is not None: - args['FunctionName'] = FunctionName + args["FunctionName"] = FunctionName if Enabled is not None: - args['Enabled'] = Enabled + args["Enabled"] = Enabled if BatchSize is not None: - args['BatchSize'] = BatchSize + args["BatchSize"] = BatchSize r = conn.update_event_source_mapping(UUID=UUID, **args) if r: - keys = ('UUID', 'BatchSize', 'EventSourceArn', - 'FunctionArn', 'LastModified', 'LastProcessingResult', - 'State', 'StateTransitionReason') - return {'updated': True, 'event_source_mapping': dict([(k, r.get(k)) for k in keys])} + keys = ( + "UUID", + "BatchSize", + "EventSourceArn", + "FunctionArn", + "LastModified", + "LastProcessingResult", + "State", + "StateTransitionReason", + ) + return { + "updated": True, + "event_source_mapping": dict([(k, r.get(k)) for k in keys]), + } else: - log.warning('Mapping was not updated') - return {'updated': False} + log.warning("Mapping was not updated") + return {"updated": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} diff --git a/salt/modules/boto_rds.py b/salt/modules/boto_rds.py index 5cc978bfa24..4b9d4ba9699 100644 --- a/salt/modules/boto_rds.py +++ b/salt/modules/boto_rds.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon RDS .. versionadded:: 2015.8.0 @@ -40,15 +40,16 @@ Connection module for Amazon RDS region: us-east-1 :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # pylint whinging perfectly valid code -#pylint: disable=W0106 +# pylint: disable=W0106 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time @@ -58,220 +59,261 @@ import salt.utils.odict as odict import salt.utils.versions from salt.exceptions import SaltInvocationError -log = logging.getLogger(__name__) - # Import third party libs from salt.ext import six + +log = logging.getLogger(__name__) + + # pylint: disable=import-error try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import boto3 - #pylint: enable=unused-import + + # pylint: enable=unused-import from botocore.exceptions import ClientError - logging.getLogger('boto').setLevel(logging.CRITICAL) - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False # pylint: enable=import-error boto3_param_map = { - 'allocated_storage': ('AllocatedStorage', int), - 'allow_major_version_upgrade': ('AllowMajorVersionUpgrade', bool), - 'apply_immediately': ('ApplyImmediately', bool), - 'auto_minor_version_upgrade': ('AutoMinorVersionUpgrade', bool), - 'availability_zone': ('AvailabilityZone', str), - 'backup_retention_period': ('BackupRetentionPeriod', int), - 'ca_certificate_identifier': ('CACertificateIdentifier', str), - 'character_set_name': ('CharacterSetName', str), - 'copy_tags_to_snapshot': ('CopyTagsToSnapshot', bool), - 'db_cluster_identifier': ('DBClusterIdentifier', str), - 'db_instance_class': ('DBInstanceClass', str), - 'db_name': ('DBName', str), - 'db_parameter_group_name': ('DBParameterGroupName', str), - 'db_port_number': ('DBPortNumber', int), - 'db_security_groups': ('DBSecurityGroups', list), - 'db_subnet_group_name': ('DBSubnetGroupName', str), - 'domain': ('Domain', str), - 'domain_iam_role_name': ('DomainIAMRoleName', str), - 'engine': ('Engine', str), - 'engine_version': ('EngineVersion', str), - 'iops': ('Iops', int), - 'kms_key_id': ('KmsKeyId', str), - 'license_model': ('LicenseModel', str), - 'master_user_password': ('MasterUserPassword', str), - 'master_username': ('MasterUsername', str), - 'monitoring_interval': ('MonitoringInterval', int), - 'monitoring_role_arn': ('MonitoringRoleArn', str), - 'multi_az': ('MultiAZ', bool), - 'name': ('DBInstanceIdentifier', str), - 'new_db_instance_identifier': ('NewDBInstanceIdentifier', str), - 'option_group_name': ('OptionGroupName', str), - 'port': ('Port', int), - 'preferred_backup_window': ('PreferredBackupWindow', str), - 'preferred_maintenance_window': ('PreferredMaintenanceWindow', str), - 'promotion_tier': ('PromotionTier', int), - 'publicly_accessible': ('PubliclyAccessible', bool), - 'storage_encrypted': ('StorageEncrypted', bool), - 'storage_type': ('StorageType', str), - 'tags': ('Tags', list), - 'tde_credential_arn': ('TdeCredentialArn', str), - 'tde_credential_password': ('TdeCredentialPassword', str), - 'vpc_security_group_ids': ('VpcSecurityGroupIds', list), + "allocated_storage": ("AllocatedStorage", int), + "allow_major_version_upgrade": ("AllowMajorVersionUpgrade", bool), + "apply_immediately": ("ApplyImmediately", bool), + "auto_minor_version_upgrade": ("AutoMinorVersionUpgrade", bool), + "availability_zone": ("AvailabilityZone", str), + "backup_retention_period": ("BackupRetentionPeriod", int), + "ca_certificate_identifier": ("CACertificateIdentifier", str), + "character_set_name": ("CharacterSetName", str), + "copy_tags_to_snapshot": ("CopyTagsToSnapshot", bool), + "db_cluster_identifier": ("DBClusterIdentifier", str), + "db_instance_class": ("DBInstanceClass", str), + "db_name": ("DBName", str), + "db_parameter_group_name": ("DBParameterGroupName", str), + "db_port_number": ("DBPortNumber", int), + "db_security_groups": ("DBSecurityGroups", list), + "db_subnet_group_name": ("DBSubnetGroupName", str), + "domain": ("Domain", str), + "domain_iam_role_name": ("DomainIAMRoleName", str), + "engine": ("Engine", str), + "engine_version": ("EngineVersion", str), + "iops": ("Iops", int), + "kms_key_id": ("KmsKeyId", str), + "license_model": ("LicenseModel", str), + "master_user_password": ("MasterUserPassword", str), + "master_username": ("MasterUsername", str), + "monitoring_interval": ("MonitoringInterval", int), + "monitoring_role_arn": ("MonitoringRoleArn", str), + "multi_az": ("MultiAZ", bool), + "name": ("DBInstanceIdentifier", str), + "new_db_instance_identifier": ("NewDBInstanceIdentifier", str), + "option_group_name": ("OptionGroupName", str), + "port": ("Port", int), + "preferred_backup_window": ("PreferredBackupWindow", str), + "preferred_maintenance_window": ("PreferredMaintenanceWindow", str), + "promotion_tier": ("PromotionTier", int), + "publicly_accessible": ("PubliclyAccessible", bool), + "storage_encrypted": ("StorageEncrypted", bool), + "storage_type": ("StorageType", str), + "tags": ("Tags", list), + "tde_credential_arn": ("TdeCredentialArn", str), + "tde_credential_password": ("TdeCredentialPassword", str), + "vpc_security_group_ids": ("VpcSecurityGroupIds", list), } def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' - return salt.utils.versions.check_boto_reqs( - boto3_ver='1.3.1' - ) + """ + return salt.utils.versions.check_boto_reqs(boto3_ver="1.3.1") def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 'rds') + __utils__["boto3.assign_funcs"](__name__, "rds") def exists(name, tags=None, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if an RDS exists. CLI example:: salt myminion boto_rds.exists myrds region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: rds = conn.describe_db_instances(DBInstanceIdentifier=name) - return {'exists': bool(rds)} + return {"exists": bool(rds)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def option_group_exists(name, tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def option_group_exists( + name, tags=None, region=None, key=None, keyid=None, profile=None +): + """ Check to see if an RDS option group exists. CLI example:: salt myminion boto_rds.option_group_exists myoptiongr region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: rds = conn.describe_option_groups(OptionGroupName=name) - return {'exists': bool(rds)} + return {"exists": bool(rds)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def parameter_group_exists(name, tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def parameter_group_exists( + name, tags=None, region=None, key=None, keyid=None, profile=None +): + """ Check to see if an RDS parameter group exists. CLI example:: salt myminion boto_rds.parameter_group_exists myparametergroup \ region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: rds = conn.describe_db_parameter_groups(DBParameterGroupName=name) - return {'exists': bool(rds), 'error': None} + return {"exists": bool(rds), "error": None} except ClientError as e: resp = {} - if e.response['Error']['Code'] == 'DBParameterGroupNotFound': - resp['exists'] = False - resp['error'] = __utils__['boto3.get_error'](e) + if e.response["Error"]["Code"] == "DBParameterGroupNotFound": + resp["exists"] = False + resp["error"] = __utils__["boto3.get_error"](e) return resp -def subnet_group_exists(name, tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def subnet_group_exists( + name, tags=None, region=None, key=None, keyid=None, profile=None +): + """ Check to see if an RDS subnet group exists. CLI example:: salt myminion boto_rds.subnet_group_exists my-param-group \ region=us-east-1 - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'exists': bool(conn)} + return {"exists": bool(conn)} rds = conn.describe_db_subnet_groups(DBSubnetGroupName=name) - return {'exists': bool(rds)} + return {"exists": bool(rds)} except ClientError as e: if "DBSubnetGroupNotFoundFault" in e.message: - return {'exists': False} + return {"exists": False} else: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create(name, allocated_storage, db_instance_class, engine, - master_username, master_user_password, db_name=None, - db_security_groups=None, vpc_security_group_ids=None, - vpc_security_groups=None, availability_zone=None, - db_subnet_group_name=None, preferred_maintenance_window=None, - db_parameter_group_name=None, backup_retention_period=None, - preferred_backup_window=None, port=None, multi_az=None, - engine_version=None, auto_minor_version_upgrade=None, - license_model=None, iops=None, option_group_name=None, - character_set_name=None, publicly_accessible=None, wait_status=None, - tags=None, db_cluster_identifier=None, storage_type=None, - tde_credential_arn=None, tde_credential_password=None, - storage_encrypted=None, kms_key_id=None, domain=None, - copy_tags_to_snapshot=None, monitoring_interval=None, - monitoring_role_arn=None, domain_iam_role_name=None, region=None, - promotion_tier=None, key=None, keyid=None, profile=None): - ''' +def create( + name, + allocated_storage, + db_instance_class, + engine, + master_username, + master_user_password, + db_name=None, + db_security_groups=None, + vpc_security_group_ids=None, + vpc_security_groups=None, + availability_zone=None, + db_subnet_group_name=None, + preferred_maintenance_window=None, + db_parameter_group_name=None, + backup_retention_period=None, + preferred_backup_window=None, + port=None, + multi_az=None, + engine_version=None, + auto_minor_version_upgrade=None, + license_model=None, + iops=None, + option_group_name=None, + character_set_name=None, + publicly_accessible=None, + wait_status=None, + tags=None, + db_cluster_identifier=None, + storage_type=None, + tde_credential_arn=None, + tde_credential_password=None, + storage_encrypted=None, + kms_key_id=None, + domain=None, + copy_tags_to_snapshot=None, + monitoring_interval=None, + monitoring_role_arn=None, + domain_iam_role_name=None, + region=None, + promotion_tier=None, + key=None, + keyid=None, + profile=None, +): + """ Create an RDS Instance CLI example to create an RDS Instance:: salt myminion boto_rds.create myrds 10 db.t2.micro MySQL sqlusr sqlpassw - ''' + """ if not allocated_storage: - raise SaltInvocationError('allocated_storage is required') + raise SaltInvocationError("allocated_storage is required") if not db_instance_class: - raise SaltInvocationError('db_instance_class is required') + raise SaltInvocationError("db_instance_class is required") if not engine: - raise SaltInvocationError('engine is required') + raise SaltInvocationError("engine is required") if not master_username: - raise SaltInvocationError('master_username is required') + raise SaltInvocationError("master_username is required") if not master_user_password: - raise SaltInvocationError('master_user_password is required') + raise SaltInvocationError("master_user_password is required") if availability_zone and multi_az: - raise SaltInvocationError('availability_zone and multi_az are mutually' - ' exclusive arguments.') + raise SaltInvocationError( + "availability_zone and multi_az are mutually" " exclusive arguments." + ) if wait_status: - wait_stati = ['available', 'modifying', 'backing-up'] + wait_stati = ["available", "modifying", "backing-up"] if wait_status not in wait_stati: raise SaltInvocationError( - 'wait_status can be one of: {0}'.format(wait_stati)) + "wait_status can be one of: {0}".format(wait_stati) + ) if vpc_security_groups: - v_tmp = __salt__['boto_secgroup.convert_to_group_ids']( - groups=vpc_security_groups, region=region, key=key, keyid=keyid, - profile=profile) - vpc_security_group_ids = (vpc_security_group_ids + v_tmp - if vpc_security_group_ids else v_tmp) + v_tmp = __salt__["boto_secgroup.convert_to_group_ids"]( + groups=vpc_security_groups, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + vpc_security_group_ids = ( + vpc_security_group_ids + v_tmp if vpc_security_group_ids else v_tmp + ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'results': bool(conn)} + return {"results": bool(conn)} kwargs = {} boto_params = set(boto3_param_map.keys()) @@ -291,164 +333,230 @@ def create(name, allocated_storage, db_instance_class, engine, rds = conn.create_db_instance(**kwargs) if not rds: - return {'created': False} + return {"created": False} if not wait_status: - return {'created': True, 'message': - 'RDS instance {0} created.'.format(name)} + return { + "created": True, + "message": "RDS instance {0} created.".format(name), + } while True: - jmespath = 'DBInstances[*].DBInstanceStatus' - status = describe_db_instances(name=name, jmespath=jmespath, - region=region, key=key, keyid=keyid, - profile=profile) + jmespath = "DBInstances[*].DBInstanceStatus" + status = describe_db_instances( + name=name, + jmespath=jmespath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if status: stat = status[0] else: # Whoops, something is horribly wrong... - return {'created': False, - 'error': "RDS instance {0} should have been created but" - " now I can't find it.".format(name)} + return { + "created": False, + "error": "RDS instance {0} should have been created but" + " now I can't find it.".format(name), + } if stat == wait_status: - return {'created': True, - 'message': 'RDS instance {0} created (current status ' - '{1})'.format(name, stat)} + return { + "created": True, + "message": "RDS instance {0} created (current status " + "{1})".format(name, stat), + } time.sleep(10) - log.info('Instance status after 10 seconds is: %s', stat) + log.info("Instance status after 10 seconds is: %s", stat) except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create_read_replica(name, source_name, db_instance_class=None, - availability_zone=None, port=None, - auto_minor_version_upgrade=None, iops=None, - option_group_name=None, publicly_accessible=None, - tags=None, db_subnet_group_name=None, - storage_type=None, copy_tags_to_snapshot=None, - monitoring_interval=None, monitoring_role_arn=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_read_replica( + name, + source_name, + db_instance_class=None, + availability_zone=None, + port=None, + auto_minor_version_upgrade=None, + iops=None, + option_group_name=None, + publicly_accessible=None, + tags=None, + db_subnet_group_name=None, + storage_type=None, + copy_tags_to_snapshot=None, + monitoring_interval=None, + monitoring_role_arn=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an RDS read replica CLI example to create an RDS read replica:: salt myminion boto_rds.create_read_replica replicaname source_name - ''' + """ if not backup_retention_period: - raise SaltInvocationError('backup_retention_period is required') - res = __salt__['boto_rds.exists'](source_name, tags, region, key, keyid, profile) - if not res.get('exists'): - return {'exists': bool(res), 'message': - 'RDS instance source {0} does not exists.'.format(source_name)} + raise SaltInvocationError("backup_retention_period is required") + res = __salt__["boto_rds.exists"](source_name, tags, region, key, keyid, profile) + if not res.get("exists"): + return { + "exists": bool(res), + "message": "RDS instance source {0} does not exists.".format(source_name), + } - res = __salt__['boto_rds.exists'](name, tags, region, key, keyid, profile) - if res.get('exists'): - return {'exists': bool(res), 'message': - 'RDS replica instance {0} already exists.'.format(name)} + res = __salt__["boto_rds.exists"](name, tags, region, key, keyid, profile) + if res.get("exists"): + return { + "exists": bool(res), + "message": "RDS replica instance {0} already exists.".format(name), + } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} - for key in ('OptionGroupName', 'MonitoringRoleArn'): + for key in ("OptionGroupName", "MonitoringRoleArn"): if locals()[key] is not None: - kwargs[key] = str(locals()[key]) # future lint: disable=blacklisted-function + kwargs[key] = str( + locals()[key] + ) # future lint: disable=blacklisted-function - for key in ('MonitoringInterval', 'Iops', 'Port'): + for key in ("MonitoringInterval", "Iops", "Port"): if locals()[key] is not None: kwargs[key] = int(locals()[key]) - for key in ('CopyTagsToSnapshot', 'AutoMinorVersionUpgrade'): + for key in ("CopyTagsToSnapshot", "AutoMinorVersionUpgrade"): if locals()[key] is not None: kwargs[key] = bool(locals()[key]) taglist = _tag_doc(tags) - rds_replica = conn.create_db_instance_read_replica(DBInstanceIdentifier=name, - SourceDBInstanceIdentifier=source_name, - DBInstanceClass=db_instance_class, - AvailabilityZone=availability_zone, - PubliclyAccessible=publicly_accessible, - Tags=taglist, DBSubnetGroupName=db_subnet_group_name, - StorageType=storage_type, - **kwargs) + rds_replica = conn.create_db_instance_read_replica( + DBInstanceIdentifier=name, + SourceDBInstanceIdentifier=source_name, + DBInstanceClass=db_instance_class, + AvailabilityZone=availability_zone, + PubliclyAccessible=publicly_accessible, + Tags=taglist, + DBSubnetGroupName=db_subnet_group_name, + StorageType=storage_type, + **kwargs + ) - return {'exists': bool(rds_replica)} + return {"exists": bool(rds_replica)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create_option_group(name, engine_name, major_engine_version, - option_group_description, tags=None, region=None, - key=None, keyid=None, profile=None): - ''' +def create_option_group( + name, + engine_name, + major_engine_version, + option_group_description, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an RDS option group CLI example to create an RDS option group:: salt myminion boto_rds.create_option_group my-opt-group mysql 5.6 \ "group description" - ''' - res = __salt__['boto_rds.option_group_exists'](name, tags, region, key, keyid, - profile) - if res.get('exists'): - return {'exists': bool(res)} + """ + res = __salt__["boto_rds.option_group_exists"]( + name, tags, region, key, keyid, profile + ) + if res.get("exists"): + return {"exists": bool(res)} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'results': bool(conn)} + return {"results": bool(conn)} taglist = _tag_doc(tags) - rds = conn.create_option_group(OptionGroupName=name, - EngineName=engine_name, - MajorEngineVersion=major_engine_version, - OptionGroupDescription=option_group_description, - Tags=taglist) + rds = conn.create_option_group( + OptionGroupName=name, + EngineName=engine_name, + MajorEngineVersion=major_engine_version, + OptionGroupDescription=option_group_description, + Tags=taglist, + ) - return {'exists': bool(rds)} + return {"exists": bool(rds)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create_parameter_group(name, db_parameter_group_family, description, - tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def create_parameter_group( + name, + db_parameter_group_family, + description, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an RDS parameter group CLI example to create an RDS parameter group:: salt myminion boto_rds.create_parameter_group my-param-group mysql5.6 \ "group description" - ''' - res = __salt__['boto_rds.parameter_group_exists'](name, tags, region, key, - keyid, profile) - if res.get('exists'): - return {'exists': bool(res)} + """ + res = __salt__["boto_rds.parameter_group_exists"]( + name, tags, region, key, keyid, profile + ) + if res.get("exists"): + return {"exists": bool(res)} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'results': bool(conn)} + return {"results": bool(conn)} taglist = _tag_doc(tags) - rds = conn.create_db_parameter_group(DBParameterGroupName=name, - DBParameterGroupFamily=db_parameter_group_family, - Description=description, - Tags=taglist) + rds = conn.create_db_parameter_group( + DBParameterGroupName=name, + DBParameterGroupFamily=db_parameter_group_family, + Description=description, + Tags=taglist, + ) if not rds: - return {'created': False, 'message': - 'Failed to create RDS parameter group {0}'.format(name)} + return { + "created": False, + "message": "Failed to create RDS parameter group {0}".format(name), + } - return {'exists': bool(rds), 'message': - 'Created RDS parameter group {0}'.format(name)} + return { + "exists": bool(rds), + "message": "Created RDS parameter group {0}".format(name), + } except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def create_subnet_group(name, description, subnet_ids, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_subnet_group( + name, + description, + subnet_ids, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an RDS subnet group CLI example to create an RDS subnet group:: @@ -456,31 +564,42 @@ def create_subnet_group(name, description, subnet_ids, tags=None, salt myminion boto_rds.create_subnet_group my-subnet-group \ "group description" '[subnet-12345678, subnet-87654321]' \ region=us-east-1 - ''' - res = __salt__['boto_rds.subnet_group_exists'](name, tags, region, key, - keyid, profile) - if res.get('exists'): - return {'exists': bool(res)} + """ + res = __salt__["boto_rds.subnet_group_exists"]( + name, tags, region, key, keyid, profile + ) + if res.get("exists"): + return {"exists": bool(res)} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'results': bool(conn)} + return {"results": bool(conn)} taglist = _tag_doc(tags) - rds = conn.create_db_subnet_group(DBSubnetGroupName=name, - DBSubnetGroupDescription=description, - SubnetIds=subnet_ids, Tags=taglist) + rds = conn.create_db_subnet_group( + DBSubnetGroupName=name, + DBSubnetGroupDescription=description, + SubnetIds=subnet_ids, + Tags=taglist, + ) - return {'created': bool(rds)} + return {"created": bool(rds)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def update_parameter_group(name, parameters, apply_method="pending-reboot", - tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def update_parameter_group( + name, + parameters, + apply_method="pending-reboot", + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Update an RDS parameter group. CLI example:: @@ -488,93 +607,126 @@ def update_parameter_group(name, parameters, apply_method="pending-reboot", salt myminion boto_rds.update_parameter_group my-param-group \ parameters='{"back_log":1, "binlog_cache_size":4096}' \ region=us-east-1 - ''' + """ - res = __salt__['boto_rds.parameter_group_exists'](name, tags, region, key, - keyid, profile) - if not res.get('exists'): - return {'exists': bool(res), 'message': - 'RDS parameter group {0} does not exist.'.format(name)} + res = __salt__["boto_rds.parameter_group_exists"]( + name, tags, region, key, keyid, profile + ) + if not res.get("exists"): + return { + "exists": bool(res), + "message": "RDS parameter group {0} does not exist.".format(name), + } param_list = [] for key, value in six.iteritems(parameters): item = odict.OrderedDict() - item.update({'ParameterName': key}) - item.update({'ApplyMethod': apply_method}) + item.update({"ParameterName": key}) + item.update({"ApplyMethod": apply_method}) if type(value) is bool: - item.update({'ParameterValue': 'on' if value else 'off'}) + item.update({"ParameterValue": "on" if value else "off"}) else: - item.update({'ParameterValue': str(value)}) # future lint: disable=blacklisted-function + item.update( + {"ParameterValue": str(value)} + ) # future lint: disable=blacklisted-function param_list.append(item) if not param_list: - return {'results': False} + return {"results": False} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'results': bool(conn)} + return {"results": bool(conn)} - res = conn.modify_db_parameter_group(DBParameterGroupName=name, - Parameters=param_list) - return {'results': bool(res)} + res = conn.modify_db_parameter_group( + DBParameterGroupName=name, Parameters=param_list + ) + return {"results": bool(res)} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe(name, tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def describe(name, tags=None, region=None, key=None, keyid=None, profile=None): + """ Return RDS instance details. CLI example:: salt myminion boto_rds.describe myrds - ''' - res = __salt__['boto_rds.exists'](name, tags, region, key, keyid, - profile) - if not res.get('exists'): - return {'exists': bool(res), 'message': - 'RDS instance {0} does not exist.'.format(name)} + """ + res = __salt__["boto_rds.exists"](name, tags, region, key, keyid, profile) + if not res.get("exists"): + return { + "exists": bool(res), + "message": "RDS instance {0} does not exist.".format(name), + } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'results': bool(conn)} + return {"results": bool(conn)} rds = conn.describe_db_instances(DBInstanceIdentifier=name) rds = [ - i for i in rds.get('DBInstances', []) - if i.get('DBInstanceIdentifier') == name + i + for i in rds.get("DBInstances", []) + if i.get("DBInstanceIdentifier") == name ].pop(0) if rds: - keys = ('DBInstanceIdentifier', 'DBInstanceClass', 'Engine', - 'DBInstanceStatus', 'DBName', 'AllocatedStorage', - 'PreferredBackupWindow', 'BackupRetentionPeriod', - 'AvailabilityZone', 'PreferredMaintenanceWindow', - 'LatestRestorableTime', 'EngineVersion', - 'AutoMinorVersionUpgrade', 'LicenseModel', - 'Iops', 'CharacterSetName', 'PubliclyAccessible', - 'StorageType', 'TdeCredentialArn', 'DBInstancePort', - 'DBClusterIdentifier', 'StorageEncrypted', 'KmsKeyId', - 'DbiResourceId', 'CACertificateIdentifier', - 'CopyTagsToSnapshot', 'MonitoringInterval', - 'MonitoringRoleArn', 'PromotionTier', - 'DomainMemberships') - return {'rds': dict([(k, rds.get(k)) for k in keys])} + keys = ( + "DBInstanceIdentifier", + "DBInstanceClass", + "Engine", + "DBInstanceStatus", + "DBName", + "AllocatedStorage", + "PreferredBackupWindow", + "BackupRetentionPeriod", + "AvailabilityZone", + "PreferredMaintenanceWindow", + "LatestRestorableTime", + "EngineVersion", + "AutoMinorVersionUpgrade", + "LicenseModel", + "Iops", + "CharacterSetName", + "PubliclyAccessible", + "StorageType", + "TdeCredentialArn", + "DBInstancePort", + "DBClusterIdentifier", + "StorageEncrypted", + "KmsKeyId", + "DbiResourceId", + "CACertificateIdentifier", + "CopyTagsToSnapshot", + "MonitoringInterval", + "MonitoringRoleArn", + "PromotionTier", + "DomainMemberships", + ) + return {"rds": dict([(k, rds.get(k)) for k in keys])} else: - return {'rds': None} + return {"rds": None} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} except IndexError: - return {'rds': None} + return {"rds": None} -def describe_db_instances(name=None, filters=None, jmespath='DBInstances', - region=None, key=None, keyid=None, profile=None): - ''' +def describe_db_instances( + name=None, + filters=None, + jmespath="DBInstances", + region=None, + key=None, + keyid=None, + profile=None, +): + """ Return a detailed listing of some, or all, DB Instances visible in the current scope. Arbitrary subelements or subsections of the returned dataset can be selected by passing in a valid JMSEPath filter as well. @@ -583,26 +735,33 @@ def describe_db_instances(name=None, filters=None, jmespath='DBInstances', salt myminion boto_rds.describe_db_instances jmespath='DBInstances[*].DBInstanceIdentifier' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - pag = conn.get_paginator('describe_db_instances') + pag = conn.get_paginator("describe_db_instances") args = {} - args.update({'DBInstanceIdentifier': name}) if name else None - args.update({'Filters': filters}) if filters else None + args.update({"DBInstanceIdentifier": name}) if name else None + args.update({"Filters": filters}) if filters else None pit = pag.paginate(**args) pit = pit.search(jmespath) if jmespath else pit try: return [p for p in pit] except ClientError as e: - code = getattr(e, 'response', {}).get('Error', {}).get('Code') - if code != 'DBInstanceNotFound': - log.error(__utils__['boto3.get_error'](e)) + code = getattr(e, "response", {}).get("Error", {}).get("Code") + if code != "DBInstanceNotFound": + log.error(__utils__["boto3.get_error"](e)) return [] -def describe_db_subnet_groups(name=None, filters=None, jmespath='DBSubnetGroups', - region=None, key=None, keyid=None, profile=None): - ''' +def describe_db_subnet_groups( + name=None, + filters=None, + jmespath="DBSubnetGroups", + region=None, + key=None, + keyid=None, + profile=None, +): + """ Return a detailed listing of some, or all, DB Subnet Groups visible in the current scope. Arbitrary subelements or subsections of the returned dataset can be selected by passing in a valid JMSEPath filter as well. @@ -611,326 +770,400 @@ def describe_db_subnet_groups(name=None, filters=None, jmespath='DBSubnetGroups' salt myminion boto_rds.describe_db_subnet_groups - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - pag = conn.get_paginator('describe_db_subnet_groups') + pag = conn.get_paginator("describe_db_subnet_groups") args = {} - args.update({'DBSubnetGroupName': name}) if name else None - args.update({'Filters': filters}) if filters else None + args.update({"DBSubnetGroupName": name}) if name else None + args.update({"Filters": filters}) if filters else None pit = pag.paginate(**args) pit = pit.search(jmespath) if jmespath else pit return [p for p in pit] -def get_endpoint(name, tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def get_endpoint(name, tags=None, region=None, key=None, keyid=None, profile=None): + """ Return the endpoint of an RDS instance. CLI example:: salt myminion boto_rds.get_endpoint myrds - ''' + """ endpoint = False - res = __salt__['boto_rds.exists'](name, tags, region, key, keyid, - profile) - if res.get('exists'): + res = __salt__["boto_rds.exists"](name, tags, region, key, keyid, profile) + if res.get("exists"): try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if conn: rds = conn.describe_db_instances(DBInstanceIdentifier=name) - if rds and 'Endpoint' in rds['DBInstances'][0]: - endpoint = rds['DBInstances'][0]['Endpoint']['Address'] + if rds and "Endpoint" in rds["DBInstances"][0]: + endpoint = rds["DBInstances"][0]["Endpoint"]["Address"] return endpoint except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} return endpoint -def delete(name, skip_final_snapshot=None, final_db_snapshot_identifier=None, - region=None, key=None, keyid=None, profile=None, tags=None, - wait_for_deletion=True, timeout=180): - ''' +def delete( + name, + skip_final_snapshot=None, + final_db_snapshot_identifier=None, + region=None, + key=None, + keyid=None, + profile=None, + tags=None, + wait_for_deletion=True, + timeout=180, +): + """ Delete an RDS instance. CLI example:: salt myminion boto_rds.delete myrds skip_final_snapshot=True \ region=us-east-1 - ''' + """ if timeout == 180 and not skip_final_snapshot: timeout = 420 if not skip_final_snapshot and not final_db_snapshot_identifier: - raise SaltInvocationError('At least one of the following must' - ' be specified: skip_final_snapshot' - ' final_db_snapshot_identifier') + raise SaltInvocationError( + "At least one of the following must" + " be specified: skip_final_snapshot" + " final_db_snapshot_identifier" + ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'deleted': bool(conn)} + return {"deleted": bool(conn)} kwargs = {} - if locals()['skip_final_snapshot'] is not None: - kwargs['SkipFinalSnapshot'] = bool(locals()['skip_final_snapshot']) + if locals()["skip_final_snapshot"] is not None: + kwargs["SkipFinalSnapshot"] = bool(locals()["skip_final_snapshot"]) - if locals()['final_db_snapshot_identifier'] is not None: - kwargs['FinalDBSnapshotIdentifier'] = str(locals()['final_db_snapshot_identifier']) # future lint: disable=blacklisted-function + if locals()["final_db_snapshot_identifier"] is not None: + kwargs["FinalDBSnapshotIdentifier"] = str( + locals()["final_db_snapshot_identifier"] + ) # future lint: disable=blacklisted-function res = conn.delete_db_instance(DBInstanceIdentifier=name, **kwargs) if not wait_for_deletion: - return {'deleted': bool(res), 'message': - 'Deleted RDS instance {0}.'.format(name)} + return { + "deleted": bool(res), + "message": "Deleted RDS instance {0}.".format(name), + } start_time = time.time() while True: - res = __salt__['boto_rds.exists'](name=name, tags=tags, region=region, - key=key, keyid=keyid, - profile=profile) - if not res.get('exists'): - return {'deleted': bool(res), 'message': - 'Deleted RDS instance {0} completely.'.format(name)} + res = __salt__["boto_rds.exists"]( + name=name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not res.get("exists"): + return { + "deleted": bool(res), + "message": "Deleted RDS instance {0} completely.".format(name), + } if time.time() - start_time > timeout: - raise SaltInvocationError('RDS instance {0} has not been ' - 'deleted completely after {1} ' - 'seconds'.format(name, timeout)) - log.info('Waiting up to %s seconds for RDS instance %s to be ' - 'deleted.', timeout, name) + raise SaltInvocationError( + "RDS instance {0} has not been " + "deleted completely after {1} " + "seconds".format(name, timeout) + ) + log.info( + "Waiting up to %s seconds for RDS instance %s to be " "deleted.", + timeout, + name, + ) time.sleep(10) except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def delete_option_group(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete an RDS option group. CLI example:: salt myminion boto_rds.delete_option_group my-opt-group \ region=us-east-1 - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'deleted': bool(conn)} + return {"deleted": bool(conn)} res = conn.delete_option_group(OptionGroupName=name) if not res: - return {'deleted': bool(res), 'message': - 'Failed to delete RDS option group {0}.'.format(name)} + return { + "deleted": bool(res), + "message": "Failed to delete RDS option group {0}.".format(name), + } - return {'deleted': bool(res), 'message': - 'Deleted RDS option group {0}.'.format(name)} + return { + "deleted": bool(res), + "message": "Deleted RDS option group {0}.".format(name), + } except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def delete_parameter_group(name, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_parameter_group(name, region=None, key=None, keyid=None, profile=None): + """ Delete an RDS parameter group. CLI example:: salt myminion boto_rds.delete_parameter_group my-param-group \ region=us-east-1 - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'results': bool(conn)} + return {"results": bool(conn)} r = conn.delete_db_parameter_group(DBParameterGroupName=name) - return {'deleted': bool(r), 'message': - 'Deleted RDS parameter group {0}.'.format(name)} + return { + "deleted": bool(r), + "message": "Deleted RDS parameter group {0}.".format(name), + } except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def delete_subnet_group(name, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_subnet_group(name, region=None, key=None, keyid=None, profile=None): + """ Delete an RDS subnet group. CLI example:: salt myminion boto_rds.delete_subnet_group my-subnet-group \ region=us-east-1 - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'results': bool(conn)} + return {"results": bool(conn)} r = conn.delete_db_subnet_group(DBSubnetGroupName=name) - return {'deleted': bool(r), 'message': - 'Deleted RDS subnet group {0}.'.format(name)} + return { + "deleted": bool(r), + "message": "Deleted RDS subnet group {0}.".format(name), + } except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe_parameter_group(name, Filters=None, MaxRecords=None, Marker=None, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_parameter_group( + name, + Filters=None, + MaxRecords=None, + Marker=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Returns a list of `DBParameterGroup` descriptions. CLI example to description of parameter group:: salt myminion boto_rds.describe_parameter_group parametergroupname\ region=us-east-1 - ''' - res = __salt__['boto_rds.parameter_group_exists'](name, tags=None, - region=region, key=key, - keyid=keyid, - profile=profile) - if not res.get('exists'): - return {'exists': bool(res)} + """ + res = __salt__["boto_rds.parameter_group_exists"]( + name, tags=None, region=region, key=key, keyid=keyid, profile=profile + ) + if not res.get("exists"): + return {"exists": bool(res)} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'results': bool(conn)} + return {"results": bool(conn)} kwargs = {} - for key in ('Marker', 'Filters'): + for key in ("Marker", "Filters"): if locals()[key] is not None: - kwargs[key] = str(locals()[key]) # future lint: disable=blacklisted-function + kwargs[key] = str( + locals()[key] + ) # future lint: disable=blacklisted-function - if locals()['MaxRecords'] is not None: - kwargs['MaxRecords'] = int(locals()['MaxRecords']) + if locals()["MaxRecords"] is not None: + kwargs["MaxRecords"] = int(locals()["MaxRecords"]) - info = conn.describe_db_parameter_groups(DBParameterGroupName=name, - **kwargs) + info = conn.describe_db_parameter_groups(DBParameterGroupName=name, **kwargs) if not info: - return {'results': bool(info), 'message': - 'Failed to get RDS description for group {0}.'.format(name)} + return { + "results": bool(info), + "message": "Failed to get RDS description for group {0}.".format(name), + } - return {'results': bool(info), 'message': - 'Got RDS descrition for group {0}.'.format(name)} + return { + "results": bool(info), + "message": "Got RDS descrition for group {0}.".format(name), + } except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def describe_parameters(name, Source=None, MaxRecords=None, Marker=None, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_parameters( + name, + Source=None, + MaxRecords=None, + Marker=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Returns a list of `DBParameterGroup` parameters. CLI example to description of parameters :: salt myminion boto_rds.describe_parameters parametergroupname\ region=us-east-1 - ''' - res = __salt__['boto_rds.parameter_group_exists'](name, tags=None, - region=region, key=key, - keyid=keyid, - profile=profile) - if not res.get('exists'): - return {'result': False, - 'message': 'Parameter group {0} does not exist'.format(name)} + """ + res = __salt__["boto_rds.parameter_group_exists"]( + name, tags=None, region=region, key=key, keyid=keyid, profile=profile + ) + if not res.get("exists"): + return { + "result": False, + "message": "Parameter group {0} does not exist".format(name), + } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'result': False, - 'message': 'Could not establish a connection to RDS'} + return { + "result": False, + "message": "Could not establish a connection to RDS", + } kwargs = {} - kwargs.update({'DBParameterGroupName': name}) - for key in ('Marker', 'Source'): + kwargs.update({"DBParameterGroupName": name}) + for key in ("Marker", "Source"): if locals()[key] is not None: - kwargs[key] = str(locals()[key]) # future lint: disable=blacklisted-function + kwargs[key] = str( + locals()[key] + ) # future lint: disable=blacklisted-function - if locals()['MaxRecords'] is not None: - kwargs['MaxRecords'] = int(locals()['MaxRecords']) + if locals()["MaxRecords"] is not None: + kwargs["MaxRecords"] = int(locals()["MaxRecords"]) - pag = conn.get_paginator('describe_db_parameters') + pag = conn.get_paginator("describe_db_parameters") pit = pag.paginate(**kwargs) - keys = ['ParameterName', 'ParameterValue', 'Description', - 'Source', 'ApplyType', 'DataType', 'AllowedValues', - 'IsModifieable', 'MinimumEngineVersion', 'ApplyMethod'] + keys = [ + "ParameterName", + "ParameterValue", + "Description", + "Source", + "ApplyType", + "DataType", + "AllowedValues", + "IsModifieable", + "MinimumEngineVersion", + "ApplyMethod", + ] parameters = odict.OrderedDict() - ret = {'result': True} + ret = {"result": True} for p in pit: - for result in p['Parameters']: + for result in p["Parameters"]: data = odict.OrderedDict() for k in keys: data[k] = result.get(k) - parameters[result.get('ParameterName')] = data + parameters[result.get("ParameterName")] = data - ret['parameters'] = parameters + ret["parameters"] = parameters return ret except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def modify_db_instance(name, - allocated_storage=None, - allow_major_version_upgrade=None, - apply_immediately=None, - auto_minor_version_upgrade=None, - backup_retention_period=None, - ca_certificate_identifier=None, - character_set_name=None, - copy_tags_to_snapshot=None, - db_cluster_identifier=None, - db_instance_class=None, - db_name=None, - db_parameter_group_name=None, - db_port_number=None, - db_security_groups=None, - db_subnet_group_name=None, - domain=None, - domain_iam_role_name=None, - engine_version=None, - iops=None, - kms_key_id=None, - license_model=None, - master_user_password=None, - monitoring_interval=None, - monitoring_role_arn=None, - multi_az=None, - new_db_instance_identifier=None, - option_group_name=None, - preferred_backup_window=None, - preferred_maintenance_window=None, - promotion_tier=None, - publicly_accessible=None, - storage_encrypted=None, - storage_type=None, - tde_credential_arn=None, - tde_credential_password=None, - vpc_security_group_ids=None, - region=None, key=None, keyid=None, profile=None): - ''' +def modify_db_instance( + name, + allocated_storage=None, + allow_major_version_upgrade=None, + apply_immediately=None, + auto_minor_version_upgrade=None, + backup_retention_period=None, + ca_certificate_identifier=None, + character_set_name=None, + copy_tags_to_snapshot=None, + db_cluster_identifier=None, + db_instance_class=None, + db_name=None, + db_parameter_group_name=None, + db_port_number=None, + db_security_groups=None, + db_subnet_group_name=None, + domain=None, + domain_iam_role_name=None, + engine_version=None, + iops=None, + kms_key_id=None, + license_model=None, + master_user_password=None, + monitoring_interval=None, + monitoring_role_arn=None, + multi_az=None, + new_db_instance_identifier=None, + option_group_name=None, + preferred_backup_window=None, + preferred_maintenance_window=None, + promotion_tier=None, + publicly_accessible=None, + storage_encrypted=None, + storage_type=None, + tde_credential_arn=None, + tde_credential_password=None, + vpc_security_group_ids=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Modify settings for a DB instance. CLI example to description of parameters :: salt myminion boto_rds.modify_db_instance db_instance_identifier region=us-east-1 - ''' - res = __salt__['boto_rds.exists'](name, tags=None, region=region, key=key, keyid=keyid, profile=profile) - if not res.get('exists'): - return {'modified': False, 'message': - 'RDS db instance {0} does not exist.'.format(name)} + """ + res = __salt__["boto_rds.exists"]( + name, tags=None, region=region, key=key, keyid=keyid, profile=profile + ) + if not res.get("exists"): + return { + "modified": False, + "message": "RDS db instance {0} does not exist.".format(name), + } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not conn: - return {'modified': False} + return {"modified": False} kwargs = {} - excluded = set(('name',)) + excluded = set(("name",)) boto_params = set(boto3_param_map.keys()) keys = set(locals().keys()) for key in keys.intersection(boto_params).difference(excluded): @@ -942,21 +1175,25 @@ def modify_db_instance(name, info = conn.modify_db_instance(DBInstanceIdentifier=name, **kwargs) if not info: - return {'modified': bool(info), 'message': - 'Failed to modify RDS db instance {0}.'.format(name)} + return { + "modified": bool(info), + "message": "Failed to modify RDS db instance {0}.".format(name), + } - return {'modified': bool(info), 'message': - 'Modified RDS db instance {0}.'.format(name), - 'results': dict(info)} + return { + "modified": bool(info), + "message": "Modified RDS db instance {0}.".format(name), + "results": dict(info), + } except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def _tag_doc(tags): taglist = [] if tags is not None: for k, v in six.iteritems(tags): - if six.text_type(k).startswith('__'): + if six.text_type(k).startswith("__"): continue - taglist.append({'Key': six.text_type(k), 'Value': six.text_type(v)}) + taglist.append({"Key": six.text_type(k), "Value": six.text_type(v)}) return taglist diff --git a/salt/modules/boto_route53.py b/salt/modules/boto_route53.py index a663ec72071..6916a2d8cda 100644 --- a/salt/modules/boto_route53.py +++ b/salt/modules/boto_route53.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Route53 .. versionadded:: 2014.7.0 @@ -41,9 +41,9 @@ Connection module for Amazon Route53 region: us-east-1 :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 from __future__ import absolute_import, print_function, unicode_literals @@ -53,62 +53,63 @@ import time # Import salt libs import salt.utils.compat -import salt.utils.versions import salt.utils.odict as odict +import salt.utils.versions from salt.exceptions import SaltInvocationError log = logging.getLogger(__name__) # Import third party libs try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import boto.route53 import boto.route53.healthcheck from boto.route53.exception import DNSServerError - #pylint: enable=unused-import - logging.getLogger('boto').setLevel(logging.CRITICAL) + + # pylint: enable=unused-import + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' + """ # create_zone params were changed in boto 2.35+ - return salt.utils.versions.check_boto_reqs( - boto_ver='2.35.0', - check_boto3=False - ) + return salt.utils.versions.check_boto_reqs(boto_ver="2.35.0", check_boto3=False) def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto.assign_funcs'](__name__, 'route53', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "route53", pack=__salt__) def _get_split_zone(zone, _conn, private_zone): - ''' + """ With boto route53, zones can only be matched by name or iterated over in a list. Since the name will be the same for public and private zones in a split DNS situation, iterate over the list and match the zone name and public/private status. - ''' + """ for _zone in _conn.get_zones(): if _zone.name == zone: - _private_zone = True if _zone.config['PrivateZone'].lower() == 'true' else False + _private_zone = ( + True if _zone.config["PrivateZone"].lower() == "true" else False + ) if _private_zone == private_zone: return _zone return False -def describe_hosted_zones(zone_id=None, domain_name=None, region=None, - key=None, keyid=None, profile=None): - ''' +def describe_hosted_zones( + zone_id=None, domain_name=None, region=None, key=None, keyid=None, profile=None +): + """ Return detailed info about one, or all, zones in the bound account. If neither zone_id nor domain_name is provided, return all zones. Note that the return format is slightly different between the 'all' @@ -139,47 +140,56 @@ def describe_hosted_zones(zone_id=None, domain_name=None, region=None, salt myminion boto_route53.describe_hosted_zones domain_name=foo.bar.com. \ profile='{"region": "us-east-1", "keyid": "A12345678AB", "key": "xblahblahblah"}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if zone_id and domain_name: - raise SaltInvocationError('At most one of zone_id or domain_name may ' - 'be provided') + raise SaltInvocationError( + "At most one of zone_id or domain_name may " "be provided" + ) retries = 10 while retries: try: if zone_id: - zone_id = zone_id.replace('/hostedzone/', - '') if zone_id.startswith('/hostedzone/') else zone_id - ret = getattr(conn.get_hosted_zone(zone_id), - 'GetHostedZoneResponse', None) + zone_id = ( + zone_id.replace("/hostedzone/", "") + if zone_id.startswith("/hostedzone/") + else zone_id + ) + ret = getattr( + conn.get_hosted_zone(zone_id), "GetHostedZoneResponse", None + ) elif domain_name: - ret = getattr(conn.get_hosted_zone_by_name(domain_name), - 'GetHostedZoneResponse', None) + ret = getattr( + conn.get_hosted_zone_by_name(domain_name), + "GetHostedZoneResponse", + None, + ) else: marker = None ret = None - while marker is not '': - r = conn.get_all_hosted_zones(start_marker=marker, - zone_list=ret) - ret = r['ListHostedZonesResponse']['HostedZones'] - marker = r['ListHostedZonesResponse'].get('NextMarker', '') + while marker is not "": + r = conn.get_all_hosted_zones(start_marker=marker, zone_list=ret) + ret = r["ListHostedZonesResponse"]["HostedZones"] + marker = r["ListHostedZonesResponse"].get("NextMarker", "") return ret if ret else [] except DNSServerError as e: if retries: - if 'Throttling' == e.code: - log.debug('Throttled by AWS API.') - elif 'PriorRequestNotComplete' == e.code: - log.debug('The request was rejected by AWS API.\ - Route 53 was still processing a prior request') + if "Throttling" == e.code: + log.debug("Throttled by AWS API.") + elif "PriorRequestNotComplete" == e.code: + log.debug( + "The request was rejected by AWS API.\ + Route 53 was still processing a prior request" + ) time.sleep(3) retries -= 1 continue - log.error('Could not list zones: %s', e.message) + log.error("Could not list zones: %s", e.message) return [] def list_all_zones_by_name(region=None, key=None, keyid=None, profile=None): - ''' + """ List, by their FQDNs, all hosted zones in the bound account. region @@ -200,14 +210,13 @@ def list_all_zones_by_name(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_route53.list_all_zones_by_name - ''' - ret = describe_hosted_zones(region=region, key=key, keyid=keyid, - profile=profile) - return [r['Name'] for r in ret] + """ + ret = describe_hosted_zones(region=region, key=key, keyid=keyid, profile=profile) + return [r["Name"] for r in ret] def list_all_zones_by_id(region=None, key=None, keyid=None, profile=None): - ''' + """ List, by their IDs, all hosted zones in the bound account. region @@ -228,16 +237,23 @@ def list_all_zones_by_id(region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_route53.list_all_zones_by_id - ''' - ret = describe_hosted_zones(region=region, key=key, keyid=keyid, - profile=profile) - return [r['Id'].replace('/hostedzone/', '') for r in ret] + """ + ret = describe_hosted_zones(region=region, key=key, keyid=keyid, profile=profile) + return [r["Id"].replace("/hostedzone/", "") for r in ret] -def zone_exists(zone, region=None, key=None, keyid=None, profile=None, - retry_on_rate_limit=None, rate_limit_retries=None, - retry_on_errors=True, error_retries=5): - ''' +def zone_exists( + zone, + region=None, + key=None, + keyid=None, + profile=None, + retry_on_rate_limit=None, + rate_limit_retries=None, + retry_on_errors=True, + error_retries=5, +): + """ Check for the existence of a Route53 hosted zone. .. versionadded:: 2015.8.0 @@ -262,9 +278,9 @@ def zone_exists(zone, region=None, key=None, keyid=None, profile=None, but please migrate to using the favored `error_retries` argument instead. - ''' + """ if region is None: - region = 'universal' + region = "universal" conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -280,20 +296,30 @@ def zone_exists(zone, region=None, key=None, keyid=None, profile=None, except DNSServerError as e: if retry_on_errors: - if 'Throttling' == e.code: - log.debug('Throttled by AWS API.') - elif 'PriorRequestNotComplete' == e.code: - log.debug('The request was rejected by AWS API.\ - Route 53 was still processing a prior request') + if "Throttling" == e.code: + log.debug("Throttled by AWS API.") + elif "PriorRequestNotComplete" == e.code: + log.debug( + "The request was rejected by AWS API.\ + Route 53 was still processing a prior request" + ) time.sleep(3) error_retries -= 1 continue six.reraise(*sys.exc_info()) -def create_zone(zone, private=False, vpc_id=None, vpc_region=None, region=None, - key=None, keyid=None, profile=None): - ''' +def create_zone( + zone, + private=False, + vpc_id=None, + vpc_region=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a Route53 hosted zone. .. versionadded:: 2015.8.0 @@ -325,13 +351,13 @@ def create_zone(zone, private=False, vpc_id=None, vpc_region=None, region=None, CLI Example:: salt myminion boto_route53.create_zone example.org - ''' + """ if region is None: - region = 'universal' + region = "universal" if private: if not vpc_id or not vpc_region: - msg = 'vpc_id and vpc_region must be specified for a private zone' + msg = "vpc_id and vpc_region must be specified for a private zone" raise SaltInvocationError(msg) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -341,15 +367,27 @@ def create_zone(zone, private=False, vpc_id=None, vpc_region=None, region=None, if _zone: return False - conn.create_zone(zone, private_zone=private, vpc_id=vpc_id, - vpc_region=vpc_region) + conn.create_zone(zone, private_zone=private, vpc_id=vpc_id, vpc_region=vpc_region) return True -def create_healthcheck(ip_addr=None, fqdn=None, region=None, key=None, keyid=None, profile=None, - port=53, hc_type='TCP', resource_path='', string_match=None, request_interval=30, - failure_threshold=3, retry_on_errors=True, error_retries=5): - ''' +def create_healthcheck( + ip_addr=None, + fqdn=None, + region=None, + key=None, + keyid=None, + profile=None, + port=53, + hc_type="TCP", + resource_path="", + string_match=None, + request_interval=30, + failure_threshold=3, + retry_on_errors=True, + error_retries=5, +): + """ Create a Route53 healthcheck .. versionadded:: 2018.3.0 @@ -411,45 +449,49 @@ def create_healthcheck(ip_addr=None, fqdn=None, region=None, key=None, keyid=Non salt myminion boto_route53.create_healthcheck 192.168.0.1 salt myminion boto_route53.create_healthcheck 192.168.0.1 port=443 hc_type=HTTPS \ resource_path=/ fqdn=blog.saltstack.furniture - ''' + """ if fqdn is None and ip_addr is None: - msg = 'One of the following must be specified: fqdn or ip_addr' + msg = "One of the following must be specified: fqdn or ip_addr" log.error(msg) - return {'error': msg} - hc_ = boto.route53.healthcheck.HealthCheck(ip_addr, - port, - hc_type, - resource_path, - fqdn=fqdn, - string_match=string_match, - request_interval=request_interval, - failure_threshold=failure_threshold) + return {"error": msg} + hc_ = boto.route53.healthcheck.HealthCheck( + ip_addr, + port, + hc_type, + resource_path, + fqdn=fqdn, + string_match=string_match, + request_interval=request_interval, + failure_threshold=failure_threshold, + ) if region is None: - region = 'universal' + region = "universal" conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) while error_retries > 0: try: - return {'result': conn.create_health_check(hc_)} + return {"result": conn.create_health_check(hc_)} except DNSServerError as exc: log.debug(exc) if retry_on_errors: - if 'Throttling' == exc.code: - log.debug('Throttled by AWS API.') - elif 'PriorRequestNotComplete' == exc.code: - log.debug('The request was rejected by AWS API.\ - Route 53 was still processing a prior request') + if "Throttling" == exc.code: + log.debug("Throttled by AWS API.") + elif "PriorRequestNotComplete" == exc.code: + log.debug( + "The request was rejected by AWS API.\ + Route 53 was still processing a prior request" + ) time.sleep(3) error_retries -= 1 continue - return {'error': __utils__['boto.get_error'](exc)} + return {"error": __utils__["boto.get_error"](exc)} return False def delete_zone(zone, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete a Route53 hosted zone. .. versionadded:: 2015.8.0 @@ -457,9 +499,9 @@ def delete_zone(zone, region=None, key=None, keyid=None, profile=None): CLI Example:: salt myminion boto_route53.delete_zone example.org - ''' + """ if region is None: - region = 'universal' + region = "universal" conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -472,18 +514,31 @@ def delete_zone(zone, region=None, key=None, keyid=None, profile=None): def _encode_name(name): - return name.replace('*', r'\052') + return name.replace("*", r"\052") def _decode_name(name): - return name.replace(r'\052', '*') + return name.replace(r"\052", "*") -def get_record(name, zone, record_type, fetch_all=False, region=None, key=None, - keyid=None, profile=None, split_dns=False, private_zone=False, - identifier=None, retry_on_rate_limit=None, - rate_limit_retries=None, retry_on_errors=True, error_retries=5): - ''' +def get_record( + name, + zone, + record_type, + fetch_all=False, + region=None, + key=None, + keyid=None, + profile=None, + split_dns=False, + private_zone=False, + identifier=None, + retry_on_rate_limit=None, + rate_limit_retries=None, + retry_on_errors=True, + error_retries=5, +): + """ Get a record from a zone. CLI example:: @@ -505,9 +560,9 @@ def get_record(name, zone, record_type, fetch_all=False, region=None, key=None, `rate_limit_retries` to ensure backwards compatibility, but please migrate to using the favored `error_retries` argument instead. - ''' + """ if region is None: - region = 'universal' + region = "universal" conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -524,7 +579,7 @@ def get_record(name, zone, record_type, fetch_all=False, region=None, key=None, else: _zone = conn.get_zone(zone) if not _zone: - msg = 'Failed to retrieve zone {0}'.format(zone) + msg = "Failed to retrieve zone {0}".format(zone) log.error(msg) return None _type = record_type.upper() @@ -532,48 +587,66 @@ def get_record(name, zone, record_type, fetch_all=False, region=None, key=None, name = _encode_name(name) - _record = _zone.find_records(name, _type, all=fetch_all, identifier=identifier) + _record = _zone.find_records( + name, _type, all=fetch_all, identifier=identifier + ) break # the while True except DNSServerError as e: if retry_on_errors: - if 'Throttling' == e.code: - log.debug('Throttled by AWS API.') - elif 'PriorRequestNotComplete' == e.code: - log.debug('The request was rejected by AWS API.\ - Route 53 was still processing a prior request') + if "Throttling" == e.code: + log.debug("Throttled by AWS API.") + elif "PriorRequestNotComplete" == e.code: + log.debug( + "The request was rejected by AWS API.\ + Route 53 was still processing a prior request" + ) time.sleep(3) error_retries -= 1 continue six.reraise(*sys.exc_info()) if _record: - ret['name'] = _decode_name(_record.name) - ret['value'] = _record.resource_records[0] - ret['record_type'] = _record.type - ret['ttl'] = _record.ttl + ret["name"] = _decode_name(_record.name) + ret["value"] = _record.resource_records[0] + ret["record_type"] = _record.type + ret["ttl"] = _record.ttl if _record.identifier: - ret['identifier'] = [] - ret['identifier'].append(_record.identifier) - ret['identifier'].append(_record.weight) + ret["identifier"] = [] + ret["identifier"].append(_record.identifier) + ret["identifier"].append(_record.weight) return ret def _munge_value(value, _type): - split_types = ['A', 'MX', 'AAAA', 'TXT', 'SRV', 'SPF', 'NS'] + split_types = ["A", "MX", "AAAA", "TXT", "SRV", "SPF", "NS"] if _type in split_types: - return value.split(',') + return value.split(",") return value -def add_record(name, value, zone, record_type, identifier=None, ttl=None, - region=None, key=None, keyid=None, profile=None, - wait_for_sync=True, split_dns=False, private_zone=False, - retry_on_rate_limit=None, rate_limit_retries=None, - retry_on_errors=True, error_retries=5): - ''' +def add_record( + name, + value, + zone, + record_type, + identifier=None, + ttl=None, + region=None, + key=None, + keyid=None, + profile=None, + wait_for_sync=True, + split_dns=False, + private_zone=False, + retry_on_rate_limit=None, + rate_limit_retries=None, + retry_on_errors=True, + error_retries=5, +): + """ Add a record to a zone. CLI example:: @@ -595,9 +668,9 @@ def add_record(name, value, zone, record_type, identifier=None, ttl=None, `rate_limit_retries` to ensure backwards compatibility, but please migrate to using the favored `error_retries` argument instead. - ''' + """ if region is None: - region = 'universal' + region = "universal" conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -614,7 +687,7 @@ def add_record(name, value, zone, record_type, identifier=None, ttl=None, else: _zone = conn.get_zone(zone) if not _zone: - msg = 'Failed to retrieve zone {0}'.format(zone) + msg = "Failed to retrieve zone {0}".format(zone) log.error(msg) return False _type = record_type.upper() @@ -622,11 +695,13 @@ def add_record(name, value, zone, record_type, identifier=None, ttl=None, except DNSServerError as e: if retry_on_errors: - if 'Throttling' == e.code: - log.debug('Throttled by AWS API.') - elif 'PriorRequestNotComplete' == e.code: - log.debug('The request was rejected by AWS API.\ - Route 53 was still processing a prior request') + if "Throttling" == e.code: + log.debug("Throttled by AWS API.") + elif "PriorRequestNotComplete" == e.code: + log.debug( + "The request was rejected by AWS API.\ + Route 53 was still processing a prior request" + ) time.sleep(3) error_retries -= 1 continue @@ -643,23 +718,39 @@ def add_record(name, value, zone, record_type, identifier=None, ttl=None, except DNSServerError as e: if retry_on_errors: - if 'Throttling' == e.code: - log.debug('Throttled by AWS API.') - elif 'PriorRequestNotComplete' == e.code: - log.debug('The request was rejected by AWS API.\ - Route 53 was still processing a prior request') + if "Throttling" == e.code: + log.debug("Throttled by AWS API.") + elif "PriorRequestNotComplete" == e.code: + log.debug( + "The request was rejected by AWS API.\ + Route 53 was still processing a prior request" + ) time.sleep(3) error_retries -= 1 continue six.reraise(*sys.exc_info()) -def update_record(name, value, zone, record_type, identifier=None, ttl=None, - region=None, key=None, keyid=None, profile=None, - wait_for_sync=True, split_dns=False, private_zone=False, - retry_on_rate_limit=None, rate_limit_retries=None, - retry_on_errors=True, error_retries=5): - ''' +def update_record( + name, + value, + zone, + record_type, + identifier=None, + ttl=None, + region=None, + key=None, + keyid=None, + profile=None, + wait_for_sync=True, + split_dns=False, + private_zone=False, + retry_on_rate_limit=None, + rate_limit_retries=None, + retry_on_errors=True, + error_retries=5, +): + """ Modify a record in a zone. CLI example:: @@ -681,9 +772,9 @@ def update_record(name, value, zone, record_type, identifier=None, ttl=None, `rate_limit_retries` to ensure backwards compatibility, but please migrate to using the favored `error_retries` argument instead. - ''' + """ if region is None: - region = 'universal' + region = "universal" conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -692,7 +783,7 @@ def update_record(name, value, zone, record_type, identifier=None, ttl=None, else: _zone = conn.get_zone(zone) if not _zone: - msg = 'Failed to retrieve zone {0}'.format(zone) + msg = "Failed to retrieve zone {0}".format(zone) log.error(msg) return False _type = record_type.upper() @@ -714,23 +805,38 @@ def update_record(name, value, zone, record_type, identifier=None, ttl=None, except DNSServerError as e: if retry_on_errors: - if 'Throttling' == e.code: - log.debug('Throttled by AWS API.') - elif 'PriorRequestNotComplete' == e.code: - log.debug('The request was rejected by AWS API.\ - Route 53 was still processing a prior request') + if "Throttling" == e.code: + log.debug("Throttled by AWS API.") + elif "PriorRequestNotComplete" == e.code: + log.debug( + "The request was rejected by AWS API.\ + Route 53 was still processing a prior request" + ) time.sleep(3) error_retries -= 1 continue six.reraise(*sys.exc_info()) -def delete_record(name, zone, record_type, identifier=None, all_records=False, - region=None, key=None, keyid=None, profile=None, - wait_for_sync=True, split_dns=False, private_zone=False, - retry_on_rate_limit=None, rate_limit_retries=None, - retry_on_errors=True, error_retries=5): - ''' +def delete_record( + name, + zone, + record_type, + identifier=None, + all_records=False, + region=None, + key=None, + keyid=None, + profile=None, + wait_for_sync=True, + split_dns=False, + private_zone=False, + retry_on_rate_limit=None, + rate_limit_retries=None, + retry_on_errors=True, + error_retries=5, +): + """ Modify a record in a zone. CLI example:: @@ -752,9 +858,9 @@ def delete_record(name, zone, record_type, identifier=None, all_records=False, `rate_limit_retries` to ensure backwards compatibility, but please migrate to using the favored `error_retries` argument instead. - ''' + """ if region is None: - region = 'universal' + region = "universal" conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -763,7 +869,7 @@ def delete_record(name, zone, record_type, identifier=None, all_records=False, else: _zone = conn.get_zone(zone) if not _zone: - msg = 'Failed to retrieve zone {0}'.format(zone) + msg = "Failed to retrieve zone {0}".format(zone) log.error(msg) return False _type = record_type.upper() @@ -776,7 +882,9 @@ def delete_record(name, zone, record_type, identifier=None, all_records=False, while error_retries > 0: try: - old_record = _zone.find_records(name, _type, all=all_records, identifier=identifier) + old_record = _zone.find_records( + name, _type, all=all_records, identifier=identifier + ) if not old_record: return False status = _zone.delete_record(old_record) @@ -784,11 +892,13 @@ def delete_record(name, zone, record_type, identifier=None, all_records=False, except DNSServerError as e: if retry_on_errors: - if 'Throttling' == e.code: - log.debug('Throttled by AWS API.') - elif 'PriorRequestNotComplete' == e.code: - log.debug('The request was rejected by AWS API.\ - Route 53 was still processing a prior request') + if "Throttling" == e.code: + log.debug("Throttled by AWS API.") + elif "PriorRequestNotComplete" == e.code: + log.debug( + "The request was rejected by AWS API.\ + Route 53 was still processing a prior request" + ) time.sleep(3) error_retries -= 1 continue @@ -802,16 +912,17 @@ def _try_func(conn, func, **args): return getattr(conn, func)(**args) except AttributeError as e: # Don't include **args in log messages - security concern. - log.error('Function `%s()` not found for AWS connection object %s', - func, conn) + log.error( + "Function `%s()` not found for AWS connection object %s", func, conn + ) return None except DNSServerError as e: - if tries and e.code == 'Throttling': - log.debug('Throttled by AWS API. Will retry in 5 seconds') + if tries and e.code == "Throttling": + log.debug("Throttled by AWS API. Will retry in 5 seconds") time.sleep(5) tries -= 1 continue - log.error('Failed calling %s(): %s', func, e) + log.error("Failed calling %s(): %s", func, e) return None @@ -822,28 +933,39 @@ def _wait_for_sync(status, conn, wait=True): if not wait: return True orig_wait = wait - log.info('Waiting up to %s seconds for Route53 changes to synchronize', orig_wait) + log.info("Waiting up to %s seconds for Route53 changes to synchronize", orig_wait) while wait > 0: change = conn.get_change(status) current = change.GetChangeResponse.ChangeInfo.Status - if current == 'INSYNC': + if current == "INSYNC": return True sleep = wait if wait % 60 == wait else 60 log.info( - 'Sleeping %s seconds waiting for changes to synch (current status %s)', - sleep, current + "Sleeping %s seconds waiting for changes to synch (current status %s)", + sleep, + current, ) time.sleep(sleep) wait -= sleep continue - log.error('Route53 changes not synced after %s seconds.', orig_wait) + log.error("Route53 changes not synced after %s seconds.", orig_wait) return False -def create_hosted_zone(domain_name, caller_ref=None, comment='', private_zone=False, vpc_id=None, - vpc_name=None, vpc_region=None, region=None, key=None, keyid=None, - profile=None): - ''' +def create_hosted_zone( + domain_name, + caller_ref=None, + comment="", + private_zone=False, + vpc_id=None, + vpc_name=None, + vpc_region=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a new Route53 Hosted Zone. Returns a Python data structure with information about the newly created Hosted Zone. @@ -898,63 +1020,78 @@ def create_hosted_zone(domain_name, caller_ref=None, comment='', private_zone=Fa CLI Example:: salt myminion boto_route53.create_hosted_zone example.org - ''' + """ if region is None: - region = 'universal' + region = "universal" - if not domain_name.endswith('.'): - raise SaltInvocationError('Domain MUST be fully-qualified, complete ' - 'with ending period.') + if not domain_name.endswith("."): + raise SaltInvocationError( + "Domain MUST be fully-qualified, complete " "with ending period." + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) deets = conn.get_hosted_zone_by_name(domain_name) if deets: - log.info('Route53 hosted zone %s already exists', domain_name) + log.info("Route53 hosted zone %s already exists", domain_name) return None - args = {'domain_name': domain_name, - 'caller_ref': caller_ref, - 'comment': comment, - 'private_zone': private_zone} + args = { + "domain_name": domain_name, + "caller_ref": caller_ref, + "comment": comment, + "private_zone": private_zone, + } if private_zone: if not _exactly_one((vpc_name, vpc_id)): - raise SaltInvocationError('Either vpc_name or vpc_id is required ' - 'when creating a private zone.') - vpcs = __salt__['boto_vpc.describe_vpcs']( - vpc_id=vpc_id, name=vpc_name, region=region, key=key, - keyid=keyid, profile=profile).get('vpcs', []) + raise SaltInvocationError( + "Either vpc_name or vpc_id is required " "when creating a private zone." + ) + vpcs = __salt__["boto_vpc.describe_vpcs"]( + vpc_id=vpc_id, + name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("vpcs", []) if vpc_region and vpcs: - vpcs = [v for v in vpcs if v['region'] == vpc_region] + vpcs = [v for v in vpcs if v["region"] == vpc_region] if not vpcs: - log.error('Private zone requested but a VPC matching given criteria' - ' not found.') + log.error( + "Private zone requested but a VPC matching given criteria" " not found." + ) return None if len(vpcs) > 1: - log.error('Private zone requested but multiple VPCs matching given ' - 'criteria found: %s.', [v['id'] for v in vpcs]) + log.error( + "Private zone requested but multiple VPCs matching given " + "criteria found: %s.", + [v["id"] for v in vpcs], + ) return None vpc = vpcs[0] if vpc_name: - vpc_id = vpc['id'] + vpc_id = vpc["id"] if not vpc_region: - vpc_region = vpc['region'] - args.update({'vpc_id': vpc_id, 'vpc_region': vpc_region}) + vpc_region = vpc["region"] + args.update({"vpc_id": vpc_id, "vpc_region": vpc_region}) else: if any((vpc_id, vpc_name, vpc_region)): - log.info('Options vpc_id, vpc_name, and vpc_region are ignored ' - 'when creating non-private zones.') + log.info( + "Options vpc_id, vpc_name, and vpc_region are ignored " + "when creating non-private zones." + ) - r = _try_func(conn, 'create_hosted_zone', **args) + r = _try_func(conn, "create_hosted_zone", **args) if r is None: - log.error('Failed to create hosted zone %s', domain_name) + log.error("Failed to create hosted zone %s", domain_name) return None - r = r.get('CreateHostedZoneResponse', {}) + r = r.get("CreateHostedZoneResponse", {}) # Pop it since it'll be irrelevant by the time we return - status = r.pop('ChangeInfo', {}).get('Id', '').replace('/change/', '') + status = r.pop("ChangeInfo", {}).get("Id", "").replace("/change/", "") synced = _wait_for_sync(status, conn, wait=600) if not synced: - log.error('Hosted zone %s not synced after 600 seconds.', domain_name) + log.error("Hosted zone %s not synced after 600 seconds.", domain_name) return None return r diff --git a/salt/modules/boto_s3.py b/salt/modules/boto_s3.py index ef952d82966..e11e726d2c1 100644 --- a/salt/modules/boto_s3.py +++ b/salt/modules/boto_s3.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon S3 using boto3 .. versionadded:: 2018.3.0 @@ -46,12 +46,13 @@ Connection module for Amazon S3 using boto3 region: us-east-1 :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -63,9 +64,11 @@ log = logging.getLogger(__name__) try: # pylint: disable=unused-import import boto3 + # pylint: enable=unused-import import botocore - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -73,29 +76,22 @@ except ImportError: def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' - return salt.utils.versions.check_boto_reqs( - boto3_ver='1.2.1' - ) + """ + return salt.utils.versions.check_boto_reqs(boto3_ver="1.2.1") def __init__(opts): # pylint: disable=unused-argument if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 's3') + __utils__["boto3.assign_funcs"](__name__, "s3") def get_object_metadata( - name, - extra_args=None, - region=None, - key=None, - keyid=None, - profile=None, + name, extra_args=None, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Get metadata about an S3 object. Returns None if the object does not exist. @@ -111,37 +107,27 @@ def get_object_metadata( key=key \\ keyid=keyid \\ profile=profile \\ - ''' - bucket, _, s3_key = name.partition('/') + """ + bucket, _, s3_key = name.partition("/") if extra_args is None: extra_args = {} conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - metadata = conn.head_object( - Bucket=bucket, - Key=s3_key, - **extra_args - ) + metadata = conn.head_object(Bucket=bucket, Key=s3_key, **extra_args) except botocore.exceptions.ClientError as e: - if e.response['Error']['Message'] == 'Not Found': - return {'result': None} - return {'error': __utils__['boto3.get_error'](e)} + if e.response["Error"]["Message"] == "Not Found": + return {"result": None} + return {"error": __utils__["boto3.get_error"](e)} - return {'result': metadata} + return {"result": metadata} def upload_file( - source, - name, - extra_args=None, - region=None, - key=None, - keyid=None, - profile=None, + source, name, extra_args=None, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Upload a local file as an S3 object. CLI Example: @@ -155,15 +141,15 @@ def upload_file( key=key \\ keyid=keyid \\ profile=profile \\ - ''' - bucket, _, s3_key = name.partition('/') + """ + bucket, _, s3_key = name.partition("/") conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.upload_file(source, bucket, s3_key, ExtraArgs=extra_args) except boto3.exceptions.S3UploadFailedError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} - log.info('S3 object uploaded to %s', name) - return {'result': True} + log.info("S3 object uploaded to %s", name) + return {"result": True} diff --git a/salt/modules/boto_s3_bucket.py b/salt/modules/boto_s3_bucket.py index 93311525fc7..7136ff99fed 100644 --- a/salt/modules/boto_s3_bucket.py +++ b/salt/modules/boto_s3_bucket.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon S3 Buckets .. versionadded:: 2016.3.0 @@ -45,7 +45,7 @@ The dependencies listed above can be installed via package or pip. key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs region: us-east-1 -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 # disable complaints about perfectly valid non-assignment code @@ -53,16 +53,18 @@ The dependencies listed above can be installed via package or pip. # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt libs -from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error import salt.utils.compat import salt.utils.json import salt.utils.versions from salt.exceptions import SaltInvocationError +# Import Salt libs +from salt.ext import six +from salt.ext.six.moves import range # pylint: disable=import-error + log = logging.getLogger(__name__) # Import third party libs @@ -72,9 +74,11 @@ try: # pylint: disable=unused-import import boto import boto3 + # pylint: enable=unused-import from botocore.exceptions import ClientError - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -82,27 +86,24 @@ except ImportError: def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # the boto_lambda execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 - return salt.utils.versions.check_boto_reqs( - boto3_ver='1.2.1' - ) + return salt.utils.versions.check_boto_reqs(boto3_ver="1.2.1") def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto3.assign_funcs'](__name__, 's3') + __utils__["boto3.assign_funcs"](__name__, "s3") -def exists(Bucket, - region=None, key=None, keyid=None, profile=None): - ''' +def exists(Bucket, region=None, key=None, keyid=None, profile=None): + """ Given a bucket name, check to see if the given bucket exists. Returns True if the given bucket exists and returns False if the given @@ -114,28 +115,34 @@ def exists(Bucket, salt myminion boto_s3_bucket.exists mybucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) buckets = conn.head_bucket(Bucket=Bucket) - return {'exists': True} + return {"exists": True} except ClientError as e: - if e.response.get('Error', {}).get('Code') == '404': - return {'exists': False} - err = __utils__['boto3.get_error'](e) - return {'error': err} + if e.response.get("Error", {}).get("Code") == "404": + return {"exists": False} + err = __utils__["boto3.get_error"](e) + return {"error": err} -def create(Bucket, - ACL=None, LocationConstraint=None, - GrantFullControl=None, - GrantRead=None, - GrantReadACP=None, - GrantWrite=None, - GrantWriteACP=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create( + Bucket, + ACL=None, + LocationConstraint=None, + GrantFullControl=None, + GrantRead=None, + GrantReadACP=None, + GrantWrite=None, + GrantWriteACP=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, create an S3 Bucket. Returns {created: true} if the bucket was created and returns @@ -151,35 +158,53 @@ def create(Bucket, GrantReadACP='emailaddress="exampl@example.com",id="2345678909876432"' \\ LocationConstraint=us-west-1 - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) kwargs = {} - for arg in ('ACL', 'GrantFullControl', - 'GrantRead', 'GrantReadACP', - 'GrantWrite', 'GrantWriteACP'): + for arg in ( + "ACL", + "GrantFullControl", + "GrantRead", + "GrantReadACP", + "GrantWrite", + "GrantWriteACP", + ): if locals()[arg] is not None: - kwargs[arg] = str(locals()[arg]) # future lint: disable=blacklisted-function + kwargs[arg] = str( + locals()[arg] + ) # future lint: disable=blacklisted-function if LocationConstraint: - kwargs['CreateBucketConfiguration'] = {'LocationConstraint': LocationConstraint} - location = conn.create_bucket(Bucket=Bucket, - **kwargs) + kwargs["CreateBucketConfiguration"] = { + "LocationConstraint": LocationConstraint + } + location = conn.create_bucket(Bucket=Bucket, **kwargs) conn.get_waiter("bucket_exists").wait(Bucket=Bucket) if location: - log.info('The newly created bucket name is located at %s', location['Location']) + log.info( + "The newly created bucket name is located at %s", location["Location"] + ) - return {'created': True, 'name': Bucket, 'Location': location['Location']} + return {"created": True, "name": Bucket, "Location": location["Location"]} else: - log.warning('Bucket was not created') - return {'created': False} + log.warning("Bucket was not created") + return {"created": False} except ClientError as e: - return {'created': False, 'error': __utils__['boto3.get_error'](e)} + return {"created": False, "error": __utils__["boto3.get_error"](e)} -def delete(Bucket, MFA=None, RequestPayer=None, Force=False, - region=None, key=None, keyid=None, profile=None): - ''' +def delete( + Bucket, + MFA=None, + RequestPayer=None, + Force=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a bucket name, delete it, optionally emptying it first. Returns {deleted: true} if the bucket was deleted and returns @@ -191,22 +216,37 @@ def delete(Bucket, MFA=None, RequestPayer=None, Force=False, salt myminion boto_s3_bucket.delete mybucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if Force: - empty(Bucket, MFA=MFA, RequestPayer=RequestPayer, region=region, - key=key, keyid=keyid, profile=profile) + empty( + Bucket, + MFA=MFA, + RequestPayer=RequestPayer, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) conn.delete_bucket(Bucket=Bucket) - return {'deleted': True} + return {"deleted": True} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def delete_objects(Bucket, Delete, MFA=None, RequestPayer=None, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_objects( + Bucket, + Delete, + MFA=None, + RequestPayer=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Delete objects in a given S3 bucket. Returns {deleted: true} if all objects were deleted @@ -218,40 +258,39 @@ def delete_objects(Bucket, Delete, MFA=None, RequestPayer=None, salt myminion boto_s3_bucket.delete_objects mybucket '{Objects: [Key: myobject]}' - ''' + """ if isinstance(Delete, six.string_types): Delete = salt.utils.json.loads(Delete) if not isinstance(Delete, dict): raise SaltInvocationError("Malformed Delete request.") - if 'Objects' not in Delete: + if "Objects" not in Delete: raise SaltInvocationError("Malformed Delete request.") failed = [] - objs = Delete['Objects'] + objs = Delete["Objects"] for i in range(0, len(objs), 1000): - chunk = objs[i:i+1000] - subset = {'Objects': chunk, 'Quiet': True} + chunk = objs[i : i + 1000] + subset = {"Objects": chunk, "Quiet": True} try: - args = {'Bucket': Bucket} - args.update({'MFA': MFA}) if MFA else None - args.update({'RequestPayer': RequestPayer}) if RequestPayer else None - args.update({'Delete': subset}) + args = {"Bucket": Bucket} + args.update({"MFA": MFA}) if MFA else None + args.update({"RequestPayer": RequestPayer}) if RequestPayer else None + args.update({"Delete": subset}) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) ret = conn.delete_objects(**args) - failed += ret.get('Errors', []) + failed += ret.get("Errors", []) except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} if failed: - return {'deleted': False, 'failed': failed} + return {"deleted": False, "failed": failed} else: - return {'deleted': True} + return {"deleted": True} -def describe(Bucket, - region=None, key=None, keyid=None, profile=None): - ''' +def describe(Bucket, region=None, key=None, keyid=None, profile=None): + """ Given a bucket name describe its properties. Returns a dictionary of interesting properties. @@ -262,62 +301,65 @@ def describe(Bucket, salt myminion boto_s3_bucket.describe mybucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) result = {} - conn_dict = {'ACL': conn.get_bucket_acl, - 'CORS': conn.get_bucket_cors, - 'LifecycleConfiguration': conn.get_bucket_lifecycle_configuration, - 'Location': conn.get_bucket_location, - 'Logging': conn.get_bucket_logging, - 'NotificationConfiguration': conn.get_bucket_notification_configuration, - 'Policy': conn.get_bucket_policy, - 'Replication': conn.get_bucket_replication, - 'RequestPayment': conn.get_bucket_request_payment, - 'Versioning': conn.get_bucket_versioning, - 'Website': conn.get_bucket_website} + conn_dict = { + "ACL": conn.get_bucket_acl, + "CORS": conn.get_bucket_cors, + "LifecycleConfiguration": conn.get_bucket_lifecycle_configuration, + "Location": conn.get_bucket_location, + "Logging": conn.get_bucket_logging, + "NotificationConfiguration": conn.get_bucket_notification_configuration, + "Policy": conn.get_bucket_policy, + "Replication": conn.get_bucket_replication, + "RequestPayment": conn.get_bucket_request_payment, + "Versioning": conn.get_bucket_versioning, + "Website": conn.get_bucket_website, + } for key, query in six.iteritems(conn_dict): try: data = query(Bucket=Bucket) except ClientError as e: - if e.response.get('Error', {}).get('Code') in ( - 'NoSuchLifecycleConfiguration', - 'NoSuchCORSConfiguration', - 'NoSuchBucketPolicy', - 'NoSuchWebsiteConfiguration', - 'ReplicationConfigurationNotFoundError', - 'NoSuchTagSet', - ): + if e.response.get("Error", {}).get("Code") in ( + "NoSuchLifecycleConfiguration", + "NoSuchCORSConfiguration", + "NoSuchBucketPolicy", + "NoSuchWebsiteConfiguration", + "ReplicationConfigurationNotFoundError", + "NoSuchTagSet", + ): continue raise - if 'ResponseMetadata' in data: - del data['ResponseMetadata'] + if "ResponseMetadata" in data: + del data["ResponseMetadata"] result[key] = data tags = {} try: data = conn.get_bucket_tagging(Bucket=Bucket) - for tagdef in data.get('TagSet'): - tags[tagdef.get('Key')] = tagdef.get('Value') + for tagdef in data.get("TagSet"): + tags[tagdef.get("Key")] = tagdef.get("Value") except ClientError as e: - if not e.response.get('Error', {}).get('Code') == 'NoSuchTagSet': + if not e.response.get("Error", {}).get("Code") == "NoSuchTagSet": raise if tags: - result['Tagging'] = tags - return {'bucket': result} + result["Tagging"] = tags + return {"bucket": result} except ClientError as e: - err = __utils__['boto3.get_error'](e) - if e.response.get('Error', {}).get('Code') == 'NoSuchBucket': - return {'bucket': None} - return {'error': __utils__['boto3.get_error'](e)} + err = __utils__["boto3.get_error"](e) + if e.response.get("Error", {}).get("Code") == "NoSuchBucket": + return {"bucket": None} + return {"error": __utils__["boto3.get_error"](e)} -def empty(Bucket, MFA=None, RequestPayer=None, region=None, key=None, - keyid=None, profile=None): - ''' +def empty( + Bucket, MFA=None, RequestPayer=None, region=None, key=None, keyid=None, profile=None +): + """ Delete all objects in a given S3 bucket. Returns {deleted: true} if all objects were deleted @@ -329,24 +371,39 @@ def empty(Bucket, MFA=None, RequestPayer=None, region=None, key=None, salt myminion boto_s3_bucket.empty mybucket - ''' + """ - stuff = list_object_versions(Bucket, region=region, key=key, keyid=keyid, - profile=profile) + stuff = list_object_versions( + Bucket, region=region, key=key, keyid=keyid, profile=profile + ) Delete = {} - Delete['Objects'] = [{'Key': v['Key'], 'VersionId': v['VersionId']} for v in stuff.get('Versions', [])] - Delete['Objects'] += [{'Key': v['Key'], 'VersionId': v['VersionId']} for v in stuff.get('DeleteMarkers', [])] - if Delete['Objects']: - ret = delete_objects(Bucket, Delete, MFA=MFA, RequestPayer=RequestPayer, - region=region, key=key, keyid=keyid, profile=profile) - failed = ret.get('failed', []) + Delete["Objects"] = [ + {"Key": v["Key"], "VersionId": v["VersionId"]} + for v in stuff.get("Versions", []) + ] + Delete["Objects"] += [ + {"Key": v["Key"], "VersionId": v["VersionId"]} + for v in stuff.get("DeleteMarkers", []) + ] + if Delete["Objects"]: + ret = delete_objects( + Bucket, + Delete, + MFA=MFA, + RequestPayer=RequestPayer, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + failed = ret.get("failed", []) if failed: - return {'deleted': False, 'failed': ret[failed]} - return {'deleted': True} + return {"deleted": False, "failed": ret[failed]} + return {"deleted": True} def list(region=None, key=None, keyid=None, profile=None): - ''' + """ List all buckets owned by the authenticated sender of the request. Returns list of buckets @@ -360,22 +417,30 @@ def list(region=None, key=None, keyid=None, profile=None): - {...} - {...} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) buckets = conn.list_buckets() - if not bool(buckets.get('Buckets')): - log.warning('No buckets found') - if 'ResponseMetadata' in buckets: - del buckets['ResponseMetadata'] + if not bool(buckets.get("Buckets")): + log.warning("No buckets found") + if "ResponseMetadata" in buckets: + del buckets["ResponseMetadata"] return buckets except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def list_object_versions(Bucket, Delimiter=None, EncodingType=None, Prefix=None, - region=None, key=None, keyid=None, profile=None): - ''' +def list_object_versions( + Bucket, + Delimiter=None, + EncodingType=None, + Prefix=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ List objects in a given S3 bucket. Returns a list of objects. @@ -386,34 +451,43 @@ def list_object_versions(Bucket, Delimiter=None, EncodingType=None, Prefix=None, salt myminion boto_s3_bucket.list_object_versions mybucket - ''' + """ try: Versions = [] DeleteMarkers = [] - args = {'Bucket': Bucket} - args.update({'Delimiter': Delimiter}) if Delimiter else None - args.update({'EncodingType': EncodingType}) if Delimiter else None - args.update({'Prefix': Prefix}) if Prefix else None + args = {"Bucket": Bucket} + args.update({"Delimiter": Delimiter}) if Delimiter else None + args.update({"EncodingType": EncodingType}) if Delimiter else None + args.update({"Prefix": Prefix}) if Prefix else None conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) IsTruncated = True while IsTruncated: ret = conn.list_object_versions(**args) - IsTruncated = ret.get('IsTruncated', False) - if IsTruncated in ('True', 'true', True): - args['KeyMarker'] = ret['NextKeyMarker'] - args['VersionIdMarker'] = ret['NextVersionIdMarker'] - Versions += ret.get('Versions', []) - DeleteMarkers += ret.get('DeleteMarkers', []) - return {'Versions': Versions, 'DeleteMarkers': DeleteMarkers} + IsTruncated = ret.get("IsTruncated", False) + if IsTruncated in ("True", "true", True): + args["KeyMarker"] = ret["NextKeyMarker"] + args["VersionIdMarker"] = ret["NextVersionIdMarker"] + Versions += ret.get("Versions", []) + DeleteMarkers += ret.get("DeleteMarkers", []) + return {"Versions": Versions, "DeleteMarkers": DeleteMarkers} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def list_objects(Bucket, Delimiter=None, EncodingType=None, Prefix=None, - FetchOwner=False, StartAfter=None, region=None, key=None, - keyid=None, profile=None): - ''' +def list_objects( + Bucket, + Delimiter=None, + EncodingType=None, + Prefix=None, + FetchOwner=False, + StartAfter=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ List objects in a given S3 bucket. Returns a list of objects. @@ -424,38 +498,43 @@ def list_objects(Bucket, Delimiter=None, EncodingType=None, Prefix=None, salt myminion boto_s3_bucket.list_objects mybucket - ''' + """ try: Contents = [] - args = {'Bucket': Bucket, 'FetchOwner': FetchOwner} - args.update({'Delimiter': Delimiter}) if Delimiter else None - args.update({'EncodingType': EncodingType}) if Delimiter else None - args.update({'Prefix': Prefix}) if Prefix else None - args.update({'StartAfter': StartAfter}) if StartAfter else None + args = {"Bucket": Bucket, "FetchOwner": FetchOwner} + args.update({"Delimiter": Delimiter}) if Delimiter else None + args.update({"EncodingType": EncodingType}) if Delimiter else None + args.update({"Prefix": Prefix}) if Prefix else None + args.update({"StartAfter": StartAfter}) if StartAfter else None conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) IsTruncated = True while IsTruncated: ret = conn.list_objects_v2(**args) - IsTruncated = ret.get('IsTruncated', False) - if IsTruncated in ('True', 'true', True): - args['ContinuationToken'] = ret['NextContinuationToken'] - Contents += ret.get('Contents', []) - return {'Contents': Contents} + IsTruncated = ret.get("IsTruncated", False) + if IsTruncated in ("True", "true", True): + args["ContinuationToken"] = ret["NextContinuationToken"] + Contents += ret.get("Contents", []) + return {"Contents": Contents} except ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} -def put_acl(Bucket, - ACL=None, - AccessControlPolicy=None, - GrantFullControl=None, - GrantRead=None, - GrantReadACP=None, - GrantWrite=None, - GrantWriteACP=None, - region=None, key=None, keyid=None, profile=None): - ''' +def put_acl( + Bucket, + ACL=None, + AccessControlPolicy=None, + GrantFullControl=None, + GrantRead=None, + GrantReadACP=None, + GrantWrite=None, + GrantWriteACP=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, update the ACL for a bucket. Returns {updated: true} if the ACL was updated and returns @@ -470,7 +549,7 @@ def put_acl(Bucket, GrantRead='uri="http://acs.amazonaws.com/groups/global/AllUsers"' \\ GrantReadACP='emailaddress="exampl@example.com",id="2345678909876432"' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -478,23 +557,27 @@ def put_acl(Bucket, if AccessControlPolicy is not None: if isinstance(AccessControlPolicy, six.string_types): AccessControlPolicy = salt.utils.json.loads(AccessControlPolicy) - kwargs['AccessControlPolicy'] = AccessControlPolicy - for arg in ('ACL', - 'GrantFullControl', - 'GrantRead', 'GrantReadACP', - 'GrantWrite', 'GrantWriteACP'): + kwargs["AccessControlPolicy"] = AccessControlPolicy + for arg in ( + "ACL", + "GrantFullControl", + "GrantRead", + "GrantReadACP", + "GrantWrite", + "GrantWriteACP", + ): if locals()[arg] is not None: - kwargs[arg] = str(locals()[arg]) # future lint: disable=blacklisted-function + kwargs[arg] = str( + locals()[arg] + ) # future lint: disable=blacklisted-function conn.put_bucket_acl(Bucket=Bucket, **kwargs) - return {'updated': True, 'name': Bucket} + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def put_cors(Bucket, - CORSRules, - region=None, key=None, keyid=None, profile=None): - ''' +def put_cors(Bucket, CORSRules, region=None, key=None, keyid=None, profile=None): + """ Given a valid config, update the CORS rules for a bucket. Returns {updated: true} if CORS was updated and returns @@ -512,22 +595,22 @@ def put_cors(Bucket, "MaxAgeSeconds":123,\\ }]' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if CORSRules is not None and isinstance(CORSRules, six.string_types): CORSRules = salt.utils.json.loads(CORSRules) - conn.put_bucket_cors(Bucket=Bucket, CORSConfiguration={'CORSRules': CORSRules}) - return {'updated': True, 'name': Bucket} + conn.put_bucket_cors(Bucket=Bucket, CORSConfiguration={"CORSRules": CORSRules}) + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def put_lifecycle_configuration(Bucket, - Rules, - region=None, key=None, keyid=None, profile=None): - ''' +def put_lifecycle_configuration( + Bucket, Rules, region=None, key=None, keyid=None, profile=None +): + """ Given a valid config, update the Lifecycle rules for a bucket. Returns {updated: true} if Lifecycle was updated and returns @@ -547,22 +630,31 @@ def put_lifecycle_configuration(Bucket, "NoncurrentVersionExpiration": {...},\\ }]' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if Rules is not None and isinstance(Rules, six.string_types): Rules = salt.utils.json.loads(Rules) - conn.put_bucket_lifecycle_configuration(Bucket=Bucket, LifecycleConfiguration={'Rules': Rules}) - return {'updated': True, 'name': Bucket} + conn.put_bucket_lifecycle_configuration( + Bucket=Bucket, LifecycleConfiguration={"Rules": Rules} + ) + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def put_logging(Bucket, - TargetBucket=None, TargetPrefix=None, TargetGrants=None, - region=None, key=None, keyid=None, profile=None): - ''' +def put_logging( + Bucket, + TargetBucket=None, + TargetPrefix=None, + TargetGrants=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, update the logging parameters for a bucket. Returns {updated: true} if parameters were updated and returns @@ -574,34 +666,42 @@ def put_logging(Bucket, salt myminion boto_s3_bucket.put_logging my_bucket log_bucket '[{...}]' prefix - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) logstate = {} - targets = {'TargetBucket': TargetBucket, - 'TargetGrants': TargetGrants, - 'TargetPrefix': TargetPrefix} + targets = { + "TargetBucket": TargetBucket, + "TargetGrants": TargetGrants, + "TargetPrefix": TargetPrefix, + } for key, val in six.iteritems(targets): if val is not None: logstate[key] = val if logstate: - logstatus = {'LoggingEnabled': logstate} + logstatus = {"LoggingEnabled": logstate} else: logstatus = {} if TargetGrants is not None and isinstance(TargetGrants, six.string_types): TargetGrants = salt.utils.json.loads(TargetGrants) conn.put_bucket_logging(Bucket=Bucket, BucketLoggingStatus=logstatus) - return {'updated': True, 'name': Bucket} + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def put_notification_configuration(Bucket, - TopicConfigurations=None, QueueConfigurations=None, - LambdaFunctionConfigurations=None, - region=None, key=None, keyid=None, profile=None): - ''' +def put_notification_configuration( + Bucket, + TopicConfigurations=None, + QueueConfigurations=None, + LambdaFunctionConfigurations=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, update the notification parameters for a bucket. Returns {updated: true} if parameters were updated and returns @@ -616,7 +716,7 @@ def put_notification_configuration(Bucket, [{...}] \\ [{...}] - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) @@ -631,21 +731,25 @@ def put_notification_configuration(Bucket, if LambdaFunctionConfigurations is None: LambdaFunctionConfigurations = [] elif isinstance(LambdaFunctionConfigurations, six.string_types): - LambdaFunctionConfigurations = salt.utils.json.loads(LambdaFunctionConfigurations) + LambdaFunctionConfigurations = salt.utils.json.loads( + LambdaFunctionConfigurations + ) # TODO allow the user to use simple names & substitute ARNs for those names - conn.put_bucket_notification_configuration(Bucket=Bucket, NotificationConfiguration={ - 'TopicConfigurations': TopicConfigurations, - 'QueueConfigurations': QueueConfigurations, - 'LambdaFunctionConfigurations': LambdaFunctionConfigurations, - }) - return {'updated': True, 'name': Bucket} + conn.put_bucket_notification_configuration( + Bucket=Bucket, + NotificationConfiguration={ + "TopicConfigurations": TopicConfigurations, + "QueueConfigurations": QueueConfigurations, + "LambdaFunctionConfigurations": LambdaFunctionConfigurations, + }, + ) + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def put_policy(Bucket, Policy, - region=None, key=None, keyid=None, profile=None): - ''' +def put_policy(Bucket, Policy, region=None, key=None, keyid=None, profile=None): + """ Given a valid config, update the policy for a bucket. Returns {updated: true} if policy was updated and returns @@ -657,37 +761,38 @@ def put_policy(Bucket, Policy, salt myminion boto_s3_bucket.put_policy my_bucket {...} - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if Policy is None: - Policy = '{}' + Policy = "{}" elif not isinstance(Policy, six.string_types): Policy = salt.utils.json.dumps(Policy) conn.put_bucket_policy(Bucket=Bucket, Policy=Policy) - return {'updated': True, 'name': Bucket} + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} def _get_role_arn(name, region=None, key=None, keyid=None, profile=None): - if name.startswith('arn:aws:iam:'): + if name.startswith("arn:aws:iam:"): return name - account_id = __salt__['boto_iam.get_account_id']( + account_id = __salt__["boto_iam.get_account_id"]( region=region, key=key, keyid=keyid, profile=profile ) - if profile and 'region' in profile: - region = profile['region'] + if profile and "region" in profile: + region = profile["region"] if region is None: - region = 'us-east-1' - return 'arn:aws:iam::{0}:role/{1}'.format(account_id, name) + region = "us-east-1" + return "arn:aws:iam::{0}:role/{1}".format(account_id, name) -def put_replication(Bucket, Role, Rules, - region=None, key=None, keyid=None, profile=None): - ''' +def put_replication( + Bucket, Role, Rules, region=None, key=None, keyid=None, profile=None +): + """ Given a valid config, update the replication configuration for a bucket. Returns {updated: true} if replication configuration was updated and returns @@ -699,28 +804,27 @@ def put_replication(Bucket, Role, Rules, salt myminion boto_s3_bucket.put_replication my_bucket my_role [...] - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - Role = _get_role_arn(name=Role, - region=region, key=key, keyid=keyid, profile=profile) + Role = _get_role_arn( + name=Role, region=region, key=key, keyid=keyid, profile=profile + ) if Rules is None: Rules = [] elif isinstance(Rules, six.string_types): Rules = salt.utils.json.loads(Rules) - conn.put_bucket_replication(Bucket=Bucket, ReplicationConfiguration={ - 'Role': Role, - 'Rules': Rules - }) - return {'updated': True, 'name': Bucket} + conn.put_bucket_replication( + Bucket=Bucket, ReplicationConfiguration={"Role": Role, "Rules": Rules} + ) + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def put_request_payment(Bucket, Payer, - region=None, key=None, keyid=None, profile=None): - ''' +def put_request_payment(Bucket, Payer, region=None, key=None, keyid=None, profile=None): + """ Given a valid config, update the request payment configuration for a bucket. Returns {updated: true} if request payment configuration was updated and returns @@ -732,21 +836,20 @@ def put_request_payment(Bucket, Payer, salt myminion boto_s3_bucket.put_request_payment my_bucket Requester - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.put_bucket_request_payment(Bucket=Bucket, RequestPaymentConfiguration={ - 'Payer': Payer, - }) - return {'updated': True, 'name': Bucket} + conn.put_bucket_request_payment( + Bucket=Bucket, RequestPaymentConfiguration={"Payer": Payer} + ) + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def put_tagging(Bucket, - region=None, key=None, keyid=None, profile=None, **kwargs): - ''' +def put_tagging(Bucket, region=None, key=None, keyid=None, profile=None, **kwargs): + """ Given a valid config, update the tags for a bucket. Returns {updated: true} if tags were updated and returns @@ -758,26 +861,32 @@ def put_tagging(Bucket, salt myminion boto_s3_bucket.put_tagging my_bucket my_role [...] - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) tagslist = [] for k, v in six.iteritems(kwargs): - if six.text_type(k).startswith('__'): + if six.text_type(k).startswith("__"): continue - tagslist.append({'Key': six.text_type(k), 'Value': six.text_type(v)}) - conn.put_bucket_tagging(Bucket=Bucket, Tagging={ - 'TagSet': tagslist, - }) - return {'updated': True, 'name': Bucket} + tagslist.append({"Key": six.text_type(k), "Value": six.text_type(v)}) + conn.put_bucket_tagging(Bucket=Bucket, Tagging={"TagSet": tagslist}) + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def put_versioning(Bucket, Status, MFADelete=None, MFA=None, - region=None, key=None, keyid=None, profile=None): - ''' +def put_versioning( + Bucket, + Status, + MFADelete=None, + MFA=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, update the versioning configuration for a bucket. Returns {updated: true} if versioning configuration was updated and returns @@ -789,28 +898,36 @@ def put_versioning(Bucket, Status, MFADelete=None, MFA=None, salt myminion boto_s3_bucket.put_versioning my_bucket Enabled - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - VersioningConfiguration = {'Status': Status} + VersioningConfiguration = {"Status": Status} if MFADelete is not None: - VersioningConfiguration['MFADelete'] = MFADelete + VersioningConfiguration["MFADelete"] = MFADelete kwargs = {} if MFA is not None: - kwargs['MFA'] = MFA - conn.put_bucket_versioning(Bucket=Bucket, - VersioningConfiguration=VersioningConfiguration, - **kwargs) - return {'updated': True, 'name': Bucket} + kwargs["MFA"] = MFA + conn.put_bucket_versioning( + Bucket=Bucket, VersioningConfiguration=VersioningConfiguration, **kwargs + ) + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def put_website(Bucket, ErrorDocument=None, IndexDocument=None, - RedirectAllRequestsTo=None, RoutingRules=None, - region=None, key=None, keyid=None, profile=None): - ''' +def put_website( + Bucket, + ErrorDocument=None, + IndexDocument=None, + RedirectAllRequestsTo=None, + RoutingRules=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid config, update the website configuration for a bucket. Returns {updated: true} if website configuration was updated and returns @@ -822,29 +939,33 @@ def put_website(Bucket, ErrorDocument=None, IndexDocument=None, salt myminion boto_s3_bucket.put_website my_bucket IndexDocument='{"Suffix":"index.html"}' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) WebsiteConfiguration = {} - for key in ('ErrorDocument', 'IndexDocument', - 'RedirectAllRequestsTo', 'RoutingRules'): + for key in ( + "ErrorDocument", + "IndexDocument", + "RedirectAllRequestsTo", + "RoutingRules", + ): val = locals()[key] if val is not None: if isinstance(val, six.string_types): WebsiteConfiguration[key] = salt.utils.json.loads(val) else: WebsiteConfiguration[key] = val - conn.put_bucket_website(Bucket=Bucket, - WebsiteConfiguration=WebsiteConfiguration) - return {'updated': True, 'name': Bucket} + conn.put_bucket_website( + Bucket=Bucket, WebsiteConfiguration=WebsiteConfiguration + ) + return {"updated": True, "name": Bucket} except ClientError as e: - return {'updated': False, 'error': __utils__['boto3.get_error'](e)} + return {"updated": False, "error": __utils__["boto3.get_error"](e)} -def delete_cors(Bucket, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_cors(Bucket, region=None, key=None, keyid=None, profile=None): + """ Delete the CORS configuration for the given bucket Returns {deleted: true} if CORS was deleted and returns @@ -856,19 +977,20 @@ def delete_cors(Bucket, salt myminion boto_s3_bucket.delete_cors my_bucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_bucket_cors(Bucket=Bucket) - return {'deleted': True, 'name': Bucket} + return {"deleted": True, "name": Bucket} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def delete_lifecycle_configuration(Bucket, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_lifecycle_configuration( + Bucket, region=None, key=None, keyid=None, profile=None +): + """ Delete the lifecycle configuration for the given bucket Returns {deleted: true} if Lifecycle was deleted and returns @@ -880,19 +1002,18 @@ def delete_lifecycle_configuration(Bucket, salt myminion boto_s3_bucket.delete_lifecycle_configuration my_bucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_bucket_lifecycle(Bucket=Bucket) - return {'deleted': True, 'name': Bucket} + return {"deleted": True, "name": Bucket} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def delete_policy(Bucket, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_policy(Bucket, region=None, key=None, keyid=None, profile=None): + """ Delete the policy from the given bucket Returns {deleted: true} if policy was deleted and returns @@ -904,19 +1025,18 @@ def delete_policy(Bucket, salt myminion boto_s3_bucket.delete_policy my_bucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_bucket_policy(Bucket=Bucket) - return {'deleted': True, 'name': Bucket} + return {"deleted": True, "name": Bucket} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def delete_replication(Bucket, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_replication(Bucket, region=None, key=None, keyid=None, profile=None): + """ Delete the replication config from the given bucket Returns {deleted: true} if replication configuration was deleted and returns @@ -928,19 +1048,18 @@ def delete_replication(Bucket, salt myminion boto_s3_bucket.delete_replication my_bucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_bucket_replication(Bucket=Bucket) - return {'deleted': True, 'name': Bucket} + return {"deleted": True, "name": Bucket} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def delete_tagging(Bucket, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_tagging(Bucket, region=None, key=None, keyid=None, profile=None): + """ Delete the tags from the given bucket Returns {deleted: true} if tags were deleted and returns @@ -952,19 +1071,18 @@ def delete_tagging(Bucket, salt myminion boto_s3_bucket.delete_tagging my_bucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_bucket_tagging(Bucket=Bucket) - return {'deleted': True, 'name': Bucket} + return {"deleted": True, "name": Bucket} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} -def delete_website(Bucket, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_website(Bucket, region=None, key=None, keyid=None, profile=None): + """ Remove the website configuration from the given bucket Returns {deleted: true} if website configuration was deleted and returns @@ -976,11 +1094,11 @@ def delete_website(Bucket, salt myminion boto_s3_bucket.delete_website my_bucket - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_bucket_website(Bucket=Bucket) - return {'deleted': True, 'name': Bucket} + return {"deleted": True, "name": Bucket} except ClientError as e: - return {'deleted': False, 'error': __utils__['boto3.get_error'](e)} + return {"deleted": False, "error": __utils__["boto3.get_error"](e)} diff --git a/salt/modules/boto_secgroup.py b/salt/modules/boto_secgroup.py index 16f6bb79cbe..cff5a265be7 100644 --- a/salt/modules/boto_secgroup.py +++ b/salt/modules/boto_secgroup.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Security Groups .. versionadded:: 2014.7.0 @@ -40,127 +40,166 @@ Connection module for Amazon Security Groups region: us-east-1 :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging -# Import Salt libs -from salt.exceptions import CommandExecutionError, SaltInvocationError import salt.utils.odict as odict import salt.utils.versions -log = logging.getLogger(__name__) +# Import Salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import third party libs from salt.ext import six + +log = logging.getLogger(__name__) + + try: # pylint: disable=unused-import import boto import boto.ec2 + # pylint: enable=unused-import - logging.getLogger('boto').setLevel(logging.CRITICAL) + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # Boto < 2.4.0 GroupOrCIDR objects have different attributes than # Boto >= 2.4.0 GroupOrCIDR objects # Differences include no group_id attribute in Boto < 2.4.0 and returning # a groupId attribute when a GroupOrCIDR object authorizes an IP range # Support for Boto < 2.4.0 can be added if needed has_boto_reqs = salt.utils.versions.check_boto_reqs( - boto_ver='2.4.0', - check_boto3=False + boto_ver="2.4.0", check_boto3=False ) if has_boto_reqs is True: - __utils__['boto.assign_funcs'](__name__, 'ec2', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "ec2", pack=__salt__) return has_boto_reqs -def exists(name=None, region=None, key=None, keyid=None, profile=None, - vpc_id=None, vpc_name=None, group_id=None): - ''' +def exists( + name=None, + region=None, + key=None, + keyid=None, + profile=None, + vpc_id=None, + vpc_name=None, + group_id=None, +): + """ Check to see if a security group exists. CLI example:: salt myminion boto_secgroup.exists mysecgroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - group = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name, - group_id=group_id, region=region, key=key, keyid=keyid, - profile=profile) + group = _get_group( + conn, + name=name, + vpc_id=vpc_id, + vpc_name=vpc_name, + group_id=group_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if group: return True else: return False -def _vpc_name_to_id(vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, - profile=None): - data = __salt__['boto_vpc.get_id'](name=vpc_name, region=region, - key=key, keyid=keyid, profile=profile) - return data.get('id') +def _vpc_name_to_id( + vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None +): + data = __salt__["boto_vpc.get_id"]( + name=vpc_name, region=region, key=key, keyid=keyid, profile=profile + ) + return data.get("id") def _split_rules(rules): - ''' + """ Split rules with combined grants into individual rules. Amazon returns a set of rules with the same protocol, from and to ports together as a single rule with a set of grants. Authorizing and revoking rules, however, is done as a split set of rules. This function splits the rules up. - ''' + """ split = [] for rule in rules: - ip_protocol = rule.get('ip_protocol') - to_port = rule.get('to_port') - from_port = rule.get('from_port') - grants = rule.get('grants') + ip_protocol = rule.get("ip_protocol") + to_port = rule.get("to_port") + from_port = rule.get("from_port") + grants = rule.get("grants") for grant in grants: - _rule = {'ip_protocol': ip_protocol, - 'to_port': to_port, - 'from_port': from_port} + _rule = { + "ip_protocol": ip_protocol, + "to_port": to_port, + "from_port": from_port, + } for key, val in six.iteritems(grant): _rule[key] = val split.append(_rule) return split -def _get_group(conn=None, name=None, vpc_id=None, vpc_name=None, group_id=None, - region=None, key=None, keyid=None, profile=None): # pylint: disable=W0613 - ''' +def _get_group( + conn=None, + name=None, + vpc_id=None, + vpc_name=None, + group_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): # pylint: disable=W0613 + """ Get a group object given a name, name and vpc_id/vpc_name or group_id. Return a boto.ec2.securitygroup.SecurityGroup object if the group is found, else return None. - ''' + """ if vpc_name and vpc_id: - raise SaltInvocationError('The params \'vpc_id\' and \'vpc_name\' ' - 'are mutually exclusive.') + raise SaltInvocationError( + "The params 'vpc_id' and 'vpc_name' " "are mutually exclusive." + ) if vpc_name: try: - vpc_id = _vpc_name_to_id(vpc_id=vpc_id, vpc_name=vpc_name, region=region, - key=key, keyid=keyid, profile=profile) + vpc_id = _vpc_name_to_id( + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) except boto.exception.BotoServerError as e: log.debug(e) return None if name: if vpc_id is None: - log.debug('getting group for %s', name) - group_filter = {'group-name': name} + log.debug("getting group for %s", name) + group_filter = {"group-name": name} filtered_groups = conn.get_all_security_groups(filters=group_filter) # security groups can have the same name if groups exist in both # EC2-Classic and EC2-VPC @@ -172,13 +211,15 @@ def _get_group(conn=None, name=None, vpc_id=None, vpc_name=None, group_id=None, return group # If there are more security groups, and no vpc_id, we can't know which one to choose. if len(filtered_groups) > 1: - raise CommandExecutionError('Security group belongs to more VPCs, specify the VPC ID!') + raise CommandExecutionError( + "Security group belongs to more VPCs, specify the VPC ID!" + ) elif len(filtered_groups) == 1: return filtered_groups[0] return None elif vpc_id: - log.debug('getting group for %s in vpc_id %s', name, vpc_id) - group_filter = {'group-name': name, 'vpc_id': vpc_id} + log.debug("getting group for %s in vpc_id %s", name, vpc_id) + group_filter = {"group-name": name, "vpc_id": vpc_id} filtered_groups = conn.get_all_security_groups(filters=group_filter) if len(filtered_groups) == 1: return filtered_groups[0] @@ -203,21 +244,23 @@ def _get_group(conn=None, name=None, vpc_id=None, vpc_name=None, group_id=None, def _parse_rules(sg, rules): _rules = [] for rule in rules: - log.debug('examining rule %s for group %s', rule, sg.id) - attrs = ['ip_protocol', 'from_port', 'to_port', 'grants'] + log.debug("examining rule %s for group %s", rule, sg.id) + attrs = ["ip_protocol", "from_port", "to_port", "grants"] _rule = odict.OrderedDict() for attr in attrs: val = getattr(rule, attr) if not val: continue - if attr == 'grants': + if attr == "grants": _grants = [] for grant in val: - log.debug('examining grant %s for', grant) - g_attrs = {'name': 'source_group_name', - 'owner_id': 'source_group_owner_id', - 'group_id': 'source_group_group_id', - 'cidr_ip': 'cidr_ip'} + log.debug("examining grant %s for", grant) + g_attrs = { + "name": "source_group_name", + "owner_id": "source_group_owner_id", + "group_id": "source_group_group_id", + "cidr_ip": "cidr_ip", + } _grant = odict.OrderedDict() for g_attr, g_attr_map in six.iteritems(g_attrs): g_val = getattr(grant, g_attr) @@ -225,10 +268,10 @@ def _parse_rules(sg, rules): continue _grant[g_attr_map] = g_val _grants.append(_grant) - _rule['grants'] = _grants - elif attr == 'from_port': + _rule["grants"] = _grants + elif attr == "from_port": _rule[attr] = int(val) - elif attr == 'to_port': + elif attr == "to_port": _rule[attr] = int(val) else: _rule[attr] = val @@ -236,9 +279,16 @@ def _parse_rules(sg, rules): return _rules -def get_all_security_groups(groupnames=None, group_ids=None, filters=None, - region=None, key=None, keyid=None, profile=None): - ''' +def get_all_security_groups( + groupnames=None, + group_ids=None, + filters=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Return a list of all Security Groups matching the given criteria and filters. @@ -253,7 +303,7 @@ def get_all_security_groups(groupnames=None, group_ids=None, filters=None, CLI example:: salt myminion boto_secgroup.get_all_security_groups filters='{group-name: mygroup}' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if isinstance(groupnames, six.string_types): @@ -261,22 +311,32 @@ def get_all_security_groups(groupnames=None, group_ids=None, filters=None, if isinstance(group_ids, six.string_types): groupnames = [group_ids] - interesting = ['description', 'id', 'instances', 'name', 'owner_id', - 'region', 'rules', 'rules_egress', 'tags', 'vpc_id'] + interesting = [ + "description", + "id", + "instances", + "name", + "owner_id", + "region", + "rules", + "rules_egress", + "tags", + "vpc_id", + ] ret = [] try: - r = conn.get_all_security_groups(groupnames=groupnames, - group_ids=group_ids, - filters=filters) + r = conn.get_all_security_groups( + groupnames=groupnames, group_ids=group_ids, filters=filters + ) for g in r: n = {} for a in interesting: v = getattr(g, a, None) - if a == 'region': + if a == "region": v = v.name - elif a in ('rules', 'rules_egress'): + elif a in ("rules", "rules_egress"): v = _parse_rules(g, v) - elif a == 'instances': + elif a == "instances": v = [i.id for i in v()] n[a] = v ret += [n] @@ -286,156 +346,241 @@ def get_all_security_groups(groupnames=None, group_ids=None, filters=None, return [] -def get_group_id(name, vpc_id=None, vpc_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def get_group_id( + name, vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None +): + """ Get a Group ID given a Group Name or Group Name and VPC ID CLI example:: salt myminion boto_secgroup.get_group_id mysecgroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if name.startswith('sg-'): - log.debug('group %s is a group id. get_group_id not called.', name) + if name.startswith("sg-"): + log.debug("group %s is a group id. get_group_id not called.", name) return name - group = _get_group(conn=conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name, - region=region, key=key, keyid=keyid, profile=profile) - return getattr(group, 'id', None) + group = _get_group( + conn=conn, + name=name, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + return getattr(group, "id", None) -def convert_to_group_ids(groups, vpc_id=None, vpc_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def convert_to_group_ids( + groups, vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None +): + """ Given a list of security groups and a vpc_id, convert_to_group_ids will convert all list items in the given list to security group ids. CLI example:: salt myminion boto_secgroup.convert_to_group_ids mysecgroup vpc-89yhh7h - ''' - log.debug('security group contents %s pre-conversion', groups) + """ + log.debug("security group contents %s pre-conversion", groups) group_ids = [] for group in groups: - group_id = get_group_id(name=group, vpc_id=vpc_id, - vpc_name=vpc_name, region=region, - key=key, keyid=keyid, profile=profile) + group_id = get_group_id( + name=group, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not group_id: # Security groups are a big deal - need to fail if any can't be resolved... - raise CommandExecutionError('Could not resolve Security Group name ' - '{0} to a Group ID'.format(group)) + raise CommandExecutionError( + "Could not resolve Security Group name " + "{0} to a Group ID".format(group) + ) else: group_ids.append(six.text_type(group_id)) - log.debug('security group contents %s post-conversion', group_ids) + log.debug("security group contents %s post-conversion", group_ids) return group_ids -def get_config(name=None, group_id=None, region=None, key=None, keyid=None, - profile=None, vpc_id=None, vpc_name=None): - ''' +def get_config( + name=None, + group_id=None, + region=None, + key=None, + keyid=None, + profile=None, + vpc_id=None, + vpc_name=None, +): + """ Get the configuration for a security group. CLI example:: salt myminion boto_secgroup.get_config mysecgroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - sg = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name, - group_id=group_id, region=region, key=key, keyid=keyid, - profile=profile) + sg = _get_group( + conn, + name=name, + vpc_id=vpc_id, + vpc_name=vpc_name, + group_id=group_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if sg: ret = odict.OrderedDict() - ret['name'] = sg.name + ret["name"] = sg.name # TODO: add support for vpc_id in return # ret['vpc_id'] = sg.vpc_id - ret['group_id'] = sg.id - ret['owner_id'] = sg.owner_id - ret['description'] = sg.description - ret['tags'] = sg.tags + ret["group_id"] = sg.id + ret["owner_id"] = sg.owner_id + ret["description"] = sg.description + ret["tags"] = sg.tags _rules = _parse_rules(sg, sg.rules) _rules_egress = _parse_rules(sg, sg.rules_egress) - ret['rules'] = _split_rules(_rules) - ret['rules_egress'] = _split_rules(_rules_egress) + ret["rules"] = _split_rules(_rules) + ret["rules_egress"] = _split_rules(_rules_egress) return ret else: return None -def create(name, description, vpc_id=None, vpc_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def create( + name, + description, + vpc_id=None, + vpc_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a security group. CLI example:: salt myminion boto_secgroup.create mysecgroup 'My Security Group' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not vpc_id and vpc_name: try: - vpc_id = _vpc_name_to_id(vpc_id=vpc_id, vpc_name=vpc_name, region=region, - key=key, keyid=keyid, profile=profile) + vpc_id = _vpc_name_to_id( + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) except boto.exception.BotoServerError as e: log.debug(e) return False created = conn.create_security_group(name, description, vpc_id) if created: - log.info('Created security group %s.', name) + log.info("Created security group %s.", name) return True else: - msg = 'Failed to create security group {0}.'.format(name) + msg = "Failed to create security group {0}.".format(name) log.error(msg) return False -def delete(name=None, group_id=None, region=None, key=None, keyid=None, - profile=None, vpc_id=None, vpc_name=None): - ''' +def delete( + name=None, + group_id=None, + region=None, + key=None, + keyid=None, + profile=None, + vpc_id=None, + vpc_name=None, +): + """ Delete a security group. CLI example:: salt myminion boto_secgroup.delete mysecgroup - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - group = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name, - group_id=group_id, region=region, key=key, keyid=keyid, - profile=profile) + group = _get_group( + conn, + name=name, + vpc_id=vpc_id, + vpc_name=vpc_name, + group_id=group_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if group: deleted = conn.delete_security_group(group_id=group.id) if deleted: - log.info('Deleted security group %s with id %s.', group.name, group.id) + log.info("Deleted security group %s with id %s.", group.name, group.id) return True else: - msg = 'Failed to delete security group {0}.'.format(name) + msg = "Failed to delete security group {0}.".format(name) log.error(msg) return False else: - log.debug('Security group not found.') + log.debug("Security group not found.") return False -def authorize(name=None, source_group_name=None, - source_group_owner_id=None, ip_protocol=None, - from_port=None, to_port=None, cidr_ip=None, group_id=None, - source_group_group_id=None, region=None, key=None, keyid=None, - profile=None, vpc_id=None, vpc_name=None, egress=False): - ''' +def authorize( + name=None, + source_group_name=None, + source_group_owner_id=None, + ip_protocol=None, + from_port=None, + to_port=None, + cidr_ip=None, + group_id=None, + source_group_group_id=None, + region=None, + key=None, + keyid=None, + profile=None, + vpc_id=None, + vpc_name=None, + egress=False, +): + """ Add a new rule to an existing security group. CLI example:: salt myminion boto_secgroup.authorize mysecgroup ip_protocol=tcp from_port=80 to_port=80 cidr_ip='['10.0.0.0/8', '192.168.0.0/24']' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - group = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name, - group_id=group_id, region=region, key=key, keyid=keyid, - profile=profile) + group = _get_group( + conn, + name=name, + vpc_id=vpc_id, + vpc_name=vpc_name, + group_id=group_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if group: try: added = None @@ -443,54 +588,86 @@ def authorize(name=None, source_group_name=None, added = conn.authorize_security_group( src_security_group_name=source_group_name, src_security_group_owner_id=source_group_owner_id, - ip_protocol=ip_protocol, from_port=from_port, to_port=to_port, - cidr_ip=cidr_ip, group_id=group.id, - src_security_group_group_id=source_group_group_id) + ip_protocol=ip_protocol, + from_port=from_port, + to_port=to_port, + cidr_ip=cidr_ip, + group_id=group.id, + src_security_group_group_id=source_group_group_id, + ) else: added = conn.authorize_security_group_egress( - ip_protocol=ip_protocol, from_port=from_port, to_port=to_port, - cidr_ip=cidr_ip, group_id=group.id, - src_group_id=source_group_group_id) + ip_protocol=ip_protocol, + from_port=from_port, + to_port=to_port, + cidr_ip=cidr_ip, + group_id=group.id, + src_group_id=source_group_group_id, + ) if added: - log.info('Added rule to security group %s with id %s', - group.name, group.id) + log.info( + "Added rule to security group %s with id %s", group.name, group.id + ) return True else: - msg = ('Failed to add rule to security group {0} with id {1}.' - .format(group.name, group.id)) + msg = "Failed to add rule to security group {0} with id {1}.".format( + group.name, group.id + ) log.error(msg) return False except boto.exception.EC2ResponseError as e: # if we are trying to add the same rule then we are already in the desired state, return true - if e.error_code == 'InvalidPermission.Duplicate': + if e.error_code == "InvalidPermission.Duplicate": return True - msg = ('Failed to add rule to security group {0} with id {1}.' - .format(group.name, group.id)) + msg = "Failed to add rule to security group {0} with id {1}.".format( + group.name, group.id + ) log.error(msg) log.error(e) return False else: - log.error('Failed to add rule to security group.') + log.error("Failed to add rule to security group.") return False -def revoke(name=None, source_group_name=None, - source_group_owner_id=None, ip_protocol=None, - from_port=None, to_port=None, cidr_ip=None, group_id=None, - source_group_group_id=None, region=None, key=None, keyid=None, - profile=None, vpc_id=None, vpc_name=None, egress=False): - ''' +def revoke( + name=None, + source_group_name=None, + source_group_owner_id=None, + ip_protocol=None, + from_port=None, + to_port=None, + cidr_ip=None, + group_id=None, + source_group_group_id=None, + region=None, + key=None, + keyid=None, + profile=None, + vpc_id=None, + vpc_name=None, + egress=False, +): + """ Remove a rule from an existing security group. CLI example:: salt myminion boto_secgroup.revoke mysecgroup ip_protocol=tcp from_port=80 to_port=80 cidr_ip='10.0.0.0/8' - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - group = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name, - group_id=group_id, region=region, key=key, keyid=keyid, - profile=profile) + group = _get_group( + conn, + name=name, + vpc_id=vpc_id, + vpc_name=vpc_name, + group_id=group_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if group: try: revoked = None @@ -498,69 +675,92 @@ def revoke(name=None, source_group_name=None, revoked = conn.revoke_security_group( src_security_group_name=source_group_name, src_security_group_owner_id=source_group_owner_id, - ip_protocol=ip_protocol, from_port=from_port, to_port=to_port, - cidr_ip=cidr_ip, group_id=group.id, - src_security_group_group_id=source_group_group_id) + ip_protocol=ip_protocol, + from_port=from_port, + to_port=to_port, + cidr_ip=cidr_ip, + group_id=group.id, + src_security_group_group_id=source_group_group_id, + ) else: revoked = conn.revoke_security_group_egress( - ip_protocol=ip_protocol, from_port=from_port, to_port=to_port, - cidr_ip=cidr_ip, group_id=group.id, - src_group_id=source_group_group_id) + ip_protocol=ip_protocol, + from_port=from_port, + to_port=to_port, + cidr_ip=cidr_ip, + group_id=group.id, + src_group_id=source_group_group_id, + ) if revoked: - log.info('Removed rule from security group %s with id %s.', - group.name, group.id) + log.info( + "Removed rule from security group %s with id %s.", + group.name, + group.id, + ) return True else: - msg = ('Failed to remove rule from security group {0} with id {1}.' - .format(group.name, group.id)) + msg = "Failed to remove rule from security group {0} with id {1}.".format( + group.name, group.id + ) log.error(msg) return False except boto.exception.EC2ResponseError as e: - msg = ('Failed to remove rule from security group {0} with id {1}.' - .format(group.name, group.id)) + msg = "Failed to remove rule from security group {0} with id {1}.".format( + group.name, group.id + ) log.error(msg) log.error(e) return False else: - log.error('Failed to remove rule from security group.') + log.error("Failed to remove rule from security group.") return False -def _find_vpcs(vpc_id=None, vpc_name=None, cidr=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def _find_vpcs( + vpc_id=None, + vpc_name=None, + cidr=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given VPC properties, find and return matching VPC ids. Borrowed from boto_vpc; these could be refactored into a common library - ''' + """ if all((vpc_id, vpc_name)): - raise SaltInvocationError('Only one of vpc_name or vpc_id may be ' - 'provided.') + raise SaltInvocationError("Only one of vpc_name or vpc_id may be " "provided.") if not any((vpc_id, vpc_name, tags, cidr)): - raise SaltInvocationError('At least one of the following must be ' - 'provided: vpc_id, vpc_name, cidr or tags.') + raise SaltInvocationError( + "At least one of the following must be " + "provided: vpc_id, vpc_name, cidr or tags." + ) - local_get_conn = __utils__['boto.get_connection_func']('vpc') + local_get_conn = __utils__["boto.get_connection_func"]("vpc") conn = local_get_conn(region=region, key=key, keyid=keyid, profile=profile) - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if vpc_id: - filter_parameters['vpc_ids'] = [vpc_id] + filter_parameters["vpc_ids"] = [vpc_id] if cidr: - filter_parameters['filters']['cidr'] = cidr + filter_parameters["filters"]["cidr"] = cidr if vpc_name: - filter_parameters['filters']['tag:Name'] = vpc_name + filter_parameters["filters"]["tag:Name"] = vpc_name if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value vpcs = conn.get_all_vpcs(**filter_parameters) - log.debug('The filters criteria %s matched the following VPCs:%s', - filter_parameters, vpcs) + log.debug( + "The filters criteria %s matched the following VPCs:%s", filter_parameters, vpcs + ) if vpcs: return [vpc.id for vpc in vpcs] @@ -568,16 +768,18 @@ def _find_vpcs(vpc_id=None, vpc_name=None, cidr=None, tags=None, return [] -def set_tags(tags, - name=None, - group_id=None, - vpc_name=None, - vpc_id=None, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def set_tags( + tags, + name=None, + group_id=None, + vpc_name=None, + vpc_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Sets tags on a security group. .. versionadded:: 2016.3.0 @@ -614,34 +816,44 @@ def set_tags(tags, .. code-block:: bash salt myminion boto_secgroup.set_tags "{'TAG1': 'Value1', 'TAG2': 'Value2'}" security_group_name vpc_id=vpc-13435 profile=my_aws_profile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - secgrp = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name, - group_id=group_id, region=region, key=key, keyid=keyid, - profile=profile) + secgrp = _get_group( + conn, + name=name, + vpc_id=vpc_id, + vpc_name=vpc_name, + group_id=group_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if secgrp: if isinstance(tags, dict): secgrp.add_tags(tags) else: - msg = 'Tags must be a dict of tagname:tagvalue' + msg = "Tags must be a dict of tagname:tagvalue" raise SaltInvocationError(msg) else: - msg = 'The security group could not be found' + msg = "The security group could not be found" raise SaltInvocationError(msg) return True -def delete_tags(tags, - name=None, - group_id=None, - vpc_name=None, - vpc_id=None, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def delete_tags( + tags, + name=None, + group_id=None, + vpc_name=None, + vpc_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Deletes tags from a security group. .. versionadded:: 2016.3.0 @@ -678,11 +890,19 @@ def delete_tags(tags, .. code-block:: bash salt myminion boto_secgroup.delete_tags ['TAG_TO_DELETE1','TAG_TO_DELETE2'] security_group_name vpc_id=vpc-13435 profile=my_aws_profile - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - secgrp = _get_group(conn, name=name, vpc_id=vpc_id, vpc_name=vpc_name, - group_id=group_id, region=region, key=key, keyid=keyid, - profile=profile) + secgrp = _get_group( + conn, + name=name, + vpc_id=vpc_id, + vpc_name=vpc_name, + group_id=group_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if secgrp: if isinstance(tags, list): tags_to_remove = {} @@ -690,9 +910,9 @@ def delete_tags(tags, tags_to_remove[tag] = None secgrp.remove_tags(tags_to_remove) else: - msg = 'Tags must be a list of tagnames to remove from the security group' + msg = "Tags must be a list of tagnames to remove from the security group" raise SaltInvocationError(msg) else: - msg = 'The security group could not be found' + msg = "The security group could not be found" raise SaltInvocationError(msg) return True diff --git a/salt/modules/boto_sns.py b/salt/modules/boto_sns.py index 4473949c6d7..5e74485a191 100644 --- a/salt/modules/boto_sns.py +++ b/salt/modules/boto_sns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon SNS :configuration: This module accepts explicit sns credentials but can also @@ -38,12 +38,13 @@ Connection module for Amazon SNS region: us-east-1 :depends: boto -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -53,36 +54,35 @@ log = logging.getLogger(__name__) # Import third party libs try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import boto.sns - #pylint: enable=unused-import - logging.getLogger('boto').setLevel(logging.CRITICAL) + + # pylint: enable=unused-import + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' - has_boto_reqs = salt.utils.versions.check_boto_reqs( - check_boto3=False - ) + """ + has_boto_reqs = salt.utils.versions.check_boto_reqs(check_boto3=False) if has_boto_reqs is True: - __utils__['boto.assign_funcs'](__name__, 'sns', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "sns", pack=__salt__) return has_boto_reqs def get_all_topics(region=None, key=None, keyid=None, profile=None): - ''' + """ Returns a list of the all topics.. CLI example:: salt myminion boto_sns.get_all_topics - ''' + """ cache_key = _cache_get_key() try: return __context__[cache_key] @@ -93,66 +93,67 @@ def get_all_topics(region=None, key=None, keyid=None, profile=None): __context__[cache_key] = {} # TODO: support >100 SNS topics (via NextToken) topics = conn.get_all_topics() - for t in topics['ListTopicsResponse']['ListTopicsResult']['Topics']: - short_name = t['TopicArn'].split(':')[-1] - __context__[cache_key][short_name] = t['TopicArn'] + for t in topics["ListTopicsResponse"]["ListTopicsResult"]["Topics"]: + short_name = t["TopicArn"].split(":")[-1] + __context__[cache_key][short_name] = t["TopicArn"] return __context__[cache_key] def exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if an SNS topic exists. CLI example:: salt myminion boto_sns.exists mytopic region=us-east-1 - ''' - topics = get_all_topics(region=region, key=key, keyid=keyid, - profile=profile) - if name.startswith('arn:aws:sns:'): + """ + topics = get_all_topics(region=region, key=key, keyid=keyid, profile=profile) + if name.startswith("arn:aws:sns:"): return name in list(topics.values()) else: return name in list(topics.keys()) def create(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Create an SNS topic. CLI example to create a topic:: salt myminion boto_sns.create mytopic region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.create_topic(name) - log.info('Created SNS topic %s', name) + log.info("Created SNS topic %s", name) _invalidate_cache() return True def delete(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete an SNS topic. CLI example to delete a topic:: salt myminion boto_sns.delete mytopic region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.delete_topic(get_arn(name, region, key, keyid, profile)) - log.info('Deleted SNS topic %s', name) + log.info("Deleted SNS topic %s", name) _invalidate_cache() return True -def get_all_subscriptions_by_topic(name, region=None, key=None, keyid=None, profile=None): - ''' +def get_all_subscriptions_by_topic( + name, region=None, key=None, keyid=None, profile=None +): + """ Get list of all subscriptions to a specific topic. CLI example to delete a topic:: salt myminion boto_sns.get_all_subscriptions_by_topic mytopic region=us-east-1 - ''' + """ cache_key = _subscriptions_cache_key(name) try: return __context__[cache_key] @@ -160,22 +161,28 @@ def get_all_subscriptions_by_topic(name, region=None, key=None, keyid=None, prof pass conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - ret = conn.get_all_subscriptions_by_topic(get_arn(name, region, key, keyid, profile)) - __context__[cache_key] = ret['ListSubscriptionsByTopicResponse']['ListSubscriptionsByTopicResult']['Subscriptions'] + ret = conn.get_all_subscriptions_by_topic( + get_arn(name, region, key, keyid, profile) + ) + __context__[cache_key] = ret["ListSubscriptionsByTopicResponse"][ + "ListSubscriptionsByTopicResult" + ]["Subscriptions"] return __context__[cache_key] -def subscribe(topic, protocol, endpoint, region=None, key=None, keyid=None, profile=None): - ''' +def subscribe( + topic, protocol, endpoint, region=None, key=None, keyid=None, profile=None +): + """ Subscribe to a Topic. CLI example to delete a topic:: salt myminion boto_sns.subscribe mytopic https https://www.example.com/sns-endpoint region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) conn.subscribe(get_arn(topic, region, key, keyid, profile), protocol, endpoint) - log.info('Subscribe %s %s to %s topic', protocol, endpoint, topic) + log.info("Subscribe %s %s to %s topic", protocol, endpoint, topic) try: del __context__[_subscriptions_cache_key(topic)] except KeyError: @@ -183,8 +190,10 @@ def subscribe(topic, protocol, endpoint, region=None, key=None, keyid=None, prof return True -def unsubscribe(topic, subscription_arn, region=None, key=None, keyid=None, profile=None): - ''' +def unsubscribe( + topic, subscription_arn, region=None, key=None, keyid=None, profile=None +): + """ Unsubscribe a specific SubscriptionArn of a topic. CLI Example: @@ -194,17 +203,17 @@ def unsubscribe(topic, subscription_arn, region=None, key=None, keyid=None, prof salt myminion boto_sns.unsubscribe my_topic my_subscription_arn region=us-east-1 .. versionadded:: 2016.11.0 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if subscription_arn.startswith('arn:aws:sns:') is False: + if subscription_arn.startswith("arn:aws:sns:") is False: return False try: conn.unsubscribe(subscription_arn) - log.info('Unsubscribe %s to %s topic', subscription_arn, topic) + log.info("Unsubscribe %s to %s topic", subscription_arn, topic) except Exception as e: # pylint: disable=broad-except - log.error('Unsubscribe Error', exc_info=True) + log.error("Unsubscribe Error", exc_info=True) return False else: __context__.pop(_subscriptions_cache_key(topic), None) @@ -212,38 +221,39 @@ def unsubscribe(topic, subscription_arn, region=None, key=None, keyid=None, prof def get_arn(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Returns the full ARN for a given topic name. CLI example:: salt myminion boto_sns.get_arn mytopic - ''' - if name.startswith('arn:aws:sns:'): + """ + if name.startswith("arn:aws:sns:"): return name - account_id = __salt__['boto_iam.get_account_id']( + account_id = __salt__["boto_iam.get_account_id"]( region=region, key=key, keyid=keyid, profile=profile ) - return 'arn:aws:sns:{0}:{1}:{2}'.format(_get_region(region, profile), - account_id, name) + return "arn:aws:sns:{0}:{1}:{2}".format( + _get_region(region, profile), account_id, name + ) def _get_region(region=None, profile=None): - if profile and 'region' in profile: - return profile['region'] - if not region and __salt__['config.option'](profile): - _profile = __salt__['config.option'](profile) - region = _profile.get('region', None) - if not region and __salt__['config.option']('sns.region'): - region = __salt__['config.option']('sns.region') + if profile and "region" in profile: + return profile["region"] + if not region and __salt__["config.option"](profile): + _profile = __salt__["config.option"](profile) + region = _profile.get("region", None) + if not region and __salt__["config.option"]("sns.region"): + region = __salt__["config.option"]("sns.region") if not region: - region = 'us-east-1' + region = "us-east-1" return region def _subscriptions_cache_key(name): - return '{0}_{1}_subscriptions'.format(_cache_get_key(), name) + return "{0}_{1}_subscriptions".format(_cache_get_key(), name) def _invalidate_cache(): @@ -254,4 +264,4 @@ def _invalidate_cache(): def _cache_get_key(): - return 'boto_sns.topics_cache' + return "boto_sns.topics_cache" diff --git a/salt/modules/boto_sqs.py b/salt/modules/boto_sqs.py index dd1ab5aa3c4..4155a3c63f5 100644 --- a/salt/modules/boto_sqs.py +++ b/salt/modules/boto_sqs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon SQS .. versionadded:: 2014.7.0 @@ -40,7 +40,7 @@ Connection module for Amazon SQS region: us-east-1 :depends: boto3 -''' +""" # keep lint from choking on _get_conn and _cache_id # pylint: disable=E0602 @@ -55,12 +55,12 @@ import salt.utils.versions # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=import-error,no-name-in-module +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse log = logging.getLogger(__name__) __func_alias__ = { - 'list_': 'list', + "list_": "list", } # Import third party libs @@ -68,27 +68,28 @@ try: # pylint: disable=unused-import import boto3 import botocore + # pylint: enable=unused-import - logging.getLogger('boto3').setLevel(logging.CRITICAL) + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO3 = True except ImportError: HAS_BOTO3 = False def __virtual__(): - ''' + """ Only load if boto3 libraries exist. - ''' + """ has_boto_reqs = salt.utils.versions.check_boto_reqs() if has_boto_reqs is True: - __utils__['boto3.assign_funcs'](__name__, 'sqs') + __utils__["boto3.assign_funcs"](__name__, "sqs") return has_boto_reqs def _preprocess_attributes(attributes): - ''' + """ Pre-process incoming queue attributes before setting them - ''' + """ if isinstance(attributes, six.string_types): attributes = salt.utils.json.loads(attributes) @@ -99,13 +100,11 @@ def _preprocess_attributes(attributes): return salt.utils.json.dumps(val) return val - return dict( - (attr, stringified(val)) for attr, val in six.iteritems(attributes) - ) + return dict((attr, stringified(val)) for attr, val in six.iteritems(attributes)) def exists(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Check to see if a queue exists. CLI Example: @@ -113,28 +112,23 @@ def exists(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_sqs.exists myqueue region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: conn.get_queue_url(QueueName=name) except botocore.exceptions.ClientError as e: - missing_code = 'AWS.SimpleQueueService.NonExistentQueue' - if e.response.get('Error', {}).get('Code') == missing_code: - return {'result': False} - return {'error': __utils__['boto3.get_error'](e)} - return {'result': True} + missing_code = "AWS.SimpleQueueService.NonExistentQueue" + if e.response.get("Error", {}).get("Code") == missing_code: + return {"result": False} + return {"error": __utils__["boto3.get_error"](e)} + return {"result": True} def create( - name, - attributes=None, - region=None, - key=None, - keyid=None, - profile=None, + name, attributes=None, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Create an SQS queue. CLI Example: @@ -142,7 +136,7 @@ def create( .. code-block:: bash salt myminion boto_sqs.create myqueue region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if attributes is None: @@ -152,12 +146,12 @@ def create( try: conn.create_queue(QueueName=name, Attributes=attributes) except botocore.exceptions.ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} - return {'result': True} + return {"error": __utils__["boto3.get_error"](e)} + return {"result": True} def delete(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete an SQS queue. CLI Example: @@ -165,19 +159,19 @@ def delete(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_sqs.delete myqueue region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - url = conn.get_queue_url(QueueName=name)['QueueUrl'] + url = conn.get_queue_url(QueueName=name)["QueueUrl"] conn.delete_queue(QueueUrl=url) except botocore.exceptions.ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} - return {'result': True} + return {"error": __utils__["boto3.get_error"](e)} + return {"result": True} -def list_(prefix='', region=None, key=None, keyid=None, profile=None): - ''' +def list_(prefix="", region=None, key=None, keyid=None, profile=None): + """ Return a list of the names of all visible queues. .. versionadded:: 2016.11.0 @@ -187,24 +181,24 @@ def list_(prefix='', region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_sqs.list region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) def extract_name(queue_url): # Note: this logic taken from boto, so should be safe - return _urlparse(queue_url).path.split('/')[2] + return _urlparse(queue_url).path.split("/")[2] try: r = conn.list_queues(QueueNamePrefix=prefix) # The 'QueueUrls' attribute is missing if there are no queues - urls = r.get('QueueUrls', []) - return {'result': [extract_name(url) for url in urls]} + urls = r.get("QueueUrls", []) + return {"result": [extract_name(url) for url in urls]} except botocore.exceptions.ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def get_attributes(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Return attributes currently set on an SQS queue. CLI Example: @@ -212,26 +206,21 @@ def get_attributes(name, region=None, key=None, keyid=None, profile=None): .. code-block:: bash salt myminion boto_sqs.get_attributes myqueue - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - url = conn.get_queue_url(QueueName=name)['QueueUrl'] - r = conn.get_queue_attributes(QueueUrl=url, AttributeNames=['All']) - return {'result': r['Attributes']} + url = conn.get_queue_url(QueueName=name)["QueueUrl"] + r = conn.get_queue_attributes(QueueUrl=url, AttributeNames=["All"]) + return {"result": r["Attributes"]} except botocore.exceptions.ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} + return {"error": __utils__["boto3.get_error"](e)} def set_attributes( - name, - attributes, - region=None, - key=None, - keyid=None, - profile=None, + name, attributes, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Set attributes on an SQS queue. CLI Example: @@ -239,14 +228,14 @@ def set_attributes( .. code-block:: bash salt myminion boto_sqs.set_attributes myqueue '{ReceiveMessageWaitTimeSeconds: 20}' region=us-east-1 - ''' + """ conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) attributes = _preprocess_attributes(attributes) try: - url = conn.get_queue_url(QueueName=name)['QueueUrl'] + url = conn.get_queue_url(QueueName=name)["QueueUrl"] conn.set_queue_attributes(QueueUrl=url, Attributes=attributes) except botocore.exceptions.ClientError as e: - return {'error': __utils__['boto3.get_error'](e)} - return {'result': True} + return {"error": __utils__["boto3.get_error"](e)} + return {"result": True} diff --git a/salt/modules/boto_ssm.py b/salt/modules/boto_ssm.py index 16ef0dda44c..682c185fddd 100644 --- a/salt/modules/boto_ssm.py +++ b/salt/modules/boto_ssm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon SSM :configuration: This module uses IAM roles assigned to the instance through @@ -12,30 +12,40 @@ Connection module for Amazon SSM http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html :depends: boto3 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.json as json + # Import Salt libs import salt.utils.versions -import salt.utils.json as json log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto libraries exist. - ''' + """ has_boto_reqs = salt.utils.versions.check_boto_reqs() if has_boto_reqs is True: - __utils__['boto3.assign_funcs'](__name__, 'ssm') + __utils__["boto3.assign_funcs"](__name__, "ssm") return has_boto_reqs -def get_parameter(name, withdecryption=False, resp_json=False, region=None, key=None, keyid=None, profile=None): - ''' +def get_parameter( + name, + withdecryption=False, + resp_json=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Retrives a parameter from SSM Parameter Store .. versionadded:: 3000 @@ -43,8 +53,10 @@ def get_parameter(name, withdecryption=False, resp_json=False, region=None, key= .. code-block:: text salt-call boto_ssm.get_parameter test-param withdescription=True - ''' - conn = __utils__['boto3.get_connection']('ssm', region=region, key=key, keyid=keyid, profile=profile) + """ + conn = __utils__["boto3.get_connection"]( + "ssm", region=region, key=key, keyid=keyid, profile=profile + ) try: resp = conn.get_parameter(Name=name, WithDecryption=withdecryption) except conn.exceptions.ParameterNotFound: @@ -52,23 +64,25 @@ def get_parameter(name, withdecryption=False, resp_json=False, region=None, key= return False if resp_json: - return json.loads(resp['Parameter']['Value']) + return json.loads(resp["Parameter"]["Value"]) else: - return resp['Parameter']['Value'] + return resp["Parameter"]["Value"] -def put_parameter(Name, - Value, - Description=None, - Type='String', - KeyId=None, - Overwrite=False, - AllowedPattern=None, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def put_parameter( + Name, + Value, + Description=None, + Type="String", + KeyId=None, + Overwrite=False, + AllowedPattern=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Sets a parameter in the SSM parameter store .. versionadded:: 3000 @@ -76,46 +90,55 @@ def put_parameter(Name, .. code-block:: text salt-call boto_ssm.put_parameter test-param test_value Type=SecureString KeyId=alias/aws/ssm Description='test encrypted key' - ''' - conn = __utils__['boto3.get_connection']('ssm', region=region, key=key, keyid=keyid, profile=profile) - if Type not in ('String', 'StringList', 'SecureString'): - raise AssertionError('Type needs to be String|StringList|SecureString') - if Type == 'SecureString' and not KeyId: - raise AssertionError('Require KeyId with SecureString') + """ + conn = __utils__["boto3.get_connection"]( + "ssm", region=region, key=key, keyid=keyid, profile=profile + ) + if Type not in ("String", "StringList", "SecureString"): + raise AssertionError("Type needs to be String|StringList|SecureString") + if Type == "SecureString" and not KeyId: + raise AssertionError("Require KeyId with SecureString") boto_args = {} if Description: - boto_args['Description'] = Description + boto_args["Description"] = Description if KeyId: - boto_args['KeyId'] = KeyId + boto_args["KeyId"] = KeyId if AllowedPattern: - boto_args['AllowedPattern'] = AllowedPattern + boto_args["AllowedPattern"] = AllowedPattern try: - resp = conn.put_parameter(Name=Name, Value=Value, Type=Type, Overwrite=Overwrite, **boto_args) + resp = conn.put_parameter( + Name=Name, Value=Value, Type=Type, Overwrite=Overwrite, **boto_args + ) except conn.exceptions.ParameterAlreadyExists: - log.warning("The parameter already exists." - " To overwrite this value, set the Overwrite option in the request to True") + log.warning( + "The parameter already exists." + " To overwrite this value, set the Overwrite option in the request to True" + ) return False - return resp['Version'] + return resp["Version"] def delete_parameter(Name, region=None, key=None, keyid=None, profile=None): - ''' + """ Removes a parameter from the SSM parameter store .. versionadded:: 3000 .. code-block:: text + salt-call boto_ssm.delete_parameter test-param - ''' - conn = __utils__['boto3.get_connection']('ssm', region=region, key=key, keyid=keyid, profile=profile) + """ + conn = __utils__["boto3.get_connection"]( + "ssm", region=region, key=key, keyid=keyid, profile=profile + ) try: resp = conn.delete_parameter(Name=Name) except conn.exceptions.ParameterNotFound: log.warning("delete_parameter: Unable to locate name: %s", Name) return False - if resp['ResponseMetadata']['HTTPStatusCode'] == 200: + if resp["ResponseMetadata"]["HTTPStatusCode"] == 200: return True else: return False diff --git a/salt/modules/boto_vpc.py b/salt/modules/boto_vpc.py index 8b63ca0c53d..5ff9e6d351a 100644 --- a/salt/modules/boto_vpc.py +++ b/salt/modules/boto_vpc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon VPC .. versionadded:: 2014.7.0 @@ -120,87 +120,94 @@ Deleting VPC peering connection via this module # specify an id salt myminion boto_vpc.delete_vpc_peering_connection conn_id=pcx-8a8939e3 -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import random import socket import time -import random # Import Salt libs import salt.utils.compat import salt.utils.versions -from salt.exceptions import SaltInvocationError, CommandExecutionError - -# from salt.utils import exactly_one -# TODO: Uncomment this and s/_exactly_one/exactly_one/ -# See note in utils.boto -PROVISIONING = 'provisioning' -PENDING_ACCEPTANCE = 'pending-acceptance' -ACTIVE = 'active' - -log = logging.getLogger(__name__) +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import third party libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error + +# from salt.utils import exactly_one +# TODO: Uncomment this and s/_exactly_one/exactly_one/ +# See note in utils.boto +PROVISIONING = "provisioning" +PENDING_ACCEPTANCE = "pending-acceptance" +ACTIVE = "active" + +log = logging.getLogger(__name__) + + # pylint: disable=import-error try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto import botocore import boto.vpc - #pylint: enable=unused-import + + # pylint: enable=unused-import from boto.exception import BotoServerError - logging.getLogger('boto').setLevel(logging.CRITICAL) + + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False # pylint: enable=import-error try: - #pylint: disable=unused-import + # pylint: disable=unused-import import boto3 - #pylint: enable=unused-import - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + # pylint: enable=unused-import + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO3 = True except ImportError: HAS_BOTO3 = False def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ # the boto_vpc execution module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 # the boto_vpc execution module relies on the create_nat_gateway() method # which was added in boto3 1.2.6 - return salt.utils.versions.check_boto_reqs( - boto_ver='2.8.0', - boto3_ver='1.2.6' - ) + return salt.utils.versions.check_boto_reqs(boto_ver="2.8.0", boto3_ver="1.2.6") def __init__(opts): salt.utils.compat.pack_dunder(__name__) if HAS_BOTO: - __utils__['boto.assign_funcs'](__name__, 'vpc', pack=__salt__) + __utils__["boto.assign_funcs"](__name__, "vpc", pack=__salt__) if HAS_BOTO3: - __utils__['boto3.assign_funcs'](__name__, 'ec2', - get_conn_funcname='_get_conn3', - cache_id_funcname='_cache_id3', - exactly_one_funcname=None) + __utils__["boto3.assign_funcs"]( + __name__, + "ec2", + get_conn_funcname="_get_conn3", + cache_id_funcname="_cache_id3", + exactly_one_funcname=None, + ) -def check_vpc(vpc_id=None, vpc_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def check_vpc( + vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None +): + """ Check whether a VPC with the given name or id exists. Returns the vpc_id or None. Raises SaltInvocationError if both vpc_id and vpc_name are None. Optionally raise a @@ -213,224 +220,295 @@ def check_vpc(vpc_id=None, vpc_name=None, region=None, key=None, .. code-block:: bash salt myminion boto_vpc.check_vpc vpc_name=myvpc profile=awsprofile - ''' + """ if not _exactly_one((vpc_name, vpc_id)): - raise SaltInvocationError('One (but not both) of vpc_id or vpc_name ' - 'must be provided.') + raise SaltInvocationError( + "One (but not both) of vpc_id or vpc_name " "must be provided." + ) if vpc_name: - vpc_id = _get_id(vpc_name=vpc_name, region=region, key=key, keyid=keyid, - profile=profile) - elif not _find_vpcs(vpc_id=vpc_id, region=region, key=key, keyid=keyid, - profile=profile): - log.info('VPC %s does not exist.', vpc_id) + vpc_id = _get_id( + vpc_name=vpc_name, region=region, key=key, keyid=keyid, profile=profile + ) + elif not _find_vpcs( + vpc_id=vpc_id, region=region, key=key, keyid=keyid, profile=profile + ): + log.info("VPC %s does not exist.", vpc_id) return None return vpc_id -def _create_resource(resource, name=None, tags=None, region=None, key=None, - keyid=None, profile=None, **kwargs): - ''' +def _create_resource( + resource, + name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + **kwargs +): + """ Create a VPC resource. Returns the resource id if created, or False if not created. - ''' + """ try: try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - create_resource = getattr(conn, 'create_' + resource) + create_resource = getattr(conn, "create_" + resource) except AttributeError: - raise AttributeError('{0} function does not exist for boto VPC ' - 'connection.'.format('create_' + resource)) + raise AttributeError( + "{0} function does not exist for boto VPC " + "connection.".format("create_" + resource) + ) - if name and _get_resource_id(resource, name, region=region, key=key, - keyid=keyid, profile=profile): - return {'created': False, 'error': {'message': - 'A {0} named {1} already exists.'.format( - resource, name)}} + if name and _get_resource_id( + resource, name, region=region, key=key, keyid=keyid, profile=profile + ): + return { + "created": False, + "error": { + "message": "A {0} named {1} already exists.".format(resource, name) + }, + } r = create_resource(**kwargs) if r: if isinstance(r, bool): - return {'created': True} + return {"created": True} else: - log.info('A %s with id %s was created', resource, r.id) + log.info("A %s with id %s was created", resource, r.id) _maybe_set_name_tag(name, r) _maybe_set_tags(tags, r) if name: - _cache_id(name, - sub_resource=resource, - resource_id=r.id, - region=region, - key=key, keyid=keyid, - profile=profile) - return {'created': True, 'id': r.id} + _cache_id( + name, + sub_resource=resource, + resource_id=r.id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + return {"created": True, "id": r.id} else: if name: - e = '{0} {1} was not created.'.format(resource, name) + e = "{0} {1} was not created.".format(resource, name) else: - e = '{0} was not created.'.format(resource) + e = "{0} was not created.".format(resource) log.warning(e) - return {'created': False, 'error': {'message': e}} + return {"created": False, "error": {"message": e}} except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} -def _delete_resource(resource, name=None, resource_id=None, region=None, - key=None, keyid=None, profile=None, **kwargs): - ''' +def _delete_resource( + resource, + name=None, + resource_id=None, + region=None, + key=None, + keyid=None, + profile=None, + **kwargs +): + """ Delete a VPC resource. Returns True if successful, otherwise False. - ''' + """ if not _exactly_one((name, resource_id)): - raise SaltInvocationError('One (but not both) of name or id must be ' - 'provided.') + raise SaltInvocationError( + "One (but not both) of name or id must be " "provided." + ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) try: - delete_resource = getattr(conn, 'delete_' + resource) + delete_resource = getattr(conn, "delete_" + resource) except AttributeError: - raise AttributeError('{0} function does not exist for boto VPC ' - 'connection.'.format('delete_' + resource)) + raise AttributeError( + "{0} function does not exist for boto VPC " + "connection.".format("delete_" + resource) + ) if name: - resource_id = _get_resource_id(resource, name, - region=region, key=key, - keyid=keyid, profile=profile) + resource_id = _get_resource_id( + resource, name, region=region, key=key, keyid=keyid, profile=profile + ) if not resource_id: - return {'deleted': False, 'error': {'message': - '{0} {1} does not exist.'.format(resource, name)}} + return { + "deleted": False, + "error": { + "message": "{0} {1} does not exist.".format(resource, name) + }, + } if delete_resource(resource_id, **kwargs): - _cache_id(name, sub_resource=resource, - resource_id=resource_id, - invalidate=True, - region=region, - key=key, keyid=keyid, - profile=profile) - return {'deleted': True} + _cache_id( + name, + sub_resource=resource, + resource_id=resource_id, + invalidate=True, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + return {"deleted": True} else: if name: - e = '{0} {1} was not deleted.'.format(resource, name) + e = "{0} {1} was not deleted.".format(resource, name) else: - e = '{0} was not deleted.'.format(resource) - return {'deleted': False, 'error': {'message': e}} + e = "{0} was not deleted.".format(resource) + return {"deleted": False, "error": {"message": e}} except BotoServerError as e: - return {'deleted': False, 'error': __utils__['boto.get_error'](e)} + return {"deleted": False, "error": __utils__["boto.get_error"](e)} -def _get_resource(resource, name=None, resource_id=None, region=None, - key=None, keyid=None, profile=None): - ''' +def _get_resource( + resource, + name=None, + resource_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get a VPC resource based on resource type and name or id. Cache the id if name was provided. - ''' + """ if not _exactly_one((name, resource_id)): - raise SaltInvocationError('One (but not both) of name or id must be ' - 'provided.') + raise SaltInvocationError( + "One (but not both) of name or id must be " "provided." + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - f = 'get_all_{0}'.format(resource) - if not f.endswith('s'): - f = f + 's' + f = "get_all_{0}".format(resource) + if not f.endswith("s"): + f = f + "s" get_resources = getattr(conn, f) filter_parameters = {} if name: - filter_parameters['filters'] = {'tag:Name': name} + filter_parameters["filters"] = {"tag:Name": name} if resource_id: - filter_parameters['{0}_ids'.format(resource)] = resource_id + filter_parameters["{0}_ids".format(resource)] = resource_id try: r = get_resources(**filter_parameters) except BotoServerError as e: - if e.code.endswith('.NotFound'): + if e.code.endswith(".NotFound"): return None raise if r: if len(r) == 1: if name: - _cache_id(name, sub_resource=resource, - resource_id=r[0].id, - region=region, - key=key, keyid=keyid, - profile=profile) + _cache_id( + name, + sub_resource=resource, + resource_id=r[0].id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) return r[0] else: - raise CommandExecutionError('Found more than one ' - '{0} named "{1}"'.format( - resource, name)) + raise CommandExecutionError( + "Found more than one " '{0} named "{1}"'.format(resource, name) + ) else: return None -def _find_resources(resource, name=None, resource_id=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def _find_resources( + resource, + name=None, + resource_id=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get VPC resources based on resource type and name, id, or tags. - ''' + """ if all((resource_id, name)): - raise SaltInvocationError('Only one of name or id may be ' - 'provided.') + raise SaltInvocationError("Only one of name or id may be " "provided.") if not any((resource_id, name, tags)): - raise SaltInvocationError('At least one of the following must be ' - 'provided: id, name, or tags.') + raise SaltInvocationError( + "At least one of the following must be " "provided: id, name, or tags." + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - f = 'get_all_{0}'.format(resource) - if not f.endswith('s'): - f = f + 's' + f = "get_all_{0}".format(resource) + if not f.endswith("s"): + f = f + "s" get_resources = getattr(conn, f) filter_parameters = {} if name: - filter_parameters['filters'] = {'tag:Name': name} + filter_parameters["filters"] = {"tag:Name": name} if resource_id: - filter_parameters['{0}_ids'.format(resource)] = resource_id + filter_parameters["{0}_ids".format(resource)] = resource_id if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value try: r = get_resources(**filter_parameters) except BotoServerError as e: - if e.code.endswith('.NotFound'): + if e.code.endswith(".NotFound"): return None raise return r -def _get_resource_id(resource, name, region=None, key=None, - keyid=None, profile=None): - ''' +def _get_resource_id(resource, name, region=None, key=None, keyid=None, profile=None): + """ Get an AWS id for a VPC resource by type and name. - ''' + """ - _id = _cache_id(name, sub_resource=resource, - region=region, key=key, - keyid=keyid, profile=profile) + _id = _cache_id( + name, + sub_resource=resource, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if _id: return _id - r = _get_resource(resource, name=name, region=region, key=key, - keyid=keyid, profile=profile) + r = _get_resource( + resource, name=name, region=region, key=key, keyid=keyid, profile=profile + ) if r: return r.id -def get_resource_id(resource, name=None, resource_id=None, region=None, - key=None, keyid=None, profile=None): - ''' +def get_resource_id( + resource, + name=None, + resource_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Get an AWS id for a VPC resource by type and name. .. versionadded:: 2015.8.0 @@ -441,18 +519,29 @@ def get_resource_id(resource, name=None, resource_id=None, region=None, salt myminion boto_vpc.get_resource_id internet_gateway myigw - ''' + """ try: - return {'id': _get_resource_id(resource, name, region=region, key=key, - keyid=keyid, profile=profile)} + return { + "id": _get_resource_id( + resource, name, region=region, key=key, keyid=keyid, profile=profile + ) + } except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} -def resource_exists(resource, name=None, resource_id=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def resource_exists( + resource, + name=None, + resource_id=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a resource type and name, return {exists: true} if it exists, {exists: false} if it does not exist, or {error: {message: error text} on error. @@ -465,52 +554,71 @@ def resource_exists(resource, name=None, resource_id=None, tags=None, salt myminion boto_vpc.resource_exists internet_gateway myigw - ''' + """ try: - return {'exists': bool(_find_resources(resource, name=name, - resource_id=resource_id, - tags=tags, region=region, - key=key, keyid=keyid, - profile=profile))} + return { + "exists": bool( + _find_resources( + resource, + name=name, + resource_id=resource_id, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ) + } except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} -def _find_vpcs(vpc_id=None, vpc_name=None, cidr=None, tags=None, - region=None, key=None, keyid=None, profile=None): +def _find_vpcs( + vpc_id=None, + vpc_name=None, + cidr=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): - ''' + """ Given VPC properties, find and return matching VPC ids. - ''' + """ if all((vpc_id, vpc_name)): - raise SaltInvocationError('Only one of vpc_name or vpc_id may be ' - 'provided.') + raise SaltInvocationError("Only one of vpc_name or vpc_id may be " "provided.") if not any((vpc_id, vpc_name, tags, cidr)): - raise SaltInvocationError('At least one of the following must be ' - 'provided: vpc_id, vpc_name, cidr or tags.') + raise SaltInvocationError( + "At least one of the following must be " + "provided: vpc_id, vpc_name, cidr or tags." + ) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if vpc_id: - filter_parameters['vpc_ids'] = [vpc_id] + filter_parameters["vpc_ids"] = [vpc_id] if cidr: - filter_parameters['filters']['cidr'] = cidr + filter_parameters["filters"]["cidr"] = cidr if vpc_name: - filter_parameters['filters']['tag:Name'] = vpc_name + filter_parameters["filters"]["tag:Name"] = vpc_name if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value vpcs = conn.get_all_vpcs(**filter_parameters) - log.debug('The filters criteria %s matched the following VPCs:%s', - filter_parameters, vpcs) + log.debug( + "The filters criteria %s matched the following VPCs:%s", filter_parameters, vpcs + ) if vpcs: return [vpc.id for vpc in vpcs] @@ -518,40 +626,56 @@ def _find_vpcs(vpc_id=None, vpc_name=None, cidr=None, tags=None, return [] -def _get_id(vpc_name=None, cidr=None, tags=None, region=None, key=None, - keyid=None, profile=None): - ''' +def _get_id( + vpc_name=None, cidr=None, tags=None, region=None, key=None, keyid=None, profile=None +): + """ Given VPC properties, return the VPC id if a match is found. - ''' + """ if vpc_name and not any((cidr, tags)): - vpc_id = _cache_id(vpc_name, region=region, - key=key, keyid=keyid, - profile=profile) + vpc_id = _cache_id( + vpc_name, region=region, key=key, keyid=keyid, profile=profile + ) if vpc_id: return vpc_id - vpc_ids = _find_vpcs(vpc_name=vpc_name, cidr=cidr, tags=tags, region=region, - key=key, keyid=keyid, profile=profile) + vpc_ids = _find_vpcs( + vpc_name=vpc_name, + cidr=cidr, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if vpc_ids: log.debug("Matching VPC: %s", " ".join(vpc_ids)) if len(vpc_ids) == 1: vpc_id = vpc_ids[0] if vpc_name: - _cache_id(vpc_name, vpc_id, - region=region, key=key, - keyid=keyid, profile=profile) + _cache_id( + vpc_name, + vpc_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) return vpc_id else: - raise CommandExecutionError('Found more than one VPC matching the criteria.') + raise CommandExecutionError( + "Found more than one VPC matching the criteria." + ) else: - log.info('No VPC found.') + log.info("No VPC found.") return None -def get_id(name=None, cidr=None, tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def get_id( + name=None, cidr=None, tags=None, region=None, key=None, keyid=None, profile=None +): + """ Given VPC properties, return the VPC id if a match is found. CLI Example: @@ -560,18 +684,35 @@ def get_id(name=None, cidr=None, tags=None, region=None, key=None, keyid=None, salt myminion boto_vpc.get_id myvpc - ''' + """ try: - return {'id': _get_id(vpc_name=name, cidr=cidr, tags=tags, region=region, - key=key, keyid=keyid, profile=profile)} + return { + "id": _get_id( + vpc_name=name, + cidr=cidr, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + } except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} -def exists(vpc_id=None, name=None, cidr=None, tags=None, region=None, key=None, - keyid=None, profile=None): - ''' +def exists( + vpc_id=None, + name=None, + cidr=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a VPC ID, check to see if the given VPC ID exists. Returns True if the given VPC ID exists and returns False if the given @@ -583,25 +724,42 @@ def exists(vpc_id=None, name=None, cidr=None, tags=None, region=None, key=None, salt myminion boto_vpc.exists myvpc - ''' + """ try: - vpc_ids = _find_vpcs(vpc_id=vpc_id, vpc_name=name, cidr=cidr, tags=tags, - region=region, key=key, keyid=keyid, profile=profile) + vpc_ids = _find_vpcs( + vpc_id=vpc_id, + vpc_name=name, + cidr=cidr, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) except BotoServerError as err: - boto_err = __utils__['boto.get_error'](err) - if boto_err.get('aws', {}).get('code') == 'InvalidVpcID.NotFound': + boto_err = __utils__["boto.get_error"](err) + if boto_err.get("aws", {}).get("code") == "InvalidVpcID.NotFound": # VPC was not found: handle the error and return False. - return {'exists': False} - return {'error': boto_err} + return {"exists": False} + return {"error": boto_err} - return {'exists': bool(vpc_ids)} + return {"exists": bool(vpc_ids)} -def create(cidr_block, instance_tenancy=None, vpc_name=None, - enable_dns_support=None, enable_dns_hostnames=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create( + cidr_block, + instance_tenancy=None, + vpc_name=None, + enable_dns_support=None, + enable_dns_hostnames=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid CIDR block, create a VPC. An optional instance_tenancy argument can be provided. If provided, the @@ -618,33 +776,46 @@ def create(cidr_block, instance_tenancy=None, vpc_name=None, salt myminion boto_vpc.create '10.0.0.0/24' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) vpc = conn.create_vpc(cidr_block, instance_tenancy=instance_tenancy) if vpc: - log.info('The newly created VPC id is %s', vpc.id) + log.info("The newly created VPC id is %s", vpc.id) _maybe_set_name_tag(vpc_name, vpc) _maybe_set_tags(tags, vpc) _maybe_set_dns(conn, vpc.id, enable_dns_support, enable_dns_hostnames) _maybe_name_route_table(conn, vpc.id, vpc_name) if vpc_name: - _cache_id(vpc_name, vpc.id, - region=region, key=key, - keyid=keyid, profile=profile) - return {'created': True, 'id': vpc.id} + _cache_id( + vpc_name, + vpc.id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + return {"created": True, "id": vpc.id} else: - log.warning('VPC was not created') - return {'created': False} + log.warning("VPC was not created") + return {"created": False} except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} -def delete(vpc_id=None, name=None, vpc_name=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def delete( + vpc_id=None, + name=None, + vpc_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a VPC ID or VPC name, delete the VPC. Returns {deleted: true} if the VPC was deleted and returns @@ -657,44 +828,59 @@ def delete(vpc_id=None, name=None, vpc_name=None, tags=None, salt myminion boto_vpc.delete vpc_id='vpc-6b1fe402' salt myminion boto_vpc.delete name='myvpc' - ''' + """ if name: - log.warning('boto_vpc.delete: name parameter is deprecated ' - 'use vpc_name instead.') + log.warning( + "boto_vpc.delete: name parameter is deprecated " "use vpc_name instead." + ) vpc_name = name if not _exactly_one((vpc_name, vpc_id)): - raise SaltInvocationError('One (but not both) of vpc_name or vpc_id must be ' - 'provided.') + raise SaltInvocationError( + "One (but not both) of vpc_name or vpc_id must be " "provided." + ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if not vpc_id: - vpc_id = _get_id(vpc_name=vpc_name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile) + vpc_id = _get_id( + vpc_name=vpc_name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not vpc_id: - return {'deleted': False, 'error': {'message': - 'VPC {0} not found'.format(vpc_name)}} + return { + "deleted": False, + "error": {"message": "VPC {0} not found".format(vpc_name)}, + } if conn.delete_vpc(vpc_id): - log.info('VPC %s was deleted.', vpc_id) + log.info("VPC %s was deleted.", vpc_id) if vpc_name: - _cache_id(vpc_name, resource_id=vpc_id, - invalidate=True, - region=region, - key=key, keyid=keyid, - profile=profile) - return {'deleted': True} + _cache_id( + vpc_name, + resource_id=vpc_id, + invalidate=True, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + return {"deleted": True} else: - log.warning('VPC %s was not deleted.', vpc_id) - return {'deleted': False} + log.warning("VPC %s was not deleted.", vpc_id) + return {"deleted": False} except BotoServerError as e: - return {'deleted': False, 'error': __utils__['boto.get_error'](e)} + return {"deleted": False, "error": __utils__["boto.get_error"](e)} -def describe(vpc_id=None, vpc_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def describe( + vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None +): + """ Given a VPC ID describe its properties. Returns a dictionary of interesting properties. @@ -709,47 +895,62 @@ def describe(vpc_id=None, vpc_name=None, region=None, key=None, salt myminion boto_vpc.describe vpc_id=vpc-123456 salt myminion boto_vpc.describe vpc_name=myvpc - ''' + """ if not any((vpc_id, vpc_name)): - raise SaltInvocationError('A valid vpc id or name needs to be specified.') + raise SaltInvocationError("A valid vpc id or name needs to be specified.") try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) except BotoServerError as err: - boto_err = __utils__['boto.get_error'](err) - if boto_err.get('aws', {}).get('code') == 'InvalidVpcID.NotFound': + boto_err = __utils__["boto.get_error"](err) + if boto_err.get("aws", {}).get("code") == "InvalidVpcID.NotFound": # VPC was not found: handle the error and return None. - return {'vpc': None} - return {'error': boto_err} + return {"vpc": None} + return {"error": boto_err} if not vpc_id: - return {'vpc': None} + return {"vpc": None} - filter_parameters = {'vpc_ids': vpc_id} + filter_parameters = {"vpc_ids": vpc_id} try: vpcs = conn.get_all_vpcs(**filter_parameters) except BotoServerError as err: - return {'error': __utils__['boto.get_error'](err)} + return {"error": __utils__["boto.get_error"](err)} if vpcs: vpc = vpcs[0] # Found! - log.debug('Found VPC: %s', vpc.id) + log.debug("Found VPC: %s", vpc.id) - keys = ('id', 'cidr_block', 'is_default', 'state', 'tags', - 'dhcp_options_id', 'instance_tenancy') + keys = ( + "id", + "cidr_block", + "is_default", + "state", + "tags", + "dhcp_options_id", + "instance_tenancy", + ) _r = dict([(k, getattr(vpc, k)) for k in keys]) - _r.update({'region': getattr(vpc, 'region').name}) - return {'vpc': _r} + _r.update({"region": getattr(vpc, "region").name}) + return {"vpc": _r} else: - return {'vpc': None} + return {"vpc": None} -def describe_vpcs(vpc_id=None, name=None, cidr=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_vpcs( + vpc_id=None, + name=None, + cidr=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Describe all VPCs, matching the filter criteria if provided. Returns a list of dictionaries with interesting properties. @@ -762,32 +963,34 @@ def describe_vpcs(vpc_id=None, name=None, cidr=None, tags=None, salt myminion boto_vpc.describe_vpcs - ''' + """ - keys = ('id', - 'cidr_block', - 'is_default', - 'state', - 'tags', - 'dhcp_options_id', - 'instance_tenancy') + keys = ( + "id", + "cidr_block", + "is_default", + "state", + "tags", + "dhcp_options_id", + "instance_tenancy", + ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if vpc_id: - filter_parameters['vpc_ids'] = [vpc_id] + filter_parameters["vpc_ids"] = [vpc_id] if cidr: - filter_parameters['filters']['cidr'] = cidr + filter_parameters["filters"]["cidr"] = cidr if name: - filter_parameters['filters']['tag:Name'] = name + filter_parameters["filters"]["tag:Name"] = name if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value vpcs = conn.get_all_vpcs(**filter_parameters) @@ -795,43 +998,48 @@ def describe_vpcs(vpc_id=None, name=None, cidr=None, tags=None, ret = [] for vpc in vpcs: _r = dict([(k, getattr(vpc, k)) for k in keys]) - _r.update({'region': getattr(vpc, 'region').name}) + _r.update({"region": getattr(vpc, "region").name}) ret.append(_r) - return {'vpcs': ret} + return {"vpcs": ret} else: - return {'vpcs': []} + return {"vpcs": []} except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} def _find_subnets(subnet_name=None, vpc_id=None, cidr=None, tags=None, conn=None): - ''' + """ Given subnet properties, find and return matching subnet ids - ''' + """ if not any([subnet_name, tags, cidr]): - raise SaltInvocationError('At least one of the following must be ' - 'specified: subnet_name, cidr or tags.') + raise SaltInvocationError( + "At least one of the following must be " + "specified: subnet_name, cidr or tags." + ) - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if cidr: - filter_parameters['filters']['cidr'] = cidr + filter_parameters["filters"]["cidr"] = cidr if subnet_name: - filter_parameters['filters']['tag:Name'] = subnet_name + filter_parameters["filters"]["tag:Name"] = subnet_name if vpc_id: - filter_parameters['filters']['VpcId'] = vpc_id + filter_parameters["filters"]["VpcId"] = vpc_id if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value subnets = conn.get_all_subnets(**filter_parameters) - log.debug('The filters criteria %s matched the following subnets: %s', - filter_parameters, subnets) + log.debug( + "The filters criteria %s matched the following subnets: %s", + filter_parameters, + subnets, + ) if subnets: return [subnet.id for subnet in subnets] @@ -839,10 +1047,20 @@ def _find_subnets(subnet_name=None, vpc_id=None, cidr=None, tags=None, conn=None return False -def create_subnet(vpc_id=None, cidr_block=None, vpc_name=None, - availability_zone=None, subnet_name=None, tags=None, - region=None, key=None, keyid=None, profile=None, auto_assign_public_ipv4=False): - ''' +def create_subnet( + vpc_id=None, + cidr_block=None, + vpc_name=None, + availability_zone=None, + subnet_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + auto_assign_public_ipv4=False, +): + """ Given a valid VPC ID or Name and a CIDR block, create a subnet for the VPC. An optional availability zone argument can be provided. @@ -860,29 +1078,45 @@ def create_subnet(vpc_id=None, cidr_block=None, vpc_name=None, subnet_name='mysubnet' cidr_block='10.0.0.0/25' salt myminion boto_vpc.create_subnet vpc_name='myvpc' \\ subnet_name='mysubnet', cidr_block='10.0.0.0/25' - ''' + """ try: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: - return {'created': False, 'error': {'message': 'VPC {0} does not exist.'.format(vpc_name or vpc_id)}} + return { + "created": False, + "error": { + "message": "VPC {0} does not exist.".format(vpc_name or vpc_id) + }, + } except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} - subnet_object_dict = _create_resource('subnet', name=subnet_name, tags=tags, vpc_id=vpc_id, - availability_zone=availability_zone, - cidr_block=cidr_block, region=region, key=key, - keyid=keyid, profile=profile) + subnet_object_dict = _create_resource( + "subnet", + name=subnet_name, + tags=tags, + vpc_id=vpc_id, + availability_zone=availability_zone, + cidr_block=cidr_block, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) # if auto_assign_public_ipv4 is requested set that to true using boto3 if auto_assign_public_ipv4: conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) - conn3.modify_subnet_attribute(MapPublicIpOnLaunch={'Value': True}, SubnetId=subnet_object_dict['id']) + conn3.modify_subnet_attribute( + MapPublicIpOnLaunch={"Value": True}, SubnetId=subnet_object_dict["id"] + ) return subnet_object_dict -def delete_subnet(subnet_id=None, subnet_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def delete_subnet( + subnet_id=None, subnet_name=None, region=None, key=None, keyid=None, profile=None +): + """ Given a subnet ID or name, delete the subnet. Returns True if the subnet was deleted and returns False if the subnet was not deleted. @@ -896,17 +1130,32 @@ def delete_subnet(subnet_id=None, subnet_name=None, region=None, key=None, salt myminion boto_vpc.delete_subnet 'subnet-6a1fe403' - ''' + """ - return _delete_resource(resource='subnet', name=subnet_name, - resource_id=subnet_id, region=region, key=key, - keyid=keyid, profile=profile) + return _delete_resource( + resource="subnet", + name=subnet_name, + resource_id=subnet_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def subnet_exists(subnet_id=None, name=None, subnet_name=None, cidr=None, - tags=None, zones=None, region=None, key=None, keyid=None, - profile=None): - ''' +def subnet_exists( + subnet_id=None, + name=None, + subnet_name=None, + cidr=None, + tags=None, + zones=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Check if a subnet exists. Returns True if the subnet exists, otherwise returns False. @@ -921,57 +1170,63 @@ def subnet_exists(subnet_id=None, name=None, subnet_name=None, cidr=None, salt myminion boto_vpc.subnet_exists subnet_id='subnet-6a1fe403' - ''' + """ if name: - log.warning('boto_vpc.subnet_exists: name parameter is deprecated ' - 'use subnet_name instead.') + log.warning( + "boto_vpc.subnet_exists: name parameter is deprecated " + "use subnet_name instead." + ) subnet_name = name if not any((subnet_id, subnet_name, cidr, tags, zones)): - raise SaltInvocationError('At least one of the following must be ' - 'specified: subnet id, cidr, subnet_name, ' - 'tags, or zones.') + raise SaltInvocationError( + "At least one of the following must be " + "specified: subnet id, cidr, subnet_name, " + "tags, or zones." + ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) except BotoServerError as err: - return {'error': __utils__['boto.get_error'](err)} + return {"error": __utils__["boto.get_error"](err)} - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if subnet_id: - filter_parameters['subnet_ids'] = [subnet_id] + filter_parameters["subnet_ids"] = [subnet_id] if subnet_name: - filter_parameters['filters']['tag:Name'] = subnet_name + filter_parameters["filters"]["tag:Name"] = subnet_name if cidr: - filter_parameters['filters']['cidr'] = cidr + filter_parameters["filters"]["cidr"] = cidr if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value if zones: - filter_parameters['filters']['availability_zone'] = zones + filter_parameters["filters"]["availability_zone"] = zones try: subnets = conn.get_all_subnets(**filter_parameters) except BotoServerError as err: - boto_err = __utils__['boto.get_error'](err) - if boto_err.get('aws', {}).get('code') == 'InvalidSubnetID.NotFound': + boto_err = __utils__["boto.get_error"](err) + if boto_err.get("aws", {}).get("code") == "InvalidSubnetID.NotFound": # Subnet was not found: handle the error and return False. - return {'exists': False} - return {'error': boto_err} + return {"exists": False} + return {"error": boto_err} - log.debug('The filters criteria %s matched the following subnets:%s', - filter_parameters, subnets) + log.debug( + "The filters criteria %s matched the following subnets:%s", + filter_parameters, + subnets, + ) if subnets: - log.info('Subnet %s exists.', subnet_name or subnet_id) - return {'exists': True} + log.info("Subnet %s exists.", subnet_name or subnet_id) + return {"exists": True} else: - log.info('Subnet %s does not exist.', subnet_name or subnet_id) - return {'exists': False} + log.info("Subnet %s does not exist.", subnet_name or subnet_id) + return {"exists": False} -def get_subnet_association(subnets, region=None, key=None, keyid=None, - profile=None): - ''' +def get_subnet_association(subnets, region=None, key=None, keyid=None, profile=None): + """ Given a subnet (aka: a vpc zone identifier) or list of subnets, returns vpc association. @@ -989,35 +1244,37 @@ def get_subnet_association(subnets, region=None, key=None, keyid=None, salt myminion boto_vpc.get_subnet_association ['subnet-61b47516','subnet-2cb9785b'] - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) # subnet_ids=subnets can accept either a string or a list subnets = conn.get_all_subnets(subnet_ids=subnets) except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} # using a set to store vpc_ids - the use of set prevents duplicate # vpc_id values vpc_ids = set() for subnet in subnets: - log.debug('examining subnet id: %s for vpc_id', subnet.id) + log.debug("examining subnet id: %s for vpc_id", subnet.id) if subnet in subnets: - log.debug('subnet id: %s is associated with vpc id: %s', - subnet.id, subnet.vpc_id) + log.debug( + "subnet id: %s is associated with vpc id: %s", subnet.id, subnet.vpc_id + ) vpc_ids.add(subnet.vpc_id) if not vpc_ids: - return {'vpc_id': None} + return {"vpc_id": None} elif len(vpc_ids) == 1: - return {'vpc_id': vpc_ids.pop()} + return {"vpc_id": vpc_ids.pop()} else: - return {'vpc_ids': list(vpc_ids)} + return {"vpc_ids": list(vpc_ids)} -def describe_subnet(subnet_id=None, subnet_name=None, region=None, - key=None, keyid=None, profile=None): - ''' +def describe_subnet( + subnet_id=None, subnet_name=None, region=None, key=None, keyid=None, profile=None +): + """ Given a subnet id or name, describe its properties. Returns a dictionary of interesting properties. @@ -1031,31 +1288,53 @@ def describe_subnet(subnet_id=None, subnet_name=None, region=None, salt myminion boto_vpc.describe_subnet subnet_id=subnet-123456 salt myminion boto_vpc.describe_subnet subnet_name=mysubnet - ''' + """ try: - subnet = _get_resource('subnet', name=subnet_name, resource_id=subnet_id, - region=region, key=key, keyid=keyid, profile=profile) + subnet = _get_resource( + "subnet", + name=subnet_name, + resource_id=subnet_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} if not subnet: - return {'subnet': None} - log.debug('Found subnet: %s', subnet.id) + return {"subnet": None} + log.debug("Found subnet: %s", subnet.id) - keys = ('id', 'cidr_block', 'availability_zone', 'tags', 'vpc_id') - ret = {'subnet': dict((k, getattr(subnet, k)) for k in keys)} - explicit_route_table_assoc = _get_subnet_explicit_route_table(ret['subnet']['id'], - ret['subnet']['vpc_id'], - conn=None, region=region, - key=key, keyid=keyid, profile=profile) + keys = ("id", "cidr_block", "availability_zone", "tags", "vpc_id") + ret = {"subnet": dict((k, getattr(subnet, k)) for k in keys)} + explicit_route_table_assoc = _get_subnet_explicit_route_table( + ret["subnet"]["id"], + ret["subnet"]["vpc_id"], + conn=None, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if explicit_route_table_assoc: - ret['subnet']['explicit_route_table_association_id'] = explicit_route_table_assoc + ret["subnet"][ + "explicit_route_table_association_id" + ] = explicit_route_table_assoc return ret -def describe_subnets(subnet_ids=None, subnet_names=None, vpc_id=None, cidr=None, - region=None, key=None, keyid=None, profile=None): - ''' +def describe_subnets( + subnet_ids=None, + subnet_names=None, + vpc_id=None, + cidr=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a VPC ID or subnet CIDR, returns a list of associated subnets and their details. Return all subnets if VPC ID or CIDR are not provided. If a subnet id or CIDR is provided, only its associated subnet details will be @@ -1081,49 +1360,63 @@ def describe_subnets(subnet_ids=None, subnet_names=None, vpc_id=None, cidr=None, salt myminion boto_vpc.describe_subnets cidr=10.0.0.0/21 - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if vpc_id: - filter_parameters['filters']['vpcId'] = vpc_id + filter_parameters["filters"]["vpcId"] = vpc_id if cidr: - filter_parameters['filters']['cidrBlock'] = cidr + filter_parameters["filters"]["cidrBlock"] = cidr if subnet_names: - filter_parameters['filters']['tag:Name'] = subnet_names + filter_parameters["filters"]["tag:Name"] = subnet_names subnets = conn.get_all_subnets(subnet_ids=subnet_ids, **filter_parameters) - log.debug('The filters criteria %s matched the following subnets: %s', - filter_parameters, subnets) + log.debug( + "The filters criteria %s matched the following subnets: %s", + filter_parameters, + subnets, + ) if not subnets: - return {'subnets': None} + return {"subnets": None} subnets_list = [] - keys = ('id', 'cidr_block', 'availability_zone', 'tags', 'vpc_id') + keys = ("id", "cidr_block", "availability_zone", "tags", "vpc_id") for item in subnets: subnet = {} for key in keys: if hasattr(item, key): subnet[key] = getattr(item, key) - explicit_route_table_assoc = _get_subnet_explicit_route_table(subnet['id'], subnet['vpc_id'], conn=conn) + explicit_route_table_assoc = _get_subnet_explicit_route_table( + subnet["id"], subnet["vpc_id"], conn=conn + ) if explicit_route_table_assoc: - subnet['explicit_route_table_association_id'] = explicit_route_table_assoc + subnet[ + "explicit_route_table_association_id" + ] = explicit_route_table_assoc subnets_list.append(subnet) - return {'subnets': subnets_list} + return {"subnets": subnets_list} except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} -def create_internet_gateway(internet_gateway_name=None, vpc_id=None, - vpc_name=None, tags=None, region=None, key=None, - keyid=None, profile=None): - ''' +def create_internet_gateway( + internet_gateway_name=None, + vpc_id=None, + vpc_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create an Internet Gateway, optionally attaching it to an existing VPC. Returns the internet gateway id if the internet gateway was created and @@ -1138,35 +1431,49 @@ def create_internet_gateway(internet_gateway_name=None, vpc_id=None, salt myminion boto_vpc.create_internet_gateway \\ internet_gateway_name=myigw vpc_name=myvpc - ''' + """ try: if vpc_id or vpc_name: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: - return {'created': False, - 'error': {'message': 'VPC {0} does not exist.'.format(vpc_name or vpc_id)}} + return { + "created": False, + "error": { + "message": "VPC {0} does not exist.".format(vpc_name or vpc_id) + }, + } - r = _create_resource('internet_gateway', name=internet_gateway_name, - tags=tags, region=region, key=key, keyid=keyid, - profile=profile) - if r.get('created') and vpc_id: + r = _create_resource( + "internet_gateway", + name=internet_gateway_name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if r.get("created") and vpc_id: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.attach_internet_gateway(r['id'], vpc_id) + conn.attach_internet_gateway(r["id"], vpc_id) log.info( - 'Attached internet gateway %s to VPC %s', - r['id'], vpc_name or vpc_id + "Attached internet gateway %s to VPC %s", r["id"], vpc_name or vpc_id ) return r except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} -def delete_internet_gateway(internet_gateway_id=None, - internet_gateway_name=None, - detach=False, region=None, - key=None, keyid=None, profile=None): - ''' +def delete_internet_gateway( + internet_gateway_id=None, + internet_gateway_name=None, + detach=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Delete an internet gateway (by name or id). Returns True if the internet gateway was deleted and otherwise False. @@ -1180,88 +1487,128 @@ def delete_internet_gateway(internet_gateway_id=None, salt myminion boto_vpc.delete_internet_gateway internet_gateway_id=igw-1a2b3c salt myminion boto_vpc.delete_internet_gateway internet_gateway_name=myigw - ''' + """ try: if internet_gateway_name: - internet_gateway_id = _get_resource_id('internet_gateway', - internet_gateway_name, - region=region, key=key, - keyid=keyid, profile=profile) + internet_gateway_id = _get_resource_id( + "internet_gateway", + internet_gateway_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not internet_gateway_id: - return {'deleted': False, 'error': { - 'message': 'internet gateway {0} does not exist.'.format( - internet_gateway_name)}} + return { + "deleted": False, + "error": { + "message": "internet gateway {0} does not exist.".format( + internet_gateway_name + ) + }, + } if detach: - igw = _get_resource('internet_gateway', - resource_id=internet_gateway_id, region=region, - key=key, keyid=keyid, profile=profile) + igw = _get_resource( + "internet_gateway", + resource_id=internet_gateway_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not igw: - return {'deleted': False, 'error': { - 'message': 'internet gateway {0} does not exist.'.format( - internet_gateway_id)}} + return { + "deleted": False, + "error": { + "message": "internet gateway {0} does not exist.".format( + internet_gateway_id + ) + }, + } if igw.attachments: - conn = _get_conn(region=region, key=key, keyid=keyid, - profile=profile) - conn.detach_internet_gateway(internet_gateway_id, - igw.attachments[0].vpc_id) - return _delete_resource('internet_gateway', - resource_id=internet_gateway_id, - region=region, key=key, keyid=keyid, - profile=profile) + conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) + conn.detach_internet_gateway( + internet_gateway_id, igw.attachments[0].vpc_id + ) + return _delete_resource( + "internet_gateway", + resource_id=internet_gateway_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) except BotoServerError as e: - return {'deleted': False, 'error': __utils__['boto.get_error'](e)} + return {"deleted": False, "error": __utils__["boto.get_error"](e)} -def _find_nat_gateways(nat_gateway_id=None, subnet_id=None, subnet_name=None, vpc_id=None, vpc_name=None, - states=('pending', 'available'), - region=None, key=None, keyid=None, profile=None): - ''' +def _find_nat_gateways( + nat_gateway_id=None, + subnet_id=None, + subnet_name=None, + vpc_id=None, + vpc_name=None, + states=("pending", "available"), + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given gateway properties, find and return matching nat gateways - ''' + """ if not any((nat_gateway_id, subnet_id, subnet_name, vpc_id, vpc_name)): - raise SaltInvocationError('At least one of the following must be ' - 'provided: nat_gateway_id, subnet_id, ' - 'subnet_name, vpc_id, or vpc_name.') - filter_parameters = {'Filter': []} + raise SaltInvocationError( + "At least one of the following must be " + "provided: nat_gateway_id, subnet_id, " + "subnet_name, vpc_id, or vpc_name." + ) + filter_parameters = {"Filter": []} if nat_gateway_id: - filter_parameters['NatGatewayIds'] = [nat_gateway_id] + filter_parameters["NatGatewayIds"] = [nat_gateway_id] if subnet_name: - subnet_id = _get_resource_id('subnet', subnet_name, - region=region, key=key, - keyid=keyid, profile=profile) + subnet_id = _get_resource_id( + "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile + ) if not subnet_id: return False if subnet_id: - filter_parameters['Filter'].append({'Name': 'subnet-id', 'Values': [subnet_id]}) + filter_parameters["Filter"].append({"Name": "subnet-id", "Values": [subnet_id]}) if vpc_name: - vpc_id = _get_resource_id('vpc', vpc_name, - region=region, key=key, - keyid=keyid, profile=profile) + vpc_id = _get_resource_id( + "vpc", vpc_name, region=region, key=key, keyid=keyid, profile=profile + ) if not vpc_id: return False if vpc_id: - filter_parameters['Filter'].append({'Name': 'vpc-id', 'Values': [vpc_id]}) + filter_parameters["Filter"].append({"Name": "vpc-id", "Values": [vpc_id]}) conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) nat_gateways = [] - for ret in __utils__['boto3.paged_call'](conn3.describe_nat_gateways, - marker_flag='NextToken', marker_arg='NextToken', - **filter_parameters): - for gw in ret.get('NatGateways', []): - if gw.get('State') in states: + for ret in __utils__["boto3.paged_call"]( + conn3.describe_nat_gateways, + marker_flag="NextToken", + marker_arg="NextToken", + **filter_parameters + ): + for gw in ret.get("NatGateways", []): + if gw.get("State") in states: nat_gateways.append(gw) - log.debug('The filters criteria %s matched the following nat gateways: %s', - filter_parameters, nat_gateways) + log.debug( + "The filters criteria %s matched the following nat gateways: %s", + filter_parameters, + nat_gateways, + ) if nat_gateways: return nat_gateways @@ -1269,11 +1616,19 @@ def _find_nat_gateways(nat_gateway_id=None, subnet_id=None, subnet_name=None, vp return False -def nat_gateway_exists(nat_gateway_id=None, subnet_id=None, subnet_name=None, - vpc_id=None, vpc_name=None, - states=('pending', 'available'), - region=None, key=None, keyid=None, profile=None): - ''' +def nat_gateway_exists( + nat_gateway_id=None, + subnet_id=None, + subnet_name=None, + vpc_id=None, + vpc_name=None, + states=("pending", "available"), + region=None, + key=None, + keyid=None, + profile=None, +): + """ Checks if a nat gateway exists. This function requires boto3 to be installed. @@ -1287,23 +1642,37 @@ def nat_gateway_exists(nat_gateway_id=None, subnet_id=None, subnet_name=None, salt myminion boto_vpc.nat_gateway_exists nat_gateway_id='nat-03b02643b43216fe7' salt myminion boto_vpc.nat_gateway_exists subnet_id='subnet-5b05942d' - ''' + """ - return bool(_find_nat_gateways(nat_gateway_id=nat_gateway_id, - subnet_id=subnet_id, - subnet_name=subnet_name, - vpc_id=vpc_id, - vpc_name=vpc_name, - states=states, - region=region, key=key, keyid=keyid, - profile=profile)) + return bool( + _find_nat_gateways( + nat_gateway_id=nat_gateway_id, + subnet_id=subnet_id, + subnet_name=subnet_name, + vpc_id=vpc_id, + vpc_name=vpc_name, + states=states, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ) -def describe_nat_gateways(nat_gateway_id=None, subnet_id=None, subnet_name=None, - vpc_id=None, vpc_name=None, - states=('pending', 'available'), - region=None, key=None, keyid=None, profile=None): - ''' +def describe_nat_gateways( + nat_gateway_id=None, + subnet_id=None, + subnet_name=None, + vpc_id=None, + vpc_name=None, + states=("pending", "available"), + region=None, + key=None, + keyid=None, + profile=None, +): + """ Return a description of nat gateways matching the selection criteria This function requires boto3 to be installed. @@ -1315,22 +1684,32 @@ def describe_nat_gateways(nat_gateway_id=None, subnet_id=None, subnet_name=None, salt myminion boto_vpc.describe_nat_gateways nat_gateway_id='nat-03b02643b43216fe7' salt myminion boto_vpc.describe_nat_gateways subnet_id='subnet-5b05942d' - ''' + """ - return _find_nat_gateways(nat_gateway_id=nat_gateway_id, - subnet_id=subnet_id, - subnet_name=subnet_name, - vpc_id=vpc_id, - vpc_name=vpc_name, - states=states, - region=region, key=key, keyid=keyid, - profile=profile) + return _find_nat_gateways( + nat_gateway_id=nat_gateway_id, + subnet_id=subnet_id, + subnet_name=subnet_name, + vpc_id=vpc_id, + vpc_name=vpc_name, + states=states, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def create_nat_gateway(subnet_id=None, - subnet_name=None, allocation_id=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_nat_gateway( + subnet_id=None, + subnet_name=None, + allocation_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Create a NAT Gateway within an existing subnet. If allocation_id is specified, the elastic IP address it references is associated with the gateway. Otherwise, a new allocation_id is created and used. @@ -1348,43 +1727,69 @@ def create_nat_gateway(subnet_id=None, salt myminion boto_vpc.create_nat_gateway subnet_name=mysubnet - ''' + """ try: if all((subnet_id, subnet_name)): - raise SaltInvocationError('Only one of subnet_name or subnet_id may be ' - 'provided.') + raise SaltInvocationError( + "Only one of subnet_name or subnet_id may be " "provided." + ) if subnet_name: - subnet_id = _get_resource_id('subnet', subnet_name, - region=region, key=key, - keyid=keyid, profile=profile) + subnet_id = _get_resource_id( + "subnet", + subnet_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not subnet_id: - return {'created': False, - 'error': {'message': 'Subnet {0} does not exist.'.format(subnet_name)}} + return { + "created": False, + "error": { + "message": "Subnet {0} does not exist.".format(subnet_name) + }, + } else: - if not _get_resource('subnet', resource_id=subnet_id, - region=region, key=key, keyid=keyid, profile=profile): - return {'created': False, - 'error': {'message': 'Subnet {0} does not exist.'.format(subnet_id)}} + if not _get_resource( + "subnet", + resource_id=subnet_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + return { + "created": False, + "error": { + "message": "Subnet {0} does not exist.".format(subnet_id) + }, + } conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if not allocation_id: - address = conn3.allocate_address(Domain='vpc') - allocation_id = address.get('AllocationId') + address = conn3.allocate_address(Domain="vpc") + allocation_id = address.get("AllocationId") # Have to go to boto3 to create NAT gateway r = conn3.create_nat_gateway(SubnetId=subnet_id, AllocationId=allocation_id) - return {'created': True, 'id': r.get('NatGateway', {}).get('NatGatewayId')} + return {"created": True, "id": r.get("NatGateway", {}).get("NatGatewayId")} except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} -def delete_nat_gateway(nat_gateway_id, - release_eips=False, region=None, - key=None, keyid=None, profile=None, - wait_for_delete=False, wait_for_delete_retries=5): - ''' +def delete_nat_gateway( + nat_gateway_id, + release_eips=False, + region=None, + key=None, + keyid=None, + profile=None, + wait_for_delete=False, + wait_for_delete_retries=5, +): + """ Delete a nat gateway (by id). Returns True if the internet gateway was deleted and otherwise False. @@ -1428,38 +1833,49 @@ def delete_nat_gateway(nat_gateway_id, salt myminion boto_vpc.delete_nat_gateway nat_gateway_id=igw-1a2b3c - ''' + """ try: conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) gwinfo = conn3.describe_nat_gateways(NatGatewayIds=[nat_gateway_id]) if gwinfo: - gwinfo = gwinfo.get('NatGateways', [None])[0] + gwinfo = gwinfo.get("NatGateways", [None])[0] conn3.delete_nat_gateway(NatGatewayId=nat_gateway_id) # wait for deleting nat gateway to finish prior to attempt to release elastic ips if wait_for_delete: for retry in range(wait_for_delete_retries, 0, -1): - if gwinfo and gwinfo['State'] not in ['deleted', 'failed']: - time.sleep((2 ** (wait_for_delete_retries - retry)) + (random.randint(0, 1000) / 1000.0)) + if gwinfo and gwinfo["State"] not in ["deleted", "failed"]: + time.sleep( + (2 ** (wait_for_delete_retries - retry)) + + (random.randint(0, 1000) / 1000.0) + ) gwinfo = conn3.describe_nat_gateways(NatGatewayIds=[nat_gateway_id]) if gwinfo: - gwinfo = gwinfo.get('NatGateways', [None])[0] + gwinfo = gwinfo.get("NatGateways", [None])[0] continue break if release_eips and gwinfo: - for addr in gwinfo.get('NatGatewayAddresses'): - conn3.release_address(AllocationId=addr.get('AllocationId')) - return {'deleted': True} + for addr in gwinfo.get("NatGatewayAddresses"): + conn3.release_address(AllocationId=addr.get("AllocationId")) + return {"deleted": True} except BotoServerError as e: - return {'deleted': False, 'error': __utils__['boto.get_error'](e)} + return {"deleted": False, "error": __utils__["boto.get_error"](e)} -def create_customer_gateway(vpn_connection_type, ip_address, bgp_asn, - customer_gateway_name=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_customer_gateway( + vpn_connection_type, + ip_address, + bgp_asn, + customer_gateway_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a valid VPN connection type, a static IP address and a customer gateway’s Border Gateway Protocol (BGP) Autonomous System Number, create a customer gateway. @@ -1473,18 +1889,31 @@ def create_customer_gateway(vpn_connection_type, ip_address, bgp_asn, salt myminion boto_vpc.create_customer_gateway 'ipsec.1', '12.1.2.3', 65534 - ''' + """ - return _create_resource('customer_gateway', customer_gateway_name, - type=vpn_connection_type, - ip_address=ip_address, bgp_asn=bgp_asn, - tags=tags, region=region, key=key, - keyid=keyid, profile=profile) + return _create_resource( + "customer_gateway", + customer_gateway_name, + type=vpn_connection_type, + ip_address=ip_address, + bgp_asn=bgp_asn, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def delete_customer_gateway(customer_gateway_id=None, customer_gateway_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_customer_gateway( + customer_gateway_id=None, + customer_gateway_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a customer gateway ID or name, delete the customer gateway. Returns True if the customer gateway was deleted and returns False if the customer gateway was not deleted. @@ -1498,18 +1927,28 @@ def delete_customer_gateway(customer_gateway_id=None, customer_gateway_name=None salt myminion boto_vpc.delete_customer_gateway 'cgw-b6a247df' - ''' + """ - return _delete_resource(resource='customer_gateway', - name=customer_gateway_name, - resource_id=customer_gateway_id, - region=region, key=key, - keyid=keyid, profile=profile) + return _delete_resource( + resource="customer_gateway", + name=customer_gateway_name, + resource_id=customer_gateway_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def customer_gateway_exists(customer_gateway_id=None, customer_gateway_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def customer_gateway_exists( + customer_gateway_id=None, + customer_gateway_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a customer gateway ID, check if the customer gateway ID exists. Returns True if the customer gateway ID exists; Returns False otherwise. @@ -1521,18 +1960,35 @@ def customer_gateway_exists(customer_gateway_id=None, customer_gateway_name=None salt myminion boto_vpc.customer_gateway_exists cgw-b6a247df salt myminion boto_vpc.customer_gateway_exists customer_gatway_name=mycgw - ''' + """ - return resource_exists('customer_gateway', name=customer_gateway_name, - resource_id=customer_gateway_id, - region=region, key=key, keyid=keyid, profile=profile) + return resource_exists( + "customer_gateway", + name=customer_gateway_name, + resource_id=customer_gateway_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def create_dhcp_options(domain_name=None, domain_name_servers=None, ntp_servers=None, - netbios_name_servers=None, netbios_node_type=None, - dhcp_options_name=None, tags=None, vpc_id=None, vpc_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_dhcp_options( + domain_name=None, + domain_name_servers=None, + ntp_servers=None, + netbios_name_servers=None, + netbios_node_type=None, + dhcp_options_name=None, + tags=None, + vpc_id=None, + vpc_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given valid DHCP options, create a DHCP options record, optionally associating it with an existing VPC. @@ -1550,36 +2006,50 @@ def create_dhcp_options(domain_name=None, domain_name_servers=None, ntp_servers= netbios_name_servers='[10.0.0.1]' netbios_node_type=1 \\ vpc_name='myvpc' - ''' + """ try: if vpc_id or vpc_name: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: - return {'created': False, - 'error': {'message': 'VPC {0} does not exist.'.format(vpc_name or vpc_id)}} + return { + "created": False, + "error": { + "message": "VPC {0} does not exist.".format(vpc_name or vpc_id) + }, + } - r = _create_resource('dhcp_options', name=dhcp_options_name, domain_name=domain_name, - domain_name_servers=domain_name_servers, - ntp_servers=ntp_servers, netbios_name_servers=netbios_name_servers, - netbios_node_type=netbios_node_type, - region=region, key=key, keyid=keyid, - profile=profile) - if r.get('created') and vpc_id: + r = _create_resource( + "dhcp_options", + name=dhcp_options_name, + domain_name=domain_name, + domain_name_servers=domain_name_servers, + ntp_servers=ntp_servers, + netbios_name_servers=netbios_name_servers, + netbios_node_type=netbios_node_type, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if r.get("created") and vpc_id: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - conn.associate_dhcp_options(r['id'], vpc_id) - log.info( - 'Associated options %s to VPC %s', - r['id'], vpc_name or vpc_id - ) + conn.associate_dhcp_options(r["id"], vpc_id) + log.info("Associated options %s to VPC %s", r["id"], vpc_name or vpc_id) return r except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} -def get_dhcp_options(dhcp_options_name=None, dhcp_options_id=None, - region=None, key=None, keyid=None, profile=None): - ''' +def get_dhcp_options( + dhcp_options_name=None, + dhcp_options_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Return a dict with the current values of the requested DHCP options set CLI Example: @@ -1589,36 +2059,54 @@ def get_dhcp_options(dhcp_options_name=None, dhcp_options_id=None, salt myminion boto_vpc.get_dhcp_options 'myfunnydhcpoptionsname' .. versionadded:: 2016.3.0 - ''' + """ if not any((dhcp_options_name, dhcp_options_id)): - raise SaltInvocationError('At least one of the following must be specified: ' - 'dhcp_options_name, dhcp_options_id.') + raise SaltInvocationError( + "At least one of the following must be specified: " + "dhcp_options_name, dhcp_options_id." + ) if not dhcp_options_id and dhcp_options_name: - dhcp_options_id = _get_resource_id('dhcp_options', dhcp_options_name, - region=region, key=key, - keyid=keyid, profile=profile) + dhcp_options_id = _get_resource_id( + "dhcp_options", + dhcp_options_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not dhcp_options_id: - return {'dhcp_options': {}} + return {"dhcp_options": {}} try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) r = conn.get_all_dhcp_options(dhcp_options_ids=[dhcp_options_id]) except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} if not r: - return {'dhcp_options': None} + return {"dhcp_options": None} - keys = ('domain_name', 'domain_name_servers', 'ntp_servers', - 'netbios_name_servers', 'netbios_node_type') + keys = ( + "domain_name", + "domain_name_servers", + "ntp_servers", + "netbios_name_servers", + "netbios_node_type", + ) - return {'dhcp_options': dict((k, r[0].options.get(k)) for k in keys)} + return {"dhcp_options": dict((k, r[0].options.get(k)) for k in keys)} -def delete_dhcp_options(dhcp_options_id=None, dhcp_options_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_dhcp_options( + dhcp_options_id=None, + dhcp_options_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Delete dhcp options by id or name. .. versionadded:: 2015.8.0 @@ -1629,18 +2117,29 @@ def delete_dhcp_options(dhcp_options_id=None, dhcp_options_name=None, salt myminion boto_vpc.delete_dhcp_options 'dopt-b6a247df' - ''' + """ - return _delete_resource(resource='dhcp_options', - name=dhcp_options_name, - resource_id=dhcp_options_id, - region=region, key=key, - keyid=keyid, profile=profile) + return _delete_resource( + resource="dhcp_options", + name=dhcp_options_name, + resource_id=dhcp_options_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def associate_dhcp_options_to_vpc(dhcp_options_id, vpc_id=None, vpc_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def associate_dhcp_options_to_vpc( + dhcp_options_id, + vpc_id=None, + vpc_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given valid DHCP options id and a valid VPC id, associate the DHCP options record with the VPC. Returns True if the DHCP options record were associated and returns False if the DHCP options record was not associated. @@ -1651,29 +2150,50 @@ def associate_dhcp_options_to_vpc(dhcp_options_id, vpc_id=None, vpc_name=None, salt myminion boto_vpc.associate_dhcp_options_to_vpc 'dhcp-a0bl34pp' 'vpc-6b1fe402' - ''' + """ try: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: - return {'associated': False, - 'error': {'message': 'VPC {0} does not exist.'.format(vpc_name or vpc_id)}} + return { + "associated": False, + "error": { + "message": "VPC {0} does not exist.".format(vpc_name or vpc_id) + }, + } conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if conn.associate_dhcp_options(dhcp_options_id, vpc_id): - log.info('DHCP options with id %s were associated with VPC %s', - dhcp_options_id, vpc_id) - return {'associated': True} + log.info( + "DHCP options with id %s were associated with VPC %s", + dhcp_options_id, + vpc_id, + ) + return {"associated": True} else: - log.warning('DHCP options with id %s were not associated with VPC %s', - dhcp_options_id, vpc_id) - return {'associated': False, 'error': {'message': 'DHCP options could not be associated.'}} + log.warning( + "DHCP options with id %s were not associated with VPC %s", + dhcp_options_id, + vpc_id, + ) + return { + "associated": False, + "error": {"message": "DHCP options could not be associated."}, + } except BotoServerError as e: - return {'associated': False, 'error': __utils__['boto.get_error'](e)} + return {"associated": False, "error": __utils__["boto.get_error"](e)} -def dhcp_options_exists(dhcp_options_id=None, name=None, dhcp_options_name=None, - tags=None, region=None, key=None, keyid=None, profile=None): - ''' +def dhcp_options_exists( + dhcp_options_id=None, + name=None, + dhcp_options_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Check if a dhcp option exists. Returns True if the dhcp option exists; Returns False otherwise. @@ -1684,23 +2204,40 @@ def dhcp_options_exists(dhcp_options_id=None, name=None, dhcp_options_name=None, salt myminion boto_vpc.dhcp_options_exists dhcp_options_id='dhcp-a0bl34pp' - ''' + """ if name: - log.warning('boto_vpc.dhcp_options_exists: name parameter is deprecated ' - 'use dhcp_options_name instead.') + log.warning( + "boto_vpc.dhcp_options_exists: name parameter is deprecated " + "use dhcp_options_name instead." + ) dhcp_options_name = name - return resource_exists('dhcp_options', name=dhcp_options_name, - resource_id=dhcp_options_id, tags=tags, - region=region, key=key, keyid=keyid, - profile=profile) + return resource_exists( + "dhcp_options", + name=dhcp_options_name, + resource_id=dhcp_options_id, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def create_network_acl(vpc_id=None, vpc_name=None, network_acl_name=None, - subnet_id=None, subnet_name=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_network_acl( + vpc_id=None, + vpc_name=None, + network_acl_name=None, + subnet_id=None, + subnet_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a vpc_id, creates a network acl. Returns the network acl id if successful, otherwise returns False. @@ -1714,52 +2251,78 @@ def create_network_acl(vpc_id=None, vpc_name=None, network_acl_name=None, salt myminion boto_vpc.create_network_acl 'vpc-6b1fe402' - ''' + """ _id = vpc_name or vpc_id try: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} if not vpc_id: - return {'created': False, - 'error': {'message': 'VPC {0} does not exist.'.format(_id)}} + return { + "created": False, + "error": {"message": "VPC {0} does not exist.".format(_id)}, + } if all((subnet_id, subnet_name)): - raise SaltInvocationError('Only one of subnet_name or subnet_id may be ' - 'provided.') + raise SaltInvocationError( + "Only one of subnet_name or subnet_id may be " "provided." + ) if subnet_name: - subnet_id = _get_resource_id('subnet', subnet_name, - region=region, key=key, - keyid=keyid, profile=profile) + subnet_id = _get_resource_id( + "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile + ) if not subnet_id: - return {'created': False, - 'error': {'message': 'Subnet {0} does not exist.'.format(subnet_name)}} + return { + "created": False, + "error": {"message": "Subnet {0} does not exist.".format(subnet_name)}, + } elif subnet_id: - if not _get_resource('subnet', resource_id=subnet_id, - region=region, key=key, keyid=keyid, profile=profile): - return {'created': False, - 'error': {'message': 'Subnet {0} does not exist.'.format(subnet_id)}} + if not _get_resource( + "subnet", + resource_id=subnet_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + return { + "created": False, + "error": {"message": "Subnet {0} does not exist.".format(subnet_id)}, + } - r = _create_resource('network_acl', name=network_acl_name, vpc_id=vpc_id, - region=region, key=key, keyid=keyid, - profile=profile) + r = _create_resource( + "network_acl", + name=network_acl_name, + vpc_id=vpc_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) - if r.get('created') and subnet_id: + if r.get("created") and subnet_id: try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - association_id = conn.associate_network_acl(r['id'], subnet_id) + association_id = conn.associate_network_acl(r["id"], subnet_id) except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} - r['association_id'] = association_id + return {"created": False, "error": __utils__["boto.get_error"](e)} + r["association_id"] = association_id return r -def delete_network_acl(network_acl_id=None, network_acl_name=None, disassociate=False, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_network_acl( + network_acl_id=None, + network_acl_name=None, + disassociate=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Delete a network acl based on the network_acl_id or network_acl_name provided. CLI Examples: @@ -1774,10 +2337,17 @@ def delete_network_acl(network_acl_id=None, network_acl_name=None, disassociate= salt myminion boto_vpc.delete_network_acl network_acl_name='myacl' \\ disassociate=true - ''' + """ if disassociate: - network_acl = _get_resource('network_acl', name=network_acl_name, region=region, key=key, keyid=keyid, profile=profile) + network_acl = _get_resource( + "network_acl", + name=network_acl_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if network_acl and network_acl.associations: subnet_id = network_acl.associations[0].subnet_id try: @@ -1786,17 +2356,28 @@ def delete_network_acl(network_acl_id=None, network_acl_name=None, disassociate= except BotoServerError: pass - return _delete_resource(resource='network_acl', - name=network_acl_name, - resource_id=network_acl_id, - region=region, key=key, - keyid=keyid, profile=profile) + return _delete_resource( + resource="network_acl", + name=network_acl_name, + resource_id=network_acl_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def network_acl_exists(network_acl_id=None, name=None, network_acl_name=None, - tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def network_acl_exists( + network_acl_id=None, + name=None, + network_acl_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Checks if a network acl exists. Returns True if the network acl exists or returns False if it doesn't exist. @@ -1806,24 +2387,38 @@ def network_acl_exists(network_acl_id=None, name=None, network_acl_name=None, .. code-block:: bash salt myminion boto_vpc.network_acl_exists network_acl_id='acl-5fb85d36' - ''' + """ if name: - log.warning('boto_vpc.network_acl_exists: name parameter is deprecated ' - 'use network_acl_name instead.') + log.warning( + "boto_vpc.network_acl_exists: name parameter is deprecated " + "use network_acl_name instead." + ) network_acl_name = name - return resource_exists('network_acl', name=network_acl_name, - resource_id=network_acl_id, tags=tags, - region=region, key=key, keyid=keyid, - profile=profile) + return resource_exists( + "network_acl", + name=network_acl_name, + resource_id=network_acl_id, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def associate_network_acl_to_subnet(network_acl_id=None, subnet_id=None, - network_acl_name=None, - subnet_name=None, region=None, - key=None, keyid=None, profile=None): - ''' +def associate_network_acl_to_subnet( + network_acl_id=None, + subnet_id=None, + network_acl_name=None, + subnet_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a network acl and subnet ids or names, associate a network acl to a subnet. CLI Example: @@ -1838,41 +2433,71 @@ def associate_network_acl_to_subnet(network_acl_id=None, subnet_id=None, salt myminion boto_vpc.associate_network_acl_to_subnet \\ network_acl_id='myacl' subnet_id='mysubnet' - ''' + """ if network_acl_name: - network_acl_id = _get_resource_id('network_acl', network_acl_name, - region=region, key=key, - keyid=keyid, profile=profile) + network_acl_id = _get_resource_id( + "network_acl", + network_acl_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not network_acl_id: - return {'associated': False, - 'error': {'message': 'Network ACL {0} does not exist.'.format(network_acl_name)}} + return { + "associated": False, + "error": { + "message": "Network ACL {0} does not exist.".format( + network_acl_name + ) + }, + } if subnet_name: - subnet_id = _get_resource_id('subnet', subnet_name, - region=region, key=key, - keyid=keyid, profile=profile) + subnet_id = _get_resource_id( + "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile + ) if not subnet_id: - return {'associated': False, - 'error': {'message': 'Subnet {0} does not exist.'.format(subnet_name)}} + return { + "associated": False, + "error": {"message": "Subnet {0} does not exist.".format(subnet_name)}, + } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) association_id = conn.associate_network_acl(network_acl_id, subnet_id) if association_id: - log.info('Network ACL with id %s was associated with subnet %s', - network_acl_id, subnet_id) + log.info( + "Network ACL with id %s was associated with subnet %s", + network_acl_id, + subnet_id, + ) - return {'associated': True, 'id': association_id} + return {"associated": True, "id": association_id} else: - log.warning('Network ACL with id %s was not associated with subnet %s', - network_acl_id, subnet_id) - return {'associated': False, 'error': {'message': 'ACL could not be assocaited.'}} + log.warning( + "Network ACL with id %s was not associated with subnet %s", + network_acl_id, + subnet_id, + ) + return { + "associated": False, + "error": {"message": "ACL could not be assocaited."}, + } except BotoServerError as e: - return {'associated': False, 'error': __utils__['boto.get_error'](e)} + return {"associated": False, "error": __utils__["boto.get_error"](e)} -def disassociate_network_acl(subnet_id=None, vpc_id=None, subnet_name=None, vpc_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def disassociate_network_acl( + subnet_id=None, + vpc_id=None, + subnet_name=None, + vpc_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a subnet ID, disassociates a network acl. CLI Example: @@ -1881,62 +2506,97 @@ def disassociate_network_acl(subnet_id=None, vpc_id=None, subnet_name=None, vpc_ salt myminion boto_vpc.disassociate_network_acl 'subnet-6a1fe403' - ''' + """ if not _exactly_one((subnet_name, subnet_id)): - raise SaltInvocationError('One (but not both) of subnet_id or subnet_name ' - 'must be provided.') + raise SaltInvocationError( + "One (but not both) of subnet_id or subnet_name " "must be provided." + ) if all((vpc_name, vpc_id)): - raise SaltInvocationError('Only one of vpc_id or vpc_name ' - 'may be provided.') + raise SaltInvocationError("Only one of vpc_id or vpc_name " "may be provided.") try: if subnet_name: - subnet_id = _get_resource_id('subnet', subnet_name, - region=region, key=key, - keyid=keyid, profile=profile) + subnet_id = _get_resource_id( + "subnet", + subnet_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not subnet_id: - return {'disassociated': False, - 'error': {'message': 'Subnet {0} does not exist.'.format(subnet_name)}} + return { + "disassociated": False, + "error": { + "message": "Subnet {0} does not exist.".format(subnet_name) + }, + } if vpc_name or vpc_id: vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) association_id = conn.disassociate_network_acl(subnet_id, vpc_id=vpc_id) - return {'disassociated': True, 'association_id': association_id} + return {"disassociated": True, "association_id": association_id} except BotoServerError as e: - return {'disassociated': False, 'error': __utils__['boto.get_error'](e)} + return {"disassociated": False, "error": __utils__["boto.get_error"](e)} -def _create_network_acl_entry(network_acl_id=None, rule_number=None, protocol=None, - rule_action=None, cidr_block=None, egress=None, - network_acl_name=None, icmp_code=None, icmp_type=None, - port_range_from=None, port_range_to=None, replace=False, - region=None, key=None, keyid=None, profile=None): +def _create_network_acl_entry( + network_acl_id=None, + rule_number=None, + protocol=None, + rule_action=None, + cidr_block=None, + egress=None, + network_acl_name=None, + icmp_code=None, + icmp_type=None, + port_range_from=None, + port_range_to=None, + replace=False, + region=None, + key=None, + keyid=None, + profile=None, +): if replace: - rkey = 'replaced' + rkey = "replaced" else: - rkey = 'created' + rkey = "created" if not _exactly_one((network_acl_name, network_acl_id)): - raise SaltInvocationError('One (but not both) of network_acl_id or ' - 'network_acl_name must be provided.') + raise SaltInvocationError( + "One (but not both) of network_acl_id or " + "network_acl_name must be provided." + ) - for v in ('rule_number', 'protocol', 'rule_action', 'cidr_block'): + for v in ("rule_number", "protocol", "rule_action", "cidr_block"): if locals()[v] is None: - raise SaltInvocationError('{0} is required.'.format(v)) + raise SaltInvocationError("{0} is required.".format(v)) if network_acl_name: - network_acl_id = _get_resource_id('network_acl', network_acl_name, - region=region, key=key, - keyid=keyid, profile=profile) + network_acl_id = _get_resource_id( + "network_acl", + network_acl_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not network_acl_id: - return {rkey: False, - 'error': {'message': 'Network ACL {0} does not exist.'.format(network_acl_name or network_acl_id)}} + return { + rkey: False, + "error": { + "message": "Network ACL {0} does not exist.".format( + network_acl_name or network_acl_id + ) + }, + } if isinstance(protocol, six.string_types): - if protocol == 'all': + if protocol == "all": protocol = -1 else: try: @@ -1949,25 +2609,45 @@ def _create_network_acl_entry(network_acl_id=None, rule_number=None, protocol=No f = conn.replace_network_acl_entry else: f = conn.create_network_acl_entry - created = f(network_acl_id, rule_number, protocol, rule_action, - cidr_block, egress=egress, icmp_code=icmp_code, - icmp_type=icmp_type, port_range_from=port_range_from, - port_range_to=port_range_to) + created = f( + network_acl_id, + rule_number, + protocol, + rule_action, + cidr_block, + egress=egress, + icmp_code=icmp_code, + icmp_type=icmp_type, + port_range_from=port_range_from, + port_range_to=port_range_to, + ) if created: - log.info('Network ACL entry was %s', rkey) + log.info("Network ACL entry was %s", rkey) else: - log.warning('Network ACL entry was not %s', rkey) + log.warning("Network ACL entry was not %s", rkey) return {rkey: created} except BotoServerError as e: - return {rkey: False, 'error': __utils__['boto.get_error'](e)} + return {rkey: False, "error": __utils__["boto.get_error"](e)} -def create_network_acl_entry(network_acl_id=None, rule_number=None, protocol=None, - rule_action=None, cidr_block=None, egress=None, - network_acl_name=None, icmp_code=None, icmp_type=None, - port_range_from=None, port_range_to=None, - region=None, key=None, keyid=None, profile=None): - ''' +def create_network_acl_entry( + network_acl_id=None, + rule_number=None, + protocol=None, + rule_action=None, + cidr_block=None, + egress=None, + network_acl_name=None, + icmp_code=None, + icmp_type=None, + port_range_from=None, + port_range_to=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates a network acl entry. CLI Example: @@ -1977,18 +2657,30 @@ def create_network_acl_entry(network_acl_id=None, rule_number=None, protocol=Non salt myminion boto_vpc.create_network_acl_entry 'acl-5fb85d36' '32767' \\ 'all' 'deny' '0.0.0.0/0' egress=true - ''' + """ kwargs = locals() return _create_network_acl_entry(**kwargs) -def replace_network_acl_entry(network_acl_id=None, rule_number=None, protocol=None, - rule_action=None, cidr_block=None, egress=None, - network_acl_name=None, icmp_code=None, icmp_type=None, - port_range_from=None, port_range_to=None, - region=None, key=None, keyid=None, profile=None): - ''' +def replace_network_acl_entry( + network_acl_id=None, + rule_number=None, + protocol=None, + rule_action=None, + cidr_block=None, + egress=None, + network_acl_name=None, + icmp_code=None, + icmp_type=None, + port_range_from=None, + port_range_to=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Replaces a network acl entry. @@ -1999,16 +2691,23 @@ def replace_network_acl_entry(network_acl_id=None, rule_number=None, protocol=No salt myminion boto_vpc.replace_network_acl_entry 'acl-5fb85d36' '32767' \\ 'all' 'deny' '0.0.0.0/0' egress=true - ''' + """ kwargs = locals() return _create_network_acl_entry(replace=True, **kwargs) -def delete_network_acl_entry(network_acl_id=None, rule_number=None, egress=None, - network_acl_name=None, region=None, key=None, keyid=None, - profile=None): - ''' +def delete_network_acl_entry( + network_acl_id=None, + rule_number=None, + egress=None, + network_acl_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Deletes a network acl entry. CLI Example: @@ -2017,37 +2716,60 @@ def delete_network_acl_entry(network_acl_id=None, rule_number=None, egress=None, salt myminion boto_vpc.delete_network_acl_entry 'acl-5fb85d36' '32767' - ''' + """ if not _exactly_one((network_acl_name, network_acl_id)): - raise SaltInvocationError('One (but not both) of network_acl_id or ' - 'network_acl_name must be provided.') + raise SaltInvocationError( + "One (but not both) of network_acl_id or " + "network_acl_name must be provided." + ) - for v in ('rule_number', 'egress'): + for v in ("rule_number", "egress"): if locals()[v] is None: - raise SaltInvocationError('{0} is required.'.format(v)) + raise SaltInvocationError("{0} is required.".format(v)) if network_acl_name: - network_acl_id = _get_resource_id('network_acl', network_acl_name, - region=region, key=key, - keyid=keyid, profile=profile) + network_acl_id = _get_resource_id( + "network_acl", + network_acl_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not network_acl_id: - return {'deleted': False, - 'error': {'message': 'Network ACL {0} does not exist.'.format(network_acl_name or network_acl_id)}} + return { + "deleted": False, + "error": { + "message": "Network ACL {0} does not exist.".format( + network_acl_name or network_acl_id + ) + }, + } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - deleted = conn.delete_network_acl_entry(network_acl_id, rule_number, egress=egress) + deleted = conn.delete_network_acl_entry( + network_acl_id, rule_number, egress=egress + ) if deleted: - log.info('Network ACL entry was deleted') + log.info("Network ACL entry was deleted") else: - log.warning('Network ACL was not deleted') - return {'deleted': deleted} + log.warning("Network ACL was not deleted") + return {"deleted": deleted} except BotoServerError as e: - return {'deleted': False, 'error': __utils__['boto.get_error'](e)} + return {"deleted": False, "error": __utils__["boto.get_error"](e)} -def create_route_table(vpc_id=None, vpc_name=None, route_table_name=None, - tags=None, region=None, key=None, keyid=None, profile=None): - ''' +def create_route_table( + vpc_id=None, + vpc_name=None, + route_table_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Creates a route table. .. versionchanged:: 2015.8.0 @@ -2061,19 +2783,35 @@ def create_route_table(vpc_id=None, vpc_name=None, route_table_name=None, route_table_name='myroutetable' salt myminion boto_vpc.create_route_table vpc_name='myvpc' \\ route_table_name='myroutetable' - ''' + """ vpc_id = check_vpc(vpc_id, vpc_name, region, key, keyid, profile) if not vpc_id: - return {'created': False, 'error': {'message': 'VPC {0} does not exist.'.format(vpc_name or vpc_id)}} + return { + "created": False, + "error": {"message": "VPC {0} does not exist.".format(vpc_name or vpc_id)}, + } - return _create_resource('route_table', route_table_name, tags=tags, - vpc_id=vpc_id, region=region, key=key, - keyid=keyid, profile=profile) + return _create_resource( + "route_table", + route_table_name, + tags=tags, + vpc_id=vpc_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def delete_route_table(route_table_id=None, route_table_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_route_table( + route_table_id=None, + route_table_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Deletes a route table. CLI Examples: @@ -2083,15 +2821,29 @@ def delete_route_table(route_table_id=None, route_table_name=None, salt myminion boto_vpc.delete_route_table route_table_id='rtb-1f382e7d' salt myminion boto_vpc.delete_route_table route_table_name='myroutetable' - ''' - return _delete_resource(resource='route_table', name=route_table_name, - resource_id=route_table_id, region=region, key=key, - keyid=keyid, profile=profile) + """ + return _delete_resource( + resource="route_table", + name=route_table_name, + resource_id=route_table_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def route_table_exists(route_table_id=None, name=None, route_table_name=None, - tags=None, region=None, key=None, keyid=None, profile=None): - ''' +def route_table_exists( + route_table_id=None, + name=None, + route_table_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Checks if a route table exists. CLI Example: @@ -2100,23 +2852,42 @@ def route_table_exists(route_table_id=None, name=None, route_table_name=None, salt myminion boto_vpc.route_table_exists route_table_id='rtb-1f382e7d' - ''' + """ if name: - log.warning('boto_vpc.route_table_exists: name parameter is deprecated ' - 'use route_table_name instead.') + log.warning( + "boto_vpc.route_table_exists: name parameter is deprecated " + "use route_table_name instead." + ) route_table_name = name - return resource_exists('route_table', name=route_table_name, - resource_id=route_table_id, tags=tags, - region=region, key=key, keyid=keyid, - profile=profile) + return resource_exists( + "route_table", + name=route_table_name, + resource_id=route_table_id, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def route_exists(destination_cidr_block, route_table_name=None, route_table_id=None, - gateway_id=None, instance_id=None, interface_id=None, tags=None, - region=None, key=None, keyid=None, profile=None, vpc_peering_connection_id=None): - ''' +def route_exists( + destination_cidr_block, + route_table_name=None, + route_table_id=None, + gateway_id=None, + instance_id=None, + interface_id=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + vpc_peering_connection_id=None, +): + """ Checks if a route exists. .. versionadded:: 2015.8.0 @@ -2127,65 +2898,77 @@ def route_exists(destination_cidr_block, route_table_name=None, route_table_id=N salt myminion boto_vpc.route_exists destination_cidr_block='10.0.0.0/20' gateway_id='local' route_table_name='test' - ''' + """ if not any((route_table_name, route_table_id)): - raise SaltInvocationError('At least one of the following must be specified: route table name or route table id.') + raise SaltInvocationError( + "At least one of the following must be specified: route table name or route table id." + ) if not any((gateway_id, instance_id, interface_id, vpc_peering_connection_id)): - raise SaltInvocationError('At least one of the following must be specified: gateway id, instance id, ' - 'interface id or VPC peering connection id.') + raise SaltInvocationError( + "At least one of the following must be specified: gateway id, instance id, " + "interface id or VPC peering connection id." + ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if route_table_id: - filter_parameters['route_table_ids'] = [route_table_id] + filter_parameters["route_table_ids"] = [route_table_id] if route_table_name: - filter_parameters['filters']['tag:Name'] = route_table_name + filter_parameters["filters"]["tag:Name"] = route_table_name if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value route_tables = conn.get_all_route_tables(**filter_parameters) if len(route_tables) != 1: - raise SaltInvocationError('Found more than one route table.') + raise SaltInvocationError("Found more than one route table.") - route_check = {'destination_cidr_block': destination_cidr_block, - 'gateway_id': gateway_id, - 'instance_id': instance_id, - 'interface_id': interface_id, - 'vpc_peering_connection_id': vpc_peering_connection_id - } + route_check = { + "destination_cidr_block": destination_cidr_block, + "gateway_id": gateway_id, + "instance_id": instance_id, + "interface_id": interface_id, + "vpc_peering_connection_id": vpc_peering_connection_id, + } for route_match in route_tables[0].routes: - route_dict = {'destination_cidr_block': route_match.destination_cidr_block, - 'gateway_id': route_match.gateway_id, - 'instance_id': route_match.instance_id, - 'interface_id': route_match.interface_id, - 'vpc_peering_connection_id': vpc_peering_connection_id - } + route_dict = { + "destination_cidr_block": route_match.destination_cidr_block, + "gateway_id": route_match.gateway_id, + "instance_id": route_match.instance_id, + "interface_id": route_match.interface_id, + "vpc_peering_connection_id": vpc_peering_connection_id, + } route_comp = set(route_dict.items()) ^ set(route_check.items()) if len(route_comp) == 0: - log.info('Route %s exists.', destination_cidr_block) - return {'exists': True} + log.info("Route %s exists.", destination_cidr_block) + return {"exists": True} - log.warning('Route %s does not exist.', destination_cidr_block) - return {'exists': False} + log.warning("Route %s does not exist.", destination_cidr_block) + return {"exists": False} except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} -def associate_route_table(route_table_id=None, subnet_id=None, - route_table_name=None, subnet_name=None, - region=None, key=None, keyid=None, - profile=None): - ''' +def associate_route_table( + route_table_id=None, + subnet_id=None, + route_table_name=None, + subnet_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given a route table and subnet name or id, associates the route table with the subnet. CLI Example: @@ -2199,42 +2982,60 @@ def associate_route_table(route_table_id=None, subnet_id=None, salt myminion boto_vpc.associate_route_table route_table_name='myrtb' \\ subnet_name='mysubnet' - ''' + """ if all((subnet_id, subnet_name)): - raise SaltInvocationError('Only one of subnet_name or subnet_id may be ' - 'provided.') + raise SaltInvocationError( + "Only one of subnet_name or subnet_id may be " "provided." + ) if subnet_name: - subnet_id = _get_resource_id('subnet', subnet_name, - region=region, key=key, - keyid=keyid, profile=profile) + subnet_id = _get_resource_id( + "subnet", subnet_name, region=region, key=key, keyid=keyid, profile=profile + ) if not subnet_id: - return {'associated': False, - 'error': {'message': 'Subnet {0} does not exist.'.format(subnet_name)}} + return { + "associated": False, + "error": {"message": "Subnet {0} does not exist.".format(subnet_name)}, + } if all((route_table_id, route_table_name)): - raise SaltInvocationError('Only one of route_table_name or route_table_id may be ' - 'provided.') + raise SaltInvocationError( + "Only one of route_table_name or route_table_id may be " "provided." + ) if route_table_name: - route_table_id = _get_resource_id('route_table', route_table_name, - region=region, key=key, - keyid=keyid, profile=profile) + route_table_id = _get_resource_id( + "route_table", + route_table_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not route_table_id: - return {'associated': False, - 'error': {'message': 'Route table {0} does not exist.'.format(route_table_name)}} + return { + "associated": False, + "error": { + "message": "Route table {0} does not exist.".format( + route_table_name + ) + }, + } try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) association_id = conn.associate_route_table(route_table_id, subnet_id) - log.info('Route table %s was associated with subnet %s', - route_table_id, subnet_id) - return {'association_id': association_id} + log.info( + "Route table %s was associated with subnet %s", route_table_id, subnet_id + ) + return {"association_id": association_id} except BotoServerError as e: - return {'associated': False, 'error': __utils__['boto.get_error'](e)} + return {"associated": False, "error": __utils__["boto.get_error"](e)} -def disassociate_route_table(association_id, region=None, key=None, keyid=None, profile=None): - ''' +def disassociate_route_table( + association_id, region=None, key=None, keyid=None, profile=None +): + """ Dissassociates a route table. association_id @@ -2246,22 +3047,30 @@ def disassociate_route_table(association_id, region=None, key=None, keyid=None, salt myminion boto_vpc.disassociate_route_table 'rtbassoc-d8ccddba' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if conn.disassociate_route_table(association_id): - log.info('Route table with association id %s has been disassociated.', association_id) - return {'disassociated': True} + log.info( + "Route table with association id %s has been disassociated.", + association_id, + ) + return {"disassociated": True} else: - log.warning('Route table with association id %s has not been disassociated.', association_id) - return {'disassociated': False} + log.warning( + "Route table with association id %s has not been disassociated.", + association_id, + ) + return {"disassociated": False} except BotoServerError as e: - return {'disassociated': False, 'error': __utils__['boto.get_error'](e)} + return {"disassociated": False, "error": __utils__["boto.get_error"](e)} -def replace_route_table_association(association_id, route_table_id, region=None, key=None, keyid=None, profile=None): - ''' +def replace_route_table_association( + association_id, route_table_id, region=None, key=None, keyid=None, profile=None +): + """ Replaces a route table association. CLI Example: @@ -2270,29 +3079,42 @@ def replace_route_table_association(association_id, route_table_id, region=None, salt myminion boto_vpc.replace_route_table_association 'rtbassoc-d8ccddba' 'rtb-1f382e7d' - ''' + """ try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - association_id = conn.replace_route_table_association_with_assoc(association_id, route_table_id) - log.info('Route table %s was reassociated with association id %s', - route_table_id, association_id) - return {'replaced': True, 'association_id': association_id} + association_id = conn.replace_route_table_association_with_assoc( + association_id, route_table_id + ) + log.info( + "Route table %s was reassociated with association id %s", + route_table_id, + association_id, + ) + return {"replaced": True, "association_id": association_id} except BotoServerError as e: - return {'replaced': False, 'error': __utils__['boto.get_error'](e)} + return {"replaced": False, "error": __utils__["boto.get_error"](e)} -def create_route(route_table_id=None, destination_cidr_block=None, - route_table_name=None, gateway_id=None, - internet_gateway_name=None, - instance_id=None, interface_id=None, - vpc_peering_connection_id=None, vpc_peering_connection_name=None, - region=None, key=None, keyid=None, profile=None, - nat_gateway_id=None, - nat_gateway_subnet_name=None, - nat_gateway_subnet_id=None, - ): - ''' +def create_route( + route_table_id=None, + destination_cidr_block=None, + route_table_name=None, + gateway_id=None, + internet_gateway_name=None, + instance_id=None, + interface_id=None, + vpc_peering_connection_id=None, + vpc_peering_connection_name=None, + region=None, + key=None, + keyid=None, + profile=None, + nat_gateway_id=None, + nat_gateway_subnet_name=None, + nat_gateway_subnet_id=None, +): + """ Creates a route. If a nat gateway is specified, boto3 must be installed @@ -2303,86 +3125,172 @@ def create_route(route_table_id=None, destination_cidr_block=None, salt myminion boto_vpc.create_route 'rtb-1f382e7d' '10.0.0.0/16' gateway_id='vgw-a1b2c3' - ''' + """ if not _exactly_one((route_table_name, route_table_id)): - raise SaltInvocationError('One (but not both) of route_table_id or route_table_name ' - 'must be provided.') + raise SaltInvocationError( + "One (but not both) of route_table_id or route_table_name " + "must be provided." + ) - if not _exactly_one((gateway_id, internet_gateway_name, instance_id, interface_id, vpc_peering_connection_id, - nat_gateway_id, nat_gateway_subnet_id, nat_gateway_subnet_name, vpc_peering_connection_name)): - raise SaltInvocationError('Only one of gateway_id, internet_gateway_name, instance_id, ' - 'interface_id, vpc_peering_connection_id, nat_gateway_id, ' - 'nat_gateway_subnet_id, nat_gateway_subnet_name or vpc_peering_connection_name may be provided.') + if not _exactly_one( + ( + gateway_id, + internet_gateway_name, + instance_id, + interface_id, + vpc_peering_connection_id, + nat_gateway_id, + nat_gateway_subnet_id, + nat_gateway_subnet_name, + vpc_peering_connection_name, + ) + ): + raise SaltInvocationError( + "Only one of gateway_id, internet_gateway_name, instance_id, " + "interface_id, vpc_peering_connection_id, nat_gateway_id, " + "nat_gateway_subnet_id, nat_gateway_subnet_name or vpc_peering_connection_name may be provided." + ) if destination_cidr_block is None: - raise SaltInvocationError('destination_cidr_block is required.') + raise SaltInvocationError("destination_cidr_block is required.") try: if route_table_name: - route_table_id = _get_resource_id('route_table', route_table_name, - region=region, key=key, - keyid=keyid, profile=profile) + route_table_id = _get_resource_id( + "route_table", + route_table_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not route_table_id: - return {'created': False, - 'error': {'message': 'route table {0} does not exist.'.format(route_table_name)}} + return { + "created": False, + "error": { + "message": "route table {0} does not exist.".format( + route_table_name + ) + }, + } if internet_gateway_name: - gateway_id = _get_resource_id('internet_gateway', internet_gateway_name, - region=region, key=key, - keyid=keyid, profile=profile) + gateway_id = _get_resource_id( + "internet_gateway", + internet_gateway_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not gateway_id: - return {'created': False, - 'error': {'message': 'internet gateway {0} does not exist.'.format(internet_gateway_name)}} + return { + "created": False, + "error": { + "message": "internet gateway {0} does not exist.".format( + internet_gateway_name + ) + }, + } if vpc_peering_connection_name: - vpc_peering_connection_id = _get_resource_id('vpc_peering_connection', vpc_peering_connection_name, - region=region, key=key, - keyid=keyid, profile=profile) + vpc_peering_connection_id = _get_resource_id( + "vpc_peering_connection", + vpc_peering_connection_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not vpc_peering_connection_id: - return {'created': False, - 'error': {'message': 'VPC peering connection {0} does not exist.'.format(vpc_peering_connection_name)}} + return { + "created": False, + "error": { + "message": "VPC peering connection {0} does not exist.".format( + vpc_peering_connection_name + ) + }, + } if nat_gateway_subnet_name: - gws = describe_nat_gateways(subnet_name=nat_gateway_subnet_name, - region=region, key=key, keyid=keyid, profile=profile) + gws = describe_nat_gateways( + subnet_name=nat_gateway_subnet_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not gws: - return {'created': False, - 'error': {'message': 'nat gateway for {0} does not exist.'.format(nat_gateway_subnet_name)}} - nat_gateway_id = gws[0]['NatGatewayId'] + return { + "created": False, + "error": { + "message": "nat gateway for {0} does not exist.".format( + nat_gateway_subnet_name + ) + }, + } + nat_gateway_id = gws[0]["NatGatewayId"] if nat_gateway_subnet_id: - gws = describe_nat_gateways(subnet_id=nat_gateway_subnet_id, - region=region, key=key, keyid=keyid, profile=profile) + gws = describe_nat_gateways( + subnet_id=nat_gateway_subnet_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not gws: - return {'created': False, - 'error': {'message': 'nat gateway for {0} does not exist.'.format(nat_gateway_subnet_id)}} - nat_gateway_id = gws[0]['NatGatewayId'] + return { + "created": False, + "error": { + "message": "nat gateway for {0} does not exist.".format( + nat_gateway_subnet_id + ) + }, + } + nat_gateway_id = gws[0]["NatGatewayId"] except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} if not nat_gateway_id: - return _create_resource('route', route_table_id=route_table_id, - destination_cidr_block=destination_cidr_block, - gateway_id=gateway_id, instance_id=instance_id, - interface_id=interface_id, vpc_peering_connection_id=vpc_peering_connection_id, - region=region, key=key, keyid=keyid, profile=profile) + return _create_resource( + "route", + route_table_id=route_table_id, + destination_cidr_block=destination_cidr_block, + gateway_id=gateway_id, + instance_id=instance_id, + interface_id=interface_id, + vpc_peering_connection_id=vpc_peering_connection_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) # for nat gateway, boto3 is required try: conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) - ret = conn3.create_route(RouteTableId=route_table_id, - DestinationCidrBlock=destination_cidr_block, - NatGatewayId=nat_gateway_id) - return {'created': True, 'id': ret.get('NatGatewayId')} + ret = conn3.create_route( + RouteTableId=route_table_id, + DestinationCidrBlock=destination_cidr_block, + NatGatewayId=nat_gateway_id, + ) + return {"created": True, "id": ret.get("NatGatewayId")} except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} -def delete_route(route_table_id=None, destination_cidr_block=None, - route_table_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def delete_route( + route_table_id=None, + destination_cidr_block=None, + route_table_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Deletes a route. CLI Example: @@ -2391,38 +3299,64 @@ def delete_route(route_table_id=None, destination_cidr_block=None, salt myminion boto_vpc.delete_route 'rtb-1f382e7d' '10.0.0.0/16' - ''' + """ if not _exactly_one((route_table_name, route_table_id)): - raise SaltInvocationError('One (but not both) of route_table_id or route_table_name ' - 'must be provided.') + raise SaltInvocationError( + "One (but not both) of route_table_id or route_table_name " + "must be provided." + ) if destination_cidr_block is None: - raise SaltInvocationError('destination_cidr_block is required.') + raise SaltInvocationError("destination_cidr_block is required.") try: if route_table_name: - route_table_id = _get_resource_id('route_table', route_table_name, - region=region, key=key, - keyid=keyid, profile=profile) + route_table_id = _get_resource_id( + "route_table", + route_table_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not route_table_id: - return {'created': False, - 'error': {'message': 'route table {0} does not exist.'.format(route_table_name)}} + return { + "created": False, + "error": { + "message": "route table {0} does not exist.".format( + route_table_name + ) + }, + } except BotoServerError as e: - return {'created': False, 'error': __utils__['boto.get_error'](e)} + return {"created": False, "error": __utils__["boto.get_error"](e)} - return _delete_resource(resource='route', resource_id=route_table_id, - destination_cidr_block=destination_cidr_block, - region=region, key=key, - keyid=keyid, profile=profile) + return _delete_resource( + resource="route", + resource_id=route_table_id, + destination_cidr_block=destination_cidr_block, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def replace_route(route_table_id=None, destination_cidr_block=None, - route_table_name=None, gateway_id=None, - instance_id=None, interface_id=None, - region=None, key=None, keyid=None, profile=None, - vpc_peering_connection_id=None): - ''' +def replace_route( + route_table_id=None, + destination_cidr_block=None, + route_table_name=None, + gateway_id=None, + instance_id=None, + interface_id=None, + region=None, + key=None, + keyid=None, + profile=None, + vpc_peering_connection_id=None, +): + """ Replaces a route. CLI Example: @@ -2431,47 +3365,73 @@ def replace_route(route_table_id=None, destination_cidr_block=None, salt myminion boto_vpc.replace_route 'rtb-1f382e7d' '10.0.0.0/16' gateway_id='vgw-a1b2c3' - ''' + """ if not _exactly_one((route_table_name, route_table_id)): - raise SaltInvocationError('One (but not both) of route_table_id or route_table_name ' - 'must be provided.') + raise SaltInvocationError( + "One (but not both) of route_table_id or route_table_name " + "must be provided." + ) if destination_cidr_block is None: - raise SaltInvocationError('destination_cidr_block is required.') + raise SaltInvocationError("destination_cidr_block is required.") try: if route_table_name: - route_table_id = _get_resource_id('route_table', route_table_name, - region=region, key=key, - keyid=keyid, profile=profile) + route_table_id = _get_resource_id( + "route_table", + route_table_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not route_table_id: - return {'replaced': False, - 'error': {'message': 'route table {0} does not exist.'.format(route_table_name)}} + return { + "replaced": False, + "error": { + "message": "route table {0} does not exist.".format( + route_table_name + ) + }, + } conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - if conn.replace_route(route_table_id, destination_cidr_block, - gateway_id=gateway_id, instance_id=instance_id, - interface_id=interface_id, vpc_peering_connection_id=vpc_peering_connection_id): + if conn.replace_route( + route_table_id, + destination_cidr_block, + gateway_id=gateway_id, + instance_id=instance_id, + interface_id=interface_id, + vpc_peering_connection_id=vpc_peering_connection_id, + ): log.info( - 'Route with cidr block %s on route table %s was replaced', - route_table_id, destination_cidr_block + "Route with cidr block %s on route table %s was replaced", + route_table_id, + destination_cidr_block, ) - return {'replaced': True} + return {"replaced": True} else: log.warning( - 'Route with cidr block %s on route table %s was not replaced', - route_table_id, destination_cidr_block + "Route with cidr block %s on route table %s was not replaced", + route_table_id, + destination_cidr_block, ) - return {'replaced': False} + return {"replaced": False} except BotoServerError as e: - return {'replaced': False, 'error': __utils__['boto.get_error'](e)} + return {"replaced": False, "error": __utils__["boto.get_error"](e)} -def describe_route_table(route_table_id=None, route_table_name=None, - tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def describe_route_table( + route_table_id=None, + route_table_name=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given route table properties, return route table details if matching table(s) exist. .. versionadded:: 2015.8.0 @@ -2482,29 +3442,31 @@ def describe_route_table(route_table_id=None, route_table_name=None, salt myminion boto_vpc.describe_route_table route_table_id='rtb-1f382e7d' - ''' + """ salt.utils.versions.warn_until( - 'Magnesium', - 'The \'describe_route_table\' method has been deprecated and ' - 'replaced by \'describe_route_tables\'.' + "Magnesium", + "The 'describe_route_table' method has been deprecated and " + "replaced by 'describe_route_tables'.", ) if not any((route_table_id, route_table_name, tags)): - raise SaltInvocationError('At least one of the following must be specified: ' - 'route table id, route table name, or tags.') + raise SaltInvocationError( + "At least one of the following must be specified: " + "route table id, route table name, or tags." + ) try: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) - filter_parameters = {'filters': {}} + filter_parameters = {"filters": {}} if route_table_id: - filter_parameters['route_table_ids'] = route_table_id + filter_parameters["route_table_ids"] = route_table_id if route_table_name: - filter_parameters['filters']['tag:Name'] = route_table_name + filter_parameters["filters"]["tag:Name"] = route_table_name if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['filters']['tag:{0}'.format(tag_name)] = tag_value + filter_parameters["filters"]["tag:{0}".format(tag_name)] = tag_value route_tables = conn.get_all_route_tables(**filter_parameters) @@ -2512,28 +3474,40 @@ def describe_route_table(route_table_id=None, route_table_name=None, return {} route_table = {} - keys = ['id', 'vpc_id', 'tags', 'routes', 'associations'] - route_keys = ['destination_cidr_block', 'gateway_id', 'instance_id', 'interface_id', 'vpc_peering_connection_id'] - assoc_keys = ['id', 'main', 'route_table_id', 'subnet_id'] + keys = ["id", "vpc_id", "tags", "routes", "associations"] + route_keys = [ + "destination_cidr_block", + "gateway_id", + "instance_id", + "interface_id", + "vpc_peering_connection_id", + ] + assoc_keys = ["id", "main", "route_table_id", "subnet_id"] for item in route_tables: for key in keys: if hasattr(item, key): route_table[key] = getattr(item, key) - if key == 'routes': + if key == "routes": route_table[key] = _key_iter(key, route_keys, item) - if key == 'associations': + if key == "associations": route_table[key] = _key_iter(key, assoc_keys, item) return route_table except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} -def describe_route_tables(route_table_id=None, route_table_name=None, - vpc_id=None, - tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def describe_route_tables( + route_table_id=None, + route_table_name=None, + vpc_id=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Given route table properties, return details of all matching route tables. This function requires boto3 to be installed. @@ -2546,86 +3520,108 @@ def describe_route_tables(route_table_id=None, route_table_name=None, salt myminion boto_vpc.describe_route_tables vpc_id='vpc-a6a9efc3' - ''' + """ if not any((route_table_id, route_table_name, tags, vpc_id)): - raise SaltInvocationError('At least one of the following must be specified: ' - 'route table id, route table name, vpc_id, or tags.') + raise SaltInvocationError( + "At least one of the following must be specified: " + "route table id, route table name, vpc_id, or tags." + ) try: conn3 = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) - filter_parameters = {'Filters': []} + filter_parameters = {"Filters": []} if route_table_id: - filter_parameters['RouteTableIds'] = [route_table_id] + filter_parameters["RouteTableIds"] = [route_table_id] if vpc_id: - filter_parameters['Filters'].append({'Name': 'vpc-id', 'Values': [vpc_id]}) + filter_parameters["Filters"].append({"Name": "vpc-id", "Values": [vpc_id]}) if route_table_name: - filter_parameters['Filters'].append({'Name': 'tag:Name', 'Values': [route_table_name]}) + filter_parameters["Filters"].append( + {"Name": "tag:Name", "Values": [route_table_name]} + ) if tags: for tag_name, tag_value in six.iteritems(tags): - filter_parameters['Filters'].append({'Name': 'tag:{0}'.format(tag_name), - 'Values': [tag_value]}) + filter_parameters["Filters"].append( + {"Name": "tag:{0}".format(tag_name), "Values": [tag_value]} + ) - route_tables = conn3.describe_route_tables(**filter_parameters).get('RouteTables', []) + route_tables = conn3.describe_route_tables(**filter_parameters).get( + "RouteTables", [] + ) if not route_tables: return [] tables = [] - keys = {'id': 'RouteTableId', - 'vpc_id': 'VpcId', - 'tags': 'Tags', - 'routes': 'Routes', - 'associations': 'Associations' - } - route_keys = {'destination_cidr_block': 'DestinationCidrBlock', - 'gateway_id': 'GatewayId', - 'instance_id': 'Instance', - 'interface_id': 'NetworkInterfaceId', - 'nat_gateway_id': 'NatGatewayId', - 'vpc_peering_connection_id': 'VpcPeeringConnectionId', - } - assoc_keys = {'id': 'RouteTableAssociationId', - 'main': 'Main', - 'route_table_id': 'RouteTableId', - 'SubnetId': 'subnet_id', - } + keys = { + "id": "RouteTableId", + "vpc_id": "VpcId", + "tags": "Tags", + "routes": "Routes", + "associations": "Associations", + } + route_keys = { + "destination_cidr_block": "DestinationCidrBlock", + "gateway_id": "GatewayId", + "instance_id": "Instance", + "interface_id": "NetworkInterfaceId", + "nat_gateway_id": "NatGatewayId", + "vpc_peering_connection_id": "VpcPeeringConnectionId", + } + assoc_keys = { + "id": "RouteTableAssociationId", + "main": "Main", + "route_table_id": "RouteTableId", + "SubnetId": "subnet_id", + } for item in route_tables: route_table = {} for outkey, inkey in six.iteritems(keys): if inkey in item: - if outkey == 'routes': + if outkey == "routes": route_table[outkey] = _key_remap(inkey, route_keys, item) - elif outkey == 'associations': + elif outkey == "associations": route_table[outkey] = _key_remap(inkey, assoc_keys, item) - elif outkey == 'tags': + elif outkey == "tags": route_table[outkey] = {} for tagitem in item.get(inkey, []): - route_table[outkey][tagitem.get('Key')] = tagitem.get('Value') + route_table[outkey][tagitem.get("Key")] = tagitem.get( + "Value" + ) else: route_table[outkey] = item.get(inkey) tables.append(route_table) return tables except BotoServerError as e: - return {'error': __utils__['boto.get_error'](e)} + return {"error": __utils__["boto.get_error"](e)} -def _create_dhcp_options(conn, domain_name=None, domain_name_servers=None, ntp_servers=None, netbios_name_servers=None, - netbios_node_type=None): - return conn.create_dhcp_options(domain_name=domain_name, domain_name_servers=domain_name_servers, - ntp_servers=ntp_servers, netbios_name_servers=netbios_name_servers, - netbios_node_type=netbios_node_type) +def _create_dhcp_options( + conn, + domain_name=None, + domain_name_servers=None, + ntp_servers=None, + netbios_name_servers=None, + netbios_node_type=None, +): + return conn.create_dhcp_options( + domain_name=domain_name, + domain_name_servers=domain_name_servers, + ntp_servers=ntp_servers, + netbios_name_servers=netbios_name_servers, + netbios_node_type=netbios_node_type, + ) def _maybe_set_name_tag(name, obj): if name: obj.add_tag("Name", name) - log.debug('%s is now named as %s', obj, name) + log.debug("%s is now named as %s", obj, name) def _maybe_set_tags(tags, obj): @@ -2637,36 +3633,36 @@ def _maybe_set_tags(tags, obj): except AttributeError: for tag, value in tags.items(): obj.add_tag(tag, value) - log.debug('The following tags: %s were added to %s', ', '.join(tags), obj) + log.debug("The following tags: %s were added to %s", ", ".join(tags), obj) def _maybe_set_dns(conn, vpcid, dns_support, dns_hostnames): if dns_support: conn.modify_vpc_attribute(vpc_id=vpcid, enable_dns_support=dns_support) - log.debug('DNS support was set to: %s on vpc %s', dns_support, vpcid) + log.debug("DNS support was set to: %s on vpc %s", dns_support, vpcid) if dns_hostnames: conn.modify_vpc_attribute(vpc_id=vpcid, enable_dns_hostnames=dns_hostnames) - log.debug('DNS hostnames was set to: %s on vpc %s', dns_hostnames, vpcid) + log.debug("DNS hostnames was set to: %s on vpc %s", dns_hostnames, vpcid) def _maybe_name_route_table(conn, vpcid, vpc_name): - route_tables = conn.get_all_route_tables(filters={'vpc_id': vpcid}) + route_tables = conn.get_all_route_tables(filters={"vpc_id": vpcid}) if not route_tables: - log.warning('no default route table found') + log.warning("no default route table found") return default_table = None for table in route_tables: - for association in getattr(table, 'associations', {}): - if getattr(association, 'main', False): + for association in getattr(table, "associations", {}): + if getattr(association, "main", False): default_table = table break if not default_table: - log.warning('no default route table found') + log.warning("no default route table found") return - name = '{0}-default-table'.format(vpc_name) + name = "{0}-default-table".format(vpc_name) _maybe_set_name_tag(name, default_table) - log.debug('Default route table name was set to: %s on vpc %s', name, vpcid) + log.debug("Default route table name was set to: %s on vpc %s", name, vpcid) def _key_iter(key, keys, item): @@ -2691,16 +3687,18 @@ def _key_remap(key, keys, item): return elements_list -def _get_subnet_explicit_route_table(subnet_id, vpc_id, conn=None, region=None, key=None, keyid=None, profile=None): - ''' +def _get_subnet_explicit_route_table( + subnet_id, vpc_id, conn=None, region=None, key=None, keyid=None, profile=None +): + """ helper function to find subnet explicit route table associations .. versionadded:: 2016.11.0 - ''' + """ if not conn: conn = _get_conn(region=region, key=key, keyid=keyid, profile=profile) if conn: - vpc_route_tables = conn.get_all_route_tables(filters={'vpc_id': vpc_id}) + vpc_route_tables = conn.get_all_route_tables(filters={"vpc_id": vpc_id}) for vpc_route_table in vpc_route_tables: for rt_association in vpc_route_table.associations: if rt_association.subnet_id == subnet_id and not rt_association.main: @@ -2708,11 +3706,20 @@ def _get_subnet_explicit_route_table(subnet_id, vpc_id, conn=None, region=None, return None -def request_vpc_peering_connection(requester_vpc_id=None, requester_vpc_name=None, - peer_vpc_id=None, peer_vpc_name=None, name=None, - peer_owner_id=None, region=None, - key=None, keyid=None, profile=None, dry_run=False): - ''' +def request_vpc_peering_connection( + requester_vpc_id=None, + requester_vpc_name=None, + peer_vpc_id=None, + peer_vpc_name=None, + name=None, + peer_owner_id=None, + region=None, + key=None, + keyid=None, + profile=None, + dry_run=False, +): + """ Request a VPC peering connection between two VPCs. .. versionadded:: 2016.11.0 @@ -2766,67 +3773,81 @@ def request_vpc_peering_connection(requester_vpc_id=None, requester_vpc_name=Non # Specify a region salt myminion boto_vpc.request_vpc_peering_connection vpc-4a3e622e vpc-be82e9da region=us-west-2 - ''' - conn = _get_conn3(region=region, key=key, keyid=keyid, - profile=profile) + """ + conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if name and _vpc_peering_conn_id_for_name(name, conn): - raise SaltInvocationError('A VPC peering connection with this name already ' - 'exists! Please specify a different name.') + raise SaltInvocationError( + "A VPC peering connection with this name already " + "exists! Please specify a different name." + ) if not _exactly_one((requester_vpc_id, requester_vpc_name)): - raise SaltInvocationError('Exactly one of requester_vpc_id or ' - 'requester_vpc_name is required') + raise SaltInvocationError( + "Exactly one of requester_vpc_id or " "requester_vpc_name is required" + ) if not _exactly_one((peer_vpc_id, peer_vpc_name)): - raise SaltInvocationError('Exactly one of peer_vpc_id or ' - 'peer_vpc_name is required.') + raise SaltInvocationError( + "Exactly one of peer_vpc_id or " "peer_vpc_name is required." + ) if requester_vpc_name: - requester_vpc_id = _get_id(vpc_name=requester_vpc_name, region=region, key=key, - keyid=keyid, profile=profile) + requester_vpc_id = _get_id( + vpc_name=requester_vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not requester_vpc_id: - return {'error': 'Could not resolve VPC name {0} to an ID'.format(requester_vpc_name)} + return { + "error": "Could not resolve VPC name {0} to an ID".format( + requester_vpc_name + ) + } if peer_vpc_name: - peer_vpc_id = _get_id(vpc_name=peer_vpc_name, region=region, key=key, - keyid=keyid, profile=profile) + peer_vpc_id = _get_id( + vpc_name=peer_vpc_name, region=region, key=key, keyid=keyid, profile=profile + ) if not peer_vpc_id: - return {'error': 'Could not resolve VPC name {0} to an ID'.format(peer_vpc_name)} + return { + "error": "Could not resolve VPC name {0} to an ID".format(peer_vpc_name) + } try: - log.debug('Trying to request vpc peering connection') + log.debug("Trying to request vpc peering connection") if not peer_owner_id: vpc_peering = conn.create_vpc_peering_connection( - VpcId=requester_vpc_id, - PeerVpcId=peer_vpc_id, - DryRun=dry_run) + VpcId=requester_vpc_id, PeerVpcId=peer_vpc_id, DryRun=dry_run + ) else: vpc_peering = conn.create_vpc_peering_connection( VpcId=requester_vpc_id, PeerVpcId=peer_vpc_id, PeerOwnerId=peer_owner_id, - DryRun=dry_run) - peering = vpc_peering.get('VpcPeeringConnection', {}) - peering_conn_id = peering.get('VpcPeeringConnectionId', 'ERROR') - msg = 'VPC peering {0} requested.'.format(peering_conn_id) + DryRun=dry_run, + ) + peering = vpc_peering.get("VpcPeeringConnection", {}) + peering_conn_id = peering.get("VpcPeeringConnectionId", "ERROR") + msg = "VPC peering {0} requested.".format(peering_conn_id) log.debug(msg) if name: - log.debug('Adding name tag to vpc peering connection') + log.debug("Adding name tag to vpc peering connection") conn.create_tags( - Resources=[peering_conn_id], - Tags=[{'Key': 'Name', 'Value': name}] + Resources=[peering_conn_id], Tags=[{"Key": "Name", "Value": name}] ) - log.debug('Applied name tag to vpc peering connection') - msg += ' With name {0}.'.format(name) + log.debug("Applied name tag to vpc peering connection") + msg += " With name {0}.".format(name) - return {'msg': msg} + return {"msg": msg} except botocore.exceptions.ClientError as err: - log.error('Got an error while trying to request vpc peering') - return {'error': __utils__['boto.get_error'](err)} + log.error("Got an error while trying to request vpc peering") + return {"error": __utils__["boto.get_error"](err)} def _get_peering_connection_ids(name, conn): - ''' + """ :param name: The name of the VPC peering connection. :type name: String :param conn: The boto aws ec2 connection. @@ -2834,27 +3855,22 @@ def _get_peering_connection_ids(name, conn): Returns the VPC peering connection ids given the VPC peering connection name. - ''' - filters = [{ - 'Name': 'tag:Name', - 'Values': [name], - }, { - 'Name': 'status-code', - 'Values': [ACTIVE, PENDING_ACCEPTANCE, PROVISIONING], - }] + """ + filters = [ + {"Name": "tag:Name", "Values": [name]}, + {"Name": "status-code", "Values": [ACTIVE, PENDING_ACCEPTANCE, PROVISIONING]}, + ] - peerings = conn.describe_vpc_peering_connections( - Filters=filters).get('VpcPeeringConnections', - []) - return [x['VpcPeeringConnectionId'] for x in peerings] + peerings = conn.describe_vpc_peering_connections(Filters=filters).get( + "VpcPeeringConnections", [] + ) + return [x["VpcPeeringConnectionId"] for x in peerings] -def describe_vpc_peering_connection(name, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def describe_vpc_peering_connection( + name, region=None, key=None, keyid=None, profile=None +): + """ Returns any VPC peering connection id(s) for the given VPC peering connection name. @@ -2879,23 +3895,15 @@ def describe_vpc_peering_connection(name, # Specify a region salt myminion boto_vpc.describe_vpc_peering_connection salt-vpc region=us-west-2 - ''' - conn = _get_conn3(region=region, key=key, keyid=keyid, - profile=profile) - return { - 'VPC-Peerings': _get_peering_connection_ids(name, conn) - } + """ + conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) + return {"VPC-Peerings": _get_peering_connection_ids(name, conn)} def accept_vpc_peering_connection( # pylint: disable=too-many-arguments - conn_id='', - name='', - region=None, - key=None, - keyid=None, - profile=None, - dry_run=False): - ''' + conn_id="", name="", region=None, key=None, keyid=None, profile=None, dry_run=False +): + """ Request a VPC peering connection between two VPCs. .. versionadded:: 2016.11.0 @@ -2922,58 +3930,70 @@ def accept_vpc_peering_connection( # pylint: disable=too-many-arguments # specify an id salt myminion boto_vpc.accept_vpc_peering_connection conn_id=pcx-8a8939e3 - ''' + """ if not _exactly_one((conn_id, name)): - raise SaltInvocationError('One (but not both) of ' - 'vpc_peering_connection_id or name ' - 'must be provided.') + raise SaltInvocationError( + "One (but not both) of " + "vpc_peering_connection_id or name " + "must be provided." + ) - conn = _get_conn3(region=region, key=key, keyid=keyid, - profile=profile) + conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if name: conn_id = _vpc_peering_conn_id_for_name(name, conn) if not conn_id: - raise SaltInvocationError('No ID found for this ' - 'VPC peering connection! ({0}) ' - 'Please make sure this VPC peering ' - 'connection exists ' - 'or invoke this function with ' - 'a VPC peering connection ' - 'ID'.format(name)) + raise SaltInvocationError( + "No ID found for this " + "VPC peering connection! ({0}) " + "Please make sure this VPC peering " + "connection exists " + "or invoke this function with " + "a VPC peering connection " + "ID".format(name) + ) try: - log.debug('Trying to accept vpc peering connection') + log.debug("Trying to accept vpc peering connection") conn.accept_vpc_peering_connection( - DryRun=dry_run, - VpcPeeringConnectionId=conn_id) - return {'msg': 'VPC peering connection accepted.'} + DryRun=dry_run, VpcPeeringConnectionId=conn_id + ) + return {"msg": "VPC peering connection accepted."} except botocore.exceptions.ClientError as err: - log.error('Got an error while trying to accept vpc peering') - return {'error': __utils__['boto.get_error'](err)} + log.error("Got an error while trying to accept vpc peering") + return {"error": __utils__["boto.get_error"](err)} def _vpc_peering_conn_id_for_name(name, conn): - ''' + """ Get the ID associated with this name - ''' - log.debug('Retrieving VPC peering connection id') + """ + log.debug("Retrieving VPC peering connection id") ids = _get_peering_connection_ids(name, conn) if not ids: ids = [None] # Let callers handle the case where we have no id elif len(ids) > 1: - raise SaltInvocationError('Found multiple VPC peering connections ' - 'with the same name!! ' - 'Please make sure you have only ' - 'one VPC peering connection named {0} ' - 'or invoke this function with a VPC ' - 'peering connection ID'.format(name)) + raise SaltInvocationError( + "Found multiple VPC peering connections " + "with the same name!! " + "Please make sure you have only " + "one VPC peering connection named {0} " + "or invoke this function with a VPC " + "peering connection ID".format(name) + ) return ids[0] -def delete_vpc_peering_connection(conn_id=None, conn_name=None, region=None, - key=None, keyid=None, profile=None, dry_run=False): - ''' +def delete_vpc_peering_connection( + conn_id=None, + conn_name=None, + region=None, + key=None, + keyid=None, + profile=None, + dry_run=False, +): + """ Delete a VPC peering connection. .. versionadded:: 2016.11.0 @@ -3011,30 +4031,36 @@ def delete_vpc_peering_connection(conn_id=None, conn_name=None, region=None, # specify an id salt myminion boto_vpc.delete_vpc_peering_connection conn_id=pcx-8a8939e3 - ''' + """ if not _exactly_one((conn_id, conn_name)): - raise SaltInvocationError('Exactly one of conn_id or ' - 'conn_name must be provided.') + raise SaltInvocationError( + "Exactly one of conn_id or " "conn_name must be provided." + ) conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if conn_name: conn_id = _vpc_peering_conn_id_for_name(conn_name, conn) if not conn_id: - raise SaltInvocationError("Couldn't resolve VPC peering connection " - "{0} to an ID".format(conn_name)) + raise SaltInvocationError( + "Couldn't resolve VPC peering connection " + "{0} to an ID".format(conn_name) + ) try: - log.debug('Trying to delete vpc peering connection') - conn.delete_vpc_peering_connection(DryRun=dry_run, VpcPeeringConnectionId=conn_id) - return {'msg': 'VPC peering connection deleted.'} + log.debug("Trying to delete vpc peering connection") + conn.delete_vpc_peering_connection( + DryRun=dry_run, VpcPeeringConnectionId=conn_id + ) + return {"msg": "VPC peering connection deleted."} except botocore.exceptions.ClientError as err: - e = __utils__['boto.get_error'](err) - log.error('Failed to delete VPC peering %s: %s', conn_name or conn_id, e) - return {'error': e} + e = __utils__["boto.get_error"](err) + log.error("Failed to delete VPC peering %s: %s", conn_name or conn_id, e) + return {"error": e} -def is_peering_connection_pending(conn_id=None, conn_name=None, region=None, - key=None, keyid=None, profile=None): - ''' +def is_peering_connection_pending( + conn_id=None, conn_name=None, region=None, key=None, keyid=None, profile=None +): + """ Check if a VPC peering connection is in the pending state. .. versionadded:: 2016.11.0 @@ -3068,35 +4094,55 @@ def is_peering_connection_pending(conn_id=None, conn_name=None, region=None, # specify an id salt myminion boto_vpc.is_peering_connection_pending conn_id=pcx-8a8939e3 - ''' + """ if not _exactly_one((conn_id, conn_name)): - raise SaltInvocationError('Exactly one of conn_id or conn_name must be provided.') + raise SaltInvocationError( + "Exactly one of conn_id or conn_name must be provided." + ) conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) if conn_id: - vpcs = conn.describe_vpc_peering_connections(VpcPeeringConnectionIds=[conn_id]).get('VpcPeeringConnections', []) + vpcs = conn.describe_vpc_peering_connections( + VpcPeeringConnectionIds=[conn_id] + ).get("VpcPeeringConnections", []) else: - filters = [{'Name': 'tag:Name', 'Values': [conn_name]}, - {'Name': 'status-code', 'Values': [ACTIVE, PENDING_ACCEPTANCE, PROVISIONING]}] - vpcs = conn.describe_vpc_peering_connections(Filters=filters).get('VpcPeeringConnections', []) + filters = [ + {"Name": "tag:Name", "Values": [conn_name]}, + { + "Name": "status-code", + "Values": [ACTIVE, PENDING_ACCEPTANCE, PROVISIONING], + }, + ] + vpcs = conn.describe_vpc_peering_connections(Filters=filters).get( + "VpcPeeringConnections", [] + ) if not vpcs: return False elif len(vpcs) > 1: - raise SaltInvocationError('Found more than one ID for the VPC peering ' - 'connection ({0}). Please call this function ' - 'with an ID instead.'.format(conn_id or conn_name)) + raise SaltInvocationError( + "Found more than one ID for the VPC peering " + "connection ({0}). Please call this function " + "with an ID instead.".format(conn_id or conn_name) + ) else: - status = vpcs[0]['Status']['Code'] + status = vpcs[0]["Status"]["Code"] return status == PENDING_ACCEPTANCE -def peering_connection_pending_from_vpc(conn_id=None, conn_name=None, vpc_id=None, - vpc_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def peering_connection_pending_from_vpc( + conn_id=None, + conn_name=None, + vpc_id=None, + vpc_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Check if a VPC peering connection is in the pending state, and requested from the given VPC. .. versionadded:: 2016.11.0 @@ -3132,36 +4178,46 @@ def peering_connection_pending_from_vpc(conn_id=None, conn_name=None, vpc_id=Non salt myminion boto_vpc.is_peering_connection_pending name=salt-vpc - ''' + """ if not _exactly_one((conn_id, conn_name)): - raise SaltInvocationError('Exactly one of conn_id or conn_name must be provided.') + raise SaltInvocationError( + "Exactly one of conn_id or conn_name must be provided." + ) if not _exactly_one((vpc_id, vpc_name)): - raise SaltInvocationError('Exactly one of vpc_id or vpc_name must be provided.') + raise SaltInvocationError("Exactly one of vpc_id or vpc_name must be provided.") if vpc_name: - vpc_id = check_vpc(vpc_name=vpc_name, region=region, key=key, keyid=keyid, profile=profile) + vpc_id = check_vpc( + vpc_name=vpc_name, region=region, key=key, keyid=keyid, profile=profile + ) if not vpc_id: - log.warning('Could not resolve VPC name %s to an ID', vpc_name) + log.warning("Could not resolve VPC name %s to an ID", vpc_name) return False conn = _get_conn3(region=region, key=key, keyid=keyid, profile=profile) - filters = [{'Name': 'requester-vpc-info.vpc-id', 'Values': [vpc_id]}, - {'Name': 'status-code', 'Values': [ACTIVE, PENDING_ACCEPTANCE, PROVISIONING]}] + filters = [ + {"Name": "requester-vpc-info.vpc-id", "Values": [vpc_id]}, + {"Name": "status-code", "Values": [ACTIVE, PENDING_ACCEPTANCE, PROVISIONING]}, + ] if conn_id: - filters += [{'Name': 'vpc-peering-connection-id', 'Values': [conn_id]}] + filters += [{"Name": "vpc-peering-connection-id", "Values": [conn_id]}] else: - filters += [{'Name': 'tag:Name', 'Values': [conn_name]}] + filters += [{"Name": "tag:Name", "Values": [conn_name]}] - vpcs = conn.describe_vpc_peering_connections(Filters=filters).get('VpcPeeringConnections', []) + vpcs = conn.describe_vpc_peering_connections(Filters=filters).get( + "VpcPeeringConnections", [] + ) if not vpcs: return False elif len(vpcs) > 1: - raise SaltInvocationError('Found more than one ID for the VPC peering ' - 'connection ({0}). Please call this function ' - 'with an ID instead.'.format(conn_id or conn_name)) + raise SaltInvocationError( + "Found more than one ID for the VPC peering " + "connection ({0}). Please call this function " + "with an ID instead.".format(conn_id or conn_name) + ) else: - status = vpcs[0]['Status']['Code'] + status = vpcs[0]["Status"]["Code"] return bool(status == PENDING_ACCEPTANCE) diff --git a/salt/modules/bower.py b/salt/modules/bower.py index 7fbba8d4f7c..1ef0e958b1d 100644 --- a/salt/modules/bower.py +++ b/salt/modules/bower.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage and query Bower packages =============================== @@ -7,7 +7,7 @@ This module manages the installed packages using Bower. Note that npm, git and bower must be installed for this module to be available. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -20,64 +20,60 @@ import salt.utils.path from salt.exceptions import CommandExecutionError from salt.utils.versions import LooseVersion as _LooseVersion - log = logging.getLogger(__name__) # Function alias to make sure not to shadow built-in's -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} def __virtual__(): - ''' + """ Only work when Bower is installed - ''' - if salt.utils.path.which('bower') is None: - return (False, 'The bower module could not be loaded: bower command not found') + """ + if salt.utils.path.which("bower") is None: + return (False, "The bower module could not be loaded: bower command not found") return True def _check_valid_version(): - ''' + """ Check the version of Bower to ensure this module will work. Currently bower must be at least version 1.3. - ''' + """ # pylint: disable=no-member - bower_version = _LooseVersion( - __salt__['cmd.run']('bower --version')) - valid_version = _LooseVersion('1.3') + bower_version = _LooseVersion(__salt__["cmd.run"]("bower --version")) + valid_version = _LooseVersion("1.3") # pylint: enable=no-member if bower_version < valid_version: raise CommandExecutionError( - '\'bower\' is not recent enough({0} < {1}). ' - 'Please Upgrade.'.format( - bower_version, valid_version - ) + "'bower' is not recent enough({0} < {1}). " + "Please Upgrade.".format(bower_version, valid_version) ) def _construct_bower_command(bower_command): - ''' + """ Create bower command line string - ''' + """ if not bower_command: - raise CommandExecutionError( - 'bower_command, e.g. install, must be specified') + raise CommandExecutionError("bower_command, e.g. install, must be specified") - cmd = ['bower'] + shlex.split(bower_command) - cmd.extend(['--config.analytics', 'false', - '--config.interactive', 'false', - '--allow-root', '--json']) + cmd = ["bower"] + shlex.split(bower_command) + cmd.extend( + [ + "--config.analytics", + "false", + "--config.interactive", + "false", + "--allow-root", + "--json", + ] + ) return cmd -def install(pkg, - dir, - pkgs=None, - runas=None, - env=None): - ''' +def install(pkg, dir, pkgs=None, runas=None, env=None): + """ Install a Bower package. If no package is specified, the dependencies (from bower.json) of the @@ -109,32 +105,30 @@ def install(pkg, salt '*' bower.install jquery#2.0 /path/to/project - ''' + """ _check_valid_version() - cmd = _construct_bower_command('install') + cmd = _construct_bower_command("install") if pkg: cmd.append(pkg) elif pkgs: cmd.extend(pkgs) - result = __salt__['cmd.run_all'](cmd, - cwd=dir, - runas=runas, - env=env, - python_shell=False) + result = __salt__["cmd.run_all"]( + cmd, cwd=dir, runas=runas, env=env, python_shell=False + ) - if result['retcode'] != 0: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0: + raise CommandExecutionError(result["stderr"]) # If package is already installed, Bower will emit empty dict to STDOUT - stdout = salt.utils.json.loads(result['stdout']) + stdout = salt.utils.json.loads(result["stdout"]) return stdout != {} def uninstall(pkg, dir, runas=None, env=None): - ''' + """ Uninstall a Bower package. pkg @@ -158,28 +152,26 @@ def uninstall(pkg, dir, runas=None, env=None): salt '*' bower.uninstall underscore /path/to/project - ''' + """ _check_valid_version() - cmd = _construct_bower_command('uninstall') + cmd = _construct_bower_command("uninstall") cmd.append(pkg) - result = __salt__['cmd.run_all'](cmd, - cwd=dir, - runas=runas, - env=env, - python_shell=False) + result = __salt__["cmd.run_all"]( + cmd, cwd=dir, runas=runas, env=env, python_shell=False + ) - if result['retcode'] != 0: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0: + raise CommandExecutionError(result["stderr"]) # If package is not installed, Bower will emit empty dict to STDOUT - stdout = salt.utils.json.loads(result['stdout']) + stdout = salt.utils.json.loads(result["stdout"]) return stdout != {} def list_(dir, runas=None, env=None): - ''' + """ List installed Bower packages. dir @@ -199,26 +191,24 @@ def list_(dir, runas=None, env=None): salt '*' bower.list /path/to/project - ''' + """ _check_valid_version() - cmd = _construct_bower_command('list') - cmd.append('--offline') + cmd = _construct_bower_command("list") + cmd.append("--offline") - result = __salt__['cmd.run_all'](cmd, - cwd=dir, - runas=runas, - env=env, - python_shell=False) + result = __salt__["cmd.run_all"]( + cmd, cwd=dir, runas=runas, env=env, python_shell=False + ) - if result['retcode'] != 0: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0: + raise CommandExecutionError(result["stderr"]) - return salt.utils.json.loads(result['stdout'])['dependencies'] + return salt.utils.json.loads(result["stdout"])["dependencies"] def prune(dir, runas=None, env=None): - ''' + """ .. versionadded:: 2017.7.0 Remove extraneous local Bower packages, i.e. those not referenced in bower.json @@ -240,19 +230,17 @@ def prune(dir, runas=None, env=None): salt '*' bower.prune /path/to/project - ''' + """ _check_valid_version() - cmd = _construct_bower_command('prune') + cmd = _construct_bower_command("prune") - result = __salt__['cmd.run_all'](cmd, - cwd=dir, - runas=runas, - env=env, - python_shell=False) + result = __salt__["cmd.run_all"]( + cmd, cwd=dir, runas=runas, env=env, python_shell=False + ) - if result['retcode'] != 0: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0: + raise CommandExecutionError(result["stderr"]) # Bower returns an empty dictionary if nothing was pruned - return salt.utils.json.loads(result['stdout']) + return salt.utils.json.loads(result["stdout"]) diff --git a/salt/modules/bridge.py b/salt/modules/bridge.py index 79794b05568..a8859edce43 100644 --- a/salt/modules/bridge.py +++ b/salt/modules/bridge.py @@ -1,65 +1,66 @@ # -*- coding: utf-8 -*- -''' +""" Module for gathering and managing bridging information -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import sys import re +import sys + import salt.utils.path - -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} # Other BSD-like derivatives that use ifconfig may work too -SUPPORTED_BSD_LIKE = ['FreeBSD', 'NetBSD', 'OpenBSD'] +SUPPORTED_BSD_LIKE = ["FreeBSD", "NetBSD", "OpenBSD"] def __virtual__(): - ''' + """ Confirm this module is supported by the OS and the system has required tools - ''' + """ supported_os_tool = { - 'FreeBSD': 'ifconfig', - 'Linux': 'brctl', - 'NetBSD': 'brconfig', - 'OpenBSD': 'ifconfig' + "FreeBSD": "ifconfig", + "Linux": "brctl", + "NetBSD": "brconfig", + "OpenBSD": "ifconfig", } - cur_os = __grains__['kernel'] + cur_os = __grains__["kernel"] for _os in supported_os_tool: if cur_os == _os and salt.utils.path.which(supported_os_tool[cur_os]): return True - return (False, 'The bridge execution module failed to load: requires one of the following tool/os' - ' combinations: ifconfig on FreeBSD/OpenBSD, brctl on Linux or brconfig on NetBSD.') + return ( + False, + "The bridge execution module failed to load: requires one of the following tool/os" + " combinations: ifconfig on FreeBSD/OpenBSD, brctl on Linux or brconfig on NetBSD.", + ) def _tool_path(ostool): - ''' + """ Internal, returns tools path - ''' + """ return salt.utils.path.which(ostool) def _linux_brshow(br=None): - ''' + """ Internal, returns bridges and enslaved interfaces (GNU/Linux - brctl) - ''' - brctl = _tool_path('brctl') + """ + brctl = _tool_path("brctl") if br: - cmd = '{0} show {1}'.format(brctl, br) + cmd = "{0} show {1}".format(brctl, br) else: - cmd = '{0} show'.format(brctl) + cmd = "{0} show".format(brctl) brs = {} - for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): + for line in __salt__["cmd.run"](cmd, python_shell=False).splitlines(): # get rid of first line - if line.startswith('bridge name'): + if line.startswith("bridge name"): continue # get rid of ^\n's vals = line.split() @@ -74,14 +75,14 @@ def _linux_brshow(br=None): brname = vals[0] brs[brname] = { - 'id': vals[1], - 'stp': vals[2], + "id": vals[1], + "stp": vals[2], } if len(vals) > 3: - brs[brname]['interfaces'] = [vals[3]] + brs[brname]["interfaces"] = [vals[3]] if len(vals) == 1 and brname: - brs[brname]['interfaces'].append(vals[0]) + brs[brname]["interfaces"].append(vals[0]) if br: try: @@ -92,82 +93,80 @@ def _linux_brshow(br=None): def _linux_bradd(br): - ''' + """ Internal, creates the bridge - ''' - brctl = _tool_path('brctl') - return __salt__['cmd.run']('{0} addbr {1}'.format(brctl, br), - python_shell=False) + """ + brctl = _tool_path("brctl") + return __salt__["cmd.run"]("{0} addbr {1}".format(brctl, br), python_shell=False) def _linux_brdel(br): - ''' + """ Internal, deletes the bridge - ''' - brctl = _tool_path('brctl') - return __salt__['cmd.run']('{0} delbr {1}'.format(brctl, br), - python_shell=False) + """ + brctl = _tool_path("brctl") + return __salt__["cmd.run"]("{0} delbr {1}".format(brctl, br), python_shell=False) def _linux_addif(br, iface): - ''' + """ Internal, adds an interface to a bridge - ''' - brctl = _tool_path('brctl') - return __salt__['cmd.run']('{0} addif {1} {2}'.format(brctl, br, iface), - python_shell=False) + """ + brctl = _tool_path("brctl") + return __salt__["cmd.run"]( + "{0} addif {1} {2}".format(brctl, br, iface), python_shell=False + ) def _linux_delif(br, iface): - ''' + """ Internal, removes an interface from a bridge - ''' - brctl = _tool_path('brctl') - return __salt__['cmd.run']('{0} delif {1} {2}'.format(brctl, br, iface), - python_shell=False) + """ + brctl = _tool_path("brctl") + return __salt__["cmd.run"]( + "{0} delif {1} {2}".format(brctl, br, iface), python_shell=False + ) def _linux_stp(br, state): - ''' + """ Internal, sets STP state - ''' - brctl = _tool_path('brctl') - return __salt__['cmd.run']('{0} stp {1} {2}'.format(brctl, br, state), - python_shell=False) + """ + brctl = _tool_path("brctl") + return __salt__["cmd.run"]( + "{0} stp {1} {2}".format(brctl, br, state), python_shell=False + ) def _bsd_brshow(br=None): - ''' + """ Internal, returns bridges and member interfaces (BSD-like: ifconfig) - ''' - if __grains__['kernel'] == 'NetBSD': + """ + if __grains__["kernel"] == "NetBSD": return _netbsd_brshow(br) - ifconfig = _tool_path('ifconfig') + ifconfig = _tool_path("ifconfig") ifaces = {} if br: ifaces[br] = br else: - cmd = '{0} -g bridge'.format(ifconfig) - for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): + cmd = "{0} -g bridge".format(ifconfig) + for line in __salt__["cmd.run"](cmd, python_shell=False).splitlines(): ifaces[line] = line brs = {} for iface in ifaces: - cmd = '{0} {1}'.format(ifconfig, iface) - for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): - brs[iface] = { - 'interfaces': [], - 'stp': 'no' - } + cmd = "{0} {1}".format(ifconfig, iface) + for line in __salt__["cmd.run"](cmd, python_shell=False).splitlines(): + brs[iface] = {"interfaces": [], "stp": "no"} line = line.lstrip() - if line.startswith('member:'): - brs[iface]['interfaces'].append(line.split(' ')[1]) - if 'STP' in line: - brs[iface]['stp'] = 'yes' + if line.startswith("member:"): + brs[iface]["interfaces"].append(line.split(" ")[1]) + if "STP" in line: + brs[iface]["stp"] = "yes" if br: return brs[br] @@ -175,36 +174,33 @@ def _bsd_brshow(br=None): def _netbsd_brshow(br=None): - ''' + """ Internal, returns bridges and enslaved interfaces (NetBSD - brconfig) - ''' - brconfig = _tool_path('brconfig') + """ + brconfig = _tool_path("brconfig") if br: - cmd = '{0} {1}'.format(brconfig, br) + cmd = "{0} {1}".format(brconfig, br) else: - cmd = '{0} -a'.format(brconfig) + cmd = "{0} -a".format(brconfig) brs = {} start_int = False - for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): - if line.startswith('bridge'): + for line in __salt__["cmd.run"](cmd, python_shell=False).splitlines(): + if line.startswith("bridge"): start_int = False - brname = line.split(':')[0] # on NetBSD, always ^bridge([0-9]+): - brs[brname] = { - 'interfaces': [], - 'stp': 'no' - } - if 'Interfaces:' in line: + brname = line.split(":")[0] # on NetBSD, always ^bridge([0-9]+): + brs[brname] = {"interfaces": [], "stp": "no"} + if "Interfaces:" in line: start_int = True continue if start_int and brname: - m = re.match(r'\s*([a-z0-9]+)\s.*<.*>', line) + m = re.match(r"\s*([a-z0-9]+)\s.*<.*>", line) if m: - brs[brname]['interfaces'].append(m.group(1)) - if 'STP' in line: - brs[brname]['stp'] = 'yes' + brs[brname]["interfaces"].append(m.group(1)) + if "STP" in line: + brs[brname]["stp"] = "yes" if br: try: @@ -215,106 +211,118 @@ def _netbsd_brshow(br=None): def _bsd_bradd(br): - ''' + """ Internal, creates the bridge - ''' - kernel = __grains__['kernel'] - ifconfig = _tool_path('ifconfig') + """ + kernel = __grains__["kernel"] + ifconfig = _tool_path("ifconfig") if not br: return False - if __salt__['cmd.retcode']('{0} {1} create up'.format(ifconfig, br), - python_shell=False) != 0: + if ( + __salt__["cmd.retcode"]( + "{0} {1} create up".format(ifconfig, br), python_shell=False + ) + != 0 + ): return False # NetBSD is two cmds - if kernel == 'NetBSD': - brconfig = _tool_path('brconfig') - if __salt__['cmd.retcode']('{0} {1} up'.format(brconfig, br), - python_shell=False) != 0: + if kernel == "NetBSD": + brconfig = _tool_path("brconfig") + if ( + __salt__["cmd.retcode"]( + "{0} {1} up".format(brconfig, br), python_shell=False + ) + != 0 + ): return False return True def _bsd_brdel(br): - ''' + """ Internal, deletes the bridge - ''' - ifconfig = _tool_path('ifconfig') + """ + ifconfig = _tool_path("ifconfig") if not br: return False - return __salt__['cmd.run']('{0} {1} destroy'.format(ifconfig, br), - python_shell=False) + return __salt__["cmd.run"]( + "{0} {1} destroy".format(ifconfig, br), python_shell=False + ) def _bsd_addif(br, iface): - ''' + """ Internal, adds an interface to a bridge - ''' - kernel = __grains__['kernel'] - if kernel == 'NetBSD': - cmd = _tool_path('brconfig') - brcmd = 'add' + """ + kernel = __grains__["kernel"] + if kernel == "NetBSD": + cmd = _tool_path("brconfig") + brcmd = "add" else: - cmd = _tool_path('ifconfig') - brcmd = 'addem' + cmd = _tool_path("ifconfig") + brcmd = "addem" if not br or not iface: return False - return __salt__['cmd.run']('{0} {1} {2} {3}'.format(cmd, br, brcmd, iface), - python_shell=False) + return __salt__["cmd.run"]( + "{0} {1} {2} {3}".format(cmd, br, brcmd, iface), python_shell=False + ) def _bsd_delif(br, iface): - ''' + """ Internal, removes an interface from a bridge - ''' - kernel = __grains__['kernel'] - if kernel == 'NetBSD': - cmd = _tool_path('brconfig') - brcmd = 'delete' + """ + kernel = __grains__["kernel"] + if kernel == "NetBSD": + cmd = _tool_path("brconfig") + brcmd = "delete" else: - cmd = _tool_path('ifconfig') - brcmd = 'deletem' + cmd = _tool_path("ifconfig") + brcmd = "deletem" if not br or not iface: return False - return __salt__['cmd.run']('{0} {1} {2} {3}'.format(cmd, br, brcmd, iface), - python_shell=False) + return __salt__["cmd.run"]( + "{0} {1} {2} {3}".format(cmd, br, brcmd, iface), python_shell=False + ) def _bsd_stp(br, state, iface): - ''' + """ Internal, sets STP state. On BSD-like, it is required to specify the STP physical interface - ''' - kernel = __grains__['kernel'] - if kernel == 'NetBSD': - cmd = _tool_path('brconfig') + """ + kernel = __grains__["kernel"] + if kernel == "NetBSD": + cmd = _tool_path("brconfig") else: - cmd = _tool_path('ifconfig') + cmd = _tool_path("ifconfig") if not br or not iface: return False - return __salt__['cmd.run']('{0} {1} {2} {3}'.format(cmd, br, state, iface), - python_shell=False) + return __salt__["cmd.run"]( + "{0} {1} {2} {3}".format(cmd, br, state, iface), python_shell=False + ) def _os_dispatch(func, *args, **kwargs): - ''' + """ Internal, dispatches functions by operating system - ''' - if __grains__['kernel'] in SUPPORTED_BSD_LIKE: - kernel = 'bsd' + """ + if __grains__["kernel"] in SUPPORTED_BSD_LIKE: + kernel = "bsd" else: - kernel = __grains__['kernel'].lower() + kernel = __grains__["kernel"].lower() - _os_func = getattr(sys.modules[__name__], '_{0}_{1}'.format(kernel, func)) + _os_func = getattr(sys.modules[__name__], "_{0}_{1}".format(kernel, func)) if callable(_os_func): return _os_func(*args, **kwargs) @@ -324,7 +332,7 @@ def _os_dispatch(func, *args, **kwargs): def show(br=None): - ''' + """ Returns bridges interfaces along with enslaved physical interfaces. If no interface is given, all bridges are shown, else only the specified bridge values are returned. @@ -335,12 +343,12 @@ def show(br=None): salt '*' bridge.show salt '*' bridge.show br0 - ''' - return _os_dispatch('brshow', br) + """ + return _os_dispatch("brshow", br) def list_(): - ''' + """ Returns the machine's bridges list CLI Example: @@ -348,8 +356,8 @@ def list_(): .. code-block:: bash salt '*' bridge.list - ''' - brs = _os_dispatch('brshow') + """ + brs = _os_dispatch("brshow") if not brs: return None brlist = [] @@ -360,7 +368,7 @@ def list_(): def interfaces(br=None): - ''' + """ Returns interfaces attached to a bridge CLI Example: @@ -368,17 +376,17 @@ def interfaces(br=None): .. code-block:: bash salt '*' bridge.interfaces br0 - ''' + """ if not br: return None - br_ret = _os_dispatch('brshow', br) + br_ret = _os_dispatch("brshow", br) if br_ret: - return br_ret['interfaces'] + return br_ret["interfaces"] def find_interfaces(*args): - ''' + """ Returns the bridge to which the interfaces are bond to CLI Example: @@ -386,8 +394,8 @@ def find_interfaces(*args): .. code-block:: bash salt '*' bridge.find_interfaces eth0 [eth1...] - ''' - brs = _os_dispatch('brshow') + """ + brs = _os_dispatch("brshow") if not brs: return None @@ -396,7 +404,7 @@ def find_interfaces(*args): for iface in args: for br in brs: try: # a bridge may not contain interfaces - if iface in brs[br]['interfaces']: + if iface in brs[br]["interfaces"]: iflist[iface] = br except Exception: # pylint: disable=broad-except pass @@ -405,7 +413,7 @@ def find_interfaces(*args): def add(br=None): - ''' + """ Creates a bridge CLI Example: @@ -413,12 +421,12 @@ def add(br=None): .. code-block:: bash salt '*' bridge.add br0 - ''' - return _os_dispatch('bradd', br) + """ + return _os_dispatch("bradd", br) def delete(br=None): - ''' + """ Deletes a bridge CLI Example: @@ -426,12 +434,12 @@ def delete(br=None): .. code-block:: bash salt '*' bridge.delete br0 - ''' - return _os_dispatch('brdel', br) + """ + return _os_dispatch("brdel", br) def addif(br=None, iface=None): - ''' + """ Adds an interface to a bridge CLI Example: @@ -439,12 +447,12 @@ def addif(br=None, iface=None): .. code-block:: bash salt '*' bridge.addif br0 eth0 - ''' - return _os_dispatch('addif', br, iface) + """ + return _os_dispatch("addif", br, iface) def delif(br=None, iface=None): - ''' + """ Removes an interface from a bridge CLI Example: @@ -452,12 +460,12 @@ def delif(br=None, iface=None): .. code-block:: bash salt '*' bridge.delif br0 eth0 - ''' - return _os_dispatch('delif', br, iface) + """ + return _os_dispatch("delif", br, iface) -def stp(br=None, state='disable', iface=None): - ''' +def stp(br=None, state="disable", iface=None): + """ Sets Spanning Tree Protocol state for a bridge CLI Example: @@ -476,14 +484,14 @@ def stp(br=None, state='disable', iface=None): salt '*' bridge.stp bridge0 enable fxp0 salt '*' bridge.stp bridge0 disable fxp0 - ''' - kernel = __grains__['kernel'] - if kernel == 'Linux': - states = {'enable': 'on', 'disable': 'off'} - return _os_dispatch('stp', br, states[state]) + """ + kernel = __grains__["kernel"] + if kernel == "Linux": + states = {"enable": "on", "disable": "off"} + return _os_dispatch("stp", br, states[state]) elif kernel in SUPPORTED_BSD_LIKE: - states = {'enable': 'stp', 'disable': '-stp'} - return _os_dispatch('stp', br, states[state], iface) + states = {"enable": "stp", "disable": "-stp"} + return _os_dispatch("stp", br, states[state], iface) else: return False diff --git a/salt/modules/bsd_shadow.py b/salt/modules/bsd_shadow.py index 79e3cf962f1..db9beecfc8d 100644 --- a/salt/modules/bsd_shadow.py +++ b/salt/modules/bsd_shadow.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage the password database on BSD systems .. important:: @@ -7,34 +7,40 @@ Manage the password database on BSD systems minion, and it is using a different module (or gives an error similar to *'shadow.info' is not available*), see :ref:`here `. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import salt.utils.files +import salt.utils.stringutils +from salt.exceptions import SaltInvocationError + +# Import salt libs +from salt.ext import six + try: import pwd except ImportError: pass -# Import salt libs -from salt.ext import six -import salt.utils.files -import salt.utils.stringutils -from salt.exceptions import SaltInvocationError # Define the module's virtual name -__virtualname__ = 'shadow' +__virtualname__ = "shadow" def __virtual__(): - if 'BSD' in __grains__.get('os', ''): + if "BSD" in __grains__.get("os", ""): return __virtualname__ - return (False, 'The bsd_shadow execution module cannot be loaded: ' - 'only available on BSD family systems.') + return ( + False, + "The bsd_shadow execution module cannot be loaded: " + "only available on BSD family systems.", + ) def default_hash(): - ''' + """ Returns the default hash used for unset passwords CLI Example: @@ -42,12 +48,12 @@ def default_hash(): .. code-block:: bash salt '*' shadow.default_hash - ''' - return '*' if __grains__['os'].lower() == 'freebsd' else '*************' + """ + return "*" if __grains__["os"].lower() == "freebsd" else "*************" def info(name): - ''' + """ Return information for the specified user CLI Example: @@ -55,35 +61,31 @@ def info(name): .. code-block:: bash salt '*' shadow.info someuser - ''' + """ try: data = pwd.getpwnam(name) - ret = { - 'name': data.pw_name, - 'passwd': data.pw_passwd} + ret = {"name": data.pw_name, "passwd": data.pw_passwd} except KeyError: - return { - 'name': '', - 'passwd': ''} + return {"name": "", "passwd": ""} if not isinstance(name, six.string_types): name = six.text_type(name) - if ':' in name: - raise SaltInvocationError('Invalid username \'{0}\''.format(name)) + if ":" in name: + raise SaltInvocationError("Invalid username '{0}'".format(name)) - if __salt__['cmd.has_exec']('pw'): - change, expire = __salt__['cmd.run_stdout']( - ['pw', 'user', 'show', name], - python_shell=False).split(':')[5:7] - elif __grains__['kernel'] in ('NetBSD', 'OpenBSD'): + if __salt__["cmd.has_exec"]("pw"): + change, expire = __salt__["cmd.run_stdout"]( + ["pw", "user", "show", name], python_shell=False + ).split(":")[5:7] + elif __grains__["kernel"] in ("NetBSD", "OpenBSD"): try: - with salt.utils.files.fopen('/etc/master.passwd', 'r') as fp_: + with salt.utils.files.fopen("/etc/master.passwd", "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('{0}:'.format(name)): - key = line.split(':') + if line.startswith("{0}:".format(name)): + key = line.split(":") change, expire = key[5:7] - ret['passwd'] = six.text_type(key[1]) + ret["passwd"] = six.text_type(key[1]) break except IOError: change = expire = None @@ -91,12 +93,12 @@ def info(name): change = expire = None try: - ret['change'] = int(change) + ret["change"] = int(change) except ValueError: pass try: - ret['expire'] = int(expire) + ret["expire"] = int(expire) except ValueError: pass @@ -104,7 +106,7 @@ def info(name): def set_change(name, change): - ''' + """ Sets the time at which the password expires (in seconds since the UNIX epoch). See ``man 8 usermod`` on NetBSD and OpenBSD or ``man 8 pw`` on FreeBSD. @@ -116,22 +118,22 @@ def set_change(name, change): .. code-block:: bash salt '*' shadow.set_change username 1419980400 - ''' + """ pre_info = info(name) - if change == pre_info['change']: + if change == pre_info["change"]: return True - if __grains__['kernel'] == 'FreeBSD': - cmd = ['pw', 'user', 'mod', name, '-f', change] + if __grains__["kernel"] == "FreeBSD": + cmd = ["pw", "user", "mod", name, "-f", change] else: - cmd = ['usermod', '-f', change, name] - __salt__['cmd.run'](cmd, python_shell=False) + cmd = ["usermod", "-f", change, name] + __salt__["cmd.run"](cmd, python_shell=False) post_info = info(name) - if post_info['change'] != pre_info['change']: - return post_info['change'] == change + if post_info["change"] != pre_info["change"]: + return post_info["change"] == change def set_expire(name, expire): - ''' + """ Sets the time at which the account expires (in seconds since the UNIX epoch). See ``man 8 usermod`` on NetBSD and OpenBSD or ``man 8 pw`` on FreeBSD. @@ -143,22 +145,22 @@ def set_expire(name, expire): .. code-block:: bash salt '*' shadow.set_expire username 1419980400 - ''' + """ pre_info = info(name) - if expire == pre_info['expire']: + if expire == pre_info["expire"]: return True - if __grains__['kernel'] == 'FreeBSD': - cmd = ['pw', 'user', 'mod', name, '-e', expire] + if __grains__["kernel"] == "FreeBSD": + cmd = ["pw", "user", "mod", name, "-e", expire] else: - cmd = ['usermod', '-e', expire, name] - __salt__['cmd.run'](cmd, python_shell=False) + cmd = ["usermod", "-e", expire, name] + __salt__["cmd.run"](cmd, python_shell=False) post_info = info(name) - if post_info['expire'] != pre_info['expire']: - return post_info['expire'] == expire + if post_info["expire"] != pre_info["expire"]: + return post_info["expire"] == expire def del_password(name): - ''' + """ .. versionadded:: 2015.8.2 Delete the password from name user @@ -168,15 +170,15 @@ def del_password(name): .. code-block:: bash salt '*' shadow.del_password username - ''' - cmd = 'pw user mod {0} -w none'.format(name) - __salt__['cmd.run'](cmd, python_shell=False, output_loglevel='quiet') + """ + cmd = "pw user mod {0} -w none".format(name) + __salt__["cmd.run"](cmd, python_shell=False, output_loglevel="quiet") uinfo = info(name) - return not uinfo['passwd'] + return not uinfo["passwd"] def set_password(name, password): - ''' + """ Set the password for a named user. The password must be a properly defined hash. The password hash can be generated with this command: @@ -202,15 +204,12 @@ def set_password(name, password): .. code-block:: bash salt '*' shadow.set_password someuser '$1$UYCIxa628.9qXjpQCjM4a..' - ''' - if __grains__.get('os', '') == 'FreeBSD': - cmd = ['pw', 'user', 'mod', name, '-H', '0'] + """ + if __grains__.get("os", "") == "FreeBSD": + cmd = ["pw", "user", "mod", name, "-H", "0"] stdin = password else: - cmd = ['usermod', '-p', password, name] + cmd = ["usermod", "-p", password, name] stdin = None - __salt__['cmd.run'](cmd, - stdin=stdin, - output_loglevel='quiet', - python_shell=False) - return info(name)['passwd'] == password + __salt__["cmd.run"](cmd, stdin=stdin, output_loglevel="quiet", python_shell=False) + return info(name)["passwd"] == password diff --git a/salt/modules/btrfs.py b/salt/modules/btrfs.py index e0ac08c19a5..bde9cb64d9c 100644 --- a/salt/modules/btrfs.py +++ b/salt/modules/btrfs.py @@ -14,12 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -''' +""" Module for managing BTRFS file systems. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import itertools import os import re @@ -35,14 +36,14 @@ from salt.ext import six def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' - return not salt.utils.platform.is_windows() and __grains__.get('kernel') == 'Linux' + """ + return not salt.utils.platform.is_windows() and __grains__.get("kernel") == "Linux" def version(): - ''' + """ Return BTRFS version. CLI Example: @@ -50,40 +51,40 @@ def version(): .. code-block:: bash salt '*' btrfs.version - ''' - out = __salt__['cmd.run_all']("btrfs --version") - if out.get('stderr'): - raise CommandExecutionError(out['stderr']) - return {'version': out['stdout'].split(" ", 1)[-1]} + """ + out = __salt__["cmd.run_all"]("btrfs --version") + if out.get("stderr"): + raise CommandExecutionError(out["stderr"]) + return {"version": out["stdout"].split(" ", 1)[-1]} def _parse_btrfs_info(data): - ''' + """ Parse BTRFS device info data. - ''' + """ ret = {} for line in [line for line in data.split("\n") if line][:-1]: if line.startswith("Label:"): line = re.sub(r"Label:\s+", "", line) label, uuid_ = [tkn.strip() for tkn in line.split("uuid:")] - ret['label'] = label != 'none' and label or None - ret['uuid'] = uuid_ + ret["label"] = label != "none" and label or None + ret["uuid"] = uuid_ continue if line.startswith("\tdevid"): dev_data = re.split(r"\s+", line.strip()) dev_id = dev_data[-1] ret[dev_id] = { - 'device_id': dev_data[1], - 'size': dev_data[3], - 'used': dev_data[5], - } + "device_id": dev_data[1], + "size": dev_data[3], + "used": dev_data[5], + } return ret def info(device): - ''' + """ Get BTRFS filesystem information. CLI Example: @@ -91,15 +92,15 @@ def info(device): .. code-block:: bash salt '*' btrfs.info /dev/sda1 - ''' - out = __salt__['cmd.run_all']("btrfs filesystem show {0}".format(device)) + """ + out = __salt__["cmd.run_all"]("btrfs filesystem show {0}".format(device)) salt.utils.fsutils._verify_run(out) - return _parse_btrfs_info(out['stdout']) + return _parse_btrfs_info(out["stdout"]) def devices(): - ''' + """ Get known BTRFS formatted devices on the system. CLI Example: @@ -107,28 +108,30 @@ def devices(): .. code-block:: bash salt '*' btrfs.devices - ''' - out = __salt__['cmd.run_all']("blkid -o export") + """ + out = __salt__["cmd.run_all"]("blkid -o export") salt.utils.fsutils._verify_run(out) - return salt.utils.fsutils._blkid_output(out['stdout'], fs_type='btrfs') + return salt.utils.fsutils._blkid_output(out["stdout"], fs_type="btrfs") def _defragment_mountpoint(mountpoint): - ''' + """ Defragment only one BTRFS mountpoint. - ''' - out = __salt__['cmd.run_all']("btrfs filesystem defragment -f {0}".format(mountpoint)) + """ + out = __salt__["cmd.run_all"]( + "btrfs filesystem defragment -f {0}".format(mountpoint) + ) return { - 'mount_point': mountpoint, - 'passed': not out['stderr'], - 'log': out['stderr'] or False, - 'range': False, + "mount_point": mountpoint, + "passed": not out["stderr"], + "log": out["stderr"] or False, + "range": False, } def defragment(path): - ''' + """ Defragment mounted BTRFS filesystem. In order to defragment a filesystem, device should be properly mounted and writable. @@ -141,30 +144,36 @@ def defragment(path): salt '*' btrfs.defragment /dev/sda1 salt '*' btrfs.defragment /path/on/filesystem - ''' + """ is_device = salt.utils.fsutils._is_device(path) mounts = salt.utils.fsutils._get_mounts("btrfs") if is_device and not mounts.get(path): - raise CommandExecutionError("Device \"{0}\" is not mounted".format(path)) + raise CommandExecutionError('Device "{0}" is not mounted'.format(path)) result = [] if is_device: for mount_point in mounts[path]: - result.append(_defragment_mountpoint(mount_point['mount_point'])) + result.append(_defragment_mountpoint(mount_point["mount_point"])) else: is_mountpoint = False for mountpoints in six.itervalues(mounts): for mpnt in mountpoints: - if path == mpnt['mount_point']: + if path == mpnt["mount_point"]: is_mountpoint = True break d_res = _defragment_mountpoint(path) - if not is_mountpoint and not d_res['passed'] and "range ioctl not supported" in d_res['log']: - d_res['log'] = "Range ioctl defragmentation is not supported in this kernel." + if ( + not is_mountpoint + and not d_res["passed"] + and "range ioctl not supported" in d_res["log"] + ): + d_res[ + "log" + ] = "Range ioctl defragmentation is not supported in this kernel." if not is_mountpoint: - d_res['mount_point'] = False - d_res['range'] = os.path.exists(path) and path or False + d_res["mount_point"] = False + d_res["range"] = os.path.exists(path) and path or False result.append(d_res) @@ -172,7 +181,7 @@ def defragment(path): def features(): - ''' + """ List currently available BTRFS features. CLI Example: @@ -180,12 +189,14 @@ def features(): .. code-block:: bash salt '*' btrfs.mkfs_features - ''' - out = __salt__['cmd.run_all']("mkfs.btrfs -O list-all") + """ + out = __salt__["cmd.run_all"]("mkfs.btrfs -O list-all") salt.utils.fsutils._verify_run(out) ret = {} - for line in [re.sub(r"\s+", " ", line) for line in out['stderr'].split("\n") if " - " in line]: + for line in [ + re.sub(r"\s+", " ", line) for line in out["stderr"].split("\n") if " - " in line + ]: option, description = line.split(" - ", 1) ret[option] = description @@ -193,15 +204,19 @@ def features(): def _usage_overall(raw): - ''' + """ Parse usage/overall. - ''' + """ data = {} for line in raw.split("\n")[1:]: - keyset = [item.strip() for item in re.sub(r"\s+", " ", line).split(":", 1) if item.strip()] + keyset = [ + item.strip() + for item in re.sub(r"\s+", " ", line).split(":", 1) + if item.strip() + ] if len(keyset) == 2: key = re.sub(r"[()]", "", keyset[0]).replace(" ", "_").lower() - if key in ['free_estimated', 'global_reserve']: # An extra field + if key in ["free_estimated", "global_reserve"]: # An extra field subk = keyset[1].split("(") data[key] = subk[0].strip() subk = subk[1].replace(")", "").split(": ") @@ -213,10 +228,10 @@ def _usage_overall(raw): def _usage_specific(raw): - ''' + """ Parse usage/specific. - ''' - get_key = lambda val: dict([tuple(val.split(":")), ]) + """ + get_key = lambda val: dict([tuple(val.split(":"))]) raw = raw.split("\n") section, size, used = raw[0].split(" ") section = section.replace(",", "_").replace(":", "").lower() @@ -234,9 +249,9 @@ def _usage_specific(raw): def _usage_unallocated(raw): - ''' + """ Parse usage/unallocated. - ''' + """ ret = {} for line in raw.split("\n")[1:]: keyset = re.sub(r"\s+", " ", line.strip()).split(" ") @@ -247,7 +262,7 @@ def _usage_unallocated(raw): def usage(path): - ''' + """ Show in which disk the chunks are allocated. CLI Example: @@ -255,16 +270,16 @@ def usage(path): .. code-block:: bash salt '*' btrfs.usage /your/mountpoint - ''' - out = __salt__['cmd.run_all']("btrfs filesystem usage {0}".format(path)) + """ + out = __salt__["cmd.run_all"]("btrfs filesystem usage {0}".format(path)) salt.utils.fsutils._verify_run(out) ret = {} - for section in out['stdout'].split("\n\n"): + for section in out["stdout"].split("\n\n"): if section.startswith("Overall:\n"): - ret['overall'] = _usage_overall(section) + ret["overall"] = _usage_overall(section) elif section.startswith("Unallocated:\n"): - ret['unallocated'] = _usage_unallocated(section) + ret["unallocated"] = _usage_unallocated(section) else: ret.update(_usage_specific(section)) @@ -272,7 +287,7 @@ def usage(path): def mkfs(*devices, **kwargs): - ''' + """ Create a file system on the specified device. By default wipes out with force. General options: @@ -303,14 +318,16 @@ def mkfs(*devices, **kwargs): salt '*' btrfs.mkfs /dev/sda1 salt '*' btrfs.mkfs /dev/sda1 noforce=True - ''' + """ if not devices: raise CommandExecutionError("No devices specified") mounts = salt.utils.fsutils._get_mounts("btrfs") for device in devices: if mounts.get(device): - raise CommandExecutionError("Device \"{0}\" should not be mounted".format(device)) + raise CommandExecutionError( + 'Device "{0}" should not be mounted'.format(device) + ) cmd = ["mkfs.btrfs"] @@ -327,16 +344,26 @@ def mkfs(*devices, **kwargs): if mto: cmd.append("-m {0}".format(mto)) - for key, option in [("-l", "leafsize"), ("-L", "label"), ("-O", "fts"), - ("-A", "allocsize"), ("-b", "bytecount"), ("-n", "nodesize"), - ("-s", "sectorsize")]: - if option == 'label' and option in kwargs: - kwargs['label'] = "'{0}'".format(kwargs["label"]) + for key, option in [ + ("-l", "leafsize"), + ("-L", "label"), + ("-O", "fts"), + ("-A", "allocsize"), + ("-b", "bytecount"), + ("-n", "nodesize"), + ("-s", "sectorsize"), + ]: + if option == "label" and option in kwargs: + kwargs["label"] = "'{0}'".format(kwargs["label"]) if kwargs.get(option): cmd.append("{0} {1}".format(key, kwargs.get(option))) if kwargs.get("uuid"): - cmd.append("-U {0}".format(kwargs.get("uuid") is True and uuid.uuid1() or kwargs.get("uuid"))) + cmd.append( + "-U {0}".format( + kwargs.get("uuid") is True and uuid.uuid1() or kwargs.get("uuid") + ) + ) if kwargs.get("nodiscard"): cmd.append("-K") @@ -345,17 +372,17 @@ def mkfs(*devices, **kwargs): cmd.extend(devices) - out = __salt__['cmd.run_all'](' '.join(cmd)) + out = __salt__["cmd.run_all"](" ".join(cmd)) salt.utils.fsutils._verify_run(out) - ret = {'log': out['stdout']} - ret.update(__salt__['btrfs.info'](devices[0])) + ret = {"log": out["stdout"]} + ret.update(__salt__["btrfs.info"](devices[0])) return ret def resize(mountpoint, size): - ''' + """ Resize filesystem. General options: @@ -369,49 +396,66 @@ def resize(mountpoint, size): salt '*' btrfs.resize /mountpoint size=+1g salt '*' btrfs.resize /dev/sda1 size=max - ''' + """ - if size == 'max': + if size == "max": if not salt.utils.fsutils._is_device(mountpoint): - raise CommandExecutionError("Mountpoint \"{0}\" should be a valid device".format(mountpoint)) + raise CommandExecutionError( + 'Mountpoint "{0}" should be a valid device'.format(mountpoint) + ) if not salt.utils.fsutils._get_mounts("btrfs").get(mountpoint): - raise CommandExecutionError("Device \"{0}\" should be mounted".format(mountpoint)) - elif len(size) < 3 or size[0] not in '-+' \ - or size[-1] not in 'kKmMgGtTpPeE' or re.sub(r"\d", "", size[1:][:-1]): - raise CommandExecutionError("Unknown size: \"{0}\". Expected: [+/-][kKmMgGtTpPeE]|max".format(size)) + raise CommandExecutionError( + 'Device "{0}" should be mounted'.format(mountpoint) + ) + elif ( + len(size) < 3 + or size[0] not in "-+" + or size[-1] not in "kKmMgGtTpPeE" + or re.sub(r"\d", "", size[1:][:-1]) + ): + raise CommandExecutionError( + 'Unknown size: "{0}". Expected: [+/-][kKmMgGtTpPeE]|max'.format( + size + ) + ) - out = __salt__['cmd.run_all']('btrfs filesystem resize {0} {1}'.format(size, mountpoint)) + out = __salt__["cmd.run_all"]( + "btrfs filesystem resize {0} {1}".format(size, mountpoint) + ) salt.utils.fsutils._verify_run(out) - ret = {'log': out['stdout']} - ret.update(__salt__['btrfs.info'](mountpoint)) + ret = {"log": out["stdout"]} + ret.update(__salt__["btrfs.info"](mountpoint)) return ret def _fsck_ext(device): - ''' + """ Check an ext2/ext3/ext4 file system. This is forced check to determine a filesystem is clean or not. NOTE: Maybe this function needs to be moved as a standard method in extfs module in a future. - ''' + """ msgs = { - 0: 'No errors', - 1: 'Filesystem errors corrected', - 2: 'System should be rebooted', - 4: 'Filesystem errors left uncorrected', - 8: 'Operational error', - 16: 'Usage or syntax error', - 32: 'Fsck canceled by user request', - 128: 'Shared-library error', + 0: "No errors", + 1: "Filesystem errors corrected", + 2: "System should be rebooted", + 4: "Filesystem errors left uncorrected", + 8: "Operational error", + 16: "Usage or syntax error", + 32: "Fsck canceled by user request", + 128: "Shared-library error", } - return msgs.get(__salt__['cmd.run_all']("fsck -f -n {0}".format(device))['retcode'], 'Unknown error') + return msgs.get( + __salt__["cmd.run_all"]("fsck -f -n {0}".format(device))["retcode"], + "Unknown error", + ) def convert(device, permanent=False, keeplf=False): - ''' + """ Convert ext2/3/4 to BTRFS. Device should be mounted. Filesystem can be converted temporarily so the further processing and rollback is possible, @@ -430,22 +474,29 @@ def convert(device, permanent=False, keeplf=False): salt '*' btrfs.convert /dev/sda1 salt '*' btrfs.convert /dev/sda1 permanent=True - ''' + """ - out = __salt__['cmd.run_all']("blkid -o export") + out = __salt__["cmd.run_all"]("blkid -o export") salt.utils.fsutils._verify_run(out) - devices = salt.utils.fsutils._blkid_output(out['stdout']) + devices = salt.utils.fsutils._blkid_output(out["stdout"]) if not devices.get(device): - raise CommandExecutionError("The device \"{0}\" was is not found.".format(device)) + raise CommandExecutionError('The device "{0}" was is not found.'.format(device)) - if not devices[device]["type"] in ['ext2', 'ext3', 'ext4']: - raise CommandExecutionError("The device \"{0}\" is a \"{1}\" file system.".format( - device, devices[device]["type"])) + if not devices[device]["type"] in ["ext2", "ext3", "ext4"]: + raise CommandExecutionError( + 'The device "{0}" is a "{1}" file system.'.format( + device, devices[device]["type"] + ) + ) - mountpoint = salt.utils.fsutils._get_mounts(devices[device]["type"]).get( - device, [{'mount_point': None}])[0].get('mount_point') - if mountpoint == '/': - raise CommandExecutionError("""One does not simply converts a root filesystem! + mountpoint = ( + salt.utils.fsutils._get_mounts(devices[device]["type"]) + .get(device, [{"mount_point": None}])[0] + .get("mount_point") + ) + if mountpoint == "/": + raise CommandExecutionError( + """One does not simply converts a root filesystem! Converting an extended root filesystem to BTRFS is a careful and lengthy process, among other steps including the following @@ -457,94 +508,107 @@ requirements: For further details, please refer to your OS vendor documentation regarding this topic. -""") +""" + ) - salt.utils.fsutils._verify_run(__salt__['cmd.run_all']("umount {0}".format(device))) + salt.utils.fsutils._verify_run(__salt__["cmd.run_all"]("umount {0}".format(device))) ret = { - 'before': { - 'fsck_status': _fsck_ext(device), - 'mount_point': mountpoint, - 'type': devices[device]["type"], + "before": { + "fsck_status": _fsck_ext(device), + "mount_point": mountpoint, + "type": devices[device]["type"], } } - salt.utils.fsutils._verify_run(__salt__['cmd.run_all']("btrfs-convert {0}".format(device))) - salt.utils.fsutils._verify_run(__salt__['cmd.run_all']("mount {0} {1}".format(device, mountpoint))) + salt.utils.fsutils._verify_run( + __salt__["cmd.run_all"]("btrfs-convert {0}".format(device)) + ) + salt.utils.fsutils._verify_run( + __salt__["cmd.run_all"]("mount {0} {1}".format(device, mountpoint)) + ) # Refresh devices - out = __salt__['cmd.run_all']("blkid -o export") + out = __salt__["cmd.run_all"]("blkid -o export") salt.utils.fsutils._verify_run(out) - devices = salt.utils.fsutils._blkid_output(out['stdout']) + devices = salt.utils.fsutils._blkid_output(out["stdout"]) - ret['after'] = { - 'fsck_status': "N/A", # ToDO - 'mount_point': mountpoint, - 'type': devices[device]["type"], + ret["after"] = { + "fsck_status": "N/A", # ToDO + "mount_point": mountpoint, + "type": devices[device]["type"], } # Post-migration procedures image_path = "{0}/ext2_saved".format(mountpoint) - orig_fstype = ret['before']['type'] + orig_fstype = ret["before"]["type"] if not os.path.exists(image_path): raise CommandExecutionError( - "BTRFS migration went wrong: the image \"{0}\" not found!".format(image_path)) + 'BTRFS migration went wrong: the image "{0}" not found!'.format(image_path) + ) if not permanent: - ret['after']['{0}_image'.format(orig_fstype)] = image_path - ret['after']['{0}_image_info'.format(orig_fstype)] = os.popen( - "file {0}/image".format(image_path)).read().strip() + ret["after"]["{0}_image".format(orig_fstype)] = image_path + ret["after"]["{0}_image_info".format(orig_fstype)] = ( + os.popen("file {0}/image".format(image_path)).read().strip() + ) else: - ret['after']['{0}_image'.format(orig_fstype)] = 'removed' - ret['after']['{0}_image_info'.format(orig_fstype)] = 'N/A' + ret["after"]["{0}_image".format(orig_fstype)] = "removed" + ret["after"]["{0}_image_info".format(orig_fstype)] = "N/A" - salt.utils.fsutils._verify_run(__salt__['cmd.run_all']("btrfs subvolume delete {0}".format(image_path))) - out = __salt__['cmd.run_all']("btrfs filesystem balance {0}".format(mountpoint)) + salt.utils.fsutils._verify_run( + __salt__["cmd.run_all"]("btrfs subvolume delete {0}".format(image_path)) + ) + out = __salt__["cmd.run_all"]("btrfs filesystem balance {0}".format(mountpoint)) salt.utils.fsutils._verify_run(out) - ret['after']['balance_log'] = out['stdout'] + ret["after"]["balance_log"] = out["stdout"] lost_found = "{0}/lost+found".format(mountpoint) if os.path.exists(lost_found) and not keeplf: - salt.utils.fsutils._verify_run(__salt__['cmd.run_all']("rm -rf {0}".format(lost_found))) + salt.utils.fsutils._verify_run( + __salt__["cmd.run_all"]("rm -rf {0}".format(lost_found)) + ) return ret def _restripe(mountpoint, direction, *devices, **kwargs): - ''' + """ Restripe BTRFS: add or remove devices from the particular mounted filesystem. - ''' + """ fs_log = [] if salt.utils.fsutils._is_device(mountpoint): raise CommandExecutionError( - "Mountpount expected, while device \"{0}\" specified".format(mountpoint)) + 'Mountpount expected, while device "{0}" specified'.format(mountpoint) + ) mounted = False for device, mntpoints in six.iteritems(salt.utils.fsutils._get_mounts("btrfs")): for mntdata in mntpoints: - if mntdata['mount_point'] == mountpoint: + if mntdata["mount_point"] == mountpoint: mounted = True break if not mounted: raise CommandExecutionError( - "No BTRFS device mounted on \"{0}\" mountpoint".format(mountpoint)) + 'No BTRFS device mounted on "{0}" mountpoint'.format(mountpoint) + ) if not devices: raise CommandExecutionError("No devices specified.") - available_devices = __salt__['btrfs.devices']() + available_devices = __salt__["btrfs.devices"]() for device in devices: if device not in six.iterkeys(available_devices): - raise CommandExecutionError("Device \"{0}\" is not recognized".format(device)) + raise CommandExecutionError('Device "{0}" is not recognized'.format(device)) - cmd = ['btrfs device {0}'.format(direction)] + cmd = ["btrfs device {0}".format(direction)] for device in devices: cmd.append(device) - if direction == 'add': + if direction == "add": if kwargs.get("nodiscard"): cmd.append("-K") if kwargs.get("force"): @@ -552,36 +616,40 @@ def _restripe(mountpoint, direction, *devices, **kwargs): cmd.append(mountpoint) - out = __salt__['cmd.run_all'](' '.join(cmd)) + out = __salt__["cmd.run_all"](" ".join(cmd)) salt.utils.fsutils._verify_run(out) - if out['stdout']: - fs_log.append(out['stdout']) + if out["stdout"]: + fs_log.append(out["stdout"]) - if direction == 'add': + if direction == "add": out = None data_conversion = kwargs.get("dc") meta_conversion = kwargs.get("mc") if data_conversion and meta_conversion: - out = __salt__['cmd.run_all']( + out = __salt__["cmd.run_all"]( "btrfs balance start -dconvert={0} -mconvert={1} {2}".format( - data_conversion, meta_conversion, mountpoint)) + data_conversion, meta_conversion, mountpoint + ) + ) else: - out = __salt__['cmd.run_all']("btrfs filesystem balance {0}".format(mountpoint)) + out = __salt__["cmd.run_all"]( + "btrfs filesystem balance {0}".format(mountpoint) + ) salt.utils.fsutils._verify_run(out) - if out['stdout']: - fs_log.append(out['stdout']) + if out["stdout"]: + fs_log.append(out["stdout"]) # Summarize the result ret = {} if fs_log: - ret.update({'log': '\n'.join(fs_log)}) - ret.update(__salt__['btrfs.info'](mountpoint)) + ret.update({"log": "\n".join(fs_log)}) + ret.update(__salt__["btrfs.info"](mountpoint)) return ret def add(mountpoint, *devices, **kwargs): - ''' + """ Add a devices to a BTRFS filesystem. General options: @@ -594,12 +662,12 @@ def add(mountpoint, *devices, **kwargs): .. code-block:: bash salt '*' btrfs.add /mountpoint /dev/sda1 /dev/sda2 - ''' - return _restripe(mountpoint, 'add', *devices, **kwargs) + """ + return _restripe(mountpoint, "add", *devices, **kwargs) def delete(mountpoint, *devices, **kwargs): - ''' + """ Remove devices from a BTRFS filesystem. CLI Example: @@ -607,14 +675,14 @@ def delete(mountpoint, *devices, **kwargs): .. code-block:: bash salt '*' btrfs.delete /mountpoint /dev/sda1 /dev/sda2 - ''' - return _restripe(mountpoint, 'delete', *devices, **kwargs) + """ + return _restripe(mountpoint, "delete", *devices, **kwargs) def _parse_proplist(data): - ''' + """ Parse properties list. - ''' + """ out = {} for line in data.split("\n"): line = re.split(r"\s+", line, 1) @@ -625,7 +693,7 @@ def _parse_proplist(data): def properties(obj, type=None, set=None): - ''' + """ List properties for given btrfs object. The object can be path of BTRFS device, mount point, or any directories/files inside the BTRFS filesystem. @@ -641,42 +709,56 @@ def properties(obj, type=None, set=None): salt '*' btrfs.properties /mountpoint salt '*' btrfs.properties /dev/sda1 type=subvol set='ro=false,label="My Storage"' - ''' - if type and type not in ['s', 'subvol', 'f', 'filesystem', 'i', 'inode', 'd', 'device']: - raise CommandExecutionError("Unknown property type: \"{0}\" specified".format(type)) + """ + if type and type not in [ + "s", + "subvol", + "f", + "filesystem", + "i", + "inode", + "d", + "device", + ]: + raise CommandExecutionError( + 'Unknown property type: "{0}" specified'.format(type) + ) - cmd = ['btrfs'] - cmd.append('property') - cmd.append(set and 'set' or 'list') + cmd = ["btrfs"] + cmd.append("property") + cmd.append(set and "set" or "list") if type: - cmd.append('-t{0}'.format(type)) + cmd.append("-t{0}".format(type)) cmd.append(obj) if set: try: - for key, value in [[item.strip() for item in keyset.split("=")] - for keyset in set.split(",")]: + for key, value in [ + [item.strip() for item in keyset.split("=")] + for keyset in set.split(",") + ]: cmd.append(key) cmd.append(value) except Exception as ex: # pylint: disable=broad-except raise CommandExecutionError(ex) - out = __salt__['cmd.run_all'](' '.join(cmd)) + out = __salt__["cmd.run_all"](" ".join(cmd)) salt.utils.fsutils._verify_run(out) if not set: ret = {} - for prop, descr in six.iteritems(_parse_proplist(out['stdout'])): - ret[prop] = {'description': descr} - value = __salt__['cmd.run_all']( - "btrfs property get {0} {1}".format(obj, prop))['stdout'] - ret[prop]['value'] = value and value.split("=")[-1] or "N/A" + for prop, descr in six.iteritems(_parse_proplist(out["stdout"])): + ret[prop] = {"description": descr} + value = __salt__["cmd.run_all"]( + "btrfs property get {0} {1}".format(obj, prop) + )["stdout"] + ret[prop]["value"] = value and value.split("=")[-1] or "N/A" return ret def subvolume_exists(path): - ''' + """ Check if a subvolume is present in the filesystem. path @@ -688,13 +770,13 @@ def subvolume_exists(path): salt '*' btrfs.subvolume_exists /mnt/var - ''' - cmd = ['btrfs', 'subvolume', 'show', path] - return __salt__['cmd.retcode'](cmd, ignore_retcode=True) == 0 + """ + cmd = ["btrfs", "subvolume", "show", path] + return __salt__["cmd.retcode"](cmd, ignore_retcode=True) == 0 def subvolume_create(name, dest=None, qgroupids=None): - ''' + """ Create subvolume `name` in `dest`. Return True if the subvolume is created, False is the subvolume is @@ -719,9 +801,9 @@ def subvolume_create(name, dest=None, qgroupids=None): salt '*' btrfs.subvolume_create var dest=/mnt salt '*' btrfs.subvolume_create var qgroupids='[200]' - ''' + """ if qgroupids and type(qgroupids) is not list: - raise CommandExecutionError('Qgroupids parameter must be a list') + raise CommandExecutionError("Qgroupids parameter must be a list") if dest: name = os.path.join(dest, name) @@ -730,19 +812,19 @@ def subvolume_create(name, dest=None, qgroupids=None): if subvolume_exists(name): return False - cmd = ['btrfs', 'subvolume', 'create'] + cmd = ["btrfs", "subvolume", "create"] if type(qgroupids) is list: - cmd.append('-i') + cmd.append("-i") cmd.extend(qgroupids) cmd.append(name) - res = __salt__['cmd.run_all'](cmd) + res = __salt__["cmd.run_all"](cmd) salt.utils.fsutils._verify_run(res) return True def subvolume_delete(name=None, names=None, commit=None): - ''' + """ Delete the subvolume(s) from the filesystem The user can remove one single subvolume (name) or multiple of @@ -772,35 +854,36 @@ def subvolume_delete(name=None, names=None, commit=None): salt '*' btrfs.subvolume_delete /var/volumes/tmp salt '*' btrfs.subvolume_delete /var/volumes/tmp commit=after - ''' + """ if not name and not (names and type(names) is list): - raise CommandExecutionError('Provide a value for the name parameter') + raise CommandExecutionError("Provide a value for the name parameter") - if commit and commit not in ('after', 'each'): - raise CommandExecutionError('Value for commit not recognized') + if commit and commit not in ("after", "each"): + raise CommandExecutionError("Value for commit not recognized") # Filter the names and take the ones that are still there - names = [n for n in itertools.chain([name], names or []) - if n and subvolume_exists(n)] + names = [ + n for n in itertools.chain([name], names or []) if n and subvolume_exists(n) + ] # If the subvolumes are gone, we are done if not names: return False - cmd = ['btrfs', 'subvolume', 'delete'] - if commit == 'after': - cmd.append('--commit-after') - elif commit == 'each': - cmd.append('--commit-each') + cmd = ["btrfs", "subvolume", "delete"] + if commit == "after": + cmd.append("--commit-after") + elif commit == "each": + cmd.append("--commit-each") cmd.extend(names) - res = __salt__['cmd.run_all'](cmd) + res = __salt__["cmd.run_all"](cmd) salt.utils.fsutils._verify_run(res) return True def subvolume_find_new(name, last_gen): - ''' + """ List the recently modified files in a subvolume name @@ -815,25 +898,25 @@ def subvolume_find_new(name, last_gen): salt '*' btrfs.subvolume_find_new /var/volumes/tmp 1024 - ''' - cmd = ['btrfs', 'subvolume', 'find-new', name, last_gen] + """ + cmd = ["btrfs", "subvolume", "find-new", name, last_gen] - res = __salt__['cmd.run_all'](cmd) + res = __salt__["cmd.run_all"](cmd) salt.utils.fsutils._verify_run(res) - lines = res['stdout'].splitlines() + lines = res["stdout"].splitlines() # Filenames are at the end of each inode line - files = [l.split()[-1] for l in lines if l.startswith('inode')] + files = [l.split()[-1] for l in lines if l.startswith("inode")] # The last transid is in the last line transid = lines[-1].split()[-1] return { - 'files': files, - 'transid': transid, + "files": files, + "transid": transid, } def subvolume_get_default(path): - ''' + """ Get the default subvolume of the filesystem path path @@ -845,13 +928,13 @@ def subvolume_get_default(path): salt '*' btrfs.subvolume_get_default /var/volumes/tmp - ''' - cmd = ['btrfs', 'subvolume', 'get-default', path] + """ + cmd = ["btrfs", "subvolume", "get-default", path] - res = __salt__['cmd.run_all'](cmd) + res = __salt__["cmd.run_all"](cmd) salt.utils.fsutils._verify_run(res) - line = res['stdout'].strip() + line = res["stdout"].strip() # The ID is the second parameter, and the name the last one, or # '(FS_TREE)' # @@ -864,13 +947,13 @@ def subvolume_get_default(path): id_ = line.split()[1] name = line.split()[-1] return { - 'id': id_, - 'name': name, + "id": id_, + "name": name, } def _pop(line, key, use_rest): - ''' + """ Helper for the line parser. If key is a prefix of line, will remove ir from the line and will @@ -879,25 +962,36 @@ def _pop(line, key, use_rest): If use_rest is True, the value will be the rest of the line. Return a tuple with the value and the rest of the line. - ''' + """ value = None if line.startswith(key): - line = line[len(key):].strip() + line = line[len(key) :].strip() if use_rest: value = line - line = '' + line = "" else: - value, line = line.split(' ', 1) + value, line = line.split(" ", 1) return value, line.strip() -def subvolume_list(path, parent_id=False, absolute=False, - ogeneration=False, generation=False, - subvolumes=False, uuid=False, parent_uuid=False, - sent_subvolume_uuid=False, snapshots=False, - readonly=False, deleted=False, generation_cmp=None, - ogeneration_cmp=None, sort=None): - ''' +def subvolume_list( + path, + parent_id=False, + absolute=False, + ogeneration=False, + generation=False, + subvolumes=False, + uuid=False, + parent_uuid=False, + sent_subvolume_uuid=False, + snapshots=False, + readonly=False, + deleted=False, + generation_cmp=None, + ogeneration_cmp=None, + sort=None, +): + """ List the subvolumes present in the filesystem. path @@ -966,45 +1060,49 @@ def subvolume_list(path, parent_id=False, absolute=False, salt '*' btrfs.subvolume_list /var/volumes/tmp path=True salt '*' btrfs.subvolume_list /var/volumes/tmp sort='[-rootid]' - ''' + """ if sort and type(sort) is not list: - raise CommandExecutionError('Sort parameter must be a list') + raise CommandExecutionError("Sort parameter must be a list") valid_sorts = [ - ''.join((order, attrib)) for order, attrib in itertools.product( - ('-', '', '+'), ('rootid', 'gen', 'ogen', 'path')) + "".join((order, attrib)) + for order, attrib in itertools.product( + ("-", "", "+"), ("rootid", "gen", "ogen", "path") + ) ] if sort and not all(s in valid_sorts for s in sort): - raise CommandExecutionError('Value for sort not recognized') + raise CommandExecutionError("Value for sort not recognized") - cmd = ['btrfs', 'subvolume', 'list'] + cmd = ["btrfs", "subvolume", "list"] - params = ((parent_id, '-p'), - (absolute, '-a'), - (ogeneration, '-c'), - (generation, '-g'), - (subvolumes, '-o'), - (uuid, '-u'), - (parent_uuid, '-q'), - (sent_subvolume_uuid, '-R'), - (snapshots, '-s'), - (readonly, '-r'), - (deleted, '-d')) + params = ( + (parent_id, "-p"), + (absolute, "-a"), + (ogeneration, "-c"), + (generation, "-g"), + (subvolumes, "-o"), + (uuid, "-u"), + (parent_uuid, "-q"), + (sent_subvolume_uuid, "-R"), + (snapshots, "-s"), + (readonly, "-r"), + (deleted, "-d"), + ) cmd.extend(p[1] for p in params if p[0]) if generation_cmp: - cmd.extend(['-G', generation_cmp]) + cmd.extend(["-G", generation_cmp]) if ogeneration_cmp: - cmd.extend(['-C', ogeneration_cmp]) + cmd.extend(["-C", ogeneration_cmp]) # We already validated the content of the list if sort: - cmd.append('--sort={}'.format(','.join(sort))) + cmd.append("--sort={}".format(",".join(sort))) cmd.append(path) - res = __salt__['cmd.run_all'](cmd) + res = __salt__["cmd.run_all"](cmd) salt.utils.fsutils._verify_run(res) # Parse the output. ID and gen are always at the begining, and @@ -1015,13 +1113,23 @@ def subvolume_list(path, parent_id=False, absolute=False, # will fail. # # This list is in order. - columns = ('ID', 'gen', 'cgen', 'parent', 'top level', 'otime', - 'parent_uuid', 'received_uuid', 'uuid', 'path') + columns = ( + "ID", + "gen", + "cgen", + "parent", + "top level", + "otime", + "parent_uuid", + "received_uuid", + "uuid", + "path", + ) result = [] - for line in res['stdout'].splitlines(): + for line in res["stdout"].splitlines(): table = {} for key in columns: - value, line = _pop(line, key, key == 'path') + value, line = _pop(line, key, key == "path") if value: table[key.lower()] = value # If line is not empty here, we are not able to parse it @@ -1032,7 +1140,7 @@ def subvolume_list(path, parent_id=False, absolute=False, def subvolume_set_default(subvolid, path): - ''' + """ Set the subvolume as default subvolid @@ -1047,16 +1155,16 @@ def subvolume_set_default(subvolid, path): salt '*' btrfs.subvolume_set_default 257 /var/volumes/tmp - ''' - cmd = ['btrfs', 'subvolume', 'set-default', subvolid, path] + """ + cmd = ["btrfs", "subvolume", "set-default", subvolid, path] - res = __salt__['cmd.run_all'](cmd) + res = __salt__["cmd.run_all"](cmd) salt.utils.fsutils._verify_run(res) return True def subvolume_show(path): - ''' + """ Show information of a given subvolume path @@ -1068,28 +1176,28 @@ def subvolume_show(path): salt '*' btrfs.subvolume_show /var/volumes/tmp - ''' - cmd = ['btrfs', 'subvolume', 'show', path] + """ + cmd = ["btrfs", "subvolume", "show", path] - res = __salt__['cmd.run_all'](cmd) + res = __salt__["cmd.run_all"](cmd) salt.utils.fsutils._verify_run(res) result = {} table = {} # The real name is the first line, later there is a table of # values separated with colon. - stdout = res['stdout'].splitlines() + stdout = res["stdout"].splitlines() key = stdout.pop(0) result[key.strip()] = table for line in stdout: - key, value = line.split(':', 1) + key, value = line.split(":", 1) table[key.lower().strip()] = value.strip() return result def subvolume_snapshot(source, dest=None, name=None, read_only=False): - ''' + """ Create a snapshot of a source subvolume source @@ -1112,13 +1220,13 @@ def subvolume_snapshot(source, dest=None, name=None, read_only=False): salt '*' btrfs.subvolume_snapshot /var/volumes/tmp dest=/.snapshots salt '*' btrfs.subvolume_snapshot /var/volumes/tmp name=backup - ''' + """ if not dest and not name: - raise CommandExecutionError('Provide parameter dest, name, or both') + raise CommandExecutionError("Provide parameter dest, name, or both") - cmd = ['btrfs', 'subvolume', 'snapshot'] + cmd = ["btrfs", "subvolume", "snapshot"] if read_only: - cmd.append('-r') + cmd.append("-r") if dest and not name: cmd.append(dest) if dest and name: @@ -1126,13 +1234,13 @@ def subvolume_snapshot(source, dest=None, name=None, read_only=False): if name: cmd.append(name) - res = __salt__['cmd.run_all'](cmd) + res = __salt__["cmd.run_all"](cmd) salt.utils.fsutils._verify_run(res) return True def subvolume_sync(path, subvolids=None, sleep=None): - ''' + """ Wait until given subvolume are completely removed from the filesystem after deletion. @@ -1152,18 +1260,18 @@ def subvolume_sync(path, subvolids=None, sleep=None): salt '*' btrfs.subvolume_sync /var/volumes/tmp salt '*' btrfs.subvolume_sync /var/volumes/tmp subvolids='[257]' - ''' + """ if subvolids and type(subvolids) is not list: - raise CommandExecutionError('Subvolids parameter must be a list') + raise CommandExecutionError("Subvolids parameter must be a list") - cmd = ['btrfs', 'subvolume', 'sync'] + cmd = ["btrfs", "subvolume", "sync"] if sleep: - cmd.extend(['-s', sleep]) + cmd.extend(["-s", sleep]) cmd.append(path) if subvolids: cmd.extend(subvolids) - res = __salt__['cmd.run_all'](cmd) + res = __salt__["cmd.run_all"](cmd) salt.utils.fsutils._verify_run(res) return True diff --git a/salt/modules/cabal.py b/salt/modules/cabal.py index 4ca659e4878..cc754df8567 100644 --- a/salt/modules/cabal.py +++ b/salt/modules/cabal.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Manage and query Cabal packages =============================== .. versionadded:: 2015.8.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging @@ -16,21 +16,20 @@ from salt.exceptions import CommandExecutionError logger = logging.getLogger(__name__) # Function alias to make sure not to shadow built-in's -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} def __virtual__(): - ''' + """ Only work when cabal-install is installed. - ''' - return (salt.utils.path.which('cabal') is not None) and \ - (salt.utils.path.which('ghc-pkg') is not None) + """ + return (salt.utils.path.which("cabal") is not None) and ( + salt.utils.path.which("ghc-pkg") is not None + ) def update(user=None, env=None): - ''' + """ Updates list of known packages. user @@ -47,16 +46,12 @@ def update(user=None, env=None): salt '*' cabal.update - ''' - return __salt__['cmd.run_all']('cabal update', runas=user, env=env) + """ + return __salt__["cmd.run_all"]("cabal update", runas=user, env=env) -def install(pkg=None, - pkgs=None, - user=None, - install_global=False, - env=None): - ''' +def install(pkg=None, pkgs=None, user=None, install_global=False, env=None): + """ Install a cabal package. pkg @@ -83,32 +78,28 @@ def install(pkg=None, salt '*' cabal.install shellcheck salt '*' cabal.install shellcheck-0.3.5 - ''' + """ - cmd = ['cabal install'] + cmd = ["cabal install"] if install_global: - cmd.append('--global') + cmd.append("--global") if pkg: cmd.append('"{0}"'.format(pkg)) elif pkgs: cmd.append('"{0}"'.format('" "'.join(pkgs))) - result = __salt__['cmd.run_all'](' '.join(cmd), runas=user, env=env) + result = __salt__["cmd.run_all"](" ".join(cmd), runas=user, env=env) - if result['retcode'] != 0: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0: + raise CommandExecutionError(result["stderr"]) return result -def list_( - pkg=None, - user=None, - installed=False, - env=None): - ''' +def list_(pkg=None, user=None, installed=False, env=None): + """ List packages matching a search string. pkg @@ -128,19 +119,19 @@ def list_( salt '*' cabal.list salt '*' cabal.list ShellCheck - ''' - cmd = ['cabal list --simple-output'] + """ + cmd = ["cabal list --simple-output"] if installed: - cmd.append('--installed') + cmd.append("--installed") if pkg: cmd.append('"{0}"'.format(pkg)) - result = __salt__['cmd.run_all'](' '.join(cmd), runas=user, env=env) + result = __salt__["cmd.run_all"](" ".join(cmd), runas=user, env=env) packages = {} - for line in result['stdout'].splitlines(): + for line in result["stdout"].splitlines(): data = line.split() package_name = data[0] package_version = data[1] @@ -149,10 +140,8 @@ def list_( return packages -def uninstall(pkg, - user=None, - env=None): - ''' +def uninstall(pkg, user=None, env=None): + """ Uninstall a cabal package. pkg @@ -170,13 +159,13 @@ def uninstall(pkg, salt '*' cabal.uninstall ShellCheck - ''' - cmd = ['ghc-pkg unregister'] + """ + cmd = ["ghc-pkg unregister"] cmd.append('"{0}"'.format(pkg)) - result = __salt__['cmd.run_all'](' '.join(cmd), runas=user, env=env) + result = __salt__["cmd.run_all"](" ".join(cmd), runas=user, env=env) - if result['retcode'] != 0: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0: + raise CommandExecutionError(result["stderr"]) return result diff --git a/salt/modules/capirca_acl.py b/salt/modules/capirca_acl.py index 277ea7b141d..f5fc60db327 100644 --- a/salt/modules/capirca_acl.py +++ b/salt/modules/capirca_acl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Capirca ACL =========== @@ -20,36 +20,40 @@ The firewall configuration is generated by Capirca_. .. _Capirca: https://github.com/google/capirca To install Capirca, execute: ``pip install capirca``. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import re +import datetime import inspect import logging -import datetime +import re -log = logging.getLogger(__file__) +# Import Salt libs +import salt.utils.files # Import third party libs from salt.ext import six + +log = logging.getLogger(__file__) + + try: import capirca import capirca.aclgen import capirca.lib.policy import capirca.lib.aclgenerator + HAS_CAPIRCA = True except ImportError: HAS_CAPIRCA = False -# Import Salt libs -import salt.utils.files # ------------------------------------------------------------------------------ # module properties # ------------------------------------------------------------------------------ -__virtualname__ = 'capirca' -__proxyenabled__ = ['*'] +__virtualname__ = "capirca" +__proxyenabled__ = ["*"] # allow any proxy type # ------------------------------------------------------------------------------ @@ -58,13 +62,13 @@ __proxyenabled__ = ['*'] def __virtual__(): - ''' + """ This module requires at least Capirca to work. - ''' + """ if HAS_CAPIRCA: return __virtualname__ else: - return (False, 'The capirca module (capirca_acl) cannot be loaded.') + return (False, "The capirca module (capirca_acl) cannot be loaded.") # ------------------------------------------------------------------------------ @@ -78,67 +82,67 @@ def __virtual__(): # we can revisit this later if necessary. _TERM_FIELDS = { - 'action': [], - 'address': [], - 'address_exclude': [], - 'comment': [], - 'counter': None, - 'expiration': None, - 'destination_address': [], - 'destination_address_exclude': [], - 'destination_port': [], - 'destination_prefix': [], - 'forwarding_class': [], - 'forwarding_class_except': [], - 'logging': [], - 'log_name': None, - 'loss_priority': None, - 'option': [], - 'owner': None, - 'policer': None, - 'port': [], - 'precedence': [], - 'principals': [], - 'protocol': [], - 'protocol_except': [], - 'qos': None, - 'pan_application': [], - 'routing_instance': None, - 'source_address': [], - 'source_address_exclude': [], - 'source_port': [], - 'source_prefix': [], - 'verbatim': [], - 'packet_length': None, - 'fragment_offset': None, - 'hop_limit': None, - 'icmp_type': [], - 'icmp_code': None, - 'ether_type': [], - 'traffic_class_count': None, - 'traffic_type': [], - 'translated': False, - 'dscp_set': None, - 'dscp_match': [], - 'dscp_except': [], - 'next_ip': None, - 'flexible_match_range': [], - 'source_prefix_except': [], - 'destination_prefix_except': [], - 'vpn': None, - 'source_tag': [], - 'destination_tag': [], - 'source_interface': None, - 'destination_interface': None, - 'platform': [], - 'platform_exclude': [], - 'timeout': None, - 'flattened': False, - 'flattened_addr': None, - 'flattened_saddr': None, - 'flattened_daddr': None, - 'priority': None, - 'ttl': None + "action": [], + "address": [], + "address_exclude": [], + "comment": [], + "counter": None, + "expiration": None, + "destination_address": [], + "destination_address_exclude": [], + "destination_port": [], + "destination_prefix": [], + "forwarding_class": [], + "forwarding_class_except": [], + "logging": [], + "log_name": None, + "loss_priority": None, + "option": [], + "owner": None, + "policer": None, + "port": [], + "precedence": [], + "principals": [], + "protocol": [], + "protocol_except": [], + "qos": None, + "pan_application": [], + "routing_instance": None, + "source_address": [], + "source_address_exclude": [], + "source_port": [], + "source_prefix": [], + "verbatim": [], + "packet_length": None, + "fragment_offset": None, + "hop_limit": None, + "icmp_type": [], + "icmp_code": None, + "ether_type": [], + "traffic_class_count": None, + "traffic_type": [], + "translated": False, + "dscp_set": None, + "dscp_match": [], + "dscp_except": [], + "next_ip": None, + "flexible_match_range": [], + "source_prefix_except": [], + "destination_prefix_except": [], + "vpn": None, + "source_tag": [], + "destination_tag": [], + "source_interface": None, + "destination_interface": None, + "platform": [], + "platform_exclude": [], + "timeout": None, + "flattened": False, + "flattened_addr": None, + "flattened_saddr": None, + "flattened_daddr": None, + "priority": None, + "ttl": None, } # IP-type fields @@ -146,15 +150,15 @@ _TERM_FIELDS = { # but they need to be converted to `nacaddr.IP` # this pre-processing is done in `_clean_term_opts` _IP_FILEDS = [ - 'source_address', - 'source_address_exclude', - 'destination_address', - 'address', - 'address_exclude', - 'flattened_addr', - 'flattened_saddr', - 'flattened_daddr', - 'next_ip' + "source_address", + "source_address_exclude", + "destination_address", + "address", + "address_exclude", + "flattened_addr", + "flattened_saddr", + "flattened_daddr", + "next_ip", ] _SERVICES = {} @@ -170,51 +174,61 @@ if HAS_CAPIRCA: def _add_object(self, obj): return - setattr(_TempTerm, 'AddObject', _add_object) + setattr(_TempTerm, "AddObject", _add_object) dumy_term = _TempTerm(None) for item in dir(dumy_term): - if hasattr(item, '__func__') or item.startswith('_') or item != item.lower(): + if hasattr(item, "__func__") or item.startswith("_") or item != item.lower(): continue _TERM_FIELDS[item] = getattr(dumy_term, item) class _Policy(capirca.lib.policy.Policy): - ''' + """ Extending the Capirca Policy class to allow inserting custom filters. - ''' + """ + def __init__(self): self.filters = [] - self.filename = '' + self.filename = "" class _Term(capirca.lib.policy.Term): - ''' + """ Extending the Capirca Term class to allow setting field valued on the fly. - ''' + """ + def __init__(self): for field, default in six.iteritems(_TERM_FIELDS): setattr(self, field, default) def _import_platform_generator(platform): - ''' + """ Given a specific platform (under the Capirca conventions), return the generator class. The generator class is identified looking under the module for a class inheriting the `ACLGenerator` class. - ''' - log.debug('Using platform: {plat}'.format(plat=platform)) + """ + log.debug("Using platform: {plat}".format(plat=platform)) for mod_name, mod_obj in inspect.getmembers(capirca.aclgen): if mod_name == platform and inspect.ismodule(mod_obj): - for plat_obj_name, plat_obj in inspect.getmembers(mod_obj): # pylint: disable=unused-variable - if inspect.isclass(plat_obj) and issubclass(plat_obj, capirca.lib.aclgenerator.ACLGenerator): - log.debug('Identified Capirca class {cls} for {plat}'.format( - cls=plat_obj, - plat=platform)) + for plat_obj_name, plat_obj in inspect.getmembers( + mod_obj + ): # pylint: disable=unused-variable + if inspect.isclass(plat_obj) and issubclass( + plat_obj, capirca.lib.aclgenerator.ACLGenerator + ): + log.debug( + "Identified Capirca class {cls} for {plat}".format( + cls=plat_obj, plat=platform + ) + ) return plat_obj - log.error('Unable to identify any Capirca plaform class for {plat}'.format(plat=platform)) + log.error( + "Unable to identify any Capirca plaform class for {plat}".format(plat=platform) + ) def _get_services_mapping(): - ''' + """ Build a map of services based on the IANA assignment list: http://www.iana.org/assignments/port-numbers @@ -227,54 +241,55 @@ def _get_services_mapping(): In the worst case, the user will not be able to specify the services shortcut and they will need to specify the protocol / port combination using the source_port / destination_port & protocol fields. - ''' + """ if _SERVICES: return _SERVICES - services_txt = '' + services_txt = "" try: - with salt.utils.files.fopen('/etc/services', 'r') as srv_f: + with salt.utils.files.fopen("/etc/services", "r") as srv_f: services_txt = salt.utils.stringutils.to_unicode(srv_f.read()) except IOError as ioe: - log.error('Unable to read from /etc/services:') + log.error("Unable to read from /etc/services:") log.error(ioe) return _SERVICES # no mapping possible, sorry # will return the default mapping - service_rgx = re.compile(r'^([a-zA-Z0-9-]+)\s+(\d+)\/(tcp|udp)(.*)$') + service_rgx = re.compile(r"^([a-zA-Z0-9-]+)\s+(\d+)\/(tcp|udp)(.*)$") for line in services_txt.splitlines(): service_rgx_s = service_rgx.search(line) if service_rgx_s and len(service_rgx_s.groups()) == 4: srv_name, port, protocol, _ = service_rgx_s.groups() if srv_name not in _SERVICES: - _SERVICES[srv_name] = { - 'port': [], - 'protocol': [] - } + _SERVICES[srv_name] = {"port": [], "protocol": []} try: - _SERVICES[srv_name]['port'].append(int(port)) + _SERVICES[srv_name]["port"].append(int(port)) except ValueError as verr: log.error(verr) - log.error('Did not read that properly:') + log.error("Did not read that properly:") log.error(line) - log.error('Please report the above error: {port} does not seem a valid port value!'.format(port=port)) - _SERVICES[srv_name]['protocol'].append(protocol) + log.error( + "Please report the above error: {port} does not seem a valid port value!".format( + port=port + ) + ) + _SERVICES[srv_name]["protocol"].append(protocol) return _SERVICES def _translate_port(port): - ''' + """ Look into services and return the port value using the service name as lookup value. - ''' + """ services = _get_services_mapping() - if port in services and services[port]['port']: - return services[port]['port'][0] + if port in services and services[port]["port"]: + return services[port]["port"][0] return port def _make_it_list(dict_, field_name, value): - ''' + """ Return the object list. - ''' + """ prev_value = [] # firsly we'll collect the prev value if field_name in dict_: @@ -283,7 +298,7 @@ def _make_it_list(dict_, field_name, value): return prev_value elif isinstance(value, (tuple, list)): # other type of iterables - if field_name in ('source_port', 'destination_port'): + if field_name in ("source_port", "destination_port"): # port fields are more special # they can either be a list of integers, either a list of tuples # list of integers = a list of ports @@ -309,12 +324,10 @@ def _make_it_list(dict_, field_name, value): port_start = _translate_port(port_start) if not isinstance(port_end, int): port_end = _translate_port(port_end) - translated_portval.append( - (port_start, port_end) - ) + translated_portval.append((port_start, port_end)) return list(set(prev_value + translated_portval)) return list(set(prev_value + list(value))) - if field_name in ('source_port', 'destination_port'): + if field_name in ("source_port", "destination_port"): if not isinstance(value, int): value = _translate_port(value) return list(set(prev_value + [(value, value)])) # a list of tuples @@ -323,56 +336,62 @@ def _make_it_list(dict_, field_name, value): def _clean_term_opts(term_opts): - ''' + """ Cleanup the term opts: - strip Null and empty valuee, defaulting their value to their base definition from _TERM_FIELDS - convert to `nacaddr.IP` fields from `_IP_FILEDS` - create lists for those fields requiring it - ''' + """ clean_opts = {} _services = _get_services_mapping() for field, value in six.iteritems(term_opts): # firstly we'll process special fields like source_service or destination_services # which will inject values directly in the source or destination port and protocol - if field == 'source_service' and value: + if field == "source_service" and value: if isinstance(value, six.string_types): value = _make_it_list(clean_opts, field, value) - log.debug('Processing special source services:') + log.debug("Processing special source services:") log.debug(value) for service in value: if service and service in _services: # if valid source_service # take the port and protocol values from the global and inject in the term config - clean_opts['source_port'] = _make_it_list(clean_opts, - 'source_port', - _services[service]['port']) - clean_opts['protocol'] = _make_it_list(clean_opts, - 'protocol', - _services[service]['protocol']) - log.debug('Built source_port field, after processing special source services:') - log.debug(clean_opts.get('source_port')) - log.debug('Built protocol field, after processing special source services:') - log.debug(clean_opts.get('protocol')) - elif field == 'destination_service' and value: + clean_opts["source_port"] = _make_it_list( + clean_opts, "source_port", _services[service]["port"] + ) + clean_opts["protocol"] = _make_it_list( + clean_opts, "protocol", _services[service]["protocol"] + ) + log.debug( + "Built source_port field, after processing special source services:" + ) + log.debug(clean_opts.get("source_port")) + log.debug("Built protocol field, after processing special source services:") + log.debug(clean_opts.get("protocol")) + elif field == "destination_service" and value: if isinstance(value, six.string_types): value = _make_it_list(clean_opts, field, value) - log.debug('Processing special destination services:') + log.debug("Processing special destination services:") log.debug(value) for service in value: if service and service in _services: # if valid destination_service # take the port and protocol values from the global and inject in the term config - clean_opts['destination_port'] = _make_it_list(clean_opts, - 'destination_port', - _services[service]['port']) - clean_opts['protocol'] = _make_it_list(clean_opts, - 'protocol', - _services[service]['protocol']) - log.debug('Built source_port field, after processing special destination services:') - log.debug(clean_opts.get('destination_service')) - log.debug('Built protocol field, after processing special destination services:') - log.debug(clean_opts.get('protocol')) + clean_opts["destination_port"] = _make_it_list( + clean_opts, "destination_port", _services[service]["port"] + ) + clean_opts["protocol"] = _make_it_list( + clean_opts, "protocol", _services[service]["protocol"] + ) + log.debug( + "Built source_port field, after processing special destination services:" + ) + log.debug(clean_opts.get("destination_service")) + log.debug( + "Built protocol field, after processing special destination services:" + ) + log.debug(clean_opts.get("protocol")) # not a special field, but it has to be a valid one elif field in _TERM_FIELDS and value and value != _TERM_FIELDS[field]: # if not a special field type @@ -393,9 +412,9 @@ def _clean_term_opts(term_opts): def _lookup_element(lst, key): - ''' + """ Find an dictionary in a list of dictionaries, given its main key. - ''' + """ if not lst: return {} for ele in lst: @@ -406,22 +425,20 @@ def _lookup_element(lst, key): return {} -def _get_pillar_cfg(pillar_key, - pillarenv=None, - saltenv=None): - ''' +def _get_pillar_cfg(pillar_key, pillarenv=None, saltenv=None): + """ Retrieve the pillar data from the right environment. - ''' - pillar_cfg = __salt__['pillar.get'](pillar_key, - pillarenv=pillarenv, - saltenv=saltenv) + """ + pillar_cfg = __salt__["pillar.get"]( + pillar_key, pillarenv=pillarenv, saltenv=saltenv + ) return pillar_cfg def _cleanup(lst): - ''' + """ Return a list of non-empty dictionaries. - ''' + """ clean = [] for ele in lst: if ele and isinstance(ele, dict): @@ -430,14 +447,14 @@ def _cleanup(lst): def _merge_list_of_dict(first, second, prepend=True): - ''' + """ Merge lists of dictionaries. Each element of the list is a dictionary having one single key. That key is then used as unique lookup. The first element list has higher priority than the second. When there's an overlap between the two lists, it won't change the position, but the content. - ''' + """ first = _cleanup(first) second = _cleanup(second) if not first and not second: @@ -472,59 +489,66 @@ def _merge_list_of_dict(first, second, prepend=True): return merged -def _get_term_object(filter_name, - term_name, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=True, - **term_fields): - ''' +def _get_term_object( + filter_name, + term_name, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=True, + **term_fields +): + """ Return an instance of the ``_Term`` class given the term options. - ''' - log.debug('Generating config for term {tname} under filter {fname}'.format( - tname=term_name, - fname=filter_name - )) + """ + log.debug( + "Generating config for term {tname} under filter {fname}".format( + tname=term_name, fname=filter_name + ) + ) term = _Term() term.name = term_name term_opts = {} if merge_pillar: - term_opts = get_term_pillar(filter_name, - term_name, - pillar_key=pillar_key, - saltenv=saltenv, - pillarenv=pillarenv) - log.debug('Merging with pillar data:') + term_opts = get_term_pillar( + filter_name, + term_name, + pillar_key=pillar_key, + saltenv=saltenv, + pillarenv=pillarenv, + ) + log.debug("Merging with pillar data:") log.debug(term_opts) term_opts = _clean_term_opts(term_opts) - log.debug('Cleaning up pillar data:') + log.debug("Cleaning up pillar data:") log.debug(term_opts) - log.debug('Received processing opts:') + log.debug("Received processing opts:") log.debug(term_fields) - log.debug('Cleaning up processing opts:') + log.debug("Cleaning up processing opts:") term_fields = _clean_term_opts(term_fields) log.debug(term_fields) - log.debug('Final term opts:') + log.debug("Final term opts:") term_opts.update(term_fields) log.debug(term_fields) for field, value in six.iteritems(term_opts): # setting the field attributes to the term instance of _Term setattr(term, field, value) - log.debug('Term config:') + log.debug("Term config:") log.debug(six.text_type(term)) return term -def _get_policy_object(platform, - filters=None, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=True): - ''' +def _get_policy_object( + platform, + filters=None, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=True, +): + """ Return an instance of the ``_Policy`` class given the filters config. - ''' + """ policy = _Policy() policy_filters = [] if not filters: @@ -535,11 +559,8 @@ def _get_policy_object(platform, filter_name = filter_.keys()[0] filter_config = filter_.values()[0] header = capirca.lib.policy.Header() # same header everywhere - target_opts = [ - platform, - filter_name - ] - filter_options = filter_config.pop('options', None) + target_opts = [platform, filter_name] + filter_options = filter_config.pop("options", None) if filter_options: filter_options = _make_it_list({}, filter_name, filter_options) # make sure the filter options are sent as list @@ -547,38 +568,39 @@ def _get_policy_object(platform, target = capirca.lib.policy.Target(target_opts) header.AddObject(target) filter_terms = [] - for term_ in filter_config.get('terms', []): + for term_ in filter_config.get("terms", []): if term_ and isinstance(term_, dict): term_name = term_.keys()[0] term_fields = term_.values()[0] - term = _get_term_object(filter_name, - term_name, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar, - **term_fields) + term = _get_term_object( + filter_name, + term_name, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + **term_fields + ) filter_terms.append(term) - policy_filters.append( - (header, filter_terms) - ) + policy_filters.append((header, filter_terms)) policy.filters = policy_filters - log.debug('Policy config:') + log.debug("Policy config:") log.debug(six.text_type(policy)) platform_generator = _import_platform_generator(platform) policy_config = platform_generator(policy, 2) - log.debug('Generating policy config for {platform}:'.format( - platform=platform)) + log.debug("Generating policy config for {platform}:".format(platform=platform)) log.debug(six.text_type(policy_config)) return policy_config -def _revision_tag(text, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d'): - ''' +def _revision_tag( + text, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", +): + """ Refactor revision tag comments. Capirca generates the filter text having the following tag keys: @@ -589,46 +611,51 @@ def _revision_tag(text, This function goes through all the config lines and replaces those tags with the content requested by the user. If a certain value is not provided, the corresponding tag will be stripped. - ''' + """ timestamp = datetime.datetime.now().strftime(revision_date_format) new_text = [] for line in text.splitlines(): - if '$Id:$' in line: + if "$Id:$" in line: if not revision_id: # if no explicit revision ID required continue # jump to next line, ignore this one - line = line.replace('$Id:$', '$Id: {rev_id} $'.format(rev_id=revision_id)) - if '$Revision:$' in line: + line = line.replace("$Id:$", "$Id: {rev_id} $".format(rev_id=revision_id)) + if "$Revision:$" in line: if not revision_no: # if no explicit revision number required continue # jump to next line, ignore this one - line = line.replace('$Revision:$', '$Revision: {rev_no} $'.format(rev_no=revision_no)) - if '$Date:$' in line: + line = line.replace( + "$Revision:$", "$Revision: {rev_no} $".format(rev_no=revision_no) + ) + if "$Date:$" in line: if not revision_date: continue # jump - line = line.replace('$Date:$', '$Date: {ts} $'.format(ts=timestamp)) + line = line.replace("$Date:$", "$Date: {ts} $".format(ts=timestamp)) new_text.append(line) - return '\n'.join(new_text) + return "\n".join(new_text) + # ------------------------------------------------------------------------------ # callable functions # ------------------------------------------------------------------------------ -def get_term_config(platform, - filter_name, - term_name, - filter_options=None, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=True, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d', - source_service=None, - destination_service=None, - **term_fields): - ''' +def get_term_config( + platform, + filter_name, + term_name, + filter_options=None, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=True, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", + source_service=None, + destination_service=None, + **term_fields +): + """ Return the configuration of a single policy term. platform @@ -862,50 +889,55 @@ def get_term_config(platform, remark term-name permit ip host 1.2.3.4 host 5.6.7.8 exit - ''' + """ terms = [] - term = { - term_name: { - } - } + term = {term_name: {}} term[term_name].update(term_fields) - term[term_name].update({ - 'source_service': _make_it_list({}, 'source_service', source_service), - 'destination_service': _make_it_list({}, 'destination_service', destination_service), - }) + term[term_name].update( + { + "source_service": _make_it_list({}, "source_service", source_service), + "destination_service": _make_it_list( + {}, "destination_service", destination_service + ), + } + ) terms.append(term) if not filter_options: filter_options = [] - return get_filter_config(platform, - filter_name, - filter_options=filter_options, - terms=terms, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar, - only_lower_merge=True, - revision_id=revision_id, - revision_no=revision_no, - revision_date=revision_date, - revision_date_format=revision_date_format) + return get_filter_config( + platform, + filter_name, + filter_options=filter_options, + terms=terms, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + only_lower_merge=True, + revision_id=revision_id, + revision_no=revision_no, + revision_date=revision_date, + revision_date_format=revision_date_format, + ) -def get_filter_config(platform, - filter_name, - filter_options=None, - terms=None, - prepend=True, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=True, - only_lower_merge=False, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d'): - ''' +def get_filter_config( + platform, + filter_name, + filter_options=None, + terms=None, + prepend=True, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=True, + only_lower_merge=False, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", +): + """ Return the configuration of a policy filter. platform @@ -999,57 +1031,65 @@ def get_filter_config(platform, - [5678, 5680] protocol: tcp action: accept - ''' + """ if not filter_options: filter_options = [] if not terms: terms = [] if merge_pillar and not only_lower_merge: - acl_pillar_cfg = _get_pillar_cfg(pillar_key, - saltenv=saltenv, - pillarenv=pillarenv) + acl_pillar_cfg = _get_pillar_cfg( + pillar_key, saltenv=saltenv, pillarenv=pillarenv + ) filter_pillar_cfg = _lookup_element(acl_pillar_cfg, filter_name) - filter_options = filter_options or filter_pillar_cfg.pop('options', None) + filter_options = filter_options or filter_pillar_cfg.pop("options", None) if filter_pillar_cfg: # Only when it was able to find the filter in the ACL config - pillar_terms = filter_pillar_cfg.get('terms', []) # No problem if empty in the pillar + pillar_terms = filter_pillar_cfg.get( + "terms", [] + ) # No problem if empty in the pillar terms = _merge_list_of_dict(terms, pillar_terms, prepend=prepend) # merge the passed variable with the pillar data # any filter term not defined here, will be appended from the pillar # new terms won't be removed filters = [] - filters.append({ - filter_name: { - 'options': _make_it_list({}, filter_name, filter_options), - 'terms': terms + filters.append( + { + filter_name: { + "options": _make_it_list({}, filter_name, filter_options), + "terms": terms, + } } - }) - return get_policy_config(platform, - filters=filters, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar, - only_lower_merge=True, - revision_id=revision_id, - revision_no=revision_no, - revision_date=revision_date, - revision_date_format=revision_date_format) + ) + return get_policy_config( + platform, + filters=filters, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + only_lower_merge=True, + revision_id=revision_id, + revision_no=revision_no, + revision_date=revision_date, + revision_date_format=revision_date_format, + ) -def get_policy_config(platform, - filters=None, - prepend=True, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=True, - only_lower_merge=False, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d'): - ''' +def get_policy_config( + platform, + filters=None, + prepend=True, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=True, + only_lower_merge=False, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", +): + """ Return the configuration of the whole policy. platform @@ -1181,38 +1221,39 @@ def get_policy_config(platform, - tcp - udp action: reject - ''' + """ if not filters: filters = [] if merge_pillar and not only_lower_merge: # the pillar key for the policy config is the `pillar_key` itself - policy_pillar_cfg = _get_pillar_cfg(pillar_key, - saltenv=saltenv, - pillarenv=pillarenv) + policy_pillar_cfg = _get_pillar_cfg( + pillar_key, saltenv=saltenv, pillarenv=pillarenv + ) # now, let's merge everything witht the pillar data # again, this will not remove any extra filters/terms # but it will merge with the pillar data # if this behaviour is not wanted, the user can set `merge_pillar` as `False` filters = _merge_list_of_dict(filters, policy_pillar_cfg, prepend=prepend) - policy_object = _get_policy_object(platform, - filters=filters, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar) + policy_object = _get_policy_object( + platform, + filters=filters, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + ) policy_text = six.text_type(policy_object) - return _revision_tag(policy_text, - revision_id=revision_id, - revision_no=revision_no, - revision_date=revision_date, - revision_date_format=revision_date_format) + return _revision_tag( + policy_text, + revision_id=revision_id, + revision_no=revision_no, + revision_date=revision_date, + revision_date_format=revision_date_format, + ) -def get_filter_pillar(filter_name, - pillar_key='acl', - pillarenv=None, - saltenv=None): - ''' +def get_filter_pillar(filter_name, pillar_key="acl", pillarenv=None, saltenv=None): + """ Helper that can be used inside a state SLS, in order to get the filter configuration given its name. @@ -1229,19 +1270,15 @@ def get_filter_pillar(filter_name, saltenv Included only for compatibility with :conf_minion:`pillarenv_from_saltenv`, and is otherwise ignored. - ''' - pillar_cfg = _get_pillar_cfg(pillar_key, - pillarenv=pillarenv, - saltenv=saltenv) + """ + pillar_cfg = _get_pillar_cfg(pillar_key, pillarenv=pillarenv, saltenv=saltenv) return _lookup_element(pillar_cfg, filter_name) -def get_term_pillar(filter_name, - term_name, - pillar_key='acl', - pillarenv=None, - saltenv=None): - ''' +def get_term_pillar( + filter_name, term_name, pillar_key="acl", pillarenv=None, saltenv=None +): + """ Helper that can be used inside a state SLS, in order to get the term configuration given its name, under a certain filter uniquely identified by its name. @@ -1262,11 +1299,10 @@ def get_term_pillar(filter_name, saltenv Included only for compatibility with :conf_minion:`pillarenv_from_saltenv`, and is otherwise ignored. - ''' - filter_pillar_cfg = get_filter_pillar(filter_name, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv) - term_pillar_cfg = filter_pillar_cfg.get('terms', []) + """ + filter_pillar_cfg = get_filter_pillar( + filter_name, pillar_key=pillar_key, pillarenv=pillarenv, saltenv=saltenv + ) + term_pillar_cfg = filter_pillar_cfg.get("terms", []) term_opts = _lookup_element(term_pillar_cfg, term_name) return term_opts diff --git a/salt/modules/cassandra.py b/salt/modules/cassandra.py index d1b5be18c2b..d03f92e4061 100644 --- a/salt/modules/cassandra.py +++ b/salt/modules/cassandra.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Cassandra NoSQL Database Module :depends: - pycassa Cassandra Python adapter @@ -10,58 +10,67 @@ Cassandra NoSQL Database Module cassandra.nodetool: /usr/local/bin/nodetool cassandra.host: localhost cassandra.thrift_port: 9160 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -log = logging.getLogger(__name__) # Import salt libs import salt.utils.path from salt.ext import six +log = logging.getLogger(__name__) + + HAS_PYCASSA = False try: from pycassa.system_manager import SystemManager + HAS_PYCASSA = True except ImportError: pass def __virtual__(): - ''' + """ Only load if pycassa is available and the system is configured - ''' + """ if not HAS_PYCASSA: - return (False, 'The cassandra execution module cannot be loaded: pycassa not installed.') + return ( + False, + "The cassandra execution module cannot be loaded: pycassa not installed.", + ) - if HAS_PYCASSA and salt.utils.path.which('nodetool'): - return 'cassandra' - return (False, 'The cassandra execution module cannot be loaded: nodetool not found.') + if HAS_PYCASSA and salt.utils.path.which("nodetool"): + return "cassandra" + return ( + False, + "The cassandra execution module cannot be loaded: nodetool not found.", + ) def _nodetool(cmd): - ''' + """ Internal cassandra nodetool wrapper. Some functions are not available via pycassa so we must rely on nodetool. - ''' - nodetool = __salt__['config.option']('cassandra.nodetool') - host = __salt__['config.option']('cassandra.host') - return __salt__['cmd.run_stdout']('{0} -h {1} {2}'.format(nodetool, host, cmd)) + """ + nodetool = __salt__["config.option"]("cassandra.nodetool") + host = __salt__["config.option"]("cassandra.host") + return __salt__["cmd.run_stdout"]("{0} -h {1} {2}".format(nodetool, host, cmd)) def _sys_mgr(): - ''' + """ Return a pycassa system manager connection object - ''' - thrift_port = six.text_type(__salt__['config.option']('cassandra.THRIFT_PORT')) - host = __salt__['config.option']('cassandra.host') - return SystemManager('{0}:{1}'.format(host, thrift_port)) + """ + thrift_port = six.text_type(__salt__["config.option"]("cassandra.THRIFT_PORT")) + host = __salt__["config.option"]("cassandra.host") + return SystemManager("{0}:{1}".format(host, thrift_port)) def compactionstats(): - ''' + """ Return compactionstats info CLI Example: @@ -69,12 +78,12 @@ def compactionstats(): .. code-block:: bash salt '*' cassandra.compactionstats - ''' - return _nodetool('compactionstats') + """ + return _nodetool("compactionstats") def version(): - ''' + """ Return the cassandra version CLI Example: @@ -82,12 +91,12 @@ def version(): .. code-block:: bash salt '*' cassandra.version - ''' - return _nodetool('version') + """ + return _nodetool("version") def netstats(): - ''' + """ Return netstats info CLI Example: @@ -95,12 +104,12 @@ def netstats(): .. code-block:: bash salt '*' cassandra.netstats - ''' - return _nodetool('netstats') + """ + return _nodetool("netstats") def tpstats(): - ''' + """ Return tpstats info CLI Example: @@ -108,12 +117,12 @@ def tpstats(): .. code-block:: bash salt '*' cassandra.tpstats - ''' - return _nodetool('tpstats') + """ + return _nodetool("tpstats") def info(): - ''' + """ Return cassandra node info CLI Example: @@ -121,12 +130,12 @@ def info(): .. code-block:: bash salt '*' cassandra.info - ''' - return _nodetool('info') + """ + return _nodetool("info") def ring(): - ''' + """ Return cassandra ring info CLI Example: @@ -134,12 +143,12 @@ def ring(): .. code-block:: bash salt '*' cassandra.ring - ''' - return _nodetool('ring') + """ + return _nodetool("ring") def keyspaces(): - ''' + """ Return existing keyspaces CLI Example: @@ -147,13 +156,13 @@ def keyspaces(): .. code-block:: bash salt '*' cassandra.keyspaces - ''' + """ sys = _sys_mgr() return sys.list_keyspaces() def column_families(keyspace=None): - ''' + """ Return existing column families for all keyspaces or just the provided one. @@ -163,7 +172,7 @@ def column_families(keyspace=None): salt '*' cassandra.column_families salt '*' cassandra.column_families - ''' + """ sys = _sys_mgr() ksps = sys.list_keyspaces() @@ -181,7 +190,7 @@ def column_families(keyspace=None): def column_family_definition(keyspace, column_family): - ''' + """ Return a dictionary of column family definitions for the given keyspace/column_family @@ -191,11 +200,11 @@ def column_family_definition(keyspace, column_family): salt '*' cassandra.column_family_definition - ''' + """ sys = _sys_mgr() try: return vars(sys.get_keyspace_column_families(keyspace)[column_family]) except Exception: # pylint: disable=broad-except - log.debug('Invalid Keyspace/CF combination') + log.debug("Invalid Keyspace/CF combination") return None diff --git a/salt/modules/cassandra_cql.py b/salt/modules/cassandra_cql.py index 30db93dccce..0d389b2a78c 100644 --- a/salt/modules/cassandra_cql.py +++ b/salt/modules/cassandra_cql.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Cassandra Database Module .. versionadded:: 2015.5.0 @@ -78,38 +78,43 @@ queries based on the internal schema of said version. # defaults to 4, if not set protocol_version: 3 -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import re import ssl # Import Salt Libs import salt.utils.json +import salt.utils.versions from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range -import salt.utils.versions -SSL_VERSION = 'ssl_version' +SSL_VERSION = "ssl_version" log = logging.getLogger(__name__) -__virtualname__ = 'cassandra_cql' +__virtualname__ = "cassandra_cql" HAS_DRIVER = False try: # pylint: disable=import-error,no-name-in-module from cassandra.cluster import Cluster from cassandra.cluster import NoHostAvailable - from cassandra.connection import ConnectionException, \ - ConnectionShutdown, OperationTimedOut + from cassandra.connection import ( + ConnectionException, + ConnectionShutdown, + OperationTimedOut, + ) from cassandra.auth import PlainTextAuthProvider from cassandra.query import dict_factory + # pylint: enable=import-error,no-name-in-module HAS_DRIVER = True except ImportError: @@ -117,23 +122,23 @@ except ImportError: def __virtual__(): - ''' + """ Return virtual name of the module only if the python driver can be loaded. :return: The virtual name of the module. :rtype: str - ''' + """ if HAS_DRIVER: return __virtualname__ - return (False, 'Cannot load cassandra_cql module: python driver not found') + return (False, "Cannot load cassandra_cql module: python driver not found") def _async_log_errors(errors): - log.error('Cassandra_cql asynchronous call returned: %s', errors) + log.error("Cassandra_cql asynchronous call returned: %s", errors) def _load_properties(property_name, config_option, set_default=False, default=None): - ''' + """ Load properties for the cassandra module from config or pillar. :param property_name: The property to load. @@ -146,11 +151,13 @@ def _load_properties(property_name, config_option, set_default=False, default=No :type default: str or int :return: The property fetched from the configuration or default. :rtype: str or list of str - ''' + """ if not property_name: - log.debug("No property specified in function, trying to load from salt configuration") + log.debug( + "No property specified in function, trying to load from salt configuration" + ) try: - options = __salt__['config.option']('cassandra') + options = __salt__["config.option"]("cassandra") except BaseException as e: log.error("Failed to get cassandra config options. Reason: %s", e) raise @@ -158,49 +165,56 @@ def _load_properties(property_name, config_option, set_default=False, default=No loaded_property = options.get(config_option) if not loaded_property: if set_default: - log.debug('Setting default Cassandra %s to %s', config_option, default) + log.debug("Setting default Cassandra %s to %s", config_option, default) loaded_property = default else: - log.error('No cassandra %s specified in the configuration or passed to the module.', config_option) - raise CommandExecutionError("ERROR: Cassandra {0} cannot be empty.".format(config_option)) + log.error( + "No cassandra %s specified in the configuration or passed to the module.", + config_option, + ) + raise CommandExecutionError( + "ERROR: Cassandra {0} cannot be empty.".format(config_option) + ) return loaded_property return property_name def _get_ssl_opts(): - ''' + """ Parse out ssl_options for Cassandra cluster connection. Make sure that the ssl_version (if any specified) is valid. - ''' - sslopts = __salt__['config.option']('cassandra').get('ssl_options', None) + """ + sslopts = __salt__["config.option"]("cassandra").get("ssl_options", None) ssl_opts = {} if sslopts: - ssl_opts['ca_certs'] = sslopts['ca_certs'] + ssl_opts["ca_certs"] = sslopts["ca_certs"] if SSL_VERSION in sslopts: - if not sslopts[SSL_VERSION].startswith('PROTOCOL_'): - valid_opts = ', '.join( - [x for x in dir(ssl) if x.startswith('PROTOCOL_')] + if not sslopts[SSL_VERSION].startswith("PROTOCOL_"): + valid_opts = ", ".join( + [x for x in dir(ssl) if x.startswith("PROTOCOL_")] + ) + raise CommandExecutionError( + "Invalid protocol_version " + "specified! " + "Please make sure " + "that the ssl protocol" + "version is one from the SSL" + "module. " + "Valid options are " + "{0}".format(valid_opts) ) - raise CommandExecutionError('Invalid protocol_version ' - 'specified! ' - 'Please make sure ' - 'that the ssl protocol' - 'version is one from the SSL' - 'module. ' - 'Valid options are ' - '{0}'.format(valid_opts)) else: - ssl_opts[SSL_VERSION] = \ - getattr(ssl, sslopts[SSL_VERSION]) + ssl_opts[SSL_VERSION] = getattr(ssl, sslopts[SSL_VERSION]) return ssl_opts else: return None -def _connect(contact_points=None, port=None, cql_user=None, cql_pass=None, - protocol_version=None): - ''' +def _connect( + contact_points=None, port=None, cql_user=None, cql_pass=None, protocol_version=None +): + """ Connect to a Cassandra cluster. :param contact_points: The Cassandra cluster addresses, can either be a string or a list of IPs. @@ -215,7 +229,7 @@ def _connect(contact_points=None, port=None, cql_user=None, cql_pass=None, :type port: int :return: The session and cluster objects. :rtype: cluster object, session object - ''' + """ # Lazy load the Cassandra cluster and session for this module by creating a # cluster and session when cql_query is called the first time. Get the # Cassandra cluster and session from this module's __context__ after it is @@ -229,61 +243,98 @@ def _connect(contact_points=None, port=None, cql_user=None, cql_pass=None, # Perhaps if Master/Minion daemons could be enhanced to call an "__unload__" # function, or something similar for each loaded module, connection pools # and the like can be gracefully reclaimed/shutdown. - if (__context__ - and 'cassandra_cql_returner_cluster' in __context__ - and 'cassandra_cql_returner_session' in __context__): - return __context__['cassandra_cql_returner_cluster'], __context__['cassandra_cql_returner_session'] + if ( + __context__ + and "cassandra_cql_returner_cluster" in __context__ + and "cassandra_cql_returner_session" in __context__ + ): + return ( + __context__["cassandra_cql_returner_cluster"], + __context__["cassandra_cql_returner_session"], + ) else: - contact_points = _load_properties(property_name=contact_points, config_option='cluster') - contact_points = contact_points if isinstance(contact_points, list) else contact_points.split(',') - port = _load_properties(property_name=port, config_option='port', set_default=True, default=9042) - cql_user = _load_properties(property_name=cql_user, config_option='username', set_default=True, default="cassandra") - cql_pass = _load_properties(property_name=cql_pass, config_option='password', set_default=True, default="cassandra") - protocol_version = _load_properties(property_name=protocol_version, - config_option='protocol_version', - set_default=True, default=4) + contact_points = _load_properties( + property_name=contact_points, config_option="cluster" + ) + contact_points = ( + contact_points + if isinstance(contact_points, list) + else contact_points.split(",") + ) + port = _load_properties( + property_name=port, config_option="port", set_default=True, default=9042 + ) + cql_user = _load_properties( + property_name=cql_user, + config_option="username", + set_default=True, + default="cassandra", + ) + cql_pass = _load_properties( + property_name=cql_pass, + config_option="password", + set_default=True, + default="cassandra", + ) + protocol_version = _load_properties( + property_name=protocol_version, + config_option="protocol_version", + set_default=True, + default=4, + ) try: auth_provider = PlainTextAuthProvider(username=cql_user, password=cql_pass) ssl_opts = _get_ssl_opts() if ssl_opts: - cluster = Cluster(contact_points, - port=port, - auth_provider=auth_provider, - ssl_options=ssl_opts, - protocol_version=protocol_version, - compression=True) + cluster = Cluster( + contact_points, + port=port, + auth_provider=auth_provider, + ssl_options=ssl_opts, + protocol_version=protocol_version, + compression=True, + ) else: - cluster = Cluster(contact_points, port=port, - auth_provider=auth_provider, - protocol_version=protocol_version, - compression=True) + cluster = Cluster( + contact_points, + port=port, + auth_provider=auth_provider, + protocol_version=protocol_version, + compression=True, + ) for recontimes in range(1, 4): try: session = cluster.connect() break except OperationTimedOut: - log.warning('Cassandra cluster.connect timed out, try %s', recontimes) + log.warning( + "Cassandra cluster.connect timed out, try %s", recontimes + ) if recontimes >= 3: raise # TODO: Call cluster.shutdown() when the module is unloaded on shutdown. - __context__['cassandra_cql_returner_cluster'] = cluster - __context__['cassandra_cql_returner_session'] = session - __context__['cassandra_cql_prepared'] = {} + __context__["cassandra_cql_returner_cluster"] = cluster + __context__["cassandra_cql_returner_session"] = session + __context__["cassandra_cql_prepared"] = {} - log.debug('Successfully connected to Cassandra cluster at %s', contact_points) + log.debug( + "Successfully connected to Cassandra cluster at %s", contact_points + ) return cluster, session except TypeError: pass except (ConnectionException, ConnectionShutdown, NoHostAvailable): - log.error('Could not connect to Cassandra cluster at %s', contact_points) - raise CommandExecutionError('ERROR: Could not connect to Cassandra cluster.') + log.error("Could not connect to Cassandra cluster at %s", contact_points) + raise CommandExecutionError( + "ERROR: Could not connect to Cassandra cluster." + ) def cql_query(query, contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' + """ Run a query on a Cassandra cluster and return a dictionary. :param query: The query to execute. @@ -306,14 +357,19 @@ def cql_query(query, contact_points=None, port=None, cql_user=None, cql_pass=Non .. code-block:: bash salt 'cassandra-server' cassandra_cql.cql_query "SELECT * FROM users_by_name WHERE first_name = 'jane'" - ''' + """ try: - cluster, session = _connect(contact_points=contact_points, port=port, cql_user=cql_user, cql_pass=cql_pass) + cluster, session = _connect( + contact_points=contact_points, + port=port, + cql_user=cql_user, + cql_pass=cql_pass, + ) except CommandExecutionError: - log.critical('Could not get Cassandra cluster session.') + log.critical("Could not get Cassandra cluster session.") raise except BaseException as e: - log.critical('Unexpected error while getting Cassandra cluster session: %s', e) + log.critical("Unexpected error while getting Cassandra cluster session: %s", e) raise session.row_factory = dict_factory @@ -324,11 +380,13 @@ def cql_query(query, contact_points=None, port=None, cql_user=None, cql_pass=Non # Find the query for the current cluster version. # https://issues.apache.org/jira/browse/CASSANDRA-6717 if isinstance(query, dict): - cluster_version = version(contact_points=contact_points, - port=port, - cql_user=cql_user, - cql_pass=cql_pass) - match = re.match(r'^(\d+)\.(\d+)(?:\.(\d+))?', cluster_version) + cluster_version = version( + contact_points=contact_points, + port=port, + cql_user=cql_user, + cql_pass=cql_pass, + ) + match = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?", cluster_version) major, minor, point = match.groups() # try to find the specific version in the query dictionary # then try the major version @@ -337,12 +395,12 @@ def cql_query(query, contact_points=None, port=None, cql_user=None, cql_pass=Non query = query[cluster_version] except KeyError: query = query.get(major, max(query)) - log.debug('New query is: %s', query) + log.debug("New query is: %s", query) try: results = session.execute(query) except BaseException as e: - log.error('Failed to execute query: %s\n reason: %s', query, e) + log.error("Failed to execute query: %s\n reason: %s", query, e) msg = "ERROR: Cassandra query failed: {0} reason: {1}".format(query, e) raise CommandExecutionError(msg) @@ -362,9 +420,18 @@ def cql_query(query, contact_points=None, port=None, cql_user=None, cql_pass=Non return ret -def cql_query_with_prepare(query, statement_name, statement_arguments, callback_errors=None, contact_points=None, - port=None, cql_user=None, cql_pass=None, **kwargs): - ''' +def cql_query_with_prepare( + query, + statement_name, + statement_arguments, + callback_errors=None, + contact_points=None, + port=None, + cql_user=None, + cql_pass=None, + **kwargs +): + """ Run a query on a Cassandra cluster and return a dictionary. This function should not be used asynchronously for SELECTs -- it will not @@ -406,40 +473,46 @@ def cql_query_with_prepare(query, statement_name, statement_arguments, callback_ # Select data, should not be asynchronous because there is not currently a facility to return data from a future salt this-node cassandra_cql.cql_query_with_prepare "name_select" "SELECT * FROM USERS WHERE first_name=?" \ statement_arguments=['John'] - ''' + """ # Backward-compatibility with Python 3.7: "async" is a reserved word - asynchronous = kwargs.get('async', False) + asynchronous = kwargs.get("async", False) try: - cluster, session = _connect(contact_points=contact_points, port=port, - cql_user=cql_user, cql_pass=cql_pass) + cluster, session = _connect( + contact_points=contact_points, + port=port, + cql_user=cql_user, + cql_pass=cql_pass, + ) except CommandExecutionError: - log.critical('Could not get Cassandra cluster session.') + log.critical("Could not get Cassandra cluster session.") raise except BaseException as e: - log.critical('Unexpected error while getting Cassandra cluster session: %s', e) + log.critical("Unexpected error while getting Cassandra cluster session: %s", e) raise - if statement_name not in __context__['cassandra_cql_prepared']: + if statement_name not in __context__["cassandra_cql_prepared"]: try: bound_statement = session.prepare(query) - __context__['cassandra_cql_prepared'][statement_name] = bound_statement + __context__["cassandra_cql_prepared"][statement_name] = bound_statement except BaseException as e: - log.critical('Unexpected error while preparing SQL statement: %s', e) + log.critical("Unexpected error while preparing SQL statement: %s", e) raise else: - bound_statement = __context__['cassandra_cql_prepared'][statement_name] + bound_statement = __context__["cassandra_cql_prepared"][statement_name] session.row_factory = dict_factory ret = [] try: if asynchronous: - future_results = session.execute_async(bound_statement.bind(statement_arguments)) + future_results = session.execute_async( + bound_statement.bind(statement_arguments) + ) # future_results.add_callbacks(_async_log_errors) else: results = session.execute(bound_statement.bind(statement_arguments)) except BaseException as e: - log.error('Failed to execute query: %s\n reason: %s', query, e) + log.error("Failed to execute query: %s\n reason: %s", query, e) msg = "ERROR: Cassandra query failed: {0} reason: {1}".format(query, e) raise CommandExecutionError(msg) @@ -463,7 +536,7 @@ def cql_query_with_prepare(query, statement_name, statement_arguments, callback_ def version(contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' + """ Show the Cassandra version. :param contact_points: The Cassandra cluster addresses, can either be a string or a list of IPs. @@ -484,25 +557,25 @@ def version(contact_points=None, port=None, cql_user=None, cql_pass=None): salt 'minion1' cassandra_cql.version salt 'minion1' cassandra_cql.version contact_points=minion1 - ''' - query = '''select release_version + """ + query = """select release_version from system.local - limit 1;''' + limit 1;""" try: ret = cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not get Cassandra version.') + log.critical("Could not get Cassandra version.") raise except BaseException as e: - log.critical('Unexpected error while getting Cassandra version: %s', e) + log.critical("Unexpected error while getting Cassandra version: %s", e) raise - return ret[0].get('release_version') + return ret[0].get("release_version") def info(contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' + """ Show the Cassandra information for this cluster. :param contact_points: The Cassandra cluster addresses, can either be a string or a list of IPs. @@ -523,9 +596,9 @@ def info(contact_points=None, port=None, cql_user=None, cql_pass=None): salt 'minion1' cassandra_cql.info salt 'minion1' cassandra_cql.info contact_points=minion1 - ''' + """ - query = '''select cluster_name, + query = """select cluster_name, data_center, partitioner, host_id, @@ -535,24 +608,24 @@ def info(contact_points=None, port=None, cql_user=None, cql_pass=None): schema_version, thrift_version from system.local - limit 1;''' + limit 1;""" ret = {} try: ret = cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not list Cassandra info.') + log.critical("Could not list Cassandra info.") raise except BaseException as e: - log.critical('Unexpected error while listing Cassandra info: %s', e) + log.critical("Unexpected error while listing Cassandra info: %s", e) raise return ret def list_keyspaces(contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' + """ List keyspaces in a Cassandra cluster. :param contact_points: The Cassandra cluster addresses, can either be a string or a list of IPs. @@ -573,10 +646,10 @@ def list_keyspaces(contact_points=None, port=None, cql_user=None, cql_pass=None) salt 'minion1' cassandra_cql.list_keyspaces salt 'minion1' cassandra_cql.list_keyspaces contact_points=minion1 port=9000 - ''' + """ query = { - '2': 'select keyspace_name from system.schema_keyspaces;', - '3': 'select keyspace_name from system_schema.keyspaces;', + "2": "select keyspace_name from system.schema_keyspaces;", + "3": "select keyspace_name from system_schema.keyspaces;", } ret = {} @@ -584,17 +657,19 @@ def list_keyspaces(contact_points=None, port=None, cql_user=None, cql_pass=None) try: ret = cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not list keyspaces.') + log.critical("Could not list keyspaces.") raise except BaseException as e: - log.critical('Unexpected error while listing keyspaces: %s', e) + log.critical("Unexpected error while listing keyspaces: %s", e) raise return ret -def list_column_families(keyspace=None, contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' +def list_column_families( + keyspace=None, contact_points=None, port=None, cql_user=None, cql_pass=None +): + """ List column families in a Cassandra cluster for all keyspaces or just the provided one. :param keyspace: The keyspace to provide the column families for, optional. @@ -619,14 +694,18 @@ def list_column_families(keyspace=None, contact_points=None, port=None, cql_user salt 'minion1' cassandra_cql.list_column_families contact_points=minion1 salt 'minion1' cassandra_cql.list_column_families keyspace=system - ''' + """ where_clause = "where keyspace_name = '{0}'".format(keyspace) if keyspace else "" query = { - '2': '''select columnfamily_name from system.schema_columnfamilies - {0};'''.format(where_clause), - '3': '''select column_name from system_schema.columns - {0};'''.format(where_clause), + "2": """select columnfamily_name from system.schema_columnfamilies + {0};""".format( + where_clause + ), + "3": """select column_name from system_schema.columns + {0};""".format( + where_clause + ), } ret = {} @@ -634,17 +713,19 @@ def list_column_families(keyspace=None, contact_points=None, port=None, cql_user try: ret = cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not list column families.') + log.critical("Could not list column families.") raise except BaseException as e: - log.critical('Unexpected error while listing column families: %s', e) + log.critical("Unexpected error while listing column families: %s", e) raise return ret -def keyspace_exists(keyspace, contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' +def keyspace_exists( + keyspace, contact_points=None, port=None, cql_user=None, cql_pass=None +): + """ Check if a keyspace exists in a Cassandra cluster. :param keyspace The keyspace name to check for. @@ -665,29 +746,41 @@ def keyspace_exists(keyspace, contact_points=None, port=None, cql_user=None, cql .. code-block:: bash salt 'minion1' cassandra_cql.keyspace_exists keyspace=system - ''' + """ query = { - '2': '''select keyspace_name from system.schema_keyspaces - where keyspace_name = '{0}';'''.format(keyspace), - '3': '''select keyspace_name from system_schema.keyspaces - where keyspace_name = '{0}';'''.format(keyspace), + "2": """select keyspace_name from system.schema_keyspaces + where keyspace_name = '{0}';""".format( + keyspace + ), + "3": """select keyspace_name from system_schema.keyspaces + where keyspace_name = '{0}';""".format( + keyspace + ), } try: ret = cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not determine if keyspace exists.') + log.critical("Could not determine if keyspace exists.") raise except BaseException as e: - log.critical('Unexpected error while determining if keyspace exists: %s', e) + log.critical("Unexpected error while determining if keyspace exists: %s", e) raise return True if ret else False -def create_keyspace(keyspace, replication_strategy='SimpleStrategy', replication_factor=1, replication_datacenters=None, - contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' +def create_keyspace( + keyspace, + replication_strategy="SimpleStrategy", + replication_factor=1, + replication_datacenters=None, + contact_points=None, + port=None, + cql_user=None, + cql_pass=None, +): + """ Create a new keyspace in Cassandra. :param keyspace: The keyspace name @@ -719,18 +812,18 @@ def create_keyspace(keyspace, replication_strategy='SimpleStrategy', replication salt 'minion1' cassandra_cql.create_keyspace keyspace=newkeyspace replication_strategy=NetworkTopologyStrategy \ replication_datacenters='{"datacenter_1": 3, "datacenter_2": 2}' - ''' + """ existing_keyspace = keyspace_exists(keyspace, contact_points, port) if not existing_keyspace: # Add the strategy, replication_factor, etc. - replication_map = { - 'class': replication_strategy - } + replication_map = {"class": replication_strategy} if replication_datacenters: if isinstance(replication_datacenters, six.string_types): try: - replication_datacenter_map = salt.utils.json.loads(replication_datacenters) + replication_datacenter_map = salt.utils.json.loads( + replication_datacenters + ) replication_map.update(**replication_datacenter_map) except BaseException: # pylint: disable=W0703 log.error("Could not load json replication_datacenters.") @@ -738,24 +831,28 @@ def create_keyspace(keyspace, replication_strategy='SimpleStrategy', replication else: replication_map.update(**replication_datacenters) else: - replication_map['replication_factor'] = replication_factor + replication_map["replication_factor"] = replication_factor - query = '''create keyspace {0} + query = """create keyspace {0} with replication = {1} - and durable_writes = true;'''.format(keyspace, replication_map) + and durable_writes = true;""".format( + keyspace, replication_map + ) try: cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not create keyspace.') + log.critical("Could not create keyspace.") raise except BaseException as e: - log.critical('Unexpected error while creating keyspace: %s', e) + log.critical("Unexpected error while creating keyspace: %s", e) raise -def drop_keyspace(keyspace, contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' +def drop_keyspace( + keyspace, contact_points=None, port=None, cql_user=None, cql_pass=None +): + """ Drop a keyspace if it exists in a Cassandra cluster. :param keyspace: The keyspace to drop. @@ -778,24 +875,24 @@ def drop_keyspace(keyspace, contact_points=None, port=None, cql_user=None, cql_p salt 'minion1' cassandra_cql.drop_keyspace keyspace=test salt 'minion1' cassandra_cql.drop_keyspace keyspace=test contact_points=minion1 - ''' + """ existing_keyspace = keyspace_exists(keyspace, contact_points, port) if existing_keyspace: - query = '''drop keyspace {0};'''.format(keyspace) + query = """drop keyspace {0};""".format(keyspace) try: cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not drop keyspace.') + log.critical("Could not drop keyspace.") raise except BaseException as e: - log.critical('Unexpected error while dropping keyspace: %s', e) + log.critical("Unexpected error while dropping keyspace: %s", e) raise return True def list_users(contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' + """ List existing users in this Cassandra cluster. :param contact_points: The Cassandra cluster addresses, can either be a string or a list of IPs. @@ -816,7 +913,7 @@ def list_users(contact_points=None, port=None, cql_user=None, cql_pass=None): salt 'minion1' cassandra_cql.list_users salt 'minion1' cassandra_cql.list_users contact_points=minion1 - ''' + """ query = "list users;" ret = {} @@ -824,17 +921,25 @@ def list_users(contact_points=None, port=None, cql_user=None, cql_pass=None): try: ret = cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not list users.') + log.critical("Could not list users.") raise except BaseException as e: - log.critical('Unexpected error while listing users: %s', e) + log.critical("Unexpected error while listing users: %s", e) raise return ret -def create_user(username, password, superuser=False, contact_points=None, port=None, cql_user=None, cql_pass=None): - ''' +def create_user( + username, + password, + superuser=False, + contact_points=None, + port=None, + cql_user=None, + cql_pass=None, +): + """ Create a new cassandra user with credentials and superuser status. :param username: The name of the new user. @@ -863,28 +968,42 @@ def create_user(username, password, superuser=False, contact_points=None, port=N salt 'minion1' cassandra_cql.create_user username=joe password=secret superuser=True salt 'minion1' cassandra_cql.create_user username=joe password=secret superuser=True contact_points=minion1 - ''' - superuser_cql = 'superuser' if superuser else 'nosuperuser' - query = '''create user if not exists {0} with password '{1}' {2};'''.format(username, password, superuser_cql) - log.debug("Attempting to create a new user with username=%s superuser=%s", username, superuser_cql) + """ + superuser_cql = "superuser" if superuser else "nosuperuser" + query = """create user if not exists {0} with password '{1}' {2};""".format( + username, password, superuser_cql + ) + log.debug( + "Attempting to create a new user with username=%s superuser=%s", + username, + superuser_cql, + ) # The create user query doesn't actually return anything if the query succeeds. # If the query fails, catch the exception, log a messange and raise it again. try: cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not create user.') + log.critical("Could not create user.") raise except BaseException as e: - log.critical('Unexpected error while creating user: %s', e) + log.critical("Unexpected error while creating user: %s", e) raise return True -def list_permissions(username=None, resource=None, resource_type='keyspace', permission=None, contact_points=None, - port=None, cql_user=None, cql_pass=None): - ''' +def list_permissions( + username=None, + resource=None, + resource_type="keyspace", + permission=None, + contact_points=None, + port=None, + cql_user=None, + cql_pass=None, +): + """ List permissions. :param username: The name of the user to list permissions for. @@ -916,9 +1035,13 @@ def list_permissions(username=None, resource=None, resource_type='keyspace', per salt 'minion1' cassandra_cql.list_permissions username=joe resource=test_table resource_type=table \ permission=select contact_points=minion1 - ''' - keyspace_cql = "{0} {1}".format(resource_type, resource) if resource else "all keyspaces" - permission_cql = "{0} permission".format(permission) if permission else "all permissions" + """ + keyspace_cql = ( + "{0} {1}".format(resource_type, resource) if resource else "all keyspaces" + ) + permission_cql = ( + "{0} permission".format(permission) if permission else "all permissions" + ) query = "list {0} on {1}".format(permission_cql, keyspace_cql) if username: @@ -931,18 +1054,26 @@ def list_permissions(username=None, resource=None, resource_type='keyspace', per try: ret = cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not list permissions.') + log.critical("Could not list permissions.") raise except BaseException as e: - log.critical('Unexpected error while listing permissions: %s', e) + log.critical("Unexpected error while listing permissions: %s", e) raise return ret -def grant_permission(username, resource=None, resource_type='keyspace', permission=None, contact_points=None, port=None, - cql_user=None, cql_pass=None): - ''' +def grant_permission( + username, + resource=None, + resource_type="keyspace", + permission=None, + contact_points=None, + port=None, + cql_user=None, + cql_pass=None, +): + """ Grant permissions to a user. :param username: The name of the user to grant permissions to. @@ -974,19 +1105,23 @@ def grant_permission(username, resource=None, resource_type='keyspace', permissi salt 'minion1' cassandra_cql.grant_permission username=joe resource=test_table resource_type=table \ permission=select contact_points=minion1 - ''' - permission_cql = "grant {0}".format(permission) if permission else "grant all permissions" - resource_cql = "on {0} {1}".format(resource_type, resource) if resource else "on all keyspaces" + """ + permission_cql = ( + "grant {0}".format(permission) if permission else "grant all permissions" + ) + resource_cql = ( + "on {0} {1}".format(resource_type, resource) if resource else "on all keyspaces" + ) query = "{0} {1} to {2}".format(permission_cql, resource_cql, username) log.debug("Attempting to grant permissions with query '%s'", query) try: cql_query(query, contact_points, port, cql_user, cql_pass) except CommandExecutionError: - log.critical('Could not grant permissions.') + log.critical("Could not grant permissions.") raise except BaseException as e: - log.critical('Unexpected error while granting permissions: %s', e) + log.critical("Unexpected error while granting permissions: %s", e) raise return True diff --git a/salt/modules/celery.py b/salt/modules/celery.py index 9d917d96b23..8dc787c4346 100644 --- a/salt/modules/celery.py +++ b/salt/modules/celery.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for scheduling celery tasks. The worker is independent of salt and thus can run in a different virtualenv or on a different python version, as long as broker, backend and serializer configurations match. Also note that celery and packages required by the celery broker, e.g. redis must be installed to load @@ -7,7 +7,7 @@ the salt celery execution module. .. note:: A new app (and thus new connections) is created for each task execution -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -25,23 +25,36 @@ log = logging.getLogger(__name__) try: from celery import Celery from celery.exceptions import TimeoutError # pylint: disable=no-name-in-module + HAS_CELERY = True except ImportError: HAS_CELERY = False def __virtual__(): - ''' + """ Only load if celery libraries exist. - ''' + """ if not HAS_CELERY: - return False, 'The celery module could not be loaded: celery library not found' + return False, "The celery module could not be loaded: celery library not found" return True -def run_task(task_name, args=None, kwargs=None, broker=None, backend=None, wait_for_result=False, timeout=None, - propagate=True, interval=0.5, no_ack=True, raise_timeout=True, config=None): - ''' +def run_task( + task_name, + args=None, + kwargs=None, + broker=None, + backend=None, + wait_for_result=False, + timeout=None, + propagate=True, + interval=0.5, + no_ack=True, + raise_timeout=True, + config=None, +): + """ Execute celery tasks. For celery specific parameters see celery documentation. @@ -88,9 +101,9 @@ def run_task(task_name, args=None, kwargs=None, broker=None, backend=None, wait_ config Config dict for celery app, See celery documentation - ''' + """ if not broker: - raise SaltInvocationError('broker parameter is required') + raise SaltInvocationError("broker parameter is required") with Celery(broker=broker, backend=backend, set_as_current=False) as app: if config: @@ -103,10 +116,16 @@ def run_task(task_name, args=None, kwargs=None, broker=None, backend=None, wait_ if wait_for_result: try: - return async_result.get(timeout=timeout, propagate=propagate, - interval=interval, no_ack=no_ack) + return async_result.get( + timeout=timeout, + propagate=propagate, + interval=interval, + no_ack=no_ack, + ) except TimeoutError as ex: - log.error('Waiting for the result of a celery task execution timed out.') + log.error( + "Waiting for the result of a celery task execution timed out." + ) if raise_timeout: six.reraise(*sys.exc_info()) return False diff --git a/salt/modules/ceph.py b/salt/modules/ceph.py index c8c1b607837..f3df17cdc1b 100644 --- a/salt/modules/ceph.py +++ b/salt/modules/ceph.py @@ -1,22 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide ceph control with salt. :depends: - ceph_cfg Python module .. versionadded:: 2016.11.0 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import logging +import logging log = logging.getLogger(__name__) -__virtualname__ = 'ceph' +__virtualname__ = "ceph" try: import ceph_cfg + HAS_CEPH_CFG = True except ImportError: HAS_CEPH_CFG = False @@ -24,13 +25,15 @@ except ImportError: def __virtual__(): if HAS_CEPH_CFG is False: - msg = 'ceph_cfg unavailable: {0} execution module cant be loaded '.format(__virtualname__) + msg = "ceph_cfg unavailable: {0} execution module cant be loaded ".format( + __virtualname__ + ) return False, msg return __virtualname__ def partition_list(): - ''' + """ List partitions by disk CLI Example: @@ -38,12 +41,12 @@ def partition_list(): .. code-block:: bash salt '*' ceph.partition_list - ''' + """ return ceph_cfg.partition_list() def partition_list_osd(): - ''' + """ List all OSD data partitions by partition CLI Example: @@ -51,12 +54,12 @@ def partition_list_osd(): .. code-block:: bash salt '*' ceph.partition_list_osd - ''' + """ return ceph_cfg.partition_list_osd() def partition_list_journal(): - ''' + """ List all OSD journal partitions by partition CLI Example: @@ -64,12 +67,12 @@ def partition_list_journal(): .. code-block:: bash salt '*' ceph.partition_list_journal - ''' + """ return ceph_cfg.partition_list_journal() def osd_discover(): - ''' + """ List all OSD by cluster CLI Example: @@ -78,12 +81,12 @@ def osd_discover(): salt '*' ceph.osd_discover - ''' + """ return ceph_cfg.osd_discover() def partition_is(dev): - ''' + """ Check whether a given device path is a partition or a full disk. CLI Example: @@ -91,12 +94,12 @@ def partition_is(dev): .. code-block:: bash salt '*' ceph.partition_is /dev/sdc1 - ''' + """ return ceph_cfg.partition_is(dev) def zap(target=None, **kwargs): - ''' + """ Destroy the partition table and content of a given disk. .. code-block:: bash @@ -113,7 +116,7 @@ def zap(target=None, **kwargs): cluster_uuid The cluster UUID. Defaults to value found in ceph config file. - ''' + """ if target is not None: log.warning("Depricated use of function, use kwargs") target = kwargs.get("dev", target) @@ -122,7 +125,7 @@ def zap(target=None, **kwargs): def osd_prepare(**kwargs): - ''' + """ Prepare an OSD CLI Example: @@ -157,12 +160,12 @@ def osd_prepare(**kwargs): journal_uuid set the OSD journal UUID. If set will return if OSD with journal UUID already exists. - ''' + """ return ceph_cfg.osd_prepare(**kwargs) def osd_activate(**kwargs): - ''' + """ Activate an OSD CLI Example: @@ -170,12 +173,12 @@ def osd_activate(**kwargs): .. code-block:: bash salt '*' ceph.osd_activate 'osd_dev'='/dev/vdc' - ''' + """ return ceph_cfg.osd_activate(**kwargs) def keyring_create(**kwargs): - ''' + """ Create keyring for cluster CLI Example: @@ -195,12 +198,12 @@ def keyring_create(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.keyring_create(**kwargs) def keyring_save(**kwargs): - ''' + """ Create save keyring locally CLI Example: @@ -220,12 +223,12 @@ def keyring_save(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.keyring_save(**kwargs) def keyring_purge(**kwargs): - ''' + """ Delete keyring for cluster CLI Example: @@ -247,12 +250,12 @@ def keyring_purge(**kwargs): The cluster name. Defaults to ``ceph``. If no ceph config file is found, this command will fail. - ''' + """ return ceph_cfg.keyring_purge(**kwargs) def keyring_present(**kwargs): - ''' + """ Returns ``True`` if the keyring is present on disk, otherwise ``False`` CLI Example: @@ -272,12 +275,12 @@ def keyring_present(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.keyring_present(**kwargs) def keyring_auth_add(**kwargs): - ''' + """ Add keyring to authorized list CLI Example: @@ -297,12 +300,12 @@ def keyring_auth_add(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.keyring_auth_add(**kwargs) def keyring_auth_del(**kwargs): - ''' + """ Remove keyring from authorised list CLI Example: @@ -322,12 +325,12 @@ def keyring_auth_del(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.keyring_auth_del(**kwargs) def mon_is(**kwargs): - ''' + """ Returns ``True`` if the target is a mon node, otherwise ``False`` CLI Example: @@ -343,12 +346,12 @@ def mon_is(**kwargs): cluster_uuid The cluster UUID. Defaults to value found in ceph config file. - ''' + """ return ceph_cfg.mon_is(**kwargs) def mon_status(**kwargs): - ''' + """ Get status from mon daemon CLI Example: @@ -364,12 +367,12 @@ def mon_status(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.status(**kwargs) def mon_quorum(**kwargs): - ''' + """ Returns ``True`` if the mon daemon is in the quorum, otherwise ``False`` CLI Example: @@ -385,12 +388,12 @@ def mon_quorum(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.mon_quorum(**kwargs) def mon_active(**kwargs): - ''' + """ Returns ``True`` if the mon daemon is running, otherwise ``False`` CLI Example: @@ -406,12 +409,12 @@ def mon_active(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.mon_active(**kwargs) def mon_create(**kwargs): - ''' + """ Create a mon node CLI Example: @@ -427,12 +430,12 @@ def mon_create(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.mon_create(**kwargs) def rgw_pools_create(**kwargs): - ''' + """ Create pools for rgw CLI Example: @@ -446,12 +449,12 @@ def rgw_pools_create(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.rgw_pools_create(**kwargs) def rgw_pools_missing(**kwargs): - ''' + """ Show pools missing for rgw CLI Example: @@ -465,12 +468,12 @@ def rgw_pools_missing(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.rgw_pools_missing(**kwargs) def rgw_create(**kwargs): - ''' + """ Create a rgw CLI Example: @@ -490,12 +493,12 @@ def rgw_create(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.rgw_create(**kwargs) def rgw_destroy(**kwargs): - ''' + """ Remove a rgw CLI Example: @@ -515,12 +518,12 @@ def rgw_destroy(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.rgw_destroy(**kwargs) def mds_create(**kwargs): - ''' + """ Create a mds CLI Example: @@ -548,12 +551,12 @@ def mds_create(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.mds_create(**kwargs) def mds_destroy(**kwargs): - ''' + """ Remove a mds CLI Example: @@ -573,12 +576,12 @@ def mds_destroy(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.mds_destroy(**kwargs) def keyring_auth_list(**kwargs): - ''' + """ List all cephx authorization keys CLI Example: @@ -594,12 +597,12 @@ def keyring_auth_list(**kwargs): cluster_uuid The cluster UUID. Defaults to value found in ceph config file. - ''' + """ return ceph_cfg.keyring_auth_list(**kwargs) def pool_list(**kwargs): - ''' + """ List all pools CLI Example: @@ -615,12 +618,12 @@ def pool_list(**kwargs): cluster_uuid The cluster UUID. Defaults to value found in ceph config file. - ''' + """ return ceph_cfg.pool_list(**kwargs) def pool_add(pool_name, **kwargs): - ''' + """ Create a pool CLI Example: @@ -651,12 +654,12 @@ def pool_add(pool_name, **kwargs): crush_ruleset The crush map rule set - ''' + """ return ceph_cfg.pool_add(pool_name, **kwargs) def pool_del(pool_name, **kwargs): - ''' + """ Delete a pool CLI Example: @@ -672,12 +675,12 @@ def pool_del(pool_name, **kwargs): cluster_uuid The cluster UUID. Defaults to value found in ceph config file. - ''' + """ return ceph_cfg.pool_del(pool_name, **kwargs) def purge(**kwargs): - ''' + """ purge ceph configuration on the node CLI Example: @@ -693,12 +696,12 @@ def purge(**kwargs): cluster_uuid The cluster UUID. Defaults to value found in ceph config file. - ''' + """ return ceph_cfg.purge(**kwargs) def ceph_version(): - ''' + """ Get the version of ceph installed CLI Example: @@ -706,12 +709,12 @@ def ceph_version(): .. code-block:: bash salt '*' ceph.ceph_version - ''' + """ return ceph_cfg.ceph_version() def cluster_quorum(**kwargs): - ''' + """ Get the cluster's quorum status CLI Example: @@ -727,12 +730,12 @@ def cluster_quorum(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.cluster_quorum(**kwargs) def cluster_status(**kwargs): - ''' + """ Get the cluster status, including health if in quorum CLI Example: @@ -748,5 +751,5 @@ def cluster_status(**kwargs): cluster_name The cluster name. Defaults to ``ceph``. - ''' + """ return ceph_cfg.cluster_status(**kwargs) diff --git a/salt/modules/chassis.py b/salt/modules/chassis.py index d2169d807c3..bd92bc5e41c 100644 --- a/salt/modules/chassis.py +++ b/salt/modules/chassis.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Glue execution module to link to the :mod:`fx2 proxymodule `. Depends: :mod:`iDRAC Remote execution module (salt.modules.dracr) ` @@ -12,41 +12,44 @@ called ``chconfig``. That function looks up the function passed in the ``cmd`` parameter in :mod:`salt.modules.dracr ` and calls it. .. versionadded:: 2015.8.2 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -import salt.utils.platform +import salt.utils.platform log = logging.getLogger(__name__) -__proxyenabled__ = ['fx2'] -__virtualname__ = 'chassis' +__proxyenabled__ = ["fx2"] +__virtualname__ = "chassis" def __virtual__(): - ''' + """ Only work on proxy - ''' + """ if salt.utils.platform.is_proxy(): return __virtualname__ - return (False, 'The chassis execution module cannot be loaded: ' - 'this only works in proxy minions.') + return ( + False, + "The chassis execution module cannot be loaded: " + "this only works in proxy minions.", + ) def chassis_credentials(): - proxyprefix = __opts__['proxy']['proxytype'] - (username, password) = __proxy__[proxyprefix+'.find_credentials']() + proxyprefix = __opts__["proxy"]["proxytype"] + (username, password) = __proxy__[proxyprefix + ".find_credentials"]() return (username, password) def cmd(cmd, *args, **kwargs): - proxyprefix = __opts__['proxy']['proxytype'] + proxyprefix = __opts__["proxy"]["proxytype"] (username, password) = chassis_credentials() - kwargs['admin_username'] = username - kwargs['admin_password'] = password - kwargs['host'] = __proxy__[proxyprefix+'.host']() - proxycmd = __opts__['proxy']['proxytype'] + '.chconfig' + kwargs["admin_username"] = username + kwargs["admin_password"] = password + kwargs["host"] = __proxy__[proxyprefix + ".host"]() + proxycmd = __opts__["proxy"]["proxytype"] + ".chconfig" return __proxy__[proxycmd](cmd, *args, **kwargs) diff --git a/salt/modules/chef.py b/salt/modules/chef.py index 786508b0d4a..028dcdb3d0c 100644 --- a/salt/modules/chef.py +++ b/salt/modules/chef.py @@ -1,18 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" Execute chef in server or solo mode -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import tempfile +import salt.utils.decorators.path + # Import Salt libs import salt.utils.path import salt.utils.platform -import salt.utils.decorators.path # Import 3rd-party libs from salt.ext import six @@ -21,43 +23,36 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if chef is installed - ''' - if not salt.utils.path.which('chef-client'): - return (False, 'Cannot load chef module: chef-client not found') + """ + if not salt.utils.path.which("chef-client"): + return (False, "Cannot load chef module: chef-client not found") return True def _default_logfile(exe_name): - ''' + """ Retrieve the logfile name - ''' + """ if salt.utils.platform.is_windows(): - tmp_dir = os.path.join(__opts__['cachedir'], 'tmp') + tmp_dir = os.path.join(__opts__["cachedir"], "tmp") if not os.path.isdir(tmp_dir): os.mkdir(tmp_dir) - logfile_tmp = tempfile.NamedTemporaryFile(dir=tmp_dir, - prefix=exe_name, - suffix='.log', - delete=False) + logfile_tmp = tempfile.NamedTemporaryFile( + dir=tmp_dir, prefix=exe_name, suffix=".log", delete=False + ) logfile = logfile_tmp.name logfile_tmp.close() else: - logfile = salt.utils.path.join( - '/var/log', - '{0}.log'.format(exe_name) - ) + logfile = salt.utils.path.join("/var/log", "{0}.log".format(exe_name)) return logfile -@salt.utils.decorators.path.which('chef-client') -def client(whyrun=False, - localmode=False, - logfile=None, - **kwargs): - ''' +@salt.utils.decorators.path.which("chef-client") +def client(whyrun=False, localmode=False, logfile=None, **kwargs): + """ Execute a chef client run and return a dict with the stderr, stdout, return code, and pid. @@ -123,29 +118,29 @@ def client(whyrun=False, whyrun Enable whyrun mode when set to True - ''' + """ if logfile is None: - logfile = _default_logfile('chef-client') - args = ['chef-client', - '--no-color', - '--once', - '--logfile "{0}"'.format(logfile), - '--format doc'] + logfile = _default_logfile("chef-client") + args = [ + "chef-client", + "--no-color", + "--once", + '--logfile "{0}"'.format(logfile), + "--format doc", + ] if whyrun: - args.append('--why-run') + args.append("--why-run") if localmode: - args.append('--local-mode') + args.append("--local-mode") return _exec_cmd(*args, **kwargs) -@salt.utils.decorators.path.which('chef-solo') -def solo(whyrun=False, - logfile=None, - **kwargs): - ''' +@salt.utils.decorators.path.which("chef-solo") +def solo(whyrun=False, logfile=None, **kwargs): + """ Execute a chef solo run and return a dict with the stderr, stdout, return code, and pid. @@ -192,16 +187,18 @@ def solo(whyrun=False, whyrun Enable whyrun mode when set to True - ''' + """ if logfile is None: - logfile = _default_logfile('chef-solo') - args = ['chef-solo', - '--no-color', - '--logfile "{0}"'.format(logfile), - '--format doc'] + logfile = _default_logfile("chef-solo") + args = [ + "chef-solo", + "--no-color", + '--logfile "{0}"'.format(logfile), + "--format doc", + ] if whyrun: - args.append('--why-run') + args.append("--why-run") return _exec_cmd(*args, **kwargs) @@ -209,12 +206,15 @@ def solo(whyrun=False, def _exec_cmd(*args, **kwargs): # Compile the command arguments - cmd_args = ' '.join(args) - cmd_kwargs = ''.join([ - ' --{0} {1}'.format(k, v) - for k, v in six.iteritems(kwargs) if not k.startswith('__') - ]) - cmd_exec = '{0}{1}'.format(cmd_args, cmd_kwargs) - log.debug('Chef command: {0}'.format(cmd_exec)) + cmd_args = " ".join(args) + cmd_kwargs = "".join( + [ + " --{0} {1}".format(k, v) + for k, v in six.iteritems(kwargs) + if not k.startswith("__") + ] + ) + cmd_exec = "{0}{1}".format(cmd_args, cmd_kwargs) + log.debug("Chef command: {0}".format(cmd_exec)) - return __salt__['cmd.run_all'](cmd_exec, python_shell=False) + return __salt__["cmd.run_all"](cmd_exec, python_shell=False) diff --git a/salt/modules/chocolatey.py b/salt/modules/chocolatey.py index 1b2c17674db..04fcf1120d8 100644 --- a/salt/modules/chocolatey.py +++ b/salt/modules/chocolatey.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" A dead simple module wrapping calls to the Chocolatey package manager (http://chocolatey.org) .. versionadded:: 2014.1.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -12,115 +12,124 @@ import logging import os import re import tempfile -from requests.structures import CaseInsensitiveDict # Import salt libs import salt.utils.data import salt.utils.platform +from requests.structures import CaseInsensitiveDict +from salt.exceptions import ( + CommandExecutionError, + CommandNotFoundError, + SaltInvocationError, +) from salt.utils.versions import LooseVersion as _LooseVersion -from salt.exceptions import CommandExecutionError, CommandNotFoundError, \ - SaltInvocationError - log = logging.getLogger(__name__) -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} def __virtual__(): - ''' + """ Confirm this module is on a Windows system running Vista or later. While it is possible to make Chocolatey run under XP and Server 2003 with an awful lot of hassle (e.g. SSL is completely broken), the PowerShell shim for simulating UAC forces a GUI prompt, and is not compatible with salt-minion running as SYSTEM. - ''' + """ if not salt.utils.platform.is_windows(): - return (False, 'Cannot load module chocolatey: Chocolatey requires ' - 'Windows') + return (False, "Cannot load module chocolatey: Chocolatey requires Windows") - if __grains__['osrelease'] in ('XP', '2003Server'): - return (False, 'Cannot load module chocolatey: Chocolatey requires ' - 'Windows Vista or later') + if __grains__["osrelease"] in ("XP", "2003Server"): + return ( + False, + "Cannot load module chocolatey: Chocolatey requires " + "Windows Vista or later", + ) - return 'chocolatey' + return "chocolatey" def _clear_context(): - ''' + """ Clear variables stored in __context__. Run this function when a new version of chocolatey is installed. - ''' - choco_items = [x for x in __context__ if x.startswith('chocolatey.')] + """ + choco_items = [x for x in __context__ if x.startswith("chocolatey.")] for var in choco_items: __context__.pop(var) def _yes(): - ''' + """ Returns ['--yes'] if on v0.9.9.0 or later, otherwise returns an empty list Confirm all prompts (--yes_ is available on v0.9.9.0 or later - ''' - if 'chocolatey._yes' in __context__: - return __context__['chocolatey._yes'] - if _LooseVersion(chocolatey_version()) >= _LooseVersion('0.9.9'): - answer = ['--yes'] + """ + if "chocolatey._yes" in __context__: + return __context__["chocolatey._yes"] + if _LooseVersion(chocolatey_version()) >= _LooseVersion("0.9.9"): + answer = ["--yes"] else: answer = [] - __context__['chocolatey._yes'] = answer - return __context__['chocolatey._yes'] + __context__["chocolatey._yes"] = answer + return __context__["chocolatey._yes"] def _no_progress(): - ''' + """ Returns ['--no-progress'] if on v0.10.4 or later, otherwise returns an empty list - ''' - if 'chocolatey._no_progress' in __context__: - return __context__['chocolatey._no_progress'] - if _LooseVersion(chocolatey_version()) >= _LooseVersion('0.10.4'): - answer = ['--no-progress'] + """ + if "chocolatey._no_progress" in __context__: + return __context__["chocolatey._no_progress"] + if _LooseVersion(chocolatey_version()) >= _LooseVersion("0.10.4"): + answer = ["--no-progress"] else: - log.warning('--no-progress unsupported in choco < 0.10.4') + log.warning("--no-progress unsupported in choco < 0.10.4") answer = [] - __context__['chocolatey._no_progress'] = answer - return __context__['chocolatey._no_progress'] + __context__["chocolatey._no_progress"] = answer + return __context__["chocolatey._no_progress"] def _find_chocolatey(): - ''' + """ Returns the full path to chocolatey.bat on the host. - ''' + """ # Check context - if 'chocolatey._path' in __context__: - return __context__['chocolatey._path'] + if "chocolatey._path" in __context__: + return __context__["chocolatey._path"] # Check the path - choc_path = __salt__['cmd.which']('chocolatey.exe') + choc_path = __salt__["cmd.which"]("chocolatey.exe") if choc_path: - __context__['chocolatey._path'] = choc_path - return __context__['chocolatey._path'] + __context__["chocolatey._path"] = choc_path + return __context__["chocolatey._path"] # Check in common locations choc_defaults = [ - os.path.join(os.environ.get('ProgramData'), 'Chocolatey', 'bin', 'chocolatey.exe'), - os.path.join(os.environ.get('SystemDrive'), 'Chocolatey', 'bin', 'chocolatey.bat')] + os.path.join( + os.environ.get("ProgramData"), "Chocolatey", "bin", "chocolatey.exe" + ), + os.path.join( + os.environ.get("SystemDrive"), "Chocolatey", "bin", "chocolatey.bat" + ), + ] for choc_exe in choc_defaults: if os.path.isfile(choc_exe): - __context__['chocolatey._path'] = choc_exe - return __context__['chocolatey._path'] + __context__["chocolatey._path"] = choc_exe + return __context__["chocolatey._path"] # Not installed, raise an error - err = ('Chocolatey not installed. Use chocolatey.bootstrap to ' - 'install the Chocolatey package manager.') + err = ( + "Chocolatey not installed. Use chocolatey.bootstrap to " + "install the Chocolatey package manager." + ) raise CommandExecutionError(err) def chocolatey_version(): - ''' + """ Returns the version of Chocolatey installed on the minion. CLI Example: @@ -128,20 +137,20 @@ def chocolatey_version(): .. code-block:: bash salt '*' chocolatey.chocolatey_version - ''' - if 'chocolatey._version' in __context__: - return __context__['chocolatey._version'] + """ + if "chocolatey._version" in __context__: + return __context__["chocolatey._version"] cmd = [_find_chocolatey()] - cmd.append('-v') - out = __salt__['cmd.run'](cmd, python_shell=False) - __context__['chocolatey._version'] = out + cmd.append("-v") + out = __salt__["cmd.run"](cmd, python_shell=False) + __context__["chocolatey._version"] = out - return __context__['chocolatey._version'] + return __context__["chocolatey._version"] def bootstrap(force=False): - ''' + """ Download and install the latest version of the Chocolatey package manager via the official bootstrap. @@ -162,86 +171,103 @@ def bootstrap(force=False): salt '*' chocolatey.bootstrap salt '*' chocolatey.bootstrap force=True - ''' + """ # Check if Chocolatey is already present in the path try: choc_path = _find_chocolatey() except CommandExecutionError: choc_path = None if choc_path and not force: - return 'Chocolatey found at {0}'.format(choc_path) + return "Chocolatey found at {0}".format(choc_path) # The following lookup tables are required to determine the correct # download required to install PowerShell. That's right, there's more # than one! You're welcome. ps_downloads = { - ('Vista', 'x86'): 'http://download.microsoft.com/download/A/7/5/A75BC017-63CE-47D6-8FA4-AFB5C21BAC54/Windows6.0-KB968930-x86.msu', - ('Vista', 'AMD64'): 'http://download.microsoft.com/download/3/C/8/3C8CF51E-1D9D-4DAA-AAEA-5C48D1CD055C/Windows6.0-KB968930-x64.msu', - ('2008Server', 'x86'): 'http://download.microsoft.com/download/F/9/E/F9EF6ACB-2BA8-4845-9C10-85FC4A69B207/Windows6.0-KB968930-x86.msu', - ('2008Server', 'AMD64'): 'http://download.microsoft.com/download/2/8/6/28686477-3242-4E96-9009-30B16BED89AF/Windows6.0-KB968930-x64.msu' + ( + "Vista", + "x86", + ): "http://download.microsoft.com/download/A/7/5/A75BC017-63CE-47D6-8FA4-AFB5C21BAC54/Windows6.0-KB968930-x86.msu", + ( + "Vista", + "AMD64", + ): "http://download.microsoft.com/download/3/C/8/3C8CF51E-1D9D-4DAA-AAEA-5C48D1CD055C/Windows6.0-KB968930-x64.msu", + ( + "2008Server", + "x86", + ): "http://download.microsoft.com/download/F/9/E/F9EF6ACB-2BA8-4845-9C10-85FC4A69B207/Windows6.0-KB968930-x86.msu", + ( + "2008Server", + "AMD64", + ): "http://download.microsoft.com/download/2/8/6/28686477-3242-4E96-9009-30B16BED89AF/Windows6.0-KB968930-x64.msu", } # It took until .NET v4.0 for Microsoft got the hang of making installers, # this should work under any version of Windows - net4_url = 'http://download.microsoft.com/download/1/B/E/1BE39E79-7E39-46A3-96FF-047F95396215/dotNetFx40_Full_setup.exe' + net4_url = "http://download.microsoft.com/download/1/B/E/1BE39E79-7E39-46A3-96FF-047F95396215/dotNetFx40_Full_setup.exe" temp_dir = tempfile.gettempdir() # Check if PowerShell is installed. This should be the case for every # Windows release following Server 2008. - ps_path = 'C:\\Windows\\SYSTEM32\\WindowsPowerShell\\v1.0\\powershell.exe' + ps_path = "C:\\Windows\\SYSTEM32\\WindowsPowerShell\\v1.0\\powershell.exe" - if not __salt__['cmd.has_exec'](ps_path): - if (__grains__['osrelease'], __grains__['cpuarch']) in ps_downloads: + if not __salt__["cmd.has_exec"](ps_path): + if (__grains__["osrelease"], __grains__["cpuarch"]) in ps_downloads: # Install the appropriate release of PowerShell v2.0 - url = ps_downloads[(__grains__['osrelease'], __grains__['cpuarch'])] - dest = os.path.join(temp_dir, 'powershell.exe') - __salt__['cp.get_url'](url, dest) - cmd = [dest, '/quiet', '/norestart'] - result = __salt__['cmd.run_all'](cmd, python_shell=False) - if result['retcode'] != 0: - err = ('Installing Windows PowerShell failed. Please run the ' - 'installer GUI on the host to get a more specific ' - 'reason.') + url = ps_downloads[(__grains__["osrelease"], __grains__["cpuarch"])] + dest = os.path.join(temp_dir, "powershell.exe") + __salt__["cp.get_url"](url, dest) + cmd = [dest, "/quiet", "/norestart"] + result = __salt__["cmd.run_all"](cmd, python_shell=False) + if result["retcode"] != 0: + err = ( + "Installing Windows PowerShell failed. Please run the " + "installer GUI on the host to get a more specific " + "reason." + ) raise CommandExecutionError(err) else: - err = 'Windows PowerShell not found' + err = "Windows PowerShell not found" raise CommandNotFoundError(err) # Run the .NET Framework 4 web installer - dest = os.path.join(temp_dir, 'dotnet4.exe') - __salt__['cp.get_url'](net4_url, dest) - cmd = [dest, '/q', '/norestart'] - result = __salt__['cmd.run_all'](cmd, python_shell=False) - if result['retcode'] != 0: - err = ('Installing .NET v4.0 failed. Please run the installer GUI on ' - 'the host to get a more specific reason.') + dest = os.path.join(temp_dir, "dotnet4.exe") + __salt__["cp.get_url"](net4_url, dest) + cmd = [dest, "/q", "/norestart"] + result = __salt__["cmd.run_all"](cmd, python_shell=False) + if result["retcode"] != 0: + err = ( + "Installing .NET v4.0 failed. Please run the installer GUI on " + "the host to get a more specific reason." + ) raise CommandExecutionError(err) # Run the Chocolatey bootstrap. cmd = ( - '{0} -NoProfile -ExecutionPolicy unrestricted ' + "{0} -NoProfile -ExecutionPolicy unrestricted " '-Command "iex ((new-object net.webclient).' - 'DownloadString(\'https://chocolatey.org/install.ps1\'))" ' - '&& SET PATH=%PATH%;%systemdrive%\\chocolatey\\bin' - .format(ps_path) + "DownloadString('https://chocolatey.org/install.ps1'))\" " + "&& SET PATH=%PATH%;%systemdrive%\\chocolatey\\bin".format(ps_path) ) - result = __salt__['cmd.run_all'](cmd, python_shell=True) + result = __salt__["cmd.run_all"](cmd, python_shell=True) - if result['retcode'] != 0: - err = 'Bootstrapping Chocolatey failed: {0}'.format(result['stderr']) + if result["retcode"] != 0: + err = "Bootstrapping Chocolatey failed: {0}".format(result["stderr"]) raise CommandExecutionError(err) - return result['stdout'] + return result["stdout"] -def list_(narrow=None, - all_versions=False, - pre_versions=False, - source=None, - local_only=False, - exact=False): - ''' +def list_( + narrow=None, + all_versions=False, + pre_versions=False, + source=None, + local_only=False, + exact=False, +): + """ Instructs Chocolatey to pull a vague package list from the repository. Args: @@ -279,43 +305,43 @@ def list_(narrow=None, salt '*' chocolatey.list salt '*' chocolatey.list all_versions=True - ''' + """ choc_path = _find_chocolatey() - cmd = [choc_path, 'list'] + cmd = [choc_path, "list"] if narrow: cmd.append(narrow) if salt.utils.data.is_true(all_versions): - cmd.append('--allversions') + cmd.append("--allversions") if salt.utils.data.is_true(pre_versions): - cmd.append('--prerelease') + cmd.append("--prerelease") if source: - cmd.extend(['--source', source]) + cmd.extend(["--source", source]) if local_only: - cmd.append('--local-only') + cmd.append("--local-only") if exact: - cmd.append('--exact') + cmd.append("--exact") # This is needed to parse the output correctly - cmd.append('--limit-output') + cmd.append("--limit-output") - result = __salt__['cmd.run_all'](cmd, python_shell=False) + result = __salt__["cmd.run_all"](cmd, python_shell=False) # Chocolatey introduced Enhanced Exit Codes starting with version 0.10.12 # Exit Code 2 means there were no results, but is not a failure # This may start to effect other functions in the future as Chocolatey # moves more functions to this new paradigm # https://github.com/chocolatey/choco/issues/1758 - if result['retcode'] not in [0, 2]: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] not in [0, 2]: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) ret = CaseInsensitiveDict({}) - pkg_re = re.compile(r'(\S+)\|(\S+)') - for line in result['stdout'].split('\n'): + pkg_re = re.compile(r"(\S+)\|(\S+)") + for line in result["stdout"].split("\n"): if line.startswith("No packages"): return ret for name, ver in pkg_re.findall(line): - if 'chocolatey' in name: + if "chocolatey" in name: continue if name not in ret: ret[name] = [] @@ -325,7 +351,7 @@ def list_(narrow=None, def list_webpi(): - ''' + """ Instructs Chocolatey to pull a full package list from the Microsoft Web PI repository. @@ -337,20 +363,20 @@ def list_webpi(): .. code-block:: bash salt '*' chocolatey.list_webpi - ''' + """ choc_path = _find_chocolatey() - cmd = [choc_path, 'list', '--source', 'webpi'] - result = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = [choc_path, "list", "--source", "webpi"] + result = __salt__["cmd.run_all"](cmd, python_shell=False) - if result['retcode'] != 0: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] != 0: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) - return result['stdout'] + return result["stdout"] def list_windowsfeatures(): - ''' + """ Instructs Chocolatey to pull a full package list from the Windows Features list, via the Deployment Image Servicing and Management tool. @@ -362,30 +388,32 @@ def list_windowsfeatures(): .. code-block:: bash salt '*' chocolatey.list_windowsfeatures - ''' + """ choc_path = _find_chocolatey() - cmd = [choc_path, 'list', '--source', 'windowsfeatures'] - result = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = [choc_path, "list", "--source", "windowsfeatures"] + result = __salt__["cmd.run_all"](cmd, python_shell=False) - if result['retcode'] != 0: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] != 0: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) - return result['stdout'] + return result["stdout"] -def install(name, - version=None, - source=None, - force=False, - pre_versions=False, - install_args=None, - override_args=False, - force_x86=False, - package_args=None, - allow_multiple=False, - execution_timeout=None): - ''' +def install( + name, + version=None, + source=None, + force=False, + pre_versions=False, + install_args=None, + override_args=False, + force_x86=False, + package_args=None, + allow_multiple=False, + execution_timeout=None, +): + """ Instructs Chocolatey to install a package. Args: @@ -455,54 +483,55 @@ def install(name, salt '*' chocolatey.install salt '*' chocolatey.install version= salt '*' chocolatey.install install_args= override_args=True - ''' + """ if force and allow_multiple: raise SaltInvocationError( - 'Cannot use \'force\' in conjunction with \'allow_multiple\'') + "Cannot use 'force' in conjunction with 'allow_multiple'" + ) choc_path = _find_chocolatey() # chocolatey helpfully only supports a single package argument # CORRECTION: it also supports multiple package names separated by spaces # but any additional arguments apply to ALL packages specified - cmd = [choc_path, 'install', name] + cmd = [choc_path, "install", name] if version: - cmd.extend(['--version', version]) + cmd.extend(["--version", version]) if source: - cmd.extend(['--source', source]) + cmd.extend(["--source", source]) if salt.utils.data.is_true(force): - cmd.append('--force') + cmd.append("--force") if salt.utils.data.is_true(pre_versions): - cmd.append('--prerelease') + cmd.append("--prerelease") if install_args: - cmd.extend(['--installarguments', install_args]) + cmd.extend(["--installarguments", install_args]) if override_args: - cmd.append('--overridearguments') + cmd.append("--overridearguments") if force_x86: - cmd.append('--forcex86') + cmd.append("--forcex86") if package_args: - cmd.extend(['--packageparameters', package_args]) + cmd.extend(["--packageparameters", package_args]) if allow_multiple: - cmd.append('--allow-multiple') + cmd.append("--allow-multiple") if execution_timeout: - cmd.extend(['--execution-timeout', execution_timeout]) + cmd.extend(["--execution-timeout", execution_timeout]) # Salt doesn't need to see the progress cmd.extend(_no_progress()) cmd.extend(_yes()) - result = __salt__['cmd.run_all'](cmd, python_shell=False) + result = __salt__["cmd.run_all"](cmd, python_shell=False) - if result['retcode'] not in [0, 1641, 3010]: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] not in [0, 1641, 3010]: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) - if name == 'chocolatey': + if name == "chocolatey": _clear_context() - return result['stdout'] + return result["stdout"] def install_cygwin(name, install_args=None, override_args=False): - ''' + """ Instructs Chocolatey to install a package via Cygwin. name @@ -524,15 +553,14 @@ def install_cygwin(name, install_args=None, override_args=False): salt '*' chocolatey.install_cygwin salt '*' chocolatey.install_cygwin install_args= override_args=True - ''' - return install(name, - source='cygwin', - install_args=install_args, - override_args=override_args) + """ + return install( + name, source="cygwin", install_args=install_args, override_args=override_args + ) def install_gem(name, version=None, install_args=None, override_args=False): - ''' + """ Instructs Chocolatey to install a package via Ruby's Gems. name @@ -560,16 +588,18 @@ def install_gem(name, version=None, install_args=None, override_args=False): salt '*' chocolatey.install_gem salt '*' chocolatey.install_gem version= salt '*' chocolatey.install_gem install_args= override_args=True - ''' - return install(name, - version=version, - source='ruby', - install_args=install_args, - override_args=override_args) + """ + return install( + name, + version=version, + source="ruby", + install_args=install_args, + override_args=override_args, + ) def install_missing(name, version=None, source=None): - ''' + """ Instructs Chocolatey to install a package if it doesn't already exist. .. versionchanged:: 2014.7.0 @@ -595,30 +625,30 @@ def install_missing(name, version=None, source=None): salt '*' chocolatey.install_missing salt '*' chocolatey.install_missing version= - ''' - if _LooseVersion(chocolatey_version()) >= _LooseVersion('0.9.8.24'): - log.warning('installmissing is deprecated, using install') + """ + if _LooseVersion(chocolatey_version()) >= _LooseVersion("0.9.8.24"): + log.warning("installmissing is deprecated, using install") return install(name, version=version) # chocolatey helpfully only supports a single package argument - cmd = [_find_chocolatey(), 'installmissing', name] + cmd = [_find_chocolatey(), "installmissing", name] if version: - cmd.extend(['--version', version]) + cmd.extend(["--version", version]) if source: - cmd.extend(['--source', source]) + cmd.extend(["--source", source]) # Shouldn't need this as this code should never run on v0.9.9 and newer cmd.extend(_yes()) - result = __salt__['cmd.run_all'](cmd, python_shell=False) + result = __salt__["cmd.run_all"](cmd, python_shell=False) - if result['retcode'] != 0: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] != 0: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) - return result['stdout'] + return result["stdout"] def install_python(name, version=None, install_args=None, override_args=False): - ''' + """ Instructs Chocolatey to install a package via Python's easy_install. name @@ -645,16 +675,18 @@ def install_python(name, version=None, install_args=None, override_args=False): salt '*' chocolatey.install_python salt '*' chocolatey.install_python version= salt '*' chocolatey.install_python install_args= override_args=True - ''' - return install(name, - version=version, - source='python', - install_args=install_args, - override_args=override_args) + """ + return install( + name, + version=version, + source="python", + install_args=install_args, + override_args=override_args, + ) def install_windowsfeatures(name): - ''' + """ Instructs Chocolatey to install a Windows Feature via the Deployment Image Servicing and Management tool. @@ -666,12 +698,12 @@ def install_windowsfeatures(name): .. code-block:: bash salt '*' chocolatey.install_windowsfeatures - ''' - return install(name, source='windowsfeatures') + """ + return install(name, source="windowsfeatures") def install_webpi(name, install_args=None, override_args=False): - ''' + """ Instructs Chocolatey to install a package via the Microsoft Web PI service. name @@ -693,15 +725,14 @@ def install_webpi(name, install_args=None, override_args=False): salt '*' chocolatey.install_webpi salt '*' chocolatey.install_webpi install_args= override_args=True - ''' - return install(name, - source='webpi', - install_args=install_args, - override_args=override_args) + """ + return install( + name, source="webpi", install_args=install_args, override_args=override_args + ) def uninstall(name, version=None, uninstall_args=None, override_args=False): - ''' + """ Instructs Chocolatey to uninstall a package. name @@ -729,35 +760,37 @@ def uninstall(name, version=None, uninstall_args=None, override_args=False): salt '*' chocolatey.uninstall salt '*' chocolatey.uninstall version= salt '*' chocolatey.uninstall version= uninstall_args= override_args=True - ''' + """ # chocolatey helpfully only supports a single package argument - cmd = [_find_chocolatey(), 'uninstall', name] + cmd = [_find_chocolatey(), "uninstall", name] if version: - cmd.extend(['--version', version]) + cmd.extend(["--version", version]) if uninstall_args: - cmd.extend(['--uninstallarguments', uninstall_args]) + cmd.extend(["--uninstallarguments", uninstall_args]) if override_args: - cmd.extend(['--overridearguments']) + cmd.extend(["--overridearguments"]) cmd.extend(_yes()) - result = __salt__['cmd.run_all'](cmd, python_shell=False) + result = __salt__["cmd.run_all"](cmd, python_shell=False) - if result['retcode'] not in [0, 1605, 1614, 1641]: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] not in [0, 1605, 1614, 1641]: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) - return result['stdout'] + return result["stdout"] -def upgrade(name, - version=None, - source=None, - force=False, - pre_versions=False, - install_args=None, - override_args=False, - force_x86=False, - package_args=None): - ''' +def upgrade( + name, + version=None, + source=None, + force=False, + pre_versions=False, + install_args=None, + override_args=False, + force_x86=False, + package_args=None, +): + """ .. versionadded:: 2016.3.4 Instructs Chocolatey to upgrade packages on the system. (update is being @@ -808,41 +841,41 @@ def upgrade(name, salt "*" chocolatey.upgrade all salt "*" chocolatey.upgrade pre_versions=True - ''' + """ # chocolatey helpfully only supports a single package argument - cmd = [_find_chocolatey(), 'upgrade', name] + cmd = [_find_chocolatey(), "upgrade", name] if version: - cmd.extend(['--version', version]) + cmd.extend(["--version", version]) if source: - cmd.extend(['--source', source]) + cmd.extend(["--source", source]) if salt.utils.data.is_true(force): - cmd.append('--force') + cmd.append("--force") if salt.utils.data.is_true(pre_versions): - cmd.append('--prerelease') + cmd.append("--prerelease") if install_args: - cmd.extend(['--installarguments', install_args]) + cmd.extend(["--installarguments", install_args]) if override_args: - cmd.append('--overridearguments') + cmd.append("--overridearguments") if force_x86: - cmd.append('--forcex86') + cmd.append("--forcex86") if package_args: - cmd.extend(['--packageparameters', package_args]) + cmd.extend(["--packageparameters", package_args]) # Salt doesn't need to see the progress cmd.extend(_no_progress()) cmd.extend(_yes()) - result = __salt__['cmd.run_all'](cmd, python_shell=False) + result = __salt__["cmd.run_all"](cmd, python_shell=False) - if result['retcode'] not in [0, 1641, 3010]: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] not in [0, 1641, 3010]: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) - return result['stdout'] + return result["stdout"] def update(name, source=None, pre_versions=False): - ''' + """ Instructs Chocolatey to update packages on the system. name @@ -862,32 +895,32 @@ def update(name, source=None, pre_versions=False): salt "*" chocolatey.update all salt "*" chocolatey.update pre_versions=True - ''' + """ # chocolatey helpfully only supports a single package argument - if _LooseVersion(chocolatey_version()) >= _LooseVersion('0.9.8.24'): - log.warning('update is deprecated, using upgrade') + if _LooseVersion(chocolatey_version()) >= _LooseVersion("0.9.8.24"): + log.warning("update is deprecated, using upgrade") return upgrade(name, source=source, pre_versions=pre_versions) - cmd = [_find_chocolatey(), 'update', name] + cmd = [_find_chocolatey(), "update", name] if source: - cmd.extend(['--source', source]) + cmd.extend(["--source", source]) if salt.utils.data.is_true(pre_versions): - cmd.append('--prerelease') + cmd.append("--prerelease") # Salt doesn't need to see the progress cmd.extend(_no_progress()) cmd.extend(_yes()) - result = __salt__['cmd.run_all'](cmd, python_shell=False) + result = __salt__["cmd.run_all"](cmd, python_shell=False) - if result['retcode'] not in [0, 1641, 3010]: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] not in [0, 1641, 3010]: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) - return result['stdout'] + return result["stdout"] def version(name, check_remote=False, source=None, pre_versions=False): - ''' + """ Instructs Chocolatey to check an installed package version, and optionally compare it to one available from a remote feed. @@ -917,7 +950,7 @@ def version(name, check_remote=False, source=None, pre_versions=False): salt "*" chocolatey.version salt "*" chocolatey.version check_remote=True - ''' + """ installed = list_(narrow=name, local_only=True) packages = {} @@ -930,14 +963,13 @@ def version(name, check_remote=False, source=None, pre_versions=False): available = list_(narrow=name, pre_versions=pre_versions, source=source) for pkg in packages: - packages[pkg] = {'installed': installed[pkg], - 'available': available[pkg]} + packages[pkg] = {"installed": installed[pkg], "available": available[pkg]} return packages def add_source(name, source_location, username=None, password=None): - ''' + """ Instructs Chocolatey to add a source. name @@ -961,23 +993,31 @@ def add_source(name, source_location, username=None, password=None): salt '*' chocolatey.add_source salt '*' chocolatey.add_source user= password= - ''' - cmd = [_find_chocolatey(), 'sources', 'add', '--name', name, '--source', source_location] + """ + cmd = [ + _find_chocolatey(), + "sources", + "add", + "--name", + name, + "--source", + source_location, + ] if username: - cmd.extend(['--user', username]) + cmd.extend(["--user", username]) if password: - cmd.extend(['--password', password]) - result = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd.extend(["--password", password]) + result = __salt__["cmd.run_all"](cmd, python_shell=False) - if result['retcode'] != 0: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] != 0: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) - return result['stdout'] + return result["stdout"] def _change_source_state(name, state): - ''' + """ Instructs Chocolatey to change the state of a source. name @@ -986,19 +1026,19 @@ def _change_source_state(name, state): state State in which you want the chocolatey repository. - ''' - cmd = [_find_chocolatey(), 'source', state, '--name', name] - result = __salt__['cmd.run_all'](cmd, python_shell=False) + """ + cmd = [_find_chocolatey(), "source", state, "--name", name] + result = __salt__["cmd.run_all"](cmd, python_shell=False) - if result['retcode'] != 0: - err = 'Running chocolatey failed: {0}'.format(result['stdout']) + if result["retcode"] != 0: + err = "Running chocolatey failed: {0}".format(result["stdout"]) raise CommandExecutionError(err) - return result['stdout'] + return result["stdout"] def enable_source(name): - ''' + """ Instructs Chocolatey to enable a source. name @@ -1010,12 +1050,12 @@ def enable_source(name): salt '*' chocolatey.enable_source - ''' + """ return _change_source_state(name, "enable") def disable_source(name): - ''' + """ Instructs Chocolatey to disable a source. name @@ -1026,5 +1066,5 @@ def disable_source(name): .. code-block:: bash salt '*' chocolatey.disable_source - ''' + """ return _change_source_state(name, "disable") diff --git a/salt/modules/chronos.py b/salt/modules/chronos.py index 559cb9ad500..daf3e3e052a 100644 --- a/salt/modules/chronos.py +++ b/salt/modules/chronos.py @@ -1,56 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" Module providing a simple management interface to a chronos cluster. Currently this only works when run through a proxy minion. .. versionadded:: 2015.8.2 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + import salt.utils.http import salt.utils.json import salt.utils.platform from salt.exceptions import get_error_message - -__proxyenabled__ = ['chronos'] +__proxyenabled__ = ["chronos"] log = logging.getLogger(__file__) def __virtual__(): # only valid in proxy minions for now - return salt.utils.platform.is_proxy() and 'proxy' in __opts__ + return salt.utils.platform.is_proxy() and "proxy" in __opts__ def _base_url(): - ''' + """ Return the proxy configured base url. - ''' + """ base_url = "http://locahost:4400" - if 'proxy' in __opts__: - base_url = __opts__['proxy'].get('base_url', base_url) + if "proxy" in __opts__: + base_url = __opts__["proxy"].get("base_url", base_url) return base_url def _jobs(): - ''' + """ Return the currently configured jobs. - ''' + """ response = salt.utils.http.query( - "{0}/scheduler/jobs".format(_base_url()), - decode_type='json', - decode=True, + "{0}/scheduler/jobs".format(_base_url()), decode_type="json", decode=True, ) jobs = {} - for job in response['dict']: - jobs[job.pop('name')] = job + for job in response["dict"]: + jobs[job.pop("name")] = job return jobs def jobs(): - ''' + """ Return a list of the currently installed job names. CLI Example: @@ -58,14 +56,14 @@ def jobs(): .. code-block:: bash salt chronos-minion-id chronos.jobs - ''' + """ job_names = _jobs().keys() job_names.sort() - return {'jobs': job_names} + return {"jobs": job_names} def has_job(name): - ''' + """ Return whether the given job is currently configured. CLI Example: @@ -73,12 +71,12 @@ def has_job(name): .. code-block:: bash salt chronos-minion-id chronos.has_job my-job - ''' + """ return name in _jobs() def job(name): - ''' + """ Return the current server configuration for the specified job. CLI Example: @@ -86,15 +84,15 @@ def job(name): .. code-block:: bash salt chronos-minion-id chronos.job my-job - ''' + """ jobs = _jobs() if name in jobs: - return {'job': jobs[name]} + return {"job": jobs[name]} return None def update_job(name, config): - ''' + """ Update the specified job with the given configuration. CLI Example: @@ -102,32 +100,26 @@ def update_job(name, config): .. code-block:: bash salt chronos-minion-id chronos.update_job my-job '' - ''' - if 'name' not in config: - config['name'] = name + """ + if "name" not in config: + config["name"] = name data = salt.utils.json.dumps(config) try: response = salt.utils.http.query( "{0}/scheduler/iso8601".format(_base_url()), - method='POST', + method="POST", data=data, - header_dict={ - 'Content-Type': 'application/json', - }, + header_dict={"Content-Type": "application/json"}, ) - log.debug('update response: %s', response) - return {'success': True} + log.debug("update response: %s", response) + return {"success": True} except Exception as ex: # pylint: disable=broad-except - log.error('unable to update chronos job: %s', get_error_message(ex)) - return { - 'exception': { - 'message': get_error_message(ex), - } - } + log.error("unable to update chronos job: %s", get_error_message(ex)) + return {"exception": {"message": get_error_message(ex)}} def rm_job(name): - ''' + """ Remove the specified job from the server. CLI Example: @@ -135,9 +127,8 @@ def rm_job(name): .. code-block:: bash salt chronos-minion-id chronos.rm_job my-job - ''' + """ response = salt.utils.http.query( - "{0}/scheduler/job/{1}".format(_base_url(), name), - method='DELETE', + "{0}/scheduler/job/{1}".format(_base_url(), name), method="DELETE", ) return True diff --git a/salt/modules/chroot.py b/salt/modules/chroot.py index bc089ebf18e..6512a70f88d 100644 --- a/salt/modules/chroot.py +++ b/salt/modules/chroot.py @@ -1,19 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" :maintainer: Alberto Planas :maturity: new :depends: None :platform: Linux -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import os import sys import tempfile - import salt import salt.client.ssh.state import salt.client.ssh.wrapper.state @@ -22,36 +22,33 @@ import salt.exceptions import salt.ext.six as six import salt.utils.args - -__func_alias__ = { - 'apply_': 'apply' -} +__func_alias__ = {"apply_": "apply"} log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Chroot command is required. - ''' - if __utils__['path.which']('chroot') is not None: + """ + if __utils__["path.which"]("chroot") is not None: return True else: - return (False, 'Module chroot requires the command chroot') + return (False, "Module chroot requires the command chroot") def exist(root): - ''' + """ Return True if the chroot environment is present. - ''' - dev = os.path.join(root, 'dev') - proc = os.path.join(root, 'proc') - sys = os.path.join(root, 'sys') + """ + dev = os.path.join(root, "dev") + proc = os.path.join(root, "proc") + sys = os.path.join(root, "sys") return all(os.path.isdir(i) for i in (root, dev, proc, sys)) def create(root): - ''' + """ Create a basic chroot environment. Note that this environment is not functional. The caller needs to @@ -67,23 +64,23 @@ def create(root): salt myminion chroot.create /chroot - ''' + """ if not exist(root): - dev = os.path.join(root, 'dev') - proc = os.path.join(root, 'proc') - sys = os.path.join(root, 'sys') + dev = os.path.join(root, "dev") + proc = os.path.join(root, "proc") + sys = os.path.join(root, "sys") try: os.makedirs(dev, mode=0o755) os.makedirs(proc, mode=0o555) os.makedirs(sys, mode=0o555) except OSError as e: - log.error('Error when trying to create chroot directories: %s', e) + log.error("Error when trying to create chroot directories: %s", e) return False return True def call(root, function, *args, **kwargs): - ''' + """ Executes a Salt function inside a chroot environment. The chroot does not need to have Salt installed, but Python is @@ -102,23 +99,21 @@ def call(root, function, *args, **kwargs): salt myminion chroot.call /chroot test.ping salt myminion chroot.call /chroot ssh.set_auth_key user key=mykey - ''' + """ if not function: - raise salt.exceptions.CommandExecutionError( - 'Missing function parameter') + raise salt.exceptions.CommandExecutionError("Missing function parameter") if not exist(root): - raise salt.exceptions.CommandExecutionError( - 'Chroot environment not found') + raise salt.exceptions.CommandExecutionError("Chroot environment not found") # Create a temporary directory inside the chroot where we can # untar salt-thin thin_dest_path = tempfile.mkdtemp(dir=root) - thin_path = __utils__['thin.gen_thin']( - __opts__['cachedir'], - extra_mods=__salt__['config.option']('thin_extra_mods', ''), - so_mods=__salt__['config.option']('thin_so_mods', '') + thin_path = __utils__["thin.gen_thin"]( + __opts__["cachedir"], + extra_mods=__salt__["config.option"]("thin_extra_mods", ""), + so_mods=__salt__["config.option"]("thin_so_mods", ""), ) # Some bug in Salt is preventing us to use `archive.tar` here. A # AsyncZeroMQReqChannel is not closed at the end os the salt-call, @@ -126,50 +121,51 @@ def call(root, function, *args, **kwargs): # # stdout = __salt__['archive.tar']('xzf', thin_path, dest=thin_dest_path) # - stdout = __salt__['cmd.run'](['tar', 'xzf', thin_path, - '-C', thin_dest_path]) + stdout = __salt__["cmd.run"](["tar", "xzf", thin_path, "-C", thin_dest_path]) if stdout: - __utils__['files.rm_rf'](thin_dest_path) - return {'result': False, 'comment': stdout} + __utils__["files.rm_rf"](thin_dest_path) + return {"result": False, "comment": stdout} - chroot_path = os.path.join(os.path.sep, - os.path.relpath(thin_dest_path, root)) + chroot_path = os.path.join(os.path.sep, os.path.relpath(thin_dest_path, root)) try: safe_kwargs = salt.utils.args.clean_kwargs(**kwargs) - salt_argv = [ - 'python{}'.format(sys.version_info[0]), - os.path.join(chroot_path, 'salt-call'), - '--metadata', - '--local', - '--log-file', os.path.join(chroot_path, 'log'), - '--cachedir', os.path.join(chroot_path, 'cache'), - '--out', 'json', - '-l', 'quiet', - '--', - function - ] + list(args) + [ - '{}={}'.format(k, v) for (k, v) in safe_kwargs.items() - ] - ret = __salt__['cmd.run_chroot'](root, [str(x) for x in salt_argv]) + salt_argv = ( + [ + "python{}".format(sys.version_info[0]), + os.path.join(chroot_path, "salt-call"), + "--metadata", + "--local", + "--log-file", + os.path.join(chroot_path, "log"), + "--cachedir", + os.path.join(chroot_path, "cache"), + "--out", + "json", + "-l", + "quiet", + "--", + function, + ] + + list(args) + + ["{}={}".format(k, v) for (k, v) in safe_kwargs.items()] + ) + ret = __salt__["cmd.run_chroot"](root, [str(x) for x in salt_argv]) # Process "real" result in stdout try: - data = __utils__['json.find_json'](ret['stdout']) - local = data.get('local', data) - if isinstance(local, dict) and 'retcode' in local: - __context__['retcode'] = local['retcode'] - return local.get('return', data) + data = __utils__["json.find_json"](ret["stdout"]) + local = data.get("local", data) + if isinstance(local, dict) and "retcode" in local: + __context__["retcode"] = local["retcode"] + return local.get("return", data) except (KeyError, ValueError): - return { - 'result': False, - 'comment': "Can't parse container command output" - } + return {"result": False, "comment": "Can't parse container command output"} finally: - __utils__['files.rm_rf'](thin_dest_path) + __utils__["files.rm_rf"](thin_dest_path) def apply_(root, mods=None, **kwargs): - ''' + """ Apply an state inside a chroot. This function will call `chroot.highstate` or `chroot.sls` based @@ -190,21 +186,21 @@ def apply_(root, mods=None, **kwargs): salt myminion chroot.apply /chroot stuff salt myminion chroot.apply /chroot stuff pillar='{"foo": "bar"}' - ''' + """ if mods: return sls(root, mods, **kwargs) return highstate(root, **kwargs) def _create_and_execute_salt_state(root, chunks, file_refs, test, hash_type): - ''' + """ Create the salt_stage tarball, and execute in the chroot - ''' + """ # Create the tar containing the state pkg and relevant files. salt.client.ssh.wrapper.state._cleanup_slsmod_low_data(chunks) trans_tar = salt.client.ssh.state.prep_trans_tar( - salt.fileclient.get_file_client(__opts__), chunks, file_refs, - __pillar__, root) + salt.fileclient.get_file_client(__opts__), chunks, file_refs, __pillar__, root + ) trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, hash_type) ret = None @@ -212,21 +208,26 @@ def _create_and_execute_salt_state(root, chunks, file_refs, test, hash_type): # Create a temporary directory inside the chroot where we can move # the salt_stage.tgz salt_state_path = tempfile.mkdtemp(dir=root) - salt_state_path = os.path.join(salt_state_path, 'salt_state.tgz') - salt_state_path_in_chroot = salt_state_path.replace(root, '', 1) + salt_state_path = os.path.join(salt_state_path, "salt_state.tgz") + salt_state_path_in_chroot = salt_state_path.replace(root, "", 1) try: salt.utils.files.copyfile(trans_tar, salt_state_path) - ret = call(root, 'state.pkg', salt_state_path_in_chroot, - test=test, pkg_sum=trans_tar_sum, - hash_type=hash_type) + ret = call( + root, + "state.pkg", + salt_state_path_in_chroot, + test=test, + pkg_sum=trans_tar_sum, + hash_type=hash_type, + ) finally: - __utils__['files.rm_rf'](salt_state_path) + __utils__["files.rm_rf"](salt_state_path) return ret -def sls(root, mods, saltenv='base', test=None, exclude=None, **kwargs): - ''' +def sls(root, mods, saltenv="base", test=None, exclude=None, **kwargs): + """ Execute the states in one or more SLS files inside the chroot. root @@ -256,30 +257,30 @@ def sls(root, mods, saltenv='base', test=None, exclude=None, **kwargs): .. code-block:: bash salt '*' chroot.sls /chroot stuff pillar='{"foo": "bar"}' - ''' + """ # Get a copy of the pillar data, to avoid overwriting the current # pillar, instead the one delegated pillar = copy.deepcopy(__pillar__) - pillar.update(kwargs.get('pillar', {})) + pillar.update(kwargs.get("pillar", {})) # Clone the options data and apply some default values. May not be # needed, as this module just delegate opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) st_ = salt.client.ssh.state.SSHHighState( - opts, pillar, __salt__, - salt.fileclient.get_file_client(__opts__)) + opts, pillar, __salt__, salt.fileclient.get_file_client(__opts__) + ) if isinstance(mods, six.string_types): - mods = mods.split(',') + mods = mods.split(",") high_data, errors = st_.render_highstate({saltenv: mods}) if exclude: if isinstance(exclude, six.string_types): - exclude = exclude.split(',') - if '__exclude__' in high_data: - high_data['__exclude__'].extend(exclude) + exclude = exclude.split(",") + if "__exclude__" in high_data: + high_data["__exclude__"].extend(exclude) else: - high_data['__exclude__'] = exclude + high_data["__exclude__"] = exclude high_data, ext_errors = st_.state.reconcile_extend(high_data) errors += ext_errors @@ -299,16 +300,16 @@ def sls(root, mods, saltenv='base', test=None, exclude=None, **kwargs): file_refs = salt.client.ssh.state.lowstate_file_refs( chunks, salt.client.ssh.wrapper.state._merge_extra_filerefs( - kwargs.get('extra_filerefs', ''), - opts.get('extra_filerefs', ''))) + kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "") + ), + ) - hash_type = opts['hash_type'] - return _create_and_execute_salt_state(root, chunks, file_refs, test, - hash_type) + hash_type = opts["hash_type"] + return _create_and_execute_salt_state(root, chunks, file_refs, test, hash_type) def highstate(root, **kwargs): - ''' + """ Retrieve the state data from the salt master for this minion and execute it inside the chroot. @@ -325,33 +326,33 @@ def highstate(root, **kwargs): salt myminion chroot.highstate /chroot salt myminion chroot.highstate /chroot pillar='{"foo": "bar"}' - ''' + """ # Get a copy of the pillar data, to avoid overwriting the current # pillar, instead the one delegated pillar = copy.deepcopy(__pillar__) - pillar.update(kwargs.get('pillar', {})) + pillar.update(kwargs.get("pillar", {})) # Clone the options data and apply some default values. May not be # needed, as this module just delegate opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) st_ = salt.client.ssh.state.SSHHighState( - opts, pillar, __salt__, - salt.fileclient.get_file_client(__opts__)) + opts, pillar, __salt__, salt.fileclient.get_file_client(__opts__) + ) # Compile and verify the raw chunks chunks = st_.compile_low_chunks() file_refs = salt.client.ssh.state.lowstate_file_refs( chunks, salt.client.ssh.wrapper.state._merge_extra_filerefs( - kwargs.get('extra_filerefs', ''), - opts.get('extra_filerefs', ''))) + kwargs.get("extra_filerefs", ""), opts.get("extra_filerefs", "") + ), + ) # Check for errors for chunk in chunks: if not isinstance(chunk, dict): - __context__['retcode'] = 1 + __context__["retcode"] = 1 return chunks - test = kwargs.pop('test', False) - hash_type = opts['hash_type'] - return _create_and_execute_salt_state(root, chunks, file_refs, test, - hash_type) + test = kwargs.pop("test", False) + hash_type = opts["hash_type"] + return _create_and_execute_salt_state(root, chunks, file_refs, test, hash_type) diff --git a/salt/modules/cimc.py b/salt/modules/cimc.py index bd903db4615..320f2f12d2b 100644 --- a/salt/modules/cimc.py +++ b/salt/modules/cimc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide Cisco UCS compatibility to Salt :codeauthor: ``Spencer Ervin `` @@ -23,37 +23,38 @@ This execution module was designed to handle connections to a Cisco UCS server. This module adds support to send connections directly to the device through the rest API. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.proxy.cimc + # Import Salt Libs import salt.utils.platform -import salt.proxy.cimc log = logging.getLogger(__name__) -__virtualname__ = 'cimc' +__virtualname__ = "cimc" def __virtual__(): - ''' + """ Will load for the cimc proxy minions. - ''' + """ try: - if salt.utils.platform.is_proxy() and \ - __opts__['proxy']['proxytype'] == 'cimc': + if salt.utils.platform.is_proxy() and __opts__["proxy"]["proxytype"] == "cimc": return __virtualname__ except KeyError: pass - return False, 'The cimc execution module can only be loaded for cimc proxy minions.' + return False, "The cimc execution module can only be loaded for cimc proxy minions." def activate_backup_image(reset=False): - ''' + """ Activates the firmware backup image. CLI Example: @@ -66,7 +67,7 @@ def activate_backup_image(reset=False): salt '*' cimc.activate_backup_image salt '*' cimc.activate_backup_image reset=True - ''' + """ dn = "sys/rack-unit-1/mgmt/fw-boot-def/bootunit-combined" @@ -76,15 +77,17 @@ def activate_backup_image(reset=False): r = "yes" inconfig = """""".format(r) + adminState='trigger' image='backup' resetOnActivate='{0}' />""".format( + r + ) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret def create_user(uid=None, username=None, password=None, priv=None): - ''' + """ Create a CIMC user with username and password. Args: @@ -102,7 +105,7 @@ def create_user(uid=None, username=None, password=None, priv=None): salt '*' cimc.create_user 11 username=admin password=foobar priv=admin - ''' + """ if not uid: raise salt.exceptions.CommandExecutionError("The user ID must be specified.") @@ -114,23 +117,24 @@ def create_user(uid=None, username=None, password=None, priv=None): raise salt.exceptions.CommandExecutionError("The password must be specified.") if not priv: - raise salt.exceptions.CommandExecutionError("The privilege level must be specified.") + raise salt.exceptions.CommandExecutionError( + "The privilege level must be specified." + ) dn = "sys/user-ext/user-{0}".format(uid) inconfig = """""".format(uid, - username, - priv, - password) + pwd="{3}" dn="sys/user-ext/user-{0}"/>""".format( + uid, username, priv, password + ) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret def get_bios_defaults(): - ''' + """ Get the default values of BIOS tokens. CLI Example: @@ -139,14 +143,14 @@ def get_bios_defaults(): salt '*' cimc.get_bios_defaults - ''' - ret = __proxy__['cimc.get_config_resolver_class']('biosPlatformDefaults', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("biosPlatformDefaults", True) return ret def get_bios_settings(): - ''' + """ Get the C240 server BIOS token values. CLI Example: @@ -155,14 +159,14 @@ def get_bios_settings(): salt '*' cimc.get_bios_settings - ''' - ret = __proxy__['cimc.get_config_resolver_class']('biosSettings', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("biosSettings", True) return ret def get_boot_order(): - ''' + """ Retrieves the configured boot order table. CLI Example: @@ -171,14 +175,14 @@ def get_boot_order(): salt '*' cimc.get_boot_order - ''' - ret = __proxy__['cimc.get_config_resolver_class']('lsbootDef', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("lsbootDef", True) return ret def get_cpu_details(): - ''' + """ Get the CPU product ID details. CLI Example: @@ -187,14 +191,14 @@ def get_cpu_details(): salt '*' cimc.get_cpu_details - ''' - ret = __proxy__['cimc.get_config_resolver_class']('pidCatalogCpu', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("pidCatalogCpu", True) return ret def get_disks(): - ''' + """ Get the HDD product ID details. CLI Example: @@ -203,14 +207,14 @@ def get_disks(): salt '*' cimc.get_disks - ''' - ret = __proxy__['cimc.get_config_resolver_class']('pidCatalogHdd', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("pidCatalogHdd", True) return ret def get_ethernet_interfaces(): - ''' + """ Get the adapter Ethernet interface details. CLI Example: @@ -219,14 +223,14 @@ def get_ethernet_interfaces(): salt '*' cimc.get_ethernet_interfaces - ''' - ret = __proxy__['cimc.get_config_resolver_class']('adaptorHostEthIf', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("adaptorHostEthIf", True) return ret def get_fibre_channel_interfaces(): - ''' + """ Get the adapter fibre channel interface details. CLI Example: @@ -235,14 +239,14 @@ def get_fibre_channel_interfaces(): salt '*' cimc.get_fibre_channel_interfaces - ''' - ret = __proxy__['cimc.get_config_resolver_class']('adaptorHostFcIf', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("adaptorHostFcIf", True) return ret def get_firmware(): - ''' + """ Retrieves the current running firmware versions of server components. CLI Example: @@ -251,14 +255,14 @@ def get_firmware(): salt '*' cimc.get_firmware - ''' - ret = __proxy__['cimc.get_config_resolver_class']('firmwareRunning', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("firmwareRunning", False) return ret def get_hostname(): - ''' + """ Retrieves the hostname from the device. .. versionadded:: 2019.2.0 @@ -269,17 +273,17 @@ def get_hostname(): salt '*' cimc.get_hostname - ''' - ret = __proxy__['cimc.get_config_resolver_class']('mgmtIf', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("mgmtIf", True) try: - return ret['outConfigs']['mgmtIf'][0]['hostname'] + return ret["outConfigs"]["mgmtIf"][0]["hostname"] except Exception as err: # pylint: disable=broad-except return "Unable to retrieve hostname" def get_ldap(): - ''' + """ Retrieves LDAP server details. CLI Example: @@ -288,14 +292,14 @@ def get_ldap(): salt '*' cimc.get_ldap - ''' - ret = __proxy__['cimc.get_config_resolver_class']('aaaLdap', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("aaaLdap", True) return ret def get_management_interface(): - ''' + """ Retrieve the management interface details. CLI Example: @@ -304,14 +308,14 @@ def get_management_interface(): salt '*' cimc.get_management_interface - ''' - ret = __proxy__['cimc.get_config_resolver_class']('mgmtIf', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("mgmtIf", False) return ret def get_memory_token(): - ''' + """ Get the memory RAS BIOS token. CLI Example: @@ -320,14 +324,16 @@ def get_memory_token(): salt '*' cimc.get_memory_token - ''' - ret = __proxy__['cimc.get_config_resolver_class']('biosVfSelectMemoryRASConfiguration', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]( + "biosVfSelectMemoryRASConfiguration", False + ) return ret def get_memory_unit(): - ''' + """ Get the IMM/Memory unit product ID details. CLI Example: @@ -336,14 +342,14 @@ def get_memory_unit(): salt '*' cimc.get_memory_unit - ''' - ret = __proxy__['cimc.get_config_resolver_class']('pidCatalogDimm', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("pidCatalogDimm", True) return ret def get_network_adapters(): - ''' + """ Get the list of network adapaters and configuration details. CLI Example: @@ -352,14 +358,14 @@ def get_network_adapters(): salt '*' cimc.get_network_adapters - ''' - ret = __proxy__['cimc.get_config_resolver_class']('networkAdapterEthIf', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("networkAdapterEthIf", True) return ret def get_ntp(): - ''' + """ Retrieves the current running NTP configuration. CLI Example: @@ -368,14 +374,14 @@ def get_ntp(): salt '*' cimc.get_ntp - ''' - ret = __proxy__['cimc.get_config_resolver_class']('commNtpProvider', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("commNtpProvider", False) return ret def get_pci_adapters(): - ''' + """ Get the PCI adapter product ID details. CLI Example: @@ -384,14 +390,14 @@ def get_pci_adapters(): salt '*' cimc.get_disks - ''' - ret = __proxy__['cimc.get_config_resolver_class']('pidCatalogPCIAdapter', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("pidCatalogPCIAdapter", True) return ret def get_power_configuration(): - ''' + """ Get the configuration of the power settings from the device. This is only available on some C-Series servers. @@ -403,14 +409,14 @@ def get_power_configuration(): salt '*' cimc.get_power_configuration - ''' - ret = __proxy__['cimc.get_config_resolver_class']('biosVfResumeOnACPowerLoss', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("biosVfResumeOnACPowerLoss", True) return ret def get_power_supplies(): - ''' + """ Retrieves the power supply unit details. CLI Example: @@ -419,14 +425,14 @@ def get_power_supplies(): salt '*' cimc.get_power_supplies - ''' - ret = __proxy__['cimc.get_config_resolver_class']('equipmentPsu', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("equipmentPsu", False) return ret def get_snmp_config(): - ''' + """ Get the snmp configuration details. CLI Example: @@ -435,14 +441,14 @@ def get_snmp_config(): salt '*' cimc.get_snmp_config - ''' - ret = __proxy__['cimc.get_config_resolver_class']('commSnmp', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("commSnmp", False) return ret def get_syslog(): - ''' + """ Get the Syslog client-server details. CLI Example: @@ -451,14 +457,14 @@ def get_syslog(): salt '*' cimc.get_syslog - ''' - ret = __proxy__['cimc.get_config_resolver_class']('commSyslogClient', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("commSyslogClient", False) return ret def get_syslog_settings(): - ''' + """ Get the Syslog configuration settings from the system. .. versionadded:: 2019.2.0 @@ -469,14 +475,14 @@ def get_syslog_settings(): salt '*' cimc.get_syslog_settings - ''' - ret = __proxy__['cimc.get_config_resolver_class']('commSyslog', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("commSyslog", False) return ret def get_system_info(): - ''' + """ Get the system information. CLI Example: @@ -485,14 +491,14 @@ def get_system_info(): salt '*' cimc.get_system_info - ''' - ret = __proxy__['cimc.get_config_resolver_class']('computeRackUnit', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("computeRackUnit", False) return ret def get_users(): - ''' + """ Get the CIMC users. CLI Example: @@ -501,14 +507,14 @@ def get_users(): salt '*' cimc.get_users - ''' - ret = __proxy__['cimc.get_config_resolver_class']('aaaUser', False) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("aaaUser", False) return ret def get_vic_adapters(): - ''' + """ Get the VIC adapter general profile details. CLI Example: @@ -517,14 +523,14 @@ def get_vic_adapters(): salt '*' cimc.get_vic_adapters - ''' - ret = __proxy__['cimc.get_config_resolver_class']('adaptorGenProfile', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("adaptorGenProfile", True) return ret def get_vic_uplinks(): - ''' + """ Get the VIC adapter uplink port details. CLI Example: @@ -533,19 +539,21 @@ def get_vic_uplinks(): salt '*' cimc.get_vic_uplinks - ''' - ret = __proxy__['cimc.get_config_resolver_class']('adaptorExtEthIf', True) + """ + ret = __proxy__["cimc.get_config_resolver_class"]("adaptorExtEthIf", True) return ret -def mount_share(name=None, - remote_share=None, - remote_file=None, - mount_type="nfs", - username=None, - password=None): - ''' +def mount_share( + name=None, + remote_share=None, + remote_file=None, + mount_type="nfs", + username=None, + password=None, +): + """ Mounts a remote file through a remote share. Currently, this feature is supported in version 1.5 or greater. The remote share can be either NFS, CIFS, or WWW. @@ -582,34 +590,42 @@ def mount_share(name=None, salt '*' cimc.mount_share name=WIN7 remote_share=10.xxx.27.xxx:/nfs remote_file=sl1huu.iso username=bob password=badpassword - ''' + """ if not name: raise salt.exceptions.CommandExecutionError("The share name must be specified.") if not remote_share: - raise salt.exceptions.CommandExecutionError("The remote share path must be specified.") + raise salt.exceptions.CommandExecutionError( + "The remote share path must be specified." + ) if not remote_file: - raise salt.exceptions.CommandExecutionError("The remote file name must be specified.") + raise salt.exceptions.CommandExecutionError( + "The remote file name must be specified." + ) if username and password: - mount_options = " mountOptions='username={0},password={1}'".format(username, password) + mount_options = " mountOptions='username={0},password={1}'".format( + username, password + ) else: mount_options = "" - dn = 'sys/svc-ext/vmedia-svc/vmmap-{0}'.format(name) + dn = "sys/svc-ext/vmedia-svc/vmmap-{0}".format(name) inconfig = """""".format(name, mount_type, mount_options, remote_file, remote_share) + volumeName='Win12' />""".format( + name, mount_type, mount_options, remote_file, remote_share + ) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret def reboot(): - ''' + """ Power cycling the server. CLI Example: @@ -618,19 +634,19 @@ def reboot(): salt '*' cimc.reboot - ''' + """ dn = "sys/rack-unit-1" inconfig = """""" - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret def set_hostname(hostname=None): - ''' + """ Sets the hostname on the server. .. versionadded:: 2019.2.0 @@ -644,17 +660,19 @@ def set_hostname(hostname=None): salt '*' cimc.set_hostname foobar - ''' + """ if not hostname: raise salt.exceptions.CommandExecutionError("Hostname option must be provided.") dn = "sys/rack-unit-1/mgmt/if-1" - inconfig = """""".format(hostname) + inconfig = """""".format( + hostname + ) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) try: - if ret['outConfig']['mgmtIf'][0]['status'] == 'modified': + if ret["outConfig"]["mgmtIf"][0]["status"] == "modified": return True else: return False @@ -663,7 +681,7 @@ def set_hostname(hostname=None): def set_logging_levels(remote=None, local=None): - ''' + """ Sets the logging levels of the CIMC devices. The logging levels must match the following options: emergency, alert, critical, error, warning, notice, informational, debug. @@ -681,16 +699,18 @@ def set_logging_levels(remote=None, local=None): salt '*' cimc.set_logging_levels remote=error local=notice - ''' + """ - logging_options = ['emergency', - 'alert', - 'critical', - 'error', - 'warning', - 'notice', - 'informational', - 'debug'] + logging_options = [ + "emergency", + "alert", + "critical", + "error", + "warning", + "notice", + "informational", + "debug", + ] query = "" @@ -698,24 +718,28 @@ def set_logging_levels(remote=None, local=None): if remote in logging_options: query += ' remoteSeverity="{0}"'.format(remote) else: - raise salt.exceptions.CommandExecutionError("Remote Severity option is not valid.") + raise salt.exceptions.CommandExecutionError( + "Remote Severity option is not valid." + ) if local: if local in logging_options: query += ' localSeverity="{0}"'.format(local) else: - raise salt.exceptions.CommandExecutionError("Local Severity option is not valid.") + raise salt.exceptions.CommandExecutionError( + "Local Severity option is not valid." + ) dn = "sys/svc-ext/syslog" inconfig = """""".format(query) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret -def set_ntp_server(server1='', server2='', server3='', server4=''): - ''' +def set_ntp_server(server1="", server2="", server3="", server4=""): + """ Sets the NTP servers configuration. This will also enable the client NTP service. Args: @@ -735,19 +759,21 @@ def set_ntp_server(server1='', server2='', server3='', server4=''): salt '*' cimc.set_ntp_server 10.10.10.1 foo.bar.com - ''' + """ dn = "sys/svc-ext/ntp-svc" inconfig = """""".format(server1, server2, server3, server4) + ntpServer3="{2}" ntpServer4="{3}"/>""".format( + server1, server2, server3, server4 + ) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret def set_power_configuration(policy=None, delayType=None, delayValue=None): - ''' + """ Sets the power configuration on the device. This is only available for some C-Series servers. @@ -786,7 +812,7 @@ def set_power_configuration(policy=None, delayType=None, delayValue=None): salt '*' cimc.set_power_configuration reset fixed 0 - ''' + """ query = "" if policy == "reset": @@ -799,26 +825,32 @@ def set_power_configuration(policy=None, delayType=None, delayValue=None): elif delayType == "random": query += ' delayType="random"' else: - raise salt.exceptions.CommandExecutionError("Invalid delay type entered.") + raise salt.exceptions.CommandExecutionError( + "Invalid delay type entered." + ) elif policy == "stay-off": query = ' vpResumeOnACPowerLoss="reset"' elif policy == "last-state": query = ' vpResumeOnACPowerLoss="last-state"' else: - raise salt.exceptions.CommandExecutionError("The power state must be specified.") + raise salt.exceptions.CommandExecutionError( + "The power state must be specified." + ) dn = "sys/rack-unit-1/board/Resume-on-AC-power-loss" inconfig = """ - """.format(query) + """.format( + query + ) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret def set_syslog_server(server=None, type="primary"): - ''' + """ Set the SYSLOG server on the host. Args: @@ -836,29 +868,37 @@ def set_syslog_server(server=None, type="primary"): salt '*' cimc.set_syslog_server foo.bar.com secondary - ''' + """ if not server: - raise salt.exceptions.CommandExecutionError("The SYSLOG server must be specified.") + raise salt.exceptions.CommandExecutionError( + "The SYSLOG server must be specified." + ) if type == "primary": dn = "sys/svc-ext/syslog/client-primary" inconfig = """ """.format(server) + dn='sys/svc-ext/syslog/client-primary'> """.format( + server + ) elif type == "secondary": dn = "sys/svc-ext/syslog/client-secondary" inconfig = """ """.format(server) + dn='sys/svc-ext/syslog/client-secondary'> """.format( + server + ) else: - raise salt.exceptions.CommandExecutionError("The SYSLOG type must be either primary or secondary.") + raise salt.exceptions.CommandExecutionError( + "The SYSLOG type must be either primary or secondary." + ) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret def set_user(uid=None, username=None, password=None, priv=None, status=None): - ''' + """ Sets a CIMC user with specified configurations. .. versionadded:: 2019.2.0 @@ -880,7 +920,7 @@ def set_user(uid=None, username=None, password=None, priv=None, status=None): salt '*' cimc.set_user 11 username=admin password=foobar priv=admin active - ''' + """ conf = "" if not uid: @@ -900,16 +940,15 @@ def set_user(uid=None, username=None, password=None, priv=None, status=None): dn = "sys/user-ext/user-{0}".format(uid) - inconfig = """""".format(uid, - conf) + inconfig = """""".format(uid, conf) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret def tftp_update_bios(server=None, path=None): - ''' + """ Update the BIOS firmware through TFTP. Args: @@ -923,10 +962,12 @@ def tftp_update_bios(server=None, path=None): salt '*' cimc.tftp_update_bios foo.bar.com HP-SL2.cap - ''' + """ if not server: - raise salt.exceptions.CommandExecutionError("The server name must be specified.") + raise salt.exceptions.CommandExecutionError( + "The server name must be specified." + ) if not path: raise salt.exceptions.CommandExecutionError("The TFTP path must be specified.") @@ -935,15 +976,17 @@ def tftp_update_bios(server=None, path=None): inconfig = """""".format(server, path) + type='blade-bios' />""".format( + server, path + ) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret def tftp_update_cimc(server=None, path=None): - ''' + """ Update the CIMC firmware through TFTP. Args: @@ -957,10 +1000,12 @@ def tftp_update_cimc(server=None, path=None): salt '*' cimc.tftp_update_cimc foo.bar.com HP-SL2.bin - ''' + """ if not server: - raise salt.exceptions.CommandExecutionError("The server name must be specified.") + raise salt.exceptions.CommandExecutionError( + "The server name must be specified." + ) if not path: raise salt.exceptions.CommandExecutionError("The TFTP path must be specified.") @@ -969,8 +1014,10 @@ def tftp_update_cimc(server=None, path=None): inconfig = """""".format(server, path) + type='blade-controller' />""".format( + server, path + ) - ret = __proxy__['cimc.set_config_modify'](dn, inconfig, False) + ret = __proxy__["cimc.set_config_modify"](dn, inconfig, False) return ret diff --git a/salt/modules/ciscoconfparse_mod.py b/salt/modules/ciscoconfparse_mod.py index 2163081218d..747a75c19eb 100644 --- a/salt/modules/ciscoconfparse_mod.py +++ b/salt/modules/ciscoconfparse_mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Execution module for `ciscoconfparse `_ .. versionadded:: 2019.2.0 @@ -18,16 +18,18 @@ See http://www.pennington.net/py/ciscoconfparse/index.html for further details. This module depends on the Python library with the same name, ``ciscoconfparse`` - to install execute: ``pip install ciscoconfparse``. -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +from salt.exceptions import SaltException # Import Salt modules from salt.ext import six -from salt.exceptions import SaltException try: import ciscoconfparse + HAS_CISCOCONFPARSE = True except ImportError: HAS_CISCOCONFPARSE = False @@ -36,7 +38,7 @@ except ImportError: # module properties # ------------------------------------------------------------------------------ -__virtualname__ = 'ciscoconfparse' +__virtualname__ = "ciscoconfparse" # ------------------------------------------------------------------------------ # property functions @@ -44,32 +46,37 @@ __virtualname__ = 'ciscoconfparse' def __virtual__(): - return HAS_CISCOCONFPARSE + if HAS_CISCOCONFPARSE: + return HAS_CISCOCONFPARSE + else: + return (False, "Missing dependency ciscoconfparse") + # ------------------------------------------------------------------------------ # helper functions -- will not be exported # ------------------------------------------------------------------------------ -def _get_ccp(config=None, config_path=None, saltenv='base'): - ''' - ''' +def _get_ccp(config=None, config_path=None, saltenv="base"): + """ + """ if config_path: - config = __salt__['cp.get_file_str'](config_path, saltenv=saltenv) + config = __salt__["cp.get_file_str"](config_path, saltenv=saltenv) if config is False: - raise SaltException('{} is not available'.format(config_path)) + raise SaltException("{} is not available".format(config_path)) if isinstance(config, six.string_types): config = config.splitlines() ccp = ciscoconfparse.CiscoConfParse(config) return ccp + # ------------------------------------------------------------------------------ # callable functions # ------------------------------------------------------------------------------ -def find_objects(config=None, config_path=None, regex=None, saltenv='base'): - ''' +def find_objects(config=None, config_path=None, regex=None, saltenv="base"): + """ Return all the line objects that match the expression in the ``regex`` argument. @@ -105,14 +112,14 @@ def find_objects(config=None, config_path=None, regex=None, saltenv='base'): regex='Gigabit') for obj in objects: print(obj.text) - ''' + """ ccp = _get_ccp(config=config, config_path=config_path, saltenv=saltenv) lines = ccp.find_objects(regex) return lines -def find_lines(config=None, config_path=None, regex=None, saltenv='base'): - ''' +def find_lines(config=None, config_path=None, regex=None, saltenv="base"): + """ Return all the lines (as text) that match the expression in the ``regex`` argument. @@ -148,21 +155,22 @@ def find_lines(config=None, config_path=None, regex=None, saltenv='base'): - ip address dhcp - ip address 172.20.0.1 255.255.255.0 - no ip address - ''' - lines = find_objects(config=config, - config_path=config_path, - regex=regex, - saltenv=saltenv) + """ + lines = find_objects( + config=config, config_path=config_path, regex=regex, saltenv=saltenv + ) return [line.text for line in lines] -def find_objects_w_child(config=None, - config_path=None, - parent_regex=None, - child_regex=None, - ignore_ws=False, - saltenv='base'): - ''' +def find_objects_w_child( + config=None, + config_path=None, + parent_regex=None, + child_regex=None, + ignore_ws=False, + saltenv="base", +): + """ Parse through the children of all parent lines matching ``parent_regex``, and return a list of child objects, which matched the ``child_regex``. @@ -205,19 +213,21 @@ def find_objects_w_child(config=None, child_regex='stopbits') for obj in objects: print(obj.text) - ''' + """ ccp = _get_ccp(config=config, config_path=config_path, saltenv=saltenv) lines = ccp.find_objects_w_child(parent_regex, child_regex, ignore_ws=ignore_ws) return lines -def find_lines_w_child(config=None, - config_path=None, - parent_regex=None, - child_regex=None, - ignore_ws=False, - saltenv='base'): - r''' +def find_lines_w_child( + config=None, + config_path=None, + parent_regex=None, + child_regex=None, + ignore_ws=False, + saltenv="base", +): + r""" Return a list of parent lines (as text) matching the regular expression ``parent_regex`` that have children lines matching ``child_regex``. @@ -251,23 +261,27 @@ def find_lines_w_child(config=None, salt '*' ciscoconfparse.find_lines_w_child config_path=https://bit.ly/2mAdq7z parent_line='line con' child_line='stopbits' salt '*' ciscoconfparse.find_lines_w_child config_path=https://bit.ly/2uIRxau parent_regex='ge-(.*)' child_regex='unit \d+' - ''' - lines = find_objects_w_child(config=config, - config_path=config_path, - parent_regex=parent_regex, - child_regex=child_regex, - ignore_ws=ignore_ws, - saltenv=saltenv) + """ + lines = find_objects_w_child( + config=config, + config_path=config_path, + parent_regex=parent_regex, + child_regex=child_regex, + ignore_ws=ignore_ws, + saltenv=saltenv, + ) return [line.text for line in lines] -def find_objects_wo_child(config=None, - config_path=None, - parent_regex=None, - child_regex=None, - ignore_ws=False, - saltenv='base'): - ''' +def find_objects_wo_child( + config=None, + config_path=None, + parent_regex=None, + child_regex=None, + ignore_ws=False, + saltenv="base", +): + """ Return a list of parent ``ciscoconfparse.IOSCfgLine`` objects, which matched the ``parent_regex`` and whose children did *not* match ``child_regex``. Only the parent ``ciscoconfparse.IOSCfgLine`` objects will be returned. For @@ -313,19 +327,21 @@ def find_objects_wo_child(config=None, child_regex='stopbits') for obj in objects: print(obj.text) - ''' + """ ccp = _get_ccp(config=config, config_path=config_path, saltenv=saltenv) lines = ccp.find_objects_wo_child(parent_regex, child_regex, ignore_ws=ignore_ws) return lines -def find_lines_wo_child(config=None, - config_path=None, - parent_regex=None, - child_regex=None, - ignore_ws=False, - saltenv='base'): - ''' +def find_lines_wo_child( + config=None, + config_path=None, + parent_regex=None, + child_regex=None, + ignore_ws=False, + saltenv="base", +): + """ Return a list of parent ``ciscoconfparse.IOSCfgLine`` lines as text, which matched the ``parent_regex`` and whose children did *not* match ``child_regex``. Only the parent ``ciscoconfparse.IOSCfgLine`` text lines will be returned. @@ -361,22 +377,22 @@ def find_lines_wo_child(config=None, .. code-block:: bash salt '*' ciscoconfparse.find_lines_wo_child config_path=https://bit.ly/2mAdq7z parent_line='line con' child_line='stopbits' - ''' - lines = find_objects_wo_child(config=config, - config_path=config_path, - parent_regex=parent_regex, - child_regex=child_regex, - ignore_ws=ignore_ws, - saltenv=saltenv) + """ + lines = find_objects_wo_child( + config=config, + config_path=config_path, + parent_regex=parent_regex, + child_regex=child_regex, + ignore_ws=ignore_ws, + saltenv=saltenv, + ) return [line.text for line in lines] -def filter_lines(config=None, - config_path=None, - parent_regex=None, - child_regex=None, - saltenv='base'): - ''' +def filter_lines( + config=None, config_path=None, parent_regex=None, child_regex=None, saltenv="base" +): + """ Return a list of detailed matches, for the configuration blocks (parent-child relationship) whose parent respects the regular expressions configured via the ``parent_regex`` argument, and the child matches the ``child_regex`` @@ -421,7 +437,7 @@ def filter_lines(config=None, 'child': ' shutdown' } ] - ''' + """ ret = [] ccp = _get_ccp(config=config, config_path=config_path, saltenv=saltenv) parent_lines = ccp.find_objects(parent_regex) @@ -429,15 +445,13 @@ def filter_lines(config=None, child_lines = parent_line.re_search_children(child_regex) if child_lines: for child_line in child_lines: - ret.append({ - 'match': True, - 'parent': parent_line.text, - 'child': child_line.text - }) + ret.append( + { + "match": True, + "parent": parent_line.text, + "child": child_line.text, + } + ) else: - ret.append({ - 'match': False, - 'parent': parent_line.text, - 'child': None - }) + ret.append({"match": False, "parent": parent_line.text, "child": None}) return ret diff --git a/salt/modules/cisconso.py b/salt/modules/cisconso.py index 14ecc6d049e..8623ee4b9be 100644 --- a/salt/modules/cisconso.py +++ b/salt/modules/cisconso.py @@ -1,41 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" Execution module for Cisco Network Services Orchestrator Proxy minions .. versionadded: 2016.11.0 For documentation on setting up the cisconso proxy minion look in the documentation for :mod:`salt.proxy.cisconso`. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import salt.utils.platform from salt.ext import six -__proxyenabled__ = ['cisconso'] -__virtualname__ = 'cisconso' +__proxyenabled__ = ["cisconso"] +__virtualname__ = "cisconso" def __virtual__(): if salt.utils.platform.is_proxy(): return __virtualname__ - return (False, 'The cisconso execution module failed to load: ' - 'only available on proxy minions.') + return ( + False, + "The cisconso execution module failed to load: " + "only available on proxy minions.", + ) def info(): - ''' + """ Return system information for grains of the NSO proxy minion .. code-block:: bash salt '*' cisconso.info - ''' - return _proxy_cmd('info') + """ + return _proxy_cmd("info") def get_data(datastore, path): - ''' + """ Get the configuration of the device tree at the given path :param datastore: The datastore, e.g. running, operational. @@ -52,14 +55,14 @@ def get_data(datastore, path): .. code-block:: bash salt cisco-nso cisconso.get_data running 'devices/ex0' - ''' + """ if isinstance(path, six.string_types): - path = '/'.split(path) - return _proxy_cmd('get_data', datastore, path) + path = "/".split(path) + return _proxy_cmd("get_data", datastore, path) def set_data_value(datastore, path, data): - ''' + """ Set a data entry in a datastore :param datastore: The datastore, e.g. running, operational. @@ -79,25 +82,25 @@ def set_data_value(datastore, path, data): .. code-block:: bash salt cisco-nso cisconso.set_data_value running 'devices/ex0/routes' 10.0.0.20/24 - ''' + """ if isinstance(path, six.string_types): - path = '/'.split(path) - return _proxy_cmd('set_data_value', datastore, path, data) + path = "/".split(path) + return _proxy_cmd("set_data_value", datastore, path, data) def get_rollbacks(): - ''' + """ Get a list of stored configuration rollbacks .. code-block:: bash salt cisco-nso cisconso.get_rollbacks - ''' - return _proxy_cmd('get_rollbacks') + """ + return _proxy_cmd("get_rollbacks") def get_rollback(name): - ''' + """ Get the backup of stored a configuration rollback :param name: Typically an ID of the backup @@ -109,12 +112,12 @@ def get_rollback(name): .. code-block:: bash salt cisco-nso cisconso.get_rollback 52 - ''' - return _proxy_cmd('get_rollback', name) + """ + return _proxy_cmd("get_rollback", name) def apply_rollback(datastore, name): - ''' + """ Apply a system rollback :param datastore: The datastore, e.g. running, operational. @@ -127,12 +130,12 @@ def apply_rollback(datastore, name): .. code-block:: bash salt cisco-nso cisconso.apply_rollback 52 - ''' - return _proxy_cmd('apply_rollback', datastore, name) + """ + return _proxy_cmd("apply_rollback", datastore, name) def _proxy_cmd(command, *args, **kwargs): - ''' + """ run commands from __proxy__ :mod:`salt.proxy.cisconso` @@ -144,12 +147,12 @@ def _proxy_cmd(command, *args, **kwargs): kwargs key word arguments to pass to `command` function - ''' - proxy_prefix = __opts__['proxy']['proxytype'] - proxy_cmd = '.'.join([proxy_prefix, command]) + """ + proxy_prefix = __opts__["proxy"]["proxytype"] + proxy_cmd = ".".join([proxy_prefix, command]) if proxy_cmd not in __proxy__: return False for k in kwargs: - if k.startswith('__pub_'): + if k.startswith("__pub_"): kwargs.pop(k) return __proxy__[proxy_cmd](*args, **kwargs) diff --git a/salt/modules/cloud.py b/salt/modules/cloud.py index 9464347ada0..ec525ff499b 100644 --- a/salt/modules/cloud.py +++ b/salt/modules/cloud.py @@ -1,56 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" Salt-specific interface for calling Salt Cloud directly -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import logging + import copy +import logging +import os + import salt.utils.data - -# Import salt libs -try: - import salt.cloud - HAS_SALTCLOUD = True -except ImportError: - HAS_SALTCLOUD = False - from salt.exceptions import SaltCloudConfigError # Import 3rd-party libs from salt.ext import six +# Import salt libs +try: + import salt.cloud + + HAS_SALTCLOUD = True +except ImportError: + HAS_SALTCLOUD = False + + log = logging.getLogger(__name__) -__func_alias__ = { - 'profile_': 'profile' -} +__func_alias__ = {"profile_": "profile"} def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ if HAS_SALTCLOUD: return True - return (False, 'The cloud execution module cannot be loaded: only available on non-Windows systems.') + return ( + False, + "The cloud execution module cannot be loaded: only available on non-Windows systems.", + ) def _get_client(): - ''' + """ Return a cloud client - ''' + """ client = salt.cloud.CloudClient( - os.path.join(os.path.dirname(__opts__['conf_file']), 'cloud'), - pillars=copy.deepcopy(__pillar__.get('cloud', {})) + os.path.join(os.path.dirname(__opts__["conf_file"]), "cloud"), + pillars=copy.deepcopy(__pillar__.get("cloud", {})), ) return client -def list_sizes(provider='all'): - ''' +def list_sizes(provider="all"): + """ List cloud provider sizes for the given providers CLI Example: @@ -58,14 +62,14 @@ def list_sizes(provider='all'): .. code-block:: bash salt minionname cloud.list_sizes my-gce-config - ''' + """ client = _get_client() sizes = client.list_sizes(provider) return sizes -def list_images(provider='all'): - ''' +def list_images(provider="all"): + """ List cloud provider images for the given providers CLI Example: @@ -73,14 +77,14 @@ def list_images(provider='all'): .. code-block:: bash salt minionname cloud.list_images my-gce-config - ''' + """ client = _get_client() images = client.list_images(provider) return images -def list_locations(provider='all'): - ''' +def list_locations(provider="all"): + """ List cloud provider locations for the given providers CLI Example: @@ -88,14 +92,14 @@ def list_locations(provider='all'): .. code-block:: bash salt minionname cloud.list_locations my-gce-config - ''' + """ client = _get_client() locations = client.list_locations(provider) return locations -def query(query_type='list_nodes'): - ''' +def query(query_type="list_nodes"): + """ List cloud provider data for all providers CLI Examples: @@ -105,14 +109,14 @@ def query(query_type='list_nodes'): salt minionname cloud.query salt minionname cloud.query list_nodes_full salt minionname cloud.query list_nodes_select - ''' + """ client = _get_client() info = client.query(query_type) return info -def full_query(query_type='list_nodes_full'): - ''' +def full_query(query_type="list_nodes_full"): + """ List all available cloud provider data CLI Example: @@ -120,12 +124,12 @@ def full_query(query_type='list_nodes_full'): .. code-block:: bash salt minionname cloud.full_query - ''' + """ return query(query_type=query_type) -def select_query(query_type='list_nodes_select'): - ''' +def select_query(query_type="list_nodes_select"): + """ List selected nodes CLI Example: @@ -133,12 +137,12 @@ def select_query(query_type='list_nodes_select'): .. code-block:: bash salt minionname cloud.select_query - ''' + """ return query(query_type=query_type) def has_instance(name, provider=None): - ''' + """ Return true if the instance is found on a provider CLI Example: @@ -146,7 +150,7 @@ def has_instance(name, provider=None): .. code-block:: bash salt minionname cloud.has_instance myinstance - ''' + """ data = get_instance(name, provider) if data is None: return False @@ -154,7 +158,7 @@ def has_instance(name, provider=None): def get_instance(name, provider=None): - ''' + """ Return details on an instance. Similar to the cloud action show_instance @@ -172,8 +176,8 @@ def get_instance(name, provider=None): {{ salt['cloud.get_instance']('myinstance')['mac_address'] }} - ''' - data = action(fun='show_instance', names=[name], provider=provider) + """ + data = action(fun="show_instance", names=[name], provider=provider) info = salt.utils.data.simple_types_filter(data) try: # get the first: [alias][driver][vm_name] @@ -184,7 +188,7 @@ def get_instance(name, provider=None): def profile_(profile, names, vm_overrides=None, opts=None, **kwargs): - ''' + """ Spin up an instance using Salt Cloud CLI Example: @@ -192,7 +196,7 @@ def profile_(profile, names, vm_overrides=None, opts=None, **kwargs): .. code-block:: bash salt minionname cloud.profile my-gce-config myinstance - ''' + """ client = _get_client() if isinstance(opts, dict): client.opts.update(opts) @@ -201,7 +205,7 @@ def profile_(profile, names, vm_overrides=None, opts=None, **kwargs): def map_run(path=None, **kwargs): - ''' + """ Execute a salt cloud map file Cloud Map data can be retrieved from several sources: @@ -223,14 +227,14 @@ def map_run(path=None, **kwargs): salt minionname cloud.map_run map_pillar='' .. versionchanged:: 2018.3.1 salt minionname cloud.map_run map_data='' - ''' + """ client = _get_client() info = client.map_run(path, **kwargs) return info def destroy(names): - ''' + """ Destroy the named VM(s) CLI Example: @@ -238,20 +242,14 @@ def destroy(names): .. code-block:: bash salt minionname cloud.destroy myinstance - ''' + """ client = _get_client() info = client.destroy(names) return info -def action( - fun=None, - cloudmap=None, - names=None, - provider=None, - instance=None, - **kwargs): - ''' +def action(fun=None, cloudmap=None, names=None, provider=None, instance=None, **kwargs): + """ Execute a single action on the given provider/instance CLI Example: @@ -261,7 +259,7 @@ def action( salt minionname cloud.action start instance=myinstance salt minionname cloud.action stop instance=myinstance salt minionname cloud.action show_image provider=my-ec2-config image=ami-1624987f - ''' + """ client = _get_client() try: info = client.action(fun, cloudmap, names, provider, instance, kwargs) @@ -273,7 +271,7 @@ def action( def create(provider, names, opts=None, **kwargs): - ''' + """ Create an instance using Salt Cloud CLI Example: @@ -281,7 +279,7 @@ def create(provider, names, opts=None, **kwargs): .. code-block:: bash salt minionname cloud.create my-ec2-config myinstance image=ami-1624987f size='t1.micro' ssh_username=ec2-user securitygroup=default delvol_on_destroy=True - ''' + """ client = _get_client() if isinstance(opts, dict): client.opts.update(opts) @@ -290,7 +288,7 @@ def create(provider, names, opts=None, **kwargs): def volume_list(provider): - ''' + """ List block storage volumes CLI Example: @@ -299,14 +297,14 @@ def volume_list(provider): salt minionname cloud.volume_list my-nova - ''' + """ client = _get_client() - info = client.extra_action(action='volume_list', provider=provider, names='name') - return info['name'] + info = client.extra_action(action="volume_list", provider=provider, names="name") + return info["name"] def volume_delete(provider, names, **kwargs): - ''' + """ Delete volume CLI Example: @@ -315,14 +313,16 @@ def volume_delete(provider, names, **kwargs): salt minionname cloud.volume_delete my-nova myblock - ''' + """ client = _get_client() - info = client.extra_action(provider=provider, names=names, action='volume_delete', **kwargs) + info = client.extra_action( + provider=provider, names=names, action="volume_delete", **kwargs + ) return info def volume_create(provider, names, **kwargs): - ''' + """ Create volume CLI Example: @@ -331,14 +331,16 @@ def volume_create(provider, names, **kwargs): salt minionname cloud.volume_create my-nova myblock size=100 voltype=SSD - ''' + """ client = _get_client() - info = client.extra_action(action='volume_create', names=names, provider=provider, **kwargs) + info = client.extra_action( + action="volume_create", names=names, provider=provider, **kwargs + ) return info def volume_attach(provider, names, **kwargs): - ''' + """ Attach volume to a server CLI Example: @@ -347,14 +349,16 @@ def volume_attach(provider, names, **kwargs): salt minionname cloud.volume_attach my-nova myblock server_name=myserver device='/dev/xvdf' - ''' + """ client = _get_client() - info = client.extra_action(provider=provider, names=names, action='volume_attach', **kwargs) + info = client.extra_action( + provider=provider, names=names, action="volume_attach", **kwargs + ) return info def volume_detach(provider, names, **kwargs): - ''' + """ Detach volume from a server CLI Example: @@ -363,14 +367,16 @@ def volume_detach(provider, names, **kwargs): salt minionname cloud.volume_detach my-nova myblock server_name=myserver - ''' + """ client = _get_client() - info = client.extra_action(provider=provider, names=names, action='volume_detach', **kwargs) + info = client.extra_action( + provider=provider, names=names, action="volume_detach", **kwargs + ) return info def network_list(provider): - ''' + """ List private networks CLI Example: @@ -379,13 +385,13 @@ def network_list(provider): salt minionname cloud.network_list my-nova - ''' + """ client = _get_client() - return client.extra_action(action='network_list', provider=provider, names='names') + return client.extra_action(action="network_list", provider=provider, names="names") def network_create(provider, names, **kwargs): - ''' + """ Create private network CLI Example: @@ -394,13 +400,15 @@ def network_create(provider, names, **kwargs): salt minionname cloud.network_create my-nova names=['salt'] cidr='192.168.100.0/24' - ''' + """ client = _get_client() - return client.extra_action(provider=provider, names=names, action='network_create', **kwargs) + return client.extra_action( + provider=provider, names=names, action="network_create", **kwargs + ) def virtual_interface_list(provider, names, **kwargs): - ''' + """ List virtual interfaces on a server CLI Example: @@ -409,13 +417,15 @@ def virtual_interface_list(provider, names, **kwargs): salt minionname cloud.virtual_interface_list my-nova names=['salt-master'] - ''' + """ client = _get_client() - return client.extra_action(provider=provider, names=names, action='virtual_interface_list', **kwargs) + return client.extra_action( + provider=provider, names=names, action="virtual_interface_list", **kwargs + ) def virtual_interface_create(provider, names, **kwargs): - ''' + """ Attach private interfaces to a server CLI Example: @@ -424,6 +434,8 @@ def virtual_interface_create(provider, names, **kwargs): salt minionname cloud.virtual_interface_create my-nova names=['salt-master'] net_name='salt' - ''' + """ client = _get_client() - return client.extra_action(provider=provider, names=names, action='virtual_interface_create', **kwargs) + return client.extra_action( + provider=provider, names=names, action="virtual_interface_create", **kwargs + ) diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py index 8cc7d2699a1..b4343db04b7 100644 --- a/salt/modules/cmdmod.py +++ b/salt/modules/cmdmod.py @@ -1,26 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" A module for shelling out. Keep in mind that this module is insecure, in that it can give whomever has access to the master root execution access to all salt minions. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import base64 +import fnmatch + # Import python libs import functools import glob import logging import os +import re import shutil import subprocess import sys +import tempfile import time import traceback -import fnmatch -import base64 -import re -import tempfile + +import salt.grains.extra # Import salt libs import salt.utils.args @@ -38,12 +41,14 @@ import salt.utils.versions import salt.utils.vt import salt.utils.win_dacl import salt.utils.win_reg -import salt.grains.extra +from salt.exceptions import ( + CommandExecutionError, + SaltInvocationError, + TimedProcTimeoutError, +) from salt.ext import six -from salt.exceptions import CommandExecutionError, TimedProcTimeoutError, \ - SaltInvocationError +from salt.ext.six.moves import map, range, zip from salt.log import LOG_LEVELS -from salt.ext.six.moves import range, zip, map # Only available on POSIX systems, nonfatal on windows try: @@ -55,19 +60,21 @@ except ImportError: if salt.utils.platform.is_windows(): from salt.utils.win_runas import runas as win_runas from salt.utils.win_functions import escape_argument as _cmd_quote + HAS_WIN_RUNAS = True else: from salt.ext.six.moves import shlex_quote as _cmd_quote + HAS_WIN_RUNAS = False -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] # Define the module's virtual name -__virtualname__ = 'cmd' +__virtualname__ = "cmd" # Set up logging log = logging.getLogger(__name__) -DEFAULT_SHELL = salt.grains.extra.shell()['shell'] +DEFAULT_SHELL = salt.grains.extra.shell()["shell"] # Overwriting the cmd python module makes debugging modules with pdb a bit @@ -77,28 +84,28 @@ def __virtual__(): def _check_cb(cb_): - ''' + """ If the callback is None or is not callable, return a lambda that returns the value passed. - ''' + """ if cb_ is not None: - if hasattr(cb_, '__call__'): + if hasattr(cb_, "__call__"): return cb_ else: - log.error('log_callback is not callable, ignoring') + log.error("log_callback is not callable, ignoring") return lambda x: x def _python_shell_default(python_shell, __pub_jid): - ''' + """ Set python_shell default based on remote execution and __opts__['cmd_safe'] - ''' + """ try: # Default to python_shell=True when run directly from remote execution # system. Cross-module calls won't have a jid. if __pub_jid and python_shell is None: return True - elif __opts__.get('cmd_safe', True) is False and python_shell is None: + elif __opts__.get("cmd_safe", True) is False and python_shell is None: # Override-switch for python_shell return True except NameError: @@ -108,87 +115,84 @@ def _python_shell_default(python_shell, __pub_jid): def _chroot_pids(chroot): pids = [] - for root in glob.glob('/proc/[0-9]*/root'): + for root in glob.glob("/proc/[0-9]*/root"): try: link = os.path.realpath(root) if link.startswith(chroot): - pids.append(int(os.path.basename( - os.path.dirname(root) - ))) + pids.append(int(os.path.basename(os.path.dirname(root)))) except OSError: pass return pids -def _render_cmd(cmd, cwd, template, saltenv='base', pillarenv=None, pillar_override=None): - ''' +def _render_cmd( + cmd, cwd, template, saltenv="base", pillarenv=None, pillar_override=None +): + """ If template is a valid template engine, process the cmd and cwd through that engine. - ''' + """ if not template: return (cmd, cwd) # render the path as a template using path_template_engine as the engine if template not in salt.utils.templates.TEMPLATE_REGISTRY: raise CommandExecutionError( - 'Attempted to render file paths with unavailable engine ' - '{0}'.format(template) + "Attempted to render file paths with unavailable engine " + "{0}".format(template) ) kwargs = {} - kwargs['salt'] = __salt__ + kwargs["salt"] = __salt__ if pillarenv is not None or pillar_override is not None: - pillarenv = pillarenv or __opts__['pillarenv'] - kwargs['pillar'] = _gather_pillar(pillarenv, pillar_override) + pillarenv = pillarenv or __opts__["pillarenv"] + kwargs["pillar"] = _gather_pillar(pillarenv, pillar_override) else: - kwargs['pillar'] = __pillar__ - kwargs['grains'] = __grains__ - kwargs['opts'] = __opts__ - kwargs['saltenv'] = saltenv + kwargs["pillar"] = __pillar__ + kwargs["grains"] = __grains__ + kwargs["opts"] = __opts__ + kwargs["saltenv"] = saltenv def _render(contents): # write out path to temp file tmp_path_fn = salt.utils.files.mkstemp() - with salt.utils.files.fopen(tmp_path_fn, 'w+') as fp_: + with salt.utils.files.fopen(tmp_path_fn, "w+") as fp_: fp_.write(salt.utils.stringutils.to_str(contents)) data = salt.utils.templates.TEMPLATE_REGISTRY[template]( - tmp_path_fn, - to_str=True, - **kwargs + tmp_path_fn, to_str=True, **kwargs ) salt.utils.files.safe_rm(tmp_path_fn) - if not data['result']: + if not data["result"]: # Failed to render the template raise CommandExecutionError( - 'Failed to execute cmd with error: {0}'.format( - data['data'] - ) + "Failed to execute cmd with error: {0}".format(data["data"]) ) else: - return data['data'] + return data["data"] cmd = _render(cmd) cwd = _render(cwd) return (cmd, cwd) -def _check_loglevel(level='info'): - ''' +def _check_loglevel(level="info"): + """ Retrieve the level code for use in logging.Logger.log(). - ''' + """ try: level = level.lower() - if level == 'quiet': + if level == "quiet": return None else: return LOG_LEVELS[level] except (AttributeError, KeyError): log.error( - 'Invalid output_loglevel \'%s\'. Valid levels are: %s. Falling ' - 'back to \'info\'.', - level, ', '.join(sorted(LOG_LEVELS, reverse=True)) + "Invalid output_loglevel '%s'. Valid levels are: %s. Falling " + "back to 'info'.", + level, + ", ".join(sorted(LOG_LEVELS, reverse=True)), ) - return LOG_LEVELS['info'] + return LOG_LEVELS["info"] def _parse_env(env): @@ -202,16 +206,16 @@ def _parse_env(env): def _gather_pillar(pillarenv, pillar_override): - ''' + """ Whenever a state run starts, gather the pillar data fresh - ''' + """ pillar = salt.pillar.get_pillar( __opts__, __grains__, - __opts__['id'], - __opts__['saltenv'], + __opts__["id"], + __opts__["saltenv"], pillar_override=pillar_override, - pillarenv=pillarenv + pillarenv=pillarenv, ) ret = pillar.compile_pillar() if pillar_override and isinstance(pillar_override, dict): @@ -220,22 +224,26 @@ def _gather_pillar(pillarenv, pillar_override): def _check_avail(cmd): - ''' + """ Check to see if the given command can be run - ''' + """ if isinstance(cmd, list): - cmd = ' '.join([six.text_type(x) if not isinstance(x, six.string_types) else x - for x in cmd]) + cmd = " ".join( + [ + six.text_type(x) if not isinstance(x, six.string_types) else x + for x in cmd + ] + ) bret = True wret = False - if __salt__['config.get']('cmd_blacklist_glob'): - blist = __salt__['config.get']('cmd_blacklist_glob', []) + if __salt__["config.get"]("cmd_blacklist_glob"): + blist = __salt__["config.get"]("cmd_blacklist_glob", []) for comp in blist: if fnmatch.fnmatch(cmd, comp): # BAD! you are blacklisted bret = False - if __salt__['config.get']('cmd_whitelist_glob', []): - blist = __salt__['config.get']('cmd_whitelist_glob', []) + if __salt__["config.get"]("cmd_whitelist_glob", []): + blist = __salt__["config.get"]("cmd_whitelist_glob", []) for comp in blist: if fnmatch.fnmatch(cmd, comp): # GOOD! You are whitelisted @@ -247,71 +255,73 @@ def _check_avail(cmd): return bret and wret -def _run(cmd, - cwd=None, - stdin=None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - output_encoding=None, - output_loglevel='debug', - log_callback=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=False, - env=None, - clean_env=False, - prepend_path=None, - rstrip=True, - template=None, - umask=None, - timeout=None, - with_communicate=True, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - pillarenv=None, - pillar_override=None, - use_vt=False, - password=None, - bg=False, - encoded_cmd=False, - success_retcodes=None, - **kwargs): - ''' +def _run( + cmd, + cwd=None, + stdin=None, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + output_encoding=None, + output_loglevel="debug", + log_callback=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=False, + env=None, + clean_env=False, + prepend_path=None, + rstrip=True, + template=None, + umask=None, + timeout=None, + with_communicate=True, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + pillarenv=None, + pillar_override=None, + use_vt=False, + password=None, + bg=False, + encoded_cmd=False, + success_retcodes=None, + **kwargs +): + """ Do the DRY thing and only call subprocess.Popen() once - ''' - if 'pillar' in kwargs and not pillar_override: - pillar_override = kwargs['pillar'] - if output_loglevel != 'quiet' and _is_valid_shell(shell) is False: + """ + if "pillar" in kwargs and not pillar_override: + pillar_override = kwargs["pillar"] + if output_loglevel != "quiet" and _is_valid_shell(shell) is False: log.warning( - 'Attempt to run a shell command with what may be an invalid shell! ' - 'Check to ensure that the shell <%s> is valid for this user.', - shell + "Attempt to run a shell command with what may be an invalid shell! " + "Check to ensure that the shell <%s> is valid for this user.", + shell, ) output_loglevel = _check_loglevel(output_loglevel) log_callback = _check_cb(log_callback) use_sudo = False - if runas is None and '__context__' in globals(): - runas = __context__.get('runas') + if runas is None and "__context__" in globals(): + runas = __context__.get("runas") - if password is None and '__context__' in globals(): - password = __context__.get('runas_password') + if password is None and "__context__" in globals(): + password = __context__.get("runas_password") # Set the default working directory to the home directory of the user # salt-minion is running as. Defaults to home directory of user under which # the minion is running. if not cwd: - cwd = os.path.expanduser('~{0}'.format('' if not runas else runas)) + cwd = os.path.expanduser("~{0}".format("" if not runas else runas)) # make sure we can access the cwd # when run from sudo or another environment where the euid is # changed ~ will expand to the home of the original uid and # the euid might not have access to it. See issue #1844 if not os.access(cwd, os.R_OK): - cwd = '/' + cwd = "/" if salt.utils.platform.is_windows(): cwd = os.path.abspath(os.sep) else: @@ -325,12 +335,12 @@ def _run(cmd, if not salt.utils.platform.is_windows(): if not os.path.isfile(shell) or not os.access(shell, os.X_OK): - msg = 'The shell {0} is not available'.format(shell) + msg = "The shell {0} is not available".format(shell) raise CommandExecutionError(msg) if salt.utils.platform.is_windows() and use_vt: # Memozation so not much overhead - raise CommandExecutionError('VT not available on windows') + raise CommandExecutionError("VT not available on windows") - if shell.lower().strip() == 'powershell': + if shell.lower().strip() == "powershell": # Strip whitespace if isinstance(cmd, six.string_types): cmd = cmd.strip() @@ -343,12 +353,16 @@ def _run(cmd, # extract_stack() returns a list of tuples. # The last item in the list [-1] is the current method. # The third item[2] in each tuple is the name of that method. - if stack[-2][2] == 'script': - cmd = 'Powershell -NonInteractive -NoProfile -ExecutionPolicy Bypass {0}'.format(cmd.replace('"', '\\"')) + if stack[-2][2] == "script": + cmd = "Powershell -NonInteractive -NoProfile -ExecutionPolicy Bypass {0}".format( + cmd.replace('"', '\\"') + ) elif encoded_cmd: - cmd = 'Powershell -NonInteractive -EncodedCommand {0}'.format(cmd) + cmd = "Powershell -NonInteractive -EncodedCommand {0}".format(cmd) else: - cmd = 'Powershell -NonInteractive -NoProfile "{0}"'.format(cmd.replace('"', '\\"')) + cmd = 'Powershell -NonInteractive -NoProfile "{0}"'.format( + cmd.replace('"', '\\"') + ) # munge the cmd and cwd through the template (cmd, cwd) = _render_cmd(cmd, cwd, template, saltenv, pillarenv, pillar_override) @@ -357,7 +371,7 @@ def _run(cmd, # If the pub jid is here then this is a remote ex or salt call command and needs to be # checked if blacklisted - if '__pub_jid' in kwargs: + if "__pub_jid" in kwargs: if not _check_avail(cmd): raise CommandExecutionError( 'The shell command "{0}" is not permitted'.format(cmd) @@ -366,9 +380,12 @@ def _run(cmd, env = _parse_env(env) for bad_env_key in (x for x, y in six.iteritems(env) if y is None): - log.error('Environment variable \'%s\' passed without a value. ' - 'Setting value to an empty string', bad_env_key) - env[bad_env_key] = '' + log.error( + "Environment variable '%s' passed without a value. " + "Setting value to an empty string", + bad_env_key, + ) + env[bad_env_key] = "" def _get_stripped(cmd): # Return stripped command string copies to improve logging. @@ -383,26 +400,25 @@ def _run(cmd, # 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 {0}{1}{0} {2}{3}in directory \'{4}\'{5}'.format( - '\'' if not isinstance(cmd, list) else '', - _get_stripped(cmd), - 'as user \'{0}\' '.format(runas) if runas else '', - 'in group \'{0}\' '.format(group) if group else '', - cwd, - '. Executing command in the background, no output will be ' - 'logged.' if bg else '' - ) + msg = "Executing command {0}{1}{0} {2}{3}in directory '{4}'{5}".format( + "'" if not isinstance(cmd, list) else "", + _get_stripped(cmd), + "as user '{0}' ".format(runas) if runas else "", + "in group '{0}' ".format(group) if group else "", + cwd, + ". Executing command in the background, no output will be " "logged." + if bg + else "", ) log.info(log_callback(msg)) if runas and salt.utils.platform.is_windows(): if not HAS_WIN_RUNAS: - msg = 'missing salt/utils/win_runas.py' + msg = "missing salt/utils/win_runas.py" raise CommandExecutionError(msg) if isinstance(cmd, (list, tuple)): - cmd = ' '.join(cmd) + cmd = " ".join(cmd) return win_runas(cmd, runas, password, cwd) @@ -411,25 +427,26 @@ def _run(cmd, # just run it from the environment on macOS as that method doesn't work # properly when run as root for certain commands. if isinstance(cmd, (list, tuple)): - cmd = ' '.join(map(_cmd_quote, cmd)) + cmd = " ".join(map(_cmd_quote, cmd)) # Ensure directory is correct before running command - cmd = 'cd -- {dir} && {{ {cmd}\n }}'.format(dir=_cmd_quote(cwd), cmd=cmd) + cmd = "cd -- {dir} && {{ {cmd}\n }}".format(dir=_cmd_quote(cwd), cmd=cmd) # Ensure environment is correct for a newly logged-in user by running # the command under bash as a login shell try: - user_shell = __salt__['user.info'](runas)['shell'] - if re.search('bash$', user_shell): - cmd = '{shell} -l -c {cmd}'.format(shell=user_shell, - cmd=_cmd_quote(cmd)) + user_shell = __salt__["user.info"](runas)["shell"] + if re.search("bash$", user_shell): + cmd = "{shell} -l -c {cmd}".format( + shell=user_shell, cmd=_cmd_quote(cmd) + ) except KeyError: pass # Ensure the login is simulated correctly (note: su runs sh, not bash, # which causes the environment to be initialised incorrectly, which is # fixed by the previous line of code) - cmd = 'su -l {0} -c {1}'.format(_cmd_quote(runas), _cmd_quote(cmd)) + cmd = "su -l {0} -c {1}".format(_cmd_quote(runas), _cmd_quote(cmd)) # Set runas to None, because if you try to run `su -l` after changing # user, su will prompt for the password of the user and cause salt to @@ -441,23 +458,19 @@ def _run(cmd, try: pwd.getpwnam(runas) except KeyError: - raise CommandExecutionError( - 'User \'{0}\' is not available'.format(runas) - ) + raise CommandExecutionError("User '{0}' is not available".format(runas)) if group: if salt.utils.platform.is_windows(): - msg = 'group is not currently available on Windows' + msg = "group is not currently available on Windows" raise SaltInvocationError(msg) - if not which_bin(['sudo']): - msg = 'group argument requires sudo but not found' + if not which_bin(["sudo"]): + msg = "group argument requires sudo but not found" raise CommandExecutionError(msg) try: grp.getgrnam(group) except KeyError: - raise CommandExecutionError( - 'Group \'{0}\' is not available'.format(runas) - ) + raise CommandExecutionError("Group '{0}' is not available".format(runas)) else: use_sudo = True @@ -467,61 +480,68 @@ def _run(cmd, # Use markers to thwart any stdout noise # There must be a better way to do this. import uuid - marker = '<<<' + str(uuid.uuid4()) + '>>>' + + marker = "<<<" + str(uuid.uuid4()) + ">>>" marker_b = marker.encode(__salt_system_encoding__) py_code = ( - 'import sys, os, itertools; ' - 'sys.stdout.write(\"' + marker + '\"); ' - 'sys.stdout.write(\"\\0\".join(itertools.chain(*os.environ.items()))); ' - 'sys.stdout.write(\"' + marker + '\");' + "import sys, os, itertools; " + 'sys.stdout.write("' + marker + '"); ' + 'sys.stdout.write("\\0".join(itertools.chain(*os.environ.items()))); ' + 'sys.stdout.write("' + marker + '");' ) if use_sudo: - env_cmd = ['sudo'] + env_cmd = ["sudo"] # runas is optional if use_sudo is set. if runas: - env_cmd.extend(['-u', runas]) + env_cmd.extend(["-u", runas]) if group: - env_cmd.extend(['-g', group]) + env_cmd.extend(["-g", group]) if shell != DEFAULT_SHELL: - env_cmd.extend(['-s', '--', shell, '-c']) + env_cmd.extend(["-s", "--", shell, "-c"]) else: - env_cmd.extend(['-i', '--']) + env_cmd.extend(["-i", "--"]) env_cmd.extend([sys.executable]) - elif __grains__['os'] in ['FreeBSD']: - env_cmd = ('su', '-', runas, '-c', - "{0} -c {1}".format(shell, sys.executable)) - elif __grains__['os_family'] in ['Solaris']: - env_cmd = ('su', '-', runas, '-c', sys.executable) - elif __grains__['os_family'] in ['AIX']: - env_cmd = ('su', '-', runas, '-c', sys.executable) + elif __grains__["os"] in ["FreeBSD"]: + env_cmd = ( + "su", + "-", + runas, + "-c", + "{0} -c {1}".format(shell, sys.executable), + ) + elif __grains__["os_family"] in ["Solaris"]: + env_cmd = ("su", "-", runas, "-c", sys.executable) + elif __grains__["os_family"] in ["AIX"]: + env_cmd = ("su", "-", runas, "-c", sys.executable) else: - env_cmd = ('su', '-s', shell, '-', runas, '-c', sys.executable) - msg = 'env command: {0}'.format(env_cmd) + env_cmd = ("su", "-s", shell, "-", runas, "-c", sys.executable) + msg = "env command: {0}".format(env_cmd) log.debug(log_callback(msg)) env_bytes, env_encoded_err = subprocess.Popen( env_cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, - stdin=subprocess.PIPE + stdin=subprocess.PIPE, ).communicate(salt.utils.stringutils.to_bytes(py_code)) marker_count = env_bytes.count(marker_b) if marker_count == 0: # Possibly PAM prevented the login log.error( - 'Environment could not be retrieved for user \'%s\': ' - 'stderr=%r stdout=%r', - runas, env_encoded_err, env_bytes + "Environment could not be retrieved for user '%s': " + "stderr=%r stdout=%r", + runas, + env_encoded_err, + env_bytes, ) # Ensure that we get an empty env_runas dict below since we # were not able to get the environment. - env_bytes = b'' + env_bytes = b"" elif marker_count != 2: raise CommandExecutionError( - 'Environment could not be retrieved for user \'{0}\'', - info={'stderr': repr(env_encoded_err), - 'stdout': repr(env_bytes)} + "Environment could not be retrieved for user '{0}'", + info={"stderr": repr(env_encoded_err), "stdout": repr(env_bytes)}, ) else: # Strip the marker @@ -529,33 +549,33 @@ def _run(cmd, if six.PY2: import itertools - env_runas = dict(itertools.izip(*[iter(env_bytes.split(b'\0'))]*2)) + + env_runas = dict(itertools.izip(*[iter(env_bytes.split(b"\0"))] * 2)) elif six.PY3: - env_runas = dict(list(zip(*[iter(env_bytes.split(b'\0'))]*2))) + env_runas = dict(list(zip(*[iter(env_bytes.split(b"\0"))] * 2))) env_runas = dict( - (salt.utils.stringutils.to_str(k), - salt.utils.stringutils.to_str(v)) + (salt.utils.stringutils.to_str(k), salt.utils.stringutils.to_str(v)) for k, v in six.iteritems(env_runas) ) env_runas.update(env) # Fix platforms like Solaris that don't set a USER env var in the # user's default environment as obtained above. - if env_runas.get('USER') != runas: - env_runas['USER'] = runas + if env_runas.get("USER") != runas: + env_runas["USER"] = runas # Fix some corner cases where shelling out to get the user's # environment returns the wrong home directory. - runas_home = os.path.expanduser('~{0}'.format(runas)) - if env_runas.get('HOME') != runas_home: - env_runas['HOME'] = runas_home + runas_home = os.path.expanduser("~{0}".format(runas)) + if env_runas.get("HOME") != runas_home: + env_runas["HOME"] = runas_home env = env_runas except ValueError as exc: - log.exception('Error raised retrieving environment for user %s', runas) + log.exception("Error raised retrieving environment for user %s", runas) raise CommandExecutionError( - 'Environment could not be retrieved for user \'{0}\': {1}'.format( + "Environment could not be retrieved for user '{0}': {1}".format( runas, exc ) ) @@ -565,23 +585,23 @@ def _run(cmd, # Default to C! # Salt only knows how to parse English words # Don't override if the user has passed LC_ALL - env.setdefault('LC_CTYPE', 'C') - env.setdefault('LC_NUMERIC', 'C') - env.setdefault('LC_TIME', 'C') - env.setdefault('LC_COLLATE', 'C') - env.setdefault('LC_MONETARY', 'C') - env.setdefault('LC_MESSAGES', 'C') - env.setdefault('LC_PAPER', 'C') - env.setdefault('LC_NAME', 'C') - env.setdefault('LC_ADDRESS', 'C') - env.setdefault('LC_TELEPHONE', 'C') - env.setdefault('LC_MEASUREMENT', 'C') - env.setdefault('LC_IDENTIFICATION', 'C') - env.setdefault('LANGUAGE', 'C') + env.setdefault("LC_CTYPE", "C") + env.setdefault("LC_NUMERIC", "C") + env.setdefault("LC_TIME", "C") + env.setdefault("LC_COLLATE", "C") + env.setdefault("LC_MONETARY", "C") + env.setdefault("LC_MESSAGES", "C") + env.setdefault("LC_PAPER", "C") + env.setdefault("LC_NAME", "C") + env.setdefault("LC_ADDRESS", "C") + env.setdefault("LC_TELEPHONE", "C") + env.setdefault("LC_MEASUREMENT", "C") + env.setdefault("LC_IDENTIFICATION", "C") + env.setdefault("LANGUAGE", "C") else: # On Windows set the codepage to US English. if python_shell: - cmd = 'chcp 437 > nul & ' + cmd + cmd = "chcp 437 > nul & " + cmd if clean_env: run_env = env @@ -589,36 +609,38 @@ def _run(cmd, else: if salt.utils.platform.is_windows(): import nt + run_env = nt.environ.copy() else: run_env = os.environ.copy() run_env.update(env) if prepend_path: - run_env['PATH'] = ':'.join((prepend_path, run_env['PATH'])) + run_env["PATH"] = ":".join((prepend_path, run_env["PATH"])) if python_shell is None: python_shell = False - new_kwargs = {'cwd': cwd, - 'shell': python_shell, - 'env': run_env if six.PY3 else salt.utils.data.encode(run_env), - 'stdin': six.text_type(stdin) if stdin is not None else stdin, - 'stdout': stdout, - 'stderr': stderr, - 'with_communicate': with_communicate, - 'timeout': timeout, - 'bg': bg, - } + new_kwargs = { + "cwd": cwd, + "shell": python_shell, + "env": run_env if six.PY3 else salt.utils.data.encode(run_env), + "stdin": six.text_type(stdin) if stdin is not None else stdin, + "stdout": stdout, + "stderr": stderr, + "with_communicate": with_communicate, + "timeout": timeout, + "bg": bg, + } - if 'stdin_raw_newlines' in kwargs: - new_kwargs['stdin_raw_newlines'] = kwargs['stdin_raw_newlines'] + if "stdin_raw_newlines" in kwargs: + new_kwargs["stdin_raw_newlines"] = kwargs["stdin_raw_newlines"] if umask is not None: - _umask = six.text_type(umask).lstrip('0') + _umask = six.text_type(umask).lstrip("0") - if _umask == '': - msg = 'Zero umask is not allowed.' + if _umask == "": + msg = "Zero umask is not allowed." raise CommandExecutionError(msg) try: @@ -629,53 +651,47 @@ def _run(cmd, _umask = None if runas or group or umask: - new_kwargs['preexec_fn'] = functools.partial( - salt.utils.user.chugid_and_umask, - runas, - _umask, - group) + new_kwargs["preexec_fn"] = functools.partial( + salt.utils.user.chugid_and_umask, runas, _umask, group + ) if not salt.utils.platform.is_windows(): # close_fds is not supported on Windows platforms if you redirect # stdin/stdout/stderr - if new_kwargs['shell'] is True: - new_kwargs['executable'] = shell - new_kwargs['close_fds'] = True + if new_kwargs["shell"] is True: + new_kwargs["executable"] = shell + new_kwargs["close_fds"] = True if not os.path.isabs(cwd) or not os.path.isdir(cwd): raise CommandExecutionError( - 'Specified cwd \'{0}\' either not absolute or does not exist' - .format(cwd) + "Specified cwd '{0}' either not absolute or does not exist".format(cwd) ) - if python_shell is not True \ - and not salt.utils.platform.is_windows() \ - and not isinstance(cmd, list): + if ( + python_shell is not True + and not salt.utils.platform.is_windows() + and not isinstance(cmd, list) + ): cmd = salt.utils.args.shlex_split(cmd) if success_retcodes is None: success_retcodes = [0] else: try: - success_retcodes = [int(i) for i in - salt.utils.args.split_input( - success_retcodes - )] + success_retcodes = [ + int(i) for i in salt.utils.args.split_input(success_retcodes) + ] except ValueError: - raise SaltInvocationError( - 'success_retcodes must be a list of integers' - ) + raise SaltInvocationError("success_retcodes must be a list of integers") if not use_vt: # This is where the magic happens try: proc = salt.utils.timed_subprocess.TimedProc(cmd, **new_kwargs) except (OSError, IOError) as exc: msg = ( - 'Unable to run command \'{0}\' with the context \'{1}\', ' - 'reason: {2}'.format( - cmd if output_loglevel is not None else 'REDACTED', - new_kwargs, - exc + "Unable to run command '{0}' with the context '{1}', " + "reason: {2}".format( + cmd if output_loglevel is not None else "REDACTED", new_kwargs, exc ) ) raise CommandExecutionError(msg) @@ -683,52 +699,55 @@ def _run(cmd, try: proc.run() except TimedProcTimeoutError as exc: - ret['stdout'] = six.text_type(exc) - ret['stderr'] = '' - ret['retcode'] = None - ret['pid'] = proc.process.pid + ret["stdout"] = six.text_type(exc) + ret["stderr"] = "" + ret["retcode"] = None + ret["pid"] = proc.process.pid # ok return code for timeouts? - ret['retcode'] = 1 + ret["retcode"] = 1 return ret - if output_loglevel != 'quiet' and output_encoding is not None: - log.debug('Decoding output from command %s using %s encoding', - cmd, output_encoding) + if output_loglevel != "quiet" and output_encoding is not None: + log.debug( + "Decoding output from command %s using %s encoding", + cmd, + output_encoding, + ) try: out = salt.utils.stringutils.to_unicode( - proc.stdout, - encoding=output_encoding) + proc.stdout, encoding=output_encoding + ) except TypeError: # stdout is None - out = '' + out = "" except UnicodeDecodeError: out = salt.utils.stringutils.to_unicode( - proc.stdout, - encoding=output_encoding, - errors='replace') - if output_loglevel != 'quiet': + proc.stdout, encoding=output_encoding, errors="replace" + ) + if output_loglevel != "quiet": log.error( - 'Failed to decode stdout from command %s, non-decodable ' - 'characters have been replaced', cmd + "Failed to decode stdout from command %s, non-decodable " + "characters have been replaced", + cmd, ) try: err = salt.utils.stringutils.to_unicode( - proc.stderr, - encoding=output_encoding) + proc.stderr, encoding=output_encoding + ) except TypeError: # stderr is None - err = '' + err = "" except UnicodeDecodeError: err = salt.utils.stringutils.to_unicode( - proc.stderr, - encoding=output_encoding, - errors='replace') - if output_loglevel != 'quiet': + proc.stderr, encoding=output_encoding, errors="replace" + ) + if output_loglevel != "quiet": log.error( - 'Failed to decode stderr from command %s, non-decodable ' - 'characters have been replaced', cmd + "Failed to decode stderr from command %s, non-decodable " + "characters have been replaced", + cmd, ) if rstrip: @@ -736,20 +755,20 @@ def _run(cmd, out = out.rstrip() if err is not None: err = err.rstrip() - ret['pid'] = proc.process.pid - ret['retcode'] = proc.process.returncode - if ret['retcode'] in success_retcodes: - ret['retcode'] = 0 - ret['stdout'] = out - ret['stderr'] = err + ret["pid"] = proc.process.pid + ret["retcode"] = proc.process.returncode + if ret["retcode"] in success_retcodes: + ret["retcode"] = 0 + ret["stdout"] = out + ret["stderr"] = err else: - formatted_timeout = '' + formatted_timeout = "" if timeout: - formatted_timeout = ' (timeout: {0}s)'.format(timeout) + formatted_timeout = " (timeout: {0}s)".format(timeout) if output_loglevel is not None: - msg = 'Running {0} in VT{1}'.format(cmd, formatted_timeout) + msg = "Running {0} in VT{1}".format(cmd, formatted_timeout) log.debug(log_callback(msg)) - stdout, stderr = '', '' + stdout, stderr = "", "" now = time.time() if timeout: will_timeout = now + timeout @@ -757,20 +776,20 @@ def _run(cmd, will_timeout = -1 try: proc = salt.utils.vt.Terminal( - cmd, - shell=True, - log_stdout=True, - log_stderr=True, - cwd=cwd, - preexec_fn=new_kwargs.get('preexec_fn', None), - env=run_env, - log_stdin_level=output_loglevel, - log_stdout_level=output_loglevel, - log_stderr_level=output_loglevel, - stream_stdout=True, - stream_stderr=True + cmd, + shell=True, + log_stdout=True, + log_stderr=True, + cwd=cwd, + preexec_fn=new_kwargs.get("preexec_fn", None), + env=run_env, + log_stdin_level=output_loglevel, + log_stdout_level=output_loglevel, + log_stderr_level=output_loglevel, + stream_stdout=True, + stream_stderr=True, ) - ret['pid'] = proc.pid + ret["pid"] = proc.pid while proc.has_unread_data: try: try: @@ -778,132 +797,134 @@ def _run(cmd, try: cstdout, cstderr = proc.recv() except IOError: - cstdout, cstderr = '', '' + cstdout, cstderr = "", "" if cstdout: stdout += cstdout else: - cstdout = '' + cstdout = "" if cstderr: stderr += cstderr else: - cstderr = '' + cstderr = "" if timeout and (time.time() > will_timeout): - ret['stderr'] = ( - 'SALT: Timeout after {0}s\n{1}').format( - timeout, stderr) - ret['retcode'] = None + ret["stderr"] = ("SALT: Timeout after {0}s\n{1}").format( + timeout, stderr + ) + ret["retcode"] = None break except KeyboardInterrupt: - ret['stderr'] = 'SALT: User break\n{0}'.format(stderr) - ret['retcode'] = 1 + ret["stderr"] = "SALT: User break\n{0}".format(stderr) + ret["retcode"] = 1 break except salt.utils.vt.TerminalException as exc: - log.error('VT: %s', exc, - exc_info_on_loglevel=logging.DEBUG) - ret = {'retcode': 1, 'pid': '2'} + log.error("VT: %s", exc, exc_info_on_loglevel=logging.DEBUG) + ret = {"retcode": 1, "pid": "2"} break # only set stdout on success as we already mangled in other # cases - ret['stdout'] = stdout + ret["stdout"] = stdout if not proc.isalive(): # Process terminated, i.e., not canceled by the user or by # the timeout - ret['stderr'] = stderr - ret['retcode'] = proc.exitstatus - if ret['retcode'] in success_retcodes: - ret['retcode'] = 0 - ret['pid'] = proc.pid + ret["stderr"] = stderr + ret["retcode"] = proc.exitstatus + if ret["retcode"] in success_retcodes: + ret["retcode"] = 0 + ret["pid"] = proc.pid finally: proc.close(terminate=True, kill=True) try: if ignore_retcode: - __context__['retcode'] = 0 + __context__["retcode"] = 0 else: - __context__['retcode'] = ret['retcode'] + __context__["retcode"] = ret["retcode"] except NameError: # Ignore the context error during grain generation pass # Log the output if output_loglevel is not None: - if not ignore_retcode and ret['retcode'] != 0: - if output_loglevel < LOG_LEVELS['error']: - output_loglevel = LOG_LEVELS['error'] - msg = ( - 'Command \'{0}\' failed with return code: {1}'.format( - cmd, - ret['retcode'] - ) + if not ignore_retcode and ret["retcode"] != 0: + if output_loglevel < LOG_LEVELS["error"]: + output_loglevel = LOG_LEVELS["error"] + msg = "Command '{0}' failed with return code: {1}".format( + cmd, ret["retcode"] ) log.error(log_callback(msg)) - if ret['stdout']: - log.log(output_loglevel, 'stdout: {0}'.format(log_callback(ret['stdout']))) - if ret['stderr']: - log.log(output_loglevel, 'stderr: {0}'.format(log_callback(ret['stderr']))) - if ret['retcode']: - log.log(output_loglevel, 'retcode: {0}'.format(ret['retcode'])) + if ret["stdout"]: + log.log(output_loglevel, "stdout: {0}".format(log_callback(ret["stdout"]))) + if ret["stderr"]: + log.log(output_loglevel, "stderr: {0}".format(log_callback(ret["stderr"]))) + if ret["retcode"]: + log.log(output_loglevel, "retcode: {0}".format(ret["retcode"])) return ret -def _run_quiet(cmd, - cwd=None, - stdin=None, - output_encoding=None, - runas=None, - shell=DEFAULT_SHELL, - python_shell=False, - env=None, - template=None, - umask=None, - timeout=None, - reset_system_locale=True, - saltenv='base', - pillarenv=None, - pillar_override=None, - success_retcodes=None): - ''' +def _run_quiet( + cmd, + cwd=None, + stdin=None, + output_encoding=None, + runas=None, + shell=DEFAULT_SHELL, + python_shell=False, + env=None, + template=None, + umask=None, + timeout=None, + reset_system_locale=True, + saltenv="base", + pillarenv=None, + pillar_override=None, + success_retcodes=None, +): + """ Helper for running commands quietly for minion startup - ''' - return _run(cmd, - runas=runas, - cwd=cwd, - stdin=stdin, - stderr=subprocess.STDOUT, - output_encoding=output_encoding, - output_loglevel='quiet', - log_callback=None, - shell=shell, - python_shell=python_shell, - env=env, - template=template, - umask=umask, - timeout=timeout, - reset_system_locale=reset_system_locale, - saltenv=saltenv, - pillarenv=pillarenv, - pillar_override=pillar_override, - success_retcodes=success_retcodes)['stdout'] + """ + return _run( + cmd, + runas=runas, + cwd=cwd, + stdin=stdin, + stderr=subprocess.STDOUT, + output_encoding=output_encoding, + output_loglevel="quiet", + log_callback=None, + shell=shell, + python_shell=python_shell, + env=env, + template=template, + umask=umask, + timeout=timeout, + reset_system_locale=reset_system_locale, + saltenv=saltenv, + pillarenv=pillarenv, + pillar_override=pillar_override, + success_retcodes=success_retcodes, + )["stdout"] -def _run_all_quiet(cmd, - cwd=None, - stdin=None, - runas=None, - shell=DEFAULT_SHELL, - python_shell=False, - env=None, - template=None, - umask=None, - timeout=None, - reset_system_locale=True, - saltenv='base', - pillarenv=None, - pillar_override=None, - output_encoding=None, - success_retcodes=None): +def _run_all_quiet( + cmd, + cwd=None, + stdin=None, + runas=None, + shell=DEFAULT_SHELL, + python_shell=False, + env=None, + template=None, + umask=None, + timeout=None, + reset_system_locale=True, + saltenv="base", + pillarenv=None, + pillar_override=None, + output_encoding=None, + success_retcodes=None, +): - ''' + """ Helper for running commands quietly for minion startup. Returns a dict of return data. @@ -911,56 +932,60 @@ def _run_all_quiet(cmd, cmd.run_all directly to _run_all_quiet in certain chicken-and-egg situations where modules need to work both before and after the __salt__ dictionary is populated (cf dracr.py) - ''' - return _run(cmd, - runas=runas, - cwd=cwd, - stdin=stdin, - shell=shell, - python_shell=python_shell, - env=env, - output_encoding=output_encoding, - output_loglevel='quiet', - log_callback=None, - template=template, - umask=umask, - timeout=timeout, - reset_system_locale=reset_system_locale, - saltenv=saltenv, - pillarenv=pillarenv, - pillar_override=pillar_override, - success_retcodes=success_retcodes) - - -def run(cmd, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=None, - env=None, - clean_env=False, - template=None, - rstrip=True, - umask=None, - output_encoding=None, - output_loglevel='debug', + """ + return _run( + cmd, + runas=runas, + cwd=cwd, + stdin=stdin, + shell=shell, + python_shell=python_shell, + env=env, + output_encoding=output_encoding, + output_loglevel="quiet", log_callback=None, - hide_output=False, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - bg=False, - password=None, - encoded_cmd=False, - raise_err=False, - prepend_path=None, - success_retcodes=None, - **kwargs): - r''' + template=template, + umask=umask, + timeout=timeout, + reset_system_locale=reset_system_locale, + saltenv=saltenv, + pillarenv=pillarenv, + pillar_override=pillar_override, + success_retcodes=success_retcodes, + ) + + +def run( + cmd, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=None, + env=None, + clean_env=False, + template=None, + rstrip=True, + umask=None, + output_encoding=None, + output_loglevel="debug", + log_callback=None, + hide_output=False, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + bg=False, + password=None, + encoded_cmd=False, + raise_err=False, + prepend_path=None, + success_retcodes=None, + **kwargs +): + r""" Execute the passed command and return the output as a string :param str cmd: The command to run. ex: ``ls -lart /home`` @@ -1147,85 +1172,85 @@ def run(cmd, .. code-block:: bash salt '*' cmd.run cmd='sed -e s/=/:/g' - ''' - python_shell = _python_shell_default(python_shell, - kwargs.get('__pub_jid', '')) - ret = _run(cmd, - runas=runas, - group=group, - shell=shell, - python_shell=python_shell, - cwd=cwd, - stdin=stdin, - stderr=subprocess.STDOUT, - env=env, - clean_env=clean_env, - prepend_path=prepend_path, - template=template, - rstrip=rstrip, - umask=umask, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - log_callback=log_callback, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - use_vt=use_vt, - bg=bg, - password=password, - encoded_cmd=encoded_cmd, - success_retcodes=success_retcodes, - **kwargs) + """ + python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", "")) + ret = _run( + cmd, + runas=runas, + group=group, + shell=shell, + python_shell=python_shell, + cwd=cwd, + stdin=stdin, + stderr=subprocess.STDOUT, + env=env, + clean_env=clean_env, + prepend_path=prepend_path, + template=template, + rstrip=rstrip, + umask=umask, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + log_callback=log_callback, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + use_vt=use_vt, + bg=bg, + password=password, + encoded_cmd=encoded_cmd, + success_retcodes=success_retcodes, + **kwargs + ) log_callback = _check_cb(log_callback) lvl = _check_loglevel(output_loglevel) if lvl is not None: - if not ignore_retcode and ret['retcode'] != 0: - if lvl < LOG_LEVELS['error']: - lvl = LOG_LEVELS['error'] - msg = ( - 'Command \'{0}\' failed with return code: {1}'.format( - cmd, - ret['retcode'] - ) + if not ignore_retcode and ret["retcode"] != 0: + if lvl < LOG_LEVELS["error"]: + lvl = LOG_LEVELS["error"] + msg = "Command '{0}' failed with return code: {1}".format( + cmd, ret["retcode"] ) log.error(log_callback(msg)) if raise_err: raise CommandExecutionError( - log_callback(ret['stdout'] if not hide_output else '') + log_callback(ret["stdout"] if not hide_output else "") ) - log.log(lvl, 'output: %s', log_callback(ret['stdout'])) - return ret['stdout'] if not hide_output else '' + log.log(lvl, "output: %s", log_callback(ret["stdout"])) + return ret["stdout"] if not hide_output else "" -def shell(cmd, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - env=None, - clean_env=False, - template=None, - rstrip=True, - umask=None, - output_encoding=None, - output_loglevel='debug', - log_callback=None, - hide_output=False, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - bg=False, - password=None, - prepend_path=None, - success_retcodes=None, - **kwargs): - ''' +def shell( + cmd, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + env=None, + clean_env=False, + template=None, + rstrip=True, + umask=None, + output_encoding=None, + output_loglevel="debug", + log_callback=None, + hide_output=False, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + bg=False, + password=None, + prepend_path=None, + success_retcodes=None, + **kwargs +): + """ Execute the passed command and return the output as a string. .. versionadded:: 2015.5.0 @@ -1255,7 +1280,7 @@ def shell(cmd, .. code-block:: bash - cmd.shell 'echo '\\''h=\\"baz\\"'\\\''' runas=macuser + cmd.shell 'echo '\\''h=\\"baz\\"'\\''' runas=macuser :param str group: Group to run command as. Not currently supported on Windows. @@ -1399,65 +1424,69 @@ def shell(cmd, .. code-block:: bash salt '*' cmd.shell cmd='sed -e s/=/:/g' - ''' - if 'python_shell' in kwargs: - python_shell = kwargs.pop('python_shell') + """ + if "python_shell" in kwargs: + python_shell = kwargs.pop("python_shell") else: python_shell = True - return run(cmd, - cwd=cwd, - stdin=stdin, - runas=runas, - group=group, - shell=shell, - env=env, - clean_env=clean_env, - prepend_path=prepend_path, - template=template, - rstrip=rstrip, - umask=umask, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - log_callback=log_callback, - hide_output=hide_output, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - use_vt=use_vt, - python_shell=python_shell, - bg=bg, - password=password, - success_retcodes=success_retcodes, - **kwargs) + return run( + cmd, + cwd=cwd, + stdin=stdin, + runas=runas, + group=group, + shell=shell, + env=env, + clean_env=clean_env, + prepend_path=prepend_path, + template=template, + rstrip=rstrip, + umask=umask, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + log_callback=log_callback, + hide_output=hide_output, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + use_vt=use_vt, + python_shell=python_shell, + bg=bg, + password=password, + success_retcodes=success_retcodes, + **kwargs + ) -def run_stdout(cmd, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=None, - env=None, - clean_env=False, - template=None, - rstrip=True, - umask=None, - output_encoding=None, - output_loglevel='debug', - log_callback=None, - hide_output=False, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - password=None, - prepend_path=None, - success_retcodes=None, - **kwargs): - ''' +def run_stdout( + cmd, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=None, + env=None, + clean_env=False, + template=None, + rstrip=True, + umask=None, + output_encoding=None, + output_loglevel="debug", + log_callback=None, + hide_output=False, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + password=None, + prepend_path=None, + success_retcodes=None, + **kwargs +): + """ Execute a command, and only return the standard out :param str cmd: The command to run. ex: ``ls -lart /home`` @@ -1485,7 +1514,7 @@ def run_stdout(cmd, .. code-block:: bash - cmd.run_stdout 'echo '\\''h=\\"baz\\"'\\\''' runas=macuser + cmd.run_stdout 'echo '\\''h=\\"baz\\"'\\''' runas=macuser :param str password: Windows only. Required when specifying ``runas``. This parameter will be ignored on non-Windows platforms. @@ -1609,63 +1638,66 @@ def run_stdout(cmd, .. code-block:: bash salt '*' cmd.run_stdout "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' - ''' - python_shell = _python_shell_default(python_shell, - kwargs.get('__pub_jid', '')) - ret = _run(cmd, - runas=runas, - group=group, - cwd=cwd, - stdin=stdin, - shell=shell, - python_shell=python_shell, - env=env, - clean_env=clean_env, - prepend_path=prepend_path, - template=template, - rstrip=rstrip, - umask=umask, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - log_callback=log_callback, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - use_vt=use_vt, - password=password, - success_retcodes=success_retcodes, - **kwargs) + """ + python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", "")) + ret = _run( + cmd, + runas=runas, + group=group, + cwd=cwd, + stdin=stdin, + shell=shell, + python_shell=python_shell, + env=env, + clean_env=clean_env, + prepend_path=prepend_path, + template=template, + rstrip=rstrip, + umask=umask, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + log_callback=log_callback, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + use_vt=use_vt, + password=password, + success_retcodes=success_retcodes, + **kwargs + ) - return ret['stdout'] if not hide_output else '' + return ret["stdout"] if not hide_output else "" -def run_stderr(cmd, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=None, - env=None, - clean_env=False, - template=None, - rstrip=True, - umask=None, - output_encoding=None, - output_loglevel='debug', - log_callback=None, - hide_output=False, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - password=None, - prepend_path=None, - success_retcodes=None, - **kwargs): - ''' +def run_stderr( + cmd, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=None, + env=None, + clean_env=False, + template=None, + rstrip=True, + umask=None, + output_encoding=None, + output_loglevel="debug", + log_callback=None, + hide_output=False, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + password=None, + prepend_path=None, + success_retcodes=None, + **kwargs +): + """ Execute a command and only return the standard error :param str cmd: The command to run. ex: ``ls -lart /home`` @@ -1693,7 +1725,7 @@ def run_stderr(cmd, .. code-block:: bash - cmd.run_stderr 'echo '\\''h=\\"baz\\"'\\\''' runas=macuser + cmd.run_stderr 'echo '\\''h=\\"baz\\"'\\''' runas=macuser :param str password: Windows only. Required when specifying ``runas``. This parameter will be ignored on non-Windows platforms. @@ -1817,65 +1849,68 @@ def run_stderr(cmd, .. code-block:: bash salt '*' cmd.run_stderr "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' - ''' - python_shell = _python_shell_default(python_shell, - kwargs.get('__pub_jid', '')) - ret = _run(cmd, - runas=runas, - group=group, - cwd=cwd, - stdin=stdin, - shell=shell, - python_shell=python_shell, - env=env, - clean_env=clean_env, - prepend_path=prepend_path, - template=template, - rstrip=rstrip, - umask=umask, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - log_callback=log_callback, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - use_vt=use_vt, - saltenv=saltenv, - password=password, - success_retcodes=success_retcodes, - **kwargs) + """ + python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", "")) + ret = _run( + cmd, + runas=runas, + group=group, + cwd=cwd, + stdin=stdin, + shell=shell, + python_shell=python_shell, + env=env, + clean_env=clean_env, + prepend_path=prepend_path, + template=template, + rstrip=rstrip, + umask=umask, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + log_callback=log_callback, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + use_vt=use_vt, + saltenv=saltenv, + password=password, + success_retcodes=success_retcodes, + **kwargs + ) - return ret['stderr'] if not hide_output else '' + return ret["stderr"] if not hide_output else "" -def run_all(cmd, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=None, - env=None, - clean_env=False, - template=None, - rstrip=True, - umask=None, - output_encoding=None, - output_loglevel='debug', - log_callback=None, - hide_output=False, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - redirect_stderr=False, - password=None, - encoded_cmd=False, - prepend_path=None, - success_retcodes=None, - **kwargs): - ''' +def run_all( + cmd, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=None, + env=None, + clean_env=False, + template=None, + rstrip=True, + umask=None, + output_encoding=None, + output_loglevel="debug", + log_callback=None, + hide_output=False, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + redirect_stderr=False, + password=None, + encoded_cmd=False, + prepend_path=None, + success_retcodes=None, + **kwargs +): + """ Execute the passed command and return a dict of return data :param str cmd: The command to run. ex: ``ls -lart /home`` @@ -1903,7 +1938,7 @@ def run_all(cmd, .. code-block:: bash - cmd.run_all 'echo '\\''h=\\"baz\\"'\\\''' runas=macuser + cmd.run_all 'echo '\\''h=\\"baz\\"'\\''' runas=macuser :param str password: Windows only. Required when specifying ``runas``. This parameter will be ignored on non-Windows platforms. @@ -2049,65 +2084,68 @@ def run_all(cmd, .. code-block:: bash salt '*' cmd.run_all "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' - ''' - python_shell = _python_shell_default(python_shell, - kwargs.get('__pub_jid', '')) + """ + python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", "")) stderr = subprocess.STDOUT if redirect_stderr else subprocess.PIPE - ret = _run(cmd, - runas=runas, - group=group, - cwd=cwd, - stdin=stdin, - stderr=stderr, - shell=shell, - python_shell=python_shell, - env=env, - clean_env=clean_env, - prepend_path=prepend_path, - template=template, - rstrip=rstrip, - umask=umask, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - log_callback=log_callback, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - use_vt=use_vt, - password=password, - encoded_cmd=encoded_cmd, - success_retcodes=success_retcodes, - **kwargs) + ret = _run( + cmd, + runas=runas, + group=group, + cwd=cwd, + stdin=stdin, + stderr=stderr, + shell=shell, + python_shell=python_shell, + env=env, + clean_env=clean_env, + prepend_path=prepend_path, + template=template, + rstrip=rstrip, + umask=umask, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + log_callback=log_callback, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + use_vt=use_vt, + password=password, + encoded_cmd=encoded_cmd, + success_retcodes=success_retcodes, + **kwargs + ) if hide_output: - ret['stdout'] = ret['stderr'] = '' + ret["stdout"] = ret["stderr"] = "" return ret -def retcode(cmd, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=None, - env=None, - clean_env=False, - template=None, - umask=None, - output_encoding=None, - output_loglevel='debug', - log_callback=None, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - password=None, - success_retcodes=None, - **kwargs): - ''' +def retcode( + cmd, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=None, + env=None, + clean_env=False, + template=None, + umask=None, + output_encoding=None, + output_loglevel="debug", + log_callback=None, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + password=None, + success_retcodes=None, + **kwargs +): + """ Execute a shell command and return the command's return code. :param str cmd: The command to run. ex: ``ls -lart /home`` @@ -2135,7 +2173,7 @@ def retcode(cmd, .. code-block:: bash - cmd.retcode 'echo '\\''h=\\"baz\\"'\\\''' runas=macuser + cmd.retcode 'echo '\\''h=\\"baz\\"'\\''' runas=macuser :param str password: Windows only. Required when specifying ``runas``. This parameter will be ignored on non-Windows platforms. @@ -2248,109 +2286,116 @@ def retcode(cmd, .. code-block:: bash salt '*' cmd.retcode "grep f" stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' - ''' - python_shell = _python_shell_default(python_shell, - kwargs.get('__pub_jid', '')) + """ + python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", "")) - ret = _run(cmd, - runas=runas, - group=group, - cwd=cwd, - stdin=stdin, - stderr=subprocess.STDOUT, - shell=shell, - python_shell=python_shell, - env=env, - clean_env=clean_env, - template=template, - umask=umask, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - log_callback=log_callback, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - use_vt=use_vt, - password=password, - success_retcodes=success_retcodes, - **kwargs) - return ret['retcode'] + ret = _run( + cmd, + runas=runas, + group=group, + cwd=cwd, + stdin=stdin, + stderr=subprocess.STDOUT, + shell=shell, + python_shell=python_shell, + env=env, + clean_env=clean_env, + template=template, + umask=umask, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + log_callback=log_callback, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + use_vt=use_vt, + password=password, + success_retcodes=success_retcodes, + **kwargs + ) + return ret["retcode"] -def _retcode_quiet(cmd, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=False, - env=None, - clean_env=False, - template=None, - umask=None, - output_encoding=None, - log_callback=None, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - password=None, - success_retcodes=None, - **kwargs): - ''' +def _retcode_quiet( + cmd, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=False, + env=None, + clean_env=False, + template=None, + umask=None, + output_encoding=None, + log_callback=None, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + password=None, + success_retcodes=None, + **kwargs +): + """ Helper for running commands quietly for minion startup. Returns same as the retcode() function. - ''' - return retcode(cmd, - cwd=cwd, - stdin=stdin, - runas=runas, - group=group, - shell=shell, - python_shell=python_shell, - env=env, - clean_env=clean_env, - template=template, - umask=umask, - output_encoding=output_encoding, - output_loglevel='quiet', - log_callback=log_callback, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - use_vt=use_vt, - password=password, - success_retcodes=success_retcodes, - **kwargs) + """ + return retcode( + cmd, + cwd=cwd, + stdin=stdin, + runas=runas, + group=group, + shell=shell, + python_shell=python_shell, + env=env, + clean_env=clean_env, + template=template, + umask=umask, + output_encoding=output_encoding, + output_loglevel="quiet", + log_callback=log_callback, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + use_vt=use_vt, + password=password, + success_retcodes=success_retcodes, + **kwargs + ) -def script(source, - args=None, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=None, - env=None, - template=None, - umask=None, - output_encoding=None, - output_loglevel='debug', - log_callback=None, - hide_output=False, - timeout=None, - reset_system_locale=True, - saltenv='base', - use_vt=False, - bg=False, - password=None, - success_retcodes=None, - **kwargs): - ''' +def script( + source, + args=None, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=None, + env=None, + template=None, + umask=None, + output_encoding=None, + output_loglevel="debug", + log_callback=None, + hide_output=False, + timeout=None, + reset_system_locale=True, + saltenv="base", + use_vt=False, + bg=False, + password=None, + success_retcodes=None, + **kwargs +): + """ Download a script from a remote location and execute the script locally. The script can be located on the salt master file server or on an HTTP/FTP server. @@ -2504,127 +2549,132 @@ def script(source, .. code-block:: bash salt '*' cmd.script salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' - ''' - python_shell = _python_shell_default(python_shell, - kwargs.get('__pub_jid', '')) + """ + python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", "")) def _cleanup_tempfile(path): try: - __salt__['file.remove'](path) + __salt__["file.remove"](path) except (SaltInvocationError, CommandExecutionError) as exc: log.error( - 'cmd.script: Unable to clean tempfile \'%s\': %s', - path, exc, exc_info_on_loglevel=logging.DEBUG + "cmd.script: Unable to clean tempfile '%s': %s", + path, + exc, + exc_info_on_loglevel=logging.DEBUG, ) - if '__env__' in kwargs: + if "__env__" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('__env__') + kwargs.pop("__env__") win_cwd = False if salt.utils.platform.is_windows() and runas and cwd is None: # Create a temp working directory - cwd = tempfile.mkdtemp(dir=__opts__['cachedir']) + cwd = tempfile.mkdtemp(dir=__opts__["cachedir"]) win_cwd = True - salt.utils.win_dacl.set_permissions(obj_name=cwd, - principal=runas, - permissions='full_control') + salt.utils.win_dacl.set_permissions( + obj_name=cwd, principal=runas, permissions="full_control" + ) path = salt.utils.files.mkstemp(dir=cwd, suffix=os.path.splitext(source)[1]) if template: - if 'pillarenv' in kwargs or 'pillar' in kwargs: - pillarenv = kwargs.get('pillarenv', __opts__.get('pillarenv')) - kwargs['pillar'] = _gather_pillar(pillarenv, kwargs.get('pillar')) - fn_ = __salt__['cp.get_template'](source, - path, - template, - saltenv, - **kwargs) + if "pillarenv" in kwargs or "pillar" in kwargs: + pillarenv = kwargs.get("pillarenv", __opts__.get("pillarenv")) + kwargs["pillar"] = _gather_pillar(pillarenv, kwargs.get("pillar")) + fn_ = __salt__["cp.get_template"](source, path, template, saltenv, **kwargs) if not fn_: _cleanup_tempfile(path) # If a temp working directory was created (Windows), let's remove that if win_cwd: _cleanup_tempfile(cwd) - return {'pid': 0, - 'retcode': 1, - 'stdout': '', - 'stderr': '', - 'cache_error': True} + return { + "pid": 0, + "retcode": 1, + "stdout": "", + "stderr": "", + "cache_error": True, + } else: - fn_ = __salt__['cp.cache_file'](source, saltenv) + fn_ = __salt__["cp.cache_file"](source, saltenv) if not fn_: _cleanup_tempfile(path) # If a temp working directory was created (Windows), let's remove that if win_cwd: _cleanup_tempfile(cwd) - return {'pid': 0, - 'retcode': 1, - 'stdout': '', - 'stderr': '', - 'cache_error': True} + return { + "pid": 0, + "retcode": 1, + "stdout": "", + "stderr": "", + "cache_error": True, + } shutil.copyfile(fn_, path) if not salt.utils.platform.is_windows(): os.chmod(path, 320) - os.chown(path, __salt__['file.user_to_uid'](runas), -1) + os.chown(path, __salt__["file.user_to_uid"](runas), -1) - if salt.utils.platform.is_windows() and shell.lower() != 'powershell': + if salt.utils.platform.is_windows() and shell.lower() != "powershell": cmd_path = _cmd_quote(path, escape=False) else: cmd_path = _cmd_quote(path) - ret = _run(cmd_path + ' ' + six.text_type(args) if args else cmd_path, - cwd=cwd, - stdin=stdin, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - log_callback=log_callback, - runas=runas, - group=group, - shell=shell, - python_shell=python_shell, - env=env, - umask=umask, - timeout=timeout, - reset_system_locale=reset_system_locale, - saltenv=saltenv, - use_vt=use_vt, - bg=bg, - password=password, - success_retcodes=success_retcodes, - **kwargs) + ret = _run( + cmd_path + " " + six.text_type(args) if args else cmd_path, + cwd=cwd, + stdin=stdin, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + log_callback=log_callback, + runas=runas, + group=group, + shell=shell, + python_shell=python_shell, + env=env, + umask=umask, + timeout=timeout, + reset_system_locale=reset_system_locale, + saltenv=saltenv, + use_vt=use_vt, + bg=bg, + password=password, + success_retcodes=success_retcodes, + **kwargs + ) _cleanup_tempfile(path) # If a temp working directory was created (Windows), let's remove that if win_cwd: _cleanup_tempfile(cwd) if hide_output: - ret['stdout'] = ret['stderr'] = '' + ret["stdout"] = ret["stderr"] = "" return ret -def script_retcode(source, - args=None, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=None, - env=None, - template='jinja', - umask=None, - timeout=None, - reset_system_locale=True, - saltenv='base', - output_encoding=None, - output_loglevel='debug', - log_callback=None, - use_vt=False, - password=None, - success_retcodes=None, - **kwargs): - ''' +def script_retcode( + source, + args=None, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=None, + env=None, + template="jinja", + umask=None, + timeout=None, + reset_system_locale=True, + saltenv="base", + output_encoding=None, + output_loglevel="debug", + log_callback=None, + use_vt=False, + password=None, + success_retcodes=None, + **kwargs +): + """ Download a script from a remote location and execute the script locally. The script can be located on the salt master file server or on an HTTP/FTP server. @@ -2754,36 +2804,38 @@ def script_retcode(source, .. code-block:: bash salt '*' cmd.script_retcode salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' - ''' - if '__env__' in kwargs: + """ + if "__env__" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('__env__') + kwargs.pop("__env__") - return script(source=source, - args=args, - cwd=cwd, - stdin=stdin, - runas=runas, - group=group, - shell=shell, - python_shell=python_shell, - env=env, - template=template, - umask=umask, - timeout=timeout, - reset_system_locale=reset_system_locale, - saltenv=saltenv, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - log_callback=log_callback, - use_vt=use_vt, - password=password, - success_retcodes=success_retcodes, - **kwargs)['retcode'] + return script( + source=source, + args=args, + cwd=cwd, + stdin=stdin, + runas=runas, + group=group, + shell=shell, + python_shell=python_shell, + env=env, + template=template, + umask=umask, + timeout=timeout, + reset_system_locale=reset_system_locale, + saltenv=saltenv, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + log_callback=log_callback, + use_vt=use_vt, + password=password, + success_retcodes=success_retcodes, + **kwargs + )["retcode"] def which(cmd): - ''' + """ Returns the path of an executable available on the minion, None otherwise CLI Example: @@ -2791,12 +2843,12 @@ def which(cmd): .. code-block:: bash salt '*' cmd.which cat - ''' + """ return salt.utils.path.which(cmd) def which_bin(cmds): - ''' + """ Returns the first command found in a list of commands CLI Example: @@ -2804,12 +2856,12 @@ def which_bin(cmds): .. code-block:: bash salt '*' cmd.which_bin '[pip2, pip, pip-python]' - ''' + """ return salt.utils.path.which_bin(cmds) def has_exec(cmd): - ''' + """ Returns true if the executable is available on the minion, false otherwise CLI Example: @@ -2817,12 +2869,12 @@ def has_exec(cmd): .. code-block:: bash salt '*' cmd.has_exec cat - ''' + """ return which(cmd) is not None def exec_code(lang, code, cwd=None, args=None, **kwargs): - ''' + """ Pass in two strings, the first naming the executable language, aka - python2, python3, ruby, perl, lua, etc. the second string containing the code you wish to execute. The stdout will be returned. @@ -2835,12 +2887,12 @@ def exec_code(lang, code, cwd=None, args=None, **kwargs): salt '*' cmd.exec_code ruby 'puts "cheese"' salt '*' cmd.exec_code ruby 'puts "cheese"' args='["arg1", "arg2"]' env='{"FOO": "bar"}' - ''' - return exec_code_all(lang, code, cwd, args, **kwargs)['stdout'] + """ + return exec_code_all(lang, code, cwd, args, **kwargs)["stdout"] def exec_code_all(lang, code, cwd=None, args=None, **kwargs): - ''' + """ Pass in two strings, the first naming the executable language, aka - python2, python3, ruby, perl, lua, etc. the second string containing the code you wish to execute. All cmd artifacts (stdout, stderr, retcode, pid) @@ -2854,7 +2906,7 @@ def exec_code_all(lang, code, cwd=None, args=None, **kwargs): salt '*' cmd.exec_code_all ruby 'puts "cheese"' salt '*' cmd.exec_code_all ruby 'puts "cheese"' args='["arg1", "arg2"]' env='{"FOO": "bar"}' - ''' + """ powershell = lang.lower().startswith("powershell") if powershell: @@ -2862,7 +2914,7 @@ def exec_code_all(lang, code, cwd=None, args=None, **kwargs): else: codefile = salt.utils.files.mkstemp() - with salt.utils.files.fopen(codefile, 'w+t', binary=False) as fp_: + with salt.utils.files.fopen(codefile, "w+t", binary=False) as fp_: fp_.write(salt.utils.stringutils.to_str(code)) if powershell: @@ -2880,8 +2932,8 @@ def exec_code_all(lang, code, cwd=None, args=None, **kwargs): return ret -def tty(device, echo=''): - ''' +def tty(device, echo=""): + """ Echo a string to a specific tty CLI Example: @@ -2890,52 +2942,50 @@ def tty(device, echo=''): salt '*' cmd.tty tty0 'This is a test' salt '*' cmd.tty pts3 'This is a test' - ''' - if device.startswith('tty'): - teletype = '/dev/{0}'.format(device) - elif device.startswith('pts'): - teletype = '/dev/{0}'.format(device.replace('pts', 'pts/')) + """ + if device.startswith("tty"): + teletype = "/dev/{0}".format(device) + elif device.startswith("pts"): + teletype = "/dev/{0}".format(device.replace("pts", "pts/")) else: - return {'Error': 'The specified device is not a valid TTY'} + return {"Error": "The specified device is not a valid TTY"} try: - with salt.utils.files.fopen(teletype, 'wb') as tty_device: + with salt.utils.files.fopen(teletype, "wb") as tty_device: tty_device.write(salt.utils.stringutils.to_bytes(echo)) - return { - 'Success': 'Message was successfully echoed to {0}'.format(teletype) - } + return {"Success": "Message was successfully echoed to {0}".format(teletype)} except IOError: - return { - 'Error': 'Echoing to {0} returned error'.format(teletype) - } + return {"Error": "Echoing to {0} returned error".format(teletype)} -def run_chroot(root, - cmd, - cwd=None, - stdin=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=True, - binds=None, - env=None, - clean_env=False, - template=None, - rstrip=True, - umask=None, - output_encoding=None, - output_loglevel='quiet', - log_callback=None, - hide_output=False, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - bg=False, - success_retcodes=None, - **kwargs): - ''' +def run_chroot( + root, + cmd, + cwd=None, + stdin=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=True, + binds=None, + env=None, + clean_env=False, + template=None, + rstrip=True, + umask=None, + output_encoding=None, + output_loglevel="quiet", + log_callback=None, + hide_output=False, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + bg=False, + success_retcodes=None, + **kwargs +): + """ .. versionadded:: 2014.7.0 This function runs :mod:`cmd.run_all ` wrapped @@ -3064,64 +3114,54 @@ def run_chroot(root, .. code-block:: bash salt '*' cmd.run_chroot /var/lib/lxc/container_name/rootfs 'sh /tmp/bootstrap.sh' - ''' - __salt__['mount.mount']( - os.path.join(root, 'dev'), - 'devtmpfs', - fstype='devtmpfs') - __salt__['mount.mount']( - os.path.join(root, 'proc'), - 'proc', - fstype='proc') - __salt__['mount.mount']( - os.path.join(root, 'sys'), - 'sysfs', - fstype='sysfs') + """ + __salt__["mount.mount"](os.path.join(root, "dev"), "devtmpfs", fstype="devtmpfs") + __salt__["mount.mount"](os.path.join(root, "proc"), "proc", fstype="proc") + __salt__["mount.mount"](os.path.join(root, "sys"), "sysfs", fstype="sysfs") binds = binds if binds else [] for bind_exported in binds: bind_exported_to = os.path.relpath(bind_exported, os.path.sep) bind_exported_to = os.path.join(root, bind_exported_to) - __salt__['mount.mount']( - bind_exported_to, - bind_exported, - opts='default,bind') + __salt__["mount.mount"](bind_exported_to, bind_exported, opts="default,bind") # Execute chroot routine - sh_ = '/bin/sh' - if os.path.isfile(os.path.join(root, 'bin/bash')): - sh_ = '/bin/bash' + sh_ = "/bin/sh" + if os.path.isfile(os.path.join(root, "bin/bash")): + sh_ = "/bin/bash" if isinstance(cmd, (list, tuple)): - cmd = ' '.join([six.text_type(i) for i in cmd]) - cmd = 'chroot {0} {1} -c {2}'.format(root, sh_, _cmd_quote(cmd)) + cmd = " ".join([six.text_type(i) for i in cmd]) + cmd = "chroot {0} {1} -c {2}".format(root, sh_, _cmd_quote(cmd)) - run_func = __context__.pop('cmd.run_chroot.func', run_all) + run_func = __context__.pop("cmd.run_chroot.func", run_all) - ret = run_func(cmd, - runas=runas, - group=group, - cwd=cwd, - stdin=stdin, - shell=shell, - python_shell=python_shell, - env=env, - clean_env=clean_env, - template=template, - rstrip=rstrip, - umask=umask, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - log_callback=log_callback, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - pillarenv=kwargs.get('pillarenv'), - pillar=kwargs.get('pillar'), - use_vt=use_vt, - success_retcodes=success_retcodes, - bg=bg) + ret = run_func( + cmd, + runas=runas, + group=group, + cwd=cwd, + stdin=stdin, + shell=shell, + python_shell=python_shell, + env=env, + clean_env=clean_env, + template=template, + rstrip=rstrip, + umask=umask, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + log_callback=log_callback, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + pillarenv=kwargs.get("pillarenv"), + pillar=kwargs.get("pillar"), + use_vt=use_vt, + success_retcodes=success_retcodes, + bg=bg, + ) # Kill processes running in the chroot for i in range(6): @@ -3134,38 +3174,42 @@ def run_chroot(root, os.kill(pid, sig) if _chroot_pids(root): - log.error('Processes running in chroot could not be killed, ' - 'filesystem will remain mounted') + log.error( + "Processes running in chroot could not be killed, " + "filesystem will remain mounted" + ) for bind_exported in binds: bind_exported_to = os.path.relpath(bind_exported, os.path.sep) bind_exported_to = os.path.join(root, bind_exported_to) - __salt__['mount.umount'](bind_exported_to) + __salt__["mount.umount"](bind_exported_to) - __salt__['mount.umount'](os.path.join(root, 'sys')) - __salt__['mount.umount'](os.path.join(root, 'proc')) - __salt__['mount.umount'](os.path.join(root, 'dev')) + __salt__["mount.umount"](os.path.join(root, "sys")) + __salt__["mount.umount"](os.path.join(root, "proc")) + __salt__["mount.umount"](os.path.join(root, "dev")) if hide_output: - ret['stdout'] = ret['stderr'] = '' + ret["stdout"] = ret["stderr"] = "" return ret def _is_valid_shell(shell): - ''' + """ Attempts to search for valid shells on a system and see if a given shell is in the list - ''' + """ if salt.utils.platform.is_windows(): return True # Don't even try this for Windows - shells = '/etc/shells' + shells = "/etc/shells" available_shells = [] if os.path.exists(shells): try: - with salt.utils.files.fopen(shells, 'r') as shell_fp: - lines = [salt.utils.stringutils.to_unicode(x) - for x in shell_fp.read().splitlines()] + with salt.utils.files.fopen(shells, "r") as shell_fp: + lines = [ + salt.utils.stringutils.to_unicode(x) + for x in shell_fp.read().splitlines() + ] for line in lines: - if line.startswith('#'): + if line.startswith("#"): continue else: available_shells.append(line) @@ -3181,7 +3225,7 @@ def _is_valid_shell(shell): def shells(): - ''' + """ Lists the valid shells on this system via the /etc/shells file .. versionadded:: 2015.5.0 @@ -3189,17 +3233,19 @@ def shells(): CLI Example:: salt '*' cmd.shells - ''' - shells_fn = '/etc/shells' + """ + shells_fn = "/etc/shells" ret = [] if os.path.exists(shells_fn): try: - with salt.utils.files.fopen(shells_fn, 'r') as shell_fp: - lines = [salt.utils.stringutils.to_unicode(x) - for x in shell_fp.read().splitlines()] + with salt.utils.files.fopen(shells_fn, "r") as shell_fp: + lines = [ + salt.utils.stringutils.to_unicode(x) + for x in shell_fp.read().splitlines() + ] for line in lines: line = line.strip() - if line.startswith('#'): + if line.startswith("#"): continue elif not line: continue @@ -3211,7 +3257,7 @@ def shells(): def shell_info(shell, list_modules=False): - ''' + """ .. versionadded:: 2016.11.0 Provides information about a shell or script languages which often use @@ -3252,77 +3298,96 @@ def shell_info(shell, list_modules=False): salt '*' cmd.shell_info powershell :codeauthor: Damon Atkins - ''' + """ regex_shells = { - 'bash': [r'version (\d\S*)', 'bash', '--version'], - 'bash-test-error': [r'versioZ ([-\w.]+)', 'bash', '--version'], # used to test an error result - 'bash-test-env': [r'(HOME=.*)', 'bash', '-c', 'declare'], # used to test an error result - 'zsh': [r'^zsh (\d\S*)', 'zsh', '--version'], - 'tcsh': [r'^tcsh (\d\S*)', 'tcsh', '--version'], - 'cmd': [r'Version ([\d.]+)', 'cmd.exe', '/C', 'ver'], - 'powershell': [r'PSVersion\s+(\d\S*)', 'powershell', '-NonInteractive', '$PSVersionTable'], - 'perl': [r'^(\d\S*)', 'perl', '-e', 'printf "%vd\n", $^V;'], - 'python': [r'^Python (\d\S*)', 'python', '-V'], - 'ruby': [r'^ruby (\d\S*)', 'ruby', '-v'], - 'php': [r'^PHP (\d\S*)', 'php', '-v'] + "bash": [r"version (\d\S*)", "bash", "--version"], + "bash-test-error": [ + r"versioZ ([-\w.]+)", + "bash", + "--version", + ], # used to test an error result + "bash-test-env": [ + r"(HOME=.*)", + "bash", + "-c", + "declare", + ], # used to test an error result + "zsh": [r"^zsh (\d\S*)", "zsh", "--version"], + "tcsh": [r"^tcsh (\d\S*)", "tcsh", "--version"], + "cmd": [r"Version ([\d.]+)", "cmd.exe", "/C", "ver"], + "powershell": [ + r"PSVersion\s+(\d\S*)", + "powershell", + "-NonInteractive", + "$PSVersionTable", + ], + "perl": [r"^(\d\S*)", "perl", "-e", 'printf "%vd\n", $^V;'], + "python": [r"^Python (\d\S*)", "python", "-V"], + "ruby": [r"^ruby (\d\S*)", "ruby", "-v"], + "php": [r"^PHP (\d\S*)", "php", "-v"], } # Ensure ret['installed'] always as a value of True, False or None (not sure) - ret = {'installed': False} - if salt.utils.platform.is_windows() and shell == 'powershell': + ret = {"installed": False} + if salt.utils.platform.is_windows() and shell == "powershell": pw_keys = salt.utils.win_reg.list_keys( - hive='HKEY_LOCAL_MACHINE', - key='Software\\Microsoft\\PowerShell') + hive="HKEY_LOCAL_MACHINE", key="Software\\Microsoft\\PowerShell" + ) pw_keys.sort(key=int) if len(pw_keys) == 0: return { - 'error': 'Unable to locate \'powershell\' Reason: Cannot be ' - 'found in registry.', - 'installed': False, + "error": "Unable to locate 'powershell' Reason: Cannot be " + "found in registry.", + "installed": False, } for reg_ver in pw_keys: install_data = salt.utils.win_reg.read_value( - hive='HKEY_LOCAL_MACHINE', - key='Software\\Microsoft\\PowerShell\\{0}'.format(reg_ver), - vname='Install') - if install_data.get('vtype') == 'REG_DWORD' and \ - install_data.get('vdata') == 1: + hive="HKEY_LOCAL_MACHINE", + key="Software\\Microsoft\\PowerShell\\{0}".format(reg_ver), + vname="Install", + ) + if ( + install_data.get("vtype") == "REG_DWORD" + and install_data.get("vdata") == 1 + ): details = salt.utils.win_reg.list_values( - hive='HKEY_LOCAL_MACHINE', - key='Software\\Microsoft\\PowerShell\\{0}\\' - 'PowerShellEngine'.format(reg_ver)) + hive="HKEY_LOCAL_MACHINE", + key="Software\\Microsoft\\PowerShell\\{0}\\" + "PowerShellEngine".format(reg_ver), + ) # reset data, want the newest version details only as powershell # is backwards compatible ret = {} # if all goes well this will become True - ret['installed'] = None - ret['path'] = which('powershell.exe') + ret["installed"] = None + ret["path"] = which("powershell.exe") for attribute in details: - if attribute['vname'].lower() == '(default)': + if attribute["vname"].lower() == "(default)": continue - elif attribute['vname'].lower() == 'powershellversion': - ret['psversion'] = attribute['vdata'] - ret['version_raw'] = attribute['vdata'] - elif attribute['vname'].lower() == 'runtimeversion': - ret['crlversion'] = attribute['vdata'] - if ret['crlversion'][0].lower() == 'v': - ret['crlversion'] = ret['crlversion'][1::] - elif attribute['vname'].lower() == 'pscompatibleversion': + elif attribute["vname"].lower() == "powershellversion": + ret["psversion"] = attribute["vdata"] + ret["version_raw"] = attribute["vdata"] + elif attribute["vname"].lower() == "runtimeversion": + ret["crlversion"] = attribute["vdata"] + if ret["crlversion"][0].lower() == "v": + ret["crlversion"] = ret["crlversion"][1::] + elif attribute["vname"].lower() == "pscompatibleversion": # reg attribute does not end in s, the powershell # attribute does - ret['pscompatibleversions'] = \ - attribute['vdata'].replace(' ', '').split(',') + ret["pscompatibleversions"] = ( + attribute["vdata"].replace(" ", "").split(",") + ) else: # keys are lower case as python is case sensitive the # registry is not - ret[attribute['vname'].lower()] = attribute['vdata'] + ret[attribute["vname"].lower()] = attribute["vdata"] else: if shell not in regex_shells: return { - 'error': 'Salt does not know how to get the version number for ' - '{0}'.format(shell), - 'installed': None + "error": "Salt does not know how to get the version number for " + "{0}".format(shell), + "installed": None, } shell_data = regex_shells[shell] pattern = shell_data.pop(0) @@ -3332,13 +3397,14 @@ def shell_info(shell, list_modules=False): # themselves in setting HOME as they do it in many different ways if salt.utils.platform.is_windows(): import nt + newenv = nt.environ else: newenv = os.environ - if ('HOME' not in newenv) and (not salt.utils.platform.is_windows()): - newenv['HOME'] = os.path.expanduser('~') - log.debug('HOME environment set to %s', newenv['HOME']) + if ("HOME" not in newenv) and (not salt.utils.platform.is_windows()): + newenv["HOME"] = os.path.expanduser("~") + log.debug("HOME environment set to %s", newenv["HOME"]) try: proc = salt.utils.timed_subprocess.TimedProc( shell_data, @@ -3346,76 +3412,84 @@ def shell_info(shell, list_modules=False): stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=10, - env=newenv - ) + env=newenv, + ) except (OSError, IOError) as exc: return { - 'error': 'Unable to run command \'{0}\' Reason: {1}'.format(' '.join(shell_data), exc), - 'installed': False, + "error": "Unable to run command '{0}' Reason: {1}".format( + " ".join(shell_data), exc + ), + "installed": False, } try: proc.run() except TimedProcTimeoutError as exc: return { - 'error': 'Unable to run command \'{0}\' Reason: Timed out.'.format(' '.join(shell_data)), - 'installed': False, + "error": "Unable to run command '{0}' Reason: Timed out.".format( + " ".join(shell_data) + ), + "installed": False, } - ret['path'] = which(shell_data[0]) + ret["path"] = which(shell_data[0]) pattern_result = re.search(pattern, proc.stdout, flags=re.IGNORECASE) # only set version if we find it, so code later on can deal with it if pattern_result: - ret['version_raw'] = pattern_result.group(1) + ret["version_raw"] = pattern_result.group(1) - if 'version_raw' in ret: - version_results = re.match(r'(\d[\d.]*)', ret['version_raw']) + if "version_raw" in ret: + version_results = re.match(r"(\d[\d.]*)", ret["version_raw"]) if version_results: - ret['installed'] = True - ver_list = version_results.group(1).split('.')[:3] + ret["installed"] = True + ver_list = version_results.group(1).split(".")[:3] if len(ver_list) == 1: - ver_list.append('0') - ret['version'] = '.'.join(ver_list[:3]) + ver_list.append("0") + ret["version"] = ".".join(ver_list[:3]) else: - ret['installed'] = None # Have an unexpected result + ret["installed"] = None # Have an unexpected result # Get a list of the PowerShell modules which are potentially available # to be imported - if shell == 'powershell' and ret['installed'] and list_modules: - ret['modules'] = salt.utils.powershell.get_modules() + if shell == "powershell" and ret["installed"] and list_modules: + ret["modules"] = salt.utils.powershell.get_modules() - if 'version' not in ret: - ret['error'] = 'The version regex pattern for shell {0}, could not ' \ - 'find the version string'.format(shell) - ret['stdout'] = proc.stdout # include stdout so they can see the issue - log.error(ret['error']) + if "version" not in ret: + ret["error"] = ( + "The version regex pattern for shell {0}, could not " + "find the version string".format(shell) + ) + ret["stdout"] = proc.stdout # include stdout so they can see the issue + log.error(ret["error"]) return ret -def powershell(cmd, - cwd=None, - stdin=None, - runas=None, - shell=DEFAULT_SHELL, - env=None, - clean_env=False, - template=None, - rstrip=True, - umask=None, - output_encoding=None, - output_loglevel='debug', - hide_output=False, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - password=None, - depth=None, - encode_cmd=False, - success_retcodes=None, - **kwargs): - ''' +def powershell( + cmd, + cwd=None, + stdin=None, + runas=None, + shell=DEFAULT_SHELL, + env=None, + clean_env=False, + template=None, + rstrip=True, + umask=None, + output_encoding=None, + output_loglevel="debug", + hide_output=False, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + password=None, + depth=None, + encode_cmd=False, + success_retcodes=None, + **kwargs +): + """ Execute the passed PowerShell command and return the output as a dictionary. Other ``cmd.*`` functions (besides ``cmd.powershell_all``) @@ -3580,25 +3654,25 @@ def powershell(cmd, .. code-block:: powershell salt '*' cmd.powershell "$PSVersionTable.CLRVersion" - ''' - if 'python_shell' in kwargs: - python_shell = kwargs.pop('python_shell') + """ + if "python_shell" in kwargs: + python_shell = kwargs.pop("python_shell") else: python_shell = True # Append PowerShell Object formatting # ConvertTo-JSON is only available on PowerShell 3.0 and later - psversion = shell_info('powershell')['psversion'] - if salt.utils.versions.version_cmp(psversion, '2.0') == 1: - cmd += ' | ConvertTo-JSON' + psversion = shell_info("powershell")["psversion"] + if salt.utils.versions.version_cmp(psversion, "2.0") == 1: + cmd += " | ConvertTo-JSON" if depth is not None: - cmd += ' -Depth {0}'.format(depth) + cmd += " -Depth {0}".format(depth) if encode_cmd: # Convert the cmd to UTF-16LE without a BOM and base64 encode. # Just base64 encoding UTF-8 or including a BOM is not valid. - log.debug('Encoding PowerShell command \'%s\'', cmd) - cmd_utf16 = cmd.decode('utf-8').encode('utf-16le') + log.debug("Encoding PowerShell command '%s'", cmd) + cmd_utf16 = cmd.decode("utf-8").encode("utf-16le") cmd = base64.standard_b64encode(cmd_utf16) encoded_cmd = True else: @@ -3609,32 +3683,34 @@ def powershell(cmd, # caught in a try/catch block. For example, the `Get-WmiObject` command will # often return a "Non Terminating Error". To fix this, make sure # `-ErrorAction Stop` is set in the powershell command - cmd = 'try {' + cmd + '} catch { "{}" }' + cmd = "try {" + cmd + '} catch { "{}" }' # Retrieve the response, while overriding shell with 'powershell' - response = run(cmd, - cwd=cwd, - stdin=stdin, - runas=runas, - shell='powershell', - env=env, - clean_env=clean_env, - template=template, - rstrip=rstrip, - umask=umask, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - hide_output=hide_output, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - use_vt=use_vt, - python_shell=python_shell, - password=password, - encoded_cmd=encoded_cmd, - success_retcodes=success_retcodes, - **kwargs) + response = run( + cmd, + cwd=cwd, + stdin=stdin, + runas=runas, + shell="powershell", + env=env, + clean_env=clean_env, + template=template, + rstrip=rstrip, + umask=umask, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + hide_output=hide_output, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + use_vt=use_vt, + python_shell=python_shell, + password=password, + encoded_cmd=encoded_cmd, + success_retcodes=success_retcodes, + **kwargs + ) try: return salt.utils.json.loads(response) @@ -3643,31 +3719,33 @@ def powershell(cmd, return {} -def powershell_all(cmd, - cwd=None, - stdin=None, - runas=None, - shell=DEFAULT_SHELL, - env=None, - clean_env=False, - template=None, - rstrip=True, - umask=None, - output_encoding=None, - output_loglevel='debug', - quiet=False, - timeout=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - use_vt=False, - password=None, - depth=None, - encode_cmd=False, - force_list=False, - success_retcodes=None, - **kwargs): - ''' +def powershell_all( + cmd, + cwd=None, + stdin=None, + runas=None, + shell=DEFAULT_SHELL, + env=None, + clean_env=False, + template=None, + rstrip=True, + umask=None, + output_encoding=None, + output_loglevel="debug", + quiet=False, + timeout=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + use_vt=False, + password=None, + depth=None, + encode_cmd=False, + force_list=False, + success_retcodes=None, + **kwargs +): + """ Execute the passed PowerShell command and return a dictionary with a result field representing the output of the command, as well as other fields showing us what the PowerShell invocation wrote to ``stderr``, the process @@ -3905,108 +3983,108 @@ def powershell_all(cmd, .. code-block:: bash salt '*' cmd.powershell_all "dir mydirectory" force_list=True - ''' - if 'python_shell' in kwargs: - python_shell = kwargs.pop('python_shell') + """ + if "python_shell" in kwargs: + python_shell = kwargs.pop("python_shell") else: python_shell = True # Append PowerShell Object formatting - cmd += ' | ConvertTo-JSON' + cmd += " | ConvertTo-JSON" if depth is not None: - cmd += ' -Depth {0}'.format(depth) + cmd += " -Depth {0}".format(depth) if encode_cmd: # Convert the cmd to UTF-16LE without a BOM and base64 encode. # Just base64 encoding UTF-8 or including a BOM is not valid. - log.debug('Encoding PowerShell command \'%s\'', cmd) - cmd_utf16 = cmd.decode('utf-8').encode('utf-16le') + log.debug("Encoding PowerShell command '%s'", cmd) + cmd_utf16 = cmd.decode("utf-8").encode("utf-16le") cmd = base64.standard_b64encode(cmd_utf16) encoded_cmd = True else: encoded_cmd = False # Retrieve the response, while overriding shell with 'powershell' - response = run_all(cmd, - cwd=cwd, - stdin=stdin, - runas=runas, - shell='powershell', - env=env, - clean_env=clean_env, - template=template, - rstrip=rstrip, - umask=umask, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - quiet=quiet, - timeout=timeout, - reset_system_locale=reset_system_locale, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - use_vt=use_vt, - python_shell=python_shell, - password=password, - encoded_cmd=encoded_cmd, - success_retcodes=success_retcodes, - **kwargs) - stdoutput = response['stdout'] + response = run_all( + cmd, + cwd=cwd, + stdin=stdin, + runas=runas, + shell="powershell", + env=env, + clean_env=clean_env, + template=template, + rstrip=rstrip, + umask=umask, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + quiet=quiet, + timeout=timeout, + reset_system_locale=reset_system_locale, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + use_vt=use_vt, + python_shell=python_shell, + password=password, + encoded_cmd=encoded_cmd, + success_retcodes=success_retcodes, + **kwargs + ) + stdoutput = response["stdout"] # if stdoutput is the empty string and force_list is True we return an empty list # Otherwise we return response with no result key if not stdoutput: - response.pop('stdout') + response.pop("stdout") if force_list: - response['result'] = [] + response["result"] = [] return response # If we fail to parse stdoutput we will raise an exception try: result = salt.utils.json.loads(stdoutput) except Exception: # pylint: disable=broad-except - err_msg = "cmd.powershell_all " + \ - "cannot parse the Powershell output." + err_msg = "cmd.powershell_all " + "cannot parse the Powershell output." response["cmd"] = cmd - raise CommandExecutionError( - message=err_msg, - info=response - ) + raise CommandExecutionError(message=err_msg, info=response) response.pop("stdout") if type(result) is not list: if force_list: - response['result'] = [result] + response["result"] = [result] else: - response['result'] = result + response["result"] = result else: # result type is list so the force_list param has no effect - response['result'] = result + response["result"] = result return response -def run_bg(cmd, - cwd=None, - runas=None, - group=None, - shell=DEFAULT_SHELL, - python_shell=None, - env=None, - clean_env=False, - template=None, - umask=None, - timeout=None, - output_encoding=None, - output_loglevel='debug', - log_callback=None, - reset_system_locale=True, - ignore_retcode=False, - saltenv='base', - password=None, - prepend_path=None, - success_retcodes=None, - **kwargs): - r''' +def run_bg( + cmd, + cwd=None, + runas=None, + group=None, + shell=DEFAULT_SHELL, + python_shell=None, + env=None, + clean_env=False, + template=None, + umask=None, + timeout=None, + output_encoding=None, + output_loglevel="debug", + log_callback=None, + reset_system_locale=True, + ignore_retcode=False, + saltenv="base", + password=None, + prepend_path=None, + success_retcodes=None, + **kwargs +): + r""" .. versionadded: 2016.3.0 Execute the passed command in the background and return its PID @@ -4171,39 +4249,37 @@ def run_bg(cmd, .. code-block:: bash salt '*' cmd.run_bg cmd='ls -lR / | sed -e s/=/:/g > /tmp/dontwait' - ''' + """ - python_shell = _python_shell_default(python_shell, - kwargs.get('__pub_jid', '')) - res = _run(cmd, - stdin=None, - stderr=None, - stdout=None, - output_encoding=output_encoding, - output_loglevel=output_loglevel, - use_vt=None, - bg=True, - with_communicate=False, - rstrip=False, - runas=runas, - group=group, - shell=shell, - python_shell=python_shell, - cwd=cwd, - env=env, - clean_env=clean_env, - prepend_path=prepend_path, - template=template, - umask=umask, - log_callback=log_callback, - timeout=timeout, - reset_system_locale=reset_system_locale, - saltenv=saltenv, - password=password, - success_retcodes=success_retcodes, - **kwargs - ) + python_shell = _python_shell_default(python_shell, kwargs.get("__pub_jid", "")) + res = _run( + cmd, + stdin=None, + stderr=None, + stdout=None, + output_encoding=output_encoding, + output_loglevel=output_loglevel, + use_vt=None, + bg=True, + with_communicate=False, + rstrip=False, + runas=runas, + group=group, + shell=shell, + python_shell=python_shell, + cwd=cwd, + env=env, + clean_env=clean_env, + prepend_path=prepend_path, + template=template, + umask=umask, + log_callback=log_callback, + timeout=timeout, + reset_system_locale=reset_system_locale, + saltenv=saltenv, + password=password, + success_retcodes=success_retcodes, + **kwargs + ) - return { - 'pid': res['pid'] - } + return {"pid": res["pid"]} diff --git a/salt/modules/composer.py b/salt/modules/composer.py index 0b905778f73..e0caa9c3cea 100644 --- a/salt/modules/composer.py +++ b/salt/modules/composer.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Use composer to install PHP dependencies for a directory -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -14,35 +14,33 @@ import salt.utils.path from salt.exceptions import ( CommandExecutionError, CommandNotFoundError, - SaltInvocationError + SaltInvocationError, ) log = logging.getLogger(__name__) # Function alias to make sure not to shadow built-in's -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} def __virtual__(): - ''' + """ Always load - ''' + """ return True def _valid_composer(composer): - ''' + """ Validate the composer file is indeed there. - ''' + """ if salt.utils.path.which(composer): return True return False def did_composer_install(dir): - ''' + """ Test to see if the vendor directory exists in this directory dir @@ -53,29 +51,31 @@ def did_composer_install(dir): .. code-block:: bash salt '*' composer.did_composer_install /var/www/application - ''' + """ lockFile = "{0}/vendor".format(dir) if os.path.exists(lockFile): return True return False -def _run_composer(action, - directory=None, - composer=None, - php=None, - runas=None, - prefer_source=None, - prefer_dist=None, - no_scripts=None, - no_plugins=None, - optimize=None, - no_dev=None, - quiet=False, - composer_home='/root', - extra_flags=None, - env=None): - ''' +def _run_composer( + action, + directory=None, + composer=None, + php=None, + runas=None, + prefer_source=None, + prefer_dist=None, + no_scripts=None, + no_plugins=None, + optimize=None, + no_dev=None, + quiet=False, + composer_home="/root", + extra_flags=None, + env=None, +): + """ Run PHP's composer with a specific action. If composer has not been installed globally making it available in the @@ -130,31 +130,32 @@ def _run_composer(action, env A list of environment variables to be set prior to execution. - ''' + """ if composer is not None: if php is None: - php = 'php' + php = "php" else: - composer = 'composer' + composer = "composer" # Validate Composer is there if not _valid_composer(composer): raise CommandNotFoundError( - '\'composer.{0}\' is not available. Couldn\'t find \'{1}\'.' - .format(action, composer) + "'composer.{0}' is not available. Couldn't find '{1}'.".format( + action, composer + ) ) if action is None: - raise SaltInvocationError('The \'action\' argument is required') + raise SaltInvocationError("The 'action' argument is required") # Don't need a dir for the 'selfupdate' action; all other actions do need a dir - if directory is None and action != 'selfupdate': + if directory is None and action != "selfupdate": raise SaltInvocationError( - 'The \'directory\' argument is required for composer.{0}'.format(action) + "The 'directory' argument is required for composer.{0}".format(action) ) # Base Settings - cmd = [composer, action, '--no-interaction', '--no-ansi'] + cmd = [composer, action, "--no-interaction", "--no-ansi"] if extra_flags is not None: cmd.extend(salt.utils.args.shlex_split(extra_flags)) @@ -165,43 +166,40 @@ def _run_composer(action, # Add Working Dir if directory is not None: - cmd.extend(['--working-dir', directory]) + cmd.extend(["--working-dir", directory]) # Other Settings if quiet is True: - cmd.append('--quiet') + cmd.append("--quiet") if no_dev is True: - cmd.append('--no-dev') + cmd.append("--no-dev") if prefer_source is True: - cmd.append('--prefer-source') + cmd.append("--prefer-source") if prefer_dist is True: - cmd.append('--prefer-dist') + cmd.append("--prefer-dist") if no_scripts is True: - cmd.append('--no-scripts') + cmd.append("--no-scripts") if no_plugins is True: - cmd.append('--no-plugins') + cmd.append("--no-plugins") if optimize is True: - cmd.append('--optimize-autoloader') + cmd.append("--optimize-autoloader") if env is not None: env = salt.utils.data.repack_dictlist(env) - env['COMPOSER_HOME'] = composer_home + env["COMPOSER_HOME"] = composer_home else: - env = {'COMPOSER_HOME': composer_home} + env = {"COMPOSER_HOME": composer_home} - result = __salt__['cmd.run_all'](cmd, - runas=runas, - env=env, - python_shell=False) + result = __salt__["cmd.run_all"](cmd, runas=runas, env=env, python_shell=False) - if result['retcode'] != 0: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0: + raise CommandExecutionError(result["stderr"]) if quiet is True: return True @@ -209,20 +207,22 @@ def _run_composer(action, return result -def install(directory, - composer=None, - php=None, - runas=None, - prefer_source=None, - prefer_dist=None, - no_scripts=None, - no_plugins=None, - optimize=None, - no_dev=None, - quiet=False, - composer_home='/root', - env=None): - ''' +def install( + directory, + composer=None, + php=None, + runas=None, + prefer_source=None, + prefer_dist=None, + no_scripts=None, + no_plugins=None, + optimize=None, + no_dev=None, + quiet=False, + composer_home="/root", + env=None, +): + """ Install composer dependencies for a directory. If composer has not been installed globally making it available in the @@ -279,38 +279,42 @@ def install(directory, salt '*' composer.install /var/www/application \ no_dev=True optimize=True - ''' - result = _run_composer('install', - directory=directory, - composer=composer, - php=php, - runas=runas, - prefer_source=prefer_source, - prefer_dist=prefer_dist, - no_scripts=no_scripts, - no_plugins=no_plugins, - optimize=optimize, - no_dev=no_dev, - quiet=quiet, - composer_home=composer_home, - env=env) + """ + result = _run_composer( + "install", + directory=directory, + composer=composer, + php=php, + runas=runas, + prefer_source=prefer_source, + prefer_dist=prefer_dist, + no_scripts=no_scripts, + no_plugins=no_plugins, + optimize=optimize, + no_dev=no_dev, + quiet=quiet, + composer_home=composer_home, + env=env, + ) return result -def update(directory, - composer=None, - php=None, - runas=None, - prefer_source=None, - prefer_dist=None, - no_scripts=None, - no_plugins=None, - optimize=None, - no_dev=None, - quiet=False, - composer_home='/root', - env=None): - ''' +def update( + directory, + composer=None, + php=None, + runas=None, + prefer_source=None, + prefer_dist=None, + no_scripts=None, + no_plugins=None, + optimize=None, + no_dev=None, + quiet=False, + composer_home="/root", + env=None, +): + """ Update composer dependencies for a directory. If `composer install` has not yet been run, this runs `composer install` @@ -370,31 +374,29 @@ def update(directory, salt '*' composer.update /var/www/application \ no_dev=True optimize=True - ''' - result = _run_composer('update', - directory=directory, - extra_flags='--no-progress', - composer=composer, - php=php, - runas=runas, - prefer_source=prefer_source, - prefer_dist=prefer_dist, - no_scripts=no_scripts, - no_plugins=no_plugins, - optimize=optimize, - no_dev=no_dev, - quiet=quiet, - composer_home=composer_home, - env=env) + """ + result = _run_composer( + "update", + directory=directory, + extra_flags="--no-progress", + composer=composer, + php=php, + runas=runas, + prefer_source=prefer_source, + prefer_dist=prefer_dist, + no_scripts=no_scripts, + no_plugins=no_plugins, + optimize=optimize, + no_dev=no_dev, + quiet=quiet, + composer_home=composer_home, + env=env, + ) return result -def selfupdate(composer=None, - php=None, - runas=None, - quiet=False, - composer_home='/root'): - ''' +def selfupdate(composer=None, php=None, runas=None, quiet=False, composer_home="/root"): + """ Update composer itself. If composer has not been installed globally making it available in the @@ -424,12 +426,14 @@ def selfupdate(composer=None, .. code-block:: bash salt '*' composer.selfupdate - ''' - result = _run_composer('selfupdate', - extra_flags='--no-progress', - composer=composer, - php=php, - runas=runas, - quiet=quiet, - composer_home=composer_home) + """ + result = _run_composer( + "selfupdate", + extra_flags="--no-progress", + composer=composer, + php=php, + runas=runas, + quiet=quiet, + composer_home=composer_home, + ) return result diff --git a/salt/modules/config.py b/salt/modules/config.py index ffb62841bb4..49e9a404886 100644 --- a/salt/modules/config.py +++ b/salt/modules/config.py @@ -1,93 +1,104 @@ # -*- coding: utf-8 -*- -''' +""" Return config information -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import fnmatch -import os import logging +import os + +import salt._compat # Import salt libs import salt.config +import salt.syspaths as syspaths import salt.utils.data import salt.utils.dictupdate import salt.utils.files import salt.utils.platform -try: - # Gated for salt-ssh (salt.utils.cloud imports msgpack) - import salt.utils.cloud - HAS_CLOUD = True -except ImportError: - HAS_CLOUD = False - -import salt._compat -import salt.syspaths as syspaths import salt.utils.sdb as sdb # Import 3rd-party libs from salt.ext import six +try: + # Gated for salt-ssh (salt.utils.cloud imports msgpack) + import salt.utils.cloud + + HAS_CLOUD = True +except ImportError: + HAS_CLOUD = False + + if salt.utils.platform.is_windows(): _HOSTS_FILE = os.path.join( - os.environ['SystemRoot'], 'System32', 'drivers', 'etc', 'hosts') + os.environ["SystemRoot"], "System32", "drivers", "etc", "hosts" + ) else: - _HOSTS_FILE = os.path.join(os.sep, 'etc', 'hosts') + _HOSTS_FILE = os.path.join(os.sep, "etc", "hosts") log = logging.getLogger(__name__) -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] # Set up the default values for all systems -DEFAULTS = {'mongo.db': 'salt', - 'mongo.password': '', - 'mongo.port': 27017, - 'mongo.user': '', - 'redis.db': '0', - 'redis.host': 'salt', - 'redis.port': 6379, - 'test.foo': 'unconfigured', - 'ca.cert_base_path': '/etc/pki', - 'solr.cores': [], - 'solr.host': 'localhost', - 'solr.port': '8983', - 'solr.baseurl': '/solr', - 'solr.type': 'master', - 'solr.request_timeout': None, - 'solr.init_script': '/etc/rc.d/solr', - 'solr.dih.import_options': {'clean': False, 'optimize': True, - 'commit': True, 'verbose': False}, - 'solr.backup_path': None, - 'solr.num_backups': 1, - 'poudriere.config': '/usr/local/etc/poudriere.conf', - 'poudriere.config_dir': '/usr/local/etc/poudriere.d', - 'ldap.uri': '', - 'ldap.server': 'localhost', - 'ldap.port': '389', - 'ldap.tls': False, - 'ldap.no_verify': False, - 'ldap.anonymous': True, - 'ldap.scope': 2, - 'ldap.attrs': None, - 'ldap.binddn': '', - 'ldap.bindpw': '', - 'hosts.file': _HOSTS_FILE, - 'aliases.file': '/etc/aliases', - 'virt': {'tunnel': False, - 'images': os.path.join(syspaths.SRV_ROOT_DIR, 'salt-images')}, - - 'docker.exec_driver': 'docker-exec', - 'docker.compare_container_networks': { - 'static': ['Aliases', 'Links', 'IPAMConfig'], - 'automatic': ['IPAddress', 'Gateway', - 'GlobalIPv6Address', 'IPv6Gateway']}, - } +DEFAULTS = { + "mongo.db": "salt", + "mongo.password": "", + "mongo.port": 27017, + "mongo.user": "", + "redis.db": "0", + "redis.host": "salt", + "redis.port": 6379, + "test.foo": "unconfigured", + "ca.cert_base_path": "/etc/pki", + "solr.cores": [], + "solr.host": "localhost", + "solr.port": "8983", + "solr.baseurl": "/solr", + "solr.type": "master", + "solr.request_timeout": None, + "solr.init_script": "/etc/rc.d/solr", + "solr.dih.import_options": { + "clean": False, + "optimize": True, + "commit": True, + "verbose": False, + }, + "solr.backup_path": None, + "solr.num_backups": 1, + "poudriere.config": "/usr/local/etc/poudriere.conf", + "poudriere.config_dir": "/usr/local/etc/poudriere.d", + "ldap.uri": "", + "ldap.server": "localhost", + "ldap.port": "389", + "ldap.tls": False, + "ldap.no_verify": False, + "ldap.anonymous": True, + "ldap.scope": 2, + "ldap.attrs": None, + "ldap.binddn": "", + "ldap.bindpw": "", + "hosts.file": _HOSTS_FILE, + "aliases.file": "/etc/aliases", + "virt": { + "tunnel": False, + "images": os.path.join(syspaths.SRV_ROOT_DIR, "salt-images"), + }, + "docker.exec_driver": "docker-exec", + "docker.compare_container_networks": { + "static": ["Aliases", "Links", "IPAMConfig"], + "automatic": ["IPAddress", "Gateway", "GlobalIPv6Address", "IPv6Gateway"], + }, +} -def backup_mode(backup=''): - ''' +def backup_mode(backup=""): + """ Return the backup mode CLI Example: @@ -95,14 +106,14 @@ def backup_mode(backup=''): .. code-block:: bash salt '*' config.backup_mode - ''' + """ if backup: return backup - return option('backup_mode') + return option("backup_mode") def manage_mode(mode): - ''' + """ Return a mode value, normalized to a string CLI Example: @@ -110,7 +121,7 @@ def manage_mode(mode): .. code-block:: bash salt '*' config.manage_mode - ''' + """ # config.manage_mode should no longer be invoked from the __salt__ dunder # in Salt code, this function is only being left here for backwards # compatibility. @@ -118,7 +129,7 @@ def manage_mode(mode): def valid_fileproto(uri): - ''' + """ Returns a boolean value based on whether or not the URI passed has a valid remote file protocol designation @@ -127,23 +138,21 @@ def valid_fileproto(uri): .. code-block:: bash salt '*' config.valid_fileproto salt://path/to/file - ''' - return ( - six.moves.urllib.parse.urlparse(uri).scheme in - salt.utils.files.VALID_PROTOS - ) + """ + return six.moves.urllib.parse.urlparse(uri).scheme in salt.utils.files.VALID_PROTOS def option( - value, - default=None, - omit_opts=False, - omit_grains=False, - omit_pillar=False, - omit_master=False, - omit_all=False, - wildcard=False): - ''' + value, + default=None, + omit_opts=False, + omit_grains=False, + omit_pillar=False, + omit_master=False, + omit_all=False, + wildcard=False, +): + """ Returns the setting for the specified config value. The priority for matches is the same as in :py:func:`config.get `, only this function does not recurse into nested data structures. Another @@ -196,12 +205,12 @@ def option( .. code-block:: bash salt '*' config.option redis.host - ''' + """ if omit_all: omit_opts = omit_grains = omit_pillar = omit_master = True if default is None: - default = '' if not wildcard else {} + default = "" if not wildcard else {} if not wildcard: if not omit_opts: @@ -214,8 +223,8 @@ def option( if value in __pillar__: return __pillar__[value] if not omit_master: - if value in __pillar__.get('master', {}): - return __pillar__['master'][value] + if value in __pillar__.get("master", {}): + return __pillar__["master"][value] if value in DEFAULTS: return DEFAULTS[value] @@ -225,29 +234,24 @@ def option( # We need to do the checks in the reverse order so that minion opts # takes precedence ret = {} - for omit, data in ((omit_master, __pillar__.get('master', {})), - (omit_pillar, __pillar__), - (omit_grains, __grains__), - (omit_opts, __opts__)): + for omit, data in ( + (omit_master, __pillar__.get("master", {})), + (omit_pillar, __pillar__), + (omit_grains, __grains__), + (omit_opts, __opts__), + ): if not omit: - ret.update( - {x: data[x] for x in fnmatch.filter(data, value)} - ) + ret.update({x: data[x] for x in fnmatch.filter(data, value)}) # Check the DEFAULTS as well to see if the pattern matches it - for item in (x for x in fnmatch.filter(DEFAULTS, value) - if x not in ret): + for item in (x for x in fnmatch.filter(DEFAULTS, value) if x not in ret): ret[item] = DEFAULTS[item] # If no matches, return the default return ret or default -def merge(value, - default='', - omit_opts=False, - omit_master=False, - omit_pillar=False): - ''' +def merge(value, default="", omit_opts=False, omit_master=False, omit_pillar=False): + """ Retrieves an option based on key, merging all matches. Same as ``option()`` except that it merges all matches, rather than taking @@ -258,7 +262,7 @@ def merge(value, .. code-block:: bash salt '*' config.merge schedule - ''' + """ ret = None if not omit_opts: if value in __opts__: @@ -266,8 +270,8 @@ def merge(value, if isinstance(ret, six.string_types): return ret if not omit_master: - if value in __pillar__.get('master', {}): - tmp = __pillar__['master'][value] + if value in __pillar__.get("master", {}): + tmp = __pillar__["master"][value] if ret is None: ret = tmp if isinstance(ret, six.string_types): @@ -275,8 +279,7 @@ def merge(value, elif isinstance(ret, dict) and isinstance(tmp, dict): tmp.update(ret) ret = tmp - elif isinstance(ret, (list, tuple)) and isinstance(tmp, - (list, tuple)): + elif isinstance(ret, (list, tuple)) and isinstance(tmp, (list, tuple)): ret = list(ret) + list(tmp) if not omit_pillar: if value in __pillar__: @@ -288,8 +291,7 @@ def merge(value, elif isinstance(ret, dict) and isinstance(tmp, dict): tmp.update(ret) ret = tmp - elif isinstance(ret, (list, tuple)) and isinstance(tmp, - (list, tuple)): + elif isinstance(ret, (list, tuple)) and isinstance(tmp, (list, tuple)): ret = list(ret) + list(tmp) if ret is None and value in DEFAULTS: return DEFAULTS[value] @@ -298,9 +300,17 @@ def merge(value, return ret -def get(key, default='', delimiter=':', merge=None, omit_opts=False, - omit_pillar=False, omit_master=False, omit_grains=False): - ''' +def get( + key, + default="", + delimiter=":", + merge=None, + omit_opts=False, + omit_pillar=False, + omit_master=False, + omit_grains=False, +): + """ .. versionadded: 0.14.0 Attempt to retrieve the named value from the minion config file, pillar, @@ -437,78 +447,77 @@ def get(key, default='', delimiter=':', merge=None, omit_opts=False, salt '*' config.get pkg:apache salt '*' config.get lxc.container_profile:centos merge=recurse - ''' + """ if merge is None: if not omit_opts: ret = salt.utils.data.traverse_dict_and_list( - __opts__, - key, - '_|-', - delimiter=delimiter) - if ret != '_|-': + __opts__, key, "_|-", delimiter=delimiter + ) + if ret != "_|-": return sdb.sdb_get(ret, __opts__) if not omit_grains: ret = salt.utils.data.traverse_dict_and_list( - __grains__, - key, - '_|-', - delimiter) - if ret != '_|-': + __grains__, key, "_|-", delimiter + ) + if ret != "_|-": return sdb.sdb_get(ret, __opts__) if not omit_pillar: ret = salt.utils.data.traverse_dict_and_list( - __pillar__, - key, - '_|-', - delimiter=delimiter) - if ret != '_|-': + __pillar__, key, "_|-", delimiter=delimiter + ) + if ret != "_|-": return sdb.sdb_get(ret, __opts__) if not omit_master: ret = salt.utils.data.traverse_dict_and_list( - __pillar__.get('master', {}), - key, - '_|-', - delimiter=delimiter) - if ret != '_|-': + __pillar__.get("master", {}), key, "_|-", delimiter=delimiter + ) + if ret != "_|-": return sdb.sdb_get(ret, __opts__) ret = salt.utils.data.traverse_dict_and_list( - DEFAULTS, - key, - '_|-', - delimiter=delimiter) - log.debug("key: %s, ret: %s", key, ret) - if ret != '_|-': + DEFAULTS, key, "_|-", delimiter=delimiter + ) + if ret != "_|-": return sdb.sdb_get(ret, __opts__) else: - if merge not in ('recurse', 'overwrite'): - log.warning('Unsupported merge strategy \'{0}\'. Falling back ' - 'to \'recurse\'.'.format(merge)) - merge = 'recurse' + if merge not in ("recurse", "overwrite"): + log.warning( + "Unsupported merge strategy '{0}'. Falling back " + "to 'recurse'.".format(merge) + ) + merge = "recurse" - merge_lists = salt.config.master_config('/etc/salt/master').get('pillar_merge_lists') + merge_lists = salt.config.master_config("/etc/salt/master").get( + "pillar_merge_lists" + ) data = copy.copy(DEFAULTS) - data = salt.utils.dictupdate.merge(data, __pillar__.get('master', {}), strategy=merge, merge_lists=merge_lists) - data = salt.utils.dictupdate.merge(data, __pillar__, strategy=merge, merge_lists=merge_lists) - data = salt.utils.dictupdate.merge(data, __grains__, strategy=merge, merge_lists=merge_lists) - data = salt.utils.dictupdate.merge(data, __opts__, strategy=merge, merge_lists=merge_lists) + data = salt.utils.dictupdate.merge( + data, __pillar__.get("master", {}), strategy=merge, merge_lists=merge_lists + ) + data = salt.utils.dictupdate.merge( + data, __pillar__, strategy=merge, merge_lists=merge_lists + ) + data = salt.utils.dictupdate.merge( + data, __grains__, strategy=merge, merge_lists=merge_lists + ) + data = salt.utils.dictupdate.merge( + data, __opts__, strategy=merge, merge_lists=merge_lists + ) ret = salt.utils.data.traverse_dict_and_list( - data, - key, - '_|-', - delimiter=delimiter) - if ret != '_|-': + data, key, "_|-", delimiter=delimiter + ) + if ret != "_|-": return sdb.sdb_get(ret, __opts__) return default def dot_vals(value): - ''' + """ Pass in a configuration value that should be preceded by the module name and a dot, this will return a list of all read key/value pairs @@ -517,19 +526,19 @@ def dot_vals(value): .. code-block:: bash salt '*' config.dot_vals host - ''' + """ ret = {} - for key, val in six.iteritems(__pillar__.get('master', {})): - if key.startswith('{0}.'.format(value)): + for key, val in six.iteritems(__pillar__.get("master", {})): + if key.startswith("{0}.".format(value)): ret[key] = val for key, val in six.iteritems(__opts__): - if key.startswith('{0}.'.format(value)): + if key.startswith("{0}.".format(value)): ret[key] = val return ret def gather_bootstrap_script(bootstrap=None): - ''' + """ Download the salt-bootstrap script, and return its location bootstrap @@ -540,16 +549,16 @@ def gather_bootstrap_script(bootstrap=None): .. code-block:: bash salt '*' config.gather_bootstrap_script - ''' + """ if not HAS_CLOUD: - return False, 'config.gather_bootstrap_script is unavailable' + return False, "config.gather_bootstrap_script is unavailable" ret = salt.utils.cloud.update_bootstrap(__opts__, url=bootstrap) - if 'Success' in ret and len(ret['Success']['Files updated']) > 0: - return ret['Success']['Files updated'][0] + if "Success" in ret and len(ret["Success"]["Files updated"]) > 0: + return ret["Success"]["Files updated"][0] def items(): - ''' + """ Return the complete config from the currently running minion process. This includes defaults for values not set in the config file. @@ -558,5 +567,5 @@ def items(): .. code-block:: bash salt '*' config.items - ''' + """ return __opts__ diff --git a/salt/modules/consul.py b/salt/modules/consul.py index 1b360be1ba0..a9f2c2e3c7c 100644 --- a/salt/modules/consul.py +++ b/salt/modules/consul.py @@ -1,19 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" Interact with Consul https://www.consul.io -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import logging # Import salt libs import salt.utils.http import salt.utils.json +from salt.exceptions import SaltInvocationError # Import 3rd-party libs from salt.ext import six @@ -21,40 +23,39 @@ from salt.ext.six.moves import http_client, urllib log = logging.getLogger(__name__) -from salt.exceptions import SaltInvocationError # Don't shadow built-ins. -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} -__virtualname__ = 'consul' +__virtualname__ = "consul" def _get_config(): - ''' + """ Retrieve Consul configuration - ''' - return __salt__['config.get']('consul.url') or \ - __salt__['config.get']('consul:url') + """ + return __salt__["config.get"]("consul.url") or __salt__["config.get"]("consul:url") def _get_token(): - ''' + """ Retrieve Consul configuration - ''' - return __salt__['config.get']('consul.token') or \ - __salt__['config.get']('consul:token') + """ + return __salt__["config.get"]("consul.token") or __salt__["config.get"]( + "consul:token" + ) -def _query(function, - consul_url, - token=None, - method='GET', - api_version='v1', - data=None, - query_params=None): - ''' +def _query( + function, + consul_url, + token=None, + method="GET", + api_version="v1", + data=None, + query_params=None, +): + """ Consul object method function to construct and execute on the API URL. :param api_url: The Consul api url. @@ -63,22 +64,21 @@ def _query(function, :param method: The HTTP method, e.g. GET or POST. :param data: The data to be sent for POST method. This param is ignored for GET requests. :return: The json response from the API call or False. - ''' + """ if not query_params: query_params = {} - ret = {'data': '', - 'res': True} + ret = {"data": "", "res": True} if not token: token = _get_token() headers = {"X-Consul-Token": token, "Content-Type": "application/json"} - base_url = urllib.parse.urljoin(consul_url, '{0}/'.format(api_version)) + base_url = urllib.parse.urljoin(consul_url, "{0}/".format(api_version)) url = urllib.parse.urljoin(base_url, function, False) - if method == 'GET': + if method == "GET": data = None else: if data is None: @@ -96,25 +96,25 @@ def _query(function, opts=__opts__, ) - if result.get('status', None) == http_client.OK: - ret['data'] = result.get('dict', result) - ret['res'] = True - elif result.get('status', None) == http_client.NO_CONTENT: - ret['res'] = False - elif result.get('status', None) == http_client.NOT_FOUND: - ret['data'] = 'Key not found.' - ret['res'] = False + if result.get("status", None) == http_client.OK: + ret["data"] = result.get("dict", result) + ret["res"] = True + elif result.get("status", None) == http_client.NO_CONTENT: + ret["res"] = False + elif result.get("status", None) == http_client.NOT_FOUND: + ret["data"] = "Key not found." + ret["res"] = False else: if result: - ret['data'] = result - ret['res'] = True + ret["data"] = result + ret["res"] = True else: - ret['res'] = False + ret["res"] = False return ret def list_(consul_url=None, token=None, key=None, **kwargs): - ''' + """ List keys in Consul :param consul_url: The Consul server URL. @@ -128,40 +128,39 @@ def list_(consul_url=None, token=None, key=None, **kwargs): salt '*' consul.list salt '*' consul.list key='web' - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret query_params = {} - if 'recurse' in kwargs: - query_params['recurse'] = 'True' + if "recurse" in kwargs: + query_params["recurse"] = "True" # No key so recurse and show all values if not key: - query_params['recurse'] = 'True' - function = 'kv/' + query_params["recurse"] = "True" + function = "kv/" else: - function = 'kv/{0}'.format(key) + function = "kv/{0}".format(key) - query_params['keys'] = 'True' - query_params['separator'] = '/' - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + query_params["keys"] = "True" + query_params["separator"] = "/" + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def get(consul_url=None, key=None, token=None, recurse=False, decode=False, raw=False): - ''' + """ Get key from Consul :param consul_url: The Consul server URL. @@ -190,42 +189,41 @@ def get(consul_url=None, key=None, token=None, recurse=False, decode=False, raw= By default Consult will return other information about the key, the raw option will return only the raw value. - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not key: raise SaltInvocationError('Required argument "key" is missing.') query_params = {} - function = 'kv/{0}'.format(key) + function = "kv/{0}".format(key) if recurse: - query_params['recurse'] = 'True' + query_params["recurse"] = "True" if raw: - query_params['raw'] = True - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + query_params["raw"] = True + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) - if ret['res']: + if ret["res"]: if decode: - for item in ret['data']: - if item['Value'] is not None: - item['Value'] = base64.b64decode(item['Value']) + for item in ret["data"]: + if item["Value"] is not None: + item["Value"] = base64.b64decode(item["Value"]) else: - item['Value'] = "" + item["Value"] = "" return ret def put(consul_url=None, token=None, key=None, value=None, **kwargs): - ''' + """ Put values into Consul :param consul_url: The Consul server URL. @@ -252,101 +250,108 @@ def put(consul_url=None, token=None, key=None, value=None, **kwargs): salt '*' consul.put key='web/key1' value="Hello there" release='d5d371f4-c380-5280-12fd-8810be175592' - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not key: raise SaltInvocationError('Required argument "key" is missing.') # Invalid to specified these together - conflicting_args = ['cas', 'release', 'acquire'] + conflicting_args = ["cas", "release", "acquire"] for _l1 in conflicting_args: for _l2 in conflicting_args: if _l1 in kwargs and _l2 in kwargs and _l1 != _l2: - raise SaltInvocationError('Using arguments `{0}` and `{1}`' - ' together is invalid.'.format(_l1, _l2)) + raise SaltInvocationError( + "Using arguments `{0}` and `{1}`" + " together is invalid.".format(_l1, _l2) + ) query_params = {} available_sessions = session_list(consul_url=consul_url, return_list=True) _current = get(consul_url=consul_url, key=key) - if 'flags' in kwargs: - if kwargs['flags'] >= 0 and kwargs['flags'] <= 2**64: - query_params['flags'] = kwargs['flags'] + if "flags" in kwargs: + if kwargs["flags"] >= 0 and kwargs["flags"] <= 2 ** 64: + query_params["flags"] = kwargs["flags"] - if 'cas' in kwargs: - if _current['res']: - if kwargs['cas'] == 0: - ret['message'] = ('Key {0} exists, index ' - 'must be non-zero.'.format(key)) - ret['res'] = False + if "cas" in kwargs: + if _current["res"]: + if kwargs["cas"] == 0: + ret["message"] = "Key {0} exists, index " "must be non-zero.".format( + key + ) + ret["res"] = False return ret - if kwargs['cas'] != _current['data']['ModifyIndex']: - ret['message'] = ('Key {0} exists, but indexes ' - 'do not match.'.format(key)) - ret['res'] = False + if kwargs["cas"] != _current["data"]["ModifyIndex"]: + ret["message"] = "Key {0} exists, but indexes " "do not match.".format( + key + ) + ret["res"] = False return ret - query_params['cas'] = kwargs['cas'] + query_params["cas"] = kwargs["cas"] else: - ret['message'] = ('Key {0} does not exists, ' - 'CAS argument can not be used.'.format(key)) - ret['res'] = False + ret[ + "message" + ] = "Key {0} does not exists, " "CAS argument can not be used.".format(key) + ret["res"] = False return ret - if 'acquire' in kwargs: - if kwargs['acquire'] not in available_sessions: - ret['message'] = '{0} is not a valid session.'.format(kwargs['acquire']) - ret['res'] = False + if "acquire" in kwargs: + if kwargs["acquire"] not in available_sessions: + ret["message"] = "{0} is not a valid session.".format(kwargs["acquire"]) + ret["res"] = False return ret - query_params['acquire'] = kwargs['acquire'] + query_params["acquire"] = kwargs["acquire"] - if 'release' in kwargs: - if _current['res']: - if 'Session' in _current['data']: - if _current['data']['Session'] == kwargs['release']: - query_params['release'] = kwargs['release'] + if "release" in kwargs: + if _current["res"]: + if "Session" in _current["data"]: + if _current["data"]["Session"] == kwargs["release"]: + query_params["release"] = kwargs["release"] else: - ret['message'] = '{0} locked by another session.'.format(key) - ret['res'] = False + ret["message"] = "{0} locked by another session.".format(key) + ret["res"] = False return ret else: - ret['message'] = '{0} is not a valid session.'.format(kwargs['acquire']) - ret['res'] = False + ret["message"] = "{0} is not a valid session.".format(kwargs["acquire"]) + ret["res"] = False else: - log.error('Key {0} does not exist. Skipping release.') + log.error("Key {0} does not exist. Skipping release.") data = value - function = 'kv/{0}'.format(key) - method = 'PUT' - ret = _query(consul_url=consul_url, - token=token, - function=function, - method=method, - data=data, - query_params=query_params) + function = "kv/{0}".format(key) + method = "PUT" + ret = _query( + consul_url=consul_url, + token=token, + function=function, + method=method, + data=data, + query_params=query_params, + ) - if ret['res']: - ret['res'] = True - ret['data'] = 'Added key {0} with value {1}.'.format(key, value) + if ret["res"]: + ret["res"] = True + ret["data"] = "Added key {0} with value {1}.".format(key, value) else: - ret['res'] = False - ret['data'] = 'Unable to add key {0} with value {1}.'.format(key, value) + ret["res"] = False + ret["data"] = "Unable to add key {0} with value {1}.".format(key, value) return ret def delete(consul_url=None, token=None, key=None, **kwargs): - ''' + """ Delete values from Consul :param consul_url: The Consul server URL. @@ -363,14 +368,14 @@ def delete(consul_url=None, token=None, key=None, **kwargs): salt '*' consul.delete key='web' salt '*' consul.delete key='web' recurse='True' - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not key: @@ -378,36 +383,40 @@ def delete(consul_url=None, token=None, key=None, **kwargs): query_params = {} - if 'recurse' in kwargs: - query_params['recurse'] = True + if "recurse" in kwargs: + query_params["recurse"] = True - if 'cas' in kwargs: - if kwargs['cas'] > 0: - query_params['cas'] = kwargs['cas'] + if "cas" in kwargs: + if kwargs["cas"] > 0: + query_params["cas"] = kwargs["cas"] else: - ret['message'] = ('Check and Set Operation ', - 'value must be greater than 0.') - ret['res'] = False + ret["message"] = ( + "Check and Set Operation ", + "value must be greater than 0.", + ) + ret["res"] = False return ret - function = 'kv/{0}'.format(key) - ret = _query(consul_url=consul_url, - token=token, - function=function, - method='DELETE', - query_params=query_params) + function = "kv/{0}".format(key) + ret = _query( + consul_url=consul_url, + token=token, + function=function, + method="DELETE", + query_params=query_params, + ) - if ret['res']: - ret['res'] = True - ret['message'] = 'Deleted key {0}.'.format(key) + if ret["res"]: + ret["res"] = True + ret["message"] = "Deleted key {0}.".format(key) else: - ret['res'] = False - ret['message'] = 'Unable to delete key {0}.'.format(key) + ret["res"] = False + ret["message"] = "Unable to delete key {0}.".format(key) return ret def agent_checks(consul_url=None, token=None): - ''' + """ Returns the checks the local agent is managing :param consul_url: The Consul server URL. @@ -419,26 +428,23 @@ def agent_checks(consul_url=None, token=None): salt '*' consul.agent_checks - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - function = 'agent/checks' - ret = _query(consul_url=consul_url, - function=function, - token=token, - method='GET') + function = "agent/checks" + ret = _query(consul_url=consul_url, function=function, token=token, method="GET") return ret def agent_services(consul_url=None, token=None): - ''' + """ Returns the services the local agent is managing :param consul_url: The Consul server URL. @@ -450,26 +456,23 @@ def agent_services(consul_url=None, token=None): salt '*' consul.agent_services - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - function = 'agent/services' - ret = _query(consul_url=consul_url, - function=function, - token=token, - method='GET') + function = "agent/services" + ret = _query(consul_url=consul_url, function=function, token=token, method="GET") return ret def agent_members(consul_url=None, token=None, **kwargs): - ''' + """ Returns the members as seen by the local serf agent :param consul_url: The Consul server URL. @@ -481,31 +484,33 @@ def agent_members(consul_url=None, token=None, **kwargs): salt '*' consul.agent_members - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'wan' in kwargs: - query_params['wan'] = kwargs['wan'] + if "wan" in kwargs: + query_params["wan"] = kwargs["wan"] - function = 'agent/members' - ret = _query(consul_url=consul_url, - function=function, - token=token, - method='GET', - query_params=query_params) + function = "agent/members" + ret = _query( + consul_url=consul_url, + function=function, + token=token, + method="GET", + query_params=query_params, + ) return ret def agent_self(consul_url=None, token=None): - ''' + """ Returns the local node configuration :param consul_url: The Consul server URL. @@ -517,28 +522,30 @@ def agent_self(consul_url=None, token=None): salt '*' consul.agent_self - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - function = 'agent/self' - ret = _query(consul_url=consul_url, - function=function, - token=token, - method='GET', - query_params=query_params) + function = "agent/self" + ret = _query( + consul_url=consul_url, + function=function, + token=token, + method="GET", + query_params=query_params, + ) return ret def agent_maintenance(consul_url=None, token=None, **kwargs): - ''' + """ Manages node maintenance mode :param consul_url: The Consul server URL. @@ -557,45 +564,46 @@ def agent_maintenance(consul_url=None, token=None, **kwargs): salt '*' consul.agent_maintenance enable='False' reason='Upgrade in progress' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'enable' in kwargs: - query_params['enable'] = kwargs['enable'] + if "enable" in kwargs: + query_params["enable"] = kwargs["enable"] else: - ret['message'] = 'Required parameter "enable" is missing.' - ret['res'] = False + ret["message"] = 'Required parameter "enable" is missing.' + ret["res"] = False return ret - if 'reason' in kwargs: - query_params['reason'] = kwargs['reason'] + if "reason" in kwargs: + query_params["reason"] = kwargs["reason"] - function = 'agent/maintenance' - res = _query(consul_url=consul_url, - function=function, - token=token, - method='PUT', - query_params=query_params) - if res['res']: - ret['res'] = True - ret['message'] = ('Agent maintenance mode ' - '{0}ed.'.format(kwargs['enable'])) + function = "agent/maintenance" + res = _query( + consul_url=consul_url, + function=function, + token=token, + method="PUT", + query_params=query_params, + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Agent maintenance mode " "{0}ed.".format(kwargs["enable"]) else: - ret['res'] = True - ret['message'] = 'Unable to change maintenance mode for agent.' + ret["res"] = True + ret["message"] = "Unable to change maintenance mode for agent." return ret def agent_join(consul_url=None, token=None, address=None, **kwargs): - ''' + """ Triggers the local agent to join a node :param consul_url: The Consul server URL. @@ -609,40 +617,42 @@ def agent_join(consul_url=None, token=None, address=None, **kwargs): salt '*' consul.agent_join address='192.168.1.1' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not address: raise SaltInvocationError('Required argument "address" is missing.') - if 'wan' in kwargs: - query_params['wan'] = kwargs['wan'] + if "wan" in kwargs: + query_params["wan"] = kwargs["wan"] - function = 'agent/join/{0}'.format(address) - res = _query(consul_url=consul_url, - function=function, - token=token, - method='GET', - query_params=query_params) - if res['res']: - ret['res'] = True - ret['message'] = 'Agent joined the cluster' + function = "agent/join/{0}".format(address) + res = _query( + consul_url=consul_url, + function=function, + token=token, + method="GET", + query_params=query_params, + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Agent joined the cluster" else: - ret['res'] = False - ret['message'] = 'Unable to join the cluster.' + ret["res"] = False + ret["message"] = "Unable to join the cluster." return ret def agent_leave(consul_url=None, token=None, node=None): - ''' + """ Used to instruct the agent to force a node into the left state. :param consul_url: The Consul server URL. @@ -655,37 +665,39 @@ def agent_leave(consul_url=None, token=None, node=None): salt '*' consul.agent_leave node='web1.example.com' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not node: raise SaltInvocationError('Required argument "node" is missing.') - function = 'agent/force-leave/{0}'.format(node) - res = _query(consul_url=consul_url, - function=function, - token=token, - method='GET', - query_params=query_params) - if res['res']: - ret['res'] = True - ret['message'] = 'Node {0} put in leave state.'.format(node) + function = "agent/force-leave/{0}".format(node) + res = _query( + consul_url=consul_url, + function=function, + token=token, + method="GET", + query_params=query_params, + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Node {0} put in leave state.".format(node) else: - ret['res'] = False - ret['message'] = 'Unable to change state for {0}.'.format(node) + ret["res"] = False + ret["message"] = "Unable to change state for {0}.".format(node) return ret def agent_check_register(consul_url=None, token=None, **kwargs): - ''' + """ The register endpoint is used to add a new check to the local agent. :param consul_url: The Consul server URL. @@ -710,70 +722,68 @@ def agent_check_register(consul_url=None, token=None, **kwargs): salt '*' consul.agent_check_register name='Memory Utilization' script='/usr/local/bin/check_mem.py' interval='15s' - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'name' in kwargs: - data['Name'] = kwargs['name'] + if "name" in kwargs: + data["Name"] = kwargs["name"] else: raise SaltInvocationError('Required argument "name" is missing.') - if True not in [True for item in ('script', 'http', 'ttl') if item in kwargs]: - ret['message'] = 'Required parameter "script" or "http" is missing.' - ret['res'] = False + if True not in [True for item in ("script", "http", "ttl") if item in kwargs]: + ret["message"] = 'Required parameter "script" or "http" is missing.' + ret["res"] = False return ret - if 'id' in kwargs: - data['ID'] = kwargs['id'] + if "id" in kwargs: + data["ID"] = kwargs["id"] - if 'notes' in kwargs: - data['Notes'] = kwargs['notes'] + if "notes" in kwargs: + data["Notes"] = kwargs["notes"] - if 'script' in kwargs: - if 'interval' not in kwargs: - ret['message'] = 'Required parameter "interval" is missing.' - ret['res'] = False + if "script" in kwargs: + if "interval" not in kwargs: + ret["message"] = 'Required parameter "interval" is missing.' + ret["res"] = False return ret - data['Script'] = kwargs['script'] - data['Interval'] = kwargs['interval'] + data["Script"] = kwargs["script"] + data["Interval"] = kwargs["interval"] - if 'http' in kwargs: - if 'interval' not in kwargs: - ret['message'] = 'Required parameter "interval" is missing.' - ret['res'] = False + if "http" in kwargs: + if "interval" not in kwargs: + ret["message"] = 'Required parameter "interval" is missing.' + ret["res"] = False return ret - data['HTTP'] = kwargs['http'] - data['Interval'] = kwargs['interval'] + data["HTTP"] = kwargs["http"] + data["Interval"] = kwargs["interval"] - if 'ttl' in kwargs: - data['TTL'] = kwargs['ttl'] + if "ttl" in kwargs: + data["TTL"] = kwargs["ttl"] - function = 'agent/check/register' - res = _query(consul_url=consul_url, - function=function, - token=token, - method='PUT', - data=data) + function = "agent/check/register" + res = _query( + consul_url=consul_url, function=function, token=token, method="PUT", data=data + ) - if res['res']: - ret['res'] = True - ret['message'] = ('Check {0} added to agent.'.format(kwargs['name'])) + if res["res"]: + ret["res"] = True + ret["message"] = "Check {0} added to agent.".format(kwargs["name"]) else: - ret['res'] = False - ret['message'] = 'Unable to add check to agent.' + ret["res"] = False + ret["message"] = "Unable to add check to agent." return ret def agent_check_deregister(consul_url=None, token=None, checkid=None): - ''' + """ The agent will take care of deregistering the check from the Catalog. :param consul_url: The Consul server URL. @@ -786,35 +796,32 @@ def agent_check_deregister(consul_url=None, token=None, checkid=None): salt '*' consul.agent_check_deregister checkid='Memory Utilization' - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not checkid: raise SaltInvocationError('Required argument "checkid" is missing.') - function = 'agent/check/deregister/{0}'.format(checkid) - res = _query(consul_url=consul_url, - function=function, - token=token, - method='GET') - if res['res']: - ret['res'] = True - ret['message'] = ('Check {0} removed from agent.'.format(checkid)) + function = "agent/check/deregister/{0}".format(checkid) + res = _query(consul_url=consul_url, function=function, token=token, method="GET") + if res["res"]: + ret["res"] = True + ret["message"] = "Check {0} removed from agent.".format(checkid) else: - ret['res'] = False - ret['message'] = 'Unable to remove check from agent.' + ret["res"] = False + ret["message"] = "Unable to remove check from agent." return ret def agent_check_pass(consul_url=None, token=None, checkid=None, **kwargs): - ''' + """ This endpoint is used with a check that is of the TTL type. When this is called, the status of the check is set to passing and the TTL clock is reset. @@ -830,40 +837,42 @@ def agent_check_pass(consul_url=None, token=None, checkid=None, **kwargs): salt '*' consul.agent_check_pass checkid='redis_check1' note='Forcing check into passing state.' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not checkid: raise SaltInvocationError('Required argument "checkid" is missing.') - if 'note' in kwargs: - query_params['note'] = kwargs['note'] + if "note" in kwargs: + query_params["note"] = kwargs["note"] - function = 'agent/check/pass/{0}'.format(checkid) - res = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params, - method='GET') - if res['res']: - ret['res'] = True - ret['message'] = 'Check {0} marked as passing.'.format(checkid) + function = "agent/check/pass/{0}".format(checkid) + res = _query( + consul_url=consul_url, + function=function, + token=token, + query_params=query_params, + method="GET", + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Check {0} marked as passing.".format(checkid) else: - ret['res'] = False - ret['message'] = 'Unable to update check {0}.'.format(checkid) + ret["res"] = False + ret["message"] = "Unable to update check {0}.".format(checkid) return ret def agent_check_warn(consul_url=None, token=None, checkid=None, **kwargs): - ''' + """ This endpoint is used with a check that is of the TTL type. When this is called, the status of the check is set to warning and the TTL clock is reset. @@ -879,40 +888,42 @@ def agent_check_warn(consul_url=None, token=None, checkid=None, **kwargs): salt '*' consul.agent_check_warn checkid='redis_check1' note='Forcing check into warning state.' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not checkid: raise SaltInvocationError('Required argument "checkid" is missing.') - if 'note' in kwargs: - query_params['note'] = kwargs['note'] + if "note" in kwargs: + query_params["note"] = kwargs["note"] - function = 'agent/check/warn/{0}'.format(checkid) - res = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params, - method='GET') - if res['res']: - ret['res'] = True - ret['message'] = 'Check {0} marked as warning.'.format(checkid) + function = "agent/check/warn/{0}".format(checkid) + res = _query( + consul_url=consul_url, + function=function, + token=token, + query_params=query_params, + method="GET", + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Check {0} marked as warning.".format(checkid) else: - ret['res'] = False - ret['message'] = 'Unable to update check {0}.'.format(checkid) + ret["res"] = False + ret["message"] = "Unable to update check {0}.".format(checkid) return ret def agent_check_fail(consul_url=None, token=None, checkid=None, **kwargs): - ''' + """ This endpoint is used with a check that is of the TTL type. When this is called, the status of the check is set to critical and the TTL clock is reset. @@ -928,40 +939,42 @@ def agent_check_fail(consul_url=None, token=None, checkid=None, **kwargs): salt '*' consul.agent_check_fail checkid='redis_check1' note='Forcing check into critical state.' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not checkid: raise SaltInvocationError('Required argument "checkid" is missing.') - if 'note' in kwargs: - query_params['note'] = kwargs['note'] + if "note" in kwargs: + query_params["note"] = kwargs["note"] - function = 'agent/check/fail/{0}'.format(checkid) - res = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params, - method='GET') - if res['res']: - ret['res'] = True - ret['message'] = 'Check {0} marked as critical.'.format(checkid) + function = "agent/check/fail/{0}".format(checkid) + res = _query( + consul_url=consul_url, + function=function, + token=token, + query_params=query_params, + method="GET", + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Check {0} marked as critical.".format(checkid) else: - ret['res'] = False - ret['message'] = 'Unable to update check {0}.'.format(checkid) + ret["res"] = False + ret["message"] = "Unable to update check {0}.".format(checkid) return ret def agent_service_register(consul_url=None, token=None, **kwargs): - ''' + """ The used to add a new service, with an optional health check, to the local agent. @@ -991,91 +1004,89 @@ def agent_service_register(consul_url=None, token=None, **kwargs): salt '*' consul.agent_service_register name='redis' tags='["master", "v1"]' address="127.0.0.1" port="8080" check_script="/usr/local/bin/check_redis.py" interval="10s" - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret lc_kwargs = dict() for k, v in six.iteritems(kwargs): lc_kwargs[k.lower()] = v - if 'name' in lc_kwargs: - data['Name'] = lc_kwargs['name'] + if "name" in lc_kwargs: + data["Name"] = lc_kwargs["name"] else: raise SaltInvocationError('Required argument "name" is missing.') - if 'address' in lc_kwargs: - data['Address'] = lc_kwargs['address'] + if "address" in lc_kwargs: + data["Address"] = lc_kwargs["address"] - if 'port' in lc_kwargs: - data['Port'] = lc_kwargs['port'] + if "port" in lc_kwargs: + data["Port"] = lc_kwargs["port"] - if 'id' in lc_kwargs: - data['ID'] = lc_kwargs['id'] + if "id" in lc_kwargs: + data["ID"] = lc_kwargs["id"] - if 'tags' in lc_kwargs: - _tags = lc_kwargs['tags'] + if "tags" in lc_kwargs: + _tags = lc_kwargs["tags"] if not isinstance(_tags, list): _tags = [_tags] - data['Tags'] = _tags + data["Tags"] = _tags - if 'enabletagoverride' in lc_kwargs: - data['EnableTagOverride'] = lc_kwargs['enabletagoverride'] + if "enabletagoverride" in lc_kwargs: + data["EnableTagOverride"] = lc_kwargs["enabletagoverride"] - if 'check' in lc_kwargs: + if "check" in lc_kwargs: dd = dict() - for k, v in six.iteritems(lc_kwargs['check']): + for k, v in six.iteritems(lc_kwargs["check"]): dd[k.lower()] = v interval_required = False check_dd = dict() - if 'script' in dd: + if "script" in dd: interval_required = True - check_dd['Script'] = dd['script'] - if 'http' in dd: + check_dd["Script"] = dd["script"] + if "http" in dd: interval_required = True - check_dd['HTTP'] = dd['http'] - if 'ttl' in dd: - check_dd['TTL'] = dd['ttl'] - if 'interval' in dd: - check_dd['Interval'] = dd['interval'] + check_dd["HTTP"] = dd["http"] + if "ttl" in dd: + check_dd["TTL"] = dd["ttl"] + if "interval" in dd: + check_dd["Interval"] = dd["interval"] if interval_required: - if 'Interval' not in check_dd: - ret['message'] = 'Required parameter "interval" is missing.' - ret['res'] = False + if "Interval" not in check_dd: + ret["message"] = 'Required parameter "interval" is missing.' + ret["res"] = False return ret else: - if 'Interval' in check_dd: - del check_dd['Interval'] # not required, so ignore it + if "Interval" in check_dd: + del check_dd["Interval"] # not required, so ignore it if check_dd > 0: - data['Check'] = check_dd # if empty, ignore it + data["Check"] = check_dd # if empty, ignore it - function = 'agent/service/register' - res = _query(consul_url=consul_url, - function=function, - token=token, - method='PUT', - data=data) - if res['res']: - ret['res'] = True - ret['message'] = 'Service {0} registered on agent.'.format(kwargs['name']) + function = "agent/service/register" + res = _query( + consul_url=consul_url, function=function, token=token, method="PUT", data=data + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Service {0} registered on agent.".format(kwargs["name"]) else: - ret['res'] = False - ret['message'] = 'Unable to register service {0}.'.format(kwargs['name']) + ret["res"] = False + ret["message"] = "Unable to register service {0}.".format(kwargs["name"]) return ret def agent_service_deregister(consul_url=None, token=None, serviceid=None): - ''' + """ Used to remove a service. :param consul_url: The Consul server URL. @@ -1088,37 +1099,35 @@ def agent_service_deregister(consul_url=None, token=None, serviceid=None): salt '*' consul.agent_service_deregister serviceid='redis' - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not serviceid: raise SaltInvocationError('Required argument "serviceid" is missing.') - function = 'agent/service/deregister/{0}'.format(serviceid) - res = _query(consul_url=consul_url, - function=function, - token=token, - method='PUT', - data=data) - if res['res']: - ret['res'] = True - ret['message'] = 'Service {0} removed from agent.'.format(serviceid) + function = "agent/service/deregister/{0}".format(serviceid) + res = _query( + consul_url=consul_url, function=function, token=token, method="PUT", data=data + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Service {0} removed from agent.".format(serviceid) else: - ret['res'] = False - ret['message'] = 'Unable to remove service {0}.'.format(serviceid) + ret["res"] = False + ret["message"] = "Unable to remove service {0}.".format(serviceid) return ret def agent_service_maintenance(consul_url=None, token=None, serviceid=None, **kwargs): - ''' + """ Used to place a service into maintenance mode. :param consul_url: The Consul server URL. @@ -1134,49 +1143,48 @@ def agent_service_maintenance(consul_url=None, token=None, serviceid=None, **kwa salt '*' consul.agent_service_deregister serviceid='redis' enable='True' reason='Down for upgrade' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not serviceid: raise SaltInvocationError('Required argument "serviceid" is missing.') - if 'enable' in kwargs: - query_params['enable'] = kwargs['enable'] + if "enable" in kwargs: + query_params["enable"] = kwargs["enable"] else: - ret['message'] = 'Required parameter "enable" is missing.' - ret['res'] = False + ret["message"] = 'Required parameter "enable" is missing.' + ret["res"] = False return ret - if 'reason' in kwargs: - query_params['reason'] = kwargs['reason'] + if "reason" in kwargs: + query_params["reason"] = kwargs["reason"] - function = 'agent/service/maintenance/{0}'.format(serviceid) - res = _query(consul_url=consul_url, - token=token, - function=function, - query_params=query_params) + function = "agent/service/maintenance/{0}".format(serviceid) + res = _query( + consul_url=consul_url, token=token, function=function, query_params=query_params + ) - if res['res']: - ret['res'] = True - ret['message'] = ('Service {0} set in ' - 'maintenance mode.'.format(serviceid)) + if res["res"]: + ret["res"] = True + ret["message"] = "Service {0} set in " "maintenance mode.".format(serviceid) else: - ret['res'] = False - ret['message'] = ('Unable to set service ' - '{0} to maintenance mode.'.format(serviceid)) + ret["res"] = False + ret["message"] = "Unable to set service " "{0} to maintenance mode.".format( + serviceid + ) return ret def session_create(consul_url=None, token=None, **kwargs): - ''' + """ Used to create a session. :param consul_url: The Consul server URL. @@ -1205,69 +1213,65 @@ def session_create(consul_url=None, token=None, **kwargs): salt '*' consul.session_create node='node1' name='my-session' behavior='delete' ttl='3600s' - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret data = {} - if 'lockdelay' in kwargs: - data['LockDelay'] = kwargs['lockdelay'] + if "lockdelay" in kwargs: + data["LockDelay"] = kwargs["lockdelay"] - if 'node' in kwargs: - data['Node'] = kwargs['node'] + if "node" in kwargs: + data["Node"] = kwargs["node"] - if 'name' in kwargs: - data['Name'] = kwargs['name'] + if "name" in kwargs: + data["Name"] = kwargs["name"] else: raise SaltInvocationError('Required argument "name" is missing.') - if 'checks' in kwargs: - data['Touch'] = kwargs['touch'] + if "checks" in kwargs: + data["Touch"] = kwargs["touch"] - if 'behavior' in kwargs: - if not kwargs['behavior'] in ('delete', 'release'): - ret['message'] = ('Behavior must be ', - 'either delete or release.') - ret['res'] = False + if "behavior" in kwargs: + if not kwargs["behavior"] in ("delete", "release"): + ret["message"] = ("Behavior must be ", "either delete or release.") + ret["res"] = False return ret - data['Behavior'] = kwargs['behavior'] + data["Behavior"] = kwargs["behavior"] - if 'ttl' in kwargs: - _ttl = kwargs['ttl'] - if six.text_type(_ttl).endswith('s'): + if "ttl" in kwargs: + _ttl = kwargs["ttl"] + if six.text_type(_ttl).endswith("s"): _ttl = _ttl[:-1] if int(_ttl) < 0 or int(_ttl) > 3600: - ret['message'] = ('TTL must be ', - 'between 0 and 3600.') - ret['res'] = False + ret["message"] = ("TTL must be ", "between 0 and 3600.") + ret["res"] = False return ret - data['TTL'] = '{0}s'.format(_ttl) + data["TTL"] = "{0}s".format(_ttl) - function = 'session/create' - res = _query(consul_url=consul_url, - function=function, - token=token, - method='PUT', - data=data) + function = "session/create" + res = _query( + consul_url=consul_url, function=function, token=token, method="PUT", data=data + ) - if res['res']: - ret['res'] = True - ret['message'] = 'Created session {0}.'.format(kwargs['name']) + if res["res"]: + ret["res"] = True + ret["message"] = "Created session {0}.".format(kwargs["name"]) else: - ret['res'] = False - ret['message'] = 'Unable to create session {0}.'.format(kwargs['name']) + ret["res"] = False + ret["message"] = "Unable to create session {0}.".format(kwargs["name"]) return ret def session_list(consul_url=None, token=None, return_list=False, **kwargs): - ''' + """ Used to list sessions. :param consul_url: The Consul server URL. @@ -1284,37 +1288,36 @@ def session_list(consul_url=None, token=None, return_list=False, **kwargs): salt '*' consul.session_list - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret query_params = {} - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - function = 'session/list' - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "session/list" + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) if return_list: _list = [] - for item in ret['data']: - _list.append(item['ID']) + for item in ret["data"]: + _list.append(item["ID"]) return _list return ret def session_destroy(consul_url=None, token=None, session=None, **kwargs): - ''' + """ Destroy session :param consul_url: The Consul server URL. @@ -1329,14 +1332,14 @@ def session_destroy(consul_url=None, token=None, session=None, **kwargs): salt '*' consul.session_destroy session='c1c4d223-91cb-3d1f-1ee8-f2af9e7b6716' - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not session: @@ -1344,25 +1347,24 @@ def session_destroy(consul_url=None, token=None, session=None, **kwargs): query_params = {} - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - function = 'session/destroy/{0}'.format(session) - res = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) - if res['res']: - ret['res'] = True - ret['message'] = 'Created Service {0}.'.format(kwargs['name']) + function = "session/destroy/{0}".format(session) + res = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Created Service {0}.".format(kwargs["name"]) else: - ret['res'] = False - ret['message'] = 'Unable to create service {0}.'.format(kwargs['name']) + ret["res"] = False + ret["message"] = "Unable to create service {0}.".format(kwargs["name"]) return ret def session_info(consul_url=None, token=None, session=None, **kwargs): - ''' + """ Information about a session :param consul_url: The Consul server URL. @@ -1377,14 +1379,14 @@ def session_info(consul_url=None, token=None, session=None, **kwargs): salt '*' consul.session_info session='c1c4d223-91cb-3d1f-1ee8-f2af9e7b6716' - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not session: @@ -1392,19 +1394,18 @@ def session_info(consul_url=None, token=None, session=None, **kwargs): query_params = {} - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - function = 'session/info/{0}'.format(session) - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "session/info/{0}".format(session) + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def catalog_register(consul_url=None, token=None, **kwargs): - ''' + """ Registers a new node, service, or check :param consul_url: The Consul server URL. @@ -1432,140 +1433,147 @@ def catalog_register(consul_url=None, token=None, **kwargs): salt '*' consul.catalog_register node='node1' address='192.168.1.1' service='redis' service_address='127.0.0.1' service_port='8080' service_id='redis_server1' - ''' + """ ret = {} data = {} - data['NodeMeta'] = {} + data["NodeMeta"] = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'datacenter' in kwargs: - data['Datacenter'] = kwargs['datacenter'] + if "datacenter" in kwargs: + data["Datacenter"] = kwargs["datacenter"] - if 'node' in kwargs: - data['Node'] = kwargs['node'] + if "node" in kwargs: + data["Node"] = kwargs["node"] else: - ret['message'] = 'Required argument node argument is missing.' - ret['res'] = False + ret["message"] = "Required argument node argument is missing." + ret["res"] = False return ret - if 'address' in kwargs: - if isinstance(kwargs['address'], list): - _address = kwargs['address'][0] + if "address" in kwargs: + if isinstance(kwargs["address"], list): + _address = kwargs["address"][0] else: - _address = kwargs['address'] - data['Address'] = _address + _address = kwargs["address"] + data["Address"] = _address else: - ret['message'] = 'Required argument address argument is missing.' - ret['res'] = False + ret["message"] = "Required argument address argument is missing." + ret["res"] = False return ret - if 'ip_interfaces' in kwargs: - data['TaggedAddresses'] = {} - for k in kwargs['ip_interfaces']: - if kwargs['ip_interfaces'].get(k): - data['TaggedAddresses'][k] = kwargs['ip_interfaces'][k][0] + if "ip_interfaces" in kwargs: + data["TaggedAddresses"] = {} + for k in kwargs["ip_interfaces"]: + if kwargs["ip_interfaces"].get(k): + data["TaggedAddresses"][k] = kwargs["ip_interfaces"][k][0] - if 'service' in kwargs: - data['Service'] = {} - data['Service']['Service'] = kwargs['service'] + if "service" in kwargs: + data["Service"] = {} + data["Service"]["Service"] = kwargs["service"] - if 'service_address' in kwargs: - data['Service']['Address'] = kwargs['service_address'] + if "service_address" in kwargs: + data["Service"]["Address"] = kwargs["service_address"] - if 'service_port' in kwargs: - data['Service']['Port'] = kwargs['service_port'] + if "service_port" in kwargs: + data["Service"]["Port"] = kwargs["service_port"] - if 'service_id' in kwargs: - data['Service']['ID'] = kwargs['service_id'] + if "service_id" in kwargs: + data["Service"]["ID"] = kwargs["service_id"] - if 'service_tags' in kwargs: - _tags = kwargs['service_tags'] + if "service_tags" in kwargs: + _tags = kwargs["service_tags"] if not isinstance(_tags, list): _tags = [_tags] - data['Service']['Tags'] = _tags + data["Service"]["Tags"] = _tags - if 'cpu' in kwargs: - data['NodeMeta']['Cpu'] = kwargs['cpu'] + if "cpu" in kwargs: + data["NodeMeta"]["Cpu"] = kwargs["cpu"] - if 'num_cpus' in kwargs: - data['NodeMeta']['Cpu_num'] = kwargs['num_cpus'] + if "num_cpus" in kwargs: + data["NodeMeta"]["Cpu_num"] = kwargs["num_cpus"] - if 'mem' in kwargs: - data['NodeMeta']['Memory'] = kwargs['mem'] + if "mem" in kwargs: + data["NodeMeta"]["Memory"] = kwargs["mem"] - if 'oscode' in kwargs: - data['NodeMeta']['Os'] = kwargs['oscode'] + if "oscode" in kwargs: + data["NodeMeta"]["Os"] = kwargs["oscode"] - if 'osarch' in kwargs: - data['NodeMeta']['Osarch'] = kwargs['osarch'] + if "osarch" in kwargs: + data["NodeMeta"]["Osarch"] = kwargs["osarch"] - if 'kernel' in kwargs: - data['NodeMeta']['Kernel'] = kwargs['kernel'] + if "kernel" in kwargs: + data["NodeMeta"]["Kernel"] = kwargs["kernel"] - if 'kernelrelease' in kwargs: - data['NodeMeta']['Kernelrelease'] = kwargs['kernelrelease'] + if "kernelrelease" in kwargs: + data["NodeMeta"]["Kernelrelease"] = kwargs["kernelrelease"] - if 'localhost' in kwargs: - data['NodeMeta']['localhost'] = kwargs['localhost'] + if "localhost" in kwargs: + data["NodeMeta"]["localhost"] = kwargs["localhost"] - if 'nodename' in kwargs: - data['NodeMeta']['nodename'] = kwargs['nodename'] + if "nodename" in kwargs: + data["NodeMeta"]["nodename"] = kwargs["nodename"] - if 'os_family' in kwargs: - data['NodeMeta']['os_family'] = kwargs['os_family'] + if "os_family" in kwargs: + data["NodeMeta"]["os_family"] = kwargs["os_family"] - if 'lsb_distrib_description' in kwargs: - data['NodeMeta']['lsb_distrib_description'] = kwargs['lsb_distrib_description'] + if "lsb_distrib_description" in kwargs: + data["NodeMeta"]["lsb_distrib_description"] = kwargs["lsb_distrib_description"] - if 'master' in kwargs: - data['NodeMeta']['master'] = kwargs['master'] + if "master" in kwargs: + data["NodeMeta"]["master"] = kwargs["master"] - if 'check' in kwargs: - data['Check'] = {} - data['Check']['Name'] = kwargs['check'] + if "check" in kwargs: + data["Check"] = {} + data["Check"]["Name"] = kwargs["check"] - if 'check_status' in kwargs: - if kwargs['check_status'] not in ('unknown', 'passing', 'warning', 'critical'): - ret['message'] = 'Check status must be unknown, passing, warning, or critical.' - ret['res'] = False + if "check_status" in kwargs: + if kwargs["check_status"] not in ( + "unknown", + "passing", + "warning", + "critical", + ): + ret[ + "message" + ] = "Check status must be unknown, passing, warning, or critical." + ret["res"] = False return ret - data['Check']['Status'] = kwargs['check_status'] + data["Check"]["Status"] = kwargs["check_status"] - if 'check_service' in kwargs: - data['Check']['ServiceID'] = kwargs['check_service'] + if "check_service" in kwargs: + data["Check"]["ServiceID"] = kwargs["check_service"] - if 'check_id' in kwargs: - data['Check']['CheckID'] = kwargs['check_id'] + if "check_id" in kwargs: + data["Check"]["CheckID"] = kwargs["check_id"] - if 'check_notes' in kwargs: - data['Check']['Notes'] = kwargs['check_notes'] + if "check_notes" in kwargs: + data["Check"]["Notes"] = kwargs["check_notes"] - function = 'catalog/register' - res = _query(consul_url=consul_url, - function=function, - token=token, - method='PUT', - data=data) - if res['res']: - ret['res'] = True - ret['message'] = ('Catalog registration ' - 'for {0} successful.'.format(kwargs['node'])) + function = "catalog/register" + res = _query( + consul_url=consul_url, function=function, token=token, method="PUT", data=data + ) + if res["res"]: + ret["res"] = True + ret["message"] = "Catalog registration " "for {0} successful.".format( + kwargs["node"] + ) else: - ret['res'] = False - ret['message'] = ('Catalog registration ' - 'for {0} failed.'.format(kwargs['node'])) - ret['data'] = data + ret["res"] = False + ret["message"] = "Catalog registration " "for {0} failed.".format( + kwargs["node"] + ) + ret["data"] = data return ret def catalog_deregister(consul_url=None, token=None, **kwargs): - ''' + """ Deregisters a node, service, or check :param consul_url: The Consul server URL. @@ -1582,52 +1590,49 @@ def catalog_deregister(consul_url=None, token=None, **kwargs): salt '*' consul.catalog_register node='node1' serviceid='redis_server1' checkid='redis_check1' - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'datacenter' in kwargs: - data['Datacenter'] = kwargs['datacenter'] + if "datacenter" in kwargs: + data["Datacenter"] = kwargs["datacenter"] - if 'node' in kwargs: - data['Node'] = kwargs['node'] + if "node" in kwargs: + data["Node"] = kwargs["node"] else: - ret['message'] = 'Node argument required.' - ret['res'] = False + ret["message"] = "Node argument required." + ret["res"] = False return ret - if 'checkid' in kwargs: - data['CheckID'] = kwargs['checkid'] + if "checkid" in kwargs: + data["CheckID"] = kwargs["checkid"] - if 'serviceid' in kwargs: - data['ServiceID'] = kwargs['serviceid'] + if "serviceid" in kwargs: + data["ServiceID"] = kwargs["serviceid"] - function = 'catalog/deregister' - res = _query(consul_url=consul_url, - function=function, - token=token, - method='PUT', - data=data) + function = "catalog/deregister" + res = _query( + consul_url=consul_url, function=function, token=token, method="PUT", data=data + ) - if res['res']: - ret['res'] = True - ret['message'] = 'Catalog item {0} removed.'.format(kwargs['node']) + if res["res"]: + ret["res"] = True + ret["message"] = "Catalog item {0} removed.".format(kwargs["node"]) else: - ret['res'] = False - ret['message'] = ('Removing Catalog ' - 'item {0} failed.'.format(kwargs['node'])) + ret["res"] = False + ret["message"] = "Removing Catalog " "item {0} failed.".format(kwargs["node"]) return ret def catalog_datacenters(consul_url=None, token=None): - ''' + """ Return list of available datacenters from catalog. :param consul_url: The Consul server URL. @@ -1639,25 +1644,23 @@ def catalog_datacenters(consul_url=None, token=None): salt '*' consul.catalog_datacenters - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - function = 'catalog/datacenters' - ret = _query(consul_url=consul_url, - function=function, - token=token) + function = "catalog/datacenters" + ret = _query(consul_url=consul_url, function=function, token=token) return ret def catalog_nodes(consul_url=None, token=None, **kwargs): - ''' + """ Return list of available nodes from catalog. :param consul_url: The Consul server URL. @@ -1671,30 +1674,29 @@ def catalog_nodes(consul_url=None, token=None, **kwargs): salt '*' consul.catalog_nodes - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - function = 'catalog/nodes' - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "catalog/nodes" + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def catalog_services(consul_url=None, token=None, **kwargs): - ''' + """ Return list of available services rom catalog. :param consul_url: The Consul server URL. @@ -1708,30 +1710,29 @@ def catalog_services(consul_url=None, token=None, **kwargs): salt '*' consul.catalog_services - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - function = 'catalog/services' - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "catalog/services" + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def catalog_service(consul_url=None, token=None, service=None, **kwargs): - ''' + """ Information about the registered service. :param consul_url: The Consul server URL. @@ -1746,36 +1747,35 @@ def catalog_service(consul_url=None, token=None, service=None, **kwargs): salt '*' consul.catalog_service service='redis' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not service: raise SaltInvocationError('Required argument "service" is missing.') - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - if 'tag' in kwargs: - query_params['tag'] = kwargs['tag'] + if "tag" in kwargs: + query_params["tag"] = kwargs["tag"] - function = 'catalog/service/{0}'.format(service) - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "catalog/service/{0}".format(service) + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def catalog_node(consul_url=None, token=None, node=None, **kwargs): - ''' + """ Information about the registered node. :param consul_url: The Consul server URL. @@ -1790,33 +1790,32 @@ def catalog_node(consul_url=None, token=None, node=None, **kwargs): salt '*' consul.catalog_service service='redis' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not node: raise SaltInvocationError('Required argument "node" is missing.') - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - function = 'catalog/node/{0}'.format(node) - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "catalog/node/{0}".format(node) + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def health_node(consul_url=None, token=None, node=None, **kwargs): - ''' + """ Health information about the registered node. :param consul_url: The Consul server URL. @@ -1831,33 +1830,32 @@ def health_node(consul_url=None, token=None, node=None, **kwargs): salt '*' consul.health_node node='node1' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not node: raise SaltInvocationError('Required argument "node" is missing.') - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - function = 'health/node/{0}'.format(node) - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "health/node/{0}".format(node) + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def health_checks(consul_url=None, token=None, service=None, **kwargs): - ''' + """ Health information about the registered service. :param consul_url: The Consul server URL. @@ -1872,33 +1870,32 @@ def health_checks(consul_url=None, token=None, service=None, **kwargs): salt '*' consul.health_checks service='redis1' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not service: raise SaltInvocationError('Required argument "service" is missing.') - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - function = 'health/checks/{0}'.format(service) - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "health/checks/{0}".format(service) + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def health_service(consul_url=None, token=None, service=None, **kwargs): - ''' + """ Health information about the registered service. :param consul_url: The Consul server URL. @@ -1918,39 +1915,38 @@ def health_service(consul_url=None, token=None, service=None, **kwargs): salt '*' consul.health_service service='redis1' passing='True' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not service: raise SaltInvocationError('Required argument "service" is missing.') - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - if 'tag' in kwargs: - query_params['tag'] = kwargs['tag'] + if "tag" in kwargs: + query_params["tag"] = kwargs["tag"] - if 'passing' in kwargs: - query_params['passing'] = kwargs['passing'] + if "passing" in kwargs: + query_params["passing"] = kwargs["passing"] - function = 'health/service/{0}'.format(service) - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "health/service/{0}".format(service) + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def health_state(consul_url=None, token=None, state=None, **kwargs): - ''' + """ Returns the checks in the state provided on the path. :param consul_url: The Consul server URL. @@ -1970,38 +1966,37 @@ def health_state(consul_url=None, token=None, state=None, **kwargs): salt '*' consul.health_state service='redis1' passing='True' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not state: raise SaltInvocationError('Required argument "state" is missing.') - if 'dc' in kwargs: - query_params['dc'] = kwargs['dc'] + if "dc" in kwargs: + query_params["dc"] = kwargs["dc"] - if state not in ('any', 'unknown', 'passing', 'warning', 'critical'): - ret['message'] = 'State must be any, unknown, passing, warning, or critical.' - ret['res'] = False + if state not in ("any", "unknown", "passing", "warning", "critical"): + ret["message"] = "State must be any, unknown, passing, warning, or critical." + ret["res"] = False return ret - function = 'health/state/{0}'.format(state) - ret = _query(consul_url=consul_url, - function=function, - token=token, - query_params=query_params) + function = "health/state/{0}".format(state) + ret = _query( + consul_url=consul_url, function=function, token=token, query_params=query_params + ) return ret def status_leader(consul_url=None, token=None): - ''' + """ Returns the current Raft leader :param consul_url: The Consul server URL. @@ -2013,25 +2008,23 @@ def status_leader(consul_url=None, token=None): salt '*' consul.status_leader - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - function = 'status/leader' - ret = _query(consul_url=consul_url, - function=function, - token=token) + function = "status/leader" + ret = _query(consul_url=consul_url, function=function, token=token) return ret def status_peers(consul_url, token=None): - ''' + """ Returns the current Raft peer set :param consul_url: The Consul server URL. @@ -2044,25 +2037,23 @@ def status_peers(consul_url, token=None): salt '*' consul.status_peers - ''' + """ ret = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - function = 'status/peers' - ret = _query(consul_url=consul_url, - function=function, - token=token) + function = "status/peers" + ret = _query(consul_url=consul_url, function=function, token=token) return ret def acl_create(consul_url=None, token=None, **kwargs): - ''' + """ Create a new ACL token. :param consul_url: The Consul server URL. @@ -2080,47 +2071,44 @@ def acl_create(consul_url=None, token=None, **kwargs): salt '*' consul.acl_create - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'name' in kwargs: - data['Name'] = kwargs['name'] + if "name" in kwargs: + data["Name"] = kwargs["name"] else: raise SaltInvocationError('Required argument "name" is missing.') - if 'type' in kwargs: - data['Type'] = kwargs['type'] + if "type" in kwargs: + data["Type"] = kwargs["type"] - if 'rules' in kwargs: - data['Rules'] = kwargs['rules'] + if "rules" in kwargs: + data["Rules"] = kwargs["rules"] - function = 'acl/create' - res = _query(consul_url=consul_url, - token=token, - data=data, - method='PUT', - function=function) + function = "acl/create" + res = _query( + consul_url=consul_url, token=token, data=data, method="PUT", function=function + ) - if res['res']: - ret['res'] = True - ret['message'] = 'ACL {0} created.'.format(kwargs['name']) + if res["res"]: + ret["res"] = True + ret["message"] = "ACL {0} created.".format(kwargs["name"]) else: - ret['res'] = False - ret['message'] = ('Removing Catalog ' - 'item {0} failed.'.format(kwargs['name'])) + ret["res"] = False + ret["message"] = "Removing Catalog " "item {0} failed.".format(kwargs["name"]) return ret def acl_update(consul_url=None, token=None, **kwargs): - ''' + """ Update an ACL token. :param consul_url: The Consul server URL. @@ -2139,55 +2127,52 @@ def acl_update(consul_url=None, token=None, **kwargs): salt '*' consul.acl_update - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'id' in kwargs: - data['ID'] = kwargs['id'] + if "id" in kwargs: + data["ID"] = kwargs["id"] else: - ret['message'] = 'Required parameter "id" is missing.' - ret['res'] = False + ret["message"] = 'Required parameter "id" is missing.' + ret["res"] = False return ret - if 'name' in kwargs: - data['Name'] = kwargs['name'] + if "name" in kwargs: + data["Name"] = kwargs["name"] else: raise SaltInvocationError('Required argument "name" is missing.') - if 'type' in kwargs: - data['Type'] = kwargs['type'] + if "type" in kwargs: + data["Type"] = kwargs["type"] - if 'rules' in kwargs: - data['Rules'] = kwargs['rules'] + if "rules" in kwargs: + data["Rules"] = kwargs["rules"] - function = 'acl/update' - res = _query(consul_url=consul_url, - token=token, - data=data, - method='PUT', - function=function) + function = "acl/update" + res = _query( + consul_url=consul_url, token=token, data=data, method="PUT", function=function + ) - if res['res']: - ret['res'] = True - ret['message'] = 'ACL {0} created.'.format(kwargs['name']) + if res["res"]: + ret["res"] = True + ret["message"] = "ACL {0} created.".format(kwargs["name"]) else: - ret['res'] = False - ret['message'] = ('Adding ACL ' - '{0} failed.'.format(kwargs['name'])) + ret["res"] = False + ret["message"] = "Adding ACL " "{0} failed.".format(kwargs["name"]) return ret def acl_delete(consul_url=None, token=None, **kwargs): - ''' + """ Delete an ACL token. :param consul_url: The Consul server URL. @@ -2200,42 +2185,39 @@ def acl_delete(consul_url=None, token=None, **kwargs): salt '*' consul.acl_delete id='c1c4d223-91cb-3d1f-1ee8-f2af9e7b6716' - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'id' not in kwargs: - ret['message'] = 'Required parameter "id" is missing.' - ret['res'] = False + if "id" not in kwargs: + ret["message"] = 'Required parameter "id" is missing.' + ret["res"] = False return ret - function = 'acl/delete/{0}'.format(kwargs['id']) - res = _query(consul_url=consul_url, - token=token, - data=data, - method='PUT', - function=function) + function = "acl/delete/{0}".format(kwargs["id"]) + res = _query( + consul_url=consul_url, token=token, data=data, method="PUT", function=function + ) - if res['res']: - ret['res'] = True - ret['message'] = 'ACL {0} deleted.'.format(kwargs['id']) + if res["res"]: + ret["res"] = True + ret["message"] = "ACL {0} deleted.".format(kwargs["id"]) else: - ret['res'] = False - ret['message'] = ('Removing ACL ' - '{0} failed.'.format(kwargs['id'])) + ret["res"] = False + ret["message"] = "Removing ACL " "{0} failed.".format(kwargs["id"]) return ret def acl_info(consul_url=None, **kwargs): - ''' + """ Information about an ACL token. :param consul_url: The Consul server URL. @@ -2248,32 +2230,29 @@ def acl_info(consul_url=None, **kwargs): salt '*' consul.acl_info id='c1c4d223-91cb-3d1f-1ee8-f2af9e7b6716' - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'id' not in kwargs: - ret['message'] = 'Required parameter "id" is missing.' - ret['res'] = False + if "id" not in kwargs: + ret["message"] = 'Required parameter "id" is missing.' + ret["res"] = False return ret - function = 'acl/info/{0}'.format(kwargs['id']) - ret = _query(consul_url=consul_url, - data=data, - method='GET', - function=function) + function = "acl/info/{0}".format(kwargs["id"]) + ret = _query(consul_url=consul_url, data=data, method="GET", function=function) return ret def acl_clone(consul_url=None, token=None, **kwargs): - ''' + """ Information about an ACL token. :param consul_url: The Consul server URL. @@ -2287,41 +2266,38 @@ def acl_clone(consul_url=None, token=None, **kwargs): salt '*' consul.acl_info id='c1c4d223-91cb-3d1f-1ee8-f2af9e7b6716' - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'id' not in kwargs: - ret['message'] = 'Required parameter "id" is missing.' - ret['res'] = False + if "id" not in kwargs: + ret["message"] = 'Required parameter "id" is missing.' + ret["res"] = False return ret - function = 'acl/clone/{0}'.format(kwargs['id']) - res = _query(consul_url=consul_url, - token=token, - data=data, - method='PUT', - function=function) - if res['res']: - ret['res'] = True - ret['message'] = 'ACL {0} cloned.'.format(kwargs['name']) - ret['ID'] = ret['data'] + function = "acl/clone/{0}".format(kwargs["id"]) + res = _query( + consul_url=consul_url, token=token, data=data, method="PUT", function=function + ) + if res["res"]: + ret["res"] = True + ret["message"] = "ACL {0} cloned.".format(kwargs["name"]) + ret["ID"] = ret["data"] else: - ret['res'] = False - ret['message'] = ('Cloning ACL' - 'item {0} failed.'.format(kwargs['name'])) + ret["res"] = False + ret["message"] = "Cloning ACL" "item {0} failed.".format(kwargs["name"]) return ret def acl_list(consul_url=None, token=None, **kwargs): - ''' + """ List the ACL tokens. :param consul_url: The Consul server URL. @@ -2333,33 +2309,31 @@ def acl_list(consul_url=None, token=None, **kwargs): salt '*' consul.acl_list - ''' + """ ret = {} data = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'id' not in kwargs: - ret['message'] = 'Required parameter "id" is missing.' - ret['res'] = False + if "id" not in kwargs: + ret["message"] = 'Required parameter "id" is missing.' + ret["res"] = False return ret - function = 'acl/list' - ret = _query(consul_url=consul_url, - token=token, - data=data, - method='PUT', - function=function) + function = "acl/list" + ret = _query( + consul_url=consul_url, token=token, data=data, method="PUT", function=function + ) return ret def event_fire(consul_url=None, token=None, name=None, **kwargs): - ''' + """ List the ACL tokens. :param consul_url: The Consul server URL. @@ -2377,52 +2351,53 @@ def event_fire(consul_url=None, token=None, name=None, **kwargs): salt '*' consul.event_fire name='deploy' - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret if not name: raise SaltInvocationError('Required argument "name" is missing.') - if 'dc' in kwargs: - query_params = kwargs['dc'] + if "dc" in kwargs: + query_params = kwargs["dc"] - if 'node' in kwargs: - query_params = kwargs['node'] + if "node" in kwargs: + query_params = kwargs["node"] - if 'service' in kwargs: - query_params = kwargs['service'] + if "service" in kwargs: + query_params = kwargs["service"] - if 'tag' in kwargs: - query_params = kwargs['tag'] + if "tag" in kwargs: + query_params = kwargs["tag"] - function = 'event/fire/{0}'.format(name) - res = _query(consul_url=consul_url, - token=token, - query_params=query_params, - method='PUT', - function=function) + function = "event/fire/{0}".format(name) + res = _query( + consul_url=consul_url, + token=token, + query_params=query_params, + method="PUT", + function=function, + ) - if res['res']: - ret['res'] = True - ret['message'] = 'Event {0} fired.'.format(name) - ret['data'] = ret['data'] + if res["res"]: + ret["res"] = True + ret["message"] = "Event {0} fired.".format(name) + ret["data"] = ret["data"] else: - ret['res'] = False - ret['message'] = ('Cloning ACL' - 'item {0} failed.'.format(kwargs['name'])) + ret["res"] = False + ret["message"] = "Cloning ACL" "item {0} failed.".format(kwargs["name"]) return ret def event_list(consul_url=None, token=None, **kwargs): - ''' + """ List the recent events. :param consul_url: The Consul server URL. @@ -2435,25 +2410,24 @@ def event_list(consul_url=None, token=None, **kwargs): salt '*' consul.event_list - ''' + """ ret = {} query_params = {} if not consul_url: consul_url = _get_config() if not consul_url: - log.error('No Consul URL found.') - ret['message'] = 'No Consul URL found.' - ret['res'] = False + log.error("No Consul URL found.") + ret["message"] = "No Consul URL found." + ret["res"] = False return ret - if 'name' in kwargs: - query_params = kwargs['name'] + if "name" in kwargs: + query_params = kwargs["name"] else: raise SaltInvocationError('Required argument "name" is missing.') - function = 'event/list/' - ret = _query(consul_url=consul_url, - token=token, - query_params=query_params, - function=function) + function = "event/list/" + ret = _query( + consul_url=consul_url, token=token, query_params=query_params, function=function + ) return ret diff --git a/salt/modules/container_resource.py b/salt/modules/container_resource.py index aec04e301c5..491d90017e6 100644 --- a/salt/modules/container_resource.py +++ b/salt/modules/container_resource.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Common resources for LXC and systemd-nspawn containers .. versionadded:: 2015.8.0 @@ -8,12 +8,13 @@ These functions are not designed to be called directly, but instead from the :mod:`lxc `, :mod:`nspawn `, and :mod:`docker ` execution modules. They provide for common logic to be re-used for common actions. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import functools + import copy +import functools import logging import os import pipes @@ -28,59 +29,59 @@ from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) -PATH = 'PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/bin:' \ - '/usr/local/bin:/usr/local/sbin' +PATH = "PATH=/bin:/usr/bin:/sbin:/usr/sbin:/opt/bin:" "/usr/local/bin:/usr/local/sbin" def _validate(wrapped): - ''' + """ Decorator for common function argument validation - ''' + """ + @functools.wraps(wrapped) def wrapper(*args, **kwargs): - container_type = kwargs.get('container_type') - exec_driver = kwargs.get('exec_driver') + container_type = kwargs.get("container_type") + exec_driver = kwargs.get("exec_driver") valid_driver = { - 'docker': ('lxc-attach', 'nsenter', 'docker-exec'), - 'lxc': ('lxc-attach',), - 'nspawn': ('nsenter',), + "docker": ("lxc-attach", "nsenter", "docker-exec"), + "lxc": ("lxc-attach",), + "nspawn": ("nsenter",), } if container_type not in valid_driver: raise SaltInvocationError( - 'Invalid container type \'{0}\'. Valid types are: {1}' - .format(container_type, ', '.join(sorted(valid_driver))) + "Invalid container type '{0}'. Valid types are: {1}".format( + container_type, ", ".join(sorted(valid_driver)) + ) ) if exec_driver not in valid_driver[container_type]: raise SaltInvocationError( - 'Invalid command execution driver. Valid drivers are: {0}' - .format(', '.join(valid_driver[container_type])) + "Invalid command execution driver. Valid drivers are: {0}".format( + ", ".join(valid_driver[container_type]) + ) ) - if exec_driver == 'lxc-attach' and not salt.utils.path.which('lxc-attach'): + if exec_driver == "lxc-attach" and not salt.utils.path.which("lxc-attach"): raise SaltInvocationError( - 'The \'lxc-attach\' execution driver has been chosen, but ' - 'lxc-attach is not available. LXC may not be installed.' + "The 'lxc-attach' execution driver has been chosen, but " + "lxc-attach is not available. LXC may not be installed." ) return wrapped(*args, **salt.utils.args.clean_kwargs(**kwargs)) + return wrapper def _nsenter(pid): - ''' + """ Return the nsenter command to attach to the named container - ''' - return ( - 'nsenter --target {0} --mount --uts --ipc --net --pid' - .format(pid) - ) + """ + return "nsenter --target {0} --mount --uts --ipc --net --pid".format(pid) def _get_md5(name, path, run_func): - ''' + """ Get the MD5 checksum of a file from a container - ''' - output = run_func(name, - 'md5sum {0}'.format(pipes.quote(path)), - ignore_retcode=True)['stdout'] + """ + output = run_func( + name, "md5sum {0}".format(pipes.quote(path)), ignore_retcode=True + )["stdout"] try: return output.split()[0] except IndexError: @@ -89,7 +90,7 @@ def _get_md5(name, path, run_func): def cache_file(source): - ''' + """ Wrapper for cp.cache_file which raises an error if the file was unable to be cached. @@ -98,37 +99,37 @@ def cache_file(source): .. code-block:: bash salt myminion container_resource.cache_file salt://foo/bar/baz.txt - ''' + """ try: # Don't just use cp.cache_file for this. Docker has its own code to # pull down images from the web. - if source.startswith('salt://'): - cached_source = __salt__['cp.cache_file'](source) + if source.startswith("salt://"): + cached_source = __salt__["cp.cache_file"](source) if not cached_source: - raise CommandExecutionError( - 'Unable to cache {0}'.format(source) - ) + raise CommandExecutionError("Unable to cache {0}".format(source)) return cached_source except AttributeError: - raise SaltInvocationError('Invalid source file {0}'.format(source)) + raise SaltInvocationError("Invalid source file {0}".format(source)) return source @_validate -def run(name, - cmd, - container_type=None, - exec_driver=None, - output=None, - no_start=False, - stdin=None, - python_shell=True, - output_loglevel='debug', - ignore_retcode=False, - path=None, - use_vt=False, - keep_env=None): - ''' +def run( + name, + cmd, + container_type=None, + exec_driver=None, + output=None, + no_start=False, + stdin=None, + python_shell=True, + output_loglevel="debug", + ignore_retcode=False, + path=None, + use_vt=False, + keep_env=None, +): + """ Common logic for running shell commands in containers path @@ -140,97 +141,105 @@ def run(name, .. code-block:: bash salt myminion container_resource.run mycontainer 'ps aux' container_type=docker exec_driver=nsenter output=stdout - ''' - valid_output = ('stdout', 'stderr', 'retcode', 'all') + """ + valid_output = ("stdout", "stderr", "retcode", "all") if output is None: - cmd_func = 'cmd.run' + cmd_func = "cmd.run" elif output not in valid_output: raise SaltInvocationError( - '\'output\' param must be one of the following: {0}' - .format(', '.join(valid_output)) + "'output' param must be one of the following: {0}".format( + ", ".join(valid_output) + ) ) else: - cmd_func = 'cmd.run_all' + cmd_func = "cmd.run_all" if keep_env is None or isinstance(keep_env, bool): to_keep = [] elif not isinstance(keep_env, (list, tuple)): try: - to_keep = keep_env.split(',') + to_keep = keep_env.split(",") except AttributeError: - log.warning('Invalid keep_env value, ignoring') + log.warning("Invalid keep_env value, ignoring") to_keep = [] else: to_keep = keep_env - if exec_driver == 'lxc-attach': - full_cmd = 'lxc-attach ' + if exec_driver == "lxc-attach": + full_cmd = "lxc-attach " if path: - full_cmd += '-P {0} '.format(pipes.quote(path)) + full_cmd += "-P {0} ".format(pipes.quote(path)) if keep_env is not True: - full_cmd += '--clear-env ' - if 'PATH' not in to_keep: - full_cmd += '--set-var {0} '.format(PATH) + full_cmd += "--clear-env " + if "PATH" not in to_keep: + full_cmd += "--set-var {0} ".format(PATH) # --clear-env results in a very restrictive PATH # (/bin:/usr/bin), use a good fallback. - full_cmd += ' '.join( - ['--set-var {0}={1}'.format(x, pipes.quote(os.environ[x])) + full_cmd += " ".join( + [ + "--set-var {0}={1}".format(x, pipes.quote(os.environ[x])) for x in to_keep - if x in os.environ] + if x in os.environ + ] ) - full_cmd += ' -n {0} -- {1}'.format(pipes.quote(name), cmd) - elif exec_driver == 'nsenter': - pid = __salt__['{0}.pid'.format(container_type)](name) - full_cmd = ( - 'nsenter --target {0} --mount --uts --ipc --net --pid -- ' - .format(pid) + full_cmd += " -n {0} -- {1}".format(pipes.quote(name), cmd) + elif exec_driver == "nsenter": + pid = __salt__["{0}.pid".format(container_type)](name) + full_cmd = "nsenter --target {0} --mount --uts --ipc --net --pid -- ".format( + pid ) if keep_env is not True: - full_cmd += 'env -i ' - if 'PATH' not in to_keep: - full_cmd += '{0} '.format(PATH) - full_cmd += ' '.join( - ['{0}={1}'.format(x, pipes.quote(os.environ[x])) + full_cmd += "env -i " + if "PATH" not in to_keep: + full_cmd += "{0} ".format(PATH) + full_cmd += " ".join( + [ + "{0}={1}".format(x, pipes.quote(os.environ[x])) for x in to_keep - if x in os.environ] + if x in os.environ + ] ) - full_cmd += ' {0}'.format(cmd) - elif exec_driver == 'docker-exec': + full_cmd += " {0}".format(cmd) + elif exec_driver == "docker-exec": # We're using docker exec on the CLI as opposed to via docker-py, since # the Docker API doesn't return stdout and stderr separately. - full_cmd = 'docker exec ' + full_cmd = "docker exec " if stdin: - full_cmd += '-i ' - full_cmd += '{0} '.format(name) + full_cmd += "-i " + full_cmd += "{0} ".format(name) if keep_env is not True: - full_cmd += 'env -i ' - if 'PATH' not in to_keep: - full_cmd += '{0} '.format(PATH) - full_cmd += ' '.join( - ['{0}={1}'.format(x, pipes.quote(os.environ[x])) + full_cmd += "env -i " + if "PATH" not in to_keep: + full_cmd += "{0} ".format(PATH) + full_cmd += " ".join( + [ + "{0}={1}".format(x, pipes.quote(os.environ[x])) for x in to_keep - if x in os.environ] + if x in os.environ + ] ) - full_cmd += ' {0}'.format(cmd) + full_cmd += " {0}".format(cmd) if not use_vt: - ret = __salt__[cmd_func](full_cmd, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - ignore_retcode=ignore_retcode) + ret = __salt__[cmd_func]( + full_cmd, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + ignore_retcode=ignore_retcode, + ) else: - stdout, stderr = '', '' + stdout, stderr = "", "" proc = salt.utils.vt.Terminal( full_cmd, shell=python_shell, - log_stdin_level='quiet' if output_loglevel == 'quiet' else 'info', + log_stdin_level="quiet" if output_loglevel == "quiet" else "info", log_stdout_level=output_loglevel, log_stderr_level=output_loglevel, log_stdout=True, log_stderr=True, stream_stdout=False, - stream_stderr=False + stream_stderr=False, ) # Consume output try: @@ -247,19 +256,24 @@ def run(name, time.sleep(0.5) except KeyboardInterrupt: break - ret = stdout if output is None \ - else {'retcode': proc.exitstatus, - 'pid': 2, - 'stdout': stdout, - 'stderr': stderr} + ret = ( + stdout + if output is None + else { + "retcode": proc.exitstatus, + "pid": 2, + "stdout": stdout, + "stderr": stderr, + } + ) except salt.utils.vt.TerminalException: trace = traceback.format_exc() log.error(trace) - ret = stdout if output is None \ - else {'retcode': 127, - 'pid': 2, - 'stdout': stdout, - 'stderr': stderr} + ret = ( + stdout + if output is None + else {"retcode": 127, "pid": 2, "stdout": stdout, "stderr": stderr} + ) finally: proc.terminate() @@ -267,15 +281,17 @@ def run(name, @_validate -def copy_to(name, - source, - dest, - container_type=None, - path=None, - exec_driver=None, - overwrite=False, - makedirs=False): - ''' +def copy_to( + name, + source, + dest, + container_type=None, + path=None, + exec_driver=None, + overwrite=False, + makedirs=False, +): + """ Common logic for copying files to containers path @@ -287,22 +303,21 @@ def copy_to(name, .. code-block:: bash salt myminion container_resource.copy_to mycontainer /local/file/path /container/file/path container_type=docker exec_driver=nsenter - ''' + """ # Get the appropriate functions - state = __salt__['{0}.state'.format(container_type)] + state = __salt__["{0}.state".format(container_type)] def run_all(*args, **akwargs): akwargs = copy.deepcopy(akwargs) - if container_type in ['lxc'] and 'path' not in akwargs: - akwargs['path'] = path - return __salt__['{0}.run_all'.format(container_type)]( - *args, **akwargs) + if container_type in ["lxc"] and "path" not in akwargs: + akwargs["path"] = path + return __salt__["{0}.run_all".format(container_type)](*args, **akwargs) state_kwargs = {} - cmd_kwargs = {'ignore_retcode': True} - if container_type in ['lxc']: - cmd_kwargs['path'] = path - state_kwargs['path'] = path + cmd_kwargs = {"ignore_retcode": True} + if container_type in ["lxc"]: + cmd_kwargs["path"] = path + state_kwargs["path"] = path def _state(name): if state_kwargs: @@ -311,30 +326,27 @@ def copy_to(name, return state(name) c_state = _state(name) - if c_state != 'running': - raise CommandExecutionError( - 'Container \'{0}\' is not running'.format(name) - ) + if c_state != "running": + raise CommandExecutionError("Container '{0}' is not running".format(name)) local_file = cache_file(source) source_dir, source_name = os.path.split(local_file) # Source file sanity checks if not os.path.isabs(local_file): - raise SaltInvocationError('Source path must be absolute') + raise SaltInvocationError("Source path must be absolute") elif not os.path.exists(local_file): - raise SaltInvocationError( - 'Source file {0} does not exist'.format(local_file) - ) + raise SaltInvocationError("Source file {0} does not exist".format(local_file)) elif not os.path.isfile(local_file): - raise SaltInvocationError('Source must be a regular file') + raise SaltInvocationError("Source must be a regular file") # Destination file sanity checks if not os.path.isabs(dest): - raise SaltInvocationError('Destination path must be absolute') - if run_all(name, - 'test -d {0}'.format(pipes.quote(dest)), - **cmd_kwargs)['retcode'] == 0: + raise SaltInvocationError("Destination path must be absolute") + if ( + run_all(name, "test -d {0}".format(pipes.quote(dest)), **cmd_kwargs)["retcode"] + == 0 + ): # Destination is a directory, full path to dest file will include the # basename of the source file. dest = os.path.join(dest, source_name) @@ -343,63 +355,77 @@ def copy_to(name, # dir is a directory, and then (if makedirs=True) attempt to create the # parent directory. dest_dir, dest_name = os.path.split(dest) - if run_all(name, - 'test -d {0}'.format(pipes.quote(dest_dir)), - **cmd_kwargs)['retcode'] != 0: + if ( + run_all(name, "test -d {0}".format(pipes.quote(dest_dir)), **cmd_kwargs)[ + "retcode" + ] + != 0 + ): if makedirs: - result = run_all(name, - 'mkdir -p {0}'.format(pipes.quote(dest_dir)), - **cmd_kwargs) - if result['retcode'] != 0: - error = ('Unable to create destination directory {0} in ' - 'container \'{1}\''.format(dest_dir, name)) - if result['stderr']: - error += ': {0}'.format(result['stderr']) + result = run_all( + name, "mkdir -p {0}".format(pipes.quote(dest_dir)), **cmd_kwargs + ) + if result["retcode"] != 0: + error = ( + "Unable to create destination directory {0} in " + "container '{1}'".format(dest_dir, name) + ) + if result["stderr"]: + error += ": {0}".format(result["stderr"]) raise CommandExecutionError(error) else: raise SaltInvocationError( - 'Directory {0} does not exist on {1} container \'{2}\'' - .format(dest_dir, container_type, name) + "Directory {0} does not exist on {1} container '{2}'".format( + dest_dir, container_type, name + ) ) - if not overwrite and run_all(name, - 'test -e {0}'.format(pipes.quote(dest)), - **cmd_kwargs)['retcode'] == 0: + if ( + not overwrite + and run_all(name, "test -e {0}".format(pipes.quote(dest)), **cmd_kwargs)[ + "retcode" + ] + == 0 + ): raise CommandExecutionError( - 'Destination path {0} already exists. Use overwrite=True to ' - 'overwrite it'.format(dest) + "Destination path {0} already exists. Use overwrite=True to " + "overwrite it".format(dest) ) # Before we try to replace the file, compare checksums. - source_md5 = __salt__['file.get_sum'](local_file, 'md5') + source_md5 = __salt__["file.get_sum"](local_file, "md5") if source_md5 == _get_md5(name, dest, run_all): - log.debug('{0} and {1}:{2} are the same file, skipping copy' - .format(source, name, dest)) + log.debug( + "{0} and {1}:{2} are the same file, skipping copy".format( + source, name, dest + ) + ) return True - log.debug('Copying {0} to {1} container \'{2}\' as {3}' - .format(source, container_type, name, dest)) + log.debug( + "Copying {0} to {1} container '{2}' as {3}".format( + source, container_type, name, dest + ) + ) # Using cat here instead of opening the file, reading it into memory, # and passing it as stdin to run(). This will keep down memory # usage for the minion and make the operation run quicker. - if exec_driver == 'lxc-attach': - lxcattach = 'lxc-attach' + if exec_driver == "lxc-attach": + lxcattach = "lxc-attach" if path: - lxcattach += ' -P {0}'.format(pipes.quote(path)) + lxcattach += " -P {0}".format(pipes.quote(path)) copy_cmd = ( 'cat "{0}" | {4} --clear-env --set-var {1} -n {2} -- ' 'tee "{3}"'.format(local_file, PATH, name, dest, lxcattach) ) - elif exec_driver == 'nsenter': - pid = __salt__['{0}.pid'.format(container_type)](name) - copy_cmd = ( - 'cat "{0}" | {1} env -i {2} tee "{3}"' - .format(local_file, _nsenter(pid), PATH, dest) + elif exec_driver == "nsenter": + pid = __salt__["{0}.pid".format(container_type)](name) + copy_cmd = 'cat "{0}" | {1} env -i {2} tee "{3}"'.format( + local_file, _nsenter(pid), PATH, dest ) - elif exec_driver == 'docker-exec': - copy_cmd = ( - 'cat "{0}" | docker exec -i {1} env -i {2} tee "{3}"' - .format(local_file, name, PATH, dest) + elif exec_driver == "docker-exec": + copy_cmd = 'cat "{0}" | docker exec -i {1} env -i {2} tee "{3}"'.format( + local_file, name, PATH, dest ) - __salt__['cmd.run'](copy_cmd, python_shell=True, output_loglevel='quiet') + __salt__["cmd.run"](copy_cmd, python_shell=True, output_loglevel="quiet") return source_md5 == _get_md5(name, dest, run_all) diff --git a/salt/modules/cp.py b/salt/modules/cp.py index 3e9bee25812..4f292fb4bcb 100644 --- a/salt/modules/cp.py +++ b/salt/modules/cp.py @@ -1,58 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" Minion side functions for salt-cp -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import errno -import os -import logging import fnmatch +import logging +import os + +import salt.crypt +import salt.fileclient # Import salt libs import salt.minion -import salt.fileclient +import salt.transport.client import salt.utils.data import salt.utils.files import salt.utils.gzip_util import salt.utils.path import salt.utils.templates import salt.utils.url -import salt.crypt -import salt.transport.client from salt.exceptions import CommandExecutionError -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=import-error,no-name-in-module # Import 3rd-party libs from salt.ext import six +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse log = logging.getLogger(__name__) -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] def _auth(): - ''' + """ Return the auth object - ''' - if 'auth' not in __context__: - __context__['auth'] = salt.crypt.SAuth(__opts__) - return __context__['auth'] + """ + if "auth" not in __context__: + __context__["auth"] = salt.crypt.SAuth(__opts__) + return __context__["auth"] def _gather_pillar(pillarenv, pillar_override): - ''' + """ Whenever a state run starts, gather the pillar data fresh - ''' + """ pillar = salt.pillar.get_pillar( __opts__, __grains__, - __opts__['id'], - __opts__['saltenv'], + __opts__["id"], + __opts__["saltenv"], pillar_override=pillar_override, - pillarenv=pillarenv + pillarenv=pillarenv, ) ret = pillar.compile_pillar() if pillar_override and isinstance(pillar_override, dict): @@ -61,26 +63,25 @@ def _gather_pillar(pillarenv, pillar_override): def recv(files, dest): - ''' + """ Used with salt-cp, pass the files dict, and the destination. This function receives small fast copy files from the master via salt-cp. It does not work via the CLI. - ''' + """ ret = {} for path, data in six.iteritems(files): - if os.path.basename(path) == os.path.basename(dest) \ - and not os.path.isdir(dest): + if os.path.basename(path) == os.path.basename(dest) and not os.path.isdir(dest): final = dest elif os.path.isdir(dest): final = os.path.join(dest, os.path.basename(path)) elif os.path.isdir(os.path.dirname(dest)): final = dest else: - return 'Destination unavailable' + return "Destination unavailable" try: - with salt.utils.files.fopen(final, 'w+') as fp_: + with salt.utils.files.fopen(final, "w+") as fp_: fp_.write(data) ret[final] = True except IOError: @@ -90,15 +91,15 @@ def recv(files, dest): def recv_chunked(dest, chunk, append=False, compressed=True, mode=None): - ''' + """ This function receives files copied to the minion using ``salt-cp`` and is not intended to be used directly on the CLI. - ''' - if 'retcode' not in __context__: - __context__['retcode'] = 0 + """ + if "retcode" not in __context__: + __context__["retcode"] = 0 def _error(msg): - __context__['retcode'] = 1 + __context__["retcode"] = 1 return msg if chunk is None: @@ -108,14 +109,14 @@ def recv_chunked(dest, chunk, append=False, compressed=True, mode=None): except OSError as exc: if exc.errno == errno.EEXIST: if os.path.isfile(dest): - return 'Path exists and is a file' + return "Path exists and is a file" else: return _error(exc.__str__()) return True chunk = base64.b64decode(chunk) - open_mode = 'ab' if append else 'wb' + open_mode = "ab" if append else "wb" try: fh_ = salt.utils.files.fopen(dest, open_mode) # pylint: disable=W8470 except (IOError, OSError) as exc: @@ -131,8 +132,7 @@ def recv_chunked(dest, chunk, append=False, compressed=True, mode=None): try: # Write the chunk to disk - fh_.write(salt.utils.gzip_util.uncompress(chunk) if compressed - else chunk) + fh_.write(salt.utils.gzip_util.uncompress(chunk) if compressed else chunk) except (IOError, OSError) as exc: # Write failed return _error(exc.__str__()) @@ -140,8 +140,8 @@ def recv_chunked(dest, chunk, append=False, compressed=True, mode=None): # Write successful if not append and mode is not None: # If this is the first chunk we're writing, set the mode - #log.debug('Setting mode for %s to %s', dest, oct(mode)) - log.debug('Setting mode for %s to %s', dest, mode) + # log.debug('Setting mode for %s to %s', dest, oct(mode)) + log.debug("Setting mode for %s to %s", dest, mode) try: os.chmod(dest, mode) except OSError: @@ -155,91 +155,84 @@ def recv_chunked(dest, chunk, append=False, compressed=True, mode=None): def _mk_client(): - ''' + """ Create a file client and add it to the context. Each file client needs to correspond to a unique copy of the opts dictionary, therefore it's hashed by the id of the __opts__ dict - ''' - if 'cp.fileclient_{0}'.format(id(__opts__)) not in __context__: - __context__['cp.fileclient_{0}'.format(id(__opts__))] = \ - salt.fileclient.get_file_client(__opts__) + """ + if "cp.fileclient_{0}".format(id(__opts__)) not in __context__: + __context__[ + "cp.fileclient_{0}".format(id(__opts__)) + ] = salt.fileclient.get_file_client(__opts__) def _client(): - ''' + """ Return a client, hashed by the list of masters - ''' + """ _mk_client() - return __context__['cp.fileclient_{0}'.format(id(__opts__))] + return __context__["cp.fileclient_{0}".format(id(__opts__))] def _render_filenames(path, dest, saltenv, template, **kw): - ''' + """ Process markup in the :param:`path` and :param:`dest` variables (NOT the files under the paths they ultimately point to) according to the markup format provided by :param:`template`. - ''' + """ if not template: return (path, dest) # render the path as a template using path_template_engine as the engine if template not in salt.utils.templates.TEMPLATE_REGISTRY: raise CommandExecutionError( - 'Attempted to render file paths with unavailable engine ' - '{0}'.format(template) + "Attempted to render file paths with unavailable engine " + "{0}".format(template) ) kwargs = {} - kwargs['salt'] = __salt__ - if 'pillarenv' in kw or 'pillar' in kw: - pillarenv = kw.get('pillarenv', __opts__.get('pillarenv')) - kwargs['pillar'] = _gather_pillar(pillarenv, kw.get('pillar')) + kwargs["salt"] = __salt__ + if "pillarenv" in kw or "pillar" in kw: + pillarenv = kw.get("pillarenv", __opts__.get("pillarenv")) + kwargs["pillar"] = _gather_pillar(pillarenv, kw.get("pillar")) else: - kwargs['pillar'] = __pillar__ - kwargs['grains'] = __grains__ - kwargs['opts'] = __opts__ - kwargs['saltenv'] = saltenv + kwargs["pillar"] = __pillar__ + kwargs["grains"] = __grains__ + kwargs["opts"] = __opts__ + kwargs["saltenv"] = saltenv def _render(contents): - ''' + """ Render :param:`contents` into a literal pathname by writing it to a temp file, rendering that file, and returning the result. - ''' + """ # write out path to temp file tmp_path_fn = salt.utils.files.mkstemp() - with salt.utils.files.fopen(tmp_path_fn, 'w+') as fp_: + with salt.utils.files.fopen(tmp_path_fn, "w+") as fp_: fp_.write(salt.utils.stringutils.to_str(contents)) data = salt.utils.templates.TEMPLATE_REGISTRY[template]( - tmp_path_fn, - to_str=True, - **kwargs + tmp_path_fn, to_str=True, **kwargs ) salt.utils.files.safe_rm(tmp_path_fn) - if not data['result']: + if not data["result"]: # Failed to render the template raise CommandExecutionError( - 'Failed to render file path with error: {0}'.format( - data['data'] - ) + "Failed to render file path with error: {0}".format(data["data"]) ) else: - return data['data'] + return data["data"] path = _render(path) dest = _render(dest) return (path, dest) -def get_file(path, - dest, - saltenv='base', - makedirs=False, - template=None, - gzip=None, - **kwargs): - ''' +def get_file( + path, dest, saltenv="base", makedirs=False, template=None, gzip=None, **kwargs +): + """ .. versionchanged:: 2018.3.0 ``dest`` can now be a directory @@ -283,7 +276,7 @@ def get_file(path, .. note:: It may be necessary to quote the URL when using the querystring method, depending on the shell being used to run the command. - ''' + """ (path, dest) = _render_filenames(path, dest, saltenv, template, **kwargs) path, senv = salt.utils.url.split_env(path) @@ -291,18 +284,13 @@ def get_file(path, saltenv = senv if not hash_file(path, saltenv): - return '' + return "" else: - return _client().get_file( - path, - dest, - makedirs, - saltenv, - gzip) + return _client().get_file(path, dest, makedirs, saltenv, gzip) def envs(): - ''' + """ List available environments for fileserver CLI Example @@ -310,17 +298,14 @@ def envs(): .. code-block:: bash salt '*' cp.envs - ''' + """ return _client().envs() -def get_template(path, - dest, - template='jinja', - saltenv='base', - makedirs=False, - **kwargs): - ''' +def get_template( + path, dest, template="jinja", saltenv="base", makedirs=False, **kwargs +): + """ Render a file as a template before setting it down. Warning, order is not the same as in fileclient.cp for non breaking old API. @@ -330,26 +315,20 @@ def get_template(path, .. code-block:: bash salt '*' cp.get_template salt://path/to/template /minion/dest - ''' - if 'salt' not in kwargs: - kwargs['salt'] = __salt__ - if 'pillar' not in kwargs: - kwargs['pillar'] = __pillar__ - if 'grains' not in kwargs: - kwargs['grains'] = __grains__ - if 'opts' not in kwargs: - kwargs['opts'] = __opts__ - return _client().get_template( - path, - dest, - template, - makedirs, - saltenv, - **kwargs) + """ + if "salt" not in kwargs: + kwargs["salt"] = __salt__ + if "pillar" not in kwargs: + kwargs["pillar"] = __pillar__ + if "grains" not in kwargs: + kwargs["grains"] = __grains__ + if "opts" not in kwargs: + kwargs["opts"] = __opts__ + return _client().get_template(path, dest, template, makedirs, saltenv, **kwargs) -def get_dir(path, dest, saltenv='base', template=None, gzip=None, **kwargs): - ''' +def get_dir(path, dest, saltenv="base", template=None, gzip=None, **kwargs): + """ Used to recursively copy a directory from the salt master CLI Example: @@ -359,14 +338,14 @@ def get_dir(path, dest, saltenv='base', template=None, gzip=None, **kwargs): salt '*' cp.get_dir salt://path/to/dir/ /minion/dest get_dir supports the same template and gzip arguments as get_file. - ''' + """ (path, dest) = _render_filenames(path, dest, saltenv, template, **kwargs) return _client().get_dir(path, dest, saltenv, gzip) -def get_url(path, dest='', saltenv='base', makedirs=False, source_hash=None): - ''' +def get_url(path, dest="", saltenv="base", makedirs=False, source_hash=None): + """ .. versionchanged:: 2018.3.0 ``dest`` can now be a directory @@ -412,24 +391,28 @@ def get_url(path, dest='', saltenv='base', makedirs=False, source_hash=None): salt '*' cp.get_url salt://my/file /tmp/this_file_is_mine salt '*' cp.get_url http://www.slashdot.org /tmp/index.html - ''' + """ if isinstance(dest, six.string_types): result = _client().get_url( - path, dest, makedirs, saltenv, source_hash=source_hash) + path, dest, makedirs, saltenv, source_hash=source_hash + ) else: result = _client().get_url( - path, None, makedirs, saltenv, no_cache=True, source_hash=source_hash) + path, None, makedirs, saltenv, no_cache=True, source_hash=source_hash + ) if not result: - log.error('Unable to fetch file %s from saltenv %s.', - salt.utils.url.redact_http_basic_auth(path), - saltenv) + log.error( + "Unable to fetch file %s from saltenv %s.", + salt.utils.url.redact_http_basic_auth(path), + saltenv, + ) if result: return salt.utils.stringutils.to_unicode(result) return result -def get_file_str(path, saltenv='base'): - ''' +def get_file_str(path, saltenv="base"): + """ Download a file from a URL to the Minion cache directory and return the contents of that file @@ -440,19 +423,19 @@ def get_file_str(path, saltenv='base'): .. code-block:: bash salt '*' cp.get_file_str salt://my/file - ''' + """ fn_ = cache_file(path, saltenv) if isinstance(fn_, six.string_types): try: - with salt.utils.files.fopen(fn_, 'r') as fp_: + with salt.utils.files.fopen(fn_, "r") as fp_: return salt.utils.stringutils.to_unicode(fp_.read()) except IOError: return False return fn_ -def cache_file(path, saltenv='base', source_hash=None): - ''' +def cache_file(path, saltenv="base", source_hash=None): + """ Used to cache a single file on the Minion Returns the location of the new cached file on the Minion @@ -486,11 +469,11 @@ def cache_file(path, saltenv='base', source_hash=None): .. note:: It may be necessary to quote the URL when using the querystring method, depending on the shell being used to run the command. - ''' + """ path = salt.utils.data.decode(path) saltenv = salt.utils.data.decode(saltenv) - contextkey = '{0}_|-{1}_|-{2}'.format('cp.cache_file', path, saltenv) + contextkey = "{0}_|-{1}_|-{2}".format("cp.cache_file", path, saltenv) path_is_remote = _urlparse(path).scheme in salt.utils.files.REMOTE_PROTOS try: @@ -516,10 +499,7 @@ def cache_file(path, saltenv='base', source_hash=None): result = _client().cache_file(path, saltenv, source_hash=source_hash) if not result: - log.error( - 'Unable to cache file \'%s\' from saltenv \'%s\'.', - path, saltenv - ) + log.error("Unable to cache file '%s' from saltenv '%s'.", path, saltenv) if path_is_remote: # Cache was successful, store the result in __context__ to prevent # multiple caches (see above). @@ -527,8 +507,8 @@ def cache_file(path, saltenv='base', source_hash=None): return result -def cache_files(paths, saltenv='base'): - ''' +def cache_files(paths, saltenv="base"): + """ Used to gather many files from the Master, the gathered files will be saved in the minion cachedir reflective to the paths retrieved from the Master @@ -562,13 +542,14 @@ def cache_files(paths, saltenv='base'): .. note:: It may be necessary to quote the URL when using the querystring method, depending on the shell being used to run the command. - ''' + """ return _client().cache_files(paths, saltenv) -def cache_dir(path, saltenv='base', include_empty=False, include_pat=None, - exclude_pat=None): - ''' +def cache_dir( + path, saltenv="base", include_empty=False, include_pat=None, exclude_pat=None +): + """ Download and cache everything under a directory from the master @@ -598,14 +579,12 @@ def cache_dir(path, saltenv='base', include_empty=False, include_pat=None, salt '*' cp.cache_dir salt://path/to/dir salt '*' cp.cache_dir salt://path/to/dir include_pat='E@*.py$' - ''' - return _client().cache_dir( - path, saltenv, include_empty, include_pat, exclude_pat - ) + """ + return _client().cache_dir(path, saltenv, include_empty, include_pat, exclude_pat) -def cache_master(saltenv='base'): - ''' +def cache_master(saltenv="base"): + """ Retrieve all of the files on the master and cache them locally CLI Example: @@ -613,12 +592,12 @@ def cache_master(saltenv='base'): .. code-block:: bash salt '*' cp.cache_master - ''' + """ return _client().cache_master(saltenv) def cache_local_file(path): - ''' + """ Cache a local file on the minion in the localfiles cache CLI Example: @@ -626,9 +605,9 @@ def cache_local_file(path): .. code-block:: bash salt '*' cp.cache_local_file /etc/hosts - ''' + """ if not os.path.exists(path): - return '' + return "" path_cached = is_cached(path) @@ -637,15 +616,15 @@ def cache_local_file(path): path_hash = hash_file(path) path_cached_hash = hash_file(path_cached) - if path_hash['hsum'] == path_cached_hash['hsum']: + if path_hash["hsum"] == path_cached_hash["hsum"]: return path_cached # The file hasn't been cached or has changed; cache it return _client().cache_local_file(path) -def list_states(saltenv='base'): - ''' +def list_states(saltenv="base"): + """ List all of the available state modules in an environment CLI Example: @@ -653,12 +632,12 @@ def list_states(saltenv='base'): .. code-block:: bash salt '*' cp.list_states - ''' + """ return _client().list_states(saltenv) -def list_master(saltenv='base', prefix=''): - ''' +def list_master(saltenv="base", prefix=""): + """ List all of the files stored on the master CLI Example: @@ -666,12 +645,12 @@ def list_master(saltenv='base', prefix=''): .. code-block:: bash salt '*' cp.list_master - ''' + """ return _client().file_list(saltenv, prefix) -def list_master_dirs(saltenv='base', prefix=''): - ''' +def list_master_dirs(saltenv="base", prefix=""): + """ List all of the directories stored on the master CLI Example: @@ -679,12 +658,12 @@ def list_master_dirs(saltenv='base', prefix=''): .. code-block:: bash salt '*' cp.list_master_dirs - ''' + """ return _client().dir_list(saltenv, prefix) -def list_master_symlinks(saltenv='base', prefix=''): - ''' +def list_master_symlinks(saltenv="base", prefix=""): + """ List all of the symlinks stored on the master CLI Example: @@ -692,12 +671,12 @@ def list_master_symlinks(saltenv='base', prefix=''): .. code-block:: bash salt '*' cp.list_master_symlinks - ''' + """ return _client().symlink_list(saltenv, prefix) -def list_minion(saltenv='base'): - ''' +def list_minion(saltenv="base"): + """ List all of the files cached on the minion CLI Example: @@ -705,12 +684,12 @@ def list_minion(saltenv='base'): .. code-block:: bash salt '*' cp.list_minion - ''' + """ return _client().file_local_list(saltenv) -def is_cached(path, saltenv='base'): - ''' +def is_cached(path, saltenv="base"): + """ Returns the full path to a file if it is cached locally on the minion otherwise returns a blank string @@ -719,12 +698,12 @@ def is_cached(path, saltenv='base'): .. code-block:: bash salt '*' cp.is_cached salt://path/to/file - ''' + """ return _client().is_cached(path, saltenv) -def hash_file(path, saltenv='base'): - ''' +def hash_file(path, saltenv="base"): + """ Return the hash of a file, to get the hash of a file on the salt master file server prepend the path with salt:// otherwise, prepend the file with / for a local file. @@ -734,7 +713,7 @@ def hash_file(path, saltenv='base'): .. code-block:: bash salt '*' cp.hash_file salt://path/to/file - ''' + """ path, senv = salt.utils.url.split_env(path) if senv: saltenv = senv @@ -742,8 +721,8 @@ def hash_file(path, saltenv='base'): return _client().hash_file(path, saltenv) -def stat_file(path, saltenv='base', octal=True): - ''' +def stat_file(path, saltenv="base", octal=True): + """ Return the permissions of a file, to get the permissions of a file on the salt master file server prepend the path with salt:// otherwise, prepend the file with / for a local file. @@ -753,7 +732,7 @@ def stat_file(path, saltenv='base', octal=True): .. code-block:: bash salt '*' cp.stat_file salt://path/to/file - ''' + """ path, senv = salt.utils.url.split_env(path) if senv: saltenv = senv @@ -765,7 +744,7 @@ def stat_file(path, saltenv='base', octal=True): def push(path, keep_symlinks=False, upload_path=None, remove_source=False): - ''' + """ WARNING Files pushed to the master will have global read permissions.. Push a file from the minion up to the master, the file will be saved to @@ -796,22 +775,22 @@ def push(path, keep_symlinks=False, upload_path=None, remove_source=False): salt '*' cp.push /etc/system-release keep_symlinks=True salt '*' cp.push /etc/fstab upload_path='/new/path/fstab' salt '*' cp.push /tmp/filename remove_source=True - ''' - log.debug('Trying to copy \'%s\' to master', path) - if '../' in path or not os.path.isabs(path): - log.debug('Path must be absolute, returning False') + """ + log.debug("Trying to copy '%s' to master", path) + if "../" in path or not os.path.isabs(path): + log.debug("Path must be absolute, returning False") return False if not keep_symlinks: path = os.path.realpath(path) if not os.path.isfile(path): - log.debug('Path failed os.path.isfile check, returning False') + log.debug("Path failed os.path.isfile check, returning False") return False auth = _auth() if upload_path: - if '../' in upload_path: - log.debug('Path must be absolute, returning False') - log.debug('Bad path: %s', upload_path) + if "../" in upload_path: + log.debug("Path must be absolute, returning False") + log.debug("Bad path: %s", upload_path) return False load_path = upload_path.lstrip(os.sep) else: @@ -826,39 +805,43 @@ def push(path, keep_symlinks=False, upload_path=None, remove_source=False): # Finally, split the remaining path into a list for delivery to the master load_path_list = [_f for _f in load_path_split_drive.split(os.sep) if _f] - load = {'cmd': '_file_recv', - 'id': __opts__['id'], - 'path': load_path_list, - 'size': os.path.getsize(path), - 'tok': auth.gen_token(b'salt')} + load = { + "cmd": "_file_recv", + "id": __opts__["id"], + "path": load_path_list, + "size": os.path.getsize(path), + "tok": auth.gen_token(b"salt"), + } with salt.transport.client.ReqChannel.factory(__opts__) as channel: - with salt.utils.files.fopen(path, 'rb') as fp_: + with salt.utils.files.fopen(path, "rb") as fp_: init_send = False while True: - load['loc'] = fp_.tell() - load['data'] = fp_.read(__opts__['file_buffer_size']) - if not load['data'] and init_send: + load["loc"] = fp_.tell() + load["data"] = fp_.read(__opts__["file_buffer_size"]) + if not load["data"] and init_send: if remove_source: try: salt.utils.files.rm_rf(path) - log.debug('Removing source file \'%s\'', path) + log.debug("Removing source file '%s'", path) except IOError: - log.error('cp.push failed to remove file \'%s\'', path) + log.error("cp.push failed to remove file '%s'", path) return False return True ret = channel.send(load) if not ret: - log.error('cp.push Failed transfer failed. Ensure master has ' - '\'file_recv\' set to \'True\' and that the file ' - 'is not larger than the \'file_recv_size_max\' ' - 'setting on the master.') + log.error( + "cp.push Failed transfer failed. Ensure master has " + "'file_recv' set to 'True' and that the file " + "is not larger than the 'file_recv_size_max' " + "setting on the master." + ) return ret init_send = True def push_dir(path, glob=None, upload_path=None): - ''' + """ Push a directory from the minion up to the master, the files will be saved to the salt master in the master's minion files cachedir (defaults to ``/var/cache/salt/master/minions/minion-id/files``). It also has a glob @@ -881,8 +864,8 @@ def push_dir(path, glob=None, upload_path=None): salt '*' cp.push /usr/lib/mysql salt '*' cp.push /usr/lib/mysql upload_path='/newmysql/path' salt '*' cp.push_dir /etc/modprobe.d/ glob='*.conf' - ''' - if '../' in path or not os.path.isabs(path): + """ + if "../" in path or not os.path.isabs(path): return False tmpupload_path = upload_path path = os.path.realpath(path) @@ -893,15 +876,18 @@ def push_dir(path, glob=None, upload_path=None): for root, _, files in salt.utils.path.os_walk(path): filelist += [os.path.join(root, tmpfile) for tmpfile in files] if glob is not None: - filelist = [fi for fi in filelist if fnmatch.fnmatch(os.path.basename(fi), glob)] + filelist = [ + fi for fi in filelist if fnmatch.fnmatch(os.path.basename(fi), glob) + ] if not filelist: return False for tmpfile in filelist: if upload_path and tmpfile.startswith(path): - tmpupload_path = os.path.join(os.path.sep, - upload_path.strip(os.path.sep), - tmpfile.replace(path, '') - .strip(os.path.sep)) + tmpupload_path = os.path.join( + os.path.sep, + upload_path.strip(os.path.sep), + tmpfile.replace(path, "").strip(os.path.sep), + ) ret = push(tmpfile, upload_path=tmpupload_path) if not ret: return ret diff --git a/salt/modules/cpan.py b/salt/modules/cpan.py index ab765f2bd01..506eef2be1e 100644 --- a/salt/modules/cpan.py +++ b/salt/modules/cpan.py @@ -1,15 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" Manage Perl modules using CPAN .. versionadded:: 2015.5.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os import os.path -import logging # Import salt libs import salt.utils.files @@ -18,22 +19,20 @@ import salt.utils.path log = logging.getLogger(__name__) # Don't shadow built-ins. -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} def __virtual__(): - ''' + """ Only work on supported POSIX-like systems - ''' - if salt.utils.path.which('cpan'): + """ + if salt.utils.path.which("cpan"): return True - return (False, 'Unable to locate cpan. Make sure it is installed and in the PATH.') + return (False, "Unable to locate cpan. Make sure it is installed and in the PATH.") def install(module): - ''' + """ Install a Perl module from CPAN CLI Example: @@ -41,30 +40,30 @@ def install(module): .. code-block:: bash salt '*' cpan.install Template::Alloy - ''' + """ ret = { - 'old': None, - 'new': None, + "old": None, + "new": None, } old_info = show(module) - cmd = 'cpan -i {0}'.format(module) - out = __salt__['cmd.run'](cmd) + cmd = "cpan -i {0}".format(module) + out = __salt__["cmd.run"](cmd) if "don't know what it is" in out: - ret['error'] = 'CPAN cannot identify this package' + ret["error"] = "CPAN cannot identify this package" return ret new_info = show(module) - ret['old'] = old_info.get('installed version', None) - ret['new'] = new_info['installed version'] + ret["old"] = old_info.get("installed version", None) + ret["new"] = new_info["installed version"] return ret def remove(module, details=False): - ''' + """ Attempt to remove a Perl module that was installed from CPAN. Because the ``cpan`` command doesn't actually support "uninstall"-like functionality, this function will attempt to do what it can, with what it has from CPAN. @@ -76,62 +75,58 @@ def remove(module, details=False): .. code-block:: bash salt '*' cpan.remove Old::Package - ''' + """ ret = { - 'old': None, - 'new': None, + "old": None, + "new": None, } info = show(module) - if 'error' in info: - return { - 'error': info['error'] - } + if "error" in info: + return {"error": info["error"]} - version = info.get('installed version', None) + version = info.get("installed version", None) if version is None: return ret - ret['old'] = version + ret["old"] = version - if 'cpan build dirs' not in info: - return { - 'error': 'No CPAN data available to use for uninstalling' - } + if "cpan build dirs" not in info: + return {"error": "No CPAN data available to use for uninstalling"} - mod_pathfile = module.replace('::', '/') + '.pm' - ins_path = info['installed file'].replace(mod_pathfile, '') + mod_pathfile = module.replace("::", "/") + ".pm" + ins_path = info["installed file"].replace(mod_pathfile, "") files = [] - for build_dir in info['cpan build dirs']: + for build_dir in info["cpan build dirs"]: contents = os.listdir(build_dir) - if 'MANIFEST' not in contents: + if "MANIFEST" not in contents: continue - mfile = os.path.join(build_dir, 'MANIFEST') - with salt.utils.files.fopen(mfile, 'r') as fh_: + mfile = os.path.join(build_dir, "MANIFEST") + with salt.utils.files.fopen(mfile, "r") as fh_: for line in fh_.readlines(): line = salt.utils.stringutils.to_unicode(line) - if line.startswith('lib/'): - files.append(line.replace('lib/', ins_path).strip()) + if line.startswith("lib/"): + files.append(line.replace("lib/", ins_path).strip()) rm_details = {} for file_ in files: if file_ in rm_details: continue - log.trace('Removing {0}'.format(file_)) - if __salt__['file.remove'](file_): - rm_details[file_] = 'removed' + log.trace("Removing {0}".format(file_)) + if __salt__["file.remove"](file_): + rm_details[file_] = "removed" else: - rm_details[file_] = 'unable to remove' + rm_details[file_] = "unable to remove" if details: - ret['details'] = rm_details + ret["details"] = rm_details return ret def list_(): - ''' + """ List installed Perl modules, and the version installed CLI Example: @@ -139,10 +134,10 @@ def list_(): .. code-block:: bash salt '*' cpan.list - ''' + """ ret = {} - cmd = 'cpan -l' - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "cpan -l" + out = __salt__["cmd.run"](cmd).splitlines() for line in out: comps = line.split() ret[comps[0]] = comps[1] @@ -150,7 +145,7 @@ def list_(): def show(module): - ''' + """ Show information about a specific Perl module CLI Example: @@ -158,64 +153,64 @@ def show(module): .. code-block:: bash salt '*' cpan.show Template::Alloy - ''' + """ ret = {} - ret['name'] = module + ret["name"] = module # This section parses out details from CPAN, if possible - cmd = 'cpan -D {0}'.format(module) - out = __salt__['cmd.run'](cmd).splitlines() - mode = 'skip' + cmd = "cpan -D {0}".format(module) + out = __salt__["cmd.run"](cmd).splitlines() + mode = "skip" info = [] for line in out: - if line.startswith('-------------'): - mode = 'parse' + if line.startswith("-------------"): + mode = "parse" continue - if mode == 'skip': + if mode == "skip": continue info.append(line) if len(info) == 6: # If the module is not installed, we'll be short a line - info.insert(2, '') + info.insert(2, "") if len(info) < 6: # This must not be a real package - ret['error'] = 'This package does not seem to exist' + ret["error"] = "This package does not seem to exist" return ret - ret['description'] = info[0].strip() - ret['cpan file'] = info[1].strip() + ret["description"] = info[0].strip() + ret["cpan file"] = info[1].strip() if info[2].strip(): - ret['installed file'] = info[2].strip() + ret["installed file"] = info[2].strip() else: - ret['installed file'] = None - comps = info[3].split(':') + ret["installed file"] = None + comps = info[3].split(":") if len(comps) > 1: - ret['installed version'] = comps[1].strip() - if 'installed version' not in ret or not ret['installed version']: - ret['installed version'] = None - comps = info[4].split(':') + ret["installed version"] = comps[1].strip() + if "installed version" not in ret or not ret["installed version"]: + ret["installed version"] = None + comps = info[4].split(":") comps = comps[1].split() - ret['cpan version'] = comps[0].strip() - ret['author name'] = info[5].strip() - ret['author email'] = info[6].strip() + ret["cpan version"] = comps[0].strip() + ret["author name"] = info[5].strip() + ret["author email"] = info[6].strip() # Check and see if there are cpan build directories config = show_config() - build_dir = config.get('build_dir', None) + build_dir = config.get("build_dir", None) if build_dir is not None: - ret['cpan build dirs'] = [] + ret["cpan build dirs"] = [] builds = os.listdir(build_dir) - pfile = module.replace('::', '-') + pfile = module.replace("::", "-") for file_ in builds: if file_.startswith(pfile): - ret['cpan build dirs'].append(os.path.join(build_dir, file_)) + ret["cpan build dirs"].append(os.path.join(build_dir, file_)) return ret def show_config(): - ''' + """ Return a dict of CPAN configuration values CLI Example: @@ -223,16 +218,16 @@ def show_config(): .. code-block:: bash salt '*' cpan.show_config - ''' + """ ret = {} - cmd = 'cpan -J' - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "cpan -J" + out = __salt__["cmd.run"](cmd).splitlines() for line in out: - if '=>' not in line: + if "=>" not in line: # TODO: Some options take up multiple lines, so this doesn't always work continue - comps = line.split('=>') - key = comps[0].replace("'", '').strip() - val = comps[1].replace("',", '').replace("'", '').strip() + comps = line.split("=>") + key = comps[0].replace("'", "").strip() + val = comps[1].replace("',", "").replace("'", "").strip() ret[key] = val return ret diff --git a/salt/modules/cron.py b/salt/modules/cron.py index a81c62552d4..3336897d902 100644 --- a/salt/modules/cron.py +++ b/salt/modules/cron.py @@ -1,13 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Work with cron .. note:: Salt does not escape cron metacharacters automatically. You should backslash-escape percent characters and any other metacharacters that might be interpreted incorrectly by the shell. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import python libs import os @@ -19,20 +21,21 @@ import salt.utils.files import salt.utils.functools import salt.utils.path import salt.utils.stringutils - from salt.ext import six from salt.ext.six.moves import range -TAG = '# Lines below here are managed by Salt, do not edit\n' -SALT_CRON_IDENTIFIER = 'SALT_CRON_IDENTIFIER' -SALT_CRON_NO_IDENTIFIER = 'NO ID SET' +TAG = "# Lines below here are managed by Salt, do not edit\n" +SALT_CRON_IDENTIFIER = "SALT_CRON_IDENTIFIER" +SALT_CRON_NO_IDENTIFIER = "NO ID SET" + +log = logging.getLogger(__name__) def __virtual__(): - if salt.utils.path.which('crontab'): + if salt.utils.path.which("crontab"): return True else: - return (False, 'Cannot load cron module: crontab command not found') + return (False, "Cannot load cron module: crontab command not found") def _ensure_string(val): @@ -43,14 +46,14 @@ def _ensure_string(val): try: return salt.utils.stringutils.to_unicode(val) except TypeError: - return '' + return "" def _cron_id(cron): - '''SAFETYBELT, Only set if we really have an identifier''' + """SAFETYBELT, Only set if we really have an identifier""" cid = None - if cron['identifier']: - cid = cron['identifier'] + if cron["identifier"]: + cid = cron["identifier"] else: cid = SALT_CRON_NO_IDENTIFIER if cid: @@ -58,14 +61,14 @@ def _cron_id(cron): def _cron_matched(cron, cmd, identifier=None): - '''Check if: + """Check if: - we find a cron with same cmd, old state behavior - but also be smart enough to remove states changed crons where we do not removed priorly by a cron.absent by matching on the provided identifier. We assure retrocompatibility by only checking on identifier if and only if an identifier was set on the serialized crontab - ''' + """ ret, id_matched = False, None cid = _cron_id(cron) if cid: @@ -77,7 +80,7 @@ def _cron_matched(cron, cmd, identifier=None): # we have not the same command, but the default id # to not set that as a match if ( - cron.get('cmd', None) != cmd + cron.get("cmd", None) != cmd and cid == SALT_CRON_NO_IDENTIFIER and eidentifier == SALT_CRON_NO_IDENTIFIER ): @@ -88,25 +91,22 @@ def _cron_matched(cron, cmd, identifier=None): # crons where command is the same # but with the default if that we gonna overwrite if ( - cron.get('cmd', None) == cmd + cron.get("cmd", None) == cmd and cid == SALT_CRON_NO_IDENTIFIER and identifier ): cid = eidentifier id_matched = eidentifier == cid - if ( - ((id_matched is None) and cmd == cron.get('cmd', None)) - or id_matched - ): + if ((id_matched is None) and cmd == cron.get("cmd", None)) or id_matched: ret = True return ret def _needs_change(old, new): if old != new: - if new == 'random': + if new == "random": # Allow switch from '*' or not present to 'random' - if old == '*': + if old == "*": return True elif new is not None: return True @@ -114,86 +114,84 @@ def _needs_change(old, new): def _render_tab(lst): - ''' + """ Takes a tab list structure and renders it to a list for applying it to a file - ''' + """ ret = [] - for pre in lst['pre']: - ret.append('{0}\n'.format(pre)) + for pre in lst["pre"]: + ret.append("{0}\n".format(pre)) if ret: if ret[-1] != TAG: ret.append(TAG) else: ret.append(TAG) - for env in lst['env']: - if (env['value'] is None) or (env['value'] == ""): - ret.append('{0}=""\n'.format(env['name'])) + for env in lst["env"]: + if (env["value"] is None) or (env["value"] == ""): + ret.append('{0}=""\n'.format(env["name"])) else: - ret.append('{0}={1}\n'.format(env['name'], env['value'])) - for cron in lst['crons']: - if cron['comment'] is not None or cron['identifier'] is not None: - comment = '#' - if cron['comment']: - comment += ' {0}'.format( - cron['comment'].replace('\n', '\n# ')) - if cron['identifier']: - comment += ' {0}:{1}'.format(SALT_CRON_IDENTIFIER, - cron['identifier']) + ret.append("{0}={1}\n".format(env["name"], env["value"])) + for cron in lst["crons"]: + if cron["comment"] is not None or cron["identifier"] is not None: + comment = "#" + if cron["comment"]: + comment += " {0}".format(cron["comment"].replace("\n", "\n# ")) + if cron["identifier"]: + comment += " {0}:{1}".format(SALT_CRON_IDENTIFIER, cron["identifier"]) - comment += '\n' + comment += "\n" ret.append(comment) - ret.append('{0}{1} {2} {3} {4} {5} {6}\n'.format( - cron['commented'] is True and '#DISABLED#' or '', - cron['minute'], - cron['hour'], - cron['daymonth'], - cron['month'], - cron['dayweek'], - cron['cmd'] - ) - ) - for cron in lst['special']: - if cron['comment'] is not None or cron['identifier'] is not None: - comment = '#' - if cron['comment']: - comment += ' {0}'.format( - cron['comment'].rstrip().replace('\n', '\n# ')) - if cron['identifier']: - comment += ' {0}:{1}'.format(SALT_CRON_IDENTIFIER, - cron['identifier']) + ret.append( + "{0}{1} {2} {3} {4} {5} {6}\n".format( + cron["commented"] is True and "#DISABLED#" or "", + cron["minute"], + cron["hour"], + cron["daymonth"], + cron["month"], + cron["dayweek"], + cron["cmd"], + ) + ) + for cron in lst["special"]: + if cron["comment"] is not None or cron["identifier"] is not None: + comment = "#" + if cron["comment"]: + comment += " {0}".format(cron["comment"].rstrip().replace("\n", "\n# ")) + if cron["identifier"]: + comment += " {0}:{1}".format(SALT_CRON_IDENTIFIER, cron["identifier"]) - comment += '\n' + comment += "\n" ret.append(comment) - ret.append('{0}{1} {2}\n'.format( - cron['commented'] is True and '#DISABLED#' or '', - cron['spec'], - cron['cmd'] - ) - ) + ret.append( + "{0}{1} {2}\n".format( + cron["commented"] is True and "#DISABLED#" or "", + cron["spec"], + cron["cmd"], + ) + ) return ret def _get_cron_cmdstr(path, user=None): - ''' + """ Returns a format string, to be used to build a crontab command. - ''' + """ if user: - cmd = 'crontab -u {0}'.format(user) + cmd = "crontab -u {0}".format(user) else: - cmd = 'crontab' - return '{0} {1}'.format(cmd, path) + cmd = "crontab" + return "{0} {1}".format(cmd, path) def _check_instance_uid_match(user): - ''' + """ Returns true if running instance's UID matches the specified user UID - ''' - return os.geteuid() == __salt__['file.user_to_uid'](user) + """ + return os.geteuid() == __salt__["file.user_to_uid"](user) def write_cron_file(user, path): - ''' + """ Writes the contents of a file to a user's crontab CLI Example: @@ -207,29 +205,36 @@ def write_cron_file(user, path): .. note:: Some OS' do not support specifying user via the `crontab` command i.e. (Solaris, AIX) - ''' + """ # Some OS' do not support specifying user via the `crontab` command - if __grains__.get('os_family') in ('Solaris', 'AIX'): - return __salt__['cmd.retcode'](_get_cron_cmdstr(path), - runas=user, - python_shell=False) == 0 + if __grains__.get("os_family") in ("Solaris", "AIX"): + return ( + __salt__["cmd.retcode"]( + _get_cron_cmdstr(path), runas=user, python_shell=False + ) + == 0 + ) # If Salt is running from same user as requested in cron module we don't need any user switch elif _check_instance_uid_match(user): - return __salt__['cmd.retcode'](_get_cron_cmdstr(path), - python_shell=False) == 0 + return __salt__["cmd.retcode"](_get_cron_cmdstr(path), python_shell=False) == 0 # If Salt is running from root user it could modify any user's crontab - elif _check_instance_uid_match('root'): - return __salt__['cmd.retcode'](_get_cron_cmdstr(path, user), - python_shell=False) == 0 + elif _check_instance_uid_match("root"): + return ( + __salt__["cmd.retcode"](_get_cron_cmdstr(path, user), python_shell=False) + == 0 + ) # Edge cases here, let's try do a runas else: - return __salt__['cmd.retcode'](_get_cron_cmdstr(path), - runas=user, - python_shell=False) == 0 + return ( + __salt__["cmd.retcode"]( + _get_cron_cmdstr(path), runas=user, python_shell=False + ) + == 0 + ) def write_cron_file_verbose(user, path): - ''' + """ Writes the contents of a file to a user's crontab and return error message on error CLI Example: @@ -243,61 +248,68 @@ def write_cron_file_verbose(user, path): .. note:: Some OS' do not support specifying user via the `crontab` command i.e. (Solaris, AIX) - ''' + """ # Some OS' do not support specifying user via the `crontab` command - if __grains__.get('os_family') in ('Solaris', 'AIX'): - return __salt__['cmd.run_all'](_get_cron_cmdstr(path), - runas=user, - python_shell=False) + if __grains__.get("os_family") in ("Solaris", "AIX"): + return __salt__["cmd.run_all"]( + _get_cron_cmdstr(path), runas=user, python_shell=False + ) # If Salt is running from same user as requested in cron module we don't need any user switch elif _check_instance_uid_match(user): - return __salt__['cmd.run_all'](_get_cron_cmdstr(path), - python_shell=False) + return __salt__["cmd.run_all"](_get_cron_cmdstr(path), python_shell=False) # If Salt is running from root user it could modify any user's crontab - elif _check_instance_uid_match('root'): - return __salt__['cmd.run_all'](_get_cron_cmdstr(path, user), - python_shell=False) + elif _check_instance_uid_match("root"): + return __salt__["cmd.run_all"](_get_cron_cmdstr(path, user), python_shell=False) # Edge cases here, let's try do a runas else: - return __salt__['cmd.run_all'](_get_cron_cmdstr(path), - runas=user, - python_shell=False) + return __salt__["cmd.run_all"]( + _get_cron_cmdstr(path), runas=user, python_shell=False + ) def _write_cron_lines(user, lines): - ''' + """ Takes a list of lines to be committed to a user's crontab and writes it - ''' + """ lines = [salt.utils.stringutils.to_str(_l) for _l in lines] path = salt.utils.files.mkstemp() - if _check_instance_uid_match('root') or __grains__.get('os_family') in ('Solaris', 'AIX'): + if _check_instance_uid_match("root") or __grains__.get("os_family") in ( + "Solaris", + "AIX", + ): # In some cases crontab command should be executed as user rather than root - with salt.utils.files.fpopen(path, 'w+', uid=__salt__['file.user_to_uid'](user), mode=0o600) as fp_: + with salt.utils.files.fpopen( + path, "w+", uid=__salt__["file.user_to_uid"](user), mode=0o600 + ) as fp_: fp_.writelines(lines) - ret = __salt__['cmd.run_all'](_get_cron_cmdstr(path), - runas=user, - python_shell=False) + ret = __salt__["cmd.run_all"]( + _get_cron_cmdstr(path), runas=user, python_shell=False + ) else: - with salt.utils.files.fpopen(path, 'w+', mode=0o600) as fp_: + with salt.utils.files.fpopen(path, "w+", mode=0o600) as fp_: fp_.writelines(lines) - ret = __salt__['cmd.run_all'](_get_cron_cmdstr(path, user), - python_shell=False) + ret = __salt__["cmd.run_all"](_get_cron_cmdstr(path, user), python_shell=False) os.remove(path) return ret def _date_time_match(cron, **kwargs): - ''' + """ Returns true if the minute, hour, etc. params match their counterparts from the dict returned from list_tab(). - ''' - return all([kwargs.get(x) is None or cron[x] == six.text_type(kwargs[x]) - or (six.text_type(kwargs[x]).lower() == 'random' and cron[x] != '*') - for x in ('minute', 'hour', 'daymonth', 'month', 'dayweek')]) + """ + return all( + [ + kwargs.get(x) is None + or cron[x] == six.text_type(kwargs[x]) + or (six.text_type(kwargs[x]).lower() == "random" and cron[x] != "*") + for x in ("minute", "hour", "daymonth", "month", "dayweek") + ] + ) def raw_cron(user): - ''' + """ Return the contents of the user's crontab CLI Example: @@ -305,49 +317,53 @@ def raw_cron(user): .. code-block:: bash salt '*' cron.raw_cron root - ''' + """ # Some OS' do not support specifying user via the `crontab` command - if __grains__.get('os_family') in ('Solaris', 'AIX'): - cmd = 'crontab -l' + if __grains__.get("os_family") in ("Solaris", "AIX"): + cmd = "crontab -l" # Preserve line endings - lines = salt.utils.data.decode(__salt__['cmd.run_stdout'](cmd, - runas=user, - ignore_retcode=True, - rstrip=False, - python_shell=False)).splitlines(True) + lines = salt.utils.data.decode( + __salt__["cmd.run_stdout"]( + cmd, runas=user, ignore_retcode=True, rstrip=False, python_shell=False + ) + ).splitlines(True) # If Salt is running from same user as requested in cron module we don't need any user switch elif _check_instance_uid_match(user): - cmd = 'crontab -l' + cmd = "crontab -l" # Preserve line endings - lines = salt.utils.data.decode(__salt__['cmd.run_stdout'](cmd, - ignore_retcode=True, - rstrip=False, - python_shell=False)).splitlines(True) + lines = salt.utils.data.decode( + __salt__["cmd.run_stdout"]( + cmd, ignore_retcode=True, rstrip=False, python_shell=False + ) + ).splitlines(True) # If Salt is running from root user it could modify any user's crontab - elif _check_instance_uid_match('root'): - cmd = 'crontab -u {0} -l'.format(user) + elif _check_instance_uid_match("root"): + cmd = "crontab -u {0} -l".format(user) # Preserve line endings - lines = salt.utils.data.decode(__salt__['cmd.run_stdout'](cmd, - ignore_retcode=True, - rstrip=False, - python_shell=False)).splitlines(True) + lines = salt.utils.data.decode( + __salt__["cmd.run_stdout"]( + cmd, ignore_retcode=True, rstrip=False, python_shell=False + ) + ).splitlines(True) # Edge cases here, let's try do a runas else: - cmd = 'crontab -l' + cmd = "crontab -l" # Preserve line endings - lines = salt.utils.data.decode(__salt__['cmd.run_stdout'](cmd, - runas=user, - ignore_retcode=True, - rstrip=False, - python_shell=False)).splitlines(True) + lines = salt.utils.data.decode( + __salt__["cmd.run_stdout"]( + cmd, runas=user, ignore_retcode=True, rstrip=False, python_shell=False + ) + ).splitlines(True) - if len(lines) != 0 and lines[0].startswith('# DO NOT EDIT THIS FILE - edit the master and reinstall.'): + if len(lines) != 0 and lines[0].startswith( + "# DO NOT EDIT THIS FILE - edit the master and reinstall." + ): del lines[0:3] - return ''.join(lines) + return "".join(lines) def list_tab(user): - ''' + """ Return the contents of the specified user's crontab CLI Example: @@ -355,46 +371,43 @@ def list_tab(user): .. code-block:: bash salt '*' cron.list_tab root - ''' + """ data = raw_cron(user) - ret = {'pre': [], - 'crons': [], - 'special': [], - 'env': []} + ret = {"pre": [], "crons": [], "special": [], "env": []} flag = False comment = None identifier = None for line in data.splitlines(): - if line == '# Lines below here are managed by Salt, do not edit': + if line == "# Lines below here are managed by Salt, do not edit": flag = True continue if flag: commented_cron_job = False - if line.startswith('#DISABLED#'): + if line.startswith("#DISABLED#"): # It's a commented cron job line = line[10:] commented_cron_job = True - if line.startswith('@'): + if line.startswith("@"): # Its a "special" line dat = {} comps = line.split() if len(comps) < 2: # Invalid line continue - dat['spec'] = comps[0] - dat['cmd'] = ' '.join(comps[1:]) - dat['identifier'] = identifier - dat['comment'] = comment - dat['commented'] = False + dat["spec"] = comps[0] + dat["cmd"] = " ".join(comps[1:]) + dat["identifier"] = identifier + dat["comment"] = comment + dat["commented"] = False if commented_cron_job: - dat['commented'] = True - ret['special'].append(dat) + dat["commented"] = True + ret["special"].append(dat) identifier = None comment = None commented_cron_job = False - elif line.startswith('#'): + elif line.startswith("#"): # It's a comment! Catch it! - comment_line = line.lstrip('# ') + comment_line = line.lstrip("# ") # load the identifier if any if SALT_CRON_IDENTIFIER in comment_line: @@ -407,48 +420,82 @@ def list_tab(user): if comment is None: comment = comment_line else: - comment += '\n' + comment_line - elif line.find('=') > 0 and (' ' not in line or line.index('=') < line.index(' ')): + comment += "\n" + comment_line + elif line.find("=") > 0 and ( + " " not in line or line.index("=") < line.index(" ") + ): # Appears to be a ENV setup line - comps = line.split('=', 1) + comps = line.split("=", 1) dat = {} - dat['name'] = comps[0] - dat['value'] = comps[1] - ret['env'].append(dat) - elif len(line.split(' ')) > 5: + dat["name"] = comps[0] + dat["value"] = comps[1] + ret["env"].append(dat) + elif len(line.split(" ")) > 5: # Appears to be a standard cron line - comps = line.split(' ') - dat = {'minute': comps[0], - 'hour': comps[1], - 'daymonth': comps[2], - 'month': comps[3], - 'dayweek': comps[4], - 'identifier': identifier, - 'cmd': ' '.join(comps[5:]), - 'comment': comment, - 'commented': False} + comps = line.split(" ") + dat = { + "minute": comps[0], + "hour": comps[1], + "daymonth": comps[2], + "month": comps[3], + "dayweek": comps[4], + "identifier": identifier, + "cmd": " ".join(comps[5:]), + "comment": comment, + "commented": False, + } if commented_cron_job: - dat['commented'] = True - ret['crons'].append(dat) + dat["commented"] = True + ret["crons"].append(dat) identifier = None comment = None commented_cron_job = False else: - ret['pre'].append(line) + ret["pre"].append(line) return ret # For consistency's sake -ls = salt.utils.functools.alias_function(list_tab, 'ls') +ls = salt.utils.functools.alias_function(list_tab, "ls") -def set_special(user, - special, - cmd, - commented=False, - comment=None, - identifier=None): - ''' +def get_entry(user, identifier=None, cmd=None): + """ + Return the specified entry from user's crontab. + identifier will be used if specified, otherwise will lookup cmd + Either identifier or cmd should be specified. + + user: + User's crontab to query + + identifier: + Search for line with identifier + + cmd: + Search for cron line with cmd + + CLI Example: + + .. code-block:: bash + + salt '*' cron.get_entry root identifier=task1 + """ + if identifier and cmd: + log.warning("Both identifier and cmd are specified. Only using identifier.") + cmd = None + + cron_entries = list_tab(user).get("crons", []) + list_tab(user).get("special", []) + + for cron_entry in cron_entries: + if identifier and cron_entry.get("identifier") == identifier: + return cron_entry + elif cmd and cron_entry.get("cmd") == cmd: + return cron_entry + return False + + +def set_special(user, special, cmd, commented=False, comment=None, identifier=None): + """ Set up a special command in the crontab. CLI Example: @@ -456,123 +503,133 @@ def set_special(user, .. code-block:: bash salt '*' cron.set_special root @hourly 'echo foobar' - ''' + """ lst = list_tab(user) - for cron in lst['special']: + for cron in lst["special"]: cid = _cron_id(cron) if _cron_matched(cron, cmd, identifier): test_setted_id = ( - cron['identifier'] is None + cron["identifier"] is None and SALT_CRON_NO_IDENTIFIER - or cron['identifier']) - tests = [(cron['comment'], comment), - (cron['commented'], commented), - (identifier, test_setted_id), - (cron['spec'], special)] + or cron["identifier"] + ) + tests = [ + (cron["comment"], comment), + (cron["commented"], commented), + (identifier, test_setted_id), + (cron["spec"], special), + ] if cid or identifier: - tests.append((cron['cmd'], cmd)) + tests.append((cron["cmd"], cmd)) if any([_needs_change(x, y) for x, y in tests]): rm_special(user, cmd, identifier=cid) # Use old values when setting the new job if there was no # change needed for a given parameter - if not _needs_change(cron['spec'], special): - special = cron['spec'] - if not _needs_change(cron['commented'], commented): - commented = cron['commented'] - if not _needs_change(cron['comment'], comment): - comment = cron['comment'] - if not _needs_change(cron['cmd'], cmd): - cmd = cron['cmd'] - if ( - cid == SALT_CRON_NO_IDENTIFIER - ): + if not _needs_change(cron["spec"], special): + special = cron["spec"] + if not _needs_change(cron["commented"], commented): + commented = cron["commented"] + if not _needs_change(cron["comment"], comment): + comment = cron["comment"] + if not _needs_change(cron["cmd"], cmd): + cmd = cron["cmd"] + if cid == SALT_CRON_NO_IDENTIFIER: if identifier: cid = identifier if ( cid == SALT_CRON_NO_IDENTIFIER - and cron['identifier'] is None + and cron["identifier"] is None ): cid = None - cron['identifier'] = cid - if not cid or ( - cid and not _needs_change(cid, identifier) - ): + cron["identifier"] = cid + if not cid or (cid and not _needs_change(cid, identifier)): identifier = cid - jret = set_special(user, special, cmd, commented=commented, - comment=comment, identifier=identifier) - if jret == 'new': - return 'updated' + jret = set_special( + user, + special, + cmd, + commented=commented, + comment=comment, + identifier=identifier, + ) + if jret == "new": + return "updated" else: return jret - return 'present' - cron = {'spec': special, - 'cmd': cmd, - 'identifier': identifier, - 'comment': comment, - 'commented': commented} - lst['special'].append(cron) + return "present" + cron = { + "spec": special, + "cmd": cmd, + "identifier": identifier, + "comment": comment, + "commented": commented, + } + lst["special"].append(cron) comdat = _write_cron_lines(user, _render_tab(lst)) - if comdat['retcode']: + if comdat["retcode"]: # Failed to commit, return the error - return comdat['stderr'] - return 'new' + return comdat["stderr"] + return "new" def _get_cron_date_time(**kwargs): - ''' + """ Returns a dict of date/time values to be used in a cron entry - ''' + """ # Define ranges (except daymonth, as it depends on the month) range_max = { - 'minute': list(list(range(60))), - 'hour': list(list(range(24))), - 'month': list(list(range(1, 13))), - 'dayweek': list(list(range(7))) + "minute": list(list(range(60))), + "hour": list(list(range(24))), + "month": list(list(range(1, 13))), + "dayweek": list(list(range(7))), } ret = {} - for param in ('minute', 'hour', 'month', 'dayweek'): - value = six.text_type(kwargs.get(param, '1')).lower() - if value == 'random': + for param in ("minute", "hour", "month", "dayweek"): + value = six.text_type(kwargs.get(param, "1")).lower() + if value == "random": ret[param] = six.text_type(random.sample(range_max[param], 1)[0]) - elif len(value.split(':')) == 2: - cron_range = sorted(value.split(':')) + elif len(value.split(":")) == 2: + cron_range = sorted(value.split(":")) start, end = int(cron_range[0]), int(cron_range[1]) ret[param] = six.text_type(random.randint(start, end)) else: ret[param] = value - if ret['month'] in '1 3 5 7 8 10 12'.split(): + if ret["month"] in "1 3 5 7 8 10 12".split(): daymonth_max = 31 - elif ret['month'] in '4 6 9 11'.split(): + elif ret["month"] in "4 6 9 11".split(): daymonth_max = 30 else: # This catches both '2' and '*' daymonth_max = 28 - daymonth = six.text_type(kwargs.get('daymonth', '1')).lower() - if daymonth == 'random': - ret['daymonth'] = \ - six.text_type(random.sample(list(list(range(1, (daymonth_max + 1)))), 1)[0]) + daymonth = six.text_type(kwargs.get("daymonth", "1")).lower() + if daymonth == "random": + ret["daymonth"] = six.text_type( + random.sample(list(list(range(1, (daymonth_max + 1)))), 1)[0] + ) else: - ret['daymonth'] = daymonth + ret["daymonth"] = daymonth return ret -def set_job(user, - minute, - hour, - daymonth, - month, - dayweek, - cmd, - commented=False, - comment=None, - identifier=None): - ''' +def set_job( + user, + minute, + hour, + daymonth, + month, + dayweek, + cmd, + commented=False, + comment=None, + identifier=None, +): + """ Sets a cron job up for a specified user. CLI Example: @@ -580,7 +637,7 @@ def set_job(user, .. code-block:: bash salt '*' cron.set_job root '*' '*' '*' '*' 1 /usr/local/weekly - ''' + """ # Scrub the types minute = six.text_type(minute).lower() hour = six.text_type(hour).lower() @@ -588,85 +645,97 @@ def set_job(user, month = six.text_type(month).lower() dayweek = six.text_type(dayweek).lower() lst = list_tab(user) - for cron in lst['crons']: + for cron in lst["crons"]: cid = _cron_id(cron) if _cron_matched(cron, cmd, identifier): test_setted_id = ( - cron['identifier'] is None + cron["identifier"] is None and SALT_CRON_NO_IDENTIFIER - or cron['identifier']) - tests = [(cron['comment'], comment), - (cron['commented'], commented), - (identifier, test_setted_id), - (cron['minute'], minute), - (cron['hour'], hour), - (cron['daymonth'], daymonth), - (cron['month'], month), - (cron['dayweek'], dayweek)] + or cron["identifier"] + ) + tests = [ + (cron["comment"], comment), + (cron["commented"], commented), + (identifier, test_setted_id), + (cron["minute"], minute), + (cron["hour"], hour), + (cron["daymonth"], daymonth), + (cron["month"], month), + (cron["dayweek"], dayweek), + ] if cid or identifier: - tests.append((cron['cmd'], cmd)) + tests.append((cron["cmd"], cmd)) if any([_needs_change(x, y) for x, y in tests]): rm_job(user, cmd, identifier=cid) # Use old values when setting the new job if there was no # change needed for a given parameter - if not _needs_change(cron['minute'], minute): - minute = cron['minute'] - if not _needs_change(cron['hour'], hour): - hour = cron['hour'] - if not _needs_change(cron['daymonth'], daymonth): - daymonth = cron['daymonth'] - if not _needs_change(cron['month'], month): - month = cron['month'] - if not _needs_change(cron['dayweek'], dayweek): - dayweek = cron['dayweek'] - if not _needs_change(cron['commented'], commented): - commented = cron['commented'] - if not _needs_change(cron['comment'], comment): - comment = cron['comment'] - if not _needs_change(cron['cmd'], cmd): - cmd = cron['cmd'] - if ( - cid == SALT_CRON_NO_IDENTIFIER - ): + if not _needs_change(cron["minute"], minute): + minute = cron["minute"] + if not _needs_change(cron["hour"], hour): + hour = cron["hour"] + if not _needs_change(cron["daymonth"], daymonth): + daymonth = cron["daymonth"] + if not _needs_change(cron["month"], month): + month = cron["month"] + if not _needs_change(cron["dayweek"], dayweek): + dayweek = cron["dayweek"] + if not _needs_change(cron["commented"], commented): + commented = cron["commented"] + if not _needs_change(cron["comment"], comment): + comment = cron["comment"] + if not _needs_change(cron["cmd"], cmd): + cmd = cron["cmd"] + if cid == SALT_CRON_NO_IDENTIFIER: if identifier: cid = identifier if ( cid == SALT_CRON_NO_IDENTIFIER - and cron['identifier'] is None + and cron["identifier"] is None ): cid = None - cron['identifier'] = cid - if not cid or ( - cid and not _needs_change(cid, identifier) - ): + cron["identifier"] = cid + if not cid or (cid and not _needs_change(cid, identifier)): identifier = cid - jret = set_job(user, minute, hour, daymonth, - month, dayweek, cmd, commented=commented, - comment=comment, identifier=identifier) - if jret == 'new': - return 'updated' + jret = set_job( + user, + minute, + hour, + daymonth, + month, + dayweek, + cmd, + commented=commented, + comment=comment, + identifier=identifier, + ) + if jret == "new": + return "updated" else: return jret - return 'present' - cron = {'cmd': cmd, - 'identifier': identifier, - 'comment': comment, - 'commented': commented} - cron.update(_get_cron_date_time(minute=minute, hour=hour, - daymonth=daymonth, month=month, - dayweek=dayweek)) - lst['crons'].append(cron) + return "present" + cron = { + "cmd": cmd, + "identifier": identifier, + "comment": comment, + "commented": commented, + } + cron.update( + _get_cron_date_time( + minute=minute, hour=hour, daymonth=daymonth, month=month, dayweek=dayweek + ) + ) + lst["crons"].append(cron) comdat = _write_cron_lines(user, _render_tab(lst)) - if comdat['retcode']: + if comdat["retcode"]: # Failed to commit, return the error - return comdat['stderr'] - return 'new' + return comdat["stderr"] + return "new" def rm_special(user, cmd, special=None, identifier=None): - ''' + """ Remove a special cron job for a specified user. CLI Example: @@ -674,39 +743,41 @@ def rm_special(user, cmd, special=None, identifier=None): .. code-block:: bash salt '*' cron.rm_special root /usr/bin/foo - ''' + """ lst = list_tab(user) - ret = 'absent' + ret = "absent" rm_ = None - for ind in range(len(lst['special'])): + for ind in range(len(lst["special"])): if rm_ is not None: break - if _cron_matched(lst['special'][ind], cmd, identifier=identifier): + if _cron_matched(lst["special"][ind], cmd, identifier=identifier): if special is None: # No special param was specified rm_ = ind else: - if lst['special'][ind]['spec'] == special: + if lst["special"][ind]["spec"] == special: rm_ = ind if rm_ is not None: - lst['special'].pop(rm_) - ret = 'removed' + lst["special"].pop(rm_) + ret = "removed" comdat = _write_cron_lines(user, _render_tab(lst)) - if comdat['retcode']: + if comdat["retcode"]: # Failed to commit, return the error - return comdat['stderr'] + return comdat["stderr"] return ret -def rm_job(user, - cmd, - minute=None, - hour=None, - daymonth=None, - month=None, - dayweek=None, - identifier=None): - ''' +def rm_job( + user, + cmd, + minute=None, + hour=None, + daymonth=None, + month=None, + dayweek=None, + identifier=None, +): + """ Remove a cron job for a specified user. If any of the day/time params are specified, the job will only be removed if the specified params match. @@ -716,41 +787,44 @@ def rm_job(user, salt '*' cron.rm_job root /usr/local/weekly salt '*' cron.rm_job root /usr/bin/foo dayweek=1 - ''' + """ lst = list_tab(user) - ret = 'absent' + ret = "absent" rm_ = None - for ind in range(len(lst['crons'])): + for ind in range(len(lst["crons"])): if rm_ is not None: break - if _cron_matched(lst['crons'][ind], cmd, identifier=identifier): - if not any([x is not None - for x in (minute, hour, daymonth, month, dayweek)]): + if _cron_matched(lst["crons"][ind], cmd, identifier=identifier): + if not any( + [x is not None for x in (minute, hour, daymonth, month, dayweek)] + ): # No date/time params were specified rm_ = ind else: - if _date_time_match(lst['crons'][ind], - minute=minute, - hour=hour, - daymonth=daymonth, - month=month, - dayweek=dayweek): + if _date_time_match( + lst["crons"][ind], + minute=minute, + hour=hour, + daymonth=daymonth, + month=month, + dayweek=dayweek, + ): rm_ = ind if rm_ is not None: - lst['crons'].pop(rm_) - ret = 'removed' + lst["crons"].pop(rm_) + ret = "removed" comdat = _write_cron_lines(user, _render_tab(lst)) - if comdat['retcode']: + if comdat["retcode"]: # Failed to commit, return the error - return comdat['stderr'] + return comdat["stderr"] return ret -rm = salt.utils.functools.alias_function(rm_job, 'rm') +rm = salt.utils.functools.alias_function(rm_job, "rm") def set_env(user, name, value=None): - ''' + """ Set up an environment variable in the crontab. CLI Example: @@ -758,29 +832,29 @@ def set_env(user, name, value=None): .. code-block:: bash salt '*' cron.set_env root MAILTO user@example.com - ''' + """ lst = list_tab(user) - for env in lst['env']: - if name == env['name']: - if value != env['value']: + for env in lst["env"]: + if name == env["name"]: + if value != env["value"]: rm_env(user, name) jret = set_env(user, name, value) - if jret == 'new': - return 'updated' + if jret == "new": + return "updated" else: return jret - return 'present' - env = {'name': name, 'value': value} - lst['env'].append(env) + return "present" + env = {"name": name, "value": value} + lst["env"].append(env) comdat = _write_cron_lines(user, _render_tab(lst)) - if comdat['retcode']: + if comdat["retcode"]: # Failed to commit, return the error - return comdat['stderr'] - return 'new' + return comdat["stderr"] + return "new" def rm_env(user, name): - ''' + """ Remove cron environment variable for a specified user. CLI Example: @@ -788,18 +862,18 @@ def rm_env(user, name): .. code-block:: bash salt '*' cron.rm_env root MAILTO - ''' + """ lst = list_tab(user) - ret = 'absent' + ret = "absent" rm_ = None - for ind in range(len(lst['env'])): - if name == lst['env'][ind]['name']: + for ind in range(len(lst["env"])): + if name == lst["env"][ind]["name"]: rm_ = ind if rm_ is not None: - lst['env'].pop(rm_) - ret = 'removed' + lst["env"].pop(rm_) + ret = "removed" comdat = _write_cron_lines(user, _render_tab(lst)) - if comdat['retcode']: + if comdat["retcode"]: # Failed to commit, return the error - return comdat['stderr'] + return comdat["stderr"] return ret diff --git a/salt/modules/cryptdev.py b/salt/modules/cryptdev.py index a3a3d542fa6..97e68353688 100644 --- a/salt/modules/cryptdev.py +++ b/salt/modules/cryptdev.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Salt module to manage Unix cryptsetup jobs and the crypttab file .. versionadded:: 2018.3.0 -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals + import logging import os import re @@ -24,83 +25,85 @@ from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'cryptdev' +__virtualname__ = "cryptdev" def __virtual__(): - ''' + """ Only load on POSIX-like systems - ''' + """ if salt.utils.platform.is_windows(): - return (False, 'The cryptdev module cannot be loaded: not a POSIX-like system') + return (False, "The cryptdev module cannot be loaded: not a POSIX-like system") return True class _crypttab_entry(object): - ''' + """ Utility class for manipulating crypttab entries. Primarily we're parsing, formatting, and comparing lines. Parsing emits dicts expected from crypttab() or raises a ValueError. - ''' + """ class ParseError(ValueError): - '''Error raised when a line isn't parsible as a crypttab entry''' + """Error raised when a line isn't parsible as a crypttab entry""" - crypttab_keys = ('name', 'device', 'password', 'options') - crypttab_format = '{name: <12} {device: <44} {password: <22} {options}\n' + crypttab_keys = ("name", "device", "password", "options") + crypttab_format = "{name: <12} {device: <44} {password: <22} {options}\n" @classmethod def dict_from_line(cls, line, keys=crypttab_keys): if len(keys) != 4: - raise ValueError('Invalid key array: {0}'.format(keys)) - if line.startswith('#'): - raise cls.ParseError('Comment!') + raise ValueError("Invalid key array: {0}".format(keys)) + if line.startswith("#"): + raise cls.ParseError("Comment!") comps = line.split() # If there are only three entries, then the options have been omitted. if len(comps) == 3: - comps += [''] + comps += [""] if len(comps) != 4: - raise cls.ParseError('Invalid Entry!') + raise cls.ParseError("Invalid Entry!") return dict(six.moves.zip(keys, comps)) @classmethod def from_line(cls, *args, **kwargs): - return cls(** cls.dict_from_line(*args, **kwargs)) + return cls(**cls.dict_from_line(*args, **kwargs)) @classmethod def dict_to_line(cls, entry): return cls.crypttab_format.format(**entry) def __str__(self): - '''String value, only works for full repr''' + """String value, only works for full repr""" return self.dict_to_line(self.criteria) def __repr__(self): - '''Always works''' + """Always works""" return repr(self.criteria) def pick(self, keys): - '''Returns an instance with just those keys''' + """Returns an instance with just those keys""" subset = dict([(key, self.criteria[key]) for key in keys]) return self.__class__(**subset) def __init__(self, **criteria): - '''Store non-empty, non-null values to use as filter''' - self.criteria = {key: salt.utils.stringutils.to_unicode(value) - for key, value in six.iteritems(criteria) - if value is not None} + """Store non-empty, non-null values to use as filter""" + self.criteria = { + key: salt.utils.stringutils.to_unicode(value) + for key, value in six.iteritems(criteria) + if value is not None + } @staticmethod def norm_path(path): - '''Resolve equivalent paths equivalently''' + """Resolve equivalent paths equivalently""" return os.path.normcase(os.path.normpath(path)) def match(self, line): - '''Compare potentially partial criteria against a complete line''' + """Compare potentially partial criteria against a complete line""" entry = self.dict_from_line(line) for key, value in six.iteritems(self.criteria): if entry[key] != value: @@ -109,28 +112,28 @@ class _crypttab_entry(object): def active(): - ''' + """ List existing device-mapper device details. - ''' + """ ret = {} # TODO: This command should be extended to collect more information, such as UUID. - devices = __salt__['cmd.run_stdout']('dmsetup ls --target crypt') - out_regex = re.compile(r'(?P\w+)\W+\((?P\d+), (?P\d+)\)') + devices = __salt__["cmd.run_stdout"]("dmsetup ls --target crypt") + out_regex = re.compile(r"(?P\w+)\W+\((?P\d+), (?P\d+)\)") log.debug(devices) - for line in devices.split('\n'): + for line in devices.split("\n"): match = out_regex.match(line) if match: dev_info = match.groupdict() - ret[dev_info['devname']] = dev_info + ret[dev_info["devname"]] = dev_info else: - log.warning('dmsetup output does not match expected format') + log.warning("dmsetup output does not match expected format") return ret -def crypttab(config='/etc/crypttab'): - ''' +def crypttab(config="/etc/crypttab"): + """ List the contents of the crypttab CLI Example: @@ -138,31 +141,31 @@ def crypttab(config='/etc/crypttab'): .. code-block:: bash salt '*' cryptdev.crypttab - ''' + """ ret = {} if not os.path.isfile(config): return ret with salt.utils.files.fopen(config) as ifile: for line in ifile: - line = salt.utils.stringutils.to_unicode(line).rstrip('\n') + line = salt.utils.stringutils.to_unicode(line).rstrip("\n") try: entry = _crypttab_entry.dict_from_line(line) - entry['options'] = entry['options'].split(',') + entry["options"] = entry["options"].split(",") # Handle duplicate names by appending `_` - while entry['name'] in ret: - entry['name'] += '_' + while entry["name"] in ret: + entry["name"] += "_" - ret[entry.pop('name')] = entry + ret[entry.pop("name")] = entry except _crypttab_entry.ParseError: pass return ret -def rm_crypttab(name, config='/etc/crypttab'): - ''' +def rm_crypttab(name, config="/etc/crypttab"): + """ Remove the named mapping from the crypttab. If the described entry does not exist, nothing is changed, but the command succeeds by returning ``'absent'``. If a line is removed, it returns ``'change'``. @@ -172,7 +175,7 @@ def rm_crypttab(name, config='/etc/crypttab'): .. code-block:: bash salt '*' cryptdev.rm_crypttab foo - ''' + """ modified = False criteria = _crypttab_entry(name=name) @@ -180,7 +183,7 @@ def rm_crypttab(name, config='/etc/crypttab'): # the list. At the end, re-create the config from just those lines. lines = [] try: - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) try: @@ -193,30 +196,33 @@ def rm_crypttab(name, config='/etc/crypttab'): lines.append(line) except (IOError, OSError) as exc: - msg = 'Could not read from {0}: {1}' + msg = "Could not read from {0}: {1}" raise CommandExecutionError(msg.format(config, exc)) if modified: try: - with salt.utils.files.fopen(config, 'w+') as ofile: - ofile.writelines((salt.utils.stringutils.to_str(line) for line in lines)) + with salt.utils.files.fopen(config, "w+") as ofile: + ofile.writelines( + (salt.utils.stringutils.to_str(line) for line in lines) + ) except (IOError, OSError) as exc: - msg = 'Could not write to {0}: {1}' + msg = "Could not write to {0}: {1}" raise CommandExecutionError(msg.format(config, exc)) # If we reach this point, the changes were successful - return 'change' if modified else 'absent' + return "change" if modified else "absent" def set_crypttab( - name, - device, - password='none', - options='', - config='/etc/crypttab', - test=False, - match_on='name'): - ''' + name, + device, + password="none", + options="", + config="/etc/crypttab", + test=False, + match_on="name", +): + """ Verify that this device is represented in the crypttab, change the device to match the name passed, or add the name if it is not present. @@ -225,25 +231,25 @@ def set_crypttab( .. code-block:: bash salt '*' cryptdev.set_crypttab foo /dev/sdz1 mypassword swap,size=256 - ''' + """ # Fix the options type if it is not a string if options is None: - options = '' + options = "" elif isinstance(options, six.string_types): pass elif isinstance(options, list): - options = ','.join(options) + options = ",".join(options) else: - msg = 'options must be a string or list of strings' + msg = "options must be a string or list of strings" raise CommandExecutionError(msg) # preserve arguments for updating entry_args = { - 'name': name, - 'device': device, - 'password': password if password is not None else 'none', - 'options': options, + "name": name, + "device": device, + "password": password if password is not None else "none", + "options": options, } lines = [] @@ -253,7 +259,7 @@ def set_crypttab( if isinstance(match_on, list): pass elif not isinstance(match_on, six.string_types): - msg = 'match_on must be a string or list of strings' + msg = "match_on must be a string or list of strings" raise CommandExecutionError(msg) else: match_on = [match_on] @@ -275,18 +281,18 @@ def set_crypttab( raise CommandExecutionError('Bad config file "{0}"'.format(config)) try: - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) try: if criteria.match(line): # Note: If ret isn't None here, # we've matched multiple lines - ret = 'present' + ret = "present" if entry.match(line): lines.append(line) else: - ret = 'change' + ret = "change" lines.append(six.text_type(entry)) else: lines.append(line) @@ -295,29 +301,31 @@ def set_crypttab( lines.append(line) except (IOError, OSError) as exc: - msg = 'Couldn\'t read from {0}: {1}' + msg = "Couldn't read from {0}: {1}" raise CommandExecutionError(msg.format(config, exc)) # add line if not present or changed if ret is None: lines.append(six.text_type(entry)) - ret = 'new' + ret = "new" - if ret != 'present': # ret in ['new', 'change']: + if ret != "present": # ret in ['new', 'change']: if not test: try: - with salt.utils.files.fopen(config, 'w+') as ofile: + with salt.utils.files.fopen(config, "w+") as ofile: # The line was changed, commit it! - ofile.writelines((salt.utils.stringutils.to_str(line) for line in lines)) + ofile.writelines( + (salt.utils.stringutils.to_str(line) for line in lines) + ) except (IOError, OSError): - msg = 'File not writable {0}' + msg = "File not writable {0}" raise CommandExecutionError(msg.format(config)) return ret def open(name, device, keyfile): - ''' + """ Open a crypt device using ``cryptsetup``. The ``keyfile`` must not be ``None`` or ``'none'``, because ``cryptsetup`` will otherwise ask for the password interactively. @@ -327,17 +335,20 @@ def open(name, device, keyfile): .. code-block:: bash salt '*' cryptdev.open foo /dev/sdz1 /path/to/keyfile - ''' - if keyfile is None or keyfile == 'none' or keyfile == '-': - raise CommandExecutionError('For immediate crypt device mapping, keyfile must not be none') + """ + if keyfile is None or keyfile == "none" or keyfile == "-": + raise CommandExecutionError( + "For immediate crypt device mapping, keyfile must not be none" + ) - code = __salt__['cmd.retcode']('cryptsetup open --key-file {0} {1} {2}' - .format(keyfile, device, name)) + code = __salt__["cmd.retcode"]( + "cryptsetup open --key-file {0} {1} {2}".format(keyfile, device, name) + ) return code == 0 def close(name): - ''' + """ Close a crypt device using ``cryptsetup``. CLI Example: @@ -345,6 +356,6 @@ def close(name): .. code-block:: bash salt '*' cryptdev.close foo - ''' - code = __salt__['cmd.retcode']('cryptsetup close {0}'.format(name)) + """ + code = __salt__["cmd.retcode"]("cryptsetup close {0}".format(name)) return code == 0 diff --git a/salt/modules/csf.py b/salt/modules/csf.py index 64d86e3a3c1..d4e7cfef9fa 100644 --- a/salt/modules/csf.py +++ b/salt/modules/csf.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Support for Config Server Firewall (CSF) ======================================== :maintainer: Mostafa Hussein :maturity: new :platform: Linux -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import re # Import Salt Libs @@ -18,42 +19,45 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load if csf exists on the system - ''' - if salt.utils.path.which('csf') is None: - return (False, - 'The csf execution module cannot be loaded: csf unavailable.') + """ + if salt.utils.path.which("csf") is None: + return (False, "The csf execution module cannot be loaded: csf unavailable.") else: return True def _temp_exists(method, ip): - ''' + """ Checks if the ip exists as a temporary rule based on the method supplied, (tempallow, tempdeny). - ''' - _type = method.replace('temp', '').upper() - cmd = "csf -t | awk -v code=1 -v type=_type -v ip=ip '$1==type && $2==ip {{code=0}} END {{exit code}}'".format(_type=_type, ip=ip) - exists = __salt__['cmd.run_all'](cmd) - return not bool(exists['retcode']) + """ + _type = method.replace("temp", "").upper() + cmd = "csf -t | awk -v code=1 -v type=_type -v ip=ip '$1==type && $2==ip {{code=0}} END {{exit code}}'".format( + _type=_type, ip=ip + ) + exists = __salt__["cmd.run_all"](cmd) + return not bool(exists["retcode"]) def _exists_with_port(method, rule): - path = '/etc/csf/csf.{0}'.format(method) - return __salt__['file.contains'](path, rule) + path = "/etc/csf/csf.{0}".format(method) + return __salt__["file.contains"](path, rule) -def exists(method, - ip, - port=None, - proto='tcp', - direction='in', - port_origin='d', - ip_origin='d', - ttl=None, - comment=''): - ''' +def exists( + method, + ip, + port=None, + proto="tcp", + direction="in", + port_origin="d", + ip_origin="d", + ttl=None, + comment="", +): + """ Returns true a rule for the ip already exists based on the method supplied. Returns false if not found. @@ -63,154 +67,168 @@ def exists(method, salt '*' csf.exists allow 1.2.3.4 salt '*' csf.exists tempdeny 1.2.3.4 - ''' - if method.startswith('temp'): + """ + if method.startswith("temp"): return _temp_exists(method, ip) if port: - rule = _build_port_rule(ip, port, proto, direction, port_origin, ip_origin, comment) + rule = _build_port_rule( + ip, port, proto, direction, port_origin, ip_origin, comment + ) return _exists_with_port(method, rule) - exists = __salt__['cmd.run_all']("egrep ^'{0} +' /etc/csf/csf.{1}".format(ip, method)) - return not bool(exists['retcode']) + exists = __salt__["cmd.run_all"]( + "egrep ^'{0} +' /etc/csf/csf.{1}".format(ip, method) + ) + return not bool(exists["retcode"]) def __csf_cmd(cmd): - ''' + """ Execute csf command - ''' - csf_cmd = '{0} {1}'.format(salt.utils.path.which('csf'), cmd) - out = __salt__['cmd.run_all'](csf_cmd) + """ + csf_cmd = "{0} {1}".format(salt.utils.path.which("csf"), cmd) + out = __salt__["cmd.run_all"](csf_cmd) - if out['retcode'] != 0: - if not out['stderr']: - ret = out['stdout'] + if out["retcode"] != 0: + if not out["stderr"]: + ret = out["stdout"] else: - ret = out['stderr'] - raise CommandExecutionError( - 'csf failed: {0}'.format(ret) - ) + ret = out["stderr"] + raise CommandExecutionError("csf failed: {0}".format(ret)) else: - ret = out['stdout'] + ret = out["stdout"] return ret def _status_csf(): - ''' + """ Return True if csf is running otherwise return False - ''' - cmd = 'test -e /etc/csf/csf.disable' - out = __salt__['cmd.run_all'](cmd) - return bool(out['retcode']) + """ + cmd = "test -e /etc/csf/csf.disable" + out = __salt__["cmd.run_all"](cmd) + return bool(out["retcode"]) def _get_opt(method): - ''' + """ Returns the cmd option based on a long form argument. - ''' + """ opts = { - 'allow': '-a', - 'deny': '-d', - 'unallow': '-ar', - 'undeny': '-dr', - 'tempallow': '-ta', - 'tempdeny': '-td', - 'temprm': '-tr' + "allow": "-a", + "deny": "-d", + "unallow": "-ar", + "undeny": "-dr", + "tempallow": "-ta", + "tempdeny": "-td", + "temprm": "-tr", } return opts[method] def _build_args(method, ip, comment): - ''' + """ Returns the cmd args for csf basic allow/deny commands. - ''' + """ opt = _get_opt(method) - args = '{0} {1}'.format(opt, ip) + args = "{0} {1}".format(opt, ip) if comment: - args += ' {0}'.format(comment) + args += " {0}".format(comment) return args -def _access_rule(method, - ip=None, - port=None, - proto='tcp', - direction='in', - port_origin='d', - ip_origin='d', - comment=''): - ''' +def _access_rule( + method, + ip=None, + port=None, + proto="tcp", + direction="in", + port_origin="d", + ip_origin="d", + comment="", +): + """ Handles the cmd execution for allow and deny commands. - ''' + """ if _status_csf(): if ip is None: - return {'error': 'You must supply an ip address or CIDR.'} + return {"error": "You must supply an ip address or CIDR."} if port is None: args = _build_args(method, ip, comment) return __csf_cmd(args) else: - if method not in ['allow', 'deny']: - return {'error': 'Only allow and deny rules are allowed when specifying a port.'} - return _access_rule_with_port(method=method, - ip=ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin, - comment=comment) + if method not in ["allow", "deny"]: + return { + "error": "Only allow and deny rules are allowed when specifying a port." + } + return _access_rule_with_port( + method=method, + ip=ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + comment=comment, + ) def _build_port_rule(ip, port, proto, direction, port_origin, ip_origin, comment): kwargs = { - 'ip': ip, - 'port': port, - 'proto': proto, - 'direction': direction, - 'port_origin': port_origin, - 'ip_origin': ip_origin, + "ip": ip, + "port": port, + "proto": proto, + "direction": direction, + "port_origin": port_origin, + "ip_origin": ip_origin, } - rule = '{proto}|{direction}|{port_origin}={port}|{ip_origin}={ip}'.format(**kwargs) + rule = "{proto}|{direction}|{port_origin}={port}|{ip_origin}={ip}".format(**kwargs) if comment: - rule += ' #{0}'.format(comment) + rule += " #{0}".format(comment) return rule -def _remove_access_rule_with_port(method, - ip, - port, - proto='tcp', - direction='in', - port_origin='d', - ip_origin='d', - ttl=None): +def _remove_access_rule_with_port( + method, + ip, + port, + proto="tcp", + direction="in", + port_origin="d", + ip_origin="d", + ttl=None, +): - rule = _build_port_rule(ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin, - comment='') + rule = _build_port_rule( + ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + comment="", + ) - rule = rule.replace('|', '[|]') - rule = rule.replace('.', '[.]') - result = __salt__['file.replace']('/etc/csf/csf.{0}'.format(method), - pattern='^{0}(( +)?\#.*)?$\n'.format(rule), # pylint: disable=W1401 - repl='') + rule = rule.replace("|", "[|]") + rule = rule.replace(".", "[.]") + result = __salt__["file.replace"]( + "/etc/csf/csf.{0}".format(method), + pattern="^{0}(( +)?\#.*)?$\n".format(rule), # pylint: disable=W1401 + repl="", + ) return result def _csf_to_list(option): - ''' + """ Extract comma-separated values from a csf.conf option and return a list. - ''' + """ result = [] line = get_option(option) if line: - csv = line.split('=')[1].replace(' ', '').replace('"', '') - result = csv.split(',') + csv = line.split("=")[1].replace(" ", "").replace('"', "") + result = csv.split(",") return result @@ -221,9 +239,9 @@ def split_option(option): def get_option(option): pattern = '^{0}(\ +)?\=(\ +)?".*"$'.format(option) # pylint: disable=W1401 - grep = __salt__['file.grep']('/etc/csf/csf.conf', pattern, '-E') - if 'stdout' in grep and grep['stdout']: - line = grep['stdout'] + grep = __salt__["file.grep"]("/etc/csf/csf.conf", pattern, "-E") + if "stdout" in grep and grep["stdout"]: + line = grep["stdout"] return line return None @@ -231,19 +249,21 @@ def get_option(option): def set_option(option, value): current_option = get_option(option) if not current_option: - return {'error': 'No such option exists in csf.conf'} - result = __salt__['file.replace']('/etc/csf/csf.conf', - pattern='^{0}(\ +)?\=(\ +)?".*"'.format(option), # pylint: disable=W1401 - repl='{0} = "{1}"'.format(option, value)) + return {"error": "No such option exists in csf.conf"} + result = __salt__["file.replace"]( + "/etc/csf/csf.conf", + pattern='^{0}(\ +)?\=(\ +)?".*"'.format(option), # pylint: disable=W1401 + repl='{0} = "{1}"'.format(option, value), + ) return result def get_skipped_nics(ipv6=False): if ipv6: - option = 'ETH6_DEVICE_SKIP' + option = "ETH6_DEVICE_SKIP" else: - option = 'ETH_DEVICE_SKIP' + option = "ETH_DEVICE_SKIP" skipped_nics = _csf_to_list(option) return skipped_nics @@ -257,142 +277,154 @@ def skip_nic(nic, ipv6=False): def skip_nics(nics, ipv6=False): if ipv6: - ipv6 = '6' + ipv6 = "6" else: - ipv6 = '' - nics_csv = ','.join(six.moves.map(six.text_type, nics)) - result = __salt__['file.replace']('/etc/csf/csf.conf', - pattern='^ETH{0}_DEVICE_SKIP(\ +)?\=(\ +)?".*"'.format(ipv6), # pylint: disable=W1401 - repl='ETH{0}_DEVICE_SKIP = "{1}"'.format(ipv6, nics_csv)) + ipv6 = "" + nics_csv = ",".join(six.moves.map(six.text_type, nics)) + result = __salt__["file.replace"]( + "/etc/csf/csf.conf", + # pylint: disable=anomalous-backslash-in-string + pattern='^ETH{0}_DEVICE_SKIP(\ +)?\=(\ +)?".*"'.format(ipv6), + # pylint: enable=anomalous-backslash-in-string + repl='ETH{0}_DEVICE_SKIP = "{1}"'.format(ipv6, nics_csv), + ) return result -def _access_rule_with_port(method, - ip, - port, - proto='tcp', - direction='in', - port_origin='d', - ip_origin='d', - ttl=None, - comment=''): +def _access_rule_with_port( + method, + ip, + port, + proto="tcp", + direction="in", + port_origin="d", + ip_origin="d", + ttl=None, + comment="", +): results = {} - if direction == 'both': - directions = ['in', 'out'] + if direction == "both": + directions = ["in", "out"] else: directions = [direction] for direction in directions: - _exists = exists(method, - ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin, - ttl=ttl, - comment=comment) + _exists = exists( + method, + ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + ttl=ttl, + comment=comment, + ) if not _exists: - rule = _build_port_rule(ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin, - comment=comment) - path = '/etc/csf/csf.{0}'.format(method) - results[direction] = __salt__['file.append'](path, rule) + rule = _build_port_rule( + ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + comment=comment, + ) + path = "/etc/csf/csf.{0}".format(method) + results[direction] = __salt__["file.append"](path, rule) return results -def _tmp_access_rule(method, - ip=None, - ttl=None, - port=None, - direction='in', - port_origin='d', - ip_origin='d', - comment=''): - ''' +def _tmp_access_rule( + method, + ip=None, + ttl=None, + port=None, + direction="in", + port_origin="d", + ip_origin="d", + comment="", +): + """ Handles the cmd execution for tempdeny and tempallow commands. - ''' + """ if _status_csf(): if ip is None: - return {'error': 'You must supply an ip address or CIDR.'} + return {"error": "You must supply an ip address or CIDR."} if ttl is None: - return {'error': 'You must supply a ttl.'} + return {"error": "You must supply a ttl."} args = _build_tmp_access_args(method, ip, ttl, port, direction, comment) return __csf_cmd(args) def _build_tmp_access_args(method, ip, ttl, port, direction, comment): - ''' + """ Builds the cmd args for temporary access/deny opts. - ''' + """ opt = _get_opt(method) - args = '{0} {1} {2}'.format(opt, ip, ttl) + args = "{0} {1} {2}".format(opt, ip, ttl) if port: - args += ' -p {0}'.format(port) + args += " -p {0}".format(port) if direction: - args += ' -d {0}'.format(direction) + args += " -d {0}".format(direction) if comment: - args += ' #{0}'.format(comment) + args += " #{0}".format(comment) return args def running(): - ''' + """ Check csf status CLI Example: .. code-block:: bash salt '*' csf.running - ''' + """ return _status_csf() def disable(): - ''' + """ Disable csf permanently CLI Example: .. code-block:: bash salt '*' csf.disable - ''' + """ if _status_csf(): - return __csf_cmd('-x') + return __csf_cmd("-x") def enable(): - ''' + """ Activate csf if not running CLI Example: .. code-block:: bash salt '*' csf.enable - ''' + """ if not _status_csf(): - return __csf_cmd('-e') + return __csf_cmd("-e") def reload(): - ''' + """ Restart csf CLI Example: .. code-block:: bash salt '*' csf.reload - ''' - return __csf_cmd('-r') + """ + return __csf_cmd("-r") -def tempallow(ip=None, ttl=None, port=None, direction=None, comment=''): - ''' +def tempallow(ip=None, ttl=None, port=None, direction=None, comment=""): + """ Add an rule to the temporary ip allow list. See :func:`_access_rule`. 1- Add an IP: @@ -401,12 +433,12 @@ def tempallow(ip=None, ttl=None, port=None, direction=None, comment=''): .. code-block:: bash salt '*' csf.tempallow 127.0.0.1 3600 port=22 direction='in' comment='# Temp dev ssh access' - ''' - return _tmp_access_rule('tempallow', ip, ttl, port, direction, comment) + """ + return _tmp_access_rule("tempallow", ip, ttl, port, direction, comment) -def tempdeny(ip=None, ttl=None, port=None, direction=None, comment=''): - ''' +def tempdeny(ip=None, ttl=None, port=None, direction=None, comment=""): + """ Add a rule to the temporary ip deny list. See :func:`_access_rule`. 1- Add an IP: @@ -415,19 +447,21 @@ def tempdeny(ip=None, ttl=None, port=None, direction=None, comment=''): .. code-block:: bash salt '*' csf.tempdeny 127.0.0.1 300 port=22 direction='in' comment='# Brute force attempt' - ''' - return _tmp_access_rule('tempdeny', ip, ttl, port, direction, comment) + """ + return _tmp_access_rule("tempdeny", ip, ttl, port, direction, comment) -def allow(ip, - port=None, - proto='tcp', - direction='in', - port_origin='d', - ip_origin='s', - ttl=None, - comment=''): - ''' +def allow( + ip, + port=None, + proto="tcp", + direction="in", + port_origin="d", + ip_origin="s", + ttl=None, + comment="", +): + """ Add an rule to csf allowed hosts See :func:`_access_rule`. 1- Add an IP: @@ -437,26 +471,30 @@ def allow(ip, salt '*' csf.allow 127.0.0.1 salt '*' csf.allow 127.0.0.1 comment="Allow localhost" - ''' - return _access_rule('allow', - ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin, - comment=comment) + """ + return _access_rule( + "allow", + ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + comment=comment, + ) -def deny(ip, - port=None, - proto='tcp', - direction='in', - port_origin='d', - ip_origin='d', - ttl=None, - comment=''): - ''' +def deny( + ip, + port=None, + proto="tcp", + direction="in", + port_origin="d", + ip_origin="d", + ttl=None, + comment="", +): + """ Add an rule to csf denied hosts See :func:`_access_rule`. 1- Deny an IP: @@ -466,18 +504,20 @@ def deny(ip, salt '*' csf.deny 127.0.0.1 salt '*' csf.deny 127.0.0.1 comment="Too localhosty" - ''' - return _access_rule('deny', ip, port, proto, direction, port_origin, ip_origin, comment) + """ + return _access_rule( + "deny", ip, port, proto, direction, port_origin, ip_origin, comment + ) def remove_temp_rule(ip): - opt = _get_opt('temprm') - args = '{0} {1}'.format(opt, ip) + opt = _get_opt("temprm") + args = "{0} {1}".format(opt, ip) return __csf_cmd(args) def unallow(ip): - ''' + """ Remove a rule from the csf denied hosts See :func:`_access_rule`. 1- Deny an IP: @@ -486,12 +526,12 @@ def unallow(ip): .. code-block:: bash salt '*' csf.unallow 127.0.0.1 - ''' - return _access_rule('unallow', ip) + """ + return _access_rule("unallow", ip) def undeny(ip): - ''' + """ Remove a rule from the csf denied hosts See :func:`_access_rule`. 1- Deny an IP: @@ -500,41 +540,45 @@ def undeny(ip): .. code-block:: bash salt '*' csf.undeny 127.0.0.1 - ''' - return _access_rule('undeny', ip) + """ + return _access_rule("undeny", ip) -def remove_rule(method, - ip, - port=None, - proto='tcp', - direction='in', - port_origin='d', - ip_origin='s', - ttl=None, - comment=''): +def remove_rule( + method, + ip, + port=None, + proto="tcp", + direction="in", + port_origin="d", + ip_origin="s", + ttl=None, + comment="", +): - if method.startswith('temp') or ttl: + if method.startswith("temp") or ttl: return remove_temp_rule(ip) if not port: - if method == 'allow': + if method == "allow": return unallow(ip) - elif method == 'deny': + elif method == "deny": return undeny(ip) if port: - return _remove_access_rule_with_port(method=method, - ip=ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin) + return _remove_access_rule_with_port( + method=method, + ip=ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + ) -def allow_ports(ports, proto='tcp', direction='in'): - ''' +def allow_ports(ports, proto="tcp", direction="in"): + """ Fully replace the incoming or outgoing ports line in the csf.conf file - e.g. TCP_IN, TCP_OUT, UDP_IN, UDP_OUT, etc. @@ -544,7 +588,7 @@ def allow_ports(ports, proto='tcp', direction='in'): .. code-block:: bash salt '*' csf.allow_ports ports="[22,80,443,4505,4506]" proto='tcp' direction='in' - ''' + """ results = [] ports = set(ports) @@ -552,20 +596,24 @@ def allow_ports(ports, proto='tcp', direction='in'): proto = proto.upper() direction = direction.upper() _validate_direction_and_proto(direction, proto) - ports_csv = ','.join(six.moves.map(six.text_type, ports)) + ports_csv = ",".join(six.moves.map(six.text_type, ports)) directions = build_directions(direction) for direction in directions: - result = __salt__['file.replace']('/etc/csf/csf.conf', - pattern='^{0}_{1}(\ +)?\=(\ +)?".*"$'.format(proto, direction), # pylint: disable=W1401 - repl='{0}_{1} = "{2}"'.format(proto, direction, ports_csv)) + result = __salt__["file.replace"]( + "/etc/csf/csf.conf", + # pylint: disable=anomalous-backslash-in-string + pattern='^{0}_{1}(\ +)?\=(\ +)?".*"$'.format(proto, direction), + # pylint: enable=anomalous-backslash-in-string + repl='{0}_{1} = "{2}"'.format(proto, direction, ports_csv), + ) results.append(result) return results -def get_ports(proto='tcp', direction='in'): - ''' +def get_ports(proto="tcp", direction="in"): + """ Lists ports from csf.conf based on direction and protocol. e.g. - TCP_IN, TCP_OUT, UDP_IN, UDP_OUT, etc.. @@ -574,7 +622,7 @@ def get_ports(proto='tcp', direction='in'): .. code-block:: bash salt '*' csf.allow_port 22 proto='tcp' direction='in' - ''' + """ proto = proto.upper() direction = direction.upper() @@ -582,35 +630,33 @@ def get_ports(proto='tcp', direction='in'): _validate_direction_and_proto(direction, proto) directions = build_directions(direction) for direction in directions: - option = '{0}_{1}'.format(proto, direction) + option = "{0}_{1}".format(proto, direction) results[direction] = _csf_to_list(option) return results def _validate_direction_and_proto(direction, proto): - if direction.upper() not in ['IN', 'OUT', 'BOTH']: + if direction.upper() not in ["IN", "OUT", "BOTH"]: + raise SaltInvocationError("You must supply a direction of in, out, or both") + if proto.upper() not in ["TCP", "UDP", "TCP6", "UDP6"]: raise SaltInvocationError( - 'You must supply a direction of in, out, or both' - ) - if proto.upper() not in ['TCP', 'UDP', 'TCP6', 'UDP6']: - raise SaltInvocationError( - 'You must supply tcp, udp, tcp6, or udp6 for the proto keyword' + "You must supply tcp, udp, tcp6, or udp6 for the proto keyword" ) return def build_directions(direction): direction = direction.upper() - if direction == 'BOTH': - directions = ['IN', 'OUT'] + if direction == "BOTH": + directions = ["IN", "OUT"] else: directions = [direction] return directions -def allow_port(port, proto='tcp', direction='both'): - ''' +def allow_port(port, proto="tcp", direction="both"): + """ Like allow_ports, but it will append to the existing entry instead of replacing it. Takes a single port instead of a list of ports. @@ -620,7 +666,7 @@ def allow_port(port, proto='tcp', direction='both'): .. code-block:: bash salt '*' csf.allow_port 22 proto='tcp' direction='in' - ''' + """ ports = get_ports(proto=proto, direction=direction) direction = direction.upper() @@ -635,29 +681,29 @@ def allow_port(port, proto='tcp', direction='both'): def get_testing_status(): - testing = _csf_to_list('TESTING')[0] + testing = _csf_to_list("TESTING")[0] return testing def _toggle_testing(val): - if val == 'on': - val = '1' - elif val == 'off': - val = '0' + if val == "on": + val = "1" + elif val == "off": + val = "0" else: - raise SaltInvocationError( - "Only valid arg is 'on' or 'off' here." - ) + raise SaltInvocationError("Only valid arg is 'on' or 'off' here.") - result = __salt__['file.replace']('/etc/csf/csf.conf', - pattern='^TESTING(\ +)?\=(\ +)?".*"', # pylint: disable=W1401 - repl='TESTING = "{0}"'.format(val)) + result = __salt__["file.replace"]( + "/etc/csf/csf.conf", + pattern='^TESTING(\ +)?\=(\ +)?".*"', # pylint: disable=W1401 + repl='TESTING = "{0}"'.format(val), + ) return result def enable_testing_mode(): - return _toggle_testing('on') + return _toggle_testing("on") def disable_testing_mode(): - return _toggle_testing('off') + return _toggle_testing("off") diff --git a/salt/modules/cyg.py b/salt/modules/cyg.py index 9efe36382ba..2e351bb1814 100644 --- a/salt/modules/cyg.py +++ b/salt/modules/cyg.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Manage cygwin packages. Module file to accompany the cyg state. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import bz2 + # Import python libs import logging -import re import os -import bz2 - -# Import 3rd-party libs -from salt.ext.six.moves.urllib.request import urlopen as _urlopen # pylint: disable=no-name-in-module,import-error +import re # Import Salt libs import salt.utils.files @@ -24,6 +22,8 @@ from salt.exceptions import SaltInvocationError # Import 3rd-party libs from salt.ext import six +# Import 3rd-party libs +from salt.ext.six.moves.urllib.request import urlopen as _urlopen LOG = logging.getLogger(__name__) @@ -31,82 +31,79 @@ DEFAULT_MIRROR = "ftp://mirrors.kernel.org/sourceware/cygwin/" DEFAULT_MIRROR_KEY = "" # Define the module's virtual name -__virtualname__ = 'cyg' +__virtualname__ = "cyg" def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ - return (False, 'Module cyg: module only works on Windows systems.') + return (False, "Module cyg: module only works on Windows systems.") -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} -def _get_cyg_dir(cyg_arch='x86_64'): - ''' +def _get_cyg_dir(cyg_arch="x86_64"): + """ Return the cygwin install directory based on the architecture. - ''' - if cyg_arch == 'x86_64': - return 'cygwin64' - elif cyg_arch == 'x86': - return 'cygwin' + """ + if cyg_arch == "x86_64": + return "cygwin64" + elif cyg_arch == "x86": + return "cygwin" - raise SaltInvocationError( - 'Invalid architecture {arch}'.format(arch=cyg_arch)) + raise SaltInvocationError("Invalid architecture {arch}".format(arch=cyg_arch)) -def _check_cygwin_installed(cyg_arch='x86_64'): - ''' +def _check_cygwin_installed(cyg_arch="x86_64"): + """ Return True or False if cygwin is installed. Use the cygcheck executable to check install. It is installed as part of the base package, and we use it to check packages - ''' - path_to_cygcheck = os.sep.join(['C:', - _get_cyg_dir(cyg_arch), - 'bin', 'cygcheck.exe']) - LOG.debug('Path to cygcheck.exe: {0}'.format(path_to_cygcheck)) + """ + path_to_cygcheck = os.sep.join( + ["C:", _get_cyg_dir(cyg_arch), "bin", "cygcheck.exe"] + ) + LOG.debug("Path to cygcheck.exe: {0}".format(path_to_cygcheck)) if not os.path.exists(path_to_cygcheck): - LOG.debug('Could not find cygcheck.exe') + LOG.debug("Could not find cygcheck.exe") return False return True -def _get_all_packages(mirror=DEFAULT_MIRROR, - cyg_arch='x86_64'): - ''' +def _get_all_packages(mirror=DEFAULT_MIRROR, cyg_arch="x86_64"): + """ Return the list of packages based on the mirror provided. - ''' - if 'cyg.all_packages' not in __context__: - __context__['cyg.all_packages'] = {} - if mirror not in __context__['cyg.all_packages']: - __context__['cyg.all_packages'][mirror] = [] - if not __context__['cyg.all_packages'][mirror]: - pkg_source = '/'.join([mirror, cyg_arch, 'setup.bz2']) + """ + if "cyg.all_packages" not in __context__: + __context__["cyg.all_packages"] = {} + if mirror not in __context__["cyg.all_packages"]: + __context__["cyg.all_packages"][mirror] = [] + if not __context__["cyg.all_packages"][mirror]: + pkg_source = "/".join([mirror, cyg_arch, "setup.bz2"]) file_data = _urlopen(pkg_source).read() - file_lines = bz2.decompress(file_data).decode('utf_8', - errors='replace' - ).splitlines() + file_lines = ( + bz2.decompress(file_data).decode("utf_8", errors="replace").splitlines() + ) - packages = [re.search('^@ ([^ ]+)', line).group(1) for - line in file_lines if re.match('^@ [^ ]+', line)] + packages = [ + re.search("^@ ([^ ]+)", line).group(1) + for line in file_lines + if re.match("^@ [^ ]+", line) + ] - __context__['cyg.all_packages'][mirror] = packages + __context__["cyg.all_packages"][mirror] = packages - return __context__['cyg.all_packages'][mirror] + return __context__["cyg.all_packages"][mirror] -def check_valid_package(package, - cyg_arch='x86_64', - mirrors=None): - ''' +def check_valid_package(package, cyg_arch="x86_64", mirrors=None): + """ Check if the package is valid on the given mirrors. Args: @@ -121,11 +118,11 @@ def check_valid_package(package, .. code-block:: bash salt '*' cyg.check_valid_package - ''' + """ if mirrors is None: mirrors = [{DEFAULT_MIRROR: DEFAULT_MIRROR_KEY}] - LOG.debug('Checking Valid Mirrors: {0}'.format(mirrors)) + LOG.debug("Checking Valid Mirrors: {0}".format(mirrors)) for mirror in mirrors: for mirror_url, key in mirror.items(): @@ -134,19 +131,17 @@ def check_valid_package(package, return False -def _run_silent_cygwin(cyg_arch='x86_64', - args=None, - mirrors=None): - ''' +def _run_silent_cygwin(cyg_arch="x86_64", args=None, mirrors=None): + """ Retrieve the correct setup.exe. Run it with the correct arguments to get the bare minimum cygwin installation up and running. - ''' - cyg_cache_dir = os.sep.join(['c:', 'cygcache']) - cyg_setup = 'setup-{0}.exe'.format(cyg_arch) + """ + cyg_cache_dir = os.sep.join(["c:", "cygcache"]) + cyg_setup = "setup-{0}.exe".format(cyg_arch) cyg_setup_path = os.sep.join([cyg_cache_dir, cyg_setup]) - cyg_setup_source = 'http://cygwin.com/{0}'.format(cyg_setup) + cyg_setup_source = "http://cygwin.com/{0}".format(cyg_setup) # cyg_setup_source_hash = 'http://cygwin.com/{0}.sig'.format(cyg_setup) # until a hash gets published that we can verify the newest setup against @@ -162,55 +157,51 @@ def _run_silent_cygwin(cyg_arch='x86_64', setup_command = cyg_setup_path options = [] - options.append('--local-package-dir {0}'.format(cyg_cache_dir)) + options.append("--local-package-dir {0}".format(cyg_cache_dir)) if mirrors is None: mirrors = [{DEFAULT_MIRROR: DEFAULT_MIRROR_KEY}] for mirror in mirrors: for mirror_url, key in mirror.items(): - options.append('--site {0}'.format(mirror_url)) + options.append("--site {0}".format(mirror_url)) if key: - options.append('--pubkey {0}'.format(key)) - options.append('--no-desktop') - options.append('--quiet-mode') - options.append('--disable-buggy-antivirus') + options.append("--pubkey {0}".format(key)) + options.append("--no-desktop") + options.append("--quiet-mode") + options.append("--disable-buggy-antivirus") if args is not None: for arg in args: options.append(arg) - cmdline_args = ' '.join(options) - setup_command = ' '.join([cyg_setup_path, cmdline_args]) + cmdline_args = " ".join(options) + setup_command = " ".join([cyg_setup_path, cmdline_args]) - ret = __salt__['cmd.run_all']( - setup_command + ret = __salt__["cmd.run_all"](setup_command) + + if ret["retcode"] == 0: + return ret["stdout"] + else: + return False + + +def _cygcheck(args, cyg_arch="x86_64"): + """ + Run the cygcheck executable. + """ + cmd = " ".join( + [os.sep.join(["c:", _get_cyg_dir(cyg_arch), "bin", "cygcheck"]), "-c", args] ) - if ret['retcode'] == 0: - return ret['stdout'] + ret = __salt__["cmd.run_all"](cmd) + + if ret["retcode"] == 0: + return ret["stdout"] else: return False -def _cygcheck(args, cyg_arch='x86_64'): - ''' - Run the cygcheck executable. - ''' - cmd = ' '.join([ - os.sep.join(['c:', _get_cyg_dir(cyg_arch), 'bin', 'cygcheck']), - '-c', args]) - - ret = __salt__['cmd.run_all'](cmd) - - if ret['retcode'] == 0: - return ret['stdout'] - else: - return False - - -def install(packages=None, - cyg_arch='x86_64', - mirrors=None): - ''' +def install(packages=None, cyg_arch="x86_64", mirrors=None): + """ Install one or several packages. packages : None @@ -226,11 +217,11 @@ def install(packages=None, salt '*' cyg.install dos2unix salt '*' cyg.install dos2unix mirrors="[{'http://mirror': 'http://url/to/public/key}]' - ''' + """ args = [] # If we want to install packages if packages is not None: - args.append('--packages {pkgs}'.format(pkgs=packages)) + args.append("--packages {pkgs}".format(pkgs=packages)) # but we don't have cygwin installed yet if not _check_cygwin_installed(cyg_arch): # install just the base system @@ -239,10 +230,8 @@ def install(packages=None, return _run_silent_cygwin(cyg_arch=cyg_arch, args=args, mirrors=mirrors) -def uninstall(packages, - cyg_arch='x86_64', - mirrors=None): - ''' +def uninstall(packages, cyg_arch="x86_64", mirrors=None): + """ Uninstall one or several packages. packages @@ -258,20 +247,20 @@ def uninstall(packages, salt '*' cyg.uninstall dos2unix salt '*' cyg.uninstall dos2unix mirrors="[{'http://mirror': 'http://url/to/public/key}]" - ''' + """ args = [] if packages is not None: - args.append('--remove-packages {pkgs}'.format(pkgs=packages)) - LOG.debug('args: {0}'.format(args)) + args.append("--remove-packages {pkgs}".format(pkgs=packages)) + LOG.debug("args: {0}".format(args)) if not _check_cygwin_installed(cyg_arch): - LOG.debug('We\'re convinced cygwin isn\'t installed') + LOG.debug("We're convinced cygwin isn't installed") return True return _run_silent_cygwin(cyg_arch=cyg_arch, args=args, mirrors=mirrors) -def update(cyg_arch='x86_64', mirrors=None): - ''' +def update(cyg_arch="x86_64", mirrors=None): + """ Update all packages. cyg_arch : x86_64 @@ -284,21 +273,25 @@ def update(cyg_arch='x86_64', mirrors=None): salt '*' cyg.update salt '*' cyg.update dos2unix mirrors="[{'http://mirror': 'http://url/to/public/key}]" - ''' + """ args = [] - args.append('--upgrade-also') + args.append("--upgrade-also") # Can't update something that isn't installed if not _check_cygwin_installed(cyg_arch): - LOG.debug('Cygwin ({0}) not installed,\ - could not update'.format(cyg_arch)) + LOG.debug( + "Cygwin ({0}) not installed,\ + could not update".format( + cyg_arch + ) + ) return False return _run_silent_cygwin(cyg_arch=cyg_arch, args=args, mirrors=mirrors) -def list_(package='', cyg_arch='x86_64'): - ''' +def list_(package="", cyg_arch="x86_64"): + """ List locally installed packages. package : '' @@ -313,15 +306,15 @@ def list_(package='', cyg_arch='x86_64'): .. code-block:: bash salt '*' cyg.list - ''' + """ pkgs = {} - args = ' '.join(['-c', '-d', package]) + args = " ".join(["-c", "-d", package]) stdout = _cygcheck(args, cyg_arch=cyg_arch) lines = [] if isinstance(stdout, six.string_types): lines = salt.utils.stringutils.to_unicode(stdout).splitlines() for line in lines: - match = re.match(r'^([^ ]+) *([^ ]+)', line) + match = re.match(r"^([^ ]+) *([^ ]+)", line) if match: pkg = match.group(1) version = match.group(2) diff --git a/salt/modules/daemontools.py b/salt/modules/daemontools.py index 719816c48b8..e53f29f71b0 100644 --- a/salt/modules/daemontools.py +++ b/salt/modules/daemontools.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" daemontools service module. This module will create daemontools type service watcher. @@ -11,7 +11,7 @@ so it can be used to maintain services using the ``provider`` argument: myservice: service.running: - provider: daemontools -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -25,18 +25,16 @@ import salt.utils.path from salt.exceptions import CommandExecutionError # Function alias to not shadow built-ins. -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} log = logging.getLogger(__name__) -__virtualname__ = 'daemontools' +__virtualname__ = "daemontools" VALID_SERVICE_DIRS = [ - '/service', - '/var/service', - '/etc/service', + "/service", + "/var/service", + "/etc/service", ] SERVICE_DIR = None for service_dir in VALID_SERVICE_DIRS: @@ -47,24 +45,24 @@ for service_dir in VALID_SERVICE_DIRS: def __virtual__(): # Ensure that daemontools is installed properly. - BINS = frozenset(('svc', 'supervise', 'svok')) + BINS = frozenset(("svc", "supervise", "svok")) if all(salt.utils.path.which(b) for b in BINS) and SERVICE_DIR: return __virtualname__ - return False + return (False, "Missing dependency: {0}".format(BINS)) def _service_path(name): - ''' + """ build service path - ''' + """ if not SERVICE_DIR: raise CommandExecutionError("Could not find service directory.") - return '{0}/{1}'.format(SERVICE_DIR, name) + return "{0}/{1}".format(SERVICE_DIR, name) -#-- states.service compatible args +# -- states.service compatible args def start(name): - ''' + """ Starts service via daemontools CLI Example: @@ -72,15 +70,15 @@ def start(name): .. code-block:: bash salt '*' daemontools.start - ''' - __salt__['file.remove']('{0}/down'.format(_service_path(name))) - cmd = 'svc -u {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + __salt__["file.remove"]("{0}/down".format(_service_path(name))) + cmd = "svc -u {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd, python_shell=False) -#-- states.service compatible args +# -- states.service compatible args def stop(name): - ''' + """ Stops service via daemontools CLI Example: @@ -88,14 +86,14 @@ def stop(name): .. code-block:: bash salt '*' daemontools.stop - ''' - __salt__['file.touch']('{0}/down'.format(_service_path(name))) - cmd = 'svc -d {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + __salt__["file.touch"]("{0}/down".format(_service_path(name))) + cmd = "svc -d {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def term(name): - ''' + """ Send a TERM to service via daemontools CLI Example: @@ -103,14 +101,14 @@ def term(name): .. code-block:: bash salt '*' daemontools.term - ''' - cmd = 'svc -t {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "svc -t {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd, python_shell=False) -#-- states.service compatible +# -- states.service compatible def reload_(name): - ''' + """ Wrapper for term() CLI Example: @@ -118,13 +116,13 @@ def reload_(name): .. code-block:: bash salt '*' daemontools.reload - ''' + """ term(name) -#-- states.service compatible +# -- states.service compatible def restart(name): - ''' + """ Restart service via daemontools. This will stop/start service CLI Example: @@ -132,16 +130,16 @@ def restart(name): .. code-block:: bash salt '*' daemontools.restart - ''' - ret = 'restart False' + """ + ret = "restart False" if stop(name) and start(name): - ret = 'restart True' + ret = "restart True" return ret -#-- states.service compatible +# -- states.service compatible def full_restart(name): - ''' + """ Calls daemontools.restart() function CLI Example: @@ -149,13 +147,13 @@ def full_restart(name): .. code-block:: bash salt '*' daemontools.full_restart - ''' + """ restart(name) -#-- states.service compatible +# -- states.service compatible def status(name, sig=None): - ''' + """ Return the status for a service via daemontools, return pid if running CLI Example: @@ -163,18 +161,18 @@ def status(name, sig=None): .. code-block:: bash salt '*' daemontools.status - ''' - cmd = 'svstat {0}'.format(_service_path(name)) - out = __salt__['cmd.run_stdout'](cmd, python_shell=False) + """ + cmd = "svstat {0}".format(_service_path(name)) + out = __salt__["cmd.run_stdout"](cmd, python_shell=False) try: - pid = re.search(r'\(pid (\d+)\)', out).group(1) + pid = re.search(r"\(pid (\d+)\)", out).group(1) except AttributeError: - pid = '' + pid = "" return pid def available(name): - ''' + """ Returns ``True`` if the specified service is available, otherwise returns ``False``. @@ -183,12 +181,12 @@ def available(name): .. code-block:: bash salt '*' daemontools.available foo - ''' + """ return name in get_all() def missing(name): - ''' + """ The inverse of daemontools.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -198,12 +196,12 @@ def missing(name): .. code-block:: bash salt '*' daemontools.missing foo - ''' + """ return name not in get_all() def get_all(): - ''' + """ Return a list of all available services CLI Example: @@ -211,15 +209,15 @@ def get_all(): .. code-block:: bash salt '*' daemontools.get_all - ''' + """ if not SERVICE_DIR: raise CommandExecutionError("Could not find service directory.") - #- List all daemontools services in + # - List all daemontools services in return sorted(os.listdir(SERVICE_DIR)) def enabled(name, **kwargs): - ''' + """ Return True if the named service is enabled, false otherwise A service is considered enabled if in your service directory: - an executable ./run file exist @@ -235,23 +233,23 @@ def enabled(name, **kwargs): .. code-block:: bash salt '*' daemontools.enabled - ''' + """ if not available(name): - log.error('Service {0} not found'.format(name)) + log.error("Service {0} not found".format(name)) return False - run_file = os.path.join(SERVICE_DIR, name, 'run') - down_file = os.path.join(SERVICE_DIR, name, 'down') + run_file = os.path.join(SERVICE_DIR, name, "run") + down_file = os.path.join(SERVICE_DIR, name, "down") return ( - os.path.isfile(run_file) and - os.access(run_file, os.X_OK) and not - os.path.isfile(down_file) + os.path.isfile(run_file) + and os.access(run_file, os.X_OK) + and not os.path.isfile(down_file) ) def disabled(name): - ''' + """ Return True if the named service is enabled, false otherwise .. versionadded:: 2015.5.6 @@ -261,5 +259,5 @@ def disabled(name): .. code-block:: bash salt '*' daemontools.disabled - ''' + """ return not enabled(name) diff --git a/salt/modules/data.py b/salt/modules/data.py index 1d550e53fd6..574c432427a 100644 --- a/salt/modules/data.py +++ b/salt/modules/data.py @@ -1,18 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" Manage a local persistent data structure that can hold any arbitrary data specific to the minion -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import python libs -import os import ast import logging +# Import python libs +import os + +import salt.payload + # Import salt libs import salt.utils.files -import salt.payload # Import 3rd-party lib from salt.ext import six @@ -21,7 +23,7 @@ log = logging.getLogger(__name__) def clear(): - ''' + """ Clear out all of the data in the minion datastore, this function is destructive! @@ -30,16 +32,16 @@ def clear(): .. code-block:: bash salt '*' data.clear - ''' + """ try: - os.remove(os.path.join(__opts__['cachedir'], 'datastore')) + os.remove(os.path.join(__opts__["cachedir"], "datastore")) except (IOError, OSError): pass return True def load(): - ''' + """ Return all of the data in the minion datastore CLI Example: @@ -47,19 +49,19 @@ def load(): .. code-block:: bash salt '*' data.load - ''' + """ serial = salt.payload.Serial(__opts__) try: - datastore_path = os.path.join(__opts__['cachedir'], 'datastore') - with salt.utils.files.fopen(datastore_path, 'rb') as rfh: + datastore_path = os.path.join(__opts__["cachedir"], "datastore") + with salt.utils.files.fopen(datastore_path, "rb") as rfh: return serial.loads(rfh.read()) except (IOError, OSError, NameError): return {} def dump(new_data): - ''' + """ Replace the entire datastore with a passed data structure CLI Example: @@ -67,7 +69,7 @@ def dump(new_data): .. code-block:: bash salt '*' data.dump '{'eggs': 'spam'}' - ''' + """ if not isinstance(new_data, dict): if isinstance(ast.literal_eval(new_data), dict): new_data = ast.literal_eval(new_data) @@ -75,8 +77,8 @@ def dump(new_data): return False try: - datastore_path = os.path.join(__opts__['cachedir'], 'datastore') - with salt.utils.files.fopen(datastore_path, 'w+b') as fn_: + datastore_path = os.path.join(__opts__["cachedir"], "datastore") + with salt.utils.files.fopen(datastore_path, "w+b") as fn_: serial = salt.payload.Serial(__opts__) serial.dump(new_data, fn_) @@ -87,7 +89,7 @@ def dump(new_data): def update(key, value): - ''' + """ Update a key with a value in the minion datastore CLI Example: @@ -95,7 +97,7 @@ def update(key, value): .. code-block:: bash salt '*' data.update - ''' + """ store = load() store[key] = value dump(store) @@ -103,7 +105,7 @@ def update(key, value): def cas(key, value, old_value): - ''' + """ Check and set a value in the minion datastore CLI Example: @@ -111,7 +113,7 @@ def cas(key, value, old_value): .. code-block:: bash salt '*' data.cas - ''' + """ store = load() if key not in store: return False @@ -125,7 +127,7 @@ def cas(key, value, old_value): def pop(key, default=None): - ''' + """ Pop (return & delete) a value from the minion datastore .. versionadded:: 2015.5.2 @@ -135,7 +137,7 @@ def pop(key, default=None): .. code-block:: bash salt '*' data.pop "there was no val" - ''' + """ store = load() val = store.pop(key, default) dump(store) @@ -143,7 +145,7 @@ def pop(key, default=None): def get(key, default=None): - ''' + """ Get a (list of) value(s) from the minion datastore .. versionadded:: 2015.8.0 @@ -154,7 +156,7 @@ def get(key, default=None): salt '*' data.get key salt '*' data.get '["key1", "key2"]' - ''' + """ store = load() if isinstance(key, six.string_types): @@ -166,7 +168,7 @@ def get(key, default=None): def keys(): - ''' + """ Get all keys from the minion datastore .. versionadded:: 2015.8.0 @@ -176,13 +178,13 @@ def keys(): .. code-block:: bash salt '*' data.keys - ''' + """ store = load() return store.keys() def values(): - ''' + """ Get values from the minion datastore .. versionadded:: 2015.8.0 @@ -192,13 +194,13 @@ def values(): .. code-block:: bash salt '*' data.values - ''' + """ store = load() return store.values() def items(): - ''' + """ Get items from the minion datastore .. versionadded:: 2015.8.0 @@ -208,13 +210,13 @@ def items(): .. code-block:: bash salt '*' data.items - ''' + """ store = load() return store.items() def has_key(key): - ''' + """ Check if key is in the minion datastore .. versionadded:: 2015.8.0 @@ -224,6 +226,6 @@ def has_key(key): .. code-block:: bash salt '*' data.has_key - ''' + """ store = load() return key in store diff --git a/salt/modules/datadog_api.py b/salt/modules/datadog_api.py index 7ac90ddec57..6dee8e99f5c 100644 --- a/salt/modules/datadog_api.py +++ b/salt/modules/datadog_api.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An execution module that interacts with the Datadog API The following parameters are required for all functions. @@ -12,14 +12,15 @@ app_key Full argument reference is available on the Datadog API reference page https://docs.datadoghq.com/api/ -''' +""" # Import salt libs from __future__ import absolute_import, print_function, unicode_literals -from salt.exceptions import SaltInvocationError # Import third party libs import requests +from salt.exceptions import SaltInvocationError + HAS_DATADOG = True try: import datadog @@ -27,43 +28,42 @@ except ImportError: HAS_DATADOG = False # Define the module's virtual name -__virtualname__ = 'datadog' +__virtualname__ = "datadog" def __virtual__(): if HAS_DATADOG: - return 'datadog' + return "datadog" else: - message = 'Unable to import the python datadog module. Is it installed?' + message = "Unable to import the python datadog module. Is it installed?" return False, message def _initialize_connection(api_key, app_key): - ''' + """ Initialize Datadog connection - ''' + """ if api_key is None: - raise SaltInvocationError('api_key must be specified') + raise SaltInvocationError("api_key must be specified") if app_key is None: - raise SaltInvocationError('app_key must be specified') - options = { - 'api_key': api_key, - 'app_key': app_key - } + raise SaltInvocationError("app_key must be specified") + options = {"api_key": api_key, "app_key": app_key} datadog.initialize(**options) -def schedule_downtime(scope, - api_key=None, - app_key=None, - monitor_id=None, - start=None, - end=None, - message=None, - recurrence=None, - timezone=None, - test=False): - ''' +def schedule_downtime( + scope, + api_key=None, + app_key=None, + monitor_id=None, + start=None, + end=None, + message=None, + recurrence=None, + timezone=None, + test=False, +): + """ Schedule downtime for a scope of monitors. CLI Example: @@ -83,48 +83,47 @@ def schedule_downtime(scope, :param message: A message to send in a notification for this downtime :param recurrence: Repeat this downtime periodically :param timezone: Specify the timezone - ''' - ret = {'result': False, - 'response': None, - 'comment': ''} + """ + ret = {"result": False, "response": None, "comment": ""} if api_key is None: - raise SaltInvocationError('api_key must be specified') + raise SaltInvocationError("api_key must be specified") if app_key is None: - raise SaltInvocationError('app_key must be specified') + raise SaltInvocationError("app_key must be specified") if test is True: - ret['result'] = True - ret['comment'] = 'A schedule downtime API call would have been made.' + ret["result"] = True + ret["comment"] = "A schedule downtime API call would have been made." return ret _initialize_connection(api_key, app_key) # Schedule downtime try: - response = datadog.api.Downtime.create(scope=scope, - monitor_id=monitor_id, - start=start, - end=end, - message=message, - recurrence=recurrence, - timezone=timezone) + response = datadog.api.Downtime.create( + scope=scope, + monitor_id=monitor_id, + start=start, + end=end, + message=message, + recurrence=recurrence, + timezone=timezone, + ) except ValueError: - comment = ('Unexpected exception in Datadog Schedule Downtime API ' - 'call. Are your keys correct?') - ret['comment'] = comment + comment = ( + "Unexpected exception in Datadog Schedule Downtime API " + "call. Are your keys correct?" + ) + ret["comment"] = comment return ret - ret['response'] = response - if 'active' in response.keys(): - ret['result'] = True - ret['comment'] = 'Successfully scheduled downtime' + ret["response"] = response + if "active" in response.keys(): + ret["result"] = True + ret["comment"] = "Successfully scheduled downtime" return ret -def cancel_downtime(api_key=None, - app_key=None, - scope=None, - id=None): - ''' +def cancel_downtime(api_key=None, app_key=None, scope=None, id=None): + """ Cancel a downtime by id or by scope. CLI Example: @@ -139,59 +138,54 @@ def cancel_downtime(api_key=None, :param id: The downtime ID :param scope: The downtime scope - ''' + """ if api_key is None: - raise SaltInvocationError('api_key must be specified') + raise SaltInvocationError("api_key must be specified") if app_key is None: - raise SaltInvocationError('app_key must be specified') + raise SaltInvocationError("app_key must be specified") _initialize_connection(api_key, app_key) - ret = {'result': False, - 'response': None, - 'comment': ''} + ret = {"result": False, "response": None, "comment": ""} if id: response = datadog.api.Downtime.delete(id) - ret['response'] = response + ret["response"] = response if not response: # Then call has succeeded - ret['result'] = True - ret['comment'] = 'Successfully cancelled downtime' + ret["result"] = True + ret["comment"] = "Successfully cancelled downtime" return ret elif scope: - params = { - 'api_key': api_key, - 'application_key': app_key, - 'scope': scope - } + params = {"api_key": api_key, "application_key": app_key, "scope": scope} response = requests.post( - 'https://app.datadoghq.com/api/v1/downtime/cancel/by_scope', - params=params - ) + "https://app.datadoghq.com/api/v1/downtime/cancel/by_scope", params=params + ) if response.status_code == 200: - ret['result'] = True - ret['response'] = response.json() - ret['comment'] = 'Successfully cancelled downtime' + ret["result"] = True + ret["response"] = response.json() + ret["comment"] = "Successfully cancelled downtime" else: - ret['response'] = response.text - ret['comment'] = 'Status Code: {}'.format(response.status_code) + ret["response"] = response.text + ret["comment"] = "Status Code: {}".format(response.status_code) return ret else: - raise SaltInvocationError('One of id or scope must be specified') + raise SaltInvocationError("One of id or scope must be specified") return ret -def post_event(api_key=None, - app_key=None, - title=None, - text=None, - date_happened=None, - priority=None, - host=None, - tags=None, - alert_type=None, - aggregation_key=None, - source_type_name=None): - ''' +def post_event( + api_key=None, + app_key=None, + title=None, + text=None, + date_happened=None, + priority=None, + host=None, + tags=None, + alert_type=None, + aggregation_key=None, + source_type_name=None, +): + """ Post an event to the Datadog stream. CLI Example @@ -220,45 +214,47 @@ def post_event(api_key=None, :param aggregation_key: An arbitrary string to use for aggregation, max length of 100 characters. :param source_type_name: The type of event being posted. - ''' + """ _initialize_connection(api_key, app_key) if title is None: - raise SaltInvocationError('title must be specified') + raise SaltInvocationError("title must be specified") if text is None: - raise SaltInvocationError('text must be specified') - if alert_type not in [None, 'error', 'warning', 'info', 'success']: + raise SaltInvocationError("text must be specified") + if alert_type not in [None, "error", "warning", "info", "success"]: # Datadog only supports these alert types but the API doesn't return an # error for an incorrect alert_type, so we can do it here for now. # https://github.com/DataDog/datadogpy/issues/215 - message = ('alert_type must be one of "error", "warning", "info", or ' - '"success"') + message = ( + 'alert_type must be one of "error", "warning", "info", or ' '"success"' + ) raise SaltInvocationError(message) - ret = {'result': False, - 'response': None, - 'comment': ''} + ret = {"result": False, "response": None, "comment": ""} try: - response = datadog.api.Event.create(title=title, - text=text, - date_happened=date_happened, - priority=priority, - host=host, - tags=tags, - alert_type=alert_type, - aggregation_key=aggregation_key, - source_type_name=source_type_name - ) + response = datadog.api.Event.create( + title=title, + text=text, + date_happened=date_happened, + priority=priority, + host=host, + tags=tags, + alert_type=alert_type, + aggregation_key=aggregation_key, + source_type_name=source_type_name, + ) except ValueError: - comment = ('Unexpected exception in Datadog Post Event API ' - 'call. Are your keys correct?') - ret['comment'] = comment + comment = ( + "Unexpected exception in Datadog Post Event API " + "call. Are your keys correct?" + ) + ret["comment"] = comment return ret - ret['response'] = response - if 'status' in response.keys(): - ret['result'] = True - ret['comment'] = 'Successfully sent event' + ret["response"] = response + if "status" in response.keys(): + ret["result"] = True + ret["comment"] = "Successfully sent event" else: - ret['comment'] = 'Error in posting event.' + ret["comment"] = "Error in posting event." return ret diff --git a/salt/modules/ddns.py b/salt/modules/ddns.py index f4361d0e4c7..0b348498c8b 100644 --- a/salt/modules/ddns.py +++ b/salt/modules/ddns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for RFC 2136 dynamic DNS updates. :depends: - dnspython Python module @@ -22,11 +22,16 @@ Support for RFC 2136 dynamic DNS updates. .. code-block:: json {"keyname.": "keycontent"} -''' +""" from __future__ import absolute_import, print_function, unicode_literals + # Import python libs import logging +import salt.utils.files +import salt.utils.json +from salt.ext import six + log = logging.getLogger(__name__) try: @@ -38,31 +43,30 @@ try: except ImportError as e: dns_support = False -import salt.utils.files -import salt.utils.json -from salt.ext import six - def __virtual__(): - ''' + """ Confirm dnspython is available. - ''' + """ if dns_support: - return 'ddns' - return (False, 'The ddns execution module cannot be loaded: dnspython not installed.') + return "ddns" + return ( + False, + "The ddns execution module cannot be loaded: dnspython not installed.", + ) def _config(name, key=None, **kwargs): - ''' + """ Return a value for 'name' from command line args then config file options. Specify 'key' if the config file option is not the same as 'name'. - ''' + """ if key is None: key = name if name in kwargs: value = kwargs[name] else: - value = __salt__['config.option']('ddns.{0}'.format(key)) + value = __salt__["config.option"]("ddns.{0}".format(key)) if not value: value = None return value @@ -76,9 +80,18 @@ def _get_keyring(keyfile): return keyring -def add_host(zone, name, ttl, ip, nameserver='127.0.0.1', replace=True, - timeout=5, port=53, **kwargs): - ''' +def add_host( + zone, + name, + ttl, + ip, + nameserver="127.0.0.1", + replace=True, + timeout=5, + port=53, + **kwargs +): + """ Add, replace, or update the A and PTR (reverse) records for a host. CLI Example: @@ -86,32 +99,31 @@ def add_host(zone, name, ttl, ip, nameserver='127.0.0.1', replace=True, .. code-block:: bash salt ns1 ddns.add_host example.com host1 60 10.1.1.1 - ''' - res = update(zone, name, ttl, 'A', ip, nameserver, timeout, replace, port, - **kwargs) + """ + res = update(zone, name, ttl, "A", ip, nameserver, timeout, replace, port, **kwargs) if res is False: return False - fqdn = '{0}.{1}.'.format(name, zone) - parts = ip.split('.')[::-1] + fqdn = "{0}.{1}.".format(name, zone) + parts = ip.split(".")[::-1] popped = [] # Iterate over possible reverse zones while len(parts) > 1: p = parts.pop(0) popped.append(p) - zone = '{0}.{1}'.format('.'.join(parts), 'in-addr.arpa.') - name = '.'.join(popped) - ptr = update(zone, name, ttl, 'PTR', fqdn, nameserver, timeout, - replace, port, **kwargs) + zone = "{0}.{1}".format(".".join(parts), "in-addr.arpa.") + name = ".".join(popped) + ptr = update( + zone, name, ttl, "PTR", fqdn, nameserver, timeout, replace, port, **kwargs + ) if ptr: return True return res -def delete_host(zone, name, nameserver='127.0.0.1', timeout=5, port=53, - **kwargs): - ''' +def delete_host(zone, name, nameserver="127.0.0.1", timeout=5, port=53, **kwargs): + """ Delete the forward and reverse records for a host. Returns true if any records are deleted. @@ -121,39 +133,58 @@ def delete_host(zone, name, nameserver='127.0.0.1', timeout=5, port=53, .. code-block:: bash salt ns1 ddns.delete_host example.com host1 - ''' - fqdn = '{0}.{1}'.format(name, zone) - request = dns.message.make_query(fqdn, 'A') + """ + fqdn = "{0}.{1}".format(name, zone) + request = dns.message.make_query(fqdn, "A") answer = dns.query.udp(request, nameserver, timeout, port) try: ips = [i.address for i in answer.answer[0].items] except IndexError: ips = [] - res = delete(zone, name, nameserver=nameserver, timeout=timeout, port=port, - **kwargs) + res = delete( + zone, name, nameserver=nameserver, timeout=timeout, port=port, **kwargs + ) - fqdn = fqdn + '.' + fqdn = fqdn + "." for ip in ips: - parts = ip.split('.')[::-1] + parts = ip.split(".")[::-1] popped = [] # Iterate over possible reverse zones while len(parts) > 1: p = parts.pop(0) popped.append(p) - zone = '{0}.{1}'.format('.'.join(parts), 'in-addr.arpa.') - name = '.'.join(popped) - ptr = delete(zone, name, 'PTR', fqdn, nameserver=nameserver, - timeout=timeout, port=port, **kwargs) + zone = "{0}.{1}".format(".".join(parts), "in-addr.arpa.") + name = ".".join(popped) + ptr = delete( + zone, + name, + "PTR", + fqdn, + nameserver=nameserver, + timeout=timeout, + port=port, + **kwargs + ) if ptr: res = True return res -def update(zone, name, ttl, rdtype, data, nameserver='127.0.0.1', timeout=5, - replace=False, port=53, **kwargs): - ''' +def update( + zone, + name, + ttl, + rdtype, + data, + nameserver="127.0.0.1", + timeout=5, + replace=False, + port=53, + **kwargs +): + """ Add, replace, or update a DNS record. nameserver must be an IP address and the minion running this module must have update privileges on that server. @@ -164,13 +195,13 @@ def update(zone, name, ttl, rdtype, data, nameserver='127.0.0.1', timeout=5, .. code-block:: bash salt ns1 ddns.update example.com host1 60 A 10.0.0.1 - ''' + """ name = six.text_type(name) - if name[-1:] == '.': + if name[-1:] == ".": fqdn = name else: - fqdn = '{0}.{1}'.format(name, zone) + fqdn = "{0}.{1}".format(name, zone) request = dns.message.make_query(fqdn, rdtype) answer = dns.query.udp(request, nameserver, timeout, port) @@ -178,10 +209,9 @@ def update(zone, name, ttl, rdtype, data, nameserver='127.0.0.1', timeout=5, rdtype = dns.rdatatype.from_text(rdtype) rdata = dns.rdata.from_text(dns.rdataclass.IN, rdtype, data) - keyring = _get_keyring(_config('keyfile', **kwargs)) - keyname = _config('keyname', **kwargs) - keyalgorithm = _config('keyalgorithm', - **kwargs) or 'HMAC-MD5.SIG-ALG.REG.INT' + keyring = _get_keyring(_config("keyfile", **kwargs)) + keyname = _config("keyname", **kwargs) + keyalgorithm = _config("keyalgorithm", **kwargs) or "HMAC-MD5.SIG-ALG.REG.INT" is_exist = False for rrset in answer.answer: @@ -191,8 +221,9 @@ def update(zone, name, ttl, rdtype, data, nameserver='127.0.0.1', timeout=5, is_exist = True break - dns_update = dns.update.Update(zone, keyring=keyring, keyname=keyname, - keyalgorithm=keyalgorithm) + dns_update = dns.update.Update( + zone, keyring=keyring, keyname=keyname, keyalgorithm=keyalgorithm + ) if replace: dns_update.replace(name, ttl, rdata) elif not is_exist: @@ -205,9 +236,17 @@ def update(zone, name, ttl, rdtype, data, nameserver='127.0.0.1', timeout=5, return True -def delete(zone, name, rdtype=None, data=None, nameserver='127.0.0.1', - timeout=5, port=53, **kwargs): - ''' +def delete( + zone, + name, + rdtype=None, + data=None, + nameserver="127.0.0.1", + timeout=5, + port=53, + **kwargs +): + """ Delete a DNS record. CLI Example: @@ -215,26 +254,26 @@ def delete(zone, name, rdtype=None, data=None, nameserver='127.0.0.1', .. code-block:: bash salt ns1 ddns.delete example.com host1 A - ''' + """ name = six.text_type(name) - if name[-1:] == '.': + if name[-1:] == ".": fqdn = name else: - fqdn = '{0}.{1}'.format(name, zone) + fqdn = "{0}.{1}".format(name, zone) - request = dns.message.make_query(fqdn, (rdtype or 'ANY')) + request = dns.message.make_query(fqdn, (rdtype or "ANY")) answer = dns.query.udp(request, nameserver, timeout, port) if not answer.answer: return None - keyring = _get_keyring(_config('keyfile', **kwargs)) - keyname = _config('keyname', **kwargs) - keyalgorithm = _config('keyalgorithm', - **kwargs) or 'HMAC-MD5.SIG-ALG.REG.INT' + keyring = _get_keyring(_config("keyfile", **kwargs)) + keyname = _config("keyname", **kwargs) + keyalgorithm = _config("keyalgorithm", **kwargs) or "HMAC-MD5.SIG-ALG.REG.INT" - dns_update = dns.update.Update(zone, keyring=keyring, keyname=keyname, - keyalgorithm=keyalgorithm) + dns_update = dns.update.Update( + zone, keyring=keyring, keyname=keyname, keyalgorithm=keyalgorithm + ) if rdtype: rdtype = dns.rdatatype.from_text(rdtype) diff --git a/salt/modules/deb_apache.py b/salt/modules/deb_apache.py index 69de2113ba5..89c3d448bd0 100644 --- a/salt/modules/deb_apache.py +++ b/salt/modules/deb_apache.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Support for Apache Please note: The functions in here are Debian-specific. Placing them in this separate file will allow them to load only on Debian-based systems, while still loading under the ``apache`` namespace. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging # Import salt libs import salt.utils.decorators.path @@ -18,36 +19,36 @@ import salt.utils.path log = logging.getLogger(__name__) -__virtualname__ = 'apache' +__virtualname__ = "apache" -SITE_ENABLED_DIR = '/etc/apache2/sites-enabled' +SITE_ENABLED_DIR = "/etc/apache2/sites-enabled" def __virtual__(): - ''' + """ Only load the module if apache is installed - ''' + """ cmd = _detect_os() - if salt.utils.path.which(cmd) and __grains__['os_family'] == 'Debian': + if salt.utils.path.which(cmd) and __grains__["os_family"] == "Debian": return __virtualname__ - return (False, 'apache execution module not loaded: apache not installed.') + return (False, "apache execution module not loaded: apache not installed.") def _detect_os(): - ''' + """ Apache commands and paths differ depending on packaging - ''' + """ # TODO: Add pillar support for the apachectl location - if __grains__['os_family'] == 'RedHat': - return 'apachectl' - elif __grains__['os_family'] == 'Debian': - return 'apache2ctl' + if __grains__["os_family"] == "RedHat": + return "apachectl" + elif __grains__["os_family"] == "Debian": + return "apache2ctl" else: - return 'apachectl' + return "apachectl" def check_site_enabled(site): - ''' + """ Checks to see if the specific site symlink is in /etc/apache2/sites-enabled. This will only be functional on Debian-based operating systems (Ubuntu, @@ -59,22 +60,23 @@ def check_site_enabled(site): salt '*' apache.check_site_enabled example.com salt '*' apache.check_site_enabled example.com.conf - ''' - if site.endswith('.conf'): + """ + if site.endswith(".conf"): site_file = site else: - site_file = '{0}.conf'.format(site) - if os.path.islink('{0}/{1}'.format(SITE_ENABLED_DIR, site_file)): + site_file = "{0}.conf".format(site) + if os.path.islink("{0}/{1}".format(SITE_ENABLED_DIR, site_file)): return True - elif site == 'default' and \ - os.path.islink('{0}/000-{1}'.format(SITE_ENABLED_DIR, site_file)): + elif site == "default" and os.path.islink( + "{0}/000-{1}".format(SITE_ENABLED_DIR, site_file) + ): return True else: return False def a2ensite(site): - ''' + """ Runs a2ensite for the given site. This will only be functional on Debian-based operating systems (Ubuntu, @@ -85,30 +87,30 @@ def a2ensite(site): .. code-block:: bash salt '*' apache.a2ensite example.com - ''' + """ ret = {} - command = ['a2ensite', site] + command = ["a2ensite", site] try: - status = __salt__['cmd.retcode'](command, python_shell=False) + status = __salt__["cmd.retcode"](command, python_shell=False) except Exception as e: # pylint: disable=broad-except return e - ret['Name'] = 'Apache2 Enable Site' - ret['Site'] = site + ret["Name"] = "Apache2 Enable Site" + ret["Site"] = site if status == 1: - ret['Status'] = 'Site {0} Not found'.format(site) + ret["Status"] = "Site {0} Not found".format(site) elif status == 0: - ret['Status'] = 'Site {0} enabled'.format(site) + ret["Status"] = "Site {0} enabled".format(site) else: - ret['Status'] = status + ret["Status"] = status return ret def a2dissite(site): - ''' + """ Runs a2dissite for the given site. This will only be functional on Debian-based operating systems (Ubuntu, @@ -119,30 +121,30 @@ def a2dissite(site): .. code-block:: bash salt '*' apache.a2dissite example.com - ''' + """ ret = {} - command = ['a2dissite', site] + command = ["a2dissite", site] try: - status = __salt__['cmd.retcode'](command, python_shell=False) + status = __salt__["cmd.retcode"](command, python_shell=False) except Exception as e: # pylint: disable=broad-except return e - ret['Name'] = 'Apache2 Disable Site' - ret['Site'] = site + ret["Name"] = "Apache2 Disable Site" + ret["Site"] = site if status == 256: - ret['Status'] = 'Site {0} Not found'.format(site) + ret["Status"] = "Site {0} Not found".format(site) elif status == 0: - ret['Status'] = 'Site {0} disabled'.format(site) + ret["Status"] = "Site {0} disabled".format(site) else: - ret['Status'] = status + ret["Status"] = status return ret def check_mod_enabled(mod): - ''' + """ Checks to see if the specific mod symlink is in /etc/apache2/mods-enabled. This will only be functional on Debian-based operating systems (Ubuntu, @@ -155,16 +157,16 @@ def check_mod_enabled(mod): salt '*' apache.check_mod_enabled status salt '*' apache.check_mod_enabled status.load salt '*' apache.check_mod_enabled status.conf - ''' - if mod.endswith('.load') or mod.endswith('.conf'): + """ + if mod.endswith(".load") or mod.endswith(".conf"): mod_file = mod else: - mod_file = '{0}.load'.format(mod) - return os.path.islink('/etc/apache2/mods-enabled/{0}'.format(mod_file)) + mod_file = "{0}.load".format(mod) + return os.path.islink("/etc/apache2/mods-enabled/{0}".format(mod_file)) def a2enmod(mod): - ''' + """ Runs a2enmod for the given mod. This will only be functional on Debian-based operating systems (Ubuntu, @@ -175,30 +177,30 @@ def a2enmod(mod): .. code-block:: bash salt '*' apache.a2enmod vhost_alias - ''' + """ ret = {} - command = ['a2enmod', mod] + command = ["a2enmod", mod] try: - status = __salt__['cmd.retcode'](command, python_shell=False) + status = __salt__["cmd.retcode"](command, python_shell=False) except Exception as e: # pylint: disable=broad-except return e - ret['Name'] = 'Apache2 Enable Mod' - ret['Mod'] = mod + ret["Name"] = "Apache2 Enable Mod" + ret["Mod"] = mod if status == 1: - ret['Status'] = 'Mod {0} Not found'.format(mod) + ret["Status"] = "Mod {0} Not found".format(mod) elif status == 0: - ret['Status'] = 'Mod {0} enabled'.format(mod) + ret["Status"] = "Mod {0} enabled".format(mod) else: - ret['Status'] = status + ret["Status"] = status return ret def a2dismod(mod): - ''' + """ Runs a2dismod for the given mod. This will only be functional on Debian-based operating systems (Ubuntu, @@ -209,30 +211,30 @@ def a2dismod(mod): .. code-block:: bash salt '*' apache.a2dismod vhost_alias - ''' + """ ret = {} - command = ['a2dismod', mod] + command = ["a2dismod", mod] try: - status = __salt__['cmd.retcode'](command, python_shell=False) + status = __salt__["cmd.retcode"](command, python_shell=False) except Exception as e: # pylint: disable=broad-except return e - ret['Name'] = 'Apache2 Disable Mod' - ret['Mod'] = mod + ret["Name"] = "Apache2 Disable Mod" + ret["Mod"] = mod if status == 256: - ret['Status'] = 'Mod {0} Not found'.format(mod) + ret["Status"] = "Mod {0} Not found".format(mod) elif status == 0: - ret['Status'] = 'Mod {0} disabled'.format(mod) + ret["Status"] = "Mod {0} disabled".format(mod) else: - ret['Status'] = status + ret["Status"] = status return ret def check_conf_enabled(conf): - ''' + """ .. versionadded:: 2016.3.0 Checks to see if the specific conf symlink is in /etc/apache2/conf-enabled. @@ -246,17 +248,17 @@ def check_conf_enabled(conf): salt '*' apache.check_conf_enabled security salt '*' apache.check_conf_enabled security.conf - ''' - if conf.endswith('.conf'): + """ + if conf.endswith(".conf"): conf_file = conf else: - conf_file = '{0}.conf'.format(conf) - return os.path.islink('/etc/apache2/conf-enabled/{0}'.format(conf_file)) + conf_file = "{0}.conf".format(conf) + return os.path.islink("/etc/apache2/conf-enabled/{0}".format(conf_file)) -@salt.utils.decorators.path.which('a2enconf') +@salt.utils.decorators.path.which("a2enconf") def a2enconf(conf): - ''' + """ .. versionadded:: 2016.3.0 Runs a2enconf for the given conf. @@ -269,31 +271,31 @@ def a2enconf(conf): .. code-block:: bash salt '*' apache.a2enconf security - ''' + """ ret = {} - command = ['a2enconf', conf] + command = ["a2enconf", conf] try: - status = __salt__['cmd.retcode'](command, python_shell=False) + status = __salt__["cmd.retcode"](command, python_shell=False) except Exception as e: # pylint: disable=broad-except return e - ret['Name'] = 'Apache2 Enable Conf' - ret['Conf'] = conf + ret["Name"] = "Apache2 Enable Conf" + ret["Conf"] = conf if status == 1: - ret['Status'] = 'Conf {0} Not found'.format(conf) + ret["Status"] = "Conf {0} Not found".format(conf) elif status == 0: - ret['Status'] = 'Conf {0} enabled'.format(conf) + ret["Status"] = "Conf {0} enabled".format(conf) else: - ret['Status'] = status + ret["Status"] = status return ret -@salt.utils.decorators.path.which('a2disconf') +@salt.utils.decorators.path.which("a2disconf") def a2disconf(conf): - ''' + """ .. versionadded:: 2016.3.0 Runs a2disconf for the given conf. @@ -306,23 +308,23 @@ def a2disconf(conf): .. code-block:: bash salt '*' apache.a2disconf security - ''' + """ ret = {} - command = ['a2disconf', conf] + command = ["a2disconf", conf] try: - status = __salt__['cmd.retcode'](command, python_shell=False) + status = __salt__["cmd.retcode"](command, python_shell=False) except Exception as e: # pylint: disable=broad-except return e - ret['Name'] = 'Apache2 Disable Conf' - ret['Conf'] = conf + ret["Name"] = "Apache2 Disable Conf" + ret["Conf"] = conf if status == 256: - ret['Status'] = 'Conf {0} Not found'.format(conf) + ret["Status"] = "Conf {0} Not found".format(conf) elif status == 0: - ret['Status'] = 'Conf {0} disabled'.format(conf) + ret["Status"] = "Conf {0} disabled".format(conf) else: - ret['Status'] = status + ret["Status"] = status return ret diff --git a/salt/modules/deb_postgres.py b/salt/modules/deb_postgres.py index 9b5db2ac0dc..d47fd6dc108 100644 --- a/salt/modules/deb_postgres.py +++ b/salt/modules/deb_postgres.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide Postgres compatibility to salt for debian family specific tools. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import pipes @@ -17,25 +18,25 @@ from salt.ext import six log = logging.getLogger(__name__) -__virtualname__ = 'postgres' +__virtualname__ = "postgres" def __virtual__(): - ''' + """ Only load this module if the pg_createcluster bin exists - ''' - if salt.utils.path.which('pg_createcluster'): + """ + if salt.utils.path.which("pg_createcluster"): return __virtualname__ - return (False, 'postgres execution module not loaded: pg_createcluste command not found.') + return ( + False, + "postgres execution module not loaded: pg_createcluste command not found.", + ) -def cluster_create(version, - name='main', - port=None, - locale=None, - encoding=None, - datadir=None): - ''' +def cluster_create( + version, name="main", port=None, locale=None, encoding=None, datadir=None +): + """ Adds a cluster to the Postgres server. .. warning: @@ -52,28 +53,29 @@ def cluster_create(version, salt '*' postgres.cluster_create '9.3' locale='fr_FR' - ''' - cmd = [salt.utils.path.which('pg_createcluster')] + """ + cmd = [salt.utils.path.which("pg_createcluster")] if port: - cmd += ['--port', six.text_type(port)] + cmd += ["--port", six.text_type(port)] if locale: - cmd += ['--locale', locale] + cmd += ["--locale", locale] if encoding: - cmd += ['--encoding', encoding] + cmd += ["--encoding", encoding] if datadir: - cmd += ['--datadir', datadir] + cmd += ["--datadir", datadir] cmd += [version, name] - cmdstr = ' '.join([pipes.quote(c) for c in cmd]) - ret = __salt__['cmd.run_all'](cmdstr, python_shell=False) - if ret.get('retcode', 0) != 0: - log.error('Error creating a Postgresql' - ' cluster {0}/{1}'.format(version, name)) + cmdstr = " ".join([pipes.quote(c) for c in cmd]) + ret = __salt__["cmd.run_all"](cmdstr, python_shell=False) + if ret.get("retcode", 0) != 0: + log.error( + "Error creating a Postgresql" " cluster {0}/{1}".format(version, name) + ) return False return ret def cluster_list(verbose=False): - ''' + """ Return a list of cluster of Postgres server (tuples of version and name). CLI Example: @@ -83,20 +85,19 @@ def cluster_list(verbose=False): salt '*' postgres.cluster_list salt '*' postgres.cluster_list verbose=True - ''' - cmd = [salt.utils.path.which('pg_lsclusters'), '--no-header'] - ret = __salt__['cmd.run_all'](' '.join([pipes.quote(c) for c in cmd])) - if ret.get('retcode', 0) != 0: - log.error('Error listing clusters') - cluster_dict = _parse_pg_lscluster(ret['stdout']) + """ + cmd = [salt.utils.path.which("pg_lsclusters"), "--no-header"] + ret = __salt__["cmd.run_all"](" ".join([pipes.quote(c) for c in cmd])) + if ret.get("retcode", 0) != 0: + log.error("Error listing clusters") + cluster_dict = _parse_pg_lscluster(ret["stdout"]) if verbose: return cluster_dict return cluster_dict.keys() -def cluster_exists(version, - name='main'): - ''' +def cluster_exists(version, name="main"): + """ Checks if a given version and name of a cluster exists. CLI Example: @@ -106,14 +107,12 @@ def cluster_exists(version, salt '*' postgres.cluster_exists '9.3' salt '*' postgres.cluster_exists '9.3' 'main' - ''' - return '{0}/{1}'.format(version, name) in cluster_list() + """ + return "{0}/{1}".format(version, name) in cluster_list() -def cluster_remove(version, - name='main', - stop=False): - ''' +def cluster_remove(version, name="main", stop=False): + """ Remove a cluster on a Postgres server. By default it doesn't try to stop the cluster. @@ -127,35 +126,37 @@ def cluster_remove(version, salt '*' postgres.cluster_remove '9.3' 'main' stop=True - ''' - cmd = [salt.utils.path.which('pg_dropcluster')] + """ + cmd = [salt.utils.path.which("pg_dropcluster")] if stop: - cmd += ['--stop'] + cmd += ["--stop"] cmd += [version, name] - cmdstr = ' '.join([pipes.quote(c) for c in cmd]) - ret = __salt__['cmd.run_all'](cmdstr, python_shell=False) + cmdstr = " ".join([pipes.quote(c) for c in cmd]) + ret = __salt__["cmd.run_all"](cmdstr, python_shell=False) # FIXME - return Boolean ? - if ret.get('retcode', 0) != 0: - log.error('Error removing a Postgresql' - ' cluster {0}/{1}'.format(version, name)) + if ret.get("retcode", 0) != 0: + log.error( + "Error removing a Postgresql" " cluster {0}/{1}".format(version, name) + ) else: - ret['changes'] = ('Successfully removed' - ' cluster {0}/{1}').format(version, name) + ret["changes"] = ("Successfully removed" " cluster {0}/{1}").format( + version, name + ) return ret def _parse_pg_lscluster(output): - ''' + """ Helper function to parse the output of pg_lscluster - ''' + """ cluster_dict = {} for line in output.splitlines(): - version, name, port, status, user, datadir, log = ( - line.split()) - cluster_dict['{0}/{1}'.format(version, name)] = { - 'port': int(port), - 'status': status, - 'user': user, - 'datadir': datadir, - 'log': log} + version, name, port, status, user, datadir, log = line.split() + cluster_dict["{0}/{1}".format(version, name)] = { + "port": int(port), + "status": status, + "user": user, + "datadir": datadir, + "log": log, + } return cluster_dict diff --git a/salt/modules/debconfmod.py b/salt/modules/debconfmod.py index a4e08870f05..3a2c3a109ca 100644 --- a/salt/modules/debconfmod.py +++ b/salt/modules/debconfmod.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Support for Debconf -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -9,53 +9,59 @@ import logging import os import re +import salt.utils.files + # Import salt libs import salt.utils.path -import salt.utils.files import salt.utils.stringutils import salt.utils.versions log = logging.getLogger(__name__) -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} # Define the module's virtual name -__virtualname__ = 'debconf' +__virtualname__ = "debconf" def __virtual__(): - ''' + """ Confirm this module is on a Debian based system and that debconf-utils is installed. - ''' - if __grains__['os_family'] != 'Debian': - return (False, 'The debconfmod module could not be loaded: ' - 'unsupported OS family') + """ + if __grains__["os_family"] != "Debian": + return ( + False, + "The debconfmod module could not be loaded: unsupported OS family", + ) - if salt.utils.path.which('debconf-get-selections') is None: - return (False, 'The debconfmod module could not be loaded: ' - 'debconf-utils is not installed.') + if salt.utils.path.which("debconf-get-selections") is None: + return ( + False, + "The debconfmod module could not be loaded: " + "debconf-utils is not installed.", + ) return __virtualname__ def _unpack_lines(out): - ''' + """ Unpack the debconf lines - ''' - rexp = ('(?ms)' - '^(?P[^#]\\S+)[\t ]+' - '(?P\\S+)[\t ]+' - '(?P\\S+)[\t ]+' - '(?P[^\n]*)$') + """ + rexp = ( + "(?ms)" + "^(?P[^#]\\S+)[\t ]+" + "(?P\\S+)[\t ]+" + "(?P\\S+)[\t ]+" + "(?P[^\n]*)$" + ) lines = re.findall(rexp, out) return lines def get_selections(fetchempty=True): - ''' + """ Answers to debconf questions for all packages in the following format:: {'package': [['question', 'type', 'value'], ...]} @@ -65,26 +71,24 @@ def get_selections(fetchempty=True): .. code-block:: bash salt '*' debconf.get_selections - ''' + """ selections = {} - cmd = 'debconf-get-selections' + cmd = "debconf-get-selections" - out = __salt__['cmd.run_stdout'](cmd) + out = __salt__["cmd.run_stdout"](cmd) lines = _unpack_lines(out) for line in lines: package, question, type_, value = line if fetchempty or value: - (selections - .setdefault(package, []) - .append([question, type_, value])) + (selections.setdefault(package, []).append([question, type_, value])) return selections def show(name): - ''' + """ Answers to debconf questions for a package in the following format:: [['question', 'type', 'value'], ...] @@ -96,7 +100,7 @@ def show(name): .. code-block:: bash salt '*' debconf.show - ''' + """ selections = get_selections() result = selections.get(name) @@ -104,16 +108,16 @@ def show(name): def _set_file(path): - ''' + """ Execute the set selections command for debconf - ''' - cmd = 'debconf-set-selections {0}'.format(path) + """ + cmd = "debconf-set-selections {0}".format(path) - __salt__['cmd.run_stdout'](cmd, python_shell=False) + __salt__["cmd.run_stdout"](cmd, python_shell=False) def set_(package, question, type, value, *extra): - ''' + """ Set answers to debconf questions for a package. CLI Example: @@ -121,10 +125,10 @@ def set_(package, question, type, value, *extra): .. code-block:: bash salt '*' debconf.set [ ...] - ''' + """ if extra: - value = ' '.join((value,) + tuple(extra)) + value = " ".join((value,) + tuple(extra)) fd_, fname = salt.utils.files.mkstemp(prefix="salt-", close_fd=False) @@ -139,8 +143,8 @@ def set_(package, question, type, value, *extra): return True -def set_template(path, template, context, defaults, saltenv='base', **kwargs): - ''' +def set_template(path, template, context, defaults, saltenv="base", **kwargs): + """ Set answers to debconf questions from a template. path @@ -161,22 +165,23 @@ def set_template(path, template, context, defaults, saltenv='base', **kwargs): salt '*' debconf.set_template salt://pathto/pkg.selections.jinja jinja None None - ''' + """ - path = __salt__['cp.get_template']( + path = __salt__["cp.get_template"]( path=path, dest=None, template=template, saltenv=saltenv, context=context, defaults=defaults, - **kwargs) + **kwargs + ) return set_file(path, saltenv, **kwargs) -def set_file(path, saltenv='base', **kwargs): - ''' +def set_file(path, saltenv="base", **kwargs): + """ Set answers to debconf questions from a file. CLI Example: @@ -184,12 +189,12 @@ def set_file(path, saltenv='base', **kwargs): .. code-block:: bash salt '*' debconf.set_file salt://pathto/pkg.selections - ''' - if '__env__' in kwargs: + """ + if "__env__" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('__env__') + kwargs.pop("__env__") - path = __salt__['cp.cache_file'](path, saltenv) + path = __salt__["cp.cache_file"](path, saltenv) if path: _set_file(path) return True diff --git a/salt/modules/debian_ip.py b/salt/modules/debian_ip.py index c8bd587a1ec..a3664c01dce 100644 --- a/salt/modules/debian_ip.py +++ b/salt/modules/debian_ip.py @@ -1,26 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" The networking module for Debian-based distros References: * http://www.debian.org/doc/manuals/debian-reference/ch05.en.html -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import functools import logging -import os.path import os +import os.path import re import time # Import third party libs import jinja2 import jinja2.exceptions -from salt.ext import six -from salt.ext.six.moves import StringIO # pylint: disable=import-error,no-name-in-module # Import salt libs import salt.utils.dns @@ -29,7 +28,10 @@ import salt.utils.odict import salt.utils.stringutils import salt.utils.templates import salt.utils.validate.net - +from salt.ext import six +from salt.ext.six.moves import ( # pylint: disable=import-error,no-name-in-module + StringIO, +) # Set up logging log = logging.getLogger(__name__) @@ -37,251 +39,260 @@ log = logging.getLogger(__name__) # Set up template environment JINJA = jinja2.Environment( loader=jinja2.FileSystemLoader( - os.path.join(salt.utils.templates.TEMPLATE_DIRNAME, 'debian_ip') + os.path.join(salt.utils.templates.TEMPLATE_DIRNAME, "debian_ip") ) ) # Define the module's virtual name -__virtualname__ = 'ip' +__virtualname__ = "ip" def __virtual__(): - ''' + """ Confine this module to Debian-based distros - ''' - if __grains__['os_family'] == 'Debian': + """ + if __grains__["os_family"] == "Debian": return __virtualname__ - return (False, 'The debian_ip module could not be loaded: ' - 'unsupported OS family') + return (False, "The debian_ip module could not be loaded: unsupported OS family") _ETHTOOL_CONFIG_OPTS = { - 'speed': 'link-speed', - 'duplex': 'link-duplex', - 'autoneg': 'ethernet-autoneg', - 'ethernet-port': 'ethernet-port', - 'wol': 'ethernet-wol', - 'driver-message-level': 'driver-message-level', - 'ethernet-pause-rx': 'ethernet-pause-rx', - 'ethernet-pause-tx': 'ethernet-pause-tx', - 'ethernet-pause-autoneg': 'ethernet-pause-autoneg', - 'rx': 'offload-rx', - 'tx': 'offload-tx', - 'sg': 'offload-sg', - 'tso': 'offload-tso', - 'ufo': 'offload-ufo', - 'gso': 'offload-gso', - 'gro': 'offload-gro', - 'lro': 'offload-lro', - 'hardware-irq-coalesce-adaptive-rx': 'hardware-irq-coalesce-adaptive-rx', - 'hardware-irq-coalesce-adaptive-tx': 'hardware-irq-coalesce-adaptive-tx', - 'hardware-irq-coalesce-rx-usecs': 'hardware-irq-coalesce-rx-usecs', - 'hardware-irq-coalesce-rx-frames': 'hardware-irq-coalesce-rx-frames', - 'hardware-dma-ring-rx': 'hardware-dma-ring-rx', - 'hardware-dma-ring-rx-mini': 'hardware-dma-ring-rx-mini', - 'hardware-dma-ring-rx-jumbo': 'hardware-dma-ring-rx-jumbo', - 'hardware-dma-ring-tx': 'hardware-dma-ring-tx', + "speed": "link-speed", + "duplex": "link-duplex", + "autoneg": "ethernet-autoneg", + "ethernet-port": "ethernet-port", + "wol": "ethernet-wol", + "driver-message-level": "driver-message-level", + "ethernet-pause-rx": "ethernet-pause-rx", + "ethernet-pause-tx": "ethernet-pause-tx", + "ethernet-pause-autoneg": "ethernet-pause-autoneg", + "rx": "offload-rx", + "tx": "offload-tx", + "sg": "offload-sg", + "tso": "offload-tso", + "ufo": "offload-ufo", + "gso": "offload-gso", + "gro": "offload-gro", + "lro": "offload-lro", + "hardware-irq-coalesce-adaptive-rx": "hardware-irq-coalesce-adaptive-rx", + "hardware-irq-coalesce-adaptive-tx": "hardware-irq-coalesce-adaptive-tx", + "hardware-irq-coalesce-rx-usecs": "hardware-irq-coalesce-rx-usecs", + "hardware-irq-coalesce-rx-frames": "hardware-irq-coalesce-rx-frames", + "hardware-dma-ring-rx": "hardware-dma-ring-rx", + "hardware-dma-ring-rx-mini": "hardware-dma-ring-rx-mini", + "hardware-dma-ring-rx-jumbo": "hardware-dma-ring-rx-jumbo", + "hardware-dma-ring-tx": "hardware-dma-ring-tx", } _REV_ETHTOOL_CONFIG_OPTS = { - 'link-speed': 'speed', - 'link-duplex': 'duplex', - 'ethernet-autoneg': 'autoneg', - 'ethernet-port': 'ethernet-port', - 'ethernet-wol': 'wol', - 'driver-message-level': 'driver-message-level', - 'ethernet-pause-rx': 'ethernet-pause-rx', - 'ethernet-pause-tx': 'ethernet-pause-tx', - 'ethernet-pause-autoneg': 'ethernet-pause-autoneg', - 'offload-rx': 'rx', - 'offload-tx': 'tx', - 'offload-sg': 'sg', - 'offload-tso': 'tso', - 'offload-ufo': 'ufo', - 'offload-gso': 'gso', - 'offload-lro': 'lro', - 'offload-gro': 'gro', - 'hardware-irq-coalesce-adaptive-rx': 'hardware-irq-coalesce-adaptive-rx', - 'hardware-irq-coalesce-adaptive-tx': 'hardware-irq-coalesce-adaptive-tx', - 'hardware-irq-coalesce-rx-usecs': 'hardware-irq-coalesce-rx-usecs', - 'hardware-irq-coalesce-rx-frames': 'hardware-irq-coalesce-rx-frames', - 'hardware-dma-ring-rx': 'hardware-dma-ring-rx', - 'hardware-dma-ring-rx-mini': 'hardware-dma-ring-rx-mini', - 'hardware-dma-ring-rx-jumbo': 'hardware-dma-ring-rx-jumbo', - 'hardware-dma-ring-tx': 'hardware-dma-ring-tx', + "link-speed": "speed", + "link-duplex": "duplex", + "ethernet-autoneg": "autoneg", + "ethernet-port": "ethernet-port", + "ethernet-wol": "wol", + "driver-message-level": "driver-message-level", + "ethernet-pause-rx": "ethernet-pause-rx", + "ethernet-pause-tx": "ethernet-pause-tx", + "ethernet-pause-autoneg": "ethernet-pause-autoneg", + "offload-rx": "rx", + "offload-tx": "tx", + "offload-sg": "sg", + "offload-tso": "tso", + "offload-ufo": "ufo", + "offload-gso": "gso", + "offload-lro": "lro", + "offload-gro": "gro", + "hardware-irq-coalesce-adaptive-rx": "hardware-irq-coalesce-adaptive-rx", + "hardware-irq-coalesce-adaptive-tx": "hardware-irq-coalesce-adaptive-tx", + "hardware-irq-coalesce-rx-usecs": "hardware-irq-coalesce-rx-usecs", + "hardware-irq-coalesce-rx-frames": "hardware-irq-coalesce-rx-frames", + "hardware-dma-ring-rx": "hardware-dma-ring-rx", + "hardware-dma-ring-rx-mini": "hardware-dma-ring-rx-mini", + "hardware-dma-ring-rx-jumbo": "hardware-dma-ring-rx-jumbo", + "hardware-dma-ring-tx": "hardware-dma-ring-tx", } _DEB_CONFIG_PPPOE_OPTS = { - 'user': 'user', - 'password': 'password', - 'provider': 'provider', - 'pppoe_iface': 'pppoe_iface', - 'noipdefault': 'noipdefault', - 'usepeerdns': 'usepeerdns', - 'defaultroute': 'defaultroute', - 'holdoff': 'holdoff', - 'maxfail': 'maxfail', - 'hide-password': 'hide-password', - 'lcp-echo-interval': 'lcp-echo-interval', - 'lcp-echo-failure': 'lcp-echo-failure', - 'connect': 'connect', - 'noauth': 'noauth', - 'persist': 'persist', - 'mtu': 'mtu', - 'noaccomp': 'noaccomp', - 'linkname': 'linkname', + "user": "user", + "password": "password", + "provider": "provider", + "pppoe_iface": "pppoe_iface", + "noipdefault": "noipdefault", + "usepeerdns": "usepeerdns", + "defaultroute": "defaultroute", + "holdoff": "holdoff", + "maxfail": "maxfail", + "hide-password": "hide-password", + "lcp-echo-interval": "lcp-echo-interval", + "lcp-echo-failure": "lcp-echo-failure", + "connect": "connect", + "noauth": "noauth", + "persist": "persist", + "mtu": "mtu", + "noaccomp": "noaccomp", + "linkname": "linkname", } -_DEB_ROUTES_FILE = '/etc/network/routes' -_DEB_NETWORK_FILE = '/etc/network/interfaces' -_DEB_NETWORK_DIR = '/etc/network/interfaces.d/' -_DEB_NETWORK_UP_DIR = '/etc/network/if-up.d/' -_DEB_NETWORK_DOWN_DIR = '/etc/network/if-down.d/' -_DEB_NETWORK_CONF_FILES = '/etc/modprobe.d/' -_DEB_NETWORKING_FILE = '/etc/default/networking' -_DEB_HOSTNAME_FILE = '/etc/hostname' -_DEB_RESOLV_FILE = '/etc/resolv.conf' -_DEB_PPP_DIR = '/etc/ppp/peers/' +_DEB_ROUTES_FILE = "/etc/network/routes" +_DEB_NETWORK_FILE = "/etc/network/interfaces" +_DEB_NETWORK_DIR = "/etc/network/interfaces.d/" +_DEB_NETWORK_UP_DIR = "/etc/network/if-up.d/" +_DEB_NETWORK_DOWN_DIR = "/etc/network/if-down.d/" +_DEB_NETWORK_CONF_FILES = "/etc/modprobe.d/" +_DEB_NETWORKING_FILE = "/etc/default/networking" +_DEB_HOSTNAME_FILE = "/etc/hostname" +_DEB_RESOLV_FILE = "/etc/resolv.conf" +_DEB_PPP_DIR = "/etc/ppp/peers/" -_CONFIG_TRUE = ['yes', 'on', 'true', '1', True] -_CONFIG_FALSE = ['no', 'off', 'false', '0', False] +_CONFIG_TRUE = ["yes", "on", "true", "1", True] +_CONFIG_FALSE = ["no", "off", "false", "0", False] _IFACE_TYPES = [ - 'eth', 'bond', 'alias', 'clone', - 'ipsec', 'dialup', 'bridge', 'slave', - 'vlan', 'pppoe', 'source', + "eth", + "bond", + "alias", + "clone", + "ipsec", + "dialup", + "bridge", + "slave", + "vlan", + "pppoe", + "source", ] def _error_msg_iface(iface, option, expected): - ''' + """ Build an appropriate error message from a given option and a list of expected values. - ''' - msg = 'Invalid option -- Interface: {0}, Option: {1}, Expected: [{2}]' - return msg.format(iface, option, '|'.join(str(e) for e in expected)) + """ + msg = "Invalid option -- Interface: {0}, Option: {1}, Expected: [{2}]" + return msg.format(iface, option, "|".join(str(e) for e in expected)) def _error_msg_routes(iface, option, expected): - ''' + """ Build an appropriate error message from a given option and a list of expected values. - ''' - msg = 'Invalid option -- Route interface: {0}, Option: {1}, Expected: [{2}]' + """ + msg = "Invalid option -- Route interface: {0}, Option: {1}, Expected: [{2}]" return msg.format(iface, option, expected) def _log_default_iface(iface, opt, value): - msg = 'Using default option -- Interface: {0} Option: {1} Value: {2}' + msg = "Using default option -- Interface: {0} Option: {1} Value: {2}" log.info(msg.format(iface, opt, value)) def _error_msg_network(option, expected): - ''' + """ Build an appropriate error message from a given option and a list of expected values. - ''' - msg = 'Invalid network setting -- Setting: {0}, Expected: [{1}]' - return msg.format(option, '|'.join(str(e) for e in expected)) + """ + msg = "Invalid network setting -- Setting: {0}, Expected: [{1}]" + return msg.format(option, "|".join(str(e) for e in expected)) def _log_default_network(opt, value): - msg = 'Using existing setting -- Setting: {0} Value: {1}' + msg = "Using existing setting -- Setting: {0} Value: {1}" log.info(msg.format(opt, value)) def _raise_error_iface(iface, option, expected): - ''' + """ Log and raise an error with a logical formatted message. - ''' + """ msg = _error_msg_iface(iface, option, expected) log.error(msg) raise AttributeError(msg) def _raise_error_network(option, expected): - ''' + """ Log and raise an error with a logical formatted message. - ''' + """ msg = _error_msg_network(option, expected) log.error(msg) raise AttributeError(msg) def _raise_error_routes(iface, option, expected): - ''' + """ Log and raise an error with a logical formatted message. - ''' + """ msg = _error_msg_routes(iface, option, expected) log.error(msg) raise AttributeError(msg) def _read_file(path): - ''' + """ Reads and returns the contents of a text file - ''' + """ try: - with salt.utils.files.flopen(path, 'rb') as contents: - return [salt.utils.stringutils.to_str(line) for line in contents.readlines()] + with salt.utils.files.flopen(path, "rb") as contents: + return [ + salt.utils.stringutils.to_str(line) for line in contents.readlines() + ] except (OSError, IOError): - return '' + return "" def _parse_resolve(): - ''' + """ Parse /etc/resolv.conf - ''' + """ return salt.utils.dns.parse_resolv(_DEB_RESOLV_FILE) def _parse_domainname(): - ''' + """ Parse /etc/resolv.conf and return domainname - ''' - return _parse_resolve().get('domain', '') + """ + return _parse_resolve().get("domain", "") def _parse_searchdomain(): - ''' + """ Parse /etc/resolv.conf and return searchdomain - ''' - return _parse_resolve().get('search', '') + """ + return _parse_resolve().get("search", "") def _parse_hostname(): - ''' + """ Parse /etc/hostname and return hostname - ''' + """ contents = _read_file(_DEB_HOSTNAME_FILE) if contents: - return contents[0].split('\n')[0] + return contents[0].split("\n")[0] else: - return '' + return "" def _parse_current_network_settings(): - ''' + """ Parse /etc/default/networking and return current configuration - ''' + """ opts = salt.utils.odict.OrderedDict() - opts['networking'] = '' + opts["networking"] = "" if os.path.isfile(_DEB_NETWORKING_FILE): with salt.utils.files.fopen(_DEB_NETWORKING_FILE) as contents: for line in contents: salt.utils.stringutils.to_unicode(line) - if line.startswith('#'): + if line.startswith("#"): continue - elif line.startswith('CONFIGURE_INTERFACES'): - opts['networking'] = line.split('=', 1)[1].strip() + elif line.startswith("CONFIGURE_INTERFACES"): + opts["networking"] = line.split("=", 1)[1].strip() hostname = _parse_hostname() domainname = _parse_domainname() searchdomain = _parse_searchdomain() - opts['hostname'] = hostname - opts['domainname'] = domainname - opts['searchdomain'] = searchdomain + opts["hostname"] = hostname + opts["domainname"] = domainname + opts["searchdomain"] = searchdomain return opts @@ -290,21 +301,18 @@ def _parse_current_network_settings(): def __ipv4_quad(value): - '''validate an IPv4 address''' - return (salt.utils.validate.net.ipv4_addr(value), value, - 'dotted IPv4 address') + """validate an IPv4 address""" + return (salt.utils.validate.net.ipv4_addr(value), value, "dotted IPv4 address") def __ipv6(value): - '''validate an IPv6 address''' - return (salt.utils.validate.net.ipv6_addr(value), value, - 'IPv6 address') + """validate an IPv6 address""" + return (salt.utils.validate.net.ipv6_addr(value), value, "IPv6 address") def __mac(value): - '''validate a mac address''' - return (salt.utils.validate.net.mac(value), value, - 'MAC address') + """validate a mac address""" + return (salt.utils.validate.net.mac(value), value, "MAC address") def __anything(value): @@ -312,30 +320,30 @@ def __anything(value): def __int(value): - '''validate an integer''' + """validate an integer""" valid, _value = False, value try: _value = int(value) valid = True except ValueError: pass - return (valid, _value, 'integer') + return (valid, _value, "integer") def __float(value): - '''validate a float''' + """validate a float""" valid, _value = False, value try: _value = float(value) valid = True except ValueError: pass - return (valid, _value, 'float') + return (valid, _value, "float") def __ipv4_netmask(value): - '''validate an IPv4 dotted quad or integer CIDR netmask''' - valid, errmsg = False, 'dotted quad or integer CIDR (0->32)' + """validate an IPv4 dotted quad or integer CIDR netmask""" + valid, errmsg = False, "dotted quad or integer CIDR (0->32)" valid, value, _ = __int(value) if not (valid and 0 <= value <= 32): valid = salt.utils.validate.net.netmask(value) @@ -343,15 +351,15 @@ def __ipv4_netmask(value): def __ipv6_netmask(value): - '''validate an IPv6 integer netmask''' - valid, errmsg = False, 'IPv6 netmask (0->128)' + """validate an IPv6 integer netmask""" + valid, errmsg = False, "IPv6 netmask (0->128)" valid, value, _ = __int(value) - valid = (valid and 0 <= value <= 128) + valid = valid and 0 <= value <= 128 return (valid, value, errmsg) def __within2(value, within=None, errmsg=None, dtype=None): - '''validate that a value is in ``within`` and optionally a ``dtype``''' + """validate that a value is in ``within`` and optionally a ``dtype``""" valid, _value = False, value if dtype: try: @@ -363,169 +371,172 @@ def __within2(value, within=None, errmsg=None, dtype=None): valid = _value in within if errmsg is None: if dtype: - typename = getattr(dtype, '__name__', - hasattr(dtype, '__class__') - and getattr(dtype.__class__, 'name', dtype)) - errmsg = '{0} within \'{1}\''.format(typename, within) + typename = getattr( + dtype, + "__name__", + hasattr(dtype, "__class__") and getattr(dtype.__class__, "name", dtype), + ) + errmsg = "{0} within '{1}'".format(typename, within) else: - errmsg = 'within \'{0}\''.format(within) + errmsg = "within '{0}'".format(within) return (valid, _value, errmsg) def __within(within=None, errmsg=None, dtype=None): - return functools.partial(__within2, within=within, - errmsg=errmsg, dtype=dtype) + return functools.partial(__within2, within=within, errmsg=errmsg, dtype=dtype) def __space_delimited_list(value): - '''validate that a value contains one or more space-delimited values''' + """validate that a value contains one or more space-delimited values""" if isinstance(value, six.string_types): value = value.strip().split() - if hasattr(value, '__iter__') and value != []: - return (True, value, 'space-delimited string') + if hasattr(value, "__iter__") and value != []: + return (True, value, "space-delimited string") else: - return (False, value, '{0} is not a valid space-delimited value.\n'.format(value)) + return ( + False, + value, + "{0} is not a valid space-delimited value.\n".format(value), + ) SALT_ATTR_TO_DEBIAN_ATTR_MAP = { - 'dns': 'dns-nameservers', - 'search': 'dns-search', - 'hwaddr': 'hwaddress', # TODO: this limits bootp functionality - 'ipaddr': 'address', - 'ipaddrs': 'addresses', + "dns": "dns-nameservers", + "search": "dns-search", + "hwaddr": "hwaddress", # TODO: this limits bootp functionality + "ipaddr": "address", + "ipaddrs": "addresses", } DEBIAN_ATTR_TO_SALT_ATTR_MAP = dict( - (v, k) for (k, v) in six.iteritems(SALT_ATTR_TO_DEBIAN_ATTR_MAP)) + (v, k) for (k, v) in six.iteritems(SALT_ATTR_TO_DEBIAN_ATTR_MAP) +) # TODO -DEBIAN_ATTR_TO_SALT_ATTR_MAP['address'] = 'address' -DEBIAN_ATTR_TO_SALT_ATTR_MAP['hwaddress'] = 'hwaddress' +DEBIAN_ATTR_TO_SALT_ATTR_MAP["address"] = "address" +DEBIAN_ATTR_TO_SALT_ATTR_MAP["hwaddress"] = "hwaddress" -IPV4_VALID_PROTO = ['bootp', 'dhcp', 'static', 'manual', 'loopback', 'ppp'] +IPV4_VALID_PROTO = ["bootp", "dhcp", "static", "manual", "loopback", "ppp"] IPV4_ATTR_MAP = { - 'proto': __within(IPV4_VALID_PROTO, dtype=six.text_type), + "proto": __within(IPV4_VALID_PROTO, dtype=six.text_type), # ipv4 static & manual - 'address': __ipv4_quad, - 'addresses': __anything, - 'netmask': __ipv4_netmask, - 'broadcast': __ipv4_quad, - 'metric': __int, - 'gateway': __ipv4_quad, # supports a colon-delimited list - 'pointopoint': __ipv4_quad, - 'hwaddress': __mac, - 'mtu': __int, - 'scope': __within(['global', 'link', 'host'], dtype=six.text_type), + "address": __ipv4_quad, + "addresses": __anything, + "netmask": __ipv4_netmask, + "broadcast": __ipv4_quad, + "metric": __int, + "gateway": __ipv4_quad, # supports a colon-delimited list + "pointopoint": __ipv4_quad, + "hwaddress": __mac, + "mtu": __int, + "scope": __within(["global", "link", "host"], dtype=six.text_type), # dhcp - 'hostname': __anything, - 'leasehours': __int, - 'leasetime': __int, - 'vendor': __anything, - 'client': __anything, + "hostname": __anything, + "leasehours": __int, + "leasetime": __int, + "vendor": __anything, + "client": __anything, # bootp - 'bootfile': __anything, - 'server': __ipv4_quad, - 'hwaddr': __mac, + "bootfile": __anything, + "server": __ipv4_quad, + "hwaddr": __mac, # tunnel - 'mode': __within(['gre', 'GRE', 'ipip', 'IPIP', '802.3ad'], dtype=six.text_type), - 'endpoint': __ipv4_quad, - 'dstaddr': __ipv4_quad, - 'local': __ipv4_quad, - 'ttl': __int, + "mode": __within(["gre", "GRE", "ipip", "IPIP", "802.3ad"], dtype=six.text_type), + "endpoint": __ipv4_quad, + "dstaddr": __ipv4_quad, + "local": __ipv4_quad, + "ttl": __int, # bond - 'slaves': __anything, + "slaves": __anything, # ppp - 'provider': __anything, - 'unit': __int, - 'options': __anything, + "provider": __anything, + "unit": __int, + "options": __anything, # resolvconf - 'dns-nameservers': __space_delimited_list, - 'dns-search': __space_delimited_list, + "dns-nameservers": __space_delimited_list, + "dns-search": __space_delimited_list, # - 'vlan-raw-device': __anything, + "vlan-raw-device": __anything, # - 'network': __anything, # i don't know what this is - - 'test': __anything, # TODO - 'enable_ipv6': __anything, # TODO + "network": __anything, # i don't know what this is + "test": __anything, # TODO + "enable_ipv6": __anything, # TODO } -IPV6_VALID_PROTO = ['auto', 'loopback', 'static', 'manual', - 'dhcp', 'v4tunnel', '6to4'] +IPV6_VALID_PROTO = ["auto", "loopback", "static", "manual", "dhcp", "v4tunnel", "6to4"] IPV6_ATTR_MAP = { - 'proto': __within(IPV6_VALID_PROTO), + "proto": __within(IPV6_VALID_PROTO), # ipv6 static & manual - 'address': __ipv6, - 'addresses': __anything, - 'netmask': __ipv6_netmask, - 'broadcast': __ipv6, - 'gateway': __ipv6, # supports a colon-delimited list - 'hwaddress': __mac, - 'mtu': __int, - 'scope': __within(['global', 'site', 'link', 'host'], dtype=six.text_type), + "address": __ipv6, + "addresses": __anything, + "netmask": __ipv6_netmask, + "broadcast": __ipv6, + "gateway": __ipv6, # supports a colon-delimited list + "hwaddress": __mac, + "mtu": __int, + "scope": __within(["global", "site", "link", "host"], dtype=six.text_type), # inet6 auto - 'privext': __within([0, 1, 2], dtype=int), - 'dhcp': __within([0, 1], dtype=int), + "privext": __within([0, 1, 2], dtype=int), + "dhcp": __within([0, 1], dtype=int), # inet6 static & manual & dhcp - 'media': __anything, - 'accept_ra': __within([0, 1], dtype=int), - 'autoconf': __within([0, 1], dtype=int), - 'preferred-lifetime': __int, - 'dad-attempts': __int, # 0 to disable - 'dad-interval': __float, + "media": __anything, + "accept_ra": __within([0, 1], dtype=int), + "autoconf": __within([0, 1], dtype=int), + "preferred-lifetime": __int, + "dad-attempts": __int, # 0 to disable + "dad-interval": __float, # bond - 'slaves': __anything, + "slaves": __anything, # tunnel - 'mode': __within(['gre', 'GRE', 'ipip', 'IPIP', '802.3ad'], dtype=six.text_type), - 'endpoint': __ipv4_quad, - 'local': __ipv4_quad, - 'ttl': __int, + "mode": __within(["gre", "GRE", "ipip", "IPIP", "802.3ad"], dtype=six.text_type), + "endpoint": __ipv4_quad, + "local": __ipv4_quad, + "ttl": __int, # resolvconf - 'dns-nameservers': __space_delimited_list, - 'dns-search': __space_delimited_list, + "dns-nameservers": __space_delimited_list, + "dns-search": __space_delimited_list, # - 'vlan-raw-device': __anything, - - 'test': __anything, # TODO - 'enable_ipv6': __anything, # TODO + "vlan-raw-device": __anything, + "test": __anything, # TODO + "enable_ipv6": __anything, # TODO } WIRELESS_ATTR_MAP = { - 'wireless-essid': __anything, - 'wireless-mode': __anything, # TODO - 'wpa-ap-scan': __within([0, 1, 2], dtype=int), # TODO - 'wpa-conf': __anything, - 'wpa-driver': __anything, - 'wpa-group': __anything, - 'wpa-key-mgmt': __anything, - 'wpa-pairwise': __anything, - 'wpa-psk': __anything, - 'wpa-proto': __anything, # partial(__within, - 'wpa-roam': __anything, - 'wpa-ssid': __anything, # TODO + "wireless-essid": __anything, + "wireless-mode": __anything, # TODO + "wpa-ap-scan": __within([0, 1, 2], dtype=int), # TODO + "wpa-conf": __anything, + "wpa-driver": __anything, + "wpa-group": __anything, + "wpa-key-mgmt": __anything, + "wpa-pairwise": __anything, + "wpa-psk": __anything, + "wpa-proto": __anything, # partial(__within, + "wpa-roam": __anything, + "wpa-ssid": __anything, # TODO } ATTRMAPS = { - 'inet': [IPV4_ATTR_MAP, WIRELESS_ATTR_MAP], - 'inet6': [IPV6_ATTR_MAP, WIRELESS_ATTR_MAP] + "inet": [IPV4_ATTR_MAP, WIRELESS_ATTR_MAP], + "inet6": [IPV6_ATTR_MAP, WIRELESS_ATTR_MAP], } -def _validate_interface_option(attr, value, addrfam='inet'): - '''lookup the validation function for a [addrfam][attr] and +def _validate_interface_option(attr, value, addrfam="inet"): + """lookup the validation function for a [addrfam][attr] and return the results :param attr: attribute name :param value: raw setting value :param addrfam: address family (inet, inet6, - ''' - valid, _value, errmsg = False, value, 'Unknown validator' + """ + valid, _value, errmsg = False, value, "Unknown validator" attrmaps = ATTRMAPS.get(addrfam, []) for attrmap in attrmaps: if attr in attrmap: @@ -536,21 +547,21 @@ def _validate_interface_option(attr, value, addrfam='inet'): def _attrmaps_contain_attr(attr): - return ( - attr in WIRELESS_ATTR_MAP or - attr in IPV4_ATTR_MAP or - attr in IPV6_ATTR_MAP) + return attr in WIRELESS_ATTR_MAP or attr in IPV4_ATTR_MAP or attr in IPV6_ATTR_MAP def _parse_interfaces(interface_files=None): - ''' + """ Parse /etc/network/interfaces and return current configured interfaces - ''' + """ if interface_files is None: interface_files = [] # Add this later. if os.path.exists(_DEB_NETWORK_DIR): - interface_files += ['{0}/{1}'.format(_DEB_NETWORK_DIR, dir) for dir in os.listdir(_DEB_NETWORK_DIR)] + interface_files += [ + "{0}/{1}".format(_DEB_NETWORK_DIR, dir) + for dir in os.listdir(_DEB_NETWORK_DIR) + ] if os.path.isfile(_DEB_NETWORK_FILE): interface_files.insert(0, _DEB_NETWORK_FILE) @@ -567,14 +578,14 @@ def _parse_interfaces(interface_files=None): # Identify the clauses by the first word of each line. # Go to the next line if the current line is a comment # or all spaces. - if line.lstrip().startswith('#') or line.isspace(): + if line.lstrip().startswith("#") or line.isspace(): continue # Parse the iface clause - if line.startswith('iface'): + if line.startswith("iface"): sline = line.split() if len(sline) != 4: - msg = 'Interface file malformed: {0}.' + msg = "Interface file malformed: {0}." msg = msg.format(sline) log.error(msg) raise AttributeError(msg) @@ -588,17 +599,19 @@ def _parse_interfaces(interface_files=None): adapters[iface_name] = salt.utils.odict.OrderedDict() # Create item in dict, if not already there - if 'data' not in adapters[iface_name]: - adapters[iface_name]['data'] = salt.utils.odict.OrderedDict() + if "data" not in adapters[iface_name]: + adapters[iface_name]["data"] = salt.utils.odict.OrderedDict() - if addrfam not in adapters[iface_name]['data']: - adapters[iface_name]['data'][addrfam] = salt.utils.odict.OrderedDict() + if addrfam not in adapters[iface_name]["data"]: + adapters[iface_name]["data"][ + addrfam + ] = salt.utils.odict.OrderedDict() - iface_dict = adapters[iface_name]['data'][addrfam] + iface_dict = adapters[iface_name]["data"][addrfam] - iface_dict['addrfam'] = addrfam - iface_dict['proto'] = method - iface_dict['filename'] = interface_file + iface_dict["addrfam"] = addrfam + iface_dict["proto"] = method + iface_dict["filename"] = interface_file # Parse the detail clauses. elif line[0].isspace(): @@ -609,123 +622,130 @@ def _parse_interfaces(interface_files=None): attr, valuestr = line.rstrip().split(None, 1) if _attrmaps_contain_attr(attr): - if '-' in attr: - attrname = attr.replace('-', '_') + if "-" in attr: + attrname = attr.replace("-", "_") else: attrname = attr (valid, value, errmsg) = _validate_interface_option( - attr, valuestr, addrfam) - if attrname == 'address' and 'address' in iface_dict: - if 'addresses' not in iface_dict: - iface_dict['addresses'] = [] - iface_dict['addresses'].append(value) + attr, valuestr, addrfam + ) + if attrname == "address" and "address" in iface_dict: + if "addresses" not in iface_dict: + iface_dict["addresses"] = [] + iface_dict["addresses"].append(value) else: iface_dict[attrname] = value elif attr in _REV_ETHTOOL_CONFIG_OPTS: - if 'ethtool' not in iface_dict: - iface_dict['ethtool'] = salt.utils.odict.OrderedDict() - iface_dict['ethtool'][attr] = valuestr + if "ethtool" not in iface_dict: + iface_dict["ethtool"] = salt.utils.odict.OrderedDict() + iface_dict["ethtool"][attr] = valuestr - elif attr.startswith('bond'): - opt = re.split(r'[_-]', attr, maxsplit=1)[1] - if 'bonding' not in iface_dict: - iface_dict['bonding'] = salt.utils.odict.OrderedDict() - iface_dict['bonding'][opt] = valuestr + elif attr.startswith("bond"): + opt = re.split(r"[_-]", attr, maxsplit=1)[1] + if "bonding" not in iface_dict: + iface_dict["bonding"] = salt.utils.odict.OrderedDict() + iface_dict["bonding"][opt] = valuestr - elif attr.startswith('bridge'): - opt = re.split(r'[_-]', attr, maxsplit=1)[1] - if 'bridging' not in iface_dict: - iface_dict['bridging'] = salt.utils.odict.OrderedDict() - iface_dict['bridging'][opt] = valuestr + elif attr.startswith("bridge"): + opt = re.split(r"[_-]", attr, maxsplit=1)[1] + if "bridging" not in iface_dict: + iface_dict["bridging"] = salt.utils.odict.OrderedDict() + iface_dict["bridging"][opt] = valuestr - elif attr in ['up', 'pre-up', 'post-up', - 'down', 'pre-down', 'post-down']: + elif attr in [ + "up", + "pre-up", + "post-up", + "down", + "pre-down", + "post-down", + ]: cmd = valuestr - cmd_key = '{0}_cmds'.format(re.sub('-', '_', attr)) + cmd_key = "{0}_cmds".format(re.sub("-", "_", attr)) if cmd_key not in iface_dict: iface_dict[cmd_key] = [] iface_dict[cmd_key].append(cmd) - elif line.startswith('auto'): + elif line.startswith("auto"): for word in line.split()[1:]: if word not in adapters: adapters[word] = salt.utils.odict.OrderedDict() - adapters[word]['enabled'] = True + adapters[word]["enabled"] = True - elif line.startswith('allow-hotplug'): + elif line.startswith("allow-hotplug"): for word in line.split()[1:]: if word not in adapters: adapters[word] = salt.utils.odict.OrderedDict() - adapters[word]['hotplug'] = True + adapters[word]["hotplug"] = True - elif line.startswith('source'): - if 'source' not in adapters: - adapters['source'] = salt.utils.odict.OrderedDict() + elif line.startswith("source"): + if "source" not in adapters: + adapters["source"] = salt.utils.odict.OrderedDict() # Create item in dict, if not already there - if 'data' not in adapters['source']: - adapters['source']['data'] = salt.utils.odict.OrderedDict() - adapters['source']['data']['sources'] = [] - adapters['source']['data']['sources'].append(line.split()[1]) + if "data" not in adapters["source"]: + adapters["source"]["data"] = salt.utils.odict.OrderedDict() + adapters["source"]["data"]["sources"] = [] + adapters["source"]["data"]["sources"].append(line.split()[1]) # Return a sorted list of the keys for bond, bridge and ethtool options to # ensure a consistent order for iface_name in adapters: - if iface_name == 'source': + if iface_name == "source": continue - if 'data' not in adapters[iface_name]: - msg = 'Interface file malformed for interface: {0}.'.format(iface_name) + if "data" not in adapters[iface_name]: + msg = "Interface file malformed for interface: {0}.".format(iface_name) log.error(msg) adapters.pop(iface_name) continue - for opt in ['ethtool', 'bonding', 'bridging']: - if 'inet' in adapters[iface_name]['data']: - if opt in adapters[iface_name]['data']['inet']: - opt_keys = sorted(adapters[iface_name]['data']['inet'][opt].keys()) - adapters[iface_name]['data']['inet'][opt + '_keys'] = opt_keys + for opt in ["ethtool", "bonding", "bridging"]: + if "inet" in adapters[iface_name]["data"]: + if opt in adapters[iface_name]["data"]["inet"]: + opt_keys = sorted(adapters[iface_name]["data"]["inet"][opt].keys()) + adapters[iface_name]["data"]["inet"][opt + "_keys"] = opt_keys return adapters def _parse_ethtool_opts(opts, iface): - ''' + """ Filters given options and outputs valid settings for ETHTOOLS_OPTS If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ config = {} - if 'autoneg' in opts: - if opts['autoneg'] in _CONFIG_TRUE: - config.update({'autoneg': 'on'}) - elif opts['autoneg'] in _CONFIG_FALSE: - config.update({'autoneg': 'off'}) + if "autoneg" in opts: + if opts["autoneg"] in _CONFIG_TRUE: + config.update({"autoneg": "on"}) + elif opts["autoneg"] in _CONFIG_FALSE: + config.update({"autoneg": "off"}) else: - _raise_error_iface(iface, 'autoneg', _CONFIG_TRUE + _CONFIG_FALSE) + _raise_error_iface(iface, "autoneg", _CONFIG_TRUE + _CONFIG_FALSE) - if 'duplex' in opts: - valid = ['full', 'half'] - if opts['duplex'] in valid: - config.update({'duplex': opts['duplex']}) + if "duplex" in opts: + valid = ["full", "half"] + if opts["duplex"] in valid: + config.update({"duplex": opts["duplex"]}) else: - _raise_error_iface(iface, 'duplex', valid) + _raise_error_iface(iface, "duplex", valid) - if 'speed' in opts: - valid = ['10', '100', '1000', '10000'] - if six.text_type(opts['speed']) in valid: - config.update({'speed': opts['speed']}) + if "speed" in opts: + valid = ["10", "100", "1000", "10000"] + if six.text_type(opts["speed"]) in valid: + config.update({"speed": opts["speed"]}) else: - _raise_error_iface(iface, opts['speed'], valid) + _raise_error_iface(iface, opts["speed"], valid) valid = _CONFIG_TRUE + _CONFIG_FALSE - for option in ('rx', 'tx', 'sg', 'tso', 'ufo', 'gso', 'gro', 'lro'): + for option in ("rx", "tx", "sg", "tso", "ufo", "gso", "gro", "lro"): if option in opts: if opts[option] in _CONFIG_TRUE: - config.update({option: 'on'}) + config.update({option: "on"}) elif opts[option] in _CONFIG_FALSE: - config.update({option: 'off'}) + config.update({option: "off"}) else: _raise_error_iface(iface, option, valid) @@ -733,28 +753,36 @@ def _parse_ethtool_opts(opts, iface): def _parse_ethtool_pppoe_opts(opts, iface): - ''' + """ Filters given options and outputs valid settings for ETHTOOLS_PPPOE_OPTS If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ config = {} for opt in _DEB_CONFIG_PPPOE_OPTS: if opt in opts: config[opt] = opts[opt] - if 'provider' in opts and not opts['provider']: - _raise_error_iface(iface, 'provider', _CONFIG_TRUE + _CONFIG_FALSE) + if "provider" in opts and not opts["provider"]: + _raise_error_iface(iface, "provider", _CONFIG_TRUE + _CONFIG_FALSE) valid = _CONFIG_TRUE + _CONFIG_FALSE - for option in ('noipdefault', 'usepeerdns', 'defaultroute', 'hide-password', 'noauth', 'persist', 'noaccomp'): + for option in ( + "noipdefault", + "usepeerdns", + "defaultroute", + "hide-password", + "noauth", + "persist", + "noaccomp", + ): if option in opts: if opts[option] in _CONFIG_TRUE: - config.update({option: 'True'}) + config.update({option: "True"}) elif opts[option] in _CONFIG_FALSE: - config.update({option: 'False'}) + config.update({option: "False"}) else: _raise_error_iface(iface, option, valid) @@ -762,278 +790,273 @@ def _parse_ethtool_pppoe_opts(opts, iface): def _parse_settings_bond(opts, iface): - ''' + """ Filters given options and outputs valid settings for requested operation. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ bond_def = { # 803.ad aggregation selection logic # 0 for stable (default) # 1 for bandwidth # 2 for count - 'ad_select': '0', + "ad_select": "0", # Max number of transmit queues (default = 16) - 'tx_queues': '16', + "tx_queues": "16", # Link monitoring in milliseconds. Most NICs support this - 'miimon': '100', + "miimon": "100", # ARP interval in milliseconds - 'arp_interval': '250', + "arp_interval": "250", # Delay before considering link down in milliseconds (miimon * 2) - 'downdelay': '200', + "downdelay": "200", # lacp_rate 0: Slow - every 30 seconds # lacp_rate 1: Fast - every 1 second - 'lacp_rate': '0', + "lacp_rate": "0", # Max bonds for this driver - 'max_bonds': '1', + "max_bonds": "1", # Specifies the time, in milliseconds, to wait before # enabling a slave after a link recovery has been # detected. Only used with miimon. - 'updelay': '0', + "updelay": "0", # Used with miimon. # On: driver sends mii # Off: ethtool sends mii - 'use_carrier': 'on', + "use_carrier": "on", # Default. Don't change unless you know what you are doing. - 'xmit_hash_policy': 'layer2', + "xmit_hash_policy": "layer2", } - if opts['mode'] in ['balance-rr', '0']: - log.info( - 'Device: {0} Bonding Mode: load balancing (round-robin)'.format( - iface - ) - ) + if opts["mode"] in ["balance-rr", "0"]: + log.info("Device: {0} Bonding Mode: load balancing (round-robin)".format(iface)) return _parse_settings_bond_0(opts, iface, bond_def) - elif opts['mode'] in ['active-backup', '1']: + elif opts["mode"] in ["active-backup", "1"]: log.info( - 'Device: {0} Bonding Mode: fault-tolerance (active-backup)'.format( - iface - ) + "Device: {0} Bonding Mode: fault-tolerance (active-backup)".format(iface) ) return _parse_settings_bond_1(opts, iface, bond_def) - elif opts['mode'] in ['balance-xor', '2']: - log.info( - 'Device: {0} Bonding Mode: load balancing (xor)'.format(iface) - ) + elif opts["mode"] in ["balance-xor", "2"]: + log.info("Device: {0} Bonding Mode: load balancing (xor)".format(iface)) return _parse_settings_bond_2(opts, iface, bond_def) - elif opts['mode'] in ['broadcast', '3']: - log.info( - 'Device: {0} Bonding Mode: fault-tolerance (broadcast)'.format( - iface - ) - ) + elif opts["mode"] in ["broadcast", "3"]: + log.info("Device: {0} Bonding Mode: fault-tolerance (broadcast)".format(iface)) return _parse_settings_bond_3(opts, iface, bond_def) - elif opts['mode'] in ['802.3ad', '4']: + elif opts["mode"] in ["802.3ad", "4"]: log.info( - 'Device: {0} Bonding Mode: IEEE 802.3ad Dynamic link ' - 'aggregation'.format(iface) + "Device: {0} Bonding Mode: IEEE 802.3ad Dynamic link " + "aggregation".format(iface) ) return _parse_settings_bond_4(opts, iface, bond_def) - elif opts['mode'] in ['balance-tlb', '5']: - log.info( - 'Device: {0} Bonding Mode: transmit load balancing'.format(iface) - ) + elif opts["mode"] in ["balance-tlb", "5"]: + log.info("Device: {0} Bonding Mode: transmit load balancing".format(iface)) return _parse_settings_bond_5(opts, iface, bond_def) - elif opts['mode'] in ['balance-alb', '6']: - log.info( - 'Device: {0} Bonding Mode: adaptive load balancing'.format(iface) - ) + elif opts["mode"] in ["balance-alb", "6"]: + log.info("Device: {0} Bonding Mode: adaptive load balancing".format(iface)) return _parse_settings_bond_6(opts, iface, bond_def) else: valid = [ - '0', '1', '2', '3', '4', '5', '6', - 'balance-rr', 'active-backup', 'balance-xor', - 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb' + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "balance-rr", + "active-backup", + "balance-xor", + "broadcast", + "802.3ad", + "balance-tlb", + "balance-alb", ] - _raise_error_iface(iface, 'mode', valid) + _raise_error_iface(iface, "mode", valid) def _parse_settings_bond_0(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond0. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' - bond = {'mode': '0'} + """ + bond = {"mode": "0"} # ARP targets in n.n.n.n form - valid = ['list of ips (up to 16)'] - if 'arp_ip_target' in opts: - if isinstance(opts['arp_ip_target'], list): - if 1 <= len(opts['arp_ip_target']) <= 16: - bond.update({'arp_ip_target': ''}) - for ip in opts['arp_ip_target']: # pylint: disable=C0103 - if len(bond['arp_ip_target']) > 0: - bond['arp_ip_target'] = bond['arp_ip_target'] + ',' + ip + valid = ["list of ips (up to 16)"] + if "arp_ip_target" in opts: + if isinstance(opts["arp_ip_target"], list): + if 1 <= len(opts["arp_ip_target"]) <= 16: + bond.update({"arp_ip_target": ""}) + for ip in opts["arp_ip_target"]: # pylint: disable=C0103 + if len(bond["arp_ip_target"]) > 0: + bond["arp_ip_target"] = bond["arp_ip_target"] + "," + ip else: - bond['arp_ip_target'] = ip + bond["arp_ip_target"] = ip else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) - if 'arp_interval' in opts: + if "arp_interval" in opts: try: - int(opts['arp_interval']) - bond.update({'arp_interval': opts['arp_interval']}) + int(opts["arp_interval"]) + bond.update({"arp_interval": opts["arp_interval"]}) except ValueError: - _raise_error_iface(iface, 'arp_interval', ['integer']) + _raise_error_iface(iface, "arp_interval", ["integer"]) else: - _log_default_iface(iface, 'arp_interval', bond_def['arp_interval']) - bond.update({'arp_interval': bond_def['arp_interval']}) + _log_default_iface(iface, "arp_interval", bond_def["arp_interval"]) + bond.update({"arp_interval": bond_def["arp_interval"]}) return bond def _parse_settings_bond_1(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond1. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' - bond = {'mode': '1'} + """ + bond = {"mode": "1"} - for binding in ['miimon', 'downdelay', 'updelay']: + for binding in ["miimon", "downdelay", "updelay"]: if binding in opts: try: int(opts[binding]) bond.update({binding: opts[binding]}) except ValueError: - _raise_error_iface(iface, binding, ['integer']) + _raise_error_iface(iface, binding, ["integer"]) else: _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'primary' in opts: - bond.update({'primary': opts['primary']}) + if "primary" in opts: + bond.update({"primary": opts["primary"]}) - if not (__grains__['os'] == "Ubuntu" and __grains__['osrelease_info'][0] >= 16): - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if not (__grains__["os"] == "Ubuntu" and __grains__["osrelease_info"][0] >= 16): + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) return bond def _parse_settings_bond_2(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond2. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ - bond = {'mode': '2'} + bond = {"mode": "2"} - valid = ['list of ips (up to 16)'] - if 'arp_ip_target' in opts: - if isinstance(opts['arp_ip_target'], list): - if 1 <= len(opts['arp_ip_target']) <= 16: - bond.update({'arp_ip_target': ''}) - for ip in opts['arp_ip_target']: # pylint: disable=C0103 - if len(bond['arp_ip_target']) > 0: - bond['arp_ip_target'] = bond['arp_ip_target'] + ',' + ip + valid = ["list of ips (up to 16)"] + if "arp_ip_target" in opts: + if isinstance(opts["arp_ip_target"], list): + if 1 <= len(opts["arp_ip_target"]) <= 16: + bond.update({"arp_ip_target": ""}) + for ip in opts["arp_ip_target"]: # pylint: disable=C0103 + if len(bond["arp_ip_target"]) > 0: + bond["arp_ip_target"] = bond["arp_ip_target"] + "," + ip else: - bond['arp_ip_target'] = ip + bond["arp_ip_target"] = ip else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) - if 'arp_interval' in opts: + if "arp_interval" in opts: try: - int(opts['arp_interval']) - bond.update({'arp_interval': opts['arp_interval']}) + int(opts["arp_interval"]) + bond.update({"arp_interval": opts["arp_interval"]}) except ValueError: - _raise_error_iface(iface, 'arp_interval', ['integer']) + _raise_error_iface(iface, "arp_interval", ["integer"]) else: - _log_default_iface(iface, 'arp_interval', bond_def['arp_interval']) - bond.update({'arp_interval': bond_def['arp_interval']}) + _log_default_iface(iface, "arp_interval", bond_def["arp_interval"]) + bond.update({"arp_interval": bond_def["arp_interval"]}) - if 'hashing-algorithm' in opts: - valid = ['layer2', 'layer2+3', 'layer3+4'] - if opts['hashing-algorithm'] in valid: - bond.update({'xmit_hash_policy': opts['hashing-algorithm']}) + if "hashing-algorithm" in opts: + valid = ["layer2", "layer2+3", "layer3+4"] + if opts["hashing-algorithm"] in valid: + bond.update({"xmit_hash_policy": opts["hashing-algorithm"]}) else: - _raise_error_iface(iface, 'hashing-algorithm', valid) + _raise_error_iface(iface, "hashing-algorithm", valid) return bond def _parse_settings_bond_3(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond3. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' - bond = {'mode': '3'} + """ + bond = {"mode": "3"} - for binding in ['miimon', 'downdelay', 'updelay']: + for binding in ["miimon", "downdelay", "updelay"]: if binding in opts: try: int(opts[binding]) bond.update({binding: opts[binding]}) except ValueError: - _raise_error_iface(iface, binding, ['integer']) + _raise_error_iface(iface, binding, ["integer"]) else: _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) return bond def _parse_settings_bond_4(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond4. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ - bond = {'mode': '4'} + bond = {"mode": "4"} - for binding in ['miimon', 'downdelay', 'updelay', 'lacp_rate', 'ad_select']: + for binding in ["miimon", "downdelay", "updelay", "lacp_rate", "ad_select"]: if binding in opts: - if binding == 'lacp_rate': - if opts[binding] == 'fast': - opts.update({binding: '1'}) - if opts[binding] == 'slow': - opts.update({binding: '0'}) - valid = ['fast', '1', 'slow', '0'] + if binding == "lacp_rate": + if opts[binding] == "fast": + opts.update({binding: "1"}) + if opts[binding] == "slow": + opts.update({binding: "0"}) + valid = ["fast", "1", "slow", "0"] else: - valid = ['integer'] + valid = ["integer"] try: int(opts[binding]) bond.update({binding: opts[binding]}) @@ -1043,271 +1066,271 @@ def _parse_settings_bond_4(opts, iface, bond_def): _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) - if 'hashing-algorithm' in opts: - valid = ['layer2', 'layer2+3', 'layer3+4'] - if opts['hashing-algorithm'] in valid: - bond.update({'xmit_hash_policy': opts['hashing-algorithm']}) + if "hashing-algorithm" in opts: + valid = ["layer2", "layer2+3", "layer3+4"] + if opts["hashing-algorithm"] in valid: + bond.update({"xmit_hash_policy": opts["hashing-algorithm"]}) else: - _raise_error_iface(iface, 'hashing-algorithm', valid) + _raise_error_iface(iface, "hashing-algorithm", valid) return bond def _parse_settings_bond_5(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond5. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' - bond = {'mode': '5'} + """ + bond = {"mode": "5"} - for binding in ['miimon', 'downdelay', 'updelay']: + for binding in ["miimon", "downdelay", "updelay"]: if binding in opts: try: int(opts[binding]) bond.update({binding: opts[binding]}) except ValueError: - _raise_error_iface(iface, binding, ['integer']) + _raise_error_iface(iface, binding, ["integer"]) else: _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) - if 'primary' in opts: - bond.update({'primary': opts['primary']}) + if "primary" in opts: + bond.update({"primary": opts["primary"]}) return bond def _parse_settings_bond_6(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond6. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' - bond = {'mode': '6'} + """ + bond = {"mode": "6"} - for binding in ['miimon', 'downdelay', 'updelay']: + for binding in ["miimon", "downdelay", "updelay"]: if binding in opts: try: int(opts[binding]) bond.update({binding: opts[binding]}) except ValueError: - _raise_error_iface(iface, binding, ['integer']) + _raise_error_iface(iface, binding, ["integer"]) else: _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) - if 'primary' in opts: - bond.update({'primary': opts['primary']}) + if "primary" in opts: + bond.update({"primary": opts["primary"]}) return bond def _parse_bridge_opts(opts, iface): - ''' + """ Filters given options and outputs valid settings for BRIDGING_OPTS If an option has a value that is not expected, this function will log the Interface, Setting and what was expected. - ''' + """ config = {} - if 'ports' in opts: - if isinstance(opts['ports'], list): - opts['ports'] = ' '.join(opts['ports']) - config.update({'ports': opts['ports']}) + if "ports" in opts: + if isinstance(opts["ports"], list): + opts["ports"] = " ".join(opts["ports"]) + config.update({"ports": opts["ports"]}) - for opt in ['ageing', 'fd', 'gcint', 'hello', 'maxage']: + for opt in ["ageing", "fd", "gcint", "hello", "maxage"]: if opt in opts: try: float(opts[opt]) config.update({opt: opts[opt]}) except ValueError: - _raise_error_iface(iface, opt, ['float']) + _raise_error_iface(iface, opt, ["float"]) - for opt in ['bridgeprio', 'maxwait']: + for opt in ["bridgeprio", "maxwait"]: if opt in opts: if isinstance(opts[opt], int): config.update({opt: opts[opt]}) else: - _raise_error_iface(iface, opt, ['integer']) + _raise_error_iface(iface, opt, ["integer"]) - if 'hw' in opts: + if "hw" in opts: # match 12 hex digits with either : or - as separators between pairs - if re.match('[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$', - opts['hw'].lower()): - config.update({'hw': opts['hw']}) + if re.match( + "[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", opts["hw"].lower() + ): + config.update({"hw": opts["hw"]}) else: - _raise_error_iface(iface, 'hw', ['valid MAC address']) + _raise_error_iface(iface, "hw", ["valid MAC address"]) - for opt in ['pathcost', 'portprio']: + for opt in ["pathcost", "portprio"]: if opt in opts: try: port, cost_or_prio = opts[opt].split() int(cost_or_prio) - config.update({opt: '{0} {1}'.format(port, cost_or_prio)}) + config.update({opt: "{0} {1}".format(port, cost_or_prio)}) except ValueError: - _raise_error_iface(iface, opt, ['interface integer']) + _raise_error_iface(iface, opt, ["interface integer"]) - if 'stp' in opts: - if opts['stp'] in _CONFIG_TRUE: - config.update({'stp': 'on'}) - elif opts['stp'] in _CONFIG_FALSE: - config.update({'stp': 'off'}) + if "stp" in opts: + if opts["stp"] in _CONFIG_TRUE: + config.update({"stp": "on"}) + elif opts["stp"] in _CONFIG_FALSE: + config.update({"stp": "off"}) else: - _raise_error_iface(iface, 'stp', _CONFIG_TRUE + _CONFIG_FALSE) + _raise_error_iface(iface, "stp", _CONFIG_TRUE + _CONFIG_FALSE) - if 'waitport' in opts: - if isinstance(opts['waitport'], int): - config.update({'waitport': opts['waitport']}) + if "waitport" in opts: + if isinstance(opts["waitport"], int): + config.update({"waitport": opts["waitport"]}) else: - values = opts['waitport'].split() + values = opts["waitport"].split() waitport_time = values.pop(0) if waitport_time.isdigit() and values: - config.update({ - 'waitport': '{0} {1}'.format( - waitport_time, ' '.join(values) - ) - }) + config.update( + {"waitport": "{0} {1}".format(waitport_time, " ".join(values))} + ) else: - _raise_error_iface(iface, opt, ['integer [interfaces]']) + _raise_error_iface(iface, opt, ["integer [interfaces]"]) return config def _parse_settings_eth(opts, iface_type, enabled, iface): - ''' + """ Filters given options and outputs valid settings for a network interface. - ''' + """ adapters = salt.utils.odict.OrderedDict() adapters[iface] = salt.utils.odict.OrderedDict() - adapters[iface]['type'] = iface_type + adapters[iface]["type"] = iface_type - adapters[iface]['data'] = salt.utils.odict.OrderedDict() - iface_data = adapters[iface]['data'] - iface_data['inet'] = salt.utils.odict.OrderedDict() - iface_data['inet6'] = salt.utils.odict.OrderedDict() + adapters[iface]["data"] = salt.utils.odict.OrderedDict() + iface_data = adapters[iface]["data"] + iface_data["inet"] = salt.utils.odict.OrderedDict() + iface_data["inet6"] = salt.utils.odict.OrderedDict() if enabled: - adapters[iface]['enabled'] = True + adapters[iface]["enabled"] = True - if opts.get('hotplug', False): - adapters[iface]['hotplug'] = True + if opts.get("hotplug", False): + adapters[iface]["hotplug"] = True # Defaults assume IPv4 (inet) interfaces unless enable_ipv6=True - def_addrfam = 'inet' + def_addrfam = "inet" dual_stack = False # If enable_ipv6=True, then expet either IPv6-only or dual stack. - if 'enable_ipv6' in opts and opts['enable_ipv6']: - iface_data['inet6']['addrfam'] = 'inet6' - iface_data['inet6']['netmask'] = '64' # defaults to 64 - def_addrfam = 'inet6' + if "enable_ipv6" in opts and opts["enable_ipv6"]: + iface_data["inet6"]["addrfam"] = "inet6" + iface_data["inet6"]["netmask"] = "64" # defaults to 64 + def_addrfam = "inet6" - if 'iface_type' in opts and opts['iface_type'] == 'vlan': - iface_data['inet6']['vlan_raw_device'] = ( - re.sub(r'\.\d*', '', iface)) + if "iface_type" in opts and opts["iface_type"] == "vlan": + iface_data["inet6"]["vlan_raw_device"] = re.sub(r"\.\d*", "", iface) - if 'ipaddr' in opts and 'ipv6ipaddr' in opts: + if "ipaddr" in opts and "ipv6ipaddr" in opts: # If both 'ipaddr' and 'ipv6ipaddr' are present; expect dual stack - iface_data['inet']['addrfam'] = 'inet' - def_addrfam = 'inet' + iface_data["inet"]["addrfam"] = "inet" + def_addrfam = "inet" dual_stack = True else: # If enable_ipv6=False|None, IPv6 settings should not be set. - iface_data['inet']['addrfam'] = 'inet' + iface_data["inet"]["addrfam"] = "inet" - if iface_type not in ['bridge']: + if iface_type not in ["bridge"]: tmp_ethtool = _parse_ethtool_opts(opts, iface) if tmp_ethtool: ethtool = {} for item in tmp_ethtool: ethtool[_ETHTOOL_CONFIG_OPTS[item]] = tmp_ethtool[item] - iface_data[def_addrfam]['ethtool'] = ethtool + iface_data[def_addrfam]["ethtool"] = ethtool # return a list of sorted keys to ensure consistent order - iface_data[def_addrfam]['ethtool_keys'] = sorted(ethtool) + iface_data[def_addrfam]["ethtool_keys"] = sorted(ethtool) - if iface_type == 'bridge': + if iface_type == "bridge": bridging = _parse_bridge_opts(opts, iface) if bridging: - opts.pop('mode', None) - iface_data[def_addrfam]['bridging'] = bridging - iface_data[def_addrfam]['bridging_keys'] = sorted(bridging) - iface_data[def_addrfam]['addrfam'] = def_addrfam + opts.pop("mode", None) + iface_data[def_addrfam]["bridging"] = bridging + iface_data[def_addrfam]["bridging_keys"] = sorted(bridging) + iface_data[def_addrfam]["addrfam"] = def_addrfam - elif iface_type == 'bond': + elif iface_type == "bond": bonding = _parse_settings_bond(opts, iface) if bonding: - opts.pop('mode', None) - iface_data[def_addrfam]['bonding'] = bonding - iface_data[def_addrfam]['bonding']['slaves'] = opts['slaves'] - iface_data[def_addrfam]['bonding_keys'] = sorted(bonding) - iface_data[def_addrfam]['addrfam'] = def_addrfam + opts.pop("mode", None) + iface_data[def_addrfam]["bonding"] = bonding + iface_data[def_addrfam]["bonding"]["slaves"] = opts["slaves"] + iface_data[def_addrfam]["bonding_keys"] = sorted(bonding) + iface_data[def_addrfam]["addrfam"] = def_addrfam - elif iface_type == 'slave': - adapters[iface]['master'] = opts['master'] + elif iface_type == "slave": + adapters[iface]["master"] = opts["master"] - opts['proto'] = 'manual' - iface_data[def_addrfam]['master'] = adapters[iface]['master'] - iface_data[def_addrfam]['addrfam'] = def_addrfam + opts["proto"] = "manual" + iface_data[def_addrfam]["master"] = adapters[iface]["master"] + iface_data[def_addrfam]["addrfam"] = def_addrfam - elif iface_type == 'vlan': - iface_data[def_addrfam]['vlan_raw_device'] = re.sub(r'\.\d*', '', iface) - iface_data[def_addrfam]['addrfam'] = def_addrfam + elif iface_type == "vlan": + iface_data[def_addrfam]["vlan_raw_device"] = re.sub(r"\.\d*", "", iface) + iface_data[def_addrfam]["addrfam"] = def_addrfam - elif iface_type == 'pppoe': + elif iface_type == "pppoe": tmp_ethtool = _parse_ethtool_pppoe_opts(opts, iface) if tmp_ethtool: for item in tmp_ethtool: - adapters[iface]['data'][def_addrfam][_DEB_CONFIG_PPPOE_OPTS[item]] = tmp_ethtool[item] - iface_data[def_addrfam]['addrfam'] = def_addrfam + adapters[iface]["data"][def_addrfam][ + _DEB_CONFIG_PPPOE_OPTS[item] + ] = tmp_ethtool[item] + iface_data[def_addrfam]["addrfam"] = def_addrfam for opt in opts: # trim leading "ipv6" from option - if opt.startswith('ipv6'): + if opt.startswith("ipv6"): optname = opt[4:] # trim off the ipv6 v6only = True else: @@ -1319,70 +1342,80 @@ def _parse_settings_eth(opts, iface_type, enabled, iface): valuestr = opts[opt] # default to 'static' if proto is 'none' - if optname == 'proto' and valuestr == 'none': - valuestr = 'static' + if optname == "proto" and valuestr == "none": + valuestr = "static" # If option is v6-only, don't validate against inet and always set value if v6only: (valid, value, errmsg) = _validate_interface_option( - _optname, valuestr, addrfam='inet6') + _optname, valuestr, addrfam="inet6" + ) if not valid: - _raise_error_iface(iface, '\'{0}\' \'{1}\''.format(opt, valuestr), [errmsg]) + _raise_error_iface( + iface, "'{0}' '{1}'".format(opt, valuestr), [errmsg] + ) # replace dashes with underscores for jinja - _optname = _optname.replace('-', '_') - iface_data['inet6'][_optname] = value + _optname = _optname.replace("-", "_") + iface_data["inet6"][_optname] = value # Else, if it's a dual stack, the option may belong in both; apply v4 opt as v6 default elif dual_stack: valid_once = False errmsg = None - for addrfam in ['inet', 'inet6']: + for addrfam in ["inet", "inet6"]: (valid, value, errmsg) = _validate_interface_option( - _optname, valuestr, addrfam=addrfam) + _optname, valuestr, addrfam=addrfam + ) if valid: valid_once = True # replace dashes with underscores for jinja - _optname = _optname.replace('-', '_') + _optname = _optname.replace("-", "_") # if a v6-only version of this option was set; don't override # otherwise, if dual stack, use the v4 version as a default value for v6 # allows overriding with =None - if addrfam == 'inet' or _optname not in iface_data['inet6']: + if addrfam == "inet" or _optname not in iface_data["inet6"]: iface_data[addrfam][_optname] = value if not valid_once: _raise_error_iface( - iface, - '\'{0}\' \'{1}\''.format(opt, valuestr), - [errmsg] + iface, "'{0}' '{1}'".format(opt, valuestr), [errmsg] ) # Else, it goes in the default(only) addrfam # Not assuming v4 allows a v6 block to be created without lots of "ipv6" prefixes else: (valid, value, errmsg) = _validate_interface_option( - _optname, valuestr, addrfam=def_addrfam) + _optname, valuestr, addrfam=def_addrfam + ) if not valid: _raise_error_iface( - iface, - '\'{0}\' \'{1}\''.format(opt, valuestr), - [errmsg] + iface, "'{0}' '{1}'".format(opt, valuestr), [errmsg] ) # replace dashes with underscores for jinja - _optname = _optname.replace('-', '_') + _optname = _optname.replace("-", "_") iface_data[def_addrfam][_optname] = value - for opt in ['up_cmds', 'pre_up_cmds', 'post_up_cmds', - 'down_cmds', 'pre_down_cmds', 'post_down_cmds']: + for opt in [ + "up_cmds", + "pre_up_cmds", + "post_up_cmds", + "down_cmds", + "pre_down_cmds", + "post_down_cmds", + ]: if opt in opts: iface_data[def_addrfam][opt] = opts[opt] - for addrfam in ['inet', 'inet6']: - if 'addrfam' in iface_data[addrfam] and iface_data[addrfam]['addrfam'] == addrfam: + for addrfam in ["inet", "inet6"]: + if ( + "addrfam" in iface_data[addrfam] + and iface_data[addrfam]["addrfam"] == addrfam + ): pass else: iface_data.pop(addrfam) @@ -1391,78 +1424,78 @@ def _parse_settings_eth(opts, iface_type, enabled, iface): def _parse_settings_source(opts, iface_type, enabled, iface): - ''' + """ Filters given options and outputs valid settings for a network interface. - ''' + """ adapters = salt.utils.odict.OrderedDict() adapters[iface] = salt.utils.odict.OrderedDict() - adapters[iface]['type'] = iface_type + adapters[iface]["type"] = iface_type - adapters[iface]['data'] = salt.utils.odict.OrderedDict() - iface_data = adapters[iface]['data'] - iface_data['sources'] = [opts['source']] + adapters[iface]["data"] = salt.utils.odict.OrderedDict() + iface_data = adapters[iface]["data"] + iface_data["sources"] = [opts["source"]] return adapters def _parse_network_settings(opts, current): - ''' + """ Filters given options and outputs valid settings for the global network settings file. - ''' + """ # Normalize keys opts = dict((k.lower(), v) for (k, v) in six.iteritems(opts)) current = dict((k.lower(), v) for (k, v) in six.iteritems(current)) result = {} valid = _CONFIG_TRUE + _CONFIG_FALSE - if 'enabled' not in opts: + if "enabled" not in opts: try: - opts['networking'] = current['networking'] - _log_default_network('networking', current['networking']) + opts["networking"] = current["networking"] + _log_default_network("networking", current["networking"]) except ValueError: - _raise_error_network('networking', valid) + _raise_error_network("networking", valid) else: - opts['networking'] = opts['enabled'] + opts["networking"] = opts["enabled"] - if opts['networking'] in valid: - if opts['networking'] in _CONFIG_TRUE: - result['networking'] = 'yes' - elif opts['networking'] in _CONFIG_FALSE: - result['networking'] = 'no' + if opts["networking"] in valid: + if opts["networking"] in _CONFIG_TRUE: + result["networking"] = "yes" + elif opts["networking"] in _CONFIG_FALSE: + result["networking"] = "no" else: - _raise_error_network('networking', valid) + _raise_error_network("networking", valid) - if 'hostname' not in opts: + if "hostname" not in opts: try: - opts['hostname'] = current['hostname'] - _log_default_network('hostname', current['hostname']) + opts["hostname"] = current["hostname"] + _log_default_network("hostname", current["hostname"]) except ValueError: - _raise_error_network('hostname', ['server1.example.com']) + _raise_error_network("hostname", ["server1.example.com"]) - if opts['hostname']: - result['hostname'] = opts['hostname'] + if opts["hostname"]: + result["hostname"] = opts["hostname"] else: - _raise_error_network('hostname', ['server1.example.com']) + _raise_error_network("hostname", ["server1.example.com"]) - if 'search' in opts: - result['search'] = opts['search'] + if "search" in opts: + result["search"] = opts["search"] return result def _parse_routes(iface, opts): - ''' + """ Filters given options and outputs valid settings for the route settings file. - ''' + """ # Normalize keys opts = dict((k.lower(), v) for (k, v) in six.iteritems(opts)) result = {} - if 'routes' not in opts: - _raise_error_routes(iface, 'routes', 'List of routes') + if "routes" not in opts: + _raise_error_routes(iface, "routes", "List of routes") for opt in opts: result[opt] = opts[opt] @@ -1471,24 +1504,24 @@ def _parse_routes(iface, opts): def _write_file(iface, data, folder, pattern): - ''' + """ Writes a file to disk - ''' + """ filename = os.path.join(folder, pattern.format(iface)) if not os.path.exists(folder): - msg = '{0} cannot be written. {1} does not exist' + msg = "{0} cannot be written. {1} does not exist" msg = msg.format(filename, folder) log.error(msg) raise AttributeError(msg) - with salt.utils.files.flopen(filename, 'w') as fout: + with salt.utils.files.flopen(filename, "w") as fout: fout.write(salt.utils.stringutils.to_str(data)) return filename def _write_file_routes(iface, data, folder, pattern): - ''' + """ Writes a file to disk - ''' + """ # ifup / ifdown is executing given folder via run-parts. # according to run-parts man-page, only filenames with this pattern are # executed: (^[a-zA-Z0-9_-]+$) @@ -1497,41 +1530,41 @@ def _write_file_routes(iface, data, folder, pattern): # (default would have been in example /etc/network/if-up.d/route-bond0.12) # these dots in the iface name need to be replaced by underscores, so it # can be executed by run-parts - iface = iface.replace('.', '_') + iface = iface.replace(".", "_") filename = os.path.join(folder, pattern.format(iface)) if not os.path.exists(folder): - msg = '{0} cannot be written. {1} does not exist' + msg = "{0} cannot be written. {1} does not exist" msg = msg.format(filename, folder) log.error(msg) raise AttributeError(msg) - with salt.utils.files.flopen(filename, 'w') as fout: + with salt.utils.files.flopen(filename, "w") as fout: fout.write(salt.utils.stringutils.to_str(data)) - __salt__['file.set_mode'](filename, '0755') + __salt__["file.set_mode"](filename, "0755") return filename def _write_file_network(data, filename, create=False): - ''' + """ Writes a file to disk If file does not exist, only create if create argument is True - ''' + """ if not os.path.exists(filename) and not create: - msg = '{0} cannot be written. {0} does not exist\ - and create is set to False' + msg = "{0} cannot be written. {0} does not exist\ + and create is set to False" msg = msg.format(filename) log.error(msg) raise AttributeError(msg) - with salt.utils.files.flopen(filename, 'w') as fout: + with salt.utils.files.flopen(filename, "w") as fout: fout.write(salt.utils.stringutils.to_str(data)) def _read_temp(data): - ''' + """ Return what would be written to disk - ''' + """ tout = StringIO() tout.write(data) tout.seek(0) @@ -1542,98 +1575,98 @@ def _read_temp(data): def _read_temp_ifaces(iface, data): - ''' + """ Return what would be written to disk for interfaces - ''' + """ try: - template = JINJA.get_template('debian_eth.jinja') + template = JINJA.get_template("debian_eth.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template debian_eth.jinja') - return '' + log.error("Could not load template debian_eth.jinja") + return "" - ifcfg = template.render({'name': iface, 'data': data}) + ifcfg = template.render({"name": iface, "data": data}) # Return as an array so the difflib works - return [item + '\n' for item in ifcfg.split('\n')] + return [item + "\n" for item in ifcfg.split("\n")] def _write_file_ifaces(iface, data, **settings): - ''' + """ Writes a file to disk - ''' + """ try: - eth_template = JINJA.get_template('debian_eth.jinja') - source_template = JINJA.get_template('debian_source.jinja') + eth_template = JINJA.get_template("debian_eth.jinja") + source_template = JINJA.get_template("debian_source.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template debian_eth.jinja') - return '' + log.error("Could not load template debian_eth.jinja") + return "" # Read /etc/network/interfaces into a dict adapters = _parse_interfaces() # Apply supplied settings over on-disk settings adapters[iface] = data - ifcfg = '' + ifcfg = "" for adapter in adapters: - if 'type' in adapters[adapter] and adapters[adapter]['type'] == 'source': - tmp = source_template.render({'name': adapter, 'data': adapters[adapter]}) + if "type" in adapters[adapter] and adapters[adapter]["type"] == "source": + tmp = source_template.render({"name": adapter, "data": adapters[adapter]}) else: - tmp = eth_template.render({'name': adapter, 'data': adapters[adapter]}) + tmp = eth_template.render({"name": adapter, "data": adapters[adapter]}) ifcfg = ifcfg + tmp if adapter == iface: saved_ifcfg = tmp _SEPARATE_FILE = False - if 'filename' in settings: - if not settings['filename'].startswith('/'): - filename = '{0}/{1}'.format(_DEB_NETWORK_DIR, settings['filename']) + if "filename" in settings: + if not settings["filename"].startswith("/"): + filename = "{0}/{1}".format(_DEB_NETWORK_DIR, settings["filename"]) else: - filename = settings['filename'] + filename = settings["filename"] _SEPARATE_FILE = True else: - if 'filename' in adapters[adapter]['data']: - filename = adapters[adapter]['data'] + if "filename" in adapters[adapter]["data"]: + filename = adapters[adapter]["data"] else: filename = _DEB_NETWORK_FILE if not os.path.exists(os.path.dirname(filename)): - msg = '{0} cannot be written.' + msg = "{0} cannot be written." msg = msg.format(os.path.dirname(filename)) log.error(msg) raise AttributeError(msg) - with salt.utils.files.flopen(filename, 'w') as fout: + with salt.utils.files.flopen(filename, "w") as fout: if _SEPARATE_FILE: fout.write(salt.utils.stringutils.to_str(saved_ifcfg)) else: fout.write(salt.utils.stringutils.to_str(ifcfg)) # Return as an array so the difflib works - return saved_ifcfg.split('\n') + return saved_ifcfg.split("\n") def _write_file_ppp_ifaces(iface, data): - ''' + """ Writes a file to disk - ''' + """ try: - template = JINJA.get_template('debian_ppp_eth.jinja') + template = JINJA.get_template("debian_ppp_eth.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template debian_ppp_eth.jinja') - return '' + log.error("Could not load template debian_ppp_eth.jinja") + return "" adapters = _parse_interfaces() adapters[iface] = data - ifcfg = '' - tmp = template.render({'data': adapters[iface]}) + ifcfg = "" + tmp = template.render({"data": adapters[iface]}) ifcfg = tmp + ifcfg - filename = _DEB_PPP_DIR + '/' + adapters[iface]['data']['inet']['provider'] + filename = _DEB_PPP_DIR + "/" + adapters[iface]["data"]["inet"]["provider"] if not os.path.exists(os.path.dirname(filename)): - msg = '{0} cannot be written.' + msg = "{0} cannot be written." msg = msg.format(os.path.dirname(filename)) log.error(msg) raise AttributeError(msg) - with salt.utils.files.fopen(filename, 'w') as fout: + with salt.utils.files.fopen(filename, "w") as fout: fout.write(salt.utils.stringutils.to_str(ifcfg)) # Return as an array so the difflib works @@ -1641,7 +1674,7 @@ def _write_file_ppp_ifaces(iface, data): def build_bond(iface, **settings): - ''' + """ Create a bond script in /etc/modprobe.d with the passed settings and load the bonding kernel module. @@ -1650,40 +1683,45 @@ def build_bond(iface, **settings): .. code-block:: bash salt '*' ip.build_bond bond0 mode=balance-alb - ''' - deb_major = __grains__['osrelease'][:1] + """ + deb_major = __grains__["osrelease"][:1] opts = _parse_settings_bond(settings, iface) try: - template = JINJA.get_template('conf.jinja') + template = JINJA.get_template("conf.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template conf.jinja') - return '' - data = template.render({'name': iface, 'bonding': opts}) + log.error("Could not load template conf.jinja") + return "" + data = template.render({"name": iface, "bonding": opts}) - if 'test' in settings and settings['test']: + if "test" in settings and settings["test"]: return _read_temp(data) - _write_file(iface, data, _DEB_NETWORK_CONF_FILES, '{0}.conf'.format(iface)) - path = os.path.join(_DEB_NETWORK_CONF_FILES, '{0}.conf'.format(iface)) - if deb_major == '5': - for line_type in ('alias', 'options'): - cmd = ['sed', '-i', '-e', r'/^{0}\s{1}.*/d'.format(line_type, iface), - '/etc/modprobe.conf'] - __salt__['cmd.run'](cmd, python_shell=False) - __salt__['file.append']('/etc/modprobe.conf', path) + _write_file(iface, data, _DEB_NETWORK_CONF_FILES, "{0}.conf".format(iface)) + path = os.path.join(_DEB_NETWORK_CONF_FILES, "{0}.conf".format(iface)) + if deb_major == "5": + for line_type in ("alias", "options"): + cmd = [ + "sed", + "-i", + "-e", + r"/^{0}\s{1}.*/d".format(line_type, iface), + "/etc/modprobe.conf", + ] + __salt__["cmd.run"](cmd, python_shell=False) + __salt__["file.append"]("/etc/modprobe.conf", path) # Load kernel module - __salt__['kmod.load']('bonding') + __salt__["kmod.load"]("bonding") # install ifenslave-2.6 - __salt__['pkg.install']('ifenslave-2.6') + __salt__["pkg.install"]("ifenslave-2.6") return _read_file(path) def build_interface(iface, iface_type, enabled, **settings): - ''' + """ Build an interface script for a network interface. CLI Example: @@ -1691,68 +1729,68 @@ def build_interface(iface, iface_type, enabled, **settings): .. code-block:: bash salt '*' ip.build_interface eth0 eth - ''' + """ iface_type = iface_type.lower() if iface_type not in _IFACE_TYPES: _raise_error_iface(iface, iface_type, _IFACE_TYPES) - if 'proto' not in settings: - settings['proto'] = 'static' + if "proto" not in settings: + settings["proto"] = "static" - if iface_type == 'slave': - settings['slave'] = 'yes' - if 'master' not in settings: - msg = 'master is a required setting for slave interfaces' + if iface_type == "slave": + settings["slave"] = "yes" + if "master" not in settings: + msg = "master is a required setting for slave interfaces" log.error(msg) raise AttributeError(msg) - elif iface_type == 'vlan': - settings['vlan'] = 'yes' - __salt__['pkg.install']('vlan') + elif iface_type == "vlan": + settings["vlan"] = "yes" + __salt__["pkg.install"]("vlan") - elif iface_type == 'pppoe': - settings['pppoe'] = 'yes' - if not __salt__['pkg.version']('ppp'): - inst = __salt__['pkg.install']('ppp') + elif iface_type == "pppoe": + settings["pppoe"] = "yes" + if not __salt__["pkg.version"]("ppp"): + inst = __salt__["pkg.install"]("ppp") - elif iface_type == 'bond': - if 'slaves' not in settings: - msg = 'slaves is a required setting for bond interfaces' + elif iface_type == "bond": + if "slaves" not in settings: + msg = "slaves is a required setting for bond interfaces" log.error(msg) raise AttributeError(msg) - elif iface_type == 'bridge': - if 'ports' not in settings: + elif iface_type == "bridge": + if "ports" not in settings: msg = ( - 'ports is a required setting for bridge interfaces on Debian ' - 'or Ubuntu based systems' + "ports is a required setting for bridge interfaces on Debian " + "or Ubuntu based systems" ) log.error(msg) raise AttributeError(msg) - __salt__['pkg.install']('bridge-utils') + __salt__["pkg.install"]("bridge-utils") - if iface_type in ['eth', 'bond', 'bridge', 'slave', 'vlan', 'pppoe']: + if iface_type in ["eth", "bond", "bridge", "slave", "vlan", "pppoe"]: opts = _parse_settings_eth(settings, iface_type, enabled, iface) - if iface_type in ['source']: + if iface_type in ["source"]: opts = _parse_settings_source(settings, iface_type, enabled, iface) - if 'test' in settings and settings['test']: + if "test" in settings and settings["test"]: return _read_temp_ifaces(iface, opts[iface]) ifcfg = _write_file_ifaces(iface, opts[iface], **settings) - if iface_type == 'pppoe': + if iface_type == "pppoe": _write_file_ppp_ifaces(iface, opts[iface]) # ensure lines in list end with newline, so difflib works - return [item + '\n' for item in ifcfg] + return [item + "\n" for item in ifcfg] def build_routes(iface, **settings): - ''' + """ Add route scripts for a network interface using up commands. CLI Example: @@ -1760,43 +1798,35 @@ def build_routes(iface, **settings): .. code-block:: bash salt '*' ip.build_routes eth0 - ''' + """ opts = _parse_routes(iface, settings) try: - template = JINJA.get_template('route_eth.jinja') + template = JINJA.get_template("route_eth.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template route_eth.jinja') - return '' + log.error("Could not load template route_eth.jinja") + return "" - add_routecfg = template.render(route_type='add', - routes=opts['routes'], - iface=iface) + add_routecfg = template.render(route_type="add", routes=opts["routes"], iface=iface) - del_routecfg = template.render(route_type='del', - routes=opts['routes'], - iface=iface) + del_routecfg = template.render(route_type="del", routes=opts["routes"], iface=iface) - if 'test' in settings and settings['test']: + if "test" in settings and settings["test"]: return _read_temp(add_routecfg + del_routecfg) - filename = _write_file_routes(iface, - add_routecfg, - _DEB_NETWORK_UP_DIR, - 'route-{0}') + filename = _write_file_routes(iface, add_routecfg, _DEB_NETWORK_UP_DIR, "route-{0}") results = _read_file(filename) - filename = _write_file_routes(iface, - del_routecfg, - _DEB_NETWORK_DOWN_DIR, - 'route-{0}') + filename = _write_file_routes( + iface, del_routecfg, _DEB_NETWORK_DOWN_DIR, "route-{0}" + ) results += _read_file(filename) return results def down(iface, iface_type): - ''' + """ Shutdown a network interface CLI Example: @@ -1804,17 +1834,17 @@ def down(iface, iface_type): .. code-block:: bash salt '*' ip.down eth0 eth - ''' + """ # Slave devices are controlled by the master. # Source 'interfaces' aren't brought down. - if iface_type not in ['slave', 'source']: - cmd = ['ip', 'link', 'set', iface, 'down'] - return __salt__['cmd.run'](cmd, python_shell=False) + if iface_type not in ["slave", "source"]: + cmd = ["ip", "link", "set", iface, "down"] + return __salt__["cmd.run"](cmd, python_shell=False) return None def get_bond(iface): - ''' + """ Return the content of a bond script CLI Example: @@ -1822,13 +1852,13 @@ def get_bond(iface): .. code-block:: bash salt '*' ip.get_bond bond0 - ''' - path = os.path.join(_DEB_NETWORK_CONF_FILES, '{0}.conf'.format(iface)) + """ + path = os.path.join(_DEB_NETWORK_CONF_FILES, "{0}.conf".format(iface)) return _read_file(path) def get_interface(iface): - ''' + """ Return the contents of an interface script CLI Example: @@ -1836,29 +1866,29 @@ def get_interface(iface): .. code-block:: bash salt '*' ip.get_interface eth0 - ''' + """ adapters = _parse_interfaces() if iface in adapters: try: - if iface == 'source': - template = JINJA.get_template('debian_source.jinja') + if iface == "source": + template = JINJA.get_template("debian_source.jinja") else: - template = JINJA.get_template('debian_eth.jinja') + template = JINJA.get_template("debian_eth.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template debian_eth.jinja') - return '' + log.error("Could not load template debian_eth.jinja") + return "" - ifcfg = template.render({'name': iface, 'data': adapters[iface]}) + ifcfg = template.render({"name": iface, "data": adapters[iface]}) # ensure lines in list end with newline, so difflib works - return [item + '\n' for item in ifcfg.split('\n')] + return [item + "\n" for item in ifcfg.split("\n")] else: return [] def up(iface, iface_type): # pylint: disable=C0103 - ''' + """ Start up a network interface CLI Example: @@ -1866,17 +1896,17 @@ def up(iface, iface_type): # pylint: disable=C0103 .. code-block:: bash salt '*' ip.up eth0 eth - ''' + """ # Slave devices are controlled by the master. # Source 'interfaces' aren't brought up. - if iface_type not in ('slave', 'source'): - cmd = ['ip', 'link', 'set', iface, 'up'] - return __salt__['cmd.run'](cmd, python_shell=False) + if iface_type not in ("slave", "source"): + cmd = ["ip", "link", "set", iface, "up"] + return __salt__["cmd.run"](cmd, python_shell=False) return None def get_network_settings(): - ''' + """ Return the contents of the global network script. CLI Example: @@ -1884,44 +1914,45 @@ def get_network_settings(): .. code-block:: bash salt '*' ip.get_network_settings - ''' + """ skip_etc_default_networking = ( - __grains__['osfullname'] == 'Ubuntu' and - int(__grains__['osrelease'].split('.')[0]) >= 12) + __grains__["osfullname"] == "Ubuntu" + and int(__grains__["osrelease"].split(".")[0]) >= 12 + ) if skip_etc_default_networking: settings = {} - if __salt__['service.available']('networking'): - if __salt__['service.status']('networking'): - settings['networking'] = "yes" + if __salt__["service.available"]("networking"): + if __salt__["service.status"]("networking"): + settings["networking"] = "yes" else: - settings['networking'] = "no" + settings["networking"] = "no" else: - settings['networking'] = "no" + settings["networking"] = "no" hostname = _parse_hostname() domainname = _parse_domainname() searchdomain = _parse_searchdomain() - settings['hostname'] = hostname - settings['domainname'] = domainname - settings['searchdomain'] = searchdomain + settings["hostname"] = hostname + settings["domainname"] = domainname + settings["searchdomain"] = searchdomain else: settings = _parse_current_network_settings() try: - template = JINJA.get_template('display-network.jinja') + template = JINJA.get_template("display-network.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template display-network.jinja') - return '' + log.error("Could not load template display-network.jinja") + return "" network = template.render(settings) return _read_temp(network) def get_routes(iface): - ''' + """ Return the routes for the interface CLI Example: @@ -1929,19 +1960,19 @@ def get_routes(iface): .. code-block:: bash salt '*' ip.get_routes eth0 - ''' + """ - filename = os.path.join(_DEB_NETWORK_UP_DIR, 'route-{0}'.format(iface)) + filename = os.path.join(_DEB_NETWORK_UP_DIR, "route-{0}".format(iface)) results = _read_file(filename) - filename = os.path.join(_DEB_NETWORK_DOWN_DIR, 'route-{0}'.format(iface)) + filename = os.path.join(_DEB_NETWORK_DOWN_DIR, "route-{0}".format(iface)) results += _read_file(filename) return results def apply_network_settings(**settings): - ''' + """ Apply global network configuration. CLI Example: @@ -1949,41 +1980,41 @@ def apply_network_settings(**settings): .. code-block:: bash salt '*' ip.apply_network_settings - ''' - if 'require_reboot' not in settings: - settings['require_reboot'] = False + """ + if "require_reboot" not in settings: + settings["require_reboot"] = False - if 'apply_hostname' not in settings: - settings['apply_hostname'] = False + if "apply_hostname" not in settings: + settings["apply_hostname"] = False hostname_res = True - if settings['apply_hostname'] in _CONFIG_TRUE: - if 'hostname' in settings: - hostname_res = __salt__['network.mod_hostname'](settings['hostname']) + if settings["apply_hostname"] in _CONFIG_TRUE: + if "hostname" in settings: + hostname_res = __salt__["network.mod_hostname"](settings["hostname"]) else: log.warning( - 'The network state sls is trying to apply hostname ' - 'changes but no hostname is defined.' + "The network state sls is trying to apply hostname " + "changes but no hostname is defined." ) hostname_res = False res = True - if settings['require_reboot'] in _CONFIG_TRUE: + if settings["require_reboot"] in _CONFIG_TRUE: log.warning( - 'The network state sls is requiring a reboot of the system to ' - 'properly apply network configuration.' + "The network state sls is requiring a reboot of the system to " + "properly apply network configuration." ) res = True else: - stop = __salt__['service.stop']('networking') + stop = __salt__["service.stop"]("networking") time.sleep(2) - res = stop and __salt__['service.start']('networking') + res = stop and __salt__["service.start"]("networking") return hostname_res and res def build_network_settings(**settings): - ''' + """ Build the global network script. CLI Example: @@ -1991,7 +2022,7 @@ def build_network_settings(**settings): .. code-block:: bash salt '*' ip.build_network_settings - ''' + """ changes = [] # Read current configuration and store default values @@ -2004,103 +2035,104 @@ def build_network_settings(**settings): # beginning with the 12.04 release so we disable or enable # the networking related services on boot skip_etc_default_networking = ( - __grains__['osfullname'] == 'Ubuntu' and - int(__grains__['osrelease'].split('.')[0]) >= 12) + __grains__["osfullname"] == "Ubuntu" + and int(__grains__["osrelease"].split(".")[0]) >= 12 + ) if skip_etc_default_networking: - if opts['networking'] == 'yes': - service_cmd = 'service.enable' + if opts["networking"] == "yes": + service_cmd = "service.enable" else: - service_cmd = 'service.disable' + service_cmd = "service.disable" - if __salt__['service.available']('NetworkManager'): - __salt__[service_cmd]('NetworkManager') + if __salt__["service.available"]("NetworkManager"): + __salt__[service_cmd]("NetworkManager") - if __salt__['service.available']('networking'): - __salt__[service_cmd]('networking') + if __salt__["service.available"]("networking"): + __salt__[service_cmd]("networking") else: try: - template = JINJA.get_template('network.jinja') + template = JINJA.get_template("network.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template network.jinja') - return '' + log.error("Could not load template network.jinja") + return "" network = template.render(opts) - if 'test' in settings and settings['test']: + if "test" in settings and settings["test"]: return _read_temp(network) # Write settings _write_file_network(network, _DEB_NETWORKING_FILE, True) # Get hostname and domain from opts - sline = opts['hostname'].split('.', 1) - opts['hostname'] = sline[0] - current_domainname = current_network_settings['domainname'] - current_searchdomain = current_network_settings['searchdomain'] + sline = opts["hostname"].split(".", 1) + opts["hostname"] = sline[0] + current_domainname = current_network_settings["domainname"] + current_searchdomain = current_network_settings["searchdomain"] new_domain = False if len(sline) > 1: new_domainname = sline[1] if new_domainname != current_domainname: domainname = new_domainname - opts['domainname'] = new_domainname + opts["domainname"] = new_domainname new_domain = True else: domainname = current_domainname - opts['domainname'] = domainname + opts["domainname"] = domainname else: domainname = current_domainname - opts['domainname'] = domainname + opts["domainname"] = domainname new_search = False - if 'search' in opts: - new_searchdomain = opts['search'] + if "search" in opts: + new_searchdomain = opts["search"] if new_searchdomain != current_searchdomain: searchdomain = new_searchdomain - opts['searchdomain'] = new_searchdomain + opts["searchdomain"] = new_searchdomain new_search = True else: searchdomain = current_searchdomain - opts['searchdomain'] = searchdomain + opts["searchdomain"] = searchdomain else: searchdomain = current_searchdomain - opts['searchdomain'] = searchdomain + opts["searchdomain"] = searchdomain # If the domain changes, then we should write the resolv.conf file. if new_domain or new_search: # Look for existing domain line and update if necessary resolve = _parse_resolve() - domain_prog = re.compile(r'domain\s+') - search_prog = re.compile(r'search\s+') + domain_prog = re.compile(r"domain\s+") + search_prog = re.compile(r"search\s+") new_contents = [] for item in _read_file(_DEB_RESOLV_FILE): if domain_prog.match(item): - item = 'domain {0}'.format(domainname) + item = "domain {0}".format(domainname) elif search_prog.match(item): - item = 'search {0}'.format(searchdomain) + item = "search {0}".format(searchdomain) new_contents.append(item) # A domain line didn't exist so we'll add one in # with the new domainname - if 'domain' not in resolve: - new_contents.insert(0, 'domain {0}' . format(domainname)) + if "domain" not in resolve: + new_contents.insert(0, "domain {0}".format(domainname)) # A search line didn't exist so we'll add one in # with the new search domain - if 'search' not in resolve: - new_contents.insert('domain' in resolve, 'search {0}'.format(searchdomain)) + if "search" not in resolve: + new_contents.insert("domain" in resolve, "search {0}".format(searchdomain)) - new_resolv = '\n'.join(new_contents) + new_resolv = "\n".join(new_contents) # Write /etc/resolv.conf - if not ('test' in settings and settings['test']): + if not ("test" in settings and settings["test"]): _write_file_network(new_resolv, _DEB_RESOLV_FILE) # used for returning the results back try: - template = JINJA.get_template('display-network.jinja') + template = JINJA.get_template("display-network.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template display-network.jinja') - return '' + log.error("Could not load template display-network.jinja") + return "" network = template.render(opts) changes.extend(_read_temp(network)) diff --git a/salt/modules/debian_service.py b/salt/modules/debian_service.py index 109a752bdac..f14dda4f40e 100644 --- a/salt/modules/debian_service.py +++ b/salt/modules/debian_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Service support for Debian systems (uses update-rc.d and /sbin/service) .. important:: @@ -7,74 +7,83 @@ Service support for Debian systems (uses update-rc.d and /sbin/service) minion, and it is using a different module (or gives an error similar to *'service.start' is not available*), see :ref:`here `. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import fnmatch +import glob + # Import python libs import logging -import glob -import fnmatch import re -# Import 3rd-party libs -# pylint: disable=import-error -from salt.ext.six.moves import shlex_quote as _cmd_quote -# pylint: enable=import-error - # Import salt libs import salt.utils.systemd -__func_alias__ = { - 'reload_': 'reload' -} +# Import 3rd-party libs +# pylint: disable=import-error +from salt.ext.six.moves import shlex_quote as _cmd_quote + +# pylint: enable=import-error + + +__func_alias__ = {"reload_": "reload"} # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" log = logging.getLogger(__name__) -_DEFAULT_VER = '7.0.0' +_DEFAULT_VER = "7.0.0" def __virtual__(): - ''' + """ Only work on Debian and when systemd isn't running - ''' - if __grains__['os'] in ('Debian', 'Raspbian', 'Devuan', 'NILinuxRT') and not salt.utils.systemd.booted(__context__): + """ + if __grains__["os"] in ( + "Debian", + "Raspbian", + "Devuan", + "NILinuxRT", + ) and not salt.utils.systemd.booted(__context__): return __virtualname__ else: - return (False, 'The debian_service module could not be loaded: ' - 'unsupported OS family and/or systemd running.') + return ( + False, + "The debian_service module could not be loaded: " + "unsupported OS family and/or systemd running.", + ) def _service_cmd(*args): osmajor = _osrel()[0] - if osmajor < '6': - cmd = '/etc/init.d/{0} {1}'.format(args[0], ' '.join(args[1:])) + if osmajor < "6": + cmd = "/etc/init.d/{0} {1}".format(args[0], " ".join(args[1:])) else: - cmd = 'service {0} {1}'.format(args[0], ' '.join(args[1:])) + cmd = "service {0} {1}".format(args[0], " ".join(args[1:])) return cmd def _get_runlevel(): - ''' + """ returns the current runlevel - ''' - out = __salt__['cmd.run']('runlevel') + """ + out = __salt__["cmd.run"]("runlevel") # unknown can be returned while inside a container environment, since # this is due to a lack of init, it should be safe to assume runlevel # 2, which is Debian's default. If not, all service related states # will throw an out of range exception here which will cause # other functions to fail. - if 'unknown' in out: - return '2' + if "unknown" in out: + return "2" else: return out.split()[1] def get_enabled(): - ''' + """ Return a list of service that are enabled on boot CLI Example: @@ -82,17 +91,17 @@ def get_enabled(): .. code-block:: bash salt '*' service.get_enabled - ''' - prefix = '/etc/rc[S{0}].d/S'.format(_get_runlevel()) + """ + prefix = "/etc/rc[S{0}].d/S".format(_get_runlevel()) ret = set() - lines = glob.glob('{0}*'.format(prefix)) + lines = glob.glob("{0}*".format(prefix)) for line in lines: - ret.add(re.split(prefix + r'\d+', line)[1]) + ret.add(re.split(prefix + r"\d+", line)[1]) return sorted(ret) def get_disabled(): - ''' + """ Return a set of services that are installed but disabled CLI Example: @@ -100,12 +109,12 @@ def get_disabled(): .. code-block:: bash salt '*' service.get_disabled - ''' + """ return sorted(set(get_all()) - set(get_enabled())) def available(name): - ''' + """ Returns ``True`` if the specified service is available, otherwise returns ``False``. @@ -114,12 +123,12 @@ def available(name): .. code-block:: bash salt '*' service.available sshd - ''' + """ return name in get_all() def missing(name): - ''' + """ The inverse of service.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -129,12 +138,12 @@ def missing(name): .. code-block:: bash salt '*' service.missing sshd - ''' + """ return name not in get_all() def get_all(): - ''' + """ Return all available boot services CLI Example: @@ -142,19 +151,19 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' + """ ret = set() - lines = glob.glob('/etc/init.d/*') + lines = glob.glob("/etc/init.d/*") for line in lines: - service = line.split('/etc/init.d/')[1] + service = line.split("/etc/init.d/")[1] # Remove README. If it's an enabled service, it will be added back in. - if service != 'README': + if service != "README": ret.add(service) return sorted(ret | set(get_enabled())) def start(name): - ''' + """ Start the specified service CLI Example: @@ -162,13 +171,13 @@ def start(name): .. code-block:: bash salt '*' service.start - ''' - cmd = _service_cmd(name, 'start') - return not __salt__['cmd.retcode'](cmd) + """ + cmd = _service_cmd(name, "start") + return not __salt__["cmd.retcode"](cmd) def stop(name): - ''' + """ Stop the specified service CLI Example: @@ -176,13 +185,13 @@ def stop(name): .. code-block:: bash salt '*' service.stop - ''' - cmd = _service_cmd(name, 'stop') - return not __salt__['cmd.retcode'](cmd) + """ + cmd = _service_cmd(name, "stop") + return not __salt__["cmd.retcode"](cmd) def restart(name): - ''' + """ Restart the named service CLI Example: @@ -190,13 +199,13 @@ def restart(name): .. code-block:: bash salt '*' service.restart - ''' - cmd = _service_cmd(name, 'restart') - return not __salt__['cmd.retcode'](cmd) + """ + cmd = _service_cmd(name, "restart") + return not __salt__["cmd.retcode"](cmd) def reload_(name): - ''' + """ Reload the named service CLI Example: @@ -204,13 +213,13 @@ def reload_(name): .. code-block:: bash salt '*' service.reload - ''' - cmd = _service_cmd(name, 'reload') - return not __salt__['cmd.retcode'](cmd) + """ + cmd = _service_cmd(name, "reload") + return not __salt__["cmd.retcode"](cmd) def force_reload(name): - ''' + """ Force-reload the named service CLI Example: @@ -218,13 +227,13 @@ def force_reload(name): .. code-block:: bash salt '*' service.force_reload - ''' - cmd = _service_cmd(name, 'force-reload') - return not __salt__['cmd.retcode'](cmd) + """ + cmd = _service_cmd(name, "force-reload") + return not __salt__["cmd.retcode"](cmd) def status(name, sig=None): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -245,33 +254,33 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status [service signature] - ''' + """ if sig: - return bool(__salt__['status.pid'](sig)) + return bool(__salt__["status.pid"](sig)) - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: services = [name] results = {} for service in services: - cmd = _service_cmd(service, 'status') - results[service] = not __salt__['cmd.retcode'](cmd, ignore_retcode=True) + cmd = _service_cmd(service, "status") + results[service] = not __salt__["cmd.retcode"](cmd, ignore_retcode=True) if contains_globbing: return results return results[name] def _osrel(): - osrel = __grains__.get('osrelease', _DEFAULT_VER) + osrel = __grains__.get("osrelease", _DEFAULT_VER) if not osrel: osrel = _DEFAULT_VER return osrel def enable(name, **kwargs): - ''' + """ Enable the named service to start at boot CLI Example: @@ -279,24 +288,24 @@ def enable(name, **kwargs): .. code-block:: bash salt '*' service.enable - ''' + """ osmajor = _osrel()[0] - if osmajor < '6': - cmd = 'update-rc.d -f {0} defaults 99'.format(_cmd_quote(name)) + if osmajor < "6": + cmd = "update-rc.d -f {0} defaults 99".format(_cmd_quote(name)) else: - cmd = 'update-rc.d {0} enable'.format(_cmd_quote(name)) + cmd = "update-rc.d {0} enable".format(_cmd_quote(name)) try: if int(osmajor) >= 6: - cmd = 'insserv {0} && '.format(_cmd_quote(name)) + cmd + cmd = "insserv {0} && ".format(_cmd_quote(name)) + cmd except ValueError: osrel = _osrel() - if osrel == 'testing/unstable' or osrel == 'unstable' or osrel.endswith("/sid"): - cmd = 'insserv {0} && '.format(_cmd_quote(name)) + cmd - return not __salt__['cmd.retcode'](cmd, python_shell=True) + if osrel == "testing/unstable" or osrel == "unstable" or osrel.endswith("/sid"): + cmd = "insserv {0} && ".format(_cmd_quote(name)) + cmd + return not __salt__["cmd.retcode"](cmd, python_shell=True) def disable(name, **kwargs): - ''' + """ Disable the named service to start at boot CLI Example: @@ -304,17 +313,17 @@ def disable(name, **kwargs): .. code-block:: bash salt '*' service.disable - ''' + """ osmajor = _osrel()[0] - if osmajor < '6': - cmd = 'update-rc.d -f {0} remove'.format(name) + if osmajor < "6": + cmd = "update-rc.d -f {0} remove".format(name) else: - cmd = 'update-rc.d {0} disable'.format(name) - return not __salt__['cmd.retcode'](cmd) + cmd = "update-rc.d {0} disable".format(name) + return not __salt__["cmd.retcode"](cmd) def enabled(name, **kwargs): - ''' + """ Return True if the named service is enabled, false otherwise CLI Example: @@ -322,12 +331,12 @@ def enabled(name, **kwargs): .. code-block:: bash salt '*' service.enabled - ''' + """ return name in get_enabled() def disabled(name): - ''' + """ Return True if the named service is disabled, false otherwise CLI Example: @@ -335,5 +344,5 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled - ''' + """ return name in get_disabled() diff --git a/salt/modules/debuild_pkgbuild.py b/salt/modules/debuild_pkgbuild.py index 335409199d2..1fd3b5c229c 100644 --- a/salt/modules/debuild_pkgbuild.py +++ b/salt/modules/debuild_pkgbuild.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Debian Package builder system .. versionadded:: 2015.8.0 @@ -8,16 +8,17 @@ This system allows for all of the components to build debs safely in chrooted environments. This also provides a function to generate debian repositories This module implements the pkgbuild interface -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import logging import os -import tempfile -import shutil import re +import shutil +import tempfile import time import traceback @@ -26,20 +27,21 @@ import salt.utils.files import salt.utils.path import salt.utils.stringutils import salt.utils.vt -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import third-party libs from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=no-name-in-module,import-error +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse HAS_LIBS = False -SIGN_PROMPT_RE = re.compile(r'Enter passphrase: ', re.M) -REPREPRO_SIGN_PROMPT_RE = re.compile(r'Passphrase: ', re.M) +SIGN_PROMPT_RE = re.compile(r"Enter passphrase: ", re.M) +REPREPRO_SIGN_PROMPT_RE = re.compile(r"Passphrase: ", re.M) try: - import gnupg # pylint: disable=unused-import + import gnupg # pylint: disable=unused-import import salt.modules.gpg + HAS_LIBS = True except ImportError: pass @@ -47,16 +49,16 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'pkgbuild' +__virtualname__ = "pkgbuild" def __virtual__(): - ''' + """ Confirm this module is on a Debian-based system, and has required utilities - ''' - if __grains__.get('os_family', False) in ('Kali', 'Debian'): + """ + if __grains__.get("os_family", False) in ("Kali", "Debian"): missing_util = False - utils_reqd = ['gpg', 'debuild', 'pbuilder', 'reprepro'] + utils_reqd = ["gpg", "debuild", "pbuilder", "reprepro"] for named_util in utils_reqd: if not salt.utils.path.which(named_util): missing_util = True @@ -64,56 +66,61 @@ def __virtual__(): if HAS_LIBS and not missing_util: return __virtualname__ else: - return False, ('The debbuild module could not be loaded: requires python-gnupg, gpg, debuild, ' - 'pbuilder and reprepro utilities to be installed') + return ( + False, + ( + "The debbuild module could not be loaded: requires python-gnupg, gpg, debuild, " + "pbuilder and reprepro utilities to be installed" + ), + ) else: - return (False, 'The debbuild module could not be loaded: unsupported OS family') + return (False, "The debbuild module could not be loaded: unsupported OS family") def _check_repo_sign_utils_support(name): - ''' + """ Check for specified command name in search path - ''' + """ if salt.utils.path.which(name): return True else: raise CommandExecutionError( - 'utility \'{0}\' needs to be installed or made available in search path'.format(name) + "utility '{0}' needs to be installed or made available in search path".format( + name + ) ) def _check_repo_gpg_phrase_utils(): - ''' + """ Check for /usr/lib/gnupg2/gpg-preset-passphrase is installed - ''' - util_name = '/usr/lib/gnupg2/gpg-preset-passphrase' - if __salt__['file.file_exists'](util_name): + """ + util_name = "/usr/lib/gnupg2/gpg-preset-passphrase" + if __salt__["file.file_exists"](util_name): return True else: raise CommandExecutionError( - 'utility \'{0}\' needs to be installed'.format(util_name) + "utility '{0}' needs to be installed".format(util_name) ) def _get_build_env(env): - ''' + """ Get build environment overrides dictionary to use in build process - ''' - env_override = '' + """ + env_override = "" if env is None: return env_override if not isinstance(env, dict): - raise SaltInvocationError( - '\'env\' must be a Python dictionary' - ) + raise SaltInvocationError("'env' must be a Python dictionary") for key, value in env.items(): - env_override += '{0}={1}\n'.format(key, value) - env_override += 'export {0}\n'.format(key) + env_override += "{0}={1}\n".format(key, value) + env_override += "export {0}\n".format(key) return env_override def _get_repo_options_env(env): - ''' + """ Get repo environment overrides dictionary to use in repo options process env @@ -133,22 +140,20 @@ def _get_repo_options_env(env): quotes to be used as strings. More info on this (and other) PyYAML idiosyncrasies can be found :ref:`here `. - ''' - env_options = '' + """ + env_options = "" if env is None: return env_options if not isinstance(env, dict): - raise SaltInvocationError( - '\'env\' must be a Python dictionary' - ) + raise SaltInvocationError("'env' must be a Python dictionary") for key, value in env.items(): - if key == 'OPTIONS': - env_options += '{0}\n'.format(value) + if key == "OPTIONS": + env_options += "{0}\n".format(value) return env_options def _get_repo_dists_env(env): - ''' + """ Get repo environment overrides dictionary to use in repo distributions process env @@ -175,64 +180,62 @@ def _get_repo_dists_env(env): quotes to be used as strings. More info on this (and other) PyYAML idiosyncrasies can be found :ref:`here `. - ''' + """ # env key with tuple of control information for handling input env dictionary # 0 | M - Mandatory, O - Optional, I - Ignore # 1 | 'text string for repo field' # 2 | 'default value' dflts_dict = { - 'OPTIONS': ('I', '', 'processed by _get_repo_options_env'), - 'ORIGIN': ('O', 'Origin', 'SaltStack'), - 'LABEL': ('O', 'Label', 'salt_debian'), - 'SUITE': ('O', 'Suite', 'stable'), - 'VERSION': ('O', 'Version', '9.0'), - 'CODENAME': ('M', 'Codename', 'stretch'), - 'ARCHS': ('M', 'Architectures', 'i386 amd64 source'), - 'COMPONENTS': ('M', 'Components', 'main'), - 'DESCRIPTION': ('O', 'Description', 'SaltStack debian package repo'), + "OPTIONS": ("I", "", "processed by _get_repo_options_env"), + "ORIGIN": ("O", "Origin", "SaltStack"), + "LABEL": ("O", "Label", "salt_debian"), + "SUITE": ("O", "Suite", "stable"), + "VERSION": ("O", "Version", "9.0"), + "CODENAME": ("M", "Codename", "stretch"), + "ARCHS": ("M", "Architectures", "i386 amd64 source"), + "COMPONENTS": ("M", "Components", "main"), + "DESCRIPTION": ("O", "Description", "SaltStack debian package repo"), } - env_dists = '' - codename = '' + env_dists = "" + codename = "" dflts_keys = list(dflts_dict.keys()) if env is None: for key, value in dflts_dict.items(): - if dflts_dict[key][0] == 'M': - env_dists += '{0}: {1}\n'.format(dflts_dict[key][1], dflts_dict[key][2]) - if key == 'CODENAME': + if dflts_dict[key][0] == "M": + env_dists += "{0}: {1}\n".format(dflts_dict[key][1], dflts_dict[key][2]) + if key == "CODENAME": codename = dflts_dict[key][2] return (codename, env_dists) if not isinstance(env, dict): - raise SaltInvocationError( - '\'env\' must be a Python dictionary' - ) + raise SaltInvocationError("'env' must be a Python dictionary") env_man_seen = [] for key, value in env.items(): if key in dflts_keys: - if dflts_dict[key][0] == 'M': + if dflts_dict[key][0] == "M": env_man_seen.append(key) - if key == 'CODENAME': + if key == "CODENAME": codename = value - if dflts_dict[key][0] != 'I': - env_dists += '{0}: {1}\n'.format(dflts_dict[key][1], value) + if dflts_dict[key][0] != "I": + env_dists += "{0}: {1}\n".format(dflts_dict[key][1], value) else: - env_dists += '{0}: {1}\n'.format(key, value) + env_dists += "{0}: {1}\n".format(key, value) # ensure mandatories are included env_keys = list(env.keys()) for key in env_keys: - if key in dflts_keys and dflts_dict[key][0] == 'M' and key not in env_man_seen: - env_dists += '{0}: {1}\n'.format(dflts_dict[key][1], dflts_dict[key][2]) - if key == 'CODENAME': + if key in dflts_keys and dflts_dict[key][0] == "M" and key not in env_man_seen: + env_dists += "{0}: {1}\n".format(dflts_dict[key][1], dflts_dict[key][2]) + if key == "CODENAME": codename = value return (codename, env_dists) -def _create_pbuilders(env, runas='root'): - ''' +def _create_pbuilders(env, runas="root"): + """ Create the .pbuilder family of files in user's home directory env @@ -261,62 +264,59 @@ def _create_pbuilders(env, runas='root'): Ensure the user has correct permissions to any files and directories which are to be utilized. - ''' - home = os.path.expanduser('~{0}'.format(runas)) - pbuilderrc = os.path.join(home, '.pbuilderrc') + """ + home = os.path.expanduser("~{0}".format(runas)) + pbuilderrc = os.path.join(home, ".pbuilderrc") if not os.path.isfile(pbuilderrc): - raise SaltInvocationError( - 'pbuilderrc environment is incorrectly setup' - ) + raise SaltInvocationError("pbuilderrc environment is incorrectly setup") env_overrides = _get_build_env(env) if env_overrides and not env_overrides.isspace(): - with salt.utils.files.fopen(pbuilderrc, 'a') as fow: + with salt.utils.files.fopen(pbuilderrc, "a") as fow: fow.write(salt.utils.stringutils.to_str(env_overrides)) cmd = "chown {0}:{0} {1}".format(runas, pbuilderrc) - retrc = __salt__['cmd.retcode'](cmd, runas='root') + retrc = __salt__["cmd.retcode"](cmd, runas="root") if retrc != 0: raise SaltInvocationError( - "Create pbuilderrc in home directory failed with return error \'{0}\', " - "check logs for further details".format( - retrc) + "Create pbuilderrc in home directory failed with return error '{0}', " + "check logs for further details".format(retrc) ) def _mk_tree(): - ''' + """ Create the debian build area - ''' + """ basedir = tempfile.mkdtemp() return basedir -def _get_spec(tree_base, spec, saltenv='base'): - ''' +def _get_spec(tree_base, spec, saltenv="base"): + """ Get the spec file (tarball of the debian sub-dir to use) and place it in build area - ''' + """ spec_tgt = os.path.basename(spec) dest = os.path.join(tree_base, spec_tgt) - return __salt__['cp.get_url'](spec, dest, saltenv=saltenv) + return __salt__["cp.get_url"](spec, dest, saltenv=saltenv) -def _get_src(tree_base, source, saltenv='base'): - ''' +def _get_src(tree_base, source, saltenv="base"): + """ Get the named sources and place them into the tree_base - ''' + """ parsed = _urlparse(source) sbase = os.path.basename(source) dest = os.path.join(tree_base, sbase) if parsed.scheme: - __salt__['cp.get_url'](source, dest, saltenv=saltenv) + __salt__["cp.get_url"](source, dest, saltenv=saltenv) else: shutil.copy(source, dest) -def make_src_pkg(dest_dir, spec, sources, env=None, saltenv='base', runas='root'): - ''' +def make_src_pkg(dest_dir, spec, sources, env=None, saltenv="base", runas="root"): + """ Create a platform specific source package from the given platform spec/control file and sources CLI Example: @@ -372,7 +372,7 @@ def make_src_pkg(dest_dir, spec, sources, env=None, saltenv='base', runas='root' Ensure the user has correct permissions to any files and directories which are to be utilized. - ''' + """ _create_pbuilders(env, runas) tree_base = _mk_tree() ret = [] @@ -380,38 +380,34 @@ def make_src_pkg(dest_dir, spec, sources, env=None, saltenv='base', runas='root' os.makedirs(dest_dir) # ensure directories are writable - root_user = 'root' + root_user = "root" retrc = 0 cmd = "chown {0}:{0} {1}".format(runas, tree_base) - retrc = __salt__['cmd.retcode'](cmd, runas='root') + retrc = __salt__["cmd.retcode"](cmd, runas="root") if retrc != 0: raise SaltInvocationError( - "make_src_pkg ensuring tree_base \'{0}\' ownership failed with return error \'{1}\', " - "check logs for further details".format( - tree_base, - retrc) + "make_src_pkg ensuring tree_base '{0}' ownership failed with return error '{1}', " + "check logs for further details".format(tree_base, retrc) ) cmd = "chown {0}:{0} {1}".format(runas, dest_dir) - retrc = __salt__['cmd.retcode'](cmd, runas=root_user) + retrc = __salt__["cmd.retcode"](cmd, runas=root_user) if retrc != 0: raise SaltInvocationError( - "make_src_pkg ensuring dest_dir \'{0}\' ownership failed with return error \'{1}\', " - "check logs for further details".format( - dest_dir, - retrc) + "make_src_pkg ensuring dest_dir '{0}' ownership failed with return error '{1}', " + "check logs for further details".format(dest_dir, retrc) ) spec_pathfile = _get_spec(tree_base, spec, saltenv) # build salt equivalents from scratch if isinstance(sources, six.string_types): - sources = sources.split(',') + sources = sources.split(",") for src in sources: _get_src(tree_base, src, saltenv) # .dsc then assumes sources already build - if spec_pathfile.endswith('.dsc'): + if spec_pathfile.endswith(".dsc"): for efile in os.listdir(tree_base): full = os.path.join(tree_base, efile) trgt = os.path.join(dest_dir, efile) @@ -424,50 +420,50 @@ def make_src_pkg(dest_dir, spec, sources, env=None, saltenv='base', runas='root' # and manipulate the name for debian use (convert minix and add '+ds') salttarball = None for afile in os.listdir(tree_base): - if afile.startswith('salt-') and afile.endswith('.tar.gz'): + if afile.startswith("salt-") and afile.endswith(".tar.gz"): salttarball = afile break else: return ret - frontname = salttarball.split('.tar.gz') + frontname = salttarball.split(".tar.gz") salttar_name = frontname[0] - k = salttar_name.rfind('-') - debname = salttar_name[:k] + '_' + salttar_name[k+1:] - debname += '+ds' - debname_orig = debname + '.orig.tar.gz' + k = salttar_name.rfind("-") + debname = salttar_name[:k] + "_" + salttar_name[k + 1 :] + debname += "+ds" + debname_orig = debname + ".orig.tar.gz" abspath_debname = os.path.join(tree_base, debname) - cmd = 'tar -xvzf {0}'.format(salttarball) - retrc = __salt__['cmd.retcode'](cmd, cwd=tree_base, runas=root_user) - cmd = 'mv {0} {1}'.format(salttar_name, debname) - retrc |= __salt__['cmd.retcode'](cmd, cwd=tree_base, runas=root_user) - cmd = 'tar -cvzf {0} {1}'.format(os.path.join(tree_base, debname_orig), debname) - retrc |= __salt__['cmd.retcode'](cmd, cwd=tree_base, runas=root_user) - cmd = 'rm -f {0}'.format(salttarball) - retrc |= __salt__['cmd.retcode'](cmd, cwd=tree_base, runas=root_user, env=env) - cmd = 'cp {0} {1}'.format(spec_pathfile, abspath_debname) - retrc |= __salt__['cmd.retcode'](cmd, cwd=abspath_debname, runas=root_user) - cmd = 'tar -xvJf {0}'.format(spec_pathfile) - retrc |= __salt__['cmd.retcode'](cmd, cwd=abspath_debname, runas=root_user, env=env) - cmd = 'rm -f {0}'.format(os.path.basename(spec_pathfile)) - retrc |= __salt__['cmd.retcode'](cmd, cwd=abspath_debname, runas=root_user) - cmd = 'debuild -S -uc -us -sa' - retrc |= __salt__['cmd.retcode'](cmd, cwd=abspath_debname, runas=root_user, python_shell=True, env=env) - cmd = 'rm -fR {0}'.format(abspath_debname) - retrc |= __salt__['cmd.retcode'](cmd, runas=root_user) + cmd = "tar -xvzf {0}".format(salttarball) + retrc = __salt__["cmd.retcode"](cmd, cwd=tree_base, runas=root_user) + cmd = "mv {0} {1}".format(salttar_name, debname) + retrc |= __salt__["cmd.retcode"](cmd, cwd=tree_base, runas=root_user) + cmd = "tar -cvzf {0} {1}".format(os.path.join(tree_base, debname_orig), debname) + retrc |= __salt__["cmd.retcode"](cmd, cwd=tree_base, runas=root_user) + cmd = "rm -f {0}".format(salttarball) + retrc |= __salt__["cmd.retcode"](cmd, cwd=tree_base, runas=root_user, env=env) + cmd = "cp {0} {1}".format(spec_pathfile, abspath_debname) + retrc |= __salt__["cmd.retcode"](cmd, cwd=abspath_debname, runas=root_user) + cmd = "tar -xvJf {0}".format(spec_pathfile) + retrc |= __salt__["cmd.retcode"](cmd, cwd=abspath_debname, runas=root_user, env=env) + cmd = "rm -f {0}".format(os.path.basename(spec_pathfile)) + retrc |= __salt__["cmd.retcode"](cmd, cwd=abspath_debname, runas=root_user) + cmd = "debuild -S -uc -us -sa" + retrc |= __salt__["cmd.retcode"]( + cmd, cwd=abspath_debname, runas=root_user, python_shell=True, env=env + ) + cmd = "rm -fR {0}".format(abspath_debname) + retrc |= __salt__["cmd.retcode"](cmd, runas=root_user) if retrc != 0: raise SaltInvocationError( - 'Make source package for destination directory {0}, spec {1}, sources {2}, failed ' - 'with return error {3}, check logs for further details'.format( - dest_dir, - spec, - sources, - retrc) + "Make source package for destination directory {0}, spec {1}, sources {2}, failed " + "with return error {3}, check logs for further details".format( + dest_dir, spec, sources, retrc + ) ) for dfile in os.listdir(tree_base): - if not dfile.endswith('.build'): + if not dfile.endswith(".build"): full = os.path.join(tree_base, dfile) trgt = os.path.join(dest_dir, dfile) shutil.copy(full, trgt) @@ -476,17 +472,19 @@ def make_src_pkg(dest_dir, spec, sources, env=None, saltenv='base', runas='root' return ret -def build(runas, - tgt, - dest_dir, - spec, - sources, - deps, - env, - template, - saltenv='base', - log_dir='/var/log/salt/pkgbuild'): # pylint: disable=unused-argument - ''' +def build( + runas, + tgt, + dest_dir, + spec, + sources, + deps, + env, + template, + saltenv="base", + log_dir="/var/log/salt/pkgbuild", +): # pylint: disable=unused-argument + """ Given the package destination directory, the tarball containing debian files (e.g. control) and package sources, use pbuilder to safely build the platform package @@ -502,7 +500,7 @@ def build(runas, This example command should build the libnacl package for Debian using pbuilder and place it in /var/www/html/ on the minion - ''' + """ ret = {} retrc = 0 try: @@ -515,90 +513,104 @@ def build(runas, dscs = make_src_pkg(dsc_dir, spec, sources, env, saltenv, runas) except Exception as exc: # pylint: disable=broad-except shutil.rmtree(dsc_dir) - log.error('Failed to make src package, exception \'{0}\''.format(exc)) + log.error("Failed to make src package, exception '{0}'".format(exc)) return ret - root_user = 'root' + root_user = "root" - #ensure pbuilder setup from runas if other than root + # ensure pbuilder setup from runas if other than root if runas != root_user: - user_home = os.path.expanduser('~{0}'.format(runas)) - root_home = os.path.expanduser('~root') - cmd = 'cp {0}/.pbuilderrc {1}/'.format(user_home, root_home) - retrc = __salt__['cmd.retcode'](cmd, runas=root_user, python_shell=True, env=env) - cmd = 'cp -R {0}/.pbuilder-hooks {1}/'.format(user_home, root_home) - retrc = __salt__['cmd.retcode'](cmd, runas=root_user, python_shell=True, env=env) + user_home = os.path.expanduser("~{0}".format(runas)) + root_home = os.path.expanduser("~root") + cmd = "cp {0}/.pbuilderrc {1}/".format(user_home, root_home) + retrc = __salt__["cmd.retcode"]( + cmd, runas=root_user, python_shell=True, env=env + ) + cmd = "cp -R {0}/.pbuilder-hooks {1}/".format(user_home, root_home) + retrc = __salt__["cmd.retcode"]( + cmd, runas=root_user, python_shell=True, env=env + ) if retrc != 0: raise SaltInvocationError( - "build copy pbuilder files from \'{0}\' to \'{1}\' returned error \'{2}\', " - "check logs for further details".format(user_home, root_home, retrc)) + "build copy pbuilder files from '{0}' to '{1}' returned error '{2}', " + "check logs for further details".format(user_home, root_home, retrc) + ) - cmd = '/usr/sbin/pbuilder --create' - retrc = __salt__['cmd.retcode'](cmd, runas=root_user, python_shell=True, env=env) + cmd = "/usr/sbin/pbuilder --create" + retrc = __salt__["cmd.retcode"](cmd, runas=root_user, python_shell=True, env=env) if retrc != 0: raise SaltInvocationError( - 'pbuilder create failed with return error \'{0}\', ' - 'check logs for further details'.format(retrc)) + "pbuilder create failed with return error '{0}', " + "check logs for further details".format(retrc) + ) # use default /var/cache/pbuilder/result - results_dir = '/var/cache/pbuilder/result' + results_dir = "/var/cache/pbuilder/result" # ensure clean - cmd = 'rm -fR {0}'.format(results_dir) - retrc |= __salt__['cmd.retcode'](cmd, runas=root_user, python_shell=True, env=env) + cmd = "rm -fR {0}".format(results_dir) + retrc |= __salt__["cmd.retcode"](cmd, runas=root_user, python_shell=True, env=env) # dscs should only contain salt orig and debian tarballs and dsc file for dsc in dscs: afile = os.path.basename(dsc) os.path.join(dest_dir, afile) - if dsc.endswith('.dsc'): + if dsc.endswith(".dsc"): dbase = os.path.dirname(dsc) try: - cmd = 'chown {0}:{0} -R {1}'.format(runas, dbase) - retrc |= __salt__['cmd.retcode'](cmd, runas=root_user, python_shell=True, env=env) - cmd = '/usr/sbin/pbuilder update --override-config' - retrc |= __salt__['cmd.retcode'](cmd, runas=root_user, python_shell=True, env=env) + cmd = "chown {0}:{0} -R {1}".format(runas, dbase) + retrc |= __salt__["cmd.retcode"]( + cmd, runas=root_user, python_shell=True, env=env + ) + cmd = "/usr/sbin/pbuilder update --override-config" + retrc |= __salt__["cmd.retcode"]( + cmd, runas=root_user, python_shell=True, env=env + ) cmd = '/usr/sbin/pbuilder build --debbuildopts "-sa" {0}'.format(dsc) - retrc |= __salt__['cmd.retcode'](cmd, runas=root_user, python_shell=True, env=env) + retrc |= __salt__["cmd.retcode"]( + cmd, runas=root_user, python_shell=True, env=env + ) if retrc != 0: raise SaltInvocationError( - 'pbuilder build or update failed with return error {0}, ' - 'check logs for further details'.format(retrc) + "pbuilder build or update failed with return error {0}, " + "check logs for further details".format(retrc) ) # ignore local deps generated package file for bfile in os.listdir(results_dir): - if bfile != 'Packages': + if bfile != "Packages": full = os.path.join(results_dir, bfile) bdist = os.path.join(dest_dir, bfile) shutil.copy(full, bdist) - ret.setdefault('Packages', []).append(bdist) + ret.setdefault("Packages", []).append(bdist) except Exception as exc: # pylint: disable=broad-except - log.error('Error building from \'{0}\', execption \'{1}\''.format(dsc, exc)) + log.error("Error building from '{0}', execption '{1}'".format(dsc, exc)) # remove any Packages file created for local dependency processing for pkgzfile in os.listdir(dest_dir): - if pkgzfile == 'Packages': + if pkgzfile == "Packages": pkgzabsfile = os.path.join(dest_dir, pkgzfile) os.remove(pkgzabsfile) - cmd = 'chown {0}:{0} -R {1}'.format(runas, dest_dir) - __salt__['cmd.retcode'](cmd, runas=root_user, python_shell=True, env=env) + cmd = "chown {0}:{0} -R {1}".format(runas, dest_dir) + __salt__["cmd.retcode"](cmd, runas=root_user, python_shell=True, env=env) shutil.rmtree(dsc_dir) return ret -def make_repo(repodir, - keyid=None, - env=None, - use_passphrase=False, - gnupghome='/etc/salt/gpgkeys', - runas='root', - timeout=15.0): - ''' +def make_repo( + repodir, + keyid=None, + env=None, + use_passphrase=False, + gnupghome="/etc/salt/gpgkeys", + runas="root", + timeout=15.0, +): + """ Make a package repository and optionally sign it and packages present Given the repodir (directory to create repository in), create a Debian @@ -671,177 +683,191 @@ def make_repo(repodir, salt '*' pkgbuild.make_repo /var/www/html - ''' - res = { - 'retcode': 1, - 'stdout': '', - 'stderr': 'initialization value' - } + """ + res = {"retcode": 1, "stdout": "", "stderr": "initialization value"} retrc = 0 if gnupghome and env is None: env = {} - env['GNUPGHOME'] = gnupghome + env["GNUPGHOME"] = gnupghome - repoconf = os.path.join(repodir, 'conf') + repoconf = os.path.join(repodir, "conf") if not os.path.isdir(repoconf): os.makedirs(repoconf) codename, repocfg_dists = _get_repo_dists_env(env) - repoconfdist = os.path.join(repoconf, 'distributions') - with salt.utils.files.fopen(repoconfdist, 'w') as fow: + repoconfdist = os.path.join(repoconf, "distributions") + with salt.utils.files.fopen(repoconfdist, "w") as fow: fow.write(salt.utils.stringutils.to_str(repocfg_dists)) repocfg_opts = _get_repo_options_env(env) - repoconfopts = os.path.join(repoconf, 'options') - with salt.utils.files.fopen(repoconfopts, 'w') as fow: + repoconfopts = os.path.join(repoconf, "options") + with salt.utils.files.fopen(repoconfopts, "w") as fow: fow.write(salt.utils.stringutils.to_str(repocfg_opts)) - cmd = 'chown {0}:{0} -R {1}'.format(runas, repoconf) - retrc = __salt__['cmd.retcode'](cmd, runas='root') + cmd = "chown {0}:{0} -R {1}".format(runas, repoconf) + retrc = __salt__["cmd.retcode"](cmd, runas="root") if retrc != 0: raise SaltInvocationError( - 'failed to ensure rights to repoconf directory, error {0}, ' - 'check logs for further details'.format(retrc) + "failed to ensure rights to repoconf directory, error {0}, " + "check logs for further details".format(retrc) ) local_keygrip_to_use = None local_key_fingerprint = None local_keyid = None - phrase = '' + phrase = "" # preset passphase and interaction with gpg-agent - gpg_info_file = '{0}/gpg-agent-info-salt'.format(gnupghome) - gpg_tty_info_file = '{0}/gpg-tty-info-salt'.format(gnupghome) + gpg_info_file = "{0}/gpg-agent-info-salt".format(gnupghome) + gpg_tty_info_file = "{0}/gpg-tty-info-salt".format(gnupghome) # if using older than gnupg 2.1, then env file exists - older_gnupg = __salt__['file.file_exists'](gpg_info_file) + older_gnupg = __salt__["file.file_exists"](gpg_info_file) if keyid is not None: - with salt.utils.files.fopen(repoconfdist, 'a') as fow: - fow.write(salt.utils.stringutils.to_str('SignWith: {0}\n'.format(keyid))) + with salt.utils.files.fopen(repoconfdist, "a") as fow: + fow.write(salt.utils.stringutils.to_str("SignWith: {0}\n".format(keyid))) # import_keys - pkg_pub_key_file = '{0}/{1}'.format(gnupghome, __salt__['pillar.get']('gpg_pkg_pub_keyname', None)) - pkg_priv_key_file = '{0}/{1}'.format(gnupghome, __salt__['pillar.get']('gpg_pkg_priv_keyname', None)) + pkg_pub_key_file = "{0}/{1}".format( + gnupghome, __salt__["pillar.get"]("gpg_pkg_pub_keyname", None) + ) + pkg_priv_key_file = "{0}/{1}".format( + gnupghome, __salt__["pillar.get"]("gpg_pkg_priv_keyname", None) + ) if pkg_pub_key_file is None or pkg_priv_key_file is None: raise SaltInvocationError( - 'Pillar data should contain Public and Private keys associated with \'keyid\'' + "Pillar data should contain Public and Private keys associated with 'keyid'" ) try: - __salt__['gpg.import_key'](user=runas, filename=pkg_pub_key_file, gnupghome=gnupghome) - __salt__['gpg.import_key'](user=runas, filename=pkg_priv_key_file, gnupghome=gnupghome) + __salt__["gpg.import_key"]( + user=runas, filename=pkg_pub_key_file, gnupghome=gnupghome + ) + __salt__["gpg.import_key"]( + user=runas, filename=pkg_priv_key_file, gnupghome=gnupghome + ) except SaltInvocationError: raise SaltInvocationError( - 'Public and Private key files associated with Pillar data and \'keyid\' ' - '{0} could not be found' - .format(keyid) + "Public and Private key files associated with Pillar data and 'keyid' " + "{0} could not be found".format(keyid) ) # gpg keys should have been loaded as part of setup # retrieve specified key, obtain fingerprint and preset passphrase - local_keys = __salt__['gpg.list_keys'](user=runas, gnupghome=gnupghome) + local_keys = __salt__["gpg.list_keys"](user=runas, gnupghome=gnupghome) for gpg_key in local_keys: - if keyid == gpg_key['keyid'][8:]: - local_keygrip_to_use = gpg_key['fingerprint'] - local_key_fingerprint = gpg_key['fingerprint'] - local_keyid = gpg_key['keyid'] + if keyid == gpg_key["keyid"][8:]: + local_keygrip_to_use = gpg_key["fingerprint"] + local_key_fingerprint = gpg_key["fingerprint"] + local_keyid = gpg_key["keyid"] break if not older_gnupg: try: - _check_repo_sign_utils_support('gpg2') - cmd = 'gpg2 --with-keygrip --list-secret-keys' + _check_repo_sign_utils_support("gpg2") + cmd = "gpg2 --with-keygrip --list-secret-keys" except CommandExecutionError: # later gpg versions have dispensed with gpg2 - Ubuntu 18.04 - cmd = 'gpg --with-keygrip --list-secret-keys' - local_keys2_keygrip = __salt__['cmd.run'](cmd, runas=runas, env=env) + cmd = "gpg --with-keygrip --list-secret-keys" + local_keys2_keygrip = __salt__["cmd.run"](cmd, runas=runas, env=env) local_keys2 = iter(local_keys2_keygrip.splitlines()) try: for line in local_keys2: - if line.startswith('sec'): + if line.startswith("sec"): line_fingerprint = next(local_keys2).lstrip().rstrip() if local_key_fingerprint == line_fingerprint: - lkeygrip = next(local_keys2).split('=') + lkeygrip = next(local_keys2).split("=") local_keygrip_to_use = lkeygrip[1].lstrip().rstrip() break except StopIteration: raise SaltInvocationError( - 'unable to find keygrip associated with fingerprint \'{0}\' for keyid \'{1}\'' - .format(local_key_fingerprint, local_keyid) + "unable to find keygrip associated with fingerprint '{0}' for keyid '{1}'".format( + local_key_fingerprint, local_keyid + ) ) if local_keyid is None: raise SaltInvocationError( - 'The key ID \'{0}\' was not found in GnuPG keyring at \'{1}\'' - .format(keyid, gnupghome) + "The key ID '{0}' was not found in GnuPG keyring at '{1}'".format( + keyid, gnupghome + ) ) - _check_repo_sign_utils_support('debsign') + _check_repo_sign_utils_support("debsign") if older_gnupg: - with salt.utils.files.fopen(gpg_info_file, 'r') as fow: + with salt.utils.files.fopen(gpg_info_file, "r") as fow: gpg_raw_info = fow.readlines() for gpg_info_line in gpg_raw_info: gpg_info_line = salt.utils.stringutils.to_unicode(gpg_info_line) - gpg_info = gpg_info_line.split('=') + gpg_info = gpg_info_line.split("=") env[gpg_info[0]] = gpg_info[1] break else: - with salt.utils.files.fopen(gpg_tty_info_file, 'r') as fow: + with salt.utils.files.fopen(gpg_tty_info_file, "r") as fow: gpg_raw_info = fow.readlines() for gpg_tty_info_line in gpg_raw_info: gpg_tty_info_line = salt.utils.stringutils.to_unicode(gpg_tty_info_line) - gpg_tty_info = gpg_tty_info_line.split('=') + gpg_tty_info = gpg_tty_info_line.split("=") env[gpg_tty_info[0]] = gpg_tty_info[1] break if use_passphrase: _check_repo_gpg_phrase_utils() - phrase = __salt__['pillar.get']('gpg_passphrase') + phrase = __salt__["pillar.get"]("gpg_passphrase") cmd = '/usr/lib/gnupg2/gpg-preset-passphrase --verbose --preset --passphrase "{0}" {1}'.format( - phrase, - local_keygrip_to_use) - retrc |= __salt__['cmd.retcode'](cmd, runas=runas, env=env) + phrase, local_keygrip_to_use + ) + retrc |= __salt__["cmd.retcode"](cmd, runas=runas, env=env) for debfile in os.listdir(repodir): abs_file = os.path.join(repodir, debfile) - if debfile.endswith('.changes'): + if debfile.endswith(".changes"): os.remove(abs_file) - if debfile.endswith('.dsc'): + if debfile.endswith(".dsc"): # sign_it_here if older_gnupg: if local_keyid is not None: - cmd = 'debsign --re-sign -k {0} {1}'.format(keyid, abs_file) - retrc |= __salt__['cmd.retcode'](cmd, runas=runas, cwd=repodir, use_vt=True, env=env) + cmd = "debsign --re-sign -k {0} {1}".format(keyid, abs_file) + retrc |= __salt__["cmd.retcode"]( + cmd, runas=runas, cwd=repodir, use_vt=True, env=env + ) - cmd = 'reprepro --ignore=wrongdistribution --component=main -Vb . includedsc {0} {1}'.format( - codename, - abs_file) - retrc |= __salt__['cmd.retcode'](cmd, runas=runas, cwd=repodir, use_vt=True, env=env) + cmd = "reprepro --ignore=wrongdistribution --component=main -Vb . includedsc {0} {1}".format( + codename, abs_file + ) + retrc |= __salt__["cmd.retcode"]( + cmd, runas=runas, cwd=repodir, use_vt=True, env=env + ) else: # interval of 0.125 is really too fast on some systems interval = 0.5 if local_keyid is not None: number_retries = timeout / interval times_looped = 0 - error_msg = 'Failed to debsign file {0}'.format(abs_file) - if ((__grains__['os'] in ['Ubuntu'] and __grains__['osmajorrelease'] < 18) - or (__grains__['os'] in ['Debian'] and __grains__['osmajorrelease'] <= 8)): - cmd = 'debsign --re-sign -k {0} {1}'.format(keyid, abs_file) + error_msg = "Failed to debsign file {0}".format(abs_file) + if ( + __grains__["os"] in ["Ubuntu"] + and __grains__["osmajorrelease"] < 18 + ) or ( + __grains__["os"] in ["Debian"] + and __grains__["osmajorrelease"] <= 8 + ): + cmd = "debsign --re-sign -k {0} {1}".format(keyid, abs_file) try: proc = salt.utils.vt.Terminal( cmd, env=env, shell=True, stream_stdout=True, - stream_stderr=True + stream_stderr=True, ) while proc.has_unread_data: stdout, _ = proc.recv() @@ -853,49 +879,53 @@ def make_repo(repodir, if times_looped > number_retries: raise SaltInvocationError( - 'Attempting to sign file {0} failed, timed out after {1} seconds'.format( - abs_file, - int(times_looped * interval)) + "Attempting to sign file {0} failed, timed out after {1} seconds".format( + abs_file, int(times_looped * interval) + ) ) time.sleep(interval) proc_exitstatus = proc.exitstatus if proc_exitstatus != 0: raise SaltInvocationError( - 'Signing file {0} failed with proc.status {1}'.format( - abs_file, - proc_exitstatus) + "Signing file {0} failed with proc.status {1}".format( + abs_file, proc_exitstatus + ) ) except salt.utils.vt.TerminalException as err: trace = traceback.format_exc() log.error(error_msg, err, trace) - res = { - 'retcode': 1, - 'stdout': '', - 'stderr': trace} + res = {"retcode": 1, "stdout": "", "stderr": trace} finally: proc.close(terminate=True, kill=True) else: - cmd = 'debsign --re-sign -k {0} {1}'.format(local_key_fingerprint, abs_file) - retrc |= __salt__['cmd.retcode'](cmd, runas=runas, cwd=repodir, use_vt=True, env=env) + cmd = "debsign --re-sign -k {0} {1}".format( + local_key_fingerprint, abs_file + ) + retrc |= __salt__["cmd.retcode"]( + cmd, runas=runas, cwd=repodir, use_vt=True, env=env + ) number_retries = timeout / interval times_looped = 0 - error_msg = 'Failed to reprepro includedsc file {0}'.format(abs_file) - cmd = 'reprepro --ignore=wrongdistribution --component=main -Vb . includedsc {0} {1}'.format( - codename, - abs_file) - if ((__grains__['os'] in ['Ubuntu'] and __grains__['osmajorrelease'] < 18) - or (__grains__['os'] in ['Debian'] and __grains__['osmajorrelease'] <= 8)): + error_msg = "Failed to reprepro includedsc file {0}".format(abs_file) + cmd = "reprepro --ignore=wrongdistribution --component=main -Vb . includedsc {0} {1}".format( + codename, abs_file + ) + if ( + __grains__["os"] in ["Ubuntu"] and __grains__["osmajorrelease"] < 18 + ) or ( + __grains__["os"] in ["Debian"] and __grains__["osmajorrelease"] <= 8 + ): try: proc = salt.utils.vt.Terminal( - cmd, - env=env, - shell=True, - cwd=repodir, - stream_stdout=True, - stream_stderr=True - ) + cmd, + env=env, + shell=True, + cwd=repodir, + stream_stdout=True, + stream_stderr=True, + ) while proc.has_unread_data: stdout, _ = proc.recv() if stdout and REPREPRO_SIGN_PROMPT_RE.search(stdout): @@ -906,40 +936,43 @@ def make_repo(repodir, if times_looped > number_retries: raise SaltInvocationError( - 'Attempting to reprepro includedsc for file {0} failed, timed out after {1} loops' - .format(abs_file, times_looped) - ) + "Attempting to reprepro includedsc for file {0} failed, timed out after {1} loops".format( + abs_file, times_looped + ) + ) time.sleep(interval) proc_exitstatus = proc.exitstatus if proc_exitstatus != 0: raise SaltInvocationError( - 'Reprepro includedsc for codename {0} and file {1} failed with proc.status {2}'.format( - codename, - abs_file, - proc_exitstatus) + "Reprepro includedsc for codename {0} and file {1} failed with proc.status {2}".format( + codename, abs_file, proc_exitstatus + ) ) except salt.utils.vt.TerminalException as err: trace = traceback.format_exc() log.error(error_msg, err, trace) - res = { - 'retcode': 1, - 'stdout': '', - 'stderr': trace - } + res = {"retcode": 1, "stdout": "", "stderr": trace} finally: proc.close(terminate=True, kill=True) else: - retrc |= __salt__['cmd.retcode'](cmd, runas=runas, cwd=repodir, use_vt=True, env=env) + retrc |= __salt__["cmd.retcode"]( + cmd, runas=runas, cwd=repodir, use_vt=True, env=env + ) if retrc != 0: raise SaltInvocationError( - 'Making a repo encountered errors, return error {0}, check logs for further details'.format(retrc)) + "Making a repo encountered errors, return error {0}, check logs for further details".format( + retrc + ) + ) - if debfile.endswith('.deb'): - cmd = 'reprepro --ignore=wrongdistribution --component=main -Vb . includedeb {0} {1}'.format( - codename, - abs_file) - res = __salt__['cmd.run_all'](cmd, runas=runas, cwd=repodir, use_vt=True, env=env) + if debfile.endswith(".deb"): + cmd = "reprepro --ignore=wrongdistribution --component=main -Vb . includedeb {0} {1}".format( + codename, abs_file + ) + res = __salt__["cmd.run_all"]( + cmd, runas=runas, cwd=repodir, use_vt=True, env=env + ) return res diff --git a/salt/modules/defaults.py b/salt/modules/defaults.py index 677602caf42..eae4a567b75 100644 --- a/salt/modules/defaults.py +++ b/salt/modules/defaults.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Module to work with salt formula defaults files -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import os @@ -17,46 +18,45 @@ import salt.utils.json import salt.utils.url import salt.utils.yaml -__virtualname__ = 'defaults' +__virtualname__ = "defaults" log = logging.getLogger(__name__) def _mk_client(): - ''' + """ Create a file client and add it to the context - ''' - if 'cp.fileclient' not in __context__: - __context__['cp.fileclient'] = \ - salt.fileclient.get_file_client(__opts__) + """ + if "cp.fileclient" not in __context__: + __context__["cp.fileclient"] = salt.fileclient.get_file_client(__opts__) def _load(formula): - ''' + """ Generates a list of salt:///defaults.(json|yaml) files and fetches them from the Salt master. Returns first defaults file as python dict. - ''' + """ # Compute possibilities _mk_client() paths = [] - for ext in ('yaml', 'json'): - source_url = salt.utils.url.create(formula + '/defaults.' + ext) + for ext in ("yaml", "json"): + source_url = salt.utils.url.create(formula + "/defaults." + ext) paths.append(source_url) # Fetch files from master - defaults_files = __context__['cp.fileclient'].cache_files(paths) + defaults_files = __context__["cp.fileclient"].cache_files(paths) for file_ in defaults_files: if not file_: # Skip empty string returned by cp.fileclient.cache_files. continue - suffix = file_.rsplit('.', 1)[-1] - if suffix == 'yaml': + suffix = file_.rsplit(".", 1)[-1] + if suffix == "yaml": loader = salt.utils.yaml.safe_load - elif suffix == 'json': + elif suffix == "json": loader = salt.utils.json.load else: log.debug("Failed to determine loader for %r", file_) @@ -71,8 +71,8 @@ def _load(formula): return defaults or {} -def get(key, default=''): - ''' +def get(key, default=""): + """ defaults.get is used much like pillar.get except that it will read a default value for a pillar from defaults.json or defaults.yaml files that are stored in the root of a salt formula. @@ -88,11 +88,11 @@ def get(key, default=''): For example, querying ``core:users:root`` will try to load ``salt://core/defaults.yaml`` and ``salt://core/defaults.json``. - ''' + """ # Determine formula namespace from query - if ':' in key: - namespace, key = key.split(':', 1) + if ":" in key: + namespace, key = key.split(":", 1) else: namespace, key = key, None @@ -107,7 +107,7 @@ def get(key, default=''): def merge(dest, src, merge_lists=False, in_place=True): - ''' + """ defaults.merge Allows deep merging of dicts in formulas. @@ -118,14 +118,15 @@ def merge(dest, src, merge_lists=False, in_place=True): If True, it will merge into dest dict, if not it will make a new copy from that dict and return it. - CLI Example: - .. code-block:: bash + CLI Example: - salt '*' default.merge a=b d=e + .. code-block:: bash + + salt '*' defaults.merge '{a: b}' '{d: e}' It is more typical to use this in a templating language in formulas, instead of directly on the command-line. - ''' + """ if in_place: merged = dest else: @@ -134,7 +135,7 @@ def merge(dest, src, merge_lists=False, in_place=True): def deepcopy(source): - ''' + """ defaults.deepcopy Allows deep copy of objects in formulas. @@ -143,12 +144,12 @@ def deepcopy(source): It is more typical to use this in a templating language in formulas, instead of directly on the command-line. - ''' + """ return copy.deepcopy(source) def update(dest, defaults, merge_lists=True, in_place=True): - ''' + """ defaults.update Allows to set defaults for group of data set e.g. group for nodes. @@ -198,7 +199,7 @@ def update(dest, defaults, merge_lists=True, in_place=True): It is more typical to use this in a templating language in formulas, instead of directly on the command-line. - ''' + """ if in_place: nodes = dest diff --git a/salt/modules/devmap.py b/salt/modules/devmap.py index 572165b8286..9928227cb4f 100644 --- a/salt/modules/devmap.py +++ b/salt/modules/devmap.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Device-Mapper module -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import os.path def multipath_list(): - ''' + """ Device-Mapper Multipath list CLI Example: @@ -15,13 +16,13 @@ def multipath_list(): .. code-block:: bash salt '*' devmap.multipath_list - ''' - cmd = 'multipath -l' - return __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "multipath -l" + return __salt__["cmd.run"](cmd).splitlines() def multipath_flush(device): - ''' + """ Device-Mapper Multipath flush CLI Example: @@ -29,9 +30,9 @@ def multipath_flush(device): .. code-block:: bash salt '*' devmap.multipath_flush mpath1 - ''' + """ if not os.path.exists(device): - return '{0} does not exist'.format(device) + return "{0} does not exist".format(device) - cmd = 'multipath -f {0}'.format(device) - return __salt__['cmd.run'](cmd).splitlines() + cmd = "multipath -f {0}".format(device) + return __salt__["cmd.run"](cmd).splitlines() diff --git a/salt/modules/dig.py b/salt/modules/dig.py index 31af50ed38c..566f4e7a316 100644 --- a/salt/modules/dig.py +++ b/salt/modules/dig.py @@ -1,36 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Compendium of generic DNS utilities. The 'dig' command line tool must be installed in order to use this module. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +# Import python libs +import logging +import re + # Import salt libs import salt.utils.network import salt.utils.path from salt.ext import six -# Import python libs -import logging -import re - log = logging.getLogger(__name__) -__virtualname__ = 'dig' +__virtualname__ = "dig" def __virtual__(): - ''' + """ Only load module if dig binary is present - ''' - if salt.utils.path.which('dig'): + """ + if salt.utils.path.which("dig"): return __virtualname__ - return (False, 'The dig execution module cannot be loaded: ' - 'the dig binary is not in the path.') + return ( + False, + "The dig execution module cannot be loaded: " + "the dig binary is not in the path.", + ) def check_ip(addr): - ''' + """ Check if address is a valid IP. returns True if valid, otherwise False. CLI Example: @@ -39,10 +42,10 @@ def check_ip(addr): salt ns1 dig.check_ip 127.0.0.1 salt ns1 dig.check_ip 1111:2222:3333:4444:5555:6666:7777:8888 - ''' + """ try: - addr = addr.rsplit('/', 1) + addr = addr.rsplit("/", 1) except AttributeError: # Non-string passed return False @@ -73,7 +76,7 @@ def check_ip(addr): def A(host, nameserver=None): - ''' + """ Return the A record for ``host``. Always returns a list. @@ -83,29 +86,27 @@ def A(host, nameserver=None): .. code-block:: bash salt ns1 dig.A www.google.com - ''' - dig = ['dig', '+short', six.text_type(host), 'A'] + """ + dig = ["dig", "+short", six.text_type(host), "A"] if nameserver is not None: - dig.append('@{0}'.format(nameserver)) + dig.append("@{0}".format(nameserver)) - cmd = __salt__['cmd.run_all'](dig, python_shell=False) + cmd = __salt__["cmd.run_all"](dig, python_shell=False) # In this case, 0 is not the same as False - if cmd['retcode'] != 0: + if cmd["retcode"] != 0: log.warning( - 'dig returned exit code \'{0}\'. Returning empty list as ' - 'fallback.'.format( - cmd['retcode'] - ) + "dig returned exit code '{0}'. Returning empty list as " + "fallback.".format(cmd["retcode"]) ) return [] # make sure all entries are IPs - return [x for x in cmd['stdout'].split('\n') if check_ip(x)] + return [x for x in cmd["stdout"].split("\n") if check_ip(x)] def AAAA(host, nameserver=None): - ''' + """ Return the AAAA record for ``host``. Always returns a list. @@ -115,29 +116,27 @@ def AAAA(host, nameserver=None): .. code-block:: bash salt ns1 dig.AAAA www.google.com - ''' - dig = ['dig', '+short', six.text_type(host), 'AAAA'] + """ + dig = ["dig", "+short", six.text_type(host), "AAAA"] if nameserver is not None: - dig.append('@{0}'.format(nameserver)) + dig.append("@{0}".format(nameserver)) - cmd = __salt__['cmd.run_all'](dig, python_shell=False) + cmd = __salt__["cmd.run_all"](dig, python_shell=False) # In this case, 0 is not the same as False - if cmd['retcode'] != 0: + if cmd["retcode"] != 0: log.warning( - 'dig returned exit code \'{0}\'. Returning empty list as ' - 'fallback.'.format( - cmd['retcode'] - ) + "dig returned exit code '{0}'. Returning empty list as " + "fallback.".format(cmd["retcode"]) ) return [] # make sure all entries are IPs - return [x for x in cmd['stdout'].split('\n') if check_ip(x)] + return [x for x in cmd["stdout"].split("\n") if check_ip(x)] def NS(domain, resolve=True, nameserver=None): - ''' + """ Return a list of IPs of the nameservers for ``domain`` If ``resolve`` is False, don't resolve names. @@ -147,35 +146,33 @@ def NS(domain, resolve=True, nameserver=None): .. code-block:: bash salt ns1 dig.NS google.com - ''' - dig = ['dig', '+short', six.text_type(domain), 'NS'] + """ + dig = ["dig", "+short", six.text_type(domain), "NS"] if nameserver is not None: - dig.append('@{0}'.format(nameserver)) + dig.append("@{0}".format(nameserver)) - cmd = __salt__['cmd.run_all'](dig, python_shell=False) + cmd = __salt__["cmd.run_all"](dig, python_shell=False) # In this case, 0 is not the same as False - if cmd['retcode'] != 0: + if cmd["retcode"] != 0: log.warning( - 'dig returned exit code \'{0}\'. Returning empty list as ' - 'fallback.'.format( - cmd['retcode'] - ) + "dig returned exit code '{0}'. Returning empty list as " + "fallback.".format(cmd["retcode"]) ) return [] if resolve: ret = [] - for ns_host in cmd['stdout'].split('\n'): + for ns_host in cmd["stdout"].split("\n"): for ip_addr in A(ns_host, nameserver): ret.append(ip_addr) return ret - return cmd['stdout'].split('\n') + return cmd["stdout"].split("\n") -def SPF(domain, record='SPF', nameserver=None): - ''' +def SPF(domain, record="SPF", nameserver=None): + """ Return the allowed IPv4 ranges in the SPF record for ``domain``. If record is ``SPF`` and the SPF record is empty, the TXT record will be @@ -187,34 +184,35 @@ def SPF(domain, record='SPF', nameserver=None): .. code-block:: bash salt ns1 dig.SPF google.com - ''' - spf_re = re.compile(r'(?:\+|~)?(ip[46]|include):(.+)') - cmd = ['dig', '+short', six.text_type(domain), record] + """ + spf_re = re.compile(r"(?:\+|~)?(ip[46]|include):(.+)") + cmd = ["dig", "+short", six.text_type(domain), record] if nameserver is not None: - cmd.append('@{0}'.format(nameserver)) + cmd.append("@{0}".format(nameserver)) - result = __salt__['cmd.run_all'](cmd, python_shell=False) + result = __salt__["cmd.run_all"](cmd, python_shell=False) # In this case, 0 is not the same as False - if result['retcode'] != 0: + if result["retcode"] != 0: log.warning( - 'dig returned exit code \'{0}\'. Returning empty list as fallback.' - .format(result['retcode']) + "dig returned exit code '{0}'. Returning empty list as fallback.".format( + result["retcode"] + ) ) return [] - if result['stdout'] == '' and record == 'SPF': + if result["stdout"] == "" and record == "SPF": # empty string is successful query, but nothing to return. So, try TXT # record. - return SPF(domain, 'TXT', nameserver) + return SPF(domain, "TXT", nameserver) - sections = re.sub('"', '', result['stdout']).split() - if len(sections) == 0 or sections[0] != 'v=spf1': + sections = re.sub('"', "", result["stdout"]).split() + if len(sections) == 0 or sections[0] != "v=spf1": return [] - if sections[1].startswith('redirect='): + if sections[1].startswith("redirect="): # Run a lookup on the part after 'redirect=' (9 chars) - return SPF(sections[1][9:], 'SPF', nameserver) + return SPF(sections[1][9:], "SPF", nameserver) ret = [] for section in sections[1:]: try: @@ -222,15 +220,15 @@ def SPF(domain, record='SPF', nameserver=None): except AttributeError: # Regex was not matched continue - if mechanism == 'include': - ret.extend(SPF(address, 'SPF', nameserver)) - elif mechanism in ('ip4', 'ip6') and check_ip(address): + if mechanism == "include": + ret.extend(SPF(address, "SPF", nameserver)) + elif mechanism in ("ip4", "ip6") and check_ip(address): ret.append(address) return ret def MX(domain, resolve=False, nameserver=None): - ''' + """ Return a list of lists for the MX of ``domain``. If the ``resolve`` argument is True, resolve IPs for the servers. @@ -245,35 +243,31 @@ def MX(domain, resolve=False, nameserver=None): .. code-block:: bash salt ns1 dig.MX google.com - ''' - dig = ['dig', '+short', six.text_type(domain), 'MX'] + """ + dig = ["dig", "+short", six.text_type(domain), "MX"] if nameserver is not None: - dig.append('@{0}'.format(nameserver)) + dig.append("@{0}".format(nameserver)) - cmd = __salt__['cmd.run_all'](dig, python_shell=False) + cmd = __salt__["cmd.run_all"](dig, python_shell=False) # In this case, 0 is not the same as False - if cmd['retcode'] != 0: + if cmd["retcode"] != 0: log.warning( - 'dig returned exit code \'{0}\'. Returning empty list as ' - 'fallback.'.format( - cmd['retcode'] - ) + "dig returned exit code '{0}'. Returning empty list as " + "fallback.".format(cmd["retcode"]) ) return [] - stdout = [x.split() for x in cmd['stdout'].split('\n')] + stdout = [x.split() for x in cmd["stdout"].split("\n")] if resolve: - return [ - (lambda x: [x[0], A(x[1], nameserver)[0]])(x) for x in stdout - ] + return [(lambda x: [x[0], A(x[1], nameserver)[0]])(x) for x in stdout] return stdout def TXT(host, nameserver=None): - ''' + """ Return the TXT record for ``host``. Always returns a list. @@ -283,24 +277,22 @@ def TXT(host, nameserver=None): .. code-block:: bash salt ns1 dig.TXT google.com - ''' - dig = ['dig', '+short', six.text_type(host), 'TXT'] + """ + dig = ["dig", "+short", six.text_type(host), "TXT"] if nameserver is not None: - dig.append('@{0}'.format(nameserver)) + dig.append("@{0}".format(nameserver)) - cmd = __salt__['cmd.run_all'](dig, python_shell=False) + cmd = __salt__["cmd.run_all"](dig, python_shell=False) - if cmd['retcode'] != 0: + if cmd["retcode"] != 0: log.warning( - 'dig returned exit code \'{0}\'. Returning empty list as ' - 'fallback.'.format( - cmd['retcode'] - ) + "dig returned exit code '{0}'. Returning empty list as " + "fallback.".format(cmd["retcode"]) ) return [] - return [i for i in cmd['stdout'].split('\n')] + return [i for i in cmd["stdout"].split("\n")] # Let lowercase work, since that is the convention for Salt functions diff --git a/salt/modules/disk.py b/salt/modules/disk.py index f7ce4667c6b..603edcea885 100644 --- a/salt/modules/disk.py +++ b/salt/modules/disk.py @@ -1,20 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing disks and blockdevices -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import collections +import decimal + # Import python libs import logging import os -import subprocess import re -import collections -import decimal - -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import zip +import subprocess # Import salt libs import salt.utils.decorators @@ -23,36 +20,47 @@ import salt.utils.path import salt.utils.platform from salt.exceptions import CommandExecutionError -__func_alias__ = { - 'format_': 'format' -} +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import zip + +__func_alias__ = {"format_": "format"} log = logging.getLogger(__name__) -HAS_HDPARM = salt.utils.path.which('hdparm') is not None -HAS_IOSTAT = salt.utils.path.which('iostat') is not None +HAS_HDPARM = salt.utils.path.which("hdparm") is not None +HAS_IOSTAT = salt.utils.path.which("iostat") is not None def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ if salt.utils.platform.is_windows(): - return False, 'This module doesn\'t work on Windows.' + return False, "This module doesn't work on Windows." return True def _parse_numbers(text): - ''' + """ Convert a string to a number, allowing for a K|M|G|T postfix, 32.8K. Returns a decimal number if the string is a real number, or the string unchanged otherwise. - ''' + """ if text.isdigit(): return decimal.Decimal(text) try: - postPrefixes = {'K': '10E3', 'M': '10E6', 'G': '10E9', 'T': '10E12', 'P': '10E15', 'E': '10E18', 'Z': '10E21', 'Y': '10E24'} + postPrefixes = { + "K": "10E3", + "M": "10E6", + "G": "10E9", + "T": "10E12", + "P": "10E15", + "E": "10E18", + "Z": "10E21", + "Y": "10E24", + } if text[-1] in postPrefixes.keys(): v = decimal.Decimal(text[:-1]) v = v * decimal.Decimal(postPrefixes[text[-1]]) @@ -64,25 +72,23 @@ def _parse_numbers(text): def _clean_flags(args, caller): - ''' + """ Sanitize flags passed into df - ''' - flags = '' + """ + flags = "" if args is None: return flags - allowed = ('a', 'B', 'h', 'H', 'i', 'k', 'l', 'P', 't', 'T', 'x', 'v') + allowed = ("a", "B", "h", "H", "i", "k", "l", "P", "t", "T", "x", "v") for flag in args: if flag in allowed: flags += flag else: - raise CommandExecutionError( - 'Invalid flag passed to {0}'.format(caller) - ) + raise CommandExecutionError("Invalid flag passed to {0}".format(caller)) return flags def usage(args=None): - ''' + """ Return usage information for volumes mounted on this minion .. versionchanged:: 2019.2.0 @@ -94,32 +100,34 @@ def usage(args=None): .. code-block:: bash salt '*' disk.usage - ''' - flags = _clean_flags(args, 'disk.usage') - if not os.path.isfile('/etc/mtab') and __grains__['kernel'] == 'Linux': - log.error('df cannot run without /etc/mtab') - if __grains__.get('virtual_subtype') == 'LXC': - log.error('df command failed and LXC detected. If you are running ' - 'a Docker container, consider linking /proc/mounts to ' - '/etc/mtab or consider running Docker with -privileged') + """ + flags = _clean_flags(args, "disk.usage") + if not os.path.isfile("/etc/mtab") and __grains__["kernel"] == "Linux": + log.error("df cannot run without /etc/mtab") + if __grains__.get("virtual_subtype") == "LXC": + log.error( + "df command failed and LXC detected. If you are running " + "a Docker container, consider linking /proc/mounts to " + "/etc/mtab or consider running Docker with -privileged" + ) return {} - if __grains__['kernel'] == 'Linux': - cmd = 'df -P' - elif __grains__['kernel'] == 'OpenBSD' or __grains__['kernel'] == 'AIX': - cmd = 'df -kP' - elif __grains__['kernel'] == 'SunOS': - cmd = 'df -k' + if __grains__["kernel"] == "Linux": + cmd = "df -P" + elif __grains__["kernel"] == "OpenBSD" or __grains__["kernel"] == "AIX": + cmd = "df -kP" + elif __grains__["kernel"] == "SunOS": + cmd = "df -k" else: - cmd = 'df' + cmd = "df" if flags: - cmd += ' -{0}'.format(flags) + cmd += " -{0}".format(flags) ret = {} - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() oldline = None for line in out: if not line: continue - if line.startswith('Filesystem'): + if line.startswith("Filesystem"): continue if oldline: line = oldline + " " + line @@ -130,38 +138,38 @@ def usage(args=None): else: oldline = None while len(comps) >= 2 and not comps[1].isdigit(): - comps[0] = '{0} {1}'.format(comps[0], comps[1]) + comps[0] = "{0} {1}".format(comps[0], comps[1]) comps.pop(1) if len(comps) < 2: continue try: - if __grains__['kernel'] == 'Darwin': + if __grains__["kernel"] == "Darwin": ret[comps[8]] = { - 'filesystem': comps[0], - '512-blocks': comps[1], - 'used': comps[2], - 'available': comps[3], - 'capacity': comps[4], - 'iused': comps[5], - 'ifree': comps[6], - '%iused': comps[7], + "filesystem": comps[0], + "512-blocks": comps[1], + "used": comps[2], + "available": comps[3], + "capacity": comps[4], + "iused": comps[5], + "ifree": comps[6], + "%iused": comps[7], } else: ret[comps[5]] = { - 'filesystem': comps[0], - '1K-blocks': comps[1], - 'used': comps[2], - 'available': comps[3], - 'capacity': comps[4], + "filesystem": comps[0], + "1K-blocks": comps[1], + "used": comps[2], + "available": comps[3], + "capacity": comps[4], } except IndexError: - log.error('Problem parsing disk usage information') + log.error("Problem parsing disk usage information") ret = {} return ret def inodeusage(args=None): - ''' + """ Return inode usage information for volumes mounted on this minion CLI Example: @@ -169,18 +177,18 @@ def inodeusage(args=None): .. code-block:: bash salt '*' disk.inodeusage - ''' - flags = _clean_flags(args, 'disk.inodeusage') - if __grains__['kernel'] == 'AIX': - cmd = 'df -i' + """ + flags = _clean_flags(args, "disk.inodeusage") + if __grains__["kernel"] == "AIX": + cmd = "df -i" else: - cmd = 'df -iP' + cmd = "df -iP" if flags: - cmd += ' -{0}'.format(flags) + cmd += " -{0}".format(flags) ret = {} - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() for line in out: - if line.startswith('Filesystem'): + if line.startswith("Filesystem"): continue comps = line.split() # Don't choke on empty lines @@ -188,38 +196,38 @@ def inodeusage(args=None): continue try: - if __grains__['kernel'] == 'OpenBSD': + if __grains__["kernel"] == "OpenBSD": ret[comps[8]] = { - 'inodes': int(comps[5]) + int(comps[6]), - 'used': comps[5], - 'free': comps[6], - 'use': comps[7], - 'filesystem': comps[0], + "inodes": int(comps[5]) + int(comps[6]), + "used": comps[5], + "free": comps[6], + "use": comps[7], + "filesystem": comps[0], } - elif __grains__['kernel'] == 'AIX': + elif __grains__["kernel"] == "AIX": ret[comps[6]] = { - 'inodes': comps[4], - 'used': comps[5], - 'free': comps[2], - 'use': comps[5], - 'filesystem': comps[0], + "inodes": comps[4], + "used": comps[5], + "free": comps[2], + "use": comps[5], + "filesystem": comps[0], } else: ret[comps[5]] = { - 'inodes': comps[1], - 'used': comps[2], - 'free': comps[3], - 'use': comps[4], - 'filesystem': comps[0], + "inodes": comps[1], + "used": comps[2], + "free": comps[3], + "use": comps[4], + "filesystem": comps[0], } except (IndexError, ValueError): - log.error('Problem parsing inode usage information') + log.error("Problem parsing inode usage information") ret = {} return ret def percent(args=None): - ''' + """ Return partition information for volumes mounted on this minion CLI Example: @@ -227,38 +235,38 @@ def percent(args=None): .. code-block:: bash salt '*' disk.percent /var - ''' - if __grains__['kernel'] == 'Linux': - cmd = 'df -P' - elif __grains__['kernel'] == 'OpenBSD' or __grains__['kernel'] == 'AIX': - cmd = 'df -kP' + """ + if __grains__["kernel"] == "Linux": + cmd = "df -P" + elif __grains__["kernel"] == "OpenBSD" or __grains__["kernel"] == "AIX": + cmd = "df -kP" else: - cmd = 'df' + cmd = "df" ret = {} - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() for line in out: if not line: continue - if line.startswith('Filesystem'): + if line.startswith("Filesystem"): continue comps = line.split() while len(comps) >= 2 and not comps[1].isdigit(): - comps[0] = '{0} {1}'.format(comps[0], comps[1]) + comps[0] = "{0} {1}".format(comps[0], comps[1]) comps.pop(1) if len(comps) < 2: continue try: - if __grains__['kernel'] == 'Darwin': + if __grains__["kernel"] == "Darwin": ret[comps[8]] = comps[4] else: ret[comps[5]] = comps[4] except IndexError: - log.error('Problem parsing disk usage information') + log.error("Problem parsing disk usage information") ret = {} if args and args not in ret: log.error( - 'Problem parsing disk usage information: Partition \'%s\' ' - 'does not exist!', args + "Problem parsing disk usage information: Partition '%s' " "does not exist!", + args, ) ret = {} elif args: @@ -267,9 +275,9 @@ def percent(args=None): return ret -@salt.utils.decorators.path.which('blkid') +@salt.utils.decorators.path.which("blkid") def blkid(device=None, token=None): - ''' + """ Return block device attributes: UUID, LABEL, etc. This function only works on systems where blkid is available. @@ -287,28 +295,28 @@ def blkid(device=None, token=None): salt '*' disk.blkid /dev/sda salt '*' disk.blkid token='UUID=6a38ee5-7235-44e7-8b22-816a403bad5d' salt '*' disk.blkid token='TYPE=ext4' - ''' - cmd = ['blkid'] + """ + cmd = ["blkid"] if device: cmd.append(device) elif token: - cmd.extend(['-t', token]) + cmd.extend(["-t", token]) ret = {} - blkid_result = __salt__['cmd.run_all'](cmd, python_shell=False) + blkid_result = __salt__["cmd.run_all"](cmd, python_shell=False) - if blkid_result['retcode'] > 0: + if blkid_result["retcode"] > 0: return ret - for line in blkid_result['stdout'].splitlines(): + for line in blkid_result["stdout"].splitlines(): if not line: continue comps = line.split() device = comps[0][:-1] info = {} - device_attributes = re.split(('\"*\"'), line.partition(' ')[2]) - for key, value in zip(*[iter(device_attributes)]*2): - key = key.strip('=').strip(' ') + device_attributes = re.split(('"*"'), line.partition(" ")[2]) + for key, value in zip(*[iter(device_attributes)] * 2): + key = key.strip("=").strip(" ") info[key] = value.strip('"') ret[device] = info @@ -316,7 +324,7 @@ def blkid(device=None, token=None): def tune(device, **kwargs): - ''' + """ Set attributes for the specified device CLI Example: @@ -330,32 +338,34 @@ def tune(device, **kwargs): See the ``blockdev(8)`` manpage for a more complete description of these options. - ''' + """ - kwarg_map = {'read-ahead': 'setra', - 'filesystem-read-ahead': 'setfra', - 'read-only': 'setro', - 'read-write': 'setrw'} - opts = '' + kwarg_map = { + "read-ahead": "setra", + "filesystem-read-ahead": "setfra", + "read-only": "setro", + "read-write": "setrw", + } + opts = "" args = [] for key in kwargs: if key in kwarg_map: switch = kwarg_map[key] - if key != 'read-write': - args.append(switch.replace('set', 'get')) + if key != "read-write": + args.append(switch.replace("set", "get")) else: - args.append('getro') - if kwargs[key] == 'True' or kwargs[key] is True: - opts += '--{0} '.format(key) + args.append("getro") + if kwargs[key] == "True" or kwargs[key] is True: + opts += "--{0} ".format(key) else: - opts += '--{0} {1} '.format(switch, kwargs[key]) - cmd = 'blockdev {0}{1}'.format(opts, device) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + opts += "--{0} {1} ".format(switch, kwargs[key]) + cmd = "blockdev {0}{1}".format(opts, device) + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() return dump(device, args) def wipe(device): - ''' + """ Remove the filesystem information CLI Example: @@ -363,40 +373,42 @@ def wipe(device): .. code-block:: bash salt '*' disk.wipe /dev/sda1 - ''' + """ - cmd = 'wipefs -a {0}'.format(device) + cmd = "wipefs -a {0}".format(device) try: - out = __salt__['cmd.run_all'](cmd, python_shell=False) + out = __salt__["cmd.run_all"](cmd, python_shell=False) except subprocess.CalledProcessError as err: return False - if out['retcode'] == 0: + if out["retcode"] == 0: return True else: - log.error('Error wiping device %s: %s', device, out['stderr']) + log.error("Error wiping device %s: %s", device, out["stderr"]) return False def dump(device, args=None): - ''' + """ Return all contents of dumpe2fs for a specified device CLI Example: .. code-block:: bash salt '*' disk.dump /dev/sda1 - ''' - cmd = 'blockdev --getro --getsz --getss --getpbsz --getiomin --getioopt --getalignoff ' \ - '--getmaxsect --getsize --getsize64 --getra --getfra {0}'.format(device) + """ + cmd = ( + "blockdev --getro --getsz --getss --getpbsz --getiomin --getioopt --getalignoff " + "--getmaxsect --getsize --getsize64 --getra --getfra {0}".format(device) + ) ret = {} - opts = [c[2:] for c in cmd.split() if c.startswith('--')] - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out['retcode'] == 0: - lines = [line for line in out['stdout'].splitlines() if line] + opts = [c[2:] for c in cmd.split() if c.startswith("--")] + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out["retcode"] == 0: + lines = [line for line in out["stdout"].splitlines() if line] count = 0 for line in lines: ret[opts[count]] = line - count = count+1 + count = count + 1 if args: temp_ret = {} for arg in args: @@ -409,32 +421,34 @@ def dump(device, args=None): def resize2fs(device): - ''' + """ Resizes the filesystem. CLI Example: .. code-block:: bash salt '*' disk.resize2fs /dev/sda1 - ''' - cmd = 'resize2fs {0}'.format(device) + """ + cmd = "resize2fs {0}".format(device) try: - out = __salt__['cmd.run_all'](cmd, python_shell=False) + out = __salt__["cmd.run_all"](cmd, python_shell=False) except subprocess.CalledProcessError as err: return False - if out['retcode'] == 0: + if out["retcode"] == 0: return True -@salt.utils.decorators.path.which('sync') -@salt.utils.decorators.path.which('mkfs') -def format_(device, - fs_type='ext4', - inode_size=None, - lazy_itable_init=None, - fat=None, - force=False): - ''' +@salt.utils.decorators.path.which("sync") +@salt.utils.decorators.path.which("mkfs") +def format_( + device, + fs_type="ext4", + inode_size=None, + lazy_itable_init=None, + fat=None, + force=False, +): + """ Format a filesystem onto a device .. versionadded:: 2016.11.0 @@ -476,35 +490,35 @@ def format_(device, .. code-block:: bash salt '*' disk.format /dev/sdX1 - ''' - cmd = ['mkfs', '-t', six.text_type(fs_type)] + """ + cmd = ["mkfs", "-t", six.text_type(fs_type)] if inode_size is not None: - if fs_type[:3] == 'ext': - cmd.extend(['-i', six.text_type(inode_size)]) - elif fs_type == 'xfs': - cmd.extend(['-i', 'size={0}'.format(inode_size)]) + if fs_type[:3] == "ext": + cmd.extend(["-i", six.text_type(inode_size)]) + elif fs_type == "xfs": + cmd.extend(["-i", "size={0}".format(inode_size)]) if lazy_itable_init is not None: - if fs_type[:3] == 'ext': - cmd.extend(['-E', 'lazy_itable_init={0}'.format(lazy_itable_init)]) + if fs_type[:3] == "ext": + cmd.extend(["-E", "lazy_itable_init={0}".format(lazy_itable_init)]) if fat is not None and fat in (12, 16, 32): - if fs_type[-3:] == 'fat': - cmd.extend(['-F', fat]) + if fs_type[-3:] == "fat": + cmd.extend(["-F", fat]) if force: - if fs_type[:3] == 'ext': - cmd.append('-F') - elif fs_type == 'xfs': - cmd.append('-f') + if fs_type[:3] == "ext": + cmd.append("-F") + elif fs_type == "xfs": + cmd.append("-f") cmd.append(six.text_type(device)) - mkfs_success = __salt__['cmd.retcode'](cmd, ignore_retcode=True) == 0 - sync_success = __salt__['cmd.retcode']('sync', ignore_retcode=True) == 0 + mkfs_success = __salt__["cmd.retcode"](cmd, ignore_retcode=True) == 0 + sync_success = __salt__["cmd.retcode"]("sync", ignore_retcode=True) == 0 return all([mkfs_success, sync_success]) -@salt.utils.decorators.path.which_bin(['lsblk', 'df']) +@salt.utils.decorators.path.which_bin(["lsblk", "df"]) def fstype(device): - ''' + """ Return the filesystem name of the specified device .. versionadded:: 2016.11.0 @@ -517,55 +531,59 @@ def fstype(device): .. code-block:: bash salt '*' disk.fstype /dev/sdX1 - ''' - if salt.utils.path.which('lsblk'): - lsblk_out = __salt__['cmd.run']('lsblk -o fstype {0}'.format(device)).splitlines() + """ + if salt.utils.path.which("lsblk"): + lsblk_out = __salt__["cmd.run"]( + "lsblk -o fstype {0}".format(device) + ).splitlines() if len(lsblk_out) > 1: fs_type = lsblk_out[1].strip() if fs_type: return fs_type - if salt.utils.path.which('df'): + if salt.utils.path.which("df"): # the fstype was not set on the block device, so inspect the filesystem # itself for its type - if __grains__['kernel'] == 'AIX' and os.path.isfile('/usr/sysv/bin/df'): - df_out = __salt__['cmd.run']('/usr/sysv/bin/df -n {0}'.format(device)).split() + if __grains__["kernel"] == "AIX" and os.path.isfile("/usr/sysv/bin/df"): + df_out = __salt__["cmd.run"]( + "/usr/sysv/bin/df -n {0}".format(device) + ).split() if len(df_out) > 2: fs_type = df_out[2] if fs_type: return fs_type else: - df_out = __salt__['cmd.run']('df -T {0}'.format(device)).splitlines() + df_out = __salt__["cmd.run"]("df -T {0}".format(device)).splitlines() if len(df_out) > 1: fs_type = df_out[1] if fs_type: return fs_type - return '' + return "" @salt.utils.decorators.depends(HAS_HDPARM) def _hdparm(args, failhard=True): - ''' + """ Execute hdparm Fail hard when required return output when possible - ''' - cmd = 'hdparm {0}'.format(args) - result = __salt__['cmd.run_all'](cmd) - if result['retcode'] != 0: - msg = '{0}: {1}'.format(cmd, result['stderr']) + """ + cmd = "hdparm {0}".format(args) + result = __salt__["cmd.run_all"](cmd) + if result["retcode"] != 0: + msg = "{0}: {1}".format(cmd, result["stderr"]) if failhard: raise CommandExecutionError(msg) else: log.warning(msg) - return result['stdout'] + return result["stdout"] @salt.utils.decorators.depends(HAS_HDPARM) def hdparms(disks, args=None): - ''' + """ Retrieve all info's for all disks parse 'em into a nice dict (which, considering hdparms output, is quite a hassle) @@ -576,51 +594,51 @@ def hdparms(disks, args=None): .. code-block:: bash salt '*' disk.hdparms /dev/sda - ''' - all_parms = 'aAbBcCdgHiJkMmNnQrRuW' + """ + all_parms = "aAbBcCdgHiJkMmNnQrRuW" if args is None: args = all_parms elif isinstance(args, (list, tuple)): - args = ''.join(args) + args = "".join(args) if not isinstance(disks, (list, tuple)): disks = [disks] out = {} for disk in disks: - if not disk.startswith('/dev'): - disk = '/dev/{0}'.format(disk) + if not disk.startswith("/dev"): + disk = "/dev/{0}".format(disk) disk_data = {} - for line in _hdparm('-{0} {1}'.format(args, disk), False).splitlines(): + for line in _hdparm("-{0} {1}".format(args, disk), False).splitlines(): line = line.strip() - if len(line) == 0 or line == disk + ':': + if len(line) == 0 or line == disk + ":": continue - if ':' in line: - key, vals = line.split(':', 1) - key = re.sub(r' is$', '', key) - elif '=' in line: - key, vals = line.split('=', 1) + if ":" in line: + key, vals = line.split(":", 1) + key = re.sub(r" is$", "", key) + elif "=" in line: + key, vals = line.split("=", 1) else: continue - key = key.strip().lower().replace(' ', '_') + key = key.strip().lower().replace(" ", "_") vals = vals.strip() rvals = [] - if re.match(r'[0-9]+ \(.*\)', vals): - vals = vals.split(' ') + if re.match(r"[0-9]+ \(.*\)", vals): + vals = vals.split(" ") rvals.append(int(vals[0])) - rvals.append(vals[1].strip('()')) + rvals.append(vals[1].strip("()")) else: valdict = {} - for val in re.split(r'[/,]', vals.strip()): + for val in re.split(r"[/,]", vals.strip()): val = val.strip() try: val = int(val) rvals.append(val) except Exception: # pylint: disable=broad-except - if '=' in val: - deep_key, val = val.split('=', 1) + if "=" in val: + deep_key, val = val.split("=", 1) deep_key = deep_key.strip() val = val.strip() if val: @@ -642,7 +660,7 @@ def hdparms(disks, args=None): @salt.utils.decorators.depends(HAS_HDPARM) def hpa(disks, size=None): - ''' + """ Get/set Host Protected Area settings T13 INCITS 346-2001 (1367D) defines the BEER (Boot Engineering Extension Record) @@ -663,20 +681,18 @@ def hpa(disks, size=None): salt '*' disk.hpa /dev/sda salt '*' disk.hpa /dev/sda 5% salt '*' disk.hpa /dev/sda 10543256 - ''' + """ hpa_data = {} - for disk, data in hdparms(disks, 'N').items(): + for disk, data in hdparms(disks, "N").items(): visible, total, status = data.values()[0] - if visible == total or 'disabled' in status: - hpa_data[disk] = { - 'total': total - } + if visible == total or "disabled" in status: + hpa_data[disk] = {"total": total} else: hpa_data[disk] = { - 'total': total, - 'visible': visible, - 'hidden': total - visible + "total": total, + "visible": visible, + "hidden": total - visible, } if size is None: @@ -684,20 +700,20 @@ def hpa(disks, size=None): for disk, data in hpa_data.items(): try: - size = data['total'] - int(size) + size = data["total"] - int(size) except Exception: # pylint: disable=broad-except - if '%' in size: - size = int(size.strip('%')) - size = (100 - size) * data['total'] + if "%" in size: + size = int(size.strip("%")) + size = (100 - size) * data["total"] size /= 100 if size <= 0: - size = data['total'] + size = data["total"] - _hdparm('--yes-i-know-what-i-am-doing -Np{0} {1}'.format(size, disk)) + _hdparm("--yes-i-know-what-i-am-doing -Np{0} {1}".format(size, disk)) def smart_attributes(dev, attributes=None, values=None): - ''' + """ Fetch SMART attributes Providing attributes will deliver only requested attributes Providing values will deliver only requested values for attributes @@ -714,34 +730,34 @@ def smart_attributes(dev, attributes=None, values=None): salt '*' disk.smart_attributes /dev/sda salt '*' disk.smart_attributes /dev/sda attributes=(5,187,188,197,198) - ''' + """ - if not dev.startswith('/dev/'): - dev = '/dev/' + dev + if not dev.startswith("/dev/"): + dev = "/dev/" + dev - cmd = 'smartctl --attributes {0}'.format(dev) - smart_result = __salt__['cmd.run_all'](cmd, output_loglevel='quiet') - if smart_result['retcode'] != 0: - raise CommandExecutionError(smart_result['stderr']) + cmd = "smartctl --attributes {0}".format(dev) + smart_result = __salt__["cmd.run_all"](cmd, output_loglevel="quiet") + if smart_result["retcode"] != 0: + raise CommandExecutionError(smart_result["stderr"]) - smart_result = iter(smart_result['stdout'].splitlines()) + smart_result = iter(smart_result["stdout"].splitlines()) fields = [] for line in smart_result: - if line.startswith('ID#'): - fields = re.split(r'\s+', line.strip()) + if line.startswith("ID#"): + fields = re.split(r"\s+", line.strip()) fields = [key.lower() for key in fields[1:]] break if values is not None: - fields = [field if field in values else '_' for field in fields] + fields = [field if field in values else "_" for field in fields] smart_attr = {} for line in smart_result: - if not re.match(r'[\s]*\d', line): + if not re.match(r"[\s]*\d", line): break - line = re.split(r'\s+', line.strip(), maxsplit=len(fields)) + line = re.split(r"\s+", line.strip(), maxsplit=len(fields)) attr = int(line[0]) if attributes is not None and attr not in attributes: @@ -749,7 +765,7 @@ def smart_attributes(dev, attributes=None, values=None): data = dict(zip(fields, line[1:])) try: - del data['_'] + del data["_"] except Exception: # pylint: disable=broad-except pass @@ -759,7 +775,7 @@ def smart_attributes(dev, attributes=None, values=None): val = int(val) except Exception: # pylint: disable=broad-except try: - val = [int(value) for value in val.split(' ')] + val = [int(value) for value in val.split(" ")] except Exception: # pylint: disable=broad-except pass data[field] = val @@ -771,7 +787,7 @@ def smart_attributes(dev, attributes=None, values=None): @salt.utils.decorators.depends(HAS_IOSTAT) def iostat(interval=1, count=5, disks=None): - ''' + """ Gather and return (averaged) IO stats. .. versionadded:: 2016.3.0 @@ -784,7 +800,7 @@ def iostat(interval=1, count=5, disks=None): .. code-block:: bash salt '*' disk.iostat 1 5 disks=sda - ''' + """ if salt.utils.platform.is_linux(): return _iostat_linux(interval, count, disks) elif salt.utils.platform.is_freebsd(): @@ -794,26 +810,31 @@ def iostat(interval=1, count=5, disks=None): def _iostats_dict(header, stats): - ''' + """ Transpose collected data, average it, stomp it in dict using header Use Decimals so we can properly calc & round, convert to float 'caus' we can't transmit Decimals over 0mq - ''' - stats = [float((sum(stat) / len(stat)).quantize(decimal.Decimal('.01'))) for stat in zip(*stats)] + """ + stats = [ + float((sum(stat) / len(stat)).quantize(decimal.Decimal(".01"))) + for stat in zip(*stats) + ] stats = dict(zip(header, stats)) return stats def _iostat_fbsd(interval, count, disks): - ''' + """ Tested on FreeBSD, quite likely other BSD's only need small changes in cmd syntax - ''' + """ if disks is None: - iostat_cmd = 'iostat -xC -w {0} -c {1} '.format(interval, count) + iostat_cmd = "iostat -xC -w {0} -c {1} ".format(interval, count) elif isinstance(disks, six.string_types): - iostat_cmd = 'iostat -x -w {0} -c {1} {2}'.format(interval, count, disks) + iostat_cmd = "iostat -x -w {0} -c {1} {2}".format(interval, count, disks) else: - iostat_cmd = 'iostat -x -w {0} -c {1} {2}'.format(interval, count, ' '.join(disks)) + iostat_cmd = "iostat -x -w {0} -c {1} {2}".format( + interval, count, " ".join(disks) + ) sys_stats = [] dev_stats = collections.defaultdict(list) @@ -821,9 +842,11 @@ def _iostat_fbsd(interval, count, disks): dev_header = [] h_len = 1000 # randomly absurdly high - ret = iter(__salt__['cmd.run_stdout'](iostat_cmd, output_loglevel='quiet').splitlines()) + ret = iter( + __salt__["cmd.run_stdout"](iostat_cmd, output_loglevel="quiet").splitlines() + ) for line in ret: - if not line.startswith('device'): + if not line.startswith("device"): continue elif not dev_header: dev_header = line.split()[1:] @@ -852,7 +875,7 @@ def _iostat_fbsd(interval, count, disks): sys_stats = [stat[h_len:] for stat in stats] dev_stats[disk] = [stat[0:h_len] for stat in stats] - iostats['sys'] = _iostats_dict(sys_header, sys_stats) + iostats["sys"] = _iostats_dict(sys_header, sys_stats) for disk, stats in dev_stats.items(): iostats[disk] = _iostats_dict(dev_header, stats) @@ -862,25 +885,27 @@ def _iostat_fbsd(interval, count, disks): def _iostat_linux(interval, count, disks): if disks is None: - iostat_cmd = 'iostat -x {0} {1} '.format(interval, count) + iostat_cmd = "iostat -x {0} {1} ".format(interval, count) elif isinstance(disks, six.string_types): - iostat_cmd = 'iostat -xd {0} {1} {2}'.format(interval, count, disks) + iostat_cmd = "iostat -xd {0} {1} {2}".format(interval, count, disks) else: - iostat_cmd = 'iostat -xd {0} {1} {2}'.format(interval, count, ' '.join(disks)) + iostat_cmd = "iostat -xd {0} {1} {2}".format(interval, count, " ".join(disks)) sys_stats = [] dev_stats = collections.defaultdict(list) sys_header = [] dev_header = [] - ret = iter(__salt__['cmd.run_stdout'](iostat_cmd, output_loglevel='quiet').splitlines()) + ret = iter( + __salt__["cmd.run_stdout"](iostat_cmd, output_loglevel="quiet").splitlines() + ) for line in ret: - if line.startswith('avg-cpu:'): + if line.startswith("avg-cpu:"): if not sys_header: sys_header = tuple(line.split()[1:]) line = [decimal.Decimal(x) for x in next(ret).split()] sys_stats.append(line) - elif line.startswith('Device:'): + elif line.startswith("Device:"): if not dev_header: dev_header = tuple(line.split()[1:]) while line is not False: @@ -895,7 +920,7 @@ def _iostat_linux(interval, count, disks): iostats = {} if sys_header: - iostats['sys'] = _iostats_dict(sys_header, sys_stats) + iostats["sys"] = _iostats_dict(sys_header, sys_stats) for disk, stats in dev_stats.items(): iostats[disk] = _iostats_dict(dev_header, stats) @@ -904,32 +929,32 @@ def _iostat_linux(interval, count, disks): def _iostat_aix(interval, count, disks): - ''' + """ AIX support to gather and return (averaged) IO stats. - ''' - log.debug('DGM disk iostat entry') + """ + log.debug("DGM disk iostat entry") if disks is None: - iostat_cmd = 'iostat -dD {0} {1} '.format(interval, count) + iostat_cmd = "iostat -dD {0} {1} ".format(interval, count) elif isinstance(disks, six.string_types): - iostat_cmd = 'iostat -dD {0} {1} {2}'.format(disks, interval, count) + iostat_cmd = "iostat -dD {0} {1} {2}".format(disks, interval, count) else: - iostat_cmd = 'iostat -dD {0} {1} {2}'.format(' '.join(disks), interval, count) + iostat_cmd = "iostat -dD {0} {1} {2}".format(" ".join(disks), interval, count) ret = {} procn = None fields = [] - disk_name = '' - disk_mode = '' + disk_name = "" + disk_mode = "" dev_stats = collections.defaultdict(list) - for line in __salt__['cmd.run'](iostat_cmd).splitlines(): + for line in __salt__["cmd.run"](iostat_cmd).splitlines(): # Note: iostat -dD is per-system # - #root@l490vp031_pub:~/devtest# iostat -dD hdisk6 1 3 + # root@l490vp031_pub:~/devtest# iostat -dD hdisk6 1 3 # - #System configuration: lcpu=8 drives=1 paths=2 vdisks=2 + # System configuration: lcpu=8 drives=1 paths=2 vdisks=2 # - #hdisk6 xfer: %tm_act bps tps bread bwrtn + # hdisk6 xfer: %tm_act bps tps bread bwrtn # 0.0 0.0 0.0 0.0 0.0 # read: rps avgserv minserv maxserv timeouts fails # 0.0 0.0 0.0 0.0 0 0 @@ -937,9 +962,9 @@ def _iostat_aix(interval, count, disks): # 0.0 0.0 0.0 0.0 0 0 # queue: avgtime mintime maxtime avgwqsz avgsqsz sqfull # 0.0 0.0 0.0 0.0 0.0 0.0 - #-------------------------------------------------------------------------------- + # -------------------------------------------------------------------------------- # - #hdisk6 xfer: %tm_act bps tps bread bwrtn + # hdisk6 xfer: %tm_act bps tps bread bwrtn # 9.6 16.4K 4.0 16.4K 0.0 # read: rps avgserv minserv maxserv timeouts fails # 4.0 4.9 0.3 9.9 0 0 @@ -947,9 +972,9 @@ def _iostat_aix(interval, count, disks): # 0.0 0.0 0.0 0.0 0 0 # queue: avgtime mintime maxtime avgwqsz avgsqsz sqfull # 0.0 0.0 0.0 0.0 0.0 0.0 - #-------------------------------------------------------------------------------- + # -------------------------------------------------------------------------------- # - #hdisk6 xfer: %tm_act bps tps bread bwrtn + # hdisk6 xfer: %tm_act bps tps bread bwrtn # 0.0 0.0 0.0 0.0 0.0 # read: rps avgserv minserv maxserv timeouts fails # 0.0 0.0 0.3 9.9 0 0 @@ -957,13 +982,13 @@ def _iostat_aix(interval, count, disks): # 0.0 0.0 0.0 0.0 0 0 # queue: avgtime mintime maxtime avgwqsz avgsqsz sqfull # 0.0 0.0 0.0 0.0 0.0 0.0 - #-------------------------------------------------------------------------------- - if not line or line.startswith('System') or line.startswith('-----------'): + # -------------------------------------------------------------------------------- + if not line or line.startswith("System") or line.startswith("-----------"): continue - if not re.match(r'\s', line): - #seen disk name - dsk_comps = line.split(':') + if not re.match(r"\s", line): + # seen disk name + dsk_comps = line.split(":") dsk_firsts = dsk_comps[0].split() disk_name = dsk_firsts[0] disk_mode = dsk_firsts[1] @@ -973,22 +998,22 @@ def _iostat_aix(interval, count, disks): procn = len(dev_stats[disk_name]) dev_stats[disk_name].append({}) dev_stats[disk_name][procn][disk_mode] = {} - dev_stats[disk_name][procn][disk_mode]['fields'] = fields - dev_stats[disk_name][procn][disk_mode]['stats'] = [] + dev_stats[disk_name][procn][disk_mode]["fields"] = fields + dev_stats[disk_name][procn][disk_mode]["stats"] = [] continue - if ':' in line: - comps = line.split(':') + if ":" in line: + comps = line.split(":") fields = comps[1].split() disk_mode = comps[0].lstrip() if disk_mode not in dev_stats[disk_name][0].keys(): dev_stats[disk_name][0][disk_mode] = {} - dev_stats[disk_name][0][disk_mode]['fields'] = fields - dev_stats[disk_name][0][disk_mode]['stats'] = [] + dev_stats[disk_name][0][disk_mode]["fields"] = fields + dev_stats[disk_name][0][disk_mode]["stats"] = [] else: line = line.split() stats = [_parse_numbers(x) for x in line[:]] - dev_stats[disk_name][0][disk_mode]['stats'].append(stats) + dev_stats[disk_name][0][disk_mode]["stats"].append(stats) iostats = {} @@ -996,8 +1021,8 @@ def _iostat_aix(interval, count, disks): iostats[disk] = {} for modes in list_modes: for disk_mode in modes.keys(): - fields = modes[disk_mode]['fields'] - stats = modes[disk_mode]['stats'] + fields = modes[disk_mode]["fields"] + stats = modes[disk_mode]["stats"] iostats[disk][disk_mode] = _iostats_dict(fields, stats) return iostats diff --git a/salt/modules/djangomod.py b/salt/modules/djangomod.py index c081c440ff2..48f1f0fe4fa 100644 --- a/salt/modules/djangomod.py +++ b/salt/modules/djangomod.py @@ -1,22 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" Manage Django sites -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os +import salt.exceptions + # Import Salt libs import salt.utils.path -import salt.exceptions # Import 3rd-party libs from salt.ext import six # Define the module's virtual name -__virtualname__ = 'django' +__virtualname__ = "django" def __virtual__(): @@ -24,32 +26,36 @@ def __virtual__(): def _get_django_admin(bin_env): - ''' + """ Return the django admin - ''' + """ if not bin_env: - if salt.utils.path.which('django-admin.py'): - return 'django-admin.py' - elif salt.utils.path.which('django-admin'): - return 'django-admin' + if salt.utils.path.which("django-admin.py"): + return "django-admin.py" + elif salt.utils.path.which("django-admin"): + return "django-admin" else: raise salt.exceptions.CommandExecutionError( - "django-admin or django-admin.py not found on PATH") + "django-admin or django-admin.py not found on PATH" + ) # try to get django-admin.py bin from env - if os.path.exists(os.path.join(bin_env, 'bin', 'django-admin.py')): - return os.path.join(bin_env, 'bin', 'django-admin.py') + if os.path.exists(os.path.join(bin_env, "bin", "django-admin.py")): + return os.path.join(bin_env, "bin", "django-admin.py") return bin_env -def command(settings_module, - command, - bin_env=None, - pythonpath=None, - env=None, - runas=None, - *args, **kwargs): - ''' +def command( + settings_module, + command, + bin_env=None, + pythonpath=None, + env=None, + runas=None, + *args, + **kwargs +): + """ Run arbitrary django management command CLI Example: @@ -57,31 +63,33 @@ def command(settings_module, .. code-block:: bash salt '*' django.command - ''' + """ dja = _get_django_admin(bin_env) - cmd = '{0} {1} --settings={2}'.format(dja, command, settings_module) + cmd = "{0} {1} --settings={2}".format(dja, command, settings_module) if pythonpath: - cmd = '{0} --pythonpath={1}'.format(cmd, pythonpath) + cmd = "{0} --pythonpath={1}".format(cmd, pythonpath) for arg in args: - cmd = '{0} --{1}'.format(cmd, arg) + cmd = "{0} --{1}".format(cmd, arg) for key, value in six.iteritems(kwargs): - if not key.startswith('__'): - cmd = '{0} --{1}={2}'.format(cmd, key, value) - return __salt__['cmd.run'](cmd, env=env, runas=runas, python_shell=False) + if not key.startswith("__"): + cmd = "{0} --{1}={2}".format(cmd, key, value) + return __salt__["cmd.run"](cmd, env=env, runas=runas, python_shell=False) -def syncdb(settings_module, - bin_env=None, - migrate=False, - database=None, - pythonpath=None, - env=None, - noinput=True, - runas=None): - ''' +def syncdb( + settings_module, + bin_env=None, + migrate=False, + database=None, + pythonpath=None, + env=None, + noinput=True, + runas=None, +): + """ Run syncdb Execute the Django-Admin syncdb command, if South is available on the @@ -96,35 +104,33 @@ def syncdb(settings_module, .. code-block:: bash salt '*' django.syncdb - ''' + """ args = [] kwargs = {} if migrate: - args.append('migrate') + args.append("migrate") if database: - kwargs['database'] = database + kwargs["database"] = database if noinput: - args.append('noinput') + args.append("noinput") - return command(settings_module, - 'syncdb', - bin_env, - pythonpath, - env, - runas, - *args, **kwargs) + return command( + settings_module, "syncdb", bin_env, pythonpath, env, runas, *args, **kwargs + ) -def migrate(settings_module, - app_label=None, - migration_name=None, - bin_env=None, - database=None, - pythonpath=None, - env=None, - noinput=True, - runas=None): - ''' +def migrate( + settings_module, + app_label=None, + migration_name=None, + bin_env=None, + database=None, + pythonpath=None, + env=None, + noinput=True, + runas=None, +): + """ Run migrate Execute the Django-Admin migrate command (requires Django 1.7 or higher). @@ -187,39 +193,37 @@ def migrate(settings_module, salt '*' django.migrate salt '*' django.migrate salt '*' django.migrate - ''' + """ args = [] kwargs = {} if database: - kwargs['database'] = database + kwargs["database"] = database if noinput: - args.append('noinput') + args.append("noinput") if app_label and migration_name: cmd = "migrate {0} {1}".format(app_label, migration_name) elif app_label: cmd = "migrate {0}".format(app_label) else: - cmd = 'migrate' + cmd = "migrate" - return command(settings_module, - cmd, - bin_env, - pythonpath, - env, - runas, - *args, **kwargs) + return command( + settings_module, cmd, bin_env, pythonpath, env, runas, *args, **kwargs + ) -def createsuperuser(settings_module, - username, - email, - bin_env=None, - database=None, - pythonpath=None, - env=None, - runas=None): - ''' +def createsuperuser( + settings_module, + username, + email, + bin_env=None, + database=None, + pythonpath=None, + env=None, + runas=None, +): + """ Create a super user for the database. This function defaults to use the ``--noinput`` flag which prevents the creation of a password for the superuser. @@ -229,30 +233,27 @@ def createsuperuser(settings_module, .. code-block:: bash salt '*' django.createsuperuser user user@example.com - ''' - args = ['noinput'] - kwargs = dict( - email=email, - username=username, - ) + """ + args = ["noinput"] + kwargs = dict(email=email, username=username,) if database: - kwargs['database'] = database - return command(settings_module, - 'createsuperuser', - bin_env, - pythonpath, - env, - runas, - *args, **kwargs) + kwargs["database"] = database + return command( + settings_module, + "createsuperuser", + bin_env, + pythonpath, + env, + runas, + *args, + **kwargs + ) -def loaddata(settings_module, - fixtures, - bin_env=None, - database=None, - pythonpath=None, - env=None): - ''' +def loaddata( + settings_module, fixtures, bin_env=None, database=None, pythonpath=None, env=None +): + """ Load fixture data Fixtures: @@ -264,34 +265,31 @@ def loaddata(settings_module, salt '*' django.loaddata - ''' + """ args = [] kwargs = {} if database: - kwargs['database'] = database + kwargs["database"] = database - cmd = '{0} {1}'.format('loaddata', ' '.join(fixtures.split(','))) + cmd = "{0} {1}".format("loaddata", " ".join(fixtures.split(","))) - return command(settings_module, - cmd, - bin_env, - pythonpath, - env, - *args, **kwargs) + return command(settings_module, cmd, bin_env, pythonpath, env, *args, **kwargs) -def collectstatic(settings_module, - bin_env=None, - no_post_process=False, - ignore=None, - dry_run=False, - clear=False, - link=False, - no_default_ignore=False, - pythonpath=None, - env=None, - runas=None): - ''' +def collectstatic( + settings_module, + bin_env=None, + no_post_process=False, + ignore=None, + dry_run=False, + clear=False, + link=False, + no_default_ignore=False, + pythonpath=None, + env=None, + runas=None, +): + """ Collect static files from each of your applications into a single location that can easily be served in production. @@ -300,26 +298,29 @@ def collectstatic(settings_module, .. code-block:: bash salt '*' django.collectstatic - ''' - args = ['noinput'] + """ + args = ["noinput"] kwargs = {} if no_post_process: - args.append('no-post-process') + args.append("no-post-process") if ignore: - kwargs['ignore'] = ignore + kwargs["ignore"] = ignore if dry_run: - args.append('dry-run') + args.append("dry-run") if clear: - args.append('clear') + args.append("clear") if link: - args.append('link') + args.append("link") if no_default_ignore: - args.append('no-default-ignore') + args.append("no-default-ignore") - return command(settings_module, - 'collectstatic', - bin_env, - pythonpath, - env, - runas, - *args, **kwargs) + return command( + settings_module, + "collectstatic", + bin_env, + pythonpath, + env, + runas, + *args, + **kwargs + ) diff --git a/salt/modules/dnsmasq.py b/salt/modules/dnsmasq.py index 2f79e240843..d1bd5eec71b 100644 --- a/salt/modules/dnsmasq.py +++ b/salt/modules/dnsmasq.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing dnsmasq -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -20,20 +21,20 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work on POSIX-like systems. - ''' + """ if salt.utils.platform.is_windows(): return ( False, - 'dnsmasq execution module cannot be loaded: only works on ' - 'non-Windows systems.' + "dnsmasq execution module cannot be loaded: only works on " + "non-Windows systems.", ) return True def version(): - ''' + """ Shows installed version of dnsmasq. CLI Example: @@ -41,15 +42,15 @@ def version(): .. code-block:: bash salt '*' dnsmasq.version - ''' - cmd = 'dnsmasq -v' - out = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "dnsmasq -v" + out = __salt__["cmd.run"](cmd).splitlines() comps = out[0].split() return comps[2] def fullversion(): - ''' + """ Shows installed version of dnsmasq and compile options. CLI Example: @@ -57,18 +58,17 @@ def fullversion(): .. code-block:: bash salt '*' dnsmasq.fullversion - ''' - cmd = 'dnsmasq -v' - out = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "dnsmasq -v" + out = __salt__["cmd.run"](cmd).splitlines() comps = out[0].split() version_num = comps[2] comps = out[1].split() - return {'version': version_num, - 'compile options': comps[3:]} + return {"version": version_num, "compile options": comps[3:]} -def set_config(config_file='/etc/dnsmasq.conf', follow=True, **kwargs): - ''' +def set_config(config_file="/etc/dnsmasq.conf", follow=True, **kwargs): + """ Sets a value or a set of values in the specified file. By default, if conf-dir is configured in this file, salt will attempt to set the option in any file inside the conf-dir where it has already been enabled. If it @@ -93,46 +93,46 @@ def set_config(config_file='/etc/dnsmasq.conf', follow=True, **kwargs): salt '*' dnsmasq.set_config domain=mydomain.com salt '*' dnsmasq.set_config follow=False domain=mydomain.com salt '*' dnsmasq.set_config config_file=/etc/dnsmasq.conf domain=mydomain.com - ''' + """ dnsopts = get_config(config_file) includes = [config_file] - if follow is True and 'conf-dir' in dnsopts: - for filename in os.listdir(dnsopts['conf-dir']): - if filename.startswith('.'): + if follow is True and "conf-dir" in dnsopts: + for filename in os.listdir(dnsopts["conf-dir"]): + if filename.startswith("."): continue - if filename.endswith('~'): + if filename.endswith("~"): continue - if filename.endswith('bak'): + if filename.endswith("bak"): continue - if filename.endswith('#') and filename.endswith('#'): + if filename.endswith("#") and filename.endswith("#"): continue - includes.append('{0}/{1}'.format(dnsopts['conf-dir'], filename)) + includes.append("{0}/{1}".format(dnsopts["conf-dir"], filename)) ret_kwargs = {} for key in kwargs: # Filter out __pub keys as they should not be added to the config file # See Issue #34263 for more information - if key.startswith('__'): + if key.startswith("__"): continue ret_kwargs[key] = kwargs[key] if key in dnsopts: if isinstance(dnsopts[key], six.string_types): for config in includes: - __salt__['file.sed'](path=config, - before='^{0}=.*'.format(key), - after='{0}={1}'.format(key, kwargs[key])) + __salt__["file.sed"]( + path=config, + before="^{0}=.*".format(key), + after="{0}={1}".format(key, kwargs[key]), + ) else: - __salt__['file.append'](config_file, - '{0}={1}'.format(key, kwargs[key])) + __salt__["file.append"](config_file, "{0}={1}".format(key, kwargs[key])) else: - __salt__['file.append'](config_file, - '{0}={1}'.format(key, kwargs[key])) + __salt__["file.append"](config_file, "{0}={1}".format(key, kwargs[key])) return ret_kwargs -def get_config(config_file='/etc/dnsmasq.conf'): - ''' +def get_config(config_file="/etc/dnsmasq.conf"): + """ Dumps all options from the config file. config_file @@ -145,41 +145,40 @@ def get_config(config_file='/etc/dnsmasq.conf'): salt '*' dnsmasq.get_config salt '*' dnsmasq.get_config config_file=/etc/dnsmasq.conf - ''' + """ dnsopts = _parse_dnamasq(config_file) - if 'conf-dir' in dnsopts: - for filename in os.listdir(dnsopts['conf-dir']): - if filename.startswith('.'): + if "conf-dir" in dnsopts: + for filename in os.listdir(dnsopts["conf-dir"]): + if filename.startswith("."): continue - if filename.endswith('~'): + if filename.endswith("~"): continue - if filename.endswith('#') and filename.endswith('#'): + if filename.endswith("#") and filename.endswith("#"): continue - dnsopts.update(_parse_dnamasq('{0}/{1}'.format(dnsopts['conf-dir'], - filename))) + dnsopts.update( + _parse_dnamasq("{0}/{1}".format(dnsopts["conf-dir"], filename)) + ) return dnsopts def _parse_dnamasq(filename): - ''' + """ Generic function for parsing dnsmasq files including includes. - ''' + """ fileopts = {} if not os.path.isfile(filename): - raise CommandExecutionError( - 'Error: No such file \'{0}\''.format(filename) - ) + raise CommandExecutionError("Error: No such file '{0}'".format(filename)) - with salt.utils.files.fopen(filename, 'r') as fp_: + with salt.utils.files.fopen(filename, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) if not line.strip(): continue - if line.startswith('#'): + if line.startswith("#"): continue - if '=' in line: - comps = line.split('=') + if "=" in line: + comps = line.split("=") if comps[0] in fileopts: if isinstance(fileopts[comps[0]], six.string_types): temp = fileopts[comps[0]] @@ -188,7 +187,7 @@ def _parse_dnamasq(filename): else: fileopts[comps[0]] = comps[1].strip() else: - if 'unparsed' not in fileopts: - fileopts['unparsed'] = [] - fileopts['unparsed'].append(line) + if "unparsed" not in fileopts: + fileopts["unparsed"] = [] + fileopts["unparsed"].append(line) return fileopts diff --git a/salt/modules/dnsutil.py b/salt/modules/dnsutil.py index e27173426a3..82219a3a630 100644 --- a/salt/modules/dnsutil.py +++ b/salt/modules/dnsutil.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Compendium of generic DNS utilities. .. note:: Some functions in the ``dnsutil`` execution module depend on ``dig``. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import socket import time @@ -22,15 +23,15 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Generic, should work on any platform (including Windows). Functionality which requires dependencies outside of Python do not belong in this module. - ''' + """ return True -def parse_hosts(hostsfile='/etc/hosts', hosts=None): - ''' +def parse_hosts(hostsfile="/etc/hosts", hosts=None): + """ Parse /etc/hosts file. CLI Example: @@ -38,19 +39,19 @@ def parse_hosts(hostsfile='/etc/hosts', hosts=None): .. code-block:: bash salt '*' dnsutil.parse_hosts - ''' + """ if not hosts: try: - with salt.utils.files.fopen(hostsfile, 'r') as fp_: + with salt.utils.files.fopen(hostsfile, "r") as fp_: hosts = salt.utils.stringutils.to_unicode(fp_.read()) except Exception: # pylint: disable=broad-except - return 'Error: hosts data was not found' + return "Error: hosts data was not found" hostsdict = {} for line in hosts.splitlines(): if not line: continue - if line.startswith('#'): + if line.startswith("#"): continue comps = line.split() ip = comps[0] @@ -60,8 +61,8 @@ def parse_hosts(hostsfile='/etc/hosts', hosts=None): return hostsdict -def hosts_append(hostsfile='/etc/hosts', ip_addr=None, entries=None): - ''' +def hosts_append(hostsfile="/etc/hosts", ip_addr=None, entries=None): + """ Append a single line to the /etc/hosts file. CLI Example: @@ -69,8 +70,8 @@ def hosts_append(hostsfile='/etc/hosts', ip_addr=None, entries=None): .. code-block:: bash salt '*' dnsutil.hosts_append /etc/hosts 127.0.0.1 ad1.yuk.co,ad2.yuk.co - ''' - host_list = entries.split(',') + """ + host_list = entries.split(",") hosts = parse_hosts(hostsfile=hostsfile) if ip_addr in hosts: for host in host_list: @@ -78,18 +79,17 @@ def hosts_append(hostsfile='/etc/hosts', ip_addr=None, entries=None): host_list.remove(host) if not host_list: - return 'No additional hosts were added to {0}'.format(hostsfile) + return "No additional hosts were added to {0}".format(hostsfile) - append_line = '\n{0} {1}'.format(ip_addr, ' '.join(host_list)) - with salt.utils.files.fopen(hostsfile, 'a') as fp_: + append_line = "\n{0} {1}".format(ip_addr, " ".join(host_list)) + with salt.utils.files.fopen(hostsfile, "a") as fp_: fp_.write(salt.utils.stringutils.to_str(append_line)) - return 'The following line was added to {0}:{1}'.format(hostsfile, - append_line) + return "The following line was added to {0}:{1}".format(hostsfile, append_line) -def hosts_remove(hostsfile='/etc/hosts', entries=None): - ''' +def hosts_remove(hostsfile="/etc/hosts", entries=None): + """ Remove a host from the /etc/hosts file. If doing so will leave a line containing only an IP address, then the line will be deleted. This function will leave comments and blank lines intact. @@ -100,27 +100,27 @@ def hosts_remove(hostsfile='/etc/hosts', entries=None): salt '*' dnsutil.hosts_remove /etc/hosts ad1.yuk.co salt '*' dnsutil.hosts_remove /etc/hosts ad2.yuk.co,ad1.yuk.co - ''' - with salt.utils.files.fopen(hostsfile, 'r') as fp_: + """ + with salt.utils.files.fopen(hostsfile, "r") as fp_: hosts = salt.utils.stringutils.to_unicode(fp_.read()) - host_list = entries.split(',') - with salt.utils.files.fopen(hostsfile, 'w') as out_file: + host_list = entries.split(",") + with salt.utils.files.fopen(hostsfile, "w") as out_file: for line in hosts.splitlines(): - if not line or line.strip().startswith('#'): - out_file.write(salt.utils.stringutils.to_str('{0}\n'.format(line))) + if not line or line.strip().startswith("#"): + out_file.write(salt.utils.stringutils.to_str("{0}\n".format(line))) continue comps = line.split() for host in host_list: if host in comps[1:]: comps.remove(host) if len(comps) > 1: - out_file.write(salt.utils.stringutils.to_str(' '.join(comps))) - out_file.write(salt.utils.stringutils.to_str('\n')) + out_file.write(salt.utils.stringutils.to_str(" ".join(comps))) + out_file.write(salt.utils.stringutils.to_str("\n")) def parse_zone(zonefile=None, zone=None): - ''' + """ Parses a zone file. Can be passed raw zone data on the API level. CLI Example: @@ -128,69 +128,70 @@ def parse_zone(zonefile=None, zone=None): .. code-block:: bash salt ns1 dnsutil.parse_zone /var/lib/named/example.com.zone - ''' + """ if zonefile: try: - with salt.utils.files.fopen(zonefile, 'r') as fp_: + with salt.utils.files.fopen(zonefile, "r") as fp_: zone = salt.utils.stringutils.to_unicode(fp_.read()) except Exception: # pylint: disable=broad-except pass if not zone: - return 'Error: Zone data was not found' + return "Error: Zone data was not found" zonedict = {} - mode = 'single' + mode = "single" for line in zone.splitlines(): - comps = line.split(';') + comps = line.split(";") line = comps[0].strip() if not line: continue comps = line.split() - if line.startswith('$'): - zonedict[comps[0].replace('$', '')] = comps[1] + if line.startswith("$"): + zonedict[comps[0].replace("$", "")] = comps[1] continue - if '(' in line and ')' not in line: - mode = 'multi' - multi = '' - if mode == 'multi': - multi += ' {0}'.format(line) - if ')' in line: - mode = 'single' - line = multi.replace('(', '').replace(')', '') + if "(" in line and ")" not in line: + mode = "multi" + multi = "" + if mode == "multi": + multi += " {0}".format(line) + if ")" in line: + mode = "single" + line = multi.replace("(", "").replace(")", "") else: continue - if 'ORIGIN' in zonedict: - comps = line.replace('@', zonedict['ORIGIN']).split() + if "ORIGIN" in zonedict: + comps = line.replace("@", zonedict["ORIGIN"]).split() else: comps = line.split() - if 'SOA' in line: - if comps[1] != 'IN': + if "SOA" in line: + if comps[1] != "IN": comps.pop(1) - zonedict['ORIGIN'] = comps[0] - zonedict['NETWORK'] = comps[1] - zonedict['SOURCE'] = comps[3] - zonedict['CONTACT'] = comps[4].replace('.', '@', 1) - zonedict['SERIAL'] = comps[5] - zonedict['REFRESH'] = _to_seconds(comps[6]) - zonedict['RETRY'] = _to_seconds(comps[7]) - zonedict['EXPIRE'] = _to_seconds(comps[8]) - zonedict['MINTTL'] = _to_seconds(comps[9]) + zonedict["ORIGIN"] = comps[0] + zonedict["NETWORK"] = comps[1] + zonedict["SOURCE"] = comps[3] + zonedict["CONTACT"] = comps[4].replace(".", "@", 1) + zonedict["SERIAL"] = comps[5] + zonedict["REFRESH"] = _to_seconds(comps[6]) + zonedict["RETRY"] = _to_seconds(comps[7]) + zonedict["EXPIRE"] = _to_seconds(comps[8]) + zonedict["MINTTL"] = _to_seconds(comps[9]) continue - if comps[0] == 'IN': - comps.insert(0, zonedict['ORIGIN']) - if not comps[0].endswith('.') and 'NS' not in line: - comps[0] = '{0}.{1}'.format(comps[0], zonedict['ORIGIN']) - if comps[2] == 'NS': - zonedict.setdefault('NS', []).append(comps[3]) - elif comps[2] == 'MX': - if 'MX' not in zonedict: - zonedict.setdefault('MX', []).append({'priority': comps[3], - 'host': comps[4]}) - elif comps[3] in ('A', 'AAAA'): + if comps[0] == "IN": + comps.insert(0, zonedict["ORIGIN"]) + if not comps[0].endswith(".") and "NS" not in line: + comps[0] = "{0}.{1}".format(comps[0], zonedict["ORIGIN"]) + if comps[2] == "NS": + zonedict.setdefault("NS", []).append(comps[3]) + elif comps[2] == "MX": + if "MX" not in zonedict: + zonedict.setdefault("MX", []).append( + {"priority": comps[3], "host": comps[4]} + ) + elif comps[3] in ("A", "AAAA"): zonedict.setdefault(comps[3], {})[comps[0]] = { - 'TARGET': comps[4], - 'TTL': comps[1], + "TARGET": comps[4], + "TTL": comps[1], } else: zonedict.setdefault(comps[2], {})[comps[0]] = comps[3] @@ -198,18 +199,18 @@ def parse_zone(zonefile=None, zone=None): def _to_seconds(timestr): - ''' + """ Converts a time value to seconds. As per RFC1035 (page 45), max time is 1 week, so anything longer (or unreadable) will be set to one week (604800 seconds). - ''' + """ timestr = timestr.upper() - if 'H' in timestr: - seconds = int(timestr.replace('H', '')) * 3600 - elif 'D' in timestr: - seconds = int(timestr.replace('D', '')) * 86400 - elif 'W' in timestr: + if "H" in timestr: + seconds = int(timestr.replace("H", "")) * 3600 + elif "D" in timestr: + seconds = int(timestr.replace("D", "")) * 86400 + elif "W" in timestr: seconds = 604800 else: try: @@ -222,16 +223,16 @@ def _to_seconds(timestr): def _has_dig(): - ''' + """ The dig-specific functions have been moved into their own module, but because they are also DNS utilities, a compatibility layer exists. This function helps add that layer. - ''' - return salt.utils.path.which('dig') is not None + """ + return salt.utils.path.which("dig") is not None def check_ip(ip_addr): - ''' + """ Check that string ip_addr is a valid IP CLI Example: @@ -239,15 +240,15 @@ def check_ip(ip_addr): .. code-block:: bash salt ns1 dnsutil.check_ip 127.0.0.1 - ''' + """ if _has_dig(): - return __salt__['dig.check_ip'](ip_addr) + return __salt__["dig.check_ip"](ip_addr) - return 'This function requires dig, which is not currently available' + return "This function requires dig, which is not currently available" def A(host, nameserver=None): - ''' + """ Return the A record(s) for ``host``. Always returns a list. @@ -257,22 +258,27 @@ def A(host, nameserver=None): .. code-block:: bash salt ns1 dnsutil.A www.google.com - ''' + """ if _has_dig(): - return __salt__['dig.A'](host, nameserver) + return __salt__["dig.A"](host, nameserver) elif nameserver is None: # fall back to the socket interface, if we don't care who resolves try: - addresses = [sock[4][0] for sock in socket.getaddrinfo(host, None, socket.AF_INET, 0, socket.SOCK_RAW)] + addresses = [ + sock[4][0] + for sock in socket.getaddrinfo( + host, None, socket.AF_INET, 0, socket.SOCK_RAW + ) + ] return addresses except socket.gaierror: - return 'Unable to resolve {0}'.format(host) + return "Unable to resolve {0}".format(host) - return 'This function requires dig, which is not currently available' + return "This function requires dig, which is not currently available" def AAAA(host, nameserver=None): - ''' + """ Return the AAAA record(s) for ``host``. Always returns a list. @@ -284,22 +290,27 @@ def AAAA(host, nameserver=None): .. code-block:: bash salt ns1 dnsutil.AAAA www.google.com - ''' + """ if _has_dig(): - return __salt__['dig.AAAA'](host, nameserver) + return __salt__["dig.AAAA"](host, nameserver) elif nameserver is None: # fall back to the socket interface, if we don't care who resolves try: - addresses = [sock[4][0] for sock in socket.getaddrinfo(host, None, socket.AF_INET6, 0, socket.SOCK_RAW)] + addresses = [ + sock[4][0] + for sock in socket.getaddrinfo( + host, None, socket.AF_INET6, 0, socket.SOCK_RAW + ) + ] return addresses except socket.gaierror: - return 'Unable to resolve {0}'.format(host) + return "Unable to resolve {0}".format(host) - return 'This function requires dig, which is not currently available' + return "This function requires dig, which is not currently available" def NS(domain, resolve=True, nameserver=None): - ''' + """ Return a list of IPs of the nameservers for ``domain`` If 'resolve' is False, don't resolve names. @@ -310,15 +321,15 @@ def NS(domain, resolve=True, nameserver=None): salt ns1 dnsutil.NS google.com - ''' + """ if _has_dig(): - return __salt__['dig.NS'](domain, resolve, nameserver) + return __salt__["dig.NS"](domain, resolve, nameserver) - return 'This function requires dig, which is not currently available' + return "This function requires dig, which is not currently available" -def SPF(domain, record='SPF', nameserver=None): - ''' +def SPF(domain, record="SPF", nameserver=None): + """ Return the allowed IPv4 ranges in the SPF record for ``domain``. If record is ``SPF`` and the SPF record is empty, the TXT record will be @@ -330,15 +341,15 @@ def SPF(domain, record='SPF', nameserver=None): .. code-block:: bash salt ns1 dnsutil.SPF google.com - ''' + """ if _has_dig(): - return __salt__['dig.SPF'](domain, record, nameserver) + return __salt__["dig.SPF"](domain, record, nameserver) - return 'This function requires dig, which is not currently available' + return "This function requires dig, which is not currently available" def MX(domain, resolve=False, nameserver=None): - ''' + """ Return a list of lists for the MX of ``domain``. If the 'resolve' argument is True, resolve IPs for the servers. @@ -353,15 +364,15 @@ def MX(domain, resolve=False, nameserver=None): .. code-block:: bash salt ns1 dnsutil.MX google.com - ''' + """ if _has_dig(): - return __salt__['dig.MX'](domain, resolve, nameserver) + return __salt__["dig.MX"](domain, resolve, nameserver) - return 'This function requires dig, which is not currently available' + return "This function requires dig, which is not currently available" -def serial(zone='', update=False): - ''' +def serial(zone="", update=False): + """ Return, store and update a dns serial for your zone files. zone: a keyword for a specific zone @@ -384,18 +395,18 @@ def serial(zone='', update=False): .. code-block:: bash salt ns1 dnsutil.serial example.com - ''' + """ grains = {} - key = 'dnsserial' + key = "dnsserial" if zone: - key += '_{0}'.format(zone) - stored = __salt__['grains.get'](key=key) - present = time.strftime('%Y%m%d01') + key += "_{0}".format(zone) + stored = __salt__["grains.get"](key=key) + present = time.strftime("%Y%m%d01") if not update: return stored or present if stored and stored >= present: current = six.text_type(int(stored) + 1) else: current = present - __salt__['grains.setval'](key=key, val=current) + __salt__["grains.setval"](key=key, val=current) return current diff --git a/salt/modules/dockercompose.py b/salt/modules/dockercompose.py index c10915bb8b2..64dfc799f9d 100644 --- a/salt/modules/dockercompose.py +++ b/salt/modules/dockercompose.py @@ -1,6 +1,5 @@ - # -*- coding: utf-8 -*- -''' +""" Module to import docker-compose via saltstack .. versionadded:: 2016.3.0 @@ -103,7 +102,7 @@ Functions Detailed Function Documentation ------------------------------- -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -111,14 +110,11 @@ import inspect import logging import os import re +from operator import attrgetter import salt.utils.files import salt.utils.stringutils - from salt.ext import six - -from operator import attrgetter - from salt.serializers import json from salt.utils import yaml @@ -126,39 +122,44 @@ try: import compose from compose.cli.command import get_project from compose.service import ConvergenceStrategy + HAS_DOCKERCOMPOSE = True except ImportError: HAS_DOCKERCOMPOSE = False try: from compose.project import OneOffFilter + USE_FILTERCLASS = True except ImportError: USE_FILTERCLASS = False MIN_DOCKERCOMPOSE = (1, 5, 0) -VERSION_RE = r'([\d.]+)' +VERSION_RE = r"([\d.]+)" log = logging.getLogger(__name__) debug = False -__virtualname__ = 'dockercompose' -DEFAULT_DC_FILENAMES = ('docker-compose.yml', 'docker-compose.yaml') +__virtualname__ = "dockercompose" +DEFAULT_DC_FILENAMES = ("docker-compose.yml", "docker-compose.yaml") def __virtual__(): if HAS_DOCKERCOMPOSE: match = re.match(VERSION_RE, six.text_type(compose.__version__)) if match: - version = tuple([int(x) for x in match.group(1).split('.')]) + version = tuple([int(x) for x in match.group(1).split(".")]) if version >= MIN_DOCKERCOMPOSE: return __virtualname__ - return (False, 'The dockercompose execution module not loaded: ' - 'compose python library not available.') + return ( + False, + "The dockercompose execution module not loaded: " + "compose python library not available.", + ) def __standardize_result(status, message, data=None, debug_msg=None): - ''' + """ Standardizes all responses :param status: @@ -166,28 +167,25 @@ def __standardize_result(status, message, data=None, debug_msg=None): :param data: :param debug_msg: :return: - ''' - result = { - 'status': status, - 'message': message - } + """ + result = {"status": status, "message": message} if data is not None: - result['return'] = data + result["return"] = data if debug_msg is not None and debug: - result['debug'] = debug_msg + result["debug"] = debug_msg return result def __get_docker_file_path(path): - ''' + """ Determines the filepath to use :param path: :return: - ''' + """ if os.path.isfile(path): return path for dc_filename in DEFAULT_DC_FILENAMES: @@ -198,90 +196,89 @@ def __get_docker_file_path(path): def __read_docker_compose_file(file_path): - ''' + """ Read the compose file if it exists in the directory :param file_path: :return: - ''' + """ if not os.path.isfile(file_path): - return __standardize_result(False, - 'Path {} is not present'.format(file_path), - None, None) + return __standardize_result( + False, "Path {} is not present".format(file_path), None, None + ) try: - with salt.utils.files.fopen(file_path, 'r') as fl: + with salt.utils.files.fopen(file_path, "r") as fl: file_name = os.path.basename(file_path) - result = {file_name: ''} + result = {file_name: ""} for line in fl: result[file_name] += salt.utils.stringutils.to_unicode(line) except EnvironmentError: - return __standardize_result(False, - 'Could not read {0}'.format(file_path), - None, None) - return __standardize_result(True, - 'Reading content of {0}'.format(file_path), - result, None) + return __standardize_result( + False, "Could not read {0}".format(file_path), None, None + ) + return __standardize_result( + True, "Reading content of {0}".format(file_path), result, None + ) def __load_docker_compose(path): - ''' + """ Read the compose file and load its' contents :param path: :return: - ''' + """ file_path = __get_docker_file_path(path) if file_path is None: - msg = 'Could not find docker-compose file at {0}'.format(path) - return None, __standardize_result(False, msg, - None, None) + msg = "Could not find docker-compose file at {0}".format(path) + return None, __standardize_result(False, msg, None, None) if not os.path.isfile(file_path): - return None, __standardize_result(False, - 'Path {} is not present'.format(file_path), - None, None) + return ( + None, + __standardize_result( + False, "Path {} is not present".format(file_path), None, None + ), + ) try: - with salt.utils.files.fopen(file_path, 'r') as fl: + with salt.utils.files.fopen(file_path, "r") as fl: loaded = yaml.load(fl) except EnvironmentError: - return None, __standardize_result(False, - 'Could not read {0}'.format(file_path), - None, None) + return ( + None, + __standardize_result( + False, "Could not read {0}".format(file_path), None, None + ), + ) except yaml.YAMLError as yerr: - msg = 'Could not parse {0} {1}'.format(file_path, yerr) - return None, __standardize_result(False, msg, - None, None) + msg = "Could not parse {0} {1}".format(file_path, yerr) + return None, __standardize_result(False, msg, None, None) if not loaded: - msg = 'Got empty compose file at {0}'.format(file_path) - return None, __standardize_result(False, msg, - None, None) - if 'services' not in loaded: - loaded['services'] = {} - result = { - 'compose_content': loaded, - 'file_name': os.path.basename(file_path) - } + msg = "Got empty compose file at {0}".format(file_path) + return None, __standardize_result(False, msg, None, None) + if "services" not in loaded: + loaded["services"] = {} + result = {"compose_content": loaded, "file_name": os.path.basename(file_path)} return result, None def __dump_docker_compose(path, content, already_existed): - ''' + """ Dumps :param path: :param content: the not-yet dumped content :return: - ''' + """ try: dumped = yaml.safe_dump(content, indent=2, default_flow_style=False) return __write_docker_compose(path, dumped, already_existed) except TypeError as t_err: - msg = 'Could not dump {0} {1}'.format(content, t_err) - return __standardize_result(False, msg, - None, None) + msg = "Could not dump {0} {1}".format(content, t_err) + return __standardize_result(False, msg, None, None) def __write_docker_compose(path, docker_compose, already_existed): - ''' + """ Write docker-compose to a path in order to use it with docker-compose ( config check ) @@ -291,8 +288,8 @@ def __write_docker_compose(path, docker_compose, already_existed): contains the docker-compose file :return: - ''' - if path.lower().endswith(('.yml', '.yaml')): + """ + if path.lower().endswith((".yml", ".yaml")): file_path = path dir_name = os.path.dirname(path) else: @@ -301,12 +298,12 @@ def __write_docker_compose(path, docker_compose, already_existed): if os.path.isdir(dir_name) is False: os.mkdir(dir_name) try: - with salt.utils.files.fopen(file_path, 'w') as fl: + with salt.utils.files.fopen(file_path, "w") as fl: fl.write(salt.utils.stringutils.to_str(docker_compose)) except EnvironmentError: - return __standardize_result(False, - 'Could not write {0}'.format(file_path), - None, None) + return __standardize_result( + False, "Could not write {0}".format(file_path), None, None + ) project = __load_project_from_file_path(file_path) if isinstance(project, dict): if not already_existed: @@ -316,38 +313,38 @@ def __write_docker_compose(path, docker_compose, already_existed): def __load_project(path): - ''' + """ Load a docker-compose project from path :param path: :return: - ''' + """ file_path = __get_docker_file_path(path) if file_path is None: - msg = 'Could not find docker-compose file at {0}'.format(path) - return __standardize_result(False, - msg, - None, None) + msg = "Could not find docker-compose file at {0}".format(path) + return __standardize_result(False, msg, None, None) return __load_project_from_file_path(file_path) def __load_project_from_file_path(file_path): - ''' + """ Load a docker-compose project from file path :param path: :return: - ''' + """ try: - project = get_project(project_dir=os.path.dirname(file_path), - config_path=[os.path.basename(file_path)]) + project = get_project( + project_dir=os.path.dirname(file_path), + config_path=[os.path.basename(file_path)], + ) except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) return project def __load_compose_definitions(path, definition): - ''' + """ Will load the compose file located at path Then determines the format/contents of the sent definition @@ -356,86 +353,88 @@ def __load_compose_definitions(path, definition): :param path: :param definition: :return tuple(compose_result, loaded_definition, err): - ''' + """ compose_result, err = __load_docker_compose(path) if err: return None, None, err if isinstance(definition, dict): return compose_result, definition, None - elif definition.strip().startswith('{'): + elif definition.strip().startswith("{"): try: loaded_definition = json.deserialize(definition) except json.DeserializationError as jerr: - msg = 'Could not parse {0} {1}'.format(definition, jerr) - return None, None, __standardize_result(False, msg, - None, None) + msg = "Could not parse {0} {1}".format(definition, jerr) + return None, None, __standardize_result(False, msg, None, None) else: try: loaded_definition = yaml.load(definition) except yaml.YAMLError as yerr: - msg = 'Could not parse {0} {1}'.format(definition, yerr) - return None, None, __standardize_result(False, msg, - None, None) + msg = "Could not parse {0} {1}".format(definition, yerr) + return None, None, __standardize_result(False, msg, None, None) return compose_result, loaded_definition, None def __dump_compose_file(path, compose_result, success_msg, already_existed): - ''' + """ Utility function to dump the compose result to a file. :param path: :param compose_result: :param success_msg: the message to give upon success :return: - ''' - ret = __dump_docker_compose(path, - compose_result['compose_content'], - already_existed) + """ + ret = __dump_docker_compose( + path, compose_result["compose_content"], already_existed + ) if isinstance(ret, dict): return ret - return __standardize_result(True, success_msg, - compose_result['compose_content'], None) + return __standardize_result( + True, success_msg, compose_result["compose_content"], None + ) def __handle_except(inst): - ''' + """ Handle exception and return a standard result :param inst: :return: - ''' - return __standardize_result(False, - 'Docker-compose command {0} failed'. - format(inspect.stack()[1][3]), - '{0}'.format(inst), None) + """ + return __standardize_result( + False, + "Docker-compose command {0} failed".format(inspect.stack()[1][3]), + "{0}".format(inst), + None, + ) def _get_convergence_plans(project, service_names): - ''' + """ Get action executed for each container :param project: :param service_names: :return: - ''' + """ ret = {} - plans = project._get_convergence_plans(project.get_services(service_names), - ConvergenceStrategy.changed) + plans = project._get_convergence_plans( + project.get_services(service_names), ConvergenceStrategy.changed + ) for cont in plans: (action, container) = plans[cont] - if action == 'create': - ret[cont] = 'Creating container' - elif action == 'recreate': - ret[cont] = 'Re-creating container' - elif action == 'start': - ret[cont] = 'Starting container' - elif action == 'noop': - ret[cont] = 'Container is up to date' + if action == "create": + ret[cont] = "Creating container" + elif action == "recreate": + ret[cont] = "Re-creating container" + elif action == "start": + ret[cont] = "Starting container" + elif action == "noop": + ret[cont] = "Container is up to date" return ret def get(path): - ''' + """ Get the content of the docker-compose file into a directory path @@ -446,25 +445,25 @@ def get(path): .. code-block:: bash salt myminion dockercompose.get /path/where/docker-compose/stored - ''' + """ file_path = __get_docker_file_path(path) if file_path is None: - return __standardize_result(False, - 'Path {} is not present'.format(path), - None, None) + return __standardize_result( + False, "Path {} is not present".format(path), None, None + ) salt_result = __read_docker_compose_file(file_path) - if not salt_result['status']: + if not salt_result["status"]: return salt_result project = __load_project(path) if isinstance(project, dict): - salt_result['return']['valid'] = False + salt_result["return"]["valid"] = False else: - salt_result['return']['valid'] = True + salt_result["return"]["valid"] = True return salt_result def create(path, docker_compose): - ''' + """ Create and validate a docker-compose file into a directory path @@ -478,25 +477,28 @@ def create(path, docker_compose): .. code-block:: bash salt myminion dockercompose.create /path/where/docker-compose/stored content - ''' + """ if docker_compose: - ret = __write_docker_compose(path, - docker_compose, - already_existed=False) + ret = __write_docker_compose(path, docker_compose, already_existed=False) if isinstance(ret, dict): return ret else: - return __standardize_result(False, - 'Creating a docker-compose project failed, you must send a valid docker-compose file', - None, None) - return __standardize_result(True, - 'Successfully created the docker-compose file', - {'compose.base_dir': path}, - None) + return __standardize_result( + False, + "Creating a docker-compose project failed, you must send a valid docker-compose file", + None, + None, + ) + return __standardize_result( + True, + "Successfully created the docker-compose file", + {"compose.base_dir": path}, + None, + ) def pull(path, service_names=None): - ''' + """ Pull image for containers in the docker-compose file, service_names is a python list, if omitted pull all images @@ -511,7 +513,7 @@ def pull(path, service_names=None): salt myminion dockercompose.pull /path/where/docker-compose/stored salt myminion dockercompose.pull /path/where/docker-compose/stored '[janus]' - ''' + """ project = __load_project(path) if isinstance(project, dict): @@ -521,12 +523,13 @@ def pull(path, service_names=None): project.pull(service_names) except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Pulling containers images via docker-compose succeeded', - None, None) + return __standardize_result( + True, "Pulling containers images via docker-compose succeeded", None, None + ) def build(path, service_names=None): - ''' + """ Build image for containers in the docker-compose file, service_names is a python list, if omitted build images for all containers. Please note that at the moment the module does not allow you to upload your Dockerfile, @@ -545,7 +548,7 @@ def build(path, service_names=None): salt myminion dockercompose.build /path/where/docker-compose/stored salt myminion dockercompose.build /path/where/docker-compose/stored '[janus]' - ''' + """ project = __load_project(path) if isinstance(project, dict): @@ -555,12 +558,13 @@ def build(path, service_names=None): project.build(service_names) except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Building containers images via docker-compose succeeded', - None, None) + return __standardize_result( + True, "Building containers images via docker-compose succeeded", None, None + ) def restart(path, service_names=None): - ''' + """ Restart container(s) in the docker-compose file, service_names is a python list, if omitted restart all containers @@ -576,7 +580,7 @@ def restart(path, service_names=None): salt myminion dockercompose.restart /path/where/docker-compose/stored salt myminion dockercompose.restart /path/where/docker-compose/stored '[janus]' - ''' + """ project = __load_project(path) debug_ret = {} @@ -588,17 +592,22 @@ def restart(path, service_names=None): project.restart(service_names) if debug: for container in project.containers(): - if service_names is None or container.get('Name')[1:] in service_names: + if ( + service_names is None + or container.get("Name")[1:] in service_names + ): container.inspect_if_not_inspected() - debug_ret[container.get('Name')] = container.inspect() - result[container.get('Name')] = 'restarted' + debug_ret[container.get("Name")] = container.inspect() + result[container.get("Name")] = "restarted" except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Restarting containers via docker-compose', result, debug_ret) + return __standardize_result( + True, "Restarting containers via docker-compose", result, debug_ret + ) def stop(path, service_names=None): - ''' + """ Stop running containers in the docker-compose file, service_names is a python list, if omitted stop all containers @@ -613,7 +622,7 @@ def stop(path, service_names=None): salt myminion dockercompose.stop /path/where/docker-compose/stored salt myminion dockercompose.stop /path/where/docker-compose/stored '[janus]' - ''' + """ project = __load_project(path) debug_ret = {} @@ -625,17 +634,22 @@ def stop(path, service_names=None): project.stop(service_names) if debug: for container in project.containers(stopped=True): - if service_names is None or container.get('Name')[1:] in service_names: + if ( + service_names is None + or container.get("Name")[1:] in service_names + ): container.inspect_if_not_inspected() - debug_ret[container.get('Name')] = container.inspect() - result[container.get('Name')] = 'stopped' + debug_ret[container.get("Name")] = container.inspect() + result[container.get("Name")] = "stopped" except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Stopping containers via docker-compose', result, debug_ret) + return __standardize_result( + True, "Stopping containers via docker-compose", result, debug_ret + ) def pause(path, service_names=None): - ''' + """ Pause running containers in the docker-compose file, service_names is a python list, if omitted pause all containers @@ -650,7 +664,7 @@ def pause(path, service_names=None): salt myminion dockercompose.pause /path/where/docker-compose/stored salt myminion dockercompose.pause /path/where/docker-compose/stored '[janus]' - ''' + """ project = __load_project(path) debug_ret = {} @@ -662,17 +676,22 @@ def pause(path, service_names=None): project.pause(service_names) if debug: for container in project.containers(): - if service_names is None or container.get('Name')[1:] in service_names: + if ( + service_names is None + or container.get("Name")[1:] in service_names + ): container.inspect_if_not_inspected() - debug_ret[container.get('Name')] = container.inspect() - result[container.get('Name')] = 'paused' + debug_ret[container.get("Name")] = container.inspect() + result[container.get("Name")] = "paused" except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Pausing containers via docker-compose', result, debug_ret) + return __standardize_result( + True, "Pausing containers via docker-compose", result, debug_ret + ) def unpause(path, service_names=None): - ''' + """ Un-Pause containers in the docker-compose file, service_names is a python list, if omitted unpause all containers @@ -687,7 +706,7 @@ def unpause(path, service_names=None): salt myminion dockercompose.pause /path/where/docker-compose/stored salt myminion dockercompose.pause /path/where/docker-compose/stored '[janus]' - ''' + """ project = __load_project(path) debug_ret = {} @@ -699,17 +718,22 @@ def unpause(path, service_names=None): project.unpause(service_names) if debug: for container in project.containers(): - if service_names is None or container.get('Name')[1:] in service_names: + if ( + service_names is None + or container.get("Name")[1:] in service_names + ): container.inspect_if_not_inspected() - debug_ret[container.get('Name')] = container.inspect() - result[container.get('Name')] = 'unpaused' + debug_ret[container.get("Name")] = container.inspect() + result[container.get("Name")] = "unpaused" except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Un-Pausing containers via docker-compose', result, debug_ret) + return __standardize_result( + True, "Un-Pausing containers via docker-compose", result, debug_ret + ) def start(path, service_names=None): - ''' + """ Start containers in the docker-compose file, service_names is a python list, if omitted start all containers @@ -724,7 +748,7 @@ def start(path, service_names=None): salt myminion dockercompose.start /path/where/docker-compose/stored salt myminion dockercompose.start /path/where/docker-compose/stored '[janus]' - ''' + """ project = __load_project(path) debug_ret = {} @@ -736,17 +760,22 @@ def start(path, service_names=None): project.start(service_names) if debug: for container in project.containers(): - if service_names is None or container.get('Name')[1:] in service_names: + if ( + service_names is None + or container.get("Name")[1:] in service_names + ): container.inspect_if_not_inspected() - debug_ret[container.get('Name')] = container.inspect() - result[container.get('Name')] = 'started' + debug_ret[container.get("Name")] = container.inspect() + result[container.get("Name")] = "started" except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Starting containers via docker-compose', result, debug_ret) + return __standardize_result( + True, "Starting containers via docker-compose", result, debug_ret + ) def kill(path, service_names=None): - ''' + """ Kill containers in the docker-compose file, service_names is a python list, if omitted kill all containers @@ -761,7 +790,7 @@ def kill(path, service_names=None): salt myminion dockercompose.kill /path/where/docker-compose/stored salt myminion dockercompose.kill /path/where/docker-compose/stored '[janus]' - ''' + """ project = __load_project(path) debug_ret = {} @@ -773,17 +802,22 @@ def kill(path, service_names=None): project.kill(service_names) if debug: for container in project.containers(stopped=True): - if service_names is None or container.get('Name')[1:] in service_names: + if ( + service_names is None + or container.get("Name")[1:] in service_names + ): container.inspect_if_not_inspected() - debug_ret[container.get('Name')] = container.inspect() - result[container.get('Name')] = 'killed' + debug_ret[container.get("Name")] = container.inspect() + result[container.get("Name")] = "killed" except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Killing containers via docker-compose', result, debug_ret) + return __standardize_result( + True, "Killing containers via docker-compose", result, debug_ret + ) def rm(path, service_names=None): - ''' + """ Remove stopped containers in the docker-compose file, service_names is a python list, if omitted remove all stopped containers @@ -798,7 +832,7 @@ def rm(path, service_names=None): salt myminion dockercompose.rm /path/where/docker-compose/stored salt myminion dockercompose.rm /path/where/docker-compose/stored '[janus]' - ''' + """ project = __load_project(path) if isinstance(project, dict): @@ -808,11 +842,13 @@ def rm(path, service_names=None): project.remove_stopped(service_names) except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Removing stopped containers via docker-compose', None, None) + return __standardize_result( + True, "Removing stopped containers via docker-compose", None, None + ) def ps(path): - ''' + """ List all running containers and report some information about them path @@ -823,7 +859,7 @@ def ps(path): .. code-block:: bash salt myminion dockercompose.ps /path/where/docker-compose/stored - ''' + """ project = __load_project(path) result = {} @@ -832,30 +868,32 @@ def ps(path): else: if USE_FILTERCLASS: containers = sorted( - project.containers(None, stopped=True) + - project.containers(None, OneOffFilter.only), - key=attrgetter('name')) + project.containers(None, stopped=True) + + project.containers(None, OneOffFilter.only), + key=attrgetter("name"), + ) else: containers = sorted( - project.containers(None, stopped=True) + - project.containers(None, one_off=True), - key=attrgetter('name')) + project.containers(None, stopped=True) + + project.containers(None, one_off=True), + key=attrgetter("name"), + ) for container in containers: command = container.human_readable_command if len(command) > 30: - command = '{0} ...'.format(command[:26]) + command = "{0} ...".format(command[:26]) result[container.name] = { - 'id': container.id, - 'name': container.name, - 'command': command, - 'state': container.human_readable_state, - 'ports': container.human_readable_ports, + "id": container.id, + "name": container.name, + "command": command, + "state": container.human_readable_state, + "ports": container.human_readable_ports, } - return __standardize_result(True, 'Listing docker-compose containers', result, None) + return __standardize_result(True, "Listing docker-compose containers", result, None) def up(path, service_names=None): - ''' + """ Create and start containers defined in the docker-compose.yml file located in path, service_names is a python list, if omitted create and start all containers @@ -871,7 +909,7 @@ def up(path, service_names=None): salt myminion dockercompose.up /path/where/docker-compose/stored salt myminion dockercompose.up /path/where/docker-compose/stored '[janus]' - ''' + """ debug_ret = {} project = __load_project(path) @@ -883,16 +921,21 @@ def up(path, service_names=None): ret = project.up(service_names) if debug: for container in ret: - if service_names is None or container.get('Name')[1:] in service_names: + if ( + service_names is None + or container.get("Name")[1:] in service_names + ): container.inspect_if_not_inspected() - debug_ret[container.get('Name')] = container.inspect() + debug_ret[container.get("Name")] = container.inspect() except Exception as inst: # pylint: disable=broad-except return __handle_except(inst) - return __standardize_result(True, 'Adding containers via docker-compose', result, debug_ret) + return __standardize_result( + True, "Adding containers via docker-compose", result, debug_ret + ) def service_create(path, service_name, definition): - ''' + """ Create the definition of a docker-compose service This fails when the service already exists This does not pull or up the service @@ -910,22 +953,27 @@ def service_create(path, service_name, definition): .. code-block:: bash salt myminion dockercompose.service_create /path/where/docker-compose/stored service_name definition - ''' - compose_result, loaded_definition, err = __load_compose_definitions(path, definition) + """ + compose_result, loaded_definition, err = __load_compose_definitions( + path, definition + ) if err: return err - services = compose_result['compose_content']['services'] + services = compose_result["compose_content"]["services"] if service_name in services: - msg = 'Service {0} already exists'.format(service_name) + msg = "Service {0} already exists".format(service_name) return __standardize_result(False, msg, None, None) services[service_name] = loaded_definition - return __dump_compose_file(path, compose_result, - 'Service {0} created'.format(service_name), - already_existed=True) + return __dump_compose_file( + path, + compose_result, + "Service {0} created".format(service_name), + already_existed=True, + ) def service_upsert(path, service_name, definition): - ''' + """ Create or update the definition of a docker-compose service This does not pull or up the service This wil re-write your yaml file. Comments will be lost. Indentation is set to 2 spaces @@ -942,22 +990,27 @@ def service_upsert(path, service_name, definition): .. code-block:: bash salt myminion dockercompose.service_upsert /path/where/docker-compose/stored service_name definition - ''' - compose_result, loaded_definition, err = __load_compose_definitions(path, definition) + """ + compose_result, loaded_definition, err = __load_compose_definitions( + path, definition + ) if err: return err - services = compose_result['compose_content']['services'] + services = compose_result["compose_content"]["services"] if service_name in services: - msg = 'Service {0} already exists'.format(service_name) + msg = "Service {0} already exists".format(service_name) return __standardize_result(False, msg, None, None) services[service_name] = loaded_definition - return __dump_compose_file(path, compose_result, - 'Service definition for {0} is set'.format(service_name), - already_existed=True) + return __dump_compose_file( + path, + compose_result, + "Service definition for {0} is set".format(service_name), + already_existed=True, + ) def service_remove(path, service_name): - ''' + """ Remove the definition of a docker-compose service This does not rm the container This wil re-write your yaml file. Comments will be lost. Indentation is set to 2 spaces @@ -972,23 +1025,26 @@ def service_remove(path, service_name): .. code-block:: bash salt myminion dockercompose.service_remove /path/where/docker-compose/stored service_name - ''' + """ compose_result, err = __load_docker_compose(path) if err: return err - services = compose_result['compose_content']['services'] + services = compose_result["compose_content"]["services"] if service_name not in services: - return __standardize_result(False, - 'Service {0} did not exists'.format(service_name), - None, None) + return __standardize_result( + False, "Service {0} did not exists".format(service_name), None, None + ) del services[service_name] - return __dump_compose_file(path, compose_result, - 'Service {0} is removed from {1}'.format(service_name, path), - already_existed=True) + return __dump_compose_file( + path, + compose_result, + "Service {0} is removed from {1}".format(service_name, path), + already_existed=True, + ) def service_set_tag(path, service_name, tag): - ''' + """ Change the tag of a docker-compose service This does not pull or up the service This wil re-write your yaml file. Comments will be lost. Indentation is set to 2 spaces @@ -1005,21 +1061,27 @@ def service_set_tag(path, service_name, tag): .. code-block:: bash salt myminion dockercompose.service_create /path/where/docker-compose/stored service_name tag - ''' + """ compose_result, err = __load_docker_compose(path) if err: return err - services = compose_result['compose_content']['services'] + services = compose_result["compose_content"]["services"] if service_name not in services: - return __standardize_result(False, - 'Service {0} did not exists'.format(service_name), - None, None) - if 'image' not in services[service_name]: - return __standardize_result(False, - 'Service {0} did not contain the variable "image"'.format(service_name), - None, None) - image = services[service_name]['image'].split(':')[0] - services[service_name]['image'] = '{0}:{1}'.format(image, tag) - return __dump_compose_file(path, compose_result, - 'Service {0} is set to tag "{1}"'.format(service_name, tag), - already_existed=True) + return __standardize_result( + False, "Service {0} did not exists".format(service_name), None, None + ) + if "image" not in services[service_name]: + return __standardize_result( + False, + 'Service {0} did not contain the variable "image"'.format(service_name), + None, + None, + ) + image = services[service_name]["image"].split(":")[0] + services[service_name]["image"] = "{0}:{1}".format(image, tag) + return __dump_compose_file( + path, + compose_result, + 'Service {0} is set to tag "{1}"'.format(service_name, tag), + already_existed=True, + ) diff --git a/salt/modules/dockermod.py b/salt/modules/dockermod.py index 28a2107cece..1967ab7a919 100644 --- a/salt/modules/dockermod.py +++ b/salt/modules/dockermod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Docker Containers .. versionadded:: 2015.8.0 @@ -195,16 +195,15 @@ This execution module provides functions that shadow those from the :mod:`cmd Detailed Function Documentation ------------------------------- -''' +""" # Import Python Futures from __future__ import absolute_import -__docformat__ = 'restructuredtext en' - # Import Python libs import bz2 import copy + # Remove unused-import from disabled pylint checks when we uncomment the logic # in _get_exec_driver() which checks the docker version import fnmatch @@ -217,30 +216,34 @@ import pipes import re import shutil import string +import subprocess import sys import time import uuid -import subprocess -# Import Salt libs -from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.ext import six -from salt.ext.six.moves import map # pylint: disable=import-error,redefined-builtin +import salt.client.ssh.state +import salt.exceptions +import salt.fileclient +import salt.pillar import salt.utils.docker.translate.container import salt.utils.docker.translate.network import salt.utils.functools import salt.utils.json import salt.utils.path -import salt.pillar -import salt.exceptions -import salt.fileclient +# Import Salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError +from salt.ext import six +from salt.ext.six.moves import map # pylint: disable=import-error,redefined-builtin from salt.state import HighState -import salt.client.ssh.state + +__docformat__ = "restructuredtext en" + # pylint: disable=import-error try: import docker + HAS_DOCKER_PY = True except ImportError: HAS_DOCKER_PY = False @@ -256,83 +259,88 @@ except ImportError: try: import timelib + HAS_TIMELIB = True except ImportError: HAS_TIMELIB = False # pylint: enable=import-error -HAS_NSENTER = bool(salt.utils.path.which('nsenter')) +HAS_NSENTER = bool(salt.utils.path.which("nsenter")) # Set up logging log = logging.getLogger(__name__) # Don't shadow built-in's. __func_alias__ = { - 'import_': 'import', - 'ps_': 'ps', - 'rm_': 'rm', - 'signal_': 'signal', - 'start_': 'start', - 'tag_': 'tag', - 'apply_': 'apply' + "import_": "import", + "ps_": "ps", + "rm_": "rm", + "signal_": "signal", + "start_": "start", + "tag_": "tag", + "apply_": "apply", } # Minimum supported versions MIN_DOCKER = (1, 9, 0) MIN_DOCKER_PY = (1, 6, 0) -VERSION_RE = r'([\d.]+)' +VERSION_RE = r"([\d.]+)" NOTSET = object() # Define the module's virtual name and alias -__virtualname__ = 'docker' -__virtual_aliases__ = ('dockerng', 'moby') +__virtualname__ = "docker" +__virtual_aliases__ = ("dockerng", "moby") -__proxyenabled__ = ['docker'] +__proxyenabled__ = ["docker"] __outputter__ = { - 'sls': 'highstate', - 'apply_': 'highstate', - 'highstate': 'highstate', + "sls": "highstate", + "apply_": "highstate", + "highstate": "highstate", } def __virtual__(): - ''' + """ Only load if docker libs are present - ''' + """ if HAS_DOCKER_PY: try: docker_py_versioninfo = _get_docker_py_versioninfo() except Exception: # pylint: disable=broad-except # May fail if we try to connect to a docker daemon but can't - return (False, 'Docker module found, but no version could be' - ' extracted') + return (False, "Docker module found, but no version could be extracted") # Don't let a failure to interpret the version keep this module from # loading. Log a warning (log happens in _get_docker_py_versioninfo()). if docker_py_versioninfo is None: - return (False, 'Docker module found, but no version could be' - ' extracted') + return (False, "Docker module found, but no version could be extracted") if docker_py_versioninfo >= MIN_DOCKER_PY: try: - docker_versioninfo = version().get('VersionInfo') + docker_versioninfo = version().get("VersionInfo") except Exception: # pylint: disable=broad-except docker_versioninfo = None if docker_versioninfo is None or docker_versioninfo >= MIN_DOCKER: return __virtualname__ else: - return (False, - 'Insufficient Docker version (required: {0}, ' - 'installed: {1})'.format( - '.'.join(map(str, MIN_DOCKER)), - '.'.join(map(str, docker_versioninfo)))) - return (False, - 'Insufficient docker-py version (required: {0}, ' - 'installed: {1})'.format( - '.'.join(map(str, MIN_DOCKER_PY)), - '.'.join(map(str, docker_py_versioninfo)))) - return (False, 'Could not import docker module, is docker-py installed?') + return ( + False, + "Insufficient Docker version (required: {0}, " + "installed: {1})".format( + ".".join(map(str, MIN_DOCKER)), + ".".join(map(str, docker_versioninfo)), + ), + ) + return ( + False, + "Insufficient docker-py version (required: {0}, " + "installed: {1})".format( + ".".join(map(str, MIN_DOCKER_PY)), + ".".join(map(str, docker_py_versioninfo)), + ), + ) + return (False, "Could not import docker module, is docker-py installed?") class DockerJSONDecoder(json.JSONDecoder): @@ -347,9 +355,9 @@ class DockerJSONDecoder(json.JSONDecoder): def _get_docker_py_versioninfo(): - ''' + """ Returns the version_info tuple from docker-py - ''' + """ try: return docker.version_info except AttributeError: @@ -359,134 +367,145 @@ def _get_docker_py_versioninfo(): def _get_client(timeout=NOTSET, **kwargs): client_kwargs = {} if timeout is not NOTSET: - client_kwargs['timeout'] = timeout - for key, val in (('base_url', 'docker.url'), - ('version', 'docker.version')): - param = __salt__['config.option'](val, NOTSET) + client_kwargs["timeout"] = timeout + for key, val in (("base_url", "docker.url"), ("version", "docker.version")): + param = __salt__["config.option"](val, NOTSET) if param is not NOTSET: client_kwargs[key] = param - if 'base_url' not in client_kwargs and 'DOCKER_HOST' in os.environ: + if "base_url" not in client_kwargs and "DOCKER_HOST" in os.environ: # Check if the DOCKER_HOST environment variable has been set - client_kwargs['base_url'] = os.environ.get('DOCKER_HOST') + client_kwargs["base_url"] = os.environ.get("DOCKER_HOST") - if 'version' not in client_kwargs: + if "version" not in client_kwargs: # Let docker-py auto detect docker version incase # it's not defined by user. - client_kwargs['version'] = 'auto' + client_kwargs["version"] = "auto" - docker_machine = __salt__['config.option']('docker.machine', NOTSET) + docker_machine = __salt__["config.option"]("docker.machine", NOTSET) if docker_machine is not NOTSET: - docker_machine_json = __salt__['cmd.run']( - ['docker-machine', 'inspect', docker_machine], - python_shell=False) + docker_machine_json = __salt__["cmd.run"]( + ["docker-machine", "inspect", docker_machine], python_shell=False + ) try: - docker_machine_json = \ - salt.utils.json.loads(docker_machine_json) - docker_machine_tls = \ - docker_machine_json['HostOptions']['AuthOptions'] - docker_machine_ip = docker_machine_json['Driver']['IPAddress'] - client_kwargs['base_url'] = \ - 'https://' + docker_machine_ip + ':2376' - client_kwargs['tls'] = docker.tls.TLSConfig( - client_cert=(docker_machine_tls['ClientCertPath'], - docker_machine_tls['ClientKeyPath']), - ca_cert=docker_machine_tls['CaCertPath'], + docker_machine_json = salt.utils.json.loads(docker_machine_json) + docker_machine_tls = docker_machine_json["HostOptions"]["AuthOptions"] + docker_machine_ip = docker_machine_json["Driver"]["IPAddress"] + client_kwargs["base_url"] = "https://" + docker_machine_ip + ":2376" + client_kwargs["tls"] = docker.tls.TLSConfig( + client_cert=( + docker_machine_tls["ClientCertPath"], + docker_machine_tls["ClientKeyPath"], + ), + ca_cert=docker_machine_tls["CaCertPath"], assert_hostname=False, - verify=True) + verify=True, + ) except Exception as exc: # pylint: disable=broad-except raise CommandExecutionError( - 'Docker machine {0} failed: {1}'.format(docker_machine, exc)) + "Docker machine {0} failed: {1}".format(docker_machine, exc) + ) try: # docker-py 2.0 renamed this client attribute ret = docker.APIClient(**client_kwargs) except AttributeError: + # pylint: disable=not-callable ret = docker.Client(**client_kwargs) + # pylint: enable=not-callable - log.debug('docker-py API version: %s', getattr(ret, 'api_version', None)) + log.debug("docker-py API version: %s", getattr(ret, "api_version", None)) return ret def _get_state(inspect_results): - ''' + """ Helper for deriving the current state of the container from the inspect results. - ''' - if inspect_results.get('State', {}).get('Paused', False): - return 'paused' - elif inspect_results.get('State', {}).get('Running', False): - return 'running' + """ + if inspect_results.get("State", {}).get("Paused", False): + return "paused" + elif inspect_results.get("State", {}).get("Running", False): + return "running" else: - return 'stopped' + return "stopped" # Decorators def _docker_client(wrapped): - ''' + """ Decorator to run a function that requires the use of a docker.Client() instance. - ''' + """ + @functools.wraps(wrapped) def wrapper(*args, **kwargs): - ''' + """ Ensure that the client is present - ''' - kwargs = __utils__['args.clean_kwargs'](**kwargs) - timeout = kwargs.pop('client_timeout', NOTSET) - if 'docker.client' not in __context__ \ - or not hasattr(__context__['docker.client'], 'timeout'): - __context__['docker.client'] = _get_client(timeout=timeout, **kwargs) + """ + kwargs = __utils__["args.clean_kwargs"](**kwargs) + timeout = kwargs.pop("client_timeout", NOTSET) + if "docker.client" not in __context__ or not hasattr( + __context__["docker.client"], "timeout" + ): + __context__["docker.client"] = _get_client(timeout=timeout, **kwargs) orig_timeout = None - if timeout is not NOTSET \ - and hasattr(__context__['docker.client'], 'timeout') \ - and __context__['docker.client'].timeout != timeout: + if ( + timeout is not NOTSET + and hasattr(__context__["docker.client"], "timeout") + and __context__["docker.client"].timeout != timeout + ): # Temporarily override timeout - orig_timeout = __context__['docker.client'].timeout - __context__['docker.client'].timeout = timeout + orig_timeout = __context__["docker.client"].timeout + __context__["docker.client"].timeout = timeout ret = wrapped(*args, **kwargs) if orig_timeout is not None: - __context__['docker.client'].timeout = orig_timeout + __context__["docker.client"].timeout = orig_timeout return ret + return wrapper def _refresh_mine_cache(wrapped): - ''' + """ Decorator to trigger a refresh of salt mine data. - ''' + """ + @functools.wraps(wrapped) def wrapper(*args, **kwargs): - ''' + """ refresh salt mine on exit. - ''' - returned = wrapped(*args, **__utils__['args.clean_kwargs'](**kwargs)) + """ + returned = wrapped(*args, **__utils__["args.clean_kwargs"](**kwargs)) if _check_update_mine(): - __salt__['mine.send']('docker.ps', verbose=True, all=True, host=True) + __salt__["mine.send"]("docker.ps", verbose=True, all=True, host=True) return returned + return wrapper def _check_update_mine(): try: - ret = __context__['docker.update_mine'] + ret = __context__["docker.update_mine"] except KeyError: - ret = __context__['docker.update_mine'] = __salt__[ - 'config.option']('docker.update_mine', default=True) + ret = __context__["docker.update_mine"] = __salt__["config.option"]( + "docker.update_mine", default=True + ) return ret # Helper functions def _change_state(name, action, expected, *args, **kwargs): - ''' + """ Change the state of a container - ''' + """ pre = state(name) - if action != 'restart' and pre == expected: - return {'result': False, - 'state': {'old': expected, 'new': expected}, - 'comment': ('Container \'{0}\' already {1}' - .format(name, expected))} + if action != "restart" and pre == expected: + return { + "result": False, + "state": {"old": expected, "new": expected}, + "comment": ("Container '{0}' already {1}".format(name, expected)), + } _client_wrapper(action, name, *args, **kwargs) _clear_context() try: @@ -494,37 +513,39 @@ def _change_state(name, action, expected, *args, **kwargs): except CommandExecutionError: # Container doesn't exist anymore post = None - ret = {'result': post == expected, - 'state': {'old': pre, 'new': post}} + ret = {"result": post == expected, "state": {"old": pre, "new": post}} return ret def _clear_context(): - ''' + """ Clear the state/exists values stored in context - ''' + """ # Can't use 'for key in __context__' or six.iterkeys(__context__) because # an exception will be raised if the size of the dict is modified during # iteration. keep_context = ( - 'docker.client', 'docker.exec_driver', 'docker._pull_status', - 'docker.docker_version', 'docker.docker_py_version' + "docker.client", + "docker.exec_driver", + "docker._pull_status", + "docker.docker_version", + "docker.docker_py_version", ) for key in list(__context__): try: - if key.startswith('docker.') and key not in keep_context: + if key.startswith("docker.") and key not in keep_context: __context__.pop(key) except AttributeError: pass def _get_md5(name, path): - ''' + """ Get the MD5 checksum of a file from a container - ''' - output = run_stdout(name, - 'md5sum {0}'.format(pipes.quote(path)), - ignore_retcode=True) + """ + output = run_stdout( + name, "md5sum {0}".format(pipes.quote(path)), ignore_retcode=True + ) try: return output.split()[0] except IndexError: @@ -533,12 +554,12 @@ def _get_md5(name, path): def _get_exec_driver(): - ''' + """ Get the method to be used in shell commands - ''' - contextkey = 'docker.exec_driver' + """ + contextkey = "docker.exec_driver" if contextkey not in __context__: - from_config = __salt__['config.option'](contextkey, None) + from_config = __salt__["config.option"](contextkey, None) # This if block can be removed once we make docker-exec a default # option, as it is part of the logic in the commented block above. if from_config is not None: @@ -547,63 +568,63 @@ def _get_exec_driver(): # The execution driver was removed in Docker 1.13.1, docker-exec is now # the default. - driver = info().get('ExecutionDriver', 'docker-exec') - if driver == 'docker-exec': + driver = info().get("ExecutionDriver", "docker-exec") + if driver == "docker-exec": __context__[contextkey] = driver - elif driver.startswith('lxc-'): - __context__[contextkey] = 'lxc-attach' - elif driver.startswith('native-') and HAS_NSENTER: - __context__[contextkey] = 'nsenter' + elif driver.startswith("lxc-"): + __context__[contextkey] = "lxc-attach" + elif driver.startswith("native-") and HAS_NSENTER: + __context__[contextkey] = "nsenter" elif not driver.strip() and HAS_NSENTER: log.warning( - 'ExecutionDriver from \'docker info\' is blank, falling ' - 'back to using \'nsenter\'. To squelch this warning, set ' - 'docker.exec_driver. See the Salt documentation for the ' - 'docker module for more information.' + "ExecutionDriver from 'docker info' is blank, falling " + "back to using 'nsenter'. To squelch this warning, set " + "docker.exec_driver. See the Salt documentation for the " + "docker module for more information." ) - __context__[contextkey] = 'nsenter' + __context__[contextkey] = "nsenter" else: raise NotImplementedError( - 'Unknown docker ExecutionDriver \'{0}\', or didn\'t find ' - 'command to attach to the container'.format(driver) + "Unknown docker ExecutionDriver '{0}', or didn't find " + "command to attach to the container".format(driver) ) return __context__[contextkey] def _get_top_level_images(imagedata, subset=None): - ''' + """ Returns a list of the top-level images (those which are not parents). If ``subset`` (an iterable) is passed, the top-level images in the subset will be returned, otherwise all top-level images will be returned. - ''' + """ try: - parents = [imagedata[x]['ParentId'] for x in imagedata] + parents = [imagedata[x]["ParentId"] for x in imagedata] filter_ = subset if subset is not None else imagedata return [x for x in filter_ if x not in parents] except (KeyError, TypeError): raise CommandExecutionError( - 'Invalid image data passed to _get_top_level_images(). Please ' - 'report this issue. Full image data: {0}'.format(imagedata) + "Invalid image data passed to _get_top_level_images(). Please " + "report this issue. Full image data: {0}".format(imagedata) ) def _prep_pull(): - ''' + """ Populate __context__ with the current (pre-pull) image IDs (see the docstring for _pull_status for more information). - ''' - __context__['docker._pull_status'] = [x[:12] for x in images(all=True)] + """ + __context__["docker._pull_status"] = [x[:12] for x in images(all=True)] def _scrub_links(links, name): - ''' + """ Remove container name from HostConfig:Links values to enable comparing container configurations correctly. - ''' + """ if isinstance(links, list): ret = [] for l in links: - ret.append(l.replace('/{0}/'.format(name), '/', 1)) + ret.append(l.replace("/{0}/".format(name), "/", 1)) else: ret = links @@ -612,59 +633,61 @@ def _scrub_links(links, name): def _ulimit_sort(ulimit_val): if isinstance(ulimit_val, list): - return sorted(ulimit_val, - key=lambda x: (x.get('Name'), - x.get('Hard', 0), - x.get('Soft', 0))) + return sorted( + ulimit_val, + key=lambda x: (x.get("Name"), x.get("Hard", 0), x.get("Soft", 0)), + ) return ulimit_val def _size_fmt(num): - ''' + """ Format bytes as human-readable file sizes - ''' + """ try: num = int(num) if num < 1024: - return '{0} bytes'.format(num) + return "{0} bytes".format(num) num /= 1024.0 - for unit in ('KiB', 'MiB', 'GiB', 'TiB', 'PiB'): + for unit in ("KiB", "MiB", "GiB", "TiB", "PiB"): if num < 1024.0: - return '{0:3.1f} {1}'.format(num, unit) + return "{0:3.1f} {1}".format(num, unit) num /= 1024.0 except Exception: # pylint: disable=broad-except - log.error('Unable to format file size for \'%s\'', num) - return 'unknown' + log.error("Unable to format file size for '%s'", num) + return "unknown" @_docker_client def _client_wrapper(attr, *args, **kwargs): - ''' + """ Common functionality for running low-level API calls - ''' - catch_api_errors = kwargs.pop('catch_api_errors', True) - func = getattr(__context__['docker.client'], attr, None) - if func is None or not hasattr(func, '__call__'): - raise SaltInvocationError('Invalid client action \'{0}\''.format(attr)) - if attr in ('push', 'pull'): + """ + catch_api_errors = kwargs.pop("catch_api_errors", True) + func = getattr(__context__["docker.client"], attr, None) + if func is None or not hasattr(func, "__call__"): + raise SaltInvocationError("Invalid client action '{0}'".format(attr)) + if attr in ("push", "pull"): try: # Refresh auth config from config.json - __context__['docker.client'].reload_config() + __context__["docker.client"].reload_config() except AttributeError: pass - err = '' + err = "" try: log.debug( 'Attempting to run docker-py\'s "%s" function ' - 'with args=%s and kwargs=%s', attr, args, kwargs + "with args=%s and kwargs=%s", + attr, + args, + kwargs, ) ret = func(*args, **kwargs) except docker.errors.APIError as exc: if catch_api_errors: # Generic handling of Docker API errors raise CommandExecutionError( - 'Error {0}: {1}'.format(exc.response.status_code, - exc.explanation) + "Error {0}: {1}".format(exc.response.status_code, exc.explanation) ) else: # Allow API errors to be caught further up the stack @@ -679,42 +702,43 @@ def _client_wrapper(attr, *args, **kwargs): # If we're here, it's because an exception was caught earlier, and the # API command failed. - msg = 'Unable to perform {0}'.format(attr) + msg = "Unable to perform {0}".format(attr) if err: - msg += ': {0}'.format(err) + msg += ": {0}".format(err) raise CommandExecutionError(msg) def _build_status(data, item): - ''' + """ Process a status update from a docker build, updating the data structure - ''' - stream = item['stream'] - if 'Running in' in stream: - data.setdefault('Intermediate_Containers', []).append( - stream.rstrip().split()[-1]) - if 'Successfully built' in stream: - data['Id'] = stream.rstrip().split()[-1] + """ + stream = item["stream"] + if "Running in" in stream: + data.setdefault("Intermediate_Containers", []).append( + stream.rstrip().split()[-1] + ) + if "Successfully built" in stream: + data["Id"] = stream.rstrip().split()[-1] def _import_status(data, item, repo_name, repo_tag): - ''' + """ Process a status update from docker import, updating the data structure - ''' - status = item['status'] + """ + status = item["status"] try: - if 'Downloading from' in status: + if "Downloading from" in status: return elif all(x in string.hexdigits for x in status): # Status is an image ID - data['Image'] = '{0}:{1}'.format(repo_name, repo_tag) - data['Id'] = status + data["Image"] = "{0}:{1}".format(repo_name, repo_tag) + data["Id"] = status except (AttributeError, TypeError): pass def _pull_status(data, item): - ''' + """ Process a status update from a docker pull, updating the data structure. For containers created with older versions of Docker, there is no @@ -730,91 +754,88 @@ def _pull_status(data, item): function, because by design we clear the relevant context variables once we've made changes to allow the next call to images() to pick up any changes that were made. - ''' + """ + def _already_exists(id_): - ''' + """ Layer already exists - ''' - already_pulled = data.setdefault('Layers', {}).setdefault( - 'Already_Pulled', []) + """ + already_pulled = data.setdefault("Layers", {}).setdefault("Already_Pulled", []) if id_ not in already_pulled: already_pulled.append(id_) def _new_layer(id_): - ''' + """ Pulled a new layer - ''' - pulled = data.setdefault('Layers', {}).setdefault( - 'Pulled', []) + """ + pulled = data.setdefault("Layers", {}).setdefault("Pulled", []) if id_ not in pulled: pulled.append(id_) - if 'docker._pull_status' not in __context__: + if "docker._pull_status" not in __context__: log.warning( - '_pull_status context variable was not populated, information on ' - 'downloaded layers may be inaccurate. Please report this to the ' - 'SaltStack development team, and if possible include the image ' - '(and tag) that was being pulled.' + "_pull_status context variable was not populated, information on " + "downloaded layers may be inaccurate. Please report this to the " + "SaltStack development team, and if possible include the image " + "(and tag) that was being pulled." ) - __context__['docker._pull_status'] = NOTSET - status = item['status'] - if status == 'Already exists': - _already_exists(item['id']) - elif status in 'Pull complete': - _new_layer(item['id']) - elif status.startswith('Status: '): - data['Status'] = status[8:] - elif status == 'Download complete': - if __context__['docker._pull_status'] is not NOTSET: - id_ = item['id'] - if id_ in __context__['docker._pull_status']: + __context__["docker._pull_status"] = NOTSET + status = item["status"] + if status == "Already exists": + _already_exists(item["id"]) + elif status in "Pull complete": + _new_layer(item["id"]) + elif status.startswith("Status: "): + data["Status"] = status[8:] + elif status == "Download complete": + if __context__["docker._pull_status"] is not NOTSET: + id_ = item["id"] + if id_ in __context__["docker._pull_status"]: _already_exists(id_) else: _new_layer(id_) def _push_status(data, item): - ''' + """ Process a status update from a docker push, updating the data structure - ''' - status = item['status'].lower() - if 'id' in item: - if 'already pushed' in status or 'already exists' in status: + """ + status = item["status"].lower() + if "id" in item: + if "already pushed" in status or "already exists" in status: # Layer already exists - already_pushed = data.setdefault('Layers', {}).setdefault( - 'Already_Pushed', []) - already_pushed.append(item['id']) - elif 'successfully pushed' in status or status == 'pushed': + already_pushed = data.setdefault("Layers", {}).setdefault( + "Already_Pushed", [] + ) + already_pushed.append(item["id"]) + elif "successfully pushed" in status or status == "pushed": # Pushed a new layer - pushed = data.setdefault('Layers', {}).setdefault( - 'Pushed', []) - pushed.append(item['id']) + pushed = data.setdefault("Layers", {}).setdefault("Pushed", []) + pushed.append(item["id"]) def _error_detail(data, item): - ''' + """ Process an API error, updating the data structure - ''' - err = item['errorDetail'] - if 'code' in err: + """ + err = item["errorDetail"] + if "code" in err: try: - msg = ': '.join(( - item['errorDetail']['code'], - item['errorDetail']['message'] - )) + msg = ": ".join( + (item["errorDetail"]["code"], item["errorDetail"]["message"]) + ) except TypeError: - msg = '{0}: {1}'.format( - item['errorDetail']['code'], - item['errorDetail']['message'], + msg = "{0}: {1}".format( + item["errorDetail"]["code"], item["errorDetail"]["message"], ) else: - msg = item['errorDetail']['message'] + msg = item["errorDetail"]["message"] data.append(msg) # Functions to handle docker-py client args def get_client_args(limit=None): - ''' + """ .. versionadded:: 2016.3.6,2016.11.4,2017.7.0 .. versionchanged:: 2017.7.0 Replaced the container config args with the ones from the API's @@ -859,70 +880,74 @@ def get_client_args(limit=None): salt myminion docker.get_client_args salt myminion docker.get_client_args logs salt myminion docker.get_client_args create_container,connect_container_to_network - ''' - return __utils__['docker.get_client_args'](limit=limit) + """ + return __utils__["docker.get_client_args"](limit=limit) -def _get_create_kwargs(skip_translate=None, - ignore_collisions=False, - validate_ip_addrs=True, - client_args=None, - **kwargs): - ''' +def _get_create_kwargs( + skip_translate=None, + ignore_collisions=False, + validate_ip_addrs=True, + client_args=None, + **kwargs +): + """ Take input kwargs and return a kwargs dict to pass to docker-py's create_container() function. - ''' + """ - networks = kwargs.pop('networks', {}) - if kwargs.get('network_mode', '') in networks: - networks = {kwargs['network_mode']: networks[kwargs['network_mode']]} + networks = kwargs.pop("networks", {}) + if kwargs.get("network_mode", "") in networks: + networks = {kwargs["network_mode"]: networks[kwargs["network_mode"]]} else: networks = {} - kwargs = __utils__['docker.translate_input']( + kwargs = __utils__["docker.translate_input"]( salt.utils.docker.translate.container, skip_translate=skip_translate, ignore_collisions=ignore_collisions, validate_ip_addrs=validate_ip_addrs, - **__utils__['args.clean_kwargs'](**kwargs)) + **__utils__["args.clean_kwargs"](**kwargs) + ) if networks: - kwargs['networking_config'] = _create_networking_config(networks) + kwargs["networking_config"] = _create_networking_config(networks) if client_args is None: try: - client_args = get_client_args(['create_container', 'host_config']) + client_args = get_client_args(["create_container", "host_config"]) except CommandExecutionError as exc: - log.error('docker.create: Error getting client args: \'%s\'', - exc.__str__(), exc_info=True) - raise CommandExecutionError( - 'Failed to get client args: {0}'.format(exc)) + log.error( + "docker.create: Error getting client args: '%s'", + exc.__str__(), + exc_info=True, + ) + raise CommandExecutionError("Failed to get client args: {0}".format(exc)) full_host_config = {} host_kwargs = {} create_kwargs = {} # Using list() becausee we'll be altering kwargs during iteration for arg in list(kwargs): - if arg in client_args['host_config']: + if arg in client_args["host_config"]: host_kwargs[arg] = kwargs.pop(arg) continue - if arg in client_args['create_container']: - if arg == 'host_config': + if arg in client_args["create_container"]: + if arg == "host_config": full_host_config.update(kwargs.pop(arg)) else: create_kwargs[arg] = kwargs.pop(arg) continue - create_kwargs['host_config'] = \ - _client_wrapper('create_host_config', **host_kwargs) + create_kwargs["host_config"] = _client_wrapper("create_host_config", **host_kwargs) # In the event that a full host_config was passed, overlay it on top of the # one we just created. - create_kwargs['host_config'].update(full_host_config) + create_kwargs["host_config"].update(full_host_config) # The "kwargs" dict at this point will only contain unused args return create_kwargs, kwargs def compare_containers(first, second, ignore=None): - ''' + """ .. versionadded:: 2017.7.0 .. versionchanged:: 2018.3.0 Renamed from ``docker.compare_container`` to @@ -949,37 +974,37 @@ def compare_containers(first, second, ignore=None): salt myminion docker.compare_containers foo bar salt myminion docker.compare_containers foo bar ignore=Hostname - ''' - ignore = __utils__['args.split_input'](ignore or []) + """ + ignore = __utils__["args.split_input"](ignore or []) result1 = inspect_container(first) result2 = inspect_container(second) ret = {} - for conf_dict in ('Config', 'HostConfig'): + for conf_dict in ("Config", "HostConfig"): for item in result1[conf_dict]: if item in ignore: continue val1 = result1[conf_dict][item] val2 = result2[conf_dict].get(item) - if item in ('OomKillDisable',) or (val1 is None or val2 is None): + if item in ("OomKillDisable",) or (val1 is None or val2 is None): if bool(val1) != bool(val2): - ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2} - elif item == 'Image': - image1 = inspect_image(val1)['Id'] - image2 = inspect_image(val2)['Id'] + ret.setdefault(conf_dict, {})[item] = {"old": val1, "new": val2} + elif item == "Image": + image1 = inspect_image(val1)["Id"] + image2 = inspect_image(val2)["Id"] if image1 != image2: - ret.setdefault(conf_dict, {})[item] = {'old': image1, 'new': image2} + ret.setdefault(conf_dict, {})[item] = {"old": image1, "new": image2} else: - if item == 'Links': + if item == "Links": val1 = sorted(_scrub_links(val1, first)) val2 = sorted(_scrub_links(val2, second)) - if item == 'Ulimits': + if item == "Ulimits": val1 = _ulimit_sort(val1) val2 = _ulimit_sort(val2) - if item == 'Env': + if item == "Env": val1 = sorted(val1) val2 = sorted(val2) if val1 != val2: - ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2} + ret.setdefault(conf_dict, {})[item] = {"old": val1, "new": val2} # Check for optionally-present items that were in the second container # and not the first. for item in result2[conf_dict]: @@ -989,36 +1014,36 @@ def compare_containers(first, second, ignore=None): continue val1 = result1[conf_dict].get(item) val2 = result2[conf_dict][item] - if item in ('OomKillDisable',) or (val1 is None or val2 is None): + if item in ("OomKillDisable",) or (val1 is None or val2 is None): if bool(val1) != bool(val2): - ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2} - elif item == 'Image': - image1 = inspect_image(val1)['Id'] - image2 = inspect_image(val2)['Id'] + ret.setdefault(conf_dict, {})[item] = {"old": val1, "new": val2} + elif item == "Image": + image1 = inspect_image(val1)["Id"] + image2 = inspect_image(val2)["Id"] if image1 != image2: - ret.setdefault(conf_dict, {})[item] = {'old': image1, 'new': image2} + ret.setdefault(conf_dict, {})[item] = {"old": image1, "new": image2} else: - if item == 'Links': + if item == "Links": val1 = sorted(_scrub_links(val1, first)) val2 = sorted(_scrub_links(val2, second)) - if item == 'Ulimits': + if item == "Ulimits": val1 = _ulimit_sort(val1) val2 = _ulimit_sort(val2) - if item == 'Env': + if item == "Env": val1 = sorted(val1) val2 = sorted(val2) if val1 != val2: - ret.setdefault(conf_dict, {})[item] = {'old': val1, 'new': val2} + ret.setdefault(conf_dict, {})[item] = {"old": val1, "new": val2} return ret compare_container = salt.utils.functools.alias_function( - compare_containers, - 'compare_container') + compare_containers, "compare_container" +) def compare_container_networks(first, second): - ''' + """ .. versionadded:: 2018.3.0 Returns the differences between two containers' networks. When a network is @@ -1076,18 +1101,15 @@ def compare_container_networks(first, second): .. code-block:: bash salt myminion docker.compare_container_networks foo bar - ''' + """ + def _get_nets(data): - return data.get('NetworkSettings', {}).get('Networks', {}) + return data.get("NetworkSettings", {}).get("Networks", {}) - compare_keys = __salt__['config.option']('docker.compare_container_networks') + compare_keys = __salt__["config.option"]("docker.compare_container_networks") - result1 = inspect_container(first) \ - if not isinstance(first, dict) \ - else first - result2 = inspect_container(second) \ - if not isinstance(second, dict) \ - else second + result1 = inspect_container(first) if not isinstance(first, dict) else first + result2 = inspect_container(second) if not isinstance(second, dict) else second nets1 = _get_nets(result1) nets2 = _get_nets(result2) state1 = state(first) @@ -1102,96 +1124,104 @@ def compare_container_networks(first, second): all_nets.update(nets2) for net_name in all_nets: try: - connected_containers = inspect_network(net_name).get('Containers', {}) + connected_containers = inspect_network(net_name).get("Containers", {}) except Exception as exc: # pylint: disable=broad-except # Shouldn't happen unless a network was removed outside of Salt # between the time that a docker_container.running state started # and when this comparison took place. - log.warning( - 'Failed to inspect Docker network %s: %s', net_name, exc) + log.warning("Failed to inspect Docker network %s: %s", net_name, exc) continue else: - if state1 == 'running' \ - and net_name in nets1 \ - and result1['Id'] not in connected_containers: + if ( + state1 == "running" + and net_name in nets1 + and result1["Id"] not in connected_containers + ): del nets1[net_name] - if state2 == 'running' \ - and net_name in nets2 \ - and result2['Id'] not in connected_containers: + if ( + state2 == "running" + and net_name in nets2 + and result2["Id"] not in connected_containers + ): del nets2[net_name] ret = {} def _check_ipconfig(ret, net_name, **kwargs): # Make some variables to make the logic below easier to understand - nets1_missing = 'old' not in kwargs + nets1_missing = "old" not in kwargs if nets1_missing: nets1_static = False else: - nets1_static = bool(kwargs['old']) + nets1_static = bool(kwargs["old"]) nets1_autoip = not nets1_static and not nets1_missing - nets2_missing = 'new' not in kwargs + nets2_missing = "new" not in kwargs if nets2_missing: nets2_static = False else: - nets2_static = bool(kwargs['new']) + nets2_static = bool(kwargs["new"]) nets2_autoip = not nets2_static and not nets2_missing - autoip_keys = compare_keys.get('automatic', []) + autoip_keys = compare_keys.get("automatic", []) if nets1_autoip and (nets2_static or nets2_missing): for autoip_key in autoip_keys: autoip_val = nets1[net_name].get(autoip_key) if autoip_val: ret.setdefault(net_name, {})[autoip_key] = { - 'old': autoip_val, 'new': None + "old": autoip_val, + "new": None, } if nets2_static: - ret.setdefault(net_name, {})['IPAMConfig'] = { - 'old': None, 'new': kwargs['new'] + ret.setdefault(net_name, {})["IPAMConfig"] = { + "old": None, + "new": kwargs["new"], } if not any(x in ret.get(net_name, {}) for x in autoip_keys): - ret.setdefault(net_name, {})['IPConfiguration'] = { - 'old': 'automatic', - 'new': 'static' if nets2_static else 'not connected' + ret.setdefault(net_name, {})["IPConfiguration"] = { + "old": "automatic", + "new": "static" if nets2_static else "not connected", } elif nets2_autoip and (nets1_static or nets1_missing): for autoip_key in autoip_keys: autoip_val = nets2[net_name].get(autoip_key) if autoip_val: ret.setdefault(net_name, {})[autoip_key] = { - 'old': None, 'new': autoip_val + "old": None, + "new": autoip_val, } if not any(x in ret.get(net_name, {}) for x in autoip_keys): - ret.setdefault(net_name, {})['IPConfiguration'] = { - 'old': 'static' if nets1_static else 'not connected', - 'new': 'automatic' + ret.setdefault(net_name, {})["IPConfiguration"] = { + "old": "static" if nets1_static else "not connected", + "new": "automatic", } if nets1_static: - ret.setdefault(net_name, {})['IPAMConfig'] = { - 'old': kwargs['old'], 'new': None + ret.setdefault(net_name, {})["IPAMConfig"] = { + "old": kwargs["old"], + "new": None, } else: - old_val = kwargs.get('old') - new_val = kwargs.get('new') + old_val = kwargs.get("old") + new_val = kwargs.get("new") if old_val != new_val: # Static IP configuration present in both containers and there # are differences, so report them - ret.setdefault(net_name, {})['IPAMConfig'] = { - 'old': old_val, 'new': new_val + ret.setdefault(net_name, {})["IPAMConfig"] = { + "old": old_val, + "new": new_val, } for net_name in (x for x in nets1 if x not in nets2): # Network is not in the network_settings, but the container is attached # to the network - for key in compare_keys.get('static', []): + for key in compare_keys.get("static", []): val = nets1[net_name].get(key) - if key == 'IPAMConfig': + if key == "IPAMConfig": _check_ipconfig(ret, net_name, old=val) if val: - if key == 'Aliases': + if key == "Aliases": try: - val.remove(result1['Config']['Hostname']) + val.remove(result1["Config"]["Hostname"]) except (ValueError, AttributeError): pass else: @@ -1199,21 +1229,21 @@ def compare_container_networks(first, second): # The only alias was the default one for the # hostname continue - ret.setdefault(net_name, {})[key] = {'old': val, 'new': None} + ret.setdefault(net_name, {})[key] = {"old": val, "new": None} for net_name in nets2: if net_name not in nets1: # Container is not attached to the network, but network is present # in the network_settings - for key in compare_keys.get('static', []): + for key in compare_keys.get("static", []): val = nets2[net_name].get(key) - if key == 'IPAMConfig': + if key == "IPAMConfig": _check_ipconfig(ret, net_name, new=val) continue elif val: - if key == 'Aliases': + if key == "Aliases": try: - val.remove(result2['Config']['Hostname']) + val.remove(result2["Config"]["Hostname"]) except (ValueError, AttributeError): pass else: @@ -1221,9 +1251,9 @@ def compare_container_networks(first, second): # The only alias was the default one for the # hostname continue - ret.setdefault(net_name, {})[key] = {'old': None, 'new': val} + ret.setdefault(net_name, {})[key] = {"old": None, "new": val} else: - for key in compare_keys.get('static', []): + for key in compare_keys.get("static", []): old_val = nets1[net_name][key] new_val = nets2[net_name][key] for item in (old_val, new_val): @@ -1232,29 +1262,29 @@ def compare_container_networks(first, second): item.sort() except AttributeError: pass - if key == 'Aliases': + if key == "Aliases": # Normalize for hostname alias try: - old_val.remove(result1['Config']['Hostname']) + old_val.remove(result1["Config"]["Hostname"]) except (AttributeError, ValueError): pass try: - old_val.remove(result1['Id'][:12]) + old_val.remove(result1["Id"][:12]) except (AttributeError, ValueError): pass if not old_val: old_val = None try: - new_val.remove(result2['Config']['Hostname']) + new_val.remove(result2["Config"]["Hostname"]) except (AttributeError, ValueError): pass try: - new_val.remove(result2['Id'][:12]) + new_val.remove(result2["Id"][:12]) except (AttributeError, ValueError): pass if not new_val: new_val = None - elif key == 'IPAMConfig': + elif key == "IPAMConfig": _check_ipconfig(ret, net_name, old=old_val, new=new_val) # We don't need the final check since it's included in the # _check_ipconfig helper @@ -1262,14 +1292,12 @@ def compare_container_networks(first, second): if bool(old_val) is bool(new_val) is False: continue elif old_val != new_val: - ret.setdefault(net_name, {})[key] = { - 'old': old_val, 'new': new_val - } + ret.setdefault(net_name, {})[key] = {"old": old_val, "new": new_val} return ret -def compare_networks(first, second, ignore='Name,Id,Created,Containers'): - ''' +def compare_networks(first, second, ignore="Name,Id,Created,Containers"): + """ .. versionadded:: 2018.3.0 Compare two networks and return any differences between the two @@ -1290,8 +1318,8 @@ def compare_networks(first, second, ignore='Name,Id,Created,Containers'): .. code-block:: bash salt myminion docker.compare_network foo bar - ''' - ignore = __utils__['args.split_input'](ignore or []) + """ + ignore = __utils__["args.split_input"](ignore or []) net1 = inspect_network(first) if not isinstance(first, dict) else first net2 = inspect_network(second) if not isinstance(second, dict) else second ret = {} @@ -1306,27 +1334,28 @@ def compare_networks(first, second, ignore='Name,Id,Created,Containers'): val2 = net2.get(item) if bool(val1) is bool(val2) is False: continue - elif item == 'IPAM': + elif item == "IPAM": for subkey in val1: subval1 = val1[subkey] subval2 = val2.get(subkey) if bool(subval1) is bool(subval2) is False: continue - elif subkey == 'Config': - kvsort = lambda x: (list(six.iterkeys(x)), - list(six.itervalues(x))) - config1 = sorted(val1['Config'], key=kvsort) - config2 = sorted(val2.get('Config', []), key=kvsort) + elif subkey == "Config": + kvsort = lambda x: (list(six.iterkeys(x)), list(six.itervalues(x))) + config1 = sorted(val1["Config"], key=kvsort) + config2 = sorted(val2.get("Config", []), key=kvsort) if config1 != config2: - ret.setdefault('IPAM', {})['Config'] = { - 'old': config1, 'new': config2 + ret.setdefault("IPAM", {})["Config"] = { + "old": config1, + "new": config2, } elif subval1 != subval2: - ret.setdefault('IPAM', {})[subkey] = { - 'old': subval1, 'new': subval2 + ret.setdefault("IPAM", {})[subkey] = { + "old": subval1, + "new": subval2, } elif val1 != val2: - ret[item] = {'old': val1, 'new': val2} + ret[item] = {"old": val1, "new": val2} # Check for optionally-present items that were in the second network # and not the first. @@ -1336,13 +1365,13 @@ def compare_networks(first, second, ignore='Name,Id,Created,Containers'): if bool(val1) is bool(val2) is False: continue elif val1 != val2: - ret[item] = {'old': val1, 'new': val2} + ret[item] = {"old": val1, "new": val2} return ret def connected(name, verbose=False): - ''' + """ .. versionadded:: 2018.3.0 Return a list of running containers attached to the specified network @@ -1359,26 +1388,28 @@ def connected(name, verbose=False): .. code-block:: bash salt myminion docker.connected net_name - ''' - containers = inspect_network(name).get('Containers', {}) + """ + containers = inspect_network(name).get("Containers", {}) ret = {} for cid, cinfo in six.iteritems(containers): # The Containers dict is keyed by container ID, but we want the results # to be keyed by container name, so we need to pop off the Name and # then add the Id key to the cinfo dict. try: - name = cinfo.pop('Name') + name = cinfo.pop("Name") except (KeyError, AttributeError): # Should never happen log.warning( - '\'Name\' key not present in container definition for ' - 'container ID \'%s\' within inspect results for Docker ' - 'network \'%s\'. Full container definition: %s', - cid, name, cinfo + "'Name' key not present in container definition for " + "container ID '%s' within inspect results for Docker " + "network '%s'. Full container definition: %s", + cid, + name, + cinfo, ) continue else: - cinfo['Id'] = cid + cinfo["Id"] = cid ret[name] = cinfo if not verbose: return list(ret) @@ -1386,7 +1417,7 @@ def connected(name, verbose=False): def login(*registries): - ''' + """ .. versionadded:: 2016.3.7,2016.11.4,2017.7.0 Performs a ``docker login`` to authenticate to one or more configured @@ -1415,75 +1446,69 @@ def login(*registries): salt myminion docker.login salt myminion docker.login hub salt myminion docker.login hub https://mydomain.tld/registry/ - ''' + """ # NOTE: This function uses the "docker login" CLI command so that login # information is added to the config.json, since docker-py isn't designed # to do so. - registry_auth = __salt__['config.get']('docker-registries', {}) - ret = {'retcode': 0} - errors = ret.setdefault('Errors', []) + registry_auth = __salt__["config.get"]("docker-registries", {}) + ret = {"retcode": 0} + errors = ret.setdefault("Errors", []) if not isinstance(registry_auth, dict): - errors.append('\'docker-registries\' Pillar value must be a dictionary') + errors.append("'docker-registries' Pillar value must be a dictionary") registry_auth = {} for reg_name, reg_conf in six.iteritems( - __salt__['config.option']('*-docker-registries', wildcard=True)): + __salt__["config.option"]("*-docker-registries", wildcard=True) + ): try: registry_auth.update(reg_conf) except TypeError: errors.append( - 'Docker registry \'{0}\' was not specified as a ' - 'dictionary'.format(reg_name) + "Docker registry '{0}' was not specified as a " + "dictionary".format(reg_name) ) # If no registries passed, we will auth to all of them if not registries: registries = list(registry_auth) - results = ret.setdefault('Results', {}) + results = ret.setdefault("Results", {}) for registry in registries: if registry not in registry_auth: - errors.append( - 'No match found for registry \'{0}\''.format(registry) - ) + errors.append("No match found for registry '{0}'".format(registry)) continue try: - username = registry_auth[registry]['username'] - password = registry_auth[registry]['password'] + username = registry_auth[registry]["username"] + password = registry_auth[registry]["password"] except TypeError: - errors.append( - 'Invalid configuration for registry \'{0}\''.format(registry) - ) + errors.append("Invalid configuration for registry '{0}'".format(registry)) except KeyError as exc: - errors.append( - 'Missing {0} for registry \'{1}\''.format(exc, registry) - ) + errors.append("Missing {0} for registry '{1}'".format(exc, registry)) else: - cmd = ['docker', 'login', '-u', username, '-p', password] - if registry.lower() != 'hub': + cmd = ["docker", "login", "-u", username, "-p", password] + if registry.lower() != "hub": cmd.append(registry) log.debug( - 'Attempting to login to docker registry \'%s\' as user \'%s\'', - registry, username + "Attempting to login to docker registry '%s' as user '%s'", + registry, + username, ) - login_cmd = __salt__['cmd.run_all']( - cmd, - python_shell=False, - output_loglevel='quiet', + login_cmd = __salt__["cmd.run_all"]( + cmd, python_shell=False, output_loglevel="quiet", ) - results[registry] = login_cmd['retcode'] == 0 + results[registry] = login_cmd["retcode"] == 0 if not results[registry]: - if login_cmd['stderr']: - errors.append(login_cmd['stderr']) - elif login_cmd['stdout']: - errors.append(login_cmd['stdout']) + if login_cmd["stderr"]: + errors.append(login_cmd["stderr"]) + elif login_cmd["stdout"]: + errors.append(login_cmd["stdout"]) if errors: - ret['retcode'] = 1 + ret["retcode"] = 1 return ret # Functions for information gathering def depends(name): - ''' + """ Returns the containers and images, if any, which depend on the given image name @@ -1504,26 +1529,27 @@ def depends(name): salt myminion docker.depends myimage salt myminion docker.depends 0123456789ab - ''' + """ # Resolve tag or short-SHA to full SHA - image_id = inspect_image(name)['Id'] + image_id = inspect_image(name)["Id"] container_depends = [] for container in six.itervalues(ps_(all=True, verbose=True)): - if container['Info']['Image'] == image_id: - container_depends.extend( - [x.lstrip('/') for x in container['Names']] - ) + if container["Info"]["Image"] == image_id: + container_depends.extend([x.lstrip("/") for x in container["Names"]]) return { - 'Containers': container_depends, - 'Images': [x[:12] for x, y in six.iteritems(images(all=True)) - if y['ParentId'] == image_id] + "Containers": container_depends, + "Images": [ + x[:12] + for x, y in six.iteritems(images(all=True)) + if y["ParentId"] == image_id + ], } def diff(name): - ''' + """ Get information on changes made to container's filesystem since it was created. Equivalent to running the ``docker diff`` Docker CLI command. @@ -1548,24 +1574,25 @@ def diff(name): .. code-block:: bash salt myminion docker.diff mycontainer - ''' - changes = _client_wrapper('diff', name) - kind_map = {0: 'Changed', 1: 'Added', 2: 'Deleted'} + """ + changes = _client_wrapper("diff", name) + kind_map = {0: "Changed", 1: "Added", 2: "Deleted"} ret = {} for change in changes: - key = kind_map.get(change['Kind'], 'Unknown') - ret.setdefault(key, []).append(change['Path']) - if 'Unknown' in ret: + key = kind_map.get(change["Kind"], "Unknown") + ret.setdefault(key, []).append(change["Path"]) + if "Unknown" in ret: log.error( - 'Unknown changes detected in docker.diff of container %s. ' - 'This is probably due to a change in the Docker API. Please ' - 'report this to the SaltStack developers', name + "Unknown changes detected in docker.diff of container %s. " + "This is probably due to a change in the Docker API. Please " + "report this to the SaltStack developers", + name, ) return ret def exists(name): - ''' + """ Check if a given container exists name @@ -1582,14 +1609,12 @@ def exists(name): .. code-block:: bash salt myminion docker.exists mycontainer - ''' - contextkey = 'docker.exists.{0}'.format(name) + """ + contextkey = "docker.exists.{0}".format(name) if contextkey in __context__: return __context__[contextkey] try: - c_info = _client_wrapper('inspect_container', - name, - catch_api_errors=False) + c_info = _client_wrapper("inspect_container", name, catch_api_errors=False) except docker.errors.APIError: __context__[contextkey] = False else: @@ -1598,7 +1623,7 @@ def exists(name): def history(name, quiet=False): - ''' + """ Return the history for an image. Equivalent to running the ``docker history`` Docker CLI command. @@ -1651,43 +1676,41 @@ def history(name, quiet=False): .. code-block:: bash salt myminion docker.exists mycontainer - ''' - response = _client_wrapper('history', name) + """ + response = _client_wrapper("history", name) key_map = { - 'CreatedBy': 'Command', - 'Created': 'Time_Created_Epoch', + "CreatedBy": "Command", + "Created": "Time_Created_Epoch", } - command_prefix = re.compile(r'^/bin/sh -c (?:#\(nop\) )?') + command_prefix = re.compile(r"^/bin/sh -c (?:#\(nop\) )?") ret = [] # history is most-recent first, reverse this so it is ordered top-down for item in reversed(response): step = {} for key, val in six.iteritems(item): step_key = key_map.get(key, key) - if step_key == 'Command': + if step_key == "Command": if not val: # We assume that an empty build step is 'FROM scratch' - val = 'FROM scratch' + val = "FROM scratch" else: - val = command_prefix.sub('', val) + val = command_prefix.sub("", val) step[step_key] = val - if 'Time_Created_Epoch' in step: - step['Time_Created_Local'] = \ - time.strftime( - '%Y-%m-%d %H:%M:%S %Z', - time.localtime(step['Time_Created_Epoch']) - ) - for param in ('Size',): + if "Time_Created_Epoch" in step: + step["Time_Created_Local"] = time.strftime( + "%Y-%m-%d %H:%M:%S %Z", time.localtime(step["Time_Created_Epoch"]) + ) + for param in ("Size",): if param in step: - step['{0}_Human'.format(param)] = _size_fmt(step[param]) + step["{0}_Human".format(param)] = _size_fmt(step[param]) ret.append(copy.deepcopy(step)) if quiet: - return [x.get('Command') for x in ret] + return [x.get("Command") for x in ret] return ret def images(verbose=False, **kwargs): - ''' + """ Returns information about the Docker images on the Minion. Equivalent to running the ``docker images`` Docker CLI command. @@ -1711,52 +1734,56 @@ def images(verbose=False, **kwargs): salt myminion docker.images salt myminion docker.images all=True - ''' - if 'docker.images' not in __context__: - response = _client_wrapper('images', all=kwargs.get('all', False)) + """ + if "docker.images" not in __context__: + response = _client_wrapper("images", all=kwargs.get("all", False)) key_map = { - 'Created': 'Time_Created_Epoch', + "Created": "Time_Created_Epoch", } for img in response: - img_id = img.pop('Id', None) + img_id = img.pop("Id", None) if img_id is None: continue for item in img: - img_state = ('untagged' if - img['RepoTags'] in ( - [':'], # docker API <1.24 - None, # docker API >=1.24 - ) else 'tagged') - bucket = __context__.setdefault('docker.images', {}) + img_state = ( + "untagged" + if img["RepoTags"] + in ( + [":"], # docker API <1.24 + None, # docker API >=1.24 + ) + else "tagged" + ) + bucket = __context__.setdefault("docker.images", {}) bucket = bucket.setdefault(img_state, {}) img_key = key_map.get(item, item) bucket.setdefault(img_id, {})[img_key] = img[item] - if 'Time_Created_Epoch' in bucket.get(img_id, {}): - bucket[img_id]['Time_Created_Local'] = \ - time.strftime( - '%Y-%m-%d %H:%M:%S %Z', - time.localtime(bucket[img_id]['Time_Created_Epoch']) - ) - for param in ('Size', 'VirtualSize'): + if "Time_Created_Epoch" in bucket.get(img_id, {}): + bucket[img_id]["Time_Created_Local"] = time.strftime( + "%Y-%m-%d %H:%M:%S %Z", + time.localtime(bucket[img_id]["Time_Created_Epoch"]), + ) + for param in ("Size", "VirtualSize"): if param in bucket.get(img_id, {}): - bucket[img_id]['{0}_Human'.format(param)] = \ - _size_fmt(bucket[img_id][param]) + bucket[img_id]["{0}_Human".format(param)] = _size_fmt( + bucket[img_id][param] + ) - context_data = __context__.get('docker.images', {}) - ret = copy.deepcopy(context_data.get('tagged', {})) - if kwargs.get('all', False): - ret.update(copy.deepcopy(context_data.get('untagged', {}))) + context_data = __context__.get("docker.images", {}) + ret = copy.deepcopy(context_data.get("tagged", {})) + if kwargs.get("all", False): + ret.update(copy.deepcopy(context_data.get("untagged", {}))) # If verbose info was requested, go get it if verbose: for img_id in ret: - ret[img_id]['Info'] = inspect_image(img_id) + ret[img_id]["Info"] = inspect_image(img_id) return ret def info(): - ''' + """ Returns a dictionary of system-wide information. Equivalent to running the ``docker info`` Docker CLI command. @@ -1765,12 +1792,12 @@ def info(): .. code-block:: bash salt myminion docker.info - ''' - return _client_wrapper('info') + """ + return _client_wrapper("info") def inspect(name): - ''' + """ .. versionchanged:: 2017.7.0 Volumes and networks are now checked, in addition to containers and images. @@ -1801,35 +1828,35 @@ def inspect(name): salt myminion docker.inspect mycontainer salt myminion docker.inspect busybox - ''' + """ try: return inspect_container(name) except CommandExecutionError as exc: - if 'does not exist' not in exc.strerror: + if "does not exist" not in exc.strerror: raise try: return inspect_image(name) except CommandExecutionError as exc: - if not exc.strerror.startswith('Error 404'): + if not exc.strerror.startswith("Error 404"): raise try: return inspect_volume(name) except CommandExecutionError as exc: - if not exc.strerror.startswith('Error 404'): + if not exc.strerror.startswith("Error 404"): raise try: return inspect_network(name) except CommandExecutionError as exc: - if not exc.strerror.startswith('Error 404'): + if not exc.strerror.startswith("Error 404"): raise raise CommandExecutionError( - 'Error 404: No such image/container/volume/network: {0}'.format(name) + "Error 404: No such image/container/volume/network: {0}".format(name) ) def inspect_container(name): - ''' + """ Retrieves container information. Equivalent to running the ``docker inspect`` Docker CLI command, but will only look for container information. @@ -1848,12 +1875,12 @@ def inspect_container(name): salt myminion docker.inspect_container mycontainer salt myminion docker.inspect_container 0123456789ab - ''' - return _client_wrapper('inspect_container', name) + """ + return _client_wrapper("inspect_container", name) def inspect_image(name): - ''' + """ Retrieves image information. Equivalent to running the ``docker inspect`` Docker CLI command, but will only look for image information. @@ -1878,16 +1905,16 @@ def inspect_image(name): salt myminion docker.inspect_image busybox salt myminion docker.inspect_image centos:6 salt myminion docker.inspect_image 0123456789ab - ''' - ret = _client_wrapper('inspect_image', name) - for param in ('Size', 'VirtualSize'): + """ + ret = _client_wrapper("inspect_image", name) + for param in ("Size", "VirtualSize"): if param in ret: - ret['{0}_Human'.format(param)] = _size_fmt(ret[param]) + ret["{0}_Human".format(param)] = _size_fmt(ret[param]) return ret def list_containers(**kwargs): - ''' + """ Returns a list of containers by name. This is different from :py:func:`docker.ps ` in that :py:func:`docker.ps ` returns its results @@ -1901,19 +1928,19 @@ def list_containers(**kwargs): .. code-block:: bash salt myminion docker.inspect_image - ''' + """ ret = set() - for item in six.itervalues(ps_(all=kwargs.get('all', False))): - names = item.get('Names') + for item in six.itervalues(ps_(all=kwargs.get("all", False))): + names = item.get("Names") if not names: continue - for c_name in [x.lstrip('/') for x in names or []]: + for c_name in [x.lstrip("/") for x in names or []]: ret.add(c_name) return sorted(ret) def list_tags(): - ''' + """ Returns a list of tagged images CLI Example: @@ -1921,17 +1948,17 @@ def list_tags(): .. code-block:: bash salt myminion docker.list_tags - ''' + """ ret = set() for item in six.itervalues(images()): - if not item.get('RepoTags'): + if not item.get("RepoTags"): continue - ret.update(set(item['RepoTags'])) + ret.update(set(item["RepoTags"])) return sorted(ret) def resolve_image_id(name): - ''' + """ .. versionadded:: 2018.3.0 Given an image name (or partial image ID), return the full image ID. If no @@ -1945,23 +1972,24 @@ def resolve_image_id(name): salt myminion docker.resolve_image_id foo salt myminion docker.resolve_image_id foo:bar salt myminion docker.resolve_image_id 36540f359ca3 - ''' + """ try: inspect_result = inspect_image(name) - return inspect_result['Id'] + return inspect_result["Id"] except CommandExecutionError: # No matching image pulled locally, or inspect_image otherwise failed pass except KeyError: log.error( - 'Inspecting docker image \'%s\' returned an unexpected data ' - 'structure: %s', name, inspect_result + "Inspecting docker image '%s' returned an unexpected data " "structure: %s", + name, + inspect_result, ) return False def resolve_tag(name, **kwargs): - ''' + """ .. versionadded:: 2017.7.2 .. versionchanged:: 2018.3.0 Instead of matching against pulled tags using @@ -1998,15 +2026,15 @@ def resolve_tag(name, **kwargs): salt myminion docker.resolve_tag busybox salt myminion docker.resolve_tag centos:7 all=True salt myminion docker.resolve_tag c9f378ac27d9 - ''' - kwargs = __utils__['args.clean_kwargs'](**kwargs) - all_ = kwargs.pop('all', False) + """ + kwargs = __utils__["args.clean_kwargs"](**kwargs) + all_ = kwargs.pop("all", False) if kwargs: - __utils__['args.invalid_kwargs'](kwargs) + __utils__["args.invalid_kwargs"](kwargs) try: inspect_result = inspect_image(name) - tags = inspect_result['RepoTags'] + tags = inspect_result["RepoTags"] if all_: if tags: return tags @@ -2019,17 +2047,18 @@ def resolve_tag(name, **kwargs): return False except KeyError: log.error( - 'Inspecting docker image \'%s\' returned an unexpected data ' - 'structure: %s', name, inspect_result + "Inspecting docker image '%s' returned an unexpected data " "structure: %s", + name, + inspect_result, ) except IndexError: # The image passed is an untagged image ID pass - return [inspect_result['Id']] if all_ else inspect_result['Id'] + return [inspect_result["Id"]] if all_ else inspect_result["Id"] def logs(name, **kwargs): - ''' + """ .. versionchanged:: 2018.3.0 Support for all of docker-py's `logs()`_ function's arguments, with the exception of ``stream``. @@ -2084,13 +2113,13 @@ def logs(name, **kwargs): salt myminion docker.logs mycontainer since='1 hour ago' salt myminion docker.logs mycontainer since='1 week ago' salt myminion docker.logs mycontainer since='1 fortnight ago' - ''' - kwargs = __utils__['args.clean_kwargs'](**kwargs) - if 'stream' in kwargs: - raise SaltInvocationError('The \'stream\' argument is not supported') + """ + kwargs = __utils__["args.clean_kwargs"](**kwargs) + if "stream" in kwargs: + raise SaltInvocationError("The 'stream' argument is not supported") try: - kwargs['since'] = int(kwargs['since']) + kwargs["since"] = int(kwargs["since"]) except KeyError: pass except (ValueError, TypeError): @@ -2099,21 +2128,20 @@ def logs(name, **kwargs): # APIError. if HAS_TIMELIB: try: - kwargs['since'] = timelib.strtodatetime(kwargs['since']) + kwargs["since"] = timelib.strtodatetime(kwargs["since"]) except Exception as exc: # pylint: disable=broad-except log.warning( - 'docker.logs: Failed to parse \'%s\' using timelib: %s', - kwargs['since'], exc + "docker.logs: Failed to parse '%s' using timelib: %s", + kwargs["since"], + exc, ) # logs() returns output as bytestrings - return salt.utils.stringutils.to_unicode( - _client_wrapper('logs', name, **kwargs) - ) + return salt.utils.stringutils.to_unicode(_client_wrapper("logs", name, **kwargs)) def pid(name): - ''' + """ Returns the PID of a container name @@ -2125,12 +2153,12 @@ def pid(name): salt myminion docker.pid mycontainer salt myminion docker.pid 0123456789ab - ''' - return inspect_container(name)['State']['Pid'] + """ + return inspect_container(name)["State"]["Pid"] def port(name, private_port=None): - ''' + """ Returns port mapping information for a given container. Equivalent to running the ``docker port`` Docker CLI command. @@ -2168,30 +2196,27 @@ def port(name, private_port=None): salt myminion docker.port mycontainer salt myminion docker.port mycontainer 5000 salt myminion docker.port mycontainer 5000/udp - ''' - pattern_used = bool(re.search(r'[*?\[]', name)) - names = fnmatch.filter(list_containers(all=True), name) \ - if pattern_used \ - else [name] + """ + pattern_used = bool(re.search(r"[*?\[]", name)) + names = fnmatch.filter(list_containers(all=True), name) if pattern_used else [name] if private_port is None: - pattern = '*' + pattern = "*" else: # Sanity checks if isinstance(private_port, six.integer_types): - pattern = '{0}/*'.format(private_port) + pattern = "{0}/*".format(private_port) else: err = ( - 'Invalid private_port \'{0}\'. Must either be a port number, ' - 'or be in port/protocol notation (e.g. 5000/tcp)' - .format(private_port) + "Invalid private_port '{0}'. Must either be a port number, " + "or be in port/protocol notation (e.g. 5000/tcp)".format(private_port) ) try: - port_num, _, protocol = private_port.partition('/') + port_num, _, protocol = private_port.partition("/") protocol = protocol.lower() - if not port_num.isdigit() or protocol not in ('tcp', 'udp'): + if not port_num.isdigit() or protocol not in ("tcp", "udp"): raise SaltInvocationError(err) - pattern = port_num + '/' + protocol + pattern = port_num + "/" + protocol except AttributeError: raise SaltInvocationError(err) @@ -2200,16 +2225,14 @@ def port(name, private_port=None): # docker.client.Client.port() doesn't do what we need, so just inspect # the container and get the information from there. It's what they're # already doing (poorly) anyway. - mappings = inspect_container(c_name).get( - 'NetworkSettings', {}).get('Ports', {}) - ret[c_name] = dict((x, mappings[x]) - for x in fnmatch.filter(mappings, pattern)) + mappings = inspect_container(c_name).get("NetworkSettings", {}).get("Ports", {}) + ret[c_name] = dict((x, mappings[x]) for x in fnmatch.filter(mappings, pattern)) return ret.get(name, {}) if not pattern_used else ret def ps_(filters=None, **kwargs): - ''' + """ Returns information about the Docker containers on the Minion. Equivalent to running the ``docker ps`` Docker CLI command. @@ -2244,48 +2267,49 @@ def ps_(filters=None, **kwargs): salt myminion docker.ps salt myminion docker.ps all=True salt myminion docker.ps filters="{'label': 'role=web'}" - ''' - response = _client_wrapper('containers', all=True, filters=filters) + """ + response = _client_wrapper("containers", all=True, filters=filters) key_map = { - 'Created': 'Time_Created_Epoch', + "Created": "Time_Created_Epoch", } context_data = {} for container in response: - c_id = container.pop('Id', None) + c_id = container.pop("Id", None) if c_id is None: continue for item in container: - c_state = 'running' \ - if container.get('Status', '').lower().startswith('up ') \ - else 'stopped' + c_state = ( + "running" + if container.get("Status", "").lower().startswith("up ") + else "stopped" + ) bucket = context_data.setdefault(c_state, {}) c_key = key_map.get(item, item) bucket.setdefault(c_id, {})[c_key] = container[item] - if 'Time_Created_Epoch' in bucket.get(c_id, {}): - bucket[c_id]['Time_Created_Local'] = \ - time.strftime( - '%Y-%m-%d %H:%M:%S %Z', - time.localtime(bucket[c_id]['Time_Created_Epoch']) - ) + if "Time_Created_Epoch" in bucket.get(c_id, {}): + bucket[c_id]["Time_Created_Local"] = time.strftime( + "%Y-%m-%d %H:%M:%S %Z", + time.localtime(bucket[c_id]["Time_Created_Epoch"]), + ) - ret = copy.deepcopy(context_data.get('running', {})) - if kwargs.get('all', False): - ret.update(copy.deepcopy(context_data.get('stopped', {}))) + ret = copy.deepcopy(context_data.get("running", {})) + if kwargs.get("all", False): + ret.update(copy.deepcopy(context_data.get("stopped", {}))) # If verbose info was requested, go get it - if kwargs.get('verbose', False): + if kwargs.get("verbose", False): for c_id in ret: - ret[c_id]['Info'] = inspect_container(c_id) + ret[c_id]["Info"] = inspect_container(c_id) - if kwargs.get('host', False): - ret.setdefault( - 'host', {}).setdefault( - 'interfaces', {}).update(__salt__['network.interfaces']()) + if kwargs.get("host", False): + ret.setdefault("host", {}).setdefault("interfaces", {}).update( + __salt__["network.interfaces"]() + ) return ret def state(name): - ''' + """ Returns the state of the container name @@ -2303,8 +2327,8 @@ def state(name): .. code-block:: bash salt myminion docker.state mycontainer - ''' - contextkey = 'docker.state.{0}'.format(name) + """ + contextkey = "docker.state.{0}".format(name) if contextkey in __context__: return __context__[contextkey] __context__[contextkey] = _get_state(inspect_container(name)) @@ -2312,7 +2336,7 @@ def state(name): def search(name, official=False, trusted=False): - ''' + """ Searches the registry for an image name @@ -2342,28 +2366,28 @@ def search(name, official=False, trusted=False): salt myminion docker.search centos salt myminion docker.search centos official=True - ''' - response = _client_wrapper('search', name) + """ + response = _client_wrapper("search", name) if not response: raise CommandExecutionError( - 'No images matched the search string \'{0}\''.format(name) + "No images matched the search string '{0}'".format(name) ) key_map = { - 'description': 'Description', - 'is_official': 'Official', - 'is_trusted': 'Trusted', - 'star_count': 'Stars' + "description": "Description", + "is_official": "Official", + "is_trusted": "Trusted", + "star_count": "Stars", } limit = [] if official: - limit.append('Official') + limit.append("Official") if trusted: - limit.append('Trusted') + limit.append("Trusted") results = {} for item in response: - c_name = item.pop('name', None) + c_name = item.pop("name", None) if c_name is not None: for key in item: mapped_key = key_map.get(key, key) @@ -2382,7 +2406,7 @@ def search(name, official=False, trusted=False): def top(name): - ''' + """ Runs the `docker top` command on a specific container name @@ -2400,17 +2424,17 @@ def top(name): salt myminion docker.top mycontainer salt myminion docker.top 0123456789ab - ''' - response = _client_wrapper('top', name) + """ + response = _client_wrapper("top", name) # Read in column names columns = {} - for idx, col_name in enumerate(response['Titles']): + for idx, col_name in enumerate(response["Titles"]): columns[idx] = col_name # Build return dict ret = [] - for process in response['Processes']: + for process in response["Processes"]: cur_proc = {} for idx, val in enumerate(process): cur_proc[columns[idx]] = val @@ -2419,7 +2443,7 @@ def top(name): def version(): - ''' + """ Returns a dictionary of Docker version information. Equivalent to running the ``docker version`` Docker CLI command. @@ -2428,40 +2452,44 @@ def version(): .. code-block:: bash salt myminion docker.version - ''' - ret = _client_wrapper('version') + """ + ret = _client_wrapper("version") version_re = re.compile(VERSION_RE) - if 'Version' in ret: - match = version_re.match(six.text_type(ret['Version'])) + if "Version" in ret: + match = version_re.match(six.text_type(ret["Version"])) if match: - ret['VersionInfo'] = tuple( - [int(x) for x in match.group(1).split('.')] - ) - if 'ApiVersion' in ret: - match = version_re.match(six.text_type(ret['ApiVersion'])) + ret["VersionInfo"] = tuple([int(x) for x in match.group(1).split(".")]) + if "ApiVersion" in ret: + match = version_re.match(six.text_type(ret["ApiVersion"])) if match: - ret['ApiVersionInfo'] = tuple( - [int(x) for x in match.group(1).split('.')] - ) + ret["ApiVersionInfo"] = tuple([int(x) for x in match.group(1).split(".")]) return ret def _create_networking_config(networks): log.debug("creating networking config from {}".format(networks)) - return _client_wrapper('create_networking_config', - {k: _client_wrapper('create_endpoint_config', **v) for k, v in networks.items()}) + return _client_wrapper( + "create_networking_config", + { + k: _client_wrapper("create_endpoint_config", **v) + for k, v in networks.items() + }, + ) + # Functions to manage containers @_refresh_mine_cache -def create(image, - name=None, - start=False, - skip_translate=None, - ignore_collisions=False, - validate_ip_addrs=True, - client_timeout=salt.utils.docker.CLIENT_TIMEOUT, - **kwargs): - ''' +def create( + image, + name=None, + start=False, + skip_translate=None, + ignore_collisions=False, + validate_ip_addrs=True, + client_timeout=salt.utils.docker.CLIENT_TIMEOUT, + **kwargs +): + """ Create a new container image @@ -3178,67 +3206,67 @@ def create(image, salt myminion docker.create myuser/mycontainer volumes="/mnt/vol1,/mnt/vol2" # Create a CentOS 7 container that will stay running once started salt myminion docker.create centos:7 name=mycent7 interactive=True tty=True command=bash - ''' - if kwargs.pop('inspect', True) and not resolve_image_id(image): + """ + if kwargs.pop("inspect", True) and not resolve_image_id(image): pull(image, client_timeout=client_timeout) kwargs, unused_kwargs = _get_create_kwargs( skip_translate=skip_translate, ignore_collisions=ignore_collisions, validate_ip_addrs=validate_ip_addrs, - **kwargs) + **kwargs + ) if unused_kwargs: log.warning( - 'The following arguments were ignored because they are not ' - 'recognized by docker-py: %s', sorted(unused_kwargs) + "The following arguments were ignored because they are not " + "recognized by docker-py: %s", + sorted(unused_kwargs), ) log.debug( - 'docker.create: creating container %susing the following ' - 'arguments: %s', - 'with name \'{0}\' '.format(name) if name is not None else '', - kwargs + "docker.create: creating container %susing the following " "arguments: %s", + "with name '{0}' ".format(name) if name is not None else "", + kwargs, ) time_started = time.time() - response = _client_wrapper('create_container', - image, - name=name, - **kwargs) - response['Time_Elapsed'] = time.time() - time_started + response = _client_wrapper("create_container", image, name=name, **kwargs) + response["Time_Elapsed"] = time.time() - time_started _clear_context() if name is None: - name = inspect_container(response['Id'])['Name'].lstrip('/') - response['Name'] = name + name = inspect_container(response["Id"])["Name"].lstrip("/") + response["Name"] = name if start: try: start_(name) except CommandExecutionError as exc: raise CommandExecutionError( - 'Failed to start container after creation', - info={'response': response, 'error': exc.__str__()} + "Failed to start container after creation", + info={"response": response, "error": exc.__str__()}, ) else: - response['Started'] = True + response["Started"] = True return response @_refresh_mine_cache -def run_container(image, - name=None, - skip_translate=None, - ignore_collisions=False, - validate_ip_addrs=True, - client_timeout=salt.utils.docker.CLIENT_TIMEOUT, - bg=False, - replace=False, - force=False, - networks=None, - **kwargs): - ''' +def run_container( + image, + name=None, + skip_translate=None, + ignore_collisions=False, + validate_ip_addrs=True, + client_timeout=salt.utils.docker.CLIENT_TIMEOUT, + bg=False, + replace=False, + force=False, + networks=None, + **kwargs +): + """ .. versionadded:: 2018.3.0 Equivalent to ``docker run`` on the Docker CLI. Runs the container, waits @@ -3296,27 +3324,27 @@ def run_container(image, salt myminion docker.run_container myuser/myimage command='perl /scripts/sync.py' networks=net1,net2 # net1 using automatic IP, net2 using static IPv4 address salt myminion docker.run_container myuser/myimage command='perl /scripts/sync.py' networks='{"net1": {}, "net2": {"ipv4_address": "192.168.27.12"}}' - ''' - if kwargs.pop('inspect', True) and not resolve_image_id(image): + """ + if kwargs.pop("inspect", True) and not resolve_image_id(image): pull(image, client_timeout=client_timeout) removed_ids = None if name is not None: try: - pre_state = __salt__['docker.state'](name) + pre_state = __salt__["docker.state"](name) except CommandExecutionError: pass else: - if pre_state == 'running' and not (replace and force): + if pre_state == "running" and not (replace and force): raise CommandExecutionError( - 'Container \'{0}\' exists and is running. Run with ' - 'replace=True and force=True to force removal of the ' - 'existing container.'.format(name) + "Container '{0}' exists and is running. Run with " + "replace=True and force=True to force removal of the " + "existing container.".format(name) ) elif not replace: raise CommandExecutionError( - 'Container \'{0}\' exists. Run with replace=True to ' - 'remove the existing container'.format(name) + "Container '{0}' exists. Run with replace=True to " + "remove the existing container".format(name) ) else: # We don't have to try/except this, we want it to raise a @@ -3326,91 +3354,91 @@ def run_container(image, removed_ids = rm_(name, force=force) log_kwargs = {} - for argname in get_client_args('logs')['logs']: + for argname in get_client_args("logs")["logs"]: try: log_kwargs[argname] = kwargs.pop(argname) except KeyError: pass # Ignore the stream argument if passed - log_kwargs.pop('stream', None) + log_kwargs.pop("stream", None) kwargs, unused_kwargs = _get_create_kwargs( skip_translate=skip_translate, ignore_collisions=ignore_collisions, validate_ip_addrs=validate_ip_addrs, - **kwargs) + **kwargs + ) # _get_create_kwargs() will have processed auto_remove and put it into the # host_config, so check the host_config to see whether or not auto_remove # was enabled. - auto_remove = kwargs.get('host_config', {}).get('AutoRemove', False) + auto_remove = kwargs.get("host_config", {}).get("AutoRemove", False) if unused_kwargs: log.warning( - 'The following arguments were ignored because they are not ' - 'recognized by docker-py: %s', sorted(unused_kwargs) + "The following arguments were ignored because they are not " + "recognized by docker-py: %s", + sorted(unused_kwargs), ) if networks: if isinstance(networks, six.string_types): - networks = {x: {} for x in networks.split(',')} - if not isinstance(networks, dict) \ - or not all(isinstance(x, dict) - for x in six.itervalues(networks)): - raise SaltInvocationError('Invalid format for networks argument') + networks = {x: {} for x in networks.split(",")} + if not isinstance(networks, dict) or not all( + isinstance(x, dict) for x in six.itervalues(networks) + ): + raise SaltInvocationError("Invalid format for networks argument") log.debug( - 'docker.create: creating container %susing the following ' - 'arguments: %s', - 'with name \'{0}\' '.format(name) if name is not None else '', - kwargs + "docker.create: creating container %susing the following " "arguments: %s", + "with name '{0}' ".format(name) if name is not None else "", + kwargs, ) time_started = time.time() # Create the container - ret = _client_wrapper('create_container', image, name=name, **kwargs) + ret = _client_wrapper("create_container", image, name=name, **kwargs) if removed_ids: - ret['Replaces'] = removed_ids + ret["Replaces"] = removed_ids if name is None: - name = inspect_container(ret['Id'])['Name'].lstrip('/') - ret['Name'] = name + name = inspect_container(ret["Id"])["Name"].lstrip("/") + ret["Name"] = name def _append_warning(ret, msg): - warnings = ret.pop('Warnings', None) + warnings = ret.pop("Warnings", None) if warnings is None: warnings = [msg] elif isinstance(ret, list): warnings.append(msg) else: warnings = [warnings, msg] - ret['Warnings'] = warnings + ret["Warnings"] = warnings - exc_info = {'return': ret} + exc_info = {"return": ret} try: if networks: try: for net_name, net_conf in six.iteritems(networks): - __salt__['docker.connect_container_to_network']( - ret['Id'], - net_name, - **net_conf) + __salt__["docker.connect_container_to_network"]( + ret["Id"], net_name, **net_conf + ) except CommandExecutionError as exc: # Make an effort to remove the container if auto_remove was enabled if auto_remove: try: rm_(name) except CommandExecutionError as rm_exc: - exc_info.setdefault('other_errors', []).append( - 'Failed to auto_remove container: {0}'.format(rm_exc) + exc_info.setdefault("other_errors", []).append( + "Failed to auto_remove container: {0}".format(rm_exc) ) # Raise original exception with additonal info raise CommandExecutionError(exc.__str__(), info=exc_info) # Start the container output = [] - start_(ret['Id']) + start_(ret["Id"]) if not bg: # Can't use logs() here because we've disabled "stream" in that # function. Also, note that if you want to troubleshoot this for loop @@ -3420,37 +3448,35 @@ def run_container(image, # exits during iteration, the next iteration of the generator will # raise an exception since the container will no longer exist. try: - for line in _client_wrapper('logs', - ret['Id'], - stream=True, - timestamps=False): + for line in _client_wrapper( + "logs", ret["Id"], stream=True, timestamps=False + ): output.append(salt.utils.stringutils.to_unicode(line)) except CommandExecutionError: msg = ( - 'Failed to get logs from container. This may be because ' - 'the container exited before Salt was able to attach to ' - 'it to retrieve the logs. Consider setting auto_remove ' - 'to False.' + "Failed to get logs from container. This may be because " + "the container exited before Salt was able to attach to " + "it to retrieve the logs. Consider setting auto_remove " + "to False." ) _append_warning(ret, msg) # Container has exited, note the elapsed time - ret['Time_Elapsed'] = time.time() - time_started + ret["Time_Elapsed"] = time.time() - time_started _clear_context() if not bg: - ret['Logs'] = ''.join(output) + ret["Logs"] = "".join(output) if not auto_remove: try: - cinfo = inspect_container(ret['Id']) + cinfo = inspect_container(ret["Id"]) except CommandExecutionError: - _append_warning( - ret, 'Failed to inspect container after running') + _append_warning(ret, "Failed to inspect container after running") else: - cstate = cinfo.get('State', {}) - cstatus = cstate.get('Status') - if cstatus != 'exited': - _append_warning(ret, 'Container state is not \'exited\'') - ret['ExitCode'] = cstate.get('ExitCode') + cstate = cinfo.get("State", {}) + cstatus = cstate.get("Status") + if cstatus != "exited": + _append_warning(ret, "Container state is not 'exited'") + ret["ExitCode"] = cstate.get("ExitCode") except CommandExecutionError as exc: try: @@ -3458,7 +3484,7 @@ def run_container(image, except (TypeError, ValueError): # In the event exc.info wasn't a dict (extremely unlikely), append # it to other_errors as a fallback. - exc_info.setdefault('other_errors', []).append(exc.info) + exc_info.setdefault("other_errors", []).append(exc.info) # Re-raise with all of the available additional info raise CommandExecutionError(exc.__str__(), info=exc_info) @@ -3466,7 +3492,7 @@ def run_container(image, def copy_from(name, source, dest, overwrite=False, makedirs=False): - ''' + """ Copy a file from inside a container to the Minion name @@ -3498,16 +3524,14 @@ def copy_from(name, source, dest, overwrite=False, makedirs=False): .. code-block:: bash salt myminion docker.copy_from mycontainer /var/log/nginx/access.log /home/myuser - ''' + """ c_state = state(name) - if c_state != 'running': - raise CommandExecutionError( - 'Container \'{0}\' is not running'.format(name) - ) + if c_state != "running": + raise CommandExecutionError("Container '{0}' is not running".format(name)) # Destination file sanity checks if not os.path.isabs(dest): - raise SaltInvocationError('Destination path must be absolute') + raise SaltInvocationError("Destination path must be absolute") if os.path.isdir(dest): # Destination is a directory, full path to dest file will include the # basename of the source file. @@ -3524,69 +3548,63 @@ def copy_from(name, source, dest, overwrite=False, makedirs=False): os.makedirs(dest_dir) except OSError as exc: raise CommandExecutionError( - 'Unable to make destination directory {0}: {1}' - .format(dest_dir, exc) + "Unable to make destination directory {0}: {1}".format( + dest_dir, exc + ) ) else: raise SaltInvocationError( - 'Directory {0} does not exist'.format(dest_dir) + "Directory {0} does not exist".format(dest_dir) ) if not overwrite and os.path.exists(dest): raise CommandExecutionError( - 'Destination path {0} already exists. Use overwrite=True to ' - 'overwrite it'.format(dest) + "Destination path {0} already exists. Use overwrite=True to " + "overwrite it".format(dest) ) # Source file sanity checks if not os.path.isabs(source): - raise SaltInvocationError('Source path must be absolute') + raise SaltInvocationError("Source path must be absolute") else: - if retcode(name, - 'test -e {0}'.format(pipes.quote(source)), - ignore_retcode=True) == 0: - if retcode(name, - 'test -f {0}'.format(pipes.quote(source)), - ignore_retcode=True) != 0: - raise SaltInvocationError('Source must be a regular file') - else: - raise SaltInvocationError( - 'Source file {0} does not exist'.format(source) + if ( + retcode( + name, "test -e {0}".format(pipes.quote(source)), ignore_retcode=True ) + == 0 + ): + if ( + retcode( + name, "test -f {0}".format(pipes.quote(source)), ignore_retcode=True + ) + != 0 + ): + raise SaltInvocationError("Source must be a regular file") + else: + raise SaltInvocationError("Source file {0} does not exist".format(source)) # Before we try to replace the file, compare checksums. source_md5 = _get_md5(name, source) - if source_md5 == __salt__['file.get_sum'](dest, 'md5'): - log.debug( - '%s:%s and %s are the same file, skipping copy', - name, source, dest - ) + if source_md5 == __salt__["file.get_sum"](dest, "md5"): + log.debug("%s:%s and %s are the same file, skipping copy", name, source, dest) return True - log.debug( - 'Copying %s from container \'%s\' to local path %s', - source, name, dest - ) + log.debug("Copying %s from container '%s' to local path %s", source, name, dest) try: - src_path = ':'.join((name, source)) + src_path = ":".join((name, source)) except TypeError: - src_path = '{0}:{1}'.format(name, source) - cmd = ['docker', 'cp', src_path, dest_dir] - __salt__['cmd.run'](cmd, python_shell=False) - return source_md5 == __salt__['file.get_sum'](dest, 'md5') + src_path = "{0}:{1}".format(name, source) + cmd = ["docker", "cp", src_path, dest_dir] + __salt__["cmd.run"](cmd, python_shell=False) + return source_md5 == __salt__["file.get_sum"](dest, "md5") # Docker cp gets a file from the container, alias this to copy_from -cp = salt.utils.functools.alias_function(copy_from, 'cp') +cp = salt.utils.functools.alias_function(copy_from, "cp") -def copy_to(name, - source, - dest, - exec_driver=None, - overwrite=False, - makedirs=False): - ''' +def copy_to(name, source, dest, exec_driver=None, overwrite=False, makedirs=False): + """ Copy a file from the host into a container name @@ -3624,26 +3642,22 @@ def copy_to(name, .. code-block:: bash salt myminion docker.copy_to mycontainer /tmp/foo /root/foo - ''' + """ if exec_driver is None: exec_driver = _get_exec_driver() - return __salt__['container_resource.copy_to']( + return __salt__["container_resource.copy_to"]( name, - __salt__['container_resource.cache_file'](source), + __salt__["container_resource.cache_file"](source), dest, container_type=__virtualname__, exec_driver=exec_driver, overwrite=overwrite, - makedirs=makedirs) + makedirs=makedirs, + ) -def export(name, - path, - overwrite=False, - makedirs=False, - compression=None, - **kwargs): - ''' +def export(name, path, overwrite=False, makedirs=False, compression=None, **kwargs): + """ Exports a container to a tar archive. It can also optionally compress that tar archive, and push it up to the Master. @@ -3707,8 +3721,8 @@ def export(name, salt myminion docker.export mycontainer /tmp/mycontainer.tar salt myminion docker.export mycontainer /tmp/mycontainer.tar.xz push=True - ''' - err = 'Path \'{0}\' is not absolute'.format(path) + """ + err = "Path '{0}' is not absolute".format(path) try: if not os.path.isabs(path): raise SaltInvocationError(err) @@ -3716,80 +3730,79 @@ def export(name, raise SaltInvocationError(err) if os.path.exists(path) and not overwrite: - raise CommandExecutionError('{0} already exists'.format(path)) + raise CommandExecutionError("{0} already exists".format(path)) if compression is None: - if path.endswith('.tar.gz') or path.endswith('.tgz'): - compression = 'gzip' - elif path.endswith('.tar.bz2') or path.endswith('.tbz2'): - compression = 'bzip2' - elif path.endswith('.tar.xz') or path.endswith('.txz'): + if path.endswith(".tar.gz") or path.endswith(".tgz"): + compression = "gzip" + elif path.endswith(".tar.bz2") or path.endswith(".tbz2"): + compression = "bzip2" + elif path.endswith(".tar.xz") or path.endswith(".txz"): if HAS_LZMA: - compression = 'xz' + compression = "xz" else: raise CommandExecutionError( - 'XZ compression unavailable. Install the backports.lzma ' - 'module and xz-utils to enable XZ compression.' + "XZ compression unavailable. Install the backports.lzma " + "module and xz-utils to enable XZ compression." ) - elif compression == 'gz': - compression = 'gzip' - elif compression == 'bz2': - compression = 'bzip2' - elif compression == 'lzma': - compression = 'xz' + elif compression == "gz": + compression = "gzip" + elif compression == "bz2": + compression = "bzip2" + elif compression == "lzma": + compression = "xz" - if compression and compression not in ('gzip', 'bzip2', 'xz'): - raise SaltInvocationError( - 'Invalid compression type \'{0}\''.format(compression) - ) + if compression and compression not in ("gzip", "bzip2", "xz"): + raise SaltInvocationError("Invalid compression type '{0}'".format(compression)) parent_dir = os.path.dirname(path) if not os.path.isdir(parent_dir): if not makedirs: raise CommandExecutionError( - 'Parent dir {0} of destination path does not exist. Use ' - 'makedirs=True to create it.'.format(parent_dir) + "Parent dir {0} of destination path does not exist. Use " + "makedirs=True to create it.".format(parent_dir) ) try: os.makedirs(parent_dir) except OSError as exc: raise CommandExecutionError( - 'Unable to make parent dir {0}: {1}' - .format(parent_dir, exc) + "Unable to make parent dir {0}: {1}".format(parent_dir, exc) ) - if compression == 'gzip': + if compression == "gzip": try: - out = gzip.open(path, 'wb') + out = gzip.open(path, "wb") except OSError as exc: raise CommandExecutionError( - 'Unable to open {0} for writing: {1}'.format(path, exc) + "Unable to open {0} for writing: {1}".format(path, exc) ) - elif compression == 'bzip2': + elif compression == "bzip2": compressor = bz2.BZ2Compressor() - elif compression == 'xz': + elif compression == "xz": compressor = lzma.LZMACompressor() time_started = time.time() try: - if compression != 'gzip': + if compression != "gzip": # gzip doesn't use a Compressor object, it uses a .open() method to # open the filehandle. If not using gzip, we need to open the # filehandle here. We make sure to close it in the "finally" block # below. - out = __utils__['files.fopen'](path, 'wb') # pylint: disable=resource-leakage - response = _client_wrapper('export', name) + out = __utils__["files.fopen"]( + path, "wb" + ) # pylint: disable=resource-leakage + response = _client_wrapper("export", name) buf = None - while buf != '': + while buf != "": buf = response.read(4096) if buf: - if compression in ('bzip2', 'xz'): + if compression in ("bzip2", "xz"): data = compressor.compress(buf) if data: out.write(data) else: out.write(buf) - if compression in ('bzip2', 'xz'): + if compression in ("bzip2", "xz"): # Flush any remaining data out of the compressor data = compressor.flush() if data: @@ -3801,26 +3814,26 @@ def export(name, except OSError: pass raise CommandExecutionError( - 'Error occurred during container export: {0}'.format(exc) + "Error occurred during container export: {0}".format(exc) ) finally: out.close() - ret = {'Time_Elapsed': time.time() - time_started} + ret = {"Time_Elapsed": time.time() - time_started} - ret['Path'] = path - ret['Size'] = os.stat(path).st_size - ret['Size_Human'] = _size_fmt(ret['Size']) + ret["Path"] = path + ret["Size"] = os.stat(path).st_size + ret["Size_Human"] = _size_fmt(ret["Size"]) # Process push if kwargs.get(push, False): - ret['Push'] = __salt__['cp.push'](path) + ret["Push"] = __salt__["cp.push"](path) return ret @_refresh_mine_cache def rm_(name, force=False, volumes=False, **kwargs): - ''' + """ Removes a container name @@ -3861,39 +3874,40 @@ def rm_(name, force=False, volumes=False, **kwargs): salt myminion docker.rm mycontainer salt myminion docker.rm mycontainer force=True - ''' - kwargs = __utils__['args.clean_kwargs'](**kwargs) - stop_ = kwargs.pop('stop', False) - timeout = kwargs.pop('timeout', None) + """ + kwargs = __utils__["args.clean_kwargs"](**kwargs) + stop_ = kwargs.pop("stop", False) + timeout = kwargs.pop("timeout", None) auto_remove = False if kwargs: - __utils__['args.invalid_kwargs'](kwargs) + __utils__["args.invalid_kwargs"](kwargs) - if state(name) == 'running' and not (force or stop_): + if state(name) == "running" and not (force or stop_): raise CommandExecutionError( - 'Container \'{0}\' is running, use force=True to forcibly ' - 'remove this container'.format(name) + "Container '{0}' is running, use force=True to forcibly " + "remove this container".format(name) ) if stop_ and not force: inspect_results = inspect_container(name) try: - auto_remove = inspect_results['HostConfig']['AutoRemove'] + auto_remove = inspect_results["HostConfig"]["AutoRemove"] except KeyError: log.error( - 'Failed to find AutoRemove in inspect results, Docker API may ' - 'have changed. Full results: %s', inspect_results + "Failed to find AutoRemove in inspect results, Docker API may " + "have changed. Full results: %s", + inspect_results, ) stop(name, timeout=timeout) pre = ps_(all=True) if not auto_remove: - _client_wrapper('remove_container', name, v=volumes, force=force) + _client_wrapper("remove_container", name, v=volumes, force=force) _clear_context() return [x for x in pre if x not in ps_(all=True)] def rename(name, new_name): - ''' + """ .. versionadded:: 2017.7.0 Renames a container. Returns ``True`` if successful, and raises an error if @@ -3911,26 +3925,28 @@ def rename(name, new_name): .. code-block:: bash salt myminion docker.rename foo bar - ''' - id_ = inspect_container(name)['Id'] - log.debug('Renaming container \'%s\' (ID: %s) to \'%s\'', name, id_, new_name) - _client_wrapper('rename', id_, new_name) + """ + id_ = inspect_container(name)["Id"] + log.debug("Renaming container '%s' (ID: %s) to '%s'", name, id_, new_name) + _client_wrapper("rename", id_, new_name) # Confirm that the ID of the container corresponding to the new name is the # same as it was before. - return inspect_container(new_name)['Id'] == id_ + return inspect_container(new_name)["Id"] == id_ # Functions to manage images -def build(path=None, - repository=None, - tag=None, - cache=True, - rm=True, - api_response=False, - fileobj=None, - dockerfile=None, - buildargs=None): - ''' +def build( + path=None, + repository=None, + tag=None, + cache=True, + rm=True, + api_response=False, + fileobj=None, + dockerfile=None, + buildargs=None, +): + """ .. versionchanged:: 2018.3.0 If the built image should be tagged, then the repository and tag must now be passed separately using the ``repository`` and ``tag`` @@ -4014,14 +4030,14 @@ def build(path=None, salt myminion docker.build /path/to/docker/build/dir salt myminion docker.build https://github.com/myuser/myrepo.git repository=myimage tag=latest salt myminion docker.build /path/to/docker/build/dir dockerfile=Dockefile.different repository=myimage tag=dev - ''' + """ _prep_pull() if repository or tag: if not repository and tag: # Have to have both or neither raise SaltInvocationError( - 'If tagging, both a repository and tag are required' + "If tagging, both a repository and tag are required" ) else: if not isinstance(repository, six.string_types): @@ -4032,25 +4048,26 @@ def build(path=None, # For the build function in the low-level API, the "tag" refers to the full # tag (e.g. myuser/myimage:mytag). This is different than in other # functions, where the repo and tag are passed separately. - image_tag = '{0}:{1}'.format(repository, tag) if repository and tag else None + image_tag = "{0}:{1}".format(repository, tag) if repository and tag else None time_started = time.time() - response = _client_wrapper('build', - path=path, - tag=image_tag, - quiet=False, - fileobj=fileobj, - rm=rm, - nocache=not cache, - dockerfile=dockerfile, - buildargs=buildargs) - ret = {'Time_Elapsed': time.time() - time_started} + response = _client_wrapper( + "build", + path=path, + tag=image_tag, + quiet=False, + fileobj=fileobj, + rm=rm, + nocache=not cache, + dockerfile=dockerfile, + buildargs=buildargs, + ) + ret = {"Time_Elapsed": time.time() - time_started} _clear_context() if not response: raise CommandExecutionError( - 'Build failed for {0}, no response returned from Docker API' - .format(path) + "Build failed for {0}, no response returned from Docker API".format(path) ) stream_data = [] @@ -4063,46 +4080,39 @@ def build(path=None, item_type = next(iter(item)) except StopIteration: continue - if item_type == 'status': + if item_type == "status": _pull_status(ret, item) - if item_type == 'stream': + if item_type == "stream": _build_status(ret, item) - elif item_type == 'errorDetail': + elif item_type == "errorDetail": _error_detail(errors, item) - if 'Id' not in ret: + if "Id" not in ret: # API returned information, but there was no confirmation of a # successful build. - msg = 'Build failed for {0}'.format(path) + msg = "Build failed for {0}".format(path) log.error(msg) log.error(stream_data) if errors: - msg += '. Error(s) follow:\n\n{0}'.format( - '\n\n'.join(errors) - ) + msg += ". Error(s) follow:\n\n{0}".format("\n\n".join(errors)) raise CommandExecutionError(msg) - resolved_tag = resolve_tag(ret['Id'], all=True) + resolved_tag = resolve_tag(ret["Id"], all=True) if resolved_tag: - ret['Image'] = resolved_tag + ret["Image"] = resolved_tag else: - ret['Warning'] = \ - 'Failed to tag image as {0}'.format(image_tag) + ret["Warning"] = "Failed to tag image as {0}".format(image_tag) if api_response: - ret['API_Response'] = stream_data + ret["API_Response"] = stream_data if rm: - ret.pop('Intermediate_Containers', None) + ret.pop("Intermediate_Containers", None) return ret -def commit(name, - repository, - tag='latest', - message=None, - author=None): - ''' +def commit(name, repository, tag="latest", message=None, author=None): + """ .. versionchanged:: 2018.3.0 The repository and tag must now be passed separately using the ``repository`` and ``tag`` arguments, rather than together in the (now @@ -4149,7 +4159,7 @@ def commit(name, .. code-block:: bash salt myminion docker.commit mycontainer myuser/myimage mytag - ''' + """ if not isinstance(repository, six.string_types): repository = six.text_type(repository) if not isinstance(tag, six.string_types): @@ -4157,30 +4167,26 @@ def commit(name, time_started = time.time() response = _client_wrapper( - 'commit', - name, - repository=repository, - tag=tag, - message=message, - author=author) - ret = {'Time_Elapsed': time.time() - time_started} + "commit", name, repository=repository, tag=tag, message=message, author=author + ) + ret = {"Time_Elapsed": time.time() - time_started} _clear_context() image_id = None - for id_ in ('Id', 'id', 'ID'): + for id_ in ("Id", "id", "ID"): if id_ in response: image_id = response[id_] break if image_id is None: - raise CommandExecutionError('No image ID was returned in API response') + raise CommandExecutionError("No image ID was returned in API response") - ret['Id'] = image_id + ret["Id"] = image_id return ret def dangling(prune=False, force=False): - ''' + """ Return top-level images (those on which no other images depend) which do not have a tag assigned to them. These include: @@ -4217,30 +4223,30 @@ def dangling(prune=False, force=False): salt myminion docker.dangling salt myminion docker.dangling prune=True - ''' + """ all_images = images(all=True) - dangling_images = [x[:12] for x in _get_top_level_images(all_images) - if all_images[x]['RepoTags'] is None] + dangling_images = [ + x[:12] + for x in _get_top_level_images(all_images) + if all_images[x]["RepoTags"] is None + ] if not prune: return dangling_images ret = {} for image in dangling_images: try: - ret.setdefault(image, {})['Removed'] = rmi(image, force=force) + ret.setdefault(image, {})["Removed"] = rmi(image, force=force) except Exception as exc: # pylint: disable=broad-except err = exc.__str__() log.error(err) - ret.setdefault(image, {})['Comment'] = err - ret[image]['Removed'] = False + ret.setdefault(image, {})["Comment"] = err + ret[image]["Removed"] = False return ret -def import_(source, - repository, - tag='latest', - api_response=False): - ''' +def import_(source, repository, tag="latest", api_response=False): + """ .. versionchanged:: 2018.3.0 The repository and tag must now be passed separately using the ``repository`` and ``tag`` arguments, rather than together in the (now @@ -4290,29 +4296,25 @@ def import_(source, salt myminion docker.import /tmp/cent7-minimal.tar.xz myuser/centos salt myminion docker.import /tmp/cent7-minimal.tar.xz myuser/centos:7 salt myminion docker.import salt://dockerimages/cent7-minimal.tar.xz myuser/centos:7 - ''' + """ if not isinstance(repository, six.string_types): repository = six.text_type(repository) if not isinstance(tag, six.string_types): tag = six.text_type(tag) - path = __salt__['container_resource.cache_file'](source) + path = __salt__["container_resource.cache_file"](source) time_started = time.time() - response = _client_wrapper('import_image', - path, - repository=repository, - tag=tag) - ret = {'Time_Elapsed': time.time() - time_started} + response = _client_wrapper("import_image", path, repository=repository, tag=tag) + ret = {"Time_Elapsed": time.time() - time_started} _clear_context() if not response: raise CommandExecutionError( - 'Import failed for {0}, no response returned from Docker API' - .format(source) + "Import failed for {0}, no response returned from Docker API".format(source) ) elif api_response: - ret['API_Response'] = response + ret["API_Response"] = response errors = [] # Iterate through API response and collect information @@ -4321,26 +4323,24 @@ def import_(source, item_type = next(iter(item)) except StopIteration: continue - if item_type == 'status': + if item_type == "status": _import_status(ret, item, repository, tag) - elif item_type == 'errorDetail': + elif item_type == "errorDetail": _error_detail(errors, item) - if 'Id' not in ret: + if "Id" not in ret: # API returned information, but there was no confirmation of a # successful push. - msg = 'Import failed for {0}'.format(source) + msg = "Import failed for {0}".format(source) if errors: - msg += '. Error(s) follow:\n\n{0}'.format( - '\n\n'.join(errors) - ) + msg += ". Error(s) follow:\n\n{0}".format("\n\n".join(errors)) raise CommandExecutionError(msg) return ret def load(path, repository=None, tag=None): - ''' + """ .. versionchanged:: 2018.3.0 If the loaded image should be tagged, then the repository and tag must now be passed separately using the ``repository`` and ``tag`` @@ -4400,41 +4400,37 @@ def load(path, repository=None, tag=None): salt myminion docker.load /path/to/image.tar salt myminion docker.load salt://path/to/docker/saved/image.tar repository=myuser/myimage tag=mytag - ''' + """ if (repository or tag) and not (repository and tag): # Have to have both or neither - raise SaltInvocationError( - 'If tagging, both a repository and tag are required' - ) + raise SaltInvocationError("If tagging, both a repository and tag are required") - local_path = __salt__['container_resource.cache_file'](path) + local_path = __salt__["container_resource.cache_file"](path) if not os.path.isfile(local_path): - raise CommandExecutionError( - 'Source file {0} does not exist'.format(path) - ) + raise CommandExecutionError("Source file {0} does not exist".format(path)) pre = images(all=True) - cmd = ['docker', 'load', '-i', local_path] + cmd = ["docker", "load", "-i", local_path] time_started = time.time() - result = __salt__['cmd.run_all'](cmd) - ret = {'Time_Elapsed': time.time() - time_started} + result = __salt__["cmd.run_all"](cmd) + ret = {"Time_Elapsed": time.time() - time_started} _clear_context() post = images(all=True) - if result['retcode'] != 0: - msg = 'Failed to load image(s) from {0}'.format(path) - if result['stderr']: - msg += ': {0}'.format(result['stderr']) + if result["retcode"] != 0: + msg = "Failed to load image(s) from {0}".format(path) + if result["stderr"]: + msg += ": {0}".format(result["stderr"]) raise CommandExecutionError(msg) - ret['Path'] = path + ret["Path"] = path new_layers = [x for x in post if x not in pre] - ret['Layers'] = [x[:12] for x in new_layers] + ret["Layers"] = [x[:12] for x in new_layers] top_level_images = _get_top_level_images(post, subset=new_layers) if repository or tag: if len(top_level_images) > 1: - ret['Warning'] = ( - 'More than one top-level image layer was loaded ({0}), no ' - 'image was tagged'.format(', '.join(top_level_images)) + ret["Warning"] = ( + "More than one top-level image layer was loaded ({0}), no " + "image was tagged".format(", ".join(top_level_images)) ) else: # Normally just joining the two would be quicker than a str.format, @@ -4442,26 +4438,23 @@ def load(path, repository=None, tag=None): # strings when passed (e.g. a numeric tag would be loaded as an int # or float), and because the tag_ function will stringify them if # need be, a str.format is the correct thing to do here. - tagged_image = '{0}:{1}'.format(repository, tag) + tagged_image = "{0}:{1}".format(repository, tag) try: - result = tag_(top_level_images[0], - repository=repository, - tag=tag) - ret['Image'] = tagged_image + result = tag_(top_level_images[0], repository=repository, tag=tag) + ret["Image"] = tagged_image except IndexError: - ret['Warning'] = ('No top-level image layers were loaded, no ' - 'image was tagged') + ret["Warning"] = ( + "No top-level image layers were loaded, no " "image was tagged" + ) except Exception as exc: # pylint: disable=broad-except - ret['Warning'] = ( - 'Failed to tag {0} as {1}: {2}'.format( - top_level_images[0], tagged_image, exc - ) + ret["Warning"] = "Failed to tag {0} as {1}: {2}".format( + top_level_images[0], tagged_image, exc ) return ret def layers(name): - ''' + """ Returns a list of the IDs of layers belonging to the specified image, with the top-most layer (the one correspnding to the passed name) appearing last. @@ -4474,22 +4467,25 @@ def layers(name): .. code-block:: bash salt myminion docker.layers centos:7 - ''' + """ ret = [] - cmd = ['docker', 'history', '-q', name] + cmd = ["docker", "history", "-q", name] for line in reversed( - __salt__['cmd.run_stdout'](cmd, python_shell=False).splitlines()): + __salt__["cmd.run_stdout"](cmd, python_shell=False).splitlines() + ): ret.append(line) if not ret: - raise CommandExecutionError('Image \'{0}\' not found'.format(name)) + raise CommandExecutionError("Image '{0}' not found".format(name)) return ret -def pull(image, - insecure_registry=False, - api_response=False, - client_timeout=salt.utils.docker.CLIENT_TIMEOUT): - ''' +def pull( + image, + insecure_registry=False, + api_response=False, + client_timeout=salt.utils.docker.CLIENT_TIMEOUT, +): + """ .. versionchanged:: 2018.3.0 If no tag is specified in the ``image`` argument, all tags for the image will be pulled. For this reason is it recommended to pass @@ -4537,58 +4533,58 @@ def pull(image, salt myminion docker.pull centos salt myminion docker.pull centos:6 - ''' + """ _prep_pull() - kwargs = {'stream': True, - 'client_timeout': client_timeout} + kwargs = {"stream": True, "client_timeout": client_timeout} if insecure_registry: - kwargs['insecure_registry'] = insecure_registry + kwargs["insecure_registry"] = insecure_registry time_started = time.time() - response = _client_wrapper('pull', image, **kwargs) - ret = {'Time_Elapsed': time.time() - time_started, 'retcode': 0} + response = _client_wrapper("pull", image, **kwargs) + ret = {"Time_Elapsed": time.time() - time_started, "retcode": 0} _clear_context() if not response: raise CommandExecutionError( - 'Pull failed for {0}, no response returned from Docker API' - .format(image) + "Pull failed for {0}, no response returned from Docker API".format(image) ) elif api_response: - ret['API_Response'] = response + ret["API_Response"] = response errors = [] # Iterate through API response and collect information for event in response: - log.debug('pull event: %s', event) + log.debug("pull event: %s", event) try: event = salt.utils.json.loads(event) except Exception as exc: # pylint: disable=broad-except raise CommandExecutionError( - 'Unable to interpret API event: \'{0}\''.format(event), - info={'Error': exc.__str__()} + "Unable to interpret API event: '{0}'".format(event), + info={"Error": exc.__str__()}, ) try: event_type = next(iter(event)) except StopIteration: continue - if event_type == 'status': + if event_type == "status": _pull_status(ret, event) - elif event_type == 'errorDetail': + elif event_type == "errorDetail": _error_detail(errors, event) if errors: - ret['Errors'] = errors - ret['retcode'] = 1 + ret["Errors"] = errors + ret["retcode"] = 1 return ret -def push(image, - insecure_registry=False, - api_response=False, - client_timeout=salt.utils.docker.CLIENT_TIMEOUT): - ''' +def push( + image, + insecure_registry=False, + api_response=False, + client_timeout=salt.utils.docker.CLIENT_TIMEOUT, +): + """ .. versionchanged:: 2015.8.4 The ``Id`` and ``Image`` keys are no longer present in the return data. This is due to changes in the Docker Remote API. @@ -4632,27 +4628,25 @@ def push(image, salt myminion docker.push myuser/mycontainer salt myminion docker.push myuser/mycontainer:mytag - ''' + """ if not isinstance(image, six.string_types): image = six.text_type(image) - kwargs = {'stream': True, - 'client_timeout': client_timeout} + kwargs = {"stream": True, "client_timeout": client_timeout} if insecure_registry: - kwargs['insecure_registry'] = insecure_registry + kwargs["insecure_registry"] = insecure_registry time_started = time.time() - response = _client_wrapper('push', image, **kwargs) - ret = {'Time_Elapsed': time.time() - time_started, 'retcode': 0} + response = _client_wrapper("push", image, **kwargs) + ret = {"Time_Elapsed": time.time() - time_started, "retcode": 0} _clear_context() if not response: raise CommandExecutionError( - 'Push failed for {0}, no response returned from Docker API' - .format(image) + "Push failed for {0}, no response returned from Docker API".format(image) ) elif api_response: - ret['API_Response'] = response + ret["API_Response"] = response errors = [] # Iterate through API response and collect information @@ -4661,26 +4655,26 @@ def push(image, event = salt.utils.json.loads(event) except Exception as exc: # pylint: disable=broad-except raise CommandExecutionError( - 'Unable to interpret API event: \'{0}\''.format(event), - info={'Error': exc.__str__()} + "Unable to interpret API event: '{0}'".format(event), + info={"Error": exc.__str__()}, ) try: event_type = next(iter(event)) except StopIteration: continue - if event_type == 'status': + if event_type == "status": _push_status(ret, event) - elif event_type == 'errorDetail': + elif event_type == "errorDetail": _error_detail(errors, event) if errors: - ret['Errors'] = errors - ret['retcode'] = 1 + ret["Errors"] = errors + ret["retcode"] = 1 return ret def rmi(*names, **kwargs): - ''' + """ Removes an image name @@ -4711,57 +4705,55 @@ def rmi(*names, **kwargs): salt myminion docker.rmi busybox salt myminion docker.rmi busybox force=True salt myminion docker.rmi foo bar baz - ''' + """ pre_images = images(all=True) pre_tags = list_tags() - force = kwargs.get('force', False) - noprune = not kwargs.get('prune', True) + force = kwargs.get("force", False) + noprune = not kwargs.get("prune", True) errors = [] for name in names: - image_id = inspect_image(name)['Id'] + image_id = inspect_image(name)["Id"] try: - _client_wrapper('remove_image', - image_id, - force=force, - noprune=noprune, - catch_api_errors=False) + _client_wrapper( + "remove_image", + image_id, + force=force, + noprune=noprune, + catch_api_errors=False, + ) except docker.errors.APIError as exc: if exc.response.status_code == 409: errors.append(exc.explanation) deps = depends(name) - if deps['Containers'] or deps['Images']: - err = 'Image is in use by ' - if deps['Containers']: - err += 'container(s): {0}'.format( - ', '.join(deps['Containers']) - ) - if deps['Images']: - if deps['Containers']: - err += ' and ' - err += 'image(s): {0}'.format(', '.join(deps['Images'])) + if deps["Containers"] or deps["Images"]: + err = "Image is in use by " + if deps["Containers"]: + err += "container(s): {0}".format(", ".join(deps["Containers"])) + if deps["Images"]: + if deps["Containers"]: + err += " and " + err += "image(s): {0}".format(", ".join(deps["Images"])) errors.append(err) else: - errors.append('Error {0}: {1}'.format(exc.response.status_code, - exc.explanation)) + errors.append( + "Error {0}: {1}".format(exc.response.status_code, exc.explanation) + ) _clear_context() - ret = {'Layers': [x for x in pre_images if x not in images(all=True)], - 'Tags': [x for x in pre_tags if x not in list_tags()], - 'retcode': 0} + ret = { + "Layers": [x for x in pre_images if x not in images(all=True)], + "Tags": [x for x in pre_tags if x not in list_tags()], + "retcode": 0, + } if errors: - ret['Errors'] = errors - ret['retcode'] = 1 + ret["Errors"] = errors + ret["retcode"] = 1 return ret -def save(name, - path, - overwrite=False, - makedirs=False, - compression=None, - **kwargs): - ''' +def save(name, path, overwrite=False, makedirs=False, compression=None, **kwargs): + """ Saves an image and to a file on the minion. Equivalent to running the ``docker save`` Docker CLI command, but unlike ``docker save`` this will also work on named images instead of just images IDs. @@ -4834,8 +4826,8 @@ def save(name, salt myminion docker.save centos:7 /tmp/cent7.tar salt myminion docker.save 0123456789ab cdef01234567 /tmp/saved.tar - ''' - err = 'Path \'{0}\' is not absolute'.format(path) + """ + err = "Path '{0}' is not absolute".format(path) try: if not os.path.isabs(path): raise SaltInvocationError(err) @@ -4843,89 +4835,89 @@ def save(name, raise SaltInvocationError(err) if os.path.exists(path) and not overwrite: - raise CommandExecutionError('{0} already exists'.format(path)) + raise CommandExecutionError("{0} already exists".format(path)) if compression is None: - if path.endswith('.tar.gz') or path.endswith('.tgz'): - compression = 'gzip' - elif path.endswith('.tar.bz2') or path.endswith('.tbz2'): - compression = 'bzip2' - elif path.endswith('.tar.xz') or path.endswith('.txz'): + if path.endswith(".tar.gz") or path.endswith(".tgz"): + compression = "gzip" + elif path.endswith(".tar.bz2") or path.endswith(".tbz2"): + compression = "bzip2" + elif path.endswith(".tar.xz") or path.endswith(".txz"): if HAS_LZMA: - compression = 'xz' + compression = "xz" else: raise CommandExecutionError( - 'XZ compression unavailable. Install the backports.lzma ' - 'module and xz-utils to enable XZ compression.' + "XZ compression unavailable. Install the backports.lzma " + "module and xz-utils to enable XZ compression." ) - elif compression == 'gz': - compression = 'gzip' - elif compression == 'bz2': - compression = 'bzip2' - elif compression == 'lzma': - compression = 'xz' + elif compression == "gz": + compression = "gzip" + elif compression == "bz2": + compression = "bzip2" + elif compression == "lzma": + compression = "xz" - if compression and compression not in ('gzip', 'bzip2', 'xz'): - raise SaltInvocationError( - 'Invalid compression type \'{0}\''.format(compression) - ) + if compression and compression not in ("gzip", "bzip2", "xz"): + raise SaltInvocationError("Invalid compression type '{0}'".format(compression)) parent_dir = os.path.dirname(path) if not os.path.isdir(parent_dir): if not makedirs: raise CommandExecutionError( - 'Parent dir \'{0}\' of destination path does not exist. Use ' - 'makedirs=True to create it.'.format(parent_dir) + "Parent dir '{0}' of destination path does not exist. Use " + "makedirs=True to create it.".format(parent_dir) ) if compression: - saved_path = __utils__['files.mkstemp']() + saved_path = __utils__["files.mkstemp"]() else: saved_path = path # use the image name if its valid if not use the image id - image_to_save = name if name in inspect_image(name)['RepoTags'] else inspect_image(name)['Id'] - cmd = ['docker', 'save', '-o', saved_path, image_to_save] + image_to_save = ( + name if name in inspect_image(name)["RepoTags"] else inspect_image(name)["Id"] + ) + cmd = ["docker", "save", "-o", saved_path, image_to_save] time_started = time.time() - result = __salt__['cmd.run_all'](cmd, python_shell=False) - if result['retcode'] != 0: - err = 'Failed to save image(s) to {0}'.format(path) - if result['stderr']: - err += ': {0}'.format(result['stderr']) + result = __salt__["cmd.run_all"](cmd, python_shell=False) + if result["retcode"] != 0: + err = "Failed to save image(s) to {0}".format(path) + if result["stderr"]: + err += ": {0}".format(result["stderr"]) raise CommandExecutionError(err) if compression: - if compression == 'gzip': + if compression == "gzip": try: - out = gzip.open(path, 'wb') + out = gzip.open(path, "wb") except OSError as exc: raise CommandExecutionError( - 'Unable to open {0} for writing: {1}'.format(path, exc) + "Unable to open {0} for writing: {1}".format(path, exc) ) - elif compression == 'bzip2': + elif compression == "bzip2": compressor = bz2.BZ2Compressor() - elif compression == 'xz': + elif compression == "xz": compressor = lzma.LZMACompressor() try: - with __utils__['files.fopen'](saved_path, 'rb') as uncompressed: + with __utils__["files.fopen"](saved_path, "rb") as uncompressed: # No need to decode on read and encode on on write, since we're # reading and immediately writing out bytes. - if compression != 'gzip': + if compression != "gzip": # gzip doesn't use a Compressor object, it uses a .open() # method to open the filehandle. If not using gzip, we need # to open the filehandle here. - out = __utils__['files.fopen'](path, 'wb') + out = __utils__["files.fopen"](path, "wb") buf = None - while buf != '': + while buf != "": buf = uncompressed.read(4096) if buf: - if compression in ('bzip2', 'xz'): + if compression in ("bzip2", "xz"): data = compressor.compress(buf) if data: out.write(data) else: out.write(buf) - if compression in ('bzip2', 'xz'): + if compression in ("bzip2", "xz"): # Flush any remaining data out of the compressor data = compressor.flush() if data: @@ -4937,7 +4929,7 @@ def save(name, except OSError: pass raise CommandExecutionError( - 'Error occurred during image save: {0}'.format(exc) + "Error occurred during image save: {0}".format(exc) ) finally: try: @@ -4946,21 +4938,21 @@ def save(name, except OSError: pass out.close() - ret = {'Time_Elapsed': time.time() - time_started} + ret = {"Time_Elapsed": time.time() - time_started} - ret['Path'] = path - ret['Size'] = os.stat(path).st_size - ret['Size_Human'] = _size_fmt(ret['Size']) + ret["Path"] = path + ret["Size"] = os.stat(path).st_size + ret["Size_Human"] = _size_fmt(ret["Size"]) # Process push - if kwargs.get('push', False): - ret['Push'] = __salt__['cp.push'](path) + if kwargs.get("push", False): + ret["Push"] = __salt__["cp.push"](path) return ret -def tag_(name, repository, tag='latest', force=False): - ''' +def tag_(name, repository, tag="latest", force=False): + """ .. versionchanged:: 2018.3.0 The repository and tag must now be passed separately using the ``repository`` and ``tag`` arguments, rather than together in the (now @@ -4994,18 +4986,16 @@ def tag_(name, repository, tag='latest', force=False): .. code-block:: bash salt myminion docker.tag 0123456789ab myrepo/mycontainer mytag - ''' + """ if not isinstance(repository, six.string_types): repository = six.text_type(repository) if not isinstance(tag, six.string_types): tag = six.text_type(tag) - image_id = inspect_image(name)['Id'] - response = _client_wrapper('tag', - image_id, - repository=repository, - tag=tag, - force=force) + image_id = inspect_image(name)["Id"] + response = _client_wrapper( + "tag", image_id, repository=repository, tag=tag, force=force + ) _clear_context() # Only non-error return case is a True return, so just return the response return response @@ -5013,7 +5003,7 @@ def tag_(name, repository, tag='latest', force=False): # Network Management def networks(names=None, ids=None): - ''' + """ .. versionchanged:: 2017.7.0 The ``names`` and ``ids`` can be passed as a comma-separated list now, as well as a Python list. @@ -5034,33 +5024,35 @@ def networks(names=None, ids=None): salt myminion docker.networks names=network-web salt myminion docker.networks ids=1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc - ''' + """ if names is not None: - names = __utils__['args.split_input'](names) + names = __utils__["args.split_input"](names) if ids is not None: - ids = __utils__['args.split_input'](ids) + ids = __utils__["args.split_input"](ids) - response = _client_wrapper('networks', names=names, ids=ids) + response = _client_wrapper("networks", names=names, ids=ids) # Work around https://github.com/docker/docker-py/issues/1775 for idx, netinfo in enumerate(response): try: - containers = inspect_network(netinfo['Id'])['Containers'] + containers = inspect_network(netinfo["Id"])["Containers"] except Exception: # pylint: disable=broad-except continue else: if containers: - response[idx]['Containers'] = containers + response[idx]["Containers"] = containers return response -def create_network(name, - skip_translate=None, - ignore_collisions=False, - validate_ip_addrs=True, - client_timeout=salt.utils.docker.CLIENT_TIMEOUT, - **kwargs): - ''' +def create_network( + name, + skip_translate=None, + ignore_collisions=False, + validate_ip_addrs=True, + client_timeout=salt.utils.docker.CLIENT_TIMEOUT, + **kwargs +): + """ .. versionchanged:: 2018.3.0 Support added for network configuration options other than ``driver`` and ``driver_opts``, as well as IPAM configuration. @@ -5293,39 +5285,42 @@ def create_network(name, salt myminion docker.create_network macvlan_network driver=macvlan driver_opts="{'parent':'eth0'}" gateway=172.20.0.1 subnet=172.20.0.0/24 # IPv4 and IPv6 salt myminion docker.create_network mynet ipam_pools='[{"subnet": "10.0.0.0/24", "gateway": "10.0.0.1"}, {"subnet": "fe3f:2180:26:1::60/123", "gateway": "fe3f:2180:26:1::61"}]' - ''' - kwargs = __utils__['docker.translate_input']( + """ + kwargs = __utils__["docker.translate_input"]( salt.utils.docker.translate.network, skip_translate=skip_translate, ignore_collisions=ignore_collisions, validate_ip_addrs=validate_ip_addrs, - **__utils__['args.clean_kwargs'](**kwargs)) + **__utils__["args.clean_kwargs"](**kwargs) + ) - if 'ipam' not in kwargs: + if "ipam" not in kwargs: ipam_kwargs = {} for key in [ - x for x in ['ipam_driver', 'ipam_opts'] - + get_client_args('ipam_config')['ipam_config'] - if x in kwargs]: + x + for x in ["ipam_driver", "ipam_opts"] + + get_client_args("ipam_config")["ipam_config"] + if x in kwargs + ]: ipam_kwargs[key] = kwargs.pop(key) - ipam_pools = kwargs.pop('ipam_pools', ()) + ipam_pools = kwargs.pop("ipam_pools", ()) # Don't go through the work of building a config dict if no # IPAM-specific configuration was passed. Just create the network # without specifying IPAM configuration. if ipam_pools or ipam_kwargs: - kwargs['ipam'] = __utils__['docker.create_ipam_config']( - *ipam_pools, - **ipam_kwargs) + kwargs["ipam"] = __utils__["docker.create_ipam_config"]( + *ipam_pools, **ipam_kwargs + ) - response = _client_wrapper('create_network', name, **kwargs) + response = _client_wrapper("create_network", name, **kwargs) _clear_context() # Only non-error return case is a True return, so just return the response return response def remove_network(network_id): - ''' + """ Remove a network network_id @@ -5337,14 +5332,14 @@ def remove_network(network_id): salt myminion docker.remove_network mynet salt myminion docker.remove_network 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc - ''' - response = _client_wrapper('remove_network', network_id) + """ + response = _client_wrapper("remove_network", network_id) _clear_context() return True def inspect_network(network_id): - ''' + """ Inspect Network network_id @@ -5355,15 +5350,15 @@ def inspect_network(network_id): .. code-block:: bash salt myminion docker.inspect_network 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc - ''' - response = _client_wrapper('inspect_network', network_id) + """ + response = _client_wrapper("inspect_network", network_id) _clear_context() # Only non-error return case is a True return, so just return the response return response def connect_container_to_network(container, net_id, **kwargs): - ''' + """ .. versionadded:: 2015.8.3 .. versionchanged:: 2017.7.0 Support for ``ipv4_address`` argument added @@ -5388,26 +5383,27 @@ def connect_container_to_network(container, net_id, **kwargs): salt myminion docker.connect_container_to_network web-1 mynet salt myminion docker.connect_container_to_network web-1 mynet ipv4_address=10.20.0.10 salt myminion docker.connect_container_to_network web-1 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc - ''' - kwargs = __utils__['args.clean_kwargs'](**kwargs) + """ + kwargs = __utils__["args.clean_kwargs"](**kwargs) log.debug( - 'Connecting container \'%s\' to network \'%s\' with the following ' - 'configuration: %s', container, net_id, kwargs + "Connecting container '%s' to network '%s' with the following " + "configuration: %s", + container, + net_id, + kwargs, + ) + response = _client_wrapper( + "connect_container_to_network", container, net_id, **kwargs ) - response = _client_wrapper('connect_container_to_network', - container, - net_id, - **kwargs) log.debug( - 'Successfully connected container \'%s\' to network \'%s\'', - container, net_id + "Successfully connected container '%s' to network '%s'", container, net_id ) _clear_context() return True def disconnect_container_from_network(container, network_id): - ''' + """ .. versionadded:: 2015.8.3 Disconnect container from network @@ -5424,24 +5420,22 @@ def disconnect_container_from_network(container, network_id): salt myminion docker.disconnect_container_from_network web-1 mynet salt myminion docker.disconnect_container_from_network web-1 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc - ''' - log.debug( - 'Disconnecting container \'%s\' from network \'%s\'', - container, network_id + """ + log.debug("Disconnecting container '%s' from network '%s'", container, network_id) + response = _client_wrapper( + "disconnect_container_from_network", container, network_id ) - response = _client_wrapper('disconnect_container_from_network', - container, - network_id) log.debug( - 'Successfully disconnected container \'%s\' from network \'%s\'', - container, network_id + "Successfully disconnected container '%s' from network '%s'", + container, + network_id, ) _clear_context() return True def disconnect_all_containers_from_network(network_id): - ''' + """ .. versionadded:: 2018.3.0 Runs :py:func:`docker.disconnect_container_from_network @@ -5458,7 +5452,7 @@ def disconnect_all_containers_from_network(network_id): salt myminion docker.disconnect_all_containers_from_network mynet salt myminion docker.disconnect_all_containers_from_network 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc - ''' + """ connected_containers = connected(network_id) ret = [] failed = [] @@ -5468,22 +5462,22 @@ def disconnect_all_containers_from_network(network_id): ret.append(cname) except CommandExecutionError as exc: msg = exc.__str__() - if '404' not in msg: + if "404" not in msg: # If 404 was in the error, then the container no longer exists, # so to avoid a race condition we won't consider 404 errors to # men that removal failed. failed.append(msg) if failed: raise CommandExecutionError( - 'One or more containers failed to be removed', - info={'removed': ret, 'errors': failed} + "One or more containers failed to be removed", + info={"removed": ret, "errors": failed}, ) return ret # Volume Management def volumes(filters=None): - ''' + """ List existing volumes .. versionadded:: 2015.8.4 @@ -5496,14 +5490,14 @@ def volumes(filters=None): .. code-block:: bash salt myminion docker.volumes filters="{'dangling': True}" - ''' - response = _client_wrapper('volumes', filters=filters) + """ + response = _client_wrapper("volumes", filters=filters) # Only non-error return case is a True return, so just return the response return response def create_volume(name, driver=None, driver_opts=None): - ''' + """ Create a new volume .. versionadded:: 2015.8.4 @@ -5522,16 +5516,17 @@ def create_volume(name, driver=None, driver_opts=None): .. code-block:: bash salt myminion docker.create_volume my_volume driver=local - ''' - response = _client_wrapper('create_volume', name, driver=driver, - driver_opts=driver_opts) + """ + response = _client_wrapper( + "create_volume", name, driver=driver, driver_opts=driver_opts + ) _clear_context() # Only non-error return case is a True return, so just return the response return response def remove_volume(name): - ''' + """ Remove a volume .. versionadded:: 2015.8.4 @@ -5544,14 +5539,14 @@ def remove_volume(name): .. code-block:: bash salt myminion docker.remove_volume my_volume - ''' - response = _client_wrapper('remove_volume', name) + """ + response = _client_wrapper("remove_volume", name) _clear_context() return True def inspect_volume(name): - ''' + """ Inspect Volume .. versionadded:: 2015.8.4 @@ -5564,8 +5559,8 @@ def inspect_volume(name): .. code-block:: bash salt myminion docker.inspect_volume my_volume - ''' - response = _client_wrapper('inspect_volume', name) + """ + response = _client_wrapper("inspect_volume", name) _clear_context() # Only non-error return case is a True return, so just return the response return response @@ -5574,7 +5569,7 @@ def inspect_volume(name): # Functions to manage container state @_refresh_mine_cache def kill(name): - ''' + """ Kill all processes in a running container instead of performing a graceful shutdown @@ -5596,13 +5591,13 @@ def kill(name): .. code-block:: bash salt myminion docker.kill mycontainer - ''' - return _change_state(name, 'kill', 'stopped') + """ + return _change_state(name, "kill", "stopped") @_refresh_mine_cache def pause(name): - ''' + """ Pauses a container name @@ -5624,21 +5619,22 @@ def pause(name): .. code-block:: bash salt myminion docker.pause mycontainer - ''' + """ orig_state = state(name) - if orig_state == 'stopped': - return {'result': False, - 'state': {'old': orig_state, 'new': orig_state}, - 'comment': ('Container \'{0}\' is stopped, cannot pause' - .format(name))} - return _change_state(name, 'pause', 'paused') + if orig_state == "stopped": + return { + "result": False, + "state": {"old": orig_state, "new": orig_state}, + "comment": ("Container '{0}' is stopped, cannot pause".format(name)), + } + return _change_state(name, "pause", "paused") -freeze = salt.utils.functools.alias_function(pause, 'freeze') +freeze = salt.utils.functools.alias_function(pause, "freeze") def restart(name, timeout=10): - ''' + """ Restarts a container name @@ -5666,16 +5662,16 @@ def restart(name, timeout=10): salt myminion docker.restart mycontainer salt myminion docker.restart mycontainer timeout=20 - ''' - ret = _change_state(name, 'restart', 'running', timeout=timeout) - if ret['result']: - ret['restarted'] = True + """ + ret = _change_state(name, "restart", "running", timeout=timeout) + if ret["result"]: + ret["restarted"] = True return ret @_refresh_mine_cache def signal_(name, signal): - ''' + """ Send a signal to a container. Signals can be either strings or numbers, and are defined in the **Standard Signals** section of the ``signal(7)`` manpage. Run ``man 7 signal`` on a Linux host to browse this manpage. @@ -5696,14 +5692,14 @@ def signal_(name, signal): .. code-block:: bash salt myminion docker.signal mycontainer SIGHUP - ''' - _client_wrapper('kill', name, signal=signal) + """ + _client_wrapper("kill", name, signal=signal) return True @_refresh_mine_cache def start_(name): - ''' + """ Start a container name @@ -5724,20 +5720,21 @@ def start_(name): .. code-block:: bash salt myminion docker.start mycontainer - ''' + """ orig_state = state(name) - if orig_state == 'paused': - return {'result': False, - 'state': {'old': orig_state, 'new': orig_state}, - 'comment': ('Container \'{0}\' is paused, cannot start' - .format(name))} + if orig_state == "paused": + return { + "result": False, + "state": {"old": orig_state, "new": orig_state}, + "comment": ("Container '{0}' is paused, cannot start".format(name)), + } - return _change_state(name, 'start', 'running') + return _change_state(name, "start", "running") @_refresh_mine_cache def stop(name, timeout=None, **kwargs): - ''' + """ Stops a running container name @@ -5774,38 +5771,41 @@ def stop(name, timeout=None, **kwargs): salt myminion docker.stop mycontainer salt myminion docker.stop mycontainer unpause=True salt myminion docker.stop mycontainer timeout=20 - ''' + """ if timeout is None: try: # Get timeout from container config - timeout = inspect_container(name)['Config']['StopTimeout'] + timeout = inspect_container(name)["Config"]["StopTimeout"] except KeyError: # Fall back to a global default defined in salt.utils.docker timeout = salt.utils.docker.SHUTDOWN_TIMEOUT orig_state = state(name) - if orig_state == 'paused': - if kwargs.get('unpause', False): - unpause_result = _change_state(name, 'unpause', 'running') - if unpause_result['result'] is False: - unpause_result['comment'] = ( - 'Failed to unpause container \'{0}\''.format(name) + if orig_state == "paused": + if kwargs.get("unpause", False): + unpause_result = _change_state(name, "unpause", "running") + if unpause_result["result"] is False: + unpause_result["comment"] = "Failed to unpause container '{0}'".format( + name ) return unpause_result else: - return {'result': False, - 'state': {'old': orig_state, 'new': orig_state}, - 'comment': ('Container \'{0}\' is paused, run with ' - 'unpause=True to unpause before stopping' - .format(name))} - ret = _change_state(name, 'stop', 'stopped', timeout=timeout) - ret['state']['old'] = orig_state + return { + "result": False, + "state": {"old": orig_state, "new": orig_state}, + "comment": ( + "Container '{0}' is paused, run with " + "unpause=True to unpause before stopping".format(name) + ), + } + ret = _change_state(name, "stop", "stopped", timeout=timeout) + ret["state"]["old"] = orig_state return ret @_refresh_mine_cache def unpause(name): - ''' + """ Unpauses a container name @@ -5827,21 +5827,22 @@ def unpause(name): .. code-block:: bash salt myminion docker.pause mycontainer - ''' + """ orig_state = state(name) - if orig_state == 'stopped': - return {'result': False, - 'state': {'old': orig_state, 'new': orig_state}, - 'comment': ('Container \'{0}\' is stopped, cannot unpause' - .format(name))} - return _change_state(name, 'unpause', 'running') + if orig_state == "stopped": + return { + "result": False, + "state": {"old": orig_state, "new": orig_state}, + "comment": ("Container '{0}' is stopped, cannot unpause".format(name)), + } + return _change_state(name, "unpause", "running") -unfreeze = salt.utils.functools.alias_function(unpause, 'unfreeze') +unfreeze = salt.utils.functools.alias_function(unpause, "unfreeze") def wait(name, ignore_already_stopped=False, fail_on_exit_status=False): - ''' + """ Wait for the container to exit gracefully, and return its exit code .. note:: @@ -5875,15 +5876,17 @@ def wait(name, ignore_already_stopped=False, fail_on_exit_status=False): .. code-block:: bash salt myminion docker.wait mycontainer - ''' + """ try: pre = state(name) except CommandExecutionError: # Container doesn't exist anymore - return {'result': ignore_already_stopped, - 'comment': 'Container \'{0}\' absent'.format(name)} - already_stopped = pre == 'stopped' - response = _client_wrapper('wait', name) + return { + "result": ignore_already_stopped, + "comment": "Container '{0}' absent".format(name), + } + already_stopped = pre == "stopped" + response = _client_wrapper("wait", name) _clear_context() try: post = state(name) @@ -5893,24 +5896,33 @@ def wait(name, ignore_already_stopped=False, fail_on_exit_status=False): if already_stopped: success = ignore_already_stopped - elif post == 'stopped': + elif post == "stopped": success = True else: success = False - result = {'result': success, - 'state': {'old': pre, 'new': post}, - 'exit_status': response} + result = { + "result": success, + "state": {"old": pre, "new": post}, + "exit_status": response, + } if already_stopped: - result['comment'] = 'Container \'{0}\' already stopped'.format(name) - if fail_on_exit_status and result['result']: - result['result'] = result['exit_status'] == 0 + result["comment"] = "Container '{0}' already stopped".format(name) + if fail_on_exit_status and result["result"]: + result["result"] = result["exit_status"] == 0 return result -def prune(containers=False, networks=False, images=False, - build=False, volumes=False, system=None, **filters): - ''' +def prune( + containers=False, + networks=False, + images=False, + build=False, + volumes=False, + system=None, + **filters +): + """ .. versionadded:: 2019.2.0 Prune Docker's various subsystems @@ -5971,11 +5983,11 @@ def prune(containers=False, networks=False, images=False, salt myminion docker.prune system=True until=12h salt myminion docker.prune images=True dangling=True salt myminion docker.prune images=True label=foo,bar=baz - ''' + """ if system is None and not any((containers, images, networks, build)): system = True - filters = __utils__['args.clean_kwargs'](**filters) + filters = __utils__["args.clean_kwargs"](**filters) for fname in list(filters): if not isinstance(filters[fname], bool): # support comma-separated values @@ -5983,49 +5995,48 @@ def prune(containers=False, networks=False, images=False, ret = {} if system or containers: - ret['containers'] = _client_wrapper('prune_containers', filters=filters) + ret["containers"] = _client_wrapper("prune_containers", filters=filters) if system or images: - ret['images'] = _client_wrapper('prune_images', filters=filters) + ret["images"] = _client_wrapper("prune_images", filters=filters) if system or networks: - ret['networks'] = _client_wrapper('prune_networks', filters=filters) + ret["networks"] = _client_wrapper("prune_networks", filters=filters) if system or build: try: # Doesn't exist currently in docker-py as of 3.0.1 - ret['build'] = _client_wrapper('prune_build', filters=filters) + ret["build"] = _client_wrapper("prune_build", filters=filters) except SaltInvocationError: # It's not in docker-py yet, POST directly to the API endpoint - ret['build'] = _client_wrapper( - '_result', - _client_wrapper( - '_post', - _client_wrapper('_url', '/build/prune') - ), - True + ret["build"] = _client_wrapper( + "_result", + _client_wrapper("_post", _client_wrapper("_url", "/build/prune")), + True, ) if volumes: - ret['volumes'] = _client_wrapper('prune_volumes', filters=filters) + ret["volumes"] = _client_wrapper("prune_volumes", filters=filters) return ret # Functions to run commands inside containers @_refresh_mine_cache -def _run(name, - cmd, - exec_driver=None, - output=None, - stdin=None, - python_shell=True, - output_loglevel='debug', - ignore_retcode=False, - use_vt=False, - keep_env=None): - ''' +def _run( + name, + cmd, + exec_driver=None, + output=None, + stdin=None, + python_shell=True, + output_loglevel="debug", + ignore_retcode=False, + use_vt=False, + keep_env=None, +): + """ Common logic for docker.run functions - ''' + """ if exec_driver is None: exec_driver = _get_exec_driver() - ret = __salt__['container_resource.run']( + ret = __salt__["container_resource.run"]( name, cmd, container_type=__virtualname__, @@ -6036,96 +6047,104 @@ def _run(name, output_loglevel=output_loglevel, ignore_retcode=ignore_retcode, use_vt=use_vt, - keep_env=keep_env) + keep_env=keep_env, + ) - if output in (None, 'all'): + if output in (None, "all"): return ret else: return ret[output] @_refresh_mine_cache -def _script(name, - source, - saltenv='base', - args=None, - template=None, - exec_driver=None, - stdin=None, - python_shell=True, - output_loglevel='debug', - ignore_retcode=False, - use_vt=False, - keep_env=None): - ''' +def _script( + name, + source, + saltenv="base", + args=None, + template=None, + exec_driver=None, + stdin=None, + python_shell=True, + output_loglevel="debug", + ignore_retcode=False, + use_vt=False, + keep_env=None, +): + """ Common logic to run a script on a container - ''' + """ + def _cleanup_tempfile(path): - ''' + """ Remove the tempfile allocated for the script - ''' + """ try: os.remove(path) except (IOError, OSError) as exc: - log.error( - 'cmd.script: Unable to clean tempfile \'%s\': %s', - path, exc - ) + log.error("cmd.script: Unable to clean tempfile '%s': %s", path, exc) - path = __utils__['files.mkstemp'](dir='/tmp', - prefix='salt', - suffix=os.path.splitext(source)[1]) + path = __utils__["files.mkstemp"]( + dir="/tmp", prefix="salt", suffix=os.path.splitext(source)[1] + ) if template: - fn_ = __salt__['cp.get_template'](source, path, template, saltenv) + fn_ = __salt__["cp.get_template"](source, path, template, saltenv) if not fn_: _cleanup_tempfile(path) - return {'pid': 0, - 'retcode': 1, - 'stdout': '', - 'stderr': '', - 'cache_error': True} + return { + "pid": 0, + "retcode": 1, + "stdout": "", + "stderr": "", + "cache_error": True, + } else: - fn_ = __salt__['cp.cache_file'](source, saltenv) + fn_ = __salt__["cp.cache_file"](source, saltenv) if not fn_: _cleanup_tempfile(path) - return {'pid': 0, - 'retcode': 1, - 'stdout': '', - 'stderr': '', - 'cache_error': True} + return { + "pid": 0, + "retcode": 1, + "stdout": "", + "stderr": "", + "cache_error": True, + } shutil.copyfile(fn_, path) if exec_driver is None: exec_driver = _get_exec_driver() copy_to(name, path, path, exec_driver=exec_driver) - run(name, 'chmod 700 ' + path) + run(name, "chmod 700 " + path) ret = run_all( name, - path + ' ' + six.text_type(args) if args else path, + path + " " + six.text_type(args) if args else path, exec_driver=exec_driver, stdin=stdin, python_shell=python_shell, output_loglevel=output_loglevel, ignore_retcode=ignore_retcode, use_vt=use_vt, - keep_env=keep_env) + keep_env=keep_env, + ) _cleanup_tempfile(path) - run(name, 'rm ' + path) + run(name, "rm " + path) return ret -def retcode(name, - cmd, - exec_driver=None, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' +def retcode( + name, + cmd, + exec_driver=None, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :py:func:`cmd.retcode ` within a container name @@ -6160,29 +6179,33 @@ def retcode(name, .. code-block:: bash salt myminion docker.retcode mycontainer 'ls -l /etc' - ''' - return _run(name, - cmd, - exec_driver=exec_driver, - output='retcode', - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) - - -def run(name, + """ + return _run( + name, cmd, - exec_driver=None, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' + exec_driver=exec_driver, + output="retcode", + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) + + +def run( + name, + cmd, + exec_driver=None, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :py:func:`cmd.run ` within a container name @@ -6217,29 +6240,33 @@ def run(name, .. code-block:: bash salt myminion docker.run mycontainer 'ls -l /etc' - ''' - return _run(name, - cmd, - exec_driver=exec_driver, - output=None, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) + """ + return _run( + name, + cmd, + exec_driver=exec_driver, + output=None, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) -def run_all(name, - cmd, - exec_driver=None, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' +def run_all( + name, + cmd, + exec_driver=None, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :py:func:`cmd.run_all ` within a container .. note:: @@ -6280,29 +6307,33 @@ def run_all(name, .. code-block:: bash salt myminion docker.run_all mycontainer 'ls -l /etc' - ''' - return _run(name, - cmd, - exec_driver=exec_driver, - output='all', - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) + """ + return _run( + name, + cmd, + exec_driver=exec_driver, + output="all", + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) -def run_stderr(name, - cmd, - exec_driver=None, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' +def run_stderr( + name, + cmd, + exec_driver=None, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :py:func:`cmd.run_stderr ` within a container @@ -6338,29 +6369,33 @@ def run_stderr(name, .. code-block:: bash salt myminion docker.run_stderr mycontainer 'ls -l /etc' - ''' - return _run(name, - cmd, - exec_driver=exec_driver, - output='stderr', - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) + """ + return _run( + name, + cmd, + exec_driver=exec_driver, + output="stderr", + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) -def run_stdout(name, - cmd, - exec_driver=None, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' +def run_stdout( + name, + cmd, + exec_driver=None, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :py:func:`cmd.run_stdout ` within a container @@ -6396,32 +6431,36 @@ def run_stdout(name, .. code-block:: bash salt myminion docker.run_stdout mycontainer 'ls -l /etc' - ''' - return _run(name, - cmd, - exec_driver=exec_driver, - output='stdout', - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) + """ + return _run( + name, + cmd, + exec_driver=exec_driver, + output="stdout", + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) -def script(name, - source, - saltenv='base', - args=None, - template=None, - exec_driver=None, - stdin=None, - python_shell=True, - output_loglevel='debug', - ignore_retcode=False, - use_vt=False, - keep_env=None): - ''' +def script( + name, + source, + saltenv="base", + args=None, + template=None, + exec_driver=None, + stdin=None, + python_shell=True, + output_loglevel="debug", + ignore_retcode=False, + use_vt=False, + keep_env=None, +): + """ Run :py:func:`cmd.script ` within a container .. note:: @@ -6472,34 +6511,38 @@ def script(name, salt myminion docker.script mycontainer salt://docker_script.py salt myminion docker.script mycontainer salt://scripts/runme.sh 'arg1 arg2 "arg 3"' salt myminion docker.script mycontainer salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' output_loglevel=quiet - ''' - return _script(name, - source, - saltenv=saltenv, - args=args, - template=template, - exec_driver=exec_driver, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - ignore_retcode=ignore_retcode, - use_vt=use_vt, - keep_env=keep_env) + """ + return _script( + name, + source, + saltenv=saltenv, + args=args, + template=template, + exec_driver=exec_driver, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + ignore_retcode=ignore_retcode, + use_vt=use_vt, + keep_env=keep_env, + ) -def script_retcode(name, - source, - saltenv='base', - args=None, - template=None, - exec_driver=None, - stdin=None, - python_shell=True, - output_loglevel='debug', - ignore_retcode=False, - use_vt=False, - keep_env=None): - ''' +def script_retcode( + name, + source, + saltenv="base", + args=None, + template=None, + exec_driver=None, + stdin=None, + python_shell=True, + output_loglevel="debug", + ignore_retcode=False, + use_vt=False, + keep_env=None, +): + """ Run :py:func:`cmd.script_retcode ` within a container @@ -6545,61 +6588,61 @@ def script_retcode(name, salt myminion docker.script_retcode mycontainer salt://docker_script.py salt myminion docker.script_retcode mycontainer salt://scripts/runme.sh 'arg1 arg2 "arg 3"' salt myminion docker.script_retcode mycontainer salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' output_loglevel=quiet - ''' - return _script(name, - source, - saltenv=saltenv, - args=args, - template=template, - exec_driver=exec_driver, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - ignore_retcode=ignore_retcode, - use_vt=use_vt, - keep_env=keep_env)['retcode'] + """ + return _script( + name, + source, + saltenv=saltenv, + args=args, + template=template, + exec_driver=exec_driver, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + ignore_retcode=ignore_retcode, + use_vt=use_vt, + keep_env=keep_env, + )["retcode"] def _mk_fileclient(): - ''' + """ Create a file client and add it to the context. - ''' - if 'cp.fileclient' not in __context__: - __context__['cp.fileclient'] = salt.fileclient.get_file_client(__opts__) + """ + if "cp.fileclient" not in __context__: + __context__["cp.fileclient"] = salt.fileclient.get_file_client(__opts__) def _generate_tmp_path(): - return os.path.join( - '/tmp', - 'salt.docker.{0}'.format(uuid.uuid4().hex[:6])) + return os.path.join("/tmp", "salt.docker.{0}".format(uuid.uuid4().hex[:6])) -def _prepare_trans_tar(name, sls_opts, mods=None, pillar=None, extra_filerefs=''): - ''' +def _prepare_trans_tar(name, sls_opts, mods=None, pillar=None, extra_filerefs=""): + """ Prepares a self contained tarball that has the state to be applied in the container - ''' + """ chunks = _compile_state(sls_opts, mods) # reuse it from salt.ssh, however this function should # be somewhere else refs = salt.client.ssh.state.lowstate_file_refs(chunks, extra_filerefs) _mk_fileclient() trans_tar = salt.client.ssh.state.prep_trans_tar( - __context__['cp.fileclient'], - chunks, refs, pillar, name) + __context__["cp.fileclient"], chunks, refs, pillar, name + ) return trans_tar def _compile_state(sls_opts, mods=None): - ''' + """ Generates the chunks of lowdata from the list of modules - ''' + """ st_ = HighState(sls_opts) if not mods: return st_.compile_low_chunks() - high_data, errors = st_.render_highstate({sls_opts['saltenv']: mods}) + high_data, errors = st_.render_highstate({sls_opts["saltenv"]: mods}) high_data, ext_errors = st_.state.reconcile_extend(high_data) errors += ext_errors errors += st_.state.verify_high(high_data) @@ -6618,7 +6661,7 @@ def _compile_state(sls_opts, mods=None): def call(name, function, *args, **kwargs): - ''' + """ Executes a Salt function inside a running container .. versionadded:: 2016.11.0 @@ -6639,74 +6682,90 @@ def call(name, function, *args, **kwargs): salt myminion test.arg arg1 arg2 key1=val1 salt myminion dockerng.call compassionate_mirzakhani test.arg arg1 arg2 key1=val1 - ''' + """ # where to put the salt-thin thin_dest_path = _generate_tmp_path() - mkdirp_thin_argv = ['mkdir', '-p', thin_dest_path] + mkdirp_thin_argv = ["mkdir", "-p", thin_dest_path] # make thin_dest_path in the container ret = run_all(name, subprocess.list2cmdline(mkdirp_thin_argv)) - if ret['retcode'] != 0: - return {'result': False, 'comment': ret['stderr']} + if ret["retcode"] != 0: + return {"result": False, "comment": ret["stderr"]} if function is None: - raise CommandExecutionError('Missing function parameter') + raise CommandExecutionError("Missing function parameter") # move salt into the container - thin_path = __utils__['thin.gen_thin']( - __opts__['cachedir'], - extra_mods=__salt__['config.option']("thin_extra_mods", ''), - so_mods=__salt__['config.option']("thin_so_mods", '') + thin_path = __utils__["thin.gen_thin"]( + __opts__["cachedir"], + extra_mods=__salt__["config.option"]("thin_extra_mods", ""), + so_mods=__salt__["config.option"]("thin_so_mods", ""), + ) + ret = copy_to( + name, thin_path, os.path.join(thin_dest_path, os.path.basename(thin_path)) ) - ret = copy_to(name, thin_path, os.path.join(thin_dest_path, os.path.basename(thin_path))) # untar archive - untar_cmd = ["python", "-c", ( - "import tarfile; " - "tarfile.open(\"{0}/{1}\").extractall(path=\"{0}\")" - ).format(thin_dest_path, os.path.basename(thin_path))] + untar_cmd = [ + "python", + "-c", + ("import tarfile; " 'tarfile.open("{0}/{1}").extractall(path="{0}")').format( + thin_dest_path, os.path.basename(thin_path) + ), + ] ret = run_all(name, subprocess.list2cmdline(untar_cmd)) - if ret['retcode'] != 0: - return {'result': False, 'comment': ret['stderr']} + if ret["retcode"] != 0: + return {"result": False, "comment": ret["stderr"]} try: - salt_argv = [ - 'python{0}'.format(sys.version_info[0]), - os.path.join(thin_dest_path, 'salt-call'), - '--metadata', - '--local', - '--log-file', os.path.join(thin_dest_path, 'log'), - '--cachedir', os.path.join(thin_dest_path, 'cache'), - '--out', 'json', - '-l', 'quiet', - '--', - function - ] + list(args) + ['{0}={1}'.format(key, value) for (key, value) in kwargs.items() if not key.startswith('__')] + salt_argv = ( + [ + "python{0}".format(sys.version_info[0]), + os.path.join(thin_dest_path, "salt-call"), + "--metadata", + "--local", + "--log-file", + os.path.join(thin_dest_path, "log"), + "--cachedir", + os.path.join(thin_dest_path, "cache"), + "--out", + "json", + "-l", + "quiet", + "--", + function, + ] + + list(args) + + [ + "{0}={1}".format(key, value) + for (key, value) in kwargs.items() + if not key.startswith("__") + ] + ) ret = run_all(name, subprocess.list2cmdline(map(str, salt_argv))) # python not found - if ret['retcode'] != 0: - raise CommandExecutionError(ret['stderr']) + if ret["retcode"] != 0: + raise CommandExecutionError(ret["stderr"]) # process "real" result in stdout try: - data = __utils__['json.find_json'](ret['stdout']) - local = data.get('local', data) + data = __utils__["json.find_json"](ret["stdout"]) + local = data.get("local", data) if isinstance(local, dict): - if 'retcode' in local: - __context__['retcode'] = local['retcode'] - return local.get('return', data) + if "retcode" in local: + __context__["retcode"] = local["retcode"] + return local.get("return", data) except ValueError: - return {'result': False, - 'comment': 'Can\'t parse container command output'} + return {"result": False, "comment": "Can't parse container command output"} finally: # delete the thin dir so that it does not end in the image - rm_thin_argv = ['rm', '-rf', thin_dest_path] + rm_thin_argv = ["rm", "-rf", thin_dest_path] run_all(name, subprocess.list2cmdline(rm_thin_argv)) def apply_(name, mods=None, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Apply states! This function will call highstate or state.sls based on the @@ -6720,14 +6779,14 @@ def apply_(name, mods=None, **kwargs): salt 'docker' docker.apply web01 salt 'docker' docker.apply web01 test salt 'docker' docker.apply web01 test,pkgs - ''' + """ if mods: return sls(name, mods, **kwargs) return highstate(name, **kwargs) def sls(name, mods=None, **kwargs): - ''' + """ Apply the states defined by the specified SLS modules to the running container @@ -6770,83 +6829,88 @@ def sls(name, mods=None, **kwargs): salt myminion docker.sls compassionate_mirzakhani mods=rails,web - ''' - mods = [item.strip() for item in mods.split(',')] if mods else [] + """ + mods = [item.strip() for item in mods.split(",")] if mods else [] # Figure out the saltenv/pillarenv to use - pillar_override = kwargs.pop('pillar', None) - if 'saltenv' not in kwargs: - kwargs['saltenv'] = 'base' - sls_opts = __utils__['state.get_sls_opts'](__opts__, **kwargs) + pillar_override = kwargs.pop("pillar", None) + if "saltenv" not in kwargs: + kwargs["saltenv"] = "base" + sls_opts = __utils__["state.get_sls_opts"](__opts__, **kwargs) # gather grains from the container - grains = call(name, 'grains.items') + grains = call(name, "grains.items") # compile pillar with container grains pillar = salt.pillar.get_pillar( __opts__, grains, - __opts__['id'], + __opts__["id"], pillar_override=pillar_override, - pillarenv=sls_opts['pillarenv']).compile_pillar() + pillarenv=sls_opts["pillarenv"], + ).compile_pillar() if pillar_override and isinstance(pillar_override, dict): pillar.update(pillar_override) - sls_opts['grains'].update(grains) - sls_opts['pillar'].update(pillar) + sls_opts["grains"].update(grains) + sls_opts["pillar"].update(pillar) trans_tar = _prepare_trans_tar( name, sls_opts, mods=mods, pillar=pillar, - extra_filerefs=kwargs.get('extra_filerefs', '')) + extra_filerefs=kwargs.get("extra_filerefs", ""), + ) # where to put the salt trans tar trans_dest_path = _generate_tmp_path() - mkdirp_trans_argv = ['mkdir', '-p', trans_dest_path] + mkdirp_trans_argv = ["mkdir", "-p", trans_dest_path] # put_archive requires the path to exist ret = run_all(name, subprocess.list2cmdline(mkdirp_trans_argv)) - if ret['retcode'] != 0: - return {'result': False, 'comment': ret['stderr']} + if ret["retcode"] != 0: + return {"result": False, "comment": ret["stderr"]} ret = None try: - trans_tar_sha256 = __utils__['hashutils.get_hash'](trans_tar, 'sha256') - copy_to(name, - trans_tar, - os.path.join(trans_dest_path, 'salt_state.tgz'), - exec_driver=_get_exec_driver(), - overwrite=True) + trans_tar_sha256 = __utils__["hashutils.get_hash"](trans_tar, "sha256") + copy_to( + name, + trans_tar, + os.path.join(trans_dest_path, "salt_state.tgz"), + exec_driver=_get_exec_driver(), + overwrite=True, + ) # Now execute the state into the container - ret = call(name, - 'state.pkg', - os.path.join(trans_dest_path, 'salt_state.tgz'), - trans_tar_sha256, - 'sha256') + ret = call( + name, + "state.pkg", + os.path.join(trans_dest_path, "salt_state.tgz"), + trans_tar_sha256, + "sha256", + ) finally: # delete the trans dir so that it does not end in the image - rm_trans_argv = ['rm', '-rf', trans_dest_path] + rm_trans_argv = ["rm", "-rf", trans_dest_path] run_all(name, subprocess.list2cmdline(rm_trans_argv)) # delete the local version of the trans tar try: os.remove(trans_tar) except (IOError, OSError) as exc: log.error( - 'docker.sls: Unable to remove state tarball \'%s\': %s', - trans_tar, exc + "docker.sls: Unable to remove state tarball '%s': %s", trans_tar, exc ) if not isinstance(ret, dict): - __context__['retcode'] = 1 - elif not __utils__['state.check_result'](ret): - __context__['retcode'] = 2 + __context__["retcode"] = 1 + elif not __utils__["state.check_result"](ret): + __context__["retcode"] = 2 else: - __context__['retcode'] = 0 + __context__["retcode"] = 0 return ret -def highstate(name, saltenv='base', **kwargs): - ''' +def highstate(name, saltenv="base", **kwargs): + """ Apply a highstate to the running container .. versionadded:: 2019.2.0 @@ -6866,17 +6930,14 @@ def highstate(name, saltenv='base', **kwargs): salt myminion docker.highstate compassionate_mirzakhani - ''' - return sls(name, saltenv='base', **kwargs) + """ + return sls(name, saltenv="base", **kwargs) -def sls_build(repository, - tag='latest', - base='opensuse/python', - mods=None, - dryrun=False, - **kwargs): - ''' +def sls_build( + repository, tag="latest", base="opensuse/python", mods=None, dryrun=False, **kwargs +): + """ .. versionchanged:: 2018.3.0 The repository and tag must now be passed separately using the ``repository`` and ``tag`` arguments, rather than together in the (now @@ -6946,28 +7007,26 @@ def sls_build(repository, salt myminion docker.sls_build imgname base=mybase mods=rails,web - ''' - create_kwargs = __utils__['args.clean_kwargs'](**copy.deepcopy(kwargs)) - for key in ('image', 'name', 'cmd', 'interactive', 'tty', 'extra_filerefs'): + """ + create_kwargs = __utils__["args.clean_kwargs"](**copy.deepcopy(kwargs)) + for key in ("image", "name", "cmd", "interactive", "tty", "extra_filerefs"): try: del create_kwargs[key] except KeyError: pass # start a new container - ret = create(image=base, - cmd='sleep infinity', - interactive=True, - tty=True, - **create_kwargs) - id_ = ret['Id'] + ret = create( + image=base, cmd="sleep infinity", interactive=True, tty=True, **create_kwargs + ) + id_ = ret["Id"] try: start_(id_) # Now execute the state into the container ret = sls(id_, mods, **kwargs) # fail if the state was not successful - if not dryrun and not __utils__['state.check_result'](ret): + if not dryrun and not __utils__["state.check_result"](ret): raise CommandExecutionError(ret) if dryrun is False: ret = commit(id_, repository, tag=tag) diff --git a/salt/modules/dpkg_lowpkg.py b/salt/modules/dpkg_lowpkg.py index 4ac8efd2f29..2b0c6fd195c 100644 --- a/salt/modules/dpkg_lowpkg.py +++ b/salt/modules/dpkg_lowpkg.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Support for DEB packages -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import datetime + # Import python libs import logging import os import re -import datetime # Import salt libs import salt.utils.args @@ -21,21 +22,24 @@ from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'lowpkg' +__virtualname__ = "lowpkg" def __virtual__(): - ''' + """ Confirm this module is on a Debian based system - ''' - if __grains__['os_family'] == 'Debian': + """ + if __grains__["os_family"] == "Debian": return __virtualname__ - return (False, 'The dpkg execution module cannot be loaded: ' - 'only works on Debian family systems.') + return ( + False, + "The dpkg execution module cannot be loaded: " + "only works on Debian family systems.", + ) -def bin_pkg_info(path, saltenv='base'): - ''' +def bin_pkg_info(path, saltenv="base"): + """ .. versionadded:: 2015.8.0 Parses RPM metadata and returns a dictionary of information about the @@ -57,61 +61,56 @@ def bin_pkg_info(path, saltenv='base'): salt '*' lowpkg.bin_pkg_info /root/foo-1.2.3-1ubuntu1_all.deb salt '*' lowpkg.bin_pkg_info salt://foo-1.2.3-1ubuntu1_all.deb - ''' + """ # If the path is a valid protocol, pull it down using cp.cache_file - if __salt__['config.valid_fileproto'](path): - newpath = __salt__['cp.cache_file'](path, saltenv) + if __salt__["config.valid_fileproto"](path): + newpath = __salt__["cp.cache_file"](path, saltenv) if not newpath: raise CommandExecutionError( - 'Unable to retrieve {0} from saltenv \'{1}\'' - .format(path, saltenv) + "Unable to retrieve {0} from saltenv '{1}'".format(path, saltenv) ) path = newpath else: if not os.path.exists(path): - raise CommandExecutionError( - '{0} does not exist on minion'.format(path) - ) + raise CommandExecutionError("{0} does not exist on minion".format(path)) elif not os.path.isabs(path): - raise SaltInvocationError( - '{0} does not exist on minion'.format(path) - ) + raise SaltInvocationError("{0} does not exist on minion".format(path)) - cmd = ['dpkg', '-I', path] - result = __salt__['cmd.run_all'](cmd, output_loglevel='trace') - if result['retcode'] != 0: - msg = 'Unable to get info for ' + path - if result['stderr']: - msg += ': ' + result['stderr'] + cmd = ["dpkg", "-I", path] + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace") + if result["retcode"] != 0: + msg = "Unable to get info for " + path + if result["stderr"]: + msg += ": " + result["stderr"] raise CommandExecutionError(msg) ret = {} - for line in result['stdout'].splitlines(): + for line in result["stdout"].splitlines(): line = line.strip() - if line.startswith('Package:'): - ret['name'] = line.split()[-1] - elif line.startswith('Version:'): - ret['version'] = line.split()[-1] - elif line.startswith('Architecture:'): - ret['arch'] = line.split()[-1] + if line.startswith("Package:"): + ret["name"] = line.split()[-1] + elif line.startswith("Version:"): + ret["version"] = line.split()[-1] + elif line.startswith("Architecture:"): + ret["arch"] = line.split()[-1] - missing = [x for x in ('name', 'version', 'arch') if x not in ret] + missing = [x for x in ("name", "version", "arch") if x not in ret] if missing: raise CommandExecutionError( - 'Unable to get {0} for {1}'.format(', '.join(missing), path) + "Unable to get {0} for {1}".format(", ".join(missing), path) ) - if __grains__.get('cpuarch', '') == 'x86_64': - osarch = __grains__.get('osarch', '') - arch = ret['arch'] - if arch != 'all' and osarch == 'amd64' and osarch != arch: - ret['name'] += ':{0}'.format(arch) + if __grains__.get("cpuarch", "") == "x86_64": + osarch = __grains__.get("osarch", "") + arch = ret["arch"] + if arch != "all" and osarch == "amd64" and osarch != arch: + ret["name"] += ":{0}".format(arch) return ret def unpurge(*packages): - ''' + """ Change package selection for each package specified to 'install' CLI Example: @@ -119,24 +118,24 @@ def unpurge(*packages): .. code-block:: bash salt '*' lowpkg.unpurge curl - ''' + """ if not packages: return {} - old = __salt__['pkg.list_pkgs'](purge_desired=True) + old = __salt__["pkg.list_pkgs"](purge_desired=True) ret = {} - __salt__['cmd.run']( - ['dpkg', '--set-selections'], - stdin=r'\n'.join(['{0} install'.format(x) for x in packages]), + __salt__["cmd.run"]( + ["dpkg", "--set-selections"], + stdin=r"\n".join(["{0} install".format(x) for x in packages]), python_shell=False, - output_loglevel='trace' + output_loglevel="trace", ) - __context__.pop('pkg.list_pkgs', None) - new = __salt__['pkg.list_pkgs'](purge_desired=True) + __context__.pop("pkg.list_pkgs", None) + new = __salt__["pkg.list_pkgs"](purge_desired=True) return salt.utils.data.compare_dicts(old, new) def list_pkgs(*packages): - ''' + """ List the packages currently installed in a dict:: {'': ''} @@ -152,25 +151,25 @@ def list_pkgs(*packages): salt '*' lowpkg.list_pkgs salt '*' lowpkg.list_pkgs httpd - ''' + """ pkgs = {} - cmd = 'dpkg -l {0}'.format(' '.join(packages)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out['retcode'] != 0: - msg = 'Error: ' + out['stderr'] + cmd = "dpkg -l {0}".format(" ".join(packages)) + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out["retcode"] != 0: + msg = "Error: " + out["stderr"] log.error(msg) return msg - out = out['stdout'] + out = out["stdout"] for line in out.splitlines(): - if line.startswith('ii '): + if line.startswith("ii "): comps = line.split() pkgs[comps[1]] = comps[2] return pkgs def file_list(*packages): - ''' + """ List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -182,37 +181,36 @@ def file_list(*packages): salt '*' lowpkg.file_list httpd salt '*' lowpkg.file_list httpd postfix salt '*' lowpkg.file_list - ''' + """ errors = [] ret = set([]) pkgs = {} - cmd = 'dpkg -l {0}'.format(' '.join(packages)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out['retcode'] != 0: - msg = 'Error: ' + out['stderr'] + cmd = "dpkg -l {0}".format(" ".join(packages)) + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out["retcode"] != 0: + msg = "Error: " + out["stderr"] log.error(msg) return msg - out = out['stdout'] + out = out["stdout"] for line in out.splitlines(): - if line.startswith('ii '): + if line.startswith("ii "): comps = line.split() - pkgs[comps[1]] = {'version': comps[2], - 'description': ' '.join(comps[3:])} - if 'No packages found' in line: + pkgs[comps[1]] = {"version": comps[2], "description": " ".join(comps[3:])} + if "No packages found" in line: errors.append(line) for pkg in pkgs: files = [] - cmd = 'dpkg -L {0}'.format(pkg) - for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): + cmd = "dpkg -L {0}".format(pkg) + for line in __salt__["cmd.run"](cmd, python_shell=False).splitlines(): files.append(line) fileset = set(files) ret = ret.union(fileset) - return {'errors': errors, 'files': list(ret)} + return {"errors": errors, "files": list(ret)} def file_dict(*packages): - ''' + """ List the files that belong to a package, grouped by package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -224,107 +222,112 @@ def file_dict(*packages): salt '*' lowpkg.file_list httpd salt '*' lowpkg.file_list httpd postfix salt '*' lowpkg.file_list - ''' + """ errors = [] ret = {} pkgs = {} - cmd = 'dpkg -l {0}'.format(' '.join(packages)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out['retcode'] != 0: - msg = 'Error: ' + out['stderr'] + cmd = "dpkg -l {0}".format(" ".join(packages)) + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out["retcode"] != 0: + msg = "Error: " + out["stderr"] log.error(msg) return msg - out = out['stdout'] + out = out["stdout"] for line in out.splitlines(): - if line.startswith('ii '): + if line.startswith("ii "): comps = line.split() - pkgs[comps[1]] = {'version': comps[2], - 'description': ' '.join(comps[3:])} - if 'No packages found' in line: + pkgs[comps[1]] = {"version": comps[2], "description": " ".join(comps[3:])} + if "No packages found" in line: errors.append(line) for pkg in pkgs: files = [] - cmd = 'dpkg -L {0}'.format(pkg) - for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): + cmd = "dpkg -L {0}".format(pkg) + for line in __salt__["cmd.run"](cmd, python_shell=False).splitlines(): files.append(line) ret[pkg] = files - return {'errors': errors, 'packages': ret} + return {"errors": errors, "packages": ret} def _get_pkg_info(*packages, **kwargs): - ''' + """ Return list of package information. If 'packages' parameter is empty, then data about all installed packages will be returned. :param packages: Specified packages. :param failhard: Throw an exception if no packages found. :return: - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - failhard = kwargs.pop('failhard', True) + failhard = kwargs.pop("failhard", True) if kwargs: salt.utils.args.invalid_kwargs(kwargs) - if __grains__['os'] == 'Ubuntu' and __grains__['osrelease_info'] < (12, 4): - bin_var = '${binary}' + if __grains__["os"] == "Ubuntu" and __grains__["osrelease_info"] < (12, 4): + bin_var = "${binary}" else: - bin_var = '${Package}' + bin_var = "${Package}" ret = [] - cmd = "dpkg-query -W -f='package:" + bin_var + "\\n" \ - "revision:${binary:Revision}\\n" \ - "architecture:${Architecture}\\n" \ - "maintainer:${Maintainer}\\n" \ - "summary:${Summary}\\n" \ - "source:${source:Package}\\n" \ - "version:${Version}\\n" \ - "section:${Section}\\n" \ - "installed_size:${Installed-size}\\n" \ - "size:${Size}\\n" \ - "MD5:${MD5sum}\\n" \ - "SHA1:${SHA1}\\n" \ - "SHA256:${SHA256}\\n" \ - "origin:${Origin}\\n" \ - "homepage:${Homepage}\\n" \ - "status:${db:Status-Abbrev}\\n" \ - "======\\n" \ - "description:${Description}\\n" \ - "------\\n'" - cmd += ' {0}'.format(' '.join(packages)) + cmd = ( + "dpkg-query -W -f='package:" + bin_var + "\\n" + "revision:${binary:Revision}\\n" + "architecture:${Architecture}\\n" + "maintainer:${Maintainer}\\n" + "summary:${Summary}\\n" + "source:${source:Package}\\n" + "version:${Version}\\n" + "section:${Section}\\n" + "installed_size:${Installed-size}\\n" + "size:${Size}\\n" + "MD5:${MD5sum}\\n" + "SHA1:${SHA1}\\n" + "SHA256:${SHA256}\\n" + "origin:${Origin}\\n" + "homepage:${Homepage}\\n" + "status:${db:Status-Abbrev}\\n" + "======\\n" + "description:${Description}\\n" + "------\\n'" + ) + cmd += " {0}".format(" ".join(packages)) cmd = cmd.strip() - call = __salt__['cmd.run_all'](cmd, python_chell=False) - if call['retcode']: + call = __salt__["cmd.run_all"](cmd, python_chell=False) + if call["retcode"]: if failhard: - raise CommandExecutionError("Error getting packages information: {0}".format(call['stderr'])) + raise CommandExecutionError( + "Error getting packages information: {0}".format(call["stderr"]) + ) else: return ret - for pkg_info in [elm for elm in re.split(r"------", call['stdout']) if elm.strip()]: + for pkg_info in [elm for elm in re.split(r"------", call["stdout"]) if elm.strip()]: pkg_data = {} pkg_info, pkg_descr = re.split(r"======", pkg_info) - for pkg_info_line in [el.strip() for el in pkg_info.split(os.linesep) if el.strip()]: + for pkg_info_line in [ + el.strip() for el in pkg_info.split(os.linesep) if el.strip() + ]: key, value = pkg_info_line.split(":", 1) if value: pkg_data[key] = value - install_date = _get_pkg_install_time(pkg_data.get('package')) + install_date = _get_pkg_install_time(pkg_data.get("package")) if install_date: - pkg_data['install_date'] = install_date - pkg_data['description'] = pkg_descr.split(":", 1)[-1] + pkg_data["install_date"] = install_date + pkg_data["description"] = pkg_descr.split(":", 1)[-1] ret.append(pkg_data) return ret def _get_pkg_license(pkg): - ''' + """ Try to get a license from the package. Based on https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ :param pkg: :return: - ''' + """ licenses = set() cpr = "/usr/share/doc/{0}/copyright".format(pkg) if os.path.exists(cpr): @@ -337,29 +340,34 @@ def _get_pkg_license(pkg): def _get_pkg_install_time(pkg): - ''' + """ Return package install time, based on the /var/lib/dpkg/info/.list :return: - ''' + """ iso_time = None if pkg is not None: location = "/var/lib/dpkg/info/{0}.list".format(pkg) if os.path.exists(location): - iso_time = datetime.datetime.utcfromtimestamp(int(os.path.getmtime(location))).isoformat() + "Z" + iso_time = ( + datetime.datetime.utcfromtimestamp( + int(os.path.getmtime(location)) + ).isoformat() + + "Z" + ) return iso_time def _get_pkg_ds_avail(): - ''' + """ Get the package information of the available packages, maintained by dselect. Note, this will be not very useful, if dselect isn't installed. :return: - ''' + """ avail = "/var/lib/dpkg/available" - if not salt.utils.path.which('dselect') or not os.path.exists(avail): + if not salt.utils.path.which("dselect") or not os.path.exists(avail): return dict() # Do not update with dselect, just read what is. @@ -383,7 +391,7 @@ def _get_pkg_ds_avail(): def info(*packages, **kwargs): - ''' + """ Returns a detailed summary of package information for provided package names. If no packages are specified, all packages will be returned. @@ -405,32 +413,40 @@ def info(*packages, **kwargs): salt '*' lowpkg.info salt '*' lowpkg.info apache2 bash salt '*' lowpkg.info 'php5*' failhard=false - ''' + """ # Get the missing information from the /var/lib/dpkg/available, if it is there. # However, this file is operated by dselect which has to be installed. dselect_pkg_avail = _get_pkg_ds_avail() kwargs = salt.utils.args.clean_kwargs(**kwargs) - failhard = kwargs.pop('failhard', True) + failhard = kwargs.pop("failhard", True) if kwargs: salt.utils.args.invalid_kwargs(kwargs) ret = dict() for pkg in _get_pkg_info(*packages, failhard=failhard): # Merge extra information from the dselect, if available - for pkg_ext_k, pkg_ext_v in dselect_pkg_avail.get(pkg['package'], {}).items(): + for pkg_ext_k, pkg_ext_v in dselect_pkg_avail.get(pkg["package"], {}).items(): if pkg_ext_k not in pkg: pkg[pkg_ext_k] = pkg_ext_v # Remove "technical" keys - for t_key in ['installed_size', 'depends', 'recommends', - 'provides', 'replaces', 'conflicts', 'bugs', - 'description-md5', 'task']: + for t_key in [ + "installed_size", + "depends", + "recommends", + "provides", + "replaces", + "conflicts", + "bugs", + "description-md5", + "task", + ]: if t_key in pkg: del pkg[t_key] - lic = _get_pkg_license(pkg['package']) + lic = _get_pkg_license(pkg["package"]) if lic: - pkg['license'] = lic - ret[pkg['package']] = pkg + pkg["license"] = lic + ret[pkg["package"]] = pkg return ret diff --git a/salt/modules/drac.py b/salt/modules/drac.py index 3cbf3ad17b5..5d0f87f7c3e 100644 --- a/salt/modules/drac.py +++ b/salt/modules/drac.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Manage Dell DRAC -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -12,31 +13,32 @@ import salt.utils.path # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext.six.moves import range log = logging.getLogger(__name__) def __virtual__(): - if salt.utils.path.which('racadm'): + if salt.utils.path.which("racadm"): return True - return (False, 'The drac execution module cannot be loaded: racadm binary not in path.') + return ( + False, + "The drac execution module cannot be loaded: racadm binary not in path.", + ) def __parse_drac(output): - ''' + """ Parse Dell DRAC output - ''' + """ drac = {} - section = '' + section = "" for i in output.splitlines(): - if len(i.rstrip()) > 0 and '=' in i: + if len(i.rstrip()) > 0 and "=" in i: if section in drac: - drac[section].update(dict( - [[prop.strip() for prop in i.split('=')]] - )) + drac[section].update(dict([[prop.strip() for prop in i.split("=")]])) else: section = i.strip()[:-1] if section not in drac and section: @@ -46,20 +48,20 @@ def __parse_drac(output): def __execute_cmd(command): - ''' + """ Execute rac commands - ''' - cmd = __salt__['cmd.run_all']('racadm {0}'.format(command)) + """ + cmd = __salt__["cmd.run_all"]("racadm {0}".format(command)) - if cmd['retcode'] != 0: - log.warning('racadm return an exit code \'{0}\'.'.format(cmd['retcode'])) + if cmd["retcode"] != 0: + log.warning("racadm return an exit code '{0}'.".format(cmd["retcode"])) return False return True def system_info(): - ''' + """ Return System information CLI Example: @@ -67,17 +69,17 @@ def system_info(): .. code-block:: bash salt dell drac.system_info - ''' - cmd = __salt__['cmd.run_all']('racadm getsysinfo') + """ + cmd = __salt__["cmd.run_all"]("racadm getsysinfo") - if cmd['retcode'] != 0: - log.warning('racadm return an exit code \'{0}\'.'.format(cmd['retcode'])) + if cmd["retcode"] != 0: + log.warning("racadm return an exit code '{0}'.".format(cmd["retcode"])) - return __parse_drac(cmd['stdout']) + return __parse_drac(cmd["stdout"]) def network_info(): - ''' + """ Return Network Configuration CLI Example: @@ -85,18 +87,18 @@ def network_info(): .. code-block:: bash salt dell drac.network_info - ''' + """ - cmd = __salt__['cmd.run_all']('racadm getniccfg') + cmd = __salt__["cmd.run_all"]("racadm getniccfg") - if cmd['retcode'] != 0: - log.warning('racadm return an exit code \'{0}\'.'.format(cmd['retcode'])) + if cmd["retcode"] != 0: + log.warning("racadm return an exit code '{0}'.".format(cmd["retcode"])) - return __parse_drac(cmd['stdout']) + return __parse_drac(cmd["stdout"]) def nameservers(*ns): - ''' + """ Configure the nameservers on the DRAC CLI Example: @@ -105,21 +107,25 @@ def nameservers(*ns): salt dell drac.nameservers [NAMESERVERS] salt dell drac.nameservers ns1.example.com ns2.example.com - ''' + """ if len(ns) > 2: - log.warning('racadm only supports two nameservers') + log.warning("racadm only supports two nameservers") return False for i in range(1, len(ns) + 1): - if not __execute_cmd('config -g cfgLanNetworking -o \ - cfgDNSServer{0} {1}'.format(i, ns[i - 1])): + if not __execute_cmd( + "config -g cfgLanNetworking -o \ + cfgDNSServer{0} {1}".format( + i, ns[i - 1] + ) + ): return False return True def syslog(server, enable=True): - ''' + """ Configure syslog remote logging, by default syslog will automatically be enabled if a server is specified. However, if you want to disable syslog you will need to specify a server followed by False @@ -130,17 +136,23 @@ def syslog(server, enable=True): salt dell drac.syslog [SYSLOG IP] [ENABLE/DISABLE] salt dell drac.syslog 0.0.0.0 False - ''' - if enable and __execute_cmd('config -g cfgRemoteHosts -o \ - cfgRhostsSyslogEnable 1'): - return __execute_cmd('config -g cfgRemoteHosts -o \ - cfgRhostsSyslogServer1 {0}'.format(server)) + """ + if enable and __execute_cmd( + "config -g cfgRemoteHosts -o \ + cfgRhostsSyslogEnable 1" + ): + return __execute_cmd( + "config -g cfgRemoteHosts -o \ + cfgRhostsSyslogServer1 {0}".format( + server + ) + ) - return __execute_cmd('config -g cfgRemoteHosts -o cfgRhostsSyslogEnable 0') + return __execute_cmd("config -g cfgRemoteHosts -o cfgRhostsSyslogEnable 0") def email_alerts(action): - ''' + """ Enable/Disable email alerts CLI Example: @@ -149,18 +161,22 @@ def email_alerts(action): salt dell drac.email_alerts True salt dell drac.email_alerts False - ''' + """ if action: - return __execute_cmd('config -g cfgEmailAlert -o \ - cfgEmailAlertEnable -i 1 1') + return __execute_cmd( + "config -g cfgEmailAlert -o \ + cfgEmailAlertEnable -i 1 1" + ) else: - return __execute_cmd('config -g cfgEmailAlert -o \ - cfgEmailAlertEnable -i 1 0') + return __execute_cmd( + "config -g cfgEmailAlert -o \ + cfgEmailAlertEnable -i 1 0" + ) def list_users(): - ''' + """ List all DRAC users CLI Example: @@ -168,28 +184,32 @@ def list_users(): .. code-block:: bash salt dell drac.list_users - ''' + """ users = {} - _username = '' + _username = "" for idx in range(1, 17): - cmd = __salt__['cmd.run_all']('racadm getconfig -g \ - cfgUserAdmin -i {0}'.format(idx)) + cmd = __salt__["cmd.run_all"]( + "racadm getconfig -g \ + cfgUserAdmin -i {0}".format( + idx + ) + ) - if cmd['retcode'] != 0: - log.warning('racadm return an exit code \'{0}\'.'.format(cmd['retcode'])) + if cmd["retcode"] != 0: + log.warning("racadm return an exit code '{0}'.".format(cmd["retcode"])) - for user in cmd['stdout'].splitlines(): - if not user.startswith('cfg'): + for user in cmd["stdout"].splitlines(): + if not user.startswith("cfg"): continue - (key, val) = user.split('=') + (key, val) = user.split("=") - if key.startswith('cfgUserAdminUserName'): + if key.startswith("cfgUserAdminUserName"): _username = val.strip() if val: - users[_username] = {'index': idx} + users[_username] = {"index": idx} else: break else: @@ -199,7 +219,7 @@ def list_users(): def delete_user(username, uid=None): - ''' + """ Delete a user CLI Example: @@ -208,24 +228,28 @@ def delete_user(username, uid=None): salt dell drac.delete_user [USERNAME] [UID - optional] salt dell drac.delete_user diana 4 - ''' + """ if uid is None: user = list_users() - uid = user[username]['index'] + uid = user[username]["index"] if uid: - return __execute_cmd('config -g cfgUserAdmin -o \ - cfgUserAdminUserName -i {0} ""'.format(uid)) + return __execute_cmd( + 'config -g cfgUserAdmin -o \ + cfgUserAdminUserName -i {0} ""'.format( + uid + ) + ) else: - log.warning('\'{0}\' does not exist'.format(username)) + log.warning("'{0}' does not exist".format(username)) return False return True def change_password(username, password, uid=None): - ''' + """ Change users password CLI Example: @@ -234,23 +258,27 @@ def change_password(username, password, uid=None): salt dell drac.change_password [USERNAME] [PASSWORD] [UID - optional] salt dell drac.change_password diana secret - ''' + """ if uid is None: user = list_users() - uid = user[username]['index'] + uid = user[username]["index"] if uid: - return __execute_cmd('config -g cfgUserAdmin -o \ - cfgUserAdminPassword -i {0} {1}'.format(uid, password)) + return __execute_cmd( + "config -g cfgUserAdmin -o \ + cfgUserAdminPassword -i {0} {1}".format( + uid, password + ) + ) else: - log.warning('\'{0}\' does not exist'.format(username)) + log.warning("'{0}' does not exist".format(username)) return False return True def create_user(username, password, permissions, users=None): - ''' + """ Create user accounts CLI Example: @@ -270,42 +298,50 @@ def create_user(username, password, permissions, users=None): * virtual_media : Access Virtual Media * test_alerts : Test Alerts * debug_commands : Execute Debug Commands - ''' + """ _uids = set() if users is None: users = list_users() if username in users: - log.warning('\'{0}\' already exists'.format(username)) + log.warning("'{0}' already exists".format(username)) return False for idx in six.iterkeys(users): - _uids.add(users[idx]['index']) + _uids.add(users[idx]["index"]) uid = sorted(list(set(range(2, 12)) - _uids), reverse=True).pop() # Create user accountvfirst - if not __execute_cmd('config -g cfgUserAdmin -o \ - cfgUserAdminUserName -i {0} {1}'.format(uid, username)): + if not __execute_cmd( + "config -g cfgUserAdmin -o \ + cfgUserAdminUserName -i {0} {1}".format( + uid, username + ) + ): delete_user(username, uid) return False # Configure users permissions if not set_permissions(username, permissions, uid): - log.warning('unable to set user permissions') + log.warning("unable to set user permissions") delete_user(username, uid) return False # Configure users password if not change_password(username, password, uid): - log.warning('unable to set user password') + log.warning("unable to set user password") delete_user(username, uid) return False # Enable users admin - if not __execute_cmd('config -g cfgUserAdmin -o \ - cfgUserAdminEnable -i {0} 1'.format(uid)): + if not __execute_cmd( + "config -g cfgUserAdmin -o \ + cfgUserAdminEnable -i {0} 1".format( + uid + ) + ): delete_user(username, uid) return False @@ -313,7 +349,7 @@ def create_user(username, password, permissions, users=None): def set_permissions(username, permissions, uid=None): - ''' + """ Configure users permissions CLI Example: @@ -333,37 +369,43 @@ def set_permissions(username, permissions, uid=None): * virtual_media : Access Virtual Media * test_alerts : Test Alerts * debug_commands : Execute Debug Commands - ''' - privileges = {'login': '0x0000001', - 'drac': '0x0000002', - 'user_management': '0x0000004', - 'clear_logs': '0x0000008', - 'server_control_commands': '0x0000010', - 'console_redirection': '0x0000020', - 'virtual_media': '0x0000040', - 'test_alerts': '0x0000080', - 'debug_commands': '0x0000100'} + """ + privileges = { + "login": "0x0000001", + "drac": "0x0000002", + "user_management": "0x0000004", + "clear_logs": "0x0000008", + "server_control_commands": "0x0000010", + "console_redirection": "0x0000020", + "virtual_media": "0x0000040", + "test_alerts": "0x0000080", + "debug_commands": "0x0000100", + } permission = 0 # When users don't provide a user ID we need to search for this if uid is None: user = list_users() - uid = user[username]['index'] + uid = user[username]["index"] # Generate privilege bit mask - for i in permissions.split(','): + for i in permissions.split(","): perm = i.strip() if perm in privileges: permission += int(privileges[perm], 16) - return __execute_cmd('config -g cfgUserAdmin -o \ - cfgUserAdminPrivilege -i {0} 0x{1:08X}'.format(uid, permission)) + return __execute_cmd( + "config -g cfgUserAdmin -o \ + cfgUserAdminPrivilege -i {0} 0x{1:08X}".format( + uid, permission + ) + ) def set_snmp(community): - ''' + """ Configure SNMP community string CLI Example: @@ -372,13 +414,17 @@ def set_snmp(community): salt dell drac.set_snmp [COMMUNITY] salt dell drac.set_snmp public - ''' - return __execute_cmd('config -g cfgOobSnmp -o \ - cfgOobSnmpAgentCommunity {0}'.format(community)) + """ + return __execute_cmd( + "config -g cfgOobSnmp -o \ + cfgOobSnmpAgentCommunity {0}".format( + community + ) + ) def set_network(ip, netmask, gateway): - ''' + """ Configure Network CLI Example: @@ -387,14 +433,12 @@ def set_network(ip, netmask, gateway): salt dell drac.set_network [DRAC IP] [NETMASK] [GATEWAY] salt dell drac.set_network 192.168.0.2 255.255.255.0 192.168.0.1 - ''' - return __execute_cmd('setniccfg -s {0} {1} {2}'.format( - ip, netmask, gateway - )) + """ + return __execute_cmd("setniccfg -s {0} {1} {2}".format(ip, netmask, gateway)) def server_reboot(): - ''' + """ Issues a power-cycle operation on the managed server. This action is similar to pressing the power button on the system's front panel to power down and then power up the system. @@ -404,12 +448,12 @@ def server_reboot(): .. code-block:: bash salt dell drac.server_reboot - ''' - return __execute_cmd('serveraction powercycle') + """ + return __execute_cmd("serveraction powercycle") def server_poweroff(): - ''' + """ Powers down the managed server. CLI Example: @@ -417,12 +461,12 @@ def server_poweroff(): .. code-block:: bash salt dell drac.server_poweroff - ''' - return __execute_cmd('serveraction powerdown') + """ + return __execute_cmd("serveraction powerdown") def server_poweron(): - ''' + """ Powers up the managed server. CLI Example: @@ -430,12 +474,12 @@ def server_poweron(): .. code-block:: bash salt dell drac.server_poweron - ''' - return __execute_cmd('serveraction powerup') + """ + return __execute_cmd("serveraction powerup") def server_hardreset(): - ''' + """ Performs a reset (reboot) operation on the managed server. CLI Example: @@ -443,12 +487,12 @@ def server_hardreset(): .. code-block:: bash salt dell drac.server_hardreset - ''' - return __execute_cmd('serveraction hardreset') + """ + return __execute_cmd("serveraction hardreset") def server_pxe(): - ''' + """ Configure server to PXE perform a one off PXE boot CLI Example: @@ -456,14 +500,16 @@ def server_pxe(): .. code-block:: bash salt dell drac.server_pxe - ''' - if __execute_cmd('config -g cfgServerInfo -o \ - cfgServerFirstBootDevice PXE'): - if __execute_cmd('config -g cfgServerInfo -o cfgServerBootOnce 1'): + """ + if __execute_cmd( + "config -g cfgServerInfo -o \ + cfgServerFirstBootDevice PXE" + ): + if __execute_cmd("config -g cfgServerInfo -o cfgServerBootOnce 1"): return server_reboot else: - log.warning('failed to set boot order') + log.warning("failed to set boot order") return False - log.warning('failed to configure PXE boot') + log.warning("failed to configure PXE boot") return False diff --git a/salt/modules/dracr.py b/salt/modules/dracr.py index d5b278d2012..af6db9f0acd 100644 --- a/salt/modules/dracr.py +++ b/salt/modules/dracr.py @@ -1,61 +1,62 @@ # -*- coding: utf-8 -*- -''' +""" Manage Dell DRAC. .. versionadded:: 2015.8.2 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import re +import salt.utils.path + # Import Salt libs from salt.exceptions import CommandExecutionError -import salt.utils.path # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext.six.moves import map +from salt.ext.six.moves import map, range log = logging.getLogger(__name__) -__proxyenabled__ = ['fx2'] +__proxyenabled__ = ["fx2"] try: - run_all = __salt__['cmd.run_all'] + run_all = __salt__["cmd.run_all"] except (NameError, KeyError): import salt.modules.cmdmod - __salt__ = { - 'cmd.run_all': salt.modules.cmdmod.run_all - } + + __salt__ = {"cmd.run_all": salt.modules.cmdmod.run_all} def __virtual__(): - if salt.utils.path.which('racadm'): + if salt.utils.path.which("racadm"): return True - return (False, 'The drac execution module cannot be loaded: racadm binary not in path.') + return ( + False, + "The drac execution module cannot be loaded: racadm binary not in path.", + ) def __parse_drac(output): - ''' + """ Parse Dell DRAC output - ''' + """ drac = {} - section = '' + section = "" for i in output.splitlines(): - if i.strip().endswith(':') and '=' not in i: + if i.strip().endswith(":") and "=" not in i: section = i[0:-1] drac[section] = {} - if len(i.rstrip()) > 0 and '=' in i: + if len(i.rstrip()) > 0 and "=" in i: if section in drac: - drac[section].update(dict( - [[prop.strip() for prop in i.split('=')]] - )) + drac[section].update(dict([[prop.strip() for prop in i.split("=")]])) else: section = i.strip() if section not in drac and section: @@ -64,119 +65,113 @@ def __parse_drac(output): return drac -def __execute_cmd(command, host=None, - admin_username=None, admin_password=None, - module=None): - ''' +def __execute_cmd( + command, host=None, admin_username=None, admin_password=None, module=None +): + """ Execute rac commands - ''' + """ if module: # -a takes 'server' or 'switch' to represent all servers # or all switches in a chassis. Allow # user to say 'module=ALL_SERVER' or 'module=ALL_SWITCH' - if module.startswith('ALL_'): - modswitch = '-a '\ - + module[module.index('_') + 1:len(module)].lower() + if module.startswith("ALL_"): + modswitch = "-a " + module[module.index("_") + 1 : len(module)].lower() else: - modswitch = '-m {0}'.format(module) + modswitch = "-m {0}".format(module) else: - modswitch = '' + modswitch = "" if not host: # This is a local call - cmd = __salt__['cmd.run_all']('racadm {0} {1}'.format(command, - modswitch)) + cmd = __salt__["cmd.run_all"]("racadm {0} {1}".format(command, modswitch)) else: - cmd = __salt__['cmd.run_all']( - 'racadm -r {0} -u {1} -p {2} {3} {4}'.format(host, - admin_username, - admin_password, - command, - modswitch), - output_loglevel='quiet') + cmd = __salt__["cmd.run_all"]( + "racadm -r {0} -u {1} -p {2} {3} {4}".format( + host, admin_username, admin_password, command, modswitch + ), + output_loglevel="quiet", + ) - if cmd['retcode'] != 0: - log.warning('racadm returned an exit code of %s', cmd['retcode']) + if cmd["retcode"] != 0: + log.warning("racadm returned an exit code of %s", cmd["retcode"]) return False return True -def __execute_ret(command, host=None, - admin_username=None, admin_password=None, - module=None): - ''' +def __execute_ret( + command, host=None, admin_username=None, admin_password=None, module=None +): + """ Execute rac commands - ''' + """ if module: - if module == 'ALL': - modswitch = '-a ' + if module == "ALL": + modswitch = "-a " else: - modswitch = '-m {0}'.format(module) + modswitch = "-m {0}".format(module) else: - modswitch = '' + modswitch = "" if not host: # This is a local call - cmd = __salt__['cmd.run_all']('racadm {0} {1}'.format(command, - modswitch)) + cmd = __salt__["cmd.run_all"]("racadm {0} {1}".format(command, modswitch)) else: - cmd = __salt__['cmd.run_all']( - 'racadm -r {0} -u {1} -p {2} {3} {4}'.format(host, - admin_username, - admin_password, - command, - modswitch), - output_loglevel='quiet') + cmd = __salt__["cmd.run_all"]( + "racadm -r {0} -u {1} -p {2} {3} {4}".format( + host, admin_username, admin_password, command, modswitch + ), + output_loglevel="quiet", + ) - if cmd['retcode'] != 0: - log.warning('racadm returned an exit code of %s', cmd['retcode']) + if cmd["retcode"] != 0: + log.warning("racadm returned an exit code of %s", cmd["retcode"]) else: fmtlines = [] - for l in cmd['stdout'].splitlines(): - if l.startswith('Security Alert'): + for l in cmd["stdout"].splitlines(): + if l.startswith("Security Alert"): continue - if l.startswith('RAC1168:'): + if l.startswith("RAC1168:"): break - if l.startswith('RAC1169:'): + if l.startswith("RAC1169:"): break - if l.startswith('Continuing execution'): + if l.startswith("Continuing execution"): continue if len(l.strip()) == 0: continue fmtlines.append(l) - if '=' in l: + if "=" in l: continue - cmd['stdout'] = '\n'.join(fmtlines) + cmd["stdout"] = "\n".join(fmtlines) return cmd -def get_dns_dracname(host=None, - admin_username=None, admin_password=None): +def get_dns_dracname(host=None, admin_username=None, admin_password=None): - ret = __execute_ret('get iDRAC.NIC.DNSRacName', host=host, - admin_username=admin_username, - admin_password=admin_password) - parsed = __parse_drac(ret['stdout']) + ret = __execute_ret( + "get iDRAC.NIC.DNSRacName", + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) + parsed = __parse_drac(ret["stdout"]) return parsed -def set_dns_dracname(name, - host=None, - admin_username=None, - admin_password=None): +def set_dns_dracname(name, host=None, admin_username=None, admin_password=None): - ret = __execute_ret('set iDRAC.NIC.DNSRacName {0}'.format(name), - host=host, - admin_username=admin_username, - admin_password=admin_password) + ret = __execute_ret( + "set iDRAC.NIC.DNSRacName {0}".format(name), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) return ret -def system_info(host=None, - admin_username=None, admin_password=None, - module=None): - ''' +def system_info(host=None, admin_username=None, admin_password=None, module=None): + """ Return System information CLI Example: @@ -184,62 +179,71 @@ def system_info(host=None, .. code-block:: bash salt dell dracr.system_info - ''' - cmd = __execute_ret('getsysinfo', host=host, - admin_username=admin_username, - admin_password=admin_password, - module=module) + """ + cmd = __execute_ret( + "getsysinfo", + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) - if cmd['retcode'] != 0: - log.warning('racadm returned an exit code of %s', cmd['retcode']) + if cmd["retcode"] != 0: + log.warning("racadm returned an exit code of %s", cmd["retcode"]) return cmd - return __parse_drac(cmd['stdout']) + return __parse_drac(cmd["stdout"]) -def set_niccfg(ip=None, netmask=None, gateway=None, dhcp=False, - host=None, - admin_username=None, - admin_password=None, - module=None): +def set_niccfg( + ip=None, + netmask=None, + gateway=None, + dhcp=False, + host=None, + admin_username=None, + admin_password=None, + module=None, +): - cmdstr = 'setniccfg ' + cmdstr = "setniccfg " if dhcp: - cmdstr += '-d ' + cmdstr += "-d " else: - cmdstr += '-s ' + ip + ' ' + netmask + ' ' + gateway + cmdstr += "-s " + ip + " " + netmask + " " + gateway - return __execute_cmd(cmdstr, host=host, - admin_username=admin_username, - admin_password=admin_password, - module=module) + return __execute_cmd( + cmdstr, + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) -def set_nicvlan(vlan=None, - host=None, - admin_username=None, - admin_password=None, - module=None): +def set_nicvlan( + vlan=None, host=None, admin_username=None, admin_password=None, module=None +): - cmdstr = 'setniccfg -v ' + cmdstr = "setniccfg -v " if vlan: cmdstr += vlan - ret = __execute_cmd(cmdstr, host=host, - admin_username=admin_username, - admin_password=admin_password, - module=module) + ret = __execute_cmd( + cmdstr, + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) return ret -def network_info(host=None, - admin_username=None, - admin_password=None, - module=None): - ''' +def network_info(host=None, admin_username=None, admin_password=None, module=None): + """ Return Network Configuration CLI Example: @@ -247,41 +251,40 @@ def network_info(host=None, .. code-block:: bash salt dell dracr.network_info - ''' + """ - inv = inventory(host=host, admin_username=admin_username, - admin_password=admin_password) + inv = inventory( + host=host, admin_username=admin_username, admin_password=admin_password + ) if inv is None: cmd = {} - cmd['retcode'] = -1 - cmd['stdout'] = 'Problem getting switch inventory' + cmd["retcode"] = -1 + cmd["stdout"] = "Problem getting switch inventory" return cmd - if module not in inv.get('switch') and module not in inv.get('server'): + if module not in inv.get("switch") and module not in inv.get("server"): cmd = {} - cmd['retcode'] = -1 - cmd['stdout'] = 'No module {0} found.'.format(module) + cmd["retcode"] = -1 + cmd["stdout"] = "No module {0} found.".format(module) return cmd - cmd = __execute_ret('getniccfg', host=host, - admin_username=admin_username, - admin_password=admin_password, - module=module) + cmd = __execute_ret( + "getniccfg", + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) - if cmd['retcode'] != 0: - log.warning('racadm returned an exit code of %s', cmd['retcode']) + if cmd["retcode"] != 0: + log.warning("racadm returned an exit code of %s", cmd["retcode"]) - cmd['stdout'] = 'Network:\n' + 'Device = ' + module + '\n' + \ - cmd['stdout'] - return __parse_drac(cmd['stdout']) + cmd["stdout"] = "Network:\n" + "Device = " + module + "\n" + cmd["stdout"] + return __parse_drac(cmd["stdout"]) -def nameservers(ns, - host=None, - admin_username=None, - admin_password=None, - module=None): - ''' +def nameservers(ns, host=None, admin_username=None, admin_password=None, module=None): + """ Configure the nameservers on the DRAC CLI Example: @@ -292,26 +295,33 @@ def nameservers(ns, salt dell dracr.nameservers ns1.example.com ns2.example.com admin_username=root admin_password=calvin module=server-1 host=192.168.1.1 - ''' + """ if len(ns) > 2: - log.warning('racadm only supports two nameservers') + log.warning("racadm only supports two nameservers") return False for i in range(1, len(ns) + 1): - if not __execute_cmd('config -g cfgLanNetworking -o ' - 'cfgDNSServer{0} {1}'.format(i, ns[i - 1]), - host=host, - admin_username=admin_username, - admin_password=admin_password, - module=module): + if not __execute_cmd( + "config -g cfgLanNetworking -o " "cfgDNSServer{0} {1}".format(i, ns[i - 1]), + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ): return False return True -def syslog(server, enable=True, host=None, - admin_username=None, admin_password=None, module=None): - ''' +def syslog( + server, + enable=True, + host=None, + admin_username=None, + admin_password=None, + module=None, +): + """ Configure syslog remote logging, by default syslog will automatically be enabled if a server is specified. However, if you want to disable syslog you will need to specify a server followed by False @@ -322,32 +332,33 @@ def syslog(server, enable=True, host=None, salt dell dracr.syslog [SYSLOG IP] [ENABLE/DISABLE] salt dell dracr.syslog 0.0.0.0 False - ''' - if enable and __execute_cmd('config -g cfgRemoteHosts -o ' - 'cfgRhostsSyslogEnable 1', - host=host, - admin_username=admin_username, - admin_password=admin_password, - module=None): - return __execute_cmd('config -g cfgRemoteHosts -o ' - 'cfgRhostsSyslogServer1 {0}'.format(server), - host=host, - admin_username=admin_username, - admin_password=admin_password, - module=module) + """ + if enable and __execute_cmd( + "config -g cfgRemoteHosts -o " "cfgRhostsSyslogEnable 1", + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=None, + ): + return __execute_cmd( + "config -g cfgRemoteHosts -o " "cfgRhostsSyslogServer1 {0}".format(server), + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) - return __execute_cmd('config -g cfgRemoteHosts -o cfgRhostsSyslogEnable 0', - host=host, - admin_username=admin_username, - admin_password=admin_password, - module=module) + return __execute_cmd( + "config -g cfgRemoteHosts -o cfgRhostsSyslogEnable 0", + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) -def email_alerts(action, - host=None, - admin_username=None, - admin_password=None): - ''' +def email_alerts(action, host=None, admin_username=None, admin_password=None): + """ Enable/Disable email alerts CLI Example: @@ -356,23 +367,21 @@ def email_alerts(action, salt dell dracr.email_alerts True salt dell dracr.email_alerts False - ''' + """ if action: - return __execute_cmd('config -g cfgEmailAlert -o ' - 'cfgEmailAlertEnable -i 1 1', host=host, - admin_username=admin_username, - admin_password=admin_password) + return __execute_cmd( + "config -g cfgEmailAlert -o " "cfgEmailAlertEnable -i 1 1", + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) else: - return __execute_cmd('config -g cfgEmailAlert -o ' - 'cfgEmailAlertEnable -i 1 0') + return __execute_cmd("config -g cfgEmailAlert -o " "cfgEmailAlertEnable -i 1 0") -def list_users(host=None, - admin_username=None, - admin_password=None, - module=None): - ''' +def list_users(host=None, admin_username=None, admin_password=None, module=None): + """ List all DRAC users CLI Example: @@ -380,30 +389,32 @@ def list_users(host=None, .. code-block:: bash salt dell dracr.list_users - ''' + """ users = {} - _username = '' + _username = "" for idx in range(1, 17): - cmd = __execute_ret('getconfig -g ' - 'cfgUserAdmin -i {0}'.format(idx), - host=host, admin_username=admin_username, - admin_password=admin_password) + cmd = __execute_ret( + "getconfig -g " "cfgUserAdmin -i {0}".format(idx), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) - if cmd['retcode'] != 0: - log.warning('racadm returned an exit code of %s', cmd['retcode']) + if cmd["retcode"] != 0: + log.warning("racadm returned an exit code of %s", cmd["retcode"]) - for user in cmd['stdout'].splitlines(): - if not user.startswith('cfg'): + for user in cmd["stdout"].splitlines(): + if not user.startswith("cfg"): continue - (key, val) = user.split('=') + (key, val) = user.split("=") - if key.startswith('cfgUserAdminUserName'): + if key.startswith("cfgUserAdminUserName"): _username = val.strip() if val: - users[_username] = {'index': idx} + users[_username] = {"index": idx} else: break else: @@ -413,12 +424,10 @@ def list_users(host=None, return users -def delete_user(username, - uid=None, - host=None, - admin_username=None, - admin_password=None): - ''' +def delete_user( + username, uid=None, host=None, admin_username=None, admin_password=None +): + """ Delete a user CLI Example: @@ -427,26 +436,34 @@ def delete_user(username, salt dell dracr.delete_user [USERNAME] [UID - optional] salt dell dracr.delete_user diana 4 - ''' + """ if uid is None: user = list_users() - uid = user[username]['index'] + uid = user[username]["index"] if uid: - return __execute_cmd('config -g cfgUserAdmin -o ' - 'cfgUserAdminUserName -i {0} ""'.format(uid), - host=host, admin_username=admin_username, - admin_password=admin_password) + return __execute_cmd( + "config -g cfgUserAdmin -o " 'cfgUserAdminUserName -i {0} ""'.format(uid), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) else: - log.warning('User \'%s\' does not exist', username) + log.warning("User '%s' does not exist", username) return False -def change_password(username, password, uid=None, host=None, - admin_username=None, admin_password=None, - module=None): - ''' +def change_password( + username, + password, + uid=None, + host=None, + admin_username=None, + admin_password=None, + module=None, +): + """ Change user's password CLI Example: @@ -464,29 +481,37 @@ def change_password(username, password, uid=None, host=None, Many late-model Dell chassis have 'root' as UID 1, so if you can depend on that then setting the password is much quicker. Raises an error if the supplied password is greater than 20 chars. - ''' + """ if len(password) > 20: - raise CommandExecutionError('Supplied password should be 20 characters or less') + raise CommandExecutionError("Supplied password should be 20 characters or less") if uid is None: - user = list_users(host=host, admin_username=admin_username, - admin_password=admin_password, module=module) - uid = user[username]['index'] + user = list_users( + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) + uid = user[username]["index"] if uid: - return __execute_cmd('config -g cfgUserAdmin -o ' - 'cfgUserAdminPassword -i {0} {1}' - .format(uid, password), - host=host, admin_username=admin_username, - admin_password=admin_password, module=module) + return __execute_cmd( + "config -g cfgUserAdmin -o " + "cfgUserAdminPassword -i {0} {1}".format(uid, password), + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) else: - log.warning('racadm: user \'%s\' does not exist', username) + log.warning("racadm: user '%s' does not exist", username) return False -def deploy_password(username, password, host=None, admin_username=None, - admin_password=None, module=None): - ''' +def deploy_password( + username, password, host=None, admin_username=None, admin_password=None, module=None +): + """ Change the QuickDeploy password, used for switches as well CLI Example: @@ -503,16 +528,18 @@ def deploy_password(username, password, host=None, admin_username=None, be necessary if one is not sure which user slot contains the one you want. Many late-model Dell chassis have 'root' as UID 1, so if you can depend on that then setting the password is much quicker. - ''' - return __execute_cmd('deploy -u {0} -p {1}'.format( - username, password), host=host, admin_username=admin_username, - admin_password=admin_password, module=module + """ + return __execute_cmd( + "deploy -u {0} -p {1}".format(username, password), + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, ) -def deploy_snmp(snmp, host=None, admin_username=None, - admin_password=None, module=None): - ''' +def deploy_snmp(snmp, host=None, admin_username=None, admin_password=None, module=None): + """ Change the QuickDeploy SNMP community string, used for switches as well CLI Example: @@ -524,18 +551,26 @@ def deploy_snmp(snmp, host=None, admin_username=None, admin_password= salt dell dracr.deploy_password diana secret - ''' - return __execute_cmd('deploy -v SNMPv2 {0} ro'.format(snmp), - host=host, - admin_username=admin_username, - admin_password=admin_password, - module=module) + """ + return __execute_cmd( + "deploy -v SNMPv2 {0} ro".format(snmp), + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) -def create_user(username, password, permissions, - users=None, host=None, - admin_username=None, admin_password=None): - ''' +def create_user( + username, + password, + permissions, + users=None, + host=None, + admin_username=None, + admin_password=None, +): + """ Create user accounts CLI Example: @@ -555,55 +590,58 @@ def create_user(username, password, permissions, * virtual_media : Access Virtual Media * test_alerts : Test Alerts * debug_commands : Execute Debug Commands - ''' + """ _uids = set() if users is None: users = list_users() if username in users: - log.warning('racadm: user \'%s\' already exists', username) + log.warning("racadm: user '%s' already exists", username) return False for idx in six.iterkeys(users): - _uids.add(users[idx]['index']) + _uids.add(users[idx]["index"]) uid = sorted(list(set(range(2, 12)) - _uids), reverse=True).pop() # Create user account first - if not __execute_cmd('config -g cfgUserAdmin -o ' - 'cfgUserAdminUserName -i {0} {1}' - .format(uid, username), - host=host, admin_username=admin_username, - admin_password=admin_password): + if not __execute_cmd( + "config -g cfgUserAdmin -o " + "cfgUserAdminUserName -i {0} {1}".format(uid, username), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ): delete_user(username, uid) return False # Configure users permissions if not set_permissions(username, permissions, uid): - log.warning('unable to set user permissions') + log.warning("unable to set user permissions") delete_user(username, uid) return False # Configure users password if not change_password(username, password, uid): - log.warning('unable to set user password') + log.warning("unable to set user password") delete_user(username, uid) return False # Enable users admin - if not __execute_cmd('config -g cfgUserAdmin -o ' - 'cfgUserAdminEnable -i {0} 1'.format(uid)): + if not __execute_cmd( + "config -g cfgUserAdmin -o " "cfgUserAdminEnable -i {0} 1".format(uid) + ): delete_user(username, uid) return False return True -def set_permissions(username, permissions, - uid=None, host=None, - admin_username=None, admin_password=None): - ''' +def set_permissions( + username, permissions, uid=None, host=None, admin_username=None, admin_password=None +): + """ Configure users permissions CLI Example: @@ -624,41 +662,44 @@ def set_permissions(username, permissions, * virtual_media : Access Virtual Media * test_alerts : Test Alerts * debug_commands : Execute Debug Commands - ''' - privileges = {'login': '0x0000001', - 'drac': '0x0000002', - 'user_management': '0x0000004', - 'clear_logs': '0x0000008', - 'server_control_commands': '0x0000010', - 'console_redirection': '0x0000020', - 'virtual_media': '0x0000040', - 'test_alerts': '0x0000080', - 'debug_commands': '0x0000100'} + """ + privileges = { + "login": "0x0000001", + "drac": "0x0000002", + "user_management": "0x0000004", + "clear_logs": "0x0000008", + "server_control_commands": "0x0000010", + "console_redirection": "0x0000020", + "virtual_media": "0x0000040", + "test_alerts": "0x0000080", + "debug_commands": "0x0000100", + } permission = 0 # When users don't provide a user ID we need to search for this if uid is None: user = list_users() - uid = user[username]['index'] + uid = user[username]["index"] # Generate privilege bit mask - for i in permissions.split(','): + for i in permissions.split(","): perm = i.strip() if perm in privileges: permission += int(privileges[perm], 16) - return __execute_cmd('config -g cfgUserAdmin -o ' - 'cfgUserAdminPrivilege -i {0} 0x{1:08X}' - .format(uid, permission), - host=host, admin_username=admin_username, - admin_password=admin_password) + return __execute_cmd( + "config -g cfgUserAdmin -o " + "cfgUserAdminPrivilege -i {0} 0x{1:08X}".format(uid, permission), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) -def set_snmp(community, host=None, - admin_username=None, admin_password=None): - ''' +def set_snmp(community, host=None, admin_username=None, admin_password=None): + """ Configure CMC or individual iDRAC SNMP community string. Use ``deploy_snmp`` for configuring chassis switch SNMP. @@ -668,16 +709,19 @@ def set_snmp(community, host=None, salt dell dracr.set_snmp [COMMUNITY] salt dell dracr.set_snmp public - ''' - return __execute_cmd('config -g cfgOobSnmp -o ' - 'cfgOobSnmpAgentCommunity {0}'.format(community), - host=host, admin_username=admin_username, - admin_password=admin_password) + """ + return __execute_cmd( + "config -g cfgOobSnmp -o " "cfgOobSnmpAgentCommunity {0}".format(community), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) -def set_network(ip, netmask, gateway, host=None, - admin_username=None, admin_password=None): - ''' +def set_network( + ip, netmask, gateway, host=None, admin_username=None, admin_password=None +): + """ Configure Network on the CMC or individual iDRAC. Use ``set_niccfg`` for blade and switch addresses. @@ -688,18 +732,23 @@ def set_network(ip, netmask, gateway, host=None, salt dell dracr.set_network [DRAC IP] [NETMASK] [GATEWAY] salt dell dracr.set_network 192.168.0.2 255.255.255.0 192.168.0.1 admin_username=root admin_password=calvin host=192.168.1.1 - ''' - return __execute_cmd('setniccfg -s {0} {1} {2}'.format( - ip, netmask, gateway, host=host, admin_username=admin_username, - admin_password=admin_password - )) + """ + return __execute_cmd( + "setniccfg -s {0} {1} {2}".format( + ip, + netmask, + gateway, + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) + ) -def server_power(status, host=None, - admin_username=None, - admin_password=None, - module=None): - ''' +def server_power( + status, host=None, admin_username=None, admin_password=None, module=None +): + """ status One of 'powerup', 'powerdown', 'powercycle', 'hardreset', 'graceshutdown' @@ -724,17 +773,18 @@ def server_power(status, host=None, salt dell dracr.server_reboot salt dell dracr.server_reboot module=server-1 - ''' - return __execute_cmd('serveraction {0}'.format(status), - host=host, admin_username=admin_username, - admin_password=admin_password, module=module) + """ + return __execute_cmd( + "serveraction {0}".format(status), + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) -def server_reboot(host=None, - admin_username=None, - admin_password=None, - module=None): - ''' +def server_reboot(host=None, admin_username=None, admin_password=None, module=None): + """ Issues a power-cycle operation on the managed server. This action is similar to pressing the power button on the system's front panel to power down and then power up the system. @@ -759,17 +809,18 @@ def server_reboot(host=None, salt dell dracr.server_reboot salt dell dracr.server_reboot module=server-1 - ''' - return __execute_cmd('serveraction powercycle', - host=host, admin_username=admin_username, - admin_password=admin_password, module=module) + """ + return __execute_cmd( + "serveraction powercycle", + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) -def server_poweroff(host=None, - admin_username=None, - admin_password=None, - module=None): - ''' +def server_poweroff(host=None, admin_username=None, admin_password=None, module=None): + """ Powers down the managed server. host @@ -791,17 +842,18 @@ def server_poweroff(host=None, salt dell dracr.server_poweroff salt dell dracr.server_poweroff module=server-1 - ''' - return __execute_cmd('serveraction powerdown', - host=host, admin_username=admin_username, - admin_password=admin_password, module=module) + """ + return __execute_cmd( + "serveraction powerdown", + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) -def server_poweron(host=None, - admin_username=None, - admin_password=None, - module=None): - ''' +def server_poweron(host=None, admin_username=None, admin_password=None, module=None): + """ Powers up the managed server. host @@ -823,17 +875,18 @@ def server_poweron(host=None, salt dell dracr.server_poweron salt dell dracr.server_poweron module=server-1 - ''' - return __execute_cmd('serveraction powerup', - host=host, admin_username=admin_username, - admin_password=admin_password, module=module) + """ + return __execute_cmd( + "serveraction powerup", + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) -def server_hardreset(host=None, - admin_username=None, - admin_password=None, - module=None): - ''' +def server_hardreset(host=None, admin_username=None, admin_password=None, module=None): + """ Performs a reset (reboot) operation on the managed server. host @@ -855,19 +908,20 @@ def server_hardreset(host=None, salt dell dracr.server_hardreset salt dell dracr.server_hardreset module=server-1 - ''' - return __execute_cmd('serveraction hardreset', - host=host, - admin_username=admin_username, - admin_password=admin_password, - module=module) + """ + return __execute_cmd( + "serveraction hardreset", + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) -def server_powerstatus(host=None, - admin_username=None, - admin_password=None, - module=None): - ''' +def server_powerstatus( + host=None, admin_username=None, admin_password=None, module=None +): + """ return the power status for the passed module CLI Example: @@ -875,30 +929,31 @@ def server_powerstatus(host=None, .. code-block:: bash salt dell drac.server_powerstatus - ''' - ret = __execute_ret('serveraction powerstatus', - host=host, admin_username=admin_username, - admin_password=admin_password, - module=module) + """ + ret = __execute_ret( + "serveraction powerstatus", + host=host, + admin_username=admin_username, + admin_password=admin_password, + module=module, + ) - result = {'retcode': 0} - if ret['stdout'] == 'ON': - result['status'] = True - result['comment'] = 'Power is on' - if ret['stdout'] == 'OFF': - result['status'] = False - result['comment'] = 'Power is on' - if ret['stdout'].startswith('ERROR'): - result['status'] = False - result['comment'] = ret['stdout'] + result = {"retcode": 0} + if ret["stdout"] == "ON": + result["status"] = True + result["comment"] = "Power is on" + if ret["stdout"] == "OFF": + result["status"] = False + result["comment"] = "Power is on" + if ret["stdout"].startswith("ERROR"): + result["status"] = False + result["comment"] = ret["stdout"] return result -def server_pxe(host=None, - admin_username=None, - admin_password=None): - ''' +def server_pxe(host=None, admin_username=None, admin_password=None): + """ Configure server to PXE perform a one off PXE boot CLI Example: @@ -906,26 +961,30 @@ def server_pxe(host=None, .. code-block:: bash salt dell dracr.server_pxe - ''' - if __execute_cmd('config -g cfgServerInfo -o cfgServerFirstBootDevice PXE', - host=host, admin_username=admin_username, - admin_password=admin_password): - if __execute_cmd('config -g cfgServerInfo -o cfgServerBootOnce 1', - host=host, admin_username=admin_username, - admin_password=admin_password): + """ + if __execute_cmd( + "config -g cfgServerInfo -o cfgServerFirstBootDevice PXE", + host=host, + admin_username=admin_username, + admin_password=admin_password, + ): + if __execute_cmd( + "config -g cfgServerInfo -o cfgServerBootOnce 1", + host=host, + admin_username=admin_username, + admin_password=admin_password, + ): return server_reboot else: - log.warning('failed to set boot order') + log.warning("failed to set boot order") return False - log.warning('failed to configure PXE boot') + log.warning("failed to configure PXE boot") return False -def list_slotnames(host=None, - admin_username=None, - admin_password=None): - ''' +def list_slotnames(host=None, admin_username=None, admin_password=None): + """ List the names of all slots in the chassis. host @@ -944,38 +1003,41 @@ def list_slotnames(host=None, salt-call --local dracr.list_slotnames host=111.222.333.444 admin_username=root admin_password=secret - ''' - slotraw = __execute_ret('getslotname', - host=host, admin_username=admin_username, - admin_password=admin_password) + """ + slotraw = __execute_ret( + "getslotname", + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) - if slotraw['retcode'] != 0: + if slotraw["retcode"] != 0: return slotraw slots = {} stripheader = True - for l in slotraw['stdout'].splitlines(): - if l.startswith('<'): + for l in slotraw["stdout"].splitlines(): + if l.startswith("<"): stripheader = False continue if stripheader: continue fields = l.split() slots[fields[0]] = {} - slots[fields[0]]['slot'] = fields[0] + slots[fields[0]]["slot"] = fields[0] if len(fields) > 1: - slots[fields[0]]['slotname'] = fields[1] + slots[fields[0]]["slotname"] = fields[1] else: - slots[fields[0]]['slotname'] = '' + slots[fields[0]]["slotname"] = "" if len(fields) > 2: - slots[fields[0]]['hostname'] = fields[2] + slots[fields[0]]["hostname"] = fields[2] else: - slots[fields[0]]['hostname'] = '' + slots[fields[0]]["hostname"] = "" return slots def get_slotname(slot, host=None, admin_username=None, admin_password=None): - ''' + """ Get the name of a slot number in the chassis. slot @@ -997,18 +1059,18 @@ def get_slotname(slot, host=None, admin_username=None, admin_password=None): salt-call --local dracr.get_slotname 0 host=111.222.333.444 admin_username=root admin_password=secret - ''' - slots = list_slotnames(host=host, admin_username=admin_username, - admin_password=admin_password) + """ + slots = list_slotnames( + host=host, admin_username=admin_username, admin_password=admin_password + ) # The keys for this dictionary are strings, not integers, so convert the # argument to a string slot = six.text_type(slot) - return slots[slot]['slotname'] + return slots[slot]["slotname"] -def set_slotname(slot, name, host=None, - admin_username=None, admin_password=None): - ''' +def set_slotname(slot, name, host=None, admin_username=None, admin_password=None): + """ Set the name of a slot in a chassis. slot @@ -1033,17 +1095,17 @@ def set_slotname(slot, name, host=None, salt '*' dracr.set_slotname 2 my-slotname host=111.222.333.444 admin_username=root admin_password=secret - ''' - return __execute_cmd('config -g cfgServerInfo -o cfgServerName -i {0} {1}'.format(slot, name), - host=host, admin_username=admin_username, - admin_password=admin_password) + """ + return __execute_cmd( + "config -g cfgServerInfo -o cfgServerName -i {0} {1}".format(slot, name), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) -def set_chassis_name(name, - host=None, - admin_username=None, - admin_password=None): - ''' +def set_chassis_name(name, host=None, admin_username=None, admin_password=None): + """ Set the name of the chassis. name @@ -1065,14 +1127,17 @@ def set_chassis_name(name, salt '*' dracr.set_chassis_name my-chassis host=111.222.333.444 admin_username=root admin_password=secret - ''' - return __execute_cmd('setsysinfo -c chassisname {0}'.format(name), - host=host, admin_username=admin_username, - admin_password=admin_password) + """ + return __execute_cmd( + "setsysinfo -c chassisname {0}".format(name), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) def get_chassis_name(host=None, admin_username=None, admin_password=None): - ''' + """ Get the name of a chassis. host @@ -1091,10 +1156,13 @@ def get_chassis_name(host=None, admin_username=None, admin_password=None): salt '*' dracr.get_chassis_name host=111.222.333.444 admin_username=root admin_password=secret - ''' - return bare_rac_cmd('getchassisname', host=host, - admin_username=admin_username, - admin_password=admin_password) + """ + return bare_rac_cmd( + "getchassisname", + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) def inventory(host=None, admin_username=None, admin_password=None): @@ -1102,17 +1170,19 @@ def inventory(host=None, admin_username=None, admin_password=None): return {x: y} fields = {} - fields['server'] = ['name', 'idrac_version', 'blade_type', 'gen', - 'updateable'] - fields['switch'] = ['name', 'model_name', 'hw_version', 'fw_version'] - fields['cmc'] = ['name', 'cmc_version', 'updateable'] - fields['chassis'] = ['name', 'fw_version', 'fqdd'] + fields["server"] = ["name", "idrac_version", "blade_type", "gen", "updateable"] + fields["switch"] = ["name", "model_name", "hw_version", "fw_version"] + fields["cmc"] = ["name", "cmc_version", "updateable"] + fields["chassis"] = ["name", "fw_version", "fqdd"] - rawinv = __execute_ret('getversion', host=host, - admin_username=admin_username, - admin_password=admin_password) + rawinv = __execute_ret( + "getversion", + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) - if rawinv['retcode'] != 0: + if rawinv["retcode"] != 0: return rawinv in_server = False @@ -1120,33 +1190,33 @@ def inventory(host=None, admin_username=None, admin_password=None): in_cmc = False in_chassis = False ret = {} - ret['server'] = {} - ret['switch'] = {} - ret['cmc'] = {} - ret['chassis'] = {} - for l in rawinv['stdout'].splitlines(): - if l.startswith(''): + ret["server"] = {} + ret["switch"] = {} + ret["cmc"] = {} + ret["chassis"] = {} + for l in rawinv["stdout"].splitlines(): + if l.startswith(""): in_server = True in_switch = False in_cmc = False in_chassis = False continue - if l.startswith(''): + if l.startswith(""): in_server = False in_switch = True in_cmc = False in_chassis = False continue - if l.startswith(''): + if l.startswith(""): in_server = False in_switch = False in_cmc = True in_chassis = False continue - if l.startswith(''): + if l.startswith(""): in_server = False in_switch = False in_cmc = False @@ -1156,33 +1226,36 @@ def inventory(host=None, admin_username=None, admin_password=None): if len(l) < 1: continue - line = re.split(' +', l.strip()) + line = re.split(" +", l.strip()) if in_server: - ret['server'][line[0]] = dict( - (k, v) for d in map(mapit, fields['server'], line) for (k, v) - in d.items()) + ret["server"][line[0]] = dict( + (k, v) + for d in map(mapit, fields["server"], line) + for (k, v) in d.items() + ) if in_switch: - ret['switch'][line[0]] = dict( - (k, v) for d in map(mapit, fields['switch'], line) for (k, v) - in d.items()) + ret["switch"][line[0]] = dict( + (k, v) + for d in map(mapit, fields["switch"], line) + for (k, v) in d.items() + ) if in_cmc: - ret['cmc'][line[0]] = dict( - (k, v) for d in map(mapit, fields['cmc'], line) for (k, v) in - d.items()) + ret["cmc"][line[0]] = dict( + (k, v) for d in map(mapit, fields["cmc"], line) for (k, v) in d.items() + ) if in_chassis: - ret['chassis'][line[0]] = dict( - (k, v) for d in map(mapit, fields['chassis'], line) for k, v in - d.items()) + ret["chassis"][line[0]] = dict( + (k, v) + for d in map(mapit, fields["chassis"], line) + for k, v in d.items() + ) return ret -def set_chassis_location(location, - host=None, - admin_username=None, - admin_password=None): - ''' +def set_chassis_location(location, host=None, admin_username=None, admin_password=None): + """ Set the location of the chassis. location @@ -1204,16 +1277,17 @@ def set_chassis_location(location, salt '*' dracr.set_chassis_location location-name host=111.222.333.444 admin_username=root admin_password=secret - ''' - return __execute_cmd('setsysinfo -c chassislocation {0}'.format(location), - host=host, admin_username=admin_username, - admin_password=admin_password) + """ + return __execute_cmd( + "setsysinfo -c chassislocation {0}".format(location), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) -def get_chassis_location(host=None, - admin_username=None, - admin_password=None): - ''' +def get_chassis_location(host=None, admin_username=None, admin_password=None): + """ Get the location of the chassis. host @@ -1232,17 +1306,16 @@ def get_chassis_location(host=None, salt '*' dracr.set_chassis_location host=111.222.333.444 admin_username=root admin_password=secret - ''' - return system_info(host=host, - admin_username=admin_username, - admin_password=admin_password)['Chassis Information']['Chassis Location'] + """ + return system_info( + host=host, admin_username=admin_username, admin_password=admin_password + )["Chassis Information"]["Chassis Location"] -def set_chassis_datacenter(location, - host=None, - admin_username=None, - admin_password=None): - ''' +def set_chassis_datacenter( + location, host=None, admin_username=None, admin_password=None +): + """ Set the location of the chassis. location @@ -1264,16 +1337,19 @@ def set_chassis_datacenter(location, salt '*' dracr.set_chassis_datacenter datacenter-name host=111.222.333.444 admin_username=root admin_password=secret - ''' - return set_general('cfgLocation', 'cfgLocationDatacenter', location, - host=host, admin_username=admin_username, - admin_password=admin_password) + """ + return set_general( + "cfgLocation", + "cfgLocationDatacenter", + location, + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) -def get_chassis_datacenter(host=None, - admin_username=None, - admin_password=None): - ''' +def get_chassis_datacenter(host=None, admin_username=None, admin_password=None): + """ Get the datacenter of the chassis. host @@ -1292,37 +1368,50 @@ def get_chassis_datacenter(host=None, salt '*' dracr.set_chassis_location host=111.222.333.444 admin_username=root admin_password=secret - ''' - return get_general('cfgLocation', 'cfgLocationDatacenter', host=host, - admin_username=admin_username, admin_password=admin_password) + """ + return get_general( + "cfgLocation", + "cfgLocationDatacenter", + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) -def set_general(cfg_sec, cfg_var, val, host=None, - admin_username=None, admin_password=None): - return __execute_cmd('config -g {0} -o {1} {2}'.format(cfg_sec, - cfg_var, val), - host=host, - admin_username=admin_username, - admin_password=admin_password) +def set_general( + cfg_sec, cfg_var, val, host=None, admin_username=None, admin_password=None +): + return __execute_cmd( + "config -g {0} -o {1} {2}".format(cfg_sec, cfg_var, val), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) -def get_general(cfg_sec, cfg_var, host=None, - admin_username=None, admin_password=None): - ret = __execute_ret('getconfig -g {0} -o {1}'.format(cfg_sec, cfg_var), - host=host, - admin_username=admin_username, - admin_password=admin_password) +def get_general(cfg_sec, cfg_var, host=None, admin_username=None, admin_password=None): + ret = __execute_ret( + "getconfig -g {0} -o {1}".format(cfg_sec, cfg_var), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) - if ret['retcode'] == 0: - return ret['stdout'] + if ret["retcode"] == 0: + return ret["stdout"] else: return ret -def idrac_general(blade_name, command, idrac_password=None, - host=None, - admin_username=None, admin_password=None): - ''' +def idrac_general( + blade_name, + command, + idrac_password=None, + host=None, + admin_username=None, + admin_password=None, +): + """ Run a generic racadm command against a particular blade in a chassis. Blades are usually named things like 'server-1', 'server-2', etc. If the iDRAC has a different @@ -1343,67 +1432,60 @@ def idrac_general(blade_name, command, idrac_password=None, salt fx2 chassis.cmd idrac_general server-1 'get BIOS.SysProfileSettings' - ''' + """ - module_network = network_info(host, admin_username, - admin_password, blade_name) + module_network = network_info(host, admin_username, admin_password, blade_name) if idrac_password is not None: password = idrac_password else: password = admin_password - idrac_ip = module_network['Network']['IP Address'] + idrac_ip = module_network["Network"]["IP Address"] - ret = __execute_ret(command, host=idrac_ip, - admin_username='root', - admin_password=password) + ret = __execute_ret( + command, host=idrac_ip, admin_username="root", admin_password=password + ) - if ret['retcode'] == 0: - return ret['stdout'] + if ret["retcode"] == 0: + return ret["stdout"] else: return ret -def _update_firmware(cmd, - host=None, - admin_username=None, - admin_password=None): +def _update_firmware(cmd, host=None, admin_username=None, admin_password=None): if not admin_username: - admin_username = __pillar__['proxy']['admin_username'] + admin_username = __pillar__["proxy"]["admin_username"] if not admin_username: - admin_password = __pillar__['proxy']['admin_password'] + admin_password = __pillar__["proxy"]["admin_password"] - ret = __execute_ret(cmd, - host=host, - admin_username=admin_username, - admin_password=admin_password) + ret = __execute_ret( + cmd, host=host, admin_username=admin_username, admin_password=admin_password + ) - if ret['retcode'] == 0: - return ret['stdout'] + if ret["retcode"] == 0: + return ret["stdout"] else: return ret -def bare_rac_cmd(cmd, host=None, - admin_username=None, admin_password=None): - ret = __execute_ret('{0}'.format(cmd), - host=host, - admin_username=admin_username, - admin_password=admin_password) +def bare_rac_cmd(cmd, host=None, admin_username=None, admin_password=None): + ret = __execute_ret( + "{0}".format(cmd), + host=host, + admin_username=admin_username, + admin_password=admin_password, + ) - if ret['retcode'] == 0: - return ret['stdout'] + if ret["retcode"] == 0: + return ret["stdout"] else: return ret -def update_firmware(filename, - host=None, - admin_username=None, - admin_password=None): - ''' +def update_firmware(filename, host=None, admin_username=None, admin_password=None): + """ Updates firmware using local firmware file .. code-block:: bash @@ -1417,22 +1499,22 @@ def update_firmware(filename, racadm update –f firmware.exe -u user –p pass - ''' + """ if os.path.exists(filename): - return _update_firmware('update -f {0}'.format(filename), - host=None, - admin_username=None, - admin_password=None) + return _update_firmware( + "update -f {0}".format(filename), + host=None, + admin_username=None, + admin_password=None, + ) else: - raise CommandExecutionError('Unable to find firmware file {0}' - .format(filename)) + raise CommandExecutionError("Unable to find firmware file {0}".format(filename)) -def update_firmware_nfs_or_cifs(filename, share, - host=None, - admin_username=None, - admin_password=None): - ''' +def update_firmware_nfs_or_cifs( + filename, share, host=None, admin_username=None, admin_password=None +): + """ Executes the following for CIFS (using username and password stored in the pillar data) @@ -1462,14 +1544,16 @@ def update_firmware_nfs_or_cifs(filename, share, salt dell dracr.update_firmware_nfs_or_cifs \ firmware.exe IP-address:/share - ''' + """ if os.path.exists(filename): - return _update_firmware('update -f {0} -l {1}'.format(filename, share), - host=None, - admin_username=None, - admin_password=None) + return _update_firmware( + "update -f {0} -l {1}".format(filename, share), + host=None, + admin_username=None, + admin_password=None, + ) else: - raise CommandExecutionError('Unable to find firmware file {0}' - .format(filename)) + raise CommandExecutionError("Unable to find firmware file {0}".format(filename)) + # def get_idrac_nic() diff --git a/salt/modules/drbd.py b/salt/modules/drbd.py index 24fcfc73677..4dc733976f8 100644 --- a/salt/modules/drbd.py +++ b/salt/modules/drbd.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" DRBD administration module -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging @@ -21,7 +21,7 @@ def _analyse_overview_field(content): def overview(): - ''' + """ Show status of the DRBD devices, support two nodes only. CLI Example: @@ -29,13 +29,13 @@ def overview(): .. code-block:: bash salt '*' drbd.overview - ''' - cmd = 'drbd-overview' - for line in __salt__['cmd.run'](cmd).splitlines(): + """ + cmd = "drbd-overview" + for line in __salt__["cmd.run"](cmd).splitlines(): ret = {} fields = line.strip().split() - minnum = fields[0].split(':')[0] - device = fields[0].split(':')[1] + minnum = fields[0].split(":")[0] + device = fields[0].split(":")[1] connstate, _ = _analyse_overview_field(fields[1]) localrole, partnerrole = _analyse_overview_field(fields[2]) localdiskstate, partnerdiskstate = _analyse_overview_field(fields[3]) @@ -49,44 +49,44 @@ def overview(): remainsize = fields[8] perc = fields[9] ret = { - 'minor number': minnum, - 'device': device, - 'connection state': connstate, - 'local role': localrole, - 'partner role': partnerrole, - 'local disk state': localdiskstate, - 'partner disk state': partnerdiskstate, - 'mountpoint': mountpoint, - 'fs': fs_mounted, - 'total size': totalsize, - 'used': usedsize, - 'remains': remainsize, - 'percent': perc, + "minor number": minnum, + "device": device, + "connection state": connstate, + "local role": localrole, + "partner role": partnerrole, + "local disk state": localdiskstate, + "partner disk state": partnerdiskstate, + "mountpoint": mountpoint, + "fs": fs_mounted, + "total size": totalsize, + "used": usedsize, + "remains": remainsize, + "percent": perc, } else: ret = { - 'minor number': minnum, - 'device': device, - 'connection state': connstate, - 'local role': localrole, - 'partner role': partnerrole, - 'local disk state': localdiskstate, - 'partner disk state': partnerdiskstate, + "minor number": minnum, + "device": device, + "connection state": connstate, + "local role": localrole, + "partner role": partnerrole, + "local disk state": localdiskstate, + "partner disk state": partnerdiskstate, } else: syncbar = fields[4] synced = fields[6] syncedbytes = fields[7] - sync = synced+syncedbytes + sync = synced + syncedbytes ret = { - 'minor number': minnum, - 'device': device, - 'connection state': connstate, - 'local role': localrole, - 'partner role': partnerrole, - 'local disk state': localdiskstate, - 'partner disk state': partnerdiskstate, - 'synchronisation: ': syncbar, - 'synched': sync, + "minor number": minnum, + "device": device, + "connection state": connstate, + "local role": localrole, + "partner role": partnerrole, + "local disk state": localdiskstate, + "partner disk state": partnerdiskstate, + "synchronisation: ": syncbar, + "synched": sync, } return ret diff --git a/salt/modules/dummyproxy_pkg.py b/salt/modules/dummyproxy_pkg.py index 1813bbd9ccb..6fc90be14c6 100644 --- a/salt/modules/dummyproxy_pkg.py +++ b/salt/modules/dummyproxy_pkg.py @@ -1,59 +1,57 @@ # -*- coding: utf-8 -*- -''' +""" Package support for the dummy proxy used by the test suite -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging + import salt.utils.data import salt.utils.platform from salt.ext import six - log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Only work on systems that are a proxy minion - ''' + """ try: - if salt.utils.platform.is_proxy() \ - and __opts__['proxy']['proxytype'] == 'dummy': + if salt.utils.platform.is_proxy() and __opts__["proxy"]["proxytype"] == "dummy": return __virtualname__ except KeyError: return ( False, - 'The dummyproxy_package execution module failed to load. Check ' - 'the proxy key in pillar or /etc/salt/proxy.' + "The dummyproxy_package execution module failed to load. Check " + "the proxy key in pillar or /etc/salt/proxy.", ) return ( False, - 'The dummyproxy_package execution module failed to load: only works ' - 'on a dummy proxy minion.' + "The dummyproxy_package execution module failed to load: only works " + "on a dummy proxy minion.", ) def list_pkgs(versions_as_list=False, **kwargs): - return __proxy__['dummy.package_list']() + return __proxy__["dummy.package_list"]() -def install(name=None, refresh=False, fromrepo=None, - pkgs=None, sources=None, **kwargs): - return __proxy__['dummy.package_install'](name, **kwargs) +def install(name=None, refresh=False, fromrepo=None, pkgs=None, sources=None, **kwargs): + return __proxy__["dummy.package_install"](name, **kwargs) def remove(name=None, pkgs=None, **kwargs): - return __proxy__['dummy.package_remove'](name) + return __proxy__["dummy.package_remove"](name) def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -64,40 +62,43 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' + """ if len(names) == 1: - vers = __proxy__['dummy.package_status'](names[0]) + vers = __proxy__["dummy.package_status"](names[0]) return vers[names[0]] else: results = {} for n in names: - vers = __proxy__['dummy.package_status'](n) + vers = __proxy__["dummy.package_status"](n) results.update(vers) return results -def upgrade(name=None, pkgs=None, refresh=True, skip_verify=True, - normalize=True, **kwargs): - old = __proxy__['dummy.package_list']() - new = __proxy__['dummy.uptodate']() - pkg_installed = __proxy__['dummy.upgrade']() +def upgrade( + name=None, pkgs=None, refresh=True, skip_verify=True, normalize=True, **kwargs +): + old = __proxy__["dummy.package_list"]() + new = __proxy__["dummy.uptodate"]() + pkg_installed = __proxy__["dummy.upgrade"]() ret = salt.utils.data.compare_dicts(old, pkg_installed) return ret -def installed(name, - version=None, - refresh=False, - fromrepo=None, - skip_verify=False, - pkgs=None, - sources=None, - **kwargs): +def installed( + name, + version=None, + refresh=False, + fromrepo=None, + skip_verify=False, + pkgs=None, + sources=None, + **kwargs +): - p = __proxy__['dummy.package_status'](name) + p = __proxy__["dummy.package_status"](name) if version is None: - if 'ret' in p: - return six.text_type(p['ret']) + if "ret" in p: + return six.text_type(p["ret"]) else: return True else: diff --git a/salt/modules/dummyproxy_service.py b/salt/modules/dummyproxy_service.py index 30794be81c8..26ff95cd0a5 100644 --- a/salt/modules/dummyproxy_service.py +++ b/salt/modules/dummyproxy_service.py @@ -1,10 +1,10 @@ - # -*- coding: utf-8 -*- -''' +""" Provide the service module for the dummy proxy used in integration tests -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -12,39 +12,36 @@ import salt.utils.platform log = logging.getLogger(__name__) -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" def __virtual__(): - ''' + """ Only work on systems that are a proxy minion - ''' + """ try: - if salt.utils.platform.is_proxy() \ - and __opts__['proxy']['proxytype'] == 'dummy': + if salt.utils.platform.is_proxy() and __opts__["proxy"]["proxytype"] == "dummy": return __virtualname__ except KeyError: return ( False, - 'The dummyproxy_service execution module failed to load. Check ' - 'the proxy key in pillar or /etc/salt/proxy.' + "The dummyproxy_service execution module failed to load. Check " + "the proxy key in pillar or /etc/salt/proxy.", ) return ( False, - 'The dummyproxy_service execution module failed to load: only works ' - 'on the integration testsuite dummy proxy minion.' + "The dummyproxy_service execution module failed to load: only works " + "on the integration testsuite dummy proxy minion.", ) def get_all(): - ''' + """ Return a list of all available services .. versionadded:: 2016.11.3 @@ -54,13 +51,13 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' - proxy_fn = 'dummy.service_list' + """ + proxy_fn = "dummy.service_list" return __proxy__[proxy_fn]() def list_(): - ''' + """ Return a list of all available services. .. versionadded:: 2016.11.3 @@ -70,12 +67,12 @@ def list_(): .. code-block:: bash salt '*' service.list - ''' + """ return get_all() def start(name, sig=None): - ''' + """ Start the specified service on the dummy .. versionadded:: 2016.11.3 @@ -85,14 +82,14 @@ def start(name, sig=None): .. code-block:: bash salt '*' service.start - ''' + """ - proxy_fn = 'dummy.service_start' + proxy_fn = "dummy.service_start" return __proxy__[proxy_fn](name) def stop(name, sig=None): - ''' + """ Stop the specified service on the dummy .. versionadded:: 2016.11.3 @@ -102,13 +99,13 @@ def stop(name, sig=None): .. code-block:: bash salt '*' service.stop - ''' - proxy_fn = 'dummy.service_stop' + """ + proxy_fn = "dummy.service_stop" return __proxy__[proxy_fn](name) def restart(name, sig=None): - ''' + """ Restart the specified service with dummy. .. versionadded:: 2016.11.3 @@ -118,14 +115,14 @@ def restart(name, sig=None): .. code-block:: bash salt '*' service.restart - ''' + """ - proxy_fn = 'dummy.service_restart' + proxy_fn = "dummy.service_restart" return __proxy__[proxy_fn](name) def status(name, sig=None): - ''' + """ Return the status for a service via dummy, returns a bool whether the service is running. @@ -136,31 +133,31 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status - ''' + """ - proxy_fn = 'dummy.service_status' + proxy_fn = "dummy.service_status" resp = __proxy__[proxy_fn](name) - if resp['comment'] == 'stopped': + if resp["comment"] == "stopped": return False - if resp['comment'] == 'running': + if resp["comment"] == "running": return True def running(name, sig=None): - ''' + """ Return whether this service is running. .. versionadded:: 2016.11.3 - ''' + """ return status(name).get(name, False) def enabled(name, sig=None): - ''' + """ Only the 'redbull' service is 'enabled' in the test .. versionadded:: 2016.11.3 - ''' - return name == 'redbull' + """ + return name == "redbull" diff --git a/salt/modules/ebuildpkg.py b/salt/modules/ebuildpkg.py index cb77ff7852f..7ebb5ae6685 100644 --- a/salt/modules/ebuildpkg.py +++ b/salt/modules/ebuildpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for Portage .. important:: @@ -12,15 +12,16 @@ Support for Portage For now all package names *MUST* include the package category, i.e. ``'vim'`` will not work, ``'app-editors/vim'`` will. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import copy +import datetime +import logging + # Import python libs import os -import copy -import logging import re -import datetime # Import salt libs import salt.utils.args @@ -34,20 +35,22 @@ import salt.utils.versions from salt.exceptions import CommandExecutionError, MinionError from salt.ext import six - # Import third party libs HAS_PORTAGE = False try: import portage + HAS_PORTAGE = True except ImportError: import os import sys - if os.path.isdir('/usr/lib/portage/pym'): + + if os.path.isdir("/usr/lib/portage/pym"): try: # In a virtualenv, the portage python path needs to be manually added - sys.path.insert(0, '/usr/lib/portage/pym') + sys.path.insert(0, "/usr/lib/portage/pym") import portage + HAS_PORTAGE = True except ImportError: pass @@ -55,28 +58,33 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Confirm this module is on a Gentoo based system - ''' - if HAS_PORTAGE and __grains__['os'] == 'Gentoo': + """ + if HAS_PORTAGE and __grains__["os"] == "Gentoo": return __virtualname__ - return (False, 'The ebuild execution module cannot be loaded: either the system is not Gentoo or the portage python library is not available.') + return ( + False, + "The ebuild execution module cannot be loaded: either the system is not Gentoo or the portage python library is not available.", + ) def _vartree(): import portage # pylint: disable=3rd-party-module-not-gated + portage = salt.utils.compat.reload(portage) - return portage.db[portage.root]['vartree'] + return portage.db[portage.root]["vartree"] def _porttree(): import portage # pylint: disable=3rd-party-module-not-gated + portage = salt.utils.compat.reload(portage) - return portage.db[portage.root]['porttree'] + return portage.db[portage.root]["porttree"] def _p_to_cp(p): @@ -88,7 +96,7 @@ def _p_to_cp(p): pass try: - ret = _porttree().dbapi.xmatch('bestmatch-visible', p) + ret = _porttree().dbapi.xmatch("bestmatch-visible", p) if ret: return portage.dep_getkey(ret) except portage.exception.InvalidAtom: @@ -105,11 +113,11 @@ def _p_to_cp(p): def _allnodes(): - if 'portage._allnodes' in __context__: - return __context__['portage._allnodes'] + if "portage._allnodes" in __context__: + return __context__["portage._allnodes"] else: ret = _porttree().getallnodes() - __context__['portage._allnodes'] = ret + __context__["portage._allnodes"] = ret return ret @@ -136,42 +144,41 @@ def _cpv_to_version(cpv): def _process_emerge_err(stdout, stderr): - ''' + """ Used to parse emerge output to provide meaningful output when emerge fails - ''' + """ ret = {} - rexp = re.compile(r'^[<>=][^ ]+/[^ ]+ [^\n]+', re.M) + rexp = re.compile(r"^[<>=][^ ]+/[^ ]+ [^\n]+", re.M) - slot_conflicts = re.compile(r'^[^ \n]+/[^ ]+:[^ ]', re.M).findall(stderr) + slot_conflicts = re.compile(r"^[^ \n]+/[^ ]+:[^ ]", re.M).findall(stderr) if slot_conflicts: - ret['slot conflicts'] = slot_conflicts + ret["slot conflicts"] = slot_conflicts - blocked = re.compile(r'(?m)^\[blocks .+\] ' - r'([^ ]+/[^ ]+-[0-9]+[^ ]+)' - r'.*$').findall(stdout) + blocked = re.compile( + r"(?m)^\[blocks .+\] " r"([^ ]+/[^ ]+-[0-9]+[^ ]+)" r".*$" + ).findall(stdout) - unsatisfied = re.compile( - r'Error: The above package list contains').findall(stderr) + unsatisfied = re.compile(r"Error: The above package list contains").findall(stderr) # If there were blocks and emerge could not resolve it. if blocked and unsatisfied: - ret['blocked'] = blocked + ret["blocked"] = blocked - sections = re.split('\n\n', stderr) + sections = re.split("\n\n", stderr) for section in sections: - if 'The following keyword changes' in section: - ret['keywords'] = rexp.findall(section) - elif 'The following license changes' in section: - ret['license'] = rexp.findall(section) - elif 'The following USE changes' in section: - ret['use'] = rexp.findall(section) - elif 'The following mask changes' in section: - ret['mask'] = rexp.findall(section) + if "The following keyword changes" in section: + ret["keywords"] = rexp.findall(section) + elif "The following license changes" in section: + ret["license"] = rexp.findall(section) + elif "The following USE changes" in section: + ret["use"] = rexp.findall(section) + elif "The following mask changes" in section: + ret["mask"] = rexp.findall(section) return ret def check_db(*names, **kwargs): - ''' + """ .. versionadded:: 0.17.0 Returns a dict containing the following information for each specified @@ -190,27 +197,28 @@ def check_db(*names, **kwargs): .. code-block:: bash salt '*' pkg.check_db - ''' + """ ### NOTE: kwargs is not used here but needs to be present due to it being ### required in the check_db function in other package providers. ret = {} for name in names: if name in ret: - log.warning('pkg.check_db: Duplicate package name \'{0}\' ' - 'submitted'.format(name)) + log.warning( + "pkg.check_db: Duplicate package name '{0}' " "submitted".format(name) + ) continue - if '/' not in name: - ret.setdefault(name, {})['found'] = False - ret[name]['suggestions'] = porttree_matches(name) + if "/" not in name: + ret.setdefault(name, {})["found"] = False + ret[name]["suggestions"] = porttree_matches(name) else: - ret.setdefault(name, {})['found'] = name in _allnodes() - if ret[name]['found'] is False: - ret[name]['suggestions'] = [] + ret.setdefault(name, {})["found"] = name in _allnodes() + if ret[name]["found"] is False: + ret[name]["suggestions"] = [] return ret def ex_mod_init(low): - ''' + """ If the config option ``ebuild.enforce_nice_config`` is set to True, this module will enforce a nice tree structure for /etc/portage/package.* configuration files. @@ -236,14 +244,14 @@ def ex_mod_init(low): .. code-block:: bash salt '*' pkg.ex_mod_init - ''' - if __salt__['config.get']('ebuild.enforce_nice_config', False): - __salt__['portage_config.enforce_nice_config']() + """ + if __salt__["config.get"]("ebuild.enforce_nice_config", False): + __salt__["portage_config.enforce_nice_config"]() return True def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -254,11 +262,11 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version ... - ''' - refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) + """ + refresh = salt.utils.data.is_true(kwargs.pop("refresh", True)) if len(names) == 0: - return '' + return "" # Refresh before looking for the latest version available if refresh: @@ -267,10 +275,15 @@ def latest_version(*names, **kwargs): ret = {} # Initialize the dict with empty strings for name in names: - ret[name] = '' + ret[name] = "" installed = _cpv_to_version(_vartree().dep_bestmatch(name)) avail = _cpv_to_version(_porttree().dep_bestmatch(name)) - if avail and (not installed or salt.utils.versions.compare(ver1=installed, oper='<', ver2=avail, cmp_func=version_cmp)): + if avail and ( + not installed + or salt.utils.versions.compare( + ver1=installed, oper="<", ver2=avail, cmp_func=version_cmp + ) + ): ret[name] = avail # Return a string if only one package name passed @@ -280,61 +293,67 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def _get_upgradable(backtrack=3): - ''' + """ Utility function to get upgradable packages Sample return data: { 'pkgname': '1.2.3-45', ... } - ''' + """ - cmd = ['emerge', - '--ask', 'n', - '--backtrack', '{0}'.format(backtrack), - '--pretend', - '--update', - '--newuse', - '--deep', - '@world'] + cmd = [ + "emerge", + "--ask", + "n", + "--backtrack", + "{0}".format(backtrack), + "--pretend", + "--update", + "--newuse", + "--deep", + "@world", + ] - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + call = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if call['retcode'] != 0: - msg = 'Failed to get upgrades' - for key in ('stderr', 'stdout'): + if call["retcode"] != 0: + msg = "Failed to get upgrades" + for key in ("stderr", "stdout"): if call[key]: - msg += ': ' + call[key] + msg += ": " + call[key] break raise CommandExecutionError(msg) else: - out = call['stdout'] + out = call["stdout"] - rexp = re.compile(r'(?m)^\[.+\] ' - r'([^ ]+/[^ ]+)' # Package string - '-' - r'([0-9]+[^ ]+)' # Version - r'.*$') - keys = ['name', 'version'] + rexp = re.compile( + r"(?m)^\[.+\] " + r"([^ ]+/[^ ]+)" # Package string + "-" + r"([0-9]+[^ ]+)" # Version + r".*$" + ) + keys = ["name", "version"] _get = lambda l, k: l[keys.index(k)] upgrades = rexp.findall(out) ret = {} for line in upgrades: - name = _get(line, 'name') - version_num = _get(line, 'version') + name = _get(line, "name") + version_num = _get(line, "version") ret[name] = version_num return ret def list_upgrades(refresh=True, backtrack=3, **kwargs): # pylint: disable=W0613 - ''' + """ List all available package upgrades. refresh @@ -352,14 +371,14 @@ def list_upgrades(refresh=True, backtrack=3, **kwargs): # pylint: disable=W0613 .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ if salt.utils.data.is_true(refresh): refresh_db() return _get_upgradable(backtrack) def upgrade_available(name): - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -367,12 +386,12 @@ def upgrade_available(name): .. code-block:: bash salt '*' pkg.upgrade_available - ''' - return latest_version(name) != '' + """ + return latest_version(name) != "" def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -383,17 +402,17 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def porttree_matches(name): - ''' + """ Returns a list containing the matches for a given package name from the portage tree. Note that the specific version of the package will not be provided for packages that have several versions in the portage tree, but rather the name of the package (i.e. "dev-python/paramiko"). - ''' + """ matches = [] for category in _porttree().dbapi.categories: if _porttree().dbapi.cp_list(category + "/" + name): @@ -402,7 +421,7 @@ def porttree_matches(name): def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed in a dict:: {'': ''} @@ -412,36 +431,35 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret ret = {} pkgs = _vartree().dbapi.cpv_all() for cpv in pkgs: - __salt__['pkg_resource.add_pkg'](ret, - _cpv_to_cp(cpv), - _cpv_to_version(cpv)) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.add_pkg"](ret, _cpv_to_cp(cpv), _cpv_to_version(cpv)) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def refresh_db(): - ''' + """ Update the portage tree using the first available method from the following list: @@ -463,42 +481,46 @@ def refresh_db(): .. code-block:: bash salt '*' pkg.refresh_db - ''' - has_emaint = os.path.isdir('/etc/portage/repos.conf') - has_eix = True if 'eix.sync' in __salt__ else False - has_webrsync = True if __salt__['makeconf.features_contains']('webrsync-gpg') else False + """ + has_emaint = os.path.isdir("/etc/portage/repos.conf") + has_eix = True if "eix.sync" in __salt__ else False + has_webrsync = ( + True if __salt__["makeconf.features_contains"]("webrsync-gpg") else False + ) # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) # Option to prevent syncing package tree if done in the last 24 hours - if __salt__['pillar.get']('portage:sync_wait_one_day', False): - main_repo_root = __salt__['cmd.run']('portageq get_repo_path / gentoo') + if __salt__["pillar.get"]("portage:sync_wait_one_day", False): + main_repo_root = __salt__["cmd.run"]("portageq get_repo_path / gentoo") day = datetime.timedelta(days=1) now = datetime.datetime.now() timestamp = datetime.datetime.fromtimestamp(os.path.getmtime(main_repo_root)) if now - timestamp < day: - log.info('Did not sync package tree since last sync was done at' - ' {0}, less than 1 day ago'.format(timestamp)) + log.info( + "Did not sync package tree since last sync was done at" + " {0}, less than 1 day ago".format(timestamp) + ) return False if has_emaint: - return __salt__['cmd.retcode']('emaint sync -a') == 0 + return __salt__["cmd.retcode"]("emaint sync -a") == 0 elif has_eix: - return __salt__['eix.sync']() + return __salt__["eix.sync"]() elif has_webrsync: # GPG sign verify is supported only for 'webrsync' - cmd = 'emerge-webrsync -q' + cmd = "emerge-webrsync -q" # Prefer 'delta-webrsync' to 'webrsync' - if salt.utils.path.which('emerge-delta-webrsync'): - cmd = 'emerge-delta-webrsync -q' - return __salt__['cmd.retcode'](cmd) == 0 + if salt.utils.path.which("emerge-delta-webrsync"): + cmd = "emerge-delta-webrsync -q" + return __salt__["cmd.retcode"](cmd) == 0 else: # Default to deprecated `emerge --sync` form - return __salt__['cmd.retcode']('emerge --ask n --quiet --sync') == 0 + return __salt__["cmd.retcode"]("emerge --ask n --quiet --sync") == 0 def _flags_changed(inst_flags, conf_flags): - ''' + """ @type inst_flags: list @param inst_flags: list of use flags which were used when package was installed @@ -506,7 +528,7 @@ def _flags_changed(inst_flags, conf_flags): @param conf_flags: list of use flags form portage/package.use @rtype: bool @return: True, if lists have changes - ''' + """ conf_flags = conf_flags[:] for i in inst_flags: try: @@ -516,16 +538,18 @@ def _flags_changed(inst_flags, conf_flags): return True if conf_flags else False -def install(name=None, - refresh=False, - pkgs=None, - sources=None, - slot=None, - fromrepo=None, - uses=None, - binhost=None, - **kwargs): - ''' +def install( + name=None, + refresh=False, + pkgs=None, + sources=None, + slot=None, + fromrepo=None, + uses=None, + binhost=None, + **kwargs +): + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -628,22 +652,24 @@ def install(name=None, {'': {'old': '', 'new': ''}} - ''' - log.debug('Called modules.pkg.install: {0}'.format( - { - 'name': name, - 'refresh': refresh, - 'pkgs': pkgs, - 'sources': sources, - 'kwargs': kwargs, - 'binhost': binhost, - } - )) + """ + log.debug( + "Called modules.pkg.install: {0}".format( + { + "name": name, + "refresh": refresh, + "pkgs": pkgs, + "sources": sources, + "kwargs": kwargs, + "binhost": binhost, + } + ) + ) if salt.utils.data.is_true(refresh): refresh_db() try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: @@ -651,34 +677,34 @@ def install(name=None, # Handle version kwarg for a single package target if pkgs is None and sources is None: - version_num = kwargs.get('version') + version_num = kwargs.get("version") if not version_num: - version_num = '' + version_num = "" if slot is not None: - version_num += ':{0}'.format(slot) + version_num += ":{0}".format(slot) if fromrepo is not None: - version_num += '::{0}'.format(fromrepo) + version_num += "::{0}".format(fromrepo) if uses is not None: - version_num += '[{0}]'.format(','.join(uses)) + version_num += "[{0}]".format(",".join(uses)) pkg_params = {name: version_num} if pkg_params is None or len(pkg_params) == 0: return {} - elif pkg_type == 'file': - emerge_opts = ['tbz2file'] + elif pkg_type == "file": + emerge_opts = ["tbz2file"] else: emerge_opts = [] - if binhost == 'try': - bin_opts = ['-g'] - elif binhost == 'force': - bin_opts = ['-G'] + if binhost == "try": + bin_opts = ["-g"] + elif binhost == "force": + bin_opts = ["-G"] else: bin_opts = [] changes = {} - if pkg_type == 'repository': + if pkg_type == "repository": targets = list() for param, version_num in six.iteritems(pkg_params): original_param = param @@ -691,33 +717,37 @@ def install(name=None, else: keyword = None - match = re.match('^(~)?([<>])?(=)?([^<>=]*)$', version_num) + match = re.match("^(~)?([<>])?(=)?([^<>=]*)$", version_num) if match: keyword, gt_lt, eq, verstr = match.groups() - prefix = gt_lt or '' - prefix += eq or '' + prefix = gt_lt or "" + prefix += eq or "" # If no prefix characters were supplied and verstr contains a version, use '=' - if len(verstr) > 0 and verstr[0] != ':' and verstr[0] != '[': - prefix = prefix or '=' - target = '{0}{1}-{2}'.format(prefix, param, verstr) + if len(verstr) > 0 and verstr[0] != ":" and verstr[0] != "[": + prefix = prefix or "=" + target = "{0}{1}-{2}".format(prefix, param, verstr) else: - target = '{0}{1}'.format(param, verstr) + target = "{0}{1}".format(param, verstr) else: - target = '{0}'.format(param) + target = "{0}".format(param) - if '[' in target: - old = __salt__['portage_config.get_flags_from_package_conf']('use', target) - __salt__['portage_config.append_use_flags'](target) - new = __salt__['portage_config.get_flags_from_package_conf']('use', target) + if "[" in target: + old = __salt__["portage_config.get_flags_from_package_conf"]( + "use", target + ) + __salt__["portage_config.append_use_flags"](target) + new = __salt__["portage_config.get_flags_from_package_conf"]( + "use", target + ) if old != new: - changes[param + '-USE'] = {'old': old, 'new': new} - target = target[:target.rfind('[')] + changes[param + "-USE"] = {"old": old, "new": new} + target = target[: target.rfind("[")] if keyword is not None: - __salt__['portage_config.append_to_package_conf']('accept_keywords', - target, - ['~ARCH']) - changes[param + '-ACCEPT_KEYWORD'] = {'old': '', 'new': '~ARCH'} + __salt__["portage_config.append_to_package_conf"]( + "accept_keywords", target, ["~ARCH"] + ) + changes[param + "-ACCEPT_KEYWORD"] = {"old": "", "new": "~ARCH"} if not changes: inst_v = version(param) @@ -725,48 +755,49 @@ def install(name=None, # Prevent latest_version from calling refresh_db. Either we # just called it or we were asked not to. if latest_version(param, refresh=False) == inst_v: - all_uses = __salt__['portage_config.get_cleared_flags'](param) + all_uses = __salt__["portage_config.get_cleared_flags"](param) if _flags_changed(*all_uses): - changes[param] = {'version': inst_v, - 'old': {'use': all_uses[0]}, - 'new': {'use': all_uses[1]}} + changes[param] = { + "version": inst_v, + "old": {"use": all_uses[0]}, + "new": {"use": all_uses[1]}, + } targets.append(target) else: targets = pkg_params cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) - cmd.extend(['emerge', '--ask', 'n', '--quiet']) + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) + cmd.extend(["emerge", "--ask", "n", "--quiet"]) cmd.extend(bin_opts) cmd.extend(emerge_opts) cmd.extend(targets) old = list_pkgs() - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - if call['retcode'] != 0: - needed_changes = _process_emerge_err(call['stdout'], call['stderr']) + call = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if call["retcode"] != 0: + needed_changes = _process_emerge_err(call["stdout"], call["stderr"]) else: needed_changes = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() changes.update(salt.utils.data.compare_dicts(old, new)) if needed_changes: raise CommandExecutionError( - 'Error occurred installing package(s)', - info={'needed changes': needed_changes, 'changes': changes} + "Error occurred installing package(s)", + info={"needed changes": needed_changes, "changes": changes}, ) return changes def update(pkg, slot=None, fromrepo=None, refresh=False, binhost=None): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -805,61 +836,55 @@ def update(pkg, slot=None, fromrepo=None, refresh=False, binhost=None): .. code-block:: bash salt '*' pkg.update - ''' + """ if salt.utils.data.is_true(refresh): refresh_db() full_atom = pkg if slot is not None: - full_atom = '{0}:{1}'.format(full_atom, slot) + full_atom = "{0}:{1}".format(full_atom, slot) if fromrepo is not None: - full_atom = '{0}::{1}'.format(full_atom, fromrepo) + full_atom = "{0}::{1}".format(full_atom, fromrepo) - if binhost == 'try': - bin_opts = ['-g'] - elif binhost == 'force': - bin_opts = ['-G'] + if binhost == "try": + bin_opts = ["-g"] + elif binhost == "force": + bin_opts = ["-G"] else: bin_opts = [] old = list_pkgs() cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) - cmd.extend(['emerge', - '--ask', 'n', - '--quiet', - '--update', - '--newuse', - '--oneshot']) + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) + cmd.extend(["emerge", "--ask", "n", "--quiet", "--update", "--newuse", "--oneshot"]) cmd.extend(bin_opts) cmd.append(full_atom) - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - if call['retcode'] != 0: - needed_changes = _process_emerge_err(call['stdout'], call['stderr']) + call = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if call["retcode"] != 0: + needed_changes = _process_emerge_err(call["stdout"], call["stderr"]) else: needed_changes = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if needed_changes: raise CommandExecutionError( - 'Problem encountered updating package(s)', - info={'needed_changes': needed_changes, 'changes': ret} + "Problem encountered updating package(s)", + info={"needed_changes": needed_changes, "changes": ret}, ) return ret def upgrade(refresh=True, binhost=None, backtrack=3): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -901,55 +926,58 @@ def upgrade(refresh=True, binhost=None, backtrack=3): .. code-block:: bash salt '*' pkg.upgrade - ''' - ret = {'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"changes": {}, "result": True, "comment": ""} if salt.utils.data.is_true(refresh): refresh_db() - if binhost == 'try': - bin_opts = ['--getbinpkg'] - elif binhost == 'force': - bin_opts = ['--getbinpkgonly'] + if binhost == "try": + bin_opts = ["--getbinpkg"] + elif binhost == "force": + bin_opts = ["--getbinpkgonly"] else: bin_opts = [] old = list_pkgs() cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) - cmd.extend(['emerge', - '--ask', 'n', - '--quiet', - '--backtrack', '{0}'.format(backtrack), - '--update', - '--newuse', - '--deep']) + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) + cmd.extend( + [ + "emerge", + "--ask", + "n", + "--quiet", + "--backtrack", + "{0}".format(backtrack), + "--update", + "--newuse", + "--deep", + ] + ) if bin_opts: cmd.extend(bin_opts) - cmd.append('@world') + cmd.append("@world") - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - __context__.pop('pkg.list_pkgs', None) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret def remove(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -993,19 +1021,24 @@ def remove(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs): salt '*' pkg.remove slot=4.4 fromrepo=gentoo salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) old = list_pkgs() - if name and not pkgs and (slot is not None or fromrepo is not None)and len(pkg_params) == 1: + if ( + name + and not pkgs + and (slot is not None or fromrepo is not None) + and len(pkg_params) == 1 + ): fullatom = name if slot is not None: - targets = ['{0}:{1}'.format(fullatom, slot)] + targets = ["{0}:{1}".format(fullatom, slot)] if fromrepo is not None: - targets = ['{0}::{1}'.format(fullatom, fromrepo)] + targets = ["{0}::{1}".format(fullatom, fromrepo)] targets = [fullatom] else: targets = [x for x in pkg_params if x in old] @@ -1014,41 +1047,34 @@ def remove(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs): return {} cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) - cmd.extend(['emerge', - '--ask', 'n', - '--quiet', - '--unmerge', - '--quiet-unmerge-warn']) + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) + cmd.extend(["emerge", "--ask", "n", "--quiet", "--unmerge", "--quiet-unmerge-warn"]) cmd.extend(targets) - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False - ) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def purge(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -1094,14 +1120,14 @@ def purge(name=None, slot=None, fromrepo=None, pkgs=None, **kwargs): salt '*' pkg.purge slot=4.4 salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' + """ ret = remove(name=name, slot=slot, fromrepo=fromrepo, pkgs=pkgs) ret.update(depclean(name=name, slot=slot, fromrepo=fromrepo, pkgs=pkgs)) return ret def depclean(name=None, slot=None, fromrepo=None, pkgs=None): - ''' + """ Portage has a function to remove unused dependencies. If a package is provided, it will only removed the package if no other package depends on it. @@ -1126,34 +1152,37 @@ def depclean(name=None, slot=None, fromrepo=None, pkgs=None): .. code-block:: bash salt '*' pkg.depclean - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) old = list_pkgs() - if name and not pkgs and (slot is not None or fromrepo is not None)and len(pkg_params) == 1: + if ( + name + and not pkgs + and (slot is not None or fromrepo is not None) + and len(pkg_params) == 1 + ): fullatom = name if slot is not None: - targets = ['{0}:{1}'.format(fullatom, slot)] + targets = ["{0}:{1}".format(fullatom, slot)] if fromrepo is not None: - targets = ['{0}::{1}'.format(fullatom, fromrepo)] + targets = ["{0}::{1}".format(fullatom, fromrepo)] targets = [fullatom] else: targets = [x for x in pkg_params if x in old] - cmd = ['emerge', '--ask', 'n', '--quiet', '--depclean'] + targets - __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - __context__.pop('pkg.list_pkgs', None) + cmd = ["emerge", "--ask", "n", "--quiet", "--depclean"] + targets + __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() return salt.utils.data.compare_dicts(old, new) def version_cmp(pkg1, pkg2, **kwargs): - ''' + """ Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem making the comparison. @@ -1163,18 +1192,18 @@ def version_cmp(pkg1, pkg2, **kwargs): .. code-block:: bash salt '*' pkg.version_cmp '0.2.4-0' '0.2.4.1-0' - ''' + """ # ignore_epoch is not supported here, but has to be included for API # compatibility. Rather than putting this argument into the function # definition (and thus have it show up in the docs), we just pop it out of # the kwargs dict and then raise an exception if any kwargs other than # ignore_epoch were passed. kwargs = salt.utils.args.clean_kwargs(**kwargs) - kwargs.pop('ignore_epoch', None) + kwargs.pop("ignore_epoch", None) if kwargs: salt.utils.args.invalid_kwargs(kwargs) - regex = r'^~?([^:\[]+):?[^\[]*\[?.*$' + regex = r"^~?([^:\[]+):?[^\[]*\[?.*$" ver1 = re.match(regex, pkg1) ver2 = re.match(regex, pkg2) @@ -1184,7 +1213,7 @@ def version_cmp(pkg1, pkg2, **kwargs): def version_clean(version): - ''' + """ Clean the version string removing extra data. CLI Example: @@ -1192,12 +1221,12 @@ def version_clean(version): .. code-block:: bash salt '*' pkg.version_clean - ''' - return re.match(r'^~?[<>]?=?([^<>=:\[]+).*$', version) + """ + return re.match(r"^~?[<>]?=?([^<>=:\[]+).*$", version) def check_extra_requirements(pkgname, pkgver): - ''' + """ Check if the installed package already has the given requirements. CLI Example: @@ -1205,51 +1234,55 @@ def check_extra_requirements(pkgname, pkgver): .. code-block:: bash salt '*' pkg.check_extra_requirements 'sys-devel/gcc' '~>4.1.2:4.1::gentoo[nls,fortran]' - ''' + """ keyword = None - match = re.match('^(~)?([<>])?(=)?([^<>=]*)$', pkgver) + match = re.match("^(~)?([<>])?(=)?([^<>=]*)$", pkgver) if match: keyword, gt_lt, eq, verstr = match.groups() - prefix = gt_lt or '' - prefix += eq or '' + prefix = gt_lt or "" + prefix += eq or "" # We need to delete quotes around use flag list elements verstr = verstr.replace("'", "") # If no prefix characters were supplied and verstr contains a version, use '=' - if verstr[0] != ':' and verstr[0] != '[': - prefix = prefix or '=' - atom = '{0}{1}-{2}'.format(prefix, pkgname, verstr) + if verstr[0] != ":" and verstr[0] != "[": + prefix = prefix or "=" + atom = "{0}{1}-{2}".format(prefix, pkgname, verstr) else: - atom = '{0}{1}'.format(pkgname, verstr) + atom = "{0}{1}".format(pkgname, verstr) else: return True try: - cpv = _porttree().dbapi.xmatch('bestmatch-visible', atom) + cpv = _porttree().dbapi.xmatch("bestmatch-visible", atom) except portage.exception.InvalidAtom as iae: - log.error('Unable to find a matching package for {0}: ({1})'.format(atom, iae)) + log.error("Unable to find a matching package for {0}: ({1})".format(atom, iae)) return False - if cpv == '': + if cpv == "": return False try: - cur_repo, cur_use = _vartree().dbapi.aux_get(cpv, ['repository', 'USE']) + cur_repo, cur_use = _vartree().dbapi.aux_get(cpv, ["repository", "USE"]) except KeyError: return False - des_repo = re.match(r'^.+::([^\[]+).*$', atom) + des_repo = re.match(r"^.+::([^\[]+).*$", atom) if des_repo and des_repo.group(1) != cur_repo: return False des_uses = set(portage.dep.dep_getusedeps(atom)) cur_use = cur_use.split() - if len([x for x in des_uses.difference(cur_use) - if x[0] != '-' or x[1:] in cur_use]) > 0: + if ( + len( + [x for x in des_uses.difference(cur_use) if x[0] != "-" or x[1:] in cur_use] + ) + > 0 + ): return False if keyword: - if not __salt__['portage_config.has_flag']('accept_keywords', atom, '~ARCH'): + if not __salt__["portage_config.has_flag"]("accept_keywords", atom, "~ARCH"): return False return True diff --git a/salt/modules/eix.py b/salt/modules/eix.py index ab482a21a5e..2d9e3192cff 100644 --- a/salt/modules/eix.py +++ b/salt/modules/eix.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Support for Eix -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -9,16 +9,19 @@ import salt.utils.path def __virtual__(): - ''' + """ Only works on Gentoo systems with eix installed - ''' - if __grains__['os'] == 'Gentoo' and salt.utils.path.which('eix'): - return 'eix' - return (False, 'The eix execution module cannot be loaded: either the system is not Gentoo or the eix binary is not in the path.') + """ + if __grains__["os"] == "Gentoo" and salt.utils.path.which("eix"): + return "eix" + return ( + False, + "The eix execution module cannot be loaded: either the system is not Gentoo or the eix binary is not in the path.", + ) def sync(): - ''' + """ Sync portage/overlay trees and update the eix database CLI Example: @@ -26,28 +29,34 @@ def sync(): .. code-block:: bash salt '*' eix.sync - ''' + """ cmd = 'eix-sync -q -C "--ask" -C "n"' - if 'makeconf.features_contains'in __salt__ and __salt__['makeconf.features_contains']('webrsync-gpg'): + if "makeconf.features_contains" in __salt__ and __salt__[ + "makeconf.features_contains" + ]("webrsync-gpg"): # GPG sign verify is supported only for "webrsync" - if salt.utils.path.which('emerge-delta-webrsync'): # We prefer 'delta-webrsync' to 'webrsync' - cmd += ' -W' + if salt.utils.path.which( + "emerge-delta-webrsync" + ): # We prefer 'delta-webrsync' to 'webrsync' + cmd += " -W" else: - cmd += ' -w' - return __salt__['cmd.retcode'](cmd) == 0 + cmd += " -w" + return __salt__["cmd.retcode"](cmd) == 0 else: - if __salt__['cmd.retcode'](cmd) == 0: + if __salt__["cmd.retcode"](cmd) == 0: return True # We fall back to "webrsync" if "rsync" fails for some reason - if salt.utils.path.which('emerge-delta-webrsync'): # We prefer 'delta-webrsync' to 'webrsync' - cmd += ' -W' + if salt.utils.path.which( + "emerge-delta-webrsync" + ): # We prefer 'delta-webrsync' to 'webrsync' + cmd += " -W" else: - cmd += ' -w' - return __salt__['cmd.retcode'](cmd) == 0 + cmd += " -w" + return __salt__["cmd.retcode"](cmd) == 0 def update(): - ''' + """ Update the eix database CLI Example: @@ -55,6 +64,6 @@ def update(): .. code-block:: bash salt '*' eix.update - ''' - cmd = 'eix-update --quiet' - return __salt__['cmd.retcode'](cmd) == 0 + """ + cmd = "eix-update --quiet" + return __salt__["cmd.retcode"](cmd) == 0 diff --git a/salt/modules/elasticsearch.py b/salt/modules/elasticsearch.py index 0d48905e910..f77e29dd4e2 100644 --- a/salt/modules/elasticsearch.py +++ b/salt/modules/elasticsearch.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Elasticsearch - A distributed RESTful search and analytics server Module to provide Elasticsearch compatibility to Salt @@ -47,10 +47,11 @@ Module to provide Elasticsearch compatibility to Salt overwrite options passed into pillar. Some functionality might be limited by elasticsearch-py and Elasticsearch server versions. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import sys @@ -63,26 +64,34 @@ log = logging.getLogger(__name__) # Import third party libs try: import elasticsearch - from elasticsearch import RequestsHttpConnection # pylint: disable=no-name-in-module - logging.getLogger('elasticsearch').setLevel(logging.CRITICAL) + + # pylint: disable=no-name-in-module + from elasticsearch import RequestsHttpConnection + + # pylint: enable=no-name-in-module + + logging.getLogger("elasticsearch").setLevel(logging.CRITICAL) HAS_ELASTICSEARCH = True except ImportError: HAS_ELASTICSEARCH = False def __virtual__(): - ''' + """ Only load if elasticsearch libraries exist. - ''' + """ if not HAS_ELASTICSEARCH: - return (False, 'Cannot load module elasticsearch: elasticsearch libraries not found') + return ( + False, + "Cannot load module elasticsearch: elasticsearch libraries not found", + ) return True def _get_instance(hosts=None, profile=None): - ''' + """ Return the elasticsearch instance - ''' + """ es = None proxies = None use_ssl = False @@ -92,29 +101,29 @@ def _get_instance(hosts=None, profile=None): timeout = 10 if profile is None: - profile = 'elasticsearch' + profile = "elasticsearch" if isinstance(profile, six.string_types): - _profile = __salt__['config.option'](profile, None) + _profile = __salt__["config.option"](profile, None) elif isinstance(profile, dict): _profile = profile if _profile: - hosts = _profile.get('host', hosts) + hosts = _profile.get("host", hosts) if not hosts: - hosts = _profile.get('hosts', hosts) - proxies = _profile.get('proxies', None) - use_ssl = _profile.get('use_ssl', False) - ca_certs = _profile.get('ca_certs', None) - verify_certs = _profile.get('verify_certs', True) - username = _profile.get('username', None) - password = _profile.get('password', None) - timeout = _profile.get('timeout', 10) + hosts = _profile.get("hosts", hosts) + proxies = _profile.get("proxies", None) + use_ssl = _profile.get("use_ssl", False) + ca_certs = _profile.get("ca_certs", None) + verify_certs = _profile.get("verify_certs", True) + username = _profile.get("username", None) + password = _profile.get("password", None) + timeout = _profile.get("timeout", 10) if username and password: http_auth = (username, password) if not hosts: - hosts = ['127.0.0.1:9200'] + hosts = ["127.0.0.1:9200"] if isinstance(hosts, six.string_types): hosts = [hosts] try: @@ -122,7 +131,7 @@ def _get_instance(hosts=None, profile=None): # Custom connection class to use requests module with proxies class ProxyConnection(RequestsHttpConnection): def __init__(self, *args, **kwargs): - proxies = kwargs.pop('proxies', {}) + proxies = kwargs.pop("proxies", {}) super(ProxyConnection, self).__init__(*args, **kwargs) self.session.proxies = proxies @@ -138,24 +147,27 @@ def _get_instance(hosts=None, profile=None): ) else: es = elasticsearch.Elasticsearch( - hosts, - use_ssl=use_ssl, - ca_certs=ca_certs, - verify_certs=verify_certs, - http_auth=http_auth, - timeout=timeout, - ) + hosts, + use_ssl=use_ssl, + ca_certs=ca_certs, + verify_certs=verify_certs, + http_auth=http_auth, + timeout=timeout, + ) # Try the connection es.info() except elasticsearch.exceptions.TransportError as err: raise CommandExecutionError( - 'Could not connect to Elasticsearch host/ cluster {0} due to {1}'.format(hosts, err)) + "Could not connect to Elasticsearch host/ cluster {0} due to {1}".format( + hosts, err + ) + ) return es def ping(allow_failure=False, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Test connection to Elasticsearch instance. This method does not fail if not explicitly specified. @@ -167,7 +179,7 @@ def ping(allow_failure=False, hosts=None, profile=None): salt myminion elasticsearch.ping allow_failure=True salt myminion elasticsearch.ping profile=elasticsearch-extra - ''' + """ try: _get_instance(hosts, profile) except CommandExecutionError as e: @@ -178,7 +190,7 @@ def ping(allow_failure=False, hosts=None, profile=None): def info(hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Return Elasticsearch information. @@ -187,17 +199,21 @@ def info(hosts=None, profile=None): salt myminion elasticsearch.info salt myminion elasticsearch.info profile=elasticsearch-extra - ''' + """ es = _get_instance(hosts, profile) try: return es.info() except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve server information, server returned code {0} with message {1}".format(e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve server information, server returned code {0} with message {1}".format( + e.status_code, e.error + ) + ) def node_info(nodes=None, flat_settings=False, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Return Elasticsearch node information. @@ -210,17 +226,21 @@ def node_info(nodes=None, flat_settings=False, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.node_info flat_settings=True - ''' + """ es = _get_instance(hosts, profile) try: return es.nodes.info(node_id=nodes, flat_settings=flat_settings) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve node information, server returned code {0} with message {1}".format(e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve node information, server returned code {0} with message {1}".format( + e.status_code, e.error + ) + ) -def cluster_health(index=None, level='cluster', local=False, hosts=None, profile=None): - ''' +def cluster_health(index=None, level="cluster", local=False, hosts=None, profile=None): + """ .. versionadded:: 2017.7.0 Return Elasticsearch cluster health. @@ -235,17 +255,21 @@ def cluster_health(index=None, level='cluster', local=False, hosts=None, profile CLI example:: salt myminion elasticsearch.cluster_health - ''' + """ es = _get_instance(hosts, profile) try: return es.cluster.health(index=index, level=level, local=local) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve health information, server returned code {0} with message {1}".format(e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve health information, server returned code {0} with message {1}".format( + e.status_code, e.error + ) + ) def cluster_stats(nodes=None, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Return Elasticsearch cluster stats. @@ -256,17 +280,23 @@ def cluster_stats(nodes=None, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.cluster_stats - ''' + """ es = _get_instance(hosts, profile) try: return es.cluster.stats(node_id=nodes) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve cluster stats, server returned code {0} with message {1}".format(e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve cluster stats, server returned code {0} with message {1}".format( + e.status_code, e.error + ) + ) -def cluster_get_settings(flat_settings=False, include_defaults=False, hosts=None, profile=None): - ''' +def cluster_get_settings( + flat_settings=False, include_defaults=False, hosts=None, profile=None +): + """ .. versionadded:: 3000 Return Elasticsearch cluster settings. @@ -280,17 +310,23 @@ def cluster_get_settings(flat_settings=False, include_defaults=False, hosts=None CLI example:: salt myminion elasticsearch.cluster_get_settings - ''' + """ es = _get_instance(hosts, profile) try: - return es.cluster.get_settings(flat_settings=flat_settings, include_defaults=include_defaults) + return es.cluster.get_settings( + flat_settings=flat_settings, include_defaults=include_defaults + ) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve cluster settings, server returned code {0} with message {1}".format(e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve cluster settings, server returned code {0} with message {1}".format( + e.status_code, e.error + ) + ) def cluster_put_settings(body=None, flat_settings=False, hosts=None, profile=None): - ''' + """ .. versionadded:: 3000 Set Elasticsearch cluster settings. @@ -306,20 +342,24 @@ def cluster_put_settings(body=None, flat_settings=False, hosts=None, profile=Non salt myminion elasticsearch.cluster_put_settings '{"persistent": {"indices.recovery.max_bytes_per_sec": "50mb"}}' salt myminion elasticsearch.cluster_put_settings '{"transient": {"indices.recovery.max_bytes_per_sec": "50mb"}}' - ''' + """ if not body: - message = 'You must provide a body with settings' + message = "You must provide a body with settings" raise SaltInvocationError(message) es = _get_instance(hosts, profile) try: return es.cluster.put_settings(body=body, flat_settings=flat_settings) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot update cluster settings, server returned code {0} with message {1}".format(e.status_code, e.error)) + raise CommandExecutionError( + "Cannot update cluster settings, server returned code {0} with message {1}".format( + e.status_code, e.error + ) + ) def alias_create(indices, alias, hosts=None, body=None, profile=None, source=None): - ''' + """ Create an alias for a specific index/indices indices @@ -334,24 +374,28 @@ def alias_create(indices, alias, hosts=None, body=None, profile=None, source=Non CLI example:: salt myminion elasticsearch.alias_create testindex_v1 testindex - ''' + """ es = _get_instance(hosts, profile) if source and body: - message = 'Either body or source should be specified but not both.' + message = "Either body or source should be specified but not both." raise SaltInvocationError(message) if source: - body = __salt__['cp.get_file_str']( - source, - saltenv=__opts__.get('saltenv', 'base')) + body = __salt__["cp.get_file_str"]( + source, saltenv=__opts__.get("saltenv", "base") + ) try: result = es.indices.put_alias(index=indices, name=alias, body=body) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot create alias {0} in index {1}, server returned code {2} with message {3}".format(alias, indices, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create alias {0} in index {1}, server returned code {2} with message {3}".format( + alias, indices, e.status_code, e.error + ) + ) def alias_delete(indices, aliases, hosts=None, body=None, profile=None, source=None): - ''' + """ Delete an alias of an index indices @@ -362,27 +406,31 @@ def alias_delete(indices, aliases, hosts=None, body=None, profile=None, source=N CLI example:: salt myminion elasticsearch.alias_delete testindex_v1 testindex - ''' + """ es = _get_instance(hosts, profile) if source and body: - message = 'Either body or source should be specified but not both.' + message = "Either body or source should be specified but not both." raise SaltInvocationError(message) if source: - body = __salt__['cp.get_file_str']( - source, - saltenv=__opts__.get('saltenv', 'base')) + body = __salt__["cp.get_file_str"]( + source, saltenv=__opts__.get("saltenv", "base") + ) try: result = es.indices.delete_alias(index=indices, name=aliases) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.exceptions.NotFoundError: return True except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot delete alias {0} in index {1}, server returned code {2} with message {3}".format(aliases, indices, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot delete alias {0} in index {1}, server returned code {2} with message {3}".format( + aliases, indices, e.status_code, e.error + ) + ) def alias_exists(aliases, indices=None, hosts=None, profile=None): - ''' + """ Return a boolean indicating whether given alias exists indices @@ -393,18 +441,22 @@ def alias_exists(aliases, indices=None, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.alias_exists None testindex - ''' + """ es = _get_instance(hosts, profile) try: return es.indices.exists_alias(name=aliases, index=indices) except elasticsearch.exceptions.NotFoundError: return False except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot get alias {0} in index {1}, server returned code {2} with message {3}".format(aliases, indices, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot get alias {0} in index {1}, server returned code {2} with message {3}".format( + aliases, indices, e.status_code, e.error + ) + ) def alias_get(indices=None, aliases=None, hosts=None, profile=None): - ''' + """ Check for the existence of an alias and if it exists, return it indices @@ -415,7 +467,7 @@ def alias_get(indices=None, aliases=None, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.alias_get testindex - ''' + """ es = _get_instance(hosts, profile) try: @@ -423,11 +475,17 @@ def alias_get(indices=None, aliases=None, hosts=None, profile=None): except elasticsearch.exceptions.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot get alias {0} in index {1}, server returned code {2} with message {3}".format(aliases, indices, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot get alias {0} in index {1}, server returned code {2} with message {3}".format( + aliases, indices, e.status_code, e.error + ) + ) -def document_create(index, doc_type, body=None, id=None, hosts=None, profile=None, source=None): - ''' +def document_create( + index, doc_type, body=None, id=None, hosts=None, profile=None, source=None +): + """ Create a document in a specified index index @@ -444,23 +502,27 @@ def document_create(index, doc_type, body=None, id=None, hosts=None, profile=Non CLI example:: salt myminion elasticsearch.document_create testindex doctype1 '{}' - ''' + """ es = _get_instance(hosts, profile) if source and body: - message = 'Either body or source should be specified but not both.' + message = "Either body or source should be specified but not both." raise SaltInvocationError(message) if source: - body = __salt__['cp.get_file_str']( - source, - saltenv=__opts__.get('saltenv', 'base')) + body = __salt__["cp.get_file_str"]( + source, saltenv=__opts__.get("saltenv", "base") + ) try: return es.index(index=index, doc_type=doc_type, body=body, id=id) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot create document in index {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create document in index {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) def document_delete(index, doc_type, id, hosts=None, profile=None): - ''' + """ Delete a document from an index index @@ -473,7 +535,7 @@ def document_delete(index, doc_type, id, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.document_delete testindex doctype1 AUx-384m0Bug_8U80wQZ - ''' + """ es = _get_instance(hosts, profile) try: @@ -481,11 +543,15 @@ def document_delete(index, doc_type, id, hosts=None, profile=None): except elasticsearch.exceptions.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot delete document {0} in index {1}, server returned code {2} with message {3}".format(id, index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot delete document {0} in index {1}, server returned code {2} with message {3}".format( + id, index, e.status_code, e.error + ) + ) -def document_exists(index, id, doc_type='_all', hosts=None, profile=None): - ''' +def document_exists(index, id, doc_type="_all", hosts=None, profile=None): + """ Return a boolean indicating whether given document exists index @@ -498,7 +564,7 @@ def document_exists(index, id, doc_type='_all', hosts=None, profile=None): CLI example:: salt myminion elasticsearch.document_exists testindex AUx-384m0Bug_8U80wQZ - ''' + """ es = _get_instance(hosts, profile) try: @@ -506,11 +572,15 @@ def document_exists(index, id, doc_type='_all', hosts=None, profile=None): except elasticsearch.exceptions.NotFoundError: return False except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve document {0} from index {1}, server returned code {2} with message {3}".format(id, index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve document {0} from index {1}, server returned code {2} with message {3}".format( + id, index, e.status_code, e.error + ) + ) -def document_get(index, id, doc_type='_all', hosts=None, profile=None): - ''' +def document_get(index, id, doc_type="_all", hosts=None, profile=None): + """ Check for the existence of a document and if it exists, return it index @@ -523,7 +593,7 @@ def document_get(index, id, doc_type='_all', hosts=None, profile=None): CLI example:: salt myminion elasticsearch.document_get testindex AUx-384m0Bug_8U80wQZ - ''' + """ es = _get_instance(hosts, profile) try: @@ -531,11 +601,15 @@ def document_get(index, id, doc_type='_all', hosts=None, profile=None): except elasticsearch.exceptions.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve document {0} from index {1}, server returned code {2} with message {3}".format(id, index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve document {0} from index {1}, server returned code {2} with message {3}".format( + id, index, e.status_code, e.error + ) + ) def index_create(index, body=None, hosts=None, profile=None, source=None): - ''' + """ Create an index index @@ -549,27 +623,33 @@ def index_create(index, body=None, hosts=None, profile=None, source=None): salt myminion elasticsearch.index_create testindex salt myminion elasticsearch.index_create testindex2 '{"settings" : {"index" : {"number_of_shards" : 3, "number_of_replicas" : 2}}}' - ''' + """ es = _get_instance(hosts, profile) if source and body: - message = 'Either body or source should be specified but not both.' + message = "Either body or source should be specified but not both." raise SaltInvocationError(message) if source: - body = __salt__['cp.get_file_str']( - source, - saltenv=__opts__.get('saltenv', 'base')) + body = __salt__["cp.get_file_str"]( + source, saltenv=__opts__.get("saltenv", "base") + ) try: result = es.indices.create(index=index, body=body) - return result.get('acknowledged', False) and result.get("shards_acknowledged", True) + return result.get("acknowledged", False) and result.get( + "shards_acknowledged", True + ) except elasticsearch.TransportError as e: if "index_already_exists_exception" == e.error: return True - raise CommandExecutionError("Cannot create index {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create index {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) def index_delete(index, hosts=None, profile=None): - ''' + """ Delete an index index @@ -578,21 +658,25 @@ def index_delete(index, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.index_delete testindex - ''' + """ es = _get_instance(hosts, profile) try: result = es.indices.delete(index=index) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.exceptions.NotFoundError: return True except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot delete index {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot delete index {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) def index_exists(index, hosts=None, profile=None): - ''' + """ Return a boolean indicating whether given index exists index @@ -601,7 +685,7 @@ def index_exists(index, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.index_exists testindex - ''' + """ es = _get_instance(hosts, profile) try: @@ -609,11 +693,15 @@ def index_exists(index, hosts=None, profile=None): except elasticsearch.exceptions.NotFoundError: return False except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve index {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve index {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) def index_get(index, hosts=None, profile=None): - ''' + """ Check for the existence of an index and if it exists, return it index @@ -622,7 +710,7 @@ def index_get(index, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.index_get testindex - ''' + """ es = _get_instance(hosts, profile) try: @@ -630,11 +718,22 @@ def index_get(index, hosts=None, profile=None): except elasticsearch.exceptions.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve index {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve index {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) -def index_open(index, allow_no_indices=True, expand_wildcards='closed', ignore_unavailable=True, hosts=None, profile=None): - ''' +def index_open( + index, + allow_no_indices=True, + expand_wildcards="closed", + ignore_unavailable=True, + hosts=None, + profile=None, +): + """ .. versionadded:: 2017.7.0 Open specified index. @@ -651,19 +750,35 @@ def index_open(index, allow_no_indices=True, expand_wildcards='closed', ignore_u CLI example:: salt myminion elasticsearch.index_open testindex - ''' + """ es = _get_instance(hosts, profile) try: - result = es.indices.open(index=index, allow_no_indices=allow_no_indices, expand_wildcards=expand_wildcards, ignore_unavailable=ignore_unavailable) + result = es.indices.open( + index=index, + allow_no_indices=allow_no_indices, + expand_wildcards=expand_wildcards, + ignore_unavailable=ignore_unavailable, + ) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot open index {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot open index {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) -def index_close(index, allow_no_indices=True, expand_wildcards='open', ignore_unavailable=True, hosts=None, profile=None): - ''' +def index_close( + index, + allow_no_indices=True, + expand_wildcards="open", + ignore_unavailable=True, + hosts=None, + profile=None, +): + """ .. versionadded:: 2017.7.0 Close specified index. @@ -680,19 +795,28 @@ def index_close(index, allow_no_indices=True, expand_wildcards='open', ignore_un CLI example:: salt myminion elasticsearch.index_close testindex - ''' + """ es = _get_instance(hosts, profile) try: - result = es.indices.close(index=index, allow_no_indices=allow_no_indices, expand_wildcards=expand_wildcards, ignore_unavailable=ignore_unavailable) + result = es.indices.close( + index=index, + allow_no_indices=allow_no_indices, + expand_wildcards=expand_wildcards, + ignore_unavailable=ignore_unavailable, + ) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot close index {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot close index {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) def index_get_settings(hosts=None, profile=None, **kwargs): - ''' + """ .. versionadded:: 3000 Check for the existence of an index and if it exists, return its settings @@ -722,14 +846,14 @@ def index_get_settings(hosts=None, profile=None, **kwargs): CLI example:: salt myminion elasticsearch.index_get_settings index=testindex - ''' + """ es = _get_instance(hosts, profile) # Filtering Salt internal keys filtered_kwargs = kwargs.copy() for k in kwargs: - if k.startswith('__'): + if k.startswith("__"): filtered_kwargs.pop(k) try: @@ -737,11 +861,15 @@ def index_get_settings(hosts=None, profile=None, **kwargs): except elasticsearch.exceptions.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve index settings {0}, server returned code {1} with message {2}".format(kwargs, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve index settings {0}, server returned code {1} with message {2}".format( + kwargs, e.status_code, e.error + ) + ) def index_put_settings(body=None, hosts=None, profile=None, source=None, **kwargs): - ''' + """ .. versionadded:: 3000 Update existing index settings @@ -777,33 +905,37 @@ def index_put_settings(body=None, hosts=None, profile=None, source=None, **kwarg CLI example:: salt myminion elasticsearch.index_put_settings index=testindex body='{"settings" : {"index" : {"number_of_replicas" : 2}}}' - ''' + """ es = _get_instance(hosts, profile) if source and body: - message = 'Either body or source should be specified but not both.' + message = "Either body or source should be specified but not both." raise SaltInvocationError(message) if source: - body = __salt__['cp.get_file_str']( - source, - saltenv=__opts__.get('saltenv', 'base')) + body = __salt__["cp.get_file_str"]( + source, saltenv=__opts__.get("saltenv", "base") + ) # Filtering Salt internal keys filtered_kwargs = kwargs.copy() for k in kwargs: - if k.startswith('__'): + if k.startswith("__"): filtered_kwargs.pop(k) try: result = es.indices.put_settings(body=body, **filtered_kwargs) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.exceptions.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot update index settings {0}, server returned code {1} with message {2}".format(kwargs, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot update index settings {0}, server returned code {1} with message {2}".format( + kwargs, e.status_code, e.error + ) + ) def mapping_create(index, doc_type, body=None, hosts=None, profile=None, source=None): - ''' + """ Create a mapping in a given index index @@ -818,25 +950,29 @@ def mapping_create(index, doc_type, body=None, hosts=None, profile=None, source= CLI example:: salt myminion elasticsearch.mapping_create testindex user '{ "user" : { "properties" : { "message" : {"type" : "string", "store" : true } } } }' - ''' + """ es = _get_instance(hosts, profile) if source and body: - message = 'Either body or source should be specified but not both.' + message = "Either body or source should be specified but not both." raise SaltInvocationError(message) if source: - body = __salt__['cp.get_file_str']( - source, - saltenv=__opts__.get('saltenv', 'base')) + body = __salt__["cp.get_file_str"]( + source, saltenv=__opts__.get("saltenv", "base") + ) try: result = es.indices.put_mapping(index=index, doc_type=doc_type, body=body) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot create mapping {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create mapping {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) def mapping_delete(index, doc_type, hosts=None, profile=None): - ''' + """ Delete a mapping (type) along with its data. As of Elasticsearch 5.0 this is no longer available. index @@ -847,22 +983,26 @@ def mapping_delete(index, doc_type, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.mapping_delete testindex user - ''' + """ es = _get_instance(hosts, profile) try: result = es.indices.delete_mapping(index=index, doc_type=doc_type) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.exceptions.NotFoundError: return True except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot delete mapping {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot delete mapping {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) except AttributeError: raise CommandExecutionError("Method is not applicable for Elasticsearch 5.0+") def mapping_get(index, doc_type, hosts=None, profile=None): - ''' + """ Retrieve mapping definition of index or index/type index @@ -873,7 +1013,7 @@ def mapping_get(index, doc_type, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.mapping_get testindex user - ''' + """ es = _get_instance(hosts, profile) try: @@ -881,11 +1021,15 @@ def mapping_get(index, doc_type, hosts=None, profile=None): except elasticsearch.exceptions.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve mapping {0}, server returned code {1} with message {2}".format(index, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve mapping {0}, server returned code {1} with message {2}".format( + index, e.status_code, e.error + ) + ) def index_template_create(name, body=None, hosts=None, profile=None, source=None): - ''' + """ Create an index template name @@ -900,24 +1044,28 @@ def index_template_create(name, body=None, hosts=None, profile=None, source=None CLI example:: salt myminion elasticsearch.index_template_create testindex_templ '{ "template": "logstash-*", "order": 1, "settings": { "number_of_shards": 1 } }' - ''' + """ es = _get_instance(hosts, profile) if source and body: - message = 'Either body or source should be specified but not both.' + message = "Either body or source should be specified but not both." raise SaltInvocationError(message) if source: - body = __salt__['cp.get_file_str']( - source, - saltenv=__opts__.get('saltenv', 'base')) + body = __salt__["cp.get_file_str"]( + source, saltenv=__opts__.get("saltenv", "base") + ) try: result = es.indices.put_template(name=name, body=body) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot create template {0}, server returned code {1} with message {2}".format(name, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create template {0}, server returned code {1} with message {2}".format( + name, e.status_code, e.error + ) + ) def index_template_delete(name, hosts=None, profile=None): - ''' + """ Delete an index template (type) along with its data name @@ -926,20 +1074,24 @@ def index_template_delete(name, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.index_template_delete testindex_templ user - ''' + """ es = _get_instance(hosts, profile) try: result = es.indices.delete_template(name=name) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.exceptions.NotFoundError: return True except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot delete template {0}, server returned code {1} with message {2}".format(name, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot delete template {0}, server returned code {1} with message {2}".format( + name, e.status_code, e.error + ) + ) def index_template_exists(name, hosts=None, profile=None): - ''' + """ Return a boolean indicating whether given index template exists name @@ -948,16 +1100,20 @@ def index_template_exists(name, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.index_template_exists testindex_templ - ''' + """ es = _get_instance(hosts, profile) try: return es.indices.exists_template(name=name) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve template {0}, server returned code {1} with message {2}".format(name, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve template {0}, server returned code {1} with message {2}".format( + name, e.status_code, e.error + ) + ) def index_template_get(name, hosts=None, profile=None): - ''' + """ Retrieve template definition of index or index/type name @@ -966,7 +1122,7 @@ def index_template_get(name, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.index_template_get testindex_templ - ''' + """ es = _get_instance(hosts, profile) try: @@ -974,11 +1130,15 @@ def index_template_get(name, hosts=None, profile=None): except elasticsearch.exceptions.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot retrieve template {0}, server returned code {1} with message {2}".format(name, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot retrieve template {0}, server returned code {1} with message {2}".format( + name, e.status_code, e.error + ) + ) def pipeline_get(id, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Retrieve Ingest pipeline definition. Available since Elasticsearch 5.0. @@ -989,7 +1149,7 @@ def pipeline_get(id, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.pipeline_get mypipeline - ''' + """ es = _get_instance(hosts, profile) try: @@ -997,13 +1157,17 @@ def pipeline_get(id, hosts=None, profile=None): except elasticsearch.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot create pipeline {0}, server returned code {1} with message {2}".format(id, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create pipeline {0}, server returned code {1} with message {2}".format( + id, e.status_code, e.error + ) + ) except AttributeError: raise CommandExecutionError("Method is applicable only for Elasticsearch 5.0+") def pipeline_delete(id, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Delete Ingest pipeline. Available since Elasticsearch 5.0. @@ -1014,22 +1178,26 @@ def pipeline_delete(id, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.pipeline_delete mypipeline - ''' + """ es = _get_instance(hosts, profile) try: ret = es.ingest.delete_pipeline(id=id) - return ret.get('acknowledged', False) + return ret.get("acknowledged", False) except elasticsearch.NotFoundError: return True except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot delete pipeline {0}, server returned code {1} with message {2}".format(id, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot delete pipeline {0}, server returned code {1} with message {2}".format( + id, e.status_code, e.error + ) + ) except AttributeError: raise CommandExecutionError("Method is applicable only for Elasticsearch 5.0+") def pipeline_create(id, body, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Create Ingest pipeline by supplied definition. Available since Elasticsearch 5.0. @@ -1042,19 +1210,23 @@ def pipeline_create(id, body, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.pipeline_create mypipeline '{"description": "my custom pipeline", "processors": [{"set" : {"field": "collector_timestamp_millis", "value": "{{_ingest.timestamp}}"}}]}' - ''' + """ es = _get_instance(hosts, profile) try: out = es.ingest.put_pipeline(id=id, body=body) - return out.get('acknowledged', False) + return out.get("acknowledged", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot create pipeline {0}, server returned code {1} with message {2}".format(id, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create pipeline {0}, server returned code {1} with message {2}".format( + id, e.status_code, e.error + ) + ) except AttributeError: raise CommandExecutionError("Method is applicable only for Elasticsearch 5.0+") def pipeline_simulate(id, body, verbose=False, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Simulate existing Ingest pipeline on provided data. Available since Elasticsearch 5.0. @@ -1069,18 +1241,22 @@ def pipeline_simulate(id, body, verbose=False, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.pipeline_simulate mypipeline '{"docs":[{"_index":"index","_type":"type","_id":"id","_source":{"foo":"bar"}},{"_index":"index","_type":"type","_id":"id","_source":{"foo":"rab"}}]}' verbose=True - ''' + """ es = _get_instance(hosts, profile) try: return es.ingest.simulate(id=id, body=body, verbose=verbose) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot simulate pipeline {0}, server returned code {1} with message {2}".format(id, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot simulate pipeline {0}, server returned code {1} with message {2}".format( + id, e.status_code, e.error + ) + ) except AttributeError: raise CommandExecutionError("Method is applicable only for Elasticsearch 5.0+") def search_template_get(id, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Obtain existing search template definition. @@ -1091,7 +1267,7 @@ def search_template_get(id, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.search_template_get mytemplate - ''' + """ es = _get_instance(hosts, profile) try: @@ -1099,11 +1275,15 @@ def search_template_get(id, hosts=None, profile=None): except elasticsearch.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot obtain search template {0}, server returned code {1} with message {2}".format(id, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot obtain search template {0}, server returned code {1} with message {2}".format( + id, e.status_code, e.error + ) + ) def search_template_create(id, body, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Create search template by supplied definition @@ -1116,19 +1296,23 @@ def search_template_create(id, body, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.search_template_create mytemplate '{"template":{"query":{"match":{"title":"{{query_string}}"}}}}' - ''' + """ es = _get_instance(hosts, profile) try: result = es.put_template(id=id, body=body) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot create search template {0}, server returned code {1} with message {2}".format(id, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create search template {0}, server returned code {1} with message {2}".format( + id, e.status_code, e.error + ) + ) def search_template_delete(id, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Delete existing search template definition. @@ -1139,21 +1323,25 @@ def search_template_delete(id, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.search_template_delete mytemplate - ''' + """ es = _get_instance(hosts, profile) try: result = es.delete_template(id=id) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.NotFoundError: return True except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot delete search template {0}, server returned code {1} with message {2}".format(id, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot delete search template {0}, server returned code {1} with message {2}".format( + id, e.status_code, e.error + ) + ) def repository_get(name, local=False, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Get existing repository details. @@ -1166,7 +1354,7 @@ def repository_get(name, local=False, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.repository_get testrepo - ''' + """ es = _get_instance(hosts, profile) try: @@ -1174,11 +1362,15 @@ def repository_get(name, local=False, hosts=None, profile=None): except elasticsearch.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot obtain repository {0}, server returned code {1} with message {2}".format(name, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot obtain repository {0}, server returned code {1} with message {2}".format( + name, e.status_code, e.error + ) + ) def repository_create(name, body, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Create repository for storing snapshots. Note that shared repository paths have to be specified in path.repo Elasticsearch configuration option. @@ -1191,19 +1383,23 @@ def repository_create(name, body, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.repository_create testrepo '{"type":"fs","settings":{"location":"/tmp/test","compress":true}}' - ''' + """ es = _get_instance(hosts, profile) try: result = es.snapshot.create_repository(repository=name, body=body) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot create repository {0}, server returned code {1} with message {2}".format(name, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create repository {0}, server returned code {1} with message {2}".format( + name, e.status_code, e.error + ) + ) def repository_delete(name, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Delete existing repository. @@ -1214,21 +1410,25 @@ def repository_delete(name, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.repository_delete testrepo - ''' + """ es = _get_instance(hosts, profile) try: result = es.snapshot.delete_repository(repository=name) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.NotFoundError: return True except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot delete repository {0}, server returned code {1} with message {2}".format(name, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot delete repository {0}, server returned code {1} with message {2}".format( + name, e.status_code, e.error + ) + ) def repository_verify(name, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Obtain list of cluster nodes which successfully verified this repository. @@ -1239,7 +1439,7 @@ def repository_verify(name, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.repository_verify testrepo - ''' + """ es = _get_instance(hosts, profile) try: @@ -1247,11 +1447,17 @@ def repository_verify(name, hosts=None, profile=None): except elasticsearch.NotFoundError: return None except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot verify repository {0}, server returned code {1} with message {2}".format(name, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot verify repository {0}, server returned code {1} with message {2}".format( + name, e.status_code, e.error + ) + ) -def snapshot_status(repository=None, snapshot=None, ignore_unavailable=False, hosts=None, profile=None): - ''' +def snapshot_status( + repository=None, snapshot=None, ignore_unavailable=False, hosts=None, profile=None +): + """ .. versionadded:: 2017.7.0 Obtain status of all currently running snapshots. @@ -1266,17 +1472,27 @@ def snapshot_status(repository=None, snapshot=None, ignore_unavailable=False, ho CLI example:: salt myminion elasticsearch.snapshot_status ignore_unavailable=True - ''' + """ es = _get_instance(hosts, profile) try: - return es.snapshot.status(repository=repository, snapshot=snapshot, ignore_unavailable=ignore_unavailable) + return es.snapshot.status( + repository=repository, + snapshot=snapshot, + ignore_unavailable=ignore_unavailable, + ) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot obtain snapshot status, server returned code {0} with message {1}".format(e.status_code, e.error)) + raise CommandExecutionError( + "Cannot obtain snapshot status, server returned code {0} with message {1}".format( + e.status_code, e.error + ) + ) -def snapshot_get(repository, snapshot, ignore_unavailable=False, hosts=None, profile=None): - ''' +def snapshot_get( + repository, snapshot, ignore_unavailable=False, hosts=None, profile=None +): + """ .. versionadded:: 2017.7.0 Obtain snapshot residing in specified repository. @@ -1291,17 +1507,25 @@ def snapshot_get(repository, snapshot, ignore_unavailable=False, hosts=None, pro CLI example:: salt myminion elasticsearch.snapshot_get testrepo testsnapshot - ''' + """ es = _get_instance(hosts, profile) try: - return es.snapshot.get(repository=repository, snapshot=snapshot, ignore_unavailable=ignore_unavailable) + return es.snapshot.get( + repository=repository, + snapshot=snapshot, + ignore_unavailable=ignore_unavailable, + ) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot obtain details of snapshot {0} in repository {1}, server returned code {2} with message {3}".format(snapshot, repository, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot obtain details of snapshot {0} in repository {1}, server returned code {2} with message {3}".format( + snapshot, repository, e.status_code, e.error + ) + ) def snapshot_create(repository, snapshot, body=None, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Create snapshot in specified repository by supplied definition. @@ -1316,19 +1540,25 @@ def snapshot_create(repository, snapshot, body=None, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.snapshot_create testrepo testsnapshot '{"indices":"index_1,index_2","ignore_unavailable":true,"include_global_state":false}' - ''' + """ es = _get_instance(hosts, profile) try: - response = es.snapshot.create(repository=repository, snapshot=snapshot, body=body) + response = es.snapshot.create( + repository=repository, snapshot=snapshot, body=body + ) - return response.get('accepted', False) + return response.get("accepted", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot create snapshot {0} in repository {1}, server returned code {2} with message {3}".format(snapshot, repository, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot create snapshot {0} in repository {1}, server returned code {2} with message {3}".format( + snapshot, repository, e.status_code, e.error + ) + ) def snapshot_restore(repository, snapshot, body=None, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Restore existing snapshot in specified repository by supplied definition. @@ -1343,19 +1573,25 @@ def snapshot_restore(repository, snapshot, body=None, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.snapshot_restore testrepo testsnapshot '{"indices":"index_1,index_2","ignore_unavailable":true,"include_global_state":true}' - ''' + """ es = _get_instance(hosts, profile) try: - response = es.snapshot.restore(repository=repository, snapshot=snapshot, body=body) + response = es.snapshot.restore( + repository=repository, snapshot=snapshot, body=body + ) - return response.get('accepted', False) + return response.get("accepted", False) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot restore snapshot {0} in repository {1}, server returned code {2} with message {3}".format(snapshot, repository, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot restore snapshot {0} in repository {1}, server returned code {2} with message {3}".format( + snapshot, repository, e.status_code, e.error + ) + ) def snapshot_delete(repository, snapshot, hosts=None, profile=None): - ''' + """ .. versionadded:: 2017.7.0 Delete snapshot from specified repository. @@ -1368,21 +1604,25 @@ def snapshot_delete(repository, snapshot, hosts=None, profile=None): CLI example:: salt myminion elasticsearch.snapshot_delete testrepo testsnapshot - ''' + """ es = _get_instance(hosts, profile) try: result = es.snapshot.delete(repository=repository, snapshot=snapshot) - return result.get('acknowledged', False) + return result.get("acknowledged", False) except elasticsearch.NotFoundError: return True except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot delete snapshot {0} from repository {1}, server returned code {2} with message {3}".format(snapshot, repository, e.status_code, e.error)) + raise CommandExecutionError( + "Cannot delete snapshot {0} from repository {1}, server returned code {2} with message {3}".format( + snapshot, repository, e.status_code, e.error + ) + ) def flush_synced(hosts=None, profile=None, **kwargs): - ''' + """ .. versionadded:: 3000 Perform a normal flush, then add a generated unique marker (sync_id) to all shards. @@ -1413,10 +1653,14 @@ def flush_synced(hosts=None, profile=None, **kwargs): CLI example:: salt myminion elasticsearch.flush_synced index='index1,index2' ignore_unavailable=True allow_no_indices=True expand_wildcards='all' - ''' + """ es = _get_instance(hosts, profile) try: return es.indices.flush_synced(kwargs) except elasticsearch.TransportError as e: - raise CommandExecutionError("Cannot flush synced, server returned code {} with message {}".format(e.status_code, e.error)) + raise CommandExecutionError( + "Cannot flush synced, server returned code {} with message {}".format( + e.status_code, e.error + ) + ) diff --git a/salt/modules/environ.py b/salt/modules/environ.py index d7eebaa96eb..15858728ef1 100644 --- a/salt/modules/environ.py +++ b/salt/modules/environ.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Support for getting and setting the environment variables of the current salt process. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import logging +import os # Import Salt libs import salt.utils.platform @@ -18,14 +19,14 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ No dependency checks, and not renaming, just return True - ''' + """ return True def setval(key, val, false_unsets=False, permanent=False): - ''' + """ Set a single salt process environment variable. Returns True on success. @@ -59,60 +60,60 @@ def setval(key, val, false_unsets=False, permanent=False): salt '*' environ.setval baz val=False false_unsets=True salt '*' environ.setval baz bar permanent=True salt '*' environ.setval baz bar permanent=HKLM - ''' + """ is_windows = salt.utils.platform.is_windows() if is_windows: - permanent_hive = 'HKCU' - permanent_key = 'Environment' - if permanent == 'HKLM': - permanent_hive = 'HKLM' - permanent_key = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + permanent_hive = "HKCU" + permanent_key = "Environment" + if permanent == "HKLM": + permanent_hive = "HKLM" + permanent_key = ( + r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" + ) if not isinstance(key, six.string_types): log.debug( - '{0}: \'key\' argument is not a string type: \'{1}\'' - .format(__name__, key) + "{0}: 'key' argument is not a string type: '{1}'".format(__name__, key) ) if val is False: if false_unsets is True: try: os.environ.pop(key, None) if permanent and is_windows: - __salt__['reg.delete_value'](permanent_hive, permanent_key, key) + __salt__["reg.delete_value"](permanent_hive, permanent_key, key) return None except Exception as exc: # pylint: disable=broad-except log.error( - '{0}: Exception occurred when unsetting ' - 'environ key \'{1}\': \'{2}\'' - .format(__name__, key, exc) + "{0}: Exception occurred when unsetting " + "environ key '{1}': '{2}'".format(__name__, key, exc) ) return False else: - val = '' + val = "" if isinstance(val, six.string_types): try: os.environ[key] = val if permanent and is_windows: - __salt__['reg.set_value'](permanent_hive, permanent_key, key, val) + __salt__["reg.set_value"](permanent_hive, permanent_key, key, val) return os.environ[key] except Exception as exc: # pylint: disable=broad-except log.error( - '{0}: Exception occurred when setting' - 'environ key \'{1}\': \'{2}\'' - .format(__name__, key, exc) + "{0}: Exception occurred when setting" + "environ key '{1}': '{2}'".format(__name__, key, exc) ) return False else: log.debug( - '{0}: \'val\' argument for key \'{1}\' is not a string ' - 'or False: \'{2}\'' - .format(__name__, key, val) + "{0}: 'val' argument for key '{1}' is not a string " + "or False: '{2}'".format(__name__, key, val) ) return False -def setenv(environ, false_unsets=False, clear_all=False, update_minion=False, permanent=False): - ''' +def setenv( + environ, false_unsets=False, clear_all=False, update_minion=False, permanent=False +): + """ Set multiple salt process environment variables from a dict. Returns a dict. @@ -157,12 +158,11 @@ def setenv(environ, false_unsets=False, clear_all=False, update_minion=False, pe salt '*' environ.setenv '{"foo": "bar", "baz": "quux"}' salt '*' environ.setenv '{"a": "b", "c": False}' false_unsets=True - ''' + """ ret = {} if not isinstance(environ, dict): log.debug( - '{0}: \'environ\' argument is not a dict: \'{1}\'' - .format(__name__, environ) + "{0}: 'environ' argument is not a dict: '{1}'".format(__name__, environ) ) return False if clear_all is True: @@ -177,24 +177,26 @@ def setenv(environ, false_unsets=False, clear_all=False, update_minion=False, pe ret[key] = setval(key, val, false_unsets, permanent=permanent) else: log.debug( - '{0}: \'val\' argument for key \'{1}\' is not a string ' - 'or False: \'{2}\'' - .format(__name__, key, val) + "{0}: 'val' argument for key '{1}' is not a string " + "or False: '{2}'".format(__name__, key, val) ) return False if update_minion is True: - __salt__['event.fire']({'environ': environ, - 'false_unsets': false_unsets, - 'clear_all': clear_all, - 'permanent': permanent - }, - 'environ_setenv') + __salt__["event.fire"]( + { + "environ": environ, + "false_unsets": false_unsets, + "clear_all": clear_all, + "permanent": permanent, + }, + "environ_setenv", + ) return ret -def get(key, default=''): - ''' +def get(key, default=""): + """ Get a single salt process environment variable. key @@ -211,18 +213,17 @@ def get(key, default=''): salt '*' environ.get foo salt '*' environ.get baz default=False - ''' + """ if not isinstance(key, six.string_types): log.debug( - '{0}: \'key\' argument is not a string type: \'{1}\'' - .format(__name__, key) + "{0}: 'key' argument is not a string type: '{1}'".format(__name__, key) ) return False return os.environ.get(key, default) def has_value(key, value=None): - ''' + """ Determine whether the key exists in the current salt process environment dictionary. Optionally compare the current value of the environment against the supplied value string. @@ -239,11 +240,10 @@ def has_value(key, value=None): .. code-block:: bash salt '*' environ.has_value foo - ''' + """ if not isinstance(key, six.string_types): log.debug( - '{0}: \'key\' argument is not a string type: \'{1}\'' - .format(__name__, key) + "{0}: 'key' argument is not a string type: '{1}'".format(__name__, key) ) return False try: @@ -258,8 +258,8 @@ def has_value(key, value=None): return True -def item(keys, default=''): - ''' +def item(keys, default=""): + """ Get one or more salt process environment variables. Returns a dict. @@ -277,7 +277,7 @@ def item(keys, default=''): salt '*' environ.item foo salt '*' environ.item '[foo, baz]' default=None - ''' + """ ret = {} key_list = [] if isinstance(keys, six.string_types): @@ -286,8 +286,9 @@ def item(keys, default=''): key_list = keys else: log.debug( - '{0}: \'keys\' argument is not a string or list type: \'{1}\'' - .format(__name__, keys) + "{0}: 'keys' argument is not a string or list type: '{1}'".format( + __name__, keys + ) ) for key in key_list: ret[key] = os.environ.get(key, default) @@ -295,7 +296,7 @@ def item(keys, default=''): def items(): - ''' + """ Return a dict of the entire environment set for the salt process CLI Example: @@ -303,5 +304,5 @@ def items(): .. code-block:: bash salt '*' environ.items - ''' + """ return dict(os.environ) diff --git a/salt/modules/eselect.py b/salt/modules/eselect.py index 824408bfdb9..3a58377452a 100644 --- a/salt/modules/eselect.py +++ b/salt/modules/eselect.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Support for eselect, Gentoo's configuration and management tool. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -14,16 +15,21 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work on Gentoo systems with eselect installed - ''' - if __grains__['os'] == 'Gentoo' and salt.utils.path.which('eselect'): - return 'eselect' - return (False, 'The eselect execution module cannot be loaded: either the system is not Gentoo or the eselect binary is not in the path.') + """ + if __grains__["os"] == "Gentoo" and salt.utils.path.which("eselect"): + return "eselect" + return ( + False, + "The eselect execution module cannot be loaded: either the system is not Gentoo or the eselect binary is not in the path.", + ) -def exec_action(module, action, module_parameter=None, action_parameter=None, state_only=False): - ''' +def exec_action( + module, action, module_parameter=None, action_parameter=None, state_only=False +): + """ Execute an arbitrary action on a module. module @@ -46,15 +52,16 @@ def exec_action(module, action, module_parameter=None, action_parameter=None, st .. code-block:: bash salt '*' eselect.exec_action php update action_parameter='apache2' - ''' - out = __salt__['cmd.run']( - 'eselect --brief --colour=no {0} {1} {2} {3}'.format( - module, module_parameter or '', action, action_parameter or ''), - python_shell=False + """ + out = __salt__["cmd.run"]( + "eselect --brief --colour=no {0} {1} {2} {3}".format( + module, module_parameter or "", action, action_parameter or "" + ), + python_shell=False, ) - out = out.strip().split('\n') + out = out.strip().split("\n") - if out[0].startswith('!!! Error'): + if out[0].startswith("!!! Error"): return False if state_only: @@ -70,7 +77,7 @@ def exec_action(module, action, module_parameter=None, action_parameter=None, st def get_modules(): - ''' + """ List available ``eselect`` modules. CLI Example: @@ -78,20 +85,20 @@ def get_modules(): .. code-block:: bash salt '*' eselect.get_modules - ''' + """ modules = [] - module_list = exec_action('modules', 'list', action_parameter='--only-names') + module_list = exec_action("modules", "list", action_parameter="--only-names") if not module_list: return None for module in module_list: - if module not in ['help', 'usage', 'version']: + if module not in ["help", "usage", "version"]: modules.append(module) return modules def get_target_list(module, action_parameter=None): - ''' + """ List available targets for the given module. module @@ -107,8 +114,8 @@ def get_target_list(module, action_parameter=None): .. code-block:: bash salt '*' eselect.get_target_list kernel - ''' - exec_output = exec_action(module, 'list', action_parameter=action_parameter) + """ + exec_output = exec_action(module, "list", action_parameter=action_parameter) if not exec_output: return None @@ -122,7 +129,7 @@ def get_target_list(module, action_parameter=None): def get_current_target(module, module_parameter=None, action_parameter=None): - ''' + """ Get the currently selected target for the given module. module @@ -145,19 +152,24 @@ def get_current_target(module, module_parameter=None, action_parameter=None): .. code-block:: bash salt '*' eselect.get_current_target kernel - ''' - result = exec_action(module, 'show', module_parameter=module_parameter, action_parameter=action_parameter)[0] + """ + result = exec_action( + module, + "show", + module_parameter=module_parameter, + action_parameter=action_parameter, + )[0] if not result: return None - if result == '(unset)': + if result == "(unset)": return None return result def set_target(module, target, module_parameter=None, action_parameter=None): - ''' + """ Set the target for the given module. Target can be specified by index or name. @@ -184,18 +196,24 @@ def set_target(module, target, module_parameter=None, action_parameter=None): .. code-block:: bash salt '*' eselect.set_target kernel linux-3.17.5-gentoo - ''' + """ if action_parameter: - action_parameter = '{0} {1}'.format(action_parameter, target) + action_parameter = "{0} {1}".format(action_parameter, target) else: action_parameter = target # get list of available modules if module not in get_modules(): - log.error('Module {0} not available'.format(module)) + log.error("Module {0} not available".format(module)) return False - exec_result = exec_action(module, 'set', module_parameter=module_parameter, action_parameter=action_parameter, state_only=True) + exec_result = exec_action( + module, + "set", + module_parameter=module_parameter, + action_parameter=action_parameter, + state_only=True, + ) if exec_result: return exec_result return False diff --git a/salt/modules/esxcluster.py b/salt/modules/esxcluster.py index 913f30524f1..14e36f975f7 100644 --- a/salt/modules/esxcluster.py +++ b/salt/modules/esxcluster.py @@ -1,29 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Module used to access the esxcluster proxy connection methods -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -import salt.utils.platform +import salt.utils.platform log = logging.getLogger(__name__) -__proxyenabled__ = ['esxcluster'] +__proxyenabled__ = ["esxcluster"] # Define the module's virtual name -__virtualname__ = 'esxcluster' +__virtualname__ = "esxcluster" def __virtual__(): - ''' + """ Only work on proxy - ''' + """ if salt.utils.platform.is_proxy(): return __virtualname__ - return (False, 'Must be run on a proxy minion') + return (False, "Must be run on a proxy minion") def get_details(): - return __proxy__['esxcluster.get_details']() + return __proxy__["esxcluster.get_details"]() diff --git a/salt/modules/esxdatacenter.py b/salt/modules/esxdatacenter.py index 1a3501e748a..985c23135a9 100644 --- a/salt/modules/esxdatacenter.py +++ b/salt/modules/esxdatacenter.py @@ -1,29 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Module used to access the esxdatacenter proxy connection methods -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -import salt.utils.platform +import salt.utils.platform log = logging.getLogger(__name__) -__proxyenabled__ = ['esxdatacenter'] +__proxyenabled__ = ["esxdatacenter"] # Define the module's virtual name -__virtualname__ = 'esxdatacenter' +__virtualname__ = "esxdatacenter" def __virtual__(): - ''' + """ Only work on proxy - ''' + """ if salt.utils.platform.is_proxy(): return __virtualname__ - return (False, 'Must be run on a proxy minion') + return (False, "Must be run on a proxy minion") def get_details(): - return __proxy__['esxdatacenter.get_details']() + return __proxy__["esxdatacenter.get_details"]() diff --git a/salt/modules/esxi.py b/salt/modules/esxi.py index 072fa56c834..2473b51d998 100644 --- a/salt/modules/esxi.py +++ b/salt/modules/esxi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Glues the VMware vSphere Execution Module to the VMware ESXi Proxy Minions to the :mod:`esxi proxymodule `. @@ -25,38 +25,40 @@ type manor. salt 'esxi-proxy' esxi.cmd system_info salt 'exsi-proxy' esxi.cmd get_service_policy service_name='ssh' -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.platform - log = logging.getLogger(__name__) -__proxyenabled__ = ['esxi'] -__virtualname__ = 'esxi' +__proxyenabled__ = ["esxi"] +__virtualname__ = "esxi" def __virtual__(): - ''' + """ Only work on proxy - ''' + """ if salt.utils.platform.is_proxy(): return __virtualname__ - return (False, 'The esxi execution module failed to load: ' - 'only available on proxy minions.') + return ( + False, + "The esxi execution module failed to load: only available on proxy minions.", + ) def cmd(command, *args, **kwargs): - proxy_prefix = __opts__['proxy']['proxytype'] - proxy_cmd = proxy_prefix + '.ch_config' + proxy_prefix = __opts__["proxy"]["proxytype"] + proxy_cmd = proxy_prefix + ".ch_config" return __proxy__[proxy_cmd](command, *args, **kwargs) def get_details(): - return __proxy__['esxi.get_details']() + return __proxy__["esxi.get_details"]() diff --git a/salt/modules/esxvm.py b/salt/modules/esxvm.py index 92eeb609eb2..f242591c1f0 100644 --- a/salt/modules/esxvm.py +++ b/salt/modules/esxvm.py @@ -1,31 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Module used to access the esx proxy connection methods -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.platform - log = logging.getLogger(__name__) -__proxyenabled__ = ['esxvm'] +__proxyenabled__ = ["esxvm"] # Define the module's virtual name -__virtualname__ = 'esxvm' +__virtualname__ = "esxvm" def __virtual__(): - ''' + """ Only work on proxy - ''' + """ if salt.utils.platform.is_proxy(): return __virtualname__ - return False + return (False, "Must be run on a proxy minion") def get_details(): - return __proxy__['esxvm.get_details']() + return __proxy__["esxvm.get_details"]() diff --git a/salt/modules/etcd_mod.py b/salt/modules/etcd_mod.py index 71e249bc878..da506b00df3 100644 --- a/salt/modules/etcd_mod.py +++ b/salt/modules/etcd_mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Execution module to work with etcd :depends: - python-etcd @@ -35,46 +35,46 @@ or clusters are available. as this makes all master configuration settings available in all minion's pillars. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import third party libs try: import salt.utils.etcd_util # pylint: disable=W0611 + HAS_LIBS = True except ImportError: HAS_LIBS = False -__virtualname__ = 'etcd' +__virtualname__ = "etcd" # Set up logging log = logging.getLogger(__name__) # Define a function alias in order not to shadow built-in's -__func_alias__ = { - 'get_': 'get', - 'set_': 'set', - 'rm_': 'rm', - 'ls_': 'ls' -} +__func_alias__ = {"get_": "get", "set_": "set", "rm_": "rm", "ls_": "ls"} def __virtual__(): - ''' + """ Only return if python-etcd is installed - ''' + """ if HAS_LIBS: return __virtualname__ - return (False, 'The etcd_mod execution module cannot be loaded: ' - 'python etcd library not available.') + return ( + False, + "The etcd_mod execution module cannot be loaded: " + "python etcd library not available.", + ) def get_(key, recurse=False, profile=None, **kwargs): - ''' + """ .. versionadded:: 2014.7.0 Get a value from etcd, by direct path. Returns None on failure. @@ -87,8 +87,8 @@ def get_(key, recurse=False, profile=None, **kwargs): salt myminion etcd.get /path/to/key profile=my_etcd_config salt myminion etcd.get /path/to/key recurse=True profile=my_etcd_config salt myminion etcd.get /path/to/key host=127.0.0.1 port=2379 - ''' - client = __utils__['etcd_util.get_conn'](__opts__, profile, **kwargs) + """ + client = __utils__["etcd_util.get_conn"](__opts__, profile, **kwargs) if recurse: return client.tree(key) else: @@ -96,7 +96,7 @@ def get_(key, recurse=False, profile=None, **kwargs): def set_(key, value, profile=None, ttl=None, directory=False, **kwargs): - ''' + """ .. versionadded:: 2014.7.0 Set a key in etcd by direct path. Optionally, create a directory @@ -111,14 +111,14 @@ def set_(key, value, profile=None, ttl=None, directory=False, **kwargs): salt myminion etcd.set /path/to/key value host=127.0.0.1 port=2379 salt myminion etcd.set /path/to/dir '' directory=True salt myminion etcd.set /path/to/key value ttl=5 - ''' + """ - client = __utils__['etcd_util.get_conn'](__opts__, profile, **kwargs) + client = __utils__["etcd_util.get_conn"](__opts__, profile, **kwargs) return client.set(key, value, ttl=ttl, directory=directory) -def update(fields, path='', profile=None, **kwargs): - ''' +def update(fields, path="", profile=None, **kwargs): + """ .. versionadded:: 2016.3.0 Sets a dictionary of values in one call. Useful for large updates @@ -166,13 +166,13 @@ def update(fields, path='', profile=None, **kwargs): salt myminion etcd.update "{'/path/to/key': 'baz', '/another/key': 'bar'}" profile=my_etcd_config salt myminion etcd.update "{'/path/to/key': 'baz', '/another/key': 'bar'}" host=127.0.0.1 port=2379 salt myminion etcd.update "{'/path/to/key': 'baz', '/another/key': 'bar'}" path='/some/root' - ''' - client = __utils__['etcd_util.get_conn'](__opts__, profile, **kwargs) + """ + client = __utils__["etcd_util.get_conn"](__opts__, profile, **kwargs) return client.update(fields, path) def watch(key, recurse=False, profile=None, timeout=0, index=None, **kwargs): - ''' + """ .. versionadded:: 2016.3.0 Makes a best effort to watch for a key or tree change in etcd. @@ -190,14 +190,14 @@ def watch(key, recurse=False, profile=None, timeout=0, index=None, **kwargs): salt myminion etcd.watch /path/to/key timeout=10 salt myminion etcd.watch /patch/to/key profile=my_etcd_config index=10 salt myminion etcd.watch /patch/to/key host=127.0.0.1 port=2379 - ''' + """ - client = __utils__['etcd_util.get_conn'](__opts__, profile, **kwargs) + client = __utils__["etcd_util.get_conn"](__opts__, profile, **kwargs) return client.watch(key, recurse=recurse, timeout=timeout, index=index) -def ls_(path='/', profile=None, **kwargs): - ''' +def ls_(path="/", profile=None, **kwargs): + """ .. versionadded:: 2014.7.0 Return all keys and dirs inside a specific path. Returns an empty dict on @@ -211,13 +211,13 @@ def ls_(path='/', profile=None, **kwargs): salt myminion etcd.ls /path/to/dir/ salt myminion etcd.ls /path/to/dir/ profile=my_etcd_config salt myminion etcd.ls /path/to/dir/ host=127.0.0.1 port=2379 - ''' - client = __utils__['etcd_util.get_conn'](__opts__, profile, **kwargs) + """ + client = __utils__["etcd_util.get_conn"](__opts__, profile, **kwargs) return client.ls(path) def rm_(key, recurse=False, profile=None, **kwargs): - ''' + """ .. versionadded:: 2014.7.0 Delete a key from etcd. Returns True if the key was deleted, False if it was @@ -232,13 +232,13 @@ def rm_(key, recurse=False, profile=None, **kwargs): salt myminion etcd.rm /path/to/key profile=my_etcd_config salt myminion etcd.rm /path/to/key host=127.0.0.1 port=2379 salt myminion etcd.rm /path/to/dir recurse=True profile=my_etcd_config - ''' - client = __utils__['etcd_util.get_conn'](__opts__, profile, **kwargs) + """ + client = __utils__["etcd_util.get_conn"](__opts__, profile, **kwargs) return client.rm(key, recurse=recurse) -def tree(path='/', profile=None, **kwargs): - ''' +def tree(path="/", profile=None, **kwargs): + """ .. versionadded:: 2014.7.0 Recurse through etcd and return all values. Returns None on failure. @@ -252,6 +252,6 @@ def tree(path='/', profile=None, **kwargs): salt myminion etcd.tree profile=my_etcd_config salt myminion etcd.tree host=127.0.0.1 port=2379 salt myminion etcd.tree /path/to/keys profile=my_etcd_config - ''' - client = __utils__['etcd_util.get_conn'](__opts__, profile, **kwargs) + """ + client = __utils__["etcd_util.get_conn"](__opts__, profile, **kwargs) return client.tree(path) diff --git a/salt/modules/ethtool.py b/salt/modules/ethtool.py index e2c9b963651..27a9fb56250 100644 --- a/salt/modules/ethtool.py +++ b/salt/modules/ethtool.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for running ethtool command .. versionadded:: 2016.3.0 @@ -8,15 +8,17 @@ Module for running ethtool command :maturity: new :depends: python-ethtool :platform: linux -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import third party libs try: import ethtool + HAS_ETHTOOL = True except ImportError: HAS_ETHTOOL = False @@ -24,28 +26,28 @@ except ImportError: log = logging.getLogger(__name__) ethtool_coalesce_map = { - 'pkt_rate_high': 'pkt_rate_high', - 'pkt_rate_low': 'pkt_rate_low', - 'sample_interval': 'rate_sample_interval', - 'rx_usecs': 'rx_coalesce_usecs', - 'rx_usecs_high': 'rx_coalesce_usecs_high', - 'rx_usecs_irq': 'rx_coalesce_usecs_irq', - 'rx_usecs_low': 'rx_coalesce_usecs_low', - 'rx_frames': 'rx_max_coalesced_frames', - 'rx_frames_high': 'rx_max_coalesced_frames_high', - 'rx_frames_irg': 'rx_max_coalesced_frames_irq', - 'rx_frames_low': 'rx_max_coalesced_frames_low', - 'stats_block_usecs': 'stats_block_coalesce_usecs', - 'tx_usecs': 'tx_coalesce_usecs', - 'tx_usecs_high': 'tx_coalesce_usecs_high', - 'tx_usecs_irq': 'tx_coalesce_usecs_irq', - 'tx_usecs_low': 'tx_coalesce_usecs_low', - 'tx_frames': 'tx_max_coalesced_frames', - 'tx_frames_high': 'tx_max_coalesced_frames_high', - 'tx_frames_irq': 'tx_max_coalesced_frames_irq', - 'tx_frames_low': 'tx_max_coalesced_frames_low', - 'adaptive_rx': 'use_adaptive_rx_coalesce', - 'adaptive_tx': 'use_adaptive_tx_coalesce', + "pkt_rate_high": "pkt_rate_high", + "pkt_rate_low": "pkt_rate_low", + "sample_interval": "rate_sample_interval", + "rx_usecs": "rx_coalesce_usecs", + "rx_usecs_high": "rx_coalesce_usecs_high", + "rx_usecs_irq": "rx_coalesce_usecs_irq", + "rx_usecs_low": "rx_coalesce_usecs_low", + "rx_frames": "rx_max_coalesced_frames", + "rx_frames_high": "rx_max_coalesced_frames_high", + "rx_frames_irg": "rx_max_coalesced_frames_irq", + "rx_frames_low": "rx_max_coalesced_frames_low", + "stats_block_usecs": "stats_block_coalesce_usecs", + "tx_usecs": "tx_coalesce_usecs", + "tx_usecs_high": "tx_coalesce_usecs_high", + "tx_usecs_irq": "tx_coalesce_usecs_irq", + "tx_usecs_low": "tx_coalesce_usecs_low", + "tx_frames": "tx_max_coalesced_frames", + "tx_frames_high": "tx_max_coalesced_frames_high", + "tx_frames_irq": "tx_max_coalesced_frames_irq", + "tx_frames_low": "tx_max_coalesced_frames_low", + "adaptive_rx": "use_adaptive_rx_coalesce", + "adaptive_tx": "use_adaptive_tx_coalesce", } ethtool_coalesce_remap = {} @@ -53,14 +55,14 @@ for k, v in ethtool_coalesce_map.items(): ethtool_coalesce_remap[v] = k ethtool_ring_map = { - 'rx': 'rx_pending', - 'rx_max': 'rx_max_pending', - 'rx_mini': 'rx_mini_pending', - 'rx_mini_max': 'rx_mini_max_pending', - 'rx_jumbo': 'rx_jumbo_pending', - 'rx_jumbo_max': 'rx_jumbo_max_pending', - 'tx': 'tx_pending', - 'tx_max': 'tx_max_pending', + "rx": "rx_pending", + "rx_max": "rx_max_pending", + "rx_mini": "rx_mini_pending", + "rx_mini_max": "rx_mini_max_pending", + "rx_jumbo": "rx_jumbo_pending", + "rx_jumbo_max": "rx_jumbo_max_pending", + "tx": "tx_pending", + "tx_max": "tx_max_pending", } ethtool_ring_remap = {} @@ -68,22 +70,25 @@ for k, v in ethtool_ring_map.items(): ethtool_ring_remap[v] = k # Define the module's virtual name -__virtualname__ = 'ethtool' +__virtualname__ = "ethtool" def __virtual__(): - ''' + """ Only load this module if python-ethtool is installed - ''' + """ if HAS_ETHTOOL: return __virtualname__ else: - return (False, 'The ethtool module could not be loaded: ethtool ' - 'python libraries not found.') + return ( + False, + "The ethtool module could not be loaded: ethtool " + "python libraries not found.", + ) def show_ring(devname): - ''' + """ Queries the specified network device for rx/tx ring parameter information CLI Example: @@ -91,13 +96,13 @@ def show_ring(devname): .. code-block:: bash salt '*' ethtool.show_ring - ''' + """ try: ring = ethtool.get_ringparam(devname) except IOError: - log.error('Ring parameters not supported on %s', devname) - return 'Not supported' + log.error("Ring parameters not supported on %s", devname) + return "Not supported" ret = {} for key, value in ring.items(): @@ -107,7 +112,7 @@ def show_ring(devname): def show_coalesce(devname): - ''' + """ Queries the specified network device for coalescing information CLI Example: @@ -115,13 +120,13 @@ def show_coalesce(devname): .. code-block:: bash salt '*' ethtool.show_coalesce - ''' + """ try: coalesce = ethtool.get_coalesce(devname) except IOError: - log.error('Interrupt coalescing not supported on %s', devname) - return 'Not supported' + log.error("Interrupt coalescing not supported on %s", devname) + return "Not supported" ret = {} for key, value in coalesce.items(): @@ -131,7 +136,7 @@ def show_coalesce(devname): def show_driver(devname): - ''' + """ Queries the specified network device for associated driver information CLI Example: @@ -139,30 +144,30 @@ def show_driver(devname): .. code-block:: bash salt '*' ethtool.show_driver - ''' + """ try: module = ethtool.get_module(devname) except IOError: - log.error('Driver information not implemented on %s', devname) - return 'Not implemented' + log.error("Driver information not implemented on %s", devname) + return "Not implemented" try: businfo = ethtool.get_businfo(devname) except IOError: - log.error('Bus information no available on %s', devname) - return 'Not available' + log.error("Bus information no available on %s", devname) + return "Not available" ret = { - 'driver': module, - 'bus_info': businfo, + "driver": module, + "bus_info": businfo, } return ret def set_ring(devname, **kwargs): - ''' + """ Changes the rx/tx ring parameters of the specified network device CLI Example: @@ -170,13 +175,13 @@ def set_ring(devname, **kwargs): .. code-block:: bash salt '*' ethtool.set_ring [rx=N] [rx_mini=N] [rx_jumbo=N] [tx=N] - ''' + """ try: ring = ethtool.get_ringparam(devname) except IOError: - log.error('Ring parameters not supported on %s', devname) - return 'Not supported' + log.error("Ring parameters not supported on %s", devname) + return "Not supported" changed = False for param, value in kwargs.items(): @@ -192,12 +197,12 @@ def set_ring(devname, **kwargs): ethtool.set_ringparam(devname, ring) return show_ring(devname) except IOError: - log.error('Invalid ring arguments on %s: %s', devname, ring) - return 'Invalid arguments' + log.error("Invalid ring arguments on %s: %s", devname, ring) + return "Invalid arguments" def set_coalesce(devname, **kwargs): - ''' + """ Changes the coalescing settings of the specified network device CLI Example: @@ -209,13 +214,13 @@ def set_coalesce(devname, **kwargs): [stats_block_usecs=N] [pkt_rate_low=N] [rx_usecs_low=N] [rx_frames_low=N] [tx_usecs_low=N] [tx_frames_low=N] [pkt_rate_high=N] [rx_usecs_high=N] [rx_frames_high=N] [tx_usecs_high=N] [tx_frames_high=N] [sample_interval=N] - ''' + """ try: coalesce = ethtool.get_coalesce(devname) except IOError: - log.error('Interrupt coalescing not supported on %s', devname) - return 'Not supported' + log.error("Interrupt coalescing not supported on %s", devname) + return "Not supported" changed = False for param, value in kwargs.items(): @@ -228,15 +233,17 @@ def set_coalesce(devname, **kwargs): try: if changed: - ethtool.set_coalesce(devname, coalesce) # pylint: disable=too-many-function-args + # pylint: disable=too-many-function-args + ethtool.set_coalesce(devname, coalesce) + # pylint: enable=too-many-function-args return show_coalesce(devname) except IOError: - log.error('Invalid coalesce arguments on %s: %s', devname, coalesce) - return 'Invalid arguments' + log.error("Invalid coalesce arguments on %s: %s", devname, coalesce) + return "Invalid arguments" def show_offload(devname): - ''' + """ Queries the specified network device for the state of protocol offload and other features CLI Example: @@ -244,7 +251,7 @@ def show_offload(devname): .. code-block:: bash salt '*' ethtool.show_offload - ''' + """ try: sg = ethtool.get_sg(devname) and "on" or "off" @@ -267,17 +274,17 @@ def show_offload(devname): gso = "not supported" offload = { - 'scatter_gather': sg, - 'tcp_segmentation_offload': tso, - 'udp_fragmentation_offload': ufo, - 'generic_segmentation_offload': gso, + "scatter_gather": sg, + "tcp_segmentation_offload": tso, + "udp_fragmentation_offload": ufo, + "generic_segmentation_offload": gso, } return offload def set_offload(devname, **kwargs): - ''' + """ Changes the offload parameters and other features of the specified network device CLI Example: @@ -285,14 +292,14 @@ def set_offload(devname, **kwargs): .. code-block:: bash salt '*' ethtool.set_offload tcp_segmentation_offload=on - ''' + """ for param, value in kwargs.items(): - if param == 'tcp_segmentation_offload': + if param == "tcp_segmentation_offload": value = value == "on" and 1 or 0 try: ethtool.set_tso(devname, value) except IOError: - return 'Not supported' + return "Not supported" return show_offload(devname) diff --git a/salt/modules/event.py b/salt/modules/event.py index 7a87abce32e..27164516e7e 100644 --- a/salt/modules/event.py +++ b/salt/modules/event.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Use the :ref:`Salt Event System ` to fire events from the master to the minion and vice-versa. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import collections import logging import os @@ -14,25 +15,25 @@ import traceback # Import salt libs import salt.crypt -import salt.utils.event -import salt.utils.zeromq import salt.payload import salt.transport.client +import salt.utils.event +import salt.utils.zeromq from salt.ext import six -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] log = logging.getLogger(__name__) def _dict_subset(keys, master_dict): - ''' + """ Return a dictionary of only the subset of keys/values specified in keys - ''' + """ return dict([(k, v) for k, v in six.iteritems(master_dict) if k in keys]) def fire_master(data, tag, preload=None): - ''' + """ Fire an event off up to the master server CLI Example: @@ -40,40 +41,45 @@ def fire_master(data, tag, preload=None): .. code-block:: bash salt '*' event.fire_master '{"data":"my event data"}' 'tag' - ''' - if (__opts__.get('local', None) or __opts__.get('file_client', None) == 'local') and not __opts__.get('use_master_when_local', False): + """ + if ( + __opts__.get("local", None) or __opts__.get("file_client", None) == "local" + ) and not __opts__.get("use_master_when_local", False): # We can't send an event if we're in masterless mode - log.warning('Local mode detected. Event with tag %s will NOT be sent.', tag) + log.warning("Local mode detected. Event with tag %s will NOT be sent.", tag) return False - if preload or __opts__.get('__cli') == 'salt-call': + if preload or __opts__.get("__cli") == "salt-call": # If preload is specified, we must send a raw event (this is # slower because it has to independently authenticate) - if 'master_uri' not in __opts__: - __opts__['master_uri'] = 'tcp://{ip}:{port}'.format( - ip=salt.utils.zeromq.ip_bracket(__opts__['interface']), - port=__opts__.get('ret_port', '4506') # TODO, no fallback - ) + if "master_uri" not in __opts__: + __opts__["master_uri"] = "tcp://{ip}:{port}".format( + ip=salt.utils.zeromq.ip_bracket(__opts__["interface"]), + port=__opts__.get("ret_port", "4506"), # TODO, no fallback + ) masters = list() ret = True - if 'master_uri_list' in __opts__: - for master_uri in __opts__['master_uri_list']: + if "master_uri_list" in __opts__: + for master_uri in __opts__["master_uri_list"]: masters.append(master_uri) else: - masters.append(__opts__['master_uri']) + masters.append(__opts__["master_uri"]) auth = salt.crypt.SAuth(__opts__) - load = {'id': __opts__['id'], - 'tag': tag, - 'data': data, - 'tok': auth.gen_token(b'salt'), - 'cmd': '_minion_event'} + load = { + "id": __opts__["id"], + "tag": tag, + "data": data, + "tok": auth.gen_token(b"salt"), + "cmd": "_minion_event", + } if isinstance(preload, dict): load.update(preload) for master in masters: - with salt.transport.client.ReqChannel.factory(__opts__, - master_uri=master) as channel: + with salt.transport.client.ReqChannel.factory( + __opts__, master_uri=master + ) as channel: try: channel.send(load) # channel.send was successful. @@ -87,7 +93,9 @@ def fire_master(data, tag, preload=None): # because it is already authenticated try: return salt.utils.event.MinionEvent(__opts__, listen=False).fire_event( - {'data': data, 'tag': tag, 'events': None, 'pretag': None}, 'fire_master') + {"data": data, "tag": tag, "events": None, "pretag": None}, + "fire_master", + ) except Exception: # pylint: disable=broad-except exc_type, exc_value, exc_traceback = sys.exc_info() lines = traceback.format_exception(exc_type, exc_value, exc_traceback) @@ -96,7 +104,7 @@ def fire_master(data, tag, preload=None): def fire(data, tag): - ''' + """ Fire an event on the local minion event bus. Data must be formed as a dict. CLI Example: @@ -104,13 +112,15 @@ def fire(data, tag): .. code-block:: bash salt '*' event.fire '{"data":"my event data"}' 'tag' - ''' + """ try: - with salt.utils.event.get_event('minion', # was __opts__['id'] - sock_dir=__opts__['sock_dir'], - transport=__opts__['transport'], - opts=__opts__, - listen=False) as event: + with salt.utils.event.get_event( + "minion", # was __opts__['id'] + sock_dir=__opts__["sock_dir"], + transport=__opts__["transport"], + opts=__opts__, + listen=False, + ) as event: return event.fire_event(data, tag) except Exception: # pylint: disable=broad-except exc_type, exc_value, exc_traceback = sys.exc_info() @@ -119,15 +129,17 @@ def fire(data, tag): return False -def send(tag, - data=None, - preload=None, - with_env=False, - with_grains=False, - with_pillar=False, - with_env_opts=False, - **kwargs): - ''' +def send( + tag, + data=None, + preload=None, + with_env=False, + with_grains=False, + with_pillar=False, + with_env_opts=False, + **kwargs +): + """ Send an event to the Salt Master .. versionadded:: 2014.7.0 @@ -196,30 +208,30 @@ def send(tag, sudo -E salt-call event.send myco/jenkins/build/success with_env=[BUILD_ID, BUILD_URL, GIT_BRANCH, GIT_COMMIT] - ''' + """ data_dict = {} if with_env: if isinstance(with_env, list): - data_dict['environ'] = _dict_subset(with_env, dict(os.environ)) + data_dict["environ"] = _dict_subset(with_env, dict(os.environ)) else: - data_dict['environ'] = dict(os.environ) + data_dict["environ"] = dict(os.environ) if with_grains: if isinstance(with_grains, list): - data_dict['grains'] = _dict_subset(with_grains, __grains__) + data_dict["grains"] = _dict_subset(with_grains, __grains__) else: - data_dict['grains'] = __grains__ + data_dict["grains"] = __grains__ if with_pillar: if isinstance(with_pillar, list): - data_dict['pillar'] = _dict_subset(with_pillar, __pillar__) + data_dict["pillar"] = _dict_subset(with_pillar, __pillar__) else: - data_dict['pillar'] = __pillar__ + data_dict["pillar"] = __pillar__ if with_env_opts: - data_dict['saltenv'] = __opts__.get('saltenv', 'base') - data_dict['pillarenv'] = __opts__.get('pillarenv') + data_dict["saltenv"] = __opts__.get("saltenv", "base") + data_dict["pillarenv"] = __opts__.get("pillarenv") if kwargs: data_dict.update(kwargs) @@ -228,7 +240,11 @@ def send(tag, if isinstance(data, collections.Mapping): data_dict.update(data) - if __opts__.get('local') or __opts__.get('file_client') == 'local' or __opts__.get('master_type') == 'disable': + if ( + __opts__.get("local") + or __opts__.get("file_client") == "local" + or __opts__.get("master_type") == "disable" + ): return fire(data_dict, tag) else: return fire_master(data_dict, tag, preload=preload) diff --git a/salt/modules/extfs.py b/salt/modules/extfs.py index e7e40507577..214b9e93713 100644 --- a/salt/modules/extfs.py +++ b/salt/modules/extfs.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing ext2/3/4 file systems -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -14,20 +15,20 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ if salt.utils.platform.is_windows(): return ( False, - 'The extfs execution module cannot be loaded: only available on ' - 'non-Windows systems.' + "The extfs execution module cannot be loaded: only available on " + "non-Windows systems.", ) return True def mkfs(device, fs_type, **kwargs): - ''' + """ Create a file system on the specified device CLI Example: @@ -67,64 +68,66 @@ def mkfs(device, fs_type, **kwargs): See the ``mke2fs(8)`` manpage for a more complete description of these options. - ''' - kwarg_map = {'block_size': 'b', - 'check': 'c', - 'direct': 'D', - 'ext_opts': 'E', - 'fragment_size': 'f', - 'force': 'F', - 'blocks_per_group': 'g', - 'number_of_groups': 'G', - 'bytes_per_inode': 'i', - 'inode_size': 'I', - 'journal': 'j', - 'journal_opts': 'J', - 'blocks_file': 'l', - 'label': 'L', - 'reserved': 'm', - 'last_dir': 'M', - 'test': 'n', - 'number_of_inodes': 'N', - 'creator_os': 'o', - 'opts': 'O', - 'revision': 'r', - 'super': 'S', - 'usage_type': 'T', - 'uuid': 'U'} + """ + kwarg_map = { + "block_size": "b", + "check": "c", + "direct": "D", + "ext_opts": "E", + "fragment_size": "f", + "force": "F", + "blocks_per_group": "g", + "number_of_groups": "G", + "bytes_per_inode": "i", + "inode_size": "I", + "journal": "j", + "journal_opts": "J", + "blocks_file": "l", + "label": "L", + "reserved": "m", + "last_dir": "M", + "test": "n", + "number_of_inodes": "N", + "creator_os": "o", + "opts": "O", + "revision": "r", + "super": "S", + "usage_type": "T", + "uuid": "U", + } - opts = '' + opts = "" for key in kwargs: if key in kwarg_map: opt = kwarg_map[key] - if kwargs[key] == 'True': - opts += '-{0} '.format(opt) + if kwargs[key] == "True": + opts += "-{0} ".format(opt) else: - opts += '-{0} {1} '.format(opt, kwargs[key]) - cmd = 'mke2fs -F -t {0} {1}{2}'.format(fs_type, opts, device) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + opts += "-{0} {1} ".format(opt, kwargs[key]) + cmd = "mke2fs -F -t {0} {1}{2}".format(fs_type, opts, device) + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() ret = [] for line in out: if not line: continue - elif line.startswith('mke2fs'): + elif line.startswith("mke2fs"): continue - elif line.startswith('Discarding device blocks'): + elif line.startswith("Discarding device blocks"): continue - elif line.startswith('Allocating group tables'): + elif line.startswith("Allocating group tables"): continue - elif line.startswith('Writing inode tables'): + elif line.startswith("Writing inode tables"): continue - elif line.startswith('Creating journal'): + elif line.startswith("Creating journal"): continue - elif line.startswith('Writing superblocks'): + elif line.startswith("Writing superblocks"): continue ret.append(line) return ret def tune(device, **kwargs): - ''' + """ Set attributes for the specified device (using tune2fs) CLI Example: @@ -158,41 +161,43 @@ def tune(device, **kwargs): See the ``mke2fs(8)`` manpage for a more complete description of these options. - ''' - kwarg_map = {'max': 'c', - 'count': 'C', - 'error': 'e', - 'extended_opts': 'E', - 'force': 'f', - 'group': 'g', - 'interval': 'i', - 'journal': 'j', - 'journal_opts': 'J', - 'label': 'L', - 'last_dir': 'M', - 'opts': 'o', - 'feature': 'O', - 'mmp_check': 'p', - 'reserved': 'r', - 'quota_opts': 'Q', - 'time': 'T', - 'user': 'u', - 'uuid': 'U'} - opts = '' + """ + kwarg_map = { + "max": "c", + "count": "C", + "error": "e", + "extended_opts": "E", + "force": "f", + "group": "g", + "interval": "i", + "journal": "j", + "journal_opts": "J", + "label": "L", + "last_dir": "M", + "opts": "o", + "feature": "O", + "mmp_check": "p", + "reserved": "r", + "quota_opts": "Q", + "time": "T", + "user": "u", + "uuid": "U", + } + opts = "" for key in kwargs: if key in kwarg_map: opt = kwarg_map[key] - if kwargs[key] == 'True': - opts += '-{0} '.format(opt) + if kwargs[key] == "True": + opts += "-{0} ".format(opt) else: - opts += '-{0} {1} '.format(opt, kwargs[key]) - cmd = 'tune2fs {0}{1}'.format(opts, device) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + opts += "-{0} {1} ".format(opt, kwargs[key]) + cmd = "tune2fs {0}{1}".format(opts, device) + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() return out def attributes(device, args=None): - ''' + """ Return attributes from dumpe2fs for a specified device CLI Example: @@ -200,13 +205,13 @@ def attributes(device, args=None): .. code-block:: bash salt '*' extfs.attributes /dev/sda1 - ''' + """ fsdump = dump(device, args) - return fsdump['attributes'] + return fsdump["attributes"] def blocks(device, args=None): - ''' + """ Return block and inode info from dumpe2fs for a specified device CLI Example: @@ -214,13 +219,13 @@ def blocks(device, args=None): .. code-block:: bash salt '*' extfs.blocks /dev/sda1 - ''' + """ fsdump = dump(device, args) - return fsdump['blocks'] + return fsdump["blocks"] def dump(device, args=None): - ''' + """ Return all contents of dumpe2fs for a specified device CLI Example: @@ -228,58 +233,60 @@ def dump(device, args=None): .. code-block:: bash salt '*' extfs.dump /dev/sda1 - ''' - cmd = 'dumpe2fs {0}'.format(device) + """ + cmd = "dumpe2fs {0}".format(device) if args: - cmd = cmd + ' -' + args - ret = {'attributes': {}, 'blocks': {}} - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - mode = 'opts' + cmd = cmd + " -" + args + ret = {"attributes": {}, "blocks": {}} + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() + mode = "opts" group = None for line in out: if not line: continue - if line.startswith('dumpe2fs'): + if line.startswith("dumpe2fs"): continue - if mode == 'opts': - line = line.replace('\t', ' ') - comps = line.split(': ') - if line.startswith('Filesystem features'): - ret['attributes'][comps[0]] = comps[1].split() - elif line.startswith('Group') and not line.startswith('Group descriptor size'): - mode = 'blocks' + if mode == "opts": + line = line.replace("\t", " ") + comps = line.split(": ") + if line.startswith("Filesystem features"): + ret["attributes"][comps[0]] = comps[1].split() + elif line.startswith("Group") and not line.startswith( + "Group descriptor size" + ): + mode = "blocks" else: if len(comps) < 2: continue - ret['attributes'][comps[0]] = comps[1].strip() + ret["attributes"][comps[0]] = comps[1].strip() - if mode == 'blocks': - if line.startswith('Group'): - line = line.replace(':', '') - line = line.replace('(', '') - line = line.replace(')', '') - line = line.replace('[', '') - line = line.replace(']', '') + if mode == "blocks": + if line.startswith("Group"): + line = line.replace(":", "") + line = line.replace("(", "") + line = line.replace(")", "") + line = line.replace("[", "") + line = line.replace("]", "") comps = line.split() blkgrp = comps[1] - group = 'Group {0}'.format(blkgrp) - ret['blocks'][group] = {} - ret['blocks'][group]['group'] = blkgrp - ret['blocks'][group]['range'] = comps[3] + group = "Group {0}".format(blkgrp) + ret["blocks"][group] = {} + ret["blocks"][group]["group"] = blkgrp + ret["blocks"][group]["range"] = comps[3] # TODO: comps[4:], which may look one one of the following: # ITABLE_ZEROED # INODE_UNINIT, ITABLE_ZEROED # Does anyone know what to call these? - ret['blocks'][group]['extra'] = [] - elif 'Free blocks:' in line: - comps = line.split(': ') - free_blocks = comps[1].split(', ') - ret['blocks'][group]['free blocks'] = free_blocks - elif 'Free inodes:' in line: - comps = line.split(': ') - inodes = comps[1].split(', ') - ret['blocks'][group]['free inodes'] = inodes + ret["blocks"][group]["extra"] = [] + elif "Free blocks:" in line: + comps = line.split(": ") + free_blocks = comps[1].split(", ") + ret["blocks"][group]["free blocks"] = free_blocks + elif "Free inodes:" in line: + comps = line.split(": ") + inodes = comps[1].split(", ") + ret["blocks"][group]["free inodes"] = inodes else: line = line.strip() - ret['blocks'][group]['extra'].append(line) + ret["blocks"][group]["extra"].append(line) return ret diff --git a/salt/modules/file.py b/salt/modules/file.py index b5b70e2d4ce..c9e06373ee4 100644 --- a/salt/modules/file.py +++ b/salt/modules/file.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Manage information about regular files, directories, and special files on the minion, set/read user, group, mode, and data -''' +""" # TODO: We should add the capability to do u+r type operations here # some time in the future @@ -14,9 +14,12 @@ from __future__ import absolute_import, print_function, unicode_literals import datetime import errno import fnmatch +import glob +import hashlib import io import itertools import logging +import mmap import operator import os import re @@ -26,24 +29,13 @@ import string import sys import tempfile import time -import glob -import hashlib -import mmap + +# pylint: disable=no-name-in-module from collections import Iterable, Mapping, namedtuple + +# pylint: enable=no-name-in-module from functools import reduce # pylint: disable=redefined-builtin -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext import six -from salt.ext.six.moves import range, zip -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse -# pylint: enable=import-error,no-name-in-module,redefined-builtin - -try: - import grp - import pwd -except ImportError: - pass - # Import salt libs import salt.utils.args import salt.utils.atomicfile @@ -60,44 +52,58 @@ import salt.utils.stringutils import salt.utils.templates import salt.utils.url import salt.utils.user -import salt.utils.data import salt.utils.versions -from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError, get_error_message as _get_error_message +from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError +from salt.exceptions import get_error_message as _get_error_message + +# pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext import six +from salt.ext.six.moves import range, zip +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse from salt.utils.files import HASHES, HASHES_REVMAP +# pylint: enable=import-error,no-name-in-module,redefined-builtin + +try: + import grp + import pwd +except ImportError: + pass + + log = logging.getLogger(__name__) -__func_alias__ = { - 'makedirs_': 'makedirs' -} +__func_alias__ = {"makedirs_": "makedirs"} -AttrChanges = namedtuple('AttrChanges', 'added,removed') +AttrChanges = namedtuple("AttrChanges", "added,removed") def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ # win_file takes care of windows if salt.utils.platform.is_windows(): return ( False, - 'The file execution module cannot be loaded: only available on ' - 'non-Windows systems - use win_file instead.' + "The file execution module cannot be loaded: only available on " + "non-Windows systems - use win_file instead.", ) return True def __clean_tmp(sfn): - ''' + """ Clean out a template temp file - ''' - if sfn.startswith(os.path.join(tempfile.gettempdir(), - salt.utils.files.TEMPFILE_PREFIX)): + """ + if sfn.startswith( + os.path.join(tempfile.gettempdir(), salt.utils.files.TEMPFILE_PREFIX) + ): # Don't remove if it exists in file_roots (any saltenv) all_roots = itertools.chain.from_iterable( - six.itervalues(__opts__['file_roots'])) + six.itervalues(__opts__["file_roots"]) + ) in_roots = any(sfn.startswith(root) for root in all_roots) # Only clean up files that exist if os.path.exists(sfn) and not in_roots: @@ -105,16 +111,16 @@ def __clean_tmp(sfn): def _error(ret, err_msg): - ''' + """ Common function for setting error information for return dicts - ''' - ret['result'] = False - ret['comment'] = err_msg + """ + ret["result"] = False + ret["comment"] = err_msg return ret def _binary_replace(old, new): - ''' + """ This function does NOT do any diffing, it just checks the old and new files to see if either is binary, and provides an appropriate string noting the difference between the two files. If neither file is binary, an empty @@ -122,29 +128,29 @@ def _binary_replace(old, new): This function should only be run AFTER it has been determined that the files differ. - ''' - old_isbin = not __utils__['files.is_text'](old) - new_isbin = not __utils__['files.is_text'](new) + """ + old_isbin = not __utils__["files.is_text"](old) + new_isbin = not __utils__["files.is_text"](new) if any((old_isbin, new_isbin)): if all((old_isbin, new_isbin)): - return 'Replace binary file' + return "Replace binary file" elif old_isbin: - return 'Replace binary file with text file' + return "Replace binary file with text file" elif new_isbin: - return 'Replace text file with binary file' - return '' + return "Replace text file with binary file" + return "" def _get_bkroot(): - ''' + """ Get the location of the backup dir in the minion cache - ''' + """ # Get the cachedir from the minion config - return os.path.join(__salt__['config.get']('cachedir'), 'file_backup') + return os.path.join(__salt__["config.get"]("cachedir"), "file_backup") def _splitlines_preserving_trailing_newline(str): - ''' + """ Returns a list of the lines in the string, breaking at line boundaries and preserving a trailing newline (if present). @@ -156,17 +162,17 @@ def _splitlines_preserving_trailing_newline(str): lines = str.splitlines() if str.endswith('\n') or str.endswith('\r'): lines.append('') - ''' + """ lines = str.splitlines() - if str.endswith('\n') or str.endswith('\r'): - lines.append('') + if str.endswith("\n") or str.endswith("\r"): + lines.append("") return lines def _chattr_version(): - ''' + """ Return the version of chattr installed - ''' + """ # There's no really *good* way to get the version of chattr installed. # It's part of the e2fsprogs package - we could try to parse the version # from the package manager, but there's no guarantee that it was @@ -177,39 +183,38 @@ def _chattr_version(): # manner. # # See https://unix.stackexchange.com/a/520399/5788 for discussion. - tune2fs = salt.utils.path.which('tune2fs') + tune2fs = salt.utils.path.which("tune2fs") if not tune2fs or salt.utils.platform.is_aix(): return None cmd = [tune2fs] - result = __salt__['cmd.run'](cmd, ignore_retcode=True, python_shell=False) + result = __salt__["cmd.run"](cmd, ignore_retcode=True, python_shell=False) match = re.search( - r'tune2fs (?P[0-9\.]+)', - salt.utils.stringutils.to_str(result), + r"tune2fs (?P[0-9\.]+)", salt.utils.stringutils.to_str(result), ) if match is None: version = None else: - version = match.group('version') + version = match.group("version") return version def _chattr_has_extended_attrs(): - ''' + """ Return ``True`` if chattr supports extended attributes, that is, the version is >1.41.22. Otherwise, ``False`` - ''' + """ ver = _chattr_version() if ver is None: return False - needed_version = salt.utils.versions.LooseVersion('1.41.12') + needed_version = salt.utils.versions.LooseVersion("1.41.12") chattr_version = salt.utils.versions.LooseVersion(ver) return chattr_version > needed_version def gid_to_group(gid): - ''' + """ Convert the group id to the group name on this system gid @@ -220,16 +225,16 @@ def gid_to_group(gid): .. code-block:: bash salt '*' file.gid_to_group 0 - ''' + """ try: gid = int(gid) except ValueError: # This is not an integer, maybe it's already the group name? gid = group_to_gid(gid) - if gid == '': + if gid == "": # Don't even bother to feed it to grp - return '' + return "" try: return grp.getgrgid(gid).gr_name @@ -239,7 +244,7 @@ def gid_to_group(gid): def group_to_gid(group): - ''' + """ Convert the group to the gid on this system group @@ -250,19 +255,19 @@ def group_to_gid(group): .. code-block:: bash salt '*' file.group_to_gid root - ''' + """ if group is None: - return '' + return "" try: if isinstance(group, int): return group return grp.getgrnam(group).gr_gid except KeyError: - return '' + return "" def get_gid(path, follow_symlinks=True): - ''' + """ Return the id of the group that owns a given file path @@ -280,12 +285,14 @@ def get_gid(path, follow_symlinks=True): .. versionchanged:: 0.16.4 ``follow_symlinks`` option added - ''' - return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get('gid', -1) + """ + return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get( + "gid", -1 + ) def get_group(path, follow_symlinks=True): - ''' + """ Return the group that owns a given file path @@ -302,12 +309,14 @@ def get_group(path, follow_symlinks=True): .. versionchanged:: 0.16.4 ``follow_symlinks`` option added - ''' - return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get('group', False) + """ + return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get( + "group", False + ) def uid_to_user(uid): - ''' + """ Convert a uid to a user name uid @@ -318,7 +327,7 @@ def uid_to_user(uid): .. code-block:: bash salt '*' file.uid_to_user 0 - ''' + """ try: return pwd.getpwuid(uid).pw_name except (KeyError, NameError): @@ -327,7 +336,7 @@ def uid_to_user(uid): def user_to_uid(user): - ''' + """ Convert user name to a uid user @@ -338,7 +347,7 @@ def user_to_uid(user): .. code-block:: bash salt '*' file.user_to_uid root - ''' + """ if user is None: user = salt.utils.user.get_user() try: @@ -346,11 +355,11 @@ def user_to_uid(user): return user return pwd.getpwnam(user).pw_uid except KeyError: - return '' + return "" def get_uid(path, follow_symlinks=True): - ''' + """ Return the id of the user that owns a given file path @@ -367,12 +376,14 @@ def get_uid(path, follow_symlinks=True): .. versionchanged:: 0.16.4 ``follow_symlinks`` option added - ''' - return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get('uid', -1) + """ + return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get( + "uid", -1 + ) def get_user(path, follow_symlinks=True): - ''' + """ Return the user that owns a given file path @@ -389,12 +400,14 @@ def get_user(path, follow_symlinks=True): .. versionchanged:: 0.16.4 ``follow_symlinks`` option added - ''' - return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get('user', False) + """ + return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get( + "user", False + ) def get_mode(path, follow_symlinks=True): - ''' + """ Return the mode of a file path @@ -411,12 +424,14 @@ def get_mode(path, follow_symlinks=True): .. versionchanged:: 2014.1.0 ``follow_symlinks`` option added - ''' - return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get('mode', '') + """ + return stats(os.path.expanduser(path), follow_symlinks=follow_symlinks).get( + "mode", "" + ) def set_mode(path, mode): - ''' + """ Set the mode of a file path @@ -430,23 +445,23 @@ def set_mode(path, mode): .. code-block:: bash salt '*' file.set_mode /etc/passwd 0644 - ''' + """ path = os.path.expanduser(path) - mode = six.text_type(mode).lstrip('0Oo') + mode = six.text_type(mode).lstrip("0Oo") if not mode: - mode = '0' + mode = "0" if not os.path.exists(path): - raise CommandExecutionError('{0}: File not found'.format(path)) + raise CommandExecutionError("{0}: File not found".format(path)) try: os.chmod(path, int(mode, 8)) except Exception: # pylint: disable=broad-except - return 'Invalid Mode ' + mode + return "Invalid Mode " + mode return get_mode(path) def lchown(path, user, group): - ''' + """ Chown a file, pass the file the desired user and group without following symlinks. @@ -464,20 +479,20 @@ def lchown(path, user, group): .. code-block:: bash salt '*' file.chown /etc/passwd root root - ''' + """ path = os.path.expanduser(path) uid = user_to_uid(user) gid = group_to_gid(group) - err = '' - if uid == '': + err = "" + if uid == "": if user: - err += 'User does not exist\n' + err += "User does not exist\n" else: uid = -1 - if gid == '': + if gid == "": if group: - err += 'Group does not exist\n' + err += "Group does not exist\n" else: gid = -1 @@ -485,7 +500,7 @@ def lchown(path, user, group): def chown(path, user, group): - ''' + """ Chown a file, pass the file the desired user and group path @@ -502,20 +517,20 @@ def chown(path, user, group): .. code-block:: bash salt '*' file.chown /etc/passwd root root - ''' + """ path = os.path.expanduser(path) uid = user_to_uid(user) gid = group_to_gid(group) - err = '' - if uid == '': + err = "" + if uid == "": if user: - err += 'User does not exist\n' + err += "User does not exist\n" else: uid = -1 - if gid == '': + if gid == "": if group: - err += 'Group does not exist\n' + err += "Group does not exist\n" else: gid = -1 if not os.path.exists(path): @@ -524,14 +539,14 @@ def chown(path, user, group): return os.lchown(path, uid, gid) except OSError: pass - err += 'File not found' + err += "File not found" if err: return err return os.chown(path, uid, gid) def chgrp(path, group): - ''' + """ Change the group of a file path @@ -545,7 +560,7 @@ def chgrp(path, group): .. code-block:: bash salt '*' file.chgrp /etc/passwd root - ''' + """ path = os.path.expanduser(path) user = get_user(path) @@ -553,7 +568,7 @@ def chgrp(path, group): def _cmp_attrs(path, attrs): - ''' + """ .. versionadded:: 2018.3.0 Compare attributes of a given file to given attributes. @@ -568,13 +583,13 @@ def _cmp_attrs(path, attrs): attrs string of attributes to compare against a given file - ''' + """ # lsattr for AIX is not the same thing as lsattr for linux. if salt.utils.platform.is_aix(): return None try: - lattrs = lsattr(path).get(path, '') + lattrs = lsattr(path).get(path, "") except AttributeError: # lsattr not installed return None @@ -583,13 +598,12 @@ def _cmp_attrs(path, attrs): old = set(lattrs) return AttrChanges( - added=''.join(new-old) or None, - removed=''.join(old-new) or None, + added="".join(new - old) or None, removed="".join(old - new) or None, ) def lsattr(path): - ''' + """ .. versionadded:: 2018.3.0 .. versionchanged:: 2018.3.1 If ``lsattr`` is not installed on the system, ``None`` is returned. @@ -608,19 +622,19 @@ def lsattr(path): .. code-block:: bash salt '*' file.lsattr foo1.txt - ''' - if not salt.utils.path.which('lsattr') or salt.utils.platform.is_aix(): + """ + if not salt.utils.path.which("lsattr") or salt.utils.platform.is_aix(): return None if not os.path.exists(path): raise SaltInvocationError("File or directory does not exist: " + path) - cmd = ['lsattr', path] - result = __salt__['cmd.run'](cmd, ignore_retcode=True, python_shell=False) + cmd = ["lsattr", path] + result = __salt__["cmd.run"](cmd, ignore_retcode=True, python_shell=False) results = {} for line in result.splitlines(): - if not line.startswith('lsattr: '): + if not line.startswith("lsattr: "): attrs, file = line.split(None, 1) if _chattr_has_extended_attrs(): pattern = r"[aAcCdDeijPsStTu]" @@ -632,7 +646,7 @@ def lsattr(path): def chattr(*files, **kwargs): - ''' + """ .. versionadded:: 2018.3.0 Change the attributes of files. This function accepts one or more files and @@ -659,36 +673,37 @@ def chattr(*files, **kwargs): salt '*' file.chattr foo1.txt foo2.txt operator=add attributes=ai salt '*' file.chattr foo3.txt operator=remove attributes=i version=2 - ''' - operator = kwargs.pop('operator', None) - attributes = kwargs.pop('attributes', None) - flags = kwargs.pop('flags', None) - version = kwargs.pop('version', None) + """ + operator = kwargs.pop("operator", None) + attributes = kwargs.pop("attributes", None) + flags = kwargs.pop("flags", None) + version = kwargs.pop("version", None) - if (operator is None) or (operator not in ('add', 'remove')): + if (operator is None) or (operator not in ("add", "remove")): raise SaltInvocationError( - "Need an operator: 'add' or 'remove' to modify attributes.") + "Need an operator: 'add' or 'remove' to modify attributes." + ) if attributes is None: raise SaltInvocationError("Need attributes: [aAcCdDeijPsStTu]") - cmd = ['chattr'] + cmd = ["chattr"] if operator == "add": - attrs = '+{0}'.format(attributes) + attrs = "+{0}".format(attributes) elif operator == "remove": - attrs = '-{0}'.format(attributes) + attrs = "-{0}".format(attributes) cmd.append(attrs) if flags is not None: - cmd.append('-{0}'.format(flags)) + cmd.append("-{0}".format(flags)) if version is not None: - cmd.extend(['-v', version]) + cmd.extend(["-v", version]) cmd.extend(files) - result = __salt__['cmd.run'](cmd, python_shell=False) + result = __salt__["cmd.run"](cmd, python_shell=False) if bool(result): return False @@ -696,8 +711,8 @@ def chattr(*files, **kwargs): return True -def get_sum(path, form='sha256'): - ''' +def get_sum(path, form="sha256"): + """ Return the checksum for the given file. The following checksum algorithms are supported: @@ -719,16 +734,16 @@ def get_sum(path, form='sha256'): .. code-block:: bash salt '*' file.get_sum /etc/passwd sha512 - ''' + """ path = os.path.expanduser(path) if not os.path.isfile(path): - return 'File not found' + return "File not found" return salt.utils.hashutils.get_hash(path, form, 4096) -def get_hash(path, form='sha256', chunk_size=65536): - ''' +def get_hash(path, form="sha256", chunk_size=65536): + """ Get the hash sum of a file This is better than ``get_sum`` for the following reasons: @@ -751,16 +766,14 @@ def get_hash(path, form='sha256', chunk_size=65536): .. code-block:: bash salt '*' file.get_hash /etc/shadow - ''' + """ return salt.utils.hashutils.get_hash(os.path.expanduser(path), form, chunk_size) -def get_source_sum(file_name='', - source='', - source_hash=None, - source_hash_name=None, - saltenv='base'): - ''' +def get_source_sum( + file_name="", source="", source_hash=None, source_hash_name=None, saltenv="base" +): + """ .. versionadded:: 2016.11.0 Used by :py:func:`file.get_managed ` to @@ -800,23 +813,26 @@ def get_source_sum(file_name='', salt '*' file.get_source_sum /tmp/foo.tar.gz source=http://mydomain.tld/foo.tar.gz source_hash=499ae16dcae71eeb7c3a30c75ea7a1a6 salt '*' file.get_source_sum /tmp/foo.tar.gz source=http://mydomain.tld/foo.tar.gz source_hash=https://mydomain.tld/hashes.md5 salt '*' file.get_source_sum /tmp/foo.tar.gz source=http://mydomain.tld/foo.tar.gz source_hash=https://mydomain.tld/hashes.md5 source_hash_name=./dir2/foo.tar.gz - ''' + """ + def _invalid_source_hash_format(): - ''' + """ DRY helper for reporting invalid source_hash input - ''' + """ raise CommandExecutionError( - 'Source hash {0} format is invalid. The supported formats are: ' - '1) a hash, 2) an expression in the format =, or ' - '3) either a path to a local file containing hashes, or a URI of ' - 'a remote hash file. Supported protocols for remote hash files ' - 'are: {1}. The hash may also not be of a valid length, the ' - 'following are supported hash types and lengths: {2}.'.format( + "Source hash {0} format is invalid. The supported formats are: " + "1) a hash, 2) an expression in the format =, or " + "3) either a path to a local file containing hashes, or a URI of " + "a remote hash file. Supported protocols for remote hash files " + "are: {1}. The hash may also not be of a valid length, the " + "following are supported hash types and lengths: {2}.".format( source_hash, - ', '.join(salt.utils.files.VALID_PROTOS), - ', '.join( - ['{0} ({1})'.format(HASHES_REVMAP[x], x) - for x in sorted(HASHES_REVMAP)] + ", ".join(salt.utils.files.VALID_PROTOS), + ", ".join( + [ + "{0} ({1})".format(HASHES_REVMAP[x], x) + for x in sorted(HASHES_REVMAP) + ] ), ) ) @@ -828,13 +844,13 @@ def get_source_sum(file_name='', try: proto = _urlparse(source_hash).scheme if proto in salt.utils.files.VALID_PROTOS: - hash_fn = __salt__['cp.cache_file'](source_hash, saltenv) + hash_fn = __salt__["cp.cache_file"](source_hash, saltenv) if not hash_fn: raise CommandExecutionError( - 'Source hash file {0} not found'.format(source_hash) + "Source hash file {0} not found".format(source_hash) ) else: - if proto != '': + if proto != "": # Some unsupported protocol (e.g. foo://) is being used. # We'll get into this else block if a hash expression # (like md5=), but in those cases, the @@ -845,59 +861,61 @@ def get_source_sum(file_name='', _invalid_source_hash_format() if hash_fn is not None: - ret = extract_hash(hash_fn, '', file_name, source, source_hash_name) + ret = extract_hash(hash_fn, "", file_name, source, source_hash_name) if ret is None: _invalid_source_hash_format() - ret['hsum'] = ret['hsum'].lower() + ret["hsum"] = ret["hsum"].lower() return ret else: # The source_hash is a hash expression ret = {} try: - ret['hash_type'], ret['hsum'] = \ - [x.strip() for x in source_hash.split('=', 1)] + ret["hash_type"], ret["hsum"] = [ + x.strip() for x in source_hash.split("=", 1) + ] except AttributeError: _invalid_source_hash_format() except ValueError: # No hash type, try to figure out by hash length - if not re.match('^[{0}]+$'.format(string.hexdigits), source_hash): + if not re.match("^[{0}]+$".format(string.hexdigits), source_hash): _invalid_source_hash_format() - ret['hsum'] = source_hash + ret["hsum"] = source_hash source_hash_len = len(source_hash) if source_hash_len in HASHES_REVMAP: - ret['hash_type'] = HASHES_REVMAP[source_hash_len] + ret["hash_type"] = HASHES_REVMAP[source_hash_len] else: _invalid_source_hash_format() - if ret['hash_type'] not in HASHES: + if ret["hash_type"] not in HASHES: raise CommandExecutionError( - 'Invalid hash type \'{0}\'. Supported hash types are: {1}. ' - 'Either remove the hash type and simply use \'{2}\' as the ' - 'source_hash, or change the hash type to a supported type.' - .format(ret['hash_type'], ', '.join(HASHES), ret['hsum']) + "Invalid hash type '{0}'. Supported hash types are: {1}. " + "Either remove the hash type and simply use '{2}' as the " + "source_hash, or change the hash type to a supported type.".format( + ret["hash_type"], ", ".join(HASHES), ret["hsum"] + ) ) else: - hsum_len = len(ret['hsum']) + hsum_len = len(ret["hsum"]) if hsum_len not in HASHES_REVMAP: _invalid_source_hash_format() - elif hsum_len != HASHES[ret['hash_type']]: + elif hsum_len != HASHES[ret["hash_type"]]: raise CommandExecutionError( - 'Invalid length ({0}) for hash type \'{1}\'. Either ' - 'remove the hash type and simply use \'{2}\' as the ' - 'source_hash, or change the hash type to \'{3}\''.format( + "Invalid length ({0}) for hash type '{1}'. Either " + "remove the hash type and simply use '{2}' as the " + "source_hash, or change the hash type to '{3}'".format( hsum_len, - ret['hash_type'], - ret['hsum'], + ret["hash_type"], + ret["hsum"], HASHES_REVMAP[hsum_len], ) ) - ret['hsum'] = ret['hsum'].lower() + ret["hsum"] = ret["hsum"].lower() return ret def check_hash(path, file_hash): - ''' + """ Check if a file matches the given hash string Returns ``True`` if the hash matches, otherwise ``False``. @@ -922,13 +940,13 @@ def check_hash(path, file_hash): salt '*' file.check_hash /etc/fstab e138491e9d5b97023cea823fe17bac22 salt '*' file.check_hash /etc/fstab md5=e138491e9d5b97023cea823fe17bac22 - ''' + """ path = os.path.expanduser(path) if not isinstance(file_hash, six.string_types): - raise SaltInvocationError('hash must be a string') + raise SaltInvocationError("hash must be a string") - for sep in (':', '='): + for sep in (":", "="): if sep in file_hash: hash_type, hash_value = file_hash.split(sep, 1) break @@ -938,14 +956,16 @@ def check_hash(path, file_hash): hash_type = HASHES_REVMAP.get(hash_len) if hash_type is None: raise SaltInvocationError( - 'Hash {0} (length: {1}) could not be matched to a supported ' - 'hash type. The supported hash types and lengths are: ' - '{2}'.format( + "Hash {0} (length: {1}) could not be matched to a supported " + "hash type. The supported hash types and lengths are: " + "{2}".format( file_hash, hash_len, - ', '.join( - ['{0} ({1})'.format(HASHES_REVMAP[x], x) - for x in sorted(HASHES_REVMAP)] + ", ".join( + [ + "{0} ({1})".format(HASHES_REVMAP[x], x) + for x in sorted(HASHES_REVMAP) + ] ), ) ) @@ -954,7 +974,7 @@ def check_hash(path, file_hash): def find(path, *args, **kwargs): - ''' + """ Approximate the Unix ``find(1)`` command and return a list of paths that meet the specified criteria. @@ -1066,26 +1086,30 @@ def find(path, *args, **kwargs): salt '*' file.find / type=f name=\\*.bak size=+10m salt '*' file.find /var mtime=+30d size=+10m print=path,size,mtime salt '*' file.find /var/log name=\\*.[0-9] mtime=+30d size=+10m delete - ''' - if 'delete' in args: - kwargs['delete'] = 'f' - elif 'print' in args: - kwargs['print'] = 'path' + """ + if "delete" in args: + kwargs["delete"] = "f" + elif "print" in args: + kwargs["print"] = "path" try: finder = salt.utils.find.Finder(kwargs) except ValueError as ex: - return 'error: {0}'.format(ex) + return "error: {0}".format(ex) - ret = [item for i in [finder.find(p) for p in glob.glob(os.path.expanduser(path))] for item in i] + ret = [ + item + for i in [finder.find(p) for p in glob.glob(os.path.expanduser(path))] + for item in i + ] ret.sort() return ret def _sed_esc(string, escape_all=False): - ''' + """ Escape single quotes and forward slashes - ''' + """ special_chars = "^.[$()|*+?{" string = string.replace("'", "'\"'\"'").replace("/", "\\/") if escape_all is True: @@ -1094,16 +1118,18 @@ def _sed_esc(string, escape_all=False): return string -def sed(path, - before, - after, - limit='', - backup='.bak', - options='-r -e', - flags='g', - escape_all=False, - negate_match=False): - ''' +def sed( + path, + before, + after, + limit="", + backup=".bak", + options="-r -e", + flags="g", + escape_all=False, + negate_match=False, +): + """ .. deprecated:: 0.17.0 Use :py:func:`~salt.modules.file.replace` instead. @@ -1145,7 +1171,7 @@ def sed(path, .. code-block:: bash salt '*' file.sed /etc/httpd/httpd.conf 'LogLevel warn' 'LogLevel info' - ''' + """ # Largely inspired by Fabric's contrib.files.sed() # XXX:dc: Do we really want to always force escaping? # @@ -1160,31 +1186,28 @@ def sed(path, before = _sed_esc(before, escape_all) after = _sed_esc(after, escape_all) limit = _sed_esc(limit, escape_all) - if sys.platform == 'darwin': - options = options.replace('-r', '-E') + if sys.platform == "darwin": + options = options.replace("-r", "-E") - cmd = ['sed'] - cmd.append('-i{0}'.format(backup) if backup else '-i') + cmd = ["sed"] + cmd.append("-i{0}".format(backup) if backup else "-i") cmd.extend(salt.utils.args.shlex_split(options)) cmd.append( - r'{limit}{negate_match}s/{before}/{after}/{flags}'.format( - limit='/{0}/ '.format(limit) if limit else '', - negate_match='!' if negate_match else '', + r"{limit}{negate_match}s/{before}/{after}/{flags}".format( + limit="/{0}/ ".format(limit) if limit else "", + negate_match="!" if negate_match else "", before=before, after=after, - flags=flags + flags=flags, ) ) cmd.append(path) - return __salt__['cmd.run_all'](cmd, python_shell=False) + return __salt__["cmd.run_all"](cmd, python_shell=False) -def sed_contains(path, - text, - limit='', - flags='g'): - ''' +def sed_contains(path, text, limit="", flags="g"): + """ .. deprecated:: 0.17.0 Use :func:`search` instead. @@ -1198,7 +1221,7 @@ def sed_contains(path, .. code-block:: bash salt '*' file.contains /etc/crontab 'mymaintenance.sh' - ''' + """ # Largely inspired by Fabric's contrib.files.contains() path = os.path.expanduser(path) @@ -1207,35 +1230,37 @@ def sed_contains(path, before = _sed_esc(six.text_type(text), False) limit = _sed_esc(six.text_type(limit), False) - options = '-n -r -e' - if sys.platform == 'darwin': - options = options.replace('-r', '-E') + options = "-n -r -e" + if sys.platform == "darwin": + options = options.replace("-r", "-E") - cmd = ['sed'] + cmd = ["sed"] cmd.extend(salt.utils.args.shlex_split(options)) cmd.append( - r'{limit}s/{before}/$/{flags}'.format( - limit='/{0}/ '.format(limit) if limit else '', + r"{limit}s/{before}/$/{flags}".format( + limit="/{0}/ ".format(limit) if limit else "", before=before, - flags='p{0}'.format(flags) + flags="p{0}".format(flags), ) ) cmd.append(path) - result = __salt__['cmd.run'](cmd, python_shell=False) + result = __salt__["cmd.run"](cmd, python_shell=False) return bool(result) -def psed(path, - before, - after, - limit='', - backup='.bak', - flags='gMS', - escape_all=False, - multi=False): - ''' +def psed( + path, + before, + after, + limit="", + backup=".bak", + flags="gMS", + escape_all=False, + multi=False, +): + """ .. deprecated:: 0.17.0 Use :py:func:`~salt.modules.file.replace` instead. @@ -1281,7 +1306,7 @@ def psed(path, .. code-block:: bash salt '*' file.sed /etc/httpd/httpd.conf 'LogLevel warn' 'LogLevel info' - ''' + """ # Largely inspired by Fabric's contrib.files.sed() # XXX:dc: Do we really want to always force escaping? # @@ -1294,13 +1319,13 @@ def psed(path, after = six.text_type(after) before = _sed_esc(before, escape_all) # The pattern to replace with does not need to be escaped!!! - #after = _sed_esc(after, escape_all) + # after = _sed_esc(after, escape_all) limit = _sed_esc(limit, escape_all) - shutil.copy2(path, '{0}{1}'.format(path, backup)) + shutil.copy2(path, "{0}{1}".format(path, backup)) - with salt.utils.files.fopen(path, 'w') as ofile: - with salt.utils.files.fopen('{0}{1}'.format(path, backup), 'r') as ifile: + with salt.utils.files.fopen(path, "w") as ofile: + with salt.utils.files.fopen("{0}{1}".format(path, backup), "r") as ifile: if multi is True: for line in ifile.readline(): ofile.write( @@ -1310,7 +1335,7 @@ def psed(path, before, after, limit, - flags + flags, ) ) ) @@ -1322,38 +1347,29 @@ def psed(path, before, after, limit, - flags + flags, ) ) ) -RE_FLAG_TABLE = {'I': re.I, - 'L': re.L, - 'M': re.M, - 'S': re.S, - 'U': re.U, - 'X': re.X} +RE_FLAG_TABLE = {"I": re.I, "L": re.L, "M": re.M, "S": re.S, "U": re.U, "X": re.X} -def _psed(text, - before, - after, - limit, - flags): - ''' +def _psed(text, before, after, limit, flags): + """ Does the actual work for file.psed, so that single lines can be passed in - ''' + """ atext = text if limit: limit = re.compile(limit) comps = text.split(limit) - atext = ''.join(comps[1:]) + atext = "".join(comps[1:]) count = 1 - if 'g' in flags: + if "g" in flags: count = 0 - flags = flags.replace('g', '') + flags = flags.replace("g", "") aflags = 0 for flag in flags: @@ -1365,11 +1381,8 @@ def _psed(text, return text -def uncomment(path, - regex, - char='#', - backup='.bak'): - ''' +def uncomment(path, regex, char="#", backup=".bak"): + """ .. deprecated:: 0.17.0 Use :py:func:`~salt.modules.file.replace` instead. @@ -1394,19 +1407,12 @@ def uncomment(path, .. code-block:: bash salt '*' file.uncomment /etc/hosts.deny 'ALL: PARANOID' - ''' - return comment_line(path=path, - regex=regex, - char=char, - cmnt=False, - backup=backup) + """ + return comment_line(path=path, regex=regex, char=char, cmnt=False, backup=backup) -def comment(path, - regex, - char='#', - backup='.bak'): - ''' +def comment(path, regex, char="#", backup=".bak"): + """ .. deprecated:: 0.17.0 Use :py:func:`~salt.modules.file.replace` instead. @@ -1436,20 +1442,12 @@ def comment(path, .. code-block:: bash salt '*' file.comment /etc/modules pcspkr - ''' - return comment_line(path=path, - regex=regex, - char=char, - cmnt=True, - backup=backup) + """ + return comment_line(path=path, regex=regex, char=char, cmnt=True, backup=backup) -def comment_line(path, - regex, - char='#', - cmnt=True, - backup='.bak'): - r''' +def comment_line(path, regex, char="#", cmnt=True, backup=".bak"): + r""" Comment or Uncomment a line in a text file. :param path: string @@ -1493,30 +1491,31 @@ def comment_line(path, .. code-block:: bash salt '*' file.comment_line 'C:\salt\conf\minion' '^log_level: (warning|info|debug)' '#' False '.bk' - ''' + """ # Get the regex for comment or uncomment if cmnt: - regex = '{0}({1}){2}'.format( - '^' if regex.startswith('^') else '', - regex.lstrip('^').rstrip('$'), - '$' if regex.endswith('$') else '') + regex = "{0}({1}){2}".format( + "^" if regex.startswith("^") else "", + regex.lstrip("^").rstrip("$"), + "$" if regex.endswith("$") else "", + ) else: - regex = r'^{0}\s*({1}){2}'.format( - char, - regex.lstrip('^').rstrip('$'), - '$' if regex.endswith('$') else '') + regex = r"^{0}\s*({1}){2}".format( + char, regex.lstrip("^").rstrip("$"), "$" if regex.endswith("$") else "" + ) # Load the real path to the file path = os.path.realpath(os.path.expanduser(path)) # Make sure the file exists if not os.path.isfile(path): - raise SaltInvocationError('File not found: {0}'.format(path)) + raise SaltInvocationError("File not found: {0}".format(path)) # Make sure it is a text file - if not __utils__['files.is_text'](path): + if not __utils__["files.is_text"](path): raise SaltInvocationError( - 'Cannot perform string replacements on a binary file: {0}'.format(path)) + "Cannot perform string replacements on a binary file: {0}".format(path) + ) # First check the whole file, determine whether to make the replacement # Searching first avoids modifying the time stamp if there are no changes @@ -1528,9 +1527,7 @@ def comment_line(path, bufsize = os.path.getsize(path) try: # Use a read-only handle to open the file - with salt.utils.files.fopen(path, - mode='rb', - buffering=bufsize) as r_file: + with salt.utils.files.fopen(path, mode="rb", buffering=bufsize) as r_file: # Loop through each line of the file and look for a match for line in r_file: # Is it in this line @@ -1539,14 +1536,13 @@ def comment_line(path, # Load lines into dictionaries, set found to True orig_file.append(line) if cmnt: - new_file.append('{0}{1}'.format(char, line)) + new_file.append("{0}{1}".format(char, line)) else: new_file.append(line.lstrip(char)) found = True except (OSError, IOError) as exc: raise CommandExecutionError( - "Unable to open file '{0}'. " - "Exception: {1}".format(path, exc) + "Unable to open file '{0}'. " "Exception: {1}".format(path, exc) ) # We've searched the whole file. If we didn't find anything, return False @@ -1566,15 +1562,13 @@ def comment_line(path, try: # Open the file in write mode - mode = 'wb' if six.PY2 and salt.utils.platform.is_windows() else 'w' - with salt.utils.files.fopen(path, - mode=mode, - buffering=bufsize) as w_file: + mode = "wb" if six.PY2 and salt.utils.platform.is_windows() else "w" + with salt.utils.files.fopen(path, mode=mode, buffering=bufsize) as w_file: try: # Open the temp file in read mode - with salt.utils.files.fopen(temp_file, - mode='rb', - buffering=bufsize) as r_file: + with salt.utils.files.fopen( + temp_file, mode="rb", buffering=bufsize + ) as r_file: # Loop through each line of the file and look for a match for line in r_file: line = salt.utils.stringutils.to_unicode(line) @@ -1583,15 +1577,17 @@ def comment_line(path, if re.match(regex, line): # Write the new line if cmnt: - wline = '{0}{1}'.format(char, line) + wline = "{0}{1}".format(char, line) else: wline = line.lstrip(char) else: # Write the existing line (no change) wline = line - wline = salt.utils.stringutils.to_bytes(wline) \ - if six.PY2 and salt.utils.platform.is_windows() \ + wline = ( + salt.utils.stringutils.to_bytes(wline) + if six.PY2 and salt.utils.platform.is_windows() else salt.utils.stringutils.to_str(wline) + ) w_file.write(wline) except (OSError, IOError) as exc: raise CommandExecutionError( @@ -1607,7 +1603,7 @@ def comment_line(path, if backup: # Move the backup file to the original directory - backup_name = '{0}{1}'.format(path, backup) + backup_name = "{0}{1}".format(path, backup) try: shutil.move(temp_file, backup_name) except (OSError, IOError) as exc: @@ -1623,11 +1619,11 @@ def comment_line(path, check_perms(path, None, pre_user, pre_group, pre_mode) # Return a diff using the two dictionaries - return __utils__['stringutils.get_diff'](orig_file, new_file) + return __utils__["stringutils.get_diff"](orig_file, new_file) def _get_flags(flags): - ''' + """ Return an integer appropriate for use as a flag for the re module from a list of human-readable strings @@ -1639,7 +1635,7 @@ def _get_flags(flags): 8 >>> _get_flags(2) 2 - ''' + """ if isinstance(flags, six.string_types): flags = [flags] @@ -1649,9 +1645,7 @@ def _get_flags(flags): _flag = getattr(re, six.text_type(flag).upper()) if not isinstance(_flag, six.integer_types): - raise SaltInvocationError( - 'Invalid re flag given: {0}'.format(flag) - ) + raise SaltInvocationError("Invalid re flag given: {0}".format(flag)) _flags_acc.append(_flag) @@ -1661,22 +1655,21 @@ def _get_flags(flags): else: raise SaltInvocationError( 'Invalid re flags: "{0}", must be given either as a single flag ' - 'string, a list of strings, or as an integer'.format(flags) + "string, a list of strings, or as an integer".format(flags) ) def _add_flags(flags, new_flags): - ''' + """ Combine ``flags`` and ``new_flags`` - ''' + """ flags = _get_flags(flags) new_flags = _get_flags(new_flags) return flags | new_flags -def _mkstemp_copy(path, - preserve_inode=True): - ''' +def _mkstemp_copy(path, preserve_inode=True): + """ Create a temp file and move/copy the contents of ``path`` to the temp file. Return the path to the temp file. @@ -1691,16 +1684,15 @@ def _mkstemp_copy(path, new inode, but using the original filename. Hard links will then share an inode with the backup, instead (if using ``backup`` to create a backup copy). Default is ``True``. - ''' + """ temp_file = None # Create the temp file try: temp_file = salt.utils.files.mkstemp(prefix=salt.utils.files.TEMPFILE_PREFIX) except (OSError, IOError) as exc: raise CommandExecutionError( - "Unable to create temp file. " - "Exception: {0}".format(exc) - ) + "Unable to create temp file. " "Exception: {0}".format(exc) + ) # use `copy` to preserve the inode of the # original file, and thus preserve hardlinks # to the inode. otherwise, use `move` to @@ -1714,7 +1706,7 @@ def _mkstemp_copy(path, "Unable to copy file '{0}' to the " "temp file '{1}'. " "Exception: {2}".format(path, temp_file, exc) - ) + ) else: try: shutil.move(path, temp_file) @@ -1723,27 +1715,28 @@ def _mkstemp_copy(path, "Unable to move file '{0}' to the " "temp file '{1}'. " "Exception: {2}".format(path, temp_file, exc) - ) + ) return temp_file def _starts_till(src, probe, strip_comments=True): - ''' + """ Returns True if src and probe at least matches at the beginning till some point. - ''' + """ + def _strip_comments(txt): - ''' + """ Strip possible comments. Usually comments are one or two symbols at the beginning of the line, separated with space - ''' + """ buff = txt.split(" ", 1) return len(buff) == 2 and len(buff[0]) < 2 and buff[1] or txt def _to_words(txt): - ''' + """ Split by words - ''' + """ return txt and [w for w in txt.strip().split(" ") if w.strip()] or txt no_match = -1 @@ -1751,8 +1744,8 @@ def _starts_till(src, probe, strip_comments=True): if not src or not probe: return no_match - src = src.rstrip('\n\r') - probe = probe.rstrip('\n\r') + src = src.rstrip("\n\r") + probe = probe.rstrip("\n\r") if src == probe: return equal @@ -1760,9 +1753,9 @@ def _starts_till(src, probe, strip_comments=True): probe = _to_words(strip_comments and _strip_comments(probe) or probe) a_buff, b_buff = len(src) < len(probe) and (src, probe) or (probe, src) - b_buff = ' '.join(b_buff) + b_buff = " ".join(b_buff) for idx in range(len(a_buff)): - prb = ' '.join(a_buff[:-(idx + 1)]) + prb = " ".join(a_buff[: -(idx + 1)]) if prb and b_buff.startswith(prb): return idx @@ -1770,9 +1763,9 @@ def _starts_till(src, probe, strip_comments=True): def _regex_to_static(src, regex): - ''' + """ Expand regular expression to static match. - ''' + """ if not src or not regex: return None @@ -1786,50 +1779,52 @@ def _regex_to_static(src, regex): def _assert_occurrence(probe, target, amount=1): - ''' + """ Raise an exception, if there are different amount of specified occurrences in src. - ''' + """ occ = len(probe) if occ > amount: - msg = 'more than' + msg = "more than" elif occ < amount: - msg = 'less than' + msg = "less than" elif not occ: - msg = 'no' + msg = "no" else: msg = None if msg: - raise CommandExecutionError('Found {0} expected occurrences in "{1}" expression'.format(msg, target)) + raise CommandExecutionError( + 'Found {0} expected occurrences in "{1}" expression'.format(msg, target) + ) return occ def _set_line_indent(src, line, indent): - ''' + """ Indent the line with the source line. - ''' + """ if not indent: return line idt = [] for c in src: - if c not in ['\t', ' ']: + if c not in ["\t", " "]: break idt.append(c) - return ''.join(idt) + line.lstrip() + return "".join(idt) + line.lstrip() def _get_eol(line): - match = re.search('((? 0: @@ -2348,25 +2383,33 @@ def replace(path, # Identity check the potential change has_changes = True if pattern != repl else has_changes + orig_file = ( + r_data.read(filesize).splitlines(True) + if isinstance(r_data, mmap.mmap) + else r_data.splitlines(True) + ) + new_file = result.splitlines(True) + + if orig_file == new_file: + has_changes = False + if prepend_if_not_found or append_if_not_found: # Search for content, to avoid pre/appending the # content if it was pre/appended in a previous run. - if re.search(salt.utils.stringutils.to_bytes('^{0}($|(?=\r\n))'.format(re.escape(content))), - r_data, - flags=flags_num): + if re.search( + salt.utils.stringutils.to_bytes( + "^{0}($|(?=\r\n))".format(re.escape(content)) + ), + r_data, + flags=flags_num, + ): # Content was found, so set found. found = True - orig_file = r_data.read(filesize).splitlines(True) \ - if isinstance(r_data, mmap.mmap) \ - else r_data.splitlines(True) - new_file = result.splitlines(True) - except (OSError, IOError) as exc: raise CommandExecutionError( - "Unable to open file '{0}'. " - "Exception: {1}".format(path, exc) - ) + "Unable to open file '{0}'. " "Exception: {1}".format(path, exc) + ) finally: if r_data and isinstance(r_data, mmap.mmap): r_data.close() @@ -2375,29 +2418,26 @@ def replace(path, # Write the replacement text in this block. try: # Create a copy to read from and to use as a backup later - temp_file = _mkstemp_copy(path=path, - preserve_inode=preserve_inode) + temp_file = _mkstemp_copy(path=path, preserve_inode=preserve_inode) except (OSError, IOError) as exc: raise CommandExecutionError("Exception: {0}".format(exc)) r_data = None try: # Open the file in write mode - with salt.utils.files.fopen(path, - mode='w', - buffering=bufsize) as w_file: + with salt.utils.files.fopen(path, mode="w", buffering=bufsize) as w_file: try: # Open the temp file in read mode - with salt.utils.files.fopen(temp_file, - mode='r', - buffering=bufsize) as r_file: - r_data = mmap.mmap(r_file.fileno(), - 0, - access=mmap.ACCESS_READ) - result, nrepl = re.subn(cpattern, - repl.replace('\\', '\\\\') if backslash_literal else repl, - r_data, - count) + with salt.utils.files.fopen( + temp_file, mode="r", buffering=bufsize + ) as r_file: + r_data = mmap.mmap(r_file.fileno(), 0, access=mmap.ACCESS_READ) + result, nrepl = re.subn( + cpattern, + repl.replace("\\", "\\\\") if backslash_literal else repl, + r_data, + count, + ) try: w_file.write(salt.utils.stringutils.to_str(result)) except (OSError, IOError) as exc: @@ -2406,7 +2446,7 @@ def replace(path, "be truncated. Temporary file contains copy " "at '{1}'. " "Exception: {2}".format(path, temp_file, exc) - ) + ) except (OSError, IOError) as exc: raise CommandExecutionError("Exception: {0}".format(exc)) finally: @@ -2419,25 +2459,30 @@ def replace(path, if not_found_content is None: not_found_content = repl if prepend_if_not_found: - new_file.insert(0, not_found_content + salt.utils.stringutils.to_bytes(os.linesep)) + new_file.insert( + 0, not_found_content + salt.utils.stringutils.to_bytes(os.linesep) + ) else: # append_if_not_found # Make sure we have a newline at the end of the file if 0 != len(new_file): - if not new_file[-1].endswith(salt.utils.stringutils.to_bytes(os.linesep)): + if not new_file[-1].endswith( + salt.utils.stringutils.to_bytes(os.linesep) + ): new_file[-1] += salt.utils.stringutils.to_bytes(os.linesep) - new_file.append(not_found_content + salt.utils.stringutils.to_bytes(os.linesep)) + new_file.append( + not_found_content + salt.utils.stringutils.to_bytes(os.linesep) + ) has_changes = True if not dry_run: try: # Create a copy to read from and for later use as a backup - temp_file = _mkstemp_copy(path=path, - preserve_inode=preserve_inode) + temp_file = _mkstemp_copy(path=path, preserve_inode=preserve_inode) except (OSError, IOError) as exc: raise CommandExecutionError("Exception: {0}".format(exc)) # write new content in the file while avoiding partial reads try: - fh_ = salt.utils.atomicfile.atomic_open(path, 'wb') + fh_ = salt.utils.atomicfile.atomic_open(path, "wb") for line in new_file: fh_.write(salt.utils.stringutils.to_bytes(line)) finally: @@ -2446,7 +2491,7 @@ def replace(path, if backup and has_changes and not dry_run: # keep the backup only if it was requested # and only if there were any changes - backup_name = '{0}{1}'.format(path, backup) + backup_name = "{0}{1}".format(path, backup) try: shutil.move(temp_file, backup_name) except (OSError, IOError) as exc: @@ -2454,10 +2499,10 @@ def replace(path, "Unable to move the temp file '{0}' to the " "backup file '{1}'. " "Exception: {2}".format(path, temp_file, exc) - ) + ) if symlink: - symlink_backup = '{0}{1}'.format(given_path, backup) - target_backup = '{0}{1}'.format(target_path, backup) + symlink_backup = "{0}{1}".format(given_path, backup) + target_backup = "{0}{1}".format(target_path, backup) # Always clobber any existing symlink backup # to match the behaviour of the 'backup' option try: @@ -2469,9 +2514,8 @@ def replace(path, raise CommandExecutionError( "Unable create backup symlink '{0}'. " "Target was '{1}'. " - "Exception: {2}".format(symlink_backup, target_backup, - exc) - ) + "Exception: {2}".format(symlink_backup, target_backup, exc) + ) elif temp_file: try: os.remove(temp_file) @@ -2479,12 +2523,12 @@ def replace(path, raise CommandExecutionError( "Unable to delete temp file '{0}'. " "Exception: {1}".format(temp_file, exc) - ) + ) if not dry_run and not salt.utils.platform.is_windows(): check_perms(path, None, pre_user, pre_group, pre_mode) - differences = __utils__['stringutils.get_diff'](orig_file, new_file) + differences = __utils__["stringutils.get_diff"](orig_file, new_file) if show_changes: return differences @@ -2498,17 +2542,19 @@ def replace(path, return has_changes -def blockreplace(path, - marker_start='#-- start managed zone --', - marker_end='#-- end managed zone --', - content='', - append_if_not_found=False, - prepend_if_not_found=False, - backup='.bak', - dry_run=False, - show_changes=True, - append_newline=False): - ''' +def blockreplace( + path, + marker_start="#-- start managed zone --", + marker_end="#-- end managed zone --", + content="", + append_if_not_found=False, + prepend_if_not_found=False, + backup=".bak", + dry_run=False, + show_changes=True, + append_newline=False, +): + """ .. versionadded:: 2014.1.0 Replace content of a text block in a file, delimited by line markers @@ -2586,38 +2632,37 @@ def blockreplace(path, salt '*' file.blockreplace /etc/hosts '#-- start managed zone foobar : DO NOT EDIT --' \\ '#-- end managed zone foobar --' $'10.0.1.1 foo.foobar\\n10.0.1.2 bar.foobar' True - ''' + """ if append_if_not_found and prepend_if_not_found: raise SaltInvocationError( - 'Only one of append and prepend_if_not_found is permitted' + "Only one of append and prepend_if_not_found is permitted" ) path = os.path.expanduser(path) if not os.path.exists(path): - raise SaltInvocationError('File not found: {0}'.format(path)) + raise SaltInvocationError("File not found: {0}".format(path)) try: - file_encoding = __utils__['files.get_encoding'](path) + file_encoding = __utils__["files.get_encoding"](path) except CommandExecutionError: file_encoding = None - if __utils__['files.is_binary'](path): + if __utils__["files.is_binary"](path): if not file_encoding: raise SaltInvocationError( - 'Cannot perform string replacements on a binary file: {0}' - .format(path) - ) + "Cannot perform string replacements on a binary file: {0}".format(path) + ) - if append_newline is None and not content.endswith((os.linesep, '\n')): + if append_newline is None and not content.endswith((os.linesep, "\n")): append_newline = True # Split the content into a list of lines, removing newline characters. To # ensure that we handle both Windows and POSIX newlines, first split on # Windows newlines, and then split on POSIX newlines. split_content = [] - for win_line in content.split('\r\n'): - for content_line in win_line.split('\n'): + for win_line in content.split("\r\n"): + for content_line in win_line.split("\n"): split_content.append(content_line) line_count = len(split_content) @@ -2629,15 +2674,14 @@ def blockreplace(path, block_found = False linesep = None - def _add_content(linesep, lines=None, include_marker_start=True, - end_line=None): + def _add_content(linesep, lines=None, include_marker_start=True, end_line=None): if lines is None: lines = [] include_marker_start = True if end_line is None: end_line = marker_end - end_line = end_line.rstrip('\r\n') + linesep + end_line = end_line.rstrip("\r\n") + linesep if include_marker_start: lines.append(marker_start + linesep) @@ -2662,16 +2706,16 @@ def blockreplace(path, # no changes are required and to avoid any file access on a partially # written file. try: - fi_file = io.open(path, mode='r', encoding=file_encoding, newline='') + fi_file = io.open(path, mode="r", encoding=file_encoding, newline="") for line in fi_file: write_line_to_new_file = True if linesep is None: # Auto-detect line separator - if line.endswith('\r\n'): - linesep = '\r\n' - elif line.endswith('\n'): - linesep = '\n' + if line.endswith("\r\n"): + linesep = "\r\n" + elif line.endswith("\n"): + linesep = "\n" else: # No newline(s) in file, fall back to system's linesep linesep = os.linesep @@ -2692,9 +2736,12 @@ def blockreplace(path, # We've found and exited the block block_found = True - _add_content(linesep, lines=new_file, - include_marker_start=False, - end_line=line[marker_end_pos:]) + _add_content( + linesep, + lines=new_file, + include_marker_start=False, + end_line=line[marker_end_pos:], + ) # Save the line from the original file orig_file.append(line) @@ -2702,9 +2749,7 @@ def blockreplace(path, new_file.append(line) except (IOError, OSError) as exc: - raise CommandExecutionError( - 'Failed to read from {0}: {1}'.format(path, exc) - ) + raise CommandExecutionError("Failed to read from {0}: {1}".format(path, exc)) finally: if linesep is None: # If the file was empty, we will not have set linesep yet. Assume @@ -2719,7 +2764,7 @@ def blockreplace(path, if in_block: # unterminated block => bad, always fail raise CommandExecutionError( - 'Unterminated marked block. End of file reached before marker_end.' + "Unterminated marked block. End of file reached before marker_end." ) if not block_found: @@ -2739,45 +2784,47 @@ def blockreplace(path, block_found = True else: raise CommandExecutionError( - 'Cannot edit marked block. Markers were not found in file.' + "Cannot edit marked block. Markers were not found in file." ) if block_found: - diff = __utils__['stringutils.get_diff'](orig_file, new_file) - has_changes = diff is not '' + diff = __utils__["stringutils.get_diff"](orig_file, new_file) + has_changes = diff is not "" if has_changes and not dry_run: # changes detected # backup file attrs perms = {} - perms['user'] = get_user(path) - perms['group'] = get_group(path) - perms['mode'] = salt.utils.files.normalize_mode(get_mode(path)) + perms["user"] = get_user(path) + perms["group"] = get_group(path) + perms["mode"] = salt.utils.files.normalize_mode(get_mode(path)) # backup old content if backup is not False: - backup_path = '{0}{1}'.format(path, backup) + backup_path = "{0}{1}".format(path, backup) shutil.copy2(path, backup_path) # copy2 does not preserve ownership if salt.utils.platform.is_windows(): # This function resides in win_file.py and will be available # on Windows. The local function will be overridden # pylint: disable=E1120,E1123 - check_perms(path=backup_path, - ret=None, - owner=perms['user']) + check_perms(path=backup_path, ret=None, owner=perms["user"]) # pylint: enable=E1120,E1123 else: - check_perms(name=backup_path, - ret=None, - user=perms['user'], - group=perms['group'], - mode=perms['mode']) + check_perms( + name=backup_path, + ret=None, + user=perms["user"], + group=perms["group"], + mode=perms["mode"], + ) # write new content in the file while avoiding partial reads try: - fh_ = salt.utils.atomicfile.atomic_open(path, 'wb') + fh_ = salt.utils.atomicfile.atomic_open(path, "wb") for line in new_file: - fh_.write(salt.utils.stringutils.to_bytes(line, encoding=file_encoding)) + fh_.write( + salt.utils.stringutils.to_bytes(line, encoding=file_encoding) + ) finally: fh_.close() @@ -2786,16 +2833,16 @@ def blockreplace(path, # This function resides in win_file.py and will be available # on Windows. The local function will be overridden # pylint: disable=E1120,E1123 - check_perms(path=path, - ret=None, - owner=perms['user']) + check_perms(path=path, ret=None, owner=perms["user"]) # pylint: enable=E1120,E1123 else: - check_perms(path, - ret=None, - user=perms['user'], - group=perms['group'], - mode=perms['mode']) + check_perms( + path, + ret=None, + user=perms["user"], + group=perms["group"], + mode=perms["mode"], + ) if show_changes: return diff @@ -2803,14 +2850,8 @@ def blockreplace(path, return has_changes -def search(path, - pattern, - flags=8, - bufsize=1, - ignore_if_missing=False, - multiline=False - ): - ''' +def search(path, pattern, flags=8, bufsize=1, ignore_if_missing=False, multiline=False): + """ .. versionadded:: 0.17.0 Search for occurrences of a pattern in a file @@ -2829,27 +2870,29 @@ def search(path, .. code-block:: bash salt '*' file.search /etc/crontab 'mymaintenance.sh' - ''' + """ if multiline: - flags = _add_flags(flags, 'MULTILINE') - bufsize = 'file' + flags = _add_flags(flags, "MULTILINE") + bufsize = "file" # This function wraps file.replace on purpose in order to enforce # consistent usage, compatible regex's, expected behavior, *and* bugs. :) # Any enhancements or fixes to one should affect the other. - return replace(path, - pattern, - '', - flags=flags, - bufsize=bufsize, - dry_run=True, - search_only=True, - show_changes=False, - ignore_if_missing=ignore_if_missing) + return replace( + path, + pattern, + "", + flags=flags, + bufsize=bufsize, + dry_run=True, + search_only=True, + show_changes=False, + ignore_if_missing=ignore_if_missing, + ) -def patch(originalfile, patchfile, options='', dry_run=False): - ''' +def patch(originalfile, patchfile, options="", dry_run=False): + """ .. versionadded:: 0.10.4 Apply a patch to a file or directory. @@ -2878,60 +2921,63 @@ def patch(originalfile, patchfile, options='', dry_run=False): .. code-block:: bash salt '*' file.patch /opt/file.txt /tmp/file.txt.patch - ''' - patchpath = salt.utils.path.which('patch') + """ + patchpath = salt.utils.path.which("patch") if not patchpath: raise CommandExecutionError( - 'patch executable not found. Is the distribution\'s patch ' - 'package installed?' + "patch executable not found. Is the distribution's patch " + "package installed?" ) cmd = [patchpath] cmd.extend(salt.utils.args.shlex_split(options)) if dry_run: - if __grains__['kernel'] in ('FreeBSD', 'OpenBSD'): - cmd.append('-C') + if __grains__["kernel"] in ("FreeBSD", "OpenBSD"): + cmd.append("-C") else: - cmd.append('--dry-run') + cmd.append("--dry-run") # this argument prevents interactive prompts when the patch fails to apply. # the exit code will still be greater than 0 if that is the case. - if '-N' not in cmd and '--forward' not in cmd: - cmd.append('--forward') + if "-N" not in cmd and "--forward" not in cmd: + cmd.append("--forward") has_rejectfile_option = False for option in cmd: - if option == '-r' or option.startswith('-r ') \ - or option.startswith('--reject-file'): + if ( + option == "-r" + or option.startswith("-r ") + or option.startswith("--reject-file") + ): has_rejectfile_option = True break # by default, patch will write rejected patch files to .rej. # this option prevents that. if not has_rejectfile_option: - cmd.append('--reject-file=-') + cmd.append("--reject-file=-") - cmd.extend(['-i', patchfile]) + cmd.extend(["-i", patchfile]) if os.path.isdir(originalfile): - cmd.extend(['-d', originalfile]) + cmd.extend(["-d", originalfile]) has_strip_option = False for option in cmd: - if option.startswith('-p') or option.startswith('--strip='): + if option.startswith("-p") or option.startswith("--strip="): has_strip_option = True break if not has_strip_option: - cmd.append('--strip=0') + cmd.append("--strip=0") else: cmd.append(originalfile) - return __salt__['cmd.run_all'](cmd, python_shell=False) + return __salt__["cmd.run_all"](cmd, python_shell=False) def contains(path, text): - ''' + """ .. deprecated:: 0.17.0 Use :func:`search` instead. @@ -2942,7 +2988,7 @@ def contains(path, text): .. code-block:: bash salt '*' file.contains /etc/crontab 'mymaintenance.sh' - ''' + """ path = os.path.expanduser(path) if not os.path.exists(path): @@ -2959,8 +3005,8 @@ def contains(path, text): return False -def contains_regex(path, regex, lchar=''): - ''' +def contains_regex(path, regex, lchar=""): + """ .. deprecated:: 0.17.0 Use :func:`search` instead. @@ -2975,14 +3021,14 @@ def contains_regex(path, regex, lchar=''): .. code-block:: bash salt '*' file.contains_regex /etc/crontab - ''' + """ path = os.path.expanduser(path) if not os.path.exists(path): return False try: - with salt.utils.files.fopen(path, 'r') as target: + with salt.utils.files.fopen(path, "r") as target: for line in target: line = salt.utils.stringutils.to_unicode(line) if lchar: @@ -2995,7 +3041,7 @@ def contains_regex(path, regex, lchar=''): def contains_glob(path, glob_expr): - ''' + """ .. deprecated:: 0.17.0 Use :func:`search` instead. @@ -3006,7 +3052,7 @@ def contains_glob(path, glob_expr): .. code-block:: bash salt '*' file.contains_glob /etc/foobar '*cheese*' - ''' + """ path = os.path.expanduser(path) if not os.path.exists(path): @@ -3023,7 +3069,7 @@ def contains_glob(path, glob_expr): def append(path, *args, **kwargs): - ''' + """ .. versionadded:: 0.9.5 Append text to the end of a file @@ -3054,20 +3100,20 @@ def append(path, *args, **kwargs): salt '*' file.append /etc/motd args="['cheese=spam','spam=cheese']" - ''' + """ path = os.path.expanduser(path) # Largely inspired by Fabric's contrib.files.append() - if 'args' in kwargs: - if isinstance(kwargs['args'], list): - args = kwargs['args'] + if "args" in kwargs: + if isinstance(kwargs["args"], list): + args = kwargs["args"] else: - args = [kwargs['args']] + args = [kwargs["args"]] # Make sure we have a newline at the end of the file. Do this in binary # mode so SEEK_END with nonzero offset will work. - with salt.utils.files.fopen(path, 'rb+') as ofile: + with salt.utils.files.fopen(path, "rb+") as ofile: linesep = salt.utils.stringutils.to_bytes(os.linesep) try: ofile.seek(-len(linesep), os.SEEK_END) @@ -3083,19 +3129,17 @@ def append(path, *args, **kwargs): ofile.write(linesep) # Append lines in text mode - with salt.utils.files.fopen(path, 'a') as ofile: + with salt.utils.files.fopen(path, "a") as ofile: for new_line in args: ofile.write( - salt.utils.stringutils.to_str( - '{0}{1}'.format(new_line, os.linesep) - ) + salt.utils.stringutils.to_str("{0}{1}".format(new_line, os.linesep)) ) return 'Wrote {0} lines to "{1}"'.format(len(args), path) def prepend(path, *args, **kwargs): - ''' + """ .. versionadded:: 2014.7.0 Prepend text to the beginning of a file @@ -3126,34 +3170,35 @@ def prepend(path, *args, **kwargs): salt '*' file.prepend /etc/motd args="['cheese=spam','spam=cheese']" - ''' + """ path = os.path.expanduser(path) - if 'args' in kwargs: - if isinstance(kwargs['args'], list): - args = kwargs['args'] + if "args" in kwargs: + if isinstance(kwargs["args"], list): + args = kwargs["args"] else: - args = [kwargs['args']] + args = [kwargs["args"]] try: with salt.utils.files.fopen(path) as fhr: - contents = [salt.utils.stringutils.to_unicode(line) - for line in fhr.readlines()] + contents = [ + salt.utils.stringutils.to_unicode(line) for line in fhr.readlines() + ] except IOError: contents = [] preface = [] for line in args: - preface.append('{0}\n'.format(line)) + preface.append("{0}\n".format(line)) - with salt.utils.files.fopen(path, 'w') as ofile: + with salt.utils.files.fopen(path, "w") as ofile: contents = preface + contents - ofile.write(salt.utils.stringutils.to_str(''.join(contents))) + ofile.write(salt.utils.stringutils.to_str("".join(contents))) return 'Prepended {0} lines to "{1}"'.format(len(args), path) def write(path, *args, **kwargs): - ''' + """ .. versionadded:: 2014.7.0 Write text to a file, overwriting any existing contents. @@ -3183,25 +3228,25 @@ def write(path, *args, **kwargs): salt '*' file.write /etc/motd args="['cheese=spam','spam=cheese']" - ''' + """ path = os.path.expanduser(path) - if 'args' in kwargs: - if isinstance(kwargs['args'], list): - args = kwargs['args'] + if "args" in kwargs: + if isinstance(kwargs["args"], list): + args = kwargs["args"] else: - args = [kwargs['args']] + args = [kwargs["args"]] contents = [] for line in args: - contents.append('{0}\n'.format(line)) + contents.append("{0}\n".format(line)) with salt.utils.files.fopen(path, "w") as ofile: - ofile.write(salt.utils.stringutils.to_str(''.join(contents))) + ofile.write(salt.utils.stringutils.to_str("".join(contents))) return 'Wrote {0} lines to "{1}"'.format(len(contents), path) def touch(name, atime=None, mtime=None): - ''' + """ .. versionadded:: 0.9.5 Just like the ``touch`` command, create a file if it doesn't exist or @@ -3217,7 +3262,7 @@ def touch(name, atime=None, mtime=None): .. code-block:: bash salt '*' file.touch /var/log/emptyfile - ''' + """ name = os.path.expanduser(name) if atime and atime.isdigit(): @@ -3226,7 +3271,7 @@ def touch(name, atime=None, mtime=None): mtime = int(mtime) try: if not os.path.exists(name): - with salt.utils.files.fopen(name, 'a'): + with salt.utils.files.fopen(name, "a"): pass if not atime and not mtime: @@ -3240,7 +3285,7 @@ def touch(name, atime=None, mtime=None): os.utime(name, times) except TypeError: - raise SaltInvocationError('atime and mtime must be integers') + raise SaltInvocationError("atime and mtime must be integers") except (IOError, OSError) as exc: raise CommandExecutionError(exc.strerror) @@ -3248,7 +3293,7 @@ def touch(name, atime=None, mtime=None): def seek_read(path, size, offset): - ''' + """ .. versionadded:: 2014.1.0 Seek to a position on a file and read it @@ -3267,7 +3312,7 @@ def seek_read(path, size, offset): .. code-block:: bash salt '*' file.seek_read /path/to/file 4096 0 - ''' + """ path = os.path.expanduser(path) seek_fh = os.open(path, os.O_RDONLY) try: @@ -3279,7 +3324,7 @@ def seek_read(path, size, offset): def seek_write(path, data, offset): - ''' + """ .. versionadded:: 2014.1.0 Seek to a position on a file and write to it @@ -3298,7 +3343,7 @@ def seek_write(path, data, offset): .. code-block:: bash salt '*' file.seek_write /path/to/file 'some data' 4096 - ''' + """ path = os.path.expanduser(path) seek_fh = os.open(path, os.O_WRONLY) try: @@ -3311,7 +3356,7 @@ def seek_write(path, data, offset): def truncate(path, length): - ''' + """ .. versionadded:: 2014.1.0 Seek to a position on a file and delete everything after that point @@ -3327,14 +3372,14 @@ def truncate(path, length): .. code-block:: bash salt '*' file.truncate /path/to/file 512 - ''' + """ path = os.path.expanduser(path) - with salt.utils.files.fopen(path, 'rb+') as seek_fh: + with salt.utils.files.fopen(path, "rb+") as seek_fh: seek_fh.truncate(int(length)) def link(src, path): - ''' + """ .. versionadded:: 2014.1.0 Create a hard link to a file @@ -3344,22 +3389,22 @@ def link(src, path): .. code-block:: bash salt '*' file.link /path/to/file /path/to/link - ''' + """ src = os.path.expanduser(src) if not os.path.isabs(src): - raise SaltInvocationError('File path must be absolute.') + raise SaltInvocationError("File path must be absolute.") try: os.link(src, path) return True except (OSError, IOError) as E: - raise CommandExecutionError('Could not create \'{0}\': {1}'.format(path, E)) + raise CommandExecutionError("Could not create '{0}': {1}".format(path, E)) return False def is_hardlink(path): - ''' + """ Check if the path is a hard link by verifying that the number of links is larger than 1 @@ -3368,16 +3413,16 @@ def is_hardlink(path): .. code-block:: bash salt '*' file.is_hardlink /path/to/link - ''' + """ # Simply use lstat and count the st_nlink field to determine if this path # is hardlinked to something. res = lstat(os.path.expanduser(path)) - return res and res['st_nlink'] > 1 + return res and res["st_nlink"] > 1 def is_link(path): - ''' + """ Check if the path is a symbolic link CLI Example: @@ -3385,7 +3430,7 @@ def is_link(path): .. code-block:: bash salt '*' file.is_link /path/to/link - ''' + """ # This function exists because os.path.islink does not support Windows, # therefore a custom function will need to be called. This function # therefore helps API consistency by providing a single function to call for @@ -3395,7 +3440,7 @@ def is_link(path): def symlink(src, path): - ''' + """ Create a symbolic link (symlink, soft link) to a file CLI Example: @@ -3403,29 +3448,29 @@ def symlink(src, path): .. code-block:: bash salt '*' file.symlink /path/to/file /path/to/link - ''' + """ path = os.path.expanduser(path) try: if os.path.normpath(os.readlink(path)) == os.path.normpath(src): - log.debug('link already in correct state: %s -> %s', path, src) + log.debug("link already in correct state: %s -> %s", path, src) return True except OSError: pass if not os.path.isabs(path): - raise SaltInvocationError('File path must be absolute.') + raise SaltInvocationError("File path must be absolute.") try: os.symlink(src, path) return True except (OSError, IOError): - raise CommandExecutionError('Could not create \'{0}\''.format(path)) + raise CommandExecutionError("Could not create '{0}'".format(path)) return False def rename(src, dst): - ''' + """ Rename a file or directory CLI Example: @@ -3433,25 +3478,23 @@ def rename(src, dst): .. code-block:: bash salt '*' file.rename /path/to/src /path/to/dst - ''' + """ src = os.path.expanduser(src) dst = os.path.expanduser(dst) if not os.path.isabs(src): - raise SaltInvocationError('File path must be absolute.') + raise SaltInvocationError("File path must be absolute.") try: os.rename(src, dst) return True except OSError: - raise CommandExecutionError( - 'Could not rename \'{0}\' to \'{1}\''.format(src, dst) - ) + raise CommandExecutionError("Could not rename '{0}' to '{1}'".format(src, dst)) return False def copy(src, dst, recurse=False, remove_existing=False): - ''' + """ Copy a file or directory from source to dst In order to copy a directory, the recurse flag is required, and @@ -3476,15 +3519,15 @@ def copy(src, dst, recurse=False, remove_existing=False): salt '*' file.copy /path/to/src_dir /path/to/dst_dir recurse=True salt '*' file.copy /path/to/src_dir /path/to/dst_dir recurse=True remove_existing=True - ''' + """ src = os.path.expanduser(src) dst = os.path.expanduser(dst) if not os.path.isabs(src): - raise SaltInvocationError('File path must be absolute.') + raise SaltInvocationError("File path must be absolute.") if not os.path.exists(src): - raise CommandExecutionError('No such file or directory \'{0}\''.format(src)) + raise CommandExecutionError("No such file or directory '{0}'".format(src)) if not salt.utils.platform.is_windows(): pre_user = get_user(src) @@ -3495,7 +3538,8 @@ def copy(src, dst, recurse=False, remove_existing=False): if (os.path.exists(dst) and os.path.isdir(dst)) or os.path.isdir(src): if not recurse: raise SaltInvocationError( - "Cannot copy overwriting a directory without recurse flag set to true!") + "Cannot copy overwriting a directory without recurse flag set to true!" + ) if remove_existing: if os.path.exists(dst): shutil.rmtree(dst) @@ -3505,9 +3549,7 @@ def copy(src, dst, recurse=False, remove_existing=False): else: shutil.copyfile(src, dst) except OSError: - raise CommandExecutionError( - 'Could not copy \'{0}\' to \'{1}\''.format(src, dst) - ) + raise CommandExecutionError("Could not copy '{0}' to '{1}'".format(src, dst)) if not salt.utils.platform.is_windows(): check_perms(dst, None, pre_user, pre_group, pre_mode) @@ -3515,7 +3557,7 @@ def copy(src, dst, recurse=False, remove_existing=False): def lstat(path): - ''' + """ .. versionadded:: 2014.1.0 Returns the lstat attributes for the given file or dir. Does not support @@ -3526,22 +3568,33 @@ def lstat(path): .. code-block:: bash salt '*' file.lstat /path/to/file - ''' + """ path = os.path.expanduser(path) if not os.path.isabs(path): - raise SaltInvocationError('Path to file must be absolute.') + raise SaltInvocationError("Path to file must be absolute.") try: lst = os.lstat(path) - return dict((key, getattr(lst, key)) for key in ('st_atime', 'st_ctime', - 'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid')) + return dict( + (key, getattr(lst, key)) + for key in ( + "st_atime", + "st_ctime", + "st_gid", + "st_mode", + "st_mtime", + "st_nlink", + "st_size", + "st_uid", + ) + ) except Exception: # pylint: disable=broad-except return {} def access(path, mode): - ''' + """ .. versionadded:: 2014.1.0 Test whether the Salt process has the specified access to the file. One of @@ -3560,27 +3613,24 @@ def access(path, mode): salt '*' file.access /path/to/file f salt '*' file.access /path/to/file x - ''' + """ path = os.path.expanduser(path) if not os.path.isabs(path): - raise SaltInvocationError('Path to link must be absolute.') + raise SaltInvocationError("Path to link must be absolute.") - modes = {'f': os.F_OK, - 'r': os.R_OK, - 'w': os.W_OK, - 'x': os.X_OK} + modes = {"f": os.F_OK, "r": os.R_OK, "w": os.W_OK, "x": os.X_OK} if mode in modes: return os.access(path, modes[mode]) elif mode in six.itervalues(modes): return os.access(path, mode) else: - raise SaltInvocationError('Invalid mode specified.') + raise SaltInvocationError("Invalid mode specified.") def read(path, binary=False): - ''' + """ .. versionadded:: 2017.7.0 Return the content of the file. @@ -3590,16 +3640,16 @@ def read(path, binary=False): .. code-block:: bash salt '*' file.read /path/to/file - ''' - access_mode = 'r' + """ + access_mode = "r" if binary is True: - access_mode += 'b' + access_mode += "b" with salt.utils.files.fopen(path, access_mode) as file_obj: return salt.utils.stringutils.to_unicode(file_obj.read()) def readlink(path, canonicalize=False): - ''' + """ .. versionadded:: 2014.1.0 Return the path that a symlink points to @@ -3610,14 +3660,14 @@ def readlink(path, canonicalize=False): .. code-block:: bash salt '*' file.readlink /path/to/link - ''' + """ path = os.path.expanduser(path) if not os.path.isabs(path): - raise SaltInvocationError('Path to link must be absolute.') + raise SaltInvocationError("Path to link must be absolute.") if not os.path.islink(path): - raise SaltInvocationError('A valid link was not specified.') + raise SaltInvocationError("A valid link was not specified.") if canonicalize: return os.path.realpath(path) @@ -3626,7 +3676,7 @@ def readlink(path, canonicalize=False): def readdir(path): - ''' + """ .. versionadded:: 2014.1.0 Return a list containing the contents of a directory @@ -3636,22 +3686,22 @@ def readdir(path): .. code-block:: bash salt '*' file.readdir /path/to/dir/ - ''' + """ path = os.path.expanduser(path) if not os.path.isabs(path): - raise SaltInvocationError('Dir path must be absolute.') + raise SaltInvocationError("Dir path must be absolute.") if not os.path.isdir(path): - raise SaltInvocationError('A valid directory was not specified.') + raise SaltInvocationError("A valid directory was not specified.") - dirents = ['.', '..'] + dirents = [".", ".."] dirents.extend(os.listdir(path)) return dirents def statvfs(path): - ''' + """ .. versionadded:: 2014.1.0 Perform a statvfs call against the filesystem that the file resides on @@ -3661,24 +3711,36 @@ def statvfs(path): .. code-block:: bash salt '*' file.statvfs /path/to/file - ''' + """ path = os.path.expanduser(path) if not os.path.isabs(path): - raise SaltInvocationError('File path must be absolute.') + raise SaltInvocationError("File path must be absolute.") try: stv = os.statvfs(path) - return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree', - 'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag', - 'f_frsize', 'f_namemax')) + return dict( + (key, getattr(stv, key)) + for key in ( + "f_bavail", + "f_bfree", + "f_blocks", + "f_bsize", + "f_favail", + "f_ffree", + "f_files", + "f_flag", + "f_frsize", + "f_namemax", + ) + ) except (OSError, IOError): - raise CommandExecutionError('Could not statvfs \'{0}\''.format(path)) + raise CommandExecutionError("Could not statvfs '{0}'".format(path)) return False def stats(path, hash_type=None, follow_symlinks=True): - ''' + """ Return a dict containing the stats for a given file CLI Example: @@ -3686,7 +3748,7 @@ def stats(path, hash_type=None, follow_symlinks=True): .. code-block:: bash salt '*' file.stats /etc/passwd - ''' + """ path = os.path.expanduser(path) ret = {} @@ -3701,45 +3763,45 @@ def stats(path, hash_type=None, follow_symlinks=True): # message in this exception. Any changes made to the message for this # exception will reflect the file.directory state as well, and will # likely require changes there. - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) else: if follow_symlinks: pstat = os.stat(path) else: pstat = os.lstat(path) - ret['inode'] = pstat.st_ino - ret['uid'] = pstat.st_uid - ret['gid'] = pstat.st_gid - ret['group'] = gid_to_group(pstat.st_gid) - ret['user'] = uid_to_user(pstat.st_uid) - ret['atime'] = pstat.st_atime - ret['mtime'] = pstat.st_mtime - ret['ctime'] = pstat.st_ctime - ret['size'] = pstat.st_size - ret['mode'] = salt.utils.files.normalize_mode(oct(stat.S_IMODE(pstat.st_mode))) + ret["inode"] = pstat.st_ino + ret["uid"] = pstat.st_uid + ret["gid"] = pstat.st_gid + ret["group"] = gid_to_group(pstat.st_gid) + ret["user"] = uid_to_user(pstat.st_uid) + ret["atime"] = pstat.st_atime + ret["mtime"] = pstat.st_mtime + ret["ctime"] = pstat.st_ctime + ret["size"] = pstat.st_size + ret["mode"] = salt.utils.files.normalize_mode(oct(stat.S_IMODE(pstat.st_mode))) if hash_type: - ret['sum'] = get_hash(path, hash_type) - ret['type'] = 'file' + ret["sum"] = get_hash(path, hash_type) + ret["type"] = "file" if stat.S_ISDIR(pstat.st_mode): - ret['type'] = 'dir' + ret["type"] = "dir" if stat.S_ISCHR(pstat.st_mode): - ret['type'] = 'char' + ret["type"] = "char" if stat.S_ISBLK(pstat.st_mode): - ret['type'] = 'block' + ret["type"] = "block" if stat.S_ISREG(pstat.st_mode): - ret['type'] = 'file' + ret["type"] = "file" if stat.S_ISLNK(pstat.st_mode): - ret['type'] = 'link' + ret["type"] = "link" if stat.S_ISFIFO(pstat.st_mode): - ret['type'] = 'pipe' + ret["type"] = "pipe" if stat.S_ISSOCK(pstat.st_mode): - ret['type'] = 'socket' - ret['target'] = os.path.realpath(path) + ret["type"] = "socket" + ret["target"] = os.path.realpath(path) return ret def rmdir(path): - ''' + """ .. versionadded:: 2014.1.0 Remove the specified directory. Fails if a directory is not empty. @@ -3749,14 +3811,14 @@ def rmdir(path): .. code-block:: bash salt '*' file.rmdir /tmp/foo/ - ''' + """ path = os.path.expanduser(path) if not os.path.isabs(path): - raise SaltInvocationError('File path must be absolute.') + raise SaltInvocationError("File path must be absolute.") if not os.path.isdir(path): - raise SaltInvocationError('A valid directory was not specified.') + raise SaltInvocationError("A valid directory was not specified.") try: os.rmdir(path) @@ -3766,7 +3828,7 @@ def rmdir(path): def remove(path): - ''' + """ Remove the named file. If a directory is supplied, it will be recursively deleted. @@ -3779,11 +3841,11 @@ def remove(path): .. versionchanged:: 3000 The method now works on all types of file system entries, not just files, directories and symlinks. - ''' + """ path = os.path.expanduser(path) if not os.path.isabs(path): - raise SaltInvocationError('File path must be absolute: {0}'.format(path)) + raise SaltInvocationError("File path must be absolute: {0}".format(path)) try: if os.path.islink(path) or (os.path.exists(path) and not os.path.isdir(path)): @@ -3793,14 +3855,12 @@ def remove(path): shutil.rmtree(path) return True except (OSError, IOError) as exc: - raise CommandExecutionError( - 'Could not remove \'{0}\': {1}'.format(path, exc) - ) + raise CommandExecutionError("Could not remove '{0}': {1}".format(path, exc)) return False def directory_exists(path): - ''' + """ Tests to see if path is a valid directory. Returns True/False. CLI Example: @@ -3809,12 +3869,12 @@ def directory_exists(path): salt '*' file.directory_exists /etc - ''' + """ return os.path.isdir(os.path.expanduser(path)) def file_exists(path): - ''' + """ Tests to see if path is a valid file. Returns True/False. CLI Example: @@ -3823,12 +3883,12 @@ def file_exists(path): salt '*' file.file_exists /etc/passwd - ''' + """ return os.path.isfile(os.path.expanduser(path)) def path_exists_glob(path): - ''' + """ Tests to see if path after expansion is a valid path (file or directory). Expansion allows usage of ? * and character ranges []. Tilde expansion is not supported. Returns True/False. @@ -3841,12 +3901,12 @@ def path_exists_glob(path): salt '*' file.path_exists_glob /etc/pam*/pass* - ''' + """ return True if glob.glob(os.path.expanduser(path)) else False def restorecon(path, recursive=False): - ''' + """ Reset the SELinux context on a given path CLI Example: @@ -3854,16 +3914,16 @@ def restorecon(path, recursive=False): .. code-block:: bash salt '*' file.restorecon /home/user/.ssh/authorized_keys - ''' + """ if recursive: - cmd = ['restorecon', '-FR', path] + cmd = ["restorecon", "-FR", path] else: - cmd = ['restorecon', '-F', path] - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = ["restorecon", "-F", path] + return not __salt__["cmd.retcode"](cmd, python_shell=False) def get_selinux_context(path): - ''' + """ Get an SELinux context from a given path CLI Example: @@ -3871,25 +3931,21 @@ def get_selinux_context(path): .. code-block:: bash salt '*' file.get_selinux_context /etc/hosts - ''' - out = __salt__['cmd.run'](['ls', '-Z', path], python_shell=False) + """ + cmd_ret = __salt__["cmd.run_all"](["stat", "-c", "%C", path], python_shell=False) - try: - ret = re.search(r'\w+:\w+:\w+:\w+', out).group(0) - except AttributeError: - ret = ( - 'No selinux context information is available for {0}'.format(path) - ) + if cmd_ret["retcode"] == 0: + ret = cmd_ret["stdout"] + else: + ret = "No selinux context information is available for {0}".format(path) return ret -def set_selinux_context(path, - user=None, - role=None, - type=None, # pylint: disable=W0622 - range=None): # pylint: disable=W0622 - ''' +def set_selinux_context( + path, user=None, role=None, type=None, range=None # pylint: disable=W0622 +): # pylint: disable=W0622 + """ Set a specific SELinux label on a given path CLI Example: @@ -3898,22 +3954,22 @@ def set_selinux_context(path, salt '*' file.set_selinux_context path salt '*' file.set_selinux_context /etc/yum.repos.d/epel.repo system_u object_r system_conf_t s0 - ''' + """ if not any((user, role, type, range)): return False - cmd = ['chcon'] + cmd = ["chcon"] if user: - cmd.extend(['-u', user]) + cmd.extend(["-u", user]) if role: - cmd.extend(['-r', role]) + cmd.extend(["-r", role]) if type: - cmd.extend(['-t', type]) + cmd.extend(["-t", type]) if range: - cmd.extend(['-l', range]) + cmd.extend(["-l", range]) cmd.append(path) - ret = not __salt__['cmd.retcode'](cmd, python_shell=False) + ret = not __salt__["cmd.retcode"](cmd, python_shell=False) if ret: return get_selinux_context(path) else: @@ -3921,7 +3977,7 @@ def set_selinux_context(path, def source_list(source, source_hash, saltenv): - ''' + """ Check the source list and return the source to use CLI Example: @@ -3929,23 +3985,23 @@ def source_list(source, source_hash, saltenv): .. code-block:: bash salt '*' file.source_list salt://http/httpd.conf '{hash_type: 'md5', 'hsum': }' base - ''' - contextkey = '{0}_|-{1}_|-{2}'.format(source, source_hash, saltenv) + """ + contextkey = "{0}_|-{1}_|-{2}".format(source, source_hash, saltenv) if contextkey in __context__: return __context__[contextkey] # get the master file list if isinstance(source, list): - mfiles = [(f, saltenv) for f in __salt__['cp.list_master'](saltenv)] - mdirs = [(d, saltenv) for d in __salt__['cp.list_master_dirs'](saltenv)] + mfiles = [(f, saltenv) for f in __salt__["cp.list_master"](saltenv)] + mdirs = [(d, saltenv) for d in __salt__["cp.list_master_dirs"](saltenv)] for single in source: if isinstance(single, dict): single = next(iter(single)) path, senv = salt.utils.url.parse(single) if senv: - mfiles += [(f, senv) for f in __salt__['cp.list_master'](senv)] - mdirs += [(d, senv) for d in __salt__['cp.list_master_dirs'](senv)] + mfiles += [(f, senv) for f in __salt__["cp.list_master"](senv)] + mdirs += [(d, senv) for d in __salt__["cp.list_master_dirs"](senv)] ret = None for single in source: @@ -3965,24 +4021,27 @@ def source_list(source, source_hash, saltenv): # drive letter instead of the protocol. So, we'll add the # protocol and re-parse if urlparsed_single_src.scheme.lower() in string.ascii_lowercase: - urlparsed_single_src = _urlparse('file://' + single_src) + urlparsed_single_src = _urlparse("file://" + single_src) proto = urlparsed_single_src.scheme - if proto == 'salt': + if proto == "salt": path, senv = salt.utils.url.parse(single_src) if not senv: senv = saltenv if (path, saltenv) in mfiles or (path, saltenv) in mdirs: ret = (single_src, single_hash) break - elif proto.startswith('http') or proto == 'ftp': + elif proto.startswith("http") or proto == "ftp": ret = (single_src, single_hash) break - elif proto == 'file' and ( - os.path.exists(urlparsed_single_src.netloc) or - os.path.exists(urlparsed_single_src.path) or - os.path.exists(os.path.join( - urlparsed_single_src.netloc, - urlparsed_single_src.path))): + elif proto == "file" and ( + os.path.exists(urlparsed_single_src.netloc) + or os.path.exists(urlparsed_single_src.path) + or os.path.exists( + os.path.join( + urlparsed_single_src.netloc, urlparsed_single_src.path + ) + ) + ): ret = (single_src, single_hash) break elif single_src.startswith(os.sep) and os.path.exists(single_src): @@ -4002,17 +4061,18 @@ def source_list(source, source_hash, saltenv): # drive letter instead of the protocol. So, we'll add the # protocol and re-parse if urlparsed_src.scheme.lower() in string.ascii_lowercase: - urlparsed_src = _urlparse('file://' + single) + urlparsed_src = _urlparse("file://" + single) proto = urlparsed_src.scheme - if proto == 'file' and ( - os.path.exists(urlparsed_src.netloc) or - os.path.exists(urlparsed_src.path) or - os.path.exists(os.path.join( - urlparsed_src.netloc, - urlparsed_src.path))): + if proto == "file" and ( + os.path.exists(urlparsed_src.netloc) + or os.path.exists(urlparsed_src.path) + or os.path.exists( + os.path.join(urlparsed_src.netloc, urlparsed_src.path) + ) + ): ret = (single, source_hash) break - elif proto.startswith('http') or proto == 'ftp': + elif proto.startswith("http") or proto == "ftp": ret = (single, source_hash) break elif single.startswith(os.sep) and os.path.exists(single): @@ -4020,9 +4080,7 @@ def source_list(source, source_hash, saltenv): break if ret is None: # None of the list items matched - raise CommandExecutionError( - 'none of the specified sources were found' - ) + raise CommandExecutionError("none of the specified sources were found") else: ret = (source, source_hash) @@ -4030,13 +4088,8 @@ def source_list(source, source_hash, saltenv): return ret -def apply_template_on_contents( - contents, - template, - context, - defaults, - saltenv): - ''' +def apply_template_on_contents(contents, template, context, defaults, saltenv): + """ Return the contents after applying the templating engine contents @@ -4060,7 +4113,7 @@ def apply_template_on_contents( template=jinja \\ "context={}" "defaults={'template': 'cool'}" \\ saltenv=base - ''' + """ if template in salt.utils.templates.TEMPLATE_REGISTRY: context_dict = defaults if defaults else {} if context: @@ -4072,40 +4125,43 @@ def apply_template_on_contents( to_str=True, context=context_dict, saltenv=saltenv, - grains=__opts__['grains'], + grains=__opts__["grains"], pillar=__pillar__, salt=__salt__, - opts=__opts__)['data'] + opts=__opts__, + )["data"] if six.PY2: - contents = contents.encode('utf-8') + contents = contents.encode("utf-8") elif six.PY3 and isinstance(contents, bytes): # bytes -> str - contents = contents.decode('utf-8') + contents = contents.decode("utf-8") else: ret = {} - ret['result'] = False - ret['comment'] = ('Specified template format {0} is not supported' - ).format(template) + ret["result"] = False + ret["comment"] = ("Specified template format {0} is not supported").format( + template + ) return ret return contents def get_managed( - name, - template, - source, - source_hash, - source_hash_name, - user, - group, - mode, - attrs, - saltenv, - context, - defaults, - skip_verify=False, - **kwargs): - ''' + name, + template, + source, + source_hash, + source_hash_name, + user, + group, + mode, + attrs, + saltenv, + context, + defaults, + skip_verify=False, + **kwargs +): + """ Return the managed file data for file.managed name @@ -4158,17 +4214,17 @@ def get_managed( .. code-block:: bash salt '*' file.get_managed /etc/httpd/conf.d/httpd.conf jinja salt://http/httpd.conf '{hash_type: 'md5', 'hsum': }' None root root '755' base None None - ''' + """ # Copy the file to the minion and templatize it - sfn = '' + sfn = "" source_sum = {} def _get_local_file_source_sum(path): - ''' + """ DRY helper for getting the source_sum value from a locally cached path. - ''' - return {'hsum': get_hash(path, form='sha256'), 'hash_type': 'sha256'} + """ + return {"hsum": get_hash(path, form="sha256"), "hash_type": "sha256"} # If we have a source defined, let's figure out what the hash is if source: @@ -4176,30 +4232,37 @@ def get_managed( if urlparsed_source.scheme in salt.utils.files.VALID_PROTOS: parsed_scheme = urlparsed_source.scheme else: - parsed_scheme = '' + parsed_scheme = "" parsed_path = os.path.join( - urlparsed_source.netloc, urlparsed_source.path).rstrip(os.sep) - unix_local_source = parsed_scheme in ('file', '') + urlparsed_source.netloc, urlparsed_source.path + ).rstrip(os.sep) + unix_local_source = parsed_scheme in ("file", "") - if parsed_scheme == '': + if parsed_scheme == "": parsed_path = sfn = source if not os.path.exists(sfn): - msg = 'Local file source {0} does not exist'.format(sfn) - return '', {}, msg - elif parsed_scheme == 'file': + msg = "Local file source {0} does not exist".format(sfn) + return "", {}, msg + elif parsed_scheme == "file": sfn = parsed_path if not os.path.exists(sfn): - msg = 'Local file source {0} does not exist'.format(sfn) - return '', {}, msg + msg = "Local file source {0} does not exist".format(sfn) + return "", {}, msg if parsed_scheme and parsed_scheme.lower() in string.ascii_lowercase: - parsed_path = ':'.join([parsed_scheme, parsed_path]) - parsed_scheme = 'file' + parsed_path = ":".join([parsed_scheme, parsed_path]) + parsed_scheme = "file" - if parsed_scheme == 'salt': - source_sum = __salt__['cp.hash_file'](source, saltenv) + if parsed_scheme == "salt": + source_sum = __salt__["cp.hash_file"](source, saltenv) if not source_sum: - return '', {}, 'Source file {0} not found in saltenv \'{1}\''.format(source, saltenv) + return ( + "", + {}, + "Source file {0} not found in saltenv '{1}'".format( + source, saltenv + ), + ) elif not source_hash and unix_local_source: source_sum = _get_local_file_source_sum(parsed_path) elif not source_hash and source.startswith(os.sep): @@ -4209,34 +4272,33 @@ def get_managed( if not skip_verify: if source_hash: try: - source_sum = get_source_sum(name, - source, - source_hash, - source_hash_name, - saltenv) + source_sum = get_source_sum( + name, source, source_hash, source_hash_name, saltenv + ) except CommandExecutionError as exc: - return '', {}, exc.strerror + return "", {}, exc.strerror else: msg = ( - 'Unable to verify upstream hash of source file {0}, ' - 'please set source_hash or set skip_verify to True' - .format(salt.utils.url.redact_http_basic_auth(source)) + "Unable to verify upstream hash of source file {0}, " + "please set source_hash or set skip_verify to True".format( + salt.utils.url.redact_http_basic_auth(source) + ) ) - return '', {}, msg + return "", {}, msg if source and (template or parsed_scheme in salt.utils.files.REMOTE_PROTOS): # Check if we have the template or remote file cached cache_refetch = False - cached_dest = __salt__['cp.is_cached'](source, saltenv) + cached_dest = __salt__["cp.is_cached"](source, saltenv) if cached_dest and (source_hash or skip_verify): - htype = source_sum.get('hash_type', 'sha256') + htype = source_sum.get("hash_type", "sha256") cached_sum = get_hash(cached_dest, form=htype) if skip_verify: # prev: if skip_verify or cached_sum == source_sum['hsum']: # but `cached_sum == source_sum['hsum']` is elliptical as prev if sfn = cached_dest - source_sum = {'hsum': cached_sum, 'hash_type': htype} - elif cached_sum != source_sum.get('hsum', __opts__['hash_type']): + source_sum = {"hsum": cached_sum, "hash_type": htype} + elif cached_sum != source_sum.get("hsum", __opts__["hash_type"]): cache_refetch = True else: sfn = cached_dest @@ -4245,25 +4307,22 @@ def get_managed( # updated and the cache has to be refreshed, download the file. if not sfn or cache_refetch: try: - sfn = __salt__['cp.cache_file']( - source, - saltenv, - source_hash=source_sum.get('hsum')) + sfn = __salt__["cp.cache_file"]( + source, saltenv, source_hash=source_sum.get("hsum") + ) except Exception as exc: # pylint: disable=broad-except # A 404 or other error code may raise an exception, catch it # and return a comment that will fail the calling state. _source = salt.utils.url.redact_http_basic_auth(source) - return '', {}, 'Failed to cache {0}: {1}'.format(_source, exc) + return "", {}, "Failed to cache {0}: {1}".format(_source, exc) # If cache failed, sfn will be False, so do a truth check on sfn first # as invoking os.path.exists() on a bool raises a TypeError. if not sfn or not os.path.exists(sfn): _source = salt.utils.url.redact_http_basic_auth(source) - return sfn, {}, 'Source file \'{0}\' not found'.format(_source) + return sfn, {}, "Source file '{0}' not found".format(_source) if sfn == name: - raise SaltInvocationError( - 'Source file cannot be the same as destination' - ) + raise SaltInvocationError("Source file cannot be the same as destination") if template: if template in salt.utils.templates.TEMPLATE_REGISTRY: @@ -4282,31 +4341,32 @@ def get_managed( context=context_dict, salt=__salt__, pillar=__pillar__, - grains=__opts__['grains'], + grains=__opts__["grains"], opts=__opts__, - **kwargs) + **kwargs + ) else: - return sfn, {}, ('Specified template format {0} is not supported' - ).format(template) + return ( + sfn, + {}, + ("Specified template format {0} is not supported").format(template), + ) - if data['result']: - sfn = data['data'] - hsum = get_hash(sfn, form='sha256') - source_sum = {'hash_type': 'sha256', - 'hsum': hsum} + if data["result"]: + sfn = data["data"] + hsum = get_hash(sfn, form="sha256") + source_sum = {"hash_type": "sha256", "hsum": hsum} else: __clean_tmp(sfn) - return sfn, {}, data['data'] + return sfn, {}, data["data"] - return sfn, source_sum, '' + return sfn, source_sum, "" -def extract_hash(hash_fn, - hash_type='sha256', - file_name='', - source='', - source_hash_name=None): - ''' +def extract_hash( + hash_fn, hash_type="sha256", file_name="", source="", source_hash_name=None +): + """ .. versionchanged:: 2016.3.5 Prior to this version, only the ``file_name`` argument was considered for filename matches in the hash file. This would be problematic for @@ -4352,30 +4412,31 @@ def extract_hash(hash_fn, .. code-block:: bash salt '*' file.extract_hash /path/to/hash/file sha512 /etc/foo - ''' + """ hash_len = HASHES.get(hash_type) if hash_len is None: if hash_type: log.warning( - 'file.extract_hash: Unsupported hash_type \'%s\', falling ' - 'back to matching any supported hash_type', hash_type + "file.extract_hash: Unsupported hash_type '%s', falling " + "back to matching any supported hash_type", + hash_type, ) - hash_type = '' - hash_len_expr = '{0},{1}'.format(min(HASHES_REVMAP), max(HASHES_REVMAP)) + hash_type = "" + hash_len_expr = "{0},{1}".format(min(HASHES_REVMAP), max(HASHES_REVMAP)) else: hash_len_expr = six.text_type(hash_len) - filename_separators = string.whitespace + r'\/' + filename_separators = string.whitespace + r"\/" if source_hash_name: if not isinstance(source_hash_name, six.string_types): source_hash_name = six.text_type(source_hash_name) source_hash_name_idx = (len(source_hash_name) + 1) * -1 log.debug( - 'file.extract_hash: Extracting %s hash for file matching ' - 'source_hash_name \'%s\'', - 'any supported' if not hash_type else hash_type, - source_hash_name + "file.extract_hash: Extracting %s hash for file matching " + "source_hash_name '%s'", + "any supported" if not hash_type else hash_type, + source_hash_name, ) if file_name: if not isinstance(file_name, six.string_types): @@ -4394,22 +4455,22 @@ def extract_hash(hash_fn, basename_searches = [x for x in (file_name, source) if x] if basename_searches: log.debug( - 'file.extract_hash: %s %s hash for file matching%s: %s', - 'If no source_hash_name match found, will extract' - if source_hash_name - else 'Extracting', - 'any supported' if not hash_type else hash_type, - '' if len(basename_searches) == 1 else ' either of the following', - ', '.join(basename_searches) + "file.extract_hash: %s %s hash for file matching%s: %s", + "If no source_hash_name match found, will extract" + if source_hash_name + else "Extracting", + "any supported" if not hash_type else hash_type, + "" if len(basename_searches) == 1 else " either of the following", + ", ".join(basename_searches), ) partial = None found = {} - with salt.utils.files.fopen(hash_fn, 'r') as fp_: + with salt.utils.files.fopen(hash_fn, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line.strip()) - hash_re = r'(?i)(? 1: log.debug( - 'file.extract_hash: Multiple %s matches for %s: %s', + "file.extract_hash: Multiple %s matches for %s: %s", found_type, found_str, - ', '.join( - ['{0} ({1})'.format(x['hsum'], x['hash_type']) - for x in found[found_type]] - ) + ", ".join( + [ + "{0} ({1})".format(x["hsum"], x["hash_type"]) + for x in found[found_type] + ] + ), ) ret = found[found_type][0] log.debug( - 'file.extract_hash: Returning %s hash \'%s\' as a match of %s', - ret['hash_type'], ret['hsum'], found_str + "file.extract_hash: Returning %s hash '%s' as a match of %s", + ret["hash_type"], + ret["hsum"], + found_str, ) return ret if partial: log.debug( - 'file.extract_hash: Returning the partially identified %s hash ' - '\'%s\'', partial['hash_type'], partial['hsum'] + "file.extract_hash: Returning the partially identified %s hash " "'%s'", + partial["hash_type"], + partial["hsum"], ) return partial - log.debug('file.extract_hash: No matches, returning None') + log.debug("file.extract_hash: No matches, returning None") return None def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False): - ''' + """ Check the permissions on files, modify attributes and chown if needed. File attributes are only verified if lsattr(1) is installed. @@ -4544,25 +4619,22 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False) .. versionchanged:: 2014.1.3 ``follow_symlinks`` option added - ''' + """ name = os.path.expanduser(name) if not ret: - ret = {'name': name, - 'changes': {}, - 'comment': [], - 'result': True} - orig_comment = '' + ret = {"name": name, "changes": {}, "comment": [], "result": True} + orig_comment = "" else: - orig_comment = ret['comment'] - ret['comment'] = [] + orig_comment = ret["comment"] + ret["comment"] = [] # Check permissions perms = {} cur = stats(name, follow_symlinks=follow_symlinks) - perms['luser'] = cur['user'] - perms['lgroup'] = cur['group'] - perms['lmode'] = salt.utils.files.normalize_mode(cur['mode']) + perms["luser"] = cur["user"] + perms["lgroup"] = cur["group"] + perms["lmode"] = salt.utils.files.normalize_mode(cur["mode"]) is_dir = os.path.isdir(name) is_link = os.path.islink(name) @@ -4571,81 +4643,80 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False) if user: if isinstance(user, int): user = uid_to_user(user) - if (salt.utils.platform.is_windows() and - user_to_uid(user) != user_to_uid(perms['luser']) - ) or ( - not salt.utils.platform.is_windows() and user != perms['luser'] - ): - perms['cuser'] = user + if ( + salt.utils.platform.is_windows() + and user_to_uid(user) != user_to_uid(perms["luser"]) + ) or (not salt.utils.platform.is_windows() and user != perms["luser"]): + perms["cuser"] = user if group: if isinstance(group, int): group = gid_to_group(group) - if (salt.utils.platform.is_windows() and - group_to_gid(group) != group_to_gid(perms['lgroup']) - ) or ( - not salt.utils.platform.is_windows() and group != perms['lgroup'] - ): - perms['cgroup'] = group + if ( + salt.utils.platform.is_windows() + and group_to_gid(group) != group_to_gid(perms["lgroup"]) + ) or (not salt.utils.platform.is_windows() and group != perms["lgroup"]): + perms["cgroup"] = group - if 'cuser' in perms or 'cgroup' in perms: - if not __opts__['test']: + if "cuser" in perms or "cgroup" in perms: + if not __opts__["test"]: if os.path.islink(name) and not follow_symlinks: chown_func = lchown else: chown_func = chown if user is None: - user = perms['luser'] + user = perms["luser"] if group is None: - group = perms['lgroup'] + group = perms["lgroup"] try: chown_func(name, user, group) # Python os.chown() does reset the suid and sgid, # that's why setting the right mode again is needed here. set_mode(name, mode) except OSError: - ret['result'] = False + ret["result"] = False if user: if isinstance(user, int): user = uid_to_user(user) - if (salt.utils.platform.is_windows() and - user_to_uid(user) != user_to_uid( - get_user(name, follow_symlinks=follow_symlinks)) and - user != '' - ) or ( - not salt.utils.platform.is_windows() and - user != get_user(name, follow_symlinks=follow_symlinks) and - user != '' + if ( + salt.utils.platform.is_windows() + and user_to_uid(user) + != user_to_uid(get_user(name, follow_symlinks=follow_symlinks)) + and user != "" + ) or ( + not salt.utils.platform.is_windows() + and user != get_user(name, follow_symlinks=follow_symlinks) + and user != "" ): - if __opts__['test'] is True: - ret['changes']['user'] = user + if __opts__["test"] is True: + ret["changes"]["user"] = user else: - ret['result'] = False - ret['comment'].append('Failed to change user to {0}' - .format(user)) - elif 'cuser' in perms and user != '': - ret['changes']['user'] = user + ret["result"] = False + ret["comment"].append("Failed to change user to {0}".format(user)) + elif "cuser" in perms and user != "": + ret["changes"]["user"] = user if group: if isinstance(group, int): group = gid_to_group(group) - if (salt.utils.platform.is_windows() and - group_to_gid(group) != group_to_gid( - get_group(name, follow_symlinks=follow_symlinks)) and - user != '') or ( - not salt.utils.platform.is_windows() and - group != get_group(name, follow_symlinks=follow_symlinks) and - user != '' + if ( + salt.utils.platform.is_windows() + and group_to_gid(group) + != group_to_gid(get_group(name, follow_symlinks=follow_symlinks)) + and user != "" + ) or ( + not salt.utils.platform.is_windows() + and group != get_group(name, follow_symlinks=follow_symlinks) + and user != "" ): - if __opts__['test'] is True: - ret['changes']['group'] = group + if __opts__["test"] is True: + ret["changes"]["group"] = group else: - ret['result'] = False - ret['comment'].append('Failed to change group to {0}' - .format(group)) - elif 'cgroup' in perms and user != '': - ret['changes']['group'] = group + ret["result"] = False + ret["comment"].append("Failed to change group to {0}".format(group)) + elif "cgroup" in perms and user != "": + ret["changes"]["group"] = group # Mode changes if needed if mode is not None: @@ -4655,18 +4726,18 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False) pass else: mode = salt.utils.files.normalize_mode(mode) - if mode != perms['lmode']: - if __opts__['test'] is True: - ret['changes']['mode'] = mode + if mode != perms["lmode"]: + if __opts__["test"] is True: + ret["changes"]["mode"] = mode else: set_mode(name, mode) if mode != salt.utils.files.normalize_mode(get_mode(name)): - ret['result'] = False - ret['comment'].append( - 'Failed to change mode to {0}'.format(mode) + ret["result"] = False + ret["comment"].append( + "Failed to change mode to {0}".format(mode) ) else: - ret['changes']['mode'] = mode + ret["changes"]["mode"] = mode # Modify attributes of file if needed if attrs is not None and not is_dir: @@ -4678,68 +4749,65 @@ def check_perms(name, ret, user, group, mode, attrs=None, follow_symlinks=False) diff_attrs = _cmp_attrs(name, attrs) if diff_attrs and any(attr for attr in diff_attrs): changes = { - 'old': ''.join(lsattr(name)[name]), - 'new': None, + "old": "".join(lsattr(name)[name]), + "new": None, } - if __opts__['test'] is True: - changes['new'] = attrs + if __opts__["test"] is True: + changes["new"] = attrs else: if diff_attrs.added: chattr( - name, - operator="add", - attributes=diff_attrs.added, + name, operator="add", attributes=diff_attrs.added, ) if diff_attrs.removed: chattr( - name, - operator="remove", - attributes=diff_attrs.removed, + name, operator="remove", attributes=diff_attrs.removed, ) cmp_attrs = _cmp_attrs(name, attrs) if any(attr for attr in cmp_attrs): - ret['result'] = False - ret['comment'].append( - 'Failed to change attributes to {0}'.format(attrs) + ret["result"] = False + ret["comment"].append( + "Failed to change attributes to {0}".format(attrs) ) - changes['new'] = ''.join(lsattr(name)[name]) + changes["new"] = "".join(lsattr(name)[name]) else: - changes['new'] = attrs - if changes['old'] != changes['new']: - ret['changes']['attrs'] = changes + changes["new"] = attrs + if changes["old"] != changes["new"]: + ret["changes"]["attrs"] = changes # Only combine the comment list into a string # after all comments are added above if isinstance(orig_comment, six.string_types): if orig_comment: - ret['comment'].insert(0, orig_comment) - ret['comment'] = '; '.join(ret['comment']) + ret["comment"].insert(0, orig_comment) + ret["comment"] = "; ".join(ret["comment"]) # Set result to None at the very end of the function, # after all changes have been recorded above - if __opts__['test'] is True and ret['changes']: - ret['result'] = None + if __opts__["test"] is True and ret["changes"]: + ret["result"] = None return ret, perms def check_managed( - name, - source, - source_hash, - source_hash_name, - user, - group, - mode, - attrs, - template, - context, - defaults, - saltenv, - contents=None, - skip_verify=False, - **kwargs): - ''' + name, + source, + source_hash, + source_hash_name, + user, + group, + mode, + attrs, + template, + context, + defaults, + saltenv, + contents=None, + skip_verify=False, + **kwargs +): + """ Check to see what changes need to be made for a file CLI Example: @@ -4747,13 +4815,13 @@ def check_managed( .. code-block:: bash salt '*' file.check_managed /etc/httpd/conf.d/httpd.conf salt://http/httpd.conf '{hash_type: 'md5', 'hsum': }' root, root, '755' jinja True None None base - ''' + """ # If the source is a list then find which file exists - source, source_hash = source_list(source, # pylint: disable=W0633 - source_hash, - saltenv) + source, source_hash = source_list( + source, source_hash, saltenv # pylint: disable=W0633 + ) - sfn = '' + sfn = "" source_sum = None if contents is None: @@ -4772,45 +4840,49 @@ def check_managed( context, defaults, skip_verify, - **kwargs) + **kwargs + ) if comments: __clean_tmp(sfn) return False, comments - changes = check_file_meta(name, sfn, source, source_sum, user, - group, mode, attrs, saltenv, contents) + changes = check_file_meta( + name, sfn, source, source_sum, user, group, mode, attrs, saltenv, contents + ) # Ignore permission for files written temporary directories # Files in any path will still be set correctly using get_managed() if name.startswith(tempfile.gettempdir()): - for key in ['user', 'group', 'mode']: + for key in ["user", "group", "mode"]: changes.pop(key, None) __clean_tmp(sfn) if changes: log.info(changes) - comments = ['The following values are set to be changed:\n'] - comments.extend('{0}: {1}\n'.format(key, val) - for key, val in six.iteritems(changes)) - return None, ''.join(comments) - return True, 'The file {0} is in the correct state'.format(name) + comments = ["The following values are set to be changed:\n"] + comments.extend( + "{0}: {1}\n".format(key, val) for key, val in six.iteritems(changes) + ) + return None, "".join(comments) + return True, "The file {0} is in the correct state".format(name) def check_managed_changes( - name, - source, - source_hash, - source_hash_name, - user, - group, - mode, - attrs, - template, - context, - defaults, - saltenv, - contents=None, - skip_verify=False, - keep_mode=False, - **kwargs): - ''' + name, + source, + source_hash, + source_hash_name, + user, + group, + mode, + attrs, + template, + context, + defaults, + saltenv, + contents=None, + skip_verify=False, + keep_mode=False, + **kwargs +): + """ Return a dictionary of what changes need to be made for a file CLI Example: @@ -4818,13 +4890,13 @@ def check_managed_changes( .. code-block:: bash salt '*' file.check_managed_changes /etc/httpd/conf.d/httpd.conf salt://http/httpd.conf '{hash_type: 'md5', 'hsum': }' root, root, '755' jinja True None None base - ''' + """ # If the source is a list then find which file exists - source, source_hash = source_list(source, # pylint: disable=W0633 - source_hash, - saltenv) + source, source_hash = source_list( + source, source_hash, saltenv # pylint: disable=W0633 + ) - sfn = '' + sfn = "" source_sum = None if contents is None: @@ -4843,40 +4915,33 @@ def check_managed_changes( context, defaults, skip_verify, - **kwargs) + **kwargs + ) # Ensure that user-provided hash string is lowercase - if source_sum and ('hsum' in source_sum): - source_sum['hsum'] = source_sum['hsum'].lower() + if source_sum and ("hsum" in source_sum): + source_sum["hsum"] = source_sum["hsum"].lower() if comments: __clean_tmp(sfn) return False, comments if sfn and source and keep_mode: - if _urlparse(source).scheme in ('salt', 'file') \ - or source.startswith('/'): + if _urlparse(source).scheme in ("salt", "file") or source.startswith("/"): try: - mode = __salt__['cp.stat_file'](source, saltenv=saltenv, octal=True) + mode = __salt__["cp.stat_file"](source, saltenv=saltenv, octal=True) except Exception as exc: # pylint: disable=broad-except - log.warning('Unable to stat %s: %s', sfn, exc) - changes = check_file_meta(name, sfn, source, source_sum, user, - group, mode, attrs, saltenv, contents) + log.warning("Unable to stat %s: %s", sfn, exc) + changes = check_file_meta( + name, sfn, source, source_sum, user, group, mode, attrs, saltenv, contents + ) __clean_tmp(sfn) return changes def check_file_meta( - name, - sfn, - source, - source_sum, - user, - group, - mode, - attrs, - saltenv, - contents=None): - ''' + name, sfn, source, source_sum, user, group, mode, attrs, saltenv, contents=None +): + """ Check for the changes in the file metadata. CLI Example: @@ -4925,98 +4990,99 @@ def check_file_meta( contents File contents - ''' + """ changes = {} if not source_sum: source_sum = dict() try: - lstats = stats(name, hash_type=source_sum.get('hash_type', None), - follow_symlinks=False) + lstats = stats( + name, hash_type=source_sum.get("hash_type", None), follow_symlinks=False + ) except CommandExecutionError: lstats = {} if not lstats: - changes['newfile'] = name + changes["newfile"] = name return changes - if 'hsum' in source_sum: - if source_sum['hsum'] != lstats['sum']: + if "hsum" in source_sum: + if source_sum["hsum"] != lstats["sum"]: if not sfn and source: - sfn = __salt__['cp.cache_file']( - source, - saltenv, - source_hash=source_sum['hsum']) + sfn = __salt__["cp.cache_file"]( + source, saltenv, source_hash=source_sum["hsum"] + ) if sfn: try: - changes['diff'] = get_diff( - name, sfn, template=True, show_filenames=False) + changes["diff"] = get_diff( + name, sfn, template=True, show_filenames=False + ) except CommandExecutionError as exc: - changes['diff'] = exc.strerror + changes["diff"] = exc.strerror else: - changes['sum'] = 'Checksum differs' + changes["sum"] = "Checksum differs" if contents is not None: # Write a tempfile with the static contents - tmp = salt.utils.files.mkstemp(prefix=salt.utils.files.TEMPFILE_PREFIX, - text=True) + tmp = salt.utils.files.mkstemp( + prefix=salt.utils.files.TEMPFILE_PREFIX, text=True + ) if salt.utils.platform.is_windows(): contents = os.linesep.join( - _splitlines_preserving_trailing_newline(contents)) - with salt.utils.files.fopen(tmp, 'w') as tmp_: + _splitlines_preserving_trailing_newline(contents) + ) + with salt.utils.files.fopen(tmp, "w") as tmp_: tmp_.write(salt.utils.stringutils.to_str(contents)) # Compare the static contents with the named file try: differences = get_diff(name, tmp, show_filenames=False) except CommandExecutionError as exc: - log.error('Failed to diff files: {0}'.format(exc)) + log.error("Failed to diff files: {0}".format(exc)) differences = exc.strerror __clean_tmp(tmp) if differences: - if __salt__['config.option']('obfuscate_templates'): - changes['diff'] = '' + if __salt__["config.option"]("obfuscate_templates"): + changes["diff"] = "" else: - changes['diff'] = differences + changes["diff"] = differences if not salt.utils.platform.is_windows(): # Check owner - if (user is not None - and user != lstats['user'] - and user != lstats['uid']): - changes['user'] = user + if user is not None and user != lstats["user"] and user != lstats["uid"]: + changes["user"] = user # Check group - if (group is not None - and group != lstats['group'] - and group != lstats['gid']): - changes['group'] = group + if group is not None and group != lstats["group"] and group != lstats["gid"]: + changes["group"] = group # Normalize the file mode - smode = salt.utils.files.normalize_mode(lstats['mode']) + smode = salt.utils.files.normalize_mode(lstats["mode"]) mode = salt.utils.files.normalize_mode(mode) if mode is not None and mode != smode: - changes['mode'] = mode + changes["mode"] = mode if attrs: diff_attrs = _cmp_attrs(name, attrs) if diff_attrs is not None: - if attrs is not None \ - and (diff_attrs[0] is not None - or diff_attrs[1] is not None): - changes['attrs'] = attrs + if attrs is not None and ( + diff_attrs[0] is not None or diff_attrs[1] is not None + ): + changes["attrs"] = attrs return changes -def get_diff(file1, - file2, - saltenv='base', - show_filenames=True, - show_changes=True, - template=False, - source_hash_file1=None, - source_hash_file2=None): - ''' +def get_diff( + file1, + file2, + saltenv="base", + show_filenames=True, + show_changes=True, + template=False, + source_hash_file1=None, + source_hash_file2=None, +): + """ Return unified diff of two files file1 @@ -5072,7 +5138,7 @@ def get_diff(file1, salt '*' file.get_diff /home/fred/.vimrc salt://users/fred/.vimrc salt '*' file.get_diff /tmp/foo.txt /tmp/bar.txt - ''' + """ files = (file1, file2) source_hashes = (source_hash_file1, source_hash_file2) paths = [] @@ -5082,12 +5148,12 @@ def get_diff(file1, try: # Local file paths will just return the same path back when passed # to cp.cache_file. - cached_path = __salt__['cp.cache_file'](filename, - saltenv, - source_hash=source_hash) + cached_path = __salt__["cp.cache_file"]( + filename, saltenv, source_hash=source_hash + ) if cached_path is False: errors.append( - 'File {0} not found'.format( + "File {0} not found".format( salt.utils.stringutils.to_unicode(filename) ) ) @@ -5098,29 +5164,25 @@ def get_diff(file1, continue if errors: - raise CommandExecutionError( - 'Failed to cache one or more files', - info=errors - ) + raise CommandExecutionError("Failed to cache one or more files", info=errors) args = [] for filename in paths: try: - with salt.utils.files.fopen(filename, 'rb') as fp_: + with salt.utils.files.fopen(filename, "rb") as fp_: args.append(fp_.readlines()) except (IOError, OSError) as exc: raise CommandExecutionError( - 'Failed to read {0}: {1}'.format( - salt.utils.stringutils.to_unicode(filename), - exc.strerror + "Failed to read {0}: {1}".format( + salt.utils.stringutils.to_unicode(filename), exc.strerror ) ) if args[0] != args[1]: - if template and __salt__['config.option']('obfuscate_templates'): - ret = '' + if template and __salt__["config.option"]("obfuscate_templates"): + ret = "" elif not show_changes: - ret = '' + ret = "" else: bdiff = _binary_replace(*paths) # pylint: disable=no-value-for-parameter if bdiff: @@ -5128,34 +5190,36 @@ def get_diff(file1, else: if show_filenames: args.extend(paths) - ret = __utils__['stringutils.get_diff'](*args) + ret = __utils__["stringutils.get_diff"](*args) return ret - return '' + return "" -def manage_file(name, - sfn, - ret, - source, - source_sum, - user, - group, - mode, - attrs, - saltenv, - backup, - makedirs=False, - template=None, # pylint: disable=W0613 - show_changes=True, - contents=None, - dir_mode=None, - follow_symlinks=True, - skip_verify=False, - keep_mode=False, - encoding=None, - encoding_errors='strict', - **kwargs): - ''' +def manage_file( + name, + sfn, + ret, + source, + source_sum, + user, + group, + mode, + attrs, + saltenv, + backup, + makedirs=False, + template=None, # pylint: disable=W0613 + show_changes=True, + contents=None, + dir_mode=None, + follow_symlinks=True, + skip_verify=False, + keep_mode=False, + encoding=None, + encoding_errors="strict", + **kwargs +): + """ Checks the destination against what was retrieved with get_managed and makes the appropriate modifications (if necessary). @@ -5256,38 +5320,31 @@ def manage_file(name, .. versionchanged:: 2014.7.0 ``follow_symlinks`` option added - ''' + """ name = os.path.expanduser(name) if not ret: - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + ret = {"name": name, "changes": {}, "comment": "", "result": True} # Ensure that user-provided hash string is lowercase - if source_sum and ('hsum' in source_sum): - source_sum['hsum'] = source_sum['hsum'].lower() + if source_sum and ("hsum" in source_sum): + source_sum["hsum"] = source_sum["hsum"].lower() if source: if not sfn: # File is not present, cache it - sfn = __salt__['cp.cache_file'](source, saltenv) + sfn = __salt__["cp.cache_file"](source, saltenv) if not sfn: - return _error( - ret, 'Source file \'{0}\' not found'.format(source)) - htype = source_sum.get('hash_type', __opts__['hash_type']) + return _error(ret, "Source file '{0}' not found".format(source)) + htype = source_sum.get("hash_type", __opts__["hash_type"]) # Recalculate source sum now that file has been cached - source_sum = { - 'hash_type': htype, - 'hsum': get_hash(sfn, form=htype) - } + source_sum = {"hash_type": htype, "hsum": get_hash(sfn, form=htype)} if keep_mode: - if _urlparse(source).scheme in ('salt', 'file', ''): + if _urlparse(source).scheme in ("salt", "file", ""): try: - mode = __salt__['cp.stat_file'](source, saltenv=saltenv, octal=True) + mode = __salt__["cp.stat_file"](source, saltenv=saltenv, octal=True) except Exception as exc: # pylint: disable=broad-except - log.warning('Unable to stat %s: %s', sfn, exc) + log.warning("Unable to stat %s: %s", sfn, exc) # Check changes if the target file exists if os.path.isfile(name) or os.path.islink(name): @@ -5298,138 +5355,145 @@ def manage_file(name, # Only test the checksums on files with managed contents if source and not (not follow_symlinks and os.path.islink(real_name)): - name_sum = get_hash(real_name, source_sum.get('hash_type', __opts__['hash_type'])) + name_sum = get_hash( + real_name, source_sum.get("hash_type", __opts__["hash_type"]) + ) else: name_sum = None # Check if file needs to be replaced - if source and (name_sum is None or source_sum.get('hsum', __opts__['hash_type']) != name_sum): + if source and ( + name_sum is None + or source_sum.get("hsum", __opts__["hash_type"]) != name_sum + ): if not sfn: - sfn = __salt__['cp.cache_file'](source, saltenv) + sfn = __salt__["cp.cache_file"](source, saltenv) if not sfn: - return _error( - ret, 'Source file \'{0}\' not found'.format(source)) + return _error(ret, "Source file '{0}' not found".format(source)) # If the downloaded file came from a non salt server or local # source, and we are not skipping checksum verification, then # verify that it matches the specified checksum. - if not skip_verify \ - and _urlparse(source).scheme != 'salt': - dl_sum = get_hash(sfn, source_sum['hash_type']) - if dl_sum != source_sum['hsum']: - ret['comment'] = ( - 'Specified {0} checksum for {1} ({2}) does not match ' - 'actual checksum ({3}). If the \'source_hash\' value ' - 'refers to a remote file with multiple possible ' - 'matches, then it may be necessary to set ' - '\'source_hash_name\'.'.format( - source_sum['hash_type'], - source, - source_sum['hsum'], - dl_sum + if not skip_verify and _urlparse(source).scheme != "salt": + dl_sum = get_hash(sfn, source_sum["hash_type"]) + if dl_sum != source_sum["hsum"]: + ret["comment"] = ( + "Specified {0} checksum for {1} ({2}) does not match " + "actual checksum ({3}). If the 'source_hash' value " + "refers to a remote file with multiple possible " + "matches, then it may be necessary to set " + "'source_hash_name'.".format( + source_sum["hash_type"], source, source_sum["hsum"], dl_sum ) ) - ret['result'] = False + ret["result"] = False return ret # Print a diff equivalent to diff -u old new - if __salt__['config.option']('obfuscate_templates'): - ret['changes']['diff'] = '' + if __salt__["config.option"]("obfuscate_templates"): + ret["changes"]["diff"] = "" elif not show_changes: - ret['changes']['diff'] = '' + ret["changes"]["diff"] = "" else: try: - ret['changes']['diff'] = get_diff( - real_name, sfn, show_filenames=False) + ret["changes"]["diff"] = get_diff( + real_name, sfn, show_filenames=False + ) except CommandExecutionError as exc: - ret['changes']['diff'] = exc.strerror + ret["changes"]["diff"] = exc.strerror # Pre requisites are met, and the file needs to be replaced, do it try: - salt.utils.files.copyfile(sfn, - real_name, - __salt__['config.backup_mode'](backup), - __opts__['cachedir']) + salt.utils.files.copyfile( + sfn, + real_name, + __salt__["config.backup_mode"](backup), + __opts__["cachedir"], + ) except IOError as io_error: __clean_tmp(sfn) - return _error( - ret, 'Failed to commit change: {0}'.format(io_error)) + return _error(ret, "Failed to commit change: {0}".format(io_error)) if contents is not None: # Write the static contents to a temporary file - tmp = salt.utils.files.mkstemp(prefix=salt.utils.files.TEMPFILE_PREFIX, - text=True) - with salt.utils.files.fopen(tmp, 'wb') as tmp_: + tmp = salt.utils.files.mkstemp( + prefix=salt.utils.files.TEMPFILE_PREFIX, text=True + ) + with salt.utils.files.fopen(tmp, "wb") as tmp_: if encoding: if salt.utils.platform.is_windows(): contents = os.linesep.join( - _splitlines_preserving_trailing_newline(contents)) - log.debug('File will be encoded with %s', encoding) - tmp_.write(contents.encode(encoding=encoding, errors=encoding_errors)) + _splitlines_preserving_trailing_newline(contents) + ) + log.debug("File will be encoded with %s", encoding) + tmp_.write( + contents.encode(encoding=encoding, errors=encoding_errors) + ) else: tmp_.write(salt.utils.stringutils.to_bytes(contents)) try: differences = get_diff( - real_name, tmp, show_filenames=False, - show_changes=show_changes, template=True) + real_name, + tmp, + show_filenames=False, + show_changes=show_changes, + template=True, + ) except CommandExecutionError as exc: - ret.setdefault('warnings', []).append( - 'Failed to detect changes to file: {0}'.format(exc.strerror) + ret.setdefault("warnings", []).append( + "Failed to detect changes to file: {0}".format(exc.strerror) ) - differences = '' + differences = "" if differences: - ret['changes']['diff'] = differences + ret["changes"]["diff"] = differences # Pre requisites are met, the file needs to be replaced, do it try: - salt.utils.files.copyfile(tmp, - real_name, - __salt__['config.backup_mode'](backup), - __opts__['cachedir']) + salt.utils.files.copyfile( + tmp, + real_name, + __salt__["config.backup_mode"](backup), + __opts__["cachedir"], + ) except IOError as io_error: __clean_tmp(tmp) - return _error( - ret, 'Failed to commit change: {0}'.format(io_error)) + return _error(ret, "Failed to commit change: {0}".format(io_error)) __clean_tmp(tmp) # Check for changing symlink to regular file here if os.path.islink(name) and not follow_symlinks: if not sfn: - sfn = __salt__['cp.cache_file'](source, saltenv) + sfn = __salt__["cp.cache_file"](source, saltenv) if not sfn: - return _error( - ret, 'Source file \'{0}\' not found'.format(source)) + return _error(ret, "Source file '{0}' not found".format(source)) # If the downloaded file came from a non salt server source verify # that it matches the intended sum value - if not skip_verify and _urlparse(source).scheme != 'salt': - dl_sum = get_hash(sfn, source_sum['hash_type']) - if dl_sum != source_sum['hsum']: - ret['comment'] = ( - 'Specified {0} checksum for {1} ({2}) does not match ' - 'actual checksum ({3})'.format( - source_sum['hash_type'], - name, - source_sum['hsum'], - dl_sum + if not skip_verify and _urlparse(source).scheme != "salt": + dl_sum = get_hash(sfn, source_sum["hash_type"]) + if dl_sum != source_sum["hsum"]: + ret["comment"] = ( + "Specified {0} checksum for {1} ({2}) does not match " + "actual checksum ({3})".format( + source_sum["hash_type"], name, source_sum["hsum"], dl_sum ) ) - ret['result'] = False + ret["result"] = False return ret try: - salt.utils.files.copyfile(sfn, - name, - __salt__['config.backup_mode'](backup), - __opts__['cachedir']) + salt.utils.files.copyfile( + sfn, + name, + __salt__["config.backup_mode"](backup), + __opts__["cachedir"], + ) except IOError as io_error: __clean_tmp(sfn) - return _error( - ret, 'Failed to commit change: {0}'.format(io_error)) + return _error(ret, "Failed to commit change: {0}".format(io_error)) - ret['changes']['diff'] = \ - 'Replace symbolic link with regular file' + ret["changes"]["diff"] = "Replace symbolic link with regular file" if salt.utils.platform.is_windows(): # This function resides in win_file.py and will be available @@ -5438,22 +5502,21 @@ def manage_file(name, ret = check_perms( path=name, ret=ret, - owner=kwargs.get('win_owner'), - grant_perms=kwargs.get('win_perms'), - deny_perms=kwargs.get('win_deny_perms'), - inheritance=kwargs.get('win_inheritance', True), - reset=kwargs.get('win_perms_reset', False)) + owner=kwargs.get("win_owner"), + grant_perms=kwargs.get("win_perms"), + deny_perms=kwargs.get("win_deny_perms"), + inheritance=kwargs.get("win_inheritance", True), + reset=kwargs.get("win_perms_reset", False), + ) # pylint: enable=E1120,E1121,E1123 else: ret, _ = check_perms(name, ret, user, group, mode, attrs, follow_symlinks) - if ret['changes']: - ret['comment'] = 'File {0} updated'.format( - salt.utils.data.decode(name) - ) + if ret["changes"]: + ret["comment"] = "File {0} updated".format(salt.utils.data.decode(name)) - elif not ret['changes'] and ret['result']: - ret['comment'] = 'File {0} is in the correct state'.format( + elif not ret["changes"] and ret["result"]: + ret["comment"] = "File {0} is in the correct state".format( salt.utils.data.decode(name) ) if sfn: @@ -5468,8 +5531,7 @@ def manage_file(name, drive, _ = os.path.splitdrive(name) if drive and not os.path.exists(drive): __clean_tmp(sfn) - return _error(ret, - '{0} drive not present'.format(drive)) + return _error(ret, "{0} drive not present".format(drive)) if dir_mode is None and mode is not None: # Add execute bit to each nonzero digit in the mode, if # dir_mode was not specified. Otherwise, any @@ -5477,9 +5539,9 @@ def manage_file(name, # listed via a shell. mode_list = [x for x in six.text_type(mode)][-3:] for idx in range(len(mode_list)): - if mode_list[idx] != '0': + if mode_list[idx] != "0": mode_list[idx] = six.text_type(int(mode_list[idx]) | 1) - dir_mode = ''.join(mode_list) + dir_mode = "".join(mode_list) if salt.utils.platform.is_windows(): # This function resides in win_file.py and will be available @@ -5487,11 +5549,12 @@ def manage_file(name, # pylint: disable=E1120,E1121,E1123 makedirs_( path=name, - owner=kwargs.get('win_owner'), - grant_perms=kwargs.get('win_perms'), - deny_perms=kwargs.get('win_deny_perms'), - inheritance=kwargs.get('win_inheritance', True), - reset=kwargs.get('win_perms_reset', False)) + owner=kwargs.get("win_owner"), + grant_perms=kwargs.get("win_perms"), + deny_perms=kwargs.get("win_deny_perms"), + inheritance=kwargs.get("win_inheritance", True), + reset=kwargs.get("win_perms_reset", False), + ) # pylint: enable=E1120,E1121,E1123 else: makedirs_(name, user=user, group=group, mode=dir_mode) @@ -5499,37 +5562,32 @@ def manage_file(name, if source: # Apply the new file if not sfn: - sfn = __salt__['cp.cache_file'](source, saltenv) + sfn = __salt__["cp.cache_file"](source, saltenv) if not sfn: - return _error( - ret, 'Source file \'{0}\' not found'.format(source)) + return _error(ret, "Source file '{0}' not found".format(source)) # If the downloaded file came from a non salt server source verify # that it matches the intended sum value - if not skip_verify \ - and _urlparse(source).scheme != 'salt': - dl_sum = get_hash(sfn, source_sum['hash_type']) - if dl_sum != source_sum['hsum']: - ret['comment'] = ( - 'Specified {0} checksum for {1} ({2}) does not match ' - 'actual checksum ({3})'.format( - source_sum['hash_type'], - name, - source_sum['hsum'], - dl_sum + if not skip_verify and _urlparse(source).scheme != "salt": + dl_sum = get_hash(sfn, source_sum["hash_type"]) + if dl_sum != source_sum["hsum"]: + ret["comment"] = ( + "Specified {0} checksum for {1} ({2}) does not match " + "actual checksum ({3})".format( + source_sum["hash_type"], name, source_sum["hsum"], dl_sum ) ) - ret['result'] = False + ret["result"] = False return ret # It is a new file, set the diff accordingly - ret['changes']['diff'] = 'New file' + ret["changes"]["diff"] = "New file" if not os.path.isdir(contain_dir): if makedirs: _set_mode_and_make_dirs(name, dir_mode, mode, user, group) else: __clean_tmp(sfn) # No changes actually made - ret['changes'].pop('diff', None) - return _error(ret, 'Parent directory not present') + ret["changes"].pop("diff", None) + return _error(ret, "Parent directory not present") else: # source != True if not os.path.isdir(contain_dir): if makedirs: @@ -5537,57 +5595,57 @@ def manage_file(name, else: __clean_tmp(sfn) # No changes actually made - ret['changes'].pop('diff', None) - return _error(ret, 'Parent directory not present') + ret["changes"].pop("diff", None) + return _error(ret, "Parent directory not present") # Create the file, user rw-only if mode will be set to prevent # a small security race problem before the permissions are set with salt.utils.files.set_umask(0o077 if mode else None): # Create a new file when test is False and source is None if contents is None: - if not __opts__['test']: + if not __opts__["test"]: if touch(name): - ret['changes']['new'] = 'file {0} created'.format(name) - ret['comment'] = 'Empty file' + ret["changes"]["new"] = "file {0} created".format(name) + ret["comment"] = "Empty file" else: return _error( - ret, 'Empty file {0} not created'.format(name) + ret, "Empty file {0} not created".format(name) ) else: - if not __opts__['test']: + if not __opts__["test"]: if touch(name): - ret['changes']['diff'] = 'New file' + ret["changes"]["diff"] = "New file" else: - return _error( - ret, 'File {0} not created'.format(name) - ) + return _error(ret, "File {0} not created".format(name)) if contents is not None: # Write the static contents to a temporary file - tmp = salt.utils.files.mkstemp(prefix=salt.utils.files.TEMPFILE_PREFIX, - text=True) - with salt.utils.files.fopen(tmp, 'wb') as tmp_: + tmp = salt.utils.files.mkstemp( + prefix=salt.utils.files.TEMPFILE_PREFIX, text=True + ) + with salt.utils.files.fopen(tmp, "wb") as tmp_: if encoding: if salt.utils.platform.is_windows(): contents = os.linesep.join( - _splitlines_preserving_trailing_newline(contents)) - log.debug('File will be encoded with %s', encoding) - tmp_.write(contents.encode(encoding=encoding, errors=encoding_errors)) + _splitlines_preserving_trailing_newline(contents) + ) + log.debug("File will be encoded with %s", encoding) + tmp_.write( + contents.encode(encoding=encoding, errors=encoding_errors) + ) else: tmp_.write(salt.utils.stringutils.to_bytes(contents)) # Copy into place - salt.utils.files.copyfile(tmp, - name, - __salt__['config.backup_mode'](backup), - __opts__['cachedir']) + salt.utils.files.copyfile( + tmp, name, __salt__["config.backup_mode"](backup), __opts__["cachedir"] + ) __clean_tmp(tmp) # Now copy the file contents if there is a source file elif sfn: - salt.utils.files.copyfile(sfn, - name, - __salt__['config.backup_mode'](backup), - __opts__['cachedir']) + salt.utils.files.copyfile( + sfn, name, __salt__["config.backup_mode"](backup), __opts__["cachedir"] + ) __clean_tmp(sfn) # This is a new file, if no mode specified, use the umask to figure @@ -5603,35 +5661,33 @@ def manage_file(name, # on Windows. The local function will be overridden # pylint: disable=E1120,E1121,E1123 ret = check_perms( - path=name, + path=name, ret=ret, - owner=kwargs.get('win_owner'), - grant_perms=kwargs.get('win_perms'), - deny_perms=kwargs.get('win_deny_perms'), - inheritance=kwargs.get('win_inheritance', True), - reset=kwargs.get('win_perms_reset', False)) + owner=kwargs.get("win_owner"), + grant_perms=kwargs.get("win_perms"), + deny_perms=kwargs.get("win_deny_perms"), + inheritance=kwargs.get("win_inheritance", True), + reset=kwargs.get("win_perms_reset", False), + ) # pylint: enable=E1120,E1121,E1123 else: ret, _ = check_perms(name, ret, user, group, mode, attrs) - if not ret['comment']: - ret['comment'] = 'File ' + name + ' updated' + if not ret["comment"]: + ret["comment"] = "File " + name + " updated" - if __opts__['test']: - ret['comment'] = 'File ' + name + ' not updated' - elif not ret['changes'] and ret['result']: - ret['comment'] = 'File ' + name + ' is in the correct state' + if __opts__["test"]: + ret["comment"] = "File " + name + " not updated" + elif not ret["changes"] and ret["result"]: + ret["comment"] = "File " + name + " is in the correct state" if sfn: __clean_tmp(sfn) return ret -def mkdir(dir_path, - user=None, - group=None, - mode=None): - ''' +def mkdir(dir_path, user=None, group=None, mode=None): + """ Ensure that a directory is available. CLI Example: @@ -5639,7 +5695,7 @@ def mkdir(dir_path, .. code-block:: bash salt '*' file.mkdir /opt/jetty/context - ''' + """ dir_path = os.path.expanduser(dir_path) directory = os.path.normpath(dir_path) @@ -5653,11 +5709,8 @@ def mkdir(dir_path, return True -def makedirs_(path, - user=None, - group=None, - mode=None): - ''' +def makedirs_(path, user=None, group=None, mode=None): + """ Ensure that the directory containing this path is available. .. note:: @@ -5673,7 +5726,7 @@ def makedirs_(path, .. code-block:: bash salt '*' file.makedirs /opt/code/ - ''' + """ path = os.path.expanduser(path) if mode: @@ -5685,14 +5738,12 @@ def makedirs_(path, if os.path.isdir(dirname): # There's nothing for us to do - msg = 'Directory \'{0}\' already exists'.format(dirname) + msg = "Directory '{0}' already exists".format(dirname) log.debug(msg) return msg if os.path.exists(dirname): - msg = 'The path \'{0}\' already exists and is not a directory'.format( - dirname - ) + msg = "The path '{0}' already exists and is not a directory".format(dirname) log.debug(msg) return msg @@ -5707,23 +5758,20 @@ def makedirs_(path, if current_dirname == dirname: raise SaltInvocationError( - 'Recursive creation for path \'{0}\' would result in an ' - 'infinite loop. Please use an absolute path.'.format(dirname) + "Recursive creation for path '{0}' would result in an " + "infinite loop. Please use an absolute path.".format(dirname) ) # create parent directories from the topmost to the most deeply nested one directories_to_create.reverse() for directory_to_create in directories_to_create: # all directories have the user, group and mode set!! - log.debug('Creating directory: %s', directory_to_create) + log.debug("Creating directory: %s", directory_to_create) mkdir(directory_to_create, user=user, group=group, mode=mode) -def makedirs_perms(name, - user=None, - group=None, - mode='0755'): - ''' +def makedirs_perms(name, user=None, group=None, mode="0755"): + """ Taken and modified from os.makedirs to set user, group and mode for each directory created. @@ -5732,7 +5780,7 @@ def makedirs_perms(name, .. code-block:: bash salt '*' file.makedirs_perms /opt/code - ''' + """ name = os.path.expanduser(name) path = os.path @@ -5749,15 +5797,11 @@ def makedirs_perms(name, if tail == os.curdir: # xxx/newdir/. exists if xxx/newdir exists return os.mkdir(name) - check_perms(name, - None, - user, - group, - int('{0}'.format(mode)) if mode else None) + check_perms(name, None, user, group, int("{0}".format(mode)) if mode else None) def get_devmm(name): - ''' + """ Get major/minor info from a device CLI Example: @@ -5765,20 +5809,18 @@ def get_devmm(name): .. code-block:: bash salt '*' file.get_devmm /dev/chr - ''' + """ name = os.path.expanduser(name) if is_chrdev(name) or is_blkdev(name): stat_structure = os.stat(name) - return ( - os.major(stat_structure.st_rdev), - os.minor(stat_structure.st_rdev)) + return (os.major(stat_structure.st_rdev), os.minor(stat_structure.st_rdev)) else: return (0, 0) def is_chrdev(name): - ''' + """ Check if a file exists and is a character device. CLI Example: @@ -5786,7 +5828,7 @@ def is_chrdev(name): .. code-block:: bash salt '*' file.is_chrdev /dev/chr - ''' + """ name = os.path.expanduser(name) stat_structure = None @@ -5801,13 +5843,8 @@ def is_chrdev(name): return stat.S_ISCHR(stat_structure.st_mode) -def mknod_chrdev(name, - major, - minor, - user=None, - group=None, - mode='0660'): - ''' +def mknod_chrdev(name, major, minor, user=None, group=None, mode="0660"): + """ .. versionadded:: 0.17.0 Create a character device. @@ -5817,43 +5854,44 @@ def mknod_chrdev(name, .. code-block:: bash salt '*' file.mknod_chrdev /dev/chr 180 31 - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} - log.debug('Creating character device name:{0} major:{1} minor:{2} mode:{3}' - .format(name, major, minor, mode)) + ret = {"name": name, "changes": {}, "comment": "", "result": False} + log.debug( + "Creating character device name:{0} major:{1} minor:{2} mode:{3}".format( + name, major, minor, mode + ) + ) try: - if __opts__['test']: - ret['changes'] = {'new': 'Character device {0} created.'.format(name)} - ret['result'] = None + if __opts__["test"]: + ret["changes"] = {"new": "Character device {0} created.".format(name)} + ret["result"] = None else: - if os.mknod(name, - int(six.text_type(mode).lstrip('0Oo'), 8) | stat.S_IFCHR, - os.makedev(major, minor)) is None: - ret['changes'] = {'new': 'Character device {0} created.'.format(name)} - ret['result'] = True + if ( + os.mknod( + name, + int(six.text_type(mode).lstrip("0Oo"), 8) | stat.S_IFCHR, + os.makedev(major, minor), + ) + is None + ): + ret["changes"] = {"new": "Character device {0} created.".format(name)} + ret["result"] = True except OSError as exc: # be happy it is already there....however, if you are trying to change the # major/minor, you will need to unlink it first as os.mknod will not overwrite if exc.errno != errno.EEXIST: raise else: - ret['comment'] = 'File {0} exists and cannot be overwritten'.format(name) + ret["comment"] = "File {0} exists and cannot be overwritten".format(name) # quick pass at verifying the permissions of the newly created character device - check_perms(name, - None, - user, - group, - int('{0}'.format(mode)) if mode else None) + check_perms(name, None, user, group, int("{0}".format(mode)) if mode else None) return ret def is_blkdev(name): - ''' + """ Check if a file exists and is a block device. CLI Example: @@ -5861,7 +5899,7 @@ def is_blkdev(name): .. code-block:: bash salt '*' file.is_blkdev /dev/blk - ''' + """ name = os.path.expanduser(name) stat_structure = None @@ -5876,13 +5914,8 @@ def is_blkdev(name): return stat.S_ISBLK(stat_structure.st_mode) -def mknod_blkdev(name, - major, - minor, - user=None, - group=None, - mode='0660'): - ''' +def mknod_blkdev(name, major, minor, user=None, group=None, mode="0660"): + """ .. versionadded:: 0.17.0 Create a block device. @@ -5892,43 +5925,44 @@ def mknod_blkdev(name, .. code-block:: bash salt '*' file.mknod_blkdev /dev/blk 8 999 - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} - log.debug('Creating block device name:{0} major:{1} minor:{2} mode:{3}' - .format(name, major, minor, mode)) + ret = {"name": name, "changes": {}, "comment": "", "result": False} + log.debug( + "Creating block device name:{0} major:{1} minor:{2} mode:{3}".format( + name, major, minor, mode + ) + ) try: - if __opts__['test']: - ret['changes'] = {'new': 'Block device {0} created.'.format(name)} - ret['result'] = None + if __opts__["test"]: + ret["changes"] = {"new": "Block device {0} created.".format(name)} + ret["result"] = None else: - if os.mknod(name, - int(six.text_type(mode).lstrip('0Oo'), 8) | stat.S_IFBLK, - os.makedev(major, minor)) is None: - ret['changes'] = {'new': 'Block device {0} created.'.format(name)} - ret['result'] = True + if ( + os.mknod( + name, + int(six.text_type(mode).lstrip("0Oo"), 8) | stat.S_IFBLK, + os.makedev(major, minor), + ) + is None + ): + ret["changes"] = {"new": "Block device {0} created.".format(name)} + ret["result"] = True except OSError as exc: # be happy it is already there....however, if you are trying to change the # major/minor, you will need to unlink it first as os.mknod will not overwrite if exc.errno != errno.EEXIST: raise else: - ret['comment'] = 'File {0} exists and cannot be overwritten'.format(name) + ret["comment"] = "File {0} exists and cannot be overwritten".format(name) # quick pass at verifying the permissions of the newly created block device - check_perms(name, - None, - user, - group, - int('{0}'.format(mode)) if mode else None) + check_perms(name, None, user, group, int("{0}".format(mode)) if mode else None) return ret def is_fifo(name): - ''' + """ Check if a file exists and is a FIFO. CLI Example: @@ -5936,7 +5970,7 @@ def is_fifo(name): .. code-block:: bash salt '*' file.is_fifo /dev/fifo - ''' + """ name = os.path.expanduser(name) stat_structure = None @@ -5951,11 +5985,8 @@ def is_fifo(name): return stat.S_ISFIFO(stat_structure.st_mode) -def mknod_fifo(name, - user=None, - group=None, - mode='0660'): - ''' +def mknod_fifo(name, user=None, group=None, mode="0660"): + """ .. versionadded:: 0.17.0 Create a FIFO pipe. @@ -5965,45 +5996,32 @@ def mknod_fifo(name, .. code-block:: bash salt '*' file.mknod_fifo /dev/fifo - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} - log.debug('Creating FIFO name: {0}'.format(name)) + ret = {"name": name, "changes": {}, "comment": "", "result": False} + log.debug("Creating FIFO name: {0}".format(name)) try: - if __opts__['test']: - ret['changes'] = {'new': 'Fifo pipe {0} created.'.format(name)} - ret['result'] = None + if __opts__["test"]: + ret["changes"] = {"new": "Fifo pipe {0} created.".format(name)} + ret["result"] = None else: - if os.mkfifo(name, int(six.text_type(mode).lstrip('0Oo'), 8)) is None: - ret['changes'] = {'new': 'Fifo pipe {0} created.'.format(name)} - ret['result'] = True + if os.mkfifo(name, int(six.text_type(mode).lstrip("0Oo"), 8)) is None: + ret["changes"] = {"new": "Fifo pipe {0} created.".format(name)} + ret["result"] = True except OSError as exc: # be happy it is already there if exc.errno != errno.EEXIST: raise else: - ret['comment'] = 'File {0} exists and cannot be overwritten'.format(name) + ret["comment"] = "File {0} exists and cannot be overwritten".format(name) # quick pass at verifying the permissions of the newly created fifo - check_perms(name, - None, - user, - group, - int('{0}'.format(mode)) if mode else None) + check_perms(name, None, user, group, int("{0}".format(mode)) if mode else None) return ret -def mknod(name, - ntype, - major=0, - minor=0, - user=None, - group=None, - mode='0600'): - ''' +def mknod(name, ntype, major=0, minor=0, user=None, group=None, mode="0600"): + """ .. versionadded:: 0.17.0 Create a block device, character device, or fifo pipe. @@ -6016,25 +6034,25 @@ def mknod(name, salt '*' file.mknod /dev/chr c 180 31 salt '*' file.mknod /dev/blk b 8 999 salt '*' file.nknod /dev/fifo p - ''' + """ ret = False makedirs_(name, user, group) - if ntype == 'c': + if ntype == "c": ret = mknod_chrdev(name, major, minor, user, group, mode) - elif ntype == 'b': + elif ntype == "b": ret = mknod_blkdev(name, major, minor, user, group, mode) - elif ntype == 'p': + elif ntype == "p": ret = mknod_fifo(name, user, group, mode) else: raise SaltInvocationError( - 'Node type unavailable: \'{0}\'. Available node types are ' - 'character (\'c\'), block (\'b\'), and pipe (\'p\').'.format(ntype) + "Node type unavailable: '{0}'. Available node types are " + "character ('c'), block ('b'), and pipe ('p').".format(ntype) ) return ret def list_backups(path, limit=None): - ''' + """ .. versionadded:: 0.17.0 Lists the previous versions of a file backed up using Salt's :ref:`file @@ -6050,7 +6068,7 @@ def list_backups(path, limit=None): .. code-block:: bash salt '*' file.list_backups /foo/bar/baz.txt - ''' + """ path = os.path.expanduser(path) try: @@ -6058,14 +6076,14 @@ def list_backups(path, limit=None): except TypeError: pass except ValueError: - log.error('file.list_backups: \'limit\' value must be numeric') + log.error("file.list_backups: 'limit' value must be numeric") limit = None bkroot = _get_bkroot() parent_dir, basename = os.path.split(path) if salt.utils.platform.is_windows(): # ':' is an illegal filesystem path character on Windows - src_dir = parent_dir.replace(':', '_') + src_dir = parent_dir.replace(":", "_") else: src_dir = parent_dir[1:] # Figure out full path of location of backup file in minion cache @@ -6075,13 +6093,14 @@ def list_backups(path, limit=None): return {} files = {} - for fname in [x for x in os.listdir(bkdir) - if os.path.isfile(os.path.join(bkdir, x))]: + for fname in [ + x for x in os.listdir(bkdir) if os.path.isfile(os.path.join(bkdir, x)) + ]: if salt.utils.platform.is_windows(): # ':' is an illegal filesystem path character on Windows - strpfmt = '{0}_%a_%b_%d_%H-%M-%S_%f_%Y'.format(basename) + strpfmt = "{0}_%a_%b_%d_%H-%M-%S_%f_%Y".format(basename) else: - strpfmt = '{0}_%a_%b_%d_%H:%M:%S_%f_%Y'.format(basename) + strpfmt = "{0}_%a_%b_%d_%H:%M:%S_%f_%Y".format(basename) try: timestamp = datetime.datetime.strptime(fname, strpfmt) except ValueError: @@ -6089,26 +6108,29 @@ def list_backups(path, limit=None): # for this file. Move on to the next one. continue if salt.utils.platform.is_windows(): - str_format = '%a %b %d %Y %H-%M-%S.%f' + str_format = "%a %b %d %Y %H-%M-%S.%f" else: - str_format = '%a %b %d %Y %H:%M:%S.%f' - files.setdefault(timestamp, {})['Backup Time'] = \ - timestamp.strftime(str_format) + str_format = "%a %b %d %Y %H:%M:%S.%f" + files.setdefault(timestamp, {})["Backup Time"] = timestamp.strftime(str_format) location = os.path.join(bkdir, fname) - files[timestamp]['Size'] = os.stat(location).st_size - files[timestamp]['Location'] = location + files[timestamp]["Size"] = os.stat(location).st_size + files[timestamp]["Location"] = location - return dict(list(zip( - list(range(len(files))), - [files[x] for x in sorted(files, reverse=True)[:limit]] - ))) + return dict( + list( + zip( + list(range(len(files))), + [files[x] for x in sorted(files, reverse=True)[:limit]], + ) + ) + ) -list_backup = salt.utils.functools.alias_function(list_backups, 'list_backup') +list_backup = salt.utils.functools.alias_function(list_backups, "list_backup") def list_backups_dir(path, limit=None): - ''' + """ Lists the previous versions of a directory backed up using Salt's :ref:`file state backup ` system. @@ -6122,7 +6144,7 @@ def list_backups_dir(path, limit=None): .. code-block:: bash salt '*' file.list_backups_dir /foo/bar/baz/ - ''' + """ path = os.path.expanduser(path) try: @@ -6130,7 +6152,7 @@ def list_backups_dir(path, limit=None): except TypeError: pass except ValueError: - log.error('file.list_backups_dir: \'limit\' value must be numeric') + log.error("file.list_backups_dir: 'limit' value must be numeric") limit = None bkroot = _get_bkroot() @@ -6142,34 +6164,49 @@ def list_backups_dir(path, limit=None): return {} files = {} - f = dict([(i, len(list(n))) for i, n in itertools.groupby([x.split("_")[0] for x in sorted(os.listdir(bkdir))])]) + f = dict( + [ + (i, len(list(n))) + for i, n in itertools.groupby( + [x.split("_")[0] for x in sorted(os.listdir(bkdir))] + ) + ] + ) ff = os.listdir(bkdir) for i, n in six.iteritems(f): ssfile = {} for x in sorted(ff): - basename = x.split('_')[0] + basename = x.split("_")[0] if i == basename: - strpfmt = '{0}_%a_%b_%d_%H:%M:%S_%f_%Y'.format(basename) + strpfmt = "{0}_%a_%b_%d_%H:%M:%S_%f_%Y".format(basename) try: timestamp = datetime.datetime.strptime(x, strpfmt) except ValueError: # Folder didn't match the strp format string, so it's not a backup # for this folder. Move on to the next one. continue - ssfile.setdefault(timestamp, {})['Backup Time'] = \ - timestamp.strftime('%a %b %d %Y %H:%M:%S.%f') + ssfile.setdefault(timestamp, {})["Backup Time"] = timestamp.strftime( + "%a %b %d %Y %H:%M:%S.%f" + ) location = os.path.join(bkdir, x) - ssfile[timestamp]['Size'] = os.stat(location).st_size - ssfile[timestamp]['Location'] = location + ssfile[timestamp]["Size"] = os.stat(location).st_size + ssfile[timestamp]["Location"] = location - sfiles = dict(list(zip(list(range(n)), [ssfile[x] for x in sorted(ssfile, reverse=True)[:limit]]))) + sfiles = dict( + list( + zip( + list(range(n)), + [ssfile[x] for x in sorted(ssfile, reverse=True)[:limit]], + ) + ) + ) sefiles = {i: sfiles} files.update(sefiles) return files def restore_backup(path, backup_id): - ''' + """ .. versionadded:: 0.17.0 Restore a previous version of a file that was backed up using Salt's @@ -6186,13 +6223,12 @@ def restore_backup(path, backup_id): .. code-block:: bash salt '*' file.restore_backup /foo/bar/baz.txt 0 - ''' + """ path = os.path.expanduser(path) # Note: This only supports minion backups, so this function will need to be # modified if/when master backups are implemented. - ret = {'result': False, - 'comment': 'Invalid backup_id \'{0}\''.format(backup_id)} + ret = {"result": False, "comment": "Invalid backup_id '{0}'".format(backup_id)} try: if len(six.text_type(backup_id)) == len(six.text_type(int(backup_id))): backup = list_backups(path)[int(backup_id)] @@ -6201,29 +6237,31 @@ def restore_backup(path, backup_id): except ValueError: return ret except KeyError: - ret['comment'] = 'backup_id \'{0}\' does not exist for ' \ - '{1}'.format(backup_id, path) + ret["comment"] = "backup_id '{0}' does not exist for " "{1}".format( + backup_id, path + ) return ret salt.utils.files.backup_minion(path, _get_bkroot()) try: - shutil.copyfile(backup['Location'], path) + shutil.copyfile(backup["Location"], path) except IOError as exc: - ret['comment'] = \ - 'Unable to restore {0} to {1}: ' \ - '{2}'.format(backup['Location'], path, exc) + ret["comment"] = "Unable to restore {0} to {1}: " "{2}".format( + backup["Location"], path, exc + ) return ret else: - ret['result'] = True - ret['comment'] = 'Successfully restored {0} to ' \ - '{1}'.format(backup['Location'], path) + ret["result"] = True + ret["comment"] = "Successfully restored {0} to " "{1}".format( + backup["Location"], path + ) # Try to set proper ownership if not salt.utils.platform.is_windows(): try: fstat = os.stat(path) except (OSError, IOError): - ret['comment'] += ', but was unable to set ownership' + ret["comment"] += ", but was unable to set ownership" else: os.chown(path, fstat.st_uid, fstat.st_gid) @@ -6231,7 +6269,7 @@ def restore_backup(path, backup_id): def delete_backup(path, backup_id): - ''' + """ .. versionadded:: 0.17.0 Delete a previous version of a file that was backed up using Salt's @@ -6248,11 +6286,10 @@ def delete_backup(path, backup_id): .. code-block:: bash salt '*' file.delete_backup /var/cache/salt/minion/file_backup/home/foo/bar/baz.txt 0 - ''' + """ path = os.path.expanduser(path) - ret = {'result': False, - 'comment': 'Invalid backup_id \'{0}\''.format(backup_id)} + ret = {"result": False, "comment": "Invalid backup_id '{0}'".format(backup_id)} try: if len(six.text_type(backup_id)) == len(six.text_type(int(backup_id))): backup = list_backups(path)[int(backup_id)] @@ -6261,29 +6298,27 @@ def delete_backup(path, backup_id): except ValueError: return ret except KeyError: - ret['comment'] = 'backup_id \'{0}\' does not exist for ' \ - '{1}'.format(backup_id, path) + ret["comment"] = "backup_id '{0}' does not exist for " "{1}".format( + backup_id, path + ) return ret try: - os.remove(backup['Location']) + os.remove(backup["Location"]) except IOError as exc: - ret['comment'] = 'Unable to remove {0}: {1}'.format(backup['Location'], - exc) + ret["comment"] = "Unable to remove {0}: {1}".format(backup["Location"], exc) else: - ret['result'] = True - ret['comment'] = 'Successfully removed {0}'.format(backup['Location']) + ret["result"] = True + ret["comment"] = "Successfully removed {0}".format(backup["Location"]) return ret -remove_backup = salt.utils.functools.alias_function(delete_backup, 'remove_backup') +remove_backup = salt.utils.functools.alias_function(delete_backup, "remove_backup") -def grep(path, - pattern, - *opts): - ''' +def grep(path, pattern, *opts): + """ Grep for a string in the specified file .. note:: @@ -6318,7 +6353,7 @@ def grep(path, salt '*' file.grep /etc/sysconfig/network-scripts/ifcfg-eth0 ipaddr -- -i salt '*' file.grep /etc/sysconfig/network-scripts/ifcfg-eth0 ipaddr -- -i -B2 salt '*' file.grep "/etc/sysconfig/network-scripts/*" ipaddr -- -i -l - ''' + """ path = os.path.expanduser(path) # Backup the path in case the glob returns nothing @@ -6339,18 +6374,18 @@ def grep(path, split = salt.utils.args.shlex_split(six.text_type(opt)) if len(split) > 1: raise SaltInvocationError( - 'Passing multiple command line arguments in a single string ' - 'is not supported, please pass the following arguments ' - 'separately: {0}'.format(opt) + "Passing multiple command line arguments in a single string " + "is not supported, please pass the following arguments " + "separately: {0}".format(opt) ) split_opts.extend(split) if isinstance(path, list): - cmd = ['grep'] + split_opts + [pattern] + path + cmd = ["grep"] + split_opts + [pattern] + path else: - cmd = ['grep'] + split_opts + [pattern, path] + cmd = ["grep"] + split_opts + [pattern, path] try: - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) except (IOError, OSError) as exc: raise CommandExecutionError(exc.strerror) @@ -6358,7 +6393,7 @@ def grep(path, def open_files(by_pid=False): - ''' + """ Return a list of all physical open files on the system. CLI Examples: @@ -6367,10 +6402,10 @@ def open_files(by_pid=False): salt '*' file.open_files salt '*' file.open_files by_pid=True - ''' + """ # First we collect valid PIDs pids = {} - procfs = os.listdir('/proc/') + procfs = os.listdir("/proc/") for pfile in procfs: try: pids[int(pfile)] = [] @@ -6381,33 +6416,31 @@ def open_files(by_pid=False): # Then we look at the open files for each PID files = {} for pid in pids: - ppath = '/proc/{0}'.format(pid) + ppath = "/proc/{0}".format(pid) try: - tids = os.listdir('{0}/task'.format(ppath)) + tids = os.listdir("{0}/task".format(ppath)) except OSError: continue # Collect the names of all of the file descriptors fd_ = [] - #try: + # try: # fd_.append(os.path.realpath('{0}/task/{1}exe'.format(ppath, tid))) - #except Exception: # pylint: disable=broad-except + # except Exception: # pylint: disable=broad-except # pass - for fpath in os.listdir('{0}/fd'.format(ppath)): - fd_.append('{0}/fd/{1}'.format(ppath, fpath)) + for fpath in os.listdir("{0}/fd".format(ppath)): + fd_.append("{0}/fd/{1}".format(ppath, fpath)) for tid in tids: try: - fd_.append( - os.path.realpath('{0}/task/{1}/exe'.format(ppath, tid)) - ) + fd_.append(os.path.realpath("{0}/task/{1}/exe".format(ppath, tid))) except OSError: continue - for tpath in os.listdir('{0}/task/{1}/fd'.format(ppath, tid)): - fd_.append('{0}/task/{1}/fd/{2}'.format(ppath, tid, tpath)) + for tpath in os.listdir("{0}/task/{1}/fd".format(ppath, tid)): + fd_.append("{0}/task/{1}/fd/{2}".format(ppath, tid, tpath)) fd_ = sorted(set(fd_)) @@ -6438,7 +6471,7 @@ def open_files(by_pid=False): def pardir(): - ''' + """ Return the relative parent directory path symbol for underlying OS .. versionadded:: 2014.7.0 @@ -6455,12 +6488,12 @@ def pardir(): .. code-block:: bash salt '*' file.pardir - ''' + """ return os.path.pardir def normpath(path): - ''' + """ Returns Normalize path, eliminating double slashes, etc. .. versionadded:: 2015.5.0 @@ -6476,12 +6509,12 @@ def normpath(path): .. code-block:: bash salt '*' file.normpath 'a/b/c/..' - ''' + """ return os.path.normpath(path) def basename(path): - ''' + """ Returns the final component of a pathname .. versionadded:: 2015.5.0 @@ -6497,12 +6530,12 @@ def basename(path): .. code-block:: bash salt '*' file.basename 'test/test.config' - ''' + """ return os.path.basename(path) def dirname(path): - ''' + """ Returns the directory component of a pathname .. versionadded:: 2015.5.0 @@ -6518,12 +6551,12 @@ def dirname(path): .. code-block:: bash salt '*' file.dirname 'test/path/filename.config' - ''' + """ return os.path.dirname(path) def join(*args): - ''' + """ Return a normalized file system path for the underlying OS .. versionadded:: 2014.7.0 @@ -6546,12 +6579,12 @@ def join(*args): .. code-block:: bash salt '*' file.join '/' 'usr' 'local' 'bin' - ''' + """ return os.path.join(*args) def move(src, dst): - ''' + """ Move a file or directory CLI Example: @@ -6559,19 +6592,19 @@ def move(src, dst): .. code-block:: bash salt '*' file.move /path/to/src /path/to/dst - ''' + """ src = os.path.expanduser(src) dst = os.path.expanduser(dst) if not os.path.isabs(src): - raise SaltInvocationError('Source path must be absolute.') + raise SaltInvocationError("Source path must be absolute.") if not os.path.isabs(dst): - raise SaltInvocationError('Destination path must be absolute.') + raise SaltInvocationError("Destination path must be absolute.") ret = { - 'result': True, - 'comment': "'{0}' moved to '{1}'".format(src, dst), + "result": True, + "comment": "'{0}' moved to '{1}'".format(src, dst), } try: @@ -6585,7 +6618,7 @@ def move(src, dst): def diskusage(path): - ''' + """ Recursively calculate disk usage of path and return it in bytes @@ -6594,7 +6627,7 @@ def diskusage(path): .. code-block:: bash salt '*' file.diskusage /path/to/check - ''' + """ total_size = 0 seen = set() diff --git a/salt/modules/firewalld.py b/salt/modules/firewalld.py index a6d90d38b88..6a550d47a2b 100644 --- a/salt/modules/firewalld.py +++ b/salt/modules/firewalld.py @@ -1,62 +1,65 @@ # -*- coding: utf-8 -*- -''' +""" Support for firewalld. .. versionadded:: 2015.2.0 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import re +import salt.utils.path + # Import Salt Libs from salt.exceptions import CommandExecutionError -import salt.utils.path log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Check to see if firewall-cmd exists - ''' - if salt.utils.path.which('firewall-cmd'): + """ + if salt.utils.path.which("firewall-cmd"): return True - return (False, 'The firewalld execution module cannot be loaded: the firewall-cmd binary is not in the path.') + return ( + False, + "The firewalld execution module cannot be loaded: the firewall-cmd binary is not in the path.", + ) def __firewall_cmd(cmd): - ''' + """ Return the firewall-cmd location - ''' - firewall_cmd = '{0} {1}'.format(salt.utils.path.which('firewall-cmd'), cmd) - out = __salt__['cmd.run_all'](firewall_cmd) + """ + firewall_cmd = "{0} {1}".format(salt.utils.path.which("firewall-cmd"), cmd) + out = __salt__["cmd.run_all"](firewall_cmd) - if out['retcode'] != 0: - if not out['stderr']: - msg = out['stdout'] + if out["retcode"] != 0: + if not out["stderr"]: + msg = out["stdout"] else: - msg = out['stderr'] - raise CommandExecutionError( - 'firewall-cmd failed: {0}'.format(msg) - ) - return out['stdout'] + msg = out["stderr"] + raise CommandExecutionError("firewall-cmd failed: {0}".format(msg)) + return out["stdout"] def __mgmt(name, _type, action): - ''' + """ Perform zone management - ''' + """ # It's permanent because the 4 concerned functions need the permanent option, it's wrong without - cmd = '--{0}-{1}={2} --permanent'.format(action, _type, name) + cmd = "--{0}-{1}={2} --permanent".format(action, _type, name) return __firewall_cmd(cmd) def version(): - ''' + """ Return version from firewall-cmd CLI Example: @@ -64,12 +67,12 @@ def version(): .. code-block:: bash salt '*' firewalld.version - ''' - return __firewall_cmd('--version') + """ + return __firewall_cmd("--version") def reload_rules(): - ''' + """ Reload the firewall rules, which makes the permanent configuration the new runtime configuration without losing state information. @@ -80,12 +83,12 @@ def reload_rules(): .. code-block:: bash salt '*' firewalld.reload - ''' - return __firewall_cmd('--reload') + """ + return __firewall_cmd("--reload") def default_zone(): - ''' + """ Print default zone for connections and interfaces CLI Example: @@ -93,12 +96,12 @@ def default_zone(): .. code-block:: bash salt '*' firewalld.default_zone - ''' - return __firewall_cmd('--get-default-zone') + """ + return __firewall_cmd("--get-default-zone") def list_zones(permanent=True): - ''' + """ List everything added for or enabled in all zones CLI Example: @@ -106,20 +109,20 @@ def list_zones(permanent=True): .. code-block:: bash salt '*' firewalld.list_zones - ''' + """ zones = {} - cmd = '--list-all-zones' + cmd = "--list-all-zones" if permanent: - cmd += ' --permanent' + cmd += " --permanent" for i in __firewall_cmd(cmd).splitlines(): if i.strip(): - if bool(re.match('^[a-z0-9]', i, re.I)): + if bool(re.match("^[a-z0-9]", i, re.I)): zone_name = i.rstrip() else: - (id_, val) = i.strip().split(':') + (id_, val) = i.strip().split(":") if zones.get(zone_name, None): zones[zone_name].update({id_: val}) @@ -130,7 +133,7 @@ def list_zones(permanent=True): def get_zones(permanent=True): - ''' + """ Print predefined zones CLI Example: @@ -138,17 +141,17 @@ def get_zones(permanent=True): .. code-block:: bash salt '*' firewalld.get_zones - ''' - cmd = '--get-zones' + """ + cmd = "--get-zones" if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd).split() def get_services(permanent=True): - ''' + """ Print predefined services CLI Example: @@ -156,17 +159,17 @@ def get_services(permanent=True): .. code-block:: bash salt '*' firewalld.get_services - ''' - cmd = '--get-services' + """ + cmd = "--get-services" if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd).split() def get_icmp_types(permanent=True): - ''' + """ Print predefined icmptypes CLI Example: @@ -174,17 +177,17 @@ def get_icmp_types(permanent=True): .. code-block:: bash salt '*' firewalld.get_icmp_types - ''' - cmd = '--get-icmptypes' + """ + cmd = "--get-icmptypes" if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd).split() def new_zone(zone, restart=True): - ''' + """ Add a new zone CLI Example: @@ -199,19 +202,19 @@ def new_zone(zone, restart=True): .. code-block:: bash salt '*' firewalld.new_zone my_zone False - ''' - out = __mgmt(zone, 'zone', 'new') + """ + out = __mgmt(zone, "zone", "new") if restart: - if out == 'success': - return __firewall_cmd('--reload') + if out == "success": + return __firewall_cmd("--reload") return out def delete_zone(zone, restart=True): - ''' + """ Delete an existing zone CLI Example: @@ -226,19 +229,19 @@ def delete_zone(zone, restart=True): .. code-block:: bash salt '*' firewalld.delete_zone my_zone False - ''' - out = __mgmt(zone, 'zone', 'delete') + """ + out = __mgmt(zone, "zone", "delete") if restart: - if out == 'success': - return __firewall_cmd('--reload') + if out == "success": + return __firewall_cmd("--reload") return out def set_default_zone(zone): - ''' + """ Set default zone CLI Example: @@ -246,12 +249,12 @@ def set_default_zone(zone): .. code-block:: bash salt '*' firewalld.set_default_zone damian - ''' - return __firewall_cmd('--set-default-zone={0}'.format(zone)) + """ + return __firewall_cmd("--set-default-zone={0}".format(zone)) def new_service(name, restart=True): - ''' + """ Add a new service CLI Example: @@ -266,19 +269,19 @@ def new_service(name, restart=True): .. code-block:: bash salt '*' firewalld.new_service my_service False - ''' - out = __mgmt(name, 'service', 'new') + """ + out = __mgmt(name, "service", "new") if restart: - if out == 'success': - return __firewall_cmd('--reload') + if out == "success": + return __firewall_cmd("--reload") return out def delete_service(name, restart=True): - ''' + """ Delete an existing service CLI Example: @@ -293,19 +296,19 @@ def delete_service(name, restart=True): .. code-block:: bash salt '*' firewalld.delete_service my_service False - ''' - out = __mgmt(name, 'service', 'delete') + """ + out = __mgmt(name, "service", "delete") if restart: - if out == 'success': - return __firewall_cmd('--reload') + if out == "success": + return __firewall_cmd("--reload") return out def list_all(zone=None, permanent=True): - ''' + """ List everything added for or enabled in a zone CLI Example: @@ -319,27 +322,27 @@ def list_all(zone=None, permanent=True): .. code-block:: bash salt '*' firewalld.list_all my_zone - ''' + """ _zone = {} - id_ = '' + id_ = "" if zone: - cmd = '--zone={0} --list-all'.format(zone) + cmd = "--zone={0} --list-all".format(zone) else: - cmd = '--list-all' + cmd = "--list-all" if permanent: - cmd += ' --permanent' + cmd += " --permanent" for i in __firewall_cmd(cmd).splitlines(): - if re.match('^[a-z0-9]', i, re.I): + if re.match("^[a-z0-9]", i, re.I): zone_name = i.rstrip() else: - if i.startswith('\t'): + if i.startswith("\t"): _zone[zone_name][id_].append(i.strip()) continue - (id_, val) = i.split(':', 1) + (id_, val) = i.split(":", 1) id_ = id_.strip() @@ -352,7 +355,7 @@ def list_all(zone=None, permanent=True): def list_services(zone=None, permanent=True): - ''' + """ List services added for zone as a space separated list. If zone is omitted, default zone will be used. @@ -367,20 +370,20 @@ def list_services(zone=None, permanent=True): .. code-block:: bash salt '*' firewalld.list_services my_zone - ''' + """ if zone: - cmd = '--zone={0} --list-services'.format(zone) + cmd = "--zone={0} --list-services".format(zone) else: - cmd = '--list-services' + cmd = "--list-services" if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd).split() def add_service(service, zone=None, permanent=True): - ''' + """ Add a service for zone. If zone is omitted, default zone will be used. CLI Example: @@ -394,20 +397,20 @@ def add_service(service, zone=None, permanent=True): .. code-block:: bash salt '*' firewalld.add_service ssh my_zone - ''' + """ if zone: - cmd = '--zone={0} --add-service={1}'.format(zone, service) + cmd = "--zone={0} --add-service={1}".format(zone, service) else: - cmd = '--add-service={0}'.format(service) + cmd = "--add-service={0}".format(service) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def remove_service(service, zone=None, permanent=True): - ''' + """ Remove a service from zone. This option can be specified multiple times. If zone is omitted, default zone will be used. @@ -422,20 +425,20 @@ def remove_service(service, zone=None, permanent=True): .. code-block:: bash salt '*' firewalld.remove_service ssh dmz - ''' + """ if zone: - cmd = '--zone={0} --remove-service={1}'.format(zone, service) + cmd = "--zone={0} --remove-service={1}".format(zone, service) else: - cmd = '--remove-service={0}'.format(service) + cmd = "--remove-service={0}".format(service) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def add_service_port(service, port): - ''' + """ Add a new port to the specified service. .. versionadded:: 2016.11.0 @@ -445,16 +448,16 @@ def add_service_port(service, port): .. code-block:: bash salt '*' firewalld.add_service_port zone 80 - ''' + """ if service not in get_services(permanent=True): - raise CommandExecutionError('The service does not exist.') + raise CommandExecutionError("The service does not exist.") - cmd = '--permanent --service={0} --add-port={1}'.format(service, port) + cmd = "--permanent --service={0} --add-port={1}".format(service, port) return __firewall_cmd(cmd) def remove_service_port(service, port): - ''' + """ Remove a port from the specified service. .. versionadded:: 2016.11.0 @@ -464,16 +467,16 @@ def remove_service_port(service, port): .. code-block:: bash salt '*' firewalld.remove_service_port zone 80 - ''' + """ if service not in get_services(permanent=True): - raise CommandExecutionError('The service does not exist.') + raise CommandExecutionError("The service does not exist.") - cmd = '--permanent --service={0} --remove-port={1}'.format(service, port) + cmd = "--permanent --service={0} --remove-port={1}".format(service, port) return __firewall_cmd(cmd) def get_service_ports(service): - ''' + """ List ports of a service. .. versionadded:: 2016.11.0 @@ -483,13 +486,13 @@ def get_service_ports(service): .. code-block:: bash salt '*' firewalld.get_service_ports zone - ''' - cmd = '--permanent --service={0} --get-ports'.format(service) + """ + cmd = "--permanent --service={0} --get-ports".format(service) return __firewall_cmd(cmd).split() def add_service_protocol(service, protocol): - ''' + """ Add a new protocol to the specified service. .. versionadded:: 2016.11.0 @@ -499,14 +502,13 @@ def add_service_protocol(service, protocol): .. code-block:: bash salt '*' firewalld.add_service_protocol zone ssh - ''' - cmd = '--permanent --service={0} --add-protocol={1}'.format(service, - protocol) + """ + cmd = "--permanent --service={0} --add-protocol={1}".format(service, protocol) return __firewall_cmd(cmd) def remove_service_protocol(service, protocol): - ''' + """ Remove a protocol from the specified service. .. versionadded:: 2016.11.0 @@ -516,14 +518,13 @@ def remove_service_protocol(service, protocol): .. code-block:: bash salt '*' firewalld.remove_service_protocol zone ssh - ''' - cmd = '--permanent --service={0} --remove-protocol={1}'.format(service, - protocol) + """ + cmd = "--permanent --service={0} --remove-protocol={1}".format(service, protocol) return __firewall_cmd(cmd) def get_service_protocols(service): - ''' + """ List protocols of a service. .. versionadded:: 2016.11.0 @@ -533,13 +534,13 @@ def get_service_protocols(service): .. code-block:: bash salt '*' firewalld.get_service_protocols zone - ''' - cmd = '--permanent --service={0} --get-protocols'.format(service) + """ + cmd = "--permanent --service={0} --get-protocols".format(service) return __firewall_cmd(cmd).split() def get_masquerade(zone=None, permanent=True): - ''' + """ Show if masquerading is enabled on a zone. If zone is omitted, default zone will be used. @@ -548,17 +549,17 @@ def get_masquerade(zone=None, permanent=True): .. code-block:: bash salt '*' firewalld.get_masquerade zone - ''' + """ zone_info = list_all(zone, permanent) - if 'no' in [zone_info[i]['masquerade'][0] for i in zone_info]: + if "no" in [zone_info[i]["masquerade"][0] for i in zone_info]: return False return True def add_masquerade(zone=None, permanent=True): - ''' + """ Enable masquerade on a zone. If zone is omitted, default zone will be used. @@ -575,20 +576,20 @@ def add_masquerade(zone=None, permanent=True): .. code-block:: bash salt '*' firewalld.add_masquerade dmz - ''' + """ if zone: - cmd = '--zone={0} --add-masquerade'.format(zone) + cmd = "--zone={0} --add-masquerade".format(zone) else: - cmd = '--add-masquerade' + cmd = "--add-masquerade" if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def remove_masquerade(zone=None, permanent=True): - ''' + """ Remove masquerade on a zone. If zone is omitted, default zone will be used. @@ -605,20 +606,20 @@ def remove_masquerade(zone=None, permanent=True): .. code-block:: bash salt '*' firewalld.remove_masquerade dmz - ''' + """ if zone: - cmd = '--zone={0} --remove-masquerade'.format(zone) + cmd = "--zone={0} --remove-masquerade".format(zone) else: - cmd = '--remove-masquerade' + cmd = "--remove-masquerade" if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def add_port(zone, port, permanent=True, force_masquerade=False): - ''' + """ Allow specific ports in a zone. .. versionadded:: 2015.8.0 @@ -632,20 +633,20 @@ def add_port(zone, port, permanent=True, force_masquerade=False): force_masquerade when a zone is created ensure masquerade is also enabled on that zone. - ''' + """ if force_masquerade and not get_masquerade(zone): add_masquerade(zone) - cmd = '--zone={0} --add-port={1}'.format(zone, port) + cmd = "--zone={0} --add-port={1}".format(zone, port) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def remove_port(zone, port, permanent=True): - ''' + """ Remove a specific port from a zone. .. versionadded:: 2015.8.0 @@ -655,17 +656,17 @@ def remove_port(zone, port, permanent=True): .. code-block:: bash salt '*' firewalld.remove_port internal 443/tcp - ''' - cmd = '--zone={0} --remove-port={1}'.format(zone, port) + """ + cmd = "--zone={0} --remove-port={1}".format(zone, port) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def list_ports(zone, permanent=True): - ''' + """ List all ports in a zone. .. versionadded:: 2015.8.0 @@ -675,17 +676,19 @@ def list_ports(zone, permanent=True): .. code-block:: bash salt '*' firewalld.list_ports - ''' - cmd = '--zone={0} --list-ports'.format(zone) + """ + cmd = "--zone={0} --list-ports".format(zone) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd).split() -def add_port_fwd(zone, src, dest, proto='tcp', dstaddr='', permanent=True, force_masquerade=False): - ''' +def add_port_fwd( + zone, src, dest, proto="tcp", dstaddr="", permanent=True, force_masquerade=False +): + """ Add port forwarding. .. versionadded:: 2015.8.0 @@ -699,26 +702,22 @@ def add_port_fwd(zone, src, dest, proto='tcp', dstaddr='', permanent=True, force force_masquerade when a zone is created ensure masquerade is also enabled on that zone. - ''' + """ if force_masquerade and not get_masquerade(zone): add_masquerade(zone) - cmd = '--zone={0} --add-forward-port=port={1}:proto={2}:toport={3}:toaddr={4}'.format( - zone, - src, - proto, - dest, - dstaddr + cmd = "--zone={0} --add-forward-port=port={1}:proto={2}:toport={3}:toaddr={4}".format( + zone, src, proto, dest, dstaddr ) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) -def remove_port_fwd(zone, src, dest, proto='tcp', dstaddr='', permanent=True): - ''' +def remove_port_fwd(zone, src, dest, proto="tcp", dstaddr="", permanent=True): + """ Remove Port Forwarding. .. versionadded:: 2015.8.0 @@ -728,23 +727,19 @@ def remove_port_fwd(zone, src, dest, proto='tcp', dstaddr='', permanent=True): .. code-block:: bash salt '*' firewalld.remove_port_fwd public 80 443 tcp - ''' - cmd = '--zone={0} --remove-forward-port=port={1}:proto={2}:toport={3}:toaddr={4}'.format( - zone, - src, - proto, - dest, - dstaddr + """ + cmd = "--zone={0} --remove-forward-port=port={1}:proto={2}:toport={3}:toaddr={4}".format( + zone, src, proto, dest, dstaddr ) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def list_port_fwd(zone, permanent=True): - ''' + """ List port forwarding .. versionadded:: 2015.8.0 @@ -754,29 +749,31 @@ def list_port_fwd(zone, permanent=True): .. code-block:: bash salt '*' firewalld.list_port_fwd public - ''' + """ ret = [] - cmd = '--zone={0} --list-forward-ports'.format(zone) + cmd = "--zone={0} --list-forward-ports".format(zone) if permanent: - cmd += ' --permanent' + cmd += " --permanent" for i in __firewall_cmd(cmd).splitlines(): - (src, proto, dest, addr) = i.split(':') + (src, proto, dest, addr) = i.split(":") ret.append( - {'Source port': src.split('=')[1], - 'Protocol': proto.split('=')[1], - 'Destination port': dest.split('=')[1], - 'Destination address': addr.split('=')[1]} + { + "Source port": src.split("=")[1], + "Protocol": proto.split("=")[1], + "Destination port": dest.split("=")[1], + "Destination address": addr.split("=")[1], + } ) return ret def block_icmp(zone, icmp, permanent=True): - ''' + """ Block a specific ICMP type on a zone .. versionadded:: 2015.8.0 @@ -786,25 +783,25 @@ def block_icmp(zone, icmp, permanent=True): .. code-block:: bash salt '*' firewalld.block_icmp zone echo-reply - ''' + """ if icmp not in get_icmp_types(permanent): - log.error('Invalid ICMP type') + log.error("Invalid ICMP type") return False if icmp in list_icmp_block(zone, permanent): - log.info('ICMP block already exists') - return 'success' + log.info("ICMP block already exists") + return "success" - cmd = '--zone={0} --add-icmp-block={1}'.format(zone, icmp) + cmd = "--zone={0} --add-icmp-block={1}".format(zone, icmp) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def allow_icmp(zone, icmp, permanent=True): - ''' + """ Allow a specific ICMP type on a zone .. versionadded:: 2015.8.0 @@ -814,25 +811,25 @@ def allow_icmp(zone, icmp, permanent=True): .. code-block:: bash salt '*' firewalld.allow_icmp zone echo-reply - ''' + """ if icmp not in get_icmp_types(permanent): - log.error('Invalid ICMP type') + log.error("Invalid ICMP type") return False if icmp not in list_icmp_block(zone, permanent): - log.info('ICMP Type is already permitted') - return 'success' + log.info("ICMP Type is already permitted") + return "success" - cmd = '--zone={0} --remove-icmp-block={1}'.format(zone, icmp) + cmd = "--zone={0} --remove-icmp-block={1}".format(zone, icmp) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def list_icmp_block(zone, permanent=True): - ''' + """ List ICMP blocks on a zone .. versionadded:: 2015.8.0 @@ -842,17 +839,17 @@ def list_icmp_block(zone, permanent=True): .. code-block:: bash salt '*' firewlld.list_icmp_block zone - ''' - cmd = '--zone={0} --list-icmp-blocks'.format(zone) + """ + cmd = "--zone={0} --list-icmp-blocks".format(zone) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd).split() def make_permanent(): - ''' + """ Make current runtime configuration permanent. .. versionadded:: 2016.3.0 @@ -862,12 +859,12 @@ def make_permanent(): .. code-block:: bash salt '*' firewalld.make_permanent - ''' - return __firewall_cmd('--runtime-to-permanent') + """ + return __firewall_cmd("--runtime-to-permanent") def get_interfaces(zone, permanent=True): - ''' + """ List interfaces bound to a zone .. versionadded:: 2016.3.0 @@ -877,17 +874,17 @@ def get_interfaces(zone, permanent=True): .. code-block:: bash salt '*' firewalld.get_interfaces zone - ''' - cmd = '--zone={0} --list-interfaces'.format(zone) + """ + cmd = "--zone={0} --list-interfaces".format(zone) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd).split() def add_interface(zone, interface, permanent=True): - ''' + """ Bind an interface to a zone .. versionadded:: 2016.3.0 @@ -897,20 +894,20 @@ def add_interface(zone, interface, permanent=True): .. code-block:: bash salt '*' firewalld.add_interface zone eth0 - ''' + """ if interface in get_interfaces(zone, permanent): - log.info('Interface is already bound to zone.') + log.info("Interface is already bound to zone.") - cmd = '--zone={0} --add-interface={1}'.format(zone, interface) + cmd = "--zone={0} --add-interface={1}".format(zone, interface) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def remove_interface(zone, interface, permanent=True): - ''' + """ Remove an interface bound to a zone .. versionadded:: 2016.3.0 @@ -920,20 +917,20 @@ def remove_interface(zone, interface, permanent=True): .. code-block:: bash salt '*' firewalld.remove_interface zone eth0 - ''' + """ if interface not in get_interfaces(zone, permanent): - log.info('Interface is not bound to zone.') + log.info("Interface is not bound to zone.") - cmd = '--zone={0} --remove-interface={1}'.format(zone, interface) + cmd = "--zone={0} --remove-interface={1}".format(zone, interface) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def get_sources(zone, permanent=True): - ''' + """ List sources bound to a zone .. versionadded:: 2016.3.0 @@ -943,17 +940,17 @@ def get_sources(zone, permanent=True): .. code-block:: bash salt '*' firewalld.get_sources zone - ''' - cmd = '--zone={0} --list-sources'.format(zone) + """ + cmd = "--zone={0} --list-sources".format(zone) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd).split() def add_source(zone, source, permanent=True): - ''' + """ Bind a source to a zone .. versionadded:: 2016.3.0 @@ -963,20 +960,20 @@ def add_source(zone, source, permanent=True): .. code-block:: bash salt '*' firewalld.add_source zone 192.168.1.0/24 - ''' + """ if source in get_sources(zone, permanent): - log.info('Source is already bound to zone.') + log.info("Source is already bound to zone.") - cmd = '--zone={0} --add-source={1}'.format(zone, source) + cmd = "--zone={0} --add-source={1}".format(zone, source) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def remove_source(zone, source, permanent=True): - ''' + """ Remove a source bound to a zone .. versionadded:: 2016.3.0 @@ -986,20 +983,20 @@ def remove_source(zone, source, permanent=True): .. code-block:: bash salt '*' firewalld.remove_source zone 192.168.1.0/24 - ''' + """ if source not in get_sources(zone, permanent): - log.info('Source is not bound to zone.') + log.info("Source is not bound to zone.") - cmd = '--zone={0} --remove-source={1}'.format(zone, source) + cmd = "--zone={0} --remove-source={1}".format(zone, source) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def get_rich_rules(zone, permanent=True): - ''' + """ List rich rules bound to a zone .. versionadded:: 2016.11.0 @@ -1009,17 +1006,17 @@ def get_rich_rules(zone, permanent=True): .. code-block:: bash salt '*' firewalld.get_rich_rules zone - ''' - cmd = '--zone={0} --list-rich-rules'.format(zone) + """ + cmd = "--zone={0} --list-rich-rules".format(zone) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd).splitlines() def add_rich_rule(zone, rule, permanent=True): - ''' + """ Add a rich rule to a zone .. versionadded:: 2016.11.0 @@ -1029,17 +1026,17 @@ def add_rich_rule(zone, rule, permanent=True): .. code-block:: bash salt '*' firewalld.add_rich_rule zone 'rule' - ''' + """ cmd = "--zone={0} --add-rich-rule='{1}'".format(zone, rule) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) def remove_rich_rule(zone, rule, permanent=True): - ''' + """ Add a rich rule to a zone .. versionadded:: 2016.11.0 @@ -1049,10 +1046,10 @@ def remove_rich_rule(zone, rule, permanent=True): .. code-block:: bash salt '*' firewalld.remove_rich_rule zone 'rule' - ''' + """ cmd = "--zone={0} --remove-rich-rule='{1}'".format(zone, rule) if permanent: - cmd += ' --permanent' + cmd += " --permanent" return __firewall_cmd(cmd) diff --git a/salt/modules/freebsd_sysctl.py b/salt/modules/freebsd_sysctl.py index e15ed15febc..c8054992d8d 100644 --- a/salt/modules/freebsd_sysctl.py +++ b/salt/modules/freebsd_sysctl.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Module for viewing and modifying sysctl parameters -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -14,31 +15,34 @@ from salt.exceptions import CommandExecutionError from salt.ext import six # Define the module's virtual name -__virtualname__ = 'sysctl' +__virtualname__ = "sysctl" # Get logging started log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only runs on FreeBSD systems - ''' - if __grains__['os'] == 'FreeBSD': + """ + if __grains__["os"] == "FreeBSD": return __virtualname__ - return (False, 'The freebsd_sysctl execution module cannot be loaded: ' - 'only available on FreeBSD systems.') + return ( + False, + "The freebsd_sysctl execution module cannot be loaded: " + "only available on FreeBSD systems.", + ) -def _formatfor(name, value, config, tail=''): - if config == '/boot/loader.conf': +def _formatfor(name, value, config, tail=""): + if config == "/boot/loader.conf": return '{0}="{1}"{2}'.format(name, value, tail) else: - return '{0}={1}{2}'.format(name, value, tail) + return "{0}={1}{2}".format(name, value, tail) def show(config_file=False): - ''' + """ Return a list of sysctl parameters for this minion CLI Example: @@ -46,25 +50,25 @@ def show(config_file=False): .. code-block:: bash salt '*' sysctl.show - ''' + """ roots = ( - 'compat', - 'debug', - 'dev', - 'hptmv', - 'hw', - 'kern', - 'machdep', - 'net', - 'p1003_1b', - 'security', - 'user', - 'vfs', - 'vm' + "compat", + "debug", + "dev", + "hptmv", + "hw", + "kern", + "machdep", + "net", + "p1003_1b", + "security", + "user", + "vfs", + "vm", ) - cmd = 'sysctl -ae' + cmd = "sysctl -ae" ret = {} - comps = [''] + comps = [""] if config_file: # If the file doesn't exist, return an empty list @@ -72,31 +76,31 @@ def show(config_file=False): return [] try: - with salt.utils.files.fopen(config_file, 'r') as f: + with salt.utils.files.fopen(config_file, "r") as f: for line in f.readlines(): l = line.strip() if l != "" and not l.startswith("#"): - comps = line.split('=', 1) + comps = line.split("=", 1) ret[comps[0]] = comps[1] return ret except (OSError, IOError): - log.error('Could not open sysctl config file') + log.error("Could not open sysctl config file") return None else: - out = __salt__['cmd.run'](cmd, output_loglevel='trace') + out = __salt__["cmd.run"](cmd, output_loglevel="trace") for line in out.splitlines(): - if any([line.startswith('{0}.'.format(root)) for root in roots]): - comps = line.split('=', 1) + if any([line.startswith("{0}.".format(root)) for root in roots]): + comps = line.split("=", 1) ret[comps[0]] = comps[1] elif comps[0]: - ret[comps[0]] += '{0}\n'.format(line) + ret[comps[0]] += "{0}\n".format(line) else: continue return ret def get(name): - ''' + """ Return a single sysctl parameter for this minion CLI Example: @@ -104,14 +108,14 @@ def get(name): .. code-block:: bash salt '*' sysctl.get hw.physmem - ''' - cmd = 'sysctl -n {0}'.format(name) - out = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = "sysctl -n {0}".format(name) + out = __salt__["cmd.run"](cmd, python_shell=False) return out def assign(name, value): - ''' + """ Assign a single sysctl parameter for this minion CLI Example: @@ -119,21 +123,20 @@ def assign(name, value): .. code-block:: bash salt '*' sysctl.assign net.inet.icmp.icmplim 50 - ''' + """ ret = {} cmd = 'sysctl {0}="{1}"'.format(name, value) - data = __salt__['cmd.run_all'](cmd, python_shell=False) + data = __salt__["cmd.run_all"](cmd, python_shell=False) - if data['retcode'] != 0: - raise CommandExecutionError('sysctl failed: {0}'.format( - data['stderr'])) - new_name, new_value = data['stdout'].split(':', 1) - ret[new_name] = new_value.split(' -> ')[-1] + if data["retcode"] != 0: + raise CommandExecutionError("sysctl failed: {0}".format(data["stderr"])) + new_name, new_value = data["stdout"].split(":", 1) + ret[new_name] = new_value.split(" -> ")[-1] return ret -def persist(name, value, config='/etc/sysctl.conf'): - ''' +def persist(name, value, config="/etc/sysctl.conf"): + """ Assign and persist a simple sysctl parameter for this minion CLI Example: @@ -142,36 +145,36 @@ def persist(name, value, config='/etc/sysctl.conf'): salt '*' sysctl.persist net.inet.icmp.icmplim 50 salt '*' sysctl.persist coretemp_load NO config=/boot/loader.conf - ''' + """ nlines = [] edited = False value = six.text_type(value) - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: - line = salt.utils.stringutils.to_unicode(line).rstrip('\n') - if not line.startswith('{0}='.format(name)): + line = salt.utils.stringutils.to_unicode(line).rstrip("\n") + if not line.startswith("{0}=".format(name)): nlines.append(line) continue else: - key, rest = line.split('=', 1) + key, rest = line.split("=", 1) if rest.startswith('"'): _, rest_v, rest = rest.split('"', 2) - elif rest.startswith('\''): - _, rest_v, rest = rest.split('\'', 2) + elif rest.startswith("'"): + _, rest_v, rest = rest.split("'", 2) else: rest_v = rest.split()[0] - rest = rest[len(rest_v):] + rest = rest[len(rest_v) :] if rest_v == value: - return 'Already set' + return "Already set" new_line = _formatfor(key, value, config, rest) nlines.append(new_line) edited = True if not edited: nlines.append("{0}\n".format(_formatfor(name, value, config))) - with salt.utils.files.fopen(config, 'w+') as ofile: - nlines = [salt.utils.stringutils.to_str(_l) + '\n' for _l in nlines] + with salt.utils.files.fopen(config, "w+") as ofile: + nlines = [salt.utils.stringutils.to_str(_l) + "\n" for _l in nlines] ofile.writelines(nlines) - if config != '/boot/loader.conf': + if config != "/boot/loader.conf": assign(name, value) - return 'Updated' + return "Updated" diff --git a/salt/modules/freebsd_update.py b/salt/modules/freebsd_update.py index 32274f657a1..d2b175bc272 100644 --- a/salt/modules/freebsd_update.py +++ b/salt/modules/freebsd_update.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for freebsd-update utility on FreeBSD. .. versionadded:: 2017.7.0 @@ -7,78 +7,83 @@ Support for freebsd-update utility on FreeBSD. :maintainer: George Mamalakis :maturity: new :platform: FreeBSD -''' - -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging # Import salt libs import salt.utils.path -from salt.ext import six from salt.exceptions import CommandNotFoundError +from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'freebsd_update' -__virtual_aliases__ = ('freebsd-update',) +__virtualname__ = "freebsd_update" +__virtual_aliases__ = ("freebsd-update",) def __virtual__(): - ''' + """ .. versionadded:: 2016.3.4 Only work on FreeBSD RELEASEs >= 6.2, where freebsd-update was introduced. - ''' - if __grains__['os'] != 'FreeBSD': - return (False, 'The freebsd_update execution module cannot be loaded: only available on FreeBSD systems.') - if float(__grains__['osrelease']) < 6.2: - return (False, 'freebsd_update is only available on FreeBSD versions >= 6.2-RELESE') - if 'release' not in __grains__['kernelrelease'].lower(): - return (False, 'freebsd_update is only available on FreeBSD RELEASES') + """ + if __grains__["os"] != "FreeBSD": + return ( + False, + "The freebsd_update execution module cannot be loaded: only available on FreeBSD systems.", + ) + if float(__grains__["osrelease"]) < 6.2: + return ( + False, + "freebsd_update is only available on FreeBSD versions >= 6.2-RELESE", + ) + if "release" not in __grains__["kernelrelease"].lower(): + return (False, "freebsd_update is only available on FreeBSD RELEASES") return __virtualname__ def _cmd(**kwargs): - ''' + """ .. versionadded:: 2016.3.4 Private function that returns the freebsd-update command string to be executed. It checks if any arguments are given to freebsd-update and appends them accordingly. - ''' - update_cmd = salt.utils.path.which('freebsd-update') + """ + update_cmd = salt.utils.path.which("freebsd-update") if not update_cmd: raise CommandNotFoundError('"freebsd-update" command not found') params = [] - if 'basedir' in kwargs: - params.append('-b {0}'.format(kwargs['basedir'])) - if 'workdir' in kwargs: - params.append('-d {0}'.format(kwargs['workdir'])) - if 'conffile' in kwargs: - params.append('-f {0}'.format(kwargs['conffile'])) - if 'force' in kwargs: - params.append('-F') - if 'key' in kwargs: - params.append('-k {0}'.format(kwargs['key'])) - if 'newrelease' in kwargs: - params.append('-r {0}'.format(kwargs['newrelease'])) - if 'server' in kwargs: - params.append('-s {0}'.format(kwargs['server'])) - if 'address' in kwargs: - params.append('-t {0}'.format(kwargs['address'])) + if "basedir" in kwargs: + params.append("-b {0}".format(kwargs["basedir"])) + if "workdir" in kwargs: + params.append("-d {0}".format(kwargs["workdir"])) + if "conffile" in kwargs: + params.append("-f {0}".format(kwargs["conffile"])) + if "force" in kwargs: + params.append("-F") + if "key" in kwargs: + params.append("-k {0}".format(kwargs["key"])) + if "newrelease" in kwargs: + params.append("-r {0}".format(kwargs["newrelease"])) + if "server" in kwargs: + params.append("-s {0}".format(kwargs["server"])) + if "address" in kwargs: + params.append("-t {0}".format(kwargs["address"])) if params: - return '{0} {1}'.format(update_cmd, ' '.join(params)) + return "{0} {1}".format(update_cmd, " ".join(params)) return update_cmd -def _wrapper(orig, pre='', post='', err_=None, run_args=None, **kwargs): - ''' +def _wrapper(orig, pre="", post="", err_=None, run_args=None, **kwargs): + """ Helper function that wraps the execution of freebsd-update command. orig: @@ -98,33 +103,37 @@ def _wrapper(orig, pre='', post='', err_=None, run_args=None, **kwargs): kwargs: Parameters of freebsd-update command. - ''' - ret = '' # the message to be returned + """ + ret = "" # the message to be returned cmd = _cmd(**kwargs) - cmd_str = ' '.join([x for x in (pre, cmd, post, orig)]) + cmd_str = " ".join([x for x in (pre, cmd, post, orig)]) if run_args and isinstance(run_args, dict): - res = __salt__['cmd.run_all'](cmd_str, **run_args) + res = __salt__["cmd.run_all"](cmd_str, **run_args) else: - res = __salt__['cmd.run_all'](cmd_str) + res = __salt__["cmd.run_all"](cmd_str) if isinstance(err_, dict): # copy return values if asked to for k, v in six.itermitems(res): err_[k] = v - if 'retcode' in res and res['retcode'] != 0: - msg = ' '.join([x for x in (res['stdout'], res['stderr']) if x]) - ret = 'Unable to run "{0}" with run_args="{1}". Error: {2}'.format(cmd_str, run_args, msg) + if "retcode" in res and res["retcode"] != 0: + msg = " ".join([x for x in (res["stdout"], res["stderr"]) if x]) + ret = 'Unable to run "{0}" with run_args="{1}". Error: {2}'.format( + cmd_str, run_args, msg + ) log.error(ret) else: try: - ret = res['stdout'] + ret = res["stdout"] except KeyError: - log.error("cmd.run_all did not return a dictionary with a key named 'stdout'") + log.error( + "cmd.run_all did not return a dictionary with a key named 'stdout'" + ) return ret def fetch(**kwargs): - ''' + """ .. versionadded:: 2016.3.4 freebsd-update fetch wrapper. Based on the currently installed world and the @@ -132,21 +141,21 @@ def fetch(**kwargs): kwargs: Parameters of freebsd-update command. - ''' + """ # fetch continues when no controlling terminal is present - pre = '' - post = '' + pre = "" + post = "" run_args = {} - if float(__grains__['osrelease']) >= 10.2: - post += '--not-running-from-cron' + if float(__grains__["osrelease"]) >= 10.2: + post += "--not-running-from-cron" else: - pre += ' env PAGER=cat' - run_args['python_shell'] = True - return _wrapper('fetch', pre=pre, post=post, run_args=run_args, **kwargs) + pre += " env PAGER=cat" + run_args["python_shell"] = True + return _wrapper("fetch", pre=pre, post=post, run_args=run_args, **kwargs) def install(**kwargs): - ''' + """ .. versionadded:: 2016.3.4 freebsd-update install wrapper. Install the most recently fetched updates or @@ -154,12 +163,12 @@ def install(**kwargs): kwargs: Parameters of freebsd-update command. - ''' - return _wrapper('install', **kwargs) + """ + return _wrapper("install", **kwargs) def rollback(**kwargs): - ''' + """ .. versionadded:: 2016.3.4 freebsd-update rollback wrapper. Uninstalls the most recently installed @@ -167,12 +176,12 @@ def rollback(**kwargs): kwargs: Parameters of freebsd-update command. - ''' - return _wrapper('rollback', **kwargs) + """ + return _wrapper("rollback", **kwargs) def update(**kwargs): - ''' + """ .. versionadded:: 2016.3.4 Command that simplifies freebsd-update by running freebsd-update fetch first @@ -180,21 +189,21 @@ def update(**kwargs): kwargs: Parameters of freebsd-update command. - ''' + """ stdout = {} - for mode in ('fetch', 'install'): + for mode in ("fetch", "install"): err_ = {} ret = _wrapper(mode, err_=err_, **kwargs) - if 'retcode' in err_ and err_['retcode'] != 0: + if "retcode" in err_ and err_["retcode"] != 0: return ret - if 'stdout' in err_: - stdout[mode] = err_['stdout'] - return '\n'.join(['{0}: {1}'.format(k, v) for (k, v) in six.iteritems(stdout)]) + if "stdout" in err_: + stdout[mode] = err_["stdout"] + return "\n".join(["{0}: {1}".format(k, v) for (k, v) in six.iteritems(stdout)]) def ids(**kwargs): - ''' + """ .. versionadded:: 2016.3.4 freebsd-update IDS wrapper function. Compares the system against a "known @@ -202,12 +211,12 @@ def ids(**kwargs): kwargs: Parameters of freebsd-update command. - ''' - return _wrapper('IDS', **kwargs) + """ + return _wrapper("IDS", **kwargs) def upgrade(**kwargs): - ''' + """ .. versionadded:: 2016.3.4 Dummy function used only to print a message that upgrade is not available. @@ -221,7 +230,7 @@ def upgrade(**kwargs): kwargs: Parameters of freebsd-update command. - ''' - msg = 'freebsd-update upgrade not yet implemented.' + """ + msg = "freebsd-update upgrade not yet implemented." log.warning(msg) return msg diff --git a/salt/modules/freebsdjail.py b/salt/modules/freebsdjail.py index 9ccec04d53d..94211fbd43f 100644 --- a/salt/modules/freebsdjail.py +++ b/salt/modules/freebsdjail.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" The jail module for FreeBSD -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os import re import subprocess @@ -15,21 +16,24 @@ import salt.utils.files import salt.utils.stringutils # Define the module's virtual name -__virtualname__ = 'jail' +__virtualname__ = "jail" def __virtual__(): - ''' + """ Only runs on FreeBSD systems - ''' - if __grains__['os'] == 'FreeBSD': + """ + if __grains__["os"] == "FreeBSD": return __virtualname__ - return (False, 'The freebsdjail execution module cannot be loaded: ' - 'only available on FreeBSD systems.') + return ( + False, + "The freebsdjail execution module cannot be loaded: " + "only available on FreeBSD systems.", + ) -def start(jail=''): - ''' +def start(jail=""): + """ Start the specified jail or all, if none specified CLI Example: @@ -37,13 +41,13 @@ def start(jail=''): .. code-block:: bash salt '*' jail.start [] - ''' - cmd = 'service jail onestart {0}'.format(jail) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "service jail onestart {0}".format(jail) + return not __salt__["cmd.retcode"](cmd) -def stop(jail=''): - ''' +def stop(jail=""): + """ Stop the specified jail or all, if none specified CLI Example: @@ -51,13 +55,13 @@ def stop(jail=''): .. code-block:: bash salt '*' jail.stop [] - ''' - cmd = 'service jail onestop {0}'.format(jail) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "service jail onestop {0}".format(jail) + return not __salt__["cmd.retcode"](cmd) -def restart(jail=''): - ''' +def restart(jail=""): + """ Restart the specified jail or all, if none specified CLI Example: @@ -65,13 +69,13 @@ def restart(jail=''): .. code-block:: bash salt '*' jail.restart [] - ''' - cmd = 'service jail onerestart {0}'.format(jail) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "service jail onerestart {0}".format(jail) + return not __salt__["cmd.retcode"](cmd) def is_enabled(): - ''' + """ See if jail service is actually enabled on boot CLI Example: @@ -79,17 +83,17 @@ def is_enabled(): .. code-block:: bash salt '*' jail.is_enabled - ''' - cmd = 'service -e' - services = __salt__['cmd.run'](cmd, python_shell=False) - for service in services.split('\\n'): - if re.search('jail', service): + """ + cmd = "service -e" + services = __salt__["cmd.run"](cmd, python_shell=False) + for service in services.split("\\n"): + if re.search("jail", service): return True return False def get_enabled(): - ''' + """ Return which jails are set to be run CLI Example: @@ -97,16 +101,16 @@ def get_enabled(): .. code-block:: bash salt '*' jail.get_enabled - ''' + """ ret = [] - for rconf in ('/etc/rc.conf', '/etc/rc.conf.local'): + for rconf in ("/etc/rc.conf", "/etc/rc.conf.local"): if os.access(rconf, os.R_OK): - with salt.utils.files.fopen(rconf, 'r') as _fp: + with salt.utils.files.fopen(rconf, "r") as _fp: for line in _fp: line = salt.utils.stringutils.to_unicode(line) if not line.strip(): continue - if not line.startswith('jail_list='): + if not line.startswith("jail_list="): continue jails = line.split('"')[1].split() for j in jails: @@ -115,7 +119,7 @@ def get_enabled(): def show_config(jail): - ''' + """ Display specified jail's configuration CLI Example: @@ -123,57 +127,59 @@ def show_config(jail): .. code-block:: bash salt '*' jail.show_config - ''' + """ ret = {} if subprocess.call(["jls", "-nq", "-j", jail]) == 0: - jls = subprocess.check_output(["jls", "-nq", "-j", jail]) # pylint: disable=minimum-python-version + jls = subprocess.check_output( + ["jls", "-nq", "-j", jail] + ) # pylint: disable=minimum-python-version jailopts = salt.utils.args.shlex_split(salt.utils.stringutils.to_unicode(jls)) for jailopt in jailopts: - if '=' not in jailopt: - ret[jailopt.strip().rstrip(";")] = '1' + if "=" not in jailopt: + ret[jailopt.strip().rstrip(";")] = "1" else: - key = jailopt.split('=')[0].strip() - value = jailopt.split('=')[-1].strip().strip("\"") + key = jailopt.split("=")[0].strip() + value = jailopt.split("=")[-1].strip().strip('"') ret[key] = value else: - for rconf in ('/etc/rc.conf', '/etc/rc.conf.local'): + for rconf in ("/etc/rc.conf", "/etc/rc.conf.local"): if os.access(rconf, os.R_OK): - with salt.utils.files.fopen(rconf, 'r') as _fp: + with salt.utils.files.fopen(rconf, "r") as _fp: for line in _fp: line = salt.utils.stringutils.to_unicode(line) if not line.strip(): continue - if not line.startswith('jail_{0}_'.format(jail)): + if not line.startswith("jail_{0}_".format(jail)): continue - key, value = line.split('=') - ret[key.split('_', 2)[2]] = value.split('"')[1] - for jconf in ('/etc/jail.conf', '/usr/local/etc/jail.conf'): + key, value = line.split("=") + ret[key.split("_", 2)[2]] = value.split('"')[1] + for jconf in ("/etc/jail.conf", "/usr/local/etc/jail.conf"): if os.access(jconf, os.R_OK): - with salt.utils.files.fopen(jconf, 'r') as _fp: + with salt.utils.files.fopen(jconf, "r") as _fp: for line in _fp: line = salt.utils.stringutils.to_unicode(line) - line = line.partition('#')[0].strip() + line = line.partition("#")[0].strip() if line: - if line.split()[-1] == '{': - if line.split()[0] != jail and line.split()[0] != '*': - while line.split()[-1] != '}': + if line.split()[-1] == "{": + if line.split()[0] != jail and line.split()[0] != "*": + while line.split()[-1] != "}": line = next(_fp) - line = line.partition('#')[0].strip() + line = line.partition("#")[0].strip() else: continue - if line.split()[-1] == '}': + if line.split()[-1] == "}": continue - if '=' not in line: - ret[line.strip().rstrip(";")] = '1' + if "=" not in line: + ret[line.strip().rstrip(";")] = "1" else: - key = line.split('=')[0].strip() - value = line.split('=')[-1].strip().strip(";'\"") + key = line.split("=")[0].strip() + value = line.split("=")[-1].strip().strip(";'\"") ret[key] = value return ret def fstab(jail): - ''' + """ Display contents of a fstab(5) file defined in specified jail's configuration. If no file is defined, return False. @@ -182,43 +188,45 @@ def fstab(jail): .. code-block:: bash salt '*' jail.fstab - ''' + """ ret = [] config = show_config(jail) - if 'fstab' in config: - c_fstab = config['fstab'] - elif 'mount.fstab' in config: - c_fstab = config['mount.fstab'] - if 'fstab' in config or 'mount.fstab' in config: + if "fstab" in config: + c_fstab = config["fstab"] + elif "mount.fstab" in config: + c_fstab = config["mount.fstab"] + if "fstab" in config or "mount.fstab" in config: if os.access(c_fstab, os.R_OK): - with salt.utils.files.fopen(c_fstab, 'r') as _fp: + with salt.utils.files.fopen(c_fstab, "r") as _fp: for line in _fp: line = salt.utils.stringutils.to_unicode(line) line = line.strip() if not line: continue - if line.startswith('#'): + if line.startswith("#"): continue try: device, mpoint, fstype, opts, dump, pas_ = line.split() except ValueError: # Gracefully continue on invalid lines continue - ret.append({ - 'device': device, - 'mountpoint': mpoint, - 'fstype': fstype, - 'options': opts, - 'dump': dump, - 'pass': pas_ - }) + ret.append( + { + "device": device, + "mountpoint": mpoint, + "fstype": fstype, + "options": opts, + "dump": dump, + "pass": pas_, + } + ) if not ret: ret = False return ret def status(jail): - ''' + """ See if specified jail is currently running CLI Example: @@ -226,17 +234,17 @@ def status(jail): .. code-block:: bash salt '*' jail.status - ''' - cmd = 'jls' - found_jails = __salt__['cmd.run'](cmd, python_shell=False) - for found_jail in found_jails.split('\\n'): + """ + cmd = "jls" + found_jails = __salt__["cmd.run"](cmd, python_shell=False) + for found_jail in found_jails.split("\\n"): if re.search(jail, found_jail): return True return False def sysctl(): - ''' + """ Dump all jail related kernel states (sysctl) CLI Example: @@ -244,10 +252,10 @@ def sysctl(): .. code-block:: bash salt '*' jail.sysctl - ''' + """ ret = {} - sysctl_jail = __salt__['cmd.run']('sysctl security.jail') + sysctl_jail = __salt__["cmd.run"]("sysctl security.jail") for line in sysctl_jail.splitlines(): - key, value = line.split(':', 1) + key, value = line.split(":", 1) ret[key.strip()] = value.strip() return ret diff --git a/salt/modules/freebsdkmod.py b/salt/modules/freebsdkmod.py index ad47eab10cb..037ba11b155 100644 --- a/salt/modules/freebsdkmod.py +++ b/salt/modules/freebsdkmod.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Module to manage FreeBSD kernel modules -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os @@ -12,49 +12,52 @@ import re import salt.utils.files # Define the module's virtual name -__virtualname__ = 'kmod' +__virtualname__ = "kmod" _LOAD_MODULE = '{0}_load="YES"' -_LOADER_CONF = '/boot/loader.conf' +_LOADER_CONF = "/boot/loader.conf" _MODULE_RE = '^{0}_load="YES"' _MODULES_RE = r'^(\w+)_load="YES"' def __virtual__(): - ''' + """ Only runs on FreeBSD systems - ''' - if __grains__['kernel'] == 'FreeBSD': + """ + if __grains__["kernel"] == "FreeBSD": return __virtualname__ - return (False, 'The freebsdkmod execution module cannot be loaded: only available on FreeBSD systems.') + return ( + False, + "The freebsdkmod execution module cannot be loaded: only available on FreeBSD systems.", + ) def _new_mods(pre_mods, post_mods): - ''' + """ Return a list of the new modules, pass an kldstat dict before running modprobe and one after modprobe has run - ''' + """ pre = set() post = set() for mod in pre_mods: - pre.add(mod['module']) + pre.add(mod["module"]) for mod in post_mods: - post.add(mod['module']) + post.add(mod["module"]) return post - pre def _rm_mods(pre_mods, post_mods): - ''' + """ Return a list of the new modules, pass an kldstat dict before running modprobe and one after modprobe has run - ''' + """ pre = set() post = set() for mod in pre_mods: - pre.add(mod['module']) + pre.add(mod["module"]) for mod in post_mods: - post.add(mod['module']) + post.add(mod["module"]) return pre - post @@ -66,11 +69,11 @@ def _get_module_name(line): def _get_persistent_modules(): - ''' + """ Returns a list of modules in loader.conf that load on boot. - ''' + """ mods = set() - with salt.utils.files.fopen(_LOADER_CONF, 'r') as loader_conf: + with salt.utils.files.fopen(_LOADER_CONF, "r") as loader_conf: for line in loader_conf: line = salt.utils.stringutils.to_unicode(line) line = line.strip() @@ -81,34 +84,33 @@ def _get_persistent_modules(): def _set_persistent_module(mod): - ''' + """ Add a module to loader.conf to make it persistent. - ''' - if not mod or mod in mod_list(True) or mod not in \ - available(): + """ + if not mod or mod in mod_list(True) or mod not in available(): return set() - __salt__['file.append'](_LOADER_CONF, _LOAD_MODULE.format(mod)) + __salt__["file.append"](_LOADER_CONF, _LOAD_MODULE.format(mod)) return set([mod]) def _remove_persistent_module(mod, comment): - ''' + """ Remove module from loader.conf. If comment is true only comment line where module is. - ''' + """ if not mod or mod not in mod_list(True): return set() if comment: - __salt__['file.comment'](_LOADER_CONF, _MODULE_RE.format(mod)) + __salt__["file.comment"](_LOADER_CONF, _MODULE_RE.format(mod)) else: - __salt__['file.sed'](_LOADER_CONF, _MODULE_RE.format(mod), '') + __salt__["file.sed"](_LOADER_CONF, _MODULE_RE.format(mod), "") return set([mod]) def available(): - ''' + """ Return a list of all available kernel modules CLI Example: @@ -116,19 +118,19 @@ def available(): .. code-block:: bash salt '*' kmod.available - ''' + """ ret = [] - for path in __salt__['file.find']('/boot/kernel', name='*.ko$'): + for path in __salt__["file.find"]("/boot/kernel", name="*.ko$"): bpath = os.path.basename(path) - comps = bpath.split('.') - if 'ko' in comps: + comps = bpath.split(".") + if "ko" in comps: # This is a kernel module, return it without the .ko extension - ret.append('.'.join(comps[:comps.index('ko')])) + ret.append(".".join(comps[: comps.index("ko")])) return ret def check_available(mod): - ''' + """ Check to see if the specified kernel module is available CLI Example: @@ -136,12 +138,12 @@ def check_available(mod): .. code-block:: bash salt '*' kmod.check_available vmm - ''' + """ return mod in available() def lsmod(): - ''' + """ Return a dict containing information about currently loaded modules CLI Example: @@ -149,26 +151,22 @@ def lsmod(): .. code-block:: bash salt '*' kmod.lsmod - ''' + """ ret = [] - for line in __salt__['cmd.run']('kldstat').splitlines(): + for line in __salt__["cmd.run"]("kldstat").splitlines(): comps = line.split() if not len(comps) > 2: continue - if comps[0] == 'Id': + if comps[0] == "Id": continue - if comps[4] == 'kernel': + if comps[4] == "kernel": continue - ret.append({ - 'module': comps[4][:-3], - 'size': comps[3], - 'depcount': comps[1] - }) + ret.append({"module": comps[4][:-3], "size": comps[3], "depcount": comps[1]}) return ret def mod_list(only_persist=False): - ''' + """ Return a list of the loaded module names CLI Example: @@ -176,7 +174,7 @@ def mod_list(only_persist=False): .. code-block:: bash salt '*' kmod.mod_list - ''' + """ mods = set() if only_persist: if not _get_persistent_modules(): @@ -185,12 +183,12 @@ def mod_list(only_persist=False): mods.add(mod) else: for mod in lsmod(): - mods.add(mod['module']) + mods.add(mod["module"]) return sorted(list(mods)) def load(mod, persist=False): - ''' + """ Load the specified kernel module mod @@ -204,18 +202,17 @@ def load(mod, persist=False): .. code-block:: bash salt '*' kmod.load bhyve - ''' + """ pre_mods = lsmod() - response = __salt__['cmd.run_all']('kldload {0}'.format(mod), - python_shell=False) - if response['retcode'] == 0: + response = __salt__["cmd.run_all"]("kldload {0}".format(mod), python_shell=False) + if response["retcode"] == 0: post_mods = lsmod() mods = _new_mods(pre_mods, post_mods) persist_mods = set() if persist: persist_mods = _set_persistent_module(mod) return sorted(list(mods | persist_mods)) - elif 'module already loaded or in kernel' in response['stderr']: + elif "module already loaded or in kernel" in response["stderr"]: if persist and mod not in _get_persistent_modules(): persist_mods = _set_persistent_module(mod) return sorted(list(persist_mods)) @@ -223,11 +220,11 @@ def load(mod, persist=False): # It's compiled into the kernel return [None] else: - return 'Module {0} not found'.format(mod) + return "Module {0} not found".format(mod) def is_loaded(mod): - ''' + """ Check to see if the specified kernel module is loaded CLI Example: @@ -235,12 +232,12 @@ def is_loaded(mod): .. code-block:: bash salt '*' kmod.is_loaded vmm - ''' + """ return mod in mod_list() def remove(mod, persist=False, comment=True): - ''' + """ Remove the specified kernel module mod @@ -258,11 +255,10 @@ def remove(mod, persist=False, comment=True): .. code-block:: bash salt '*' kmod.remove vmm - ''' + """ pre_mods = lsmod() - res = __salt__['cmd.run_all']('kldunload {0}'.format(mod), - python_shell=False) - if res['retcode'] == 0: + res = __salt__["cmd.run_all"]("kldunload {0}".format(mod), python_shell=False) + if res["retcode"] == 0: post_mods = lsmod() mods = _rm_mods(pre_mods, post_mods) persist_mods = set() @@ -270,4 +266,4 @@ def remove(mod, persist=False, comment=True): persist_mods = _remove_persistent_module(mod, comment) return sorted(list(mods | persist_mods)) else: - return 'Error removing module {0}: {1}'.format(mod, res['stderr']) + return "Error removing module {0}: {1}".format(mod, res["stderr"]) diff --git a/salt/modules/freebsdpkg.py b/salt/modules/freebsdpkg.py index 43f127ef359..b5680b39a2f 100644 --- a/salt/modules/freebsdpkg.py +++ b/salt/modules/freebsdpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Remote package support using ``pkg_add(1)`` .. important:: @@ -71,7 +71,7 @@ variables, if set, but these values can also be overridden in several ways: zsh: pkg.installed: - fromrepo: ftp://ftp2.freebsd.org/ -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -89,29 +89,37 @@ from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Load as 'pkg' on FreeBSD versions less than 10. Don't load on FreeBSD 9 when the config option ``providers:pkg`` is set to 'pkgng'. - ''' - if __grains__['os'] == 'FreeBSD' and float(__grains__['osrelease']) < 10: + """ + if __grains__["os"] == "FreeBSD" and float(__grains__["osrelease"]) < 10: providers = {} - if 'providers' in __opts__: - providers = __opts__['providers'] - if providers and 'pkg' in providers and providers['pkg'] == 'pkgng': - log.debug('Configuration option \'providers:pkg\' is set to ' - '\'pkgng\', won\'t load old provider \'freebsdpkg\'.') - return (False, 'The freebsdpkg execution module cannot be loaded: the configuration option \'providers:pkg\' is set to \'pkgng\'') + if "providers" in __opts__: + providers = __opts__["providers"] + if providers and "pkg" in providers and providers["pkg"] == "pkgng": + log.debug( + "Configuration option 'providers:pkg' is set to " + "'pkgng', won't load old provider 'freebsdpkg'." + ) + return ( + False, + "The freebsdpkg execution module cannot be loaded: the configuration option 'providers:pkg' is set to 'pkgng'", + ) return __virtualname__ - return (False, 'The freebsdpkg execution module cannot be loaded: either the os is not FreeBSD or the version of FreeBSD is >= 10.') + return ( + False, + "The freebsdpkg execution module cannot be loaded: either the os is not FreeBSD or the version of FreeBSD is >= 10.", + ) def _get_repo_options(fromrepo=None, packagesite=None): - ''' + """ Return a list of tuples to seed the "env" list, which is used to set environment variables for any pkg_add commands that are spawned. @@ -122,34 +130,40 @@ def _get_repo_options(fromrepo=None, packagesite=None): freebsdpkg.PACKAGEROOT nor freebsdpkg.PACKAGESITE are specified, then an empty list is returned, and it is assumed that the system defaults (or environment variables) will be used. - ''' - root = fromrepo if fromrepo is not None \ - else __salt__['config.get']('freebsdpkg.PACKAGEROOT', None) - site = packagesite if packagesite is not None \ - else __salt__['config.get']('freebsdpkg.PACKAGESITE', None) + """ + root = ( + fromrepo + if fromrepo is not None + else __salt__["config.get"]("freebsdpkg.PACKAGEROOT", None) + ) + site = ( + packagesite + if packagesite is not None + else __salt__["config.get"]("freebsdpkg.PACKAGESITE", None) + ) ret = {} if root is not None: - ret['PACKAGEROOT'] = root + ret["PACKAGEROOT"] = root if site is not None: - ret['PACKAGESITE'] = site + ret["PACKAGESITE"] = site return ret def _match(names): - ''' + """ Since pkg_delete requires the full "pkgname-version" string, this function will attempt to match the package name with its version. Returns a list of partial matches and package names that match the "pkgname-version" string required by pkg_delete, and a list of errors encountered. - ''' + """ pkgs = list_pkgs(versions_as_list=True) errors = [] # Look for full matches full_pkg_strings = [] - out = __salt__['cmd.run_stdout'](['pkg_info'], - output_loglevel='trace', - python_shell=False) + out = __salt__["cmd.run_stdout"]( + ["pkg_info"], output_loglevel="trace", python_shell=False + ) for line in out.splitlines(): try: full_pkg_strings.append(line.split()[0]) @@ -164,28 +178,26 @@ def _match(names): cver = pkgs.get(name) if cver is not None: if len(cver) == 1: - matches.append('{0}-{1}'.format(name, cver[0])) + matches.append("{0}-{1}".format(name, cver[0])) else: ambiguous.append(name) errors.append( - 'Ambiguous package \'{0}\'. Full name/version required. ' - 'Possible matches: {1}'.format( - name, - ', '.join(['{0}-{1}'.format(name, x) for x in cver]) + "Ambiguous package '{0}'. Full name/version required. " + "Possible matches: {1}".format( + name, ", ".join(["{0}-{1}".format(name, x) for x in cver]) ) ) # Find packages that did not match anything - not_matched = \ - set(names) - set(matches) - set(full_matches) - set(ambiguous) + not_matched = set(names) - set(matches) - set(full_matches) - set(ambiguous) for name in not_matched: - errors.append('Package \'{0}\' not found'.format(name)) + errors.append("Package '{0}' not found".format(name)) return matches + full_matches, errors def latest_version(*names, **kwargs): - ''' + """ ``pkg_add(1)`` is not capable of querying for remote packages, so this function will always return results as if there is no package available for install or upgrade. @@ -196,16 +208,18 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version ... - ''' - return '' if len(names) == 1 else dict((x, '') for x in names) + """ + return "" if len(names) == 1 else dict((x, "") for x in names) # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -223,23 +237,25 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' - with_origin = kwargs.pop('with_origin', False) - ret = __salt__['pkg_resource.version'](*names, **kwargs) + """ + with_origin = kwargs.pop("with_origin", False) + ret = __salt__["pkg_resource.version"](*names, **kwargs) if not salt.utils.data.is_true(with_origin): return ret # Put the return value back into a dict since we're adding a subdict if len(names) == 1: ret = {names[0]: ret} - origins = __context__.get('pkg.origin', {}) - return dict([ - (x, {'origin': origins.get(x, ''), 'version': y}) - for x, y in six.iteritems(ret) - ]) + origins = __context__.get("pkg.origin", {}) + return dict( + [ + (x, {"origin": origins.get(x, ""), "version": y}) + for x, y in six.iteritems(ret) + ] + ) def refresh_db(): - ''' + """ ``pkg_add(1)`` does not use a local database of available packages, so this function simply returns ``True``. it exists merely for API compatibility. @@ -248,14 +264,14 @@ def refresh_db(): .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) return True def list_pkgs(versions_as_list=False, with_origin=False, **kwargs): - ''' + """ List the packages currently installed as a dict:: {'': ''} @@ -271,61 +287,61 @@ def list_pkgs(versions_as_list=False, with_origin=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) + if "pkg.list_pkgs" in __context__: + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) if salt.utils.data.is_true(with_origin): - origins = __context__.get('pkg.origin', {}) - return dict([ - (x, {'origin': origins.get(x, ''), 'version': y}) - for x, y in six.iteritems(ret) - ]) + origins = __context__.get("pkg.origin", {}) + return dict( + [ + (x, {"origin": origins.get(x, ""), "version": y}) + for x, y in six.iteritems(ret) + ] + ) return ret ret = {} origins = {} - out = __salt__['cmd.run_stdout'](['pkg_info', '-ao'], - output_loglevel='trace', - python_shell=False) - pkgs_re = re.compile(r'Information for ([^:]+):\s*Origin:\n([^\n]+)') + out = __salt__["cmd.run_stdout"]( + ["pkg_info", "-ao"], output_loglevel="trace", python_shell=False + ) + pkgs_re = re.compile(r"Information for ([^:]+):\s*Origin:\n([^\n]+)") for pkg, origin in pkgs_re.findall(out): if not pkg: continue try: - pkgname, pkgver = pkg.rsplit('-', 1) + pkgname, pkgver = pkg.rsplit("-", 1) except ValueError: continue - __salt__['pkg_resource.add_pkg'](ret, pkgname, pkgver) + __salt__["pkg_resource.add_pkg"](ret, pkgname, pkgver) origins[pkgname] = origin - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) - __context__['pkg.origin'] = origins + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) + __context__["pkg.origin"] = origins if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) if salt.utils.data.is_true(with_origin): - return dict([ - (x, {'origin': origins.get(x, ''), 'version': y}) - for x, y in six.iteritems(ret) - ]) + return dict( + [ + (x, {"origin": origins.get(x, ""), "version": y}) + for x, y in six.iteritems(ret) + ] + ) return ret -def install(name=None, - refresh=False, - fromrepo=None, - pkgs=None, - sources=None, - **kwargs): - ''' +def install(name=None, refresh=False, fromrepo=None, pkgs=None, sources=None, **kwargs): + """ Install package(s) using ``pkg_add(1)`` name @@ -376,9 +392,9 @@ def install(name=None, .. code-block:: bash salt '*' pkg.install - ''' + """ try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: @@ -387,46 +403,43 @@ def install(name=None, if not pkg_params: return {} - packageroot = kwargs.get('packageroot') + packageroot = kwargs.get("packageroot") if not fromrepo and packageroot: fromrepo = packageroot - env = _get_repo_options(fromrepo, kwargs.get('packagesite')) + env = _get_repo_options(fromrepo, kwargs.get("packagesite")) args = [] - if pkg_type == 'repository': - args.append('-r') # use remote repo + if pkg_type == "repository": + args.append("-r") # use remote repo args.extend(pkg_params) old = list_pkgs() - out = __salt__['cmd.run_all']( - ['pkg_add'] + args, - env=env, - output_loglevel='trace', - python_shell=False + out = __salt__["cmd.run_all"]( + ["pkg_add"] + args, env=env, output_loglevel="trace", python_shell=False ) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() _rehash() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def remove(name=None, pkgs=None, **kwargs): - ''' + """ Remove packages using ``pkg_delete(1)`` name @@ -451,9 +464,9 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -464,47 +477,45 @@ def remove(name=None, pkgs=None, **kwargs): if not targets: return {} - out = __salt__['cmd.run_all']( - ['pkg_delete'] + targets, - output_loglevel='trace', - python_shell=False + out = __salt__["cmd.run_all"]( + ["pkg_delete"] + targets, output_loglevel="trace", python_shell=False ) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret # Support pkg.delete to remove packages to more closely match pkg_delete -delete = salt.utils.functools.alias_function(remove, 'delete') +delete = salt.utils.functools.alias_function(remove, "delete") # No equivalent to purge packages, use remove instead -purge = salt.utils.functools.alias_function(remove, 'purge') +purge = salt.utils.functools.alias_function(remove, "purge") def _rehash(): - ''' + """ Recomputes internal hash table for the PATH variable. Use whenever a new command is created during the current session. - ''' - shell = __salt__['environ.get']('SHELL') - if shell.split('/')[-1] in ('csh', 'tcsh'): - __salt__['cmd.shell']('rehash', output_loglevel='trace') + """ + shell = __salt__["environ.get"]("SHELL") + if shell.split("/")[-1] in ("csh", "tcsh"): + __salt__["cmd.shell"]("rehash", output_loglevel="trace") def file_list(*packages): - ''' + """ List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -516,17 +527,17 @@ def file_list(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' + """ ret = file_dict(*packages) files = [] - for pkg_files in six.itervalues(ret['files']): + for pkg_files in six.itervalues(ret["files"]): files.extend(pkg_files) - ret['files'] = files + ret["files"] = files return ret def file_dict(*packages): - ''' + """ List the files that belong to a package, grouped by package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -538,32 +549,30 @@ def file_dict(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' + """ errors = [] files = {} if packages: - match_pattern = '\'{0}-[0-9]*\'' - cmd = ['pkg_info', '-QL'] + [match_pattern.format(p) for p in packages] + match_pattern = "'{0}-[0-9]*'" + cmd = ["pkg_info", "-QL"] + [match_pattern.format(p) for p in packages] else: - cmd = ['pkg_info', '-QLa'] + cmd = ["pkg_info", "-QLa"] - ret = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + ret = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - for line in ret['stderr'].splitlines(): + for line in ret["stderr"].splitlines(): errors.append(line) pkg = None - for line in ret['stdout'].splitlines(): - if pkg is not None and line.startswith('/'): + for line in ret["stdout"].splitlines(): + if pkg is not None and line.startswith("/"): files[pkg].append(line) - elif ':/' in line: - pkg, fn = line.split(':', 1) - pkg, ver = pkg.rsplit('-', 1) + elif ":/" in line: + pkg, fn = line.split(":", 1) + pkg, ver = pkg.rsplit("-", 1) files[pkg] = [fn] else: continue # unexpected string - return {'errors': errors, 'files': files} + return {"errors": errors, "files": files} diff --git a/salt/modules/freebsdports.py b/salt/modules/freebsdports.py index c5953b4a21f..d04e899bfe7 100644 --- a/salt/modules/freebsdports.py +++ b/salt/modules/freebsdports.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Install software from the FreeBSD ``ports(7)`` system .. versionadded:: 2014.1.0 @@ -13,77 +13,80 @@ this module exclusively from the command line. salt minion-id ports.config security/nmap IPV6=off salt minion-id ports.install security/nmap -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import fnmatch +import logging import os import re -import logging # Import salt libs import salt.utils.data import salt.utils.files import salt.utils.path -from salt.ext.six import string_types -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six +from salt.ext.six import string_types log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'ports' +__virtualname__ = "ports" def __virtual__(): - ''' + """ Only runs on FreeBSD systems - ''' - if __grains__['os'] == 'FreeBSD': + """ + if __grains__["os"] == "FreeBSD": return __virtualname__ - return (False, 'The freebsdports execution module cannot be loaded: ' - 'only available on FreeBSD systems.') + return ( + False, + "The freebsdports execution module cannot be loaded: " + "only available on FreeBSD systems.", + ) def _portsnap(): - ''' + """ Return 'portsnap --interactive' for FreeBSD 10, otherwise 'portsnap' - ''' - ret = ['portsnap'] - if float(__grains__['osrelease']) >= 10: - ret.append('--interactive') + """ + ret = ["portsnap"] + if float(__grains__["osrelease"]) >= 10: + ret.append("--interactive") return ret def _check_portname(name): - ''' + """ Check if portname is valid and whether or not the directory exists in the ports tree. - ''' - if not isinstance(name, string_types) or '/' not in name: + """ + if not isinstance(name, string_types) or "/" not in name: raise SaltInvocationError( - 'Invalid port name \'{0}\' (category required)'.format(name) + "Invalid port name '{0}' (category required)".format(name) ) - path = os.path.join('/usr/ports', name) + path = os.path.join("/usr/ports", name) if not os.path.isdir(path): - raise SaltInvocationError('Path \'{0}\' does not exist'.format(path)) + raise SaltInvocationError("Path '{0}' does not exist".format(path)) return path def _options_dir(name): - ''' + """ Retrieve the path to the dir containing OPTIONS file for a given port - ''' + """ _check_portname(name) - _root = '/var/db/ports' + _root = "/var/db/ports" # New path: /var/db/ports/category_portname - new_dir = os.path.join(_root, name.replace('/', '_')) + new_dir = os.path.join(_root, name.replace("/", "_")) # Old path: /var/db/ports/portname - old_dir = os.path.join(_root, name.split('/')[-1]) + old_dir = os.path.join(_root, name.split("/")[-1]) if os.path.isdir(old_dir): return old_dir @@ -91,17 +94,17 @@ def _options_dir(name): def _options_file_exists(name): - ''' + """ Returns True/False based on whether or not the options file for the specified port exists. - ''' - return os.path.isfile(os.path.join(_options_dir(name), 'options')) + """ + return os.path.isfile(os.path.join(_options_dir(name), "options")) def _write_options(name, configuration): - ''' + """ Writes a new OPTIONS file - ''' + """ _check_portname(name) pkg = next(iter(configuration)) @@ -112,42 +115,42 @@ def _write_options(name, configuration): try: os.makedirs(dirname) except OSError as exc: - raise CommandExecutionError( - 'Unable to make {0}: {1}'.format(dirname, exc) - ) + raise CommandExecutionError("Unable to make {0}: {1}".format(dirname, exc)) - with salt.utils.files.fopen(os.path.join(dirname, 'options'), 'w') as fp_: + with salt.utils.files.fopen(os.path.join(dirname, "options"), "w") as fp_: sorted_options = list(conf_ptr) sorted_options.sort() - fp_.write(salt.utils.stringutils.to_str( - '# This file was auto-generated by Salt (http://saltstack.com)\n' - '# Options for {0}\n' - '_OPTIONS_READ={0}\n' - '_FILE_COMPLETE_OPTIONS_LIST={1}\n' - .format(pkg, ' '.join(sorted_options)) - )) - opt_tmpl = 'OPTIONS_FILE_{0}SET+={1}\n' - for opt in sorted_options: - fp_.write(salt.utils.stringutils.to_str( - opt_tmpl.format( - '' if conf_ptr[opt] == 'on' else 'UN', - opt + fp_.write( + salt.utils.stringutils.to_str( + "# This file was auto-generated by Salt (http://saltstack.com)\n" + "# Options for {0}\n" + "_OPTIONS_READ={0}\n" + "_FILE_COMPLETE_OPTIONS_LIST={1}\n".format( + pkg, " ".join(sorted_options) ) - )) + ) + ) + opt_tmpl = "OPTIONS_FILE_{0}SET+={1}\n" + for opt in sorted_options: + fp_.write( + salt.utils.stringutils.to_str( + opt_tmpl.format("" if conf_ptr[opt] == "on" else "UN", opt) + ) + ) def _normalize(val): - ''' + """ Fix Salt's yaml-ification of on/off, and otherwise normalize the on/off values to be used in writing the options file - ''' + """ if isinstance(val, bool): - return 'on' if val else 'off' + return "on" if val else "off" return six.text_type(val).lower() def install(name, clean=True): - ''' + """ Install a port from the ports tree. Installs using ``BATCH=yes`` for non-interactive building. To set config options for a given port, use :mod:`ports.config `. @@ -167,38 +170,34 @@ def install(name, clean=True): .. code-block:: bash salt -t 1200 '*' ports.install security/nmap - ''' + """ portpath = _check_portname(name) - old = __salt__['pkg.list_pkgs']() - if old.get(name.rsplit('/')[-1]): + old = __salt__["pkg.list_pkgs"]() + if old.get(name.rsplit("/")[-1]): deinstall(name) - cmd = ['make', 'install'] + cmd = ["make", "install"] if clean: - cmd.append('clean') - cmd.append('BATCH=yes') - result = __salt__['cmd.run_all']( - cmd, - cwd=portpath, - reset_system_locale=False, - python_shell=False + cmd.append("clean") + cmd.append("BATCH=yes") + result = __salt__["cmd.run_all"]( + cmd, cwd=portpath, reset_system_locale=False, python_shell=False ) - if result['retcode'] != 0: - __context__['ports.install_error'] = result['stderr'] - __context__.pop('pkg.list_pkgs', None) - new = __salt__['pkg.list_pkgs']() + if result["retcode"] != 0: + __context__["ports.install_error"] = result["stderr"] + __context__.pop("pkg.list_pkgs", None) + new = __salt__["pkg.list_pkgs"]() ret = salt.utils.data.compare_dicts(old, new) - if not ret and result['retcode'] == 0: + if not ret and result["retcode"] == 0: # No change in package list, but the make install was successful. # Assume that the installation was a recompile with new options, and # set return dict so that changes are detected by the ports.installed # state. - ret = {name: {'old': old.get(name, ''), - 'new': new.get(name, '')}} + ret = {name: {"old": old.get(name, ""), "new": new.get(name, "")}} return ret def deinstall(name): - ''' + """ De-install a port. CLI Example: @@ -206,21 +205,19 @@ def deinstall(name): .. code-block:: bash salt '*' ports.deinstall security/nmap - ''' + """ portpath = _check_portname(name) - old = __salt__['pkg.list_pkgs']() - result = __salt__['cmd.run_all']( - ['make', 'deinstall', 'BATCH=yes'], - cwd=portpath, - python_shell=False + old = __salt__["pkg.list_pkgs"]() + result = __salt__["cmd.run_all"]( + ["make", "deinstall", "BATCH=yes"], cwd=portpath, python_shell=False ) - __context__.pop('pkg.list_pkgs', None) - new = __salt__['pkg.list_pkgs']() + __context__.pop("pkg.list_pkgs", None) + new = __salt__["pkg.list_pkgs"]() return salt.utils.data.compare_dicts(old, new) def rmconfig(name): - ''' + """ Clear the cached options for the specified port; run a ``make rmconfig`` name @@ -231,17 +228,13 @@ def rmconfig(name): .. code-block:: bash salt '*' ports.rmconfig security/nmap - ''' + """ portpath = _check_portname(name) - return __salt__['cmd.run']( - ['make', 'rmconfig'], - cwd=portpath, - python_shell=False - ) + return __salt__["cmd.run"](["make", "rmconfig"], cwd=portpath, python_shell=False) def showconfig(name, default=False, dict_return=False): - ''' + """ Show the configuration options for a given port. default : False @@ -258,60 +251,52 @@ def showconfig(name, default=False, dict_return=False): salt '*' ports.showconfig security/nmap salt '*' ports.showconfig security/nmap default=True - ''' + """ portpath = _check_portname(name) if default and _options_file_exists(name): saved_config = showconfig(name, default=False, dict_return=True) rmconfig(name) if _options_file_exists(name): - raise CommandExecutionError('Unable to get default configuration') - default_config = showconfig(name, default=False, - dict_return=dict_return) + raise CommandExecutionError("Unable to get default configuration") + default_config = showconfig(name, default=False, dict_return=dict_return) _write_options(name, saved_config) return default_config try: - result = __salt__['cmd.run_all']( - ['make', 'showconfig'], - cwd=portpath, - python_shell=False + result = __salt__["cmd.run_all"]( + ["make", "showconfig"], cwd=portpath, python_shell=False ) - output = result['stdout'].splitlines() - if result['retcode'] != 0: - error = result['stderr'] + output = result["stdout"].splitlines() + if result["retcode"] != 0: + error = result["stderr"] else: - error = '' + error = "" except TypeError: error = result if error: - msg = ('Error running \'make showconfig\' for {0}: {1}' - .format(name, error)) + msg = "Error running 'make showconfig' for {0}: {1}".format(name, error) log.error(msg) raise SaltInvocationError(msg) if not dict_return: - return '\n'.join(output) + return "\n".join(output) - if (not output) or ('configuration options' not in output[0]): + if (not output) or ("configuration options" not in output[0]): return {} try: - pkg = output[0].split()[-1].rstrip(':') + pkg = output[0].split()[-1].rstrip(":") except (IndexError, AttributeError, TypeError) as exc: - log.error( - 'Unable to get pkg-version string: {0}'.format(exc) - ) + log.error("Unable to get pkg-version string: {0}".format(exc)) return {} ret = {pkg: {}} output = output[1:] for line in output: try: - opt, val, desc = re.match( - r'\s+([^=]+)=(off|on): (.+)', line - ).groups() + opt, val, desc = re.match(r"\s+([^=]+)=(off|on): (.+)", line).groups() except AttributeError: continue ret[pkg][opt] = val @@ -322,7 +307,7 @@ def showconfig(name, default=False, dict_return=False): def config(name, reset=False, **kwargs): - ''' + """ Modify configuration options for a given port. Multiple options can be specified. To see the available options for a port, use :mod:`ports.showconfig `. @@ -339,7 +324,7 @@ def config(name, reset=False, **kwargs): .. code-block:: bash salt '*' ports.config security/nmap IPV6=off - ''' + """ portpath = _check_portname(name) if reset: @@ -349,7 +334,7 @@ def config(name, reset=False, **kwargs): if not configuration: raise CommandExecutionError( - 'Unable to get port configuration for \'{0}\''.format(name) + "Unable to get port configuration for '{0}'".format(name) ) # Get top-level key for later reference @@ -359,24 +344,23 @@ def config(name, reset=False, **kwargs): opts = dict( (six.text_type(x), _normalize(kwargs[x])) for x in kwargs - if not x.startswith('_') + if not x.startswith("_") ) bad_opts = [x for x in opts if x not in conf_ptr] if bad_opts: raise SaltInvocationError( - 'The following opts are not valid for port {0}: {1}' - .format(name, ', '.join(bad_opts)) + "The following opts are not valid for port {0}: {1}".format( + name, ", ".join(bad_opts) + ) ) bad_vals = [ - '{0}={1}'.format(x, y) for x, y in six.iteritems(opts) - if y not in ('on', 'off') + "{0}={1}".format(x, y) for x, y in six.iteritems(opts) if y not in ("on", "off") ] if bad_vals: raise SaltInvocationError( - 'The following key/value pairs are invalid: {0}' - .format(', '.join(bad_vals)) + "The following key/value pairs are invalid: {0}".format(", ".join(bad_vals)) ) conf_ptr.update(opts) @@ -392,7 +376,7 @@ def config(name, reset=False, **kwargs): def update(extract=False): - ''' + """ Update the ports tree extract : False @@ -404,59 +388,48 @@ def update(extract=False): .. code-block:: bash salt '*' ports.update - ''' - result = __salt__['cmd.run_all']( - _portsnap() + ['fetch'], - python_shell=False - ) - if not result['retcode'] == 0: + """ + result = __salt__["cmd.run_all"](_portsnap() + ["fetch"], python_shell=False) + if not result["retcode"] == 0: raise CommandExecutionError( - 'Unable to fetch ports snapshot: {0}'.format(result['stderr']) + "Unable to fetch ports snapshot: {0}".format(result["stderr"]) ) ret = [] try: - patch_count = re.search( - r'Fetching (\d+) patches', result['stdout'] - ).group(1) + patch_count = re.search(r"Fetching (\d+) patches", result["stdout"]).group(1) except AttributeError: patch_count = 0 try: new_port_count = re.search( - r'Fetching (\d+) new ports or files', result['stdout'] + r"Fetching (\d+) new ports or files", result["stdout"] ).group(1) except AttributeError: new_port_count = 0 - ret.append('Applied {0} new patches'.format(patch_count)) - ret.append('Fetched {0} new ports or files'.format(new_port_count)) + ret.append("Applied {0} new patches".format(patch_count)) + ret.append("Fetched {0} new ports or files".format(new_port_count)) if extract: - result = __salt__['cmd.run_all']( - _portsnap() + ['extract'], - python_shell=False - ) - if not result['retcode'] == 0: + result = __salt__["cmd.run_all"](_portsnap() + ["extract"], python_shell=False) + if not result["retcode"] == 0: raise CommandExecutionError( - 'Unable to extract ports snapshot {0}'.format(result['stderr']) + "Unable to extract ports snapshot {0}".format(result["stderr"]) ) - result = __salt__['cmd.run_all']( - _portsnap() + ['update'], - python_shell=False - ) - if not result['retcode'] == 0: + result = __salt__["cmd.run_all"](_portsnap() + ["update"], python_shell=False) + if not result["retcode"] == 0: raise CommandExecutionError( - 'Unable to apply ports snapshot: {0}'.format(result['stderr']) + "Unable to apply ports snapshot: {0}".format(result["stderr"]) ) - __context__.pop('ports.list_all', None) - return '\n'.join(ret) + __context__.pop("ports.list_all", None) + return "\n".join(ret) def list_all(): - ''' + """ Lists all ports available. CLI Example: @@ -468,19 +441,19 @@ def list_all(): .. warning:: Takes a while to run, and returns a **LOT** of output - ''' - if 'ports.list_all' not in __context__: - __context__['ports.list_all'] = [] - for path, dirs, files in salt.utils.path.os_walk('/usr/ports'): - stripped = path[len('/usr/ports'):] - if stripped.count('/') != 2 or stripped.endswith('/CVS'): + """ + if "ports.list_all" not in __context__: + __context__["ports.list_all"] = [] + for path, dirs, files in salt.utils.path.os_walk("/usr/ports"): + stripped = path[len("/usr/ports") :] + if stripped.count("/") != 2 or stripped.endswith("/CVS"): continue - __context__['ports.list_all'].append(stripped[1:]) - return __context__['ports.list_all'] + __context__["ports.list_all"].append(stripped[1:]) + return __context__["ports.list_all"] def search(name): - ''' + """ Search for matches in the ports tree. Globs are supported, and the category is optional @@ -495,20 +468,20 @@ def search(name): .. warning:: Takes a while to run - ''' + """ name = six.text_type(name) all_ports = list_all() - if '/' in name: - if name.count('/') > 1: + if "/" in name: + if name.count("/") > 1: raise SaltInvocationError( - 'Invalid search string \'{0}\'. Port names cannot have more ' - 'than one slash' + "Invalid search string '{0}'. Port names cannot have more " + "than one slash" ) else: return fnmatch.filter(all_ports, name) else: ret = [] for port in all_ports: - if fnmatch.fnmatch(port.rsplit('/')[-1], name): + if fnmatch.fnmatch(port.rsplit("/")[-1], name): ret.append(port) return ret diff --git a/salt/modules/freebsdservice.py b/salt/modules/freebsdservice.py index bc4b95d5fea..9ccc7310f62 100644 --- a/salt/modules/freebsdservice.py +++ b/salt/modules/freebsdservice.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The service module for FreeBSD .. important:: @@ -7,123 +7,126 @@ The service module for FreeBSD minion, and it is using a different module (or gives an error similar to *'service.start' is not available*), see :ref:`here `. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import fnmatch # Import python libs import logging import os -import fnmatch import re +import salt.utils.decorators as decorators +import salt.utils.files + # Import salt libs import salt.utils.path -import salt.utils.decorators as decorators -import salt.utils.files from salt.exceptions import CommandNotFoundError -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" def __virtual__(): - ''' + """ Only work on FreeBSD - ''' + """ # Disable on these platforms, specific service modules exist: - if __grains__['os'] == 'FreeBSD': + if __grains__["os"] == "FreeBSD": return __virtualname__ - return (False, 'The freebsdservice execution module cannot be loaded: only available on FreeBSD systems.') + return ( + False, + "The freebsdservice execution module cannot be loaded: only available on FreeBSD systems.", + ) @decorators.memoize def _cmd(jail=None): - ''' + """ Return full path to service command .. versionchanged:: 2016.3.4 Support for jail (representing jid or jail name) keyword argument in kwargs - ''' - service = salt.utils.path.which('service') + """ + service = salt.utils.path.which("service") if not service: - raise CommandNotFoundError('\'service\' command not found') + raise CommandNotFoundError("'service' command not found") if jail: - jexec = salt.utils.path.which('jexec') + jexec = salt.utils.path.which("jexec") if not jexec: - raise CommandNotFoundError('\'jexec\' command not found') - service = '{0} {1} {2}'.format(jexec, jail, service) + raise CommandNotFoundError("'jexec' command not found") + service = "{0} {1} {2}".format(jexec, jail, service) return service def _get_jail_path(jail): - ''' + """ .. versionadded:: 2016.3.4 Return the jail's root directory (path) as shown in jls jail The jid or jail name - ''' - jls = salt.utils.path.which('jls') + """ + jls = salt.utils.path.which("jls") if not jls: - raise CommandNotFoundError('\'jls\' command not found') - jails = __salt__['cmd.run_stdout']('{0} -n jid name path'.format(jls)) + raise CommandNotFoundError("'jls' command not found") + jails = __salt__["cmd.run_stdout"]("{0} -n jid name path".format(jls)) for j in jails.splitlines(): - jid, jname, path = (x.split('=')[1].strip() for x in j.split()) + jid, jname, path = (x.split("=")[1].strip() for x in j.split()) if jid == jail or jname == jail: - return path.rstrip('/') + return path.rstrip("/") # XΧΧ, TODO, not sure how to handle nonexistent jail - return '' + return "" def _get_rcscript(name, jail=None): - ''' + """ Return full path to service rc script .. versionchanged:: 2016.3.4 Support for jail (representing jid or jail name) keyword argument in kwargs - ''' - cmd = '{0} -r'.format(_cmd(jail)) - prf = _get_jail_path(jail) if jail else '' - for line in __salt__['cmd.run_stdout'](cmd, python_shell=False).splitlines(): - if line.endswith('{0}{1}'.format(os.path.sep, name)): + """ + cmd = "{0} -r".format(_cmd(jail)) + prf = _get_jail_path(jail) if jail else "" + for line in __salt__["cmd.run_stdout"](cmd, python_shell=False).splitlines(): + if line.endswith("{0}{1}".format(os.path.sep, name)): return os.path.join(prf, line.lstrip(os.path.sep)) return None def _get_rcvar(name, jail=None): - ''' + """ Return rcvar .. versionchanged:: 2016.3.4 Support for jail (representing jid or jail name) keyword argument in kwargs - ''' + """ if not available(name, jail): - log.error('Service %s not found', name) + log.error("Service %s not found", name) return False - cmd = '{0} {1} rcvar'.format(_cmd(jail), name) + cmd = "{0} {1} rcvar".format(_cmd(jail), name) - for line in __salt__['cmd.run_stdout'](cmd, python_shell=False).splitlines(): + for line in __salt__["cmd.run_stdout"](cmd, python_shell=False).splitlines(): if '_enable="' not in line: continue - rcvar, _ = line.split('=', 1) + rcvar, _ = line.split("=", 1) return rcvar return None def get_enabled(jail=None): - ''' + """ Return what services are set to run on boot .. versionchanged:: 2016.3.4 @@ -135,18 +138,18 @@ def get_enabled(jail=None): .. code-block:: bash salt '*' service.get_enabled - ''' + """ ret = [] service = _cmd(jail) - prf = _get_jail_path(jail) if jail else '' - for svc in __salt__['cmd.run']('{0} -e'.format(service)).splitlines(): + prf = _get_jail_path(jail) if jail else "" + for svc in __salt__["cmd.run"]("{0} -e".format(service)).splitlines(): ret.append(os.path.basename(svc)) # This is workaround for bin/173454 bug for svc in get_all(jail): if svc in ret: continue - if not os.path.exists('{0}/etc/rc.conf.d/{1}'.format(prf, svc)): + if not os.path.exists("{0}/etc/rc.conf.d/{1}".format(prf, svc)): continue if enabled(svc, jail=jail): ret.append(svc) @@ -155,7 +158,7 @@ def get_enabled(jail=None): def get_disabled(jail=None): - ''' + """ Return what services are available but not enabled to start at boot .. versionchanged:: 2016.3.4 @@ -167,16 +170,14 @@ def get_disabled(jail=None): .. code-block:: bash salt '*' service.get_disabled - ''' + """ en_ = get_enabled(jail) all_ = get_all(jail) return sorted(set(all_) - set(en_)) -def _switch(name, # pylint: disable=C0103 - on, # pylint: disable=C0103 - **kwargs): - ''' +def _switch(name, on, **kwargs): # pylint: disable=C0103 # pylint: disable=C0103 + """ Switch on/off service start at boot. .. versionchanged:: 2016.3.4 @@ -184,15 +185,15 @@ def _switch(name, # pylint: disable=C0103 Support for jail (representing jid or jail name) and chroot keyword argument in kwargs. chroot should be used when jail's /etc is mounted read-only and should point to a root directory where jail's /etc is mounted read-write. - ''' - jail = kwargs.get('jail', '') - chroot = kwargs.get('chroot', '').rstrip('/') + """ + jail = kwargs.get("jail", "") + chroot = kwargs.get("chroot", "").rstrip("/") if not available(name, jail): return False rcvar = _get_rcvar(name, jail) if not rcvar: - log.error('rcvar for service %s not found', name) + log.error("rcvar for service %s not found", name) return False if jail and not chroot: @@ -201,44 +202,45 @@ def _switch(name, # pylint: disable=C0103 # is mounted read-only chroot = _get_jail_path(jail) - config = kwargs.get('config', - __salt__['config.option']('service.config', - default='{0}/etc/rc.conf'.format(chroot) - ) - ) + config = kwargs.get( + "config", + __salt__["config.option"]( + "service.config", default="{0}/etc/rc.conf".format(chroot) + ), + ) if not config: - rcdir = '{0}/etc/rc.conf.d'.format(chroot) + rcdir = "{0}/etc/rc.conf.d".format(chroot) if not os.path.exists(rcdir) or not os.path.isdir(rcdir): - log.error('%s not exists', rcdir) + log.error("%s not exists", rcdir) return False - config = os.path.join(rcdir, rcvar.replace('_enable', '')) + config = os.path.join(rcdir, rcvar.replace("_enable", "")) nlines = [] edited = False if on: - val = 'YES' + val = "YES" else: - val = 'NO' + val = "NO" if os.path.exists(config): - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) - if not line.startswith('{0}='.format(rcvar)): + if not line.startswith("{0}=".format(rcvar)): nlines.append(line) continue - rest = line[len(line.split()[0]):] # keep comments etc + rest = line[len(line.split()[0]) :] # keep comments etc nlines.append('{0}="{1}"{2}'.format(rcvar, val, rest)) edited = True if not edited: # Ensure that the file ends in a \n - if len(nlines) > 1 and nlines[-1][-1] != '\n': - nlines[-1] = '{0}\n'.format(nlines[-1]) + if len(nlines) > 1 and nlines[-1][-1] != "\n": + nlines[-1] = "{0}\n".format(nlines[-1]) nlines.append('{0}="{1}"\n'.format(rcvar, val)) - with salt.utils.files.fopen(config, 'w') as ofile: + with salt.utils.files.fopen(config, "w") as ofile: nlines = [salt.utils.stringutils.to_str(_l) for _l in nlines] ofile.writelines(nlines) @@ -246,7 +248,7 @@ def _switch(name, # pylint: disable=C0103 def enable(name, **kwargs): - ''' + """ Enable the named service to start at boot name @@ -272,12 +274,12 @@ def enable(name, **kwargs): .. code-block:: bash salt '*' service.enable - ''' + """ return _switch(name, True, **kwargs) def disable(name, **kwargs): - ''' + """ Disable the named service to start at boot Arguments the same as for enable() @@ -295,12 +297,12 @@ def disable(name, **kwargs): .. code-block:: bash salt '*' service.disable - ''' + """ return _switch(name, False, **kwargs) def enabled(name, **kwargs): - ''' + """ Return True if the named service is enabled, false otherwise name @@ -315,26 +317,26 @@ def enabled(name, **kwargs): .. code-block:: bash salt '*' service.enabled - ''' - jail = kwargs.get('jail', '') + """ + jail = kwargs.get("jail", "") if not available(name, jail): - log.error('Service %s not found', name) + log.error("Service %s not found", name) return False - cmd = '{0} {1} rcvar'.format(_cmd(jail), name) + cmd = "{0} {1} rcvar".format(_cmd(jail), name) - for line in __salt__['cmd.run_stdout'](cmd, python_shell=False).splitlines(): + for line in __salt__["cmd.run_stdout"](cmd, python_shell=False).splitlines(): if '_enable="' not in line: continue _, state, _ = line.split('"', 2) - return state.lower() in ('yes', 'true', 'on', '1') + return state.lower() in ("yes", "true", "on", "1") # probably will never reached return False def disabled(name, **kwargs): - ''' + """ Return True if the named service is enabled, false otherwise CLI Example: @@ -342,12 +344,12 @@ def disabled(name, **kwargs): .. code-block:: bash salt '*' service.disabled - ''' + """ return not enabled(name, **kwargs) def available(name, jail=None): - ''' + """ Check that the given service is available. .. versionchanged:: 2016.3.4 @@ -359,12 +361,12 @@ def available(name, jail=None): .. code-block:: bash salt '*' service.available sshd - ''' + """ return name in get_all(jail) def missing(name, jail=None): - ''' + """ The inverse of service.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -378,12 +380,12 @@ def missing(name, jail=None): .. code-block:: bash salt '*' service.missing sshd - ''' + """ return name not in get_all(jail) def get_all(jail=None): - ''' + """ Return a list of all available services .. versionchanged:: 2016.3.4 @@ -395,17 +397,17 @@ def get_all(jail=None): .. code-block:: bash salt '*' service.get_all - ''' + """ ret = [] service = _cmd(jail) - for srv in __salt__['cmd.run']('{0} -l'.format(service)).splitlines(): + for srv in __salt__["cmd.run"]("{0} -l".format(service)).splitlines(): if not srv.isupper(): ret.append(srv) return sorted(ret) def start(name, jail=None): - ''' + """ Start the specified service .. versionchanged:: 2016.3.4 @@ -417,13 +419,13 @@ def start(name, jail=None): .. code-block:: bash salt '*' service.start - ''' - cmd = '{0} {1} onestart'.format(_cmd(jail), name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "{0} {1} onestart".format(_cmd(jail), name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def stop(name, jail=None): - ''' + """ Stop the specified service .. versionchanged:: 2016.3.4 @@ -435,13 +437,13 @@ def stop(name, jail=None): .. code-block:: bash salt '*' service.stop - ''' - cmd = '{0} {1} onestop'.format(_cmd(jail), name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "{0} {1} onestop".format(_cmd(jail), name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def restart(name, jail=None): - ''' + """ Restart the named service .. versionchanged:: 2016.3.4 @@ -453,13 +455,13 @@ def restart(name, jail=None): .. code-block:: bash salt '*' service.restart - ''' - cmd = '{0} {1} onerestart'.format(_cmd(jail), name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "{0} {1} onerestart".format(_cmd(jail), name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def reload_(name, jail=None): - ''' + """ Restart the named service .. versionchanged:: 2016.3.4 @@ -471,13 +473,13 @@ def reload_(name, jail=None): .. code-block:: bash salt '*' service.reload - ''' - cmd = '{0} {1} onereload'.format(_cmd(jail), name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "{0} {1} onereload".format(_cmd(jail), name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def status(name, sig=None, jail=None): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -500,21 +502,21 @@ def status(name, sig=None, jail=None): .. code-block:: bash salt '*' service.status [service signature] - ''' + """ if sig: - return bool(__salt__['status.pid'](sig)) + return bool(__salt__["status.pid"](sig)) - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: services = [name] results = {} for service in services: - cmd = '{0} {1} onestatus'.format(_cmd(jail), service) - results[service] = not __salt__['cmd.retcode'](cmd, - python_shell=False, - ignore_retcode=True) + cmd = "{0} {1} onestatus".format(_cmd(jail), service) + results[service] = not __salt__["cmd.retcode"]( + cmd, python_shell=False, ignore_retcode=True + ) if contains_globbing: return results return results[name] diff --git a/salt/modules/freezer.py b/salt/modules/freezer.py index 2bfeacdcb91..c69f782179f 100644 --- a/salt/modules/freezer.py +++ b/salt/modules/freezer.py @@ -1,62 +1,63 @@ # -*- coding: utf-8 -*- -''' +""" :maintainer: Alberto Planas :maturity: new :depends: None :platform: Linux -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging import os +import salt.utils.json as json from salt.exceptions import CommandExecutionError +from salt.ext.six.moves import zip from salt.utils.args import clean_kwargs from salt.utils.files import fopen -import salt.utils.json as json -from salt.ext.six.moves import zip log = logging.getLogger(__name__) __func_alias__ = { - 'list_': 'list', + "list_": "list", } def __virtual__(): - ''' + """ Freezer is based on top of the pkg module. Return True since the `pkg` module should always exist. This avoids the overhead of loading all modules. - ''' + """ return True def _states_path(): - ''' + """ Return the path where we will store the states. - ''' - return os.path.join(__opts__['cachedir'], 'freezer') + """ + return os.path.join(__opts__["cachedir"], "freezer") def _paths(name=None): - ''' + """ Return the full path for the packages and repository freezer files. - ''' - name = 'freezer' if not name else name + """ + name = "freezer" if not name else name states_path = _states_path() return ( - os.path.join(states_path, '{}-pkgs.yml'.format(name)), - os.path.join(states_path, '{}-reps.yml'.format(name)), + os.path.join(states_path, "{}-pkgs.yml".format(name)), + os.path.join(states_path, "{}-reps.yml".format(name)), ) def status(name=None): - ''' + """ Return True if there is already a frozen state. A frozen state is merely a list of packages (including the @@ -74,13 +75,13 @@ def status(name=None): salt '*' freezer.status salt '*' freezer.status pre_install - ''' - name = 'freezer' if not name else name + """ + name = "freezer" if not name else name return all(os.path.isfile(i) for i in _paths(name)) def list_(): - ''' + """ Return the list of frozen states. CLI Example: @@ -89,14 +90,14 @@ def list_(): salt '*' freezer.list - ''' + """ ret = [] states_path = _states_path() if not os.path.isdir(states_path): return ret for state in os.listdir(states_path): - if state.endswith(('-pkgs.yml', '-reps.yml')): + if state.endswith(("-pkgs.yml", "-reps.yml")): # Remove the suffix - both files start with the freezer # name ret.append(state[:-9]) @@ -104,7 +105,7 @@ def list_(): def freeze(name=None, force=False, **kwargs): - ''' + """ Save the list of package and repos in a freeze file. As this module is build on top of the pkg module, the user can @@ -127,32 +128,33 @@ def freeze(name=None, force=False, **kwargs): salt '*' freezer.freeze pre_install salt '*' freezer.freeze force=True root=/chroot - ''' + """ states_path = _states_path() try: if not os.path.exists(states_path): os.makedirs(states_path) except OSError as e: - msg = 'Error when trying to create the freezer storage %s: %s' + msg = "Error when trying to create the freezer storage %s: %s" log.error(msg, states_path, e) raise CommandExecutionError(msg % (states_path, e)) if status(name) and not force: - raise CommandExecutionError('The state is already present. Use ' - 'force parameter to overwrite.') + raise CommandExecutionError( + "The state is already present. Use " "force parameter to overwrite." + ) safe_kwargs = clean_kwargs(**kwargs) - pkgs = __salt__['pkg.list_pkgs'](**safe_kwargs) - repos = __salt__['pkg.list_repos'](**safe_kwargs) + pkgs = __salt__["pkg.list_pkgs"](**safe_kwargs) + repos = __salt__["pkg.list_repos"](**safe_kwargs) for fname, content in zip(_paths(name), (pkgs, repos)): - with fopen(fname, 'w') as fp: + with fopen(fname, "w") as fp: json.dump(content, fp) return True def _add_missing_repositories(frozen_repos, ret, **kwargs): - '''Add missing repositories and update the ret dict''' - repos = __salt__['pkg.list_repos'](**kwargs) + """Add missing repositories and update the ret dict""" + repos = __salt__["pkg.list_repos"](**kwargs) missing_repos = set(frozen_repos) - set(repos) for repo in missing_repos: try: @@ -161,65 +163,65 @@ def _add_missing_repositories(frozen_repos, ret, **kwargs): # all the parameters _tmp_kwargs = frozen_repos[repo].copy() _tmp_kwargs.update(kwargs) - __salt__['pkg.mod_repo'](repo, **_tmp_kwargs) - ret['repos']['add'].append(repo) - log.info('Added missing repository %s', repo) + __salt__["pkg.mod_repo"](repo, **_tmp_kwargs) + ret["repos"]["add"].append(repo) + log.info("Added missing repository %s", repo) except Exception as e: # pylint: disable=broad-except - msg = 'Error adding %s repository: %s' + msg = "Error adding %s repository: %s" log.error(msg, repo, e) - ret['comment'].append(msg % (repo, e)) + ret["comment"].append(msg % (repo, e)) def _remove_extra_repositories(frozen_repos, ret, **kwargs): - '''Remove extra repositories and update the ret dict''' - repos = __salt__['pkg.list_repos'](**kwargs) + """Remove extra repositories and update the ret dict""" + repos = __salt__["pkg.list_repos"](**kwargs) extra_repos = set(repos) - set(frozen_repos) for repo in extra_repos: try: - __salt__['pkg.del_repo'](repo, **kwargs) - ret['repos']['remove'].append(repo) - log.info('Removed extra repository %s', repo) + __salt__["pkg.del_repo"](repo, **kwargs) + ret["repos"]["remove"].append(repo) + log.info("Removed extra repository %s", repo) except Exception as e: # pylint: disable=broad-except - msg = 'Error removing %s repository: %s' + msg = "Error removing %s repository: %s" log.error(msg, repo, e) - ret['comment'].append(msg % (repo, e)) + ret["comment"].append(msg % (repo, e)) def _add_missing_packages(frozen_pkgs, ret, **kwargs): - '''Add missing packages and update the ret dict''' + """Add missing packages and update the ret dict""" # NOTE: we can remove the `for` using `pkgs`. This will improve # performance, but I want to have a more detalied report of what # packages are installed or failed. - pkgs = __salt__['pkg.list_pkgs'](**kwargs) + pkgs = __salt__["pkg.list_pkgs"](**kwargs) missing_pkgs = set(frozen_pkgs) - set(pkgs) for pkg in missing_pkgs: try: - __salt__['pkg.install'](name=pkg, **kwargs) - ret['pkgs']['add'].append(pkg) - log.info('Added missing package %s', pkg) + __salt__["pkg.install"](name=pkg, **kwargs) + ret["pkgs"]["add"].append(pkg) + log.info("Added missing package %s", pkg) except Exception as e: # pylint: disable=broad-except - msg = 'Error adding %s package: %s' + msg = "Error adding %s package: %s" log.error(msg, pkg, e) - ret['comment'].append(msg % (pkg, e)) + ret["comment"].append(msg % (pkg, e)) def _remove_extra_packages(frozen_pkgs, ret, **kwargs): - '''Remove extra packages and update the ret dict''' - pkgs = __salt__['pkg.list_pkgs'](**kwargs) + """Remove extra packages and update the ret dict""" + pkgs = __salt__["pkg.list_pkgs"](**kwargs) extra_pkgs = set(pkgs) - set(frozen_pkgs) for pkg in extra_pkgs: try: - __salt__['pkg.remove'](name=pkg, **kwargs) - ret['pkgs']['remove'].append(pkg) - log.info('Removed extra package %s', pkg) + __salt__["pkg.remove"](name=pkg, **kwargs) + ret["pkgs"]["remove"].append(pkg) + log.info("Removed extra package %s", pkg) except Exception as e: # pylint: disable=broad-except - msg = 'Error removing %s package: %s' + msg = "Error removing %s package: %s" log.error(msg, pkg, e) - ret['comment'].append(msg % (pkg, e)) + ret["comment"].append(msg % (pkg, e)) def restore(name=None, clean=False, **kwargs): - ''' + """ Make sure that the system contains the packages and repos from a frozen state. @@ -251,9 +253,9 @@ def restore(name=None, clean=False, **kwargs): salt '*' freezer.restore salt '*' freezer.restore root=/chroot - ''' + """ if not status(name): - raise CommandExecutionError('Frozen state not found.') + raise CommandExecutionError("Frozen state not found.") frozen_pkgs = {} frozen_repos = {} @@ -280,9 +282,9 @@ def restore(name=None, clean=False, **kwargs): # recovery will be partial. ret = { - 'pkgs': {'add': [], 'remove': []}, - 'repos': {'add': [], 'remove': []}, - 'comment': [], + "pkgs": {"add": [], "remove": []}, + "repos": {"add": [], "remove": []}, + "comment": [], } _add_missing_repositories(frozen_repos, ret, **safe_kwargs) @@ -291,7 +293,7 @@ def restore(name=None, clean=False, **kwargs): _remove_extra_repositories(frozen_repos, ret, **safe_kwargs) # Clean the cached YAML files - if clean and not ret['comment']: + if clean and not ret["comment"]: for fname in _paths(name): os.remove(fname) diff --git a/salt/modules/gcp_addon.py b/salt/modules/gcp_addon.py index 6a587de4b50..c5757b5889a 100644 --- a/salt/modules/gcp_addon.py +++ b/salt/modules/gcp_addon.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A route is a rule that specifies how certain packets should be handled by the virtual network. Routes are associated with virtual machine instances by tag, and the set of routes for a particular VM is called its routing table. @@ -11,15 +11,16 @@ routing table for a single best matching route. This module will create a route to send traffic destined to the Internet through your gateway instance. -:codeauthor: :email:`Pratik Bandarkar ` +:codeauthor: `Pratik Bandarkar ` :maturity: new :depends: google-api-python-client :platform: Linux -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -30,50 +31,56 @@ log = logging.getLogger(__name__) try: import googleapiclient.discovery import oauth2client.service_account + HAS_LIB = True except ImportError: HAS_LIB = False -__virtualname__ = 'gcp' +__virtualname__ = "gcp" def __virtual__(): - ''' + """ Check for googleapiclient api - ''' + """ if HAS_LIB is False: - return False, 'Required dependencies \'googleapiclient\' and/or \'oauth2client\' were not found.' + return ( + False, + "Required dependencies 'googleapiclient' and/or 'oauth2client' were not found.", + ) return __virtualname__ def _get_network(project_id, network_name, service): - ''' + """ Fetch network selfLink from network name. - ''' - return service.networks().get(project=project_id, - network=network_name).execute() + """ + return service.networks().get(project=project_id, network=network_name).execute() def _get_instance(project_id, instance_zone, name, service): - ''' + """ Get instance details - ''' - return service.instances().get(project=project_id, - zone=instance_zone, - instance=name).execute() + """ + return ( + service.instances() + .get(project=project_id, zone=instance_zone, instance=name) + .execute() + ) -def route_create(credential_file=None, - project_id=None, - name=None, - dest_range=None, - next_hop_instance=None, - instance_zone=None, - tags=None, - network=None, - priority=None - ): - ''' +def route_create( + credential_file=None, + project_id=None, + name=None, + dest_range=None, + next_hop_instance=None, + instance_zone=None, + tags=None, + network=None, + priority=None, +): + """ Create a route to send traffic destined to the Internet through your gateway instance @@ -113,25 +120,25 @@ def route_create(credential_file=None, In above example, the instances which are having tag "no-ip" will route the packet to instance "instance-1"(if packet is intended to other network) - ''' + """ - credentials = oauth2client.service_account.ServiceAccountCredentials.\ - from_json_keyfile_name(credential_file) - service = googleapiclient.discovery.build('compute', 'v1', - credentials=credentials) + credentials = oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name( + credential_file + ) + service = googleapiclient.discovery.build("compute", "v1", credentials=credentials) routes = service.routes() routes_config = { - 'name': six.text_type(name), - 'network': _get_network(project_id, six.text_type(network), - service=service)['selfLink'], - 'destRange': six.text_type(dest_range), - 'nextHopInstance': _get_instance(project_id, instance_zone, - next_hop_instance, - service=service)['selfLink'], - 'tags': tags, - 'priority': priority + "name": six.text_type(name), + "network": _get_network(project_id, six.text_type(network), service=service)[ + "selfLink" + ], + "destRange": six.text_type(dest_range), + "nextHopInstance": _get_instance( + project_id, instance_zone, next_hop_instance, service=service + )["selfLink"], + "tags": tags, + "priority": priority, } - route_create_request = routes.insert(project=project_id, - body=routes_config) + route_create_request = routes.insert(project=project_id, body=routes_config) return route_create_request.execute() diff --git a/salt/modules/gem.py b/salt/modules/gem.py index 5a31e60bff7..4a2186b2db2 100644 --- a/salt/modules/gem.py +++ b/salt/modules/gem.py @@ -1,27 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" Manage ruby gems. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import python libs import re -import logging # Import Salt libs import salt.utils.itertools import salt.utils.platform from salt.exceptions import CommandExecutionError -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} log = logging.getLogger(__name__) # pylint: disable=C0103 def _gem(command, ruby=None, runas=None, gem_bin=None): - ''' + """ Run the actual gem command. If rvm or rbenv is installed, run the command using the corresponding module. rbenv is not available on windows, so don't try. @@ -38,43 +37,44 @@ def _gem(command, ruby=None, runas=None, gem_bin=None): :return: Returns the full standard out including success codes or False if it fails - ''' - cmdline = [gem_bin or 'gem'] + command + """ + cmdline = [gem_bin or "gem"] + command # If a custom gem is given, use that and don't check for rvm/rbenv. User # knows best! if gem_bin is None: - if __salt__['rvm.is_installed'](runas=runas): - return __salt__['rvm.do'](ruby, cmdline, runas=runas) + if __salt__["rvm.is_installed"](runas=runas): + return __salt__["rvm.do"](ruby, cmdline, runas=runas) - if not salt.utils.platform.is_windows() \ - and __salt__['rbenv.is_installed'](runas=runas): + if not salt.utils.platform.is_windows() and __salt__["rbenv.is_installed"]( + runas=runas + ): if ruby is None: - return __salt__['rbenv.do'](cmdline, runas=runas) + return __salt__["rbenv.do"](cmdline, runas=runas) else: - return __salt__['rbenv.do_with_ruby'](ruby, - cmdline, - runas=runas) + return __salt__["rbenv.do_with_ruby"](ruby, cmdline, runas=runas) - ret = __salt__['cmd.run_all'](cmdline, runas=runas, python_shell=False) + ret = __salt__["cmd.run_all"](cmdline, runas=runas, python_shell=False) - if ret['retcode'] == 0: - return ret['stdout'] + if ret["retcode"] == 0: + return ret["stdout"] else: - raise CommandExecutionError(ret['stderr']) + raise CommandExecutionError(ret["stderr"]) -def install(gems, # pylint: disable=C0103 - ruby=None, - gem_bin=None, - runas=None, - version=None, - rdoc=False, - ri=False, - pre_releases=False, - proxy=None, - source=None): # pylint: disable=C0103 - ''' +def install( + gems, # pylint: disable=C0103 + ruby=None, + gem_bin=None, + runas=None, + version=None, + rdoc=False, + ri=False, + pre_releases=False, + proxy=None, + source=None, +): # pylint: disable=C0103 + """ Installs one or several gems. :param gems: string @@ -114,7 +114,7 @@ def install(gems, # pylint: disable=C0103 salt '*' gem.install vagrant salt '*' gem.install redphone gem_bin=/opt/sensu/embedded/bin/gem - ''' + """ try: gems = gems.split() except AttributeError: @@ -122,32 +122,29 @@ def install(gems, # pylint: disable=C0103 options = [] if version: - options.extend(['--version', version]) + options.extend(["--version", version]) if _has_rubygems_3(ruby=ruby, runas=runas, gem_bin=gem_bin): if not rdoc or not ri: - options.append('--no-document') + options.append("--no-document") if pre_releases: - options.append('--prerelease') + options.append("--prerelease") else: if not rdoc: - options.append('--no-rdoc') + options.append("--no-rdoc") if not ri: - options.append('--no-ri') + options.append("--no-ri") if pre_releases: - options.append('--pre') + options.append("--pre") if proxy: - options.extend(['-p', proxy]) + options.extend(["-p", proxy]) if source: - options.extend(['--source', source]) + options.extend(["--source", source]) - return _gem(['install'] + gems + options, - ruby, - gem_bin=gem_bin, - runas=runas) + return _gem(["install"] + gems + options, ruby, gem_bin=gem_bin, runas=runas) def uninstall(gems, ruby=None, runas=None, gem_bin=None): - ''' + """ Uninstall one or several gems. :param gems: string @@ -165,20 +162,17 @@ def uninstall(gems, ruby=None, runas=None, gem_bin=None): .. code-block:: bash salt '*' gem.uninstall vagrant - ''' + """ try: gems = gems.split() except AttributeError: pass - return _gem(['uninstall'] + gems + ['-a', '-x'], - ruby, - gem_bin=gem_bin, - runas=runas) + return _gem(["uninstall"] + gems + ["-a", "-x"], ruby, gem_bin=gem_bin, runas=runas) def update(gems, ruby=None, runas=None, gem_bin=None): - ''' + """ Update one or several gems. :param gems: string @@ -196,20 +190,17 @@ def update(gems, ruby=None, runas=None, gem_bin=None): .. code-block:: bash salt '*' gem.update vagrant - ''' + """ try: gems = gems.split() except AttributeError: pass - return _gem(['update'] + gems, - ruby, - gem_bin=gem_bin, - runas=runas) + return _gem(["update"] + gems, ruby, gem_bin=gem_bin, runas=runas) -def update_system(version='', ruby=None, runas=None, gem_bin=None): - ''' +def update_system(version="", ruby=None, runas=None, gem_bin=None): + """ Update rubygems. :param version: string : (newest) @@ -227,15 +218,12 @@ def update_system(version='', ruby=None, runas=None, gem_bin=None): .. code-block:: bash salt '*' gem.update_system - ''' - return _gem(['update', '--system', version], - ruby, - gem_bin=gem_bin, - runas=runas) + """ + return _gem(["update", "--system", version], ruby, gem_bin=gem_bin, runas=runas) def version(ruby=None, runas=None, gem_bin=None): - ''' + """ Print out the version of gem :param gem_bin: string : None @@ -251,15 +239,12 @@ def version(ruby=None, runas=None, gem_bin=None): .. code-block:: bash salt '*' gem.version - ''' - cmd = ['--version'] - stdout = _gem(cmd, - ruby, - gem_bin=gem_bin, - runas=runas) + """ + cmd = ["--version"] + stdout = _gem(cmd, ruby, gem_bin=gem_bin, runas=runas) ret = {} - for line in salt.utils.itertools.split(stdout, '\n'): - match = re.match(r'[.0-9]+', line) + for line in salt.utils.itertools.split(stdout, "\n"): + match = re.match(r"[.0-9]+", line) if match: ret = line break @@ -267,14 +252,14 @@ def version(ruby=None, runas=None, gem_bin=None): def _has_rubygems_3(ruby=None, runas=None, gem_bin=None): - match = re.match(r'^3\..*', version(ruby=ruby, runas=runas, gem_bin=gem_bin)) + match = re.match(r"^3\..*", version(ruby=ruby, runas=runas, gem_bin=gem_bin)) if match: return True return False -def list_(prefix='', ruby=None, runas=None, gem_bin=None): - ''' +def list_(prefix="", ruby=None, runas=None, gem_bin=None): + """ List locally installed gems. :param prefix: string : @@ -292,28 +277,23 @@ def list_(prefix='', ruby=None, runas=None, gem_bin=None): .. code-block:: bash salt '*' gem.list - ''' - cmd = ['list'] + """ + cmd = ["list"] if prefix: cmd.append(prefix) - stdout = _gem(cmd, - ruby, - gem_bin=gem_bin, - runas=runas) + stdout = _gem(cmd, ruby, gem_bin=gem_bin, runas=runas) ret = {} - for line in salt.utils.itertools.split(stdout, '\n'): - match = re.match(r'^([^ ]+) \((.+)\)', line) + for line in salt.utils.itertools.split(stdout, "\n"): + match = re.match(r"^([^ ]+) \((.+)\)", line) if match: gem = match.group(1) - versions = match.group(2).split(', ') + versions = match.group(2).split(", ") ret[gem] = versions return ret -def list_upgrades(ruby=None, - runas=None, - gem_bin=None): - ''' +def list_upgrades(ruby=None, runas=None, gem_bin=None): + """ .. versionadded:: 2015.8.0 Check if an upgrade is available for installed gems @@ -331,25 +311,22 @@ def list_upgrades(ruby=None, .. code-block:: bash salt '*' gem.list_upgrades - ''' - result = _gem(['outdated'], - ruby, - gem_bin=gem_bin, - runas=runas) + """ + result = _gem(["outdated"], ruby, gem_bin=gem_bin, runas=runas) ret = {} - for line in salt.utils.itertools.split(result, '\n'): - match = re.search(r'(\S+) \(\S+ < (\S+)\)', line) + for line in salt.utils.itertools.split(result, "\n"): + match = re.search(r"(\S+) \(\S+ < (\S+)\)", line) if match: name, version = match.groups() else: - log.error('Can\'t parse line \'%s\'', line) + log.error("Can't parse line '%s'", line) continue ret[name] = version return ret def sources_add(source_uri, ruby=None, runas=None, gem_bin=None): - ''' + """ Add a gem source. :param source_uri: string @@ -367,15 +344,12 @@ def sources_add(source_uri, ruby=None, runas=None, gem_bin=None): .. code-block:: bash salt '*' gem.sources_add http://rubygems.org/ - ''' - return _gem(['sources', '--add', source_uri], - ruby, - gem_bin=gem_bin, - runas=runas) + """ + return _gem(["sources", "--add", source_uri], ruby, gem_bin=gem_bin, runas=runas) def sources_remove(source_uri, ruby=None, runas=None, gem_bin=None): - ''' + """ Remove a gem source. :param source_uri: string @@ -393,15 +367,12 @@ def sources_remove(source_uri, ruby=None, runas=None, gem_bin=None): .. code-block:: bash salt '*' gem.sources_remove http://rubygems.org/ - ''' - return _gem(['sources', '--remove', source_uri], - ruby, - gem_bin=gem_bin, - runas=runas) + """ + return _gem(["sources", "--remove", source_uri], ruby, gem_bin=gem_bin, runas=runas) def sources_list(ruby=None, runas=None, gem_bin=None): - ''' + """ List the configured gem sources. :param gem_bin: string : None @@ -417,6 +388,6 @@ def sources_list(ruby=None, runas=None, gem_bin=None): .. code-block:: bash salt '*' gem.sources_list - ''' - ret = _gem(['sources'], ruby, gem_bin=gem_bin, runas=runas) + """ + ret = _gem(["sources"], ruby, gem_bin=gem_bin, runas=runas) return [] if ret is False else ret.splitlines()[2:] diff --git a/salt/modules/genesis.py b/salt/modules/genesis.py index b65349cbdb9..f375d399047 100644 --- a/salt/modules/genesis.py +++ b/salt/modules/genesis.py @@ -1,20 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing container and VM images .. versionadded:: 2014.7.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import uuid import pprint -import logging -try: - from shlex import quote as _cmd_quote # pylint: disable=E0611 -except ImportError: - from pipes import quote as _cmd_quote +import uuid # Import salt libs import salt.syspaths @@ -29,43 +26,51 @@ from salt.exceptions import SaltInvocationError # Import 3rd-party libs from salt.ext import six +try: + from shlex import quote as _cmd_quote # pylint: disable=E0611 +except ImportError: + from pipes import quote as _cmd_quote + + log = logging.getLogger(__name__) CMD_MAP = { - 'yum': ('yum', 'rpm'), - 'deb': ('debootstrap',), - 'pacman': ('pacman',), + "yum": ("yum", "rpm"), + "deb": ("debootstrap",), + "pacman": ("pacman",), } -EPEL_URL = 'http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm' +EPEL_URL = ( + "http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm" +) def __virtual__(): - ''' + """ By default, this will be available on all platforms; but not all distros will necessarily be supported - ''' + """ return True def bootstrap( - platform, - root, - img_format='dir', - fs_format='ext2', - fs_opts=None, - arch=None, - flavor=None, - repo_url=None, - static_qemu=None, - img_size=None, - mount_dir=None, - pkg_cache=None, - pkgs=None, - exclude_pkgs=None, - epel_url=EPEL_URL, - ): - ''' + platform, + root, + img_format="dir", + fs_format="ext2", + fs_opts=None, + arch=None, + flavor=None, + repo_url=None, + static_qemu=None, + img_size=None, + mount_dir=None, + pkg_cache=None, + pkgs=None, + exclude_pkgs=None, + epel_url=EPEL_URL, +): + """ Create an image for a specific platform. Please note that this function *MUST* be run as root, as images that are @@ -146,34 +151,34 @@ def bootstrap( salt myminion genesis.bootstrap deb /root/wheezy arch=amd64 \ flavor=wheezy static_qemu=/usr/bin/qemu-x86_64-static - ''' - if img_format not in ('dir', 'sparse'): + """ + if img_format not in ("dir", "sparse"): raise SaltInvocationError('The img_format must be "sparse" or "dir"') - if img_format == 'dir': + if img_format == "dir": # We can just use the root as the root - if not __salt__['file.directory_exists'](root): + if not __salt__["file.directory_exists"](root): try: - __salt__['file.mkdir'](root) + __salt__["file.mkdir"](root) except Exception as exc: # pylint: disable=broad-except - return {'Error': salt.utils.stringutils.to_unicode(pprint.pformat(exc))} - elif img_format == 'sparse': + return {"Error": salt.utils.stringutils.to_unicode(pprint.pformat(exc))} + elif img_format == "sparse": if not img_size: - raise SaltInvocationError('An img_size must be specified for a sparse file') + raise SaltInvocationError("An img_size must be specified for a sparse file") if not mount_dir: - mount_dir = '/opt/salt-genesis.{0}'.format(uuid.uuid4()) - __salt__['file.mkdir'](mount_dir, 'root', 'root', '755') - __salt__['cmd.run'](('fallocate', '-l', img_size, root), python_shell=False) + mount_dir = "/opt/salt-genesis.{0}".format(uuid.uuid4()) + __salt__["file.mkdir"](mount_dir, "root", "root", "755") + __salt__["cmd.run"](("fallocate", "-l", img_size, root), python_shell=False) _mkpart(root, fs_format, fs_opts, mount_dir) - loop1 = __salt__['cmd.run']('losetup -f') - log.debug('First loop device is {0}'.format(loop1)) - __salt__['cmd.run']('losetup {0} {1}'.format(loop1, root)) - loop2 = __salt__['cmd.run']('losetup -f') - log.debug('Second loop device is {0}'.format(loop2)) + loop1 = __salt__["cmd.run"]("losetup -f") + log.debug("First loop device is {0}".format(loop1)) + __salt__["cmd.run"]("losetup {0} {1}".format(loop1, root)) + loop2 = __salt__["cmd.run"]("losetup -f") + log.debug("Second loop device is {0}".format(loop2)) start = six.text_type(2048 * 2048) - __salt__['cmd.run']('losetup -o {0} {1} {2}'.format(start, loop2, loop1)) - __salt__['mount.mount'](mount_dir, loop2) + __salt__["cmd.run"]("losetup -o {0} {1} {2}".format(start, loop2, loop1)) + __salt__["mount.mount"](mount_dir, loop2) _populate_cache(platform, pkg_cache, mount_dir) @@ -186,14 +191,11 @@ def bootstrap( if exclude_pkgs is None: exclude_pkgs = [] - if platform in ('rpm', 'yum'): + if platform in ("rpm", "yum"): _bootstrap_yum( - root, - pkgs=pkgs, - exclude_pkgs=exclude_pkgs, - epel_url=epel_url, + root, pkgs=pkgs, exclude_pkgs=exclude_pkgs, epel_url=epel_url, ) - elif platform == 'deb': + elif platform == "deb": _bootstrap_deb( root, arch=arch, @@ -203,106 +205,102 @@ def bootstrap( pkgs=pkgs, exclude_pkgs=exclude_pkgs, ) - elif platform == 'pacman': + elif platform == "pacman": _bootstrap_pacman( - root, - img_format=img_format, - pkgs=pkgs, - exclude_pkgs=exclude_pkgs, + root, img_format=img_format, pkgs=pkgs, exclude_pkgs=exclude_pkgs, ) - if img_format != 'dir': - blkinfo = __salt__['disk.blkid'](loop2) - __salt__['file.replace']( - '{0}/boot/grub/grub.cfg'.format(mount_dir), - 'ad4103fa-d940-47ca-8506-301d8071d467', # This seems to be the default - blkinfo[loop2]['UUID'] + if img_format != "dir": + blkinfo = __salt__["disk.blkid"](loop2) + __salt__["file.replace"]( + "{0}/boot/grub/grub.cfg".format(mount_dir), + "ad4103fa-d940-47ca-8506-301d8071d467", # This seems to be the default + blkinfo[loop2]["UUID"], ) - __salt__['mount.umount'](root) - __salt__['cmd.run']('losetup -d {0}'.format(loop2)) - __salt__['cmd.run']('losetup -d {0}'.format(loop1)) - __salt__['file.rmdir'](mount_dir) + __salt__["mount.umount"](root) + __salt__["cmd.run"]("losetup -d {0}".format(loop2)) + __salt__["cmd.run"]("losetup -d {0}".format(loop1)) + __salt__["file.rmdir"](mount_dir) def _mkpart(root, fs_format, fs_opts, mount_dir): - ''' + """ Make a partition, and make it bootable .. versionadded:: Beryllium - ''' - __salt__['partition.mklabel'](root, 'msdos') - loop1 = __salt__['cmd.run']('losetup -f') - log.debug('First loop device is {0}'.format(loop1)) - __salt__['cmd.run']('losetup {0} {1}'.format(loop1, root)) - part_info = __salt__['partition.list'](loop1) - start = six.text_type(2048 * 2048) + 'B' - end = part_info['info']['size'] - __salt__['partition.mkpart'](loop1, 'primary', start=start, end=end) - __salt__['partition.set'](loop1, '1', 'boot', 'on') - part_info = __salt__['partition.list'](loop1) - loop2 = __salt__['cmd.run']('losetup -f') - log.debug('Second loop device is {0}'.format(loop2)) - start = start.rstrip('B') - __salt__['cmd.run']('losetup -o {0} {1} {2}'.format(start, loop2, loop1)) + """ + __salt__["partition.mklabel"](root, "msdos") + loop1 = __salt__["cmd.run"]("losetup -f") + log.debug("First loop device is {0}".format(loop1)) + __salt__["cmd.run"]("losetup {0} {1}".format(loop1, root)) + part_info = __salt__["partition.list"](loop1) + start = six.text_type(2048 * 2048) + "B" + end = part_info["info"]["size"] + __salt__["partition.mkpart"](loop1, "primary", start=start, end=end) + __salt__["partition.set"](loop1, "1", "boot", "on") + part_info = __salt__["partition.list"](loop1) + loop2 = __salt__["cmd.run"]("losetup -f") + log.debug("Second loop device is {0}".format(loop2)) + start = start.rstrip("B") + __salt__["cmd.run"]("losetup -o {0} {1} {2}".format(start, loop2, loop1)) _mkfs(loop2, fs_format, fs_opts) - __salt__['mount.mount'](mount_dir, loop2) - __salt__['cmd.run'](( - 'grub-install', - '--target=i386-pc', - '--debug', - '--no-floppy', - '--modules=part_msdos linux', - '--boot-directory={0}/boot'.format(mount_dir), - loop1 - ), python_shell=False) - __salt__['mount.umount'](mount_dir) - __salt__['cmd.run']('losetup -d {0}'.format(loop2)) - __salt__['cmd.run']('losetup -d {0}'.format(loop1)) + __salt__["mount.mount"](mount_dir, loop2) + __salt__["cmd.run"]( + ( + "grub-install", + "--target=i386-pc", + "--debug", + "--no-floppy", + "--modules=part_msdos linux", + "--boot-directory={0}/boot".format(mount_dir), + loop1, + ), + python_shell=False, + ) + __salt__["mount.umount"](mount_dir) + __salt__["cmd.run"]("losetup -d {0}".format(loop2)) + __salt__["cmd.run"]("losetup -d {0}".format(loop1)) return part_info def _mkfs(root, fs_format, fs_opts=None): - ''' + """ Make a filesystem using the appropriate module .. versionadded:: Beryllium - ''' + """ if fs_opts is None: fs_opts = {} - if fs_format in ('ext2', 'ext3', 'ext4'): - __salt__['extfs.mkfs'](root, fs_format, **fs_opts) - elif fs_format in ('btrfs',): - __salt__['btrfs.mkfs'](root, **fs_opts) - elif fs_format in ('xfs',): - __salt__['xfs.mkfs'](root, **fs_opts) + if fs_format in ("ext2", "ext3", "ext4"): + __salt__["extfs.mkfs"](root, fs_format, **fs_opts) + elif fs_format in ("btrfs",): + __salt__["btrfs.mkfs"](root, **fs_opts) + elif fs_format in ("xfs",): + __salt__["xfs.mkfs"](root, **fs_opts) def _populate_cache(platform, pkg_cache, mount_dir): - ''' + """ If a ``pkg_cache`` directory is specified, then use it to populate the disk image. - ''' + """ if not pkg_cache: return if not os.path.isdir(pkg_cache): return - if platform == 'pacman': - cache_dir = '{0}/var/cache/pacman/pkg'.format(mount_dir) + if platform == "pacman": + cache_dir = "{0}/var/cache/pacman/pkg".format(mount_dir) - __salt__['file.mkdir'](cache_dir, 'root', 'root', '755') - __salt__['file.copy'](pkg_cache, cache_dir, recurse=True, remove_existing=True) + __salt__["file.mkdir"](cache_dir, "root", "root", "755") + __salt__["file.copy"](pkg_cache, cache_dir, recurse=True, remove_existing=True) def _bootstrap_yum( - root, - pkg_confs='/etc/yum*', - pkgs=None, - exclude_pkgs=None, - epel_url=EPEL_URL, - ): - ''' + root, pkg_confs="/etc/yum*", pkgs=None, exclude_pkgs=None, epel_url=EPEL_URL, +): + """ Bootstrap an image using the yum tools root @@ -326,13 +324,13 @@ def _bootstrap_yum( TODO: Set up a pre-install overlay, to copy files into /etc/ and so on, which are required for the install to work. - ''' + """ if pkgs is None: pkgs = [] elif isinstance(pkgs, six.string_types): - pkgs = pkgs.split(',') + pkgs = pkgs.split(",") - default_pkgs = ('yum', 'centos-release', 'iputils') + default_pkgs = ("yum", "centos-release", "iputils") for pkg in default_pkgs: if pkg not in pkgs: pkgs.append(pkg) @@ -340,37 +338,48 @@ def _bootstrap_yum( if exclude_pkgs is None: exclude_pkgs = [] elif isinstance(exclude_pkgs, six.string_types): - exclude_pkgs = exclude_pkgs.split(',') + exclude_pkgs = exclude_pkgs.split(",") for pkg in exclude_pkgs: pkgs.remove(pkg) _make_nodes(root) - release_files = [rf for rf in os.listdir('/etc') if rf.endswith('release')] - __salt__['cmd.run']('cp /etc/resolv/conf {rfs} {root}/etc'.format(root=_cmd_quote(root), rfs=' '.join(release_files))) - __salt__['cmd.run']('cp -r {rfs} {root}/etc'.format(root=_cmd_quote(root), rfs=' '.join(release_files))) - __salt__['cmd.run']('cp -r {confs} {root}/etc'.format(root=_cmd_quote(root), confs=_cmd_quote(pkg_confs))) + release_files = [rf for rf in os.listdir("/etc") if rf.endswith("release")] + __salt__["cmd.run"]( + "cp /etc/resolv/conf {rfs} {root}/etc".format( + root=_cmd_quote(root), rfs=" ".join(release_files) + ) + ) + __salt__["cmd.run"]( + "cp -r {rfs} {root}/etc".format( + root=_cmd_quote(root), rfs=" ".join(release_files) + ) + ) + __salt__["cmd.run"]( + "cp -r {confs} {root}/etc".format( + root=_cmd_quote(root), confs=_cmd_quote(pkg_confs) + ) + ) - yum_args = ['yum', 'install', '--installroot={0}'.format(_cmd_quote(root)), '-y'] + pkgs - __salt__['cmd.run'](yum_args, python_shell=False) + yum_args = [ + "yum", + "install", + "--installroot={0}".format(_cmd_quote(root)), + "-y", + ] + pkgs + __salt__["cmd.run"](yum_args, python_shell=False) - if 'epel-release' not in exclude_pkgs: - __salt__['cmd.run']( - ('rpm', '--root={0}'.format(_cmd_quote(root)), '-Uvh', epel_url), - python_shell=False + if "epel-release" not in exclude_pkgs: + __salt__["cmd.run"]( + ("rpm", "--root={0}".format(_cmd_quote(root)), "-Uvh", epel_url), + python_shell=False, ) def _bootstrap_deb( - root, - arch, - flavor, - repo_url=None, - static_qemu=None, - pkgs=None, - exclude_pkgs=None, - ): - ''' + root, arch, flavor, repo_url=None, static_qemu=None, pkgs=None, exclude_pkgs=None, +): + """ Bootstrap an image using the Debian tools root @@ -396,35 +405,32 @@ def _bootstrap_deb( exclude_pkgs A list of packages to be excluded. - ''' + """ if repo_url is None: - repo_url = 'http://ftp.debian.org/debian/' + repo_url = "http://ftp.debian.org/debian/" - if not salt.utils.path.which('debootstrap'): - log.error('Required tool debootstrap is not installed.') + if not salt.utils.path.which("debootstrap"): + log.error("Required tool debootstrap is not installed.") return False if static_qemu and not salt.utils.validate.path.is_executable(static_qemu): - log.error('Required tool qemu not ' - 'present/readable at: {0}'.format(static_qemu)) + log.error( + "Required tool qemu not " "present/readable at: {0}".format(static_qemu) + ) return False if isinstance(pkgs, (list, tuple)): - pkgs = ','.join(pkgs) + pkgs = ",".join(pkgs) if isinstance(exclude_pkgs, (list, tuple)): - exclude_pkgs = ','.join(exclude_pkgs) + exclude_pkgs = ",".join(exclude_pkgs) - deb_args = [ - 'debootstrap', - '--foreign', - '--arch', - _cmd_quote(arch)] + deb_args = ["debootstrap", "--foreign", "--arch", _cmd_quote(arch)] if pkgs: - deb_args += ['--include', _cmd_quote(pkgs)] + deb_args += ["--include", _cmd_quote(pkgs)] if exclude_pkgs: - deb_args += ['--exclude', _cmd_quote(exclude_pkgs)] + deb_args += ["--exclude", _cmd_quote(exclude_pkgs)] deb_args += [ _cmd_quote(flavor), @@ -432,43 +438,38 @@ def _bootstrap_deb( _cmd_quote(repo_url), ] - __salt__['cmd.run'](deb_args, python_shell=False) + __salt__["cmd.run"](deb_args, python_shell=False) if static_qemu: - __salt__['cmd.run']( - 'cp {qemu} {root}/usr/bin/'.format( + __salt__["cmd.run"]( + "cp {qemu} {root}/usr/bin/".format( qemu=_cmd_quote(static_qemu), root=_cmd_quote(root) ) ) - env = {'DEBIAN_FRONTEND': 'noninteractive', - 'DEBCONF_NONINTERACTIVE_SEEN': 'true', - 'LC_ALL': 'C', - 'LANGUAGE': 'C', - 'LANG': 'C', - 'PATH': '/sbin:/bin:/usr/bin'} - __salt__['cmd.run']( - 'chroot {root} /debootstrap/debootstrap --second-stage'.format( + env = { + "DEBIAN_FRONTEND": "noninteractive", + "DEBCONF_NONINTERACTIVE_SEEN": "true", + "LC_ALL": "C", + "LANGUAGE": "C", + "LANG": "C", + "PATH": "/sbin:/bin:/usr/bin", + } + __salt__["cmd.run"]( + "chroot {root} /debootstrap/debootstrap --second-stage".format( root=_cmd_quote(root) ), - env=env + env=env, ) - __salt__['cmd.run']( - 'chroot {root} dpkg --configure -a'.format( - root=_cmd_quote(root) - ), - env=env + __salt__["cmd.run"]( + "chroot {root} dpkg --configure -a".format(root=_cmd_quote(root)), env=env ) def _bootstrap_pacman( - root, - pkg_confs='/etc/pacman*', - img_format='dir', - pkgs=None, - exclude_pkgs=None, - ): - ''' + root, pkg_confs="/etc/pacman*", img_format="dir", pkgs=None, exclude_pkgs=None, +): + """ Bootstrap an image using the pacman tools root @@ -491,15 +492,15 @@ def _bootstrap_pacman( exclude_pkgs A list of packages to be excluded. If you do not want to install the defaults, you need to include them in this list. - ''' + """ _make_nodes(root) if pkgs is None: pkgs = [] elif isinstance(pkgs, six.string_types): - pkgs = pkgs.split(',') + pkgs = pkgs.split(",") - default_pkgs = ('pacman', 'linux', 'systemd-sysvcompat', 'grub') + default_pkgs = ("pacman", "linux", "systemd-sysvcompat", "grub") for pkg in default_pkgs: if pkg not in pkgs: pkgs.append(pkg) @@ -507,67 +508,71 @@ def _bootstrap_pacman( if exclude_pkgs is None: exclude_pkgs = [] elif isinstance(exclude_pkgs, six.string_types): - exclude_pkgs = exclude_pkgs.split(',') + exclude_pkgs = exclude_pkgs.split(",") for pkg in exclude_pkgs: pkgs.remove(pkg) - if img_format != 'dir': - __salt__['mount.mount']('{0}/proc'.format(root), '/proc', fstype='', opts='bind') - __salt__['mount.mount']('{0}/dev'.format(root), '/dev', fstype='', opts='bind') + if img_format != "dir": + __salt__["mount.mount"]( + "{0}/proc".format(root), "/proc", fstype="", opts="bind" + ) + __salt__["mount.mount"]("{0}/dev".format(root), "/dev", fstype="", opts="bind") - __salt__['file.mkdir']( - '{0}/var/lib/pacman/local'.format(root), 'root', 'root', '755' + __salt__["file.mkdir"]( + "{0}/var/lib/pacman/local".format(root), "root", "root", "755" ) - pac_files = [rf for rf in os.listdir('/etc') if rf.startswith('pacman.')] + pac_files = [rf for rf in os.listdir("/etc") if rf.startswith("pacman.")] for pac_file in pac_files: - __salt__['cmd.run']('cp -r /etc/{0} {1}/etc'.format(pac_file, _cmd_quote(root))) - __salt__['file.copy']('/var/lib/pacman/sync', '{0}/var/lib/pacman/sync'.format(root), recurse=True) + __salt__["cmd.run"]("cp -r /etc/{0} {1}/etc".format(pac_file, _cmd_quote(root))) + __salt__["file.copy"]( + "/var/lib/pacman/sync", "{0}/var/lib/pacman/sync".format(root), recurse=True + ) - pacman_args = ['pacman', '--noconfirm', '-r', _cmd_quote(root), '-S'] + pkgs - __salt__['cmd.run'](pacman_args, python_shell=False) + pacman_args = ["pacman", "--noconfirm", "-r", _cmd_quote(root), "-S"] + pkgs + __salt__["cmd.run"](pacman_args, python_shell=False) - if img_format != 'dir': - __salt__['mount.umount']('{0}/proc'.format(root)) - __salt__['mount.umount']('{0}/dev'.format(root)) + if img_format != "dir": + __salt__["mount.umount"]("{0}/proc".format(root)) + __salt__["mount.umount"]("{0}/dev".format(root)) def _make_nodes(root): - ''' + """ Make the minimum number of nodes inside of /dev/. Based on: https://wiki.archlinux.org/index.php/Linux_Containers - ''' + """ dirs = ( - ('{0}/etc'.format(root), 'root', 'root', '755'), - ('{0}/dev'.format(root), 'root', 'root', '755'), - ('{0}/proc'.format(root), 'root', 'root', '755'), - ('{0}/dev/pts'.format(root), 'root', 'root', '755'), - ('{0}/dev/shm'.format(root), 'root', 'root', '1755'), + ("{0}/etc".format(root), "root", "root", "755"), + ("{0}/dev".format(root), "root", "root", "755"), + ("{0}/proc".format(root), "root", "root", "755"), + ("{0}/dev/pts".format(root), "root", "root", "755"), + ("{0}/dev/shm".format(root), "root", "root", "1755"), ) nodes = ( - ('{0}/dev/null'.format(root), 'c', 1, 3, 'root', 'root', '666'), - ('{0}/dev/zero'.format(root), 'c', 1, 5, 'root', 'root', '666'), - ('{0}/dev/random'.format(root), 'c', 1, 8, 'root', 'root', '666'), - ('{0}/dev/urandom'.format(root), 'c', 1, 9, 'root', 'root', '666'), - ('{0}/dev/tty'.format(root), 'c', 5, 0, 'root', 'root', '666'), - ('{0}/dev/tty0'.format(root), 'c', 4, 0, 'root', 'root', '666'), - ('{0}/dev/console'.format(root), 'c', 5, 1, 'root', 'root', '600'), - ('{0}/dev/full'.format(root), 'c', 1, 7, 'root', 'root', '666'), - ('{0}/dev/initctl'.format(root), 'p', 0, 0, 'root', 'root', '600'), - ('{0}/dev/ptmx'.format(root), 'c', 5, 2, 'root', 'root', '666'), + ("{0}/dev/null".format(root), "c", 1, 3, "root", "root", "666"), + ("{0}/dev/zero".format(root), "c", 1, 5, "root", "root", "666"), + ("{0}/dev/random".format(root), "c", 1, 8, "root", "root", "666"), + ("{0}/dev/urandom".format(root), "c", 1, 9, "root", "root", "666"), + ("{0}/dev/tty".format(root), "c", 5, 0, "root", "root", "666"), + ("{0}/dev/tty0".format(root), "c", 4, 0, "root", "root", "666"), + ("{0}/dev/console".format(root), "c", 5, 1, "root", "root", "600"), + ("{0}/dev/full".format(root), "c", 1, 7, "root", "root", "666"), + ("{0}/dev/initctl".format(root), "p", 0, 0, "root", "root", "600"), + ("{0}/dev/ptmx".format(root), "c", 5, 2, "root", "root", "666"), ) for path in dirs: - __salt__['file.mkdir'](*path) + __salt__["file.mkdir"](*path) for path in nodes: - __salt__['file.mknod'](*path) + __salt__["file.mknod"](*path) def avail_platforms(): - ''' + """ Return which platforms are available CLI Example: @@ -575,7 +580,7 @@ def avail_platforms(): .. code-block:: bash salt myminion genesis.avail_platforms - ''' + """ ret = {} for platform in CMD_MAP: ret[platform] = True @@ -585,8 +590,8 @@ def avail_platforms(): return ret -def pack(name, root, path=None, pack_format='tar', compress='bzip2'): - ''' +def pack(name, root, path=None, pack_format="tar", compress="bzip2"): + """ Pack up a directory structure, into a specific format CLI Examples: @@ -595,13 +600,13 @@ def pack(name, root, path=None, pack_format='tar', compress='bzip2'): salt myminion genesis.pack centos /root/centos salt myminion genesis.pack centos /root/centos pack_format='tar' - ''' - if pack_format == 'tar': + """ + if pack_format == "tar": _tar(name, root, path, compress) -def unpack(name, dest=None, path=None, pack_format='tar', compress='bz2'): - ''' +def unpack(name, dest=None, path=None, pack_format="tar", compress="bz2"): + """ Unpack an image into a directory structure CLI Example: @@ -609,79 +614,74 @@ def unpack(name, dest=None, path=None, pack_format='tar', compress='bz2'): .. code-block:: bash salt myminion genesis.unpack centos /root/centos - ''' - if pack_format == 'tar': + """ + if pack_format == "tar": _untar(name, dest, path, compress) -def _tar(name, root, path=None, compress='bzip2'): - ''' +def _tar(name, root, path=None, compress="bzip2"): + """ Pack up image in a tar format - ''' + """ if path is None: - path = os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, 'img') - if not __salt__['file.directory_exists'](path): + path = os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, "img") + if not __salt__["file.directory_exists"](path): try: - __salt__['file.mkdir'](path) + __salt__["file.mkdir"](path) except Exception as exc: # pylint: disable=broad-except - return {'Error': salt.utils.stringutils.to_unicode(pprint.pformat(exc))} + return {"Error": salt.utils.stringutils.to_unicode(pprint.pformat(exc))} compression, ext = _compress(compress) - tarfile = '{0}/{1}.tar.{2}'.format(path, name, ext) - out = __salt__['archive.tar']( - options='{0}pcf'.format(compression), - tarfile=tarfile, - sources='.', - dest=root, + tarfile = "{0}/{1}.tar.{2}".format(path, name, ext) + out = __salt__["archive.tar"]( + options="{0}pcf".format(compression), tarfile=tarfile, sources=".", dest=root, ) -def _untar(name, dest=None, path=None, compress='bz2'): - ''' +def _untar(name, dest=None, path=None, compress="bz2"): + """ Unpack a tarball to be used as a container - ''' + """ if path is None: - path = os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, 'img') + path = os.path.join(salt.syspaths.BASE_FILE_ROOTS_DIR, "img") if not dest: dest = path - if not __salt__['file.directory_exists'](dest): + if not __salt__["file.directory_exists"](dest): try: - __salt__['file.mkdir'](dest) + __salt__["file.mkdir"](dest) except Exception as exc: # pylint: disable=broad-except - return {'Error': salt.utils.stringutils.to_unicode(pprint.pformat(exc))} + return {"Error": salt.utils.stringutils.to_unicode(pprint.pformat(exc))} compression, ext = _compress(compress) - tarfile = '{0}/{1}.tar.{2}'.format(path, name, ext) - out = __salt__['archive.tar']( - options='{0}xf'.format(compression), - tarfile=tarfile, - dest=dest, + tarfile = "{0}/{1}.tar.{2}".format(path, name, ext) + out = __salt__["archive.tar"]( + options="{0}xf".format(compression), tarfile=tarfile, dest=dest, ) def _compress(compress): - ''' + """ Resolve compression flags - ''' - if compress in ('bz2', 'bzip2', 'j'): - compression = 'j' - ext = 'bz2' - elif compress in ('gz', 'gzip', 'z'): - compression = 'z' - ext = 'gz' - elif compress in ('xz', 'a', 'J'): - compression = 'J' - ext = 'xz' + """ + if compress in ("bz2", "bzip2", "j"): + compression = "j" + ext = "bz2" + elif compress in ("gz", "gzip", "z"): + compression = "z" + ext = "gz" + elif compress in ("xz", "a", "J"): + compression = "J" + ext = "xz" return compression, ext def ldd_deps(filename, ret=None): - ''' + """ Recurse through a set of dependencies reported by ``ldd``, to find associated dependencies. @@ -692,20 +692,20 @@ def ldd_deps(filename, ret=None): salt myminion genesis.ldd_deps bash salt myminion genesis.ldd_deps /bin/bash - ''' + """ if not os.path.exists(filename): filename = salt.utils.path.which(filename) if ret is None: ret = [] - out = __salt__['cmd.run'](('ldd', filename), python_shell=False) + out = __salt__["cmd.run"](("ldd", filename), python_shell=False) for line in out.splitlines(): if not line.strip(): continue - dep_path = '' - if '=>' in line: - comps = line.split(' => ') + dep_path = "" + if "=>" in line: + comps = line.split(" => ") dep_comps = comps[1].strip().split() if os.path.exists(dep_comps[0]): dep_path = dep_comps[0] @@ -726,7 +726,7 @@ def ldd_deps(filename, ret=None): def mksls(fmt, src, dst=None): - ''' + """ Convert an installation file/script to an SLS file. Currently supports ``kickstart``, ``preseed``, and ``autoyast``. @@ -736,10 +736,10 @@ def mksls(fmt, src, dst=None): salt genesis.mksls kickstart /path/to/kickstart.cfg /path/to/dest.sls .. versionadded:: Beryllium - ''' - if fmt == 'kickstart': + """ + if fmt == "kickstart": return salt.utils.kickstart.mksls(src, dst) - elif fmt == 'preseed': + elif fmt == "preseed": return salt.utils.preseed.mksls(src, dst) - elif fmt == 'autoyast': + elif fmt == "autoyast": return salt.utils.yast.mksls(src, dst) diff --git a/salt/modules/gentoo_service.py b/salt/modules/gentoo_service.py index 08bb89d1eb4..53f500003c0 100644 --- a/salt/modules/gentoo_service.py +++ b/salt/modules/gentoo_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Top level package command wrapper, used to translate the os detected by grains to the correct service manager @@ -8,50 +8,55 @@ to the correct service manager minion, and it is using a different module (or gives an error similar to *'service.start' is not available*), see :ref:`here `. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging + import fnmatch +import logging import re +import salt.utils.odict as odict + # Import salt libs import salt.utils.systemd -import salt.utils.odict as odict # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" # Define the module's function aliases -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} def __virtual__(): - ''' + """ Only work on systems which default to OpenRC - ''' - if __grains__['os'] == 'Gentoo' and not salt.utils.systemd.booted(__context__): + """ + if __grains__["os"] == "Gentoo" and not salt.utils.systemd.booted(__context__): return __virtualname__ - if __grains__['os'] == 'Alpine': + if __grains__["os"] == "Alpine": return __virtualname__ - return (False, 'The gentoo_service execution module cannot be loaded: ' - 'only available on Gentoo/Open-RC systems.') + return ( + False, + "The gentoo_service execution module cannot be loaded: " + "only available on Gentoo/Open-RC systems.", + ) def _ret_code(cmd, ignore_retcode=False): - log.debug('executing [{0}]'.format(cmd)) - sts = __salt__['cmd.retcode'](cmd, python_shell=False, ignore_retcode=ignore_retcode) + log.debug("executing [{0}]".format(cmd)) + sts = __salt__["cmd.retcode"]( + cmd, python_shell=False, ignore_retcode=ignore_retcode + ) return sts def _list_services(): - return __salt__['cmd.run']('rc-update -v show').splitlines() + return __salt__["cmd.run"]("rc-update -v show").splitlines() def _get_service_list(include_enabled=True, include_disabled=False): @@ -59,9 +64,9 @@ def _get_service_list(include_enabled=True, include_disabled=False): disabled_services = set() lines = _list_services() for line in lines: - if '|' not in line: + if "|" not in line: continue - service = [l.strip() for l in line.split('|')] + service = [l.strip() for l in line.split("|")] # enabled service should have runlevels if service[1]: if include_enabled: @@ -88,15 +93,17 @@ def _disable_delta(name, requested_runlevels): def _service_cmd(*args): - return '/etc/init.d/{0} {1}'.format(args[0], ' '.join(args[1:])) + return "/etc/init.d/{0} {1}".format(args[0], " ".join(args[1:])) def _enable_disable_cmd(name, command, runlevels=()): - return 'rc-update {0} {1} {2}'.format(command, name, ' '.join(sorted(runlevels))).strip() + return "rc-update {0} {1} {2}".format( + command, name, " ".join(sorted(runlevels)) + ).strip() def get_enabled(): - ''' + """ Return a list of service that are enabled on boot CLI Example: @@ -104,13 +111,13 @@ def get_enabled(): .. code-block:: bash salt '*' service.get_enabled - ''' + """ (enabled_services, disabled_services) = _get_service_list() return odict.OrderedDict(enabled_services) def get_disabled(): - ''' + """ Return a set of services that are installed but disabled CLI Example: @@ -118,14 +125,15 @@ def get_disabled(): .. code-block:: bash salt '*' service.get_disabled - ''' - (enabled_services, disabled_services) = _get_service_list(include_enabled=False, - include_disabled=True) + """ + (enabled_services, disabled_services) = _get_service_list( + include_enabled=False, include_disabled=True + ) return sorted(disabled_services) def available(name): - ''' + """ Returns ``True`` if the specified service is available, otherwise returns ``False``. @@ -134,14 +142,15 @@ def available(name): .. code-block:: bash salt '*' service.available sshd - ''' - (enabled_services, disabled_services) = _get_service_list(include_enabled=True, - include_disabled=True) + """ + (enabled_services, disabled_services) = _get_service_list( + include_enabled=True, include_disabled=True + ) return name in enabled_services or name in disabled_services def missing(name): - ''' + """ The inverse of service.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -151,12 +160,12 @@ def missing(name): .. code-block:: bash salt '*' service.missing sshd - ''' + """ return not available(name) def get_all(): - ''' + """ Return all available boot services CLI Example: @@ -164,15 +173,16 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' - (enabled_services, disabled_services) = _get_service_list(include_enabled=True, - include_disabled=True) + """ + (enabled_services, disabled_services) = _get_service_list( + include_enabled=True, include_disabled=True + ) enabled_services.update(dict([(s, []) for s in disabled_services])) return odict.OrderedDict(enabled_services) def start(name): - ''' + """ Start the specified service CLI Example: @@ -180,13 +190,13 @@ def start(name): .. code-block:: bash salt '*' service.start - ''' - cmd = _service_cmd(name, 'start') + """ + cmd = _service_cmd(name, "start") return not _ret_code(cmd) def stop(name): - ''' + """ Stop the specified service CLI Example: @@ -194,13 +204,13 @@ def stop(name): .. code-block:: bash salt '*' service.stop - ''' - cmd = _service_cmd(name, 'stop') + """ + cmd = _service_cmd(name, "stop") return not _ret_code(cmd) def restart(name): - ''' + """ Restart the named service CLI Example: @@ -208,13 +218,13 @@ def restart(name): .. code-block:: bash salt '*' service.restart - ''' - cmd = _service_cmd(name, 'restart') + """ + cmd = _service_cmd(name, "restart") return not _ret_code(cmd) def reload_(name): - ''' + """ Reload the named service CLI Example: @@ -222,13 +232,13 @@ def reload_(name): .. code-block:: bash salt '*' service.reload - ''' - cmd = _service_cmd(name, 'reload') + """ + cmd = _service_cmd(name, "reload") return not _ret_code(cmd) def zap(name): - ''' + """ Resets service state CLI Example: @@ -236,13 +246,13 @@ def zap(name): .. code-block:: bash salt '*' service.zap - ''' - cmd = _service_cmd(name, 'zap') + """ + cmd = _service_cmd(name, "zap") return not _ret_code(cmd) def status(name, sig=None): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -263,18 +273,18 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status [service signature] - ''' + """ if sig: - return bool(__salt__['status.pid'](sig)) + return bool(__salt__["status.pid"](sig)) - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: services = [name] results = {} for service in services: - cmd = _service_cmd(service, 'status') + cmd = _service_cmd(service, "status") results[service] = not _ret_code(cmd, ignore_retcode=True) if contains_globbing: return results @@ -282,7 +292,7 @@ def status(name, sig=None): def enable(name, **kwargs): - ''' + """ Enable the named service to start at boot CLI Example: @@ -291,20 +301,23 @@ def enable(name, **kwargs): salt '*' service.enable salt '*' service.enable - ''' - if 'runlevels' in kwargs: - requested_levels = set(kwargs['runlevels'] if isinstance(kwargs['runlevels'], - list) else [kwargs['runlevels']]) + """ + if "runlevels" in kwargs: + requested_levels = set( + kwargs["runlevels"] + if isinstance(kwargs["runlevels"], list) + else [kwargs["runlevels"]] + ) enabled_levels, disabled_levels = _enable_delta(name, requested_levels) commands = [] if disabled_levels: - commands.append(_enable_disable_cmd(name, 'delete', disabled_levels)) + commands.append(_enable_disable_cmd(name, "delete", disabled_levels)) if enabled_levels: - commands.append(_enable_disable_cmd(name, 'add', enabled_levels)) + commands.append(_enable_disable_cmd(name, "add", enabled_levels)) if not commands: return True else: - commands = [_enable_disable_cmd(name, 'add')] + commands = [_enable_disable_cmd(name, "add")] for cmd in commands: if _ret_code(cmd): return False @@ -312,7 +325,7 @@ def enable(name, **kwargs): def disable(name, **kwargs): - ''' + """ Disable the named service to start at boot CLI Example: @@ -321,20 +334,23 @@ def disable(name, **kwargs): salt '*' service.disable salt '*' service.disable - ''' + """ levels = [] - if 'runlevels' in kwargs: - requested_levels = set(kwargs['runlevels'] if isinstance(kwargs['runlevels'], - list) else [kwargs['runlevels']]) + if "runlevels" in kwargs: + requested_levels = set( + kwargs["runlevels"] + if isinstance(kwargs["runlevels"], list) + else [kwargs["runlevels"]] + ) levels = _disable_delta(name, requested_levels) if not levels: return True - cmd = _enable_disable_cmd(name, 'delete', levels) + cmd = _enable_disable_cmd(name, "delete", levels) return not _ret_code(cmd) def enabled(name, **kwargs): - ''' + """ Return True if the named service is enabled, false otherwise CLI Example: @@ -343,19 +359,22 @@ def enabled(name, **kwargs): salt '*' service.enabled salt '*' service.enabled - ''' + """ enabled_services = get_enabled() if name not in enabled_services: return False - if 'runlevels' not in kwargs: + if "runlevels" not in kwargs: return True - requested_levels = set(kwargs['runlevels'] if isinstance(kwargs['runlevels'], - list) else [kwargs['runlevels']]) + requested_levels = set( + kwargs["runlevels"] + if isinstance(kwargs["runlevels"], list) + else [kwargs["runlevels"]] + ) return len(requested_levels - set(enabled_services[name])) == 0 def disabled(name): - ''' + """ Return True if the named service is enabled, false otherwise CLI Example: @@ -363,5 +382,5 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled - ''' + """ return name in get_disabled() diff --git a/salt/modules/gentoolkitmod.py b/salt/modules/gentoolkitmod.py index d33c1483da0..cc78c371149 100644 --- a/salt/modules/gentoolkitmod.py +++ b/salt/modules/gentoolkitmod.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Support for Gentoolkit -''' +""" from __future__ import absolute_import, print_function, unicode_literals import os @@ -12,26 +12,30 @@ HAS_GENTOOLKIT = False # Import third party libs try: from gentoolkit.eclean import search, clean, cli, exclude as excludemod + HAS_GENTOOLKIT = True except ImportError: pass # Define the module's virtual name -__virtualname__ = 'gentoolkit' +__virtualname__ = "gentoolkit" def __virtual__(): - ''' + """ Only work on Gentoo systems with gentoolkit installed - ''' - if __grains__['os'] == 'Gentoo' and HAS_GENTOOLKIT: + """ + if __grains__["os"] == "Gentoo" and HAS_GENTOOLKIT: return __virtualname__ - return (False, 'The gentoolkitmod execution module cannot be loaded: ' - 'either the system is not Gentoo or the gentoolkit.eclean python module not available') + return ( + False, + "The gentoolkitmod execution module cannot be loaded: " + "either the system is not Gentoo or the gentoolkit.eclean python module not available", + ) def revdep_rebuild(lib=None): - ''' + """ Fix up broken reverse dependencies lib @@ -44,30 +48,30 @@ def revdep_rebuild(lib=None): .. code-block:: bash salt '*' gentoolkit.revdep_rebuild - ''' - cmd = 'revdep-rebuild -i --quiet --no-progress' + """ + cmd = "revdep-rebuild -i --quiet --no-progress" if lib is not None: - cmd += ' --library={0}'.format(lib) - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd += " --library={0}".format(lib) + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 def _pretty_size(size): - ''' + """ Print sizes in a similar fashion as eclean - ''' - units = [' G', ' M', ' K', ' B'] + """ + units = [" G", " M", " K", " B"] while units and size >= 1000: size = size / 1024.0 units.pop() - return '{0}{1}'.format(round(size, 1), units[-1]) + return "{0}{1}".format(round(size, 1), units[-1]) def _parse_exclude(exclude_file): - ''' + """ Parse an exclude file. Returns a dict as defined in gentoolkit.eclean.exclude.parseExcludeFile - ''' + """ if os.path.isfile(exclude_file): exclude = excludemod.parseExcludeFile(exclude_file, lambda x: None) else: @@ -75,10 +79,15 @@ def _parse_exclude(exclude_file): return exclude -def eclean_dist(destructive=False, package_names=False, size_limit=0, - time_limit=0, fetch_restricted=False, - exclude_file='/etc/eclean/distfiles.exclude'): - ''' +def eclean_dist( + destructive=False, + package_names=False, + size_limit=0, + time_limit=0, + fetch_restricted=False, + exclude_file="/etc/eclean/distfiles.exclude", +): + """ Clean obsolete portage sources destructive @@ -122,14 +131,14 @@ def eclean_dist(destructive=False, package_names=False, size_limit=0, .. code-block:: bash salt '*' gentoolkit.eclean_dist destructive=True - ''' + """ if exclude_file is None: exclude = None else: try: exclude = _parse_exclude(exclude_file) except excludemod.ParseExcludeFileException as e: - ret = {e: 'Invalid exclusion file: {0}'.format(exclude_file)} + ret = {e: "Invalid exclusion file: {0}".format(exclude_file)} return ret if time_limit != 0: @@ -140,9 +149,13 @@ def eclean_dist(destructive=False, package_names=False, size_limit=0, clean_size = 0 engine = search.DistfilesSearch(lambda x: None) clean_me, saved, deprecated = engine.findDistfiles( - destructive=destructive, package_names=package_names, - size_limit=size_limit, time_limit=time_limit, - fetch_restricted=fetch_restricted, exclude=exclude) + destructive=destructive, + package_names=package_names, + size_limit=size_limit, + time_limit=time_limit, + fetch_restricted=fetch_restricted, + exclude=exclude, + ) cleaned = dict() @@ -154,14 +167,22 @@ def eclean_dist(destructive=False, package_names=False, size_limit=0, cleaner = clean.CleanUp(_eclean_progress_controller) clean_size = cleaner.clean_dist(clean_me) - ret = {'cleaned': cleaned, 'saved': saved, 'deprecated': deprecated, - 'total_cleaned': _pretty_size(clean_size)} + ret = { + "cleaned": cleaned, + "saved": saved, + "deprecated": deprecated, + "total_cleaned": _pretty_size(clean_size), + } return ret -def eclean_pkg(destructive=False, package_names=False, time_limit=0, - exclude_file='/etc/eclean/packages.exclude'): - ''' +def eclean_pkg( + destructive=False, + package_names=False, + time_limit=0, + exclude_file="/etc/eclean/packages.exclude", +): + """ Clean obsolete binary packages destructive @@ -194,14 +215,14 @@ def eclean_pkg(destructive=False, package_names=False, time_limit=0, .. code-block:: bash salt '*' gentoolkit.eclean_pkg destructive=True - ''' + """ if exclude_file is None: exclude = None else: try: exclude = _parse_exclude(exclude_file) except excludemod.ParseExcludeFileException as e: - ret = {e: 'Invalid exclusion file: {0}'.format(exclude_file)} + ret = {e: "Invalid exclusion file: {0}".format(exclude_file)} return ret if time_limit != 0: @@ -210,10 +231,14 @@ def eclean_pkg(destructive=False, package_names=False, time_limit=0, clean_size = 0 # findPackages requires one arg, but does nothing with it. # So we will just pass None in for the required arg - clean_me = search.findPackages(None, destructive=destructive, - package_names=package_names, - time_limit=time_limit, exclude=exclude, - pkgdir=search.pkgdir) + clean_me = search.findPackages( + None, + destructive=destructive, + package_names=package_names, + time_limit=time_limit, + exclude=exclude, + pkgdir=search.pkgdir, + ) cleaned = dict() @@ -225,41 +250,39 @@ def eclean_pkg(destructive=False, package_names=False, time_limit=0, cleaner = clean.CleanUp(_eclean_progress_controller) clean_size = cleaner.clean_pkgs(clean_me, search.pkgdir) - ret = {'cleaned': cleaned, - 'total_cleaned': _pretty_size(clean_size)} + ret = {"cleaned": cleaned, "total_cleaned": _pretty_size(clean_size)} return ret def _glsa_list_process_output(output): - ''' + """ Process output from glsa_check_list into a dict Returns a dict containing the glsa id, description, status, and CVEs - ''' + """ ret = dict() for line in output: try: glsa_id, status, desc = line.split(None, 2) - if 'U' in status: - status += ' Not Affected' - elif 'N' in status: - status += ' Might be Affected' - elif 'A' in status: - status += ' Applied (injected)' - if 'CVE' in desc: + if "U" in status: + status += " Not Affected" + elif "N" in status: + status += " Might be Affected" + elif "A" in status: + status += " Applied (injected)" + if "CVE" in desc: desc, cves = desc.rsplit(None, 1) - cves = cves.split(',') + cves = cves.split(",") else: cves = list() - ret[glsa_id] = {'description': desc, 'status': status, - 'CVEs': cves} + ret[glsa_id] = {"description": desc, "status": status, "CVEs": cves} except ValueError: pass return ret def glsa_check_list(glsa_list): - ''' + """ List the status of Gentoo Linux Security Advisories glsa_list @@ -279,15 +302,15 @@ def glsa_check_list(glsa_list): .. code-block:: bash salt '*' gentoolkit.glsa_check_list 'affected' - ''' - cmd = 'glsa-check --quiet --nocolor --cve --list ' + """ + cmd = "glsa-check --quiet --nocolor --cve --list " if isinstance(glsa_list, list): for glsa in glsa_list: - cmd += glsa + ' ' - elif glsa_list == 'all' or glsa_list == 'affected': + cmd += glsa + " " + elif glsa_list == "all" or glsa_list == "affected": cmd += glsa_list ret = dict() - out = __salt__['cmd.run'](cmd, python_shell=False).split('\n') + out = __salt__["cmd.run"](cmd, python_shell=False).split("\n") ret = _glsa_list_process_output(out) return ret diff --git a/salt/modules/git.py b/salt/modules/git.py index ad0086ac105..4a8db321b45 100644 --- a/salt/modules/git.py +++ b/salt/modules/git.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Support for the Git SCM -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -23,103 +23,102 @@ import salt.utils.platform import salt.utils.stringutils import salt.utils.templates import salt.utils.url -from salt.exceptions import SaltInvocationError, CommandExecutionError -from salt.utils.versions import LooseVersion as _LooseVersion +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion log = logging.getLogger(__name__) -__func_alias__ = { - 'rm_': 'rm' -} +__func_alias__ = {"rm_": "rm"} def __virtual__(): - ''' + """ Only load if git exists on the system - ''' - if salt.utils.path.which('git') is None: - return (False, - 'The git execution module cannot be loaded: git unavailable.') + """ + if salt.utils.path.which("git") is None: + return (False, "The git execution module cannot be loaded: git unavailable.") else: return True def _check_worktree_support(failhard=True): - ''' + """ Ensure that we don't try to operate on worktrees in git < 2.5.0. - ''' + """ git_version = version(versioninfo=False) - if _LooseVersion(git_version) < _LooseVersion('2.5.0'): + if _LooseVersion(git_version) < _LooseVersion("2.5.0"): if failhard: raise CommandExecutionError( - 'Worktrees are only supported in git 2.5.0 and newer ' - '(detected git version: ' + git_version + ')' + "Worktrees are only supported in git 2.5.0 and newer " + "(detected git version: " + git_version + ")" ) return False return True -def _config_getter(get_opt, - key, - value_regex=None, - cwd=None, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None, - **kwargs): - ''' +def _config_getter( + get_opt, + key, + value_regex=None, + cwd=None, + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, + **kwargs +): + """ Common code for config.get_* functions, builds and runs the git CLI command and returns the result dict for the calling function to parse. - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - global_ = kwargs.pop('global', False) + global_ = kwargs.pop("global", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if cwd is None: if not global_: - raise SaltInvocationError( - '\'cwd\' argument required unless global=True' - ) + raise SaltInvocationError("'cwd' argument required unless global=True") else: cwd = _expand_path(cwd, user) - if get_opt == '--get-regexp': - if value_regex is not None \ - and not isinstance(value_regex, six.string_types): + if get_opt == "--get-regexp": + if value_regex is not None and not isinstance(value_regex, six.string_types): value_regex = six.text_type(value_regex) else: # Ignore value_regex value_regex = None - command = ['git', 'config'] - command.extend(_which_git_config(global_, cwd, user, password, - output_encoding=output_encoding)) + command = ["git", "config"] + command.extend( + _which_git_config(global_, cwd, user, password, output_encoding=output_encoding) + ) command.append(get_opt) command.append(key) if value_regex is not None: command.append(value_regex) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - failhard=False, - output_encoding=output_encoding) + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + failhard=False, + output_encoding=output_encoding, + ) def _expand_path(cwd, user): - ''' + """ Expand home directory - ''' + """ try: - to_expand = '~' + user if user else '~' + to_expand = "~" + user if user else "~" except TypeError: # Users should never be numeric but if we don't account for this then # we're going to get a traceback if someone passes this invalid input. - to_expand = '~' + six.text_type(user) if user else '~' + to_expand = "~" + six.text_type(user) if user else "~" try: return os.path.join(os.path.expanduser(to_expand), cwd) except AttributeError: @@ -127,9 +126,9 @@ def _expand_path(cwd, user): def _path_is_executable_others(path): - ''' + """ Check every part of path for executable permission - ''' + """ prevpath = None while path and path != prevpath: try: @@ -143,9 +142,9 @@ def _path_is_executable_others(path): def _format_opts(opts): - ''' + """ Common code to inspect opts and split them if necessary - ''' + """ if opts is None: return [] elif isinstance(opts, list): @@ -163,7 +162,7 @@ def _format_opts(opts): opts = salt.utils.args.shlex_split(opts) opts = salt.utils.data.decode(opts) try: - if opts[-1] == '--': + if opts[-1] == "--": # Strip the '--' if it was passed at the end of the opts string, # it'll be added back (if necessary) in the calling function. # Putting this check here keeps it from having to be repeated every @@ -175,31 +174,39 @@ def _format_opts(opts): def _format_git_opts(opts): - ''' + """ Do a version check and make sure that the installed version of git can support git -c - ''' + """ if opts: version_ = version(versioninfo=False) - if _LooseVersion(version_) < _LooseVersion('1.7.2'): + if _LooseVersion(version_) < _LooseVersion("1.7.2"): raise SaltInvocationError( - 'git_opts is only supported for git versions >= 1.7.2 ' - '(detected: {0})'.format(version_) + "git_opts is only supported for git versions >= 1.7.2 " + "(detected: {0})".format(version_) ) return _format_opts(opts) def _find_ssh_exe(): - ''' + """ Windows only: search for Git's bundled ssh.exe in known locations - ''' + """ # Known locations for Git's ssh.exe in Windows - globmasks = [os.path.join(os.getenv('SystemDrive'), os.sep, - 'Program Files*', 'Git', 'usr', 'bin', - 'ssh.exe'), - os.path.join(os.getenv('SystemDrive'), os.sep, - 'Program Files*', 'Git', 'bin', - 'ssh.exe')] + globmasks = [ + os.path.join( + os.getenv("SystemDrive"), + os.sep, + "Program Files*", + "Git", + "usr", + "bin", + "ssh.exe", + ), + os.path.join( + os.getenv("SystemDrive"), os.sep, "Program Files*", "Git", "bin", "ssh.exe" + ), + ] for globmask in globmasks: ssh_exe = glob.glob(globmask) if ssh_exe and os.path.isfile(ssh_exe[0]): @@ -211,20 +218,30 @@ def _find_ssh_exe(): return ret -def _git_run(command, cwd=None, user=None, password=None, identity=None, - ignore_retcode=False, failhard=True, redirect_stderr=False, - saltenv='base', output_encoding=None, **kwargs): - ''' +def _git_run( + command, + cwd=None, + user=None, + password=None, + identity=None, + ignore_retcode=False, + failhard=True, + redirect_stderr=False, + saltenv="base", + output_encoding=None, + **kwargs +): + """ simple, throw an exception with the error message on an error return code. this function may be moved to the command module, spliced with 'cmd.run_all', and used as an alternative to 'cmd.run_all'. Some commands don't return proper retcodes, so this can't replace 'cmd.run_all'. - ''' + """ env = {} if identity: - _salt_cli = __opts__.get('__cli', '') + _salt_cli = __opts__.get("__cli", "") errors = [] missing_keys = [] @@ -237,75 +254,70 @@ def _git_run(command, cwd=None, user=None, password=None, identity=None, # try each of the identities, independently tmp_identity_file = None for id_file in identity: - if 'salt://' in id_file: + if "salt://" in id_file: with salt.utils.files.set_umask(0o077): tmp_identity_file = salt.utils.files.mkstemp() _id_file = id_file - id_file = __salt__['cp.get_file'](id_file, - tmp_identity_file, - saltenv) + id_file = __salt__["cp.get_file"]( + id_file, tmp_identity_file, saltenv + ) if not id_file: - log.error('identity %s does not exist.', _id_file) - __salt__['file.remove'](tmp_identity_file) + log.error("identity %s does not exist.", _id_file) + __salt__["file.remove"](tmp_identity_file) continue else: if user: - os.chown(id_file, - __salt__['file.user_to_uid'](user), - -1) + os.chown(id_file, __salt__["file.user_to_uid"](user), -1) else: - if not __salt__['file.file_exists'](id_file): + if not __salt__["file.file_exists"](id_file): missing_keys.append(id_file) - log.error('identity %s does not exist.', id_file) + log.error("identity %s does not exist.", id_file) continue - env = { - 'GIT_IDENTITY': id_file - } + env = {"GIT_IDENTITY": id_file} # copy wrapper to area accessible by ``runas`` user # currently no support in windows for wrapping git ssh - ssh_id_wrapper = os.path.abspath(os.path.join( - salt.utils.templates.TEMPLATE_DIRNAME, - 'git/ssh-id-wrapper' - )) + ssh_id_wrapper = os.path.abspath( + os.path.join( + salt.utils.templates.TEMPLATE_DIRNAME, "git/ssh-id-wrapper" + ) + ) tmp_ssh_wrapper = None if salt.utils.platform.is_windows(): ssh_exe = _find_ssh_exe() if ssh_exe is None: raise CommandExecutionError( - 'Failed to find ssh.exe, unable to use identity file' + "Failed to find ssh.exe, unable to use identity file" ) - env['GIT_SSH_EXE'] = ssh_exe + env["GIT_SSH_EXE"] = ssh_exe # Use the windows batch file instead of the bourne shell script - ssh_id_wrapper += '.bat' - env['GIT_SSH'] = ssh_id_wrapper + ssh_id_wrapper += ".bat" + env["GIT_SSH"] = ssh_id_wrapper elif not user or _path_is_executable_others(ssh_id_wrapper): - env['GIT_SSH'] = ssh_id_wrapper + env["GIT_SSH"] = ssh_id_wrapper else: tmp_ssh_wrapper = salt.utils.files.mkstemp() salt.utils.files.copyfile(ssh_id_wrapper, tmp_ssh_wrapper) os.chmod(tmp_ssh_wrapper, 0o500) - os.chown(tmp_ssh_wrapper, __salt__['file.user_to_uid'](user), -1) - env['GIT_SSH'] = tmp_ssh_wrapper + os.chown(tmp_ssh_wrapper, __salt__["file.user_to_uid"](user), -1) + env["GIT_SSH"] = tmp_ssh_wrapper - if 'salt-call' not in _salt_cli \ - and __utils__['ssh.key_is_encrypted'](id_file): + if "salt-call" not in _salt_cli and __utils__["ssh.key_is_encrypted"]( + id_file + ): errors.append( - 'Identity file {0} is passphrase-protected and cannot be ' - 'used in a non-interactive command. Using salt-call from ' - 'the minion will allow a passphrase-protected key to be ' - 'used.'.format(id_file) + "Identity file {0} is passphrase-protected and cannot be " + "used in a non-interactive command. Using salt-call from " + "the minion will allow a passphrase-protected key to be " + "used.".format(id_file) ) continue - log.info( - 'Attempting git authentication using identity file %s', - id_file - ) + log.info("Attempting git authentication using identity file %s", id_file) try: - result = __salt__['cmd.run_all']( + result = __salt__["cmd.run_all"]( command, cwd=cwd, runas=user, @@ -316,63 +328,63 @@ def _git_run(command, cwd=None, user=None, password=None, identity=None, ignore_retcode=ignore_retcode, redirect_stderr=redirect_stderr, output_encoding=output_encoding, - **kwargs) + **kwargs + ) finally: if tmp_ssh_wrapper: # Cleanup the temporary ssh wrapper file try: - __salt__['file.remove'](tmp_ssh_wrapper) - log.debug('Removed ssh wrapper file %s', tmp_ssh_wrapper) + __salt__["file.remove"](tmp_ssh_wrapper) + log.debug("Removed ssh wrapper file %s", tmp_ssh_wrapper) except AttributeError: # No wrapper was used pass except (SaltInvocationError, CommandExecutionError) as exc: log.warning( - 'Failed to remove ssh wrapper file %s: %s', - tmp_ssh_wrapper, exc + "Failed to remove ssh wrapper file %s: %s", + tmp_ssh_wrapper, + exc, ) if tmp_identity_file: # Cleanup the temporary identity file try: - __salt__['file.remove'](tmp_identity_file) - log.debug('Removed identity file %s', tmp_identity_file) + __salt__["file.remove"](tmp_identity_file) + log.debug("Removed identity file %s", tmp_identity_file) except AttributeError: # No identify file was used pass except (SaltInvocationError, CommandExecutionError) as exc: log.warning( - 'Failed to remove identity file %s: %s', - tmp_identity_file, exc + "Failed to remove identity file %s: %s", + tmp_identity_file, + exc, ) # If the command was successful, no need to try additional IDs - if result['retcode'] == 0: + if result["retcode"] == 0: return result else: - err = result['stdout' if redirect_stderr else 'stderr'] + err = result["stdout" if redirect_stderr else "stderr"] if err: errors.append(salt.utils.url.redact_http_basic_auth(err)) # We've tried all IDs and still haven't passed, so error out if failhard: - msg = ( - 'Unable to authenticate using identity file:\n\n{0}'.format( - '\n'.join(errors) - ) + msg = "Unable to authenticate using identity file:\n\n{0}".format( + "\n".join(errors) ) if missing_keys: if errors: - msg += '\n\n' - msg += ( - 'The following identity file(s) were not found: {0}' - .format(', '.join(missing_keys)) + msg += "\n\n" + msg += "The following identity file(s) were not found: {0}".format( + ", ".join(missing_keys) ) raise CommandExecutionError(msg) return result else: - result = __salt__['cmd.run_all']( + result = __salt__["cmd.run_all"]( command, cwd=cwd, runas=user, @@ -383,85 +395,89 @@ def _git_run(command, cwd=None, user=None, password=None, identity=None, ignore_retcode=ignore_retcode, redirect_stderr=redirect_stderr, output_encoding=output_encoding, - **kwargs) + **kwargs + ) - if result['retcode'] == 0: + if result["retcode"] == 0: return result else: if failhard: - gitcommand = ' '.join(command) \ - if isinstance(command, list) \ - else command - msg = 'Command \'{0}\' failed'.format( + gitcommand = " ".join(command) if isinstance(command, list) else command + msg = "Command '{0}' failed".format( salt.utils.url.redact_http_basic_auth(gitcommand) ) - err = result['stdout' if redirect_stderr else 'stderr'] + err = result["stdout" if redirect_stderr else "stderr"] if err: - msg += ': {0}'.format( - salt.utils.url.redact_http_basic_auth(err) - ) + msg += ": {0}".format(salt.utils.url.redact_http_basic_auth(err)) raise CommandExecutionError(msg) return result def _get_toplevel(path, user=None, password=None, output_encoding=None): - ''' + """ Use git rev-parse to return the top level of a repo - ''' + """ return _git_run( - ['git', 'rev-parse', '--show-toplevel'], + ["git", "rev-parse", "--show-toplevel"], cwd=path, user=user, password=password, - output_encoding=output_encoding)['stdout'] + output_encoding=output_encoding, + )["stdout"] def _git_config(cwd, user, password, output_encoding=None): - ''' + """ Helper to retrieve git config options - ''' - contextkey = 'git.config.' + cwd + """ + contextkey = "git.config." + cwd if contextkey not in __context__: - git_dir = rev_parse(cwd, - opts=['--git-dir'], - user=user, - password=password, - ignore_retcode=True, - output_encoding=output_encoding) + git_dir = rev_parse( + cwd, + opts=["--git-dir"], + user=user, + password=password, + ignore_retcode=True, + output_encoding=output_encoding, + ) if not os.path.isabs(git_dir): - paths = (cwd, git_dir, 'config') + paths = (cwd, git_dir, "config") else: - paths = (git_dir, 'config') + paths = (git_dir, "config") __context__[contextkey] = os.path.join(*paths) return __context__[contextkey] def _which_git_config(global_, cwd, user, password, output_encoding=None): - ''' + """ Based on whether global or local config is desired, return a list of CLI args to include in the git config command. - ''' + """ if global_: - return ['--global'] + return ["--global"] version_ = _LooseVersion(version(versioninfo=False)) - if version_ >= _LooseVersion('1.7.10.2'): + if version_ >= _LooseVersion("1.7.10.2"): # --local added in 1.7.10.2 - return ['--local'] + return ["--local"] else: # For earlier versions, need to specify the path to the git config file - return ['--file', _git_config(cwd, user, password, - output_encoding=output_encoding)] + return [ + "--file", + _git_config(cwd, user, password, output_encoding=output_encoding), + ] -def add(cwd, - filename, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def add( + cwd, + filename, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ .. versionchanged:: 2015.8.0 The ``--verbose`` command line argument is now implied @@ -527,33 +543,35 @@ def add(cwd, salt myminion git.add /path/to/repo foo/bar.py salt myminion git.add /path/to/repo foo/bar.py opts='--dry-run' - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.extend(['add', '--verbose']) - command.extend( - [x for x in _format_opts(opts) if x not in ('-v', '--verbose')] - ) - command.extend(['--', filename]) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + command = ["git"] + _format_git_opts(git_opts) + command.extend(["add", "--verbose"]) + command.extend([x for x in _format_opts(opts) if x not in ("-v", "--verbose")]) + command.extend(["--", filename]) + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def archive(cwd, - output, - rev='HEAD', - prefix=None, - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None, - **kwargs): - ''' +def archive( + cwd, + output, + rev="HEAD", + prefix=None, + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, + **kwargs +): + """ .. versionchanged:: 2015.8.0 Returns ``True`` if successful, raises an error if not. @@ -662,7 +680,7 @@ def archive(cwd, .. code-block:: bash salt myminion git.archive /path/to/repo /path/to/archive.tar - ''' + """ cwd = _expand_path(cwd, user) output = _expand_path(output, user) # Sanitize kwargs and make sure that no invalid ones were passed. This @@ -670,28 +688,30 @@ def archive(cwd, # shadowing the format() global, while also not allowing unwanted arguments # to be passed. kwargs = salt.utils.args.clean_kwargs(**kwargs) - format_ = kwargs.pop('format', None) + format_ = kwargs.pop("format", None) if kwargs: salt.utils.args.invalid_kwargs(kwargs) - command = ['git'] + _format_git_opts(git_opts) - command.append('archive') + command = ["git"] + _format_git_opts(git_opts) + command.append("archive") # If prefix was set to '' then we skip adding the --prefix option, but if # it was not passed (i.e. None) we use the cwd. - if prefix != '': + if prefix != "": if not prefix: prefix = os.path.basename(cwd) + os.sep - command.extend(['--prefix', prefix]) + command.extend(["--prefix", prefix]) if format_: - command.extend(['--format', format_]) - command.extend(['--output', output, rev]) - _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) + command.extend(["--format", format_]) + command.extend(["--output", output, rev]) + _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) # No output (unless --verbose is used, and we don't want all files listed # in the output in case there are thousands), so just return True. If there # was an error in the git command, it will have already raised an exception @@ -699,15 +719,17 @@ def archive(cwd, return True -def branch(cwd, - name=None, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def branch( + cwd, + name=None, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ Interface to `git-branch(1)`_ cwd @@ -783,32 +805,36 @@ def branch(cwd, salt myminion git.branch /path/to/repo mybranch opts='-d' # Rename branch (2015.8.0 and later) salt myminion git.branch /path/to/repo newbranch opts='-m oldbranch' - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('branch') + command = ["git"] + _format_git_opts(git_opts) + command.append("branch") command.extend(_format_opts(opts)) if name is not None: command.append(name) - _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) + _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) return True -def checkout(cwd, - rev=None, - force=False, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def checkout( + cwd, + rev=None, + force=False, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ Interface to `git-checkout(1)`_ cwd @@ -884,46 +910,50 @@ def checkout(cwd, salt myminion git.checkout /path/to/repo upstream/master opts='-b newbranch' # Checking out current revision into new branch (2015.8.0 and later) salt myminion git.checkout /path/to/repo opts='-b newbranch' - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('checkout') + command = ["git"] + _format_git_opts(git_opts) + command.append("checkout") if force: - command.append('--force') + command.append("--force") opts = _format_opts(opts) command.extend(opts) - checkout_branch = any(x in opts for x in ('-b', '-B')) + checkout_branch = any(x in opts for x in ("-b", "-B")) if rev is None: if not checkout_branch: raise SaltInvocationError( - '\'rev\' argument is required unless -b or -B in opts' + "'rev' argument is required unless -b or -B in opts" ) else: command.append(rev) # Checkout message goes to stderr - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - redirect_stderr=True, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + redirect_stderr=True, + output_encoding=output_encoding, + )["stdout"] -def clone(cwd, - url=None, # Remove default value once 'repository' arg is removed - name=None, - opts='', - git_opts='', - user=None, - password=None, - identity=None, - https_user=None, - https_pass=None, - ignore_retcode=False, - saltenv='base', - output_encoding=None): - ''' +def clone( + cwd, + url=None, # Remove default value once 'repository' arg is removed + name=None, + opts="", + git_opts="", + user=None, + password=None, + identity=None, + https_user=None, + https_pass=None, + ignore_retcode=False, + saltenv="base", + output_encoding=None, +): + """ Interface to `git-clone(1)`_ cwd @@ -1031,24 +1061,23 @@ def clone(cwd, .. code-block:: bash salt myminion git.clone /path/to/repo_parent_dir git://github.com/saltstack/salt.git - ''' + """ cwd = _expand_path(cwd, user) if not url: - raise SaltInvocationError('Missing \'url\' argument') + raise SaltInvocationError("Missing 'url' argument") try: - url = salt.utils.url.add_http_basic_auth(url, - https_user, - https_pass, - https_only=True) + url = salt.utils.url.add_http_basic_auth( + url, https_user, https_pass, https_only=True + ) except ValueError as exc: raise SaltInvocationError(exc.__str__()) - command = ['git'] + _format_git_opts(git_opts) - command.append('clone') + command = ["git"] + _format_git_opts(git_opts) + command.append("clone") command.extend(_format_opts(opts)) - command.extend(['--', url]) + command.extend(["--", url]) if name is not None: command.append(name) if not os.path.exists(cwd): @@ -1062,28 +1091,32 @@ def clone(cwd, # https://github.com/saltstack/salt/issues/15519#issuecomment-128531310 # On Windows, just fall back to None (runs git clone command using the # home directory as the cwd). - clone_cwd = '/tmp' if not salt.utils.platform.is_windows() else None - _git_run(command, - cwd=clone_cwd, - user=user, - password=password, - identity=identity, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - output_encoding=output_encoding) + clone_cwd = "/tmp" if not salt.utils.platform.is_windows() else None + _git_run( + command, + cwd=clone_cwd, + user=user, + password=password, + identity=identity, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + output_encoding=output_encoding, + ) return True -def commit(cwd, - message, - opts='', - git_opts='', - user=None, - password=None, - filename=None, - ignore_retcode=False, - output_encoding=None): - ''' +def commit( + cwd, + message, + opts="", + git_opts="", + user=None, + password=None, + filename=None, + ignore_retcode=False, + output_encoding=None, +): + """ Interface to `git-commit(1)`_ cwd @@ -1161,31 +1194,35 @@ def commit(cwd, salt myminion git.commit /path/to/repo 'The commit message' salt myminion git.commit /path/to/repo 'The commit message' filename=foo/bar.py - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.extend(['commit', '-m', message]) + command = ["git"] + _format_git_opts(git_opts) + command.extend(["commit", "-m", message]) command.extend(_format_opts(opts)) if filename: # Add the '--' to terminate CLI args, but only if it wasn't already # passed in opts string. - command.extend(['--', filename]) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + command.extend(["--", filename]) + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def config_get(key, - cwd=None, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None, - **kwargs): - ''' +def config_get( + key, + cwd=None, + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, + **kwargs +): + """ Get the value of a key in the git configuration file key @@ -1247,25 +1284,27 @@ def config_get(key, salt myminion git.config_get user.name cwd=/path/to/repo salt myminion git.config_get user.email global=True salt myminion git.config_get core.gitproxy cwd=/path/to/repo all=True - ''' + """ # Sanitize kwargs and make sure that no invalid ones were passed. This # allows us to accept 'all' as an argument to this function without # shadowing all(), while also not allowing unwanted arguments to be passed. - all_ = kwargs.pop('all', False) + all_ = kwargs.pop("all", False) - result = _config_getter('--get-all', - key, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding, - **kwargs) + result = _config_getter( + "--get-all", + key, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + **kwargs + ) # git config --get exits with retcode of 1 when key does not exist - if result['retcode'] == 1: + if result["retcode"] == 1: return None - ret = result['stdout'].splitlines() + ret = result["stdout"].splitlines() if all_: return ret else: @@ -1273,18 +1312,20 @@ def config_get(key, return ret[-1] except IndexError: # Should never happen but I'm paranoid and don't like tracebacks - return '' + return "" -def config_get_regexp(key, - value_regex=None, - cwd=None, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None, - **kwargs): - r''' +def config_get_regexp( + key, + value_regex=None, + cwd=None, + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, + **kwargs +): + r""" .. versionadded:: 2015.8.0 Get the value of a key or keys in the git configuration file using regexes @@ -1349,22 +1390,24 @@ def config_get_regexp(key, salt myminion git.config_get_regexp /path/to/repo foo.bar 'baz.*' # Matches any key starting with 'user.' salt myminion git.config_get_regexp '^user\.' global=True - ''' - result = _config_getter('--get-regexp', - key, - value_regex=value_regex, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding, - **kwargs) + """ + result = _config_getter( + "--get-regexp", + key, + value_regex=value_regex, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + **kwargs + ) # git config --get exits with retcode of 1 when key does not exist ret = {} - if result['retcode'] == 1: + if result["retcode"] == 1: return ret - for line in result['stdout'].splitlines(): + for line in result["stdout"].splitlines(): try: param, value = line.split(None, 1) except ValueError: @@ -1373,19 +1416,23 @@ def config_get_regexp(key, return ret -config_get_regex = salt.utils.functools.alias_function(config_get_regexp, 'config_get_regex') +config_get_regex = salt.utils.functools.alias_function( + config_get_regexp, "config_get_regex" +) -def config_set(key, - value=None, - multivar=None, - cwd=None, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None, - **kwargs): - ''' +def config_set( + key, + value=None, + multivar=None, + cwd=None, + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, + **kwargs +): + """ .. versionchanged:: 2015.8.0 Return the value(s) of the key being set @@ -1459,32 +1506,28 @@ def config_set(key, salt myminion git.config_set user.email me@example.com cwd=/path/to/repo salt myminion git.config_set user.email foo@bar.com global=True - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - add_ = kwargs.pop('add', False) - global_ = kwargs.pop('global', False) + add_ = kwargs.pop("add", False) + global_ = kwargs.pop("global", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if cwd is None: if not global_: - raise SaltInvocationError( - '\'cwd\' argument required unless global=True' - ) + raise SaltInvocationError("'cwd' argument required unless global=True") else: cwd = _expand_path(cwd, user) if all(x is not None for x in (value, multivar)): - raise SaltInvocationError( - 'Only one of \'value\' and \'multivar\' is permitted' - ) + raise SaltInvocationError("Only one of 'value' and 'multivar' is permitted") if multivar is not None: if not isinstance(multivar, list): try: - multivar = multivar.split(',') + multivar = multivar.split(",") except AttributeError: - multivar = six.text_type(multivar).split(',') + multivar = six.text_type(multivar).split(",") else: new_multivar = [] for item in salt.utils.data.decode(multivar): @@ -1494,55 +1537,63 @@ def config_set(key, new_multivar.append(six.text_type(item)) multivar = new_multivar - command_prefix = ['git', 'config'] + command_prefix = ["git", "config"] if global_: - command_prefix.append('--global') + command_prefix.append("--global") if value is not None: command = copy.copy(command_prefix) if add_: - command.append('--add') + command.append("--add") else: - command.append('--replace-all') + command.append("--replace-all") command.extend([key, value]) - _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) + _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) else: for idx, target in enumerate(multivar): command = copy.copy(command_prefix) if idx == 0: - command.append('--replace-all') + command.append("--replace-all") else: - command.append('--add') + command.append("--add") command.extend([key, target]) - _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) - return config_get(key, - user=user, - password=password, - cwd=cwd, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding, - **{'all': True, 'global': global_}) + _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) + return config_get( + key, + user=user, + password=password, + cwd=cwd, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + **{"all": True, "global": global_} + ) -def config_unset(key, - value_regex=None, - cwd=None, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None, - **kwargs): - ''' +def config_unset( + key, + value_regex=None, + cwd=None, + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, + **kwargs +): + """ .. versionadded:: 2015.8.0 Unset a key in the git configuration file @@ -1598,77 +1649,78 @@ def config_unset(key, salt myminion git.config_unset /path/to/repo foo.bar salt myminion git.config_unset /path/to/repo foo.bar all=True - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - all_ = kwargs.pop('all', False) - global_ = kwargs.pop('global', False) + all_ = kwargs.pop("all", False) + global_ = kwargs.pop("global", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if cwd is None: if not global_: - raise SaltInvocationError( - '\'cwd\' argument required unless global=True' - ) + raise SaltInvocationError("'cwd' argument required unless global=True") else: cwd = _expand_path(cwd, user) - command = ['git', 'config'] + command = ["git", "config"] if all_: - command.append('--unset-all') + command.append("--unset-all") else: - command.append('--unset') - command.extend(_which_git_config(global_, cwd, user, password, - output_encoding=output_encoding)) + command.append("--unset") + command.extend( + _which_git_config(global_, cwd, user, password, output_encoding=output_encoding) + ) command.append(key) if value_regex is not None: command.append(value_regex) - ret = _git_run(command, - cwd=cwd if cwd != 'global' else None, - user=user, - password=password, - ignore_retcode=ignore_retcode, - failhard=False, - output_encoding=output_encoding) - retcode = ret['retcode'] + ret = _git_run( + command, + cwd=cwd if cwd != "global" else None, + user=user, + password=password, + ignore_retcode=ignore_retcode, + failhard=False, + output_encoding=output_encoding, + ) + retcode = ret["retcode"] if retcode == 0: return True elif retcode == 1: - raise CommandExecutionError('Section or key is invalid') + raise CommandExecutionError("Section or key is invalid") elif retcode == 5: - if config_get(key, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) is None: - raise CommandExecutionError( - 'Key \'{0}\' does not exist'.format(key) + if ( + config_get( + key, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, ) + is None + ): + raise CommandExecutionError("Key '{0}' does not exist".format(key)) else: - msg = 'Multiple values exist for key \'{0}\''.format(key) + msg = "Multiple values exist for key '{0}'".format(key) if value_regex is not None: - msg += ' and value_regex matches multiple values' + msg += " and value_regex matches multiple values" raise CommandExecutionError(msg) elif retcode == 6: - raise CommandExecutionError('The value_regex is invalid') + raise CommandExecutionError("The value_regex is invalid") else: - msg = ( - 'Failed to unset key \'{0}\', git config returned exit code {1}' - .format(key, retcode) + msg = "Failed to unset key '{0}', git config returned exit code {1}".format( + key, retcode ) - if ret['stderr']: - msg += '; ' + ret['stderr'] + if ret["stderr"]: + msg += "; " + ret["stderr"] raise CommandExecutionError(msg) -def current_branch(cwd, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def current_branch( + cwd, user=None, password=None, ignore_retcode=False, output_encoding=None +): + """ Returns the current branch name of a local checkout. If HEAD is detached, return the SHA1 of the revision which is currently checked out. @@ -1708,24 +1760,28 @@ def current_branch(cwd, .. code-block:: bash salt myminion git.current_branch /path/to/repo - ''' + """ cwd = _expand_path(cwd, user) - command = ['git', 'rev-parse', '--abbrev-ref', 'HEAD'] - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + command = ["git", "rev-parse", "--abbrev-ref", "HEAD"] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def describe(cwd, - rev='HEAD', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def describe( + cwd, + rev="HEAD", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ Returns the `git-describe(1)`_ string (or the SHA1 hash if there are no tags) for the given revision. @@ -1771,32 +1827,36 @@ def describe(cwd, salt myminion git.describe /path/to/repo salt myminion git.describe /path/to/repo develop - ''' + """ cwd = _expand_path(cwd, user) - command = ['git', 'describe'] - if _LooseVersion(version(versioninfo=False)) >= _LooseVersion('1.5.6'): - command.append('--always') + command = ["git", "describe"] + if _LooseVersion(version(versioninfo=False)) >= _LooseVersion("1.5.6"): + command.append("--always") command.append(rev) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def diff(cwd, - item1=None, - item2=None, - opts='', - git_opts='', - user=None, - password=None, - no_index=False, - cached=False, - paths=None, - output_encoding=None): - ''' +def diff( + cwd, + item1=None, + item2=None, + opts="", + git_opts="", + user=None, + password=None, + no_index=False, + cached=False, + paths=None, + output_encoding=None, +): + """ .. versionadded:: 2015.8.12,2016.3.3,2016.11.0 Interface to `git-diff(1)`_ @@ -1895,48 +1955,49 @@ def diff(cwd, salt myminion git.diff /path/to/repo abcdef1 aabbccd paths=path/to/file1,path/to/file2 # Diff two files with one being outside the working tree salt myminion git.diff /path/to/repo no_index=True paths=path/to/file1,/absolute/path/to/file2 - ''' + """ if no_index and cached: raise CommandExecutionError( - 'The \'no_index\' and \'cached\' options cannot be used together' + "The 'no_index' and 'cached' options cannot be used together" ) - command = ['git'] + _format_git_opts(git_opts) - command.append('diff') + command = ["git"] + _format_git_opts(git_opts) + command.append("diff") command.extend(_format_opts(opts)) if paths is not None and not isinstance(paths, (list, tuple)): try: - paths = paths.split(',') + paths = paths.split(",") except AttributeError: - paths = six.text_type(paths).split(',') + paths = six.text_type(paths).split(",") ignore_retcode = False failhard = True if no_index: - if _LooseVersion(version(versioninfo=False)) < _LooseVersion('1.5.1'): + if _LooseVersion(version(versioninfo=False)) < _LooseVersion("1.5.1"): raise CommandExecutionError( - 'The \'no_index\' option is only supported in Git 1.5.1 and ' - 'newer' + "The 'no_index' option is only supported in Git 1.5.1 and " "newer" ) ignore_retcode = True failhard = False - command.append('--no-index') + command.append("--no-index") for value in [x for x in (item1, item2) if x]: log.warning( - 'Revision \'%s\' ignored in git diff, as revisions cannot be ' - 'used when no_index=True', value + "Revision '%s' ignored in git diff, as revisions cannot be " + "used when no_index=True", + value, ) elif cached: - command.append('--cached') + command.append("--cached") if item1: command.append(item1) if item2: log.warning( - 'Second revision \'%s\' ignored in git diff, at most one ' - 'revision is considered when cached=True', item2 + "Second revision '%s' ignored in git diff, at most one " + "revision is considered when cached=True", + item2, ) else: @@ -1944,26 +2005,25 @@ def diff(cwd, command.append(value) if paths: - command.append('--') + command.append("--") command.extend(paths) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - failhard=failhard, - redirect_stderr=True, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + failhard=failhard, + redirect_stderr=True, + output_encoding=output_encoding, + )["stdout"] -def discard_local_changes(cwd, - path='.', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def discard_local_changes( + cwd, path=".", user=None, password=None, ignore_retcode=False, output_encoding=None +): + """ .. versionadded:: 2019.2.0 Runs a ``git checkout -- `` from the directory specified by ``cwd``. @@ -2002,32 +2062,36 @@ def discard_local_changes(cwd, salt myminion git.discard_local_changes /path/to/repo salt myminion git.discard_local_changes /path/to/repo path=foo - ''' + """ cwd = _expand_path(cwd, user) - command = ['git', 'checkout', '--', path] + command = ["git", "checkout", "--", path] # Checkout message goes to stderr - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - redirect_stderr=True, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + redirect_stderr=True, + output_encoding=output_encoding, + )["stdout"] -def fetch(cwd, - remote=None, - force=False, - refspecs=None, - opts='', - git_opts='', - user=None, - password=None, - identity=None, - ignore_retcode=False, - saltenv='base', - output_encoding=None): - ''' +def fetch( + cwd, + remote=None, + force=False, + refspecs=None, + opts="", + git_opts="", + user=None, + password=None, + identity=None, + ignore_retcode=False, + saltenv="base", + output_encoding=None, +): + """ .. versionchanged:: 2015.8.2 Return data is now a dictionary containing information on branches and tags that were added/updated @@ -2136,74 +2200,75 @@ def fetch(cwd, salt myminion git.fetch /path/to/repo upstream salt myminion git.fetch /path/to/repo identity=/root/.ssh/id_rsa - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('fetch') + command = ["git"] + _format_git_opts(git_opts) + command.append("fetch") if force: - command.append('--force') - command.extend( - [x for x in _format_opts(opts) if x not in ('-f', '--force')] - ) + command.append("--force") + command.extend([x for x in _format_opts(opts) if x not in ("-f", "--force")]) if remote: command.append(remote) if refspecs is not None: if not isinstance(refspecs, (list, tuple)): try: - refspecs = refspecs.split(',') + refspecs = refspecs.split(",") except AttributeError: - refspecs = six.text_type(refspecs).split(',') + refspecs = six.text_type(refspecs).split(",") refspecs = salt.utils.data.stringify(refspecs) command.extend(refspecs) - output = _git_run(command, - cwd=cwd, - user=user, - password=password, - identity=identity, - ignore_retcode=ignore_retcode, - redirect_stderr=True, - saltenv=saltenv, - output_encoding=output_encoding)['stdout'] + output = _git_run( + command, + cwd=cwd, + user=user, + password=password, + identity=identity, + ignore_retcode=ignore_retcode, + redirect_stderr=True, + saltenv=saltenv, + output_encoding=output_encoding, + )["stdout"] update_re = re.compile( - r'[\s*]*(?:([0-9a-f]+)\.\.([0-9a-f]+)|' - r'\[(?:new (tag|branch)|tag update)\])\s+(.+)->' + r"[\s*]*(?:([0-9a-f]+)\.\.([0-9a-f]+)|" + r"\[(?:new (tag|branch)|tag update)\])\s+(.+)->" ) ret = {} - for line in salt.utils.itertools.split(output, '\n'): + for line in salt.utils.itertools.split(output, "\n"): match = update_re.match(line) if match: - old_sha, new_sha, new_ref_type, ref_name = \ - match.groups() + old_sha, new_sha, new_ref_type, ref_name = match.groups() ref_name = ref_name.rstrip() if new_ref_type is not None: # ref is a new tag/branch - ref_key = 'new tags' \ - if new_ref_type == 'tag' \ - else 'new branches' + ref_key = "new tags" if new_ref_type == "tag" else "new branches" ret.setdefault(ref_key, []).append(ref_name) elif old_sha is not None: # ref is a branch update - ret.setdefault('updated branches', {})[ref_name] = \ - {'old': old_sha, 'new': new_sha} + ret.setdefault("updated branches", {})[ref_name] = { + "old": old_sha, + "new": new_sha, + } else: # ref is an updated tag - ret.setdefault('updated tags', []).append(ref_name) + ret.setdefault("updated tags", []).append(ref_name) return ret -def init(cwd, - bare=False, - template=None, - separate_git_dir=None, - shared=None, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def init( + cwd, + bare=False, + template=None, + separate_git_dir=None, + shared=None, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ Interface to `git-init(1)`_ cwd @@ -2289,39 +2354,37 @@ def init(cwd, salt myminion git.init /path/to/bare/repo.git opts='--bare' # Init a bare repo (2015.8.0 and later) salt myminion git.init /path/to/bare/repo.git bare=True - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('init') + command = ["git"] + _format_git_opts(git_opts) + command.append("init") if bare: - command.append('--bare') + command.append("--bare") if template is not None: - command.append('--template={0}'.format(template)) + command.append("--template={0}".format(template)) if separate_git_dir is not None: - command.append('--separate-git-dir={0}'.format(separate_git_dir)) + command.append("--separate-git-dir={0}".format(separate_git_dir)) if shared is not None: - if isinstance(shared, six.integer_types) \ - and not isinstance(shared, bool): - shared = '0' + six.text_type(shared) + if isinstance(shared, six.integer_types) and not isinstance(shared, bool): + shared = "0" + six.text_type(shared) elif not isinstance(shared, six.string_types): # Using lower here because booleans would be capitalized when # converted to a string. shared = six.text_type(shared).lower() - command.append('--shared={0}'.format(shared)) + command.append("--shared={0}".format(shared)) command.extend(_format_opts(opts)) command.append(cwd) - return _git_run(command, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def is_worktree(cwd, - user=None, - password=None, - output_encoding=None): - ''' +def is_worktree(cwd, user=None, password=None, output_encoding=None): + """ .. versionadded:: 2015.8.0 This function will attempt to determine if ``cwd`` is part of a @@ -2358,16 +2421,17 @@ def is_worktree(cwd, .. code-block:: bash salt myminion git.is_worktree /path/to/repo - ''' + """ cwd = _expand_path(cwd, user) try: - toplevel = _get_toplevel(cwd, user=user, password=password, - output_encoding=output_encoding) + toplevel = _get_toplevel( + cwd, user=user, password=password, output_encoding=output_encoding + ) except CommandExecutionError: return False - gitdir = os.path.join(toplevel, '.git') + gitdir = os.path.join(toplevel, ".git") try: - with salt.utils.files.fopen(gitdir, 'r') as fp_: + with salt.utils.files.fopen(gitdir, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) try: @@ -2380,7 +2444,7 @@ def is_worktree(cwd, # binary file, so that we do not read the entire file into # memory at once. We'll hit a return statement before this # loop enters a second iteration. - if label == 'gitdir:' and os.path.isabs(path): + if label == "gitdir:" and os.path.isabs(path): return True else: return False @@ -2389,13 +2453,15 @@ def is_worktree(cwd, return False -def list_branches(cwd, - remote=False, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def list_branches( + cwd, + remote=False, + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ .. versionadded:: 2015.8.0 Return a list of branches @@ -2447,24 +2513,29 @@ def list_branches(cwd, salt myminion git.list_branches /path/to/repo salt myminion git.list_branches /path/to/repo remote=True - ''' + """ cwd = _expand_path(cwd, user) - command = ['git', 'for-each-ref', '--format', '%(refname:short)', - 'refs/{0}/'.format('heads' if not remote else 'remotes')] - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'].splitlines() + command = [ + "git", + "for-each-ref", + "--format", + "%(refname:short)", + "refs/{0}/".format("heads" if not remote else "remotes"), + ] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"].splitlines() -def list_tags(cwd, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def list_tags( + cwd, user=None, password=None, ignore_retcode=False, output_encoding=None +): + """ .. versionadded:: 2015.8.0 Return a list of tags @@ -2505,25 +2576,23 @@ def list_tags(cwd, .. code-block:: bash salt myminion git.list_tags /path/to/repo - ''' + """ cwd = _expand_path(cwd, user) - command = ['git', 'for-each-ref', '--format', '%(refname:short)', - 'refs/tags/'] - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'].splitlines() + command = ["git", "for-each-ref", "--format", "%(refname:short)", "refs/tags/"] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"].splitlines() -def list_worktrees(cwd, - stale=False, - user=None, - password=None, - output_encoding=None, - **kwargs): - ''' +def list_worktrees( + cwd, stale=False, user=None, password=None, output_encoding=None, **kwargs +): + """ .. versionadded:: 2015.8.0 Returns information on worktrees @@ -2585,36 +2654,35 @@ def list_worktrees(cwd, salt myminion git.list_worktrees /path/to/repo salt myminion git.list_worktrees /path/to/repo all=True salt myminion git.list_worktrees /path/to/repo stale=True - ''' + """ if not _check_worktree_support(failhard=True): return {} cwd = _expand_path(cwd, user) kwargs = salt.utils.args.clean_kwargs(**kwargs) - all_ = kwargs.pop('all', False) + all_ = kwargs.pop("all", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if all_ and stale: - raise CommandExecutionError( - '\'all\' and \'stale\' cannot both be set to True' - ) + raise CommandExecutionError("'all' and 'stale' cannot both be set to True") - def _git_tag_points_at(cwd, rev, user=None, password=None, - output_encoding=None): - ''' + def _git_tag_points_at(cwd, rev, user=None, password=None, output_encoding=None): + """ Get any tags that point at a - ''' - return _git_run(['git', 'tag', '--points-at', rev], - cwd=cwd, - user=user, - password=password, - output_encoding=output_encoding)['stdout'].splitlines() + """ + return _git_run( + ["git", "tag", "--points-at", rev], + cwd=cwd, + user=user, + password=password, + output_encoding=output_encoding, + )["stdout"].splitlines() def _desired(is_stale, all_, stale): - ''' + """ Common logic to determine whether or not to include the worktree info in the return data. - ''' + """ if is_stale: if not all_ and not stale: # Stale worktrees are not desired, skip this one @@ -2626,51 +2694,54 @@ def list_worktrees(cwd, return True def _duplicate_worktree_path(path): - ''' + """ Log errors to the minion log notifying of duplicate worktree paths. These should not be there, but may show up due to a bug in git 2.7.0. - ''' + """ log.error( - 'git.worktree: Duplicate worktree path %s. This may be caused by ' - 'a known issue in git 2.7.0 (see ' - 'http://permalink.gmane.org/gmane.comp.version-control.git/283998)', - path + "git.worktree: Duplicate worktree path %s. This may be caused by " + "a known issue in git 2.7.0 (see " + "http://permalink.gmane.org/gmane.comp.version-control.git/283998)", + path, ) - tracked_data_points = ('worktree', 'HEAD', 'branch') + tracked_data_points = ("worktree", "HEAD", "branch") ret = {} git_version = _LooseVersion(version(versioninfo=False)) - has_native_list_subcommand = git_version >= _LooseVersion('2.7.0') + has_native_list_subcommand = git_version >= _LooseVersion("2.7.0") if has_native_list_subcommand: - out = _git_run(['git', 'worktree', 'list', '--porcelain'], - cwd=cwd, - user=user, - password=password, - output_encoding=output_encoding) - if out['retcode'] != 0: - msg = 'Failed to list worktrees' - if out['stderr']: - msg += ': {0}'.format(out['stderr']) + out = _git_run( + ["git", "worktree", "list", "--porcelain"], + cwd=cwd, + user=user, + password=password, + output_encoding=output_encoding, + ) + if out["retcode"] != 0: + msg = "Failed to list worktrees" + if out["stderr"]: + msg += ": {0}".format(out["stderr"]) raise CommandExecutionError(msg) def _untracked_item(line): - ''' + """ Log a warning - ''' - log.warning('git.worktree: Untracked line item \'%s\'', line) + """ + log.warning("git.worktree: Untracked line item '%s'", line) - for individual_worktree in \ - salt.utils.itertools.split(out['stdout'].strip(), '\n\n'): + for individual_worktree in salt.utils.itertools.split( + out["stdout"].strip(), "\n\n" + ): # Initialize the dict where we're storing the tracked data points - worktree_data = dict([(x, '') for x in tracked_data_points]) + worktree_data = dict([(x, "") for x in tracked_data_points]) - for line in salt.utils.itertools.split(individual_worktree, '\n'): + for line in salt.utils.itertools.split(individual_worktree, "\n"): try: type_, value = line.strip().split(None, 1) except ValueError: - if line == 'detached': - type_ = 'branch' - value = 'detached' + if line == "detached": + type_ = "branch" + value = "detached" else: _untracked_item(line) continue @@ -2681,8 +2752,9 @@ def list_worktrees(cwd, if worktree_data[type_]: log.error( - 'git.worktree: Unexpected duplicate %s entry ' - '\'%s\', skipping', type_, line + "git.worktree: Unexpected duplicate %s entry " "'%s', skipping", + type_, + line, ) continue @@ -2692,66 +2764,71 @@ def list_worktrees(cwd, missing = [x for x in tracked_data_points if not worktree_data[x]] if missing: log.error( - 'git.worktree: Incomplete worktree data, missing the ' - 'following information: %s. Full data below:\n%s', - ', '.join(missing), individual_worktree + "git.worktree: Incomplete worktree data, missing the " + "following information: %s. Full data below:\n%s", + ", ".join(missing), + individual_worktree, ) continue - worktree_is_stale = not os.path.isdir(worktree_data['worktree']) + worktree_is_stale = not os.path.isdir(worktree_data["worktree"]) if not _desired(worktree_is_stale, all_, stale): continue - if worktree_data['worktree'] in ret: - _duplicate_worktree_path(worktree_data['worktree']) + if worktree_data["worktree"] in ret: + _duplicate_worktree_path(worktree_data["worktree"]) - wt_ptr = ret.setdefault(worktree_data['worktree'], {}) - wt_ptr['stale'] = worktree_is_stale - wt_ptr['HEAD'] = worktree_data['HEAD'] - wt_ptr['detached'] = worktree_data['branch'] == 'detached' - if wt_ptr['detached']: - wt_ptr['branch'] = None + wt_ptr = ret.setdefault(worktree_data["worktree"], {}) + wt_ptr["stale"] = worktree_is_stale + wt_ptr["HEAD"] = worktree_data["HEAD"] + wt_ptr["detached"] = worktree_data["branch"] == "detached" + if wt_ptr["detached"]: + wt_ptr["branch"] = None # Check to see if HEAD points at a tag - tags_found = _git_tag_points_at(cwd, - wt_ptr['HEAD'], - user=user, - password=password, - output_encoding=output_encoding) + tags_found = _git_tag_points_at( + cwd, + wt_ptr["HEAD"], + user=user, + password=password, + output_encoding=output_encoding, + ) if tags_found: - wt_ptr['tags'] = tags_found + wt_ptr["tags"] = tags_found else: - wt_ptr['branch'] = \ - worktree_data['branch'].replace('refs/heads/', '', 1) + wt_ptr["branch"] = worktree_data["branch"].replace("refs/heads/", "", 1) return ret else: - toplevel = _get_toplevel(cwd, user=user, password=password, - output_encoding=output_encoding) + toplevel = _get_toplevel( + cwd, user=user, password=password, output_encoding=output_encoding + ) try: - worktree_root = rev_parse(cwd, - opts=['--git-path', 'worktrees'], - user=user, - password=password, - output_encoding=output_encoding) + worktree_root = rev_parse( + cwd, + opts=["--git-path", "worktrees"], + user=user, + password=password, + output_encoding=output_encoding, + ) except CommandExecutionError as exc: - msg = 'Failed to find worktree location for ' + cwd + msg = "Failed to find worktree location for " + cwd log.error(msg, exc_info_on_loglevel=logging.DEBUG) raise CommandExecutionError(msg) - if worktree_root.startswith('.git'): + if worktree_root.startswith(".git"): worktree_root = os.path.join(cwd, worktree_root) if not os.path.isdir(worktree_root): raise CommandExecutionError( - 'Worktree admin directory {0} not present'.format(worktree_root) + "Worktree admin directory {0} not present".format(worktree_root) ) def _read_file(path): - ''' + """ Return contents of a single line file with EOF newline stripped - ''' + """ try: - with salt.utils.files.fopen(path, 'r') as fp_: + with salt.utils.files.fopen(path, "r") as fp_: for line in fp_: ret = salt.utils.stringutils.to_unicode(line).strip() # Ignore other lines, if they exist (which they @@ -2764,26 +2841,27 @@ def list_worktrees(cwd, for worktree_name in os.listdir(worktree_root): admin_dir = os.path.join(worktree_root, worktree_name) - gitdir_file = os.path.join(admin_dir, 'gitdir') - head_file = os.path.join(admin_dir, 'HEAD') + gitdir_file = os.path.join(admin_dir, "gitdir") + head_file = os.path.join(admin_dir, "HEAD") wt_loc = _read_file(gitdir_file) head_ref = _read_file(head_file) if not os.path.isabs(wt_loc): log.error( - 'Non-absolute path found in %s. If git 2.7.0 was ' - 'installed and then downgraded, this was likely caused ' - 'by a known issue in git 2.7.0. See ' - 'http://permalink.gmane.org/gmane.comp.version-control' - '.git/283998 for more information.', gitdir_file + "Non-absolute path found in %s. If git 2.7.0 was " + "installed and then downgraded, this was likely caused " + "by a known issue in git 2.7.0. See " + "http://permalink.gmane.org/gmane.comp.version-control" + ".git/283998 for more information.", + gitdir_file, ) # Emulate what 'git worktree list' does under-the-hood, and # that is using the toplevel directory. It will still give # inaccurate results, but will avoid a traceback. wt_loc = toplevel - if wt_loc.endswith('/.git'): + if wt_loc.endswith("/.git"): wt_loc = wt_loc[:-5] worktree_is_stale = not os.path.isdir(wt_loc) @@ -2794,14 +2872,16 @@ def list_worktrees(cwd, if wt_loc in ret: _duplicate_worktree_path(wt_loc) - if head_ref.startswith('ref: '): + if head_ref.startswith("ref: "): head_ref = head_ref.split(None, 1)[-1] - wt_branch = head_ref.replace('refs/heads/', '', 1) - wt_head = rev_parse(cwd, - rev=head_ref, - user=user, - password=password, - output_encoding=output_encoding) + wt_branch = head_ref.replace("refs/heads/", "", 1) + wt_head = rev_parse( + cwd, + rev=head_ref, + user=user, + password=password, + output_encoding=output_encoding, + ) wt_detached = False else: wt_branch = None @@ -2809,38 +2889,42 @@ def list_worktrees(cwd, wt_detached = True wt_ptr = ret.setdefault(wt_loc, {}) - wt_ptr['stale'] = worktree_is_stale - wt_ptr['branch'] = wt_branch - wt_ptr['HEAD'] = wt_head - wt_ptr['detached'] = wt_detached + wt_ptr["stale"] = worktree_is_stale + wt_ptr["branch"] = wt_branch + wt_ptr["HEAD"] = wt_head + wt_ptr["detached"] = wt_detached # Check to see if HEAD points at a tag if wt_detached: - tags_found = _git_tag_points_at(cwd, - wt_head, - user=user, - password=password, - output_encoding=output_encoding) + tags_found = _git_tag_points_at( + cwd, + wt_head, + user=user, + password=password, + output_encoding=output_encoding, + ) if tags_found: - wt_ptr['tags'] = tags_found + wt_ptr["tags"] = tags_found return ret -def ls_remote(cwd=None, - remote='origin', - ref=None, - opts='', - git_opts='', - user=None, - password=None, - identity=None, - https_user=None, - https_pass=None, - ignore_retcode=False, - output_encoding=None, - saltenv='base'): - ''' +def ls_remote( + cwd=None, + remote="origin", + ref=None, + opts="", + git_opts="", + user=None, + password=None, + identity=None, + https_user=None, + https_pass=None, + ignore_retcode=False, + output_encoding=None, + saltenv="base", +): + """ Interface to `git-ls-remote(1)`_. Returns the upstream hash for a remote reference. @@ -2958,30 +3042,31 @@ def ls_remote(cwd=None, salt myminion git.ls_remote /path/to/repo origin master salt myminion git.ls_remote remote=https://mydomain.tld/repo.git ref=mytag opts='--tags' - ''' + """ if cwd is not None: cwd = _expand_path(cwd, user) try: - remote = salt.utils.url.add_http_basic_auth(remote, - https_user, - https_pass, - https_only=True) + remote = salt.utils.url.add_http_basic_auth( + remote, https_user, https_pass, https_only=True + ) except ValueError as exc: raise SaltInvocationError(exc.__str__()) - command = ['git'] + _format_git_opts(git_opts) - command.append('ls-remote') + command = ["git"] + _format_git_opts(git_opts) + command.append("ls-remote") command.extend(_format_opts(opts)) command.append(remote) if ref: command.append(ref) - output = _git_run(command, - cwd=cwd, - user=user, - password=password, - identity=identity, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - output_encoding=output_encoding)['stdout'] + output = _git_run( + command, + cwd=cwd, + user=user, + password=password, + identity=identity, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + output_encoding=output_encoding, + )["stdout"] ret = {} for line in output.splitlines(): try: @@ -2992,17 +3077,19 @@ def ls_remote(cwd=None, return ret -def merge(cwd, - rev=None, - opts='', - git_opts='', - user=None, - password=None, - identity=None, - ignore_retcode=False, - output_encoding=None, - **kwargs): - ''' +def merge( + cwd, + rev=None, + opts="", + git_opts="", + user=None, + password=None, + identity=None, + ignore_retcode=False, + output_encoding=None, + **kwargs +): + """ Interface to `git-merge(1)`_ cwd @@ -3088,41 +3175,45 @@ def merge(cwd, salt myminion git.merge /path/to/repo # .. or merge another rev salt myminion git.merge /path/to/repo rev=upstream/foo - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) if kwargs: salt.utils.args.invalid_kwargs(kwargs) cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('merge') + command = ["git"] + _format_git_opts(git_opts) + command.append("merge") command.extend(_format_opts(opts)) if rev: command.append(rev) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - identity=identity, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + identity=identity, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def merge_base(cwd, - refs=None, - octopus=False, - is_ancestor=False, - independent=False, - fork_point=None, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None, - **kwargs): - ''' +def merge_base( + cwd, + refs=None, + octopus=False, + is_ancestor=False, + independent=False, + fork_point=None, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, + **kwargs +): + """ .. versionadded:: 2015.8.0 Interface to `git-merge-base(1)`_. @@ -3232,105 +3323,115 @@ def merge_base(cwd, salt myminion git.merge_base /path/to/repo refs=8f2e542,4ad8cab is_ancestor=True salt myminion git.merge_base /path/to/repo fork_point=upstream/master salt myminion git.merge_base /path/to/repo refs=mybranch fork_point=upstream/master - ''' + """ cwd = _expand_path(cwd, user) kwargs = salt.utils.args.clean_kwargs(**kwargs) - all_ = kwargs.pop('all', False) + all_ = kwargs.pop("all", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if all_ and (independent or is_ancestor or fork_point): raise SaltInvocationError( - 'The \'all\' argument is not compatible with \'independent\', ' - '\'is_ancestor\', or \'fork_point\'' + "The 'all' argument is not compatible with 'independent', " + "'is_ancestor', or 'fork_point'" ) if refs is None: refs = [] elif not isinstance(refs, (list, tuple)): - refs = [x.strip() for x in six.text_type(refs).split(',')] + refs = [x.strip() for x in six.text_type(refs).split(",")] mutually_exclusive_count = len( [x for x in (octopus, independent, is_ancestor, fork_point) if x] ) if mutually_exclusive_count > 1: raise SaltInvocationError( - 'Only one of \'octopus\', \'independent\', \'is_ancestor\', and ' - '\'fork_point\' is permitted' + "Only one of 'octopus', 'independent', 'is_ancestor', and " + "'fork_point' is permitted" ) elif is_ancestor: if len(refs) != 2: raise SaltInvocationError( - 'Two refs/commits are required if \'is_ancestor\' is True' + "Two refs/commits are required if 'is_ancestor' is True" ) elif fork_point: if len(refs) > 1: raise SaltInvocationError( - 'At most one ref/commit can be passed if \'fork_point\' is ' - 'specified' + "At most one ref/commit can be passed if 'fork_point' is " "specified" ) elif not refs: - refs = ['HEAD'] + refs = ["HEAD"] if is_ancestor: - if _LooseVersion(version(versioninfo=False)) < _LooseVersion('1.8.0'): + if _LooseVersion(version(versioninfo=False)) < _LooseVersion("1.8.0"): # Pre 1.8.0 git doesn't have --is-ancestor, so the logic here is a # little different. First we need to resolve the first ref to a # full SHA1, and then if running git merge-base on both commits # returns an identical commit to the resolved first ref, we know # that the first ref is an ancestor of the second ref. - first_commit = rev_parse(cwd, - rev=refs[0], - opts=['--verify'], - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) - return merge_base(cwd, - refs=refs, - is_ancestor=False, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) == first_commit + first_commit = rev_parse( + cwd, + rev=refs[0], + opts=["--verify"], + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) + return ( + merge_base( + cwd, + refs=refs, + is_ancestor=False, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) + == first_commit + ) - command = ['git'] + _format_git_opts(git_opts) - command.append('merge-base') + command = ["git"] + _format_git_opts(git_opts) + command.append("merge-base") command.extend(_format_opts(opts)) if all_: - command.append('--all') + command.append("--all") if octopus: - command.append('--octopus') + command.append("--octopus") elif is_ancestor: - command.append('--is-ancestor') + command.append("--is-ancestor") elif independent: - command.append('--independent') + command.append("--independent") elif fork_point: - command.extend(['--fork-point', fork_point]) + command.extend(["--fork-point", fork_point]) command.extend(refs) - result = _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - failhard=False if is_ancestor else True, - output_encoding=output_encoding) + result = _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + failhard=False if is_ancestor else True, + output_encoding=output_encoding, + ) if is_ancestor: - return result['retcode'] == 0 - all_bases = result['stdout'].splitlines() + return result["retcode"] == 0 + all_bases = result["stdout"].splitlines() if all_: return all_bases return all_bases[0] -def merge_tree(cwd, - ref1, - ref2, - base=None, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def merge_tree( + cwd, + ref1, + ref2, + base=None, + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ .. versionadded:: 2015.8.0 Interface to `git-merge-tree(1)`_, shows the merge results and conflicts @@ -3384,37 +3485,39 @@ def merge_tree(cwd, salt myminion git.merge_tree /path/to/repo HEAD upstream/dev salt myminion git.merge_tree /path/to/repo HEAD upstream/dev base=aaf3c3d - ''' + """ cwd = _expand_path(cwd, user) - command = ['git', 'merge-tree'] + command = ["git", "merge-tree"] if base is None: try: - base = merge_base(cwd, refs=[ref1, ref2], - output_encoding=output_encoding) + base = merge_base(cwd, refs=[ref1, ref2], output_encoding=output_encoding) except (SaltInvocationError, CommandExecutionError): raise CommandExecutionError( - 'Unable to determine merge base for {0} and {1}' - .format(ref1, ref2) + "Unable to determine merge base for {0} and {1}".format(ref1, ref2) ) command.extend([base, ref1, ref2]) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def pull(cwd, - opts='', - git_opts='', - user=None, - password=None, - identity=None, - ignore_retcode=False, - saltenv='base', - output_encoding=None): - ''' +def pull( + cwd, + opts="", + git_opts="", + user=None, + password=None, + identity=None, + ignore_retcode=False, + saltenv="base", + output_encoding=None, +): + """ Interface to `git-pull(1)`_ cwd @@ -3501,34 +3604,38 @@ def pull(cwd, .. code-block:: bash salt myminion git.pull /path/to/repo opts='--rebase origin master' - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('pull') + command = ["git"] + _format_git_opts(git_opts) + command.append("pull") command.extend(_format_opts(opts)) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - identity=identity, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + identity=identity, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + output_encoding=output_encoding, + )["stdout"] -def push(cwd, - remote=None, - ref=None, - opts='', - git_opts='', - user=None, - password=None, - identity=None, - ignore_retcode=False, - saltenv='base', - output_encoding=None, - **kwargs): - ''' +def push( + cwd, + remote=None, + ref=None, + opts="", + git_opts="", + user=None, + password=None, + identity=None, + ignore_retcode=False, + saltenv="base", + output_encoding=None, + **kwargs +): + """ Interface to `git-push(1)`_ cwd @@ -3633,35 +3740,39 @@ def push(cwd, salt myminion git.push /path/to/repo upstream issue21:develop # Delete remote branch 'upstream/temp' salt myminion git.push /path/to/repo upstream :temp - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) if kwargs: salt.utils.args.invalid_kwargs(kwargs) cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('push') + command = ["git"] + _format_git_opts(git_opts) + command.append("push") command.extend(_format_opts(opts)) command.extend([remote, ref]) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - identity=identity, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + identity=identity, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + output_encoding=output_encoding, + )["stdout"] -def rebase(cwd, - rev='master', - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def rebase( + cwd, + rev="master", + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ Interface to `git-rebase(1)`_ cwd @@ -3726,33 +3837,37 @@ def rebase(cwd, salt myminion git.rebase /path/to/repo master salt myminion git.rebase /path/to/repo 'origin master' salt myminion git.rebase /path/to/repo origin/master opts='--onto newbranch' - ''' + """ cwd = _expand_path(cwd, user) opts = _format_opts(opts) - if any(x for x in opts if x in ('-i', '--interactive')): - raise SaltInvocationError('Interactive rebases are not supported') - command = ['git'] + _format_git_opts(git_opts) - command.append('rebase') + if any(x for x in opts if x in ("-i", "--interactive")): + raise SaltInvocationError("Interactive rebases are not supported") + command = ["git"] + _format_git_opts(git_opts) + command.append("rebase") command.extend(opts) if not isinstance(rev, six.string_types): rev = six.text_type(rev) command.extend(salt.utils.args.shlex_split(rev)) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def remote_get(cwd, - remote='origin', - user=None, - password=None, - redact_auth=True, - ignore_retcode=False, - output_encoding=None): - ''' +def remote_get( + cwd, + remote="origin", + user=None, + password=None, + redact_auth=True, + ignore_retcode=False, + output_encoding=None, +): + """ Get the fetch and push URL for a specific remote cwd @@ -3807,35 +3922,40 @@ def remote_get(cwd, salt myminion git.remote_get /path/to/repo salt myminion git.remote_get /path/to/repo upstream - ''' + """ cwd = _expand_path(cwd, user) - all_remotes = remotes(cwd, - user=user, - password=password, - redact_auth=redact_auth, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) + all_remotes = remotes( + cwd, + user=user, + password=password, + redact_auth=redact_auth, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) if remote not in all_remotes: raise CommandExecutionError( - 'Remote \'{0}\' not present in git checkout located at {1}' - .format(remote, cwd) + "Remote '{0}' not present in git checkout located at {1}".format( + remote, cwd + ) ) return all_remotes[remote] -def remote_refs(url, - heads=False, - tags=False, - user=None, - password=None, - identity=None, - https_user=None, - https_pass=None, - ignore_retcode=False, - output_encoding=None, - saltenv='base', - **kwargs): - ''' +def remote_refs( + url, + heads=False, + tags=False, + user=None, + password=None, + identity=None, + https_user=None, + https_pass=None, + ignore_retcode=False, + output_encoding=None, + saltenv="base", + **kwargs +): + """ .. versionadded:: 2015.8.0 Return the remote refs for the specified URL by running ``git ls-remote``. @@ -3922,35 +4042,38 @@ def remote_refs(url, salt myminion git.remote_refs https://github.com/saltstack/salt.git salt myminion git.remote_refs https://github.com/saltstack/salt.git filter=develop - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - filter_ = kwargs.pop('filter', None) + filter_ = kwargs.pop("filter", None) if kwargs: salt.utils.args.invalid_kwargs(kwargs) - command = ['git', 'ls-remote'] + command = ["git", "ls-remote"] if heads: - command.append('--heads') + command.append("--heads") if tags: - command.append('--tags') + command.append("--tags") try: - command.append(salt.utils.url.add_http_basic_auth(url, - https_user, - https_pass, - https_only=True)) + command.append( + salt.utils.url.add_http_basic_auth( + url, https_user, https_pass, https_only=True + ) + ) except ValueError as exc: raise SaltInvocationError(exc.__str__()) if filter_: command.append(filter_) - output = _git_run(command, - user=user, - password=password, - identity=identity, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - output_encoding=output_encoding)['stdout'] + output = _git_run( + command, + user=user, + password=password, + identity=identity, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + output_encoding=output_encoding, + )["stdout"] ret = {} - for line in salt.utils.itertools.split(output, '\n'): + for line in salt.utils.itertools.split(output, "\n"): try: sha1_hash, ref_name = line.split(None, 1) except ValueError: @@ -3959,19 +4082,21 @@ def remote_refs(url, return ret -def remote_set(cwd, - url, - remote='origin', - user=None, - password=None, - https_user=None, - https_pass=None, - push_url=None, - push_https_user=None, - push_https_pass=None, - ignore_retcode=False, - output_encoding=None): - ''' +def remote_set( + cwd, + url, + remote="origin", + user=None, + password=None, + https_user=None, + https_pass=None, + push_url=None, + push_https_user=None, + push_https_pass=None, + ignore_retcode=False, + output_encoding=None, +): + """ cwd The path to the git checkout @@ -4043,68 +4168,79 @@ def remote_set(cwd, salt myminion git.remote_set /path/to/repo git@github.com:user/repo.git salt myminion git.remote_set /path/to/repo git@github.com:user/repo.git remote=upstream salt myminion git.remote_set /path/to/repo https://github.com/user/repo.git remote=upstream push_url=git@github.com:user/repo.git - ''' + """ # Check if remote exists - if remote in remotes(cwd, user=user, password=password, - output_encoding=output_encoding): + if remote in remotes( + cwd, user=user, password=password, output_encoding=output_encoding + ): log.debug( - 'Remote \'%s\' already exists in git checkout located at %s, ' - 'removing so it can be re-added', remote, cwd + "Remote '%s' already exists in git checkout located at %s, " + "removing so it can be re-added", + remote, + cwd, + ) + command = ["git", "remote", "rm", remote] + _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, ) - command = ['git', 'remote', 'rm', remote] - _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) # Add remote try: - url = salt.utils.url.add_http_basic_auth(url, - https_user, - https_pass, - https_only=True) + url = salt.utils.url.add_http_basic_auth( + url, https_user, https_pass, https_only=True + ) except ValueError as exc: raise SaltInvocationError(exc.__str__()) - command = ['git', 'remote', 'add', remote, url] - _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) + command = ["git", "remote", "add", remote, url] + _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) if push_url: if not isinstance(push_url, six.string_types): push_url = six.text_type(push_url) try: - push_url = salt.utils.url.add_http_basic_auth(push_url, - push_https_user, - push_https_pass, - https_only=True) + push_url = salt.utils.url.add_http_basic_auth( + push_url, push_https_user, push_https_pass, https_only=True + ) except ValueError as exc: raise SaltInvocationError(six.text_type(exc)) - command = ['git', 'remote', 'set-url', '--push', remote, push_url] - _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) - return remote_get(cwd=cwd, - remote=remote, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding) + command = ["git", "remote", "set-url", "--push", remote, push_url] + _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) + return remote_get( + cwd=cwd, + remote=remote, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + ) -def remotes(cwd, - user=None, - password=None, - redact_auth=True, - ignore_retcode=False, - output_encoding=None): - ''' +def remotes( + cwd, + user=None, + password=None, + redact_auth=True, + ignore_retcode=False, + output_encoding=None, +): + """ Get fetch and push URLs for each remote in a git checkout cwd @@ -4156,17 +4292,19 @@ def remotes(cwd, .. code-block:: bash salt myminion git.remotes /path/to/repo - ''' + """ cwd = _expand_path(cwd, user) - command = ['git', 'remote', '--verbose'] + command = ["git", "remote", "--verbose"] ret = {} - output = _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] - for remote_line in salt.utils.itertools.split(output, '\n'): + output = _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] + for remote_line in salt.utils.itertools.split(output, "\n"): try: remote, remote_info = remote_line.split(None, 1) except ValueError: @@ -4176,11 +4314,13 @@ def remotes(cwd, except ValueError: continue # Remove parenthesis - action = action.lstrip('(').rstrip(')').lower() - if action not in ('fetch', 'push'): + action = action.lstrip("(").rstrip(")").lower() + if action not in ("fetch", "push"): log.warning( - 'Unknown action \'%s\' for remote \'%s\' in git checkout ' - 'located in %s', action, remote, cwd + "Unknown action '%s' for remote '%s' in git checkout " "located in %s", + action, + remote, + cwd, ) continue if redact_auth: @@ -4189,15 +4329,17 @@ def remotes(cwd, return ret -def reset(cwd, - opts='', - git_opts='', - user=None, - password=None, - identity=None, - ignore_retcode=False, - output_encoding=None): - ''' +def reset( + cwd, + opts="", + git_opts="", + user=None, + password=None, + identity=None, + ignore_retcode=False, + output_encoding=None, +): + """ Interface to `git-reset(1)`_, returns the stdout from the git command cwd @@ -4275,29 +4417,33 @@ def reset(cwd, salt myminion git.reset /path/to/repo ac3ee5c # Hard reset salt myminion git.reset /path/to/repo opts='--hard origin/master' - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('reset') + command = ["git"] + _format_git_opts(git_opts) + command.append("reset") command.extend(_format_opts(opts)) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - identity=identity, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + identity=identity, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def rev_parse(cwd, - rev=None, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def rev_parse( + cwd, + rev=None, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ .. versionadded:: 2015.8.0 Interface to `git-rev-parse(1)`_ @@ -4370,29 +4516,33 @@ def rev_parse(cwd, salt myminion git.rev_parse /path/to/repo 'v1.2.3^{commit}' # Find out whether or not the repo at /path/to/repo is a bare repository salt myminion git.rev_parse /path/to/repo opts='--is-bare-repository' - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('rev-parse') + command = ["git"] + _format_git_opts(git_opts) + command.append("rev-parse") command.extend(_format_opts(opts)) if rev is not None: command.append(rev) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def revision(cwd, - rev='HEAD', - short=False, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def revision( + cwd, + rev="HEAD", + short=False, + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ Returns the SHA1 hash of a given identifier (hash, branch, tag, HEAD, etc.) cwd @@ -4437,29 +4587,33 @@ def revision(cwd, .. code-block:: bash salt myminion git.revision /path/to/repo mybranch - ''' + """ cwd = _expand_path(cwd, user) - command = ['git', 'rev-parse'] + command = ["git", "rev-parse"] if short: - command.append('--short') + command.append("--short") command.append(rev) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def rm_(cwd, - filename, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def rm_( + cwd, + filename, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ Interface to `git-rm(1)`_ cwd @@ -4527,29 +4681,33 @@ def rm_(cwd, salt myminion git.rm /path/to/repo foo/bar.py salt myminion git.rm /path/to/repo foo/bar.py opts='--dry-run' salt myminion git.rm /path/to/repo foo/baz opts='-r' - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('rm') + command = ["git"] + _format_git_opts(git_opts) + command.append("rm") command.extend(_format_opts(opts)) - command.extend(['--', filename]) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + command.extend(["--", filename]) + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def stash(cwd, - action='save', - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def stash( + cwd, + action="save", + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ Interface to `git-stash(1)`_, returns the stdout from the git command cwd @@ -4610,25 +4768,23 @@ def stash(cwd, salt myminion git.stash /path/to/repo apply opts='stash@{1}' salt myminion git.stash /path/to/repo drop opts='stash@{1}' salt myminion git.stash /path/to/repo list - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.extend(['stash', action]) + command = ["git"] + _format_git_opts(git_opts) + command.extend(["stash", action]) command.extend(_format_opts(opts)) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def status(cwd, - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def status(cwd, user=None, password=None, ignore_retcode=False, output_encoding=None): + """ .. versionchanged:: 2015.8.0 Return data has changed from a list of lists to a dictionary @@ -4670,23 +4826,20 @@ def status(cwd, .. code-block:: bash salt myminion git.status /path/to/repo - ''' + """ cwd = _expand_path(cwd, user) - state_map = { - 'M': 'modified', - 'A': 'new', - 'D': 'deleted', - '??': 'untracked' - } + state_map = {"M": "modified", "A": "new", "D": "deleted", "??": "untracked"} ret = {} - command = ['git', 'status', '-z', '--porcelain'] - output = _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] - for line in output.split(str('\0')): + command = ["git", "status", "-z", "--porcelain"] + output = _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] + for line in output.split(str("\0")): try: state, filename = line.split(None, 1) except ValueError: @@ -4695,18 +4848,20 @@ def status(cwd, return ret -def submodule(cwd, - command, - opts='', - git_opts='', - user=None, - password=None, - identity=None, - ignore_retcode=False, - saltenv='base', - output_encoding=None, - **kwargs): - ''' +def submodule( + cwd, + command, + opts="", + git_opts="", + user=None, + password=None, + identity=None, + ignore_retcode=False, + saltenv="base", + output_encoding=None, + **kwargs +): + """ .. versionchanged:: 2015.8.0 Added the ``command`` argument to allow for operations other than ``update`` to be run on submodules, and deprecated the ``init`` @@ -4827,42 +4982,46 @@ def submodule(cwd, # Unregister submodule (2015.8.0 and later) salt myminion git.submodule /path/to/repo/sub/repo deinit - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - init_ = kwargs.pop('init', False) + init_ = kwargs.pop("init", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) cwd = _expand_path(cwd, user) if init_: raise SaltInvocationError( - 'The \'init\' argument is no longer supported. Either set ' - '\'command\' to \'init\', or include \'--init\' in the \'opts\' ' - 'argument and set \'command\' to \'update\'.' + "The 'init' argument is no longer supported. Either set " + "'command' to 'init', or include '--init' in the 'opts' " + "argument and set 'command' to 'update'." ) - cmd = ['git'] + _format_git_opts(git_opts) - cmd.extend(['submodule', command]) + cmd = ["git"] + _format_git_opts(git_opts) + cmd.extend(["submodule", command]) cmd.extend(_format_opts(opts)) - return _git_run(cmd, - cwd=cwd, - user=user, - password=password, - identity=identity, - ignore_retcode=ignore_retcode, - saltenv=saltenv, - output_encoding=output_encoding)['stdout'] + return _git_run( + cmd, + cwd=cwd, + user=user, + password=password, + identity=identity, + ignore_retcode=ignore_retcode, + saltenv=saltenv, + output_encoding=output_encoding, + )["stdout"] -def symbolic_ref(cwd, - ref, - value=None, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def symbolic_ref( + cwd, + ref, + value=None, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ .. versionadded:: 2015.8.0 Interface to `git-symbolic-ref(1)`_ @@ -4935,39 +5094,42 @@ def symbolic_ref(cwd, salt myminion git.symbolic_ref /path/to/repo FOO refs/heads/foo # Delete symbolic ref 'FOO' salt myminion git.symbolic_ref /path/to/repo FOO opts='--delete' - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('symbolic-ref') + command = ["git"] + _format_git_opts(git_opts) + command.append("symbolic-ref") opts = _format_opts(opts) - if value is not None and any(x in opts for x in ('-d', '--delete')): + if value is not None and any(x in opts for x in ("-d", "--delete")): raise SaltInvocationError( - 'Value cannot be set for symbolic ref if -d/--delete is included ' - 'in opts' + "Value cannot be set for symbolic ref if -d/--delete is included " "in opts" ) command.extend(opts) command.append(ref) if value: command.extend(value) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] -def tag(cwd, - name, - ref='HEAD', - message=None, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def tag( + cwd, + name, + ref="HEAD", + message=None, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ .. versionadded:: 2018.3.4 Interface to `git-tag(1)`_, adds and removes tags. @@ -5041,34 +5203,36 @@ def tag(cwd, salt myminion git.tag /path/to/repo v1.2 message='Version 1.2' # Delete the tag salt myminion git.tag /path/to/repo v1.2 opts='-d' - ''' + """ cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.append('tag') + command = ["git"] + _format_git_opts(git_opts) + command.append("tag") # Don't add options for annotated tags, since we'll automatically add them # if a message was passed. This keeps us from blocking on input, as passing # these options without a separate message option would launch an editor. - formatted_opts = [x for x in _format_opts(opts) if x not in ('-a', '--annotate')] + formatted_opts = [x for x in _format_opts(opts) if x not in ("-a", "--annotate")] # Make sure that the message was not passed in the opts - if any(x == '-m' or '--message' in x for x in formatted_opts): + if any(x == "-m" or "--message" in x for x in formatted_opts): raise SaltInvocationError( 'Tag messages must be passed in the "message" argument' ) command.extend(formatted_opts) command.append(name) - if '-d' not in formatted_opts and '--delete' not in formatted_opts: + if "-d" not in formatted_opts and "--delete" not in formatted_opts: command.append(ref) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - redirect_stderr=True, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + redirect_stderr=True, + output_encoding=output_encoding, + )["stdout"] def version(versioninfo=False): - ''' + """ .. versionadded:: 2015.8.0 Returns the version of Git installed on the minion @@ -5082,33 +5246,30 @@ def version(versioninfo=False): .. code-block:: bash salt myminion git.version - ''' - contextkey = 'git.version' - contextkey_info = 'git.versioninfo' + """ + contextkey = "git.version" + contextkey_info = "git.versioninfo" if contextkey not in __context__: try: - version_ = _git_run(['git', '--version'])['stdout'] + version_ = _git_run(["git", "--version"])["stdout"] except CommandExecutionError as exc: - log.error( - 'Failed to obtain the git version (error follows):\n%s', - exc - ) - version_ = 'unknown' + log.error("Failed to obtain the git version (error follows):\n%s", exc) + version_ = "unknown" try: __context__[contextkey] = version_.split()[-1] except IndexError: # Somehow git --version returned no stdout while not raising an # error. Should never happen but we should still account for this # possible edge case. - log.error('Running \'git --version\' returned no stdout') - __context__[contextkey] = 'unknown' + log.error("Running 'git --version' returned no stdout") + __context__[contextkey] = "unknown" if not versioninfo: return __context__[contextkey] if contextkey_info not in __context__: # Set ptr to the memory location of __context__[contextkey_info] to # prevent repeated dict lookups ptr = __context__.setdefault(contextkey_info, []) - for part in __context__[contextkey].split('.'): + for part in __context__[contextkey].split("."): try: ptr.append(int(part)) except ValueError: @@ -5116,20 +5277,22 @@ def version(versioninfo=False): return __context__[contextkey_info] -def worktree_add(cwd, - worktree_path, - ref=None, - reset_branch=None, - force=None, - detach=False, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None, - **kwargs): - ''' +def worktree_add( + cwd, + worktree_path, + ref=None, + reset_branch=None, + force=None, + detach=False, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, + **kwargs +): + """ .. versionadded:: 2015.8.0 Interface to `git-worktree(1)`_, adds a worktree @@ -5224,59 +5387,60 @@ def worktree_add(cwd, salt myminion git.worktree_add /path/to/repo/main ../hotfix ref=origin/master salt myminion git.worktree_add /path/to/repo/main ../hotfix branch=hotfix21 ref=v2.1.9.3 - ''' + """ _check_worktree_support() kwargs = salt.utils.args.clean_kwargs(**kwargs) - branch_ = kwargs.pop('branch', None) + branch_ = kwargs.pop("branch", None) if kwargs: salt.utils.args.invalid_kwargs(kwargs) cwd = _expand_path(cwd, user) if branch_ and detach: - raise SaltInvocationError( - 'Only one of \'branch\' and \'detach\' is allowed' - ) + raise SaltInvocationError("Only one of 'branch' and 'detach' is allowed") - command = ['git'] + _format_git_opts(git_opts) - command.extend(['worktree', 'add']) + command = ["git"] + _format_git_opts(git_opts) + command.extend(["worktree", "add"]) if detach: if force: log.warning( - '\'force\' argument to git.worktree_add is ignored when ' - 'detach=True' + "'force' argument to git.worktree_add is ignored when " "detach=True" ) - command.append('--detach') + command.append("--detach") else: if not branch_: branch_ = os.path.basename(worktree_path) - command.extend(['-B' if reset_branch else '-b', branch_]) + command.extend(["-B" if reset_branch else "-b", branch_]) if force: - command.append('--force') + command.append("--force") command.extend(_format_opts(opts)) command.append(worktree_path) if ref: command.append(ref) # Checkout message goes to stderr - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - redirect_stderr=True, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + redirect_stderr=True, + output_encoding=output_encoding, + )["stdout"] -def worktree_prune(cwd, - dry_run=False, - verbose=True, - expire=None, - opts='', - git_opts='', - user=None, - password=None, - ignore_retcode=False, - output_encoding=None): - ''' +def worktree_prune( + cwd, + dry_run=False, + verbose=True, + expire=None, + opts="", + git_opts="", + user=None, + password=None, + ignore_retcode=False, + output_encoding=None, +): + """ .. versionadded:: 2015.8.0 Interface to `git-worktree(1)`_, prunes stale worktree administrative data @@ -5360,28 +5524,30 @@ def worktree_prune(cwd, salt myminion git.worktree_prune /path/to/repo salt myminion git.worktree_prune /path/to/repo dry_run=True salt myminion git.worktree_prune /path/to/repo expire=1.day.ago - ''' + """ _check_worktree_support() cwd = _expand_path(cwd, user) - command = ['git'] + _format_git_opts(git_opts) - command.extend(['worktree', 'prune']) + command = ["git"] + _format_git_opts(git_opts) + command.extend(["worktree", "prune"]) if dry_run: - command.append('--dry-run') + command.append("--dry-run") if verbose: - command.append('--verbose') + command.append("--verbose") if expire: - command.extend(['--expire', expire]) + command.extend(["--expire", expire]) command.extend(_format_opts(opts)) - return _git_run(command, - cwd=cwd, - user=user, - password=password, - ignore_retcode=ignore_retcode, - output_encoding=output_encoding)['stdout'] + return _git_run( + command, + cwd=cwd, + user=user, + password=password, + ignore_retcode=ignore_retcode, + output_encoding=output_encoding, + )["stdout"] def worktree_rm(cwd, user=None, output_encoding=None): - ''' + """ .. versionadded:: 2015.8.0 Recursively removes the worktree located at ``cwd``, returning ``True`` if @@ -5422,17 +5588,15 @@ def worktree_rm(cwd, user=None, output_encoding=None): .. code-block:: bash salt myminion git.worktree_rm /path/to/worktree - ''' + """ _check_worktree_support() cwd = _expand_path(cwd, user) if not os.path.exists(cwd): - raise CommandExecutionError(cwd + ' does not exist') + raise CommandExecutionError(cwd + " does not exist") elif not is_worktree(cwd, output_encoding=output_encoding): - raise CommandExecutionError(cwd + ' is not a git worktree') + raise CommandExecutionError(cwd + " is not a git worktree") try: salt.utils.files.rm_rf(cwd) except Exception as exc: # pylint: disable=broad-except - raise CommandExecutionError( - 'Unable to remove {0}: {1}'.format(cwd, exc) - ) + raise CommandExecutionError("Unable to remove {0}: {1}".format(cwd, exc)) return True diff --git a/salt/modules/github.py b/salt/modules/github.py index 8ac79c8b98c..c5811fdf8ef 100644 --- a/salt/modules/github.py +++ b/salt/modules/github.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for interacting with the GitHub v3 API. .. versionadded:: 2016.3.0 @@ -28,16 +28,18 @@ For example: # optional: it can be dangerous to change the privacy of a repository # in an automated way. set this to True to allow privacy modifications allow_repo_privacy_changes: False -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.http + # Import Salt Libs from salt.exceptions import CommandExecutionError from salt.ext import six -import salt.utils.http # Import third party libs HAS_LIBS = False @@ -47,6 +49,7 @@ try: import github.PaginatedList import github.NamedUser from github.GithubException import UnknownObjectException + # pylint: enable=no-name-in-module HAS_LIBS = True except ImportError: @@ -54,21 +57,24 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'github' +__virtualname__ = "github" def __virtual__(): - ''' + """ Only load this module if PyGithub is installed on this minion. - ''' + """ if HAS_LIBS: return __virtualname__ - return (False, 'The github execution module cannot be loaded: ' - 'PyGithub library is not installed.') + return ( + False, + "The github execution module cannot be loaded: " + "PyGithub library is not installed.", + ) def _get_config_value(profile, config_name): - ''' + """ Helper function that returns a profile's configuration value based on the supplied configuration name. @@ -77,42 +83,33 @@ def _get_config_value(profile, config_name): config_name The configuration item's name to use to return configuration values. - ''' - config = __salt__['config.option'](profile) + """ + config = __salt__["config.option"](profile) if not config: raise CommandExecutionError( - 'Authentication information could not be found for the ' - '\'{0}\' profile.'.format(profile) + "Authentication information could not be found for the " + "'{0}' profile.".format(profile) ) config_value = config.get(config_name) if config_value is None: raise CommandExecutionError( - 'The \'{0}\' parameter was not found in the \'{1}\' ' - 'profile.'.format( - config_name, - profile - ) + "The '{0}' parameter was not found in the '{1}' " + "profile.".format(config_name, profile) ) return config_value def _get_client(profile): - ''' + """ Return the GitHub client, cached into __context__ for performance - ''' - token = _get_config_value(profile, 'token') - key = 'github.{0}:{1}'.format( - token, - _get_config_value(profile, 'org_name') - ) + """ + token = _get_config_value(profile, "token") + key = "github.{0}:{1}".format(token, _get_config_value(profile, "org_name")) if key not in __context__: - __context__[key] = github.Github( - token, - per_page=100 - ) + __context__[key] = github.Github(token, per_page=100) return __context__[key] @@ -121,25 +118,25 @@ def _get_members(organization, params=None): github.NamedUser.NamedUser, organization._requester, organization.url + "/members", - params + params, ) def _get_repos(profile, params=None, ignore_cache=False): # Use cache when no params are given - org_name = _get_config_value(profile, 'org_name') - key = 'github.{0}:repos'.format(org_name) + org_name = _get_config_value(profile, "org_name") + key = "github.{0}:repos".format(org_name) if key not in __context__ or ignore_cache or params is not None: - org_name = _get_config_value(profile, 'org_name') + org_name = _get_config_value(profile, "org_name") client = _get_client(profile) organization = client.get_organization(org_name) result = github.PaginatedList.PaginatedList( github.Repository.Repository, organization._requester, - organization.url + '/repos', - params + organization.url + "/repos", + params, ) # Only cache results if no params were given (full scan) @@ -161,7 +158,7 @@ def _get_repos(profile, params=None, ignore_cache=False): def list_users(profile="github", ignore_cache=False): - ''' + """ List all users within the organization. profile @@ -178,11 +175,9 @@ def list_users(profile="github", ignore_cache=False): salt myminion github.list_users salt myminion github.list_users profile='my-github-profile' - ''' - org_name = _get_config_value(profile, 'org_name') - key = "github.{0}:users".format( - org_name - ) + """ + org_name = _get_config_value(profile, "org_name") + key = "github.{0}:users".format(org_name) if key not in __context__ or ignore_cache: client = _get_client(profile) organization = client.get_organization(org_name) @@ -190,8 +185,8 @@ def list_users(profile="github", ignore_cache=False): return __context__[key] -def get_user(name, profile='github', user_details=False): - ''' +def get_user(name, profile="github", user_details=False): + """ Get a GitHub user by name. name @@ -213,7 +208,7 @@ def get_user(name, profile='github', user_details=False): salt myminion github.get_user github-handle salt myminion github.get_user github-handle user_details=true - ''' + """ if not user_details and name in list_users(profile): # User is in the org, no need for additional Data @@ -221,9 +216,7 @@ def get_user(name, profile='github', user_details=False): response = {} client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) + organization = client.get_organization(_get_config_value(profile, "org_name")) try: user = client.get_user(name) @@ -231,34 +224,33 @@ def get_user(name, profile='github', user_details=False): log.exception("Resource not found") return False - response['company'] = user.company - response['created_at'] = user.created_at - response['email'] = user.email - response['html_url'] = user.html_url - response['id'] = user.id - response['login'] = user.login - response['name'] = user.name - response['type'] = user.type - response['url'] = user.url + response["company"] = user.company + response["created_at"] = user.created_at + response["email"] = user.email + response["html_url"] = user.html_url + response["id"] = user.id + response["login"] = user.login + response["name"] = user.name + response["type"] = user.type + response["url"] = user.url try: headers, data = organization._requester.requestJsonAndCheck( - "GET", - organization.url + "/memberships/" + user._identity + "GET", organization.url + "/memberships/" + user._identity ) except UnknownObjectException: - response['membership_state'] = 'nonexistent' - response['in_org'] = False + response["membership_state"] = "nonexistent" + response["in_org"] = False return response - response['in_org'] = organization.has_in_members(user) - response['membership_state'] = data.get('state') + response["in_org"] = organization.has_in_members(user) + response["membership_state"] = data.get("state") return response -def add_user(name, profile='github'): - ''' +def add_user(name, profile="github"): + """ Add a GitHub user. name @@ -272,12 +264,10 @@ def add_user(name, profile='github'): .. code-block:: bash salt myminion github.add_user github-handle - ''' + """ client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) + organization = client.get_organization(_get_config_value(profile, "org_name")) try: github_named_user = client.get_user(name) @@ -286,15 +276,14 @@ def add_user(name, profile='github'): return False headers, data = organization._requester.requestJsonAndCheck( - "PUT", - organization.url + "/memberships/" + github_named_user._identity + "PUT", organization.url + "/memberships/" + github_named_user._identity ) - return data.get('state') == 'pending' + return data.get("state") == "pending" -def remove_user(name, profile='github'): - ''' +def remove_user(name, profile="github"): + """ Remove a Github user by name. name @@ -308,12 +297,10 @@ def remove_user(name, profile='github'): .. code-block:: bash salt myminion github.remove_user github-handle - ''' + """ client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) + organization = client.get_organization(_get_config_value(profile, "org_name")) try: git_user = client.get_user(name) @@ -327,8 +314,8 @@ def remove_user(name, profile='github'): return not organization.has_in_members(git_user) -def get_issue(issue_number, repo_name=None, profile='github', output='min'): - ''' +def get_issue(issue_number, repo_name=None, profile="github", output="min"): + """ Return information about a single issue in a named repository. .. versionadded:: 2016.11.0 @@ -355,19 +342,19 @@ def get_issue(issue_number, repo_name=None, profile='github', output='min'): salt myminion github.get_issue 514 salt myminion github.get_issue 514 repo_name=salt - ''' - org_name = _get_config_value(profile, 'org_name') + """ + org_name = _get_config_value(profile, "org_name") if repo_name is None: - repo_name = _get_config_value(profile, 'repo_name') + repo_name = _get_config_value(profile, "repo_name") - action = '/'.join(['repos', org_name, repo_name]) - command = 'issues/' + six.text_type(issue_number) + action = "/".join(["repos", org_name, repo_name]) + command = "issues/" + six.text_type(issue_number) ret = {} issue_data = _query(profile, action=action, command=command) - issue_id = issue_data.get('id') - if output == 'full': + issue_id = issue_data.get("id") + if output == "full": ret[issue_id] = issue_data else: ret[issue_id] = _format_issue(issue_data) @@ -375,12 +362,10 @@ def get_issue(issue_number, repo_name=None, profile='github', output='min'): return ret -def get_issue_comments(issue_number, - repo_name=None, - profile='github', - since=None, - output='min'): - ''' +def get_issue_comments( + issue_number, repo_name=None, profile="github", since=None, output="min" +): + """ Return information about the comments for a given issue in a named repository. .. versionadded:: 2016.11.0 @@ -411,47 +396,51 @@ def get_issue_comments(issue_number, salt myminion github.get_issue_comments 514 salt myminion github.get_issue 514 repo_name=salt - ''' - org_name = _get_config_value(profile, 'org_name') + """ + org_name = _get_config_value(profile, "org_name") if repo_name is None: - repo_name = _get_config_value(profile, 'repo_name') + repo_name = _get_config_value(profile, "repo_name") - action = '/'.join(['repos', org_name, repo_name]) - command = '/'.join(['issues', six.text_type(issue_number), 'comments']) + action = "/".join(["repos", org_name, repo_name]) + command = "/".join(["issues", six.text_type(issue_number), "comments"]) args = {} if since: - args['since'] = since + args["since"] = since comments = _query(profile, action=action, command=command, args=args) ret = {} for comment in comments: - comment_id = comment.get('id') - if output == 'full': + comment_id = comment.get("id") + if output == "full": ret[comment_id] = comment else: - ret[comment_id] = {'id': comment.get('id'), - 'created_at': comment.get('created_at'), - 'updated_at': comment.get('updated_at'), - 'user_login': comment.get('user').get('login')} + ret[comment_id] = { + "id": comment.get("id"), + "created_at": comment.get("created_at"), + "updated_at": comment.get("updated_at"), + "user_login": comment.get("user").get("login"), + } return ret -def get_issues(repo_name=None, - profile='github', - milestone=None, - state='open', - assignee=None, - creator=None, - mentioned=None, - labels=None, - sort='created', - direction='desc', - since=None, - output='min', - per_page=None): - ''' +def get_issues( + repo_name=None, + profile="github", + milestone=None, + state="open", + assignee=None, + creator=None, + mentioned=None, + labels=None, + sort="created", + direction="desc", + since=None, + output="min", + per_page=None, +): + """ Returns information for all issues in a given repository, based on the search options. .. versionadded:: 2016.11.0 @@ -520,48 +509,48 @@ def get_issues(repo_name=None, .. code-block:: bash salt myminion github.get_issues my-github-repo - ''' - org_name = _get_config_value(profile, 'org_name') + """ + org_name = _get_config_value(profile, "org_name") if repo_name is None: - repo_name = _get_config_value(profile, 'repo_name') + repo_name = _get_config_value(profile, "repo_name") - action = '/'.join(['repos', org_name, repo_name]) + action = "/".join(["repos", org_name, repo_name]) args = {} # Build API arguments, as necessary. if milestone: - args['milestone'] = milestone + args["milestone"] = milestone if assignee: - args['assignee'] = assignee + args["assignee"] = assignee if creator: - args['creator'] = creator + args["creator"] = creator if mentioned: - args['mentioned'] = mentioned + args["mentioned"] = mentioned if labels: - args['labels'] = labels + args["labels"] = labels if since: - args['since'] = since + args["since"] = since if per_page: - args['per_page'] = per_page + args["per_page"] = per_page # Only pass the following API args if they're not the defaults listed. - if state and state != 'open': - args['state'] = state - if sort and sort != 'created': - args['sort'] = sort - if direction and direction != 'desc': - args['direction'] = direction + if state and state != "open": + args["state"] = state + if sort and sort != "created": + args["sort"] = sort + if direction and direction != "desc": + args["direction"] = direction ret = {} - issues = _query(profile, action=action, command='issues', args=args) + issues = _query(profile, action=action, command="issues", args=args) for issue in issues: # Pull requests are included in the issue list from GitHub # Let's not include those in the return. - if issue.get('pull_request'): + if issue.get("pull_request"): continue - issue_id = issue.get('id') - if output == 'full': + issue_id = issue.get("id") + if output == "full": ret[issue_id] = issue else: ret[issue_id] = _format_issue(issue) @@ -569,14 +558,16 @@ def get_issues(repo_name=None, return ret -def get_milestones(repo_name=None, - profile='github', - state='open', - sort='due_on', - direction='asc', - output='min', - per_page=None): - ''' +def get_milestones( + repo_name=None, + profile="github", + state="open", + sort="due_on", + direction="asc", + output="min", + per_page=None, +): + """ Return information about milestones for a given repository. .. versionadded:: 2016.11.0 @@ -616,47 +607,45 @@ def get_milestones(repo_name=None, salt myminion github.get_milestones - ''' - org_name = _get_config_value(profile, 'org_name') + """ + org_name = _get_config_value(profile, "org_name") if repo_name is None: - repo_name = _get_config_value(profile, 'repo_name') + repo_name = _get_config_value(profile, "repo_name") - action = '/'.join(['repos', org_name, repo_name]) + action = "/".join(["repos", org_name, repo_name]) args = {} if per_page: - args['per_page'] = per_page + args["per_page"] = per_page # Only pass the following API args if they're not the defaults listed. - if state and state != 'open': - args['state'] = state - if sort and sort != 'due_on': - args['sort'] = sort - if direction and direction != 'asc': - args['direction'] = direction + if state and state != "open": + args["state"] = state + if sort and sort != "due_on": + args["sort"] = sort + if direction and direction != "asc": + args["direction"] = direction ret = {} - milestones = _query(profile, action=action, command='milestones', args=args) + milestones = _query(profile, action=action, command="milestones", args=args) for milestone in milestones: - milestone_id = milestone.get('id') - if output == 'full': + milestone_id = milestone.get("id") + if output == "full": ret[milestone_id] = milestone else: - milestone.pop('creator') - milestone.pop('html_url') - milestone.pop('labels_url') + milestone.pop("creator") + milestone.pop("html_url") + milestone.pop("labels_url") ret[milestone_id] = milestone return ret -def get_milestone(number=None, - name=None, - repo_name=None, - profile='github', - output='min'): - ''' +def get_milestone( + number=None, name=None, repo_name=None, profile="github", output="min" +): + """ Return information about a single milestone in a named repository. .. versionadded:: 2016.11.0 @@ -688,36 +677,36 @@ def get_milestone(number=None, salt myminion github.get_milestone 72 salt myminion github.get_milestone name=my_milestone - ''' + """ ret = {} if not any([number, name]): raise CommandExecutionError( - 'Either a milestone \'name\' or \'number\' must be provided.' + "Either a milestone 'name' or 'number' must be provided." ) - org_name = _get_config_value(profile, 'org_name') + org_name = _get_config_value(profile, "org_name") if repo_name is None: - repo_name = _get_config_value(profile, 'repo_name') + repo_name = _get_config_value(profile, "repo_name") - action = '/'.join(['repos', org_name, repo_name]) + action = "/".join(["repos", org_name, repo_name]) if number: - command = 'milestones/' + six.text_type(number) + command = "milestones/" + six.text_type(number) milestone_data = _query(profile, action=action, command=command) - milestone_id = milestone_data.get('id') - if output == 'full': + milestone_id = milestone_data.get("id") + if output == "full": ret[milestone_id] = milestone_data else: - milestone_data.pop('creator') - milestone_data.pop('html_url') - milestone_data.pop('labels_url') + milestone_data.pop("creator") + milestone_data.pop("html_url") + milestone_data.pop("labels_url") ret[milestone_id] = milestone_data return ret else: milestones = get_milestones(repo_name=repo_name, profile=profile, output=output) for key, val in six.iteritems(milestones): - if val.get('title') == name: + if val.get("title") == name: ret[key] = val return ret @@ -726,32 +715,32 @@ def get_milestone(number=None, def _repo_to_dict(repo): ret = {} - ret['id'] = repo.id - ret['name'] = repo.name - ret['full_name'] = repo.full_name - ret['owner'] = repo.owner.login - ret['private'] = repo.private - ret['html_url'] = repo.html_url - ret['description'] = repo.description - ret['fork'] = repo.fork - ret['homepage'] = repo.homepage - ret['size'] = repo.size - ret['stargazers_count'] = repo.stargazers_count - ret['watchers_count'] = repo.watchers_count - ret['language'] = repo.language - ret['open_issues_count'] = repo.open_issues_count - ret['forks'] = repo.forks - ret['open_issues'] = repo.open_issues - ret['watchers'] = repo.watchers - ret['default_branch'] = repo.default_branch - ret['has_issues'] = repo.has_issues - ret['has_wiki'] = repo.has_wiki - ret['has_downloads'] = repo.has_downloads + ret["id"] = repo.id + ret["name"] = repo.name + ret["full_name"] = repo.full_name + ret["owner"] = repo.owner.login + ret["private"] = repo.private + ret["html_url"] = repo.html_url + ret["description"] = repo.description + ret["fork"] = repo.fork + ret["homepage"] = repo.homepage + ret["size"] = repo.size + ret["stargazers_count"] = repo.stargazers_count + ret["watchers_count"] = repo.watchers_count + ret["language"] = repo.language + ret["open_issues_count"] = repo.open_issues_count + ret["forks"] = repo.forks + ret["open_issues"] = repo.open_issues + ret["watchers"] = repo.watchers + ret["default_branch"] = repo.default_branch + ret["has_issues"] = repo.has_issues + ret["has_wiki"] = repo.has_wiki + ret["has_downloads"] = repo.has_downloads return ret -def get_repo_info(repo_name, profile='github', ignore_cache=False): - ''' +def get_repo_info(repo_name, profile="github", ignore_cache=False): + """ Return information for a given repo. .. versionadded:: 2016.11.0 @@ -768,18 +757,17 @@ def get_repo_info(repo_name, profile='github', ignore_cache=False): salt myminion github.get_repo_info salt salt myminion github.get_repo_info salt profile='my-github-profile' - ''' + """ - org_name = _get_config_value(profile, 'org_name') + org_name = _get_config_value(profile, "org_name") key = "github.{0}:{1}:repo_info".format( - _get_config_value(profile, 'org_name'), - repo_name.lower() + _get_config_value(profile, "org_name"), repo_name.lower() ) if key not in __context__ or ignore_cache: client = _get_client(profile) try: - repo = client.get_repo('/'.join([org_name, repo_name])) + repo = client.get_repo("/".join([org_name, repo_name])) if not repo: return {} @@ -792,17 +780,14 @@ def get_repo_info(repo_name, profile='github', ignore_cache=False): __context__[key] = ret except github.UnknownObjectException: raise CommandExecutionError( - 'The \'{0}\' repository under the \'{1}\' organization could not ' - 'be found.'.format( - repo_name, - org_name - ) + "The '{0}' repository under the '{1}' organization could not " + "be found.".format(repo_name, org_name) ) return __context__[key] -def get_repo_teams(repo_name, profile='github'): - ''' +def get_repo_teams(repo_name, profile="github"): + """ Return teams belonging to a repository. .. versionadded:: 2017.7.0 @@ -819,36 +804,34 @@ def get_repo_teams(repo_name, profile='github'): salt myminion github.get_repo_teams salt salt myminion github.get_repo_teams salt profile='my-github-profile' - ''' + """ ret = [] - org_name = _get_config_value(profile, 'org_name') + org_name = _get_config_value(profile, "org_name") client = _get_client(profile) try: - repo = client.get_repo('/'.join([org_name, repo_name])) + repo = client.get_repo("/".join([org_name, repo_name])) except github.UnknownObjectException: raise CommandExecutionError( - 'The \'{0}\' repository under the \'{1}\' organization could not ' - 'be found.'.format(repo_name, org_name) + "The '{0}' repository under the '{1}' organization could not " + "be found.".format(repo_name, org_name) ) try: teams = repo.get_teams() for team in teams: - ret.append({ - 'id': team.id, - 'name': team.name, - 'permission': team.permission - }) + ret.append( + {"id": team.id, "name": team.name, "permission": team.permission} + ) except github.UnknownObjectException: raise CommandExecutionError( - 'Unable to retrieve teams for repository \'{0}\' under the \'{1}\' ' - 'organization.'.format(repo_name, org_name) + "Unable to retrieve teams for repository '{0}' under the '{1}' " + "organization.".format(repo_name, org_name) ) return ret -def list_repos(profile='github'): - ''' +def list_repos(profile="github"): + """ List all repositories within the organization. Includes public and private repositories within the organization Dependent upon the access rights of the profile token. @@ -864,12 +847,12 @@ def list_repos(profile='github'): salt myminion github.list_repos salt myminion github.list_repos profile='my-github-profile' - ''' + """ return [repo.name for repo in _get_repos(profile)] -def list_private_repos(profile='github'): - ''' +def list_private_repos(profile="github"): + """ List private repositories within the organization. Dependent upon the access rights of the profile token. @@ -884,7 +867,7 @@ def list_private_repos(profile='github'): salt myminion github.list_private_repos salt myminion github.list_private_repos profile='my-github-profile' - ''' + """ repos = [] for repo in _get_repos(profile): if repo.private is True: @@ -892,8 +875,8 @@ def list_private_repos(profile='github'): return repos -def list_public_repos(profile='github'): - ''' +def list_public_repos(profile="github"): + """ List public repositories within the organization. .. versionadded:: 2016.11.0 @@ -907,7 +890,7 @@ def list_public_repos(profile='github'): salt myminion github.list_public_repos salt myminion github.list_public_repos profile='my-github-profile' - ''' + """ repos = [] for repo in _get_repos(profile): if repo.private is False: @@ -915,18 +898,20 @@ def list_public_repos(profile='github'): return repos -def add_repo(name, - description=None, - homepage=None, - private=None, - has_issues=None, - has_wiki=None, - has_downloads=None, - auto_init=None, - gitignore_template=None, - license_template=None, - profile="github"): - ''' +def add_repo( + name, + description=None, + homepage=None, + private=None, + has_issues=None, + has_wiki=None, + has_downloads=None, + auto_init=None, + gitignore_template=None, + license_template=None, + profile="github", +): + """ Create a new github repository. name @@ -970,48 +955,46 @@ def add_repo(name, salt myminion github.add_repo 'repo_name' .. versionadded:: 2016.11.0 - ''' + """ try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) + organization = client.get_organization(_get_config_value(profile, "org_name")) given_params = { - 'description': description, - 'homepage': homepage, - 'private': private, - 'has_issues': has_issues, - 'has_wiki': has_wiki, - 'has_downloads': has_downloads, - 'auto_init': auto_init, - 'gitignore_template': gitignore_template, - 'license_template': license_template + "description": description, + "homepage": homepage, + "private": private, + "has_issues": has_issues, + "has_wiki": has_wiki, + "has_downloads": has_downloads, + "auto_init": auto_init, + "gitignore_template": gitignore_template, + "license_template": license_template, } - parameters = {'name': name} + parameters = {"name": name} for param_name, param_value in six.iteritems(given_params): if param_value is not None: parameters[param_name] = param_value organization._requester.requestJsonAndCheck( - "POST", - organization.url + "/repos", - input=parameters + "POST", organization.url + "/repos", input=parameters ) return True except github.GithubException: - log.exception('Error creating a repo') + log.exception("Error creating a repo") return False -def edit_repo(name, - description=None, - homepage=None, - private=None, - has_issues=None, - has_wiki=None, - has_downloads=None, - profile="github"): - ''' +def edit_repo( + name, + description=None, + homepage=None, + private=None, + has_issues=None, + has_wiki=None, + has_downloads=None, + profile="github", +): + """ Updates an existing Github repository. name @@ -1046,52 +1029,48 @@ def edit_repo(name, salt myminion github.add_repo 'repo_name' .. versionadded:: 2016.11.0 - ''' + """ try: - allow_private_change = _get_config_value(profile, 'allow_repo_privacy_changes') + allow_private_change = _get_config_value(profile, "allow_repo_privacy_changes") except CommandExecutionError: allow_private_change = False if private is not None and not allow_private_change: - raise CommandExecutionError("The private field is set to be changed for " - "repo {0} but allow_repo_privacy_changes " - "disallows this.".format(name)) + raise CommandExecutionError( + "The private field is set to be changed for " + "repo {0} but allow_repo_privacy_changes " + "disallows this.".format(name) + ) try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) + organization = client.get_organization(_get_config_value(profile, "org_name")) repo = organization.get_repo(name) given_params = { - 'description': description, - 'homepage': homepage, - 'private': private, - 'has_issues': has_issues, - 'has_wiki': has_wiki, - 'has_downloads': has_downloads + "description": description, + "homepage": homepage, + "private": private, + "has_issues": has_issues, + "has_wiki": has_wiki, + "has_downloads": has_downloads, } - parameters = {'name': name} + parameters = {"name": name} for param_name, param_value in six.iteritems(given_params): if param_value is not None: parameters[param_name] = param_value - organization._requester.requestJsonAndCheck( - "PATCH", - repo.url, - input=parameters - ) + organization._requester.requestJsonAndCheck("PATCH", repo.url, input=parameters) get_repo_info(name, profile=profile, ignore_cache=True) # Refresh cache return True except github.GithubException: - log.exception('Error editing a repo') + log.exception("Error editing a repo") return False def remove_repo(name, profile="github"): - ''' + """ Remove a Github repository. name @@ -1107,27 +1086,25 @@ def remove_repo(name, profile="github"): salt myminion github.remove_repo 'my-repo' .. versionadded:: 2016.11.0 - ''' + """ repo_info = get_repo_info(name, profile=profile) if not repo_info: - log.error('Repo %s to be removed does not exist.', name) + log.error("Repo %s to be removed does not exist.", name) return False try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) + organization = client.get_organization(_get_config_value(profile, "org_name")) repo = organization.get_repo(name) repo.delete() _get_repos(profile=profile, ignore_cache=True) # refresh cache return True except github.GithubException: - log.exception('Error deleting a repo') + log.exception("Error deleting a repo") return False def get_team(name, profile="github"): - ''' + """ Returns the team details if a team with the given name exists, or None otherwise. @@ -1142,17 +1119,19 @@ def get_team(name, profile="github"): .. code-block:: bash salt myminion github.get_team 'team_name' - ''' + """ return list_teams(profile).get(name) -def add_team(name, - description=None, - repo_names=None, - privacy=None, - permission=None, - profile="github"): - ''' +def add_team( + name, + description=None, + repo_names=None, + privacy=None, + permission=None, + profile="github", +): + """ Create a new Github team within an organization. name @@ -1181,42 +1160,34 @@ def add_team(name, salt myminion github.add_team 'team_name' .. versionadded:: 2016.11.0 - ''' + """ try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) + organization = client.get_organization(_get_config_value(profile, "org_name")) parameters = {} - parameters['name'] = name + parameters["name"] = name if description is not None: - parameters['description'] = description + parameters["description"] = description if repo_names is not None: - parameters['repo_names'] = repo_names + parameters["repo_names"] = repo_names if permission is not None: - parameters['permission'] = permission + parameters["permission"] = permission if privacy is not None: - parameters['privacy'] = privacy + parameters["privacy"] = privacy organization._requester.requestJsonAndCheck( - 'POST', - organization.url + '/teams', - input=parameters + "POST", organization.url + "/teams", input=parameters ) list_teams(ignore_cache=True) # Refresh cache return True except github.GithubException: - log.exception('Error creating a team') + log.exception("Error creating a team") return False -def edit_team(name, - description=None, - privacy=None, - permission=None, - profile="github"): - ''' +def edit_team(name, description=None, privacy=None, permission=None, profile="github"): + """ Updates an existing Github team. name @@ -1242,41 +1213,35 @@ def edit_team(name, salt myminion github.edit_team 'team_name' description='Team description' .. versionadded:: 2016.11.0 - ''' + """ team = get_team(name, profile=profile) if not team: - log.error('Team %s does not exist', name) + log.error("Team %s does not exist", name) return False try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) - team = organization.get_team(team['id']) + organization = client.get_organization(_get_config_value(profile, "org_name")) + team = organization.get_team(team["id"]) parameters = {} if name is not None: - parameters['name'] = name + parameters["name"] = name if description is not None: - parameters['description'] = description + parameters["description"] = description if privacy is not None: - parameters['privacy'] = privacy + parameters["privacy"] = privacy if permission is not None: - parameters['permission'] = permission + parameters["permission"] = permission - team._requester.requestJsonAndCheck( - "PATCH", - team.url, - input=parameters - ) + team._requester.requestJsonAndCheck("PATCH", team.url, input=parameters) return True except UnknownObjectException: - log.exception('Resource not found') + log.exception("Resource not found") return False def remove_team(name, profile="github"): - ''' + """ Remove a github team. name @@ -1292,26 +1257,24 @@ def remove_team(name, profile="github"): salt myminion github.remove_team 'team_name' .. versionadded:: 2016.11.0 - ''' + """ team_info = get_team(name, profile=profile) if not team_info: - log.error('Team %s to be removed does not exist.', name) + log.error("Team %s to be removed does not exist.", name) return False try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) - team = organization.get_team(team_info['id']) + organization = client.get_organization(_get_config_value(profile, "org_name")) + team = organization.get_team(team_info["id"]) team.delete() return list_teams(ignore_cache=True, profile=profile).get(name) is None except github.GithubException: - log.exception('Error deleting a team') + log.exception("Error deleting a team") return False def list_team_repos(team_name, profile="github", ignore_cache=False): - ''' + """ Gets the repo details for a given team as a dict from repo_name to repo details. Note that repo names are always in lower case. @@ -1331,45 +1294,41 @@ def list_team_repos(team_name, profile="github", ignore_cache=False): salt myminion github.list_team_repos 'team_name' .. versionadded:: 2016.11.0 - ''' + """ cached_team = get_team(team_name, profile=profile) if not cached_team: - log.error('Team %s does not exist.', team_name) + log.error("Team %s does not exist.", team_name) return False # Return from cache if available - if cached_team.get('repos') and not ignore_cache: - return cached_team.get('repos') + if cached_team.get("repos") and not ignore_cache: + return cached_team.get("repos") try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) - team = organization.get_team(cached_team['id']) + organization = client.get_organization(_get_config_value(profile, "org_name")) + team = organization.get_team(cached_team["id"]) except UnknownObjectException: - log.exception('Resource not found: %s', cached_team['id']) + log.exception("Resource not found: %s", cached_team["id"]) try: repos = {} for repo in team.get_repos(): - permission = 'pull' + permission = "pull" if repo.permissions.admin: - permission = 'admin' + permission = "admin" elif repo.permissions.push: - permission = 'push' + permission = "push" - repos[repo.name.lower()] = { - 'permission': permission - } - cached_team['repos'] = repos + repos[repo.name.lower()] = {"permission": permission} + cached_team["repos"] = repos return repos except UnknownObjectException: - log.exception('Resource not found: %s', cached_team['id']) + log.exception("Resource not found: %s", cached_team["id"]) return [] def add_team_repo(repo_name, team_name, profile="github", permission=None): - ''' + """ Adds a repository to a team with team_name. repo_name @@ -1395,29 +1354,25 @@ def add_team_repo(repo_name, team_name, profile="github", permission=None): salt myminion github.add_team_repo 'my_repo' 'team_name' .. versionadded:: 2016.11.0 - ''' + """ team = get_team(team_name, profile=profile) if not team: - log.error('Team %s does not exist', team_name) + log.error("Team %s does not exist", team_name) return False try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) - team = organization.get_team(team['id']) + organization = client.get_organization(_get_config_value(profile, "org_name")) + team = organization.get_team(team["id"]) repo = organization.get_repo(repo_name) except UnknownObjectException: - log.exception('Resource not found: %s', team['id']) + log.exception("Resource not found: %s", team["id"]) return False params = None if permission is not None: - params = {'permission': permission} + params = {"permission": permission} headers, data = team._requester.requestJsonAndCheck( - "PUT", - team.url + "/repos/" + repo._identity, - input=params + "PUT", team.url + "/repos/" + repo._identity, input=params ) # Try to refresh cache list_team_repos(team_name, profile=profile, ignore_cache=True) @@ -1425,7 +1380,7 @@ def add_team_repo(repo_name, team_name, profile="github", permission=None): def remove_team_repo(repo_name, team_name, profile="github"): - ''' + """ Removes a repository from a team with team_name. repo_name @@ -1444,27 +1399,27 @@ def remove_team_repo(repo_name, team_name, profile="github"): salt myminion github.remove_team_repo 'my_repo' 'team_name' .. versionadded:: 2016.11.0 - ''' + """ team = get_team(team_name, profile=profile) if not team: - log.error('Team %s does not exist', team_name) + log.error("Team %s does not exist", team_name) return False try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) - team = organization.get_team(team['id']) + organization = client.get_organization(_get_config_value(profile, "org_name")) + team = organization.get_team(team["id"]) repo = organization.get_repo(repo_name) except UnknownObjectException: - log.exception('Resource not found: %s', team['id']) + log.exception("Resource not found: %s", team["id"]) return False team.remove_from_repos(repo) - return repo_name not in list_team_repos(team_name, profile=profile, ignore_cache=True) + return repo_name not in list_team_repos( + team_name, profile=profile, ignore_cache=True + ) def list_team_members(team_name, profile="github", ignore_cache=False): - ''' + """ Gets the names of team members in lower case. team_name @@ -1483,34 +1438,31 @@ def list_team_members(team_name, profile="github", ignore_cache=False): salt myminion github.list_team_members 'team_name' .. versionadded:: 2016.11.0 - ''' + """ cached_team = get_team(team_name, profile=profile) if not cached_team: - log.error('Team %s does not exist.', team_name) + log.error("Team %s does not exist.", team_name) return False # Return from cache if available - if cached_team.get('members') and not ignore_cache: - return cached_team.get('members') + if cached_team.get("members") and not ignore_cache: + return cached_team.get("members") try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) - team = organization.get_team(cached_team['id']) + organization = client.get_organization(_get_config_value(profile, "org_name")) + team = organization.get_team(cached_team["id"]) except UnknownObjectException: - log.exception('Resource not found: %s', cached_team['id']) + log.exception("Resource not found: %s", cached_team["id"]) try: - cached_team['members'] = [member.login.lower() - for member in team.get_members()] - return cached_team['members'] + cached_team["members"] = [member.login.lower() for member in team.get_members()] + return cached_team["members"] except UnknownObjectException: - log.exception('Resource not found: %s', cached_team['id']) + log.exception("Resource not found: %s", cached_team["id"]) return [] def list_members_without_mfa(profile="github", ignore_cache=False): - ''' + """ List all members (in lower case) without MFA turned on. profile @@ -1526,30 +1478,28 @@ def list_members_without_mfa(profile="github", ignore_cache=False): salt myminion github.list_members_without_mfa .. versionadded:: 2016.11.0 - ''' - key = "github.{0}:non_mfa_users".format( - _get_config_value(profile, 'org_name') - ) + """ + key = "github.{0}:non_mfa_users".format(_get_config_value(profile, "org_name")) if key not in __context__ or ignore_cache: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) + organization = client.get_organization(_get_config_value(profile, "org_name")) - filter_key = 'filter' + filter_key = "filter" # Silly hack to see if we're past PyGithub 1.26.0, where the name of # the filter kwarg changed - if hasattr(github.Team.Team, 'membership'): - filter_key = 'filter_' + if hasattr(github.Team.Team, "membership"): + filter_key = "filter_" - __context__[key] = [m.login.lower() for m in - _get_members(organization, {filter_key: '2fa_disabled'})] + __context__[key] = [ + m.login.lower() + for m in _get_members(organization, {filter_key: "2fa_disabled"}) + ] return __context__[key] def is_team_member(name, team_name, profile="github"): - ''' + """ Returns True if the github user is in the team with team_name, or False otherwise. @@ -1569,12 +1519,12 @@ def is_team_member(name, team_name, profile="github"): salt myminion github.is_team_member 'user_name' 'team_name' .. versionadded:: 2016.11.0 - ''' + """ return name.lower() in list_team_members(team_name, profile=profile) def add_team_member(name, team_name, profile="github"): - ''' + """ Adds a team member to a team with team_name. name @@ -1593,20 +1543,18 @@ def add_team_member(name, team_name, profile="github"): salt myminion github.add_team_member 'user_name' 'team_name' .. versionadded:: 2016.11.0 - ''' + """ team = get_team(team_name, profile=profile) if not team: - log.error('Team %s does not exist', team_name) + log.error("Team %s does not exist", team_name) return False try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) - team = organization.get_team(team['id']) + organization = client.get_organization(_get_config_value(profile, "org_name")) + team = organization.get_team(team["id"]) member = client.get_user(name) except UnknownObjectException: - log.exception('Resource not found: %s', team['id']) + log.exception("Resource not found: %s", team["id"]) return False try: @@ -1615,17 +1563,17 @@ def add_team_member(name, team_name, profile="github"): headers, data = team._requester.requestJsonAndCheck( "PUT", team.url + "/memberships/" + member._identity, - input={'role': 'member'}, - parameters={'role': 'member'} + input={"role": "member"}, + parameters={"role": "member"}, ) except github.GithubException: - log.exception('Error in adding a member to a team') + log.exception("Error in adding a member to a team") return False return True def remove_team_member(name, team_name, profile="github"): - ''' + """ Removes a team member from a team with team_name. name @@ -1644,33 +1592,34 @@ def remove_team_member(name, team_name, profile="github"): salt myminion github.remove_team_member 'user_name' 'team_name' .. versionadded:: 2016.11.0 - ''' + """ team = get_team(team_name, profile=profile) if not team: - log.error('Team %s does not exist', team_name) + log.error("Team %s does not exist", team_name) return False try: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) - team = organization.get_team(team['id']) + organization = client.get_organization(_get_config_value(profile, "org_name")) + team = organization.get_team(team["id"]) member = client.get_user(name) except UnknownObjectException: - log.exception('Resource not found: %s', team['id']) + log.exception("Resource not found: %s", team["id"]) return False - if not hasattr(team, 'remove_from_members'): - return (False, 'PyGithub 1.26.0 or greater is required for team ' - 'management, please upgrade.') + if not hasattr(team, "remove_from_members"): + return ( + False, + "PyGithub 1.26.0 or greater is required for team " + "management, please upgrade.", + ) team.remove_from_members(member) return not team.has_in_members(member) def list_teams(profile="github", ignore_cache=False): - ''' + """ Lists all teams with the organization. profile @@ -1686,16 +1635,12 @@ def list_teams(profile="github", ignore_cache=False): salt myminion github.list_teams .. versionadded:: 2016.11.0 - ''' - key = 'github.{0}:teams'.format( - _get_config_value(profile, 'org_name') - ) + """ + key = "github.{0}:teams".format(_get_config_value(profile, "org_name")) if key not in __context__ or ignore_cache: client = _get_client(profile) - organization = client.get_organization( - _get_config_value(profile, 'org_name') - ) + organization = client.get_organization(_get_config_value(profile, "org_name")) teams_data = organization.get_teams() teams = {} for team in teams_data: @@ -1704,27 +1649,29 @@ def list_teams(profile="github", ignore_cache=False): # to use team._rawData instead of team.raw_data, as the latter forces # an API call to retrieve team details again. teams[team.name] = { - 'id': team.id, - 'slug': team.slug, - 'description': team._rawData['description'], - 'permission': team.permission, - 'privacy': team._rawData['privacy'] + "id": team.id, + "slug": team.slug, + "description": team._rawData["description"], + "permission": team.permission, + "privacy": team._rawData["privacy"], } __context__[key] = teams return __context__[key] -def get_prs(repo_name=None, - profile='github', - state='open', - head=None, - base=None, - sort='created', - direction='desc', - output='min', - per_page=None): - ''' +def get_prs( + repo_name=None, + profile="github", + state="open", + head=None, + base=None, + sort="created", + direction="desc", + output="min", + per_page=None, +): + """ Returns information for all pull requests in a given repository, based on the search options provided. @@ -1777,36 +1724,36 @@ def get_prs(repo_name=None, salt myminion github.get_prs salt myminion github.get_prs base=2016.11 - ''' - org_name = _get_config_value(profile, 'org_name') + """ + org_name = _get_config_value(profile, "org_name") if repo_name is None: - repo_name = _get_config_value(profile, 'repo_name') + repo_name = _get_config_value(profile, "repo_name") - action = '/'.join(['repos', org_name, repo_name]) + action = "/".join(["repos", org_name, repo_name]) args = {} # Build API arguments, as necessary. if head: - args['head'] = head + args["head"] = head if base: - args['base'] = base + args["base"] = base if per_page: - args['per_page'] = per_page + args["per_page"] = per_page # Only pass the following API args if they're not the defaults listed. - if state and state != 'open': - args['state'] = state - if sort and sort != 'created': - args['sort'] = sort - if direction and direction != 'desc': - args['direction'] = direction + if state and state != "open": + args["state"] = state + if sort and sort != "created": + args["sort"] = sort + if direction and direction != "desc": + args["direction"] = direction ret = {} - prs = _query(profile, action=action, command='pulls', args=args) + prs = _query(profile, action=action, command="pulls", args=args) for pr_ in prs: - pr_id = pr_.get('id') - if output == 'full': + pr_id = pr_.get("id") + if output == "full": ret[pr_id] = pr_ else: ret[pr_id] = _format_pr(pr_) @@ -1815,71 +1762,77 @@ def get_prs(repo_name=None, def _format_pr(pr_): - ''' + """ Helper function to format API return information into a more manageable and useful dictionary for pull request information. pr_ The pull request to format. - ''' - ret = {'id': pr_.get('id'), - 'pr_number': pr_.get('number'), - 'state': pr_.get('state'), - 'title': pr_.get('title'), - 'user': pr_.get('user').get('login'), - 'html_url': pr_.get('html_url'), - 'base_branch': pr_.get('base').get('ref')} + """ + ret = { + "id": pr_.get("id"), + "pr_number": pr_.get("number"), + "state": pr_.get("state"), + "title": pr_.get("title"), + "user": pr_.get("user").get("login"), + "html_url": pr_.get("html_url"), + "base_branch": pr_.get("base").get("ref"), + } return ret def _format_issue(issue): - ''' + """ Helper function to format API return information into a more manageable and useful dictionary for issue information. issue The issue to format. - ''' - ret = {'id': issue.get('id'), - 'issue_number': issue.get('number'), - 'state': issue.get('state'), - 'title': issue.get('title'), - 'user': issue.get('user').get('login'), - 'html_url': issue.get('html_url')} + """ + ret = { + "id": issue.get("id"), + "issue_number": issue.get("number"), + "state": issue.get("state"), + "title": issue.get("title"), + "user": issue.get("user").get("login"), + "html_url": issue.get("html_url"), + } - assignee = issue.get('assignee') + assignee = issue.get("assignee") if assignee: - assignee = assignee.get('login') + assignee = assignee.get("login") - labels = issue.get('labels') + labels = issue.get("labels") label_names = [] for label in labels: - label_names.append(label.get('name')) + label_names.append(label.get("name")) - milestone = issue.get('milestone') + milestone = issue.get("milestone") if milestone: - milestone = milestone.get('title') + milestone = milestone.get("title") - ret['assignee'] = assignee - ret['labels'] = label_names - ret['milestone'] = milestone + ret["assignee"] = assignee + ret["labels"] = label_names + ret["milestone"] = milestone return ret -def _query(profile, - action=None, - command=None, - args=None, - method='GET', - header_dict=None, - data=None, - url='https://api.github.com/', - per_page=None): - ''' +def _query( + profile, + action=None, + command=None, + args=None, + method="GET", + header_dict=None, + data=None, + url="https://api.github.com/", + per_page=None, +): + """ Make a web call to the GitHub API and deal with paginated results. - ''' + """ if not isinstance(args, dict): args = {} @@ -1887,72 +1840,72 @@ def _query(profile, url += action if command: - url += '/{0}'.format(command) + url += "/{0}".format(command) - log.debug('GitHub URL: %s', url) + log.debug("GitHub URL: %s", url) - if 'access_token' not in args.keys(): - args['access_token'] = _get_config_value(profile, 'token') - if per_page and 'per_page' not in args.keys(): - args['per_page'] = per_page + if "access_token" not in args.keys(): + args["access_token"] = _get_config_value(profile, "token") + if per_page and "per_page" not in args.keys(): + args["per_page"] = per_page if header_dict is None: header_dict = {} - if method != 'POST': - header_dict['Accept'] = 'application/json' + if method != "POST": + header_dict["Accept"] = "application/json" decode = True - if method == 'DELETE': + if method == "DELETE": decode = False # GitHub paginates all queries when returning many items. # Gather all data using multiple queries and handle pagination. complete_result = [] next_page = True - page_number = '' + page_number = "" while next_page is True: if page_number: - args['page'] = page_number - result = salt.utils.http.query(url, - method, - params=args, - data=data, - header_dict=header_dict, - decode=decode, - decode_type='json', - headers=True, - status=True, - text=True, - hide_fields=['access_token'], - opts=__opts__, - ) - log.debug('GitHub Response Status Code: %s', - result['status']) + args["page"] = page_number + result = salt.utils.http.query( + url, + method, + params=args, + data=data, + header_dict=header_dict, + decode=decode, + decode_type="json", + headers=True, + status=True, + text=True, + hide_fields=["access_token"], + opts=__opts__, + ) + log.debug("GitHub Response Status Code: %s", result["status"]) - if result['status'] == 200: - if isinstance(result['dict'], dict): + if result["status"] == 200: + if isinstance(result["dict"], dict): # If only querying for one item, such as a single issue # The GitHub API returns a single dictionary, instead of # A list of dictionaries. In that case, we can return. - return result['dict'] + return result["dict"] - complete_result = complete_result + result['dict'] + complete_result = complete_result + result["dict"] else: raise CommandExecutionError( - 'GitHub Response Error: {0}'.format(result.get('error')) + "GitHub Response Error: {0}".format(result.get("error")) ) try: - link_info = result.get('headers').get('Link').split(',')[0] + link_info = result.get("headers").get("Link").split(",")[0] except AttributeError: # Only one page of data was returned; exit the loop. next_page = False continue - if 'next' in link_info: + if "next" in link_info: # Get the 'next' page number from the Link header. - page_number = link_info.split('>')[0].split('&page=')[1] + page_number = link_info.split(">")[0].split("&page=")[1] else: # Last page already processed; break the loop. next_page = False diff --git a/salt/modules/glance.py b/salt/modules/glance.py index 13b975e2be4..233e0070a3b 100644 --- a/salt/modules/glance.py +++ b/salt/modules/glance.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for handling openstack glance calls. :optdepends: - glanceclient Python adapter @@ -33,16 +33,17 @@ Module for handling openstack glance calls. For example:: salt '*' glance.image_list profile=openstack1 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging +import pprint import re # Import salt libs -from salt.exceptions import ( - SaltInvocationError - ) +from salt.exceptions import SaltInvocationError from salt.ext import six # pylint: disable=import-error @@ -50,6 +51,7 @@ HAS_GLANCE = False try: from glanceclient import client from glanceclient import exc + HAS_GLANCE = True except ImportError: pass @@ -59,43 +61,46 @@ except ImportError: HAS_KEYSTONE = False try: from keystoneclient.v2_0 import client as kstone - #import keystoneclient.apiclient.exceptions as kstone_exc + + # import keystoneclient.apiclient.exceptions as kstone_exc HAS_KEYSTONE = True except ImportError: pass -import logging + logging.basicConfig(level=logging.DEBUG) log = logging.getLogger(__name__) -import pprint def __virtual__(): - ''' + """ Only load this module if glance is installed on this minion. - ''' + """ if HAS_GLANCE: - return 'glance' - return (False, 'The glance execution module cannot be loaded: the glanceclient python library is not available.') + return "glance" + return ( + False, + "The glance execution module cannot be loaded: the glanceclient python library is not available.", + ) __opts__ = {} def _auth(profile=None, api_version=2, **connection_args): - ''' + """ Set up glance credentials, returns `glanceclient.client.Client`. Optional parameter "api_version" defaults to 2. Only intended to be used within glance-enabled modules - ''' - __utils__['versions.warn_until']( - 'Aluminium', + """ + __utils__["versions.warn_until"]( + "Aluminium", ( - 'The glance module has been deprecated and will be removed in {version}. ' - 'Please update to using the glanceng module' + "The glance module has been deprecated and will be removed in {version}. " + "Please update to using the glanceng module" ), ) @@ -105,97 +110,101 @@ def _auth(profile=None, api_version=2, **connection_args): prefix = "keystone." def get(key, default=None): - ''' + """ Checks connection_args, then salt-minion config, falls back to specified default value. - ''' - return connection_args.get('connection_' + key, - __salt__['config.get'](prefix + key, default)) + """ + return connection_args.get( + "connection_" + key, __salt__["config.get"](prefix + key, default) + ) - user = get('user', 'admin') - password = get('password', None) - tenant = get('tenant', 'admin') - tenant_id = get('tenant_id') - auth_url = get('auth_url', 'http://127.0.0.1:35357/v2.0') - insecure = get('insecure', False) - admin_token = get('token') - region = get('region') - ks_endpoint = get('endpoint', 'http://127.0.0.1:9292/') - g_endpoint_url = __salt__['keystone.endpoint_get']('glance', profile) + user = get("user", "admin") + password = get("password", None) + tenant = get("tenant", "admin") + tenant_id = get("tenant_id") + auth_url = get("auth_url", "http://127.0.0.1:35357/v2.0") + insecure = get("insecure", False) + admin_token = get("token") + region = get("region") + ks_endpoint = get("endpoint", "http://127.0.0.1:9292/") + g_endpoint_url = __salt__["keystone.endpoint_get"]("glance", profile) # The trailing 'v2' causes URLs like thise one: # http://127.0.0.1:9292/v2/v1/images - g_endpoint_url = re.sub('/v2', '', g_endpoint_url['internalurl']) + g_endpoint_url = re.sub("/v2", "", g_endpoint_url["internalurl"]) if admin_token and api_version != 1 and not password: # If we had a password we could just # ignore the admin-token and move on... - raise SaltInvocationError('Only can use keystone admin token ' + - 'with Glance API v1') + raise SaltInvocationError( + "Only can use keystone admin token " + "with Glance API v1" + ) elif password: # Can't use the admin-token anyway - kwargs = {'username': user, - 'password': password, - 'tenant_id': tenant_id, - 'auth_url': auth_url, - 'endpoint_url': g_endpoint_url, - 'region_name': region, - 'tenant_name': tenant} + kwargs = { + "username": user, + "password": password, + "tenant_id": tenant_id, + "auth_url": auth_url, + "endpoint_url": g_endpoint_url, + "region_name": region, + "tenant_name": tenant, + } # 'insecure' keyword not supported by all v2.0 keystone clients # this ensures it's only passed in when defined if insecure: - kwargs['insecure'] = True + kwargs["insecure"] = True elif api_version == 1 and admin_token: - kwargs = {'token': admin_token, - 'auth_url': auth_url, - 'endpoint_url': g_endpoint_url} + kwargs = { + "token": admin_token, + "auth_url": auth_url, + "endpoint_url": g_endpoint_url, + } else: - raise SaltInvocationError('No credentials to authenticate with.') + raise SaltInvocationError("No credentials to authenticate with.") if HAS_KEYSTONE: log.debug( - 'Calling keystoneclient.v2_0.client.Client(%s, **%s)', - ks_endpoint, kwargs + "Calling keystoneclient.v2_0.client.Client(%s, **%s)", ks_endpoint, kwargs ) keystone = kstone.Client(**kwargs) - kwargs['token'] = keystone.get_token(keystone.session) + kwargs["token"] = keystone.get_token(keystone.session) # This doesn't realy prevent the password to show up # in the minion log as keystoneclient.session is # logging it anyway when in debug-mode - kwargs.pop('password') + kwargs.pop("password") log.debug( - 'Calling glanceclient.client.Client(%s, %s, **%s)', + "Calling glanceclient.client.Client(%s, %s, **%s)", api_version, g_endpoint_url, - kwargs + kwargs, ) # may raise exc.HTTPUnauthorized, exc.HTTPNotFound # but we deal with those elsewhere return client.Client(api_version, g_endpoint_url, **kwargs) else: - raise NotImplementedError( - "Can't retrieve a auth_token without keystone") + raise NotImplementedError("Can't retrieve a auth_token without keystone") def _add_image(collection, image): - ''' + """ Add image to given dictionary - ''' + """ image_prep = { - 'id': image.id, - 'name': image.name, - 'created_at': image.created_at, - 'file': image.file, - 'min_disk': image.min_disk, - 'min_ram': image.min_ram, - 'owner': image.owner, - 'protected': image.protected, - 'status': image.status, - 'tags': image.tags, - 'updated_at': image.updated_at, - 'visibility': image.visibility, - } + "id": image.id, + "name": image.name, + "created_at": image.created_at, + "file": image.file, + "min_disk": image.min_disk, + "min_ram": image.min_ram, + "owner": image.owner, + "protected": image.protected, + "status": image.status, + "tags": image.tags, + "updated_at": image.updated_at, + "visibility": image.visibility, + } # Those cause AttributeErrors in Icehouse' glanceclient - for attr in ['container_format', 'disk_format', 'size']: + for attr in ["container_format", "disk_format", "size"]: if attr in image: image_prep[attr] = image[attr] if type(collection) is dict: @@ -203,21 +212,24 @@ def _add_image(collection, image): elif type(collection) is list: collection.append(image_prep) else: - msg = '"collection" is {0}'.format(type(collection)) +\ - 'instead of dict or list.' + msg = ( + '"collection" is {0}'.format(type(collection)) + "instead of dict or list." + ) log.error(msg) raise TypeError(msg) return collection -def image_create(name, - location=None, - profile=None, - visibility=None, - container_format='bare', - disk_format='raw', - protected=None,): - ''' +def image_create( + name, + location=None, + profile=None, + visibility=None, + container_format="bare", + disk_format="raw", + protected=None, +): + """ Create an image (glance image-create) CLI Example, old format: @@ -235,39 +247,44 @@ def image_create(name, disk_format=qcow2 container_format=ovf The parameter 'visibility' defaults to 'public' if not specified. - ''' + """ kwargs = {} # valid options for "visibility": - v_list = ['public', 'private'] + v_list = ["public", "private"] # valid options for "container_format": - cf_list = ['ami', 'ari', 'aki', 'bare', 'ovf'] + cf_list = ["ami", "ari", "aki", "bare", "ovf"] # valid options for "disk_format": - df_list = ['ami', 'ari', 'aki', 'vhd', 'vmdk', - 'raw', 'qcow2', 'vdi', 'iso'] + df_list = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vdi", "iso"] - kwargs['copy_from'] = location + kwargs["copy_from"] = location if visibility is not None: if visibility not in v_list: - raise SaltInvocationError('"visibility" needs to be one ' + - 'of the following: {0}'.format(', '.join(v_list))) - elif visibility == 'public': - kwargs['is_public'] = True + raise SaltInvocationError( + '"visibility" needs to be one ' + + "of the following: {0}".format(", ".join(v_list)) + ) + elif visibility == "public": + kwargs["is_public"] = True else: - kwargs['is_public'] = False + kwargs["is_public"] = False else: - kwargs['is_public'] = True + kwargs["is_public"] = True if container_format not in cf_list: - raise SaltInvocationError('"container_format" needs to be ' + - 'one of the following: {0}'.format(', '.join(cf_list))) + raise SaltInvocationError( + '"container_format" needs to be ' + + "one of the following: {0}".format(", ".join(cf_list)) + ) else: - kwargs['container_format'] = container_format + kwargs["container_format"] = container_format if disk_format not in df_list: - raise SaltInvocationError('"disk_format" needs to be one ' + - 'of the following: {0}'.format(', '.join(df_list))) + raise SaltInvocationError( + '"disk_format" needs to be one ' + + "of the following: {0}".format(", ".join(df_list)) + ) else: - kwargs['disk_format'] = disk_format + kwargs["disk_format"] = disk_format if protected is not None: - kwargs['protected'] = protected + kwargs["protected"] = protected # Icehouse's glanceclient doesn't have add_location() and # glanceclient.v2 doesn't implement Client.images.create() # in a usable fashion. Thus we have to use v1 for now. @@ -277,7 +294,7 @@ def image_create(name, def image_delete(id=None, name=None, profile=None): # pylint: disable=C0103 - ''' + """ Delete an image (glance image-delete) CLI Examples: @@ -287,9 +304,9 @@ def image_delete(id=None, name=None, profile=None): # pylint: disable=C0103 salt '*' glance.image_delete c2eb2eb0-53e1-4a80-b990-8ec887eae7df salt '*' glance.image_delete id=c2eb2eb0-53e1-4a80-b990-8ec887eae7df salt '*' glance.image_delete name=f16-jeos - ''' + """ g_client = _auth(profile) - image = {'id': False, 'name': None} + image = {"id": False, "name": None} if name: for image in g_client.images.list(): if image.name == name: @@ -297,34 +314,26 @@ def image_delete(id=None, name=None, profile=None): # pylint: disable=C0103 continue if not id: return { - 'result': False, - 'comment': - 'Unable to resolve image id ' - 'for name {0}'.format(name) - } + "result": False, + "comment": "Unable to resolve image id " "for name {0}".format(name), + } elif not name: - name = image['name'] + name = image["name"] try: g_client.images.delete(id) except exc.HTTPNotFound: - return { - 'result': False, - 'comment': 'No image with ID {0}'.format(id) - } + return {"result": False, "comment": "No image with ID {0}".format(id)} except exc.HTTPForbidden as forbidden: log.error(six.text_type(forbidden)) - return { - 'result': False, - 'comment': six.text_type(forbidden) - } + return {"result": False, "comment": six.text_type(forbidden)} return { - 'result': True, - 'comment': 'Deleted image \'{0}\' ({1}).'.format(name, id), - } + "result": True, + "comment": "Deleted image '{0}' ({1}).".format(name, id), + } def image_show(id=None, name=None, profile=None): # pylint: disable=C0103 - ''' + """ Return details about a specific image (glance image-show) CLI Example: @@ -332,7 +341,7 @@ def image_show(id=None, name=None, profile=None): # pylint: disable=C0103 .. code-block:: bash salt '*' glance.image_show - ''' + """ g_client = _auth(profile) ret = {} if name: @@ -342,25 +351,19 @@ def image_show(id=None, name=None, profile=None): # pylint: disable=C0103 continue if not id: return { - 'result': False, - 'comment': - 'Unable to resolve image ID ' - 'for name \'{0}\''.format(name) - } + "result": False, + "comment": "Unable to resolve image ID " "for name '{0}'".format(name), + } try: image = g_client.images.get(id) except exc.HTTPNotFound: - return { - 'result': False, - 'comment': 'No image with ID {0}'.format(id) - } + return {"result": False, "comment": "No image with ID {0}".format(id)} pformat = pprint.PrettyPrinter(indent=4).pformat - log.debug('Properties of image {0}:\n{1}'.format( - image.name, pformat(image))) + log.debug("Properties of image {0}:\n{1}".format(image.name, pformat(image))) schema = image_schema(profile=profile) if len(schema.keys()) == 1: - schema = schema['image'] + schema = schema["image"] for key in schema: if key in image: ret[key] = image[key] @@ -368,7 +371,7 @@ def image_show(id=None, name=None, profile=None): # pylint: disable=C0103 def image_list(id=None, profile=None, name=None): # pylint: disable=C0103 - ''' + """ Return a list of available images (glance image-list) CLI Example: @@ -376,7 +379,7 @@ def image_list(id=None, profile=None, name=None): # pylint: disable=C0103 .. code-block:: bash salt '*' glance.image_list - ''' + """ g_client = _auth(profile) ret = [] for image in g_client.images.list(): @@ -387,21 +390,20 @@ def image_list(id=None, profile=None, name=None): # pylint: disable=C0103 _add_image(ret, image) return ret if name == image.name: - if name in ret and __salt__['salt_version.less_than']('Boron'): + if name in ret and __salt__["salt_version.less_than"]("Boron"): # Not really worth an exception return { - 'result': False, - 'comment': - 'More than one image with ' - 'name "{0}"'.format(name) - } + "result": False, + "comment": "More than one image with " + 'name "{0}"'.format(name), + } _add_image(ret, image) - log.debug('Returning images: {0}'.format(ret)) + log.debug("Returning images: {0}".format(ret)) return ret def image_schema(profile=None): - ''' + """ Returns names and descriptions of the schema "image"'s properties for this profile's instance of glance @@ -410,12 +412,12 @@ def image_schema(profile=None): .. code-block:: bash salt '*' glance.image_schema - ''' - return schema_get('image', profile) + """ + return schema_get("image", profile) def image_update(id=None, name=None, profile=None, **kwargs): # pylint: disable=C0103 - ''' + """ Update properties of given image. Known to work for: - min_ram (in MB) @@ -428,24 +430,22 @@ def image_update(id=None, name=None, profile=None, **kwargs): # pylint: disable salt '*' glance.image_update id=c2eb2eb0-53e1-4a80-b990-8ec887eae7df salt '*' glance.image_update name=f16-jeos - ''' + """ if id: image = image_show(id=id, profile=profile) - if 'result' in image and not image['result']: + if "result" in image and not image["result"]: return image elif len(image) == 1: image = image.values()[0] elif name: img_list = image_list(name=name, profile=profile) - if img_list is dict and 'result' in img_list: + if img_list is dict and "result" in img_list: return img_list elif len(img_list) == 0: return { - 'result': False, - 'comment': - 'No image with name \'{0}\' ' - 'found.'.format(name) - } + "result": False, + "comment": "No image with name '{0}' " "found.".format(name), + } elif len(img_list) == 1: try: image = img_list[0] @@ -453,21 +453,21 @@ def image_update(id=None, name=None, profile=None, **kwargs): # pylint: disable image = img_list[name] else: raise SaltInvocationError - log.debug('Found image:\n{0}'.format(image)) + log.debug("Found image:\n{0}".format(image)) to_update = {} for key, value in kwargs.items(): - if key.startswith('_'): + if key.startswith("_"): continue if key not in image or image[key] != value: - log.debug('add <{0}={1}> to to_update'.format(key, value)) + log.debug("add <{0}={1}> to to_update".format(key, value)) to_update[key] = value g_client = _auth(profile) - updated = g_client.images.update(image['id'], **to_update) + updated = g_client.images.update(image["id"], **to_update) return updated def schema_get(name, profile=None): - ''' + """ Known valid names of schemas are: - image - images @@ -479,19 +479,18 @@ def schema_get(name, profile=None): .. code-block:: bash salt '*' glance.schema_get name=f16-jeos - ''' + """ g_client = _auth(profile) pformat = pprint.PrettyPrinter(indent=4).pformat schema_props = {} for prop in g_client.schemas.get(name).properties: schema_props[prop.name] = prop.description - log.debug('Properties of schema {0}:\n{1}'.format( - name, pformat(schema_props))) + log.debug("Properties of schema {0}:\n{1}".format(name, pformat(schema_props))) return {name: schema_props} def _item_list(profile=None): - ''' + """ Template for writing list functions Return a list of available items (glance items-list) @@ -500,12 +499,12 @@ def _item_list(profile=None): .. code-block:: bash salt '*' glance.item_list - ''' + """ g_client = _auth(profile) ret = [] for item in g_client.items.list(): ret.append(item.__dict__) - #ret[item.name] = { + # ret[item.name] = { # 'name': item.name, # } return ret diff --git a/salt/modules/glanceng.py b/salt/modules/glanceng.py index e71a9f86230..9477b015801 100644 --- a/salt/modules/glanceng.py +++ b/salt/modules/glanceng.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Glance module for interacting with OpenStack Glance .. versionadded:: 2018.3.0 @@ -24,34 +24,38 @@ Example configuration project_domain_name: myproject auth_url: https://example.org:5000/v3 identity_api_version: 3 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals HAS_SHADE = False try: import shade + HAS_SHADE = True except ImportError: pass -__virtualname__ = 'glanceng' +__virtualname__ = "glanceng" def __virtual__(): - ''' + """ Only load this module if shade python module is installed - ''' + """ if HAS_SHADE: return __virtualname__ - return (False, 'The glanceng execution module failed to load: shade python module is not available') + return ( + False, + "The glanceng execution module failed to load: shade python module is not available", + ) def compare_changes(obj, **kwargs): - ''' + """ Compare two dicts returning only keys that exist in the first dict and are different in the second one - ''' + """ changes = {} for k, v in obj.items(): if k in kwargs: @@ -61,52 +65,52 @@ def compare_changes(obj, **kwargs): def _clean_kwargs(keep_name=False, **kwargs): - ''' + """ Sanatize the the arguments for use with shade - ''' - if 'name' in kwargs and not keep_name: - kwargs['name_or_id'] = kwargs.pop('name') + """ + if "name" in kwargs and not keep_name: + kwargs["name_or_id"] = kwargs.pop("name") - return __utils__['args.clean_kwargs'](**kwargs) + return __utils__["args.clean_kwargs"](**kwargs) def setup_clouds(auth=None): - ''' + """ Call functions to create Shade cloud objects in __context__ to take advantage of Shade's in-memory caching across several states - ''' + """ get_operator_cloud(auth) get_openstack_cloud(auth) def get_operator_cloud(auth=None): - ''' + """ Return an operator_cloud - ''' + """ if auth is None: - auth = __salt__['config.option']('glance', {}) - if 'shade_opcloud' in __context__: - if __context__['shade_opcloud'].auth == auth: - return __context__['shade_opcloud'] - __context__['shade_opcloud'] = shade.operator_cloud(**auth) - return __context__['shade_opcloud'] + auth = __salt__["config.option"]("glance", {}) + if "shade_opcloud" in __context__: + if __context__["shade_opcloud"].auth == auth: + return __context__["shade_opcloud"] + __context__["shade_opcloud"] = shade.operator_cloud(**auth) + return __context__["shade_opcloud"] def get_openstack_cloud(auth=None): - ''' + """ Return an openstack_cloud - ''' + """ if auth is None: - auth = __salt__['config.option']('glance', {}) - if 'shade_oscloud' in __context__: - if __context__['shade_oscloud'].auth == auth: - return __context__['shade_oscloud'] - __context__['shade_oscloud'] = shade.openstack_cloud(**auth) - return __context__['shade_oscloud'] + auth = __salt__["config.option"]("glance", {}) + if "shade_oscloud" in __context__: + if __context__["shade_oscloud"].auth == auth: + return __context__["shade_oscloud"] + __context__["shade_oscloud"] = shade.openstack_cloud(**auth) + return __context__["shade_oscloud"] def image_create(auth=None, **kwargs): - ''' + """ Create an image CLI Example: @@ -115,14 +119,14 @@ def image_create(auth=None, **kwargs): salt '*' glanceng.image_create name=cirros file=cirros.raw disk_format=raw salt '*' glanceng.image_create name=cirros file=cirros.raw disk_format=raw hw_scsi_model=virtio-scsi hw_disk_bus=scsi - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_image(**kwargs) def image_delete(auth=None, **kwargs): - ''' + """ Delete an image CLI Example: @@ -131,14 +135,14 @@ def image_delete(auth=None, **kwargs): salt '*' glanceng.image_delete name=image1 salt '*' glanceng.image_delete name=0e4febc2a5ab4f2c8f374b054162506d - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_image(**kwargs) def image_list(auth=None, **kwargs): - ''' + """ List images CLI Example: @@ -147,14 +151,14 @@ def image_list(auth=None, **kwargs): salt '*' glanceng.image_list salt '*' glanceng.image_list - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_images(**kwargs) def image_search(auth=None, **kwargs): - ''' + """ Search for images CLI Example: @@ -163,14 +167,14 @@ def image_search(auth=None, **kwargs): salt '*' glanceng.image_search name=image1 salt '*' glanceng.image_search - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.search_images(**kwargs) def image_get(auth=None, **kwargs): - ''' + """ Get a single image CLI Example: @@ -179,14 +183,14 @@ def image_get(auth=None, **kwargs): salt '*' glanceng.image_get name=image1 salt '*' glanceng.image_get name=0e4febc2a5ab4f2c8f374b054162506d - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_image(**kwargs) def update_image_properties(auth=None, **kwargs): - ''' + """ Update properties for an image CLI Example: @@ -195,7 +199,7 @@ def update_image_properties(auth=None, **kwargs): salt '*' glanceng.update_image_properties name=image1 hw_scsi_model=virtio-scsi hw_disk_bus=scsi salt '*' glanceng.update_image_properties name=0e4febc2a5ab4f2c8f374b054162506d min_ram=1024 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.update_image_properties(**kwargs) diff --git a/salt/modules/glassfish.py b/salt/modules/glassfish.py index 1f9c85f30a7..44df1d3cbba 100644 --- a/salt/modules/glassfish.py +++ b/salt/modules/glassfish.py @@ -1,55 +1,67 @@ # -*- coding: utf-8 -*- -''' +""" Module for working with the Glassfish/Payara 4.x management API .. versionadded:: Carbon :depends: requests -''' +""" from __future__ import absolute_import, print_function, unicode_literals - import salt.defaults.exitcodes import salt.utils.json from salt.exceptions import CommandExecutionError +from salt.ext.six.moves.urllib.parse import ( # pylint: disable=no-name-in-module + quote, + unquote, +) -from salt.ext.six.moves.urllib.parse import quote, unquote # pylint: disable=no-name-in-module try: import requests + HAS_LIBS = True except ImportError: HAS_LIBS = False -__virtualname__ = 'glassfish' +__virtualname__ = "glassfish" # Default server -DEFAULT_SERVER = {'ssl': False, 'url': 'localhost', 'port': 4848, 'user': None, 'password': None} +DEFAULT_SERVER = { + "ssl": False, + "url": "localhost", + "port": 4848, + "user": None, + "password": None, +} def __virtual__(): - ''' + """ Only load if requests is installed - ''' + """ if HAS_LIBS: return __virtualname__ else: - return False, 'The "{0}" module could not be loaded: ' \ - '"requests" is not installed.'.format(__virtualname__) + return ( + False, + 'The "{0}" module could not be loaded: ' + '"requests" is not installed.'.format(__virtualname__), + ) def _get_headers(): - ''' + """ Return fixed dict with headers (JSON data + mandatory "Requested by" header) - ''' + """ return { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'X-Requested-By': 'GlassFish REST HTML interface' + "Accept": "application/json", + "Content-Type": "application/json", + "X-Requested-By": "GlassFish REST HTML interface", } def _get_auth(username, password): - ''' + """ Returns the HTTP auth header - ''' + """ if username and password: return requests.auth.HTTPBasicAuth(username, password) else: @@ -57,161 +69,161 @@ def _get_auth(username, password): def _get_url(ssl, url, port, path): - ''' + """ Returns the URL of the endpoint - ''' + """ if ssl: - return 'https://{0}:{1}/management/domain/{2}'.format(url, port, path) + return "https://{0}:{1}/management/domain/{2}".format(url, port, path) else: - return 'http://{0}:{1}/management/domain/{2}'.format(url, port, path) + return "http://{0}:{1}/management/domain/{2}".format(url, port, path) def _get_server(server): - ''' + """ Returns the server information if provided, or the defaults - ''' + """ return server if server else DEFAULT_SERVER def _clean_data(data): - ''' + """ Removes SaltStack params from **kwargs - ''' + """ for key in list(data): - if key.startswith('__pub'): + if key.startswith("__pub"): del data[key] return data def _api_response(response): - ''' + """ Check response status code + success_code returned by glassfish - ''' + """ if response.status_code == 404: - __context__['retcode'] = salt.defaults.exitcodes.SALT_BUILD_FAIL - raise CommandExecutionError('Element doesn\'t exists') + __context__["retcode"] = salt.defaults.exitcodes.SALT_BUILD_FAIL + raise CommandExecutionError("Element doesn't exists") if response.status_code == 401: - __context__['retcode'] = salt.defaults.exitcodes.SALT_BUILD_FAIL - raise CommandExecutionError('Bad username or password') + __context__["retcode"] = salt.defaults.exitcodes.SALT_BUILD_FAIL + raise CommandExecutionError("Bad username or password") elif response.status_code == 200 or response.status_code == 500: try: data = salt.utils.json.loads(response.content) - if data['exit_code'] != 'SUCCESS': - __context__['retcode'] = salt.defaults.exitcodes.SALT_BUILD_FAIL - raise CommandExecutionError(data['message']) + if data["exit_code"] != "SUCCESS": + __context__["retcode"] = salt.defaults.exitcodes.SALT_BUILD_FAIL + raise CommandExecutionError(data["message"]) return data except ValueError: - __context__['retcode'] = salt.defaults.exitcodes.SALT_BUILD_FAIL - raise CommandExecutionError('The server returned no data') + __context__["retcode"] = salt.defaults.exitcodes.SALT_BUILD_FAIL + raise CommandExecutionError("The server returned no data") else: response.raise_for_status() def _api_get(path, server=None): - ''' + """ Do a GET request to the API - ''' + """ server = _get_server(server) response = requests.get( - url=_get_url(server['ssl'], server['url'], server['port'], path), - auth=_get_auth(server['user'], server['password']), - headers=_get_headers(), - verify=False + url=_get_url(server["ssl"], server["url"], server["port"], path), + auth=_get_auth(server["user"], server["password"]), + headers=_get_headers(), + verify=False, ) return _api_response(response) def _api_post(path, data, server=None): - ''' + """ Do a POST request to the API - ''' + """ server = _get_server(server) response = requests.post( - url=_get_url(server['ssl'], server['url'], server['port'], path), - auth=_get_auth(server['user'], server['password']), - headers=_get_headers(), - data=salt.utils.json.dumps(data), - verify=False + url=_get_url(server["ssl"], server["url"], server["port"], path), + auth=_get_auth(server["user"], server["password"]), + headers=_get_headers(), + data=salt.utils.json.dumps(data), + verify=False, ) return _api_response(response) def _api_delete(path, data, server=None): - ''' + """ Do a DELETE request to the API - ''' + """ server = _get_server(server) response = requests.delete( - url=_get_url(server['ssl'], server['url'], server['port'], path), - auth=_get_auth(server['user'], server['password']), - headers=_get_headers(), - params=data, - verify=False + url=_get_url(server["ssl"], server["url"], server["port"], path), + auth=_get_auth(server["user"], server["password"]), + headers=_get_headers(), + params=data, + verify=False, ) return _api_response(response) # "Middle layer": uses _api_* functions to enum/get/create/update/delete elements def _enum_elements(name, server=None): - ''' + """ Enum elements - ''' + """ elements = [] data = _api_get(name, server) - if any(data['extraProperties']['childResources']): - for element in data['extraProperties']['childResources']: + if any(data["extraProperties"]["childResources"]): + for element in data["extraProperties"]["childResources"]: elements.append(element) return elements return None def _get_element_properties(name, element_type, server=None): - ''' + """ Get an element's properties - ''' + """ properties = {} - data = _api_get('{0}/{1}/property'.format(element_type, name), server) + data = _api_get("{0}/{1}/property".format(element_type, name), server) # Get properties into a dict - if any(data['extraProperties']['properties']): - for element in data['extraProperties']['properties']: - properties[element['name']] = element['value'] + if any(data["extraProperties"]["properties"]): + for element in data["extraProperties"]["properties"]: + properties[element["name"]] = element["value"] return properties return {} def _get_element(name, element_type, server=None, with_properties=True): - ''' + """ Get an element with or without properties - ''' + """ element = {} - name = quote(name, safe='') - data = _api_get('{0}/{1}'.format(element_type, name), server) + name = quote(name, safe="") + data = _api_get("{0}/{1}".format(element_type, name), server) # Format data, get properties if asked, and return the whole thing - if any(data['extraProperties']['entity']): - for key, value in data['extraProperties']['entity'].items(): + if any(data["extraProperties"]["entity"]): + for key, value in data["extraProperties"]["entity"].items(): element[key] = value if with_properties: - element['properties'] = _get_element_properties(name, element_type) + element["properties"] = _get_element_properties(name, element_type) return element return None def _create_element(name, element_type, data, server=None): - ''' + """ Create a new element - ''' + """ # Define property and id from name and properties + remove SaltStack parameters - if 'properties' in data: - data['property'] = '' - for key, value in data['properties'].items(): - if not data['property']: - data['property'] += '{0}={1}'.format(key, value.replace(':', '\\:')) + if "properties" in data: + data["property"] = "" + for key, value in data["properties"].items(): + if not data["property"]: + data["property"] += "{0}={1}".format(key, value.replace(":", "\\:")) else: - data['property'] += ':{0}={1}'.format(key, value.replace(':', '\\:')) - del data['properties'] + data["property"] += ":{0}={1}".format(key, value.replace(":", "\\:")) + del data["properties"] # Send request _api_post(element_type, _clean_data(data), server) @@ -219,19 +231,19 @@ def _create_element(name, element_type, data, server=None): def _update_element(name, element_type, data, server=None): - ''' + """ Update an element, including its properties - ''' + """ # Urlencode the name (names may have slashes) - name = quote(name, safe='') + name = quote(name, safe="") # Update properties first - if 'properties' in data: + if "properties" in data: properties = [] - for key, value in data['properties'].items(): - properties.append({'name': key, 'value': value}) - _api_post('{0}/{1}/property'.format(element_type, name), properties, server) - del data['properties'] + for key, value in data["properties"].items(): + properties.append({"name": key, "value": value}) + _api_post("{0}/{1}/property".format(element_type, name), properties, server) + del data["properties"] # If the element only contained properties if not data: @@ -242,67 +254,67 @@ def _update_element(name, element_type, data, server=None): if update_data: update_data.update(data) else: - __context__['retcode'] = salt.defaults.exitcodes.SALT_BUILD_FAIL - raise CommandExecutionError('Cannot update {0}'.format(name)) + __context__["retcode"] = salt.defaults.exitcodes.SALT_BUILD_FAIL + raise CommandExecutionError("Cannot update {0}".format(name)) # Finally, update the element - _api_post('{0}/{1}'.format(element_type, name), _clean_data(update_data), server) + _api_post("{0}/{1}".format(element_type, name), _clean_data(update_data), server) return unquote(name) def _delete_element(name, element_type, data, server=None): - ''' + """ Delete an element - ''' - _api_delete('{0}/{1}'.format(element_type, quote(name, safe='')), data, server) + """ + _api_delete("{0}/{1}".format(element_type, quote(name, safe="")), data, server) return name # Connector connection pools def enum_connector_c_pool(server=None): - ''' + """ Enum connection pools - ''' - return _enum_elements('resources/connector-connection-pool', server) + """ + return _enum_elements("resources/connector-connection-pool", server) def get_connector_c_pool(name, server=None): - ''' + """ Get a specific connection pool - ''' - return _get_element(name, 'resources/connector-connection-pool', server) + """ + return _get_element(name, "resources/connector-connection-pool", server) def create_connector_c_pool(name, server=None, **kwargs): - ''' + """ Create a connection pool - ''' + """ defaults = { - 'connectionDefinitionName': 'javax.jms.ConnectionFactory', - 'resourceAdapterName': 'jmsra', - 'associateWithThread': False, - 'connectionCreationRetryAttempts': 0, - 'connectionCreationRetryIntervalInSeconds': 0, - 'connectionLeakReclaim': False, - 'connectionLeakTimeoutInSeconds': 0, - 'description': '', - 'failAllConnections': False, - 'id': name, - 'idleTimeoutInSeconds': 300, - 'isConnectionValidationRequired': False, - 'lazyConnectionAssociation': False, - 'lazyConnectionEnlistment': False, - 'matchConnections': True, - 'maxConnectionUsageCount': 0, - 'maxPoolSize': 32, - 'maxWaitTimeInMillis': 60000, - 'ping': False, - 'poolResizeQuantity': 2, - 'pooling': True, - 'steadyPoolSize': 8, - 'target': 'server', - 'transactionSupport': '', - 'validateAtmostOncePeriodInSeconds': 0 + "connectionDefinitionName": "javax.jms.ConnectionFactory", + "resourceAdapterName": "jmsra", + "associateWithThread": False, + "connectionCreationRetryAttempts": 0, + "connectionCreationRetryIntervalInSeconds": 0, + "connectionLeakReclaim": False, + "connectionLeakTimeoutInSeconds": 0, + "description": "", + "failAllConnections": False, + "id": name, + "idleTimeoutInSeconds": 300, + "isConnectionValidationRequired": False, + "lazyConnectionAssociation": False, + "lazyConnectionEnlistment": False, + "matchConnections": True, + "maxConnectionUsageCount": 0, + "maxPoolSize": 32, + "maxWaitTimeInMillis": 60000, + "ping": False, + "poolResizeQuantity": 2, + "pooling": True, + "steadyPoolSize": 8, + "target": "server", + "transactionSupport": "", + "validateAtmostOncePeriodInSeconds": 0, } # Data = defaults + merge kwargs + remove salt @@ -310,124 +322,126 @@ def create_connector_c_pool(name, server=None, **kwargs): data.update(kwargs) # Check TransactionSupport against acceptable values - if data['transactionSupport'] and data['transactionSupport'] not in ( - 'XATransaction', - 'LocalTransaction', - 'NoTransaction' - ): - raise CommandExecutionError('Invalid transaction support') + if data["transactionSupport"] and data["transactionSupport"] not in ( + "XATransaction", + "LocalTransaction", + "NoTransaction", + ): + raise CommandExecutionError("Invalid transaction support") - return _create_element(name, 'resources/connector-connection-pool', data, server) + return _create_element(name, "resources/connector-connection-pool", data, server) def update_connector_c_pool(name, server=None, **kwargs): - ''' + """ Update a connection pool - ''' - if 'transactionSupport' in kwargs and kwargs['transactionSupport'] not in ( - 'XATransaction', - 'LocalTransaction', - 'NoTransaction' - ): - raise CommandExecutionError('Invalid transaction support') - return _update_element(name, 'resources/connector-connection-pool', kwargs, server) + """ + if "transactionSupport" in kwargs and kwargs["transactionSupport"] not in ( + "XATransaction", + "LocalTransaction", + "NoTransaction", + ): + raise CommandExecutionError("Invalid transaction support") + return _update_element(name, "resources/connector-connection-pool", kwargs, server) -def delete_connector_c_pool(name, target='server', cascade=True, server=None): - ''' +def delete_connector_c_pool(name, target="server", cascade=True, server=None): + """ Delete a connection pool - ''' - data = {'target': target, 'cascade': cascade} - return _delete_element(name, 'resources/connector-connection-pool', data, server) + """ + data = {"target": target, "cascade": cascade} + return _delete_element(name, "resources/connector-connection-pool", data, server) # Connector resources def enum_connector_resource(server=None): - ''' + """ Enum connection resources - ''' - return _enum_elements('resources/connector-resource', server) + """ + return _enum_elements("resources/connector-resource", server) def get_connector_resource(name, server=None): - ''' + """ Get a specific connection resource - ''' - return _get_element(name, 'resources/connector-resource', server) + """ + return _get_element(name, "resources/connector-resource", server) def create_connector_resource(name, server=None, **kwargs): - ''' + """ Create a connection resource - ''' + """ defaults = { - 'description': '', - 'enabled': True, - 'id': name, - 'poolName': '', - 'objectType': 'user', - 'target': 'server' + "description": "", + "enabled": True, + "id": name, + "poolName": "", + "objectType": "user", + "target": "server", } # Data = defaults + merge kwargs + poolname data = defaults data.update(kwargs) - if not data['poolName']: - raise CommandExecutionError('No pool name!') + if not data["poolName"]: + raise CommandExecutionError("No pool name!") # Fix for lowercase vs camelCase naming differences for key, value in list(data.items()): del data[key] data[key.lower()] = value - return _create_element(name, 'resources/connector-resource', data, server) + return _create_element(name, "resources/connector-resource", data, server) def update_connector_resource(name, server=None, **kwargs): - ''' + """ Update a connection resource - ''' + """ # You're not supposed to update jndiName, if you do so, it will crash, silently - if 'jndiName' in kwargs: - del kwargs['jndiName'] - return _update_element(name, 'resources/connector-resource', kwargs, server) + if "jndiName" in kwargs: + del kwargs["jndiName"] + return _update_element(name, "resources/connector-resource", kwargs, server) -def delete_connector_resource(name, target='server', server=None): - ''' +def delete_connector_resource(name, target="server", server=None): + """ Delete a connection resource - ''' - return _delete_element(name, 'resources/connector-resource', {'target': target}, server) + """ + return _delete_element( + name, "resources/connector-resource", {"target": target}, server + ) # JMS Destinations def enum_admin_object_resource(server=None): - ''' + """ Enum JMS destinations - ''' - return _enum_elements('resources/admin-object-resource', server) + """ + return _enum_elements("resources/admin-object-resource", server) def get_admin_object_resource(name, server=None): - ''' + """ Get a specific JMS destination - ''' - return _get_element(name, 'resources/admin-object-resource', server) + """ + return _get_element(name, "resources/admin-object-resource", server) def create_admin_object_resource(name, server=None, **kwargs): - ''' + """ Create a JMS destination - ''' + """ defaults = { - 'description': '', - 'className': 'com.sun.messaging.Queue', - 'enabled': True, - 'id': name, - 'resAdapter': 'jmsra', - 'resType': 'javax.jms.Queue', - 'target': 'server' + "description": "", + "className": "com.sun.messaging.Queue", + "enabled": True, + "id": name, + "resAdapter": "jmsra", + "resType": "javax.jms.Queue", + "target": "server", } # Data = defaults + merge kwargs + poolname @@ -435,102 +449,106 @@ def create_admin_object_resource(name, server=None, **kwargs): data.update(kwargs) # ClassName isn't optional, even if the API says so - if data['resType'] == 'javax.jms.Queue': - data['className'] = 'com.sun.messaging.Queue' - elif data['resType'] == 'javax.jms.Topic': - data['className'] = 'com.sun.messaging.Topic' + if data["resType"] == "javax.jms.Queue": + data["className"] = "com.sun.messaging.Queue" + elif data["resType"] == "javax.jms.Topic": + data["className"] = "com.sun.messaging.Topic" else: - raise CommandExecutionError('resType should be "javax.jms.Queue" or "javax.jms.Topic"!') + raise CommandExecutionError( + 'resType should be "javax.jms.Queue" or "javax.jms.Topic"!' + ) - if data['resAdapter'] != 'jmsra': + if data["resAdapter"] != "jmsra": raise CommandExecutionError('resAdapter should be "jmsra"!') # Fix for lowercase vs camelCase naming differences - if 'resType' in data: - data['restype'] = data['resType'] - del data['resType'] - if 'className' in data: - data['classname'] = data['className'] - del data['className'] + if "resType" in data: + data["restype"] = data["resType"] + del data["resType"] + if "className" in data: + data["classname"] = data["className"] + del data["className"] - return _create_element(name, 'resources/admin-object-resource', data, server) + return _create_element(name, "resources/admin-object-resource", data, server) def update_admin_object_resource(name, server=None, **kwargs): - ''' + """ Update a JMS destination - ''' - if 'jndiName' in kwargs: - del kwargs['jndiName'] - return _update_element(name, 'resources/admin-object-resource', kwargs, server) + """ + if "jndiName" in kwargs: + del kwargs["jndiName"] + return _update_element(name, "resources/admin-object-resource", kwargs, server) -def delete_admin_object_resource(name, target='server', server=None): - ''' +def delete_admin_object_resource(name, target="server", server=None): + """ Delete a JMS destination - ''' - return _delete_element(name, 'resources/admin-object-resource', {'target': target}, server) + """ + return _delete_element( + name, "resources/admin-object-resource", {"target": target}, server + ) # JDBC Pools def enum_jdbc_connection_pool(server=None): - ''' + """ Enum JDBC pools - ''' - return _enum_elements('resources/jdbc-connection-pool', server) + """ + return _enum_elements("resources/jdbc-connection-pool", server) def get_jdbc_connection_pool(name, server=None): - ''' + """ Get a specific JDBC pool - ''' - return _get_element(name, 'resources/jdbc-connection-pool', server) + """ + return _get_element(name, "resources/jdbc-connection-pool", server) def create_jdbc_connection_pool(name, server=None, **kwargs): - ''' + """ Create a connection resource - ''' + """ defaults = { - 'allowNonComponentCallers': False, - 'associateWithThread': False, - 'connectionCreationRetryAttempts': '0', - 'connectionCreationRetryIntervalInSeconds': '10', - 'connectionLeakReclaim': False, - 'connectionLeakTimeoutInSeconds': '0', - 'connectionValidationMethod': 'table', - 'datasourceClassname': '', - 'description': '', - 'driverClassname': '', - 'failAllConnections': False, - 'idleTimeoutInSeconds': '300', - 'initSql': '', - 'isConnectionValidationRequired': False, - 'isIsolationLevelGuaranteed': True, - 'lazyConnectionAssociation': False, - 'lazyConnectionEnlistment': False, - 'matchConnections': False, - 'maxConnectionUsageCount': '0', - 'maxPoolSize': '32', - 'maxWaitTimeInMillis': 60000, - 'name': name, - 'nonTransactionalConnections': False, - 'ping': False, - 'poolResizeQuantity': '2', - 'pooling': True, - 'resType': '', - 'sqlTraceListeners': '', - 'statementCacheSize': '0', - 'statementLeakReclaim': False, - 'statementLeakTimeoutInSeconds': '0', - 'statementTimeoutInSeconds': '-1', - 'steadyPoolSize': '8', - 'target': 'server', - 'transactionIsolationLevel': '', - 'validateAtmostOncePeriodInSeconds': '0', - 'validationClassname': '', - 'validationTableName': '', - 'wrapJdbcObjects': True + "allowNonComponentCallers": False, + "associateWithThread": False, + "connectionCreationRetryAttempts": "0", + "connectionCreationRetryIntervalInSeconds": "10", + "connectionLeakReclaim": False, + "connectionLeakTimeoutInSeconds": "0", + "connectionValidationMethod": "table", + "datasourceClassname": "", + "description": "", + "driverClassname": "", + "failAllConnections": False, + "idleTimeoutInSeconds": "300", + "initSql": "", + "isConnectionValidationRequired": False, + "isIsolationLevelGuaranteed": True, + "lazyConnectionAssociation": False, + "lazyConnectionEnlistment": False, + "matchConnections": False, + "maxConnectionUsageCount": "0", + "maxPoolSize": "32", + "maxWaitTimeInMillis": 60000, + "name": name, + "nonTransactionalConnections": False, + "ping": False, + "poolResizeQuantity": "2", + "pooling": True, + "resType": "", + "sqlTraceListeners": "", + "statementCacheSize": "0", + "statementLeakReclaim": False, + "statementLeakTimeoutInSeconds": "0", + "statementTimeoutInSeconds": "-1", + "steadyPoolSize": "8", + "target": "server", + "transactionIsolationLevel": "", + "validateAtmostOncePeriodInSeconds": "0", + "validationClassname": "", + "validationTableName": "", + "wrapJdbcObjects": True, } # Data = defaults + merge kwargs + poolname @@ -538,140 +556,140 @@ def create_jdbc_connection_pool(name, server=None, **kwargs): data.update(kwargs) # Check resType against acceptable values - if data['resType'] not in ( - 'javax.sql.DataSource', - 'javax.sql.XADataSource', - 'javax.sql.ConnectionPoolDataSource', - 'java.sql.Driver' - ): - raise CommandExecutionError('Invalid resource type') + if data["resType"] not in ( + "javax.sql.DataSource", + "javax.sql.XADataSource", + "javax.sql.ConnectionPoolDataSource", + "java.sql.Driver", + ): + raise CommandExecutionError("Invalid resource type") # Check connectionValidationMethod against acceptable velues - if data['connectionValidationMethod'] not in ( - 'auto-commit', - 'meta-data', - 'table', - 'custom-validation' - ): - raise CommandExecutionError('Invalid connection validation method') + if data["connectionValidationMethod"] not in ( + "auto-commit", + "meta-data", + "table", + "custom-validation", + ): + raise CommandExecutionError("Invalid connection validation method") - if data['transactionIsolationLevel'] \ - and data['transactionIsolationLevel'] not in ( - 'read-uncommitted', - 'read-committed', - 'repeatable-read', - 'serializable' - ): - raise CommandExecutionError('Invalid transaction isolation level') + if data["transactionIsolationLevel"] and data["transactionIsolationLevel"] not in ( + "read-uncommitted", + "read-committed", + "repeatable-read", + "serializable", + ): + raise CommandExecutionError("Invalid transaction isolation level") - if not data['datasourceClassname'] \ - and data['resType'] in ( - 'javax.sql.DataSource', - 'javax.sql.ConnectionPoolDataSource', - 'javax.sql.XADataSource' - ): - raise CommandExecutionError('No datasource class name while using datasource resType') - if not data['driverClassname'] and data['resType'] == 'java.sql.Driver': - raise CommandExecutionError('No driver class nime while using driver resType') + if not data["datasourceClassname"] and data["resType"] in ( + "javax.sql.DataSource", + "javax.sql.ConnectionPoolDataSource", + "javax.sql.XADataSource", + ): + raise CommandExecutionError( + "No datasource class name while using datasource resType" + ) + if not data["driverClassname"] and data["resType"] == "java.sql.Driver": + raise CommandExecutionError("No driver class nime while using driver resType") - return _create_element(name, 'resources/jdbc-connection-pool', data, server) + return _create_element(name, "resources/jdbc-connection-pool", data, server) def update_jdbc_connection_pool(name, server=None, **kwargs): - ''' + """ Update a JDBC pool - ''' - return _update_element(name, 'resources/jdbc-connection-pool', kwargs, server) + """ + return _update_element(name, "resources/jdbc-connection-pool", kwargs, server) -def delete_jdbc_connection_pool(name, target='server', cascade=False, server=None): - ''' +def delete_jdbc_connection_pool(name, target="server", cascade=False, server=None): + """ Delete a JDBC pool - ''' - data = {'target': target, 'cascade': cascade} - return _delete_element(name, 'resources/jdbc-connection-pool', data, server) + """ + data = {"target": target, "cascade": cascade} + return _delete_element(name, "resources/jdbc-connection-pool", data, server) # JDBC resources def enum_jdbc_resource(server=None): - ''' + """ Enum JDBC resources - ''' - return _enum_elements('resources/jdbc-resource', server) + """ + return _enum_elements("resources/jdbc-resource", server) def get_jdbc_resource(name, server=None): - ''' + """ Get a specific JDBC resource - ''' - return _get_element(name, 'resources/jdbc-resource', server) + """ + return _get_element(name, "resources/jdbc-resource", server) def create_jdbc_resource(name, server=None, **kwargs): - ''' + """ Create a JDBC resource - ''' + """ defaults = { - 'description': '', - 'enabled': True, - 'id': name, - 'poolName': '', - 'target': 'server' + "description": "", + "enabled": True, + "id": name, + "poolName": "", + "target": "server", } # Data = defaults + merge kwargs + poolname data = defaults data.update(kwargs) - if not data['poolName']: - raise CommandExecutionError('No pool name!') + if not data["poolName"]: + raise CommandExecutionError("No pool name!") - return _create_element(name, 'resources/jdbc-resource', data, server) + return _create_element(name, "resources/jdbc-resource", data, server) def update_jdbc_resource(name, server=None, **kwargs): - ''' + """ Update a JDBC resource - ''' + """ # You're not supposed to update jndiName, if you do so, it will crash, silently - if 'jndiName' in kwargs: - del kwargs['jndiName'] - return _update_element(name, 'resources/jdbc-resource', kwargs, server) + if "jndiName" in kwargs: + del kwargs["jndiName"] + return _update_element(name, "resources/jdbc-resource", kwargs, server) -def delete_jdbc_resource(name, target='server', server=None): - ''' +def delete_jdbc_resource(name, target="server", server=None): + """ Delete a JDBC resource - ''' - return _delete_element(name, 'resources/jdbc-resource', {'target': target}, server) + """ + return _delete_element(name, "resources/jdbc-resource", {"target": target}, server) # System properties def get_system_properties(server=None): - ''' + """ Get system properties - ''' + """ properties = {} - data = _api_get('system-properties', server) + data = _api_get("system-properties", server) # Get properties into a dict - if any(data['extraProperties']['systemProperties']): - for element in data['extraProperties']['systemProperties']: - properties[element['name']] = element['value'] + if any(data["extraProperties"]["systemProperties"]): + for element in data["extraProperties"]["systemProperties"]: + properties[element["name"]] = element["value"] return properties return {} def update_system_properties(data, server=None): - ''' + """ Update system properties - ''' - _api_post('system-properties', _clean_data(data), server) + """ + _api_post("system-properties", _clean_data(data), server) return data def delete_system_properties(name, server=None): - ''' + """ Delete a system property - ''' - _api_delete('system-properties/{0}'.format(name), None, server) + """ + _api_delete("system-properties/{0}".format(name), None, server) diff --git a/salt/modules/glusterfs.py b/salt/modules/glusterfs.py index 3ca2301e0e9..53e08c3fb9c 100644 --- a/salt/modules/glusterfs.py +++ b/salt/modules/glusterfs.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Manage a glusterfs pool -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -12,7 +12,7 @@ import xml.etree.ElementTree as ET # Import salt libs import salt.utils.cloud import salt.utils.path -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import 3rd-party libs from salt.ext import six @@ -21,42 +21,42 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if the gluster command exists - ''' - if salt.utils.path.which('gluster'): + """ + if salt.utils.path.which("gluster"): return True - return (False, 'glusterfs server is not installed') + return (False, "glusterfs server is not installed") def _get_version(): # Set the default minor version to 6 for tests version = [3, 6] - cmd = 'gluster --version' - result = __salt__['cmd.run'](cmd).splitlines() + cmd = "gluster --version" + result = __salt__["cmd.run"](cmd).splitlines() for line in result: - if line.startswith('glusterfs'): - version = line.split()[-1].split('.') + if line.startswith("glusterfs"): + version = line.split()[-1].split(".") version = [int(i) for i in version] return tuple(version) def _gluster_ok(xml_data): - ''' + """ Extract boolean return value from Gluster's XML output. - ''' - return int(xml_data.find('opRet').text) == 0 + """ + return int(xml_data.find("opRet").text) == 0 def _gluster_output_cleanup(result): - ''' + """ Gluster versions prior to 6 have a bug that requires tricking isatty. This adds "gluster> " to the output. Strip it off and produce clean xml for ElementTree. - ''' - ret = '' + """ + ret = "" for line in result.splitlines(): - if line.startswith('gluster>'): + if line.startswith("gluster>"): ret += line[9:].strip() else: ret += line.strip() @@ -65,46 +65,42 @@ def _gluster_output_cleanup(result): def _gluster_xml(cmd): - ''' + """ Perform a gluster --xml command and log result. - ''' + """ # We will pass the command string as stdin to allow for much longer # command strings. This is especially useful for creating large volumes # where the list of bricks exceeds 128 characters. if _get_version() < (3, 6,): - result = __salt__['cmd.run']( + result = __salt__["cmd.run"]( 'script -q -c "gluster --xml --mode=script"', stdin="{0}\n\004".format(cmd) ) else: - result = __salt__['cmd.run']( - 'gluster --xml --mode=script', stdin="{0}\n".format(cmd) + result = __salt__["cmd.run"]( + "gluster --xml --mode=script", stdin="{0}\n".format(cmd) ) try: root = ET.fromstring(_gluster_output_cleanup(result)) except ET.ParseError: - raise CommandExecutionError('\n'.join(result.splitlines()[:-1])) + raise CommandExecutionError("\n".join(result.splitlines()[:-1])) if _gluster_ok(root): - output = root.find('output') + output = root.find("output") if output is not None: - log.info('Gluster call "%s" succeeded: %s', - cmd, - root.find('output').text) + log.info('Gluster call "%s" succeeded: %s', cmd, root.find("output").text) else: log.info('Gluster call "%s" succeeded', cmd) else: - log.error('Failed gluster call: %s: %s', - cmd, - root.find('opErrstr').text) + log.error("Failed gluster call: %s: %s", cmd, root.find("opErrstr").text) return root def _gluster(cmd): - ''' + """ Perform a gluster command and return a boolean status. - ''' + """ return _gluster_ok(_gluster_xml(cmd)) @@ -116,9 +112,9 @@ def _etree_to_dict(t): def _iter(root, term): - ''' + """ Checks for python2.6 or python2.7 - ''' + """ if sys.version_info < (2, 7): return root.getiterator(term) else: @@ -126,7 +122,7 @@ def _iter(root, term): def peer_status(): - ''' + """ Return peer status information The return value is a dictionary with peer UUIDs as keys and dicts of peer @@ -158,29 +154,29 @@ def peer_status(): State: Peer in Cluster (Connected) - ''' - root = _gluster_xml('peer status') + """ + root = _gluster_xml("peer status") if not _gluster_ok(root): return None result = {} - for peer in _iter(root, 'peer'): - uuid = peer.find('uuid').text - result[uuid] = {'hostnames': []} + for peer in _iter(root, "peer"): + uuid = peer.find("uuid").text + result[uuid] = {"hostnames": []} for item in peer: - if item.tag == 'hostname': - result[uuid]['hostnames'].append(item.text) - elif item.tag == 'hostnames': + if item.tag == "hostname": + result[uuid]["hostnames"].append(item.text) + elif item.tag == "hostnames": for hostname in item: - if hostname.text not in result[uuid]['hostnames']: - result[uuid]['hostnames'].append(hostname.text) - elif item.tag != 'uuid': + if hostname.text not in result[uuid]["hostnames"]: + result[uuid]["hostnames"].append(hostname.text) + elif item.tag != "uuid": result[uuid][item.tag] = item.text return result def peer(name): - ''' + """ Add another node into the peer list. name @@ -208,18 +204,26 @@ def peer(name): - ''' - if salt.utils.cloud.check_name(name, 'a-zA-Z0-9._-'): - raise SaltInvocationError( - 'Invalid characters in peer name "{0}"'.format(name)) + """ + if salt.utils.cloud.check_name(name, "a-zA-Z0-9._-"): + raise SaltInvocationError('Invalid characters in peer name "{0}"'.format(name)) - cmd = 'peer probe {0}'.format(name) + cmd = "peer probe {0}".format(name) return _gluster(cmd) -def create_volume(name, bricks, stripe=False, replica=False, device_vg=False, - transport='tcp', start=False, force=False, arbiter=False): - ''' +def create_volume( + name, + bricks, + stripe=False, + replica=False, + device_vg=False, + transport="tcp", + start=False, + force=False, + arbiter=False, +): + """ Create a glusterfs volume name @@ -268,47 +272,51 @@ def create_volume(name, bricks, stripe=False, replica=False, device_vg=False, salt gluster1 glusterfs.create vol2 '["gluster1:/export/vol2/brick", \ "gluster2:/export/vol2/brick"]' replica=2 start=True - ''' + """ # If single brick given as a string, accept it if isinstance(bricks, six.string_types): bricks = [bricks] # Error for block devices with multiple bricks if device_vg and len(bricks) > 1: - raise SaltInvocationError('Block device backend volume does not ' + - 'support multiple bricks') + raise SaltInvocationError( + "Block device backend volume does not " + "support multiple bricks" + ) # Validate bricks syntax for brick in bricks: try: - peer_name, path = brick.split(':') - if not path.startswith('/'): + peer_name, path = brick.split(":") + if not path.startswith("/"): raise SaltInvocationError( - 'Brick paths must start with / in {0}'.format(brick)) + "Brick paths must start with / in {0}".format(brick) + ) except ValueError: raise SaltInvocationError( - 'Brick syntax is : got {0}'.format(brick)) + "Brick syntax is : got {0}".format(brick) + ) # Validate arbiter config if arbiter and replica != 3: - raise SaltInvocationError('Arbiter configuration only valid ' + - 'in replica 3 volume') + raise SaltInvocationError( + "Arbiter configuration only valid " + "in replica 3 volume" + ) # Format creation call - cmd = 'volume create {0} '.format(name) + cmd = "volume create {0} ".format(name) if stripe: - cmd += 'stripe {0} '.format(stripe) + cmd += "stripe {0} ".format(stripe) if replica: - cmd += 'replica {0} '.format(replica) + cmd += "replica {0} ".format(replica) if arbiter: - cmd += 'arbiter 1 ' + cmd += "arbiter 1 " if device_vg: - cmd += 'device vg ' - if transport != 'tcp': - cmd += 'transport {0} '.format(transport) - cmd += ' '.join(bricks) + cmd += "device vg " + if transport != "tcp": + cmd += "transport {0} ".format(transport) + cmd += " ".join(bricks) if force: - cmd += ' force' + cmd += " force" if not _gluster(cmd): return False @@ -319,7 +327,7 @@ def create_volume(name, bricks, stripe=False, replica=False, device_vg=False, def list_volumes(): - ''' + """ List configured volumes CLI Example: @@ -327,17 +335,17 @@ def list_volumes(): .. code-block:: bash salt '*' glusterfs.list_volumes - ''' + """ - root = _gluster_xml('volume list') + root = _gluster_xml("volume list") if not _gluster_ok(root): return None - results = [x.text for x in _iter(root, 'volume')] + results = [x.text for x in _iter(root, "volume")] return results def status(name): - ''' + """ Check the status of a gluster volume. name @@ -348,50 +356,49 @@ def status(name): .. code-block:: bash salt '*' glusterfs.status myvolume - ''' + """ # Get volume status - root = _gluster_xml('volume status {0}'.format(name)) + root = _gluster_xml("volume status {0}".format(name)) if not _gluster_ok(root): # Most probably non-existing volume, the error output is logged # This return value is easy to test and intuitive return None - ret = {'bricks': {}, 'nfs': {}, 'healers': {}} + ret = {"bricks": {}, "nfs": {}, "healers": {}} def etree_legacy_wrap(t): ret = _etree_to_dict(t) - ret['online'] = (ret['status'] == '1') - ret['host'] = ret['hostname'] + ret["online"] = ret["status"] == "1" + ret["host"] = ret["hostname"] return ret # Build a hash to map hostname to peerid hostref = {} - for node in _iter(root, 'node'): - peerid = node.find('peerid').text - hostname = node.find('hostname').text - if hostname not in ('NFS Server', 'Self-heal Daemon'): + for node in _iter(root, "node"): + peerid = node.find("peerid").text + hostname = node.find("hostname").text + if hostname not in ("NFS Server", "Self-heal Daemon"): hostref[peerid] = hostname - for node in _iter(root, 'node'): - hostname = node.find('hostname').text - if hostname not in ('NFS Server', 'Self-heal Daemon'): - path = node.find('path').text - ret['bricks'][ - '{0}:{1}'.format(hostname, path)] = etree_legacy_wrap(node) - elif hostname == 'NFS Server': - peerid = node.find('peerid').text + for node in _iter(root, "node"): + hostname = node.find("hostname").text + if hostname not in ("NFS Server", "Self-heal Daemon"): + path = node.find("path").text + ret["bricks"]["{0}:{1}".format(hostname, path)] = etree_legacy_wrap(node) + elif hostname == "NFS Server": + peerid = node.find("peerid").text true_hostname = hostref[peerid] - ret['nfs'][true_hostname] = etree_legacy_wrap(node) + ret["nfs"][true_hostname] = etree_legacy_wrap(node) else: - peerid = node.find('peerid').text + peerid = node.find("peerid").text true_hostname = hostref[peerid] - ret['healers'][true_hostname] = etree_legacy_wrap(node) + ret["healers"][true_hostname] = etree_legacy_wrap(node) return ret def info(name=None): - ''' + """ .. versionadded:: 2015.8.4 Return gluster volume info. @@ -404,41 +411,41 @@ def info(name=None): .. code-block:: bash salt '*' glusterfs.info - ''' - cmd = 'volume info' + """ + cmd = "volume info" if name is not None: - cmd += ' ' + name + cmd += " " + name root = _gluster_xml(cmd) if not _gluster_ok(root): return None ret = {} - for volume in _iter(root, 'volume'): - name = volume.find('name').text + for volume in _iter(root, "volume"): + name = volume.find("name").text ret[name] = _etree_to_dict(volume) bricks = {} - for i, brick in enumerate(_iter(volume, 'brick'), start=1): - brickkey = 'brick{0}'.format(i) - bricks[brickkey] = {'path': brick.text} + for i, brick in enumerate(_iter(volume, "brick"), start=1): + brickkey = "brick{0}".format(i) + bricks[brickkey] = {"path": brick.text} for child in brick: - if not child.tag == 'name': + if not child.tag == "name": bricks[brickkey].update({child.tag: child.text}) for k, v in brick.items(): bricks[brickkey][k] = v - ret[name]['bricks'] = bricks + ret[name]["bricks"] = bricks options = {} - for option in _iter(volume, 'option'): - options[option.find('name').text] = option.find('value').text - ret[name]['options'] = options + for option in _iter(volume, "option"): + options[option.find("name").text] = option.find("value").text + ret[name]["options"] = options return ret def start_volume(name, force=False): - ''' + """ Start a gluster volume name @@ -453,17 +460,17 @@ def start_volume(name, force=False): .. code-block:: bash salt '*' glusterfs.start mycluster - ''' - cmd = 'volume start {0}'.format(name) + """ + cmd = "volume start {0}".format(name) if force: - cmd = '{0} force'.format(cmd) + cmd = "{0} force".format(cmd) volinfo = info(name) if name not in volinfo: log.error("Cannot start non-existing volume %s", name) return False - if not force and volinfo[name]['status'] == '1': + if not force and volinfo[name]["status"] == "1": log.info("Volume %s already started", name) return True @@ -471,7 +478,7 @@ def start_volume(name, force=False): def stop_volume(name, force=False): - ''' + """ Stop a gluster volume name @@ -487,24 +494,24 @@ def stop_volume(name, force=False): .. code-block:: bash salt '*' glusterfs.stop_volume mycluster - ''' + """ volinfo = info() if name not in volinfo: - log.error('Cannot stop non-existing volume %s', name) + log.error("Cannot stop non-existing volume %s", name) return False - if int(volinfo[name]['status']) != 1: - log.warning('Attempt to stop already stopped volume %s', name) + if int(volinfo[name]["status"]) != 1: + log.warning("Attempt to stop already stopped volume %s", name) return True - cmd = 'volume stop {0}'.format(name) + cmd = "volume stop {0}".format(name) if force: - cmd += ' force' + cmd += " force" return _gluster(cmd) def delete_volume(target, stop=True): - ''' + """ Deletes a gluster volume target @@ -518,30 +525,30 @@ def delete_volume(target, stop=True): .. code-block:: bash salt '*' glusterfs.delete_volume - ''' + """ volinfo = info() if target not in volinfo: - log.error('Cannot delete non-existing volume %s', target) + log.error("Cannot delete non-existing volume %s", target) return False # Stop volume if requested to and it is running - running = (volinfo[target]['status'] == '1') + running = volinfo[target]["status"] == "1" if not stop and running: # Fail if volume is running if stop is not requested - log.error('Volume %s must be stopped before deletion', target) + log.error("Volume %s must be stopped before deletion", target) return False if running: if not stop_volume(target, force=True): return False - cmd = 'volume delete {0}'.format(target) + cmd = "volume delete {0}".format(target) return _gluster(cmd) def add_volume_bricks(name, bricks): - ''' + """ Add brick(s) to an existing volume name @@ -555,40 +562,39 @@ def add_volume_bricks(name, bricks): .. code-block:: bash salt '*' glusterfs.add_volume_bricks - ''' + """ volinfo = info() if name not in volinfo: - log.error('Volume %s does not exist, cannot add bricks', name) + log.error("Volume %s does not exist, cannot add bricks", name) return False new_bricks = [] - cmd = 'volume add-brick {0}'.format(name) + cmd = "volume add-brick {0}".format(name) if isinstance(bricks, six.string_types): bricks = [bricks] - volume_bricks = [x['path'] for x in volinfo[name]['bricks'].values()] + volume_bricks = [x["path"] for x in volinfo[name]["bricks"].values()] for brick in bricks: if brick in volume_bricks: log.debug( - 'Brick %s already in volume %s...excluding from command', - brick, - name) + "Brick %s already in volume %s...excluding from command", brick, name + ) else: new_bricks.append(brick) if len(new_bricks) > 0: for brick in new_bricks: - cmd += ' {0}'.format(brick) + cmd += " {0}".format(brick) return _gluster(cmd) return True def enable_quota_volume(name): - ''' + """ Enable quota on a glusterfs volume. name @@ -599,16 +605,16 @@ def enable_quota_volume(name): .. code-block:: bash salt '*' glusterfs.enable_quota_volume - ''' + """ - cmd = 'volume quota {0} enable'.format(name) + cmd = "volume quota {0} enable".format(name) if not _gluster(cmd): return False return True def disable_quota_volume(name): - ''' + """ Disable quota on a glusterfs volume. name @@ -619,16 +625,16 @@ def disable_quota_volume(name): .. code-block:: bash salt '*' glusterfs.disable_quota_volume - ''' + """ - cmd = 'volume quota {0} disable'.format(name) + cmd = "volume quota {0} disable".format(name) if not _gluster(cmd): return False return True def set_quota_volume(name, path, size, enable_quota=False): - ''' + """ Set quota to glusterfs volume. name @@ -648,12 +654,12 @@ def set_quota_volume(name, path, size, enable_quota=False): .. code-block:: bash salt '*' glusterfs.set_quota_volume enable_quota=True - ''' - cmd = 'volume quota {0}'.format(name) + """ + cmd = "volume quota {0}".format(name) if path: - cmd += ' limit-usage {0}'.format(path) + cmd += " limit-usage {0}".format(path) if size: - cmd += ' {0}'.format(size) + cmd += " {0}".format(size) if enable_quota: if not enable_quota_volume(name): @@ -664,7 +670,7 @@ def set_quota_volume(name, path, size, enable_quota=False): def unset_quota_volume(name, path): - ''' + """ Unset quota on glusterfs volume name @@ -678,10 +684,10 @@ def unset_quota_volume(name, path): .. code-block:: bash salt '*' glusterfs.unset_quota_volume - ''' - cmd = 'volume quota {0}'.format(name) + """ + cmd = "volume quota {0}".format(name) if path: - cmd += ' remove {0}'.format(path) + cmd += " remove {0}".format(path) if not _gluster(cmd): return False @@ -689,7 +695,7 @@ def unset_quota_volume(name, path): def list_quota_volume(name): - ''' + """ List quotas of glusterfs volume name @@ -700,24 +706,24 @@ def list_quota_volume(name): .. code-block:: bash salt '*' glusterfs.list_quota_volume - ''' - cmd = 'volume quota {0}'.format(name) - cmd += ' list' + """ + cmd = "volume quota {0}".format(name) + cmd += " list" root = _gluster_xml(cmd) if not _gluster_ok(root): return None ret = {} - for limit in _iter(root, 'limit'): - path = limit.find('path').text + for limit in _iter(root, "limit"): + path = limit.find("path").text ret[path] = _etree_to_dict(limit) return ret def get_op_version(name): - ''' + """ .. versionadded:: 2019.2.0 Returns the glusterfs volume op-version @@ -730,29 +736,29 @@ def get_op_version(name): .. code-block:: bash salt '*' glusterfs.get_op_version - ''' + """ - cmd = 'volume get {0} cluster.op-version'.format(name) + cmd = "volume get {0} cluster.op-version".format(name) root = _gluster_xml(cmd) if not _gluster_ok(root): - return False, root.find('opErrstr').text + return False, root.find("opErrstr").text result = {} - for op_version in _iter(root, 'volGetopts'): + for op_version in _iter(root, "volGetopts"): for item in op_version: - if item.tag == 'Value': + if item.tag == "Value": result = item.text - elif item.tag == 'Opt': + elif item.tag == "Opt": for child in item: - if child.tag == 'Value': + if child.tag == "Value": result = child.text return result def get_max_op_version(): - ''' + """ .. versionadded:: 2019.2.0 Returns the glusterfs volume's max op-version value @@ -762,31 +768,36 @@ def get_max_op_version(): .. code-block:: bash salt '*' glusterfs.get_max_op_version - ''' + """ if _get_version() < (3, 10,): - return False, 'Glusterfs version must be 3.10+. Your version is {0}.'.format(str('.'.join(str(i) for i in _get_version()))) + return ( + False, + "Glusterfs version must be 3.10+. Your version is {0}.".format( + str(".".join(str(i) for i in _get_version())) + ), + ) - cmd = 'volume get all cluster.max-op-version' + cmd = "volume get all cluster.max-op-version" root = _gluster_xml(cmd) if not _gluster_ok(root): - return False, root.find('opErrstr').text + return False, root.find("opErrstr").text result = {} - for max_op_version in _iter(root, 'volGetopts'): + for max_op_version in _iter(root, "volGetopts"): for item in max_op_version: - if item.tag == 'Value': + if item.tag == "Value": result = item.text - elif item.tag == 'Opt': + elif item.tag == "Opt": for child in item: - if child.tag == 'Value': + if child.tag == "Value": result = child.text return result def set_op_version(version): - ''' + """ .. versionadded:: 2019.2.0 Set the glusterfs volume op-version @@ -799,19 +810,19 @@ def set_op_version(version): .. code-block:: bash salt '*' glusterfs.set_op_version - ''' + """ - cmd = 'volume set all cluster.op-version {0}'.format(version) + cmd = "volume set all cluster.op-version {0}".format(version) root = _gluster_xml(cmd) if not _gluster_ok(root): - return False, root.find('opErrstr').text + return False, root.find("opErrstr").text - return root.find('output').text + return root.find("output").text def get_version(): - ''' + """ .. versionadded:: 2019.2.0 Returns the version of glusterfs. @@ -819,6 +830,6 @@ def get_version(): .. code-block:: bash salt '*' glusterfs.get_version - ''' + """ - return '.'.join(_get_version()) + return ".".join(_get_version()) diff --git a/salt/modules/gnomedesktop.py b/salt/modules/gnomedesktop.py index 158eb9bc9cd..333780506ea 100644 --- a/salt/modules/gnomedesktop.py +++ b/salt/modules/gnomedesktop.py @@ -1,24 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" GNOME implementations -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import re + import logging -try: - import pwd - HAS_PWD = True -except ImportError: - HAS_PWD = False +import re # Import Salt libs import salt.utils.path +try: + import pwd + + HAS_PWD = True +except ImportError: + HAS_PWD = False + + # Import 3rd-party libs try: from gi.repository import Gio, GLib # pylint: disable=W0611 + HAS_GLIB = True except ImportError: HAS_GLIB = False @@ -26,22 +31,23 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'gnome' +__virtualname__ = "gnome" # Don't shadow built-in's. -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Only load if the Gio and Glib modules are available - ''' + """ if HAS_PWD and HAS_GLIB: return __virtualname__ - return (False, 'The gnome_desktop execution module cannot be loaded: ' - 'The Gio and GLib modules are not available') + return ( + False, + "The gnome_desktop execution module cannot be loaded: " + "The Gio and GLib modules are not available", + ) class _GSettings(object): @@ -54,64 +60,68 @@ class _GSettings(object): @property def gsetting_command(self): - ''' + """ return the command to run the gsettings binary - ''' - if salt.utils.path.which_bin(['dbus-run-session']): - cmd = ['dbus-run-session', '--', 'gsettings'] + """ + if salt.utils.path.which_bin(["dbus-run-session"]): + cmd = ["dbus-run-session", "--", "gsettings"] else: - cmd = ['dbus-launch', '--exit-with-session', 'gsettings'] + cmd = ["dbus-launch", "--exit-with-session", "gsettings"] return cmd def _get(self): - ''' + """ get the value for user in gsettings - ''' + """ user = self.USER try: uid = pwd.getpwnam(user).pw_uid except KeyError: - log.info('User does not exist') + log.info("User does not exist") return False - cmd = self.gsetting_command + ['get', str(self.SCHEMA), str(self.KEY)] + cmd = self.gsetting_command + ["get", str(self.SCHEMA), str(self.KEY)] environ = {} - environ['XDG_RUNTIME_DIR'] = '/run/user/{0}'.format(uid) - result = __salt__['cmd.run_all'](cmd, runas=user, env=environ, python_shell=False) + environ["XDG_RUNTIME_DIR"] = "/run/user/{0}".format(uid) + result = __salt__["cmd.run_all"]( + cmd, runas=user, env=environ, python_shell=False + ) - if 'stdout' in result: - if 'uint32' in result['stdout']: - return re.sub('uint32 ', '', result['stdout']) + if "stdout" in result: + if "uint32" in result["stdout"]: + return re.sub("uint32 ", "", result["stdout"]) else: - return result['stdout'] + return result["stdout"] else: return False def _set(self, value): - ''' + """ set the value for user in gsettings - ''' + """ user = self.USER try: uid = pwd.getpwnam(user).pw_uid except KeyError: - log.info('User does not exist') + log.info("User does not exist") result = {} - result['retcode'] = 1 - result['stdout'] = 'User {0} does not exist'.format(user) + result["retcode"] = 1 + result["stdout"] = "User {0} does not exist".format(user) return result - cmd = self.gsetting_command + ['set', self.SCHEMA, self.KEY, value] + cmd = self.gsetting_command + ["set", self.SCHEMA, self.KEY, value] environ = {} - environ['XDG_RUNTIME_DIR'] = '/run/user/{0}'.format(uid) - result = __salt__['cmd.run_all'](cmd, runas=user, env=environ, python_shell=False) + environ["XDG_RUNTIME_DIR"] = "/run/user/{0}".format(uid) + result = __salt__["cmd.run_all"]( + cmd, runas=user, env=environ, python_shell=False + ) return result def ping(**kwargs): - ''' + """ A test to ensure the GNOME module is loaded CLI Example: @@ -120,12 +130,12 @@ def ping(**kwargs): salt '*' gnome.ping user= - ''' + """ return True def getIdleDelay(**kwargs): - ''' + """ Return the current idle delay setting in seconds CLI Example: @@ -134,15 +144,15 @@ def getIdleDelay(**kwargs): salt '*' gnome.getIdleDelay user= - ''' - _gsession = _GSettings(user=kwargs.get('user'), - schema='org.gnome.desktop.session', - key='idle-delay') + """ + _gsession = _GSettings( + user=kwargs.get("user"), schema="org.gnome.desktop.session", key="idle-delay" + ) return _gsession._get() def setIdleDelay(delaySeconds, **kwargs): - ''' + """ Set the current idle delay setting in seconds CLI Example: @@ -151,15 +161,15 @@ def setIdleDelay(delaySeconds, **kwargs): salt '*' gnome.setIdleDelay user= - ''' - _gsession = _GSettings(user=kwargs.get('user'), - schema='org.gnome.desktop.session', - key='idle-delay') + """ + _gsession = _GSettings( + user=kwargs.get("user"), schema="org.gnome.desktop.session", key="idle-delay" + ) return _gsession._set(delaySeconds) def getClockFormat(**kwargs): - ''' + """ Return the current clock format, either 12h or 24h format. CLI Example: @@ -168,15 +178,17 @@ def getClockFormat(**kwargs): salt '*' gnome.getClockFormat user= - ''' - _gsession = _GSettings(user=kwargs.get('user'), - schema='org.gnome.desktop.interface', - key='clock-format') + """ + _gsession = _GSettings( + user=kwargs.get("user"), + schema="org.gnome.desktop.interface", + key="clock-format", + ) return _gsession._get() def setClockFormat(clockFormat, **kwargs): - ''' + """ Set the clock format, either 12h or 24h format. CLI Example: @@ -185,17 +197,19 @@ def setClockFormat(clockFormat, **kwargs): salt '*' gnome.setClockFormat <12h|24h> user= - ''' - if clockFormat != '12h' and clockFormat != '24h': + """ + if clockFormat != "12h" and clockFormat != "24h": return False - _gsession = _GSettings(user=kwargs.get('user'), - schema='org.gnome.desktop.interface', - key='clock-format') + _gsession = _GSettings( + user=kwargs.get("user"), + schema="org.gnome.desktop.interface", + key="clock-format", + ) return _gsession._set(clockFormat) def getClockShowDate(**kwargs): - ''' + """ Return the current setting, if the date is shown in the clock CLI Example: @@ -204,15 +218,17 @@ def getClockShowDate(**kwargs): salt '*' gnome.getClockShowDate user= - ''' - _gsession = _GSettings(user=kwargs.get('user'), - schema='org.gnome.desktop.interface', - key='clock-show-date') + """ + _gsession = _GSettings( + user=kwargs.get("user"), + schema="org.gnome.desktop.interface", + key="clock-show-date", + ) return _gsession._get() def setClockShowDate(kvalue, **kwargs): - ''' + """ Set whether the date is visible in the clock CLI Example: @@ -221,17 +237,19 @@ def setClockShowDate(kvalue, **kwargs): salt '*' gnome.setClockShowDate user= - ''' + """ if kvalue is not True and kvalue is not False: return False - _gsession = _GSettings(user=kwargs.get('user'), - schema='org.gnome.desktop.interface', - key='clock-show-date') + _gsession = _GSettings( + user=kwargs.get("user"), + schema="org.gnome.desktop.interface", + key="clock-show-date", + ) return _gsession._set(kvalue) def getIdleActivation(**kwargs): - ''' + """ Get whether the idle activation is enabled CLI Example: @@ -240,15 +258,17 @@ def getIdleActivation(**kwargs): salt '*' gnome.getIdleActivation user= - ''' - _gsession = _GSettings(user=kwargs.get('user'), - schema='org.gnome.desktop.screensaver', - key='idle-activation-enabled') + """ + _gsession = _GSettings( + user=kwargs.get("user"), + schema="org.gnome.desktop.screensaver", + key="idle-activation-enabled", + ) return _gsession._get() def setIdleActivation(kvalue, **kwargs): - ''' + """ Set whether the idle activation is enabled CLI Example: @@ -257,17 +277,19 @@ def setIdleActivation(kvalue, **kwargs): salt '*' gnome.setIdleActivation user= - ''' + """ if kvalue is not True and kvalue is not False: return False - _gsession = _GSettings(user=kwargs.get('user'), - schema='org.gnome.desktop.screensaver', - key='idle-activation-enabled') + _gsession = _GSettings( + user=kwargs.get("user"), + schema="org.gnome.desktop.screensaver", + key="idle-activation-enabled", + ) return _gsession._set(kvalue) def get(schema=None, key=None, user=None, **kwargs): - ''' + """ Get key in a particular GNOME schema CLI Example: @@ -276,13 +298,13 @@ def get(schema=None, key=None, user=None, **kwargs): salt '*' gnome.get user= schema=org.gnome.desktop.screensaver key=idle-activation-enabled - ''' + """ _gsession = _GSettings(user=user, schema=schema, key=key) return _gsession._get() def set_(schema=None, key=None, user=None, value=None, **kwargs): - ''' + """ Set key in a particular GNOME schema CLI Example: @@ -291,6 +313,6 @@ def set_(schema=None, key=None, user=None, value=None, **kwargs): salt '*' gnome.set user= schema=org.gnome.desktop.screensaver key=idle-activation-enabled value=False - ''' + """ _gsession = _GSettings(user=user, schema=schema, key=key) return _gsession._set(value) diff --git a/salt/modules/google_chat.py b/salt/modules/google_chat.py index 464f2966e52..ff4af53ad22 100644 --- a/salt/modules/google_chat.py +++ b/salt/modules/google_chat.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for sending messages to google chat. .. versionadded:: 2019.2.0 @@ -8,17 +8,17 @@ To use this module you need to configure a webhook in the google chat room where you would like the message to be sent, see: https://developers.google.com/hangouts/chat/how-tos/webhooks -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import json +import json # ------------------------------------------------------------------------------ # module properties # ------------------------------------------------------------------------------ -__virtualname__ = 'google_chat' +__virtualname__ = "google_chat" # ------------------------------------------------------------------------------ # property functions @@ -28,29 +28,32 @@ __virtualname__ = 'google_chat' def __virtual__(): return __virtualname__ + # ------------------------------------------------------------------------------ # callable functions # ------------------------------------------------------------------------------ def send_message(url, message): - ''' + """ Send a message to the google chat room specified in the webhook url. .. code-block:: bash salt '*' google_chat.send_message "https://chat.googleapis.com/v1/spaces/example_space/messages?key=example_key" "This is a test message" - ''' - headers = {'Content-Type': 'application/json'} - data = {'text': message} - result = __utils__['http.query'](url, - 'POST', - data=json.dumps(data), - header_dict=headers, - decode=True, - status=True) + """ + headers = {"Content-Type": "application/json"} + data = {"text": message} + result = __utils__["http.query"]( + url, + "POST", + data=json.dumps(data), + header_dict=headers, + decode=True, + status=True, + ) - if result.get('status', 0) == 200: + if result.get("status", 0) == 200: return True else: return False diff --git a/salt/modules/gpg.py b/salt/modules/gpg.py index 9dd1007c1b4..d8606ff0985 100644 --- a/salt/modules/gpg.py +++ b/salt/modules/gpg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage a GPG keychains, add keys, create keys, retrieve keys from keyservers. Sign, encrypt and sign plus encrypt text and files. @@ -10,10 +10,11 @@ Sign, encrypt and sign plus encrypt text and files. The ``python-gnupg`` library and ``gpg`` binary are required to be installed. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import functools import logging import os @@ -25,112 +26,122 @@ import salt.utils.files import salt.utils.path import salt.utils.stringutils from salt.exceptions import SaltInvocationError -from salt.utils.versions import LooseVersion as _LooseVersion # Import 3rd-party libs from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'gpg' +__virtualname__ = "gpg" LETTER_TRUST_DICT = { - 'e': 'Expired', - 'q': 'Unknown', - 'n': 'Not Trusted', - 'f': 'Fully Trusted', - 'm': 'Marginally Trusted', - 'u': 'Ultimately Trusted', - '-': 'Unknown', + "e": "Expired", + "q": "Unknown", + "n": "Not Trusted", + "f": "Fully Trusted", + "m": "Marginally Trusted", + "u": "Ultimately Trusted", + "-": "Unknown", } NUM_TRUST_DICT = { - 'expired': '1', - 'unknown': '2', - 'not_trusted': '3', - 'marginally': '4', - 'fully': '5', - 'ultimately': '6', + "expired": "1", + "unknown": "2", + "not_trusted": "3", + "marginally": "4", + "fully": "5", + "ultimately": "6", } INV_NUM_TRUST_DICT = { - '1': 'Expired', - '2': 'Unknown', - '3': 'Not Trusted', - '4': 'Marginally', - '5': 'Fully Trusted', - '6': 'Ultimately Trusted' + "1": "Expired", + "2": "Unknown", + "3": "Not Trusted", + "4": "Marginally", + "5": "Fully Trusted", + "6": "Ultimately Trusted", } VERIFY_TRUST_LEVELS = { - '0': 'Undefined', - '1': 'Never', - '2': 'Marginal', - '3': 'Fully', - '4': 'Ultimate' + "0": "Undefined", + "1": "Never", + "2": "Marginal", + "3": "Fully", + "4": "Ultimate", } GPG_1_3_1 = False try: import gnupg + HAS_GPG_BINDINGS = True - GPG_1_3_1 = _LooseVersion(gnupg.__version__) >= _LooseVersion('1.3.1') + GPG_1_3_1 = _LooseVersion(gnupg.__version__) >= _LooseVersion("1.3.1") except ImportError: HAS_GPG_BINDINGS = False def _gpg(): - ''' + """ Returns the path to the gpg binary - ''' + """ # Get the path to the gpg binary. - return salt.utils.path.which('gpg') + return salt.utils.path.which("gpg") def __virtual__(): - ''' + """ Makes sure that python-gnupg and gpg are available. - ''' + """ if not _gpg(): - return (False, 'The gpg execution module cannot be loaded: ' - 'gpg binary is not in the path.') + return ( + False, + "The gpg execution module cannot be loaded: " + "gpg binary is not in the path.", + ) - return __virtualname__ if HAS_GPG_BINDINGS \ - else (False, 'The gpg execution module cannot be loaded; the ' - 'gnupg python module is not installed.') + return ( + __virtualname__ + if HAS_GPG_BINDINGS + else ( + False, + "The gpg execution module cannot be loaded; the " + "gnupg python module is not installed.", + ) + ) def _get_user_info(user=None): - ''' + """ Wrapper for user.info Salt function - ''' + """ if not user: # Get user Salt runnining as - user = __salt__['config.option']('user') + user = __salt__["config.option"]("user") - userinfo = __salt__['user.info'](user) + userinfo = __salt__["user.info"](user) if not userinfo: - if user == 'salt': + if user == "salt": # Special case with `salt` user: # if it doesn't exist then fall back to user Salt running as userinfo = _get_user_info() else: - raise SaltInvocationError('User {0} does not exist'.format(user)) + raise SaltInvocationError("User {0} does not exist".format(user)) return userinfo def _get_user_gnupghome(user): - ''' + """ Return default GnuPG home directory path for a user - ''' - if user == 'salt': - gnupghome = os.path.join(__salt__['config.get']('config_dir'), 'gpgkeys') + """ + if user == "salt": + gnupghome = os.path.join(__salt__["config.get"]("config_dir"), "gpgkeys") else: - gnupghome = os.path.join(_get_user_info(user)['home'], '.gnupg') + gnupghome = os.path.join(_get_user_info(user)["home"], ".gnupg") return gnupghome @@ -138,11 +149,11 @@ def _get_user_gnupghome(user): def _restore_ownership(func): @functools.wraps(func) def func_wrapper(*args, **kwargs): - ''' + """ Wrap gpg function calls to fix permissions - ''' - user = kwargs.get('user') - gnupghome = kwargs.get('gnupghome') + """ + user = kwargs.get("user") + gnupghome = kwargs.get("gnupghome") if not gnupghome: gnupghome = _get_user_gnupghome(user) @@ -150,24 +161,24 @@ def _restore_ownership(func): userinfo = _get_user_info(user) run_user = _get_user_info() - if userinfo['uid'] != run_user['uid'] and os.path.exists(gnupghome): + if userinfo["uid"] != run_user["uid"] and os.path.exists(gnupghome): # Given user is different from one who runs Salt process, # need to fix ownership permissions for GnuPG home dir - group = __salt__['file.gid_to_group'](run_user['gid']) - for path in [gnupghome] + __salt__['file.find'](gnupghome): - __salt__['file.chown'](path, run_user['name'], group) + group = __salt__["file.gid_to_group"](run_user["gid"]) + for path in [gnupghome] + __salt__["file.find"](gnupghome): + __salt__["file.chown"](path, run_user["name"], group) # Filter special kwargs for key in list(kwargs): - if key.startswith('__'): + if key.startswith("__"): del kwargs[key] ret = func(*args, **kwargs) - if userinfo['uid'] != run_user['uid']: - group = __salt__['file.gid_to_group'](userinfo['gid']) - for path in [gnupghome] + __salt__['file.find'](gnupghome): - __salt__['file.chown'](path, user, group) + if userinfo["uid"] != run_user["uid"]: + group = __salt__["file.gid_to_group"](userinfo["gid"]) + for path in [gnupghome] + __salt__["file.find"](gnupghome): + __salt__["file.chown"](path, user, group) return ret @@ -175,9 +186,9 @@ def _restore_ownership(func): def _create_gpg(user=None, gnupghome=None): - ''' + """ Create the GPG object - ''' + """ if not gnupghome: gnupghome = _get_user_gnupghome(user) @@ -190,18 +201,18 @@ def _create_gpg(user=None, gnupghome=None): def _list_keys(user=None, gnupghome=None, secret=False): - ''' + """ Helper function for Listing keys - ''' + """ gpg = _create_gpg(user, gnupghome) _keys = gpg.list_keys(secret) return _keys def _search_keys(text, keyserver, user=None): - ''' + """ Helper function for searching keys from keyserver - ''' + """ gpg = _create_gpg(user) if keyserver: _keys = gpg.search_keys(text, keyserver) @@ -211,7 +222,7 @@ def _search_keys(text, keyserver, user=None): def search_keys(text, keyserver=None, user=None): - ''' + """ Search keys from keyserver text @@ -235,36 +246,39 @@ def search_keys(text, keyserver=None, user=None): salt '*' gpg.search_keys user@example.com keyserver=keyserver.ubuntu.com user=username - ''' + """ if GPG_1_3_1: - raise SaltInvocationError('The search_keys function is not support with this version of python-gnupg.') + raise SaltInvocationError( + "The search_keys function is not support with this version of python-gnupg." + ) else: if not keyserver: - keyserver = 'pgp.mit.edu' + keyserver = "pgp.mit.edu" _keys = [] for _key in _search_keys(text, keyserver, user): - tmp = {'keyid': _key['keyid'], - 'uids': _key['uids']} + tmp = {"keyid": _key["keyid"], "uids": _key["uids"]} - expires = _key.get('expires', None) - date = _key.get('date', None) - length = _key.get('length', None) + expires = _key.get("expires", None) + date = _key.get("date", None) + length = _key.get("length", None) if expires: - tmp['expires'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['expires']))) + tmp["expires"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["expires"])) + ) if date: - tmp['created'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['date']))) + tmp["created"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["date"])) + ) if length: - tmp['keyLength'] = _key['length'] + tmp["keyLength"] = _key["length"] _keys.append(tmp) return _keys def list_keys(user=None, gnupghome=None): - ''' + """ List keys in GPG keychain user @@ -281,37 +295,41 @@ def list_keys(user=None, gnupghome=None): salt '*' gpg.list_keys - ''' + """ _keys = [] for _key in _list_keys(user, gnupghome): - tmp = {'keyid': _key['keyid'], - 'fingerprint': _key['fingerprint'], - 'uids': _key['uids']} + tmp = { + "keyid": _key["keyid"], + "fingerprint": _key["fingerprint"], + "uids": _key["uids"], + } - expires = _key.get('expires', None) - date = _key.get('date', None) - length = _key.get('length', None) - owner_trust = _key.get('ownertrust', None) - trust = _key.get('trust', None) + expires = _key.get("expires", None) + date = _key.get("date", None) + length = _key.get("length", None) + owner_trust = _key.get("ownertrust", None) + trust = _key.get("trust", None) if expires: - tmp['expires'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['expires']))) + tmp["expires"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["expires"])) + ) if date: - tmp['created'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['date']))) + tmp["created"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["date"])) + ) if length: - tmp['keyLength'] = _key['length'] + tmp["keyLength"] = _key["length"] if owner_trust: - tmp['ownerTrust'] = LETTER_TRUST_DICT[_key['ownertrust']] + tmp["ownerTrust"] = LETTER_TRUST_DICT[_key["ownertrust"]] if trust: - tmp['trust'] = LETTER_TRUST_DICT[_key['trust']] + tmp["trust"] = LETTER_TRUST_DICT[_key["trust"]] _keys.append(tmp) return _keys def list_secret_keys(user=None, gnupghome=None): - ''' + """ List secret keys in GPG keychain user @@ -328,48 +346,54 @@ def list_secret_keys(user=None, gnupghome=None): salt '*' gpg.list_secret_keys - ''' + """ _keys = [] for _key in _list_keys(user, gnupghome, secret=True): - tmp = {'keyid': _key['keyid'], - 'fingerprint': _key['fingerprint'], - 'uids': _key['uids']} + tmp = { + "keyid": _key["keyid"], + "fingerprint": _key["fingerprint"], + "uids": _key["uids"], + } - expires = _key.get('expires', None) - date = _key.get('date', None) - length = _key.get('length', None) - owner_trust = _key.get('ownertrust', None) - trust = _key.get('trust', None) + expires = _key.get("expires", None) + date = _key.get("date", None) + length = _key.get("length", None) + owner_trust = _key.get("ownertrust", None) + trust = _key.get("trust", None) if expires: - tmp['expires'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['expires']))) + tmp["expires"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["expires"])) + ) if date: - tmp['created'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['date']))) + tmp["created"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["date"])) + ) if length: - tmp['keyLength'] = _key['length'] + tmp["keyLength"] = _key["length"] if owner_trust: - tmp['ownerTrust'] = LETTER_TRUST_DICT[_key['ownertrust']] + tmp["ownerTrust"] = LETTER_TRUST_DICT[_key["ownertrust"]] if trust: - tmp['trust'] = LETTER_TRUST_DICT[_key['trust']] + tmp["trust"] = LETTER_TRUST_DICT[_key["trust"]] _keys.append(tmp) return _keys @_restore_ownership -def create_key(key_type='RSA', - key_length=1024, - name_real='Autogenerated Key', - name_comment='Generated by SaltStack', - name_email=None, - subkey_type=None, - subkey_length=None, - expire_date=None, - use_passphrase=False, - user=None, - gnupghome=None): - ''' +def create_key( + key_type="RSA", + key_length=1024, + name_real="Autogenerated Key", + name_comment="Generated by SaltStack", + name_email=None, + subkey_type=None, + subkey_length=None, + expire_date=None, + use_passphrase=False, + user=None, + gnupghome=None, +): + """ Create a key in the GPG keychain .. note:: @@ -430,60 +454,55 @@ def create_key(key_type='RSA', salt -t 15 '*' gpg.create_key - ''' - ret = { - 'res': True, - 'fingerprint': '', - 'message': '' - } + """ + ret = {"res": True, "fingerprint": "", "message": ""} - create_params = {'key_type': key_type, - 'key_length': key_length, - 'name_real': name_real, - 'name_comment': name_comment, - } + create_params = { + "key_type": key_type, + "key_length": key_length, + "name_real": name_real, + "name_comment": name_comment, + } gpg = _create_gpg(user, gnupghome) if name_email: - create_params['name_email'] = name_email + create_params["name_email"] = name_email if subkey_type: - create_params['subkey_type'] = subkey_type + create_params["subkey_type"] = subkey_type if subkey_length: - create_params['subkey_length'] = subkey_length + create_params["subkey_length"] = subkey_length if expire_date: - create_params['expire_date'] = expire_date + create_params["expire_date"] = expire_date if use_passphrase: - gpg_passphrase = __salt__['pillar.get']('gpg_passphrase') + gpg_passphrase = __salt__["pillar.get"]("gpg_passphrase") if not gpg_passphrase: - ret['res'] = False - ret['message'] = "gpg_passphrase not available in pillar." + ret["res"] = False + ret["message"] = "gpg_passphrase not available in pillar." return ret else: - create_params['passphrase'] = gpg_passphrase + create_params["passphrase"] = gpg_passphrase input_data = gpg.gen_key_input(**create_params) key = gpg.gen_key(input_data) if key.fingerprint: - ret['fingerprint'] = key.fingerprint - ret['message'] = 'GPG key pair successfully generated.' + ret["fingerprint"] = key.fingerprint + ret["message"] = "GPG key pair successfully generated." else: - ret['res'] = False - ret['message'] = 'Unable to generate GPG key pair.' + ret["res"] = False + ret["message"] = "Unable to generate GPG key pair." return ret -def delete_key(keyid=None, - fingerprint=None, - delete_secret=False, - user=None, - gnupghome=None): - ''' +def delete_key( + keyid=None, fingerprint=None, delete_secret=False, user=None, gnupghome=None +): + """ Get a key from the GPG keychain keyid @@ -516,48 +535,51 @@ def delete_key(keyid=None, salt '*' gpg.delete_key keyid=3FAD9F1E user=username delete_secret=True - ''' - ret = { - 'res': True, - 'message': '' - } + """ + ret = {"res": True, "message": ""} if fingerprint and keyid: - ret['res'] = False - ret['message'] = 'Only specify one argument, fingerprint or keyid' + ret["res"] = False + ret["message"] = "Only specify one argument, fingerprint or keyid" return ret if not fingerprint and not keyid: - ret['res'] = False - ret['message'] = 'Required argument, fingerprint or keyid' + ret["res"] = False + ret["message"] = "Required argument, fingerprint or keyid" return ret gpg = _create_gpg(user, gnupghome) key = get_key(keyid, fingerprint, user) if key: - fingerprint = key['fingerprint'] + fingerprint = key["fingerprint"] skey = get_secret_key(keyid, fingerprint, user) if skey and not delete_secret: - ret['res'] = False - ret['message'] = 'Secret key exists, delete first or pass delete_secret=True.' + ret["res"] = False + ret[ + "message" + ] = "Secret key exists, delete first or pass delete_secret=True." return ret - elif skey and delete_secret and six.text_type(gpg.delete_keys(fingerprint, True)) == 'ok': + elif ( + skey + and delete_secret + and six.text_type(gpg.delete_keys(fingerprint, True)) == "ok" + ): # Delete the secret key - ret['message'] = 'Secret key for {0} deleted\n'.format(fingerprint) + ret["message"] = "Secret key for {0} deleted\n".format(fingerprint) # Delete the public key - if six.text_type(gpg.delete_keys(fingerprint)) == 'ok': - ret['message'] += 'Public key for {0} deleted'.format(fingerprint) - ret['res'] = True + if six.text_type(gpg.delete_keys(fingerprint)) == "ok": + ret["message"] += "Public key for {0} deleted".format(fingerprint) + ret["res"] = True return ret else: - ret['res'] = False - ret['message'] = 'Key not available in keychain.' + ret["res"] = False + ret["message"] = "Key not available in keychain." return ret def get_key(keyid=None, fingerprint=None, user=None, gnupghome=None): - ''' + """ Get a key from the GPG keychain keyid @@ -584,34 +606,38 @@ def get_key(keyid=None, fingerprint=None, user=None, gnupghome=None): salt '*' gpg.get_key keyid=3FAD9F1E user=username - ''' + """ tmp = {} for _key in _list_keys(user, gnupghome): - if (_key['fingerprint'] == fingerprint or - _key['keyid'] == keyid or - _key['keyid'][8:] == keyid): - tmp['keyid'] = _key['keyid'] - tmp['fingerprint'] = _key['fingerprint'] - tmp['uids'] = _key['uids'] + if ( + _key["fingerprint"] == fingerprint + or _key["keyid"] == keyid + or _key["keyid"][8:] == keyid + ): + tmp["keyid"] = _key["keyid"] + tmp["fingerprint"] = _key["fingerprint"] + tmp["uids"] = _key["uids"] - expires = _key.get('expires', None) - date = _key.get('date', None) - length = _key.get('length', None) - owner_trust = _key.get('ownertrust', None) - trust = _key.get('trust', None) + expires = _key.get("expires", None) + date = _key.get("date", None) + length = _key.get("length", None) + owner_trust = _key.get("ownertrust", None) + trust = _key.get("trust", None) if expires: - tmp['expires'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['expires']))) + tmp["expires"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["expires"])) + ) if date: - tmp['created'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['date']))) + tmp["created"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["date"])) + ) if length: - tmp['keyLength'] = _key['length'] + tmp["keyLength"] = _key["length"] if owner_trust: - tmp['ownerTrust'] = LETTER_TRUST_DICT[_key['ownertrust']] + tmp["ownerTrust"] = LETTER_TRUST_DICT[_key["ownertrust"]] if trust: - tmp['trust'] = LETTER_TRUST_DICT[_key['trust']] + tmp["trust"] = LETTER_TRUST_DICT[_key["trust"]] if not tmp: return False else: @@ -619,7 +645,7 @@ def get_key(keyid=None, fingerprint=None, user=None, gnupghome=None): def get_secret_key(keyid=None, fingerprint=None, user=None, gnupghome=None): - ''' + """ Get a key from the GPG keychain keyid @@ -646,34 +672,38 @@ def get_secret_key(keyid=None, fingerprint=None, user=None, gnupghome=None): salt '*' gpg.get_secret_key keyid=3FAD9F1E user=username - ''' + """ tmp = {} for _key in _list_keys(user, gnupghome, secret=True): - if (_key['fingerprint'] == fingerprint or - _key['keyid'] == keyid or - _key['keyid'][8:] == keyid): - tmp['keyid'] = _key['keyid'] - tmp['fingerprint'] = _key['fingerprint'] - tmp['uids'] = _key['uids'] + if ( + _key["fingerprint"] == fingerprint + or _key["keyid"] == keyid + or _key["keyid"][8:] == keyid + ): + tmp["keyid"] = _key["keyid"] + tmp["fingerprint"] = _key["fingerprint"] + tmp["uids"] = _key["uids"] - expires = _key.get('expires', None) - date = _key.get('date', None) - length = _key.get('length', None) - owner_trust = _key.get('ownertrust', None) - trust = _key.get('trust', None) + expires = _key.get("expires", None) + date = _key.get("date", None) + length = _key.get("length", None) + owner_trust = _key.get("ownertrust", None) + trust = _key.get("trust", None) if expires: - tmp['expires'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['expires']))) + tmp["expires"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["expires"])) + ) if date: - tmp['created'] = time.strftime('%Y-%m-%d', - time.localtime(float(_key['date']))) + tmp["created"] = time.strftime( + "%Y-%m-%d", time.localtime(float(_key["date"])) + ) if length: - tmp['keyLength'] = _key['length'] + tmp["keyLength"] = _key["length"] if owner_trust: - tmp['ownerTrust'] = LETTER_TRUST_DICT[_key['ownertrust']] + tmp["ownerTrust"] = LETTER_TRUST_DICT[_key["ownertrust"]] if trust: - tmp['trust'] = LETTER_TRUST_DICT[_key['trust']] + tmp["trust"] = LETTER_TRUST_DICT[_key["trust"]] if not tmp: return False else: @@ -681,11 +711,8 @@ def get_secret_key(keyid=None, fingerprint=None, user=None, gnupghome=None): @_restore_ownership -def import_key(text=None, - filename=None, - user=None, - gnupghome=None): - r''' +def import_key(text=None, filename=None, user=None, gnupghome=None): + r""" Import a key from text or file text @@ -709,54 +736,51 @@ def import_key(text=None, salt '*' gpg.import_key text='-----BEGIN PGP PUBLIC KEY BLOCK-----\n ... -----END PGP PUBLIC KEY BLOCK-----' salt '*' gpg.import_key filename='/path/to/public-key-file' - ''' - ret = { - 'res': True, - 'message': '' - } + """ + ret = {"res": True, "message": ""} gpg = _create_gpg(user, gnupghome) if not text and not filename: - raise SaltInvocationError('filename or text must be passed.') + raise SaltInvocationError("filename or text must be passed.") if filename: try: - with salt.utils.files.flopen(filename, 'rb') as _fp: + with salt.utils.files.flopen(filename, "rb") as _fp: text = salt.utils.stringutils.to_unicode(_fp.read()) except IOError: - raise SaltInvocationError('filename does not exist.') + raise SaltInvocationError("filename does not exist.") imported_data = gpg.import_keys(text) if GPG_1_3_1: counts = imported_data.counts - if counts.get('imported') or counts.get('imported_rsa'): - ret['message'] = 'Successfully imported key(s).' - elif counts.get('unchanged'): - ret['message'] = 'Key(s) already exist in keychain.' - elif counts.get('not_imported'): - ret['res'] = False - ret['message'] = 'Unable to import key.' - elif not counts.get('count'): - ret['res'] = False - ret['message'] = 'Unable to import key.' + if counts.get("imported") or counts.get("imported_rsa"): + ret["message"] = "Successfully imported key(s)." + elif counts.get("unchanged"): + ret["message"] = "Key(s) already exist in keychain." + elif counts.get("not_imported"): + ret["res"] = False + ret["message"] = "Unable to import key." + elif not counts.get("count"): + ret["res"] = False + ret["message"] = "Unable to import key." else: if imported_data.imported or imported_data.imported_rsa: - ret['message'] = 'Successfully imported key(s).' + ret["message"] = "Successfully imported key(s)." elif imported_data.unchanged: - ret['message'] = 'Key(s) already exist in keychain.' + ret["message"] = "Key(s) already exist in keychain." elif imported_data.not_imported: - ret['res'] = False - ret['message'] = 'Unable to import key.' + ret["res"] = False + ret["message"] = "Unable to import key." elif not imported_data.count: - ret['res'] = False - ret['message'] = 'Unable to import key.' + ret["res"] = False + ret["message"] = "Unable to import key." return ret def export_key(keyids=None, secret=False, user=None, gnupghome=None): - ''' + """ Export a key from the GPG keychain keyids @@ -786,17 +810,17 @@ def export_key(keyids=None, secret=False, user=None, gnupghome=None): salt '*' gpg.export_key keyids="['3FAD9F1E','3FBD8F1E']" user=username - ''' + """ gpg = _create_gpg(user, gnupghome) if isinstance(keyids, six.string_types): - keyids = keyids.split(',') + keyids = keyids.split(",") return gpg.export_keys(keyids, secret) @_restore_ownership def receive_keys(keyserver=None, keys=None, user=None, gnupghome=None): - ''' + """ Receive key(s) from keyserver and add them to keychain keyserver @@ -824,38 +848,35 @@ def receive_keys(keyserver=None, keys=None, user=None, gnupghome=None): salt '*' gpg.receive_keys keys=3FAD9F1E user=username - ''' - ret = { - 'res': True, - 'changes': {}, - 'message': [] - } + """ + ret = {"res": True, "changes": {}, "message": []} gpg = _create_gpg(user, gnupghome) if not keyserver: - keyserver = 'pgp.mit.edu' + keyserver = "pgp.mit.edu" if isinstance(keys, six.string_types): - keys = keys.split(',') + keys = keys.split(",") recv_data = gpg.recv_keys(keyserver, *keys) for result in recv_data.results: - if 'ok' in result: - if result['ok'] == '1': - ret['message'].append('Key {0} added to keychain'.format(result['fingerprint'])) - elif result['ok'] == '0': - ret['message'].append('Key {0} already exists in keychain'.format(result['fingerprint'])) - elif 'problem' in result: - ret['message'].append('Unable to add key to keychain') + if "ok" in result: + if result["ok"] == "1": + ret["message"].append( + "Key {0} added to keychain".format(result["fingerprint"]) + ) + elif result["ok"] == "0": + ret["message"].append( + "Key {0} already exists in keychain".format(result["fingerprint"]) + ) + elif "problem" in result: + ret["message"].append("Unable to add key to keychain") return ret -def trust_key(keyid=None, - fingerprint=None, - trust_level=None, - user=None): - ''' +def trust_key(keyid=None, fingerprint=None, trust_level=None, user=None): + """ Set the trust level for a key in GPG keychain keyid @@ -882,83 +903,85 @@ def trust_key(keyid=None, salt '*' gpg.trust_key fingerprint='53C96788253E58416D20BCD352952C84C3252192' trust_level='not_trusted' salt '*' gpg.trust_key keys=3FAD9F1E trust_level='ultimately' user='username' - ''' - ret = { - 'res': True, - 'message': '' - } + """ + ret = {"res": True, "message": ""} - _VALID_TRUST_LEVELS = ['expired', 'unknown', - 'not_trusted', 'marginally', - 'fully', 'ultimately'] + _VALID_TRUST_LEVELS = [ + "expired", + "unknown", + "not_trusted", + "marginally", + "fully", + "ultimately", + ] if fingerprint and keyid: - ret['res'] = False - ret['message'] = 'Only specify one argument, fingerprint or keyid' + ret["res"] = False + ret["message"] = "Only specify one argument, fingerprint or keyid" return ret if not fingerprint: if keyid: key = get_key(keyid, user=user) if key: - if 'fingerprint' not in key: - ret['res'] = False - ret['message'] = 'Fingerprint not found for keyid {0}'.format(keyid) + if "fingerprint" not in key: + ret["res"] = False + ret["message"] = "Fingerprint not found for keyid {0}".format(keyid) return ret - fingerprint = key['fingerprint'] + fingerprint = key["fingerprint"] else: - ret['res'] = False - ret['message'] = 'KeyID {0} not in GPG keychain'.format(keyid) + ret["res"] = False + ret["message"] = "KeyID {0} not in GPG keychain".format(keyid) return ret else: - ret['res'] = False - ret['message'] = 'Required argument, fingerprint or keyid' + ret["res"] = False + ret["message"] = "Required argument, fingerprint or keyid" return ret if trust_level not in _VALID_TRUST_LEVELS: - return 'ERROR: Valid trust levels - {0}'.format(','.join(_VALID_TRUST_LEVELS)) + return "ERROR: Valid trust levels - {0}".format(",".join(_VALID_TRUST_LEVELS)) - stdin = '{0}:{1}\n'.format(fingerprint, NUM_TRUST_DICT[trust_level]) - cmd = [_gpg(), '--import-ownertrust'] + stdin = "{0}:{1}\n".format(fingerprint, NUM_TRUST_DICT[trust_level]) + cmd = [_gpg(), "--import-ownertrust"] _user = user - if user == 'salt': - homeDir = os.path.join(__salt__['config.get']('config_dir'), 'gpgkeys') - cmd.extend(['--homedir', homeDir]) - _user = 'root' - res = __salt__['cmd.run_all'](cmd, - stdin=stdin, - runas=_user, - python_shell=False) + if user == "salt": + homeDir = os.path.join(__salt__["config.get"]("config_dir"), "gpgkeys") + cmd.extend(["--homedir", homeDir]) + _user = "root" + res = __salt__["cmd.run_all"](cmd, stdin=stdin, runas=_user, python_shell=False) - if not res['retcode'] == 0: - ret['res'] = False - ret['message'] = res['stderr'] + if not res["retcode"] == 0: + ret["res"] = False + ret["message"] = res["stderr"] else: - if res['stderr']: - _match = re.findall(r'\d', res['stderr']) + if res["stderr"]: + _match = re.findall(r"\d", res["stderr"]) if len(_match) == 2: - ret['fingerprint'] = fingerprint - ret['message'] = 'Changing ownership trust from {0} to {1}.'.format( - INV_NUM_TRUST_DICT[_match[0]], - INV_NUM_TRUST_DICT[_match[1]] - ) + ret["fingerprint"] = fingerprint + ret["message"] = "Changing ownership trust from {0} to {1}.".format( + INV_NUM_TRUST_DICT[_match[0]], INV_NUM_TRUST_DICT[_match[1]] + ) else: - ret['fingerprint'] = fingerprint - ret['message'] = 'Setting ownership trust to {0}.'.format(INV_NUM_TRUST_DICT[_match[0]]) + ret["fingerprint"] = fingerprint + ret["message"] = "Setting ownership trust to {0}.".format( + INV_NUM_TRUST_DICT[_match[0]] + ) else: - ret['message'] = res['stderr'] + ret["message"] = res["stderr"] return ret -def sign(user=None, - keyid=None, - text=None, - filename=None, - output=None, - use_passphrase=False, - gnupghome=None): - ''' +def sign( + user=None, + keyid=None, + text=None, + filename=None, + output=None, + use_passphrase=False, + gnupghome=None, +): + """ Sign message or file user @@ -996,12 +1019,12 @@ def sign(user=None, salt '*' gpg.sign filename='/path/to/important.file' use_passphrase=True - ''' + """ gpg = _create_gpg(user, gnupghome) if use_passphrase: - gpg_passphrase = __salt__['pillar.get']('gpg_passphrase') + gpg_passphrase = __salt__["pillar.get"]("gpg_passphrase") if not gpg_passphrase: - raise SaltInvocationError('gpg_passphrase not available in pillar.') + raise SaltInvocationError("gpg_passphrase not available in pillar.") else: gpg_passphrase = None @@ -1009,32 +1032,31 @@ def sign(user=None, gnupg_version = _LooseVersion(gnupg.__version__) if text: - if gnupg_version >= _LooseVersion('1.3.1'): + if gnupg_version >= _LooseVersion("1.3.1"): signed_data = gpg.sign(text, default_key=keyid, passphrase=gpg_passphrase) else: signed_data = gpg.sign(text, keyid=keyid, passphrase=gpg_passphrase) elif filename: - with salt.utils.files.flopen(filename, 'rb') as _fp: - if gnupg_version >= _LooseVersion('1.3.1'): - signed_data = gpg.sign(text, default_key=keyid, passphrase=gpg_passphrase) + with salt.utils.files.flopen(filename, "rb") as _fp: + if gnupg_version >= _LooseVersion("1.3.1"): + signed_data = gpg.sign( + text, default_key=keyid, passphrase=gpg_passphrase + ) else: signed_data = gpg.sign_file(_fp, keyid=keyid, passphrase=gpg_passphrase) if output: - with salt.utils.files.flopen(output, 'wb') as fout: + with salt.utils.files.flopen(output, "wb") as fout: fout.write(salt.utils.stringutils.to_bytes(signed_data.data)) else: - raise SaltInvocationError('filename or text must be passed.') + raise SaltInvocationError("filename or text must be passed.") return signed_data.data -def verify(text=None, - user=None, - filename=None, - gnupghome=None, - signature=None, - trustmodel=None): - ''' +def verify( + text=None, user=None, filename=None, gnupghome=None, signature=None, trustmodel=None +): + """ Verify a message or file text @@ -1077,19 +1099,21 @@ def verify(text=None, salt '*' gpg.verify filename='/path/to/important.file' use_passphrase=True salt '*' gpg.verify filename='/path/to/important.file' trustmodel=direct - ''' + """ gpg = _create_gpg(user) - trustmodels = ('pgp', 'classic', 'tofu', 'tofu+pgp', 'direct', 'always', 'auto') + trustmodels = ("pgp", "classic", "tofu", "tofu+pgp", "direct", "always", "auto") if trustmodel and trustmodel not in trustmodels: - msg = 'Invalid trustmodel defined: {}. Use one of: {}'.format(trustmodel, ', '.join(trustmodels)) + msg = "Invalid trustmodel defined: {}. Use one of: {}".format( + trustmodel, ", ".join(trustmodels) + ) log.warn(msg) - return {'res': False, 'message': msg} + return {"res": False, "message": msg} extra_args = [] if trustmodel: - extra_args.extend(['--trust-model', trustmodel]) + extra_args.extend(["--trust-model", trustmodel]) if text: verified = gpg.verify(text, extra_args=extra_args) @@ -1097,37 +1121,39 @@ def verify(text=None, if signature: # need to call with fopen instead of flopen due to: # https://bitbucket.org/vinay.sajip/python-gnupg/issues/76/verify_file-closes-passed-file-handle - with salt.utils.files.fopen(signature, 'rb') as _fp: + with salt.utils.files.fopen(signature, "rb") as _fp: verified = gpg.verify_file(_fp, filename, extra_args=extra_args) else: - with salt.utils.files.flopen(filename, 'rb') as _fp: + with salt.utils.files.flopen(filename, "rb") as _fp: verified = gpg.verify_file(_fp, extra_args=extra_args) else: - raise SaltInvocationError('filename or text must be passed.') + raise SaltInvocationError("filename or text must be passed.") ret = {} if verified.trust_level is not None: - ret['res'] = True - ret['username'] = verified.username - ret['key_id'] = verified.key_id - ret['trust_level'] = VERIFY_TRUST_LEVELS[six.text_type(verified.trust_level)] - ret['message'] = 'The signature is verified.' + ret["res"] = True + ret["username"] = verified.username + ret["key_id"] = verified.key_id + ret["trust_level"] = VERIFY_TRUST_LEVELS[six.text_type(verified.trust_level)] + ret["message"] = "The signature is verified." else: - ret['res'] = False - ret['message'] = 'The signature could not be verified.' + ret["res"] = False + ret["message"] = "The signature could not be verified." return ret -def encrypt(user=None, - recipients=None, - text=None, - filename=None, - output=None, - sign=None, - use_passphrase=False, - gnupghome=None, - bare=False): - ''' +def encrypt( + user=None, + recipients=None, + text=None, + filename=None, + output=None, + sign=None, + use_passphrase=False, + gnupghome=None, + bare=False, +): + """ Encrypt a message or file user @@ -1172,18 +1198,15 @@ def encrypt(user=None, salt '*' gpg.encrypt filename='/path/to/important.file' use_passphrase=True - ''' - ret = { - 'res': True, - 'comment': '' - } + """ + ret = {"res": True, "comment": ""} gpg = _create_gpg(user, gnupghome) if use_passphrase: - gpg_passphrase = __salt__['pillar.get']('gpg_passphrase') + gpg_passphrase = __salt__["pillar.get"]("gpg_passphrase") if not gpg_passphrase: - raise SaltInvocationError('gpg_passphrase not available in pillar.') - gpg_passphrase = gpg_passphrase['gpg_passphrase'] + raise SaltInvocationError("gpg_passphrase not available in pillar.") + gpg_passphrase = gpg_passphrase["gpg_passphrase"] else: gpg_passphrase = None @@ -1193,45 +1216,59 @@ def encrypt(user=None, if GPG_1_3_1: # This version does not allow us to encrypt using the # file stream # have to read in the contents and encrypt. - with salt.utils.files.flopen(filename, 'rb') as _fp: + with salt.utils.files.flopen(filename, "rb") as _fp: _contents = salt.utils.stringutils.to_unicode(_fp.read()) - result = gpg.encrypt(_contents, recipients, passphrase=gpg_passphrase, output=output) + result = gpg.encrypt( + _contents, recipients, passphrase=gpg_passphrase, output=output + ) else: # This version allows encrypting the file stream - with salt.utils.files.flopen(filename, 'rb') as _fp: + with salt.utils.files.flopen(filename, "rb") as _fp: if output: - result = gpg.encrypt_file(_fp, recipients, passphrase=gpg_passphrase, output=output, sign=sign) + result = gpg.encrypt_file( + _fp, + recipients, + passphrase=gpg_passphrase, + output=output, + sign=sign, + ) else: - result = gpg.encrypt_file(_fp, recipients, passphrase=gpg_passphrase, sign=sign) + result = gpg.encrypt_file( + _fp, recipients, passphrase=gpg_passphrase, sign=sign + ) else: - raise SaltInvocationError('filename or text must be passed.') + raise SaltInvocationError("filename or text must be passed.") if result.ok: if not bare: if output: - ret['comment'] = 'Encrypted data has been written to {0}'.format(output) + ret["comment"] = "Encrypted data has been written to {0}".format(output) else: - ret['comment'] = result.data + ret["comment"] = result.data else: ret = result.data else: if not bare: - ret['res'] = False - ret['comment'] = '{0}.\nPlease check the salt-minion log.'.format(result.status) + ret["res"] = False + ret["comment"] = "{0}.\nPlease check the salt-minion log.".format( + result.status + ) else: ret = False log.error(result.stderr) return ret -def decrypt(user=None, - text=None, - filename=None, - output=None, - use_passphrase=False, - gnupghome=None, - bare=False): - ''' +def decrypt( + user=None, + text=None, + filename=None, + output=None, + use_passphrase=False, + gnupghome=None, + bare=False, +): + """ Decrypt a message or file user @@ -1267,43 +1304,42 @@ def decrypt(user=None, salt '*' gpg.decrypt filename='/path/to/important.file.gpg' use_passphrase=True - ''' - ret = { - 'res': True, - 'comment': '' - } + """ + ret = {"res": True, "comment": ""} gpg = _create_gpg(user, gnupghome) if use_passphrase: - gpg_passphrase = __salt__['pillar.get']('gpg_passphrase') + gpg_passphrase = __salt__["pillar.get"]("gpg_passphrase") if not gpg_passphrase: - raise SaltInvocationError('gpg_passphrase not available in pillar.') - gpg_passphrase = gpg_passphrase['gpg_passphrase'] + raise SaltInvocationError("gpg_passphrase not available in pillar.") + gpg_passphrase = gpg_passphrase["gpg_passphrase"] else: gpg_passphrase = None if text: result = gpg.decrypt(text, passphrase=gpg_passphrase) elif filename: - with salt.utils.files.flopen(filename, 'rb') as _fp: + with salt.utils.files.flopen(filename, "rb") as _fp: if output: result = gpg.decrypt_file(_fp, passphrase=gpg_passphrase, output=output) else: result = gpg.decrypt_file(_fp, passphrase=gpg_passphrase) else: - raise SaltInvocationError('filename or text must be passed.') + raise SaltInvocationError("filename or text must be passed.") if result.ok: if not bare: if output: - ret['comment'] = 'Decrypted data has been written to {0}'.format(output) + ret["comment"] = "Decrypted data has been written to {0}".format(output) else: - ret['comment'] = result.data + ret["comment"] = result.data else: ret = result.data else: if not bare: - ret['res'] = False - ret['comment'] = '{0}.\nPlease check the salt-minion log.'.format(result.status) + ret["res"] = False + ret["comment"] = "{0}.\nPlease check the salt-minion log.".format( + result.status + ) else: ret = False diff --git a/salt/modules/grafana4.py b/salt/modules/grafana4.py index 4d1d4db393f..402c92f0059 100644 --- a/salt/modules/grafana4.py +++ b/salt/modules/grafana4.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for working with the Grafana v4 API .. versionadded:: 2017.7.0 @@ -19,51 +19,53 @@ Module for working with the Grafana v4 API grafana_user: admin grafana_password: admin grafana_timeout: 3 -''' +""" from __future__ import absolute_import, print_function, unicode_literals +from salt.ext.six import string_types + try: import requests + HAS_LIBS = True except ImportError: HAS_LIBS = False -from salt.ext.six import string_types - -__virtualname__ = 'grafana4' +__virtualname__ = "grafana4" def __virtual__(): - ''' + """ Only load if requests is installed - ''' + """ if HAS_LIBS: return __virtualname__ else: - return False, 'The "{0}" module could not be loaded: ' \ - '"requests" is not installed.'.format(__virtualname__) + return ( + False, + 'The "{0}" module could not be loaded: ' + '"requests" is not installed.'.format(__virtualname__), + ) def _get_headers(profile): - headers = {'Content-type': 'application/json'} - if profile.get('grafana_token', False): - headers['Authorization'] = 'Bearer {0}'.format( - profile['grafana_token']) + headers = {"Content-type": "application/json"} + if profile.get("grafana_token", False): + headers["Authorization"] = "Bearer {0}".format(profile["grafana_token"]) return headers def _get_auth(profile): - if profile.get('grafana_token', False): + if profile.get("grafana_token", False): return None return requests.auth.HTTPBasicAuth( - profile['grafana_user'], - profile['grafana_password'] + profile["grafana_user"], profile["grafana_password"] ) -def get_users(profile='grafana'): - ''' +def get_users(profile="grafana"): + """ List all users. profile @@ -75,22 +77,22 @@ def get_users(profile='grafana'): .. code-block:: bash salt '*' grafana4.get_users - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.get( - '{0}/api/users'.format(profile['grafana_url']), + "{0}/api/users".format(profile["grafana_url"]), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def get_user(login, profile='grafana'): - ''' +def get_user(login, profile="grafana"): + """ Show a single user. login @@ -105,16 +107,16 @@ def get_user(login, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_user - ''' + """ data = get_users(profile) for user in data: - if user['login'] == login: + if user["login"] == login: return user return None -def get_user_data(userid, profile='grafana'): - ''' +def get_user_data(userid, profile="grafana"): + """ Get user data. userid @@ -129,22 +131,22 @@ def get_user_data(userid, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_user_data - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.get( - '{0}/api/users/{1}'.format(profile['grafana_url'], userid), + "{0}/api/users/{1}".format(profile["grafana_url"], userid), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def create_user(profile='grafana', **kwargs): - ''' +def create_user(profile="grafana", **kwargs): + """ Create a new user. login @@ -168,23 +170,23 @@ def create_user(profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.create_user login= password= email= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.post( - '{0}/api/admin/users'.format(profile['grafana_url']), + "{0}/api/admin/users".format(profile["grafana_url"]), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def update_user(userid, profile='grafana', **kwargs): - ''' +def update_user(userid, profile="grafana", **kwargs): + """ Update an existing user. userid @@ -208,23 +210,23 @@ def update_user(userid, profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.update_user login= email= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.put( - '{0}/api/users/{1}'.format(profile['grafana_url'], userid), + "{0}/api/users/{1}".format(profile["grafana_url"], userid), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def update_user_password(userid, profile='grafana', **kwargs): - ''' +def update_user_password(userid, profile="grafana", **kwargs): + """ Update a user password. userid @@ -242,24 +244,23 @@ def update_user_password(userid, profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.update_user_password password= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.put( - '{0}/api/admin/users/{1}/password'.format( - profile['grafana_url'], userid), + "{0}/api/admin/users/{1}/password".format(profile["grafana_url"], userid), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def update_user_permissions(userid, profile='grafana', **kwargs): - ''' +def update_user_permissions(userid, profile="grafana", **kwargs): + """ Update a user password. userid @@ -277,24 +278,23 @@ def update_user_permissions(userid, profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.update_user_permissions isGrafanaAdmin= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.put( - '{0}/api/admin/users/{1}/permissions'.format( - profile['grafana_url'], userid), + "{0}/api/admin/users/{1}/permissions".format(profile["grafana_url"], userid), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def delete_user(userid, profile='grafana'): - ''' +def delete_user(userid, profile="grafana"): + """ Delete a user. userid @@ -309,22 +309,22 @@ def delete_user(userid, profile='grafana'): .. code-block:: bash salt '*' grafana4.delete_user - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.delete( - '{0}/api/admin/users/{1}'.format(profile['grafana_url'], userid), + "{0}/api/admin/users/{1}".format(profile["grafana_url"], userid), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def get_user_orgs(userid, profile='grafana'): - ''' +def get_user_orgs(userid, profile="grafana"): + """ Get the list of organisations a user belong to. userid @@ -339,22 +339,22 @@ def get_user_orgs(userid, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_user_orgs - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.get( - '{0}/api/users/{1}/orgs'.format(profile['grafana_url'], userid), + "{0}/api/users/{1}/orgs".format(profile["grafana_url"], userid), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def delete_user_org(userid, orgid, profile='grafana'): - ''' +def delete_user_org(userid, orgid, profile="grafana"): + """ Remove a user from an organization. userid @@ -372,23 +372,22 @@ def delete_user_org(userid, orgid, profile='grafana'): .. code-block:: bash salt '*' grafana4.delete_user_org - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.delete( - '{0}/api/orgs/{1}/users/{2}'.format( - profile['grafana_url'], orgid, userid), + "{0}/api/orgs/{1}/users/{2}".format(profile["grafana_url"], orgid, userid), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def get_orgs(profile='grafana'): - ''' +def get_orgs(profile="grafana"): + """ List all organizations. profile @@ -400,22 +399,22 @@ def get_orgs(profile='grafana'): .. code-block:: bash salt '*' grafana4.get_orgs - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.get( - '{0}/api/orgs'.format(profile['grafana_url']), + "{0}/api/orgs".format(profile["grafana_url"]), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def get_org(name, profile='grafana'): - ''' +def get_org(name, profile="grafana"): + """ Show a single organization. name @@ -430,22 +429,22 @@ def get_org(name, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_org - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.get( - '{0}/api/orgs/name/{1}'.format(profile['grafana_url'], name), + "{0}/api/orgs/name/{1}".format(profile["grafana_url"], name), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def switch_org(orgname, profile='grafana'): - ''' +def switch_org(orgname, profile="grafana"): + """ Switch the current organization. name @@ -460,23 +459,23 @@ def switch_org(orgname, profile='grafana'): .. code-block:: bash salt '*' grafana4.switch_org - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) org = get_org(orgname, profile) response = requests.post( - '{0}/api/user/using/{1}'.format(profile['grafana_url'], org['id']), + "{0}/api/user/using/{1}".format(profile["grafana_url"], org["id"]), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return org -def get_org_users(orgname=None, profile='grafana'): - ''' +def get_org_users(orgname=None, profile="grafana"): + """ Get the list of users that belong to the organization. orgname @@ -491,24 +490,24 @@ def get_org_users(orgname=None, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_org_users - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.get( - '{0}/api/org/users'.format(profile['grafana_url']), + "{0}/api/org/users".format(profile["grafana_url"]), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def create_org_user(orgname=None, profile='grafana', **kwargs): - ''' +def create_org_user(orgname=None, profile="grafana", **kwargs): + """ Add user to the organization. loginOrEmail @@ -533,25 +532,25 @@ def create_org_user(orgname=None, profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.create_org_user loginOrEmail= role= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.post( - '{0}/api/org/users'.format(profile['grafana_url']), + "{0}/api/org/users".format(profile["grafana_url"]), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def update_org_user(userid, orgname=None, profile='grafana', **kwargs): - ''' +def update_org_user(userid, orgname=None, profile="grafana", **kwargs): + """ Update user role in the organization. userid @@ -579,25 +578,25 @@ def update_org_user(userid, orgname=None, profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.update_org_user loginOrEmail= role= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.patch( - '{0}/api/org/users/{1}'.format(profile['grafana_url'], userid), + "{0}/api/org/users/{1}".format(profile["grafana_url"], userid), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def delete_org_user(userid, orgname=None, profile='grafana'): - ''' +def delete_org_user(userid, orgname=None, profile="grafana"): + """ Remove user from the organization. userid @@ -615,24 +614,24 @@ def delete_org_user(userid, orgname=None, profile='grafana'): .. code-block:: bash salt '*' grafana4.delete_org_user - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.delete( - '{0}/api/org/users/{1}'.format(profile['grafana_url'], userid), + "{0}/api/org/users/{1}".format(profile["grafana_url"], userid), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def get_org_address(orgname=None, profile='grafana'): - ''' +def get_org_address(orgname=None, profile="grafana"): + """ Get the organization address. orgname @@ -647,24 +646,24 @@ def get_org_address(orgname=None, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_org_address - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.get( - '{0}/api/org/address'.format(profile['grafana_url']), + "{0}/api/org/address".format(profile["grafana_url"]), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def update_org_address(orgname=None, profile='grafana', **kwargs): - ''' +def update_org_address(orgname=None, profile="grafana", **kwargs): + """ Update the organization address. orgname @@ -697,25 +696,25 @@ def update_org_address(orgname=None, profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.update_org_address country= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.put( - '{0}/api/org/address'.format(profile['grafana_url']), + "{0}/api/org/address".format(profile["grafana_url"]), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def get_org_prefs(orgname=None, profile='grafana'): - ''' +def get_org_prefs(orgname=None, profile="grafana"): + """ Get the organization preferences. orgname @@ -730,24 +729,24 @@ def get_org_prefs(orgname=None, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_org_prefs - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.get( - '{0}/api/org/preferences'.format(profile['grafana_url']), + "{0}/api/org/preferences".format(profile["grafana_url"]), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def update_org_prefs(orgname=None, profile='grafana', **kwargs): - ''' +def update_org_prefs(orgname=None, profile="grafana", **kwargs): + """ Update the organization preferences. orgname @@ -771,25 +770,25 @@ def update_org_prefs(orgname=None, profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.update_org_prefs theme= timezone= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.put( - '{0}/api/org/preferences'.format(profile['grafana_url']), + "{0}/api/org/preferences".format(profile["grafana_url"]), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def create_org(profile='grafana', **kwargs): - ''' +def create_org(profile="grafana", **kwargs): + """ Create a new organization. name @@ -804,23 +803,23 @@ def create_org(profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.create_org - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.post( - '{0}/api/orgs'.format(profile['grafana_url']), + "{0}/api/orgs".format(profile["grafana_url"]), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def update_org(orgid, profile='grafana', **kwargs): - ''' +def update_org(orgid, profile="grafana", **kwargs): + """ Update an existing organization. orgid @@ -838,23 +837,23 @@ def update_org(orgid, profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.update_org name= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.put( - '{0}/api/orgs/{1}'.format(profile['grafana_url'], orgid), + "{0}/api/orgs/{1}".format(profile["grafana_url"], orgid), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def delete_org(orgid, profile='grafana'): - ''' +def delete_org(orgid, profile="grafana"): + """ Delete an organization. orgid @@ -869,22 +868,22 @@ def delete_org(orgid, profile='grafana'): .. code-block:: bash salt '*' grafana4.delete_org - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.delete( - '{0}/api/orgs/{1}'.format(profile['grafana_url'], orgid), + "{0}/api/orgs/{1}".format(profile["grafana_url"], orgid), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def get_datasources(orgname=None, profile='grafana'): - ''' +def get_datasources(orgname=None, profile="grafana"): + """ List all datasources in an organisation. orgname @@ -899,24 +898,24 @@ def get_datasources(orgname=None, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_datasources - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.get( - '{0}/api/datasources'.format(profile['grafana_url']), + "{0}/api/datasources".format(profile["grafana_url"]), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def get_datasource(name, orgname=None, profile='grafana'): - ''' +def get_datasource(name, orgname=None, profile="grafana"): + """ Show a single datasource in an organisation. name @@ -934,16 +933,16 @@ def get_datasource(name, orgname=None, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_datasource - ''' + """ data = get_datasources(orgname=orgname, profile=profile) for datasource in data: - if datasource['name'] == name: + if datasource["name"] == name: return datasource return None -def create_datasource(orgname=None, profile='grafana', **kwargs): - ''' +def create_datasource(orgname=None, profile="grafana", **kwargs): + """ Create a new datasource in an organisation. name @@ -1003,25 +1002,25 @@ def create_datasource(orgname=None, profile='grafana', **kwargs): salt '*' grafana4.create_datasource - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.post( - '{0}/api/datasources'.format(profile['grafana_url']), + "{0}/api/datasources".format(profile["grafana_url"]), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def update_datasource(datasourceid, orgname=None, profile='grafana', **kwargs): - ''' +def update_datasource(datasourceid, orgname=None, profile="grafana", **kwargs): + """ Update a datasource. datasourceid @@ -1081,25 +1080,25 @@ def update_datasource(datasourceid, orgname=None, profile='grafana', **kwargs): salt '*' grafana4.update_datasource - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.put( - '{0}/api/datasources/{1}'.format(profile['grafana_url'], datasourceid), + "{0}/api/datasources/{1}".format(profile["grafana_url"], datasourceid), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() # temporary fix for https://github.com/grafana/grafana/issues/6869 - #return response.json() + # return response.json() return {} -def delete_datasource(datasourceid, orgname=None, profile='grafana'): - ''' +def delete_datasource(datasourceid, orgname=None, profile="grafana"): + """ Delete a datasource. datasourceid @@ -1114,22 +1113,22 @@ def delete_datasource(datasourceid, orgname=None, profile='grafana'): .. code-block:: bash salt '*' grafana4.delete_datasource - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) response = requests.delete( - '{0}/api/datasources/{1}'.format(profile['grafana_url'], datasourceid), + "{0}/api/datasources/{1}".format(profile["grafana_url"], datasourceid), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def get_dashboard(slug, orgname=None, profile='grafana'): - ''' +def get_dashboard(slug, orgname=None, profile="grafana"): + """ Get a dashboard. slug @@ -1147,27 +1146,27 @@ def get_dashboard(slug, orgname=None, profile='grafana'): .. code-block:: bash salt '*' grafana4.get_dashboard - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.get( - '{0}/api/dashboards/db/{1}'.format(profile['grafana_url'], slug), + "{0}/api/dashboards/db/{1}".format(profile["grafana_url"], slug), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) data = response.json() if response.status_code == 404: return None if response.status_code >= 400: response.raise_for_status() - return data.get('dashboard') + return data.get("dashboard") -def delete_dashboard(slug, orgname=None, profile='grafana'): - ''' +def delete_dashboard(slug, orgname=None, profile="grafana"): + """ Delete a dashboard. slug @@ -1185,24 +1184,24 @@ def delete_dashboard(slug, orgname=None, profile='grafana'): .. code-block:: bash salt '*' grafana4.delete_dashboard - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.delete( - '{0}/api/dashboards/db/{1}'.format(profile['grafana_url'], slug), + "{0}/api/dashboards/db/{1}".format(profile["grafana_url"], slug), auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() return response.json() -def create_update_dashboard(orgname=None, profile='grafana', **kwargs): - ''' +def create_update_dashboard(orgname=None, profile="grafana", **kwargs): + """ Create or update a dashboard. dashboard @@ -1223,17 +1222,17 @@ def create_update_dashboard(orgname=None, profile='grafana', **kwargs): .. code-block:: bash salt '*' grafana4.create_update_dashboard dashboard= overwrite=True orgname= - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) if orgname: switch_org(orgname, profile) response = requests.post( - "{0}/api/dashboards/db".format(profile.get('grafana_url')), + "{0}/api/dashboards/db".format(profile.get("grafana_url")), json=kwargs, auth=_get_auth(profile), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) if response.status_code >= 400: response.raise_for_status() diff --git a/salt/modules/grains.py b/salt/modules/grains.py index b8474006d41..3c4f8dd699b 100644 --- a/salt/modules/grains.py +++ b/salt/modules/grains.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return/control aspects of the grains data Grains set or altered with this module are stored in the 'grains' @@ -8,20 +8,19 @@ file on the minions. By default, this file is located at: ``/etc/salt/grains`` .. Note:: This does **NOT** override any grains set in the minion config file. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import collections +import logging +import math +import operator import os import random -import logging -import operator -import collections -import math from functools import reduce # pylint: disable=redefined-builtin -# Import Salt libs -from salt.ext import six import salt.utils.compat import salt.utils.data import salt.utils.files @@ -30,55 +29,58 @@ import salt.utils.platform import salt.utils.yaml from salt.defaults import DEFAULT_TARGET_DELIM from salt.exceptions import SaltException + +# Import Salt libs +from salt.ext import six from salt.ext.six.moves import range -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] # Seed the grains dict so cython will build __grains__ = {} # Change the default outputter to make it more readable __outputter__ = { - 'items': 'nested', - 'item': 'nested', - 'setval': 'nested', + "items": "nested", + "item": "nested", + "setval": "nested", } # http://stackoverflow.com/a/12414913/127816 _infinitedict = lambda: collections.defaultdict(_infinitedict) -_non_existent_key = 'NonExistentValueMagicNumberSpK3hnufdHfeBUXCfqVK' +_non_existent_key = "NonExistentValueMagicNumberSpK3hnufdHfeBUXCfqVK" log = logging.getLogger(__name__) def _serial_sanitizer(instr): - '''Replaces the last 1/4 of a string with X's''' + """Replaces the last 1/4 of a string with X's""" length = len(instr) - index = int(math.floor(length * .75)) - return '{0}{1}'.format(instr[:index], 'X' * (length - index)) + index = int(math.floor(length * 0.75)) + return "{0}{1}".format(instr[:index], "X" * (length - index)) -_FQDN_SANITIZER = lambda x: 'MINION.DOMAINNAME' -_HOSTNAME_SANITIZER = lambda x: 'MINION' -_DOMAINNAME_SANITIZER = lambda x: 'DOMAINNAME' +_FQDN_SANITIZER = lambda x: "MINION.DOMAINNAME" +_HOSTNAME_SANITIZER = lambda x: "MINION" +_DOMAINNAME_SANITIZER = lambda x: "DOMAINNAME" # A dictionary of grain -> function mappings for sanitizing grain output. This # is used when the 'sanitize' flag is given. _SANITIZERS = { - 'serialnumber': _serial_sanitizer, - 'domain': _DOMAINNAME_SANITIZER, - 'fqdn': _FQDN_SANITIZER, - 'id': _FQDN_SANITIZER, - 'host': _HOSTNAME_SANITIZER, - 'localhost': _HOSTNAME_SANITIZER, - 'nodename': _HOSTNAME_SANITIZER, + "serialnumber": _serial_sanitizer, + "domain": _DOMAINNAME_SANITIZER, + "fqdn": _FQDN_SANITIZER, + "id": _FQDN_SANITIZER, + "host": _HOSTNAME_SANITIZER, + "localhost": _HOSTNAME_SANITIZER, + "nodename": _HOSTNAME_SANITIZER, } -def get(key, default='', delimiter=DEFAULT_TARGET_DELIM, ordered=True): - ''' +def get(key, default="", delimiter=DEFAULT_TARGET_DELIM, ordered=True): + """ Attempt to retrieve the named value from grains, if the named value is not available return the passed default. The default return is an empty string. @@ -111,20 +113,16 @@ def get(key, default='', delimiter=DEFAULT_TARGET_DELIM, ordered=True): salt '*' grains.get pkg:apache salt '*' grains.get abc::def|ghi delimiter='|' - ''' + """ if ordered is True: grains = __grains__ else: grains = salt.utils.json.loads(salt.utils.json.dumps(__grains__)) - return salt.utils.data.traverse_dict_and_list( - grains, - key, - default, - delimiter) + return salt.utils.data.traverse_dict_and_list(grains, key, default, delimiter) def has_value(key): - ''' + """ Determine whether a key exists in the grains dictionary. Given a grains dictionary that contains the following structure:: @@ -140,15 +138,15 @@ def has_value(key): .. code-block:: bash salt '*' grains.has_value pkg:apache - ''' - return salt.utils.data.traverse_dict_and_list( - __grains__, - key, - KeyError) is not KeyError + """ + return ( + salt.utils.data.traverse_dict_and_list(__grains__, key, KeyError) + is not KeyError + ) def items(sanitize=False): - ''' + """ Return all of the minion's grains CLI Example: @@ -162,7 +160,7 @@ def items(sanitize=False): .. code-block:: bash salt '*' grains.items sanitize=True - ''' + """ if salt.utils.data.is_true(sanitize): out = dict(__grains__) for key, func in six.iteritems(_SANITIZERS): @@ -174,7 +172,7 @@ def items(sanitize=False): def item(*args, **kwargs): - ''' + """ Return one or more grains CLI Example: @@ -189,22 +187,20 @@ def item(*args, **kwargs): .. code-block:: bash salt '*' grains.item host sanitize=True - ''' + """ ret = {} - default = kwargs.get('default', '') - delimiter = kwargs.get('delimiter', DEFAULT_TARGET_DELIM) + default = kwargs.get("default", "") + delimiter = kwargs.get("delimiter", DEFAULT_TARGET_DELIM) try: for arg in args: ret[arg] = salt.utils.data.traverse_dict_and_list( - __grains__, - arg, - default, - delimiter) + __grains__, arg, default, delimiter + ) except KeyError: pass - if salt.utils.data.is_true(kwargs.get('sanitize')): + if salt.utils.data.is_true(kwargs.get("sanitize")): for arg, func in six.iteritems(_SANITIZERS): if arg in ret: ret[arg] = func(ret[arg]) @@ -212,7 +208,7 @@ def item(*args, **kwargs): def setvals(grains, destructive=False): - ''' + """ Set new grains values in the grains config file destructive @@ -224,57 +220,45 @@ def setvals(grains, destructive=False): .. code-block:: bash salt '*' grains.setvals "{'key1': 'val1', 'key2': 'val2'}" - ''' + """ new_grains = grains if not isinstance(new_grains, collections.Mapping): - raise SaltException('setvals grains must be a dictionary.') + raise SaltException("setvals grains must be a dictionary.") grains = {} - if os.path.isfile(__opts__['conf_file']): + if os.path.isfile(__opts__["conf_file"]): if salt.utils.platform.is_proxy(): gfn = os.path.join( - os.path.dirname(__opts__['conf_file']), - 'proxy.d', - __opts__['id'], - 'grains' + os.path.dirname(__opts__["conf_file"]), + "proxy.d", + __opts__["id"], + "grains", ) else: - gfn = os.path.join( - os.path.dirname(__opts__['conf_file']), - 'grains' - ) - elif os.path.isdir(__opts__['conf_file']): + gfn = os.path.join(os.path.dirname(__opts__["conf_file"]), "grains") + elif os.path.isdir(__opts__["conf_file"]): if salt.utils.platform.is_proxy(): gfn = os.path.join( - __opts__['conf_file'], - 'proxy.d', - __opts__['id'], - 'grains' + __opts__["conf_file"], "proxy.d", __opts__["id"], "grains" ) else: - gfn = os.path.join( - __opts__['conf_file'], - 'grains' - ) + gfn = os.path.join(__opts__["conf_file"], "grains") else: if salt.utils.platform.is_proxy(): gfn = os.path.join( - os.path.dirname(__opts__['conf_file']), - 'proxy.d', - __opts__['id'], - 'grains' + os.path.dirname(__opts__["conf_file"]), + "proxy.d", + __opts__["id"], + "grains", ) else: - gfn = os.path.join( - os.path.dirname(__opts__['conf_file']), - 'grains' - ) + gfn = os.path.join(os.path.dirname(__opts__["conf_file"]), "grains") if os.path.isfile(gfn): - with salt.utils.files.fopen(gfn, 'rb') as fp_: + with salt.utils.files.fopen(gfn, "rb") as fp_: try: grains = salt.utils.yaml.safe_load(fp_) except salt.utils.yaml.YAMLError as exc: - return 'Unable to read existing grains file: {0}'.format(exc) + return "Unable to read existing grains file: {0}".format(exc) if not isinstance(grains, dict): grains = {} for key, val in six.iteritems(new_grains): @@ -287,31 +271,25 @@ def setvals(grains, destructive=False): grains[key] = val __grains__[key] = val try: - with salt.utils.files.fopen(gfn, 'w+') as fp_: + with salt.utils.files.fopen(gfn, "w+") as fp_: salt.utils.yaml.safe_dump(grains, fp_, default_flow_style=False) except (IOError, OSError): - log.error( - 'Unable to write to grains file at %s. Check permissions.', - gfn - ) - fn_ = os.path.join(__opts__['cachedir'], 'module_refresh') + log.error("Unable to write to grains file at %s. Check permissions.", gfn) + fn_ = os.path.join(__opts__["cachedir"], "module_refresh") try: - with salt.utils.files.flopen(fn_, 'w+'): + with salt.utils.files.flopen(fn_, "w+"): pass except (IOError, OSError): - log.error( - 'Unable to write to cache file %s. Check permissions.', - fn_ - ) - if not __opts__.get('local', False): + log.error("Unable to write to cache file %s. Check permissions.", fn_) + if not __opts__.get("local", False): # Refresh the grains - __salt__['saltutil.refresh_grains']() + __salt__["saltutil.refresh_grains"]() # Return the grains we just set to confirm everything was OK return new_grains def setval(key, val, destructive=False): - ''' + """ Set a grains value in the grains config file key @@ -330,12 +308,12 @@ def setval(key, val, destructive=False): salt '*' grains.setval key val salt '*' grains.setval key "{'sub-key': 'val', 'sub-key2': 'val2'}" - ''' + """ return setvals({key: val}, destructive) def append(key, val, convert=False, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ .. versionadded:: 0.17.0 Append a value to a list in the grains config file. If the grain doesn't @@ -366,15 +344,15 @@ def append(key, val, convert=False, delimiter=DEFAULT_TARGET_DELIM): .. code-block:: bash salt '*' grains.append key val - ''' + """ grains = get(key, [], delimiter) if convert: if not isinstance(grains, list): grains = [] if grains is None else [grains] if not isinstance(grains, list): - return 'The key {0} is not a valid list'.format(key) + return "The key {0} is not a valid list".format(key) if val in grains: - return 'The val {0} was already in the list {1}'.format(val, key) + return "The val {0} was already in the list {1}".format(val, key) if isinstance(val, list): for item in val: grains.append(item) @@ -392,7 +370,7 @@ def append(key, val, convert=False, delimiter=DEFAULT_TARGET_DELIM): def remove(key, val, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ .. versionadded:: 0.17.0 Remove a value from a list in the grains config file @@ -416,12 +394,12 @@ def remove(key, val, delimiter=DEFAULT_TARGET_DELIM): .. code-block:: bash salt '*' grains.remove key val - ''' + """ grains = get(key, [], delimiter) if not isinstance(grains, list): - return 'The key {0} is not a valid list'.format(key) + return "The key {0} is not a valid list".format(key) if val not in grains: - return 'The val {0} was not in the list {1}'.format(val, key) + return "The val {0} was not in the list {1}".format(val, key) grains.remove(val) while delimiter in key: @@ -435,7 +413,7 @@ def remove(key, val, delimiter=DEFAULT_TARGET_DELIM): def delkey(key): - ''' + """ .. versionadded:: 2017.7.0 Remove a grain completely from the grain system, this will remove the @@ -449,12 +427,12 @@ def delkey(key): .. code-block:: bash salt '*' grains.delkey key - ''' + """ setval(key, None, destructive=True) def delval(key, destructive=False): - ''' + """ .. versionadded:: 0.17.0 Delete a grain value from the grains config file. This will just set the @@ -472,12 +450,12 @@ def delval(key, destructive=False): .. code-block:: bash salt '*' grains.delval key - ''' + """ setval(key, None, destructive=destructive) def ls(): # pylint: disable=C0103 - ''' + """ Return a list of all available grains CLI Example: @@ -485,12 +463,12 @@ def ls(): # pylint: disable=C0103 .. code-block:: bash salt '*' grains.ls - ''' + """ return sorted(__grains__) -def filter_by(lookup_dict, grain='os_family', merge=None, default='default', base=None): - ''' +def filter_by(lookup_dict, grain="os_family", merge=None, default="default", base=None): + """ .. versionadded:: 0.17.0 Look up the given grain in a given dictionary for the current OS and return @@ -591,23 +569,25 @@ def filter_by(lookup_dict, grain='os_family', merge=None, default='default', bas # next one renders {A: {B: G}, D: J} salt '*' grains.filter_by '{default: {A: {B: C}, D: E}, F: {A: {B: G}}, H: {D: I}}' 'xxx' '{D: J}' 'F' 'default' # next same as above when default='H' instead of 'F' renders {A: {B: C}, D: J} - ''' - return salt.utils.data.filter_by(lookup_dict=lookup_dict, - lookup=grain, - traverse=__grains__, - merge=merge, - default=default, - base=base) + """ + return salt.utils.data.filter_by( + lookup_dict=lookup_dict, + lookup=grain, + traverse=__grains__, + merge=merge, + default=default, + base=base, + ) def _dict_from_path(path, val, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Given a lookup string in the form of 'foo:bar:baz" return a nested dictionary of the appropriate depth with the final segment as a value. >>> _dict_from_path('foo:bar:baz', 'somevalue') {"foo": {"bar": {"baz": "somevalue"}} - ''' + """ nested_dict = _infinitedict() keys = path.rsplit(delimiter) lastplace = reduce(operator.getitem, keys[:-1], nested_dict) @@ -616,10 +596,10 @@ def _dict_from_path(path, val, delimiter=DEFAULT_TARGET_DELIM): return nested_dict -def get_or_set_hash(name, - length=8, - chars='abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'): - ''' +def get_or_set_hash( + name, length=8, chars="abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)" +): + """ Perform a one-time generation of a hash and write it to the local grains. If that grain has already been set return the value instead. @@ -648,11 +628,11 @@ def get_or_set_hash(name, as directives by the YAML parser, such as strings beginning with ``%``. To avoid issues when using the output of this function in an SLS file containing YAML+Jinja, surround the call with single quotes. - ''' + """ ret = get(name, None) if ret is None: - val = ''.join([random.SystemRandom().choice(chars) for _ in range(length)]) + val = "".join([random.SystemRandom().choice(chars) for _ in range(length)]) if DEFAULT_TARGET_DELIM in name: root, rest = name.split(DEFAULT_TARGET_DELIM, 1) @@ -666,12 +646,8 @@ def get_or_set_hash(name, return get(name) -def set(key, - val='', - force=False, - destructive=False, - delimiter=DEFAULT_TARGET_DELIM): - ''' +def set(key, val="", force=False, destructive=False, delimiter=DEFAULT_TARGET_DELIM): + """ Set a key to an arbitrary value. It is used like setval but works with nested keys. @@ -695,50 +671,51 @@ def set(key, salt '*' grains.set 'apps:myApp:port' 2209 salt '*' grains.set 'apps:myApp' '{port: 2209}' - ''' + """ - ret = {'comment': '', - 'changes': {}, - 'result': True} + ret = {"comment": "", "changes": {}, "result": True} # Get val type - _new_value_type = 'simple' + _new_value_type = "simple" if isinstance(val, dict): - _new_value_type = 'complex' + _new_value_type = "complex" elif isinstance(val, list): - _new_value_type = 'complex' + _new_value_type = "complex" _non_existent = object() _existing_value = get(key, _non_existent, delimiter) _value = _existing_value - _existing_value_type = 'simple' + _existing_value_type = "simple" if _existing_value is _non_existent: _existing_value_type = None elif isinstance(_existing_value, dict): - _existing_value_type = 'complex' + _existing_value_type = "complex" elif isinstance(_existing_value, list): - _existing_value_type = 'complex' + _existing_value_type = "complex" - if _existing_value_type is not None and _existing_value == val \ - and (val is not None or destructive is not True): - ret['comment'] = 'Grain is already set' + if ( + _existing_value_type is not None + and _existing_value == val + and (val is not None or destructive is not True) + ): + ret["comment"] = "Grain is already set" return ret if _existing_value is not None and not force: - if _existing_value_type == 'complex': - ret['comment'] = ( - 'The key \'{0}\' exists but is a dict or a list. ' - 'Use \'force=True\' to overwrite.'.format(key) + if _existing_value_type == "complex": + ret["comment"] = ( + "The key '{0}' exists but is a dict or a list. " + "Use 'force=True' to overwrite.".format(key) ) - ret['result'] = False + ret["result"] = False return ret - elif _new_value_type == 'complex' and _existing_value_type is not None: - ret['comment'] = ( - 'The key \'{0}\' exists and the given value is a dict or a ' - 'list. Use \'force=True\' to overwrite.'.format(key) + elif _new_value_type == "complex" and _existing_value_type is not None: + ret["comment"] = ( + "The key '{0}' exists and the given value is a dict or a " + "list. Use 'force=True' to overwrite.".format(key) ) - ret['result'] = False + ret["result"] = False return ret else: _value = val @@ -769,26 +746,27 @@ def set(key, elif _existing_value == rest or force: _existing_value = {rest: _value} else: - ret['comment'] = ( - 'The key \'{0}\' value is \'{1}\', which is different from ' - 'the provided key \'{2}\'. Use \'force=True\' to overwrite.' - .format(key, _existing_value, rest) + ret["comment"] = ( + "The key '{0}' value is '{1}', which is different from " + "the provided key '{2}'. Use 'force=True' to overwrite.".format( + key, _existing_value, rest + ) ) - ret['result'] = False + ret["result"] = False return ret _value = _existing_value _setval_ret = setval(key, _value, destructive=destructive) if isinstance(_setval_ret, dict): - ret['changes'] = _setval_ret + ret["changes"] = _setval_ret else: - ret['comment'] = _setval_ret - ret['result'] = False + ret["comment"] = _setval_ret + ret["result"] = False return ret def equals(key, value): - ''' + """ Used to make sure the minion's grain key/value matches. Returns ``True`` if matches otherwise ``False``. @@ -801,7 +779,7 @@ def equals(key, value): salt '*' grains.equals fqdn salt '*' grains.equals systemd:version 219 - ''' + """ return six.text_type(value) == six.text_type(get(key)) diff --git a/salt/modules/groupadd.py b/salt/modules/groupadd.py index 1cf59014604..8aaedef4213 100644 --- a/salt/modules/groupadd.py +++ b/salt/modules/groupadd.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage groups on Linux, OpenBSD and NetBSD .. important:: @@ -7,17 +7,19 @@ Manage groups on Linux, OpenBSD and NetBSD minion, and it is using a different module (or gives an error similar to *'group.info' is not available*), see :ref:`here `. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import logging + import functools +import logging import os -from salt.ext import six import salt.utils.files import salt.utils.stringutils +from salt.ext import six + try: import grp except ImportError: @@ -27,21 +29,24 @@ log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'group' +__virtualname__ = "group" def __virtual__(): - ''' + """ Set the user module if the kernel is Linux or OpenBSD - ''' - if __grains__['kernel'] in ('Linux', 'OpenBSD', 'NetBSD'): + """ + if __grains__["kernel"] in ("Linux", "OpenBSD", "NetBSD"): return __virtualname__ - return (False, 'The groupadd execution module cannot be loaded: ' - ' only available on Linux, OpenBSD and NetBSD') + return ( + False, + "The groupadd execution module cannot be loaded: " + " only available on Linux, OpenBSD and NetBSD", + ) def add(name, gid=None, system=False, root=None): - ''' + """ Add the specified group name @@ -61,25 +66,25 @@ def add(name, gid=None, system=False, root=None): .. code-block:: bash salt '*' group.add foo 3456 - ''' - cmd = ['groupadd'] + """ + cmd = ["groupadd"] if gid: - cmd.append('-g {0}'.format(gid)) - if system and __grains__['kernel'] != 'OpenBSD': - cmd.append('-r') + cmd.append("-g {0}".format(gid)) + if system and __grains__["kernel"] != "OpenBSD": + cmd.append("-r") if root is not None: - cmd.extend(('-R', root)) + cmd.extend(("-R", root)) cmd.append(name) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - return not ret['retcode'] + return not ret["retcode"] def delete(name, root=None): - ''' + """ Remove the named group name @@ -93,21 +98,21 @@ def delete(name, root=None): .. code-block:: bash salt '*' group.delete foo - ''' - cmd = ['groupdel'] + """ + cmd = ["groupdel"] if root is not None: - cmd.extend(('-R', root)) + cmd.extend(("-R", root)) cmd.append(name) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - return not ret['retcode'] + return not ret["retcode"] def info(name, root=None): - ''' + """ Return information about a group name @@ -121,7 +126,7 @@ def info(name, root=None): .. code-block:: bash salt '*' group.info foo - ''' + """ if root is not None: getgrnam = functools.partial(_getgrnam, root=root) else: @@ -136,17 +141,19 @@ def info(name, root=None): def _format_info(data): - ''' + """ Return formatted information in a pretty way. - ''' - return {'name': data.gr_name, - 'passwd': data.gr_passwd, - 'gid': data.gr_gid, - 'members': data.gr_mem} + """ + return { + "name": data.gr_name, + "passwd": data.gr_passwd, + "gid": data.gr_gid, + "members": data.gr_mem, + } def getent(refresh=False, root=None): - ''' + """ Return info on all groups refresh @@ -160,9 +167,9 @@ def getent(refresh=False, root=None): .. code-block:: bash salt '*' group.getent - ''' - if 'group.getent' in __context__ and not refresh: - return __context__['group.getent'] + """ + if "group.getent" in __context__ and not refresh: + return __context__["group.getent"] ret = [] if root is not None: @@ -172,14 +179,14 @@ def getent(refresh=False, root=None): for grinfo in getgrall(): ret.append(_format_info(grinfo)) - __context__['group.getent'] = ret + __context__["group.getent"] = ret return ret def _chattrib(name, key, value, param, root=None): - ''' + """ Change an attribute for a named user - ''' + """ pre_info = info(name, root=root) if not pre_info: return False @@ -187,19 +194,19 @@ def _chattrib(name, key, value, param, root=None): if value == pre_info[key]: return True - cmd = ['groupmod'] + cmd = ["groupmod"] if root is not None: - cmd.extend(('-R', root)) + cmd.extend(("-R", root)) cmd.extend((param, value, name)) - __salt__['cmd.run'](cmd, python_shell=False) + __salt__["cmd.run"](cmd, python_shell=False) return info(name, root=root).get(key) == value def chgid(name, gid, root=None): - ''' + """ Change the gid for a named group name @@ -216,12 +223,12 @@ def chgid(name, gid, root=None): .. code-block:: bash salt '*' group.chgid foo 4376 - ''' - return _chattrib(name, 'gid', gid, '-g', root=root) + """ + return _chattrib(name, "gid", gid, "-g", root=root) def adduser(name, username, root=None): - ''' + """ Add a user in the group. name @@ -241,31 +248,37 @@ def adduser(name, username, root=None): Verifies if a valid username 'bar' as a member of an existing group 'foo', if not then adds it. - ''' - on_redhat_5 = __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '5' - on_suse_11 = __grains__.get('os_family') == 'Suse' and __grains__.get('osmajorrelease') == '11' + """ + on_redhat_5 = ( + __grains__.get("os_family") == "RedHat" + and __grains__.get("osmajorrelease") == "5" + ) + on_suse_11 = ( + __grains__.get("os_family") == "Suse" + and __grains__.get("osmajorrelease") == "11" + ) - if __grains__['kernel'] == 'Linux': + if __grains__["kernel"] == "Linux": if on_redhat_5: - cmd = ['gpasswd', '-a', username, name] + cmd = ["gpasswd", "-a", username, name] elif on_suse_11: - cmd = ['usermod', '-A', name, username] + cmd = ["usermod", "-A", name, username] else: - cmd = ['gpasswd', '--add', username, name] + cmd = ["gpasswd", "--add", username, name] if root is not None: - cmd.extend(('--root', root)) + cmd.extend(("--root", root)) else: - cmd = ['usermod', '-G', name, username] + cmd = ["usermod", "-G", name, username] if root is not None: - cmd.extend(('-R', root)) + cmd.extend(("-R", root)) - retcode = __salt__['cmd.retcode'](cmd, python_shell=False) + retcode = __salt__["cmd.retcode"](cmd, python_shell=False) return not retcode def deluser(name, username, root=None): - ''' + """ Remove a user from the group. name @@ -285,32 +298,41 @@ def deluser(name, username, root=None): Removes a member user 'bar' from a group 'foo'. If group is not present then returns True. - ''' - on_redhat_5 = __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '5' - on_suse_11 = __grains__.get('os_family') == 'Suse' and __grains__.get('osmajorrelease') == '11' + """ + on_redhat_5 = ( + __grains__.get("os_family") == "RedHat" + and __grains__.get("osmajorrelease") == "5" + ) + on_suse_11 = ( + __grains__.get("os_family") == "Suse" + and __grains__.get("osmajorrelease") == "11" + ) - grp_info = __salt__['group.info'](name) + grp_info = __salt__["group.info"](name) try: - if username in grp_info['members']: - if __grains__['kernel'] == 'Linux': + if username in grp_info["members"]: + if __grains__["kernel"] == "Linux": if on_redhat_5: - cmd = ['gpasswd', '-d', username, name] + cmd = ["gpasswd", "-d", username, name] elif on_suse_11: - cmd = ['usermod', '-R', name, username] + cmd = ["usermod", "-R", name, username] else: - cmd = ['gpasswd', '--del', username, name] + cmd = ["gpasswd", "--del", username, name] if root is not None: - cmd.extend(('--root', root)) - retcode = __salt__['cmd.retcode'](cmd, python_shell=False) - elif __grains__['kernel'] == 'OpenBSD': - out = __salt__['cmd.run_stdout']('id -Gn {0}'.format(username), - python_shell=False) - cmd = ['usermod', '-S'] - cmd.append(','.join([g for g in out.split() if g != six.text_type(name)])) - cmd.append('{0}'.format(username)) - retcode = __salt__['cmd.retcode'](cmd, python_shell=False) + cmd.extend(("--root", root)) + retcode = __salt__["cmd.retcode"](cmd, python_shell=False) + elif __grains__["kernel"] == "OpenBSD": + out = __salt__["cmd.run_stdout"]( + "id -Gn {0}".format(username), python_shell=False + ) + cmd = ["usermod", "-S"] + cmd.append( + ",".join([g for g in out.split() if g != six.text_type(name)]) + ) + cmd.append("{0}".format(username)) + retcode = __salt__["cmd.retcode"](cmd, python_shell=False) else: - log.error('group.deluser is not yet supported on this platform') + log.error("group.deluser is not yet supported on this platform") return False return not retcode else: @@ -320,7 +342,7 @@ def deluser(name, username, root=None): def members(name, members_list, root=None): - ''' + """ Replaces members of the group with a provided list. name @@ -338,82 +360,90 @@ def members(name, members_list, root=None): Replaces a membership list for a local group 'foo'. foo:x:1234:user1,user2,user3,... - ''' - on_redhat_5 = __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '5' - on_suse_11 = __grains__.get('os_family') == 'Suse' and __grains__.get('osmajorrelease') == '11' + """ + on_redhat_5 = ( + __grains__.get("os_family") == "RedHat" + and __grains__.get("osmajorrelease") == "5" + ) + on_suse_11 = ( + __grains__.get("os_family") == "Suse" + and __grains__.get("osmajorrelease") == "11" + ) - if __grains__['kernel'] == 'Linux': + if __grains__["kernel"] == "Linux": if on_redhat_5: - cmd = ['gpasswd', '-M', members_list, name] + cmd = ["gpasswd", "-M", members_list, name] elif on_suse_11: - for old_member in __salt__['group.info'](name).get('members'): - __salt__['cmd.run']('groupmod -R {0} {1}'.format(old_member, name), python_shell=False) - cmd = ['groupmod', '-A', members_list, name] + for old_member in __salt__["group.info"](name).get("members"): + __salt__["cmd.run"]( + "groupmod -R {0} {1}".format(old_member, name), python_shell=False + ) + cmd = ["groupmod", "-A", members_list, name] else: - cmd = ['gpasswd', '--members', members_list, name] + cmd = ["gpasswd", "--members", members_list, name] if root is not None: - cmd.extend(('--root', root)) - retcode = __salt__['cmd.retcode'](cmd, python_shell=False) - elif __grains__['kernel'] == 'OpenBSD': + cmd.extend(("--root", root)) + retcode = __salt__["cmd.retcode"](cmd, python_shell=False) + elif __grains__["kernel"] == "OpenBSD": retcode = 1 - grp_info = __salt__['group.info'](name) - if grp_info and name in grp_info['name']: - __salt__['cmd.run']('groupdel {0}'.format(name), - python_shell=False) - __salt__['cmd.run']('groupadd -g {0} {1}'.format( - grp_info['gid'], name), python_shell=False) + grp_info = __salt__["group.info"](name) + if grp_info and name in grp_info["name"]: + __salt__["cmd.run"]("groupdel {0}".format(name), python_shell=False) + __salt__["cmd.run"]( + "groupadd -g {0} {1}".format(grp_info["gid"], name), python_shell=False + ) for user in members_list.split(","): if user: - retcode = __salt__['cmd.retcode']( - ['usermod', '-G', name, user], - python_shell=False) + retcode = __salt__["cmd.retcode"]( + ["usermod", "-G", name, user], python_shell=False + ) if not retcode == 0: break # provided list is '': users previously deleted from group else: retcode = 0 else: - log.error('group.members is not yet supported on this platform') + log.error("group.members is not yet supported on this platform") return False return not retcode def _getgrnam(name, root=None): - ''' + """ Alternative implementation for getgrnam, that use only /etc/group - ''' - root = root or '/' - passwd = os.path.join(root, 'etc/group') + """ + root = root or "/" + passwd = os.path.join(root, "etc/group") with salt.utils.files.fopen(passwd) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - comps = line.strip().split(':') + comps = line.strip().split(":") if len(comps) < 4: - log.debug('Ignoring group line: %s', line) + log.debug("Ignoring group line: %s", line) continue if comps[0] == name: # Generate a getpwnam compatible output comps[2] = int(comps[2]) - comps[3] = comps[3].split(',') if comps[3] else [] + comps[3] = comps[3].split(",") if comps[3] else [] return grp.struct_group(comps) - raise KeyError('getgrnam(): name not found: {}'.format(name)) + raise KeyError("getgrnam(): name not found: {}".format(name)) def _getgrall(root=None): - ''' + """ Alternative implemetantion for getgrall, that use only /etc/group - ''' - root = root or '/' - passwd = os.path.join(root, 'etc/group') + """ + root = root or "/" + passwd = os.path.join(root, "etc/group") with salt.utils.files.fopen(passwd) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - comps = line.strip().split(':') + comps = line.strip().split(":") if len(comps) < 4: - log.debug('Ignoring group line: %s', line) + log.debug("Ignoring group line: %s", line) continue # Generate a getgrall compatible output comps[2] = int(comps[2]) - comps[3] = comps[3].split(',') if comps[3] else [] + comps[3] = comps[3].split(",") if comps[3] else [] yield grp.struct_group(comps) diff --git a/salt/modules/grub_legacy.py b/salt/modules/grub_legacy.py index 0d840537efc..9271fda2c1a 100644 --- a/salt/modules/grub_legacy.py +++ b/salt/modules/grub_legacy.py @@ -1,44 +1,48 @@ # -*- coding: utf-8 -*- -''' +""" Support for GRUB Legacy -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os +import salt.utils.decorators as decorators + # Import salt libs import salt.utils.files -import salt.utils.decorators as decorators from salt.exceptions import CommandExecutionError # Define the module's virtual name -__virtualname__ = 'grub' +__virtualname__ = "grub" def __virtual__(): - ''' + """ Only load the module if grub is installed - ''' + """ if os.path.exists(_detect_conf()): return __virtualname__ - return (False, 'The grub_legacy execution module cannot be loaded: ' - 'the grub config file does not exist in /boot/grub/') + return ( + False, + "The grub_legacy execution module cannot be loaded: " + "the grub config file does not exist in /boot/grub/", + ) @decorators.memoize def _detect_conf(): - ''' + """ GRUB conf location differs depending on distro - ''' - if __grains__['os_family'] == 'RedHat': - return '/boot/grub/grub.conf' + """ + if __grains__["os_family"] == "RedHat": + return "/boot/grub/grub.conf" # Defaults for Ubuntu, Debian, Arch, and others - return '/boot/grub/menu.lst' + return "/boot/grub/menu.lst" def version(): - ''' + """ Return server version from grub --version CLI Example: @@ -46,14 +50,14 @@ def version(): .. code-block:: bash salt '*' grub.version - ''' - cmd = '/sbin/grub --version' - out = __salt__['cmd.run'](cmd) + """ + cmd = "/sbin/grub --version" + out = __salt__["cmd.run"](cmd) return out def conf(): - ''' + """ Parse GRUB conf file CLI Example: @@ -61,32 +65,32 @@ def conf(): .. code-block:: bash salt '*' grub.conf - ''' - stanza = '' + """ + stanza = "" stanzas = [] in_stanza = False ret = {} pos = 0 try: - with salt.utils.files.fopen(_detect_conf(), 'r') as _fp: + with salt.utils.files.fopen(_detect_conf(), "r") as _fp: for line in _fp: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('#'): + if line.startswith("#"): continue - if line.startswith('\n'): + if line.startswith("\n"): in_stanza = False - if 'title' in stanza: - stanza += 'order {0}'.format(pos) + if "title" in stanza: + stanza += "order {0}".format(pos) pos += 1 stanzas.append(stanza) - stanza = '' + stanza = "" continue - if line.strip().startswith('title'): + if line.strip().startswith("title"): if in_stanza: - stanza += 'order {0}'.format(pos) + stanza += "order {0}".format(pos) pos += 1 stanzas.append(stanza) - stanza = '' + stanza = "" else: in_stanza = True if in_stanza: @@ -95,32 +99,32 @@ def conf(): key, value = _parse_line(line) ret[key] = value if in_stanza: - if not line.endswith('\n'): - line += '\n' + if not line.endswith("\n"): + line += "\n" stanza += line - stanza += 'order {0}'.format(pos) + stanza += "order {0}".format(pos) pos += 1 stanzas.append(stanza) except (IOError, OSError) as exc: msg = "Could not read grub config: {0}" raise CommandExecutionError(msg.format(exc)) - ret['stanzas'] = [] + ret["stanzas"] = [] for stanza in stanzas: mydict = {} for line in stanza.strip().splitlines(): key, value = _parse_line(line) mydict[key] = value - ret['stanzas'].append(mydict) + ret["stanzas"].append(mydict) return ret -def _parse_line(line=''): - ''' +def _parse_line(line=""): + """ Used by conf() to break config lines into name/value pairs - ''' + """ parts = line.split() key = parts.pop(0) - value = ' '.join(parts) + value = " ".join(parts) return key, value diff --git a/salt/modules/guestfs.py b/salt/modules/guestfs.py index 6bb3f752465..2134b78a47b 100644 --- a/salt/modules/guestfs.py +++ b/salt/modules/guestfs.py @@ -1,16 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Interact with virtual machine images via libguestfs :depends: - libguestfs -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import hashlib +import logging # Import Python libs import os import tempfile -import hashlib -import logging +import time # Import Salt libs import salt.utils.path @@ -19,16 +21,19 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if libguestfs python bindings are installed - ''' - if salt.utils.path.which('guestmount'): - return 'guestfs' - return (False, 'The guestfs execution module cannot be loaded: guestmount binary not in path.') + """ + if salt.utils.path.which("guestmount"): + return "guestfs" + return ( + False, + "The guestfs execution module cannot be loaded: guestmount binary not in path.", + ) -def mount(location, access='rw', root=None): - ''' +def mount(location, access="rw", root=None): + """ Mount an image CLI Example: @@ -36,14 +41,12 @@ def mount(location, access='rw', root=None): .. code-block:: bash salt '*' guest.mount /srv/images/fedora.qcow - ''' + """ if root is None: root = os.path.join( - tempfile.gettempdir(), - 'guest', - location.lstrip(os.sep).replace('/', '.') + tempfile.gettempdir(), "guest", location.lstrip(os.sep).replace("/", ".") ) - log.debug('Using root %s', root) + log.debug("Using root %s", root) if not os.path.isdir(root): try: os.makedirs(root) @@ -53,21 +56,45 @@ def mount(location, access='rw', root=None): while True: if os.listdir(root): # Stuff is in there, don't use it - hash_type = getattr(hashlib, __opts__.get('hash_type', 'md5')) + hash_type = getattr(hashlib, __opts__.get("hash_type", "md5")) rand = hash_type(os.urandom(32)).hexdigest() root = os.path.join( tempfile.gettempdir(), - 'guest', - location.lstrip(os.sep).replace('/', '.') + rand + "guest", + location.lstrip(os.sep).replace("/", ".") + rand, ) - log.debug('Establishing new root as %s', root) + log.debug("Establishing new root as %s", root) if not os.path.isdir(root): try: os.makedirs(root) except OSError: - log.info('Path already existing: %s', root) + log.info("Path already existing: %s", root) else: break - cmd = 'guestmount -i -a {0} --{1} {2}'.format(location, access, root) - __salt__['cmd.run'](cmd, python_shell=False) + cmd = "guestmount -i -a {0} --{1} {2}".format(location, access, root) + __salt__["cmd.run"](cmd, python_shell=False) return root + + +def umount(name, disk=None): + """ + Unmount an image + + CLI Example: + + .. code-block:: bash + + salt '*' guestfs.umount /mountpoint disk=/srv/images/fedora.qcow + """ + cmd = "guestunmount -q {0}".format(name) + __salt__["cmd.run"](cmd) + + # Wait at most 5s that the disk is no longuer used + loops = 0 + while ( + disk is not None + and loops < 5 + and len(__salt__["cmd.run"]("lsof {0}".format(disk)).splitlines()) != 0 + ): + loops = loops + 1 + time.sleep(1) diff --git a/salt/modules/hadoop.py b/salt/modules/hadoop.py index 82f9963eb80..5f16226442a 100644 --- a/salt/modules/hadoop.py +++ b/salt/modules/hadoop.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for hadoop :maintainer: Yann Jouanin @@ -8,27 +8,30 @@ Support for hadoop :platform: linux -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.utils.path -__authorized_modules__ = ['version', 'namenode', 'dfsadmin', 'dfs', 'fs'] +__authorized_modules__ = ["version", "namenode", "dfsadmin", "dfs", "fs"] def __virtual__(): - ''' + """ Check if hadoop is present, then load the module - ''' - if salt.utils.path.which('hadoop') or salt.utils.path.which('hdfs'): - return 'hadoop' - return (False, 'The hadoop execution module cannot be loaded: hadoop or hdfs binary not in path.') + """ + if salt.utils.path.which("hadoop") or salt.utils.path.which("hdfs"): + return "hadoop" + return ( + False, + "The hadoop execution module cannot be loaded: hadoop or hdfs binary not in path.", + ) def _hadoop_cmd(module, command, *args): - ''' + """ Hadoop/hdfs command wrapper As Hadoop command has been deprecated this module will default @@ -39,26 +42,31 @@ def _hadoop_cmd(module, command, *args): Follows hadoop command template: hadoop module -command args E.g.: hadoop dfs -ls / - ''' - tool = 'hadoop' - if salt.utils.path.which('hdfs'): - tool = 'hdfs' + """ + tool = "hadoop" + if salt.utils.path.which("hdfs"): + tool = "hdfs" out = None if module and command: if module in __authorized_modules__: - mappings = {'tool': tool, 'module': module, 'command': command, 'args': ' '.join(args)} - cmd = '{tool} {module} -{command} {args}'.format(**mappings) - out = __salt__['cmd.run'](cmd, python_shell=False) + mappings = { + "tool": tool, + "module": module, + "command": command, + "args": " ".join(args), + } + cmd = "{tool} {module} -{command} {args}".format(**mappings) + out = __salt__["cmd.run"](cmd, python_shell=False) else: - return 'Error: Unknown module' + return "Error: Unknown module" else: - return 'Error: Module and command not defined' + return "Error: Module and command not defined" return out def version(): - ''' + """ Return version from hadoop version CLI Example: @@ -66,14 +74,14 @@ def version(): .. code-block:: bash salt '*' hadoop.version - ''' - module = 'version' + """ + module = "version" out = _hadoop_cmd(module, True).split() return out[1] def dfs(command=None, *args): - ''' + """ Execute a command on DFS CLI Example: @@ -81,15 +89,15 @@ def dfs(command=None, *args): .. code-block:: bash salt '*' hadoop.dfs ls / - ''' + """ if command: - return _hadoop_cmd('dfs', command, *args) + return _hadoop_cmd("dfs", command, *args) else: - return 'Error: command must be provided' + return "Error: command must be provided" def dfsadmin_report(arg=None): - ''' + """ .. versionadded:: 2019.2.0 Reports basic filesystem information and statistics. Optional flags may be used to filter the list of displayed DataNodes. @@ -102,18 +110,18 @@ def dfsadmin_report(arg=None): .. code-block:: bash salt '*' hadoop.dfsadmin -report - ''' + """ if arg is not None: - if arg in ['live', 'dead', 'decommissioning']: - return _hadoop_cmd('dfsadmin', 'report', arg) + if arg in ["live", "dead", "decommissioning"]: + return _hadoop_cmd("dfsadmin", "report", arg) else: return "Error: the arg is wrong, it must be in ['live', 'dead', 'decommissioning']" else: - return _hadoop_cmd('dfsadmin', 'report') + return _hadoop_cmd("dfsadmin", "report") def dfs_present(path): - ''' + """ Check if a file or directory is present on the distributed FS. CLI Example: @@ -123,14 +131,14 @@ def dfs_present(path): salt '*' hadoop.dfs_present /some_random_file Returns True if the file is present - ''' - cmd_return = _hadoop_cmd('dfs', 'stat', path) - match = 'No such file or directory' + """ + cmd_return = _hadoop_cmd("dfs", "stat", path) + match = "No such file or directory" return False if match in cmd_return else True def dfs_absent(path): - ''' + """ Check if a file or directory is absent on the distributed FS. CLI Example: @@ -140,22 +148,22 @@ def dfs_absent(path): salt '*' hadoop.dfs_absent /some_random_file Returns True if the file is absent - ''' - cmd_return = _hadoop_cmd('dfs', 'stat', path) - match = 'No such file or directory' + """ + cmd_return = _hadoop_cmd("dfs", "stat", path) + match = "No such file or directory" return True if match in cmd_return else False def namenode_format(force=None): - ''' + """ Format a name node .. code-block:: bash salt '*' hadoop.namenode_format force=True - ''' - force_param = '' + """ + force_param = "" if force: - force_param = '-force' + force_param = "-force" - return _hadoop_cmd('namenode', 'format', '-nonInteractive', force_param) + return _hadoop_cmd("namenode", "format", "-nonInteractive", force_param) diff --git a/salt/modules/haproxyconn.py b/salt/modules/haproxyconn.py index d21ec8bccec..2544d9498a6 100644 --- a/salt/modules/haproxyconn.py +++ b/salt/modules/haproxyconn.py @@ -1,30 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" Support for haproxy .. versionadded:: 2014.7.0 -''' +""" # Import Python libs from __future__ import absolute_import, generators, print_function, unicode_literals -import os + import logging +import os import stat import time try: import haproxy.cmds # pylint: disable=no-name-in-module import haproxy.conn # pylint: disable=no-name-in-module + HAS_HAPROXY = True except ImportError: HAS_HAPROXY = False log = logging.getLogger(__name__) -__virtualname__ = 'haproxy' +__virtualname__ = "haproxy" # Default socket location -DEFAULT_SOCKET_URL = '/var/run/haproxy.sock' +DEFAULT_SOCKET_URL = "/var/run/haproxy.sock" # Numeric fields returned by stats FIELD_NUMERIC = ["weight", "bin", "bout"] @@ -33,27 +35,30 @@ FIELD_NODE_NAME = "name" def __virtual__(): - ''' + """ Only load the module if haproxyctl is installed - ''' + """ if HAS_HAPROXY: return __virtualname__ - return (False, 'The haproxyconn execution module cannot be loaded: haproxyctl module not available') + return ( + False, + "The haproxyconn execution module cannot be loaded: haproxyctl module not available", + ) def _get_conn(socket=DEFAULT_SOCKET_URL): - ''' + """ Get connection to haproxy socket. - ''' - assert os.path.exists(socket), '{0} does not exist.'.format(socket) + """ + assert os.path.exists(socket), "{0} does not exist.".format(socket) issock = os.stat(socket).st_mode - assert stat.S_ISSOCK(issock), '{0} is not a socket.'.format(socket) + assert stat.S_ISSOCK(issock), "{0} is not a socket.".format(socket) ha_conn = haproxy.conn.HaPConn(socket) return ha_conn def list_servers(backend, socket=DEFAULT_SOCKET_URL, objectify=False): - ''' + """ List servers in haproxy backend. backend @@ -67,14 +72,14 @@ def list_servers(backend, socket=DEFAULT_SOCKET_URL, objectify=False): .. code-block:: bash salt '*' haproxy.list_servers mysql - ''' + """ ha_conn = _get_conn(socket) ha_cmd = haproxy.cmds.listServers(backend=backend) return ha_conn.sendCmd(ha_cmd, objectify=objectify) -def wait_state(backend, server, value='up', timeout=60*5, socket=DEFAULT_SOCKET_URL): - ''' +def wait_state(backend, server, value="up", timeout=60 * 5, socket=DEFAULT_SOCKET_URL): + """ Wait for a specific server state @@ -98,16 +103,19 @@ def wait_state(backend, server, value='up', timeout=60*5, socket=DEFAULT_SOCKET_ .. code-block:: bash salt '*' haproxy.wait_state mysql server01 up 60 - ''' + """ t = time.time() + timeout while time.time() < t: - if get_backend(backend=backend, socket=socket)[server]["status"].lower() == value.lower(): + if ( + get_backend(backend=backend, socket=socket)[server]["status"].lower() + == value.lower() + ): return True return False def get_backend(backend, socket=DEFAULT_SOCKET_URL): - ''' + """ Receive information about a specific backend. @@ -122,9 +130,11 @@ def get_backend(backend, socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.get_backend mysql - ''' + """ - backend_data = list_servers(backend=backend, socket=socket).replace('\n', ' ').split(' ') + backend_data = ( + list_servers(backend=backend, socket=socket).replace("\n", " ").split(" ") + ) result = {} # Convert given string to Integer @@ -137,7 +147,7 @@ def get_backend(backend, socket=DEFAULT_SOCKET_URL): for data in backend_data: # Check if field or server name if ":" in data: - active_field = data.replace(':', '').lower() + active_field = data.replace(":", "").lower() continue elif active_field.lower() == FIELD_NODE_NAME: active_server = data @@ -156,7 +166,7 @@ def get_backend(backend, socket=DEFAULT_SOCKET_URL): def enable_server(name, backend, socket=DEFAULT_SOCKET_URL): - ''' + """ Enable Server in haproxy name @@ -173,10 +183,10 @@ def enable_server(name, backend, socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.enable_server web1.example.com www - ''' + """ - if backend == '*': - backends = show_backends(socket=socket).split('\n') + if backend == "*": + backends = show_backends(socket=socket).split("\n") else: backends = [backend] @@ -191,7 +201,7 @@ def enable_server(name, backend, socket=DEFAULT_SOCKET_URL): def disable_server(name, backend, socket=DEFAULT_SOCKET_URL): - ''' + """ Disable server in haproxy. name @@ -208,10 +218,10 @@ def disable_server(name, backend, socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.disable_server db1.example.com mysql - ''' + """ - if backend == '*': - backends = show_backends(socket=socket).split('\n') + if backend == "*": + backends = show_backends(socket=socket).split("\n") else: backends = [backend] @@ -226,7 +236,7 @@ def disable_server(name, backend, socket=DEFAULT_SOCKET_URL): def get_weight(name, backend, socket=DEFAULT_SOCKET_URL): - ''' + """ Get server weight name @@ -243,14 +253,14 @@ def get_weight(name, backend, socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.get_weight web1.example.com www - ''' + """ ha_conn = _get_conn(socket) ha_cmd = haproxy.cmds.getWeight(server=name, backend=backend) return ha_conn.sendCmd(ha_cmd) def set_weight(name, backend, weight=0, socket=DEFAULT_SOCKET_URL): - ''' + """ Set server weight name @@ -270,7 +280,7 @@ def set_weight(name, backend, weight=0, socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.set_weight web1.example.com www 13 - ''' + """ ha_conn = _get_conn(socket) ha_cmd = haproxy.cmds.getWeight(server=name, backend=backend, weight=weight) ha_conn.sendCmd(ha_cmd) @@ -278,7 +288,7 @@ def set_weight(name, backend, weight=0, socket=DEFAULT_SOCKET_URL): def set_state(name, backend, state, socket=DEFAULT_SOCKET_URL): - ''' + """ Force a server's administrative state to a new state. This can be useful to disable load balancing and/or any traffic to a server. Setting the state to "ready" puts the server in normal mode, and the command is the equivalent of @@ -306,13 +316,14 @@ def set_state(name, backend, state, socket=DEFAULT_SOCKET_URL): salt '*' haproxy.set_state my_proxy_server my_backend ready - ''' + """ # Pulling this in from the latest 0.5 release which is not yet in PyPi. # https://github.com/neurogeek/haproxyctl class setServerState(haproxy.cmds.Cmd): """Set server state command.""" + cmdTxt = "set server %(backend)s/%(server)s state %(value)s\r\n" - p_args = ['backend', 'server', 'value'] + p_args = ["backend", "server", "value"] helpTxt = "Force a server's administrative state to a new state." ha_conn = _get_conn(socket) @@ -321,7 +332,7 @@ def set_state(name, backend, state, socket=DEFAULT_SOCKET_URL): def show_frontends(socket=DEFAULT_SOCKET_URL): - ''' + """ Show HaProxy frontends socket @@ -332,14 +343,14 @@ def show_frontends(socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.show_frontends - ''' + """ ha_conn = _get_conn(socket) ha_cmd = haproxy.cmds.showFrontends() return ha_conn.sendCmd(ha_cmd) def list_frontends(socket=DEFAULT_SOCKET_URL): - ''' + """ List HaProxy frontends @@ -351,12 +362,12 @@ def list_frontends(socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.list_frontends - ''' - return show_frontends(socket=socket).split('\n') + """ + return show_frontends(socket=socket).split("\n") def show_backends(socket=DEFAULT_SOCKET_URL): - ''' + """ Show HaProxy Backends socket @@ -367,14 +378,14 @@ def show_backends(socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.show_backends - ''' + """ ha_conn = _get_conn(socket) ha_cmd = haproxy.cmds.showBackends() return ha_conn.sendCmd(ha_cmd) def list_backends(servers=True, socket=DEFAULT_SOCKET_URL): - ''' + """ List HaProxy Backends @@ -389,9 +400,9 @@ def list_backends(servers=True, socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.list_backends - ''' + """ if not servers: - return show_backends(socket=socket).split('\n') + return show_backends(socket=socket).split("\n") else: result = {} for backend in list_backends(servers=False, socket=socket): @@ -400,7 +411,7 @@ def list_backends(servers=True, socket=DEFAULT_SOCKET_URL): def get_sessions(name, backend, socket=DEFAULT_SOCKET_URL): - ''' + """ .. versionadded:: 2016.11.0 Get number of current sessions on server in backend (scur) @@ -419,7 +430,8 @@ def get_sessions(name, backend, socket=DEFAULT_SOCKET_URL): .. code-block:: bash salt '*' haproxy.get_sessions web1.example.com www - ''' + """ + class getStats(haproxy.cmds.Cmd): p_args = ["backend", "server"] cmdTxt = "show stat\r\n" @@ -428,8 +440,8 @@ def get_sessions(name, backend, socket=DEFAULT_SOCKET_URL): ha_conn = _get_conn(socket) ha_cmd = getStats(server=name, backend=backend) result = ha_conn.sendCmd(ha_cmd) - for line in result.split('\n'): + for line in result.split("\n"): if line.startswith(backend): - outCols = line.split(',') + outCols = line.split(",") if outCols[1] == name: return outCols[4] diff --git a/salt/modules/hashutil.py b/salt/modules/hashutil.py index 2d7ccfcc18f..4c7c9243d23 100644 --- a/salt/modules/hashutil.py +++ b/salt/modules/hashutil.py @@ -1,30 +1,32 @@ # encoding: utf-8 -''' +""" A collection of hashing and encoding functions -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import hashlib import hmac # Import Salt libs import salt.exceptions -from salt.ext import six import salt.utils.files import salt.utils.hashutils import salt.utils.stringutils +from salt.ext import six if six.PY2: from StringIO import StringIO + BytesIO = StringIO elif six.PY3: from io import BytesIO, StringIO -def digest(instr, checksum='md5'): - ''' +def digest(instr, checksum="md5"): + """ Return a checksum digest for a string instr @@ -38,23 +40,24 @@ def digest(instr, checksum='md5'): .. code-block:: bash salt '*' hashutil.digest 'get salted' - ''' + """ hashing_funcs = { - 'md5': __salt__['hashutil.md5_digest'], - 'sha256': __salt__['hashutil.sha256_digest'], - 'sha512': __salt__['hashutil.sha512_digest'], + "md5": __salt__["hashutil.md5_digest"], + "sha256": __salt__["hashutil.sha256_digest"], + "sha512": __salt__["hashutil.sha512_digest"], } hash_func = hashing_funcs.get(checksum) if hash_func is None: raise salt.exceptions.CommandExecutionError( - "Hash func '{0}' is not supported.".format(checksum)) + "Hash func '{0}' is not supported.".format(checksum) + ) return hash_func(instr) -def digest_file(infile, checksum='md5'): - ''' +def digest_file(infile, checksum="md5"): + """ Return a checksum digest for a file infile @@ -69,19 +72,20 @@ def digest_file(infile, checksum='md5'): .. code-block:: bash salt '*' hashutil.digest_file /path/to/file - ''' - if not __salt__['file.file_exists'](infile): + """ + if not __salt__["file.file_exists"](infile): raise salt.exceptions.CommandExecutionError( - "File path '{0}' not found.".format(infile)) + "File path '{0}' not found.".format(infile) + ) - with salt.utils.files.fopen(infile, 'rb') as f: - file_hash = __salt__['hashutil.digest'](f.read(), checksum) + with salt.utils.files.fopen(infile, "rb") as f: + file_hash = __salt__["hashutil.digest"](f.read(), checksum) return file_hash def base64_b64encode(instr): - ''' + """ Encode a string as base64 using the "modern" Python interface. Among other possible differences, the "modern" encoder does not include @@ -94,12 +98,12 @@ def base64_b64encode(instr): .. code-block:: bash salt '*' hashutil.base64_b64encode 'get salted' - ''' + """ return salt.utils.hashutils.base64_b64encode(instr) def base64_b64decode(instr): - ''' + """ Decode a base64-encoded string using the "modern" Python interface .. versionadded:: 2016.3.0 @@ -109,12 +113,12 @@ def base64_b64decode(instr): .. code-block:: bash salt '*' hashutil.base64_b64decode 'Z2V0IHNhbHRlZA==' - ''' + """ return salt.utils.hashutils.base64_b64decode(instr) def base64_encodestring(instr): - ''' + """ Encode a byte-like object as base64 using the "modern" Python interface. Among other possible differences, the "modern" encoder includes @@ -128,12 +132,12 @@ def base64_encodestring(instr): .. code-block:: bash salt '*' hashutil.base64_encodestring 'get salted' - ''' + """ return salt.utils.hashutils.base64_encodestring(instr) def base64_encodefile(fname): - ''' + """ Read a file from the file system and return as a base64 encoded string .. versionadded:: 2016.3.0 @@ -155,10 +159,10 @@ def base64_encodefile(fname): .. code-block:: bash salt '*' hashutil.base64_encodefile /path/to/binary_file - ''' + """ encoded_f = BytesIO() - with salt.utils.files.fopen(fname, 'rb') as f: + with salt.utils.files.fopen(fname, "rb") as f: base64.encode(f, encoded_f) encoded_f.seek(0) @@ -166,7 +170,7 @@ def base64_encodefile(fname): def base64_decodestring(instr): - ''' + """ Decode a base64-encoded byte-like object using the "modern" Python interface .. versionadded:: 3000 @@ -177,12 +181,12 @@ def base64_decodestring(instr): salt '*' hashutil.base64_decodestring instr='Z2V0IHNhbHRlZAo=' - ''' + """ return salt.utils.hashutils.base64_decodestring(instr) def base64_decodefile(instr, outfile): - r''' + r""" Decode a base64-encoded string and write the result to a file .. versionadded:: 2016.3.0 @@ -192,17 +196,17 @@ def base64_decodefile(instr, outfile): .. code-block:: bash salt '*' hashutil.base64_decodefile instr='Z2V0IHNhbHRlZAo=' outfile='/path/to/binary_file' - ''' + """ encoded_f = StringIO(instr) - with salt.utils.files.fopen(outfile, 'wb') as f: + with salt.utils.files.fopen(outfile, "wb") as f: base64.decode(encoded_f, f) return True def md5_digest(instr): - ''' + """ Generate an md5 hash of a given string .. versionadded:: 2014.7.0 @@ -212,12 +216,12 @@ def md5_digest(instr): .. code-block:: bash salt '*' hashutil.md5_digest 'get salted' - ''' + """ return salt.utils.hashutils.md5_digest(instr) def sha256_digest(instr): - ''' + """ Generate an sha256 hash of a given string .. versionadded:: 2014.7.0 @@ -227,12 +231,12 @@ def sha256_digest(instr): .. code-block:: bash salt '*' hashutil.sha256_digest 'get salted' - ''' + """ return salt.utils.hashutils.sha256_digest(instr) def sha512_digest(instr): - ''' + """ Generate an sha512 hash of a given string .. versionadded:: 2014.7.0 @@ -242,12 +246,12 @@ def sha512_digest(instr): .. code-block:: bash salt '*' hashutil.sha512_digest 'get salted' - ''' + """ return salt.utils.hashutils.sha512_digest(instr) def hmac_signature(string, shared_secret, challenge_hmac): - ''' + """ Verify a challenging hmac signature against a string / shared-secret .. versionadded:: 2014.7.0 @@ -259,12 +263,12 @@ def hmac_signature(string, shared_secret, challenge_hmac): .. code-block:: bash salt '*' hashutil.hmac_signature 'get salted' 'shared secret' 'eBWf9bstXg+NiP5AOwppB5HMvZiYMPzEM9W5YMm/AmQ=' - ''' + """ return salt.utils.hashutils.hmac_signature(string, shared_secret, challenge_hmac) def hmac_compute(string, shared_secret): - ''' + """ .. versionadded:: 3000 Compute a HMAC SHA256 digest using a string and secret. @@ -274,12 +278,12 @@ def hmac_compute(string, shared_secret): .. code-block:: bash salt '*' hashutil.hmac_compute 'get salted' 'shared secret' - ''' + """ return salt.utils.hashutils.hmac_compute(string, shared_secret) def github_signature(string, shared_secret, challenge_hmac): - ''' + """ Verify a challenging hmac signature against a string / shared-secret for github webhooks. @@ -292,10 +296,10 @@ def github_signature(string, shared_secret, challenge_hmac): .. code-block:: bash salt '*' hashutil.github_signature '{"ref":....} ' 'shared secret' 'sha1=bc6550fc290acf5b42283fa8deaf55cea0f8c206' - ''' + """ msg = string key = shared_secret - hashtype, challenge = challenge_hmac.split('=') + hashtype, challenge = challenge_hmac.split("=") if six.text_type: msg = salt.utils.stringutils.to_bytes(msg) key = salt.utils.stringutils.to_bytes(key) diff --git a/salt/modules/heat.py b/salt/modules/heat.py index f747b860b2c..a2f47875444 100644 --- a/salt/modules/heat.py +++ b/salt/modules/heat.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for handling OpenStack Heat calls .. versionadded:: 2017.7.0 @@ -38,26 +38,29 @@ Module for handling OpenStack Heat calls For example:: salt '*' heat.flavor_list profile=openstack1 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import time -import logging -# Import Salt libs -from salt.exceptions import SaltInvocationError -from salt.ext import six +import logging +import time + import salt.utils.files import salt.utils.json import salt.utils.stringutils import salt.utils.versions import salt.utils.yaml +# Import Salt libs +from salt.exceptions import SaltInvocationError +from salt.ext import six + # pylint: disable=import-error HAS_HEAT = False try: import heatclient + HAS_HEAT = True except ImportError: pass @@ -65,110 +68,114 @@ except ImportError: HAS_OSLO = False try: from oslo_serialization import jsonutils + HAS_OSLO = True except ImportError: pass -SECTIONS = ( - PARAMETER_DEFAULTS, - PARAMETERS, - RESOURCE_REGISTRY, - EVENT_SINKS) = ( - 'parameter_defaults', - 'parameters', - 'resource_registry', - 'event_sinks') +SECTIONS = (PARAMETER_DEFAULTS, PARAMETERS, RESOURCE_REGISTRY, EVENT_SINKS) = ( + "parameter_defaults", + "parameters", + "resource_registry", + "event_sinks", +) logging.basicConfig(level=logging.DEBUG) log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if heat is installed on this minion. - ''' + """ if HAS_HEAT and HAS_OSLO: - return 'heat' - return (False, 'The heat execution module cannot be loaded: ' - 'the heatclient and oslo_serialization' - # 'the heatclient and keystoneclient and oslo_serialization' - ' python library is not available.') + return "heat" + return ( + False, + "The heat execution module cannot be loaded: " + "the heatclient and oslo_serialization" + # 'the heatclient and keystoneclient and oslo_serialization' + " python library is not available.", + ) __opts__ = {} def _auth(profile=None, api_version=1, **connection_args): - ''' + """ Set up heat credentials, returns `heatclient.client.Client`. Optional parameter "api_version" defaults to 1. Only intended to be used within heat-enabled modules - ''' + """ if profile: - prefix = profile + ':keystone.' + prefix = profile + ":keystone." else: - prefix = 'keystone.' + prefix = "keystone." def get(key, default=None): - ''' + """ Checks connection_args, then salt-minion config, falls back to specified default value. - ''' - return connection_args.get('connection_' + key, - __salt__['config.get'](prefix + key, default)) + """ + return connection_args.get( + "connection_" + key, __salt__["config.get"](prefix + key, default) + ) - user = get('user', 'admin') - password = get('password', None) - tenant = get('tenant', 'admin') - tenant_id = get('tenant_id') - auth_url = get('auth_url', 'http://127.0.0.1:35357/v2.0') - insecure = get('insecure', False) - admin_token = get('token') - region_name = get('region_name', None) + user = get("user", "admin") + password = get("password", None) + tenant = get("tenant", "admin") + tenant_id = get("tenant_id") + auth_url = get("auth_url", "http://127.0.0.1:35357/v2.0") + insecure = get("insecure", False) + admin_token = get("token") + region_name = get("region_name", None) if admin_token and api_version != 1 and not password: # If we had a password we could just # ignore the admin-token and move on... - raise SaltInvocationError('Only can use keystone admin token ' + - 'with Heat API v1') + raise SaltInvocationError( + "Only can use keystone admin token " + "with Heat API v1" + ) elif password: # Can't use the admin-token anyway - kwargs = {'username': user, - 'password': password, - 'tenant_id': tenant_id, - 'auth_url': auth_url, - 'region_name': region_name, - 'tenant_name': tenant} + kwargs = { + "username": user, + "password": password, + "tenant_id": tenant_id, + "auth_url": auth_url, + "region_name": region_name, + "tenant_name": tenant, + } # 'insecure' keyword not supported by all v2.0 keystone clients # this ensures it's only passed in when defined if insecure: - kwargs['insecure'] = True + kwargs["insecure"] = True elif api_version == 1 and admin_token: - kwargs = {'token': admin_token, - 'auth_url': auth_url} + kwargs = {"token": admin_token, "auth_url": auth_url} else: - raise SaltInvocationError('No credentials to authenticate with.') + raise SaltInvocationError("No credentials to authenticate with.") - token = __salt__['keystone.token_get'](profile) - kwargs['token'] = token['id'] + token = __salt__["keystone.token_get"](profile) + kwargs["token"] = token["id"] # This doesn't realy prevent the password to show up # in the minion log as keystoneclient.session is # logging it anyway when in debug-mode - kwargs.pop('password') + kwargs.pop("password") try: - heat_endpoint = __salt__['keystone.endpoint_get']('heat', profile)['url'] + heat_endpoint = __salt__["keystone.endpoint_get"]("heat", profile)["url"] except KeyError: - heat_endpoint = __salt__['keystone.endpoint_get']('heat', profile)['publicurl'] + heat_endpoint = __salt__["keystone.endpoint_get"]("heat", profile)["publicurl"] heat_endpoint = heat_endpoint % token log.debug( - 'Calling heatclient.client.Client(%s, %s, **%s)', + "Calling heatclient.client.Client(%s, %s, **%s)", api_version, heat_endpoint, - kwargs + kwargs, ) # may raise exc.HTTPUnauthorized, exc.HTTPNotFound # but we deal with those elsewhere @@ -176,11 +183,11 @@ def _auth(profile=None, api_version=1, **connection_args): def _parse_template(tmpl_str): - ''' + """ Parsing template - ''' + """ tmpl_str = tmpl_str.strip() - if tmpl_str.startswith('{'): + if tmpl_str.startswith("{"): tpl = salt.utils.json.loads(tmpl_str) else: try: @@ -190,17 +197,19 @@ def _parse_template(tmpl_str): else: if tpl is None: tpl = {} - if not ('HeatTemplateFormatVersion' in tpl - or 'heat_template_version' in tpl - or 'AWSTemplateFormatVersion' in tpl): - raise ValueError(('Template format version not found.')) + if not ( + "HeatTemplateFormatVersion" in tpl + or "heat_template_version" in tpl + or "AWSTemplateFormatVersion" in tpl + ): + raise ValueError(("Template format version not found.")) return tpl def _parse_environment(env_str): - ''' + """ Parsing template - ''' + """ try: env = salt.utils.yaml.safe_load(env_str) except salt.utils.yaml.YAMLError as exc: @@ -209,9 +218,7 @@ def _parse_environment(env_str): if env is None: env = {} elif not isinstance(env, dict): - raise ValueError( - 'The environment is not a valid YAML mapping data type.' - ) + raise ValueError("The environment is not a valid YAML mapping data type.") for param in env: if param not in SECTIONS: @@ -221,50 +228,53 @@ def _parse_environment(env_str): def _get_stack_events(h_client, stack_id, event_args): - ''' + """ Get event for stack - ''' - event_args['stack_id'] = stack_id - event_args['resource_name'] = None + """ + event_args["stack_id"] = stack_id + event_args["resource_name"] = None try: events = h_client.events.list(**event_args) except heatclient.exc.HTTPNotFound as exc: raise heatclient.exc.CommandError(six.text_type(exc)) else: for event in events: - event.stack_name = stack_id.split('/')[0] + event.stack_name = stack_id.split("/")[0] return events -def _poll_for_events(h_client, stack_name, action=None, poll_period=5, - timeout=60, marker=None): - ''' +def _poll_for_events( + h_client, stack_name, action=None, poll_period=5, timeout=60, marker=None +): + """ Polling stack events - ''' + """ if action: - stop_status = ('{0}_FAILED'.format(action), '{0}_COMPLETE'.format(action)) + stop_status = ("{0}_FAILED".format(action), "{0}_COMPLETE".format(action)) stop_check = lambda a: a in stop_status else: - stop_check = lambda a: a.endswith('_COMPLETE') or a.endswith('_FAILED') + stop_check = lambda a: a.endswith("_COMPLETE") or a.endswith("_FAILED") timeout_sec = timeout * 60 no_event_polls = 0 - msg_template = ('\n Stack %(name)s %(status)s \n') + msg_template = "\n Stack %(name)s %(status)s \n" while True: - events = _get_stack_events(h_client, stack_id=stack_name, - event_args={'sort_dir': 'asc', 'marker': marker}) + events = _get_stack_events( + h_client, + stack_id=stack_name, + event_args={"sort_dir": "asc", "marker": marker}, + ) if len(events) == 0: no_event_polls += 1 else: no_event_polls = 0 # set marker to last event that was received. - marker = getattr(events[-1], 'id', None) + marker = getattr(events[-1], "id", None) for event in events: # check if stack event was also received - if getattr(event, 'resource_name', '') == stack_name: - stack_status = getattr(event, 'resource_status', '') - msg = msg_template % dict( - name=stack_name, status=stack_status) + if getattr(event, "resource_name", "") == stack_name: + stack_status = getattr(event, "resource_status", "") + msg = msg_template % dict(name=stack_name, status=stack_status) if stop_check(stack_status): return stack_status, msg @@ -272,8 +282,7 @@ def _poll_for_events(h_client, stack_name, action=None, poll_period=5, # after 2 polls with no events, fall back to a stack get stack = h_client.stacks.get(stack_name) stack_status = stack.stack_status - msg = msg_template % dict( - name=stack_name, status=stack_status) + msg = msg_template % dict(name=stack_name, status=stack_status) if stop_check(stack_status): return stack_status, msg # go back to event polling again @@ -282,13 +291,13 @@ def _poll_for_events(h_client, stack_name, action=None, poll_period=5, time.sleep(poll_period) timeout_sec -= poll_period if timeout_sec <= 0: - stack_status = '{0}_FAILED'.format(action) - msg = 'Timeout expired' + stack_status = "{0}_FAILED".format(action) + msg = "Timeout expired" return stack_status, msg def list_stack(profile=None): - ''' + """ Return a list of available stack (heat stack-list) profile @@ -299,27 +308,27 @@ def list_stack(profile=None): .. code-block:: bash salt '*' heat.list_stack profile=openstack1 - ''' + """ ret = {} h_client = _auth(profile) for stack in h_client.stacks.list(): links = {} for link in stack.links: - links[link['rel']] = link['href'] + links[link["rel"]] = link["href"] ret[stack.stack_name] = { - 'status': stack.stack_status, - 'id': stack.id, - 'name': stack.stack_name, - 'creation': stack.creation_time, - 'owner': stack.stack_owner, - 'reason': stack.stack_status_reason, - 'links': links, + "status": stack.stack_status, + "id": stack.id, + "name": stack.stack_name, + "creation": stack.creation_time, + "owner": stack.stack_owner, + "reason": stack.stack_status_reason, + "links": links, } return ret def show_stack(name=None, profile=None): - ''' + """ Return details about a specific stack (heat stack-show) name @@ -333,41 +342,34 @@ def show_stack(name=None, profile=None): .. code-block:: bash salt '*' heat.show_stack name=mystack profile=openstack1 - ''' + """ h_client = _auth(profile) if not name: - return { - 'result': False, - 'comment': - 'Parameter name missing or None' - } + return {"result": False, "comment": "Parameter name missing or None"} try: ret = {} stack = h_client.stacks.get(name) links = {} for link in stack.links: - links[link['rel']] = link['href'] + links[link["rel"]] = link["href"] ret[stack.stack_name] = { - 'status': stack.stack_status, - 'id': stack.id, - 'name': stack.stack_name, - 'creation': stack.creation_time, - 'owner': stack.stack_owner, - 'reason': stack.stack_status_reason, - 'parameters': stack.parameters, - 'links': links, + "status": stack.stack_status, + "id": stack.id, + "name": stack.stack_name, + "creation": stack.creation_time, + "owner": stack.stack_owner, + "reason": stack.stack_status_reason, + "parameters": stack.parameters, + "links": links, } - ret['result'] = True + ret["result"] = True except heatclient.exc.HTTPNotFound: - return { - 'result': False, - 'comment': 'No stack {0}'.format(name) - } + return {"result": False, "comment": "No stack {0}".format(name)} return ret def delete_stack(name=None, poll=0, timeout=60, profile=None): - ''' + """ Delete a stack (heat stack-delete) name @@ -389,53 +391,58 @@ def delete_stack(name=None, poll=0, timeout=60, profile=None): salt '*' heat.delete_stack name=mystack poll=5 \\ profile=openstack1 - ''' + """ h_client = _auth(profile) - ret = { - 'result': True, - 'comment': '' - } + ret = {"result": True, "comment": ""} if not name: - ret['result'] = False - ret['comment'] = 'Parameter name missing or None' + ret["result"] = False + ret["comment"] = "Parameter name missing or None" return ret try: h_client.stacks.delete(name) except heatclient.exc.HTTPNotFound: - ret['result'] = False - ret['comment'] = 'No stack {0}'.format(name) + ret["result"] = False + ret["comment"] = "No stack {0}".format(name) except heatclient.exc.HTTPForbidden as forbidden: log.exception(forbidden) - ret['result'] = False - ret['comment'] = six.text_type(forbidden) - if ret['result'] is False: + ret["result"] = False + ret["comment"] = six.text_type(forbidden) + if ret["result"] is False: return ret if poll > 0: try: - stack_status, msg = _poll_for_events(h_client, name, action='DELETE', - poll_period=poll, timeout=timeout) + stack_status, msg = _poll_for_events( + h_client, name, action="DELETE", poll_period=poll, timeout=timeout + ) except heatclient.exc.CommandError: - ret['comment'] = 'Deleted stack {0}.'.format(name) + ret["comment"] = "Deleted stack {0}.".format(name) return ret except Exception as ex: # pylint: disable=W0703 - log.exception('Delete failed {0}'.format(ex)) - ret['result'] = False - ret['comment'] = '{0}'.format(ex) + log.exception("Delete failed {0}".format(ex)) + ret["result"] = False + ret["comment"] = "{0}".format(ex) return ret - if stack_status == 'DELETE_FAILED': - ret['result'] = False - ret['comment'] = 'Deleted stack FAILED\'{0}\'{1}.'.format(name, msg) + if stack_status == "DELETE_FAILED": + ret["result"] = False + ret["comment"] = "Deleted stack FAILED'{0}'{1}.".format(name, msg) else: - ret['comment'] = 'Deleted stack {0}.'.format(name) + ret["comment"] = "Deleted stack {0}.".format(name) return ret -def create_stack(name=None, template_file=None, environment=None, - parameters=None, poll=0, rollback=False, timeout=60, - profile=None): - ''' +def create_stack( + name=None, + template_file=None, + environment=None, + parameters=None, + poll=0, + rollback=False, + timeout=60, + profile=None, +): + """ Create a stack (heat stack-create) name @@ -477,17 +484,14 @@ def create_stack(name=None, template_file=None, environment=None, The spelling mistake in parameter `enviroment` was corrected to `environment`. The `enviroment` spelling mistake has been removed in Salt 3000. - ''' + """ h_client = _auth(profile) - ret = { - 'result': True, - 'comment': '' - } + ret = {"result": True, "comment": ""} if not parameters: parameters = {} if template_file: template_tmp_file = salt.utils.files.mkstemp() - tsfn, source_sum, comment_ = __salt__['file.get_managed']( + tsfn, source_sum, comment_ = __salt__["file.get_managed"]( name=template_tmp_file, template=None, source=template_file, @@ -497,13 +501,14 @@ def create_stack(name=None, template_file=None, environment=None, group=None, mode=None, attrs=None, - saltenv='base', + saltenv="base", context=None, defaults=None, skip_verify=False, - kwargs=None) + kwargs=None, + ) - template_manage_result = __salt__['file.manage_file']( + template_manage_result = __salt__["file.manage_file"]( name=template_tmp_file, sfn=tsfn, ret=None, @@ -513,44 +518,47 @@ def create_stack(name=None, template_file=None, environment=None, group=None, mode=None, attrs=None, - saltenv='base', + saltenv="base", backup=None, makedirs=True, template=None, show_changes=False, contents=None, - dir_mode=None) - if template_manage_result['result']: - with salt.utils.files.fopen(template_tmp_file, 'r') as tfp_: + dir_mode=None, + ) + if template_manage_result["result"]: + with salt.utils.files.fopen(template_tmp_file, "r") as tfp_: tpl = salt.utils.stringutils.to_unicode(tfp_.read()) salt.utils.files.safe_rm(template_tmp_file) try: template = _parse_template(tpl) except ValueError as ex: - ret['result'] = False - ret['comment'] = 'Error parsing template {0}'.format(ex) + ret["result"] = False + ret["comment"] = "Error parsing template {0}".format(ex) else: - ret['result'] = False - ret['comment'] = 'Can not open template: {0} {1}'.format(template_file, comment_) + ret["result"] = False + ret["comment"] = "Can not open template: {0} {1}".format( + template_file, comment_ + ) else: - ret['result'] = False - ret['comment'] = 'Can not open template' - if ret['result'] is False: + ret["result"] = False + ret["comment"] = "Can not open template" + if ret["result"] is False: return ret kwargs = {} - kwargs['template'] = template + kwargs["template"] = template try: h_client.stacks.validate(**kwargs) except Exception as ex: # pylint: disable=W0703 - log.exception('Template not valid {0}'.format(ex)) - ret['result'] = False - ret['comment'] = 'Template not valid {0}'.format(ex) + log.exception("Template not valid {0}".format(ex)) + ret["result"] = False + ret["comment"] = "Template not valid {0}".format(ex) return ret env = {} if environment: environment_tmp_file = salt.utils.files.mkstemp() - esfn, source_sum, comment_ = __salt__['file.get_managed']( + esfn, source_sum, comment_ = __salt__["file.get_managed"]( name=environment_tmp_file, template=None, source=environment, @@ -560,13 +568,14 @@ def create_stack(name=None, template_file=None, environment=None, group=None, mode=None, attrs=None, - saltenv='base', + saltenv="base", context=None, defaults=None, skip_verify=False, - kwargs=None) + kwargs=None, + ) - environment_manage_result = __salt__['file.manage_file']( + environment_manage_result = __salt__["file.manage_file"]( name=environment_tmp_file, sfn=esfn, ret=None, @@ -576,60 +585,71 @@ def create_stack(name=None, template_file=None, environment=None, group=None, mode=None, attrs=None, - saltenv='base', + saltenv="base", backup=None, makedirs=True, template=None, show_changes=False, contents=None, - dir_mode=None) - if environment_manage_result['result']: - with salt.utils.files.fopen(environment_tmp_file, 'r') as efp_: + dir_mode=None, + ) + if environment_manage_result["result"]: + with salt.utils.files.fopen(environment_tmp_file, "r") as efp_: env_str = salt.utils.stringutils.to_unicode(efp_.read()) salt.utils.files.safe_rm(environment_tmp_file) try: env = _parse_environment(env_str) except ValueError as ex: - ret['result'] = False - ret['comment'] = 'Error parsing template {0}'.format(ex) + ret["result"] = False + ret["comment"] = "Error parsing template {0}".format(ex) else: - ret['result'] = False - ret['comment'] = 'Can not open environment: {0}, {1}'.format(environment, comment_) - if ret['result'] is False: + ret["result"] = False + ret["comment"] = "Can not open environment: {0}, {1}".format( + environment, comment_ + ) + if ret["result"] is False: return ret fields = { - 'stack_name': name, - 'disable_rollback': not rollback, - 'parameters': parameters, - 'template': template, - 'environment': env, - 'timeout_mins': timeout + "stack_name": name, + "disable_rollback": not rollback, + "parameters": parameters, + "template": template, + "environment": env, + "timeout_mins": timeout, } # If one or more environments is found, pass the listing to the server try: h_client.stacks.create(**fields) except Exception as ex: # pylint: disable=W0703 - log.exception('Create failed {0}'.format(ex)) - ret['result'] = False - ret['comment'] = '{0}'.format(ex) + log.exception("Create failed {0}".format(ex)) + ret["result"] = False + ret["comment"] = "{0}".format(ex) return ret if poll > 0: - stack_status, msg = _poll_for_events(h_client, name, action='CREATE', - poll_period=poll, timeout=timeout) - if stack_status == 'CREATE_FAILED': - ret['result'] = False - ret['comment'] = 'Created stack FAILED\'{0}\'{1}.'.format(name, msg) - if ret['result'] is True: - ret['comment'] = 'Created stack \'{0}\'.'.format(name) + stack_status, msg = _poll_for_events( + h_client, name, action="CREATE", poll_period=poll, timeout=timeout + ) + if stack_status == "CREATE_FAILED": + ret["result"] = False + ret["comment"] = "Created stack FAILED'{0}'{1}.".format(name, msg) + if ret["result"] is True: + ret["comment"] = "Created stack '{0}'.".format(name) return ret -def update_stack(name=None, template_file=None, environment=None, - parameters=None, poll=0, rollback=False, timeout=60, - profile=None): - ''' +def update_stack( + name=None, + template_file=None, + environment=None, + parameters=None, + poll=0, + rollback=False, + timeout=60, + profile=None, +): + """ Update a stack (heat stack-template) name @@ -671,21 +691,18 @@ def update_stack(name=None, template_file=None, environment=None, The spelling mistake in parameter `enviroment` was corrected to `environment`. The `enviroment` spelling mistake has been removed in Salt 3000. - ''' + """ h_client = _auth(profile) - ret = { - 'result': True, - 'comment': '' - } + ret = {"result": True, "comment": ""} if not name: - ret['result'] = False - ret['comment'] = 'Parameter name missing or None' + ret["result"] = False + ret["comment"] = "Parameter name missing or None" return ret if not parameters: parameters = {} if template_file: template_tmp_file = salt.utils.files.mkstemp() - tsfn, source_sum, comment_ = __salt__['file.get_managed']( + tsfn, source_sum, comment_ = __salt__["file.get_managed"]( name=template_tmp_file, template=None, source=template_file, @@ -695,13 +712,14 @@ def update_stack(name=None, template_file=None, environment=None, group=None, mode=None, attrs=None, - saltenv='base', + saltenv="base", context=None, defaults=None, skip_verify=False, - kwargs=None) + kwargs=None, + ) - template_manage_result = __salt__['file.manage_file']( + template_manage_result = __salt__["file.manage_file"]( name=template_tmp_file, sfn=tsfn, ret=None, @@ -711,44 +729,47 @@ def update_stack(name=None, template_file=None, environment=None, group=None, mode=None, attrs=None, - saltenv='base', + saltenv="base", backup=None, makedirs=True, template=None, show_changes=False, contents=None, - dir_mode=None) - if template_manage_result['result']: - with salt.utils.files.fopen(template_tmp_file, 'r') as tfp_: + dir_mode=None, + ) + if template_manage_result["result"]: + with salt.utils.files.fopen(template_tmp_file, "r") as tfp_: tpl = salt.utils.stringutils.to_unicode(tfp_.read()) salt.utils.files.safe_rm(template_tmp_file) try: template = _parse_template(tpl) except ValueError as ex: - ret['result'] = False - ret['comment'] = 'Error parsing template {0}'.format(ex) + ret["result"] = False + ret["comment"] = "Error parsing template {0}".format(ex) else: - ret['result'] = False - ret['comment'] = 'Can not open template: {0} {1}'.format(template_file, comment_) + ret["result"] = False + ret["comment"] = "Can not open template: {0} {1}".format( + template_file, comment_ + ) else: - ret['result'] = False - ret['comment'] = 'Can not open template' - if ret['result'] is False: + ret["result"] = False + ret["comment"] = "Can not open template" + if ret["result"] is False: return ret kwargs = {} - kwargs['template'] = template + kwargs["template"] = template try: h_client.stacks.validate(**kwargs) except Exception as ex: # pylint: disable=W0703 - log.exception('Template not valid {0}'.format(ex)) - ret['result'] = False - ret['comment'] = 'Template not valid {0}'.format(ex) + log.exception("Template not valid {0}".format(ex)) + ret["result"] = False + ret["comment"] = "Template not valid {0}".format(ex) return ret env = {} if environment: environment_tmp_file = salt.utils.files.mkstemp() - esfn, source_sum, comment_ = __salt__['file.get_managed']( + esfn, source_sum, comment_ = __salt__["file.get_managed"]( name=environment_tmp_file, template=None, source=environment, @@ -758,13 +779,14 @@ def update_stack(name=None, template_file=None, environment=None, group=None, mode=None, attrs=None, - saltenv='base', + saltenv="base", context=None, defaults=None, skip_verify=False, - kwargs=None) + kwargs=None, + ) - environment_manage_result = __salt__['file.manage_file']( + environment_manage_result = __salt__["file.manage_file"]( name=environment_tmp_file, sfn=esfn, ret=None, @@ -774,57 +796,61 @@ def update_stack(name=None, template_file=None, environment=None, group=None, mode=None, attrs=None, - saltenv='base', + saltenv="base", backup=None, makedirs=True, template=None, show_changes=False, contents=None, - dir_mode=None) - if environment_manage_result['result']: - with salt.utils.files.fopen(environment_tmp_file, 'r') as efp_: + dir_mode=None, + ) + if environment_manage_result["result"]: + with salt.utils.files.fopen(environment_tmp_file, "r") as efp_: env_str = salt.utils.stringutils.to_unicode(efp_.read()) salt.utils.files.safe_rm(environment_tmp_file) try: env = _parse_environment(env_str) except ValueError as ex: - ret['result'] = False - ret['comment'] = 'Error parsing template {0}'.format(ex) + ret["result"] = False + ret["comment"] = "Error parsing template {0}".format(ex) else: - ret['result'] = False - ret['comment'] = 'Can not open environment: {0}, {1}'.format(environment, comment_) - if ret['result'] is False: + ret["result"] = False + ret["comment"] = "Can not open environment: {0}, {1}".format( + environment, comment_ + ) + if ret["result"] is False: return ret fields = { - 'disable_rollback': not rollback, - 'parameters': parameters, - 'template': template, - 'environment': env, - 'timeout_mins': timeout + "disable_rollback": not rollback, + "parameters": parameters, + "template": template, + "environment": env, + "timeout_mins": timeout, } try: h_client.stacks.update(name, **fields) except Exception as ex: # pylint: disable=W0703 - log.exception('Update failed {0}'.format(ex)) - ret['result'] = False - ret['comment'] = 'Update failed {0}'.format(ex) + log.exception("Update failed {0}".format(ex)) + ret["result"] = False + ret["comment"] = "Update failed {0}".format(ex) return ret if poll > 0: - stack_status, msg = _poll_for_events(h_client, name, action='UPDATE', - poll_period=poll, timeout=timeout) - if stack_status == 'UPDATE_FAILED': - ret['result'] = False - ret['comment'] = 'Updated stack FAILED\'{0}\'{1}.'.format(name, msg) - if ret['result'] is True: - ret['comment'] = 'Updated stack \'{0}\'.'.format(name), + stack_status, msg = _poll_for_events( + h_client, name, action="UPDATE", poll_period=poll, timeout=timeout + ) + if stack_status == "UPDATE_FAILED": + ret["result"] = False + ret["comment"] = "Updated stack FAILED'{0}'{1}.".format(name, msg) + if ret["result"] is True: + ret["comment"] = ("Updated stack '{0}'.".format(name),) return ret def template_stack(name=None, profile=None): - ''' + """ Return template a specific stack (heat stack-template) name @@ -838,36 +864,22 @@ def template_stack(name=None, profile=None): .. code-block:: bash salt '*' heat.template_stack name=mystack profile=openstack1 - ''' + """ h_client = _auth(profile) if not name: - return { - 'result': False, - 'comment': - 'Parameter name missing or None' - } + return {"result": False, "comment": "Parameter name missing or None"} try: get_template = h_client.stacks.template(name) except heatclient.exc.HTTPNotFound: - return { - 'result': False, - 'comment': 'No stack with {0}'.format(name) - } + return {"result": False, "comment": "No stack with {0}".format(name)} except heatclient.exc.BadRequest: - return { - 'result': False, - 'comment': 'Bad request fot stack {0}'.format(name) - } - if 'heat_template_version' in get_template: + return {"result": False, "comment": "Bad request fot stack {0}".format(name)} + if "heat_template_version" in get_template: template = salt.utils.yaml.safe_dump(get_template) else: template = jsonutils.dumps(get_template, indent=2, ensure_ascii=False) - checksum = __salt__['hashutil.digest'](template) - ret = { - 'template': template, - 'result': True, - 'checksum': checksum - } + checksum = __salt__["hashutil.digest"](template) + ret = {"template": template, "result": True, "checksum": checksum} return ret diff --git a/salt/modules/helm.py b/salt/modules/helm.py new file mode 100644 index 00000000000..016c446c739 --- /dev/null +++ b/salt/modules/helm.py @@ -0,0 +1,1561 @@ +# -*- coding: utf-8 -*- +""" +Interface with Helm + +:depends: helm_ package installed on minion's system. + +.. note:: + This module use the helm-cli. The helm-cli binary have to be present in your Salt-Minion path. + +Helm-CLI vs Salt-Modules +-------------- + +This module is a wrapper of the helm binary. +All helm v3.0 command are implemented. + +To install a chart with the helm-cli: + +.. code-block:: bash + + helm install grafana stable/grafana --wait --values /path/to/values.yaml + + +To install a chart with the Salt-Module: + +.. code-block:: bash + + salt '*' helm.install grafana stable/grafana values='/path/to/values.yaml' flags="['wait']" + + +Detailed Function Documentation +------------------------------- +""" + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +import copy +import logging +import re + +from salt.exceptions import CommandExecutionError + +# Import Salt libs +from salt.serializers import json + +log = logging.getLogger(__name__) + +# Don't shadow built-in's. +__func_alias__ = { + "help_": "help", + "list_": "list", +} + + +def _prepare_cmd(binary="helm", commands=None, flags=None, kvflags=None): + """ + + :param binary: + :param commands: + :param flags: + :param kvflags: + :return: + """ + if commands is None: + commands = [] + if flags is None: + flags = [] + else: + flags = copy.deepcopy(flags) + if kvflags is None: + kvflags = {} + else: + kvflags = copy.deepcopy(kvflags) + + cmd = (binary,) + + for command in commands: + cmd += (command,) + + for arg in flags: + if not re.search(r"^--.*", arg): + arg = "--" + arg + cmd += (arg,) + + for key, val in kvflags.items(): + if not re.search(r"^--.*", key): + key = "--" + key + if key == "--set" and isinstance(val, list): + for set_val in val: + cmd += ( + key, + set_val, + ) + else: + cmd += ( + key, + val, + ) + + return cmd + + +def _exec_cmd(commands=None, flags=None, kvflags=None): + """ + + :param commands: + :param flags: + :param kvflags: + :return: + """ + cmd = _prepare_cmd(commands=commands, flags=flags, kvflags=kvflags) + cmd_string = " ".join(cmd) + + try: + result = __salt__["cmd.run_all"](cmd=cmd) + result.update({"cmd": cmd_string}) + except CommandExecutionError as err: + result = {"retcode": -1, "stdout": "", "stderr": err, "cmd": cmd_string} + log.error(result) + + return result + + +def _exec_true_return(commands=None, flags=None, kvflags=None): + """ + + :param commands: + :param flags: + :param kvflags: + :return: + """ + cmd_result = _exec_cmd(commands=commands, flags=flags, kvflags=kvflags) + if cmd_result.get("retcode", -1) == 0: + result = True + else: + result = cmd_result.get("stderr", "") + return result + + +def _exec_string_return(commands=None, flags=None, kvflags=None): + """ + + :param commands: + :param flags: + :param kvflags: + :return: + """ + cmd_result = _exec_cmd(commands=commands, flags=flags, kvflags=kvflags) + if cmd_result.get("retcode", -1) == 0: + result = cmd_result.get("stdout", "") + else: + result = cmd_result.get("stderr", "") + return result + + +def _exec_dict_return(commands=None, flags=None, kvflags=None): + """ + + :param commands: + :param flags: + :param kvflags: + :return: + """ + if kvflags is None: + kvflags = {} + if not ("output" in kvflags.keys() or "--output" in kvflags.keys()): + kvflags.update({"output": "json"}) + cmd_result = _exec_cmd(commands=commands, flags=flags, kvflags=kvflags) + if cmd_result.get("retcode", -1) == 0: + if kvflags.get("output") == "json" or kvflags.get("--output") == "json": + result = json.deserialize(cmd_result.get("stdout", "")) + else: + result = cmd_result.get("stdout", "") + else: + result = cmd_result.get("stderr", "") + return result + + +def completion(shell, flags=None, kvflags=None): + """ + Generate auto-completions script for Helm for the specified shell (bash or zsh). + Return the shell auto-completion content. + + shell + (string) One of ['bash', 'zsh']. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.completion bash + + """ + return _exec_string_return( + commands=["completion", shell], flags=flags, kvflags=kvflags + ) + + +def create(name, flags=None, kvflags=None): + """ + Creates a chart directory along with the common files and directories used in a chart. + Return True if succeed, else the error message. + + name + (string) The chart name to create. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.create NAME + + """ + return _exec_true_return(commands=["create", name], flags=flags, kvflags=kvflags) + + +def dependency_build(chart, flags=None, kvflags=None): + """ + Build out the charts/ directory from the Chart.lock file. + Return True if succeed, else the error message. + + chart + (string) The chart name to build dependency. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.dependency_build CHART + + """ + return _exec_true_return( + commands=["dependency", "build", chart], flags=flags, kvflags=kvflags + ) + + +def dependency_list(chart, flags=None, kvflags=None): + """ + List all of the dependencies declared in a chart. + Return chart dependencies if succeed, else the error message. + + chart + (string) The chart name to list dependency. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.dependency_list CHART + + """ + return _exec_string_return( + commands=["dependency", "list", chart], flags=flags, kvflags=kvflags + ) + + +def dependency_update(chart, flags=None, kvflags=None): + """ + Update the on-disk dependencies to mirror Chart.yaml. + Return True if succeed, else the error message. + + chart + (string) The chart name to update dependency. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.dependency_update CHART + + """ + return _exec_true_return( + commands=["dependency", "update", chart], flags=flags, kvflags=kvflags + ) + + +def env(flags=None, kvflags=None): + """ + Prints out all the environment information in use by Helm. + Return Helm environments variables if succeed, else the error message. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.env + + """ + return _exec_string_return(commands=["env"], flags=flags, kvflags=kvflags) + + +def get_all(release, flags=None, kvflags=None): + """ + Prints a human readable collection of information about the notes, hooks, supplied values, and generated manifest file of the given release. + Return release information if succeed, else the error message. + + release + (string) Release name to get information from. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.get_all RELEASE + + """ + return _exec_string_return( + commands=["get", "all", release], flags=flags, kvflags=kvflags + ) + + +def get_hooks(release, flags=None, kvflags=None): + """ + Prints a human readable collection of information about the hooks of the given release. + Return release hooks information if succeed, else the error message. + + release + (string) Release name to get hooks information from. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.get_hooks RELEASE + + """ + return _exec_string_return( + commands=["get", "hooks", release], flags=flags, kvflags=kvflags + ) + + +def get_manifest(release, flags=None, kvflags=None): + """ + Prints a human readable collection of information about the manifest of the given release. + Return release manifest information if succeed, else the error message. + + release + (string) Release name to get manifest information from. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.get_manifest RELEASE + + """ + return _exec_string_return( + commands=["get", "manifest", release], flags=flags, kvflags=kvflags + ) + + +def get_notes(release, flags=None, kvflags=None): + """ + Prints a human readable collection of information about the notes of the given release. + Return release notes information if succeed, else the error message. + + release + (string) Release name to get notes information from. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.get_notes RELEASE + + """ + return _exec_string_return( + commands=["get", "notes", release], flags=flags, kvflags=kvflags + ) + + +def get_values(release, flags=None, kvflags=None): + """ + Prints a human readable collection of information about the values of the given release. + Return release values information if succeed, else the error message. + + release + (string) Release name to get values information from. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.get_values RELEASE + + # In YAML format + salt '*' helm.get_values RELEASE kvflags="{'output': 'yaml'}" + + """ + return _exec_dict_return( + commands=["get", "values", release], flags=flags, kvflags=kvflags + ) + + +def help_(command, flags=None, kvflags=None): + """ + Provides help for any command in the application. + Return the full help if succeed, else the error message. + + command + (string) Command to get help. ex: 'get' + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.help COMMAND + + """ + return _exec_string_return(commands=["help", command], flags=flags, kvflags=kvflags) + + +def history(release, flags=None, kvflags=None): + """ + Prints historical revisions for a given release. + Return release historic if succeed, else the error message. + + release + (string) Release name to get history from. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.history RELEASE + + # In YAML format + salt '*' helm.history RELEASE kvflags="{'output': 'yaml'}" + + """ + return _exec_dict_return( + commands=["history", release], flags=flags, kvflags=kvflags + ) + + +def install( + release, + chart, + values=None, + version=None, + namespace=None, + set=None, + flags=None, + kvflags=None, +): + """ + Installs a chart archive. + Return True if succeed, else the error message. + + release + (string) Release name to get values information from. + + chart + (string) Chart name to install. + + values + (string) Absolute path to the values.yaml file. + + version + (string) The exact chart version to install. If this is not specified, the latest version is installed. + + namespace + (string) The namespace scope for this request. + + set + (string or list) Set a values on the command line. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.install RELEASE CHART + + # With values file. + salt '*' helm.install RELEASE CHART values='/path/to/values.yaml' + + """ + if values: + if kvflags: + kvflags.update({"values": values}) + else: + kvflags = {"values": values} + if version: + if kvflags: + kvflags.update({"version": version}) + else: + kvflags = {"version": version} + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + if set: + if kvflags: + kvflags.update({"set": set}) + else: + kvflags = {"set": set} + return _exec_true_return( + commands=["install", release, chart], flags=flags, kvflags=kvflags + ) + + +def lint(path, values=None, namespace=None, set=None, flags=None, kvflags=None): + """ + Takes a path to a chart and runs a series of tests to verify that the chart is well-formed. + Return True if succeed, else the error message. + + path + (string) The path to the chart to lint. + + values + (string) Absolute path to the values.yaml file. + + namespace + (string) The namespace scope for this request. + + set + (string or list) Set a values on the command line. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.lint PATH + + """ + if values: + if kvflags: + kvflags.update({"values": values}) + else: + kvflags = {"values": values} + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + if set: + if kvflags: + kvflags.update({"set": set}) + else: + kvflags = {"set": set} + return _exec_true_return(commands=["lint", path], flags=flags, kvflags=kvflags) + + +def list_(namespace=None, flags=None, kvflags=None): + """ + Lists all of the releases. By default, it lists only releases that are deployed or failed. + Return the list of release if succeed, else the error message. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.list + + # In YAML format + salt '*' helm.list kvflags="{'output': 'yaml'}" + + """ + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + return _exec_dict_return(commands=["list"], flags=flags, kvflags=kvflags) + + +def package(chart, flags=None, kvflags=None): + """ + Packages a chart into a versioned chart archive file. If a path is given, this will look at that path for a chart + (which must contain a Chart.yaml file) and then package that directory. + Return True if succeed, else the error message. + + chart + (string) Chart name to package. Can be an absolute path. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.package CHART + + # With destination path. + salt '*' helm.package CHART kvflags="{'destination': '/path/to/the/package'}" + + """ + return _exec_true_return(commands=["package", chart], flags=flags, kvflags=kvflags) + + +def plugin_install(path, flags=None, kvflags=None): + """ + Install a Helm plugin from a url to a VCS repo or a local path. + Return True if succeed, else the error message. + + path + (string) Path to the local plugin. Can be an url. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.plugin_install PATH + + """ + return _exec_true_return( + commands=["plugin", "install", path], flags=flags, kvflags=kvflags + ) + + +def plugin_list(flags=None, kvflags=None): + """ + List installed Helm plugins. + Return the plugin list if succeed, else the error message. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.plugin_list + + """ + return _exec_string_return( + commands=["plugin", "list"], flags=flags, kvflags=kvflags + ) + + +def plugin_uninstall(plugin, flags=None, kvflags=None): + """ + Uninstall a Helm plugin. + Return True if succeed, else the error message. + + plugin + (string) The plugin to uninstall. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.plugin_uninstall PLUGIN + + """ + return _exec_true_return( + commands=["plugin", "uninstall", plugin], flags=flags, kvflags=kvflags + ) + + +def plugin_update(plugin, flags=None, kvflags=None): + """ + Update a Helm plugin. + Return True if succeed, else the error message. + + plugin + (string) The plugin to update. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.plugin_update PLUGIN + + """ + return _exec_true_return( + commands=["plugin", "update", plugin], flags=flags, kvflags=kvflags + ) + + +def pull(pkg, flags=None, kvflags=None): + """ + Retrieve a package from a package repository, and download it locally. + Return True if succeed, else the error message. + + pkg + (string) The package to pull. Can be url or repo/chartname. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.pull PKG + + # With destination path to write the chart. + salt '*' helm.pull PKG kvflags="{'destination': '/path/to/the/chart'}" + + """ + return _exec_true_return(commands=["pull", pkg], flags=flags, kvflags=kvflags) + + +def repo_add(name, url, namespace=None, flags=None, kvflags=None): + """ + Add a chart repository. + Return True if succeed, else the error message. + + name + (string) The local name of the repository to install. Have to be unique. + + url + (string) The url to the repository. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.repo_add NAME URL + + """ + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + return _exec_true_return( + commands=["repo", "add", name, url], flags=flags, kvflags=kvflags + ) + + +def repo_index(directory, namespace=None, flags=None, kvflags=None): + """ + Read the current directory and generate an index file based on the charts found. + Return True if succeed, else the error message. + + directory + (string) The path to the index. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.index DIRECTORY + + """ + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + return _exec_true_return( + commands=["repo", "index", directory], flags=flags, kvflags=kvflags + ) + + +def repo_list(namespace=None, flags=None, kvflags=None): + """ + List a chart repository. + Return the repository list if succeed, else the error message. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.repo_list + + # In YAML format + salt '*' helm.repo_list kvflags="{'output': 'yaml'}" + + """ + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + return _exec_dict_return(commands=["repo", "list"], flags=flags, kvflags=kvflags) + + +def repo_remove(name, namespace=None, flags=None, kvflags=None): + """ + Remove a chart repository. + Return True if succeed, else the error message. + + name + (string) The local name of the repository to remove. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.repo_remove NAME + + """ + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + return _exec_true_return( + commands=["repo", "remove", name], flags=flags, kvflags=kvflags + ) + + +def repo_update(namespace=None, flags=None, kvflags=None): + """ + Update all charts repository. + Return True if succeed, else the error message. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.repo_update + + """ + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + return _exec_true_return(commands=["repo", "update"], flags=flags, kvflags=kvflags) + + +def repo_manage( + present=None, absent=None, prune=False, namespace=None, flags=None, kvflags=None +): + """ + Manage charts repository. + Return the summery of all actions. + + present + (list) List of repository to be present. It's a list of dict: [{'name': 'local_name', 'url': 'repository_url'}] + + absent + (list) List of local name repository to be absent. + + prune + (boolean - default: False) If True, all repository already present but not in the present list would be removed. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.repo_manage present="[{'name': 'LOCAL_NAME', 'url': 'REPO_URL'}]" absent="['LOCAL_NAME']" + + """ + if present is None: + present = [] + else: + present = copy.deepcopy(present) + if absent is None: + absent = [] + else: + absent = copy.deepcopy(absent) + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + + repos_present = repo_list(namespace=namespace, flags=flags, kvflags=kvflags) + if not isinstance(repos_present, list): + repos_present = [] + result = {"present": [], "added": [], "absent": [], "removed": [], "failed": []} + + for repo in present: + if not ( + isinstance(repo, dict) and "name" in repo.keys() and "url" in repo.keys() + ): + raise CommandExecutionError( + "Parameter present have to be formatted like " + "[{'name': '', 'url': ''}]" + ) + + already_present = False + for (index, repo_present) in enumerate(repos_present): + if repo.get("name") == repo_present.get("name") and repo.get( + "url" + ) == repo_present.get("url"): + result["present"].append(repo) + repos_present.pop(index) + already_present = True + break + + if not already_present: + repo_add_status = repo_add( + repo.get("name"), + repo.get("url"), + namespace=namespace, + flags=flags, + kvflags=kvflags, + ) + if isinstance(repo_add_status, bool) and repo_add_status: + result["added"].append(repo) + else: + result["failed"].append(repo) + + for repo in repos_present: + if prune: + absent.append(repo.get("name")) + elif not repo.get("name") in absent: + result["present"].append(repo) + + for name in absent: + remove_status = repo_remove(name, namespace=namespace) + if isinstance(remove_status, bool) and remove_status: + result["removed"].append(name) + else: + result["absent"].append(name) + + return result + + +def rollback(release, revision, namespace=None, flags=None, kvflags=None): + """ + Rolls back a release to a previous revision. + To see release revision number, execute the history module. + Return True if succeed, else the error message. + + release + (string) The name of the release to managed. + + revision + (string) The revision number to roll back to. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.rollback RELEASE REVISION + + # In dry-run mode. + salt '*' helm.rollback RELEASE REVISION flags=['dry-run'] + + """ + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + return _exec_true_return( + commands=["rollback", release, revision], flags=flags, kvflags=kvflags + ) + + +def search_hub(keyword, flags=None, kvflags=None): + """ + Search the Helm Hub or an instance of Monocular for Helm charts. + Return the research result if succeed, else the error message. + + keyword + (string) The keyword to search in the hub. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.search_hub KEYWORD + + # In YAML format + salt '*' helm.search_hub KEYWORD kvflags="{'output': 'yaml'}" + + """ + return _exec_dict_return( + commands=["search", "hub", keyword], flags=flags, kvflags=kvflags + ) + + +def search_repo(keyword, flags=None, kvflags=None): + """ + Search reads through all of the repositories configured on the system, and looks for matches. Search of these + repositories uses the metadata stored on the system. + Return the research result if succeed, else the error message. + + keyword + (string) The keyword to search in the repo. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.search_hub KEYWORD + + # In YAML format + salt '*' helm.search_hub KEYWORD kvflags="{'output': 'yaml'}" + + """ + return _exec_dict_return( + commands=["search", "repo", keyword], flags=flags, kvflags=kvflags + ) + + +def show_all(chart, flags=None, kvflags=None): + """ + Inspects a chart (directory, file, or URL) and displays all its content (values.yaml, Charts.yaml, README). + Return chart information if succeed, else the error message. + + chart + (string) The chart to inspect. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.show_all CHART + + """ + return _exec_string_return( + commands=["show", "all", chart], flags=flags, kvflags=kvflags + ) + + +def show_chart(chart, flags=None, kvflags=None): + """ + Inspects a chart (directory, file, or URL) and displays the contents of the Charts.yaml file. + Return chart information if succeed, else the error message. + + chart + (string) The chart to inspect. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.show_chart CHART + + """ + return _exec_string_return( + commands=["show", "chart", chart], flags=flags, kvflags=kvflags + ) + + +def show_readme(chart, flags=None, kvflags=None): + """ + Inspects a chart (directory, file, or URL) and displays the contents of the README file. + Return chart information if succeed, else the error message. + + chart + (string) The chart to inspect. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.show_readme CHART + + """ + return _exec_string_return( + commands=["show", "readme", chart], flags=flags, kvflags=kvflags + ) + + +def show_values(chart, flags=None, kvflags=None): + """ + Inspects a chart (directory, file, or URL) and displays the contents of the values.yaml file. + Return chart information if succeed, else the error message. + + chart + (string) The chart to inspect. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.show_values CHART + + """ + return _exec_string_return( + commands=["show", "values", chart], flags=flags, kvflags=kvflags + ) + + +def status(release, namespace=None, flags=None, kvflags=None): + """ + Show the status of the release. + Return the release status if succeed, else the error message. + + release + (string) The release to status. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.status RELEASE + + # In YAML format + salt '*' helm.status RELEASE kvflags="{'output': 'yaml'}" + + """ + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + return _exec_dict_return(commands=["status", release], flags=flags, kvflags=kvflags) + + +def template( + name, chart, values=None, output_dir=None, set=None, flags=None, kvflags=None +): + """ + Render chart templates locally and display the output. + Return the chart renderer if succeed, else the error message. + + name + (string) The template name. + + chart + (string) The chart to template. + + values + (string) Absolute path to the values.yaml file. + + output_dir + (string) Absolute path to the output directory. + + set + (string or list) Set a values on the command line. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.template NAME CHART + + # With values file. + salt '*' helm.template NAME CHART values='/path/to/values.yaml' output_dir='path/to/output/dir' + + """ + if values: + if kvflags: + kvflags.update({"values": values}) + else: + kvflags = {"values": values} + if set: + if kvflags: + kvflags.update({"set": set}) + else: + kvflags = {"set": set} + if output_dir: + kvflags.update({"output-dir": output_dir}) + return _exec_string_return( + commands=["template", name, chart], flags=flags, kvflags=kvflags + ) + + +def test(release, flags=None, kvflags=None): + """ + Runs the tests for a release. + Return the test result if succeed, else the error message. + + release + (string) The release name to test. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.test RELEASE + + """ + return _exec_string_return(commands=["test", release], flags=flags, kvflags=kvflags) + + +def uninstall(release, namespace=None, flags=None, kvflags=None): + """ + Uninstall the release name. + Return True if succeed, else the error message. + + release + (string) The name of the release to managed. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.uninstall RELEASE + + # In dry-run mode. + salt '*' helm.uninstall RELEASE flags=['dry-run'] + + """ + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + return _exec_true_return( + commands=["uninstall", release], flags=flags, kvflags=kvflags + ) + + +def upgrade( + release, + chart, + values=None, + version=None, + namespace=None, + set=None, + flags=None, + kvflags=None, +): + """ + Upgrades a release to a new version of a chart. + Return True if succeed, else the error message. + + release + (string) The name of the release to managed. + + chart + (string) The chart to managed. + + values + (string) Absolute path to the values.yaml file. + + version + (string) The exact chart version to install. If this is not specified, the latest version is installed. + + namespace + (string) The namespace scope for this request. + + set + (string or list) Set a values on the command line. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.upgrade RELEASE CHART + + # In dry-run mode. + salt '*' helm.upgrade RELEASE CHART flags=['dry-run'] + + # With values file. + salt '*' helm.upgrade RELEASE CHART values='/path/to/values.yaml' + + """ + if values: + if kvflags: + kvflags.update({"values": values}) + else: + kvflags = {"values": values} + if version: + if kvflags: + kvflags.update({"version": version}) + else: + kvflags = {"version": version} + if namespace: + if kvflags: + kvflags.update({"namespace": namespace}) + else: + kvflags = {"namespace": namespace} + if set: + if kvflags: + kvflags.update({"set": set}) + else: + kvflags = {"set": set} + return _exec_true_return( + commands=["upgrade", release, chart], flags=flags, kvflags=kvflags + ) + + +def verify(path, flags=None, kvflags=None): + """ + Verify that the given chart has a valid provenance file. + Return True if succeed, else the error message. + + path + (string) The path to the chart file. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.verify PATH + + """ + return _exec_true_return(commands=["verify", path], flags=flags, kvflags=kvflags) + + +def version(flags=None, kvflags=None): + """ + Show the version for Helm. + Return version information if succeed, else the error message. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + CLI Example: + + .. code-block:: bash + + salt '*' helm.version + + """ + return _exec_string_return(commands=["version"], flags=flags, kvflags=kvflags) diff --git a/salt/modules/hg.py b/salt/modules/hg.py index 676657e5d99..2817133525e 100644 --- a/salt/modules/hg.py +++ b/salt/modules/hg.py @@ -1,37 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" Support for the Mercurial SCM -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.data +import salt.utils.path + # Import salt libs from salt.exceptions import CommandExecutionError -import salt.utils.data -import salt.utils.path log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if hg is installed - ''' - if salt.utils.path.which('hg') is None: - return (False, - 'The hg execution module cannot be loaded: hg unavailable.') + """ + if salt.utils.path.which("hg") is None: + return (False, "The hg execution module cannot be loaded: hg unavailable.") else: return True def _ssh_flag(identity_path): - return ['--ssh', 'ssh -i {0}'.format(identity_path)] + return ["--ssh", "ssh -i {0}".format(identity_path)] -def revision(cwd, rev='tip', short=False, user=None): - ''' +def revision(cwd, rev="tip", short=False, user=None): + """ Returns the long hash of a given identifier (hash, branch, tag, HEAD, etc) cwd @@ -51,29 +52,19 @@ def revision(cwd, rev='tip', short=False, user=None): .. code-block:: bash salt '*' hg.revision /path/to/repo mybranch - ''' - cmd = [ - 'hg', - 'id', - '-i', - '--debug' if not short else '', - '-r', - '{0}'.format(rev)] + """ + cmd = ["hg", "id", "-i", "--debug" if not short else "", "-r", "{0}".format(rev)] - result = __salt__['cmd.run_all']( - cmd, - cwd=cwd, - runas=user, - python_shell=False) + result = __salt__["cmd.run_all"](cmd, cwd=cwd, runas=user, python_shell=False) - if result['retcode'] == 0: - return result['stdout'] + if result["retcode"] == 0: + return result["stdout"] else: - return '' + return "" -def describe(cwd, rev='tip', user=None): - ''' +def describe(cwd, rev="tip", user=None): + """ Mimic git describe and return an identifier for the given revision cwd @@ -90,26 +81,22 @@ def describe(cwd, rev='tip', user=None): .. code-block:: bash salt '*' hg.describe /path/to/repo - ''' + """ cmd = [ - 'hg', - 'log', - '-r', - '{0}'.format(rev), - '--template', - "'{{latesttag}}-{{latesttagdistance}}-{{node|short}}'" - ] - desc = __salt__['cmd.run_stdout']( - cmd, - cwd=cwd, - runas=user, - python_shell=False) + "hg", + "log", + "-r", + "{0}".format(rev), + "--template", + "'{{latesttag}}-{{latesttagdistance}}-{{node|short}}'", + ] + desc = __salt__["cmd.run_stdout"](cmd, cwd=cwd, runas=user, python_shell=False) return desc or revision(cwd, rev, short=True) -def archive(cwd, output, rev='tip', fmt=None, prefix=None, user=None): - ''' +def archive(cwd, output, rev="tip", fmt=None, prefix=None, user=None): + """ Export a tarball from the repository cwd @@ -139,25 +126,25 @@ def archive(cwd, output, rev='tip', fmt=None, prefix=None, user=None): .. code-block:: bash salt '*' hg.archive /path/to/repo output=/tmp/archive.tgz fmt=tgz - ''' + """ cmd = [ - 'hg', - 'archive', - '{0}'.format(output), - '--rev', - '{0}'.format(rev), - ] + "hg", + "archive", + "{0}".format(output), + "--rev", + "{0}".format(rev), + ] if fmt: - cmd.append('--type') - cmd.append('{0}'.format(fmt)) + cmd.append("--type") + cmd.append("{0}".format(fmt)) if prefix: - cmd.append('--prefix') + cmd.append("--prefix") cmd.append('"{0}"'.format(prefix)) - return __salt__['cmd.run'](cmd, cwd=cwd, runas=user, python_shell=False) + return __salt__["cmd.run"](cmd, cwd=cwd, runas=user, python_shell=False) def pull(cwd, opts=None, user=None, identity=None, repository=None): - ''' + """ Perform a pull on the given repository cwd @@ -182,8 +169,8 @@ def pull(cwd, opts=None, user=None, identity=None, repository=None): .. code-block:: bash salt '*' hg.pull /path/to/repo opts=-u - ''' - cmd = ['hg', 'pull'] + """ + cmd = ["hg", "pull"] if identity: cmd.extend(_ssh_flag(identity)) if opts: @@ -192,17 +179,17 @@ def pull(cwd, opts=None, user=None, identity=None, repository=None): if repository is not None: cmd.append(repository) - ret = __salt__['cmd.run_all'](cmd, cwd=cwd, runas=user, python_shell=False) - if ret['retcode'] != 0: + ret = __salt__["cmd.run_all"](cmd, cwd=cwd, runas=user, python_shell=False) + if ret["retcode"] != 0: raise CommandExecutionError( - 'Hg command failed: {0}'.format(ret.get('stderr', ret['stdout'])) + "Hg command failed: {0}".format(ret.get("stderr", ret["stdout"])) ) - return ret['stdout'] + return ret["stdout"] def update(cwd, rev, force=False, user=None): - ''' + """ Update to a given revision cwd @@ -222,22 +209,22 @@ def update(cwd, rev, force=False, user=None): .. code-block:: bash salt devserver1 hg.update /path/to/repo somebranch - ''' - cmd = ['hg', 'update', '{0}'.format(rev)] + """ + cmd = ["hg", "update", "{0}".format(rev)] if force: - cmd.append('-C') + cmd.append("-C") - ret = __salt__['cmd.run_all'](cmd, cwd=cwd, runas=user, python_shell=False) - if ret['retcode'] != 0: + ret = __salt__["cmd.run_all"](cmd, cwd=cwd, runas=user, python_shell=False) + if ret["retcode"] != 0: raise CommandExecutionError( - 'Hg command failed: {0}'.format(ret.get('stderr', ret['stdout'])) + "Hg command failed: {0}".format(ret.get("stderr", ret["stdout"])) ) - return ret['stdout'] + return ret["stdout"] def clone(cwd, repository, opts=None, user=None, identity=None): - ''' + """ Clone a new repository cwd @@ -262,25 +249,25 @@ def clone(cwd, repository, opts=None, user=None, identity=None): .. code-block:: bash salt '*' hg.clone /path/to/repo https://bitbucket.org/birkenfeld/sphinx - ''' - cmd = ['hg', 'clone', '{0}'.format(repository), '{0}'.format(cwd)] + """ + cmd = ["hg", "clone", "{0}".format(repository), "{0}".format(cwd)] if opts: for opt in opts.split(): - cmd.append('{0}'.format(opt)) + cmd.append("{0}".format(opt)) if identity: cmd.extend(_ssh_flag(identity)) - ret = __salt__['cmd.run_all'](cmd, runas=user, python_shell=False) - if ret['retcode'] != 0: + ret = __salt__["cmd.run_all"](cmd, runas=user, python_shell=False) + if ret["retcode"] != 0: raise CommandExecutionError( - 'Hg command failed: {0}'.format(ret.get('stderr', ret['stdout'])) + "Hg command failed: {0}".format(ret.get("stderr", ret["stdout"])) ) - return ret['stdout'] + return ret["stdout"] def status(cwd, opts=None, user=None): - ''' + """ Show changed files of the given repository cwd @@ -297,23 +284,23 @@ def status(cwd, opts=None, user=None): .. code-block:: bash salt '*' hg.status /path/to/repo - ''' + """ + def _status(cwd): - cmd = ['hg', 'status'] + cmd = ["hg", "status"] if opts: for opt in opts.split(): - cmd.append('{0}'.format(opt)) - out = __salt__['cmd.run_stdout']( - cmd, cwd=cwd, runas=user, python_shell=False) + cmd.append("{0}".format(opt)) + out = __salt__["cmd.run_stdout"](cmd, cwd=cwd, runas=user, python_shell=False) types = { - 'M': 'modified', - 'A': 'added', - 'R': 'removed', - 'C': 'clean', - '!': 'missing', - '?': 'not tracked', - 'I': 'ignored', - ' ': 'origin of the previous file', + "M": "modified", + "A": "added", + "R": "removed", + "C": "clean", + "!": "missing", + "?": "not tracked", + "I": "ignored", + " ": "origin of the previous file", } ret = {} for line in out.splitlines(): diff --git a/salt/modules/highstate_doc.py b/salt/modules/highstate_doc.py index cda08337162..d30ab92f29c 100644 --- a/salt/modules/highstate_doc.py +++ b/salt/modules/highstate_doc.py @@ -6,13 +6,13 @@ This module renders highstate configuration into a more human readable format. How it works: - `highstate or lowstate` data is parsed with a `proccesser` this defaults to `highstate_doc.proccesser_markdown`. - The proccessed data is passed to a `jinja` template that builds up the document content. +`highstate or lowstate` data is parsed with a `proccesser` this defaults to `highstate_doc.proccesser_markdown`. +The proccessed data is passed to a `jinja` template that builds up the document content. configuration: Pillar -.. code-block:: yaml +.. code-block:: none # the following defaults can be overrided highstate_doc.config: @@ -145,7 +145,7 @@ Other hints If you wish to customize the document format: -.. code-block:: yaml +.. code-block:: none # you could also create a new `proccesser` for perhaps reStructuredText # highstate_doc.config: @@ -178,47 +178,59 @@ If you wish to customize the document format: - mode: '0640' -Some `replace_text_regex` values that might be helpful. +Some `replace_text_regex` values that might be helpful:: - ## CERTS - '-----BEGIN RSA PRIVATE KEY-----[\r\n\t\f\S]{0,2200}': 'XXXXXXX' - '-----BEGIN CERTIFICATE-----[\r\n\t\f\S]{0,2200}': 'XXXXXXX' - '-----BEGIN DH PARAMETERS-----[\r\n\t\f\S]{0,2200}': 'XXXXXXX' - '-----BEGIN PRIVATE KEY-----[\r\n\t\f\S]{0,2200}': 'XXXXXXX' - '-----BEGIN OPENSSH PRIVATE KEY-----[\r\n\t\f\S]{0,2200}': 'XXXXXXX' - 'ssh-rsa .* ': 'ssh-rsa XXXXXXX ' - 'ssh-dss .* ': 'ssh-dss XXXXXXX ' - ## DB - 'DB_PASS.*': 'DB_PASS = XXXXXXX' - '5432:*:*:.*': '5432:*:XXXXXXX' - "'PASSWORD': .*": "'PASSWORD': 'XXXXXXX'," - " PASSWORD '.*'": " PASSWORD 'XXXXXXX'" - 'PGPASSWORD=.* ': 'PGPASSWORD=XXXXXXX' - "_replication password '.*'": "_replication password 'XXXXXXX'" - ## OTHER - 'EMAIL_HOST_PASSWORD =.*': 'EMAIL_HOST_PASSWORD =XXXXXXX' - "net ads join -U '.*@MFCFADS.MATH.EXAMPLE.CA.* ": "net ads join -U '.*@MFCFADS.MATH.EXAMPLE.CA%XXXXXXX " - "net ads join -U '.*@NEXUS.EXAMPLE.CA.* ": "net ads join -U '.*@NEXUS.EXAMPLE.CA%XXXXXXX " - 'install-uptrack .* --autoinstall': 'install-uptrack XXXXXXX --autoinstall' - 'accesskey = .*': 'accesskey = XXXXXXX' - 'auth_pass .*': 'auth_pass XXXXXXX' - 'PSK "0x.*': 'PSK "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' - 'SECRET_KEY.*': 'SECRET_KEY = XXXXXXX' - "password=.*": "password=XXXXXXX" - '.*': 'XXXXXXX' - '.*': 'XXXXXXX' - 'application.secret = ".*"': 'application.secret = "XXXXXXX"' - 'url = "postgres://.*"': 'url = "postgres://XXXXXXX"' - 'PASS_.*_PASS': 'PASS_XXXXXXX_PASS' - ## HTACCESS - ':{PLAIN}.*': ':{PLAIN}XXXXXXX' + CERTS + ----- + + ``'-----BEGIN RSA PRIVATE KEY-----[\\r\\n\\t\\f\\S]{0,2200}': 'XXXXXXX'`` + ``'-----BEGIN CERTIFICATE-----[\\r\\n\\t\\f\\S]{0,2200}': 'XXXXXXX'`` + ``'-----BEGIN DH PARAMETERS-----[\\r\\n\\t\\f\\S]{0,2200}': 'XXXXXXX'`` + ``'-----BEGIN PRIVATE KEY-----[\\r\\n\\t\\f\\S]{0,2200}': 'XXXXXXX'`` + ``'-----BEGIN OPENSSH PRIVATE KEY-----[\\r\\n\\t\\f\\S]{0,2200}': 'XXXXXXX'`` + ``'ssh-rsa .* ': 'ssh-rsa XXXXXXX '`` + ``'ssh-dss .* ': 'ssh-dss XXXXXXX '`` + + DB + -- + + ``'DB_PASS.*': 'DB_PASS = XXXXXXX'`` + ``'5432:*:*:.*': '5432:*:XXXXXXX'`` + ``"'PASSWORD': .*": "'PASSWORD': 'XXXXXXX',"`` + ``" PASSWORD '.*'": " PASSWORD 'XXXXXXX'"`` + ``'PGPASSWORD=.* ': 'PGPASSWORD=XXXXXXX'`` + ``"_replication password '.*'": "_replication password 'XXXXXXX'"`` + + OTHER + ----- + + ``'EMAIL_HOST_PASSWORD =.*': 'EMAIL_HOST_PASSWORD =XXXXXXX'`` + ``"net ads join -U '.*@MFCFADS.MATH.EXAMPLE.CA.* ": "net ads join -U '.*@MFCFADS.MATH.EXAMPLE.CA%XXXXXXX "`` + ``"net ads join -U '.*@NEXUS.EXAMPLE.CA.* ": "net ads join -U '.*@NEXUS.EXAMPLE.CA%XXXXXXX "`` + ``'install-uptrack .* --autoinstall': 'install-uptrack XXXXXXX --autoinstall'`` + ``'accesskey = .*': 'accesskey = XXXXXXX'`` + ``'auth_pass .*': 'auth_pass XXXXXXX'`` + ``'PSK "0x.*': 'PSK "0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'`` + ``'SECRET_KEY.*': 'SECRET_KEY = XXXXXXX'`` + ``"password=.*": "password=XXXXXXX"`` + ``'.*': 'XXXXXXX'`` + ``'.*': 'XXXXXXX'`` + ``'application.secret = ".*"': 'application.secret = "XXXXXXX"'`` + ``'url = "postgres://.*"': 'url = "postgres://XXXXXXX"'`` + ``'PASS_.*_PASS': 'PASS_XXXXXXX_PASS'`` + + HTACCESS + -------- + + ``':{PLAIN}.*': ':{PLAIN}XXXXXXX'`` ''' # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import re + import logging +import re # Import Salt libs import salt.utils.files @@ -226,7 +238,7 @@ import salt.utils.stringutils import salt.utils.templates as tpl import salt.utils.yaml -__virtualname__ = 'highstate_doc' +__virtualname__ = "highstate_doc" log = logging.getLogger(__name__) @@ -244,7 +256,8 @@ markdown_basic_jinja_template_txt = """ {%- endfor %} """ -markdown_default_jinja_template_txt = """ +markdown_default_jinja_template_txt = ( + """ Configuration Managment =============================================================================== @@ -266,10 +279,14 @@ The following sections are a rendered view of what the configuration management controlled on this system. Each item is handled in order from top to bottom unless some requisites like `require` force other ordering. -""" + markdown_basic_jinja_template_txt +""" + + markdown_basic_jinja_template_txt +) -markdown_advanced_jinja_template_txt = markdown_default_jinja_template_txt + """ +markdown_advanced_jinja_template_txt = ( + markdown_default_jinja_template_txt + + """ {% if vars.get('doc_other', True) -%} Other information @@ -333,50 +350,51 @@ salt grain: hwaddr_interfaces {% endif %} """ +) def markdown_basic_jinja_template(**kwargs): - ''' + """ Return text for a simple markdown jinja template This function can be used from the `highstate_doc.render` modules `jinja_template_function` option. - ''' + """ return markdown_basic_jinja_template_txt def markdown_default_jinja_template(**kwargs): - ''' + """ Return text for a markdown jinja template that included a header This function can be used from the `highstate_doc.render` modules `jinja_template_function` option. - ''' + """ return markdown_default_jinja_template_txt def markdown_full_jinja_template(**kwargs): - ''' + """ Return text for an advanced markdown jinja template This function can be used from the `highstate_doc.render` modules `jinja_template_function` option. - ''' + """ return markdown_advanced_jinja_template_txt def _get_config(**kwargs): - ''' + """ Return configuration - ''' + """ config = { - 'filter_id_regex': ['.*!doc_skip'], - 'filter_function_regex': [], - 'replace_text_regex': {}, - 'proccesser': 'highstate_doc.proccesser_markdown', - 'max_render_file_size': 10000, - 'note': None + "filter_id_regex": [".*!doc_skip"], + "filter_function_regex": [], + "replace_text_regex": {}, + "proccesser": "highstate_doc.proccesser_markdown", + "max_render_file_size": 10000, + "note": None, } - if '__salt__' in globals(): - config_key = '{0}.config'.format(__virtualname__) - config.update(__salt__['config.get'](config_key, {})) + if "__salt__" in globals(): + config_key = "{0}.config".format(__virtualname__) + config.update(__salt__["config.get"](config_key, {})) # pylint: disable=C0201 for k in set(config.keys()) & set(kwargs.keys()): config[k] = kwargs[k] @@ -384,7 +402,7 @@ def _get_config(**kwargs): def read_file(name): - ''' + """ output the contents of a file: this is a workaround if the cp.push module does not work. @@ -410,10 +428,10 @@ def read_file(name): with open(m + '/README.md','wb') as fin: fin.write(d) print('ADDED: ' + m + '/README.md') - ''' - out = '' + """ + out = "" try: - with salt.utils.files.fopen(name, 'r') as f: + with salt.utils.files.fopen(name, "r") as f: out = salt.utils.stringutils.to_unicode(f.read()) except Exception as ex: # pylint: disable=broad-except log.error(ex) @@ -421,77 +439,87 @@ def read_file(name): return out -def render(jinja_template_text=None, jinja_template_function='highstate_doc.markdown_default_jinja_template', **kwargs): - ''' +def render( + jinja_template_text=None, + jinja_template_function="highstate_doc.markdown_default_jinja_template", + **kwargs +): + """ Render highstate to a text format (default Markdown) if `jinja_template_text` is not set, `jinja_template_function` is used. jinja_template_text: jinja text that the render uses to create the document. jinja_template_function: a salt module call that returns template text. - options: - highstate_doc.markdown_basic_jinja_template - highstate_doc.markdown_default_jinja_template - highstate_doc.markdown_full_jinja_template - ''' + + :options: + highstate_doc.markdown_basic_jinja_template + highstate_doc.markdown_default_jinja_template + highstate_doc.markdown_full_jinja_template + + """ config = _get_config(**kwargs) lowstates = proccess_lowstates(**kwargs) # TODO: __env__, context = { - 'saltenv': None, - 'config': config, - 'lowstates': lowstates, - 'salt': __salt__, - 'pillar': __pillar__, - 'grains': __grains__, - 'opts': __opts__, - 'kwargs': kwargs, + "saltenv": None, + "config": config, + "lowstates": lowstates, + "salt": __salt__, + "pillar": __pillar__, + "grains": __grains__, + "opts": __opts__, + "kwargs": kwargs, } template_text = jinja_template_text if template_text is None and jinja_template_function: template_text = __salt__[jinja_template_function](**kwargs) if template_text is None: - raise Exception('No jinja template text') + raise Exception("No jinja template text") txt = tpl.render_jinja_tmpl(template_text, context, tmplpath=None) # after proccessing the template replace passwords or other data. - rt = config.get('replace_text_regex') + rt = config.get("replace_text_regex") for r in rt: txt = re.sub(r, rt[r], txt) return txt def _blacklist_filter(s, config): - ss = s['state'] - sf = s['fun'] - state_function = '{0}.{1}'.format(s['state'], s['fun']) - for b in config['filter_function_regex']: + ss = s["state"] + sf = s["fun"] + state_function = "{0}.{1}".format(s["state"], s["fun"]) + for b in config["filter_function_regex"]: if re.match(b, state_function): return True - for b in config['filter_id_regex']: - if re.match(b, s['__id__']): + for b in config["filter_id_regex"]: + if re.match(b, s["__id__"]): return True return False def proccess_lowstates(**kwargs): - ''' + """ return proccessed lowstate data that was not blacklisted render_module_function is used to provide your own. defaults to from_lowstate - ''' + """ states = [] config = _get_config(**kwargs) - proccesser = config.get('proccesser') - ls = __salt__['state.show_lowstate']() + proccesser = config.get("proccesser") + ls = __salt__["state.show_lowstate"]() if not isinstance(ls, list): - raise Exception('ERROR: to see details run: [salt-call state.show_lowstate] <-----***-SEE-***') + raise Exception( + "ERROR: to see details run: [salt-call state.show_lowstate] <-----***-SEE-***" + ) else: if len(ls) > 0: if not isinstance(ls[0], dict): - raise Exception('ERROR: to see details run: [salt-call state.show_lowstate] <-----***-SEE-***') + raise Exception( + "ERROR: to see details run: [salt-call state.show_lowstate] <-----***-SEE-***" + ) for s in ls: if _blacklist_filter(s, config): @@ -502,13 +530,26 @@ def proccess_lowstates(**kwargs): def _state_data_to_yaml_string(data, whitelist=None, blacklist=None): - ''' + """ return a data dict in yaml string format. - ''' + """ y = {} if blacklist is None: # TODO: use salt defined STATE_REQUISITE_IN_KEYWORDS STATE_RUNTIME_KEYWORDS STATE_INTERNAL_KEYWORDS - blacklist = ['__env__', '__id__', '__sls__', 'fun', 'name', 'context', 'order', 'state', 'require', 'require_in', 'watch', 'watch_in'] + blacklist = [ + "__env__", + "__id__", + "__sls__", + "fun", + "name", + "context", + "order", + "state", + "require", + "require_in", + "watch", + "watch_in", + ] kset = set(data.keys()) if blacklist: kset -= set(blacklist) @@ -522,69 +563,75 @@ def _state_data_to_yaml_string(data, whitelist=None, blacklist=None): def _md_fix(text): - ''' + """ sanitize text data that is to be displayed in a markdown code block - ''' - return text.replace('```', '``[`][markdown parse fix]') + """ + return text.replace("```", "``[`][markdown parse fix]") def _format_markdown_system_file(filename, config): - ret = '' - file_stats = __salt__['file.stats'](filename) - y = _state_data_to_yaml_string(file_stats, whitelist=['user', 'group', 'mode', 'uid', 'gid', 'size']) + ret = "" + file_stats = __salt__["file.stats"](filename) + y = _state_data_to_yaml_string( + file_stats, whitelist=["user", "group", "mode", "uid", "gid", "size"] + ) if y: - ret += 'file stat {1}\n```\n{0}```\n'.format(y, filename) - file_size = file_stats.get('size') - if file_size <= config.get('max_render_file_size'): + ret += "file stat {1}\n```\n{0}```\n".format(y, filename) + file_size = file_stats.get("size") + if file_size <= config.get("max_render_file_size"): is_binary = True try: # TODO: this is linux only should find somthing portable - file_type = __salt__['cmd.shell']('\\file -i \'{0}\''.format(filename)) - if 'charset=binary' not in file_type: + file_type = __salt__["cmd.shell"]("\\file -i '{0}'".format(filename)) + if "charset=binary" not in file_type: is_binary = False except Exception as ex: # pylint: disable=broad-except # likely on a windows system, set as not binary for now. is_binary = False if is_binary: - file_data = '[[skipped binary data]]' + file_data = "[[skipped binary data]]" else: - with salt.utils.files.fopen(filename, 'r') as f: + with salt.utils.files.fopen(filename, "r") as f: file_data = salt.utils.stringutils.to_unicode(f.read()) file_data = _md_fix(file_data) - ret += 'file data {1}\n```\n{0}\n```\n'.format(file_data, filename) + ret += "file data {1}\n```\n{0}\n```\n".format(file_data, filename) else: - ret += '```\n{0}\n```\n'.format('SKIPPED LARGE FILE!\nSet {0}:max_render_file_size > {1} to render.'.format('{0}.config'.format(__virtualname__), file_size)) + ret += "```\n{0}\n```\n".format( + "SKIPPED LARGE FILE!\nSet {0}:max_render_file_size > {1} to render.".format( + "{0}.config".format(__virtualname__), file_size + ) + ) return ret def _format_markdown_link(name): link = name - symbals = '~`!@#$%^&*()+={}[]:;"<>,.?/|\'\\' + symbals = "~`!@#$%^&*()+={}[]:;\"<>,.?/|'\\" for s in symbals: - link = link.replace(s, '') - link = link.replace(' ', '-') + link = link.replace(s, "") + link = link.replace(" ", "-") return link def _format_markdown_requisite(state, stateid, makelink=True): - ''' + """ format requisite as a link users can click - ''' - fmt_id = '{0}: {1}'.format(state, stateid) + """ + fmt_id = "{0}: {1}".format(state, stateid) if makelink: - return ' * [{0}](#{1})\n'.format(fmt_id, _format_markdown_link(fmt_id)) + return " * [{0}](#{1})\n".format(fmt_id, _format_markdown_link(fmt_id)) else: - return ' * `{0}`\n'.format(fmt_id) + return " * `{0}`\n".format(fmt_id) def proccesser_markdown(lowstate_item, config, **kwargs): - ''' + """ Takes low state data and returns a dict of proccessed data that is by default used in a jinja template when rendering a markdown highstate_doc. This `lowstate_item_markdown` given a lowstate item, returns a dict like: - .. code-block:: yaml + .. code-block:: none vars: # the raw lowstate_item that was proccessed id: # the 'id' of the state. @@ -597,96 +644,102 @@ def proccesser_markdown(lowstate_item, config, **kwargs): requisites: # requisite like [watch_in, require_in] details: # state name, parameters and other details like file contents - ''' + """ # TODO: switch or ... ext call. s = lowstate_item - state_function = '{0}.{1}'.format(s['state'], s['fun']) - id_full = '{0}: {1}'.format(s['state'], s['__id__']) + state_function = "{0}.{1}".format(s["state"], s["fun"]) + id_full = "{0}: {1}".format(s["state"], s["__id__"]) # TODO: use salt defined STATE_REQUISITE_IN_KEYWORDS - requisites = '' - if s.get('watch'): - requisites += 'run or update after changes in:\n' - for w in s.get('watch', []): + requisites = "" + if s.get("watch"): + requisites += "run or update after changes in:\n" + for w in s.get("watch", []): requisites += _format_markdown_requisite(w.items()[0][0], w.items()[0][1]) - requisites += '\n' - if s.get('watch_in'): - requisites += 'after changes, run or update:\n' - for w in s.get('watch_in', []): + requisites += "\n" + if s.get("watch_in"): + requisites += "after changes, run or update:\n" + for w in s.get("watch_in", []): requisites += _format_markdown_requisite(w.items()[0][0], w.items()[0][1]) - requisites += '\n' - if s.get('require') and len(s.get('require')) > 0: - requisites += 'require:\n' - for w in s.get('require', []): + requisites += "\n" + if s.get("require") and len(s.get("require")) > 0: + requisites += "require:\n" + for w in s.get("require", []): requisites += _format_markdown_requisite(w.items()[0][0], w.items()[0][1]) - requisites += '\n' - if s.get('require_in'): - requisites += 'required in:\n' - for w in s.get('require_in', []): + requisites += "\n" + if s.get("require_in"): + requisites += "required in:\n" + for w in s.get("require_in", []): requisites += _format_markdown_requisite(w.items()[0][0], w.items()[0][1]) - requisites += '\n' + requisites += "\n" - details = '' + details = "" - if state_function == 'highstate_doc.note': - if 'contents' in s: - details += '\n{0}\n'.format(s['contents']) - if 'source' in s: - text = __salt__['cp.get_file_str'](s['source']) + if state_function == "highstate_doc.note": + if "contents" in s: + details += "\n{0}\n".format(s["contents"]) + if "source" in s: + text = __salt__["cp.get_file_str"](s["source"]) if text: - details += '\n{0}\n'.format(text) + details += "\n{0}\n".format(text) else: - details += '\n{0}\n'.format('ERROR: opening {0}'.format(s['source'])) + details += "\n{0}\n".format("ERROR: opening {0}".format(s["source"])) - if state_function == 'pkg.installed': - pkgs = s.get('pkgs', s.get('name')) - details += '\n```\ninstall: {0}\n```\n'.format(pkgs) + if state_function == "pkg.installed": + pkgs = s.get("pkgs", s.get("name")) + details += "\n```\ninstall: {0}\n```\n".format(pkgs) - if state_function == 'file.recurse': - details += '''recurse copy of files\n''' + if state_function == "file.recurse": + details += """recurse copy of files\n""" y = _state_data_to_yaml_string(s) if y: - details += '```\n{0}\n```\n'.format(y) - if '!doc_recurse' in id_full: - findfiles = __salt__['file.find'](path=s.get('name'), type='f') - if len(findfiles) < 10 or '!doc_recurse_force' in id_full: + details += "```\n{0}\n```\n".format(y) + if "!doc_recurse" in id_full: + findfiles = __salt__["file.find"](path=s.get("name"), type="f") + if len(findfiles) < 10 or "!doc_recurse_force" in id_full: for f in findfiles: details += _format_markdown_system_file(f, config) else: - details += ''' > Skipping because more than 10 files to display.\n''' - details += ''' > HINT: to force include !doc_recurse_force in state id.\n''' + details += """ > Skipping because more than 10 files to display.\n""" + details += ( + """ > HINT: to force include !doc_recurse_force in state id.\n""" + ) else: - details += ''' > For more details review logs and Salt state files.\n\n''' - details += ''' > HINT: for improved docs use multiple file.managed states or file.archive, git.latest. etc.\n''' - details += ''' > HINT: to force doc to show all files in path add !doc_recurse .\n''' + details += """ > For more details review logs and Salt state files.\n\n""" + details += """ > HINT: for improved docs use multiple file.managed states or file.archive, git.latest. etc.\n""" + details += """ > HINT: to force doc to show all files in path add !doc_recurse .\n""" - if state_function == 'file.blockreplace': - if s.get('content'): - details += 'ensure block of content is in file\n```\n{0}\n```\n'.format(_md_fix(s['content'])) - if s.get('source'): - text = '** source: ' + s.get('source') - details += 'ensure block of content is in file\n```\n{0}\n```\n'.format(_md_fix(text)) + if state_function == "file.blockreplace": + if s.get("content"): + details += "ensure block of content is in file\n```\n{0}\n```\n".format( + _md_fix(s["content"]) + ) + if s.get("source"): + text = "** source: " + s.get("source") + details += "ensure block of content is in file\n```\n{0}\n```\n".format( + _md_fix(text) + ) - if state_function == 'file.managed': - details += _format_markdown_system_file(s['name'], config) + if state_function == "file.managed": + details += _format_markdown_system_file(s["name"], config) # if no state doc is created use default state as yaml if len(details) == 0: y = _state_data_to_yaml_string(s) if y: - details += '```\n{0}```\n'.format(y) + details += "```\n{0}```\n".format(y) r = { - 'vars': lowstate_item, - 'state': s['state'], - 'name': s['name'], - 'function': s['fun'], - 'id': s['__id__'], - 'id_full': id_full, - 'state_function': state_function, - 'markdown': { - 'requisites': requisites.decode('utf-8'), - 'details': details.decode('utf-8') - } + "vars": lowstate_item, + "state": s["state"], + "name": s["name"], + "function": s["fun"], + "id": s["__id__"], + "id_full": id_full, + "state_function": state_function, + "markdown": { + "requisites": requisites.decode("utf-8"), + "details": details.decode("utf-8"), + }, } return r diff --git a/salt/modules/hosts.py b/salt/modules/hosts.py index e4dd1ae479f..e9e9fa7c97a 100644 --- a/salt/modules/hosts.py +++ b/salt/modules/hosts.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Manage the information in the hosts file -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno +import logging import os # Import salt libs @@ -15,42 +17,45 @@ import salt.utils.stringutils # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext.six.moves import range + +log = logging.getLogger(__name__) # pylint: disable=C0103 def __get_hosts_filename(): - ''' + """ Return the path to the appropriate hosts file - ''' + """ try: - return __context__['hosts.__get_hosts_filename'] + return __context__["hosts.__get_hosts_filename"] except KeyError: - __context__['hosts.__get_hosts_filename'] = \ - __salt__['config.option']('hosts.file') - return __context__['hosts.__get_hosts_filename'] + __context__["hosts.__get_hosts_filename"] = __salt__["config.option"]( + "hosts.file" + ) + return __context__["hosts.__get_hosts_filename"] def _get_or_create_hostfile(): - ''' + """ Wrapper of __get_hosts_filename but create host file if it does not exist. - ''' + """ hfn = __get_hosts_filename() if hfn is None: - hfn = '' + hfn = "" if not os.path.exists(hfn): - with salt.utils.files.fopen(hfn, 'w'): + with salt.utils.files.fopen(hfn, "w"): pass return hfn def _list_hosts(): - ''' + """ Return the hosts found in the hosts file in as an OrderedDict - ''' + """ try: - return __context__['hosts._list_hosts'] + return __context__["hosts._list_hosts"] except KeyError: count = 0 hfn = __get_hosts_filename() @@ -61,27 +66,33 @@ def _list_hosts(): line = salt.utils.stringutils.to_unicode(line).strip() if not line: continue - if line.startswith('#'): - ret.setdefault('comment-{0}'.format(count), []).append(line) + if line.startswith("#"): + ret.setdefault("comment-{0}".format(count), []).append(line) count += 1 continue - if '#' in line: - line = line[:line.index('#')].strip() + comment = None + if "#" in line: + comment = line[line.index("#") + 1 :].lstrip() + line = line[: line.index("#")].strip() comps = line.split() ip = comps.pop(0) - ret.setdefault(ip, []).extend(comps) + if comment: + ret.setdefault(ip, {}).setdefault("aliases", []).extend(comps) + ret.setdefault(ip, {}).update({"comment": comment}) + else: + ret.setdefault(ip, {}).setdefault("aliases", []).extend(comps) except (IOError, OSError) as exc: salt.utils.files.process_read_exception(exc, hfn, ignore=errno.ENOENT) # Don't set __context__ since we weren't able to read from the # hosts file. return ret - __context__['hosts._list_hosts'] = ret + __context__["hosts._list_hosts"] = ret return ret def list_hosts(): - ''' + """ Return the hosts found in the hosts file in this format:: {'': ['alias1', 'alias2', ...]} @@ -91,13 +102,13 @@ def list_hosts(): .. code-block:: bash salt '*' hosts.list_hosts - ''' + """ # msgpack does not like OrderedDict's return dict(_list_hosts()) def get_ip(host): - ''' + """ Return the ip associated with the named host CLI Example: @@ -105,20 +116,22 @@ def get_ip(host): .. code-block:: bash salt '*' hosts.get_ip - ''' + """ hosts = _list_hosts() if not hosts: - return '' + return "" # Look for the op for addr in hosts: - if host in hosts[addr]: - return addr + if isinstance(hosts[addr], dict) and "aliases" in hosts[addr]: + _hosts = hosts[addr]["aliases"] + if host in _hosts: + return addr # ip not found - return '' + return "" def get_alias(ip): - ''' + """ Return the list of aliases associated with an ip Aliases (host names) are returned in the order in which they @@ -130,15 +143,15 @@ def get_alias(ip): .. code-block:: bash salt '*' hosts.get_alias - ''' + """ hosts = _list_hosts() - if ip in hosts: - return hosts[ip] + if ip in list(hosts): + return hosts[ip]["aliases"] return [] def has_pair(ip, alias): - ''' + """ Return true if the alias is set CLI Example: @@ -146,16 +159,19 @@ def has_pair(ip, alias): .. code-block:: bash salt '*' hosts.has_pair - ''' + """ hosts = _list_hosts() try: - return alias in hosts[ip] + if isinstance(alias, list): + return set(alias).issubset(hosts[ip]["aliases"]) + else: + return alias in hosts[ip]["aliases"] except KeyError: return False -def set_host(ip, alias): - ''' +def set_host(ip, alias, comment=None): + """ Set the host entry in the hosts file for the given ip, this will overwrite any previous entry for the given ip @@ -169,29 +185,32 @@ def set_host(ip, alias): .. code-block:: bash salt '*' hosts.set_host - ''' + """ hfn = _get_or_create_hostfile() ovr = False if not os.path.isfile(hfn): return False # Make sure future calls to _list_hosts() will re-read the file - __context__.pop('hosts._list_hosts', None) + __context__.pop("hosts._list_hosts", None) - line_to_add = salt.utils.stringutils.to_bytes( - ip + '\t\t' + alias + os.linesep - ) + if comment: + line_to_add = salt.utils.stringutils.to_bytes( + ip + "\t\t" + alias + "\t\t# " + comment + os.linesep + ) + else: + line_to_add = salt.utils.stringutils.to_bytes(ip + "\t\t" + alias + os.linesep) # support removing a host entry by providing an empty string if not alias.strip(): - line_to_add = b'' + line_to_add = b"" - with salt.utils.files.fopen(hfn, 'rb') as fp_: + with salt.utils.files.fopen(hfn, "rb") as fp_: lines = fp_.readlines() for ind, _ in enumerate(lines): tmpline = lines[ind].strip() if not tmpline: continue - if tmpline.startswith(b'#'): + if tmpline.startswith(b"#"): continue comps = tmpline.split() if comps[0] == salt.utils.stringutils.to_bytes(ip): @@ -199,7 +218,7 @@ def set_host(ip, alias): lines[ind] = line_to_add ovr = True else: # remove other entries - lines[ind] = b'' + lines[ind] = b"" linesep_bytes = salt.utils.stringutils.to_bytes(os.linesep) if not ovr: # make sure there is a newline @@ -207,13 +226,13 @@ def set_host(ip, alias): lines[-1] += linesep_bytes line = line_to_add lines.append(line) - with salt.utils.files.fopen(hfn, 'wb') as ofile: + with salt.utils.files.fopen(hfn, "wb") as ofile: ofile.writelines(lines) return True def rm_host(ip, alias): - ''' + """ Remove a host entry from the hosts file CLI Example: @@ -221,42 +240,58 @@ def rm_host(ip, alias): .. code-block:: bash salt '*' hosts.rm_host - ''' + """ if not has_pair(ip, alias): return True # Make sure future calls to _list_hosts() will re-read the file - __context__.pop('hosts._list_hosts', None) + __context__.pop("hosts._list_hosts", None) hfn = _get_or_create_hostfile() - with salt.utils.files.fopen(hfn, 'rb') as fp_: + with salt.utils.files.fopen(hfn, "rb") as fp_: lines = fp_.readlines() for ind, _ in enumerate(lines): tmpline = lines[ind].strip() if not tmpline: continue - if tmpline.startswith(b'#'): + if tmpline.startswith(b"#"): continue comps = tmpline.split() + comment = None + if b"#" in tmpline: + host_info, comment = tmpline.split("#") + comment = salt.utils.stringutils.to_bytes(comment).lstrip() + else: + host_info = tmpline + host_info = salt.utils.stringutils.to_bytes(host_info) + comps = host_info.split() b_ip = salt.utils.stringutils.to_bytes(ip) b_alias = salt.utils.stringutils.to_bytes(alias) if comps[0] == b_ip: - newline = comps[0] + b'\t\t' + newline = comps[0] + b"\t\t" for existing in comps[1:]: if existing == b_alias: continue - newline += existing + b' ' + newline += existing + b" " if newline.strip() == b_ip: # No aliases exist for the line, make it empty - lines[ind] = b'' + lines[ind] = b"" else: # Only an alias was removed - lines[ind] = newline + salt.utils.stringutils.to_bytes(os.linesep) - with salt.utils.files.fopen(hfn, 'wb') as ofile: + if comment: + lines[ind] = ( + newline + + b"# " + + comment + + salt.utils.stringutils.to_bytes(os.linesep) + ) + else: + lines[ind] = newline + salt.utils.stringutils.to_bytes(os.linesep) + with salt.utils.files.fopen(hfn, "wb") as ofile: ofile.writelines(lines) return True def add_host(ip, alias): - ''' + """ Add a host to an existing entry, if the entry is not in place then create it with the given host @@ -265,7 +300,7 @@ def add_host(ip, alias): .. code-block:: bash salt '*' hosts.add_host - ''' + """ hfn = _get_or_create_hostfile() if not os.path.isfile(hfn): return False @@ -276,39 +311,79 @@ def add_host(ip, alias): hosts = _list_hosts() # Make sure future calls to _list_hosts() will re-read the file - __context__.pop('hosts._list_hosts', None) + __context__.pop("hosts._list_hosts", None) inserted = False for i, h in six.iteritems(hosts): for j in range(len(h)): - if h[j].startswith('#') and i == ip: - h.insert(j, alias) - inserted = True + if isinstance(h, list): + if h[j].startswith("#") and i == ip: + h.insert(j, alias) + inserted = True if not inserted: - hosts.setdefault(ip, []).append(alias) + hosts.setdefault(ip, {}).setdefault("aliases", []).append(alias) _write_hosts(hosts) return True +def set_comment(ip, comment): + """ + Set the comment for a host to an existing entry, + if the entry is not in place then return False + + CLI Example: + + .. code-block:: bash + + salt '*' hosts.set_comment + """ + hfn = _get_or_create_hostfile() + if not os.path.isfile(hfn): + return False + + hosts = _list_hosts() + + # Make sure future calls to _list_hosts() will re-read the file + __context__.pop("hosts._list_hosts", None) + + if ip not in hosts: + return False + + if "comment" in hosts[ip]: + if comment != hosts[ip]["comment"]: + hosts[ip]["comment"] = comment + _write_hosts(hosts) + else: + return True + else: + hosts[ip]["comment"] = comment + _write_hosts(hosts) + return True + + def _write_hosts(hosts): lines = [] - for ip, aliases in six.iteritems(hosts): + for ip, host_info in six.iteritems(hosts): if ip: - if ip.startswith('comment'): - line = ''.join(aliases) + if ip.startswith("comment"): + line = "".join(host_info) else: - line = '{0}\t\t{1}'.format( - ip, - ' '.join(aliases) + if "comment" in host_info: + line = "{0}\t\t{1}\t\t# {2}".format( + ip, " ".join(host_info["aliases"]), host_info["comment"] ) + else: + line = "{0}\t\t{1}".format(ip, " ".join(host_info["aliases"])) lines.append(line) hfn = _get_or_create_hostfile() - with salt.utils.files.fopen(hfn, 'w+') as ofile: + with salt.utils.files.fopen(hfn, "w+") as ofile: for line in lines: if line.strip(): # /etc/hosts needs to end with a newline so that some utils # that read it do not break - ofile.write(salt.utils.stringutils.to_str( - line.strip() + six.text_type(os.linesep) - )) + ofile.write( + salt.utils.stringutils.to_str( + line.strip() + six.text_type(os.linesep) + ) + ) diff --git a/salt/modules/http.py b/salt/modules/http.py index 61082995b76..61bcf12c15e 100644 --- a/salt/modules/http.py +++ b/salt/modules/http.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Module for making various web calls. Primarily designed for webhooks and the like, but also useful for basic http testing. .. versionadded:: 2015.5.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import time # Import Salt libs @@ -19,7 +20,7 @@ from salt.ext import six def query(url, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 Query a resource, and decode the return data @@ -42,11 +43,11 @@ def query(url, **kwargs): params='key1=val1&key2=val2' salt '*' http.query http://somelink.com/ method=POST \ data='somecontent' - ''' + """ opts = __opts__.copy() - if 'opts' in kwargs: - opts.update(kwargs['opts']) - del kwargs['opts'] + if "opts" in kwargs: + opts.update(kwargs["opts"]) + del kwargs["opts"] try: return salt.utils.http.query(url=url, opts=opts, **kwargs) @@ -55,7 +56,7 @@ def query(url, **kwargs): def wait_for_successful_query(url, wait_for=300, **kwargs): - ''' + """ Query a resource until a successful response, and decode the return data CLI Example: @@ -63,7 +64,7 @@ def wait_for_successful_query(url, wait_for=300, **kwargs): .. code-block:: bash salt '*' http.wait_for_successful_query http://somelink.com/ wait_for=160 request_interval=1 - ''' + """ starttime = time.time() @@ -72,7 +73,7 @@ def wait_for_successful_query(url, wait_for=300, **kwargs): result = None try: result = query(url=url, **kwargs) - if not result.get('Error') and not result.get('error'): + if not result.get("Error") and not result.get("error"): return result except Exception as exc: # pylint: disable=broad-except caught_exception = exc @@ -83,13 +84,13 @@ def wait_for_successful_query(url, wait_for=300, **kwargs): raise caught_exception # pylint: disable=E0702 return result - elif 'request_interval' in kwargs: + elif "request_interval" in kwargs: # Space requests out by delaying for an interval - time.sleep(kwargs['request_interval']) + time.sleep(kwargs["request_interval"]) def update_ca_bundle(target=None, source=None, merge_files=None): - ''' + """ Update the local CA bundle file from a URL .. versionadded:: 2015.5.0 @@ -120,13 +121,11 @@ def update_ca_bundle(target=None, source=None, merge_files=None): .. code-block:: bash salt '*' http.update_ca_bundle merge_files=/path/to/mycert.pem - ''' + """ if target is None: - target = __salt__['config.get']('ca_bundle', None) + target = __salt__["config.get"]("ca_bundle", None) if source is None: - source = __salt__['config.get']('ca_bundle_url', None) + source = __salt__["config.get"]("ca_bundle_url", None) - return salt.utils.http.update_ca_bundle( - target, source, __opts__, merge_files - ) + return salt.utils.http.update_ca_bundle(target, source, __opts__, merge_files) diff --git a/salt/modules/icinga2.py b/salt/modules/icinga2.py index 66cb8841660..21db5fe2d74 100644 --- a/salt/modules/icinga2.py +++ b/salt/modules/icinga2.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide icinga2 compatibility to salt. .. versionadded:: 2017.7.0 :depends: - icinga2 server -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -20,20 +21,20 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if the mysql libraries exist - ''' + """ # TODO: This could work on windows with some love if salt.utils.platform.is_windows(): - return (False, 'The module cannot be loaded on windows.') + return (False, "The module cannot be loaded on windows.") - if salt.utils.path.which('icinga2'): + if salt.utils.path.which("icinga2"): return True - return (False, 'Icinga2 not installed.') + return (False, "Icinga2 not installed.") def generate_ticket(domain): - ''' + """ Generate and save an icinga2 ticket. Returns:: @@ -45,13 +46,15 @@ def generate_ticket(domain): salt '*' icinga2.generate_ticket domain.tld - ''' - result = __salt__['cmd.run_all'](["icinga2", "pki", "ticket", "--cn", domain], python_shell=False) + """ + result = __salt__["cmd.run_all"]( + ["icinga2", "pki", "ticket", "--cn", domain], python_shell=False + ) return result def generate_cert(domain): - ''' + """ Generate an icinga2 client certificate and key. Returns:: @@ -63,13 +66,26 @@ def generate_cert(domain): salt '*' icinga2.generate_cert domain.tld - ''' - result = __salt__['cmd.run_all'](["icinga2", "pki", "new-cert", "--cn", domain, "--key", "{0}{1}.key".format(get_certs_path(), domain), "--cert", "{0}{1}.crt".format(get_certs_path(), domain)], python_shell=False) + """ + result = __salt__["cmd.run_all"]( + [ + "icinga2", + "pki", + "new-cert", + "--cn", + domain, + "--key", + "{0}{1}.key".format(get_certs_path(), domain), + "--cert", + "{0}{1}.crt".format(get_certs_path(), domain), + ], + python_shell=False, + ) return result def save_cert(domain, master): - ''' + """ Save the certificate for master icinga2 node. Returns:: @@ -81,14 +97,28 @@ def save_cert(domain, master): salt '*' icinga2.save_cert domain.tld master.domain.tld - ''' - result = __salt__['cmd.run_all'](["icinga2", "pki", "save-cert", "--key", "{0}{1}.key".format(get_certs_path(), domain), "--cert", "{0}{1}.cert".format(get_certs_path(), domain), "--trustedcert", - "{0}trusted-master.crt".format(get_certs_path()), "--host", master], python_shell=False) + """ + result = __salt__["cmd.run_all"]( + [ + "icinga2", + "pki", + "save-cert", + "--key", + "{0}{1}.key".format(get_certs_path(), domain), + "--cert", + "{0}{1}.cert".format(get_certs_path(), domain), + "--trustedcert", + "{0}trusted-master.crt".format(get_certs_path()), + "--host", + master, + ], + python_shell=False, + ) return result def request_cert(domain, master, ticket, port): - ''' + """ Request CA cert from master icinga2 node. Returns:: @@ -101,14 +131,34 @@ def request_cert(domain, master, ticket, port): salt '*' icinga2.request_cert domain.tld master.domain.tld TICKET_ID - ''' - result = __salt__['cmd.run_all'](["icinga2", "pki", "request", "--host", master, "--port", port, "--ticket", ticket, "--key", "{0}{1}.key".format(get_certs_path(), domain), "--cert", - "{0}{1}.crt".format(get_certs_path(), domain), "--trustedcert", "{0}trusted-master.crt".format(get_certs_path()), "--ca", "{0}ca.crt".format(get_certs_path())], python_shell=False) + """ + result = __salt__["cmd.run_all"]( + [ + "icinga2", + "pki", + "request", + "--host", + master, + "--port", + port, + "--ticket", + ticket, + "--key", + "{0}{1}.key".format(get_certs_path(), domain), + "--cert", + "{0}{1}.crt".format(get_certs_path(), domain), + "--trustedcert", + "{0}trusted-master.crt".format(get_certs_path()), + "--ca", + "{0}ca.crt".format(get_certs_path()), + ], + python_shell=False, + ) return result def node_setup(domain, master, ticket): - ''' + """ Setup the icinga2 node. Returns:: @@ -121,7 +171,23 @@ def node_setup(domain, master, ticket): salt '*' icinga2.node_setup domain.tld master.domain.tld TICKET_ID - ''' - result = __salt__['cmd.run_all'](["icinga2", "node", "setup", "--ticket", ticket, "--endpoint", master, "--zone", domain, "--master_host", master, "--trustedcert", "{0}trusted-master.crt".format(get_certs_path())], - python_shell=False) + """ + result = __salt__["cmd.run_all"]( + [ + "icinga2", + "node", + "setup", + "--ticket", + ticket, + "--endpoint", + master, + "--zone", + domain, + "--master_host", + master, + "--trustedcert", + "{0}trusted-master.crt".format(get_certs_path()), + ], + python_shell=False, + ) return result diff --git a/salt/modules/ifttt.py b/salt/modules/ifttt.py index d5f23b8c0af..f2fe6d4f9fc 100644 --- a/salt/modules/ifttt.py +++ b/salt/modules/ifttt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for IFTTT .. versionadded:: 2015.8.0 @@ -10,10 +10,11 @@ Requires an ``api_key`` in ``/etc/salt/minion``: ifttt: secret_key: '280d4699-a817-4719-ba6f-ca56e573e44f' -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time @@ -25,32 +26,30 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if apache is installed - ''' - if not __salt__['config.get']('ifttt.secret_key') and \ - not __salt__['config.get']('ifttt:secret_key'): - return (False, 'IFTTT Secret Key Unavailable, not loading.') + """ + if not __salt__["config.get"]("ifttt.secret_key") and not __salt__["config.get"]( + "ifttt:secret_key" + ): + return (False, "IFTTT Secret Key Unavailable, not loading.") return True -def _query(event=None, - method='GET', - args=None, - header_dict=None, - data=None): - ''' +def _query(event=None, method="GET", args=None, header_dict=None, data=None): + """ Make a web call to IFTTT. - ''' - secret_key = __salt__['config.get']('ifttt.secret_key') or \ - __salt__['config.get']('ifttt:secret_key') - path = 'https://maker.ifttt.com/trigger/{0}/with/key/{1}'.format(event, secret_key) + """ + secret_key = __salt__["config.get"]("ifttt.secret_key") or __salt__["config.get"]( + "ifttt:secret_key" + ) + path = "https://maker.ifttt.com/trigger/{0}/with/key/{1}".format(event, secret_key) if header_dict is None: - header_dict = {'Content-type': 'application/json'} + header_dict = {"Content-type": "application/json"} - if method != 'POST': - header_dict['Accept'] = 'application/json' + if method != "POST": + header_dict["Accept"] = "application/json" result = salt.utils.http.query( path, @@ -59,43 +58,39 @@ def _query(event=None, data=data, header_dict=header_dict, decode=True, - decode_type='auto', + decode_type="auto", text=True, status=True, cookies=True, persist_session=True, opts=__opts__, - backend='requests' + backend="requests", ) return result def trigger_event(event=None, **kwargs): - ''' + """ Trigger a configured event in IFTTT. :param event: The name of the event to trigger. :return: A dictionary with status, text, and error if result was failure. - ''' + """ - res = {'result': False, 'message': 'Something went wrong'} + res = {"result": False, "message": "Something went wrong"} data = {} - for value in ('value1', 'value2', 'value3', - 'Value1', 'Value2', 'Value3'): + for value in ("value1", "value2", "value3", "Value1", "Value2", "Value3"): if value in kwargs: data[value.lower()] = kwargs[value] - data['occurredat'] = time.strftime("%B %d, %Y %I:%M%p", time.localtime()) - result = _query(event=event, - method='POST', - data=salt.utils.json.dumps(data) - ) - if 'status' in result: - if result['status'] == 200: - res['result'] = True - res['message'] = result['text'] + data["occurredat"] = time.strftime("%B %d, %Y %I:%M%p", time.localtime()) + result = _query(event=event, method="POST", data=salt.utils.json.dumps(data)) + if "status" in result: + if result["status"] == 200: + res["result"] = True + res["message"] = result["text"] else: - if 'error' in result: - res['message'] = result['error'] + if "error" in result: + res["message"] = result["error"] return res diff --git a/salt/modules/ilo.py b/salt/modules/ilo.py index d9a924fd2a0..45745750235 100644 --- a/salt/modules/ilo.py +++ b/salt/modules/ilo.py @@ -1,75 +1,80 @@ # -*- coding: utf-8 -*- -''' +""" Manage HP ILO :depends: hponcfg (SmartStart Scripting Toolkit Linux Edition) -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import tempfile +import salt.utils.path + # Import Salt libs from salt._compat import ElementTree as ET from salt.ext import six -import salt.utils.path log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Make sure hponcfg tool is accessible - ''' - if salt.utils.path.which('hponcfg'): + """ + if salt.utils.path.which("hponcfg"): return True - return (False, 'ilo execution module not loaded: the hponcfg binary is not in the path.') + return ( + False, + "ilo execution module not loaded: the hponcfg binary is not in the path.", + ) def __execute_cmd(name, xml): - ''' + """ Execute ilom commands - ''' - ret = {name.replace('_', ' '): {}} + """ + ret = {name.replace("_", " "): {}} id_num = 0 - tmp_dir = os.path.join(__opts__['cachedir'], 'tmp') + tmp_dir = os.path.join(__opts__["cachedir"], "tmp") if not os.path.isdir(tmp_dir): os.mkdir(tmp_dir) - with tempfile.NamedTemporaryFile(dir=tmp_dir, - prefix=name + six.text_type(os.getpid()), - suffix='.xml', - mode='w', - delete=False) as fh: + with tempfile.NamedTemporaryFile( + dir=tmp_dir, + prefix=name + six.text_type(os.getpid()), + suffix=".xml", + mode="w", + delete=False, + ) as fh: tmpfilename = fh.name fh.write(xml) - cmd = __salt__['cmd.run_all']('hponcfg -f {0}'.format(tmpfilename)) + cmd = __salt__["cmd.run_all"]("hponcfg -f {0}".format(tmpfilename)) # Clean up the temp file - __salt__['file.remove'](tmpfilename) + __salt__["file.remove"](tmpfilename) - if cmd['retcode'] != 0: - for i in cmd['stderr'].splitlines(): - if i.startswith(' MESSAGE='): - return {'Failed': i.split('=')[-1]} + if cmd["retcode"] != 0: + for i in cmd["stderr"].splitlines(): + if i.startswith(" MESSAGE="): + return {"Failed": i.split("=")[-1]} return False try: - for i in ET.fromstring(''.join(cmd['stdout'].splitlines()[3:-1])): + for i in ET.fromstring("".join(cmd["stdout"].splitlines()[3:-1])): # Make sure dict keys don't collide - if ret[name.replace('_', ' ')].get(i.tag, False): - ret[name.replace('_', ' ')].update( - {i.tag + '_' + six.text_type(id_num): i.attrib} + if ret[name.replace("_", " ")].get(i.tag, False): + ret[name.replace("_", " ")].update( + {i.tag + "_" + six.text_type(id_num): i.attrib} ) id_num += 1 else: - ret[name.replace('_', ' ')].update( - {i.tag: i.attrib} - ) + ret[name.replace("_", " ")].update({i.tag: i.attrib}) except SyntaxError: return True @@ -77,7 +82,7 @@ def __execute_cmd(name, xml): def global_settings(): - ''' + """ Show global settings CLI Example: @@ -85,7 +90,7 @@ def global_settings(): .. code-block:: bash salt '*' ilo.global_settings - ''' + """ _xml = """ @@ -95,11 +100,11 @@ def global_settings(): """ - return __execute_cmd('Global_Settings', _xml) + return __execute_cmd("Global_Settings", _xml) def set_http_port(port=80): - ''' + """ Configure the port HTTP should listen on CLI Example: @@ -107,10 +112,10 @@ def set_http_port(port=80): .. code-block:: bash salt '*' ilo.set_http_port 8080 - ''' + """ _current = global_settings() - if _current['Global Settings']['HTTP_PORT']['VALUE'] == port: + if _current["Global Settings"]["HTTP_PORT"]["VALUE"] == port: return True _xml = """ @@ -121,13 +126,15 @@ def set_http_port(port=80): - """.format(port) + """.format( + port + ) - return __execute_cmd('Set_HTTP_Port', _xml) + return __execute_cmd("Set_HTTP_Port", _xml) def set_https_port(port=443): - ''' + """ Configure the port HTTPS should listen on CLI Example: @@ -135,10 +142,10 @@ def set_https_port(port=443): .. code-block:: bash salt '*' ilo.set_https_port 4334 - ''' + """ _current = global_settings() - if _current['Global Settings']['HTTP_PORT']['VALUE'] == port: + if _current["Global Settings"]["HTTP_PORT"]["VALUE"] == port: return True _xml = """ @@ -149,13 +156,15 @@ def set_https_port(port=443): - """.format(port) + """.format( + port + ) - return __execute_cmd('Set_HTTPS_Port', _xml) + return __execute_cmd("Set_HTTPS_Port", _xml) def enable_ssh(): - ''' + """ Enable the SSH daemon CLI Example: @@ -163,10 +172,10 @@ def enable_ssh(): .. code-block:: bash salt '*' ilo.enable_ssh - ''' + """ _current = global_settings() - if _current['Global Settings']['SSH_STATUS']['VALUE'] == 'Y': + if _current["Global Settings"]["SSH_STATUS"]["VALUE"] == "Y": return True _xml = """ @@ -179,11 +188,11 @@ def enable_ssh(): """ - return __execute_cmd('Enable_SSH', _xml) + return __execute_cmd("Enable_SSH", _xml) def disable_ssh(): - ''' + """ Disable the SSH daemon CLI Example: @@ -191,10 +200,10 @@ def disable_ssh(): .. code-block:: bash salt '*' ilo.disable_ssh - ''' + """ _current = global_settings() - if _current['Global Settings']['SSH_STATUS']['VALUE'] == 'N': + if _current["Global Settings"]["SSH_STATUS"]["VALUE"] == "N": return True _xml = """ @@ -207,11 +216,11 @@ def disable_ssh(): """ - return __execute_cmd('Disable_SSH', _xml) + return __execute_cmd("Disable_SSH", _xml) def set_ssh_port(port=22): - ''' + """ Enable SSH on a user defined port CLI Example: @@ -219,10 +228,10 @@ def set_ssh_port(port=22): .. code-block:: bash salt '*' ilo.set_ssh_port 2222 - ''' + """ _current = global_settings() - if _current['Global Settings']['SSH_PORT']['VALUE'] == port: + if _current["Global Settings"]["SSH_PORT"]["VALUE"] == port: return True _xml = """ @@ -233,13 +242,15 @@ def set_ssh_port(port=22): - """.format(port) + """.format( + port + ) - return __execute_cmd('Configure_SSH_Port', _xml) + return __execute_cmd("Configure_SSH_Port", _xml) def set_ssh_key(public_key): - ''' + """ Configure SSH public keys for specific users CLI Example: @@ -250,7 +261,7 @@ def set_ssh_key(public_key): The SSH public key needs to be DSA and the last argument in the key needs to be the username (case-senstive) of the ILO username. - ''' + """ _xml = """ @@ -261,13 +272,15 @@ def set_ssh_key(public_key): - """.format(public_key) + """.format( + public_key + ) - return __execute_cmd('Import_SSH_Publickey', _xml) + return __execute_cmd("Import_SSH_Publickey", _xml) def delete_ssh_key(username): - ''' + """ Delete a users SSH key from the ILO CLI Example: @@ -275,7 +288,7 @@ def delete_ssh_key(username): .. code-block:: bash salt '*' ilo.delete_user_sshkey damian - ''' + """ _xml = """ @@ -284,13 +297,15 @@ def delete_ssh_key(username): - """.format(username) + """.format( + username + ) - return __execute_cmd('Delete_user_SSH_key', _xml) + return __execute_cmd("Delete_user_SSH_key", _xml) def list_users(): - ''' + """ List all users CLI Example: @@ -298,7 +313,7 @@ def list_users(): .. code-block:: bash salt '*' ilo.list_users - ''' + """ _xml = """ @@ -307,11 +322,11 @@ def list_users(): """ - return __execute_cmd('All_users', _xml) + return __execute_cmd("All_users", _xml) def list_users_info(): - ''' + """ List all users in detail CLI Example: @@ -319,7 +334,7 @@ def list_users_info(): .. code-block:: bash salt '*' ilo.list_users_info - ''' + """ _xml = """ @@ -328,11 +343,11 @@ def list_users_info(): """ - return __execute_cmd('All_users_info', _xml) + return __execute_cmd("All_users_info", _xml) def create_user(name, password, *privileges): - ''' + """ Create user CLI Example: @@ -359,12 +374,14 @@ def create_user(name, password, *privileges): * CONFIG_ILO_PRIV Enables the user to configure iLO settings. - ''' - _priv = ['ADMIN_PRIV', - 'REMOTE_CONS_PRIV', - 'RESET_SERVER_PRIV', - 'VIRTUAL_MEDIA_PRIV', - 'CONFIG_ILO_PRIV'] + """ + _priv = [ + "ADMIN_PRIV", + "REMOTE_CONS_PRIV", + "RESET_SERVER_PRIV", + "VIRTUAL_MEDIA_PRIV", + "CONFIG_ILO_PRIV", + ] _xml = """ @@ -380,13 +397,23 @@ def create_user(name, password, *privileges): - """.format(name, password, '\n'.join(['<{0} value="Y" />'.format(i.upper()) for i in privileges if i.upper() in _priv])) + """.format( + name, + password, + "\n".join( + [ + '<{0} value="Y" />'.format(i.upper()) + for i in privileges + if i.upper() in _priv + ] + ), + ) - return __execute_cmd('Create_user', _xml) + return __execute_cmd("Create_user", _xml) def delete_user(username): - ''' + """ Delete a user CLI Example: @@ -394,20 +421,22 @@ def delete_user(username): .. code-block:: bash salt '*' ilo.delete_user damian - ''' + """ _xml = """ - """.format(username) + """.format( + username + ) - return __execute_cmd('Delete_user', _xml) + return __execute_cmd("Delete_user", _xml) def get_user(username): - ''' + """ Returns local user information, excluding the password CLI Example: @@ -415,20 +444,22 @@ def get_user(username): .. code-block:: bash salt '*' ilo.get_user damian - ''' + """ _xml = """ - """.format(username) + """.format( + username + ) - return __execute_cmd('User_Info', _xml) + return __execute_cmd("User_Info", _xml) def change_username(old_username, new_username): - ''' + """ Change a username CLI Example: @@ -436,7 +467,7 @@ def change_username(old_username, new_username): .. code-block:: bash salt '*' ilo.change_username damian diana - ''' + """ _xml = """ @@ -446,13 +477,15 @@ def change_username(old_username, new_username): - """.format(old_username, new_username) + """.format( + old_username, new_username + ) - return __execute_cmd('Change_username', _xml) + return __execute_cmd("Change_username", _xml) def change_password(username, password): - ''' + """ Reset a users password CLI Example: @@ -460,7 +493,7 @@ def change_password(username, password): .. code-block:: bash salt '*' ilo.change_password damianMyerscough - ''' + """ _xml = """ @@ -469,13 +502,15 @@ def change_password(username, password): - """.format(username, password) + """.format( + username, password + ) - return __execute_cmd('Change_password', _xml) + return __execute_cmd("Change_password", _xml) def network(): - ''' + """ Grab the current network settings CLI Example: @@ -483,7 +518,7 @@ def network(): .. code-block:: bash salt '*' ilo.network - ''' + """ _xml = """ @@ -492,11 +527,11 @@ def network(): """ - return __execute_cmd('Network_Settings', _xml) + return __execute_cmd("Network_Settings", _xml) def configure_network(ip, netmask, gateway): - ''' + """ Configure Network Interface CLI Example: @@ -504,13 +539,15 @@ def configure_network(ip, netmask, gateway): .. code-block:: bash salt '*' ilo.configure_network [IP ADDRESS] [NETMASK] [GATEWAY] - ''' + """ current = network() # Check to see if the network is already configured - if (ip in current['Network Settings']['IP_ADDRESS']['VALUE'] and - netmask in current['Network Settings']['SUBNET_MASK']['VALUE'] and - gateway in current['Network Settings']['GATEWAY_IP_ADDRESS']['VALUE']): + if ( + ip in current["Network Settings"]["IP_ADDRESS"]["VALUE"] + and netmask in current["Network Settings"]["SUBNET_MASK"]["VALUE"] + and gateway in current["Network Settings"]["GATEWAY_IP_ADDRESS"]["VALUE"] + ): return True _xml = """ @@ -523,13 +560,15 @@ def configure_network(ip, netmask, gateway): - """.format(ip, netmask, gateway) + """.format( + ip, netmask, gateway + ) - return __execute_cmd('Configure_Network', _xml) + return __execute_cmd("Configure_Network", _xml) def enable_dhcp(): - ''' + """ Enable DHCP CLI Example: @@ -537,10 +576,10 @@ def enable_dhcp(): .. code-block:: bash salt '*' ilo.enable_dhcp - ''' + """ current = network() - if current['Network Settings']['DHCP_ENABLE']['VALUE'] == 'Y': + if current["Network Settings"]["DHCP_ENABLE"]["VALUE"] == "Y": return True _xml = """ @@ -553,11 +592,11 @@ def enable_dhcp(): """ - return __execute_cmd('Enable_DHCP', _xml) + return __execute_cmd("Enable_DHCP", _xml) def disable_dhcp(): - ''' + """ Disable DHCP CLI Example: @@ -565,10 +604,10 @@ def disable_dhcp(): .. code-block:: bash salt '*' ilo.disable_dhcp - ''' + """ current = network() - if current['Network Settings']['DHCP_ENABLE']['VALUE'] == 'N': + if current["Network Settings"]["DHCP_ENABLE"]["VALUE"] == "N": return True _xml = """ @@ -581,11 +620,11 @@ def disable_dhcp(): """ - return __execute_cmd('Disable_DHCP', _xml) + return __execute_cmd("Disable_DHCP", _xml) def configure_snmp(community, snmp_port=161, snmp_trapport=161): - ''' + """ Configure SNMP CLI Example: @@ -593,7 +632,7 @@ def configure_snmp(community, snmp_port=161, snmp_trapport=161): .. code-block:: bash salt '*' ilo.configure_snmp [COMMUNITY STRING] [SNMP PORT] [SNMP TRAP PORT] - ''' + """ _xml = """ @@ -613,6 +652,8 @@ def configure_snmp(community, snmp_port=161, snmp_trapport=161): - """.format(snmp_port, snmp_trapport, community) + """.format( + snmp_port, snmp_trapport, community + ) - return __execute_cmd('Configure_SNMP', _xml) + return __execute_cmd("Configure_SNMP", _xml) diff --git a/salt/modules/incron.py b/salt/modules/incron.py index 0a6fe2b9d87..e3d01d789c9 100644 --- a/salt/modules/incron.py +++ b/salt/modules/incron.py @@ -1,43 +1,56 @@ # -*- coding: utf-8 -*- -''' +""" Work with incron -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging import os -# Import salt libs -from salt.ext import six -from salt.ext.six.moves import range import salt.utils.data import salt.utils.files import salt.utils.functools import salt.utils.stringutils +# Import salt libs +from salt.ext import six +from salt.ext.six.moves import range + # Set up logging log = logging.getLogger(__name__) -TAG = '# Line managed by Salt, do not edit' -_INCRON_SYSTEM_TAB = '/etc/incron.d/' +TAG = "# Line managed by Salt, do not edit" +_INCRON_SYSTEM_TAB = "/etc/incron.d/" _MASK_TYPES = [ - 'IN_ACCESS', 'IN_ATTRIB', 'IN_CLOSE_WRITE', - 'IN_CLOSE_NOWRITE', 'IN_CREATE', 'IN_DELETE', - 'IN_DELETE_SELF', 'IN_MODIFY', 'IN_MOVE_SELF', - 'IN_MOVED_FROM', 'IN_MOVED_TO', 'IN_OPEN', - 'IN_ALL_EVENTS', 'IN_MOVE', 'IN_CLOSE', - 'IN_DONT_FOLLOW', 'IN_ONESHOT', 'IN_ONLYDIR', - 'IN_NO_LOOP' + "IN_ACCESS", + "IN_ATTRIB", + "IN_CLOSE_WRITE", + "IN_CLOSE_NOWRITE", + "IN_CREATE", + "IN_DELETE", + "IN_DELETE_SELF", + "IN_MODIFY", + "IN_MOVE_SELF", + "IN_MOVED_FROM", + "IN_MOVED_TO", + "IN_OPEN", + "IN_ALL_EVENTS", + "IN_MOVE", + "IN_CLOSE", + "IN_DONT_FOLLOW", + "IN_ONESHOT", + "IN_ONLYDIR", + "IN_NO_LOOP", ] def _needs_change(old, new): if old != new: - if new == 'random': + if new == "random": # Allow switch from '*' or not present to 'random' - if old == '*': + if old == "*": return True elif new is not None: return True @@ -45,31 +58,27 @@ def _needs_change(old, new): def _render_tab(lst): - ''' + """ Takes a tab list structure and renders it to a list for applying it to a file - ''' + """ ret = [] - for pre in lst['pre']: - ret.append('{0}\n'.format(pre)) - for cron in lst['crons']: - ret.append('{0} {1} {2}\n'.format(cron['path'], - cron['mask'], - cron['cmd'], - ) - ) + for pre in lst["pre"]: + ret.append("{0}\n".format(pre)) + for cron in lst["crons"]: + ret.append("{0} {1} {2}\n".format(cron["path"], cron["mask"], cron["cmd"],)) return ret def _get_incron_cmdstr(path): - ''' + """ Returns a format string, to be used to build an incrontab command. - ''' - return 'incrontab {0}'.format(path) + """ + return "incrontab {0}".format(path) def write_incron_file(user, path): - ''' + """ Writes the contents of a file to a user's incrontab CLI Example: @@ -77,12 +86,17 @@ def write_incron_file(user, path): .. code-block:: bash salt '*' incron.write_incron_file root /tmp/new_incron - ''' - return __salt__['cmd.retcode'](_get_incron_cmdstr(path), runas=user, python_shell=False) == 0 + """ + return ( + __salt__["cmd.retcode"]( + _get_incron_cmdstr(path), runas=user, python_shell=False + ) + == 0 + ) def write_incron_file_verbose(user, path): - ''' + """ Writes the contents of a file to a user's incrontab and return error message on error CLI Example: @@ -90,58 +104,62 @@ def write_incron_file_verbose(user, path): .. code-block:: bash salt '*' incron.write_incron_file_verbose root /tmp/new_incron - ''' - return __salt__['cmd.run_all'](_get_incron_cmdstr(path), runas=user, python_shell=False) + """ + return __salt__["cmd.run_all"]( + _get_incron_cmdstr(path), runas=user, python_shell=False + ) def _write_incron_lines(user, lines): - ''' + """ Takes a list of lines to be committed to a user's incrontab and writes it - ''' - if user == 'system': + """ + if user == "system": ret = {} - ret['retcode'] = _write_file(_INCRON_SYSTEM_TAB, 'salt', ''.join(lines)) + ret["retcode"] = _write_file(_INCRON_SYSTEM_TAB, "salt", "".join(lines)) return ret else: path = salt.utils.files.mkstemp() - with salt.utils.files.fopen(path, 'wb') as fp_: + with salt.utils.files.fopen(path, "wb") as fp_: fp_.writelines(salt.utils.data.encode(lines)) - if __grains__['os_family'] == 'Solaris' and user != "root": - __salt__['cmd.run']('chown {0} {1}'.format(user, path), python_shell=False) - ret = __salt__['cmd.run_all'](_get_incron_cmdstr(path), runas=user, python_shell=False) + if __grains__["os_family"] == "Solaris" and user != "root": + __salt__["cmd.run"]("chown {0} {1}".format(user, path), python_shell=False) + ret = __salt__["cmd.run_all"]( + _get_incron_cmdstr(path), runas=user, python_shell=False + ) os.remove(path) return ret def _write_file(folder, filename, data): - ''' + """ Writes a file to disk - ''' + """ path = os.path.join(folder, filename) if not os.path.exists(folder): - msg = '{0} cannot be written. {1} does not exist'.format(filename, folder) + msg = "{0} cannot be written. {1} does not exist".format(filename, folder) log.error(msg) raise AttributeError(six.text_type(msg)) - with salt.utils.files.fopen(path, 'w') as fp_: + with salt.utils.files.fopen(path, "w") as fp_: fp_.write(salt.utils.stringutils.to_str(data)) return 0 def _read_file(folder, filename): - ''' + """ Reads and returns the contents of a file - ''' + """ path = os.path.join(folder, filename) try: - with salt.utils.files.fopen(path, 'rb') as contents: + with salt.utils.files.fopen(path, "rb") as contents: return salt.utils.data.decode(contents.readlines()) except (OSError, IOError): - return '' + return "" def raw_system_incron(): - ''' + """ Return the contents of the system wide incrontab CLI Example: @@ -149,13 +167,13 @@ def raw_system_incron(): .. code-block:: bash salt '*' incron.raw_system_incron - ''' - log.debug("read_file {0}" . format(_read_file(_INCRON_SYSTEM_TAB, 'salt'))) - return ''.join(_read_file(_INCRON_SYSTEM_TAB, 'salt')) + """ + log.debug("read_file {0}".format(_read_file(_INCRON_SYSTEM_TAB, "salt"))) + return "".join(_read_file(_INCRON_SYSTEM_TAB, "salt")) def raw_incron(user): - ''' + """ Return the contents of the user's incrontab CLI Example: @@ -163,16 +181,16 @@ def raw_incron(user): .. code-block:: bash salt '*' incron.raw_incron root - ''' - if __grains__['os_family'] == 'Solaris': - cmd = 'incrontab -l {0}'.format(user) + """ + if __grains__["os_family"] == "Solaris": + cmd = "incrontab -l {0}".format(user) else: - cmd = 'incrontab -l -u {0}'.format(user) - return __salt__['cmd.run_stdout'](cmd, rstrip=False, runas=user, python_shell=False) + cmd = "incrontab -l -u {0}".format(user) + return __salt__["cmd.run_stdout"](cmd, rstrip=False, runas=user, python_shell=False) def list_tab(user): - ''' + """ Return the contents of the specified user's incrontab CLI Example: @@ -180,15 +198,13 @@ def list_tab(user): .. code-block:: bash salt '*' incron.list_tab root - ''' - if user == 'system': + """ + if user == "system": data = raw_system_incron() else: data = raw_incron(user) - log.debug("user data {0}" . format(data)) - ret = {'crons': [], - 'pre': [] - } + log.debug("user data {0}".format(data)) + ret = {"crons": [], "pre": []} flag = False for line in data.splitlines(): if len(line.split()) > 3: @@ -196,23 +212,21 @@ def list_tab(user): comps = line.split() path = comps[0] mask = comps[1] - cmd = ' '.join(comps[2:]) + cmd = " ".join(comps[2:]) - dat = {'path': path, - 'mask': mask, - 'cmd': cmd} - ret['crons'].append(dat) + dat = {"path": path, "mask": mask, "cmd": cmd} + ret["crons"].append(dat) else: - ret['pre'].append(line) + ret["pre"].append(line) return ret # For consistency's sake -ls = salt.utils.functools.alias_function(list_tab, 'ls') +ls = salt.utils.functools.alias_function(list_tab, "ls") def set_job(user, path, mask, cmd): - ''' + """ Sets an incron job up for a specified user. CLI Example: @@ -220,30 +234,30 @@ def set_job(user, path, mask, cmd): .. code-block:: bash salt '*' incron.set_job root '/root' 'IN_MODIFY' 'echo "$$ $@ $# $% $&"' - ''' + """ # Scrub the types mask = six.text_type(mask).upper() # Check for valid mask types - for item in mask.split(','): + for item in mask.split(","): if item not in _MASK_TYPES: - return 'Invalid mask type: {0}' . format(item) + return "Invalid mask type: {0}".format(item) updated = False - arg_mask = mask.split(',') + arg_mask = mask.split(",") arg_mask.sort() lst = list_tab(user) updated_crons = [] # Look for existing incrons that have cmd, path and at least one of the MASKS # remove and replace with the one we're passed - for item, cron in enumerate(lst['crons']): - if path == cron['path']: - if cron['cmd'] == cmd: - cron_mask = cron['mask'].split(',') + for item, cron in enumerate(lst["crons"]): + if path == cron["path"]: + if cron["cmd"] == cmd: + cron_mask = cron["mask"].split(",") cron_mask.sort() if cron_mask == arg_mask: - return 'present' + return "present" if any([x in cron_mask for x in arg_mask]): updated = True @@ -254,26 +268,23 @@ def set_job(user, path, mask, cmd): else: updated_crons.append(cron) - cron = {'cmd': cmd, 'path': path, 'mask': mask} + cron = {"cmd": cmd, "path": path, "mask": mask} updated_crons.append(cron) - lst['crons'] = updated_crons + lst["crons"] = updated_crons comdat = _write_incron_lines(user, _render_tab(lst)) - if comdat['retcode']: + if comdat["retcode"]: # Failed to commit, return the error - return comdat['stderr'] + return comdat["stderr"] if updated: - return 'updated' + return "updated" else: - return 'new' + return "new" -def rm_job(user, - path, - mask, - cmd): - ''' +def rm_job(user, path, mask, cmd): + """ Remove a incron job for a specified user. If any of the day/time params are specified, the job will only be removed if the specified params match. @@ -282,35 +293,35 @@ def rm_job(user, .. code-block:: bash salt '*' incron.rm_job root /path - ''' + """ # Scrub the types mask = six.text_type(mask).upper() # Check for valid mask types - for item in mask.split(','): + for item in mask.split(","): if item not in _MASK_TYPES: - return 'Invalid mask type: {0}' . format(item) + return "Invalid mask type: {0}".format(item) lst = list_tab(user) - ret = 'absent' + ret = "absent" rm_ = None - for ind in range(len(lst['crons'])): + for ind in range(len(lst["crons"])): if rm_ is not None: break - if path == lst['crons'][ind]['path']: - if cmd == lst['crons'][ind]['cmd']: - if mask == lst['crons'][ind]['mask']: + if path == lst["crons"][ind]["path"]: + if cmd == lst["crons"][ind]["cmd"]: + if mask == lst["crons"][ind]["mask"]: rm_ = ind if rm_ is not None: - lst['crons'].pop(rm_) - ret = 'removed' + lst["crons"].pop(rm_) + ret = "removed" comdat = _write_incron_lines(user, _render_tab(lst)) - if comdat['retcode']: + if comdat["retcode"]: # Failed to commit, return the error - return comdat['stderr'] + return comdat["stderr"] return ret -rm = salt.utils.functools.alias_function(rm_job, 'rm') +rm = salt.utils.functools.alias_function(rm_job, "rm") diff --git a/salt/modules/influxdb08mod.py b/salt/modules/influxdb08mod.py index e29b4075d66..d04cb7e7584 100644 --- a/salt/modules/influxdb08mod.py +++ b/salt/modules/influxdb08mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" InfluxDB - A distributed time series database Module to provide InfluxDB compatibility to Salt (compatible with InfluxDB @@ -20,50 +20,56 @@ version 0.5-0.8) This data can also be passed into pillar. Options passed into opts will overwrite options passed into pillar. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import logging + try: import influxdb.influxdb08 + HAS_INFLUXDB_08 = True except ImportError: HAS_INFLUXDB_08 = False -import logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'influxdb08' +__virtualname__ = "influxdb08" def __virtual__(): - ''' + """ Only load if influxdb lib is present - ''' + """ if HAS_INFLUXDB_08: return __virtualname__ - return (False, 'The influx execution module cannot be loaded: influxdb library not available.') + return ( + False, + "The influx execution module cannot be loaded: influxdb library not available.", + ) def _client(user=None, password=None, host=None, port=None): if not user: - user = __salt__['config.option']('influxdb08.user', 'root') + user = __salt__["config.option"]("influxdb08.user", "root") if not password: - password = __salt__['config.option']('influxdb08.password', 'root') + password = __salt__["config.option"]("influxdb08.password", "root") if not host: - host = __salt__['config.option']('influxdb08.host', 'localhost') + host = __salt__["config.option"]("influxdb08.host", "localhost") if not port: - port = __salt__['config.option']('influxdb08.port', 8086) + port = __salt__["config.option"]("influxdb08.port", 8086) return influxdb.influxdb08.InfluxDBClient( - host=host, port=port, username=user, password=password) + host=host, port=port, username=user, password=password + ) def db_list(user=None, password=None, host=None, port=None): - ''' + """ List all InfluxDB databases user @@ -85,13 +91,13 @@ def db_list(user=None, password=None, host=None, port=None): salt '*' influxdb08.db_list salt '*' influxdb08.db_list - ''' + """ client = _client(user=user, password=password, host=host, port=port) return client.get_list_database() def db_exists(name, user=None, password=None, host=None, port=None): - ''' + """ Checks if a database exists in Influxdb name @@ -115,15 +121,15 @@ def db_exists(name, user=None, password=None, host=None, port=None): salt '*' influxdb08.db_exists salt '*' influxdb08.db_exists - ''' + """ dbs = db_list(user, password, host, port) if not isinstance(dbs, list): return False - return name in [db['name'] for db in dbs] + return name in [db["name"] for db in dbs] def db_create(name, user=None, password=None, host=None, port=None): - ''' + """ Create a database name @@ -147,9 +153,9 @@ def db_create(name, user=None, password=None, host=None, port=None): salt '*' influxdb08.db_create salt '*' influxdb08.db_create - ''' + """ if db_exists(name, user, password, host, port): - log.info('DB \'%s\' already exists', name) + log.info("DB '%s' already exists", name) return False client = _client(user=user, password=password, host=host, port=port) client.create_database(name) @@ -157,7 +163,7 @@ def db_create(name, user=None, password=None, host=None, port=None): def db_remove(name, user=None, password=None, host=None, port=None): - ''' + """ Remove a database name @@ -181,16 +187,16 @@ def db_remove(name, user=None, password=None, host=None, port=None): salt '*' influxdb08.db_remove salt '*' influxdb08.db_remove - ''' + """ if not db_exists(name, user, password, host, port): - log.info('DB \'%s\' does not exist', name) + log.info("DB '%s' does not exist", name) return False client = _client(user=user, password=password, host=host, port=port) return client.delete_database(name) def user_list(database=None, user=None, password=None, host=None, port=None): - ''' + """ List cluster admins or database users. If a database is specified: it will return database users list. @@ -218,7 +224,7 @@ def user_list(database=None, user=None, password=None, host=None, port=None): salt '*' influxdb08.user_list salt '*' influxdb08.user_list salt '*' influxdb08.user_list - ''' + """ client = _client(user=user, password=password, host=host, port=port) if not database: @@ -229,7 +235,7 @@ def user_list(database=None, user=None, password=None, host=None, port=None): def user_exists(name, database=None, user=None, password=None, host=None, port=None): - ''' + """ Checks if a cluster admin or database user exists. If a database is specified: it will check for database user existence. @@ -260,31 +266,27 @@ def user_exists(name, database=None, user=None, password=None, host=None, port=N salt '*' influxdb08.user_exists salt '*' influxdb08.user_exists salt '*' influxdb08.user_exists - ''' + """ users = user_list(database, user, password, host, port) if not isinstance(users, list): return False for user in users: # the dict key could be different depending on influxdb version - username = user.get('user', user.get('name')) + username = user.get("user", user.get("name")) if username: if username == name: return True else: - log.warning('Could not find username in user: %s', user) + log.warning("Could not find username in user: %s", user) return False -def user_create(name, - passwd, - database=None, - user=None, - password=None, - host=None, - port=None): - ''' +def user_create( + name, passwd, database=None, user=None, password=None, host=None, port=None +): + """ Create a cluster admin or a database user. If a database is specified: it will create database user. @@ -318,12 +320,12 @@ def user_create(name, salt '*' influxdb08.user_create salt '*' influxdb08.user_create salt '*' influxdb08.user_create - ''' + """ if user_exists(name, database, user, password, host, port): if database: - log.info('User \'%s\' already exists for DB \'%s\'', name, database) + log.info("User '%s' already exists for DB '%s'", name, database) else: - log.info('Cluster admin \'%s\' already exists', name) + log.info("Cluster admin '%s' already exists", name) return False client = _client(user=user, password=password, host=host, port=port) @@ -335,14 +337,10 @@ def user_create(name, return client.add_database_user(name, passwd) -def user_chpass(name, - passwd, - database=None, - user=None, - password=None, - host=None, - port=None): - ''' +def user_chpass( + name, passwd, database=None, user=None, password=None, host=None, port=None +): + """ Change password for a cluster admin or a database user. If a database is specified: it will update database user password. @@ -376,12 +374,12 @@ def user_chpass(name, salt '*' influxdb08.user_chpass salt '*' influxdb08.user_chpass salt '*' influxdb08.user_chpass - ''' + """ if not user_exists(name, database, user, password, host, port): if database: - log.info('User \'%s\' does not exist for DB \'%s\'', name, database) + log.info("User '%s' does not exist for DB '%s'", name, database) else: - log.info('Cluster admin \'%s\' does not exist', name) + log.info("Cluster admin '%s' does not exist", name) return False client = _client(user=user, password=password, host=host, port=port) @@ -393,13 +391,8 @@ def user_chpass(name, return client.update_database_user_password(name, passwd) -def user_remove(name, - database=None, - user=None, - password=None, - host=None, - port=None): - ''' +def user_remove(name, database=None, user=None, password=None, host=None, port=None): + """ Remove a cluster admin or a database user. If a database is specified: it will remove the database user. @@ -433,12 +426,12 @@ def user_remove(name, salt '*' influxdb08.user_remove salt '*' influxdb08.user_remove salt '*' influxdb08.user_remove - ''' + """ if not user_exists(name, database, user, password, host, port): if database: - log.info('User \'%s\' does not exist for DB \'%s\'', name, database) + log.info("User '%s' does not exist for DB '%s'", name, database) else: - log.info('Cluster admin \'%s\' does not exist', name) + log.info("Cluster admin '%s' does not exist", name) return False client = _client(user=user, password=password, host=host, port=port) @@ -450,13 +443,10 @@ def user_remove(name, return client.delete_database_user(name) -def retention_policy_get(database, - name, - user=None, - password=None, - host=None, - port=None): - ''' +def retention_policy_get( + database, name, user=None, password=None, host=None, port=None +): + """ Get an existing retention policy. database @@ -470,23 +460,20 @@ def retention_policy_get(database, .. code-block:: bash salt '*' influxdb08.retention_policy_get metrics default - ''' + """ client = _client(user=user, password=password, host=host, port=port) for policy in client.get_list_retention_policies(database): - if policy['name'] == name: + if policy["name"] == name: return policy return None -def retention_policy_exists(database, - name, - user=None, - password=None, - host=None, - port=None): - ''' +def retention_policy_exists( + database, name, user=None, password=None, host=None, port=None +): + """ Check if a retention policy exists. database @@ -500,21 +487,23 @@ def retention_policy_exists(database, .. code-block:: bash salt '*' influxdb08.retention_policy_exists metrics default - ''' + """ policy = retention_policy_get(database, name, user, password, host, port) return policy is not None -def retention_policy_add(database, - name, - duration, - replication, - default=False, - user=None, - password=None, - host=None, - port=None): - ''' +def retention_policy_add( + database, + name, + duration, + replication, + default=False, + user=None, + password=None, + host=None, + port=None, +): + """ Add a retention policy. database @@ -537,22 +526,24 @@ def retention_policy_add(database, .. code-block:: bash salt '*' influxdb.retention_policy_add metrics default 1d 1 - ''' + """ client = _client(user=user, password=password, host=host, port=port) client.create_retention_policy(name, duration, replication, database, default) return True -def retention_policy_alter(database, - name, - duration, - replication, - default=False, - user=None, - password=None, - host=None, - port=None): - ''' +def retention_policy_alter( + database, + name, + duration, + replication, + default=False, + user=None, + password=None, + host=None, + port=None, +): + """ Modify an existing retention policy. database @@ -575,21 +566,23 @@ def retention_policy_alter(database, .. code-block:: bash salt '*' influxdb08.retention_policy_modify metrics default 1d 1 - ''' + """ client = _client(user=user, password=password, host=host, port=port) client.alter_retention_policy(name, database, duration, replication, default) return True -def query(database, - query, - time_precision='s', - chunked=False, - user=None, - password=None, - host=None, - port=None): - ''' +def query( + database, + query, + time_precision="s", + chunked=False, + user=None, + password=None, + host=None, + port=None, +): + """ Querying data database @@ -622,14 +615,14 @@ def query(database, salt '*' influxdb08.query salt '*' influxdb08.query - ''' + """ client = _client(user=user, password=password, host=host, port=port) client.switch_database(database) return client.query(query, time_precision=time_precision, chunked=chunked) def login_test(name, password, database=None, host=None, port=None): - ''' + """ Checks if a credential pair can log in at all. If a database is specified: it will check for database user existence. @@ -657,7 +650,7 @@ def login_test(name, password, database=None, host=None, port=None): salt '*' influxdb08.login_test salt '*' influxdb08.login_test salt '*' influxdb08.login_test - ''' + """ try: client = _client(user=name, password=password, host=host, port=port) client.get_list_database() diff --git a/salt/modules/influxdbmod.py b/salt/modules/influxdbmod.py index 98087c64d57..8638e358c7a 100644 --- a/salt/modules/influxdbmod.py +++ b/salt/modules/influxdbmod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" InfluxDB - A distributed time series database Module to provide InfluxDB compatibility to Salt (compatible with InfluxDB @@ -26,15 +26,9 @@ version 0.9+) would override ``user`` and ``password`` while still using the defaults for ``host`` and ``port``. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -try: - import influxdb - HAS_INFLUXDB = True -except ImportError: - HAS_INFLUXDB = False - import collections import logging @@ -42,43 +36,64 @@ import logging import salt.utils.json from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS +try: + import influxdb + + HAS_INFLUXDB = True +except ImportError: + HAS_INFLUXDB = False + + log = logging.getLogger(__name__) # name used to refer to this module in __salt__ -__virtualname__ = 'influxdb' +__virtualname__ = "influxdb" def __virtual__(): - ''' + """ Only load if influxdb lib is present - ''' + """ if HAS_INFLUXDB: return __virtualname__ - return (False, ('The influxdb execution module could not be loaded:' - 'influxdb library not available.')) + return ( + False, + ( + "The influxdb execution module could not be loaded:" + "influxdb library not available." + ), + ) -def _client(influxdb_user=None, influxdb_password=None, influxdb_host=None, influxdb_port=None, **client_args): +def _client( + influxdb_user=None, + influxdb_password=None, + influxdb_host=None, + influxdb_port=None, + **client_args +): if not influxdb_user: - influxdb_user = __salt__['config.option']('influxdb.user', 'root') + influxdb_user = __salt__["config.option"]("influxdb.user", "root") if not influxdb_password: - influxdb_password = __salt__['config.option']('influxdb.password', 'root') + influxdb_password = __salt__["config.option"]("influxdb.password", "root") if not influxdb_host: - influxdb_host = __salt__['config.option']('influxdb.host', 'localhost') + influxdb_host = __salt__["config.option"]("influxdb.host", "localhost") if not influxdb_port: - influxdb_port = __salt__['config.option']('influxdb.port', 8086) + influxdb_port = __salt__["config.option"]("influxdb.port", 8086) for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in client_args: del client_args[ignore] - return influxdb.InfluxDBClient(host=influxdb_host, - port=influxdb_port, - username=influxdb_user, - password=influxdb_password, - **client_args) + return influxdb.InfluxDBClient( + host=influxdb_host, + port=influxdb_port, + username=influxdb_user, + password=influxdb_password, + **client_args + ) def list_dbs(**client_args): - ''' + """ List all InfluxDB databases. CLI Example: @@ -86,14 +101,14 @@ def list_dbs(**client_args): .. code-block:: bash salt '*' influxdb.list_dbs - ''' + """ client = _client(**client_args) return client.get_list_database() def db_exists(name, **client_args): - ''' + """ Checks if a database exists in InfluxDB. name @@ -104,15 +119,15 @@ def db_exists(name, **client_args): .. code-block:: bash salt '*' influxdb.db_exists - ''' - if name in [db['name'] for db in list_dbs(**client_args)]: + """ + if name in [db["name"] for db in list_dbs(**client_args)]: return True return False def create_db(name, **client_args): - ''' + """ Create a database. name @@ -123,9 +138,9 @@ def create_db(name, **client_args): .. code-block:: bash salt '*' influxdb.create_db - ''' + """ if db_exists(name, **client_args): - log.info('DB \'%s\' already exists', name) + log.info("DB '%s' already exists", name) return False client = _client(**client_args) @@ -135,7 +150,7 @@ def create_db(name, **client_args): def drop_db(name, **client_args): - ''' + """ Drop a database. name @@ -146,9 +161,9 @@ def drop_db(name, **client_args): .. code-block:: bash salt '*' influxdb.drop_db - ''' + """ if not db_exists(name, **client_args): - log.info('DB \'%s\' does not exist', name) + log.info("DB '%s' does not exist", name) return False client = _client(**client_args) @@ -158,7 +173,7 @@ def drop_db(name, **client_args): def list_users(**client_args): - ''' + """ List all users. CLI Example: @@ -166,14 +181,14 @@ def list_users(**client_args): .. code-block:: bash salt '*' influxdb.list_users - ''' + """ client = _client(**client_args) return client.get_list_users() def user_exists(name, **client_args): - ''' + """ Check if a user exists. name @@ -184,7 +199,7 @@ def user_exists(name, **client_args): .. code-block:: bash salt '*' influxdb.user_exists - ''' + """ if user_info(name, **client_args): return True @@ -192,7 +207,7 @@ def user_exists(name, **client_args): def user_info(name, **client_args): - ''' + """ Get information about given user. name @@ -203,9 +218,10 @@ def user_info(name, **client_args): .. code-block:: bash salt '*' influxdb.user_info - ''' - matching_users = (user for user in list_users(**client_args) - if user.get('user') == name) + """ + matching_users = ( + user for user in list_users(**client_args) if user.get("user") == name + ) try: return next(matching_users) @@ -214,7 +230,7 @@ def user_info(name, **client_args): def create_user(name, passwd, admin=False, **client_args): - ''' + """ Create a user. name @@ -233,7 +249,7 @@ def create_user(name, passwd, admin=False, **client_args): salt '*' influxdb.create_user salt '*' influxdb.create_user admin=True - ''' + """ if user_exists(name, **client_args): log.info("User '%s' already exists", name) return False @@ -245,7 +261,7 @@ def create_user(name, passwd, admin=False, **client_args): def set_user_password(name, passwd, **client_args): - ''' + """ Change password of a user. name @@ -259,9 +275,9 @@ def set_user_password(name, passwd, **client_args): .. code-block:: bash salt '*' influxdb.set_user_password - ''' + """ if not user_exists(name, **client_args): - log.info('User \'%s\' does not exist', name) + log.info("User '%s' does not exist", name) return False client = _client(**client_args) @@ -271,7 +287,7 @@ def set_user_password(name, passwd, **client_args): def grant_admin_privileges(name, **client_args): - ''' + """ Grant cluster administration privileges to a user. name @@ -282,7 +298,7 @@ def grant_admin_privileges(name, **client_args): .. code-block:: bash salt '*' influxdb.grant_admin_privileges - ''' + """ client = _client(**client_args) client.grant_admin_privileges(name) @@ -290,7 +306,7 @@ def grant_admin_privileges(name, **client_args): def revoke_admin_privileges(name, **client_args): - ''' + """ Revoke cluster administration privileges from a user. name @@ -301,7 +317,7 @@ def revoke_admin_privileges(name, **client_args): .. code-block:: bash salt '*' influxdb.revoke_admin_privileges - ''' + """ client = _client(**client_args) client.revoke_admin_privileges(name) @@ -309,7 +325,7 @@ def revoke_admin_privileges(name, **client_args): def remove_user(name, **client_args): - ''' + """ Remove a user. name @@ -320,9 +336,9 @@ def remove_user(name, **client_args): .. code-block:: bash salt '*' influxdb.remove_user - ''' + """ if not user_exists(name, **client_args): - log.info('User \'%s\' does not exist', name) + log.info("User '%s' does not exist", name) return False client = _client(**client_args) @@ -332,7 +348,7 @@ def remove_user(name, **client_args): def get_retention_policy(database, name, **client_args): - ''' + """ Get an existing retention policy. database @@ -347,18 +363,23 @@ def get_retention_policy(database, name, **client_args): .. code-block:: bash salt '*' influxdb.get_retention_policy metrics default - ''' + """ client = _client(**client_args) try: - return next((p for p in client.get_list_retention_policies(database) - if p.get('name') == name)) + return next( + ( + p + for p in client.get_list_retention_policies(database) + if p.get("name") == name + ) + ) except StopIteration: return {} def retention_policy_exists(database, name, **client_args): - ''' + """ Check if retention policy with given name exists. database @@ -373,7 +394,7 @@ def retention_policy_exists(database, name, **client_args): .. code-block:: bash salt '*' influxdb.retention_policy_exists metrics default - ''' + """ if get_retention_policy(database, name, **client_args): return True @@ -381,7 +402,7 @@ def retention_policy_exists(database, name, **client_args): def drop_retention_policy(database, name, **client_args): - ''' + """ Drop a retention policy. database @@ -395,20 +416,17 @@ def drop_retention_policy(database, name, **client_args): .. code-block:: bash salt '*' influxdb.drop_retention_policy mydb mypr - ''' + """ client = _client(**client_args) client.drop_retention_policy(name, database) return True -def create_retention_policy(database, - name, - duration, - replication, - default=False, - **client_args): - ''' +def create_retention_policy( + database, name, duration, replication, default=False, **client_args +): + """ Create a retention policy. database @@ -439,21 +457,17 @@ def create_retention_policy(database, .. code-block:: bash salt '*' influxdb.create_retention_policy metrics default 1d 1 - ''' + """ client = _client(**client_args) - client.create_retention_policy(name, duration, replication, database, - default) + client.create_retention_policy(name, duration, replication, database, default) return True -def alter_retention_policy(database, - name, - duration, - replication, - default=False, - **client_args): - ''' +def alter_retention_policy( + database, name, duration, replication, default=False, **client_args +): + """ Modify an existing retention policy. name @@ -485,15 +499,14 @@ def alter_retention_policy(database, .. code-block:: bash salt '*' influxdb.alter_retention_policy metrics default 1d 1 - ''' + """ client = _client(**client_args) - client.alter_retention_policy(name, database, duration, replication, - default) + client.alter_retention_policy(name, database, duration, replication, default) return True def list_privileges(name, **client_args): - ''' + """ List privileges from a user. name @@ -504,17 +517,17 @@ def list_privileges(name, **client_args): .. code-block:: bash salt '*' influxdb.list_privileges - ''' + """ client = _client(**client_args) res = {} for item in client.get_list_privileges(name): - res[item['database']] = item['privilege'].split()[0].lower() + res[item["database"]] = item["privilege"].split()[0].lower() return res def grant_privilege(database, privilege, username, **client_args): - ''' + """ Grant a privilege on a database to a user. database @@ -525,7 +538,7 @@ def grant_privilege(database, privilege, username, **client_args): username Name of the user to grant the privilege to. - ''' + """ client = _client(**client_args) client.grant_privilege(privilege, database, username) @@ -533,7 +546,7 @@ def grant_privilege(database, privilege, username, **client_args): def revoke_privilege(database, privilege, username, **client_args): - ''' + """ Revoke a privilege on a database from a user. database @@ -544,7 +557,7 @@ def revoke_privilege(database, privilege, username, **client_args): username Name of the user to grant the privilege to. - ''' + """ client = _client(**client_args) client.revoke_privilege(privilege, database, username) @@ -552,7 +565,7 @@ def revoke_privilege(database, privilege, username, **client_args): def continuous_query_exists(database, name, **client_args): - ''' + """ Check if continuous query with given name exists on the database. database @@ -567,7 +580,7 @@ def continuous_query_exists(database, name, **client_args): .. code-block:: bash salt '*' influxdb.continuous_query_exists metrics default - ''' + """ if get_continuous_query(database, name, **client_args): return True @@ -575,7 +588,7 @@ def continuous_query_exists(database, name, **client_args): def get_continuous_query(database, name, **client_args): - ''' + """ Get an existing continuous query. database @@ -590,20 +603,22 @@ def get_continuous_query(database, name, **client_args): .. code-block:: bash salt '*' influxdb.get_continuous_query mydb cq_month - ''' + """ client = _client(**client_args) try: - for db, cqs in client.query('SHOW CONTINUOUS QUERIES').items(): + for db, cqs in client.query("SHOW CONTINUOUS QUERIES").items(): if db[0] == database: - return next((cq for cq in cqs if cq.get('name') == name)) + return next((cq for cq in cqs if cq.get("name") == name)) except StopIteration: return {} return {} -def create_continuous_query(database, name, query, resample_time=None, coverage_period=None, **client_args): - ''' +def create_continuous_query( + database, name, query, resample_time=None, coverage_period=None, **client_args +): + """ Create a continuous query. database @@ -626,27 +641,27 @@ def create_continuous_query(database, name, query, resample_time=None, coverage_ .. code-block:: bash - salt '*' influxdb.create_continuous_query mydb cq_month 'SELECT mean(*) INTO mydb.a_month.:MEASUREMENT FROM mydb.a_week./.*/ GROUP BY time(5m), *' ''' + salt '*' influxdb.create_continuous_query mydb cq_month 'SELECT mean(*) INTO mydb.a_month.:MEASUREMENT FROM mydb.a_week./.*/ GROUP BY time(5m), *' """ client = _client(**client_args) - full_query = 'CREATE CONTINUOUS QUERY {name} ON {database}' + full_query = "CREATE CONTINUOUS QUERY {name} ON {database}" if resample_time: - full_query += ' RESAMPLE EVERY {resample_time}' + full_query += " RESAMPLE EVERY {resample_time}" if coverage_period: - full_query += ' FOR {coverage_period}' - full_query += ' BEGIN {query} END' + full_query += " FOR {coverage_period}" + full_query += " BEGIN {query} END" query = full_query.format( name=name, database=database, query=query, resample_time=resample_time, - coverage_period=coverage_period + coverage_period=coverage_period, ) client.query(query) return True def drop_continuous_query(database, name, **client_args): - ''' + """ Drop a continuous query. database @@ -661,31 +676,33 @@ def drop_continuous_query(database, name, **client_args): .. code-block:: bash salt '*' influxdb.drop_continuous_query mydb my_cq - ''' + """ client = _client(**client_args) - query = 'DROP CONTINUOUS QUERY {0} ON {1}'.format(name, database) + query = "DROP CONTINUOUS QUERY {0} ON {1}".format(name, database) client.query(query) return True def _pull_query_results(resultset): - ''' + """ Parses a ResultSet returned from InfluxDB into a dictionary of results, grouped by series names and optional JSON-encoded grouping tags. - ''' + """ _results = collections.defaultdict(lambda: {}) for _header, _values in resultset.items(): _header, _group_tags = _header if _group_tags: - _results[_header][salt.utils.json.dumps(_group_tags)] = [_value for _value in _values] + _results[_header][salt.utils.json.dumps(_group_tags)] = [ + _value for _value in _values + ] else: _results[_header] = [_value for _value in _values] return dict(sorted(_results.items())) def query(database, query, **client_args): - ''' + """ Execute a query. database @@ -693,10 +710,14 @@ def query(database, query, **client_args): query InfluxQL query string. - ''' + """ client = _client(**client_args) _result = client.query(query, database=database) if isinstance(_result, collections.Sequence): - return [_pull_query_results(_query_result) for _query_result in _result if _query_result] + return [ + _pull_query_results(_query_result) + for _query_result in _result + if _query_result + ] return [_pull_query_results(_result) if _result else {}] diff --git a/salt/modules/infoblox.py b/salt/modules/infoblox.py index b32a33ead9e..664a603196a 100644 --- a/salt/modules/infoblox.py +++ b/salt/modules/infoblox.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This module have been tested on infoblox API v1.2.1, other versions of the API are likly workable. @@ -31,21 +31,21 @@ API documents can be found on your infoblox server at: api_user=admin \ api_key=passs -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import time # Import Salt libs from salt.ext import six - IMPORT_ERR = None try: import libinfoblox except Exception as exc: # pylint: disable=broad-except IMPORT_ERR = six.text_type(exc) -__virtualname__ = 'infoblox' +__virtualname__ = "infoblox" def __virtual__(): @@ -56,19 +56,19 @@ cache = {} def _get_config(**api_opts): - ''' + """ Return configuration user passed api_opts override salt config.get vars - ''' + """ config = { - 'api_sslverify': True, - 'api_url': 'https://INFOBLOX/wapi/v1.2.1', - 'api_user': '', - 'api_key': '', + "api_sslverify": True, + "api_url": "https://INFOBLOX/wapi/v1.2.1", + "api_user": "", + "api_key": "", } - if '__salt__' in globals(): - config_key = '{0}.config'.format(__virtualname__) - config.update(__salt__['config.get'](config_key, {})) + if "__salt__" in globals(): + config_key = "{0}.config".format(__virtualname__) + config.update(__salt__["config.get"](config_key, {})) # pylint: disable=C0201 for k in set(config.keys()) & set(api_opts.keys()): config[k] = api_opts[k] @@ -78,33 +78,38 @@ def _get_config(**api_opts): def _get_infoblox(**api_opts): config = _get_config(**api_opts) # TODO: perhaps cache in __opts__ - cache_key = 'infoblox_session_{0},{1},{2}'.format( - config['api_url'], config['api_user'], config['api_key']) + cache_key = "infoblox_session_{0},{1},{2}".format( + config["api_url"], config["api_user"], config["api_key"] + ) if cache_key in cache: - timedelta = int(time.time()) - cache[cache_key]['time'] - if cache[cache_key]['obj'] and timedelta < 60: - return cache[cache_key]['obj'] + timedelta = int(time.time()) - cache[cache_key]["time"] + if cache[cache_key]["obj"] and timedelta < 60: + return cache[cache_key]["obj"] c = {} - c['time'] = int(time.time()) - c['obj'] = libinfoblox.Session(api_sslverify=config['api_sslverify'], api_url=config['api_url'], - api_user=config['api_user'], api_key=config['api_key']) + c["time"] = int(time.time()) + c["obj"] = libinfoblox.Session( + api_sslverify=config["api_sslverify"], + api_url=config["api_url"], + api_user=config["api_user"], + api_key=config["api_key"], + ) cache[cache_key] = c - return c['obj'] + return c["obj"] def diff_objects(obja, objb): - ''' + """ Diff two complex infoblox objects. This is used from salt states to detect changes in objects. Using ``func:nextavailableip`` will not cause a diff if the ipaddress is in range - ''' + """ return libinfoblox.diff_obj(obja, objb) def is_ipaddr_in_ipfunc_range(ipaddr, ipfunc): - ''' + """ Return true if the ipaddress is in the range of the nextavailableip function CLI Example: @@ -113,12 +118,12 @@ def is_ipaddr_in_ipfunc_range(ipaddr, ipfunc): salt-call infoblox.is_ipaddr_in_ipfunc_range \ ipaddr="10.0.2.2" ipfunc="func:nextavailableip:10.0.0.0/8" - ''' + """ return libinfoblox.is_ipaddr_in_ipfunc_range(ipaddr, ipfunc) def update_host(name, data, **api_opts): - ''' + """ Update host record. This is a helper call to update_object. Find a hosts ``_ref`` then call update_object with the record data. @@ -128,13 +133,13 @@ def update_host(name, data, **api_opts): .. code-block:: bash salt-call infoblox.update_host name=fqdn data={} - ''' + """ o = get_host(name=name, **api_opts) - return update_object(objref=o['_ref'], data=data, **api_opts) + return update_object(objref=o["_ref"], data=data, **api_opts) def update_object(objref, data, **api_opts): - ''' + """ Update raw infoblox object. This is a low level api call. CLI Example: @@ -142,15 +147,15 @@ def update_object(objref, data, **api_opts): .. code-block:: bash salt-call infoblox.update_object objref=[ref_of_object] data={} - ''' - if '__opts__' in globals() and __opts__['test']: - return {'Test': 'Would attempt to update object: {0}'.format(objref)} + """ + if "__opts__" in globals() and __opts__["test"]: + return {"Test": "Would attempt to update object: {0}".format(objref)} infoblox = _get_infoblox(**api_opts) return infoblox.update_object(objref, data) def delete_object(objref, **api_opts): - ''' + """ Delete infoblox object. This is a low level api call. CLI Example: @@ -158,15 +163,15 @@ def delete_object(objref, **api_opts): .. code-block:: bash salt-call infoblox.delete_object objref=[ref_of_object] - ''' - if '__opts__' in globals() and __opts__['test']: - return {'Test': 'Would attempt to delete object: {0}'.format(objref)} + """ + if "__opts__" in globals() and __opts__["test"]: + return {"Test": "Would attempt to delete object: {0}".format(objref)} infoblox = _get_infoblox(**api_opts) return infoblox.delete_object(objref) def create_object(object_type, data, **api_opts): - ''' + """ Create raw infoblox object. This is a low level api call. CLI Example: @@ -174,16 +179,22 @@ def create_object(object_type, data, **api_opts): .. code-block:: bash salt-call infoblox.update_object object_type=record:host data={} - ''' - if '__opts__' in globals() and __opts__['test']: - return {'Test': 'Would attempt to create object: {0}'.format(object_type)} + """ + if "__opts__" in globals() and __opts__["test"]: + return {"Test": "Would attempt to create object: {0}".format(object_type)} infoblox = _get_infoblox(**api_opts) return infoblox.create_object(object_type, data) -def get_object(objref, data=None, return_fields=None, max_results=None, - ensure_none_or_one_result=False, **api_opts): - ''' +def get_object( + objref, + data=None, + return_fields=None, + max_results=None, + ensure_none_or_one_result=False, + **api_opts +): + """ Get raw infoblox object. This is a low level api call. CLI Example: @@ -191,16 +202,17 @@ def get_object(objref, data=None, return_fields=None, max_results=None, .. code-block:: bash salt-call infoblox.get_object objref=[_ref of object] - ''' + """ if not data: data = {} infoblox = _get_infoblox(**api_opts) - return infoblox.get_object(objref, data, return_fields, - max_results, ensure_none_or_one_result) + return infoblox.get_object( + objref, data, return_fields, max_results, ensure_none_or_one_result + ) def create_cname(data, **api_opts): - ''' + """ Create a cname record. CLI Example: @@ -214,14 +226,14 @@ def create_cname(data, **api_opts): "view": "Internal", \ "canonical": "example-ha-0.example.com" \ } - ''' + """ infoblox = _get_infoblox(**api_opts) host = infoblox.create_cname(data=data) return host def get_cname(name=None, canonical=None, return_fields=None, **api_opts): - ''' + """ Get CNAME information. CLI Examples: @@ -230,14 +242,14 @@ def get_cname(name=None, canonical=None, return_fields=None, **api_opts): salt-call infoblox.get_cname name=example.example.com salt-call infoblox.get_cname canonical=example-ha-0.example.com - ''' + """ infoblox = _get_infoblox(**api_opts) o = infoblox.get_cname(name=name, canonical=canonical, return_fields=return_fields) return o def update_cname(name, data, **api_opts): - ''' + """ Update CNAME. This is a helper call to update_object. Find a CNAME ``_ref`` then call update_object with the record data. @@ -251,15 +263,15 @@ def update_cname(name, data, **api_opts): 'use_ttl':true, 'ttl':200, 'comment':'Salt managed CNAME'}" - ''' + """ o = get_cname(name=name, **api_opts) if not o: - raise Exception('CNAME record not found') - return update_object(objref=o['_ref'], data=data, **api_opts) + raise Exception("CNAME record not found") + return update_object(objref=o["_ref"], data=data, **api_opts) def delete_cname(name=None, canonical=None, **api_opts): - ''' + """ Delete CNAME. This is a helper call to delete_object. If record is not found, return True @@ -270,15 +282,15 @@ def delete_cname(name=None, canonical=None, **api_opts): salt-call infoblox.delete_cname name=example.example.com salt-call infoblox.delete_cname canonical=example-ha-0.example.com - ''' + """ cname = get_cname(name=name, canonical=canonical, **api_opts) if cname: - return delete_object(cname['_ref'], **api_opts) + return delete_object(cname["_ref"], **api_opts) return True def get_host(name=None, ipv4addr=None, mac=None, return_fields=None, **api_opts): - ''' + """ Get host information CLI Examples: @@ -288,14 +300,16 @@ def get_host(name=None, ipv4addr=None, mac=None, return_fields=None, **api_opts) salt-call infoblox.get_host hostname.domain.ca salt-call infoblox.get_host ipv4addr=123.123.122.12 salt-call infoblox.get_host mac=00:50:56:84:6e:ae - ''' + """ infoblox = _get_infoblox(**api_opts) - host = infoblox.get_host(name=name, mac=mac, ipv4addr=ipv4addr, return_fields=return_fields) + host = infoblox.get_host( + name=name, mac=mac, ipv4addr=ipv4addr, return_fields=return_fields + ) return host def get_host_advanced(name=None, ipv4addr=None, mac=None, **api_opts): - ''' + """ Get all host information CLI Example: @@ -303,14 +317,14 @@ def get_host_advanced(name=None, ipv4addr=None, mac=None, **api_opts): .. code-block:: bash salt-call infoblox.get_host_advanced hostname.domain.ca - ''' + """ infoblox = _get_infoblox(**api_opts) host = infoblox.get_host_advanced(name=name, mac=mac, ipv4addr=ipv4addr) return host def get_host_domainname(name, domains=None, **api_opts): - ''' + """ Get host domain name If no domains are passed, the hostname is checked for a zone in infoblox, @@ -330,19 +344,19 @@ def get_host_domainname(name, domains=None, **api_opts): domains=['domain.com', 't.domain.com.'] # returns: t.domain.com - ''' - name = name.lower().rstrip('.') + """ + name = name.lower().rstrip(".") if not domains: data = get_host(name=name, **api_opts) - if data and 'zone' in data: - return data['zone'].lower() + if data and "zone" in data: + return data["zone"].lower() else: - if name.count('.') > 1: - return name[name.find('.')+1:] + if name.count(".") > 1: + return name[name.find(".") + 1 :] return name - match = '' + match = "" for d in domains: - d = d.lower().rstrip('.') + d = d.lower().rstrip(".") if name.endswith(d) and len(d) > len(match): match = d if len(match) > 0: @@ -351,7 +365,7 @@ def get_host_domainname(name, domains=None, **api_opts): def get_host_hostname(name, domains=None, **api_opts): - ''' + """ Get hostname If no domains are passed, the hostname is checked for a zone in infoblox, @@ -374,18 +388,18 @@ def get_host_hostname(name, domains=None, **api_opts): salt-call infoblox.get_host_hostname fqdn=localhost.xxx.t.domain.com #returns: localhost - ''' - name = name.lower().rstrip('.') + """ + name = name.lower().rstrip(".") if not domains: - return name.split('.')[0] + return name.split(".")[0] domain = get_host_domainname(name, domains, **api_opts) if domain and domain in name: - return name.rsplit('.' + domain)[0] + return name.rsplit("." + domain)[0] return name def get_host_mac(name=None, allow_array=False, **api_opts): - ''' + """ Get mac address from host record. Use `allow_array` to return possible multiple values. @@ -395,13 +409,13 @@ def get_host_mac(name=None, allow_array=False, **api_opts): .. code-block:: bash salt-call infoblox.get_host_mac host=localhost.domain.com - ''' + """ data = get_host(name=name, **api_opts) - if data and 'ipv4addrs' in data: + if data and "ipv4addrs" in data: l = [] - for a in data['ipv4addrs']: - if 'mac' in a: - l.append(a['mac']) + for a in data["ipv4addrs"]: + if "mac" in a: + l.append(a["mac"]) if allow_array: return l if l: @@ -410,7 +424,7 @@ def get_host_mac(name=None, allow_array=False, **api_opts): def get_host_ipv4(name=None, mac=None, allow_array=False, **api_opts): - ''' + """ Get ipv4 address from host record. Use `allow_array` to return possible multiple values. @@ -421,13 +435,13 @@ def get_host_ipv4(name=None, mac=None, allow_array=False, **api_opts): salt-call infoblox.get_host_ipv4 host=localhost.domain.com salt-call infoblox.get_host_ipv4 mac=00:50:56:84:6e:ae - ''' + """ data = get_host(name=name, mac=mac, **api_opts) - if data and 'ipv4addrs' in data: + if data and "ipv4addrs" in data: l = [] - for a in data['ipv4addrs']: - if 'ipv4addr' in a: - l.append(a['ipv4addr']) + for a in data["ipv4addrs"]: + if "ipv4addr" in a: + l.append(a["ipv4addr"]) if allow_array: return l if l: @@ -435,10 +449,10 @@ def get_host_ipv4(name=None, mac=None, allow_array=False, **api_opts): return None -def get_host_ipv4addr_info(ipv4addr=None, mac=None, - discovered_data=None, - return_fields=None, **api_opts): - ''' +def get_host_ipv4addr_info( + ipv4addr=None, mac=None, discovered_data=None, return_fields=None, **api_opts +): + """ Get host ipv4addr information CLI Examples: @@ -448,15 +462,17 @@ def get_host_ipv4addr_info(ipv4addr=None, mac=None, salt-call infoblox.get_ipv4addr ipv4addr=123.123.122.12 salt-call infoblox.get_ipv4addr mac=00:50:56:84:6e:ae salt-call infoblox.get_ipv4addr mac=00:50:56:84:6e:ae return_fields=host return_fields='mac,host,configure_for_dhcp,ipv4addr' - ''' + """ infoblox = _get_infoblox(**api_opts) - return infoblox.get_host_ipv4addr_object(ipv4addr, mac, discovered_data, return_fields) + return infoblox.get_host_ipv4addr_object( + ipv4addr, mac, discovered_data, return_fields + ) -def get_host_ipv6addr_info(ipv6addr=None, mac=None, - discovered_data=None, - return_fields=None, **api_opts): - ''' +def get_host_ipv6addr_info( + ipv6addr=None, mac=None, discovered_data=None, return_fields=None, **api_opts +): + """ Get host ipv6addr information CLI Example: @@ -464,13 +480,15 @@ def get_host_ipv6addr_info(ipv6addr=None, mac=None, .. code-block:: bash salt-call infoblox.get_host_ipv6addr_info ipv6addr=2001:db8:85a3:8d3:1349:8a2e:370:7348 - ''' + """ infoblox = _get_infoblox(**api_opts) - return infoblox.get_host_ipv6addr_object(ipv6addr, mac, discovered_data, return_fields) + return infoblox.get_host_ipv6addr_object( + ipv6addr, mac, discovered_data, return_fields + ) def get_network(ipv4addr=None, network=None, return_fields=None, **api_opts): - ''' + """ Get list of all networks. This is helpful when looking up subnets to use with func:nextavailableip @@ -484,13 +502,15 @@ def get_network(ipv4addr=None, network=None, return_fields=None, **api_opts): .. code-block:: bash salt-call infoblox.get_network - ''' + """ infoblox = _get_infoblox(**api_opts) - return infoblox.get_network(ipv4addr=ipv4addr, network=network, return_fields=return_fields) + return infoblox.get_network( + ipv4addr=ipv4addr, network=network, return_fields=return_fields + ) def delete_host(name=None, mac=None, ipv4addr=None, **api_opts): - ''' + """ Delete host CLI Example: @@ -500,15 +520,15 @@ def delete_host(name=None, mac=None, ipv4addr=None, **api_opts): salt-call infoblox.delete_host name=example.domain.com salt-call infoblox.delete_host ipv4addr=123.123.122.12 salt-call infoblox.delete_host ipv4addr=123.123.122.12 mac=00:50:56:84:6e:ae - ''' - if '__opts__' in globals() and __opts__['test']: - return {'Test': 'Would attempt to delete host'} + """ + if "__opts__" in globals() and __opts__["test"]: + return {"Test": "Would attempt to delete host"} infoblox = _get_infoblox(**api_opts) return infoblox.delete_host(name, mac, ipv4addr) def create_host(data, **api_opts): - ''' + """ Add host record Avoid race conditions, use func:nextavailableip for ipv[4,6]addrs: @@ -536,12 +556,12 @@ def create_host(data, **api_opts): 'ipv4addr': 'func:nextavailableip:129.97.139.0/24', 'mac': '00:50:56:84:6e:ae'}], 'ipv6addrs': [], } - ''' - return create_object('record:host', data, **api_opts) + """ + return create_object("record:host", data, **api_opts) def get_ipv4_range(start_addr=None, end_addr=None, return_fields=None, **api_opts): - ''' + """ Get ip range CLI Example: @@ -549,13 +569,13 @@ def get_ipv4_range(start_addr=None, end_addr=None, return_fields=None, **api_opt .. code-block:: bash salt-call infoblox.get_ipv4_range start_addr=123.123.122.12 - ''' + """ infoblox = _get_infoblox(**api_opts) return infoblox.get_range(start_addr, end_addr, return_fields) def delete_ipv4_range(start_addr=None, end_addr=None, **api_opts): - ''' + """ Delete ip range. CLI Example: @@ -563,16 +583,16 @@ def delete_ipv4_range(start_addr=None, end_addr=None, **api_opts): .. code-block:: bash salt-call infoblox.delete_ipv4_range start_addr=123.123.122.12 - ''' + """ r = get_ipv4_range(start_addr, end_addr, **api_opts) if r: - return delete_object(r['_ref'], **api_opts) + return delete_object(r["_ref"], **api_opts) else: return True def create_ipv4_range(data, **api_opts): - ''' + """ Create a ipv4 range This is a helper function to `create_object` @@ -585,12 +605,12 @@ def create_ipv4_range(data, **api_opts): salt-call infoblox.create_ipv4_range data={ start_addr: '129.97.150.160', end_addr: '129.97.150.170'} - ''' - return create_object('range', data, **api_opts) + """ + return create_object("range", data, **api_opts) def create_a(data, **api_opts): - ''' + """ Create A record. This is a helper function to `create_object`. @@ -605,12 +625,12 @@ def create_a(data, **api_opts): name: 'fastlinux.math.example.ca' ipv4addr: '127.0.0.1' view: External - ''' - return create_object('record:a', data, **api_opts) + """ + return create_object("record:a", data, **api_opts) def get_a(name=None, ipv4addr=None, allow_array=True, **api_opts): - ''' + """ Get A record CLI Examples: @@ -619,20 +639,20 @@ def get_a(name=None, ipv4addr=None, allow_array=True, **api_opts): salt-call infoblox.get_a name=abc.example.com salt-call infoblox.get_a ipv4addr=192.168.3.5 - ''' + """ data = {} if name: - data['name'] = name + data["name"] = name if ipv4addr: - data['ipv4addr'] = ipv4addr - r = get_object('record:a', data=data, **api_opts) + data["ipv4addr"] = ipv4addr + r = get_object("record:a", data=data, **api_opts) if r and len(r) > 1 and not allow_array: - raise Exception('More than one result, use allow_array to return the data') + raise Exception("More than one result, use allow_array to return the data") return r def delete_a(name=None, ipv4addr=None, allow_array=False, **api_opts): - ''' + """ Delete A record If the A record is used as a round robin you can set ``allow_array=True`` to @@ -645,15 +665,15 @@ def delete_a(name=None, ipv4addr=None, allow_array=False, **api_opts): salt-call infoblox.delete_a name=abc.example.com salt-call infoblox.delete_a ipv4addr=192.168.3.5 salt-call infoblox.delete_a name=acname.example.com allow_array=True - ''' + """ r = get_a(name, ipv4addr, allow_array=False, **api_opts) if not r: return True if len(r) == 0: return True if len(r) > 1 and not allow_array: - raise Exception('More than one result, use allow_array to override') + raise Exception("More than one result, use allow_array to override") ret = [] for ri in r: - ret.append(delete_object(ri['_ref'], **api_opts)) + ret.append(delete_object(ri["_ref"], **api_opts)) return ret diff --git a/salt/modules/ini_manage.py b/salt/modules/ini_manage.py index 365f41f93a0..e35395bbd07 100644 --- a/salt/modules/ini_manage.py +++ b/salt/modules/ini_manage.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Edit ini files :maintainer: @@ -8,9 +8,11 @@ Edit ini files :platform: all (for example /etc/sysctl.conf) -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import re @@ -20,31 +22,30 @@ import salt.utils.files import salt.utils.json import salt.utils.stringutils from salt.exceptions import CommandExecutionError -from salt.utils.odict import OrderedDict # Import 3rd-party libs from salt.ext import six +from salt.utils.odict import OrderedDict -import logging log = logging.getLogger(__name__) -__virtualname__ = 'ini' +__virtualname__ = "ini" def __virtual__(): - ''' + """ Rename to ini - ''' + """ return __virtualname__ -INI_REGX = re.compile(r'^\s*\[(.+?)\]\s*$', flags=re.M) -COM_REGX = re.compile(r'^\s*(#|;)\s*(.*)') -INDENTED_REGX = re.compile(r'(\s+)(.*)') +INI_REGX = re.compile(r"^\s*\[(.+?)\]\s*$", flags=re.M) +COM_REGX = re.compile(r"^\s*(#|;)\s*(.*)") +INDENTED_REGX = re.compile(r"(\s+)(.*)") -def set_option(file_name, sections=None, separator='='): - ''' +def set_option(file_name, sections=None, separator="="): + """ Edit an ini file, replacing one or more sections. Returns a dictionary containing the changes made. @@ -78,7 +79,7 @@ def set_option(file_name, sections=None, separator='='): .. code-block:: bash salt '*' ini.set_option /path/to/ini '{section_foo: {key: value}}' - ''' + """ sections = sections or {} changes = {} inifile = _Ini.get_ini_file(file_name, separator=separator) @@ -87,8 +88,8 @@ def set_option(file_name, sections=None, separator='='): return changes -def get_option(file_name, section, option, separator='='): - ''' +def get_option(file_name, section, option, separator="="): + """ Get value of a key from a section in an ini file. Returns ``None`` if no matching key was found. @@ -106,7 +107,7 @@ def get_option(file_name, section, option, separator='='): .. code-block:: bash salt '*' ini.get_option /path/to/ini section_name option_name - ''' + """ inifile = _Ini.get_ini_file(file_name, separator=separator) if section: try: @@ -117,8 +118,8 @@ def get_option(file_name, section, option, separator='='): return inifile.get(option, None) -def remove_option(file_name, section, option, separator='='): - ''' +def remove_option(file_name, section, option, separator="="): + """ Remove a key/value pair from a section in an ini file. Returns the value of the removed key, or ``None`` if nothing was removed. @@ -136,7 +137,7 @@ def remove_option(file_name, section, option, separator='='): .. code-block:: bash salt '*' ini.remove_option /path/to/ini section_name option_name - ''' + """ inifile = _Ini.get_ini_file(file_name, separator=separator) if isinstance(inifile.get(section), (dict, OrderedDict)): value = inifile.get(section, {}).pop(option, None) @@ -146,8 +147,8 @@ def remove_option(file_name, section, option, separator='='): return value -def get_section(file_name, section, separator='='): - ''' +def get_section(file_name, section, separator="="): + """ Retrieve a section from an ini file. Returns the section as dictionary. If the section is not found, an empty dictionary is returned. @@ -165,17 +166,17 @@ def get_section(file_name, section, separator='='): .. code-block:: bash salt '*' ini.get_section /path/to/ini section_name - ''' + """ inifile = _Ini.get_ini_file(file_name, separator=separator) ret = {} for key, value in six.iteritems(inifile.get(section, {})): - if key[0] != '#': + if key[0] != "#": ret.update({key: value}) return ret -def remove_section(file_name, section, separator='='): - ''' +def remove_section(file_name, section, separator="="): + """ Remove a section in an ini file. Returns the removed section as dictionary, or ``None`` if nothing was removed. @@ -193,20 +194,20 @@ def remove_section(file_name, section, separator='='): .. code-block:: bash salt '*' ini.remove_section /path/to/ini section_name - ''' + """ inifile = _Ini.get_ini_file(file_name, separator=separator) if section in inifile: section = inifile.pop(section) inifile.flush() ret = {} for key, value in six.iteritems(section): - if key[0] != '#': + if key[0] != "#": ret.update({key: value}) return ret -def get_ini(file_name, separator='='): - ''' +def get_ini(file_name, separator="="): + """ Retrieve whole structure from an ini file and return it as dictionary. API Example: @@ -223,16 +224,17 @@ def get_ini(file_name, separator='='): .. code-block:: bash salt '*' ini.get_ini /path/to/ini - ''' + """ + def ini_odict2dict(odict): - ''' + """ Transform OrderedDict to regular dict recursively :param odict: OrderedDict :return: regular dict - ''' + """ ret = {} for key, val in six.iteritems(odict): - if key[0] != '#': + if key[0] != "#": if isinstance(val, (dict, OrderedDict)): ret.update({key: ini_odict2dict(val)}) else: @@ -244,16 +246,16 @@ def get_ini(file_name, separator='='): class _Section(OrderedDict): - def __init__(self, name, inicontents='', separator='=', commenter='#'): + def __init__(self, name, inicontents="", separator="=", commenter="#"): super(_Section, self).__init__(self) self.name = name self.inicontents = inicontents self.sep = separator self.com = commenter - opt_regx_prefix = r'(\s*)(.+?)\s*' - opt_regx_suffix = r'\s*(.*)\s*' - self.opt_regx_str = r'{0}(\{1}){2}'.format( + opt_regx_prefix = r"(\s*)(.+?)\s*" + opt_regx_suffix = r"\s*(.*)\s*" + self.opt_regx_str = r"{0}(\{1}){2}".format( opt_regx_prefix, self.sep, opt_regx_suffix ) self.opt_regx = re.compile(self.opt_regx_str) @@ -261,7 +263,7 @@ class _Section(OrderedDict): def refresh(self, inicontents=None): comment_count = 1 unknown_count = 1 - curr_indent = '' + curr_indent = "" inicontents = inicontents or self.inicontents inicontents = inicontents.strip(os.linesep) @@ -273,7 +275,7 @@ class _Section(OrderedDict): # Match comments com_match = COM_REGX.match(opt_str) if com_match: - name = '#comment{0}'.format(comment_count) + name = "#comment{0}".format(comment_count) self.com = com_match.group(1) comment_count += 1 self.update({name: opt_str}) @@ -281,7 +283,7 @@ class _Section(OrderedDict): # Add indented lines to the value of the previous entry. indented_match = INDENTED_REGX.match(opt_str) if indented_match: - indent = indented_match.group(1).replace('\t', ' ') + indent = indented_match.group(1).replace("\t", " ") if indent > curr_indent: options = list(self) if options: @@ -293,11 +295,11 @@ class _Section(OrderedDict): opt_match = self.opt_regx.match(opt_str) if opt_match: curr_indent, name, self.sep, value = opt_match.groups() - curr_indent = curr_indent.replace('\t', ' ') + curr_indent = curr_indent.replace("\t", " ") self.update({name: value}) continue # Anything remaining is a mystery. - name = '#unknown{0}'.format(unknown_count) + name = "#unknown{0}".format(unknown_count) self.update({name: opt_str}) unknown_count += 1 @@ -313,9 +315,9 @@ class _Section(OrderedDict): if comment_index is not None: options_backup.update({key: value}) continue - if '#comment' not in key: + if "#comment" not in key: continue - opt_match = self.opt_regx.match(value.lstrip('#')) + opt_match = self.opt_regx.match(value.lstrip("#")) if opt_match and opt_match.group(2) == opt_key: comment_index = key for key in options_backup: @@ -331,8 +333,7 @@ class _Section(OrderedDict): # Ensure the value is either a _Section or a string if isinstance(value, (dict, OrderedDict)): sect = _Section( - name=key, inicontents='', - separator=self.sep, commenter=self.com + name=key, inicontents="", separator=self.sep, commenter=self.com ) sect.update(value) value = sect @@ -342,8 +343,7 @@ class _Section(OrderedDict): value_plain = value if key not in self: - changes.update({key: {'before': None, - 'after': value_plain}}) + changes.update({key: {"before": None, "after": value_plain}}) # If it's not a section, it may already exist as a # commented-out key/value pair if not isinstance(value, _Section): @@ -358,36 +358,37 @@ class _Section(OrderedDict): changes.update({key: sub_changes}) else: if curr_value != value: - changes.update({key: {'before': curr_value, - 'after': value_plain}}) + changes.update( + {key: {"before": curr_value, "after": value_plain}} + ) super(_Section, self).update({key: value}) return changes def gen_ini(self): - yield '{0}[{1}]{0}'.format(os.linesep, self.name) + yield "{0}[{1}]{0}".format(os.linesep, self.name) sections_dict = OrderedDict() for name, value in six.iteritems(self): # Handle Comment Lines if COM_REGX.match(name): - yield '{0}{1}'.format(value, os.linesep) + yield "{0}{1}".format(value, os.linesep) # Handle Sections elif isinstance(value, _Section): sections_dict.update({name: value}) # Key / Value pairs # Adds spaces between the separator else: - yield '{0}{1}{2}{3}'.format( + yield "{0}{1}{2}{3}".format( name, - ' {0} '.format(self.sep) if self.sep != ' ' else self.sep, + " {0} ".format(self.sep) if self.sep != " " else self.sep, value, - os.linesep + os.linesep, ) for name, value in six.iteritems(sections_dict): for line in value.gen_ini(): yield line def as_ini(self): - return ''.join(self.gen_ini()) + return "".join(self.gen_ini()) def as_dict(self): return dict(self) @@ -404,12 +405,10 @@ class _Section(OrderedDict): return salt.utils.json.dumps(self, indent=4) def __eq__(self, item): - return (isinstance(item, self.__class__) and - self.name == item.name) + return isinstance(item, self.__class__) and self.name == item.name def __ne__(self, item): - return not (isinstance(item, self.__class__) and - self.name == item.name) + return not (isinstance(item, self.__class__) and self.name == item.name) class _Ini(_Section): @@ -419,7 +418,7 @@ class _Ini(_Section): with salt.utils.files.fopen(self.name) as rfh: inicontents = salt.utils.stringutils.to_unicode(rfh.read()) except (OSError, IOError) as exc: - if __opts__['test'] is False: + if __opts__["test"] is False: raise CommandExecutionError( "Unable to open file '{0}'. " "Exception: {1}".format(self.name, exc) @@ -436,9 +435,7 @@ class _Ini(_Section): super(_Ini, self).refresh(inicontents.pop()) for section_name, sect_ini in self._gen_tuples(inicontents): try: - sect_obj = _Section( - section_name, sect_ini, separator=self.sep - ) + sect_obj = _Section(section_name, sect_ini, separator=self.sep) sect_obj.refresh() self.update({sect_obj.name: sect_obj}) except StopIteration: @@ -446,18 +443,17 @@ class _Ini(_Section): def flush(self): try: - with salt.utils.files.fopen(self.name, 'wb') as outfile: + with salt.utils.files.fopen(self.name, "wb") as outfile: ini_gen = self.gen_ini() next(ini_gen) outfile.writelines(salt.utils.data.encode(list(ini_gen))) except (OSError, IOError) as exc: raise CommandExecutionError( - "Unable to write file '{0}'. " - "Exception: {1}".format(self.name, exc) + "Unable to write file '{0}'. " "Exception: {1}".format(self.name, exc) ) @staticmethod - def get_ini_file(file_name, separator='='): + def get_ini_file(file_name, separator="="): inifile = _Ini(file_name, separator=separator) inifile.refresh() return inifile diff --git a/salt/modules/inspectlib/__init__.py b/salt/modules/inspectlib/__init__.py index 4e1b84e3d61..21a539ab520 100644 --- a/salt/modules/inspectlib/__init__.py +++ b/salt/modules/inspectlib/__init__.py @@ -16,36 +16,41 @@ # Import Python libs from __future__ import absolute_import + import os +from salt.modules.inspectlib.dbhandle import DBHandle + # Import Salt libs from salt.modules.inspectlib.exceptions import InspectorSnapshotException -from salt.modules.inspectlib.dbhandle import DBHandle class EnvLoader(object): - ''' + """ Load environment. - ''' - PID_FILE = '_minion_collector.pid' - DB_FILE = '_minion_collector.db' - DEFAULT_PID_PATH = '/var/run' - DEFAULT_CACHE_PATH = '/var/cache/salt' + """ + + PID_FILE = "_minion_collector.pid" + DB_FILE = "_minion_collector.db" + DEFAULT_PID_PATH = "/var/run" + DEFAULT_CACHE_PATH = "/var/cache/salt" def __init__(self, cachedir=None, piddir=None, pidfilename=None): - ''' + """ Constructor. :param options: :param db_path: :param pid_file: - ''' - if not cachedir and '__salt__' in globals(): - cachedir = globals().get('__salt__')['config.get']('inspector.db', '') + """ + if not cachedir and "__salt__" in globals(): + cachedir = globals().get("__salt__")["config.get"]("inspector.db", "") self.dbfile = os.path.join(cachedir or self.DEFAULT_CACHE_PATH, self.DB_FILE) self.db = DBHandle(self.dbfile) - if not piddir and '__salt__' in globals(): - piddir = globals().get('__salt__')['config.get']('inspector.pid', '') - self.pidfile = os.path.join(piddir or self.DEFAULT_PID_PATH, pidfilename or self.PID_FILE) + if not piddir and "__salt__" in globals(): + piddir = globals().get("__salt__")["config.get"]("inspector.pid", "") + self.pidfile = os.path.join( + piddir or self.DEFAULT_PID_PATH, pidfilename or self.PID_FILE + ) diff --git a/salt/modules/inspectlib/collector.py b/salt/modules/inspectlib/collector.py index e199def8925..b516abf473f 100644 --- a/salt/modules/inspectlib/collector.py +++ b/salt/modules/inspectlib/collector.py @@ -16,17 +16,11 @@ # Import Python Libs from __future__ import absolute_import, print_function + +import logging import os import sys -from subprocess import Popen, PIPE, STDOUT -import logging - -# Import Salt Libs -from salt.modules.inspectlib.exceptions import (InspectorSnapshotException) -from salt.modules.inspectlib import EnvLoader -from salt.modules.inspectlib import kiwiproc -from salt.modules.inspectlib.entities import (AllowedDir, IgnoredDir, Package, - PayloadFile, PackageCfgFile) +from subprocess import PIPE, STDOUT, Popen import salt.utils.crypt import salt.utils.files @@ -34,6 +28,17 @@ import salt.utils.fsutils import salt.utils.path import salt.utils.stringutils from salt.exceptions import CommandExecutionError +from salt.modules.inspectlib import EnvLoader, kiwiproc +from salt.modules.inspectlib.entities import ( + AllowedDir, + IgnoredDir, + Package, + PackageCfgFile, + PayloadFile, +) + +# Import Salt Libs +from salt.modules.inspectlib.exceptions import InspectorSnapshotException try: import kiwi @@ -44,72 +49,92 @@ log = logging.getLogger(__name__) class Inspector(EnvLoader): - DEFAULT_MINION_CONFIG_PATH = '/etc/salt/minion' + DEFAULT_MINION_CONFIG_PATH = "/etc/salt/minion" - MODE = ['configuration', 'payload', 'all'] + MODE = ["configuration", "payload", "all"] IGNORE_MOUNTS = ["proc", "sysfs", "devtmpfs", "tmpfs", "fuse.gvfs-fuse-daemon"] IGNORE_FS_TYPES = ["autofs", "cifs", "nfs", "nfs4"] - IGNORE_PATHS = ["/tmp", "/var/tmp", "/lost+found", "/var/run", - "/var/lib/rpm", "/.snapshots", "/.zfs", "/etc/ssh", - "/root", "/home"] + IGNORE_PATHS = [ + "/tmp", + "/var/tmp", + "/lost+found", + "/var/run", + "/var/lib/rpm", + "/.snapshots", + "/.zfs", + "/etc/ssh", + "/root", + "/home", + ] def __init__(self, cachedir=None, piddir=None, pidfilename=None): - EnvLoader.__init__(self, cachedir=cachedir, piddir=piddir, pidfilename=pidfilename) + EnvLoader.__init__( + self, cachedir=cachedir, piddir=piddir, pidfilename=pidfilename + ) def create_snapshot(self): - ''' + """ Open new snapshot. :return: - ''' + """ self.db.open(new=True) return self def reuse_snapshot(self): - ''' + """ Open an existing, latest snapshot. :return: - ''' + """ self.db.open() return self def _syscall(self, command, input=None, env=None, *params): - ''' + """ Call an external system command. - ''' - return Popen([command] + list(params), stdout=PIPE, stdin=PIPE, stderr=STDOUT, - env=env or os.environ).communicate(input=input) + """ + return Popen( + [command] + list(params), + stdout=PIPE, + stdin=PIPE, + stderr=STDOUT, + env=env or os.environ, + ).communicate(input=input) def _get_cfg_pkgs(self): - ''' + """ Package scanner switcher between the platforms. :return: - ''' - if self.grains_core.os_data().get('os_family') == 'Debian': + """ + if self.grains_core.os_data().get("os_family") == "Debian": return self.__get_cfg_pkgs_dpkg() - elif self.grains_core.os_data().get('os_family') in ['Suse', 'redhat']: + elif self.grains_core.os_data().get("os_family") in ["Suse", "redhat"]: return self.__get_cfg_pkgs_rpm() else: return dict() def __get_cfg_pkgs_dpkg(self): - ''' + """ Get packages with configuration files on Dpkg systems. :return: - ''' + """ # Get list of all available packages data = dict() - for pkg_name in salt.utils.stringutils.to_str(self._syscall('dpkg-query', None, None, - '-Wf', "${binary:Package}\\n")[0]).split(os.linesep): + for pkg_name in salt.utils.stringutils.to_str( + self._syscall("dpkg-query", None, None, "-Wf", "${binary:Package}\\n")[0] + ).split(os.linesep): pkg_name = pkg_name.strip() if not pkg_name: continue data[pkg_name] = list() - for pkg_cfg_item in salt.utils.stringutils.to_str(self._syscall('dpkg-query', None, None, '-Wf', "${Conffiles}\\n", - pkg_name)[0]).split(os.linesep): + for pkg_cfg_item in salt.utils.stringutils.to_str( + self._syscall( + "dpkg-query", None, None, "-Wf", "${Conffiles}\\n", pkg_name + )[0] + ).split(os.linesep): pkg_cfg_item = pkg_cfg_item.strip() if not pkg_cfg_item: continue @@ -124,11 +149,18 @@ class Inspector(EnvLoader): return data def __get_cfg_pkgs_rpm(self): - ''' + """ Get packages with configuration files on RPM systems. - ''' - out, err = self._syscall('rpm', None, None, '-qa', '--configfiles', - '--queryformat', '%{name}-%{version}-%{release}\\n') + """ + out, err = self._syscall( + "rpm", + None, + None, + "-qa", + "--configfiles", + "--queryformat", + "%{name}-%{version}-%{release}\\n", + ) data = dict() pkg_name = None pkg_configs = [] @@ -152,23 +184,35 @@ class Inspector(EnvLoader): return data def _get_changed_cfg_pkgs(self, data): - ''' + """ Filter out unchanged packages on the Debian or RPM systems. :param data: Structure {package-name -> [ file .. file1 ]} :return: Same structure as data, except only files that were changed. - ''' + """ f_data = dict() for pkg_name, pkg_files in data.items(): cfgs = list() cfg_data = list() - if self.grains_core.os_data().get('os_family') == 'Debian': - cfg_data = salt.utils.stringutils.to_str(self._syscall("dpkg", None, None, '--verify', - pkg_name)[0]).split(os.linesep) - elif self.grains_core.os_data().get('os_family') in ['Suse', 'redhat']: - cfg_data = salt.utils.stringutils.to_str(self._syscall("rpm", None, None, '-V', '--nodeps', '--nodigest', - '--nosignature', '--nomtime', '--nolinkto', - pkg_name)[0]).split(os.linesep) + if self.grains_core.os_data().get("os_family") == "Debian": + cfg_data = salt.utils.stringutils.to_str( + self._syscall("dpkg", None, None, "--verify", pkg_name)[0] + ).split(os.linesep) + elif self.grains_core.os_data().get("os_family") in ["Suse", "redhat"]: + cfg_data = salt.utils.stringutils.to_str( + self._syscall( + "rpm", + None, + None, + "-V", + "--nodeps", + "--nodigest", + "--nosignature", + "--nomtime", + "--nolinkto", + pkg_name, + )[0] + ).split(os.linesep) for line in cfg_data: line = line.strip() if not line or line.find(" c ") < 0 or line.split(" ")[0].find("5") < 0: @@ -182,12 +226,12 @@ class Inspector(EnvLoader): return f_data def _save_cfg_packages(self, data): - ''' + """ Save configuration packages. (NG) :param data: :return: - ''' + """ pkg_id = 0 pkg_cfg_id = 0 for pkg_name, pkg_configs in data.items(): @@ -207,17 +251,21 @@ class Inspector(EnvLoader): pkg_id += 1 def _save_payload(self, files, directories, links): - ''' + """ Save payload (unmanaged files) :param files: :param directories: :param links: :return: - ''' + """ idx = 0 - for p_type, p_list in (('f', files), ('d', directories), ('l', links,),): + for p_type, p_list in ( + ("f", files), + ("d", directories), + ("l", links,), + ): for p_obj in p_list: stats = os.stat(p_obj) @@ -237,32 +285,35 @@ class Inspector(EnvLoader): self.db.store(payload) def _get_managed_files(self): - ''' + """ Build a in-memory data of all managed files. - ''' - if self.grains_core.os_data().get('os_family') == 'Debian': + """ + if self.grains_core.os_data().get("os_family") == "Debian": return self.__get_managed_files_dpkg() - elif self.grains_core.os_data().get('os_family') in ['Suse', 'redhat']: + elif self.grains_core.os_data().get("os_family") in ["Suse", "redhat"]: return self.__get_managed_files_rpm() return list(), list(), list() def __get_managed_files_dpkg(self): - ''' + """ Get a list of all system files, belonging to the Debian package manager. - ''' + """ dirs = set() links = set() files = set() - for pkg_name in salt.utils.stringutils.to_str(self._syscall("dpkg-query", None, None, - '-Wf', '${binary:Package}\\n')[0]).split(os.linesep): + for pkg_name in salt.utils.stringutils.to_str( + self._syscall("dpkg-query", None, None, "-Wf", "${binary:Package}\\n")[0] + ).split(os.linesep): pkg_name = pkg_name.strip() if not pkg_name: continue - for resource in salt.utils.stringutils.to_str(self._syscall("dpkg", None, None, '-L', pkg_name)[0]).split(os.linesep): + for resource in salt.utils.stringutils.to_str( + self._syscall("dpkg", None, None, "-L", pkg_name)[0] + ).split(os.linesep): resource = resource.strip() - if not resource or resource in ['/', './', '.']: + if not resource or resource in ["/", "./", "."]: continue if os.path.isdir(resource): dirs.add(resource) @@ -274,14 +325,16 @@ class Inspector(EnvLoader): return sorted(files), sorted(dirs), sorted(links) def __get_managed_files_rpm(self): - ''' + """ Get a list of all system files, belonging to the RedHat package manager. - ''' + """ dirs = set() links = set() files = set() - for line in salt.utils.stringutils.to_str(self._syscall("rpm", None, None, '-qlav')[0]).split(os.linesep): + for line in salt.utils.stringutils.to_str( + self._syscall("rpm", None, None, "-qlav")[0] + ).split(os.linesep): line = line.strip() if not line: continue @@ -296,9 +349,9 @@ class Inspector(EnvLoader): return sorted(files), sorted(dirs), sorted(links) def _get_all_files(self, path, *exclude): - ''' + """ Walk implementation. Version in python 2.x and 3.x works differently. - ''' + """ files = list() dirs = list() links = list() @@ -327,20 +380,22 @@ class Inspector(EnvLoader): return sorted(files), sorted(dirs), sorted(links) def _get_unmanaged_files(self, managed, system_all): - ''' + """ Get the intersection between all files and managed files. - ''' + """ m_files, m_dirs, m_links = managed s_files, s_dirs, s_links = system_all - return (sorted(list(set(s_files).difference(m_files))), - sorted(list(set(s_dirs).difference(m_dirs))), - sorted(list(set(s_links).difference(m_links)))) + return ( + sorted(list(set(s_files).difference(m_files))), + sorted(list(set(s_dirs).difference(m_dirs))), + sorted(list(set(s_links).difference(m_links))), + ) def _scan_payload(self): - ''' + """ Scan the system. - ''' + """ # Get ignored points allowed = list() for allowed_dir in self.db.get(AllowedDir): @@ -366,12 +421,14 @@ class Inspector(EnvLoader): all_dirs.extend(e_dirs) all_links.extend(e_links) - return self._get_unmanaged_files(self._get_managed_files(), (all_files, all_dirs, all_links,)) + return self._get_unmanaged_files( + self._get_managed_files(), (all_files, all_dirs, all_links,) + ) def _prepare_full_scan(self, **kwargs): - ''' + """ Prepare full system scan by setting up the database etc. - ''' + """ self.db.open(new=True) # Add ignored filesystems ignored_fs = set() @@ -380,11 +437,11 @@ class Inspector(EnvLoader): for device, data in mounts.items(): if device in self.IGNORE_MOUNTS: for mpt in data: - ignored_fs.add(mpt['mount_point']) + ignored_fs.add(mpt["mount_point"]) continue for mpt in data: - if mpt['type'] in self.IGNORE_FS_TYPES: - ignored_fs.add(mpt['mount_point']) + if mpt["type"] in self.IGNORE_FS_TYPES: + ignored_fs.add(mpt["mount_point"]) # Remove leafs of ignored filesystems ignored_all = list() @@ -412,60 +469,71 @@ class Inspector(EnvLoader): return ignored_all def _init_env(self): - ''' + """ Initialize some Salt environment. - ''' + """ from salt.config import minion_config from salt.grains import core as g_core + g_core.__opts__ = minion_config(self.DEFAULT_MINION_CONFIG_PATH) self.grains_core = g_core def snapshot(self, mode): - ''' + """ Take a snapshot of the system. - ''' + """ self._init_env() self._save_cfg_packages(self._get_changed_cfg_pkgs(self._get_cfg_pkgs())) self._save_payload(*self._scan_payload()) def request_snapshot(self, mode, priority=19, **kwargs): - ''' + """ Take a snapshot of the system. - ''' + """ if mode not in self.MODE: raise InspectorSnapshotException("Unknown mode: '{0}'".format(mode)) if is_alive(self.pidfile): - raise CommandExecutionError('Inspection already in progress.') + raise CommandExecutionError("Inspection already in progress.") self._prepare_full_scan(**kwargs) - os.system("nice -{0} python {1} {2} {3} {4} & > /dev/null".format( - priority, __file__, os.path.dirname(self.pidfile), os.path.dirname(self.dbfile), mode)) + os.system( + "nice -{0} python {1} {2} {3} {4} & > /dev/null".format( + priority, + __file__, + os.path.dirname(self.pidfile), + os.path.dirname(self.dbfile), + mode, + ) + ) - def export(self, description, local=False, path='/tmp', format='qcow2'): - ''' + def export(self, description, local=False, path="/tmp", format="qcow2"): + """ Export description for Kiwi. :param local: :param path: :return: - ''' + """ kiwiproc.__salt__ = __salt__ - return kiwiproc.KiwiExporter(grains=__grains__, - format=format).load(**description).export('something') + return ( + kiwiproc.KiwiExporter(grains=__grains__, format=format) + .load(**description) + .export("something") + ) - def build(self, format='qcow2', path='/tmp'): - ''' + def build(self, format="qcow2", path="/tmp"): + """ Build an image using Kiwi. :param format: :param path: :return: - ''' + """ if kiwi is None: - msg = 'Unable to build the image due to the missing dependencies: Kiwi module is not available.' + msg = "Unable to build the image due to the missing dependencies: Kiwi module is not available." log.error(msg) raise CommandExecutionError(msg) @@ -473,9 +541,9 @@ class Inspector(EnvLoader): def is_alive(pidfile): - ''' + """ Check if PID is still alive. - ''' + """ try: with salt.utils.files.fopen(pidfile) as fp_: os.kill(int(fp_.read().strip()), 0) @@ -487,13 +555,13 @@ def is_alive(pidfile): def main(dbfile, pidfile, mode): - ''' + """ Main analyzer routine. - ''' + """ Inspector(dbfile, pidfile).reuse_snapshot().snapshot(mode) -if __name__ == '__main__': +if __name__ == "__main__": if len(sys.argv) != 4: print("This module is not intended to use directly!", file=sys.stderr) sys.exit(1) @@ -519,8 +587,10 @@ if __name__ == '__main__': pid = os.fork() if pid > 0: salt.utils.crypt.reinit_crypto() - with salt.utils.files.fopen(os.path.join(pidfile, EnvLoader.PID_FILE), 'w') as fp_: - fp_.write('{0}\n'.format(pid)) + with salt.utils.files.fopen( + os.path.join(pidfile, EnvLoader.PID_FILE), "w" + ) as fp_: + fp_.write("{0}\n".format(pid)) sys.exit(0) except OSError as ex: sys.exit(1) diff --git a/salt/modules/inspectlib/dbhandle.py b/salt/modules/inspectlib/dbhandle.py index 4bb4a202bef..b5d20b13056 100644 --- a/salt/modules/inspectlib/dbhandle.py +++ b/salt/modules/inspectlib/dbhandle.py @@ -16,68 +16,74 @@ # Import Python LIbs from __future__ import absolute_import + +from salt.modules.inspectlib.entities import ( + AllowedDir, + IgnoredDir, + Package, + PackageCfgFile, + PayloadFile, +) from salt.modules.inspectlib.fsdb import CsvDB -from salt.modules.inspectlib.entities import (Package, PackageCfgFile, - PayloadFile, IgnoredDir, AllowedDir) class DBHandleBase(object): - ''' + """ Handle for the *volatile* database, which serves the purpose of caching the inspected data. This database can be destroyed or corrupted, so it should be simply re-created from scratch. - ''' + """ def __init__(self, path): - ''' + """ Constructor. - ''' + """ self._path = path self.init_queries = list() self._db = CsvDB(self._path) def open(self, new=False): - ''' + """ Init the database, if required. - ''' + """ self._db.new() if new else self._db.open() # pylint: disable=W0106 self._run_init_queries() def _run_init_queries(self): - ''' + """ Initialization queries - ''' + """ for obj in (Package, PackageCfgFile, PayloadFile, IgnoredDir, AllowedDir): self._db.create_table_from_object(obj()) def purge(self): - ''' + """ Purge whole database. - ''' + """ for table_name in self._db.list_tables(): self._db.flush(table_name) self._run_init_queries() def flush(self, table): - ''' + """ Flush the table. - ''' + """ self._db.flush(table) def close(self): - ''' + """ Close the database connection. - ''' + """ self._db.close() def __getattr__(self, item): - ''' + """ Proxy methods from the Database instance. :param item: :return: - ''' + """ return getattr(self._db, item) @@ -85,18 +91,18 @@ class DBHandle(DBHandleBase): __instance = None def __new__(cls, *args, **kwargs): - ''' + """ Keep singleton. - ''' + """ if not cls.__instance: cls.__instance = super(DBHandle, cls).__new__(cls) return cls.__instance def __init__(self, path): - ''' + """ Database handle for the specific :param path: :return: - ''' + """ DBHandleBase.__init__(self, path) diff --git a/salt/modules/inspectlib/entities.py b/salt/modules/inspectlib/entities.py index 35d282a4ff3..196c6d01fe4 100644 --- a/salt/modules/inspectlib/entities.py +++ b/salt/modules/inspectlib/entities.py @@ -15,66 +15,72 @@ # limitations under the License. from __future__ import absolute_import + from salt.modules.inspectlib.fsdb import CsvDBEntity class IgnoredDir(CsvDBEntity): - ''' + """ Ignored directories - ''' - _TABLE = 'inspector_ignored' + """ + + _TABLE = "inspector_ignored" def __init__(self): - self.path = '' + self.path = "" class AllowedDir(CsvDBEntity): - ''' + """ Allowed directories - ''' - _TABLE = 'inspector_allowed' + """ + + _TABLE = "inspector_allowed" def __init__(self): - self.path = '' + self.path = "" class Package(CsvDBEntity): - ''' + """ Package. - ''' - _TABLE = 'inspector_pkg' + """ + + _TABLE = "inspector_pkg" def __init__(self): self.id = 0 - self.name = '' + self.name = "" class PackageCfgFile(CsvDBEntity): - ''' + """ Config file, belongs to the package - ''' - _TABLE = 'inspector_pkg_cfg_files' + """ + + _TABLE = "inspector_pkg_cfg_files" def __init__(self): self.id = 0 self.pkgid = 0 - self.path = '' + self.path = "" class PayloadFile(CsvDBEntity): - ''' + """ Payload file. - ''' - _TABLE = 'inspector_payload' + """ + + _TABLE = "inspector_payload" def __init__(self): self.id = 0 - self.path = '' - self.p_type = '' + self.path = "" + self.p_type = "" self.mode = 0 self.uid = 0 self.gid = 0 self.p_size = 0 - self.atime = 0. - self.mtime = 0. - self.ctime = 0. + self.atime = 0.0 + self.mtime = 0.0 + self.ctime = 0.0 diff --git a/salt/modules/inspectlib/exceptions.py b/salt/modules/inspectlib/exceptions.py index ef8c8508ad3..6852b7fea41 100644 --- a/salt/modules/inspectlib/exceptions.py +++ b/salt/modules/inspectlib/exceptions.py @@ -16,24 +16,24 @@ class InspectorSnapshotException(Exception): - ''' + """ Snapshot exception. - ''' + """ class InspectorQueryException(Exception): - ''' + """ Exception that is only for the inspector query. - ''' + """ class SIException(Exception): - ''' + """ System information exception. - ''' + """ class InspectorKiwiProcessorException(Exception): - ''' + """ Kiwi builder/exporter exception. - ''' + """ diff --git a/salt/modules/inspectlib/fsdb.py b/salt/modules/inspectlib/fsdb.py index 1d0b2ddfba0..2874932b0a4 100644 --- a/salt/modules/inspectlib/fsdb.py +++ b/salt/modules/inspectlib/fsdb.py @@ -14,46 +14,50 @@ # See the License for the specific language governing permissions and # limitations under the License. -''' +""" :codeauthor: Bo Maryniuk -''' +""" from __future__ import absolute_import, with_statement -import os -import sys -import re + import csv import datetime import gzip +import os +import re import shutil -from salt.utils.odict import OrderedDict +import sys + from salt.ext.six.moves import zip +from salt.utils.odict import OrderedDict class CsvDBEntity(object): - ''' + """ Serializable object for the table. - ''' + """ + def _serialize(self, description): - ''' + """ Serialize the object to a row for CSV according to the table description. :return: - ''' + """ return [getattr(self, attr) for attr in description] class CsvDB(object): - ''' + """ File-based CSV database. This database is in-memory operating relatively small plain text csv files. - ''' + """ + def __init__(self, path): - ''' + """ Constructor to store the database files. :param path: - ''' + """ self._prepare(path) self._opened = False self.db_path = None @@ -66,19 +70,19 @@ class CsvDB(object): os.makedirs(self.path) def _label(self): - ''' + """ Create label of the database, based on the date-time. :return: - ''' - return datetime.datetime.utcnow().strftime('%Y%m%d-%H%M%S') + """ + return datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%S") def new(self): - ''' + """ Create a new database and opens it. :return: - ''' + """ dbname = self._label() self.db_path = os.path.join(self.path, dbname) if not os.path.exists(self.db_path): @@ -89,12 +93,12 @@ class CsvDB(object): return dbname def purge(self, dbid): - ''' + """ Purge the database. :param dbid: :return: - ''' + """ db_path = os.path.join(self.path, dbid) if os.path.exists(db_path): shutil.rmtree(db_path, ignore_errors=True) @@ -102,33 +106,33 @@ class CsvDB(object): return False def flush(self, table): - ''' + """ Flush table. :param table: :return: - ''' + """ table_path = os.path.join(self.db_path, table) if os.path.exists(table_path): os.unlink(table_path) def list(self): - ''' + """ List all the databases on the given path. :return: - ''' + """ databases = [] for dbname in os.listdir(self.path): databases.append(dbname) return list(reversed(sorted(databases))) def list_tables(self): - ''' + """ Load existing tables and their descriptions. :return: - ''' + """ if not self._tables: for table_name in os.listdir(self.db_path): self._tables[table_name] = self._load_table(table_name) @@ -136,74 +140,86 @@ class CsvDB(object): return self._tables.keys() def _load_table(self, table_name): - with gzip.open(os.path.join(self.db_path, table_name), 'rb') as table: - return OrderedDict([tuple(elm.split(':')) for elm in next(csv.reader(table))]) + with gzip.open(os.path.join(self.db_path, table_name), "rb") as table: + return OrderedDict( + [tuple(elm.split(":")) for elm in next(csv.reader(table))] + ) def open(self, dbname=None): - ''' + """ Open database from the path with the name or latest. If there are no yet databases, create a new implicitly. :return: - ''' + """ databases = self.list() if self.is_closed(): - self.db_path = os.path.join(self.path, dbname or (databases and databases[0] or self.new())) + self.db_path = os.path.join( + self.path, dbname or (databases and databases[0] or self.new()) + ) if not self._opened: self.list_tables() self._opened = True def close(self): - ''' + """ Close the database. :return: - ''' + """ self._opened = False def is_closed(self): - ''' + """ Return if the database is closed. :return: - ''' + """ return not self._opened def create_table_from_object(self, obj): - ''' + """ Create a table from the object. NOTE: This method doesn't stores anything. :param obj: :return: - ''' + """ get_type = lambda item: str(type(item)).split("'")[1] if not os.path.exists(os.path.join(self.db_path, obj._TABLE)): - with gzip.open(os.path.join(self.db_path, obj._TABLE), 'wb') as table_file: - csv.writer(table_file).writerow(['{col}:{type}'.format(col=elm[0], type=get_type(elm[1])) - for elm in tuple(obj.__dict__.items())]) + with gzip.open(os.path.join(self.db_path, obj._TABLE), "wb") as table_file: + csv.writer(table_file).writerow( + [ + "{col}:{type}".format(col=elm[0], type=get_type(elm[1])) + for elm in tuple(obj.__dict__.items()) + ] + ) self._tables[obj._TABLE] = self._load_table(obj._TABLE) def store(self, obj, distinct=False): - ''' + """ Store an object in the table. :param obj: An object to store :param distinct: Store object only if there is none identical of such. If at least one field is different, store it. :return: - ''' + """ if distinct: - fields = dict(zip(self._tables[obj._TABLE].keys(), - obj._serialize(self._tables[obj._TABLE]))) + fields = dict( + zip( + self._tables[obj._TABLE].keys(), + obj._serialize(self._tables[obj._TABLE]), + ) + ) db_obj = self.get(obj.__class__, eq=fields) if db_obj and distinct: raise Exception("Object already in the database.") - with gzip.open(os.path.join(self.db_path, obj._TABLE), 'a') as table: + with gzip.open(os.path.join(self.db_path, obj._TABLE), "a") as table: csv.writer(table).writerow(self._validate_object(obj)) def update(self, obj, matches=None, mt=None, lt=None, eq=None): - ''' + """ Update object(s) in the database. :param obj: @@ -212,7 +228,7 @@ class CsvDB(object): :param lt: :param eq: :return: - ''' + """ updated = False objects = list() for _obj in self.get(obj.__class__): @@ -229,7 +245,7 @@ class CsvDB(object): return updated def delete(self, obj, matches=None, mt=None, lt=None, eq=None): - ''' + """ Delete object from the database. :param obj: @@ -238,7 +254,7 @@ class CsvDB(object): :param lt: :param eq: :return: - ''' + """ deleted = False objects = list() for _obj in self.get(obj): @@ -257,11 +273,11 @@ class CsvDB(object): def _validate_object(self, obj): descr = self._tables.get(obj._TABLE) if descr is None: - raise Exception('Table {0} not found.'.format(obj._TABLE)) + raise Exception("Table {0} not found.".format(obj._TABLE)) return obj._serialize(self._tables[obj._TABLE]) def __criteria(self, obj, matches=None, mt=None, lt=None, eq=None): - ''' + """ Returns True if object is aligned to the criteria. :param obj: @@ -270,7 +286,7 @@ class CsvDB(object): :param lt: :param eq: :return: Boolean - ''' + """ # Fail matcher if "less than" for field, value in (mt or {}).items(): if getattr(obj, field) <= value: @@ -294,7 +310,7 @@ class CsvDB(object): return True def get(self, obj, matches=None, mt=None, lt=None, eq=None): - ''' + """ Get objects from the table. :param table_name: @@ -303,9 +319,9 @@ class CsvDB(object): :param lt: Less than. :param eq: Equals. :return: - ''' + """ objects = [] - with gzip.open(os.path.join(self.db_path, obj._TABLE), 'rb') as table: + with gzip.open(os.path.join(self.db_path, obj._TABLE), "rb") as table: header = None for data in csv.reader(table): if not header: @@ -313,19 +329,21 @@ class CsvDB(object): continue _obj = obj() for t_attr, t_data in zip(header, data): - t_attr, t_type = t_attr.split(':') + t_attr, t_type = t_attr.split(":") setattr(_obj, t_attr, self._to_type(t_data, t_type)) if self.__criteria(_obj, matches=matches, mt=mt, lt=lt, eq=eq): objects.append(_obj) return objects def _to_type(self, data, type): - if type == 'int': + if type == "int": data = int(data) - elif type == 'float': + elif type == "float": data = float(data) - elif type == 'long': - data = sys.version_info[0] == 2 and long(data) or int(data) # pylint: disable=undefined-variable,incompatible-py3-code + elif type == "long": + # pylint: disable=undefined-variable,incompatible-py3-code + data = sys.version_info[0] == 2 and long(data) or int(data) + # pylint: enable=undefined-variable,incompatible-py3-code else: data = str(data) return data diff --git a/salt/modules/inspectlib/kiwiproc.py b/salt/modules/inspectlib/kiwiproc.py index c9b42344daa..066335bdf2a 100644 --- a/salt/modules/inspectlib/kiwiproc.py +++ b/salt/modules/inspectlib/kiwiproc.py @@ -16,19 +16,22 @@ # Import python libs from __future__ import absolute_import + import os -from xml.dom import minidom import platform import socket +from xml.dom import minidom + +# Import salt libs +import salt.utils.files +from salt.modules.inspectlib.exceptions import InspectorKiwiProcessorException + try: import grp import pwd except ImportError: pass -# Import salt libs -import salt.utils.files -from salt.modules.inspectlib.exceptions import InspectorKiwiProcessorException # Import third party libs try: @@ -38,33 +41,34 @@ except ImportError: class KiwiExporter(object): - ''' + """ Exports system description as Kiwi configuration. - ''' + """ + def __init__(self, grains, format): self.__grains__ = grains self.format = format - self._data = type('data', (), {}) + self._data = type("data", (), {}) self.name = None def load(self, **descr): - ''' + """ Load data by keys. :param data: :return: - ''' + """ for obj, data in descr.items(): setattr(self._data, obj, data) return self def export(self, name): - ''' + """ Export to the Kiwi config.xml as text. :return: - ''' + """ self.name = name root = self._create_doc() @@ -74,69 +78,80 @@ class KiwiExporter(object): self._set_users(root) self._set_packages(root) - return '\n'.join([line for line in minidom.parseString( - etree.tostring(root, encoding='UTF-8', pretty_print=True)).toprettyxml(indent=" ").split("\n") - if line.strip()]) + return "\n".join( + [ + line + for line in minidom.parseString( + etree.tostring(root, encoding="UTF-8", pretty_print=True) + ) + .toprettyxml(indent=" ") + .split("\n") + if line.strip() + ] + ) def _get_package_manager(self): - ''' + """ Get package manager. :return: - ''' + """ ret = None - if self.__grains__.get('os_family') in ('Kali', 'Debian'): - ret = 'apt-get' - elif self.__grains__.get('os_family', '') == 'Suse': - ret = 'zypper' - elif self.__grains__.get('os_family', '') == 'redhat': - ret = 'yum' + if self.__grains__.get("os_family") in ("Kali", "Debian"): + ret = "apt-get" + elif self.__grains__.get("os_family", "") == "Suse": + ret = "zypper" + elif self.__grains__.get("os_family", "") == "redhat": + ret = "yum" if ret is None: - raise InspectorKiwiProcessorException('Unsupported platform: {0}'.format(self.__grains__.get('os_family'))) + raise InspectorKiwiProcessorException( + "Unsupported platform: {0}".format(self.__grains__.get("os_family")) + ) return ret def _set_preferences(self, node): - ''' + """ Set preferences. :return: - ''' - pref = etree.SubElement(node, 'preferences') - pacman = etree.SubElement(pref, 'packagemanager') + """ + pref = etree.SubElement(node, "preferences") + pacman = etree.SubElement(pref, "packagemanager") pacman.text = self._get_package_manager() - p_version = etree.SubElement(pref, 'version') - p_version.text = '0.0.1' - p_type = etree.SubElement(pref, 'type') - p_type.set('image', 'vmx') + p_version = etree.SubElement(pref, "version") + p_version.text = "0.0.1" + p_type = etree.SubElement(pref, "type") + p_type.set("image", "vmx") - for disk_id, disk_data in self._data.system.get('disks', {}).items(): - if disk_id.startswith('/dev'): - p_type.set('filesystem', disk_data.get('type') or 'ext3') + for disk_id, disk_data in self._data.system.get("disks", {}).items(): + if disk_id.startswith("/dev"): + p_type.set("filesystem", disk_data.get("type") or "ext3") break - p_type.set('installiso', 'true') - p_type.set('boot', "vmxboot/suse-leap42.1") - p_type.set('format', self.format) - p_type.set('bootloader', 'grub2') - p_type.set('timezone', __salt__['timezone.get_zone']()) - p_type.set('hwclock', __salt__['timezone.get_hwclock']()) + p_type.set("installiso", "true") + p_type.set("boot", "vmxboot/suse-leap42.1") + p_type.set("format", self.format) + p_type.set("bootloader", "grub2") + p_type.set("timezone", __salt__["timezone.get_zone"]()) + p_type.set("hwclock", __salt__["timezone.get_hwclock"]()) return pref def _get_user_groups(self, user): - ''' + """ Get user groups. :param user: :return: - ''' - return [g.gr_name for g in grp.getgrall() - if user in g.gr_mem] + [grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name] + """ + return [g.gr_name for g in grp.getgrall() if user in g.gr_mem] + [ + grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name + ] def _set_users(self, node): - ''' + """ Create existing local users. @@ -145,113 +160,117 @@ class KiwiExporter(object): :param node: :return: - ''' + """ # Get real local users with the local passwords shadow = {} - with salt.utils.files.fopen('/etc/shadow') as rfh: + with salt.utils.files.fopen("/etc/shadow") as rfh: for sh_line in rfh.read().split(os.linesep): if sh_line.strip(): login, pwd = sh_line.split(":")[:2] - if pwd and pwd[0] not in '!*': - shadow[login] = {'p': pwd} + if pwd and pwd[0] not in "!*": + shadow[login] = {"p": pwd} - with salt.utils.files.fopen('/etc/passwd') as rfh: + with salt.utils.files.fopen("/etc/passwd") as rfh: for ps_line in rfh.read().split(os.linesep): if ps_line.strip(): - ps_line = ps_line.strip().split(':') + ps_line = ps_line.strip().split(":") if ps_line[0] in shadow: - shadow[ps_line[0]]['h'] = ps_line[5] - shadow[ps_line[0]]['s'] = ps_line[6] - shadow[ps_line[0]]['g'] = self._get_user_groups(ps_line[0]) + shadow[ps_line[0]]["h"] = ps_line[5] + shadow[ps_line[0]]["s"] = ps_line[6] + shadow[ps_line[0]]["g"] = self._get_user_groups(ps_line[0]) users_groups = [] - users_node = etree.SubElement(node, 'users') + users_node = etree.SubElement(node, "users") for u_name, u_data in shadow.items(): - user_node = etree.SubElement(users_node, 'user') - user_node.set('password', u_data['p']) - user_node.set('home', u_data['h']) - user_node.set('name', u_name) - users_groups.extend(u_data['g']) - users_node.set('group', ','.join(users_groups)) + user_node = etree.SubElement(users_node, "user") + user_node.set("password", u_data["p"]) + user_node.set("home", u_data["h"]) + user_node.set("name", u_name) + users_groups.extend(u_data["g"]) + users_node.set("group", ",".join(users_groups)) return users_node def _set_repositories(self, node): - ''' + """ Create repositories. :param node: :return: - ''' + """ priority = 99 - for repo_id, repo_data in self._data.software.get('repositories', {}).items(): + for repo_id, repo_data in self._data.software.get("repositories", {}).items(): if type(repo_data) == list: repo_data = repo_data[0] - if repo_data.get('enabled') or not repo_data.get('disabled'): # RPM and Debian, respectively - uri = repo_data.get('baseurl', repo_data.get('uri')) + if repo_data.get("enabled") or not repo_data.get( + "disabled" + ): # RPM and Debian, respectively + uri = repo_data.get("baseurl", repo_data.get("uri")) if not uri: continue - repo = etree.SubElement(node, 'repository') - if self.__grains__.get('os_family') in ('Kali', 'Debian'): - repo.set('alias', repo_id) - repo.set('distribution', repo_data['dist']) + repo = etree.SubElement(node, "repository") + if self.__grains__.get("os_family") in ("Kali", "Debian"): + repo.set("alias", repo_id) + repo.set("distribution", repo_data["dist"]) else: - repo.set('alias', repo_data['alias']) - if self.__grains__.get('os_family', '') == 'Suse': - repo.set('type', 'yast2') # TODO: Check for options! - repo.set('priority', str(priority)) - source = etree.SubElement(repo, 'source') - source.set('path', uri) # RPM and Debian, respectively + repo.set("alias", repo_data["alias"]) + if self.__grains__.get("os_family", "") == "Suse": + repo.set("type", "yast2") # TODO: Check for options! + repo.set("priority", str(priority)) + source = etree.SubElement(repo, "source") + source.set("path", uri) # RPM and Debian, respectively priority -= 1 def _set_packages(self, node): - ''' + """ Set packages and collections. :param node: :return: - ''' - pkgs = etree.SubElement(node, 'packages') - for pkg_name, pkg_version in sorted(self._data.software.get('packages', {}).items()): - pkg = etree.SubElement(pkgs, 'package') - pkg.set('name', pkg_name) + """ + pkgs = etree.SubElement(node, "packages") + for pkg_name, pkg_version in sorted( + self._data.software.get("packages", {}).items() + ): + pkg = etree.SubElement(pkgs, "package") + pkg.set("name", pkg_name) # Add collections (SUSE) - if self.__grains__.get('os_family', '') == 'Suse': - for ptn_id, ptn_data in self._data.software.get('patterns', {}).items(): - if ptn_data.get('installed'): - ptn = etree.SubElement(pkgs, 'namedCollection') - ptn.set('name', ptn_id) + if self.__grains__.get("os_family", "") == "Suse": + for ptn_id, ptn_data in self._data.software.get("patterns", {}).items(): + if ptn_data.get("installed"): + ptn = etree.SubElement(pkgs, "namedCollection") + ptn.set("name", ptn_id) return pkgs def _set_description(self, node): - ''' + """ Create a system description. :return: - ''' + """ hostname = socket.getfqdn() or platform.node() - descr = etree.SubElement(node, 'description') - author = etree.SubElement(descr, 'author') + descr = etree.SubElement(node, "description") + author = etree.SubElement(descr, "author") author.text = "salt.modules.node on {0}".format(hostname) - contact = etree.SubElement(descr, 'contact') - contact.text = 'root@{0}'.format(hostname) - specs = etree.SubElement(descr, 'specification') - specs.text = 'Rebuild of {0}, based on Salt inspection.'.format(hostname) + contact = etree.SubElement(descr, "contact") + contact.text = "root@{0}".format(hostname) + specs = etree.SubElement(descr, "specification") + specs.text = "Rebuild of {0}, based on Salt inspection.".format(hostname) return descr def _create_doc(self): - ''' + """ Create document. :return: - ''' - root = etree.Element('image') - root.set('schemaversion', '6.3') - root.set('name', self.name) + """ + root = etree.Element("image") + root.set("schemaversion", "6.3") + root.set("name", self.name) return root diff --git a/salt/modules/inspectlib/query.py b/salt/modules/inspectlib/query.py index 2ce33ce845b..ed0167f6787 100644 --- a/salt/modules/inspectlib/query.py +++ b/salt/modules/inspectlib/query.py @@ -16,96 +16,104 @@ # Import Python Libs from __future__ import absolute_import + +import logging import os import time -import logging # Import Salt Libs import salt.utils.files import salt.utils.fsutils import salt.utils.network -from salt.modules.inspectlib.exceptions import (InspectorQueryException, SIException) from salt.modules.inspectlib import EnvLoader -from salt.modules.inspectlib.entities import (Package, PackageCfgFile, PayloadFile) +from salt.modules.inspectlib.entities import Package, PackageCfgFile, PayloadFile +from salt.modules.inspectlib.exceptions import InspectorQueryException, SIException log = logging.getLogger(__name__) class SysInfo(object): - ''' + """ System information. - ''' + """ def __init__(self, systype): if systype.lower() == "solaris": raise SIException("Platform {0} not (yet) supported.".format(systype)) def _grain(self, grain): - ''' + """ An alias for grains getter. - ''' - return __grains__.get(grain, 'N/A') + """ + return __grains__.get(grain, "N/A") def _get_disk_size(self, device): - ''' + """ Get a size of a disk. - ''' - out = __salt__['cmd.run_all']("df {0}".format(device)) - if out['retcode']: - msg = "Disk size info error: {0}".format(out['stderr']) + """ + out = __salt__["cmd.run_all"]("df {0}".format(device)) + if out["retcode"]: + msg = "Disk size info error: {0}".format(out["stderr"]) log.error(msg) raise SIException(msg) - devpath, blocks, used, available, used_p, mountpoint = [elm for elm in - out['stdout'].split(os.linesep)[-1].split(" ") if elm] + devpath, blocks, used, available, used_p, mountpoint = [ + elm for elm in out["stdout"].split(os.linesep)[-1].split(" ") if elm + ] return { - 'device': devpath, 'blocks': blocks, 'used': used, - 'available': available, 'used (%)': used_p, 'mounted': mountpoint, + "device": devpath, + "blocks": blocks, + "used": used, + "available": available, + "used (%)": used_p, + "mounted": mountpoint, } def _get_fs(self): - ''' + """ Get available file systems and their types. - ''' + """ data = dict() for dev, dev_data in salt.utils.fsutils._blkid().items(): dev = self._get_disk_size(dev) - device = dev.pop('device') - dev['type'] = dev_data['type'] + device = dev.pop("device") + dev["type"] = dev_data["type"] data[device] = dev return data def _get_mounts(self): - ''' + """ Get mounted FS on the system. - ''' + """ return salt.utils.fsutils._get_mounts() def _get_cpu(self): - ''' + """ Get available CPU information. - ''' + """ # CPU data in grains is OK-ish, but lscpu is still better in this case - out = __salt__['cmd.run_all']("lscpu") + out = __salt__["cmd.run_all"]("lscpu") salt.utils.fsutils._verify_run(out) data = dict() - for descr, value in [elm.split(":", 1) for elm in out['stdout'].split(os.linesep)]: + for descr, value in [ + elm.split(":", 1) for elm in out["stdout"].split(os.linesep) + ]: data[descr.strip()] = value.strip() return data def _get_mem(self): - ''' + """ Get memory. - ''' - out = __salt__['cmd.run_all']("vmstat -s") - if out['retcode']: - raise SIException("Memory info error: {0}".format(out['stderr'])) + """ + out = __salt__["cmd.run_all"]("vmstat -s") + if out["retcode"]: + raise SIException("Memory info error: {0}".format(out["stderr"])) ret = dict() - for line in out['stdout'].split(os.linesep): + for line in out["stdout"].split(os.linesep): line = line.strip() if not line: continue @@ -117,33 +125,33 @@ class SysInfo(object): return ret def _get_network(self): - ''' + """ Get network configuration. - ''' + """ data = dict() - data['interfaces'] = salt.utils.network.interfaces() - data['subnets'] = salt.utils.network.subnets() + data["interfaces"] = salt.utils.network.interfaces() + data["subnets"] = salt.utils.network.subnets() return data def _get_os(self): - ''' + """ Get operating system summary - ''' + """ return { - 'name': self._grain('os'), - 'family': self._grain('os_family'), - 'arch': self._grain('osarch'), - 'release': self._grain('osrelease'), + "name": self._grain("os"), + "family": self._grain("os_family"), + "arch": self._grain("osarch"), + "release": self._grain("osrelease"), } class Query(EnvLoader): - ''' + """ Query the system. This class is actually puts all Salt features together, so there would be no need to pick it from various places. - ''' + """ # Configuration: config files # Identity: users/groups @@ -154,54 +162,67 @@ class Query(EnvLoader): # all: include all scopes (scary!) # payload: files that are not managed - SCOPES = ["changes", "configuration", "identity", "system", "software", "services", "payload", "all"] + SCOPES = [ + "changes", + "configuration", + "identity", + "system", + "software", + "services", + "payload", + "all", + ] def __init__(self, scope, cachedir=None): - ''' + """ Constructor. :param scope: :return: - ''' + """ if scope and scope not in self.SCOPES: raise InspectorQueryException( - "Unknown scope: {0}. Must be one of: {1}".format(repr(scope), ", ".join(self.SCOPES)) + "Unknown scope: {0}. Must be one of: {1}".format( + repr(scope), ", ".join(self.SCOPES) + ) ) elif not scope: raise InspectorQueryException( - "Scope cannot be empty. Must be one of: {0}".format(", ".join(self.SCOPES)) + "Scope cannot be empty. Must be one of: {0}".format( + ", ".join(self.SCOPES) + ) ) EnvLoader.__init__(self, cachedir=cachedir) - self.scope = '_' + scope + self.scope = "_" + scope self.local_identity = dict() def __call__(self, *args, **kwargs): - ''' + """ Call the query with the defined scope. :param args: :param kwargs: :return: - ''' + """ return getattr(self, self.scope)(*args, **kwargs) def _changes(self, *args, **kwargs): - ''' + """ Returns all diffs to the configuration files. - ''' + """ raise Exception("Not yet implemented") def _configuration(self, *args, **kwargs): - ''' + """ Return configuration files. - ''' + """ data = dict() self.db.open() for pkg in self.db.get(Package): configs = list() - for pkg_cfg in self.db.get(PackageCfgFile, eq={'pkgid': pkg.id}): + for pkg_cfg in self.db.get(PackageCfgFile, eq={"pkgid": pkg.id}): configs.append(pkg_cfg.path) data[pkg.name] = configs @@ -211,75 +232,85 @@ class Query(EnvLoader): return data def _get_local_users(self, disabled=None): - ''' + """ Return all known local accounts to the system. - ''' + """ users = dict() - path = '/etc/passwd' - with salt.utils.files.fopen(path, 'r') as fp_: + path = "/etc/passwd" + with salt.utils.files.fopen(path, "r") as fp_: for line in fp_: line = line.strip() - if ':' not in line: + if ":" not in line: continue - name, password, uid, gid, gecos, directory, shell = line.split(':') - active = not (password == '*' or password.startswith('!')) - if (disabled is False and active) or (disabled is True and not active) or disabled is None: + name, password, uid, gid, gecos, directory, shell = line.split(":") + active = not (password == "*" or password.startswith("!")) + if ( + (disabled is False and active) + or (disabled is True and not active) + or disabled is None + ): users[name] = { - 'uid': uid, - 'git': gid, - 'info': gecos, - 'home': directory, - 'shell': shell, - 'disabled': not active + "uid": uid, + "git": gid, + "info": gecos, + "home": directory, + "shell": shell, + "disabled": not active, } return users def _get_local_groups(self): - ''' + """ Return all known local groups to the system. - ''' + """ groups = dict() - path = '/etc/group' - with salt.utils.files.fopen(path, 'r') as fp_: + path = "/etc/group" + with salt.utils.files.fopen(path, "r") as fp_: for line in fp_: line = line.strip() - if ':' not in line: + if ":" not in line: continue - name, password, gid, users = line.split(':') + name, password, gid, users = line.split(":") groups[name] = { - 'gid': gid, + "gid": gid, } if users: - groups[name]['users'] = users.split(',') + groups[name]["users"] = users.split(",") return groups def _get_external_accounts(self, locals): - ''' + """ Return all known accounts, excluding local accounts. - ''' + """ users = dict() - out = __salt__['cmd.run_all']("passwd -S -a") - if out['retcode']: + out = __salt__["cmd.run_all"]("passwd -S -a") + if out["retcode"]: # System does not supports all accounts descriptions, just skipping. return users - status = {'L': 'Locked', 'NP': 'No password', 'P': 'Usable password', 'LK': 'Locked'} - for data in [elm.strip().split(" ") for elm in out['stdout'].split(os.linesep) if elm.strip()]: + status = { + "L": "Locked", + "NP": "No password", + "P": "Usable password", + "LK": "Locked", + } + for data in [ + elm.strip().split(" ") + for elm in out["stdout"].split(os.linesep) + if elm.strip() + ]: if len(data) < 2: continue name, login = data[:2] if name not in locals: - users[name] = { - 'login': login, - 'status': status.get(login, 'N/A') - } + users[name] = {"login": login, "status": status.get(login, "N/A")} return users def _identity(self, *args, **kwargs): - ''' + """ Local users and groups. accounts @@ -289,126 +320,128 @@ class Query(EnvLoader): disabled True (or False, default) to return only disabled accounts. - ''' - LOCAL = 'local accounts' - EXT = 'external accounts' + """ + LOCAL = "local accounts" + EXT = "external accounts" data = dict() - data[LOCAL] = self._get_local_users(disabled=kwargs.get('disabled')) - data[EXT] = self._get_external_accounts(data[LOCAL].keys()) or 'N/A' - data['local groups'] = self._get_local_groups() + data[LOCAL] = self._get_local_users(disabled=kwargs.get("disabled")) + data[EXT] = self._get_external_accounts(data[LOCAL].keys()) or "N/A" + data["local groups"] = self._get_local_groups() return data def _system(self, *args, **kwargs): - ''' + """ This basically calls grains items and picks out only necessary information in a certain structure. :param args: :param kwargs: :return: - ''' + """ sysinfo = SysInfo(__grains__.get("kernel")) data = dict() - data['cpu'] = sysinfo._get_cpu() - data['disks'] = sysinfo._get_fs() - data['mounts'] = sysinfo._get_mounts() - data['memory'] = sysinfo._get_mem() - data['network'] = sysinfo._get_network() - data['os'] = sysinfo._get_os() + data["cpu"] = sysinfo._get_cpu() + data["disks"] = sysinfo._get_fs() + data["mounts"] = sysinfo._get_mounts() + data["memory"] = sysinfo._get_mem() + data["network"] = sysinfo._get_network() + data["os"] = sysinfo._get_os() return data def _software(self, *args, **kwargs): - ''' + """ Return installed software. - ''' + """ data = dict() - if 'exclude' in kwargs: - excludes = kwargs['exclude'].split(",") + if "exclude" in kwargs: + excludes = kwargs["exclude"].split(",") else: excludes = list() os_family = __grains__.get("os_family").lower() # Get locks - if os_family == 'suse': + if os_family == "suse": LOCKS = "pkg.list_locks" - if 'products' not in excludes: - products = __salt__['pkg.list_products']() + if "products" not in excludes: + products = __salt__["pkg.list_products"]() if products: - data['products'] = products - elif os_family == 'redhat': + data["products"] = products + elif os_family == "redhat": LOCKS = "pkg.get_locked_packages" else: LOCKS = None - if LOCKS and 'locks' not in excludes: + if LOCKS and "locks" not in excludes: locks = __salt__[LOCKS]() if locks: - data['locks'] = locks + data["locks"] = locks # Get patterns - if os_family == 'suse': - PATTERNS = 'pkg.list_installed_patterns' - elif os_family == 'redhat': - PATTERNS = 'pkg.group_list' + if os_family == "suse": + PATTERNS = "pkg.list_installed_patterns" + elif os_family == "redhat": + PATTERNS = "pkg.group_list" else: PATTERNS = None - if PATTERNS and 'patterns' not in excludes: + if PATTERNS and "patterns" not in excludes: patterns = __salt__[PATTERNS]() if patterns: - data['patterns'] = patterns + data["patterns"] = patterns # Get packages - if 'packages' not in excludes: - data['packages'] = __salt__['pkg.list_pkgs']() + if "packages" not in excludes: + data["packages"] = __salt__["pkg.list_pkgs"]() # Get repositories - if 'repositories' not in excludes: - repos = __salt__['pkg.list_repos']() + if "repositories" not in excludes: + repos = __salt__["pkg.list_repos"]() if repos: - data['repositories'] = repos + data["repositories"] = repos return data def _services(self, *args, **kwargs): - ''' + """ Get list of enabled and disabled services on the particular system. - ''' + """ return { - 'enabled': __salt__['service.get_enabled'](), - 'disabled': __salt__['service.get_disabled'](), + "enabled": __salt__["service.get_enabled"](), + "disabled": __salt__["service.get_disabled"](), } def _id_resolv(self, iid, named=True, uid=True): - ''' + """ Resolve local users and groups. :param iid: :param named: :param uid: :return: - ''' + """ if not self.local_identity: - self.local_identity['users'] = self._get_local_users() - self.local_identity['groups'] = self._get_local_groups() + self.local_identity["users"] = self._get_local_users() + self.local_identity["groups"] = self._get_local_groups() if not named: return iid - for name, meta in self.local_identity[uid and 'users' or 'groups'].items(): - if (uid and int(meta.get('uid', -1)) == iid) or (not uid and int(meta.get('gid', -1)) == iid): + for name, meta in self.local_identity[uid and "users" or "groups"].items(): + if (uid and int(meta.get("uid", -1)) == iid) or ( + not uid and int(meta.get("gid", -1)) == iid + ): return name return iid def _payload(self, *args, **kwargs): - ''' + """ Find all unmanaged files. Returns maximum 1000 values. Parameters: @@ -427,7 +460,8 @@ class Query(EnvLoader): Options: * **total**: Return a total amount of found payload files - ''' + """ + def _size_format(size, fmt): if fmt is None: return size @@ -442,23 +476,33 @@ class Query(EnvLoader): elif fmt == "gb": return "{0} Gb".format(round((float(size) / 0x400 / 0x400 / 0x400), 2)) - filter = kwargs.get('filter') - offset = kwargs.get('offset', 0) + filter = kwargs.get("filter") + offset = kwargs.get("offset", 0) timeformat = kwargs.get("time", "tz") if timeformat not in ["ticks", "tz"]: - raise InspectorQueryException('Unknown "{0}" value for parameter "time"'.format(timeformat)) - tfmt = lambda param: timeformat == "tz" and time.strftime("%b %d %Y %H:%M:%S", time.gmtime(param)) or int(param) + raise InspectorQueryException( + 'Unknown "{0}" value for parameter "time"'.format(timeformat) + ) + tfmt = ( + lambda param: timeformat == "tz" + and time.strftime("%b %d %Y %H:%M:%S", time.gmtime(param)) + or int(param) + ) size_fmt = kwargs.get("size") if size_fmt is not None and size_fmt.lower() not in ["b", "kb", "mb", "gb"]: - raise InspectorQueryException('Unknown "{0}" value for parameter "size". ' - 'Should be either B, Kb, Mb or Gb'.format(timeformat)) + raise InspectorQueryException( + 'Unknown "{0}" value for parameter "size". ' + "Should be either B, Kb, Mb or Gb".format(timeformat) + ) owners = kwargs.get("owners", "id") if owners not in ["name", "id"]: - raise InspectorQueryException('Unknown "{0}" value for parameter "owners". ' - 'Should be either name or id (default)'.format(owners)) + raise InspectorQueryException( + 'Unknown "{0}" value for parameter "owners". ' + "Should be either name or id (default)".format(owners) + ) incl_type = [prm for prm in kwargs.get("type", "").lower().split(",") if prm] if not incl_type: @@ -466,45 +510,51 @@ class Query(EnvLoader): for i_type in incl_type: if i_type not in ["directory", "dir", "d", "file", "f", "link", "l"]: - raise InspectorQueryException('Unknown "{0}" values for parameter "type". ' - 'Should be comma separated one or more of ' - 'dir, file and/or link.'.format(", ".join(incl_type))) + raise InspectorQueryException( + 'Unknown "{0}" values for parameter "type". ' + "Should be comma separated one or more of " + "dir, file and/or link.".format(", ".join(incl_type)) + ) self.db.open() if "total" in args: - return {'total': len(self.db.get(PayloadFile))} + return {"total": len(self.db.get(PayloadFile))} brief = kwargs.get("brief") pld_files = list() if brief else dict() - for pld_data in self.db.get(PayloadFile)[offset:offset + kwargs.get('max', 1000)]: + for pld_data in self.db.get(PayloadFile)[ + offset : offset + kwargs.get("max", 1000) + ]: if brief: pld_files.append(pld_data.path) else: pld_files[pld_data.path] = { - 'uid': self._id_resolv(pld_data.uid, named=(owners == "id")), - 'gid': self._id_resolv(pld_data.gid, named=(owners == "id"), uid=False), - 'size': _size_format(pld_data.p_size, fmt=size_fmt), - 'mode': oct(pld_data.mode), - 'accessed': tfmt(pld_data.atime), - 'modified': tfmt(pld_data.mtime), - 'created': tfmt(pld_data.ctime), + "uid": self._id_resolv(pld_data.uid, named=(owners == "id")), + "gid": self._id_resolv( + pld_data.gid, named=(owners == "id"), uid=False + ), + "size": _size_format(pld_data.p_size, fmt=size_fmt), + "mode": oct(pld_data.mode), + "accessed": tfmt(pld_data.atime), + "modified": tfmt(pld_data.mtime), + "created": tfmt(pld_data.ctime), } return pld_files def _all(self, *args, **kwargs): - ''' + """ Return all the summary of the particular system. - ''' + """ data = dict() - data['software'] = self._software(**kwargs) - data['system'] = self._system(**kwargs) - data['services'] = self._services(**kwargs) + data["software"] = self._software(**kwargs) + data["system"] = self._system(**kwargs) + data["services"] = self._services(**kwargs) try: - data['configuration'] = self._configuration(**kwargs) + data["configuration"] = self._configuration(**kwargs) except InspectorQueryException as ex: - data['configuration'] = 'N/A' + data["configuration"] = "N/A" log.error(ex) - data['payload'] = self._payload(**kwargs) or 'N/A' + data["payload"] = self._payload(**kwargs) or "N/A" return data diff --git a/salt/modules/inspector.py b/salt/modules/inspector.py index a1676fc953f..f87a2425948 100644 --- a/salt/modules/inspector.py +++ b/salt/modules/inspector.py @@ -14,51 +14,64 @@ # See the License for the specific language governing permissions and # limitations under the License. -''' +""" Module for full system inspection. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + +import getpass import logging import os -import getpass -from salt.modules.inspectlib.exceptions import (InspectorQueryException, - InspectorSnapshotException, - InspectorKiwiProcessorException) -# Import Salt libs -from salt.ext import six import salt.utils.fsutils import salt.utils.platform from salt.exceptions import CommandExecutionError from salt.exceptions import get_error_message as _get_error_message +# Import Salt libs +from salt.ext import six +from salt.modules.inspectlib.exceptions import ( + InspectorKiwiProcessorException, + InspectorQueryException, + InspectorSnapshotException, +) + log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' - return not salt.utils.platform.is_windows() and 'inspector' + """ + return not salt.utils.platform.is_windows() and "inspector" def _(module): - ''' + """ Get inspectlib module for the lazy loader. :param module: :return: - ''' + """ mod = None # pylint: disable=E0598 try: # importlib is in Python 2.7+ and 3+ import importlib + mod = importlib.import_module("salt.modules.inspectlib.{0}".format(module)) except ImportError: # No importlib around (2.6) - mod = getattr(__import__("salt.modules.inspectlib", globals(), locals(), fromlist=[six.text_type(module)]), module) + mod = getattr( + __import__( + "salt.modules.inspectlib", + globals(), + locals(), + fromlist=[six.text_type(module)], + ), + module, + ) # pylint: enable=E0598 mod.__grains__ = __grains__ @@ -68,8 +81,8 @@ def _(module): return mod -def inspect(mode='all', priority=19, **kwargs): - ''' +def inspect(mode="all", priority=19, **kwargs): + """ Start node inspection and save the data to the database for further query. Parameters: @@ -90,12 +103,12 @@ def inspect(mode='all', priority=19, **kwargs): salt '*' inspector.inspect salt '*' inspector.inspect configuration salt '*' inspector.inspect payload filter=/opt,/ext/oracle - ''' + """ collector = _("collector") try: - return collector.Inspector(cachedir=__opts__['cachedir'], - piddir=os.path.dirname(__opts__['pidfile']))\ - .request_snapshot(mode, priority=priority, **kwargs) + return collector.Inspector( + cachedir=__opts__["cachedir"], piddir=os.path.dirname(__opts__["pidfile"]) + ).request_snapshot(mode, priority=priority, **kwargs) except InspectorSnapshotException as ex: raise CommandExecutionError(ex) except Exception as ex: # pylint: disable=broad-except @@ -104,7 +117,7 @@ def inspect(mode='all', priority=19, **kwargs): def query(*args, **kwargs): - ''' + """ Query the node for specific information. Parameters: @@ -157,10 +170,12 @@ def query(*args, **kwargs): salt '*' inspector.query scope=system salt '*' inspector.query scope=payload type=file,link filter=/etc size=Kb brief=False - ''' + """ query = _("query") try: - return query.Query(kwargs.get('scope'), cachedir=__opts__['cachedir'])(*args, **kwargs) + return query.Query(kwargs.get("scope"), cachedir=__opts__["cachedir"])( + *args, **kwargs + ) except InspectorQueryException as ex: raise CommandExecutionError(ex) except Exception as ex: # pylint: disable=broad-except @@ -168,8 +183,8 @@ def query(*args, **kwargs): raise Exception(ex) -def build(format='qcow2', path='/tmp/'): - ''' +def build(format="qcow2", path="/tmp/"): + """ Build an image from a current system description. The image is a system image can be output in bootable ISO or QCOW2 formats. @@ -186,11 +201,13 @@ def build(format='qcow2', path='/tmp/'): salt myminion inspector.build salt myminion inspector.build format=iso path=/opt/builds/ - ''' + """ try: - _("collector").Inspector(cachedir=__opts__['cachedir'], - piddir=os.path.dirname(__opts__['pidfile']), - pidfilename='').reuse_snapshot().build(format=format, path=path) + _("collector").Inspector( + cachedir=__opts__["cachedir"], + piddir=os.path.dirname(__opts__["pidfile"]), + pidfilename="", + ).reuse_snapshot().build(format=format, path=path) except InspectorKiwiProcessorException as ex: raise CommandExecutionError(ex) except Exception as ex: # pylint: disable=broad-except @@ -198,8 +215,8 @@ def build(format='qcow2', path='/tmp/'): raise Exception(ex) -def export(local=False, path="/tmp", format='qcow2'): - ''' +def export(local=False, path="/tmp", format="qcow2"): + """ Export an image description for Kiwi. Parameters: @@ -214,12 +231,19 @@ def export(local=False, path="/tmp", format='qcow2'): salt myminion inspector.export salt myminion inspector.export format=iso path=/opt/builds/ - ''' - if getpass.getuser() != 'root': - raise CommandExecutionError('In order to export system, the minion should run as "root".') + """ + if getpass.getuser() != "root": + raise CommandExecutionError( + 'In order to export system, the minion should run as "root".' + ) try: - description = _("query").Query('all', cachedir=__opts__['cachedir'])() - return _("collector").Inspector().reuse_snapshot().export(description, local=local, path=path, format=format) + description = _("query").Query("all", cachedir=__opts__["cachedir"])() + return ( + _("collector") + .Inspector() + .reuse_snapshot() + .export(description, local=local, path=path, format=format) + ) except InspectorKiwiProcessorException as ex: raise CommandExecutionError(ex) except Exception as ex: # pylint: disable=broad-except @@ -228,7 +252,7 @@ def export(local=False, path="/tmp", format='qcow2'): def snapshots(): - ''' + """ List current description snapshots. CLI Example: @@ -236,10 +260,16 @@ def snapshots(): .. code-block:: bash salt myminion inspector.snapshots - ''' + """ try: - return _("collector").Inspector(cachedir=__opts__['cachedir'], - piddir=os.path.dirname(__opts__['pidfile'])).db.list() + return ( + _("collector") + .Inspector( + cachedir=__opts__["cachedir"], + piddir=os.path.dirname(__opts__["pidfile"]), + ) + .db.list() + ) except InspectorSnapshotException as err: raise CommandExecutionError(err) except Exception as err: # pylint: disable=broad-except @@ -248,7 +278,7 @@ def snapshots(): def delete(all=False, *databases): - ''' + """ Remove description snapshots from the system. ::parameter: all. Default: False. Remove all snapshots, if set to True. @@ -259,14 +289,15 @@ def delete(all=False, *databases): salt myminion inspector.delete .. salt myminion inspector.delete all=True - ''' + """ if not all and not databases: - raise CommandExecutionError('At least one database ID required.') + raise CommandExecutionError("At least one database ID required.") try: ret = dict() - inspector = _("collector").Inspector(cachedir=__opts__['cachedir'], - piddir=os.path.dirname(__opts__['pidfile'])) + inspector = _("collector").Inspector( + cachedir=__opts__["cachedir"], piddir=os.path.dirname(__opts__["pidfile"]) + ) for dbid in all and inspector.db.list() or databases: ret[dbid] = inspector.db._db.purge(six.text_type(dbid)) return ret diff --git a/salt/modules/introspect.py b/salt/modules/introspect.py index 3a20065d014..9e6518ae2b4 100644 --- a/salt/modules/introspect.py +++ b/salt/modules/introspect.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Functions to perform introspection on a minion, and return data in a format usable by Salt States -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import 3rd-party libs @@ -13,10 +14,9 @@ from salt.ext import six def running_service_owners( - exclude=('/dev', '/home', '/media', '/proc', '/run', '/sys/', '/tmp', - '/var') - ): - ''' + exclude=("/dev", "/home", "/media", "/proc", "/run", "/sys/", "/tmp", "/var") +): + """ Determine which packages own the currently running services. By default, excludes files whose full path starts with ``/dev``, ``/home``, ``/media``, ``/proc``, ``/run``, ``/sys``, ``/tmp`` and ``/var``. This can be @@ -25,27 +25,27 @@ def running_service_owners( CLI Example: salt myminion introspect.running_service_owners - ''' + """ error = {} - if 'pkg.owner' not in __salt__: - error['Unsupported Package Manager'] = ( - 'The module for the package manager on this system does not ' - 'support looking up which package(s) owns which file(s)' + if "pkg.owner" not in __salt__: + error["Unsupported Package Manager"] = ( + "The module for the package manager on this system does not " + "support looking up which package(s) owns which file(s)" ) - if 'file.open_files' not in __salt__: - error['Unsupported File Module'] = ( - 'The file module on this system does not ' - 'support looking up open files on the system' + if "file.open_files" not in __salt__: + error["Unsupported File Module"] = ( + "The file module on this system does not " + "support looking up open files on the system" ) if error: - return {'Error': error} + return {"Error": error} ret = {} - open_files = __salt__['file.open_files']() + open_files = __salt__["file.open_files"]() - execs = __salt__['service.execs']() + execs = __salt__["service.execs"]() for path in open_files: ignore = False for bad_dir in exclude: @@ -60,52 +60,52 @@ def running_service_owners( for service in execs: if path == execs[service]: - pkg = __salt__['pkg.owner'](path) + pkg = __salt__["pkg.owner"](path) ret[service] = next(six.itervalues(pkg)) return ret def enabled_service_owners(): - ''' + """ Return which packages own each of the services that are currently enabled. CLI Example: salt myminion introspect.enabled_service_owners - ''' + """ error = {} - if 'pkg.owner' not in __salt__: - error['Unsupported Package Manager'] = ( - 'The module for the package manager on this system does not ' - 'support looking up which package(s) owns which file(s)' + if "pkg.owner" not in __salt__: + error["Unsupported Package Manager"] = ( + "The module for the package manager on this system does not " + "support looking up which package(s) owns which file(s)" ) - if 'service.show' not in __salt__: - error['Unsupported Service Manager'] = ( - 'The module for the service manager on this system does not ' - 'support showing descriptive service data' + if "service.show" not in __salt__: + error["Unsupported Service Manager"] = ( + "The module for the service manager on this system does not " + "support showing descriptive service data" ) if error: - return {'Error': error} + return {"Error": error} ret = {} - services = __salt__['service.get_enabled']() + services = __salt__["service.get_enabled"]() for service in services: - data = __salt__['service.show'](service) - if 'ExecStart' not in data: + data = __salt__["service.show"](service) + if "ExecStart" not in data: continue - start_cmd = data['ExecStart']['path'] - pkg = __salt__['pkg.owner'](start_cmd) + start_cmd = data["ExecStart"]["path"] + pkg = __salt__["pkg.owner"](start_cmd) ret[service] = next(six.itervalues(pkg)) return ret def service_highstate(requires=True): - ''' + """ Return running and enabled services in a highstate structure. By default also returns package dependencies for those services, which means that package definitions must be created outside this function. To drop the @@ -115,32 +115,28 @@ def service_highstate(requires=True): salt myminion introspect.service_highstate salt myminion introspect.service_highstate requires=False - ''' + """ ret = {} running = running_service_owners() for service in running: - ret[service] = {'service': ['running']} + ret[service] = {"service": ["running"]} if requires: - ret[service]['service'].append( - {'require': {'pkg': running[service]}} - ) + ret[service]["service"].append({"require": {"pkg": running[service]}}) enabled = enabled_service_owners() for service in enabled: if service in ret: - ret[service]['service'].append({'enabled': True}) + ret[service]["service"].append({"enabled": True}) else: - ret[service] = {'service': [{'enabled': True}]} + ret[service] = {"service": [{"enabled": True}]} if requires: exists = False - for item in ret[service]['service']: - if isinstance(item, dict) and next(six.iterkeys(item)) == 'require': + for item in ret[service]["service"]: + if isinstance(item, dict) and next(six.iterkeys(item)) == "require": exists = True if not exists: - ret[service]['service'].append( - {'require': {'pkg': enabled[service]}} - ) + ret[service]["service"].append({"require": {"pkg": enabled[service]}}) return ret diff --git a/salt/modules/iosconfig.py b/salt/modules/iosconfig.py index 39f0cc2e26a..5d0d759c59b 100644 --- a/salt/modules/iosconfig.py +++ b/salt/modules/iosconfig.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Cisco IOS configuration manipulation helpers .. versionadded:: 2019.2.0 @@ -7,26 +7,27 @@ Cisco IOS configuration manipulation helpers This module provides a collection of helper functions for Cisco IOS style configuration manipulation. This module does not have external dependencies and can be used from any Proxy or regular Minion. -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import python stdlib import difflib +import salt.utils.dictdiffer +import salt.utils.dictupdate +from salt.exceptions import SaltException + # Import Salt modules from salt.ext import six -import salt.utils.dictupdate -import salt.utils.dictdiffer from salt.utils.odict import OrderedDict -from salt.exceptions import SaltException # ------------------------------------------------------------------------------ # module properties # ------------------------------------------------------------------------------ -__virtualname__ = 'iosconfig' -__proxyenabled__ = ['*'] +__virtualname__ = "iosconfig" +__proxyenabled__ = ["*"] # ------------------------------------------------------------------------------ # helper functions -- will not be exported @@ -67,28 +68,26 @@ def _attach_data_to_path_tags(obj, path, data, list_=False): obj_tmp[path_item]["#standalone"] = True -def _parse_text_config(config_lines, - with_tags=False, - current_indent=0, - nested=False): +def _parse_text_config(config_lines, with_tags=False, current_indent=0, nested=False): struct_cfg = OrderedDict() while config_lines: line = config_lines.pop(0) - if not line.strip() or line.lstrip().startswith('!'): + if not line.strip() or line.lstrip().startswith("!"): # empty or comment continue current_line = line.lstrip() leading_spaces = len(line) - len(current_line) if leading_spaces > current_indent: - current_block = _parse_text_config(config_lines, - current_indent=leading_spaces, - with_tags=with_tags, - nested=True) + current_block = _parse_text_config( + config_lines, + current_indent=leading_spaces, + with_tags=with_tags, + nested=True, + ) if with_tags: - _attach_data_to_path_tags(struct_cfg, - current_line, - current_block, - nested) + _attach_data_to_path_tags( + struct_cfg, current_line, current_block, nested + ) else: _attach_data_to_path(struct_cfg, current_line, current_block) elif leading_spaces < current_indent: @@ -96,15 +95,16 @@ def _parse_text_config(config_lines, break else: if not nested: - current_block = _parse_text_config(config_lines, - current_indent=leading_spaces, - with_tags=with_tags, - nested=True) + current_block = _parse_text_config( + config_lines, + current_indent=leading_spaces, + with_tags=with_tags, + nested=True, + ) if with_tags: - _attach_data_to_path_tags(struct_cfg, - current_line, - current_block, - nested) + _attach_data_to_path_tags( + struct_cfg, current_line, current_block, nested + ) else: _attach_data_to_path(struct_cfg, current_line, current_block) else: @@ -114,35 +114,32 @@ def _parse_text_config(config_lines, def _get_diff_text(old, new): - ''' + """ Returns the diff of two text blobs. - ''' - diff = difflib.unified_diff(old.splitlines(1), - new.splitlines(1)) - return ''.join([x.replace('\r', '') for x in diff]) + """ + diff = difflib.unified_diff(old.splitlines(1), new.splitlines(1)) + return "".join([x.replace("\r", "") for x in diff]) def _print_config_text(tree, indentation=0): - ''' + """ Return the config as text from a config tree. - ''' - config = '' + """ + config = "" for key, value in six.iteritems(tree): - config += '{indent}{line}\n'.format(indent=' '*indentation, line=key) + config += "{indent}{line}\n".format(indent=" " * indentation, line=key) if value: - config += _print_config_text(value, indentation=indentation+1) + config += _print_config_text(value, indentation=indentation + 1) return config + # ------------------------------------------------------------------------------ # callable functions # ------------------------------------------------------------------------------ -def tree(config=None, - path=None, - with_tags=False, - saltenv='base'): - ''' +def tree(config=None, path=None, with_tags=False, saltenv="base"): + """ Transform Cisco IOS style configuration to structured Python dictionary. Depending on the value of the ``with_tags`` argument, this function may provide different views, valuable in different situations. @@ -170,17 +167,17 @@ def tree(config=None, salt '*' iosconfig.tree path=salt://path/to/my/config.txt salt '*' iosconfig.tree path=https://bit.ly/2mAdq7z - ''' + """ if path: - config = __salt__['cp.get_file_str'](path, saltenv=saltenv) + config = __salt__["cp.get_file_str"](path, saltenv=saltenv) if config is False: - raise SaltException('{} is not available'.format(path)) + raise SaltException("{} is not available".format(path)) config_lines = config.splitlines() return _parse_text_config(config_lines, with_tags=with_tags) -def clean(config=None, path=None, saltenv='base'): - ''' +def clean(config=None, path=None, saltenv="base"): + """ Return a clean version of the config, without any special signs (such as ``!`` as an individual line) or empty lines, but just lines with significant value in the configuration of the network device. @@ -205,17 +202,19 @@ def clean(config=None, path=None, saltenv='base'): salt '*' iosconfig.clean path=salt://path/to/my/config.txt salt '*' iosconfig.clean path=https://bit.ly/2mAdq7z - ''' + """ config_tree = tree(config=config, path=path, saltenv=saltenv) return _print_config_text(config_tree) -def merge_tree(initial_config=None, - initial_path=None, - merge_config=None, - merge_path=None, - saltenv='base'): - ''' +def merge_tree( + initial_config=None, + initial_path=None, + merge_config=None, + merge_path=None, + saltenv="base", +): + """ Return the merge tree of the ``initial_config`` with the ``merge_config``, as a Python dictionary. @@ -248,22 +247,20 @@ def merge_tree(initial_config=None, .. code-block:: bash salt '*' iosconfig.merge_tree initial_path=salt://path/to/running.cfg merge_path=salt://path/to/merge.cfg - ''' - merge_tree = tree(config=merge_config, - path=merge_path, - saltenv=saltenv) - initial_tree = tree(config=initial_config, - path=initial_path, - saltenv=saltenv) + """ + merge_tree = tree(config=merge_config, path=merge_path, saltenv=saltenv) + initial_tree = tree(config=initial_config, path=initial_path, saltenv=saltenv) return salt.utils.dictupdate.merge(initial_tree, merge_tree) -def merge_text(initial_config=None, - initial_path=None, - merge_config=None, - merge_path=None, - saltenv='base'): - ''' +def merge_text( + initial_config=None, + initial_path=None, + merge_config=None, + merge_path=None, + saltenv="base", +): + """ Return the merge result of the ``initial_config`` with the ``merge_config``, as plain text. @@ -296,21 +293,25 @@ def merge_text(initial_config=None, .. code-block:: bash salt '*' iosconfig.merge_text initial_path=salt://path/to/running.cfg merge_path=salt://path/to/merge.cfg - ''' - candidate_tree = merge_tree(initial_config=initial_config, - initial_path=initial_path, - merge_config=merge_config, - merge_path=merge_path, - saltenv=saltenv) + """ + candidate_tree = merge_tree( + initial_config=initial_config, + initial_path=initial_path, + merge_config=merge_config, + merge_path=merge_path, + saltenv=saltenv, + ) return _print_config_text(candidate_tree) -def merge_diff(initial_config=None, - initial_path=None, - merge_config=None, - merge_path=None, - saltenv='base'): - ''' +def merge_diff( + initial_config=None, + initial_path=None, + merge_config=None, + merge_path=None, + saltenv="base", +): + """ Return the merge diff, as text, after merging the merge config into the initial config. @@ -343,24 +344,28 @@ def merge_diff(initial_config=None, .. code-block:: bash salt '*' iosconfig.merge_diff initial_path=salt://path/to/running.cfg merge_path=salt://path/to/merge.cfg - ''' + """ if initial_path: - initial_config = __salt__['cp.get_file_str'](initial_path, saltenv=saltenv) - candidate_config = merge_text(initial_config=initial_config, - merge_config=merge_config, - merge_path=merge_path, - saltenv=saltenv) + initial_config = __salt__["cp.get_file_str"](initial_path, saltenv=saltenv) + candidate_config = merge_text( + initial_config=initial_config, + merge_config=merge_config, + merge_path=merge_path, + saltenv=saltenv, + ) clean_running_dict = tree(config=initial_config) clean_running = _print_config_text(clean_running_dict) return _get_diff_text(clean_running, candidate_config) -def diff_tree(candidate_config=None, - candidate_path=None, - running_config=None, - running_path=None, - saltenv='base'): - ''' +def diff_tree( + candidate_config=None, + candidate_path=None, + running_config=None, + running_path=None, + saltenv="base", +): + """ Return the diff, as Python dictionary, between the candidate and the running configuration. @@ -394,22 +399,20 @@ def diff_tree(candidate_config=None, .. code-block:: bash salt '*' iosconfig.diff_tree candidate_path=salt://path/to/candidate.cfg running_path=salt://path/to/running.cfg - ''' - candidate_tree = tree(config=candidate_config, - path=candidate_path, - saltenv=saltenv) - running_tree = tree(config=running_config, - path=running_path, - saltenv=saltenv) + """ + candidate_tree = tree(config=candidate_config, path=candidate_path, saltenv=saltenv) + running_tree = tree(config=running_config, path=running_path, saltenv=saltenv) return salt.utils.dictdiffer.deep_diff(running_tree, candidate_tree) -def diff_text(candidate_config=None, - candidate_path=None, - running_config=None, - running_path=None, - saltenv='base'): - ''' +def diff_text( + candidate_config=None, + candidate_path=None, + running_config=None, + running_path=None, + saltenv="base", +): + """ Return the diff, as text, between the candidate and the running config. candidate_config @@ -442,11 +445,9 @@ def diff_text(candidate_config=None, .. code-block:: bash salt '*' iosconfig.diff_text candidate_path=salt://path/to/candidate.cfg running_path=salt://path/to/running.cfg - ''' - candidate_text = clean(config=candidate_config, - path=candidate_path, - saltenv=saltenv) - running_text = clean(config=running_config, - path=running_path, - saltenv=saltenv) + """ + candidate_text = clean( + config=candidate_config, path=candidate_path, saltenv=saltenv + ) + running_text = clean(config=running_config, path=running_path, saltenv=saltenv) return _get_diff_text(running_text, candidate_text) diff --git a/salt/modules/ipmi.py b/salt/modules/ipmi.py index 39efdb76ff0..4d2dac53d7f 100644 --- a/salt/modules/ipmi.py +++ b/salt/modules/ipmi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support IPMI commands over LAN. This module does not talk to the local systems hardware through IPMI drivers. It uses a python module `pyghmi`. @@ -29,7 +29,7 @@ systems hardware through IPMI drivers. It uses a python module `pyghmi`. salt-call ipmi.get_user api_host=myipmienabled.system api_user=admin api_pass=pass uid=1 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -37,7 +37,6 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs from salt.ext import six - IMPORT_ERR = None try: from pyghmi.ipmi import command @@ -45,7 +44,7 @@ try: except Exception as ex: # pylint: disable=broad-except IMPORT_ERR = six.text_type(ex) -__virtualname__ = 'ipmi' +__virtualname__ = "ipmi" def __virtual__(): @@ -53,20 +52,20 @@ def __virtual__(): def _get_config(**kwargs): - ''' + """ Return configuration - ''' + """ config = { - 'api_host': 'localhost', - 'api_port': 623, - 'api_user': 'admin', - 'api_pass': '', - 'api_kg': None, - 'api_login_timeout': 2, + "api_host": "localhost", + "api_port": 623, + "api_user": "admin", + "api_pass": "", + "api_kg": None, + "api_login_timeout": 2, } - if '__salt__' in globals(): - config_key = '{0}.config'.format(__virtualname__) - config.update(__salt__['config.get'](config_key, {})) + if "__salt__" in globals(): + config_key = "{0}.config".format(__virtualname__) + config.update(__salt__["config.get"](config_key, {})) for k in set(config) & set(kwargs): config[k] = kwargs[k] return config @@ -77,9 +76,13 @@ class _IpmiCommand(object): def __init__(self, **kwargs): config = _get_config(**kwargs) - self.o = command.Command(bmc=config['api_host'], userid=config['api_user'], - password=config['api_pass'], port=config['api_port'], - kg=config['api_kg']) + self.o = command.Command( + bmc=config["api_host"], + userid=config["api_user"], + password=config["api_pass"], + port=config["api_port"], + kg=config["api_kg"], + ) def __enter__(self): return self.o @@ -93,20 +96,22 @@ class _IpmiSession(object): o = None def _onlogon(self, response): - if 'error' in response: - raise Exception(response['error']) + if "error" in response: + raise Exception(response["error"]) def __init__(self, **kwargs): config = _get_config(**kwargs) - self.o = session.Session(bmc=config['api_host'], - userid=config['api_user'], - password=config['api_pass'], - port=config['api_port'], - kg=config['api_kg'], - onlogon=self._onlogon) + self.o = session.Session( + bmc=config["api_host"], + userid=config["api_user"], + password=config["api_pass"], + port=config["api_port"], + kg=config["api_kg"], + onlogon=self._onlogon, + ) while not self.o.logged: # override timeout - self.o.maxtimeout = config['api_login_timeout'] + self.o.maxtimeout = config["api_login_timeout"] self.o.wait_for_rsp(timeout=1) self.o.maxtimeout = 5 @@ -118,8 +123,10 @@ class _IpmiSession(object): self.o.logout() -def raw_command(netfn, command, bridge_request=None, data=(), retry=True, delay_xmit=None, **kwargs): - ''' +def raw_command( + netfn, command, bridge_request=None, data=(), retry=True, delay_xmit=None, **kwargs +): + """ Send raw ipmi command This allows arbitrary IPMI bytes to be issued. This is commonly used @@ -145,19 +152,21 @@ def raw_command(netfn, command, bridge_request=None, data=(), retry=True, delay_ salt-call ipmi.raw_command netfn=0x06 command=0x46 data=[0x02] # this will return the name of the user with id 2 in bytes - ''' + """ with _IpmiSession(**kwargs) as s: - r = s.raw_command(netfn=int(netfn), - command=int(command), - bridge_request=bridge_request, - data=data, - retry=retry, - delay_xmit=delay_xmit) + r = s.raw_command( + netfn=int(netfn), + command=int(command), + bridge_request=bridge_request, + data=data, + retry=retry, + delay_xmit=delay_xmit, + ) return r def fast_connect_test(**kwargs): - ''' + """ Returns True if connection success. This uses an aggressive timeout value! @@ -173,24 +182,31 @@ def fast_connect_test(**kwargs): .. code-block:: bash salt-call ipmi.fast_connect_test api_host=172.168.0.9 - ''' + """ try: - if 'api_login_timeout' not in kwargs: - kwargs['api_login_timeout'] = 0 + if "api_login_timeout" not in kwargs: + kwargs["api_login_timeout"] = 0 with _IpmiSession(**kwargs) as s: # TODO: should a test command be fired? - #s.raw_command(netfn=6, command=1, retry=False) + # s.raw_command(netfn=6, command=1, retry=False) return True except Exception as e: # pylint: disable=broad-except return False return True -def set_channel_access(channel=14, access_update_mode='non_volatile', - alerting=False, per_msg_auth=False, user_level_auth=False, - access_mode='always', privilege_update_mode='non_volatile', - privilege_level='administrator', **kwargs): - ''' +def set_channel_access( + channel=14, + access_update_mode="non_volatile", + alerting=False, + per_msg_auth=False, + user_level_auth=False, + access_mode="always", + privilege_update_mode="non_volatile", + privilege_level="administrator", + **kwargs +): + """ Set channel access :param channel: number [1:7] @@ -271,14 +287,22 @@ def set_channel_access(channel=14, access_update_mode='non_volatile', .. code-block:: bash salt-call ipmi.set_channel_access privilege_level='administrator' - ''' + """ with _IpmiCommand(**kwargs) as s: - return s.set_channel_access(channel, access_update_mode, alerting, per_msg_auth, user_level_auth, - access_mode, privilege_update_mode, privilege_level) + return s.set_channel_access( + channel, + access_update_mode, + alerting, + per_msg_auth, + user_level_auth, + access_mode, + privilege_update_mode, + privilege_level, + ) -def get_channel_access(channel=14, read_mode='non_volatile', **kwargs): - ''' +def get_channel_access(channel=14, read_mode="non_volatile", **kwargs): + """ :param kwargs:api_host='127.0.0.1' api_user='admin' api_pass='example' api_port=623 :param channel: number [1:7] @@ -324,13 +348,13 @@ def get_channel_access(channel=14, read_mode='non_volatile', **kwargs): .. code-block:: bash salt-call ipmi.get_channel_access channel=1 - ''' + """ with _IpmiCommand(**kwargs) as s: return s.get_channel_access(channel) def get_channel_info(channel=14, **kwargs): - ''' + """ Get channel info :param channel: number [1:7] @@ -358,14 +382,21 @@ def get_channel_info(channel=14, **kwargs): .. code-block:: bash salt-call ipmi.get_channel_info - ''' + """ with _IpmiCommand(**kwargs) as s: return s.get_channel_info(channel) -def set_user_access(uid, channel=14, callback=True, link_auth=True, ipmi_msg=True, - privilege_level='administrator', **kwargs): - ''' +def set_user_access( + uid, + channel=14, + callback=True, + link_auth=True, + ipmi_msg=True, + privilege_level="administrator", + **kwargs +): + """ Set user access :param uid: user number [1:16] @@ -424,13 +455,15 @@ def set_user_access(uid, channel=14, callback=True, link_auth=True, ipmi_msg=Tru .. code-block:: bash salt-call ipmi.set_user_access uid=2 privilege_level='operator' - ''' + """ with _IpmiCommand(**kwargs) as s: - return s.set_user_access(uid, channel, callback, link_auth, ipmi_msg, privilege_level) + return s.set_user_access( + uid, channel, callback, link_auth, ipmi_msg, privilege_level + ) def get_user_access(uid, channel=14, **kwargs): - ''' + """ Get user access :param uid: user number [1:16] @@ -462,14 +495,14 @@ def get_user_access(uid, channel=14, **kwargs): .. code-block:: bash salt-call ipmi.get_user_access uid=2 - ''' + """ ## user access available during call-in or callback direct connection with _IpmiCommand(**kwargs) as s: return s.get_user_access(uid, channel=channel) def set_user_name(uid, name, **kwargs): - ''' + """ Set user name :param uid: user number [1:16] @@ -486,13 +519,13 @@ def set_user_name(uid, name, **kwargs): .. code-block:: bash salt-call ipmi.set_user_name uid=2 name='steverweber' - ''' + """ with _IpmiCommand(**kwargs) as s: return s.set_user_name(uid, name) def get_user_name(uid, return_none_on_error=True, **kwargs): - ''' + """ Get user name :param uid: user number [1:16] @@ -509,13 +542,13 @@ def get_user_name(uid, return_none_on_error=True, **kwargs): .. code-block:: bash salt-call ipmi.get_user_name uid=2 - ''' + """ with _IpmiCommand(**kwargs) as s: return s.get_user_name(uid, return_none_on_error=True) -def set_user_password(uid, mode='set_password', password=None, **kwargs): - ''' +def set_user_password(uid, mode="set_password", password=None, **kwargs): + """ Set user password and (modes) :param uid: id number of user. see: get_names_uid()['name'] @@ -545,14 +578,14 @@ def set_user_password(uid, mode='set_password', password=None, **kwargs): salt-call ipmi.set_user_password api_host=127.0.0.1 api_user=admin api_pass=pass uid=1 password=newPass salt-call ipmi.set_user_password uid=1 mode=enable - ''' + """ with _IpmiCommand(**kwargs) as s: - s.set_user_password(uid, mode='set_password', password=password) + s.set_user_password(uid, mode="set_password", password=password) return True def get_health(**kwargs): - ''' + """ Get Summarize health This provides a summary of the health of the managed system. @@ -573,13 +606,13 @@ def get_health(**kwargs): .. code-block:: bash salt-call ipmi.get_health api_host=127.0.0.1 api_user=admin api_pass=pass - ''' + """ with _IpmiCommand(**kwargs) as s: return s.get_health() def get_power(**kwargs): - ''' + """ Get current power state The response, if successful, should contain 'powerstate' key and @@ -597,13 +630,13 @@ def get_power(**kwargs): .. code-block:: bash salt-call ipmi.get_power api_host=127.0.0.1 api_user=admin api_pass=pass - ''' + """ with _IpmiCommand(**kwargs) as s: - return s.get_power()['powerstate'] + return s.get_power()["powerstate"] def get_sensor_data(**kwargs): - ''' + """ Get sensor readings Iterates sensor reading objects @@ -620,19 +653,20 @@ def get_sensor_data(**kwargs): .. code-block:: bash salt-call ipmi.get_sensor_data api_host=127.0.0.1 api_user=admin api_pass=pass - ''' + """ import ast + with _IpmiCommand(**kwargs) as s: data = {} for reading in s.get_sensor_data(): if reading: r = ast.literal_eval(repr(reading)) - data[r.pop('name')] = r + data[r.pop("name")] = r return data def get_bootdev(**kwargs): - ''' + """ Get current boot device override information. Provides the current requested boot device. Be aware that not all IPMI @@ -652,13 +686,13 @@ def get_bootdev(**kwargs): .. code-block:: bash salt-call ipmi.get_bootdev api_host=127.0.0.1 api_user=admin api_pass=pass - ''' + """ with _IpmiCommand(**kwargs) as s: return s.get_bootdev() -def set_power(state='power_on', wait=True, **kwargs): - ''' +def set_power(state="power_on", wait=True, **kwargs): + """ Request power state change :param name: @@ -686,17 +720,17 @@ def set_power(state='power_on', wait=True, **kwargs): .. code-block:: bash salt-call ipmi.set_power state=shutdown wait=True - ''' - if state is True or state == 'power_on': - state = 'on' - if state is False or state == 'power_off': - state = 'off' + """ + if state is True or state == "power_on": + state = "on" + if state is False or state == "power_off": + state = "off" with _IpmiCommand(**kwargs) as s: return s.set_power(state, wait=wait) -def set_bootdev(bootdev='default', persist=False, uefiboot=False, **kwargs): - ''' +def set_bootdev(bootdev="default", persist=False, uefiboot=False, **kwargs): + """ Set boot device to use on next reboot :param bootdev: @@ -732,13 +766,13 @@ def set_bootdev(bootdev='default', persist=False, uefiboot=False, **kwargs): .. code-block:: bash salt-call ipmi.set_bootdev bootdev=network persist=True - ''' + """ with _IpmiCommand(**kwargs) as s: return s.set_bootdev(bootdev) def set_identify(on=True, duration=600, **kwargs): - ''' + """ Request identify light Request the identify light to turn off, on for a duration, @@ -759,13 +793,13 @@ def set_identify(on=True, duration=600, **kwargs): .. code-block:: bash salt-call ipmi.set_identify - ''' + """ with _IpmiCommand(**kwargs) as s: return s.set_identify(on=on, duration=duration) def get_channel_max_user_count(channel=14, **kwargs): - ''' + """ Get max users in channel :param channel: number [1:7] @@ -782,13 +816,13 @@ def get_channel_max_user_count(channel=14, **kwargs): .. code-block:: bash salt-call ipmi.get_channel_max_user_count - ''' + """ access = get_user_access(channel=channel, uid=1, **kwargs) - return access['channel_info']['max_user_count'] + return access["channel_info"]["max_user_count"] def get_user(uid, channel=14, **kwargs): - ''' + """ Get user from uid and access on channel :param uid: user number [1:16] @@ -819,15 +853,15 @@ def get_user(uid, channel=14, **kwargs): .. code-block:: bash salt-call ipmi.get_user uid=2 - ''' + """ name = get_user_name(uid, **kwargs) access = get_user_access(uid, channel, **kwargs) - data = {'name': name, 'uid': uid, 'channel': channel, 'access': access['access']} + data = {"name": name, "uid": uid, "channel": channel, "access": access["access"]} return data def get_users(channel=14, **kwargs): - ''' + """ get list of users and access information :param channel: number [1:7] @@ -855,14 +889,23 @@ def get_users(channel=14, **kwargs): .. code-block:: bash salt-call ipmi.get_users api_host=172.168.0.7 - ''' + """ with _IpmiCommand(**kwargs) as c: return c.get_users(channel) -def create_user(uid, name, password, channel=14, callback=False, - link_auth=True, ipmi_msg=True, privilege_level='administrator', **kwargs): - ''' +def create_user( + uid, + name, + password, + channel=14, + callback=False, + link_auth=True, + ipmi_msg=True, + privilege_level="administrator", + **kwargs +): + """ create/ensure a user is created with provided settings. :param privilege_level: @@ -886,14 +929,15 @@ def create_user(uid, name, password, channel=14, callback=False, .. code-block:: bash salt-call ipmi.create_user uid=2 name=steverweber api_host=172.168.0.7 api_pass=nevertell - ''' + """ with _IpmiCommand(**kwargs) as c: - return c.create_user(uid, name, password, channel, callback, - link_auth, ipmi_msg, privilege_level) + return c.create_user( + uid, name, password, channel, callback, link_auth, ipmi_msg, privilege_level + ) def user_delete(uid, channel=14, **kwargs): - ''' + """ Delete user (helper) :param uid: user number [1:16] @@ -910,6 +954,6 @@ def user_delete(uid, channel=14, **kwargs): .. code-block:: bash salt-call ipmi.user_delete uid=2 - ''' + """ with _IpmiCommand(**kwargs) as c: return c.user_delete(uid, channel) diff --git a/salt/modules/ipset.py b/salt/modules/ipset.py index 9c03d8c6917..c600c445730 100644 --- a/salt/modules/ipset.py +++ b/salt/modules/ipset.py @@ -1,20 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Support for ipset -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt libs -from salt.ext import six -from salt.ext.six.moves import map, range import salt.utils.path # Import third-party libs from salt._compat import ipaddress +# Import Salt libs +from salt.ext import six +from salt.ext.six.moves import map, range + # Set up logging log = logging.getLogger(__name__) @@ -28,107 +30,246 @@ def long_range(start, end): _IPSET_FAMILIES = { - 'ipv4': 'inet', - 'ip4': 'inet', - 'ipv6': 'inet6', - 'ip6': 'inet6', - } + "ipv4": "inet", + "ip4": "inet", + "ipv6": "inet6", + "ip6": "inet6", +} -_IPSET_SET_TYPES = set([ - 'bitmap:ip', - 'bitmap:ip,mac', - 'bitmap:port', - 'hash:ip', - 'hash:mac', - 'hash:ip,port', - 'hash:ip,port,ip', - 'hash:ip,port,net', - 'hash:net', - 'hash:net,net', - 'hash:net,iface', - 'hash:net,port', - 'hash:net,port,net', - 'hash:ip,mark', - 'list:set' - ]) +_IPSET_SET_TYPES = set( + [ + "bitmap:ip", + "bitmap:ip,mac", + "bitmap:port", + "hash:ip", + "hash:mac", + "hash:ip,port", + "hash:ip,port,ip", + "hash:ip,port,net", + "hash:net", + "hash:net,net", + "hash:net,iface", + "hash:net,port", + "hash:net,port,net", + "hash:ip,mark", + "list:set", + ] +) _CREATE_OPTIONS = { - 'bitmap:ip': set(['range', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'bitmap:ip,mac': set(['range', 'timeout', 'counters', 'comment', 'skbinfo']), - 'bitmap:port': set(['range', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:ip': set(['family', 'hashsize', 'maxelem', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:mac': set(['hashsize', 'maxelem', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:net': set(['family', 'hashsize', 'maxelem', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:net,net': set(['family', 'hashsize', 'maxelem', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:net,port': set(['family', 'hashsize', 'maxelem', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:net,port,net': set(['family', 'hashsize', 'maxelem', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:ip,port,ip': set(['family', 'hashsize', 'maxelem', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:ip,port,net': set(['family', 'hashsize', 'maxelem', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:ip,port': set(['family', 'hashsize', 'maxelem', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:ip,mark': set(['family', 'markmask', 'hashsize', 'maxelem', 'timeout', 'counters', 'comment', 'skbinfo']), - 'hash:net,iface': set(['family', 'hashsize', 'maxelem', 'netmask', 'timeout', 'counters', 'comment', 'skbinfo']), - 'list:set': set(['size', 'timeout', 'counters', 'comment']), + "bitmap:ip": set(["range", "netmask", "timeout", "counters", "comment", "skbinfo"]), + "bitmap:ip,mac": set(["range", "timeout", "counters", "comment", "skbinfo"]), + "bitmap:port": set(["range", "timeout", "counters", "comment", "skbinfo"]), + "hash:ip": set( + [ + "family", + "hashsize", + "maxelem", + "netmask", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "hash:mac": set( + ["hashsize", "maxelem", "timeout", "counters", "comment", "skbinfo"] + ), + "hash:net": set( + [ + "family", + "hashsize", + "maxelem", + "netmask", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "hash:net,net": set( + [ + "family", + "hashsize", + "maxelem", + "netmask", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "hash:net,port": set( + [ + "family", + "hashsize", + "maxelem", + "netmask", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "hash:net,port,net": set( + [ + "family", + "hashsize", + "maxelem", + "netmask", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "hash:ip,port,ip": set( + [ + "family", + "hashsize", + "maxelem", + "netmask", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "hash:ip,port,net": set( + [ + "family", + "hashsize", + "maxelem", + "netmask", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "hash:ip,port": set( + [ + "family", + "hashsize", + "maxelem", + "netmask", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "hash:ip,mark": set( + [ + "family", + "markmask", + "hashsize", + "maxelem", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "hash:net,iface": set( + [ + "family", + "hashsize", + "maxelem", + "netmask", + "timeout", + "counters", + "comment", + "skbinfo", + ] + ), + "list:set": set(["size", "timeout", "counters", "comment"]), } -_CREATE_OPTIONS_WITHOUT_VALUE = set(['comment', 'counters', 'skbinfo']) +_CREATE_OPTIONS_WITHOUT_VALUE = set(["comment", "counters", "skbinfo"]) _CREATE_OPTIONS_REQUIRED = { - 'bitmap:ip': ['range'], - 'bitmap:ip,mac': ['range'], - 'bitmap:port': ['range'], - 'hash:ip': [], - 'hash:mac': [], - 'hash:net': [], - 'hash:net,net': [], - 'hash:ip,port': [], - 'hash:net,port': [], - 'hash:ip,port,ip': [], - 'hash:ip,port,net': [], - 'hash:net,port,net': [], - 'hash:net,iface': [], - 'hash:ip,mark': [], - 'list:set': [] + "bitmap:ip": ["range"], + "bitmap:ip,mac": ["range"], + "bitmap:port": ["range"], + "hash:ip": [], + "hash:mac": [], + "hash:net": [], + "hash:net,net": [], + "hash:ip,port": [], + "hash:net,port": [], + "hash:ip,port,ip": [], + "hash:ip,port,net": [], + "hash:net,port,net": [], + "hash:net,iface": [], + "hash:ip,mark": [], + "list:set": [], } _ADD_OPTIONS = { - 'bitmap:ip': set(['timeout', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'bitmap:ip,mac': set(['timeout', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'bitmap:port': set(['timeout', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbprio']), - 'hash:ip': set(['timeout', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:mac': set(['timeout', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:net': set(['timeout', 'nomatch', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:net,net': set(['timeout', 'nomatch', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:net,port': set(['timeout', 'nomatch', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:net,port,net': set(['timeout', 'nomatch', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:ip,port,ip': set(['timeout', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:ip,port,net': set(['timeout', 'nomatch', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:ip,port': set(['timeout', 'nomatch', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:net,iface': set(['timeout', 'nomatch', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'hash:ip,mark': set(['timeout', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), - 'list:set': set(['timeout', 'packets', 'bytes', 'skbmark', 'skbprio', 'skbqueue']), + "bitmap:ip": set(["timeout", "packets", "bytes", "skbmark", "skbprio", "skbqueue"]), + "bitmap:ip,mac": set( + ["timeout", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "bitmap:port": set( + ["timeout", "packets", "bytes", "skbmark", "skbprio", "skbprio"] + ), + "hash:ip": set(["timeout", "packets", "bytes", "skbmark", "skbprio", "skbqueue"]), + "hash:mac": set(["timeout", "packets", "bytes", "skbmark", "skbprio", "skbqueue"]), + "hash:net": set( + ["timeout", "nomatch", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "hash:net,net": set( + ["timeout", "nomatch", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "hash:net,port": set( + ["timeout", "nomatch", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "hash:net,port,net": set( + ["timeout", "nomatch", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "hash:ip,port,ip": set( + ["timeout", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "hash:ip,port,net": set( + ["timeout", "nomatch", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "hash:ip,port": set( + ["timeout", "nomatch", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "hash:net,iface": set( + ["timeout", "nomatch", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "hash:ip,mark": set( + ["timeout", "packets", "bytes", "skbmark", "skbprio", "skbqueue"] + ), + "list:set": set(["timeout", "packets", "bytes", "skbmark", "skbprio", "skbqueue"]), } def __virtual__(): - ''' + """ Only load the module if ipset is installed - ''' - if salt.utils.path.which('ipset'): + """ + if salt.utils.path.which("ipset"): return True - return (False, 'The ipset execution modules cannot be loaded: ipset binary not in path.') + return ( + False, + "The ipset execution modules cannot be loaded: ipset binary not in path.", + ) def _ipset_cmd(): - ''' + """ Return correct command - ''' - return salt.utils.path.which('ipset') + """ + return salt.utils.path.which("ipset") def version(): - ''' + """ Return version from ipset --version CLI Example: @@ -137,14 +278,14 @@ def version(): salt '*' ipset.version - ''' - cmd = '{0} --version' . format(_ipset_cmd()) - out = __salt__['cmd.run'](cmd).split() + """ + cmd = "{0} --version".format(_ipset_cmd()) + out = __salt__["cmd.run"](cmd).split() return out[1] -def new_set(set=None, set_type=None, family='ipv4', comment=False, **kwargs): - ''' +def new_set(set=None, set_type=None, family="ipv4", comment=False, **kwargs): + """ .. versionadded:: 2014.7.0 Create new custom set @@ -159,48 +300,48 @@ def new_set(set=None, set_type=None, family='ipv4', comment=False, **kwargs): IPv6: salt '*' ipset.new_set custom_set list:set family=ipv6 - ''' + """ ipset_family = _IPSET_FAMILIES[family] if not set: - return 'Error: Set needs to be specified' + return "Error: Set needs to be specified" if not set_type: - return 'Error: Set Type needs to be specified' + return "Error: Set Type needs to be specified" if set_type not in _IPSET_SET_TYPES: - return 'Error: Set Type is invalid' + return "Error: Set Type is invalid" # Check for required arguments for item in _CREATE_OPTIONS_REQUIRED[set_type]: if item not in kwargs: - return 'Error: {0} is a required argument'.format(item) + return "Error: {0} is a required argument".format(item) - cmd = '{0} create {1} {2}'.format(_ipset_cmd(), set, set_type) + cmd = "{0} create {1} {2}".format(_ipset_cmd(), set, set_type) for item in _CREATE_OPTIONS[set_type]: if item in kwargs: if item in _CREATE_OPTIONS_WITHOUT_VALUE: - cmd = '{0} {1} '.format(cmd, item) + cmd = "{0} {1} ".format(cmd, item) else: - cmd = '{0} {1} {2} '.format(cmd, item, kwargs[item]) + cmd = "{0} {1} {2} ".format(cmd, item, kwargs[item]) # Family only valid for certain set types - if 'family' in _CREATE_OPTIONS[set_type]: - cmd = '{0} family {1}'.format(cmd, ipset_family) + if "family" in _CREATE_OPTIONS[set_type]: + cmd = "{0} family {1}".format(cmd, ipset_family) if comment: - cmd = '{0} comment'.format(cmd) + cmd = "{0} comment".format(cmd) - out = __salt__['cmd.run'](cmd, python_shell=False) + out = __salt__["cmd.run"](cmd, python_shell=False) if not out: out = True return out -def delete_set(set=None, family='ipv4'): - ''' +def delete_set(set=None, family="ipv4"): + """ .. versionadded:: 2014.7.0 Delete ipset set. @@ -213,21 +354,21 @@ def delete_set(set=None, family='ipv4'): IPv6: salt '*' ipset.delete_set custom_set family=ipv6 - ''' + """ if not set: - return 'Error: Set needs to be specified' + return "Error: Set needs to be specified" - cmd = '{0} destroy {1}'.format(_ipset_cmd(), set) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} destroy {1}".format(_ipset_cmd(), set) + out = __salt__["cmd.run"](cmd, python_shell=False) if not out: out = True return out -def rename_set(set=None, new_set=None, family='ipv4'): - ''' +def rename_set(set=None, new_set=None, family="ipv4"): + """ .. versionadded:: 2014.7.0 Delete ipset set. @@ -240,32 +381,32 @@ def rename_set(set=None, new_set=None, family='ipv4'): IPv6: salt '*' ipset.rename_set custom_set new_set=new_set_name family=ipv6 - ''' + """ if not set: - return 'Error: Set needs to be specified' + return "Error: Set needs to be specified" if not new_set: - return 'Error: New name for set needs to be specified' + return "Error: New name for set needs to be specified" settype = _find_set_type(set) if not settype: - return 'Error: Set does not exist' + return "Error: Set does not exist" settype = _find_set_type(new_set) if settype: - return 'Error: New Set already exists' + return "Error: New Set already exists" - cmd = '{0} rename {1} {2}'.format(_ipset_cmd(), set, new_set) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} rename {1} {2}".format(_ipset_cmd(), set, new_set) + out = __salt__["cmd.run"](cmd, python_shell=False) if not out: out = True return out -def list_sets(family='ipv4'): - ''' +def list_sets(family="ipv4"): + """ .. versionadded:: 2014.7.0 List all ipset sets. @@ -276,11 +417,11 @@ def list_sets(family='ipv4'): salt '*' ipset.list_sets - ''' - cmd = '{0} list -t'.format(_ipset_cmd()) - out = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = "{0} list -t".format(_ipset_cmd()) + out = __salt__["cmd.run"](cmd, python_shell=False) - _tmp = out.split('\n') + _tmp = out.split("\n") count = 0 sets = [] @@ -290,13 +431,13 @@ def list_sets(family='ipv4'): count = count + 1 sets.append({}) continue - key, value = item.split(':', 1) + key, value = item.split(":", 1) sets[count][key] = value[1:] return sets -def check_set(set=None, family='ipv4'): - ''' +def check_set(set=None, family="ipv4"): + """ Check that given ipset set exists. .. versionadded:: 2014.7.0 @@ -307,9 +448,9 @@ def check_set(set=None, family='ipv4'): salt '*' ipset.check_set setname - ''' + """ if not set: - return 'Error: Set needs to be specified' + return "Error: Set needs to be specified" setinfo = _find_set_info(set) if not setinfo: @@ -317,8 +458,8 @@ def check_set(set=None, family='ipv4'): return True -def add(setname=None, entry=None, family='ipv4', **kwargs): - ''' +def add(setname=None, entry=None, family="ipv4", **kwargs): + """ Append an entry to the specified set. CLI Example: @@ -329,57 +470,57 @@ def add(setname=None, entry=None, family='ipv4', **kwargs): salt '*' ipset.add setname 192.168.0.3,AA:BB:CC:DD:EE:FF - ''' + """ if not setname: - return 'Error: Set needs to be specified' + return "Error: Set needs to be specified" if not entry: - return 'Error: Entry needs to be specified' + return "Error: Entry needs to be specified" setinfo = _find_set_info(setname) if not setinfo: - return 'Error: Set {0} does not exist'.format(setname) + return "Error: Set {0} does not exist".format(setname) - settype = setinfo['Type'] + settype = setinfo["Type"] - cmd = '{0}'.format(entry) + cmd = "{0}".format(entry) - if 'timeout' in kwargs: - if 'timeout' not in setinfo['Header']: - return 'Error: Set {0} not created with timeout support'.format(setname) + if "timeout" in kwargs: + if "timeout" not in setinfo["Header"]: + return "Error: Set {0} not created with timeout support".format(setname) - if 'packets' in kwargs or 'bytes' in kwargs: - if 'counters' not in setinfo['Header']: - return 'Error: Set {0} not created with counters support'.format(setname) + if "packets" in kwargs or "bytes" in kwargs: + if "counters" not in setinfo["Header"]: + return "Error: Set {0} not created with counters support".format(setname) - if 'comment' in kwargs: - if 'comment' not in setinfo['Header']: - return 'Error: Set {0} not created with comment support'.format(setname) - if 'comment' not in entry: - cmd = '{0} comment "{1}"'.format(cmd, kwargs['comment']) + if "comment" in kwargs: + if "comment" not in setinfo["Header"]: + return "Error: Set {0} not created with comment support".format(setname) + if "comment" not in entry: + cmd = '{0} comment "{1}"'.format(cmd, kwargs["comment"]) - if len(set(['skbmark', 'skbprio', 'skbqueue']) & set(kwargs.keys())) > 0: - if 'skbinfo' not in setinfo['Header']: - return 'Error: Set {0} not created with skbinfo support'.format(setname) + if len(set(["skbmark", "skbprio", "skbqueue"]) & set(kwargs.keys())) > 0: + if "skbinfo" not in setinfo["Header"]: + return "Error: Set {0} not created with skbinfo support".format(setname) for item in _ADD_OPTIONS[settype]: if item in kwargs: - cmd = '{0} {1} {2}'.format(cmd, item, kwargs[item]) + cmd = "{0} {1} {2}".format(cmd, item, kwargs[item]) current_members = _find_set_members(setname) if cmd in current_members: - return 'Warn: Entry {0} already exists in set {1}'.format(cmd, setname) + return "Warn: Entry {0} already exists in set {1}".format(cmd, setname) # Using -exist to ensure entries are updated if the comment changes - cmd = '{0} add -exist {1} {2}'.format(_ipset_cmd(), setname, cmd) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} add -exist {1} {2}".format(_ipset_cmd(), setname, cmd) + out = __salt__["cmd.run"](cmd, python_shell=False) if len(out) == 0: - return 'Success' - return 'Error: {0}'.format(out) + return "Success" + return "Error: {0}".format(out) -def delete(set=None, entry=None, family='ipv4', **kwargs): - ''' +def delete(set=None, entry=None, family="ipv4", **kwargs): + """ Delete an entry from the specified set. CLI Example: @@ -388,27 +529,27 @@ def delete(set=None, entry=None, family='ipv4', **kwargs): salt '*' ipset.delete setname 192.168.0.3,AA:BB:CC:DD:EE:FF - ''' + """ if not set: - return 'Error: Set needs to be specified' + return "Error: Set needs to be specified" if not entry: - return 'Error: Entry needs to be specified' + return "Error: Entry needs to be specified" settype = _find_set_type(set) if not settype: - return 'Error: Set {0} does not exist'.format(set) + return "Error: Set {0} does not exist".format(set) - cmd = '{0} del {1} {2}'.format(_ipset_cmd(), set, entry) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} del {1} {2}".format(_ipset_cmd(), set, entry) + out = __salt__["cmd.run"](cmd, python_shell=False) if len(out) == 0: - return 'Success' - return 'Error: {0}'.format(out) + return "Success" + return "Error: {0}".format(out) -def check(set=None, entry=None, family='ipv4'): - ''' +def check(set=None, entry=None, family="ipv4"): + """ Check that an entry exists in the specified set. set @@ -433,15 +574,15 @@ def check(set=None, entry=None, family='ipv4'): salt '*' ipset.check setname '192.168.0.1 comment "Hello"' - ''' + """ if not set: - return 'Error: Set needs to be specified' + return "Error: Set needs to be specified" if not entry: - return 'Error: Entry needs to be specified' + return "Error: Entry needs to be specified" settype = _find_set_type(set) if not settype: - return 'Error: Set {0} does not exist'.format(set) + return "Error: Set {0} does not exist".format(set) current_members = _parse_members(settype, _find_set_members(set)) @@ -461,8 +602,8 @@ def check(set=None, entry=None, family='ipv4'): return False -def test(set=None, entry=None, family='ipv4', **kwargs): - ''' +def test(set=None, entry=None, family="ipv4", **kwargs): + """ Test if an entry is in the specified set. CLI Example: @@ -473,28 +614,28 @@ def test(set=None, entry=None, family='ipv4', **kwargs): IPv6: salt '*' ipset.test setname fd81:fc56:9ac7::/48 - ''' + """ if not set: - return 'Error: Set needs to be specified' + return "Error: Set needs to be specified" if not entry: - return 'Error: Entry needs to be specified' + return "Error: Entry needs to be specified" settype = _find_set_type(set) if not settype: - return 'Error: Set {0} does not exist'.format(set) + return "Error: Set {0} does not exist".format(set) - cmd = '{0} test {1} {2}'.format(_ipset_cmd(), set, entry) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} test {1} {2}".format(_ipset_cmd(), set, entry) + out = __salt__["cmd.run_all"](cmd, python_shell=False) - if out['retcode'] > 0: + if out["retcode"] > 0: # Entry doesn't exist in set return false return False return True -def flush(set=None, family='ipv4'): - ''' +def flush(set=None, family="ipv4"): + """ Flush entries in the specified set, Flush all sets if set is not specified. @@ -510,18 +651,18 @@ def flush(set=None, family='ipv4'): salt '*' ipset.flush salt '*' ipset.flush set - ''' + """ settype = _find_set_type(set) if not settype: - return 'Error: Set {0} does not exist'.format(set) + return "Error: Set {0} does not exist".format(set) ipset_family = _IPSET_FAMILIES[family] if set: - cmd = '{0} flush {1}'.format(_ipset_cmd(), set) + cmd = "{0} flush {1}".format(_ipset_cmd(), set) else: - cmd = '{0} flush'.format(_ipset_cmd()) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} flush".format(_ipset_cmd()) + out = __salt__["cmd.run"](cmd, python_shell=False) if len(out) == 0: return True @@ -530,58 +671,58 @@ def flush(set=None, family='ipv4'): def _find_set_members(set): - ''' + """ Return list of members for a set - ''' + """ - cmd = '{0} list {1}'.format(_ipset_cmd(), set) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} list {1}".format(_ipset_cmd(), set) + out = __salt__["cmd.run_all"](cmd, python_shell=False) - if out['retcode'] > 0: + if out["retcode"] > 0: # Set doesn't exist return false return False - _tmp = out['stdout'].split('\n') + _tmp = out["stdout"].split("\n") members = [] startMembers = False for i in _tmp: if startMembers: members.append(i) - if 'Members:' in i: + if "Members:" in i: startMembers = True return members def _find_set_info(set): - ''' + """ Return information about the set - ''' + """ - cmd = '{0} list -t {1}'.format(_ipset_cmd(), set) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} list -t {1}".format(_ipset_cmd(), set) + out = __salt__["cmd.run_all"](cmd, python_shell=False) - if out['retcode'] > 0: + if out["retcode"] > 0: # Set doesn't exist return false return False setinfo = {} - _tmp = out['stdout'].split('\n') + _tmp = out["stdout"].split("\n") for item in _tmp: # Only split if item has a colon - if ':' in item: - key, value = item.split(':', 1) + if ":" in item: + key, value = item.split(":", 1) setinfo[key] = value[1:] return setinfo def _find_set_type(set): - ''' + """ Find the type of the set - ''' + """ setinfo = _find_set_info(set) if setinfo: - return setinfo['Type'] + return setinfo["Type"] else: return False @@ -595,22 +736,22 @@ def _parse_members(settype, members): def _parse_member(settype, member, strict=False): - subtypes = settype.split(':')[1].split(',') + subtypes = settype.split(":")[1].split(",") - all_parts = member.split(' ', 1) - parts = all_parts[0].split(',') + all_parts = member.split(" ", 1) + parts = all_parts[0].split(",") parsed_member = [] for i in range(len(subtypes)): subtype = subtypes[i] part = parts[i] - if subtype in ['ip', 'net']: + if subtype in ["ip", "net"]: try: - if '/' in part: + if "/" in part: part = ipaddress.ip_network(part, strict=strict) - elif '-' in part: - start, end = list(map(ipaddress.ip_address, part.split('-'))) + elif "-" in part: + start, end = list(map(ipaddress.ip_address, part.split("-"))) part = list(ipaddress.summarize_address_range(start, end)) else: @@ -618,7 +759,7 @@ def _parse_member(settype, member, strict=False): except ValueError: pass - elif subtype == 'port': + elif subtype == "port": part = int(part) parsed_member.append(part) @@ -679,10 +820,8 @@ def _compare_member_parts(member_part, entry_part): def _is_network(o): - return isinstance(o, ipaddress.IPv4Network) \ - or isinstance(o, ipaddress.IPv6Network) + return isinstance(o, ipaddress.IPv4Network) or isinstance(o, ipaddress.IPv6Network) def _is_address(o): - return isinstance(o, ipaddress.IPv4Address) \ - or isinstance(o, ipaddress.IPv6Address) + return isinstance(o, ipaddress.IPv4Address) or isinstance(o, ipaddress.IPv6Address) diff --git a/salt/modules/iptables.py b/salt/modules/iptables.py index e232c6931fd..8b58543f0b3 100644 --- a/salt/modules/iptables.py +++ b/salt/modules/iptables.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for iptables Configuration Options @@ -24,108 +24,114 @@ master config. The configuration is read using :py:func:`config.get - "-A POSTROUTING" - "-A CATTLE_POSTROUTING" - "-A FORWARD" -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import python libs import os import re +import string import sys import uuid -import string # Import salt libs import salt.utils.args import salt.utils.files import salt.utils.path -from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS from salt.exceptions import SaltException from salt.ext import six +from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS -import logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if iptables is installed - ''' - if not salt.utils.path.which('iptables'): - return (False, 'The iptables execution module cannot be loaded: iptables not installed.') + """ + if not salt.utils.path.which("iptables"): + return ( + False, + "The iptables execution module cannot be loaded: iptables not installed.", + ) return True -def _iptables_cmd(family='ipv4'): - ''' +def _iptables_cmd(family="ipv4"): + """ Return correct command based on the family, e.g. ipv4 or ipv6 - ''' - if family == 'ipv6': - return salt.utils.path.which('ip6tables') + """ + if family == "ipv6": + return salt.utils.path.which("ip6tables") else: - return salt.utils.path.which('iptables') + return salt.utils.path.which("iptables") -def _has_option(option, family='ipv4'): - ''' +def _has_option(option, family="ipv4"): + """ Return truth of whether iptables has `option`. For example: .. code-block:: python _has_option('--wait') _has_option('--check', family='ipv6') - ''' - cmd = '{0} --help'.format(_iptables_cmd(family)) - if option in __salt__['cmd.run'](cmd, output_loglevel='quiet'): + """ + cmd = "{0} --help".format(_iptables_cmd(family)) + if option in __salt__["cmd.run"](cmd, output_loglevel="quiet"): return True return False -def _conf(family='ipv4'): - ''' +def _conf(family="ipv4"): + """ Some distros have a specific location for config files - ''' - if __grains__['os_family'] == 'RedHat': - if family == 'ipv6': - return '/etc/sysconfig/ip6tables' + """ + if __grains__["os_family"] == "RedHat": + if family == "ipv6": + return "/etc/sysconfig/ip6tables" else: - return '/etc/sysconfig/iptables' - elif __grains__['os_family'] == 'Arch': - if family == 'ipv6': - return '/etc/iptables/ip6tables.rules' + return "/etc/sysconfig/iptables" + elif __grains__["os_family"] == "Arch": + if family == "ipv6": + return "/etc/iptables/ip6tables.rules" else: - return '/etc/iptables/iptables.rules' - elif __grains__['os_family'] == 'Debian': - if family == 'ipv6': - return '/etc/iptables/rules.v6' + return "/etc/iptables/iptables.rules" + elif __grains__["os_family"] == "Debian": + if family == "ipv6": + return "/etc/iptables/rules.v6" else: - return '/etc/iptables/rules.v4' - elif __grains__['os'] == 'Gentoo': - if family == 'ipv6': - return '/var/lib/ip6tables/rules-save' + return "/etc/iptables/rules.v4" + elif __grains__["os"] == "Gentoo": + if family == "ipv6": + return "/var/lib/ip6tables/rules-save" else: - return '/var/lib/iptables/rules-save' - elif __grains__['os_family'] == 'Suse': + return "/var/lib/iptables/rules-save" + elif __grains__["os_family"] == "Suse": # SuSE does not seem to use separate files for IPv4 and IPv6 - return '/etc/sysconfig/scripts/SuSEfirewall2-custom' - elif __grains__['os_family'] == 'Void': - if family == 'ipv4': - return '/etc/iptables/iptables.rules' + return "/etc/sysconfig/scripts/SuSEfirewall2-custom" + elif __grains__["os_family"] == "Void": + if family == "ipv4": + return "/etc/iptables/iptables.rules" else: - return '/etc/iptables/ip6tables.rules' - elif __grains__['os'] == 'Alpine': - if family == 'ipv6': - return '/etc/iptables/rules6-save' + return "/etc/iptables/ip6tables.rules" + elif __grains__["os"] == "Alpine": + if family == "ipv6": + return "/etc/iptables/rules6-save" else: - return '/etc/iptables/rules-save' + return "/etc/iptables/rules-save" else: - raise SaltException('Saving iptables to file is not' + - ' supported on {0}.'.format(__grains__['os']) + - ' Please file an issue with SaltStack') + raise SaltException( + "Saving iptables to file is not" + + " supported on {0}.".format(__grains__["os"]) + + " Please file an issue with SaltStack" + ) def _conf_save_filters(): - ''' + """ Return array of strings from `save_filters` in config. This array will be pulled from minion config, minion grains, @@ -134,13 +140,13 @@ def _conf_save_filters(): .. code-block:: python _conf_save_filters() - ''' - config = __salt__['config.option']('iptables.save_filters', []) + """ + config = __salt__["config.option"]("iptables.save_filters", []) return config def _regex_iptables_save(cmd_output, filters=None): - ''' + """ Return string with `save_filter` regex entries removed. For example: If `filters` is not provided, it will be pulled from minion config, @@ -150,32 +156,31 @@ def _regex_iptables_save(cmd_output, filters=None): .. code-block:: python _regex_iptables_save(cmd_output, ['-A DOCKER*']) - ''' + """ # grab RE compiled filters from context for performance - if 'iptables.save_filters' not in __context__: - __context__['iptables.save_filters'] = [] + if "iptables.save_filters" not in __context__: + __context__["iptables.save_filters"] = [] for pattern in filters or _conf_save_filters(): try: - __context__['iptables.save_filters']\ - .append(re.compile(pattern)) + __context__["iptables.save_filters"].append(re.compile(pattern)) except re.error as e: - log.warning('Skipping regex rule: \'%s\': %s', - pattern, e) + log.warning("Skipping regex rule: '%s': %s", pattern, e) continue - if len(__context__['iptables.save_filters']) > 0: + if len(__context__["iptables.save_filters"]) > 0: # line by line get rid of any regex matches - _filtered_cmd_output = \ - [line for line in cmd_output.splitlines(True) - if not any(reg.search(line) for reg - in __context__['iptables.save_filters'])] - return ''.join(_filtered_cmd_output) + _filtered_cmd_output = [ + line + for line in cmd_output.splitlines(True) + if not any(reg.search(line) for reg in __context__["iptables.save_filters"]) + ] + return "".join(_filtered_cmd_output) return cmd_output -def version(family='ipv4'): - ''' +def version(family="ipv4"): + """ Return version from iptables --version CLI Example: @@ -186,15 +191,22 @@ def version(family='ipv4'): IPv6: salt '*' iptables.version family=ipv6 - ''' - cmd = '{0} --version' . format(_iptables_cmd(family)) - out = __salt__['cmd.run'](cmd).split() + """ + cmd = "{0} --version".format(_iptables_cmd(family)) + out = __salt__["cmd.run"](cmd).split() return out[1] -def build_rule(table='filter', chain=None, command=None, position='', full=None, family='ipv4', - **kwargs): - ''' +def build_rule( + table="filter", + chain=None, + command=None, + position="", + full=None, + family="ipv4", + **kwargs +): + """ Build a well-formatted iptables rule based on kwargs. A `table` and `chain` are not required, unless `full` is True. @@ -250,129 +262,151 @@ def build_rule(table='filter', chain=None, command=None, position='', full=None, salt '*' iptables.build_rule filter INPUT command=I position=3 \\ full=True match=state connstate=RELATED,ESTABLISHED jump=ACCEPT \\ family=ipv6 - ''' - if 'target' in kwargs: - kwargs['jump'] = kwargs.pop('target') + """ + if "target" in kwargs: + kwargs["jump"] = kwargs.pop("target") # Ignore name and state for this function - kwargs.pop('name', None) - kwargs.pop('state', None) + kwargs.pop("name", None) + kwargs.pop("state", None) - for ignore in list(_STATE_INTERNAL_KEYWORDS) + ['chain', 'save', 'table']: + for ignore in list(_STATE_INTERNAL_KEYWORDS) + ["chain", "save", "table"]: if ignore in kwargs: del kwargs[ignore] rule = [] protocol = False - bang_not_pat = re.compile(r'(!|not)\s?') + bang_not_pat = re.compile(r"(!|not)\s?") def maybe_add_negation(arg): - ''' + """ Will check if the defined argument is intended to be negated, (i.e. prefixed with '!' or 'not'), and add a '! ' to the rule. The prefix will be removed from the value in the kwargs dict. - ''' + """ value = str(kwargs[arg]) - if value.startswith('!') or value.startswith('not'): - kwargs[arg] = re.sub(bang_not_pat, '', value) - return '! ' - return '' + if value.startswith("!") or value.startswith("not"): + kwargs[arg] = re.sub(bang_not_pat, "", value) + return "! " + return "" - if 'if' in kwargs: - rule.append('{0}-i {1}'.format(maybe_add_negation('if'), kwargs['if'])) - del kwargs['if'] + if "if" in kwargs: + rule.append("{0}-i {1}".format(maybe_add_negation("if"), kwargs["if"])) + del kwargs["if"] - if 'of' in kwargs: - rule.append('{0}-o {1}'.format(maybe_add_negation('of'), kwargs['of'])) - del kwargs['of'] + if "of" in kwargs: + rule.append("{0}-o {1}".format(maybe_add_negation("of"), kwargs["of"])) + del kwargs["of"] - if 'proto' in kwargs and kwargs.get('match') != 'policy': - kwargs['protocol'] = kwargs['proto'] - del kwargs['proto'] + if "proto" in kwargs and kwargs.get("match") != "policy": + kwargs["protocol"] = kwargs["proto"] + del kwargs["proto"] # Handle the case 'proto' in kwargs and kwargs.get('match') == 'policy' below - if 'protocol' in kwargs: + if "protocol" in kwargs: if not protocol: - rule.append('{0}-p {1}'.format(maybe_add_negation('protocol'), kwargs['protocol'])) + rule.append( + "{0}-p {1}".format(maybe_add_negation("protocol"), kwargs["protocol"]) + ) protocol = True - del kwargs['protocol'] + del kwargs["protocol"] - if 'match' in kwargs: - match_value = kwargs['match'] + if "match" in kwargs: + match_value = kwargs["match"] if not isinstance(match_value, list): - match_value = match_value.split(',') + match_value = match_value.split(",") for match in match_value: - rule.append('-m {0}'.format(match)) - if 'name_' in kwargs and match.strip() in ('pknock', 'quota2', 'recent'): - rule.append('--name {0}'.format(kwargs['name_'])) - del kwargs['name_'] - if 'proto' in kwargs and kwargs.get('match') == 'policy': - rule.append('{0}--proto {1}'.format(maybe_add_negation('proto'), kwargs['proto'])) - del kwargs['proto'] - del kwargs['match'] + rule.append("-m {0}".format(match)) + if "name_" in kwargs and match.strip() in ("pknock", "quota2", "recent"): + rule.append("--name {0}".format(kwargs["name_"])) + del kwargs["name_"] + if "proto" in kwargs and kwargs.get("match") == "policy": + rule.append( + "{0}--proto {1}".format(maybe_add_negation("proto"), kwargs["proto"]) + ) + del kwargs["proto"] + del kwargs["match"] - if 'match-set' in kwargs: - if isinstance(kwargs['match-set'], six.string_types): - kwargs['match-set'] = [kwargs['match-set']] - for match_set in kwargs['match-set']: - negative_match_set = '' - if match_set.startswith('!') or match_set.startswith('not'): - negative_match_set = '! ' - match_set = re.sub(bang_not_pat, '', match_set) - rule.append('-m set {0}--match-set {1}'.format(negative_match_set, match_set)) - del kwargs['match-set'] + if "match-set" in kwargs: + if isinstance(kwargs["match-set"], six.string_types): + kwargs["match-set"] = [kwargs["match-set"]] + for match_set in kwargs["match-set"]: + negative_match_set = "" + if match_set.startswith("!") or match_set.startswith("not"): + negative_match_set = "! " + match_set = re.sub(bang_not_pat, "", match_set) + rule.append( + "-m set {0}--match-set {1}".format(negative_match_set, match_set) + ) + del kwargs["match-set"] - if 'connstate' in kwargs: - if '-m state' not in rule: - rule.append('-m state') + if "connstate" in kwargs: + if "-m state" not in rule: + rule.append("-m state") - rule.append('{0}--state {1}'.format(maybe_add_negation('connstate'), kwargs['connstate'])) + rule.append( + "{0}--state {1}".format( + maybe_add_negation("connstate"), kwargs["connstate"] + ) + ) - del kwargs['connstate'] + del kwargs["connstate"] - if 'dport' in kwargs: - rule.append('{0}--dport {1}'.format(maybe_add_negation('dport'), kwargs['dport'])) - del kwargs['dport'] + if "dport" in kwargs: + rule.append( + "{0}--dport {1}".format(maybe_add_negation("dport"), kwargs["dport"]) + ) + del kwargs["dport"] - if 'sport' in kwargs: - rule.append('{0}--sport {1}'.format(maybe_add_negation('sport'), kwargs['sport'])) - del kwargs['sport'] + if "sport" in kwargs: + rule.append( + "{0}--sport {1}".format(maybe_add_negation("sport"), kwargs["sport"]) + ) + del kwargs["sport"] - for multiport_arg in ('dports', 'sports'): + for multiport_arg in ("dports", "sports"): if multiport_arg in kwargs: - if '-m multiport' not in rule: - rule.append('-m multiport') + if "-m multiport" not in rule: + rule.append("-m multiport") if not protocol: - return 'Error: protocol must be specified' + return "Error: protocol must be specified" mp_value = kwargs[multiport_arg] if isinstance(mp_value, list): - if any(i for i in mp_value if str(i).startswith('!') or str(i).startswith('not')): - mp_value = [re.sub(bang_not_pat, '', str(item)) for item in mp_value] - rule.append('!') - dports = ','.join(str(i) for i in mp_value) + if any( + i + for i in mp_value + if str(i).startswith("!") or str(i).startswith("not") + ): + mp_value = [ + re.sub(bang_not_pat, "", str(item)) for item in mp_value + ] + rule.append("!") + dports = ",".join(str(i) for i in mp_value) else: - if str(mp_value).startswith('!') or str(mp_value).startswith('not'): - dports = re.sub(bang_not_pat, '', mp_value) - rule.append('!') + if str(mp_value).startswith("!") or str(mp_value).startswith("not"): + dports = re.sub(bang_not_pat, "", mp_value) + rule.append("!") else: dports = mp_value - rule.append('--{0} {1}'.format(multiport_arg, dports)) + rule.append("--{0} {1}".format(multiport_arg, dports)) del kwargs[multiport_arg] - if 'comment' in kwargs: - if '-m comment' not in rule: - rule.append('-m comment') + if "comment" in kwargs: + if "-m comment" not in rule: + rule.append("-m comment") - rule.append('--comment "{0}"'.format(kwargs['comment'])) - del kwargs['comment'] + rule.append('--comment "{0}"'.format(kwargs["comment"])) + del kwargs["comment"] # --set in ipset is deprecated, works but returns error. # rewrite to --match-set if not empty, otherwise treat as recent option - if 'set' in kwargs and kwargs['set']: - rule.append('{0}--match-set {1}'.format(maybe_add_negation('set'), kwargs['set'])) - del kwargs['set'] + if "set" in kwargs and kwargs["set"]: + rule.append( + "{0}--match-set {1}".format(maybe_add_negation("set"), kwargs["set"]) + ) + del kwargs["set"] # Jumps should appear last, except for any arguments that are passed to # jumps, which of course need to follow. @@ -380,137 +414,133 @@ def build_rule(table='filter', chain=None, command=None, position='', full=None, # All jump arguments as extracted from man iptables-extensions, man iptables, # man xtables-addons and http://www.iptables.info/en/iptables-targets-and-jumps.html after_jump_arguments = ( - 'j', # j and jump needs to be first - 'jump', - + "j", # j and jump needs to be first + "jump", # IPTABLES - 'add-set', - 'and-mark', - 'and-tos', - 'checksum-fill', - 'clamp-mss-to-pmtu', - 'clustermac', - 'ctevents', - 'ctmask', - 'del-set', - 'ecn-tcp-remove', - 'exist', - 'expevents', - 'gateway', - 'hash-init', - 'hashmode', - 'helper', - 'label', - 'local-node', - 'log-ip-options', - 'log-level', - 'log-prefix', - 'log-tcp-options', - 'log-tcp-sequence', - 'log-uid', - 'mask', - 'new', - 'nfmask', - 'nflog-group', - 'nflog-prefix', - 'nflog-range', - 'nflog-threshold', - 'nodst', - 'notrack', - 'on-ip', - 'on-port', - 'or-mark', - 'or-tos', - 'persistent', - 'queue-balance', - 'queue-bypass', - 'queue-num', - 'random', - 'rateest-ewmalog', - 'rateest-interval', - 'rateest-name', - 'reject-with', - 'restore', - 'restore-mark', + "add-set", + "and-mark", + "and-tos", + "checksum-fill", + "clamp-mss-to-pmtu", + "clustermac", + "ctevents", + "ctmask", + "del-set", + "ecn-tcp-remove", + "exist", + "expevents", + "gateway", + "hash-init", + "hashmode", + "helper", + "label", + "local-node", + "log-ip-options", + "log-level", + "log-prefix", + "log-tcp-options", + "log-tcp-sequence", + "log-uid", + "mask", + "new", + "nfmask", + "nflog-group", + "nflog-prefix", + "nflog-range", + "nflog-threshold", + "nodst", + "notrack", + "on-ip", + "on-port", + "or-mark", + "or-tos", + "persistent", + "queue-balance", + "queue-bypass", + "queue-num", + "random", + "rateest-ewmalog", + "rateest-interval", + "rateest-name", + "reject-with", + "restore", + "restore-mark", #'save', # no arg, problematic name: How do we avoid collision with this? - 'save-mark', - 'selctx', - 'set-class', - 'set-dscp', - 'set-dscp-class', - 'set-mark', - 'set-mss', - 'set-tos', - 'set-xmark', - 'strip-options', - 'timeout', - 'to', - 'to-destination', - 'to-ports', - 'to-source', - 'total-nodes', - 'tproxy-mark', - 'ttl-dec', - 'ttl-inc', - 'ttl-set', - 'type', - 'ulog-cprange', - 'ulog-nlgroup', - 'ulog-prefix', - 'ulog-qthreshold', - 'xor-mark', - 'xor-tos', - 'zone', - + "save-mark", + "selctx", + "set-class", + "set-dscp", + "set-dscp-class", + "set-mark", + "set-mss", + "set-tos", + "set-xmark", + "strip-options", + "timeout", + "to", + "to-destination", + "to-ports", + "to-source", + "total-nodes", + "tproxy-mark", + "ttl-dec", + "ttl-inc", + "ttl-set", + "type", + "ulog-cprange", + "ulog-nlgroup", + "ulog-prefix", + "ulog-qthreshold", + "xor-mark", + "xor-tos", + "zone", # IPTABLES-EXTENSIONS - 'dst-pfx', - 'hl-dec', - 'hl-inc', - 'hl-set', - 'hmark-dport-mask', - 'hmark-dst-prefix', - 'hmark-mod', - 'hmark-offset', - 'hmark-proto-mask', - 'hmark-rnd', - 'hmark-spi-mask', - 'hmark-sport-mask', - 'hmark-src-prefix', - 'hmark-tuple', - 'led-always-blink', - 'led-delay', - 'led-trigger-id', - 'queue-cpu-fanout', - 'src-pfx', - + "dst-pfx", + "hl-dec", + "hl-inc", + "hl-set", + "hmark-dport-mask", + "hmark-dst-prefix", + "hmark-mod", + "hmark-offset", + "hmark-proto-mask", + "hmark-rnd", + "hmark-spi-mask", + "hmark-sport-mask", + "hmark-src-prefix", + "hmark-tuple", + "led-always-blink", + "led-delay", + "led-trigger-id", + "queue-cpu-fanout", + "src-pfx", # WEB - 'to-port', - + "to-port", # XTABLES - 'addr', - 'and-mask', - 'delude', - 'honeypot', - 'or-mask', - 'prefix', - 'reset', - 'reuse', - 'set-mac', - 'shift', - 'static', - 'tarpit', - 'tname', - 'ttl', + "addr", + "and-mask", + "delude", + "honeypot", + "or-mask", + "prefix", + "reset", + "reuse", + "set-mac", + "shift", + "static", + "tarpit", + "tname", + "ttl", ) for after_jump_argument in after_jump_arguments: if after_jump_argument in kwargs: value = kwargs[after_jump_argument] - if value in (None, ''): # options without arguments - after_jump.append('--{0}'.format(after_jump_argument)) + if value in (None, ""): # options without arguments + after_jump.append("--{0}".format(after_jump_argument)) elif any(ws_char in str(value) for ws_char in string.whitespace): after_jump.append('--{0} "{1}"'.format(after_jump_argument, value)) else: - after_jump.append('--{0} {1}'.format(after_jump_argument, value)) + after_jump.append("--{0} {1}".format(after_jump_argument, value)) del kwargs[after_jump_argument] for key in kwargs: @@ -518,35 +548,43 @@ def build_rule(table='filter', chain=None, command=None, position='', full=None, # don't use .items() since maybe_add_negation removes the prefix from # the value in the kwargs, thus we need to fetch it after that has run value = kwargs[key] - flag = '-' if len(key) == 1 else '--' - value = '' if value in (None, '') else ' {0}'.format(value) - rule.append('{0}{1}{2}{3}'.format(negation, flag, key, value)) + flag = "-" if len(key) == 1 else "--" + value = "" if value in (None, "") else " {0}".format(value) + rule.append("{0}{1}{2}{3}".format(negation, flag, key, value)) rule += after_jump if full: if not table: - return 'Error: Table needs to be specified' + return "Error: Table needs to be specified" if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" if not command: - return 'Error: Command needs to be specified' + return "Error: Command needs to be specified" - if command in 'ACDIRLSFZNXPE': - flag = '-' + if command in "ACDIRLSFZNXPE": + flag = "-" else: - flag = '--' + flag = "--" - wait = '--wait' if _has_option('--wait', family) else '' + wait = "--wait" if _has_option("--wait", family) else "" - return '{0} {1} -t {2} {3}{4} {5} {6} {7}'.format(_iptables_cmd(family), - wait, table, flag, command, chain, position, ' '.join(rule)) + return "{0} {1} -t {2} {3}{4} {5} {6} {7}".format( + _iptables_cmd(family), + wait, + table, + flag, + command, + chain, + position, + " ".join(rule), + ) - return ' '.join(rule) + return " ".join(rule) -def get_saved_rules(conf_file=None, family='ipv4'): - ''' +def get_saved_rules(conf_file=None, family="ipv4"): + """ Return a data structure of the rules in the conf file CLI Example: @@ -557,12 +595,12 @@ def get_saved_rules(conf_file=None, family='ipv4'): IPv6: salt '*' iptables.get_saved_rules family=ipv6 - ''' + """ return _parse_conf(conf_file=conf_file, family=family) -def get_rules(family='ipv4'): - ''' +def get_rules(family="ipv4"): + """ Return a data structure of the current, in-memory rules CLI Example: @@ -574,12 +612,12 @@ def get_rules(family='ipv4'): IPv6: salt '*' iptables.get_rules family=ipv6 - ''' + """ return _parse_conf(in_mem=True, family=family) -def get_saved_policy(table='filter', chain=None, conf_file=None, family='ipv4'): - ''' +def get_saved_policy(table="filter", chain=None, conf_file=None, family="ipv4"): + """ Return the current policy for the specified table/chain CLI Examples: @@ -595,19 +633,19 @@ def get_saved_policy(table='filter', chain=None, conf_file=None, family='ipv4'): salt '*' iptables.get_saved_policy filter INPUT \\ conf_file=/etc/iptables.saved family=ipv6 - ''' + """ if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" rules = _parse_conf(conf_file, family=family) try: - return rules[table][chain]['policy'] + return rules[table][chain]["policy"] except KeyError: return None -def get_policy(table='filter', chain=None, family='ipv4'): - ''' +def get_policy(table="filter", chain=None, family="ipv4"): + """ Return the current policy for the specified table/chain CLI Example: @@ -618,19 +656,19 @@ def get_policy(table='filter', chain=None, family='ipv4'): IPv6: salt '*' iptables.get_policy filter INPUT family=ipv6 - ''' + """ if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" rules = _parse_conf(in_mem=True, family=family) try: - return rules[table][chain]['policy'] + return rules[table][chain]["policy"] except KeyError: return None -def set_policy(table='filter', chain=None, policy=None, family='ipv4'): - ''' +def set_policy(table="filter", chain=None, policy=None, family="ipv4"): + """ Set the current policy for the specified table/chain CLI Example: @@ -641,21 +679,22 @@ def set_policy(table='filter', chain=None, policy=None, family='ipv4'): IPv6: salt '*' iptables.set_policy filter INPUT ACCEPT family=ipv6 - ''' + """ if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" if not policy: - return 'Error: Policy needs to be specified' + return "Error: Policy needs to be specified" - wait = '--wait' if _has_option('--wait', family) else '' - cmd = '{0} {1} -t {2} -P {3} {4}'.format( - _iptables_cmd(family), wait, table, chain, policy) - out = __salt__['cmd.run'](cmd) + wait = "--wait" if _has_option("--wait", family) else "" + cmd = "{0} {1} -t {2} -P {3} {4}".format( + _iptables_cmd(family), wait, table, chain, policy + ) + out = __salt__["cmd.run"](cmd) return out -def save(filename=None, family='ipv4'): - ''' +def save(filename=None, family="ipv4"): + """ Save the current in-memory rules to disk CLI Example: @@ -666,28 +705,28 @@ def save(filename=None, family='ipv4'): IPv6: salt '*' iptables.save /etc/sysconfig/iptables family=ipv6 - ''' + """ if _conf() and not filename: filename = _conf(family) - log.debug('Saving rules to %s', filename) + log.debug("Saving rules to %s", filename) parent_dir = os.path.dirname(filename) if not os.path.isdir(parent_dir): os.makedirs(parent_dir) - cmd = '{0}-save'.format(_iptables_cmd(family)) - ipt = __salt__['cmd.run'](cmd) + cmd = "{0}-save".format(_iptables_cmd(family)) + ipt = __salt__["cmd.run"](cmd) # regex out the output if configured with filters if len(_conf_save_filters()) > 0: ipt = _regex_iptables_save(ipt) - out = __salt__['file.write'](filename, ipt) + out = __salt__["file.write"](filename, ipt) return out -def check(table='filter', chain=None, rule=None, family='ipv4'): - ''' +def check(table="filter", chain=None, rule=None, family="ipv4"): + """ Check for the existence of a rule in the table and chain This function accepts a rule in a standard iptables command format, @@ -706,31 +745,33 @@ def check(table='filter', chain=None, rule=None, family='ipv4'): salt '*' iptables.check filter INPUT \\ rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' \\ family=ipv6 - ''' + """ if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" if not rule: - return 'Error: Rule needs to be specified' + return "Error: Rule needs to be specified" ipt_cmd = _iptables_cmd(family) - if _has_option('--check', family): - cmd = '{0} -t {1} -C {2} {3}'.format(ipt_cmd, table, chain, rule) - out = __salt__['cmd.run'](cmd, output_loglevel='quiet') + if _has_option("--check", family): + cmd = "{0} -t {1} -C {2} {3}".format(ipt_cmd, table, chain, rule) + out = __salt__["cmd.run"](cmd, output_loglevel="quiet") else: _chain_name = hex(uuid.getnode()) # Create temporary table - __salt__['cmd.run']('{0} -t {1} -N {2}'.format(ipt_cmd, table, _chain_name)) - __salt__['cmd.run']('{0} -t {1} -A {2} {3}'.format(ipt_cmd, table, _chain_name, rule)) + __salt__["cmd.run"]("{0} -t {1} -N {2}".format(ipt_cmd, table, _chain_name)) + __salt__["cmd.run"]( + "{0} -t {1} -A {2} {3}".format(ipt_cmd, table, _chain_name, rule) + ) - out = __salt__['cmd.run']('{0}-save'.format(ipt_cmd)) + out = __salt__["cmd.run"]("{0}-save".format(ipt_cmd)) # Clean up temporary table - __salt__['cmd.run']('{0} -t {1} -F {2}'.format(ipt_cmd, table, _chain_name)) - __salt__['cmd.run']('{0} -t {1} -X {2}'.format(ipt_cmd, table, _chain_name)) + __salt__["cmd.run"]("{0} -t {1} -F {2}".format(ipt_cmd, table, _chain_name)) + __salt__["cmd.run"]("{0} -t {1} -X {2}".format(ipt_cmd, table, _chain_name)) for i in out.splitlines(): - if i.startswith('-A {0}'.format(_chain_name)): + if i.startswith("-A {0}".format(_chain_name)): if i.replace(_chain_name, chain) in out.splitlines(): return True @@ -741,8 +782,8 @@ def check(table='filter', chain=None, rule=None, family='ipv4'): return out -def check_chain(table='filter', chain=None, family='ipv4'): - ''' +def check_chain(table="filter", chain=None, family="ipv4"): + """ .. versionadded:: 2014.1.0 Check for the existence of a chain in the table @@ -755,13 +796,13 @@ def check_chain(table='filter', chain=None, family='ipv4'): IPv6: salt '*' iptables.check_chain filter INPUT family=ipv6 - ''' + """ if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" - cmd = '{0}-save -t {1}'.format(_iptables_cmd(family), table) - out = __salt__['cmd.run'](cmd).find(':{0} '.format(chain)) + cmd = "{0}-save -t {1}".format(_iptables_cmd(family), table) + out = __salt__["cmd.run"](cmd).find(":{0} ".format(chain)) if out != -1: out = True @@ -771,8 +812,8 @@ def check_chain(table='filter', chain=None, family='ipv4'): return out -def new_chain(table='filter', chain=None, family='ipv4'): - ''' +def new_chain(table="filter", chain=None, family="ipv4"): + """ .. versionadded:: 2014.1.0 Create new custom chain to the specified table. @@ -785,23 +826,22 @@ def new_chain(table='filter', chain=None, family='ipv4'): IPv6: salt '*' iptables.new_chain filter CUSTOM_CHAIN family=ipv6 - ''' + """ if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" - wait = '--wait' if _has_option('--wait', family) else '' - cmd = '{0} {1} -t {2} -N {3}'.format( - _iptables_cmd(family), wait, table, chain) - out = __salt__['cmd.run'](cmd) + wait = "--wait" if _has_option("--wait", family) else "" + cmd = "{0} {1} -t {2} -N {3}".format(_iptables_cmd(family), wait, table, chain) + out = __salt__["cmd.run"](cmd) if not out: out = True return out -def delete_chain(table='filter', chain=None, family='ipv4'): - ''' +def delete_chain(table="filter", chain=None, family="ipv4"): + """ .. versionadded:: 2014.1.0 Delete custom chain to the specified table. @@ -814,23 +854,22 @@ def delete_chain(table='filter', chain=None, family='ipv4'): IPv6: salt '*' iptables.delete_chain filter CUSTOM_CHAIN family=ipv6 - ''' + """ if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" - wait = '--wait' if _has_option('--wait', family) else '' - cmd = '{0} {1} -t {2} -X {3}'.format( - _iptables_cmd(family), wait, table, chain) - out = __salt__['cmd.run'](cmd) + wait = "--wait" if _has_option("--wait", family) else "" + cmd = "{0} {1} -t {2} -X {3}".format(_iptables_cmd(family), wait, table, chain) + out = __salt__["cmd.run"](cmd) if not out: out = True return out -def append(table='filter', chain=None, rule=None, family='ipv4'): - ''' +def append(table="filter", chain=None, rule=None, family="ipv4"): + """ Append a rule to the specified table/chain. This function accepts a rule in a standard iptables command format, @@ -849,27 +888,28 @@ def append(table='filter', chain=None, rule=None, family='ipv4'): salt '*' iptables.append filter INPUT \\ rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' \\ family=ipv6 - ''' + """ if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" if not rule: - return 'Error: Rule needs to be specified' + return "Error: Rule needs to be specified" - wait = '--wait' if _has_option('--wait', family) else '' + wait = "--wait" if _has_option("--wait", family) else "" returnCheck = check(table, chain, rule, family) if isinstance(returnCheck, bool) and returnCheck: return False - cmd = '{0} {1} -t {2} -A {3} {4}'.format( - _iptables_cmd(family), wait, table, chain, rule) - out = __salt__['cmd.run'](cmd) + cmd = "{0} {1} -t {2} -A {3} {4}".format( + _iptables_cmd(family), wait, table, chain, rule + ) + out = __salt__["cmd.run"](cmd) if len(out) == 0: return True else: return False -def insert(table='filter', chain=None, position=None, rule=None, family='ipv4'): - ''' +def insert(table="filter", chain=None, position=None, rule=None, family="ipv4"): + """ Insert a rule into the specified table/chain, at the specified position. This function accepts a rule in a standard iptables command format, @@ -893,33 +933,34 @@ def insert(table='filter', chain=None, position=None, rule=None, family='ipv4'): salt '*' iptables.insert filter INPUT position=3 \\ rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' \\ family=ipv6 - ''' + """ if not chain: - return 'Error: Chain needs to be specified' + return "Error: Chain needs to be specified" if not position: - return 'Error: Position needs to be specified or use append (-A)' + return "Error: Position needs to be specified or use append (-A)" if not rule: - return 'Error: Rule needs to be specified' + return "Error: Rule needs to be specified" if position < 0: rules = get_rules(family=family) - size = len(rules[table][chain]['rules']) + size = len(rules[table][chain]["rules"]) position = (size + position) + 1 if position is 0: position = 1 - wait = '--wait' if _has_option('--wait', family) else '' + wait = "--wait" if _has_option("--wait", family) else "" returnCheck = check(table, chain, rule, family) if isinstance(returnCheck, bool) and returnCheck: return False - cmd = '{0} {1} -t {2} -I {3} {4} {5}'.format( - _iptables_cmd(family), wait, table, chain, position, rule) - out = __salt__['cmd.run'](cmd) + cmd = "{0} {1} -t {2} -I {3} {4} {5}".format( + _iptables_cmd(family), wait, table, chain, position, rule + ) + out = __salt__["cmd.run"](cmd) return out -def delete(table, chain=None, position=None, rule=None, family='ipv4'): - ''' +def delete(table, chain=None, position=None, rule=None, family="ipv4"): + """ Delete a rule from the specified table/chain, specifying either the rule in its entirety, or the rule's position in the chain. @@ -941,23 +982,24 @@ def delete(table, chain=None, position=None, rule=None, family='ipv4'): salt '*' iptables.delete filter INPUT \\ rule='-m state --state RELATED,ESTABLISHED -j ACCEPT' \\ family=ipv6 - ''' + """ if position and rule: - return 'Error: Only specify a position or a rule, not both' + return "Error: Only specify a position or a rule, not both" if position: rule = position - wait = '--wait' if _has_option('--wait', family) else '' - cmd = '{0} {1} -t {2} -D {3} {4}'.format( - _iptables_cmd(family), wait, table, chain, rule) - out = __salt__['cmd.run'](cmd) + wait = "--wait" if _has_option("--wait", family) else "" + cmd = "{0} {1} -t {2} -D {3} {4}".format( + _iptables_cmd(family), wait, table, chain, rule + ) + out = __salt__["cmd.run"](cmd) return out -def flush(table='filter', chain='', family='ipv4'): - ''' +def flush(table="filter", chain="", family="ipv4"): + """ Flush the chain in the specified table, flush all chains in the specified table if not specified chain. @@ -969,632 +1011,613 @@ def flush(table='filter', chain='', family='ipv4'): IPv6: salt '*' iptables.flush filter INPUT family=ipv6 - ''' + """ - wait = '--wait' if _has_option('--wait', family) else '' - cmd = '{0} {1} -t {2} -F {3}'.format(_iptables_cmd(family), wait, table, chain) - out = __salt__['cmd.run'](cmd) + wait = "--wait" if _has_option("--wait", family) else "" + cmd = "{0} {1} -t {2} -F {3}".format(_iptables_cmd(family), wait, table, chain) + out = __salt__["cmd.run"](cmd) return out -def _parse_conf(conf_file=None, in_mem=False, family='ipv4'): - ''' +def _parse_conf(conf_file=None, in_mem=False, family="ipv4"): + """ If a file is not passed in, and the correct one for this OS is not detected, return False - ''' + """ if _conf() and not conf_file and not in_mem: conf_file = _conf(family) - rules = '' + rules = "" if conf_file: - with salt.utils.files.fopen(conf_file, 'r') as ifile: + with salt.utils.files.fopen(conf_file, "r") as ifile: rules = ifile.read() elif in_mem: - cmd = '{0}-save' . format(_iptables_cmd(family)) - rules = __salt__['cmd.run'](cmd) + cmd = "{0}-save".format(_iptables_cmd(family)) + rules = __salt__["cmd.run"](cmd) else: - raise SaltException('A file was not found to parse') + raise SaltException("A file was not found to parse") ret = {} - table = '' + table = "" parser = _parser() for line in rules.splitlines(): line = salt.utils.stringutils.to_unicode(line) - if line.startswith('*'): - table = line.replace('*', '') + if line.startswith("*"): + table = line.replace("*", "") ret[table] = {} - elif line.startswith(':'): + elif line.startswith(":"): comps = line.split() - chain = comps[0].replace(':', '') + chain = comps[0].replace(":", "") ret[table][chain] = {} - ret[table][chain]['policy'] = comps[1] - counters = comps[2].replace('[', '').replace(']', '') - (pcount, bcount) = counters.split(':') - ret[table][chain]['packet count'] = pcount - ret[table][chain]['byte count'] = bcount - ret[table][chain]['rules'] = [] - ret[table][chain]['rules_comment'] = {} - elif line.startswith('-A'): + ret[table][chain]["policy"] = comps[1] + counters = comps[2].replace("[", "").replace("]", "") + (pcount, bcount) = counters.split(":") + ret[table][chain]["packet count"] = pcount + ret[table][chain]["byte count"] = bcount + ret[table][chain]["rules"] = [] + ret[table][chain]["rules_comment"] = {} + elif line.startswith("-A"): args = salt.utils.args.shlex_split(line) index = 0 while index + 1 < len(args): - swap = args[index] == '!' and args[index + 1].startswith('-') + swap = args[index] == "!" and args[index + 1].startswith("-") if swap: args[index], args[index + 1] = args[index + 1], args[index] - if args[index].startswith('-'): + if args[index].startswith("-"): index += 1 - if args[index].startswith('-') or (args[index] == '!' and - not swap): - args.insert(index, '') + if args[index].startswith("-") or (args[index] == "!" and not swap): + args.insert(index, "") else: - while (index + 1 < len(args) and - args[index + 1] != '!' and - not args[index + 1].startswith('-')): - args[index] += ' {0}'.format(args.pop(index + 1)) + while ( + index + 1 < len(args) + and args[index + 1] != "!" + and not args[index + 1].startswith("-") + ): + args[index] += " {0}".format(args.pop(index + 1)) index += 1 - if args[-1].startswith('-'): - args.append('') + if args[-1].startswith("-"): + args.append("") parsed_args = [] opts, _ = parser.parse_known_args(args) parsed_args = vars(opts) ret_args = {} - chain = parsed_args['append'] + chain = parsed_args["append"] for arg in parsed_args: - if parsed_args[arg] and arg is not 'append': + if parsed_args[arg] and arg is not "append": ret_args[arg] = parsed_args[arg] - if parsed_args['comment'] is not None: - comment = parsed_args['comment'][0].strip('"') - ret[table][chain[0]]['rules_comment'][comment] = ret_args - ret[table][chain[0]]['rules'].append(ret_args) + if parsed_args["comment"] is not None: + comment = parsed_args["comment"][0].strip('"') + ret[table][chain[0]]["rules_comment"][comment] = ret_args + ret[table][chain[0]]["rules"].append(ret_args) return ret def _parser(): - ''' + """ This function attempts to list all the options documented in the iptables(8) and iptables-extensions(8) man pages. They will not all be used by all parts of the module; use them intelligently and appropriately. - ''' + """ add_arg = None - if sys.version.startswith('2.6'): + if sys.version.startswith("2.6"): import optparse + parser = optparse.OptionParser() add_arg = parser.add_option else: import argparse # pylint: disable=minimum-python-version + parser = argparse.ArgumentParser() add_arg = parser.add_argument # COMMANDS - add_arg('-A', '--append', dest='append', action='append') - add_arg('-D', '--delete', dest='delete', action='append') - add_arg('-I', '--insert', dest='insert', action='append') - add_arg('-R', '--replace', dest='replace', action='append') - add_arg('-L', '--list', dest='list', action='append') - add_arg('-F', '--flush', dest='flush', action='append') - add_arg('-Z', '--zero', dest='zero', action='append') - add_arg('-N', '--new-chain', dest='new-chain', action='append') - add_arg('-X', '--delete-chain', dest='delete-chain', action='append') - add_arg('-P', '--policy', dest='policy', action='append') - add_arg('-E', '--rename-chain', dest='rename-chain', action='append') + add_arg("-A", "--append", dest="append", action="append") + add_arg("-D", "--delete", dest="delete", action="append") + add_arg("-I", "--insert", dest="insert", action="append") + add_arg("-R", "--replace", dest="replace", action="append") + add_arg("-L", "--list", dest="list", action="append") + add_arg("-F", "--flush", dest="flush", action="append") + add_arg("-Z", "--zero", dest="zero", action="append") + add_arg("-N", "--new-chain", dest="new-chain", action="append") + add_arg("-X", "--delete-chain", dest="delete-chain", action="append") + add_arg("-P", "--policy", dest="policy", action="append") + add_arg("-E", "--rename-chain", dest="rename-chain", action="append") # PARAMETERS - add_arg('-p', '--protocol', dest='protocol', action='append') - add_arg('-s', '--source', dest='source', action='append') - add_arg('-d', '--destination', dest='destination', action='append') - add_arg('-j', '--jump', dest='jump', action='append') - add_arg('-g', '--goto', dest='goto', action='append') - add_arg('-i', '--in-interface', dest='in-interface', action='append') - add_arg('-o', '--out-interface', dest='out-interface', action='append') - add_arg('-f', '--fragment', dest='fragment', action='append') - add_arg('-c', '--set-counters', dest='set-counters', action='append') + add_arg("-p", "--protocol", dest="protocol", action="append") + add_arg("-s", "--source", dest="source", action="append") + add_arg("-d", "--destination", dest="destination", action="append") + add_arg("-j", "--jump", dest="jump", action="append") + add_arg("-g", "--goto", dest="goto", action="append") + add_arg("-i", "--in-interface", dest="in-interface", action="append") + add_arg("-o", "--out-interface", dest="out-interface", action="append") + add_arg("-f", "--fragment", dest="fragment", action="append") + add_arg("-c", "--set-counters", dest="set-counters", action="append") # MATCH EXTENSIONS - add_arg('-m', '--match', dest='match', action='append') + add_arg("-m", "--match", dest="match", action="append") ## addrtype - add_arg('--src-type', dest='src-type', action='append') - add_arg('--dst-type', dest='dst-type', action='append') - add_arg('--limit-iface-in', dest='limit-iface-in', action='append') - add_arg('--limit-iface-out', dest='limit-iface-out', action='append') + add_arg("--src-type", dest="src-type", action="append") + add_arg("--dst-type", dest="dst-type", action="append") + add_arg("--limit-iface-in", dest="limit-iface-in", action="append") + add_arg("--limit-iface-out", dest="limit-iface-out", action="append") ## ah - add_arg('--ahspi', dest='ahspi', action='append') - add_arg('--ahlen', dest='ahlen', action='append') - add_arg('--ahres', dest='ahres', action='append') + add_arg("--ahspi", dest="ahspi", action="append") + add_arg("--ahlen", dest="ahlen", action="append") + add_arg("--ahres", dest="ahres", action="append") ## bpf - add_arg('--bytecode', dest='bytecode', action='append') + add_arg("--bytecode", dest="bytecode", action="append") ## cgroup - add_arg('--cgroup', dest='cgroup', action='append') + add_arg("--cgroup", dest="cgroup", action="append") ## cluster - add_arg('--cluster-total-nodes', - dest='cluster-total-nodes', - action='append') - add_arg('--cluster-local-node', dest='cluster-local-node', action='append') - add_arg('--cluster-local-nodemask', - dest='cluster-local-nodemask', - action='append') - add_arg('--cluster-hash-seed', dest='cluster-hash-seed', action='append') - add_arg('--h-length', dest='h-length', action='append') - add_arg('--mangle-mac-s', dest='mangle-mac-s', action='append') - add_arg('--mangle-mac-d', dest='mangle-mac-d', action='append') + add_arg("--cluster-total-nodes", dest="cluster-total-nodes", action="append") + add_arg("--cluster-local-node", dest="cluster-local-node", action="append") + add_arg("--cluster-local-nodemask", dest="cluster-local-nodemask", action="append") + add_arg("--cluster-hash-seed", dest="cluster-hash-seed", action="append") + add_arg("--h-length", dest="h-length", action="append") + add_arg("--mangle-mac-s", dest="mangle-mac-s", action="append") + add_arg("--mangle-mac-d", dest="mangle-mac-d", action="append") ## comment - add_arg('--comment', dest='comment', action='append') + add_arg("--comment", dest="comment", action="append") ## connbytes - add_arg('--connbytes', dest='connbytes', action='append') - add_arg('--connbytes-dir', dest='connbytes-dir', action='append') - add_arg('--connbytes-mode', dest='connbytes-mode', action='append') + add_arg("--connbytes", dest="connbytes", action="append") + add_arg("--connbytes-dir", dest="connbytes-dir", action="append") + add_arg("--connbytes-mode", dest="connbytes-mode", action="append") ## connlabel - add_arg('--label', dest='label', action='append') + add_arg("--label", dest="label", action="append") ## connlimit - add_arg('--connlimit-upto', dest='connlimit-upto', action='append') - add_arg('--connlimit-above', dest='connlimit-above', action='append') - add_arg('--connlimit-mask', dest='connlimit-mask', action='append') - add_arg('--connlimit-saddr', dest='connlimit-saddr', action='append') - add_arg('--connlimit-daddr', dest='connlimit-daddr', action='append') + add_arg("--connlimit-upto", dest="connlimit-upto", action="append") + add_arg("--connlimit-above", dest="connlimit-above", action="append") + add_arg("--connlimit-mask", dest="connlimit-mask", action="append") + add_arg("--connlimit-saddr", dest="connlimit-saddr", action="append") + add_arg("--connlimit-daddr", dest="connlimit-daddr", action="append") ## connmark - add_arg('--mark', dest='mark', action='append') + add_arg("--mark", dest="mark", action="append") ## conntrack - add_arg('--ctstate', dest='ctstate', action='append') - add_arg('--ctproto', dest='ctproto', action='append') - add_arg('--ctorigsrc', dest='ctorigsrc', action='append') - add_arg('--ctorigdst', dest='ctorigdst', action='append') - add_arg('--ctreplsrc', dest='ctreplsrc', action='append') - add_arg('--ctrepldst', dest='ctrepldst', action='append') - add_arg('--ctorigsrcport', dest='ctorigsrcport', action='append') - add_arg('--ctorigdstport', dest='ctorigdstport', action='append') - add_arg('--ctreplsrcport', dest='ctreplsrcport', action='append') - add_arg('--ctrepldstport', dest='ctrepldstport', action='append') - add_arg('--ctstatus', dest='ctstatus', action='append') - add_arg('--ctexpire', dest='ctexpire', action='append') - add_arg('--ctdir', dest='ctdir', action='append') + add_arg("--ctstate", dest="ctstate", action="append") + add_arg("--ctproto", dest="ctproto", action="append") + add_arg("--ctorigsrc", dest="ctorigsrc", action="append") + add_arg("--ctorigdst", dest="ctorigdst", action="append") + add_arg("--ctreplsrc", dest="ctreplsrc", action="append") + add_arg("--ctrepldst", dest="ctrepldst", action="append") + add_arg("--ctorigsrcport", dest="ctorigsrcport", action="append") + add_arg("--ctorigdstport", dest="ctorigdstport", action="append") + add_arg("--ctreplsrcport", dest="ctreplsrcport", action="append") + add_arg("--ctrepldstport", dest="ctrepldstport", action="append") + add_arg("--ctstatus", dest="ctstatus", action="append") + add_arg("--ctexpire", dest="ctexpire", action="append") + add_arg("--ctdir", dest="ctdir", action="append") ## cpu - add_arg('--cpu', dest='cpu', action='append') + add_arg("--cpu", dest="cpu", action="append") ## dccp - add_arg('--sport', '--source-port', dest='source_port', action='append') - add_arg('--dport', - '--destination-port', - dest='destination_port', - action='append') - add_arg('--dccp-types', dest='dccp-types', action='append') - add_arg('--dccp-option', dest='dccp-option', action='append') + add_arg("--sport", "--source-port", dest="source_port", action="append") + add_arg("--dport", "--destination-port", dest="destination_port", action="append") + add_arg("--dccp-types", dest="dccp-types", action="append") + add_arg("--dccp-option", dest="dccp-option", action="append") ## devgroup - add_arg('--src-group', dest='src-group', action='append') - add_arg('--dst-group', dest='dst-group', action='append') + add_arg("--src-group", dest="src-group", action="append") + add_arg("--dst-group", dest="dst-group", action="append") ## dscp - add_arg('--dscp', dest='dscp', action='append') - add_arg('--dscp-class', dest='dscp-class', action='append') + add_arg("--dscp", dest="dscp", action="append") + add_arg("--dscp-class", dest="dscp-class", action="append") ## dst - add_arg('--dst-len', dest='dst-len', action='append') - add_arg('--dst-opts', dest='dst-opts', action='append') + add_arg("--dst-len", dest="dst-len", action="append") + add_arg("--dst-opts", dest="dst-opts", action="append") ## ecn - add_arg('--ecn-tcp-cwr', dest='ecn-tcp-cwr', action='append') - add_arg('--ecn-tcp-ece', dest='ecn-tcp-ece', action='append') - add_arg('--ecn-ip-ect', dest='ecn-ip-ect', action='append') + add_arg("--ecn-tcp-cwr", dest="ecn-tcp-cwr", action="append") + add_arg("--ecn-tcp-ece", dest="ecn-tcp-ece", action="append") + add_arg("--ecn-ip-ect", dest="ecn-ip-ect", action="append") ## esp - add_arg('--espspi', dest='espspi', action='append') + add_arg("--espspi", dest="espspi", action="append") ## frag - add_arg('--fragid', dest='fragid', action='append') - add_arg('--fraglen', dest='fraglen', action='append') - add_arg('--fragres', dest='fragres', action='append') - add_arg('--fragfirst', dest='fragfirst', action='append') - add_arg('--fragmore', dest='fragmore', action='append') - add_arg('--fraglast', dest='fraglast', action='append') + add_arg("--fragid", dest="fragid", action="append") + add_arg("--fraglen", dest="fraglen", action="append") + add_arg("--fragres", dest="fragres", action="append") + add_arg("--fragfirst", dest="fragfirst", action="append") + add_arg("--fragmore", dest="fragmore", action="append") + add_arg("--fraglast", dest="fraglast", action="append") ## hashlimit - add_arg('--hashlimit-upto', dest='hashlimit-upto', action='append') - add_arg('--hashlimit-above', dest='hashlimit-above', action='append') - add_arg('--hashlimit-burst', dest='hashlimit-burst', action='append') - add_arg('--hashlimit-mode', dest='hashlimit-mode', action='append') - add_arg('--hashlimit-srcmask', dest='hashlimit-srcmask', action='append') - add_arg('--hashlimit-dstmask', dest='hashlimit-dstmask', action='append') - add_arg('--hashlimit-name', dest='hashlimit-name', action='append') - add_arg('--hashlimit-htable-size', - dest='hashlimit-htable-size', - action='append') - add_arg('--hashlimit-htable-max', - dest='hashlimit-htable-max', - action='append') - add_arg('--hashlimit-htable-expire', - dest='hashlimit-htable-expire', - action='append') - add_arg('--hashlimit-htable-gcinterval', - dest='hashlimit-htable-gcinterval', - action='append') + add_arg("--hashlimit-upto", dest="hashlimit-upto", action="append") + add_arg("--hashlimit-above", dest="hashlimit-above", action="append") + add_arg("--hashlimit-burst", dest="hashlimit-burst", action="append") + add_arg("--hashlimit-mode", dest="hashlimit-mode", action="append") + add_arg("--hashlimit-srcmask", dest="hashlimit-srcmask", action="append") + add_arg("--hashlimit-dstmask", dest="hashlimit-dstmask", action="append") + add_arg("--hashlimit-name", dest="hashlimit-name", action="append") + add_arg("--hashlimit-htable-size", dest="hashlimit-htable-size", action="append") + add_arg("--hashlimit-htable-max", dest="hashlimit-htable-max", action="append") + add_arg( + "--hashlimit-htable-expire", dest="hashlimit-htable-expire", action="append" + ) + add_arg( + "--hashlimit-htable-gcinterval", + dest="hashlimit-htable-gcinterval", + action="append", + ) ## hbh - add_arg('--hbh-len', dest='hbh-len', action='append') - add_arg('--hbh-opts', dest='hbh-opts', action='append') + add_arg("--hbh-len", dest="hbh-len", action="append") + add_arg("--hbh-opts", dest="hbh-opts", action="append") ## helper - add_arg('--helper', dest='helper', action='append') + add_arg("--helper", dest="helper", action="append") ## hl - add_arg('--hl-eq', dest='hl-eq', action='append') - add_arg('--hl-lt', dest='hl-lt', action='append') - add_arg('--hl-gt', dest='hl-gt', action='append') + add_arg("--hl-eq", dest="hl-eq", action="append") + add_arg("--hl-lt", dest="hl-lt", action="append") + add_arg("--hl-gt", dest="hl-gt", action="append") ## icmp - add_arg('--icmp-type', dest='icmp-type', action='append') + add_arg("--icmp-type", dest="icmp-type", action="append") ## icmp6 - add_arg('--icmpv6-type', dest='icmpv6-type', action='append') + add_arg("--icmpv6-type", dest="icmpv6-type", action="append") ## iprange - add_arg('--src-range', dest='src-range', action='append') - add_arg('--dst-range', dest='dst-range', action='append') + add_arg("--src-range", dest="src-range", action="append") + add_arg("--dst-range", dest="dst-range", action="append") ## ipv6header - add_arg('--soft', dest='soft', action='append') - add_arg('--header', dest='header', action='append') + add_arg("--soft", dest="soft", action="append") + add_arg("--header", dest="header", action="append") ## ipvs - add_arg('--ipvs', dest='ipvs', action='append') - add_arg('--vproto', dest='vproto', action='append') - add_arg('--vaddr', dest='vaddr', action='append') - add_arg('--vport', dest='vport', action='append') - add_arg('--vdir', dest='vdir', action='append') - add_arg('--vmethod', dest='vmethod', action='append') - add_arg('--vportctl', dest='vportctl', action='append') + add_arg("--ipvs", dest="ipvs", action="append") + add_arg("--vproto", dest="vproto", action="append") + add_arg("--vaddr", dest="vaddr", action="append") + add_arg("--vport", dest="vport", action="append") + add_arg("--vdir", dest="vdir", action="append") + add_arg("--vmethod", dest="vmethod", action="append") + add_arg("--vportctl", dest="vportctl", action="append") ## length - add_arg('--length', dest='length', action='append') + add_arg("--length", dest="length", action="append") ## limit - add_arg('--limit', dest='limit', action='append') - add_arg('--limit-burst', dest='limit-burst', action='append') + add_arg("--limit", dest="limit", action="append") + add_arg("--limit-burst", dest="limit-burst", action="append") ## mac - add_arg('--mac-source', dest='mac-source', action='append') + add_arg("--mac-source", dest="mac-source", action="append") ## mh - add_arg('--mh-type', dest='mh-type', action='append') + add_arg("--mh-type", dest="mh-type", action="append") ## multiport - add_arg('--sports', '--source-ports', dest='source-ports', action='append') - add_arg('--dports', - '--destination-ports', - dest='destination-ports', - action='append') - add_arg('--ports', dest='ports', action='append') + add_arg("--sports", "--source-ports", dest="source-ports", action="append") + add_arg( + "--dports", "--destination-ports", dest="destination-ports", action="append" + ) + add_arg("--ports", dest="ports", action="append") ## nfacct - add_arg('--nfacct-name', dest='nfacct-name', action='append') + add_arg("--nfacct-name", dest="nfacct-name", action="append") ## osf - add_arg('--genre', dest='genre', action='append') - add_arg('--ttl', dest='ttl', action='append') - add_arg('--log', dest='log', action='append') + add_arg("--genre", dest="genre", action="append") + add_arg("--ttl", dest="ttl", action="append") + add_arg("--log", dest="log", action="append") ## owner - add_arg('--uid-owner', dest='uid-owner', action='append') - add_arg('--gid-owner', dest='gid-owner', action='append') - add_arg('--socket-exists', dest='socket-exists', action='append') + add_arg("--uid-owner", dest="uid-owner", action="append") + add_arg("--gid-owner", dest="gid-owner", action="append") + add_arg("--socket-exists", dest="socket-exists", action="append") ## physdev - add_arg('--physdev-in', dest='physdev-in', action='append') - add_arg('--physdev-out', dest='physdev-out', action='append') - add_arg('--physdev-is-in', dest='physdev-is-in', action='append') - add_arg('--physdev-is-out', dest='physdev-is-out', action='append') - add_arg('--physdev-is-bridged', dest='physdev-is-bridged', action='append') + add_arg("--physdev-in", dest="physdev-in", action="append") + add_arg("--physdev-out", dest="physdev-out", action="append") + add_arg("--physdev-is-in", dest="physdev-is-in", action="append") + add_arg("--physdev-is-out", dest="physdev-is-out", action="append") + add_arg("--physdev-is-bridged", dest="physdev-is-bridged", action="append") ## pkttype - add_arg('--pkt-type', dest='pkt-type', action='append') + add_arg("--pkt-type", dest="pkt-type", action="append") ## policy - add_arg('--dir', dest='dir', action='append') - add_arg('--pol', dest='pol', action='append') - add_arg('--strict', dest='strict', action='append') - add_arg('--reqid', dest='reqid', action='append') - add_arg('--spi', dest='spi', action='append') - add_arg('--proto', dest='proto', action='append') - add_arg('--mode', dest='mode', action='append') - add_arg('--tunnel-src', dest='tunnel-src', action='append') - add_arg('--tunnel-dst', dest='tunnel-dst', action='append') - add_arg('--next', dest='next', action='append') + add_arg("--dir", dest="dir", action="append") + add_arg("--pol", dest="pol", action="append") + add_arg("--strict", dest="strict", action="append") + add_arg("--reqid", dest="reqid", action="append") + add_arg("--spi", dest="spi", action="append") + add_arg("--proto", dest="proto", action="append") + add_arg("--mode", dest="mode", action="append") + add_arg("--tunnel-src", dest="tunnel-src", action="append") + add_arg("--tunnel-dst", dest="tunnel-dst", action="append") + add_arg("--next", dest="next", action="append") ## quota - add_arg('--quota', dest='quota', action='append') + add_arg("--quota", dest="quota", action="append") ## rateest - add_arg('--rateest', dest='rateest', action='append') - add_arg('--rateest1', dest='rateest1', action='append') - add_arg('--rateest2', dest='rateest2', action='append') - add_arg('--rateest-delta', dest='rateest-delta', action='append') - add_arg('--rateest-bps', dest='rateest-bps', action='append') - add_arg('--rateest-bps1', dest='rateest-bps1', action='append') - add_arg('--rateest-bps2', dest='rateest-bps2', action='append') - add_arg('--rateest-pps', dest='rateest-pps', action='append') - add_arg('--rateest-pps1', dest='rateest-pps1', action='append') - add_arg('--rateest-pps2', dest='rateest-pps2', action='append') - add_arg('--rateest-lt', dest='rateest-lt', action='append') - add_arg('--rateest-gt', dest='rateest-gt', action='append') - add_arg('--rateest-eq', dest='rateest-eq', action='append') - add_arg('--rateest-name', dest='rateest-name', action='append') - add_arg('--rateest-interval', dest='rateest-interval', action='append') - add_arg('--rateest-ewma', dest='rateest-ewma', action='append') + add_arg("--rateest", dest="rateest", action="append") + add_arg("--rateest1", dest="rateest1", action="append") + add_arg("--rateest2", dest="rateest2", action="append") + add_arg("--rateest-delta", dest="rateest-delta", action="append") + add_arg("--rateest-bps", dest="rateest-bps", action="append") + add_arg("--rateest-bps1", dest="rateest-bps1", action="append") + add_arg("--rateest-bps2", dest="rateest-bps2", action="append") + add_arg("--rateest-pps", dest="rateest-pps", action="append") + add_arg("--rateest-pps1", dest="rateest-pps1", action="append") + add_arg("--rateest-pps2", dest="rateest-pps2", action="append") + add_arg("--rateest-lt", dest="rateest-lt", action="append") + add_arg("--rateest-gt", dest="rateest-gt", action="append") + add_arg("--rateest-eq", dest="rateest-eq", action="append") + add_arg("--rateest-name", dest="rateest-name", action="append") + add_arg("--rateest-interval", dest="rateest-interval", action="append") + add_arg("--rateest-ewma", dest="rateest-ewma", action="append") ## realm - add_arg('--realm', dest='realm', action='append') + add_arg("--realm", dest="realm", action="append") ## recent - add_arg('--name', dest='name', action='append') - add_arg('--set', dest='set', action='append') - add_arg('--rsource', dest='rsource', action='append') - add_arg('--rdest', dest='rdest', action='append') - add_arg('--mask', dest='mask', action='append') - add_arg('--rcheck', dest='rcheck', action='append') - add_arg('--update', dest='update', action='append') - add_arg('--remove', dest='remove', action='append') - add_arg('--seconds', dest='seconds', action='append') - add_arg('--reap', dest='reap', action='append') - add_arg('--hitcount', dest='hitcount', action='append') - add_arg('--rttl', dest='rttl', action='append') + add_arg("--name", dest="name", action="append") + add_arg("--set", dest="set", action="append") + add_arg("--rsource", dest="rsource", action="append") + add_arg("--rdest", dest="rdest", action="append") + add_arg("--mask", dest="mask", action="append") + add_arg("--rcheck", dest="rcheck", action="append") + add_arg("--update", dest="update", action="append") + add_arg("--remove", dest="remove", action="append") + add_arg("--seconds", dest="seconds", action="append") + add_arg("--reap", dest="reap", action="append") + add_arg("--hitcount", dest="hitcount", action="append") + add_arg("--rttl", dest="rttl", action="append") ## rpfilter - add_arg('--loose', dest='loose', action='append') - add_arg('--validmark', dest='validmark', action='append') - add_arg('--accept-local', dest='accept-local', action='append') - add_arg('--invert', dest='invert', action='append') + add_arg("--loose", dest="loose", action="append") + add_arg("--validmark", dest="validmark", action="append") + add_arg("--accept-local", dest="accept-local", action="append") + add_arg("--invert", dest="invert", action="append") ## rt - add_arg('--rt-type', dest='rt-type', action='append') - add_arg('--rt-segsleft', dest='rt-segsleft', action='append') - add_arg('--rt-len', dest='rt-len', action='append') - add_arg('--rt-0-res', dest='rt-0-res', action='append') - add_arg('--rt-0-addrs', dest='rt-0-addrs', action='append') - add_arg('--rt-0-not-strict', dest='rt-0-not-strict', action='append') + add_arg("--rt-type", dest="rt-type", action="append") + add_arg("--rt-segsleft", dest="rt-segsleft", action="append") + add_arg("--rt-len", dest="rt-len", action="append") + add_arg("--rt-0-res", dest="rt-0-res", action="append") + add_arg("--rt-0-addrs", dest="rt-0-addrs", action="append") + add_arg("--rt-0-not-strict", dest="rt-0-not-strict", action="append") ## sctp - add_arg('--chunk-types', dest='chunk-types', action='append') + add_arg("--chunk-types", dest="chunk-types", action="append") ## set - add_arg('--match-set', dest='match-set', action='append') - add_arg('--return-nomatch', dest='return-nomatch', action='append') - add_arg('--update-counters', dest='update-counters', action='append') - add_arg('--update-subcounters', dest='update-subcounters', action='append') - add_arg('--packets-eq', dest='packets-eq', action='append') - add_arg('--packets-lt', dest='packets-lt', action='append') - add_arg('--packets-gt', dest='packets-gt', action='append') - add_arg('--bytes-eq', dest='bytes-eq', action='append') - add_arg('--bytes-lt', dest='bytes-lt', action='append') - add_arg('--bytes-gt', dest='bytes-gt', action='append') + add_arg("--match-set", dest="match-set", action="append") + add_arg("--return-nomatch", dest="return-nomatch", action="append") + add_arg("--update-counters", dest="update-counters", action="append") + add_arg("--update-subcounters", dest="update-subcounters", action="append") + add_arg("--packets-eq", dest="packets-eq", action="append") + add_arg("--packets-lt", dest="packets-lt", action="append") + add_arg("--packets-gt", dest="packets-gt", action="append") + add_arg("--bytes-eq", dest="bytes-eq", action="append") + add_arg("--bytes-lt", dest="bytes-lt", action="append") + add_arg("--bytes-gt", dest="bytes-gt", action="append") ## socket - add_arg('--transparent', dest='transparent', action='append') - add_arg('--nowildcard', dest='nowildcard', action='append') + add_arg("--transparent", dest="transparent", action="append") + add_arg("--nowildcard", dest="nowildcard", action="append") ## state - add_arg('--state', dest='state', action='append') + add_arg("--state", dest="state", action="append") ## statistic - add_arg('--probability', dest='probability', action='append') - add_arg('--every', dest='every', action='append') - add_arg('--packet', dest='packet', action='append') + add_arg("--probability", dest="probability", action="append") + add_arg("--every", dest="every", action="append") + add_arg("--packet", dest="packet", action="append") ## string - add_arg('--algo', dest='algo', action='append') - add_arg('--from', dest='from', action='append') - add_arg('--to', dest='to', action='append') - add_arg('--string', dest='string', action='append') - add_arg('--hex-string', dest='hex-string', action='append') + add_arg("--algo", dest="algo", action="append") + add_arg("--from", dest="from", action="append") + add_arg("--to", dest="to", action="append") + add_arg("--string", dest="string", action="append") + add_arg("--hex-string", dest="hex-string", action="append") ## tcp - add_arg('--tcp-flags', dest='tcp-flags', action='append') - add_arg('--syn', dest='syn', action='append') - add_arg('--tcp-option', dest='tcp-option', action='append') + add_arg("--tcp-flags", dest="tcp-flags", action="append") + add_arg("--syn", dest="syn", action="append") + add_arg("--tcp-option", dest="tcp-option", action="append") ## tcpmss - add_arg('--mss', dest='mss', action='append') + add_arg("--mss", dest="mss", action="append") ## time - add_arg('--datestart', dest='datestart', action='append') - add_arg('--datestop', dest='datestop', action='append') - add_arg('--timestart', dest='timestart', action='append') - add_arg('--timestop', dest='timestop', action='append') - add_arg('--monthdays', dest='monthdays', action='append') - add_arg('--weekdays', dest='weekdays', action='append') - add_arg('--contiguous', dest='contiguous', action='append') - add_arg('--kerneltz', dest='kerneltz', action='append') - add_arg('--utc', dest='utc', action='append') - add_arg('--localtz', dest='localtz', action='append') + add_arg("--datestart", dest="datestart", action="append") + add_arg("--datestop", dest="datestop", action="append") + add_arg("--timestart", dest="timestart", action="append") + add_arg("--timestop", dest="timestop", action="append") + add_arg("--monthdays", dest="monthdays", action="append") + add_arg("--weekdays", dest="weekdays", action="append") + add_arg("--contiguous", dest="contiguous", action="append") + add_arg("--kerneltz", dest="kerneltz", action="append") + add_arg("--utc", dest="utc", action="append") + add_arg("--localtz", dest="localtz", action="append") ## tos - add_arg('--tos', dest='tos', action='append') + add_arg("--tos", dest="tos", action="append") ## ttl - add_arg('--ttl-eq', dest='ttl-eq', action='append') - add_arg('--ttl-gt', dest='ttl-gt', action='append') - add_arg('--ttl-lt', dest='ttl-lt', action='append') + add_arg("--ttl-eq", dest="ttl-eq", action="append") + add_arg("--ttl-gt", dest="ttl-gt", action="append") + add_arg("--ttl-lt", dest="ttl-lt", action="append") ## u32 - add_arg('--u32', dest='u32', action='append') + add_arg("--u32", dest="u32", action="append") # Xtables-addons matches ## condition - add_arg('--condition', dest='condition', action='append') + add_arg("--condition", dest="condition", action="append") ## dhcpmac - add_arg('--mac', dest='mac', action='append') + add_arg("--mac", dest="mac", action="append") ## fuzzy - add_arg('--lower-limit', dest='lower-limit', action='append') - add_arg('--upper-limit', dest='upper-limit', action='append') + add_arg("--lower-limit", dest="lower-limit", action="append") + add_arg("--upper-limit", dest="upper-limit", action="append") ## geoip - add_arg('--src-cc', - '--source-country', - dest='source-country', - action='append') - add_arg('--dst-cc', - '--destination-country', - dest='destination-country', - action='append') + add_arg("--src-cc", "--source-country", dest="source-country", action="append") + add_arg( + "--dst-cc", "--destination-country", dest="destination-country", action="append" + ) ## gradm - add_arg('--enabled', dest='enabled', action='append') - add_arg('--disabled', dest='disabled', action='append') + add_arg("--enabled", dest="enabled", action="append") + add_arg("--disabled", dest="disabled", action="append") ## iface - add_arg('--iface', dest='iface', action='append') - add_arg('--dev-in', dest='dev-in', action='append') - add_arg('--dev-out', dest='dev-out', action='append') - add_arg('--up', dest='up', action='append') - add_arg('--down', dest='down', action='append') - add_arg('--broadcast', dest='broadcast', action='append') - add_arg('--loopback', dest='loopback', action='append') - add_arg('--pointtopoint', dest='pointtopoint', action='append') - add_arg('--running', dest='running', action='append') - add_arg('--noarp', dest='noarp', action='append') - add_arg('--arp', dest='arp', action='append') - add_arg('--promisc', dest='promisc', action='append') - add_arg('--multicast', dest='multicast', action='append') - add_arg('--dynamic', dest='dynamic', action='append') - add_arg('--lower-up', dest='lower-up', action='append') - add_arg('--dormant', dest='dormant', action='append') + add_arg("--iface", dest="iface", action="append") + add_arg("--dev-in", dest="dev-in", action="append") + add_arg("--dev-out", dest="dev-out", action="append") + add_arg("--up", dest="up", action="append") + add_arg("--down", dest="down", action="append") + add_arg("--broadcast", dest="broadcast", action="append") + add_arg("--loopback", dest="loopback", action="append") + add_arg("--pointtopoint", dest="pointtopoint", action="append") + add_arg("--running", dest="running", action="append") + add_arg("--noarp", dest="noarp", action="append") + add_arg("--arp", dest="arp", action="append") + add_arg("--promisc", dest="promisc", action="append") + add_arg("--multicast", dest="multicast", action="append") + add_arg("--dynamic", dest="dynamic", action="append") + add_arg("--lower-up", dest="lower-up", action="append") + add_arg("--dormant", dest="dormant", action="append") ## ipp2p - add_arg('--edk', dest='edk', action='append') - add_arg('--kazaa', dest='kazaa', action='append') - add_arg('--gnu', dest='gnu', action='append') - add_arg('--dc', dest='dc', action='append') - add_arg('--bit', dest='bit', action='append') - add_arg('--apple', dest='apple', action='append') - add_arg('--soul', dest='soul', action='append') - add_arg('--winmx', dest='winmx', action='append') - add_arg('--ares', dest='ares', action='append') - add_arg('--debug', dest='debug', action='append') + add_arg("--edk", dest="edk", action="append") + add_arg("--kazaa", dest="kazaa", action="append") + add_arg("--gnu", dest="gnu", action="append") + add_arg("--dc", dest="dc", action="append") + add_arg("--bit", dest="bit", action="append") + add_arg("--apple", dest="apple", action="append") + add_arg("--soul", dest="soul", action="append") + add_arg("--winmx", dest="winmx", action="append") + add_arg("--ares", dest="ares", action="append") + add_arg("--debug", dest="debug", action="append") ## ipv4options - add_arg('--flags', dest='flags', action='append') - add_arg('--any', dest='any', action='append') + add_arg("--flags", dest="flags", action="append") + add_arg("--any", dest="any", action="append") ## length2 - add_arg('--layer3', dest='layer3', action='append') - add_arg('--layer4', dest='layer4', action='append') - add_arg('--layer5', dest='layer5', action='append') + add_arg("--layer3", dest="layer3", action="append") + add_arg("--layer4", dest="layer4", action="append") + add_arg("--layer5", dest="layer5", action="append") ## lscan - add_arg('--stealth', dest='stealth', action='append') - add_arg('--synscan', dest='synscan', action='append') - add_arg('--cnscan', dest='cnscan', action='append') - add_arg('--grscan', dest='grscan', action='append') + add_arg("--stealth", dest="stealth", action="append") + add_arg("--synscan", dest="synscan", action="append") + add_arg("--cnscan", dest="cnscan", action="append") + add_arg("--grscan", dest="grscan", action="append") ## psd - add_arg('--psd-weight-threshold', - dest='psd-weight-threshold', - action='append') - add_arg('--psd-delay-threshold', - dest='psd-delay-threshold', - action='append') - add_arg('--psd-lo-ports-weight', - dest='psd-lo-ports-weight', - action='append') - add_arg('--psd-hi-ports-weight', - dest='psd-hi-ports-weight', - action='append') + add_arg("--psd-weight-threshold", dest="psd-weight-threshold", action="append") + add_arg("--psd-delay-threshold", dest="psd-delay-threshold", action="append") + add_arg("--psd-lo-ports-weight", dest="psd-lo-ports-weight", action="append") + add_arg("--psd-hi-ports-weight", dest="psd-hi-ports-weight", action="append") ## quota2 - add_arg('--grow', dest='grow', action='append') - add_arg('--no-change', dest='no-change', action='append') - add_arg('--packets', dest='packets', action='append') + add_arg("--grow", dest="grow", action="append") + add_arg("--no-change", dest="no-change", action="append") + add_arg("--packets", dest="packets", action="append") ## pknock - add_arg('--knockports', dest='knockports', action='append') - add_arg('--time', dest='time', action='append') - add_arg('--autoclose', dest='autoclose', action='append') - add_arg('--checkip', dest='checkip', action='append') + add_arg("--knockports", dest="knockports", action="append") + add_arg("--time", dest="time", action="append") + add_arg("--autoclose", dest="autoclose", action="append") + add_arg("--checkip", dest="checkip", action="append") # TARGET EXTENSIONS ## AUDIT - add_arg('--type', dest='type', action='append') + add_arg("--type", dest="type", action="append") ## CHECKSUM - add_arg('--checksum-fill', dest='checksum-fill', action='append') + add_arg("--checksum-fill", dest="checksum-fill", action="append") ## CLASSIFY - add_arg('--set-class', dest='set-class', action='append') + add_arg("--set-class", dest="set-class", action="append") ## CLUSTERIP - add_arg('--new', dest='new', action='append') - add_arg('--hashmode', dest='hashmode', action='append') - add_arg('--clustermac', dest='clustermac', action='append') - add_arg('--total-nodes', dest='total-nodes', action='append') - add_arg('--local-node', dest='local-node', action='append') - add_arg('--hash-init', dest='hash-init', action='append') + add_arg("--new", dest="new", action="append") + add_arg("--hashmode", dest="hashmode", action="append") + add_arg("--clustermac", dest="clustermac", action="append") + add_arg("--total-nodes", dest="total-nodes", action="append") + add_arg("--local-node", dest="local-node", action="append") + add_arg("--hash-init", dest="hash-init", action="append") ## CONNMARK - add_arg('--set-xmark', dest='set-xmark', action='append') - add_arg('--save-mark', dest='save-mark', action='append') - add_arg('--restore-mark', dest='restore-mark', action='append') - add_arg('--and-mark', dest='and-mark', action='append') - add_arg('--or-mark', dest='or-mark', action='append') - add_arg('--xor-mark', dest='xor-mark', action='append') - add_arg('--set-mark', dest='set-mark', action='append') - add_arg('--nfmask', dest='nfmask', action='append') - add_arg('--ctmask', dest='ctmask', action='append') + add_arg("--set-xmark", dest="set-xmark", action="append") + add_arg("--save-mark", dest="save-mark", action="append") + add_arg("--restore-mark", dest="restore-mark", action="append") + add_arg("--and-mark", dest="and-mark", action="append") + add_arg("--or-mark", dest="or-mark", action="append") + add_arg("--xor-mark", dest="xor-mark", action="append") + add_arg("--set-mark", dest="set-mark", action="append") + add_arg("--nfmask", dest="nfmask", action="append") + add_arg("--ctmask", dest="ctmask", action="append") ## CONNSECMARK - add_arg('--save', dest='save', action='append') - add_arg('--restore', dest='restore', action='append') + add_arg("--save", dest="save", action="append") + add_arg("--restore", dest="restore", action="append") ## CT - add_arg('--notrack', dest='notrack', action='append') - add_arg('--ctevents', dest='ctevents', action='append') - add_arg('--expevents', dest='expevents', action='append') - add_arg('--zone', dest='zone', action='append') - add_arg('--timeout', dest='timeout', action='append') + add_arg("--notrack", dest="notrack", action="append") + add_arg("--ctevents", dest="ctevents", action="append") + add_arg("--expevents", dest="expevents", action="append") + add_arg("--zone", dest="zone", action="append") + add_arg("--timeout", dest="timeout", action="append") ## DNAT - add_arg('--to-destination', dest='to-destination', action='append') - add_arg('--random', dest='random', action='append') - add_arg('--persistent', dest='persistent', action='append') + add_arg("--to-destination", dest="to-destination", action="append") + add_arg("--random", dest="random", action="append") + add_arg("--persistent", dest="persistent", action="append") ## DNPT - add_arg('--src-pfx', dest='src-pfx', action='append') - add_arg('--dst-pfx', dest='dst-pfx', action='append') + add_arg("--src-pfx", dest="src-pfx", action="append") + add_arg("--dst-pfx", dest="dst-pfx", action="append") ## DSCP - add_arg('--set-dscp', dest='set-dscp', action='append') - add_arg('--set-dscp-class', dest='set-dscp-class', action='append') + add_arg("--set-dscp", dest="set-dscp", action="append") + add_arg("--set-dscp-class", dest="set-dscp-class", action="append") ## ECN - add_arg('--ecn-tcp-remove', dest='ecn-tcp-remove', action='append') + add_arg("--ecn-tcp-remove", dest="ecn-tcp-remove", action="append") ## HL - add_arg('--hl-set', dest='hl-set', action='append') - add_arg('--hl-dec', dest='hl-dec', action='append') - add_arg('--hl-inc', dest='hl-inc', action='append') + add_arg("--hl-set", dest="hl-set", action="append") + add_arg("--hl-dec", dest="hl-dec", action="append") + add_arg("--hl-inc", dest="hl-inc", action="append") ## HMARK - add_arg('--hmark-tuple', dest='hmark-tuple', action='append') - add_arg('--hmark-mod', dest='hmark-mod', action='append') - add_arg('--hmark-offset', dest='hmark-offset', action='append') - add_arg('--hmark-src-prefix', dest='hmark-src-prefix', action='append') - add_arg('--hmark-dst-prefix', dest='hmark-dst-prefix', action='append') - add_arg('--hmark-sport-mask', dest='hmark-sport-mask', action='append') - add_arg('--hmark-dport-mask', dest='hmark-dport-mask', action='append') - add_arg('--hmark-spi-mask', dest='hmark-spi-mask', action='append') - add_arg('--hmark-proto-mask', dest='hmark-proto-mask', action='append') - add_arg('--hmark-rnd', dest='hmark-rnd', action='append') + add_arg("--hmark-tuple", dest="hmark-tuple", action="append") + add_arg("--hmark-mod", dest="hmark-mod", action="append") + add_arg("--hmark-offset", dest="hmark-offset", action="append") + add_arg("--hmark-src-prefix", dest="hmark-src-prefix", action="append") + add_arg("--hmark-dst-prefix", dest="hmark-dst-prefix", action="append") + add_arg("--hmark-sport-mask", dest="hmark-sport-mask", action="append") + add_arg("--hmark-dport-mask", dest="hmark-dport-mask", action="append") + add_arg("--hmark-spi-mask", dest="hmark-spi-mask", action="append") + add_arg("--hmark-proto-mask", dest="hmark-proto-mask", action="append") + add_arg("--hmark-rnd", dest="hmark-rnd", action="append") ## LED - add_arg('--led-trigger-id', dest='led-trigger-id', action='append') - add_arg('--led-delay', dest='led-delay', action='append') - add_arg('--led-always-blink', dest='led-always-blink', action='append') + add_arg("--led-trigger-id", dest="led-trigger-id", action="append") + add_arg("--led-delay", dest="led-delay", action="append") + add_arg("--led-always-blink", dest="led-always-blink", action="append") ## LOG - add_arg('--log-level', dest='log-level', action='append') - add_arg('--log-prefix', dest='log-prefix', action='append') - add_arg('--log-tcp-sequence', dest='log-tcp-sequence', action='append') - add_arg('--log-tcp-options', dest='log-tcp-options', action='append') - add_arg('--log-ip-options', dest='log-ip-options', action='append') - add_arg('--log-uid', dest='log-uid', action='append') + add_arg("--log-level", dest="log-level", action="append") + add_arg("--log-prefix", dest="log-prefix", action="append") + add_arg("--log-tcp-sequence", dest="log-tcp-sequence", action="append") + add_arg("--log-tcp-options", dest="log-tcp-options", action="append") + add_arg("--log-ip-options", dest="log-ip-options", action="append") + add_arg("--log-uid", dest="log-uid", action="append") ## MASQUERADE - add_arg('--to-ports', dest='to-ports', action='append') + add_arg("--to-ports", dest="to-ports", action="append") ## NFLOG - add_arg('--nflog-group', dest='nflog-group', action='append') - add_arg('--nflog-prefix', dest='nflog-prefix', action='append') - add_arg('--nflog-range', dest='nflog-range', action='append') - add_arg('--nflog-threshold', dest='nflog-threshold', action='append') + add_arg("--nflog-group", dest="nflog-group", action="append") + add_arg("--nflog-prefix", dest="nflog-prefix", action="append") + add_arg("--nflog-range", dest="nflog-range", action="append") + add_arg("--nflog-threshold", dest="nflog-threshold", action="append") ## NFQUEUE - add_arg('--queue-num', dest='queue-num', action='append') - add_arg('--queue-balance', dest='queue-balance', action='append') - add_arg('--queue-bypass', dest='queue-bypass', action='append') - add_arg('--queue-cpu-fanout', dest='queue-cpu-fanout', action='append') + add_arg("--queue-num", dest="queue-num", action="append") + add_arg("--queue-balance", dest="queue-balance", action="append") + add_arg("--queue-bypass", dest="queue-bypass", action="append") + add_arg("--queue-cpu-fanout", dest="queue-cpu-fanout", action="append") ## RATEEST - add_arg('--rateest-ewmalog', dest='rateest-ewmalog', action='append') + add_arg("--rateest-ewmalog", dest="rateest-ewmalog", action="append") ## REJECT - add_arg('--reject-with', dest='reject-with', action='append') + add_arg("--reject-with", dest="reject-with", action="append") ## SAME - add_arg('--nodst', dest='nodst', action='append') + add_arg("--nodst", dest="nodst", action="append") ## SECMARK - add_arg('--selctx', dest='selctx', action='append') + add_arg("--selctx", dest="selctx", action="append") ## SET - add_arg('--add-set', dest='add-set', action='append') - add_arg('--del-set', dest='del-set', action='append') - add_arg('--exist', dest='exist', action='append') + add_arg("--add-set", dest="add-set", action="append") + add_arg("--del-set", dest="del-set", action="append") + add_arg("--exist", dest="exist", action="append") ## SNAT - add_arg('--to-source', dest='to-source', action='append') + add_arg("--to-source", dest="to-source", action="append") ## TCPMSS - add_arg('--set-mss', dest='set-mss', action='append') - add_arg('--clamp-mss-to-pmtu', dest='clamp-mss-to-pmtu', action='append') + add_arg("--set-mss", dest="set-mss", action="append") + add_arg("--clamp-mss-to-pmtu", dest="clamp-mss-to-pmtu", action="append") ## TCPOPTSTRIP - add_arg('--strip-options', dest='strip-options', action='append') + add_arg("--strip-options", dest="strip-options", action="append") ## TEE - add_arg('--gateway', dest='gateway', action='append') + add_arg("--gateway", dest="gateway", action="append") ## TOS - add_arg('--set-tos', dest='set-tos', action='append') - add_arg('--and-tos', dest='and-tos', action='append') - add_arg('--or-tos', dest='or-tos', action='append') - add_arg('--xor-tos', dest='xor-tos', action='append') + add_arg("--set-tos", dest="set-tos", action="append") + add_arg("--and-tos", dest="and-tos", action="append") + add_arg("--or-tos", dest="or-tos", action="append") + add_arg("--xor-tos", dest="xor-tos", action="append") ## TPROXY - add_arg('--on-port', dest='on-port', action='append') - add_arg('--on-ip', dest='on-ip', action='append') - add_arg('--tproxy-mark', dest='tproxy-mark', action='append') + add_arg("--on-port", dest="on-port", action="append") + add_arg("--on-ip", dest="on-ip", action="append") + add_arg("--tproxy-mark", dest="tproxy-mark", action="append") ## TTL - add_arg('--ttl-set', dest='ttl-set', action='append') - add_arg('--ttl-dec', dest='ttl-dec', action='append') - add_arg('--ttl-inc', dest='ttl-inc', action='append') + add_arg("--ttl-set", dest="ttl-set", action="append") + add_arg("--ttl-dec", dest="ttl-dec", action="append") + add_arg("--ttl-inc", dest="ttl-inc", action="append") ## ULOG - add_arg('--ulog-nlgroup', dest='ulog-nlgroup', action='append') - add_arg('--ulog-prefix', dest='ulog-prefix', action='append') - add_arg('--ulog-cprange', dest='ulog-cprange', action='append') - add_arg('--ulog-qthreshold', dest='ulog-qthreshold', action='append') + add_arg("--ulog-nlgroup", dest="ulog-nlgroup", action="append") + add_arg("--ulog-prefix", dest="ulog-prefix", action="append") + add_arg("--ulog-cprange", dest="ulog-cprange", action="append") + add_arg("--ulog-qthreshold", dest="ulog-qthreshold", action="append") # Xtables-addons targets ## ACCOUNT - add_arg('--addr', dest='addr', action='append') - add_arg('--tname', dest='tname', action='append') + add_arg("--addr", dest="addr", action="append") + add_arg("--tname", dest="tname", action="append") ## CHAOS - add_arg('--delude', dest='delude', action='append') - add_arg('--tarpit', dest='tarpit', action='append') + add_arg("--delude", dest="delude", action="append") + add_arg("--tarpit", dest="tarpit", action="append") ## DHCPMAC - add_arg('--set-mac', dest='set-mac', action='append') + add_arg("--set-mac", dest="set-mac", action="append") ## DNETMAP - add_arg('--prefix', dest='prefix', action='append') - add_arg('--reuse', dest='reuse', action='append') - add_arg('--static', dest='static', action='append') + add_arg("--prefix", dest="prefix", action="append") + add_arg("--reuse", dest="reuse", action="append") + add_arg("--static", dest="static", action="append") ## IPMARK - add_arg('--and-mask', dest='and-mask', action='append') - add_arg('--or-mask', dest='or-mask', action='append') - add_arg('--shift', dest='shift', action='append') + add_arg("--and-mask", dest="and-mask", action="append") + add_arg("--or-mask", dest="or-mask", action="append") + add_arg("--shift", dest="shift", action="append") ## TARPIT - add_arg('--honeypot', dest='honeypot', action='append') - add_arg('--reset', dest='reset', action='append') + add_arg("--honeypot", dest="honeypot", action="append") + add_arg("--reset", dest="reset", action="append") return parser diff --git a/salt/modules/iwtools.py b/salt/modules/iwtools.py index 3cd44facf22..1b3af25e6a6 100644 --- a/salt/modules/iwtools.py +++ b/salt/modules/iwtools.py @@ -1,109 +1,117 @@ # -*- coding: utf-8 -*- -''' +""" Support for Wireless Tools for Linux -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.path from salt.exceptions import SaltInvocationError - log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if iwconfig is installed - ''' - if salt.utils.path.which('iwconfig'): + """ + if salt.utils.path.which("iwconfig"): return True - return (False, 'The iwtools execution module cannot be loaded: ' - 'iwconfig is not installed.') + return ( + False, + "The iwtools execution module cannot be loaded: iwconfig is not installed.", + ) def scan(iface, style=None): - ''' + """ List networks on a wireless interface CLI Examples: salt minion iwtools.scan wlp3s0 salt minion iwtools.scan wlp3s0 list - ''' + """ if not _valid_iface(iface): - raise SaltInvocationError( - 'The interface specified is not valid' - ) + raise SaltInvocationError("The interface specified is not valid") - out = __salt__['cmd.run']('iwlist {0} scan'.format(iface)) - if 'Network is down' in out: - __salt__['cmd.run']('ip link set {0} up'.format(iface)) - out = __salt__['cmd.run']('iwlist {0} scan'.format(iface)) + out = __salt__["cmd.run"]("iwlist {0} scan".format(iface)) + if "Network is down" in out: + __salt__["cmd.run"]("ip link set {0} up".format(iface)) + out = __salt__["cmd.run"]("iwlist {0} scan".format(iface)) ret = {} tmp = {} for line in out.splitlines(): if not line.strip(): continue - if 'Scan completed' in line: + if "Scan completed" in line: continue - if line.strip().startswith('Cell'): - comps = line.split(' - ') + if line.strip().startswith("Cell"): + comps = line.split(" - ") line = comps[1] if tmp: - ret[tmp['Address']] = tmp + ret[tmp["Address"]] = tmp tmp = {} - comps = line.split(':') - if comps[0].strip() == 'Address': + comps = line.split(":") + if comps[0].strip() == "Address": # " is a valid character in SSIDs, but iwlist likes to wrap SSIDs in them comps[1] = comps[1].lstrip('"').rstrip('"') - if comps[0].strip() == 'IE': - if 'IE' not in tmp: - tmp['IE'] = [] - tmp['IE'].append(':'.join(comps[1:]).strip()) + if comps[0].strip() == "IE": + if "IE" not in tmp: + tmp["IE"] = [] + tmp["IE"].append(":".join(comps[1:]).strip()) else: - tmp[comps[0].strip()] = ':'.join(comps[1:]).strip() + tmp[comps[0].strip()] = ":".join(comps[1:]).strip() - ret[tmp['Address']] = tmp + ret[tmp["Address"]] = tmp - if style == 'list': + if style == "list": return ret.keys() return ret def set_mode(iface, mode): - ''' + """ List networks on a wireless interface CLI Example: salt minion iwtools.set_mode wlp3s0 Managed - ''' + """ if not _valid_iface(iface): - raise SaltInvocationError( - 'The interface specified is not valid' - ) + raise SaltInvocationError("The interface specified is not valid") - valid_modes = ('Managed', 'Ad-Hoc', 'Master', 'Repeater', 'Secondary', 'Monitor', 'Auto') + valid_modes = ( + "Managed", + "Ad-Hoc", + "Master", + "Repeater", + "Secondary", + "Monitor", + "Auto", + ) if mode not in valid_modes: raise SaltInvocationError( - 'One of the following modes must be specified: {0}'.format(', '.join(valid_modes)) + "One of the following modes must be specified: {0}".format( + ", ".join(valid_modes) + ) ) - __salt__['ip.down'](iface) - out = __salt__['cmd.run']('iwconfig {0} mode {1}'.format(iface, mode)) - __salt__['ip.up'](iface) + __salt__["ip.down"](iface) + out = __salt__["cmd.run"]("iwconfig {0} mode {1}".format(iface, mode)) + __salt__["ip.up"](iface) return mode def _valid_iface(iface): - ''' + """ Validate the specified interface - ''' + """ ifaces = list_interfaces() if iface in ifaces.keys(): return True @@ -111,45 +119,45 @@ def _valid_iface(iface): def list_interfaces(style=None): - ''' + """ List all of the wireless interfaces CLI Example: salt minion iwtools.list_interfaces - ''' + """ ret = {} tmp = None iface = None - out = __salt__['cmd.run']('iwconfig') + out = __salt__["cmd.run"]("iwconfig") for line in out.splitlines(): if not line: continue - if 'no wireless extensions' in line: + if "no wireless extensions" in line: continue - comps = line.strip().split(' ') - if not line.startswith(' '): + comps = line.strip().split(" ") + if not line.startswith(" "): if tmp is not None: ret[iface] = tmp.copy() iface = comps.pop(0) - tmp = {'extra': []} + tmp = {"extra": []} for item in comps: - if ':' in item: - parts = item.split(':') + if ":" in item: + parts = item.split(":") key = parts[0].strip() value = parts[1].strip() - if key == 'ESSID': + if key == "ESSID": value = value.lstrip('"').rstrip('"') tmp[key] = value - elif '=' in item: - parts = item.split('=') + elif "=" in item: + parts = item.split("=") tmp[parts[0].strip()] = parts[1].strip() else: - tmp['extra'].append(item) + tmp["extra"].append(item) ret[iface] = tmp.copy() - if style == 'list': + if style == "list": return ret.keys() return ret diff --git a/salt/modules/jboss7.py b/salt/modules/jboss7.py index da9e7b2452d..1d8ca1649a2 100644 --- a/salt/modules/jboss7.py +++ b/salt/modules/jboss7.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing JBoss AS 7 through the CLI interface. .. versionadded:: 2015.5.0 @@ -20,12 +20,13 @@ Example: cli_user: 'jbossadm' cli_password: 'jbossadm' -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import re + import logging +import re # Import Salt libs import salt.utils.dictdiffer as dictdiffer @@ -36,13 +37,11 @@ from salt.ext import six log = logging.getLogger(__name__) -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} def status(jboss_config, host=None, server_config=None): - ''' + """ Get status of running jboss instance. jboss_config @@ -60,21 +59,25 @@ def status(jboss_config, host=None, server_config=None): salt '*' jboss7.status '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' - ''' + """ log.debug("======================== MODULE FUNCTION: jboss7.status") if host is None and server_config is None: - operation = ':read-attribute(name=server-state)' + operation = ":read-attribute(name=server-state)" elif host is not None and server_config is not None: operation = '/host="{host}"/server-config="{server_config}"/:read-attribute(name=status)'.format( - host=host, - server_config=server_config) + host=host, server_config=server_config + ) else: - raise SaltInvocationError('Invalid parameters. Must either pass both host and server_config or neither') - return __salt__['jboss7_cli.run_operation'](jboss_config, operation, fail_on_error=False, retries=0) + raise SaltInvocationError( + "Invalid parameters. Must either pass both host and server_config or neither" + ) + return __salt__["jboss7_cli.run_operation"]( + jboss_config, operation, fail_on_error=False, retries=0 + ) def stop_server(jboss_config, host=None): - ''' + """ Stop running jboss instance jboss_config @@ -89,22 +92,31 @@ def stop_server(jboss_config, host=None): salt '*' jboss7.stop_server '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' - ''' + """ log.debug("======================== MODULE FUNCTION: jboss7.stop_server") if host is None: - operation = ':shutdown' + operation = ":shutdown" else: operation = '/host="{host}"/:shutdown'.format(host=host) - shutdown_result = __salt__['jboss7_cli.run_operation'](jboss_config, operation, fail_on_error=False) + shutdown_result = __salt__["jboss7_cli.run_operation"]( + jboss_config, operation, fail_on_error=False + ) # JBoss seems to occasionaly close the channel immediately when :shutdown is sent - if shutdown_result['success'] or (not shutdown_result['success'] and 'Operation failed: Channel closed' in shutdown_result['stdout']): + if shutdown_result["success"] or ( + not shutdown_result["success"] + and "Operation failed: Channel closed" in shutdown_result["stdout"] + ): return shutdown_result else: - raise Exception('''Cannot handle error, return code={retcode}, stdout='{stdout}', stderr='{stderr}' '''.format(**shutdown_result)) + raise Exception( + """Cannot handle error, return code={retcode}, stdout='{stdout}', stderr='{stderr}' """.format( + **shutdown_result + ) + ) def reload_(jboss_config, host=None): - ''' + """ Reload running jboss instance jboss_config @@ -119,24 +131,35 @@ def reload_(jboss_config, host=None): salt '*' jboss7.reload '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' - ''' + """ log.debug("======================== MODULE FUNCTION: jboss7.reload") if host is None: - operation = ':reload' + operation = ":reload" else: operation = '/host="{host}"/:reload'.format(host=host) - reload_result = __salt__['jboss7_cli.run_operation'](jboss_config, operation, fail_on_error=False) + reload_result = __salt__["jboss7_cli.run_operation"]( + jboss_config, operation, fail_on_error=False + ) # JBoss seems to occasionaly close the channel immediately when :reload is sent - if reload_result['success'] or (not reload_result['success'] and - ('Operation failed: Channel closed' in reload_result['stdout'] or - 'Communication error: java.util.concurrent.ExecutionException: Operation failed' in reload_result['stdout'])): + if reload_result["success"] or ( + not reload_result["success"] + and ( + "Operation failed: Channel closed" in reload_result["stdout"] + or "Communication error: java.util.concurrent.ExecutionException: Operation failed" + in reload_result["stdout"] + ) + ): return reload_result else: - raise Exception('''Cannot handle error, return code={retcode}, stdout='{stdout}', stderr='{stderr}' '''.format(**reload_result)) + raise Exception( + """Cannot handle error, return code={retcode}, stdout='{stdout}', stderr='{stderr}' """.format( + **reload_result + ) + ) def create_datasource(jboss_config, name, datasource_properties, profile=None): - ''' + """ Create datasource in running jboss instance jboss_config @@ -160,56 +183,72 @@ def create_datasource(jboss_config, name, datasource_properties, profile=None): .. code-block:: bash salt '*' jboss7.create_datasource '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' 'my_datasource' '{"driver-name": "mysql", "connection-url": "jdbc:mysql://localhost:3306/sampleDatabase", "jndi-name": "java:jboss/datasources/sampleDS", "user-name": "sampleuser", "password": "secret", "min-pool-size": 3, "use-java-context": True}' - ''' - log.debug("======================== MODULE FUNCTION: jboss7.create_datasource, name=%s, profile=%s", name, profile) - ds_resource_description = __get_datasource_resource_description(jboss_config, name, profile) + """ + log.debug( + "======================== MODULE FUNCTION: jboss7.create_datasource, name=%s, profile=%s", + name, + profile, + ) + ds_resource_description = __get_datasource_resource_description( + jboss_config, name, profile + ) operation = '/subsystem=datasources/data-source="{name}":add({properties})'.format( name=name, - properties=__get_properties_assignment_string(datasource_properties, ds_resource_description) + properties=__get_properties_assignment_string( + datasource_properties, ds_resource_description + ), ) if profile is not None: operation = '/profile="{profile}"'.format(profile=profile) + operation - return __salt__['jboss7_cli.run_operation'](jboss_config, operation, fail_on_error=False) + return __salt__["jboss7_cli.run_operation"]( + jboss_config, operation, fail_on_error=False + ) def __get_properties_assignment_string(datasource_properties, ds_resource_description): assignment_strings = [] - ds_attributes = ds_resource_description['attributes'] + ds_attributes = ds_resource_description["attributes"] for key, val in six.iteritems(datasource_properties): - assignment_strings.append(__get_single_assignment_string(key, val, ds_attributes)) + assignment_strings.append( + __get_single_assignment_string(key, val, ds_attributes) + ) - return ','.join(assignment_strings) + return ",".join(assignment_strings) def __get_single_assignment_string(key, val, ds_attributes): - return '{0}={1}'.format(key, __format_value(key, val, ds_attributes)) + return "{0}={1}".format(key, __format_value(key, val, ds_attributes)) def __format_value(key, value, ds_attributes): - type_ = ds_attributes[key]['type'] - if type_ == 'BOOLEAN': - if value in ('true', 'false'): + type_ = ds_attributes[key]["type"] + if type_ == "BOOLEAN": + if value in ("true", "false"): return value elif isinstance(value, bool): if value: - return 'true' + return "true" else: - return 'false' + return "false" else: - raise Exception("Don't know how to convert {0} to BOOLEAN type".format(value)) + raise Exception( + "Don't know how to convert {0} to BOOLEAN type".format(value) + ) - elif type_ == 'INT': + elif type_ == "INT": return six.text_type(value) - elif type_ == 'STRING': + elif type_ == "STRING": return '"{0}"'.format(value) else: - raise Exception("Don't know how to format value {0} of type {1}".format(value, type_)) + raise Exception( + "Don't know how to format value {0} of type {1}".format(value, type_) + ) def update_datasource(jboss_config, name, new_properties, profile=None): - ''' + """ Update an existing datasource in running jboss instance. If the property doesn't exist if will be created, if it does, it will be updated with the new value @@ -235,42 +274,57 @@ def update_datasource(jboss_config, name, new_properties, profile=None): salt '*' jboss7.update_datasource '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' 'my_datasource' '{"driver-name": "mysql", "connection-url": "jdbc:mysql://localhost:3306/sampleDatabase", "jndi-name": "java:jboss/datasources/sampleDS", "user-name": "sampleuser", "password": "secret", "min-pool-size": 3, "use-java-context": True}' - ''' - log.debug("======================== MODULE FUNCTION: jboss7.update_datasource, name=%s, profile=%s", name, profile) + """ + log.debug( + "======================== MODULE FUNCTION: jboss7.update_datasource, name=%s, profile=%s", + name, + profile, + ) ds_result = __read_datasource(jboss_config, name, profile) - current_properties = ds_result['result'] + current_properties = ds_result["result"] diff = dictdiffer.DictDiffer(new_properties, current_properties) changed_properties = diff.changed() - ret = { - 'success': True, - 'comment': '' - } + ret = {"success": True, "comment": ""} if len(changed_properties) > 0: - ds_resource_description = __get_datasource_resource_description(jboss_config, name, profile) - ds_attributes = ds_resource_description['attributes'] + ds_resource_description = __get_datasource_resource_description( + jboss_config, name, profile + ) + ds_attributes = ds_resource_description["attributes"] for key in changed_properties: - update_result = __update_datasource_property(jboss_config, name, key, new_properties[key], ds_attributes, profile) - if not update_result['success']: - ret['result'] = False - ret['comment'] = ret['comment'] + ('Could not update datasource property {0} with value {1},\n stdout: {2}\n'.format(key, new_properties[key], update_result['stdout'])) + update_result = __update_datasource_property( + jboss_config, name, key, new_properties[key], ds_attributes, profile + ) + if not update_result["success"]: + ret["result"] = False + ret["comment"] = ret["comment"] + ( + "Could not update datasource property {0} with value {1},\n stdout: {2}\n".format( + key, new_properties[key], update_result["stdout"] + ) + ) return ret def __get_datasource_resource_description(jboss_config, name, profile=None): - log.debug("======================== MODULE FUNCTION: jboss7.__get_datasource_resource_description, name=%s, profile=%s", name, profile) + log.debug( + "======================== MODULE FUNCTION: jboss7.__get_datasource_resource_description, name=%s, profile=%s", + name, + profile, + ) - operation = '/subsystem=datasources/data-source="{name}":read-resource-description'.format(name=name) + operation = '/subsystem=datasources/data-source="{name}":read-resource-description'.format( + name=name + ) if profile is not None: operation = '/profile="{profile}"'.format(profile=profile) + operation - operation_result = __salt__['jboss7_cli.run_operation'](jboss_config, operation) - if operation_result['outcome']: - return operation_result['result'] + operation_result = __salt__["jboss7_cli.run_operation"](jboss_config, operation) + if operation_result["outcome"]: + return operation_result["result"] def read_datasource(jboss_config, name, profile=None): - ''' + """ Read datasource properties in the running jboss instance. jboss_config @@ -285,13 +339,16 @@ def read_datasource(jboss_config, name, profile=None): .. code-block:: bash salt '*' jboss7.read_datasource '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' - ''' - log.debug("======================== MODULE FUNCTION: jboss7.read_datasource, name=%s", name) + """ + log.debug( + "======================== MODULE FUNCTION: jboss7.read_datasource, name=%s", + name, + ) return __read_datasource(jboss_config, name, profile) def create_simple_binding(jboss_config, binding_name, value, profile=None): - ''' + """ Create a simple jndi binding in the running jboss instance jboss_config @@ -311,19 +368,23 @@ def create_simple_binding(jboss_config, binding_name, value, profile=None): '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", \\ "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' \\ my_binding_name my_binding_value - ''' - log.debug("======================== MODULE FUNCTION: jboss7.create_simple_binding, binding_name=%s, value=%s, profile=%s", binding_name, value, profile) + """ + log.debug( + "======================== MODULE FUNCTION: jboss7.create_simple_binding, binding_name=%s, value=%s, profile=%s", + binding_name, + value, + profile, + ) operation = '/subsystem=naming/binding="{binding_name}":add(binding-type=simple, value="{value}")'.format( - binding_name=binding_name, - value=__escape_binding_value(value) + binding_name=binding_name, value=__escape_binding_value(value) ) if profile is not None: operation = '/profile="{profile}"'.format(profile=profile) + operation - return __salt__['jboss7_cli.run_operation'](jboss_config, operation) + return __salt__["jboss7_cli.run_operation"](jboss_config, operation) def update_simple_binding(jboss_config, binding_name, value, profile=None): - ''' + """ Update the simple jndi binding in the running jboss instance jboss_config @@ -340,19 +401,23 @@ def update_simple_binding(jboss_config, binding_name, value, profile=None): .. code-block:: bash salt '*' jboss7.update_simple_binding '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' my_binding_name my_binding_value - ''' - log.debug("======================== MODULE FUNCTION: jboss7.update_simple_binding, binding_name=%s, value=%s, profile=%s", binding_name, value, profile) + """ + log.debug( + "======================== MODULE FUNCTION: jboss7.update_simple_binding, binding_name=%s, value=%s, profile=%s", + binding_name, + value, + profile, + ) operation = '/subsystem=naming/binding="{binding_name}":write-attribute(name=value, value="{value}")'.format( - binding_name=binding_name, - value=__escape_binding_value(value) + binding_name=binding_name, value=__escape_binding_value(value) ) if profile is not None: operation = '/profile="{profile}"'.format(profile=profile) + operation - return __salt__['jboss7_cli.run_operation'](jboss_config, operation) + return __salt__["jboss7_cli.run_operation"](jboss_config, operation) def read_simple_binding(jboss_config, binding_name, profile=None): - ''' + """ Read jndi binding in the running jboss instance jboss_config @@ -367,52 +432,69 @@ def read_simple_binding(jboss_config, binding_name, profile=None): .. code-block:: bash salt '*' jboss7.read_simple_binding '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' my_binding_name - ''' - log.debug("======================== MODULE FUNCTION: jboss7.read_simple_binding, %s", binding_name) + """ + log.debug( + "======================== MODULE FUNCTION: jboss7.read_simple_binding, %s", + binding_name, + ) return __read_simple_binding(jboss_config, binding_name, profile=profile) def __read_simple_binding(jboss_config, binding_name, profile=None): - operation = '/subsystem=naming/binding="{binding_name}":read-resource'.format(binding_name=binding_name) + operation = '/subsystem=naming/binding="{binding_name}":read-resource'.format( + binding_name=binding_name + ) if profile is not None: operation = '/profile="{profile}"'.format(profile=profile) + operation - return __salt__['jboss7_cli.run_operation'](jboss_config, operation) + return __salt__["jboss7_cli.run_operation"](jboss_config, operation) -def __update_datasource_property(jboss_config, datasource_name, name, value, ds_attributes, profile=None): - log.debug("======================== MODULE FUNCTION: jboss7.__update_datasource_property, datasource_name=%s, name=%s, value=%s, profile=%s", datasource_name, name, value, profile) +def __update_datasource_property( + jboss_config, datasource_name, name, value, ds_attributes, profile=None +): + log.debug( + "======================== MODULE FUNCTION: jboss7.__update_datasource_property, datasource_name=%s, name=%s, value=%s, profile=%s", + datasource_name, + name, + value, + profile, + ) operation = '/subsystem=datasources/data-source="{datasource_name}":write-attribute(name="{name}",value={value})'.format( - datasource_name=datasource_name, - name=name, - value=__format_value(name, value, ds_attributes) + datasource_name=datasource_name, + name=name, + value=__format_value(name, value, ds_attributes), ) if profile is not None: operation = '/profile="{profile}"'.format(profile=profile) + operation - return __salt__['jboss7_cli.run_operation'](jboss_config, operation, fail_on_error=False) + return __salt__["jboss7_cli.run_operation"]( + jboss_config, operation, fail_on_error=False + ) def __read_datasource(jboss_config, name, profile=None): - operation = '/subsystem=datasources/data-source="{name}":read-resource'.format(name=name) + operation = '/subsystem=datasources/data-source="{name}":read-resource'.format( + name=name + ) if profile is not None: operation = '/profile="{profile}"'.format(profile=profile) + operation - operation_result = __salt__['jboss7_cli.run_operation'](jboss_config, operation) + operation_result = __salt__["jboss7_cli.run_operation"](jboss_config, operation) return operation_result def __escape_binding_value(binding_name): - result = binding_name.replace('\\', '\\\\\\\\') # replace \ -> \\\\ + result = binding_name.replace("\\", "\\\\\\\\") # replace \ -> \\\\ return result def remove_datasource(jboss_config, name, profile=None): - ''' + """ Remove an existing datasource from the running jboss instance. jboss_config @@ -427,18 +509,24 @@ def remove_datasource(jboss_config, name, profile=None): .. code-block:: bash salt '*' jboss7.remove_datasource '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' my_datasource_name - ''' - log.debug("======================== MODULE FUNCTION: jboss7.remove_datasource, name=%s, profile=%s", name, profile) + """ + log.debug( + "======================== MODULE FUNCTION: jboss7.remove_datasource, name=%s, profile=%s", + name, + profile, + ) - operation = '/subsystem=datasources/data-source={name}:remove'.format(name=name) + operation = "/subsystem=datasources/data-source={name}:remove".format(name=name) if profile is not None: operation = '/profile="{profile}"'.format(profile=profile) + operation - return __salt__['jboss7_cli.run_operation'](jboss_config, operation, fail_on_error=False) + return __salt__["jboss7_cli.run_operation"]( + jboss_config, operation, fail_on_error=False + ) def deploy(jboss_config, source_file): - ''' + """ Deploy the application on the jboss instance from the local file system where minion is running. jboss_config @@ -451,14 +539,19 @@ def deploy(jboss_config, source_file): .. code-block:: bash salt '*' jboss7.deploy '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' /opt/deploy_files/my_deploy - ''' - log.debug("======================== MODULE FUNCTION: jboss7.deploy, source_file=%s", source_file) - command = 'deploy {source_file} --force '.format(source_file=source_file) - return __salt__['jboss7_cli.run_command'](jboss_config, command, fail_on_error=False) + """ + log.debug( + "======================== MODULE FUNCTION: jboss7.deploy, source_file=%s", + source_file, + ) + command = "deploy {source_file} --force ".format(source_file=source_file) + return __salt__["jboss7_cli.run_command"]( + jboss_config, command, fail_on_error=False + ) def list_deployments(jboss_config): - ''' + """ List all deployments on the jboss instance jboss_config @@ -470,18 +563,18 @@ def list_deployments(jboss_config): salt '*' jboss7.list_deployments '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' - ''' + """ log.debug("======================== MODULE FUNCTION: jboss7.list_deployments") - command_result = __salt__['jboss7_cli.run_command'](jboss_config, 'deploy') + command_result = __salt__["jboss7_cli.run_command"](jboss_config, "deploy") deployments = [] - if len(command_result['stdout']) > 0: - deployments = re.split('\\s*', command_result['stdout']) - log.debug('deployments=%s', deployments) + if len(command_result["stdout"]) > 0: + deployments = re.split("\\s*", command_result["stdout"]) + log.debug("deployments=%s", deployments) return deployments def undeploy(jboss_config, deployment): - ''' + """ Undeploy the application from jboss instance jboss_config @@ -494,7 +587,10 @@ def undeploy(jboss_config, deployment): .. code-block:: bash salt '*' jboss7.undeploy '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' my_deployment - ''' - log.debug("======================== MODULE FUNCTION: jboss7.undeploy, deployment=%s", deployment) - command = 'undeploy {deployment} '.format(deployment=deployment) - return __salt__['jboss7_cli.run_command'](jboss_config, command) + """ + log.debug( + "======================== MODULE FUNCTION: jboss7.undeploy, deployment=%s", + deployment, + ) + command = "undeploy {deployment} ".format(deployment=deployment) + return __salt__["jboss7_cli.run_command"](jboss_config, command) diff --git a/salt/modules/jboss7_cli.py b/salt/modules/jboss7_cli.py index 69e18c13c5b..aa7de9d12d9 100644 --- a/salt/modules/jboss7_cli.py +++ b/salt/modules/jboss7_cli.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for low-level interaction with JbossAS7 through CLI. This module exposes two ways of interaction with the CLI, either through commands or operations. @@ -34,13 +34,14 @@ Example: cli_user: 'jbossadm' cli_password: 'jbossadm' -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import re import pprint +import re import time # Import Salt libs @@ -53,7 +54,7 @@ log = logging.getLogger(__name__) def run_command(jboss_config, command, fail_on_error=True): - ''' + """ Execute a command against jboss instance through the CLI interface. jboss_config @@ -69,22 +70,26 @@ def run_command(jboss_config, command, fail_on_error=True): .. code-block:: bash salt '*' jboss7_cli.run_command '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' my_command - ''' + """ cli_command_result = _call_cli(jboss_config, command) - if cli_command_result['retcode'] == 0: - cli_command_result['success'] = True + if cli_command_result["retcode"] == 0: + cli_command_result["success"] = True else: if fail_on_error: - raise CommandExecutionError('''Command execution failed, return code={retcode}, stdout='{stdout}', stderr='{stderr}' '''.format(**cli_command_result)) + raise CommandExecutionError( + """Command execution failed, return code={retcode}, stdout='{stdout}', stderr='{stderr}' """.format( + **cli_command_result + ) + ) else: - cli_command_result['success'] = False + cli_command_result["success"] = False return cli_command_result def run_operation(jboss_config, operation, fail_on_error=True, retries=1): - ''' + """ Execute an operation against jboss instance through the CLI interface. jboss_config @@ -103,75 +108,98 @@ def run_operation(jboss_config, operation, fail_on_error=True, retries=1): .. code-block:: bash salt '*' jboss7_cli.run_operation '{"cli_path": "integration.modules.sysmod.SysModuleTest.test_valid_docs", "controller": "10.11.12.13:9999", "cli_user": "jbossadm", "cli_password": "jbossadm"}' my_operation - ''' + """ cli_command_result = _call_cli(jboss_config, operation, retries) - if cli_command_result['retcode'] == 0: - if _is_cli_output(cli_command_result['stdout']): - cli_result = _parse(cli_command_result['stdout']) - cli_result['success'] = cli_result['outcome'] == 'success' + if cli_command_result["retcode"] == 0: + if _is_cli_output(cli_command_result["stdout"]): + cli_result = _parse(cli_command_result["stdout"]) + cli_result["success"] = cli_result["outcome"] == "success" else: - raise CommandExecutionError('Operation has returned unparseable output: {0}'.format(cli_command_result['stdout'])) + raise CommandExecutionError( + "Operation has returned unparseable output: {0}".format( + cli_command_result["stdout"] + ) + ) else: - if _is_cli_output(cli_command_result['stdout']): - cli_result = _parse(cli_command_result['stdout']) - cli_result['success'] = False + if _is_cli_output(cli_command_result["stdout"]): + cli_result = _parse(cli_command_result["stdout"]) + cli_result["success"] = False - match = re.search(r'^(JBAS\d+):', cli_result['failure-description']) + match = re.search(r"^(JBAS\d+):", cli_result["failure-description"]) # if match is None then check for wildfly error code if match is None: - match = re.search(r'^(WFLYCTL\d+):', cli_result['failure-description']) + match = re.search(r"^(WFLYCTL\d+):", cli_result["failure-description"]) if match is not None: - cli_result['err_code'] = match.group(1) + cli_result["err_code"] = match.group(1) else: # Could not find err_code log.error("Jboss 7 operation failed! Error Code could not be found!") - cli_result['err_code'] = '-1' + cli_result["err_code"] = "-1" - cli_result['stdout'] = cli_command_result['stdout'] + cli_result["stdout"] = cli_command_result["stdout"] else: if fail_on_error: - raise CommandExecutionError('''Command execution failed, return code={retcode}, stdout='{stdout}', stderr='{stderr}' '''.format(**cli_command_result)) + raise CommandExecutionError( + """Command execution failed, return code={retcode}, stdout='{stdout}', stderr='{stderr}' """.format( + **cli_command_result + ) + ) else: cli_result = { - 'success': False, - 'stdout': cli_command_result['stdout'], - 'stderr': cli_command_result['stderr'], - 'retcode': cli_command_result['retcode'] + "success": False, + "stdout": cli_command_result["stdout"], + "stderr": cli_command_result["stderr"], + "retcode": cli_command_result["retcode"], } return cli_result def _call_cli(jboss_config, command, retries=1): command_segments = [ - jboss_config['cli_path'], - '--connect', - '--controller="{0}"'.format(jboss_config['controller']) + jboss_config["cli_path"], + "--connect", + '--controller="{0}"'.format(jboss_config["controller"]), ] - if 'cli_user' in six.iterkeys(jboss_config): - command_segments.append('--user="{0}"'.format(jboss_config['cli_user'])) - if 'cli_password' in six.iterkeys(jboss_config): - command_segments.append('--password="{0}"'.format(jboss_config['cli_password'])) + if "cli_user" in six.iterkeys(jboss_config): + command_segments.append('--user="{0}"'.format(jboss_config["cli_user"])) + if "cli_password" in six.iterkeys(jboss_config): + command_segments.append('--password="{0}"'.format(jboss_config["cli_password"])) command_segments.append('--command="{0}"'.format(__escape_command(command))) - cli_script = ' '.join(command_segments) + cli_script = " ".join(command_segments) - cli_command_result = __salt__['cmd.run_all'](cli_script) - log.debug('cli_command_result=%s', cli_command_result) + cli_command_result = __salt__["cmd.run_all"](cli_script) + log.debug("cli_command_result=%s", cli_command_result) - log.debug('========= STDOUT:\n%s', cli_command_result['stdout']) - log.debug('========= STDERR:\n%s', cli_command_result['stderr']) - log.debug('========= RETCODE: %d', cli_command_result['retcode']) + log.debug("========= STDOUT:\n%s", cli_command_result["stdout"]) + log.debug("========= STDERR:\n%s", cli_command_result["stderr"]) + log.debug("========= RETCODE: %d", cli_command_result["retcode"]) - if cli_command_result['retcode'] == 127: - raise CommandExecutionError('Could not execute jboss-cli.sh script. Have you specified server_dir variable correctly?\nCurrent CLI path: {cli_path}. '.format(cli_path=jboss_config['cli_path'])) + if cli_command_result["retcode"] == 127: + raise CommandExecutionError( + "Could not execute jboss-cli.sh script. Have you specified server_dir variable correctly?\nCurrent CLI path: {cli_path}. ".format( + cli_path=jboss_config["cli_path"] + ) + ) - if cli_command_result['retcode'] == 1 and 'Unable to authenticate against controller' in cli_command_result['stderr']: - raise CommandExecutionError('Could not authenticate against controller, please check username and password for the management console. Err code: {retcode}, stdout: {stdout}, stderr: {stderr}'.format(**cli_command_result)) + if ( + cli_command_result["retcode"] == 1 + and "Unable to authenticate against controller" in cli_command_result["stderr"] + ): + raise CommandExecutionError( + "Could not authenticate against controller, please check username and password for the management console. Err code: {retcode}, stdout: {stdout}, stderr: {stderr}".format( + **cli_command_result + ) + ) # TODO add WFLYCTL code - if cli_command_result['retcode'] == 1 and 'JBAS012144' in cli_command_result['stderr'] and retries > 0: # Cannot connect to cli - log.debug('Command failed, retrying... (%d tries left)', retries) + if ( + cli_command_result["retcode"] == 1 + and "JBAS012144" in cli_command_result["stderr"] + and retries > 0 + ): # Cannot connect to cli + log.debug("Command failed, retrying... (%d tries left)", retries) time.sleep(3) return _call_cli(jboss_config, command, retries - 1) @@ -179,7 +207,7 @@ def _call_cli(jboss_config, command, retries=1): def __escape_command(command): - ''' + """ This function escapes the command so that can be passed in the command line to JBoss CLI. Escaping commands passed to jboss is extremely confusing. If you want to save a binding that contains a single backslash character read the following explanation. @@ -224,9 +252,9 @@ def __escape_command(command): ... are all the same thing:) Remember that the command that comes in is already (3) format. Now we need to escape it further to be able to pass it to command line. - ''' - result = command.replace('\\', '\\\\') # replace \ -> \\ - result = result.replace('"', '\\"') # replace " -> \" + """ + result = command.replace("\\", "\\\\") # replace \ -> \\ + result = result.replace('"', '\\"') # replace " -> \" return result @@ -298,7 +326,9 @@ def __process_tokens_internal(tokens, start_at=0): current_key = None elif __is_dict_start(token): log.debug(" TYPE: DICT START") - dict_value, token_no = __process_tokens_internal(tokens, start_at=token_no+1) + dict_value, token_no = __process_tokens_internal( + tokens, start_at=token_no + 1 + ) log.debug(" DICT = %s ", dict_value) result[current_key] = dict_value log.debug(" %s -> %s", current_key, result[current_key]) @@ -313,7 +343,7 @@ def __process_tokens_internal(tokens, start_at=0): log.debug(" TYPE: EXPRESSION") is_expression = True else: - raise CommandExecutionError('Unknown token! Token: {0}'.format(token)) + raise CommandExecutionError("Unknown token! Token: {0}".format(token)) token_no = token_no + 1 @@ -321,26 +351,28 @@ def __process_tokens_internal(tokens, start_at=0): def __tokenize(cli_output): # add all possible tokens here # \\ means a single backslash here - tokens_re = re.compile(r'("(?:[^"\\]|\\"|\\\\)*"|=>|{|}|true|false|undefined|[0-9A-Za-z]+)', re.DOTALL) + tokens_re = re.compile( + r'("(?:[^"\\]|\\"|\\\\)*"|=>|{|}|true|false|undefined|[0-9A-Za-z]+)', re.DOTALL + ) tokens = tokens_re.findall(cli_output) log.debug("tokens=%s", tokens) return tokens def __is_dict_start(token): - return token == '{' + return token == "{" def __is_dict_end(token): - return token == '}' + return token == "}" def __is_boolean(token): - return token == 'true' or token == 'false' + return token == "true" or token == "false" def __get_boolean(token): - return token == 'true' + return token == "true" def __is_int(token): @@ -352,12 +384,14 @@ def __get_int(token): def __is_long(token): - return token[0:-1].isdigit() and token[-1] == 'L' + return token[0:-1].isdigit() and token[-1] == "L" def __get_long(token): if six.PY2: - return long(token[0:-1]) # pylint: disable=incompatible-py3-code,undefined-variable + # pylint: disable=incompatible-py3-code,undefined-variable + return long(token[0:-1]) + # pylint: enable=incompatible-py3-code,undefined-variable else: return int(token[0:-1]) @@ -371,7 +405,7 @@ def __get_datatype(token): def __is_undefined(token): - return token == 'undefined' + return token == "undefined" def __is_quoted_string(token): @@ -380,13 +414,15 @@ def __is_quoted_string(token): def __get_quoted_string(token): result = token[1:-1] # remove quotes - result = result.replace('\\\\', '\\') # unescape the output, by default all the string are escaped in the output + result = result.replace( + "\\\\", "\\" + ) # unescape the output, by default all the string are escaped in the output return result def __is_assignment(token): - return token == '=>' + return token == "=>" def __is_expression(token): - return token == 'expression' + return token == "expression" diff --git a/salt/modules/jenkinsmod.py b/salt/modules/jenkinsmod.py index dfd4527d1d9..6ba74868541 100644 --- a/salt/modules/jenkinsmod.py +++ b/salt/modules/jenkinsmod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for controlling Jenkins :depends: python-jenkins @@ -21,92 +21,107 @@ Module for controlling Jenkins jenkins: api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.files + # Import Salt libs import salt.utils.stringutils +# Import 3rd-party libs +# pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.exceptions import CommandExecutionError, SaltInvocationError + try: import jenkins + HAS_JENKINS = True except ImportError: HAS_JENKINS = False -import salt.utils.files -# Import 3rd-party libs -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.exceptions import CommandExecutionError, SaltInvocationError # pylint: enable=import-error,no-name-in-module log = logging.getLogger(__name__) -__virtualname__ = 'jenkins' +__virtualname__ = "jenkins" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ if HAS_JENKINS: - if hasattr(jenkins, 'Jenkins'): + if hasattr(jenkins, "Jenkins"): return __virtualname__ else: - return (False, - 'The wrong Python module appears to be installed. Please ' - 'make sure that \'python-jenkins\' is installed, not ' - '\'jenkins\'.') - return (False, 'The jenkins execution module cannot be loaded: ' - 'python-jenkins is not installed.') + return ( + False, + "The wrong Python module appears to be installed. Please " + "make sure that 'python-jenkins' is installed, not " + "'jenkins'.", + ) + return ( + False, + "The jenkins execution module cannot be loaded: " + "python-jenkins is not installed.", + ) def _connect(): - ''' + """ Return server object used to interact with Jenkins. :return: server object used to interact with Jenkins - ''' - jenkins_url = __salt__['config.get']('jenkins.url') or \ - __salt__['config.get']('jenkins:url') or \ - __salt__['pillar.get']('jenkins.url') + """ + jenkins_url = ( + __salt__["config.get"]("jenkins.url") + or __salt__["config.get"]("jenkins:url") + or __salt__["pillar.get"]("jenkins.url") + ) - jenkins_user = __salt__['config.get']('jenkins.user') or \ - __salt__['config.get']('jenkins:user') or \ - __salt__['pillar.get']('jenkins.user') + jenkins_user = ( + __salt__["config.get"]("jenkins.user") + or __salt__["config.get"]("jenkins:user") + or __salt__["pillar.get"]("jenkins.user") + ) - jenkins_password = __salt__['config.get']('jenkins.password') or \ - __salt__['config.get']('jenkins:password') or \ - __salt__['pillar.get']('jenkins.password') + jenkins_password = ( + __salt__["config.get"]("jenkins.password") + or __salt__["config.get"]("jenkins:password") + or __salt__["pillar.get"]("jenkins.password") + ) if not jenkins_url: - raise SaltInvocationError('No Jenkins URL found.') + raise SaltInvocationError("No Jenkins URL found.") - return jenkins.Jenkins(jenkins_url, - username=jenkins_user, - password=jenkins_password) + return jenkins.Jenkins( + jenkins_url, username=jenkins_user, password=jenkins_password + ) def _retrieve_config_xml(config_xml, saltenv): - ''' + """ Helper to cache the config XML and raise a CommandExecutionError if we fail to do so. If we successfully cache the file, return the cached path. - ''' - ret = __salt__['cp.cache_file'](config_xml, saltenv) + """ + ret = __salt__["cp.cache_file"](config_xml, saltenv) if not ret: - raise CommandExecutionError('Failed to retrieve {0}'.format(config_xml)) + raise CommandExecutionError("Failed to retrieve {0}".format(config_xml)) return ret def run(script): - ''' + """ .. versionadded:: 2017.7.0 Execute a script on the jenkins master @@ -119,14 +134,14 @@ def run(script): salt '*' jenkins.run 'Jenkins.instance.doSafeRestart()' - ''' + """ server = _connect() return server.run_script(script) def get_version(): - ''' + """ Return version of Jenkins :return: The version of Jenkins @@ -136,7 +151,7 @@ def get_version(): .. code-block:: bash salt '*' jenkins.get_version - ''' + """ server = _connect() @@ -147,7 +162,7 @@ def get_version(): def get_jobs(): - ''' + """ Return the currently configured jobs. :return: The currently configured jobs. @@ -157,7 +172,7 @@ def get_jobs(): .. code-block:: bash salt '*' jenkins.get_jobs - ''' + """ server = _connect() @@ -168,7 +183,7 @@ def get_jobs(): def job_exists(name=None): - ''' + """ Check whether the job exists in configured Jenkins jobs. :param name: The name of the job is check if it exists. @@ -180,9 +195,9 @@ def job_exists(name=None): salt '*' jenkins.job_exists jobname - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") server = _connect() if server.job_exists(name): @@ -192,7 +207,7 @@ def job_exists(name=None): def get_job_info(name=None): - ''' + """ Return information about the Jenkins job. :param name: The name of the job is check if it exists. @@ -204,14 +219,14 @@ def get_job_info(name=None): salt '*' jenkins.get_job_info jobname - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") server = _connect() if not job_exists(name): - raise CommandExecutionError('Job \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("Job '{0}' does not exist".format(name)) job_info = server.get_job_info(name) if job_info: @@ -220,7 +235,7 @@ def get_job_info(name=None): def build_job(name=None, parameters=None): - ''' + """ Initiate a build for the provided job. :param name: The name of the job is check if it exists. @@ -233,28 +248,26 @@ def build_job(name=None, parameters=None): salt '*' jenkins.build_job jobname - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") server = _connect() if not job_exists(name): - raise CommandExecutionError('Job \'{0}\' does not exist.'.format(name)) + raise CommandExecutionError("Job '{0}' does not exist.".format(name)) try: server.build_job(name, parameters) except jenkins.JenkinsException as err: raise CommandExecutionError( - 'Encountered error building job \'{0}\': {1}'.format(name, err) + "Encountered error building job '{0}': {1}".format(name, err) ) return True -def create_job(name=None, - config_xml=None, - saltenv='base'): - ''' +def create_job(name=None, config_xml=None, saltenv="base"): + """ Return the configuration file. :param name: The name of the job is check if it exists. @@ -270,12 +283,12 @@ def create_job(name=None, salt '*' jenkins.create_job jobname config_xml='salt://jenkins/config.xml' - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") if job_exists(name): - raise CommandExecutionError('Job \'{0}\' already exists'.format(name)) + raise CommandExecutionError("Job '{0}' already exists".format(name)) if not config_xml: config_xml = jenkins.EMPTY_CONFIG_XML @@ -290,15 +303,13 @@ def create_job(name=None, server.create_job(name, config_xml) except jenkins.JenkinsException as err: raise CommandExecutionError( - 'Encountered error creating job \'{0}\': {1}'.format(name, err) + "Encountered error creating job '{0}': {1}".format(name, err) ) return config_xml -def update_job(name=None, - config_xml=None, - saltenv='base'): - ''' +def update_job(name=None, config_xml=None, saltenv="base"): + """ Return the updated configuration file. :param name: The name of the job is check if it exists. @@ -314,9 +325,9 @@ def update_job(name=None, salt '*' jenkins.update_job jobname config_xml='salt://jenkins/config.xml' - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") if not config_xml: config_xml = jenkins.EMPTY_CONFIG_XML @@ -331,13 +342,13 @@ def update_job(name=None, server.reconfig_job(name, config_xml) except jenkins.JenkinsException as err: raise CommandExecutionError( - 'Encountered error updating job \'{0}\': {1}'.format(name, err) + "Encountered error updating job '{0}': {1}".format(name, err) ) return config_xml def delete_job(name=None): - ''' + """ Return true is job is deleted successfully. :param name: The name of the job to delete. @@ -349,26 +360,26 @@ def delete_job(name=None): salt '*' jenkins.delete_job jobname - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") server = _connect() if not job_exists(name): - raise CommandExecutionError('Job \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("Job '{0}' does not exist".format(name)) try: server.delete_job(name) except jenkins.JenkinsException as err: raise CommandExecutionError( - 'Encountered error deleting job \'{0}\': {1}'.format(name, err) + "Encountered error deleting job '{0}': {1}".format(name, err) ) return True def enable_job(name=None): - ''' + """ Return true is job is enabled successfully. :param name: The name of the job to enable. @@ -380,26 +391,26 @@ def enable_job(name=None): salt '*' jenkins.enable_job jobname - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") server = _connect() if not job_exists(name): - raise CommandExecutionError('Job \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("Job '{0}' does not exist".format(name)) try: server.enable_job(name) except jenkins.JenkinsException as err: raise CommandExecutionError( - 'Encountered error enabling job \'{0}\': {1}'.format(name, err) + "Encountered error enabling job '{0}': {1}".format(name, err) ) return True def disable_job(name=None): - ''' + """ Return true is job is disabled successfully. :param name: The name of the job to disable. @@ -411,27 +422,27 @@ def disable_job(name=None): salt '*' jenkins.disable_job jobname - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") server = _connect() if not job_exists(name): - raise CommandExecutionError('Job \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("Job '{0}' does not exist".format(name)) try: server.disable_job(name) except jenkins.JenkinsException as err: raise CommandExecutionError( - 'Encountered error disabling job \'{0}\': {1}'.format(name, err) + "Encountered error disabling job '{0}': {1}".format(name, err) ) return True def job_status(name=None): - ''' + """ Return the current status, enabled or disabled, of the job. :param name: The name of the job to return status for @@ -443,21 +454,21 @@ def job_status(name=None): salt '*' jenkins.job_status jobname - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") server = _connect() if not job_exists(name): - raise CommandExecutionError('Job \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("Job '{0}' does not exist".format(name)) - return server.get_job_info('empty')['buildable'] + return server.get_job_info("empty")["buildable"] def get_job_config(name=None): - ''' + """ Return the current job configuration for the provided job. :param name: The name of the job to return the configuration for. @@ -469,22 +480,22 @@ def get_job_config(name=None): salt '*' jenkins.get_job_config jobname - ''' + """ if not name: - raise SaltInvocationError('Required parameter \'name\' is missing') + raise SaltInvocationError("Required parameter 'name' is missing") server = _connect() if not job_exists(name): - raise CommandExecutionError('Job \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("Job '{0}' does not exist".format(name)) job_info = server.get_job_config(name) return job_info def plugin_installed(name): - ''' + """ .. versionadded:: 2016.11.0 Return if the plugin is installed for the provided plugin name. @@ -498,7 +509,7 @@ def plugin_installed(name): salt '*' jenkins.plugin_installed pluginName - ''' + """ server = _connect() plugins = server.get_plugins() diff --git a/salt/modules/jinja.py b/salt/modules/jinja.py index 2e8280689dd..cc7e5aa7c94 100644 --- a/salt/modules/jinja.py +++ b/salt/modules/jinja.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Module for checking jinja maps and verifying the result of loading JSON/YAML files .. versionadded:: 3000 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -21,18 +21,20 @@ log = logging.getLogger(__name__) def _strip_odict(wrapped): - ''' + """ dump to json and load it again, replaces OrderedDicts with regular ones - ''' + """ + @functools.wraps(wrapped) def strip(*args): return salt.utils.json.loads(salt.utils.json.dumps(wrapped(*args))) + return strip @_strip_odict def load_map(path, value): - ''' + """ Loads the map at the specified path, and returns the specified value from that map. @@ -47,22 +49,27 @@ def load_map(path, value): # the following syntax can be used to load the map and check the # results: salt myminion jinja.load_map myformula/map.jinja myformula - ''' - tmplstr = textwrap.dedent('''\ + """ + tmplstr = textwrap.dedent( + """\ {{% from "{path}" import {value} with context %}} {{{{ {value} | tojson }}}} - '''.format(path=path, value=value)) + """.format( + path=path, value=value + ) + ) return salt.template.compile_template_str( tmplstr, salt.loader.render(__opts__, __salt__), - __opts__['renderer'], - __opts__['renderer_blacklist'], - __opts__['renderer_whitelist']) + __opts__["renderer"], + __opts__["renderer_blacklist"], + __opts__["renderer_whitelist"], + ) @_strip_odict def import_yaml(path): - ''' + """ Loads YAML data from the specified path CLI Example: @@ -70,22 +77,27 @@ def import_yaml(path): .. code-block:: bash salt myminion jinja.import_yaml myformula/foo.yaml - ''' - tmplstr = textwrap.dedent('''\ + """ + tmplstr = textwrap.dedent( + """\ {{% import_yaml "{path}" as imported %}} {{{{ imported | tojson }}}} - '''.format(path=path)) + """.format( + path=path + ) + ) return salt.template.compile_template_str( tmplstr, salt.loader.render(__opts__, __salt__), - __opts__['renderer'], - __opts__['renderer_blacklist'], - __opts__['renderer_whitelist']) + __opts__["renderer"], + __opts__["renderer_blacklist"], + __opts__["renderer_whitelist"], + ) @_strip_odict def import_json(path): - ''' + """ Loads JSON data from the specified path CLI Example: @@ -93,14 +105,19 @@ def import_json(path): .. code-block:: bash salt myminion jinja.import_JSON myformula/foo.json - ''' - tmplstr = textwrap.dedent('''\ + """ + tmplstr = textwrap.dedent( + """\ {{% import_json "{path}" as imported %}} {{{{ imported | tojson }}}} - '''.format(path=path)) + """.format( + path=path + ) + ) return salt.template.compile_template_str( tmplstr, salt.loader.render(__opts__, __salt__), - __opts__['renderer'], - __opts__['renderer_blacklist'], - __opts__['renderer_whitelist']) + __opts__["renderer"], + __opts__["renderer_blacklist"], + __opts__["renderer_whitelist"], + ) diff --git a/salt/modules/jira_mod.py b/salt/modules/jira_mod.py index e13763bab17..50cd6c9cea9 100644 --- a/salt/modules/jira_mod.py +++ b/salt/modules/jira_mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" JIRA Execution module ===================== @@ -17,79 +17,84 @@ Configuration example: server: https://jira.atlassian.org username: salt password: pass -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -log = logging.getLogger(__name__) # Import salt modules from salt.utils.args import clean_kwargs +log = logging.getLogger(__name__) + + # Import third party modules try: import jira + HAS_JIRA = True except ImportError: HAS_JIRA = False -__virtualname__ = 'jira' -__proxyenabled__ = ['*'] +__virtualname__ = "jira" +__proxyenabled__ = ["*"] JIRA = None def __virtual__(): - return __virtualname__ if HAS_JIRA else (False, 'Please install the jira Python libary from PyPI') + return ( + __virtualname__ + if HAS_JIRA + else (False, "Please install the jira Python libary from PyPI") + ) -def _get_credentials(server=None, - username=None, - password=None): - ''' +def _get_credentials(server=None, username=None, password=None): + """ Returns the credentials merged with the config data (opts + pillar). - ''' - jira_cfg = __salt__['config.merge']('jira', default={}) + """ + jira_cfg = __salt__["config.merge"]("jira", default={}) if not server: - server = jira_cfg.get('server') + server = jira_cfg.get("server") if not username: - username = jira_cfg.get('username') + username = jira_cfg.get("username") if not password: - password = jira_cfg.get('password') + password = jira_cfg.get("password") return server, username, password -def _get_jira(server=None, - username=None, - password=None): +def _get_jira(server=None, username=None, password=None): global JIRA if not JIRA: - server, username, password = _get_credentials(server=server, - username=username, - password=password) - JIRA = jira.JIRA(basic_auth=(username, password), - server=server, - logging=True) # We want logging + server, username, password = _get_credentials( + server=server, username=username, password=password + ) + JIRA = jira.JIRA( + basic_auth=(username, password), server=server, logging=True + ) # We want logging return JIRA -def create_issue(project, - summary, - description, - template_engine='jinja', - context=None, - defaults=None, - saltenv='base', - issuetype='Bug', - priority='Normal', - labels=None, - assignee=None, - server=None, - username=None, - password=None, - **kwargs): - ''' +def create_issue( + project, + summary, + description, + template_engine="jinja", + context=None, + defaults=None, + saltenv="base", + issuetype="Bug", + priority="Normal", + labels=None, + assignee=None, + server=None, + username=None, + password=None, + **kwargs +): + """ Create a JIRA issue using the named settings. Return the JIRA ticket ID. project @@ -141,36 +146,32 @@ def create_issue(project, salt '*' jira.create_issue NET 'Ticket title' 'Ticket description' salt '*' jira.create_issue NET 'Issue on {{ opts.id }}' 'Error detected on {{ opts.id }}' template_engine=jinja - ''' + """ if template_engine: - summary = __salt__['file.apply_template_on_contents'](summary, - template=template_engine, - context=context, - defaults=defaults, - saltenv=saltenv) - description = __salt__['file.apply_template_on_contents'](description, - template=template_engine, - context=context, - defaults=defaults, - saltenv=saltenv) - jira_ = _get_jira(server=server, - username=username, - password=password) + summary = __salt__["file.apply_template_on_contents"]( + summary, + template=template_engine, + context=context, + defaults=defaults, + saltenv=saltenv, + ) + description = __salt__["file.apply_template_on_contents"]( + description, + template=template_engine, + context=context, + defaults=defaults, + saltenv=saltenv, + ) + jira_ = _get_jira(server=server, username=username, password=password) if not labels: labels = [] data = { - 'project': { - 'key': project - }, - 'summary': summary, - 'description': description, - 'issuetype': { - 'name': issuetype - }, - 'priority': { - 'name': priority - }, - 'labels': labels + "project": {"key": project}, + "summary": summary, + "description": description, + "issuetype": {"name": issuetype}, + "priority": {"name": priority}, + "labels": labels, } data.update(clean_kwargs(**kwargs)) issue = jira_.create_issue(data) @@ -180,12 +181,8 @@ def create_issue(project, return issue_key -def assign_issue(issue_key, - assignee, - server=None, - username=None, - password=None): - ''' +def assign_issue(issue_key, assignee, server=None, username=None, password=None): + """ Assign the issue to an existing user. Return ``True`` when the issue has been properly assigned. @@ -198,22 +195,22 @@ def assign_issue(issue_key, CLI Example: salt '*' jira.assign_issue NET-123 example_user - ''' - jira_ = _get_jira(server=server, - username=username, - password=password) + """ + jira_ = _get_jira(server=server, username=username, password=password) assigned = jira_.assign_issue(issue_key, assignee) return assigned -def add_comment(issue_key, - comment, - visibility=None, - is_internal=False, - server=None, - username=None, - password=None): - ''' +def add_comment( + issue_key, + comment, + visibility=None, + is_internal=False, + server=None, + username=None, + password=None, +): + """ Add a comment to an existing ticket. Return ``True`` when it successfully added the comment. @@ -239,22 +236,16 @@ def add_comment(issue_key, .. code-block:: bash salt '*' jira.add_comment NE-123 'This is a comment' - ''' - jira_ = _get_jira(server=server, - username=username, - password=password) - comm = jira_.add_comment(issue_key, - comment, - visibility=visibility, - is_internal=is_internal) + """ + jira_ = _get_jira(server=server, username=username, password=password) + comm = jira_.add_comment( + issue_key, comment, visibility=visibility, is_internal=is_internal + ) return True -def issue_closed(issue_key, - server=None, - username=None, - password=None): - ''' +def issue_closed(issue_key, server=None, username=None, password=None): + """ Check if the issue is closed. issue_key @@ -271,15 +262,13 @@ def issue_closed(issue_key, .. code-block:: bash salt '*' jira.issue_closed NE-123 - ''' + """ if not issue_key: return None - jira_ = _get_jira(server=server, - username=username, - password=password) + jira_ = _get_jira(server=server, username=username, password=password) try: ticket = jira_.issue(issue_key) except jira.exceptions.JIRAError: # Ticket not found return None - return ticket.fields().status.name == 'Closed' + return ticket.fields().status.name == "Closed" diff --git a/salt/modules/junos.py b/salt/modules/junos.py index 7b68437cd86..36c100878c3 100644 --- a/salt/modules/junos.py +++ b/salt/modules/junos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to interact with Junos devices. :maturity: new @@ -12,19 +12,15 @@ Module to interact with Junos devices. Refer to :mod:`junos ` for information on connecting to junos proxy. -''' +""" # Import Python libraries from __future__ import absolute_import, print_function, unicode_literals + import logging import os from functools import wraps -try: - from lxml import etree -except ImportError: - from salt._compat import ElementTree as etree - # Import Salt libs import salt.utils.args import salt.utils.files @@ -32,6 +28,12 @@ import salt.utils.json import salt.utils.stringutils from salt.ext import six +try: + from lxml import etree +except ImportError: + from salt._compat import ElementTree as etree + + # Juniper interface libraries # https://github.com/Juniper/py-junos-eznc try: @@ -43,6 +45,7 @@ try: import jnpr.junos.utils import jnpr.junos.cfg import jxmlease + # pylint: enable=W0611 HAS_JUNOS = True except ImportError: @@ -52,31 +55,34 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'junos' +__virtualname__ = "junos" -__proxyenabled__ = ['junos'] +__proxyenabled__ = ["junos"] def __virtual__(): - ''' + """ We need the Junos adapter libraries for this module to work. We also need a proxymodule entry in __opts__ in the opts dictionary - ''' - if HAS_JUNOS and 'proxy' in __opts__: + """ + if HAS_JUNOS and "proxy" in __opts__: return __virtualname__ else: - return (False, 'The junos module could not be loaded: ' - 'junos-eznc or jxmlease or proxy could not be loaded.') + return ( + False, + "The junos module could not be loaded: " + "junos-eznc or jxmlease or proxy could not be loaded.", + ) def timeoutDecorator(function): @wraps(function) def wrapper(*args, **kwargs): - if 'dev_timeout' in kwargs: - conn = __proxy__['junos.conn']() + if "dev_timeout" in kwargs: + conn = __proxy__["junos.conn"]() restore_timeout = conn.timeout - conn.timeout = kwargs.pop('dev_timeout', None) + conn.timeout = kwargs.pop("dev_timeout", None) try: result = function(*args, **kwargs) conn.timeout = restore_timeout @@ -91,7 +97,7 @@ def timeoutDecorator(function): def facts_refresh(): - ''' + """ Reload the facts dictionary from the device. Usually only needed if, the device configuration is changed by some other actor. This function will also refresh the facts stored in the salt grains. @@ -101,28 +107,28 @@ def facts_refresh(): .. code-block:: bash salt 'device_name' junos.facts_refresh - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True try: conn.facts_refresh() except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Execution failed due to "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Execution failed due to "{0}"'.format(exception) + ret["out"] = False return ret - ret['facts'] = __proxy__['junos.get_serialized_facts']() + ret["facts"] = __proxy__["junos.get_serialized_facts"]() try: - __salt__['saltutil.sync_grains']() + __salt__["saltutil.sync_grains"]() except Exception as exception: # pylint: disable=broad-except log.error('Grains could not be updated due to "%s"', exception) return ret def facts(): - ''' + """ Displays the facts gathered during the connection. These facts are also stored in Salt grains. @@ -131,21 +137,20 @@ def facts(): .. code-block:: bash salt 'device_name' junos.facts - ''' + """ ret = {} try: - ret['facts'] = __proxy__['junos.get_serialized_facts']() - ret['out'] = True + ret["facts"] = __proxy__["junos.get_serialized_facts"]() + ret["out"] = True except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not display facts due to "{0}"'.format( - exception) - ret['out'] = False + ret["message"] = 'Could not display facts due to "{0}"'.format(exception) + ret["out"] = False return ret @timeoutDecorator def rpc(cmd=None, dest=None, **kwargs): - ''' + """ This function executes the RPC provided as arguments on the junos device. The returned data can be stored in a file. @@ -179,92 +184,81 @@ def rpc(cmd=None, dest=None, **kwargs): salt 'device' junos.rpc get_config /var/log/config.txt format=text filter='' salt 'device' junos.rpc get-interface-information /home/user/interface.xml interface_name='lo0' terse=True salt 'device' junos.rpc get-chassis-inventory - ''' + """ - conn = __proxy__['junos.conn']() + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True if cmd is None: - ret['message'] = 'Please provide the rpc to execute.' - ret['out'] = False + ret["message"] = "Please provide the rpc to execute." + ret["out"] = False return ret - format_ = kwargs.pop('format', 'xml') + format_ = kwargs.pop("format", "xml") if not format_: - format_ = 'xml' + format_ = "xml" op = dict() - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) - elif '__pub_schedule' in kwargs: + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) + elif "__pub_schedule" in kwargs: for key, value in six.iteritems(kwargs): - if not key.startswith('__pub_'): + if not key.startswith("__pub_"): op[key] = value else: op.update(kwargs) - if cmd in ['get-config', 'get_config']: + if cmd in ["get-config", "get_config"]: filter_reply = None - if 'filter' in op: - filter_reply = etree.XML(op['filter']) - del op['filter'] + if "filter" in op: + filter_reply = etree.XML(op["filter"]) + del op["filter"] - op.update({'format': format_}) + op.update({"format": format_}) try: - reply = getattr( - conn.rpc, - cmd.replace('-', - '_'))(filter_reply, - options=op) + reply = getattr(conn.rpc, cmd.replace("-", "_"))(filter_reply, options=op) except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'RPC execution failed due to "{0}"'.format( - exception) - ret['out'] = False + ret["message"] = 'RPC execution failed due to "{0}"'.format(exception) + ret["out"] = False return ret else: - if 'filter' in op: - log.warning( - 'Filter ignored as it is only used with "get-config" rpc') + if "filter" in op: + log.warning('Filter ignored as it is only used with "get-config" rpc') try: - reply = getattr( - conn.rpc, - cmd.replace('-', - '_'))({'format': format_}, - **op) + reply = getattr(conn.rpc, cmd.replace("-", "_"))({"format": format_}, **op) except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'RPC execution failed due to "{0}"'.format( - exception) - ret['out'] = False + ret["message"] = 'RPC execution failed due to "{0}"'.format(exception) + ret["out"] = False return ret - if format_ == 'text': + if format_ == "text": # Earlier it was ret['message'] - ret['rpc_reply'] = reply.text - elif format_ == 'json': + ret["rpc_reply"] = reply.text + elif format_ == "json": # Earlier it was ret['message'] - ret['rpc_reply'] = reply + ret["rpc_reply"] = reply else: # Earlier it was ret['message'] - ret['rpc_reply'] = jxmlease.parse(etree.tostring(reply)) + ret["rpc_reply"] = jxmlease.parse(etree.tostring(reply)) if dest: - if format_ == 'text': + if format_ == "text": write_response = reply.text - elif format_ == 'json': + elif format_ == "json": write_response = salt.utils.json.dumps(reply, indent=1) else: write_response = etree.tostring(reply) - with salt.utils.files.fopen(dest, 'w') as fp: + with salt.utils.files.fopen(dest, "w") as fp: fp.write(salt.utils.stringutils.to_str(write_response)) return ret @timeoutDecorator def set_hostname(hostname=None, **kwargs): - ''' + """ Set the device's hostname hostname @@ -286,62 +280,64 @@ def set_hostname(hostname=None, **kwargs): .. code-block:: bash salt 'device_name' junos.set_hostname salt-device - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} if hostname is None: - ret['message'] = 'Please provide the hostname.' - ret['out'] = False + ret["message"] = "Please provide the hostname." + ret["out"] = False return ret op = dict() - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) else: op.update(kwargs) # Added to recent versions of JunOs # Use text format instead - set_string = 'set system host-name {0}'.format(hostname) + set_string = "set system host-name {0}".format(hostname) try: - conn.cu.load(set_string, format='set') + conn.cu.load(set_string, format="set") except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not load configuration due to error "{0}"'.format( - exception) - ret['out'] = False + ret["message"] = 'Could not load configuration due to error "{0}"'.format( + exception + ) + ret["out"] = False return ret try: commit_ok = conn.cu.commit_check() except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not commit check due to error "{0}"'.format( - exception) - ret['out'] = False + ret["message"] = 'Could not commit check due to error "{0}"'.format(exception) + ret["out"] = False return ret if commit_ok: try: conn.cu.commit(**op) - ret['message'] = 'Successfully changed hostname.' - ret['out'] = True + ret["message"] = "Successfully changed hostname." + ret["out"] = True except Exception as exception: # pylint: disable=broad-except - ret['out'] = False - ret['message'] = 'Successfully loaded host-name but commit failed with "{0}"'.format( - exception) + ret["out"] = False + ret[ + "message" + ] = 'Successfully loaded host-name but commit failed with "{0}"'.format( + exception + ) return ret else: - ret['out'] = False - ret[ - 'message'] = 'Successfully loaded host-name but pre-commit check failed.' + ret["out"] = False + ret["message"] = "Successfully loaded host-name but pre-commit check failed." conn.cu.rollback() return ret @timeoutDecorator def commit(**kwargs): - ''' + """ To commit the changes loaded in the candidate configuration. dev_timeout : 30 @@ -379,55 +375,56 @@ def commit(**kwargs): salt 'device_name' junos.commit comment='Commiting via saltstack' detail=True salt 'device_name' junos.commit dev_timeout=60 confirm=10 salt 'device_name' junos.commit sync=True dev_timeout=90 - ''' + """ - conn = __proxy__['junos.conn']() + conn = __proxy__["junos.conn"]() ret = {} op = dict() - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) else: op.update(kwargs) - op['detail'] = op.get('detail', False) + op["detail"] = op.get("detail", False) try: commit_ok = conn.cu.commit_check() except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not perform commit check due to "{0}"'.format( - exception) - ret['out'] = False + ret["message"] = 'Could not perform commit check due to "{0}"'.format(exception) + ret["out"] = False return ret if commit_ok: try: commit = conn.cu.commit(**op) - ret['out'] = True + ret["out"] = True if commit: - if op['detail']: - ret['message'] = jxmlease.parse(etree.tostring(commit)) + if op["detail"]: + ret["message"] = jxmlease.parse(etree.tostring(commit)) else: - ret['message'] = 'Commit Successful.' + ret["message"] = "Commit Successful." else: - ret['message'] = 'Commit failed.' - ret['out'] = False + ret["message"] = "Commit failed." + ret["out"] = False except Exception as exception: # pylint: disable=broad-except - ret['out'] = False - ret['message'] = \ - 'Commit check succeeded but actual commit failed with "{0}"' \ - .format(exception) + ret["out"] = False + ret[ + "message" + ] = 'Commit check succeeded but actual commit failed with "{0}"'.format( + exception + ) else: - ret['out'] = False - ret['message'] = 'Pre-commit check failed.' + ret["out"] = False + ret["message"] = "Pre-commit check failed." conn.cu.rollback() return ret @timeoutDecorator def rollback(**kwargs): - ''' + """ Roll back the last committed configuration changes and commit id : 0 @@ -455,69 +452,71 @@ def rollback(**kwargs): .. code-block:: bash salt 'device_name' junos.rollback 10 - ''' - id_ = kwargs.pop('id', 0) + """ + id_ = kwargs.pop("id", 0) ret = {} - conn = __proxy__['junos.conn']() + conn = __proxy__["junos.conn"]() op = dict() - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) else: op.update(kwargs) try: - ret['out'] = conn.cu.rollback(id_) + ret["out"] = conn.cu.rollback(id_) except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Rollback failed due to "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Rollback failed due to "{0}"'.format(exception) + ret["out"] = False return ret - if ret['out']: - ret['message'] = 'Rollback successful' + if ret["out"]: + ret["message"] = "Rollback successful" else: - ret['message'] = 'Rollback failed' + ret["message"] = "Rollback failed" return ret - if 'diffs_file' in op and op['diffs_file'] is not None: + if "diffs_file" in op and op["diffs_file"] is not None: diff = conn.cu.diff() if diff is not None: - with salt.utils.files.fopen(op['diffs_file'], 'w') as fp: + with salt.utils.files.fopen(op["diffs_file"], "w") as fp: fp.write(salt.utils.stringutils.to_str(diff)) else: log.info( - 'No diff between current configuration and \ - rollbacked configuration, so no diff file created') + "No diff between current configuration and \ + rollbacked configuration, so no diff file created" + ) try: commit_ok = conn.cu.commit_check() except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not commit check due to "{0}"'.format( - exception) - ret['out'] = False + ret["message"] = 'Could not commit check due to "{0}"'.format(exception) + ret["out"] = False return ret if commit_ok: try: conn.cu.commit(**op) - ret['out'] = True + ret["out"] = True except Exception as exception: # pylint: disable=broad-except - ret['out'] = False - ret['message'] = \ - 'Rollback successful but commit failed with error "{0}"'\ - .format(exception) + ret["out"] = False + ret[ + "message" + ] = 'Rollback successful but commit failed with error "{0}"'.format( + exception + ) return ret else: - ret['message'] = 'Rollback succesfull but pre-commit check failed.' - ret['out'] = False + ret["message"] = "Rollback succesfull but pre-commit check failed." + ret["out"] = False return ret def diff(**kwargs): - ''' + """ Returns the difference between the candidate and the current configuration id : 0 @@ -528,28 +527,27 @@ def diff(**kwargs): .. code-block:: bash salt 'device_name' junos.diff 3 - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - id_ = kwargs.pop('id', 0) + id_ = kwargs.pop("id", 0) if kwargs: salt.utils.args.invalid_kwargs(kwargs) - conn = __proxy__['junos.conn']() + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True try: - ret['message'] = conn.cu.diff(rb_id=id_) + ret["message"] = conn.cu.diff(rb_id=id_) except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not get diff with error "{0}"'.format( - exception) - ret['out'] = False + ret["message"] = 'Could not get diff with error "{0}"'.format(exception) + ret["out"] = False return ret @timeoutDecorator def ping(dest_ip=None, **kwargs): - ''' + """ Send a ping RPC to a device dest_ip @@ -580,39 +578,39 @@ def ping(dest_ip=None, **kwargs): salt 'device_name' junos.ping '8.8.8.8' count=5 salt 'device_name' junos.ping '8.8.8.8' ttl=1 rapid=True - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} if dest_ip is None: - ret['message'] = 'Please specify the destination ip to ping.' - ret['out'] = False + ret["message"] = "Please specify the destination ip to ping." + ret["out"] = False return ret - op = {'host': dest_ip} - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) + op = {"host": dest_ip} + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) else: op.update(kwargs) - op['count'] = six.text_type(op.pop('count', 5)) - if 'ttl' in op: - op['ttl'] = six.text_type(op['ttl']) + op["count"] = six.text_type(op.pop("count", 5)) + if "ttl" in op: + op["ttl"] = six.text_type(op["ttl"]) - ret['out'] = True + ret["out"] = True try: - ret['message'] = jxmlease.parse(etree.tostring(conn.rpc.ping(**op))) + ret["message"] = jxmlease.parse(etree.tostring(conn.rpc.ping(**op))) except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Execution failed due to "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Execution failed due to "{0}"'.format(exception) + ret["out"] = False return ret @timeoutDecorator def cli(command=None, **kwargs): - ''' + """ Executes the CLI commands and returns the output in specified format. \ (default is text) The output can also be stored in a file. @@ -637,50 +635,50 @@ def cli(command=None, **kwargs): salt 'device_name' junos.cli 'show system commit' salt 'device_name' junos.cli 'show version' dev_timeout=40 salt 'device_name' junos.cli 'show system alarms' format=xml dest=/home/user/cli_output.txt - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() - format_ = kwargs.pop('format', 'text') + format_ = kwargs.pop("format", "text") if not format_: - format_ = 'text' + format_ = "text" ret = {} if command is None: - ret['message'] = 'Please provide the CLI command to be executed.' - ret['out'] = False + ret["message"] = "Please provide the CLI command to be executed." + ret["out"] = False return ret op = dict() - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) else: op.update(kwargs) try: result = conn.cli(command, format_, warning=False) except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Execution failed due to "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Execution failed due to "{0}"'.format(exception) + ret["out"] = False return ret - if format_ == 'text': - ret['message'] = result + if format_ == "text": + ret["message"] = result else: result = etree.tostring(result) - ret['message'] = jxmlease.parse(result) + ret["message"] = jxmlease.parse(result) - if 'dest' in op and op['dest'] is not None: - with salt.utils.files.fopen(op['dest'], 'w') as fp: + if "dest" in op and op["dest"] is not None: + with salt.utils.files.fopen(op["dest"], "w") as fp: fp.write(salt.utils.stringutils.to_str(result)) - ret['out'] = True + ret["out"] = True return ret def shutdown(**kwargs): - ''' + """ Shut down (power off) or reboot a device running Junos OS. This includes all Routing Engines in a Virtual Chassis or a dual Routing Engine system. @@ -711,52 +709,50 @@ def shutdown(**kwargs): salt 'device_name' junos.shutdown reboot=True salt 'device_name' junos.shutdown shutdown=True in_min=10 salt 'device_name' junos.shutdown shutdown=True - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} sw = SW(conn) op = {} - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) else: op.update(kwargs) - if 'shutdown' not in op and 'reboot' not in op: - ret['message'] = \ - 'Provide either one of the arguments: shutdown or reboot.' - ret['out'] = False + if "shutdown" not in op and "reboot" not in op: + ret["message"] = "Provide either one of the arguments: shutdown or reboot." + ret["out"] = False return ret try: - if 'reboot' in op and op['reboot']: + if "reboot" in op and op["reboot"]: shut = sw.reboot - elif 'shutdown' in op and op['shutdown']: + elif "shutdown" in op and op["shutdown"]: shut = sw.poweroff else: - ret['message'] = 'Nothing to be done.' - ret['out'] = False + ret["message"] = "Nothing to be done." + ret["out"] = False return ret - if 'in_min' in op: - shut(in_min=op['in_min']) - elif 'at' in op: - shut(at=op['at']) + if "in_min" in op: + shut(in_min=op["in_min"]) + elif "at" in op: + shut(at=op["at"]) else: shut() - ret['message'] = 'Successfully powered off/rebooted.' - ret['out'] = True + ret["message"] = "Successfully powered off/rebooted." + ret["out"] = True except Exception as exception: # pylint: disable=broad-except - ret['message'] = \ - 'Could not poweroff/reboot beacause "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Could not poweroff/reboot beacause "{0}"'.format(exception) + ret["out"] = False return ret @timeoutDecorator def install_config(path=None, **kwargs): - ''' + """ Installs the given configuration file into the candidate configuration. Commits the changes if the commit checks or throws an error. @@ -827,82 +823,81 @@ def install_config(path=None, **kwargs): salt 'device_name' junos.install_config 'salt://templates/replace_config.conf' replace=True comment='Committed via SaltStack' salt 'device_name' junos.install_config 'salt://my_new_configuration.conf' dev_timeout=300 diffs_file='/salt/confs/old_config.conf' overwrite=True salt 'device_name' junos.install_config 'salt://syslog_template.conf' template_vars='{"syslog_host": "10.180.222.7"}' - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True if path is None: - ret['message'] = \ - 'Please provide the salt path where the configuration is present' - ret['out'] = False + ret[ + "message" + ] = "Please provide the salt path where the configuration is present" + ret["out"] = False return ret op = {} - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) else: op.update(kwargs) - test = op.pop('test', False) + test = op.pop("test", False) template_vars = {} if "template_vars" in op: template_vars = op["template_vars"] template_cached_path = salt.utils.files.mkstemp() - __salt__['cp.get_template']( - path, - template_cached_path, - template_vars=template_vars) + __salt__["cp.get_template"](path, template_cached_path, template_vars=template_vars) if not os.path.isfile(template_cached_path): - ret['message'] = 'Invalid file path.' - ret['out'] = False + ret["message"] = "Invalid file path." + ret["out"] = False return ret if os.path.getsize(template_cached_path) == 0: - ret['message'] = 'Template failed to render' - ret['out'] = False + ret["message"] = "Template failed to render" + ret["out"] = False return ret - write_diff = '' - if 'diffs_file' in op and op['diffs_file'] is not None: - write_diff = op['diffs_file'] - del op['diffs_file'] + write_diff = "" + if "diffs_file" in op and op["diffs_file"] is not None: + write_diff = op["diffs_file"] + del op["diffs_file"] - op['path'] = template_cached_path + op["path"] = template_cached_path - if 'format' not in op: - if path.endswith('set'): - template_format = 'set' - elif path.endswith('xml'): - template_format = 'xml' + if "format" not in op: + if path.endswith("set"): + template_format = "set" + elif path.endswith("xml"): + template_format = "xml" else: - template_format = 'text' + template_format = "text" - op['format'] = template_format + op["format"] = template_format - if 'replace' in op and op['replace']: - op['merge'] = False - del op['replace'] - elif 'overwrite' in op and op['overwrite']: - op['overwrite'] = True - elif 'overwrite' in op and not op['overwrite']: - op['merge'] = True - del op['overwrite'] + if "replace" in op and op["replace"]: + op["merge"] = False + del op["replace"] + elif "overwrite" in op and op["overwrite"]: + op["overwrite"] = True + elif "overwrite" in op and not op["overwrite"]: + op["merge"] = True + del op["overwrite"] - db_mode = op.pop('mode', 'exclusive') + db_mode = op.pop("mode", "exclusive") with Config(conn, mode=db_mode) as cu: try: cu.load(**op) except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not load configuration due to : "{0}"'.format( - exception) - ret['format'] = op['format'] - ret['out'] = False + ret["message"] = 'Could not load configuration due to : "{0}"'.format( + exception + ) + ret["format"] = op["format"] + ret["out"] = False return ret finally: @@ -910,59 +905,66 @@ def install_config(path=None, **kwargs): config_diff = cu.diff() if config_diff is None: - ret['message'] = 'Configuration already applied!' - ret['out'] = True + ret["message"] = "Configuration already applied!" + ret["out"] = True return ret commit_params = {} - if 'confirm' in op: - commit_params['confirm'] = op['confirm'] - if 'comment' in op: - commit_params['comment'] = op['comment'] + if "confirm" in op: + commit_params["confirm"] = op["confirm"] + if "comment" in op: + commit_params["comment"] = op["comment"] try: check = cu.commit_check() except Exception as exception: # pylint: disable=broad-except - ret['message'] = \ - 'Commit check threw the following exception: "{0}"'\ - .format(exception) + ret["message"] = 'Commit check threw the following exception: "{0}"'.format( + exception + ) - ret['out'] = False + ret["out"] = False return ret if check and not test: try: cu.commit(**commit_params) - ret['message'] = 'Successfully loaded and committed!' + ret["message"] = "Successfully loaded and committed!" except Exception as exception: # pylint: disable=broad-except - ret['message'] = \ - 'Commit check successful but commit failed with "{0}"'\ - .format(exception) - ret['out'] = False + ret[ + "message" + ] = 'Commit check successful but commit failed with "{0}"'.format( + exception + ) + ret["out"] = False return ret elif not check: cu.rollback() - ret['message'] = 'Loaded configuration but commit check failed, hence rolling back configuration.' - ret['out'] = False + ret[ + "message" + ] = "Loaded configuration but commit check failed, hence rolling back configuration." + ret["out"] = False else: cu.rollback() - ret['message'] = 'Commit check passed, but skipping commit for dry-run and rolling back configuration.' - ret['out'] = True + ret[ + "message" + ] = "Commit check passed, but skipping commit for dry-run and rolling back configuration." + ret["out"] = True try: if write_diff and config_diff is not None: - with salt.utils.files.fopen(write_diff, 'w') as fp: + with salt.utils.files.fopen(write_diff, "w") as fp: fp.write(salt.utils.stringutils.to_str(config_diff)) except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not write into diffs_file due to: "{0}"'.format( - exception) - ret['out'] = False + ret["message"] = 'Could not write into diffs_file due to: "{0}"'.format( + exception + ) + ret["out"] = False return ret def zeroize(): - ''' + """ Resets the device to default factory settings CLI Example: @@ -970,23 +972,23 @@ def zeroize(): .. code-block:: bash salt 'device_name' junos.zeroize - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True try: - conn.cli('request system zeroize') - ret['message'] = 'Completed zeroize and rebooted' + conn.cli("request system zeroize") + ret["message"] = "Completed zeroize and rebooted" except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not zeroize due to : "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Could not zeroize due to : "{0}"'.format(exception) + ret["out"] = False return ret @timeoutDecorator def install_os(path=None, **kwargs): - ''' + """ Installs the given image on the device. After the installation is complete\ the device is rebooted, if reboot=True is given as a keyworded argument. @@ -1037,68 +1039,71 @@ def install_os(path=None, **kwargs): salt 'device_name' junos.install_os 'salt://images/junos_image.tgz' reboot=True salt 'device_name' junos.install_os 'salt://junos_16_1.tgz' dev_timeout=300 - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True op = {} - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) else: op.update(kwargs) - no_copy_ = op.get('no_copy', False) + no_copy_ = op.get("no_copy", False) if path is None: - ret['message'] = \ - 'Please provide the salt path where the junos image is present.' - ret['out'] = False + ret[ + "message" + ] = "Please provide the salt path where the junos image is present." + ret["out"] = False return ret if not no_copy_: image_cached_path = salt.utils.files.mkstemp() - __salt__['cp.get_file'](path, image_cached_path) + __salt__["cp.get_file"](path, image_cached_path) if not os.path.isfile(image_cached_path): - ret['message'] = 'Invalid image path.' - ret['out'] = False + ret["message"] = "Invalid image path." + ret["out"] = False return ret if os.path.getsize(image_cached_path) == 0: - ret['message'] = 'Failed to copy image' - ret['out'] = False + ret["message"] = "Failed to copy image" + ret["out"] = False return ret path = image_cached_path try: conn.sw.install(path, progress=True, **op) - ret['message'] = 'Installed the os.' + ret["message"] = "Installed the os." except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Installation failed due to: "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Installation failed due to: "{0}"'.format(exception) + ret["out"] = False return ret finally: if not no_copy_: salt.utils.files.safe_rm(image_cached_path) - if 'reboot' in op and op['reboot'] is True: + if "reboot" in op and op["reboot"] is True: try: conn.sw.reboot() except Exception as exception: # pylint: disable=broad-except - ret['message'] = \ - 'Installation successful but reboot failed due to : "{0}"' \ - .format(exception) - ret['out'] = False + ret[ + "message" + ] = 'Installation successful but reboot failed due to : "{0}"'.format( + exception + ) + ret["out"] = False return ret - ret['message'] = 'Successfully installed and rebooted!' + ret["message"] = "Successfully installed and rebooted!" return ret def file_copy(src=None, dest=None): - ''' + """ Copies the file from the local device to the junos device src @@ -1112,40 +1117,39 @@ def file_copy(src=None, dest=None): .. code-block:: bash salt 'device_name' junos.file_copy /home/m2/info.txt info_copy.txt - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True if src is None: - ret['message'] = \ - 'Please provide the absolute path of the file to be copied.' - ret['out'] = False + ret["message"] = "Please provide the absolute path of the file to be copied." + ret["out"] = False return ret if not os.path.isfile(src): - ret['message'] = 'Invalid source file path' - ret['out'] = False + ret["message"] = "Invalid source file path" + ret["out"] = False return ret if dest is None: - ret['message'] = \ - 'Please provide the absolute path of the destination where the file is to be copied.' - ret['out'] = False + ret[ + "message" + ] = "Please provide the absolute path of the destination where the file is to be copied." + ret["out"] = False return ret try: with SCP(conn, progress=True) as scp: scp.put(src, dest) - ret['message'] = 'Successfully copied file from {0} to {1}'.format( - src, dest) + ret["message"] = "Successfully copied file from {0} to {1}".format(src, dest) except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not copy file : "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Could not copy file : "{0}"'.format(exception) + ret["out"] = False return ret def lock(): - ''' + """ Attempts an exclusive lock on the candidate configuration. This is a non-blocking call. @@ -1160,22 +1164,22 @@ def lock(): .. code-block:: bash salt 'device_name' junos.lock - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True try: conn.cu.lock() - ret['message'] = "Successfully locked the configuration." + ret["message"] = "Successfully locked the configuration." except jnpr.junos.exception.LockError as exception: - ret['message'] = 'Could not gain lock due to : "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Could not gain lock due to : "{0}"'.format(exception) + ret["out"] = False return ret def unlock(): - ''' + """ Unlocks the candidate configuration. CLI Example: @@ -1183,23 +1187,24 @@ def unlock(): .. code-block:: bash salt 'device_name' junos.unlock - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True try: conn.cu.unlock() - ret['message'] = "Successfully unlocked the configuration." + ret["message"] = "Successfully unlocked the configuration." except jnpr.junos.exception.UnlockError as exception: - ret['message'] = \ - 'Could not unlock configuration due to : "{0}"'.format(exception) - ret['out'] = False + ret["message"] = 'Could not unlock configuration due to : "{0}"'.format( + exception + ) + ret["out"] = False return ret def load(path=None, **kwargs): - ''' + """ Loads the configuration from the file provided onto the device. path (required) @@ -1250,22 +1255,23 @@ def load(path=None, **kwargs): salt 'device_name' junos.load 'salt://my_new_configuration.conf' overwrite=True salt 'device_name' junos.load 'salt://syslog_template.conf' template_vars='{"syslog_host": "10.180.222.7"}' - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True if path is None: - ret['message'] = \ - 'Please provide the salt path where the configuration is present' - ret['out'] = False + ret[ + "message" + ] = "Please provide the salt path where the configuration is present" + ret["out"] = False return ret op = {} - if '__pub_arg' in kwargs: - if kwargs['__pub_arg']: - if isinstance(kwargs['__pub_arg'][-1], dict): - op.update(kwargs['__pub_arg'][-1]) + if "__pub_arg" in kwargs: + if kwargs["__pub_arg"]: + if isinstance(kwargs["__pub_arg"][-1], dict): + op.update(kwargs["__pub_arg"][-1]) else: op.update(kwargs) @@ -1274,50 +1280,46 @@ def load(path=None, **kwargs): template_vars = op["template_vars"] template_cached_path = salt.utils.files.mkstemp() - __salt__['cp.get_template']( - path, - template_cached_path, - template_vars=template_vars) + __salt__["cp.get_template"](path, template_cached_path, template_vars=template_vars) if not os.path.isfile(template_cached_path): - ret['message'] = 'Invalid file path.' - ret['out'] = False + ret["message"] = "Invalid file path." + ret["out"] = False return ret if os.path.getsize(template_cached_path) == 0: - ret['message'] = 'Template failed to render' - ret['out'] = False + ret["message"] = "Template failed to render" + ret["out"] = False return ret - op['path'] = template_cached_path + op["path"] = template_cached_path - if 'format' not in op: - if path.endswith('set'): - template_format = 'set' - elif path.endswith('xml'): - template_format = 'xml' + if "format" not in op: + if path.endswith("set"): + template_format = "set" + elif path.endswith("xml"): + template_format = "xml" else: - template_format = 'text' + template_format = "text" - op['format'] = template_format + op["format"] = template_format - if 'replace' in op and op['replace']: - op['merge'] = False - del op['replace'] - elif 'overwrite' in op and op['overwrite']: - op['overwrite'] = True - elif 'overwrite' in op and not op['overwrite']: - op['merge'] = True - del op['overwrite'] + if "replace" in op and op["replace"]: + op["merge"] = False + del op["replace"] + elif "overwrite" in op and op["overwrite"]: + op["overwrite"] = True + elif "overwrite" in op and not op["overwrite"]: + op["merge"] = True + del op["overwrite"] try: conn.cu.load(**op) - ret['message'] = "Successfully loaded the configuration." + ret["message"] = "Successfully loaded the configuration." except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Could not load configuration due to : "{0}"'.format( - exception) - ret['format'] = op['format'] - ret['out'] = False + ret["message"] = 'Could not load configuration due to : "{0}"'.format(exception) + ret["format"] = op["format"] + ret["out"] = False return ret finally: salt.utils.files.safe_rm(template_cached_path) @@ -1326,7 +1328,7 @@ def load(path=None, **kwargs): def commit_check(): - ''' + """ Perform a commit check on the configuration CLI Example: @@ -1334,15 +1336,15 @@ def commit_check(): .. code-block:: bash salt 'device_name' junos.commit_check - ''' - conn = __proxy__['junos.conn']() + """ + conn = __proxy__["junos.conn"]() ret = {} - ret['out'] = True + ret["out"] = True try: conn.cu.commit_check() - ret['message'] = 'Commit check succeeded.' + ret["message"] = "Commit check succeeded." except Exception as exception: # pylint: disable=broad-except - ret['message'] = 'Commit check failed with {0}'.format(exception) - ret['out'] = False + ret["message"] = "Commit check failed with {0}".format(exception) + ret["out"] = False return ret diff --git a/salt/modules/k8s.py b/salt/modules/k8s.py index e0af204ba05..f406743cd8b 100644 --- a/salt/modules/k8s.py +++ b/salt/modules/k8s.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Salt module to manage Kubernetes cluster .. versionadded:: 2016.3.0 @@ -12,49 +12,51 @@ Roadmap: * Add rolling update * Add (auto)scalling -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals +import base64 +import logging as logger import os import re -import logging as logger -import base64 -from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=no-name-in-module - -# TODO Remove requests dependency import salt.utils.files import salt.utils.http as http import salt.utils.json +from salt.ext import six +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse -__virtualname__ = 'k8s' +# TODO Remove requests dependency + + +__virtualname__ = "k8s" # Setup the logger log = logger.getLogger(__name__) def __virtual__(): - '''Load load if python-requests is installed.''' + """Load load if python-requests is installed.""" return __virtualname__ def _guess_apiserver(apiserver_url=None): - '''Try to guees the kubemaster url from environ, + """Try to guees the kubemaster url from environ, then from `/etc/kubernetes/config` file - ''' + """ default_config = "/etc/kubernetes/config" if apiserver_url is not None: return apiserver_url if "KUBERNETES_MASTER" in os.environ: apiserver_url = os.environ.get("KUBERNETES_MASTER") - elif __salt__['config.get']('k8s:master'): - apiserver_url = __salt__['config.get']('k8s:master') - elif os.path.exists(default_config) or __salt__['config.get']('k8s:config', ""): - config = __salt__['config.get']('k8s:config', default_config) - kubeapi_regex = re.compile("""KUBE_MASTER=['"]--master=(.*)['"]""", - re.MULTILINE) + elif __salt__["config.get"]("k8s:master"): + apiserver_url = __salt__["config.get"]("k8s:master") + elif os.path.exists(default_config) or __salt__["config.get"]("k8s:config", ""): + config = __salt__["config.get"]("k8s:config", default_config) + kubeapi_regex = re.compile( + """KUBE_MASTER=['"]--master=(.*)['"]""", re.MULTILINE + ) with salt.utils.files.fopen(config) as fh_k8s: for line in fh_k8s.readlines(): match_line = kubeapi_regex.match(line) @@ -68,58 +70,57 @@ def _guess_apiserver(apiserver_url=None): def _kpost(url, data): - ''' create any object in kubernetes based on URL ''' + """ create any object in kubernetes based on URL """ # Prepare headers headers = {"Content-Type": "application/json"} # Make request log.trace("url is: %s, data is: %s", url, data) - ret = http.query(url, - method='POST', - header_dict=headers, - data=salt.utils.json.dumps(data)) + ret = http.query( + url, method="POST", header_dict=headers, data=salt.utils.json.dumps(data) + ) # Check requests status - if ret.get('error'): + if ret.get("error"): return ret else: - return salt.utils.json.loads(ret.get('body')) + return salt.utils.json.loads(ret.get("body")) def _kput(url, data): - ''' put any object in kubernetes based on URL ''' + """ put any object in kubernetes based on URL """ # Prepare headers headers = {"Content-Type": "application/json"} # Make request - ret = http.query(url, - method='PUT', - header_dict=headers, - data=salt.utils.json.dumps(data)) + ret = http.query( + url, method="PUT", header_dict=headers, data=salt.utils.json.dumps(data) + ) # Check requests status - if ret.get('error'): + if ret.get("error"): return ret else: - return salt.utils.json.loads(ret.get('body')) + return salt.utils.json.loads(ret.get("body")) def _kpatch(url, data): - ''' patch any object in kubernetes based on URL ''' + """ patch any object in kubernetes based on URL """ # Prepare headers headers = {"Content-Type": "application/json-patch+json"} # Make request - ret = http.query(url, method='PATCH', header_dict=headers, - data=salt.utils.json.dumps(data)) + ret = http.query( + url, method="PATCH", header_dict=headers, data=salt.utils.json.dumps(data) + ) # Check requests status - if ret.get('error'): + if ret.get("error"): log.error("Got an error: %s", ret.get("error")) return ret else: - return salt.utils.json.loads(ret.get('body')) + return salt.utils.json.loads(ret.get("body")) def _kname(obj): - '''Get name or names out of json result from API server''' + """Get name or names out of json result from API server""" if isinstance(obj, dict): return [obj.get("metadata", {}).get("name", "")] elif isinstance(obj, (list, tuple)): @@ -132,8 +133,8 @@ def _kname(obj): def _is_dns_subdomain(name): - ''' Check that name is DNS subdomain: One or more lowercase rfc1035/rfc1123 - labels separated by '.' with a maximum length of 253 characters ''' + """ Check that name is DNS subdomain: One or more lowercase rfc1035/rfc1123 + labels separated by '.' with a maximum length of 253 characters """ dns_subdomain = re.compile(r"""^[a-z0-9\.-]{1,253}$""") if dns_subdomain.match(name): @@ -145,10 +146,10 @@ def _is_dns_subdomain(name): def _is_port_name(name): - ''' Check that name is IANA service: An alphanumeric (a-z, and 0-9) string, + """ Check that name is IANA service: An alphanumeric (a-z, and 0-9) string, with a maximum length of 15 characters, with the '-' character allowed anywhere except the first or the last character or adjacent to another '-' - character, it must contain at least a (a-z) character ''' + character, it must contain at least a (a-z) character """ port_name = re.compile("""^[a-z0-9]{1,15}$""") if port_name.match(name): @@ -158,10 +159,10 @@ def _is_port_name(name): def _is_dns_label(name): - ''' Check that name is DNS label: An alphanumeric (a-z, and 0-9) string, + """ Check that name is DNS label: An alphanumeric (a-z, and 0-9) string, with a maximum length of 63 characters, with the '-' character allowed anywhere except the first or last character, suitable for use as a hostname - or segment in a domain name ''' + or segment in a domain name """ dns_label = re.compile(r"""^[a-z0-9][a-z0-9\.-]{1,62}$""") if dns_label.match(name): @@ -171,31 +172,31 @@ def _is_dns_label(name): def _guess_node_id(node): - '''Try to guess kube node ID using salt minion ID''' + """Try to guess kube node ID using salt minion ID""" if node is None: - return __salt__['grains.get']('id') + return __salt__["grains.get"]("id") return node def _get_labels(node, apiserver_url): - '''Get all labels from a kube node.''' + """Get all labels from a kube node.""" # Prepare URL url = "{0}/api/v1/nodes/{1}".format(apiserver_url, node) # Make request ret = http.query(url) # Check requests status - if 'body' in ret: - ret = salt.utils.json.loads(ret.get('body')) - elif ret.get('status', 0) == 404: + if "body" in ret: + ret = salt.utils.json.loads(ret.get("body")) + elif ret.get("status", 0) == 404: return "Node {0} doesn't exist".format(node) else: return ret # Get and return labels - return ret.get('metadata', {}).get('labels', {}) + return ret.get("metadata", {}).get("labels", {}) def _set_labels(node, apiserver_url, labels): - '''Replace labels dict by a new one''' + """Replace labels dict by a new one""" # Prepare URL url = "{0}/api/v1/nodes/{1}".format(apiserver_url, node) # Prepare data @@ -208,7 +209,7 @@ def _set_labels(node, apiserver_url, labels): def get_labels(node=None, apiserver_url=None): - ''' + """ .. versionadded:: 2016.3.0 Get labels from the current node @@ -220,7 +221,7 @@ def get_labels(node=None, apiserver_url=None): salt '*' k8s.get_labels salt '*' k8s.get_labels kube-node.cluster.local http://kube-master.cluster.local - ''' + """ # Get salt minion ID node = _guess_node_id(node) # Try to get kubernetes master @@ -235,7 +236,7 @@ def get_labels(node=None, apiserver_url=None): def label_present(name, value, node=None, apiserver_url=None): - ''' + """ .. versionadded:: 2016.3.0 Set label to the current node @@ -248,8 +249,8 @@ def label_present(name, value, node=None, apiserver_url=None): salt '*' k8s.label_present hw/disktype ssd kube-node.cluster.local http://kube-master.cluster.local - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # Get salt minion ID node = _guess_node_id(node) @@ -263,37 +264,37 @@ def label_present(name, value, node=None, apiserver_url=None): if name not in labels: # This is a new label - ret['changes'] = {name: value} + ret["changes"] = {name: value} labels[name] = str(value) res = _set_labels(node, apiserver_url, labels) - if res.get('status') == 409: + if res.get("status") == 409: # there is an update during operation, need to retry log.debug("Got 409, will try later") - ret['changes'] = {} - ret['comment'] = "Could not create label {0}, please retry".format(name) + ret["changes"] = {} + ret["comment"] = "Could not create label {0}, please retry".format(name) else: - ret['comment'] = "Label {0} created".format(name) + ret["comment"] = "Label {0} created".format(name) elif labels.get(name) != str(value): # This is a old label and we are going to edit it - ret['changes'] = {name: str(value)} + ret["changes"] = {name: str(value)} labels[name] = str(value) res = _set_labels(node, apiserver_url, labels) - if res.get('status') == 409: + if res.get("status") == 409: # there is an update during operation, need to retry log.debug("Got 409, will try later") - ret['changes'] = {} - ret['comment'] = "Could not update label {0}, please retry".format(name) + ret["changes"] = {} + ret["comment"] = "Could not update label {0}, please retry".format(name) else: - ret['comment'] = "Label {0} updated".format(name) + ret["comment"] = "Label {0} updated".format(name) else: # This is a old label and it has already the wanted value - ret['comment'] = "Label {0} already set".format(name) + ret["comment"] = "Label {0} already set".format(name) return ret def label_absent(name, node=None, apiserver_url=None): - ''' + """ .. versionadded:: 2016.3.0 Delete label to the current node @@ -305,8 +306,8 @@ def label_absent(name, node=None, apiserver_url=None): salt '*' k8s.label_absent hw/disktype salt '*' k8s.label_absent hw/disktype kube-node.cluster.local http://kube-master.cluster.local - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # Get salt minion ID node = _guess_node_id(node) @@ -318,29 +319,28 @@ def label_absent(name, node=None, apiserver_url=None): # Get all labels old_labels = _get_labels(node, apiserver_url) # Prepare a temp labels dict - labels = dict([(key, value) for key, value in old_labels.items() - if key != name]) + labels = dict([(key, value) for key, value in old_labels.items() if key != name]) # Compare old labels and what we want if labels == old_labels: # Label already absent - ret['comment'] = "Label {0} already absent".format(name) + ret["comment"] = "Label {0} already absent".format(name) else: # Label needs to be delete res = _set_labels(node, apiserver_url, labels) - if res.get('status') == 409: + if res.get("status") == 409: # there is an update during operation, need to retry log.debug("Got 409, will try later") - ret['changes'] = {} - ret['comment'] = "Could not delete label {0}, please retry".format(name) + ret["changes"] = {} + ret["comment"] = "Could not delete label {0}, please retry".format(name) else: - ret['changes'] = {"deleted": name} - ret['comment'] = "Label {0} absent".format(name) + ret["changes"] = {"deleted": name} + ret["comment"] = "Label {0} absent".format(name) return ret def label_folder_absent(name, node=None, apiserver_url=None): - ''' + """ .. versionadded:: 2016.3.0 Delete label folder to the current node @@ -352,9 +352,9 @@ def label_folder_absent(name, node=None, apiserver_url=None): salt '*' k8s.label_folder_absent hw salt '*' k8s.label_folder_absent hw/ kube-node.cluster.local http://kube-master.cluster.local - ''' + """ folder = name.strip("/") + "/" - ret = {'name': folder, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": folder, "result": True, "comment": "", "changes": {}} # Get salt minion ID node = _guess_node_id(node) @@ -366,29 +366,36 @@ def label_folder_absent(name, node=None, apiserver_url=None): # Get all labels old_labels = _get_labels(node, apiserver_url) # Prepare a temp labels dict - labels = dict([(key, value) for key, value in old_labels.items() - if not key.startswith(folder)]) + labels = dict( + [ + (key, value) + for key, value in old_labels.items() + if not key.startswith(folder) + ] + ) # Prepare a temp labels dict if labels == old_labels: # Label already absent - ret['comment'] = "Label folder {0} already absent".format(folder) + ret["comment"] = "Label folder {0} already absent".format(folder) else: # Label needs to be delete res = _set_labels(node, apiserver_url, labels) - if res.get('status') == 409: + if res.get("status") == 409: log.debug("Got 409, will try later") - ret['changes'] = {} - ret['comment'] = "Could not delete label folder {0}, please retry".format(folder) + ret["changes"] = {} + ret["comment"] = "Could not delete label folder {0}, please retry".format( + folder + ) else: - ret['changes'] = {"deleted": folder} - ret['comment'] = "Label folder {0} absent".format(folder) + ret["changes"] = {"deleted": folder} + ret["comment"] = "Label folder {0} absent".format(folder) return ret # Namespaces def _get_namespaces(apiserver_url, name=""): - '''Get namespace is namespace is defined otherwise return all namespaces''' + """Get namespace is namespace is defined otherwise return all namespaces""" # Prepare URL url = "{0}/api/v1/namespaces/{1}".format(apiserver_url, name) # Make request @@ -400,17 +407,11 @@ def _get_namespaces(apiserver_url, name=""): def _create_namespace(namespace, apiserver_url): - ''' create namespace on the defined k8s cluster ''' + """ create namespace on the defined k8s cluster """ # Prepare URL url = "{0}/api/v1/namespaces".format(apiserver_url) # Prepare data - data = { - "kind": "Namespace", - "apiVersion": "v1", - "metadata": { - "name": namespace, - } - } + data = {"kind": "Namespace", "apiVersion": "v1", "metadata": {"name": namespace}} log.trace("namespace creation requests: %s", data) # Make request ret = _kpost(url, data) @@ -420,7 +421,7 @@ def _create_namespace(namespace, apiserver_url): def create_namespace(name, apiserver_url=None): - ''' + """ .. versionadded:: 2016.3.0 Create kubernetes namespace from the name, similar to the functionality added to kubectl since v.1.2.0: @@ -436,8 +437,8 @@ def create_namespace(name, apiserver_url=None): salt '*' k8s.create_namespace namespace_name http://kube-master.cluster.local - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # Try to get kubernetes master apiserver_url = _guess_apiserver(apiserver_url) @@ -447,15 +448,15 @@ def create_namespace(name, apiserver_url=None): if not _get_namespaces(apiserver_url, name): # This is a new namespace _create_namespace(name, apiserver_url) - ret['changes'] = name - ret['comment'] = "Namespace {0} created".format(name) + ret["changes"] = name + ret["comment"] = "Namespace {0} created".format(name) else: - ret['comment'] = "Namespace {0} already present".format(name) + ret["comment"] = "Namespace {0} already present".format(name) return ret def get_namespaces(namespace="", apiserver_url=None): - ''' + """ .. versionadded:: 2016.3.0 Get one or all kubernetes namespaces. @@ -480,7 +481,7 @@ def get_namespaces(namespace="", apiserver_url=None): salt '*' k8s.get_namespaces salt '*' k8s.get_namespaces namespace_name http://kube-master.cluster.local - ''' + """ # Try to get kubernetes master apiserver_url = _guess_apiserver(apiserver_url) if apiserver_url is None: @@ -493,10 +494,9 @@ def get_namespaces(namespace="", apiserver_url=None): # Secrets def _get_secrets(namespace, name, apiserver_url): - '''Get secrets of the namespace.''' + """Get secrets of the namespace.""" # Prepare URL - url = "{0}/api/v1/namespaces/{1}/secrets/{2}".format(apiserver_url, - namespace, name) + url = "{0}/api/v1/namespaces/{1}/secrets/{2}".format(apiserver_url, namespace, name) # Make request ret = http.query(url) if ret.get("body"): @@ -506,10 +506,9 @@ def _get_secrets(namespace, name, apiserver_url): def _update_secret(namespace, name, data, apiserver_url): - '''Replace secrets data by a new one''' + """Replace secrets data by a new one""" # Prepare URL - url = "{0}/api/v1/namespaces/{1}/secrets/{2}".format(apiserver_url, - namespace, name) + url = "{0}/api/v1/namespaces/{1}/secrets/{2}".format(apiserver_url, namespace, name) # Prepare data data = [{"op": "replace", "path": "/data", "value": data}] # Make request @@ -520,18 +519,15 @@ def _update_secret(namespace, name, data, apiserver_url): def _create_secret(namespace, name, data, apiserver_url): - ''' create namespace on the defined k8s cluster ''' + """ create namespace on the defined k8s cluster """ # Prepare URL url = "{0}/api/v1/namespaces/{1}/secrets".format(apiserver_url, namespace) # Prepare data request = { "apiVersion": "v1", "kind": "Secret", - "metadata": { - "name": name, - "namespace": namespace, - }, - "data": data + "metadata": {"name": name, "namespace": namespace}, + "data": data, } # Make request ret = _kpost(url, request) @@ -559,17 +555,17 @@ def _decode_secrets(secrets): for i, secret in enumerate(items): log.trace(i, secret) for k, v in six.iteritems(secret.get("data", {})): - items[i]['data'][k] = base64.b64decode(v) + items[i]["data"][k] = base64.b64decode(v) secrets["items"] = items return secrets else: for k, v in six.iteritems(secrets.get("data", {})): - secrets['data'][k] = base64.b64decode(v) + secrets["data"][k] = base64.b64decode(v) return secrets def get_secrets(namespace, name="", apiserver_url=None, decode=False, brief=False): - ''' + """ Get k8s namespaces CLI Example: @@ -579,7 +575,7 @@ def get_secrets(namespace, name="", apiserver_url=None, decode=False, brief=Fals salt '*' k8s.get_secrets namespace_name salt '*' k8s.get_secrets namespace_name secret_name http://kube-master.cluster.local - ''' + """ # Try to get kubernetes master apiserver_url = _guess_apiserver(apiserver_url) if apiserver_url is None: @@ -597,12 +593,12 @@ def _source_encode(source, saltenv): try: source_url = _urlparse(source) except TypeError: - return '', {}, ('Invalid format for source parameter') + return "", {}, ("Invalid format for source parameter") - protos = ('salt', 'http', 'https', 'ftp', 'swift', 's3', 'file') + protos = ("salt", "http", "https", "ftp", "swift", "s3", "file") log.trace("parsed source looks like: %s", source_url) - if not source_url.scheme or source_url.scheme == 'file': + if not source_url.scheme or source_url.scheme == "file": # just a regular file filename = os.path.abspath(source_url.path) sname = os.path.basename(filename) @@ -612,7 +608,7 @@ def _source_encode(source, saltenv): else: if source_url.scheme in protos: # The source is a file on a server - filename = __salt__['cp.cache_file'](source, saltenv) + filename = __salt__["cp.cache_file"](source, saltenv) if not filename: log.warning("Source file: %s can not be retrieved", source) return "", "" @@ -620,8 +616,10 @@ def _source_encode(source, saltenv): return "", "" -def update_secret(namespace, name, sources, apiserver_url=None, force=True, saltenv='base'): - ''' +def update_secret( + namespace, name, sources, apiserver_url=None, force=True, saltenv="base" +): + """ .. versionadded:: 2016.3.0 alias to k8s.create_secret with update=true @@ -656,16 +654,31 @@ def update_secret(namespace, name, sources, apiserver_url=None, force=True, salt saltenv=['base'] default value is base in case 'salt://' path is used, this parameter can change the visibility of files - ''' + """ apiserver_url = _guess_apiserver(apiserver_url) - ret = create_secret(namespace, name, sources, apiserver_url=apiserver_url, - force=force, update=True, saltenv=saltenv) + ret = create_secret( + namespace, + name, + sources, + apiserver_url=apiserver_url, + force=force, + update=True, + saltenv=saltenv, + ) return ret -def create_secret(namespace, name, sources, apiserver_url=None, force=False, update=False, saltenv='base'): - ''' +def create_secret( + namespace, + name, + sources, + apiserver_url=None, + force=False, + update=False, + saltenv="base", +): + """ .. versionadded:: 2016.3.0 Create k8s secrets in the defined namespace from the list of files @@ -707,11 +720,16 @@ def create_secret(namespace, name, sources, apiserver_url=None, force=False, upd saltenv=['base'] default value is base in case 'salt://' path is used, this parameter can change the visibility of files - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not sources: - return {'name': name, 'result': False, 'comment': 'No source available', 'changes': {}} + return { + "name": name, + "result": False, + "comment": "No source available", + "changes": {}, + } apiserver_url = _guess_apiserver(apiserver_url) # we need namespace to create secret in it @@ -719,14 +737,22 @@ def create_secret(namespace, name, sources, apiserver_url=None, force=False, upd if force: _create_namespace(namespace, apiserver_url) else: - return {'name': name, 'result': False, 'comment': "Namespace doesn't exists", 'changes': {}} + return { + "name": name, + "result": False, + "comment": "Namespace doesn't exists", + "changes": {}, + } secret = _get_secrets(namespace, name, apiserver_url) if secret and not update: log.info("Secret %s is already present on %s", name, namespace) - return {'name': name, 'result': False, - 'comment': 'Secret {0} is already present'.format(name), - 'changes': {}} + return { + "name": name, + "result": False, + "comment": "Secret {0} is already present".format(name), + "changes": {}, + } data = {} @@ -739,7 +765,9 @@ def create_secret(namespace, name, sources, apiserver_url=None, force=False, upd for k, v in six.iteritems(source): sname, encoded = _source_encode(v, saltenv) if sname == encoded == "": - ret['comment'] += "Source file {0} is missing or name is incorrect\n".format(v) + ret[ + "comment" + ] += "Source file {0} is missing or name is incorrect\n".format(v) if force: continue else: @@ -750,7 +778,11 @@ def create_secret(namespace, name, sources, apiserver_url=None, force=False, upd sname, encoded = _source_encode(source, saltenv) if sname == encoded == "": if force: - ret['comment'] += "Source file {0} is missing or name is incorrect\n".format(source) + ret[ + "comment" + ] += "Source file {0} is missing or name is incorrect\n".format( + source + ) continue else: return ret @@ -764,18 +796,18 @@ def create_secret(namespace, name, sources, apiserver_url=None, force=False, upd ret["result"] = False elif secret.get("data") and data != secret.get("data"): res = _update_secret(namespace, name, data, apiserver_url) - ret['comment'] = 'Updated secret' - ret['changes'] = 'Updated secret' + ret["comment"] = "Updated secret" + ret["changes"] = "Updated secret" else: log.debug("Secret has not been changed on cluster, skipping it") - ret['comment'] = 'Has not been changed on cluster, skipping it' + ret["comment"] = "Has not been changed on cluster, skipping it" else: res = _create_secret(namespace, name, data, apiserver_url) return ret def delete_secret(namespace, name, apiserver_url=None, force=True): - ''' + """ .. versionadded:: 2016.3.0 Delete kubernetes secret in the defined namespace. Namespace is the mandatory parameter as well as name. @@ -788,8 +820,8 @@ def delete_secret(namespace, name, apiserver_url=None, force=True): salt '*' k8s.delete_secret namespace_name secret_name http://kube-master.cluster.local - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # Try to get kubernetes master apiserver_url = _guess_apiserver(apiserver_url) @@ -798,14 +830,15 @@ def delete_secret(namespace, name, apiserver_url=None, force=True): # we need namespace to delete secret in it if not _get_namespaces(apiserver_url, namespace): - return {'name': name, 'result': False, - 'comment': "Namespace doesn't exists, can't delete anything there", - 'changes': {}} + return { + "name": name, + "result": False, + "comment": "Namespace doesn't exists, can't delete anything there", + "changes": {}, + } - url = "{0}/api/v1/namespaces/{1}/secrets/{2}".format(apiserver_url, - namespace, name) - res = http.query(url, method='DELETE') - if res.get('body'): - ret['comment'] = "Removed secret {0} in {1} namespace".format(name, - namespace) + url = "{0}/api/v1/namespaces/{1}/secrets/{2}".format(apiserver_url, namespace, name) + res = http.query(url, method="DELETE") + if res.get("body"): + ret["comment"] = "Removed secret {0} in {1} namespace".format(name, namespace) return ret diff --git a/salt/modules/kapacitor.py b/salt/modules/kapacitor.py index 5c1d5ec2e5b..2a3d86e6f8c 100644 --- a/salt/modules/kapacitor.py +++ b/salt/modules/kapacitor.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Kapacitor execution module. :configuration: This module accepts connection configuration details either as @@ -21,50 +21,57 @@ Kapacitor execution module. This data can also be passed into pillar. Options passed into opts will overwrite options passed into pillar. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt lobs -from salt.ext import six +import logging as logger + import salt.utils.http import salt.utils.json import salt.utils.path + +# Import Salt lobs +from salt.ext import six from salt.utils.decorators import memoize -import logging as logger # Setup the logger log = logger.getLogger(__name__) def __virtual__(): - return 'kapacitor' if salt.utils.path.which('kapacitor') else False + if salt.utils.path.which("kapacitor"): + return "kapacitor" + else: + return (False, "Missing dependency: kapacitor") @memoize def version(): - ''' + """ Get the kapacitor version. - ''' - version = __salt__['pkg.version']('kapacitor') + """ + version = __salt__["pkg.version"]("kapacitor") if not version: - version = six.string_types(__salt__['config.option']('kapacitor.version', 'latest')) + version = six.string_types( + __salt__["config.option"]("kapacitor.version", "latest") + ) return version def _get_url(): - ''' + """ Get the kapacitor URL. - ''' - protocol = __salt__['config.option']('kapacitor.protocol', 'http') - host = __salt__['config.option']('kapacitor.host', 'localhost') - port = __salt__['config.option']('kapacitor.port', 9092) + """ + protocol = __salt__["config.option"]("kapacitor.protocol", "http") + host = __salt__["config.option"]("kapacitor.host", "localhost") + port = __salt__["config.option"]("kapacitor.port", 9092) - return '{0}://{1}:{2}'.format(protocol, host, port) + return "{0}://{1}:{2}".format(protocol, host, port) def get_task(name): - ''' + """ Get a dict of data on a task. name @@ -75,64 +82,68 @@ def get_task(name): .. code-block:: bash salt '*' kapacitor.get_task cpu - ''' + """ url = _get_url() - if version() < '0.13': - task_url = '{0}/task?name={1}'.format(url, name) + if version() < "0.13": + task_url = "{0}/task?name={1}".format(url, name) else: - task_url = '{0}/kapacitor/v1/tasks/{1}?skip-format=true'.format(url, name) + task_url = "{0}/kapacitor/v1/tasks/{1}?skip-format=true".format(url, name) response = salt.utils.http.query(task_url, status=True) - if response['status'] == 404: + if response["status"] == 404: return None - data = salt.utils.json.loads(response['body']) + data = salt.utils.json.loads(response["body"]) - if version() < '0.13': + if version() < "0.13": return { - 'script': data['TICKscript'], - 'type': data['Type'], - 'dbrps': data['DBRPs'], - 'enabled': data['Enabled'], + "script": data["TICKscript"], + "type": data["Type"], + "dbrps": data["DBRPs"], + "enabled": data["Enabled"], } return { - 'script': data['script'], - 'type': data['type'], - 'dbrps': data['dbrps'], - 'enabled': data['status'] == 'enabled', + "script": data["script"], + "type": data["type"], + "dbrps": data["dbrps"], + "enabled": data["status"] == "enabled", } def _run_cmd(cmd): - ''' + """ Run a Kapacitor task and return a dictionary of info. - ''' + """ ret = {} env_vars = { - 'KAPACITOR_URL': _get_url(), - 'KAPACITOR_UNSAFE_SSL': __salt__['config.option']('kapacitor.unsafe_ssl', 'false'), + "KAPACITOR_URL": _get_url(), + "KAPACITOR_UNSAFE_SSL": __salt__["config.option"]( + "kapacitor.unsafe_ssl", "false" + ), } - result = __salt__['cmd.run_all'](cmd, env=env_vars) + result = __salt__["cmd.run_all"](cmd, env=env_vars) - if result.get('stdout'): - ret['stdout'] = result['stdout'] - if result.get('stderr'): - ret['stderr'] = result['stderr'] - ret['success'] = result['retcode'] == 0 + if result.get("stdout"): + ret["stdout"] = result["stdout"] + if result.get("stderr"): + ret["stderr"] = result["stderr"] + ret["success"] = result["retcode"] == 0 return ret -def define_task(name, - tick_script, - task_type='stream', - database=None, - retention_policy='default', - dbrps=None): - ''' +def define_task( + name, + tick_script, + task_type="stream", + database=None, + retention_policy="default", + dbrps=None, +): + """ Define a task. Serves as both create/update. name @@ -162,40 +173,40 @@ def define_task(name, .. code-block:: bash salt '*' kapacitor.define_task cpu salt://kapacitor/cpu.tick database=telegraf - ''' + """ if not database and not dbrps: log.error("Providing database name or dbrps is mandatory.") return False - if version() < '0.13': - cmd = 'kapacitor define -name {0}'.format(name) + if version() < "0.13": + cmd = "kapacitor define -name {0}".format(name) else: - cmd = 'kapacitor define {0}'.format(name) + cmd = "kapacitor define {0}".format(name) - if tick_script.startswith('salt://'): - tick_script = __salt__['cp.cache_file'](tick_script, __env__) + if tick_script.startswith("salt://"): + tick_script = __salt__["cp.cache_file"](tick_script, __env__) - cmd += ' -tick {0}'.format(tick_script) + cmd += " -tick {0}".format(tick_script) if task_type: - cmd += ' -type {0}'.format(task_type) + cmd += " -type {0}".format(task_type) if not dbrps: dbrps = [] if database and retention_policy: - dbrp = '{0}.{1}'.format(database, retention_policy) + dbrp = "{0}.{1}".format(database, retention_policy) dbrps.append(dbrp) if dbrps: for dbrp in dbrps: - cmd += ' -dbrp {0}'.format(dbrp) + cmd += " -dbrp {0}".format(dbrp) return _run_cmd(cmd) def delete_task(name): - ''' + """ Delete a kapacitor task. name @@ -206,12 +217,12 @@ def delete_task(name): .. code-block:: bash salt '*' kapacitor.delete_task cpu - ''' - return _run_cmd('kapacitor delete tasks {0}'.format(name)) + """ + return _run_cmd("kapacitor delete tasks {0}".format(name)) def enable_task(name): - ''' + """ Enable a kapacitor task. name @@ -222,12 +233,12 @@ def enable_task(name): .. code-block:: bash salt '*' kapacitor.enable_task cpu - ''' - return _run_cmd('kapacitor enable {0}'.format(name)) + """ + return _run_cmd("kapacitor enable {0}".format(name)) def disable_task(name): - ''' + """ Disable a kapacitor task. name @@ -238,5 +249,5 @@ def disable_task(name): .. code-block:: bash salt '*' kapacitor.disable_task cpu - ''' - return _run_cmd('kapacitor disable {0}'.format(name)) + """ + return _run_cmd("kapacitor disable {0}".format(name)) diff --git a/salt/modules/kerberos.py b/salt/modules/kerberos.py index 9bb6cac8001..a41a384bcae 100644 --- a/salt/modules/kerberos.py +++ b/salt/modules/kerberos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Kerberos KDC :configuration: @@ -18,10 +18,11 @@ authenticate as. auth_keytab: /root/auth.keytab auth_principal: kadmin/admin -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -31,37 +32,35 @@ log = logging.getLogger(__name__) def __virtual__(): - if salt.utils.path.which('kadmin'): + if salt.utils.path.which("kadmin"): return True - return (False, 'The kerberos execution module not loaded: kadmin not in path') + return (False, "The kerberos execution module not loaded: kadmin not in path") def __execute_kadmin(cmd): - ''' + """ Execute kadmin commands - ''' + """ ret = {} - auth_keytab = __opts__.get('auth_keytab', None) - auth_principal = __opts__.get('auth_principal', None) + auth_keytab = __opts__.get("auth_keytab", None) + auth_principal = __opts__.get("auth_principal", None) - if __salt__['file.file_exists'](auth_keytab) and auth_principal: - return __salt__['cmd.run_all']( - 'kadmin -k -t {0} -p {1} -q "{2}"'.format( - auth_keytab, auth_principal, cmd - ) + if __salt__["file.file_exists"](auth_keytab) and auth_principal: + return __salt__["cmd.run_all"]( + 'kadmin -k -t {0} -p {1} -q "{2}"'.format(auth_keytab, auth_principal, cmd) ) else: - log.error('Unable to find kerberos keytab/principal') - ret['retcode'] = 1 - ret['comment'] = 'Missing authentication keytab/principal' + log.error("Unable to find kerberos keytab/principal") + ret["retcode"] = 1 + ret["comment"] = "Missing authentication keytab/principal" return ret def list_principals(): - ''' + """ Get all principals CLI Example: @@ -69,27 +68,27 @@ def list_principals(): .. code-block:: bash salt 'kde.example.com' kerberos.list_principals - ''' + """ ret = {} - cmd = __execute_kadmin('list_principals') + cmd = __execute_kadmin("list_principals") - if cmd['retcode'] != 0 or cmd['stderr']: - ret['comment'] = cmd['stderr'].splitlines()[-1] - ret['result'] = False + if cmd["retcode"] != 0 or cmd["stderr"]: + ret["comment"] = cmd["stderr"].splitlines()[-1] + ret["result"] = False return ret - ret = {'principals': []} + ret = {"principals": []} - for i in cmd['stdout'].splitlines()[1:]: - ret['principals'].append(i) + for i in cmd["stdout"].splitlines()[1:]: + ret["principals"].append(i) return ret def get_principal(name): - ''' + """ Get princial details CLI Example: @@ -97,19 +96,19 @@ def get_principal(name): .. code-block:: bash salt 'kdc.example.com' kerberos.get_principal root/admin - ''' + """ ret = {} - cmd = __execute_kadmin('get_principal {0}'.format(name)) + cmd = __execute_kadmin("get_principal {0}".format(name)) - if cmd['retcode'] != 0 or cmd['stderr']: - ret['comment'] = cmd['stderr'].splitlines()[-1] - ret['result'] = False + if cmd["retcode"] != 0 or cmd["stderr"]: + ret["comment"] = cmd["stderr"].splitlines()[-1] + ret["result"] = False return ret - for i in cmd['stdout'].splitlines()[1:]: - (prop, val) = i.split(':', 1) + for i in cmd["stdout"].splitlines()[1:]: + (prop, val) = i.split(":", 1) ret[prop] = val @@ -117,7 +116,7 @@ def get_principal(name): def list_policies(): - ''' + """ List policies CLI Example: @@ -125,27 +124,27 @@ def list_policies(): .. code-block:: bash salt 'kdc.example.com' kerberos.list_policies - ''' + """ ret = {} - cmd = __execute_kadmin('list_policies') + cmd = __execute_kadmin("list_policies") - if cmd['retcode'] != 0 or cmd['stderr']: - ret['comment'] = cmd['stderr'].splitlines()[-1] - ret['result'] = False + if cmd["retcode"] != 0 or cmd["stderr"]: + ret["comment"] = cmd["stderr"].splitlines()[-1] + ret["result"] = False return ret - ret = {'policies': []} + ret = {"policies": []} - for i in cmd['stdout'].splitlines()[1:]: - ret['policies'].append(i) + for i in cmd["stdout"].splitlines()[1:]: + ret["policies"].append(i) return ret def get_policy(name): - ''' + """ Get policy details CLI Example: @@ -153,19 +152,19 @@ def get_policy(name): .. code-block:: bash salt 'kdc.example.com' kerberos.get_policy my_policy - ''' + """ ret = {} - cmd = __execute_kadmin('get_policy {0}'.format(name)) + cmd = __execute_kadmin("get_policy {0}".format(name)) - if cmd['retcode'] != 0 or cmd['stderr']: - ret['comment'] = cmd['stderr'].splitlines()[-1] - ret['result'] = False + if cmd["retcode"] != 0 or cmd["stderr"]: + ret["comment"] = cmd["stderr"].splitlines()[-1] + ret["result"] = False return ret - for i in cmd['stdout'].splitlines()[1:]: - (prop, val) = i.split(':', 1) + for i in cmd["stdout"].splitlines()[1:]: + (prop, val) = i.split(":", 1) ret[prop] = val @@ -173,7 +172,7 @@ def get_policy(name): def get_privs(): - ''' + """ Current privileges CLI Example: @@ -181,19 +180,19 @@ def get_privs(): .. code-block:: bash salt 'kdc.example.com' kerberos.get_privs - ''' + """ ret = {} - cmd = __execute_kadmin('get_privs') + cmd = __execute_kadmin("get_privs") - if cmd['retcode'] != 0 or cmd['stderr']: - ret['comment'] = cmd['stderr'].splitlines()[-1] - ret['result'] = False + if cmd["retcode"] != 0 or cmd["stderr"]: + ret["comment"] = cmd["stderr"].splitlines()[-1] + ret["result"] = False return ret - for i in cmd['stdout'].splitlines()[1:]: - (prop, val) = i.split(':', 1) + for i in cmd["stdout"].splitlines()[1:]: + (prop, val) = i.split(":", 1) ret[prop] = [j for j in val.split()] @@ -201,7 +200,7 @@ def get_privs(): def create_principal(name, enctypes=None): - ''' + """ Create Principal CLI Example: @@ -209,22 +208,22 @@ def create_principal(name, enctypes=None): .. code-block:: bash salt 'kdc.example.com' kerberos.create_principal host/example.com - ''' + """ ret = {} - krb_cmd = 'addprinc -randkey' + krb_cmd = "addprinc -randkey" if enctypes: - krb_cmd += ' -e {0}'.format(enctypes) + krb_cmd += " -e {0}".format(enctypes) - krb_cmd += ' {0}'.format(name) + krb_cmd += " {0}".format(name) cmd = __execute_kadmin(krb_cmd) - if cmd['retcode'] != 0 or cmd['stderr']: - if not cmd['stderr'].splitlines()[-1].startswith('WARNING:'): - ret['comment'] = cmd['stderr'].splitlines()[-1] - ret['result'] = False + if cmd["retcode"] != 0 or cmd["stderr"]: + if not cmd["stderr"].splitlines()[-1].startswith("WARNING:"): + ret["comment"] = cmd["stderr"].splitlines()[-1] + ret["result"] = False return ret @@ -232,7 +231,7 @@ def create_principal(name, enctypes=None): def delete_principal(name): - ''' + """ Delete Principal CLI Example: @@ -240,14 +239,14 @@ def delete_principal(name): .. code-block:: bash salt 'kdc.example.com' kerberos.delete_principal host/example.com@EXAMPLE.COM - ''' + """ ret = {} - cmd = __execute_kadmin('delprinc -force {0}'.format(name)) + cmd = __execute_kadmin("delprinc -force {0}".format(name)) - if cmd['retcode'] != 0 or cmd['stderr']: - ret['comment'] = cmd['stderr'].splitlines()[-1] - ret['result'] = False + if cmd["retcode"] != 0 or cmd["stderr"]: + ret["comment"] = cmd["stderr"].splitlines()[-1] + ret["result"] = False return ret @@ -255,7 +254,7 @@ def delete_principal(name): def create_keytab(name, keytab, enctypes=None): - ''' + """ Create keytab CLI Example: @@ -263,21 +262,21 @@ def create_keytab(name, keytab, enctypes=None): .. code-block:: bash salt 'kdc.example.com' kerberos.create_keytab host/host1.example.com host1.example.com.keytab - ''' + """ ret = {} - krb_cmd = 'ktadd -k {0}'.format(keytab) + krb_cmd = "ktadd -k {0}".format(keytab) if enctypes: - krb_cmd += ' -e {0}'.format(enctypes) + krb_cmd += " -e {0}".format(enctypes) - krb_cmd += ' {0}'.format(name) + krb_cmd += " {0}".format(name) cmd = __execute_kadmin(krb_cmd) - if cmd['retcode'] != 0 or cmd['stderr']: - ret['comment'] = cmd['stderr'].splitlines()[-1] - ret['result'] = False + if cmd["retcode"] != 0 or cmd["stderr"]: + ret["comment"] = cmd["stderr"].splitlines()[-1] + ret["result"] = False return ret diff --git a/salt/modules/kernelpkg_linux_apt.py b/salt/modules/kernelpkg_linux_apt.py index 2adaab49a3c..b0119b687c8 100644 --- a/salt/modules/kernelpkg_linux_apt.py +++ b/salt/modules/kernelpkg_linux_apt.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Manage Linux kernel packages on APT-based systems -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import functools import logging import re @@ -11,8 +12,9 @@ try: # Import Salt libs from salt.ext import six from salt.utils.versions import LooseVersion as _LooseVersion - from salt.ext.six.moves import filter # pylint: disable=import-error,redefined-builtin + from salt.ext.six.moves import filter from salt.exceptions import CommandExecutionError + HAS_REQUIRED_LIBS = True except ImportError: HAS_REQUIRED_LIBS = False @@ -20,27 +22,27 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'kernelpkg' +__virtualname__ = "kernelpkg" def __virtual__(): - ''' + """ Load this module on Debian-based systems only - ''' + """ if not HAS_REQUIRED_LIBS: return (False, "Required library could not be imported") - if __grains__.get('os_family', '') in ('Kali', 'Debian'): + if __grains__.get("os_family", "") in ("Kali", "Debian"): return __virtualname__ - elif __grains__.get('os_family', '') == 'Cumulus': + elif __grains__.get("os_family", "") == "Cumulus": return __virtualname__ return (False, "Module kernelpkg_linux_apt: no APT based system detected") def active(): - ''' + """ Return the version of the running kernel. CLI Example: @@ -48,15 +50,15 @@ def active(): .. code-block:: bash salt '*' kernelpkg.active - ''' - if 'pkg.normalize_name' in __salt__: - return __salt__['pkg.normalize_name'](__grains__['kernelrelease']) + """ + if "pkg.normalize_name" in __salt__: + return __salt__["pkg.normalize_name"](__grains__["kernelrelease"]) - return __grains__['kernelrelease'] + return __grains__["kernelrelease"] def list_installed(): - ''' + """ Return a list of all installed kernels. CLI Example: @@ -64,10 +66,9 @@ def list_installed(): .. code-block:: bash salt '*' kernelpkg.list_installed - ''' - pkg_re = re.compile(r'^{0}-[\d.-]+-{1}$'.format( - _package_prefix(), _kernel_type())) - pkgs = __salt__['pkg.list_pkgs'](versions_as_list=True) + """ + pkg_re = re.compile(r"^{0}-[\d.-]+-{1}$".format(_package_prefix(), _kernel_type())) + pkgs = __salt__["pkg.list_pkgs"](versions_as_list=True) if pkgs is None: pkgs = [] @@ -80,11 +81,13 @@ def list_installed(): if six.PY2: return sorted([pkg[prefix_len:] for pkg in result], cmp=_cmp_version) else: - return sorted([pkg[prefix_len:] for pkg in result], key=functools.cmp_to_key(_cmp_version)) + return sorted( + [pkg[prefix_len:] for pkg in result], key=functools.cmp_to_key(_cmp_version) + ) def latest_available(): - ''' + """ Return the version of the latest kernel from the package repositories. CLI Example: @@ -92,19 +95,19 @@ def latest_available(): .. code-block:: bash salt '*' kernelpkg.latest_available - ''' - result = __salt__['pkg.latest_version']( - '{0}-{1}'.format(_package_prefix(), _kernel_type())) - if result == '': + """ + result = __salt__["pkg.latest_version"]( + "{0}-{1}".format(_package_prefix(), _kernel_type()) + ) + if result == "": return latest_installed() - version = re.match(r'^(\d+\.\d+\.\d+)\.(\d+)', result) - return '{0}-{1}-{2}'.format( - version.group(1), version.group(2), _kernel_type()) + version = re.match(r"^(\d+\.\d+\.\d+)\.(\d+)", result) + return "{0}-{1}-{2}".format(version.group(1), version.group(2), _kernel_type()) def latest_installed(): - ''' + """ Return the version of the latest installed kernel. CLI Example: @@ -119,7 +122,7 @@ def latest_installed(): has been installed and the system has not yet been rebooted. The :py:func:`~salt.modules.kernelpkg_linux_apt.needs_reboot` function exists to detect this condition. - ''' + """ pkgs = list_installed() if pkgs: return pkgs[-1] @@ -128,7 +131,7 @@ def latest_installed(): def needs_reboot(): - ''' + """ Detect if a new kernel version has been installed but is not running. Returns True if a new kernel is installed, False otherwise. @@ -137,12 +140,12 @@ def needs_reboot(): .. code-block:: bash salt '*' kernelpkg.needs_reboot - ''' + """ return _LooseVersion(active()) < _LooseVersion(latest_installed()) def upgrade(reboot=False, at_time=None): - ''' + """ Upgrade the kernel and optionally reboot the system. reboot : False @@ -165,28 +168,29 @@ def upgrade(reboot=False, at_time=None): An immediate reboot often shuts down the system before the minion has a chance to return, resulting in errors. A minimal delay (1 minute) is useful to ensure the result is delivered to the master. - ''' - result = __salt__['pkg.install']( - name='{0}-{1}'.format(_package_prefix(), latest_available())) + """ + result = __salt__["pkg.install"]( + name="{0}-{1}".format(_package_prefix(), latest_available()) + ) _needs_reboot = needs_reboot() ret = { - 'upgrades': result, - 'active': active(), - 'latest_installed': latest_installed(), - 'reboot_requested': reboot, - 'reboot_required': _needs_reboot + "upgrades": result, + "active": active(), + "latest_installed": latest_installed(), + "reboot_requested": reboot, + "reboot_required": _needs_reboot, } if reboot and _needs_reboot: - log.warning('Rebooting system due to kernel upgrade') - __salt__['system.reboot'](at_time=at_time) + log.warning("Rebooting system due to kernel upgrade") + __salt__["system.reboot"](at_time=at_time) return ret def upgrade_available(): - ''' + """ Detect if a new kernel version is available in the repositories. Returns True if a new kernel is available, False otherwise. @@ -195,12 +199,12 @@ def upgrade_available(): .. code-block:: bash salt '*' kernelpkg.upgrade_available - ''' + """ return _LooseVersion(latest_available()) > _LooseVersion(latest_installed()) def remove(release): - ''' + """ Remove a specific version of the kernel. release @@ -213,23 +217,25 @@ def remove(release): .. code-block:: bash salt '*' kernelpkg.remove 4.4.0-70-generic - ''' + """ if release not in list_installed(): - raise CommandExecutionError('Kernel release \'{0}\' is not installed'.format(release)) + raise CommandExecutionError( + "Kernel release '{0}' is not installed".format(release) + ) if release == active(): - raise CommandExecutionError('Active kernel cannot be removed') + raise CommandExecutionError("Active kernel cannot be removed") - target = '{0}-{1}'.format(_package_prefix(), release) - log.info('Removing kernel package %s', target) + target = "{0}-{1}".format(_package_prefix(), release) + log.info("Removing kernel package %s", target) - __salt__['pkg.purge'](target) + __salt__["pkg.purge"](target) - return {'removed': [target]} + return {"removed": [target]} def cleanup(keep_latest=True): - ''' + """ Remove all unused kernel packages from the system. keep_latest : True @@ -242,7 +248,7 @@ def cleanup(keep_latest=True): .. code-block:: bash salt '*' kernelpkg.cleanup - ''' + """ removed = [] # Loop over all installed kernel packages @@ -257,29 +263,29 @@ def cleanup(keep_latest=True): continue # Remove the kernel package - removed.extend(remove(kernel)['removed']) + removed.extend(remove(kernel)["removed"]) - return {'removed': removed} + return {"removed": removed} def _package_prefix(): - ''' + """ Return static string for the package prefix - ''' - return 'linux-image' + """ + return "linux-image" def _kernel_type(): - ''' + """ Parse the kernel name and return its type - ''' - return re.match(r'^[\d.-]+-(.+)$', active()).group(1) + """ + return re.match(r"^[\d.-]+-(.+)$", active()).group(1) def _cmp_version(item1, item2): - ''' + """ Compare function for package version sorting - ''' + """ vers1 = _LooseVersion(item1) vers2 = _LooseVersion(item2) diff --git a/salt/modules/kernelpkg_linux_yum.py b/salt/modules/kernelpkg_linux_yum.py index 51b62028159..dc879db5e80 100644 --- a/salt/modules/kernelpkg_linux_yum.py +++ b/salt/modules/kernelpkg_linux_yum.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Manage Linux kernel packages on YUM-based systems -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import functools import logging @@ -15,6 +16,7 @@ try: import salt.utils.functools import salt.utils.systemd import salt.modules.yumpkg + __IMPORT_ERROR = None except ImportError as exc: __IMPORT_ERROR = exc.__str__() @@ -22,31 +24,37 @@ except ImportError as exc: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'kernelpkg' +__virtualname__ = "kernelpkg" # Import functions from yumpkg -_yum = salt.utils.functools.namespaced_function(salt.modules.yumpkg._yum, globals()) # pylint: disable=invalid-name, protected-access +# pylint: disable=invalid-name, protected-access +_yum = salt.utils.functools.namespaced_function(salt.modules.yumpkg._yum, globals()) +# pylint: enable=invalid-name, protected-access def __virtual__(): - ''' + """ Load this module on RedHat-based systems only - ''' + """ if __IMPORT_ERROR: return (False, __IMPORT_ERROR) - if __grains__.get('os_family', '') == 'RedHat': + if __grains__.get("os_family", "") == "RedHat": return __virtualname__ - elif __grains__.get('os', '').lower() \ - in ('amazon', 'xcp', 'xenserver', 'virtuozzolinux'): + elif __grains__.get("os", "").lower() in ( + "amazon", + "xcp", + "xenserver", + "virtuozzolinux", + ): return __virtualname__ return (False, "Module kernelpkg_linux_yum: no YUM based system detected") def active(): - ''' + """ Return the version of the running kernel. CLI Example: @@ -54,15 +62,15 @@ def active(): .. code-block:: bash salt '*' kernelpkg.active - ''' - if 'pkg.normalize_name' in __salt__: - return __salt__['pkg.normalize_name'](__grains__['kernelrelease']) + """ + if "pkg.normalize_name" in __salt__: + return __salt__["pkg.normalize_name"](__grains__["kernelrelease"]) - return __grains__['kernelrelease'] + return __grains__["kernelrelease"] def list_installed(): - ''' + """ Return a list of all installed kernels. CLI Example: @@ -70,8 +78,8 @@ def list_installed(): .. code-block:: bash salt '*' kernelpkg.list_installed - ''' - result = __salt__['pkg.version'](_package_name(), versions_as_list=True) + """ + result = __salt__["pkg.version"](_package_name(), versions_as_list=True) if result is None: return [] @@ -82,7 +90,7 @@ def list_installed(): def latest_available(): - ''' + """ Return the version of the latest kernel from the package repositories. CLI Example: @@ -90,15 +98,15 @@ def latest_available(): .. code-block:: bash salt '*' kernelpkg.latest_available - ''' - result = __salt__['pkg.latest_version'](_package_name()) - if result == '': + """ + result = __salt__["pkg.latest_version"](_package_name()) + if result == "": result = latest_installed() return result def latest_installed(): - ''' + """ Return the version of the latest installed kernel. CLI Example: @@ -113,7 +121,7 @@ def latest_installed(): has been installed and the system has not yet been rebooted. The :py:func:`~salt.modules.kernelpkg_linux_yum.needs_reboot` function exists to detect this condition. - ''' + """ pkgs = list_installed() if pkgs: return pkgs[-1] @@ -122,7 +130,7 @@ def latest_installed(): def needs_reboot(): - ''' + """ Detect if a new kernel version has been installed but is not running. Returns True if a new kernel is installed, False otherwise. @@ -131,12 +139,12 @@ def needs_reboot(): .. code-block:: bash salt '*' kernelpkg.needs_reboot - ''' + """ return _LooseVersion(active()) < _LooseVersion(latest_installed()) def upgrade(reboot=False, at_time=None): - ''' + """ Upgrade the kernel and optionally reboot the system. reboot : False @@ -159,27 +167,27 @@ def upgrade(reboot=False, at_time=None): An immediate reboot often shuts down the system before the minion has a chance to return, resulting in errors. A minimal delay (1 minute) is useful to ensure the result is delivered to the master. - ''' - result = __salt__['pkg.upgrade'](name=_package_name()) + """ + result = __salt__["pkg.upgrade"](name=_package_name()) _needs_reboot = needs_reboot() ret = { - 'upgrades': result, - 'active': active(), - 'latest_installed': latest_installed(), - 'reboot_requested': reboot, - 'reboot_required': _needs_reboot + "upgrades": result, + "active": active(), + "latest_installed": latest_installed(), + "reboot_requested": reboot, + "reboot_required": _needs_reboot, } if reboot and _needs_reboot: - log.warning('Rebooting system due to kernel upgrade') - __salt__['system.reboot'](at_time=at_time) + log.warning("Rebooting system due to kernel upgrade") + __salt__["system.reboot"](at_time=at_time) return ret def upgrade_available(): - ''' + """ Detect if a new kernel version is available in the repositories. Returns True if a new kernel is available, False otherwise. @@ -188,12 +196,12 @@ def upgrade_available(): .. code-block:: bash salt '*' kernelpkg.upgrade_available - ''' + """ return _LooseVersion(latest_available()) > _LooseVersion(latest_installed()) def remove(release): - ''' + """ Remove a specific version of the kernel. release @@ -206,48 +214,47 @@ def remove(release): .. code-block:: bash salt '*' kernelpkg.remove 3.10.0-327.el7 - ''' + """ if release not in list_installed(): - raise CommandExecutionError('Kernel release \'{0}\' is not installed'.format(release)) + raise CommandExecutionError( + "Kernel release '{0}' is not installed".format(release) + ) if release == active(): - raise CommandExecutionError('Active kernel cannot be removed') + raise CommandExecutionError("Active kernel cannot be removed") - target = '{0}-{1}'.format(_package_name(), release) - log.info('Removing kernel package %s', target) - old = __salt__['pkg.list_pkgs']() + target = "{0}-{1}".format(_package_name(), release) + log.info("Removing kernel package %s", target) + old = __salt__["pkg.list_pkgs"]() # Build the command string cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) - cmd.extend([_yum(), '-y', 'remove', target]) # pylint: disable=not-callable + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) + cmd.extend([_yum(), "-y", "remove", target]) # pylint: disable=not-callable # Execute the command - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False - ) + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) # Look for the changes in installed packages - __context__.pop('pkg.list_pkgs', None) - new = __salt__['pkg.list_pkgs']() + __context__.pop("pkg.list_pkgs", None) + new = __salt__["pkg.list_pkgs"]() ret = salt.utils.data.compare_dicts(old, new) # Look for command execution errors - if out['retcode'] != 0: + if out["retcode"] != 0: raise CommandExecutionError( - 'Error occurred removing package(s)', - info={'errors': [out['stderr']], 'changes': ret} + "Error occurred removing package(s)", + info={"errors": [out["stderr"]], "changes": ret}, ) - return {'removed': [target]} + return {"removed": [target]} def cleanup(keep_latest=True): - ''' + """ Remove all unused kernel packages from the system. keep_latest : True @@ -260,7 +267,7 @@ def cleanup(keep_latest=True): .. code-block:: bash salt '*' kernelpkg.cleanup - ''' + """ removed = [] # Loop over all installed kernel packages @@ -275,22 +282,22 @@ def cleanup(keep_latest=True): continue # Remove the kernel package - removed.extend(remove(kernel)['removed']) + removed.extend(remove(kernel)["removed"]) - return {'removed': removed} + return {"removed": removed} def _package_name(): - ''' + """ Return static string for the package name - ''' - return 'kernel' + """ + return "kernel" def _cmp_version(item1, item2): - ''' + """ Compare function for package version sorting - ''' + """ vers1 = _LooseVersion(item1) vers2 = _LooseVersion(item2) diff --git a/salt/modules/key.py b/salt/modules/key.py index d6d53291494..50ec8aa1d03 100644 --- a/salt/modules/key.py +++ b/salt/modules/key.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Functions to view the minion's public key information -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os @@ -12,7 +12,7 @@ import salt.utils.crypt def finger(hash_type=None): - ''' + """ Return the minion's public key fingerprint hash_type @@ -23,17 +23,17 @@ def finger(hash_type=None): .. code-block:: bash salt '*' key.finger - ''' + """ if hash_type is None: - hash_type = __opts__['hash_type'] + hash_type = __opts__["hash_type"] return salt.utils.crypt.pem_finger( - os.path.join(__opts__['pki_dir'], 'minion.pub'), - sum_type=hash_type) + os.path.join(__opts__["pki_dir"], "minion.pub"), sum_type=hash_type + ) def finger_master(hash_type=None): - ''' + """ Return the fingerprint of the master's public key on the minion. hash_type @@ -44,10 +44,10 @@ def finger_master(hash_type=None): .. code-block:: bash salt '*' key.finger_master - ''' + """ if hash_type is None: - hash_type = __opts__['hash_type'] + hash_type = __opts__["hash_type"] return salt.utils.crypt.pem_finger( - os.path.join(__opts__['pki_dir'], 'minion_master.pub'), - sum_type=hash_type) + os.path.join(__opts__["pki_dir"], "minion_master.pub"), sum_type=hash_type + ) diff --git a/salt/modules/keyboard.py b/salt/modules/keyboard.py index 707dd72f955..0aef7021143 100644 --- a/salt/modules/keyboard.py +++ b/salt/modules/keyboard.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing keyboards on supported POSIX-like systems using systemd, or such as Redhat, Debian and Gentoo. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -15,18 +16,24 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only works with systemd or on supported POSIX-like systems - ''' - if salt.utils.path.which('localectl') \ - or __grains__['os_family'] in ('RedHat', 'Debian', 'Gentoo'): + """ + if salt.utils.path.which("localectl") or __grains__["os_family"] in ( + "RedHat", + "Debian", + "Gentoo", + ): return True - return (False, 'The keyboard exeuction module cannot be loaded: ' - 'only works on Redhat, Debian or Gentoo systems or if localectl binary in path.') + return ( + False, + "The keyboard exeuction module cannot be loaded: " + "only works on Redhat, Debian or Gentoo systems or if localectl binary in path.", + ) def get_sys(): - ''' + """ Get current system keyboard setting CLI Example: @@ -34,23 +41,23 @@ def get_sys(): .. code-block:: bash salt '*' keyboard.get_sys - ''' - cmd = '' - if salt.utils.path.which('localectl'): + """ + cmd = "" + if salt.utils.path.which("localectl"): cmd = 'localectl | grep Keymap | sed -e"s/: /=/" -e"s/^[ \t]*//"' - elif 'RedHat' in __grains__['os_family']: + elif "RedHat" in __grains__["os_family"]: cmd = 'grep LAYOUT /etc/sysconfig/keyboard | grep -vE "^#"' - elif 'Debian' in __grains__['os_family']: + elif "Debian" in __grains__["os_family"]: cmd = 'grep XKBLAYOUT /etc/default/keyboard | grep -vE "^#"' - elif 'Gentoo' in __grains__['os_family']: + elif "Gentoo" in __grains__["os_family"]: cmd = 'grep "^keymap" /etc/conf.d/keymaps | grep -vE "^#"' - out = __salt__['cmd.run'](cmd, python_shell=True).split('=') - ret = out[1].replace('"', '') + out = __salt__["cmd.run"](cmd, python_shell=True).split("=") + ret = out[1].replace('"', "") return ret def set_sys(layout): - ''' + """ Set current system keyboard setting CLI Example: @@ -58,26 +65,26 @@ def set_sys(layout): .. code-block:: bash salt '*' keyboard.set_sys dvorak - ''' - if salt.utils.path.which('localectl'): - __salt__['cmd.run']('localectl set-keymap {0}'.format(layout)) - elif 'RedHat' in __grains__['os_family']: - __salt__['file.sed']('/etc/sysconfig/keyboard', - '^LAYOUT=.*', - 'LAYOUT={0}'.format(layout)) - elif 'Debian' in __grains__['os_family']: - __salt__['file.sed']('/etc/default/keyboard', - '^XKBLAYOUT=.*', - 'XKBLAYOUT={0}'.format(layout)) - elif 'Gentoo' in __grains__['os_family']: - __salt__['file.sed']('/etc/conf.d/keymaps', - '^keymap=.*', - 'keymap={0}'.format(layout)) + """ + if salt.utils.path.which("localectl"): + __salt__["cmd.run"]("localectl set-keymap {0}".format(layout)) + elif "RedHat" in __grains__["os_family"]: + __salt__["file.sed"]( + "/etc/sysconfig/keyboard", "^LAYOUT=.*", "LAYOUT={0}".format(layout) + ) + elif "Debian" in __grains__["os_family"]: + __salt__["file.sed"]( + "/etc/default/keyboard", "^XKBLAYOUT=.*", "XKBLAYOUT={0}".format(layout) + ) + elif "Gentoo" in __grains__["os_family"]: + __salt__["file.sed"]( + "/etc/conf.d/keymaps", "^keymap=.*", "keymap={0}".format(layout) + ) return layout def get_x(): - ''' + """ Get current X keyboard setting CLI Example: @@ -85,14 +92,14 @@ def get_x(): .. code-block:: bash salt '*' keyboard.get_x - ''' - cmd = 'setxkbmap -query | grep layout' - out = __salt__['cmd.run'](cmd, python_shell=True).split(':') + """ + cmd = "setxkbmap -query | grep layout" + out = __salt__["cmd.run"](cmd, python_shell=True).split(":") return out[1].strip() def set_x(layout): - ''' + """ Set current X keyboard setting CLI Example: @@ -100,7 +107,7 @@ def set_x(layout): .. code-block:: bash salt '*' keyboard.set_x dvorak - ''' - cmd = 'setxkbmap {0}'.format(layout) - __salt__['cmd.run'](cmd) + """ + cmd = "setxkbmap {0}".format(layout) + __salt__["cmd.run"](cmd) return layout diff --git a/salt/modules/keystone.py b/salt/modules/keystone.py index b929be25ec5..52cb461339a 100644 --- a/salt/modules/keystone.py +++ b/salt/modules/keystone.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for handling openstack keystone calls. :optdepends: - keystoneclient Python adapter @@ -47,10 +47,11 @@ Module for handling openstack keystone calls. .. code-block:: bash salt '*' keystone.tenant_list profile=openstack1 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs @@ -58,85 +59,93 @@ import salt.utils.http # Import 3rd-party libs from salt.ext import six + HAS_KEYSTONE = False try: # pylint: disable=import-error from keystoneclient.v2_0 import client import keystoneclient.exceptions + HAS_KEYSTONE = True from keystoneclient.v3 import client as client3 from keystoneclient import discover from keystoneauth1 import session from keystoneauth1.identity import generic + # pylint: enable=import-error except ImportError: pass _OS_IDENTITY_API_VERSION = 2 -_TENANTS = 'tenants' +_TENANTS = "tenants" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if keystone is installed on this minion. - ''' + """ if HAS_KEYSTONE: - return 'keystone' - return (False, 'keystone execution module cannot be loaded: keystoneclient python library not available.') + return "keystone" + return ( + False, + "keystone execution module cannot be loaded: keystoneclient python library not available.", + ) __opts__ = {} def _get_kwargs(profile=None, **connection_args): - ''' + """ get connection args - ''' + """ if profile: prefix = profile + ":keystone." else: prefix = "keystone." def get(key, default=None): - ''' + """ look in connection_args first, then default to config file - ''' - return connection_args.get('connection_' + key, - __salt__['config.get'](prefix + key, default)) + """ + return connection_args.get( + "connection_" + key, __salt__["config.get"](prefix + key, default) + ) - user = get('user', 'admin') - password = get('password', 'ADMIN') - tenant = get('tenant', 'admin') - tenant_id = get('tenant_id') - auth_url = get('auth_url', 'http://127.0.0.1:35357/v2.0/') - insecure = get('insecure', False) - token = get('token') - 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') + user = get("user", "admin") + password = get("password", "ADMIN") + tenant = get("tenant", "admin") + tenant_id = get("tenant_id") + auth_url = get("auth_url", "http://127.0.0.1:35357/v2.0/") + insecure = get("insecure", False) + token = get("token") + 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") if token: - kwargs = {'token': token, - 'endpoint': endpoint} + kwargs = {"token": token, "endpoint": endpoint} else: - kwargs = {'username': user, - 'password': password, - 'tenant_name': tenant, - 'tenant_id': tenant_id, - 'auth_url': auth_url, - 'user_domain_name': user_domain_name, - 'project_domain_name': project_domain_name} + kwargs = { + "username": user, + "password": password, + "tenant_name": tenant, + "tenant_id": tenant_id, + "auth_url": auth_url, + "user_domain_name": user_domain_name, + "project_domain_name": project_domain_name, + } # 'insecure' keyword not supported by all v2.0 keystone clients # this ensures it's only passed in when defined if insecure: - kwargs['insecure'] = True + kwargs["insecure"] = True return kwargs def api_version(profile=None, **connection_args): - ''' + """ Returns the API version derived from endpoint's response. CLI Example: @@ -144,18 +153,19 @@ def api_version(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.api_version - ''' + """ kwargs = _get_kwargs(profile=profile, **connection_args) - auth_url = kwargs.get('auth_url', kwargs.get('endpoint', None)) + 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)['dict']['version']['id'] + return salt.utils.http.query( + auth_url, decode=True, decode_type="json", verify_ssl=False + )["dict"]["version"]["id"] except KeyError: return None def auth(profile=None, **connection_args): - ''' + """ Set up keystone credentials. Only intended to be used within Keystone-enabled modules. CLI Example: @@ -163,39 +173,44 @@ def auth(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.auth - ''' - __utils__['versions.warn_until']( - 'Sodium', + """ + __utils__["versions.warn_until"]( + "Sodium", ( - 'The keystone module has been deprecated and will be removed in {version}. ' - 'Please update to using the keystoneng module' + "The keystone module has been deprecated and will be removed in {version}. " + "Please update to using the keystoneng module" ), ) kwargs = _get_kwargs(profile=profile, **connection_args) - disc = discover.Discover(auth_url=kwargs['auth_url']) - v2_auth_url = disc.url_for('v2.0') - v3_auth_url = disc.url_for('v3.0') + disc = discover.Discover(auth_url=kwargs["auth_url"]) + v2_auth_url = disc.url_for("v2.0") + v3_auth_url = disc.url_for("v3.0") if v3_auth_url: global _OS_IDENTITY_API_VERSION global _TENANTS _OS_IDENTITY_API_VERSION = 3 - _TENANTS = 'projects' - kwargs['auth_url'] = v3_auth_url + _TENANTS = "projects" + kwargs["auth_url"] = v3_auth_url else: - kwargs['auth_url'] = v2_auth_url - kwargs.pop('user_domain_name') - kwargs.pop('project_domain_name') + kwargs["auth_url"] = v2_auth_url + kwargs.pop("user_domain_name") + kwargs.pop("project_domain_name") auth = generic.Password(**kwargs) sess = session.Session(auth=auth) ks_cl = disc.create_client(session=sess) return ks_cl -def ec2_credentials_create(user_id=None, name=None, - tenant_id=None, tenant=None, - profile=None, **connection_args): - ''' +def ec2_credentials_create( + user_id=None, + name=None, + tenant_id=None, + tenant=None, + profile=None, + **connection_args +): + """ Create EC2-compatible credentials for user per tenant CLI Examples: @@ -207,31 +222,34 @@ def ec2_credentials_create(user_id=None, name=None, salt '*' keystone.ec2_credentials_create \ user_id=c965f79c4f864eaaa9c3b41904e67082 \ tenant_id=722787eb540849158668370dc627ec5f - ''' + """ kstone = auth(profile, **connection_args) if name: - user_id = user_get(name=name, profile=profile, - **connection_args)[name]['id'] + user_id = user_get(name=name, profile=profile, **connection_args)[name]["id"] if not user_id: - return {'Error': 'Could not resolve User ID'} + return {"Error": "Could not resolve User ID"} if tenant: - tenant_id = tenant_get(name=tenant, profile=profile, - **connection_args)[tenant]['id'] + tenant_id = tenant_get(name=tenant, profile=profile, **connection_args)[tenant][ + "id" + ] if not tenant_id: - return {'Error': 'Could not resolve Tenant ID'} + return {"Error": "Could not resolve Tenant ID"} newec2 = kstone.ec2.create(user_id, tenant_id) - return {'access': newec2.access, - 'secret': newec2.secret, - 'tenant_id': newec2.tenant_id, - 'user_id': newec2.user_id} + return { + "access": newec2.access, + "secret": newec2.secret, + "tenant_id": newec2.tenant_id, + "user_id": newec2.user_id, + } -def ec2_credentials_delete(user_id=None, name=None, access_key=None, - profile=None, **connection_args): - ''' +def ec2_credentials_delete( + user_id=None, name=None, access_key=None, profile=None, **connection_args +): + """ Delete EC2-compatible credentials CLI Examples: @@ -243,21 +261,21 @@ def ec2_credentials_delete(user_id=None, name=None, access_key=None, salt '*' keystone.ec2_credentials_delete name=admin \ access_key=5f66d2f24f604b8bb9cd28886106f442 - ''' + """ kstone = auth(profile, **connection_args) if name: - user_id = user_get(name=name, profile=None, **connection_args)[name]['id'] + user_id = user_get(name=name, profile=None, **connection_args)[name]["id"] if not user_id: - return {'Error': 'Could not resolve User ID'} + return {"Error": "Could not resolve User ID"} kstone.ec2.delete(user_id, access_key) - return 'ec2 key "{0}" deleted under user id "{1}"'.format(access_key, - user_id) + return 'ec2 key "{0}" deleted under user id "{1}"'.format(access_key, user_id) -def ec2_credentials_get(user_id=None, name=None, access=None, - profile=None, **connection_args): - ''' +def ec2_credentials_get( + user_id=None, name=None, access=None, profile=None, **connection_args +): + """ Return ec2_credentials for a user (keystone ec2-credentials-get) CLI Examples: @@ -267,7 +285,7 @@ def ec2_credentials_get(user_id=None, name=None, access=None, salt '*' keystone.ec2_credentials_get c965f79c4f864eaaa9c3b41904e67082 access=722787eb540849158668370 salt '*' keystone.ec2_credentials_get user_id=c965f79c4f864eaaa9c3b41904e67082 access=722787eb540849158668370 salt '*' keystone.ec2_credentials_get name=nova access=722787eb540849158668370dc627ec5f - ''' + """ kstone = auth(profile, **connection_args) ret = {} if name: @@ -276,21 +294,23 @@ def ec2_credentials_get(user_id=None, name=None, access=None, user_id = user.id break if not user_id: - return {'Error': 'Unable to resolve user id'} + return {"Error": "Unable to resolve user id"} if not access: - return {'Error': 'Access key is required'} - ec2_credentials = kstone.ec2.get(user_id=user_id, access=access, - profile=profile, **connection_args) - ret[ec2_credentials.user_id] = {'user_id': ec2_credentials.user_id, - 'tenant': ec2_credentials.tenant_id, - 'access': ec2_credentials.access, - 'secret': ec2_credentials.secret} + return {"Error": "Access key is required"} + ec2_credentials = kstone.ec2.get( + user_id=user_id, access=access, profile=profile, **connection_args + ) + ret[ec2_credentials.user_id] = { + "user_id": ec2_credentials.user_id, + "tenant": ec2_credentials.tenant_id, + "access": ec2_credentials.access, + "secret": ec2_credentials.secret, + } return ret -def ec2_credentials_list(user_id=None, name=None, profile=None, - **connection_args): - ''' +def ec2_credentials_list(user_id=None, name=None, profile=None, **connection_args): + """ Return a list of ec2_credentials for a specific user (keystone ec2-credentials-list) CLI Examples: @@ -300,7 +320,7 @@ def ec2_credentials_list(user_id=None, name=None, profile=None, salt '*' keystone.ec2_credentials_list 298ce377245c4ec9b70e1c639c89e654 salt '*' keystone.ec2_credentials_list user_id=298ce377245c4ec9b70e1c639c89e654 salt '*' keystone.ec2_credentials_list name=jack - ''' + """ kstone = auth(profile, **connection_args) ret = {} if name: @@ -309,17 +329,19 @@ def ec2_credentials_list(user_id=None, name=None, profile=None, user_id = user.id break if not user_id: - return {'Error': 'Unable to resolve user id'} + return {"Error": "Unable to resolve user id"} for ec2_credential in kstone.ec2.list(user_id): - ret[ec2_credential.user_id] = {'user_id': ec2_credential.user_id, - 'tenant_id': ec2_credential.tenant_id, - 'access': ec2_credential.access, - 'secret': ec2_credential.secret} + ret[ec2_credential.user_id] = { + "user_id": ec2_credential.user_id, + "tenant_id": ec2_credential.tenant_id, + "access": ec2_credential.access, + "secret": ec2_credential.secret, + } return ret def endpoint_get(service, region=None, profile=None, interface=None, **connection_args): - ''' + """ Return a specific endpoint (keystone endpoint-get) CLI Example: @@ -329,28 +351,39 @@ def endpoint_get(service, region=None, profile=None, interface=None, **connectio salt 'v2' keystone.endpoint_get nova [region=RegionOne] salt 'v3' keystone.endpoint_get nova interface=admin [region=RegionOne] - ''' + """ auth(profile, **connection_args) services = service_list(profile, **connection_args) if service not in services: - return {'Error': 'Could not find the specified service'} - service_id = services[service]['id'] + return {"Error": "Could not find the specified service"} + service_id = services[service]["id"] endpoints = endpoint_list(profile, **connection_args) - e = [_f for _f in [e - if e['service_id'] == service_id and - (e['region'] == region if region else True) and - (e['interface'] == interface if interface else True) - else None for e in endpoints.values()] if _f] + e = [ + _f + for _f in [ + e + if e["service_id"] == service_id + and (e["region"] == region if region else True) + and (e["interface"] == interface if interface else True) + else None + for e in endpoints.values() + ] + if _f + ] if len(e) > 1: - return {'Error': 'Multiple endpoints found ({0}) for the {1} service. Please specify region.'.format(e, service)} + return { + "Error": "Multiple endpoints found ({0}) for the {1} service. Please specify region.".format( + e, service + ) + } if len(e) == 1: return e[0] - return {'Error': 'Could not find endpoint for the specified service'} + return {"Error": "Could not find endpoint for the specified service"} def endpoint_list(profile=None, **connection_args): - ''' + """ Return a list of available endpoints (keystone endpoints-list) CLI Example: @@ -358,20 +391,32 @@ def endpoint_list(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.endpoint_list - ''' + """ kstone = auth(profile, **connection_args) ret = {} for endpoint in kstone.endpoints.list(): - ret[endpoint.id] = dict((value, getattr(endpoint, value)) for value in dir(endpoint) - if not value.startswith('_') and - isinstance(getattr(endpoint, value), (six.string_types, dict, bool))) + ret[endpoint.id] = dict( + (value, getattr(endpoint, value)) + for value in dir(endpoint) + if not value.startswith("_") + and isinstance(getattr(endpoint, value), (six.string_types, dict, bool)) + ) return ret -def endpoint_create(service, publicurl=None, internalurl=None, adminurl=None, - region=None, profile=None, url=None, interface=None, **connection_args): - ''' +def endpoint_create( + service, + publicurl=None, + internalurl=None, + adminurl=None, + region=None, + profile=None, + url=None, + interface=None, + **connection_args +): + """ Create an endpoint for an Openstack service CLI Examples: @@ -381,29 +426,34 @@ def endpoint_create(service, publicurl=None, internalurl=None, adminurl=None, salt 'v2' keystone.endpoint_create nova 'http://public/url' 'http://internal/url' 'http://adminurl/url' region salt 'v3' keystone.endpoint_create nova url='http://public/url' interface='public' region='RegionOne' - ''' + """ kstone = auth(profile, **connection_args) - keystone_service = service_get(name=service, profile=profile, - **connection_args) - if not keystone_service or 'Error' in keystone_service: - return {'Error': 'Could not find the specified service'} + keystone_service = service_get(name=service, profile=profile, **connection_args) + if not keystone_service or "Error" in keystone_service: + return {"Error": "Could not find the specified service"} if _OS_IDENTITY_API_VERSION > 2: - kstone.endpoints.create(service=keystone_service[service]['id'], - region_id=region, - url=url, - interface=interface) + kstone.endpoints.create( + service=keystone_service[service]["id"], + region_id=region, + url=url, + interface=interface, + ) else: - kstone.endpoints.create(region=region, - service_id=keystone_service[service]['id'], - publicurl=publicurl, - adminurl=adminurl, - internalurl=internalurl) + kstone.endpoints.create( + region=region, + service_id=keystone_service[service]["id"], + publicurl=publicurl, + adminurl=adminurl, + internalurl=internalurl, + ) return endpoint_get(service, region, profile, interface, **connection_args) -def endpoint_delete(service, region=None, profile=None, interface=None, **connection_args): - ''' +def endpoint_delete( + service, region=None, profile=None, interface=None, **connection_args +): + """ Delete endpoints of an Openstack service CLI Examples: @@ -413,19 +463,19 @@ def endpoint_delete(service, region=None, profile=None, interface=None, **connec salt 'v2' keystone.endpoint_delete nova [region=RegionOne] salt 'v3' keystone.endpoint_delete nova interface=admin [region=RegionOne] - ''' + """ kstone = auth(profile, **connection_args) endpoint = endpoint_get(service, region, profile, interface, **connection_args) - if not endpoint or 'Error' in endpoint: - return {'Error': 'Could not find any endpoints for the service'} - kstone.endpoints.delete(endpoint['id']) + if not endpoint or "Error" in endpoint: + return {"Error": "Could not find any endpoints for the service"} + kstone.endpoints.delete(endpoint["id"]) endpoint = endpoint_get(service, region, profile, interface, **connection_args) - if not endpoint or 'Error' in endpoint: + if not endpoint or "Error" in endpoint: return True def role_create(name, profile=None, **connection_args): - ''' + """ Create a named role. CLI Example: @@ -433,18 +483,17 @@ def role_create(name, profile=None, **connection_args): .. code-block:: bash salt '*' keystone.role_create admin - ''' + """ kstone = auth(profile, **connection_args) - if 'Error' not in role_get(name=name, profile=profile, **connection_args): - return {'Error': 'Role "{0}" already exists'.format(name)} + if "Error" not in role_get(name=name, profile=profile, **connection_args): + return {"Error": 'Role "{0}" already exists'.format(name)} kstone.roles.create(name) return role_get(name=name, profile=profile, **connection_args) -def role_delete(role_id=None, name=None, profile=None, - **connection_args): - ''' +def role_delete(role_id=None, name=None, profile=None, **connection_args): + """ Delete a role (keystone role-delete) @@ -455,7 +504,7 @@ def role_delete(role_id=None, name=None, profile=None, salt '*' keystone.role_delete c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.role_delete role_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.role_delete name=admin - ''' + """ kstone = auth(profile, **connection_args) if name: @@ -464,19 +513,19 @@ def role_delete(role_id=None, name=None, profile=None, role_id = role.id break if not role_id: - return {'Error': 'Unable to resolve role id'} + return {"Error": "Unable to resolve role id"} role = kstone.roles.get(role_id) kstone.roles.delete(role) - ret = 'Role ID {0} deleted'.format(role_id) + ret = "Role ID {0} deleted".format(role_id) if name: - ret += ' ({0})'.format(name) + ret += " ({0})".format(name) return ret def role_get(role_id=None, name=None, profile=None, **connection_args): - ''' + """ Return a specific roles (keystone role-get) CLI Examples: @@ -486,7 +535,7 @@ def role_get(role_id=None, name=None, profile=None, **connection_args): salt '*' keystone.role_get c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.role_get role_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.role_get name=nova - ''' + """ kstone = auth(profile, **connection_args) ret = {} if name: @@ -495,16 +544,15 @@ def role_get(role_id=None, name=None, profile=None, **connection_args): role_id = role.id break if not role_id: - return {'Error': 'Unable to resolve role id'} + return {"Error": "Unable to resolve role id"} role = kstone.roles.get(role_id) - ret[role.name] = {'id': role.id, - 'name': role.name} + ret[role.name] = {"id": role.id, "name": role.name} return ret def role_list(profile=None, **connection_args): - ''' + """ Return a list of available roles (keystone role-list) CLI Example: @@ -512,19 +560,23 @@ def role_list(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.role_list - ''' + """ kstone = auth(profile, **connection_args) ret = {} for role in kstone.roles.list(): - ret[role.name] = dict((value, getattr(role, value)) for value in dir(role) - if not value.startswith('_') and - isinstance(getattr(role, value), (six.string_types, dict, bool))) + ret[role.name] = dict( + (value, getattr(role, value)) + for value in dir(role) + if not value.startswith("_") + and isinstance(getattr(role, value), (six.string_types, dict, bool)) + ) return ret -def service_create(name, service_type, description=None, profile=None, - **connection_args): - ''' +def service_create( + name, service_type, description=None, profile=None, **connection_args +): + """ Add service to Keystone service catalog CLI Examples: @@ -533,14 +585,14 @@ def service_create(name, service_type, description=None, profile=None, salt '*' keystone.service_create nova compute \ 'OpenStack Compute Service' - ''' + """ kstone = auth(profile, **connection_args) service = kstone.services.create(name, service_type, description=description) return service_get(service.id, profile=profile, **connection_args) def service_delete(service_id=None, name=None, profile=None, **connection_args): - ''' + """ Delete a service from Keystone service catalog CLI Examples: @@ -549,17 +601,18 @@ def service_delete(service_id=None, name=None, profile=None, **connection_args): salt '*' keystone.service_delete c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.service_delete name=nova - ''' + """ kstone = auth(profile, **connection_args) if name: - service_id = service_get(name=name, profile=profile, - **connection_args)[name]['id'] + service_id = service_get(name=name, profile=profile, **connection_args)[name][ + "id" + ] kstone.services.delete(service_id) return 'Keystone service ID "{0}" deleted'.format(service_id) def service_get(service_id=None, name=None, profile=None, **connection_args): - ''' + """ Return a specific services (keystone service-get) CLI Examples: @@ -569,7 +622,7 @@ def service_get(service_id=None, name=None, profile=None, **connection_args): salt '*' keystone.service_get c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.service_get service_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.service_get name=nova - ''' + """ kstone = auth(profile, **connection_args) ret = {} if name: @@ -578,16 +631,19 @@ def service_get(service_id=None, name=None, profile=None, **connection_args): service_id = service.id break if not service_id: - return {'Error': 'Unable to resolve service id'} + return {"Error": "Unable to resolve service id"} service = kstone.services.get(service_id) - ret[service.name] = dict((value, getattr(service, value)) for value in dir(service) - if not value.startswith('_') and - isinstance(getattr(service, value), (six.string_types, dict, bool))) + ret[service.name] = dict( + (value, getattr(service, value)) + for value in dir(service) + if not value.startswith("_") + and isinstance(getattr(service, value), (six.string_types, dict, bool)) + ) return ret def service_list(profile=None, **connection_args): - ''' + """ Return a list of available services (keystone services-list) CLI Example: @@ -595,19 +651,23 @@ def service_list(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.service_list - ''' + """ kstone = auth(profile, **connection_args) ret = {} for service in kstone.services.list(): - ret[service.name] = dict((value, getattr(service, value)) for value in dir(service) - if not value.startswith('_') and - isinstance(getattr(service, value), (six.string_types, dict, bool))) + ret[service.name] = dict( + (value, getattr(service, value)) + for value in dir(service) + if not value.startswith("_") + and isinstance(getattr(service, value), (six.string_types, dict, bool)) + ) return ret -def tenant_create(name, description=None, enabled=True, profile=None, - **connection_args): - ''' +def tenant_create( + name, description=None, enabled=True, profile=None, **connection_args +): + """ Create a keystone tenant CLI Examples: @@ -616,15 +676,16 @@ def tenant_create(name, description=None, enabled=True, profile=None, salt '*' keystone.tenant_create nova description='nova tenant' salt '*' keystone.tenant_create test enabled=False - ''' + """ kstone = auth(profile, **connection_args) new = getattr(kstone, _TENANTS, None).create(name, description, enabled) return tenant_get(new.id, profile=profile, **connection_args) -def project_create(name, domain, description=None, enabled=True, profile=None, - **connection_args): - ''' +def project_create( + name, domain, description=None, enabled=True, profile=None, **connection_args +): + """ Create a keystone project. Overrides keystone tenant_create form api V2. For keystone api V3. @@ -651,15 +712,16 @@ def project_create(name, domain, description=None, enabled=True, profile=None, salt '*' keystone.project_create nova default description='Nova Compute Project' salt '*' keystone.project_create test default enabled=False - ''' + """ kstone = auth(profile, **connection_args) - new = getattr(kstone, _TENANTS, None).create(name=name, domain=domain, - description=description, enabled=enabled) + new = getattr(kstone, _TENANTS, None).create( + name=name, domain=domain, description=description, enabled=enabled + ) return tenant_get(new.id, profile=profile, **connection_args) def tenant_delete(tenant_id=None, name=None, profile=None, **connection_args): - ''' + """ Delete a tenant (keystone tenant-delete) CLI Examples: @@ -669,7 +731,7 @@ def tenant_delete(tenant_id=None, name=None, profile=None, **connection_args): salt '*' keystone.tenant_delete c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.tenant_delete tenant_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.tenant_delete name=demo - ''' + """ kstone = auth(profile, **connection_args) if name: for tenant in getattr(kstone, _TENANTS, None).list(): @@ -677,17 +739,17 @@ def tenant_delete(tenant_id=None, name=None, profile=None, **connection_args): tenant_id = tenant.id break if not tenant_id: - return {'Error': 'Unable to resolve tenant id'} + return {"Error": "Unable to resolve tenant id"} getattr(kstone, _TENANTS, None).delete(tenant_id) - ret = 'Tenant ID {0} deleted'.format(tenant_id) + ret = "Tenant ID {0} deleted".format(tenant_id) if name: - ret += ' ({0})'.format(name) + ret += " ({0})".format(name) return ret def project_delete(project_id=None, name=None, profile=None, **connection_args): - ''' + """ Delete a project (keystone project-delete). Overrides keystone tenant-delete form api V2. For keystone api V3 only. @@ -709,18 +771,19 @@ def project_delete(project_id=None, name=None, profile=None, **connection_args): salt '*' keystone.project_delete c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.project_delete project_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.project_delete name=demo - ''' + """ auth(profile, **connection_args) if _OS_IDENTITY_API_VERSION > 2: - return tenant_delete(tenant_id=project_id, name=name, profile=None, **connection_args) + return tenant_delete( + tenant_id=project_id, name=name, profile=None, **connection_args + ) else: return False -def tenant_get(tenant_id=None, name=None, profile=None, - **connection_args): - ''' +def tenant_get(tenant_id=None, name=None, profile=None, **connection_args): + """ Return a specific tenants (keystone tenant-get) CLI Examples: @@ -730,7 +793,7 @@ def tenant_get(tenant_id=None, name=None, profile=None, salt '*' keystone.tenant_get c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.tenant_get tenant_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.tenant_get name=nova - ''' + """ kstone = auth(profile, **connection_args) ret = {} @@ -740,16 +803,19 @@ def tenant_get(tenant_id=None, name=None, profile=None, tenant_id = tenant.id break if not tenant_id: - return {'Error': 'Unable to resolve tenant id'} + return {"Error": "Unable to resolve tenant id"} tenant = getattr(kstone, _TENANTS, None).get(tenant_id) - ret[tenant.name] = dict((value, getattr(tenant, value)) for value in dir(tenant) - if not value.startswith('_') and - isinstance(getattr(tenant, value), (six.string_types, dict, bool))) + ret[tenant.name] = dict( + (value, getattr(tenant, value)) + for value in dir(tenant) + if not value.startswith("_") + and isinstance(getattr(tenant, value), (six.string_types, dict, bool)) + ) return ret def project_get(project_id=None, name=None, profile=None, **connection_args): - ''' + """ Return a specific projects (keystone project-get) Overrides keystone tenant-get form api V2. For keystone api V3 only. @@ -772,17 +838,19 @@ def project_get(project_id=None, name=None, profile=None, **connection_args): salt '*' keystone.project_get c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.project_get project_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.project_get name=nova - ''' + """ auth(profile, **connection_args) if _OS_IDENTITY_API_VERSION > 2: - return tenant_get(tenant_id=project_id, name=name, profile=None, **connection_args) + return tenant_get( + tenant_id=project_id, name=name, profile=None, **connection_args + ) else: return False def tenant_list(profile=None, **connection_args): - ''' + """ Return a list of available tenants (keystone tenants-list) CLI Example: @@ -790,19 +858,22 @@ def tenant_list(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.tenant_list - ''' + """ kstone = auth(profile, **connection_args) ret = {} for tenant in getattr(kstone, _TENANTS, None).list(): - ret[tenant.name] = dict((value, getattr(tenant, value)) for value in dir(tenant) - if not value.startswith('_') and - isinstance(getattr(tenant, value), (six.string_types, dict, bool))) + ret[tenant.name] = dict( + (value, getattr(tenant, value)) + for value in dir(tenant) + if not value.startswith("_") + and isinstance(getattr(tenant, value), (six.string_types, dict, bool)) + ) return ret def project_list(profile=None, **connection_args): - ''' + """ Return a list of available projects (keystone projects-list). Overrides keystone tenants-list form api V2. For keystone api V3 only. @@ -817,7 +888,7 @@ def project_list(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.project_list - ''' + """ auth(profile, **connection_args) if _OS_IDENTITY_API_VERSION > 2: @@ -826,9 +897,15 @@ def project_list(profile=None, **connection_args): return False -def tenant_update(tenant_id=None, name=None, description=None, - enabled=None, profile=None, **connection_args): - ''' +def tenant_update( + tenant_id=None, + name=None, + description=None, + enabled=None, + profile=None, + **connection_args +): + """ Update a tenant's information (keystone tenant-update) The following fields may be updated: name, description, enabled. Can only update name if targeting by ID @@ -839,7 +916,7 @@ def tenant_update(tenant_id=None, name=None, description=None, salt '*' keystone.tenant_update name=admin enabled=True salt '*' keystone.tenant_update c965f79c4f864eaaa9c3b41904e67082 name=admin email=admin@domain.com - ''' + """ kstone = auth(profile, **connection_args) if not tenant_id: @@ -848,7 +925,7 @@ def tenant_update(tenant_id=None, name=None, description=None, tenant_id = tenant.id break if not tenant_id: - return {'Error': 'Unable to resolve tenant id'} + return {"Error": "Unable to resolve tenant id"} tenant = getattr(kstone, _TENANTS, None).get(tenant_id) if not name: @@ -857,16 +934,27 @@ def tenant_update(tenant_id=None, name=None, description=None, description = tenant.description if enabled is None: enabled = tenant.enabled - updated = getattr(kstone, _TENANTS, None).update(tenant_id, name=name, description=description, enabled=enabled) + updated = getattr(kstone, _TENANTS, None).update( + tenant_id, name=name, description=description, enabled=enabled + ) - return dict((value, getattr(updated, value)) for value in dir(updated) - if not value.startswith('_') and - isinstance(getattr(updated, value), (six.string_types, dict, bool))) + return dict( + (value, getattr(updated, value)) + for value in dir(updated) + if not value.startswith("_") + and isinstance(getattr(updated, value), (six.string_types, dict, bool)) + ) -def project_update(project_id=None, name=None, description=None, - enabled=None, profile=None, **connection_args): - ''' +def project_update( + project_id=None, + name=None, + description=None, + enabled=None, + profile=None, + **connection_args +): + """ Update a tenant's information (keystone project-update) The following fields may be updated: name, description, enabled. Can only update name if targeting by ID @@ -897,18 +985,24 @@ def project_update(project_id=None, name=None, description=None, salt '*' keystone.project_update name=admin enabled=True salt '*' keystone.project_update c965f79c4f864eaaa9c3b41904e67082 name=admin email=admin@domain.com - ''' + """ auth(profile, **connection_args) if _OS_IDENTITY_API_VERSION > 2: - return tenant_update(tenant_id=project_id, name=name, description=description, - enabled=enabled, profile=profile, **connection_args) + return tenant_update( + tenant_id=project_id, + name=name, + description=description, + enabled=enabled, + profile=profile, + **connection_args + ) else: return False def token_get(profile=None, **connection_args): - ''' + """ Return the configured tokens (keystone token-get) CLI Example: @@ -916,17 +1010,19 @@ def token_get(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.token_get c965f79c4f864eaaa9c3b41904e67082 - ''' + """ kstone = auth(profile, **connection_args) token = kstone.service_catalog.get_token() - return {'id': token['id'], - 'expires': token['expires'], - 'user_id': token['user_id'], - 'tenant_id': token['tenant_id']} + return { + "id": token["id"], + "expires": token["expires"], + "user_id": token["user_id"], + "tenant_id": token["tenant_id"], + } def user_list(profile=None, **connection_args): - ''' + """ Return a list of available users (keystone user-list) CLI Example: @@ -934,21 +1030,24 @@ def user_list(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.user_list - ''' + """ kstone = auth(profile, **connection_args) ret = {} for user in kstone.users.list(): - ret[user.name] = dict((value, getattr(user, value, None)) for value in dir(user) - if not value.startswith('_') and - isinstance(getattr(user, value, None), (six.string_types, dict, bool))) - tenant_id = getattr(user, 'tenantId', None) + ret[user.name] = dict( + (value, getattr(user, value, None)) + for value in dir(user) + if not value.startswith("_") + and isinstance(getattr(user, value, None), (six.string_types, dict, bool)) + ) + tenant_id = getattr(user, "tenantId", None) if tenant_id: - ret[user.name]['tenant_id'] = tenant_id + ret[user.name]["tenant_id"] = tenant_id return ret def user_get(user_id=None, name=None, profile=None, **connection_args): - ''' + """ Return a specific users (keystone user-get) CLI Examples: @@ -958,7 +1057,7 @@ def user_get(user_id=None, name=None, profile=None, **connection_args): salt '*' keystone.user_get c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.user_get user_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.user_get name=nova - ''' + """ kstone = auth(profile, **connection_args) ret = {} if name: @@ -967,27 +1066,39 @@ def user_get(user_id=None, name=None, profile=None, **connection_args): user_id = user.id break if not user_id: - return {'Error': 'Unable to resolve user id'} + return {"Error": "Unable to resolve user id"} try: user = kstone.users.get(user_id) except keystoneclient.exceptions.NotFound: - msg = 'Could not find user \'{0}\''.format(user_id) + msg = "Could not find user '{0}'".format(user_id) log.error(msg) - return {'Error': msg} + return {"Error": msg} - ret[user.name] = dict((value, getattr(user, value, None)) for value in dir(user) - if not value.startswith('_') and - isinstance(getattr(user, value, None), (six.string_types, dict, bool))) + ret[user.name] = dict( + (value, getattr(user, value, None)) + for value in dir(user) + if not value.startswith("_") + and isinstance(getattr(user, value, None), (six.string_types, dict, bool)) + ) - tenant_id = getattr(user, 'tenantId', None) + tenant_id = getattr(user, "tenantId", None) if tenant_id: - ret[user.name]['tenant_id'] = tenant_id + ret[user.name]["tenant_id"] = tenant_id return ret -def user_create(name, password, email, tenant_id=None, - enabled=True, profile=None, project_id=None, description=None, **connection_args): - ''' +def user_create( + name, + password, + email, + tenant_id=None, + enabled=True, + profile=None, + project_id=None, + description=None, + **connection_args +): + """ Create a user (keystone user-create) CLI Examples: @@ -996,29 +1107,33 @@ def user_create(name, password, email, tenant_id=None, salt '*' keystone.user_create name=jack password=zero email=jack@halloweentown.org \ tenant_id=a28a7b5a999a455f84b1f5210264375e enabled=True - ''' + """ kstone = auth(profile, **connection_args) if _OS_IDENTITY_API_VERSION > 2: if tenant_id and not project_id: project_id = tenant_id - item = kstone.users.create(name=name, - password=password, - email=email, - project_id=project_id, - enabled=enabled, - description=description) + item = kstone.users.create( + name=name, + password=password, + email=email, + project_id=project_id, + enabled=enabled, + description=description, + ) else: - item = kstone.users.create(name=name, - password=password, - email=email, - tenant_id=tenant_id, - enabled=enabled) + item = kstone.users.create( + name=name, + password=password, + email=email, + tenant_id=tenant_id, + enabled=enabled, + ) return user_get(item.id, profile=profile, **connection_args) def user_delete(user_id=None, name=None, profile=None, **connection_args): - ''' + """ Delete a user (keystone user-delete) CLI Examples: @@ -1028,7 +1143,7 @@ def user_delete(user_id=None, name=None, profile=None, **connection_args): salt '*' keystone.user_delete c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.user_delete user_id=c965f79c4f864eaaa9c3b41904e67082 salt '*' keystone.user_delete name=nova - ''' + """ kstone = auth(profile, **connection_args) if name: for user in kstone.users.list(): @@ -1036,18 +1151,27 @@ def user_delete(user_id=None, name=None, profile=None, **connection_args): user_id = user.id break if not user_id: - return {'Error': 'Unable to resolve user id'} + return {"Error": "Unable to resolve user id"} kstone.users.delete(user_id) - ret = 'User ID {0} deleted'.format(user_id) + ret = "User ID {0} deleted".format(user_id) if name: - ret += ' ({0})'.format(name) + ret += " ({0})".format(name) return ret -def user_update(user_id=None, name=None, email=None, enabled=None, - tenant=None, profile=None, project=None, description=None, **connection_args): - ''' +def user_update( + user_id=None, + name=None, + email=None, + enabled=None, + tenant=None, + profile=None, + project=None, + description=None, + **connection_args +): + """ Update a user's information (keystone user-update) The following fields may be updated: name, email, enabled, tenant. Because the name is one of the fields, a valid user id is required. @@ -1058,7 +1182,7 @@ def user_update(user_id=None, name=None, email=None, enabled=None, salt '*' keystone.user_update user_id=c965f79c4f864eaaa9c3b41904e67082 name=newname salt '*' keystone.user_update c965f79c4f864eaaa9c3b41904e67082 name=newname email=newemail@domain.com - ''' + """ kstone = auth(profile, **connection_args) if not user_id: for user in kstone.users.list(): @@ -1066,7 +1190,7 @@ def user_update(user_id=None, name=None, email=None, enabled=None, user_id = user.id break if not user_id: - return {'Error': 'Unable to resolve user id'} + return {"Error": "Unable to resolve user id"} user = kstone.users.get(user_id) # Keep previous settings if not updating them if not name: @@ -1078,7 +1202,7 @@ def user_update(user_id=None, name=None, email=None, enabled=None, if _OS_IDENTITY_API_VERSION > 2: if description is None: - description = getattr(user, 'description', None) + description = getattr(user, "description", None) else: description = six.text_type(description) @@ -1089,10 +1213,16 @@ def user_update(user_id=None, name=None, email=None, enabled=None, project_id = proj.id break if not project_id: - project_id = getattr(user, 'project_id', None) + project_id = getattr(user, "project_id", None) - kstone.users.update(user=user_id, name=name, email=email, enabled=enabled, description=description, - project_id=project_id) + kstone.users.update( + user=user_id, + name=name, + email=email, + enabled=enabled, + description=description, + project_id=project_id, + ) else: kstone.users.update(user=user_id, name=name, email=email, enabled=enabled) @@ -1105,13 +1235,14 @@ def user_update(user_id=None, name=None, email=None, enabled=None, if tenant_id: kstone.users.update_tenant(user_id, tenant_id) - ret = 'Info updated for user ID {0}'.format(user_id) + ret = "Info updated for user ID {0}".format(user_id) return ret -def user_verify_password(user_id=None, name=None, password=None, - profile=None, **connection_args): - ''' +def user_verify_password( + user_id=None, name=None, password=None, profile=None, **connection_args +): + """ Verify a user's password CLI Examples: @@ -1120,17 +1251,19 @@ def user_verify_password(user_id=None, name=None, password=None, salt '*' keystone.user_verify_password name=test password=foobar salt '*' keystone.user_verify_password user_id=c965f79c4f864eaaa9c3b41904e67082 password=foobar - ''' + """ kstone = auth(profile, **connection_args) - if 'connection_endpoint' in connection_args: - auth_url = connection_args.get('connection_endpoint') + if "connection_endpoint" in connection_args: + auth_url = connection_args.get("connection_endpoint") else: if _OS_IDENTITY_API_VERSION > 2: - auth_url = __salt__['config.option']('keystone.endpoint', - 'http://127.0.0.1:35357/v3') + auth_url = __salt__["config.option"]( + "keystone.endpoint", "http://127.0.0.1:35357/v3" + ) else: - auth_url = __salt__['config.option']('keystone.endpoint', - 'http://127.0.0.1:35357/v2.0') + auth_url = __salt__["config.option"]( + "keystone.endpoint", "http://127.0.0.1:35357/v2.0" + ) if user_id: for user in kstone.users.list(): @@ -1138,24 +1271,25 @@ def user_verify_password(user_id=None, name=None, password=None, name = user.name break if not name: - return {'Error': 'Unable to resolve user name'} - kwargs = {'username': name, - 'password': password, - 'auth_url': auth_url} + return {"Error": "Unable to resolve user name"} + kwargs = {"username": name, "password": password, "auth_url": auth_url} try: if _OS_IDENTITY_API_VERSION > 2: client3.Client(**kwargs) else: client.Client(**kwargs) - except (keystoneclient.exceptions.Unauthorized, - keystoneclient.exceptions.AuthorizationFailure): + except ( + keystoneclient.exceptions.Unauthorized, + keystoneclient.exceptions.AuthorizationFailure, + ): return False return True -def user_password_update(user_id=None, name=None, password=None, - profile=None, **connection_args): - ''' +def user_password_update( + user_id=None, name=None, password=None, profile=None, **connection_args +): + """ Update a user's password (keystone user-password-update) CLI Examples: @@ -1165,7 +1299,7 @@ def user_password_update(user_id=None, name=None, password=None, salt '*' keystone.user_password_update c965f79c4f864eaaa9c3b41904e67082 password=12345 salt '*' keystone.user_password_update user_id=c965f79c4f864eaaa9c3b41904e67082 password=12345 salt '*' keystone.user_password_update name=nova password=12345 - ''' + """ kstone = auth(profile, **connection_args) if name: for user in kstone.users.list(): @@ -1173,22 +1307,31 @@ def user_password_update(user_id=None, name=None, password=None, user_id = user.id break if not user_id: - return {'Error': 'Unable to resolve user id'} + return {"Error": "Unable to resolve user id"} if _OS_IDENTITY_API_VERSION > 2: kstone.users.update(user=user_id, password=password) else: kstone.users.update_password(user=user_id, password=password) - ret = 'Password updated for user ID {0}'.format(user_id) + ret = "Password updated for user ID {0}".format(user_id) if name: - ret += ' ({0})'.format(name) + ret += " ({0})".format(name) return ret -def user_role_add(user_id=None, user=None, tenant_id=None, - tenant=None, role_id=None, role=None, profile=None, - project_id=None, project_name=None, **connection_args): - ''' +def user_role_add( + user_id=None, + user=None, + tenant_id=None, + tenant=None, + role_id=None, + role=None, + profile=None, + project_id=None, + project_name=None, + **connection_args +): + """ Add role for user in tenant (keystone user-role-add) CLI Examples: @@ -1200,7 +1343,7 @@ user_id=298ce377245c4ec9b70e1c639c89e654 \ tenant_id=7167a092ece84bae8cead4bf9d15bb3b \ role_id=ce377245c4ec9b70e1c639c89e8cead4 salt '*' keystone.user_role_add user=admin tenant=admin role=admin - ''' + """ kstone = auth(profile, **connection_args) if project_id and not tenant_id: @@ -1209,31 +1352,35 @@ role_id=ce377245c4ec9b70e1c639c89e8cead4 tenant = project_name if user: - user_id = user_get(name=user, profile=profile, - **connection_args)[user].get('id') + user_id = user_get(name=user, profile=profile, **connection_args)[user].get( + "id" + ) else: - user = next(six.iterkeys(user_get(user_id, profile=profile, - **connection_args)))['name'] + user = next( + six.iterkeys(user_get(user_id, profile=profile, **connection_args)) + )["name"] if not user_id: - return {'Error': 'Unable to resolve user id'} + return {"Error": "Unable to resolve user id"} if tenant: - tenant_id = tenant_get(name=tenant, profile=profile, - **connection_args)[tenant].get('id') + tenant_id = tenant_get(name=tenant, profile=profile, **connection_args)[ + tenant + ].get("id") else: - tenant = next(six.iterkeys(tenant_get(tenant_id, profile=profile, - **connection_args)))['name'] + tenant = next( + six.iterkeys(tenant_get(tenant_id, profile=profile, **connection_args)) + )["name"] if not tenant_id: - return {'Error': 'Unable to resolve tenant/project id'} + return {"Error": "Unable to resolve tenant/project id"} if role: - role_id = role_get(name=role, profile=profile, - **connection_args)[role]['id'] + role_id = role_get(name=role, profile=profile, **connection_args)[role]["id"] else: - role = next(six.iterkeys(role_get(role_id, profile=profile, - **connection_args)))['name'] + role = next( + six.iterkeys(role_get(role_id, profile=profile, **connection_args)) + )["name"] if not role_id: - return {'Error': 'Unable to resolve role id'} + return {"Error": "Unable to resolve role id"} if _OS_IDENTITY_API_VERSION > 2: kstone.roles.grant(role_id, user=user_id, project=tenant_id) @@ -1243,10 +1390,19 @@ role_id=ce377245c4ec9b70e1c639c89e8cead4 return ret_msg.format(role, user, tenant) -def user_role_remove(user_id=None, user=None, tenant_id=None, - tenant=None, role_id=None, role=None, - profile=None, project_id=None, project_name=None, **connection_args): - ''' +def user_role_remove( + user_id=None, + user=None, + tenant_id=None, + tenant=None, + role_id=None, + role=None, + profile=None, + project_id=None, + project_name=None, + **connection_args +): + """ Remove role for user in tenant (keystone user-role-remove) CLI Examples: @@ -1258,7 +1414,7 @@ user_id=298ce377245c4ec9b70e1c639c89e654 \ tenant_id=7167a092ece84bae8cead4bf9d15bb3b \ role_id=ce377245c4ec9b70e1c639c89e8cead4 salt '*' keystone.user_role_remove user=admin tenant=admin role=admin - ''' + """ kstone = auth(profile, **connection_args) if project_id and not tenant_id: @@ -1267,30 +1423,33 @@ role_id=ce377245c4ec9b70e1c639c89e8cead4 tenant = project_name if user: - user_id = user_get(name=user, profile=profile, - **connection_args)[user].get('id') + user_id = user_get(name=user, profile=profile, **connection_args)[user].get( + "id" + ) else: - user = next(six.iterkeys(user_get(user_id, profile=profile, - **connection_args)))['name'] + user = next( + six.iterkeys(user_get(user_id, profile=profile, **connection_args)) + )["name"] if not user_id: - return {'Error': 'Unable to resolve user id'} + return {"Error": "Unable to resolve user id"} if tenant: - tenant_id = tenant_get(name=tenant, profile=profile, - **connection_args)[tenant].get('id') + tenant_id = tenant_get(name=tenant, profile=profile, **connection_args)[ + tenant + ].get("id") else: - tenant = next(six.iterkeys(tenant_get(tenant_id, profile=profile, - **connection_args)))['name'] + tenant = next( + six.iterkeys(tenant_get(tenant_id, profile=profile, **connection_args)) + )["name"] if not tenant_id: - return {'Error': 'Unable to resolve tenant/project id'} + return {"Error": "Unable to resolve tenant/project id"} if role: - role_id = role_get(name=role, profile=profile, - **connection_args)[role]['id'] + role_id = role_get(name=role, profile=profile, **connection_args)[role]["id"] else: - role = next(six.iterkeys(role_get(role_id)))['name'] + role = next(six.iterkeys(role_get(role_id)))["name"] if not role_id: - return {'Error': 'Unable to resolve role id'} + return {"Error": "Unable to resolve role id"} if _OS_IDENTITY_API_VERSION > 2: kstone.roles.revoke(role_id, user=user_id, project=tenant_id) @@ -1300,9 +1459,17 @@ role_id=ce377245c4ec9b70e1c639c89e8cead4 return ret_msg.format(role, user, tenant) -def user_role_list(user_id=None, tenant_id=None, user_name=None, - tenant_name=None, profile=None, project_id=None, project_name=None, **connection_args): - ''' +def user_role_list( + user_id=None, + tenant_id=None, + user_name=None, + tenant_name=None, + profile=None, + project_id=None, + project_name=None, + **connection_args +): + """ Return a list of available user_roles (keystone user-roles-list) CLI Examples: @@ -1313,7 +1480,7 @@ def user_role_list(user_id=None, tenant_id=None, user_name=None, user_id=298ce377245c4ec9b70e1c639c89e654 \ tenant_id=7167a092ece84bae8cead4bf9d15bb3b salt '*' keystone.user_role_list user_name=admin tenant_name=admin - ''' + """ kstone = auth(profile, **connection_args) ret = {} @@ -1333,24 +1500,29 @@ tenant_id=7167a092ece84bae8cead4bf9d15bb3b tenant_id = tenant.id break if not user_id or not tenant_id: - return {'Error': 'Unable to resolve user or tenant/project id'} + return {"Error": "Unable to resolve user or tenant/project id"} if _OS_IDENTITY_API_VERSION > 2: for role in kstone.roles.list(user=user_id, project=tenant_id): - ret[role.name] = dict((value, getattr(role, value)) for value in dir(role) - if not value.startswith('_') and - isinstance(getattr(role, value), (six.string_types, dict, bool))) + ret[role.name] = dict( + (value, getattr(role, value)) + for value in dir(role) + if not value.startswith("_") + and isinstance(getattr(role, value), (six.string_types, dict, bool)) + ) else: for role in kstone.roles.roles_for_user(user=user_id, tenant=tenant_id): - ret[role.name] = {'id': role.id, - 'name': role.name, - 'user_id': user_id, - 'tenant_id': tenant_id} + ret[role.name] = { + "id": role.id, + "name": role.name, + "user_id": user_id, + "tenant_id": tenant_id, + } return ret def _item_list(profile=None, **connection_args): - ''' + """ Template for writing list functions Return a list of available items (keystone items-list) @@ -1359,7 +1531,7 @@ def _item_list(profile=None, **connection_args): .. code-block:: bash salt '*' keystone.item_list - ''' + """ kstone = auth(profile, **connection_args) ret = [] for item in kstone.items.list(): diff --git a/salt/modules/keystoneng.py b/salt/modules/keystoneng.py index a99734c0241..279d3d282c4 100644 --- a/salt/modules/keystoneng.py +++ b/salt/modules/keystoneng.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Keystone module for interacting with OpenStack Keystone .. versionadded:: 2018.3.0 @@ -24,35 +24,39 @@ Example configuration project_domain_name: myproject auth_url: https://example.org:5000/v3 identity_api_version: 3 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals HAS_SHADE = False try: import shade from shade.exc import OpenStackCloudException + HAS_SHADE = True except ImportError: pass -__virtualname__ = 'keystoneng' +__virtualname__ = "keystoneng" def __virtual__(): - ''' + """ Only load this module if shade python module is installed - ''' + """ if HAS_SHADE: return __virtualname__ - return (False, 'The keystoneng execution module failed to load: shade python module is not available') + return ( + False, + "The keystoneng execution module failed to load: shade python module is not available", + ) def compare_changes(obj, **kwargs): - ''' + """ Compare two dicts returning only keys that exist in the first dict and are different in the second one - ''' + """ changes = {} for k, v in obj.items(): if k in kwargs: @@ -62,74 +66,74 @@ def compare_changes(obj, **kwargs): def get_entity(ent_type, **kwargs): - ''' + """ Attempt to query Keystone for more information about an entity - ''' + """ try: - func = 'keystoneng.{}_get'.format(ent_type) + func = "keystoneng.{}_get".format(ent_type) ent = __salt__[func](**kwargs) except OpenStackCloudException as e: # NOTE(SamYaple): If this error was something other than Forbidden we # reraise the issue since we are not prepared to handle it - if 'HTTP 403' not in e.inner_exception[1][0]: + if "HTTP 403" not in e.inner_exception[1][0]: raise # NOTE(SamYaple): The user may be authorized to perform the function # they are trying to do, but not authorized to search. In such a # situation we want to trust that the user has passed a valid id, even # though we cannot validate that this is a valid id - ent = kwargs['name'] + ent = kwargs["name"] return ent def _clean_kwargs(keep_name=False, **kwargs): - ''' + """ Sanatize the the arguments for use with shade - ''' - if 'name' in kwargs and not keep_name: - kwargs['name_or_id'] = kwargs.pop('name') + """ + if "name" in kwargs and not keep_name: + kwargs["name_or_id"] = kwargs.pop("name") - return __utils__['args.clean_kwargs'](**kwargs) + return __utils__["args.clean_kwargs"](**kwargs) def setup_clouds(auth=None): - ''' + """ Call functions to create Shade cloud objects in __context__ to take advantage of Shade's in-memory caching across several states - ''' + """ get_operator_cloud(auth) get_openstack_cloud(auth) def get_operator_cloud(auth=None): - ''' + """ Return an operator_cloud - ''' + """ if auth is None: - auth = __salt__['config.option']('keystone', {}) - if 'shade_opcloud' in __context__: - if __context__['shade_opcloud'].auth == auth: - return __context__['shade_opcloud'] - __context__['shade_opcloud'] = shade.operator_cloud(**auth) - return __context__['shade_opcloud'] + auth = __salt__["config.option"]("keystone", {}) + if "shade_opcloud" in __context__: + if __context__["shade_opcloud"].auth == auth: + return __context__["shade_opcloud"] + __context__["shade_opcloud"] = shade.operator_cloud(**auth) + return __context__["shade_opcloud"] def get_openstack_cloud(auth=None): - ''' + """ Return an openstack_cloud - ''' + """ if auth is None: - auth = __salt__['config.option']('keystone', {}) - if 'shade_oscloud' in __context__: - if __context__['shade_oscloud'].auth == auth: - return __context__['shade_oscloud'] - __context__['shade_oscloud'] = shade.openstack_cloud(**auth) - return __context__['shade_oscloud'] + auth = __salt__["config.option"]("keystone", {}) + if "shade_oscloud" in __context__: + if __context__["shade_oscloud"].auth == auth: + return __context__["shade_oscloud"] + __context__["shade_oscloud"] = shade.openstack_cloud(**auth) + return __context__["shade_oscloud"] def group_create(auth=None, **kwargs): - ''' + """ Create a group CLI Example: @@ -138,14 +142,14 @@ def group_create(auth=None, **kwargs): salt '*' keystoneng.group_create name=group1 salt '*' keystoneng.group_create name=group2 domain=domain1 description='my group2' - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_group(**kwargs) def group_delete(auth=None, **kwargs): - ''' + """ Delete a group CLI Example: @@ -155,14 +159,14 @@ def group_delete(auth=None, **kwargs): salt '*' keystoneng.group_delete name=group1 salt '*' keystoneng.group_delete name=group2 domain_id=b62e76fbeeff4e8fb77073f591cf211e salt '*' keystoneng.group_delete name=0e4febc2a5ab4f2c8f374b054162506d - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_group(**kwargs) def group_update(auth=None, **kwargs): - ''' + """ Update a group CLI Example: @@ -172,16 +176,16 @@ def group_update(auth=None, **kwargs): salt '*' keystoneng.group_update name=group1 description='new description' salt '*' keystoneng.group_create name=group2 domain_id=b62e76fbeeff4e8fb77073f591cf211e new_name=newgroupname salt '*' keystoneng.group_create name=0e4febc2a5ab4f2c8f374b054162506d new_name=newgroupname - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) - if 'new_name' in kwargs: - kwargs['name'] = kwargs.pop('new_name') + if "new_name" in kwargs: + kwargs["name"] = kwargs.pop("new_name") return cloud.update_group(**kwargs) def group_list(auth=None, **kwargs): - ''' + """ List groups CLI Example: @@ -190,14 +194,14 @@ def group_list(auth=None, **kwargs): salt '*' keystoneng.group_list salt '*' keystoneng.group_list domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_groups(**kwargs) def group_search(auth=None, **kwargs): - ''' + """ Search for groups CLI Example: @@ -206,14 +210,14 @@ def group_search(auth=None, **kwargs): salt '*' keystoneng.group_search name=group1 salt '*' keystoneng.group_search domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.search_groups(**kwargs) def group_get(auth=None, **kwargs): - ''' + """ Get a single group CLI Example: @@ -223,14 +227,14 @@ def group_get(auth=None, **kwargs): salt '*' keystoneng.group_get name=group1 salt '*' keystoneng.group_get name=group2 domain_id=b62e76fbeeff4e8fb77073f591cf211e salt '*' keystoneng.group_get name=0e4febc2a5ab4f2c8f374b054162506d - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_group(**kwargs) def project_create(auth=None, **kwargs): - ''' + """ Create a project CLI Example: @@ -240,14 +244,14 @@ def project_create(auth=None, **kwargs): salt '*' keystoneng.project_create name=project1 salt '*' keystoneng.project_create name=project2 domain_id=b62e76fbeeff4e8fb77073f591cf211e salt '*' keystoneng.project_create name=project3 enabled=False description='my project3' - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_project(**kwargs) def project_delete(auth=None, **kwargs): - ''' + """ Delete a project CLI Example: @@ -257,14 +261,14 @@ def project_delete(auth=None, **kwargs): salt '*' keystoneng.project_delete name=project1 salt '*' keystoneng.project_delete name=project2 domain_id=b62e76fbeeff4e8fb77073f591cf211e salt '*' keystoneng.project_delete name=f315afcf12f24ad88c92b936c38f2d5a - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_project(**kwargs) def project_update(auth=None, **kwargs): - ''' + """ Update a project CLI Example: @@ -273,16 +277,16 @@ def project_update(auth=None, **kwargs): salt '*' keystoneng.project_update name=project1 new_name=newproject salt '*' keystoneng.project_update name=project2 enabled=False description='new description' - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) - if 'new_name' in kwargs: - kwargs['name'] = kwargs.pop('new_name') + if "new_name" in kwargs: + kwargs["name"] = kwargs.pop("new_name") return cloud.update_project(**kwargs) def project_list(auth=None, **kwargs): - ''' + """ List projects CLI Example: @@ -291,14 +295,14 @@ def project_list(auth=None, **kwargs): salt '*' keystoneng.project_list salt '*' keystoneng.project_list domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_projects(**kwargs) def project_search(auth=None, **kwargs): - ''' + """ Search projects CLI Example: @@ -308,14 +312,14 @@ def project_search(auth=None, **kwargs): salt '*' keystoneng.project_search salt '*' keystoneng.project_search name=project1 salt '*' keystoneng.project_search domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.search_projects(**kwargs) def project_get(auth=None, **kwargs): - ''' + """ Get a single project CLI Example: @@ -325,14 +329,14 @@ def project_get(auth=None, **kwargs): salt '*' keystoneng.project_get name=project1 salt '*' keystoneng.project_get name=project2 domain_id=b62e76fbeeff4e8fb77073f591cf211e salt '*' keystoneng.project_get name=f315afcf12f24ad88c92b936c38f2d5a - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_project(**kwargs) def domain_create(auth=None, **kwargs): - ''' + """ Create a domain CLI Example: @@ -340,14 +344,14 @@ def domain_create(auth=None, **kwargs): .. code-block:: bash salt '*' keystoneng.domain_create name=domain1 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_domain(**kwargs) def domain_delete(auth=None, **kwargs): - ''' + """ Delete a domain CLI Example: @@ -356,14 +360,14 @@ def domain_delete(auth=None, **kwargs): salt '*' keystoneng.domain_delete name=domain1 salt '*' keystoneng.domain_delete name=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_domain(**kwargs) def domain_update(auth=None, **kwargs): - ''' + """ Update a domain CLI Example: @@ -372,16 +376,16 @@ def domain_update(auth=None, **kwargs): salt '*' keystoneng.domain_update name=domain1 new_name=newdomain salt '*' keystoneng.domain_update name=domain1 enabled=True description='new description' - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) - if 'new_name' in kwargs: - kwargs['name'] = kwargs.pop('new_name') + if "new_name" in kwargs: + kwargs["name"] = kwargs.pop("new_name") return cloud.update_domain(**kwargs) def domain_list(auth=None, **kwargs): - ''' + """ List domains CLI Example: @@ -389,14 +393,14 @@ def domain_list(auth=None, **kwargs): .. code-block:: bash salt '*' keystoneng.domain_list - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_domains(**kwargs) def domain_search(auth=None, **kwargs): - ''' + """ Search domains CLI Example: @@ -405,14 +409,14 @@ def domain_search(auth=None, **kwargs): salt '*' keystoneng.domain_search salt '*' keystoneng.domain_search name=domain1 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.search_domains(**kwargs) def domain_get(auth=None, **kwargs): - ''' + """ Get a single domain CLI Example: @@ -421,14 +425,14 @@ def domain_get(auth=None, **kwargs): salt '*' keystoneng.domain_get name=domain1 salt '*' keystoneng.domain_get name=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_domain(**kwargs) def role_create(auth=None, **kwargs): - ''' + """ Create a role CLI Example: @@ -437,14 +441,14 @@ def role_create(auth=None, **kwargs): salt '*' keystoneng.role_create name=role1 salt '*' keystoneng.role_create name=role1 domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_role(**kwargs) def role_delete(auth=None, **kwargs): - ''' + """ Delete a role CLI Example: @@ -453,14 +457,14 @@ def role_delete(auth=None, **kwargs): salt '*' keystoneng.role_delete name=role1 domain_id=b62e76fbeeff4e8fb77073f591cf211e salt '*' keystoneng.role_delete name=1eb6edd5525e4ac39af571adee673559 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_role(**kwargs) def role_update(auth=None, **kwargs): - ''' + """ Update a role CLI Example: @@ -469,16 +473,16 @@ def role_update(auth=None, **kwargs): salt '*' keystoneng.role_update name=role1 new_name=newrole salt '*' keystoneng.role_update name=1eb6edd5525e4ac39af571adee673559 new_name=newrole - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) - if 'new_name' in kwargs: - kwargs['name'] = kwargs.pop('new_name') + if "new_name" in kwargs: + kwargs["name"] = kwargs.pop("new_name") return cloud.update_role(**kwargs) def role_list(auth=None, **kwargs): - ''' + """ List roles CLI Example: @@ -487,14 +491,14 @@ def role_list(auth=None, **kwargs): salt '*' keystoneng.role_list salt '*' keystoneng.role_list domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_roles(**kwargs) def role_search(auth=None, **kwargs): - ''' + """ Search roles CLI Example: @@ -504,14 +508,14 @@ def role_search(auth=None, **kwargs): salt '*' keystoneng.role_search salt '*' keystoneng.role_search name=role1 salt '*' keystoneng.role_search domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.search_roles(**kwargs) def role_get(auth=None, **kwargs): - ''' + """ Get a single role CLI Example: @@ -521,14 +525,14 @@ def role_get(auth=None, **kwargs): salt '*' keystoneng.role_get name=role1 salt '*' keystoneng.role_get name=role1 domain_id=b62e76fbeeff4e8fb77073f591cf211e salt '*' keystoneng.role_get name=1eb6edd5525e4ac39af571adee673559 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_role(**kwargs) def user_create(auth=None, **kwargs): - ''' + """ Create a user CLI Example: @@ -538,14 +542,14 @@ def user_create(auth=None, **kwargs): salt '*' keystoneng.user_create name=user1 salt '*' keystoneng.user_create name=user2 password=1234 enabled=False salt '*' keystoneng.user_create name=user3 domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_user(**kwargs) def user_delete(auth=None, **kwargs): - ''' + """ Delete a user CLI Example: @@ -555,14 +559,14 @@ def user_delete(auth=None, **kwargs): salt '*' keystoneng.user_delete name=user1 salt '*' keystoneng.user_delete name=user2 domain_id=b62e76fbeeff4e8fb77073f591cf211e salt '*' keystoneng.user_delete name=a42cbbfa1e894e839fd0f584d22e321f - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_user(**kwargs) def user_update(auth=None, **kwargs): - ''' + """ Update a user CLI Example: @@ -571,16 +575,16 @@ def user_update(auth=None, **kwargs): salt '*' keystoneng.user_update name=user1 enabled=False description='new description' salt '*' keystoneng.user_update name=user1 new_name=newuser - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) - if 'new_name' in kwargs: - kwargs['name'] = kwargs.pop('new_name') + if "new_name" in kwargs: + kwargs["name"] = kwargs.pop("new_name") return cloud.update_user(**kwargs) def user_list(auth=None, **kwargs): - ''' + """ List users CLI Example: @@ -589,14 +593,14 @@ def user_list(auth=None, **kwargs): salt '*' keystoneng.user_list salt '*' keystoneng.user_list domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_users(**kwargs) def user_search(auth=None, **kwargs): - ''' + """ List users CLI Example: @@ -605,14 +609,14 @@ def user_search(auth=None, **kwargs): salt '*' keystoneng.user_list salt '*' keystoneng.user_list domain_id=b62e76fbeeff4e8fb77073f591cf211e - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.search_users(**kwargs) def user_get(auth=None, **kwargs): - ''' + """ Get a single user CLI Example: @@ -622,14 +626,14 @@ def user_get(auth=None, **kwargs): salt '*' keystoneng.user_get name=user1 salt '*' keystoneng.user_get name=user1 domain_id=b62e76fbeeff4e8fb77073f591cf211e salt '*' keystoneng.user_get name=02cffaa173b2460f98e40eda3748dae5 - ''' + """ cloud = get_openstack_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_user(**kwargs) def endpoint_create(auth=None, **kwargs): - ''' + """ Create an endpoint CLI Example: @@ -639,14 +643,14 @@ def endpoint_create(auth=None, **kwargs): salt '*' keystoneng.endpoint_create interface=admin service=glance url=https://example.org:9292 salt '*' keystoneng.endpoint_create interface=public service=glance region=RegionOne url=https://example.org:9292 salt '*' keystoneng.endpoint_create interface=admin service=glance url=https://example.org:9292 enabled=True - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_endpoint(**kwargs) def endpoint_delete(auth=None, **kwargs): - ''' + """ Delete an endpoint CLI Example: @@ -654,14 +658,14 @@ def endpoint_delete(auth=None, **kwargs): .. code-block:: bash salt '*' keystoneng.endpoint_delete id=3bee4bd8c2b040ee966adfda1f0bfca9 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_endpoint(**kwargs) def endpoint_update(auth=None, **kwargs): - ''' + """ Update an endpoint CLI Example: @@ -671,14 +675,14 @@ def endpoint_update(auth=None, **kwargs): salt '*' keystoneng.endpoint_update endpoint_id=4f961ad09d2d48948896bbe7c6a79717 interface=public enabled=False salt '*' keystoneng.endpoint_update endpoint_id=4f961ad09d2d48948896bbe7c6a79717 region=newregion salt '*' keystoneng.endpoint_update endpoint_id=4f961ad09d2d48948896bbe7c6a79717 service_name_or_id=glance url=https://example.org:9292 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.update_endpoint(**kwargs) def endpoint_list(auth=None, **kwargs): - ''' + """ List endpoints CLI Example: @@ -686,14 +690,14 @@ def endpoint_list(auth=None, **kwargs): .. code-block:: bash salt '*' keystoneng.endpoint_list - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_endpoints(**kwargs) def endpoint_search(auth=None, **kwargs): - ''' + """ Search endpoints CLI Example: @@ -702,14 +706,14 @@ def endpoint_search(auth=None, **kwargs): salt '*' keystoneng.endpoint_search salt '*' keystoneng.endpoint_search id=02cffaa173b2460f98e40eda3748dae5 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.search_endpoints(**kwargs) def endpoint_get(auth=None, **kwargs): - ''' + """ Get a single endpoint CLI Example: @@ -717,14 +721,14 @@ def endpoint_get(auth=None, **kwargs): .. code-block:: bash salt '*' keystoneng.endpoint_get id=02cffaa173b2460f98e40eda3748dae5 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_endpoint(**kwargs) def service_create(auth=None, **kwargs): - ''' + """ Create a service CLI Example: @@ -733,14 +737,14 @@ def service_create(auth=None, **kwargs): salt '*' keystoneng.service_create name=glance type=image salt '*' keystoneng.service_create name=glance type=image description="Image" - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_service(**kwargs) def service_delete(auth=None, **kwargs): - ''' + """ Delete a service CLI Example: @@ -749,14 +753,14 @@ def service_delete(auth=None, **kwargs): salt '*' keystoneng.service_delete name=glance salt '*' keystoneng.service_delete name=39cc1327cdf744ab815331554430e8ec - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_service(**kwargs) def service_update(auth=None, **kwargs): - ''' + """ Update a service CLI Example: @@ -766,14 +770,14 @@ def service_update(auth=None, **kwargs): salt '*' keystoneng.service_update name=cinder type=volumev2 salt '*' keystoneng.service_update name=cinder description='new description' salt '*' keystoneng.service_update name=ab4d35e269f147b3ae2d849f77f5c88f enabled=False - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.update_service(**kwargs) def service_list(auth=None, **kwargs): - ''' + """ List services CLI Example: @@ -781,14 +785,14 @@ def service_list(auth=None, **kwargs): .. code-block:: bash salt '*' keystoneng.service_list - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_services(**kwargs) def service_search(auth=None, **kwargs): - ''' + """ Search services CLI Example: @@ -798,14 +802,14 @@ def service_search(auth=None, **kwargs): salt '*' keystoneng.service_search salt '*' keystoneng.service_search name=glance salt '*' keystoneng.service_search name=135f0403f8e544dc9008c6739ecda860 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.search_services(**kwargs) def service_get(auth=None, **kwargs): - ''' + """ Get a single service CLI Example: @@ -814,14 +818,14 @@ def service_get(auth=None, **kwargs): salt '*' keystoneng.service_get name=glance salt '*' keystoneng.service_get name=75a5804638944b3ab54f7fbfcec2305a - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_service(**kwargs) def role_assignment_list(auth=None, **kwargs): - ''' + """ List role assignments CLI Example: @@ -829,14 +833,14 @@ def role_assignment_list(auth=None, **kwargs): .. code-block:: bash salt '*' keystoneng.role_assignment_list - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_role_assignments(**kwargs) def role_grant(auth=None, **kwargs): - ''' + """ Grant a role in a project/domain to a user/group CLI Example: @@ -846,14 +850,14 @@ def role_grant(auth=None, **kwargs): salt '*' keystoneng.role_grant name=role1 user=user1 project=project1 salt '*' keystoneng.role_grant name=ddbe3e0ed74e4c7f8027bad4af03339d group=user1 project=project1 domain=domain1 salt '*' keystoneng.role_grant name=ddbe3e0ed74e4c7f8027bad4af03339d group=19573afd5e4241d8b65c42215bae9704 project=1dcac318a83b4610b7a7f7ba01465548 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.grant_role(**kwargs) def role_revoke(auth=None, **kwargs): - ''' + """ Grant a role in a project/domain to a user/group CLI Example: @@ -863,7 +867,7 @@ def role_revoke(auth=None, **kwargs): salt '*' keystoneng.role_revoke name=role1 user=user1 project=project1 salt '*' keystoneng.role_revoke name=ddbe3e0ed74e4c7f8027bad4af03339d group=user1 project=project1 domain=domain1 salt '*' keystoneng.role_revoke name=ddbe3e0ed74e4c7f8027bad4af03339d group=19573afd5e4241d8b65c42215bae9704 project=1dcac318a83b4610b7a7f7ba01465548 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.revoke_role(**kwargs) diff --git a/salt/modules/keystore.py b/salt/modules/keystore.py index 27b59f7d507..b941b9eb4f3 100644 --- a/salt/modules/keystore.py +++ b/salt/modules/keystore.py @@ -1,35 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" Module to interact with keystores -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging -from datetime import datetime import os - -log = logging.getLogger(__name__) - -__virtualname__ = 'keystore' +from datetime import datetime # Import third party libs from salt.exceptions import CommandExecutionError, SaltInvocationError +log = logging.getLogger(__name__) + +__virtualname__ = "keystore" + + try: import jks import OpenSSL + has_depends = True except ImportError: has_depends = False def __virtual__(): - ''' + """ Check dependencies - ''' + """ if has_depends is False: - msg = 'jks unavailable: {0} execution module cant be loaded '.format(__virtualname__) + msg = "jks unavailable: {0} execution module cant be loaded ".format( + __virtualname__ + ) return False, msg return __virtualname__ @@ -38,30 +43,30 @@ def _parse_cert(alias, public_cert, return_cert=False): ASN1 = OpenSSL.crypto.FILETYPE_ASN1 PEM = OpenSSL.crypto.FILETYPE_PEM cert_data = {} - sha1 = public_cert.digest(b'sha1') + sha1 = public_cert.digest(b"sha1") cert_pem = OpenSSL.crypto.dump_certificate(PEM, public_cert) raw_until = public_cert.get_notAfter() - date_until = datetime.strptime(raw_until, '%Y%m%d%H%M%SZ') + date_until = datetime.strptime(raw_until, "%Y%m%d%H%M%SZ") string_until = date_until.strftime("%B %d %Y") raw_start = public_cert.get_notBefore() - date_start = datetime.strptime(raw_start, '%Y%m%d%H%M%SZ') + date_start = datetime.strptime(raw_start, "%Y%m%d%H%M%SZ") string_start = date_start.strftime("%B %d %Y") if return_cert: - cert_data['pem'] = cert_pem - cert_data['alias'] = alias - cert_data['sha1'] = sha1 - cert_data['valid_until'] = string_until - cert_data['valid_start'] = string_start - cert_data['expired'] = date_until < datetime.now() + cert_data["pem"] = cert_pem + cert_data["alias"] = alias + cert_data["sha1"] = sha1 + cert_data["valid_until"] = string_until + cert_data["valid_start"] = string_start + cert_data["expired"] = date_until < datetime.now() return cert_data def list(keystore, passphrase, alias=None, return_cert=False): - ''' + """ Lists certificates in a keytool managed keystore. @@ -81,7 +86,7 @@ def list(keystore, passphrase, alias=None, return_cert=False): salt '*' keystore.list /usr/lib/jvm/java-8/jre/lib/security/cacerts changeit salt '*' keystore.list /usr/lib/jvm/java-8/jre/lib/security/cacerts changeit debian:verisign_-_g5.pem - ''' + """ ASN1 = OpenSSL.crypto.FILETYPE_ASN1 PEM = OpenSSL.crypto.FILETYPE_PEM decoded_certs = [] @@ -102,15 +107,17 @@ def list(keystore, passphrase, alias=None, return_cert=False): entry_data = {} if isinstance(cert_enc, jks.PrivateKeyEntry): cert_result = cert_enc.cert_chain[0][1] - entry_data['type'] = 'PrivateKeyEntry' + entry_data["type"] = "PrivateKeyEntry" elif isinstance(cert_enc, jks.TrustedCertEntry): cert_result = cert_enc.cert - entry_data['type'] = 'TrustedCertEntry' + entry_data["type"] = "TrustedCertEntry" else: - raise CommandExecutionError('Unsupported EntryType detected in keystore') + raise CommandExecutionError( + "Unsupported EntryType detected in keystore" + ) # Detect if ASN1 binary, otherwise assume PEM - if '\x30' in cert_result[0]: + if "\x30" in cert_result[0]: public_cert = OpenSSL.crypto.load_certificate(ASN1, cert_result) else: public_cert = OpenSSL.crypto.load_certificate(PEM, cert_result) @@ -122,7 +129,7 @@ def list(keystore, passphrase, alias=None, return_cert=False): def add(name, keystore, passphrase, certificate, private_key=None): - ''' + """ Adds certificates to an existing keystore or creates a new one if necesssary. :param name: alias for the certificate @@ -139,7 +146,7 @@ def add(name, keystore, passphrase, certificate, private_key=None): salt '*' keystore.add aliasname /tmp/test.store changeit certificate="-----BEGIN CERTIFICATE-----SIb...BM=-----END CERTIFICATE-----" salt '*' keystore.add keyname /tmp/test.store changeit /tmp/512.cert private_key=/tmp/512.key - ''' + """ ASN1 = OpenSSL.crypto.FILETYPE_ASN1 PEM = OpenSSL.crypto.FILETYPE_PEM certs_list = [] @@ -149,31 +156,33 @@ def add(name, keystore, passphrase, certificate, private_key=None): certs_list.append(loaded_cert) try: - cert_string = __salt__['x509.get_pem_entry'](certificate) + cert_string = __salt__["x509.get_pem_entry"](certificate) except SaltInvocationError: - raise SaltInvocationError('Invalid certificate file or string: {0}'.format(certificate)) + raise SaltInvocationError( + "Invalid certificate file or string: {0}".format(certificate) + ) if private_key: # Accept PEM input format, but convert to DES for loading into new keystore - key_string = __salt__['x509.get_pem_entry'](private_key) + key_string = __salt__["x509.get_pem_entry"](private_key) loaded_cert = OpenSSL.crypto.load_certificate(PEM, cert_string) loaded_key = OpenSSL.crypto.load_privatekey(PEM, key_string) dumped_cert = OpenSSL.crypto.dump_certificate(ASN1, loaded_cert) dumped_key = OpenSSL.crypto.dump_privatekey(ASN1, loaded_key) - new_entry = jks.PrivateKeyEntry.new(name, [dumped_cert], dumped_key, 'rsa_raw') + new_entry = jks.PrivateKeyEntry.new(name, [dumped_cert], dumped_key, "rsa_raw") else: new_entry = jks.TrustedCertEntry.new(name, cert_string) certs_list.append(new_entry) - keystore_object = jks.KeyStore.new('jks', certs_list) + keystore_object = jks.KeyStore.new("jks", certs_list) keystore_object.save(keystore, passphrase) return True def remove(name, keystore, passphrase): - ''' + """ Removes a certificate from an existing keystore. Returns True if remove was successful, otherwise False @@ -186,7 +195,7 @@ def remove(name, keystore, passphrase): .. code-block:: bash salt '*' keystore.remove aliasname /tmp/test.store changeit - ''' + """ certs_list = [] keystore_object = jks.KeyStore.load(keystore, passphrase) for alias, loaded_cert in keystore_object.entries.items(): @@ -195,7 +204,7 @@ def remove(name, keystore, passphrase): if len(keystore_object.entries) != len(certs_list): # Entry has been removed, save keystore updates - keystore_object = jks.KeyStore.new('jks', certs_list) + keystore_object = jks.KeyStore.new("jks", certs_list) keystore_object.save(keystore, passphrase) return True else: diff --git a/salt/modules/kmod.py b/salt/modules/kmod.py index b6080296d87..1ec485ab687 100644 --- a/salt/modules/kmod.py +++ b/salt/modules/kmod.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Module to manage Linux kernel modules -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import python libs import os import re -import logging # Import salt libs import salt.utils.files @@ -17,104 +18,103 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only runs on Linux systems - ''' - return __grains__['kernel'] == 'Linux' + """ + return __grains__["kernel"] == "Linux" def _new_mods(pre_mods, post_mods): - ''' + """ Return a list of the new modules, pass an lsmod dict before running modprobe and one after modprobe has run - ''' + """ pre = set() post = set() for mod in pre_mods: - pre.add(mod['module']) + pre.add(mod["module"]) for mod in post_mods: - post.add(mod['module']) + post.add(mod["module"]) return post - pre def _rm_mods(pre_mods, post_mods): - ''' + """ Return a list of the new modules, pass an lsmod dict before running modprobe and one after modprobe has run - ''' + """ pre = set() post = set() for mod in pre_mods: - pre.add(mod['module']) + pre.add(mod["module"]) for mod in post_mods: - post.add(mod['module']) + post.add(mod["module"]) return pre - post def _get_modules_conf(): - ''' + """ Return location of modules config file. Default: /etc/modules - ''' - if 'systemd' in __grains__: - return '/etc/modules-load.d/salt_managed.conf' - return '/etc/modules' + """ + if "systemd" in __grains__: + return "/etc/modules-load.d/salt_managed.conf" + return "/etc/modules" def _strip_module_name(mod): - ''' + """ Return module name and strip configuration. It is possible insert modules in this format: bonding mode=4 miimon=1000 This method return only 'bonding' - ''' - if mod.strip() == '': + """ + if mod.strip() == "": return False return mod.split()[0] def _set_persistent_module(mod): - ''' + """ Add module to configuration file to make it persistent. If module is commented uncomment it. - ''' + """ conf = _get_modules_conf() if not os.path.exists(conf): - __salt__['file.touch'](conf) + __salt__["file.touch"](conf) mod_name = _strip_module_name(mod) - if not mod_name or mod_name in mod_list(True) or mod_name \ - not in available(): + if not mod_name or mod_name in mod_list(True) or mod_name not in available(): return set() escape_mod = re.escape(mod) # If module is commented only uncomment it - if __salt__['file.search'](conf, - '^#[\t ]*{0}[\t ]*$'.format(escape_mod), - multiline=True): - __salt__['file.uncomment'](conf, escape_mod) + if __salt__["file.search"]( + conf, "^#[\t ]*{0}[\t ]*$".format(escape_mod), multiline=True + ): + __salt__["file.uncomment"](conf, escape_mod) else: - __salt__['file.append'](conf, mod) + __salt__["file.append"](conf, mod) return set([mod_name]) def _remove_persistent_module(mod, comment): - ''' + """ Remove module from configuration file. If comment is true only comment line where module is. - ''' + """ conf = _get_modules_conf() mod_name = _strip_module_name(mod) if not mod_name or mod_name not in mod_list(True): return set() escape_mod = re.escape(mod) if comment: - __salt__['file.comment'](conf, '^[\t ]*{0}[\t ]?'.format(escape_mod)) + __salt__["file.comment"](conf, "^[\t ]*{0}[\t ]?".format(escape_mod)) else: - __salt__['file.sed'](conf, '^[\t ]*{0}[\t ]?'.format(escape_mod), '') + __salt__["file.sed"](conf, "^[\t ]*{0}[\t ]?".format(escape_mod), "") return set([mod_name]) def available(): - ''' + """ Return a list of all available kernel modules CLI Example: @@ -122,36 +122,36 @@ def available(): .. code-block:: bash salt '*' kmod.available - ''' + """ ret = [] - mod_dir = os.path.join('/lib/modules/', os.uname()[2]) + mod_dir = os.path.join("/lib/modules/", os.uname()[2]) - built_in_file = os.path.join(mod_dir, 'modules.builtin') + built_in_file = os.path.join(mod_dir, "modules.builtin") if os.path.exists(built_in_file): - with salt.utils.files.fopen(built_in_file, 'r') as f: + with salt.utils.files.fopen(built_in_file, "r") as f: for line in f: # Strip .ko from the basename ret.append(os.path.basename(line)[:-4]) for root, dirs, files in salt.utils.path.os_walk(mod_dir): for fn_ in files: - if '.ko' in fn_: - ret.append(fn_[:fn_.index('.ko')].replace('-', '_')) + if ".ko" in fn_: + ret.append(fn_[: fn_.index(".ko")].replace("-", "_")) - if 'Arch' in __grains__['os_family']: + if "Arch" in __grains__["os_family"]: # Sadly this path is relative to kernel major version but ignores minor version - mod_dir_arch = '/lib/modules/extramodules-' + os.uname()[2][0:3] + '-ARCH' + mod_dir_arch = "/lib/modules/extramodules-" + os.uname()[2][0:3] + "-ARCH" for root, dirs, files in salt.utils.path.os_walk(mod_dir_arch): for fn_ in files: - if '.ko' in fn_: - ret.append(fn_[:fn_.index('.ko')].replace('-', '_')) + if ".ko" in fn_: + ret.append(fn_[: fn_.index(".ko")].replace("-", "_")) return sorted(list(ret)) def check_available(mod): - ''' + """ Check to see if the specified kernel module is available CLI Example: @@ -159,12 +159,12 @@ def check_available(mod): .. code-block:: bash salt '*' kmod.check_available kvm - ''' + """ return mod in available() def lsmod(): - ''' + """ Return a dict containing information about currently loaded modules CLI Example: @@ -172,29 +172,29 @@ def lsmod(): .. code-block:: bash salt '*' kmod.lsmod - ''' + """ ret = [] - for line in __salt__['cmd.run']('lsmod').splitlines(): + for line in __salt__["cmd.run"]("lsmod").splitlines(): comps = line.split() if not len(comps) > 2: continue - if comps[0] == 'Module': + if comps[0] == "Module": continue mdat = { - 'size': comps[1], - 'module': comps[0], - 'depcount': comps[2], + "size": comps[1], + "module": comps[0], + "depcount": comps[2], } if len(comps) > 3: - mdat['deps'] = comps[3].split(',') + mdat["deps"] = comps[3].split(",") else: - mdat['deps'] = [] + mdat["deps"] = [] ret.append(mdat) return ret def mod_list(only_persist=False): - ''' + """ Return a list of the loaded module names only_persist @@ -205,28 +205,28 @@ def mod_list(only_persist=False): .. code-block:: bash salt '*' kmod.mod_list - ''' + """ mods = set() if only_persist: conf = _get_modules_conf() if os.path.exists(conf): try: - with salt.utils.files.fopen(conf, 'r') as modules_file: + with salt.utils.files.fopen(conf, "r") as modules_file: for line in modules_file: line = line.strip() mod_name = _strip_module_name(line) - if not line.startswith('#') and mod_name: + if not line.startswith("#") and mod_name: mods.add(mod_name) except IOError: - log.error('kmod module could not open modules file at %s', conf) + log.error("kmod module could not open modules file at %s", conf) else: for mod in lsmod(): - mods.add(mod['module']) + mods.add(mod["module"]) return sorted(list(mods)) def load(mod, persist=False): - ''' + """ Load the specified kernel module mod @@ -240,10 +240,10 @@ def load(mod, persist=False): .. code-block:: bash salt '*' kmod.load kvm - ''' + """ pre_mods = lsmod() - res = __salt__['cmd.run_all']('modprobe {0}'.format(mod), python_shell=False) - if res['retcode'] == 0: + res = __salt__["cmd.run_all"]("modprobe {0}".format(mod), python_shell=False) + if res["retcode"] == 0: post_mods = lsmod() mods = _new_mods(pre_mods, post_mods) persist_mods = set() @@ -251,11 +251,11 @@ def load(mod, persist=False): persist_mods = _set_persistent_module(mod) return sorted(list(mods | persist_mods)) else: - return 'Error loading module {0}: {1}'.format(mod, res['stderr']) + return "Error loading module {0}: {1}".format(mod, res["stderr"]) def is_loaded(mod): - ''' + """ Check to see if the specified kernel module is loaded CLI Example: @@ -263,12 +263,12 @@ def is_loaded(mod): .. code-block:: bash salt '*' kmod.is_loaded kvm - ''' + """ return mod in mod_list() def remove(mod, persist=False, comment=True): - ''' + """ Remove the specified kernel module mod @@ -286,10 +286,10 @@ def remove(mod, persist=False, comment=True): .. code-block:: bash salt '*' kmod.remove kvm - ''' + """ pre_mods = lsmod() - res = __salt__['cmd.run_all']('rmmod {0}'.format(mod), python_shell=False) - if res['retcode'] == 0: + res = __salt__["cmd.run_all"]("rmmod {0}".format(mod), python_shell=False) + if res["retcode"] == 0: post_mods = lsmod() mods = _rm_mods(pre_mods, post_mods) persist_mods = set() @@ -297,4 +297,4 @@ def remove(mod, persist=False, comment=True): persist_mods = _remove_persistent_module(mod, comment) return sorted(list(mods | persist_mods)) else: - return 'Error removing module {0}: {1}'.format(mod, res['stderr']) + return "Error removing module {0}: {1}".format(mod, res["stderr"]) diff --git a/salt/modules/kubernetesmod.py b/salt/modules/kubernetesmod.py index 10e583ccf74..5582d6b9dde 100644 --- a/salt/modules/kubernetesmod.py +++ b/salt/modules/kubernetesmod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for handling kubernetes calls. :optdepends: - kubernetes Python client @@ -42,29 +42,29 @@ provided `kubeconfig` entry is preferred. - kubernetes.kubeconfig or kubernetes.kubeconfig-data - kubernetes.context -''' +""" # Import Python Futures -from __future__ import absolute_import, unicode_literals, print_function -import sys -import os.path +from __future__ import absolute_import, print_function, unicode_literals + import base64 import errno import logging -import tempfile +import os.path import signal -from time import sleep +import sys +import tempfile from contextlib import contextmanager +from time import sleep -from salt.exceptions import CommandExecutionError -from salt.ext.six import iteritems -from salt.ext import six import salt.utils.files import salt.utils.platform import salt.utils.templates import salt.utils.versions import salt.utils.yaml -from salt.exceptions import TimeoutError +from salt.exceptions import CommandExecutionError, TimeoutError +from salt.ext import six +from salt.ext.six import iteritems from salt.ext.six.moves import range # pylint: disable=import-error try: @@ -72,6 +72,7 @@ try: import kubernetes.client from kubernetes.client.rest import ApiException from urllib3.exceptions import HTTPError + # pylint: disable=no-name-in-module try: # There is an API change in Kubernetes >= 2.0.0. @@ -88,24 +89,26 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'kubernetes' +__virtualname__ = "kubernetes" def __virtual__(): - ''' + """ Check dependencies - ''' + """ if HAS_LIBS: return __virtualname__ - return False, 'python kubernetes library not found' + return False, "python kubernetes library not found" if not salt.utils.platform.is_windows(): + @contextmanager def _time_limit(seconds): def signal_handler(signum, frame): raise TimeoutError + signal.signal(signal.SIGALRM, signal_handler) signal.alarm(seconds) try: @@ -117,43 +120,43 @@ if not salt.utils.platform.is_windows(): def _setup_conn_old(**kwargs): - ''' + """ Setup kubernetes API connection singleton the old way - ''' - host = __salt__['config.option']('kubernetes.api_url', - 'http://localhost:8080') - username = __salt__['config.option']('kubernetes.user') - password = __salt__['config.option']('kubernetes.password') - ca_cert = __salt__['config.option']('kubernetes.certificate-authority-data') - client_cert = __salt__['config.option']('kubernetes.client-certificate-data') - client_key = __salt__['config.option']('kubernetes.client-key-data') - ca_cert_file = __salt__['config.option']('kubernetes.certificate-authority-file') - client_cert_file = __salt__['config.option']('kubernetes.client-certificate-file') - client_key_file = __salt__['config.option']('kubernetes.client-key-file') + """ + host = __salt__["config.option"]("kubernetes.api_url", "http://localhost:8080") + username = __salt__["config.option"]("kubernetes.user") + password = __salt__["config.option"]("kubernetes.password") + ca_cert = __salt__["config.option"]("kubernetes.certificate-authority-data") + client_cert = __salt__["config.option"]("kubernetes.client-certificate-data") + client_key = __salt__["config.option"]("kubernetes.client-key-data") + ca_cert_file = __salt__["config.option"]("kubernetes.certificate-authority-file") + client_cert_file = __salt__["config.option"]("kubernetes.client-certificate-file") + client_key_file = __salt__["config.option"]("kubernetes.client-key-file") # Override default API settings when settings are provided - if 'api_url' in kwargs: - host = kwargs.get('api_url') + if "api_url" in kwargs: + host = kwargs.get("api_url") - if 'api_user' in kwargs: - username = kwargs.get('api_user') + if "api_user" in kwargs: + username = kwargs.get("api_user") - if 'api_password' in kwargs: - password = kwargs.get('api_password') + if "api_password" in kwargs: + password = kwargs.get("api_password") - if 'api_certificate_authority_file' in kwargs: - ca_cert_file = kwargs.get('api_certificate_authority_file') + if "api_certificate_authority_file" in kwargs: + ca_cert_file = kwargs.get("api_certificate_authority_file") - if 'api_client_certificate_file' in kwargs: - client_cert_file = kwargs.get('api_client_certificate_file') + if "api_client_certificate_file" in kwargs: + client_cert_file = kwargs.get("api_client_certificate_file") - if 'api_client_key_file' in kwargs: - client_key_file = kwargs.get('api_client_key_file') + if "api_client_key_file" in kwargs: + client_key_file = kwargs.get("api_client_key_file") if ( - kubernetes.client.configuration.host != host or - kubernetes.client.configuration.user != username or - kubernetes.client.configuration.password != password): + kubernetes.client.configuration.host != host + or kubernetes.client.configuration.user != username + or kubernetes.client.configuration.password != password + ): # Recreates API connection if settings are changed kubernetes.client.configuration.__init__() @@ -164,7 +167,7 @@ def _setup_conn_old(**kwargs): if ca_cert_file: kubernetes.client.configuration.ssl_ca_cert = ca_cert_file elif ca_cert: - with tempfile.NamedTemporaryFile(prefix='salt-kube-', delete=False) as ca: + with tempfile.NamedTemporaryFile(prefix="salt-kube-", delete=False) as ca: ca.write(base64.b64decode(ca_cert)) kubernetes.client.configuration.ssl_ca_cert = ca.name else: @@ -173,7 +176,7 @@ def _setup_conn_old(**kwargs): if client_cert_file: kubernetes.client.configuration.cert_file = client_cert_file elif client_cert: - with tempfile.NamedTemporaryFile(prefix='salt-kube-', delete=False) as c: + with tempfile.NamedTemporaryFile(prefix="salt-kube-", delete=False) as c: c.write(base64.b64decode(client_cert)) kubernetes.client.configuration.cert_file = c.name else: @@ -182,7 +185,7 @@ def _setup_conn_old(**kwargs): if client_key_file: kubernetes.client.configuration.key_file = client_key_file elif client_key: - with tempfile.NamedTemporaryFile(prefix='salt-kube-', delete=False) as k: + with tempfile.NamedTemporaryFile(prefix="salt-kube-", delete=False) as k: k.write(base64.b64decode(client_key)) kubernetes.client.configuration.key_file = k.name else: @@ -192,33 +195,47 @@ def _setup_conn_old(**kwargs): # pylint: disable=no-member def _setup_conn(**kwargs): - ''' + """ Setup kubernetes API connection singleton - ''' - kubeconfig = kwargs.get('kubeconfig') or __salt__['config.option']('kubernetes.kubeconfig') - kubeconfig_data = kwargs.get('kubeconfig_data') or __salt__['config.option']('kubernetes.kubeconfig-data') - context = kwargs.get('context') or __salt__['config.option']('kubernetes.context') + """ + kubeconfig = kwargs.get("kubeconfig") or __salt__["config.option"]( + "kubernetes.kubeconfig" + ) + kubeconfig_data = kwargs.get("kubeconfig_data") or __salt__["config.option"]( + "kubernetes.kubeconfig-data" + ) + context = kwargs.get("context") or __salt__["config.option"]("kubernetes.context") - if (kubeconfig_data and not kubeconfig) or (kubeconfig_data and kwargs.get('kubeconfig_data')): - with tempfile.NamedTemporaryFile(prefix='salt-kubeconfig-', delete=False) as kcfg: + if (kubeconfig_data and not kubeconfig) or ( + kubeconfig_data and kwargs.get("kubeconfig_data") + ): + with tempfile.NamedTemporaryFile( + prefix="salt-kubeconfig-", delete=False + ) as kcfg: kcfg.write(base64.b64decode(kubeconfig_data)) kubeconfig = kcfg.name if not (kubeconfig and context): - if kwargs.get('api_url') or __salt__['config.option']('kubernetes.api_url'): - salt.utils.versions.warn_until('Sodium', - 'Kubernetes configuration via url, certificate, username and password will be removed in Sodiom. ' - 'Use \'kubeconfig\' and \'context\' instead.') + if kwargs.get("api_url") or __salt__["config.option"]("kubernetes.api_url"): + salt.utils.versions.warn_until( + "Sodium", + "Kubernetes configuration via url, certificate, username and password will be removed in Sodiom. " + "Use 'kubeconfig' and 'context' instead.", + ) try: return _setup_conn_old(**kwargs) except Exception: # pylint: disable=broad-except - raise CommandExecutionError('Old style kubernetes configuration is only supported up to python-kubernetes 2.0.0') + raise CommandExecutionError( + "Old style kubernetes configuration is only supported up to python-kubernetes 2.0.0" + ) else: - raise CommandExecutionError('Invalid kubernetes configuration. Parameter \'kubeconfig\' and \'context\' are required.') + raise CommandExecutionError( + "Invalid kubernetes configuration. Parameter 'kubeconfig' and 'context' are required." + ) kubernetes.config.load_kube_config(config_file=kubeconfig, context=context) # The return makes unit testing easier - return {'kubeconfig': kubeconfig, 'context': context} + return {"kubeconfig": kubeconfig, "context": context} def _cleanup_old(**kwargs): @@ -226,11 +243,19 @@ def _cleanup_old(**kwargs): ca = kubernetes.client.configuration.ssl_ca_cert cert = kubernetes.client.configuration.cert_file key = kubernetes.client.configuration.key_file - if cert and os.path.exists(cert) and os.path.basename(cert).startswith('salt-kube-'): + if ( + cert + and os.path.exists(cert) + and os.path.basename(cert).startswith("salt-kube-") + ): salt.utils.files.safe_rm(cert) - if key and os.path.exists(key) and os.path.basename(key).startswith('salt-kube-'): + if ( + key + and os.path.exists(key) + and os.path.basename(key).startswith("salt-kube-") + ): salt.utils.files.safe_rm(key) - if ca and os.path.exists(ca) and os.path.basename(ca).startswith('salt-kube-'): + if ca and os.path.exists(ca) and os.path.basename(ca).startswith("salt-kube-"): salt.utils.files.safe_rm(ca) except Exception: # pylint: disable=broad-except pass @@ -240,9 +265,9 @@ def _cleanup(**kwargs): if not kwargs: return _cleanup_old(**kwargs) - if 'kubeconfig' in kwargs: - kubeconfig = kwargs.get('kubeconfig') - if kubeconfig and os.path.basename(kubeconfig).startswith('salt-kubeconfig-'): + if "kubeconfig" in kwargs: + kubeconfig = kwargs.get("kubeconfig") + if kubeconfig and os.path.basename(kubeconfig).startswith("salt-kubeconfig-"): try: os.unlink(kubeconfig) except (IOError, OSError) as err: @@ -251,13 +276,13 @@ def _cleanup(**kwargs): def ping(**kwargs): - ''' + """ Checks connections with the kubernetes API server. Returns True if the connection can be established, False otherwise. CLI Example: salt '*' kubernetes.ping - ''' + """ status = True try: nodes(**kwargs) @@ -268,38 +293,41 @@ def ping(**kwargs): def nodes(**kwargs): - ''' + """ Return the names of the nodes composing the kubernetes cluster CLI Examples:: salt '*' kubernetes.nodes salt '*' kubernetes.nodes kubeconfig=/etc/salt/k8s/kubeconfig context=minikube - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.list_node() - return [k8s_node['metadata']['name'] for k8s_node in api_response.to_dict().get('items')] + return [ + k8s_node["metadata"]["name"] + for k8s_node in api_response.to_dict().get("items") + ] except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception('Exception when calling CoreV1Api->list_node') + log.exception("Exception when calling CoreV1Api->list_node") raise CommandExecutionError(exc) finally: _cleanup(**cfg) def node(name, **kwargs): - ''' + """ Return the details of the node identified by the specified name CLI Examples:: salt '*' kubernetes.node name='minikube' - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() @@ -308,7 +336,7 @@ def node(name, **kwargs): if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception('Exception when calling CoreV1Api->list_node') + log.exception("Exception when calling CoreV1Api->list_node") raise CommandExecutionError(exc) finally: _cleanup(**cfg) @@ -321,23 +349,23 @@ def node(name, **kwargs): def node_labels(name, **kwargs): - ''' + """ Return the labels of the node identified by the specified name CLI Examples:: salt '*' kubernetes.node_labels name="minikube" - ''' + """ match = node(name, **kwargs) if match is not None: - return match['metadata']['labels'] + return match["metadata"]["labels"] return {} def node_add_label(node_name, label_name, label_value, **kwargs): - ''' + """ Set the value of the label identified by `label_name` to `label_value` on the node identified by the name `node_name`. Creates the lable if not present. @@ -346,23 +374,18 @@ def node_add_label(node_name, label_name, label_value, **kwargs): salt '*' kubernetes.node_add_label node_name="minikube" \ label_name="foo" label_value="bar" - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - body = { - 'metadata': { - 'labels': { - label_name: label_value} - } - } + body = {"metadata": {"labels": {label_name: label_value}}} api_response = api_instance.patch_node(node_name, body) return api_response except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception('Exception when calling CoreV1Api->patch_node') + log.exception("Exception when calling CoreV1Api->patch_node") raise CommandExecutionError(exc) finally: _cleanup(**cfg) @@ -371,7 +394,7 @@ def node_add_label(node_name, label_name, label_value, **kwargs): def node_remove_label(node_name, label_name, **kwargs): - ''' + """ Removes the label identified by `label_name` from the node identified by the name `node_name`. @@ -379,23 +402,18 @@ def node_remove_label(node_name, label_name, **kwargs): salt '*' kubernetes.node_remove_label node_name="minikube" \ label_name="foo" - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - body = { - 'metadata': { - 'labels': { - label_name: None} - } - } + body = {"metadata": {"labels": {label_name: None}}} api_response = api_instance.patch_node(node_name, body) return api_response except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception('Exception when calling CoreV1Api->patch_node') + log.exception("Exception when calling CoreV1Api->patch_node") raise CommandExecutionError(exc) finally: _cleanup(**cfg) @@ -404,179 +422,175 @@ def node_remove_label(node_name, label_name, **kwargs): def namespaces(**kwargs): - ''' + """ Return the names of the available namespaces CLI Examples:: salt '*' kubernetes.namespaces salt '*' kubernetes.namespaces kubeconfig=/etc/salt/k8s/kubeconfig context=minikube - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.list_namespace() - return [nms['metadata']['name'] for nms in api_response.to_dict().get('items')] + return [nms["metadata"]["name"] for nms in api_response.to_dict().get("items")] except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception('Exception when calling CoreV1Api->list_namespace') + log.exception("Exception when calling CoreV1Api->list_namespace") raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def deployments(namespace='default', **kwargs): - ''' +def deployments(namespace="default", **kwargs): + """ Return a list of kubernetes deployments defined in the namespace CLI Examples:: salt '*' kubernetes.deployments salt '*' kubernetes.deployments namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.ExtensionsV1beta1Api() api_response = api_instance.list_namespaced_deployment(namespace) - return [dep['metadata']['name'] for dep in api_response.to_dict().get('items')] + return [dep["metadata"]["name"] for dep in api_response.to_dict().get("items")] except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: log.exception( - 'Exception when calling ' - 'ExtensionsV1beta1Api->list_namespaced_deployment' + "Exception when calling " + "ExtensionsV1beta1Api->list_namespaced_deployment" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def services(namespace='default', **kwargs): - ''' +def services(namespace="default", **kwargs): + """ Return a list of kubernetes services defined in the namespace CLI Examples:: salt '*' kubernetes.services salt '*' kubernetes.services namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.list_namespaced_service(namespace) - return [srv['metadata']['name'] for srv in api_response.to_dict().get('items')] + return [srv["metadata"]["name"] for srv in api_response.to_dict().get("items")] except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->list_namespaced_service' + "Exception when calling " "CoreV1Api->list_namespaced_service" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def pods(namespace='default', **kwargs): - ''' +def pods(namespace="default", **kwargs): + """ Return a list of kubernetes pods defined in the namespace CLI Examples:: salt '*' kubernetes.pods salt '*' kubernetes.pods namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.list_namespaced_pod(namespace) - return [pod['metadata']['name'] for pod in api_response.to_dict().get('items')] + return [pod["metadata"]["name"] for pod in api_response.to_dict().get("items")] except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling ' - 'CoreV1Api->list_namespaced_pod' - ) + log.exception("Exception when calling " "CoreV1Api->list_namespaced_pod") raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def secrets(namespace='default', **kwargs): - ''' +def secrets(namespace="default", **kwargs): + """ Return a list of kubernetes secrets defined in the namespace CLI Examples:: salt '*' kubernetes.secrets salt '*' kubernetes.secrets namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.list_namespaced_secret(namespace) - return [secret['metadata']['name'] for secret in api_response.to_dict().get('items')] + return [ + secret["metadata"]["name"] for secret in api_response.to_dict().get("items") + ] except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling ' - 'CoreV1Api->list_namespaced_secret' - ) + log.exception("Exception when calling " "CoreV1Api->list_namespaced_secret") raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def configmaps(namespace='default', **kwargs): - ''' +def configmaps(namespace="default", **kwargs): + """ Return a list of kubernetes configmaps defined in the namespace CLI Examples:: salt '*' kubernetes.configmaps salt '*' kubernetes.configmaps namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.list_namespaced_config_map(namespace) - return [secret['metadata']['name'] for secret in api_response.to_dict().get('items')] + return [ + secret["metadata"]["name"] for secret in api_response.to_dict().get("items") + ] except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->list_namespaced_config_map' + "Exception when calling " "CoreV1Api->list_namespaced_config_map" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def show_deployment(name, namespace='default', **kwargs): - ''' +def show_deployment(name, namespace="default", **kwargs): + """ Return the kubernetes deployment defined by name and namespace CLI Examples:: salt '*' kubernetes.show_deployment my-nginx default salt '*' kubernetes.show_deployment name=my-nginx namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.ExtensionsV1beta1Api() @@ -588,23 +602,23 @@ def show_deployment(name, namespace='default', **kwargs): return None else: log.exception( - 'Exception when calling ' - 'ExtensionsV1beta1Api->read_namespaced_deployment' + "Exception when calling " + "ExtensionsV1beta1Api->read_namespaced_deployment" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def show_service(name, namespace='default', **kwargs): - ''' +def show_service(name, namespace="default", **kwargs): + """ Return the kubernetes service defined by name and namespace CLI Examples:: salt '*' kubernetes.show_service my-nginx default salt '*' kubernetes.show_service name=my-nginx namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() @@ -616,23 +630,22 @@ def show_service(name, namespace='default', **kwargs): return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->read_namespaced_service' + "Exception when calling " "CoreV1Api->read_namespaced_service" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def show_pod(name, namespace='default', **kwargs): - ''' +def show_pod(name, namespace="default", **kwargs): + """ Return POD information for a given pod name defined in the namespace CLI Examples:: salt '*' kubernetes.show_pod guestbook-708336848-fqr2x salt '*' kubernetes.show_pod guestbook-708336848-fqr2x namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() @@ -643,23 +656,20 @@ def show_pod(name, namespace='default', **kwargs): if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling ' - 'CoreV1Api->read_namespaced_pod' - ) + log.exception("Exception when calling " "CoreV1Api->read_namespaced_pod") raise CommandExecutionError(exc) finally: _cleanup(**cfg) def show_namespace(name, **kwargs): - ''' + """ Return information for a given namespace defined by the specified name CLI Examples:: salt '*' kubernetes.show_namespace kube-system - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() @@ -670,17 +680,14 @@ def show_namespace(name, **kwargs): if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling ' - 'CoreV1Api->read_namespace' - ) + log.exception("Exception when calling " "CoreV1Api->read_namespace") raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def show_secret(name, namespace='default', decode=False, **kwargs): - ''' +def show_secret(name, namespace="default", decode=False, **kwargs): + """ Return the kubernetes secret defined by name and namespace. The secrets can be decoded if specified by the user. Warning: this has security implications. @@ -690,13 +697,13 @@ def show_secret(name, namespace='default', decode=False, **kwargs): salt '*' kubernetes.show_secret confidential default salt '*' kubernetes.show_secret name=confidential namespace=default salt '*' kubernetes.show_secret name=confidential decode=True - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.read_namespaced_secret(name, namespace) - if api_response.data and (decode or decode == 'True'): + if api_response.data and (decode or decode == "True"): for key in api_response.data: value = api_response.data[key] api_response.data[key] = base64.b64decode(value) @@ -706,30 +713,25 @@ def show_secret(name, namespace='default', decode=False, **kwargs): if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling ' - 'CoreV1Api->read_namespaced_secret' - ) + log.exception("Exception when calling " "CoreV1Api->read_namespaced_secret") raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def show_configmap(name, namespace='default', **kwargs): - ''' +def show_configmap(name, namespace="default", **kwargs): + """ Return the kubernetes configmap defined by name and namespace. CLI Examples:: salt '*' kubernetes.show_configmap game-config default salt '*' kubernetes.show_configmap name=game-config namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - api_response = api_instance.read_namespaced_config_map( - name, - namespace) + api_response = api_instance.read_namespaced_config_map(name, namespace) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -737,32 +739,30 @@ def show_configmap(name, namespace='default', **kwargs): return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->read_namespaced_config_map' + "Exception when calling " "CoreV1Api->read_namespaced_config_map" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def delete_deployment(name, namespace='default', **kwargs): - ''' +def delete_deployment(name, namespace="default", **kwargs): + """ Deletes the kubernetes deployment defined by name and namespace CLI Examples:: salt '*' kubernetes.delete_deployment my-nginx salt '*' kubernetes.delete_deployment name=my-nginx namespace=default - ''' + """ cfg = _setup_conn(**kwargs) body = kubernetes.client.V1DeleteOptions(orphan_dependents=True) try: api_instance = kubernetes.client.ExtensionsV1beta1Api() api_response = api_instance.delete_namespaced_deployment( - name=name, - namespace=namespace, - body=body) + name=name, namespace=namespace, body=body + ) mutable_api_response = api_response.to_dict() if not salt.utils.platform.is_windows(): try: @@ -770,7 +770,7 @@ def delete_deployment(name, namespace='default', **kwargs): while show_deployment(name, namespace) is not None: sleep(1) else: # pylint: disable=useless-else-on-loop - mutable_api_response['code'] = 200 + mutable_api_response["code"] = 200 except TimeoutError: pass else: @@ -778,100 +778,96 @@ def delete_deployment(name, namespace='default', **kwargs): # back to loop-counting. for i in range(60): if show_deployment(name, namespace) is None: - mutable_api_response['code'] = 200 + mutable_api_response["code"] = 200 break else: sleep(1) - if mutable_api_response['code'] != 200: - log.warning('Reached polling time limit. Deployment is not yet ' - 'deleted, but we are backing off. Sorry, but you\'ll ' - 'have to check manually.') + if mutable_api_response["code"] != 200: + log.warning( + "Reached polling time limit. Deployment is not yet " + "deleted, but we are backing off. Sorry, but you'll " + "have to check manually." + ) return mutable_api_response except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: log.exception( - 'Exception when calling ' - 'ExtensionsV1beta1Api->delete_namespaced_deployment' + "Exception when calling " + "ExtensionsV1beta1Api->delete_namespaced_deployment" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def delete_service(name, namespace='default', **kwargs): - ''' +def delete_service(name, namespace="default", **kwargs): + """ Deletes the kubernetes service defined by name and namespace CLI Examples:: salt '*' kubernetes.delete_service my-nginx default salt '*' kubernetes.delete_service name=my-nginx namespace=default - ''' + """ cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.delete_namespaced_service( - name=name, - namespace=namespace) + name=name, namespace=namespace + ) return api_response.to_dict() except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling CoreV1Api->delete_namespaced_service' - ) + log.exception("Exception when calling CoreV1Api->delete_namespaced_service") raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def delete_pod(name, namespace='default', **kwargs): - ''' +def delete_pod(name, namespace="default", **kwargs): + """ Deletes the kubernetes pod defined by name and namespace CLI Examples:: salt '*' kubernetes.delete_pod guestbook-708336848-5nl8c default salt '*' kubernetes.delete_pod name=guestbook-708336848-5nl8c namespace=default - ''' + """ cfg = _setup_conn(**kwargs) body = kubernetes.client.V1DeleteOptions(orphan_dependents=True) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.delete_namespaced_pod( - name=name, - namespace=namespace, - body=body) + name=name, namespace=namespace, body=body + ) return api_response.to_dict() except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling ' - 'CoreV1Api->delete_namespaced_pod' - ) + log.exception("Exception when calling " "CoreV1Api->delete_namespaced_pod") raise CommandExecutionError(exc) finally: _cleanup(**cfg) def delete_namespace(name, **kwargs): - ''' + """ Deletes the kubernetes namespace defined by name CLI Examples:: salt '*' kubernetes.delete_namespace salt salt '*' kubernetes.delete_namespace name=salt - ''' + """ cfg = _setup_conn(**kwargs) body = kubernetes.client.V1DeleteOptions(orphan_dependents=True) @@ -883,65 +879,58 @@ def delete_namespace(name, **kwargs): if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling ' - 'CoreV1Api->delete_namespace' - ) + log.exception("Exception when calling " "CoreV1Api->delete_namespace") raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def delete_secret(name, namespace='default', **kwargs): - ''' +def delete_secret(name, namespace="default", **kwargs): + """ Deletes the kubernetes secret defined by name and namespace CLI Examples:: salt '*' kubernetes.delete_secret confidential default salt '*' kubernetes.delete_secret name=confidential namespace=default - ''' + """ cfg = _setup_conn(**kwargs) body = kubernetes.client.V1DeleteOptions(orphan_dependents=True) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.delete_namespaced_secret( - name=name, - namespace=namespace, - body=body) + name=name, namespace=namespace, body=body + ) return api_response.to_dict() except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling CoreV1Api->delete_namespaced_secret' - ) + log.exception("Exception when calling CoreV1Api->delete_namespaced_secret") raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def delete_configmap(name, namespace='default', **kwargs): - ''' +def delete_configmap(name, namespace="default", **kwargs): + """ Deletes the kubernetes configmap defined by name and namespace CLI Examples:: salt '*' kubernetes.delete_configmap settings default salt '*' kubernetes.delete_configmap name=settings namespace=default - ''' + """ cfg = _setup_conn(**kwargs) body = kubernetes.client.V1DeleteOptions(orphan_dependents=True) try: api_instance = kubernetes.client.CoreV1Api() api_response = api_instance.delete_namespaced_config_map( - name=name, - namespace=namespace, - body=body) + name=name, namespace=namespace, body=body + ) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -949,8 +938,7 @@ def delete_configmap(name, namespace='default', **kwargs): return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->delete_namespaced_config_map' + "Exception when calling " "CoreV1Api->delete_namespaced_config_map" ) raise CommandExecutionError(exc) finally: @@ -958,19 +946,13 @@ def delete_configmap(name, namespace='default', **kwargs): def create_deployment( - name, - namespace, - metadata, - spec, - source, - template, - saltenv, - **kwargs): - ''' + name, namespace, metadata, spec, source, template, saltenv, **kwargs +): + """ Creates the kubernetes deployment as defined by the user. - ''' + """ body = __create_object_body( - kind='Deployment', + kind="Deployment", obj_class=AppsV1beta1Deployment, spec_creator=__dict_to_deployment_spec, name=name, @@ -979,14 +961,14 @@ def create_deployment( spec=spec, source=source, template=template, - saltenv=saltenv) + saltenv=saltenv, + ) cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.ExtensionsV1beta1Api() - api_response = api_instance.create_namespaced_deployment( - namespace, body) + api_response = api_instance.create_namespaced_deployment(namespace, body) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -994,28 +976,20 @@ def create_deployment( return None else: log.exception( - 'Exception when calling ' - 'ExtensionsV1beta1Api->create_namespaced_deployment' + "Exception when calling " + "ExtensionsV1beta1Api->create_namespaced_deployment" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def create_pod( - name, - namespace, - metadata, - spec, - source, - template, - saltenv, - **kwargs): - ''' +def create_pod(name, namespace, metadata, spec, source, template, saltenv, **kwargs): + """ Creates the kubernetes deployment as defined by the user. - ''' + """ body = __create_object_body( - kind='Pod', + kind="Pod", obj_class=kubernetes.client.V1Pod, spec_creator=__dict_to_pod_spec, name=name, @@ -1024,43 +998,34 @@ def create_pod( spec=spec, source=source, template=template, - saltenv=saltenv) + saltenv=saltenv, + ) cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - api_response = api_instance.create_namespaced_pod( - namespace, body) + api_response = api_instance.create_namespaced_pod(namespace, body) return api_response.to_dict() except (ApiException, HTTPError) as exc: if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling ' - 'CoreV1Api->create_namespaced_pod' - ) + log.exception("Exception when calling " "CoreV1Api->create_namespaced_pod") raise CommandExecutionError(exc) finally: _cleanup(**cfg) def create_service( - name, - namespace, - metadata, - spec, - source, - template, - saltenv, - **kwargs): - ''' + name, namespace, metadata, spec, source, template, saltenv, **kwargs +): + """ Creates the kubernetes service as defined by the user. - ''' + """ body = __create_object_body( - kind='Service', + kind="Service", obj_class=kubernetes.client.V1Service, spec_creator=__dict_to_service_spec, name=name, @@ -1069,14 +1034,14 @@ def create_service( spec=spec, source=source, template=template, - saltenv=saltenv) + saltenv=saltenv, + ) cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - api_response = api_instance.create_namespaced_service( - namespace, body) + api_response = api_instance.create_namespaced_service(namespace, body) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -1084,8 +1049,7 @@ def create_service( return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->create_namespaced_service' + "Exception when calling " "CoreV1Api->create_namespaced_service" ) raise CommandExecutionError(exc) finally: @@ -1093,14 +1057,15 @@ def create_service( def create_secret( - name, - namespace='default', - data=None, - source=None, - template=None, - saltenv='base', - **kwargs): - ''' + name, + namespace="default", + data=None, + source=None, + template=None, + saltenv="base", + **kwargs +): + """ Creates the kubernetes secret as defined by the user. CLI Examples:: @@ -1110,7 +1075,7 @@ def create_secret( salt 'minion2' kubernetes.create_secret \ name=passwords namespace=default data='{"db": "letmein"}' - ''' + """ if source: data = __read_and_render_yaml_file(source, template, saltenv) elif data is None: @@ -1123,15 +1088,14 @@ def create_secret( data[key] = base64.b64encode(data[key]) body = kubernetes.client.V1Secret( - metadata=__dict_to_object_meta(name, namespace, {}), - data=data) + metadata=__dict_to_object_meta(name, namespace, {}), data=data + ) cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - api_response = api_instance.create_namespaced_secret( - namespace, body) + api_response = api_instance.create_namespaced_secret(namespace, body) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -1139,8 +1103,7 @@ def create_secret( return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->create_namespaced_secret' + "Exception when calling " "CoreV1Api->create_namespaced_secret" ) raise CommandExecutionError(exc) finally: @@ -1148,14 +1111,9 @@ def create_secret( def create_configmap( - name, - namespace, - data, - source=None, - template=None, - saltenv='base', - **kwargs): - ''' + name, namespace, data, source=None, template=None, saltenv="base", **kwargs +): + """ Creates the kubernetes configmap as defined by the user. CLI Examples:: @@ -1165,7 +1123,7 @@ def create_configmap( salt 'minion2' kubernetes.create_configmap \ name=settings namespace=default data='{"example.conf": "# example file"}' - ''' + """ if source: data = __read_and_render_yaml_file(source, template, saltenv) elif data is None: @@ -1174,15 +1132,14 @@ def create_configmap( data = __enforce_only_strings_dict(data) body = kubernetes.client.V1ConfigMap( - metadata=__dict_to_object_meta(name, namespace, {}), - data=data) + metadata=__dict_to_object_meta(name, namespace, {}), data=data + ) cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - api_response = api_instance.create_namespaced_config_map( - namespace, body) + api_response = api_instance.create_namespaced_config_map(namespace, body) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -1190,24 +1147,21 @@ def create_configmap( return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->create_namespaced_config_map' + "Exception when calling " "CoreV1Api->create_namespaced_config_map" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def create_namespace( - name, - **kwargs): - ''' +def create_namespace(name, **kwargs): + """ Creates a namespace with the specified name. CLI Example: salt '*' kubernetes.create_namespace salt salt '*' kubernetes.create_namespace name=salt - ''' + """ meta_obj = kubernetes.client.V1ObjectMeta(name=name) body = kubernetes.client.V1Namespace(metadata=meta_obj) @@ -1224,29 +1178,21 @@ def create_namespace( if isinstance(exc, ApiException) and exc.status == 404: return None else: - log.exception( - 'Exception when calling ' - 'CoreV1Api->create_namespace' - ) + log.exception("Exception when calling " "CoreV1Api->create_namespace") raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def replace_deployment(name, - metadata, - spec, - source, - template, - saltenv, - namespace='default', - **kwargs): - ''' +def replace_deployment( + name, metadata, spec, source, template, saltenv, namespace="default", **kwargs +): + """ Replaces an existing deployment with a new one defined by name and namespace, having the specificed metadata and spec. - ''' + """ body = __create_object_body( - kind='Deployment', + kind="Deployment", obj_class=AppsV1beta1Deployment, spec_creator=__dict_to_deployment_spec, name=name, @@ -1255,14 +1201,14 @@ def replace_deployment(name, spec=spec, source=source, template=template, - saltenv=saltenv) + saltenv=saltenv, + ) cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.ExtensionsV1beta1Api() - api_response = api_instance.replace_namespaced_deployment( - name, namespace, body) + api_response = api_instance.replace_namespaced_deployment(name, namespace, body) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -1270,29 +1216,31 @@ def replace_deployment(name, return None else: log.exception( - 'Exception when calling ' - 'ExtensionsV1beta1Api->replace_namespaced_deployment' + "Exception when calling " + "ExtensionsV1beta1Api->replace_namespaced_deployment" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def replace_service(name, - metadata, - spec, - source, - template, - old_service, - saltenv, - namespace='default', - **kwargs): - ''' +def replace_service( + name, + metadata, + spec, + source, + template, + old_service, + saltenv, + namespace="default", + **kwargs +): + """ Replaces an existing service with a new one defined by name and namespace, having the specificed metadata and spec. - ''' + """ body = __create_object_body( - kind='Service', + kind="Service", obj_class=kubernetes.client.V1Service, spec_creator=__dict_to_service_spec, name=name, @@ -1301,19 +1249,19 @@ def replace_service(name, spec=spec, source=source, template=template, - saltenv=saltenv) + saltenv=saltenv, + ) # Some attributes have to be preserved # otherwise exceptions will be thrown - body.spec.cluster_ip = old_service['spec']['cluster_ip'] - body.metadata.resource_version = old_service['metadata']['resource_version'] + body.spec.cluster_ip = old_service["spec"]["cluster_ip"] + body.metadata.resource_version = old_service["metadata"]["resource_version"] cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - api_response = api_instance.replace_namespaced_service( - name, namespace, body) + api_response = api_instance.replace_namespaced_service(name, namespace, body) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -1321,22 +1269,23 @@ def replace_service(name, return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->replace_namespaced_service' + "Exception when calling " "CoreV1Api->replace_namespaced_service" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def replace_secret(name, - data, - source=None, - template=None, - saltenv='base', - namespace='default', - **kwargs): - ''' +def replace_secret( + name, + data, + source=None, + template=None, + saltenv="base", + namespace="default", + **kwargs +): + """ Replaces an existing secret with a new one defined by name and namespace, having the specificed data. @@ -1347,7 +1296,7 @@ def replace_secret(name, salt 'minion2' kubernetes.replace_secret \ name=passwords namespace=saltstack data='{"db": "passw0rd"}' - ''' + """ if source: data = __read_and_render_yaml_file(source, template, saltenv) elif data is None: @@ -1360,15 +1309,14 @@ def replace_secret(name, data[key] = base64.b64encode(data[key]) body = kubernetes.client.V1Secret( - metadata=__dict_to_object_meta(name, namespace, {}), - data=data) + metadata=__dict_to_object_meta(name, namespace, {}), data=data + ) cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - api_response = api_instance.replace_namespaced_secret( - name, namespace, body) + api_response = api_instance.replace_namespaced_secret(name, namespace, body) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -1376,22 +1324,23 @@ def replace_secret(name, return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->replace_namespaced_secret' + "Exception when calling " "CoreV1Api->replace_namespaced_secret" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def replace_configmap(name, - data, - source=None, - template=None, - saltenv='base', - namespace='default', - **kwargs): - ''' +def replace_configmap( + name, + data, + source=None, + template=None, + saltenv="base", + namespace="default", + **kwargs +): + """ Replaces an existing configmap with a new one defined by name and namespace with the specified data. @@ -1402,22 +1351,21 @@ def replace_configmap(name, salt 'minion2' kubernetes.replace_configmap \ name=settings namespace=default data='{"example.conf": "# example file"}' - ''' + """ if source: data = __read_and_render_yaml_file(source, template, saltenv) data = __enforce_only_strings_dict(data) body = kubernetes.client.V1ConfigMap( - metadata=__dict_to_object_meta(name, namespace, {}), - data=data) + metadata=__dict_to_object_meta(name, namespace, {}), data=data + ) cfg = _setup_conn(**kwargs) try: api_instance = kubernetes.client.CoreV1Api() - api_response = api_instance.replace_namespaced_config_map( - name, namespace, body) + api_response = api_instance.replace_namespaced_config_map(name, namespace, body) return api_response.to_dict() except (ApiException, HTTPError) as exc: @@ -1425,60 +1373,60 @@ def replace_configmap(name, return None else: log.exception( - 'Exception when calling ' - 'CoreV1Api->replace_namespaced_configmap' + "Exception when calling " "CoreV1Api->replace_namespaced_configmap" ) raise CommandExecutionError(exc) finally: _cleanup(**cfg) -def __create_object_body(kind, - obj_class, - spec_creator, - name, - namespace, - metadata, - spec, - source, - template, - saltenv): - ''' +def __create_object_body( + kind, + obj_class, + spec_creator, + name, + namespace, + metadata, + spec, + source, + template, + saltenv, +): + """ Create a Kubernetes Object body instance. - ''' + """ if source: src_obj = __read_and_render_yaml_file(source, template, saltenv) if ( - not isinstance(src_obj, dict) or - 'kind' not in src_obj or - src_obj['kind'] != kind): + not isinstance(src_obj, dict) + or "kind" not in src_obj + or src_obj["kind"] != kind + ): raise CommandExecutionError( - 'The source file should define only ' - 'a {0} object'.format(kind)) + "The source file should define only " "a {0} object".format(kind) + ) - if 'metadata' in src_obj: - metadata = src_obj['metadata'] - if 'spec' in src_obj: - spec = src_obj['spec'] + if "metadata" in src_obj: + metadata = src_obj["metadata"] + if "spec" in src_obj: + spec = src_obj["spec"] return obj_class( metadata=__dict_to_object_meta(name, namespace, metadata), - spec=spec_creator(spec)) + spec=spec_creator(spec), + ) -def __read_and_render_yaml_file(source, - template, - saltenv): - ''' +def __read_and_render_yaml_file(source, template, saltenv): + """ Read a yaml file and, if needed, renders that using the specifieds templating. Returns the python objects defined inside of the file. - ''' - sfn = __salt__['cp.cache_file'](source, saltenv) + """ + sfn = __salt__["cp.cache_file"](source, saltenv) if not sfn: - raise CommandExecutionError( - 'Source file \'{0}\' not found'.format(source)) + raise CommandExecutionError("Source file '{0}' not found".format(source)) - with salt.utils.files.fopen(sfn, 'r') as src: + with salt.utils.files.fopen(sfn, "r") as src: contents = src.read() if template: @@ -1494,36 +1442,37 @@ def __read_and_render_yaml_file(source, grains=__grains__, pillar=__pillar__, salt=__salt__, - opts=__opts__) + opts=__opts__, + ) - if not data['result']: + if not data["result"]: # Failed to render the template raise CommandExecutionError( - 'Failed to render file path with error: ' - '{0}'.format(data['data']) + "Failed to render file path with error: " + "{0}".format(data["data"]) ) - contents = data['data'].encode('utf-8') + contents = data["data"].encode("utf-8") else: raise CommandExecutionError( - 'Unknown template specified: {0}'.format( - template)) + "Unknown template specified: {0}".format(template) + ) return salt.utils.yaml.safe_load(contents) def __dict_to_object_meta(name, namespace, metadata): - ''' + """ Converts a dictionary into kubernetes ObjectMetaV1 instance. - ''' + """ meta_obj = kubernetes.client.V1ObjectMeta() meta_obj.namespace = namespace # Replicate `kubectl [create|replace|apply] --record` - if 'annotations' not in metadata: - metadata['annotations'] = {} - if 'kubernetes.io/change-cause' not in metadata['annotations']: - metadata['annotations']['kubernetes.io/change-cause'] = ' '.join(sys.argv) + if "annotations" not in metadata: + metadata["annotations"] = {} + if "kubernetes.io/change-cause" not in metadata["annotations"]: + metadata["annotations"]["kubernetes.io/change-cause"] = " ".join(sys.argv) for key, value in iteritems(metadata): if hasattr(meta_obj, key): @@ -1531,18 +1480,19 @@ def __dict_to_object_meta(name, namespace, metadata): if meta_obj.name != name: log.warning( - 'The object already has a name attribute, overwriting it with ' - 'the one defined inside of salt') + "The object already has a name attribute, overwriting it with " + "the one defined inside of salt" + ) meta_obj.name = name return meta_obj def __dict_to_deployment_spec(spec): - ''' + """ Converts a dictionary into kubernetes AppsV1beta1DeploymentSpec instance. - ''' - spec_obj = AppsV1beta1DeploymentSpec(template=spec.get('template', '')) + """ + spec_obj = AppsV1beta1DeploymentSpec(template=spec.get("template", "")) for key, value in iteritems(spec): if hasattr(spec_obj, key): setattr(spec_obj, key, value) @@ -1551,9 +1501,9 @@ def __dict_to_deployment_spec(spec): def __dict_to_pod_spec(spec): - ''' + """ Converts a dictionary into kubernetes V1PodSpec instance. - ''' + """ spec_obj = kubernetes.client.V1PodSpec() for key, value in iteritems(spec): if hasattr(spec_obj, key): @@ -1563,12 +1513,12 @@ def __dict_to_pod_spec(spec): def __dict_to_service_spec(spec): - ''' + """ Converts a dictionary into kubernetes V1ServiceSpec instance. - ''' + """ spec_obj = kubernetes.client.V1ServiceSpec() for key, value in iteritems(spec): # pylint: disable=too-many-nested-blocks - if key == 'ports': + if key == "ports": spec_obj.ports = [] for port in value: kube_port = kubernetes.client.V1ServicePort() @@ -1586,9 +1536,9 @@ def __dict_to_service_spec(spec): def __enforce_only_strings_dict(dictionary): - ''' + """ Returns a dictionary that has string keys and values. - ''' + """ ret = {} for key, value in iteritems(dictionary): diff --git a/salt/modules/launchctl_service.py b/salt/modules/launchctl_service.py index 052e9691cc7..0d1acf72584 100644 --- a/salt/modules/launchctl_service.py +++ b/salt/modules/launchctl_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for the management of MacOS systems that use launchd/launchctl .. important:: @@ -9,52 +9,62 @@ Module for the management of MacOS systems that use launchd/launchctl `. :depends: - plistlib Python module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import fnmatch import logging import os import plistlib -import fnmatch import re # Import salt libs import salt.utils.data +import salt.utils.decorators as decorators import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils -import salt.utils.decorators as decorators -from salt.utils.versions import LooseVersion as _LooseVersion from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" BEFORE_YOSEMITE = True def __virtual__(): - ''' + """ Only work on MacOS - ''' + """ if not salt.utils.platform.is_darwin(): - return (False, 'Failed to load the mac_service module:\n' - 'Only available on macOS systems.') + return ( + False, + "Failed to load the mac_service module:\n" + "Only available on macOS systems.", + ) - if not os.path.exists('/bin/launchctl'): - return (False, 'Failed to load the mac_service module:\n' - 'Required binary not found: "/bin/launchctl"') + if not os.path.exists("/bin/launchctl"): + return ( + False, + "Failed to load the mac_service module:\n" + 'Required binary not found: "/bin/launchctl"', + ) - if _LooseVersion(__grains__['osrelease']) >= _LooseVersion('10.11'): - return (False, 'Failed to load the mac_service module:\n' - 'Not available on El Capitan, uses mac_service.py') + if _LooseVersion(__grains__["osrelease"]) >= _LooseVersion("10.11"): + return ( + False, + "Failed to load the mac_service module:\n" + "Not available on El Capitan, uses mac_service.py", + ) - if _LooseVersion(__grains__['osrelease']) >= _LooseVersion('10.10'): + if _LooseVersion(__grains__["osrelease"]) >= _LooseVersion("10.10"): global BEFORE_YOSEMITE BEFORE_YOSEMITE = False @@ -62,22 +72,22 @@ def __virtual__(): def _launchd_paths(): - ''' + """ Paths where launchd services can be found - ''' + """ return [ - '/Library/LaunchAgents', - '/Library/LaunchDaemons', - '/System/Library/LaunchAgents', - '/System/Library/LaunchDaemons', + "/Library/LaunchAgents", + "/Library/LaunchDaemons", + "/System/Library/LaunchAgents", + "/System/Library/LaunchDaemons", ] @decorators.memoize def _available_services(): - ''' + """ Return a dictionary of all available services on the system - ''' + """ available_services = dict() for launch_dir in _launchd_paths(): for root, dirs, files in salt.utils.path.os_walk(launch_dir): @@ -93,28 +103,29 @@ def _available_services(): # This assumes most of the plist files # will be already in XML format with salt.utils.files.fopen(file_path): - plist = plistlib.readPlist( - salt.utils.data.decode(true_path) - ) + plist = plistlib.readPlist(salt.utils.data.decode(true_path)) except Exception: # pylint: disable=broad-except # If plistlib is unable to read the file we'll need to use # the system provided plutil program to do the conversion cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format( - true_path) - plist_xml = __salt__['cmd.run_all']( - cmd, python_shell=False)['stdout'] + true_path + ) + plist_xml = __salt__["cmd.run_all"](cmd, python_shell=False)[ + "stdout" + ] if six.PY2: plist = plistlib.readPlistFromString(plist_xml) else: plist = plistlib.readPlistFromBytes( - salt.utils.stringutils.to_bytes(plist_xml)) + salt.utils.stringutils.to_bytes(plist_xml) + ) try: available_services[plist.Label.lower()] = { - 'filename': filename, - 'file_path': true_path, - 'plist': plist, + "filename": filename, + "file_path": true_path, + "plist": plist, } except AttributeError: # As of MacOS 10.12 there might be plist files without Label key @@ -126,9 +137,9 @@ def _available_services(): def _service_by_name(name): - ''' + """ Return the service info for a service by label, filename or path - ''' + """ services = _available_services() name = name.lower() @@ -137,10 +148,10 @@ def _service_by_name(name): return services[name] for service in six.itervalues(services): - if service['file_path'].lower() == name: + if service["file_path"].lower() == name: # Match on full path return service - basename, ext = os.path.splitext(service['filename']) + basename, ext = os.path.splitext(service["filename"]) if basename.lower() == name: # Match on basename return service @@ -149,7 +160,7 @@ def _service_by_name(name): def get_all(): - ''' + """ Return all installed services CLI Example: @@ -157,17 +168,16 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' - cmd = 'launchctl list' + """ + cmd = "launchctl list" service_lines = [ - line for line in __salt__['cmd.run'](cmd).splitlines() - if not line.startswith('PID') + line + for line in __salt__["cmd.run"](cmd).splitlines() + if not line.startswith("PID") ] - service_labels_from_list = [ - line.split("\t")[2] for line in service_lines - ] + service_labels_from_list = [line.split("\t")[2] for line in service_lines] service_labels_from_services = list(_available_services().keys()) return sorted(set(service_labels_from_list + service_labels_from_services)) @@ -175,25 +185,23 @@ def get_all(): def _get_launchctl_data(job_label, runas=None): if BEFORE_YOSEMITE: - cmd = 'launchctl list -x {0}'.format(job_label) + cmd = "launchctl list -x {0}".format(job_label) else: - cmd = 'launchctl list {0}'.format(job_label) + cmd = "launchctl list {0}".format(job_label) - launchctl_data = __salt__['cmd.run_all'](cmd, - python_shell=False, - runas=runas) + launchctl_data = __salt__["cmd.run_all"](cmd, python_shell=False, runas=runas) - if launchctl_data['stderr']: + if launchctl_data["stderr"]: # The service is not loaded, further, it might not even exist # in either case we didn't get XML to parse, so return an empty # dict return None - return launchctl_data['stdout'] + return launchctl_data["stdout"] def available(job_label): - ''' + """ Check that the given service is available. CLI Example: @@ -201,12 +209,12 @@ def available(job_label): .. code-block:: bash salt '*' service.available com.openssh.sshd - ''' + """ return True if _service_by_name(job_label) else False def missing(job_label): - ''' + """ The inverse of service.available Check that the given service is not available. @@ -215,12 +223,12 @@ def missing(job_label): .. code-block:: bash salt '*' service.missing com.openssh.sshd - ''' + """ return False if _service_by_name(job_label) else True def status(name, runas=None): - ''' + """ Return the status for a service via systemd. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -241,9 +249,9 @@ def status(name, runas=None): .. code-block:: bash salt '*' service.status - ''' + """ - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: @@ -252,15 +260,17 @@ def status(name, runas=None): for service in services: service_info = _service_by_name(service) - lookup_name = service_info['plist']['Label'] if service_info else service + lookup_name = service_info["plist"]["Label"] if service_info else service launchctl_data = _get_launchctl_data(lookup_name, runas=runas) if launchctl_data: if BEFORE_YOSEMITE: if six.PY3: - results[service] = 'PID' in plistlib.loads(launchctl_data) + results[service] = "PID" in plistlib.loads(launchctl_data) else: - results[service] = 'PID' in dict(plistlib.readPlistFromString(launchctl_data)) + results[service] = "PID" in dict( + plistlib.readPlistFromString(launchctl_data) + ) else: pattern = '"PID" = [0-9]+;' results[service] = True if re.search(pattern, launchctl_data) else False @@ -272,7 +282,7 @@ def status(name, runas=None): def stop(job_label, runas=None): - ''' + """ Stop the specified service CLI Example: @@ -282,18 +292,17 @@ def stop(job_label, runas=None): salt '*' service.stop salt '*' service.stop org.ntp.ntpd salt '*' service.stop /System/Library/LaunchDaemons/org.ntp.ntpd.plist - ''' + """ service = _service_by_name(job_label) if service: - cmd = 'launchctl unload -w {0}'.format(service['file_path'], - runas=runas) - return not __salt__['cmd.retcode'](cmd, runas=runas, python_shell=False) + cmd = "launchctl unload -w {0}".format(service["file_path"], runas=runas) + return not __salt__["cmd.retcode"](cmd, runas=runas, python_shell=False) return False def start(job_label, runas=None): - ''' + """ Start the specified service CLI Example: @@ -303,17 +312,17 @@ def start(job_label, runas=None): salt '*' service.start salt '*' service.start org.ntp.ntpd salt '*' service.start /System/Library/LaunchDaemons/org.ntp.ntpd.plist - ''' + """ service = _service_by_name(job_label) if service: - cmd = 'launchctl load -w {0}'.format(service['file_path'], runas=runas) - return not __salt__['cmd.retcode'](cmd, runas=runas, python_shell=False) + cmd = "launchctl load -w {0}".format(service["file_path"], runas=runas) + return not __salt__["cmd.retcode"](cmd, runas=runas, python_shell=False) return False def restart(job_label, runas=None): - ''' + """ Restart the named service CLI Example: @@ -321,13 +330,13 @@ def restart(job_label, runas=None): .. code-block:: bash salt '*' service.restart - ''' + """ stop(job_label, runas=runas) return start(job_label, runas=runas) def enabled(job_label, runas=None): - ''' + """ Return True if the named service is enabled, false otherwise CLI Example: @@ -335,12 +344,12 @@ def enabled(job_label, runas=None): .. code-block:: bash salt '*' service.enabled - ''' - overrides_data = dict(plistlib.readPlist( - '/var/db/launchd.db/com.apple.launchd/overrides.plist' - )) + """ + overrides_data = dict( + plistlib.readPlist("/var/db/launchd.db/com.apple.launchd/overrides.plist") + ) if overrides_data.get(job_label, False): - if overrides_data[job_label]['Disabled']: + if overrides_data[job_label]["Disabled"]: return False else: return True @@ -349,7 +358,7 @@ def enabled(job_label, runas=None): def disabled(job_label, runas=None): - ''' + """ Return True if the named service is disabled, false otherwise CLI Example: @@ -357,12 +366,12 @@ def disabled(job_label, runas=None): .. code-block:: bash salt '*' service.disabled - ''' - overrides_data = dict(plistlib.readPlist( - '/var/db/launchd.db/com.apple.launchd/overrides.plist' - )) + """ + overrides_data = dict( + plistlib.readPlist("/var/db/launchd.db/com.apple.launchd/overrides.plist") + ) if overrides_data.get(job_label, False): - if overrides_data[job_label]['Disabled']: + if overrides_data[job_label]["Disabled"]: return True else: return False diff --git a/salt/modules/layman.py b/salt/modules/layman.py index b62b5771afb..e292357b076 100644 --- a/salt/modules/layman.py +++ b/salt/modules/layman.py @@ -1,37 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" Support for Layman -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import salt.utils.path import salt.exceptions +import salt.utils.path def __virtual__(): - ''' + """ Only work on Gentoo systems with layman installed - ''' - if __grains__['os'] == 'Gentoo' and salt.utils.path.which('layman'): - return 'layman' - return (False, 'layman execution module cannot be loaded: only available on Gentoo with layman installed.') + """ + if __grains__["os"] == "Gentoo" and salt.utils.path.which("layman"): + return "layman" + return ( + False, + "layman execution module cannot be loaded: only available on Gentoo with layman installed.", + ) def _get_makeconf(): - ''' + """ Find the correct make.conf. Gentoo recently moved the make.conf but still supports the old location, using the old location first - ''' - old_conf = '/etc/make.conf' - new_conf = '/etc/portage/make.conf' - if __salt__['file.file_exists'](old_conf): + """ + old_conf = "/etc/make.conf" + new_conf = "/etc/portage/make.conf" + if __salt__["file.file_exists"](old_conf): return old_conf - elif __salt__['file.file_exists'](new_conf): + elif __salt__["file.file_exists"](new_conf): return new_conf def add(overlay): - ''' + """ Add the given overlay from the cached remote list to your locally installed overlays. Specify 'ALL' to add all overlays from the remote list. @@ -43,30 +46,30 @@ def add(overlay): .. code-block:: bash salt '*' layman.add - ''' + """ ret = list() old_overlays = list_local() - cmd = 'layman --quietness=0 --add {0}'.format(overlay) - add_attempt = __salt__['cmd.run_all'](cmd, python_shell=False, stdin='y') - if add_attempt['retcode'] != 0: - raise salt.exceptions.CommandExecutionError(add_attempt['stdout']) + cmd = "layman --quietness=0 --add {0}".format(overlay) + add_attempt = __salt__["cmd.run_all"](cmd, python_shell=False, stdin="y") + if add_attempt["retcode"] != 0: + raise salt.exceptions.CommandExecutionError(add_attempt["stdout"]) new_overlays = list_local() # If we did not have any overlays before and we successfully added # a new one. We need to ensure the make.conf is sourcing layman's # make.conf so emerge can see the overlays if len(old_overlays) == 0 and len(new_overlays) > 0: - srcline = 'source /var/lib/layman/make.conf' + srcline = "source /var/lib/layman/make.conf" makeconf = _get_makeconf() - if not __salt__['file.contains'](makeconf, 'layman'): - __salt__['file.append'](makeconf, srcline) + if not __salt__["file.contains"](makeconf, "layman"): + __salt__["file.append"](makeconf, srcline) ret = [overlay for overlay in new_overlays if overlay not in old_overlays] return ret def delete(overlay): - ''' + """ Remove the given overlay from the your locally installed overlays. Specify 'ALL' to remove all overlays. @@ -77,29 +80,29 @@ def delete(overlay): .. code-block:: bash salt '*' layman.delete - ''' + """ ret = list() old_overlays = list_local() - cmd = 'layman --quietness=0 --delete {0}'.format(overlay) - delete_attempt = __salt__['cmd.run_all'](cmd, python_shell=False) - if delete_attempt['retcode'] != 0: - raise salt.exceptions.CommandExecutionError(delete_attempt['stdout']) + cmd = "layman --quietness=0 --delete {0}".format(overlay) + delete_attempt = __salt__["cmd.run_all"](cmd, python_shell=False) + if delete_attempt["retcode"] != 0: + raise salt.exceptions.CommandExecutionError(delete_attempt["stdout"]) new_overlays = list_local() # If we now have no overlays added, We need to ensure that the make.conf # does not source layman's make.conf, as it will break emerge if len(new_overlays) == 0: - srcline = 'source /var/lib/layman/make.conf' + srcline = "source /var/lib/layman/make.conf" makeconf = _get_makeconf() - if __salt__['file.contains'](makeconf, 'layman'): - __salt__['file.sed'](makeconf, srcline, '') + if __salt__["file.contains"](makeconf, "layman"): + __salt__["file.sed"](makeconf, srcline, "") ret = [overlay for overlay in old_overlays if overlay not in new_overlays] return ret -def sync(overlay='ALL'): - ''' +def sync(overlay="ALL"): + """ Update the specified overlay. Use 'ALL' to synchronize all overlays. This is the default if no overlay is specified. @@ -111,13 +114,13 @@ def sync(overlay='ALL'): .. code-block:: bash salt '*' layman.sync - ''' - cmd = 'layman --quietness=0 --sync {0}'.format(overlay) - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + """ + cmd = "layman --quietness=0 --sync {0}".format(overlay) + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 def list_local(): - ''' + """ List the locally installed overlays. Return a list of installed overlays: @@ -127,15 +130,15 @@ def list_local(): .. code-block:: bash salt '*' layman.list_local - ''' - cmd = 'layman --quietness=1 --list-local --nocolor' - out = __salt__['cmd.run'](cmd, python_shell=False).split('\n') + """ + cmd = "layman --quietness=1 --list-local --nocolor" + out = __salt__["cmd.run"](cmd, python_shell=False).split("\n") ret = [line.split()[1] for line in out if len(line.split()) > 2] return ret def list_all(): - ''' + """ List all overlays, including remote ones. Return a list of available overlays: @@ -145,8 +148,8 @@ def list_all(): .. code-block:: bash salt '*' layman.list_all - ''' - cmd = 'layman --quietness=1 --list --nocolor' - out = __salt__['cmd.run'](cmd, python_shell=False).split('\n') + """ + cmd = "layman --quietness=1 --list --nocolor" + out = __salt__["cmd.run"](cmd, python_shell=False).split("\n") ret = [line.split()[1] for line in out if len(line.split()) > 2] return ret diff --git a/salt/modules/ldap3.py b/salt/modules/ldap3.py index 36eec5fa88f..2354cf97358 100644 --- a/salt/modules/ldap3.py +++ b/salt/modules/ldap3.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Query and modify an LDAP database (alternative interface) ========================================================= @@ -9,50 +9,54 @@ This is an alternative to the ``ldap`` interface provided by the :py:mod:`ldapmod ` execution module. :depends: - ``ldap`` Python module -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging import sys +import salt.utils.data +from salt.ext import six + available_backends = set() try: import ldap import ldap.ldapobject # pylint: disable=no-name-in-module import ldap.modlist # pylint: disable=no-name-in-module import ldap.sasl # pylint: disable=no-name-in-module - available_backends.add('ldap') + + available_backends.add("ldap") except ImportError: pass -import salt.utils.data -from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): - '''Only load this module if the Python ldap module is present''' + """Only load this module if the Python ldap module is present""" return bool(len(available_backends)) class LDAPError(Exception): - '''Base class of all LDAP exceptions raised by backends. + """Base class of all LDAP exceptions raised by backends. This is only used for errors encountered while interacting with the LDAP server; usage errors (e.g., invalid backend name) will have a different type. :ivar cause: backend exception object, if applicable - ''' + """ + def __init__(self, message, cause=None): super(LDAPError, self).__init__(message) self.cause = cause def _convert_exception(e): - '''Convert an ldap backend exception to an LDAPError and raise it.''' - args = ('exception in ldap backend: {0}'.format(repr(e)), e) + """Convert an ldap backend exception to an LDAPError and raise it.""" + args = ("exception in ldap backend: {0}".format(repr(e)), e) if six.PY2: six.reraise(LDAPError, args, sys.exc_info()[2]) else: @@ -60,29 +64,31 @@ def _convert_exception(e): def _bind(l, bind=None): - '''Bind helper.''' + """Bind helper.""" if bind is None: return - method = bind.get('method', 'simple') + method = bind.get("method", "simple") if method is None: return - elif method == 'simple': - l.simple_bind_s(bind.get('dn', ''), bind.get('password', '')) - elif method == 'sasl': - sasl_class = getattr(ldap.sasl, - bind.get('mechanism', 'EXTERNAL').lower()) - creds = bind.get('credentials', None) + elif method == "simple": + l.simple_bind_s(bind.get("dn", ""), bind.get("password", "")) + elif method == "sasl": + sasl_class = getattr(ldap.sasl, bind.get("mechanism", "EXTERNAL").lower()) + creds = bind.get("credentials", None) if creds is None: creds = {} - auth = sasl_class(*creds.get('args', []), **creds.get('kwargs', {})) - l.sasl_interactive_bind_s(bind.get('dn', ''), auth) + auth = sasl_class(*creds.get("args", []), **creds.get("kwargs", {})) + l.sasl_interactive_bind_s(bind.get("dn", ""), auth) else: - raise ValueError('unsupported bind method "' + method - + '"; supported bind methods: simple sasl') + raise ValueError( + 'unsupported bind method "' + + method + + '"; supported bind methods: simple sasl' + ) def _format_unicode_password(pwd): - '''Formats a string per Microsoft AD password specifications. + """Formats a string per Microsoft AD password specifications. The string must be enclosed in double quotes and UTF-16 encoded. See: https://msdn.microsoft.com/en-us/library/cc223248.aspx @@ -91,8 +97,8 @@ def _format_unicode_password(pwd): :returns: A unicode string - ''' - return '"{0}"'.format(pwd).encode('utf-16-le') + """ + return '"{0}"'.format(pwd).encode("utf-16-le") class _connect_ctx(object): @@ -107,7 +113,7 @@ class _connect_ctx(object): def connect(connect_spec=None): - '''Connect and optionally bind to an LDAP server. + """Connect and optionally bind to an LDAP server. :param connect_spec: This can be an LDAP connection object returned by a previous @@ -259,56 +265,66 @@ def connect(connect_spec=None): 'dn': 'cn=admin,dc=example,dc=com', 'password': 'secret'} }" - ''' + """ if isinstance(connect_spec, _connect_ctx): return connect_spec if connect_spec is None: connect_spec = {} - backend_name = connect_spec.get('backend', 'ldap') + backend_name = connect_spec.get("backend", "ldap") if backend_name not in available_backends: - raise ValueError('unsupported backend or required Python module' - + ' unavailable: {0}'.format(backend_name)) - url = connect_spec.get('url', 'ldapi:///') + raise ValueError( + "unsupported backend or required Python module" + + " unavailable: {0}".format(backend_name) + ) + url = connect_spec.get("url", "ldapi:///") try: l = ldap.initialize(url) l.protocol_version = ldap.VERSION3 # set up tls - tls = connect_spec.get('tls', None) + tls = connect_spec.get("tls", None) if tls is None: tls = {} vars = {} for k, v in six.iteritems(tls): - if k in ('starttls', 'newctx'): + if k in ("starttls", "newctx"): vars[k] = True - elif k in ('crlcheck', 'require_cert'): - l.set_option(getattr(ldap, 'OPT_X_TLS_' + k.upper()), - getattr(ldap, 'OPT_X_TLS_' + v.upper())) + elif k in ("crlcheck", "require_cert"): + l.set_option( + getattr(ldap, "OPT_X_TLS_" + k.upper()), + getattr(ldap, "OPT_X_TLS_" + v.upper()), + ) else: - l.set_option(getattr(ldap, 'OPT_X_TLS_' + k.upper()), v) - if vars.get('starttls', False): + l.set_option(getattr(ldap, "OPT_X_TLS_" + k.upper()), v) + if vars.get("starttls", False): l.start_tls_s() - if vars.get('newctx', False): + if vars.get("newctx", False): l.set_option(ldap.OPT_X_TLS_NEWCTX, 0) # set up other options l.set_option(ldap.OPT_REFERRALS, 0) - opts = connect_spec.get('opts', None) + opts = connect_spec.get("opts", None) if opts is None: opts = {} for k, v in six.iteritems(opts): - opt = getattr(ldap, 'OPT_' + k.upper()) + opt = getattr(ldap, "OPT_" + k.upper()) l.set_option(opt, v) - _bind(l, connect_spec.get('bind', None)) + _bind(l, connect_spec.get("bind", None)) except ldap.LDAPError as e: _convert_exception(e) return _connect_ctx(l) -def search(connect_spec, base, scope='subtree', filterstr='(objectClass=*)', - attrlist=None, attrsonly=0): - '''Search an LDAP database. +def search( + connect_spec, + base, + scope="subtree", + filterstr="(objectClass=*)", + attrlist=None, + attrsonly=0, +): + """Search an LDAP database. :param connect_spec: See the documentation for the ``connect_spec`` parameter for @@ -357,9 +373,9 @@ def search(connect_spec, base, scope='subtree', filterstr='(objectClass=*)', 'password': 'secret', }, }" "base='dc=example,dc=com'" - ''' + """ l = connect(connect_spec) - scope = getattr(ldap, 'SCOPE_' + scope.upper()) + scope = getattr(ldap, "SCOPE_" + scope.upper()) try: results = l.c.search_s(base, scope, filterstr, attrlist, attrsonly) except ldap.NO_SUCH_OBJECT: @@ -370,7 +386,7 @@ def search(connect_spec, base, scope='subtree', filterstr='(objectClass=*)', def add(connect_spec, dn, attributes): - '''Add an entry to an LDAP database. + """Add an entry to an LDAP database. :param connect_spec: See the documentation for the ``connect_spec`` parameter for @@ -397,17 +413,23 @@ def add(connect_spec, dn, attributes): 'password': 'secret', }, }" "dn='dc=example,dc=com'" "attributes={'example': 'values'}" - ''' + """ l = connect(connect_spec) # convert the "iterable of values" to lists in case that's what # addModlist() expects (also to ensure that the caller's objects # are not modified) - attributes = dict(((attr, salt.utils.data.encode(list(vals))) - for attr, vals in six.iteritems(attributes))) - log.info('adding entry: dn: %s attributes: %s', repr(dn), repr(attributes)) + attributes = dict( + ( + (attr, salt.utils.data.encode(list(vals))) + for attr, vals in six.iteritems(attributes) + ) + ) + log.info("adding entry: dn: %s attributes: %s", repr(dn), repr(attributes)) - if 'unicodePwd' in attributes: - attributes['unicodePwd'] = [_format_unicode_password(x) for x in attributes['unicodePwd']] + if "unicodePwd" in attributes: + attributes["unicodePwd"] = [ + _format_unicode_password(x) for x in attributes["unicodePwd"] + ] modlist = ldap.modlist.addModlist(attributes) try: @@ -418,7 +440,7 @@ def add(connect_spec, dn, attributes): def delete(connect_spec, dn): - '''Delete an entry from an LDAP database. + """Delete an entry from an LDAP database. :param connect_spec: See the documentation for the ``connect_spec`` parameter for @@ -440,9 +462,9 @@ def delete(connect_spec, dn): 'method': 'simple', 'password': 'secret'} }" dn='cn=admin,dc=example,dc=com' - ''' + """ l = connect(connect_spec) - log.info('deleting entry: dn: %s', repr(dn)) + log.info("deleting entry: dn: %s", repr(dn)) try: l.c.delete_s(dn) except ldap.LDAPError as e: @@ -451,7 +473,7 @@ def delete(connect_spec, dn): def modify(connect_spec, dn, directives): - '''Modify an entry in an LDAP database. + """Modify an entry in an LDAP database. :param connect_spec: See the documentation for the ``connect_spec`` parameter for @@ -496,18 +518,23 @@ def modify(connect_spec, dn, directives): 'password': 'secret'} }" dn='cn=admin,dc=example,dc=com' directives="('add', 'example', ['example_val'])" - ''' + """ l = connect(connect_spec) # convert the "iterable of values" to lists in case that's what # modify_s() expects (also to ensure that the caller's objects are # not modified) - modlist = [(getattr(ldap, 'MOD_' + op.upper()), attr, list(vals)) - for op, attr, vals in directives] + modlist = [ + (getattr(ldap, "MOD_" + op.upper()), attr, list(vals)) + for op, attr, vals in directives + ] for idx, mod in enumerate(modlist): - if mod[1] == 'unicodePwd': - modlist[idx] = (mod[0], mod[1], - [_format_unicode_password(x) for x in mod[2]]) + if mod[1] == "unicodePwd": + modlist[idx] = ( + mod[0], + mod[1], + [_format_unicode_password(x) for x in mod[2]], + ) modlist = salt.utils.data.decode(modlist, to_str=True, preserve_tuples=True) try: @@ -518,7 +545,7 @@ def modify(connect_spec, dn, directives): def change(connect_spec, dn, before, after): - '''Modify an entry in an LDAP database. + """Modify an entry in an LDAP database. This does the same thing as :py:func:`modify`, but with a simpler interface. Instead of taking a list of directives, it takes a @@ -563,18 +590,26 @@ def change(connect_spec, dn, before, after): }" dn='cn=admin,dc=example,dc=com' before="{'example_value': 'before_val'}" after="{'example_value': 'after_val'}" - ''' + """ l = connect(connect_spec) # convert the "iterable of values" to lists in case that's what # modifyModlist() expects (also to ensure that the caller's dicts # are not modified) - before = dict(((attr, salt.utils.data.encode(list(vals))) - for attr, vals in six.iteritems(before))) - after = dict(((attr, salt.utils.data.encode(list(vals))) - for attr, vals in six.iteritems(after))) + before = dict( + ( + (attr, salt.utils.data.encode(list(vals))) + for attr, vals in six.iteritems(before) + ) + ) + after = dict( + ( + (attr, salt.utils.data.encode(list(vals))) + for attr, vals in six.iteritems(after) + ) + ) - if 'unicodePwd' in after: - after['unicodePwd'] = [_format_unicode_password(x) for x in after['unicodePwd']] + if "unicodePwd" in after: + after["unicodePwd"] = [_format_unicode_password(x) for x in after["unicodePwd"]] modlist = ldap.modlist.modifyModlist(before, after) diff --git a/salt/modules/ldapmod.py b/salt/modules/ldapmod.py index 829276a07b3..0da327dbcaf 100644 --- a/salt/modules/ldapmod.py +++ b/salt/modules/ldapmod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Salt interface to LDAP commands :depends: - ldap Python module @@ -38,22 +38,24 @@ Salt interface to LDAP commands dangerous situation of multiple minions sending identical update commands to the same LDAP server. It's easy enough to override this behavior, but badness may ensue - you have been warned. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time # Import Salt libs import salt.utils.data -from salt.ext import six from salt.exceptions import CommandExecutionError +from salt.ext import six # Import third party libs try: import ldap import ldap.modlist # pylint: disable=no-name-in-module + HAS_LDAP = True except ImportError: HAS_LDAP = False @@ -61,52 +63,64 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'ldap' +__virtualname__ = "ldap" def __virtual__(): - ''' + """ Only load this module if the ldap config is set - ''' + """ # These config items must be set in the minion config if HAS_LDAP: return __virtualname__ - return (False, 'The ldapmod execution module cannot be loaded: ' - 'ldap config not present.') + return ( + False, + "The ldapmod execution module cannot be loaded: ldap config not present.", + ) def _config(name, key=None, **kwargs): - ''' + """ Return a value for 'name' from command line args then config file options. Specify 'key' if the config file option is not the same as 'name'. - ''' + """ if key is None: key = name if name in kwargs: value = kwargs[name] else: - value = __salt__['config.option']('ldap.{0}'.format(key)) + value = __salt__["config.option"]("ldap.{0}".format(key)) return salt.utils.data.decode(value, to_str=True) def _connect(**kwargs): - ''' + """ Instantiate LDAP Connection class and return an LDAP connection object - ''' + """ connargs = {} - for name in ['uri', 'server', 'port', 'tls', 'no_verify', 'binddn', - 'bindpw', 'anonymous']: + for name in [ + "uri", + "server", + "port", + "tls", + "no_verify", + "binddn", + "bindpw", + "anonymous", + ]: connargs[name] = _config(name, **kwargs) return _LDAPConnection(**connargs).ldap -def search(filter, # pylint: disable=C0103 - dn=None, # pylint: disable=C0103 - scope=None, - attrs=None, - **kwargs): - ''' +def search( + filter, # pylint: disable=C0103 + dn=None, # pylint: disable=C0103 + scope=None, + attrs=None, + **kwargs +): + """ Run an arbitrary LDAP query and return the results. CLI Example: @@ -133,46 +147,48 @@ def search(filter, # pylint: disable=C0103 salt 'ldaphost' ldap.search filter=cn=myhost dn=ou=hosts,o=acme,c=gb scope=1 attrs='' server='localhost' port='7393' tls=True bindpw='ssh' - ''' + """ if not dn: - dn = _config('dn', 'basedn') # pylint: disable=C0103 + dn = _config("dn", "basedn") # pylint: disable=C0103 if not scope: - scope = _config('scope') - if attrs == '': # Allow command line 'return all' attr override + scope = _config("scope") + if attrs == "": # Allow command line 'return all' attr override attrs = None elif attrs is None: - attrs = _config('attrs') + attrs = _config("attrs") _ldap = _connect(**kwargs) start = time.time() log.debug( - 'Running LDAP search with filter:%s, dn:%s, scope:%s, ' - 'attrs:%s', filter, dn, scope, attrs + "Running LDAP search with filter:%s, dn:%s, scope:%s, " "attrs:%s", + filter, + dn, + scope, + attrs, ) results = _ldap.search_s(dn, int(scope), filter, attrs) - elapsed = (time.time() - start) + elapsed = time.time() - start if elapsed < 0.200: - elapsed_h = six.text_type(round(elapsed * 1000, 1)) + 'ms' + elapsed_h = six.text_type(round(elapsed * 1000, 1)) + "ms" else: - elapsed_h = six.text_type(round(elapsed, 2)) + 's' + elapsed_h = six.text_type(round(elapsed, 2)) + "s" ret = { - 'results': results, - 'count': len(results), - 'time': {'human': elapsed_h, 'raw': six.text_type(round(elapsed, 5))}, + "results": results, + "count": len(results), + "time": {"human": elapsed_h, "raw": six.text_type(round(elapsed, 5))}, } return ret class _LDAPConnection(object): - ''' + """ Setup an LDAP connection. - ''' + """ - def __init__(self, uri, server, port, tls, no_verify, binddn, bindpw, - anonymous): - ''' + def __init__(self, uri, server, port, tls, no_verify, binddn, bindpw, anonymous): + """ Bind to an LDAP directory using passed credentials. - ''' + """ self.uri = uri self.server = server self.port = port @@ -180,17 +196,14 @@ class _LDAPConnection(object): self.binddn = binddn self.bindpw = bindpw - if self.uri == '': - self.uri = 'ldap://{0}:{1}'.format(self.server, self.port) + if self.uri == "": + self.uri = "ldap://{0}:{1}".format(self.server, self.port) try: if no_verify: - ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, - ldap.OPT_X_TLS_NEVER) + ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) - self.ldap = ldap.initialize( - '{0}'.format(self.uri) - ) + self.ldap = ldap.initialize("{0}".format(self.uri)) self.ldap.protocol_version = 3 # ldap.VERSION3 self.ldap.set_option(ldap.OPT_REFERRALS, 0) # Needed for AD @@ -201,7 +214,7 @@ class _LDAPConnection(object): self.ldap.simple_bind_s(self.binddn, self.bindpw) except Exception as ldap_error: # pylint: disable=broad-except raise CommandExecutionError( - 'Failed to bind to LDAP server {0} as {1}: {2}'.format( + "Failed to bind to LDAP server {0} as {1}: {2}".format( self.uri, self.binddn, ldap_error ) ) diff --git a/salt/modules/libcloud_compute.py b/salt/modules/libcloud_compute.py index f1191dea590..3ae048c3021 100644 --- a/salt/modules/libcloud_compute.py +++ b/salt/modules/libcloud_compute.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Apache Libcloud Compute Management ================================== @@ -26,11 +26,11 @@ Clouds include Amazon EC2, Azure, Google GCE, VMware, OpenStack Nova secret: mysecret :depends: apache-libcloud -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -39,34 +39,38 @@ import os.path # Import salt libs import salt.utils.args import salt.utils.compat -from salt.utils.versions import LooseVersion as _LooseVersion from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion log = logging.getLogger(__name__) # Import third party libs -REQUIRED_LIBCLOUD_VERSION = '2.0.0' +REQUIRED_LIBCLOUD_VERSION = "2.0.0" try: - #pylint: disable=unused-import + # pylint: disable=unused-import import libcloud from libcloud.compute.providers import get_driver from libcloud.compute.base import Node - #pylint: enable=unused-import - if hasattr(libcloud, '__version__') and _LooseVersion(libcloud.__version__) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): + + # pylint: enable=unused-import + if hasattr(libcloud, "__version__") and _LooseVersion( + libcloud.__version__ + ) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): raise ImportError() - logging.getLogger('libcloud').setLevel(logging.CRITICAL) + logging.getLogger("libcloud").setLevel(logging.CRITICAL) HAS_LIBCLOUD = True except ImportError: HAS_LIBCLOUD = False def __virtual__(): - ''' + """ Only load if libcloud libraries exist. - ''' + """ if not HAS_LIBCLOUD: - msg = ('A apache-libcloud library with version at least {0} was not ' - 'found').format(REQUIRED_LIBCLOUD_VERSION) + msg = ( + "A apache-libcloud library with version at least {0} was not " "found" + ).format(REQUIRED_LIBCLOUD_VERSION) return (False, msg) return True @@ -76,22 +80,22 @@ def __init__(opts): def _get_driver(profile): - config = __salt__['config.option']('libcloud_compute')[profile] - cls = get_driver(config['driver']) + config = __salt__["config.option"]("libcloud_compute")[profile] + cls = get_driver(config["driver"]) args = config.copy() - del args['driver'] - args['key'] = config.get('key') - args['secret'] = config.get('secret', None) - if args['secret'] is None: - del args['secret'] - args['secure'] = config.get('secure', True) - args['host'] = config.get('host', None) - args['port'] = config.get('port', None) + del args["driver"] + args["key"] = config.get("key") + args["secret"] = config.get("secret", None) + if args["secret"] is None: + del args["secret"] + args["secure"] = config.get("secure", True) + args["host"] = config.get("host", None) + args["port"] = config.get("port", None) return cls(**args) def list_nodes(profile, **libcloud_kwargs): - ''' + """ Return a list of nodes :param profile: The profile key @@ -105,7 +109,7 @@ def list_nodes(profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.list_nodes profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) nodes = conn.list_nodes(**libcloud_kwargs) @@ -116,7 +120,7 @@ def list_nodes(profile, **libcloud_kwargs): def list_sizes(profile, location_id=None, **libcloud_kwargs): - ''' + """ Return a list of node sizes :param profile: The profile key @@ -134,7 +138,7 @@ def list_sizes(profile, location_id=None, **libcloud_kwargs): salt myminion libcloud_compute.list_sizes profile1 salt myminion libcloud_compute.list_sizes profile1 us-east1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) if location_id is not None: @@ -153,7 +157,7 @@ def list_sizes(profile, location_id=None, **libcloud_kwargs): def list_locations(profile, **libcloud_kwargs): - ''' + """ Return a list of locations for this cloud :param profile: The profile key @@ -167,7 +171,7 @@ def list_locations(profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.list_locations profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) locations = conn.list_locations(**libcloud_kwargs) @@ -179,7 +183,7 @@ def list_locations(profile, **libcloud_kwargs): def reboot_node(node_id, profile, **libcloud_kwargs): - ''' + """ Reboot a node in the cloud :param node_id: Unique ID of the node to reboot @@ -196,14 +200,14 @@ def reboot_node(node_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.reboot_node as-2346 profile1 - ''' + """ conn = _get_driver(profile=profile) node = _get_by_id(conn.list_nodes(**libcloud_kwargs), node_id) return conn.reboot_node(node, **libcloud_kwargs) def destroy_node(node_id, profile, **libcloud_kwargs): - ''' + """ Destroy a node in the cloud :param node_id: Unique ID of the node to destroy @@ -220,14 +224,14 @@ def destroy_node(node_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.destry_node as-2346 profile1 - ''' + """ conn = _get_driver(profile=profile) node = _get_by_id(conn.list_nodes(**libcloud_kwargs), node_id) return conn.destroy_node(node, **libcloud_kwargs) def list_volumes(profile, **libcloud_kwargs): - ''' + """ Return a list of storage volumes for this cloud :param profile: The profile key @@ -241,7 +245,7 @@ def list_volumes(profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.list_volumes profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) volumes = conn.list_volumes(**libcloud_kwargs) @@ -253,7 +257,7 @@ def list_volumes(profile, **libcloud_kwargs): def list_volume_snapshots(volume_id, profile, **libcloud_kwargs): - ''' + """ Return a list of storage volumes snapshots for this cloud :param volume_id: The volume identifier @@ -270,7 +274,7 @@ def list_volume_snapshots(volume_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.list_volume_snapshots vol1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) volume = _get_by_id(conn.list_volumes(), volume_id) @@ -283,7 +287,7 @@ def list_volume_snapshots(volume_id, profile, **libcloud_kwargs): def create_volume(size, name, profile, location_id=None, **libcloud_kwargs): - ''' + """ Create a storage volume :param size: Size of volume in gigabytes (required) @@ -308,7 +312,7 @@ def create_volume(size, name, profile, location_id=None, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.create_volume 1000 vol1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) if location_id is not None: @@ -322,7 +326,7 @@ def create_volume(size, name, profile, location_id=None, **libcloud_kwargs): def create_volume_snapshot(volume_id, profile, name=None, **libcloud_kwargs): - ''' + """ Create a storage volume snapshot :param volume_id: Volume ID from which to create the new @@ -343,7 +347,7 @@ def create_volume_snapshot(volume_id, profile, name=None, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.create_volume_snapshot vol1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) volume = _get_by_id(conn.list_volumes(), volume_id) @@ -353,7 +357,7 @@ def create_volume_snapshot(volume_id, profile, name=None, **libcloud_kwargs): def attach_volume(node_id, volume_id, profile, device=None, **libcloud_kwargs): - ''' + """ Attaches volume to node. :param node_id: Node ID to target @@ -376,7 +380,7 @@ def attach_volume(node_id, volume_id, profile, device=None, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.detach_volume vol1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) volume = _get_by_id(conn.list_volumes(), volume_id) @@ -385,7 +389,7 @@ def attach_volume(node_id, volume_id, profile, device=None, **libcloud_kwargs): def detach_volume(volume_id, profile, **libcloud_kwargs): - ''' + """ Detaches a volume from a node. :param volume_id: Volume ID from which to detach @@ -402,7 +406,7 @@ def detach_volume(volume_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.detach_volume vol1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) volume = _get_by_id(conn.list_volumes(), volume_id) @@ -410,7 +414,7 @@ def detach_volume(volume_id, profile, **libcloud_kwargs): def destroy_volume(volume_id, profile, **libcloud_kwargs): - ''' + """ Destroy a volume. :param volume_id: Volume ID from which to destroy @@ -427,7 +431,7 @@ def destroy_volume(volume_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.destroy_volume vol1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) volume = _get_by_id(conn.list_volumes(), volume_id) @@ -435,7 +439,7 @@ def destroy_volume(volume_id, profile, **libcloud_kwargs): def destroy_volume_snapshot(volume_id, snapshot_id, profile, **libcloud_kwargs): - ''' + """ Destroy a volume snapshot. :param volume_id: Volume ID from which the snapshot belongs @@ -455,7 +459,7 @@ def destroy_volume_snapshot(volume_id, snapshot_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.destroy_volume_snapshot snap1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) volume = _get_by_id(conn.list_volumes(), volume_id) @@ -464,7 +468,7 @@ def destroy_volume_snapshot(volume_id, snapshot_id, profile, **libcloud_kwargs): def list_images(profile, location_id=None, **libcloud_kwargs): - ''' + """ Return a list of images for this cloud :param profile: The profile key @@ -481,7 +485,7 @@ def list_images(profile, location_id=None, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.list_images profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) if location_id is not None: @@ -497,7 +501,7 @@ def list_images(profile, location_id=None, **libcloud_kwargs): def create_image(node_id, name, profile, description=None, **libcloud_kwargs): - ''' + """ Create an image from a node :param node_id: Node to run the task on. @@ -521,15 +525,17 @@ def create_image(node_id, name, profile, description=None, **libcloud_kwargs): salt myminion libcloud_compute.create_image server1 my_image profile1 salt myminion libcloud_compute.create_image server1 my_image profile1 description='test image' - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) node = _get_by_id(conn.list_nodes(), node_id) - return _simple_image(conn.create_image(node, name, description=description, **libcloud_kwargs)) + return _simple_image( + conn.create_image(node, name, description=description, **libcloud_kwargs) + ) def delete_image(image_id, profile, **libcloud_kwargs): - ''' + """ Delete an image of a node :param image_id: Image to delete @@ -546,7 +552,7 @@ def delete_image(image_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.delete_image image1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) image = _get_by_id(conn.list_images(), image_id) @@ -554,7 +560,7 @@ def delete_image(image_id, profile, **libcloud_kwargs): def get_image(image_id, profile, **libcloud_kwargs): - ''' + """ Get an image of a node :param image_id: Image to fetch @@ -571,15 +577,17 @@ def get_image(image_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.get_image image1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) image = conn.get_image(image_id, **libcloud_kwargs) return _simple_image(image) -def copy_image(source_region, image_id, name, profile, description=None, **libcloud_kwargs): - ''' +def copy_image( + source_region, image_id, name, profile, description=None, **libcloud_kwargs +): + """ Copies an image from a source region to the current region. :param source_region: Region to copy the node from. @@ -605,17 +613,18 @@ def copy_image(source_region, image_id, name, profile, description=None, **libcl .. code-block:: bash salt myminion libcloud_compute.copy_image us-east1 image1 'new image' profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) image = conn.get_image(image_id, **libcloud_kwargs) - new_image = conn.copy_image(source_region, image, name, - description=description, **libcloud_kwargs) + new_image = conn.copy_image( + source_region, image, name, description=description, **libcloud_kwargs + ) return _simple_image(new_image) def list_key_pairs(profile, **libcloud_kwargs): - ''' + """ List all the available key pair objects. :param profile: The profile key @@ -629,7 +638,7 @@ def list_key_pairs(profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.list_key_pairs profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) keys = conn.list_key_pairs(**libcloud_kwargs) @@ -641,7 +650,7 @@ def list_key_pairs(profile, **libcloud_kwargs): def get_key_pair(name, profile, **libcloud_kwargs): - ''' + """ Get a single key pair by name :param name: Name of the key pair to retrieve. @@ -658,14 +667,14 @@ def get_key_pair(name, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.get_key_pair pair1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) return _simple_key_pair(conn.get_key_pair(name, **libcloud_kwargs)) def create_key_pair(name, profile, **libcloud_kwargs): - ''' + """ Create a single key pair by name :param name: Name of the key pair to create. @@ -682,14 +691,14 @@ def create_key_pair(name, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.create_key_pair pair1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) return _simple_key_pair(conn.create_key_pair(name, **libcloud_kwargs)) def import_key_pair(name, key, profile, key_type=None, **libcloud_kwargs): - ''' + """ Import a new public key from string or a file path :param name: Key pair name. @@ -714,21 +723,21 @@ def import_key_pair(name, key, profile, key_type=None, **libcloud_kwargs): salt myminion libcloud_compute.import_key_pair pair1 key_value_data123 profile1 salt myminion libcloud_compute.import_key_pair pair1 /path/to/key profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) - if os.path.exists(key) or key_type == 'FILE': - return _simple_key_pair(conn.import_key_pair_from_file(name, - key, - **libcloud_kwargs)) + if os.path.exists(key) or key_type == "FILE": + return _simple_key_pair( + conn.import_key_pair_from_file(name, key, **libcloud_kwargs) + ) else: - return _simple_key_pair(conn.import_key_pair_from_string(name, - key, - **libcloud_kwargs)) + return _simple_key_pair( + conn.import_key_pair_from_string(name, key, **libcloud_kwargs) + ) def delete_key_pair(name, profile, **libcloud_kwargs): - ''' + """ Delete a key pair :param name: Key pair name. @@ -745,7 +754,7 @@ def delete_key_pair(name, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.delete_key_pair pair1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) key = conn.get_key_pair(name) @@ -753,7 +762,7 @@ def delete_key_pair(name, profile, **libcloud_kwargs): def extra(method, profile, **libcloud_kwargs): - ''' + """ Call an extended method on the driver :param method: Driver's method name @@ -770,7 +779,7 @@ def extra(method, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_compute.extra ex_get_permissions google container_name=my_container object_name=me.jpg --out=yaml - ''' + """ libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) conn = _get_driver(profile=profile) connection_method = getattr(conn, method) @@ -778,83 +787,79 @@ def extra(method, profile, **libcloud_kwargs): def _get_by_id(collection, id): - ''' + """ Get item from a list by the id field - ''' + """ matches = [item for item in collection if item.id == id] if len(matches) == 0: - raise ValueError('Could not find a matching item') + raise ValueError("Could not find a matching item") elif len(matches) > 1: - raise ValueError('The id matched {0} items, not 1'.format(len(matches))) + raise ValueError("The id matched {0} items, not 1".format(len(matches))) return matches[0] def _simple_volume(volume): return { - 'id': volume.id, - 'name': volume.name, - 'size': volume.size, - 'state': volume.state, - 'extra': volume.extra + "id": volume.id, + "name": volume.name, + "size": volume.size, + "state": volume.state, + "extra": volume.extra, } def _simple_location(location): - return { - 'id': location.id, - 'name': location.name, - 'country': location.country - } + return {"id": location.id, "name": location.name, "country": location.country} def _simple_size(size): return { - 'id': size.id, - 'name': size.name, - 'ram': size.ram, - 'disk': size.disk, - 'bandwidth': size.bandwidth, - 'price': size.price, - 'extra': size.extra + "id": size.id, + "name": size.name, + "ram": size.ram, + "disk": size.disk, + "bandwidth": size.bandwidth, + "price": size.price, + "extra": size.extra, } def _simple_node(node): return { - 'id': node.id, - 'name': node.name, - 'state': six.text_type(node.state), - 'public_ips': node.public_ips, - 'private_ips': node.private_ips, - 'size': _simple_size(node.size) if node.size else {}, - 'extra': node.extra + "id": node.id, + "name": node.name, + "state": six.text_type(node.state), + "public_ips": node.public_ips, + "private_ips": node.private_ips, + "size": _simple_size(node.size) if node.size else {}, + "extra": node.extra, } def _simple_volume_snapshot(snapshot): return { - 'id': snapshot.id, - 'name': snapshot.name if hasattr(snapshot, 'name') else snapshot.id, - 'size': snapshot.size, - 'extra': snapshot.extra, - 'created': snapshot.created, - 'state': snapshot.state + "id": snapshot.id, + "name": snapshot.name if hasattr(snapshot, "name") else snapshot.id, + "size": snapshot.size, + "extra": snapshot.extra, + "created": snapshot.created, + "state": snapshot.state, } def _simple_image(image): return { - 'id': image.id, - 'name': image.name, - 'extra': image.extra, + "id": image.id, + "name": image.name, + "extra": image.extra, } def _simple_key_pair(key): return { - 'name': key.name, - 'fingerprint': key.fingerprint, - 'public_key': key.public_key, - 'private_key': key.private_key, - 'extra': key.extra + "name": key.name, + "fingerprint": key.fingerprint, + "public_key": key.public_key, + "private_key": key.private_key, + "extra": key.extra, } diff --git a/salt/modules/libcloud_dns.py b/salt/modules/libcloud_dns.py index b3ed35b9390..04d2aad9012 100644 --- a/salt/modules/libcloud_dns.py +++ b/salt/modules/libcloud_dns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Apache Libcloud DNS Management ============================== @@ -24,11 +24,11 @@ Connection module for Apache Libcloud DNS management shopper_id: 12345 :depends: apache-libcloud -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -40,28 +40,32 @@ from salt.utils.versions import LooseVersion as _LooseVersion log = logging.getLogger(__name__) # Import third party libs -REQUIRED_LIBCLOUD_VERSION = '2.0.0' +REQUIRED_LIBCLOUD_VERSION = "2.0.0" try: - #pylint: disable=unused-import + # pylint: disable=unused-import import libcloud from libcloud.dns.providers import get_driver from libcloud.dns.types import RecordType - #pylint: enable=unused-import - if hasattr(libcloud, '__version__') and _LooseVersion(libcloud.__version__) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): + + # pylint: enable=unused-import + if hasattr(libcloud, "__version__") and _LooseVersion( + libcloud.__version__ + ) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): raise ImportError() - logging.getLogger('libcloud').setLevel(logging.CRITICAL) + logging.getLogger("libcloud").setLevel(logging.CRITICAL) HAS_LIBCLOUD = True except ImportError: HAS_LIBCLOUD = False def __virtual__(): - ''' + """ Only load if libcloud libraries exist. - ''' + """ if not HAS_LIBCLOUD: - msg = ('A apache-libcloud library with version at least {0} was not ' - 'found').format(REQUIRED_LIBCLOUD_VERSION) + msg = ( + "A apache-libcloud library with version at least {0} was not " "found" + ).format(REQUIRED_LIBCLOUD_VERSION) return (False, msg) return True @@ -71,20 +75,20 @@ def __init__(opts): def _get_driver(profile): - config = __salt__['config.option']('libcloud_dns')[profile] - cls = get_driver(config['driver']) + config = __salt__["config.option"]("libcloud_dns")[profile] + cls = get_driver(config["driver"]) args = config.copy() - del args['driver'] - args['key'] = config.get('key') - args['secret'] = config.get('secret', None) - args['secure'] = config.get('secure', True) - args['host'] = config.get('host', None) - args['port'] = config.get('port', None) + del args["driver"] + args["key"] = config.get("key") + args["secret"] = config.get("secret", None) + args["secure"] = config.get("secure", True) + args["host"] = config.get("host", None) + args["port"] = config.get("port", None) return cls(**args) def list_record_types(profile): - ''' + """ List available record types for the given profile, e.g. A, AAAA :param profile: The profile key @@ -95,13 +99,13 @@ def list_record_types(profile): .. code-block:: bash salt myminion libcloud_dns.list_record_types profile1 - ''' + """ conn = _get_driver(profile=profile) return conn.list_record_types() def list_zones(profile): - ''' + """ List zones for the given profile :param profile: The profile key @@ -112,13 +116,13 @@ def list_zones(profile): .. code-block:: bash salt myminion libcloud_dns.list_zones profile1 - ''' + """ conn = _get_driver(profile=profile) return [_simple_zone(zone) for zone in conn.list_zones()] def list_records(zone_id, profile, type=None): - ''' + """ List records for the given zone_id on the given profile :param zone_id: Zone to export. @@ -135,17 +139,21 @@ def list_records(zone_id, profile, type=None): .. code-block:: bash salt myminion libcloud_dns.list_records google.com profile1 - ''' + """ conn = _get_driver(profile=profile) zone = conn.get_zone(zone_id) if type is not None: - return [_simple_record(record) for record in conn.list_records(zone) if record.type == type] + return [ + _simple_record(record) + for record in conn.list_records(zone) + if record.type == type + ] else: return [_simple_record(record) for record in conn.list_records(zone)] def get_zone(zone_id, profile): - ''' + """ Get zone information for the given zone_id on the given profile :param zone_id: Zone to export. @@ -159,13 +167,13 @@ def get_zone(zone_id, profile): .. code-block:: bash salt myminion libcloud_dns.get_zone google.com profile1 - ''' + """ conn = _get_driver(profile=profile) return _simple_zone(conn.get_zone(zone_id)) def get_record(zone_id, record_id, profile): - ''' + """ Get record information for the given zone_id on the given profile :param zone_id: Zone to export. @@ -182,13 +190,13 @@ def get_record(zone_id, record_id, profile): .. code-block:: bash salt myminion libcloud_dns.get_record google.com www profile1 - ''' + """ conn = _get_driver(profile=profile) return _simple_record(conn.get_record(zone_id, record_id)) -def create_zone(domain, profile, type='master', ttl=None): - ''' +def create_zone(domain, profile, type="master", ttl=None): + """ Create a new zone. :param domain: Zone domain name (e.g. example.com) @@ -208,14 +216,14 @@ def create_zone(domain, profile, type='master', ttl=None): .. code-block:: bash salt myminion libcloud_dns.create_zone google.com profile1 - ''' + """ conn = _get_driver(profile=profile) zone = conn.create_record(domain, type=type, ttl=ttl) return _simple_zone(zone) -def update_zone(zone_id, domain, profile, type='master', ttl=None): - ''' +def update_zone(zone_id, domain, profile, type="master", ttl=None): + """ Update an existing zone. :param zone_id: Zone ID to update. @@ -238,14 +246,14 @@ def update_zone(zone_id, domain, profile, type='master', ttl=None): .. code-block:: bash salt myminion libcloud_dns.update_zone google.com google.com profile1 type=slave - ''' + """ conn = _get_driver(profile=profile) zone = conn.get_zone(zone_id) return _simple_zone(conn.update_zone(zone=zone, domain=domain, type=type, ttl=ttl)) def create_record(name, zone_id, type, data, profile): - ''' + """ Create a new record. :param name: Record name without the domain name (e.g. www). @@ -271,7 +279,7 @@ def create_record(name, zone_id, type, data, profile): .. code-block:: bash salt myminion libcloud_dns.create_record www google.com A 12.32.12.2 profile1 - ''' + """ conn = _get_driver(profile=profile) record_type = _string_to_record_type(type) zone = conn.get_zone(zone_id) @@ -279,7 +287,7 @@ def create_record(name, zone_id, type, data, profile): def delete_zone(zone_id, profile): - ''' + """ Delete a zone. :param zone_id: Zone to delete. @@ -295,14 +303,14 @@ def delete_zone(zone_id, profile): .. code-block:: bash salt myminion libcloud_dns.delete_zone google.com profile1 - ''' + """ conn = _get_driver(profile=profile) zone = conn.get_zone(zone_id=zone_id) return conn.delete_zone(zone) def delete_record(zone_id, record_id, profile): - ''' + """ Delete a record. :param zone_id: Zone to delete. @@ -321,14 +329,14 @@ def delete_record(zone_id, record_id, profile): .. code-block:: bash salt myminion libcloud_dns.delete_record google.com www profile1 - ''' + """ conn = _get_driver(profile=profile) record = conn.get_record(zone_id=zone_id, record_id=record_id) return conn.delete_record(record) def get_bind_data(zone_id, profile): - ''' + """ Export Zone to the BIND compatible format. :param zone_id: Zone to export. @@ -345,14 +353,14 @@ def get_bind_data(zone_id, profile): .. code-block:: bash salt myminion libcloud_dns.get_bind_data google.com profile1 - ''' + """ conn = _get_driver(profile=profile) zone = conn.get_zone(zone_id) return conn.export_zone_to_bind_format(zone) def extra(method, profile, **libcloud_kwargs): - ''' + """ Call an extended method on the driver :param method: Driver's method name @@ -369,7 +377,7 @@ def extra(method, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_dns.extra ex_get_permissions google container_name=my_container object_name=me.jpg --out=yaml - ''' + """ _sanitize_kwargs(libcloud_kwargs) conn = _get_driver(profile=profile) connection_method = getattr(conn, method) @@ -377,7 +385,7 @@ def extra(method, profile, **libcloud_kwargs): def _string_to_record_type(string): - ''' + """ Return a string representation of a DNS record type to a libcloud RecordType ENUM. @@ -385,7 +393,7 @@ def _string_to_record_type(string): :type string: ``str`` :rtype: :class:`RecordType` - ''' + """ string = string.upper() record_type = getattr(RecordType, string) return record_type @@ -393,21 +401,21 @@ def _string_to_record_type(string): def _simple_zone(zone): return { - 'id': zone.id, - 'domain': zone.domain, - 'type': zone.type, - 'ttl': zone.ttl, - 'extra': zone.extra + "id": zone.id, + "domain": zone.domain, + "type": zone.type, + "ttl": zone.ttl, + "extra": zone.extra, } def _simple_record(record): return { - 'id': record.id, - 'name': record.name, - 'type': record.type, - 'data': record.data, - 'zone': _simple_zone(record.zone), - 'ttl': record.ttl, - 'extra': record.extra + "id": record.id, + "name": record.name, + "type": record.type, + "data": record.data, + "zone": _simple_zone(record.zone), + "ttl": record.ttl, + "extra": record.extra, } diff --git a/salt/modules/libcloud_loadbalancer.py b/salt/modules/libcloud_loadbalancer.py index 65849be4947..3b6cadc92ca 100644 --- a/salt/modules/libcloud_loadbalancer.py +++ b/salt/modules/libcloud_loadbalancer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Apache Libcloud Load Balancer Management ======================================== @@ -26,11 +26,11 @@ Clouds include Amazon ELB, ALB, Google, Aliyun, CloudStack, Softlayer secret: mysecret :depends: apache-libcloud -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -44,28 +44,32 @@ from salt.utils.versions import LooseVersion as _LooseVersion log = logging.getLogger(__name__) # Import third party libs -REQUIRED_LIBCLOUD_VERSION = '1.5.0' +REQUIRED_LIBCLOUD_VERSION = "1.5.0" try: - #pylint: disable=unused-import + # pylint: disable=unused-import import libcloud from libcloud.loadbalancer.providers import get_driver from libcloud.loadbalancer.base import Member, Algorithm - #pylint: enable=unused-import - if hasattr(libcloud, '__version__') and _LooseVersion(libcloud.__version__) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): + + # pylint: enable=unused-import + if hasattr(libcloud, "__version__") and _LooseVersion( + libcloud.__version__ + ) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): raise ImportError() - logging.getLogger('libcloud').setLevel(logging.CRITICAL) + logging.getLogger("libcloud").setLevel(logging.CRITICAL) HAS_LIBCLOUD = True except ImportError: HAS_LIBCLOUD = False def __virtual__(): - ''' + """ Only load if libcloud libraries exist. - ''' + """ if not HAS_LIBCLOUD: - msg = ('A apache-libcloud library with version at least {0} was not ' - 'found').format(REQUIRED_LIBCLOUD_VERSION) + msg = ( + "A apache-libcloud library with version at least {0} was not " "found" + ).format(REQUIRED_LIBCLOUD_VERSION) return (False, msg) return True @@ -76,33 +80,33 @@ def __init__(opts): def _algorithm_maps(): return { - 'RANDOM': Algorithm.RANDOM, - 'ROUND_ROBIN': Algorithm.ROUND_ROBIN, - 'LEAST_CONNECTIONS': Algorithm.LEAST_CONNECTIONS, - 'WEIGHTED_ROUND_ROBIN': Algorithm.WEIGHTED_ROUND_ROBIN, - 'WEIGHTED_LEAST_CONNECTIONS': Algorithm.WEIGHTED_LEAST_CONNECTIONS, - 'SHORTEST_RESPONSE': Algorithm.SHORTEST_RESPONSE, - 'PERSISTENT_IP': Algorithm.PERSISTENT_IP + "RANDOM": Algorithm.RANDOM, + "ROUND_ROBIN": Algorithm.ROUND_ROBIN, + "LEAST_CONNECTIONS": Algorithm.LEAST_CONNECTIONS, + "WEIGHTED_ROUND_ROBIN": Algorithm.WEIGHTED_ROUND_ROBIN, + "WEIGHTED_LEAST_CONNECTIONS": Algorithm.WEIGHTED_LEAST_CONNECTIONS, + "SHORTEST_RESPONSE": Algorithm.SHORTEST_RESPONSE, + "PERSISTENT_IP": Algorithm.PERSISTENT_IP, } def _get_driver(profile): - config = __salt__['config.option']('libcloud_loadbalancer')[profile] - cls = get_driver(config['driver']) + config = __salt__["config.option"]("libcloud_loadbalancer")[profile] + cls = get_driver(config["driver"]) args = config.copy() - del args['driver'] - args['key'] = config.get('key') - args['secret'] = config.get('secret', None) - if args['secret'] is None: - del args['secret'] - args['secure'] = config.get('secure', True) - args['host'] = config.get('host', None) - args['port'] = config.get('port', None) + del args["driver"] + args["key"] = config.get("key") + args["secret"] = config.get("secret", None) + if args["secret"] is None: + del args["secret"] + args["secure"] = config.get("secure", True) + args["host"] = config.get("host", None) + args["port"] = config.get("port", None) return cls(**args) def list_balancers(profile, **libcloud_kwargs): - ''' + """ Return a list of load balancers. :param profile: The profile key @@ -116,7 +120,7 @@ def list_balancers(profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.list_balancers profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) balancers = conn.list_balancers(**libcloud_kwargs) @@ -127,7 +131,7 @@ def list_balancers(profile, **libcloud_kwargs): def list_protocols(profile, **libcloud_kwargs): - ''' + """ Return a list of supported protocols. :param profile: The profile key @@ -144,14 +148,16 @@ def list_protocols(profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.list_protocols profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) return conn.list_protocols(**libcloud_kwargs) -def create_balancer(name, port, protocol, profile, algorithm=None, members=None, **libcloud_kwargs): - ''' +def create_balancer( + name, port, protocol, profile, algorithm=None, members=None, **libcloud_kwargs +): + """ Create a new load balancer instance :param name: Name of the new load balancer (required) @@ -180,7 +186,7 @@ def create_balancer(name, port, protocol, profile, algorithm=None, members=None, .. code-block:: bash salt myminion libcloud_storage.create_balancer my_balancer 80 http profile1 - ''' + """ if algorithm is None: algorithm = Algorithm.ROUND_ROBIN else: @@ -190,18 +196,20 @@ def create_balancer(name, port, protocol, profile, algorithm=None, members=None, if members is not None: if isinstance(members, list): for m in members: - starting_members.append(Member(id=None, ip=m['ip'], port=m['port'])) + starting_members.append(Member(id=None, ip=m["ip"], port=m["port"])) else: raise ValueError("members must be of type list") libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) conn = _get_driver(profile=profile) - balancer = conn.create_balancer(name, port, protocol, algorithm, starting_members, **libcloud_kwargs) + balancer = conn.create_balancer( + name, port, protocol, algorithm, starting_members, **libcloud_kwargs + ) return _simple_balancer(balancer) def destroy_balancer(balancer_id, profile, **libcloud_kwargs): - ''' + """ Destroy a load balancer :param balancer_id: LoadBalancer ID which should be used @@ -221,7 +229,7 @@ def destroy_balancer(balancer_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.destroy_balancer balancer_1 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) balancer = conn.get_balancer(balancer_id) @@ -229,7 +237,7 @@ def destroy_balancer(balancer_id, profile, **libcloud_kwargs): def get_balancer_by_name(name, profile, **libcloud_kwargs): - ''' + """ Get the details for a load balancer by name :param name: Name of a load balancer you want to fetch @@ -248,7 +256,7 @@ def get_balancer_by_name(name, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.get_balancer_by_name my_balancer profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) balancers = conn.list_balancers(**libcloud_kwargs) @@ -262,7 +270,7 @@ def get_balancer_by_name(name, profile, **libcloud_kwargs): def get_balancer(balancer_id, profile, **libcloud_kwargs): - ''' + """ Get the details for a load balancer by ID :param balancer_id: id of a load balancer you want to fetch @@ -281,7 +289,7 @@ def get_balancer(balancer_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.get_balancer balancer123 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) balancer = conn.get_balancer(balancer_id, **libcloud_kwargs) @@ -289,7 +297,7 @@ def get_balancer(balancer_id, profile, **libcloud_kwargs): def list_supported_algorithms(profile, **libcloud_kwargs): - ''' + """ Get the supported algorithms for a profile :param profile: The profile key @@ -305,14 +313,16 @@ def list_supported_algorithms(profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.list_supported_algorithms profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) return conn.list_supported_algorithms(**libcloud_kwargs) -def balancer_attach_member(balancer_id, ip, port, profile, extra=None, **libcloud_kwargs): - ''' +def balancer_attach_member( + balancer_id, ip, port, profile, extra=None, **libcloud_kwargs +): + """ Add a new member to the load balancer :param balancer_id: id of a load balancer you want to fetch @@ -335,7 +345,7 @@ def balancer_attach_member(balancer_id, ip, port, profile, extra=None, **libclou .. code-block:: bash salt myminion libcloud_storage.balancer_attach_member balancer123 1.2.3.4 80 profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) member = Member(id=None, ip=ip, port=port, balancer=None, extra=extra) @@ -345,7 +355,7 @@ def balancer_attach_member(balancer_id, ip, port, profile, extra=None, **libclou def balancer_detach_member(balancer_id, member_id, profile, **libcloud_kwargs): - ''' + """ Add a new member to the load balancer :param balancer_id: id of a load balancer you want to fetch @@ -368,7 +378,7 @@ def balancer_detach_member(balancer_id, member_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.balancer_detach_member balancer123 member123 profile1 - ''' + """ conn = _get_driver(profile=profile) balancer = conn.get_balancer(balancer_id) members = conn.balancer_list_members(balancer=balancer) @@ -380,11 +390,13 @@ def balancer_detach_member(balancer_id, member_id, profile, **libcloud_kwargs): else: member = match[0] libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) - return conn.balancer_detach_member(balancer=balancer, member=member, **libcloud_kwargs) + return conn.balancer_detach_member( + balancer=balancer, member=member, **libcloud_kwargs + ) def list_balancer_members(balancer_id, profile, **libcloud_kwargs): - ''' + """ List the members of a load balancer :param balancer_id: id of a load balancer you want to fetch @@ -401,7 +413,7 @@ def list_balancer_members(balancer_id, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.list_balancer_members balancer123 profile1 - ''' + """ conn = _get_driver(profile=profile) balancer = conn.get_balancer(balancer_id) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) @@ -410,7 +422,7 @@ def list_balancer_members(balancer_id, profile, **libcloud_kwargs): def extra(method, profile, **libcloud_kwargs): - ''' + """ Call an extended method on the driver :param method: Driver's method name @@ -427,7 +439,7 @@ def extra(method, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_loadbalancer.extra ex_get_permissions google container_name=my_container object_name=me.jpg --out=yaml - ''' + """ libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) conn = _get_driver(profile=profile) connection_method = getattr(conn, method) @@ -436,20 +448,20 @@ def extra(method, profile, **libcloud_kwargs): def _simple_balancer(balancer): return { - 'id': balancer.id, - 'name': balancer.name, - 'state': balancer.state, - 'ip': balancer.ip, - 'port': balancer.port, - 'extra': balancer.extra + "id": balancer.id, + "name": balancer.name, + "state": balancer.state, + "ip": balancer.ip, + "port": balancer.port, + "extra": balancer.extra, } def _simple_member(member): return { - 'id': member.id, - 'ip': member.ip, - 'port': member.port, - 'balancer': _simple_balancer(member.balancer), - 'extra': member.extra + "id": member.id, + "ip": member.ip, + "port": member.port, + "balancer": _simple_balancer(member.balancer), + "extra": member.extra, } diff --git a/salt/modules/libcloud_storage.py b/salt/modules/libcloud_storage.py index 63ff8e6136b..8392efe1eb4 100644 --- a/salt/modules/libcloud_storage.py +++ b/salt/modules/libcloud_storage.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Apache Libcloud Storage Management ================================== @@ -26,11 +26,11 @@ Clouds include Amazon S3, Google Storage, Aliyun, Azure Blobs, Ceph, OpenStack s secret: mysecret :depends: apache-libcloud -''' +""" # keep lint from choking on _get_conn and _cache_id -#pylint: disable=E0602 +# pylint: disable=E0602 -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -43,27 +43,31 @@ from salt.utils.versions import LooseVersion as _LooseVersion log = logging.getLogger(__name__) # Import third party libs -REQUIRED_LIBCLOUD_VERSION = '1.5.0' +REQUIRED_LIBCLOUD_VERSION = "1.5.0" try: - #pylint: disable=unused-import + # pylint: disable=unused-import import libcloud from libcloud.storage.providers import get_driver - #pylint: enable=unused-import - if hasattr(libcloud, '__version__') and _LooseVersion(libcloud.__version__) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): + + # pylint: enable=unused-import + if hasattr(libcloud, "__version__") and _LooseVersion( + libcloud.__version__ + ) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): raise ImportError() - logging.getLogger('libcloud').setLevel(logging.CRITICAL) + logging.getLogger("libcloud").setLevel(logging.CRITICAL) HAS_LIBCLOUD = True except ImportError: HAS_LIBCLOUD = False def __virtual__(): - ''' + """ Only load if libcloud libraries exist. - ''' + """ if not HAS_LIBCLOUD: - msg = ('A apache-libcloud library with version at least {0} was not ' - 'found').format(REQUIRED_LIBCLOUD_VERSION) + msg = ( + "A apache-libcloud library with version at least {0} was not " "found" + ).format(REQUIRED_LIBCLOUD_VERSION) return (False, msg) return True @@ -73,20 +77,20 @@ def __init__(opts): def _get_driver(profile): - config = __salt__['config.option']('libcloud_storage')[profile] - cls = get_driver(config['driver']) + config = __salt__["config.option"]("libcloud_storage")[profile] + cls = get_driver(config["driver"]) args = config.copy() - del args['driver'] - args['key'] = config.get('key') - args['secret'] = config.get('secret', None) - args['secure'] = config.get('secure', True) - args['host'] = config.get('host', None) - args['port'] = config.get('port', None) + del args["driver"] + args["key"] = config.get("key") + args["secret"] = config.get("secret", None) + args["secure"] = config.get("secure", True) + args["host"] = config.get("host", None) + args["port"] = config.get("port", None) return cls(**args) def list_containers(profile, **libcloud_kwargs): - ''' + """ Return a list of containers. :param profile: The profile key @@ -100,21 +104,18 @@ def list_containers(profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.list_containers profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) containers = conn.list_containers(**libcloud_kwargs) ret = [] for container in containers: - ret.append({ - 'name': container.name, - 'extra': container.extra - }) + ret.append({"name": container.name, "extra": container.extra}) return ret def list_container_objects(container_name, profile, **libcloud_kwargs): - ''' + """ List container objects (e.g. files) for the given container_id on the given profile :param container_name: Container name @@ -131,26 +132,28 @@ def list_container_objects(container_name, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.list_container_objects MyFolder profile1 - ''' + """ conn = _get_driver(profile=profile) container = conn.get_container(container_name) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) objects = conn.list_container_objects(container, **libcloud_kwargs) ret = [] for obj in objects: - ret.append({ - 'name': obj.name, - 'size': obj.size, - 'hash': obj.hash, - 'container': obj.container.name, - 'extra': obj.extra, - 'meta_data': obj.meta_data - }) + ret.append( + { + "name": obj.name, + "size": obj.size, + "hash": obj.hash, + "container": obj.container.name, + "extra": obj.extra, + "meta_data": obj.meta_data, + } + ) return ret def create_container(container_name, profile, **libcloud_kwargs): - ''' + """ Create a container in the cloud :param container_name: Container name @@ -167,18 +170,15 @@ def create_container(container_name, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.create_container MyFolder profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) container = conn.create_container(container_name, **libcloud_kwargs) - return { - 'name': container.name, - 'extra': container.extra - } + return {"name": container.name, "extra": container.extra} def get_container(container_name, profile, **libcloud_kwargs): - ''' + """ List container details for the given container_name on the given profile :param container_name: Container name @@ -195,18 +195,15 @@ def get_container(container_name, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.get_container MyFolder profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) container = conn.get_container(container_name, **libcloud_kwargs) - return { - 'name': container.name, - 'extra': container.extra - } + return {"name": container.name, "extra": container.extra} def get_container_object(container_name, object_name, profile, **libcloud_kwargs): - ''' + """ Get the details for a container object (file or object in the cloud) :param container_name: Container name @@ -226,22 +223,30 @@ def get_container_object(container_name, object_name, profile, **libcloud_kwargs .. code-block:: bash salt myminion libcloud_storage.get_container_object MyFolder MyFile.xyz profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) obj = conn.get_container_object(container_name, object_name, **libcloud_kwargs) return { - 'name': obj.name, - 'size': obj.size, - 'hash': obj.hash, - 'container': obj.container.name, - 'extra': obj.extra, - 'meta_data': obj.meta_data} + "name": obj.name, + "size": obj.size, + "hash": obj.hash, + "container": obj.container.name, + "extra": obj.extra, + "meta_data": obj.meta_data, + } -def download_object(container_name, object_name, destination_path, profile, - overwrite_existing=False, delete_on_failure=True, **libcloud_kwargs): - ''' +def download_object( + container_name, + object_name, + destination_path, + profile, + overwrite_existing=False, + delete_on_failure=True, + **libcloud_kwargs +): + """ Download an object to the specified destination path. :param container_name: Container name @@ -279,16 +284,26 @@ def download_object(container_name, object_name, destination_path, profile, salt myminion libcloud_storage.download_object MyFolder me.jpg /tmp/me.jpg profile1 - ''' + """ conn = _get_driver(profile=profile) obj = conn.get_object(container_name, object_name) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) - return conn.download_object(obj, destination_path, overwrite_existing, delete_on_failure, **libcloud_kwargs) + return conn.download_object( + obj, destination_path, overwrite_existing, delete_on_failure, **libcloud_kwargs + ) -def upload_object(file_path, container_name, object_name, profile, extra=None, - verify_hash=True, headers=None, **libcloud_kwargs): - ''' +def upload_object( + file_path, + container_name, + object_name, + profile, + extra=None, + verify_hash=True, + headers=None, + **libcloud_kwargs +): + """ Upload an object currently located on a disk. :param file_path: Path to the object on disk. @@ -326,16 +341,24 @@ def upload_object(file_path, container_name, object_name, profile, extra=None, salt myminion libcloud_storage.upload_object /file/to/me.jpg MyFolder me.jpg profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) container = conn.get_container(container_name) - obj = conn.upload_object(file_path, container, object_name, extra, verify_hash, headers, **libcloud_kwargs) + obj = conn.upload_object( + file_path, + container, + object_name, + extra, + verify_hash, + headers, + **libcloud_kwargs + ) return obj.name def delete_object(container_name, object_name, profile, **libcloud_kwargs): - ''' + """ Delete an object in the cloud :param container_name: Container name @@ -359,7 +382,7 @@ def delete_object(container_name, object_name, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.delete_object MyFolder me.jpg profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) obj = conn.get_object(container_name, object_name, **libcloud_kwargs) @@ -367,7 +390,7 @@ def delete_object(container_name, object_name, profile, **libcloud_kwargs): def delete_container(container_name, profile, **libcloud_kwargs): - ''' + """ Delete an object container in the cloud :param container_name: Container name @@ -388,7 +411,7 @@ def delete_container(container_name, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.delete_container MyFolder profile1 - ''' + """ conn = _get_driver(profile=profile) libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) container = conn.get_container(container_name) @@ -396,7 +419,7 @@ def delete_container(container_name, profile, **libcloud_kwargs): def extra(method, profile, **libcloud_kwargs): - ''' + """ Call an extended method on the driver :param method: Driver's method name @@ -413,7 +436,7 @@ def extra(method, profile, **libcloud_kwargs): .. code-block:: bash salt myminion libcloud_storage.extra ex_get_permissions google container_name=my_container object_name=me.jpg --out=yaml - ''' + """ libcloud_kwargs = salt.utils.args.clean_kwargs(**libcloud_kwargs) conn = _get_driver(profile=profile) connection_method = getattr(conn, method) diff --git a/salt/modules/linux_acl.py b/salt/modules/linux_acl.py index c0596b0eeb7..4c119e07cf7 100644 --- a/salt/modules/linux_acl.py +++ b/salt/modules/linux_acl.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Support for Linux File Access Control Lists The Linux ACL module requires the `getfacl` and `setfacl` binaries. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -12,20 +12,23 @@ import salt.utils.path from salt.exceptions import CommandExecutionError # Define the module's virtual name -__virtualname__ = 'acl' +__virtualname__ = "acl" def __virtual__(): - ''' + """ Only load the module if getfacl is installed - ''' - if salt.utils.path.which('getfacl'): + """ + if salt.utils.path.which("getfacl"): return __virtualname__ - return (False, 'The linux_acl execution module cannot be loaded: the getfacl binary is not in the path.') + return ( + False, + "The linux_acl execution module cannot be loaded: the getfacl binary is not in the path.", + ) def version(): - ''' + """ Return facl version from getfacl --version CLI Example: @@ -33,20 +36,22 @@ def version(): .. code-block:: bash salt '*' acl.version - ''' - cmd = 'getfacl --version' - out = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "getfacl --version" + out = __salt__["cmd.run"](cmd).splitlines() ret = out[0].split() return ret[1].strip() def _raise_on_no_files(*args): if len(args) == 0: - raise CommandExecutionError('You need to specify at least one file or directory to work with!') + raise CommandExecutionError( + "You need to specify at least one file or directory to work with!" + ) def getfacl(*args, **kwargs): - ''' + """ Return (extremely verbose) map of FACLs on specified file(s) CLI Examples: @@ -56,116 +61,116 @@ def getfacl(*args, **kwargs): salt '*' acl.getfacl /tmp/house/kitchen salt '*' acl.getfacl /tmp/house/kitchen /tmp/house/livingroom salt '*' acl.getfacl /tmp/house/kitchen /tmp/house/livingroom recursive=True - ''' - recursive = kwargs.pop('recursive', False) + """ + recursive = kwargs.pop("recursive", False) _raise_on_no_files(*args) ret = {} - cmd = 'getfacl --absolute-names' + cmd = "getfacl --absolute-names" if recursive: - cmd += ' -R' + cmd += " -R" for dentry in args: cmd += ' "{0}"'.format(dentry) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - dentry = '' + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() + dentry = "" for line in out: if not line: continue - elif line.startswith('getfacl'): + elif line.startswith("getfacl"): continue - elif line.startswith('#'): - comps = line.replace('# ', '').split(': ') - if comps[0] == 'file': + elif line.startswith("#"): + comps = line.replace("# ", "").split(": ") + if comps[0] == "file": dentry = comps[1] - ret[dentry] = {'comment': {}, - 'user': [], - 'group': []} - ret[dentry]['comment'][comps[0]] = comps[1] - if comps[0] == 'flags': + ret[dentry] = {"comment": {}, "user": [], "group": []} + ret[dentry]["comment"][comps[0]] = comps[1] + if comps[0] == "flags": flags = list(comps[1]) - if flags[0] == 's': - ret[dentry]['suid'] = True - if flags[1] == 's': - ret[dentry]['sgid'] = True - if flags[2] == 't': - ret[dentry]['sticky'] = True + if flags[0] == "s": + ret[dentry]["suid"] = True + if flags[1] == "s": + ret[dentry]["sgid"] = True + if flags[2] == "t": + ret[dentry]["sticky"] = True else: - vals = _parse_acl(acl=line, - user=ret[dentry]['comment']['owner'], - group=ret[dentry]['comment']['group']) - acl_type = vals['type'] - del vals['type'] - for entity in ('user', 'group'): + vals = _parse_acl( + acl=line, + user=ret[dentry]["comment"]["owner"], + group=ret[dentry]["comment"]["group"], + ) + acl_type = vals["type"] + del vals["type"] + for entity in ("user", "group"): if entity in vals: usergroup = vals[entity] del vals[entity] - if acl_type == 'acl': + if acl_type == "acl": ret[dentry][entity].append({usergroup: vals}) - elif acl_type == 'default': - if 'defaults' not in ret[dentry]: - ret[dentry]['defaults'] = {} - if entity not in ret[dentry]['defaults']: - ret[dentry]['defaults'][entity] = [] - ret[dentry]['defaults'][entity].append({usergroup: vals}) - for entity in ('other', 'mask'): + elif acl_type == "default": + if "defaults" not in ret[dentry]: + ret[dentry]["defaults"] = {} + if entity not in ret[dentry]["defaults"]: + ret[dentry]["defaults"][entity] = [] + ret[dentry]["defaults"][entity].append({usergroup: vals}) + for entity in ("other", "mask"): if entity in vals: del vals[entity] - if acl_type == 'acl': + if acl_type == "acl": ret[dentry][entity] = [{"": vals}] - elif acl_type == 'default': - if 'defaults' not in ret[dentry]: - ret[dentry]['defaults'] = {} - ret[dentry]['defaults'][entity] = [{"": vals}] + elif acl_type == "default": + if "defaults" not in ret[dentry]: + ret[dentry]["defaults"] = {} + ret[dentry]["defaults"][entity] = [{"": vals}] return ret def _parse_acl(acl, user, group): - ''' + """ Parse a single ACL rule - ''' - comps = acl.split(':') + """ + comps = acl.split(":") vals = {} # What type of rule is this? - vals['type'] = 'acl' - if comps[0] == 'default': - vals['type'] = 'default' + vals["type"] = "acl" + if comps[0] == "default": + vals["type"] = "default" comps.pop(0) # If a user is not specified, use the owner of the file - if comps[0] == 'user' and not comps[1]: + if comps[0] == "user" and not comps[1]: comps[1] = user - elif comps[0] == 'group' and not comps[1]: + elif comps[0] == "group" and not comps[1]: comps[1] = group vals[comps[0]] = comps[1] # Set the permissions fields octal = 0 - vals['permissions'] = {} - if 'r' in comps[-1]: + vals["permissions"] = {} + if "r" in comps[-1]: octal += 4 - vals['permissions']['read'] = True + vals["permissions"]["read"] = True else: - vals['permissions']['read'] = False - if 'w' in comps[-1]: + vals["permissions"]["read"] = False + if "w" in comps[-1]: octal += 2 - vals['permissions']['write'] = True + vals["permissions"]["write"] = True else: - vals['permissions']['write'] = False - if 'x' in comps[-1]: + vals["permissions"]["write"] = False + if "x" in comps[-1]: octal += 1 - vals['permissions']['execute'] = True + vals["permissions"]["execute"] = True else: - vals['permissions']['execute'] = False - vals['octal'] = octal + vals["permissions"]["execute"] = False + vals["octal"] = octal return vals def wipefacls(*args, **kwargs): - ''' + """ Remove all FACLs from the specified file(s) CLI Examples: @@ -175,36 +180,36 @@ def wipefacls(*args, **kwargs): salt '*' acl.wipefacls /tmp/house/kitchen salt '*' acl.wipefacls /tmp/house/kitchen /tmp/house/livingroom salt '*' acl.wipefacls /tmp/house/kitchen /tmp/house/livingroom recursive=True - ''' - recursive = kwargs.pop('recursive', False) + """ + recursive = kwargs.pop("recursive", False) _raise_on_no_files(*args) - cmd = 'setfacl -b' + cmd = "setfacl -b" if recursive: - cmd += ' -R' + cmd += " -R" for dentry in args: cmd += ' "{0}"'.format(dentry) - __salt__['cmd.run'](cmd, python_shell=False) + __salt__["cmd.run"](cmd, python_shell=False) return True def _acl_prefix(acl_type): - prefix = '' - if acl_type.startswith('d'): - prefix = 'd:' - acl_type = acl_type.replace('default:', '') - acl_type = acl_type.replace('d:', '') - if acl_type == 'user' or acl_type == 'u': - prefix += 'u' - elif acl_type == 'group' or acl_type == 'g': - prefix += 'g' - elif acl_type == 'mask' or acl_type == 'm': - prefix += 'm' + prefix = "" + if acl_type.startswith("d"): + prefix = "d:" + acl_type = acl_type.replace("default:", "") + acl_type = acl_type.replace("d:", "") + if acl_type == "user" or acl_type == "u": + prefix += "u" + elif acl_type == "group" or acl_type == "g": + prefix += "g" + elif acl_type == "mask" or acl_type == "m": + prefix += "m" return prefix -def modfacl(acl_type, acl_name='', perms='', *args, **kwargs): - ''' +def modfacl(acl_type, acl_name="", perms="", *args, **kwargs): + """ Add or modify a FACL for the specified file(s) CLI Examples: @@ -217,28 +222,30 @@ def modfacl(acl_type, acl_name='', perms='', *args, **kwargs): salt '*' acl.modfacl g mygroup 0 /tmp/house/kitchen /tmp/house/livingroom salt '*' acl.modfacl user myuser rwx /tmp/house/kitchen recursive=True salt '*' acl.modfacl user myuser rwx /tmp/house/kitchen raise_err=True - ''' - recursive = kwargs.pop('recursive', False) - raise_err = kwargs.pop('raise_err', False) + """ + recursive = kwargs.pop("recursive", False) + raise_err = kwargs.pop("raise_err", False) _raise_on_no_files(*args) - cmd = 'setfacl' + cmd = "setfacl" if recursive: - cmd += ' -R' # -R must come first as -m needs the acl_* arguments that come later + cmd += ( + " -R" # -R must come first as -m needs the acl_* arguments that come later + ) - cmd += ' -m' + cmd += " -m" - cmd = '{0} {1}:{2}:{3}'.format(cmd, _acl_prefix(acl_type), acl_name, perms) + cmd = "{0} {1}:{2}:{3}".format(cmd, _acl_prefix(acl_type), acl_name, perms) for dentry in args: cmd += ' "{0}"'.format(dentry) - __salt__['cmd.run'](cmd, python_shell=False, raise_err=raise_err) + __salt__["cmd.run"](cmd, python_shell=False, raise_err=raise_err) return True -def delfacl(acl_type, acl_name='', *args, **kwargs): - ''' +def delfacl(acl_type, acl_name="", *args, **kwargs): + """ Remove specific FACL from the specified file(s) CLI Examples: @@ -250,20 +257,20 @@ def delfacl(acl_type, acl_name='', *args, **kwargs): salt '*' acl.delfacl d:u myuser /tmp/house/kitchen salt '*' acl.delfacl g myuser /tmp/house/kitchen /tmp/house/livingroom salt '*' acl.delfacl user myuser /tmp/house/kitchen recursive=True - ''' - recursive = kwargs.pop('recursive', False) + """ + recursive = kwargs.pop("recursive", False) _raise_on_no_files(*args) - cmd = 'setfacl' + cmd = "setfacl" if recursive: - cmd += ' -R' + cmd += " -R" - cmd += ' -x' + cmd += " -x" - cmd = '{0} {1}:{2}'.format(cmd, _acl_prefix(acl_type), acl_name) + cmd = "{0} {1}:{2}".format(cmd, _acl_prefix(acl_type), acl_name) for dentry in args: cmd += ' "{0}"'.format(dentry) - __salt__['cmd.run'](cmd, python_shell=False) + __salt__["cmd.run"](cmd, python_shell=False) return True diff --git a/salt/modules/linux_ip.py b/salt/modules/linux_ip.py index f76497b8ee5..bac0665de21 100644 --- a/salt/modules/linux_ip.py +++ b/salt/modules/linux_ip.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" The networking module for Non-RH/Deb Linux distros -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,32 +10,34 @@ import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils - from salt.ext.six.moves import zip -__virtualname__ = 'ip' +__virtualname__ = "ip" def __virtual__(): - ''' + """ Confine this module to Non-RH/Deb Linux distros - ''' + """ if salt.utils.platform.is_windows(): - return (False, 'Module linux_ip: Windows systems are not supported.') - if __grains__['os_family'] == 'RedHat': - return (False, 'Module linux_ip: RedHat systems are not supported.') - if __grains__['os_family'] == 'Debian': - return (False, 'Module linux_ip: Debian systems are not supported.') - if __grains__['os_family'] == 'NILinuxRT': - return (False, 'Module linux_ip: NILinuxRT systems are not supported.') - if not salt.utils.path.which('ip'): - return (False, 'The linux_ip execution module cannot be loaded: ' - 'the ip binary is not in the path.') + return (False, "Module linux_ip: Windows systems are not supported.") + if __grains__["os_family"] == "RedHat": + return (False, "Module linux_ip: RedHat systems are not supported.") + if __grains__["os_family"] == "Debian": + return (False, "Module linux_ip: Debian systems are not supported.") + if __grains__["os_family"] == "NILinuxRT": + return (False, "Module linux_ip: NILinuxRT systems are not supported.") + if not salt.utils.path.which("ip"): + return ( + False, + "The linux_ip execution module cannot be loaded: " + "the ip binary is not in the path.", + ) return __virtualname__ def down(iface, iface_type=None): - ''' + """ Shutdown a network interface CLI Example: @@ -43,15 +45,15 @@ def down(iface, iface_type=None): .. code-block:: bash salt '*' ip.down eth0 - ''' + """ # Slave devices are controlled by the master. - if iface_type not in ['slave']: - return __salt__['cmd.run']('ip link set {0} down'.format(iface)) + if iface_type not in ["slave"]: + return __salt__["cmd.run"]("ip link set {0} down".format(iface)) return None def get_interface(iface): - ''' + """ Return the contents of an interface script CLI Example: @@ -59,41 +61,41 @@ def get_interface(iface): .. code-block:: bash salt '*' ip.get_interface eth0 - ''' + """ ifaces = _ip_ifaces() return ifaces.get(iface) def _ip_ifaces(): - ''' + """ Parse output from 'ip a' - ''' + """ tmp = {} ret = {} if_ = None at_ = None - out = __salt__['cmd.run']('ip a') + out = __salt__["cmd.run"]("ip a") for line in out.splitlines(): - if not line.startswith(' '): - comps = line.split(':') + if not line.startswith(" "): + comps = line.split(":") if_ = comps[1].strip() opts_comps = comps[2].strip().split() - flags = opts_comps.pop(0).lstrip('<').rstrip('>').split(',') + flags = opts_comps.pop(0).lstrip("<").rstrip(">").split(",") opts_iter = iter(opts_comps) ret[if_] = { - 'flags': flags, - 'options': dict(list(zip(opts_iter, opts_iter))) + "flags": flags, + "options": dict(list(zip(opts_iter, opts_iter))), } else: - if line.strip().startswith('link'): + if line.strip().startswith("link"): comps = iter(line.strip().split()) - ret[if_]['link_layer'] = dict(list(zip(comps, comps))) - elif line.strip().startswith('inet'): + ret[if_]["link_layer"] = dict(list(zip(comps, comps))) + elif line.strip().startswith("inet"): comps = line.strip().split() at_ = comps[0] if len(comps) % 2 != 0: last = comps.pop() - comps[-1] += ' {0}'.format(last) + comps[-1] += " {0}".format(last) ifi = iter(comps) ret[if_][at_] = dict(list(zip(ifi, ifi))) else: @@ -104,7 +106,7 @@ def _ip_ifaces(): def up(iface, iface_type=None): - ''' + """ Start up a network interface CLI Example: @@ -112,15 +114,15 @@ def up(iface, iface_type=None): .. code-block:: bash salt '*' ip.up eth0 - ''' + """ # Slave devices are controlled by the master. - if iface_type not in ['slave']: - return __salt__['cmd.run']('ip link set {0} up'.format(iface)) + if iface_type not in ["slave"]: + return __salt__["cmd.run"]("ip link set {0} up".format(iface)) return None def get_routes(iface=None): - ''' + """ Return the current routing table CLI Examples: @@ -129,7 +131,7 @@ def get_routes(iface=None): salt '*' ip.get_routes salt '*' ip.get_routes eth0 - ''' + """ routes = _parse_routes() if iface is not None: return routes.get(iface) @@ -137,10 +139,10 @@ def get_routes(iface=None): def _parse_routes(): - ''' + """ Parse the contents of ``/proc/net/route`` - ''' - with salt.utils.files.fopen('/proc/net/route', 'r') as fp_: + """ + with salt.utils.files.fopen("/proc/net/route", "r") as fp_: out = salt.utils.stringutils.to_unicode(fp_.read()) ret = {} @@ -148,20 +150,20 @@ def _parse_routes(): tmp = {} if not line.strip(): continue - if line.startswith('Iface'): + if line.startswith("Iface"): continue comps = line.split() - tmp['iface'] = comps[0] - tmp['destination'] = _hex_to_octets(comps[1]) - tmp['gateway'] = _hex_to_octets(comps[2]) - tmp['flags'] = _route_flags(int(comps[3])) - tmp['refcnt'] = comps[4] - tmp['use'] = comps[5] - tmp['metric'] = comps[6] - tmp['mask'] = _hex_to_octets(comps[7]) - tmp['mtu'] = comps[8] - tmp['window'] = comps[9] - tmp['irtt'] = comps[10] + tmp["iface"] = comps[0] + tmp["destination"] = _hex_to_octets(comps[1]) + tmp["gateway"] = _hex_to_octets(comps[2]) + tmp["flags"] = _route_flags(int(comps[3])) + tmp["refcnt"] = comps[4] + tmp["use"] = comps[5] + tmp["metric"] = comps[6] + tmp["mask"] = _hex_to_octets(comps[7]) + tmp["mtu"] = comps[8] + tmp["window"] = comps[9] + tmp["irtt"] = comps[10] if comps[0] not in ret: ret[comps[0]] = [] ret[comps[0]].append(tmp) @@ -169,33 +171,30 @@ def _parse_routes(): def _hex_to_octets(addr): - ''' + """ Convert hex fields from /proc/net/route to octects - ''' - return '{0}:{1}:{2}:{3}'.format( - int(addr[6:8], 16), - int(addr[4:6], 16), - int(addr[2:4], 16), - int(addr[0:2], 16), + """ + return "{0}:{1}:{2}:{3}".format( + int(addr[6:8], 16), int(addr[4:6], 16), int(addr[2:4], 16), int(addr[0:2], 16), ) def _route_flags(rflags): - ''' + """ https://github.com/torvalds/linux/blob/master/include/uapi/linux/route.h https://github.com/torvalds/linux/blob/master/include/uapi/linux/ipv6_route.h - ''' - flags = '' + """ + flags = "" fmap = { - 0x0001: 'U', # RTF_UP, route is up - 0x0002: 'G', # RTF_GATEWAY, use gateway - 0x0004: 'H', # RTF_HOST, target is a host - 0x0008: 'R', # RET_REINSTATE, reinstate route for dynamic routing - 0x0010: 'D', # RTF_DYNAMIC, dynamically installed by daemon or redirect - 0x0020: 'M', # RTF_MODIFIED, modified from routing daemon or redirect - 0x00040000: 'A', # RTF_ADDRCONF, installed by addrconf - 0x01000000: 'C', # RTF_CACHE, cache entry - 0x0200: '!', # RTF_REJECT, reject route + 0x0001: "U", # RTF_UP, route is up + 0x0002: "G", # RTF_GATEWAY, use gateway + 0x0004: "H", # RTF_HOST, target is a host + 0x0008: "R", # RET_REINSTATE, reinstate route for dynamic routing + 0x0010: "D", # RTF_DYNAMIC, dynamically installed by daemon or redirect + 0x0020: "M", # RTF_MODIFIED, modified from routing daemon or redirect + 0x00040000: "A", # RTF_ADDRCONF, installed by addrconf + 0x01000000: "C", # RTF_CACHE, cache entry + 0x0200: "!", # RTF_REJECT, reject route } for item in fmap: if rflags & item: diff --git a/salt/modules/linux_lvm.py b/salt/modules/linux_lvm.py index 0a975324afa..43794c2261f 100644 --- a/salt/modules/linux_lvm.py +++ b/salt/modules/linux_lvm.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Support for Linux LVM2 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -10,27 +10,30 @@ import os.path # Import salt libs import salt.utils.path -from salt.ext import six from salt.exceptions import CommandExecutionError +from salt.ext import six # Set up logger log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'lvm' +__virtualname__ = "lvm" def __virtual__(): - ''' + """ Only load the module if lvm is installed - ''' - if salt.utils.path.which('lvm'): + """ + if salt.utils.path.which("lvm"): return __virtualname__ - return (False, 'The linux_lvm execution module cannot be loaded: the lvm binary is not in the path.') + return ( + False, + "The linux_lvm execution module cannot be loaded: the lvm binary is not in the path.", + ) def version(): - ''' + """ Return LVM version from lvm version CLI Example: @@ -38,15 +41,15 @@ def version(): .. code-block:: bash salt '*' lvm.version - ''' - cmd = 'lvm version' - out = __salt__['cmd.run'](cmd).splitlines() - ret = out[0].split(': ') + """ + cmd = "lvm version" + out = __salt__["cmd.run"](cmd).splitlines() + ret = out[0].split(": ") return ret[1].strip() def fullversion(): - ''' + """ Return all version info from lvm version CLI Example: @@ -54,18 +57,18 @@ def fullversion(): .. code-block:: bash salt '*' lvm.fullversion - ''' + """ ret = {} - cmd = 'lvm version' - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "lvm version" + out = __salt__["cmd.run"](cmd).splitlines() for line in out: - comps = line.split(':') + comps = line.split(":") ret[comps[0].strip()] = comps[1].strip() return ret -def pvdisplay(pvname='', real=False, quiet=False): - ''' +def pvdisplay(pvname="", real=False, quiet=False): + """ Return information about the physical volume(s) pvname @@ -86,45 +89,44 @@ def pvdisplay(pvname='', real=False, quiet=False): salt '*' lvm.pvdisplay salt '*' lvm.pvdisplay /dev/md0 - ''' + """ ret = {} - cmd = ['pvdisplay', '-c'] + cmd = ["pvdisplay", "-c"] if pvname: cmd.append(pvname) - cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False, - ignore_retcode=quiet) + cmd_ret = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=quiet) - if cmd_ret['retcode'] != 0: + if cmd_ret["retcode"] != 0: return {} - out = cmd_ret['stdout'].splitlines() + out = cmd_ret["stdout"].splitlines() for line in out: - if 'is a new physical volume' not in line: - comps = line.strip().split(':') + if "is a new physical volume" not in line: + comps = line.strip().split(":") if real: device = os.path.realpath(comps[0]) else: device = comps[0] ret[device] = { - 'Physical Volume Device': comps[0], - 'Volume Group Name': comps[1], - 'Physical Volume Size (kB)': comps[2], - 'Internal Physical Volume Number': comps[3], - 'Physical Volume Status': comps[4], - 'Physical Volume (not) Allocatable': comps[5], - 'Current Logical Volumes Here': comps[6], - 'Physical Extent Size (kB)': comps[7], - 'Total Physical Extents': comps[8], - 'Free Physical Extents': comps[9], - 'Allocated Physical Extents': comps[10], - } + "Physical Volume Device": comps[0], + "Volume Group Name": comps[1], + "Physical Volume Size (kB)": comps[2], + "Internal Physical Volume Number": comps[3], + "Physical Volume Status": comps[4], + "Physical Volume (not) Allocatable": comps[5], + "Current Logical Volumes Here": comps[6], + "Physical Extent Size (kB)": comps[7], + "Total Physical Extents": comps[8], + "Free Physical Extents": comps[9], + "Allocated Physical Extents": comps[10], + } if real: - ret[device]['Real Physical Volume Device'] = device + ret[device]["Real Physical Volume Device"] = device return ret -def vgdisplay(vgname='', quiet=False): - ''' +def vgdisplay(vgname="", quiet=False): + """ Return information about the volume group(s) vgname @@ -139,44 +141,43 @@ def vgdisplay(vgname='', quiet=False): salt '*' lvm.vgdisplay salt '*' lvm.vgdisplay nova-volumes - ''' + """ ret = {} - cmd = ['vgdisplay', '-c'] + cmd = ["vgdisplay", "-c"] if vgname: cmd.append(vgname) - cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False, - ignore_retcode=quiet) + cmd_ret = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=quiet) - if cmd_ret['retcode'] != 0: + if cmd_ret["retcode"] != 0: return {} - out = cmd_ret['stdout'].splitlines() + out = cmd_ret["stdout"].splitlines() for line in out: - comps = line.strip().split(':') + comps = line.strip().split(":") ret[comps[0]] = { - 'Volume Group Name': comps[0], - 'Volume Group Access': comps[1], - 'Volume Group Status': comps[2], - 'Internal Volume Group Number': comps[3], - 'Maximum Logical Volumes': comps[4], - 'Current Logical Volumes': comps[5], - 'Open Logical Volumes': comps[6], - 'Maximum Logical Volume Size': comps[7], - 'Maximum Physical Volumes': comps[8], - 'Current Physical Volumes': comps[9], - 'Actual Physical Volumes': comps[10], - 'Volume Group Size (kB)': comps[11], - 'Physical Extent Size (kB)': comps[12], - 'Total Physical Extents': comps[13], - 'Allocated Physical Extents': comps[14], - 'Free Physical Extents': comps[15], - 'UUID': comps[16], - } + "Volume Group Name": comps[0], + "Volume Group Access": comps[1], + "Volume Group Status": comps[2], + "Internal Volume Group Number": comps[3], + "Maximum Logical Volumes": comps[4], + "Current Logical Volumes": comps[5], + "Open Logical Volumes": comps[6], + "Maximum Logical Volume Size": comps[7], + "Maximum Physical Volumes": comps[8], + "Current Physical Volumes": comps[9], + "Actual Physical Volumes": comps[10], + "Volume Group Size (kB)": comps[11], + "Physical Extent Size (kB)": comps[12], + "Total Physical Extents": comps[13], + "Allocated Physical Extents": comps[14], + "Free Physical Extents": comps[15], + "UUID": comps[16], + } return ret -def lvdisplay(lvname='', quiet=False): - ''' +def lvdisplay(lvname="", quiet=False): + """ Return information about the logical volume(s) lvname @@ -191,40 +192,39 @@ def lvdisplay(lvname='', quiet=False): salt '*' lvm.lvdisplay salt '*' lvm.lvdisplay /dev/vg_myserver/root - ''' + """ ret = {} - cmd = ['lvdisplay', '-c'] + cmd = ["lvdisplay", "-c"] if lvname: cmd.append(lvname) - cmd_ret = __salt__['cmd.run_all'](cmd, python_shell=False, - ignore_retcode=quiet) + cmd_ret = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=quiet) - if cmd_ret['retcode'] != 0: + if cmd_ret["retcode"] != 0: return {} - out = cmd_ret['stdout'].splitlines() + out = cmd_ret["stdout"].splitlines() for line in out: - comps = line.strip().split(':') + comps = line.strip().split(":") ret[comps[0]] = { - 'Logical Volume Name': comps[0], - 'Volume Group Name': comps[1], - 'Logical Volume Access': comps[2], - 'Logical Volume Status': comps[3], - 'Internal Logical Volume Number': comps[4], - 'Open Logical Volumes': comps[5], - 'Logical Volume Size': comps[6], - 'Current Logical Extents Associated': comps[7], - 'Allocated Logical Extents': comps[8], - 'Allocation Policy': comps[9], - 'Read Ahead Sectors': comps[10], - 'Major Device Number': comps[11], - 'Minor Device Number': comps[12], - } + "Logical Volume Name": comps[0], + "Volume Group Name": comps[1], + "Logical Volume Access": comps[2], + "Logical Volume Status": comps[3], + "Internal Logical Volume Number": comps[4], + "Open Logical Volumes": comps[5], + "Logical Volume Size": comps[6], + "Current Logical Extents Associated": comps[7], + "Allocated Logical Extents": comps[8], + "Allocation Policy": comps[9], + "Read Ahead Sectors": comps[10], + "Major Device Number": comps[11], + "Minor Device Number": comps[12], + } return ret def pvcreate(devices, override=True, **kwargs): - ''' + """ Set a physical device to be used as an LVM physical volume override @@ -236,39 +236,49 @@ def pvcreate(devices, override=True, **kwargs): salt mymachine lvm.pvcreate /dev/sdb1,/dev/sdb2 salt mymachine lvm.pvcreate /dev/sdb1 dataalignmentoffset=7s - ''' + """ if not devices: - return 'Error: at least one device is required' + return "Error: at least one device is required" if isinstance(devices, six.string_types): - devices = devices.split(',') + devices = devices.split(",") - cmd = ['pvcreate', '-y'] + cmd = ["pvcreate", "-y"] for device in devices: if not os.path.exists(device): - raise CommandExecutionError('{0} does not exist'.format(device)) + raise CommandExecutionError("{0} does not exist".format(device)) if not pvdisplay(device, quiet=True): cmd.append(device) elif not override: - raise CommandExecutionError('Device "{0}" is already an LVM physical volume.'.format(device)) + raise CommandExecutionError( + 'Device "{0}" is already an LVM physical volume.'.format(device) + ) if not cmd[2:]: # All specified devices are already LVM volumes return True - valid = ('metadatasize', 'dataalignment', 'dataalignmentoffset', - 'pvmetadatacopies', 'metadatacopies', 'metadataignore', - 'restorefile', 'norestorefile', 'labelsector', - 'setphysicalvolumesize') - no_parameter = ('force', 'norestorefile') + valid = ( + "metadatasize", + "dataalignment", + "dataalignmentoffset", + "pvmetadatacopies", + "metadatacopies", + "metadataignore", + "restorefile", + "norestorefile", + "labelsector", + "setphysicalvolumesize", + ) + no_parameter = ("force", "norestorefile") for var in kwargs: if kwargs[var] and var in valid: - cmd.extend(['--{0}'.format(var), kwargs[var]]) + cmd.extend(["--{0}".format(var), kwargs[var]]) elif kwargs[var] and var in no_parameter: - cmd.append('--{0}'.format(var)) + cmd.append("--{0}".format(var)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out.get('retcode'): - raise CommandExecutionError(out.get('stderr')) + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out.get("retcode"): + raise CommandExecutionError(out.get("stderr")) # Verify pvcreate was successful for device in devices: @@ -279,7 +289,7 @@ def pvcreate(devices, override=True, **kwargs): def pvremove(devices, override=True): - ''' + """ Remove a physical device being used as an LVM physical volume override @@ -290,24 +300,24 @@ def pvremove(devices, override=True): .. code-block:: bash salt mymachine lvm.pvremove /dev/sdb1,/dev/sdb2 - ''' + """ if isinstance(devices, six.string_types): - devices = devices.split(',') + devices = devices.split(",") - cmd = ['pvremove', '-y'] + cmd = ["pvremove", "-y"] for device in devices: if pvdisplay(device): cmd.append(device) elif not override: - raise CommandExecutionError('{0} is not a physical volume'.format(device)) + raise CommandExecutionError("{0} is not a physical volume".format(device)) if not cmd[2:]: # Nothing to do return True - out = __salt__['cmd.run_all'](cmd, python_shell=False) - if out.get('retcode'): - raise CommandExecutionError(out.get('stderr')) + out = __salt__["cmd.run_all"](cmd, python_shell=False) + if out.get("retcode"): + raise CommandExecutionError(out.get("stderr")) # Verify pvcremove was successful for device in devices: @@ -318,7 +328,7 @@ def pvremove(devices, override=True): def vgcreate(vgname, devices, **kwargs): - ''' + """ Create an LVM volume group CLI Examples: @@ -327,29 +337,35 @@ def vgcreate(vgname, devices, **kwargs): salt mymachine lvm.vgcreate my_vg /dev/sdb1,/dev/sdb2 salt mymachine lvm.vgcreate my_vg /dev/sdb1 clustered=y - ''' + """ if not vgname or not devices: - return 'Error: vgname and device(s) are both required' + return "Error: vgname and device(s) are both required" if isinstance(devices, six.string_types): - devices = devices.split(',') + devices = devices.split(",") - cmd = ['vgcreate', vgname] + cmd = ["vgcreate", vgname] for device in devices: cmd.append(device) - valid = ('clustered', 'maxlogicalvolumes', 'maxphysicalvolumes', - 'vgmetadatacopies', 'metadatacopies', 'physicalextentsize') + valid = ( + "clustered", + "maxlogicalvolumes", + "maxphysicalvolumes", + "vgmetadatacopies", + "metadatacopies", + "physicalextentsize", + ) for var in kwargs: if kwargs[var] and var in valid: - cmd.append('--{0}'.format(var)) + cmd.append("--{0}".format(var)) cmd.append(kwargs[var]) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() vgdata = vgdisplay(vgname) - vgdata['Output from vgcreate'] = out[0].strip() + vgdata["Output from vgcreate"] = out[0].strip() return vgdata def vgextend(vgname, devices): - ''' + """ Add physical volumes to an LVM volume group CLI Examples: @@ -358,31 +374,33 @@ def vgextend(vgname, devices): salt mymachine lvm.vgextend my_vg /dev/sdb1,/dev/sdb2 salt mymachine lvm.vgextend my_vg /dev/sdb1 - ''' + """ if not vgname or not devices: - return 'Error: vgname and device(s) are both required' + return "Error: vgname and device(s) are both required" if isinstance(devices, six.string_types): - devices = devices.split(',') + devices = devices.split(",") - cmd = ['vgextend', vgname] + cmd = ["vgextend", vgname] for device in devices: cmd.append(device) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - vgdata = {'Output from vgextend': out[0].strip()} + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() + vgdata = {"Output from vgextend": out[0].strip()} return vgdata -def lvcreate(lvname, - vgname, - size=None, - extents=None, - snapshot=None, - pv=None, - thinvolume=False, - thinpool=False, - force=False, - **kwargs): - ''' +def lvcreate( + lvname, + vgname, + size=None, + extents=None, + snapshot=None, + pv=None, + thinvolume=False, + thinpool=False, + force=False, + **kwargs +): + """ Create a new logical volume, with option for which physical volume to be used CLI Examples: @@ -404,51 +422,71 @@ def lvcreate(lvname, salt '*' lvm.lvcreate new_thinpool_name vg_name size=20G thinpool=True salt '*' lvm.lvcreate new_thinvolume_name vg_name/thinpool_name size=10G thinvolume=True - ''' + """ if size and extents: - return 'Error: Please specify only one of size or extents' + return "Error: Please specify only one of size or extents" if thinvolume and thinpool: - return 'Error: Please set only one of thinvolume or thinpool to True' + return "Error: Please set only one of thinvolume or thinpool to True" - valid = ('activate', 'chunksize', 'contiguous', 'discards', 'stripes', - 'stripesize', 'minor', 'persistent', 'mirrors', 'noudevsync', - 'monitor', 'ignoremonitoring', 'permission', 'poolmetadatasize', - 'readahead', 'regionsize', 'type', - 'virtualsize', 'zero') - no_parameter = ('noudevsync', 'ignoremonitoring', 'thin', ) + valid = ( + "activate", + "chunksize", + "contiguous", + "discards", + "stripes", + "stripesize", + "minor", + "persistent", + "mirrors", + "noudevsync", + "monitor", + "ignoremonitoring", + "permission", + "poolmetadatasize", + "readahead", + "regionsize", + "type", + "virtualsize", + "zero", + ) + no_parameter = ( + "noudevsync", + "ignoremonitoring", + "thin", + ) extra_arguments = [] if kwargs: for k, v in six.iteritems(kwargs): if k in no_parameter: - extra_arguments.append('--{0}'.format(k)) + extra_arguments.append("--{0}".format(k)) elif k in valid: - extra_arguments.extend(['--{0}'.format(k), '{0}'.format(v)]) + extra_arguments.extend(["--{0}".format(k), "{0}".format(v)]) - cmd = [salt.utils.path.which('lvcreate')] + cmd = [salt.utils.path.which("lvcreate")] if thinvolume: - cmd.extend(['--thin', '-n', lvname]) + cmd.extend(["--thin", "-n", lvname]) elif thinpool: - cmd.extend(['--thinpool', lvname]) + cmd.extend(["--thinpool", lvname]) else: - cmd.extend(['-n', lvname]) + cmd.extend(["-n", lvname]) if snapshot: - cmd.extend(['-s', '{0}/{1}'.format(vgname, snapshot)]) + cmd.extend(["-s", "{0}/{1}".format(vgname, snapshot)]) else: cmd.append(vgname) if size and thinvolume: - cmd.extend(['-V', '{0}'.format(size)]) + cmd.extend(["-V", "{0}".format(size)]) elif extents and thinvolume: - return 'Error: Thin volume size cannot be specified as extents' + return "Error: Thin volume size cannot be specified as extents" elif size: - cmd.extend(['-L', '{0}'.format(size)]) + cmd.extend(["-L", "{0}".format(size)]) elif extents: - cmd.extend(['-l', '{0}'.format(extents)]) + cmd.extend(["-l", "{0}".format(extents)]) else: - return 'Error: Either size or extents must be specified' + return "Error: Either size or extents must be specified" if pv: cmd.append(pv) @@ -456,17 +494,17 @@ def lvcreate(lvname, cmd.extend(extra_arguments) if force: - cmd.append('--yes') + cmd.append("--yes") - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - lvdev = '/dev/{0}/{1}'.format(vgname, lvname) + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() + lvdev = "/dev/{0}/{1}".format(vgname, lvname) lvdata = lvdisplay(lvdev) - lvdata['Output from lvcreate'] = out[0].strip() + lvdata["Output from lvcreate"] = out[0].strip() return lvdata def vgremove(vgname): - ''' + """ Remove an LVM volume group CLI Examples: @@ -475,14 +513,14 @@ def vgremove(vgname): salt mymachine lvm.vgremove vgname salt mymachine lvm.vgremove vgname force=True - ''' - cmd = ['vgremove', '-f', vgname] - out = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = ["vgremove", "-f", vgname] + out = __salt__["cmd.run"](cmd, python_shell=False) return out.strip() def lvremove(lvname, vgname): - ''' + """ Remove a given existing logical volume from a named existing volume group CLI Example: @@ -490,14 +528,14 @@ def lvremove(lvname, vgname): .. code-block:: bash salt '*' lvm.lvremove lvname vgname force=True - ''' - cmd = ['lvremove', '-f', '{0}/{1}'.format(vgname, lvname)] - out = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = ["lvremove", "-f", "{0}/{1}".format(vgname, lvname)] + out = __salt__["cmd.run"](cmd, python_shell=False) return out.strip() def lvresize(size=None, lvpath=None, extents=None): - ''' + """ Return information about the logical volume(s) CLI Examples: @@ -508,21 +546,21 @@ def lvresize(size=None, lvpath=None, extents=None): salt '*' lvm.lvresize +12M /dev/mapper/vg1-test salt '*' lvm.lvresize lvpath=/dev/mapper/vg1-test extents=+100%FREE - ''' + """ if size and extents: - log.error('Error: Please specify only one of size or extents') + log.error("Error: Please specify only one of size or extents") return {} - cmd = ['lvresize'] + cmd = ["lvresize"] if size: - cmd.extend(['-L', '{0}'.format(size)]) + cmd.extend(["-L", "{0}".format(size)]) elif extents: - cmd.extend(['-l', '{0}'.format(extents)]) + cmd.extend(["-l", "{0}".format(extents)]) else: - log.error('Error: Either size or extents must be specified') + log.error("Error: Either size or extents must be specified") return {} cmd.append(lvpath) - cmd_ret = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - return {'Output from lvresize': cmd_ret[0].strip()} + cmd_ret = __salt__["cmd.run"](cmd, python_shell=False).splitlines() + return {"Output from lvresize": cmd_ret[0].strip()} diff --git a/salt/modules/service.py b/salt/modules/linux_service.py similarity index 62% rename from salt/modules/service.py rename to salt/modules/linux_service.py index 002b070c303..07359bf65dd 100644 --- a/salt/modules/service.py +++ b/salt/modules/linux_service.py @@ -1,64 +1,62 @@ # -*- coding: utf-8 -*- -''' +""" If Salt's OS detection does not identify a different virtual service module, the minion will fall back to using this basic module, which simply wraps sysvinit scripts. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import fnmatch + # Import python libs import os -import fnmatch import re -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} -_GRAINMAP = { - 'Arch': '/etc/rc.d', - 'Arch ARM': '/etc/rc.d' -} +_GRAINMAP = {"Arch": "/etc/rc.d", "Arch ARM": "/etc/rc.d"} def __virtual__(): - ''' + """ Only work on systems which exclusively use sysvinit - ''' + """ # Disable on these platforms, specific service modules exist: - disable = set(( - 'RedHat', - 'CentOS', - 'Amazon', - 'ScientificLinux', - 'CloudLinux', - 'Fedora', - 'Gentoo', - 'Ubuntu', - 'Debian', - 'Devuan', - 'ALT', - 'OEL', - 'Linaro', - 'elementary OS', - 'McAfee OS Server', - 'Raspbian', - 'SUSE', - )) - if __grains__.get('os') in disable: - return (False, 'Your OS is on the disabled list') + disable = set( + ( + "RedHat", + "CentOS", + "Amazon", + "ScientificLinux", + "CloudLinux", + "Fedora", + "Gentoo", + "Ubuntu", + "Debian", + "Devuan", + "ALT", + "OEL", + "Linaro", + "elementary OS", + "McAfee OS Server", + "Raspbian", + "SUSE", + ) + ) + if __grains__.get("os") in disable: + return (False, "Your OS is on the disabled list") # Disable on all non-Linux OSes as well - if __grains__['kernel'] != 'Linux': - return (False, 'Non Linux OSes are not supported') - init_grain = __grains__.get('init') - if init_grain not in (None, 'sysvinit', 'unknown'): - return (False, 'Minion is running {0}'.format(init_grain)) - elif __utils__['systemd.booted'](__context__): + if __grains__["kernel"] != "Linux": + return (False, "Non Linux OSes are not supported") + init_grain = __grains__.get("init") + if init_grain not in (None, "sysvinit", "unknown"): + return (False, "Minion is running {0}".format(init_grain)) + elif __utils__["systemd.booted"](__context__): # Should have been caught by init grain check, but check just in case - return (False, 'Minion is running systemd') - return 'service' + return (False, "Minion is running systemd") + return "service" def run(name, action): - ''' + """ Run the specified service with an action. .. versionadded:: 2015.8.1 @@ -75,16 +73,17 @@ def run(name, action): salt '*' service.run apache2 reload salt '*' service.run postgresql initdb - ''' - cmd = os.path.join( - _GRAINMAP.get(__grains__.get('os'), '/etc/init.d'), - name - ) + ' ' + action - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = ( + os.path.join(_GRAINMAP.get(__grains__.get("os"), "/etc/init.d"), name) + + " " + + action + ) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def start(name): - ''' + """ Start the specified service CLI Example: @@ -92,12 +91,12 @@ def start(name): .. code-block:: bash salt '*' service.start - ''' - return run(name, 'start') + """ + return run(name, "start") def stop(name): - ''' + """ Stop the specified service CLI Example: @@ -105,12 +104,12 @@ def stop(name): .. code-block:: bash salt '*' service.stop - ''' - return run(name, 'stop') + """ + return run(name, "stop") def restart(name): - ''' + """ Restart the specified service CLI Example: @@ -118,12 +117,12 @@ def restart(name): .. code-block:: bash salt '*' service.restart - ''' - return run(name, 'restart') + """ + return run(name, "restart") def status(name, sig=None): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to PID or empty string is returned. @@ -144,25 +143,25 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status [service signature] - ''' + """ if sig: - return __salt__['status.pid'](sig) + return __salt__["status.pid"](sig) - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: services = [name] results = {} for service in services: - results[service] = __salt__['status.pid'](service) + results[service] = __salt__["status.pid"](service) if contains_globbing: return results return results[name] def reload_(name): - ''' + """ Refreshes config files by calling service reload. Does not perform a full restart. @@ -171,12 +170,12 @@ def reload_(name): .. code-block:: bash salt '*' service.reload - ''' - return run(name, 'reload') + """ + return run(name, "reload") def available(name): - ''' + """ Returns ``True`` if the specified service is available, otherwise returns ``False``. @@ -185,12 +184,12 @@ def available(name): .. code-block:: bash salt '*' service.available sshd - ''' + """ return name in get_all() def missing(name): - ''' + """ The inverse of service.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -200,12 +199,12 @@ def missing(name): .. code-block:: bash salt '*' service.missing sshd - ''' + """ return name not in get_all() def get_all(): - ''' + """ Return a list of all available services CLI Example: @@ -213,7 +212,7 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' - if not os.path.isdir(_GRAINMAP.get(__grains__.get('os'), '/etc/init.d')): + """ + if not os.path.isdir(_GRAINMAP.get(__grains__.get("os"), "/etc/init.d")): return [] - return sorted(os.listdir(_GRAINMAP.get(__grains__.get('os'), '/etc/init.d'))) + return sorted(os.listdir(_GRAINMAP.get(__grains__.get("os"), "/etc/init.d"))) diff --git a/salt/modules/shadow.py b/salt/modules/linux_shadow.py similarity index 73% rename from salt/modules/shadow.py rename to salt/modules/linux_shadow.py index 98c7369c5eb..3ac8a82b5fd 100644 --- a/salt/modules/shadow.py +++ b/salt/modules/linux_shadow.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage the shadow file on Linux systems .. important:: @@ -7,17 +7,14 @@ Manage the shadow file on Linux systems minion, and it is using a different module (or gives an error similar to *'shadow.info' is not available*), see :ref:`here `. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import datetime +import functools # Import python libs import os -import datetime -import functools -try: - import spwd -except ImportError: - pass # Import salt libs import salt.utils.data @@ -26,19 +23,29 @@ import salt.utils.stringutils from salt.exceptions import CommandExecutionError from salt.ext import six from salt.ext.six.moves import range + +try: + import spwd +except ImportError: + pass + + try: import salt.utils.pycrypto + HAS_CRYPT = True except ImportError: HAS_CRYPT = False +__virtualname__ = "shadow" + def __virtual__(): - return __grains__.get('kernel', '') == 'Linux' + return __virtualname__ if __grains__.get("kernel", "") == "Linux" else False def default_hash(): - ''' + """ Returns the default hash used for unset passwords CLI Example: @@ -46,12 +53,12 @@ def default_hash(): .. code-block:: bash salt '*' shadow.default_hash - ''' - return '!' + """ + return "!" def info(name, root=None): - ''' + """ Return information for the specified user name @@ -65,7 +72,7 @@ def info(name, root=None): .. code-block:: bash salt '*' shadow.info root - ''' + """ if root is not None: getspnam = functools.partial(_getspnam, root=root) else: @@ -74,56 +81,58 @@ def info(name, root=None): try: data = getspnam(name) ret = { - 'name': data.sp_namp if hasattr(data, 'sp_namp') else data.sp_nam, - 'passwd': data.sp_pwdp if hasattr(data, 'sp_pwdp') else data.sp_pwd, - 'lstchg': data.sp_lstchg, - 'min': data.sp_min, - 'max': data.sp_max, - 'warn': data.sp_warn, - 'inact': data.sp_inact, - 'expire': data.sp_expire} + "name": data.sp_namp if hasattr(data, "sp_namp") else data.sp_nam, + "passwd": data.sp_pwdp if hasattr(data, "sp_pwdp") else data.sp_pwd, + "lstchg": data.sp_lstchg, + "min": data.sp_min, + "max": data.sp_max, + "warn": data.sp_warn, + "inact": data.sp_inact, + "expire": data.sp_expire, + } except KeyError: return { - 'name': '', - 'passwd': '', - 'lstchg': '', - 'min': '', - 'max': '', - 'warn': '', - 'inact': '', - 'expire': ''} + "name": "", + "passwd": "", + "lstchg": "", + "min": "", + "max": "", + "warn": "", + "inact": "", + "expire": "", + } return ret def _set_attrib(name, key, value, param, root=None, validate=True): - ''' + """ Set a parameter in /etc/shadow - ''' + """ pre_info = info(name, root=root) # If the user is not present or the attribute is already present, # we return early - if not pre_info['name']: + if not pre_info["name"]: return False if value == pre_info[key]: return True - cmd = ['chage'] + cmd = ["chage"] if root is not None: - cmd.extend(('-R', root)) + cmd.extend(("-R", root)) cmd.extend((param, value, name)) - ret = not __salt__['cmd.run'](cmd, python_shell=False) + ret = not __salt__["cmd.run"](cmd, python_shell=False) if validate: ret = info(name, root=root).get(key) == value return ret def set_inactdays(name, inactdays, root=None): - ''' + """ Set the number of days of inactivity after a password has expired before the account is locked. See man chage. @@ -141,12 +150,12 @@ def set_inactdays(name, inactdays, root=None): .. code-block:: bash salt '*' shadow.set_inactdays username 7 - ''' - return _set_attrib(name, 'inact', inactdays, '-I', root=root) + """ + return _set_attrib(name, "inact", inactdays, "-I", root=root) def set_maxdays(name, maxdays, root=None): - ''' + """ Set the maximum number of days during which a password is valid. See man chage. @@ -164,12 +173,12 @@ def set_maxdays(name, maxdays, root=None): .. code-block:: bash salt '*' shadow.set_maxdays username 90 - ''' - return _set_attrib(name, 'max', maxdays, '-M', root=root) + """ + return _set_attrib(name, "max", maxdays, "-M", root=root) def set_mindays(name, mindays, root=None): - ''' + """ Set the minimum number of days between password changes. See man chage. name @@ -186,12 +195,12 @@ def set_mindays(name, mindays, root=None): .. code-block:: bash salt '*' shadow.set_mindays username 7 - ''' - return _set_attrib(name, 'min', mindays, '-m', root=root) + """ + return _set_attrib(name, "min", mindays, "-m", root=root) -def gen_password(password, crypt_salt=None, algorithm='sha512'): - ''' +def gen_password(password, crypt_salt=None, algorithm="sha512"): + """ .. versionadded:: 2014.7.0 Generate hashed password @@ -223,17 +232,17 @@ def gen_password(password, crypt_salt=None, algorithm='sha512'): salt '*' shadow.gen_password 'I_am_password' salt '*' shadow.gen_password 'I_am_password' crypt_salt='I_am_salt' algorithm=sha256 - ''' + """ if not HAS_CRYPT: raise CommandExecutionError( - 'gen_password is not available on this operating system ' - 'because the "crypt" python module is not available.' - ) + "gen_password is not available on this operating system " + 'because the "crypt" python module is not available.' + ) return salt.utils.pycrypto.gen_hash(crypt_salt, password, algorithm) def del_password(name, root=None): - ''' + """ .. versionadded:: 2014.7.0 Delete the password from name user @@ -249,19 +258,19 @@ def del_password(name, root=None): .. code-block:: bash salt '*' shadow.del_password username - ''' - cmd = ['passwd'] + """ + cmd = ["passwd"] if root is not None: - cmd.extend(('-R', root)) - cmd.extend(('-d', name)) + cmd.extend(("-R", root)) + cmd.extend(("-d", name)) - __salt__['cmd.run'](cmd, python_shell=False, output_loglevel='quiet') + __salt__["cmd.run"](cmd, python_shell=False, output_loglevel="quiet") uinfo = info(name, root=root) - return not uinfo['passwd'] and uinfo['name'] == name + return not uinfo["passwd"] and uinfo["name"] == name def lock_password(name, root=None): - ''' + """ .. versionadded:: 2016.11.0 Lock the password from specified user @@ -277,27 +286,27 @@ def lock_password(name, root=None): .. code-block:: bash salt '*' shadow.lock_password username - ''' + """ pre_info = info(name, root=root) - if not pre_info['name']: + if not pre_info["name"]: return False - if pre_info['passwd'].startswith('!'): + if pre_info["passwd"].startswith("!"): return True - cmd = ['passwd'] + cmd = ["passwd"] if root is not None: - cmd.extend(('-R', root)) + cmd.extend(("-R", root)) - cmd.extend(('-l', name)) + cmd.extend(("-l", name)) - __salt__['cmd.run'](cmd, python_shell=False) - return info(name, root=root)['passwd'].startswith('!') + __salt__["cmd.run"](cmd, python_shell=False) + return info(name, root=root)["passwd"].startswith("!") def unlock_password(name, root=None): - ''' + """ .. versionadded:: 2016.11.0 Unlock the password from name user @@ -313,27 +322,27 @@ def unlock_password(name, root=None): .. code-block:: bash salt '*' shadow.unlock_password username - ''' + """ pre_info = info(name, root=root) - if not pre_info['name']: + if not pre_info["name"]: return False - if not pre_info['passwd'].startswith('!'): + if not pre_info["passwd"].startswith("!"): return True - cmd = ['passwd'] + cmd = ["passwd"] if root is not None: - cmd.extend(('-R', root)) + cmd.extend(("-R", root)) - cmd.extend(('-u', name)) + cmd.extend(("-u", name)) - __salt__['cmd.run'](cmd, python_shell=False) - return not info(name, root=root)['passwd'].startswith('!') + __salt__["cmd.run"](cmd, python_shell=False) + return not info(name, root=root)["passwd"].startswith("!") def set_password(name, password, use_usermod=False, root=None): - ''' + """ Set the password for a named user. The password must be a properly defined hash. The password hash can be generated with this command: @@ -363,15 +372,15 @@ def set_password(name, password, use_usermod=False, root=None): .. code-block:: bash salt '*' shadow.set_password root '$1$UYCIxa628.9qXjpQCjM4a..' - ''' + """ if not salt.utils.data.is_true(use_usermod): # Edit the shadow file directly # ALT Linux uses tcb to store password hashes. More information found # in manpage (http://docs.altlinux.org/manpages/tcb.5.html) - if __grains__['os'] == 'ALT': - s_file = '/etc/tcb/{0}/shadow'.format(name) + if __grains__["os"] == "ALT": + s_file = "/etc/tcb/{0}/shadow".format(name) else: - s_file = '/etc/shadow' + s_file = "/etc/shadow" if root: s_file = os.path.join(root, os.path.relpath(s_file, os.path.sep)) @@ -379,37 +388,37 @@ def set_password(name, password, use_usermod=False, root=None): if not os.path.isfile(s_file): return ret lines = [] - with salt.utils.files.fopen(s_file, 'rb') as fp_: + with salt.utils.files.fopen(s_file, "rb") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - comps = line.strip().split(':') + comps = line.strip().split(":") if comps[0] != name: lines.append(line) continue changed_date = datetime.datetime.today() - datetime.datetime(1970, 1, 1) comps[1] = password comps[2] = six.text_type(changed_date.days) - line = ':'.join(comps) - lines.append('{0}\n'.format(line)) - with salt.utils.files.fopen(s_file, 'w+') as fp_: + line = ":".join(comps) + lines.append("{0}\n".format(line)) + with salt.utils.files.fopen(s_file, "w+") as fp_: lines = [salt.utils.stringutils.to_str(_l) for _l in lines] fp_.writelines(lines) uinfo = info(name, root=root) - return uinfo['passwd'] == password + return uinfo["passwd"] == password else: # Use usermod -p (less secure, but more feature-complete) - cmd = ['usermod'] + cmd = ["usermod"] if root is not None: - cmd.extend(('-R', root)) - cmd.extend(('-p', password, name)) + cmd.extend(("-R", root)) + cmd.extend(("-p", password, name)) - __salt__['cmd.run'](cmd, python_shell=False, output_loglevel='quiet') + __salt__["cmd.run"](cmd, python_shell=False, output_loglevel="quiet") uinfo = info(name, root=root) - return uinfo['passwd'] == password + return uinfo["passwd"] == password def set_warndays(name, warndays, root=None): - ''' + """ Set the number of days of warning before a password change is required. See man chage. @@ -427,12 +436,12 @@ def set_warndays(name, warndays, root=None): .. code-block:: bash salt '*' shadow.set_warndays username 7 - ''' - return _set_attrib(name, 'warn', warndays, '-W', root=root) + """ + return _set_attrib(name, "warn", warndays, "-W", root=root) def set_date(name, date, root=None): - ''' + """ Sets the value for the date the password was last changed to days since the epoch (January 1, 1970). See man chage. @@ -450,12 +459,12 @@ def set_date(name, date, root=None): .. code-block:: bash salt '*' shadow.set_date username 0 - ''' - return _set_attrib(name, 'lstchg', date, '-d', root=root, validate=False) + """ + return _set_attrib(name, "lstchg", date, "-d", root=root, validate=False) def set_expire(name, expire, root=None): - ''' + """ .. versionchanged:: 2014.7.0 Sets the value for the date the account expires as days since the epoch @@ -476,12 +485,12 @@ def set_expire(name, expire, root=None): .. code-block:: bash salt '*' shadow.set_expire username -1 - ''' - return _set_attrib(name, 'expire', expire, '-E', root=root, validate=False) + """ + return _set_attrib(name, "expire", expire, "-E", root=root, validate=False) def list_users(root=None): - ''' + """ .. versionadded:: 2018.3.0 Return a list of all shadow users @@ -494,26 +503,30 @@ def list_users(root=None): .. code-block:: bash salt '*' shadow.list_users - ''' + """ if root is not None: getspall = functools.partial(_getspall, root=root) else: getspall = functools.partial(spwd.getspall) - return sorted([user.sp_namp if hasattr(user, 'sp_namp') else user.sp_nam - for user in getspall()]) + return sorted( + [ + user.sp_namp if hasattr(user, "sp_namp") else user.sp_nam + for user in getspall() + ] + ) def _getspnam(name, root=None): - ''' + """ Alternative implementation for getspnam, that use only /etc/shadow - ''' - root = '/' if not root else root - passwd = os.path.join(root, 'etc/shadow') + """ + root = "/" if not root else root + passwd = os.path.join(root, "etc/shadow") with salt.utils.files.fopen(passwd) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - comps = line.strip().split(':') + comps = line.strip().split(":") if comps[0] == name: # Generate a getspnam compatible output for i in range(2, 9): @@ -523,15 +536,15 @@ def _getspnam(name, root=None): def _getspall(root=None): - ''' + """ Alternative implementation for getspnam, that use only /etc/shadow - ''' - root = '/' if not root else root - passwd = os.path.join(root, 'etc/shadow') + """ + root = "/" if not root else root + passwd = os.path.join(root, "etc/shadow") with salt.utils.files.fopen(passwd) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - comps = line.strip().split(':') + comps = line.strip().split(":") # Generate a getspall compatible output for i in range(2, 9): comps[i] = int(comps[i]) if comps[i] else -1 diff --git a/salt/modules/linux_sysctl.py b/salt/modules/linux_sysctl.py index 272f67dba86..306589510e7 100644 --- a/salt/modules/linux_sysctl.py +++ b/salt/modules/linux_sysctl.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Module for viewing and modifying sysctl parameters -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -10,35 +10,39 @@ import os import re import string +import salt.utils.data +import salt.utils.files +import salt.utils.stringutils +import salt.utils.systemd +from salt.exceptions import CommandExecutionError + # Import salt libs from salt.ext import six from salt.ext.six import string_types -from salt.exceptions import CommandExecutionError -import salt.utils.data -import salt.utils.files -import salt.utils.systemd -import salt.utils.stringutils log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'sysctl' +__virtualname__ = "sysctl" # TODO: Add unpersist() to remove either a sysctl or sysctl/value combo from # the config def __virtual__(): - ''' + """ Only run on Linux systems - ''' - if __grains__['kernel'] != 'Linux': - return (False, 'The linux_sysctl execution module cannot be loaded: only available on Linux systems.') + """ + if __grains__["kernel"] != "Linux": + return ( + False, + "The linux_sysctl execution module cannot be loaded: only available on Linux systems.", + ) return __virtualname__ def default_config(): - ''' + """ Linux hosts using systemd 207 or later ignore ``/etc/sysctl.conf`` and only load from ``/etc/sysctl.d/*.conf``. This function will do the proper checks and return a default config file which will be valid for the Minion. Hosts @@ -49,26 +53,28 @@ def default_config(): .. code-block:: bash salt -G 'kernel:Linux' sysctl.default_config - ''' - if salt.utils.systemd.booted(__context__) \ - and salt.utils.systemd.version(__context__) >= 207: - return '/etc/sysctl.d/99-salt.conf' - return '/etc/sysctl.conf' + """ + if ( + salt.utils.systemd.booted(__context__) + and salt.utils.systemd.version(__context__) >= 207 + ): + return "/etc/sysctl.d/99-salt.conf" + return "/etc/sysctl.conf" def show(config_file=False): - ''' + """ Return a list of sysctl parameters for this minion - config: Pull the data from the system configuration file - instead of the live data. + :param config_file: Pull data from the system configuration file + instead of the live kernel. CLI Example: .. code-block:: bash salt '*' sysctl.show - ''' + """ ret = {} if config_file: # If the file doesn't exist, return an empty list @@ -79,9 +85,9 @@ def show(config_file=False): with salt.utils.files.fopen(config_file) as fp_: for line in fp_: line = salt.utils.stringutils.to_str(line) - if not line.startswith('#') and '=' in line: + if not line.startswith("#") and "=" in line: # search if we have some '=' instead of ' = ' separators - SPLIT = ' = ' + SPLIT = " = " if SPLIT not in line: SPLIT = SPLIT.strip() key, value = line.split(SPLIT, 1) @@ -89,93 +95,114 @@ def show(config_file=False): value = value.lstrip() ret[key] = value except (OSError, IOError): - log.error('Could not open sysctl file') + log.error("Could not open sysctl file") return None else: - cmd = 'sysctl -a' - out = __salt__['cmd.run_stdout'](cmd, output_loglevel='trace') + cmd = "sysctl -a" + out = __salt__["cmd.run_stdout"](cmd, output_loglevel="trace") for line in out.splitlines(): - if not line or ' = ' not in line: + if not line or " = " not in line: continue - comps = line.split(' = ', 1) + comps = line.split(" = ", 1) ret[comps[0]] = comps[1] return ret -def get(name): - ''' +def get(name, ignore=False): + """ Return a single sysctl parameter for this minion + :param name: Name of sysctl setting + :param ignore: Optional boolean to pass --ignore to sysctl (Default: False) + CLI Example: .. code-block:: bash salt '*' sysctl.get net.ipv4.ip_forward - ''' - cmd = 'sysctl -n {0}'.format(name) - out = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = "sysctl -n {0}".format(name) + if ignore: + cmd += " --ignore" + out = __salt__["cmd.run"](cmd, python_shell=False) return out -def assign(name, value): - ''' +def assign(name, value, ignore=False): + """ Assign a single sysctl parameter for this minion + :param name: Name of sysctl setting + :param value: Desired value of sysctl setting + :param ignore: Optional boolean to pass --ignore to sysctl (Default: False) + CLI Example: .. code-block:: bash salt '*' sysctl.assign net.ipv4.ip_forward 1 - ''' + """ value = six.text_type(value) if six.PY3: - tran_tab = name.translate(''.maketrans('./', '/.')) + tran_tab = name.translate("".maketrans("./", "/.")) else: - if isinstance(name, unicode): # pylint: disable=incompatible-py3-code,undefined-variable - trans_args = {ord('/'): u'.', ord('.'): u'/'} + # pylint: disable=incompatible-py3-code,undefined-variable + if isinstance(name, unicode): # pylint: disable=E0602 + trans_args = {ord("/"): ".", ord("."): "/"} else: - trans_args = string.maketrans('./', '/.') + trans_args = string.maketrans("./", "/.") + # pylint: enable=incompatible-py3-code,undefined-variable tran_tab = name.translate(trans_args) - sysctl_file = '/proc/sys/{0}'.format(tran_tab) - if not os.path.exists(sysctl_file): - raise CommandExecutionError('sysctl {0} does not exist'.format(name)) + sysctl_file = "/proc/sys/{0}".format(tran_tab) + if not ignore and not os.path.exists(sysctl_file): + raise CommandExecutionError("sysctl {0} does not exist".format(name)) ret = {} cmd = 'sysctl -w {0}="{1}"'.format(name, value) - data = __salt__['cmd.run_all'](cmd, python_shell=False) - out = data['stdout'] - err = data['stderr'] + if ignore: + cmd += " --ignore" + data = __salt__["cmd.run_all"](cmd, python_shell=False) + out = data["stdout"] + err = data["stderr"] # Example: # # sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216" # net.ipv4.tcp_rmem = 4096 87380 16777216 - regex = re.compile(r'^{0}\s+=\s+{1}$'.format(re.escape(name), re.escape(value))) + regex = re.compile(r"^{0}\s+=\s+{1}$".format(re.escape(name), re.escape(value))) - if not regex.match(out) or 'Invalid argument' in six.text_type(err): - if data['retcode'] != 0 and err: + if not regex.match(out) or "Invalid argument" in six.text_type(err): + if data["retcode"] != 0 and err: error = err + elif ignore: + ret[name] = "ignored" + return ret else: error = out - raise CommandExecutionError('sysctl -w failed: {0}'.format(error)) - new_name, new_value = out.split(' = ', 1) + raise CommandExecutionError("sysctl -w failed: {0}".format(error)) + new_name, new_value = out.split(" = ", 1) ret[new_name] = new_value return ret -def persist(name, value, config=None): - ''' +def persist(name, value, config=None, ignore=False): + """ Assign and persist a simple sysctl parameter for this minion. If ``config`` is not specified, a sensible default will be chosen using :mod:`sysctl.default_config `. + :param name: Name of sysctl setting + :param value: Desired value of sysctl setting + :param config: Optional path to sysctl.conf + :param ignore: Optional boolean to pass --ignore to sysctl (Default: False) + CLI Example: .. code-block:: bash salt '*' sysctl.persist net.ipv4.ip_forward 1 - ''' + """ if config is None: config = default_config() edited = False @@ -185,45 +212,45 @@ def persist(name, value, config=None): if not os.path.exists(sysctl_dir): os.makedirs(sysctl_dir) try: - with salt.utils.files.fopen(config, 'w+') as _fh: - _fh.write('#\n# Kernel sysctl configuration\n#\n') + with salt.utils.files.fopen(config, "w+") as _fh: + _fh.write("#\n# Kernel sysctl configuration\n#\n") except (IOError, OSError): - msg = 'Could not write to file: {0}' + msg = "Could not write to file: {0}" raise CommandExecutionError(msg.format(config)) # Read the existing sysctl.conf nlines = [] try: - with salt.utils.files.fopen(config, 'r') as _fh: + with salt.utils.files.fopen(config, "r") as _fh: # Use readlines because this should be a small file # and it seems unnecessary to indent the below for # loop since it is a fairly large block of code. config_data = salt.utils.data.decode(_fh.readlines()) except (IOError, OSError): - msg = 'Could not read from file: {0}' + msg = "Could not read from file: {0}" raise CommandExecutionError(msg.format(config)) for line in config_data: - if line.startswith('#'): + if line.startswith("#"): nlines.append(line) continue - if '=' not in line: + if "=" not in line: nlines.append(line) continue # Strip trailing whitespace and split the k,v - comps = [i.strip() for i in line.split('=', 1)] + comps = [i.strip() for i in line.split("=", 1)] # On Linux procfs, files such as /proc/sys/net/ipv4/tcp_rmem or any # other sysctl with whitespace in it consistently uses 1 tab. Lets # allow our users to put a space or tab between multi-value sysctls # and have salt not try to set it every single time. - if isinstance(comps[1], string_types) and ' ' in comps[1]: - comps[1] = re.sub(r'\s+', '\t', comps[1]) + if isinstance(comps[1], string_types) and " " in comps[1]: + comps[1] = re.sub(r"\s+", "\t", comps[1]) # Do the same thing for the value 'just in case' - if isinstance(value, string_types) and ' ' in value: - value = re.sub(r'\s+', '\t', value) + if isinstance(value, string_types) and " " in value: + value = re.sub(r"\s+", "\t", value) if len(comps) < 2: nlines.append(line) @@ -232,25 +259,28 @@ def persist(name, value, config=None): # This is the line to edit if six.text_type(comps[1]) == six.text_type(value): # It is correct in the config, check if it is correct in /proc - if six.text_type(get(name)) != six.text_type(value): - assign(name, value) - return 'Updated' + current_setting = get(name, ignore) + if not current_setting: + return "Ignored" + if six.text_type(current_setting) != six.text_type(value): + assign(name, value, ignore) + return "Updated" else: - return 'Already set' + return "Already set" - nlines.append('{0} = {1}\n'.format(name, value)) + nlines.append("{0} = {1}\n".format(name, value)) edited = True continue else: nlines.append(line) if not edited: - nlines.append('{0} = {1}\n'.format(name, value)) + nlines.append("{0} = {1}\n".format(name, value)) try: - with salt.utils.files.fopen(config, 'wb') as _fh: + with salt.utils.files.fopen(config, "wb") as _fh: _fh.writelines(salt.utils.data.encode(nlines)) except (IOError, OSError): - msg = 'Could not write to file: {0}' + msg = "Could not write to file: {0}" raise CommandExecutionError(msg.format(config)) - assign(name, value) - return 'Updated' + assign(name, value, ignore) + return "Updated" diff --git a/salt/modules/localemod.py b/salt/modules/localemod.py index 5846bc17066..628f1e52efc 100644 --- a/salt/modules/localemod.py +++ b/salt/modules/localemod.py @@ -1,115 +1,133 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing locales on POSIX-like systems. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -import re import os - -try: - import dbus -except ImportError: - dbus = None +import re # Import Salt libs import salt.utils.locales import salt.utils.path import salt.utils.platform import salt.utils.systemd -from salt.ext import six from salt.exceptions import CommandExecutionError +from salt.ext import six + +try: + import dbus +except ImportError: + dbus = None + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'locale' +__virtualname__ = "locale" def __virtual__(): - ''' + """ Exclude Windows OS. - ''' + """ if salt.utils.platform.is_windows(): - return False, 'Cannot load locale module: windows platforms are unsupported' + return False, "Cannot load locale module: windows platforms are unsupported" return __virtualname__ def _parse_dbus_locale(): - ''' + """ Get the 'System Locale' parameters from dbus - ''' + """ bus = dbus.SystemBus() - localed = bus.get_object('org.freedesktop.locale1', - '/org/freedesktop/locale1') - properties = dbus.Interface(localed, 'org.freedesktop.DBus.Properties') - system_locale = properties.Get('org.freedesktop.locale1', 'Locale') + localed = bus.get_object("org.freedesktop.locale1", "/org/freedesktop/locale1") + properties = dbus.Interface(localed, "org.freedesktop.DBus.Properties") + system_locale = properties.Get("org.freedesktop.locale1", "Locale") ret = {} for env_var in system_locale: env_var = six.text_type(env_var) - match = re.match(r'^([A-Z_]+)=(.*)$', env_var) + match = re.match(r"^([A-Z_]+)=(.*)$", env_var) if match: - ret[match.group(1)] = match.group(2).replace('"', '') + ret[match.group(1)] = match.group(2).replace('"', "") else: - log.error('Odd locale parameter "%s" detected in dbus locale ' - 'output. This should not happen. You should ' - 'probably investigate what caused this.', env_var) + log.error( + 'Odd locale parameter "%s" detected in dbus locale ' + "output. This should not happen. You should " + "probably investigate what caused this.", + env_var, + ) return ret def _localectl_status(): - ''' + """ Parse localectl status into a dict. :return: dict - ''' - if salt.utils.path.which('localectl') is None: + """ + if salt.utils.path.which("localectl") is None: raise CommandExecutionError('Unable to find "localectl"') ret = {} - locale_ctl_out = (__salt__['cmd.run']('localectl status') or '').strip() + locale_ctl_out = (__salt__["cmd.run"]("localectl status") or "").strip() ctl_key = None for line in locale_ctl_out.splitlines(): - if ': ' in line: # Keys are separate with ":" and a space (!). - ctl_key, ctl_data = line.split(': ') - ctl_key = ctl_key.strip().lower().replace(' ', '_') + if ": " in line: # Keys are separate with ":" and a space (!). + ctl_key, ctl_data = line.split(": ") + ctl_key = ctl_key.strip().lower().replace(" ", "_") else: ctl_data = line.strip() if not ctl_data: continue if ctl_key: - if '=' in ctl_data: - loc_set = ctl_data.split('=') + if "=" in ctl_data: + loc_set = ctl_data.split("=") if len(loc_set) == 2: if ctl_key not in ret: ret[ctl_key] = {} ret[ctl_key][loc_set[0]] = loc_set[1] else: - ret[ctl_key] = {'data': None if ctl_data == 'n/a' else ctl_data} + ret[ctl_key] = {"data": None if ctl_data == "n/a" else ctl_data} if not ret: - log.debug("Unable to find any locale information inside the following data:\n%s", locale_ctl_out) + log.debug( + "Unable to find any locale information inside the following data:\n%s", + locale_ctl_out, + ) raise CommandExecutionError('Unable to parse result of "localectl"') return ret -def _localectl_set(locale=''): - ''' +def _localectl_set(locale=""): + """ Use systemd's localectl command to set the LANG locale parameter, making sure not to trample on other params that have been set. - ''' - locale_params = _parse_dbus_locale() if dbus is not None else _localectl_status().get('system_locale', {}) - locale_params['LANG'] = six.text_type(locale) - args = ' '.join(['{0}="{1}"'.format(k, v) for k, v in six.iteritems(locale_params) if v is not None]) - return not __salt__['cmd.retcode']('localectl set-locale {0}'.format(args), python_shell=False) + """ + locale_params = ( + _parse_dbus_locale() + if dbus is not None + else _localectl_status().get("system_locale", {}) + ) + locale_params["LANG"] = six.text_type(locale) + args = " ".join( + [ + '{0}="{1}"'.format(k, v) + for k, v in six.iteritems(locale_params) + if v is not None + ] + ) + return not __salt__["cmd.retcode"]( + "localectl set-locale {0}".format(args), python_shell=False + ) def list_avail(): - ''' + """ Lists available (compiled) locales CLI Example: @@ -117,12 +135,12 @@ def list_avail(): .. code-block:: bash salt '*' locale.list_avail - ''' - return __salt__['cmd.run']('locale -a').split('\n') + """ + return __salt__["cmd.run"]("locale -a").split("\n") def get_locale(): - ''' + """ Get the current system locale CLI Example: @@ -130,33 +148,41 @@ def get_locale(): .. code-block:: bash salt '*' locale.get_locale - ''' - ret = '' + """ + ret = "" lc_ctl = salt.utils.systemd.booted(__context__) # localectl on SLE12 is installed but the integration is still broken in latest SP3 due to # config is rewritten by by many %post installation hooks in the older packages. # If you use it -- you will break your config. This is not the case in SLE15 anymore. - if lc_ctl and not (__grains__['os_family'] in ['Suse'] and __grains__['osmajorrelease'] in [12]): - ret = (_parse_dbus_locale() if dbus is not None else _localectl_status()['system_locale']).get('LANG', '') + if lc_ctl and not ( + __grains__["os_family"] in ["Suse"] and __grains__["osmajorrelease"] in [12] + ): + ret = ( + _parse_dbus_locale() + if dbus is not None + else _localectl_status()["system_locale"] + ).get("LANG", "") else: - if 'Suse' in __grains__['os_family']: + if "Suse" in __grains__["os_family"]: cmd = 'grep "^RC_LANG" /etc/sysconfig/language' - elif 'RedHat' in __grains__['os_family']: + elif "RedHat" in __grains__["os_family"]: cmd = 'grep "^LANG=" /etc/sysconfig/i18n' - elif 'Debian' in __grains__['os_family']: + elif "Debian" in __grains__["os_family"]: # this block only applies to Debian without systemd cmd = 'grep "^LANG=" /etc/default/locale' - elif 'Gentoo' in __grains__['os_family']: - cmd = 'eselect --brief locale show' - return __salt__['cmd.run'](cmd).strip() - elif 'Solaris' in __grains__['os_family']: + elif "Gentoo" in __grains__["os_family"]: + cmd = "eselect --brief locale show" + return __salt__["cmd.run"](cmd).strip() + elif "Solaris" in __grains__["os_family"]: cmd = 'grep "^LANG=" /etc/default/init' else: # don't waste time on a failing cmd.run - raise CommandExecutionError('Error: "{0}" is unsupported!'.format(__grains__['oscodename'])) + raise CommandExecutionError( + 'Error: "{0}" is unsupported!'.format(__grains__["oscodename"]) + ) if cmd: try: - ret = __salt__['cmd.run'](cmd).split('=')[1].replace('"', '') + ret = __salt__["cmd.run"](cmd).split("=")[1].replace('"', "") except IndexError as err: log.error('Error occurred while running "%s": %s', cmd, err) @@ -164,7 +190,7 @@ def get_locale(): def set_locale(locale): - ''' + """ Sets the current system locale CLI Example: @@ -172,64 +198,67 @@ def set_locale(locale): .. code-block:: bash salt '*' locale.set_locale 'en_US.UTF-8' - ''' + """ lc_ctl = salt.utils.systemd.booted(__context__) # localectl on SLE12 is installed but the integration is broken -- config is rewritten by YaST2 - if lc_ctl and not (__grains__['os_family'] in ['Suse'] and __grains__['osmajorrelease'] in [12]): + if lc_ctl and not ( + __grains__["os_family"] in ["Suse"] and __grains__["osmajorrelease"] in [12] + ): return _localectl_set(locale) - if 'Suse' in __grains__['os_family']: + if "Suse" in __grains__["os_family"]: # this block applies to all SUSE systems - also with systemd - if not __salt__['file.file_exists']('/etc/sysconfig/language'): - __salt__['file.touch']('/etc/sysconfig/language') - __salt__['file.replace']( - '/etc/sysconfig/language', - '^RC_LANG=.*', + if not __salt__["file.file_exists"]("/etc/sysconfig/language"): + __salt__["file.touch"]("/etc/sysconfig/language") + __salt__["file.replace"]( + "/etc/sysconfig/language", + "^RC_LANG=.*", 'RC_LANG="{0}"'.format(locale), - append_if_not_found=True + append_if_not_found=True, ) - elif 'RedHat' in __grains__['os_family']: - if not __salt__['file.file_exists']('/etc/sysconfig/i18n'): - __salt__['file.touch']('/etc/sysconfig/i18n') - __salt__['file.replace']( - '/etc/sysconfig/i18n', - '^LANG=.*', + elif "RedHat" in __grains__["os_family"]: + if not __salt__["file.file_exists"]("/etc/sysconfig/i18n"): + __salt__["file.touch"]("/etc/sysconfig/i18n") + __salt__["file.replace"]( + "/etc/sysconfig/i18n", + "^LANG=.*", 'LANG="{0}"'.format(locale), - append_if_not_found=True + append_if_not_found=True, ) - elif 'Debian' in __grains__['os_family']: + elif "Debian" in __grains__["os_family"]: # this block only applies to Debian without systemd - update_locale = salt.utils.path.which('update-locale') + update_locale = salt.utils.path.which("update-locale") if update_locale is None: raise CommandExecutionError( - 'Cannot set locale: "update-locale" was not found.') - __salt__['cmd.run'](update_locale) # (re)generate /etc/default/locale - __salt__['file.replace']( - '/etc/default/locale', - '^LANG=.*', + 'Cannot set locale: "update-locale" was not found.' + ) + __salt__["cmd.run"](update_locale) # (re)generate /etc/default/locale + __salt__["file.replace"]( + "/etc/default/locale", + "^LANG=.*", 'LANG="{0}"'.format(locale), - append_if_not_found=True + append_if_not_found=True, ) - elif 'Gentoo' in __grains__['os_family']: - cmd = 'eselect --brief locale set {0}'.format(locale) - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 - elif 'Solaris' in __grains__['os_family']: - if locale not in __salt__['locale.list_avail'](): + elif "Gentoo" in __grains__["os_family"]: + cmd = "eselect --brief locale set {0}".format(locale) + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 + elif "Solaris" in __grains__["os_family"]: + if locale not in __salt__["locale.list_avail"](): return False - __salt__['file.replace']( - '/etc/default/init', - '^LANG=.*', + __salt__["file.replace"]( + "/etc/default/init", + "^LANG=.*", 'LANG="{0}"'.format(locale), - append_if_not_found=True + append_if_not_found=True, ) else: - raise CommandExecutionError('Error: Unsupported platform!') + raise CommandExecutionError("Error: Unsupported platform!") return True def avail(locale): - ''' + """ Check if a locale is available. .. versionadded:: 2014.7.0 @@ -239,20 +268,26 @@ def avail(locale): .. code-block:: bash salt '*' locale.avail 'en_US.UTF-8' - ''' + """ try: normalized_locale = salt.utils.locales.normalize_locale(locale) except IndexError: log.error('Unable to validate locale "%s"', locale) return False - avail_locales = __salt__['locale.list_avail']() - locale_exists = next((True for x in avail_locales - if salt.utils.locales.normalize_locale(x.strip()) == normalized_locale), False) + avail_locales = __salt__["locale.list_avail"]() + locale_exists = next( + ( + True + for x in avail_locales + if salt.utils.locales.normalize_locale(x.strip()) == normalized_locale + ), + False, + ) return locale_exists def gen_locale(locale, **kwargs): - ''' + """ Generate a locale. Options: .. versionadded:: 2014.7.0 @@ -271,87 +306,94 @@ def gen_locale(locale, **kwargs): salt '*' locale.gen_locale en_US.UTF-8 salt '*' locale.gen_locale 'en_IE.UTF-8 UTF-8' # Debian/Gentoo only - ''' - on_debian = __grains__.get('os') == 'Debian' - on_ubuntu = __grains__.get('os') == 'Ubuntu' - on_gentoo = __grains__.get('os_family') == 'Gentoo' - on_suse = __grains__.get('os_family') == 'Suse' - on_solaris = __grains__.get('os_family') == 'Solaris' + """ + on_debian = __grains__.get("os") == "Debian" + on_ubuntu = __grains__.get("os") == "Ubuntu" + on_gentoo = __grains__.get("os_family") == "Gentoo" + on_suse = __grains__.get("os_family") == "Suse" + on_solaris = __grains__.get("os_family") == "Solaris" if on_solaris: # all locales are pre-generated - return locale in __salt__['locale.list_avail']() + return locale in __salt__["locale.list_avail"]() locale_info = salt.utils.locales.split_locale(locale) - locale_search_str = '{0}_{1}'.format(locale_info['language'], locale_info['territory']) + locale_search_str = "{0}_{1}".format( + locale_info["language"], locale_info["territory"] + ) # if the charmap has not been supplied, normalize by appening it - if not locale_info['charmap'] and not on_ubuntu: - locale_info['charmap'] = locale_info['codeset'] + if not locale_info["charmap"] and not on_ubuntu: + locale_info["charmap"] = locale_info["codeset"] locale = salt.utils.locales.join_locale(locale_info) if on_debian or on_gentoo: # file-based search - search = '/usr/share/i18n/SUPPORTED' - valid = __salt__['file.search'](search, - '^{0}$'.format(locale), - flags=re.MULTILINE) + search = "/usr/share/i18n/SUPPORTED" + valid = __salt__["file.search"]( + search, "^{0}$".format(locale), flags=re.MULTILINE + ) else: # directory-based search if on_suse: - search = '/usr/share/locale' + search = "/usr/share/locale" else: - search = '/usr/share/i18n/locales' + search = "/usr/share/i18n/locales" try: valid = locale_search_str in os.listdir(search) except OSError as ex: log.error(ex) - raise CommandExecutionError( - "Locale \"{0}\" is not available.".format(locale)) + raise CommandExecutionError('Locale "{0}" is not available.'.format(locale)) if not valid: - log.error( - 'The provided locale "%s" is not found in %s', locale, search) + log.error('The provided locale "%s" is not found in %s', locale, search) return False - if os.path.exists('/etc/locale.gen'): - __salt__['file.replace']( - '/etc/locale.gen', - r'^\s*#\s*{0}\s*$'.format(locale), - '{0}\n'.format(locale), - append_if_not_found=True + if os.path.exists("/etc/locale.gen"): + __salt__["file.replace"]( + "/etc/locale.gen", + r"^\s*#\s*{0}\s*$".format(locale), + "{0}\n".format(locale), + append_if_not_found=True, ) elif on_ubuntu: - __salt__['file.touch']( - '/var/lib/locales/supported.d/{0}'.format(locale_info['language']) + __salt__["file.touch"]( + "/var/lib/locales/supported.d/{0}".format(locale_info["language"]) ) - __salt__['file.replace']( - '/var/lib/locales/supported.d/{0}'.format(locale_info['language']), + __salt__["file.replace"]( + "/var/lib/locales/supported.d/{0}".format(locale_info["language"]), locale, locale, - append_if_not_found=True + append_if_not_found=True, ) - if salt.utils.path.which('locale-gen'): - cmd = ['locale-gen'] + if salt.utils.path.which("locale-gen"): + cmd = ["locale-gen"] if on_gentoo: - cmd.append('--generate') + cmd.append("--generate") if on_ubuntu: cmd.append(salt.utils.locales.normalize_locale(locale)) else: cmd.append(locale) - elif salt.utils.path.which('localedef'): - cmd = ['localedef', '--force', '-i', locale_search_str, '-f', locale_info['codeset'], - '{0}.{1}'.format(locale_search_str, - locale_info['codeset']), - kwargs.get('verbose', False) and '--verbose' or '--quiet'] + elif salt.utils.path.which("localedef"): + cmd = [ + "localedef", + "--force", + "-i", + locale_search_str, + "-f", + locale_info["codeset"], + "{0}.{1}".format(locale_search_str, locale_info["codeset"]), + kwargs.get("verbose", False) and "--verbose" or "--quiet", + ] else: raise CommandExecutionError( - 'Command "locale-gen" or "localedef" was not found on this system.') + 'Command "locale-gen" or "localedef" was not found on this system.' + ) - res = __salt__['cmd.run_all'](cmd) - if res['retcode']: - log.error(res['stderr']) + res = __salt__["cmd.run_all"](cmd) + if res["retcode"]: + log.error(res["stderr"]) - if kwargs.get('verbose'): + if kwargs.get("verbose"): return res else: - return res['retcode'] == 0 + return res["retcode"] == 0 diff --git a/salt/modules/locate.py b/salt/modules/locate.py index a334903a212..71082846822 100644 --- a/salt/modules/locate.py +++ b/salt/modules/locate.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Module for using the locate utilities -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -14,20 +14,20 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ if salt.utils.platform.is_windows(): return ( False, - 'The locate execution module cannot be loaded: only available on ' - 'non-Windows systems.' + "The locate execution module cannot be loaded: only available on " + "non-Windows systems.", ) return True def version(): - ''' + """ Returns the version of locate CLI Example: @@ -35,14 +35,14 @@ def version(): .. code-block:: bash salt '*' locate.version - ''' - cmd = 'locate -V' - out = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "locate -V" + out = __salt__["cmd.run"](cmd).splitlines() return out def stats(): - ''' + """ Returns statistics about the locate database CLI Example: @@ -50,21 +50,21 @@ def stats(): .. code-block:: bash salt '*' locate.stats - ''' + """ ret = {} - cmd = 'locate -S' - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "locate -S" + out = __salt__["cmd.run"](cmd).splitlines() for line in out: comps = line.strip().split() - if line.startswith('Database'): - ret['database'] = comps[1].replace(':', '') + if line.startswith("Database"): + ret["database"] = comps[1].replace(":", "") continue - ret[' '.join(comps[1:])] = comps[0] + ret[" ".join(comps[1:])] = comps[0] return ret def updatedb(): - ''' + """ Updates the locate database CLI Example: @@ -72,14 +72,14 @@ def updatedb(): .. code-block:: bash salt '*' locate.updatedb - ''' - cmd = 'updatedb' - out = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "updatedb" + out = __salt__["cmd.run"](cmd).splitlines() return out -def locate(pattern, database='', limit=0, **kwargs): - ''' +def locate(pattern, database="", limit=0, **kwargs): + """ Performs a file lookup. Valid options (and their defaults) are:: basename=False @@ -100,28 +100,28 @@ def locate(pattern, database='', limit=0, **kwargs): .. code-block:: bash salt '*' locate.locate - ''' - options = '' + """ + options = "" toggles = { - 'basename': 'b', - 'count': 'c', - 'existing': 'e', - 'follow': 'L', - 'ignore': 'i', - 'nofollow': 'P', - 'wholename': 'w', - } + "basename": "b", + "count": "c", + "existing": "e", + "follow": "L", + "ignore": "i", + "nofollow": "P", + "wholename": "w", + } for option in kwargs: if bool(kwargs[option]) is True and option in toggles: options += toggles[option] if options: - options = '-{0}'.format(options) + options = "-{0}".format(options) if database: - options += ' -d {0}'.format(database) + options += " -d {0}".format(database) if limit > 0: - options += ' -l {0}'.format(limit) - if 'regex' in kwargs and bool(kwargs['regex']) is True: - options += ' --regex' - cmd = 'locate {0} {1}'.format(options, pattern) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + options += " -l {0}".format(limit) + if "regex" in kwargs and bool(kwargs["regex"]) is True: + options += " --regex" + cmd = "locate {0} {1}".format(options, pattern) + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() return out diff --git a/salt/modules/logadm.py b/salt/modules/logadm.py index dc8036a199f..254d4a87447 100644 --- a/salt/modules/logadm.py +++ b/salt/modules/logadm.py @@ -1,141 +1,151 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing Solaris logadm based log rotations. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging import shlex -try: - from shlex import quote as _quote_args # pylint: disable=E0611 -except ImportError: - from pipes import quote as _quote_args -# Import salt libs -from salt.ext import six import salt.utils.args import salt.utils.decorators as decorators import salt.utils.files import salt.utils.stringutils +# Import salt libs +from salt.ext import six + +try: + from shlex import quote as _quote_args # pylint: disable=E0611 +except ImportError: + from pipes import quote as _quote_args + + log = logging.getLogger(__name__) -default_conf = '/etc/logadm.conf' +default_conf = "/etc/logadm.conf" option_toggles = { - '-c': 'copy', - '-l': 'localtime', - '-N': 'skip_missing', + "-c": "copy", + "-l": "localtime", + "-N": "skip_missing", } option_flags = { - '-A': 'age', - '-C': 'count', - '-a': 'post_command', - '-b': 'pre_command', - '-e': 'mail_addr', - '-E': 'expire_command', - '-g': 'group', - '-m': 'mode', - '-M': 'rename_command', - '-o': 'owner', - '-p': 'period', - '-P': 'timestmp', - '-R': 'old_created_command', - '-s': 'size', - '-S': 'max_size', - '-t': 'template', - '-T': 'old_pattern', - '-w': 'entryname', - '-z': 'compress_count', + "-A": "age", + "-C": "count", + "-a": "post_command", + "-b": "pre_command", + "-e": "mail_addr", + "-E": "expire_command", + "-g": "group", + "-m": "mode", + "-M": "rename_command", + "-o": "owner", + "-p": "period", + "-P": "timestmp", + "-R": "old_created_command", + "-s": "size", + "-S": "max_size", + "-t": "template", + "-T": "old_pattern", + "-w": "entryname", + "-z": "compress_count", } def __virtual__(): - ''' + """ Only work on Solaris based systems - ''' - if 'Solaris' in __grains__['os_family']: + """ + if "Solaris" in __grains__["os_family"]: return True - return (False, 'The logadm execution module cannot be loaded: only available on Solaris.') + return ( + False, + "The logadm execution module cannot be loaded: only available on Solaris.", + ) def _arg2opt(arg): - ''' + """ Turn a pass argument into the correct option - ''' + """ res = [o for o, a in option_toggles.items() if a == arg] res += [o for o, a in option_flags.items() if a == arg] return res[0] if res else None def _parse_conf(conf_file=default_conf): - ''' + """ Parse a logadm configuration file. - ''' + """ ret = {} - with salt.utils.files.fopen(conf_file, 'r') as ifile: + with salt.utils.files.fopen(conf_file, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line).strip() if not line: continue - if line.startswith('#'): + if line.startswith("#"): continue - splitline = line.split(' ', 1) + splitline = line.split(" ", 1) ret[splitline[0]] = splitline[1] return ret def _parse_options(entry, options, include_unset=True): - ''' + """ Parse a logadm options string - ''' + """ log_cfg = {} options = shlex.split(options) if len(options) == 0: return None ## identifier is entry or log? - if entry.startswith('/'): - log_cfg['log_file'] = entry + if entry.startswith("/"): + log_cfg["log_file"] = entry else: - log_cfg['entryname'] = entry + log_cfg["entryname"] = entry ## parse options # NOTE: we loop over the options because values may exist multiple times index = 0 while index < len(options): # log file - if index in [0, (len(options)-1)] and options[index].startswith('/'): - log_cfg['log_file'] = options[index] + if index in [0, (len(options) - 1)] and options[index].startswith("/"): + log_cfg["log_file"] = options[index] # check if toggle option elif options[index] in option_toggles: log_cfg[option_toggles[options[index]]] = True # check if flag option - elif options[index] in option_flags and (index+1) <= len(options): - log_cfg[option_flags[options[index]]] = int(options[index+1]) if options[index+1].isdigit() else options[index+1] + elif options[index] in option_flags and (index + 1) <= len(options): + log_cfg[option_flags[options[index]]] = ( + int(options[index + 1]) + if options[index + 1].isdigit() + else options[index + 1] + ) index += 1 # unknown options else: - if 'additional_options' not in log_cfg: - log_cfg['additional_options'] = [] - if ' ' in options[index]: - log_cfg['dditional_options'] = "'{}'".format(options[index]) + if "additional_options" not in log_cfg: + log_cfg["additional_options"] = [] + if " " in options[index]: + log_cfg["dditional_options"] = "'{}'".format(options[index]) else: - log_cfg['additional_options'].append(options[index]) + log_cfg["additional_options"].append(options[index]) index += 1 ## turn additional_options into string - if 'additional_options' in log_cfg: - log_cfg['additional_options'] = " ".join(log_cfg['additional_options']) + if "additional_options" in log_cfg: + log_cfg["additional_options"] = " ".join(log_cfg["additional_options"]) ## ensure we have a log_file # NOTE: logadm assumes logname is a file if no log_file is given - if 'log_file' not in log_cfg and 'entryname' in log_cfg: - log_cfg['log_file'] = log_cfg['entryname'] - del log_cfg['entryname'] + if "log_file" not in log_cfg and "entryname" in log_cfg: + log_cfg["log_file"] = log_cfg["entryname"] + del log_cfg["entryname"] ## include unset if include_unset: @@ -153,7 +163,7 @@ def _parse_options(entry, options, include_unset=True): def show_conf(conf_file=default_conf, name=None): - ''' + """ Show configuration conf_file : string @@ -167,20 +177,20 @@ def show_conf(conf_file=default_conf, name=None): salt '*' logadm.show_conf salt '*' logadm.show_conf name=/var/log/syslog - ''' + """ cfg = _parse_conf(conf_file) # filter if name and name in cfg: return {name: cfg[name]} elif name: - return {name: 'not found in {}'.format(conf_file)} + return {name: "not found in {}".format(conf_file)} else: return cfg def list_conf(conf_file=default_conf, log_file=None, include_unset=False): - ''' + """ Show parsed configuration .. versionadded:: 2018.3.0 @@ -199,27 +209,29 @@ def list_conf(conf_file=default_conf, log_file=None, include_unset=False): salt '*' logadm.list_conf salt '*' logadm.list_conf log=/var/log/syslog salt '*' logadm.list_conf include_unset=False - ''' + """ cfg = _parse_conf(conf_file) cfg_parsed = {} ## parse all options for entry in cfg: log_cfg = _parse_options(entry, cfg[entry], include_unset) - cfg_parsed[log_cfg['log_file'] if 'log_file' in log_cfg else log_cfg['entryname']] = log_cfg + cfg_parsed[ + log_cfg["log_file"] if "log_file" in log_cfg else log_cfg["entryname"] + ] = log_cfg ## filter if log_file and log_file in cfg_parsed: return {log_file: cfg_parsed[log_file]} elif log_file: - return {log_file: 'not found in {}'.format(conf_file)} + return {log_file: "not found in {}".format(conf_file)} else: return cfg_parsed @decorators.memoize def show_args(): - ''' + """ Show which arguments map to which flags and options. .. versionadded:: 2018.3.0 @@ -229,18 +241,18 @@ def show_args(): .. code-block:: bash salt '*' logadm.show_args - ''' - mapping = {'flags': {}, 'options': {}} + """ + mapping = {"flags": {}, "options": {}} for flag, arg in option_toggles.items(): - mapping['flags'][flag] = arg + mapping["flags"][flag] = arg for option, arg in option_flags.items(): - mapping['options'][option] = arg + mapping["options"][option] = arg return mapping def rotate(name, pattern=None, conf_file=default_conf, **kwargs): - ''' + """ Set up pattern for logging. name : string @@ -268,40 +280,35 @@ def rotate(name, pattern=None, conf_file=default_conf, **kwargs): salt '*' logadm.rotate myapplog pattern='/var/log/myapp/*.log' count=7 salt '*' logadm.rotate myapplog log_file='/var/log/myapp/*.log' count=4 owner=myappd mode='0700' - ''' + """ ## cleanup kwargs kwargs = salt.utils.args.clean_kwargs(**kwargs) ## inject name into kwargs - if 'entryname' not in kwargs and name and not name.startswith('/'): - kwargs['entryname'] = name + if "entryname" not in kwargs and name and not name.startswith("/"): + kwargs["entryname"] = name ## inject pattern into kwargs - if 'log_file' not in kwargs: - if pattern and pattern.startswith('/'): - kwargs['log_file'] = pattern + if "log_file" not in kwargs: + if pattern and pattern.startswith("/"): + kwargs["log_file"] = pattern # NOTE: for backwards compatibility check if name is a path - elif name and name.startswith('/'): - kwargs['log_file'] = name + elif name and name.startswith("/"): + kwargs["log_file"] = name ## build command log.debug("logadm.rotate - kwargs: %s", kwargs) command = "logadm -f {}".format(conf_file) for arg, val in kwargs.items(): if arg in option_toggles.values() and val: - command = "{} {}".format( - command, - _arg2opt(arg), - ) + command = "{} {}".format(command, _arg2opt(arg),) elif arg in option_flags.values(): command = "{} {} {}".format( - command, - _arg2opt(arg), - _quote_args(six.text_type(val)) + command, _arg2opt(arg), _quote_args(six.text_type(val)) ) - elif arg != 'log_file': + elif arg != "log_file": log.warning("Unknown argument %s, don't know how to map this!", arg) - if 'log_file' in kwargs: + if "log_file" in kwargs: # NOTE: except from ```man logadm``` # If no log file name is provided on a logadm command line, the entry # name is assumed to be the same as the log file name. For example, @@ -310,21 +317,21 @@ def rotate(name, pattern=None, conf_file=default_conf, **kwargs): # # % logadm -C2 -w mylog /my/really/long/log/file/name # % logadm -C2 -w /my/really/long/log/file/name - if 'entryname' not in kwargs: - command = "{} -w {}".format(command, _quote_args(kwargs['log_file'])) + if "entryname" not in kwargs: + command = "{} -w {}".format(command, _quote_args(kwargs["log_file"])) else: - command = "{} {}".format(command, _quote_args(kwargs['log_file'])) + command = "{} {}".format(command, _quote_args(kwargs["log_file"])) log.debug("logadm.rotate - command: %s", command) - result = __salt__['cmd.run_all'](command, python_shell=False) - if result['retcode'] != 0: - return dict(Error='Failed in adding log', Output=result['stderr']) + result = __salt__["cmd.run_all"](command, python_shell=False) + if result["retcode"] != 0: + return dict(Error="Failed in adding log", Output=result["stderr"]) - return dict(Result='Success') + return dict(Result="Success") def remove(name, conf_file=default_conf): - ''' + """ Remove log pattern from logadm CLI Example: @@ -332,12 +339,12 @@ def remove(name, conf_file=default_conf): .. code-block:: bash salt '*' logadm.remove myapplog - ''' + """ command = "logadm -f {0} -r {1}".format(conf_file, name) - result = __salt__['cmd.run_all'](command, python_shell=False) - if result['retcode'] != 0: + result = __salt__["cmd.run_all"](command, python_shell=False) + if result["retcode"] != 0: return dict( - Error='Failure in removing log. Possibly already removed?', - Output=result['stderr'] + Error="Failure in removing log. Possibly already removed?", + Output=result["stderr"], ) - return dict(Result='Success') + return dict(Result="Success") diff --git a/salt/modules/logmod.py b/salt/modules/logmod.py index f11175df231..991817c1566 100644 --- a/salt/modules/logmod.py +++ b/salt/modules/logmod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" On-demand logging ================= @@ -19,15 +19,16 @@ CLI Example: .. code-block:: bash salt '*' log.error "Please don't do that, this module is not for CLI use!" -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging + log = logging.getLogger(__name__) -__virtualname__ = 'log' -__proxyenabled__ = ['*'] +__virtualname__ = "log" +__proxyenabled__ = ["*"] def __virtual__(): @@ -35,48 +36,48 @@ def __virtual__(): def debug(message): - ''' + """ Log message at level DEBUG. - ''' + """ log.debug(message) return True def info(message): - ''' + """ Log message at level INFO. - ''' + """ log.info(message) return True def warning(message): - ''' + """ Log message at level WARNING. - ''' + """ log.warning(message) return True def error(message): - ''' + """ Log message at level ERROR. - ''' + """ log.error(message) return True def critical(message): - ''' + """ Log message at level CRITICAL. - ''' + """ log.critical(message) return True def exception(message): - ''' + """ Log message at level EXCEPTION. - ''' + """ log.exception(message) return True diff --git a/salt/modules/logrotate.py b/salt/modules/logrotate.py index 405ab31f99c..36d4e5f7cdd 100644 --- a/salt/modules/logrotate.py +++ b/salt/modules/logrotate.py @@ -1,52 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing logrotate. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging -# Import salt libs -from salt.ext import six import salt.utils.files import salt.utils.platform import salt.utils.stringutils from salt.exceptions import SaltInvocationError +# Import salt libs +from salt.ext import six + _LOG = logging.getLogger(__name__) -_DEFAULT_CONF = '/etc/logrotate.conf' +_DEFAULT_CONF = "/etc/logrotate.conf" # Define a function alias in order not to shadow built-in's -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ if salt.utils.platform.is_windows(): return ( False, - 'The logrotate execution module cannot be loaded: only available ' - 'on non-Windows systems.' + "The logrotate execution module cannot be loaded: only available " + "on non-Windows systems.", ) return True def _convert_if_int(value): - ''' + """ Convert to an int if necessary. :param str value: The value to check/convert. :return: The converted or passed value. :rtype: bool|int|str - ''' + """ try: value = int(six.text_type(value)) except ValueError: @@ -55,67 +55,67 @@ def _convert_if_int(value): def _parse_conf(conf_file=_DEFAULT_CONF): - ''' + """ Parse a logrotate configuration file. Includes will also be parsed, and their configuration will be stored in the return dict, as if they were part of the main config file. A dict of which configs came from which includes will be stored in the 'include files' dict inside the return dict, for later reference by the user or module. - ''' + """ ret = {} - mode = 'single' + mode = "single" multi_names = [] multi = {} prev_comps = None - with salt.utils.files.fopen(conf_file, 'r') as ifile: + with salt.utils.files.fopen(conf_file, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line).strip() if not line: continue - if line.startswith('#'): + if line.startswith("#"): continue comps = line.split() - if '{' in line and '}' not in line: - mode = 'multi' + if "{" in line and "}" not in line: + mode = "multi" if len(comps) == 1 and prev_comps: multi_names = prev_comps else: multi_names = comps multi_names.pop() continue - if '}' in line: - mode = 'single' + if "}" in line: + mode = "single" for multi_name in multi_names: ret[multi_name] = multi multi_names = [] multi = {} continue - if mode == 'single': + if mode == "single": key = ret else: key = multi - if comps[0] == 'include': - if 'include files' not in ret: - ret['include files'] = {} + if comps[0] == "include": + if "include files" not in ret: + ret["include files"] = {} for include in os.listdir(comps[1]): - if include not in ret['include files']: - ret['include files'][include] = [] + if include not in ret["include files"]: + ret["include files"][include] = [] include_path = os.path.join(comps[1], include) include_conf = _parse_conf(include_path) for file_key in include_conf: ret[file_key] = include_conf[file_key] - ret['include files'][include].append(file_key) + ret["include files"][include].append(file_key) prev_comps = comps if len(comps) > 2: - key[comps[0]] = ' '.join(comps[1:]) + key[comps[0]] = " ".join(comps[1:]) elif len(comps) > 1: key[comps[0]] = _convert_if_int(comps[1]) else: @@ -124,7 +124,7 @@ def _parse_conf(conf_file=_DEFAULT_CONF): def show_conf(conf_file=_DEFAULT_CONF): - ''' + """ Show parsed configuration :param str conf_file: The logrotate configuration file. @@ -137,12 +137,12 @@ def show_conf(conf_file=_DEFAULT_CONF): .. code-block:: bash salt '*' logrotate.show_conf - ''' + """ return _parse_conf(conf_file) def get(key, value=None, conf_file=_DEFAULT_CONF): - ''' + """ Get the value for a specific configuration line. :param str key: The command or stanza block to configure. @@ -159,7 +159,7 @@ def get(key, value=None, conf_file=_DEFAULT_CONF): salt '*' logrotate.get rotate salt '*' logrotate.get /var/log/wtmp rotate /etc/logrotate.conf - ''' + """ current_conf = _parse_conf(conf_file) stanza = current_conf.get(key, False) @@ -171,7 +171,7 @@ def get(key, value=None, conf_file=_DEFAULT_CONF): def set_(key, value, setting=None, conf_file=_DEFAULT_CONF): - ''' + """ Set a new value for a specific configuration line. :param str key: The command or block to configure. @@ -207,27 +207,29 @@ def set_(key, value, setting=None, conf_file=_DEFAULT_CONF): This module also has the ability to scan files inside an include directory, and make changes in the appropriate file. - ''' + """ conf = _parse_conf(conf_file) - for include in conf['include files']: - if key in conf['include files'][include]: - conf_file = os.path.join(conf['include'], include) + for include in conf["include files"]: + if key in conf["include files"][include]: + conf_file = os.path.join(conf["include"], include) new_line = six.text_type() kwargs = { - 'flags': 8, - 'backup': False, - 'path': conf_file, - 'pattern': '^{0}.*'.format(key), - 'show_changes': False + "flags": 8, + "backup": False, + "path": conf_file, + "pattern": "^{0}.*".format(key), + "show_changes": False, } if setting is None: current_value = conf.get(key, False) if isinstance(current_value, dict): - error_msg = ('Error: {0} includes a dict, and a specific setting inside the ' - 'dict was not declared').format(key) + error_msg = ( + "Error: {0} includes a dict, and a specific setting inside the " + "dict was not declared" + ).format(key) raise SaltInvocationError(error_msg) if value == current_value: @@ -238,15 +240,17 @@ def set_(key, value, setting=None, conf_file=_DEFAULT_CONF): if value is True: new_line = key elif value: - new_line = '{0} {1}'.format(key, value) + new_line = "{0} {1}".format(key, value) - kwargs.update({'prepend_if_not_found': True}) + kwargs.update({"prepend_if_not_found": True}) else: stanza = conf.get(key, dict()) if stanza and not isinstance(stanza, dict): - error_msg = ('Error: A setting for a dict was declared, but the ' - 'configuration line given is not a dict') + error_msg = ( + "Error: A setting for a dict was declared, but the " + "configuration line given is not a dict" + ) raise SaltInvocationError(error_msg) if setting == stanza.get(value, False): @@ -261,25 +265,27 @@ def set_(key, value, setting=None, conf_file=_DEFAULT_CONF): new_line = _dict_to_stanza(key, stanza) - kwargs.update({ - 'pattern': '^{0}.*?{{.*?}}'.format(key), - 'flags': 24, - 'append_if_not_found': True - }) + kwargs.update( + { + "pattern": "^{0}.*?{{.*?}}".format(key), + "flags": 24, + "append_if_not_found": True, + } + ) - kwargs.update({'repl': new_line}) + kwargs.update({"repl": new_line}) _LOG.debug("Setting file '%s' line: %s", conf_file, new_line) - return __salt__['file.replace'](**kwargs) + return __salt__["file.replace"](**kwargs) def _dict_to_stanza(key, stanza): - ''' + """ Convert a dict to a multi-line stanza - ''' - ret = '' + """ + ret = "" for skey in stanza: if stanza[skey] is True: - stanza[skey] = '' - ret += ' {0} {1}\n'.format(skey, stanza[skey]) - return '{0} {{\n{1}}}'.format(key, ret) + stanza[skey] = "" + ret += " {0} {1}\n".format(skey, stanza[skey]) + return "{0} {{\n{1}}}".format(key, ret) diff --git a/salt/modules/lvs.py b/salt/modules/lvs.py index 0d3baef8f8a..5b14825c96c 100644 --- a/salt/modules/lvs.py +++ b/salt/modules/lvs.py @@ -1,92 +1,96 @@ # -*- coding: utf-8 -*- -''' +""" Support for LVS (Linux Virtual Server) -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import python libs +import salt.utils.decorators as decorators # Import salt libs import salt.utils.path -import salt.utils.decorators as decorators from salt.exceptions import SaltException +# Import python libs -__func_alias__ = { - 'list_': 'list' -} + +__func_alias__ = {"list_": "list"} # Cache the output of running which('ipvsadm') @decorators.memoize def __detect_os(): - return salt.utils.path.which('ipvsadm') + return salt.utils.path.which("ipvsadm") def __virtual__(): - ''' + """ Only load if ipvsadm command exists on the system. - ''' + """ if not __detect_os(): - return (False, 'The lvs execution module cannot be loaded: the ipvsadm binary is not in the path.') + return ( + False, + "The lvs execution module cannot be loaded: the ipvsadm binary is not in the path.", + ) - return 'lvs' + return "lvs" def _build_cmd(**kwargs): - ''' + """ Build a well-formatted ipvsadm command based on kwargs. - ''' - cmd = '' + """ + cmd = "" - if 'service_address' in kwargs: - if kwargs['service_address']: - if 'protocol' in kwargs: - if kwargs['protocol'] == 'tcp': - cmd += ' -t {0}'.format(kwargs['service_address']) - elif kwargs['protocol'] == 'udp': - cmd += ' -u {0}'.format(kwargs['service_address']) - elif kwargs['protocol'] == 'fwmark': - cmd += ' -f {0}'.format(kwargs['service_address']) + if "service_address" in kwargs: + if kwargs["service_address"]: + if "protocol" in kwargs: + if kwargs["protocol"] == "tcp": + cmd += " -t {0}".format(kwargs["service_address"]) + elif kwargs["protocol"] == "udp": + cmd += " -u {0}".format(kwargs["service_address"]) + elif kwargs["protocol"] == "fwmark": + cmd += " -f {0}".format(kwargs["service_address"]) else: - raise SaltException('Error: Only support tcp, udp and fwmark service protocol') - del kwargs['protocol'] + raise SaltException( + "Error: Only support tcp, udp and fwmark service protocol" + ) + del kwargs["protocol"] else: - raise SaltException('Error: protocol should specified') - if 'scheduler' in kwargs: - if kwargs['scheduler']: - cmd += ' -s {0}'.format(kwargs['scheduler']) - del kwargs['scheduler'] + raise SaltException("Error: protocol should specified") + if "scheduler" in kwargs: + if kwargs["scheduler"]: + cmd += " -s {0}".format(kwargs["scheduler"]) + del kwargs["scheduler"] else: - raise SaltException('Error: service_address should specified') - del kwargs['service_address'] + raise SaltException("Error: service_address should specified") + del kwargs["service_address"] - if 'server_address' in kwargs: - if kwargs['server_address']: - cmd += ' -r {0}'.format(kwargs['server_address']) - if 'packet_forward_method' in kwargs and kwargs['packet_forward_method']: - if kwargs['packet_forward_method'] == 'dr': - cmd += ' -g' - elif kwargs['packet_forward_method'] == 'tunnel': - cmd += ' -i' - elif kwargs['packet_forward_method'] == 'nat': - cmd += ' -m' + if "server_address" in kwargs: + if kwargs["server_address"]: + cmd += " -r {0}".format(kwargs["server_address"]) + if "packet_forward_method" in kwargs and kwargs["packet_forward_method"]: + if kwargs["packet_forward_method"] == "dr": + cmd += " -g" + elif kwargs["packet_forward_method"] == "tunnel": + cmd += " -i" + elif kwargs["packet_forward_method"] == "nat": + cmd += " -m" else: - raise SaltException('Error: only support dr, tunnel and nat') - del kwargs['packet_forward_method'] - if 'weight' in kwargs and kwargs['weight']: - cmd += ' -w {0}'.format(kwargs['weight']) - del kwargs['weight'] + raise SaltException("Error: only support dr, tunnel and nat") + del kwargs["packet_forward_method"] + if "weight" in kwargs and kwargs["weight"]: + cmd += " -w {0}".format(kwargs["weight"]) + del kwargs["weight"] else: - raise SaltException('Error: server_address should specified') - del kwargs['server_address'] + raise SaltException("Error: server_address should specified") + del kwargs["server_address"] return cmd -def add_service(protocol=None, service_address=None, scheduler='wlc'): - ''' +def add_service(protocol=None, service_address=None, scheduler="wlc"): + """ Add a virtual service. protocol @@ -104,24 +108,26 @@ def add_service(protocol=None, service_address=None, scheduler='wlc'): .. code-block:: bash salt '*' lvs.add_service tcp 1.1.1.1:80 rr - ''' + """ - cmd = '{0} -A {1}'.format(__detect_os(), - _build_cmd(protocol=protocol, - service_address=service_address, - scheduler=scheduler)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} -A {1}".format( + __detect_os(), + _build_cmd( + protocol=protocol, service_address=service_address, scheduler=scheduler + ), + ) + out = __salt__["cmd.run_all"](cmd, python_shell=False) # A non-zero return code means fail - if out['retcode']: - ret = out['stderr'].strip() + if out["retcode"]: + ret = out["stderr"].strip() else: ret = True return ret def edit_service(protocol=None, service_address=None, scheduler=None): - ''' + """ Edit the virtual service. protocol @@ -139,24 +145,26 @@ def edit_service(protocol=None, service_address=None, scheduler=None): .. code-block:: bash salt '*' lvs.edit_service tcp 1.1.1.1:80 rr - ''' + """ - cmd = '{0} -E {1}'.format(__detect_os(), - _build_cmd(protocol=protocol, - service_address=service_address, - scheduler=scheduler)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} -E {1}".format( + __detect_os(), + _build_cmd( + protocol=protocol, service_address=service_address, scheduler=scheduler + ), + ) + out = __salt__["cmd.run_all"](cmd, python_shell=False) # A non-zero return code means fail - if out['retcode']: - ret = out['stderr'].strip() + if out["retcode"]: + ret = out["stderr"].strip() else: ret = True return ret def delete_service(protocol=None, service_address=None): - ''' + """ Delete the virtual service. @@ -172,23 +180,30 @@ def delete_service(protocol=None, service_address=None): .. code-block:: bash salt '*' lvs.delete_service tcp 1.1.1.1:80 - ''' + """ - cmd = '{0} -D {1}'.format(__detect_os(), - _build_cmd(protocol=protocol, - service_address=service_address)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} -D {1}".format( + __detect_os(), _build_cmd(protocol=protocol, service_address=service_address) + ) + out = __salt__["cmd.run_all"](cmd, python_shell=False) # A non-zero return code means fail - if out['retcode']: - ret = out['stderr'].strip() + if out["retcode"]: + ret = out["stderr"].strip() else: ret = True return ret -def add_server(protocol=None, service_address=None, server_address=None, packet_forward_method='dr', weight=1, **kwargs): - ''' +def add_server( + protocol=None, + service_address=None, + server_address=None, + packet_forward_method="dr", + weight=1, + **kwargs +): + """ Add a real server to a virtual service. @@ -213,27 +228,38 @@ def add_server(protocol=None, service_address=None, server_address=None, packet_ .. code-block:: bash salt '*' lvs.add_server tcp 1.1.1.1:80 192.168.0.11:8080 nat 1 - ''' + """ - cmd = '{0} -a {1}'.format(__detect_os(), - _build_cmd(protocol=protocol, - service_address=service_address, - server_address=server_address, - packet_forward_method=packet_forward_method, - weight=weight, - **kwargs)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} -a {1}".format( + __detect_os(), + _build_cmd( + protocol=protocol, + service_address=service_address, + server_address=server_address, + packet_forward_method=packet_forward_method, + weight=weight, + **kwargs + ), + ) + out = __salt__["cmd.run_all"](cmd, python_shell=False) # A non-zero return code means fail - if out['retcode']: - ret = out['stderr'].strip() + if out["retcode"]: + ret = out["stderr"].strip() else: ret = True return ret -def edit_server(protocol=None, service_address=None, server_address=None, packet_forward_method=None, weight=None, **kwargs): - ''' +def edit_server( + protocol=None, + service_address=None, + server_address=None, + packet_forward_method=None, + weight=None, + **kwargs +): + """ Edit a real server to a virtual service. @@ -258,27 +284,31 @@ def edit_server(protocol=None, service_address=None, server_address=None, packet .. code-block:: bash salt '*' lvs.edit_server tcp 1.1.1.1:80 192.168.0.11:8080 nat 1 - ''' + """ - cmd = '{0} -e {1}'.format(__detect_os(), - _build_cmd(protocol=protocol, - service_address=service_address, - server_address=server_address, - packet_forward_method=packet_forward_method, - weight=weight, - **kwargs)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} -e {1}".format( + __detect_os(), + _build_cmd( + protocol=protocol, + service_address=service_address, + server_address=server_address, + packet_forward_method=packet_forward_method, + weight=weight, + **kwargs + ), + ) + out = __salt__["cmd.run_all"](cmd, python_shell=False) # A non-zero return code means fail - if out['retcode']: - ret = out['stderr'].strip() + if out["retcode"]: + ret = out["stderr"].strip() else: ret = True return ret def delete_server(protocol=None, service_address=None, server_address=None): - ''' + """ Delete the realserver from the virtual service. @@ -297,24 +327,28 @@ def delete_server(protocol=None, service_address=None, server_address=None): .. code-block:: bash salt '*' lvs.delete_server tcp 1.1.1.1:80 192.168.0.11:8080 - ''' + """ - cmd = '{0} -d {1}'.format(__detect_os(), - _build_cmd(protocol=protocol, - service_address=service_address, - server_address=server_address)) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} -d {1}".format( + __detect_os(), + _build_cmd( + protocol=protocol, + service_address=service_address, + server_address=server_address, + ), + ) + out = __salt__["cmd.run_all"](cmd, python_shell=False) # A non-zero return code means fail - if out['retcode']: - ret = out['stderr'].strip() + if out["retcode"]: + ret = out["stderr"].strip() else: ret = True return ret def clear(): - ''' + """ Clear the virtual server table @@ -323,22 +357,22 @@ def clear(): .. code-block:: bash salt '*' lvs.clear - ''' + """ - cmd = '{0} -C'.format(__detect_os()) + cmd = "{0} -C".format(__detect_os()) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + out = __salt__["cmd.run_all"](cmd, python_shell=False) # A non-zero return code means fail - if out['retcode']: - ret = out['stderr'].strip() + if out["retcode"]: + ret = out["stderr"].strip() else: ret = True return ret def get_rules(): - ''' + """ Get the virtual server rules @@ -347,16 +381,16 @@ def get_rules(): .. code-block:: bash salt '*' lvs.get_rules - ''' + """ - cmd = '{0} -S -n'.format(__detect_os()) + cmd = "{0} -S -n".format(__detect_os()) - ret = __salt__['cmd.run'](cmd, python_shell=False) + ret = __salt__["cmd.run"](cmd, python_shell=False) return ret def list_(protocol=None, service_address=None): - ''' + """ List the virtual server table if service_address is not specified. If a service_address is selected, list this service only. @@ -365,27 +399,28 @@ def list_(protocol=None, service_address=None): .. code-block:: bash salt '*' lvs.list - ''' + """ if service_address: - cmd = '{0} -L {1} -n'.format(__detect_os(), - _build_cmd(protocol=protocol, - service_address=service_address)) + cmd = "{0} -L {1} -n".format( + __detect_os(), + _build_cmd(protocol=protocol, service_address=service_address), + ) else: - cmd = '{0} -L -n'.format(__detect_os()) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} -L -n".format(__detect_os()) + out = __salt__["cmd.run_all"](cmd, python_shell=False) # A non-zero return code means fail - if out['retcode']: - ret = out['stderr'].strip() + if out["retcode"]: + ret = out["stderr"].strip() else: - ret = out['stdout'].strip() + ret = out["stdout"].strip() return ret def zero(protocol=None, service_address=None): - ''' + """ Zero the packet, byte and rate counters in a service or all services. @@ -394,27 +429,27 @@ def zero(protocol=None, service_address=None): .. code-block:: bash salt '*' lvs.zero - ''' + """ if service_address: - cmd = '{0} -Z {1}'.format( + cmd = "{0} -Z {1}".format( __detect_os(), - _build_cmd(protocol=protocol, service_address=service_address) + _build_cmd(protocol=protocol, service_address=service_address), ) else: - cmd = '{0} -Z'.format(__detect_os()) - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "{0} -Z".format(__detect_os()) + out = __salt__["cmd.run_all"](cmd, python_shell=False) # A non-zero return code means fail - if out['retcode']: - ret = out['stderr'].strip() + if out["retcode"]: + ret = out["stderr"].strip() else: ret = True return ret def check_service(protocol=None, service_address=None, **kwargs): - ''' + """ Check the virtual service exists. @@ -423,14 +458,14 @@ def check_service(protocol=None, service_address=None, **kwargs): .. code-block:: bash salt '*' lvs.check_service tcp 1.1.1.1:80 - ''' + """ - cmd = '{0}'.format(_build_cmd(protocol=protocol, - service_address=service_address, - **kwargs)) + cmd = "{0}".format( + _build_cmd(protocol=protocol, service_address=service_address, **kwargs) + ) # Exact match if not kwargs: - cmd += ' ' + cmd += " " all_rules = get_rules() out = all_rules.find(cmd) @@ -438,12 +473,12 @@ def check_service(protocol=None, service_address=None, **kwargs): if out != -1: ret = True else: - ret = 'Error: service not exists' + ret = "Error: service not exists" return ret def check_server(protocol=None, service_address=None, server_address=None, **kwargs): - ''' + """ Check the real server exists in the specified service. @@ -452,15 +487,19 @@ def check_server(protocol=None, service_address=None, server_address=None, **kwa .. code-block:: bash salt '*' lvs.check_server tcp 1.1.1.1:80 192.168.0.11:8080 - ''' + """ - cmd = '{0}'.format(_build_cmd(protocol=protocol, - service_address=service_address, - server_address=server_address, - **kwargs)) + cmd = "{0}".format( + _build_cmd( + protocol=protocol, + service_address=service_address, + server_address=server_address, + **kwargs + ) + ) # Exact match if not kwargs: - cmd += ' ' + cmd += " " all_rules = get_rules() out = all_rules.find(cmd) @@ -468,5 +507,5 @@ def check_server(protocol=None, service_address=None, server_address=None, **kwa if out != -1: ret = True else: - ret = 'Error: server not exists' + ret = "Error: server not exists" return ret diff --git a/salt/modules/lxc.py b/salt/modules/lxc.py index 200a475e6a1..b9c899ba390 100644 --- a/salt/modules/lxc.py +++ b/salt/modules/lxc.py @@ -1,28 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Control Linux Containers via Salt :depends: lxc package for distribution lxc >= 1.0 (even beta alpha) is required -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import datetime + import copy -import string -import textwrap +import datetime import difflib import logging -import tempfile import os import pipes -import time -import shutil -import re import random +import re +import shutil +import string +import tempfile +import textwrap +import time + +import salt.config # Import salt libs import salt.utils.args @@ -37,36 +40,34 @@ import salt.utils.odict import salt.utils.path import salt.utils.stringutils from salt.exceptions import CommandExecutionError, SaltInvocationError -import salt.config -from salt.utils.versions import LooseVersion as _LooseVersion # Import 3rd-party libs from salt.ext import six + # pylint: disable=import-error,no-name-in-module from salt.ext.six.moves import range # pylint: disable=redefined-builtin from salt.ext.six.moves.urllib.parse import urlparse as _urlparse +from salt.utils.versions import LooseVersion as _LooseVersion + # pylint: enable=import-error,no-name-in-module # Set up logging log = logging.getLogger(__name__) # Don't shadow built-in's. -__func_alias__ = { - 'list_': 'list', - 'ls_': 'ls' -} +__func_alias__ = {"list_": "list", "ls_": "ls"} -__virtualname__ = 'lxc' -DEFAULT_NIC = 'eth0' -DEFAULT_BR = 'br0' -SEED_MARKER = '/lxc.initial_seed' -EXEC_DRIVER = 'lxc-attach' -DEFAULT_PATH = '/var/lib/lxc' +__virtualname__ = "lxc" +DEFAULT_NIC = "eth0" +DEFAULT_BR = "br0" +SEED_MARKER = "/lxc.initial_seed" +EXEC_DRIVER = "lxc-attach" +DEFAULT_PATH = "/var/lib/lxc" _marker = object() def __virtual__(): - if salt.utils.path.which('lxc-start'): + if salt.utils.path.which("lxc-start"): return __virtualname__ # To speed up the whole thing, we decided to not use the # subshell way and assume things are in place for lxc @@ -87,11 +88,14 @@ def __virtual__(): # return 'lxc' # return False # - return (False, 'The lxc execution module cannot be loaded: the lxc-start binary is not in the path.') + return ( + False, + "The lxc execution module cannot be loaded: the lxc-start binary is not in the path.", + ) def get_root_path(path): - ''' + """ Get the configured lxc root for containers .. versionadded:: 2015.8.0 @@ -102,14 +106,14 @@ def get_root_path(path): salt '*' lxc.get_root_path - ''' + """ if not path: - path = __opts__.get('lxc.root_path', DEFAULT_PATH) + path = __opts__.get("lxc.root_path", DEFAULT_PATH) return path def version(): - ''' + """ Return the actual lxc client version .. versionadded:: 2015.8.0 @@ -120,41 +124,41 @@ def version(): salt '*' lxc.version - ''' - k = 'lxc.version' + """ + k = "lxc.version" if not __context__.get(k, None): - cversion = __salt__['cmd.run_all']('lxc-info --version') - if not cversion['retcode']: - ver = _LooseVersion(cversion['stdout']) - if ver < _LooseVersion('1.0'): - raise CommandExecutionError('LXC should be at least 1.0') + cversion = __salt__["cmd.run_all"]("lxc-info --version") + if not cversion["retcode"]: + ver = _LooseVersion(cversion["stdout"]) + if ver < _LooseVersion("1.0"): + raise CommandExecutionError("LXC should be at least 1.0") __context__[k] = "{0}".format(ver) return __context__.get(k, None) def _clear_context(): - ''' + """ Clear any lxc variables set in __context__ - ''' - for var in [x for x in __context__ if x.startswith('lxc.')]: - log.trace('Clearing __context__[\'%s\']', var) + """ + for var in [x for x in __context__ if x.startswith("lxc.")]: + log.trace("Clearing __context__['%s']", var) __context__.pop(var, None) def _ip_sort(ip): - '''Ip sorting''' - idx = '001' - if ip == '127.0.0.1': - idx = '200' - if ip == '::1': - idx = '201' - elif '::' in ip: - idx = '100' - return '{0}___{1}'.format(idx, ip) + """Ip sorting""" + idx = "001" + if ip == "127.0.0.1": + idx = "200" + if ip == "::1": + idx = "201" + elif "::" in ip: + idx = "100" + return "{0}___{1}".format(idx, ip) def search_lxc_bridges(): - ''' + """ Search which bridges are potentially available as LXC bridges CLI Example: @@ -163,8 +167,8 @@ def search_lxc_bridges(): salt '*' lxc.search_lxc_bridges - ''' - bridges = __context__.get('lxc.bridges', None) + """ + bridges = __context__.get("lxc.bridges", None) # either match not yet called or no bridges were found # to handle the case where lxc was not installed on the first # call @@ -173,20 +177,16 @@ def search_lxc_bridges(): running_bridges = set() bridges.add(DEFAULT_BR) try: - output = __salt__['cmd.run_all']('brctl show') - for line in output['stdout'].splitlines()[1:]: - if not line.startswith(' '): + output = __salt__["cmd.run_all"]("brctl show") + for line in output["stdout"].splitlines()[1:]: + if not line.startswith(" "): running_bridges.add(line.split()[0].strip()) except (SaltInvocationError, CommandExecutionError): pass - for ifc, ip in six.iteritems( - __grains__.get('ip_interfaces', {}) - ): + for ifc, ip in six.iteritems(__grains__.get("ip_interfaces", {})): if ifc in running_bridges: bridges.add(ifc) - elif os.path.exists( - '/sys/devices/virtual/net/{0}/bridge'.format(ifc) - ): + elif os.path.exists("/sys/devices/virtual/net/{0}/bridge".format(ifc)): bridges.add(ifc) bridges = list(bridges) # if we found interfaces that have lxc in their names @@ -194,19 +194,20 @@ def search_lxc_bridges(): # we also try to default on br0 on other cases def sort_bridges(a): - pref = 'z' - if 'lxc' in a: - pref = 'a' - elif 'br0' == a: - pref = 'c' - return '{0}_{1}'.format(pref, a) + pref = "z" + if "lxc" in a: + pref = "a" + elif "br0" == a: + pref = "c" + return "{0}_{1}".format(pref, a) + bridges.sort(key=sort_bridges) - __context__['lxc.bridges'] = bridges + __context__["lxc.bridges"] = bridges return bridges def search_lxc_bridge(): - ''' + """ Search the first bridge which is potentially available as LXC bridge CLI Example: @@ -215,32 +216,32 @@ def search_lxc_bridge(): salt '*' lxc.search_lxc_bridge - ''' + """ return search_lxc_bridges()[0] def _get_salt_config(config, **kwargs): if not config: - config = kwargs.get('minion', {}) + config = kwargs.get("minion", {}) if not config: config = {} - config.setdefault('master', - kwargs.get('master', - __opts__.get('master', - __opts__['id']))) config.setdefault( - 'master_port', - kwargs.get('master_port', - __opts__.get('master_port', - __opts__.get('ret_port', - __opts__.get('4506'))))) - if not config['master']: + "master", kwargs.get("master", __opts__.get("master", __opts__["id"])) + ) + config.setdefault( + "master_port", + kwargs.get( + "master_port", + __opts__.get("master_port", __opts__.get("ret_port", __opts__.get("4506"))), + ), + ) + if not config["master"]: config = {} return config def cloud_init_interface(name, vm_=None, **kwargs): - ''' + """ Interface between salt.cloud.lxc driver and lxc.init ``vm_`` is a mapping of vm opts in the salt.cloud format as documented for the lxc driver. @@ -371,15 +372,13 @@ def cloud_init_interface(name, vm_=None, **kwargs): salt '*' lxc.cloud_init_interface foo - ''' + """ if vm_ is None: vm_ = {} vm_ = copy.deepcopy(vm_) vm_ = salt.utils.dictupdate.update(vm_, kwargs) - profile_data = copy.deepcopy( - vm_.get('lxc_profile', - vm_.get('profile', {}))) + profile_data = copy.deepcopy(vm_.get("lxc_profile", vm_.get("profile", {}))) if not isinstance(profile_data, (dict, six.string_types)): profile_data = {} profile = get_container_profile(profile_data) @@ -388,59 +387,58 @@ def cloud_init_interface(name, vm_=None, **kwargs): return vm_.get(k, profile.get(k, default)) if name is None: - name = vm_['name'] + name = vm_["name"] # if we are on ubuntu, default to ubuntu - default_template = '' - if __grains__.get('os', '') in ['Ubuntu']: - default_template = 'ubuntu' - image = _cloud_get('image') + default_template = "" + if __grains__.get("os", "") in ["Ubuntu"]: + default_template = "ubuntu" + image = _cloud_get("image") if not image: - _cloud_get('template', default_template) - backing = _cloud_get('backing', 'dir') + _cloud_get("template", default_template) + backing = _cloud_get("backing", "dir") if image: - profile['template'] = image - vgname = _cloud_get('vgname', None) + profile["template"] = image + vgname = _cloud_get("vgname", None) if vgname: - profile['vgname'] = vgname + profile["vgname"] = vgname if backing: - profile['backing'] = backing - snapshot = _cloud_get('snapshot', False) - autostart = bool(_cloud_get('autostart', True)) - dnsservers = _cloud_get('dnsservers', []) - dns_via_dhcp = _cloud_get('dns_via_dhcp', True) - password = _cloud_get('password', 's3cr3t') - password_encrypted = _cloud_get('password_encrypted', False) - fstype = _cloud_get('fstype', None) - lvname = _cloud_get('lvname', None) - thinpool = _cloud_get('thinpool', None) - pub_key = _cloud_get('pub_key', None) - priv_key = _cloud_get('priv_key', None) - size = _cloud_get('size', '20G') - script = _cloud_get('script', None) - script_args = _cloud_get('script_args', None) - users = _cloud_get('users', None) + profile["backing"] = backing + snapshot = _cloud_get("snapshot", False) + autostart = bool(_cloud_get("autostart", True)) + dnsservers = _cloud_get("dnsservers", []) + dns_via_dhcp = _cloud_get("dns_via_dhcp", True) + password = _cloud_get("password", "s3cr3t") + password_encrypted = _cloud_get("password_encrypted", False) + fstype = _cloud_get("fstype", None) + lvname = _cloud_get("lvname", None) + thinpool = _cloud_get("thinpool", None) + pub_key = _cloud_get("pub_key", None) + priv_key = _cloud_get("priv_key", None) + size = _cloud_get("size", "20G") + script = _cloud_get("script", None) + script_args = _cloud_get("script_args", None) + users = _cloud_get("users", None) if users is None: users = [] - ssh_username = _cloud_get('ssh_username', None) + ssh_username = _cloud_get("ssh_username", None) if ssh_username and (ssh_username not in users): users.append(ssh_username) - network_profile = _cloud_get('network_profile', None) - nic_opts = kwargs.get('nic_opts', None) - netmask = _cloud_get('netmask', '24') - path = _cloud_get('path', None) - bridge = _cloud_get('bridge', None) - gateway = _cloud_get('gateway', None) - unconditional_install = _cloud_get('unconditional_install', False) - force_install = _cloud_get('force_install', True) - config = _get_salt_config(_cloud_get('config', {}), **vm_) - default_nic = _cloud_get('default_nic', DEFAULT_NIC) + network_profile = _cloud_get("network_profile", None) + nic_opts = kwargs.get("nic_opts", None) + netmask = _cloud_get("netmask", "24") + path = _cloud_get("path", None) + bridge = _cloud_get("bridge", None) + gateway = _cloud_get("gateway", None) + unconditional_install = _cloud_get("unconditional_install", False) + force_install = _cloud_get("force_install", True) + config = _get_salt_config(_cloud_get("config", {}), **vm_) + default_nic = _cloud_get("default_nic", DEFAULT_NIC) # do the interface with lxc.init mainly via nic_opts # to avoid extra and confusing extra use cases. if not isinstance(nic_opts, dict): nic_opts = salt.utils.odict.OrderedDict() # have a reference to the default nic - eth0 = nic_opts.setdefault(default_nic, - salt.utils.odict.OrderedDict()) + eth0 = nic_opts.setdefault(default_nic, salt.utils.odict.OrderedDict()) # lxc config is based of ifc order, be sure to use odicts. if not isinstance(nic_opts, salt.utils.odict.OrderedDict): bnic_opts = salt.utils.odict.OrderedDict() @@ -448,53 +446,53 @@ def cloud_init_interface(name, vm_=None, **kwargs): nic_opts = bnic_opts gw = None # legacy salt.cloud scheme for network interfaces settings support - bridge = _cloud_get('bridge', None) - ip = _cloud_get('ip', None) - mac = _cloud_get('mac', None) + bridge = _cloud_get("bridge", None) + ip = _cloud_get("ip", None) + mac = _cloud_get("mac", None) if ip: fullip = ip if netmask: - fullip += '/{0}'.format(netmask) - eth0['ipv4'] = fullip + fullip += "/{0}".format(netmask) + eth0["ipv4"] = fullip if mac is not None: - eth0['mac'] = mac + eth0["mac"] = mac for ix, iopts in enumerate(_cloud_get("additional_ips", [])): - ifh = "eth{0}".format(ix+1) + ifh = "eth{0}".format(ix + 1) ethx = nic_opts.setdefault(ifh, {}) if gw is None: - gw = iopts.get('gateway', ethx.get('gateway', None)) + gw = iopts.get("gateway", ethx.get("gateway", None)) if gw: # only one and only one default gateway is allowed ! - eth0.pop('gateway', None) + eth0.pop("gateway", None) gateway = None # even if the gateway if on default "eth0" nic # and we popped it will work # as we reinject or set it here. - ethx['gateway'] = gw - elink = iopts.get('link', ethx.get('link', None)) + ethx["gateway"] = gw + elink = iopts.get("link", ethx.get("link", None)) if elink: - ethx['link'] = elink + ethx["link"] = elink # allow dhcp - aip = iopts.get('ipv4', iopts.get('ip', None)) + aip = iopts.get("ipv4", iopts.get("ip", None)) if aip: - ethx['ipv4'] = aip - nm = iopts.get('netmask', '') + ethx["ipv4"] = aip + nm = iopts.get("netmask", "") if nm: - ethx['ipv4'] += '/{0}'.format(nm) - for i in ('mac', 'hwaddr'): + ethx["ipv4"] += "/{0}".format(nm) + for i in ("mac", "hwaddr"): if i in iopts: - ethx['mac'] = iopts[i] + ethx["mac"] = iopts[i] break - if 'mac' not in ethx: - ethx['mac'] = salt.utils.network.gen_mac() + if "mac" not in ethx: + ethx["mac"] = salt.utils.network.gen_mac() # last round checking for unique gateway and such gw = None for ethx in [a for a in nic_opts]: ndata = nic_opts[ethx] if gw: - ndata.pop('gateway', None) - if 'gateway' in ndata: - gw = ndata['gateway'] + ndata.pop("gateway", None) + if "gateway" in ndata: + gw = ndata["gateway"] gateway = None # only use a default bridge / gateway if we configured them # via the legacy salt cloud configuration style. @@ -502,47 +500,45 @@ def cloud_init_interface(name, vm_=None, **kwargs): # salt lxc network profile style configuration which can # be also be overridden or a per interface basis via the nic_opts dict. if bridge: - eth0['link'] = bridge + eth0["link"] = bridge if gateway: - eth0['gateway'] = gateway + eth0["gateway"] = gateway # lxc_init_interface = {} - lxc_init_interface['name'] = name - lxc_init_interface['config'] = config - lxc_init_interface['memory'] = _cloud_get('memory', 0) # nolimit - lxc_init_interface['pub_key'] = pub_key - lxc_init_interface['priv_key'] = priv_key - lxc_init_interface['nic_opts'] = nic_opts - for clone_from in ['clone_from', 'clone', 'from_container']: + lxc_init_interface["name"] = name + lxc_init_interface["config"] = config + lxc_init_interface["memory"] = _cloud_get("memory", 0) # nolimit + lxc_init_interface["pub_key"] = pub_key + lxc_init_interface["priv_key"] = priv_key + lxc_init_interface["nic_opts"] = nic_opts + for clone_from in ["clone_from", "clone", "from_container"]: # clone_from should default to None if not available - lxc_init_interface['clone_from'] = _cloud_get(clone_from, None) - if lxc_init_interface['clone_from'] is not None: + lxc_init_interface["clone_from"] = _cloud_get(clone_from, None) + if lxc_init_interface["clone_from"] is not None: break - lxc_init_interface['profile'] = profile - lxc_init_interface['snapshot'] = snapshot - lxc_init_interface['dnsservers'] = dnsservers - lxc_init_interface['fstype'] = fstype - lxc_init_interface['path'] = path - lxc_init_interface['vgname'] = vgname - lxc_init_interface['size'] = size - lxc_init_interface['lvname'] = lvname - lxc_init_interface['thinpool'] = thinpool - lxc_init_interface['force_install'] = force_install - lxc_init_interface['unconditional_install'] = ( - unconditional_install - ) - lxc_init_interface['bootstrap_url'] = script - lxc_init_interface['bootstrap_args'] = script_args - lxc_init_interface['bootstrap_shell'] = _cloud_get('bootstrap_shell', 'sh') - lxc_init_interface['bootstrap_delay'] = _cloud_get('bootstrap_delay', None) - lxc_init_interface['autostart'] = autostart - lxc_init_interface['users'] = users - lxc_init_interface['password'] = password - lxc_init_interface['password_encrypted'] = password_encrypted + lxc_init_interface["profile"] = profile + lxc_init_interface["snapshot"] = snapshot + lxc_init_interface["dnsservers"] = dnsservers + lxc_init_interface["fstype"] = fstype + lxc_init_interface["path"] = path + lxc_init_interface["vgname"] = vgname + lxc_init_interface["size"] = size + lxc_init_interface["lvname"] = lvname + lxc_init_interface["thinpool"] = thinpool + lxc_init_interface["force_install"] = force_install + lxc_init_interface["unconditional_install"] = unconditional_install + lxc_init_interface["bootstrap_url"] = script + lxc_init_interface["bootstrap_args"] = script_args + lxc_init_interface["bootstrap_shell"] = _cloud_get("bootstrap_shell", "sh") + lxc_init_interface["bootstrap_delay"] = _cloud_get("bootstrap_delay", None) + lxc_init_interface["autostart"] = autostart + lxc_init_interface["users"] = users + lxc_init_interface["password"] = password + lxc_init_interface["password_encrypted"] = password_encrypted # be sure not to let objects goes inside the return # as this return will be msgpacked for use in the runner ! - lxc_init_interface['network_profile'] = network_profile - for i in ['cpu', 'cpuset', 'cpushare']: + lxc_init_interface["network_profile"] = network_profile + for i in ["cpu", "cpuset", "cpushare"]: if _cloud_get(i, None): try: lxc_init_interface[i] = vm_[i] @@ -553,37 +549,33 @@ def cloud_init_interface(name, vm_=None, **kwargs): def _get_profile(key, name, **kwargs): if isinstance(name, dict): - profilename = name.pop('name', None) + profilename = name.pop("name", None) return _get_profile(key, profilename, **name) if name is None: profile_match = {} else: - profile_match = \ - __salt__['config.get']( - 'lxc.{1}:{0}'.format(name, key), - default=None, - merge='recurse' - ) + profile_match = __salt__["config.get"]( + "lxc.{1}:{0}".format(name, key), default=None, merge="recurse" + ) if profile_match is None: # No matching profile, make the profile an empty dict so that # overrides can be applied below. profile_match = {} if not isinstance(profile_match, dict): - raise CommandExecutionError('lxc.{0} must be a dictionary'.format(key)) + raise CommandExecutionError("lxc.{0} must be a dictionary".format(key)) # Overlay the kwargs to override matched profile data overrides = salt.utils.args.clean_kwargs(**copy.deepcopy(kwargs)) profile_match = salt.utils.dictupdate.update( - copy.deepcopy(profile_match), - overrides + copy.deepcopy(profile_match), overrides ) return profile_match def get_container_profile(name=None, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 Gather a pre-configured set of container configuration parameters. If no @@ -618,13 +610,13 @@ def get_container_profile(name=None, **kwargs): salt-call lxc.get_container_profile centos salt-call lxc.get_container_profile ubuntu template=ubuntu backing=overlayfs - ''' - profile = _get_profile('container_profile', name, **kwargs) + """ + profile = _get_profile("container_profile", name, **kwargs) return profile def get_network_profile(name=None, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 Gather a pre-configured set of network configuration parameters. If no @@ -677,30 +669,30 @@ def get_network_profile(name=None, **kwargs): .. code-block:: bash salt-call lxc.get_network_profile default - ''' + """ - profile = _get_profile('network_profile', name, **kwargs) + profile = _get_profile("network_profile", name, **kwargs) return profile def _rand_cpu_str(cpu): - ''' + """ Return a random subset of cpus for the cpuset config - ''' + """ cpu = int(cpu) - avail = __salt__['status.nproc']() + avail = __salt__["status.nproc"]() if cpu < avail: - return '0-{0}'.format(avail) + return "0-{0}".format(avail) to_set = set() while len(to_set) < cpu: choice = random.randint(0, avail - 1) if choice not in to_set: to_set.add(six.text_type(choice)) - return ','.join(sorted(to_set)) + return ",".join(sorted(to_set)) def _network_conf(conf_tuples=None, **kwargs): - ''' + """ Network configuration defaults network_profile @@ -710,10 +702,10 @@ def _network_conf(conf_tuples=None, **kwargs): nic_opts overrides or extra nics in the form {nic_name: {set: tings} - ''' - nic = kwargs.get('network_profile', None) + """ + nic = kwargs.get("network_profile", None) ret = [] - nic_opts = kwargs.get('nic_opts', {}) + nic_opts = kwargs.get("nic_opts", {}) if nic_opts is None: # coming from elsewhere nic_opts = {} @@ -735,15 +727,15 @@ def _network_conf(conf_tuples=None, **kwargs): nicp[DEFAULT_NIC] = {} kwargs = copy.deepcopy(kwargs) - gateway = kwargs.pop('gateway', None) - bridge = kwargs.get('bridge', None) + gateway = kwargs.pop("gateway", None) + bridge = kwargs.get("bridge", None) if nic_opts: for dev, args in six.iteritems(nic_opts): ethx = nicp.setdefault(dev, {}) try: ethx = salt.utils.dictupdate.update(ethx, args) except AttributeError: - raise SaltInvocationError('Invalid nic_opts configuration') + raise SaltInvocationError("Invalid nic_opts configuration") ifs = [a for a in nicp] ifs += [a for a in old if a not in nicp] ifs.sort() @@ -752,89 +744,120 @@ def _network_conf(conf_tuples=None, **kwargs): args = nicp.get(dev, {}) opts = nic_opts.get(dev, {}) if nic_opts else {} old_if = old.get(dev, {}) - disable = opts.get('disable', args.get('disable', False)) + disable = opts.get("disable", args.get("disable", False)) if disable: continue - mac = opts.get('mac', - opts.get('hwaddr', - args.get('mac', - args.get('hwaddr', '')))) - type_ = opts.get('type', args.get('type', '')) - flags = opts.get('flags', args.get('flags', '')) - link = opts.get('link', args.get('link', '')) - ipv4 = opts.get('ipv4', args.get('ipv4', '')) - ipv6 = opts.get('ipv6', args.get('ipv6', '')) - infos = salt.utils.odict.OrderedDict([ - ('lxc.network.type', { - 'test': not type_, - 'value': type_, - 'old': old_if.get('lxc.network.type'), - 'default': 'veth'}), - ('lxc.network.name', { - 'test': False, - 'value': dev, - 'old': dev, - 'default': dev}), - ('lxc.network.flags', { - 'test': not flags, - 'value': flags, - 'old': old_if.get('lxc.network.flags'), - 'default': 'up'}), - ('lxc.network.link', { - 'test': not link, - 'value': link, - 'old': old_if.get('lxc.network.link'), - 'default': search_lxc_bridge()}), - ('lxc.network.hwaddr', { - 'test': not mac, - 'value': mac, - 'old': old_if.get('lxc.network.hwaddr'), - 'default': salt.utils.network.gen_mac()}), - ('lxc.network.ipv4', { - 'test': not ipv4, - 'value': ipv4, - 'old': old_if.get('lxc.network.ipv4', ''), - 'default': None}), - ('lxc.network.ipv6', { - 'test': not ipv6, - 'value': ipv6, - 'old': old_if.get('lxc.network.ipv6', ''), - 'default': None})]) + mac = opts.get( + "mac", opts.get("hwaddr", args.get("mac", args.get("hwaddr", ""))) + ) + type_ = opts.get("type", args.get("type", "")) + flags = opts.get("flags", args.get("flags", "")) + link = opts.get("link", args.get("link", "")) + ipv4 = opts.get("ipv4", args.get("ipv4", "")) + ipv6 = opts.get("ipv6", args.get("ipv6", "")) + infos = salt.utils.odict.OrderedDict( + [ + ( + "lxc.network.type", + { + "test": not type_, + "value": type_, + "old": old_if.get("lxc.network.type"), + "default": "veth", + }, + ), + ( + "lxc.network.name", + {"test": False, "value": dev, "old": dev, "default": dev}, + ), + ( + "lxc.network.flags", + { + "test": not flags, + "value": flags, + "old": old_if.get("lxc.network.flags"), + "default": "up", + }, + ), + ( + "lxc.network.link", + { + "test": not link, + "value": link, + "old": old_if.get("lxc.network.link"), + "default": search_lxc_bridge(), + }, + ), + ( + "lxc.network.hwaddr", + { + "test": not mac, + "value": mac, + "old": old_if.get("lxc.network.hwaddr"), + "default": salt.utils.network.gen_mac(), + }, + ), + ( + "lxc.network.ipv4", + { + "test": not ipv4, + "value": ipv4, + "old": old_if.get("lxc.network.ipv4", ""), + "default": None, + }, + ), + ( + "lxc.network.ipv6", + { + "test": not ipv6, + "value": ipv6, + "old": old_if.get("lxc.network.ipv6", ""), + "default": None, + }, + ), + ] + ) # for each parameter, if not explicitly set, the # config value present in the LXC configuration should # take precedence over the profile configuration for info in list(infos.keys()): bundle = infos[info] - if bundle['test']: - if bundle['old']: - bundle['value'] = bundle['old'] - elif bundle['default']: - bundle['value'] = bundle['default'] + if bundle["test"]: + if bundle["old"]: + bundle["value"] = bundle["old"] + elif bundle["default"]: + bundle["value"] = bundle["default"] for info, data in six.iteritems(infos): - if data['value']: - ret.append({info: data['value']}) + if data["value"]: + ret.append({info: data["value"]}) for key, val in six.iteritems(args): - if key == 'link' and bridge: + if key == "link" and bridge: val = bridge val = opts.get(key, val) if key in [ - 'type', 'flags', 'name', - 'gateway', 'mac', 'link', 'ipv4', 'ipv6' + "type", + "flags", + "name", + "gateway", + "mac", + "link", + "ipv4", + "ipv6", ]: continue - ret.append({'lxc.network.{0}'.format(key): val}) + ret.append({"lxc.network.{0}".format(key): val}) # gateway (in automode) must be appended following network conf ! if not gateway: - gateway = args.get('gateway', None) + gateway = args.get("gateway", None) if gateway is not None and not gateway_set: - ret.append({'lxc.network.ipv4.gateway': gateway}) + ret.append({"lxc.network.ipv4.gateway": gateway}) # only one network gateway ;) gateway_set = True # normally, this won't happen # set the gateway if specified even if we did # not managed the network underlying if gateway is not None and not gateway_set: - ret.append({'lxc.network.ipv4.gateway': gateway}) + ret.append({"lxc.network.ipv4.gateway": gateway}) # only one network gateway ;) gateway_set = True @@ -842,23 +865,23 @@ def _network_conf(conf_tuples=None, **kwargs): # verify that we did not loose the mac settings for iface in [a for a in new]: ndata = new[iface] - nmac = ndata.get('lxc.network.hwaddr', '') - ntype = ndata.get('lxc.network.type', '') - omac, otype = '', '' + nmac = ndata.get("lxc.network.hwaddr", "") + ntype = ndata.get("lxc.network.type", "") + omac, otype = "", "" if iface in old: odata = old[iface] - omac = odata.get('lxc.network.hwaddr', '') - otype = odata.get('lxc.network.type', '') + omac = odata.get("lxc.network.hwaddr", "") + otype = odata.get("lxc.network.type", "") # default for network type is setted here # attention not to change the network type # without a good and explicit reason to. if otype and not ntype: ntype = otype if not ntype: - ntype = 'veth' - new[iface]['lxc.network.type'] = ntype + ntype = "veth" + new[iface]["lxc.network.type"] = ntype if omac and not nmac: - new[iface]['lxc.network.hwaddr'] = omac + new[iface]["lxc.network.hwaddr"] = omac ret = [] for val in six.itervalues(new): @@ -867,21 +890,23 @@ def _network_conf(conf_tuples=None, **kwargs): # on old versions of lxc, still support the gateway auto mode # if we didn't explicitly say no to # (lxc.network.ipv4.gateway: auto) - if _LooseVersion(version()) <= _LooseVersion('1.0.7') and \ - True not in ['lxc.network.ipv4.gateway' in a for a in ret] and \ - True in ['lxc.network.ipv4' in a for a in ret]: - ret.append({'lxc.network.ipv4.gateway': 'auto'}) + if ( + _LooseVersion(version()) <= _LooseVersion("1.0.7") + and True not in ["lxc.network.ipv4.gateway" in a for a in ret] + and True in ["lxc.network.ipv4" in a for a in ret] + ): + ret.append({"lxc.network.ipv4.gateway": "auto"}) return ret def _get_lxc_default_data(**kwargs): kwargs = copy.deepcopy(kwargs) ret = {} - for k in ['utsname', 'rootfs']: + for k in ["utsname", "rootfs"]: val = kwargs.get(k, None) if val is not None: - ret['lxc.{0}'.format(k)] = val - autostart = kwargs.get('autostart') + ret["lxc.{0}".format(k)] = val + autostart = kwargs.get("autostart") # autostart can have made in kwargs, but with the None # value which is invalid, we need an explicit boolean # autostart = on is the default. @@ -891,29 +916,29 @@ def _get_lxc_default_data(**kwargs): # machine (re)boot only if we did not explicitly ask # not to touch to the autostart settings via # autostart == 'keep' - if autostart != 'keep': + if autostart != "keep": if autostart: - ret['lxc.start.auto'] = '1' + ret["lxc.start.auto"] = "1" else: - ret['lxc.start.auto'] = '0' - memory = kwargs.get('memory') + ret["lxc.start.auto"] = "0" + memory = kwargs.get("memory") if memory is not None: # converting the config value from MB to bytes - ret['lxc.cgroup.memory.limit_in_bytes'] = memory * 1024 * 1024 - cpuset = kwargs.get('cpuset') + ret["lxc.cgroup.memory.limit_in_bytes"] = memory * 1024 * 1024 + cpuset = kwargs.get("cpuset") if cpuset: - ret['lxc.cgroup.cpuset.cpus'] = cpuset - cpushare = kwargs.get('cpushare') - cpu = kwargs.get('cpu') + ret["lxc.cgroup.cpuset.cpus"] = cpuset + cpushare = kwargs.get("cpushare") + cpu = kwargs.get("cpu") if cpushare: - ret['lxc.cgroup.cpu.shares'] = cpushare + ret["lxc.cgroup.cpu.shares"] = cpushare if cpu and not cpuset: - ret['lxc.cgroup.cpuset.cpus'] = _rand_cpu_str(cpu) + ret["lxc.cgroup.cpuset.cpus"] = _rand_cpu_str(cpu) return ret def _config_list(conf_tuples=None, only_net=False, **kwargs): - ''' + """ Return a list of dicts from the salt level configurations conf_tuples @@ -935,7 +960,7 @@ def _config_list(conf_tuples=None, only_net=False, **kwargs): reflect the network configuration back to the conf tuples list - ''' + """ # explicit cast only_net = bool(only_net) if not conf_tuples: @@ -952,10 +977,10 @@ def _config_list(conf_tuples=None, only_net=False, **kwargs): def _get_veths(net_data): - ''' + """ Parse the nic setup inside lxc conf tuples back to a dictionary indexed by network interface - ''' + """ if isinstance(net_data, dict): net_data = list(net_data.items()) nics = salt.utils.odict.OrderedDict() @@ -968,13 +993,13 @@ def _get_veths(net_data): elif isinstance(item, six.string_types): # deal with reflection of commented lxc configs sitem = item.strip() - if sitem.startswith('#') or not sitem: + if sitem.startswith("#") or not sitem: continue - elif '=' in item: - item = tuple([a.strip() for a in item.split('=', 1)]) - if item[0] == 'lxc.network.type': + elif "=" in item: + item = tuple([a.strip() for a in item.split("=", 1)]) + if item[0] == "lxc.network.type": current_nic = salt.utils.odict.OrderedDict() - if item[0] == 'lxc.network.name': + if item[0] == "lxc.network.name": no_names = False nics[item[1].strip()] = current_nic current_nic[item[0].strip()] = item[1].strip() @@ -986,29 +1011,29 @@ def _get_veths(net_data): class _LXCConfig(object): - ''' + """ LXC configuration data - ''' - pattern = re.compile(r'^(\S+)(\s*)(=)(\s*)(.*)') - non_interpretable_pattern = re.compile(r'^((#.*)|(\s*))$') + """ + + pattern = re.compile(r"^(\S+)(\s*)(=)(\s*)(.*)") + non_interpretable_pattern = re.compile(r"^((#.*)|(\s*))$") def __init__(self, **kwargs): kwargs = copy.deepcopy(kwargs) - self.name = kwargs.pop('name', None) - path = get_root_path(kwargs.get('path', None)) + self.name = kwargs.pop("name", None) + path = get_root_path(kwargs.get("path", None)) self.data = [] if self.name: - self.path = os.path.join(path, self.name, 'config') + self.path = os.path.join(path, self.name, "config") if os.path.isfile(self.path): with salt.utils.files.fopen(self.path) as fhr: for line in salt.utils.data.decode(fhr.readlines()): match = self.pattern.findall((line.strip())) if match: self.data.append((match[0][0], match[0][-1])) - match = self.non_interpretable_pattern.findall( - (line.strip())) + match = self.non_interpretable_pattern.findall((line.strip())) if match: - self.data.append(('', match[0][0])) + self.data.append(("", match[0][0])) else: self.path = None @@ -1020,27 +1045,30 @@ class _LXCConfig(object): default_data = _get_lxc_default_data(**kwargs) for key, val in six.iteritems(default_data): _replace(key, val) - old_net = self._filter_data('lxc.network') + old_net = self._filter_data("lxc.network") net_datas = _network_conf(conf_tuples=old_net, **kwargs) if net_datas: for row in net_datas: self.data.extend(list(row.items())) # be sure to reset harmful settings - for idx in ['lxc.cgroup.memory.limit_in_bytes']: + for idx in ["lxc.cgroup.memory.limit_in_bytes"]: if not default_data.get(idx): self._filter_data(idx) def as_string(self): - chunks = ('{0[0]}{1}{0[1]}'.format(item, (' = ' if item[0] else '')) for item in self.data) - return '\n'.join(chunks) + '\n' + chunks = ( + "{0[0]}{1}{0[1]}".format(item, (" = " if item[0] else "")) + for item in self.data + ) + return "\n".join(chunks) + "\n" def write(self): if self.path: content = self.as_string() # 2 step rendering to be sure not to open/wipe the config # before as_string succeeds. - with salt.utils.files.fopen(self.path, 'w') as fic: + with salt.utils.files.fopen(self.path, "w") as fic: fic.write(salt.utils.stringutils.to_str(content)) fic.flush() @@ -1053,9 +1081,9 @@ class _LXCConfig(object): return ntf def _filter_data(self, pattern): - ''' + """ Removes parameters which match the pattern from the config data - ''' + """ removed = [] filtered = [] for param in self.data: @@ -1068,12 +1096,12 @@ class _LXCConfig(object): def _get_base(**kwargs): - ''' + """ If the needed base does not exist, then create it, if it does exist create nothing and return the name of the base lxc container so it can be cloned. - ''' - profile = get_container_profile(copy.deepcopy(kwargs.get('profile'))) + """ + profile = get_container_profile(copy.deepcopy(kwargs.get("profile"))) kw_overrides = copy.deepcopy(kwargs) def select(key, default=None): @@ -1084,71 +1112,81 @@ def _get_base(**kwargs): return profile_match return kw_overrides_match - template = select('template') - image = select('image') - vgname = select('vgname') - path = kwargs.get('path', None) + template = select("template") + image = select("image") + vgname = select("vgname") + path = kwargs.get("path", None) # remove the above three variables from kwargs, if they exist, to avoid # duplicates if create() is invoked below. - for param in ('path', 'image', 'vgname', 'template'): + for param in ("path", "image", "vgname", "template"): kwargs.pop(param, None) if image: proto = _urlparse(image).scheme - img_tar = __salt__['cp.cache_file'](image) + img_tar = __salt__["cp.cache_file"](image) img_name = os.path.basename(img_tar) hash_ = salt.utils.hashutils.get_hash( - img_tar, - __salt__['config.get']('hash_type')) - name = '__base_{0}_{1}_{2}'.format(proto, img_name, hash_) + img_tar, __salt__["config.get"]("hash_type") + ) + name = "__base_{0}_{1}_{2}".format(proto, img_name, hash_) if not exists(name, path=path): - create(name, template=template, image=image, - path=path, vgname=vgname, **kwargs) + create( + name, template=template, image=image, path=path, vgname=vgname, **kwargs + ) if vgname: - rootfs = os.path.join('/dev', vgname, name) - edit_conf(info(name, path=path)['config'], - out_format='commented', **{'lxc.rootfs': rootfs}) + rootfs = os.path.join("/dev", vgname, name) + edit_conf( + info(name, path=path)["config"], + out_format="commented", + **{"lxc.rootfs": rootfs} + ) return name elif template: - name = '__base_{0}'.format(template) + name = "__base_{0}".format(template) if not exists(name, path=path): - create(name, template=template, image=image, path=path, - vgname=vgname, **kwargs) + create( + name, template=template, image=image, path=path, vgname=vgname, **kwargs + ) if vgname: - rootfs = os.path.join('/dev', vgname, name) - edit_conf(info(name, path=path)['config'], - out_format='commented', **{'lxc.rootfs': rootfs}) + rootfs = os.path.join("/dev", vgname, name) + edit_conf( + info(name, path=path)["config"], + out_format="commented", + **{"lxc.rootfs": rootfs} + ) return name - return '' + return "" -def init(name, - config=None, - cpuset=None, - cpushare=None, - memory=None, - profile=None, - network_profile=None, - nic_opts=None, - cpu=None, - autostart=True, - password=None, - password_encrypted=None, - users=None, - dnsservers=None, - searchdomains=None, - bridge=None, - gateway=None, - pub_key=None, - priv_key=None, - force_install=False, - unconditional_install=False, - bootstrap_delay=None, - bootstrap_args=None, - bootstrap_shell=None, - bootstrap_url=None, - **kwargs): - ''' +def init( + name, + config=None, + cpuset=None, + cpushare=None, + memory=None, + profile=None, + network_profile=None, + nic_opts=None, + cpu=None, + autostart=True, + password=None, + password_encrypted=None, + users=None, + dnsservers=None, + searchdomains=None, + bridge=None, + gateway=None, + pub_key=None, + priv_key=None, + force_install=False, + unconditional_install=False, + bootstrap_delay=None, + bootstrap_args=None, + bootstrap_shell=None, + bootstrap_url=None, + **kwargs +): + """ Initialize a new container. This is a partial idempotent function as if it is already provisioned, we @@ -1309,25 +1347,24 @@ def init(name, [users=[foo]] [password='secret'] \\ [password_encrypted=(True|False)] - ''' - ret = {'name': name, - 'changes': {}} + """ + ret = {"name": name, "changes": {}} profile = get_container_profile(copy.deepcopy(profile)) if not network_profile: - network_profile = profile.get('network_profile') + network_profile = profile.get("network_profile") if not network_profile: network_profile = DEFAULT_NIC # Changes is a pointer to changes_dict['init']. This method is used so that # we can have a list of changes as they are made, providing an ordered list # of things that were changed. - changes_dict = {'init': []} - changes = changes_dict.get('init') + changes_dict = {"init": []} + changes = changes_dict.get("init") if users is None: users = [] - dusers = ['root'] + dusers = ["root"] for user in dusers: if user not in users: users.append(user) @@ -1342,32 +1379,32 @@ def init(name, return profile_match return kw_overrides_match - path = select('path') + path = select("path") bpath = get_root_path(path) state_pre = state(name, path=path) - tvg = select('vgname') - vgname = tvg if tvg else __salt__['config.get']('lxc.vgname') - start_ = select('start', True) - autostart = select('autostart', autostart) - seed = select('seed', True) - install = select('install', True) - seed_cmd = select('seed_cmd') + tvg = select("vgname") + vgname = tvg if tvg else __salt__["config.get"]("lxc.vgname") + start_ = select("start", True) + autostart = select("autostart", autostart) + seed = select("seed", True) + install = select("install", True) + seed_cmd = select("seed_cmd") salt_config = _get_salt_config(config, **kwargs) - approve_key = select('approve_key', True) - clone_from = select('clone_from') + approve_key = select("approve_key", True) + clone_from = select("clone_from") # If using a volume group then set up to make snapshot cow clones if vgname and not clone_from: try: - kwargs['vgname'] = vgname + kwargs["vgname"] = vgname clone_from = _get_base(profile=profile, **kwargs) except (SaltInvocationError, CommandExecutionError) as exc: - ret['comment'] = exc.strerror + ret["comment"] = exc.strerror if changes: - ret['changes'] = changes_dict + ret["changes"] = changes_dict return ret - if not kwargs.get('snapshot') is False: - kwargs['snapshot'] = True + if not kwargs.get("snapshot") is False: + kwargs["snapshot"] = True does_exist = exists(name, path=path) to_reboot = False remove_seed_marker = False @@ -1377,273 +1414,323 @@ def init(name, remove_seed_marker = True try: clone(name, clone_from, profile=profile, **kwargs) - changes.append({'create': 'Container cloned'}) + changes.append({"create": "Container cloned"}) except (SaltInvocationError, CommandExecutionError) as exc: - if 'already exists' in exc.strerror: - changes.append({'create': 'Container already exists'}) + if "already exists" in exc.strerror: + changes.append({"create": "Container already exists"}) else: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror if changes: - ret['changes'] = changes_dict + ret["changes"] = changes_dict return ret - cfg = _LXCConfig(name=name, network_profile=network_profile, - nic_opts=nic_opts, bridge=bridge, path=path, - gateway=gateway, autostart=autostart, - cpuset=cpuset, cpushare=cpushare, memory=memory) - old_chunks = read_conf(cfg.path, out_format='commented') + cfg = _LXCConfig( + name=name, + network_profile=network_profile, + nic_opts=nic_opts, + bridge=bridge, + path=path, + gateway=gateway, + autostart=autostart, + cpuset=cpuset, + cpushare=cpushare, + memory=memory, + ) + old_chunks = read_conf(cfg.path, out_format="commented") cfg.write() - chunks = read_conf(cfg.path, out_format='commented') + chunks = read_conf(cfg.path, out_format="commented") if old_chunks != chunks: to_reboot = True else: remove_seed_marker = True - cfg = _LXCConfig(network_profile=network_profile, - nic_opts=nic_opts, cpuset=cpuset, path=path, - bridge=bridge, gateway=gateway, - autostart=autostart, - cpushare=cpushare, memory=memory) + cfg = _LXCConfig( + network_profile=network_profile, + nic_opts=nic_opts, + cpuset=cpuset, + path=path, + bridge=bridge, + gateway=gateway, + autostart=autostart, + cpushare=cpushare, + memory=memory, + ) with cfg.tempfile() as cfile: try: create(name, config=cfile.name, profile=profile, **kwargs) - changes.append({'create': 'Container created'}) + changes.append({"create": "Container created"}) except (SaltInvocationError, CommandExecutionError) as exc: - if 'already exists' in exc.strerror: - changes.append({'create': 'Container already exists'}) + if "already exists" in exc.strerror: + changes.append({"create": "Container already exists"}) else: - ret['comment'] = exc.strerror + ret["comment"] = exc.strerror if changes: - ret['changes'] = changes_dict + ret["changes"] = changes_dict return ret - cpath = os.path.join(bpath, name, 'config') + cpath = os.path.join(bpath, name, "config") old_chunks = [] if os.path.exists(cpath): - old_chunks = read_conf(cpath, out_format='commented') - new_cfg = _config_list(conf_tuples=old_chunks, - cpu=cpu, - network_profile=network_profile, - nic_opts=nic_opts, bridge=bridge, - cpuset=cpuset, cpushare=cpushare, - memory=memory) + old_chunks = read_conf(cpath, out_format="commented") + new_cfg = _config_list( + conf_tuples=old_chunks, + cpu=cpu, + network_profile=network_profile, + nic_opts=nic_opts, + bridge=bridge, + cpuset=cpuset, + cpushare=cpushare, + memory=memory, + ) if new_cfg: - edit_conf(cpath, out_format='commented', lxc_config=new_cfg) - chunks = read_conf(cpath, out_format='commented') + edit_conf(cpath, out_format="commented", lxc_config=new_cfg) + chunks = read_conf(cpath, out_format="commented") if old_chunks != chunks: to_reboot = True # last time to be sure any of our property is correctly applied - cfg = _LXCConfig(name=name, network_profile=network_profile, - nic_opts=nic_opts, bridge=bridge, path=path, - gateway=gateway, autostart=autostart, - cpuset=cpuset, cpushare=cpushare, memory=memory) + cfg = _LXCConfig( + name=name, + network_profile=network_profile, + nic_opts=nic_opts, + bridge=bridge, + path=path, + gateway=gateway, + autostart=autostart, + cpuset=cpuset, + cpushare=cpushare, + memory=memory, + ) old_chunks = [] if os.path.exists(cfg.path): - old_chunks = read_conf(cfg.path, out_format='commented') + old_chunks = read_conf(cfg.path, out_format="commented") cfg.write() - chunks = read_conf(cfg.path, out_format='commented') + chunks = read_conf(cfg.path, out_format="commented") if old_chunks != chunks: - changes.append({'config': 'Container configuration updated'}) + changes.append({"config": "Container configuration updated"}) to_reboot = True if to_reboot: try: stop(name, path=path) except (SaltInvocationError, CommandExecutionError) as exc: - ret['comment'] = 'Unable to stop container: {0}'.format(exc) + ret["comment"] = "Unable to stop container: {0}".format(exc) if changes: - ret['changes'] = changes_dict + ret["changes"] = changes_dict return ret - if not does_exist or (does_exist and state(name, path=path) != 'running'): + if not does_exist or (does_exist and state(name, path=path) != "running"): try: start(name, path=path) except (SaltInvocationError, CommandExecutionError) as exc: - ret['comment'] = 'Unable to stop container: {0}'.format(exc) + ret["comment"] = "Unable to stop container: {0}".format(exc) if changes: - ret['changes'] = changes_dict + ret["changes"] = changes_dict return ret if remove_seed_marker: - run(name, - 'rm -f \'{0}\''.format(SEED_MARKER), + run( + name, + "rm -f '{0}'".format(SEED_MARKER), path=path, chroot_fallback=False, - python_shell=False) + python_shell=False, + ) # set the default user/password, only the first time - if ret.get('result', True) and password: - gid = '/.lxc.initial_pass' - gids = [gid, - '/lxc.initial_pass', - '/.lxc.{0}.initial_pass'.format(name)] - if not any(retcode(name, - 'test -e "{0}"'.format(x), - chroot_fallback=True, - path=path, - ignore_retcode=True) == 0 - for x in gids): + if ret.get("result", True) and password: + gid = "/.lxc.initial_pass" + gids = [gid, "/lxc.initial_pass", "/.lxc.{0}.initial_pass".format(name)] + if not any( + retcode( + name, + 'test -e "{0}"'.format(x), + chroot_fallback=True, + path=path, + ignore_retcode=True, + ) + == 0 + for x in gids + ): # think to touch the default user generated by default templates # which has a really unsecure passwords... # root is defined as a member earlier in the code - for default_user in ['ubuntu']: + for default_user in ["ubuntu"]: if ( - default_user not in users and - retcode(name, - 'id {0}'.format(default_user), - python_shell=False, - path=path, - chroot_fallback=True, - ignore_retcode=True) == 0 + default_user not in users + and retcode( + name, + "id {0}".format(default_user), + python_shell=False, + path=path, + chroot_fallback=True, + ignore_retcode=True, + ) + == 0 ): users.append(default_user) for user in users: try: - cret = set_password(name, - users=[user], - path=path, - password=password, - encrypted=password_encrypted) + cret = set_password( + name, + users=[user], + path=path, + password=password, + encrypted=password_encrypted, + ) except (SaltInvocationError, CommandExecutionError) as exc: - msg = '{0}: Failed to set password'.format( - user) + exc.strerror + msg = "{0}: Failed to set password".format(user) + exc.strerror # only hardfail in unrecoverable situation: # root cannot be setted up - if user == 'root': - ret['comment'] = msg - ret['result'] = False + if user == "root": + ret["comment"] = msg + ret["result"] = False else: log.debug(msg) - if ret.get('result', True): - changes.append({'password': 'Password(s) updated'}) - if retcode(name, - ('sh -c \'touch "{0}"; test -e "{0}"\'' - .format(gid)), - path=path, - chroot_fallback=True, - ignore_retcode=True) != 0: - ret['comment'] = 'Failed to set password marker' - changes[-1]['password'] += '. ' + ret['comment'] + '.' - ret['result'] = False + if ret.get("result", True): + changes.append({"password": "Password(s) updated"}) + if ( + retcode( + name, + ('sh -c \'touch "{0}"; test -e "{0}"\''.format(gid)), + path=path, + chroot_fallback=True, + ignore_retcode=True, + ) + != 0 + ): + ret["comment"] = "Failed to set password marker" + changes[-1]["password"] += ". " + ret["comment"] + "." + ret["result"] = False # set dns servers if any, only the first time - if ret.get('result', True) and dnsservers: + if ret.get("result", True) and dnsservers: # retro compatibility, test also old markers - gid = '/.lxc.initial_dns' - gids = [gid, - '/lxc.initial_dns', - '/lxc.{0}.initial_dns'.format(name)] - if not any(retcode(name, - 'test -e "{0}"'.format(x), - chroot_fallback=True, - path=path, - ignore_retcode=True) == 0 - for x in gids): + gid = "/.lxc.initial_dns" + gids = [gid, "/lxc.initial_dns", "/lxc.{0}.initial_dns".format(name)] + if not any( + retcode( + name, + 'test -e "{0}"'.format(x), + chroot_fallback=True, + path=path, + ignore_retcode=True, + ) + == 0 + for x in gids + ): try: - set_dns(name, - path=path, - dnsservers=dnsservers, - searchdomains=searchdomains) + set_dns( + name, path=path, dnsservers=dnsservers, searchdomains=searchdomains + ) except (SaltInvocationError, CommandExecutionError) as exc: - ret['comment'] = 'Failed to set DNS: ' + exc.strerror - ret['result'] = False + ret["comment"] = "Failed to set DNS: " + exc.strerror + ret["result"] = False else: - changes.append({'dns': 'DNS updated'}) - if retcode(name, - ('sh -c \'touch "{0}"; test -e "{0}"\'' - .format(gid)), - chroot_fallback=True, - path=path, - ignore_retcode=True) != 0: - ret['comment'] = 'Failed to set DNS marker' - changes[-1]['dns'] += '. ' + ret['comment'] + '.' - ret['result'] = False + changes.append({"dns": "DNS updated"}) + if ( + retcode( + name, + ('sh -c \'touch "{0}"; test -e "{0}"\''.format(gid)), + chroot_fallback=True, + path=path, + ignore_retcode=True, + ) + != 0 + ): + ret["comment"] = "Failed to set DNS marker" + changes[-1]["dns"] += ". " + ret["comment"] + "." + ret["result"] = False # retro compatibility, test also old markers if remove_seed_marker: - run(name, - 'rm -f \'{0}\''.format(SEED_MARKER), + run(name, "rm -f '{0}'".format(SEED_MARKER), path=path, python_shell=False) + gid = "/.lxc.initial_seed" + gids = [gid, "/lxc.initial_seed"] + if any( + retcode( + name, + "test -e {0}".format(x), path=path, - python_shell=False) - gid = '/.lxc.initial_seed' - gids = [gid, '/lxc.initial_seed'] - if ( - any(retcode(name, - 'test -e {0}'.format(x), - path=path, - chroot_fallback=True, - ignore_retcode=True) == 0 - for x in gids) or not ret.get('result', True) - ): + chroot_fallback=True, + ignore_retcode=True, + ) + == 0 + for x in gids + ) or not ret.get("result", True): pass elif seed or seed_cmd: if seed: try: result = bootstrap( - name, config=salt_config, path=path, + name, + config=salt_config, + path=path, approve_key=approve_key, - pub_key=pub_key, priv_key=priv_key, + pub_key=pub_key, + priv_key=priv_key, install=install, force_install=force_install, unconditional_install=unconditional_install, bootstrap_delay=bootstrap_delay, bootstrap_url=bootstrap_url, bootstrap_shell=bootstrap_shell, - bootstrap_args=bootstrap_args) + bootstrap_args=bootstrap_args, + ) except (SaltInvocationError, CommandExecutionError) as exc: - ret['comment'] = 'Bootstrap failed: ' + exc.strerror - ret['result'] = False + ret["comment"] = "Bootstrap failed: " + exc.strerror + ret["result"] = False else: if not result: - ret['comment'] = ('Bootstrap failed, see minion log for ' - 'more information') - ret['result'] = False - else: - changes.append( - {'bootstrap': 'Container successfully bootstrapped'} + ret["comment"] = ( + "Bootstrap failed, see minion log for " "more information" ) + ret["result"] = False + else: + changes.append({"bootstrap": "Container successfully bootstrapped"}) elif seed_cmd: try: - result = __salt__[seed_cmd](info(name, path=path)['rootfs'], - name, - salt_config) + result = __salt__[seed_cmd]( + info(name, path=path)["rootfs"], name, salt_config + ) except (SaltInvocationError, CommandExecutionError) as exc: - ret['comment'] = ('Bootstrap via seed_cmd \'{0}\' failed: {1}' - .format(seed_cmd, exc.strerror)) - ret['result'] = False + ret["comment"] = "Bootstrap via seed_cmd '{0}' failed: {1}".format( + seed_cmd, exc.strerror + ) + ret["result"] = False else: if not result: - ret['comment'] = ('Bootstrap via seed_cmd \'{0}\' failed, ' - 'see minion log for more information ' - .format(seed_cmd)) - ret['result'] = False + ret["comment"] = ( + "Bootstrap via seed_cmd '{0}' failed, " + "see minion log for more information ".format(seed_cmd) + ) + ret["result"] = False else: changes.append( - {'bootstrap': 'Container successfully bootstrapped ' - 'using seed_cmd \'{0}\'' - .format(seed_cmd)} + { + "bootstrap": "Container successfully bootstrapped " + "using seed_cmd '{0}'".format(seed_cmd) + } ) - if ret.get('result', True) and not start_: + if ret.get("result", True) and not start_: try: stop(name, path=path) except (SaltInvocationError, CommandExecutionError) as exc: - ret['comment'] = 'Unable to stop container: {0}'.format(exc) - ret['result'] = False + ret["comment"] = "Unable to stop container: {0}".format(exc) + ret["result"] = False state_post = state(name, path=path) if state_pre != state_post: - changes.append({'state': {'old': state_pre, 'new': state_post}}) + changes.append({"state": {"old": state_pre, "new": state_post}}) - if ret.get('result', True): - ret['comment'] = ('Container \'{0}\' successfully initialized' - .format(name)) - ret['result'] = True + if ret.get("result", True): + ret["comment"] = "Container '{0}' successfully initialized".format(name) + ret["result"] = True if changes: - ret['changes'] = changes_dict + ret["changes"] = changes_dict return ret def cloud_init(name, vm_=None, **kwargs): - ''' + """ Thin wrapper to lxc.init to be used from the saltcloud lxc driver name @@ -1657,14 +1744,14 @@ def cloud_init(name, vm_=None, **kwargs): .. code-block:: bash salt '*' lxc.cloud_init foo - ''' + """ init_interface = cloud_init_interface(name, vm_, **kwargs) - name = init_interface.pop('name', name) + name = init_interface.pop("name", name) return init(name, **init_interface) def images(dist=None): - ''' + """ .. versionadded:: 2015.5.0 List the available images for LXC's ``download`` template. @@ -1678,14 +1765,13 @@ def images(dist=None): salt myminion lxc.images salt myminion lxc.images dist=centos - ''' - out = __salt__['cmd.run_stdout']( - 'lxc-create -n __imgcheck -t download -- --list', - ignore_retcode=True + """ + out = __salt__["cmd.run_stdout"]( + "lxc-create -n __imgcheck -t download -- --list", ignore_retcode=True ) - if 'DIST' not in out: + if "DIST" not in out: raise CommandExecutionError( - 'Unable to run the \'download\' template script. Is it installed?' + "Unable to run the 'download' template script. Is it installed?" ) ret = {} @@ -1697,17 +1783,19 @@ def images(dist=None): continue if not passed_header: - if distro == 'DIST': + if distro == "DIST": passed_header = True continue dist_list = ret.setdefault(distro, []) - dist_list.append({ - 'release': release, - 'arch': arch, - 'variant': variant, - 'build_time': build_time, - }) + dist_list.append( + { + "release": release, + "arch": arch, + "variant": variant, + "build_time": build_time, + } + ) if dist is not None: return dict([(dist, ret.get(dist, []))]) @@ -1715,7 +1803,7 @@ def images(dist=None): def templates(): - ''' + """ .. versionadded:: 2015.5.0 List the available LXC template scripts installed on the minion @@ -1725,59 +1813,52 @@ def templates(): .. code-block:: bash salt myminion lxc.templates - ''' + """ try: - template_scripts = os.listdir('/usr/share/lxc/templates') + template_scripts = os.listdir("/usr/share/lxc/templates") except OSError: return [] else: - return [x[4:] for x in template_scripts if x.startswith('lxc-')] + return [x[4:] for x in template_scripts if x.startswith("lxc-")] -def _after_ignition_network_profile(cmd, - ret, - name, - network_profile, - path, - nic_opts): +def _after_ignition_network_profile(cmd, ret, name, network_profile, path, nic_opts): _clear_context() - if ret['retcode'] == 0 and exists(name, path=path): + if ret["retcode"] == 0 and exists(name, path=path): if network_profile: - network_changes = apply_network_profile(name, - network_profile, - path=path, - nic_opts=nic_opts) + network_changes = apply_network_profile( + name, network_profile, path=path, nic_opts=nic_opts + ) if network_changes: log.info( - 'Network changes from applying network profile \'%s\' ' - 'to newly-created container \'%s\':\n%s', - network_profile, name, network_changes + "Network changes from applying network profile '%s' " + "to newly-created container '%s':\n%s", + network_profile, + name, + network_changes, ) c_state = state(name, path=path) - return {'result': True, - 'state': {'old': None, 'new': c_state}} + return {"result": True, "state": {"old": None, "new": c_state}} else: if exists(name, path=path): # destroy the container if it was partially created - cmd = 'lxc-destroy' + cmd = "lxc-destroy" if path: - cmd += ' -P {0}'.format(pipes.quote(path)) - cmd += ' -n {0}'.format(name) - __salt__['cmd.retcode'](cmd, python_shell=False) + cmd += " -P {0}".format(pipes.quote(path)) + cmd += " -n {0}".format(name) + __salt__["cmd.retcode"](cmd, python_shell=False) raise CommandExecutionError( - 'Container could not be created with cmd \'{0}\': {1}' - .format(cmd, ret['stderr']) + "Container could not be created with cmd '{0}': {1}".format( + cmd, ret["stderr"] + ) ) -def create(name, - config=None, - profile=None, - network_profile=None, - nic_opts=None, - **kwargs): - ''' +def create( + name, config=None, profile=None, network_profile=None, nic_opts=None, **kwargs +): + """ Create a new container. name @@ -1869,11 +1950,11 @@ def create(name, Only applicable if ``backing=zfs``. (default: tank/lxc) .. versionadded:: 2015.8.0 - ''' + """ # Required params for 'download' template - download_template_deps = ('dist', 'release', 'arch') + download_template_deps = ("dist", "release", "arch") - cmd = 'lxc-create -n {0}'.format(name) + cmd = "lxc-create -n {0}".format(name) profile = get_container_profile(copy.deepcopy(profile)) kw_overrides = copy.deepcopy(kwargs) @@ -1887,110 +1968,95 @@ def create(name, return profile_match return kw_overrides_match - path = select('path') + path = select("path") if exists(name, path=path): - raise CommandExecutionError( - 'Container \'{0}\' already exists'.format(name) - ) + raise CommandExecutionError("Container '{0}' already exists".format(name)) - tvg = select('vgname') - vgname = tvg if tvg else __salt__['config.get']('lxc.vgname') + tvg = select("vgname") + vgname = tvg if tvg else __salt__["config.get"]("lxc.vgname") # The 'template' and 'image' params conflict - template = select('template') - image = select('image') + template = select("template") + image = select("image") if template and image: - raise SaltInvocationError( - 'Only one of \'template\' and \'image\' is permitted' - ) + raise SaltInvocationError("Only one of 'template' and 'image' is permitted") elif not any((template, image, profile)): raise SaltInvocationError( - 'At least one of \'template\', \'image\', and \'profile\' is ' - 'required' + "At least one of 'template', 'image', and 'profile' is " "required" ) - options = select('options') or {} - backing = select('backing') + options = select("options") or {} + backing = select("backing") if vgname and not backing: - backing = 'lvm' - lvname = select('lvname') - thinpool = select('thinpool') - fstype = select('fstype') - size = select('size', '1G') - zfsroot = select('zfsroot') - if backing in ('dir', 'overlayfs', 'btrfs', 'zfs'): + backing = "lvm" + lvname = select("lvname") + thinpool = select("thinpool") + fstype = select("fstype") + size = select("size", "1G") + zfsroot = select("zfsroot") + if backing in ("dir", "overlayfs", "btrfs", "zfs"): fstype = None size = None # some backends won't support some parameters - if backing in ('aufs', 'dir', 'overlayfs', 'btrfs'): + if backing in ("aufs", "dir", "overlayfs", "btrfs"): lvname = vgname = thinpool = None if image: - img_tar = __salt__['cp.cache_file'](image) + img_tar = __salt__["cp.cache_file"](image) template = os.path.join( - os.path.dirname(salt.__file__), - 'templates', - 'lxc', - 'salt_tarball') - options['imgtar'] = img_tar + os.path.dirname(salt.__file__), "templates", "lxc", "salt_tarball" + ) + options["imgtar"] = img_tar if path: - cmd += ' -P {0}'.format(pipes.quote(path)) + cmd += " -P {0}".format(pipes.quote(path)) if not os.path.exists(path): os.makedirs(path) if config: - cmd += ' -f {0}'.format(config) + cmd += " -f {0}".format(config) if template: - cmd += ' -t {0}'.format(template) + cmd += " -t {0}".format(template) if backing: backing = backing.lower() - cmd += ' -B {0}'.format(backing) - if backing in ('zfs',): + cmd += " -B {0}".format(backing) + if backing in ("zfs",): if zfsroot: - cmd += ' --zfsroot {0}'.format(zfsroot) - if backing in ('lvm',): + cmd += " --zfsroot {0}".format(zfsroot) + if backing in ("lvm",): if lvname: - cmd += ' --lvname {0}'.format(lvname) + cmd += " --lvname {0}".format(lvname) if vgname: - cmd += ' --vgname {0}'.format(vgname) + cmd += " --vgname {0}".format(vgname) if thinpool: - cmd += ' --thinpool {0}'.format(thinpool) - if backing not in ('dir', 'overlayfs'): + cmd += " --thinpool {0}".format(thinpool) + if backing not in ("dir", "overlayfs"): if fstype: - cmd += ' --fstype {0}'.format(fstype) + cmd += " --fstype {0}".format(fstype) if size: - cmd += ' --fssize {0}'.format(size) + cmd += " --fssize {0}".format(size) if options: - if template == 'download': - missing_deps = [x for x in download_template_deps - if x not in options] + if template == "download": + missing_deps = [x for x in download_template_deps if x not in options] if missing_deps: raise SaltInvocationError( - 'Missing params in \'options\' dict: {0}' - .format(', '.join(missing_deps)) + "Missing params in 'options' dict: {0}".format( + ", ".join(missing_deps) + ) ) - cmd += ' --' + cmd += " --" for key, val in six.iteritems(options): - cmd += ' --{0} {1}'.format(key, val) + cmd += " --{0} {1}".format(key, val) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) # please do not merge extra conflicting stuff # inside those two line (ret =, return) - return _after_ignition_network_profile(cmd, - ret, - name, - network_profile, - path, - nic_opts) + return _after_ignition_network_profile( + cmd, ret, name, network_profile, path, nic_opts + ) -def clone(name, - orig, - profile=None, - network_profile=None, - nic_opts=None, - **kwargs): - ''' +def clone(name, orig, profile=None, network_profile=None, nic_opts=None, **kwargs): + """ Create a new container as a clone of another container name @@ -2040,7 +2106,7 @@ def clone(name, salt '*' lxc.clone myclone orig=orig_container salt '*' lxc.clone myclone orig=orig_container snapshot=True - ''' + """ profile = get_container_profile(copy.deepcopy(profile)) kw_overrides = copy.deepcopy(kwargs) @@ -2052,62 +2118,57 @@ def clone(name, return profile_match return kw_overrides_match - path = select('path') + path = select("path") if exists(name, path=path): - raise CommandExecutionError( - 'Container \'{0}\' already exists'.format(name) - ) + raise CommandExecutionError("Container '{0}' already exists".format(name)) _ensure_exists(orig, path=path) - if state(orig, path=path) != 'stopped': + if state(orig, path=path) != "stopped": raise CommandExecutionError( - 'Container \'{0}\' must be stopped to be cloned'.format(orig) + "Container '{0}' must be stopped to be cloned".format(orig) ) - backing = select('backing') - snapshot = select('snapshot') - if backing in ('dir',): + backing = select("backing") + snapshot = select("snapshot") + if backing in ("dir",): snapshot = False if not snapshot: - snapshot = '' + snapshot = "" else: - snapshot = '-s' + snapshot = "-s" - size = select('size', '1G') - if backing in ('dir', 'overlayfs', 'btrfs'): + size = select("size", "1G") + if backing in ("dir", "overlayfs", "btrfs"): size = None # LXC commands and options changed in 2.0 - CF issue #34086 for details - if _LooseVersion(version()) >= _LooseVersion('2.0'): + if _LooseVersion(version()) >= _LooseVersion("2.0"): # https://linuxcontainers.org/lxc/manpages//man1/lxc-copy.1.html - cmd = 'lxc-copy' - cmd += ' {0} -n {1} -N {2}'.format(snapshot, orig, name) + cmd = "lxc-copy" + cmd += " {0} -n {1} -N {2}".format(snapshot, orig, name) else: # https://linuxcontainers.org/lxc/manpages//man1/lxc-clone.1.html - cmd = 'lxc-clone' - cmd += ' {0} -o {1} -n {2}'.format(snapshot, orig, name) + cmd = "lxc-clone" + cmd += " {0} -o {1} -n {2}".format(snapshot, orig, name) if path: - cmd += ' -P {0}'.format(pipes.quote(path)) + cmd += " -P {0}".format(pipes.quote(path)) if not os.path.exists(path): os.makedirs(path) if backing: backing = backing.lower() - cmd += ' -B {0}'.format(backing) - if backing not in ('dir', 'overlayfs'): + cmd += " -B {0}".format(backing) + if backing not in ("dir", "overlayfs"): if size: - cmd += ' -L {0}'.format(size) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd += " -L {0}".format(size) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) # please do not merge extra conflicting stuff # inside those two line (ret =, return) - return _after_ignition_network_profile(cmd, - ret, - name, - network_profile, - path, - nic_opts) + return _after_ignition_network_profile( + cmd, ret, name, network_profile, path, nic_opts + ) def ls_(active=None, cache=True, path=None): - ''' + """ Return a list of the containers available on the minion path @@ -2127,20 +2188,20 @@ def ls_(active=None, cache=True, path=None): salt '*' lxc.ls salt '*' lxc.ls active=True - ''' - contextvar = 'lxc.ls{0}'.format(path) + """ + contextvar = "lxc.ls{0}".format(path) if active: - contextvar += '.active' + contextvar += ".active" if cache and (contextvar in __context__): return __context__[contextvar] else: ret = [] - cmd = 'lxc-ls' + cmd = "lxc-ls" if path: - cmd += ' -P {0}'.format(pipes.quote(path)) + cmd += " -P {0}".format(pipes.quote(path)) if active: - cmd += ' --active' - output = __salt__['cmd.run_stdout'](cmd, python_shell=False) + cmd += " --active" + output = __salt__["cmd.run_stdout"](cmd, python_shell=False) for line in output.splitlines(): ret.extend(line.split()) __context__[contextvar] = ret @@ -2148,7 +2209,7 @@ def ls_(active=None, cache=True, path=None): def list_(extra=False, limit=None, path=None): - ''' + """ List containers classified by state extra @@ -2176,7 +2237,7 @@ def list_(extra=False, limit=None, path=None): salt '*' lxc.list salt '*' lxc.list extra=True salt '*' lxc.list limit=running - ''' + """ ctnrs = ls_(path=path) if extra: @@ -2188,24 +2249,18 @@ def list_(extra=False, limit=None, path=None): frozen = [] running = [] - ret = {'running': running, - 'stopped': stopped, - 'frozen': frozen} + ret = {"running": running, "stopped": stopped, "frozen": frozen} for container in ctnrs: - cmd = 'lxc-info' + cmd = "lxc-info" if path: - cmd += ' -P {0}'.format(pipes.quote(path)) - cmd += ' -n {0}'.format(container) - c_info = __salt__['cmd.run']( - cmd, - python_shell=False, - output_loglevel='debug' - ) + cmd += " -P {0}".format(pipes.quote(path)) + cmd += " -n {0}".format(container) + c_info = __salt__["cmd.run"](cmd, python_shell=False, output_loglevel="debug") c_state = None for line in c_info.splitlines(): - stat = line.split(':') - if stat[0] in ('State', 'state'): + stat = line.split(":") + if stat[0] in ("State", "state"): c_state = stat[1].strip() break @@ -2214,19 +2269,19 @@ def list_(extra=False, limit=None, path=None): if extra: infos = info(container, path=path) - method = 'update' + method = "update" value = {container: infos} else: - method = 'append' + method = "append" value = container - if c_state == 'STOPPED': + if c_state == "STOPPED": getattr(stopped, method)(value) continue - if c_state == 'FROZEN': + if c_state == "FROZEN": getattr(frozen, method)(value) continue - if c_state == 'RUNNING': + if c_state == "RUNNING": getattr(running, method)(value) continue @@ -2235,82 +2290,83 @@ def list_(extra=False, limit=None, path=None): return ret -def _change_state(cmd, - name, - expected, - stdin=_marker, - stdout=_marker, - stderr=_marker, - with_communicate=_marker, - use_vt=_marker, - path=None): +def _change_state( + cmd, + name, + expected, + stdin=_marker, + stdout=_marker, + stderr=_marker, + with_communicate=_marker, + use_vt=_marker, + path=None, +): pre = state(name, path=path) if pre == expected: - return {'result': True, - 'state': {'old': expected, 'new': expected}, - 'comment': 'Container \'{0}\' already {1}' - .format(name, expected)} + return { + "result": True, + "state": {"old": expected, "new": expected}, + "comment": "Container '{0}' already {1}".format(name, expected), + } - if cmd == 'lxc-destroy': + if cmd == "lxc-destroy": # Kill the container first - scmd = 'lxc-stop' + scmd = "lxc-stop" if path: - scmd += ' -P {0}'.format(pipes.quote(path)) - scmd += ' -k -n {0}'.format(name) - __salt__['cmd.run'](scmd, - python_shell=False) + scmd += " -P {0}".format(pipes.quote(path)) + scmd += " -k -n {0}".format(name) + __salt__["cmd.run"](scmd, python_shell=False) - if path and ' -P ' not in cmd: - cmd += ' -P {0}'.format(pipes.quote(path)) - cmd += ' -n {0}'.format(name) + if path and " -P " not in cmd: + cmd += " -P {0}".format(pipes.quote(path)) + cmd += " -n {0}".format(name) # certain lxc commands need to be taken with care (lxc-start) # as te command itself mess with double forks; we must not # communicate with it, but just wait for the exit status - pkwargs = {'python_shell': False, - 'redirect_stderr': True, - 'with_communicate': with_communicate, - 'use_vt': use_vt, - 'stdin': stdin, - 'stdout': stdout} + pkwargs = { + "python_shell": False, + "redirect_stderr": True, + "with_communicate": with_communicate, + "use_vt": use_vt, + "stdin": stdin, + "stdout": stdout, + } for i in [a for a in pkwargs]: val = pkwargs[i] if val is _marker: pkwargs.pop(i, None) - _cmdout = __salt__['cmd.run_all'](cmd, **pkwargs) + _cmdout = __salt__["cmd.run_all"](cmd, **pkwargs) - if _cmdout['retcode'] != 0: + if _cmdout["retcode"] != 0: raise CommandExecutionError( - 'Error changing state for container \'{0}\' using command ' - '\'{1}\': {2}'.format(name, cmd, _cmdout['stdout']) + "Error changing state for container '{0}' using command " + "'{1}': {2}".format(name, cmd, _cmdout["stdout"]) ) if expected is not None: # some commands do not wait, so we will - rcmd = 'lxc-wait' + rcmd = "lxc-wait" if path: - rcmd += ' -P {0}'.format(pipes.quote(path)) - rcmd += ' -n {0} -s {1}'.format(name, expected.upper()) - __salt__['cmd.run'](rcmd, python_shell=False, timeout=30) + rcmd += " -P {0}".format(pipes.quote(path)) + rcmd += " -n {0} -s {1}".format(name, expected.upper()) + __salt__["cmd.run"](rcmd, python_shell=False, timeout=30) _clear_context() post = state(name, path=path) - ret = {'result': post == expected, - 'state': {'old': pre, 'new': post}} + ret = {"result": post == expected, "state": {"old": pre, "new": post}} return ret def _ensure_exists(name, path=None): - ''' + """ Raise an exception if the container does not exist - ''' + """ if not exists(name, path=path): - raise CommandExecutionError( - 'Container \'{0}\' does not exist'.format(name) - ) + raise CommandExecutionError("Container '{0}' does not exist".format(name)) def _ensure_running(name, no_start=False, path=None): - ''' + """ If the container is not currently running, start it. This function returns the state that the container was in before changing @@ -2319,29 +2375,25 @@ def _ensure_running(name, no_start=False, path=None): default: /var/lib/lxc (system) .. versionadded:: 2015.8.0 - ''' + """ _ensure_exists(name, path=path) pre = state(name, path=path) - if pre == 'running': + if pre == "running": # This will be a no-op but running the function will give us a pretty # return dict. return start(name, path=path) - elif pre == 'stopped': + elif pre == "stopped": if no_start: - raise CommandExecutionError( - 'Container \'{0}\' is not running'.format(name) - ) + raise CommandExecutionError("Container '{0}' is not running".format(name)) return start(name, path=path) - elif pre == 'frozen': + elif pre == "frozen": if no_start: - raise CommandExecutionError( - 'Container \'{0}\' is not running'.format(name) - ) + raise CommandExecutionError("Container '{0}' is not running".format(name)) return unfreeze(name, path=path) def restart(name, path=None, lxc_config=None, force=False): - ''' + """ .. versionadded:: 2015.5.0 Restart the named container. If the container was not running, the @@ -2371,20 +2423,20 @@ def restart(name, path=None, lxc_config=None, force=False): .. code-block:: bash salt myminion lxc.restart name - ''' + """ _ensure_exists(name, path=path) orig_state = state(name, path=path) - if orig_state != 'stopped': + if orig_state != "stopped": stop(name, kill=force, path=path) ret = start(name, path=path, lxc_config=lxc_config) - ret['state']['old'] = orig_state - if orig_state != 'stopped': - ret['restarted'] = True + ret["state"]["old"] = orig_state + if orig_state != "stopped": + ret["restarted"] = True return ret def start(name, **kwargs): - ''' + """ Start the named container path @@ -2409,36 +2461,40 @@ def start(name, **kwargs): .. code-block:: bash salt myminion lxc.start name - ''' - path = kwargs.get('path', None) + """ + path = kwargs.get("path", None) cpath = get_root_path(path) - lxc_config = kwargs.get('lxc_config', None) - cmd = 'lxc-start' + lxc_config = kwargs.get("lxc_config", None) + cmd = "lxc-start" if not lxc_config: - lxc_config = os.path.join(cpath, name, 'config') + lxc_config = os.path.join(cpath, name, "config") # we try to start, even without config, if global opts are there if os.path.exists(lxc_config): - cmd += ' -f {0}'.format(pipes.quote(lxc_config)) - cmd += ' -d' + cmd += " -f {0}".format(pipes.quote(lxc_config)) + cmd += " -d" _ensure_exists(name, path=path) - if state(name, path=path) == 'frozen': + if state(name, path=path) == "frozen": raise CommandExecutionError( - 'Container \'{0}\' is frozen, use lxc.unfreeze'.format(name) + "Container '{0}' is frozen, use lxc.unfreeze".format(name) ) # lxc-start daemonize itself violently, we must not communicate with it - use_vt = kwargs.get('use_vt', None) - with_communicate = kwargs.get('with_communicate', False) - return _change_state(cmd, name, 'running', - stdout=None, - stderr=None, - stdin=None, - with_communicate=with_communicate, - path=path, - use_vt=use_vt) + use_vt = kwargs.get("use_vt", None) + with_communicate = kwargs.get("with_communicate", False) + return _change_state( + cmd, + name, + "running", + stdout=None, + stderr=None, + stdin=None, + with_communicate=with_communicate, + path=path, + use_vt=use_vt, + ) def stop(name, kill=False, path=None, use_vt=None): - ''' + """ Stop the named container path @@ -2465,24 +2521,24 @@ def stop(name, kill=False, path=None, use_vt=None): .. code-block:: bash salt myminion lxc.stop name - ''' + """ _ensure_exists(name, path=path) orig_state = state(name, path=path) - if orig_state == 'frozen' and not kill: + if orig_state == "frozen" and not kill: # Gracefully stopping a frozen container is slower than unfreezing and # then stopping it (at least in my testing), so if we're not # force-stopping the container, unfreeze it first. unfreeze(name, path=path) - cmd = 'lxc-stop' + cmd = "lxc-stop" if kill: - cmd += ' -k' - ret = _change_state(cmd, name, 'stopped', use_vt=use_vt, path=path) - ret['state']['old'] = orig_state + cmd += " -k" + ret = _change_state(cmd, name, "stopped", use_vt=use_vt, path=path) + ret["state"]["old"] = orig_state return ret def freeze(name, **kwargs): - ''' + """ Freeze the named container path @@ -2507,31 +2563,29 @@ def freeze(name, **kwargs): .. code-block:: bash salt '*' lxc.freeze name - ''' - use_vt = kwargs.get('use_vt', None) - path = kwargs.get('path', None) + """ + use_vt = kwargs.get("use_vt", None) + path = kwargs.get("path", None) _ensure_exists(name, path=path) orig_state = state(name, path=path) - start_ = kwargs.get('start', False) - if orig_state == 'stopped': + start_ = kwargs.get("start", False) + if orig_state == "stopped": if not start_: - raise CommandExecutionError( - 'Container \'{0}\' is stopped'.format(name) - ) + raise CommandExecutionError("Container '{0}' is stopped".format(name)) start(name, path=path) - cmd = 'lxc-freeze' + cmd = "lxc-freeze" if path: - cmd += ' -P {0}'.format(pipes.quote(path)) - ret = _change_state(cmd, name, 'frozen', use_vt=use_vt, path=path) - if orig_state == 'stopped' and start_: - ret['state']['old'] = orig_state - ret['started'] = True - ret['state']['new'] = state(name, path=path) + cmd += " -P {0}".format(pipes.quote(path)) + ret = _change_state(cmd, name, "frozen", use_vt=use_vt, path=path) + if orig_state == "stopped" and start_: + ret["state"]["old"] = orig_state + ret["started"] = True + ret["state"]["new"] = state(name, path=path) return ret def unfreeze(name, path=None, use_vt=None): - ''' + """ Unfreeze the named container. path @@ -2550,20 +2604,18 @@ def unfreeze(name, path=None, use_vt=None): .. code-block:: bash salt '*' lxc.unfreeze name - ''' + """ _ensure_exists(name, path=path) - if state(name, path=path) == 'stopped': - raise CommandExecutionError( - 'Container \'{0}\' is stopped'.format(name) - ) - cmd = 'lxc-unfreeze' + if state(name, path=path) == "stopped": + raise CommandExecutionError("Container '{0}' is stopped".format(name)) + cmd = "lxc-unfreeze" if path: - cmd += ' -P {0}'.format(pipes.quote(path)) - return _change_state(cmd, name, 'running', path=path, use_vt=use_vt) + cmd += " -P {0}".format(pipes.quote(path)) + return _change_state(cmd, name, "running", path=path, use_vt=use_vt) def destroy(name, stop=False, path=None): - ''' + """ Destroy the named container. .. warning:: @@ -2591,21 +2643,19 @@ def destroy(name, stop=False, path=None): salt '*' lxc.destroy foo salt '*' lxc.destroy foo stop=True - ''' + """ _ensure_exists(name, path=path) - if not stop and state(name, path=path) != 'stopped': - raise CommandExecutionError( - 'Container \'{0}\' is not stopped'.format(name) - ) - return _change_state('lxc-destroy', name, None, path=path) + if not stop and state(name, path=path) != "stopped": + raise CommandExecutionError("Container '{0}' is not stopped".format(name)) + return _change_state("lxc-destroy", name, None, path=path) # Compatibility between LXC and nspawn -remove = salt.utils.functools.alias_function(destroy, 'remove') +remove = salt.utils.functools.alias_function(destroy, "remove") def exists(name, path=None): - ''' + """ Returns whether the named container exists. path @@ -2619,7 +2669,7 @@ def exists(name, path=None): .. code-block:: bash salt '*' lxc.exists name - ''' + """ _exists = name in ls_(path=path) # container may be just created but we did cached earlier the @@ -2630,7 +2680,7 @@ def exists(name, path=None): def state(name, path=None): - ''' + """ Returns the state of a container. path @@ -2643,31 +2693,31 @@ def state(name, path=None): .. code-block:: bash salt '*' lxc.state name - ''' + """ # Don't use _ensure_exists() here, it will mess with _change_state() - cachekey = 'lxc.state.{0}{1}'.format(name, path) + cachekey = "lxc.state.{0}{1}".format(name, path) try: return __context__[cachekey] except KeyError: if not exists(name, path=path): __context__[cachekey] = None else: - cmd = 'lxc-info' + cmd = "lxc-info" if path: - cmd += ' -P {0}'.format(pipes.quote(path)) - cmd += ' -n {0}'.format(name) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) - if ret['retcode'] != 0: + cmd += " -P {0}".format(pipes.quote(path)) + cmd += " -n {0}".format(name) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) + if ret["retcode"] != 0: _clear_context() raise CommandExecutionError( - 'Unable to get state of container \'{0}\''.format(name) + "Unable to get state of container '{0}'".format(name) ) - c_infos = ret['stdout'].splitlines() + c_infos = ret["stdout"].splitlines() c_state = None for c_info in c_infos: - stat = c_info.split(':') - if stat[0].lower() == 'state': + stat = c_info.split(":") + if stat[0].lower() == "state": c_state = stat[1].strip().lower() break __context__[cachekey] = c_state @@ -2675,7 +2725,7 @@ def state(name, path=None): def get_parameter(name, parameter, path=None): - ''' + """ Returns the value of a cgroup parameter for a container path @@ -2689,22 +2739,22 @@ def get_parameter(name, parameter, path=None): .. code-block:: bash salt '*' lxc.get_parameter container_name memory.limit_in_bytes - ''' + """ _ensure_exists(name, path=path) - cmd = 'lxc-cgroup' + cmd = "lxc-cgroup" if path: - cmd += ' -P {0}'.format(pipes.quote(path)) - cmd += ' -n {0} {1}'.format(name, parameter) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) - if ret['retcode'] != 0: + cmd += " -P {0}".format(pipes.quote(path)) + cmd += " -n {0} {1}".format(name, parameter) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) + if ret["retcode"] != 0: raise CommandExecutionError( - 'Unable to retrieve value for \'{0}\''.format(parameter) + "Unable to retrieve value for '{0}'".format(parameter) ) - return ret['stdout'].strip() + return ret["stdout"].strip() def set_parameter(name, parameter, value, path=None): - ''' + """ Set the value of a cgroup parameter for a container. path @@ -2718,23 +2768,23 @@ def set_parameter(name, parameter, value, path=None): .. code-block:: bash salt '*' lxc.set_parameter name parameter value - ''' + """ if not exists(name, path=path): return None - cmd = 'lxc-cgroup' + cmd = "lxc-cgroup" if path: - cmd += ' -P {0}'.format(pipes.quote(path)) - cmd += ' -n {0} {1} {2}'.format(name, parameter, value) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) - if ret['retcode'] != 0: + cmd += " -P {0}".format(pipes.quote(path)) + cmd += " -n {0} {1} {2}".format(name, parameter, value) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) + if ret["retcode"] != 0: return False else: return True def info(name, path=None): - ''' + """ Returns information about a container path @@ -2748,21 +2798,21 @@ def info(name, path=None): .. code-block:: bash salt '*' lxc.info name - ''' - cachekey = 'lxc.info.{0}{1}'.format(name, path) + """ + cachekey = "lxc.info.{0}{1}".format(name, path) try: return __context__[cachekey] except KeyError: _ensure_exists(name, path=path) cpath = get_root_path(path) try: - conf_file = os.path.join(cpath, name, 'config') + conf_file = os.path.join(cpath, name, "config") except AttributeError: - conf_file = os.path.join(cpath, six.text_type(name), 'config') + conf_file = os.path.join(cpath, six.text_type(name), "config") if not os.path.isfile(conf_file): raise CommandExecutionError( - 'LXC config file {0} does not exist'.format(conf_file) + "LXC config file {0} does not exist".format(conf_file) ) ret = {} @@ -2770,8 +2820,7 @@ def info(name, path=None): with salt.utils.files.fopen(conf_file) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - comps = [x.strip() for x in - line.split('#', 1)[0].strip().split('=', 1)] + comps = [x.strip() for x in line.split("#", 1)[0].strip().split("=", 1)] if len(comps) == 2: config.append(tuple(comps)) @@ -2779,108 +2828,96 @@ def info(name, path=None): current = {} for key, val in config: - if key == 'lxc.network.type': - current = {'type': val} + if key == "lxc.network.type": + current = {"type": val} ifaces.append(current) elif not current: continue - elif key.startswith('lxc.network.'): - current[key.replace('lxc.network.', '', 1)] = val + elif key.startswith("lxc.network."): + current[key.replace("lxc.network.", "", 1)] = val if ifaces: - ret['nics'] = ifaces + ret["nics"] = ifaces - ret['rootfs'] = next( - (x[1] for x in config if x[0] == 'lxc.rootfs'), - None - ) - ret['state'] = state(name, path=path) - ret['ips'] = [] - ret['public_ips'] = [] - ret['private_ips'] = [] - ret['public_ipv4_ips'] = [] - ret['public_ipv6_ips'] = [] - ret['private_ipv4_ips'] = [] - ret['private_ipv6_ips'] = [] - ret['ipv4_ips'] = [] - ret['ipv6_ips'] = [] - ret['size'] = None - ret['config'] = conf_file + ret["rootfs"] = next((x[1] for x in config if x[0] == "lxc.rootfs"), None) + ret["state"] = state(name, path=path) + ret["ips"] = [] + ret["public_ips"] = [] + ret["private_ips"] = [] + ret["public_ipv4_ips"] = [] + ret["public_ipv6_ips"] = [] + ret["private_ipv4_ips"] = [] + ret["private_ipv6_ips"] = [] + ret["ipv4_ips"] = [] + ret["ipv6_ips"] = [] + ret["size"] = None + ret["config"] = conf_file - if ret['state'] == 'running': + if ret["state"] == "running": try: - limit = int(get_parameter(name, 'memory.limit_in_bytes')) + limit = int(get_parameter(name, "memory.limit_in_bytes")) except (CommandExecutionError, TypeError, ValueError): limit = 0 try: - usage = int(get_parameter(name, 'memory.usage_in_bytes')) + usage = int(get_parameter(name, "memory.usage_in_bytes")) except (CommandExecutionError, TypeError, ValueError): usage = 0 free = limit - usage - ret['memory_limit'] = limit - ret['memory_free'] = free - size = run_stdout(name, 'df /', path=path, python_shell=False) + ret["memory_limit"] = limit + ret["memory_free"] = free + size = run_stdout(name, "df /", path=path, python_shell=False) # The size is the 2nd column of the last line - ret['size'] = size.splitlines()[-1].split()[1] + ret["size"] = size.splitlines()[-1].split()[1] # First try iproute2 - ip_cmd = run_all( - name, 'ip link show', path=path, python_shell=False) - if ip_cmd['retcode'] == 0: - ip_data = ip_cmd['stdout'] - ip_cmd = run_all( - name, 'ip addr show', path=path, python_shell=False) - ip_data += '\n' + ip_cmd['stdout'] + ip_cmd = run_all(name, "ip link show", path=path, python_shell=False) + if ip_cmd["retcode"] == 0: + ip_data = ip_cmd["stdout"] + ip_cmd = run_all(name, "ip addr show", path=path, python_shell=False) + ip_data += "\n" + ip_cmd["stdout"] ip_data = salt.utils.network._interfaces_ip(ip_data) else: # That didn't work, try ifconfig - ip_cmd = run_all( - name, 'ifconfig', path=path, python_shell=False) - if ip_cmd['retcode'] == 0: - ip_data = \ - salt.utils.network._interfaces_ifconfig( - ip_cmd['stdout']) + ip_cmd = run_all(name, "ifconfig", path=path, python_shell=False) + if ip_cmd["retcode"] == 0: + ip_data = salt.utils.network._interfaces_ifconfig(ip_cmd["stdout"]) else: # Neither was successful, give up - log.warning( - 'Unable to run ip or ifconfig in container \'%s\'', name - ) + log.warning("Unable to run ip or ifconfig in container '%s'", name) ip_data = {} - ret['ipv4_ips'] = salt.utils.network.ip_addrs( - include_loopback=True, - interface_data=ip_data + ret["ipv4_ips"] = salt.utils.network.ip_addrs( + include_loopback=True, interface_data=ip_data ) - ret['ipv6_ips'] = salt.utils.network.ip_addrs6( - include_loopback=True, - interface_data=ip_data + ret["ipv6_ips"] = salt.utils.network.ip_addrs6( + include_loopback=True, interface_data=ip_data ) - ret['ips'] = ret['ipv4_ips'] + ret['ipv6_ips'] - for address in ret['ipv4_ips']: - if address == '127.0.0.1': - ret['private_ips'].append(address) - ret['private_ipv4_ips'].append(address) + ret["ips"] = ret["ipv4_ips"] + ret["ipv6_ips"] + for address in ret["ipv4_ips"]: + if address == "127.0.0.1": + ret["private_ips"].append(address) + ret["private_ipv4_ips"].append(address) elif salt.utils.cloud.is_public_ip(address): - ret['public_ips'].append(address) - ret['public_ipv4_ips'].append(address) + ret["public_ips"].append(address) + ret["public_ipv4_ips"].append(address) else: - ret['private_ips'].append(address) - ret['private_ipv4_ips'].append(address) - for address in ret['ipv6_ips']: - if address == '::1' or address.startswith('fe80'): - ret['private_ips'].append(address) - ret['private_ipv6_ips'].append(address) + ret["private_ips"].append(address) + ret["private_ipv4_ips"].append(address) + for address in ret["ipv6_ips"]: + if address == "::1" or address.startswith("fe80"): + ret["private_ips"].append(address) + ret["private_ipv6_ips"].append(address) else: - ret['public_ips'].append(address) - ret['public_ipv6_ips'].append(address) + ret["public_ips"].append(address) + ret["public_ipv6_ips"].append(address) - for key in [x for x in ret if x == 'ips' or x.endswith('ips')]: + for key in [x for x in ret if x == "ips" or x.endswith("ips")]: ret[key].sort(key=_ip_sort) __context__[cachekey] = ret return __context__[cachekey] def set_password(name, users, password, encrypted=True, path=None): - ''' + """ .. versionchanged:: 2015.5.0 Function renamed from ``set_pass`` to ``set_password``. Additionally, this function now supports (and defaults to using) a password hash @@ -2914,13 +2951,14 @@ def set_password(name, users, password, encrypted=True, path=None): salt '*' lxc.set_pass container-name root '$6$uJ2uAyLU$KoI67t8As/0fXtJOPcHKGXmUpcoYUcVR2K6x93walnShTCQvjRwq25yIkiCBOqgbfdKQSFnAo28/ek6716vEV1' salt '*' lxc.set_pass container-name root foo encrypted=False - ''' + """ + def _bad_user_input(): - raise SaltInvocationError('Invalid input for \'users\' parameter') + raise SaltInvocationError("Invalid input for 'users' parameter") if not isinstance(users, list): try: - users = users.split(',') + users = users.split(",") except AttributeError: _bad_user_input() if not users: @@ -2928,28 +2966,31 @@ def set_password(name, users, password, encrypted=True, path=None): failed_users = [] for user in users: - result = retcode(name, - 'chpasswd{0}'.format(' -e' if encrypted else ''), - stdin=':'.join((user, password)), - python_shell=False, - path=path, - chroot_fallback=True, - output_loglevel='quiet') + result = retcode( + name, + "chpasswd{0}".format(" -e" if encrypted else ""), + stdin=":".join((user, password)), + python_shell=False, + path=path, + chroot_fallback=True, + output_loglevel="quiet", + ) if result != 0: failed_users.append(user) if failed_users: raise CommandExecutionError( - 'Password change failed for the following user(s): {0}' - .format(', '.join(failed_users)) + "Password change failed for the following user(s): {0}".format( + ", ".join(failed_users) + ) ) return True -set_pass = salt.utils.functools.alias_function(set_password, 'set_pass') +set_pass = salt.utils.functools.alias_function(set_password, "set_pass") def update_lxc_conf(name, lxc_conf, lxc_conf_unset, path=None): - ''' + """ Edit LXC configuration options path @@ -2966,37 +3007,36 @@ def update_lxc_conf(name, lxc_conf, lxc_conf_unset, path=None): lxc_conf="[{'network.ipv4.ip':'10.0.3.5'}]" \\ lxc_conf_unset="['lxc.utsname']" - ''' + """ _ensure_exists(name, path=path) cpath = get_root_path(path) - lxc_conf_p = os.path.join(cpath, name, 'config') + lxc_conf_p = os.path.join(cpath, name, "config") if not os.path.exists(lxc_conf_p): raise SaltInvocationError( - 'Configuration file {0} does not exist'.format(lxc_conf_p) + "Configuration file {0} does not exist".format(lxc_conf_p) ) - changes = {'edited': [], 'added': [], 'removed': []} - ret = {'changes': changes, 'result': True, 'comment': ''} + changes = {"edited": [], "added": [], "removed": []} + ret = {"changes": changes, "result": True, "comment": ""} # do not use salt.utils.files.fopen ! - with salt.utils.files.fopen(lxc_conf_p, 'r') as fic: + with salt.utils.files.fopen(lxc_conf_p, "r") as fic: filtered_lxc_conf = [] for row in lxc_conf: if not row: continue for conf in row: - filtered_lxc_conf.append((conf.strip(), - row[conf].strip())) - ret['comment'] = 'lxc.conf is up to date' + filtered_lxc_conf.append((conf.strip(), row[conf].strip())) + ret["comment"] = "lxc.conf is up to date" lines = [] orig_config = salt.utils.stringutils.to_unicode(fic.read()) for line in orig_config.splitlines(): - if line.startswith('#') or not line.strip(): - lines.append([line, '']) + if line.startswith("#") or not line.strip(): + lines.append([line, ""]) else: - line = line.split('=') + line = line.split("=") index = line.pop(0) - val = (index.strip(), '='.join(line).strip()) + val = (index.strip(), "=".join(line).strip()) if val not in lines: lines.append(val) for key, item in filtered_lxc_conf: @@ -3005,54 +3045,52 @@ def update_lxc_conf(name, lxc_conf, lxc_conf_unset, path=None): if line[0] == key: matched = True lines[idx] = (key, item) - if '='.join(line[1:]).strip() != item.strip(): - changes['edited'].append( - ({line[0]: line[1:]}, {key: item})) + if "=".join(line[1:]).strip() != item.strip(): + changes["edited"].append(({line[0]: line[1:]}, {key: item})) break if not matched: if (key, item) not in lines: lines.append((key, item)) - changes['added'].append({key: item}) + changes["added"].append({key: item}) dest_lxc_conf = [] # filter unset if lxc_conf_unset: for line in lines: for opt in lxc_conf_unset: - if ( - not line[0].startswith(opt) and - line not in dest_lxc_conf - ): + if not line[0].startswith(opt) and line not in dest_lxc_conf: dest_lxc_conf.append(line) else: - changes['removed'].append(opt) + changes["removed"].append(opt) else: dest_lxc_conf = lines - conf = '' + conf = "" for key, val in dest_lxc_conf: if not val: - conf += '{0}\n'.format(key) + conf += "{0}\n".format(key) else: - conf += '{0} = {1}\n'.format(key.strip(), val.strip()) + conf += "{0} = {1}\n".format(key.strip(), val.strip()) conf_changed = conf != orig_config - chrono = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') + chrono = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S") if conf_changed: # DO NOT USE salt.utils.files.fopen here, i got (kiorky) # problems with lxc configs which were wiped ! - with salt.utils.files.fopen('{0}.{1}'.format(lxc_conf_p, chrono), 'w') as wfic: + with salt.utils.files.fopen( + "{0}.{1}".format(lxc_conf_p, chrono), "w" + ) as wfic: wfic.write(salt.utils.stringutils.to_str(conf)) - with salt.utils.files.fopen(lxc_conf_p, 'w') as wfic: + with salt.utils.files.fopen(lxc_conf_p, "w") as wfic: wfic.write(salt.utils.stringutils.to_str(conf)) - ret['comment'] = 'Updated' - ret['result'] = True + ret["comment"] = "Updated" + ret["result"] = True if not any(changes[x] for x in changes): # Ensure an empty changes dict if nothing was modified - ret['changes'] = {} + ret["changes"] = {} return ret def set_dns(name, dnsservers=None, searchdomains=None, path=None): - ''' + """ .. versionchanged:: 2015.5.0 The ``dnsservers`` and ``searchdomains`` parameters can now be passed as a comma-separated list. @@ -3072,28 +3110,24 @@ def set_dns(name, dnsservers=None, searchdomains=None, path=None): salt myminion lxc.set_dns ubuntu "['8.8.8.8', '4.4.4.4']" - ''' + """ if dnsservers is None: - dnsservers = ['8.8.8.8', '4.4.4.4'] + dnsservers = ["8.8.8.8", "4.4.4.4"] elif not isinstance(dnsservers, list): try: - dnsservers = dnsservers.split(',') + dnsservers = dnsservers.split(",") except AttributeError: - raise SaltInvocationError( - 'Invalid input for \'dnsservers\' parameter' - ) + raise SaltInvocationError("Invalid input for 'dnsservers' parameter") if searchdomains is None: searchdomains = [] elif not isinstance(searchdomains, list): try: - searchdomains = searchdomains.split(',') + searchdomains = searchdomains.split(",") except AttributeError: - raise SaltInvocationError( - 'Invalid input for \'searchdomains\' parameter' - ) - dns = ['nameserver {0}'.format(x) for x in dnsservers] - dns.extend(['search {0}'.format(x) for x in searchdomains]) - dns = '\n'.join(dns) + '\n' + raise SaltInvocationError("Invalid input for 'searchdomains' parameter") + dns = ["nameserver {0}".format(x) for x in dnsservers] + dns.extend(["search {0}".format(x) for x in searchdomains]) + dns = "\n".join(dns) + "\n" # we may be using resolvconf in the container # We need to handle that case with care: # - we create the resolv.conf runtime directory (the @@ -3105,50 +3139,58 @@ def set_dns(name, dnsservers=None, searchdomains=None, path=None): # operation. # - We also teach resolvconf to use the aforementioned dns. # - We finally also set /etc/resolv.conf in all cases - rstr = __salt__['test.random_hash']() + rstr = __salt__["test.random_hash"]() # no tmp here, apparmor won't let us execute ! - script = '/sbin/{0}_dns.sh'.format(rstr) - DNS_SCRIPT = "\n".join([ - # 'set -x', - '#!/usr/bin/env bash', - 'if [ -h /etc/resolv.conf ];then', - ' if [ "x$(readlink /etc/resolv.conf)"' - ' = "x../run/resolvconf/resolv.conf" ];then', - ' if [ ! -d /run/resolvconf/ ];then', - ' mkdir -p /run/resolvconf', - ' fi', - ' cat > /etc/resolvconf/resolv.conf.d/head < /etc/resolv.conf < /etc/resolvconf/resolv.conf.d/head < /etc/resolv.conf </dev/null 2>/dev/null;then exit 2;fi @@ -3194,38 +3236,45 @@ def running_systemd(name, cache=True, path=None): done if test -d /var/systemd/system;then exit 0;fi exit 2 - ''') + """ + ) result = run_all( - name, 'tee {0}'.format(script), path=path, - stdin=_script, python_shell=True) - if result['retcode'] == 0: - result = run_all(name, - 'sh -c "chmod +x {0};{0}"'''.format(script), - path=path, - python_shell=True) + name, "tee {0}".format(script), path=path, stdin=_script, python_shell=True + ) + if result["retcode"] == 0: + result = run_all( + name, + 'sh -c "chmod +x {0};{0}"' "".format(script), + path=path, + python_shell=True, + ) else: raise CommandExecutionError( - 'lxc {0} failed to copy initd tester'.format(name)) - run_all(name, - 'sh -c \'if [ -f "{0}" ];then rm -f "{0}";fi\'' - ''.format(script), - path=path, - ignore_retcode=True, - python_shell=True) - if result['retcode'] != 0: - error = ('Unable to determine if the container \'{0}\'' - ' was running systemd, assmuming it is not.' - ''.format(name)) - if result['stderr']: - error += ': {0}'.format(result['stderr']) + "lxc {0} failed to copy initd tester".format(name) + ) + run_all( + name, + 'sh -c \'if [ -f "{0}" ];then rm -f "{0}";fi\'' "".format(script), + path=path, + ignore_retcode=True, + python_shell=True, + ) + if result["retcode"] != 0: + error = ( + "Unable to determine if the container '{0}'" + " was running systemd, assmuming it is not." + "".format(name) + ) + if result["stderr"]: + error += ": {0}".format(result["stderr"]) # only cache result if we got a known exit code - if result['retcode'] in (0, 2): - __context__[k] = ret = not result['retcode'] + if result["retcode"] in (0, 2): + __context__[k] = ret = not result["retcode"] return ret def systemd_running_state(name, path=None): - ''' + """ Get the operational state of a systemd based container path @@ -3240,20 +3289,19 @@ def systemd_running_state(name, path=None): salt myminion lxc.systemd_running_state ubuntu - ''' + """ try: - ret = run_all(name, - 'systemctl is-system-running', - path=path, - ignore_retcode=True)['stdout'] + ret = run_all( + name, "systemctl is-system-running", path=path, ignore_retcode=True + )["stdout"] except CommandExecutionError: - ret = '' + ret = "" return ret def test_sd_started_state(name, path=None): - ''' + """ Test if a systemd container is fully started path @@ -3270,18 +3318,18 @@ def test_sd_started_state(name, path=None): salt myminion lxc.test_sd_started_state ubuntu - ''' + """ qstate = systemd_running_state(name, path=path) - if qstate in ('initializing', 'starting'): + if qstate in ("initializing", "starting"): return False - elif qstate == '': + elif qstate == "": return None else: return True def test_bare_started_state(name, path=None): - ''' + """ Test if a non systemd container is fully started For now, it consists only to test if the container is attachable @@ -3298,18 +3346,16 @@ def test_bare_started_state(name, path=None): salt myminion lxc.test_bare_started_state ubuntu - ''' + """ try: - ret = run_all( - name, 'ls', path=path, ignore_retcode=True - )['retcode'] == 0 + ret = run_all(name, "ls", path=path, ignore_retcode=True)["retcode"] == 0 except (CommandExecutionError,): ret = None return ret def wait_started(name, path=None, timeout=300): - ''' + """ Check that the system has fully inited This is actually very important for systemD based containers @@ -3328,13 +3374,11 @@ def wait_started(name, path=None, timeout=300): salt myminion lxc.wait_started ubuntu - ''' + """ if not exists(name, path=path): - raise CommandExecutionError( - 'Container {0} does does exists'.format(name)) - if not state(name, path=path) == 'running': - raise CommandExecutionError( - 'Container {0} is not running'.format(name)) + raise CommandExecutionError("Container {0} does does exists".format(name)) + if not state(name, path=path) == "running": + raise CommandExecutionError("Container {0} is not running".format(name)) ret = False if running_systemd(name, path=path): test_started = test_sd_started_state @@ -3351,8 +3395,10 @@ def wait_started(name, path=None, timeout=300): started = test_started(name, path=path) if started is None: logger( - 'Assuming %s is started, although we failed to detect that' - ' is fully started correctly', name) + "Assuming %s is started, although we failed to detect that" + " is fully started correctly", + name, + ) ret = True else: ret = started @@ -3361,39 +3407,38 @@ def wait_started(name, path=None, timeout=300): def _needs_install(name, path=None): ret = 0 - has_minion = retcode(name, - 'which salt-minion', - path=path, - ignore_retcode=True) + has_minion = retcode(name, "which salt-minion", path=path, ignore_retcode=True) # we assume that installing is when no minion is running # but testing the executable presence is not enougth for custom # installs where the bootstrap can do much more than installing # the bare salt binaries. if has_minion: processes = run_stdout(name, "ps aux", path=path) - if 'salt-minion' not in processes: + if "salt-minion" not in processes: ret = 1 else: - retcode(name, 'salt-call --local service.stop salt-minion') + retcode(name, "salt-call --local service.stop salt-minion") else: ret = 1 return ret -def bootstrap(name, - config=None, - approve_key=True, - install=True, - pub_key=None, - priv_key=None, - bootstrap_url=None, - force_install=False, - unconditional_install=False, - path=None, - bootstrap_delay=None, - bootstrap_args=None, - bootstrap_shell=None): - ''' +def bootstrap( + name, + config=None, + approve_key=True, + install=True, + pub_key=None, + priv_key=None, + bootstrap_url=None, + force_install=False, + unconditional_install=False, + path=None, + bootstrap_delay=None, + bootstrap_args=None, + bootstrap_shell=None, +): + """ Install and configure salt in a container. config @@ -3452,11 +3497,11 @@ def bootstrap(name, salt 'minion' lxc.bootstrap container_name [config=config_data] \\ [approve_key=(True|False)] [install=(True|False)] - ''' + """ wait_started(name, path=path) if bootstrap_delay is not None: try: - log.info('LXC %s: bootstrap_delay: %s', name, bootstrap_delay) + log.info("LXC %s: bootstrap_delay: %s", name, bootstrap_delay) time.sleep(bootstrap_delay) except TypeError: # Bad input, but assume since a value was passed that @@ -3473,12 +3518,12 @@ def bootstrap(name, # custom bootstrap args can be totally customized, and user could # have inserted the placeholder for the config directory. # For example, some salt bootstrap script do not use at all -c - if '{0}' not in bootstrap_args: - bootstrap_args += ' -c {0}' + if "{0}" not in bootstrap_args: + bootstrap_args += " -c {0}" else: - bootstrap_args = '-c {0}' + bootstrap_args = "-c {0}" if not bootstrap_shell: - bootstrap_shell = 'sh' + bootstrap_shell = "sh" orig_state = _ensure_running(name, path=path) if not orig_state: @@ -3487,104 +3532,126 @@ def bootstrap(name, needs_install = _needs_install(name, path=path) else: needs_install = True - seeded = retcode(name, - 'test -e \'{0}\''.format(SEED_MARKER), - path=path, - chroot_fallback=True, - ignore_retcode=True) == 0 + seeded = ( + retcode( + name, + "test -e '{0}'".format(SEED_MARKER), + path=path, + chroot_fallback=True, + ignore_retcode=True, + ) + == 0 + ) tmp = tempfile.mkdtemp() if seeded and not unconditional_install: ret = True else: ret = False - cfg_files = __salt__['seed.mkconfig']( - config, tmp=tmp, id_=name, approve_key=approve_key, - pub_key=pub_key, priv_key=priv_key) + cfg_files = __salt__["seed.mkconfig"]( + config, + tmp=tmp, + id_=name, + approve_key=approve_key, + pub_key=pub_key, + priv_key=priv_key, + ) if needs_install or force_install or unconditional_install: if install: - rstr = __salt__['test.random_hash']() - configdir = '/var/tmp/.c_{0}'.format(rstr) + rstr = __salt__["test.random_hash"]() + configdir = "/var/tmp/.c_{0}".format(rstr) - cmd = 'install -m 0700 -d {0}'.format(configdir) - if run_all( - name, cmd, path=path, python_shell=False - )['retcode'] != 0: - log.error('tmpdir %s creation failed %s', configdir, cmd) + cmd = "install -m 0700 -d {0}".format(configdir) + if run_all(name, cmd, path=path, python_shell=False)["retcode"] != 0: + log.error("tmpdir %s creation failed %s", configdir, cmd) return False - bs_ = __salt__['config.gather_bootstrap_script']( - bootstrap=bootstrap_url) - script = '/sbin/{0}_bootstrap.sh'.format(rstr) + bs_ = __salt__["config.gather_bootstrap_script"]( + bootstrap=bootstrap_url + ) + script = "/sbin/{0}_bootstrap.sh".format(rstr) copy_to(name, bs_, script, path=path) - result = run_all(name, - 'sh -c "chmod +x {0}"'.format(script), - path=path, - python_shell=True) + result = run_all( + name, + 'sh -c "chmod +x {0}"'.format(script), + path=path, + python_shell=True, + ) - copy_to(name, cfg_files['config'], - os.path.join(configdir, 'minion'), - path=path) - copy_to(name, cfg_files['privkey'], - os.path.join(configdir, 'minion.pem'), - path=path) - copy_to(name, cfg_files['pubkey'], - os.path.join(configdir, 'minion.pub'), - path=path) + copy_to( + name, + cfg_files["config"], + os.path.join(configdir, "minion"), + path=path, + ) + copy_to( + name, + cfg_files["privkey"], + os.path.join(configdir, "minion.pem"), + path=path, + ) + copy_to( + name, + cfg_files["pubkey"], + os.path.join(configdir, "minion.pub"), + path=path, + ) bootstrap_args = bootstrap_args.format(configdir) - cmd = ('{0} {2} {1}' - .format(bootstrap_shell, - bootstrap_args.replace("'", "''"), - script)) + cmd = "{0} {2} {1}".format( + bootstrap_shell, bootstrap_args.replace("'", "''"), script + ) # log ASAP the forged bootstrap command which can be wrapped # out of the output in case of unexpected problem - log.info('Running %s in LXC container \'%s\'', cmd, name) - ret = retcode(name, cmd, output_loglevel='info', - path=path, use_vt=True) == 0 + log.info("Running %s in LXC container '%s'", cmd, name) + ret = ( + retcode(name, cmd, output_loglevel="info", path=path, use_vt=True) + == 0 + ) - run_all(name, - 'sh -c \'if [ -f "{0}" ];then rm -f "{0}";fi\'' - ''.format(script), - path=path, - ignore_retcode=True, - python_shell=True) + run_all( + name, + 'sh -c \'if [ -f "{0}" ];then rm -f "{0}";fi\'' "".format(script), + path=path, + ignore_retcode=True, + python_shell=True, + ) else: ret = False else: - minion_config = salt.config.minion_config(cfg_files['config']) - pki_dir = minion_config['pki_dir'] - copy_to(name, - cfg_files['config'], - '/etc/salt/minion', - path=path) - copy_to(name, - cfg_files['privkey'], - os.path.join(pki_dir, 'minion.pem'), - path=path) - copy_to(name, - cfg_files['pubkey'], - os.path.join(pki_dir, 'minion.pub'), - path=path) - run(name, - 'salt-call --local service.enable salt-minion', + minion_config = salt.config.minion_config(cfg_files["config"]) + pki_dir = minion_config["pki_dir"] + copy_to(name, cfg_files["config"], "/etc/salt/minion", path=path) + copy_to( + name, + cfg_files["privkey"], + os.path.join(pki_dir, "minion.pem"), path=path, - python_shell=False) + ) + copy_to( + name, + cfg_files["pubkey"], + os.path.join(pki_dir, "minion.pub"), + path=path, + ) + run( + name, + "salt-call --local service.enable salt-minion", + path=path, + python_shell=False, + ) ret = True shutil.rmtree(tmp) - if orig_state == 'stopped': + if orig_state == "stopped": stop(name, path=path) - elif orig_state == 'frozen': + elif orig_state == "frozen": freeze(name, path=path) # mark seeded upon successful install if ret: - run(name, - 'touch \'{0}\''.format(SEED_MARKER), - path=path, - python_shell=False) + run(name, "touch '{0}'".format(SEED_MARKER), path=path, python_shell=False) return ret def attachable(name, path=None): - ''' + """ Return True if the named container can be attached to via the lxc-attach command @@ -3599,41 +3666,45 @@ def attachable(name, path=None): .. code-block:: bash salt 'minion' lxc.attachable ubuntu - ''' - cachekey = 'lxc.attachable{0}{1}'.format(name, path) + """ + cachekey = "lxc.attachable{0}{1}".format(name, path) try: return __context__[cachekey] except KeyError: _ensure_exists(name, path=path) # Can't use run() here because it uses attachable() and would # endlessly recurse, resulting in a traceback - log.debug('Checking if LXC container %s is attachable', name) - cmd = 'lxc-attach' + log.debug("Checking if LXC container %s is attachable", name) + cmd = "lxc-attach" if path: - cmd += ' -P {0}'.format(pipes.quote(path)) - cmd += ' --clear-env -n {0} -- /usr/bin/env'.format(name) - result = __salt__['cmd.retcode'](cmd, - python_shell=False, - output_loglevel='quiet', - ignore_retcode=True) == 0 + cmd += " -P {0}".format(pipes.quote(path)) + cmd += " --clear-env -n {0} -- /usr/bin/env".format(name) + result = ( + __salt__["cmd.retcode"]( + cmd, python_shell=False, output_loglevel="quiet", ignore_retcode=True + ) + == 0 + ) __context__[cachekey] = result return __context__[cachekey] -def _run(name, - cmd, - output=None, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - path=None, - ignore_retcode=False, - chroot_fallback=None, - keep_env='http_proxy,https_proxy,no_proxy'): - ''' +def _run( + name, + cmd, + output=None, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + path=None, + ignore_retcode=False, + chroot_fallback=None, + keep_env="http_proxy,https_proxy,no_proxy", +): + """ Common logic for lxc.run functions path @@ -3642,11 +3713,11 @@ def _run(name, .. versionadded:: 2015.8.0 - ''' + """ orig_state = state(name, path=path) try: if attachable(name, path=path): - ret = __salt__['container_resource.run']( + ret = __salt__["container_resource.run"]( name, cmd, path=path, @@ -3659,49 +3730,53 @@ def _run(name, output_loglevel=output_loglevel, ignore_retcode=ignore_retcode, use_vt=use_vt, - keep_env=keep_env) + keep_env=keep_env, + ) else: if not chroot_fallback: - raise CommandExecutionError( - '{0} is not attachable.'.format(name)) - rootfs = info(name, path=path).get('rootfs') + raise CommandExecutionError("{0} is not attachable.".format(name)) + rootfs = info(name, path=path).get("rootfs") # Set context var to make cmd.run_chroot run cmd.run instead of # cmd.run_all. - __context__['cmd.run_chroot.func'] = __salt__['cmd.run'] - ret = __salt__['cmd.run_chroot'](rootfs, - cmd, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - ignore_retcode=ignore_retcode) + __context__["cmd.run_chroot.func"] = __salt__["cmd.run"] + ret = __salt__["cmd.run_chroot"]( + rootfs, + cmd, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + ignore_retcode=ignore_retcode, + ) finally: # Make sure we honor preserve_state, even if there was an exception new_state = state(name, path=path) if preserve_state: - if orig_state == 'stopped' and new_state != 'stopped': + if orig_state == "stopped" and new_state != "stopped": stop(name, path=path) - elif orig_state == 'frozen' and new_state != 'frozen': + elif orig_state == "frozen" and new_state != "frozen": freeze(name, start=True, path=path) - if output in (None, 'all'): + if output in (None, "all"): return ret else: return ret[output] -def run(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - path=None, - ignore_retcode=False, - chroot_fallback=False, - keep_env='http_proxy,https_proxy,no_proxy'): - ''' +def run( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + path=None, + ignore_retcode=False, + chroot_fallback=False, + keep_env="http_proxy,https_proxy,no_proxy", +): + """ .. versionadded:: 2015.8.0 Run :mod:`cmd.run ` within a container @@ -3762,35 +3837,39 @@ def run(name, .. code-block:: bash salt myminion lxc.run mycontainer 'ifconfig -a' - ''' - return _run(name, - cmd, - path=path, - output=None, - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - chroot_fallback=chroot_fallback, - keep_env=keep_env) + """ + return _run( + name, + cmd, + path=path, + output=None, + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + chroot_fallback=chroot_fallback, + keep_env=keep_env, + ) -def run_stdout(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - path=None, - ignore_retcode=False, - chroot_fallback=False, - keep_env='http_proxy,https_proxy,no_proxy'): - ''' +def run_stdout( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + path=None, + ignore_retcode=False, + chroot_fallback=False, + keep_env="http_proxy,https_proxy,no_proxy", +): + """ .. versionadded:: 2015.5.0 Run :mod:`cmd.run_stdout ` within a container @@ -3851,35 +3930,39 @@ def run_stdout(name, .. code-block:: bash salt myminion lxc.run_stdout mycontainer 'ifconfig -a' - ''' - return _run(name, - cmd, - path=path, - output='stdout', - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - chroot_fallback=chroot_fallback, - keep_env=keep_env) + """ + return _run( + name, + cmd, + path=path, + output="stdout", + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + chroot_fallback=chroot_fallback, + keep_env=keep_env, + ) -def run_stderr(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - path=None, - ignore_retcode=False, - chroot_fallback=False, - keep_env='http_proxy,https_proxy,no_proxy'): - ''' +def run_stderr( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + path=None, + ignore_retcode=False, + chroot_fallback=False, + keep_env="http_proxy,https_proxy,no_proxy", +): + """ .. versionadded:: 2015.5.0 Run :mod:`cmd.run_stderr ` within a container @@ -3938,35 +4021,39 @@ def run_stderr(name, .. code-block:: bash salt myminion lxc.run_stderr mycontainer 'ip addr show' - ''' - return _run(name, - cmd, - path=path, - output='stderr', - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - chroot_fallback=chroot_fallback, - keep_env=keep_env) + """ + return _run( + name, + cmd, + path=path, + output="stderr", + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + chroot_fallback=chroot_fallback, + keep_env=keep_env, + ) -def retcode(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - path=None, - ignore_retcode=False, - chroot_fallback=False, - keep_env='http_proxy,https_proxy,no_proxy'): - ''' +def retcode( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + path=None, + ignore_retcode=False, + chroot_fallback=False, + keep_env="http_proxy,https_proxy,no_proxy", +): + """ .. versionadded:: 2015.5.0 Run :mod:`cmd.retcode ` within a container @@ -4027,35 +4114,39 @@ def retcode(name, .. code-block:: bash salt myminion lxc.retcode mycontainer 'ip addr show' - ''' - return _run(name, - cmd, - output='retcode', - path=path, - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - chroot_fallback=chroot_fallback, - keep_env=keep_env) + """ + return _run( + name, + cmd, + output="retcode", + path=path, + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + chroot_fallback=chroot_fallback, + keep_env=keep_env, + ) -def run_all(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - path=None, - ignore_retcode=False, - chroot_fallback=False, - keep_env='http_proxy,https_proxy,no_proxy'): - ''' +def run_all( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + path=None, + ignore_retcode=False, + chroot_fallback=False, + keep_env="http_proxy,https_proxy,no_proxy", +): + """ .. versionadded:: 2015.5.0 Run :mod:`cmd.run_all ` within a container @@ -4120,29 +4211,31 @@ def run_all(name, .. code-block:: bash salt myminion lxc.run_all mycontainer 'ip addr show' - ''' - return _run(name, - cmd, - output='all', - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - path=path, - ignore_retcode=ignore_retcode, - chroot_fallback=chroot_fallback, - keep_env=keep_env) + """ + return _run( + name, + cmd, + output="all", + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + path=path, + ignore_retcode=ignore_retcode, + chroot_fallback=chroot_fallback, + keep_env=keep_env, + ) def _get_md5(name, path): - ''' + """ Get the MD5 checksum of a file from a container - ''' - output = run_stdout(name, 'md5sum "{0}"'.format(path), - chroot_fallback=True, - ignore_retcode=True) + """ + output = run_stdout( + name, 'md5sum "{0}"'.format(path), chroot_fallback=True, ignore_retcode=True + ) try: return output.split()[0] except IndexError: @@ -4151,7 +4244,7 @@ def _get_md5(name, path): def copy_to(name, source, dest, overwrite=False, makedirs=False, path=None): - ''' + """ .. versionchanged:: 2015.8.0 Function renamed from ``lxc.cp`` to ``lxc.copy_to`` for consistency with other container types. ``lxc.cp`` will continue to work, however. @@ -4197,9 +4290,9 @@ def copy_to(name, source, dest, overwrite=False, makedirs=False, path=None): salt 'minion' lxc.copy_to /tmp/foo /root/foo salt 'minion' lxc.cp /tmp/foo /root/foo - ''' + """ _ensure_running(name, no_start=True, path=path) - return __salt__['container_resource.copy_to']( + return __salt__["container_resource.copy_to"]( name, source, dest, @@ -4207,14 +4300,15 @@ def copy_to(name, source, dest, overwrite=False, makedirs=False, path=None): path=path, exec_driver=EXEC_DRIVER, overwrite=overwrite, - makedirs=makedirs) + makedirs=makedirs, + ) -cp = salt.utils.functools.alias_function(copy_to, 'cp') +cp = salt.utils.functools.alias_function(copy_to, "cp") -def read_conf(conf_file, out_format='simple'): - ''' +def read_conf(conf_file, out_format="simple"): + """ Read in an LXC configuration file. By default returns a simple, unsorted dict, but can also return a more detailed structure including blank lines and comments. @@ -4229,36 +4323,35 @@ def read_conf(conf_file, out_format='simple'): salt 'minion' lxc.read_conf /etc/lxc/mycontainer.conf salt 'minion' lxc.read_conf /etc/lxc/mycontainer.conf out_format=commented - ''' + """ ret_commented = [] ret_simple = {} - with salt.utils.files.fopen(conf_file, 'r') as fp_: + with salt.utils.files.fopen(conf_file, "r") as fp_: for line in salt.utils.data.decode(fp_.readlines()): - if '=' not in line: + if "=" not in line: ret_commented.append(line) continue - comps = line.split('=') - value = '='.join(comps[1:]).strip() + comps = line.split("=") + value = "=".join(comps[1:]).strip() comment = None - if value.strip().startswith('#'): - vcomps = value.strip().split('#') + if value.strip().startswith("#"): + vcomps = value.strip().split("#") value = vcomps[1].strip() - comment = '#'.join(vcomps[1:]).strip() - ret_commented.append({comps[0].strip(): { - 'value': value, - 'comment': comment, - }}) + comment = "#".join(vcomps[1:]).strip() + ret_commented.append( + {comps[0].strip(): {"value": value, "comment": comment}} + ) else: ret_commented.append({comps[0].strip(): value}) ret_simple[comps[0].strip()] = value - if out_format == 'simple': + if out_format == "simple": return ret_simple return ret_commented def write_conf(conf_file, conf): - ''' + """ Write out an LXC configuration file This is normally only used internally. The format of the data structure @@ -4291,13 +4384,13 @@ def write_conf(conf_file, conf): salt 'minion' lxc.write_conf /etc/lxc/mycontainer.conf \\ out_format=commented - ''' + """ if not isinstance(conf, list): - raise SaltInvocationError('Configuration must be passed as a list') + raise SaltInvocationError("Configuration must be passed as a list") # construct the content prior to write to the file # to avoid half written configs - content = '' + content = "" for line in conf: if isinstance(line, (six.text_type, six.string_types)): content += line @@ -4306,27 +4399,25 @@ def write_conf(conf_file, conf): out_line = None if isinstance( line[key], - (six.text_type, six.string_types, six.integer_types, float) + (six.text_type, six.string_types, six.integer_types, float), ): - out_line = ' = '.join((key, "{0}".format(line[key]))) + out_line = " = ".join((key, "{0}".format(line[key]))) elif isinstance(line[key], dict): - out_line = ' = '.join((key, line[key]['value'])) - if 'comment' in line[key]: - out_line = ' # '.join((out_line, line[key]['comment'])) + out_line = " = ".join((key, line[key]["value"])) + if "comment" in line[key]: + out_line = " # ".join((out_line, line[key]["comment"])) if out_line: content += out_line - content += '\n' - with salt.utils.files.fopen(conf_file, 'w') as fp_: + content += "\n" + with salt.utils.files.fopen(conf_file, "w") as fp_: fp_.write(salt.utils.stringutils.to_str(content)) return {} -def edit_conf(conf_file, - out_format='simple', - read_only=False, - lxc_config=None, - **kwargs): - ''' +def edit_conf( + conf_file, out_format="simple", read_only=False, lxc_config=None, **kwargs +): + """ Edit an LXC configuration file. If a setting is already present inside the file, its value will be replaced. If it does not exist, it will be appended to the end of the file. Comments and blank lines will be kept in-tact if @@ -4364,7 +4455,7 @@ def edit_conf(conf_file, {'lxc.network.name': 'eth2', \\ 'lxc.network.ipv4': '1.2.3.5',\\ 'lxc.network.gateway': '1.2.3.1'}]" - ''' + """ data = [] try: @@ -4382,27 +4473,29 @@ def edit_conf(conf_file, for lxc_kws in lxc_config + [kwargs]: net_params = {} for kwarg in [a for a in lxc_kws]: - if kwarg.startswith('__'): + if kwarg.startswith("__"): continue - if kwarg.startswith('lxc.network.'): + if kwarg.startswith("lxc.network."): net_params[kwarg] = lxc_kws[kwarg] lxc_kws.pop(kwarg, None) if net_params: net_config.append(net_params) nic_opts = salt.utils.odict.OrderedDict() for params in net_config: - dev = params.get('lxc.network.name', DEFAULT_NIC) + dev = params.get("lxc.network.name", DEFAULT_NIC) dev_opts = nic_opts.setdefault(dev, salt.utils.odict.OrderedDict()) for param in params: - opt = param.replace('lxc.network.', '') - opt = {'hwaddr': 'mac'}.get(opt, opt) + opt = param.replace("lxc.network.", "") + opt = {"hwaddr": "mac"}.get(opt, opt) dev_opts[opt] = params[param] net_changes = [] if nic_opts: - net_changes = _config_list(conf, only_net=True, - **{'network_profile': DEFAULT_NIC, - 'nic_opts': nic_opts}) + net_changes = _config_list( + conf, + only_net=True, + **{"network_profile": DEFAULT_NIC, "nic_opts": nic_opts} + ) if net_changes: lxc_config.extend(net_changes) @@ -4413,7 +4506,7 @@ def edit_conf(conf_file, else: for key in list(line.keys()): val = line[key] - if net_changes and key.startswith('lxc.network.'): + if net_changes and key.startswith("lxc.network."): continue found = False for ix in range(len(lxc_config)): @@ -4435,7 +4528,7 @@ def edit_conf(conf_file, def reboot(name, path=None): - ''' + """ Reboot a container. @@ -4451,45 +4544,45 @@ def reboot(name, path=None): salt 'minion' lxc.reboot myvm - ''' - ret = {'result': True, - 'changes': {}, - 'comment': '{0} rebooted'.format(name)} + """ + ret = {"result": True, "changes": {}, "comment": "{0} rebooted".format(name)} does_exist = exists(name, path=path) - if does_exist and (state(name, path=path) == 'running'): + if does_exist and (state(name, path=path) == "running"): try: stop(name, path=path) except (SaltInvocationError, CommandExecutionError) as exc: - ret['comment'] = 'Unable to stop container: {0}'.format(exc) - ret['result'] = False + ret["comment"] = "Unable to stop container: {0}".format(exc) + ret["result"] = False return ret - if does_exist and (state(name, path=path) != 'running'): + if does_exist and (state(name, path=path) != "running"): try: start(name, path=path) except (SaltInvocationError, CommandExecutionError) as exc: - ret['comment'] = 'Unable to stop container: {0}'.format(exc) - ret['result'] = False + ret["comment"] = "Unable to stop container: {0}".format(exc) + ret["result"] = False return ret - ret['changes'][name] = 'rebooted' + ret["changes"][name] = "rebooted" return ret -def reconfigure(name, - cpu=None, - cpuset=None, - cpushare=None, - memory=None, - profile=None, - network_profile=None, - nic_opts=None, - bridge=None, - gateway=None, - autostart=None, - utsname=None, - rootfs=None, - path=None, - **kwargs): - ''' +def reconfigure( + name, + cpu=None, + cpuset=None, + cpushare=None, + memory=None, + profile=None, + network_profile=None, + nic_opts=None, + bridge=None, + gateway=None, + autostart=None, + utsname=None, + rootfs=None, + path=None, + **kwargs +): + """ Reconfigure a container. This only applies to a few property @@ -4547,14 +4640,16 @@ def reconfigure(name, salt-call -lall mc_lxc_fork.reconfigure foobar nic_opts="{'eth1': {'mac': '00:16:3e:dd:ee:44'}}" memory=4 - ''' + """ changes = {} cpath = get_root_path(path) - path = os.path.join(cpath, name, 'config') - ret = {'name': name, - 'comment': 'config for {0} up to date'.format(name), - 'result': True, - 'changes': changes} + path = os.path.join(cpath, name, "config") + ret = { + "name": name, + "comment": "config for {0} up to date".format(name), + "result": True, + "changes": changes, + } profile = get_container_profile(copy.deepcopy(profile)) kw_overrides = copy.deepcopy(kwargs) @@ -4565,49 +4660,53 @@ def reconfigure(name, if kw_overrides_match is _marker: return profile_match return kw_overrides_match + if nic_opts is not None and not network_profile: network_profile = DEFAULT_NIC if autostart is not None: - autostart = select('autostart', autostart) + autostart = select("autostart", autostart) else: - autostart = 'keep' + autostart = "keep" if not utsname: - utsname = select('utsname', utsname) + utsname = select("utsname", utsname) if os.path.exists(path): - old_chunks = read_conf(path, out_format='commented') - make_kw = salt.utils.odict.OrderedDict([ - ('utsname', utsname), - ('rootfs', rootfs), - ('autostart', autostart), - ('cpu', cpu), - ('gateway', gateway), - ('cpuset', cpuset), - ('cpushare', cpushare), - ('network_profile', network_profile), - ('nic_opts', nic_opts), - ('bridge', bridge)]) + old_chunks = read_conf(path, out_format="commented") + make_kw = salt.utils.odict.OrderedDict( + [ + ("utsname", utsname), + ("rootfs", rootfs), + ("autostart", autostart), + ("cpu", cpu), + ("gateway", gateway), + ("cpuset", cpuset), + ("cpushare", cpushare), + ("network_profile", network_profile), + ("nic_opts", nic_opts), + ("bridge", bridge), + ] + ) # match 0 and none as memory = 0 in lxc config is harmful if memory: - make_kw['memory'] = memory + make_kw["memory"] = memory kw = salt.utils.odict.OrderedDict() for key, val in six.iteritems(make_kw): if val is not None: kw[key] = val new_cfg = _config_list(conf_tuples=old_chunks, **kw) if new_cfg: - edit_conf(path, out_format='commented', lxc_config=new_cfg) - chunks = read_conf(path, out_format='commented') + edit_conf(path, out_format="commented", lxc_config=new_cfg) + chunks = read_conf(path, out_format="commented") if old_chunks != chunks: - ret['comment'] = '{0} lxc config updated'.format(name) - if state(name, path=path) == 'running': + ret["comment"] = "{0} lxc config updated".format(name) + if state(name, path=path) == "running": cret = reboot(name, path=path) - ret['result'] = cret['result'] + ret["result"] = cret["result"] return ret def apply_network_profile(name, network_profile, nic_opts=None, path=None): - ''' + """ .. versionadded:: 2015.5.0 Apply a network profile to a container @@ -4641,43 +4740,39 @@ def apply_network_profile(name, network_profile, nic_opts=None, path=None): salt 'minion' lxc.apply_network_profile web1 centos \\ "{eth0: {disable: true}}" - ''' + """ cpath = get_root_path(path) - cfgpath = os.path.join(cpath, name, 'config') + cfgpath = os.path.join(cpath, name, "config") before = [] - with salt.utils.files.fopen(cfgpath, 'r') as fp_: + with salt.utils.files.fopen(cfgpath, "r") as fp_: for line in fp_: before.append(line) lxcconfig = _LXCConfig(name=name, path=path) - old_net = lxcconfig._filter_data('lxc.network') + old_net = lxcconfig._filter_data("lxc.network") network_params = {} for param in _network_conf( - conf_tuples=old_net, - network_profile=network_profile, nic_opts=nic_opts + conf_tuples=old_net, network_profile=network_profile, nic_opts=nic_opts ): network_params.update(param) if network_params: - edit_conf(cfgpath, out_format='commented', **network_params) + edit_conf(cfgpath, out_format="commented", **network_params) after = [] - with salt.utils.files.fopen(cfgpath, 'r') as fp_: + with salt.utils.files.fopen(cfgpath, "r") as fp_: for line in fp_: after.append(line) - diff = '' - for line in difflib.unified_diff(before, - after, - fromfile='before', - tofile='after'): + diff = "" + for line in difflib.unified_diff(before, after, fromfile="before", tofile="after"): diff += line return diff def get_pid(name, path=None): - ''' + """ Returns a container pid. Throw an exception if the container isn't running. @@ -4686,16 +4781,22 @@ def get_pid(name, path=None): .. code-block:: bash salt '*' lxc.get_pid name - ''' - if name not in list_(limit='running', path=path): - raise CommandExecutionError('Container {0} is not running, can\'t determine PID'.format(name)) - info = __salt__['cmd.run']('lxc-info -n {0}'.format(name)).split("\n") - pid = [line.split(':')[1].strip() for line in info if re.match(r'\s*PID', line) is not None][0] + """ + if name not in list_(limit="running", path=path): + raise CommandExecutionError( + "Container {0} is not running, can't determine PID".format(name) + ) + info = __salt__["cmd.run"]("lxc-info -n {0}".format(name)).split("\n") + pid = [ + line.split(":")[1].strip() + for line in info + if re.match(r"\s*PID", line) is not None + ][0] return pid def add_veth(name, interface_name, bridge=None, path=None): - ''' + """ Add a veth to a container. Note : this function doesn't update the container config, just add the interface at runtime @@ -4714,63 +4815,85 @@ def add_veth(name, interface_name, bridge=None, path=None): salt '*' lxc.add_veth container_name eth1 br1 salt '*' lxc.add_veth container_name eth1 - ''' + """ # Get container init PID pid = get_pid(name, path=path) # Generate a ramdom string for veth and ensure that is isn't present on the system while True: - random_veth = 'veth'+''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) - if random_veth not in __salt__['network.interfaces']().keys(): + random_veth = "veth" + "".join( + random.choice(string.ascii_uppercase + string.digits) for _ in range(6) + ) + if random_veth not in __salt__["network.interfaces"]().keys(): break # Check prerequisites - if not __salt__['file.directory_exists']('/var/run/'): - raise CommandExecutionError('Directory /var/run required for lxc.add_veth doesn\'t exists') - if not __salt__['file.file_exists']('/proc/{0}/ns/net'.format(pid)): - raise CommandExecutionError('Proc file for container {0} network namespace doesn\'t exists'.format(name)) + if not __salt__["file.directory_exists"]("/var/run/"): + raise CommandExecutionError( + "Directory /var/run required for lxc.add_veth doesn't exists" + ) + if not __salt__["file.file_exists"]("/proc/{0}/ns/net".format(pid)): + raise CommandExecutionError( + "Proc file for container {0} network namespace doesn't exists".format(name) + ) - if not __salt__['file.directory_exists']('/var/run/netns'): - __salt__['file.mkdir']('/var/run/netns') + if not __salt__["file.directory_exists"]("/var/run/netns"): + __salt__["file.mkdir"]("/var/run/netns") # Ensure that the symlink is up to date (change on container restart) - if __salt__['file.is_link']('/var/run/netns/{0}'.format(name)): - __salt__['file.remove']('/var/run/netns/{0}'.format(name)) + if __salt__["file.is_link"]("/var/run/netns/{0}".format(name)): + __salt__["file.remove"]("/var/run/netns/{0}".format(name)) - __salt__['file.symlink']('/proc/{0}/ns/net'.format(pid), '/var/run/netns/{0}'.format(name)) + __salt__["file.symlink"]( + "/proc/{0}/ns/net".format(pid), "/var/run/netns/{0}".format(name) + ) # Ensure that interface doesn't exists - interface_exists = 0 == __salt__['cmd.retcode']('ip netns exec {netns} ip address list {interface}'.format( - netns=name, - interface=interface_name - )) + interface_exists = 0 == __salt__["cmd.retcode"]( + "ip netns exec {netns} ip address list {interface}".format( + netns=name, interface=interface_name + ) + ) if interface_exists: - raise CommandExecutionError('Interface {interface} already exists in {container}'.format( - interface=interface_name, - container=name - )) + raise CommandExecutionError( + "Interface {interface} already exists in {container}".format( + interface=interface_name, container=name + ) + ) # Create veth and bring it up - if __salt__['cmd.retcode']('ip link add name {veth} type veth peer name {veth}_c'.format(veth=random_veth)) != 0: - raise CommandExecutionError('Error while creating the veth pair {0}'.format(random_veth)) - if __salt__['cmd.retcode']('ip link set dev {0} up'.format(random_veth)) != 0: - raise CommandExecutionError('Error while bringing up host-side veth {0}'.format(random_veth)) + if ( + __salt__["cmd.retcode"]( + "ip link add name {veth} type veth peer name {veth}_c".format( + veth=random_veth + ) + ) + != 0 + ): + raise CommandExecutionError( + "Error while creating the veth pair {0}".format(random_veth) + ) + if __salt__["cmd.retcode"]("ip link set dev {0} up".format(random_veth)) != 0: + raise CommandExecutionError( + "Error while bringing up host-side veth {0}".format(random_veth) + ) # Attach it to the container - attached = 0 == __salt__['cmd.retcode']('ip link set dev {veth}_c netns {container} name {interface_name}'.format( - veth=random_veth, - container=name, - interface_name=interface_name - )) + attached = 0 == __salt__["cmd.retcode"]( + "ip link set dev {veth}_c netns {container} name {interface_name}".format( + veth=random_veth, container=name, interface_name=interface_name + ) + ) if not attached: - raise CommandExecutionError('Error while attaching the veth {veth} to container {container}'.format( - veth=random_veth, - container=name - )) + raise CommandExecutionError( + "Error while attaching the veth {veth} to container {container}".format( + veth=random_veth, container=name + ) + ) - __salt__['file.remove']('/var/run/netns/{0}'.format(name)) + __salt__["file.remove"]("/var/run/netns/{0}".format(name)) if bridge is not None: - __salt__['bridge.addif'](bridge, random_veth) + __salt__["bridge.addif"](bridge, random_veth) diff --git a/salt/modules/lxd.py b/salt/modules/lxd.py index d6c2d8d4b91..0300bdee441 100644 --- a/salt/modules/lxd.py +++ b/salt/modules/lxd.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing the LXD daemon and its containers. .. versionadded:: 2019.2.0 @@ -30,90 +30,94 @@ several functions to help manage it and its containers. :maturity: new :depends: python-pylxd :platform: Linux -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +# Set up logging +import logging import os from datetime import datetime +import salt.ext.six as six + # Import salt libs import salt.utils.decorators.path import salt.utils.files +from salt.exceptions import CommandExecutionError, SaltInvocationError +from salt.ext.six.moves import map, zip from salt.utils.versions import LooseVersion -from salt.exceptions import CommandExecutionError -from salt.exceptions import SaltInvocationError -import salt.ext.six as six -from salt.ext.six.moves import map -from salt.ext.six.moves import zip # Import 3rd-party libs try: import pylxd + HAS_PYLXD = True import urllib3 + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) except ImportError: HAS_PYLXD = False -# Set up logging -import logging + log = logging.getLogger(__name__) -__docformat__ = 'restructuredtext en' +__docformat__ = "restructuredtext en" _pylxd_minimal_version = "2.2.5" # Keep in sync with: https://github.com/lxc/lxd/blob/master/shared/osarch/architectures.go _architectures = { - 'unknown': '0', - 'i686': '1', - 'x86_64': '2', - 'armv7l': '3', - 'aarch64': '4', - 'ppc': '5', - 'ppc64': '6', - 'ppc64le': '7', - 's390x': '8' + "unknown": "0", + "i686": "1", + "x86_64": "2", + "armv7l": "3", + "aarch64": "4", + "ppc": "5", + "ppc64": "6", + "ppc64le": "7", + "s390x": "8", } # Keep in sync with: https://github.com/lxc/lxd/blob/master/shared/api/status_code.go CONTAINER_STATUS_RUNNING = 103 -__virtualname__ = 'lxd' +__virtualname__ = "lxd" _connection_pool = {} def __virtual__(): if HAS_PYLXD: - if (LooseVersion(pylxd_version()) < - LooseVersion(_pylxd_minimal_version)): + if LooseVersion(pylxd_version()) < LooseVersion(_pylxd_minimal_version): return ( False, - ('The lxd execution module cannot be loaded:' - ' pylxd "{0}" is not supported,' - ' you need at least pylxd "{1}"').format( - pylxd_version(), - _pylxd_minimal_version) + ( + "The lxd execution module cannot be loaded:" + ' pylxd "{0}" is not supported,' + ' you need at least pylxd "{1}"' + ).format(pylxd_version(), _pylxd_minimal_version), ) return __virtualname__ return ( False, - ('The lxd execution module cannot be loaded: ' - 'the pylxd python module is not available.') + ( + "The lxd execution module cannot be loaded: " + "the pylxd python module is not available." + ), ) ################ # LXD Management ################ -@salt.utils.decorators.path.which('lxd') +@salt.utils.decorators.path.which("lxd") def version(): - ''' + """ Returns the actual lxd version. CLI Example: @@ -122,12 +126,12 @@ def version(): salt '*' lxd.version - ''' - return __salt__['cmd.run']('lxd --version') + """ + return __salt__["cmd.run"]("lxd --version") def pylxd_version(): - ''' + """ Returns the actual pylxd version. CLI Example: @@ -136,15 +140,21 @@ def pylxd_version(): salt '*' lxd.pylxd_version - ''' + """ return pylxd.__version__ -@salt.utils.decorators.path.which('lxd') -def init(storage_backend='dir', trust_password=None, network_address=None, - network_port=None, storage_create_device=None, - storage_create_loop=None, storage_pool=None): - ''' +@salt.utils.decorators.path.which("lxd") +def init( + storage_backend="dir", + trust_password=None, + network_address=None, + network_port=None, + storage_create_device=None, + storage_create_loop=None, + storage_pool=None, +): + """ Calls lxd init --auto -- opts storage_backend : @@ -181,12 +191,9 @@ def init(storage_backend='dir', trust_password=None, network_address=None, .. code-block:: bash salt '*' lxd.init - ''' + """ - cmd = ('lxd init --auto' - ' --storage-backend="{0}"').format( - storage_backend - ) + cmd = ("lxd init --auto" ' --storage-backend="{0}"').format(storage_backend) if trust_password is not None: cmd = cmd + ' --trust-password="{0}"'.format(trust_password) @@ -198,39 +205,31 @@ def init(storage_backend='dir', trust_password=None, network_address=None, cmd = cmd + ' --network-port="{0}"'.format(network_port) if storage_create_device is not None: - cmd = cmd + ' --storage-create-device="{0}"'.format( - storage_create_device - ) + cmd = cmd + ' --storage-create-device="{0}"'.format(storage_create_device) if storage_create_loop is not None: - cmd = cmd + ' --storage-create-loop="{0}"'.format( - storage_create_loop - ) + cmd = cmd + ' --storage-create-loop="{0}"'.format(storage_create_loop) if storage_pool is not None: cmd = cmd + ' --storage-pool="{0}"'.format(storage_pool) try: - output = __salt__['cmd.run'](cmd) + output = __salt__["cmd.run"](cmd) except ValueError as e: raise CommandExecutionError( - "Failed to call: '{0}', error was: {1}".format( - cmd, six.text_type(e) - ), + "Failed to call: '{0}', error was: {1}".format(cmd, six.text_type(e)), ) - if 'error:' in output: - raise CommandExecutionError( - output[output.index('error:') + 7:], - ) + if "error:" in output: + raise CommandExecutionError(output[output.index("error:") + 7 :],) return output -@salt.utils.decorators.path.which('lxd') -@salt.utils.decorators.path.which('lxc') +@salt.utils.decorators.path.which("lxd") +@salt.utils.decorators.path.which("lxc") def config_set(key, value): - ''' + """ Set an LXD daemon config option CLI Examples: @@ -248,25 +247,20 @@ def config_set(key, value): salt '*' lxd.config_set core.trust_password blah - ''' - cmd = 'lxc config set "{0}" "{1}"'.format( - key, - value, - ) + """ + cmd = 'lxc config set "{0}" "{1}"'.format(key, value,) - output = __salt__['cmd.run'](cmd) - if 'error:' in output: - raise CommandExecutionError( - output[output.index('error:') + 7:], - ) + output = __salt__["cmd.run"](cmd) + if "error:" in output: + raise CommandExecutionError(output[output.index("error:") + 7 :],) - return 'Config value "{0}" successfully set.'.format(key), + return ('Config value "{0}" successfully set.'.format(key),) -@salt.utils.decorators.path.which('lxd') -@salt.utils.decorators.path.which('lxc') +@salt.utils.decorators.path.which("lxd") +@salt.utils.decorators.path.which("lxc") def config_get(key): - ''' + """ Get an LXD daemon config option key : @@ -277,17 +271,13 @@ def config_get(key): .. code-block:: bash salt '*' lxd.config_get core.https_address - ''' + """ - cmd = 'lxc config get "{0}"'.format( - key - ) + cmd = 'lxc config get "{0}"'.format(key) - output = __salt__['cmd.run'](cmd) - if 'error:' in output: - raise CommandExecutionError( - output[output.index('error:') + 7:], - ) + output = __salt__["cmd.run"](cmd) + if "error:" in output: + raise CommandExecutionError(output[output.index("error:") + 7 :],) return output @@ -296,7 +286,7 @@ def config_get(key): # Connection Management ####################### def pylxd_client_get(remote_addr=None, cert=None, key=None, verify_cert=True): - ''' + """ Get an pyxld client, this is not ment to be runned over the CLI. remote_addr : @@ -328,31 +318,37 @@ def pylxd_client_get(remote_addr=None, cert=None, key=None, verify_cert=True): .. _requests-docs: http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification - ''' + """ - pool_key = '|'.join((six.text_type(remote_addr), - six.text_type(cert), - six.text_type(key), - six.text_type(verify_cert),)) + pool_key = "|".join( + ( + six.text_type(remote_addr), + six.text_type(cert), + six.text_type(key), + six.text_type(verify_cert), + ) + ) if pool_key in _connection_pool: - log.debug(( - 'Returning the client "{0}" from our connection pool' - ).format(remote_addr)) + log.debug( + ('Returning the client "{0}" from our connection pool').format(remote_addr) + ) return _connection_pool[pool_key] try: - if remote_addr is None or remote_addr == '/var/lib/lxd/unix.socket': - log.debug('Trying to connect to the local unix socket') + if remote_addr is None or remote_addr == "/var/lib/lxd/unix.socket": + log.debug("Trying to connect to the local unix socket") client = pylxd.Client() else: - if remote_addr.startswith('/'): + if remote_addr.startswith("/"): client = pylxd.Client(remote_addr) else: if cert is None or key is None: raise SaltInvocationError( - ('You have to give a Cert and ' - 'Key file for remote endpoints.') + ( + "You have to give a Cert and " + "Key file for remote endpoints." + ) ) cert = os.path.expanduser(cert) @@ -360,42 +356,42 @@ def pylxd_client_get(remote_addr=None, cert=None, key=None, verify_cert=True): if not os.path.isfile(cert): raise SaltInvocationError( - ('You have given an invalid cert path: "{0}", ' - 'the file does not exists or is not a file.').format( - cert - ) + ( + 'You have given an invalid cert path: "{0}", ' + "the file does not exists or is not a file." + ).format(cert) ) if not os.path.isfile(key): raise SaltInvocationError( - ('You have given an invalid key path: "{0}", ' - 'the file does not exists or is not a file.').format( - key - ) + ( + 'You have given an invalid key path: "{0}", ' + "the file does not exists or is not a file." + ).format(key) ) - log.debug(( - 'Trying to connecto to "{0}" ' - 'with cert "{1}", key "{2}" and ' - 'verify_cert "{3!s}"'.format( - remote_addr, cert, key, verify_cert) - )) + log.debug( + ( + 'Trying to connecto to "{0}" ' + 'with cert "{1}", key "{2}" and ' + 'verify_cert "{3!s}"'.format( + remote_addr, cert, key, verify_cert + ) + ) + ) client = pylxd.Client( - endpoint=remote_addr, - cert=(cert, key,), - verify=verify_cert + endpoint=remote_addr, cert=(cert, key,), verify=verify_cert ) except pylxd.exceptions.ClientConnectionFailed: - raise CommandExecutionError( - "Failed to connect to '{0}'".format(remote_addr) - ) + raise CommandExecutionError("Failed to connect to '{0}'".format(remote_addr)) except TypeError as e: # Happens when the verification failed. raise CommandExecutionError( - ('Failed to connect to "{0}",' - ' looks like the SSL verification failed, error was: {1}' - ).format(remote_addr, six.text_type(e)) + ( + 'Failed to connect to "{0}",' + " looks like the SSL verification failed, error was: {1}" + ).format(remote_addr, six.text_type(e)) ) _connection_pool[pool_key] = client @@ -404,14 +400,14 @@ def pylxd_client_get(remote_addr=None, cert=None, key=None, verify_cert=True): def pylxd_save_object(obj): - ''' Saves an object (profile/image/container) and + """ Saves an object (profile/image/container) and translate its execpetion on failure obj : The object to save This is an internal method, no CLI Example. - ''' + """ try: obj.save() except pylxd.exceptions.LXDAPIException as e: @@ -421,7 +417,7 @@ def pylxd_save_object(obj): def authenticate(remote_addr, password, cert, key, verify_cert=True): - ''' + """ Authenticate with a remote LXDaemon. remote_addr : @@ -461,7 +457,7 @@ def authenticate(remote_addr, password, cert, key, verify_cert=True): .. _requests-docs: http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) if client.trusted: @@ -479,9 +475,10 @@ def authenticate(remote_addr, password, cert, key, verify_cert=True): ###################### # Container Management ###################### -def container_list(list_names=False, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_list( + list_names=False, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Lists containers list_names : False @@ -530,7 +527,7 @@ def container_list(list_names=False, remote_addr=None, .. _container-attributes: https://github.com/lxc/pylxd/blob/master/doc/source/containers.rst#container-attributes - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) containers = client.containers.all() @@ -540,12 +537,22 @@ def container_list(list_names=False, remote_addr=None, return map(_pylxd_model_to_dict, containers) -def container_create(name, source, profiles=None, - config=None, devices=None, architecture='x86_64', - ephemeral=False, wait=True, - remote_addr=None, cert=None, key=None, verify_cert=True, - _raw=False): - ''' +def container_create( + name, + source, + profiles=None, + config=None, + devices=None, + architecture="x86_64", + ephemeral=False, + wait=True, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, + _raw=False, +): + """ Create a container name : @@ -643,9 +650,9 @@ def container_create(name, source, profiles=None, .. _rest-api-docs: https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-1 - ''' + """ if profiles is None: - profiles = ['default'] + profiles = ["default"] if config is None: config = {} @@ -656,43 +663,37 @@ def container_create(name, source, profiles=None, client = pylxd_client_get(remote_addr, cert, key, verify_cert) if not isinstance(profiles, (list, tuple, set,)): - raise SaltInvocationError( - "'profiles' must be formatted as list/tuple/set." - ) + raise SaltInvocationError("'profiles' must be formatted as list/tuple/set.") if architecture not in _architectures: raise SaltInvocationError( - ("Unknown architecture '{0}' " - "given for the container '{1}'").format(architecture, name) + ("Unknown architecture '{0}' " "given for the container '{1}'").format( + architecture, name + ) ) if isinstance(source, six.string_types): - source = {'type': 'image', 'alias': source} + source = {"type": "image", "alias": source} - config, devices = normalize_input_values( - config, - devices - ) + config, devices = normalize_input_values(config, devices) try: container = client.containers.create( { - 'name': name, - 'architecture': _architectures[architecture], - 'profiles': profiles, - 'source': source, - 'config': config, - 'ephemeral': ephemeral + "name": name, + "architecture": _architectures[architecture], + "profiles": profiles, + "source": source, + "config": config, + "ephemeral": ephemeral, }, - wait=wait + wait=wait, ) except pylxd.exceptions.LXDAPIException as e: - raise CommandExecutionError( - six.text_type(e) - ) + raise CommandExecutionError(six.text_type(e)) if not wait: - return container.json()['operation'] + return container.json()["operation"] # Add devices if not wait and devices have been given. if devices: @@ -705,9 +706,10 @@ def container_create(name, source, profiles=None, return _pylxd_model_to_dict(container) -def container_get(name=None, remote_addr=None, - cert=None, key=None, verify_cert=True, _raw=False): - ''' Gets a container from the LXD +def container_get( + name=None, remote_addr=None, cert=None, key=None, verify_cert=True, _raw=False +): + """ Gets a container from the LXD name : The name of the container to get. @@ -739,7 +741,7 @@ def container_get(name=None, remote_addr=None, _raw : Return the pylxd object, this is internal and by states in use. - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) if name is None: @@ -751,23 +753,18 @@ def container_get(name=None, remote_addr=None, try: containers = [client.containers.get(name)] except pylxd.exceptions.LXDAPIException: - raise SaltInvocationError( - 'Container \'{0}\' not found'.format(name) - ) + raise SaltInvocationError("Container '{0}' not found".format(name)) if _raw: return containers[0] infos = [] for container in containers: - infos.append(dict([ - (container.name, _pylxd_model_to_dict(container)) - ])) + infos.append(dict([(container.name, _pylxd_model_to_dict(container))])) return infos -def container_delete(name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_delete(name, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Delete a container name : @@ -797,17 +794,16 @@ def container_delete(name, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) container.delete(wait=True) return True -def container_rename(name, newname, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_rename( + name, newname, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Rename a container name : @@ -840,10 +836,8 @@ def container_rename(name, newname, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) if container.status_code == CONTAINER_STATUS_RUNNING: raise SaltInvocationError( @@ -854,9 +848,8 @@ def container_rename(name, newname, remote_addr=None, return _pylxd_model_to_dict(container) -def container_state(name=None, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_state(name=None, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Get container state remote_addr : @@ -883,7 +876,7 @@ def container_state(name=None, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) if name is None: @@ -892,31 +885,34 @@ def container_state(name=None, remote_addr=None, try: containers = [client.containers.get(name)] except pylxd.exceptions.LXDAPIException: - raise SaltInvocationError( - 'Container \'{0}\' not found'.format(name) - ) + raise SaltInvocationError("Container '{0}' not found".format(name)) states = [] for container in containers: state = {} state = container.state() - states.append(dict([ - ( - container.name, - dict([ - (k, getattr(state, k)) - for k in dir(state) - if not k.startswith('_') - ]) + states.append( + dict( + [ + ( + container.name, + dict( + [ + (k, getattr(state, k)) + for k in dir(state) + if not k.startswith("_") + ] + ), + ) + ] ) - ])) + ) return states -def container_start(name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_start(name, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Start a container name : @@ -946,17 +942,22 @@ def container_start(name, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) container.start(wait=True) return _pylxd_model_to_dict(container) -def container_stop(name, timeout=30, force=True, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_stop( + name, + timeout=30, + force=True, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Stop a container name : @@ -986,17 +987,14 @@ def container_stop(name, timeout=30, force=True, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) container.stop(timeout, force, wait=True) return _pylxd_model_to_dict(container) -def container_restart(name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_restart(name, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Restart a container name : @@ -1026,17 +1024,14 @@ def container_restart(name, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) container.restart(wait=True) return _pylxd_model_to_dict(container) -def container_freeze(name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_freeze(name, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Freeze a container name : @@ -1066,17 +1061,14 @@ def container_freeze(name, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) container.freeze(wait=True) return _pylxd_model_to_dict(container) -def container_unfreeze(name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_unfreeze(name, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Unfreeze a container name : @@ -1106,25 +1098,25 @@ def container_unfreeze(name, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) container.unfreeze(wait=True) return _pylxd_model_to_dict(container) -def container_migrate(name, - stop_and_start=False, - remote_addr=None, - cert=None, - key=None, - verify_cert=True, - src_remote_addr=None, - src_cert=None, - src_key=None, - src_verify_cert=None): - ''' Migrate a container. +def container_migrate( + name, + stop_and_start=False, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, + src_remote_addr=None, + src_cert=None, + src_key=None, + src_verify_cert=None, +): + """ Migrate a container. If the container is running, it either must be shut down first (use stop_and_start=True) or criu must be installed @@ -1175,7 +1167,7 @@ def container_migrate(name, # Migrate phpmyadmin from srv01 to srv02 salt '*' lxd.container_migrate phpmyadmin stop_and_start=true remote_addr=https://srv02:8443 cert=~/.config/lxc/client.crt key=~/.config/lxc/client.key verify_cert=False src_remote_addr=https://srv01:8443 - ''' + """ if src_cert is None: src_cert = cert @@ -1189,16 +1181,14 @@ def container_migrate(name, name, src_remote_addr, src_cert, src_key, src_verify_cert, _raw=True ) - dest_client = pylxd_client_get( - remote_addr, cert, key, verify_cert - ) + dest_client = pylxd_client_get(remote_addr, cert, key, verify_cert) for pname in container.profiles: try: dest_client.profiles.get(pname) except pylxd.exceptions.LXDAPIException: raise SaltInvocationError( - 'not all the profiles from the source exist on the target' + "not all the profiles from the source exist on the target" ) was_running = container.status_code == CONTAINER_STATUS_RUNNING @@ -1221,9 +1211,10 @@ def container_migrate(name, return _pylxd_model_to_dict(dest_container) -def container_config_get(name, config_key, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_config_get( + name, config_key, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Get a container config value name : @@ -1256,16 +1247,21 @@ def container_config_get(name, config_key, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) - return _get_property_dict_item(container, 'config', config_key) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) + return _get_property_dict_item(container, "config", config_key) -def container_config_set(name, config_key, config_value, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_config_set( + name, + config_key, + config_value, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Set a container config value name : @@ -1301,19 +1297,16 @@ def container_config_set(name, config_key, config_value, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) - return _set_property_dict_item( - container, 'config', config_key, config_value - ) + return _set_property_dict_item(container, "config", config_key, config_value) -def container_config_delete(name, config_key, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_config_delete( + name, config_key, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Delete a container config value name : @@ -1346,19 +1339,16 @@ def container_config_delete(name, config_key, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) - return _delete_property_dict_item( - container, 'config', config_key - ) + return _delete_property_dict_item(container, "config", config_key) -def container_device_get(name, device_name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_device_get( + name, device_name, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Get a container device name : @@ -1391,19 +1381,23 @@ def container_device_get(name, device_name, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) - return _get_property_dict_item(container, 'devices', device_name) + return _get_property_dict_item(container, "devices", device_name) -def container_device_add(name, device_name, device_type='disk', - remote_addr=None, - cert=None, key=None, verify_cert=True, - **kwargs): - ''' +def container_device_add( + name, + device_name, + device_type="disk", + remote_addr=None, + cert=None, + key=None, + verify_cert=True, + **kwargs +): + """ Add a container device name : @@ -1442,20 +1436,17 @@ def container_device_add(name, device_name, device_type='disk', Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) - kwargs['type'] = device_type - return _set_property_dict_item( - container, 'devices', device_name, kwargs - ) + kwargs["type"] = device_type + return _set_property_dict_item(container, "devices", device_name, kwargs) -def container_device_delete(name, device_name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_device_delete( + name, device_name, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Delete a container device name : @@ -1488,21 +1479,28 @@ def container_device_delete(name, device_name, remote_addr=None, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) - return _delete_property_dict_item( - container, 'devices', device_name - ) + return _delete_property_dict_item(container, "devices", device_name) -def container_file_put(name, src, dst, recursive=False, overwrite=False, - mode=None, uid=None, gid=None, saltenv='base', - remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_file_put( + name, + src, + dst, + recursive=False, + overwrite=False, + mode=None, + uid=None, + gid=None, + saltenv="base", + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Put a file into a container name : @@ -1560,7 +1558,7 @@ def container_file_put(name, src, dst, recursive=False, overwrite=False, salt '*' lxd.container_file_put /var/tmp/foo /var/tmp/ - ''' + """ # Possibilities: # (src, dst, dir, dir1, and dir2 are directories) # cp /src/file1 /dst/file1 @@ -1576,22 +1574,20 @@ def container_file_put(name, src, dst, recursive=False, overwrite=False, # the decimal integer 600 (and not the octal 0600). So, it it's # and integer, handle it as if it where a octal representation. mode = six.text_type(mode) - if not mode.startswith('0'): - mode = '0{0}'.format(mode) + if not mode.startswith("0"): + mode = "0{0}".format(mode) - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) src = os.path.expanduser(src) if not os.path.isabs(src): - if src.find('://') >= 0: - cached_file = __salt__['cp.cache_file'](src, saltenv=saltenv) + if src.find("://") >= 0: + cached_file = __salt__["cp.cache_file"](src, saltenv=saltenv) if not cached_file: raise SaltInvocationError("File '{0}' not found".format(src)) if not os.path.isabs(cached_file): - raise SaltInvocationError('File path must be absolute.') + raise SaltInvocationError("File path must be absolute.") src = cached_file # Make sure that src doesn't end with '/', unless it's '/' @@ -1600,23 +1596,23 @@ def container_file_put(name, src, dst, recursive=False, overwrite=False, src = os.path.sep if not os.path.exists(src): - raise CommandExecutionError( - 'No such file or directory \'{0}\''.format(src) - ) + raise CommandExecutionError("No such file or directory '{0}'".format(src)) if os.path.isdir(src) and not recursive: raise SaltInvocationError( - ("Cannot copy overwriting a directory " - "without recursive flag set to true!") + ( + "Cannot copy overwriting a directory " + "without recursive flag set to true!" + ) ) try: dst_is_directory = False - container.files.get(os.path.join(dst, '.')) + container.files.get(os.path.join(dst, ".")) except pylxd.exceptions.NotFound: pass except pylxd.exceptions.LXDAPIException as why: - if six.text_type(why).find('Is a directory') >= 0: + if six.text_type(why).find("Is a directory") >= 0: dst_is_directory = True if os.path.isfile(src): @@ -1630,7 +1626,7 @@ def container_file_put(name, src, dst, recursive=False, overwrite=False, except pylxd.exceptions.NotFound: found = False except pylxd.exceptions.LXDAPIException as why: - if six.text_type(why).find('not found') >= 0: + if six.text_type(why).find("not found") >= 0: # Old version of pylxd found = False else: @@ -1649,16 +1645,11 @@ def container_file_put(name, src, dst, recursive=False, overwrite=False, if gid is None: gid = stat.st_gid - with salt.utils.files.fopen(src, 'rb') as src_fp: - container.files.put( - dst, src_fp.read(), - mode=mode, uid=uid, gid=gid - ) + with salt.utils.files.fopen(src, "rb") as src_fp: + container.files.put(dst, src_fp.read(), mode=mode, uid=uid, gid=gid) return True elif not os.path.isdir(src): - raise SaltInvocationError( - "Source is neither file nor directory" - ) + raise SaltInvocationError("Source is neither file nor directory") # Source is a directory # idx for dstdir = dst + src[idx:] @@ -1671,11 +1662,11 @@ def container_file_put(name, src, dst, recursive=False, overwrite=False, # Check that the parent directory of dst exists # and is a directory try: - container.files.get(os.path.join(os.path.dirname(dst), '.')) + container.files.get(os.path.join(os.path.dirname(dst), ".")) except pylxd.exceptions.NotFound: pass except pylxd.exceptions.LXDAPIException as why: - if six.text_type(why).find('Is a directory') >= 0: + if six.text_type(why).find("Is a directory") >= 0: dst_is_directory = True # destination is non-existent # cp -r /src/dir1 /scr/dir1 @@ -1685,9 +1676,7 @@ def container_file_put(name, src, dst, recursive=False, overwrite=False, # Copy src directory recursive if not overwrite: - raise SaltInvocationError( - "Destination exists and overwrite is false" - ) + raise SaltInvocationError("Destination exists and overwrite is false") # Collect all directories first, to create them in one call # (for performance reasons) @@ -1695,7 +1684,7 @@ def container_file_put(name, src, dst, recursive=False, overwrite=False, for path, _, files in os.walk(src): dstdir = os.path.join(dst, path[idx:].lstrip(os.path.sep)) dstdirs.append(dstdir) - container.execute(['mkdir', '-p'] + dstdirs) + container.execute(["mkdir", "-p"] + dstdirs) set_mode = mode set_uid = uid @@ -1717,19 +1706,28 @@ def container_file_put(name, src, dst, recursive=False, overwrite=False, if gid is None: set_gid = stat.st_gid - with salt.utils.files.fopen(src_name, 'rb') as src_fp: + with salt.utils.files.fopen(src_name, "rb") as src_fp: container.files.put( - dst_name, src_fp.read(), - mode=set_mode, uid=set_uid, gid=set_gid + dst_name, src_fp.read(), mode=set_mode, uid=set_uid, gid=set_gid ) return True -def container_file_get(name, src, dst, overwrite=False, - mode=None, uid=None, gid=None, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_file_get( + name, + src, + dst, + overwrite=False, + mode=None, + uid=None, + gid=None, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Get a file from a container name : @@ -1775,7 +1773,7 @@ def container_file_get(name, src, dst, overwrite=False, but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' + """ # Fix mode. Salt commandline doesn't use octals, so 0600 will be # the decimal integer 600 (and not the octal 0600). So, it it's # and integer, handle it as if it where a octal representation. @@ -1783,52 +1781,44 @@ def container_file_get(name, src, dst, overwrite=False, # Do only if mode is not None, otherwise we get 0None if mode is not None: mode = six.text_type(mode) - if not mode.startswith('0'): - mode = '0{0}'.format(mode) + if not mode.startswith("0"): + mode = "0{0}".format(mode) - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) dst = os.path.expanduser(dst) if not os.path.isabs(dst): - raise SaltInvocationError('File path must be absolute.') + raise SaltInvocationError("File path must be absolute.") if os.path.isdir(dst): dst = os.path.join(dst, os.path.basename(src)) elif not os.path.isdir(os.path.dirname(dst)): - raise SaltInvocationError( - "Parent directory for destination doesn't exist." - ) + raise SaltInvocationError("Parent directory for destination doesn't exist.") if os.path.exists(dst): if not overwrite: - raise SaltInvocationError( - 'Destination exists and overwrite is false.' - ) + raise SaltInvocationError("Destination exists and overwrite is false.") if not os.path.isfile(dst): - raise SaltInvocationError( - 'Destination exists but is not a file.' - ) + raise SaltInvocationError("Destination exists but is not a file.") else: dst_path = os.path.dirname(dst) if not os.path.isdir(dst_path): raise CommandExecutionError( - 'No such file or directory \'{0}\''.format(dst_path) + "No such file or directory '{0}'".format(dst_path) ) # Seems to be duplicate of line 1794, produces /path/file_name/file_name - #dst = os.path.join(dst, os.path.basename(src)) + # dst = os.path.join(dst, os.path.basename(src)) - with salt.utils.files.fopen(dst, 'wb') as df: + with salt.utils.files.fopen(dst, "wb") as df: df.write(container.files.get(src)) if mode: os.chmod(dst, mode) - if uid or uid is '0': + if uid or uid is "0": uid = int(uid) else: uid = -1 - if gid or gid is '0': + if gid or gid is "0": gid = int(gid) else: gid = -1 @@ -1837,9 +1827,10 @@ def container_file_get(name, src, dst, overwrite=False, return True -def container_execute(name, cmd, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def container_execute( + name, cmd, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Execute a command list on a container. name : @@ -1882,24 +1873,16 @@ def container_execute(name, cmd, remote_addr=None, salt '*' lxd.container_execute '["ls", "-l"]' - ''' - container = container_get( - name, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(name, remote_addr, cert, key, verify_cert, _raw=True) try: result = container.execute(cmd) saltresult = {} - if not hasattr(result, 'exit_code'): - saltresult = dict( - exit_code=0, - stdout=result[0], - stderr=result[1], - ) + if not hasattr(result, "exit_code"): + saltresult = dict(exit_code=0, stdout=result[0], stderr=result[1],) else: saltresult = dict( - exit_code=result.exit_code, - stdout=result.stdout, - stderr=result.stderr, + exit_code=result.exit_code, stdout=result.stdout, stderr=result.stderr, ) except pylxd.exceptions.NotFound as e: # TODO: Using exit_code 0 here is not always right, @@ -1907,10 +1890,10 @@ def container_execute(name, cmd, remote_addr=None, # See: https://github.com/lxc/pylxd/issues/280 saltresult = dict(exit_code=0, stdout="", stderr=six.text_type(e)) - if int(saltresult['exit_code']) > 0: - saltresult['result'] = False + if int(saltresult["exit_code"]) > 0: + saltresult["result"] = False else: - saltresult['result'] = True + saltresult["result"] = True return saltresult @@ -1918,9 +1901,10 @@ def container_execute(name, cmd, remote_addr=None, #################### # Profile Management #################### -def profile_list(list_names=False, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' Lists all profiles from the LXD. +def profile_list( + list_names=False, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Lists all profiles from the LXD. list_names : @@ -1957,7 +1941,7 @@ def profile_list(list_names=False, remote_addr=None, salt '*' lxd.profile_list true --out=json salt '*' lxd.profile_list --out=json - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) @@ -1968,10 +1952,17 @@ def profile_list(list_names=False, remote_addr=None, return map(_pylxd_model_to_dict, profiles) -def profile_create(name, config=None, devices=None, description=None, - remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' Creates a profile. +def profile_create( + name, + config=None, + devices=None, + description=None, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Creates a profile. name : The name of the profile to get. @@ -2024,13 +2015,10 @@ def profile_create(name, config=None, devices=None, description=None, See the `lxd-docs`_ for the details about the config and devices dicts. .. _lxd-docs: https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-10 - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) - config, devices = normalize_input_values( - config, - devices - ) + config, devices = normalize_input_values(config, devices) try: profile = client.profiles.create(name, config, devices) @@ -2044,9 +2032,10 @@ def profile_create(name, config=None, devices=None, description=None, return _pylxd_model_to_dict(profile) -def profile_get(name, remote_addr=None, - cert=None, key=None, verify_cert=True, _raw=False): - ''' Gets a profile from the LXD +def profile_get( + name, remote_addr=None, cert=None, key=None, verify_cert=True, _raw=False +): + """ Gets a profile from the LXD name : The name of the profile to get. @@ -2084,16 +2073,14 @@ def profile_get(name, remote_addr=None, .. code-block:: bash $ salt '*' lxd.profile_get autostart - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) profile = None try: profile = client.profiles.get(name) except pylxd.exceptions.LXDAPIException: - raise SaltInvocationError( - 'Profile \'{0}\' not found'.format(name) - ) + raise SaltInvocationError("Profile '{0}' not found".format(name)) if _raw: return profile @@ -2101,9 +2088,8 @@ def profile_get(name, remote_addr=None, return _pylxd_model_to_dict(profile) -def profile_delete(name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' Deletes a profile. +def profile_delete(name, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Deletes a profile. name : The name of the profile to delete. @@ -2138,23 +2124,17 @@ def profile_delete(name, remote_addr=None, .. code-block:: bash $ salt '*' lxd.profile_delete shared_mounts - ''' - profile = profile_get( - name, - remote_addr, - cert, - key, - verify_cert, - _raw=True - ) + """ + profile = profile_get(name, remote_addr, cert, key, verify_cert, _raw=True) profile.delete() return True -def profile_config_get(name, config_key, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' Get a profile config item. +def profile_config_get( + name, config_key, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Get a profile config item. name : The name of the profile to get the config item from. @@ -2192,23 +2172,22 @@ def profile_config_get(name, config_key, remote_addr=None, .. code-block:: bash $ salt '*' lxd.profile_config_get autostart boot.autostart - ''' - profile = profile_get( - name, - remote_addr, - cert, - key, - verify_cert, - _raw=True - ) + """ + profile = profile_get(name, remote_addr, cert, key, verify_cert, _raw=True) - return _get_property_dict_item(profile, 'config', config_key) + return _get_property_dict_item(profile, "config", config_key) -def profile_config_set(name, config_key, config_value, - remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' Set a profile config item. +def profile_config_set( + name, + config_key, + config_value, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Set a profile config item. name : The name of the profile to set the config item to. @@ -2249,24 +2228,16 @@ def profile_config_set(name, config_key, config_value, .. code-block:: bash $ salt '*' lxd.profile_config_set autostart boot.autostart 0 - ''' - profile = profile_get( - name, - remote_addr, - cert, - key, - verify_cert, - _raw=True - ) + """ + profile = profile_get(name, remote_addr, cert, key, verify_cert, _raw=True) - return _set_property_dict_item( - profile, 'config', config_key, config_value - ) + return _set_property_dict_item(profile, "config", config_key, config_value) -def profile_config_delete(name, config_key, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' Delete a profile config item. +def profile_config_delete( + name, config_key, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Delete a profile config item. name : The name of the profile to delete the config item. @@ -2304,24 +2275,16 @@ def profile_config_delete(name, config_key, remote_addr=None, .. code-block:: bash $ salt '*' lxd.profile_config_delete autostart boot.autostart.delay - ''' - profile = profile_get( - name, - remote_addr, - cert, - key, - verify_cert, - _raw=True - ) + """ + profile = profile_get(name, remote_addr, cert, key, verify_cert, _raw=True) - return _delete_property_dict_item( - profile, 'config', config_key - ) + return _delete_property_dict_item(profile, "config", config_key) -def profile_device_get(name, device_name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' Get a profile device. +def profile_device_get( + name, device_name, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Get a profile device. name : The name of the profile to get the device from. @@ -2359,24 +2322,23 @@ def profile_device_get(name, device_name, remote_addr=None, .. code-block:: bash $ salt '*' lxd.profile_device_get default eth0 - ''' - profile = profile_get( - name, - remote_addr, - cert, - key, - verify_cert, - _raw=True - ) + """ + profile = profile_get(name, remote_addr, cert, key, verify_cert, _raw=True) - return _get_property_dict_item(profile, 'devices', device_name) + return _get_property_dict_item(profile, "devices", device_name) -def profile_device_set(name, device_name, device_type='disk', - remote_addr=None, - cert=None, key=None, verify_cert=True, - **kwargs): - ''' Set a profile device. +def profile_device_set( + name, + device_name, + device_type="disk", + remote_addr=None, + cert=None, + key=None, + verify_cert=True, + **kwargs +): + """ Set a profile device. name : The name of the profile to set the device to. @@ -2414,29 +2376,21 @@ def profile_device_set(name, device_name, device_type='disk', .. code-block:: bash $ salt '*' lxd.profile_device_set autostart eth1 nic nictype=bridged parent=lxdbr0 - ''' - profile = profile_get( - name, - remote_addr, - cert, - key, - verify_cert, - _raw=True - ) + """ + profile = profile_get(name, remote_addr, cert, key, verify_cert, _raw=True) - kwargs['type'] = device_type + kwargs["type"] = device_type for k, v in six.iteritems(kwargs): kwargs[k] = six.text_type(v) - return _set_property_dict_item( - profile, 'devices', device_name, kwargs - ) + return _set_property_dict_item(profile, "devices", device_name, kwargs) -def profile_device_delete(name, device_name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' Delete a profile device. +def profile_device_delete( + name, device_name, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Delete a profile device. name : The name of the profile to delete the device. @@ -2475,27 +2429,19 @@ def profile_device_delete(name, device_name, remote_addr=None, $ salt '*' lxd.profile_device_delete autostart eth1 - ''' - profile = profile_get( - name, - remote_addr, - cert, - key, - verify_cert, - _raw=True - ) + """ + profile = profile_get(name, remote_addr, cert, key, verify_cert, _raw=True) - return _delete_property_dict_item( - profile, 'devices', device_name - ) + return _delete_property_dict_item(profile, "devices", device_name) ################## # Image Management ################## -def image_list(list_aliases=False, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' Lists all images from the LXD. +def image_list( + list_aliases=False, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Lists all images from the LXD. list_aliases : @@ -2533,23 +2479,20 @@ def image_list(list_aliases=False, remote_addr=None, $ salt '*' lxd.image_list true --out=json $ salt '*' lxd.image_list --out=json - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) images = client.images.all() if list_aliases: - return {i.fingerprint: [a['name'] for a in i.aliases] for i in images} + return {i.fingerprint: [a["name"] for a in i.aliases] for i in images} return map(_pylxd_model_to_dict, images) -def image_get(fingerprint, - remote_addr=None, - cert=None, - key=None, - verify_cert=True, - _raw=False): - ''' Get an image by its fingerprint +def image_get( + fingerprint, remote_addr=None, cert=None, key=None, verify_cert=True, _raw=False +): + """ Get an image by its fingerprint fingerprint : The fingerprint of the image to retrieve @@ -2587,7 +2530,7 @@ def image_get(fingerprint, ..code-block:: bash $ salt '*' lxd.image_get - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) image = None @@ -2595,7 +2538,7 @@ def image_get(fingerprint, image = client.images.get(fingerprint) except pylxd.exceptions.LXDAPIException: raise SaltInvocationError( - 'Image with fingerprint \'{0}\' not found'.format(fingerprint) + "Image with fingerprint '{0}' not found".format(fingerprint) ) if _raw: @@ -2604,13 +2547,10 @@ def image_get(fingerprint, return _pylxd_model_to_dict(image) -def image_get_by_alias(alias, - remote_addr=None, - cert=None, - key=None, - verify_cert=True, - _raw=False): - ''' Get an image by an alias +def image_get_by_alias( + alias, remote_addr=None, cert=None, key=None, verify_cert=True, _raw=False +): + """ Get an image by an alias alias : The alias of the image to retrieve @@ -2648,16 +2588,14 @@ def image_get_by_alias(alias, ..code-block:: bash $ salt '*' lxd.image_get_by_alias xenial/amd64 - ''' + """ client = pylxd_client_get(remote_addr, cert, key, verify_cert) image = None try: image = client.images.get_by_alias(alias) except pylxd.exceptions.LXDAPIException: - raise SaltInvocationError( - 'Image with alias \'{0}\' not found'.format(alias) - ) + raise SaltInvocationError("Image with alias '{0}' not found".format(alias)) if _raw: return image @@ -2665,12 +2603,8 @@ def image_get_by_alias(alias, return _pylxd_model_to_dict(image) -def image_delete(image, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' Delete an image by an alias or fingerprint +def image_delete(image, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Delete an image by an alias or fingerprint name : The alias or fingerprint of the image to delete, @@ -2706,7 +2640,7 @@ def image_delete(image, ..code-block:: bash $ salt '*' lxd.image_delete xenial/amd64 - ''' + """ image = _verify_image(image, remote_addr, cert, key, verify_cert) @@ -2714,17 +2648,19 @@ def image_delete(image, return True -def image_from_simplestreams(server, - alias, - remote_addr=None, - cert=None, - key=None, - verify_cert=True, - aliases=None, - public=False, - auto_update=False, - _raw=False): - ''' Create an image from simplestreams +def image_from_simplestreams( + server, + alias, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, + aliases=None, + public=False, + auto_update=False, + _raw=False, +): + """ Create an image from simplestreams server : Simplestreams server URI @@ -2774,7 +2710,7 @@ def image_from_simplestreams(server, ..code-block:: bash $ salt '*' lxd.image_from_simplestreams "https://cloud-images.ubuntu.com/releases" "trusty/amd64" aliases='["t", "trusty/amd64"]' auto_update=True - ''' + """ if aliases is None: aliases = [] @@ -2797,16 +2733,18 @@ def image_from_simplestreams(server, return _pylxd_model_to_dict(image) -def image_from_url(url, - remote_addr=None, - cert=None, - key=None, - verify_cert=True, - aliases=None, - public=False, - auto_update=False, - _raw=False): - ''' Create an image from an url +def image_from_url( + url, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, + aliases=None, + public=False, + auto_update=False, + _raw=False, +): + """ Create an image from an url url : The URL from where to download the image @@ -2853,7 +2791,7 @@ def image_from_url(url, ..code-block:: bash $ salt '*' lxd.image_from_url https://dl.stgraber.org/lxd aliases='["busybox-amd64"]' - ''' + """ if aliases is None: aliases = [] @@ -2876,16 +2814,18 @@ def image_from_url(url, return _pylxd_model_to_dict(image) -def image_from_file(filename, - remote_addr=None, - cert=None, - key=None, - verify_cert=True, - aliases=None, - public=False, - saltenv='base', - _raw=False): - ''' Create an image from a file +def image_from_file( + filename, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, + aliases=None, + public=False, + saltenv="base", + _raw=False, +): + """ Create an image from a file filename : The filename of the rootfs @@ -2932,13 +2872,13 @@ def image_from_file(filename, ..code-block:: bash $ salt '*' lxd.image_from_file salt://lxd/files/busybox.tar.xz aliases=["busybox-amd64"] - ''' + """ if aliases is None: aliases = [] - cached_file = __salt__['cp.cache_file'](filename, saltenv=saltenv) - data = b'' - with salt.utils.files.fopen(cached_file, 'r+b') as fp: + cached_file = __salt__["cp.cache_file"](filename, saltenv=saltenv) + data = b"" + with salt.utils.files.fopen(cached_file, "r+b") as fp: data = fp.read() client = pylxd_client_get(remote_addr, cert, key, verify_cert) @@ -2958,20 +2898,22 @@ def image_from_file(filename, return _pylxd_model_to_dict(image) -def image_copy_lxd(source, - src_remote_addr, - src_cert, - src_key, - src_verify_cert, - remote_addr, - cert, - key, - verify_cert=True, - aliases=None, - public=None, - auto_update=None, - _raw=False): - ''' Copy an image from another LXD instance +def image_copy_lxd( + source, + src_remote_addr, + src_cert, + src_key, + src_verify_cert, + remote_addr, + cert, + key, + verify_cert=True, + aliases=None, + public=None, + auto_update=None, + _raw=False, +): + """ Copy an image from another LXD instance source : An alias or a fingerprint of the source. @@ -3039,7 +2981,7 @@ def image_copy_lxd(source, .. code-block:: bash $ salt '*' lxd.image_copy_lxd xenial/amd64 https://srv01:8443 ~/.config/lxc/client.crt ~/.config/lxc/client.key false https://srv02:8443 ~/.config/lxc/client.crt ~/.config/lxc/client.key false aliases="['xenial/amd64']" - ''' + """ if aliases is None: aliases = [] @@ -3055,13 +2997,11 @@ def image_copy_lxd(source, src_image = None try: src_image = image_get_by_alias( - source, src_remote_addr, src_cert, - src_key, src_verify_cert, _raw=True + source, src_remote_addr, src_cert, src_key, src_verify_cert, _raw=True ) except SaltInvocationError: src_image = image_get( - source, src_remote_addr, src_cert, - src_key, src_verify_cert, _raw=True + source, src_remote_addr, src_cert, src_key, src_verify_cert, _raw=True ) # Will fail with a CommandExecutionError on connection problems. @@ -3081,14 +3021,16 @@ def image_copy_lxd(source, return _pylxd_model_to_dict(dest_image) -def image_alias_add(image, - alias, - description='', - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' Create an alias on the given image +def image_alias_add( + image, + alias, + description="", + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Create an alias on the given image image : An image alias, a fingerprint or a image object @@ -3129,24 +3071,21 @@ def image_alias_add(image, .. code-block:: bash $ salt '*' lxd.image_alias_add xenial/amd64 x "Short version of xenial/amd64" - ''' + """ image = _verify_image(image, remote_addr, cert, key, verify_cert) for alias_info in image.aliases: - if alias_info['name'] == alias: + if alias_info["name"] == alias: return True image.add_alias(alias, description) return True -def image_alias_delete(image, - alias, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' Delete an alias (this is currently not restricted to the image) +def image_alias_delete( + image, alias, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Delete an alias (this is currently not restricted to the image) image : An image alias, a fingerprint or a image object @@ -3184,7 +3123,7 @@ def image_alias_delete(image, .. code-block:: bash $ salt '*' lxd.image_alias_add xenial/amd64 x "Short version of xenial/amd64" - ''' + """ image = _verify_image(image, remote_addr, cert, key, verify_cert) try: @@ -3194,13 +3133,14 @@ def image_alias_delete(image, return True + ##################### # Snapshot Management ##################### def snapshots_all(container, remote_addr=None, cert=None, key=None, verify_cert=True): - ''' + """ Get all snapshots for a container container : @@ -3234,7 +3174,7 @@ def snapshots_all(container, remote_addr=None, cert=None, key=None, verify_cert= .. code-block:: bash $ salt '*' lxd.snapshots_all test-container - ''' + """ containers = container_get( container, remote_addr, cert, key, verify_cert, _raw=True ) @@ -3242,15 +3182,15 @@ def snapshots_all(container, remote_addr=None, cert=None, key=None, verify_cert= containers = [containers] ret = {} for cont in containers: - ret.update({cont.name: [{'name': c.name} - for c in cont.snapshots.all()]}) + ret.update({cont.name: [{"name": c.name} for c in cont.snapshots.all()]}) return ret -def snapshots_create(container, name=None, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def snapshots_create( + container, name=None, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Create a snapshot for a container container : @@ -3287,25 +3227,24 @@ def snapshots_create(container, name=None, remote_addr=None, .. code-block:: bash $ salt '*' lxd.snapshots_create test-container test-snapshot - ''' - cont = container_get( - container, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + cont = container_get(container, remote_addr, cert, key, verify_cert, _raw=True) if not name: - name = datetime.now().strftime('%Y%m%d%H%M%S') + name = datetime.now().strftime("%Y%m%d%H%M%S") cont.snapshots.create(name) for c in snapshots_all(container).get(container): - if c.get('name') == name: - return {'name': name} + if c.get("name") == name: + return {"name": name} - return {'name': False} + return {"name": False} -def snapshots_delete(container, name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def snapshots_delete( + container, name, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Delete a snapshot for a container container : @@ -3342,10 +3281,8 @@ def snapshots_delete(container, name, remote_addr=None, .. code-block:: bash $ salt '*' lxd.snapshots_delete test-container test-snapshot - ''' - cont = container_get( - container, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + cont = container_get(container, remote_addr, cert, key, verify_cert, _raw=True) try: for s in cont.snapshots.all(): @@ -3358,9 +3295,10 @@ def snapshots_delete(container, name, remote_addr=None, return False -def snapshots_get(container, name, remote_addr=None, - cert=None, key=None, verify_cert=True): - ''' +def snapshots_get( + container, name, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Get information about snapshot for a container container : @@ -3397,19 +3335,18 @@ def snapshots_get(container, name, remote_addr=None, .. code-block:: bash $ salt '*' lxd.snapshots_get test-container test-snapshot - ''' - container = container_get( - container, remote_addr, cert, key, verify_cert, _raw=True - ) + """ + container = container_get(container, remote_addr, cert, key, verify_cert, _raw=True) return container.snapshots.get(name) + ################ # Helper Methods ################ def normalize_input_values(config, devices): - ''' + """ normalize config input so returns can be put into mongodb, which doesn't like `.` This is not meant to be used on the commandline. @@ -3419,19 +3356,15 @@ def normalize_input_values(config, devices): .. code-block:: bash salt '*' lxd.normalize_input_values config={} devices={} - ''' + """ if isinstance(config, list): - if (config and - 'key' in config[0] and - 'value' in config[0]): - config = {d['key']: d['value'] for d in config} + if config and "key" in config[0] and "value" in config[0]: + config = {d["key"]: d["value"] for d in config} else: config = {} if isinstance(config, six.string_types): - raise SaltInvocationError( - "config can't be a string, validate your YAML input." - ) + raise SaltInvocationError("config can't be a string, validate your YAML input.") if isinstance(devices, six.string_types): raise SaltInvocationError( @@ -3447,11 +3380,14 @@ def normalize_input_values(config, devices): for k, v in six.iteritems(devices[dn]): devices[dn][k] = v - return (config, devices,) + return ( + config, + devices, + ) def sync_config_devices(obj, newconfig, newdevices, test=False): - ''' Syncs the given config and devices with the object + """ Syncs the given config and devices with the object (a profile or a container) returns a changes dict with all changes made. @@ -3466,7 +3402,7 @@ def sync_config_devices(obj, newconfig, newdevices, test=False): test: Wherever to not change anything and give "Would change" message. - ''' + """ changes = {} # @@ -3475,23 +3411,31 @@ def sync_config_devices(obj, newconfig, newdevices, test=False): if newconfig is None: newconfig = {} - newconfig = dict(list(zip( - map(six.text_type, newconfig.keys()), - map(six.text_type, newconfig.values()) - ))) + newconfig = dict( + list( + zip( + map(six.text_type, newconfig.keys()), + map(six.text_type, newconfig.values()), + ) + ) + ) cck = set(newconfig.keys()) - obj.config = dict(list(zip( - map(six.text_type, obj.config.keys()), - map(six.text_type, obj.config.values()) - ))) + obj.config = dict( + list( + zip( + map(six.text_type, obj.config.keys()), + map(six.text_type, obj.config.values()), + ) + ) + ) ock = set(obj.config.keys()) config_changes = {} # Removed keys for k in ock.difference(cck): # Ignore LXD internals. - if k.startswith('volatile.') or k.startswith('image.'): + if k.startswith("volatile.") or k.startswith("image."): continue if not test: @@ -3507,14 +3451,13 @@ def sync_config_devices(obj, newconfig, newdevices, test=False): # same keys for k in cck.intersection(ock): # Ignore LXD internals. - if k.startswith('volatile.') or k.startswith('image.'): + if k.startswith("volatile.") or k.startswith("image."): continue if newconfig[k] != obj.config[k]: if not test: config_changes[k] = ( - 'Changed config key "{0}" to "{1}", ' - 'its value was "{2}"' + 'Changed config key "{0}" to "{1}", ' 'its value was "{2}"' ).format(k, newconfig[k], obj.config[k]) obj.config[k] = newconfig[k] else: @@ -3526,21 +3469,21 @@ def sync_config_devices(obj, newconfig, newdevices, test=False): # New keys for k in cck.difference(ock): # Ignore LXD internals. - if k.startswith('volatile.') or k.startswith('image.'): + if k.startswith("volatile.") or k.startswith("image."): continue if not test: - config_changes[k] = ( - 'Added config key "{0}" = "{1}"' - ).format(k, newconfig[k]) + config_changes[k] = ('Added config key "{0}" = "{1}"').format( + k, newconfig[k] + ) obj.config[k] = newconfig[k] else: - config_changes[k] = ( - 'Would add config key "{0}" = "{1}"' - ).format(k, newconfig[k]) + config_changes[k] = ('Would add config key "{0}" = "{1}"').format( + k, newconfig[k] + ) if config_changes: - changes['config'] = config_changes + changes["config"] = config_changes # # devices changes @@ -3555,23 +3498,19 @@ def sync_config_devices(obj, newconfig, newdevices, test=False): # Removed devices for k in dk.difference(ndk): # Ignore LXD internals. - if k == u'root': + if k == "root": continue if not test: - devices_changes[k] = ( - 'Removed device "{0}"' - ).format(k) + devices_changes[k] = ('Removed device "{0}"').format(k) del obj.devices[k] else: - devices_changes[k] = ( - 'Would remove device "{0}"' - ).format(k) + devices_changes[k] = ('Would remove device "{0}"').format(k) # Changed devices for k, v in six.iteritems(obj.devices): # Ignore LXD internals also for new devices. - if k == u'root': + if k == "root": continue if k not in newdevices: @@ -3580,64 +3519,52 @@ def sync_config_devices(obj, newconfig, newdevices, test=False): if newdevices[k] != v: if not test: - devices_changes[k] = ( - 'Changed device "{0}"' - ).format(k) + devices_changes[k] = ('Changed device "{0}"').format(k) obj.devices[k] = newdevices[k] else: - devices_changes[k] = ( - 'Would change device "{0}"' - ).format(k) + devices_changes[k] = ('Would change device "{0}"').format(k) # New devices for k in ndk.difference(dk): # Ignore LXD internals. - if k == u'root': + if k == "root": continue if not test: - devices_changes[k] = ( - 'Added device "{0}"' - ).format(k) + devices_changes[k] = ('Added device "{0}"').format(k) obj.devices[k] = newdevices[k] else: - devices_changes[k] = ( - 'Would add device "{0}"' - ).format(k) + devices_changes[k] = ('Would add device "{0}"').format(k) if devices_changes: - changes['devices'] = devices_changes + changes["devices"] = devices_changes return changes def _set_property_dict_item(obj, prop, key, value): - ''' Sets the dict item key of the attr from obj. + """ Sets the dict item key of the attr from obj. Basicaly it does getattr(obj, prop)[key] = value. For the disk device we added some checks to make device changes on the CLI saver. - ''' + """ attr = getattr(obj, prop) - if prop == 'devices': - device_type = value['type'] + if prop == "devices": + device_type = value["type"] - if device_type == 'disk': + if device_type == "disk": - if 'path' not in value: - raise SaltInvocationError( - "path must be given as parameter" - ) + if "path" not in value: + raise SaltInvocationError("path must be given as parameter") - if value['path'] != '/' and 'source' not in value: - raise SaltInvocationError( - "source must be given as parameter" - ) + if value["path"] != "/" and "source" not in value: + raise SaltInvocationError("source must be given as parameter") for k in value.keys(): - if k.startswith('__'): + if k.startswith("__"): del value[k] attr[key] = value @@ -3653,9 +3580,7 @@ def _set_property_dict_item(obj, prop, key, value): def _get_property_dict_item(obj, prop, key): attr = getattr(obj, prop) if key not in attr: - raise SaltInvocationError( - "'{0}' doesn't exists".format(key) - ) + raise SaltInvocationError("'{0}' doesn't exists".format(key)) return attr[key] @@ -3663,9 +3588,7 @@ def _get_property_dict_item(obj, prop, key): def _delete_property_dict_item(obj, prop, key): attr = getattr(obj, prop) if key not in attr: - raise SaltInvocationError( - "'{0}' doesn't exists".format(key) - ) + raise SaltInvocationError("'{0}' doesn't exists".format(key)) del attr[key] pylxd_save_object(obj) @@ -3673,11 +3596,7 @@ def _delete_property_dict_item(obj, prop, key): return True -def _verify_image(image, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): +def _verify_image(image, remote_addr=None, cert=None, key=None, verify_cert=True): # Get image by alias/fingerprint or check for fingerprint attribute if isinstance(image, six.string_types): name = image @@ -3688,23 +3607,17 @@ def _verify_image(image, image = None try: image = image_get_by_alias( - name, remote_addr, cert, - key, verify_cert, _raw=True + name, remote_addr, cert, key, verify_cert, _raw=True ) except SaltInvocationError: - image = image_get( - name, remote_addr, cert, - key, verify_cert, _raw=True - ) - elif not hasattr(image, 'fingerprint'): - raise SaltInvocationError( - 'Invalid image \'{0}\''.format(image) - ) + image = image_get(name, remote_addr, cert, key, verify_cert, _raw=True) + elif not hasattr(image, "fingerprint"): + raise SaltInvocationError("Invalid image '{0}'".format(image)) return image def _pylxd_model_to_dict(obj): - '''Translates a plyxd model object to a dict''' + """Translates a plyxd model object to a dict""" marshalled = {} for key in obj.__attributes__.keys(): if hasattr(obj, key): @@ -3717,13 +3630,13 @@ def _pylxd_model_to_dict(obj): # if HAS_PYLXD: - import pylxd.exceptions # NOQA + import pylxd.exceptions # NOQA - if not hasattr(pylxd.exceptions, 'NotFound'): + if not hasattr(pylxd.exceptions, "NotFound"): # Old version of pylxd class NotFound(pylxd.exceptions.LXDAPIException): - '''An exception raised when an object is not found.''' + """An exception raised when an object is not found.""" pylxd.exceptions.NotFound = NotFound @@ -3733,22 +3646,21 @@ if HAS_PYLXD: from pylxd.models.container import Container class FilesManager(Container.FilesManager): - def put(self, filepath, data, mode=None, uid=None, gid=None): headers = {} if mode is not None: if isinstance(mode, int): mode = oct(mode) - elif not mode.startswith('0'): - mode = '0{0}'.format(mode) - headers['X-LXD-mode'] = mode + elif not mode.startswith("0"): + mode = "0{0}".format(mode) + headers["X-LXD-mode"] = mode if uid is not None: - headers['X-LXD-uid'] = six.text_type(uid) + headers["X-LXD-uid"] = six.text_type(uid) if gid is not None: - headers['X-LXD-gid'] = six.text_type(gid) - response = self._client.api.containers[ - self._container.name].files.post( - params={'path': filepath}, data=data, headers=headers) + headers["X-LXD-gid"] = six.text_type(gid) + response = self._client.api.containers[self._container.name].files.post( + params={"path": filepath}, data=data, headers=headers + ) return response.status_code == 200 Container.FilesManager = FilesManager diff --git a/salt/modules/mac_assistive.py b/salt/modules/mac_assistive.py index 283ca896cda..4c1f082b8b3 100644 --- a/salt/modules/mac_assistive.py +++ b/salt/modules/mac_assistive.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This module allows you to manage assistive access on macOS minions with 10.9+ .. versionadded:: 2016.3.0 @@ -7,12 +7,13 @@ This module allows you to manage assistive access on macOS minions with 10.9+ .. code-block:: bash salt '*' assistive.install /usr/bin/osascript -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import re +from __future__ import absolute_import, print_function, unicode_literals + import logging +import re # Import salt libs import salt.utils.platform @@ -22,22 +23,22 @@ from salt.utils.versions import LooseVersion as _LooseVersion log = logging.getLogger(__name__) -__virtualname__ = 'assistive' +__virtualname__ = "assistive" def __virtual__(): - ''' + """ Only work on Mac OS - ''' + """ if not salt.utils.platform.is_darwin(): - return False, 'Must be run on macOS' - if _LooseVersion(__grains__['osrelease']) < salt.utils.stringutils.to_str('10.9'): - return False, 'Must be run on macOS 10.9 or newer' + return False, "Must be run on macOS" + if _LooseVersion(__grains__["osrelease"]) < salt.utils.stringutils.to_str("10.9"): + return False, "Must be run on macOS 10.9 or newer" return __virtualname__ def install(app_id, enable=True): - ''' + """ Install a bundle ID or command as being allowed to use assistive access. @@ -53,38 +54,47 @@ def install(app_id, enable=True): salt '*' assistive.install /usr/bin/osascript salt '*' assistive.install com.smileonmymac.textexpander - ''' - ge_el_capitan = True if _LooseVersion(__grains__['osrelease']) >= salt.utils.stringutils.to_str('10.11') else False - ge_mojave = True if _LooseVersion(__grains__['osrelease']) >= salt.utils.stringutils.to_str('10.14') else False - client_type = _client_type(app_id) - enable_str = '1' if enable else '0' - cmd = 'sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" ' \ - '"INSERT or REPLACE INTO access VALUES(\'kTCCServiceAccessibility\',\'{0}\',{1},{2},1,NULL{3}{4})"'.\ - format(app_id, - client_type, - enable_str, - ',NULL' if ge_el_capitan else '', - ",NULL,NULL,NULL,NULL,''" if ge_mojave else '') - - call = __salt__['cmd.run_all']( - cmd, - output_loglevel='debug', - python_shell=False + """ + ge_el_capitan = ( + True + if _LooseVersion(__grains__["osrelease"]) + >= salt.utils.stringutils.to_str("10.11") + else False + ) + ge_mojave = ( + True + if _LooseVersion(__grains__["osrelease"]) + >= salt.utils.stringutils.to_str("10.14") + else False + ) + client_type = _client_type(app_id) + enable_str = "1" if enable else "0" + cmd = ( + 'sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" ' + "\"INSERT or REPLACE INTO access VALUES('kTCCServiceAccessibility','{0}',{1},{2},1,NULL{3}{4})\"".format( + app_id, + client_type, + enable_str, + ",NULL" if ge_el_capitan else "", + ",NULL,NULL,NULL,NULL,''" if ge_mojave else "", + ) ) - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] - if 'stdout' in call: - comment += call['stdout'] - raise CommandExecutionError('Error installing app: {0}'.format(comment)) + call = __salt__["cmd.run_all"](cmd, output_loglevel="debug", python_shell=False) + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] + if "stdout" in call: + comment += call["stdout"] + + raise CommandExecutionError("Error installing app: {0}".format(comment)) return True def installed(app_id): - ''' + """ Check if a bundle ID or command is listed in assistive access. This will not check to see if it's enabled. @@ -97,7 +107,7 @@ def installed(app_id): salt '*' assistive.installed /usr/bin/osascript salt '*' assistive.installed com.smileonmymac.textexpander - ''' + """ for a in _get_assistive_access(): if app_id == a[0]: return True @@ -106,7 +116,7 @@ def installed(app_id): def enable(app_id, enabled=True): - ''' + """ Enable or disable an existing assistive access application. app_id @@ -121,27 +131,29 @@ def enable(app_id, enabled=True): salt '*' assistive.enable /usr/bin/osascript salt '*' assistive.enable com.smileonmymac.textexpander enabled=False - ''' - enable_str = '1' if enabled else '0' + """ + enable_str = "1" if enabled else "0" for a in _get_assistive_access(): if app_id == a[0]: - cmd = 'sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" ' \ - '"UPDATE access SET allowed=\'{0}\' WHERE client=\'{1}\'"'.format(enable_str, app_id) - - call = __salt__['cmd.run_all']( - cmd, - output_loglevel='debug', - python_shell=False + cmd = ( + 'sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" ' + "\"UPDATE access SET allowed='{0}' WHERE client='{1}'\"".format( + enable_str, app_id + ) ) - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] - if 'stdout' in call: - comment += call['stdout'] + call = __salt__["cmd.run_all"]( + cmd, output_loglevel="debug", python_shell=False + ) - raise CommandExecutionError('Error enabling app: {0}'.format(comment)) + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] + if "stdout" in call: + comment += call["stdout"] + + raise CommandExecutionError("Error enabling app: {0}".format(comment)) return True @@ -149,7 +161,7 @@ def enable(app_id, enabled=True): def enabled(app_id): - ''' + """ Check if a bundle ID or command is listed in assistive access and enabled. @@ -162,16 +174,16 @@ def enabled(app_id): salt '*' assistive.enabled /usr/bin/osascript salt '*' assistive.enabled com.smileonmymac.textexpander - ''' + """ for a in _get_assistive_access(): - if app_id == a[0] and a[1] == '1': + if app_id == a[0] and a[1] == "1": return True return False def remove(app_id): - ''' + """ Remove a bundle ID or command as being allowed to use assistive access. app_id @@ -183,55 +195,53 @@ def remove(app_id): salt '*' assistive.remove /usr/bin/osascript salt '*' assistive.remove com.smileonmymac.textexpander - ''' - cmd = 'sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" ' \ - '"DELETE from access where client=\'{0}\'"'.format(app_id) - call = __salt__['cmd.run_all']( - cmd, - output_loglevel='debug', - python_shell=False + """ + cmd = ( + 'sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" ' + "\"DELETE from access where client='{0}'\"".format(app_id) ) + call = __salt__["cmd.run_all"](cmd, output_loglevel="debug", python_shell=False) - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] - if 'stdout' in call: - comment += call['stdout'] + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] + if "stdout" in call: + comment += call["stdout"] - raise CommandExecutionError('Error removing app: {0}'.format(comment)) + raise CommandExecutionError("Error removing app: {0}".format(comment)) return True def _client_type(app_id): - ''' + """ Determine whether the given ID is a bundle ID or a a path to a command - ''' - return '1' if app_id[0] == '/' else '0' + """ + return "1" if app_id[0] == "/" else "0" def _get_assistive_access(): - ''' + """ Get a list of all of the assistive access applications installed, returns as a ternary showing whether each app is enabled or not. - ''' + """ cmd = 'sqlite3 "/Library/Application Support/com.apple.TCC/TCC.db" "SELECT * FROM access"' - call = __salt__['cmd.run_all']( - cmd, - output_loglevel='debug', - python_shell=False + call = __salt__["cmd.run_all"](cmd, output_loglevel="debug", python_shell=False) + + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] + if "stdout" in call: + comment += call["stdout"] + + raise CommandExecutionError("Error: {0}".format(comment)) + + out = call["stdout"] + return re.findall( + r"kTCCServiceAccessibility\|(.*)\|[0-9]{1}\|([0-9]{1})\|[0-9]{1}\|", + out, + re.MULTILINE, ) - - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] - if 'stdout' in call: - comment += call['stdout'] - - raise CommandExecutionError('Error: {0}'.format(comment)) - - out = call['stdout'] - return re.findall(r'kTCCServiceAccessibility\|(.*)\|[0-9]{1}\|([0-9]{1})\|[0-9]{1}\|', out, re.MULTILINE) diff --git a/salt/modules/mac_brew_pkg.py b/salt/modules/mac_brew_pkg.py index 5484955edc1..0d2b11dc571 100644 --- a/salt/modules/mac_brew_pkg.py +++ b/salt/modules/mac_brew_pkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Homebrew for macOS .. important:: @@ -7,7 +7,7 @@ Homebrew for macOS minion, and it is using a different module (or gives an error similar to *'pkg.install' is not available*), see :ref:`here `. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -32,36 +32,39 @@ from salt.ext.six.moves import zip log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Confine this module to Mac OS with Homebrew. - ''' + """ - if salt.utils.path.which('brew') and __grains__['os'] == 'MacOS': + if salt.utils.path.which("brew") and __grains__["os"] == "MacOS": return __virtualname__ - return (False, 'The brew module could not be loaded: brew not found or grain os != MacOS') + return ( + False, + "The brew module could not be loaded: brew not found or grain os != MacOS", + ) def _list_taps(): - ''' + """ List currently installed brew taps - ''' - cmd = 'tap' - return _call_brew(cmd)['stdout'].splitlines() + """ + cmd = "tap" + return _call_brew(cmd)["stdout"].splitlines() def _tap(tap, runas=None): - ''' + """ Add unofficial GitHub repos to the list of formulas that brew tracks, updates, and installs from. - ''' + """ if tap in _list_taps(): return True - cmd = 'tap {0}'.format(tap) + cmd = "tap {0}".format(tap) try: _call_brew(cmd) except CommandExecutionError: @@ -72,33 +75,31 @@ def _tap(tap, runas=None): def _homebrew_bin(): - ''' + """ Returns the full path to the homebrew binary in the PATH - ''' - ret = __salt__['cmd.run']('brew --prefix', output_loglevel='trace') - ret += '/bin/brew' + """ + ret = __salt__["cmd.run"]("brew --prefix", output_loglevel="trace") + ret += "/bin/brew" return ret def _call_brew(cmd, failhard=True): - ''' + """ Calls the brew command with the user account of brew - ''' - user = __salt__['file.get_user'](_homebrew_bin()) - runas = user if user != __opts__['user'] else None - cmd = '{} {}'.format(salt.utils.path.which('brew'), cmd) - result = __salt__['cmd.run_all'](cmd, - runas=runas, - output_loglevel='trace', - python_shell=False) - if failhard and result['retcode'] != 0: - raise CommandExecutionError('Brew command failed', - info={'result': result}) + """ + user = __salt__["file.get_user"](_homebrew_bin()) + runas = user if user != __opts__["user"] else None + cmd = "{} {}".format(salt.utils.path.which("brew"), cmd) + result = __salt__["cmd.run_all"]( + cmd, runas=runas, output_loglevel="trace", python_shell=False + ) + if failhard and result["retcode"] != 0: + raise CommandExecutionError("Brew command failed", info={"result": result}) return result def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed in a dict:: {'': ''} @@ -108,79 +109,84 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret ret = {} - cmd = 'info --json=v1 --installed' - package_info = salt.utils.json.loads(_call_brew(cmd)['stdout']) + cmd = "info --json=v1 --installed" + package_info = salt.utils.json.loads(_call_brew(cmd)["stdout"]) for package in package_info: # Brew allows multiple versions of the same package to be installed. # Salt allows for this, so it must be accounted for. - versions = [v['version'] for v in package['installed']] + versions = [v["version"] for v in package["installed"]] # Brew allows for aliasing of packages, all of which will be # installable from a Salt call, so all names must be accounted for. - names = package['aliases'] + [package['name'], package['full_name']] + names = package["aliases"] + [package["name"], package["full_name"]] # Create a list of tuples containing all possible combinations of # names and versions, because all are valid. combinations = [(n, v) for n in names for v in versions] for name, version in combinations: - __salt__['pkg_resource.add_pkg'](ret, name, version) + __salt__["pkg_resource.add_pkg"](ret, name, version) # Grab packages from brew cask, if available. # Brew Cask doesn't provide a JSON interface, must be parsed the old way. try: - cask_cmd = 'cask list --versions' - out = _call_brew(cask_cmd)['stdout'] + cask_cmd = "cask list --versions" + out = _call_brew(cask_cmd)["stdout"] for line in out.splitlines(): try: - name_and_versions = line.split(' ') + name_and_versions = line.split(" ") pkg_name = name_and_versions[0] # Get cask namespace - info_cmd = 'cask info {}'.format(pkg_name) - match = re.search(r'^From: .*/(.+?)/homebrew-(.+?)/.*$', - _call_brew(info_cmd)['stdout'], re.MULTILINE) + info_cmd = "cask info {}".format(pkg_name) + match = re.search( + r"^From: .*/(.+?)/homebrew-(.+?)/.*$", + _call_brew(info_cmd)["stdout"], + re.MULTILINE, + ) if match: - namespace = '/'.join((match.group(1).lower(), - match.group(2).lower())) + namespace = "/".join( + (match.group(1).lower(), match.group(2).lower()) + ) else: - namespace = 'homebrew/cask' + namespace = "homebrew/cask" - name = '/'.join((namespace, pkg_name)) + name = "/".join((namespace, pkg_name)) installed_versions = name_and_versions[1:] key_func = functools.cmp_to_key(salt.utils.versions.version_cmp) newest_version = sorted(installed_versions, key=key_func).pop() except ValueError: continue - __salt__['pkg_resource.add_pkg'](ret, name, newest_version) + __salt__["pkg_resource.add_pkg"](ret, name, newest_version) except CommandExecutionError: pass - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -191,12 +197,12 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation @@ -209,16 +215,18 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version - ''' - refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) + """ + refresh = salt.utils.data.is_true(kwargs.pop("refresh", True)) if refresh: refresh_db() def get_version(pkg_info): # Perhaps this will need an option to pick devel by default - return pkg_info['versions']['stable'] or pkg_info['versions']['devel'] + return pkg_info["versions"]["stable"] or pkg_info["versions"]["devel"] - versions_dict = dict((key, get_version(val)) for key, val in six.iteritems(_info(*names))) + versions_dict = dict( + (key, get_version(val)) for key, val in six.iteritems(_info(*names)) + ) if len(names) == 1: return next(six.itervalues(versions_dict)) @@ -227,11 +235,13 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def remove(name=None, pkgs=None, **kwargs): - ''' + """ Removes packages with ``brew uninstall``. name @@ -256,12 +266,10 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ try: name, pkgs = _fix_cask_namespace(name, pkgs) - pkg_params = __salt__['pkg_resource.parse_targets']( - name, pkgs, **kwargs - )[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs, **kwargs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -269,29 +277,29 @@ def remove(name=None, pkgs=None, **kwargs): targets = [x for x in pkg_params if x in old] if not targets: return {} - cmd = 'uninstall {0}'.format(' '.join(targets)) + cmd = "uninstall {0}".format(" ".join(targets)) out = _call_brew(cmd) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def refresh_db(): - ''' + """ Update the homebrew package repository. CLI Example: @@ -299,19 +307,19 @@ def refresh_db(): .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) - cmd = 'update' - if _call_brew(cmd)['retcode']: - log.error('Failed to update') + cmd = "update" + if _call_brew(cmd)["retcode"]: + log.error("Failed to update") return False return True def _info(*pkgs): - ''' + """ Get all info brew can provide about a list of packages. Does not do any kind of processing, so the format depends entirely on @@ -324,19 +332,18 @@ def _info(*pkgs): Caveat: If one of the packages does not exist, no packages will be included in the output. - ''' - cmd = 'info --json=v1 {0}'.format(' '.join(pkgs)) + """ + cmd = "info --json=v1 {0}".format(" ".join(pkgs)) brew_result = _call_brew(cmd) - if brew_result['retcode']: - log.error('Failed to get info about packages: %s', - ' '.join(pkgs)) + if brew_result["retcode"]: + log.error("Failed to get info about packages: %s", " ".join(pkgs)) return {} - output = salt.utils.json.loads(brew_result['stdout']) + output = salt.utils.json.loads(brew_result["stdout"]) return dict(zip(pkgs, output)) def install(name=None, pkgs=None, taps=None, options=None, **kwargs): - ''' + """ Install the passed package(s) with ``brew install`` name @@ -396,11 +403,11 @@ def install(name=None, pkgs=None, taps=None, options=None, **kwargs): .. code-block:: bash salt '*' pkg.install 'package package package' - ''' + """ try: name, pkgs = _fix_cask_namespace(name, pkgs) - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( - name, pkgs, kwargs.get('sources', {}) + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( + name, pkgs, kwargs.get("sources", {}) ) except MinionError as exc: raise CommandExecutionError(exc) @@ -408,7 +415,7 @@ def install(name=None, pkgs=None, taps=None, options=None, **kwargs): if pkg_params is None or len(pkg_params) == 0: return {} - formulas = ' '.join(pkg_params) + formulas = " ".join(pkg_params) old = list_pkgs() # Ensure we've tapped the repo if necessary @@ -422,31 +429,31 @@ def install(name=None, pkgs=None, taps=None, options=None, **kwargs): _tap(tap) if options: - cmd = 'install {0} {1}'.format(formulas, ' '.join(options)) + cmd = "install {0} {1}".format(formulas, " ".join(options)) else: - cmd = 'install {0}'.format(formulas) + cmd = "install {0}".format(formulas) out = _call_brew(cmd) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 - ''' + """ Check whether or not an upgrade is available for all packages CLI Example: @@ -454,15 +461,15 @@ def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ if refresh: refresh_db() - res = _call_brew('outdated --json=v1') + res = _call_brew("outdated --json=v1") ret = {} try: - data = salt.utils.json.loads(res['stdout']) + data = salt.utils.json.loads(res["stdout"]) except ValueError as err: msg = 'unable to interpret output from "brew outdated": {0}'.format(err) log.error(msg) @@ -470,12 +477,12 @@ def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 for pkg in data: # current means latest available to brew - ret[pkg['name']] = pkg['current_version'] + ret[pkg["name"]] = pkg["current_version"] return ret def upgrade_available(pkg): - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -483,12 +490,12 @@ def upgrade_available(pkg): .. code-block:: bash salt '*' pkg.upgrade_available - ''' + """ return pkg in list_upgrades() def upgrade(refresh=True): - ''' + """ Upgrade outdated, unpinned brews. refresh @@ -507,33 +514,34 @@ def upgrade(refresh=True): .. code-block:: bash salt '*' pkg.upgrade - ''' - ret = {'changes': {}, - 'result': True, - 'comment': '', - } + """ + ret = { + "changes": {}, + "result": True, + "comment": "", + } old = list_pkgs() if salt.utils.data.is_true(refresh): refresh_db() - result = _call_brew('upgrade', failhard=False) - __context__.pop('pkg.list_pkgs', None) + result = _call_brew("upgrade", failhard=False) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret def info_installed(*names): - ''' + """ Return the information of the named package(s) installed on the system. .. versionadded:: 2016.3.1 @@ -547,7 +555,7 @@ def info_installed(*names): salt '*' pkg.info_installed salt '*' pkg.info_installed ... - ''' + """ return _info(*names) @@ -567,14 +575,14 @@ def _fix_cask_namespace(name=None, pkgs=None): show_warning = False - if name and name.startswith('caskroom/cask/'): + if name and name.startswith("caskroom/cask/"): show_warning = True name = name.replace("caskroom/cask/", "homebrew/cask/") if pkgs: pkgs_ = [] for pkg in pkgs: - if isinstance(pkg, str) and pkg.startswith('caskroom/cask/'): + if isinstance(pkg, str) and pkg.startswith("caskroom/cask/"): show_warning = True pkg = pkg.replace("caskroom/cask/", "homebrew/cask/") pkgs_.append(pkg) @@ -585,9 +593,9 @@ def _fix_cask_namespace(name=None, pkgs=None): if show_warning: salt.utils.versions.warn_until( - 'Sodium', - 'The \'caskroom/cask/\' namespace for brew-cask packages ' - 'is deprecated. Use \'homebrew/cask/\' instead.' + "Sodium", + "The 'caskroom/cask/' namespace for brew-cask packages " + "is deprecated. Use 'homebrew/cask/' instead.", ) return name, pkgs diff --git a/salt/modules/mac_desktop.py b/salt/modules/mac_desktop.py index cdd1ea37e11..102d6a311c7 100644 --- a/salt/modules/mac_desktop.py +++ b/salt/modules/mac_desktop.py @@ -1,28 +1,28 @@ # -*- coding: utf-8 -*- -''' +""" macOS implementations of various commands in the "desktop" interface -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.platform from salt.exceptions import CommandExecutionError # Define the module's virtual name -__virtualname__ = 'desktop' +__virtualname__ = "desktop" def __virtual__(): - ''' + """ Only load on Mac systems - ''' + """ if salt.utils.platform.is_darwin(): return __virtualname__ - return False, 'Cannot load macOS desktop module: This is not a macOS host.' + return False, "Cannot load macOS desktop module: This is not a macOS host." def get_output_volume(): - ''' + """ Get the output volume (range 0 to 100) CLI Example: @@ -30,20 +30,16 @@ def get_output_volume(): .. code-block:: bash salt '*' desktop.get_output_volume - ''' + """ cmd = 'osascript -e "get output volume of (get volume settings)"' - call = __salt__['cmd.run_all']( - cmd, - output_loglevel='debug', - python_shell=False - ) + call = __salt__["cmd.run_all"](cmd, output_loglevel="debug", python_shell=False) _check_cmd(call) - return call.get('stdout') + return call.get("stdout") def set_output_volume(volume): - ''' + """ Set the volume of sound. volume @@ -54,20 +50,16 @@ def set_output_volume(volume): .. code-block:: bash salt '*' desktop.set_output_volume - ''' + """ cmd = 'osascript -e "set volume output volume {0}"'.format(volume) - call = __salt__['cmd.run_all']( - cmd, - output_loglevel='debug', - python_shell=False - ) + call = __salt__["cmd.run_all"](cmd, output_loglevel="debug", python_shell=False) _check_cmd(call) return get_output_volume() def screensaver(): - ''' + """ Launch the screensaver. CLI Example: @@ -75,20 +67,16 @@ def screensaver(): .. code-block:: bash salt '*' desktop.screensaver - ''' - cmd = 'open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app' - call = __salt__['cmd.run_all']( - cmd, - output_loglevel='debug', - python_shell=False - ) + """ + cmd = "open /System/Library/Frameworks/ScreenSaver.framework/Versions/A/Resources/ScreenSaverEngine.app" + call = __salt__["cmd.run_all"](cmd, output_loglevel="debug", python_shell=False) _check_cmd(call) return True def lock(): - ''' + """ Lock the desktop session CLI Example: @@ -96,20 +84,16 @@ def lock(): .. code-block:: bash salt '*' desktop.lock - ''' - cmd = '/System/Library/CoreServices/Menu\\ Extras/User.menu/Contents/Resources/CGSession -suspend' - call = __salt__['cmd.run_all']( - cmd, - output_loglevel='debug', - python_shell=False - ) + """ + cmd = "/System/Library/CoreServices/Menu\\ Extras/User.menu/Contents/Resources/CGSession -suspend" + call = __salt__["cmd.run_all"](cmd, output_loglevel="debug", python_shell=False) _check_cmd(call) return True def say(*words): - ''' + """ Say some words. words @@ -120,31 +104,27 @@ def say(*words): .. code-block:: bash salt '*' desktop.say ... - ''' - cmd = 'say {0}'.format(' '.join(words)) - call = __salt__['cmd.run_all']( - cmd, - output_loglevel='debug', - python_shell=False - ) + """ + cmd = "say {0}".format(" ".join(words)) + call = __salt__["cmd.run_all"](cmd, output_loglevel="debug", python_shell=False) _check_cmd(call) return True def _check_cmd(call): - ''' + """ Check the output of the cmd.run_all function call. - ''' - if call['retcode'] != 0: - comment = '' - std_err = call.get('stderr') - std_out = call.get('stdout') + """ + if call["retcode"] != 0: + comment = "" + std_err = call.get("stderr") + std_out = call.get("stdout") if std_err: comment += std_err if std_out: comment += std_out - raise CommandExecutionError('Error running command: {0}'.format(comment)) + raise CommandExecutionError("Error running command: {0}".format(comment)) return call diff --git a/salt/modules/mac_group.py b/salt/modules/mac_group.py index 745679c8461..e553dc6ac9c 100644 --- a/salt/modules/mac_group.py +++ b/salt/modules/mac_group.py @@ -1,32 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" Manage groups on Mac OS 10.7+ -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -try: - import grp -except ImportError: - pass +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.utils.functools import salt.utils.itertools import salt.utils.stringutils from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.modules.mac_user import _dscl, _flush_dscl_cache from salt.ext import six +from salt.modules.mac_user import _dscl, _flush_dscl_cache + +try: + import grp +except ImportError: + pass + # Define the module's virtual name -__virtualname__ = 'group' +__virtualname__ = "group" def __virtual__(): global _dscl, _flush_dscl_cache - if (__grains__.get('kernel') != 'Darwin' or - __grains__['osrelease_info'] < (10, 7)): - return (False, 'The mac_group execution module cannot be loaded: only available on Darwin-based systems >= 10.7') + if __grains__.get("kernel") != "Darwin" or __grains__["osrelease_info"] < (10, 7): + return ( + False, + "The mac_group execution module cannot be loaded: only available on Darwin-based systems >= 10.7", + ) _dscl = salt.utils.functools.namespaced_function(_dscl, globals()) _flush_dscl_cache = salt.utils.functools.namespaced_function( _flush_dscl_cache, globals() @@ -35,7 +39,7 @@ def __virtual__(): def add(name, gid=None, **kwargs): - ''' + """ Add the specified group CLI Example: @@ -43,53 +47,47 @@ def add(name, gid=None, **kwargs): .. code-block:: bash salt '*' group.add foo 3456 - ''' + """ ### NOTE: **kwargs isn't used here but needs to be included in this ### function for compatibility with the group.present state if info(name): - raise CommandExecutionError( - 'Group \'{0}\' already exists'.format(name) - ) + raise CommandExecutionError("Group '{0}' already exists".format(name)) if salt.utils.stringutils.contains_whitespace(name): - raise SaltInvocationError('Group name cannot contain whitespace') - if name.startswith('_'): + raise SaltInvocationError("Group name cannot contain whitespace") + if name.startswith("_"): raise SaltInvocationError( - 'Salt will not create groups beginning with underscores' + "Salt will not create groups beginning with underscores" ) if gid is not None and not isinstance(gid, int): - raise SaltInvocationError('gid must be an integer') + raise SaltInvocationError("gid must be an integer") # check if gid is already in use gid_list = _list_gids() if six.text_type(gid) in gid_list: - raise CommandExecutionError( - 'gid \'{0}\' already exists'.format(gid) - ) + raise CommandExecutionError("gid '{0}' already exists".format(gid)) - cmd = ['dseditgroup', '-o', 'create'] + cmd = ["dseditgroup", "-o", "create"] if gid: - cmd.extend(['-i', gid]) + cmd.extend(["-i", gid]) cmd.append(name) - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 def _list_gids(): - ''' + """ Return a list of gids in use - ''' - output = __salt__['cmd.run']( - ['dscacheutil', '-q', 'group'], - output_loglevel='quiet', - python_shell=False + """ + output = __salt__["cmd.run"]( + ["dscacheutil", "-q", "group"], output_loglevel="quiet", python_shell=False ) ret = set() - for line in salt.utils.itertools.split(output, '\n'): - if line.startswith('gid:'): + for line in salt.utils.itertools.split(output, "\n"): + if line.startswith("gid:"): ret.update(line.split()[1:]) return sorted(ret) def delete(name): - ''' + """ Remove the named group CLI Example: @@ -97,21 +95,21 @@ def delete(name): .. code-block:: bash salt '*' group.delete foo - ''' + """ if salt.utils.stringutils.contains_whitespace(name): - raise SaltInvocationError('Group name cannot contain whitespace') - if name.startswith('_'): + raise SaltInvocationError("Group name cannot contain whitespace") + if name.startswith("_"): raise SaltInvocationError( - 'Salt will not remove groups beginning with underscores' + "Salt will not remove groups beginning with underscores" ) if not info(name): return True - cmd = ['dseditgroup', '-o', 'delete', name] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd = ["dseditgroup", "-o", "delete", name] + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 def adduser(group, name): - ''' + """ Add a user in the group. CLI Example: @@ -122,13 +120,13 @@ def adduser(group, name): Verifies if a valid username 'bar' as a member of an existing group 'foo', if not then adds it. - ''' - cmd = 'dscl . -merge /Groups/{0} GroupMembership {1}'.format(group, name) - return __salt__['cmd.retcode'](cmd) == 0 + """ + cmd = "dscl . -merge /Groups/{0} GroupMembership {1}".format(group, name) + return __salt__["cmd.retcode"](cmd) == 0 def deluser(group, name): - ''' + """ Remove a user from the group .. versionadded:: 2016.3.0 @@ -141,13 +139,13 @@ def deluser(group, name): Removes a member user 'bar' from a group 'foo'. If group is not present then returns True. - ''' - cmd = 'dscl . -delete /Groups/{0} GroupMembership {1}'.format(group, name) - return __salt__['cmd.retcode'](cmd) == 0 + """ + cmd = "dscl . -delete /Groups/{0} GroupMembership {1}".format(group, name) + return __salt__["cmd.retcode"](cmd) == 0 def members(name, members_list): - ''' + """ Replaces members of the group with a provided list. .. versionadded:: 2016.3.0 @@ -157,15 +155,17 @@ def members(name, members_list): salt '*' group.members foo 'user1,user2,user3,...' Replaces a membership list for a local group 'foo'. - ''' + """ retcode = 1 - grp_info = __salt__['group.info'](name) - if grp_info and name in grp_info['name']: - cmd = '/usr/bin/dscl . -delete /Groups/{0} GroupMembership'.format(name) - retcode = __salt__['cmd.retcode'](cmd) == 0 - for user in members_list.split(','): - cmd = '/usr/bin/dscl . -merge /Groups/{0} GroupMembership {1}'.format(name, user) - retcode = __salt__['cmd.retcode'](cmd) + grp_info = __salt__["group.info"](name) + if grp_info and name in grp_info["name"]: + cmd = "/usr/bin/dscl . -delete /Groups/{0} GroupMembership".format(name) + retcode = __salt__["cmd.retcode"](cmd) == 0 + for user in members_list.split(","): + cmd = "/usr/bin/dscl . -merge /Groups/{0} GroupMembership {1}".format( + name, user + ) + retcode = __salt__["cmd.retcode"](cmd) if not retcode == 0: break # provided list is '': users previously deleted from group @@ -176,7 +176,7 @@ def members(name, members_list): def info(name): - ''' + """ Return information about a group CLI Example: @@ -184,9 +184,9 @@ def info(name): .. code-block:: bash salt '*' group.info foo - ''' + """ if salt.utils.stringutils.contains_whitespace(name): - raise SaltInvocationError('Group name cannot contain whitespace') + raise SaltInvocationError("Group name cannot contain whitespace") try: # getgrnam seems to cache weirdly, so don't use it grinfo = next(iter(x for x in grp.getgrall() if x.gr_name == name)) @@ -197,17 +197,19 @@ def info(name): def _format_info(data): - ''' + """ Return formatted information in a pretty way. - ''' - return {'name': data.gr_name, - 'gid': data.gr_gid, - 'passwd': data.gr_passwd, - 'members': data.gr_mem} + """ + return { + "name": data.gr_name, + "gid": data.gr_gid, + "passwd": data.gr_passwd, + "members": data.gr_mem, + } def getent(refresh=False): - ''' + """ Return info on all groups CLI Example: @@ -215,20 +217,20 @@ def getent(refresh=False): .. code-block:: bash salt '*' group.getent - ''' - if 'group.getent' in __context__ and not refresh: - return __context__['group.getent'] + """ + if "group.getent" in __context__ and not refresh: + return __context__["group.getent"] ret = [] for grinfo in grp.getgrall(): - if not grinfo.gr_name.startswith('_'): + if not grinfo.gr_name.startswith("_"): ret.append(_format_info(grinfo)) - __context__['group.getent'] = ret + __context__["group.getent"] = ret return ret def chgid(name, gid): - ''' + """ Change the gid for a named group CLI Example: @@ -236,16 +238,14 @@ def chgid(name, gid): .. code-block:: bash salt '*' group.chgid foo 4376 - ''' + """ if not isinstance(gid, int): - raise SaltInvocationError('gid must be an integer') - pre_gid = __salt__['file.group_to_gid'](name) + raise SaltInvocationError("gid must be an integer") + pre_gid = __salt__["file.group_to_gid"](name) pre_info = info(name) if not pre_info: - raise CommandExecutionError( - 'Group \'{0}\' does not exist'.format(name) - ) - if gid == pre_info['gid']: + raise CommandExecutionError("Group '{0}' does not exist".format(name)) + if gid == pre_info["gid"]: return True - cmd = ['dseditgroup', '-o', 'edit', '-i', gid, name] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd = ["dseditgroup", "-o", "edit", "-i", gid, name] + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 diff --git a/salt/modules/mac_keychain.py b/salt/modules/mac_keychain.py index d5e2ad8e83c..d7efddb3055 100644 --- a/salt/modules/mac_keychain.py +++ b/salt/modules/mac_keychain.py @@ -1,52 +1,57 @@ # -*- coding: utf-8 -*- -''' +""" Install certificates into the keychain on Mac OS .. versionadded:: 2016.3.0 -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import re - import shlex -try: - import pipes - HAS_DEPS = True -except ImportError: - HAS_DEPS = False # Import Salt libs import salt.utils.platform -log = logging.getLogger(__name__) -__virtualname__ = 'keychain' +try: + import pipes -if hasattr(shlex, 'quote'): + HAS_DEPS = True +except ImportError: + HAS_DEPS = False + + +log = logging.getLogger(__name__) +__virtualname__ = "keychain" + +if hasattr(shlex, "quote"): _quote = shlex.quote -elif HAS_DEPS and hasattr(pipes, 'quote'): +elif HAS_DEPS and hasattr(pipes, "quote"): _quote = pipes.quote else: _quote = None def __virtual__(): - ''' + """ Only work on Mac OS - ''' + """ if salt.utils.platform.is_darwin() and _quote is not None: return __virtualname__ - return False + return (False, "Only available on Mac OS systems with pipes") -def install(cert, - password, - keychain="/Library/Keychains/System.keychain", - allow_any=False, - keychain_password=None): - ''' +def install( + cert, + password, + keychain="/Library/Keychains/System.keychain", + allow_any=False, + keychain_password=None, +): + """ Install a certificate cert @@ -78,20 +83,20 @@ def install(cert, .. code-block:: bash salt '*' keychain.install test.p12 test123 - ''' + """ if keychain_password is not None: unlock_keychain(keychain, keychain_password) - cmd = 'security import {0} -P {1} -k {2}'.format(cert, password, keychain) + cmd = "security import {0} -P {1} -k {2}".format(cert, password, keychain) if allow_any: - cmd += ' -A' - return __salt__['cmd.run'](cmd) + cmd += " -A" + return __salt__["cmd.run"](cmd) -def uninstall(cert_name, - keychain="/Library/Keychains/System.keychain", - keychain_password=None): - ''' +def uninstall( + cert_name, keychain="/Library/Keychains/System.keychain", keychain_password=None +): + """ Uninstall a certificate from a keychain cert_name @@ -113,16 +118,16 @@ def uninstall(cert_name, .. code-block:: bash salt '*' keychain.install test.p12 test123 - ''' + """ if keychain_password is not None: unlock_keychain(keychain, keychain_password) cmd = 'security delete-certificate -c "{0}" {1}'.format(cert_name, keychain) - return __salt__['cmd.run'](cmd) + return __salt__["cmd.run"](cmd) def list_certs(keychain="/Library/Keychains/System.keychain"): - ''' + """ List all of the installed certificates keychain @@ -134,15 +139,17 @@ def list_certs(keychain="/Library/Keychains/System.keychain"): .. code-block:: bash salt '*' keychain.list_certs - ''' - cmd = 'security find-certificate -a {0} | grep -o "alis".*\\" | ' \ - 'grep -o \'\\"[-A-Za-z0-9.:() ]*\\"\''.format(_quote(keychain)) - out = __salt__['cmd.run'](cmd, python_shell=True) - return out.replace('"', '').split('\n') + """ + cmd = ( + 'security find-certificate -a {0} | grep -o "alis".*\\" | ' + "grep -o '\\\"[-A-Za-z0-9.:() ]*\\\"'".format(_quote(keychain)) + ) + out = __salt__["cmd.run"](cmd, python_shell=True) + return out.replace('"', "").split("\n") def get_friendly_name(cert, password): - ''' + """ Get the friendly name of the given certificate cert @@ -160,15 +167,17 @@ def get_friendly_name(cert, password): .. code-block:: bash salt '*' keychain.get_friendly_name /tmp/test.p12 test123 - ''' - cmd = 'openssl pkcs12 -in {0} -passin pass:{1} -info -nodes -nokeys 2> /dev/null | ' \ - 'grep friendlyName:'.format(_quote(cert), _quote(password)) - out = __salt__['cmd.run'](cmd, python_shell=True) + """ + cmd = ( + "openssl pkcs12 -in {0} -passin pass:{1} -info -nodes -nokeys 2> /dev/null | " + "grep friendlyName:".format(_quote(cert), _quote(password)) + ) + out = __salt__["cmd.run"](cmd, python_shell=True) return out.replace("friendlyName: ", "").strip() def get_default_keychain(user=None, domain="user"): - ''' + """ Get the default keychain user @@ -182,13 +191,13 @@ def get_default_keychain(user=None, domain="user"): .. code-block:: bash salt '*' keychain.get_default_keychain - ''' + """ cmd = "security default-keychain -d {0}".format(domain) - return __salt__['cmd.run'](cmd, runas=user) + return __salt__["cmd.run"](cmd, runas=user) def set_default_keychain(keychain, domain="user", user=None): - ''' + """ Set the default keychain keychain @@ -205,13 +214,13 @@ def set_default_keychain(keychain, domain="user", user=None): .. code-block:: bash salt '*' keychain.set_keychain /Users/fred/Library/Keychains/login.keychain - ''' + """ cmd = "security default-keychain -d {0} -s {1}".format(domain, keychain) - return __salt__['cmd.run'](cmd, runas=user) + return __salt__["cmd.run"](cmd, runas=user) def unlock_keychain(keychain, password): - ''' + """ Unlock the given keychain with the password keychain @@ -228,13 +237,13 @@ def unlock_keychain(keychain, password): .. code-block:: bash salt '*' keychain.unlock_keychain /tmp/test.p12 test123 - ''' - cmd = 'security unlock-keychain -p {0} {1}'.format(password, keychain) - __salt__['cmd.run'](cmd) + """ + cmd = "security unlock-keychain -p {0} {1}".format(password, keychain) + __salt__["cmd.run"](cmd) def get_hash(name, password=None): - ''' + """ Returns the hash of a certificate in the keychain. name @@ -250,15 +259,21 @@ def get_hash(name, password=None): .. code-block:: bash salt '*' keychain.get_hash /tmp/test.p12 test123 - ''' + """ - if '.p12' in name[-4:]: - cmd = 'openssl pkcs12 -in {0} -passin pass:{1} -passout pass:{1}'.format(name, password) + if ".p12" in name[-4:]: + cmd = "openssl pkcs12 -in {0} -passin pass:{1} -passout pass:{1}".format( + name, password + ) else: cmd = 'security find-certificate -c "{0}" -m -p'.format(name) - out = __salt__['cmd.run'](cmd) - matches = re.search('-----BEGIN CERTIFICATE-----(.*)-----END CERTIFICATE-----', out, re.DOTALL | re.MULTILINE) + out = __salt__["cmd.run"](cmd) + matches = re.search( + "-----BEGIN CERTIFICATE-----(.*)-----END CERTIFICATE-----", + out, + re.DOTALL | re.MULTILINE, + ) if matches: return matches.group(1) else: diff --git a/salt/modules/mac_pkgutil.py b/salt/modules/mac_pkgutil.py index e9f9046006e..3db399d8ce2 100644 --- a/salt/modules/mac_pkgutil.py +++ b/salt/modules/mac_pkgutil.py @@ -1,43 +1,45 @@ # -*- coding: utf-8 -*- -''' +""" Installer support for macOS. Installer is the native .pkg/.mpkg package manager for macOS. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os.path -# Import 3rd-party libs -from salt.ext.six.moves import urllib # pylint: disable=import-error +import salt.utils.itertools +import salt.utils.mac_utils # Import Salt libs import salt.utils.path import salt.utils.platform -import salt.utils.itertools -import salt.utils.mac_utils from salt.exceptions import SaltInvocationError +# Import 3rd-party libs +from salt.ext.six.moves import urllib # pylint: disable=import-error + # Don't shadow built-in's. -__func_alias__ = {'list_': 'list'} +__func_alias__ = {"list_": "list"} # Define the module's virtual name -__virtualname__ = 'pkgutil' +__virtualname__ = "pkgutil" def __virtual__(): if not salt.utils.platform.is_darwin(): - return (False, 'Only available on Mac OS systems') + return (False, "Only available on Mac OS systems") - if not salt.utils.path.which('pkgutil'): - return (False, 'Missing pkgutil binary') + if not salt.utils.path.which("pkgutil"): + return (False, "Missing pkgutil binary") return __virtualname__ def list_(): - ''' + """ List the installed packages. :return: A list of installed packages @@ -48,14 +50,14 @@ def list_(): .. code-block:: bash salt '*' pkgutil.list - ''' - cmd = 'pkgutil --pkgs' + """ + cmd = "pkgutil --pkgs" ret = salt.utils.mac_utils.execute_return_result(cmd) return ret.splitlines() def is_installed(package_id): - ''' + """ Returns whether a given package id is installed. :return: True if installed, otherwise False @@ -66,16 +68,16 @@ def is_installed(package_id): .. code-block:: bash salt '*' pkgutil.is_installed com.apple.pkg.gcc4.2Leo - ''' + """ return package_id in list_() def _install_from_path(path): - ''' + """ Internal function to install a package from the given path - ''' + """ if not os.path.exists(path): - msg = 'File not found: {0}'.format(path) + msg = "File not found: {0}".format(path) raise SaltInvocationError(msg) cmd = 'installer -pkg "{0}" -target /'.format(path) @@ -83,7 +85,7 @@ def _install_from_path(path): def install(source, package_id): - ''' + """ Install a .pkg from an URI or an absolute path. :param str source: The path to a package. @@ -98,13 +100,13 @@ def install(source, package_id): .. code-block:: bash salt '*' pkgutil.install source=/vagrant/build_essentials.pkg package_id=com.apple.pkg.gcc4.2Leo - ''' + """ if is_installed(package_id): return True uri = urllib.parse.urlparse(source) - if not uri.scheme == '': - msg = 'Unsupported scheme for source uri: {0}'.format(uri.scheme) + if not uri.scheme == "": + msg = "Unsupported scheme for source uri: {0}".format(uri.scheme) raise SaltInvocationError(msg) _install_from_path(source) @@ -113,7 +115,7 @@ def install(source, package_id): def forget(package_id): - ''' + """ .. versionadded:: 2016.3.0 Remove the receipt data about the specified package. Does not remove files. @@ -131,7 +133,7 @@ def forget(package_id): .. code-block:: bash salt '*' pkgutil.forget com.apple.pkg.gcc4.2Leo - ''' - cmd = 'pkgutil --forget {0}'.format(package_id) + """ + cmd = "pkgutil --forget {0}".format(package_id) salt.utils.mac_utils.execute_return_success(cmd) return not is_installed(package_id) diff --git a/salt/modules/mac_portspkg.py b/salt/modules/mac_portspkg.py index 78a38d54a94..e662b2875d4 100644 --- a/salt/modules/mac_portspkg.py +++ b/salt/modules/mac_portspkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for MacPorts under macOS. This module has some caveats. @@ -28,10 +28,11 @@ don't upgrade the packaging software when doing a package database update. In other words `salt mac-machine pkg.refresh_db` is more like `apt-get update; apt-get upgrade dpkg apt-get` than simply `apt-get update`. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import re @@ -39,10 +40,10 @@ import re # Import salt libs import salt.utils.data import salt.utils.functools +import salt.utils.mac_utils import salt.utils.path import salt.utils.pkg import salt.utils.platform -import salt.utils.mac_utils import salt.utils.versions from salt.exceptions import CommandExecutionError @@ -52,30 +53,30 @@ from salt.ext import six log = logging.getLogger(__name__) LIST_ACTIVE_ONLY = True -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Confine this module to Mac OS with MacPorts. - ''' + """ if not salt.utils.platform.is_darwin(): - return False, 'mac_ports only available on MacOS' + return False, "mac_ports only available on MacOS" - if not salt.utils.path.which('port'): + if not salt.utils.path.which("port"): return False, 'mac_ports requires the "port" binary' return __virtualname__ -def _list(query=''): - cmd = 'port list {0}'.format(query) +def _list(query=""): + cmd = "port list {0}".format(query) out = salt.utils.mac_utils.execute_return_result(cmd) ret = {} for line in out.splitlines(): try: - name, version_num, category = re.split(r'\s+', line.lstrip())[0:3] + name, version_num, category = re.split(r"\s+", line.lstrip())[0:3] version_num = version_num[1:] except ValueError: continue @@ -85,7 +86,7 @@ def _list(query=''): def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed in a dict:: {'': ''} @@ -95,42 +96,43 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # 'removed', 'purge_desired' not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret ret = {} - cmd = ['port', 'installed'] + cmd = ["port", "installed"] out = salt.utils.mac_utils.execute_return_result(cmd) for line in out.splitlines(): try: - name, version_num, active = re.split(r'\s+', line.lstrip())[0:3] + name, version_num, active = re.split(r"\s+", line.lstrip())[0:3] version_num = version_num[1:] except ValueError: continue - if not LIST_ACTIVE_ONLY or active == '(active)': - __salt__['pkg_resource.add_pkg'](ret, name, version_num) + if not LIST_ACTIVE_ONLY or active == "(active)": + __salt__["pkg_resource.add_pkg"](ret, name, version_num) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -141,12 +143,12 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation @@ -161,31 +163,35 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version - ''' + """ - if salt.utils.data.is_true(kwargs.get('refresh', True)): + if salt.utils.data.is_true(kwargs.get("refresh", True)): refresh_db() - available = _list(' '.join(names)) or {} - installed = __salt__['pkg.list_pkgs']() or {} + available = _list(" ".join(names)) or {} + installed = __salt__["pkg.list_pkgs"]() or {} ret = {} for key, val in six.iteritems(available): - if key not in installed or salt.utils.versions.compare(ver1=installed[key], oper='<', ver2=val): + if key not in installed or salt.utils.versions.compare( + ver1=installed[key], oper="<", ver2=val + ): ret[key] = val else: - ret[key] = '{0} (installed)'.format(version(key)) + ret[key] = "{0} (installed)".format(version(key)) return ret # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def remove(name=None, pkgs=None, **kwargs): - ''' + """ Removes packages with ``port uninstall``. name @@ -210,38 +216,37 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' - pkg_params = __salt__['pkg_resource.parse_targets'](name, - pkgs, - **kwargs)[0] + """ + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs, **kwargs)[0] old = list_pkgs() targets = [x for x in pkg_params if x in old] if not targets: return {} - cmd = ['port', 'uninstall'] + cmd = ["port", "uninstall"] cmd.extend(targets) - err_message = '' + err_message = "" try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: err_message = exc.strerror - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if err_message: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': err_message, 'changes': ret}) + "Problem encountered removing package(s)", + info={"errors": err_message, "changes": ret}, + ) return ret def install(name=None, refresh=False, pkgs=None, **kwargs): - ''' + """ Install the passed package(s) with ``port install`` name @@ -298,24 +303,23 @@ def install(name=None, refresh=False, pkgs=None, **kwargs): .. code-block:: bash salt '*' pkg.install 'package package package' - ''' - pkg_params, pkg_type = \ - __salt__['pkg_resource.parse_targets'](name, pkgs, {}) + """ + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"](name, pkgs, {}) if salt.utils.data.is_true(refresh): refresh_db() # Handle version kwarg for a single package target if pkgs is None: - version_num = kwargs.get('version') - variant_spec = kwargs.get('variant') + version_num = kwargs.get("version") + variant_spec = kwargs.get("variant") spec = {} if version_num: - spec['version'] = version_num + spec["version"] = version_num if variant_spec: - spec['variant'] = variant_spec + spec["variant"] = variant_spec pkg_params = {name: spec} @@ -327,36 +331,37 @@ def install(name=None, refresh=False, pkgs=None, **kwargs): formulas_array.append(pname) if pparams: - if 'version' in pparams: - formulas_array.append('@' + pparams['version']) + if "version" in pparams: + formulas_array.append("@" + pparams["version"]) - if 'variant' in pparams: - formulas_array.append(pparams['variant']) + if "variant" in pparams: + formulas_array.append(pparams["variant"]) old = list_pkgs() - cmd = ['port', 'install'] + cmd = ["port", "install"] cmd.extend(formulas_array) - err_message = '' + err_message = "" try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: err_message = exc.strerror - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if err_message: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': err_message, 'changes': ret}) + "Problem encountered installing package(s)", + info={"errors": err_message, "changes": ret}, + ) return ret def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 - ''' + """ Check whether or not an upgrade is available for all packages Options: @@ -369,15 +374,15 @@ def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ if refresh: refresh_db() - return _list('outdated') + return _list("outdated") def upgrade_available(pkg, refresh=True): - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -385,12 +390,12 @@ def upgrade_available(pkg, refresh=True): .. code-block:: bash salt '*' pkg.upgrade_available - ''' + """ return pkg in list_upgrades(refresh=refresh) def refresh_db(): - ''' + """ Update ports with ``port selfupdate`` CLI Example: @@ -398,15 +403,15 @@ def refresh_db(): .. code-block:: bash salt mac pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) - cmd = ['port', 'selfupdate'] + cmd = ["port", "selfupdate"] return salt.utils.mac_utils.execute_return_success(cmd) def upgrade(refresh=True): # pylint: disable=W0613 - ''' + """ Run a full upgrade using MacPorts 'port upgrade outdated' Options: @@ -427,23 +432,21 @@ def upgrade(refresh=True): # pylint: disable=W0613 .. code-block:: bash salt '*' pkg.upgrade - ''' + """ if refresh: refresh_db() old = list_pkgs() - cmd = ['port', 'upgrade', 'outdated'] - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - __context__.pop('pkg.list_pkgs', None) + cmd = ["port", "upgrade", "outdated"] + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret diff --git a/salt/modules/mac_power.py b/salt/modules/mac_power.py index 0fed9157b01..2d26a0c7aaa 100644 --- a/salt/modules/mac_power.py +++ b/salt/modules/mac_power.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Module for editing power settings on macOS .. versionadded:: 2016.3.0 -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.mac_utils @@ -16,22 +16,25 @@ from salt.exceptions import SaltInvocationError from salt.ext import six from salt.ext.six.moves import range -__virtualname__ = 'power' +__virtualname__ = "power" def __virtual__(): - ''' + """ Only for macOS - ''' + """ if not salt.utils.platform.is_darwin(): - return (False, 'The mac_power module could not be loaded: ' - 'module only works on macOS systems.') + return ( + False, + "The mac_power module could not be loaded: " + "module only works on macOS systems.", + ) return __virtualname__ def _validate_sleep(minutes): - ''' + """ Helper function that validates the minutes parameter. Can be any number between 1 and 180. Can also be the string values "Never" and "Off". @@ -39,41 +42,48 @@ def _validate_sleep(minutes): it will error if "On" is passed Returns: The value to be passed to the command - ''' + """ # Must be a value between 1 and 180 or Never/Off if isinstance(minutes, six.string_types): - if minutes.lower() in ['never', 'off']: - return 'Never' + if minutes.lower() in ["never", "off"]: + return "Never" else: - msg = 'Invalid String Value for Minutes.\n' \ - 'String values must be "Never" or "Off".\n' \ - 'Passed: {0}'.format(minutes) + msg = ( + "Invalid String Value for Minutes.\n" + 'String values must be "Never" or "Off".\n' + "Passed: {0}".format(minutes) + ) raise SaltInvocationError(msg) elif isinstance(minutes, bool): if minutes: - msg = 'Invalid Boolean Value for Minutes.\n' \ - 'Boolean value "On" or "True" is not allowed.\n' \ - 'Salt CLI converts "On" to boolean True.\n' \ - 'Passed: {0}'.format(minutes) + msg = ( + "Invalid Boolean Value for Minutes.\n" + 'Boolean value "On" or "True" is not allowed.\n' + 'Salt CLI converts "On" to boolean True.\n' + "Passed: {0}".format(minutes) + ) raise SaltInvocationError(msg) else: - return 'Never' + return "Never" elif isinstance(minutes, int): if minutes in range(1, 181): return minutes else: - msg = 'Invalid Integer Value for Minutes.\n' \ - 'Integer values must be between 1 and 180.\n' \ - 'Passed: {0}'.format(minutes) + msg = ( + "Invalid Integer Value for Minutes.\n" + "Integer values must be between 1 and 180.\n" + "Passed: {0}".format(minutes) + ) raise SaltInvocationError(msg) else: - msg = 'Unknown Variable Type Passed for Minutes.\n' \ - 'Passed: {0}'.format(minutes) + msg = "Unknown Variable Type Passed for Minutes.\n" "Passed: {0}".format( + minutes + ) raise SaltInvocationError(msg) def get_sleep(): - ''' + """ Displays the amount of idle time until the machine sleeps. Settings for Computer, Display, and Hard Disk are displayed. @@ -87,14 +97,16 @@ def get_sleep(): .. code-block:: bash salt '*' power.get_sleep - ''' - return {'Computer': get_computer_sleep(), - 'Display': get_display_sleep(), - 'Hard Disk': get_harddisk_sleep()} + """ + return { + "Computer": get_computer_sleep(), + "Display": get_display_sleep(), + "Hard Disk": get_harddisk_sleep(), + } def set_sleep(minutes): - ''' + """ Sets the amount of idle time until the machine sleeps. Sets the same value for Computer, Display, and Hard Disk. Pass "Never" or "Off" for computers that should never sleep. @@ -111,22 +123,19 @@ def set_sleep(minutes): salt '*' power.set_sleep 120 salt '*' power.set_sleep never - ''' + """ value = _validate_sleep(minutes) - cmd = 'systemsetup -setsleep {0}'.format(value) + cmd = "systemsetup -setsleep {0}".format(value) salt.utils.mac_utils.execute_return_success(cmd) state = [] for check in (get_computer_sleep, get_display_sleep, get_harddisk_sleep): - state.append(salt.utils.mac_utils.confirm_updated( - value, - check, - )) + state.append(salt.utils.mac_utils.confirm_updated(value, check,)) return all(state) def get_computer_sleep(): - ''' + """ Display the amount of idle time until the computer sleeps. :return: A string representing the sleep settings for the computer @@ -137,14 +146,13 @@ def get_computer_sleep(): ..code-block:: bash salt '*' power.get_computer_sleep - ''' - ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getcomputersleep') + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -getcomputersleep") return salt.utils.mac_utils.parse_return(ret) def set_computer_sleep(minutes): - ''' + """ Set the amount of idle time until the computer sleeps. Pass "Never" of "Off" to never sleep. @@ -160,19 +168,16 @@ def set_computer_sleep(minutes): salt '*' power.set_computer_sleep 120 salt '*' power.set_computer_sleep off - ''' + """ value = _validate_sleep(minutes) - cmd = 'systemsetup -setcomputersleep {0}'.format(value) + cmd = "systemsetup -setcomputersleep {0}".format(value) salt.utils.mac_utils.execute_return_success(cmd) - return salt.utils.mac_utils.confirm_updated( - str(value), - get_computer_sleep, - ) + return salt.utils.mac_utils.confirm_updated(str(value), get_computer_sleep,) def get_display_sleep(): - ''' + """ Display the amount of idle time until the display sleeps. :return: A string representing the sleep settings for the displey @@ -183,14 +188,13 @@ def get_display_sleep(): ..code-block:: bash salt '*' power.get_display_sleep - ''' - ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getdisplaysleep') + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -getdisplaysleep") return salt.utils.mac_utils.parse_return(ret) def set_display_sleep(minutes): - ''' + """ Set the amount of idle time until the display sleeps. Pass "Never" of "Off" to never sleep. @@ -206,19 +210,16 @@ def set_display_sleep(minutes): salt '*' power.set_display_sleep 120 salt '*' power.set_display_sleep off - ''' + """ value = _validate_sleep(minutes) - cmd = 'systemsetup -setdisplaysleep {0}'.format(value) + cmd = "systemsetup -setdisplaysleep {0}".format(value) salt.utils.mac_utils.execute_return_success(cmd) - return salt.utils.mac_utils.confirm_updated( - str(value), - get_display_sleep, - ) + return salt.utils.mac_utils.confirm_updated(str(value), get_display_sleep,) def get_harddisk_sleep(): - ''' + """ Display the amount of idle time until the hard disk sleeps. :return: A string representing the sleep settings for the hard disk @@ -229,14 +230,13 @@ def get_harddisk_sleep(): ..code-block:: bash salt '*' power.get_harddisk_sleep - ''' - ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getharddisksleep') + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -getharddisksleep") return salt.utils.mac_utils.parse_return(ret) def set_harddisk_sleep(minutes): - ''' + """ Set the amount of idle time until the harddisk sleeps. Pass "Never" of "Off" to never sleep. @@ -252,19 +252,16 @@ def set_harddisk_sleep(minutes): salt '*' power.set_harddisk_sleep 120 salt '*' power.set_harddisk_sleep off - ''' + """ value = _validate_sleep(minutes) - cmd = 'systemsetup -setharddisksleep {0}'.format(value) + cmd = "systemsetup -setharddisksleep {0}".format(value) salt.utils.mac_utils.execute_return_success(cmd) - return salt.utils.mac_utils.confirm_updated( - str(value), - get_harddisk_sleep, - ) + return salt.utils.mac_utils.confirm_updated(str(value), get_harddisk_sleep,) def get_wake_on_modem(): - ''' + """ Displays whether 'wake on modem' is on or off if supported :return: A string value representing the "wake on modem" settings @@ -275,15 +272,16 @@ def get_wake_on_modem(): .. code-block:: bash salt '*' power.get_wake_on_modem - ''' - ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getwakeonmodem') - return salt.utils.mac_utils.validate_enabled( - salt.utils.mac_utils.parse_return(ret)) == 'on' + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -getwakeonmodem") + return ( + salt.utils.mac_utils.validate_enabled(salt.utils.mac_utils.parse_return(ret)) + == "on" + ) def set_wake_on_modem(enabled): - ''' + """ Set whether or not the computer will wake from sleep when modem activity is detected. @@ -299,19 +297,16 @@ def set_wake_on_modem(enabled): .. code-block:: bash salt '*' power.set_wake_on_modem True - ''' + """ state = salt.utils.mac_utils.validate_enabled(enabled) - cmd = 'systemsetup -setwakeonmodem {0}'.format(state) + cmd = "systemsetup -setwakeonmodem {0}".format(state) salt.utils.mac_utils.execute_return_success(cmd) - return salt.utils.mac_utils.confirm_updated( - state, - get_wake_on_modem, - ) + return salt.utils.mac_utils.confirm_updated(state, get_wake_on_modem,) def get_wake_on_network(): - ''' + """ Displays whether 'wake on network' is on or off if supported :return: A string value representing the "wake on network" settings @@ -322,15 +317,18 @@ def get_wake_on_network(): .. code-block:: bash salt '*' power.get_wake_on_network - ''' + """ ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getwakeonnetworkaccess') - return salt.utils.mac_utils.validate_enabled( - salt.utils.mac_utils.parse_return(ret)) == 'on' + "systemsetup -getwakeonnetworkaccess" + ) + return ( + salt.utils.mac_utils.validate_enabled(salt.utils.mac_utils.parse_return(ret)) + == "on" + ) def set_wake_on_network(enabled): - ''' + """ Set whether or not the computer will wake from sleep when network activity is detected. @@ -346,19 +344,16 @@ def set_wake_on_network(enabled): .. code-block:: bash salt '*' power.set_wake_on_network True - ''' + """ state = salt.utils.mac_utils.validate_enabled(enabled) - cmd = 'systemsetup -setwakeonnetworkaccess {0}'.format(state) + cmd = "systemsetup -setwakeonnetworkaccess {0}".format(state) salt.utils.mac_utils.execute_return_success(cmd) - return salt.utils.mac_utils.confirm_updated( - state, - get_wake_on_network, - ) + return salt.utils.mac_utils.confirm_updated(state, get_wake_on_network,) def get_restart_power_failure(): - ''' + """ Displays whether 'restart on power failure' is on or off if supported :return: A string value representing the "restart on power failure" settings @@ -369,15 +364,18 @@ def get_restart_power_failure(): .. code-block:: bash salt '*' power.get_restart_power_failure - ''' + """ ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getrestartpowerfailure') - return salt.utils.mac_utils.validate_enabled( - salt.utils.mac_utils.parse_return(ret)) == 'on' + "systemsetup -getrestartpowerfailure" + ) + return ( + salt.utils.mac_utils.validate_enabled(salt.utils.mac_utils.parse_return(ret)) + == "on" + ) def set_restart_power_failure(enabled): - ''' + """ Set whether or not the computer will automatically restart after a power failure. @@ -393,19 +391,16 @@ def set_restart_power_failure(enabled): .. code-block:: bash salt '*' power.set_restart_power_failure True - ''' + """ state = salt.utils.mac_utils.validate_enabled(enabled) - cmd = 'systemsetup -setrestartpowerfailure {0}'.format(state) + cmd = "systemsetup -setrestartpowerfailure {0}".format(state) salt.utils.mac_utils.execute_return_success(cmd) - return salt.utils.mac_utils.confirm_updated( - state, - get_restart_power_failure, - ) + return salt.utils.mac_utils.confirm_updated(state, get_restart_power_failure,) def get_restart_freeze(): - ''' + """ Displays whether 'restart on freeze' is on or off if supported :return: A string value representing the "restart on freeze" settings @@ -416,15 +411,16 @@ def get_restart_freeze(): .. code-block:: bash salt '*' power.get_restart_freeze - ''' - ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getrestartfreeze') - return salt.utils.mac_utils.validate_enabled( - salt.utils.mac_utils.parse_return(ret)) == 'on' + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -getrestartfreeze") + return ( + salt.utils.mac_utils.validate_enabled(salt.utils.mac_utils.parse_return(ret)) + == "on" + ) def set_restart_freeze(enabled): - ''' + """ Specifies whether the server restarts automatically after a system freeze. This setting doesn't seem to be editable. The command completes successfully but the setting isn't actually updated. This is probably a macOS. The @@ -442,20 +438,16 @@ def set_restart_freeze(enabled): .. code-block:: bash salt '*' power.set_restart_freeze True - ''' + """ state = salt.utils.mac_utils.validate_enabled(enabled) - cmd = 'systemsetup -setrestartfreeze {0}'.format(state) + cmd = "systemsetup -setrestartfreeze {0}".format(state) salt.utils.mac_utils.execute_return_success(cmd) - return salt.utils.mac_utils.confirm_updated( - state, - get_restart_freeze, - True - ) + return salt.utils.mac_utils.confirm_updated(state, get_restart_freeze, True) def get_sleep_on_power_button(): - ''' + """ Displays whether 'allow power button to sleep computer' is on or off if supported @@ -469,15 +461,18 @@ def get_sleep_on_power_button(): .. code-block:: bash salt '*' power.get_sleep_on_power_button - ''' + """ ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getallowpowerbuttontosleepcomputer') - return salt.utils.mac_utils.validate_enabled( - salt.utils.mac_utils.parse_return(ret)) == 'on' + "systemsetup -getallowpowerbuttontosleepcomputer" + ) + return ( + salt.utils.mac_utils.validate_enabled(salt.utils.mac_utils.parse_return(ret)) + == "on" + ) def set_sleep_on_power_button(enabled): - ''' + """ Set whether or not the power button can sleep the computer. :param bool enabled: True to enable, False to disable. "On" and "Off" are @@ -492,12 +487,9 @@ def set_sleep_on_power_button(enabled): .. code-block:: bash salt '*' power.set_sleep_on_power_button True - ''' + """ state = salt.utils.mac_utils.validate_enabled(enabled) - cmd = 'systemsetup -setallowpowerbuttontosleepcomputer {0}'.format(state) + cmd = "systemsetup -setallowpowerbuttontosleepcomputer {0}".format(state) salt.utils.mac_utils.execute_return_success(cmd) - return salt.utils.mac_utils.confirm_updated( - state, - get_sleep_on_power_button, - ) + return salt.utils.mac_utils.confirm_updated(state, get_sleep_on_power_button,) diff --git a/salt/modules/mac_service.py b/salt/modules/mac_service.py index 857c168384b..85dd408c0cd 100644 --- a/salt/modules/mac_service.py +++ b/salt/modules/mac_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The service module for macOS .. versionadded:: 2016.3.0 @@ -21,8 +21,8 @@ This module has support for services in the following locations. path and a ``runas`` user is NOT specified, the current console user will be used to properly interact with the service. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -35,46 +35,57 @@ import salt.utils.path import salt.utils.platform import salt.utils.stringutils from salt.exceptions import CommandExecutionError -from salt.utils.versions import LooseVersion as _LooseVersion # Import 3rd party libs from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" __func_alias__ = { - 'list_': 'list', + "list_": "list", } log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only for macOS with launchctl - ''' + """ if not salt.utils.platform.is_darwin(): - return (False, 'Failed to load the mac_service module:\n' - 'Only available on macOS systems.') + return ( + False, + "Failed to load the mac_service module:\n" + "Only available on macOS systems.", + ) - if not salt.utils.path.which('launchctl'): - return (False, 'Failed to load the mac_service module:\n' - 'Required binary not found: "launchctl"') + if not salt.utils.path.which("launchctl"): + return ( + False, + "Failed to load the mac_service module:\n" + 'Required binary not found: "launchctl"', + ) - if not salt.utils.path.which('plutil'): - return (False, 'Failed to load the mac_service module:\n' - 'Required binary not found: "plutil"') + if not salt.utils.path.which("plutil"): + return ( + False, + "Failed to load the mac_service module:\n" + 'Required binary not found: "plutil"', + ) - if _LooseVersion(__grains__['osrelease']) < _LooseVersion('10.11'): - return (False, 'Failed to load the mac_service module:\n' - 'Requires macOS 10.11 or newer') + if _LooseVersion(__grains__["osrelease"]) < _LooseVersion("10.11"): + return ( + False, + "Failed to load the mac_service module:\nRequires macOS 10.11 or newer", + ) return __virtualname__ def _name_in_services(name, services): - ''' + """ Checks to see if the given service is in the given services. :param str name: Service label, file name, or full path @@ -85,16 +96,16 @@ def _name_in_services(name, services): an empty dictionary :rtype: dict - ''' + """ if name in services: # Match on label return services[name] for service in six.itervalues(services): - if service['file_path'].lower() == name: + if service["file_path"].lower() == name: # Match on full path return service - basename, ext = os.path.splitext(service['file_name']) + basename, ext = os.path.splitext(service["file_name"]) if basename.lower() == name: # Match on basename return service @@ -103,7 +114,7 @@ def _name_in_services(name, services): def _get_service(name): - ''' + """ Get information about a service. If the service is not found, raise an error @@ -111,8 +122,8 @@ def _get_service(name): :return: The service information for the service, otherwise an Error :rtype: dict - ''' - services = __utils__['mac_utils.available_services']() + """ + services = __utils__["mac_utils.available_services"]() name = name.lower() service = _name_in_services(name, services) @@ -125,28 +136,28 @@ def _get_service(name): # we received a cached batch of services, if not we did a fresh check # so we need to raise that the service could not be found. try: - if not __context__['using_cached_services']: - raise CommandExecutionError('Service not found: {0}'.format(name)) + if not __context__["using_cached_services"]: + raise CommandExecutionError("Service not found: {0}".format(name)) except KeyError: pass # we used a cached version to check, a service could have been made # between now and then, we should refresh our available services. - services = __utils__['mac_utils.available_services'](refresh=True) + services = __utils__["mac_utils.available_services"](refresh=True) # check to see if we found the service we are looking for. service = _name_in_services(name, services) if not service: # Could not find the service after refresh raise. - raise CommandExecutionError('Service not found: {0}'.format(name)) + raise CommandExecutionError("Service not found: {0}".format(name)) # found it :) return service def _always_running_service(name): - ''' + """ Check if the service should always be running based on the KeepAlive Key in the service plist. @@ -158,14 +169,14 @@ def _always_running_service(name): :rtype: bool .. versionadded:: 2019.2.0 - ''' + """ # get all the info from the launchctl service service_info = show(name) # get the value for the KeepAlive key in service plist try: - keep_alive = service_info['plist']['KeepAlive'] + keep_alive = service_info["plist"]["KeepAlive"] except KeyError: return False @@ -173,7 +184,7 @@ def _always_running_service(name): if isinstance(keep_alive, dict): # check for pathstate - for _file, value in six.iteritems(keep_alive.get('PathState', {})): + for _file, value in six.iteritems(keep_alive.get("PathState", {})): if value is True and os.path.exists(_file): return True elif value is False and not os.path.exists(_file): @@ -186,7 +197,7 @@ def _always_running_service(name): def _get_domain_target(name, service_target=False): - ''' + """ Returns the domain/service target and path for a service. This is used to determine whether or not a service should be loaded in a user space or system space. @@ -202,32 +213,32 @@ def _get_domain_target(name, service_target=False): :rtype: tuple .. versionadded:: 2019.2.0 - ''' + """ # Get service information service = _get_service(name) # get the path to the service - path = service['file_path'] + path = service["file_path"] # most of the time we'll be at the system level. - domain_target = 'system' + domain_target = "system" # check if a LaunchAgent as we should treat these differently. - if 'LaunchAgents' in path: + if "LaunchAgents" in path: # Get the console user so we can service in the correct session - uid = __utils__['mac_utils.console_user']() - domain_target = 'gui/{}'.format(uid) + uid = __utils__["mac_utils.console_user"]() + domain_target = "gui/{}".format(uid) # check to see if we need to make it a full service target. if service_target is True: - domain_target = '{}/{}'.format(domain_target, service['plist']['Label']) + domain_target = "{}/{}".format(domain_target, service["plist"]["Label"]) return (domain_target, path) def _launch_agent(name): - ''' + """ Checks to see if the provided service is a LaunchAgent :param str name: Service label, file name, or full path @@ -237,18 +248,18 @@ def _launch_agent(name): :rtype: bool .. versionadded:: 2019.2.0 - ''' + """ # Get the path to the service. - path = _get_service(name)['file_path'] + path = _get_service(name)["file_path"] - if 'LaunchAgents' not in path: + if "LaunchAgents" not in path: return False return True def show(name): - ''' + """ Show properties of a launchctl service :param str name: Service label, file name, or full path @@ -263,12 +274,12 @@ def show(name): salt '*' service.show org.cups.cupsd # service label salt '*' service.show org.cups.cupsd.plist # file name salt '*' service.show /System/Library/LaunchDaemons/org.cups.cupsd.plist # full path - ''' + """ return _get_service(name) def launchctl(sub_cmd, *args, **kwargs): - ''' + """ Run a launchctl command and raise an error if it fails :param str sub_cmd: Sub command supplied to launchctl @@ -291,12 +302,12 @@ def launchctl(sub_cmd, *args, **kwargs): .. code-block:: bash salt '*' service.launchctl debug org.cups.cupsd - ''' - return __utils__['mac_utils.launchctl'](sub_cmd, *args, **kwargs) + """ + return __utils__["mac_utils.launchctl"](sub_cmd, *args, **kwargs) def list_(name=None, runas=None): - ''' + """ Run launchctl list and return the output :param str name: The name of the service to list @@ -313,31 +324,26 @@ def list_(name=None, runas=None): salt '*' service.list salt '*' service.list org.cups.cupsd - ''' + """ if name: # Get service information and label service = _get_service(name) - label = service['plist']['Label'] + label = service["plist"]["Label"] # we can assume if we are trying to list a LaunchAgent we need # to run as a user, if not provided, we'll use the console user. if not runas and _launch_agent(name): - runas = __utils__['mac_utils.console_user'](username=True) + runas = __utils__["mac_utils.console_user"](username=True) # Collect information on service: will raise an error if it fails - return launchctl('list', - label, - return_stdout=True, - runas=runas) + return launchctl("list", label, return_stdout=True, runas=runas) # Collect information on all services: will raise an error if it fails - return launchctl('list', - return_stdout=True, - runas=runas) + return launchctl("list", return_stdout=True, runas=runas) def enable(name, runas=None): - ''' + """ Enable a launchd service. Raises an error if the service fails to be enabled :param str name: Service label, file name, or full path @@ -352,16 +358,16 @@ def enable(name, runas=None): .. code-block:: bash salt '*' service.enable org.cups.cupsd - ''' + """ # Get the domain target. enable requires a full service_target = _get_domain_target(name, service_target=True)[0] # Enable the service: will raise an error if it fails - return launchctl('enable', service_target, runas=runas) + return launchctl("enable", service_target, runas=runas) def disable(name, runas=None): - ''' + """ Disable a launchd service. Raises an error if the service fails to be disabled @@ -377,16 +383,16 @@ def disable(name, runas=None): .. code-block:: bash salt '*' service.disable org.cups.cupsd - ''' + """ # Get the service target. enable requires a full service_target = _get_domain_target(name, service_target=True)[0] # disable the service: will raise an error if it fails - return launchctl('disable', service_target, runas=runas) + return launchctl("disable", service_target, runas=runas) def start(name, runas=None): - ''' + """ Start a launchd service. Raises an error if the service fails to start .. note:: @@ -405,16 +411,16 @@ def start(name, runas=None): .. code-block:: bash salt '*' service.start org.cups.cupsd - ''' + """ # Get the domain target. domain_target, path = _get_domain_target(name) # Load (bootstrap) the service: will raise an error if it fails - return launchctl('bootstrap', domain_target, path, runas=runas) + return launchctl("bootstrap", domain_target, path, runas=runas) def stop(name, runas=None): - ''' + """ Stop a launchd service. Raises an error if the service fails to stop .. note:: @@ -434,16 +440,16 @@ def stop(name, runas=None): .. code-block:: bash salt '*' service.stop org.cups.cupsd - ''' + """ # Get the domain target. domain_target, path = _get_domain_target(name) # Stop (bootout) the service: will raise an error if it fails - return launchctl('bootout', domain_target, path, runas=runas) + return launchctl("bootout", domain_target, path, runas=runas) def restart(name, runas=None): - ''' + """ Unloads and reloads a launchd service. Raises an error if the service fails to reload @@ -459,7 +465,7 @@ def restart(name, runas=None): .. code-block:: bash salt '*' service.restart org.cups.cupsd - ''' + """ # Restart the service: will raise an error if it fails if enabled(name): stop(name, runas=runas) @@ -469,7 +475,7 @@ def restart(name, runas=None): def status(name, sig=None, runas=None): - ''' + """ Return the status for a service. :param str name: Used to find the service from launchctl. Can be any part @@ -490,45 +496,45 @@ def status(name, sig=None, runas=None): .. code-block:: bash salt '*' service.status cups - ''' + """ # Find service with ps if sig: - return __salt__['status.pid'](sig) + return __salt__["status.pid"](sig) try: _get_service(name) except CommandExecutionError as msg: log.error(msg) - return '' + return "" if not runas and _launch_agent(name): - runas = __utils__['mac_utils.console_user'](username=True) + runas = __utils__["mac_utils.console_user"](username=True) output = list_(runas=runas) # Used a string here instead of a list because that's what the linux version # of this module does - pids = '' + pids = "" for line in output.splitlines(): - if 'PID' in line: + if "PID" in line: continue if re.search(name, line.split()[-1]): if line.split()[0].isdigit(): if pids: - pids += '\n' + pids += "\n" pids += line.split()[0] # mac services are a little different than other platforms as they may be # set to run on intervals and may not always active with a PID. This will # return a string 'loaded' if it shouldn't always be running and is enabled. if not _always_running_service(name) and enabled(name) and not pids: - return 'loaded' + return "loaded" return pids def available(name): - ''' + """ Check that the given service is available. :param str name: The name of the service @@ -541,7 +547,7 @@ def available(name): .. code-block:: bash salt '*' service.available com.openssh.sshd - ''' + """ try: _get_service(name) return True @@ -550,7 +556,7 @@ def available(name): def missing(name): - ''' + """ The inverse of service.available Check that the given service is not available. @@ -564,12 +570,12 @@ def missing(name): .. code-block:: bash salt '*' service.missing com.openssh.sshd - ''' + """ return not available(name) def enabled(name, runas=None): - ''' + """ Check if the specified service is enabled :param str name: The name of the service to look up @@ -584,7 +590,7 @@ def enabled(name, runas=None): .. code-block:: bash salt '*' service.enabled org.cups.cupsd - ''' + """ # Try to list the service. If it can't be listed, it's not enabled try: list_(name=name, runas=runas) @@ -593,8 +599,8 @@ def enabled(name, runas=None): return False -def disabled(name, runas=None, domain='system'): - ''' +def disabled(name, runas=None, domain="system"): + """ Check if the specified service is not enabled. This is the opposite of ``service.enabled`` @@ -612,26 +618,23 @@ def disabled(name, runas=None, domain='system'): .. code-block:: bash salt '*' service.disabled org.cups.cupsd - ''' + """ - disabled = launchctl('print-disabled', - domain, - return_stdout=True, - runas=runas) + disabled = launchctl("print-disabled", domain, return_stdout=True, runas=runas) for service in disabled.split("\n"): if name in service: - srv_name = service.split("=>")[0].split("\"")[1] + srv_name = service.split("=>")[0].split('"')[1] status = service.split("=>")[1] if name != srv_name: pass else: - return True if 'true' in status.lower() else False + return True if "true" in status.lower() else False return False def get_all(runas=None): - ''' + """ Return a list of services that are enabled or available. Can be used to find the name of a service. @@ -645,19 +648,19 @@ def get_all(runas=None): .. code-block:: bash salt '*' service.get_all - ''' + """ # Get list of enabled services enabled = get_enabled(runas=runas) # Get list of all services - available = list(__utils__['mac_utils.available_services']().keys()) + available = list(__utils__["mac_utils.available_services"]().keys()) # Return composite list return sorted(set(enabled + available)) def get_enabled(runas=None): - ''' + """ Return a list of all services that are enabled. Can be used to find the name of a service. @@ -671,7 +674,7 @@ def get_enabled(runas=None): .. code-block:: bash salt '*' service.get_enabled - ''' + """ # Collect list of enabled services stdout = list_(runas=runas) service_lines = [line for line in stdout.splitlines()] @@ -680,10 +683,10 @@ def get_enabled(runas=None): enabled = [] for line in service_lines: # Skip header line - if line.startswith('PID'): + if line.startswith("PID"): continue - pid, status, label = line.split('\t') + pid, status, label = line.split("\t") enabled.append(label) return sorted(set(enabled)) diff --git a/salt/modules/mac_shadow.py b/salt/modules/mac_shadow.py index 2f615da9053..5096293d8fe 100644 --- a/salt/modules/mac_shadow.py +++ b/salt/modules/mac_shadow.py @@ -1,49 +1,51 @@ # -*- coding: utf-8 -*- -''' +""" Manage macOS local directory passwords and policies .. versionadded:: 2016.3.0 Note that it is usually better to apply password policies through the creation of a configuration profile. -''' +""" # Authentication concepts reference: # https://developer.apple.com/library/mac/documentation/Networking/Conceptual/Open_Directory/openDirectoryConcepts/openDirectoryConcepts.html#//apple_ref/doc/uid/TP40000917-CH3-CIFCAIBB -from __future__ import absolute_import, unicode_literals, print_function -from datetime import datetime - - -try: - import pwd - HAS_PWD = True -except ImportError: - HAS_PWD = False +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import logging +from datetime import datetime + import salt.utils.mac_utils import salt.utils.platform from salt.exceptions import CommandExecutionError +try: + import pwd + + HAS_PWD = True +except ImportError: + HAS_PWD = False + + log = logging.getLogger(__name__) # Start logging -__virtualname__ = 'shadow' +__virtualname__ = "shadow" def __virtual__(): # Is this macOS? if not salt.utils.platform.is_darwin(): - return False, 'Not macOS' + return False, "Not macOS" if HAS_PWD: return __virtualname__ else: - return (False, 'The pwd module failed to load.') + return (False, "The pwd module failed to load.") def _get_account_policy(name): - ''' + """ Get the entire accountPolicy and return it as a dictionary. For use by this module only @@ -53,22 +55,22 @@ def _get_account_policy(name): :rtype: dict :raises: CommandExecutionError on user not found or any other unknown error - ''' + """ - cmd = 'pwpolicy -u {0} -getpolicy'.format(name) + cmd = "pwpolicy -u {0} -getpolicy".format(name) try: ret = salt.utils.mac_utils.execute_return_result(cmd) except CommandExecutionError as exc: - if 'Error: user <{0}> not found'.format(name) in exc.strerror: - raise CommandExecutionError('User not found: {0}'.format(name)) - raise CommandExecutionError('Unknown error: {0}'.format(exc.strerror)) + if "Error: user <{0}> not found".format(name) in exc.strerror: + raise CommandExecutionError("User not found: {0}".format(name)) + raise CommandExecutionError("Unknown error: {0}".format(exc.strerror)) try: - policy_list = ret.split('\n')[1].split(' ') + policy_list = ret.split("\n")[1].split(" ") policy_dict = {} for policy in policy_list: - if '=' in policy: - key, value = policy.split('=') + if "=" in policy: + key, value = policy.split("=") policy_dict[key] = value return policy_dict except IndexError: @@ -76,7 +78,7 @@ def _get_account_policy(name): def _set_account_policy(name, policy): - ''' + """ Set a value in the user accountPolicy. For use by this module only :param str name: The user name @@ -86,19 +88,19 @@ def _set_account_policy(name, policy): :rtype: bool :raises: CommandExecutionError on user not found or any other unknown error - ''' + """ cmd = 'pwpolicy -u {0} -setpolicy "{1}"'.format(name, policy) try: return salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: - if 'Error: user <{0}> not found'.format(name) in exc.strerror: - raise CommandExecutionError('User not found: {0}'.format(name)) - raise CommandExecutionError('Unknown error: {0}'.format(exc.strerror)) + if "Error: user <{0}> not found".format(name) in exc.strerror: + raise CommandExecutionError("User not found: {0}".format(name)) + raise CommandExecutionError("Unknown error: {0}".format(exc.strerror)) def _get_account_policy_data_value(name, key): - ''' + """ Return the value for a key in the accountPolicy section of the user's plist file. For use by this module only @@ -109,36 +111,36 @@ def _get_account_policy_data_value(name, key): :rtype: str :raises: CommandExecutionError on user not found or any other unknown error - ''' - cmd = 'dscl . -readpl /Users/{0} accountPolicyData {1}'.format(name, key) + """ + cmd = "dscl . -readpl /Users/{0} accountPolicyData {1}".format(name, key) try: ret = salt.utils.mac_utils.execute_return_result(cmd) except CommandExecutionError as exc: - if 'eDSUnknownNodeName' in exc.strerror: - raise CommandExecutionError('User not found: {0}'.format(name)) - raise CommandExecutionError('Unknown error: {0}'.format(exc.strerror)) + if "eDSUnknownNodeName" in exc.strerror: + raise CommandExecutionError("User not found: {0}".format(name)) + raise CommandExecutionError("Unknown error: {0}".format(exc.strerror)) return ret def _convert_to_datetime(unix_timestamp): - ''' + """ Converts a unix timestamp to a human readable date/time :param float unix_timestamp: A unix timestamp :return: A date/time in the format YYYY-mm-dd HH:MM:SS :rtype: str - ''' + """ try: unix_timestamp = float(unix_timestamp) - return datetime.fromtimestamp(unix_timestamp).strftime('%Y-%m-%d %H:%M:%S') + return datetime.fromtimestamp(unix_timestamp).strftime("%Y-%m-%d %H:%M:%S") except (ValueError, TypeError): - return 'Invalid Timestamp' + return "Invalid Timestamp" def info(name): - ''' + """ Return information for the specified user :param str name: The username @@ -151,40 +153,44 @@ def info(name): .. code-block:: bash salt '*' shadow.info admin - ''' + """ try: data = pwd.getpwnam(name) - return {'name': data.pw_name, - 'passwd': data.pw_passwd, - 'account_created': get_account_created(name), - 'login_failed_count': get_login_failed_count(name), - 'login_failed_last': get_login_failed_last(name), - 'lstchg': get_last_change(name), - 'max': get_maxdays(name), - 'expire': get_expire(name), - 'change': get_change(name), - 'min': 'Unavailable', - 'warn': 'Unavailable', - 'inact': 'Unavailable'} + return { + "name": data.pw_name, + "passwd": data.pw_passwd, + "account_created": get_account_created(name), + "login_failed_count": get_login_failed_count(name), + "login_failed_last": get_login_failed_last(name), + "lstchg": get_last_change(name), + "max": get_maxdays(name), + "expire": get_expire(name), + "change": get_change(name), + "min": "Unavailable", + "warn": "Unavailable", + "inact": "Unavailable", + } except KeyError: - log.debug('User not found: %s', name) - return {'name': '', - 'passwd': '', - 'account_created': '', - 'login_failed_count': '', - 'login_failed_last': '', - 'lstchg': '', - 'max': '', - 'expire': '', - 'change': '', - 'min': '', - 'warn': '', - 'inact': ''} + log.debug("User not found: %s", name) + return { + "name": "", + "passwd": "", + "account_created": "", + "login_failed_count": "", + "login_failed_last": "", + "lstchg": "", + "max": "", + "expire": "", + "change": "", + "min": "", + "warn": "", + "inact": "", + } def get_account_created(name): - ''' + """ Get the date/time the account was created :param str name: The username of the account @@ -199,8 +205,8 @@ def get_account_created(name): .. code-block:: bash salt '*' shadow.get_account_created admin - ''' - ret = _get_account_policy_data_value(name, 'creationTime') + """ + ret = _get_account_policy_data_value(name, "creationTime") unix_timestamp = salt.utils.mac_utils.parse_return(ret) @@ -210,7 +216,7 @@ def get_account_created(name): def get_last_change(name): - ''' + """ Get the date/time the account was changed :param str name: The username of the account @@ -225,8 +231,8 @@ def get_last_change(name): .. code-block:: bash salt '*' shadow.get_last_change admin - ''' - ret = _get_account_policy_data_value(name, 'passwordLastSetTime') + """ + ret = _get_account_policy_data_value(name, "passwordLastSetTime") unix_timestamp = salt.utils.mac_utils.parse_return(ret) @@ -236,7 +242,7 @@ def get_last_change(name): def get_login_failed_count(name): - ''' + """ Get the the number of failed login attempts :param str name: The username of the account @@ -251,14 +257,14 @@ def get_login_failed_count(name): .. code-block:: bash salt '*' shadow.get_login_failed_count admin - ''' - ret = _get_account_policy_data_value(name, 'failedLoginCount') + """ + ret = _get_account_policy_data_value(name, "failedLoginCount") return salt.utils.mac_utils.parse_return(ret) def get_login_failed_last(name): - ''' + """ Get the date/time of the last failed login attempt :param str name: The username of the account @@ -274,8 +280,8 @@ def get_login_failed_last(name): .. code-block:: bash salt '*' shadow.get_login_failed_last admin - ''' - ret = _get_account_policy_data_value(name, 'failedLoginTimestamp') + """ + ret = _get_account_policy_data_value(name, "failedLoginTimestamp") unix_timestamp = salt.utils.mac_utils.parse_return(ret) @@ -285,7 +291,7 @@ def get_login_failed_last(name): def set_maxdays(name, days): - ''' + """ Set the maximum age of the password in days :param str name: The username of the account @@ -302,17 +308,16 @@ def set_maxdays(name, days): .. code-block:: bash salt '*' shadow.set_maxdays admin 90 - ''' + """ minutes = days * 24 * 60 - _set_account_policy( - name, 'maxMinutesUntilChangePassword={0}'.format(minutes)) + _set_account_policy(name, "maxMinutesUntilChangePassword={0}".format(minutes)) return get_maxdays(name) == days def get_maxdays(name): - ''' + """ Get the maximum age of the password :param str name: The username of the account @@ -327,18 +332,18 @@ def get_maxdays(name): .. code-block:: bash salt '*' shadow.get_maxdays admin 90 - ''' + """ policies = _get_account_policy(name) - if 'maxMinutesUntilChangePassword' in policies: - max_minutes = policies['maxMinutesUntilChangePassword'] + if "maxMinutesUntilChangePassword" in policies: + max_minutes = policies["maxMinutesUntilChangePassword"] return int(max_minutes) / 24 / 60 return 0 def set_mindays(name, days): - ''' + """ Set the minimum password age in days. Not available in macOS. :param str name: The user name @@ -353,12 +358,12 @@ def set_mindays(name, days): .. code-block:: bash salt '*' shadow.set_mindays admin 90 - ''' + """ return False def set_inactdays(name, days): - ''' + """ Set the number if inactive days before the account is locked. Not available in macOS @@ -374,12 +379,12 @@ def set_inactdays(name, days): .. code-block:: bash salt '*' shadow.set_inactdays admin 90 - ''' + """ return False def set_warndays(name, days): - ''' + """ Set the number of days before the password expires that the user will start to see a warning. Not available in macOS @@ -395,12 +400,12 @@ def set_warndays(name, days): .. code-block:: bash salt '*' shadow.set_warndays admin 90 - ''' + """ return False def set_change(name, date): - ''' + """ Sets the date on which the password expires. The user will be required to change their password. Format is mm/dd/yyyy @@ -419,15 +424,16 @@ def set_change(name, date): .. code-block:: bash salt '*' shadow.set_change username 09/21/2016 - ''' + """ _set_account_policy( - name, 'usingExpirationDate=1 expirationDateGMT={0}'.format(date)) + name, "usingExpirationDate=1 expirationDateGMT={0}".format(date) + ) return get_change(name) == date def get_change(name): - ''' + """ Gets the date on which the password expires :param str name: The name of the user account @@ -442,17 +448,17 @@ def get_change(name): .. code-block:: bash salt '*' shadow.get_change username - ''' + """ policies = _get_account_policy(name) - if 'expirationDateGMT' in policies: - return policies['expirationDateGMT'] + if "expirationDateGMT" in policies: + return policies["expirationDateGMT"] - return 'Value not set' + return "Value not set" def set_expire(name, date): - ''' + """ Sets the date on which the account expires. The user will not be able to login after this date. Date format is mm/dd/yyyy @@ -471,15 +477,16 @@ def set_expire(name, date): .. code-block:: bash salt '*' shadow.set_expire username 07/23/2015 - ''' + """ _set_account_policy( - name, 'usingHardExpirationDate=1 hardExpireDateGMT={0}'.format(date)) + name, "usingHardExpirationDate=1 hardExpireDateGMT={0}".format(date) + ) return get_expire(name) == date def get_expire(name): - ''' + """ Gets the date on which the account expires :param str name: The name of the user account @@ -494,17 +501,17 @@ def get_expire(name): .. code-block:: bash salt '*' shadow.get_expire username - ''' + """ policies = _get_account_policy(name) - if 'hardExpireDateGMT' in policies: - return policies['hardExpireDateGMT'] + if "hardExpireDateGMT" in policies: + return policies["hardExpireDateGMT"] - return 'Value not set' + return "Value not set" def del_password(name): - ''' + """ Deletes the account password :param str name: The user name of the account @@ -519,25 +526,25 @@ def del_password(name): .. code-block:: bash salt '*' shadow.del_password username - ''' + """ # This removes the password cmd = "dscl . -passwd /Users/{0} ''".format(name) try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: - if 'eDSUnknownNodeName' in exc.strerror: - raise CommandExecutionError('User not found: {0}'.format(name)) - raise CommandExecutionError('Unknown error: {0}'.format(exc.strerror)) + if "eDSUnknownNodeName" in exc.strerror: + raise CommandExecutionError("User not found: {0}".format(name)) + raise CommandExecutionError("Unknown error: {0}".format(exc.strerror)) # This is so it looks right in shadow.info cmd = "dscl . -create /Users/{0} Password '*'".format(name) salt.utils.mac_utils.execute_return_success(cmd) - return info(name)['passwd'] == '*' + return info(name)["passwd"] == "*" def set_password(name, password): - ''' + """ Set the password for a named user (insecure, the password will be in the process list while the command is running) @@ -556,13 +563,13 @@ def set_password(name, password): .. code-block:: bash salt '*' mac_shadow.set_password macuser macpassword - ''' + """ cmd = "dscl . -passwd /Users/{0} '{1}'".format(name, password) try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: - if 'eDSUnknownNodeName' in exc.strerror: - raise CommandExecutionError('User not found: {0}'.format(name)) - raise CommandExecutionError('Unknown error: {0}'.format(exc.strerror)) + if "eDSUnknownNodeName" in exc.strerror: + raise CommandExecutionError("User not found: {0}".format(name)) + raise CommandExecutionError("Unknown error: {0}".format(exc.strerror)) return True diff --git a/salt/modules/mac_softwareupdate.py b/salt/modules/mac_softwareupdate.py index cee95fc2794..bb95e03f756 100644 --- a/salt/modules/mac_softwareupdate.py +++ b/salt/modules/mac_softwareupdate.py @@ -1,44 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" Support for the softwareupdate command on MacOS. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals +import os # Import python libs import re -import os # import salt libs import salt.utils.data import salt.utils.files -import salt.utils.path import salt.utils.mac_utils +import salt.utils.path import salt.utils.platform from salt.exceptions import CommandExecutionError, SaltInvocationError -__virtualname__ = 'softwareupdate' +__virtualname__ = "softwareupdate" def __virtual__(): - ''' + """ Only for MacOS - ''' + """ if not salt.utils.platform.is_darwin(): - return (False, 'The softwareupdate module could not be loaded: ' - 'module only works on MacOS systems.') + return ( + False, + "The softwareupdate module could not be loaded: " + "module only works on MacOS systems.", + ) return __virtualname__ def _get_available(recommended=False, restart=False): - ''' + """ Utility function to get all available update packages. Sample return date: { 'updatename': '1.2.3-45', ... } - ''' - cmd = ['softwareupdate', '--list'] + """ + cmd = ["softwareupdate", "--list"] out = salt.utils.mac_utils.execute_return_result(cmd) # rexp parses lines that look like the following: @@ -46,25 +49,23 @@ def _get_available(recommended=False, restart=False): # Safari (6.1.2), 51679K [recommended] # - iCal-1.0.2 # iCal, 1.0.2, 6520K - rexp = re.compile('(?m)^ [*|-] ' - r'([^ ].*)[\r\n].*\(([^\)]+)') + rexp = re.compile("(?m)^ [*|-] " r"([^ ].*)[\r\n].*\(([^\)]+)") if salt.utils.data.is_true(recommended): # rexp parses lines that look like the following: # * Safari6.1.2MountainLion-6.1.2 # Safari (6.1.2), 51679K [recommended] - rexp = re.compile('(?m)^ [*] ' - r'([^ ].*)[\r\n].*\(([^\)]+)') + rexp = re.compile("(?m)^ [*] " r"([^ ].*)[\r\n].*\(([^\)]+)") - keys = ['name', 'version'] + keys = ["name", "version"] _get = lambda l, k: l[keys.index(k)] updates = rexp.findall(out) ret = {} for line in updates: - name = _get(line, 'name') - version_num = _get(line, 'version') + name = _get(line, "name") + version_num = _get(line, "version") ret[name] = version_num if not salt.utils.data.is_true(restart): @@ -73,8 +74,7 @@ def _get_available(recommended=False, restart=False): # rexp parses lines that look like the following: # * Safari6.1.2MountainLion-6.1.2 # Safari (6.1.2), 51679K [recommended] [restart] - rexp1 = re.compile('(?m)^ [*|-] ' - r'([^ ].*)[\r\n].*restart*') + rexp1 = re.compile("(?m)^ [*|-] " r"([^ ].*)[\r\n].*restart*") restart_updates = rexp1.findall(out) ret_restart = {} @@ -86,7 +86,7 @@ def _get_available(recommended=False, restart=False): def list_available(recommended=False, restart=False): - ''' + """ List all available updates. :param bool recommended: Show only recommended updates. @@ -101,12 +101,12 @@ def list_available(recommended=False, restart=False): .. code-block:: bash salt '*' softwareupdate.list_available - ''' + """ return _get_available(recommended, restart) def ignore(name): - ''' + """ Ignore a specific program update. When an update is ignored the '-' and version number at the end will be omitted, so "SecUpd2014-001-1.0" becomes "SecUpd2014-001". It will be removed automatically if present. An update @@ -123,18 +123,18 @@ def ignore(name): .. code-block:: bash salt '*' softwareupdate.ignore - ''' + """ # remove everything after and including the '-' in the updates name. - to_ignore = name.rsplit('-', 1)[0] + to_ignore = name.rsplit("-", 1)[0] - cmd = ['softwareupdate', '--ignore', to_ignore] + cmd = ["softwareupdate", "--ignore", to_ignore] salt.utils.mac_utils.execute_return_success(cmd) return to_ignore in list_ignored() def list_ignored(): - ''' + """ List all updates that have been ignored. Ignored updates are shown without the '-' and version number at the end, this is how the softwareupdate command works. @@ -147,22 +147,21 @@ def list_ignored(): .. code-block:: bash salt '*' softwareupdate.list_ignored - ''' - cmd = ['softwareupdate', '--list', '--ignore'] + """ + cmd = ["softwareupdate", "--list", "--ignore"] out = salt.utils.mac_utils.execute_return_result(cmd) # rep parses lines that look like the following: # "Safari6.1.2MountainLion-6.1.2", # or: # Safari6.1.2MountainLion-6.1.2 - rexp = re.compile('(?m)^ ["]?' - r'([^,|\s].*[^"|\n|,])[,|"]?') + rexp = re.compile('(?m)^ ["]?' r'([^,|\s].*[^"|\n|,])[,|"]?') return rexp.findall(out) def reset_ignored(): - ''' + """ Make sure the ignored updates are not ignored anymore, returns a list of the updates that are no longer ignored. @@ -174,15 +173,15 @@ def reset_ignored(): .. code-block:: bash salt '*' softwareupdate.reset_ignored - ''' - cmd = ['softwareupdate', '--reset-ignored'] + """ + cmd = ["softwareupdate", "--reset-ignored"] salt.utils.mac_utils.execute_return_success(cmd) return list_ignored() == [] def schedule_enabled(): - ''' + """ Check the status of automatic update scheduling. :return: True if scheduling is enabled, False if disabled @@ -194,17 +193,17 @@ def schedule_enabled(): .. code-block:: bash salt '*' softwareupdate.schedule_enabled - ''' - cmd = ['softwareupdate', '--schedule'] + """ + cmd = ["softwareupdate", "--schedule"] ret = salt.utils.mac_utils.execute_return_result(cmd) enabled = ret.split()[-1] - return salt.utils.mac_utils.validate_enabled(enabled) == 'on' + return salt.utils.mac_utils.validate_enabled(enabled) == "on" def schedule_enable(enable): - ''' + """ Enable/disable automatic update scheduling. :param enable: True/On/Yes/1 to turn on automatic updates. False/No/Off/0 @@ -221,19 +220,21 @@ def schedule_enable(enable): .. code-block:: bash salt '*' softwareupdate.schedule_enable on|off - ''' + """ status = salt.utils.mac_utils.validate_enabled(enable) - cmd = ['softwareupdate', - '--schedule', - salt.utils.mac_utils.validate_enabled(status)] + cmd = [ + "softwareupdate", + "--schedule", + salt.utils.mac_utils.validate_enabled(status), + ] salt.utils.mac_utils.execute_return_success(cmd) return salt.utils.mac_utils.validate_enabled(schedule_enabled()) == status def update_all(recommended=False, restart=True): - ''' + """ Install all available updates. Returns a dictionary containing the name of the update and the status of its installation. @@ -254,14 +255,14 @@ def update_all(recommended=False, restart=True): .. code-block:: bash salt '*' softwareupdate.update_all - ''' + """ to_update = _get_available(recommended, restart) if not to_update: return {} for _update in to_update: - cmd = ['softwareupdate', '--install', _update] + cmd = ["softwareupdate", "--install", _update] salt.utils.mac_utils.execute_return_success(cmd) ret = {} @@ -274,7 +275,7 @@ def update_all(recommended=False, restart=True): def update(name): - ''' + """ Install a named update. :param str name: The name of the of the update to install. @@ -287,18 +288,18 @@ def update(name): .. code-block:: bash salt '*' softwareupdate.update - ''' + """ if not update_available(name): - raise SaltInvocationError('Update not available: {0}'.format(name)) + raise SaltInvocationError("Update not available: {0}".format(name)) - cmd = ['softwareupdate', '--install', name] + cmd = ["softwareupdate", "--install", name] salt.utils.mac_utils.execute_return_success(cmd) return not update_available(name) def update_available(name): - ''' + """ Check whether or not an update is available with a given name. :param str name: The name of the update to look for @@ -312,12 +313,12 @@ def update_available(name): salt '*' softwareupdate.update_available salt '*' softwareupdate.update_available "" - ''' + """ return name in _get_available() def list_downloads(): - ''' + """ Return a list of all updates that have been downloaded locally. :return: A list of updates that have been downloaded @@ -328,29 +329,31 @@ def list_downloads(): .. code-block:: bash salt '*' softwareupdate.list_downloads - ''' + """ outfiles = [] - for root, subFolder, files in salt.utils.path.os_walk('/Library/Updates'): + for root, subFolder, files in salt.utils.path.os_walk("/Library/Updates"): for f in files: outfiles.append(os.path.join(root, f)) dist_files = [] for f in outfiles: - if f.endswith('.dist'): + if f.endswith(".dist"): dist_files.append(f) ret = [] for update in _get_available(): for f in dist_files: with salt.utils.files.fopen(f) as fhr: - if update.rsplit('-', 1)[0] in salt.utils.stringutils.to_unicode(fhr.read()): + if update.rsplit("-", 1)[0] in salt.utils.stringutils.to_unicode( + fhr.read() + ): ret.append(update) return ret def download(name): - ''' + """ Download a named update so that it can be installed later with the ``update`` or ``update_all`` functions @@ -364,21 +367,21 @@ def download(name): .. code-block:: bash salt '*' softwareupdate.download - ''' + """ if not update_available(name): - raise SaltInvocationError('Update not available: {0}'.format(name)) + raise SaltInvocationError("Update not available: {0}".format(name)) if name in list_downloads(): return True - cmd = ['softwareupdate', '--download', name] + cmd = ["softwareupdate", "--download", name] salt.utils.mac_utils.execute_return_success(cmd) return name in list_downloads() def download_all(recommended=False, restart=True): - ''' + """ Download all available updates so that they can be installed later with the ``update`` or ``update_all`` functions. It returns a list of updates that are now downloaded. @@ -397,7 +400,7 @@ def download_all(recommended=False, restart=True): .. code-block:: bash salt '*' softwareupdate.download_all - ''' + """ to_download = _get_available(recommended, restart) for name in to_download: @@ -407,7 +410,7 @@ def download_all(recommended=False, restart=True): def get_catalog(): - ''' + """ .. versionadded:: 2016.3.0 Get the current catalog being used for update lookups. Will return a url if @@ -422,26 +425,24 @@ def get_catalog(): .. code-block:: bash salt '*' softwareupdates.get_catalog - ''' - cmd = ['defaults', - 'read', - '/Library/Preferences/com.apple.SoftwareUpdate.plist'] + """ + cmd = ["defaults", "read", "/Library/Preferences/com.apple.SoftwareUpdate.plist"] out = salt.utils.mac_utils.execute_return_result(cmd) - if 'AppleCatalogURL' in out: - cmd.append('AppleCatalogURL') + if "AppleCatalogURL" in out: + cmd.append("AppleCatalogURL") out = salt.utils.mac_utils.execute_return_result(cmd) return out - elif 'CatalogURL' in out: - cmd.append('CatalogURL') + elif "CatalogURL" in out: + cmd.append("CatalogURL") out = salt.utils.mac_utils.execute_return_result(cmd) return out else: - return 'Default' + return "Default" def set_catalog(url): - ''' + """ .. versionadded:: 2016.3.0 Set the Software Update Catalog to the URL specified @@ -456,11 +457,11 @@ def set_catalog(url): .. code-block:: bash salt '*' softwareupdates.set_catalog http://swupd.local:8888/index.sucatalog - ''' + """ # This command always returns an error code, though it completes # successfully. Success will be determined by making sure get_catalog # returns the passed url - cmd = ['softwareupdate', '--set-catalog', url] + cmd = ["softwareupdate", "--set-catalog", url] try: salt.utils.mac_utils.execute_return_success(cmd) @@ -471,7 +472,7 @@ def set_catalog(url): def reset_catalog(): - ''' + """ .. versionadded:: 2016.3.0 Reset the Software Update Catalog to the default. @@ -484,15 +485,15 @@ def reset_catalog(): .. code-block:: bash salt '*' softwareupdates.reset_catalog - ''' + """ # This command always returns an error code, though it completes # successfully. Success will be determined by making sure get_catalog # returns 'Default' - cmd = ['softwareupdate', '--clear-catalog'] + cmd = ["softwareupdate", "--clear-catalog"] try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: pass - return get_catalog() == 'Default' + return get_catalog() == "Default" diff --git a/salt/modules/mac_sysctl.py b/salt/modules/mac_sysctl.py index f0f4447e9bc..e8b3b7d173a 100644 --- a/salt/modules/mac_sysctl.py +++ b/salt/modules/mac_sysctl.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Module for viewing and modifying sysctl parameters -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os @@ -13,21 +13,24 @@ from salt.exceptions import CommandExecutionError from salt.ext import six # Define the module's virtual name -__virtualname__ = 'sysctl' +__virtualname__ = "sysctl" def __virtual__(): - ''' + """ Only run on Darwin (macOS) systems - ''' - if __grains__['os'] == 'MacOS': + """ + if __grains__["os"] == "MacOS": return __virtualname__ - return (False, 'The darwin_sysctl execution module cannot be loaded: ' - 'Only available on macOS systems.') + return ( + False, + "The darwin_sysctl execution module cannot be loaded: " + "Only available on macOS systems.", + ) def show(config_file=False): - ''' + """ Return a list of sysctl parameters for this minion CLI Example: @@ -35,25 +38,25 @@ def show(config_file=False): .. code-block:: bash salt '*' sysctl.show - ''' + """ roots = ( - 'audit', - 'debug', - 'hw', - 'hw', - 'kern', - 'machdep', - 'net', - 'net', - 'security', - 'user', - 'vfs', - 'vm', + "audit", + "debug", + "hw", + "hw", + "kern", + "machdep", + "net", + "net", + "security", + "user", + "vfs", + "vm", ) - cmd = 'sysctl -a' + cmd = "sysctl -a" ret = {} - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) - comps = [''] + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) + comps = [""] for line in out.splitlines(): # This might need to be converted to a regex, and more, as sysctl output # can for some reason contain entries such as: @@ -65,21 +68,21 @@ def show(config_file=False): # # Yes. That's two `kern.clockrate`. # - if any([line.startswith('{0}.'.format(root)) for root in roots]): - comps = line.split(': ' if ': ' in line else ' = ', 1) + if any([line.startswith("{0}.".format(root)) for root in roots]): + comps = line.split(": " if ": " in line else " = ", 1) if len(comps) == 2: ret[comps[0]] = comps[1] else: - ret[comps[0]] = '' + ret[comps[0]] = "" elif comps[0]: - ret[comps[0]] += '{0}\n'.format(line) + ret[comps[0]] += "{0}\n".format(line) else: continue return ret def get(name): - ''' + """ Return a single sysctl parameter for this minion name @@ -90,14 +93,14 @@ def get(name): .. code-block:: bash salt '*' sysctl.get hw.physmem - ''' - cmd = 'sysctl -n {0}'.format(name) - out = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = "sysctl -n {0}".format(name) + out = __salt__["cmd.run"](cmd, python_shell=False) return out def assign(name, value): - ''' + """ Assign a single sysctl parameter for this minion name @@ -111,21 +114,20 @@ def assign(name, value): .. code-block:: bash salt '*' sysctl.assign net.inet.icmp.icmplim 50 - ''' + """ ret = {} cmd = 'sysctl -w {0}="{1}"'.format(name, value) - data = __salt__['cmd.run_all'](cmd, python_shell=False) + data = __salt__["cmd.run_all"](cmd, python_shell=False) - if data['retcode'] != 0: - raise CommandExecutionError('sysctl failed: {0}'.format( - data['stderr'])) - new_name, new_value = data['stdout'].split(':', 1) - ret[new_name] = new_value.split(' -> ')[-1] + if data["retcode"] != 0: + raise CommandExecutionError("sysctl failed: {0}".format(data["stderr"])) + new_name, new_value = data["stdout"].split(":", 1) + ret[new_name] = new_value.split(" -> ")[-1] return ret -def persist(name, value, config='/etc/sysctl.conf', apply_change=False): - ''' +def persist(name, value, config="/etc/sysctl.conf", apply_change=False): + """ Assign and persist a simple sysctl parameter for this minion name @@ -148,7 +150,7 @@ def persist(name, value, config='/etc/sysctl.conf', apply_change=False): salt '*' sysctl.persist net.inet.icmp.icmplim 50 salt '*' sysctl.persist coretemp_load NO config=/etc/sysctl.conf - ''' + """ nlines = [] edited = False value = six.text_type(value) @@ -156,38 +158,38 @@ def persist(name, value, config='/etc/sysctl.conf', apply_change=False): # If the sysctl.conf is not present, add it if not os.path.isfile(config): try: - with salt.utils.files.fopen(config, 'w+') as _fh: - _fh.write('#\n# Kernel sysctl configuration\n#\n') + with salt.utils.files.fopen(config, "w+") as _fh: + _fh.write("#\n# Kernel sysctl configuration\n#\n") except (IOError, OSError): - msg = 'Could not write to file: {0}' + msg = "Could not write to file: {0}" raise CommandExecutionError(msg.format(config)) - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) - if not line.startswith('{0}='.format(name)): + if not line.startswith("{0}=".format(name)): nlines.append(line) continue else: - key, rest = line.split('=', 1) + key, rest = line.split("=", 1) if rest.startswith('"'): _, rest_v, rest = rest.split('"', 2) - elif rest.startswith('\''): - _, rest_v, rest = rest.split('\'', 2) + elif rest.startswith("'"): + _, rest_v, rest = rest.split("'", 2) else: rest_v = rest.split()[0] - rest = rest[len(rest_v):] + rest = rest[len(rest_v) :] if rest_v == value: - return 'Already set' - nlines.append('{0}={1}\n'.format(name, value)) + return "Already set" + nlines.append("{0}={1}\n".format(name, value)) edited = True if not edited: - nlines.append('{0}={1}\n'.format(name, value)) + nlines.append("{0}={1}\n".format(name, value)) nlines = [salt.utils.stringutils.to_str(_l) for _l in nlines] - with salt.utils.files.fopen(config, 'w+') as ofile: + with salt.utils.files.fopen(config, "w+") as ofile: ofile.writelines(nlines) # If apply_change=True, apply edits to system if apply_change is True: assign(name, value) - return 'Updated and applied' - return 'Updated' + return "Updated and applied" + return "Updated" diff --git a/salt/modules/mac_system.py b/salt/modules/mac_system.py index ab8a4ccd66f..dd8182662e0 100644 --- a/salt/modules/mac_system.py +++ b/salt/modules/mac_system.py @@ -1,13 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" System module for sleeping, restarting, and shutting down the system on Mac OS X .. versionadded:: 2016.3.0 .. warning:: Using this module will enable ``atrun`` on the system if it is disabled. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import getpass + +# Import salt libs +import salt.utils.platform +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import python libs try: # python 3 @@ -15,58 +21,56 @@ try: # python 3 except ImportError: # python 2 from pipes import quote as _cmd_quote -import getpass -# Import salt libs -import salt.utils.platform -from salt.exceptions import SaltInvocationError, CommandExecutionError - -__virtualname__ = 'system' +__virtualname__ = "system" def __virtual__(): - ''' + """ Only for MacOS with atrun enabled - ''' + """ if not salt.utils.platform.is_darwin(): - return (False, 'The mac_system module could not be loaded: ' - 'module only works on MacOS systems.') + return ( + False, + "The mac_system module could not be loaded: " + "module only works on MacOS systems.", + ) - if getpass.getuser() != 'root': - return False, 'The mac_system module is not useful for non-root users.' + if getpass.getuser() != "root": + return False, "The mac_system module is not useful for non-root users." if not _atrun_enabled(): if not _enable_atrun(): - return False, 'atrun could not be enabled on this system' + return False, "atrun could not be enabled on this system" return __virtualname__ def _atrun_enabled(): - ''' + """ Check to see if atrun is running and enabled on the system - ''' + """ try: - return __salt__['service.list']('com.apple.atrun') + return __salt__["service.list"]("com.apple.atrun") except CommandExecutionError: return False def _enable_atrun(): - ''' + """ Enable and start the atrun daemon - ''' - name = 'com.apple.atrun' + """ + name = "com.apple.atrun" try: - __salt__['service.enable'](name) - __salt__['service.start'](name) + __salt__["service.enable"](name) + __salt__["service.start"](name) except CommandExecutionError: return False return _atrun_enabled() def _execute_command(cmd, at_time=None): - ''' + """ Helper function to execute the command :param str cmd: the command to run @@ -74,14 +78,14 @@ def _execute_command(cmd, at_time=None): :param str at_time: If passed, the cmd will be scheduled. Returns: bool - ''' + """ if at_time: - cmd = 'echo \'{0}\' | at {1}'.format(cmd, _cmd_quote(at_time)) - return not bool(__salt__['cmd.retcode'](cmd, python_shell=True)) + cmd = "echo '{0}' | at {1}".format(cmd, _cmd_quote(at_time)) + return not bool(__salt__["cmd.retcode"](cmd, python_shell=True)) def halt(at_time=None): - ''' + """ Halt a running system :param str at_time: Any valid `at` expression. For example, some valid at @@ -104,13 +108,13 @@ def halt(at_time=None): salt '*' system.halt salt '*' system.halt 'now + 10 minutes' - ''' - cmd = 'shutdown -h now' + """ + cmd = "shutdown -h now" return _execute_command(cmd, at_time) def sleep(at_time=None): - ''' + """ Sleep the system. If a user is active on the system it will likely fail to sleep. @@ -134,13 +138,13 @@ def sleep(at_time=None): salt '*' system.sleep salt '*' system.sleep '10:00 PM' - ''' - cmd = 'shutdown -s now' + """ + cmd = "shutdown -s now" return _execute_command(cmd, at_time) def restart(at_time=None): - ''' + """ Restart the system :param str at_time: Any valid `at` expression. For example, some valid at @@ -163,13 +167,13 @@ def restart(at_time=None): salt '*' system.restart salt '*' system.restart '12:00 PM fri' - ''' - cmd = 'shutdown -r now' + """ + cmd = "shutdown -r now" return _execute_command(cmd, at_time) def shutdown(at_time=None): - ''' + """ Shutdown the system :param str at_time: Any valid `at` expression. For example, some valid at @@ -192,12 +196,12 @@ def shutdown(at_time=None): salt '*' system.shutdown salt '*' system.shutdown 'now + 1 hour' - ''' + """ return halt(at_time) def get_remote_login(): - ''' + """ Displays whether remote login (SSH) is on or off. :return: True if remote login is on, False if off @@ -208,18 +212,18 @@ def get_remote_login(): .. code-block:: bash salt '*' system.get_remote_login - ''' - ret = __utils__['mac_utils.execute_return_result']( - 'systemsetup -getremotelogin') + """ + ret = __utils__["mac_utils.execute_return_result"]("systemsetup -getremotelogin") - enabled = __utils__['mac_utils.validate_enabled']( - __utils__['mac_utils.parse_return'](ret)) + enabled = __utils__["mac_utils.validate_enabled"]( + __utils__["mac_utils.parse_return"](ret) + ) - return enabled == 'on' + return enabled == "on" def set_remote_login(enable): - ''' + """ Set the remote login (SSH) to either on or off. :param bool enable: True to enable, False to disable. "On" and "Off" are @@ -234,19 +238,19 @@ def set_remote_login(enable): .. code-block:: bash salt '*' system.set_remote_login True - ''' - state = __utils__['mac_utils.validate_enabled'](enable) + """ + state = __utils__["mac_utils.validate_enabled"](enable) - cmd = 'systemsetup -f -setremotelogin {0}'.format(state) - __utils__['mac_utils.execute_return_success'](cmd) + cmd = "systemsetup -f -setremotelogin {0}".format(state) + __utils__["mac_utils.execute_return_success"](cmd) - return __utils__['mac_utils.confirm_updated'](state, - get_remote_login, - normalize_ret=True) + return __utils__["mac_utils.confirm_updated"]( + state, get_remote_login, normalize_ret=True + ) def get_remote_events(): - ''' + """ Displays whether remote apple events are on or off. :return: True if remote apple events are on, False if off @@ -257,18 +261,20 @@ def get_remote_events(): .. code-block:: bash salt '*' system.get_remote_events - ''' - ret = __utils__['mac_utils.execute_return_result']( - 'systemsetup -getremoteappleevents') + """ + ret = __utils__["mac_utils.execute_return_result"]( + "systemsetup -getremoteappleevents" + ) - enabled = __utils__['mac_utils.validate_enabled']( - __utils__['mac_utils.parse_return'](ret)) + enabled = __utils__["mac_utils.validate_enabled"]( + __utils__["mac_utils.parse_return"](ret) + ) - return enabled == 'on' + return enabled == "on" def set_remote_events(enable): - ''' + """ Set whether the server responds to events sent by other computers (such as AppleScripts) @@ -284,21 +290,19 @@ def set_remote_events(enable): .. code-block:: bash salt '*' system.set_remote_events On - ''' - state = __utils__['mac_utils.validate_enabled'](enable) + """ + state = __utils__["mac_utils.validate_enabled"](enable) - cmd = 'systemsetup -setremoteappleevents {0}'.format(state) - __utils__['mac_utils.execute_return_success'](cmd) + cmd = "systemsetup -setremoteappleevents {0}".format(state) + __utils__["mac_utils.execute_return_success"](cmd) - return __utils__['mac_utils.confirm_updated']( - state, - get_remote_events, - normalize_ret=True, + return __utils__["mac_utils.confirm_updated"]( + state, get_remote_events, normalize_ret=True, ) def get_computer_name(): - ''' + """ Gets the computer name :return: The computer name @@ -309,15 +313,14 @@ def get_computer_name(): .. code-block:: bash salt '*' system.get_computer_name - ''' - ret = __utils__['mac_utils.execute_return_result']( - 'scutil --get ComputerName') + """ + ret = __utils__["mac_utils.execute_return_result"]("scutil --get ComputerName") - return __utils__['mac_utils.parse_return'](ret) + return __utils__["mac_utils.parse_return"](ret) def set_computer_name(name): - ''' + """ Set the computer name :param str name: The new computer name @@ -330,18 +333,15 @@ def set_computer_name(name): .. code-block:: bash salt '*' system.set_computer_name "Mike's Mac" - ''' + """ cmd = 'scutil --set ComputerName "{0}"'.format(name) - __utils__['mac_utils.execute_return_success'](cmd) + __utils__["mac_utils.execute_return_success"](cmd) - return __utils__['mac_utils.confirm_updated']( - name, - get_computer_name, - ) + return __utils__["mac_utils.confirm_updated"](name, get_computer_name,) def get_subnet_name(): - ''' + """ Gets the local subnet name :return: The local subnet name @@ -352,15 +352,16 @@ def get_subnet_name(): .. code-block:: bash salt '*' system.get_subnet_name - ''' - ret = __utils__['mac_utils.execute_return_result']( - 'systemsetup -getlocalsubnetname') + """ + ret = __utils__["mac_utils.execute_return_result"]( + "systemsetup -getlocalsubnetname" + ) - return __utils__['mac_utils.parse_return'](ret) + return __utils__["mac_utils.parse_return"](ret) def set_subnet_name(name): - ''' + """ Set the local subnet name :param str name: The new local subnet name @@ -377,18 +378,15 @@ def set_subnet_name(name): The following will be set as 'Mikes-Mac' salt '*' system.set_subnet_name "Mike's Mac" - ''' + """ cmd = 'systemsetup -setlocalsubnetname "{0}"'.format(name) - __utils__['mac_utils.execute_return_success'](cmd) + __utils__["mac_utils.execute_return_success"](cmd) - return __utils__['mac_utils.confirm_updated']( - name, - get_subnet_name, - ) + return __utils__["mac_utils.confirm_updated"](name, get_subnet_name,) def get_startup_disk(): - ''' + """ Displays the current startup disk :return: The current startup disk @@ -399,15 +397,14 @@ def get_startup_disk(): .. code-block:: bash salt '*' system.get_startup_disk - ''' - ret = __utils__['mac_utils.execute_return_result']( - 'systemsetup -getstartupdisk') + """ + ret = __utils__["mac_utils.execute_return_result"]("systemsetup -getstartupdisk") - return __utils__['mac_utils.parse_return'](ret) + return __utils__["mac_utils.parse_return"](ret) def list_startup_disks(): - ''' + """ List all valid startup disks on the system. :return: A list of valid startup disks @@ -418,15 +415,14 @@ def list_startup_disks(): .. code-block:: bash salt '*' system.list_startup_disks - ''' - ret = __utils__['mac_utils.execute_return_result']( - 'systemsetup -liststartupdisks') + """ + ret = __utils__["mac_utils.execute_return_result"]("systemsetup -liststartupdisks") return ret.splitlines() def set_startup_disk(path): - ''' + """ Set the current startup disk to the indicated path. Use ``system.list_startup_disks`` to find valid startup disks on the system. @@ -440,25 +436,24 @@ def set_startup_disk(path): .. code-block:: bash salt '*' system.set_startup_disk /System/Library/CoreServices - ''' + """ if path not in list_startup_disks(): - msg = 'Invalid value passed for path.\n' \ - 'Must be a valid startup disk as found in ' \ - 'system.list_startup_disks.\n' \ - 'Passed: {0}'.format(path) + msg = ( + "Invalid value passed for path.\n" + "Must be a valid startup disk as found in " + "system.list_startup_disks.\n" + "Passed: {0}".format(path) + ) raise SaltInvocationError(msg) - cmd = 'systemsetup -setstartupdisk {0}'.format(path) - __utils__['mac_utils.execute_return_result'](cmd) + cmd = "systemsetup -setstartupdisk {0}".format(path) + __utils__["mac_utils.execute_return_result"](cmd) - return __utils__['mac_utils.confirm_updated']( - path, - get_startup_disk, - ) + return __utils__["mac_utils.confirm_updated"](path, get_startup_disk,) def get_restart_delay(): - ''' + """ Get the number of seconds after which the computer will start up after a power failure. @@ -471,15 +466,16 @@ def get_restart_delay(): .. code-block:: bash salt '*' system.get_restart_delay - ''' - ret = __utils__['mac_utils.execute_return_result']( - 'systemsetup -getwaitforstartupafterpowerfailure') + """ + ret = __utils__["mac_utils.execute_return_result"]( + "systemsetup -getwaitforstartupafterpowerfailure" + ) - return __utils__['mac_utils.parse_return'](ret) + return __utils__["mac_utils.parse_return"](ret) def set_restart_delay(seconds): - ''' + """ Set the number of seconds after which the computer will start up after a power failure. @@ -505,24 +501,23 @@ def set_restart_delay(seconds): .. code-block:: bash salt '*' system.set_restart_delay 180 - ''' + """ if seconds % 30 != 0: - msg = 'Invalid value passed for seconds.\n' \ - 'Must be a multiple of 30.\n' \ - 'Passed: {0}'.format(seconds) + msg = ( + "Invalid value passed for seconds.\n" + "Must be a multiple of 30.\n" + "Passed: {0}".format(seconds) + ) raise SaltInvocationError(msg) - cmd = 'systemsetup -setwaitforstartupafterpowerfailure {0}'.format(seconds) - __utils__['mac_utils.execute_return_success'](cmd) + cmd = "systemsetup -setwaitforstartupafterpowerfailure {0}".format(seconds) + __utils__["mac_utils.execute_return_success"](cmd) - return __utils__['mac_utils.confirm_updated']( - seconds, - get_restart_delay, - ) + return __utils__["mac_utils.confirm_updated"](seconds, get_restart_delay,) def get_disable_keyboard_on_lock(): - ''' + """ Get whether or not the keyboard should be disabled when the X Serve enclosure lock is engaged. @@ -534,18 +529,20 @@ def get_disable_keyboard_on_lock(): ..code-block:: bash salt '*' system.get_disable_keyboard_on_lock - ''' - ret = __utils__['mac_utils.execute_return_result']( - 'systemsetup -getdisablekeyboardwhenenclosurelockisengaged') + """ + ret = __utils__["mac_utils.execute_return_result"]( + "systemsetup -getdisablekeyboardwhenenclosurelockisengaged" + ) - enabled = __utils__['mac_utils.validate_enabled']( - __utils__['mac_utils.parse_return'](ret)) + enabled = __utils__["mac_utils.validate_enabled"]( + __utils__["mac_utils.parse_return"](ret) + ) - return enabled == 'on' + return enabled == "on" def set_disable_keyboard_on_lock(enable): - ''' + """ Get whether or not the keyboard should be disabled when the X Serve enclosure lock is engaged. @@ -561,22 +558,21 @@ def set_disable_keyboard_on_lock(enable): .. code-block:: bash salt '*' system.set_disable_keyboard_on_lock False - ''' - state = __utils__['mac_utils.validate_enabled'](enable) + """ + state = __utils__["mac_utils.validate_enabled"](enable) - cmd = 'systemsetup -setdisablekeyboardwhenenclosurelockisengaged ' \ - '{0}'.format(state) - __utils__['mac_utils.execute_return_success'](cmd) + cmd = "systemsetup -setdisablekeyboardwhenenclosurelockisengaged " "{0}".format( + state + ) + __utils__["mac_utils.execute_return_success"](cmd) - return __utils__['mac_utils.confirm_updated']( - state, - get_disable_keyboard_on_lock, - normalize_ret=True, + return __utils__["mac_utils.confirm_updated"]( + state, get_disable_keyboard_on_lock, normalize_ret=True, ) def get_boot_arch(): - ''' + """ Get the kernel architecture setting from ``com.apple.Boot.plist`` :return: A string value representing the boot architecture setting @@ -587,24 +583,25 @@ def get_boot_arch(): .. code-block:: bash salt '*' system.get_boot_arch - ''' - ret = __utils__['mac_utils.execute_return_result']( - 'systemsetup -getkernelbootarchitecturesetting') + """ + ret = __utils__["mac_utils.execute_return_result"]( + "systemsetup -getkernelbootarchitecturesetting" + ) - arch = __utils__['mac_utils.parse_return'](ret) + arch = __utils__["mac_utils.parse_return"](ret) - if 'default' in arch: - return 'default' - elif 'i386' in arch: - return 'i386' - elif 'x86_64' in arch: - return 'x86_64' + if "default" in arch: + return "default" + elif "i386" in arch: + return "i386" + elif "x86_64" in arch: + return "x86_64" - return 'unknown' + return "unknown" -def set_boot_arch(arch='default'): - ''' +def set_boot_arch(arch="default"): + """ Set the kernel to boot in 32 or 64 bit mode on next boot. .. note:: @@ -628,17 +625,16 @@ def set_boot_arch(arch='default'): .. code-block:: bash salt '*' system.set_boot_arch i386 - ''' - if arch not in ['i386', 'x86_64', 'default']: - msg = 'Invalid value passed for arch.\n' \ - 'Must be i386, x86_64, or default.\n' \ - 'Passed: {0}'.format(arch) + """ + if arch not in ["i386", "x86_64", "default"]: + msg = ( + "Invalid value passed for arch.\n" + "Must be i386, x86_64, or default.\n" + "Passed: {0}".format(arch) + ) raise SaltInvocationError(msg) - cmd = 'systemsetup -setkernelbootarchitecture {0}'.format(arch) - __utils__['mac_utils.execute_return_success'](cmd) + cmd = "systemsetup -setkernelbootarchitecture {0}".format(arch) + __utils__["mac_utils.execute_return_success"](cmd) - return __utils__['mac_utils.confirm_updated']( - arch, - get_boot_arch, - ) + return __utils__["mac_utils.confirm_updated"](arch, get_boot_arch,) diff --git a/salt/modules/mac_timezone.py b/salt/modules/mac_timezone.py index 6e4a18bffd6..def7ecc5dc3 100644 --- a/salt/modules/mac_timezone.py +++ b/salt/modules/mac_timezone.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Module for editing date/time settings on macOS .. versionadded:: 2016.3.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs from datetime import datetime @@ -14,22 +14,25 @@ import salt.utils.mac_utils import salt.utils.platform from salt.exceptions import SaltInvocationError -__virtualname__ = 'timezone' +__virtualname__ = "timezone" def __virtual__(): - ''' + """ Only for macOS - ''' + """ if not salt.utils.platform.is_darwin(): - return (False, 'The mac_timezone module could not be loaded: ' - 'module only works on macOS systems.') + return ( + False, + "The mac_timezone module could not be loaded: " + "module only works on macOS systems.", + ) return __virtualname__ def _get_date_time_format(dt_string): - ''' + """ Function that detects the date/time format for the string passed. :param str dt_string: @@ -39,14 +42,14 @@ def _get_date_time_format(dt_string): :rtype: str :raises: SaltInvocationError on Invalid Date/Time string - ''' + """ valid_formats = [ - '%H:%M', - '%H:%M:%S', - '%m:%d:%y', - '%m:%d:%Y', - '%m/%d/%y', - '%m/%d/%Y' + "%H:%M", + "%H:%M:%S", + "%m:%d:%y", + "%m:%d:%Y", + "%m/%d/%y", + "%m/%d/%Y", ] for dt_format in valid_formats: try: @@ -54,12 +57,12 @@ def _get_date_time_format(dt_string): return dt_format except ValueError: continue - msg = 'Invalid Date/Time Format: {0}'.format(dt_string) + msg = "Invalid Date/Time Format: {0}".format(dt_string) raise SaltInvocationError(msg) def get_date(): - ''' + """ Displays the current date :return: the system date @@ -70,13 +73,13 @@ def get_date(): .. code-block:: bash salt '*' timezone.get_date - ''' - ret = salt.utils.mac_utils.execute_return_result('systemsetup -getdate') + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -getdate") return salt.utils.mac_utils.parse_return(ret) def set_date(date): - ''' + """ Set the current month, day, and year :param str date: The date to set. Valid date formats are: @@ -97,16 +100,16 @@ def set_date(date): .. code-block:: bash salt '*' timezone.set_date 1/13/2016 - ''' + """ date_format = _get_date_time_format(date) dt_obj = datetime.strptime(date, date_format) - cmd = 'systemsetup -setdate {0}'.format(dt_obj.strftime('%m:%d:%Y')) + cmd = "systemsetup -setdate {0}".format(dt_obj.strftime("%m:%d:%Y")) return salt.utils.mac_utils.execute_return_success(cmd) def get_time(): - ''' + """ Get the current system time. :return: The current time in 24 hour format @@ -117,13 +120,13 @@ def get_time(): .. code-block:: bash salt '*' timezone.get_time - ''' - ret = salt.utils.mac_utils.execute_return_result('systemsetup -gettime') + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -gettime") return salt.utils.mac_utils.parse_return(ret) def set_time(time): - ''' + """ Sets the current time. Must be in 24 hour format. :param str time: The time to set in 24 hour format. The value must be @@ -140,17 +143,17 @@ def set_time(time): .. code-block:: bash salt '*' timezone.set_time '"17:34"' - ''' + """ # time must be double quoted '"17:46"' time_format = _get_date_time_format(time) dt_obj = datetime.strptime(time, time_format) - cmd = 'systemsetup -settime {0}'.format(dt_obj.strftime('%H:%M:%S')) + cmd = "systemsetup -settime {0}".format(dt_obj.strftime("%H:%M:%S")) return salt.utils.mac_utils.execute_return_success(cmd) def get_zone(): - ''' + """ Displays the current time zone :return: The current time zone @@ -161,13 +164,13 @@ def get_zone(): .. code-block:: bash salt '*' timezone.get_zone - ''' - ret = salt.utils.mac_utils.execute_return_result('systemsetup -gettimezone') + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -gettimezone") return salt.utils.mac_utils.parse_return(ret) def get_zonecode(): - ''' + """ Displays the current time zone abbreviated code :return: The current time zone code @@ -178,12 +181,12 @@ def get_zonecode(): .. code-block:: bash salt '*' timezone.get_zonecode - ''' - return salt.utils.mac_utils.execute_return_result('date +%Z') + """ + return salt.utils.mac_utils.execute_return_result("date +%Z") def get_offset(): - ''' + """ Displays the current time zone offset :return: The current time zone offset @@ -194,12 +197,12 @@ def get_offset(): .. code-block:: bash salt '*' timezone.get_offset - ''' - return salt.utils.mac_utils.execute_return_result('date +%z') + """ + return salt.utils.mac_utils.execute_return_result("date +%z") def list_zones(): - ''' + """ Displays a list of available time zones. Use this list when setting a time zone using ``timezone.set_zone`` @@ -211,16 +214,15 @@ def list_zones(): .. code-block:: bash salt '*' timezone.list_zones - ''' - ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -listtimezones') + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -listtimezones") zones = salt.utils.mac_utils.parse_return(ret) return [x.strip() for x in zones.splitlines()] def set_zone(time_zone): - ''' + """ Set the local time zone. Use ``timezone.list_zones`` to list valid time_zone arguments @@ -237,18 +239,19 @@ def set_zone(time_zone): .. code-block:: bash salt '*' timezone.set_zone America/Denver - ''' + """ if time_zone not in list_zones(): - raise SaltInvocationError('Invalid Timezone: {0}'.format(time_zone)) + raise SaltInvocationError("Invalid Timezone: {0}".format(time_zone)) salt.utils.mac_utils.execute_return_success( - 'systemsetup -settimezone {0}'.format(time_zone)) + "systemsetup -settimezone {0}".format(time_zone) + ) return time_zone in get_zone() def zone_compare(time_zone): - ''' + """ Compares the given timezone name with the system timezone name. :return: True if they are the same, False if not @@ -259,12 +262,12 @@ def zone_compare(time_zone): .. code-block:: bash salt '*' timezone.zone_compare America/Boise - ''' + """ return time_zone == get_zone() def get_using_network_time(): - ''' + """ Display whether network time is on or off :return: True if network time is on, False if off @@ -275,16 +278,17 @@ def get_using_network_time(): .. code-block:: bash salt '*' timezone.get_using_network_time - ''' - ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getusingnetworktime') + """ + ret = salt.utils.mac_utils.execute_return_result("systemsetup -getusingnetworktime") - return salt.utils.mac_utils.validate_enabled( - salt.utils.mac_utils.parse_return(ret)) == 'on' + return ( + salt.utils.mac_utils.validate_enabled(salt.utils.mac_utils.parse_return(ret)) + == "on" + ) def set_using_network_time(enable): - ''' + """ Set whether network time is on or off. :param enable: True to enable, False to disable. Can also use 'on' or 'off' @@ -300,18 +304,17 @@ def set_using_network_time(enable): .. code-block:: bash salt '*' timezone.set_using_network_time True - ''' + """ state = salt.utils.mac_utils.validate_enabled(enable) - cmd = 'systemsetup -setusingnetworktime {0}'.format(state) + cmd = "systemsetup -setusingnetworktime {0}".format(state) salt.utils.mac_utils.execute_return_success(cmd) - return state == salt.utils.mac_utils.validate_enabled( - get_using_network_time()) + return state == salt.utils.mac_utils.validate_enabled(get_using_network_time()) def get_time_server(): - ''' + """ Display the currently set network time server. :return: the network time server @@ -322,14 +325,15 @@ def get_time_server(): .. code-block:: bash salt '*' timezone.get_time_server - ''' + """ ret = salt.utils.mac_utils.execute_return_result( - 'systemsetup -getnetworktimeserver') + "systemsetup -getnetworktimeserver" + ) return salt.utils.mac_utils.parse_return(ret) -def set_time_server(time_server='time.apple.com'): - ''' +def set_time_server(time_server="time.apple.com"): + """ Designates a network time server. Enter the IP address or DNS name for the network time server. @@ -348,15 +352,15 @@ def set_time_server(time_server='time.apple.com'): .. code-block:: bash salt '*' timezone.set_time_server time.acme.com - ''' - cmd = 'systemsetup -setnetworktimeserver {0}'.format(time_server) + """ + cmd = "systemsetup -setnetworktimeserver {0}".format(time_server) salt.utils.mac_utils.execute_return_success(cmd) return time_server in get_time_server() def get_hwclock(): - ''' + """ Get current hardware clock setting (UTC or localtime) CLI Example: @@ -364,13 +368,13 @@ def get_hwclock(): .. code-block:: bash salt '*' timezone.get_hwclock - ''' + """ # Need to search for a way to figure it out ... return False def set_hwclock(clock): - ''' + """ Sets the hardware clock to be either UTC or localtime CLI Example: @@ -378,6 +382,6 @@ def set_hwclock(clock): .. code-block:: bash salt '*' timezone.set_hwclock UTC - ''' + """ # Need to search for a way to figure it out ... return False diff --git a/salt/modules/mac_user.py b/salt/modules/mac_user.py index 1a81dcb5265..9d1ebdb7ff1 100644 --- a/salt/modules/mac_user.py +++ b/salt/modules/mac_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage users on Mac OS 10.7+ .. important:: @@ -7,21 +7,14 @@ Manage users on Mac OS 10.7+ minion, and it is using a different module (or gives an error similar to *'user.info' is not available*), see :ref:`here `. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -try: - import pwd -except ImportError: - pass +from __future__ import absolute_import, print_function, unicode_literals + import logging import time -# Import 3rdp-party libs -from salt.ext.six.moves import range, map # pylint: disable=import-error,redefined-builtin -from salt.ext.six import string_types - # Import salt libs import salt.utils.args import salt.utils.data @@ -31,43 +24,52 @@ import salt.utils.stringutils import salt.utils.user from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six +from salt.ext.six import string_types + +# Import 3rdp-party libs +from salt.ext.six.moves import map, range + +try: + import pwd +except ImportError: + pass + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'user' +__virtualname__ = "user" def __virtual__(): - if (__grains__.get('kernel') != 'Darwin' or - __grains__['osrelease_info'] < (10, 7)): - return False + if __grains__.get("kernel") != "Darwin" or __grains__["osrelease_info"] < (10, 7): + return (False, "Only available on Mac OS 10.7+ systems") else: return __virtualname__ def _flush_dscl_cache(): - ''' + """ Flush dscl cache - ''' - __salt__['cmd.run'](['dscacheutil', '-flushcache'], python_shell=False) + """ + __salt__["cmd.run"](["dscacheutil", "-flushcache"], python_shell=False) -def _dscl(cmd, ctype='create'): - ''' +def _dscl(cmd, ctype="create"): + """ Run a dscl -create command - ''' - if __grains__['osrelease_info'] < (10, 8): - source, noderoot = '.', '' + """ + if __grains__["osrelease_info"] < (10, 8): + source, noderoot = ".", "" else: - source, noderoot = 'localhost', '/Local/Default' + source, noderoot = "localhost", "/Local/Default" if noderoot: cmd[0] = noderoot + cmd[0] - return __salt__['cmd.run_all']( - ['dscl', source, '-' + ctype] + cmd, - output_loglevel='quiet' if ctype == 'passwd' else 'debug', - python_shell=False + return __salt__["cmd.run_all"]( + ["dscl", source, "-" + ctype] + cmd, + output_loglevel="quiet" if ctype == "passwd" else "debug", + python_shell=False, ) @@ -78,16 +80,18 @@ def _first_avail_uid(): return idx -def add(name, - uid=None, - gid=None, - groups=None, - home=None, - shell=None, - fullname=None, - createhome=True, - **kwargs): - ''' +def add( + name, + uid=None, + gid=None, + groups=None, + home=None, + shell=None, + fullname=None, + createhome=True, + **kwargs +): + """ Add a user to the minion CLI Example: @@ -95,39 +99,39 @@ def add(name, .. code-block:: bash salt '*' user.add name - ''' + """ if info(name): - raise CommandExecutionError('User \'{0}\' already exists'.format(name)) + raise CommandExecutionError("User '{0}' already exists".format(name)) if salt.utils.stringutils.contains_whitespace(name): - raise SaltInvocationError('Username cannot contain whitespace') + raise SaltInvocationError("Username cannot contain whitespace") if uid is None: uid = _first_avail_uid() if gid is None: gid = 20 # gid 20 == 'staff', the default group if home is None: - home = '/Users/{0}'.format(name) + home = "/Users/{0}".format(name) if shell is None: - shell = '/bin/bash' + shell = "/bin/bash" if fullname is None: - fullname = '' + fullname = "" if not isinstance(uid, int): - raise SaltInvocationError('uid must be an integer') + raise SaltInvocationError("uid must be an integer") if not isinstance(gid, int): - raise SaltInvocationError('gid must be an integer') + raise SaltInvocationError("gid must be an integer") - name_path = '/Users/{0}'.format(name) - _dscl([name_path, 'UniqueID', uid]) - _dscl([name_path, 'PrimaryGroupID', gid]) - _dscl([name_path, 'UserShell', shell]) - _dscl([name_path, 'NFSHomeDirectory', home]) - _dscl([name_path, 'RealName', fullname]) + name_path = "/Users/{0}".format(name) + _dscl([name_path, "UniqueID", uid]) + _dscl([name_path, "PrimaryGroupID", gid]) + _dscl([name_path, "UserShell", shell]) + _dscl([name_path, "NFSHomeDirectory", home]) + _dscl([name_path, "RealName", fullname]) # Make sure home directory exists if createhome: - __salt__['file.mkdir'](home, user=uid, group=gid) + __salt__["file.mkdir"](home, user=uid, group=gid) # dscl buffers changes, sleep before setting group membership time.sleep(1) @@ -137,7 +141,7 @@ def add(name, def delete(name, remove=False, force=False): - ''' + """ Remove a user from the minion CLI Example: @@ -145,29 +149,29 @@ def delete(name, remove=False, force=False): .. code-block:: bash salt '*' user.delete name remove=True force=True - ''' + """ if salt.utils.stringutils.contains_whitespace(name): - raise SaltInvocationError('Username cannot contain whitespace') + raise SaltInvocationError("Username cannot contain whitespace") if not info(name): return True # force is added for compatibility with user.absent state function if force: - log.warning('force option is unsupported on MacOS, ignoring') + log.warning("force option is unsupported on MacOS, ignoring") # remove home directory from filesystem if remove: - __salt__['file.remove'](info(name)['home']) + __salt__["file.remove"](info(name)["home"]) # Remove from any groups other than primary group. Needs to be done since # group membership is managed separately from users and an entry for the # user will persist even after the user is removed. chgroups(name, ()) - return _dscl(['/Users/{0}'.format(name)], ctype='delete')['retcode'] == 0 + return _dscl(["/Users/{0}".format(name)], ctype="delete")["retcode"] == 0 def getent(refresh=False): - ''' + """ Return the list of all info for all users CLI Example: @@ -175,19 +179,19 @@ def getent(refresh=False): .. code-block:: bash salt '*' user.getent - ''' - if 'user.getent' in __context__ and not refresh: - return __context__['user.getent'] + """ + if "user.getent" in __context__ and not refresh: + return __context__["user.getent"] ret = [] for data in pwd.getpwall(): ret.append(_format_info(data)) - __context__['user.getent'] = ret + __context__["user.getent"] = ret return ret def chuid(name, uid): - ''' + """ Change the uid for a named user CLI Example: @@ -195,26 +199,23 @@ def chuid(name, uid): .. code-block:: bash salt '*' user.chuid foo 4376 - ''' + """ if not isinstance(uid, int): - raise SaltInvocationError('uid must be an integer') + raise SaltInvocationError("uid must be an integer") pre_info = info(name) if not pre_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) - if uid == pre_info['uid']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if uid == pre_info["uid"]: return True - _dscl( - ['/Users/{0}'.format(name), 'UniqueID', pre_info['uid'], uid], - ctype='change' - ) + _dscl(["/Users/{0}".format(name), "UniqueID", pre_info["uid"], uid], ctype="change") # dscl buffers changes, sleep 1 second before checking if new value # matches desired value time.sleep(1) - return info(name).get('uid') == uid + return info(name).get("uid") == uid def chgid(name, gid): - ''' + """ Change the default group of the user CLI Example: @@ -222,26 +223,26 @@ def chgid(name, gid): .. code-block:: bash salt '*' user.chgid foo 4376 - ''' + """ if not isinstance(gid, int): - raise SaltInvocationError('gid must be an integer') + raise SaltInvocationError("gid must be an integer") pre_info = info(name) if not pre_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) - if gid == pre_info['gid']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if gid == pre_info["gid"]: return True _dscl( - ['/Users/{0}'.format(name), 'PrimaryGroupID', pre_info['gid'], gid], - ctype='change' + ["/Users/{0}".format(name), "PrimaryGroupID", pre_info["gid"], gid], + ctype="change", ) # dscl buffers changes, sleep 1 second before checking if new value # matches desired value time.sleep(1) - return info(name).get('gid') == gid + return info(name).get("gid") == gid def chshell(name, shell): - ''' + """ Change the default shell of the user CLI Example: @@ -249,24 +250,24 @@ def chshell(name, shell): .. code-block:: bash salt '*' user.chshell foo /bin/zsh - ''' + """ pre_info = info(name) if not pre_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) - if shell == pre_info['shell']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if shell == pre_info["shell"]: return True _dscl( - ['/Users/{0}'.format(name), 'UserShell', pre_info['shell'], shell], - ctype='change' + ["/Users/{0}".format(name), "UserShell", pre_info["shell"], shell], + ctype="change", ) # dscl buffers changes, sleep 1 second before checking if new value # matches desired value time.sleep(1) - return info(name).get('shell') == shell + return info(name).get("shell") == shell def chhome(name, home, **kwargs): - ''' + """ Change the home directory of the user CLI Example: @@ -274,32 +275,31 @@ def chhome(name, home, **kwargs): .. code-block:: bash salt '*' user.chhome foo /Users/foo - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - persist = kwargs.pop('persist', False) + persist = kwargs.pop("persist", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if persist: - log.info('Ignoring unsupported \'persist\' argument to user.chhome') + log.info("Ignoring unsupported 'persist' argument to user.chhome") pre_info = info(name) if not pre_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) - if home == pre_info['home']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if home == pre_info["home"]: return True _dscl( - ['/Users/{0}'.format(name), 'NFSHomeDirectory', - pre_info['home'], home], - ctype='change' + ["/Users/{0}".format(name), "NFSHomeDirectory", pre_info["home"], home], + ctype="change", ) # dscl buffers changes, sleep 1 second before checking if new value # matches desired value time.sleep(1) - return info(name).get('home') == home + return info(name).get("home") == home def chfullname(name, fullname): - ''' + """ Change the user's Full Name CLI Example: @@ -307,31 +307,31 @@ def chfullname(name, fullname): .. code-block:: bash salt '*' user.chfullname foo 'Foo Bar' - ''' + """ fullname = salt.utils.data.decode(fullname) pre_info = info(name) if not pre_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) - pre_info['fullname'] = salt.utils.data.decode(pre_info['fullname']) - if fullname == pre_info['fullname']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + pre_info["fullname"] = salt.utils.data.decode(pre_info["fullname"]) + if fullname == pre_info["fullname"]: return True _dscl( - ['/Users/{0}'.format(name), 'RealName', fullname], + ["/Users/{0}".format(name), "RealName", fullname], # use a 'create' command, because a 'change' command would fail if # current fullname is an empty string. The 'create' will just overwrite # this field. - ctype='create' + ctype="create", ) # dscl buffers changes, sleep 1 second before checking if new value # matches desired value time.sleep(1) - current = salt.utils.data.decode(info(name).get('fullname')) + current = salt.utils.data.decode(info(name).get("fullname")) return current == fullname def chgroups(name, groups, append=False): - ''' + """ Change the groups to which the user belongs. Note that the user's primary group does not have to be one of the groups passed, membership in the user's primary group is automatically assumed. @@ -349,46 +349,42 @@ def chgroups(name, groups, append=False): .. code-block:: bash salt '*' user.chgroups foo wheel,root - ''' + """ ### NOTE: **args isn't used here but needs to be included in this ### function for compatibility with the user.present state uinfo = info(name) if not uinfo: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("User '{0}' does not exist".format(name)) if isinstance(groups, string_types): - groups = groups.split(',') + groups = groups.split(",") bad_groups = [x for x in groups if salt.utils.stringutils.contains_whitespace(x)] if bad_groups: raise SaltInvocationError( - 'Invalid group name(s): {0}'.format(', '.join(bad_groups)) + "Invalid group name(s): {0}".format(", ".join(bad_groups)) ) ugrps = set(list_groups(name)) desired = set(six.text_type(x) for x in groups if bool(six.text_type(x))) - primary_group = __salt__['file.gid_to_group'](uinfo['gid']) + primary_group = __salt__["file.gid_to_group"](uinfo["gid"]) if primary_group: desired.add(primary_group) if ugrps == desired: return True # Add groups from which user is missing for group in desired - ugrps: - _dscl( - ['/Groups/{0}'.format(group), 'GroupMembership', name], - ctype='append' - ) + _dscl(["/Groups/{0}".format(group), "GroupMembership", name], ctype="append") if not append: # Remove from extra groups for group in ugrps - desired: _dscl( - ['/Groups/{0}'.format(group), 'GroupMembership', name], - ctype='delete' + ["/Groups/{0}".format(group), "GroupMembership", name], ctype="delete" ) time.sleep(1) return set(list_groups(name)) == desired def info(name): - ''' + """ Return user information CLI Example: @@ -396,7 +392,7 @@ def info(name): .. code-block:: bash salt '*' user.info root - ''' + """ try: data = pwd.getpwnam(name) except KeyError: @@ -406,21 +402,23 @@ def info(name): def _format_info(data): - ''' + """ Return user information in a pretty way - ''' - return {'gid': data.pw_gid, - 'groups': list_groups(data.pw_name), - 'home': data.pw_dir, - 'name': data.pw_name, - 'shell': data.pw_shell, - 'uid': data.pw_uid, - 'fullname': data.pw_gecos} + """ + return { + "gid": data.pw_gid, + "groups": list_groups(data.pw_name), + "home": data.pw_dir, + "name": data.pw_name, + "shell": data.pw_shell, + "uid": data.pw_uid, + "fullname": data.pw_gecos, + } -@salt.utils.decorators.path.which('id') +@salt.utils.decorators.path.which("id") def primary_group(name): - ''' + """ Return the primary group of the named user .. versionadded:: 2016.3.0 @@ -430,12 +428,12 @@ def primary_group(name): .. code-block:: bash salt '*' user.primary_group saltadmin - ''' - return __salt__['cmd.run'](['id', '-g', '-n', name]) + """ + return __salt__["cmd.run"](["id", "-g", "-n", name]) def list_groups(name): - ''' + """ Return a list of groups the named user belongs to. name @@ -451,13 +449,13 @@ def list_groups(name): .. code-block:: bash salt '*' user.list_groups foo - ''' + """ groups = [group for group in salt.utils.user.get_group_list(name)] return groups def list_users(): - ''' + """ Return a list of all users CLI Example: @@ -465,13 +463,13 @@ def list_users(): .. code-block:: bash salt '*' user.list_users - ''' - users = _dscl(['/users'], 'list')['stdout'] + """ + users = _dscl(["/users"], "list")["stdout"] return users.split() def rename(name, new_name): - ''' + """ Change the username for a named user CLI Example: @@ -479,27 +477,22 @@ def rename(name, new_name): .. code-block:: bash salt '*' user.rename name new_name - ''' + """ current_info = info(name) if not current_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("User '{0}' does not exist".format(name)) new_info = info(new_name) if new_info: - raise CommandExecutionError( - 'User \'{0}\' already exists'.format(new_name) - ) - _dscl( - ['/Users/{0}'.format(name), 'RecordName', name, new_name], - ctype='change' - ) + raise CommandExecutionError("User '{0}' already exists".format(new_name)) + _dscl(["/Users/{0}".format(name), "RecordName", name, new_name], ctype="change") # dscl buffers changes, sleep 1 second before checking if new value # matches desired value time.sleep(1) - return info(new_name).get('RecordName') == new_name + return info(new_name).get("RecordName") == new_name def get_auto_login(): - ''' + """ .. versionadded:: 2016.3.0 Gets the current setting for Auto Login @@ -512,17 +505,19 @@ def get_auto_login(): .. code-block:: bash salt '*' user.get_auto_login - ''' - cmd = ['defaults', - 'read', - '/Library/Preferences/com.apple.loginwindow.plist', - 'autoLoginUser'] - ret = __salt__['cmd.run_all'](cmd, ignore_retcode=True) - return False if ret['retcode'] else ret['stdout'] + """ + cmd = [ + "defaults", + "read", + "/Library/Preferences/com.apple.loginwindow.plist", + "autoLoginUser", + ] + ret = __salt__["cmd.run_all"](cmd, ignore_retcode=True) + return False if ret["retcode"] else ret["stdout"] def _kcpassword(password): - ''' + """ Internal function for obfuscating the password used for AutoLogin This is later written as the contents of the ``/etc/kcpassword`` file @@ -538,7 +533,7 @@ def _kcpassword(password): Returns: str: The obfuscated password - ''' + """ # The magic 11 bytes - these are just repeated # 0x7D 0x89 0x52 0x23 0xD2 0xBC 0xDD 0xEA 0xA3 0xB9 0x1F key = [125, 137, 82, 35, 210, 188, 221, 234, 163, 185, 31] @@ -561,18 +556,19 @@ def _kcpassword(password): # corresponding item in the key # The length of the password, or the length of the key, whichever is # smaller - for password_index in range(chunk_index, - min(chunk_index + len(key), len(password))): + for password_index in range( + chunk_index, min(chunk_index + len(key), len(password)) + ): password[password_index] = password[password_index] ^ key[key_index] key_index += 1 # Convert each byte back to a character password = list(map(chr, password)) - return b''.join(salt.utils.data.encode(password)) + return b"".join(salt.utils.data.encode(password)) def enable_auto_login(name, password): - ''' + """ .. versionadded:: 2016.3.0 Configures the machine to auto login with the specified user @@ -593,27 +589,29 @@ def enable_auto_login(name, password): .. code-block:: bash salt '*' user.enable_auto_login stevej - ''' + """ # Make the entry into the defaults file - cmd = ['defaults', - 'write', - '/Library/Preferences/com.apple.loginwindow.plist', - 'autoLoginUser', - name] - __salt__['cmd.run'](cmd) + cmd = [ + "defaults", + "write", + "/Library/Preferences/com.apple.loginwindow.plist", + "autoLoginUser", + name, + ] + __salt__["cmd.run"](cmd) current = get_auto_login() # Create/Update the kcpassword file with an obfuscated password o_password = _kcpassword(password=password) with salt.utils.files.set_umask(0o077): - with salt.utils.files.fopen('/etc/kcpassword', 'w' if six.PY2 else 'wb') as fd: + with salt.utils.files.fopen("/etc/kcpassword", "w" if six.PY2 else "wb") as fd: fd.write(o_password) return current if isinstance(current, bool) else current.lower() == name.lower() def disable_auto_login(): - ''' + """ .. versionadded:: 2016.3.0 Disables auto login on the machine @@ -626,15 +624,17 @@ def disable_auto_login(): .. code-block:: bash salt '*' user.disable_auto_login - ''' + """ # Remove the kcpassword file - cmd = 'rm -f /etc/kcpassword' - __salt__['cmd.run'](cmd) + cmd = "rm -f /etc/kcpassword" + __salt__["cmd.run"](cmd) # Remove the entry from the defaults file - cmd = ['defaults', - 'delete', - '/Library/Preferences/com.apple.loginwindow.plist', - 'autoLoginUser'] - __salt__['cmd.run'](cmd) + cmd = [ + "defaults", + "delete", + "/Library/Preferences/com.apple.loginwindow.plist", + "autoLoginUser", + ] + __salt__["cmd.run"](cmd) return True if not get_auto_login() else False diff --git a/salt/modules/mac_xattr.py b/salt/modules/mac_xattr.py index e5bd5d402d2..7bf3c5dd72a 100644 --- a/salt/modules/mac_xattr.py +++ b/salt/modules/mac_xattr.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" This module allows you to manage extended attributes on files or directories .. code-block:: bash salt '*' xattr.list /path/to/file -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python Libs import logging @@ -20,21 +20,21 @@ log = logging.getLogger(__name__) __virtualname__ = "xattr" __func_alias__ = { - 'list_': 'list', + "list_": "list", } def __virtual__(): - ''' + """ Only work on Mac OS - ''' - if __grains__['os'] in ['MacOS', 'Darwin']: + """ + if __grains__["os"] in ["MacOS", "Darwin"]: return __virtualname__ - return False + return (False, "Only available on Mac OS systems") def list_(path, **kwargs): - ''' + """ List all of the extended attributes on the given file/directory :param str path: The file(s) to get attributes from @@ -53,19 +53,19 @@ def list_(path, **kwargs): salt '*' xattr.list /path/to/file salt '*' xattr.list /path/to/file hex=True - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - hex_ = kwargs.pop('hex', False) + hex_ = kwargs.pop("hex", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) - cmd = ['xattr', path] + cmd = ["xattr", path] try: ret = salt.utils.mac_utils.execute_return_result(cmd) except CommandExecutionError as exc: - if 'No such file' in exc.strerror: - raise CommandExecutionError('File not found: {0}'.format(path)) - raise CommandExecutionError('Unknown Error: {0}'.format(exc.strerror)) + if "No such file" in exc.strerror: + raise CommandExecutionError("File not found: {0}".format(path)) + raise CommandExecutionError("Unknown Error: {0}".format(exc.strerror)) if not ret: return {} @@ -74,13 +74,13 @@ def list_(path, **kwargs): attrs = {} for id_ in attrs_ids: - attrs[id_] = read(path, id_, **{'hex': hex_}) + attrs[id_] = read(path, id_, **{"hex": hex_}) return attrs def read(path, attribute, **kwargs): - ''' + """ Read the given attributes on the given file/directory :param str path: The file to get attributes from @@ -101,31 +101,31 @@ def read(path, attribute, **kwargs): salt '*' xattr.read /path/to/file com.test.attr salt '*' xattr.read /path/to/file com.test.attr hex=True - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - hex_ = kwargs.pop('hex', False) + hex_ = kwargs.pop("hex", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) - cmd = ['xattr', '-p'] + cmd = ["xattr", "-p"] if hex_: - cmd.append('-x') + cmd.append("-x") cmd.extend([attribute, path]) try: ret = salt.utils.mac_utils.execute_return_result(cmd) except CommandExecutionError as exc: - if 'No such file' in exc.strerror: - raise CommandExecutionError('File not found: {0}'.format(path)) - if 'No such xattr' in exc.strerror: - raise CommandExecutionError('Attribute not found: {0}'.format(attribute)) - raise CommandExecutionError('Unknown Error: {0}'.format(exc.strerror)) + if "No such file" in exc.strerror: + raise CommandExecutionError("File not found: {0}".format(path)) + if "No such xattr" in exc.strerror: + raise CommandExecutionError("Attribute not found: {0}".format(attribute)) + raise CommandExecutionError("Unknown Error: {0}".format(exc.strerror)) return ret def write(path, attribute, value, **kwargs): - ''' + """ Causes the given attribute name to be assigned the given value :param str path: The file(s) to get attributes from @@ -147,29 +147,29 @@ def write(path, attribute, value, **kwargs): salt '*' xattr.write /path/to/file "com.test.attr" "value" - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - hex_ = kwargs.pop('hex', False) + hex_ = kwargs.pop("hex", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) - cmd = ['xattr', '-w'] + cmd = ["xattr", "-w"] if hex_: - cmd.append('-x') + cmd.append("-x") cmd.extend([attribute, value, path]) try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: - if 'No such file' in exc.strerror: - raise CommandExecutionError('File not found: {0}'.format(path)) - raise CommandExecutionError('Unknown Error: {0}'.format(exc.strerror)) + if "No such file" in exc.strerror: + raise CommandExecutionError("File not found: {0}".format(path)) + raise CommandExecutionError("Unknown Error: {0}".format(exc.strerror)) - return read(path, attribute, **{'hex': hex_}) == value + return read(path, attribute, **{"hex": hex_}) == value def delete(path, attribute): - ''' + """ Removes the given attribute from the file :param str path: The file(s) to get attributes from @@ -188,22 +188,22 @@ def delete(path, attribute): .. code-block:: bash salt '*' xattr.delete /path/to/file "com.test.attr" - ''' + """ cmd = 'xattr -d "{0}" "{1}"'.format(attribute, path) try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: - if 'No such file' in exc.strerror: - raise CommandExecutionError('File not found: {0}'.format(path)) - if 'No such xattr' in exc.strerror: - raise CommandExecutionError('Attribute not found: {0}'.format(attribute)) - raise CommandExecutionError('Unknown Error: {0}'.format(exc.strerror)) + if "No such file" in exc.strerror: + raise CommandExecutionError("File not found: {0}".format(path)) + if "No such xattr" in exc.strerror: + raise CommandExecutionError("Attribute not found: {0}".format(attribute)) + raise CommandExecutionError("Unknown Error: {0}".format(exc.strerror)) return attribute not in list_(path) def clear(path): - ''' + """ Causes the all attributes on the file/directory to be removed :param str path: The file(s) to get attributes from @@ -217,13 +217,13 @@ def clear(path): .. code-block:: bash salt '*' xattr.delete /path/to/file "com.test.attr" - ''' + """ cmd = 'xattr -c "{0}"'.format(path) try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: - if 'No such file' in exc.strerror: - raise CommandExecutionError('File not found: {0}'.format(path)) - raise CommandExecutionError('Unknown Error: {0}'.format(exc.strerror)) + if "No such file" in exc.strerror: + raise CommandExecutionError("File not found: {0}".format(path)) + raise CommandExecutionError("Unknown Error: {0}".format(exc.strerror)) return list_(path) == {} diff --git a/salt/modules/macdefaults.py b/salt/modules/macdefaults.py index cac3d59e049..f21f779e83a 100644 --- a/salt/modules/macdefaults.py +++ b/salt/modules/macdefaults.py @@ -1,31 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" Set defaults on Mac OS -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.platform log = logging.getLogger(__name__) -__virtualname__ = 'macdefaults' +__virtualname__ = "macdefaults" def __virtual__(): - ''' + """ Only work on Mac OS - ''' + """ if salt.utils.platform.is_darwin(): return __virtualname__ return False -def write(domain, key, value, type='string', user=None): - ''' +def write(domain, key, value, type="string", user=None): + """ Write a default to the system CLI Example: @@ -53,19 +54,19 @@ def write(domain, key, value, type='string', user=None): The user to write the defaults to - ''' - if type == 'bool' or type == 'boolean': + """ + if type == "bool" or type == "boolean": if value is True: - value = 'TRUE' + value = "TRUE" elif value is False: - value = 'FALSE' + value = "FALSE" cmd = 'defaults write "{0}" "{1}" -{2} "{3}"'.format(domain, key, type, value) - return __salt__['cmd.run_all'](cmd, runas=user) + return __salt__["cmd.run_all"](cmd, runas=user) def read(domain, key, user=None): - ''' + """ Write a default to the system CLI Example: @@ -85,13 +86,13 @@ def read(domain, key, user=None): user The user to write the defaults to - ''' + """ cmd = 'defaults read "{0}" "{1}"'.format(domain, key) - return __salt__['cmd.run'](cmd, runas=user) + return __salt__["cmd.run"](cmd, runas=user) def delete(domain, key, user=None): - ''' + """ Delete a default from the system CLI Example: @@ -111,6 +112,6 @@ def delete(domain, key, user=None): user The user to delete the defaults with - ''' + """ cmd = 'defaults delete "{0}" "{1}"'.format(domain, key) - return __salt__['cmd.run_all'](cmd, runas=user, output_loglevel='debug') + return __salt__["cmd.run_all"](cmd, runas=user, output_loglevel="debug") diff --git a/salt/modules/macpackage.py b/salt/modules/macpackage.py index 6e6bed357bd..a58ca602913 100644 --- a/salt/modules/macpackage.py +++ b/salt/modules/macpackage.py @@ -1,47 +1,50 @@ # -*- coding: utf-8 -*- -''' +""" Install pkg, dmg and .app applications on macOS minions. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import os -import logging +from __future__ import absolute_import, print_function, unicode_literals +import logging +import os import shlex -try: - import pipes - HAS_DEPS = True -except ImportError: - HAS_DEPS = False # Import Salt libs import salt.utils.platform +try: + import pipes + + HAS_DEPS = True +except ImportError: + HAS_DEPS = False + + log = logging.getLogger(__name__) -__virtualname__ = 'macpackage' +__virtualname__ = "macpackage" -if hasattr(shlex, 'quote'): +if hasattr(shlex, "quote"): _quote = shlex.quote -elif HAS_DEPS and hasattr(pipes, 'quote'): +elif HAS_DEPS and hasattr(pipes, "quote"): _quote = pipes.quote else: _quote = None def __virtual__(): - ''' + """ Only work on Mac OS - ''' + """ if salt.utils.platform.is_darwin() and _quote is not None: return __virtualname__ - return False + return (False, "Only available on Mac OS systems with pipes") -def install(pkg, target='LocalSystem', store=False, allow_untrusted=False): - ''' +def install(pkg, target="LocalSystem", store=False, allow_untrusted=False): + """ Install a pkg file Args: @@ -59,30 +62,30 @@ def install(pkg, target='LocalSystem', store=False, allow_untrusted=False): .. code-block:: bash salt '*' macpackage.install test.pkg - ''' - if '*.' not in pkg: + """ + if "*." not in pkg: # If we use wildcards, we cannot use quotes pkg = _quote(pkg) target = _quote(target) - cmd = 'installer -pkg {0} -target {1}'.format(pkg, target) + cmd = "installer -pkg {0} -target {1}".format(pkg, target) if store: - cmd += ' -store' + cmd += " -store" if allow_untrusted: - cmd += ' -allowUntrusted' + cmd += " -allowUntrusted" # We can only use wildcards in python_shell which is # sent by the macpackage state python_shell = False - if '*.' in cmd: + if "*." in cmd: python_shell = True - return __salt__['cmd.run_all'](cmd, python_shell=python_shell) + return __salt__["cmd.run_all"](cmd, python_shell=python_shell) -def install_app(app, target='/Applications/'): - ''' +def install_app(app, target="/Applications/"): + """ Install an app file by moving it into the specified Applications directory Args: @@ -98,25 +101,25 @@ def install_app(app, target='/Applications/'): .. code-block:: bash salt '*' macpackage.install_app /tmp/tmp.app /Applications/ - ''' + """ - if target[-4:] != '.app': - if app[-1:] == '/': + if target[-4:] != ".app": + if app[-1:] == "/": base_app = os.path.basename(app[:-1]) else: base_app = os.path.basename(app) target = os.path.join(target, base_app) - if not app[-1] == '/': - app += '/' + if not app[-1] == "/": + app += "/" cmd = 'rsync -a --delete "{0}" "{1}"'.format(app, target) - return __salt__['cmd.run'](cmd) + return __salt__["cmd.run"](cmd) def uninstall_app(app): - ''' + """ Uninstall an app file by removing it from the Applications directory Args: @@ -130,13 +133,13 @@ def uninstall_app(app): .. code-block:: bash salt '*' macpackage.uninstall_app /Applications/app.app - ''' + """ - return __salt__['file.remove'](app) + return __salt__["file.remove"](app) def mount(dmg): - ''' + """ Attempt to mount a dmg file to a temporary location and return the location of the pkg file inside @@ -152,17 +155,19 @@ def mount(dmg): .. code-block:: bash salt '*' macpackage.mount /tmp/software.dmg - ''' + """ - temp_dir = __salt__['temp.dir'](prefix='dmg-') + temp_dir = __salt__["temp.dir"](prefix="dmg-") - cmd = 'hdiutil attach -readonly -nobrowse -mountpoint {0} "{1}"'.format(temp_dir, dmg) + cmd = 'hdiutil attach -readonly -nobrowse -mountpoint {0} "{1}"'.format( + temp_dir, dmg + ) - return __salt__['cmd.run'](cmd), temp_dir + return __salt__["cmd.run"](cmd), temp_dir def unmount(mountpoint): - ''' + """ Attempt to unmount a dmg file from a temporary location Args: @@ -176,15 +181,15 @@ def unmount(mountpoint): .. code-block:: bash salt '*' macpackage.unmount /dev/disk2 - ''' + """ cmd = 'hdiutil detach "{0}"'.format(mountpoint) - return __salt__['cmd.run'](cmd) + return __salt__["cmd.run"](cmd) def installed_pkgs(): - ''' + """ Return the list of installed packages on the machine Returns: @@ -195,15 +200,15 @@ def installed_pkgs(): .. code-block:: bash salt '*' macpackage.installed_pkgs - ''' + """ - cmd = 'pkgutil --pkgs' + cmd = "pkgutil --pkgs" - return __salt__['cmd.run'](cmd).split('\n') + return __salt__["cmd.run"](cmd).split("\n") def get_pkg_id(pkg): - ''' + """ Attempt to get the package ID from a .pkg file Args: @@ -217,23 +222,23 @@ def get_pkg_id(pkg): .. code-block:: bash salt '*' macpackage.get_pkg_id /tmp/test.pkg - ''' + """ pkg = _quote(pkg) package_ids = [] # Create temp directory - temp_dir = __salt__['temp.dir'](prefix='pkg-') + temp_dir = __salt__["temp.dir"](prefix="pkg-") try: # List all of the PackageInfo files - cmd = 'xar -t -f {0} | grep PackageInfo'.format(pkg) - out = __salt__['cmd.run'](cmd, python_shell=True, output_loglevel='quiet') - files = out.split('\n') + cmd = "xar -t -f {0} | grep PackageInfo".format(pkg) + out = __salt__["cmd.run"](cmd, python_shell=True, output_loglevel="quiet") + files = out.split("\n") - if 'Error opening' not in out: + if "Error opening" not in out: # Extract the PackageInfo files - cmd = 'xar -x -f {0} {1}'.format(pkg, ' '.join(files)) - __salt__['cmd.run'](cmd, cwd=temp_dir, output_loglevel='quiet') + cmd = "xar -x -f {0} {1}".format(pkg, " ".join(files)) + __salt__["cmd.run"](cmd, cwd=temp_dir, output_loglevel="quiet") # Find our identifiers for f in files: @@ -245,13 +250,13 @@ def get_pkg_id(pkg): finally: # Clean up - __salt__['file.remove'](temp_dir) + __salt__["file.remove"](temp_dir) return package_ids def get_mpkg_ids(mpkg): - ''' + """ Attempt to get the package IDs from a mounted .mpkg file Args: @@ -265,16 +270,16 @@ def get_mpkg_ids(mpkg): .. code-block:: bash salt '*' macpackage.get_mpkg_ids /dev/disk2 - ''' + """ mpkg = _quote(mpkg) package_infos = [] base_path = os.path.dirname(mpkg) # List all of the .pkg files - cmd = 'find {0} -name *.pkg'.format(base_path) - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "find {0} -name *.pkg".format(base_path) + out = __salt__["cmd.run"](cmd, python_shell=True) - pkg_files = out.split('\n') + pkg_files = out.split("\n") for p in pkg_files: package_infos.extend(get_pkg_id(p)) @@ -284,28 +289,30 @@ def get_mpkg_ids(mpkg): def _get_pkg_id_from_pkginfo(pkginfo): # Find our identifiers pkginfo = _quote(pkginfo) - cmd = 'cat {0} | grep -Eo \'identifier="[a-zA-Z.0-9\\-]*"\' | cut -c 13- | tr -d \'"\''.format(pkginfo) - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "cat {0} | grep -Eo 'identifier=\"[a-zA-Z.0-9\\-]*\"' | cut -c 13- | tr -d '\"'".format( + pkginfo + ) + out = __salt__["cmd.run"](cmd, python_shell=True) - if 'No such file' not in out: - return out.split('\n') + if "No such file" not in out: + return out.split("\n") return [] def _get_pkg_id_dir(path): - path = _quote(os.path.join(path, 'Contents/Info.plist')) + path = _quote(os.path.join(path, "Contents/Info.plist")) cmd = '/usr/libexec/PlistBuddy -c "print :CFBundleIdentifier" {0}'.format(path) # We can only use wildcards in python_shell which is # sent by the macpackage state python_shell = False - if '*.' in cmd: + if "*." in cmd: python_shell = True - out = __salt__['cmd.run'](cmd, python_shell=python_shell) + out = __salt__["cmd.run"](cmd, python_shell=python_shell) - if 'Does Not Exist' not in out: + if "Does Not Exist" not in out: return [out] return [] diff --git a/salt/modules/makeconf.py b/salt/modules/makeconf.py index 3f7d41b0406..271c37eb68b 100644 --- a/salt/modules/makeconf.py +++ b/salt/modules/makeconf.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Support for modifying make.conf under Gentoo -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -12,50 +12,55 @@ import salt.utils.files def __virtual__(): - ''' + """ Only work on Gentoo - ''' - if __grains__['os'] == 'Gentoo': - return 'makeconf' - return (False, 'The makeconf execution module cannot be loaded: only available on Gentoo systems.') + """ + if __grains__["os"] == "Gentoo": + return "makeconf" + return ( + False, + "The makeconf execution module cannot be loaded: only available on Gentoo systems.", + ) def _get_makeconf(): - ''' + """ Find the correct make.conf. Gentoo recently moved the make.conf but still supports the old location, using the old location first - ''' - old_conf = '/etc/make.conf' - new_conf = '/etc/portage/make.conf' - if __salt__['file.file_exists'](old_conf): + """ + old_conf = "/etc/make.conf" + new_conf = "/etc/portage/make.conf" + if __salt__["file.file_exists"](old_conf): return old_conf - elif __salt__['file.file_exists'](new_conf): + elif __salt__["file.file_exists"](new_conf): return new_conf def _add_var(var, value): - ''' + """ Add a new var to the make.conf. If using layman, the source line for the layman make.conf needs to be at the very end of the config. This ensures that the new var will be above the source line. - ''' + """ makeconf = _get_makeconf() - layman = 'source /var/lib/layman/make.conf' + layman = "source /var/lib/layman/make.conf" fullvar = '{0}="{1}"'.format(var, value) - if __salt__['file.contains'](makeconf, layman): + if __salt__["file.contains"](makeconf, layman): # TODO perhaps make this a function in the file module? - cmd = ['sed', '-i', r'/{0}/ i\{1}'.format( - layman.replace('/', '\\/'), - fullvar), - makeconf] - __salt__['cmd.run'](cmd) + cmd = [ + "sed", + "-i", + r"/{0}/ i\{1}".format(layman.replace("/", "\\/"), fullvar), + makeconf, + ] + __salt__["cmd.run"](cmd) else: - __salt__['file.append'](makeconf, fullvar) + __salt__["file.append"](makeconf, fullvar) def set_var(var, value): - ''' + """ Set a variable in the make.conf Return a dict containing the new value for variable:: @@ -68,25 +73,25 @@ def set_var(var, value): .. code-block:: bash salt '*' makeconf.set_var 'LINGUAS' 'en' - ''' + """ makeconf = _get_makeconf() old_value = get_var(var) # If var already in file, replace its value if old_value is not None: - __salt__['file.sed']( - makeconf, '^{0}=.*'.format(var), '{0}="{1}"'.format(var, value) + __salt__["file.sed"]( + makeconf, "^{0}=.*".format(var), '{0}="{1}"'.format(var, value) ) else: _add_var(var, value) new_value = get_var(var) - return {var: {'old': old_value, 'new': new_value}} + return {var: {"old": old_value, "new": new_value}} def remove_var(var): - ''' + """ Remove a variable from the make.conf Return a dict containing the new value for the variable:: @@ -99,21 +104,21 @@ def remove_var(var): .. code-block:: bash salt '*' makeconf.remove_var 'LINGUAS' - ''' + """ makeconf = _get_makeconf() old_value = get_var(var) # If var is in file if old_value is not None: - __salt__['file.sed'](makeconf, '^{0}=.*'.format(var), '') + __salt__["file.sed"](makeconf, "^{0}=.*".format(var), "") new_value = get_var(var) - return {var: {'old': old_value, 'new': new_value}} + return {var: {"old": old_value, "new": new_value}} def append_var(var, value): - ''' + """ Add to or create a new variable in the make.conf Return a dict containing the new value for variable:: @@ -126,25 +131,26 @@ def append_var(var, value): .. code-block:: bash salt '*' makeconf.append_var 'LINGUAS' 'en' - ''' + """ makeconf = _get_makeconf() old_value = get_var(var) # If var already in file, add to its value if old_value is not None: - appended_value = '{0} {1}'.format(old_value, value) - __salt__['file.sed'](makeconf, '^{0}=.*'.format(var), - '{0}="{1}"'.format(var, appended_value)) + appended_value = "{0} {1}".format(old_value, value) + __salt__["file.sed"]( + makeconf, "^{0}=.*".format(var), '{0}="{1}"'.format(var, appended_value) + ) else: _add_var(var, value) new_value = get_var(var) - return {var: {'old': old_value, 'new': new_value}} + return {var: {"old": old_value, "new": new_value}} def trim_var(var, value): - ''' + """ Remove a value from a variable in the make.conf Return a dict containing the new value for variable:: @@ -157,21 +163,21 @@ def trim_var(var, value): .. code-block:: bash salt '*' makeconf.trim_var 'LINGUAS' 'en' - ''' + """ makeconf = _get_makeconf() old_value = get_var(var) # If var in file, trim value from its value if old_value is not None: - __salt__['file.sed'](makeconf, value, '', limit=var) + __salt__["file.sed"](makeconf, value, "", limit=var) new_value = get_var(var) - return {var: {'old': old_value, 'new': new_value}} + return {var: {"old": old_value, "new": new_value}} def get_var(var): - ''' + """ Get the value of a variable in make.conf Return the value of the variable or None if the variable is not in @@ -182,25 +188,25 @@ def get_var(var): .. code-block:: bash salt '*' makeconf.get_var 'LINGUAS' - ''' + """ makeconf = _get_makeconf() # Open makeconf with salt.utils.files.fopen(makeconf) as fn_: conf_file = salt.utils.data.decode(fn_.readlines()) for line in conf_file: if line.startswith(var): - ret = line.split('=', 1)[1] + ret = line.split("=", 1)[1] if '"' in ret: ret = ret.split('"')[1] - elif '#' in ret: - ret = ret.split('#')[0] + elif "#" in ret: + ret = ret.split("#")[0] ret = ret.strip() return ret return None def var_contains(var, value): - ''' + """ Verify if variable contains a value in make.conf Return True if value is set for var @@ -210,17 +216,17 @@ def var_contains(var, value): .. code-block:: bash salt '*' makeconf.var_contains 'LINGUAS' 'en' - ''' + """ setval = get_var(var) # Remove any escaping that was needed to past through salt - value = value.replace('\\', '') + value = value.replace("\\", "") if setval is None: return False return value in setval.split() def set_cflags(value): - ''' + """ Set the CFLAGS variable Return a dict containing the new value for variable:: @@ -233,12 +239,12 @@ def set_cflags(value): .. code-block:: bash salt '*' makeconf.set_cflags '-march=native -O2 -pipe' - ''' - return set_var('CFLAGS', value) + """ + return set_var("CFLAGS", value) def get_cflags(): - ''' + """ Get the value of CFLAGS variable in the make.conf Return the value of the variable or None if the variable is @@ -249,12 +255,12 @@ def get_cflags(): .. code-block:: bash salt '*' makeconf.get_cflags - ''' - return get_var('CFLAGS') + """ + return get_var("CFLAGS") def append_cflags(value): - ''' + """ Add to or create a new CFLAGS in the make.conf Return a dict containing the new value for variable:: @@ -267,12 +273,12 @@ def append_cflags(value): .. code-block:: bash salt '*' makeconf.append_cflags '-pipe' - ''' - return append_var('CFLAGS', value) + """ + return append_var("CFLAGS", value) def trim_cflags(value): - ''' + """ Remove a value from CFLAGS variable in the make.conf Return a dict containing the new value for variable:: @@ -285,12 +291,12 @@ def trim_cflags(value): .. code-block:: bash salt '*' makeconf.trim_cflags '-pipe' - ''' - return trim_var('CFLAGS', value) + """ + return trim_var("CFLAGS", value) def cflags_contains(value): - ''' + """ Verify if CFLAGS variable contains a value in make.conf Return True if value is set for var @@ -300,12 +306,12 @@ def cflags_contains(value): .. code-block:: bash salt '*' makeconf.cflags_contains '-pipe' - ''' - return var_contains('CFLAGS', value) + """ + return var_contains("CFLAGS", value) def set_cxxflags(value): - ''' + """ Set the CXXFLAGS variable Return a dict containing the new value for variable:: @@ -318,12 +324,12 @@ def set_cxxflags(value): .. code-block:: bash salt '*' makeconf.set_cxxflags '-march=native -O2 -pipe' - ''' - return set_var('CXXFLAGS', value) + """ + return set_var("CXXFLAGS", value) def get_cxxflags(): - ''' + """ Get the value of CXXFLAGS variable in the make.conf Return the value of the variable or None if the variable is @@ -334,12 +340,12 @@ def get_cxxflags(): .. code-block:: bash salt '*' makeconf.get_cxxflags - ''' - return get_var('CXXFLAGS') + """ + return get_var("CXXFLAGS") def append_cxxflags(value): - ''' + """ Add to or create a new CXXFLAGS in the make.conf Return a dict containing the new value for variable:: @@ -352,12 +358,12 @@ def append_cxxflags(value): .. code-block:: bash salt '*' makeconf.append_cxxflags '-pipe' - ''' - return append_var('CXXFLAGS', value) + """ + return append_var("CXXFLAGS", value) def trim_cxxflags(value): - ''' + """ Remove a value from CXXFLAGS variable in the make.conf Return a dict containing the new value for variable:: @@ -370,12 +376,12 @@ def trim_cxxflags(value): .. code-block:: bash salt '*' makeconf.trim_cxxflags '-pipe' - ''' - return trim_var('CXXFLAGS', value) + """ + return trim_var("CXXFLAGS", value) def cxxflags_contains(value): - ''' + """ Verify if CXXFLAGS variable contains a value in make.conf Return True if value is set for var @@ -385,12 +391,12 @@ def cxxflags_contains(value): .. code-block:: bash salt '*' makeconf.cxxflags_contains '-pipe' - ''' - return var_contains('CXXFLAGS', value) + """ + return var_contains("CXXFLAGS", value) def set_chost(value): - ''' + """ Set the CHOST variable Return a dict containing the new value for variable:: @@ -403,12 +409,12 @@ def set_chost(value): .. code-block:: bash salt '*' makeconf.set_chost 'x86_64-pc-linux-gnu' - ''' - return set_var('CHOST', value) + """ + return set_var("CHOST", value) def get_chost(): - ''' + """ Get the value of CHOST variable in the make.conf Return the value of the variable or None if the variable is @@ -419,12 +425,12 @@ def get_chost(): .. code-block:: bash salt '*' makeconf.get_chost - ''' - return get_var('CHOST') + """ + return get_var("CHOST") def chost_contains(value): - ''' + """ Verify if CHOST variable contains a value in make.conf Return True if value is set for var @@ -434,12 +440,12 @@ def chost_contains(value): .. code-block:: bash salt '*' makeconf.chost_contains 'x86_64-pc-linux-gnu' - ''' - return var_contains('CHOST', value) + """ + return var_contains("CHOST", value) def set_makeopts(value): - ''' + """ Set the MAKEOPTS variable Return a dict containing the new value for variable:: @@ -452,12 +458,12 @@ def set_makeopts(value): .. code-block:: bash salt '*' makeconf.set_makeopts '-j3' - ''' - return set_var('MAKEOPTS', value) + """ + return set_var("MAKEOPTS", value) def get_makeopts(): - ''' + """ Get the value of MAKEOPTS variable in the make.conf Return the value of the variable or None if the variable is @@ -468,12 +474,12 @@ def get_makeopts(): .. code-block:: bash salt '*' makeconf.get_makeopts - ''' - return get_var('MAKEOPTS') + """ + return get_var("MAKEOPTS") def append_makeopts(value): - ''' + """ Add to or create a new MAKEOPTS in the make.conf Return a dict containing the new value for variable:: @@ -486,12 +492,12 @@ def append_makeopts(value): .. code-block:: bash salt '*' makeconf.append_makeopts '-j3' - ''' - return append_var('MAKEOPTS', value) + """ + return append_var("MAKEOPTS", value) def trim_makeopts(value): - ''' + """ Remove a value from MAKEOPTS variable in the make.conf Return a dict containing the new value for variable:: @@ -504,12 +510,12 @@ def trim_makeopts(value): .. code-block:: bash salt '*' makeconf.trim_makeopts '-j3' - ''' - return trim_var('MAKEOPTS', value) + """ + return trim_var("MAKEOPTS", value) def makeopts_contains(value): - ''' + """ Verify if MAKEOPTS variable contains a value in make.conf Return True if value is set for var @@ -519,12 +525,12 @@ def makeopts_contains(value): .. code-block:: bash salt '*' makeconf.makeopts_contains '-j3' - ''' - return var_contains('MAKEOPTS', value) + """ + return var_contains("MAKEOPTS", value) def set_emerge_default_opts(value): - ''' + """ Set the EMERGE_DEFAULT_OPTS variable Return a dict containing the new value for variable:: @@ -537,12 +543,12 @@ def set_emerge_default_opts(value): .. code-block:: bash salt '*' makeconf.set_emerge_default_opts '--jobs' - ''' - return set_var('EMERGE_DEFAULT_OPTS', value) + """ + return set_var("EMERGE_DEFAULT_OPTS", value) def get_emerge_default_opts(): - ''' + """ Get the value of EMERGE_DEFAULT_OPTS variable in the make.conf Return the value of the variable or None if the variable is @@ -553,12 +559,12 @@ def get_emerge_default_opts(): .. code-block:: bash salt '*' makeconf.get_emerge_default_opts - ''' - return get_var('EMERGE_DEFAULT_OPTS') + """ + return get_var("EMERGE_DEFAULT_OPTS") def append_emerge_default_opts(value): - ''' + """ Add to or create a new EMERGE_DEFAULT_OPTS in the make.conf Return a dict containing the new value for variable:: @@ -571,12 +577,12 @@ def append_emerge_default_opts(value): .. code-block:: bash salt '*' makeconf.append_emerge_default_opts '--jobs' - ''' - return append_var('EMERGE_DEFAULT_OPTS', value) + """ + return append_var("EMERGE_DEFAULT_OPTS", value) def trim_emerge_default_opts(value): - ''' + """ Remove a value from EMERGE_DEFAULT_OPTS variable in the make.conf Return a dict containing the new value for variable:: @@ -589,12 +595,12 @@ def trim_emerge_default_opts(value): .. code-block:: bash salt '*' makeconf.trim_emerge_default_opts '--jobs' - ''' - return trim_var('EMERGE_DEFAULT_OPTS', value) + """ + return trim_var("EMERGE_DEFAULT_OPTS", value) def emerge_default_opts_contains(value): - ''' + """ Verify if EMERGE_DEFAULT_OPTS variable contains a value in make.conf Return True if value is set for var @@ -604,12 +610,12 @@ def emerge_default_opts_contains(value): .. code-block:: bash salt '*' makeconf.emerge_default_opts_contains '--jobs' - ''' - return var_contains('EMERGE_DEFAULT_OPTS', value) + """ + return var_contains("EMERGE_DEFAULT_OPTS", value) def set_gentoo_mirrors(value): - ''' + """ Set the GENTOO_MIRRORS variable Return a dict containing the new value for variable:: @@ -622,12 +628,12 @@ def set_gentoo_mirrors(value): .. code-block:: bash salt '*' makeconf.set_gentoo_mirrors 'http://distfiles.gentoo.org' - ''' - return set_var('GENTOO_MIRRORS', value) + """ + return set_var("GENTOO_MIRRORS", value) def get_gentoo_mirrors(): - ''' + """ Get the value of GENTOO_MIRRORS variable in the make.conf Return the value of the variable or None if the variable is @@ -638,12 +644,12 @@ def get_gentoo_mirrors(): .. code-block:: bash salt '*' makeconf.get_gentoo_mirrors - ''' - return get_var('GENTOO_MIRRORS') + """ + return get_var("GENTOO_MIRRORS") def append_gentoo_mirrors(value): - ''' + """ Add to or create a new GENTOO_MIRRORS in the make.conf Return a dict containing the new value for variable:: @@ -656,12 +662,12 @@ def append_gentoo_mirrors(value): .. code-block:: bash salt '*' makeconf.append_gentoo_mirrors 'http://distfiles.gentoo.org' - ''' - return append_var('GENTOO_MIRRORS', value) + """ + return append_var("GENTOO_MIRRORS", value) def trim_gentoo_mirrors(value): - ''' + """ Remove a value from GENTOO_MIRRORS variable in the make.conf Return a dict containing the new value for variable:: @@ -674,12 +680,12 @@ def trim_gentoo_mirrors(value): .. code-block:: bash salt '*' makeconf.trim_gentoo_mirrors 'http://distfiles.gentoo.org' - ''' - return trim_var('GENTOO_MIRRORS', value) + """ + return trim_var("GENTOO_MIRRORS", value) def gentoo_mirrors_contains(value): - ''' + """ Verify if GENTOO_MIRRORS variable contains a value in make.conf Return True if value is set for var @@ -689,12 +695,12 @@ def gentoo_mirrors_contains(value): .. code-block:: bash salt '*' makeconf.gentoo_mirrors_contains 'http://distfiles.gentoo.org' - ''' - return var_contains('GENTOO_MIRRORS', value) + """ + return var_contains("GENTOO_MIRRORS", value) def set_sync(value): - ''' + """ Set the SYNC variable Return a dict containing the new value for variable:: @@ -707,12 +713,12 @@ def set_sync(value): .. code-block:: bash salt '*' makeconf.set_sync 'rsync://rsync.namerica.gentoo.org/gentoo-portage' - ''' - return set_var('SYNC', value) + """ + return set_var("SYNC", value) def get_sync(): - ''' + """ Get the value of SYNC variable in the make.conf Return the value of the variable or None if the variable is @@ -723,12 +729,12 @@ def get_sync(): .. code-block:: bash salt '*' makeconf.get_sync - ''' - return get_var('SYNC') + """ + return get_var("SYNC") def sync_contains(value): - ''' + """ Verify if SYNC variable contains a value in make.conf Return True if value is set for var @@ -738,12 +744,12 @@ def sync_contains(value): .. code-block:: bash salt '*' makeconf.sync_contains 'rsync://rsync.namerica.gentoo.org/gentoo-portage' - ''' - return var_contains('SYNC', value) + """ + return var_contains("SYNC", value) def get_features(): - ''' + """ Get the value of FEATURES variable in the make.conf Return the value of the variable or None if the variable is @@ -754,12 +760,12 @@ def get_features(): .. code-block:: bash salt '*' makeconf.get_features - ''' - return get_var('FEATURES') + """ + return get_var("FEATURES") def append_features(value): - ''' + """ Add to or create a new FEATURES in the make.conf Return a dict containing the new value for variable:: @@ -772,12 +778,12 @@ def append_features(value): .. code-block:: bash salt '*' makeconf.append_features 'webrsync-gpg' - ''' - return append_var('FEATURES', value) + """ + return append_var("FEATURES", value) def trim_features(value): - ''' + """ Remove a value from FEATURES variable in the make.conf Return a dict containing the new value for variable:: @@ -790,12 +796,12 @@ def trim_features(value): .. code-block:: bash salt '*' makeconf.trim_features 'webrsync-gpg' - ''' - return trim_var('FEATURES', value) + """ + return trim_var("FEATURES", value) def features_contains(value): - ''' + """ Verify if FEATURES variable contains a value in make.conf Return True if value is set for var @@ -805,5 +811,5 @@ def features_contains(value): .. code-block:: bash salt '*' makeconf.features_contains 'webrsync-gpg' - ''' - return var_contains('FEATURES', value) + """ + return var_contains("FEATURES", value) diff --git a/salt/modules/mandrill.py b/salt/modules/mandrill.py index 7044060154e..6b8029d9a81 100644 --- a/salt/modules/mandrill.py +++ b/salt/modules/mandrill.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Mandrill ======== @@ -16,10 +16,11 @@ In the minion configuration file, the following block is required: key: .. versionadded:: 2018.3.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -29,106 +30,92 @@ import salt.utils.versions # import third party try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False -__virtualname__ = 'mandrill' +__virtualname__ = "mandrill" log = logging.getLogger(__file__) -BASE_URL = 'https://mandrillapp.com/api' +BASE_URL = "https://mandrillapp.com/api" DEFAULT_VERSION = 1 def __virtual__(): - ''' + """ Return the execution module virtualname. - ''' + """ if HAS_REQUESTS is False: - return False, 'The requests python package is required for the mandrill execution module' + return ( + False, + "The requests python package is required for the mandrill execution module", + ) return __virtualname__ def _default_ret(): - ''' + """ Default dictionary returned. - ''' - return { - 'result': False, - 'comment': '', - 'out': None - } + """ + return {"result": False, "comment": "", "out": None} -def _get_api_params(api_url=None, - api_version=None, - api_key=None): - ''' +def _get_api_params(api_url=None, api_version=None, api_key=None): + """ Retrieve the API params from the config file. - ''' - mandrill_cfg = __salt__['config.merge']('mandrill') + """ + mandrill_cfg = __salt__["config.merge"]("mandrill") if not mandrill_cfg: mandrill_cfg = {} return { - 'api_url': api_url or mandrill_cfg.get('api_url') or BASE_URL, # optional - 'api_key': api_key or mandrill_cfg.get('key'), # mandatory - 'api_version': api_version or mandrill_cfg.get('api_version') or DEFAULT_VERSION + "api_url": api_url or mandrill_cfg.get("api_url") or BASE_URL, # optional + "api_key": api_key or mandrill_cfg.get("key"), # mandatory + "api_version": api_version + or mandrill_cfg.get("api_version") + or DEFAULT_VERSION, } -def _get_url(method, - api_url, - api_version): - ''' +def _get_url(method, api_url, api_version): + """ Build the API URL. - ''' - return '{url}/{version}/{method}.json'.format(url=api_url, - version=float(api_version), - method=method) + """ + return "{url}/{version}/{method}.json".format( + url=api_url, version=float(api_version), method=method + ) def _get_headers(): - ''' + """ Return HTTP headers required for the Mandrill API. - ''' - return { - 'content-type': 'application/json', - 'user-agent': 'Mandrill-Python/1.0.57' - } + """ + return {"content-type": "application/json", "user-agent": "Mandrill-Python/1.0.57"} -def _http_request(url, - headers=None, - data=None): - ''' +def _http_request(url, headers=None, data=None): + """ Make the HTTP request and return the body as python object. - ''' + """ if not headers: headers = _get_headers() session = requests.session() - log.debug('Querying %s', url) - req = session.post(url, - headers=headers, - data=salt.utils.json.dumps(data)) + log.debug("Querying %s", url) + req = session.post(url, headers=headers, data=salt.utils.json.dumps(data)) req_body = req.json() ret = _default_ret() - log.debug('Status code: %d', req.status_code) - log.debug('Response body:') + log.debug("Status code: %d", req.status_code) + log.debug("Response body:") log.debug(req_body) if req.status_code != 200: if req.status_code == 500: - ret['comment'] = req_body.pop('message', '') - ret['out'] = req_body + ret["comment"] = req_body.pop("message", "") + ret["out"] = req_body return ret - ret.update({ - 'comment': req_body.get('error', '') - }) + ret.update({"comment": req_body.get("error", "")}) return ret - ret.update({ - 'result': True, - 'out': req.json() - }) + ret.update({"result": True, "out": req.json()}) return ret @@ -137,15 +124,17 @@ def _http_request(url, # ------------------------------------------------------------------------------ -def send(message, - asynchronous=False, - ip_pool=None, - send_at=None, - api_url=None, - api_version=None, - api_key=None, - **kwargs): - ''' +def send( + message, + asynchronous=False, + ip_pool=None, + send_at=None, + api_url=None, + api_version=None, + api_key=None, + **kwargs +): + """ Send out the email using the details from the ``message`` argument. message @@ -230,23 +219,24 @@ def send(message, sent result: True - ''' - if 'async' in kwargs: # Remove this in Sodium - salt.utils.versions.warn_until('Sodium', 'Parameter "async" is renamed to "asynchronous" ' - 'and will be removed in version {version}.') - asynchronous = bool(kwargs['async']) + """ + if "async" in kwargs: # Remove this in Sodium + salt.utils.versions.warn_until( + "Sodium", + 'Parameter "async" is renamed to "asynchronous" ' + "and will be removed in version {version}.", + ) + asynchronous = bool(kwargs["async"]) - params = _get_api_params(api_url=api_url, - api_version=api_version, - api_key=api_key) - url = _get_url('messages/send', - api_url=params['api_url'], - api_version=params['api_version']) + params = _get_api_params(api_url=api_url, api_version=api_version, api_key=api_key) + url = _get_url( + "messages/send", api_url=params["api_url"], api_version=params["api_version"] + ) data = { - 'key': params['api_key'], - 'message': message, - 'async': asynchronous, - 'ip_pool': ip_pool, - 'send_at': send_at + "key": params["api_key"], + "message": message, + "async": asynchronous, + "ip_pool": ip_pool, + "send_at": send_at, } return _http_request(url, data=data) diff --git a/salt/modules/marathon.py b/salt/modules/marathon.py index 7da53e7ea13..7f827918c7e 100644 --- a/salt/modules/marathon.py +++ b/salt/modules/marathon.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Module providing a simple management interface to a marathon cluster. Currently this only works when run through a proxy minion. .. versionadded:: 2015.8.2 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -17,43 +18,42 @@ import salt.utils.json import salt.utils.platform from salt.exceptions import get_error_message - -__proxyenabled__ = ['marathon'] +__proxyenabled__ = ["marathon"] log = logging.getLogger(__file__) def __virtual__(): # only valid in proxy minions for now - if salt.utils.platform.is_proxy() and 'proxy' in __opts__: + if salt.utils.platform.is_proxy() and "proxy" in __opts__: return True return ( False, - 'The marathon execution module cannot be loaded: this only works on ' - 'proxy minions.' + "The marathon execution module cannot be loaded: this only works on " + "proxy minions.", ) def _base_url(): - ''' + """ Return the proxy configured base url. - ''' + """ base_url = "http://locahost:8080" - if 'proxy' in __opts__: - base_url = __opts__['proxy'].get('base_url', base_url) + if "proxy" in __opts__: + base_url = __opts__["proxy"].get("base_url", base_url) return base_url def _app_id(app_id): - ''' + """ Make sure the app_id is in the correct format. - ''' - if app_id[0] != '/': - app_id = '/{0}'.format(app_id) + """ + if app_id[0] != "/": + app_id = "/{0}".format(app_id) return app_id def apps(): - ''' + """ Return a list of the currently installed app ids. CLI Example: @@ -61,17 +61,15 @@ def apps(): .. code-block:: bash salt marathon-minion-id marathon.apps - ''' + """ response = salt.utils.http.query( - "{0}/v2/apps".format(_base_url()), - decode_type='json', - decode=True, + "{0}/v2/apps".format(_base_url()), decode_type="json", decode=True, ) - return {'apps': [app['id'] for app in response['dict']['apps']]} + return {"apps": [app["id"] for app in response["dict"]["apps"]]} def has_app(id): - ''' + """ Return whether the given app id is currently configured. CLI Example: @@ -79,12 +77,12 @@ def has_app(id): .. code-block:: bash salt marathon-minion-id marathon.has_app my-app - ''' - return _app_id(id) in apps()['apps'] + """ + return _app_id(id) in apps()["apps"] def app(id): - ''' + """ Return the current server configuration for the specified app. CLI Example: @@ -92,17 +90,15 @@ def app(id): .. code-block:: bash salt marathon-minion-id marathon.app my-app - ''' + """ response = salt.utils.http.query( - "{0}/v2/apps/{1}".format(_base_url(), id), - decode_type='json', - decode=True, + "{0}/v2/apps/{1}".format(_base_url(), id), decode_type="json", decode=True, ) - return response['dict'] + return response["dict"] def update_app(id, config): - ''' + """ Update the specified app with the given configuration. CLI Example: @@ -110,39 +106,35 @@ def update_app(id, config): .. code-block:: bash salt marathon-minion-id marathon.update_app my-app '' - ''' - if 'id' not in config: - config['id'] = id - config.pop('version', None) + """ + if "id" not in config: + config["id"] = id + config.pop("version", None) # mirror marathon-ui handling for uris deprecation (see # mesosphere/marathon-ui#594 for more details) - config.pop('fetch', None) + config.pop("fetch", None) data = salt.utils.json.dumps(config) try: response = salt.utils.http.query( "{0}/v2/apps/{1}?force=true".format(_base_url(), id), - method='PUT', - decode_type='json', + method="PUT", + decode_type="json", decode=True, data=data, header_dict={ - 'Content-Type': 'application/json', - 'Accept': 'application/json', + "Content-Type": "application/json", + "Accept": "application/json", }, ) - log.debug('update response: %s', response) - return response['dict'] + log.debug("update response: %s", response) + return response["dict"] except Exception as ex: # pylint: disable=broad-except - log.error('unable to update marathon app: %s', get_error_message(ex)) - return { - 'exception': { - 'message': get_error_message(ex), - } - } + log.error("unable to update marathon app: %s", get_error_message(ex)) + return {"exception": {"message": get_error_message(ex)}} def rm_app(id): - ''' + """ Remove the specified app from the server. CLI Example: @@ -150,18 +142,18 @@ def rm_app(id): .. code-block:: bash salt marathon-minion-id marathon.rm_app my-app - ''' + """ response = salt.utils.http.query( "{0}/v2/apps/{1}".format(_base_url(), id), - method='DELETE', - decode_type='json', + method="DELETE", + decode_type="json", decode=True, ) - return response['dict'] + return response["dict"] def info(): - ''' + """ Return configuration and status information about the marathon instance. CLI Example: @@ -169,17 +161,15 @@ def info(): .. code-block:: bash salt marathon-minion-id marathon.info - ''' + """ response = salt.utils.http.query( - "{0}/v2/info".format(_base_url()), - decode_type='json', - decode=True, + "{0}/v2/info".format(_base_url()), decode_type="json", decode=True, ) - return response['dict'] + return response["dict"] def restart_app(id, restart=False, force=True): - ''' + """ Restart the current server configuration for the specified app. :param restart: Restart the app @@ -203,31 +193,27 @@ def restart_app(id, restart=False, force=True): The force option tells marathon to ignore the current app deployment if there is one. - ''' - ret = {'restarted': None} + """ + ret = {"restarted": None} if not restart: - ret['restarted'] = False + ret["restarted"] = False return ret try: response = salt.utils.http.query( "{0}/v2/apps/{1}/restart?force={2}".format(_base_url(), _app_id(id), force), - method='POST', - decode_type='json', + method="POST", + decode_type="json", decode=True, header_dict={ - 'Content-Type': 'application/json', - 'Accept': 'application/json', + "Content-Type": "application/json", + "Accept": "application/json", }, ) - log.debug('restart response: %s', response) + log.debug("restart response: %s", response) - ret['restarted'] = True - ret.update(response['dict']) + ret["restarted"] = True + ret.update(response["dict"]) return ret except Exception as ex: # pylint: disable=broad-except - log.error('unable to restart marathon app: %s', ex.message) - return { - 'exception': { - 'message': ex.message, - } - } + log.error("unable to restart marathon app: %s", ex.message) + return {"exception": {"message": ex.message}} diff --git a/salt/modules/match.py b/salt/modules/match.py index b6a9a31002d..9ab9bbeedb5 100644 --- a/salt/modules/match.py +++ b/salt/modules/match.py @@ -1,29 +1,28 @@ # -*- coding: utf-8 -*- -''' +""" The match module allows for match routines to be run and determine target specs -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import copy + # Import python libs import inspect import logging import sys -import copy # Import salt libs import salt.loader from salt.defaults import DEFAULT_TARGET_DELIM from salt.ext import six -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} log = logging.getLogger(__name__) def compound(tgt, minion_id=None): - ''' + """ Return True if the minion ID matches the given compound target minion_id @@ -36,24 +35,24 @@ def compound(tgt, minion_id=None): .. code-block:: bash salt '*' match.compound 'L@cheese,foo and *' - ''' + """ if minion_id is not None: opts = copy.copy(__opts__) if not isinstance(minion_id, six.string_types): minion_id = six.text_type(minion_id) - opts['id'] = minion_id + opts["id"] = minion_id else: opts = __opts__ matchers = salt.loader.matchers(opts) try: - return matchers['compound_match.match'](tgt) + return matchers["compound_match.match"](tgt) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False def ipcidr(tgt): - ''' + """ Return True if the minion matches the given ipcidr target CLI Example: @@ -71,17 +70,17 @@ def ipcidr(tgt): - match: ipcidr - nodeclass: internal - ''' + """ matchers = salt.loader.matchers(__opts__) try: - return matchers['ipcidr_match.match'](tgt, opts=__opts__) + return matchers["ipcidr_match.match"](tgt, opts=__opts__) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False def pillar_pcre(tgt, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Return True if the minion matches the given pillar_pcre target. The ``delimiter`` argument can be used to specify a different delimiter. @@ -102,17 +101,19 @@ def pillar_pcre(tgt, delimiter=DEFAULT_TARGET_DELIM): .. versionadded:: 0.16.4 .. deprecated:: 2015.8.0 - ''' + """ matchers = salt.loader.matchers(__opts__) try: - return matchers['pillar_pcre_match.match'](tgt, delimiter=delimiter, opts=__opts__) + return matchers["pillar_pcre_match.match"]( + tgt, delimiter=delimiter, opts=__opts__ + ) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False def pillar(tgt, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Return True if the minion matches the given pillar target. The ``delimiter`` argument can be used to specify a different delimiter. @@ -133,17 +134,17 @@ def pillar(tgt, delimiter=DEFAULT_TARGET_DELIM): .. versionadded:: 0.16.4 .. deprecated:: 2015.8.0 - ''' + """ matchers = salt.loader.matchers(__opts__) try: - return matchers['pillar_match.match'](tgt, delimiter=delimiter, opts=__opts__) + return matchers["pillar_match.match"](tgt, delimiter=delimiter, opts=__opts__) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False def data(tgt): - ''' + """ Return True if the minion matches the given data target CLI Example: @@ -151,17 +152,17 @@ def data(tgt): .. code-block:: bash salt '*' match.data 'spam:eggs' - ''' + """ matchers = salt.loader.matchers(__opts__) try: - return matchers['data_match.match'](tgt, opts=__opts__) + return matchers["data_match.match"](tgt, opts=__opts__) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False def grain_pcre(tgt, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Return True if the minion matches the given grain_pcre target. The ``delimiter`` argument can be used to specify a different delimiter. @@ -182,17 +183,19 @@ def grain_pcre(tgt, delimiter=DEFAULT_TARGET_DELIM): .. versionadded:: 0.16.4 .. deprecated:: 2015.8.0 - ''' + """ matchers = salt.loader.matchers(__opts__) try: - return matchers['grain_pcre_match.match'](tgt, delimiter=delimiter, opts=__opts__) + return matchers["grain_pcre_match.match"]( + tgt, delimiter=delimiter, opts=__opts__ + ) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False def grain(tgt, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Return True if the minion matches the given grain target. The ``delimiter`` argument can be used to specify a different delimiter. @@ -213,17 +216,17 @@ def grain(tgt, delimiter=DEFAULT_TARGET_DELIM): .. versionadded:: 0.16.4 .. deprecated:: 2015.8.0 - ''' + """ matchers = salt.loader.matchers(__opts__) try: - return matchers['grain_match.match'](tgt, delimiter=delimiter, opts=__opts__) + return matchers["grain_match.match"](tgt, delimiter=delimiter, opts=__opts__) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False def list_(tgt, minion_id=None): - ''' + """ Return True if the minion ID matches the given list target minion_id @@ -236,24 +239,24 @@ def list_(tgt, minion_id=None): .. code-block:: bash salt '*' match.list 'server1,server2' - ''' + """ if minion_id is not None: opts = copy.copy(__opts__) if not isinstance(minion_id, six.string_types): minion_id = six.text_type(minion_id) - opts['id'] = minion_id + opts["id"] = minion_id else: opts = __opts__ matchers = salt.loader.matchers(opts) try: - return matchers['list_match.match'](tgt, opts=__opts__) + return matchers["list_match.match"](tgt, opts=__opts__) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False def pcre(tgt, minion_id=None): - ''' + """ Return True if the minion ID matches the given pcre target minion_id @@ -266,24 +269,24 @@ def pcre(tgt, minion_id=None): .. code-block:: bash salt '*' match.pcre '.*' - ''' + """ if minion_id is not None: opts = copy.copy(__opts__) if not isinstance(minion_id, six.string_types): minion_id = six.text_type(minion_id) - opts['id'] = minion_id + opts["id"] = minion_id else: opts = __opts__ matchers = salt.loader.matchers(opts) try: - return matchers['pcre_match.match'](tgt, opts=__opts__) + return matchers["pcre_match.match"](tgt, opts=__opts__) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False def glob(tgt, minion_id=None): - ''' + """ Return True if the minion ID matches the given glob target minion_id @@ -296,28 +299,25 @@ def glob(tgt, minion_id=None): .. code-block:: bash salt '*' match.glob '*' - ''' + """ if minion_id is not None: opts = copy.copy(__opts__) if not isinstance(minion_id, six.string_types): minion_id = six.text_type(minion_id) - opts['id'] = minion_id + opts["id"] = minion_id else: opts = __opts__ matchers = salt.loader.matchers(opts) try: - return matchers['glob_match.match'](tgt, opts=__opts__) + return matchers["glob_match.match"](tgt, opts=__opts__) except Exception as exc: # pylint: disable=broad-except log.exception(exc) return False -def filter_by(lookup, - tgt_type='compound', - minion_id=None, - default='default'): - ''' +def filter_by(lookup, tgt_type="compound", minion_id=None, default="default"): + """ Return the first match in a dictionary of target patterns .. versionadded:: 2014.7.0 @@ -340,20 +340,21 @@ def filter_by(lookup, # Make the filtered data available to Pillar: roles: {{ roles | yaml() }} - ''' - expr_funcs = dict(inspect.getmembers(sys.modules[__name__], - predicate=inspect.isfunction)) + """ + expr_funcs = dict( + inspect.getmembers(sys.modules[__name__], predicate=inspect.isfunction) + ) for key in lookup: - params = (key, minion_id) if minion_id else (key, ) + params = (key, minion_id) if minion_id else (key,) if expr_funcs[tgt_type](*params): return lookup[key] return lookup.get(default, None) -def search_by(lookup, tgt_type='compound', minion_id=None): - ''' +def search_by(lookup, tgt_type="compound", minion_id=None): + """ Search a dictionary of target strings for matching targets This is the inverse of :py:func:`match.filter_by @@ -380,14 +381,15 @@ def search_by(lookup, tgt_type='compound', minion_id=None): # Make the filtered data available to Pillar: roles: {{ roles | yaml() }} - ''' - expr_funcs = dict(inspect.getmembers(sys.modules[__name__], - predicate=inspect.isfunction)) + """ + expr_funcs = dict( + inspect.getmembers(sys.modules[__name__], predicate=inspect.isfunction) + ) matches = [] for key, target_list in lookup.items(): for target in target_list: - params = (target, minion_id) if minion_id else (target, ) + params = (target, minion_id) if minion_id else (target,) if expr_funcs[tgt_type](*params): matches.append(key) diff --git a/salt/modules/mattermost.py b/salt/modules/mattermost.py index 98dc6606ed3..4271e26b8b8 100644 --- a/salt/modules/mattermost.py +++ b/salt/modules/mattermost.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for sending messages to Mattermost .. versionadded:: 2017.7.0 @@ -13,10 +13,11 @@ Module for sending messages to Mattermost mattermost: hook: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_url: https://example.com -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -26,76 +27,76 @@ from salt.exceptions import SaltInvocationError log = logging.getLogger(__name__) -__virtualname__ = 'mattermost' +__virtualname__ = "mattermost" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ def _get_hook(): - ''' + """ Retrieves and return the Mattermost's configured hook :return: String: the hook string - ''' - hook = __salt__['config.get']('mattermost.hook') or \ - __salt__['config.get']('mattermost:hook') + """ + hook = __salt__["config.get"]("mattermost.hook") or __salt__["config.get"]( + "mattermost:hook" + ) if not hook: - raise SaltInvocationError('No Mattermost Hook found') + raise SaltInvocationError("No Mattermost Hook found") return hook def _get_api_url(): - ''' + """ Retrieves and return the Mattermost's configured api url :return: String: the api url string - ''' - api_url = __salt__['config.get']('mattermost.api_url') or \ - __salt__['config.get']('mattermost:api_url') + """ + api_url = __salt__["config.get"]("mattermost.api_url") or __salt__["config.get"]( + "mattermost:api_url" + ) if not api_url: - raise SaltInvocationError('No Mattermost API URL found') + raise SaltInvocationError("No Mattermost API URL found") return api_url def _get_channel(): - ''' + """ Retrieves the Mattermost's configured channel :return: String: the channel string - ''' - channel = __salt__['config.get']('mattermost.channel') or \ - __salt__['config.get']('mattermost:channel') + """ + channel = __salt__["config.get"]("mattermost.channel") or __salt__["config.get"]( + "mattermost:channel" + ) return channel def _get_username(): - ''' + """ Retrieves the Mattermost's configured username :return: String: the username string - ''' - username = __salt__['config.get']('mattermost.username') or \ - __salt__['config.get']('mattermost:username') + """ + username = __salt__["config.get"]("mattermost.username") or __salt__["config.get"]( + "mattermost:username" + ) return username -def post_message(message, - channel=None, - username=None, - api_url=None, - hook=None): - ''' +def post_message(message, channel=None, username=None, api_url=None, hook=None): + """ Send a message to a Mattermost channel. :param channel: The channel name, either will work. @@ -110,7 +111,7 @@ def post_message(message, .. code-block:: bash salt '*' mattermost.post_message message='Build is done' - ''' + """ if not api_url: api_url = _get_api_url() @@ -124,16 +125,18 @@ def post_message(message, channel = _get_channel() if not message: - log.error('message is a required option.') + log.error("message is a required option.") parameters = dict() if channel: - parameters['channel'] = channel + parameters["channel"] = channel if username: - parameters['username'] = username - parameters['text'] = '```' + message + '```' # pre-formatted, fixed-width text - log.debug('Parameters: %s', parameters) - data = str('payload={0}').format(salt.utils.json.dumps(parameters)) # pylint: disable=blacklisted-function + parameters["username"] = username + parameters["text"] = "```" + message + "```" # pre-formatted, fixed-width text + log.debug("Parameters: %s", parameters) + data = str("payload={0}").format( + salt.utils.json.dumps(parameters) + ) # pylint: disable=blacklisted-function result = salt.utils.mattermost.query(api_url=api_url, hook=hook, data=data) return bool(result) diff --git a/salt/modules/mdadm_raid.py b/salt/modules/mdadm_raid.py index 1581a558b66..b194f8dc5b6 100644 --- a/salt/modules/mdadm_raid.py +++ b/salt/modules/mdadm_raid.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Salt module to manage RAID arrays with mdadm -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging import re # Import salt libs @@ -21,27 +22,31 @@ log = logging.getLogger(__name__) # Define a function alias in order not to shadow built-in's -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} # Define the module's virtual name -__virtualname__ = 'raid' +__virtualname__ = "raid" def __virtual__(): - ''' + """ mdadm provides raid functions for Linux - ''' - if __grains__['kernel'] != 'Linux': - return (False, 'The mdadm execution module cannot be loaded: only available on Linux.') - if not salt.utils.path.which('mdadm'): - return (False, 'The mdadm execution module cannot be loaded: the mdadm binary is not in the path.') + """ + if __grains__["kernel"] != "Linux": + return ( + False, + "The mdadm execution module cannot be loaded: only available on Linux.", + ) + if not salt.utils.path.which("mdadm"): + return ( + False, + "The mdadm execution module cannot be loaded: the mdadm binary is not in the path.", + ) return __virtualname__ def list_(): - ''' + """ List the RAID devices. CLI Example: @@ -49,25 +54,25 @@ def list_(): .. code-block:: bash salt '*' raid.list - ''' + """ ret = {} - for line in (__salt__['cmd.run_stdout'] - (['mdadm', '--detail', '--scan'], - python_shell=False).splitlines()): - if ' ' not in line: + for line in __salt__["cmd.run_stdout"]( + ["mdadm", "--detail", "--scan"], python_shell=False + ).splitlines(): + if " " not in line: continue comps = line.split() device = comps[1] ret[device] = {"device": device} for comp in comps[2:]: - key = comp.split('=')[0].lower() - value = comp.split('=')[1] + key = comp.split("=")[0].lower() + value = comp.split("=")[1] ret[device][key] = value return ret -def detail(device='/dev/md0'): - ''' +def detail(device="/dev/md0"): + """ Show detail for a specified RAID device CLI Example: @@ -75,44 +80,44 @@ def detail(device='/dev/md0'): .. code-block:: bash salt '*' raid.detail '/dev/md0' - ''' + """ ret = {} - ret['members'] = {} + ret["members"] = {} # Lets make sure the device exists before running mdadm if not os.path.exists(device): msg = "Device {0} doesn't exist!" raise CommandExecutionError(msg.format(device)) - cmd = ['mdadm', '--detail', device] - for line in __salt__['cmd.run_stdout'](cmd, python_shell=False).splitlines(): + cmd = ["mdadm", "--detail", device] + for line in __salt__["cmd.run_stdout"](cmd, python_shell=False).splitlines(): if line.startswith(device): continue - if ' ' not in line: + if " " not in line: continue - if ':' not in line: - if '/dev/' in line: + if ":" not in line: + if "/dev/" in line: comps = line.split() state = comps[4:-1] - ret['members'][comps[0]] = { - 'device': comps[-1], - 'major': comps[1], - 'minor': comps[2], - 'number': comps[0], - 'raiddevice': comps[3], - 'state': ' '.join(state), + ret["members"][comps[0]] = { + "device": comps[-1], + "major": comps[1], + "minor": comps[2], + "number": comps[0], + "raiddevice": comps[3], + "state": " ".join(state), } continue - comps = line.split(' : ') + comps = line.split(" : ") comps[0] = comps[0].lower() comps[0] = comps[0].strip() - comps[0] = comps[0].replace(' ', '_') + comps[0] = comps[0].replace(" ", "_") ret[comps[0]] = comps[1].strip() return ret def destroy(device): - ''' + """ Destroy a RAID device. WARNING This will zero the superblock of all members of the RAID array.. @@ -122,39 +127,39 @@ def destroy(device): .. code-block:: bash salt '*' raid.destroy /dev/md0 - ''' + """ try: details = detail(device) except CommandExecutionError: return False - stop_cmd = ['mdadm', '--stop', device] - zero_cmd = ['mdadm', '--zero-superblock'] + stop_cmd = ["mdadm", "--stop", device] + zero_cmd = ["mdadm", "--zero-superblock"] - if __salt__['cmd.retcode'](stop_cmd, python_shell=False): - for number in details['members']: - zero_cmd.append(details['members'][number]['device']) - __salt__['cmd.retcode'](zero_cmd, python_shell=False) + if __salt__["cmd.retcode"](stop_cmd, python_shell=False): + for number in details["members"]: + zero_cmd.append(details["members"][number]["device"]) + __salt__["cmd.retcode"](zero_cmd, python_shell=False) # Remove entry from config file: - if __grains__.get('os_family') == 'Debian': - cfg_file = '/etc/mdadm/mdadm.conf' + if __grains__.get("os_family") == "Debian": + cfg_file = "/etc/mdadm/mdadm.conf" else: - cfg_file = '/etc/mdadm.conf' + cfg_file = "/etc/mdadm.conf" try: - __salt__['file.replace'](cfg_file, 'ARRAY {0} .*'.format(device), '') + __salt__["file.replace"](cfg_file, "ARRAY {0} .*".format(device), "") except SaltInvocationError: pass - if __salt__['raid.list']().get(device) is None: + if __salt__["raid.list"]().get(device) is None: return True else: return False def stop(): - ''' + """ Shut down all arrays that can be shut down (i.e. are not currently in use). CLI Example: @@ -162,22 +167,17 @@ def stop(): .. code-block:: bash salt '*' raid.stop - ''' - cmd = 'mdadm --stop --scan' + """ + cmd = "mdadm --stop --scan" - if __salt__['cmd.retcode'](cmd): + if __salt__["cmd.retcode"](cmd): return True return False -def create(name, - level, - devices, - metadata='default', - test_mode=False, - **kwargs): - ''' +def create(name, level, devices, metadata="default", test_mode=False, **kwargs): + """ Create a RAID device. .. versionchanged:: 2014.7.0 @@ -229,38 +229,35 @@ def create(name, salt '*' raid.detail /dev/md0 For more info, read the ``mdadm(8)`` manpage - ''' + """ opts = [] raid_devices = len(devices) for key in kwargs: - if not key.startswith('__'): - opts.append('--{0}'.format(key)) + if not key.startswith("__"): + opts.append("--{0}".format(key)) if kwargs[key] is not True: opts.append(six.text_type(kwargs[key])) - if key == 'spare-devices': + if key == "spare-devices": raid_devices -= int(kwargs[key]) - cmd = ['mdadm', - '-C', name, - '-R', - '-v', - '-l', six.text_type(level), - ] + opts + [ - '-e', six.text_type(metadata), - '-n', six.text_type(raid_devices), - ] + devices + cmd = ( + ["mdadm", "-C", name, "-R", "-v", "-l", six.text_type(level)] + + opts + + ["-e", six.text_type(metadata), "-n", six.text_type(raid_devices)] + + devices + ) - cmd_str = ' '.join(cmd) + cmd_str = " ".join(cmd) if test_mode is True: return cmd_str elif test_mode is False: - return __salt__['cmd.run'](cmd, python_shell=False) + return __salt__["cmd.run"](cmd, python_shell=False) def save_config(): - ''' + """ Save RAID configuration to config file. Same as: @@ -275,42 +272,41 @@ def save_config(): salt '*' raid.save_config - ''' - scan = __salt__['cmd.run']('mdadm --detail --scan', python_shell=False).splitlines() + """ + scan = __salt__["cmd.run"]("mdadm --detail --scan", python_shell=False).splitlines() # Issue with mdadm and ubuntu # REF: http://askubuntu.com/questions/209702/why-is-my-raid-dev-md1-showing-up-as-dev-md126-is-mdadm-conf-being-ignored - if __grains__['os'] == 'Ubuntu': - buggy_ubuntu_tags = ['name', 'metadata'] + if __grains__["os"] == "Ubuntu": + buggy_ubuntu_tags = ["name", "metadata"] for i, elem in enumerate(scan): for bad_tag in buggy_ubuntu_tags: - pattern = r'\s{0}=\S+'.format(re.escape(bad_tag)) + pattern = r"\s{0}=\S+".format(re.escape(bad_tag)) pattern = re.compile(pattern, flags=re.I) - scan[i] = re.sub(pattern, '', scan[i]) + scan[i] = re.sub(pattern, "", scan[i]) - if __grains__.get('os_family') == 'Debian': - cfg_file = '/etc/mdadm/mdadm.conf' + if __grains__.get("os_family") == "Debian": + cfg_file = "/etc/mdadm/mdadm.conf" else: - cfg_file = '/etc/mdadm.conf' + cfg_file = "/etc/mdadm.conf" try: vol_d = dict([(line.split()[1], line) for line in scan]) for vol in vol_d: - pattern = r'^ARRAY\s+{0}.*$'.format(re.escape(vol)) - __salt__['file.replace'](cfg_file, pattern, vol_d[vol], append_if_not_found=True) + pattern = r"^ARRAY\s+{0}.*$".format(re.escape(vol)) + __salt__["file.replace"]( + cfg_file, pattern, vol_d[vol], append_if_not_found=True + ) except SaltInvocationError: # File is missing - __salt__['file.write'](cfg_file, args=scan) + __salt__["file.write"](cfg_file, args=scan) - if __grains__.get('os_family') == 'Debian': - return __salt__['cmd.run']('update-initramfs -u') - elif __grains__.get('os_family') == 'RedHat': - return __salt__['cmd.run']('dracut --force') + if __grains__.get("os_family") == "Debian": + return __salt__["cmd.run"]("update-initramfs -u") + elif __grains__.get("os_family") == "RedHat": + return __salt__["cmd.run"]("dracut --force") -def assemble(name, - devices, - test_mode=False, - **kwargs): - ''' +def assemble(name, devices, test_mode=False, **kwargs): + """ Assemble a RAID device. CLI Examples: @@ -340,28 +336,28 @@ def assemble(name, Executes command on the host(s) and prints out the mdadm output. For more info, read the ``mdadm`` manpage. - ''' + """ opts = [] for key in kwargs: - if not key.startswith('__'): - opts.append('--{0}'.format(key)) + if not key.startswith("__"): + opts.append("--{0}".format(key)) if kwargs[key] is not True: opts.append(kwargs[key]) # Devices may have been written with a blob: if isinstance(devices, six.string_types): - devices = devices.split(',') + devices = devices.split(",") - cmd = ['mdadm', '-A', name, '-v'] + opts + devices + cmd = ["mdadm", "-A", name, "-v"] + opts + devices if test_mode is True: return cmd elif test_mode is False: - return __salt__['cmd.run'](cmd, python_shell=False) + return __salt__["cmd.run"](cmd, python_shell=False) def examine(device, quiet=False): - ''' + """ Show detail for a specified RAID component device device @@ -375,10 +371,10 @@ def examine(device, quiet=False): .. code-block:: bash salt '*' raid.examine '/dev/sda1' - ''' - res = __salt__['cmd.run_stdout']('mdadm -Y -E {0}'.format(device), - python_shell=False, - ignore_retcode=quiet) + """ + res = __salt__["cmd.run_stdout"]( + "mdadm -Y -E {0}".format(device), python_shell=False, ignore_retcode=quiet + ) ret = {} for line in res.splitlines(): @@ -388,7 +384,7 @@ def examine(device, quiet=False): def add(name, device): - ''' + """ Add new device to RAID array. CLI Example: @@ -397,9 +393,9 @@ def add(name, device): salt '*' raid.add /dev/md0 /dev/sda1 - ''' + """ - cmd = 'mdadm --manage {0} --add {1}'.format(name, device) - if __salt__['cmd.retcode'](cmd) == 0: + cmd = "mdadm --manage {0} --add {1}".format(name, device) + if __salt__["cmd.retcode"](cmd) == 0: return True return False diff --git a/salt/modules/mdata.py b/salt/modules/mdata.py index d271ea845c0..529ac50a680 100644 --- a/salt/modules/mdata.py +++ b/salt/modules/mdata.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for managaging metadata in SmartOS Zones .. versionadded:: 2016.3.0 @@ -7,79 +7,79 @@ Module for managaging metadata in SmartOS Zones :maintainer: Jorge Schrauwen :maturity: new :platform: smartos -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.decorators as decorators + # Import Salt libs import salt.utils.path import salt.utils.platform -import salt.utils.decorators as decorators log = logging.getLogger(__name__) # Function aliases __func_alias__ = { - 'list_': 'list', - 'get_': 'get', - 'put_': 'put', - 'delete_': 'delete', + "list_": "list", + "get_": "get", + "put_": "put", + "delete_": "delete", } # Define the module's virtual name -__virtualname__ = 'mdata' +__virtualname__ = "mdata" @decorators.memoize def _check_mdata_list(): - ''' + """ looks to see if mdata-list is present on the system - ''' - return salt.utils.path.which('mdata-list') + """ + return salt.utils.path.which("mdata-list") @decorators.memoize def _check_mdata_get(): - ''' + """ looks to see if mdata-get is present on the system - ''' - return salt.utils.path.which('mdata-get') + """ + return salt.utils.path.which("mdata-get") @decorators.memoize def _check_mdata_put(): - ''' + """ looks to see if mdata-put is present on the system - ''' - return salt.utils.path.which('mdata-put') + """ + return salt.utils.path.which("mdata-put") @decorators.memoize def _check_mdata_delete(): - ''' + """ looks to see if mdata-delete is present on the system - ''' - return salt.utils.path.which('mdata-delete') + """ + return salt.utils.path.which("mdata-delete") def __virtual__(): - ''' + """ Provides mdata only on SmartOS - ''' + """ if _check_mdata_list() and not salt.utils.platform.is_smartos_globalzone(): return __virtualname__ return ( False, - '{0} module can only be loaded on SmartOS zones'.format( - __virtualname__ - ) + "{0} module can only be loaded on SmartOS zones".format(__virtualname__), ) def list_(): - ''' + """ List available metadata CLI Example: @@ -87,16 +87,16 @@ def list_(): .. code-block:: bash salt '*' mdata.list - ''' + """ mdata = _check_mdata_list() if mdata: - cmd = '{0}'.format(mdata) - return __salt__['cmd.run'](cmd, ignore_retcode=True).splitlines() + cmd = "{0}".format(mdata) + return __salt__["cmd.run"](cmd, ignore_retcode=True).splitlines() return {} def get_(*keyname): - ''' + """ Get metadata keyname : string @@ -112,7 +112,7 @@ def get_(*keyname): salt '*' mdata.get salt:role salt '*' mdata.get user-script salt:role - ''' + """ mdata = _check_mdata_get() ret = {} @@ -121,17 +121,17 @@ def get_(*keyname): for k in keyname: if mdata: - cmd = '{0} {1}'.format(mdata, k) - res = __salt__['cmd.run_all'](cmd, ignore_retcode=True) - ret[k] = res['stdout'] if res['retcode'] == 0 else '' + cmd = "{0} {1}".format(mdata, k) + res = __salt__["cmd.run_all"](cmd, ignore_retcode=True) + ret[k] = res["stdout"] if res["retcode"] == 0 else "" else: - ret[k] = '' + ret[k] = "" return ret def put_(keyname, val): - ''' + """ Put metadata prop : string @@ -144,19 +144,19 @@ def put_(keyname, val): .. code-block:: bash salt '*' mdata.list - ''' + """ mdata = _check_mdata_put() ret = {} if mdata: - cmd = 'echo {2} | {0} {1}'.format(mdata, keyname, val) - ret = __salt__['cmd.run_all'](cmd, python_shell=True, ignore_retcode=True) + cmd = "echo {2} | {0} {1}".format(mdata, keyname, val) + ret = __salt__["cmd.run_all"](cmd, python_shell=True, ignore_retcode=True) - return ret['retcode'] == 0 + return ret["retcode"] == 0 def delete_(*keyname): - ''' + """ Delete metadata prop : string @@ -168,18 +168,19 @@ def delete_(*keyname): salt '*' mdata.get salt:role salt '*' mdata.get user-script salt:role - ''' + """ mdata = _check_mdata_delete() valid_keynames = list_() ret = {} for k in keyname: if mdata and k in valid_keynames: - cmd = '{0} {1}'.format(mdata, k) - ret[k] = __salt__['cmd.run_all'](cmd, ignore_retcode=True)['retcode'] == 0 + cmd = "{0} {1}".format(mdata, k) + ret[k] = __salt__["cmd.run_all"](cmd, ignore_retcode=True)["retcode"] == 0 else: ret[k] = True return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/memcached.py b/salt/modules/memcached.py index d3371b2ae24..16bfbc3bc4f 100644 --- a/salt/modules/memcached.py +++ b/salt/modules/memcached.py @@ -1,13 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Module for Management of Memcached Keys .. versionadded:: 2014.1.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# TODO: use salt.utils.memcache - # Import python libs import logging @@ -16,14 +14,18 @@ import salt.utils.functools from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six +# TODO: use salt.utils.memcache + + # Import third party libs try: import memcache + HAS_MEMCACHE = True except ImportError: HAS_MEMCACHE = False -DEFAULT_HOST = '127.0.0.1' +DEFAULT_HOST = "127.0.0.1" DEFAULT_PORT = 11211 DEFAULT_TIME = 0 DEFAULT_MIN_COMPRESS_LEN = 0 @@ -31,48 +33,47 @@ DEFAULT_MIN_COMPRESS_LEN = 0 log = logging.getLogger(__name__) # Don't shadow built-ins -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} -__virtualname__ = 'memcached' +__virtualname__ = "memcached" def __virtual__(): - ''' + """ Only load if python-memcache is installed - ''' + """ if HAS_MEMCACHE: return __virtualname__ - return (False, 'The memcached execution module cannot be loaded: ' - 'python memcache library not available.') + return ( + False, + "The memcached execution module cannot be loaded: " + "python memcache library not available.", + ) def _connect(host=DEFAULT_HOST, port=DEFAULT_PORT): - ''' + """ Returns a tuple of (user, host, port) with config, pillar, or default values assigned to missing values. - ''' + """ if six.text_type(port).isdigit(): - return memcache.Client(['{0}:{1}'.format(host, port)], debug=0) - raise SaltInvocationError('port must be an integer') + return memcache.Client(["{0}:{1}".format(host, port)], debug=0) + raise SaltInvocationError("port must be an integer") def _check_stats(conn): - ''' + """ Helper function to check the stats data passed into it, and raise an exception if none are returned. Otherwise, the stats are returned. - ''' + """ stats = conn.get_stats() if not stats: - raise CommandExecutionError( - 'memcached server is down or does not exist' - ) + raise CommandExecutionError("memcached server is down or does not exist") return stats def status(host=DEFAULT_HOST, port=DEFAULT_PORT): - ''' + """ Get memcached status CLI Example: @@ -80,7 +81,7 @@ def status(host=DEFAULT_HOST, port=DEFAULT_PORT): .. code-block:: bash salt '*' memcached.status - ''' + """ conn = _connect(host, port) try: stats = _check_stats(conn)[0] @@ -91,7 +92,7 @@ def status(host=DEFAULT_HOST, port=DEFAULT_PORT): def get(key, host=DEFAULT_HOST, port=DEFAULT_PORT): - ''' + """ Retrieve value for a key CLI Example: @@ -99,19 +100,21 @@ def get(key, host=DEFAULT_HOST, port=DEFAULT_PORT): .. code-block:: bash salt '*' memcached.get - ''' + """ conn = _connect(host, port) _check_stats(conn) return conn.get(key) -def set_(key, - value, - host=DEFAULT_HOST, - port=DEFAULT_PORT, - time=DEFAULT_TIME, - min_compress_len=DEFAULT_MIN_COMPRESS_LEN): - ''' +def set_( + key, + value, + host=DEFAULT_HOST, + port=DEFAULT_PORT, + time=DEFAULT_TIME, + min_compress_len=DEFAULT_MIN_COMPRESS_LEN, +): + """ Set a key on the memcached server, overwriting the value if it exists. CLI Example: @@ -119,21 +122,18 @@ def set_(key, .. code-block:: bash salt '*' memcached.set - ''' + """ if not isinstance(time, six.integer_types): - raise SaltInvocationError('\'time\' must be an integer') + raise SaltInvocationError("'time' must be an integer") if not isinstance(min_compress_len, six.integer_types): - raise SaltInvocationError('\'min_compress_len\' must be an integer') + raise SaltInvocationError("'min_compress_len' must be an integer") conn = _connect(host, port) _check_stats(conn) return conn.set(key, value, time, min_compress_len) -def delete(key, - host=DEFAULT_HOST, - port=DEFAULT_PORT, - time=DEFAULT_TIME): - ''' +def delete(key, host=DEFAULT_HOST, port=DEFAULT_PORT, time=DEFAULT_TIME): + """ Delete a key from memcache server CLI Example: @@ -141,21 +141,23 @@ def delete(key, .. code-block:: bash salt '*' memcached.delete - ''' + """ if not isinstance(time, six.integer_types): - raise SaltInvocationError('\'time\' must be an integer') + raise SaltInvocationError("'time' must be an integer") conn = _connect(host, port) _check_stats(conn) return bool(conn.delete(key, time)) -def add(key, - value, - host=DEFAULT_HOST, - port=DEFAULT_PORT, - time=DEFAULT_TIME, - min_compress_len=DEFAULT_MIN_COMPRESS_LEN): - ''' +def add( + key, + value, + host=DEFAULT_HOST, + port=DEFAULT_PORT, + time=DEFAULT_TIME, + min_compress_len=DEFAULT_MIN_COMPRESS_LEN, +): + """ Add a key to the memcached server, but only if it does not exist. Returns False if the key already exists. @@ -164,28 +166,25 @@ def add(key, .. code-block:: bash salt '*' memcached.add - ''' + """ if not isinstance(time, six.integer_types): - raise SaltInvocationError('\'time\' must be an integer') + raise SaltInvocationError("'time' must be an integer") if not isinstance(min_compress_len, six.integer_types): - raise SaltInvocationError('\'min_compress_len\' must be an integer') + raise SaltInvocationError("'min_compress_len' must be an integer") conn = _connect(host, port) _check_stats(conn) - return conn.add( - key, - value, - time=time, - min_compress_len=min_compress_len - ) + return conn.add(key, value, time=time, min_compress_len=min_compress_len) -def replace(key, - value, - host=DEFAULT_HOST, - port=DEFAULT_PORT, - time=DEFAULT_TIME, - min_compress_len=DEFAULT_MIN_COMPRESS_LEN): - ''' +def replace( + key, + value, + host=DEFAULT_HOST, + port=DEFAULT_PORT, + time=DEFAULT_TIME, + min_compress_len=DEFAULT_MIN_COMPRESS_LEN, +): + """ Replace a key on the memcached server. This only succeeds if the key already exists. This is the opposite of :mod:`memcached.add ` @@ -195,23 +194,18 @@ def replace(key, .. code-block:: bash salt '*' memcached.replace - ''' + """ if not isinstance(time, six.integer_types): - raise SaltInvocationError('\'time\' must be an integer') + raise SaltInvocationError("'time' must be an integer") if not isinstance(min_compress_len, six.integer_types): - raise SaltInvocationError('\'min_compress_len\' must be an integer') + raise SaltInvocationError("'min_compress_len' must be an integer") conn = _connect(host, port) stats = conn.get_stats() - return conn.replace( - key, - value, - time=time, - min_compress_len=min_compress_len - ) + return conn.replace(key, value, time=time, min_compress_len=min_compress_len) def increment(key, delta=1, host=DEFAULT_HOST, port=DEFAULT_PORT): - ''' + """ Increment the value of a key CLI Example: @@ -220,30 +214,29 @@ def increment(key, delta=1, host=DEFAULT_HOST, port=DEFAULT_PORT): salt '*' memcached.increment salt '*' memcached.increment 2 - ''' + """ conn = _connect(host, port) _check_stats(conn) cur = get(key) if cur is None: - raise CommandExecutionError('Key \'{0}\' does not exist'.format(key)) + raise CommandExecutionError("Key '{0}' does not exist".format(key)) elif not isinstance(cur, six.integer_types): raise CommandExecutionError( - 'Value for key \'{0}\' must be an integer to be ' - 'incremented'.format(key) + "Value for key '{0}' must be an integer to be " "incremented".format(key) ) try: return conn.incr(key, delta) except ValueError: - raise SaltInvocationError('Delta value must be an integer') + raise SaltInvocationError("Delta value must be an integer") -incr = salt.utils.functools.alias_function(increment, 'incr') +incr = salt.utils.functools.alias_function(increment, "incr") def decrement(key, delta=1, host=DEFAULT_HOST, port=DEFAULT_PORT): - ''' + """ Decrement the value of a key CLI Example: @@ -252,23 +245,22 @@ def decrement(key, delta=1, host=DEFAULT_HOST, port=DEFAULT_PORT): salt '*' memcached.decrement salt '*' memcached.decrement 2 - ''' + """ conn = _connect(host, port) _check_stats(conn) cur = get(key) if cur is None: - raise CommandExecutionError('Key \'{0}\' does not exist'.format(key)) + raise CommandExecutionError("Key '{0}' does not exist".format(key)) elif not isinstance(cur, six.integer_types): raise CommandExecutionError( - 'Value for key \'{0}\' must be an integer to be ' - 'decremented'.format(key) + "Value for key '{0}' must be an integer to be " "decremented".format(key) ) try: return conn.decr(key, delta) except ValueError: - raise SaltInvocationError('Delta value must be an integer') + raise SaltInvocationError("Delta value must be an integer") -decr = salt.utils.functools.alias_function(decrement, 'decr') +decr = salt.utils.functools.alias_function(decrement, "decr") diff --git a/salt/modules/mine.py b/salt/modules/mine.py index 9a2ee58a0cb..cde4d15b224 100644 --- a/salt/modules/mine.py +++ b/salt/modules/mine.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" The function cache system allows for data to be stored on the master so it can be easily read by other minions -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time import traceback @@ -12,57 +13,61 @@ import traceback # Import salt libs import salt.crypt import salt.payload -import salt.utils.args -import salt.utils.event -import salt.utils.network import salt.transport.client +import salt.utils.args +import salt.utils.dictupdate +import salt.utils.event +import salt.utils.functools import salt.utils.mine import salt.utils.minions -import salt.utils.dictupdate -import salt.utils.functools +import salt.utils.network from salt.exceptions import SaltClientError # Import 3rd-party libs from salt.ext import six -MINE_INTERNAL_KEYWORDS = frozenset([ - '__pub_user', - '__pub_arg', - '__pub_fun', - '__pub_jid', - '__pub_tgt', - '__pub_tgt_type', - '__pub_ret' -]) +MINE_INTERNAL_KEYWORDS = frozenset( + [ + "__pub_user", + "__pub_arg", + "__pub_fun", + "__pub_jid", + "__pub_tgt", + "__pub_tgt_type", + "__pub_ret", + ] +) -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] log = logging.getLogger(__name__) def _auth(): - ''' + """ Return the auth object - ''' - if 'auth' not in __context__: + """ + if "auth" not in __context__: try: - __context__['auth'] = salt.crypt.SAuth(__opts__) + __context__["auth"] = salt.crypt.SAuth(__opts__) except SaltClientError: - log.error('Could not authenticate with master.' - 'Mine data will not be transmitted.') - return __context__['auth'] + log.error( + "Could not authenticate with master." + "Mine data will not be transmitted." + ) + return __context__["auth"] def _mine_function_available(func): if func not in __salt__: - log.error('Function %s in mine_functions not available', func) + log.error("Function %s in mine_functions not available", func) return False return True def _mine_send(load, opts): eventer = salt.utils.event.MinionEvent(opts, listen=False) - event_ret = eventer.fire_event(load, '_minion_mine') + event_ret = eventer.fire_event(load, "_minion_mine") # We need to pause here to allow for the decoupled nature of # events time to allow the mine to propagate time.sleep(0.5) @@ -70,22 +75,21 @@ def _mine_send(load, opts): def _mine_get(load, opts): - if opts.get('transport', '') in ('zeromq', 'tcp'): + if opts.get("transport", "") in ("zeromq", "tcp"): try: - load['tok'] = _auth().gen_token(b'salt') + load["tok"] = _auth().gen_token(b"salt") except AttributeError: - log.error('Mine could not authenticate with master. ' - 'Mine could not be retrieved.' - ) + log.error( + "Mine could not authenticate with master. " + "Mine could not be retrieved." + ) return False with salt.transport.client.ReqChannel.factory(opts) as channel: return channel.send(load) -def _mine_store( - mine_data, - clear=False): - ''' +def _mine_store(mine_data, clear=False): + """ Helper function to store the provided mine data. This will store either locally in the cache (for masterless setups), or in the master's cache. @@ -93,27 +97,27 @@ def _mine_store( :param dict mine_data: Dictionary with function_name: function_data to store. :param bool clear: Whether or not to clear (`True`) the mine data for the function names present in ``mine_data``, or update it (`False`). - ''' + """ # Store in the salt-minion's local cache - if __opts__['file_client'] == 'local': + if __opts__["file_client"] == "local": if not clear: - old = __salt__['data.get']('mine_cache') + old = __salt__["data.get"]("mine_cache") if isinstance(old, dict): old.update(mine_data) mine_data = old - return __salt__['data.update']('mine_cache', mine_data) + return __salt__["data.update"]("mine_cache", mine_data) # Store on the salt master load = { - 'cmd': '_mine', - 'data': mine_data, - 'id': __opts__['id'], - 'clear': clear, + "cmd": "_mine", + "data": mine_data, + "id": __opts__["id"], + "clear": clear, } return _mine_send(load, __opts__) def update(clear=False, mine_functions=None): - ''' + """ Call the configured functions and send the data back up to the master. The functions to be called are merged from the master config, pillar and minion config under the option `mine_functions`: @@ -164,9 +168,9 @@ def update(clear=False, mine_functions=None): .. code-block:: bash salt '*' mine.update - ''' + """ if not mine_functions: - mine_functions = __salt__['config.merge']('mine_functions', {}) + mine_functions = __salt__["config.merge"]("mine_functions", {}) # If we don't have any mine functions configured, then we should just bail out if not mine_functions: return @@ -179,8 +183,12 @@ def update(clear=False, mine_functions=None): mine_data = {} for function_alias, function_data in six.iteritems(mine_functions): - function_name, function_args, function_kwargs, minion_acl = \ - salt.utils.mine.parse_function_definition(function_data) + ( + function_name, + function_args, + function_kwargs, + minion_acl, + ) = salt.utils.mine.parse_function_definition(function_data) if not _mine_function_available(function_name or function_alias): continue try: @@ -191,13 +199,15 @@ def update(clear=False, mine_functions=None): ) except Exception: # pylint: disable=broad-except trace = traceback.format_exc() - log.error('Function %s in mine.update failed to execute', function_name or function_alias) - log.debug('Error: %s', trace) + log.error( + "Function %s in mine.update failed to execute", + function_name or function_alias, + ) + log.debug("Error: %s", trace) continue - if minion_acl.get('allow_tgt'): + if minion_acl.get("allow_tgt"): mine_data[function_alias] = salt.utils.mine.wrap_acl_structure( - res, - **minion_acl + res, **minion_acl ) else: mine_data[function_alias] = res @@ -205,7 +215,7 @@ def update(clear=False, mine_functions=None): def send(name, *args, **kwargs): - ''' + """ Send a specific function and its result to the salt mine. This gets stored in either the local cache, or the salt master's cache. @@ -242,40 +252,33 @@ def send(name, *args, **kwargs): salt '*' mine.send network.ip_addrs eth0 salt '*' mine.send eth0_ip_addrs mine_function=network.ip_addrs eth0 salt '*' mine.send eth0_ip_addrs mine_function=network.ip_addrs eth0 allow_tgt='G@grain:value' allow_tgt_type=compound - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - mine_function = kwargs.pop('mine_function', None) - allow_tgt = kwargs.pop('allow_tgt', None) - allow_tgt_type = kwargs.pop('allow_tgt_type', None) + mine_function = kwargs.pop("mine_function", None) + allow_tgt = kwargs.pop("allow_tgt", None) + allow_tgt_type = kwargs.pop("allow_tgt_type", None) mine_data = {} try: res = salt.utils.functools.call_function( - __salt__[mine_function or name], - *args, - **kwargs + __salt__[mine_function or name], *args, **kwargs ) except Exception as exc: # pylint: disable=broad-except trace = traceback.format_exc() - log.error('Function %s in mine.send failed to execute', mine_function or name) - log.debug('Error: %s', trace) + log.error("Function %s in mine.send failed to execute", mine_function or name) + log.debug("Error: %s", trace) return False if allow_tgt: mine_data[name] = salt.utils.mine.wrap_acl_structure( - res, - allow_tgt=allow_tgt, - allow_tgt_type=allow_tgt_type + res, allow_tgt=allow_tgt, allow_tgt_type=allow_tgt_type ) else: mine_data[name] = res return _mine_store(mine_data) -def get(tgt, - fun, - tgt_type='glob', - exclude_minion=False): - ''' +def get(tgt, fun, tgt_type="glob", exclude_minion=False): + """ Get data from the mine. :param str tgt: Target whose mine data to get. @@ -311,29 +314,30 @@ def get(tgt, tgt='*', fun='network.ip_addrs', tgt_type='glob') %} - ''' + """ # Load from local minion's cache - if __opts__['file_client'] == 'local': + if __opts__["file_client"] == "local": ret = {} - is_target = {'glob': __salt__['match.glob'], - 'pcre': __salt__['match.pcre'], - 'list': __salt__['match.list'], - 'grain': __salt__['match.grain'], - 'grain_pcre': __salt__['match.grain_pcre'], - 'ipcidr': __salt__['match.ipcidr'], - 'compound': __salt__['match.compound'], - 'pillar': __salt__['match.pillar'], - 'pillar_pcre': __salt__['match.pillar_pcre'], - }[tgt_type](tgt) + is_target = { + "glob": __salt__["match.glob"], + "pcre": __salt__["match.pcre"], + "list": __salt__["match.list"], + "grain": __salt__["match.grain"], + "grain_pcre": __salt__["match.grain_pcre"], + "ipcidr": __salt__["match.ipcidr"], + "compound": __salt__["match.compound"], + "pillar": __salt__["match.pillar"], + "pillar_pcre": __salt__["match.pillar_pcre"], + }[tgt_type](tgt) if not is_target: return ret - data = __salt__['data.get']('mine_cache') + data = __salt__["data.get"]("mine_cache") if not isinstance(data, dict): return ret if isinstance(fun, six.string_types): - functions = list(set(fun.split(','))) + functions = list(set(fun.split(","))) _ret_dict = len(functions) > 1 elif isinstance(fun, list): functions = fun @@ -351,27 +355,27 @@ def get(tgt, # Backwards compatibility with non-ACL mine data. res = data[function] if _ret_dict: - ret.setdefault(function, {})[__opts__['id']] = res + ret.setdefault(function, {})[__opts__["id"]] = res else: - ret[__opts__['id']] = res + ret[__opts__["id"]] = res return ret # Load from master load = { - 'cmd': '_mine_get', - 'id': __opts__['id'], - 'tgt': tgt, - 'fun': fun, - 'tgt_type': tgt_type, + "cmd": "_mine_get", + "id": __opts__["id"], + "tgt": tgt, + "fun": fun, + "tgt_type": tgt_type, } ret = _mine_get(load, __opts__) - if exclude_minion and __opts__['id'] in ret: - del ret[__opts__['id']] + if exclude_minion and __opts__["id"] in ret: + del ret[__opts__["id"]] return ret def delete(fun): - ''' + """ Remove specific function contents of minion. :param str fun: The name of the function. @@ -383,22 +387,22 @@ def delete(fun): .. code-block:: bash salt '*' mine.delete 'network.interfaces' - ''' - if __opts__['file_client'] == 'local': - data = __salt__['data.get']('mine_cache') + """ + if __opts__["file_client"] == "local": + data = __salt__["data.get"]("mine_cache") if isinstance(data, dict) and fun in data: del data[fun] - return __salt__['data.update']('mine_cache', data) + return __salt__["data.update"]("mine_cache", data) load = { - 'cmd': '_mine_delete', - 'id': __opts__['id'], - 'fun': fun, + "cmd": "_mine_delete", + "id": __opts__["id"], + "fun": fun, } return _mine_send(load, __opts__) def flush(): - ''' + """ Remove all mine contents of minion. :rtype: bool @@ -409,18 +413,18 @@ def flush(): .. code-block:: bash salt '*' mine.flush - ''' - if __opts__['file_client'] == 'local': - return __salt__['data.update']('mine_cache', {}) + """ + if __opts__["file_client"] == "local": + return __salt__["data.update"]("mine_cache", {}) load = { - 'cmd': '_mine_flush', - 'id': __opts__['id'], + "cmd": "_mine_flush", + "id": __opts__["id"], } return _mine_send(load, __opts__) def get_docker(interfaces=None, cidrs=None, with_container_id=False): - ''' + """ .. versionchanged:: 2017.7.8,2018.3.3 When :conf_minion:`docker.update_mine` is set to ``False`` for a given minion, no mine data will be populated for that minion, and thus none @@ -450,7 +454,7 @@ def get_docker(interfaces=None, cidrs=None, with_container_id=False): salt '*' mine.get_docker cidrs='107.170.147.0/24' salt '*' mine.get_docker cidrs='["107.170.147.0/24", "172.17.42.0/24"]' salt '*' mine.get_docker interfaces='["eth0", "eth1"]' cidrs='["107.170.147.0/24", "172.17.42.0/24"]' - ''' + """ # Enforce that interface and cidr are lists if interfaces: interface_ = [] @@ -462,28 +466,28 @@ def get_docker(interfaces=None, cidrs=None, with_container_id=False): cidrs = cidr_ # Get docker info - cmd = 'docker.ps' - docker_hosts = get('*', cmd) + cmd = "docker.ps" + docker_hosts = get("*", cmd) proxy_lists = {} # Process docker info for containers in six.itervalues(docker_hosts): - host = containers.pop('host') + host = containers.pop("host") host_ips = [] # Prepare host_ips list if not interfaces: - for info in six.itervalues(host['interfaces']): - if 'inet' in info: - for ip_ in info['inet']: - host_ips.append(ip_['address']) + for info in six.itervalues(host["interfaces"]): + if "inet" in info: + for ip_ in info["inet"]: + host_ips.append(ip_["address"]) else: for interface in interfaces: - if interface in host['interfaces']: - if 'inet' in host['interfaces'][interface]: - for item in host['interfaces'][interface]['inet']: - host_ips.append(item['address']) + if interface in host["interfaces"]: + if "inet" in host["interfaces"][interface]: + for item in host["interfaces"][interface]["inet"]: + host_ips.append(item["address"]) host_ips = list(set(host_ips)) # Filter out ips from host_ips with cidrs @@ -497,17 +501,23 @@ def get_docker(interfaces=None, cidrs=None, with_container_id=False): # Process each container for container in six.itervalues(containers): - container_id = container['Info']['Id'] - if container['Image'] not in proxy_lists: - proxy_lists[container['Image']] = {} - for dock_port in container['Ports']: + container_id = container["Info"]["Id"] + if container["Image"] not in proxy_lists: + proxy_lists[container["Image"]] = {} + for dock_port in container["Ports"]: # IP exists only if port is exposed - ip_address = dock_port.get('IP') + ip_address = dock_port.get("IP") # If port is 0.0.0.0, then we must get the docker host IP - if ip_address == '0.0.0.0': + if ip_address == "0.0.0.0": for ip_ in host_ips: - containers = proxy_lists[container['Image']].setdefault('ipv4', {}).setdefault(dock_port['PrivatePort'], []) - container_network_footprint = '{0}:{1}'.format(ip_, dock_port['PublicPort']) + containers = ( + proxy_lists[container["Image"]] + .setdefault("ipv4", {}) + .setdefault(dock_port["PrivatePort"], []) + ) + container_network_footprint = "{0}:{1}".format( + ip_, dock_port["PublicPort"] + ) if with_container_id: value = (container_network_footprint, container_id) else: @@ -515,8 +525,14 @@ def get_docker(interfaces=None, cidrs=None, with_container_id=False): if value not in containers: containers.append(value) elif ip_address: - containers = proxy_lists[container['Image']].setdefault('ipv4', {}).setdefault(dock_port['PrivatePort'], []) - container_network_footprint = '{0}:{1}'.format(dock_port['IP'], dock_port['PublicPort']) + containers = ( + proxy_lists[container["Image"]] + .setdefault("ipv4", {}) + .setdefault(dock_port["PrivatePort"], []) + ) + container_network_footprint = "{0}:{1}".format( + dock_port["IP"], dock_port["PublicPort"] + ) if with_container_id: value = (container_network_footprint, container_id) else: @@ -528,7 +544,7 @@ def get_docker(interfaces=None, cidrs=None, with_container_id=False): def valid(): - ''' + """ List valid entries in mine configuration. CLI Example: @@ -536,21 +552,26 @@ def valid(): .. code-block:: bash salt '*' mine.valid - ''' - mine_functions = __salt__['config.merge']('mine_functions', {}) + """ + mine_functions = __salt__["config.merge"]("mine_functions", {}) # If we don't have any mine functions configured, then we should just bail out if not mine_functions: return mine_data = {} for function_alias, function_data in six.iteritems(mine_functions): - function_name, function_args, function_kwargs, minion_acl = \ - salt.utils.mine.parse_function_definition(function_data) + ( + function_name, + function_args, + function_kwargs, + minion_acl, + ) = salt.utils.mine.parse_function_definition(function_data) if not _mine_function_available(function_name or function_alias): continue if function_name: mine_data[function_alias] = { - function_name: function_args + [{key, value} for key, value in six.iteritems(function_kwargs)] + function_name: function_args + + [{key, value} for key, value in six.iteritems(function_kwargs)] } else: mine_data[function_alias] = function_data diff --git a/salt/modules/minion.py b/salt/modules/minion.py index 76fd832b2e6..e424cfee6ea 100644 --- a/salt/modules/minion.py +++ b/salt/modules/minion.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide information about minions -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -9,22 +9,21 @@ import os import sys import time +import salt.key + # Import Salt libs import salt.utils.data -import salt.key # Import third party libs from salt.ext import six from salt.ext.six.moves import range # Don't shadow built-ins. -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} def list_(): - ''' + """ Return a list of accepted, denied, unaccepted and rejected keys. This is the same output as `salt-key -L` @@ -33,11 +32,11 @@ def list_(): .. code-block:: bash salt 'master' minion.list - ''' - pki_dir = __salt__['config.get']('pki_dir', '') + """ + pki_dir = __salt__["config.get"]("pki_dir", "") # We have to replace the minion/master directories - pki_dir = pki_dir.replace('minion', 'master') + pki_dir = pki_dir.replace("minion", "master") # The source code below is (nearly) a copy of salt.key.Key.list_keys key_dirs = _check_minions_directories(pki_dir) @@ -48,7 +47,7 @@ def list_(): ret[os.path.basename(dir_)] = [] try: for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(dir_)): - if not fn_.startswith('.'): + if not fn_.startswith("."): if os.path.isfile(os.path.join(dir_, fn_)): ret[os.path.basename(dir_)].append(fn_) except (OSError, IOError): @@ -59,11 +58,11 @@ def list_(): def _check_minions_directories(pki_dir): - ''' + """ Return the minion keys directory paths. This function is a copy of salt.key.Key._check_minions_directories. - ''' + """ minions_accepted = os.path.join(pki_dir, salt.key.Key.ACC) minions_pre = os.path.join(pki_dir, salt.key.Key.PEND) minions_rejected = os.path.join(pki_dir, salt.key.Key.REJ) @@ -73,7 +72,7 @@ def _check_minions_directories(pki_dir): def kill(timeout=15): - ''' + """ Kill the salt minion. timeout @@ -101,49 +100,49 @@ def kill(timeout=15): The result of the salt command shows the process ID of the minions and the results of a kill signal to the minion in as the ``retcode`` value: ``0`` is success, anything else is a failure. - ''' + """ ret = { - 'killed': None, - 'retcode': 1, + "killed": None, + "retcode": 1, } comment = [] - pid = __grains__.get('pid') + pid = __grains__.get("pid") if not pid: comment.append('Unable to find "pid" in grains') - ret['retcode'] = salt.defaults.exitcodes.EX_SOFTWARE + ret["retcode"] = salt.defaults.exitcodes.EX_SOFTWARE else: - if 'ps.kill_pid' not in __salt__: - comment.append('Missing command: ps.kill_pid') - ret['retcode'] = salt.defaults.exitcodes.EX_SOFTWARE + if "ps.kill_pid" not in __salt__: + comment.append("Missing command: ps.kill_pid") + ret["retcode"] = salt.defaults.exitcodes.EX_SOFTWARE else: # The retcode status comes from the first kill signal - ret['retcode'] = int(not __salt__['ps.kill_pid'](pid)) + ret["retcode"] = int(not __salt__["ps.kill_pid"](pid)) # If the signal was successfully delivered then wait for the # process to die - check by sending signals until signal delivery # fails. - if ret['retcode']: - comment.append('ps.kill_pid failed') + if ret["retcode"]: + comment.append("ps.kill_pid failed") else: for _ in range(timeout): time.sleep(1) - signaled = __salt__['ps.kill_pid'](pid) + signaled = __salt__["ps.kill_pid"](pid) if not signaled: - ret['killed'] = pid + ret["killed"] = pid break else: # The process did not exit before the timeout - comment.append('Timed out waiting for minion to exit') - ret['retcode'] = salt.defaults.exitcodes.EX_TEMPFAIL + comment.append("Timed out waiting for minion to exit") + ret["retcode"] = salt.defaults.exitcodes.EX_TEMPFAIL if comment: - ret['comment'] = comment + ret["comment"] = comment return ret def restart(): - ''' + """ Kill and restart the salt minion. The configuration key ``minion_restart_command`` is an argv list for the @@ -202,56 +201,58 @@ def restart(): in the restart it will be reflected in a non-zero ``retcode`` and possibly output in the ``stderr`` and/or ``stdout`` values along with addition information in the ``comment`` field as is demonstrated with ``minion2``. - ''' + """ should_kill = True should_restart = True comment = [] ret = { - 'killed': None, - 'restart': {}, - 'retcode': 0, + "killed": None, + "restart": {}, + "retcode": 0, } - restart_cmd = __salt__['config.get']('minion_restart_command') + restart_cmd = __salt__["config.get"]("minion_restart_command") if restart_cmd: - comment.append('Using configuration minion_restart_command:') - comment.extend([' {0}'.format(arg) for arg in restart_cmd]) + comment.append("Using configuration minion_restart_command:") + comment.extend([" {0}".format(arg) for arg in restart_cmd]) else: - if '-d' in sys.argv: + if "-d" in sys.argv: restart_cmd = sys.argv - comment.append('Restart using process argv:') - comment.extend([' {0}'.format(arg) for arg in restart_cmd]) + comment.append("Restart using process argv:") + comment.extend([" {0}".format(arg) for arg in restart_cmd]) else: should_restart = False - comment.append('Not running in daemon mode - will not restart process after killing') + comment.append( + "Not running in daemon mode - will not restart process after killing" + ) if should_kill: ret.update(kill()) - if 'comment' in ret and ret['comment']: - if isinstance(ret['comment'], six.string_types): - comment.append(ret['comment']) + if "comment" in ret and ret["comment"]: + if isinstance(ret["comment"], six.string_types): + comment.append(ret["comment"]) else: - comment.extend(ret['comment']) - if ret['retcode']: - comment.append('Kill failed - not restarting') + comment.extend(ret["comment"]) + if ret["retcode"]: + comment.append("Kill failed - not restarting") should_restart = False if should_restart: - ret['restart'] = __salt__['cmd.run_all'](restart_cmd, env=os.environ) + ret["restart"] = __salt__["cmd.run_all"](restart_cmd, env=os.environ) # Do not want to mislead users to think that the returned PID from # cmd.run_all() is the PID of the new salt minion - just delete the # returned PID. - if 'pid' in ret['restart']: - del ret['restart']['pid'] - if ret['restart'].get('retcode', None): - comment.append('Restart failed') - ret['retcode'] = ret['restart']['retcode'] - if 'retcode' in ret['restart']: + if "pid" in ret["restart"]: + del ret["restart"]["pid"] + if ret["restart"].get("retcode", None): + comment.append("Restart failed") + ret["retcode"] = ret["restart"]["retcode"] + if "retcode" in ret["restart"]: # Just want a single retcode - del ret['restart']['retcode'] + del ret["restart"]["retcode"] if comment: - ret['comment'] = comment + ret["comment"] = comment return ret diff --git a/salt/modules/mod_random.py b/salt/modules/mod_random.py index 641bea073b9..b572d9e10b9 100644 --- a/salt/modules/mod_random.py +++ b/salt/modules/mod_random.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Provides access to randomness generators. ========================================= .. versionadded:: 2014.7.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import hashlib import random @@ -20,31 +21,34 @@ from salt.exceptions import SaltInvocationError from salt.ext import six if six.PY2: - ALGORITHMS_ATTR_NAME = 'algorithms' + ALGORITHMS_ATTR_NAME = "algorithms" else: - ALGORITHMS_ATTR_NAME = 'algorithms_guaranteed' + ALGORITHMS_ATTR_NAME = "algorithms_guaranteed" # Define the module's virtual name -__virtualname__ = 'random' +__virtualname__ = "random" -def __virtual__(algorithm='sha512'): - ''' +def __virtual__(algorithm="sha512"): + """ Sanity check for compatibility with Python 2.6 / 2.7 - ''' + """ # The hashlib function on Python <= 2.6 does not provide the attribute 'algorithms' # This attribute was introduced on Python >= 2.7 if six.PY2: - if not hasattr(hashlib, 'algorithms') and not hasattr(hashlib, algorithm): - return (False, 'The random execution module cannot be loaded: only available in Python >= 2.7.') + if not hasattr(hashlib, "algorithms") and not hasattr(hashlib, algorithm): + return ( + False, + "The random execution module cannot be loaded: only available in Python >= 2.7.", + ) # Under python >= 3.2, the attribute name changed to 'algorithms_guaranteed' # Since we support python 3.4+, we're good return __virtualname__ -def hash(value, algorithm='sha512'): - ''' +def hash(value, algorithm="sha512"): + """ .. versionadded:: 2014.7.0 Encodes a value with the specified encoder. @@ -61,12 +65,14 @@ def hash(value, algorithm='sha512'): .. code-block:: bash salt '*' random.hash 'I am a string' md5 - ''' + """ if six.PY3 and isinstance(value, six.string_types): # Under Python 3 we must work with bytes value = value.encode(__salt_system_encoding__) - if hasattr(hashlib, ALGORITHMS_ATTR_NAME) and algorithm in getattr(hashlib, ALGORITHMS_ATTR_NAME): + if hasattr(hashlib, ALGORITHMS_ATTR_NAME) and algorithm in getattr( + hashlib, ALGORITHMS_ATTR_NAME + ): hasher = hashlib.new(algorithm) hasher.update(value) out = hasher.hexdigest() @@ -75,13 +81,13 @@ def hash(value, algorithm='sha512'): hasher.update(value) out = hasher.hexdigest() else: - raise SaltInvocationError('You must specify a valid algorithm.') + raise SaltInvocationError("You must specify a valid algorithm.") return out -def str_encode(value, encoder='base64'): - ''' +def str_encode(value, encoder="base64"): + """ .. versionadded:: 2014.7.0 value @@ -95,35 +101,35 @@ def str_encode(value, encoder='base64'): .. code-block:: bash salt '*' random.str_encode 'I am a new string' base64 - ''' + """ if six.PY2: try: out = value.encode(encoder) except LookupError: - raise SaltInvocationError('You must specify a valid encoder') + raise SaltInvocationError("You must specify a valid encoder") except AttributeError: - raise SaltInvocationError('Value must be an encode-able string') + raise SaltInvocationError("Value must be an encode-able string") else: if isinstance(value, six.string_types): value = value.encode(__salt_system_encoding__) - if encoder == 'base64': + if encoder == "base64": try: out = base64.b64encode(value) out = out.decode(__salt_system_encoding__) except TypeError: - raise SaltInvocationError('Value must be an encode-able string') + raise SaltInvocationError("Value must be an encode-able string") else: try: out = value.encode(encoder) except LookupError: - raise SaltInvocationError('You must specify a valid encoder') + raise SaltInvocationError("You must specify a valid encoder") except AttributeError: - raise SaltInvocationError('Value must be an encode-able string') + raise SaltInvocationError("Value must be an encode-able string") return out def get_str(length=20): - ''' + """ .. versionadded:: 2014.7.0 Returns a random string of the specified length. @@ -136,12 +142,12 @@ def get_str(length=20): .. code-block:: bash salt '*' random.get_str 128 - ''' + """ return salt.utils.pycrypto.secure_password(length) -def shadow_hash(crypt_salt=None, password=None, algorithm='sha512'): - ''' +def shadow_hash(crypt_salt=None, password=None, algorithm="sha512"): + """ Generates a salted hash suitable for /etc/shadow. crypt_salt : None @@ -160,12 +166,12 @@ def shadow_hash(crypt_salt=None, password=None, algorithm='sha512'): .. code-block:: bash salt '*' random.shadow_hash 'My5alT' 'MyP@asswd' md5 - ''' + """ return salt.utils.pycrypto.gen_hash(crypt_salt, password, algorithm) def rand_int(start=1, end=10, seed=None): - ''' + """ Returns a random integer number between the start and end number. .. versionadded: 2015.5.3 @@ -188,14 +194,14 @@ def rand_int(start=1, end=10, seed=None): .. code-block:: bash salt '*' random.rand_int 1 10 - ''' + """ if seed is not None: random.seed(seed) return random.randint(start, end) def seed(range=10, hash=None): - ''' + """ Returns a random number within a range. Optional hash argument can be any hashable object. If hash is omitted or None, the id of the minion is used. @@ -212,9 +218,9 @@ def seed(range=10, hash=None): .. code-block:: bash salt '*' random.seed 10 hash=None - ''' + """ if hash is None: - hash = __grains__['id'] + hash = __grains__["id"] random.seed(hash) return random.randrange(range) diff --git a/salt/modules/modjk.py b/salt/modules/modjk.py index 7291369f997..ae8820bc1c3 100644 --- a/salt/modules/modjk.py +++ b/salt/modules/modjk.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Control Modjk via the Apache Tomcat "Status" worker (http://tomcat.apache.org/connectors-doc/reference/status.html) @@ -29,7 +29,7 @@ this module. pass: secret2 realm: authentication realm2 for digest passwords timeout: 600 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import 3rd-party libs @@ -37,26 +37,29 @@ from __future__ import absolute_import, print_function, unicode_literals from salt.ext import six from salt.ext.six.moves.urllib.parse import urlencode as _urlencode from salt.ext.six.moves.urllib.request import ( - HTTPBasicAuthHandler as _HTTPBasicAuthHandler, - HTTPDigestAuthHandler as _HTTPDigestAuthHandler, - urlopen as _urlopen, - build_opener as _build_opener, - install_opener as _install_opener + HTTPBasicAuthHandler as _HTTPBasicAuthHandler, ) +from salt.ext.six.moves.urllib.request import ( + HTTPDigestAuthHandler as _HTTPDigestAuthHandler, +) +from salt.ext.six.moves.urllib.request import build_opener as _build_opener +from salt.ext.six.moves.urllib.request import install_opener as _install_opener +from salt.ext.six.moves.urllib.request import urlopen as _urlopen + # pylint: enable=import-error,no-name-in-module def __virtual__(): - ''' + """ Always load - ''' + """ return True def _auth(url, user, passwd, realm): - ''' + """ returns a authentication handler. - ''' + """ basic = _HTTPBasicAuthHandler() basic.add_password(realm=realm, uri=url, user=user, passwd=passwd) @@ -65,55 +68,55 @@ def _auth(url, user, passwd, realm): return _build_opener(basic, digest) -def _do_http(opts, profile='default'): - ''' +def _do_http(opts, profile="default"): + """ Make the http request and return the data - ''' + """ ret = {} - url = __salt__['config.get']('modjk:{0}:url'.format(profile), '') - user = __salt__['config.get']('modjk:{0}:user'.format(profile), '') - passwd = __salt__['config.get']('modjk:{0}:pass'.format(profile), '') - realm = __salt__['config.get']('modjk:{0}:realm'.format(profile), '') - timeout = __salt__['config.get']('modjk:{0}:timeout'.format(profile), '') + url = __salt__["config.get"]("modjk:{0}:url".format(profile), "") + user = __salt__["config.get"]("modjk:{0}:user".format(profile), "") + passwd = __salt__["config.get"]("modjk:{0}:pass".format(profile), "") + realm = __salt__["config.get"]("modjk:{0}:realm".format(profile), "") + timeout = __salt__["config.get"]("modjk:{0}:timeout".format(profile), "") if not url: - raise Exception('missing url in profile {0}'.format(profile)) + raise Exception("missing url in profile {0}".format(profile)) if user and passwd: auth = _auth(url=url, realm=realm, user=user, passwd=passwd) _install_opener(auth) - url += '?{0}'.format(_urlencode(opts)) + url += "?{0}".format(_urlencode(opts)) for line in _urlopen(url, timeout=timeout).read().splitlines(): - splt = line.split('=', 1) + splt = line.split("=", 1) if splt[0] in ret: - ret[splt[0]] += ',{0}'.format(splt[1]) + ret[splt[0]] += ",{0}".format(splt[1]) else: ret[splt[0]] = splt[1] return ret -def _worker_ctl(worker, lbn, vwa, profile='default'): - ''' +def _worker_ctl(worker, lbn, vwa, profile="default"): + """ enable/disable/stop a worker - ''' + """ cmd = { - 'cmd': 'update', - 'mime': 'prop', - 'w': lbn, - 'sw': worker, - 'vwa': vwa, + "cmd": "update", + "mime": "prop", + "w": lbn, + "sw": worker, + "vwa": vwa, } - return _do_http(cmd, profile)['worker.result.type'] == 'OK' + return _do_http(cmd, profile)["worker.result.type"] == "OK" -def version(profile='default'): - ''' +def version(profile="default"): + """ Return the modjk version CLI Examples: @@ -122,17 +125,17 @@ def version(profile='default'): salt '*' modjk.version salt '*' modjk.version other-profile - ''' + """ cmd = { - 'cmd': 'version', - 'mime': 'prop', + "cmd": "version", + "mime": "prop", } - return _do_http(cmd, profile)['worker.jk_version'].split('/')[-1] + return _do_http(cmd, profile)["worker.jk_version"].split("/")[-1] -def get_running(profile='default'): - ''' +def get_running(profile="default"): + """ Get the current running config (not from disk) CLI Examples: @@ -141,17 +144,17 @@ def get_running(profile='default'): salt '*' modjk.get_running salt '*' modjk.get_running other-profile - ''' + """ cmd = { - 'cmd': 'list', - 'mime': 'prop', + "cmd": "list", + "mime": "prop", } return _do_http(cmd, profile) -def dump_config(profile='default'): - ''' +def dump_config(profile="default"): + """ Dump the original configuration that was loaded from disk CLI Examples: @@ -160,17 +163,17 @@ def dump_config(profile='default'): salt '*' modjk.dump_config salt '*' modjk.dump_config other-profile - ''' + """ cmd = { - 'cmd': 'dump', - 'mime': 'prop', + "cmd": "dump", + "mime": "prop", } return _do_http(cmd, profile) -def list_configured_members(lbn, profile='default'): - ''' +def list_configured_members(lbn, profile="default"): + """ Return a list of member workers from the configuration files CLI Examples: @@ -179,20 +182,20 @@ def list_configured_members(lbn, profile='default'): salt '*' modjk.list_configured_members loadbalancer1 salt '*' modjk.list_configured_members loadbalancer1 other-profile - ''' + """ config = dump_config(profile) try: - ret = config['worker.{0}.balance_workers'.format(lbn)] + ret = config["worker.{0}.balance_workers".format(lbn)] except KeyError: return [] - return [_f for _f in ret.strip().split(',') if _f] + return [_f for _f in ret.strip().split(",") if _f] -def workers(profile='default'): - ''' +def workers(profile="default"): + """ Return a list of member workers and their status CLI Examples: @@ -201,17 +204,17 @@ def workers(profile='default'): salt '*' modjk.workers salt '*' modjk.workers other-profile - ''' + """ config = get_running(profile) - lbn = config['worker.list'].split(',') + lbn = config["worker.list"].split(",") worker_list = [] ret = {} for lb in lbn: try: worker_list.extend( - config['worker.{0}.balance_workers'.format(lb)].split(',') + config["worker.{0}.balance_workers".format(lb)].split(",") ) except KeyError: pass @@ -220,15 +223,15 @@ def workers(profile='default'): for worker in worker_list: ret[worker] = { - 'activation': config['worker.{0}.activation'.format(worker)], - 'state': config['worker.{0}.state'.format(worker)], + "activation": config["worker.{0}.activation".format(worker)], + "state": config["worker.{0}.state".format(worker)], } return ret -def recover_all(lbn, profile='default'): - ''' +def recover_all(lbn, profile="default"): + """ Set the all the workers in lbn to recover and activate them if they are not CLI Examples: @@ -237,28 +240,28 @@ def recover_all(lbn, profile='default'): salt '*' modjk.recover_all loadbalancer1 salt '*' modjk.recover_all loadbalancer1 other-profile - ''' + """ ret = {} config = get_running(profile) try: - workers_ = config['worker.{0}.balance_workers'.format(lbn)].split(',') + workers_ = config["worker.{0}.balance_workers".format(lbn)].split(",") except KeyError: return ret for worker in workers_: curr_state = worker_status(worker, profile) - if curr_state['activation'] != 'ACT': + if curr_state["activation"] != "ACT": worker_activate(worker, lbn, profile) - if not curr_state['state'].startswith('OK'): + if not curr_state["state"].startswith("OK"): worker_recover(worker, lbn, profile) ret[worker] = worker_status(worker, profile) return ret -def reset_stats(lbn, profile='default'): - ''' +def reset_stats(lbn, profile="default"): + """ Reset all runtime statistics for the load balancer CLI Examples: @@ -267,18 +270,18 @@ def reset_stats(lbn, profile='default'): salt '*' modjk.reset_stats loadbalancer1 salt '*' modjk.reset_stats loadbalancer1 other-profile - ''' + """ cmd = { - 'cmd': 'reset', - 'mime': 'prop', - 'w': lbn, + "cmd": "reset", + "mime": "prop", + "w": lbn, } - return _do_http(cmd, profile)['worker.result.type'] == 'OK' + return _do_http(cmd, profile)["worker.result.type"] == "OK" -def lb_edit(lbn, settings, profile='default'): - ''' +def lb_edit(lbn, settings, profile="default"): + """ Edit the loadbalancer settings Note: http://tomcat.apache.org/connectors-doc/reference/status.html @@ -290,17 +293,17 @@ def lb_edit(lbn, settings, profile='default'): salt '*' modjk.lb_edit loadbalancer1 "{'vlr': 1, 'vlt': 60}" salt '*' modjk.lb_edit loadbalancer1 "{'vlr': 1, 'vlt': 60}" other-profile - ''' + """ - settings['cmd'] = 'update' - settings['mime'] = 'prop' - settings['w'] = lbn + settings["cmd"] = "update" + settings["mime"] = "prop" + settings["w"] = lbn - return _do_http(settings, profile)['worker.result.type'] == 'OK' + return _do_http(settings, profile)["worker.result.type"] == "OK" -def bulk_stop(workers, lbn, profile='default'): - ''' +def bulk_stop(workers, lbn, profile="default"): + """ Stop all the given workers in the specific load balancer CLI Examples: @@ -312,12 +315,12 @@ def bulk_stop(workers, lbn, profile='default'): salt '*' modjk.bulk_stop ["node1","node2","node3"] loadbalancer1 salt '*' modjk.bulk_stop ["node1","node2","node3"] loadbalancer1 other-profile - ''' + """ ret = {} if isinstance(workers, six.string_types): - workers = workers.split(',') + workers = workers.split(",") for worker in workers: try: @@ -328,8 +331,8 @@ def bulk_stop(workers, lbn, profile='default'): return ret -def bulk_activate(workers, lbn, profile='default'): - ''' +def bulk_activate(workers, lbn, profile="default"): + """ Activate all the given workers in the specific load balancer CLI Examples: @@ -341,12 +344,12 @@ def bulk_activate(workers, lbn, profile='default'): salt '*' modjk.bulk_activate ["node1","node2","node3"] loadbalancer1 salt '*' modjk.bulk_activate ["node1","node2","node3"] loadbalancer1 other-profile - ''' + """ ret = {} if isinstance(workers, six.string_types): - workers = workers.split(',') + workers = workers.split(",") for worker in workers: try: @@ -357,8 +360,8 @@ def bulk_activate(workers, lbn, profile='default'): return ret -def bulk_disable(workers, lbn, profile='default'): - ''' +def bulk_disable(workers, lbn, profile="default"): + """ Disable all the given workers in the specific load balancer CLI Examples: @@ -370,12 +373,12 @@ def bulk_disable(workers, lbn, profile='default'): salt '*' modjk.bulk_disable ["node1","node2","node3"] loadbalancer1 salt '*' modjk.bulk_disable ["node1","node2","node3"] loadbalancer1 other-profile - ''' + """ ret = {} if isinstance(workers, six.string_types): - workers = workers.split(',') + workers = workers.split(",") for worker in workers: try: @@ -386,8 +389,8 @@ def bulk_disable(workers, lbn, profile='default'): return ret -def bulk_recover(workers, lbn, profile='default'): - ''' +def bulk_recover(workers, lbn, profile="default"): + """ Recover all the given workers in the specific load balancer CLI Examples: @@ -399,12 +402,12 @@ def bulk_recover(workers, lbn, profile='default'): salt '*' modjk.bulk_recover ["node1","node2","node3"] loadbalancer1 salt '*' modjk.bulk_recover ["node1","node2","node3"] loadbalancer1 other-profile - ''' + """ ret = {} if isinstance(workers, six.string_types): - workers = workers.split(',') + workers = workers.split(",") for worker in workers: try: @@ -415,8 +418,8 @@ def bulk_recover(workers, lbn, profile='default'): return ret -def worker_status(worker, profile='default'): - ''' +def worker_status(worker, profile="default"): + """ Return the state of the worker CLI Examples: @@ -425,20 +428,20 @@ def worker_status(worker, profile='default'): salt '*' modjk.worker_status node1 salt '*' modjk.worker_status node1 other-profile - ''' + """ config = get_running(profile) try: return { - 'activation': config['worker.{0}.activation'.format(worker)], - 'state': config['worker.{0}.state'.format(worker)], + "activation": config["worker.{0}.activation".format(worker)], + "state": config["worker.{0}.state".format(worker)], } except KeyError: return False -def worker_recover(worker, lbn, profile='default'): - ''' +def worker_recover(worker, lbn, profile="default"): + """ Set the worker to recover this module will fail if it is in OK state @@ -448,19 +451,19 @@ def worker_recover(worker, lbn, profile='default'): salt '*' modjk.worker_recover node1 loadbalancer1 salt '*' modjk.worker_recover node1 loadbalancer1 other-profile - ''' + """ cmd = { - 'cmd': 'recover', - 'mime': 'prop', - 'w': lbn, - 'sw': worker, + "cmd": "recover", + "mime": "prop", + "w": lbn, + "sw": worker, } return _do_http(cmd, profile) -def worker_disable(worker, lbn, profile='default'): - ''' +def worker_disable(worker, lbn, profile="default"): + """ Set the worker to disable state in the lbn load balancer CLI Examples: @@ -469,13 +472,13 @@ def worker_disable(worker, lbn, profile='default'): salt '*' modjk.worker_disable node1 loadbalancer1 salt '*' modjk.worker_disable node1 loadbalancer1 other-profile - ''' + """ - return _worker_ctl(worker, lbn, 'd', profile) + return _worker_ctl(worker, lbn, "d", profile) -def worker_activate(worker, lbn, profile='default'): - ''' +def worker_activate(worker, lbn, profile="default"): + """ Set the worker to activate state in the lbn load balancer CLI Examples: @@ -484,13 +487,13 @@ def worker_activate(worker, lbn, profile='default'): salt '*' modjk.worker_activate node1 loadbalancer1 salt '*' modjk.worker_activate node1 loadbalancer1 other-profile - ''' + """ - return _worker_ctl(worker, lbn, 'a', profile) + return _worker_ctl(worker, lbn, "a", profile) -def worker_stop(worker, lbn, profile='default'): - ''' +def worker_stop(worker, lbn, profile="default"): + """ Set the worker to stopped state in the lbn load balancer CLI Examples: @@ -499,13 +502,13 @@ def worker_stop(worker, lbn, profile='default'): salt '*' modjk.worker_activate node1 loadbalancer1 salt '*' modjk.worker_activate node1 loadbalancer1 other-profile - ''' + """ - return _worker_ctl(worker, lbn, 's', profile) + return _worker_ctl(worker, lbn, "s", profile) -def worker_edit(worker, lbn, settings, profile='default'): - ''' +def worker_edit(worker, lbn, settings, profile="default"): + """ Edit the worker settings Note: http://tomcat.apache.org/connectors-doc/reference/status.html @@ -517,11 +520,11 @@ def worker_edit(worker, lbn, settings, profile='default'): salt '*' modjk.worker_edit node1 loadbalancer1 "{'vwf': 500, 'vwd': 60}" salt '*' modjk.worker_edit node1 loadbalancer1 "{'vwf': 500, 'vwd': 60}" other-profile - ''' + """ - settings['cmd'] = 'update' - settings['mime'] = 'prop' - settings['w'] = lbn - settings['sw'] = worker + settings["cmd"] = "update" + settings["mime"] = "prop" + settings["w"] = lbn + settings["sw"] = worker - return _do_http(settings, profile)['worker.result.type'] == 'OK' + return _do_http(settings, profile)["worker.result.type"] == "OK" diff --git a/salt/modules/mongodb.py b/salt/modules/mongodb.py index 8cdb819102d..1d2f2baa209 100644 --- a/salt/modules/mongodb.py +++ b/salt/modules/mongodb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide MongoDB functionality to Salt :configuration: This module uses PyMongo, and accepts configuration details as @@ -12,7 +12,7 @@ Module to provide MongoDB functionality to Salt This data can also be passed into pillar. Options passed into opts will overwrite options passed into pillar. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -22,14 +22,15 @@ import sys # Import salt libs import salt.utils.json -from salt.utils.versions import LooseVersion as _LooseVersion from salt.exceptions import get_error_message as _get_error_message - # Import third party libs from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion + try: import pymongo + HAS_MONGODB = True except ImportError: HAS_MONGODB = False @@ -38,28 +39,33 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if pymongo is installed - ''' + """ if HAS_MONGODB: - return 'mongodb' + return "mongodb" else: - return (False, 'The mongodb execution module cannot be loaded: the pymongo library is not available.') + return ( + False, + "The mongodb execution module cannot be loaded: the pymongo library is not available.", + ) -def _connect(user=None, password=None, host=None, port=None, database='admin', authdb=None): - ''' +def _connect( + user=None, password=None, host=None, port=None, database="admin", authdb=None +): + """ Returns a tuple of (user, host, port) with config, pillar, or default values assigned to missing values. - ''' + """ if not user: - user = __salt__['config.option']('mongodb.user') + user = __salt__["config.option"]("mongodb.user") if not password: - password = __salt__['config.option']('mongodb.password') + password = __salt__["config.option"]("mongodb.password") if not host: - host = __salt__['config.option']('mongodb.host') + host = __salt__["config.option"]("mongodb.host") if not port: - port = __salt__['config.option']('mongodb.port') + port = __salt__["config.option"]("mongodb.port") if not authdb: authdb = database @@ -69,16 +75,16 @@ def _connect(user=None, password=None, host=None, port=None, database='admin', a if user and password: mdb.authenticate(user, password, source=authdb) except pymongo.errors.PyMongoError: - log.error('Error connecting to database %s', database) + log.error("Error connecting to database %s", database) return False return conn def _to_dict(objects): - ''' + """ Potentially interprets a string as JSON for usage with mongo - ''' + """ try: if isinstance(objects, six.string_types): objects = salt.utils.json.loads(objects) @@ -90,7 +96,7 @@ def _to_dict(objects): def db_list(user=None, password=None, host=None, port=None, authdb=None): - ''' + """ List all MongoDB databases CLI Example: @@ -98,13 +104,13 @@ def db_list(user=None, password=None, host=None, port=None, authdb=None): .. code-block:: bash salt '*' mongodb.db_list - ''' + """ conn = _connect(user, password, host, port, authdb=authdb) if not conn: - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" try: - log.info('Listing databases') + log.info("Listing databases") return conn.database_names() except pymongo.errors.PyMongoError as err: log.error(err) @@ -112,7 +118,7 @@ def db_list(user=None, password=None, host=None, port=None, authdb=None): def db_exists(name, user=None, password=None, host=None, port=None, authdb=None): - ''' + """ Checks if a database exists in MongoDB CLI Example: @@ -120,7 +126,7 @@ def db_exists(name, user=None, password=None, host=None, port=None, authdb=None) .. code-block:: bash salt '*' mongodb.db_exists - ''' + """ dbs = db_list(user, password, host, port, authdb=authdb) if isinstance(dbs, six.string_types): @@ -130,7 +136,7 @@ def db_exists(name, user=None, password=None, host=None, port=None, authdb=None) def db_remove(name, user=None, password=None, host=None, port=None, authdb=None): - ''' + """ Remove a MongoDB database CLI Example: @@ -138,27 +144,29 @@ def db_remove(name, user=None, password=None, host=None, port=None, authdb=None) .. code-block:: bash salt '*' mongodb.db_remove - ''' + """ conn = _connect(user, password, host, port, authdb=authdb) if not conn: - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" try: - log.info('Removing database %s', name) + log.info("Removing database %s", name) conn.drop_database(name) except pymongo.errors.PyMongoError as err: - log.error('Removing database %s failed with error: %s', name, err) + log.error("Removing database %s failed with error: %s", name, err) return six.text_type(err) return True def _version(mdb): - return mdb.command('buildInfo')['version'] + return mdb.command("buildInfo")["version"] -def version(user=None, password=None, host=None, port=None, database='admin', authdb=None): - ''' +def version( + user=None, password=None, host=None, port=None, database="admin", authdb=None +): + """ Get MongoDB instance version CLI Example: @@ -166,7 +174,7 @@ def version(user=None, password=None, host=None, port=None, database='admin', au .. code-block:: bash salt '*' mongodb.version - ''' + """ conn = _connect(user, password, host, port, authdb=authdb) if not conn: err_msg = "Failed to connect to MongoDB database {0}:{1}".format(host, port) @@ -177,13 +185,14 @@ def version(user=None, password=None, host=None, port=None, database='admin', au mdb = pymongo.database.Database(conn, database) return _version(mdb) except pymongo.errors.PyMongoError as err: - log.error('Listing users failed with error: %s', err) + log.error("Listing users failed with error: %s", err) return six.text_type(err) -def user_find(name, user=None, password=None, host=None, port=None, - database='admin', authdb=None): - ''' +def user_find( + name, user=None, password=None, host=None, port=None, database="admin", authdb=None +): + """ Get single user from MongoDB CLI Example: @@ -191,7 +200,7 @@ def user_find(name, user=None, password=None, host=None, port=None, .. code-block:: bash salt '*' mongodb.user_find - ''' + """ conn = _connect(user, password, host, port, authdb=authdb) if not conn: err_msg = "Failed to connect to MongoDB database {0}:{1}".format(host, port) @@ -202,12 +211,14 @@ def user_find(name, user=None, password=None, host=None, port=None, try: return mdb.command("usersInfo", name)["users"] except pymongo.errors.PyMongoError as err: - log.error('Listing users failed with error: %s', err) + log.error("Listing users failed with error: %s", err) return (False, six.text_type(err)) -def user_list(user=None, password=None, host=None, port=None, database='admin', authdb=None): - ''' +def user_list( + user=None, password=None, host=None, port=None, database="admin", authdb=None +): + """ List users of a MongoDB database CLI Example: @@ -215,40 +226,37 @@ def user_list(user=None, password=None, host=None, port=None, database='admin', .. code-block:: bash salt '*' mongodb.user_list - ''' + """ conn = _connect(user, password, host, port, authdb=authdb) if not conn: - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" try: - log.info('Listing users') + log.info("Listing users") mdb = pymongo.database.Database(conn, database) output = [] mongodb_version = _version(mdb) - if _LooseVersion(mongodb_version) >= _LooseVersion('2.6'): - for user in mdb.command('usersInfo')['users']: - output.append( - {'user': user['user'], - 'roles': user['roles']} - ) + if _LooseVersion(mongodb_version) >= _LooseVersion("2.6"): + for user in mdb.command("usersInfo")["users"]: + output.append({"user": user["user"], "roles": user["roles"]}) else: for user in mdb.system.users.find(): output.append( - {'user': user['user'], - 'readOnly': user.get('readOnly', 'None')} + {"user": user["user"], "readOnly": user.get("readOnly", "None")} ) return output except pymongo.errors.PyMongoError as err: - log.error('Listing users failed with error: %s', err) + log.error("Listing users failed with error: %s", err) return six.text_type(err) -def user_exists(name, user=None, password=None, host=None, port=None, - database='admin', authdb=None): - ''' +def user_exists( + name, user=None, password=None, host=None, port=None, database="admin", authdb=None +): + """ Checks if a user exists in MongoDB CLI Example: @@ -256,22 +264,31 @@ def user_exists(name, user=None, password=None, host=None, port=None, .. code-block:: bash salt '*' mongodb.user_exists - ''' + """ users = user_list(user, password, host, port, database, authdb) if isinstance(users, six.string_types): - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" for user in users: - if name == dict(user).get('user'): + if name == dict(user).get("user"): return True return False -def user_create(name, passwd, user=None, password=None, host=None, port=None, - database='admin', authdb=None, roles=None): - ''' +def user_create( + name, + passwd, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, + roles=None, +): + """ Create a MongoDB user CLI Example: @@ -279,27 +296,28 @@ def user_create(name, passwd, user=None, password=None, host=None, port=None, .. code-block:: bash salt '*' mongodb.user_create - ''' + """ conn = _connect(user, password, host, port, authdb=authdb) if not conn: - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" if not roles: roles = [] try: - log.info('Creating user %s', name) + log.info("Creating user %s", name) mdb = pymongo.database.Database(conn, database) mdb.add_user(name, passwd, roles=roles) except pymongo.errors.PyMongoError as err: - log.error('Creating database %s failed with error: %s', name, err) + log.error("Creating database %s failed with error: %s", name, err) return six.text_type(err) return True -def user_remove(name, user=None, password=None, host=None, port=None, - database='admin', authdb=None): - ''' +def user_remove( + name, user=None, password=None, host=None, port=None, database="admin", authdb=None +): + """ Remove a MongoDB user CLI Example: @@ -307,25 +325,26 @@ def user_remove(name, user=None, password=None, host=None, port=None, .. code-block:: bash salt '*' mongodb.user_remove - ''' + """ conn = _connect(user, password, host, port) if not conn: - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" try: - log.info('Removing user %s', name) + log.info("Removing user %s", name) mdb = pymongo.database.Database(conn, database) mdb.remove_user(name) except pymongo.errors.PyMongoError as err: - log.error('Creating database %s failed with error: %s', name, err) + log.error("Creating database %s failed with error: %s", name, err) return six.text_type(err) return True -def user_roles_exists(name, roles, database, user=None, password=None, host=None, - port=None, authdb=None): - ''' +def user_roles_exists( + name, roles, database, user=None, password=None, host=None, port=None, authdb=None +): + """ Checks if a user of a MongoDB database has specified roles CLI Examples: @@ -337,33 +356,34 @@ def user_roles_exists(name, roles, database, user=None, password=None, host=None .. code-block:: bash salt '*' mongodb.user_roles_exists johndoe '[{"role": "readWrite", "db": "dbname" }, {"role": "read", "db": "otherdb"}]' dbname admin adminpwd localhost 27017 - ''' + """ try: roles = _to_dict(roles) except Exception: # pylint: disable=broad-except - return 'Roles provided in wrong format' + return "Roles provided in wrong format" users = user_list(user, password, host, port, database, authdb) if isinstance(users, six.string_types): - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" for user in users: - if name == dict(user).get('user'): + if name == dict(user).get("user"): for role in roles: # if the role was provided in the shortened form, we convert it to a long form if not isinstance(role, dict): - role = {'role': role, 'db': database} - if role not in dict(user).get('roles', []): + role = {"role": role, "db": database} + if role not in dict(user).get("roles", []): return False return True return False -def user_grant_roles(name, roles, database, user=None, password=None, host=None, - port=None, authdb=None): - ''' +def user_grant_roles( + name, roles, database, user=None, password=None, host=None, port=None, authdb=None +): + """ Grant one or many roles to a MongoDB user CLI Examples: @@ -375,30 +395,33 @@ def user_grant_roles(name, roles, database, user=None, password=None, host=None, .. code-block:: bash salt '*' mongodb.user_grant_roles janedoe '[{"role": "readWrite", "db": "dbname" }, {"role": "read", "db": "otherdb"}]' dbname admin adminpwd localhost 27017 - ''' + """ conn = _connect(user, password, host, port, authdb=authdb) if not conn: - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" try: roles = _to_dict(roles) except Exception: # pylint: disable=broad-except - return 'Roles provided in wrong format' + return "Roles provided in wrong format" try: - log.info('Granting roles %s to user %s', roles, name) + log.info("Granting roles %s to user %s", roles, name) mdb = pymongo.database.Database(conn, database) mdb.command("grantRolesToUser", name, roles=roles) except pymongo.errors.PyMongoError as err: - log.error('Granting roles %s to user %s failed with error: %s', roles, name, err) + log.error( + "Granting roles %s to user %s failed with error: %s", roles, name, err + ) return six.text_type(err) return True -def user_revoke_roles(name, roles, database, user=None, password=None, host=None, - port=None, authdb=None): - ''' +def user_revoke_roles( + name, roles, database, user=None, password=None, host=None, port=None, authdb=None +): + """ Revoke one or many roles to a MongoDB user CLI Examples: @@ -410,30 +433,40 @@ def user_revoke_roles(name, roles, database, user=None, password=None, host=None .. code-block:: bash salt '*' mongodb.user_revoke_roles janedoe '[{"role": "readWrite", "db": "dbname" }, {"role": "read", "db": "otherdb"}]' dbname admin adminpwd localhost 27017 - ''' + """ conn = _connect(user, password, host, port, authdb=authdb) if not conn: - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" try: roles = _to_dict(roles) except Exception: # pylint: disable=broad-except - return 'Roles provided in wrong format' + return "Roles provided in wrong format" try: - log.info('Revoking roles %s from user %s', roles, name) + log.info("Revoking roles %s from user %s", roles, name) mdb = pymongo.database.Database(conn, database) mdb.command("revokeRolesFromUser", name, roles=roles) except pymongo.errors.PyMongoError as err: - log.error('Revoking roles %s from user %s failed with error: %s', roles, name, err) + log.error( + "Revoking roles %s from user %s failed with error: %s", roles, name, err + ) return six.text_type(err) return True -def insert(objects, collection, user=None, password=None, - host=None, port=None, database='admin', authdb=None): - ''' +def insert( + objects, + collection, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, +): + """ Insert an object or list of objects into a collection CLI Example: @@ -442,7 +475,7 @@ def insert(objects, collection, user=None, password=None, salt '*' mongodb.insert '[{"foo": "FOO", "bar": "BAR"}, {"foo": "BAZ", "bar": "BAM"}]' mycollection - ''' + """ conn = _connect(user, password, host, port, database, authdb) if not conn: return "Failed to connect to mongo database" @@ -463,8 +496,17 @@ def insert(objects, collection, user=None, password=None, return err -def update_one(objects, collection, user=None, password=None, host=None, port=None, database='admin', authdb=None): - ''' +def update_one( + objects, + collection, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, +): + """ Update an object into a collection http://api.mongodb.com/python/current/api/pymongo/collection.html#pymongo.collection.Collection.update_one @@ -476,20 +518,22 @@ def update_one(objects, collection, user=None, password=None, host=None, port=No salt '*' mongodb.update_one '{"_id": "my_minion"} {"bar": "BAR"}' mycollection - ''' + """ conn = _connect(user, password, host, port, database, authdb) if not conn: return "Failed to connect to mongo database" objects = six.text_type(objects) - objs = re.split(r'}\s+{', objects) + objs = re.split(r"}\s+{", objects) if len(objs) is not 2: - return "Your request does not contain a valid " + \ - "'{_\"id\": \"my_id\"} {\"my_doc\": \"my_val\"}'" + return ( + "Your request does not contain a valid " + + '\'{_"id": "my_id"} {"my_doc": "my_val"}\'' + ) - objs[0] = objs[0] + '}' - objs[1] = '{' + objs[1] + objs[0] = objs[0] + "}" + objs[1] = "{" + objs[1] document = [] @@ -504,36 +548,37 @@ def update_one(objects, collection, user=None, password=None, host=None, port=No _update_doc = document[1] # need a string to perform the test, so using objs[0] - test_f = find(collection, - objs[0], - user, - password, - host, - port, - database, - authdb) + test_f = find(collection, objs[0], user, password, host, port, database, authdb) if not isinstance(test_f, list): - return 'The find result is not well formatted. An error appears; cannot update.' + return "The find result is not well formatted. An error appears; cannot update." elif len(test_f) < 1: - return 'Did not find any result. You should try an insert before.' + return "Did not find any result. You should try an insert before." elif len(test_f) > 1: - return 'Too many results. Please try to be more specific.' + return "Too many results. Please try to be more specific." else: try: log.info("Updating %r into %s.%s", _id_field, database, collection) mdb = pymongo.database.Database(conn, database) col = getattr(mdb, collection) - ids = col.update_one(_id_field, {'$set': _update_doc}) + ids = col.update_one(_id_field, {"$set": _update_doc}) nb_mod = ids.modified_count return "{0} objects updated".format(nb_mod) except pymongo.errors.PyMongoError as err: - log.error('Updating object %s failed with error %s', objects, err) + log.error("Updating object %s failed with error %s", objects, err) return err -def find(collection, query=None, user=None, password=None, - host=None, port=None, database='admin', authdb=None): - ''' +def find( + collection, + query=None, + user=None, + password=None, + host=None, + port=None, + database="admin", + authdb=None, +): + """ Find an object or list of objects in a collection CLI Example: @@ -542,10 +587,10 @@ def find(collection, query=None, user=None, password=None, salt '*' mongodb.find mycollection '[{"foo": "FOO", "bar": "BAR"}]' - ''' + """ conn = _connect(user, password, host, port, database, authdb) if not conn: - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" try: query = _to_dict(query) @@ -563,9 +608,18 @@ def find(collection, query=None, user=None, password=None, return err -def remove(collection, query=None, user=None, password=None, - host=None, port=None, database='admin', w=1, authdb=None): - ''' +def remove( + collection, + query=None, + user=None, + password=None, + host=None, + port=None, + database="admin", + w=1, + authdb=None, +): + """ Remove an object or list of objects into a collection CLI Example: @@ -574,10 +628,10 @@ def remove(collection, query=None, user=None, password=None, salt '*' mongodb.remove mycollection '[{"foo": "FOO", "bar": "BAR"}, {"foo": "BAZ", "bar": "BAM"}]' - ''' + """ conn = _connect(user, password, host, port, database, authdb) if not conn: - return 'Failed to connect to mongo database' + return "Failed to connect to mongo database" try: query = _to_dict(query) @@ -589,7 +643,7 @@ def remove(collection, query=None, user=None, password=None, mdb = pymongo.database.Database(conn, database) col = getattr(mdb, collection) ret = col.remove(query, w=w) - return "{0} objects removed".format(ret['n']) + return "{0} objects removed".format(ret["n"]) except pymongo.errors.PyMongoError as err: log.error("Removing objects failed with error: %s", _get_error_message(err)) return _get_error_message(err) diff --git a/salt/modules/monit.py b/salt/modules/monit.py index 5cb667b8a93..aa57d5b1cf0 100644 --- a/salt/modules/monit.py +++ b/salt/modules/monit.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Monit service module. This module will create a monit type service watcher. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -13,34 +13,37 @@ import salt.utils.path # Function alias to make sure not to shadow built-in's __func_alias__ = { - 'id_': 'id', - 'reload_': 'reload', + "id_": "id", + "reload_": "reload", } def __virtual__(): - if salt.utils.path.which('monit') is not None: + if salt.utils.path.which("monit") is not None: # The monit binary exists, let the module load return True - return (False, 'The monit execution module cannot be loaded: the monit binary is not in the path.') + return ( + False, + "The monit execution module cannot be loaded: the monit binary is not in the path.", + ) def start(name): - ''' + """ CLI Example: .. code-block:: bash salt '*' monit.start - ''' - cmd = 'monit start {0}'.format(name) + """ + cmd = "monit start {0}".format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def stop(name): - ''' + """ Stops service via monit CLI Example: @@ -48,14 +51,14 @@ def stop(name): .. code-block:: bash salt '*' monit.stop - ''' - cmd = 'monit stop {0}'.format(name) + """ + cmd = "monit stop {0}".format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def restart(name): - ''' + """ Restart service via monit CLI Example: @@ -63,14 +66,14 @@ def restart(name): .. code-block:: bash salt '*' monit.restart - ''' - cmd = 'monit restart {0}'.format(name) + """ + cmd = "monit restart {0}".format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def unmonitor(name): - ''' + """ Unmonitor service via monit CLI Example: @@ -78,14 +81,14 @@ def unmonitor(name): .. code-block:: bash salt '*' monit.unmonitor - ''' - cmd = 'monit unmonitor {0}'.format(name) + """ + cmd = "monit unmonitor {0}".format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def monitor(name): - ''' + """ monitor service via monit CLI Example: @@ -93,14 +96,14 @@ def monitor(name): .. code-block:: bash salt '*' monit.monitor - ''' - cmd = 'monit monitor {0}'.format(name) + """ + cmd = "monit monitor {0}".format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + return not __salt__["cmd.retcode"](cmd, python_shell=False) -def summary(svc_name=''): - ''' +def summary(svc_name=""): + """ Display a summary from monit CLI Example: @@ -109,22 +112,20 @@ def summary(svc_name=''): salt '*' monit.summary salt '*' monit.summary - ''' + """ ret = {} - cmd = 'monit summary' - res = __salt__['cmd.run'](cmd).splitlines() + cmd = "monit summary" + res = __salt__["cmd.run"](cmd).splitlines() for line in res: - if 'daemon is not running' in line: - return dict(monit='daemon is not running', result=False) - elif not line or svc_name not in line or 'The Monit daemon' in line: + if "daemon is not running" in line: + return dict(monit="daemon is not running", result=False) + elif not line or svc_name not in line or "The Monit daemon" in line: continue else: - parts = line.split('\'') + parts = line.split("'") if len(parts) == 3: - resource, name, status_ = ( - parts[0].strip(), parts[1], parts[2].strip() - ) - if svc_name != '' and svc_name != name: + resource, name, status_ = (parts[0].strip(), parts[1], parts[2].strip()) + if svc_name != "" and svc_name != name: continue if resource not in ret: ret[resource] = {} @@ -132,8 +133,8 @@ def summary(svc_name=''): return ret -def status(svc_name=''): - ''' +def status(svc_name=""): + """ Display a process status from monit CLI Example: @@ -142,11 +143,11 @@ def status(svc_name=''): salt '*' monit.status salt '*' monit.status - ''' - cmd = 'monit status' - res = __salt__['cmd.run'](cmd) - prostr = 'Process'+' '*28 - s = res.replace('Process', prostr).replace("'", '').split('\n\n') + """ + cmd = "monit status" + res = __salt__["cmd.run"](cmd) + prostr = "Process" + " " * 28 + s = res.replace("Process", prostr).replace("'", "").split("\n\n") entries = {} for process in s[1:-1]: pro = process.splitlines() @@ -155,15 +156,15 @@ def status(svc_name=''): key = items[:36].strip() tmp[key] = items[35:].strip() entries[pro[0].split()[1]] = tmp - if svc_name == '': + if svc_name == "": ret = entries else: - ret = entries.get(svc_name, 'No such service') + ret = entries.get(svc_name, "No such service") return ret def reload_(): - ''' + """ .. versionadded:: 2016.3.0 Reload monit configuration @@ -173,13 +174,13 @@ def reload_(): .. code-block:: bash salt '*' monit.reload - ''' - cmd = 'monit reload' - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "monit reload" + return not __salt__["cmd.retcode"](cmd, python_shell=False) def configtest(): - ''' + """ .. versionadded:: 2016.3.0 Test monit configuration syntax @@ -189,25 +190,25 @@ def configtest(): .. code-block:: bash salt '*' monit.configtest - ''' + """ ret = {} - cmd = 'monit -t' - out = __salt__['cmd.run_all'](cmd) + cmd = "monit -t" + out = __salt__["cmd.run_all"](cmd) - if out['retcode'] != 0: - ret['comment'] = 'Syntax Error' - ret['stderr'] = out['stderr'] - ret['result'] = False + if out["retcode"] != 0: + ret["comment"] = "Syntax Error" + ret["stderr"] = out["stderr"] + ret["result"] = False return ret - ret['comment'] = 'Syntax OK' - ret['stdout'] = out['stdout'] - ret['result'] = True + ret["comment"] = "Syntax OK" + ret["stdout"] = out["stdout"] + ret["result"] = True return ret def version(): - ''' + """ .. versionadded:: 2016.3.0 Return version from monit -V @@ -217,15 +218,15 @@ def version(): .. code-block:: bash salt '*' monit.version - ''' - cmd = 'monit -V' - out = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "monit -V" + out = __salt__["cmd.run"](cmd).splitlines() ret = out[0].split() return ret[-1] def id_(reset=False): - ''' + """ .. versionadded:: 2016.3.0 Return monit unique id. @@ -238,22 +239,22 @@ def id_(reset=False): .. code-block:: bash salt '*' monit.id [reset=True] - ''' + """ if reset: - id_pattern = re.compile(r'Monit id (?P[^ ]+)') - cmd = 'echo y|monit -r' - out = __salt__['cmd.run_all'](cmd, python_shell=True) - ret = id_pattern.search(out['stdout']).group('id') + id_pattern = re.compile(r"Monit id (?P[^ ]+)") + cmd = "echo y|monit -r" + out = __salt__["cmd.run_all"](cmd, python_shell=True) + ret = id_pattern.search(out["stdout"]).group("id") return ret if ret else False else: - cmd = 'monit -i' - out = __salt__['cmd.run'](cmd) - ret = out.split(':')[-1].strip() + cmd = "monit -i" + out = __salt__["cmd.run"](cmd) + ret = out.split(":")[-1].strip() return ret def validate(): - ''' + """ .. versionadded:: 2016.3.0 Check all services @@ -263,6 +264,6 @@ def validate(): .. code-block:: bash salt '*' monit.validate - ''' - cmd = 'monit validate' - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "monit validate" + return not __salt__["cmd.retcode"](cmd, python_shell=False) diff --git a/salt/modules/moosefs.py b/salt/modules/moosefs.py index d6d78d7d154..f266d158758 100644 --- a/salt/modules/moosefs.py +++ b/salt/modules/moosefs.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Module for gathering and managing information about MooseFS -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -9,16 +9,19 @@ import salt.utils.path def __virtual__(): - ''' + """ Only load if the mfs commands are installed - ''' - if salt.utils.path.which('mfsgetgoal'): - return 'moosefs' - return (False, 'The moosefs execution module cannot be loaded: the mfsgetgoal binary is not in the path.') + """ + if salt.utils.path.which("mfsgetgoal"): + return "moosefs" + return ( + False, + "The moosefs execution module cannot be loaded: the mfsgetgoal binary is not in the path.", + ) def dirinfo(path, opts=None): - ''' + """ Return information on a directory located on the Moose CLI Example: @@ -26,25 +29,25 @@ def dirinfo(path, opts=None): .. code-block:: bash salt '*' moosefs.dirinfo /path/to/dir/ [-[n][h|H]] - ''' - cmd = 'mfsdirinfo' + """ + cmd = "mfsdirinfo" ret = {} if opts: - cmd += ' -' + opts - cmd += ' ' + path - out = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd += " -" + opts + cmd += " " + path + out = __salt__["cmd.run_all"](cmd, python_shell=False) - output = out['stdout'].splitlines() + output = out["stdout"].splitlines() for line in output: if not line: continue - comps = line.split(':') + comps = line.split(":") ret[comps[0].strip()] = comps[1].strip() return ret def fileinfo(path): - ''' + """ Return information on a file located on the Moose CLI Example: @@ -52,45 +55,45 @@ def fileinfo(path): .. code-block:: bash salt '*' moosefs.fileinfo /path/to/dir/ - ''' - cmd = 'mfsfileinfo ' + path + """ + cmd = "mfsfileinfo " + path ret = {} - chunknum = '' - out = __salt__['cmd.run_all'](cmd, python_shell=False) + chunknum = "" + out = __salt__["cmd.run_all"](cmd, python_shell=False) - output = out['stdout'].splitlines() + output = out["stdout"].splitlines() for line in output: if not line: continue - if '/' in line: - comps = line.split('/') + if "/" in line: + comps = line.split("/") - chunknum = comps[0].strip().split(':') - meta = comps[1].strip().split(' ') + chunknum = comps[0].strip().split(":") + meta = comps[1].strip().split(" ") - chunk = chunknum[0].replace('chunk ', '') + chunk = chunknum[0].replace("chunk ", "") loc = chunknum[1].strip() - id_ = meta[0].replace('(id:', '') - ver = meta[1].replace(')', '').replace('ver:', '') + id_ = meta[0].replace("(id:", "") + ver = meta[1].replace(")", "").replace("ver:", "") ret[chunknum[0]] = { - 'chunk': chunk, - 'loc': loc, - 'id': id_, - 'ver': ver, + "chunk": chunk, + "loc": loc, + "id": id_, + "ver": ver, } - if 'copy' in line: - copyinfo = line.strip().split(':') + if "copy" in line: + copyinfo = line.strip().split(":") ret[chunknum[0]][copyinfo[0]] = { - 'copy': copyinfo[0].replace('copy ', ''), - 'ip': copyinfo[1].strip(), - 'port': copyinfo[2], + "copy": copyinfo[0].replace("copy ", ""), + "ip": copyinfo[1].strip(), + "port": copyinfo[2], } return ret def mounts(): - ''' + """ Return a list of current MooseFS mounts CLI Example: @@ -98,34 +101,33 @@ def mounts(): .. code-block:: bash salt '*' moosefs.mounts - ''' - cmd = 'mount' + """ + cmd = "mount" ret = {} - out = __salt__['cmd.run_all'](cmd) + out = __salt__["cmd.run_all"](cmd) - output = out['stdout'].splitlines() + output = out["stdout"].splitlines() for line in output: if not line: continue - if 'fuse.mfs' in line: - comps = line.split(' ') - info1 = comps[0].split(':') - info2 = info1[1].split('/') + if "fuse.mfs" in line: + comps = line.split(" ") + info1 = comps[0].split(":") + info2 = info1[1].split("/") ret[comps[2]] = { - 'remote': { - 'master': info1[0], - 'port': info2[0], - 'subfolder': '/' + info2[1], + "remote": { + "master": info1[0], + "port": info2[0], + "subfolder": "/" + info2[1], }, - 'local': comps[2], - 'options': (comps[5].replace('(', '').replace(')', '') - .split(',')), + "local": comps[2], + "options": (comps[5].replace("(", "").replace(")", "").split(",")), } return ret def getgoal(path, opts=None): - ''' + """ Return goal(s) for a file or directory CLI Example: @@ -134,21 +136,21 @@ def getgoal(path, opts=None): salt '*' moosefs.getgoal /path/to/file [-[n][h|H]] salt '*' moosefs.getgoal /path/to/dir/ [-[n][h|H][r]] - ''' - cmd = 'mfsgetgoal' + """ + cmd = "mfsgetgoal" ret = {} if opts: - cmd += ' -' + opts + cmd += " -" + opts else: - opts = '' - cmd += ' ' + path - out = __salt__['cmd.run_all'](cmd, python_shell=False) + opts = "" + cmd += " " + path + out = __salt__["cmd.run_all"](cmd, python_shell=False) - output = out['stdout'].splitlines() - if 'r' not in opts: - goal = output[0].split(': ') + output = out["stdout"].splitlines() + if "r" not in opts: + goal = output[0].split(": ") ret = { - 'goal': goal[1], + "goal": goal[1], } else: for line in output: @@ -157,7 +159,7 @@ def getgoal(path, opts=None): if path in line: continue comps = line.split() - keytext = comps[0] + ' with goal' + keytext = comps[0] + " with goal" if keytext not in ret: ret[keytext] = {} ret[keytext][comps[3]] = comps[5] diff --git a/salt/modules/mount.py b/salt/modules/mount.py index 2fce15f1e52..38de8fbb3b2 100644 --- a/salt/modules/mount.py +++ b/salt/modules/mount.py @@ -1,55 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" Salt module to manage Unix mounts and the fstab file -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import re -import logging # Import salt libs import salt.utils.args import salt.utils.data import salt.utils.files +import salt.utils.mount import salt.utils.path import salt.utils.platform -import salt.utils.mount import salt.utils.stringutils -from salt.utils.odict import OrderedDict -from salt.exceptions import CommandNotFoundError, CommandExecutionError +from salt.exceptions import CommandExecutionError, CommandNotFoundError # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import filter, zip # pylint: disable=import-error,redefined-builtin - +from salt.ext.six.moves import filter, zip +from salt.utils.odict import OrderedDict # Set up logger log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'mount' +__virtualname__ = "mount" def __virtual__(): - ''' + """ Only load on POSIX-like systems - ''' + """ # Disable on Windows, a specific file module exists: if salt.utils.platform.is_windows(): - return (False, 'The mount module cannot be loaded: not a POSIX-like system.') + return (False, "The mount module cannot be loaded: not a POSIX-like system.") return True def _list_mounts(): ret = {} - if __grains__['os'] in ['MacOS', 'Darwin']: - mounts = __salt__['cmd.run_stdout']('mount') + if __grains__["os"] in ["MacOS", "Darwin"]: + mounts = __salt__["cmd.run_stdout"]("mount") else: - mounts = __salt__['cmd.run_stdout']('mount -l') + mounts = __salt__["cmd.run_stdout"]("mount -l") - for line in mounts.split('\n'): + for line in mounts.split("\n"): comps = re.sub(r"\s+", " ", line).split() if len(comps) >= 3: ret[comps[2]] = comps[0] @@ -58,182 +58,208 @@ def _list_mounts(): def _active_mountinfo(ret): _list = _list_mounts() - filename = '/proc/self/mountinfo' + filename = "/proc/self/mountinfo" if not os.access(filename, os.R_OK): - msg = 'File not readable {0}' + msg = "File not readable {0}" raise CommandExecutionError(msg.format(filename)) - if 'disk.blkid' not in __context__: - __context__['disk.blkid'] = __salt__['disk.blkid']() - blkid_info = __context__['disk.blkid'] + if "disk.blkid" not in __context__: + __context__["disk.blkid"] = __salt__["disk.blkid"]() + blkid_info = __context__["disk.blkid"] with salt.utils.files.fopen(filename) as ifile: for line in ifile: comps = salt.utils.stringutils.to_unicode(line).split() - device = comps[2].split(':') + device = comps[2].split(":") # each line can have any number of # optional parameters, we use the # location of the separator field to # determine the location of the elements # after it. - _sep = comps.index('-') + _sep = comps.index("-") device_name = comps[_sep + 2] device_uuid = None device_label = None if device_name: - device_uuid = blkid_info.get(device_name, {}).get('UUID') + device_uuid = blkid_info.get(device_name, {}).get("UUID") device_uuid = device_uuid and device_uuid.lower() - device_label = blkid_info.get(device_name, {}).get('LABEL') - ret[comps[4]] = {'mountid': comps[0], - 'parentid': comps[1], - 'major': device[0], - 'minor': device[1], - 'root': comps[3], - 'opts': _resolve_user_group_names(comps[5].split(',')), - 'fstype': comps[_sep + 1], - 'device': device_name.replace('\\040', '\\ '), - 'alt_device': _list.get(comps[4], None), - 'superopts': _resolve_user_group_names(comps[_sep + 3].split(',')), - 'device_uuid': device_uuid, - 'device_label': device_label} + device_label = blkid_info.get(device_name, {}).get("LABEL") + ret[comps[4]] = { + "mountid": comps[0], + "parentid": comps[1], + "major": device[0], + "minor": device[1], + "root": comps[3], + "opts": _resolve_user_group_names(comps[5].split(",")), + "fstype": comps[_sep + 1], + "device": device_name.replace("\\040", "\\ "), + "alt_device": _list.get(comps[4], None), + "superopts": _resolve_user_group_names(comps[_sep + 3].split(",")), + "device_uuid": device_uuid, + "device_label": device_label, + } return ret def _active_mounts(ret): - ''' + """ List active mounts on Linux systems - ''' + """ _list = _list_mounts() - filename = '/proc/self/mounts' + filename = "/proc/self/mounts" if not os.access(filename, os.R_OK): - msg = 'File not readable {0}' + msg = "File not readable {0}" raise CommandExecutionError(msg.format(filename)) with salt.utils.files.fopen(filename) as ifile: for line in ifile: comps = salt.utils.stringutils.to_unicode(line).split() - ret[comps[1]] = {'device': comps[0], - 'alt_device': _list.get(comps[1], None), - 'fstype': comps[2], - 'opts': _resolve_user_group_names(comps[3].split(','))} + ret[comps[1]] = { + "device": comps[0], + "alt_device": _list.get(comps[1], None), + "fstype": comps[2], + "opts": _resolve_user_group_names(comps[3].split(",")), + } return ret def _active_mounts_aix(ret): - ''' + """ List active mounts on AIX systems - ''' - for line in __salt__['cmd.run_stdout']('mount -p').split('\n'): + """ + for line in __salt__["cmd.run_stdout"]("mount -p").split("\n"): comps = re.sub(r"\s+", " ", line).split() if comps: - if comps[0] == 'node' or comps[0] == '--------': + if comps[0] == "node" or comps[0] == "--------": continue comps_len = len(comps) - if line.startswith((' ', '\t')): - curr_opts = _resolve_user_group_names(comps[6].split(',')) if 7 == comps_len else [] + if line.startswith((" ", "\t")): + curr_opts = ( + _resolve_user_group_names(comps[6].split(",")) + if 7 == comps_len + else [] + ) if curr_opts: - ret[comps[1]] = {'device': comps[0], - 'fstype': comps[2], - 'opts': curr_opts} + ret[comps[1]] = { + "device": comps[0], + "fstype": comps[2], + "opts": curr_opts, + } else: - ret[comps[1]] = {'device': comps[0], - 'fstype': comps[2]} + ret[comps[1]] = {"device": comps[0], "fstype": comps[2]} else: - curr_opts = _resolve_user_group_names(comps[7].split(',')) if 8 == comps_len else [] + curr_opts = ( + _resolve_user_group_names(comps[7].split(",")) + if 8 == comps_len + else [] + ) if curr_opts: - ret[comps[2]] = {'node': comps[0], - 'device': comps[1], - 'fstype': comps[3], - 'opts': curr_opts} + ret[comps[2]] = { + "node": comps[0], + "device": comps[1], + "fstype": comps[3], + "opts": curr_opts, + } else: - ret[comps[2]] = {'node': comps[0], - 'device': comps[1], - 'fstype': comps[3]} + ret[comps[2]] = { + "node": comps[0], + "device": comps[1], + "fstype": comps[3], + } return ret def _active_mounts_freebsd(ret): - ''' + """ List active mounts on FreeBSD systems - ''' - for line in __salt__['cmd.run_stdout']('mount -p').split('\n'): + """ + for line in __salt__["cmd.run_stdout"]("mount -p").split("\n"): comps = re.sub(r"\s+", " ", line).split() - ret[comps[1]] = {'device': comps[0], - 'fstype': comps[2], - 'opts': _resolve_user_group_names(comps[3].split(','))} + ret[comps[1]] = { + "device": comps[0], + "fstype": comps[2], + "opts": _resolve_user_group_names(comps[3].split(",")), + } return ret def _active_mounts_solaris(ret): - ''' + """ List active mounts on Solaris systems - ''' - for line in __salt__['cmd.run_stdout']('mount -v').split('\n'): + """ + for line in __salt__["cmd.run_stdout"]("mount -v").split("\n"): comps = re.sub(r"\s+", " ", line).split() - ret[comps[2]] = {'device': comps[0], - 'fstype': comps[4], - 'opts': _resolve_user_group_names(comps[5].split('/'))} + ret[comps[2]] = { + "device": comps[0], + "fstype": comps[4], + "opts": _resolve_user_group_names(comps[5].split("/")), + } return ret def _active_mounts_openbsd(ret): - ''' + """ List active mounts on OpenBSD systems - ''' - for line in __salt__['cmd.run_stdout']('mount -v').split('\n'): + """ + for line in __salt__["cmd.run_stdout"]("mount -v").split("\n"): comps = re.sub(r"\s+", " ", line).split() - parens = re.findall(r'\((.*?)\)', line, re.DOTALL) + parens = re.findall(r"\((.*?)\)", line, re.DOTALL) if len(parens) > 1: - nod = __salt__['cmd.run_stdout']('ls -l {0}'.format(comps[0])) - nod = ' '.join(nod.split()).split(" ") - ret[comps[3]] = {'device': comps[0], - 'fstype': comps[5], - 'opts': _resolve_user_group_names(parens[1].split(", ")), - 'major': six.text_type(nod[4].strip(",")), - 'minor': six.text_type(nod[5]), - 'device_uuid': parens[0]} + nod = __salt__["cmd.run_stdout"]("ls -l {0}".format(comps[0])) + nod = " ".join(nod.split()).split(" ") + ret[comps[3]] = { + "device": comps[0], + "fstype": comps[5], + "opts": _resolve_user_group_names(parens[1].split(", ")), + "major": six.text_type(nod[4].strip(",")), + "minor": six.text_type(nod[5]), + "device_uuid": parens[0], + } else: - ret[comps[2]] = {'device': comps[0], - 'fstype': comps[4], - 'opts': _resolve_user_group_names(parens[0].split(", "))} + ret[comps[2]] = { + "device": comps[0], + "fstype": comps[4], + "opts": _resolve_user_group_names(parens[0].split(", ")), + } return ret def _active_mounts_darwin(ret): - ''' + """ List active mounts on Mac OS systems - ''' - for line in __salt__['cmd.run_stdout']('mount').split('\n'): + """ + for line in __salt__["cmd.run_stdout"]("mount").split("\n"): comps = re.sub(r"\s+", " ", line).split() - parens = re.findall(r'\((.*?)\)', line, re.DOTALL)[0].split(", ") - ret[comps[2]] = {'device': comps[0], - 'fstype': parens[0], - 'opts': _resolve_user_group_names(parens[1:])} + parens = re.findall(r"\((.*?)\)", line, re.DOTALL)[0].split(", ") + ret[comps[2]] = { + "device": comps[0], + "fstype": parens[0], + "opts": _resolve_user_group_names(parens[1:]), + } return ret def _resolve_user_group_names(opts): - ''' + """ Resolve user and group names in related opts - ''' - name_id_opts = {'uid': 'user.info', - 'gid': 'group.info'} + """ + name_id_opts = {"uid": "user.info", "gid": "group.info"} for ind, opt in enumerate(opts): - if opt.split('=')[0] in name_id_opts: - _givenid = opt.split('=')[1] - _param = opt.split('=')[0] + if opt.split("=")[0] in name_id_opts: + _givenid = opt.split("=")[1] + _param = opt.split("=")[0] _id = _givenid - if not re.match('[0-9]+$', _givenid): + if not re.match("[0-9]+$", _givenid): _info = __salt__[name_id_opts[_param]](_givenid) if _info and _param in _info: _id = _info[_param] - opts[ind] = _param + '=' + six.text_type(_id) - opts[ind] = opts[ind].replace('\\040', '\\ ') + opts[ind] = _param + "=" + six.text_type(_id) + opts[ind] = opts[ind].replace("\\040", "\\ ") return opts def active(extended=False): - ''' + """ List the active mounts. CLI Example: @@ -241,17 +267,17 @@ def active(extended=False): .. code-block:: bash salt '*' mount.active - ''' + """ ret = {} - if __grains__['os'] == 'FreeBSD': + if __grains__["os"] == "FreeBSD": _active_mounts_freebsd(ret) - elif 'AIX' in __grains__['kernel']: + elif "AIX" in __grains__["kernel"]: _active_mounts_aix(ret) - elif __grains__['kernel'] == 'SunOS': + elif __grains__["kernel"] == "SunOS": _active_mounts_solaris(ret) - elif __grains__['os'] == 'OpenBSD': + elif __grains__["os"] == "OpenBSD": _active_mounts_openbsd(ret) - elif __grains__['os'] in ['MacOS', 'Darwin']: + elif __grains__["os"] in ["MacOS", "Darwin"]: _active_mounts_darwin(ret) else: if extended: @@ -265,87 +291,91 @@ def active(extended=False): class _fstab_entry(object): - ''' + """ Utility class for manipulating fstab entries. Primarily we're parsing, formatting, and comparing lines. Parsing emits dicts expected from fstab() or raises a ValueError. Note: We'll probably want to use os.normpath and os.normcase on 'name' - ''' + """ class ParseError(ValueError): - ''' + """ Error raised when a line isn't parsible as an fstab entry - ''' + """ - fstab_keys = ('device', 'name', 'fstype', 'opts', 'dump', 'pass_num') + fstab_keys = ("device", "name", "fstype", "opts", "dump", "pass_num") # preserve data format - compatibility_keys = ('device', 'name', 'fstype', 'opts', 'dump', 'pass') + compatibility_keys = ("device", "name", "fstype", "opts", "dump", "pass") - fstab_format = '{device}\t\t{name}\t{fstype}\t{opts}\t{dump} {pass_num}\n' + fstab_format = "{device}\t\t{name}\t{fstype}\t{opts}\t{dump} {pass_num}\n" @classmethod def dict_from_line(cls, line, keys=fstab_keys): if len(keys) != 6: - raise ValueError('Invalid key array: {0}'.format(keys)) - if line.startswith('#'): + raise ValueError("Invalid key array: {0}".format(keys)) + if line.startswith("#"): raise cls.ParseError("Comment!") comps = line.split() if len(comps) < 4 or len(comps) > 6: raise cls.ParseError("Invalid Entry!") - comps.extend(['0'] * (len(keys) - len(comps))) + comps.extend(["0"] * (len(keys) - len(comps))) return dict(zip(keys, comps)) @classmethod def from_line(cls, *args, **kwargs): - return cls(** cls.dict_from_line(*args, **kwargs)) + return cls(**cls.dict_from_line(*args, **kwargs)) @classmethod def dict_to_line(cls, entry): return cls.fstab_format.format(**entry) def __str__(self): - ''' + """ String value, only works for full repr - ''' + """ return self.dict_to_line(self.criteria) def __repr__(self): - ''' + """ Always works - ''' + """ return repr(self.criteria) def pick(self, keys): - ''' + """ Returns an instance with just those keys - ''' + """ subset = dict([(key, self.criteria[key]) for key in keys]) return self.__class__(**subset) def __init__(self, **criteria): - ''' + """ Store non-empty, non-null values to use as filter - ''' - items = [key_value for key_value in six.iteritems(criteria) if key_value[1] is not None] + """ + items = [ + key_value + for key_value in six.iteritems(criteria) + if key_value[1] is not None + ] items = [(key_value1[0], six.text_type(key_value1[1])) for key_value1 in items] self.criteria = dict(items) @staticmethod def norm_path(path): - ''' + """ Resolve equivalent paths equivalently - ''' + """ return os.path.normcase(os.path.normpath(path)) def match(self, line): - ''' + """ Compare potentially partial criteria against line - ''' + """ entry = self.dict_from_line(line) for key, value in six.iteritems(self.criteria): if entry[key] != value: @@ -354,7 +384,7 @@ class _fstab_entry(object): class _vfstab_entry(object): - ''' + """ Utility class for manipulating vfstab entries. Primarily we're parsing, formatting, and comparing lines. Parsing emits dicts expected from fstab() or raises a ValueError. @@ -366,18 +396,26 @@ class _vfstab_entry(object): #to mount to fsck point type pass at boot options # /devices - /devices devfs - no - - ''' + """ class ParseError(ValueError): - '''Error raised when a line isn't parsible as an fstab entry''' + """Error raised when a line isn't parsible as an fstab entry""" - vfstab_keys = ('device', 'device_fsck', 'name', 'fstype', 'pass_fsck', 'mount_at_boot', 'opts') + vfstab_keys = ( + "device", + "device_fsck", + "name", + "fstype", + "pass_fsck", + "mount_at_boot", + "opts", + ) # NOTE: weird formatting to match default spacing on Solaris - vfstab_format = '{device:<11} {device_fsck:<3} {name:<19} {fstype:<8} {pass_fsck:<3} {mount_at_boot:<6} {opts}\n' + vfstab_format = "{device:<11} {device_fsck:<3} {name:<19} {fstype:<8} {pass_fsck:<3} {mount_at_boot:<6} {opts}\n" @classmethod def dict_from_line(cls, line): - if line.startswith('#'): + if line.startswith("#"): raise cls.ParseError("Comment!") comps = line.split() @@ -388,50 +426,54 @@ class _vfstab_entry(object): @classmethod def from_line(cls, *args, **kwargs): - return cls(** cls.dict_from_line(*args, **kwargs)) + return cls(**cls.dict_from_line(*args, **kwargs)) @classmethod def dict_to_line(cls, entry): return cls.vfstab_format.format(**entry) def __str__(self): - ''' + """ String value, only works for full repr - ''' + """ return self.dict_to_line(self.criteria) def __repr__(self): - ''' + """ Always works - ''' + """ return repr(self.criteria) def pick(self, keys): - ''' + """ Returns an instance with just those keys - ''' + """ subset = dict([(key, self.criteria[key]) for key in keys]) return self.__class__(**subset) def __init__(self, **criteria): - ''' + """ Store non-empty, non-null values to use as filter - ''' - items = [key_value for key_value in six.iteritems(criteria) if key_value[1] is not None] + """ + items = [ + key_value + for key_value in six.iteritems(criteria) + if key_value[1] is not None + ] items = [(key_value1[0], six.text_type(key_value1[1])) for key_value1 in items] self.criteria = dict(items) @staticmethod def norm_path(path): - ''' + """ Resolve equivalent paths equivalently - ''' + """ return os.path.normcase(os.path.normpath(path)) def match(self, line): - ''' + """ Compare potentially partial criteria against line - ''' + """ entry = self.dict_from_line(line) for key, value in six.iteritems(self.criteria): if entry[key] != value: @@ -440,43 +482,62 @@ class _vfstab_entry(object): class _FileSystemsEntry(object): - ''' + """ Utility class for manipulating filesystem entries. Primarily we're parsing, formatting, and comparing lines. Parsing emits dicts expected from fstab() or raises a ValueError. Note: We'll probably want to use os.normpath and os.normcase on 'name' - ''' + """ class ParseError(ValueError): - ''' + """ Error raised when a line isn't parsible as an fstab entry - ''' + """ - filesystems_keys = ('device', 'name', 'fstype', 'vfstype', 'opts', 'mount') + filesystems_keys = ("device", "name", "fstype", "vfstype", "opts", "mount") # preserve data format of filesystems - compatibility_keys = ('dev', 'dev', 'name', 'fstype', 'vfstype', 'opts', 'mount', 'type', 'vfs', 'account', 'boot', 'check', 'free', 'nodename', 'quota', 'size', 'vol', 'log') + compatibility_keys = ( + "dev", + "dev", + "name", + "fstype", + "vfstype", + "opts", + "mount", + "type", + "vfs", + "account", + "boot", + "check", + "free", + "nodename", + "quota", + "size", + "vol", + "log", + ) @classmethod def dict_from_lines(cls, lines, keys=filesystems_keys): if len(lines) < 2: - raise ValueError('Invalid number of lines: {0}'.format(lines)) + raise ValueError("Invalid number of lines: {0}".format(lines)) if not keys: # if empty force default filesystems_keys keys = _FileSystemsEntry.filesystems_keys elif len(keys) < 6: - raise ValueError('Invalid key name array: {0}'.format(keys)) + raise ValueError("Invalid key name array: {0}".format(keys)) blk_lines = lines orddict = OrderedDict() - orddict['name'] = blk_lines[0].split(':')[0].strip() + orddict["name"] = blk_lines[0].split(":")[0].strip() blk_lines.pop(0) for line in blk_lines: - if line.startswith('#'): + if line.startswith("#"): raise cls.ParseError("Comment!") - comps = line.split('= ') + comps = line.split("= ") if len(comps) != 2: raise cls.ParseError("Invalid Entry!") @@ -484,7 +545,9 @@ class _FileSystemsEntry(object): if key_name in keys: orddict[key_name] = comps[1].strip() else: - raise ValueError('Invalid name for use in filesystems: {0}'.format(key_name)) + raise ValueError( + "Invalid name for use in filesystems: {0}".format(key_name) + ) return orddict @@ -494,7 +557,7 @@ class _FileSystemsEntry(object): if keys: for key, value in keys: # ignore unknown or local scope keys - if key.startswith('__'): + if key.startswith("__"): continue if key in _FileSystemsEntry.compatibility_keys: cmdln_dict[key] = value @@ -503,61 +566,65 @@ class _FileSystemsEntry(object): @classmethod def from_line(cls, *args, **kwargs): - return cls(** cls.dict_from_cmd_line(*args, **kwargs)) + return cls(**cls.dict_from_cmd_line(*args, **kwargs)) @classmethod def dict_to_lines(cls, fsys_dict_entry): entry = fsys_dict_entry - strg_out = entry['name'] + ':' + os.linesep + strg_out = entry["name"] + ":" + os.linesep for k, v in six.viewitems(entry): - if 'name' not in k: - strg_out += '\t{0}\t\t= {1}'.format(k, v) + os.linesep + if "name" not in k: + strg_out += "\t{0}\t\t= {1}".format(k, v) + os.linesep strg_out += os.linesep return six.text_type(strg_out) def dict_from_entry(self): ret = OrderedDict() - ret[self.criteria['name']] = self.criteria + ret[self.criteria["name"]] = self.criteria return ret def __str__(self): - ''' + """ String value, only works for full repr - ''' + """ return self.dict_to_lines(self.criteria) def __repr__(self): - ''' + """ Always works - ''' + """ return repr(self.criteria) def pick(self, keys): - ''' + """ Returns an instance with just those keys - ''' + """ subset = dict([(key, self.criteria[key]) for key in keys]) return self.__class__(**subset) def __init__(self, **criteria): - ''' + """ Store non-empty, non-null values to use as filter - ''' - items = [key_value for key_value in six.iteritems(criteria) if key_value[1] is not None] + """ + items = [ + key_value + for key_value in six.iteritems(criteria) + if key_value[1] is not None + ] items = [(key_value1[0], six.text_type(key_value1[1])) for key_value1 in items] self.criteria = OrderedDict(items) @staticmethod def norm_path(path): - ''' + """ Resolve equivalent paths equivalently - ''' + """ return os.path.normcase(os.path.normpath(path)) def match(self, fsys_view): - ''' + """ Compare potentially partial criteria against built filesystems entry dictionary - ''' + """ evalue_dict = fsys_view[1] for key, value in six.viewitems(self.criteria): if key in evalue_dict: @@ -568,14 +635,14 @@ class _FileSystemsEntry(object): return True def __getitem__(self, key): - ''' + """ Return value for input key - ''' + """ return self.criteria[key] -def fstab(config='/etc/fstab'): - ''' +def fstab(config="/etc/fstab"): + """ .. versionchanged:: 2016.3.2 List the contents of the fstab @@ -585,7 +652,7 @@ def fstab(config='/etc/fstab'): .. code-block:: bash salt '*' mount.fstab - ''' + """ ret = {} if not os.path.isfile(config): return ret @@ -593,22 +660,21 @@ def fstab(config='/etc/fstab'): for line in ifile: line = salt.utils.stringutils.to_unicode(line) try: - if __grains__['kernel'] == 'SunOS': + if __grains__["kernel"] == "SunOS": # Note: comments use in default vfstab file! - if line[0] == '#': + if line[0] == "#": continue - entry = _vfstab_entry.dict_from_line( - line) + entry = _vfstab_entry.dict_from_line(line) else: entry = _fstab_entry.dict_from_line( - line, - _fstab_entry.compatibility_keys) + line, _fstab_entry.compatibility_keys + ) - entry['opts'] = entry['opts'].split(',') - while entry['name'] in ret: - entry['name'] += '_' + entry["opts"] = entry["opts"].split(",") + while entry["name"] in ret: + entry["name"] += "_" - ret[entry.pop('name')] = entry + ret[entry.pop("name")] = entry except _fstab_entry.ParseError: pass except _vfstab_entry.ParseError: @@ -617,8 +683,8 @@ def fstab(config='/etc/fstab'): return ret -def vfstab(config='/etc/vfstab'): - ''' +def vfstab(config="/etc/vfstab"): + """ .. versionadded:: 2016.3.2 List the contents of the vfstab @@ -628,13 +694,13 @@ def vfstab(config='/etc/vfstab'): .. code-block:: bash salt '*' mount.vfstab - ''' + """ # NOTE: vfstab is a wrapper for fstab return fstab(config) -def rm_fstab(name, device, config='/etc/fstab'): - ''' +def rm_fstab(name, device, config="/etc/fstab"): + """ .. versionchanged:: 2016.3.2 Remove the mount point from the fstab @@ -644,17 +710,17 @@ def rm_fstab(name, device, config='/etc/fstab'): .. code-block:: bash salt '*' mount.rm_fstab /mnt/foo /dev/sdg - ''' + """ modified = False - if __grains__['kernel'] == 'SunOS': + if __grains__["kernel"] == "SunOS": criteria = _vfstab_entry(name=name, device=device) else: criteria = _fstab_entry(name=name, device=device) lines = [] try: - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) try: @@ -674,7 +740,7 @@ def rm_fstab(name, device, config='/etc/fstab'): if modified: try: - with salt.utils.files.fopen(config, 'wb') as ofile: + with salt.utils.files.fopen(config, "wb") as ofile: ofile.writelines(salt.utils.data.encode(lines)) except (IOError, OSError) as exc: msg = "Couldn't write to {0}: {1}" @@ -685,8 +751,8 @@ def rm_fstab(name, device, config='/etc/fstab'): return True -def rm_vfstab(name, device, config='/etc/vfstab'): - ''' +def rm_vfstab(name, device, config="/etc/vfstab"): + """ .. versionadded:: 2016.3.2 Remove the mount point from the vfstab @@ -696,24 +762,25 @@ def rm_vfstab(name, device, config='/etc/vfstab'): .. code-block:: bash salt '*' mount.rm_vfstab /mnt/foo /device/c0t0d0p0 - ''' + """ ## NOTE: rm_vfstab is a wrapper for rm_fstab return rm_fstab(name, device, config) def set_fstab( - name, - device, - fstype, - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - test=False, - match_on='auto', - not_change=False, - **kwargs): - ''' + name, + device, + fstype, + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + test=False, + match_on="auto", + not_change=False, + **kwargs +): + """ Verify that this mount is represented in the fstab, change the mount to match the data passed, or add the mount if it is not present. @@ -725,20 +792,20 @@ def set_fstab( .. code-block:: bash salt '*' mount.set_fstab /mnt/foo /dev/sdz1 ext4 - ''' + """ # Fix the opts type if it is a list if isinstance(opts, list): - opts = ','.join(opts) + opts = ",".join(opts) # preserve arguments for updating entry_args = { - 'name': name, - 'device': device.replace('\\ ', '\\040'), - 'fstype': fstype, - 'opts': opts.replace('\\ ', '\\040'), - 'dump': dump, - 'pass_num': pass_num, + "name": name, + "device": device.replace("\\ ", "\\040"), + "fstype": fstype, + "opts": opts.replace("\\ ", "\\040"), + "dump": dump, + "pass_num": pass_num, } lines = [] @@ -748,30 +815,33 @@ def set_fstab( if isinstance(match_on, list): pass elif not isinstance(match_on, six.string_types): - msg = 'match_on must be a string or list of strings' + msg = "match_on must be a string or list of strings" raise CommandExecutionError(msg) - elif match_on == 'auto': + elif match_on == "auto": # Try to guess right criteria for auto.... # NOTE: missing some special fstypes here - specialFSes = frozenset([ - 'none', - 'tmpfs', - 'sysfs', - 'proc', - 'fusectl', - 'debugfs', - 'securityfs', - 'devtmpfs', - 'cgroup', - 'nfs', - 'nfs4', - 'glusterfs', - 'btrfs']) + specialFSes = frozenset( + [ + "none", + "tmpfs", + "sysfs", + "proc", + "fusectl", + "debugfs", + "securityfs", + "devtmpfs", + "cgroup", + "nfs", + "nfs4", + "glusterfs", + "btrfs", + ] + ) if fstype in specialFSes: - match_on = ['name'] + match_on = ["name"] else: - match_on = ['device'] + match_on = ["device"] else: match_on = [match_on] @@ -792,18 +862,18 @@ def set_fstab( raise CommandExecutionError('Bad config file "{0}"'.format(config)) try: - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) try: if criteria.match(line): # Note: If ret isn't None here, # we've matched multiple lines - ret = 'present' + ret = "present" if entry.match(line) or not_change: lines.append(line) else: - ret = 'change' + ret = "change" lines.append(six.text_type(entry)) else: lines.append(line) @@ -812,41 +882,42 @@ def set_fstab( lines.append(line) except (IOError, OSError) as exc: - msg = 'Couldn\'t read from {0}: {1}' + msg = "Couldn't read from {0}: {1}" raise CommandExecutionError(msg.format(config, exc)) # add line if not present or changed if ret is None: lines.append(six.text_type(entry)) - ret = 'new' + ret = "new" - if ret != 'present': # ret in ['new', 'change']: + if ret != "present": # ret in ['new', 'change']: if not salt.utils.args.test_mode(test=test, **kwargs): try: - with salt.utils.files.fopen(config, 'wb') as ofile: + with salt.utils.files.fopen(config, "wb") as ofile: # The line was changed, commit it! ofile.writelines(salt.utils.data.encode(lines)) except (IOError, OSError): - msg = 'File not writable {0}' + msg = "File not writable {0}" raise CommandExecutionError(msg.format(config)) return ret def set_vfstab( - name, - device, - fstype, - opts='-', - device_fsck='-', - pass_fsck='-', - mount_at_boot='yes', - config='/etc/vfstab', - test=False, - match_on='auto', - not_change=False, - **kwargs): - ''' + name, + device, + fstype, + opts="-", + device_fsck="-", + pass_fsck="-", + mount_at_boot="yes", + config="/etc/vfstab", + test=False, + match_on="auto", + not_change=False, + **kwargs +): + """ ..verionadded:: 2016.3.2 Verify that this mount is represented in the fstab, change the mount to match the data passed, or add the mount if it is not present. @@ -859,25 +930,25 @@ def set_vfstab( .. code-block:: bash salt '*' mount.set_vfstab /mnt/foo /device/c0t0d0p0 ufs - ''' + """ # Fix the opts type if it is a list if isinstance(opts, list): - opts = ','.join(opts) + opts = ",".join(opts) # Map unknown values for mount_at_boot to no - if mount_at_boot != 'yes': - mount_at_boot = 'no' + if mount_at_boot != "yes": + mount_at_boot = "no" # preserve arguments for updating entry_args = { - 'name': name, - 'device': device, - 'fstype': fstype, - 'opts': opts, - 'device_fsck': device_fsck, - 'pass_fsck': pass_fsck, - 'mount_at_boot': mount_at_boot, + "name": name, + "device": device, + "fstype": fstype, + "opts": opts, + "device_fsck": device_fsck, + "pass_fsck": pass_fsck, + "mount_at_boot": mount_at_boot, } lines = [] @@ -887,24 +958,19 @@ def set_vfstab( if isinstance(match_on, list): pass elif not isinstance(match_on, six.string_types): - msg = 'match_on must be a string or list of strings' + msg = "match_on must be a string or list of strings" raise CommandExecutionError(msg) - elif match_on == 'auto': + elif match_on == "auto": # Try to guess right criteria for auto.... # NOTE: missing some special fstypes here - specialFSes = frozenset([ - 'devfs', - 'proc', - 'ctfs', - 'objfs', - 'sharefs', - 'fs', - 'tmpfs']) + specialFSes = frozenset( + ["devfs", "proc", "ctfs", "objfs", "sharefs", "fs", "tmpfs"] + ) if fstype in specialFSes: - match_on = ['name'] + match_on = ["name"] else: - match_on = ['device'] + match_on = ["device"] else: match_on = [match_on] @@ -925,18 +991,18 @@ def set_vfstab( raise CommandExecutionError('Bad config file "{0}"'.format(config)) try: - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) try: if criteria.match(line): # Note: If ret isn't None here, # we've matched multiple lines - ret = 'present' + ret = "present" if entry.match(line) or not_change: lines.append(line) else: - ret = 'change' + ret = "change" lines.append(six.text_type(entry)) else: lines.append(line) @@ -945,29 +1011,29 @@ def set_vfstab( lines.append(line) except (IOError, OSError) as exc: - msg = 'Couldn\'t read from {0}: {1}' + msg = "Couldn't read from {0}: {1}" raise CommandExecutionError(msg.format(config, exc)) # add line if not present or changed if ret is None: lines.append(six.text_type(entry)) - ret = 'new' + ret = "new" - if ret != 'present': # ret in ['new', 'change']: + if ret != "present": # ret in ['new', 'change']: if not salt.utils.args.test_mode(test=test, **kwargs): try: - with salt.utils.files.fopen(config, 'wb') as ofile: + with salt.utils.files.fopen(config, "wb") as ofile: # The line was changed, commit it! ofile.writelines(salt.utils.data.encode(lines)) except (IOError, OSError): - msg = 'File not writable {0}' + msg = "File not writable {0}" raise CommandExecutionError(msg.format(config)) return ret -def rm_automaster(name, device, config='/etc/auto_salt'): - ''' +def rm_automaster(name, device, config="/etc/auto_salt"): + """ Remove the mount point from the auto_master CLI Example: @@ -975,17 +1041,17 @@ def rm_automaster(name, device, config='/etc/auto_salt'): .. code-block:: bash salt '*' mount.rm_automaster /mnt/foo /dev/sdg - ''' + """ contents = automaster(config) if name not in contents: return True # The entry is present, get rid of it lines = [] try: - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('#'): + if line.startswith("#"): # Commented lines.append(line) continue @@ -1016,27 +1082,28 @@ def rm_automaster(name, device, config='/etc/auto_salt'): raise CommandExecutionError(msg.format(config, exc)) try: - with salt.utils.files.fopen(config, 'wb') as ofile: + with salt.utils.files.fopen(config, "wb") as ofile: ofile.writelines(salt.utils.data.encode(lines)) except (IOError, OSError) as exc: msg = "Couldn't write to {0}: {1}" raise CommandExecutionError(msg.format(config, exc)) # Update automount - __salt__['cmd.run']('automount -cv') + __salt__["cmd.run"]("automount -cv") return True def set_automaster( - name, - device, - fstype, - opts='', - config='/etc/auto_salt', - test=False, - not_change=False, - **kwargs): - ''' + name, + device, + fstype, + opts="", + config="/etc/auto_salt", + test=False, + not_change=False, + **kwargs +): + """ Verify that this mount is represented in the auto_salt, change the mount to match the data passed, or add the mount if it is not present. @@ -1045,31 +1112,31 @@ def set_automaster( .. code-block:: bash salt '*' mount.set_automaster /mnt/foo /dev/sdz1 ext4 - ''' + """ # Fix the opts type if it is a list if isinstance(opts, list): - opts = ','.join(opts) + opts = ",".join(opts) lines = [] change = False present = False automaster_file = "/etc/auto_master" if not os.path.isfile(config): - __salt__['file.touch'](config) - __salt__['file.append'](automaster_file, "/-\t\t\t{0}".format(config)) + __salt__["file.touch"](config) + __salt__["file.append"](automaster_file, "/-\t\t\t{0}".format(config)) name = "/..{0}".format(name) device_fmt = "{0}:{1}".format(fstype, device) type_opts = "-fstype={0},{1}".format(fstype, opts) - if fstype == 'smbfs': + if fstype == "smbfs": device_fmt = device_fmt.replace(fstype, "") try: - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('#'): + if line.startswith("#"): # Commented lines.append(line) continue @@ -1099,59 +1166,50 @@ def set_automaster( comps[2] = device_fmt if change: log.debug( - 'auto_master entry for mount point %s needs to be ' - 'updated', name - ) - newline = ( - '{0}\t{1}\t{2}\n'.format( - name, type_opts, device_fmt) + "auto_master entry for mount point %s needs to be " + "updated", + name, ) + newline = "{0}\t{1}\t{2}\n".format(name, type_opts, device_fmt) lines.append(newline) else: lines.append(line) except (IOError, OSError) as exc: - msg = 'Couldn\'t read from {0}: {1}' + msg = "Couldn't read from {0}: {1}" raise CommandExecutionError(msg.format(config, exc)) if change: if not salt.utils.args.test_mode(test=test, **kwargs): try: - with salt.utils.files.fopen(config, 'wb') as ofile: + with salt.utils.files.fopen(config, "wb") as ofile: # The line was changed, commit it! ofile.writelines(salt.utils.data.encode(lines)) except (IOError, OSError): - msg = 'File not writable {0}' + msg = "File not writable {0}" raise CommandExecutionError(msg.format(config)) - return 'change' + return "change" if not change: if present: # The right entry is already here - return 'present' + return "present" else: if not salt.utils.args.test_mode(test=test, **kwargs): # The entry is new, add it to the end of the fstab - newline = ( - '{0}\t{1}\t{2}\n'.format( - name, type_opts, device_fmt) - ) + newline = "{0}\t{1}\t{2}\n".format(name, type_opts, device_fmt) lines.append(newline) try: - with salt.utils.files.fopen(config, 'wb') as ofile: + with salt.utils.files.fopen(config, "wb") as ofile: # The line was changed, commit it! ofile.writelines(salt.utils.data.encode(lines)) except (IOError, OSError): - raise CommandExecutionError( - 'File not writable {0}'.format( - config - ) - ) - return 'new' + raise CommandExecutionError("File not writable {0}".format(config)) + return "new" -def automaster(config='/etc/auto_salt'): - ''' +def automaster(config="/etc/auto_salt"): + """ List the contents of the auto master CLI Example: @@ -1159,14 +1217,14 @@ def automaster(config='/etc/auto_salt'): .. code-block:: bash salt '*' mount.automaster - ''' + """ ret = {} if not os.path.isfile(config): return ret with salt.utils.files.fopen(config) as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('#'): + if line.startswith("#"): # Commented continue if not line.strip(): @@ -1180,16 +1238,16 @@ def automaster(config='/etc/auto_salt'): prefix = "/.." name = comps[0].replace(prefix, "") device_fmt = comps[2].split(":") - opts = comps[1].split(',') + opts = comps[1].split(",") - ret[name] = {'device': device_fmt[1], - 'fstype': opts[0], - 'opts': opts[1:]} + ret[name] = {"device": device_fmt[1], "fstype": opts[0], "opts": opts[1:]} return ret -def mount(name, device, mkmnt=False, fstype='', opts='defaults', user=None, util='mount'): - ''' +def mount( + name, device, mkmnt=False, fstype="", opts="defaults", user=None, util="mount" +): + """ Mount a device CLI Example: @@ -1197,55 +1255,55 @@ def mount(name, device, mkmnt=False, fstype='', opts='defaults', user=None, util .. code-block:: bash salt '*' mount.mount /mnt/foo /dev/sdz1 True - ''' - if util != 'mount': + """ + if util != "mount": # This functionality used to live in img.mount_image - if util == 'guestfs': - return __salt__['guestfs.mount'](name, root=device) - elif util == 'qemu_nbd': - mnt = __salt__['qemu_nbd.init'](name, device) + if util == "guestfs": + return __salt__["guestfs.mount"](name, root=device) + elif util == "qemu_nbd": + mnt = __salt__["qemu_nbd.init"](name, device) if not mnt: return False first = next(six.iterkeys(mnt)) - __context__['img.mnt_{0}'.format(first)] = mnt + __context__["img.mnt_{0}".format(first)] = mnt return first return False # Darwin doesn't expect defaults when mounting without other options - if 'defaults' in opts and __grains__['os'] in ['MacOS', 'Darwin', 'AIX']: + if "defaults" in opts and __grains__["os"] in ["MacOS", "Darwin", "AIX"]: opts = None if isinstance(opts, six.string_types): - opts = opts.split(',') + opts = opts.split(",") if not os.path.exists(name) and mkmnt: - __salt__['file.mkdir'](name, user=user) + __salt__["file.mkdir"](name, user=user) - args = '' + args = "" if opts is not None: - lopts = ','.join(opts) - args = '-o {0}'.format(lopts) + lopts = ",".join(opts) + args = "-o {0}".format(lopts) if fstype: # use of fstype on AIX differs from typical Linux use of -t # functionality AIX uses -v vfsname, -t fstype mounts all with # fstype in /etc/filesystems - if 'AIX' in __grains__['os']: - args += ' -v {0}'.format(fstype) - elif 'solaris' in __grains__['os'].lower(): - args += ' -F {0}'.format(fstype) + if "AIX" in __grains__["os"]: + args += " -v {0}".format(fstype) + elif "solaris" in __grains__["os"].lower(): + args += " -F {0}".format(fstype) else: - args += ' -t {0}'.format(fstype) + args += " -t {0}".format(fstype) - cmd = 'mount {0} {1} {2} '.format(args, device, name) - out = __salt__['cmd.run_all'](cmd, runas=user, python_shell=False) - if out['retcode']: - return out['stderr'] + cmd = "mount {0} {1} {2} ".format(args, device, name) + out = __salt__["cmd.run_all"](cmd, runas=user, python_shell=False) + if out["retcode"]: + return out["stderr"] return True -def remount(name, device, mkmnt=False, fstype='', opts='defaults', user=None): - ''' +def remount(name, device, mkmnt=False, fstype="", opts="defaults", user=None): + """ Attempt to remount a device, if the device is not already mounted, mount is called @@ -1254,56 +1312,60 @@ def remount(name, device, mkmnt=False, fstype='', opts='defaults', user=None): .. code-block:: bash salt '*' mount.remount /mnt/foo /dev/sdz1 True - ''' + """ force_mount = False - if __grains__['os'] in ['MacOS', 'Darwin']: - if opts == 'defaults': - opts = 'noowners' - if fstype == 'smbfs': + if __grains__["os"] in ["MacOS", "Darwin"]: + if opts == "defaults": + opts = "noowners" + if fstype == "smbfs": force_mount = True - if 'AIX' in __grains__['os']: - if opts == 'defaults': + if "AIX" in __grains__["os"]: + if opts == "defaults": opts = [] if isinstance(opts, six.string_types): - opts = opts.split(',') + opts = opts.split(",") mnts = active() if name in mnts: # The mount point is mounted, attempt to remount it with the given data - if 'remount' not in opts and __grains__['os'] not in ['OpenBSD', 'MacOS', 'Darwin']: - opts.append('remount') + if "remount" not in opts and __grains__["os"] not in [ + "OpenBSD", + "MacOS", + "Darwin", + ]: + opts.append("remount") if force_mount: # We need to force the mount but first we should unmount umount(name, device, user=user) - lopts = ','.join(opts) - args = '-o {0}'.format(lopts) + lopts = ",".join(opts) + args = "-o {0}".format(lopts) if fstype: # use of fstype on AIX differs from typical Linux use of # -t functionality AIX uses -v vfsname, -t fstype mounts # all with fstype in /etc/filesystems - if 'AIX' in __grains__['os']: - args += ' -v {0}'.format(fstype) - elif 'solaris' in __grains__['os'].lower(): - args += ' -F {0}'.format(fstype) + if "AIX" in __grains__["os"]: + args += " -v {0}".format(fstype) + elif "solaris" in __grains__["os"].lower(): + args += " -F {0}".format(fstype) else: - args += ' -t {0}'.format(fstype) + args += " -t {0}".format(fstype) - if __grains__['os'] not in ['OpenBSD', 'MacOS', 'Darwin'] or force_mount: - cmd = 'mount {0} {1} {2} '.format(args, device, name) + if __grains__["os"] not in ["OpenBSD", "MacOS", "Darwin"] or force_mount: + cmd = "mount {0} {1} {2} ".format(args, device, name) else: - cmd = 'mount -u {0} {1} {2} '.format(args, device, name) - out = __salt__['cmd.run_all'](cmd, runas=user, python_shell=False) - if out['retcode']: - return out['stderr'] + cmd = "mount -u {0} {1} {2} ".format(args, device, name) + out = __salt__["cmd.run_all"](cmd, runas=user, python_shell=False) + if out["retcode"]: + return out["stderr"] return True # Mount a filesystem that isn't already return mount(name, device, mkmnt, fstype, opts, user=user) -def umount(name, device=None, user=None, util='mount'): - ''' +def umount(name, device=None, user=None, util="mount"): + """ Attempt to unmount a device by specifying the directory it is mounted on CLI Example: @@ -1316,12 +1378,15 @@ def umount(name, device=None, user=None, util='mount'): .. code-block:: bash salt '*' mount.umount /mnt/foo /dev/xvdc1 - ''' - if util != 'mount': + """ + if util == "guestfs": + __salt__["guestfs.umount"](name, disk=device) + return + elif util == "qemu_nbd": # This functionality used to live in img.umount_image - if 'qemu_nbd.clear' in __salt__: - if 'img.mnt_{0}'.format(name) in __context__: - __salt__['qemu_nbd.clear'](__context__['img.mnt_{0}'.format(name)]) + if "qemu_nbd.clear" in __salt__: + if "img.mnt_{0}".format(name) in __context__: + __salt__["qemu_nbd.clear"](__context__["img.mnt_{0}".format(name)]) return mnts = active() @@ -1329,17 +1394,17 @@ def umount(name, device=None, user=None, util='mount'): return "{0} does not have anything mounted".format(name) if not device: - cmd = 'umount {0}'.format(name) + cmd = "umount {0}".format(name) else: - cmd = 'umount {0}'.format(device) - out = __salt__['cmd.run_all'](cmd, runas=user, python_shell=False) - if out['retcode']: - return out['stderr'] + cmd = "umount {0}".format(device) + out = __salt__["cmd.run_all"](cmd, runas=user, python_shell=False) + if out["retcode"]: + return out["stderr"] return True def is_fuse_exec(cmd): - ''' + """ Returns true if the command passed is a fuse mountable application. CLI Example: @@ -1347,21 +1412,21 @@ def is_fuse_exec(cmd): .. code-block:: bash salt '*' mount.is_fuse_exec sshfs - ''' + """ cmd_path = salt.utils.path.which(cmd) # No point in running ldd on a command that doesn't exist if not cmd_path: return False - elif not salt.utils.path.which('ldd'): - raise CommandNotFoundError('ldd') + elif not salt.utils.path.which("ldd"): + raise CommandNotFoundError("ldd") - out = __salt__['cmd.run']('ldd {0}'.format(cmd_path), python_shell=False) - return 'libfuse' in out + out = __salt__["cmd.run"]("ldd {0}".format(cmd_path), python_shell=False) + return "libfuse" in out def swaps(): - ''' + """ Return a dict containing information on active swap .. versionchanged:: 2016.3.2 @@ -1371,56 +1436,64 @@ def swaps(): .. code-block:: bash salt '*' mount.swaps - ''' + """ ret = {} - if __grains__['kernel'] == 'SunOS': - for line in __salt__['cmd.run_stdout']('swap -l').splitlines(): - if line.startswith('swapfile'): + if __grains__["kernel"] == "SunOS": + for line in __salt__["cmd.run_stdout"]("swap -l").splitlines(): + if line.startswith("swapfile"): continue comps = line.split() - ret[comps[0]] = {'type': 'device' if comps[0].startswith(('/dev', 'swap')) else 'file', - 'size': int(comps[3]), - 'used': (int(comps[3]) - int(comps[4])), - 'priority': '-'} - elif 'AIX' in __grains__['kernel']: - for line in __salt__['cmd.run_stdout']('swap -l').splitlines(): - if line.startswith('device'): + ret[comps[0]] = { + "type": "device" if comps[0].startswith(("/dev", "swap")) else "file", + "size": int(comps[3]), + "used": (int(comps[3]) - int(comps[4])), + "priority": "-", + } + elif "AIX" in __grains__["kernel"]: + for line in __salt__["cmd.run_stdout"]("swap -l").splitlines(): + if line.startswith("device"): continue comps = line.split() # AIX uses MB for units - ret[comps[0]] = {'type': 'device', - 'size': int(comps[3][:-2]) * 1024, - 'used': (int(comps[3][:-2]) - int(comps[4][:-2])) * 1024, - 'priority': '-'} - elif __grains__['os'] != 'OpenBSD': - with salt.utils.files.fopen('/proc/swaps') as fp_: + ret[comps[0]] = { + "type": "device", + "size": int(comps[3][:-2]) * 1024, + "used": (int(comps[3][:-2]) - int(comps[4][:-2])) * 1024, + "priority": "-", + } + elif __grains__["os"] != "OpenBSD": + with salt.utils.files.fopen("/proc/swaps") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('Filename'): + if line.startswith("Filename"): continue comps = line.split() - ret[comps[0]] = {'type': comps[1], - 'size': comps[2], - 'used': comps[3], - 'priority': comps[4]} + ret[comps[0]] = { + "type": comps[1], + "size": comps[2], + "used": comps[3], + "priority": comps[4], + } else: - for line in __salt__['cmd.run_stdout']('swapctl -kl').splitlines(): - if line.startswith(('Device', 'Total')): + for line in __salt__["cmd.run_stdout"]("swapctl -kl").splitlines(): + if line.startswith(("Device", "Total")): continue swap_type = "file" comps = line.split() - if comps[0].startswith('/dev/'): + if comps[0].startswith("/dev/"): swap_type = "partition" - ret[comps[0]] = {'type': swap_type, - 'size': comps[1], - 'used': comps[2], - 'priority': comps[5]} + ret[comps[0]] = { + "type": swap_type, + "size": comps[1], + "used": comps[2], + "priority": comps[5], + } return ret def swapon(name, priority=None): - ''' + """ Activate a swap disk .. versionchanged:: 2016.3.2 @@ -1430,36 +1503,36 @@ def swapon(name, priority=None): .. code-block:: bash salt '*' mount.swapon /root/swapfile - ''' + """ ret = {} on_ = swaps() if name in on_: - ret['stats'] = on_[name] - ret['new'] = False + ret["stats"] = on_[name] + ret["new"] = False return ret - if __grains__['kernel'] == 'SunOS': - if __grains__['virtual'] != 'zone': - __salt__['cmd.run']('swap -a {0}'.format(name), python_shell=False) + if __grains__["kernel"] == "SunOS": + if __grains__["virtual"] != "zone": + __salt__["cmd.run"]("swap -a {0}".format(name), python_shell=False) else: return False else: - cmd = 'swapon {0}'.format(name) - if priority and 'AIX' not in __grains__['kernel']: - cmd += ' -p {0}'.format(priority) - __salt__['cmd.run'](cmd, python_shell=False) + cmd = "swapon {0}".format(name) + if priority and "AIX" not in __grains__["kernel"]: + cmd += " -p {0}".format(priority) + __salt__["cmd.run"](cmd, python_shell=False) on_ = swaps() if name in on_: - ret['stats'] = on_[name] - ret['new'] = True + ret["stats"] = on_[name] + ret["new"] = True return ret return ret def swapoff(name): - ''' + """ Deactivate a named swap mount .. versionchanged:: 2016.3.2 @@ -1469,19 +1542,18 @@ def swapoff(name): .. code-block:: bash salt '*' mount.swapoff /root/swapfile - ''' + """ on_ = swaps() if name in on_: - if __grains__['kernel'] == 'SunOS': - if __grains__['virtual'] != 'zone': - __salt__['cmd.run']('swap -a {0}'.format(name), python_shell=False) + if __grains__["kernel"] == "SunOS": + if __grains__["virtual"] != "zone": + __salt__["cmd.run"]("swap -a {0}".format(name), python_shell=False) else: return False - elif __grains__['os'] != 'OpenBSD': - __salt__['cmd.run']('swapoff {0}'.format(name), python_shell=False) + elif __grains__["os"] != "OpenBSD": + __salt__["cmd.run"]("swapoff {0}".format(name), python_shell=False) else: - __salt__['cmd.run']('swapctl -d {0}'.format(name), - python_shell=False) + __salt__["cmd.run"]("swapctl -d {0}".format(name), python_shell=False) on_ = swaps() if name in on_: return False @@ -1490,7 +1562,7 @@ def swapoff(name): def is_mounted(name): - ''' + """ .. versionadded:: 2014.7.0 Provide information if the path is mounted @@ -1500,7 +1572,7 @@ def is_mounted(name): .. code-block:: bash salt '*' mount.is_mounted /mnt/share - ''' + """ active_ = active() if name in active_: return True @@ -1509,7 +1581,7 @@ def is_mounted(name): def read_mount_cache(name): - ''' + """ .. versionadded:: 2018.3.0 Provide information if the path is mounted @@ -1519,21 +1591,17 @@ def read_mount_cache(name): .. code-block:: bash salt '*' mount.read_mount_cache /mnt/share - ''' + """ cache = salt.utils.mount.read_cache(__opts__) if cache: - if 'mounts' in cache and cache['mounts']: - if name in cache['mounts']: - return cache['mounts'][name] + if "mounts" in cache and cache["mounts"]: + if name in cache["mounts"]: + return cache["mounts"][name] return {} -def write_mount_cache(real_name, - device, - mkmnt, - fstype, - mount_opts): - ''' +def write_mount_cache(real_name, device, mkmnt, fstype, mount_opts): + """ .. versionadded:: 2018.3.0 Provide information if the path is mounted @@ -1550,30 +1618,32 @@ def write_mount_cache(real_name, .. code-block:: bash salt '*' mount.write_mount_cache /mnt/share /dev/sda1 False ext4 defaults,nosuid - ''' + """ cache = salt.utils.mount.read_cache(__opts__) if not cache: cache = {} - cache['mounts'] = {} + cache["mounts"] = {} else: - if 'mounts' not in cache: - cache['mounts'] = {} + if "mounts" not in cache: + cache["mounts"] = {} - cache['mounts'][real_name] = {'device': device, - 'fstype': fstype, - 'mkmnt': mkmnt, - 'opts': mount_opts} + cache["mounts"][real_name] = { + "device": device, + "fstype": fstype, + "mkmnt": mkmnt, + "opts": mount_opts, + } cache_write = salt.utils.mount.write_cache(cache, __opts__) if cache_write: return True else: - raise CommandExecutionError('Unable to write mount cache.') + raise CommandExecutionError("Unable to write mount cache.") def delete_mount_cache(real_name): - ''' + """ .. versionadded:: 2018.3.0 Provide information if the path is mounted @@ -1583,21 +1653,21 @@ def delete_mount_cache(real_name): .. code-block:: bash salt '*' mount.delete_mount_cache /mnt/share - ''' + """ cache = salt.utils.mount.read_cache(__opts__) if cache: - if 'mounts' in cache: - if real_name in cache['mounts']: - del cache['mounts'][real_name] + if "mounts" in cache: + if real_name in cache["mounts"]: + del cache["mounts"][real_name] cache_write = salt.utils.mount.write_cache(cache, __opts__) if not cache_write: - raise CommandExecutionError('Unable to write mount cache.') + raise CommandExecutionError("Unable to write mount cache.") return True -def _filesystems(config='/etc/filesystems', leading_key=True): - ''' +def _filesystems(config="/etc/filesystems", leading_key=True): + """ Return the contents of the filesystems in an OrderedDict config @@ -1609,11 +1679,11 @@ def _filesystems(config='/etc/filesystems', leading_key=True): False return dictionary keyed by 'name' and value as dictionary with all keys, values (name included) OrderedDict({ '/dir' : OrderedDict({'name': '/dir', 'dev': '/dev/hd8', ... })}) - ''' + """ ret = OrderedDict() lines = [] parsing_block = False - if not os.path.isfile(config) or 'AIX' not in __grains__['kernel']: + if not os.path.isfile(config) or "AIX" not in __grains__["kernel"]: return ret # read in block of filesystems, block starts with '/' till empty line @@ -1622,28 +1692,28 @@ def _filesystems(config='/etc/filesystems', leading_key=True): line = salt.utils.stringutils.to_unicode(line) # skip till first entry - if not line.startswith('/') and not parsing_block: + if not line.startswith("/") and not parsing_block: continue - if line.startswith('/'): + if line.startswith("/"): parsing_block = True lines.append(line) elif not line.split(): parsing_block = False try: entry = _FileSystemsEntry.dict_from_lines( - lines, - _FileSystemsEntry.compatibility_keys) + lines, _FileSystemsEntry.compatibility_keys + ) lines = [] - if 'opts' in entry: - entry['opts'] = entry['opts'].split(',') - while entry['name'] in ret: - entry['name'] += '_' + if "opts" in entry: + entry["opts"] = entry["opts"].split(",") + while entry["name"] in ret: + entry["name"] += "_" if leading_key: - ret[entry.pop('name')] = entry + ret[entry.pop("name")] = entry else: - ret[entry['name']] = entry + ret[entry["name"]] = entry except _FileSystemsEntry.ParseError: pass @@ -1653,8 +1723,8 @@ def _filesystems(config='/etc/filesystems', leading_key=True): return ret -def filesystems(config='/etc/filesystems'): - ''' +def filesystems(config="/etc/filesystems"): + """ .. versionadded:: 2018.3.3 List the contents of the filesystems @@ -1664,9 +1734,9 @@ def filesystems(config='/etc/filesystems'): .. code-block:: bash salt '*' mount.filesystems - ''' + """ ret = {} - if 'AIX' not in __grains__['kernel']: + if "AIX" not in __grains__["kernel"]: return ret ret_dict = _filesystems(config) @@ -1678,17 +1748,18 @@ def filesystems(config='/etc/filesystems'): def set_filesystems( - name, - device, - vfstype, - opts='-', - mount='true', - config='/etc/filesystems', - test=False, - match_on='auto', - not_change=False, - **kwargs): - ''' + name, + device, + vfstype, + opts="-", + mount="true", + config="/etc/filesystems", + test=False, + match_on="auto", + not_change=False, + **kwargs +): + """ .. versionadded:: 2018.3.3 Verify that this mount is represented in the filesystems, change the mount @@ -1712,67 +1783,70 @@ def set_filesystems( .. code-block:: bash salt '*' mount.set_filesystems /mnt/foo /dev/sdz1 jfs2 - ''' + """ # Fix the opts type if it is a list if isinstance(opts, list): - opts = ','.join(opts) + opts = ",".join(opts) # preserve arguments for updating entry_args = { - 'name': name, - 'dev': device.replace('\\ ', '\\040'), - 'vfstype': vfstype, - 'opts': opts, - 'mount': mount, + "name": name, + "dev": device.replace("\\ ", "\\040"), + "vfstype": vfstype, + "opts": opts, + "mount": mount, } view_lines = [] ret = None - if 'AIX' not in __grains__['kernel']: + if "AIX" not in __grains__["kernel"]: return ret # Transform match_on into list--items will be checked later if isinstance(match_on, list): pass elif not isinstance(match_on, six.string_types): - raise CommandExecutionError('match_on must be a string or list of strings') - elif match_on == 'auto': + raise CommandExecutionError("match_on must be a string or list of strings") + elif match_on == "auto": # Try to guess right criteria for auto.... # added IBM types from sys/vmount.h after btrfs # NOTE: missing some special fstypes here - specialFSes = frozenset([ - 'none', - 'tmpfs', - 'sysfs', - 'proc', - 'fusectl', - 'debugfs', - 'securityfs', - 'devtmpfs', - 'cgroup', - 'btrfs', - 'cdrfs', - 'procfs', - 'jfs', - 'jfs2', - 'nfs', - 'sfs', - 'nfs3', - 'cachefs', - 'udfs', - 'cifs', - 'namefs', - 'pmemfs', - 'ahafs', - 'nfs4', - 'autofs', - 'stnfs']) + specialFSes = frozenset( + [ + "none", + "tmpfs", + "sysfs", + "proc", + "fusectl", + "debugfs", + "securityfs", + "devtmpfs", + "cgroup", + "btrfs", + "cdrfs", + "procfs", + "jfs", + "jfs2", + "nfs", + "sfs", + "nfs3", + "cachefs", + "udfs", + "cifs", + "namefs", + "pmemfs", + "ahafs", + "nfs4", + "autofs", + "stnfs", + ] + ) if vfstype in specialFSes: - match_on = ['name'] + match_on = ["name"] else: - match_on = ['dev'] + match_on = ["dev"] else: match_on = [match_on] @@ -1784,7 +1858,9 @@ def set_filesystems( except KeyError: filterFn = lambda key: key not in _FileSystemsEntry.compatibility_keys invalid_keys = filter(filterFn, match_on) - raise CommandExecutionError('Unrecognized keys in match_on: "{0}"'.format(invalid_keys)) + raise CommandExecutionError( + 'Unrecognized keys in match_on: "{0}"'.format(invalid_keys) + ) # parse file, use ret to cache status if not os.path.isfile(config): @@ -1795,41 +1871,41 @@ def set_filesystems( fsys_filedict = _filesystems(config, False) for fsys_view in six.viewitems(fsys_filedict): if criteria.match(fsys_view): - ret = 'present' + ret = "present" if entry_ip.match(fsys_view) or not_change: view_lines.append(fsys_view) else: - ret = 'change' - kv = entry_ip['name'] + ret = "change" + kv = entry_ip["name"] view_lines.append((kv, entry_ip)) else: view_lines.append(fsys_view) except (IOError, OSError) as exc: - raise CommandExecutionError('Couldn\'t read from {0}: {1}'.format(config, exc)) + raise CommandExecutionError("Couldn't read from {0}: {1}".format(config, exc)) # add line if not present or changed if ret is None: for dict_view in six.viewitems(entry_ip.dict_from_entry()): view_lines.append(dict_view) - ret = 'new' + ret = "new" - if ret != 'present': # ret in ['new', 'change']: + if ret != "present": # ret in ['new', 'change']: try: - with salt.utils.files.fopen(config, 'wb') as ofile: + with salt.utils.files.fopen(config, "wb") as ofile: # The line was changed, commit it! for fsys_view in view_lines: entry = fsys_view[1] mystrg = _FileSystemsEntry.dict_to_lines(entry) ofile.writelines(salt.utils.data.encode(mystrg)) except (IOError, OSError): - raise CommandExecutionError('File not writable {0}'.format(config)) + raise CommandExecutionError("File not writable {0}".format(config)) return ret -def rm_filesystems(name, device, config='/etc/filesystems'): - ''' +def rm_filesystems(name, device, config="/etc/filesystems"): + """ .. versionadded:: 2018.3.3 Remove the mount point from the filesystems @@ -1839,11 +1915,11 @@ def rm_filesystems(name, device, config='/etc/filesystems'): .. code-block:: bash salt '*' mount.rm_filesystems /mnt/foo /dev/sdg - ''' + """ modified = False view_lines = [] - if 'AIX' not in __grains__['kernel']: + if "AIX" not in __grains__["kernel"]: return modified criteria = _FileSystemsEntry(name=name, dev=device) @@ -1864,12 +1940,14 @@ def rm_filesystems(name, device, config='/etc/filesystems'): if modified: try: - with salt.utils.files.fopen(config, 'wb') as ofile: + with salt.utils.files.fopen(config, "wb") as ofile: for fsys_view in view_lines: entry = fsys_view[1] mystrg = _FileSystemsEntry.dict_to_lines(entry) ofile.writelines(salt.utils.data.encode(mystrg)) except (IOError, OSError) as exc: - raise CommandExecutionError("Couldn't write to {0}: {1}".format(config, exc)) + raise CommandExecutionError( + "Couldn't write to {0}: {1}".format(config, exc) + ) return modified diff --git a/salt/modules/mssql.py b/salt/modules/mssql.py index 51350475a27..d9666ab64e3 100644 --- a/salt/modules/mssql.py +++ b/salt/modules/mssql.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide MS SQL Server compatibility to salt. :depends: - FreeTDS @@ -19,7 +19,7 @@ Module to provide MS SQL Server compatibility to salt. in most cases they can be left at the default setting. Options that are directly passed into functions will overwrite options from configs or pillars. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -28,40 +28,45 @@ from __future__ import absolute_import, print_function, unicode_literals import salt.ext.six as six import salt.utils.json - try: import pymssql + HAS_ALL_IMPORTS = True except ImportError: HAS_ALL_IMPORTS = False _DEFAULTS = { - 'server': 'localhost', - 'port': 1433, - 'user': 'sysdba', - 'password': '', - 'database': '', - 'as_dict': False + "server": "localhost", + "port": 1433, + "user": "sysdba", + "password": "", + "database": "", + "as_dict": False, } def __virtual__(): - ''' + """ Only load this module if all imports succeeded bin exists - ''' + """ if HAS_ALL_IMPORTS: return True - return (False, 'The mssql execution module cannot be loaded: the pymssql python library is not available.') + return ( + False, + "The mssql execution module cannot be loaded: the pymssql python library is not available.", + ) def _get_connection(**kwargs): connection_args = {} - for arg in ('server', 'port', 'user', 'password', 'database', 'as_dict'): + for arg in ("server", "port", "user", "password", "database", "as_dict"): if arg in kwargs: connection_args[arg] = kwargs[arg] else: - connection_args[arg] = __salt__['config.option']('mssql.'+arg, _DEFAULTS.get(arg, None)) + connection_args[arg] = __salt__["config.option"]( + "mssql." + arg, _DEFAULTS.get(arg, None) + ) return pymssql.connect(**connection_args) @@ -72,7 +77,7 @@ class _MssqlEncoder(salt.utils.json.JSONEncoder): def tsql_query(query, **kwargs): - ''' + """ Run a SQL query and return query result as list of tuples, or a list of dictionaries if as_dict was passed, or an empty list if no data is available. CLI Example: @@ -80,19 +85,21 @@ def tsql_query(query, **kwargs): .. code-block:: bash salt minion mssql.tsql_query 'SELECT @@version as version' as_dict=True - ''' + """ try: cur = _get_connection(**kwargs).cursor() cur.execute(query) # Making sure the result is JSON serializable - return salt.utils.json.loads(_MssqlEncoder().encode({'resultset': cur.fetchall()}))['resultset'] + return salt.utils.json.loads( + _MssqlEncoder().encode({"resultset": cur.fetchall()}) + )["resultset"] except Exception as err: # pylint: disable=broad-except # Trying to look like the output of cur.fetchall() - return (('Could not run the query', ), (six.text_type(err), )) + return (("Could not run the query",), (six.text_type(err),)) def version(**kwargs): - ''' + """ Return the version of a MS SQL server. CLI Example: @@ -100,12 +107,12 @@ def version(**kwargs): .. code-block:: bash salt minion mssql.version - ''' - return tsql_query('SELECT @@version', **kwargs) + """ + return tsql_query("SELECT @@version", **kwargs) def db_list(**kwargs): - ''' + """ Return the database list created on a MS SQL server. CLI Example: @@ -113,12 +120,15 @@ def db_list(**kwargs): .. code-block:: bash salt minion mssql.db_list - ''' - return [row[0] for row in tsql_query('SELECT name FROM sys.databases', as_dict=False, **kwargs)] + """ + return [ + row[0] + for row in tsql_query("SELECT name FROM sys.databases", as_dict=False, **kwargs) + ] def db_exists(database_name, **kwargs): - ''' + """ Find if a specific database exists on the MS SQL server. CLI Example: @@ -126,13 +136,23 @@ def db_exists(database_name, **kwargs): .. code-block:: bash salt minion mssql.db_exists database_name='DBNAME' - ''' + """ # We should get one, and only one row - return len(tsql_query("SELECT database_id FROM sys.databases WHERE NAME='{0}'".format(database_name), **kwargs)) == 1 + return ( + len( + tsql_query( + "SELECT database_id FROM sys.databases WHERE NAME='{0}'".format( + database_name + ), + **kwargs + ) + ) + == 1 + ) -def db_create(database, containment='NONE', new_database_options=None, **kwargs): - ''' +def db_create(database, containment="NONE", new_database_options=None, **kwargs): + """ Creates a new database. Does not update options of existing databases. new_database_options can only be a list of strings @@ -142,12 +162,12 @@ def db_create(database, containment='NONE', new_database_options=None, **kwargs) .. code-block:: bash salt minion mssql.db_create DB_NAME - ''' - if containment not in ['NONE', 'PARTIAL']: - return 'CONTAINMENT can be one of NONE and PARTIAL' + """ + if containment not in ["NONE", "PARTIAL"]: + return "CONTAINMENT can be one of NONE and PARTIAL" sql = "CREATE DATABASE [{0}] CONTAINMENT = {1} ".format(database, containment) if new_database_options: - sql += ' WITH ' + ', '.join(new_database_options) + sql += " WITH " + ", ".join(new_database_options) conn = None try: conn = _get_connection(**kwargs) @@ -156,7 +176,7 @@ def db_create(database, containment='NONE', new_database_options=None, **kwargs) # cur.execute(sql) conn.cursor().execute(sql) except Exception as e: # pylint: disable=broad-except - return 'Could not create the login: {0}'.format(e) + return "Could not create the login: {0}".format(e) finally: if conn: conn.autocommit(False) @@ -165,7 +185,7 @@ def db_create(database, containment='NONE', new_database_options=None, **kwargs) def db_remove(database_name, **kwargs): - ''' + """ Drops a specific database from the MS SQL server. It will not drop any of 'master', 'model', 'msdb' or 'tempdb'. @@ -174,26 +194,35 @@ def db_remove(database_name, **kwargs): .. code-block:: bash salt minion mssql.db_remove database_name='DBNAME' - ''' + """ try: - if db_exists(database_name, **kwargs) and database_name not in ['master', 'model', 'msdb', 'tempdb']: + if db_exists(database_name, **kwargs) and database_name not in [ + "master", + "model", + "msdb", + "tempdb", + ]: conn = _get_connection(**kwargs) conn.autocommit(True) cur = conn.cursor() - cur.execute('ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE'.format(database_name)) - cur.execute('DROP DATABASE {0}'.format(database_name)) + cur.execute( + "ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE".format( + database_name + ) + ) + cur.execute("DROP DATABASE {0}".format(database_name)) conn.autocommit(False) conn.close() return True else: return False except Exception as e: # pylint: disable=broad-except - return 'Could not find the database: {0}'.format(e) + return "Could not find the database: {0}".format(e) def role_list(**kwargs): - ''' + """ Lists database roles. CLI Example: @@ -201,13 +230,13 @@ def role_list(**kwargs): .. code-block:: bash salt minion mssql.role_list - ''' - return tsql_query(query='sp_helprole', as_dict=True, **kwargs) + """ + return tsql_query(query="sp_helprole", as_dict=True, **kwargs) def role_exists(role, **kwargs): - ''' + """ Checks if a role exists. CLI Example: @@ -215,13 +244,16 @@ def role_exists(role, **kwargs): .. code-block:: bash salt minion mssql.role_exists db_owner - ''' + """ # We should get one, and only one row - return len(tsql_query(query='sp_helprole "{0}"'.format(role), as_dict=True, **kwargs)) == 1 + return ( + len(tsql_query(query='sp_helprole "{0}"'.format(role), as_dict=True, **kwargs)) + == 1 + ) def role_create(role, owner=None, grants=None, **kwargs): - ''' + """ Creates a new database role. If no owner is specified, the role will be owned by the user that executes CREATE ROLE, which is the user argument or mssql.user option. @@ -232,13 +264,13 @@ def role_create(role, owner=None, grants=None, **kwargs): .. code-block:: bash salt minion mssql.role_create role=product01 owner=sysdba grants='["SELECT", "INSERT", "UPDATE", "DELETE", "EXECUTE"]' - ''' + """ if not grants: grants = [] - sql = 'CREATE ROLE {0}'.format(role) + sql = "CREATE ROLE {0}".format(role) if owner: - sql += ' AUTHORIZATION {0}'.format(owner) + sql += " AUTHORIZATION {0}".format(owner) conn = None try: conn = _get_connection(**kwargs) @@ -247,9 +279,9 @@ def role_create(role, owner=None, grants=None, **kwargs): # cur.execute(sql) conn.cursor().execute(sql) for grant in grants: - conn.cursor().execute('GRANT {0} TO [{1}]'.format(grant, role)) + conn.cursor().execute("GRANT {0} TO [{1}]".format(grant, role)) except Exception as e: # pylint: disable=broad-except - return 'Could not create the role: {0}'.format(e) + return "Could not create the role: {0}".format(e) finally: if conn: conn.autocommit(False) @@ -258,7 +290,7 @@ def role_create(role, owner=None, grants=None, **kwargs): def role_remove(role, **kwargs): - ''' + """ Remove a database role. CLI Example: @@ -266,21 +298,21 @@ def role_remove(role, **kwargs): .. code-block:: bash salt minion mssql.role_create role=test_role01 - ''' + """ try: conn = _get_connection(**kwargs) conn.autocommit(True) cur = conn.cursor() - cur.execute('DROP ROLE {0}'.format(role)) + cur.execute("DROP ROLE {0}".format(role)) conn.autocommit(True) conn.close() return True except Exception as e: # pylint: disable=broad-except - return 'Could not create the role: {0}'.format(e) + return "Could not create the role: {0}".format(e) -def login_exists(login, domain='', **kwargs): - ''' +def login_exists(login, domain="", **kwargs): + """ Find if a login exists in the MS SQL server. domain, if provided, will be prepended to login @@ -289,19 +321,36 @@ def login_exists(login, domain='', **kwargs): .. code-block:: bash salt minion mssql.login_exists 'LOGIN' - ''' + """ if domain: - login = '{0}\\{1}'.format(domain, login) + login = "{0}\\{1}".format(domain, login) try: # We should get one, and only one row - return len(tsql_query(query="SELECT name FROM sys.syslogins WHERE name='{0}'".format(login), **kwargs)) == 1 + return ( + len( + tsql_query( + query="SELECT name FROM sys.syslogins WHERE name='{0}'".format( + login + ), + **kwargs + ) + ) + == 1 + ) except Exception as e: # pylint: disable=broad-except - return 'Could not find the login: {0}'.format(e) + return "Could not find the login: {0}".format(e) -def login_create(login, new_login_password=None, new_login_domain='', new_login_roles=None, new_login_options=None, **kwargs): - ''' +def login_create( + login, + new_login_password=None, + new_login_domain="", + new_login_roles=None, + new_login_options=None, + **kwargs +): + """ Creates a new login. Does not update password of existing logins. For Windows authentication, provide ``new_login_domain``. For SQL Server authentication, prvide ``new_login_password``. Since hashed passwords are @@ -319,14 +368,14 @@ def login_create(login, new_login_password=None, new_login_domain='', new_login_ .. code-block:: bash salt minion mssql.login_create LOGIN_NAME database=DBNAME [new_login_password=PASSWORD] - ''' + """ # One and only one of password and domain should be specifies if bool(new_login_password) == bool(new_login_domain): return False if login_exists(login, new_login_domain, **kwargs): return False if new_login_domain: - login = '{0}\\{1}'.format(new_login_domain, login) + login = "{0}\\{1}".format(new_login_domain, login) if not new_login_roles: new_login_roles = [] if not new_login_options: @@ -336,11 +385,13 @@ def login_create(login, new_login_password=None, new_login_domain='', new_login_ if new_login_domain: sql += " FROM WINDOWS " elif isinstance(new_login_password, six.integer_types): - new_login_options.insert(0, "PASSWORD=0x{0:x} HASHED".format(new_login_password)) + new_login_options.insert( + 0, "PASSWORD=0x{0:x} HASHED".format(new_login_password) + ) else: # Plain test password new_login_options.insert(0, "PASSWORD=N'{0}'".format(new_login_password)) if new_login_options: - sql += ' WITH ' + ', '.join(new_login_options) + sql += " WITH " + ", ".join(new_login_options) conn = None try: conn = _get_connection(**kwargs) @@ -349,9 +400,11 @@ def login_create(login, new_login_password=None, new_login_domain='', new_login_ # cur.execute(sql) conn.cursor().execute(sql) for role in new_login_roles: - conn.cursor().execute('ALTER SERVER ROLE [{0}] ADD MEMBER [{1}]'.format(role, login)) + conn.cursor().execute( + "ALTER SERVER ROLE [{0}] ADD MEMBER [{1}]".format(role, login) + ) except Exception as e: # pylint: disable=broad-except - return 'Could not create the login: {0}'.format(e) + return "Could not create the login: {0}".format(e) finally: if conn: conn.autocommit(False) @@ -360,7 +413,7 @@ def login_create(login, new_login_password=None, new_login_domain='', new_login_ def login_remove(login, **kwargs): - ''' + """ Removes an login. CLI Example: @@ -368,7 +421,7 @@ def login_remove(login, **kwargs): .. code-block:: bash salt minion mssql.login_remove LOGINNAME - ''' + """ try: conn = _get_connection(**kwargs) conn.autocommit(True) @@ -378,11 +431,11 @@ def login_remove(login, **kwargs): conn.close() return True except Exception as e: # pylint: disable=broad-except - return 'Could not remove the login: {0}'.format(e) + return "Could not remove the login: {0}".format(e) -def user_exists(username, domain='', database=None, **kwargs): - ''' +def user_exists(username, domain="", database=None, **kwargs): + """ Find if an user exists in a specific database on the MS SQL server. domain, if provided, will be prepended to username @@ -391,17 +444,25 @@ def user_exists(username, domain='', database=None, **kwargs): .. code-block:: bash salt minion mssql.user_exists 'USERNAME' [database='DBNAME'] - ''' + """ if domain: - username = '{0}\\{1}'.format(domain, username) + username = "{0}\\{1}".format(domain, username) if database: - kwargs['database'] = database + kwargs["database"] = database # We should get one, and only one row - return len(tsql_query(query="SELECT name FROM sysusers WHERE name='{0}'".format(username), **kwargs)) == 1 + return ( + len( + tsql_query( + query="SELECT name FROM sysusers WHERE name='{0}'".format(username), + **kwargs + ) + ) + == 1 + ) def user_list(**kwargs): - ''' + """ Get the user list for a specific database on the MS SQL server. CLI Example: @@ -409,12 +470,21 @@ def user_list(**kwargs): .. code-block:: bash salt minion mssql.user_list [database='DBNAME'] - ''' - return [row[0] for row in tsql_query("SELECT name FROM sysusers where issqluser=1 or isntuser=1", as_dict=False, **kwargs)] + """ + return [ + row[0] + for row in tsql_query( + "SELECT name FROM sysusers where issqluser=1 or isntuser=1", + as_dict=False, + **kwargs + ) + ] -def user_create(username, login=None, domain='', database=None, roles=None, options=None, **kwargs): - ''' +def user_create( + username, login=None, domain="", database=None, roles=None, options=None, **kwargs +): + """ Creates a new user. If login is not specified, the user will be created without a login. domain, if provided, will be prepended to username. options can only be a list of strings @@ -424,16 +494,16 @@ def user_create(username, login=None, domain='', database=None, roles=None, opti .. code-block:: bash salt minion mssql.user_create USERNAME database=DBNAME - ''' + """ if domain and not login: - return 'domain cannot be set without login' + return "domain cannot be set without login" if user_exists(username, domain, **kwargs): - return 'User {0} already exists'.format(username) + return "User {0} already exists".format(username) if domain: - username = '{0}\\{1}'.format(domain, username) - login = '{0}\\{1}'.format(domain, login) if login else login + username = "{0}\\{1}".format(domain, username) + login = "{0}\\{1}".format(domain, login) if login else login if database: - kwargs['database'] = database + kwargs["database"] = database if not roles: roles = [] if not options: @@ -448,7 +518,7 @@ def user_create(username, login=None, domain='', database=None, roles=None, opti else: # Plain test password sql += " WITHOUT LOGIN" if options: - sql += ' WITH ' + ', '.join(options) + sql += " WITH " + ", ".join(options) conn = None try: conn = _get_connection(**kwargs) @@ -457,9 +527,11 @@ def user_create(username, login=None, domain='', database=None, roles=None, opti # cur.execute(sql) conn.cursor().execute(sql) for role in roles: - conn.cursor().execute('ALTER ROLE [{0}] ADD MEMBER [{1}]'.format(role, username)) + conn.cursor().execute( + "ALTER ROLE [{0}] ADD MEMBER [{1}]".format(role, username) + ) except Exception as e: # pylint: disable=broad-except - return 'Could not create the user: {0}'.format(e) + return "Could not create the user: {0}".format(e) finally: if conn: conn.autocommit(False) @@ -468,7 +540,7 @@ def user_create(username, login=None, domain='', database=None, roles=None, opti def user_remove(username, **kwargs): - ''' + """ Removes an user. CLI Example: @@ -476,9 +548,9 @@ def user_remove(username, **kwargs): .. code-block:: bash salt minion mssql.user_remove USERNAME database=DBNAME - ''' + """ # 'database' argument is mandatory - if 'database' not in kwargs: + if "database" not in kwargs: return False try: conn = _get_connection(**kwargs) @@ -489,4 +561,4 @@ def user_remove(username, **kwargs): conn.close() return True except Exception as e: # pylint: disable=broad-except - return 'Could not create the user: {0}'.format(e) + return "Could not create the user: {0}".format(e) diff --git a/salt/modules/msteams.py b/salt/modules/msteams.py index 69e40f75c29..2b64e55e3ae 100644 --- a/salt/modules/msteams.py +++ b/salt/modules/msteams.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for sending messages to MS Teams .. versionadded:: 2017.7.0 @@ -12,50 +12,49 @@ Module for sending messages to MS Teams msteams: hook_url: https://outlook.office.com/webhook/837 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +# Import 3rd-party libs +import salt.ext.six.moves.http_client # pylint: disable=import-error,no-name-in-module,redefined-builtin + # Import Salt libs import salt.utils.json from salt.exceptions import SaltInvocationError -# Import 3rd-party libs -import salt.ext.six.moves.http_client # pylint: disable=import-error,no-name-in-module,redefined-builtin - log = logging.getLogger(__name__) -__virtualname__ = 'msteams' +__virtualname__ = "msteams" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ def _get_hook_url(): - ''' + """ Return hook_url from minion/master config file or from pillar - ''' - hook_url = __salt__['config.get']('msteams.hook_url') or \ - __salt__['config.get']('msteams:hook_url') + """ + hook_url = __salt__["config.get"]("msteams.hook_url") or __salt__["config.get"]( + "msteams:hook_url" + ) if not hook_url: - raise SaltInvocationError('No MS Teams hook_url found.') + raise SaltInvocationError("No MS Teams hook_url found.") return hook_url -def post_card(message, - hook_url=None, - title=None, - theme_color=None): - ''' +def post_card(message, hook_url=None, title=None, theme_color=None): + """ Send a message to an MS Teams channel. :param message: The message to send to the MS Teams channel. :param hook_url: The Teams webhook URL, if not specified in the configuration. @@ -68,29 +67,21 @@ def post_card(message, .. code-block:: bash salt '*' msteams.post_card message="Build is done" - ''' + """ if not hook_url: hook_url = _get_hook_url() if not message: - log.error('message is a required option.') + log.error("message is a required option.") - payload = { - "text": message, - "title": title, - "themeColor": theme_color - } + payload = {"text": message, "title": title, "themeColor": theme_color} - result = salt.utils.http.query(hook_url, - method='POST', - data=salt.utils.json.dumps(payload), - status=True) + result = salt.utils.http.query( + hook_url, method="POST", data=salt.utils.json.dumps(payload), status=True + ) - if result['status'] <= 201: + if result["status"] <= 201: return True else: - return { - 'res': False, - 'message': result.get('body', result['status']) - } + return {"res": False, "message": result.get("body", result["status"])} diff --git a/salt/modules/munin.py b/salt/modules/munin.py index 27547e1d602..dc1fe4de590 100644 --- a/salt/modules/munin.py +++ b/salt/modules/munin.py @@ -1,37 +1,41 @@ # -*- coding: utf-8 -*- -''' +""" Run munin plugins/checks from salt and format the output as data. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os import stat -# Import salt libs -from salt.ext import six import salt.utils.files import salt.utils.stringutils -PLUGINDIR = '/etc/munin/plugins/' +# Import salt libs +from salt.ext import six + +PLUGINDIR = "/etc/munin/plugins/" def __virtual__(): - ''' + """ Only load the module if munin-node is installed - ''' - if os.path.exists('/etc/munin/munin-node.conf'): - return 'munin' - return (False, 'The munin execution module cannot be loaded: munin-node is not installed.') + """ + if os.path.exists("/etc/munin/munin-node.conf"): + return "munin" + return ( + False, + "The munin execution module cannot be loaded: munin-node is not installed.", + ) -def _get_conf(fname='/etc/munin/munin-node.cfg'): - with salt.utils.files.fopen(fname, 'r') as fp_: +def _get_conf(fname="/etc/munin/munin-node.cfg"): + with salt.utils.files.fopen(fname, "r") as fp_: return salt.utils.stringutils.to_unicode(fp_.read()) def run(plugins): - ''' + """ Run one or more named munin plugins CLI Example: @@ -40,27 +44,27 @@ def run(plugins): salt '*' munin.run uptime salt '*' munin.run uptime,cpu,load,memory - ''' + """ all_plugins = list_plugins() if isinstance(plugins, six.string_types): - plugins = plugins.split(',') + plugins = plugins.split(",") data = {} for plugin in plugins: if plugin not in all_plugins: continue data[plugin] = {} - muninout = __salt__['cmd.run']( - 'munin-run {0}'.format(plugin), - python_shell=False) - for line in muninout.split('\n'): - if 'value' in line: # This skips multigraph lines, etc - key, val = line.split(' ') - key = key.split('.')[0] + muninout = __salt__["cmd.run"]( + "munin-run {0}".format(plugin), python_shell=False + ) + for line in muninout.split("\n"): + if "value" in line: # This skips multigraph lines, etc + key, val = line.split(" ") + key = key.split(".")[0] try: # We only want numbers - if '.' in val: + if "." in val: val = float(val) else: val = int(val) @@ -71,7 +75,7 @@ def run(plugins): def run_all(): - ''' + """ Run all the munin plugins CLI Example: @@ -79,7 +83,7 @@ def run_all(): .. code-block:: bash salt '*' munin.run_all - ''' + """ plugins = list_plugins() ret = {} for plugin in plugins: @@ -88,7 +92,7 @@ def run_all(): def list_plugins(): - ''' + """ List all the munin plugins CLI Example: @@ -96,7 +100,7 @@ def list_plugins(): .. code-block:: bash salt '*' munin.list_plugins - ''' + """ pluginlist = os.listdir(PLUGINDIR) ret = [] for plugin in pluginlist: diff --git a/salt/modules/mysql.py b/salt/modules/mysql.py index 35276f8e352..4a8361dbcd0 100644 --- a/salt/modules/mysql.py +++ b/salt/modules/mysql.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide MySQL compatibility to salt. :depends: - MySQLdb Python module @@ -31,18 +31,19 @@ Module to provide MySQL compatibility to salt. Connection arguments from the minion config file can be overridden on the CLI by using the arguments defined :mod:`here `. Additionally, it is now possible to setup a user with no password. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import hashlib -import time import logging -import re -import sys -import shlex import os +import re +import shlex +import sys +import time # Import salt libs import salt.utils.data @@ -51,8 +52,9 @@ import salt.utils.stringutils # Import third party libs from salt.ext import six + # pylint: disable=import-error -from salt.ext.six.moves import range, zip # pylint: disable=no-name-in-module,redefined-builtin +from salt.ext.six.moves import range, zip try: # Trying to import MySQLdb @@ -65,6 +67,7 @@ except ImportError: try: # MySQLdb import failed, try to import PyMySQL import pymysql + pymysql.install_as_MySQLdb() import MySQLdb import MySQLdb.cursors @@ -80,108 +83,101 @@ log = logging.getLogger(__name__) __opts__ = {} __grants__ = [ - 'ALL PRIVILEGES', - 'ALTER', - 'ALTER ROUTINE', - 'BACKUP_ADMIN', - 'BINLOG_ADMIN', - 'CONNECTION_ADMIN', - 'CREATE', - 'CREATE ROLE', - 'CREATE ROUTINE', - 'CREATE TABLESPACE', - 'CREATE TEMPORARY TABLES', - 'CREATE USER', - 'CREATE VIEW', - 'DELETE', - 'DROP', - 'DROP ROLE', - 'ENCRYPTION_KEY_ADMIN', - 'EVENT', - 'EXECUTE', - 'FILE', - 'GRANT OPTION', - 'GROUP_REPLICATION_ADMIN', - 'INDEX', - 'INSERT', - 'LOCK TABLES', - 'PERSIST_RO_VARIABLES_ADMIN', - 'PROCESS', - 'REFERENCES', - 'RELOAD', - 'REPLICATION CLIENT', - 'REPLICATION SLAVE', - 'REPLICATION_SLAVE_ADMIN', - 'RESOURCE_GROUP_ADMIN', - 'RESOURCE_GROUP_USER', - 'ROLE_ADMIN', - 'SELECT', - 'SET_USER_ID', - 'SHOW DATABASES', - 'SHOW VIEW', - 'SHUTDOWN', - 'SUPER', - 'SYSTEM_VARIABLES_ADMIN', - 'TRIGGER', - 'UPDATE', - 'USAGE', - 'XA_RECOVER_ADMIN' + "ALL PRIVILEGES", + "ALTER", + "ALTER ROUTINE", + "BACKUP_ADMIN", + "BINLOG_ADMIN", + "CONNECTION_ADMIN", + "CREATE", + "CREATE ROLE", + "CREATE ROUTINE", + "CREATE TABLESPACE", + "CREATE TEMPORARY TABLES", + "CREATE USER", + "CREATE VIEW", + "DELETE", + "DROP", + "DROP ROLE", + "ENCRYPTION_KEY_ADMIN", + "EVENT", + "EXECUTE", + "FILE", + "GRANT OPTION", + "GROUP_REPLICATION_ADMIN", + "INDEX", + "INSERT", + "LOCK TABLES", + "PERSIST_RO_VARIABLES_ADMIN", + "PROCESS", + "REFERENCES", + "RELOAD", + "REPLICATION CLIENT", + "REPLICATION SLAVE", + "REPLICATION_SLAVE_ADMIN", + "RESOURCE_GROUP_ADMIN", + "RESOURCE_GROUP_USER", + "ROLE_ADMIN", + "SELECT", + "SET_USER_ID", + "SHOW DATABASES", + "SHOW VIEW", + "SHUTDOWN", + "SUPER", + "SYSTEM_VARIABLES_ADMIN", + "TRIGGER", + "UPDATE", + "USAGE", + "XA_RECOVER_ADMIN", ] -__ssl_options_parameterized__ = [ - 'CIPHER', - 'ISSUER', - 'SUBJECT' -] -__ssl_options__ = __ssl_options_parameterized__ + [ - 'SSL', - 'X509' -] +__ssl_options_parameterized__ = ["CIPHER", "ISSUER", "SUBJECT"] +__ssl_options__ = __ssl_options_parameterized__ + ["SSL", "X509"] __all_privileges__ = [ - 'ALTER', - 'ALTER ROUTINE', - 'BACKUP_ADMIN', - 'BINLOG_ADMIN', - 'CONNECTION_ADMIN', - 'CREATE', - 'CREATE ROLE', - 'CREATE ROUTINE', - 'CREATE TABLESPACE', - 'CREATE TEMPORARY TABLES', - 'CREATE USER', - 'CREATE VIEW', - 'DELETE', - 'DROP', - 'DROP ROLE', - 'ENCRYPTION_KEY_ADMIN', - 'EVENT', - 'EXECUTE', - 'FILE', - 'GROUP_REPLICATION_ADMIN', - 'INDEX', - 'INSERT', - 'LOCK TABLES', - 'PERSIST_RO_VARIABLES_ADMIN', - 'PROCESS', - 'REFERENCES', - 'RELOAD', - 'REPLICATION CLIENT', - 'REPLICATION SLAVE', - 'REPLICATION_SLAVE_ADMIN', - 'RESOURCE_GROUP_ADMIN', - 'RESOURCE_GROUP_USER', - 'ROLE_ADMIN', - 'SELECT', - 'SET_USER_ID', - 'SHOW DATABASES', - 'SHOW VIEW', - 'SHUTDOWN', - 'SUPER', - 'SYSTEM_VARIABLES_ADMIN', - 'TRIGGER', - 'UPDATE', - 'XA_RECOVER_ADMIN' + "ALTER", + "ALTER ROUTINE", + "BACKUP_ADMIN", + "BINLOG_ADMIN", + "CONNECTION_ADMIN", + "CREATE", + "CREATE ROLE", + "CREATE ROUTINE", + "CREATE TABLESPACE", + "CREATE TEMPORARY TABLES", + "CREATE USER", + "CREATE VIEW", + "DELETE", + "DROP", + "DROP ROLE", + "ENCRYPTION_KEY_ADMIN", + "EVENT", + "EXECUTE", + "FILE", + "GROUP_REPLICATION_ADMIN", + "INDEX", + "INSERT", + "LOCK TABLES", + "PERSIST_RO_VARIABLES_ADMIN", + "PROCESS", + "REFERENCES", + "RELOAD", + "REPLICATION CLIENT", + "REPLICATION SLAVE", + "REPLICATION_SLAVE_ADMIN", + "RESOURCE_GROUP_ADMIN", + "RESOURCE_GROUP_USER", + "ROLE_ADMIN", + "SELECT", + "SET_USER_ID", + "SHOW DATABASES", + "SHOW VIEW", + "SHUTDOWN", + "SUPER", + "SYSTEM_VARIABLES_ADMIN", + "TRIGGER", + "UPDATE", + "XA_RECOVER_ADMIN", ] r''' @@ -257,15 +253,15 @@ And theses could be mixed, in a like query value with args: 'f\_o\%%o`b\'a"r' def __virtual__(): - ''' + """ Confirm that a python mysql client is installed. - ''' - return bool(MySQLdb), 'No python mysql client installed.' if MySQLdb is None else '' + """ + return bool(MySQLdb), "No python mysql client installed." if MySQLdb is None else "" def __mysql_hash_password(password): _password = hashlib.sha1(password.encode()).digest() - _password = '*{0}'.format(hashlib.sha1(_password).hexdigest().upper()) + _password = "*{0}".format(hashlib.sha1(_password).hexdigest().upper()) return _password @@ -277,7 +273,7 @@ def __check_table(name, table, **connection_args): s_name = quote_identifier(name) s_table = quote_identifier(table) # identifiers cannot be used as values - qry = 'CHECK TABLE {0}.{1}'.format(s_name, s_table) + qry = "CHECK TABLE {0}.{1}".format(s_name, s_table) _execute(cur, qry) results = cur.fetchall() log.debug(results) @@ -292,7 +288,7 @@ def __repair_table(name, table, **connection_args): s_name = quote_identifier(name) s_table = quote_identifier(table) # identifiers cannot be used as values - qry = 'REPAIR TABLE {0}.{1}'.format(s_name, s_table) + qry = "REPAIR TABLE {0}.{1}".format(s_name, s_table) _execute(cur, qry) results = cur.fetchall() log.debug(results) @@ -307,7 +303,7 @@ def __optimize_table(name, table, **connection_args): s_name = quote_identifier(name) s_table = quote_identifier(table) # identifiers cannot be used as values - qry = 'OPTIMIZE TABLE {0}.{1}'.format(s_name, s_table) + qry = "OPTIMIZE TABLE {0}.{1}".format(s_name, s_table) _execute(cur, qry) results = cur.fetchall() log.debug(results) @@ -315,28 +311,26 @@ def __optimize_table(name, table, **connection_args): def __password_column(**connection_args): - if 'mysql.password_column'in __context__: - return __context__['mysql.password_column'] + if "mysql.password_column" in __context__: + return __context__["mysql.password_column"] dbc = _connect(**connection_args) if dbc is None: - return 'Password' + return "Password" cur = dbc.cursor() - qry = ('SELECT column_name from information_schema.COLUMNS ' - 'WHERE table_schema=%(schema)s and table_name=%(table)s ' - 'and column_name=%(column)s') - args = { - 'schema': 'mysql', - 'table': 'user', - 'column': 'Password' - } + qry = ( + "SELECT column_name from information_schema.COLUMNS " + "WHERE table_schema=%(schema)s and table_name=%(table)s " + "and column_name=%(column)s" + ) + args = {"schema": "mysql", "table": "user", "column": "Password"} _execute(cur, qry, args) if int(cur.rowcount) > 0: - __context__['mysql.password_column'] = 'Password' + __context__["mysql.password_column"] = "Password" else: - __context__['mysql.password_column'] = 'authentication_string' + __context__["mysql.password_column"] = "authentication_string" - return __context__['mysql.password_column'] + return __context__["mysql.password_column"] def __get_auth_plugin(user, host, **connection_args): @@ -345,31 +339,31 @@ def __get_auth_plugin(user, host, **connection_args): return [] cur = dbc.cursor(MySQLdb.cursors.DictCursor) try: - qry = 'SELECT plugin FROM mysql.user WHERE User=%(user)s and Host=%(host)s' - args = {'user': user, 'host': host} + qry = "SELECT plugin FROM mysql.user WHERE User=%(user)s and Host=%(host)s" + args = {"user": user, "host": host} _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) - return 'mysql_native_password' + return "mysql_native_password" results = cur.fetchall() log.debug(results) if results: - return results[0].get('plugin', 'mysql_native_password') + return results[0].get("plugin", "mysql_native_password") else: - return 'mysql_native_password' + return "mysql_native_password" def _connect(**kwargs): - ''' + """ wrap authentication credentials here - ''' + """ connargs = dict() def _connarg(name, key=None, get_opts=True): - ''' + """ Add key to connargs, only if name exists in our kwargs or, if get_opts is true, as mysql. in __opts__ or __pillar__ @@ -377,62 +371,69 @@ def _connect(**kwargs): then pillar. To avoid collision with other functions, kwargs-based connection arguments are prefixed with 'connection_' (i.e. 'connection_host', 'connection_user', etc.). - ''' + """ if key is None: key = name if name in kwargs: connargs[key] = kwargs[name] elif get_opts: - prefix = 'connection_' + prefix = "connection_" if name.startswith(prefix): try: - name = name[len(prefix):] + name = name[len(prefix) :] except IndexError: return - val = __salt__['config.option']('mysql.{0}'.format(name), None) + val = __salt__["config.option"]("mysql.{0}".format(name), None) if val is not None: connargs[key] = val # If a default file is explicitly passed to kwargs, don't grab the # opts/pillar settings, as it can override info in the defaults file - if 'connection_default_file' in kwargs: + if "connection_default_file" in kwargs: get_opts = False else: get_opts = True - _connarg('connection_host', 'host', get_opts) - _connarg('connection_user', 'user', get_opts) - _connarg('connection_pass', 'passwd', get_opts) - _connarg('connection_port', 'port', get_opts) - _connarg('connection_db', 'db', get_opts) - _connarg('connection_conv', 'conv', get_opts) - _connarg('connection_unix_socket', 'unix_socket', get_opts) - _connarg('connection_default_file', 'read_default_file', get_opts) - _connarg('connection_default_group', 'read_default_group', get_opts) + _connarg("connection_host", "host", get_opts) + _connarg("connection_user", "user", get_opts) + _connarg("connection_pass", "passwd", get_opts) + _connarg("connection_port", "port", get_opts) + _connarg("connection_db", "db", get_opts) + _connarg("connection_conv", "conv", get_opts) + _connarg("connection_unix_socket", "unix_socket", get_opts) + _connarg("connection_default_file", "read_default_file", get_opts) + _connarg("connection_default_group", "read_default_group", get_opts) # MySQLdb states that this is required for charset usage # but in fact it's more than it's internally activated # when charset is used, activating use_unicode here would # retrieve utf8 strings as unicode() objects in salt # and we do not want that. - #_connarg('connection_use_unicode', 'use_unicode') - connargs['use_unicode'] = False - _connarg('connection_charset', 'charset') + # _connarg('connection_use_unicode', 'use_unicode') + connargs["use_unicode"] = False + _connarg("connection_charset", "charset") # Ensure MySQldb knows the format we use for queries with arguments - MySQLdb.paramstyle = 'pyformat' + MySQLdb.paramstyle = "pyformat" for key in copy.deepcopy(connargs): if not connargs[key]: del connargs[key] - if connargs.get('passwd', True) is None: # If present but set to None. (Extreme edge case.) - log.warning('MySQL password of None found. Attempting passwordless login.') - connargs.pop('passwd') + if ( + connargs.get("passwd", True) is None + ): # If present but set to None. (Extreme edge case.) + log.warning("MySQL password of None found. Attempting passwordless login.") + connargs.pop("passwd") try: dbc = MySQLdb.connect(**connargs) except OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err + log.error(err) + return None + except MySQLdb.err.InternalError as exc: + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return None @@ -441,7 +442,7 @@ def _connect(**kwargs): def _grant_to_tokens(grant): - ''' + """ This should correspond fairly closely to the YAML rendering of a mysql_grants state which comes out as follows: @@ -470,21 +471,21 @@ def _grant_to_tokens(grant): - host: MySQL host - grant: [grant1, grant2] (ala SELECT, USAGE, etc) - database: MySQL DB - ''' - log.debug('_grant_to_tokens entry \'%s\'', grant) + """ + log.debug("_grant_to_tokens entry '%s'", grant) dict_mode = False if isinstance(grant, dict): dict_mode = True # Everything coming in dictionary form was made for a MySQLdb execute # call and contain a '%%' escaping of '%' characters for MySQLdb # that we should remove here. - grant_sql = grant.get('qry', 'undefined').replace('%%', '%') - sql_args = grant.get('args', {}) - host = sql_args.get('host', 'undefined') - user = sql_args.get('user', 'undefined') + grant_sql = grant.get("qry", "undefined").replace("%%", "%") + sql_args = grant.get("args", {}) + host = sql_args.get("host", "undefined") + user = sql_args.get("user", "undefined") else: grant_sql = grant - user = '' + user = "" # the replace part is for presence of ` character in the db name # the shell escape is \` but mysql escape is ``. Spaces should not be # exploded as users or db names could contain spaces. @@ -505,75 +506,77 @@ def _grant_to_tokens(grant): # ['GRANT', 'USAGE', 'ON', '*', '.', '*', 'TO', '\'user ";--,?:&/\\\'', # '@', "'localhost'"] lex = shlex.shlex(grant_sql) - lex.quotes = '\'`' + lex.quotes = "'`" lex.whitespace_split = False - lex.commenters = '' - lex.wordchars += '\"' + lex.commenters = "" + lex.wordchars += '"' exploded_grant = list(lex) grant_tokens = [] multiword_statement = [] position_tracker = 1 # Skip the initial 'GRANT' word token - database = '' - phrase = 'grants' + database = "" + phrase = "grants" for token in exploded_grant[position_tracker:]: - if token == ',' and phrase == 'grants': + if token == "," and phrase == "grants": position_tracker += 1 continue - if token == 'ON' and phrase == 'grants': - phrase = 'db' + if token == "ON" and phrase == "grants": + phrase = "db" position_tracker += 1 continue - elif token == 'TO' and phrase == 'tables': - phrase = 'user' + elif token == "TO" and phrase == "tables": + phrase = "user" position_tracker += 1 continue - elif token == '@' and phrase == 'pre-host': - phrase = 'host' + elif token == "@" and phrase == "pre-host": + phrase = "host" position_tracker += 1 continue - if phrase == 'grants': + if phrase == "grants": # Read-ahead - if exploded_grant[position_tracker + 1] == ',' \ - or exploded_grant[position_tracker + 1] == 'ON': + if ( + exploded_grant[position_tracker + 1] == "," + or exploded_grant[position_tracker + 1] == "ON" + ): # End of token detected if multiword_statement: multiword_statement.append(token) - grant_tokens.append(' '.join(multiword_statement)) + grant_tokens.append(" ".join(multiword_statement)) multiword_statement = [] else: grant_tokens.append(token) else: # This is a multi-word, ala LOCK TABLES multiword_statement.append(token) - elif phrase == 'db': + elif phrase == "db": # the shlex splitter may have split on special database characters ` database += token # Read-ahead try: - if exploded_grant[position_tracker + 1] == '.': - phrase = 'tables' + if exploded_grant[position_tracker + 1] == ".": + phrase = "tables" except IndexError: break - elif phrase == 'tables': + elif phrase == "tables": database += token - elif phrase == 'user': + elif phrase == "user": if dict_mode: break else: user += token # Read-ahead - if exploded_grant[position_tracker + 1] == '@': - phrase = 'pre-host' + if exploded_grant[position_tracker + 1] == "@": + phrase = "pre-host" - elif phrase == 'host': + elif phrase == "host": host = token break @@ -584,23 +587,16 @@ def _grant_to_tokens(grant): user = user.strip("'") host = host.strip("'") log.debug( - 'grant to token \'%s\'::\'%s\'::\'%s\'::\'%s\'', - user, - host, - grant_tokens, - database + "grant to token '%s'::'%s'::'%s'::'%s'", user, host, grant_tokens, database ) except UnboundLocalError: - host = '' + host = "" - return dict(user=user, - host=host, - grant=grant_tokens, - database=database) + return dict(user=user, host=host, grant=grant_tokens, database=database) def quote_identifier(identifier, for_grants=False): - r''' + r""" Return an identifier name (column, table, database, etc) escaped for MySQL This means surrounded by "`" character and escaping this character inside. @@ -619,16 +615,19 @@ def quote_identifier(identifier, for_grants=False): .. code-block:: bash salt '*' mysql.quote_identifier 'foo`bar' - ''' + """ if for_grants: - return '`' + identifier.replace('`', '``').replace('_', r'\_') \ - .replace('%', r'%%') + '`' + return ( + "`" + + identifier.replace("`", "``").replace("_", r"\_").replace("%", r"%%") + + "`" + ) else: - return '`' + identifier.replace('`', '``').replace('%', '%%') + '`' + return "`" + identifier.replace("`", "``").replace("%", "%%") + "`" def _execute(cur, qry, args=None): - ''' + """ Internal wrapper around MySQLdb cursor.execute() function MySQLDb does not apply the same filters when arguments are used with the @@ -637,18 +636,41 @@ def _execute(cur, qry, args=None): arguments the '%%' is not managed. We cannot apply Identifier quoting in a predictable way if the query are not always applying the same filters. So this wrapper ensure this escape is not made if no arguments are used. - ''' + """ if args is None or args == {}: - qry = qry.replace('%%', '%') - log.debug('Doing query: %s', qry) + qry = qry.replace("%%", "%") + log.debug("Doing query: %s", qry) return cur.execute(qry) else: - log.debug('Doing query: %s args: %s ', qry, repr(args)) + log.debug("Doing query: %s args: %s ", qry, repr(args)) return cur.execute(qry, args) +def _sanitize_comments(content): + # Remove comments which might affect line by line parsing + # Regex should remove any text begining with # (or --) not inside of ' or " + content = re.sub( + r"""(['"](?:[^'"]+|(?<=\\)['"])*['"])|#[^\n]*""", + lambda m: m.group(1) or "", + content, + re.S, + ) + content = re.sub( + r"""(['"](?:[^'"]+|(?<=\\)['"])*['"])|--[^\n]*""", + lambda m: m.group(1) or "", + content, + re.S, + ) + cleaned = "" + for line in content.splitlines(): + line = line.strip() + if line != "": + cleaned += line + "\n" + return cleaned + + def query(database, query, **connection_args): - ''' + """ Run an arbitrary SQL query and return the results or the number of affected rows. @@ -710,7 +732,7 @@ def query(database, query, **connection_args): .. code-block:: jinja {{ salt['mysql.query']('mydb', 'SELECT info from mytable limit 1')['results'][0][0] }} - ''' + """ # Doesn't do anything about sql warnings, e.g. empty values on an insert. # I don't think it handles multiple queries at once, so adding "commit" # might not work. @@ -722,7 +744,7 @@ def query(database, query, **connection_args): conv = dict(zip(conv_iter, [str] * len(orig_conv))) # some converters are lists, do not break theses - conv_mysqldb = {'MYSQLDB': True} + conv_mysqldb = {"MYSQLDB": True} if conv_mysqldb.get(MySQLdb.__package__.upper()): conv[FIELD_TYPE.BLOB] = [ (FLAG.BINARY, str), @@ -737,29 +759,29 @@ def query(database, query, **connection_args): (FLAG.BINARY, str), ] - connection_args.update({'connection_db': database, 'connection_conv': conv}) + connection_args.update({"connection_db": database, "connection_conv": conv}) dbc = _connect(**connection_args) if dbc is None: return {} cur = dbc.cursor() start = time.time() - log.debug('Using db: %s to run query %s', database, query) + log.debug("Using db: %s to run query %s", database, query) try: affected = _execute(cur, query) except OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False results = cur.fetchall() - elapsed = (time.time() - start) + elapsed = time.time() - start if elapsed < 0.200: - elapsed_h = str(round(elapsed * 1000, 1)) + 'ms' + elapsed_h = str(round(elapsed * 1000, 1)) + "ms" else: - elapsed_h = str(round(elapsed, 2)) + 's' + elapsed_h = str(round(elapsed, 2)) + "s" ret = {} - ret['query time'] = {'human': elapsed_h, 'raw': str(round(elapsed, 5))} + ret["query time"] = {"human": elapsed_h, "raw": str(round(elapsed, 5))} select_keywords = ["SELECT", "SHOW", "DESC"] select_query = False for keyword in select_keywords: @@ -767,20 +789,20 @@ def query(database, query, **connection_args): select_query = True break if select_query: - ret['rows returned'] = affected + ret["rows returned"] = affected columns = () for column in cur.description: columns += (column[0],) - ret['columns'] = columns - ret['results'] = results + ret["columns"] = columns + ret["results"] = results return ret else: - ret['rows affected'] = affected + ret["rows affected"] = affected return ret def file_query(database, file_name, **connection_args): - ''' + """ Run an arbitrary SQL query from the specified file and return the the number of affected rows. @@ -807,26 +829,38 @@ def file_query(database, file_name, **connection_args): {'query time': {'human': '39.0ms', 'raw': '0.03899'}, 'rows affected': 1L} - ''' - if any(file_name.startswith(proto) for proto in ('salt://', 'http://', 'https://', 'swift://', 's3://')): - file_name = __salt__['cp.cache_file'](file_name) + """ + if any( + file_name.startswith(proto) + for proto in ("salt://", "http://", "https://", "swift://", "s3://") + ): + file_name = __salt__["cp.cache_file"](file_name) if os.path.exists(file_name): - with salt.utils.files.fopen(file_name, 'r') as ifile: + with salt.utils.files.fopen(file_name, "r") as ifile: contents = salt.utils.stringutils.to_unicode(ifile.read()) else: log.error('File "%s" does not exist', file_name) return False query_string = "" - ret = {'rows returned': 0, 'columns': [], 'results': [], 'rows affected': 0, 'query time': {'raw': 0}} + ret = { + "rows returned": 0, + "columns": [], + "results": [], + "rows affected": 0, + "query time": {"raw": 0}, + } + + contents = _sanitize_comments(contents) + # Walk the each line of the sql file to get accurate row affected results for line in contents.splitlines(): - if re.match(r'--', line): # ignore sql comments - continue - if not re.search(r'[^-;]+;', line): # keep appending lines that don't end in ; + if not re.search(r"[^-;]+;", line): # keep appending lines that don't end in ; query_string = query_string + line else: - query_string = query_string + line # append lines that end with ; and run query + query_string = ( + query_string + line + ) # append lines that end with ; and run query query_result = query(database, query_string, **connection_args) query_string = "" @@ -834,18 +868,21 @@ def file_query(database, file_name, **connection_args): # Fail out on error return False - if 'query time' in query_result: - ret['query time']['raw'] += float(query_result['query time']['raw']) - if 'rows returned' in query_result: - ret['rows returned'] += query_result['rows returned'] - if 'columns' in query_result: - ret['columns'].append(query_result['columns']) - if 'results' in query_result: - ret['results'].append(query_result['results']) - if 'rows affected' in query_result: - ret['rows affected'] += query_result['rows affected'] - ret['query time']['human'] = six.text_type(round(float(ret['query time']['raw']), 2)) + 's' - ret['query time']['raw'] = round(float(ret['query time']['raw']), 5) + if "query time" in query_result: + ret["query time"]["raw"] += float(query_result["query time"]["raw"]) + if "rows returned" in query_result: + ret["rows returned"] += query_result["rows returned"] + if "columns" in query_result: + ret["columns"].append(query_result["columns"]) + if "results" in query_result: + ret["results"].append(query_result["results"]) + if "rows affected" in query_result: + ret["rows affected"] += query_result["rows affected"] + + ret["query time"]["human"] = ( + six.text_type(round(float(ret["query time"]["raw"]), 2)) + "s" + ) + ret["query time"]["raw"] = round(float(ret["query time"]["raw"]), 5) # Remove empty keys in ret ret = {k: v for k, v in six.iteritems(ret) if v} @@ -854,7 +891,7 @@ def file_query(database, file_name, **connection_args): def status(**connection_args): - ''' + """ Return the status of a MySQL server using the output from the ``SHOW STATUS`` query. @@ -863,17 +900,17 @@ def status(**connection_args): .. code-block:: bash salt '*' mysql.status - ''' + """ dbc = _connect(**connection_args) if dbc is None: return {} cur = dbc.cursor() - qry = 'SHOW STATUS' + qry = "SHOW STATUS" try: _execute(cur, qry) except OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return {} @@ -885,7 +922,7 @@ def status(**connection_args): def version(**connection_args): - ''' + """ Return the version of a MySQL server using the output from the ``SELECT VERSION()`` query. @@ -894,32 +931,32 @@ def version(**connection_args): .. code-block:: bash salt '*' mysql.version - ''' - if 'mysql.version' in __context__: - return __context__['mysql.version'] + """ + if "mysql.version" in __context__: + return __context__["mysql.version"] dbc = _connect(**connection_args) if dbc is None: - return '' + return "" cur = dbc.cursor() - qry = 'SELECT VERSION()' + qry = "SELECT VERSION()" try: _execute(cur, qry) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) - return '' + return "" try: - __context__['mysql.version'] = salt.utils.data.decode(cur.fetchone()[0]) - return __context__['mysql.version'] + __context__["mysql.version"] = salt.utils.data.decode(cur.fetchone()[0]) + return __context__["mysql.version"] except IndexError: - return '' + return "" def slave_lag(**connection_args): - ''' + """ Return the number of seconds that a slave SQL server is lagging behind the master, if the host is not a slave it will return -1. If the server is configured to be a slave for replication but slave IO is not running then @@ -931,17 +968,17 @@ def slave_lag(**connection_args): .. code-block:: bash salt '*' mysql.slave_lag - ''' + """ dbc = _connect(**connection_args) if dbc is None: return -3 cur = dbc.cursor(MySQLdb.cursors.DictCursor) - qry = 'show slave status' + qry = "show slave status" try: _execute(cur, qry) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return -3 @@ -953,15 +990,15 @@ def slave_lag(**connection_args): # sure that it is a slave. return -1 else: - if results['Slave_IO_Running'] == 'Yes': - return results['Seconds_Behind_Master'] + if results["Slave_IO_Running"] == "Yes": + return results["Seconds_Behind_Master"] else: # Replication is broken if you get here. return -2 def free_slave(**connection_args): - ''' + """ Frees a slave from its master. This is a WIP, do not use. CLI Example: @@ -969,14 +1006,14 @@ def free_slave(**connection_args): .. code-block:: bash salt '*' mysql.free_slave - ''' + """ slave_db = _connect(**connection_args) if slave_db is None: - return '' + return "" slave_cur = slave_db.cursor(MySQLdb.cursors.DictCursor) - slave_cur.execute('show slave status') + slave_cur.execute("show slave status") slave_status = slave_cur.fetchone() - master = {'host': slave_status['Master_Host']} + master = {"host": slave_status["Master_Host"]} try: # Try to connect to the master and flush logs before promoting to @@ -986,28 +1023,28 @@ def free_slave(**connection_args): # function. master_db = _connect(**master) if master_db is None: - return '' + return "" master_cur = master_db.cursor() - master_cur.execute('flush logs') + master_cur.execute("flush logs") master_db.close() except MySQLdb.OperationalError: pass - slave_cur.execute('stop slave') - slave_cur.execute('reset master') - slave_cur.execute('change master to MASTER_HOST=''') - slave_cur.execute('show slave status') + slave_cur.execute("stop slave") + slave_cur.execute("reset master") + slave_cur.execute("change master to MASTER_HOST=" "") + slave_cur.execute("show slave status") results = slave_cur.fetchone() if results is None: - return 'promoted' + return "promoted" else: - return 'failed' + return "failed" # Database related actions def db_list(**connection_args): - ''' + """ Return a list of databases of a MySQL server using the output from the ``SHOW DATABASES`` query. @@ -1016,17 +1053,17 @@ def db_list(**connection_args): .. code-block:: bash salt '*' mysql.db_list - ''' + """ dbc = _connect(**connection_args) if dbc is None: return [] cur = dbc.cursor() - qry = 'SHOW DATABASES' + qry = "SHOW DATABASES" try: _execute(cur, qry) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return [] @@ -1040,7 +1077,7 @@ def db_list(**connection_args): def alter_db(name, character_set=None, collate=None, **connection_args): - ''' + """ Modify database using ``ALTER DATABASE %(dbname)s CHARACTER SET %(charset)s COLLATE %(collation)s;`` query. @@ -1049,22 +1086,23 @@ def alter_db(name, character_set=None, collate=None, **connection_args): .. code-block:: bash salt '*' mysql.alter_db testdb charset='latin1' - ''' + """ dbc = _connect(**connection_args) if dbc is None: return [] cur = dbc.cursor() existing = db_get(name, **connection_args) - qry = 'ALTER DATABASE `{0}` CHARACTER SET {1} COLLATE {2};'.format( - name.replace('%', r'\%').replace('_', r'\_'), - character_set or existing.get('character_set'), - collate or existing.get('collate')) + qry = "ALTER DATABASE `{0}` CHARACTER SET {1} COLLATE {2};".format( + name.replace("%", r"\%").replace("_", r"\_"), + character_set or existing.get("character_set"), + collate or existing.get("collate"), + ) args = {} _execute(cur, qry, args) def db_get(name, **connection_args): - ''' + """ Return a list of databases of a MySQL server using the output from the ``SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='dbname';`` query. @@ -1074,24 +1112,25 @@ def db_get(name, **connection_args): .. code-block:: bash salt '*' mysql.db_get test - ''' + """ dbc = _connect(**connection_args) if dbc is None: return [] cur = dbc.cursor() - qry = ('SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM ' - 'INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME=%(dbname)s;') + qry = ( + "SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME FROM " + "INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME=%(dbname)s;" + ) args = {"dbname": name} _execute(cur, qry, args) if cur.rowcount: rows = cur.fetchall() - return {'character_set': rows[0][0], - 'collate': rows[0][1]} + return {"character_set": rows[0][0], "collate": rows[0][1]} return {} def db_tables(name, **connection_args): - ''' + """ Shows the tables in the given MySQL database (if exists) CLI Example: @@ -1099,9 +1138,9 @@ def db_tables(name, **connection_args): .. code-block:: bash salt '*' mysql.db_tables 'database' - ''' + """ if not db_exists(name, **connection_args): - log.info('Database \'%s\' does not exist', name) + log.info("Database '%s' does not exist", name) return False dbc = _connect(**connection_args) @@ -1110,12 +1149,12 @@ def db_tables(name, **connection_args): cur = dbc.cursor() s_name = quote_identifier(name) # identifiers cannot be used as values - qry = 'SHOW TABLES IN {0}'.format(s_name) + qry = "SHOW TABLES IN {0}".format(s_name) try: _execute(cur, qry) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return [] @@ -1128,7 +1167,7 @@ def db_tables(name, **connection_args): def db_exists(name, **connection_args): - ''' + """ Checks if a database exists on the MySQL server. CLI Example: @@ -1136,7 +1175,7 @@ def db_exists(name, **connection_args): .. code-block:: bash salt '*' mysql.db_exists 'dbname' - ''' + """ dbc = _connect(**connection_args) if dbc is None: return False @@ -1149,8 +1188,8 @@ def db_exists(name, **connection_args): try: _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False cur.fetchall() @@ -1158,7 +1197,7 @@ def db_exists(name, **connection_args): def db_create(name, character_set=None, collate=None, **connection_args): - ''' + """ Adds a databases to the MySQL server. name @@ -1176,10 +1215,10 @@ def db_create(name, character_set=None, collate=None, **connection_args): salt '*' mysql.db_create 'dbname' salt '*' mysql.db_create 'dbname' 'utf8' 'utf8_general_ci' - ''' + """ # check if db exists if db_exists(name, **connection_args): - log.info('DB \'%s\' already exists', name) + log.info("DB '%s' already exists", name) return False # db doesn't exist, proceed @@ -1189,29 +1228,29 @@ def db_create(name, character_set=None, collate=None, **connection_args): cur = dbc.cursor() s_name = quote_identifier(name) # identifiers cannot be used as values - qry = 'CREATE DATABASE IF NOT EXISTS {0}'.format(s_name) + qry = "CREATE DATABASE IF NOT EXISTS {0}".format(s_name) args = {} if character_set is not None: - qry += ' CHARACTER SET %(character_set)s' - args['character_set'] = character_set + qry += " CHARACTER SET %(character_set)s" + args["character_set"] = character_set if collate is not None: - qry += ' COLLATE %(collate)s' - args['collate'] = collate - qry += ';' + qry += " COLLATE %(collate)s" + args["collate"] = collate + qry += ";" try: if _execute(cur, qry, args): - log.info('DB \'%s\' created', name) + log.info("DB '%s' created", name) return True except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False def db_remove(name, **connection_args): - ''' + """ Removes a databases from the MySQL server. CLI Example: @@ -1219,14 +1258,14 @@ def db_remove(name, **connection_args): .. code-block:: bash salt '*' mysql.db_remove 'dbname' - ''' + """ # check if db exists if not db_exists(name, **connection_args): - log.info('DB \'%s\' does not exist', name) + log.info("DB '%s' does not exist", name) return False - if name in ('mysql', 'information_scheme'): - log.info('DB \'%s\' may not be removed', name) + if name in ("mysql", "information_scheme"): + log.info("DB '%s' may not be removed", name) return False # db does exists, proceed @@ -1236,26 +1275,26 @@ def db_remove(name, **connection_args): cur = dbc.cursor() s_name = quote_identifier(name) # identifiers cannot be used as values - qry = 'DROP DATABASE {0};'.format(s_name) + qry = "DROP DATABASE {0};".format(s_name) try: _execute(cur, qry) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False if not db_exists(name, **connection_args): - log.info('Database \'%s\' has been removed', name) + log.info("Database '%s' has been removed", name) return True - log.info('Database \'%s\' has not been removed', name) + log.info("Database '%s' has not been removed", name) return False # User related actions def user_list(**connection_args): - ''' + """ Return a list of users on a MySQL server CLI Example: @@ -1263,17 +1302,17 @@ def user_list(**connection_args): .. code-block:: bash salt '*' mysql.user_list - ''' + """ dbc = _connect(**connection_args) if dbc is None: return [] cur = dbc.cursor(MySQLdb.cursors.DictCursor) try: - qry = 'SELECT User,Host FROM mysql.user' + qry = "SELECT User,Host FROM mysql.user" _execute(cur, qry) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return [] results = cur.fetchall() @@ -1281,91 +1320,99 @@ def user_list(**connection_args): return results -def _mysql_user_exists(user, - host='localhost', - password=None, - password_hash=None, - passwordless=False, - unix_socket=False, - password_column=None, - auth_plugin='mysql_native_password', - **connection_args): +def _mysql_user_exists( + user, + host="localhost", + password=None, + password_hash=None, + passwordless=False, + unix_socket=False, + password_column=None, + auth_plugin="mysql_native_password", + **connection_args +): server_version = salt.utils.data.decode(version(**connection_args)) - compare_version = '8.0.11' - qry = ('SELECT User,Host FROM mysql.user WHERE User = %(user)s AND ' - 'Host = %(host)s') + compare_version = "8.0.11" + qry = ( + "SELECT User,Host FROM mysql.user WHERE User = %(user)s AND " "Host = %(host)s" + ) args = {} - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host if salt.utils.data.is_true(passwordless): if salt.utils.data.is_true(unix_socket): - qry += ' AND plugin=%(unix_socket)s' - args['unix_socket'] = 'auth_socket' + qry += " AND plugin=%(unix_socket)s" + args["unix_socket"] = "auth_socket" else: - qry += ' AND ' + password_column + ' = \'\'' + qry += " AND " + password_column + " = ''" elif password: if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: - if auth_plugin == 'mysql_native_password': + if auth_plugin == "mysql_native_password": _password = __mysql_hash_password(six.text_type(password)) - qry += ' AND ' + password_column + ' = %(password)s' - args['password'] = six.text_type(_password) + qry += " AND " + password_column + " = %(password)s" + args["password"] = six.text_type(_password) else: - err = 'Unable to verify password.' + err = "Unable to verify password." log.error(err) - __context__['mysql.error'] = err + __context__["mysql.error"] = err else: - qry += ' AND ' + password_column + ' = PASSWORD(%(password)s)' - args['password'] = six.text_type(password) + qry += " AND " + password_column + " = PASSWORD(%(password)s)" + args["password"] = six.text_type(password) elif password_hash: - qry += ' AND ' + password_column + ' = %(password)s' - args['password'] = password_hash + qry += " AND " + password_column + " = %(password)s" + args["password"] = password_hash return qry, args -def _mariadb_user_exists(user, - host='localhost', - password=None, - password_hash=None, - passwordless=False, - unix_socket=False, - password_column=None, - auth_plugin='mysql_native_password', - **connection_args): +def _mariadb_user_exists( + user, + host="localhost", + password=None, + password_hash=None, + passwordless=False, + unix_socket=False, + password_column=None, + auth_plugin="mysql_native_password", + **connection_args +): - qry = ('SELECT User,Host FROM mysql.user WHERE User = %(user)s AND ' - 'Host = %(host)s') + qry = ( + "SELECT User,Host FROM mysql.user WHERE User = %(user)s AND " "Host = %(host)s" + ) args = {} - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host if salt.utils.data.is_true(passwordless): if salt.utils.data.is_true(unix_socket): - qry += ' AND plugin=%(unix_socket)s' - args['unix_socket'] = 'unix_socket' + qry += " AND plugin=%(unix_socket)s" + args["unix_socket"] = "unix_socket" else: - qry += ' AND ' + password_column + ' = \'\'' + qry += " AND " + password_column + " = ''" elif password: - qry += ' AND ' + password_column + ' = PASSWORD(%(password)s)' - args['password'] = six.text_type(password) + qry += " AND " + password_column + " = PASSWORD(%(password)s)" + args["password"] = six.text_type(password) elif password_hash: - qry += ' AND ' + password_column + ' = %(password)s' - args['password'] = password_hash + qry += " AND " + password_column + " = %(password)s" + args["password"] = password_hash return qry, args -def user_exists(user, - host='localhost', - password=None, - password_hash=None, - passwordless=False, - unix_socket=False, - password_column=None, - **connection_args): - ''' +def user_exists( + user, + host="localhost", + password=None, + password_hash=None, + passwordless=False, + unix_socket=False, + password_column=None, + **connection_args +): + """ Checks if a user exists on the MySQL server. A login can be checked to see if passwordless login is permitted by omitting ``password`` and ``password_hash``, and using ``passwordless=True``. @@ -1381,24 +1428,29 @@ def user_exists(user, salt '*' mysql.user_exists 'username' 'hostname' password_hash='hash' salt '*' mysql.user_exists 'username' passwordless=True salt '*' mysql.user_exists 'username' password_column='authentication_string' - ''' + """ run_verify = False server_version = salt.utils.data.decode(version(**connection_args)) if not server_version: - last_err = __context__['mysql.error'] - err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format(last_err) + last_err = __context__["mysql.error"] + err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format( + last_err + ) log.error(err) return False dbc = _connect(**connection_args) # Did we fail to connect with the user we are checking # Its password might have previously change with the same command/state - if dbc is None \ - and __context__['mysql.error'] \ - .startswith("MySQL Error 1045: Access denied for user '{0}'@".format(user)) \ - and password: + if ( + dbc is None + and __context__["mysql.error"].startswith( + "MySQL Error 1045: Access denied for user '{0}'@".format(user) + ) + and password + ): # Clear the previous error - __context__['mysql.error'] = None - connection_args['connection_pass'] = password + __context__["mysql.error"] = None + connection_args["connection_pass"] = password dbc = _connect(**connection_args) if dbc is None: return False @@ -1409,40 +1461,44 @@ def user_exists(user, auth_plugin = __get_auth_plugin(user, host, **connection_args) cur = dbc.cursor() - if 'MariaDB' in server_version: - qry, args = _mariadb_user_exists(user, - host, - password, - password_hash, - passwordless, - unix_socket, - password_column=password_column, - auth_plugin=auth_plugin, - **connection_args) + if "MariaDB" in server_version: + qry, args = _mariadb_user_exists( + user, + host, + password, + password_hash, + passwordless, + unix_socket, + password_column=password_column, + auth_plugin=auth_plugin, + **connection_args + ) else: - qry, args = _mysql_user_exists(user, - host, - password, - password_hash, - passwordless, - unix_socket, - password_column=password_column, - auth_plugin=auth_plugin, - **connection_args) + qry, args = _mysql_user_exists( + user, + host, + password, + password_hash, + passwordless, + unix_socket, + password_column=password_column, + auth_plugin=auth_plugin, + **connection_args + ) try: _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False return cur.rowcount == 1 -def user_info(user, host='localhost', **connection_args): - ''' +def user_info(user, host="localhost", **connection_args): + """ Get full info on a MySQL user CLI Example: @@ -1450,23 +1506,22 @@ def user_info(user, host='localhost', **connection_args): .. code-block:: bash salt '*' mysql.user_info root localhost - ''' + """ dbc = _connect(**connection_args) if dbc is None: return False cur = dbc.cursor(MySQLdb.cursors.DictCursor) - qry = ('SELECT * FROM mysql.user WHERE User = %(user)s AND ' - 'Host = %(host)s') + qry = "SELECT * FROM mysql.user WHERE User = %(user)s AND " "Host = %(host)s" args = {} - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host try: _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False result = cur.fetchone() @@ -1474,110 +1529,116 @@ def user_info(user, host='localhost', **connection_args): return result -def _mysql_user_create(user, - host='localhost', - password=None, - password_hash=None, - allow_passwordless=False, - unix_socket=False, - password_column=None, - auth_plugin='mysql_native_password', - **connection_args): +def _mysql_user_create( + user, + host="localhost", + password=None, + password_hash=None, + allow_passwordless=False, + unix_socket=False, + password_column=None, + auth_plugin="mysql_native_password", + **connection_args +): server_version = salt.utils.data.decode(version(**connection_args)) - compare_version = '8.0.11' + compare_version = "8.0.11" - qry = 'CREATE USER %(user)s@%(host)s' + qry = "CREATE USER %(user)s@%(host)s" args = {} - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host if password is not None: if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: - args['auth_plugin'] = auth_plugin - qry += ' IDENTIFIED WITH %(auth_plugin)s BY %(password)s' + args["auth_plugin"] = auth_plugin + qry += " IDENTIFIED WITH %(auth_plugin)s BY %(password)s" else: - qry += ' IDENTIFIED BY %(password)s' - args['password'] = six.text_type(password) + qry += " IDENTIFIED BY %(password)s" + args["password"] = six.text_type(password) elif password_hash is not None: if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: - qry += ' IDENTIFIED BY %(password)s' + qry += " IDENTIFIED BY %(password)s" else: - qry += ' IDENTIFIED BY PASSWORD %(password)s' - args['password'] = password_hash + qry += " IDENTIFIED BY PASSWORD %(password)s" + args["password"] = password_hash elif salt.utils.data.is_true(allow_passwordless): - if not plugin_status('auth_socket', **connection_args): - err = 'The auth_socket plugin is not enabled.' + if not plugin_status("auth_socket", **connection_args): + err = "The auth_socket plugin is not enabled." log.error(err) - __context__['mysql.error'] = err + __context__["mysql.error"] = err qry = False else: if salt.utils.data.is_true(unix_socket): - if host == 'localhost': - qry += ' IDENTIFIED WITH auth_socket' + if host == "localhost": + qry += " IDENTIFIED WITH auth_socket" else: - log.error( - 'Auth via unix_socket can be set only for host=localhost' - ) + log.error("Auth via unix_socket can be set only for host=localhost") else: - log.error('password or password_hash must be specified, unless ' - 'allow_passwordless=True') + log.error( + "password or password_hash must be specified, unless " + "allow_passwordless=True" + ) qry = False return qry, args -def _mariadb_user_create(user, - host='localhost', - password=None, - password_hash=None, - allow_passwordless=False, - unix_socket=False, - password_column=None, - auth_plugin='mysql_native_password', - **connection_args): +def _mariadb_user_create( + user, + host="localhost", + password=None, + password_hash=None, + allow_passwordless=False, + unix_socket=False, + password_column=None, + auth_plugin="mysql_native_password", + **connection_args +): - qry = 'CREATE USER %(user)s@%(host)s' + qry = "CREATE USER %(user)s@%(host)s" args = {} - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host if password is not None: - qry += ' IDENTIFIED BY %(password)s' - args['password'] = six.text_type(password) + qry += " IDENTIFIED BY %(password)s" + args["password"] = six.text_type(password) elif password_hash is not None: - qry += ' IDENTIFIED BY PASSWORD %(password)s' - args['password'] = password_hash + qry += " IDENTIFIED BY PASSWORD %(password)s" + args["password"] = password_hash elif salt.utils.data.is_true(allow_passwordless): - if not plugin_status('unix_socket', **connection_args): - err = 'The unix_socket plugin is not enabled.' + if not plugin_status("unix_socket", **connection_args): + err = "The unix_socket plugin is not enabled." log.error(err) - __context__['mysql.error'] = err + __context__["mysql.error"] = err qry = False else: if salt.utils.data.is_true(unix_socket): - if host == 'localhost': - qry += ' IDENTIFIED VIA unix_socket' + if host == "localhost": + qry += " IDENTIFIED VIA unix_socket" else: - log.error( - 'Auth via unix_socket can be set only for host=localhost' - ) + log.error("Auth via unix_socket can be set only for host=localhost") else: - log.error('password or password_hash must be specified, unless ' - 'allow_passwordless=True') + log.error( + "password or password_hash must be specified, unless " + "allow_passwordless=True" + ) qry = False return qry, args -def user_create(user, - host='localhost', - password=None, - password_hash=None, - allow_passwordless=False, - unix_socket=False, - password_column=None, - auth_plugin='mysql_native_password', - **connection_args): - ''' +def user_create( + user, + host="localhost", + password=None, + password_hash=None, + allow_passwordless=False, + unix_socket=False, + password_column=None, + auth_plugin="mysql_native_password", + **connection_args +): + """ Creates a MySQL user host @@ -1623,16 +1684,18 @@ def user_create(user, salt '*' mysql.user_create 'username' 'hostname' 'password' salt '*' mysql.user_create 'username' 'hostname' password_hash='hash' salt '*' mysql.user_create 'username' 'hostname' allow_passwordless=True - ''' + """ server_version = salt.utils.data.decode(version(**connection_args)) if not server_version: - last_err = __context__['mysql.error'] - err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format(last_err) + last_err = __context__["mysql.error"] + err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format( + last_err + ) log.error(err) return False if user_exists(user, host, **connection_args): - log.info('User \'%s\'@\'%s\' already exists', user, host) + log.info("User '%s'@'%s' already exists", user, host) return False dbc = _connect(**connection_args) @@ -1643,26 +1706,30 @@ def user_create(user, password_column = __password_column(**connection_args) cur = dbc.cursor() - if 'MariaDB' in server_version: - qry, args = _mariadb_user_create(user, - host, - password, - password_hash, - allow_passwordless, - unix_socket, - password_column=password_column, - auth_plugin=auth_plugin, - **connection_args) + if "MariaDB" in server_version: + qry, args = _mariadb_user_create( + user, + host, + password, + password_hash, + allow_passwordless, + unix_socket, + password_column=password_column, + auth_plugin=auth_plugin, + **connection_args + ) else: - qry, args = _mysql_user_create(user, - host, - password, - password_hash, - allow_passwordless, - unix_socket, - password_column=password_column, - auth_plugin=auth_plugin, - **connection_args) + qry, args = _mysql_user_create( + user, + host, + password, + password_hash, + allow_passwordless, + unix_socket, + password_column=password_column, + auth_plugin=auth_plugin, + **connection_args + ) if isinstance(qry, bool): return qry @@ -1670,151 +1737,188 @@ def user_create(user, try: _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False - if user_exists(user, - host, - password, - password_hash, - password_column=password_column, - **connection_args): - msg = 'User \'{0}\'@\'{1}\' has been created'.format(user, host) + if user_exists( + user, + host, + password, + password_hash, + password_column=password_column, + **connection_args + ): + msg = "User '{0}'@'{1}' has been created".format(user, host) if not any((password, password_hash)): - msg += ' with passwordless login' + msg += " with passwordless login" log.info(msg) return True - log.info('User \'%s\'@\'%s\' was not created', user, host) + log.info("User '%s'@'%s' was not created", user, host) return False -def _mysql_user_chpass(user, - host='localhost', - password=None, - password_hash=None, - allow_passwordless=False, - unix_socket=None, - password_column=None, - auth_plugin='mysql_native_password', - **connection_args): +def _mysql_user_chpass( + user, + host="localhost", + password=None, + password_hash=None, + allow_passwordless=False, + unix_socket=None, + password_column=None, + auth_plugin="mysql_native_password", + **connection_args +): server_version = salt.utils.data.decode(version(**connection_args)) - compare_version = '8.0.11' + compare_version = "8.0.11" args = {} if password is not None: if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: - password_sql = '%(password)s' + password_sql = "%(password)s" else: - password_sql = 'PASSWORD(%(password)s)' - args['password'] = password + password_sql = "PASSWORD(%(password)s)" + args["password"] = password elif password_hash is not None: - password_sql = '%(password)s' - args['password'] = password_hash + password_sql = "%(password)s" + args["password"] = password_hash elif not salt.utils.data.is_true(allow_passwordless): - log.error('password or password_hash must be specified, unless ' - 'allow_passwordless=True') + log.error( + "password or password_hash must be specified, unless " + "allow_passwordless=True" + ) return False else: - password_sql = '\'\'' + password_sql = "''" - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: qry = "ALTER USER %(user)s@%(host)s IDENTIFIED BY %(password)s;" else: - qry = ('UPDATE mysql.user SET ' + password_column + '=' + password_sql + - ' WHERE User=%(user)s AND Host = %(host)s;') - if salt.utils.data.is_true(allow_passwordless) and \ - salt.utils.data.is_true(unix_socket): - if host == 'localhost': - if not plugin_status('auth_socket', **connection_args): - err = 'The auth_socket plugin is not enabled.' + qry = ( + "UPDATE mysql.user SET " + + password_column + + "=" + + password_sql + + " WHERE User=%(user)s AND Host = %(host)s;" + ) + if salt.utils.data.is_true(allow_passwordless) and salt.utils.data.is_true( + unix_socket + ): + if host == "localhost": + if not plugin_status("auth_socket", **connection_args): + err = "The auth_socket plugin is not enabled." log.error(err) - __context__['mysql.error'] = err + __context__["mysql.error"] = err qry = False else: - args['unix_socket'] = 'auth_socket' - if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: + args["unix_socket"] = "auth_socket" + if ( + salt.utils.versions.version_cmp(server_version, compare_version) + >= 0 + ): qry = "ALTER USER %(user)s@%(host)s IDENTIFIED WITH %(unix_socket)s AS %(user)s;" else: - qry = ('UPDATE mysql.user SET ' + password_column + '=' - + password_sql + ', plugin=%(unix_socket)s' + - ' WHERE User=%(user)s AND Host = %(host)s;') + qry = ( + "UPDATE mysql.user SET " + + password_column + + "=" + + password_sql + + ", plugin=%(unix_socket)s" + + " WHERE User=%(user)s AND Host = %(host)s;" + ) else: - log.error('Auth via unix_socket can be set only for host=localhost') + log.error("Auth via unix_socket can be set only for host=localhost") return qry, args -def _mariadb_user_chpass(user, - host='localhost', - password=None, - password_hash=None, - allow_passwordless=False, - unix_socket=None, - password_column=None, - auth_plugin='mysql_native_password', - **connection_args): +def _mariadb_user_chpass( + user, + host="localhost", + password=None, + password_hash=None, + allow_passwordless=False, + unix_socket=None, + password_column=None, + auth_plugin="mysql_native_password", + **connection_args +): server_version = salt.utils.data.decode(version(**connection_args)) - compare_version = '10.4.0' + compare_version = "10.4.0" args = {} if password is not None: - password_sql = 'PASSWORD(%(password)s)' - args['password'] = password + password_sql = "PASSWORD(%(password)s)" + args["password"] = password elif password_hash is not None: - password_sql = '%(password)s' - args['password'] = password_hash + password_sql = "%(password)s" + args["password"] = password_hash elif not salt.utils.data.is_true(allow_passwordless): - log.error('password or password_hash must be specified, unless ' - 'allow_passwordless=True') + log.error( + "password or password_hash must be specified, unless " + "allow_passwordless=True" + ) return False else: - password_sql = '\'\'' + password_sql = "''" - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: qry = "ALTER USER %(user)s@%(host)s IDENTIFIED BY %(password)s;" else: - qry = ('UPDATE mysql.user SET ' + password_column + '=' + password_sql + - ' WHERE User=%(user)s AND Host = %(host)s;') - if salt.utils.data.is_true(allow_passwordless) and \ - salt.utils.data.is_true(unix_socket): - if host == 'localhost': - if not plugin_status('unix_socket', **connection_args): - err = 'The unix_socket plugin is not enabled.' + qry = ( + "UPDATE mysql.user SET " + + password_column + + "=" + + password_sql + + " WHERE User=%(user)s AND Host = %(host)s;" + ) + if salt.utils.data.is_true(allow_passwordless) and salt.utils.data.is_true( + unix_socket + ): + if host == "localhost": + if not plugin_status("unix_socket", **connection_args): + err = "The unix_socket plugin is not enabled." log.error(err) - __context__['mysql.error'] = err + __context__["mysql.error"] = err qry = False else: - args['unix_socket'] = 'unix_socket' - qry = ('UPDATE mysql.user SET ' + password_column + '=' - + password_sql + ', plugin=%(unix_socket)s' + - ' WHERE User=%(user)s AND Host = %(host)s;') + args["unix_socket"] = "unix_socket" + qry = ( + "UPDATE mysql.user SET " + + password_column + + "=" + + password_sql + + ", plugin=%(unix_socket)s" + + " WHERE User=%(user)s AND Host = %(host)s;" + ) else: - log.error('Auth via unix_socket can be set only for host=localhost') + log.error("Auth via unix_socket can be set only for host=localhost") return qry, args -def user_chpass(user, - host='localhost', - password=None, - password_hash=None, - allow_passwordless=False, - unix_socket=None, - password_column=None, - **connection_args): - ''' +def user_chpass( + user, + host="localhost", + password=None, + password_hash=None, + allow_passwordless=False, + unix_socket=None, + password_column=None, + **connection_args +): + """ Change password for a MySQL user host @@ -1851,16 +1955,18 @@ def user_chpass(user, salt '*' mysql.user_chpass frank localhost newpassword salt '*' mysql.user_chpass frank localhost password_hash='hash' salt '*' mysql.user_chpass frank localhost allow_passwordless=True - ''' + """ server_version = salt.utils.data.decode(version(**connection_args)) if not server_version: - last_err = __context__['mysql.error'] - err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format(last_err) + last_err = __context__["mysql.error"] + err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format( + last_err + ) log.error(err) return False if not user_exists(user, host, **connection_args): - log.info('User \'%s\'@\'%s\' does not exists', user, host) + log.info("User '%s'@'%s' does not exists", user, host) return False dbc = _connect(**connection_args) @@ -1875,65 +1981,69 @@ def user_chpass(user, cur = dbc.cursor() - if 'MariaDB' in server_version: - qry, args = _mariadb_user_chpass(user, - host, - password, - password_hash, - allow_passwordless, - unix_socket, - password_column=password_column, - auth_plugin=auth_plugin, - **connection_args) + if "MariaDB" in server_version: + qry, args = _mariadb_user_chpass( + user, + host, + password, + password_hash, + allow_passwordless, + unix_socket, + password_column=password_column, + auth_plugin=auth_plugin, + **connection_args + ) else: - qry, args = _mysql_user_chpass(user, - host, - password, - password_hash, - allow_passwordless, - unix_socket, - password_column=password_column, - auth_plugin=auth_plugin, - **connection_args) + qry, args = _mysql_user_chpass( + user, + host, + password, + password_hash, + allow_passwordless, + unix_socket, + password_column=password_column, + auth_plugin=auth_plugin, + **connection_args + ) try: result = _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False - compare_version = '10.4.0' if 'MariaDB' in server_version else '8.0.11' + compare_version = "10.4.0" if "MariaDB" in server_version else "8.0.11" res = False if salt.utils.versions.version_cmp(server_version, compare_version) >= 0: - _execute(cur, 'FLUSH PRIVILEGES;') + _execute(cur, "FLUSH PRIVILEGES;") res = True else: if result: - _execute(cur, 'FLUSH PRIVILEGES;') + _execute(cur, "FLUSH PRIVILEGES;") res = True if res: log.info( - 'Password for user \'%s\'@\'%s\' has been %s', - user, host, - 'changed' if any((password, password_hash)) else 'cleared' + "Password for user '%s'@'%s' has been %s", + user, + host, + "changed" if any((password, password_hash)) else "cleared", ) return True else: log.info( - 'Password for user \'%s\'@\'%s\' was not %s', - user, host, - 'changed' if any((password, password_hash)) else 'cleared' + "Password for user '%s'@'%s' was not %s", + user, + host, + "changed" if any((password, password_hash)) else "cleared", ) return False -def user_remove(user, - host='localhost', - **connection_args): - ''' +def user_remove(user, host="localhost", **connection_args): + """ Delete MySQL user CLI Example: @@ -1941,10 +2051,10 @@ def user_remove(user, .. code-block:: bash salt '*' mysql.user_remove frank localhost - ''' + """ if not user_exists(user, host, **connection_args): - err = 'User \'%s\'@\'%s\' does not exists', user, host - __context__['mysql.error'] = err + err = "User '%s'@'%s' does not exists", user, host + __context__["mysql.error"] = err log.info(err) return False @@ -1953,28 +2063,28 @@ def user_remove(user, return False cur = dbc.cursor() - qry = 'DROP USER %(user)s@%(host)s' + qry = "DROP USER %(user)s@%(host)s" args = {} - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host try: _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False if not user_exists(user, host, **connection_args): - log.info('User \'%s\'@\'%s\' has been removed', user, host) + log.info("User '%s'@'%s' has been removed", user, host) return True - log.info('User \'%s\'@\'%s\' has NOT been removed', user, host) + log.info("User '%s'@'%s' has NOT been removed", user, host) return False def tokenize_grant(grant): - ''' + """ External wrapper function :param grant: :return: dict @@ -1985,15 +2095,13 @@ def tokenize_grant(grant): salt '*' mysql.tokenize_grant \ "GRANT SELECT, INSERT ON testdb.* TO 'testuser'@'localhost'" - ''' + """ return _grant_to_tokens(grant) # Maintenance -def db_check(name, - table=None, - **connection_args): - ''' +def db_check(name, table=None, **connection_args): + """ Repairs the full database or just a given table CLI Example: @@ -2002,24 +2110,22 @@ def db_check(name, salt '*' mysql.db_check dbname salt '*' mysql.db_check dbname dbtable - ''' + """ ret = [] if table is None: # we need to check all tables tables = db_tables(name, **connection_args) for table in tables: - log.info('Checking table \'%s\' in db \'%s\'..', name, table) + log.info("Checking table '%s' in db '%s'..", name, table) ret.append(__check_table(name, table, **connection_args)) else: - log.info('Checking table \'%s\' in db \'%s\'..', name, table) + log.info("Checking table '%s' in db '%s'..", name, table) ret = __check_table(name, table, **connection_args) return ret -def db_repair(name, - table=None, - **connection_args): - ''' +def db_repair(name, table=None, **connection_args): + """ Repairs the full database or just a given table CLI Example: @@ -2027,24 +2133,22 @@ def db_repair(name, .. code-block:: bash salt '*' mysql.db_repair dbname - ''' + """ ret = [] if table is None: # we need to repair all tables tables = db_tables(name, **connection_args) for table in tables: - log.info('Repairing table \'%s\' in db \'%s\'..', name, table) + log.info("Repairing table '%s' in db '%s'..", name, table) ret.append(__repair_table(name, table, **connection_args)) else: - log.info('Repairing table \'%s\' in db \'%s\'..', name, table) + log.info("Repairing table '%s' in db '%s'..", name, table) ret = __repair_table(name, table, **connection_args) return ret -def db_optimize(name, - table=None, - **connection_args): - ''' +def db_optimize(name, table=None, **connection_args): + """ Optimizes the full database or just a given table CLI Example: @@ -2052,16 +2156,16 @@ def db_optimize(name, .. code-block:: bash salt '*' mysql.db_optimize dbname - ''' + """ ret = [] if table is None: # we need to optimize all tables tables = db_tables(name, **connection_args) for table in tables: - log.info('Optimizing table \'%s\' in db \'%s\'..', name, table) + log.info("Optimizing table '%s' in db '%s'..", name, table) ret.append(__optimize_table(name, table, **connection_args)) else: - log.info('Optimizing table \'%s\' in db \'%s\'..', name, table) + log.info("Optimizing table '%s' in db '%s'..", name, table) ret = __optimize_table(name, table, **connection_args) return ret @@ -2070,16 +2174,14 @@ def db_optimize(name, def __grant_normalize(grant): # MySQL normalizes ALL to ALL PRIVILEGES, we do the same so that # grant_exists and grant_add ALL work correctly - if grant == 'ALL': - grant = 'ALL PRIVILEGES' + if grant == "ALL": + grant = "ALL PRIVILEGES" # Grants are paste directly in SQL, must filter it exploded_grants = grant.split(",") for chkgrant in exploded_grants: if chkgrant.strip().upper() not in __grants__: - raise Exception('Invalid grant : \'{0}\''.format( - chkgrant - )) + raise Exception("Invalid grant : '{0}'".format(chkgrant)) return grant @@ -2094,67 +2196,68 @@ def __ssl_option_sanitize(ssl_option): normal_key = key.strip().upper() if normal_key not in __ssl_options__: - raise Exception('Invalid SSL option : \'{0}\''.format( - key - )) + raise Exception("Invalid SSL option : '{0}'".format(key)) if normal_key in __ssl_options_parameterized__: # SSL option parameters (cipher, issuer, subject) are pasted directly to SQL so # we need to sanitize for single quotes... - new_ssl_option.append("{0} '{1}'".format(normal_key, opt[key].replace("'", ''))) + new_ssl_option.append( + "{0} '{1}'".format(normal_key, opt[key].replace("'", "")) + ) # omit if falsey elif opt[key]: new_ssl_option.append(normal_key) - return ' REQUIRE ' + ' AND '.join(new_ssl_option) + return " REQUIRE " + " AND ".join(new_ssl_option) -def __grant_generate(grant, - database, - user, - host='localhost', - grant_option=False, - escape=True, - ssl_option=False): - ''' +def __grant_generate( + grant, + database, + user, + host="localhost", + grant_option=False, + escape=True, + ssl_option=False, +): + """ Validate grants and build the query that could set the given grants Note that this query contains arguments for user and host but not for grants or database. - ''' + """ # TODO: Re-order the grant so it is according to the # SHOW GRANTS for xxx@yyy query (SELECT comes first, etc) - grant = re.sub(r'\s*,\s*', ', ', grant).upper() + grant = re.sub(r"\s*,\s*", ", ", grant).upper() grant = __grant_normalize(grant) - db_part = database.rpartition('.') + db_part = database.rpartition(".") dbc = db_part[0] table = db_part[2] if escape: - if dbc != '*': + if dbc != "*": # _ and % are authorized on GRANT queries and should get escaped # on the db name, but only if not requesting a table level grant - dbc = quote_identifier(dbc, for_grants=(table == '*')) - if table != '*': + dbc = quote_identifier(dbc, for_grants=(table == "*")) + if table != "*": table = quote_identifier(table) # identifiers cannot be used as values, and same thing for grants - qry = 'GRANT {0} ON {1}.{2} TO %(user)s@%(host)s'.format(grant, dbc, table) + qry = "GRANT {0} ON {1}.{2} TO %(user)s@%(host)s".format(grant, dbc, table) args = {} - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host if isinstance(ssl_option, list) and ssl_option: qry += __ssl_option_sanitize(ssl_option) if salt.utils.data.is_true(grant_option): - qry += ' WITH GRANT OPTION' - log.debug('Grant Query generated: %s args %s', qry, repr(args)) - return {'qry': qry, 'args': args} + qry += " WITH GRANT OPTION" + log.debug("Grant Query generated: %s args %s", qry, repr(args)) + return {"qry": qry, "args": args} -def user_grants(user, - host='localhost', **connection_args): - ''' +def user_grants(user, host="localhost", **connection_args): + """ Shows the grants for the given MySQL user (if it exists) CLI Example: @@ -2162,46 +2265,48 @@ def user_grants(user, .. code-block:: bash salt '*' mysql.user_grants 'frank' 'localhost' - ''' + """ if not user_exists(user, host, **connection_args): - log.info('User \'%s\'@\'%s\' does not exist', user, host) + log.info("User '%s'@'%s' does not exist", user, host) return False dbc = _connect(**connection_args) if dbc is None: return False cur = dbc.cursor() - qry = 'SHOW GRANTS FOR %(user)s@%(host)s' + qry = "SHOW GRANTS FOR %(user)s@%(host)s" args = {} - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host try: _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False ret = [] results = salt.utils.data.decode(cur.fetchall()) for grant in results: - tmp = grant[0].split(' IDENTIFIED BY')[0] - if 'WITH GRANT OPTION' in grant[0] and 'WITH GRANT OPTION' not in tmp: - tmp = '{0} WITH GRANT OPTION'.format(tmp) + tmp = grant[0].split(" IDENTIFIED BY")[0] + if "WITH GRANT OPTION" in grant[0] and "WITH GRANT OPTION" not in tmp: + tmp = "{0} WITH GRANT OPTION".format(tmp) ret.append(tmp) log.debug(ret) return ret -def grant_exists(grant, - database, - user, - host='localhost', - grant_option=False, - escape=True, - **connection_args): - ''' +def grant_exists( + grant, + database, + user, + host="localhost", + grant_option=False, + escape=True, + **connection_args +): + """ Checks to see if a grant exists in the database CLI Example: @@ -2210,47 +2315,53 @@ def grant_exists(grant, salt '*' mysql.grant_exists \ 'SELECT,INSERT,UPDATE,...' 'database.*' 'frank' 'localhost' - ''' + """ server_version = salt.utils.data.decode(version(**connection_args)) if not server_version: - last_err = __context__['mysql.error'] - err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format(last_err) + last_err = __context__["mysql.error"] + err = 'MySQL Error: Unable to fetch current server version. Last error was: "{}"'.format( + last_err + ) log.error(err) return False - if 'ALL' in grant: - if salt.utils.versions.version_cmp(server_version, '8.0') >= 0 and \ - 'MariaDB' not in server_version: - grant = ','.join([i for i in __all_privileges__]) + if "ALL" in grant: + if ( + salt.utils.versions.version_cmp(server_version, "8.0") >= 0 + and "MariaDB" not in server_version + ): + grant = ",".join([i for i in __all_privileges__]) else: - grant = 'ALL PRIVILEGES' + grant = "ALL PRIVILEGES" try: - target = __grant_generate( - grant, database, user, host, grant_option, escape - ) + target = __grant_generate(grant, database, user, host, grant_option, escape) except Exception: # pylint: disable=broad-except - log.error('Error during grant generation.') + log.error("Error during grant generation.") return False grants = user_grants(user, host, **connection_args) if grants is False: - log.error('Grant does not exist or may not be ordered properly. In some cases, ' - 'this could also indicate a connection error. Check your configuration.') + log.error( + "Grant does not exist or may not be ordered properly. In some cases, " + "this could also indicate a connection error. Check your configuration." + ) return False # Combine grants that match the same database _grants = {} for grant in grants: grant_token = _grant_to_tokens(grant) - if grant_token['database'] not in _grants: - _grants[grant_token['database']] = {'user': grant_token['user'], - 'database': grant_token['database'], - 'host': grant_token['host'], - 'grant': grant_token['grant']} + if grant_token["database"] not in _grants: + _grants[grant_token["database"]] = { + "user": grant_token["user"], + "database": grant_token["database"], + "host": grant_token["host"], + "grant": grant_token["grant"], + } else: - _grants[grant_token['database']]['grant'].extend(grant_token['grant']) + _grants[grant_token["database"]]["grant"].extend(grant_token["grant"]) target_tokens = _grant_to_tokens(target) for database, grant_tokens in _grants.items(): @@ -2258,41 +2369,57 @@ def grant_exists(grant, _grant_tokens = {} _target_tokens = {} - _grant_matches = [True if i in grant_tokens['grant'] - else False for i in target_tokens['grant']] + _grant_matches = [ + True if i in grant_tokens["grant"] else False + for i in target_tokens["grant"] + ] - for item in ['user', 'database', 'host']: - _grant_tokens[item] = grant_tokens[item].replace('"', '').replace('\\', '').replace('`', '') - _target_tokens[item] = target_tokens[item].replace('"', '').replace('\\', '').replace('`', '') + for item in ["user", "database", "host"]: + _grant_tokens[item] = ( + grant_tokens[item] + .replace('"', "") + .replace("\\", "") + .replace("`", "") + ) + _target_tokens[item] = ( + target_tokens[item] + .replace('"', "") + .replace("\\", "") + .replace("`", "") + ) - if _grant_tokens['user'] == _target_tokens['user'] and \ - _grant_tokens['database'] == _target_tokens['database'] and \ - _grant_tokens['host'] == _target_tokens['host'] and \ - all(_grant_matches): + if ( + _grant_tokens["user"] == _target_tokens["user"] + and _grant_tokens["database"] == _target_tokens["database"] + and _grant_tokens["host"] == _target_tokens["host"] + and all(_grant_matches) + ): return True else: - log.debug('grants mismatch \'%s\'<>\'%s\'', grant_tokens, target_tokens) + log.debug("grants mismatch '%s'<>'%s'", grant_tokens, target_tokens) except Exception as exc: # pylint: disable=broad-except # Fallback to strict parsing log.exception(exc) if grants is not False and target in grants: - log.debug('Grant exists.') + log.debug("Grant exists.") return True - log.debug('Grant does not exist, or is perhaps not ordered properly?') + log.debug("Grant does not exist, or is perhaps not ordered properly?") return False -def grant_add(grant, - database, - user, - host='localhost', - grant_option=False, - escape=True, - ssl_option=False, - **connection_args): - ''' +def grant_add( + grant, + database, + user, + host="localhost", + grant_option=False, + escape=True, + ssl_option=False, + **connection_args +): + """ Adds a grant to the MySQL server. For database, make sure you specify database.table or database.* @@ -2303,7 +2430,7 @@ def grant_add(grant, salt '*' mysql.grant_add \ 'SELECT,INSERT,UPDATE,...' 'database.*' 'frank' 'localhost' - ''' + """ dbc = _connect(**connection_args) if dbc is None: return False @@ -2312,41 +2439,43 @@ def grant_add(grant, # Avoid spaces problems grant = grant.strip() try: - qry = __grant_generate(grant, database, user, host, grant_option, escape, ssl_option) + qry = __grant_generate( + grant, database, user, host, grant_option, escape, ssl_option + ) except Exception: # pylint: disable=broad-except - log.error('Error during grant generation') + log.error("Error during grant generation") return False try: - _execute(cur, qry['qry'], qry['args']) + _execute(cur, qry["qry"], qry["args"]) except (MySQLdb.OperationalError, MySQLdb.ProgrammingError) as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False if grant_exists( - grant, database, user, host, grant_option, escape, - **connection_args): + grant, database, user, host, grant_option, escape, **connection_args + ): log.info( - 'Grant \'%s\' on \'%s\' for user \'%s\' has been added', - grant, database, user + "Grant '%s' on '%s' for user '%s' has been added", grant, database, user ) return True log.info( - 'Grant \'%s\' on \'%s\' for user \'%s\' has NOT been added', - grant, database, user + "Grant '%s' on '%s' for user '%s' has NOT been added", grant, database, user ) return False -def grant_revoke(grant, - database, - user, - host='localhost', - grant_option=False, - escape=True, - **connection_args): - ''' +def grant_revoke( + grant, + database, + user, + host="localhost", + grant_option=False, + escape=True, + **connection_args +): + """ Removes a grant from the MySQL server. CLI Example: @@ -2355,7 +2484,7 @@ def grant_revoke(grant, salt '*' mysql.grant_revoke \ 'SELECT,INSERT,UPDATE' 'database.*' 'frank' 'localhost' - ''' + """ dbc = _connect(**connection_args) if dbc is None: return False @@ -2364,60 +2493,60 @@ def grant_revoke(grant, grant = __grant_normalize(grant) if salt.utils.data.is_true(grant_option): - grant += ', GRANT OPTION' + grant += ", GRANT OPTION" - db_part = database.rpartition('.') + db_part = database.rpartition(".") dbc = db_part[0] table = db_part[2] - if dbc != '*': + if dbc != "*": # _ and % are authorized on GRANT queries and should get escaped # on the db name, but only if not requesting a table level grant - s_database = quote_identifier(dbc, for_grants=(table == '*')) - if dbc == '*': + s_database = quote_identifier(dbc, for_grants=(table == "*")) + if dbc == "*": # add revoke for *.* # before the modification query send to mysql will looks like # REVOKE SELECT ON `*`.* FROM %(user)s@%(host)s s_database = dbc - if table != '*': + if table != "*": table = quote_identifier(table) # identifiers cannot be used as values, same thing for grants - qry = 'REVOKE {0} ON {1}.{2} FROM %(user)s@%(host)s;'.format( - grant, - s_database, - table + qry = "REVOKE {0} ON {1}.{2} FROM %(user)s@%(host)s;".format( + grant, s_database, table ) args = {} - args['user'] = user - args['host'] = host + args["user"] = user + args["host"] = host try: _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False - if not grant_exists(grant, - database, - user, - host, - grant_option, - escape, - **connection_args): + if not grant_exists( + grant, database, user, host, grant_option, escape, **connection_args + ): log.info( - 'Grant \'%s\' on \'%s\' for user \'%s\' has been ' - 'revoked', grant, database, user) + "Grant '%s' on '%s' for user '%s' has been " "revoked", + grant, + database, + user, + ) return True log.info( - 'Grant \'%s\' on \'%s\' for user \'%s\' has NOT been ' - 'revoked', grant, database, user) + "Grant '%s' on '%s' for user '%s' has NOT been " "revoked", + grant, + database, + user, + ) return False def processlist(**connection_args): - ''' + """ Retrieves the processlist from the MySQL server via "SHOW FULL PROCESSLIST". @@ -2443,14 +2572,14 @@ def processlist(**connection_args): salt '*' mysql.processlist - ''' + """ ret = [] dbc = _connect(**connection_args) if dbc is None: return [] cur = dbc.cursor() - _execute(cur, 'SHOW FULL PROCESSLIST') + _execute(cur, "SHOW FULL PROCESSLIST") hdr = [c[0] for c in cur.description] for _ in range(cur.rowcount): row = cur.fetchone() @@ -2463,32 +2592,32 @@ def processlist(**connection_args): def __do_query_into_hash(conn, sql_str): - ''' + """ Perform the query that is passed to it (sql_str). Returns: results in a dict. - ''' + """ mod = sys._getframe().f_code.co_name - log.debug('%s<--(%s)', mod, sql_str) + log.debug("%s<--(%s)", mod, sql_str) rtn_results = [] try: cursor = conn.cursor() except MySQLdb.MySQLError: - log.error('%s: Can\'t get cursor for SQL->%s', mod, sql_str) + log.error("%s: Can't get cursor for SQL->%s", mod, sql_str) cursor.close() - log.debug('%s-->', mod) + log.debug("%s-->", mod) return rtn_results try: _execute(cursor, sql_str) except MySQLdb.MySQLError: - log.error('%s: try to execute : SQL->%s', mod, sql_str) + log.error("%s: try to execute : SQL->%s", mod, sql_str) cursor.close() - log.debug('%s-->', mod) + log.debug("%s-->", mod) return rtn_results qrs = cursor.fetchall() @@ -2504,12 +2633,12 @@ def __do_query_into_hash(conn, sql_str): rtn_results.append(row) cursor.close() - log.debug('%s-->', mod) + log.debug("%s-->", mod) return rtn_results def get_master_status(**connection_args): - ''' + """ Retrieves the master status from the minion. Returns:: @@ -2525,9 +2654,9 @@ def get_master_status(**connection_args): salt '*' mysql.get_master_status - ''' + """ mod = sys._getframe().f_code.co_name - log.debug('%s<--', mod) + log.debug("%s<--", mod) conn = _connect(**connection_args) if conn is None: return [] @@ -2538,12 +2667,12 @@ def get_master_status(**connection_args): if len(rtnv) == 0: rtnv.append([]) - log.debug('%s-->%s', mod, len(rtnv[0])) + log.debug("%s-->%s", mod, len(rtnv[0])) return rtnv[0] def get_slave_status(**connection_args): - ''' + """ Retrieves the slave status from the minion. Returns:: @@ -2595,9 +2724,9 @@ def get_slave_status(**connection_args): salt '*' mysql.get_slave_status - ''' + """ mod = sys._getframe().f_code.co_name - log.debug('%s<--', mod) + log.debug("%s<--", mod) conn = _connect(**connection_args) if conn is None: return [] @@ -2608,12 +2737,12 @@ def get_slave_status(**connection_args): if len(rtnv) == 0: rtnv.append([]) - log.debug('%s-->%s', mod, len(rtnv[0])) + log.debug("%s-->%s", mod, len(rtnv[0])) return rtnv[0] def showvariables(**connection_args): - ''' + """ Retrieves the show variables from the minion. Returns:: @@ -2625,9 +2754,9 @@ def showvariables(**connection_args): salt '*' mysql.showvariables - ''' + """ mod = sys._getframe().f_code.co_name - log.debug('%s<--', mod) + log.debug("%s<--", mod) conn = _connect(**connection_args) if conn is None: return [] @@ -2636,12 +2765,12 @@ def showvariables(**connection_args): if len(rtnv) == 0: rtnv.append([]) - log.debug('%s-->%s', mod, len(rtnv[0])) + log.debug("%s-->%s", mod, len(rtnv[0])) return rtnv def showglobal(**connection_args): - ''' + """ Retrieves the show global variables from the minion. Returns:: @@ -2653,9 +2782,9 @@ def showglobal(**connection_args): salt '*' mysql.showglobal - ''' + """ mod = sys._getframe().f_code.co_name - log.debug('%s<--', mod) + log.debug("%s<--", mod) conn = _connect(**connection_args) if conn is None: return [] @@ -2664,12 +2793,12 @@ def showglobal(**connection_args): if len(rtnv) == 0: rtnv.append([]) - log.debug('%s-->%s', mod, len(rtnv[0])) + log.debug("%s-->%s", mod, len(rtnv[0])) return rtnv def verify_login(user, password=None, **connection_args): - ''' + """ Attempt to login using the provided credentials. If successful, return true. Otherwise, return False. @@ -2678,23 +2807,23 @@ def verify_login(user, password=None, **connection_args): .. code-block:: bash salt '*' mysql.verify_login root password - ''' + """ # Override the connection args for username and password - connection_args['connection_user'] = user - connection_args['connection_pass'] = password + connection_args["connection_user"] = user + connection_args["connection_pass"] = password dbc = _connect(**connection_args) if dbc is None: # Clear the mysql.error if unable to connect # if the connection fails, we simply return False - if 'mysql.error' in __context__: - del __context__['mysql.error'] + if "mysql.error" in __context__: + del __context__["mysql.error"] return False return True def plugins_list(**connection_args): - ''' + """ Return a list of plugins and their status from the ``SHOW PLUGINS`` query. @@ -2703,31 +2832,31 @@ def plugins_list(**connection_args): .. code-block:: bash salt '*' mysql.plugins_list - ''' + """ dbc = _connect(**connection_args) if dbc is None: return [] cur = dbc.cursor() - qry = 'SHOW PLUGINS' + qry = "SHOW PLUGINS" try: _execute(cur, qry) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return [] ret = [] results = cur.fetchall() for dbs in results: - ret.append({'name': dbs[0], 'status': dbs[1]}) + ret.append({"name": dbs[0], "status": dbs[1]}) log.debug(ret) return ret def plugin_add(name, soname=None, **connection_args): - ''' + """ Add a plugina. CLI Example: @@ -2735,20 +2864,20 @@ def plugin_add(name, soname=None, **connection_args): .. code-block:: bash salt '*' mysql.plugin_add auth_socket - ''' + """ if not name: - log.error('Plugin name is required.') + log.error("Plugin name is required.") return False if plugin_status(name, **connection_args): - log.error('Plugin %s is already installed.', name) + log.error("Plugin %s is already installed.", name) return True dbc = _connect(**connection_args) if dbc is None: return False cur = dbc.cursor() - qry = 'INSTALL PLUGIN {0}'.format(name) + qry = "INSTALL PLUGIN {0}".format(name) if soname: qry += ' SONAME "{0}"'.format(soname) @@ -2758,8 +2887,8 @@ def plugin_add(name, soname=None, **connection_args): try: _execute(cur, qry) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False @@ -2767,7 +2896,7 @@ def plugin_add(name, soname=None, **connection_args): def plugin_remove(name, **connection_args): - ''' + """ Remove a plugin. CLI Example: @@ -2775,28 +2904,28 @@ def plugin_remove(name, **connection_args): .. code-block:: bash salt '*' mysql.plugin_remove auth_socket - ''' + """ if not name: - log.error('Plugin name is required.') + log.error("Plugin name is required.") return False if not plugin_status(name, **connection_args): - log.error('Plugin %s is not installed.', name) + log.error("Plugin %s is not installed.", name) return True dbc = _connect(**connection_args) if dbc is None: return False cur = dbc.cursor() - qry = 'UNINSTALL PLUGIN {0}'.format(name) + qry = "UNINSTALL PLUGIN {0}".format(name) args = {} - args['name'] = name + args["name"] = name try: _execute(cur, qry) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) return False @@ -2804,7 +2933,7 @@ def plugin_remove(name, **connection_args): def plugin_status(name, **connection_args): - ''' + """ Return the status of a plugin. CLI Example: @@ -2812,32 +2941,32 @@ def plugin_status(name, **connection_args): .. code-block:: bash salt '*' mysql.plugin_status auth_socket - ''' + """ if not name: - log.error('Plugin name is required.') + log.error("Plugin name is required.") return False dbc = _connect(**connection_args) if dbc is None: - return '' + return "" cur = dbc.cursor() - qry = 'SELECT PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = %(name)s' + qry = "SELECT PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = %(name)s" args = {} - args['name'] = name + args["name"] = name try: _execute(cur, qry, args) except MySQLdb.OperationalError as exc: - err = 'MySQL Error {0}: {1}'.format(*exc.args) - __context__['mysql.error'] = err + err = "MySQL Error {0}: {1}".format(*exc.args) + __context__["mysql.error"] = err log.error(err) - return '' + return "" try: status = cur.fetchone() if status is None: - return '' + return "" else: return status[0] except IndexError: - return '' + return "" diff --git a/salt/modules/nacl.py b/salt/modules/nacl.py index bb16deb6471..827e7e763bf 100644 --- a/salt/modules/nacl.py +++ b/salt/modules/nacl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This module helps include encrypted passwords in pillars, grains and salt state files. :depends: libnacl, https://github.com/saltstack/libnacl @@ -148,7 +148,7 @@ Optional small program to encrypt data without needing salt modules. echo 'apassword' | nacl_enc.py -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -156,7 +156,7 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.nacl -__virtualname__ = 'nacl' +__virtualname__ = "nacl" def __virtual__(): @@ -164,7 +164,7 @@ def __virtual__(): def keygen(sk_file=None, pk_file=None, **kwargs): - ''' + """ Use libnacl to generate a keypair. If no `sk_file` is defined return a keypair. @@ -182,23 +182,23 @@ def keygen(sk_file=None, pk_file=None, **kwargs): salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl salt-call nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub salt-call --local nacl.keygen - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.keygen(sk_file, pk_file, **kwargs) def enc(data, **kwargs): - ''' + """ Alias to `{box_type}_encrypt` box_type: secretbox, sealedbox(default) - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.enc(data, **kwargs) def enc_file(name, out=None, **kwargs): - ''' + """ This is a helper function to encrypt a file and return its contents. You can provide an optional output file using `out` @@ -213,23 +213,23 @@ def enc_file(name, out=None, **kwargs): salt-call nacl.enc_file name=salt://crt/mycert out=/tmp/cert salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \ sk_file=/etc/salt/pki/master/nacl.pub - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.enc_file(name, out, **kwargs) def dec(data, **kwargs): - ''' + """ Alias to `{box_type}_decrypt` box_type: secretbox, sealedbox(default) - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.dec(data, **kwargs) def dec_file(name, out=None, **kwargs): - ''' + """ This is a helper function to decrypt a file and return its contents. You can provide an optional output file using `out` @@ -244,13 +244,13 @@ def dec_file(name, out=None, **kwargs): salt-call nacl.dec_file name=salt://crt/mycert.nacl out=/tmp/id_rsa salt-run nacl.dec_file name=/tmp/id_rsa.nacl box_type=secretbox \ sk_file=/etc/salt/pki/master/nacl.pub - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.dec_file(name, out, **kwargs) def sealedbox_encrypt(data, **kwargs): - ''' + """ Encrypt data using a public key generated from `nacl.keygen`. The encryptd data can be decrypted using `nacl.sealedbox_decrypt` only with the secret key. @@ -261,13 +261,13 @@ def sealedbox_encrypt(data, **kwargs): salt-run nacl.sealedbox_encrypt datatoenc salt-call --local nacl.sealedbox_encrypt datatoenc pk_file=/etc/salt/pki/master/nacl.pub salt-call --local nacl.sealedbox_encrypt datatoenc pk='vrwQF7cNiNAVQVAiS3bvcbJUnF0cN6fU9YTZD9mBfzQ=' - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.sealedbox_encrypt(data, **kwargs) def sealedbox_decrypt(data, **kwargs): - ''' + """ Decrypt data using a secret key that was encrypted using a public key with `nacl.sealedbox_encrypt`. CLI Examples: @@ -277,13 +277,13 @@ def sealedbox_decrypt(data, **kwargs): salt-call nacl.sealedbox_decrypt pEXHQM6cuaF7A= salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo=' - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.sealedbox_decrypt(data, **kwargs) def secretbox_encrypt(data, **kwargs): - ''' + """ Encrypt data using a secret key generated from `nacl.keygen`. The same secret key can be used to decrypt the data using `nacl.secretbox_decrypt`. @@ -294,13 +294,13 @@ def secretbox_encrypt(data, **kwargs): salt-run nacl.secretbox_encrypt datatoenc salt-call --local nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl salt-call --local nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo=' - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.secretbox_encrypt(data, **kwargs) def secretbox_decrypt(data, **kwargs): - ''' + """ Decrypt data that was encrypted using `nacl.secretbox_encrypt` using the secret key that was generated from `nacl.keygen`. @@ -311,6 +311,6 @@ def secretbox_decrypt(data, **kwargs): salt-call nacl.secretbox_decrypt pEXHQM6cuaF7A= salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo=' - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.secretbox_decrypt(data, **kwargs) diff --git a/salt/modules/nagios.py b/salt/modules/nagios.py index b2cd5377270..dc1d5b0c9c2 100644 --- a/salt/modules/nagios.py +++ b/salt/modules/nagios.py @@ -1,48 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" Run nagios plugins/checks from salt and get the return as data. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import stat -import logging # Import 3rd-party libs from salt.ext import six log = logging.getLogger(__name__) -PLUGINDIR = '/usr/lib/nagios/plugins/' +PLUGINDIR = "/usr/lib/nagios/plugins/" def __virtual__(): - ''' + """ Only load if nagios-plugins are installed - ''' + """ if os.path.isdir(PLUGINDIR): - return 'nagios' - return (False, 'The nagios execution module cannot be loaded: nagios-plugins are not installed.') + return "nagios" + return ( + False, + "The nagios execution module cannot be loaded: nagios-plugins are not installed.", + ) -def _execute_cmd(plugin, args='', run_type='cmd.retcode'): - ''' +def _execute_cmd(plugin, args="", run_type="cmd.retcode"): + """ Execute nagios plugin if it's in the directory with salt command specified in run_type - ''' + """ data = {} all_plugins = list_plugins() if plugin in all_plugins: data = __salt__[run_type]( - '{0}{1} {2}'.format(PLUGINDIR, plugin, args), - python_shell=False) + "{0}{1} {2}".format(PLUGINDIR, plugin, args), python_shell=False + ) return data def _execute_pillar(pillar_name, run_type): - ''' + """ Run one or more nagios plugins from pillar data and get the result of run_type The pillar have to be in this format: ------ @@ -55,8 +59,8 @@ def _execute_pillar(pillar_name, run_type): APT: - check_apt ------- - ''' - groups = __salt__['pillar.get'](pillar_name) + """ + groups = __salt__["pillar.get"](pillar_name) data = {} for group in groups: @@ -70,7 +74,7 @@ def _execute_pillar(pillar_name, run_type): args = command[plugin] else: plugin = command - args = '' + args = "" command_key = _format_dict_key(args, plugin) data[group][command_key] = run_type(plugin, args) return data @@ -78,16 +82,16 @@ def _execute_pillar(pillar_name, run_type): def _format_dict_key(args, plugin): key_name = plugin - args_key = args.replace(' ', '') - if args != '': - args_key = '_' + args_key + args_key = args.replace(" ", "") + if args != "": + args_key = "_" + args_key key_name = plugin + args_key return key_name -def run(plugin, args=''): - ''' +def run(plugin, args=""): + """ Run nagios plugin and return all the data execution with cmd.run CLI Example: @@ -96,16 +100,16 @@ def run(plugin, args=''): salt '*' nagios.run check_apt salt '*' nagios.run check_icmp '8.8.8.8' - ''' - data = _execute_cmd(plugin, args, 'cmd.run') + """ + data = _execute_cmd(plugin, args, "cmd.run") return data -def retcode(plugin, args='', key_name=None): - ''' +def retcode(plugin, args="", key_name=None): + """ Run one nagios plugin and return retcode of the execution - ''' + """ data = {} # Remove all the spaces, the key must not have any space @@ -114,22 +118,22 @@ def retcode(plugin, args='', key_name=None): data[key_name] = {} - status = _execute_cmd(plugin, args, 'cmd.retcode') - data[key_name]['status'] = status + status = _execute_cmd(plugin, args, "cmd.retcode") + data[key_name]["status"] = status return data -def run_all(plugin, args=''): - ''' +def run_all(plugin, args=""): + """ Run nagios plugin and return all the data execution with cmd.run_all - ''' - data = _execute_cmd(plugin, args, 'cmd.run_all') + """ + data = _execute_cmd(plugin, args, "cmd.run_all") return data def retcode_pillar(pillar_name): - ''' + """ Run one or more nagios plugins from pillar data and get the result of cmd.retcode The pillar have to be in this format:: @@ -155,8 +159,8 @@ def retcode_pillar(pillar_name): .. code-block:: bash salt '*' nagios.retcode webserver - ''' - groups = __salt__['pillar.get'](pillar_name) + """ + groups = __salt__["pillar.get"](pillar_name) check = {} data = {} @@ -171,26 +175,26 @@ def retcode_pillar(pillar_name): args = command[plugin] else: plugin = command - args = '' + args = "" check.update(retcode(plugin, args, group)) current_value = 0 - new_value = int(check[group]['status']) + new_value = int(check[group]["status"]) if group in data: - current_value = int(data[group]['status']) + current_value = int(data[group]["status"]) if (new_value > current_value) or (group not in data): if group not in data: data[group] = {} - data[group]['status'] = new_value + data[group]["status"] = new_value return data def run_pillar(pillar_name): - ''' + """ Run one or more nagios plugins from pillar data and get the result of cmd.run The pillar have to be in this format:: @@ -215,14 +219,14 @@ def run_pillar(pillar_name): .. code-block:: bash salt '*' nagios.run webserver - ''' + """ data = _execute_pillar(pillar_name, run) return data def run_all_pillar(pillar_name): - ''' + """ Run one or more nagios plugins from pillar data and get the result of cmd.run_all The pillar have to be in this format:: @@ -247,13 +251,13 @@ def run_all_pillar(pillar_name): .. code-block:: bash salt '*' nagios.run webserver - ''' + """ data = _execute_pillar(pillar_name, run_all) return data def list_plugins(): - ''' + """ List all the nagios plugins CLI Example: @@ -261,7 +265,7 @@ def list_plugins(): .. code-block:: bash salt '*' nagios.list_plugins - ''' + """ plugin_list = os.listdir(PLUGINDIR) ret = [] for plugin in plugin_list: diff --git a/salt/modules/nagios_rpc.py b/salt/modules/nagios_rpc.py index 80d167e1c88..6fc13c1bf0a 100644 --- a/salt/modules/nagios_rpc.py +++ b/salt/modules/nagios_rpc.py @@ -1,21 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" Check Host & Service status from Nagios via JSON RPC. .. versionadded:: 2015.8.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs # pylint: disable=import-error,no-name-in-module,redefined-builtin import salt.ext.six.moves.http_client -from salt.exceptions import CommandExecutionError - import salt.utils.http +from salt.exceptions import CommandExecutionError # pylint: enable=import-error,no-name-in-module @@ -23,62 +23,62 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if requests is successfully imported - ''' - return 'nagios_rpc' + """ + return "nagios_rpc" def _config(): - ''' + """ Get configuration items for URL, Username and Password - ''' - status_url = __salt__['config.get']('nagios.status_url') or \ - __salt__['config.get']('nagios:status_url') + """ + status_url = __salt__["config.get"]("nagios.status_url") or __salt__["config.get"]( + "nagios:status_url" + ) if not status_url: - raise CommandExecutionError('Missing Nagios URL in the configuration.') + raise CommandExecutionError("Missing Nagios URL in the configuration.") - username = __salt__['config.get']('nagios.username') or \ - __salt__['config.get']('nagios:username') - password = __salt__['config.get']('nagios.password') or \ - __salt__['config.get']('nagios:password') - return { - 'url': status_url, - 'username': username, - 'password': password - } + username = __salt__["config.get"]("nagios.username") or __salt__["config.get"]( + "nagios:username" + ) + password = __salt__["config.get"]("nagios.password") or __salt__["config.get"]( + "nagios:password" + ) + return {"url": status_url, "username": username, "password": password} def _status_query(query, hostname, enumerate=None, service=None): - ''' + """ Send query along to Nagios. - ''' + """ config = _config() data = None params = { - 'hostname': hostname, - 'query': query, + "hostname": hostname, + "query": query, } - ret = { - 'result': False - } + ret = {"result": False} if enumerate: - params['formatoptions'] = 'enumerate' + params["formatoptions"] = "enumerate" if service: - params['servicedescription'] = service + params["servicedescription"] = service - if config['username'] and config['password'] is not None: - auth = (config['username'], config['password'],) + if config["username"] and config["password"] is not None: + auth = ( + config["username"], + config["password"], + ) else: auth = None try: result = salt.utils.http.query( - config['url'], - method='GET', + config["url"], + method="GET", params=params, decode=True, data=data, @@ -86,32 +86,32 @@ def _status_query(query, hostname, enumerate=None, service=None): status=True, header_dict={}, auth=auth, - backend='requests', + backend="requests", opts=__opts__, ) except ValueError: - ret['error'] = 'Please ensure Nagios is running.' - ret['result'] = False + ret["error"] = "Please ensure Nagios is running." + ret["result"] = False return ret - if result.get('status', None) == salt.ext.six.moves.http_client.OK: + if result.get("status", None) == salt.ext.six.moves.http_client.OK: try: - ret['json_data'] = result['dict'] - ret['result'] = True + ret["json_data"] = result["dict"] + ret["result"] = True except ValueError: - ret['error'] = 'Please ensure Nagios is running.' - elif result.get('status', None) == salt.ext.six.moves.http_client.UNAUTHORIZED: - ret['error'] = 'Authentication failed. Please check the configuration.' - elif result.get('status', None) == salt.ext.six.moves.http_client.NOT_FOUND: - ret['error'] = 'URL {0} was not found.'.format(config['url']) + ret["error"] = "Please ensure Nagios is running." + elif result.get("status", None) == salt.ext.six.moves.http_client.UNAUTHORIZED: + ret["error"] = "Authentication failed. Please check the configuration." + elif result.get("status", None) == salt.ext.six.moves.http_client.NOT_FOUND: + ret["error"] = "URL {0} was not found.".format(config["url"]) else: - ret['error'] = 'Results: {0}'.format(result.text) + ret["error"] = "Results: {0}".format(result.text) return ret def host_status(hostname=None, **kwargs): - ''' + """ Check status of a particular host By default statuses are returned in a numeric format. @@ -132,26 +132,30 @@ def host_status(hostname=None, **kwargs): salt '*' nagios_rpc.host_status hostname=webserver.domain.com salt '*' nagios_rpc.host_status hostname=webserver.domain.com numeric=False - ''' + """ if not hostname: - raise CommandExecutionError('Missing hostname parameter') + raise CommandExecutionError("Missing hostname parameter") - target = 'host' - numeric = kwargs.get('numeric') + target = "host" + numeric = kwargs.get("numeric") data = _status_query(target, hostname, enumerate=numeric) - ret = {'result': data['result']} - if ret['result']: - ret['status'] = data.get('json_data', {}).get('data', {}).get(target, {}).get('status', - not numeric and 'Unknown' or 2) + ret = {"result": data["result"]} + if ret["result"]: + ret["status"] = ( + data.get("json_data", {}) + .get("data", {}) + .get(target, {}) + .get("status", not numeric and "Unknown" or 2) + ) else: - ret['error'] = data['error'] + ret["error"] = data["error"] return ret def service_status(hostname=None, service=None, **kwargs): - ''' + """ Check status of a particular service on a host on it in Nagios. By default statuses are returned in a numeric format. @@ -175,22 +179,26 @@ def service_status(hostname=None, service=None, **kwargs): salt '*' nagios_rpc.service_status hostname=webserver.domain.com service='HTTP' salt '*' nagios_rpc.service_status hostname=webserver.domain.com service='HTTP' numeric=False - ''' + """ if not hostname: - raise CommandExecutionError('Missing hostname parameter') + raise CommandExecutionError("Missing hostname parameter") if not service: - raise CommandExecutionError('Missing service parameter') + raise CommandExecutionError("Missing service parameter") - target = 'service' - numeric = kwargs.get('numeric') + target = "service" + numeric = kwargs.get("numeric") data = _status_query(target, hostname, service=service, enumerate=numeric) - ret = {'result': data['result']} - if ret['result']: - ret['status'] = data.get('json_data', {}).get('data', {}).get(target, {}).get('status', - not numeric and 'Unknown' or 2) + ret = {"result": data["result"]} + if ret["result"]: + ret["status"] = ( + data.get("json_data", {}) + .get("data", {}) + .get(target, {}) + .get("status", not numeric and "Unknown" or 2) + ) else: - ret['error'] = data['error'] + ret["error"] = data["error"] return ret diff --git a/salt/modules/namecheap_domains.py b/salt/modules/namecheap_domains.py index dcef0c28e3c..b30abe8f34d 100644 --- a/salt/modules/namecheap_domains.py +++ b/salt/modules/namecheap_domains.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Namecheap Domain Management .. versionadded:: 2017.7.0 @@ -25,10 +25,14 @@ file, or in the Pillar data. namecheap.url: https://api.namecheap.com/xml.response #Sandbox url #namecheap.url: https://api.sandbox.namecheap.xml.response -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging +# Import 3rd-party libs +from salt.ext import six + CAN_USE_NAMECHEAP = True try: @@ -36,23 +40,21 @@ try: except ImportError: CAN_USE_NAMECHEAP = False -# Import 3rd-party libs -from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Check to make sure requests and xml are installed and requests - ''' + """ if CAN_USE_NAMECHEAP: - return 'namecheap_domains' + return "namecheap_domains" return False def reactivate(domain_name): - ''' + """ Try to reactivate the expired domain name Returns the following information: @@ -67,21 +69,23 @@ def reactivate(domain_name): .. code-block:: bash salt 'my-minion' namecheap_domains.reactivate my-domain-name - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.reactivate') - opts['DomainName'] = domain_name + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.reactivate") + opts["DomainName"] = domain_name response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return {} - domainreactivateresult = response_xml.getElementsByTagName('DomainReactivateResult')[0] + domainreactivateresult = response_xml.getElementsByTagName( + "DomainReactivateResult" + )[0] return salt.utils.namecheap.xml_to_dict(domainreactivateresult) def renew(domain_name, years, promotion_code=None): - ''' + """ Try to renew the specified expiring domain name for a specified number of years domain_name @@ -103,13 +107,13 @@ def renew(domain_name, years, promotion_code=None): .. code-block:: bash salt 'my-minion' namecheap_domains.renew my-domain-name 5 - ''' + """ - opts = salt.utils.namecheap.get_opts('namecheap.domains.renew') - opts['DomainName'] = domain_name - opts['Years'] = years + opts = salt.utils.namecheap.get_opts("namecheap.domains.renew") + opts["DomainName"] = domain_name + opts["Years"] = years if promotion_code is not None: - opts['PromotionCode'] = promotion_code + opts["PromotionCode"] = promotion_code response_xml = salt.utils.namecheap.post_request(opts) @@ -121,7 +125,7 @@ def renew(domain_name, years, promotion_code=None): def create(domain_name, years, **kwargs): - ''' + """ Try to register the specified domain name domain_name @@ -145,27 +149,164 @@ def create(domain_name, years, **kwargs): .. code-block:: bash salt 'my-minion' namecheap_domains.create my-domain-name 2 - ''' - idn_codes = ('afr', 'alb', 'ara', 'arg', 'arm', 'asm', 'ast', 'ave', 'awa', 'aze', 'bak', 'bal', 'ban', 'baq', - 'bas', 'bel', 'ben', 'bho', 'bos', 'bul', 'bur', 'car', 'cat', 'che', 'chi', 'chv', 'cop', 'cos', - 'cze', 'dan', 'div', 'doi', 'dut', 'eng', 'est', 'fao', 'fij', 'fin', 'fre', 'fry', 'geo', 'ger', - 'gla', 'gle', 'gon', 'gre', 'guj', 'heb', 'hin', 'hun', 'inc', 'ind', 'inh', 'isl', 'ita', 'jav', - 'jpn', 'kas', 'kaz', 'khm', 'kir', 'kor', 'kur', 'lao', 'lav', 'lit', 'ltz', 'mal', 'mkd', 'mlt', - 'mol', 'mon', 'mri', 'msa', 'nep', 'nor', 'ori', 'oss', 'pan', 'per', 'pol', 'por', 'pus', 'raj', - 'rum', 'rus', 'san', 'scr', 'sin', 'slo', 'slv', 'smo', 'snd', 'som', 'spa', 'srd', 'srp', 'swa', - 'swe', 'syr', 'tam', 'tel', 'tgk', 'tha', 'tib', 'tur', 'ukr', 'urd', 'uzb', 'vie', 'wel', 'yid') + """ + idn_codes = ( + "afr", + "alb", + "ara", + "arg", + "arm", + "asm", + "ast", + "ave", + "awa", + "aze", + "bak", + "bal", + "ban", + "baq", + "bas", + "bel", + "ben", + "bho", + "bos", + "bul", + "bur", + "car", + "cat", + "che", + "chi", + "chv", + "cop", + "cos", + "cze", + "dan", + "div", + "doi", + "dut", + "eng", + "est", + "fao", + "fij", + "fin", + "fre", + "fry", + "geo", + "ger", + "gla", + "gle", + "gon", + "gre", + "guj", + "heb", + "hin", + "hun", + "inc", + "ind", + "inh", + "isl", + "ita", + "jav", + "jpn", + "kas", + "kaz", + "khm", + "kir", + "kor", + "kur", + "lao", + "lav", + "lit", + "ltz", + "mal", + "mkd", + "mlt", + "mol", + "mon", + "mri", + "msa", + "nep", + "nor", + "ori", + "oss", + "pan", + "per", + "pol", + "por", + "pus", + "raj", + "rum", + "rus", + "san", + "scr", + "sin", + "slo", + "slv", + "smo", + "snd", + "som", + "spa", + "srd", + "srp", + "swa", + "swe", + "syr", + "tam", + "tel", + "tgk", + "tha", + "tib", + "tur", + "ukr", + "urd", + "uzb", + "vie", + "wel", + "yid", + ) - require_opts = ['AdminAddress1', 'AdminCity', 'AdminCountry', 'AdminEmailAddress', 'AdminFirstName', - 'AdminLastName', 'AdminPhone', 'AdminPostalCode', 'AdminStateProvince', 'AuxBillingAddress1', - 'AuxBillingCity', 'AuxBillingCountry', 'AuxBillingEmailAddress', 'AuxBillingFirstName', - 'AuxBillingLastName', 'AuxBillingPhone', 'AuxBillingPostalCode', 'AuxBillingStateProvince', - 'RegistrantAddress1', 'RegistrantCity', 'RegistrantCountry', 'RegistrantEmailAddress', - 'RegistrantFirstName', 'RegistrantLastName', 'RegistrantPhone', 'RegistrantPostalCode', - 'RegistrantStateProvince', 'TechAddress1', 'TechCity', 'TechCountry', 'TechEmailAddress', - 'TechFirstName', 'TechLastName', 'TechPhone', 'TechPostalCode', 'TechStateProvince', 'Years'] - opts = salt.utils.namecheap.get_opts('namecheap.domains.create') - opts['DomainName'] = domain_name - opts['Years'] = six.text_type(years) + require_opts = [ + "AdminAddress1", + "AdminCity", + "AdminCountry", + "AdminEmailAddress", + "AdminFirstName", + "AdminLastName", + "AdminPhone", + "AdminPostalCode", + "AdminStateProvince", + "AuxBillingAddress1", + "AuxBillingCity", + "AuxBillingCountry", + "AuxBillingEmailAddress", + "AuxBillingFirstName", + "AuxBillingLastName", + "AuxBillingPhone", + "AuxBillingPostalCode", + "AuxBillingStateProvince", + "RegistrantAddress1", + "RegistrantCity", + "RegistrantCountry", + "RegistrantEmailAddress", + "RegistrantFirstName", + "RegistrantLastName", + "RegistrantPhone", + "RegistrantPostalCode", + "RegistrantStateProvince", + "TechAddress1", + "TechCity", + "TechCountry", + "TechEmailAddress", + "TechFirstName", + "TechLastName", + "TechPhone", + "TechPostalCode", + "TechStateProvince", + "Years", + ] + opts = salt.utils.namecheap.get_opts("namecheap.domains.create") + opts["DomainName"] = domain_name + opts["Years"] = six.text_type(years) def add_to_opts(opts_dict, kwargs, value, suffix, prefices): for prefix in prefices: @@ -174,24 +315,54 @@ def create(domain_name, years, **kwargs): opts_dict[nextkey] = value for key, value in six.iteritems(kwargs): - if key.startswith('Registrant'): - add_to_opts(opts, kwargs, value, key[10:], ['Tech', 'Admin', 'AuxBilling', 'Billing']) + if key.startswith("Registrant"): + add_to_opts( + opts, + kwargs, + value, + key[10:], + ["Tech", "Admin", "AuxBilling", "Billing"], + ) - if key.startswith('Tech'): - add_to_opts(opts, kwargs, value, key[4:], ['Registrant', 'Admin', 'AuxBilling', 'Billing']) + if key.startswith("Tech"): + add_to_opts( + opts, + kwargs, + value, + key[4:], + ["Registrant", "Admin", "AuxBilling", "Billing"], + ) - if key.startswith('Admin'): - add_to_opts(opts, kwargs, value, key[5:], ['Registrant', 'Tech', 'AuxBilling', 'Billing']) + if key.startswith("Admin"): + add_to_opts( + opts, + kwargs, + value, + key[5:], + ["Registrant", "Tech", "AuxBilling", "Billing"], + ) - if key.startswith('AuxBilling'): - add_to_opts(opts, kwargs, value, key[10:], ['Registrant', 'Tech', 'Admin', 'Billing']) + if key.startswith("AuxBilling"): + add_to_opts( + opts, + kwargs, + value, + key[10:], + ["Registrant", "Tech", "Admin", "Billing"], + ) - if key.startswith('Billing'): - add_to_opts(opts, kwargs, value, key[7:], ['Registrant', 'Tech', 'Admin', 'AuxBilling']) + if key.startswith("Billing"): + add_to_opts( + opts, + kwargs, + value, + key[7:], + ["Registrant", "Tech", "Admin", "AuxBilling"], + ) - if key == 'IdnCode' and key not in idn_codes: - log.error('Invalid IdnCode') - raise Exception('Invalid IdnCode') + if key == "IdnCode" and key not in idn_codes: + log.error("Invalid IdnCode") + raise Exception("Invalid IdnCode") opts[key] = value @@ -210,7 +381,7 @@ def create(domain_name, years, **kwargs): def check(*domains_to_check): - ''' + """ Checks the availability of domains domains_to_check @@ -224,9 +395,9 @@ def check(*domains_to_check): .. code-block:: bash salt 'my-minion' namecheap_domains.check domain-to-check - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.check') - opts['DomainList'] = ','.join(domains_to_check) + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.check") + opts["DomainList"] = ",".join(domains_to_check) response_xml = salt.utils.namecheap.get_request(opts) @@ -236,13 +407,15 @@ def check(*domains_to_check): domains_checked = {} for result in response_xml.getElementsByTagName("DomainCheckResult"): available = result.getAttribute("Available") - domains_checked[result.getAttribute("Domain").lower()] = salt.utils.namecheap.string_to_value(available) + domains_checked[ + result.getAttribute("Domain").lower() + ] = salt.utils.namecheap.string_to_value(available) return domains_checked def get_info(domain_name): - ''' + """ Returns information about the requested domain returns a dictionary of information about the domain_name @@ -255,9 +428,9 @@ def get_info(domain_name): .. code-block:: bash salt 'my-minion' namecheap_domains.get_info my-domain-name - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.getinfo') - opts['DomainName'] = domain_name + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.getinfo") + opts["DomainName"] = domain_name response_xml = salt.utils.namecheap.get_request(opts) @@ -270,7 +443,7 @@ def get_info(domain_name): def get_tld_list(): - ''' + """ Returns a list of TLDs as objects CLI Example: @@ -278,9 +451,11 @@ def get_tld_list(): .. code-block:: bash salt 'my-minion' namecheap_domains.get_tld_list - ''' + """ - response_xml = salt.utils.namecheap.get_request(salt.utils.namecheap.get_opts('namecheap.domains.gettldlist')) + response_xml = salt.utils.namecheap.get_request( + salt.utils.namecheap.get_opts("namecheap.domains.gettldlist") + ) if response_xml is None: return [] @@ -290,23 +465,19 @@ def get_tld_list(): for e in tldresult.getElementsByTagName("Tld"): tld = salt.utils.namecheap.atts_to_dict(e) - tld['data'] = e.firstChild.data + tld["data"] = e.firstChild.data categories = [] subcategories = e.getElementsByTagName("Categories")[0] for c in subcategories.getElementsByTagName("TldCategory"): categories.append(salt.utils.namecheap.atts_to_dict(c)) - tld['categories'] = categories + tld["categories"] = categories tlds.append(tld) return tlds -def get_list(list_type=None, - search_term=None, - page=None, - page_size=None, - sort_by=None): - ''' +def get_list(list_type=None, search_term=None, page=None, page_size=None, sort_by=None): + """ Returns a list of domains for the particular user as a list of objects offset by ``page`` length of ``page_size`` @@ -332,35 +503,42 @@ def get_list(list_type=None, .. code-block:: bash salt 'my-minion' namecheap_domains.get_list - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.getList') + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.getList") if list_type is not None: - if list_type not in ['ALL', 'EXPIRING', 'EXPIRED']: - log.error('Invalid option for list_type') - raise Exception('Invalid option for list_type') - opts['ListType'] = list_type + if list_type not in ["ALL", "EXPIRING", "EXPIRED"]: + log.error("Invalid option for list_type") + raise Exception("Invalid option for list_type") + opts["ListType"] = list_type if search_term is not None: if len(search_term) > 70: - log.warning('search_term trimmed to first 70 characters') + log.warning("search_term trimmed to first 70 characters") search_term = search_term[0:70] - opts['SearchTerm'] = search_term + opts["SearchTerm"] = search_term if page is not None: - opts['Page'] = page + opts["Page"] = page if page_size is not None: if page_size > 100 or page_size < 10: - log.error('Invalid option for page') - raise Exception('Invalid option for page') - opts['PageSize'] = page_size + log.error("Invalid option for page") + raise Exception("Invalid option for page") + opts["PageSize"] = page_size if sort_by is not None: - if sort_by not in ['NAME', 'NAME_DESC', 'EXPIREDATE', 'EXPIREDATE_DESC', 'CREATEDATE', 'CREATEDATE_DESC']: - log.error('Invalid option for sort_by') - raise Exception('Invalid option for sort_by') - opts['SortBy'] = sort_by + if sort_by not in [ + "NAME", + "NAME_DESC", + "EXPIREDATE", + "EXPIREDATE_DESC", + "CREATEDATE", + "CREATEDATE_DESC", + ]: + log.error("Invalid option for sort_by") + raise Exception("Invalid option for sort_by") + opts["SortBy"] = sort_by response_xml = salt.utils.namecheap.get_request(opts) diff --git a/salt/modules/namecheap_domains_dns.py b/salt/modules/namecheap_domains_dns.py index 8adc5691af4..f77ec4a2334 100644 --- a/salt/modules/namecheap_domains_dns.py +++ b/salt/modules/namecheap_domains_dns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Namecheap DNS Management .. versionadded:: 2017.7.0 @@ -25,7 +25,7 @@ file, or in the Pillar data. namecheap.url: https://api.namecheap.com/xml.response #Sandbox url #namecheap.url: https://api.sandbox.namecheap.xml.response -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -41,20 +41,20 @@ try: except ImportError: CAN_USE_NAMECHEAP = False -__virtualname__ = 'namecheap_domains_dns' +__virtualname__ = "namecheap_domains_dns" def __virtual__(): - ''' + """ Check to make sure requests and xml are installed and requests - ''' + """ if CAN_USE_NAMECHEAP: - return 'namecheap_domains_dns' + return "namecheap_domains_dns" return False def get_hosts(sld, tld): - ''' + """ Retrieves DNS host record settings for the requested domain. returns a dictionary of information about the requested domain @@ -70,22 +70,24 @@ def get_hosts(sld, tld): .. code-block:: bash salt 'my-minion' namecheap_domains_dns.get_hosts sld tld - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.dns.gethosts') - opts['TLD'] = tld - opts['SLD'] = sld + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.dns.gethosts") + opts["TLD"] = tld + opts["SLD"] = sld response_xml = salt.utils.namecheap.get_request(opts) if response_xml is None: return {} - domaindnsgethostsresult = response_xml.getElementsByTagName('DomainDNSGetHostsResult')[0] + domaindnsgethostsresult = response_xml.getElementsByTagName( + "DomainDNSGetHostsResult" + )[0] return salt.utils.namecheap.xml_to_dict(domaindnsgethostsresult) def get_list(sld, tld): - ''' + """ Gets a list of DNS servers associated with the requested domain. returns a dictionary of information about requested domain @@ -101,22 +103,24 @@ def get_list(sld, tld): .. code-block:: bash salt 'my-minion' namecheap_domains_dns.get_list sld tld - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.dns.getlist') - opts['TLD'] = tld - opts['SLD'] = sld + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.dns.getlist") + opts["TLD"] = tld + opts["SLD"] = sld response_xml = salt.utils.namecheap.get_request(opts) if response_xml is None: return {} - domaindnsgetlistresult = response_xml.getElementsByTagName('DomainDNSGetListResult')[0] + domaindnsgetlistresult = response_xml.getElementsByTagName( + "DomainDNSGetListResult" + )[0] return salt.utils.namecheap.xml_to_dict(domaindnsgetlistresult) def set_hosts(sld, tld, hosts): - ''' + """ Sets DNS host records settings for the requested domain. returns True if the host records were set successfully @@ -145,33 +149,33 @@ def set_hosts(sld, tld, hosts): .. code-block:: bash salt 'my-minion' namecheap_domains_dns.set_hosts sld tld hosts - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.dns.setHosts') - opts['SLD'] = sld - opts['TLD'] = tld + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.dns.setHosts") + opts["SLD"] = sld + opts["TLD"] = tld i = 1 for hostrecord in hosts: str_i = six.text_type(i) - opts['HostName' + str_i] = hostrecord['hostname'] - opts['RecordType' + str_i] = hostrecord['recordtype'] - opts['Address' + str_i] = hostrecord['address'] - if 'ttl' in hostrecord: - opts['TTL' + str_i] = hostrecord['ttl'] - if 'mxpref' in hostrecord: - opts['MXPref' + str_i] = hostrecord['mxpref'] - opts['EmailType'] = hostrecord['emailtype'] + opts["HostName" + str_i] = hostrecord["hostname"] + opts["RecordType" + str_i] = hostrecord["recordtype"] + opts["Address" + str_i] = hostrecord["address"] + if "ttl" in hostrecord: + opts["TTL" + str_i] = hostrecord["ttl"] + if "mxpref" in hostrecord: + opts["MXPref" + str_i] = hostrecord["mxpref"] + opts["EmailType"] = hostrecord["emailtype"] i += 1 response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return False - dnsresult = response_xml.getElementsByTagName('DomainDNSSetHostsResult')[0] - return salt.utils.namecheap.string_to_value(dnsresult.getAttribute('IsSuccess')) + dnsresult = response_xml.getElementsByTagName("DomainDNSSetHostsResult")[0] + return salt.utils.namecheap.string_to_value(dnsresult.getAttribute("IsSuccess")) def set_custom(sld, tld, nameservers): - ''' + """ Sets domain to use custom DNS servers. returns True if the custom nameservers were set successfully @@ -190,21 +194,21 @@ def set_custom(sld, tld, nameservers): .. code-block:: bash salt 'my-minion' namecheap_domains_dns.set_custom sld tld nameserver - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.dns.setCustom') - opts['SLD'] = sld - opts['TLD'] = tld - opts['Nameservers'] = ','.join(nameservers) + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.dns.setCustom") + opts["SLD"] = sld + opts["TLD"] = tld + opts["Nameservers"] = ",".join(nameservers) response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return False - dnsresult = response_xml.getElementsByTagName('DomainDNSSetCustomResult')[0] - return salt.utils.namecheap.string_to_value(dnsresult.getAttribute('Update')) + dnsresult = response_xml.getElementsByTagName("DomainDNSSetCustomResult")[0] + return salt.utils.namecheap.string_to_value(dnsresult.getAttribute("Update")) def set_default(sld, tld): - ''' + """ Sets domain to use namecheap default DNS servers. Required for free services like Host record management, URL forwarding, email forwarding, dynamic DNS and other value added services. @@ -223,13 +227,13 @@ def set_default(sld, tld): .. code-block:: bash salt 'my-minion' namecheap_domains_dns.set_default sld tld - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.dns.setDefault') - opts['SLD'] = sld - opts['TLD'] = tld + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.dns.setDefault") + opts["SLD"] = sld + opts["TLD"] = tld response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return False - dnsresult = response_xml.getElementsByTagName('DomainDNSSetDefaultResult')[0] - return salt.utils.namecheap.string_to_value(dnsresult.getAttribute('Updated')) + dnsresult = response_xml.getElementsByTagName("DomainDNSSetDefaultResult")[0] + return salt.utils.namecheap.string_to_value(dnsresult.getAttribute("Updated")) diff --git a/salt/modules/namecheap_domains_ns.py b/salt/modules/namecheap_domains_ns.py index 95ce1ff8f7e..e606e420245 100644 --- a/salt/modules/namecheap_domains_ns.py +++ b/salt/modules/namecheap_domains_ns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Namecheap Nameserver Management .. versionadded:: 2017.7.0 @@ -25,7 +25,7 @@ file, or in the Pillar data. namecheap.url: https://api.namecheap.com/xml.response #Sandbox url #namecheap.url: https://api.sandbox.namecheap.xml.response -''' +""" from __future__ import absolute_import, print_function, unicode_literals CAN_USE_NAMECHEAP = True @@ -37,20 +37,20 @@ except ImportError: CAN_USE_NAMECHEAP = False -__virtualname__ = 'namecheap_domains_ns' +__virtualname__ = "namecheap_domains_ns" def __virtual__(): - ''' + """ Check to make sure requests and xml are installed and requests - ''' + """ if CAN_USE_NAMECHEAP: - return 'namecheap_domains_ns' + return "namecheap_domains_ns" return False def get_info(sld, tld, nameserver): - ''' + """ Retrieves information about a registered nameserver. Returns the following information: @@ -72,23 +72,23 @@ def get_info(sld, tld, nameserver): .. code-block:: bash salt '*' namecheap_domains_ns.get_info sld tld nameserver - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.ns.delete') - opts['SLD'] = sld - opts['TLD'] = tld - opts['Nameserver'] = nameserver + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.ns.delete") + opts["SLD"] = sld + opts["TLD"] = tld + opts["Nameserver"] = nameserver response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return {} - domainnsinforesult = response_xml.getElementsByTagName('DomainNSInfoResult')[0] + domainnsinforesult = response_xml.getElementsByTagName("DomainNSInfoResult")[0] return salt.utils.namecheap.xml_to_dict(domainnsinforesult) def update(sld, tld, nameserver, old_ip, new_ip): - ''' + """ Deletes a nameserver. Returns ``True`` if the nameserver was updated successfully. @@ -112,24 +112,26 @@ def update(sld, tld, nameserver, old_ip, new_ip): .. code-block:: bash salt '*' namecheap_domains_ns.update sld tld nameserver old_ip new_ip - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.ns.update') - opts['SLD'] = sld - opts['TLD'] = tld - opts['Nameserver'] = nameserver - opts['OldIP'] = old_ip - opts['IP'] = new_ip + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.ns.update") + opts["SLD"] = sld + opts["TLD"] = tld + opts["Nameserver"] = nameserver + opts["OldIP"] = old_ip + opts["IP"] = new_ip response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return False - domainnsupdateresult = response_xml.getElementsByTagName('DomainNSUpdateResult')[0] - return salt.utils.namecheap.string_to_value(domainnsupdateresult.getAttribute('IsSuccess')) + domainnsupdateresult = response_xml.getElementsByTagName("DomainNSUpdateResult")[0] + return salt.utils.namecheap.string_to_value( + domainnsupdateresult.getAttribute("IsSuccess") + ) def delete(sld, tld, nameserver): - ''' + """ Deletes a nameserver. Returns ``True`` if the nameserver was deleted successfully @@ -147,22 +149,24 @@ def delete(sld, tld, nameserver): .. code-block:: bash salt '*' namecheap_domains_ns.delete sld tld nameserver - ''' - opts = salt.utils.namecheap.get_opts('namecheap.domains.ns.delete') - opts['SLD'] = sld - opts['TLD'] = tld - opts['Nameserver'] = nameserver + """ + opts = salt.utils.namecheap.get_opts("namecheap.domains.ns.delete") + opts["SLD"] = sld + opts["TLD"] = tld + opts["Nameserver"] = nameserver response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return False - domainnsdeleteresult = response_xml.getElementsByTagName('DomainNSDeleteResult')[0] - return salt.utils.namecheap.string_to_value(domainnsdeleteresult.getAttribute('IsSuccess')) + domainnsdeleteresult = response_xml.getElementsByTagName("DomainNSDeleteResult")[0] + return salt.utils.namecheap.string_to_value( + domainnsdeleteresult.getAttribute("IsSuccess") + ) def create(sld, tld, nameserver, ip): - ''' + """ Creates a new nameserver. Returns ``True`` if the nameserver was created successfully. @@ -183,17 +187,19 @@ def create(sld, tld, nameserver, ip): .. code-block:: bash salt '*' namecheap_domains_ns.create sld tld nameserver ip - ''' + """ - opts = salt.utils.namecheap.get_opts('namecheap.domains.ns.create') - opts['SLD'] = sld - opts['TLD'] = tld - opts['Nameserver'] = nameserver - opts['IP'] = ip + opts = salt.utils.namecheap.get_opts("namecheap.domains.ns.create") + opts["SLD"] = sld + opts["TLD"] = tld + opts["Nameserver"] = nameserver + opts["IP"] = ip response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return False - domainnscreateresult = response_xml.getElementsByTagName('DomainNSCreateResult')[0] - return salt.utils.namecheap.string_to_value(domainnscreateresult.getAttribute('IsSuccess')) + domainnscreateresult = response_xml.getElementsByTagName("DomainNSCreateResult")[0] + return salt.utils.namecheap.string_to_value( + domainnscreateresult.getAttribute("IsSuccess") + ) diff --git a/salt/modules/namecheap_ssl.py b/salt/modules/namecheap_ssl.py index 743a9f861da..43233b172c4 100644 --- a/salt/modules/namecheap_ssl.py +++ b/salt/modules/namecheap_ssl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Namecheap SSL Certificate Management .. versionadded:: 2017.7.0 @@ -25,43 +25,48 @@ file, or in the Pillar data. namecheap.url: https://api.namecheap.com/xml.response #Sandbox url #namecheap.url: https://api.sandbox.namecheap.xml.response -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.files import salt.utils.stringutils +# Import 3rd-party libs +from salt.ext import six + try: import salt.utils.namecheap + CAN_USE_NAMECHEAP = True except ImportError: CAN_USE_NAMECHEAP = False -# Import 3rd-party libs -from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Check to make sure requests and xml are installed and requests - ''' + """ if CAN_USE_NAMECHEAP: - return 'namecheap_ssl' + return "namecheap_ssl" return False -def reissue(csr_file, - certificate_id, - web_server_type, - approver_email=None, - http_dc_validation=False, - **kwargs): - ''' +def reissue( + csr_file, + certificate_id, + web_server_type, + approver_email=None, + http_dc_validation=False, + **kwargs +): + """ Reissues a purchased SSL certificate. Returns a dictionary of result values. @@ -123,18 +128,28 @@ def reissue(csr_file, .. code-block:: bash salt 'my-minion' namecheap_ssl.reissue my-csr-file my-cert-id apachessl - ''' - return __get_certificates('namecheap.ssl.reissue', "SSLReissueResult", csr_file, certificate_id, web_server_type, - approver_email, http_dc_validation, kwargs) + """ + return __get_certificates( + "namecheap.ssl.reissue", + "SSLReissueResult", + csr_file, + certificate_id, + web_server_type, + approver_email, + http_dc_validation, + kwargs, + ) -def activate(csr_file, - certificate_id, - web_server_type, - approver_email=None, - http_dc_validation=False, - **kwargs): - ''' +def activate( + csr_file, + certificate_id, + web_server_type, + approver_email=None, + http_dc_validation=False, + **kwargs +): + """ Activates a newly-purchased SSL certificate. Returns a dictionary of result values. @@ -196,76 +211,85 @@ def activate(csr_file, .. code-block:: bash salt 'my-minion' namecheap_ssl.activate my-csr-file my-cert-id apachessl - ''' - return __get_certificates('namecheap.ssl.activate', 'SSLActivateResult', csr_file, certificate_id, web_server_type, - approver_email, http_dc_validation, kwargs) + """ + return __get_certificates( + "namecheap.ssl.activate", + "SSLActivateResult", + csr_file, + certificate_id, + web_server_type, + approver_email, + http_dc_validation, + kwargs, + ) -def __get_certificates(command, - result_tag_name, - csr_file, - certificate_id, - web_server_type, - approver_email, - http_dc_validation, - kwargs): +def __get_certificates( + command, + result_tag_name, + csr_file, + certificate_id, + web_server_type, + approver_email, + http_dc_validation, + kwargs, +): - web_server_types = ('apacheopenssl', - 'apachessl', - 'apacheraven', - 'apachessleay', - 'c2net', - 'ibmhttp', - 'iplanet', - 'domino', - 'dominogo4625', - 'dominogo4626', - 'netscape', - 'zeusv3', - 'apache2', - 'apacheapachessl', - 'cobaltseries', - 'cpanel', - 'ensim', - 'hsphere', - 'ipswitch', - 'plesk', - 'tomcat', - 'weblogic', - 'website', - 'webstar', - 'iis', - 'other', - 'iis4', - 'iis5', - ) + web_server_types = ( + "apacheopenssl", + "apachessl", + "apacheraven", + "apachessleay", + "c2net", + "ibmhttp", + "iplanet", + "domino", + "dominogo4625", + "dominogo4626", + "netscape", + "zeusv3", + "apache2", + "apacheapachessl", + "cobaltseries", + "cpanel", + "ensim", + "hsphere", + "ipswitch", + "plesk", + "tomcat", + "weblogic", + "website", + "webstar", + "iis", + "other", + "iis4", + "iis5", + ) if web_server_type not in web_server_types: - log.error('Invalid option for web_server_type=%s', web_server_type) - raise Exception('Invalid option for web_server_type=' + web_server_type) + log.error("Invalid option for web_server_type=%s", web_server_type) + raise Exception("Invalid option for web_server_type=" + web_server_type) if approver_email is not None and http_dc_validation: - log.error('approver_email and http_dc_validation cannot both have values') - raise Exception('approver_email and http_dc_validation cannot both have values') + log.error("approver_email and http_dc_validation cannot both have values") + raise Exception("approver_email and http_dc_validation cannot both have values") if approver_email is None and not http_dc_validation: - log.error('approver_email or http_dc_validation must have a value') - raise Exception('approver_email or http_dc_validation must have a value') + log.error("approver_email or http_dc_validation must have a value") + raise Exception("approver_email or http_dc_validation must have a value") opts = salt.utils.namecheap.get_opts(command) - with salt.utils.files.fopen(csr_file, 'rb') as csr_handle: - opts['csr'] = salt.utils.stringutils.to_unicode( - csr_handle.read() - ) + with salt.utils.files.fopen(csr_file, "rb") as csr_handle: + opts["csr"] = salt.utils.stringutils.to_unicode(csr_handle.read()) - opts['CertificateID'] = certificate_id - opts['WebServerType'] = web_server_type + opts["CertificateID"] = certificate_id + opts["WebServerType"] = web_server_type if approver_email is not None: - opts['ApproverEmail'] = approver_email + opts["ApproverEmail"] = approver_email if http_dc_validation: - opts['HTTPDCValidation'] = 'True' + opts["HTTPDCValidation"] = "True" for key, value in six.iteritems(kwargs): opts[key] = value @@ -279,21 +303,26 @@ def __get_certificates(command, result = salt.utils.namecheap.atts_to_dict(sslresult) if http_dc_validation: - validation_tag = sslresult.getElementsByTagName('HttpDCValidation') + validation_tag = sslresult.getElementsByTagName("HttpDCValidation") if validation_tag is not None and len(validation_tag) > 0: validation_tag = validation_tag[0] - if validation_tag.getAttribute('ValueAvailable').lower() == 'true': - validation_dict = {'filename': validation_tag.getElementsByTagName('FileName')[0].childNodes[0].data, - 'filecontent': validation_tag.getElementsByTagName('FileContent')[0].childNodes[ - 0].data} - result['httpdcvalidation'] = validation_dict + if validation_tag.getAttribute("ValueAvailable").lower() == "true": + validation_dict = { + "filename": validation_tag.getElementsByTagName("FileName")[0] + .childNodes[0] + .data, + "filecontent": validation_tag.getElementsByTagName("FileContent")[0] + .childNodes[0] + .data, + } + result["httpdcvalidation"] = validation_dict return result def renew(years, certificate_id, certificate_type, promotion_code=None): - ''' + """ Renews an SSL certificate if it is ACTIVE and Expires <= 30 days. Returns the following information: @@ -350,65 +379,66 @@ def renew(years, certificate_id, certificate_type, promotion_code=None): .. code-block:: bash salt 'my-minion' namecheap_ssl.renew 1 my-cert-id RapidSSL - ''' + """ - valid_certs = ('QuickSSL Premium', - 'RapidSSL', - 'RapidSSL Wildcard', - 'PremiumSSL', - 'InstantSSL', - 'PositiveSSL', - 'PositiveSSL Wildcard', - 'True BusinessID with EV', - 'True BusinessID', - 'True BusinessID Wildcard', - 'True BusinessID Multi Domain', - 'True BusinessID with EV Multi Domain', - 'Secure Site', - 'Secure Site Pro', - 'Secure Site with EV', - 'Secure Site Pro with EV', - 'EssentialSSL', - 'EssentialSSL Wildcard', - 'InstantSSL Pro', - 'PremiumSSL Wildcard', - 'EV SSL', - 'EV SSL SGC', - 'SSL123', - 'SSL Web Server', - 'SGC Supercert', - 'SSL Webserver EV', - 'EV Multi Domain SSL', - 'Multi Domain SSL', - 'PositiveSSL Multi Domain', - 'Unified Communications', - ) + valid_certs = ( + "QuickSSL Premium", + "RapidSSL", + "RapidSSL Wildcard", + "PremiumSSL", + "InstantSSL", + "PositiveSSL", + "PositiveSSL Wildcard", + "True BusinessID with EV", + "True BusinessID", + "True BusinessID Wildcard", + "True BusinessID Multi Domain", + "True BusinessID with EV Multi Domain", + "Secure Site", + "Secure Site Pro", + "Secure Site with EV", + "Secure Site Pro with EV", + "EssentialSSL", + "EssentialSSL Wildcard", + "InstantSSL Pro", + "PremiumSSL Wildcard", + "EV SSL", + "EV SSL SGC", + "SSL123", + "SSL Web Server", + "SGC Supercert", + "SSL Webserver EV", + "EV Multi Domain SSL", + "Multi Domain SSL", + "PositiveSSL Multi Domain", + "Unified Communications", + ) if certificate_type not in valid_certs: - log.error('Invalid option for certificate_type=%s', certificate_type) - raise Exception('Invalid option for certificate_type=' + certificate_type) + log.error("Invalid option for certificate_type=%s", certificate_type) + raise Exception("Invalid option for certificate_type=" + certificate_type) if years < 1 or years > 5: - log.error('Invalid option for years=%s', six.text_type(years)) - raise Exception('Invalid option for years=' + six.text_type(years)) + log.error("Invalid option for years=%s", six.text_type(years)) + raise Exception("Invalid option for years=" + six.text_type(years)) - opts = salt.utils.namecheap.get_opts('namecheap.ssl.renew') - opts['Years'] = six.text_type(years) - opts['CertificateID'] = six.text_type(certificate_id) - opts['SSLType'] = certificate_type + opts = salt.utils.namecheap.get_opts("namecheap.ssl.renew") + opts["Years"] = six.text_type(years) + opts["CertificateID"] = six.text_type(certificate_id) + opts["SSLType"] = certificate_type if promotion_code is not None: - opts['PromotionCode'] = promotion_code + opts["PromotionCode"] = promotion_code response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return {} - sslrenewresult = response_xml.getElementsByTagName('SSLRenewResult')[0] + sslrenewresult = response_xml.getElementsByTagName("SSLRenewResult")[0] return salt.utils.namecheap.atts_to_dict(sslrenewresult) def create(years, certificate_type, promotion_code=None, sans_to_add=None): - ''' + """ Creates a new SSL certificate. Returns the following information: - Whether or not the SSL order was successful @@ -529,62 +559,63 @@ def create(years, certificate_type, promotion_code=None, sans_to_add=None): .. code-block:: bash salt 'my-minion' namecheap_ssl.create 2 RapidSSL - ''' - valid_certs = ('QuickSSL Premium', - 'RapidSSL', - 'RapidSSL Wildcard', - 'PremiumSSL', - 'InstantSSL', - 'PositiveSSL', - 'PositiveSSL Wildcard', - 'True BusinessID with EV', - 'True BusinessID', - 'True BusinessID Wildcard', - 'True BusinessID Multi Domain', - 'True BusinessID with EV Multi Domain', - 'Secure Site', - 'Secure Site Pro', - 'Secure Site with EV', - 'Secure Site Pro with EV', - 'EssentialSSL', - 'EssentialSSL Wildcard', - 'InstantSSL Pro', - 'PremiumSSL Wildcard', - 'EV SSL', - 'EV SSL SGC', - 'SSL123', - 'SSL Web Server', - 'SGC Supercert', - 'SSL Webserver EV', - 'EV Multi Domain SSL', - 'Multi Domain SSL', - 'PositiveSSL Multi Domain', - 'Unified Communications', - ) + """ + valid_certs = ( + "QuickSSL Premium", + "RapidSSL", + "RapidSSL Wildcard", + "PremiumSSL", + "InstantSSL", + "PositiveSSL", + "PositiveSSL Wildcard", + "True BusinessID with EV", + "True BusinessID", + "True BusinessID Wildcard", + "True BusinessID Multi Domain", + "True BusinessID with EV Multi Domain", + "Secure Site", + "Secure Site Pro", + "Secure Site with EV", + "Secure Site Pro with EV", + "EssentialSSL", + "EssentialSSL Wildcard", + "InstantSSL Pro", + "PremiumSSL Wildcard", + "EV SSL", + "EV SSL SGC", + "SSL123", + "SSL Web Server", + "SGC Supercert", + "SSL Webserver EV", + "EV Multi Domain SSL", + "Multi Domain SSL", + "PositiveSSL Multi Domain", + "Unified Communications", + ) if certificate_type not in valid_certs: - log.error('Invalid option for certificate_type=%s', certificate_type) - raise Exception('Invalid option for certificate_type=' + certificate_type) + log.error("Invalid option for certificate_type=%s", certificate_type) + raise Exception("Invalid option for certificate_type=" + certificate_type) if years < 1 or years > 5: - log.error('Invalid option for years=%s', six.text_type(years)) - raise Exception('Invalid option for years=' + six.text_type(years)) + log.error("Invalid option for years=%s", six.text_type(years)) + raise Exception("Invalid option for years=" + six.text_type(years)) - opts = salt.utils.namecheap.get_opts('namecheap.ssl.create') + opts = salt.utils.namecheap.get_opts("namecheap.ssl.create") - opts['Years'] = years - opts['Type'] = certificate_type + opts["Years"] = years + opts["Type"] = certificate_type if promotion_code is not None: - opts['PromotionCode'] = promotion_code + opts["PromotionCode"] = promotion_code if sans_to_add is not None: - opts['SANStoADD'] = sans_to_add + opts["SANStoADD"] = sans_to_add response_xml = salt.utils.namecheap.post_request(opts) if response_xml is None: return {} - sslcreateresult = response_xml.getElementsByTagName('SSLCreateResult')[0] - sslcertinfo = sslcreateresult.getElementsByTagName('SSLCertificate')[0] + sslcreateresult = response_xml.getElementsByTagName("SSLCreateResult")[0] + sslcertinfo = sslcreateresult.getElementsByTagName("SSLCertificate")[0] result = salt.utils.namecheap.atts_to_dict(sslcreateresult) result.update(salt.utils.namecheap.atts_to_dict(sslcertinfo)) @@ -592,7 +623,7 @@ def create(years, certificate_type, promotion_code=None, sans_to_add=None): def parse_csr(csr_file, certificate_type, http_dc_validation=False): - ''' + """ Parses the CSR. Returns a dictionary of result values. csr_file @@ -641,63 +672,62 @@ def parse_csr(csr_file, certificate_type, http_dc_validation=False): .. code-block:: bash salt 'my-minion' namecheap_ssl.parse_csr my-csr-file PremiumSSL - ''' - valid_certs = ('QuickSSL Premium', - 'RapidSSL', - 'RapidSSL Wildcard', - 'PremiumSSL', - 'InstantSSL', - 'PositiveSSL', - 'PositiveSSL Wildcard', - 'True BusinessID with EV', - 'True BusinessID', - 'True BusinessID Wildcard', - 'True BusinessID Multi Domain', - 'True BusinessID with EV Multi Domain', - 'Secure Site', - 'Secure Site Pro', - 'Secure Site with EV', - 'Secure Site Pro with EV', - 'EssentialSSL', - 'EssentialSSL Wildcard', - 'InstantSSL Pro', - 'PremiumSSL Wildcard', - 'EV SSL', - 'EV SSL SGC', - 'SSL123', - 'SSL Web Server', - 'SGC Supercert', - 'SSL Webserver EV', - 'EV Multi Domain SSL', - 'Multi Domain SSL', - 'PositiveSSL Multi Domain', - 'Unified Communications', - ) + """ + valid_certs = ( + "QuickSSL Premium", + "RapidSSL", + "RapidSSL Wildcard", + "PremiumSSL", + "InstantSSL", + "PositiveSSL", + "PositiveSSL Wildcard", + "True BusinessID with EV", + "True BusinessID", + "True BusinessID Wildcard", + "True BusinessID Multi Domain", + "True BusinessID with EV Multi Domain", + "Secure Site", + "Secure Site Pro", + "Secure Site with EV", + "Secure Site Pro with EV", + "EssentialSSL", + "EssentialSSL Wildcard", + "InstantSSL Pro", + "PremiumSSL Wildcard", + "EV SSL", + "EV SSL SGC", + "SSL123", + "SSL Web Server", + "SGC Supercert", + "SSL Webserver EV", + "EV Multi Domain SSL", + "Multi Domain SSL", + "PositiveSSL Multi Domain", + "Unified Communications", + ) if certificate_type not in valid_certs: - log.error('Invalid option for certificate_type=%s', certificate_type) - raise Exception('Invalid option for certificate_type=' + certificate_type) + log.error("Invalid option for certificate_type=%s", certificate_type) + raise Exception("Invalid option for certificate_type=" + certificate_type) - opts = salt.utils.namecheap.get_opts('namecheap.ssl.parseCSR') + opts = salt.utils.namecheap.get_opts("namecheap.ssl.parseCSR") - with salt.utils.files.fopen(csr_file, 'rb') as csr_handle: - opts['csr'] = salt.utils.stringutils.to_unicode( - csr_handle.read() - ) + with salt.utils.files.fopen(csr_file, "rb") as csr_handle: + opts["csr"] = salt.utils.stringutils.to_unicode(csr_handle.read()) - opts['CertificateType'] = certificate_type + opts["CertificateType"] = certificate_type if http_dc_validation: - opts['HTTPDCValidation'] = 'true' + opts["HTTPDCValidation"] = "true" response_xml = salt.utils.namecheap.post_request(opts) - sslparseresult = response_xml.getElementsByTagName('SSLParseCSRResult')[0] + sslparseresult = response_xml.getElementsByTagName("SSLParseCSRResult")[0] return salt.utils.namecheap.xml_to_dict(sslparseresult) def get_list(**kwargs): - ''' + """ Returns a list of SSL certificates for a particular user ListType : All @@ -735,8 +765,8 @@ def get_list(**kwargs): .. code-block:: bash salt 'my-minion' namecheap_ssl.get_list Processing - ''' - opts = salt.utils.namecheap.get_opts('namecheap.ssl.getList') + """ + opts = salt.utils.namecheap.get_opts("namecheap.ssl.getList") for key, value in six.iteritems(kwargs): opts[key] = value @@ -745,10 +775,10 @@ def get_list(**kwargs): if response_xml is None: return [] - ssllistresult = response_xml.getElementsByTagName('SSLListResult')[0] + ssllistresult = response_xml.getElementsByTagName("SSLListResult")[0] result = [] - for e in ssllistresult.getElementsByTagName('SSL'): + for e in ssllistresult.getElementsByTagName("SSL"): ssl = salt.utils.namecheap.atts_to_dict(e) result.append(ssl) @@ -756,7 +786,7 @@ def get_list(**kwargs): def get_info(certificate_id, returncertificate=False, returntype=None): - ''' + """ Retrieves information about the requested SSL certificate. Returns a dictionary of information about the SSL certificate with two keys: @@ -782,25 +812,34 @@ def get_info(certificate_id, returncertificate=False, returntype=None): .. code-block:: bash salt 'my-minion' namecheap_ssl.get_info my-cert-id - ''' - opts = salt.utils.namecheap.get_opts('namecheap.ssl.getinfo') - opts['certificateID'] = certificate_id + """ + opts = salt.utils.namecheap.get_opts("namecheap.ssl.getinfo") + opts["certificateID"] = certificate_id if returncertificate: - opts['returncertificate'] = "true" + opts["returncertificate"] = "true" if returntype is None: - log.error('returntype must be specified when returncertificate is set to True') - raise Exception('returntype must be specified when returncertificate is set to True') + log.error( + "returntype must be specified when returncertificate is set to True" + ) + raise Exception( + "returntype must be specified when returncertificate is set to True" + ) if returntype not in ["Individual", "PKCS7"]: - log.error('returntype must be specified as Individual or PKCS7, not %s', returntype) - raise Exception('returntype must be specified as Individual or PKCS7, not ' + returntype) - opts['returntype'] = returntype + log.error( + "returntype must be specified as Individual or PKCS7, not %s", + returntype, + ) + raise Exception( + "returntype must be specified as Individual or PKCS7, not " + returntype + ) + opts["returntype"] = returntype response_xml = salt.utils.namecheap.get_request(opts) if response_xml is None: return {} - sslinforesult = response_xml.getElementsByTagName('SSLGetInfoResult')[0] + sslinforesult = response_xml.getElementsByTagName("SSLGetInfoResult")[0] return salt.utils.namecheap.xml_to_dict(sslinforesult) diff --git a/salt/modules/namecheap_users.py b/salt/modules/namecheap_users.py index 543674b3e04..c26b30e6a2c 100644 --- a/salt/modules/namecheap_users.py +++ b/salt/modules/namecheap_users.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Namecheap User Management .. versionadded:: 2017.7.0 @@ -25,7 +25,7 @@ file, or in the Pillar data. namecheap.url: https://api.namecheap.com/xml.response #Sandbox url #namecheap.url: https://api.sandbox.namecheap.xml.response -''' +""" from __future__ import absolute_import, print_function, unicode_literals CAN_USE_NAMECHEAP = True @@ -38,16 +38,16 @@ except ImportError: def __virtual__(): - ''' + """ Check to make sure requests and xml are installed and requests - ''' + """ if CAN_USE_NAMECHEAP: - return 'namecheap_users' + return "namecheap_users" return False def get_balances(): - ''' + """ Gets information about fund in the user's account. This method returns the following information: Available Balance, Account Balance, Earned Amount, Withdrawable Amount and Funds Required for AutoRenew. @@ -62,8 +62,8 @@ def get_balances(): .. code-block:: bash salt 'my-minion' namecheap_users.get_balances - ''' - opts = salt.utils.namecheap.get_opts('namecheap.users.getBalances') + """ + opts = salt.utils.namecheap.get_opts("namecheap.users.getBalances") response_xml = salt.utils.namecheap.get_request(opts) @@ -75,7 +75,7 @@ def get_balances(): def check_balances(minimum=100): - ''' + """ Checks if the provided minimum value is present in the user's account. Returns a boolean. Returns ``False`` if the user's account balance is less @@ -91,9 +91,9 @@ def check_balances(minimum=100): salt 'my-minion' namecheap_users.check_balances salt 'my-minion' namecheap_users.check_balances minimum=150 - ''' + """ min_float = float(minimum) result = get_balances() - if result['accountbalance'] <= min_float: + if result["accountbalance"] <= min_float: return False return True diff --git a/salt/modules/napalm_bgp.py b/salt/modules/napalm_bgp.py index d0964ea4402..879b925a2fa 100644 --- a/salt/modules/napalm_bgp.py +++ b/salt/modules/napalm_bgp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM BGP ========== @@ -16,26 +16,28 @@ Dependencies - :mod:`napalm proxy minion ` .. versionadded:: 2016.11.0 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import python lib import logging -log = logging.getLogger(__file__) # import NAPALM utils import salt.utils.napalm from salt.utils.napalm import proxy_napalm_wrap +log = logging.getLogger(__file__) + + # ---------------------------------------------------------------------------------------------------------------------- # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'bgp' -__proxyenabled__ = ['napalm'] +__virtualname__ = "bgp" +__proxyenabled__ = ["napalm"] # uses NAPALM-based proxy to interact with network devices -__virtual_aliases__ = ('napalm_bgp',) +__virtual_aliases__ = ("napalm_bgp",) # ---------------------------------------------------------------------------------------------------------------------- # property functions @@ -43,11 +45,12 @@ __virtual_aliases__ = ('napalm_bgp',) def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -60,7 +63,7 @@ def __virtual__(): @proxy_napalm_wrap def config(group=None, neighbor=None, **kwargs): - ''' + """ Provides the BGP configuration on the device. :param group: Name of the group selected to display the configuration. @@ -158,22 +161,19 @@ def config(group=None, neighbor=None, **kwargs): } } } - ''' + """ return salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'get_bgp_config', - **{ - 'group': group, - 'neighbor': neighbor - } + "get_bgp_config", + **{"group": group, "neighbor": neighbor} ) @proxy_napalm_wrap def neighbors(neighbor=None, **kwargs): - ''' + """ Provides details regarding the BGP sessions configured on the network device. :param neighbor: IP Address of a specific neighbor. @@ -269,12 +269,10 @@ def neighbors(neighbor=None, **kwargs): ] } } - ''' + """ return salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'get_bgp_neighbors_detail', - **{ - 'neighbor_address': neighbor - } + "get_bgp_neighbors_detail", + **{"neighbor_address": neighbor} ) diff --git a/salt/modules/napalm_formula.py b/salt/modules/napalm_formula.py index 0425979c30f..fedbc4a543a 100644 --- a/salt/modules/napalm_formula.py +++ b/salt/modules/napalm_formula.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM Formula helpers ====================== @@ -8,65 +8,62 @@ NAPALM Formula helpers This is an Execution Module providing helpers for various NAPALM formulas, e.g., napalm-interfaces-formula, napalm-bgp-formula, napalm-ntp-formula etc., meant to provide various helper functions to make the templates more readable. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import copy -import logging import fnmatch +import logging + +import salt.ext.six as six +import salt.utils.dictupdate # Import salt modules import salt.utils.napalm -import salt.ext.six as six -import salt.utils.dictupdate from salt.defaults import DEFAULT_TARGET_DELIM from salt.utils.data import traverse_dict_and_list as _traverse_dict_and_list -__proxyenabled__ = ['*'] -__virtualname__ = 'napalm_formula' +__proxyenabled__ = ["*"] +__virtualname__ = "napalm_formula" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Available only on NAPALM Minions. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) -def _container_path(model, - key=None, - container=None, - delim=DEFAULT_TARGET_DELIM): - ''' +def _container_path(model, key=None, container=None, delim=DEFAULT_TARGET_DELIM): + """ Generate all the possible paths within an OpenConfig-like object. This function returns a generator. - ''' + """ if not key: - key = '' + key = "" if not container: - container = 'config' + container = "config" for model_key, model_value in six.iteritems(model): if key: - key_depth = '{prev_key}{delim}{cur_key}'.format(prev_key=key, - delim=delim, - cur_key=model_key) + key_depth = "{prev_key}{delim}{cur_key}".format( + prev_key=key, delim=delim, cur_key=model_key + ) else: key_depth = model_key if model_key == container: yield key_depth else: - for value in _container_path(model_value, - key=key_depth, - container=container, - delim=delim): + for value in _container_path( + model_value, key=key_depth, container=container, delim=delim + ): yield value def container_path(model, key=None, container=None, delim=DEFAULT_TARGET_DELIM): - ''' + """ Return the list of all the possible paths in a container, down to the ``config`` container. This function can be used to verify that the ``model`` is a Python object @@ -97,12 +94,12 @@ def container_path(model, key=None, container=None, delim=DEFAULT_TARGET_DELIM): - interfaces:interface:Ethernet1:config - interfaces:interface:Ethernet1:subinterfaces:subinterface:0:config - interfaces:interface:Ethernet2:config - ''' + """ return list(_container_path(model)) def setval(key, val, dict_=None, delim=DEFAULT_TARGET_DELIM): - ''' + """ Set a value under the dictionary hierarchy identified under the key. The target 'foo/bar/baz' returns the dictionary hierarchy {'foo': {'bar': {'baz': {}}}}. @@ -117,7 +114,7 @@ def setval(key, val, dict_=None, delim=DEFAULT_TARGET_DELIM): .. code-block:: bash salt '*' formula.setval foo:baz:bar True - ''' + """ if not dict_: dict_ = {} prev_hier = dict_ @@ -131,7 +128,7 @@ def setval(key, val, dict_=None, delim=DEFAULT_TARGET_DELIM): def traverse(data, key, default=None, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Traverse a dict or list using a colon-delimited (or otherwise delimited, using the ``delimiter`` param) target string. The target ``foo:bar:0`` will return ``data['foo']['bar'][0]`` if this value exists, and will otherwise @@ -146,12 +143,12 @@ def traverse(data, key, default=None, delimiter=DEFAULT_TARGET_DELIM): .. code-block:: bash salt '*' napalm_formula.traverse "{'foo': {'bar': {'baz': True}}}" foo:baz:bar - ''' + """ return _traverse_dict_and_list(data, key, default=default, delimiter=delimiter) def dictupdate(dest, upd, recursive_update=True, merge_lists=False): - ''' + """ Recursive version of the default dict.update Merges upd recursively into dest @@ -163,15 +160,14 @@ def dictupdate(dest, upd, recursive_update=True, merge_lists=False): The list in ``upd`` is added to the list in ``dest``, so the resulting list is ``dest[key] + upd[key]``. This behaviour is only activated when ``recursive_update=True``. By default ``merge_lists=False``. - ''' - return salt.utils.dictupdate.update(dest, upd, recursive_update=recursive_update, merge_lists=merge_lists) + """ + return salt.utils.dictupdate.update( + dest, upd, recursive_update=recursive_update, merge_lists=merge_lists + ) -def defaults(model, - defaults_, - delim='//', - flipped_merge=False): - ''' +def defaults(model, defaults_, delim="//", flipped_merge=False): + """ Apply the defaults to a Python dictionary having the structure as described in the OpenConfig standards. @@ -204,40 +200,36 @@ def defaults(model, As one can notice in the example above, the ``*`` corresponds to the interface name, therefore, the defaults will be applied on all the interfaces. - ''' + """ merged = {} - log.debug('Applying the defaults:') + log.debug("Applying the defaults:") log.debug(defaults_) - log.debug('openconfig like dictionary:') + log.debug("openconfig like dictionary:") log.debug(model) for model_path in _container_path(model, delim=delim): for default_path in _container_path(defaults_, delim=delim): - log.debug('Comparing %s to %s', model_path, default_path) - if not fnmatch.fnmatch(model_path, default_path) or\ - not len(model_path.split(delim)) == len(default_path.split(delim)): + log.debug("Comparing %s to %s", model_path, default_path) + if not fnmatch.fnmatch(model_path, default_path) or not len( + model_path.split(delim) + ) == len(default_path.split(delim)): continue - log.debug('%s matches %s', model_path, default_path) + log.debug("%s matches %s", model_path, default_path) # If there's a match, it will build the dictionary from the top - devault_val = _traverse_dict_and_list(defaults_, - default_path, - delimiter=delim) + devault_val = _traverse_dict_and_list( + defaults_, default_path, delimiter=delim + ) merged = setval(model_path, devault_val, dict_=merged, delim=delim) - log.debug('Complete default dictionary') + log.debug("Complete default dictionary") log.debug(merged) - log.debug('Merging with the model') + log.debug("Merging with the model") log.debug(model) if flipped_merge: return salt.utils.dictupdate.update(model, merged) return salt.utils.dictupdate.update(merged, model) -def render_field(dictionary, - field, - prepend=None, - append=None, - quotes=False, - **opts): - ''' +def render_field(dictionary, field, prepend=None, append=None, quotes=False, **opts): + """ Render a field found under the ``field`` level of the hierarchy in the ``dictionary`` object. This is useful to render a field in a Jinja template without worrying that @@ -293,28 +285,26 @@ def render_field(dictionary, .. code-block:: text description "Interface description"; - ''' + """ value = traverse(dictionary, field) if value is None: - return '' + return "" if prepend is None: - prepend = field.replace('_', '-') + prepend = field.replace("_", "-") if append is None: - if __grains__['os'] in ('junos',): - append = ';' + if __grains__["os"] in ("junos",): + append = ";" else: - append = '' + append = "" if quotes: value = '"{value}"'.format(value=value) - return '{prepend} {value}{append}'.format(prepend=prepend, - value=value, - append=append) + return "{prepend} {value}{append}".format( + prepend=prepend, value=value, append=append + ) -def render_fields(dictionary, - *fields, - **opts): - ''' +def render_fields(dictionary, *fields, **opts): + """ This function works similarly to :mod:`render_field ` but for a list of fields from the same dictionary, rendering, indenting and @@ -351,14 +341,14 @@ def render_fields(dictionary, mtu "68" description "Interface description" - ''' + """ results = [] for field in fields: res = render_field(dictionary, field, **opts) if res: results.append(res) - if 'indent' not in opts: - opts['indent'] = 0 - if 'separator' not in opts: - opts['separator'] = '\n{ind}'.format(ind=' '*opts['indent']) - return opts['separator'].join(results) + if "indent" not in opts: + opts["indent"] = 0 + if "separator" not in opts: + opts["separator"] = "\n{ind}".format(ind=" " * opts["indent"]) + return opts["separator"].join(results) diff --git a/salt/modules/napalm_mod.py b/salt/modules/napalm_mod.py index 23acd6d0e24..c9847b9cb0b 100644 --- a/salt/modules/napalm_mod.py +++ b/salt/modules/napalm_mod.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM helpers ============== Helpers for the NAPALM modules. .. versionadded:: 2017.7.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python stdlib import inspect @@ -15,39 +15,44 @@ import logging # import NAPALM utils import salt.utils.napalm -from salt.utils.napalm import proxy_napalm_wrap +from salt.exceptions import CommandExecutionError # Import Salt modules from salt.ext import six from salt.utils.decorators import depends -from salt.exceptions import CommandExecutionError +from salt.utils.napalm import proxy_napalm_wrap try: from netmiko import BaseConnection + HAS_NETMIKO = True except ImportError: HAS_NETMIKO = False try: import napalm.base.netmiko_helpers # pylint: disable=no-name-in-module + HAS_NETMIKO_HELPERS = True except ImportError: HAS_NETMIKO_HELPERS = False try: import jxmlease # pylint: disable=unused-import + HAS_JXMLEASE = True except ImportError: HAS_JXMLEASE = False try: import ciscoconfparse # pylint: disable=unused-import + HAS_CISCOCONFPARSE = True except ImportError: HAS_CISCOCONFPARSE = False try: import scp # pylint: disable=unused-import + HAS_SCP = True except ImportError: HAS_SCP = False @@ -56,8 +61,8 @@ except ImportError: # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'napalm' -__proxyenabled__ = ['*'] +__virtualname__ = "napalm" +__proxyenabled__ = ["*"] # uses NAPALM-based proxy to interact with network devices log = logging.getLogger(__file__) @@ -68,18 +73,19 @@ log = logging.getLogger(__file__) def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- def _get_netmiko_args(optional_args): - ''' + """ Check for Netmiko arguments that were passed in as NAPALM optional arguments. Return a dictionary of these optional args that will be passed into the @@ -91,7 +97,7 @@ def _get_netmiko_args(optional_args): older versions of NAPALM, and stability across Salt features. If the netmiko helpers module is available however, it will prefer that implementation nevertheless. - ''' + """ if HAS_NETMIKO_HELPERS: return napalm.base.netmiko_helpers.netmiko_args(optional_args) # Older version don't have the netmiko_helpers module, the following code is @@ -99,11 +105,11 @@ def _get_netmiko_args(optional_args): # https://github.com/napalm-automation/napalm/blob/develop/napalm/base/netmiko_helpers.py netmiko_args, _, _, netmiko_defaults = inspect.getargspec(BaseConnection.__init__) check_self = netmiko_args.pop(0) - if check_self != 'self': - raise ValueError('Error processing Netmiko arguments') + if check_self != "self": + raise ValueError("Error processing Netmiko arguments") netmiko_argument_map = dict(six.moves.zip(netmiko_args, netmiko_defaults)) # Netmiko arguments that are integrated into NAPALM already - netmiko_filter = ['ip', 'host', 'username', 'password', 'device_type', 'timeout'] + netmiko_filter = ["ip", "host", "username", "password", "device_type", "timeout"] # Filter out all of the arguments that are integrated into NAPALM for k in netmiko_filter: netmiko_argument_map.pop(k) @@ -119,13 +125,15 @@ def _get_netmiko_args(optional_args): def _inject_junos_proxy(napalm_device): - ''' + """ Inject the junos.conn key into the __proxy__, reusing the existing NAPALM connection to the Junos device. - ''' + """ + def _ret_device(): - return napalm_device['DRIVER'].device - __proxy__['junos.conn'] = _ret_device + return napalm_device["DRIVER"].device + + __proxy__["junos.conn"] = _ret_device # Injecting the junos.conn key into the __proxy__ object, we can then # access the features that already exist into the junos module, as long # as the rest of the dependencies are installed (jxmlease). @@ -137,25 +145,24 @@ def _inject_junos_proxy(napalm_device): def _junos_prep_fun(napalm_device): - ''' + """ Prepare the Junos function. - ''' - if __grains__['os'] != 'junos': + """ + if __grains__["os"] != "junos": return { - 'out': None, - 'result': False, - 'comment': 'This function is only available on Junos' + "out": None, + "result": False, + "comment": "This function is only available on Junos", } if not HAS_JXMLEASE: return { - 'out': None, - 'result': False, - 'comment': 'Please install jxmlease (``pip install jxmlease``) to be able to use this function.' + "out": None, + "result": False, + "comment": "Please install jxmlease (``pip install jxmlease``) to be able to use this function.", } _inject_junos_proxy(napalm_device) - return { - 'result': True - } + return {"result": True} + # ---------------------------------------------------------------------------------------------------------------------- # callable functions @@ -164,7 +171,7 @@ def _junos_prep_fun(napalm_device): @proxy_napalm_wrap def alive(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns the alive status of the connection layer. The output is a dictionary under the usual dictionary output of the NAPALM modules. @@ -183,17 +190,15 @@ def alive(**kwargs): # pylint: disable=unused-argument out: is_alive: False comment: '' - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'is_alive', - **{} + napalm_device, "is_alive", **{} # pylint: disable=undefined-variable ) @proxy_napalm_wrap def reconnect(force=False, **kwargs): # pylint: disable=unused-argument - ''' + """ Reconnect the NAPALM proxy when the connection is dropped by the network device. The connection can be forced to be restarted @@ -209,41 +214,33 @@ def reconnect(force=False, **kwargs): # pylint: disable=unused-argument salt '*' napalm.reconnect salt '*' napalm.reconnect force=True - ''' - default_ret = { - 'out': None, - 'result': True, - 'comment': 'Already alive.' - } + """ + default_ret = {"out": None, "result": True, "comment": "Already alive."} if not salt.utils.napalm.is_proxy(__opts__): # regular minion is always alive # otherwise, the user would not be able to execute this command return default_ret is_alive = alive() - log.debug('Is alive fetch:') + log.debug("Is alive fetch:") log.debug(is_alive) - if not is_alive.get('result', False) or\ - not is_alive.get('out', False) or\ - not is_alive.get('out', {}).get('is_alive', False) or\ - force: # even if alive, but the user wants to force a restart - proxyid = __opts__.get('proxyid') or __opts__.get('id') + if ( + not is_alive.get("result", False) + or not is_alive.get("out", False) + or not is_alive.get("out", {}).get("is_alive", False) + or force + ): # even if alive, but the user wants to force a restart + proxyid = __opts__.get("proxyid") or __opts__.get("id") # close the connection - log.info('Closing the NAPALM proxy connection with %s', proxyid) + log.info("Closing the NAPALM proxy connection with %s", proxyid) salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'close', - **{} + napalm_device, "close", **{} # pylint: disable=undefined-variable ) # and re-open - log.info('Re-opening the NAPALM proxy connection with %s', proxyid) + log.info("Re-opening the NAPALM proxy connection with %s", proxyid) salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'open', - **{} + napalm_device, "open", **{} # pylint: disable=undefined-variable ) - default_ret.update({ - 'comment': 'Connection restarted!' - }) + default_ret.update({"comment": "Connection restarted!"}) return default_ret # otherwise, I have nothing to do here: return default_ret @@ -251,7 +248,7 @@ def reconnect(force=False, **kwargs): # pylint: disable=unused-argument @proxy_napalm_wrap def call(method, *args, **kwargs): - ''' + """ Execute arbitrary methods from the NAPALM library. To see the expected output, please consult the NAPALM documentation. @@ -267,11 +264,11 @@ def call(method, *args, **kwargs): salt '*' napalm.call get_lldp_neighbors salt '*' napalm.call get_firewall_policies salt '*' napalm.call get_bgp_config group='my-group' - ''' + """ clean_kwargs = {} for karg, warg in six.iteritems(kwargs): # remove the __pub args - if not karg.startswith('__pub_'): + if not karg.startswith("__pub_"): clean_kwargs[karg] = warg return salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable @@ -282,11 +279,8 @@ def call(method, *args, **kwargs): @proxy_napalm_wrap -def compliance_report(filepath=None, - string=None, - renderer='jinja|yaml', - **kwargs): - ''' +def compliance_report(filepath=None, string=None, renderer="jinja|yaml", **kwargs): + """ Return the compliance report. filepath @@ -405,21 +399,20 @@ def compliance_report(filepath=None, skipped: result: True - ''' - validation_string = __salt__['slsutil.renderer'](path=filepath, - string=string, - default_renderer=renderer, - **kwargs) + """ + validation_string = __salt__["slsutil.renderer"]( + path=filepath, string=string, default_renderer=renderer, **kwargs + ) return salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'compliance_report', - validation_source=validation_string + "compliance_report", + validation_source=validation_string, ) @proxy_napalm_wrap def netmiko_args(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 Return the key-value arguments used for the authentication arguments for @@ -447,32 +440,34 @@ def netmiko_args(**kwargs): .. code-block:: bash salt '*' napalm.netmiko_args - ''' + """ if not HAS_NETMIKO: - raise CommandExecutionError('Please install netmiko to be able to use this feature.') + raise CommandExecutionError( + "Please install netmiko to be able to use this feature." + ) kwargs = {} napalm_opts = salt.utils.napalm.get_device_opts(__opts__, salt_obj=__salt__) - optional_args = napalm_opts['OPTIONAL_ARGS'] + optional_args = napalm_opts["OPTIONAL_ARGS"] netmiko_args = _get_netmiko_args(optional_args) - kwargs['host'] = napalm_opts['HOSTNAME'] - kwargs['username'] = napalm_opts['USERNAME'] - kwargs['password'] = napalm_opts['PASSWORD'] - kwargs['timeout'] = napalm_opts['TIMEOUT'] + kwargs["host"] = napalm_opts["HOSTNAME"] + kwargs["username"] = napalm_opts["USERNAME"] + kwargs["password"] = napalm_opts["PASSWORD"] + kwargs["timeout"] = napalm_opts["TIMEOUT"] kwargs.update(netmiko_args) netmiko_device_type_map = { - 'junos': 'juniper_junos', - 'ios': 'cisco_ios', - 'iosxr': 'cisco_xr', - 'eos': 'arista_eos', - 'nxos_ssh': 'cisco_nxos', - 'asa': 'cisco_asa', - 'fortios': 'fortinet', - 'panos': 'paloalto_panos', - 'aos': 'alcatel_aos', - 'vyos': 'vyos', - 'f5': 'f5_ltm', - 'ce': 'huawei', - 's350': 'cisco_s300' + "junos": "juniper_junos", + "ios": "cisco_ios", + "iosxr": "cisco_xr", + "eos": "arista_eos", + "nxos_ssh": "cisco_nxos", + "asa": "cisco_asa", + "fortios": "fortinet", + "panos": "paloalto_panos", + "aos": "alcatel_aos", + "vyos": "vyos", + "f5": "f5_ltm", + "ce": "huawei", + "s350": "cisco_s300", } # If you have a device type that is not listed here, please submit a PR # to add it, and/or add the map into your opts/Pillar: netmiko_device_type_map @@ -482,15 +477,17 @@ def netmiko_args(**kwargs): # junos: juniper_junos # ios: cisco_ios # - #etc. - netmiko_device_type_map.update(__salt__['config.get']('netmiko_device_type_map', {})) - kwargs['device_type'] = netmiko_device_type_map[__grains__['os']] + # etc. + netmiko_device_type_map.update( + __salt__["config.get"]("netmiko_device_type_map", {}) + ) + kwargs["device_type"] = netmiko_device_type_map[__grains__["os"]] return kwargs @proxy_napalm_wrap def netmiko_fun(fun, *args, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Call an arbitrary function from the :mod:`Netmiko` @@ -514,9 +511,9 @@ def netmiko_fun(fun, *args, **kwargs): .. code-block:: bash salt '*' napalm.netmiko_fun send_command 'show version' - ''' - if 'netmiko.' not in fun: - fun = 'netmiko.{fun}'.format(fun=fun) + """ + if "netmiko." not in fun: + fun = "netmiko.{fun}".format(fun=fun) netmiko_kwargs = netmiko_args() kwargs.update(netmiko_kwargs) return __salt__[fun](*args, **kwargs) @@ -524,7 +521,7 @@ def netmiko_fun(fun, *args, **kwargs): @proxy_napalm_wrap def netmiko_call(method, *args, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Execute an arbitrary Netmiko method, passing the authentication details from @@ -545,15 +542,15 @@ def netmiko_call(method, *args, **kwargs): .. code-block:: bash salt '*' napalm.netmiko_call send_command 'show version' - ''' + """ netmiko_kwargs = netmiko_args() kwargs.update(netmiko_kwargs) - return __salt__['netmiko.call'](method, *args, **kwargs) + return __salt__["netmiko.call"](method, *args, **kwargs) @proxy_napalm_wrap def netmiko_multi_call(*methods, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Execute a list of arbitrary Netmiko methods, passing the authentication @@ -572,15 +569,15 @@ def netmiko_multi_call(*methods, **kwargs): .. code-block:: bash salt '*' napalm.netmiko_multi_call "{'name': 'send_command', 'args': ['show version']}" "{'name': 'send_command', 'args': ['show interfaces']}" - ''' + """ netmiko_kwargs = netmiko_args() kwargs.update(netmiko_kwargs) - return __salt__['netmiko.multi_call'](*methods, **kwargs) + return __salt__["netmiko.multi_call"](*methods, **kwargs) @proxy_napalm_wrap def netmiko_commands(*commands, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Invoke one or more commands to be executed on the remote device, via Netmiko. @@ -620,7 +617,7 @@ def netmiko_commands(*commands, **kwargs): .. code-block:: bash salt '*' napalm.netmiko_commands 'show version' 'show interfaces' - ''' + """ conn = netmiko_conn(**kwargs) ret = [] for cmd in commands: @@ -630,7 +627,7 @@ def netmiko_commands(*commands, **kwargs): @proxy_napalm_wrap def netmiko_config(*config_commands, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Load a list of configuration commands on the remote device, via Netmiko. @@ -692,16 +689,15 @@ def netmiko_config(*config_commands, **kwargs): salt '*' napalm.netmiko_config 'set system ntp peer 1.2.3.4' commit=True salt '*' napalm.netmiko_config https://bit.ly/2sgljCB - ''' + """ netmiko_kwargs = netmiko_args() kwargs.update(netmiko_kwargs) - return __salt__['netmiko.send_config'](config_commands=config_commands, - **kwargs) + return __salt__["netmiko.send_config"](config_commands=config_commands, **kwargs) @proxy_napalm_wrap def netmiko_conn(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 Return the connection object with the network device, over Netmiko, passing @@ -719,15 +715,15 @@ def netmiko_conn(**kwargs): conn = __salt__['napalm.netmiko_conn']() res = conn.send_command('show interfaces') conn.disconnect() - ''' + """ netmiko_kwargs = netmiko_args() kwargs.update(netmiko_kwargs) - return __salt__['netmiko.get_connection'](**kwargs) + return __salt__["netmiko.get_connection"](**kwargs) @proxy_napalm_wrap def junos_rpc(cmd=None, dest=None, format=None, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Execute an RPC request on the remote Junos device. @@ -767,27 +763,24 @@ def junos_rpc(cmd=None, dest=None, format=None, **kwargs): .. code-block:: bash salt '*' napalm.junos_rpc get-lldp-neighbors-information - salt '*' napalm.junos_rcp get-config - ''' + salt '*' napalm.junos_rpc get-config + """ prep = _junos_prep_fun(napalm_device) # pylint: disable=undefined-variable - if not prep['result']: + if not prep["result"]: return prep if not format: - format = 'xml' - rpc_ret = __salt__['junos.rpc'](cmd=cmd, - dest=dest, - format=format, - **kwargs) - rpc_ret['comment'] = rpc_ret.pop('message', '') - rpc_ret['result'] = rpc_ret.pop('out', False) - rpc_ret['out'] = rpc_ret.pop('rpc_reply', None) + format = "xml" + rpc_ret = __salt__["junos.rpc"](cmd=cmd, dest=dest, format=format, **kwargs) + rpc_ret["comment"] = rpc_ret.pop("message", "") + rpc_ret["result"] = rpc_ret.pop("out", False) + rpc_ret["out"] = rpc_ret.pop("rpc_reply", None) # The comment field is "message" in the Junos module return rpc_ret @proxy_napalm_wrap def junos_commit(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 Commit the changes loaded in the candidate configuration. @@ -827,16 +820,16 @@ def junos_commit(**kwargs): salt '*' napalm.junos_commit comment='Commitiing via Salt' detail=True salt '*' napalm.junos_commit dev_timeout=60 confirm=10 salt '*' napalm.junos_commit sync=True dev_timeout=90 - ''' + """ prep = _junos_prep_fun(napalm_device) # pylint: disable=undefined-variable - if not prep['result']: + if not prep["result"]: return prep - return __salt__['junos.commit'](**kwargs) + return __salt__["junos.commit"](**kwargs) @proxy_napalm_wrap def junos_install_os(path=None, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Installs the given image on the device. @@ -866,16 +859,16 @@ def junos_install_os(path=None, **kwargs): .. code-block:: bash salt '*' napalm.junos_install_os salt://images/junos_16_1.tgz reboot=True - ''' + """ prep = _junos_prep_fun(napalm_device) # pylint: disable=undefined-variable - if not prep['result']: + if not prep["result"]: return prep - return __salt__['junos.install_os'](path=path, **kwargs) + return __salt__["junos.install_os"](path=path, **kwargs) @proxy_napalm_wrap def junos_facts(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 The complete list of Junos facts collected by ``junos-eznc``. @@ -885,26 +878,26 @@ def junos_facts(**kwargs): .. code-block:: bash salt '*' napalm.junos_facts - ''' + """ prep = _junos_prep_fun(napalm_device) # pylint: disable=undefined-variable - if not prep['result']: + if not prep["result"]: return prep - facts = dict(napalm_device['DRIVER'].device.facts) # pylint: disable=undefined-variable - if 'version_info' in facts: - facts['version_info'] = \ - dict(facts['version_info']) + # pylint: disable=undefined-variable + facts = dict(napalm_device["DRIVER"].device.facts) + # pylint: enable=undefined-variable + if "version_info" in facts: + facts["version_info"] = dict(facts["version_info"]) # For backward compatibility. 'junos_info' is present # only of in newer versions of facts. - if 'junos_info' in facts: - for re in facts['junos_info']: - facts['junos_info'][re]['object'] = \ - dict(facts['junos_info'][re]['object']) + if "junos_info" in facts: + for re in facts["junos_info"]: + facts["junos_info"][re]["object"] = dict(facts["junos_info"][re]["object"]) return facts @proxy_napalm_wrap def junos_cli(command, format=None, dev_timeout=None, dest=None, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Execute a CLI command and return the output in the specified format. @@ -928,20 +921,18 @@ def junos_cli(command, format=None, dev_timeout=None, dest=None, **kwargs): .. code-block:: bash salt '*' napalm.junos_cli 'show lldp neighbors' - ''' + """ prep = _junos_prep_fun(napalm_device) # pylint: disable=undefined-variable - if not prep['result']: + if not prep["result"]: return prep - return __salt__['junos.cli'](command, - format=format, - dev_timeout=dev_timeout, - dest=dest, - **kwargs) + return __salt__["junos.cli"]( + command, format=format, dev_timeout=dev_timeout, dest=dest, **kwargs + ) @proxy_napalm_wrap def junos_copy_file(src, dst, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Copies the file on the remote Junos device. @@ -958,17 +949,17 @@ def junos_copy_file(src, dst, **kwargs): .. code-block:: bash salt '*' napalm.junos_copy_file https://example.com/junos.cfg /var/tmp/myjunos.cfg - ''' + """ prep = _junos_prep_fun(napalm_device) # pylint: disable=undefined-variable - if not prep['result']: + if not prep["result"]: return prep - cached_src = __salt__['cp.cache_file'](src) - return __salt__['junos.file_copy'](cached_src, dst) + cached_src = __salt__["cp.cache_file"](src) + return __salt__["junos.file_copy"](cached_src, dst) @proxy_napalm_wrap def junos_call(fun, *args, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Execute an arbitrary function from the @@ -991,25 +982,25 @@ def junos_call(fun, *args, **kwargs): .. code-block:: bash salt '*' napalm.junos_fun cli 'show system commit' - ''' + """ prep = _junos_prep_fun(napalm_device) # pylint: disable=undefined-variable - if not prep['result']: + if not prep["result"]: return prep - if 'junos.' not in fun: - mod_fun = 'junos.{}'.format(fun) + if "junos." not in fun: + mod_fun = "junos.{}".format(fun) else: mod_fun = fun if mod_fun not in __salt__: return { - 'out': None, - 'result': False, - 'comment': '{} is not a valid function'.format(fun) + "out": None, + "result": False, + "comment": "{} is not a valid function".format(fun), } return __salt__[mod_fun](*args, **kwargs) def pyeapi_nxos_api_args(**prev_kwargs): - ''' + """ .. versionadded:: 2019.2.0 Return the key-value arguments used for the authentication arguments for the @@ -1020,24 +1011,24 @@ def pyeapi_nxos_api_args(**prev_kwargs): .. code-block:: bash salt '*' napalm.pyeapi_nxos_api_args - ''' + """ kwargs = {} napalm_opts = salt.utils.napalm.get_device_opts(__opts__, salt_obj=__salt__) - optional_args = napalm_opts['OPTIONAL_ARGS'] - kwargs['host'] = napalm_opts['HOSTNAME'] - kwargs['username'] = napalm_opts['USERNAME'] - kwargs['password'] = napalm_opts['PASSWORD'] - kwargs['timeout'] = napalm_opts['TIMEOUT'] - kwargs['transport'] = optional_args.get('transport') - kwargs['port'] = optional_args.get('port') - kwargs['verify'] = optional_args.get('verify') + optional_args = napalm_opts["OPTIONAL_ARGS"] + kwargs["host"] = napalm_opts["HOSTNAME"] + kwargs["username"] = napalm_opts["USERNAME"] + kwargs["password"] = napalm_opts["PASSWORD"] + kwargs["timeout"] = napalm_opts["TIMEOUT"] + kwargs["transport"] = optional_args.get("transport") + kwargs["port"] = optional_args.get("port") + kwargs["verify"] = optional_args.get("verify") prev_kwargs.update(kwargs) return prev_kwargs @proxy_napalm_wrap def pyeapi_run_commands(*commands, **kwargs): - ''' + """ Execute a list of commands on the Arista switch, via the ``pyeapi`` library. This function forwards the existing connection details to the :mod:`pyeapi.run_commands ` @@ -1056,14 +1047,14 @@ def pyeapi_run_commands(*commands, **kwargs): salt '*' napalm.pyeapi_run_commands 'show version' encoding=text salt '*' napalm.pyeapi_run_commands 'show ip bgp neighbors' - ''' + """ pyeapi_kwargs = pyeapi_nxos_api_args(**kwargs) - return __salt__['pyeapi.run_commands'](*commands, **pyeapi_kwargs) + return __salt__["pyeapi.run_commands"](*commands, **pyeapi_kwargs) @proxy_napalm_wrap def pyeapi_call(method, *args, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Invoke an arbitrary method from the ``pyeapi`` library. @@ -1083,14 +1074,14 @@ def pyeapi_call(method, *args, **kwargs): salt '*' napalm.pyeapi_call run_commands 'show version' encoding=text salt '*' napalm.pyeapi_call get_config as_string=True - ''' + """ pyeapi_kwargs = pyeapi_nxos_api_args(**kwargs) - return __salt__['pyeapi.call'](method, *args, **pyeapi_kwargs) + return __salt__["pyeapi.call"](method, *args, **pyeapi_kwargs) @proxy_napalm_wrap def pyeapi_conn(**kwargs): - ''' + """ .. versionadded:: 2019.2.0 Return the connection object with the Arista switch, over ``pyeapi``, @@ -1108,20 +1099,22 @@ def pyeapi_conn(**kwargs): conn = __salt__['napalm.pyeapi_conn']() res1 = conn.run_commands('show version') res2 = conn.get_config(as_string=True) - ''' + """ pyeapi_kwargs = pyeapi_nxos_api_args(**kwargs) - return __salt__['pyeapi.get_connection'](**pyeapi_kwargs) + return __salt__["pyeapi.get_connection"](**pyeapi_kwargs) @proxy_napalm_wrap -def pyeapi_config(commands=None, - config_file=None, - template_engine='jinja', - context=None, - defaults=None, - saltenv='base', - **kwargs): - ''' +def pyeapi_config( + commands=None, + config_file=None, + template_engine="jinja", + context=None, + defaults=None, + saltenv="base", + **kwargs +): + """ .. versionadded:: 2019.2.0 Configures the Arista switch with the specified commands, via the ``pyeapi`` @@ -1168,22 +1161,22 @@ def pyeapi_config(commands=None, .. code-block:: bash salt '*' napalm.pyeapi_config 'ntp server 1.2.3.4' - ''' + """ pyeapi_kwargs = pyeapi_nxos_api_args(**kwargs) - return __salt__['pyeapi.config'](commands=commands, - config_file=config_file, - template_engine=template_engine, - context=context, - defaults=defaults, - saltenv=saltenv, - **pyeapi_kwargs) + return __salt__["pyeapi.config"]( + commands=commands, + config_file=config_file, + template_engine=template_engine, + context=context, + defaults=defaults, + saltenv=saltenv, + **pyeapi_kwargs + ) @proxy_napalm_wrap -def nxos_api_rpc(commands, - method='cli', - **kwargs): - ''' +def nxos_api_rpc(commands, method="cli", **kwargs): + """ .. versionadded:: 2019.2.0 Execute an arbitrary RPC request via the Nexus API. @@ -1200,20 +1193,22 @@ def nxos_api_rpc(commands, .. code-block:: bash salt '*' napalm.nxos_api_rpc 'show version' - ''' + """ nxos_api_kwargs = pyeapi_nxos_api_args(**kwargs) - return __salt__['nxos_api.rpc'](commands, method=method, **nxos_api_kwargs) + return __salt__["nxos_api.rpc"](commands, method=method, **nxos_api_kwargs) @proxy_napalm_wrap -def nxos_api_config(commands=None, - config_file=None, - template_engine='jinja', - context=None, - defaults=None, - saltenv='base', - **kwargs): - ''' +def nxos_api_config( + commands=None, + config_file=None, + template_engine="jinja", + context=None, + defaults=None, + saltenv="base", + **kwargs +): + """ .. versionadded:: 2019.2.0 Configures the Nexus switch with the specified commands, via the NX-API. @@ -1258,20 +1253,22 @@ def nxos_api_config(commands=None, salt '*' napalm.nxos_api_config 'spanning-tree mode mstp' salt '*' napalm.nxos_api_config config_file=https://bit.ly/2LGLcDy context="{'servers': ['1.2.3.4']}" - ''' + """ nxos_api_kwargs = pyeapi_nxos_api_args(**kwargs) - return __salt__['nxos_api.config'](commands=commands, - config_file=config_file, - template_engine=template_engine, - context=context, - defaults=defaults, - saltenv=saltenv, - **nxos_api_kwargs) + return __salt__["nxos_api.config"]( + commands=commands, + config_file=config_file, + template_engine=template_engine, + context=context, + defaults=defaults, + saltenv=saltenv, + **nxos_api_kwargs + ) @proxy_napalm_wrap def nxos_api_show(commands, raw_text=True, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Execute one or more show (non-configuration) commands. @@ -1288,16 +1285,14 @@ def nxos_api_show(commands, raw_text=True, **kwargs): salt '*' napalm.nxos_api_show 'show version' salt '*' napalm.nxos_api_show 'show bgp sessions' 'show processes' raw_text=False - ''' + """ nxos_api_kwargs = pyeapi_nxos_api_args(**kwargs) - return __salt__['nxos_api.show'](commands, - raw_text=raw_text, - **nxos_api_kwargs) + return __salt__["nxos_api.show"](commands, raw_text=raw_text, **nxos_api_kwargs) @proxy_napalm_wrap def rpc(command, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 This is a wrapper to execute RPC requests on various network operating @@ -1340,21 +1335,21 @@ def rpc(command, **kwargs): salt '*' napalm.rpc 'show version' salt '*' napalm.rpc get-interfaces - ''' + """ default_map = { - 'junos': 'napalm.junos_rpc', - 'eos': 'napalm.pyeapi_run_commands', - 'nxos': 'napalm.nxos_api_rpc' + "junos": "napalm.junos_rpc", + "eos": "napalm.pyeapi_run_commands", + "nxos": "napalm.nxos_api_rpc", } - napalm_map = __salt__['config.get']('napalm_rpc_map', {}) + napalm_map = __salt__["config.get"]("napalm_rpc_map", {}) napalm_map.update(default_map) - fun = napalm_map.get(__grains__['os'], 'napalm.netmiko_commands') + fun = napalm_map.get(__grains__["os"], "napalm.netmiko_commands") return __salt__[fun](command, **kwargs) @depends(HAS_CISCOCONFPARSE) -def config_find_lines(regex, source='running'): - r''' +def config_find_lines(regex, source="running"): + r""" .. versionadded:: 2019.2.0 Return the configuration lines that match the regular expressions from the @@ -1373,15 +1368,14 @@ def config_find_lines(regex, source='running'): .. code-block:: bash salt '*' napalm.config_find_lines '^interface Ethernet1\d' - ''' - config_txt = __salt__['net.config'](source=source)['out'][source] - return __salt__['ciscoconfparse.find_lines'](config=config_txt, - regex=regex) + """ + config_txt = __salt__["net.config"](source=source)["out"][source] + return __salt__["ciscoconfparse.find_lines"](config=config_txt, regex=regex) @depends(HAS_CISCOCONFPARSE) -def config_lines_w_child(parent_regex, child_regex, source='running'): - r''' +def config_lines_w_child(parent_regex, child_regex, source="running"): + r""" .. versionadded:: 2019.2.0 Return the configuration lines that match the regular expressions from the @@ -1411,16 +1405,16 @@ def config_lines_w_child(parent_regex, child_regex, source='running'): salt '*' napalm.config_lines_w_child '^interface' 'ip address' salt '*' napalm.config_lines_w_child '^interface' 'shutdown' source=candidate - ''' - config_txt = __salt__['net.config'](source=source)['out'][source] - return __salt__['ciscoconfparse.find_lines_w_child'](config=config_txt, - parent_regex=parent_regex, - child_regex=child_regex) + """ + config_txt = __salt__["net.config"](source=source)["out"][source] + return __salt__["ciscoconfparse.find_lines_w_child"]( + config=config_txt, parent_regex=parent_regex, child_regex=child_regex + ) @depends(HAS_CISCOCONFPARSE) -def config_lines_wo_child(parent_regex, child_regex, source='running'): - ''' +def config_lines_wo_child(parent_regex, child_regex, source="running"): + """ .. versionadded:: 2019.2.0 Return the configuration lines that match the regular expressions from the @@ -1451,16 +1445,16 @@ def config_lines_wo_child(parent_regex, child_regex, source='running'): salt '*' napalm.config_lines_wo_child '^interface' 'ip address' salt '*' napalm.config_lines_wo_child '^interface' 'shutdown' source=candidate - ''' - config_txt = __salt__['net.config'](source=source)['out'][source] - return __salt__['ciscoconfparse.find_lines_wo_child'](config=config_txt, - parent_regex=parent_regex, - child_regex=child_regex) + """ + config_txt = __salt__["net.config"](source=source)["out"][source] + return __salt__["ciscoconfparse.find_lines_wo_child"]( + config=config_txt, parent_regex=parent_regex, child_regex=child_regex + ) @depends(HAS_CISCOCONFPARSE) -def config_filter_lines(parent_regex, child_regex, source='running'): - r''' +def config_filter_lines(parent_regex, child_regex, source="running"): + r""" .. versionadded:: 2019.2.0 Return a list of detailed matches, for the configuration blocks (parent-child @@ -1498,15 +1492,15 @@ def config_filter_lines(parent_regex, child_regex, source='running'): salt '*' napalm.config_filter_lines '^interface' 'ip address' salt '*' napalm.config_filter_lines '^interface' 'shutdown' source=candidate - ''' - config_txt = __salt__['net.config'](source=source)['out'][source] - return __salt__['ciscoconfparse.filter_lines'](config=config_txt, - parent_regex=parent_regex, - child_regex=child_regex) + """ + config_txt = __salt__["net.config"](source=source)["out"][source] + return __salt__["ciscoconfparse.filter_lines"]( + config=config_txt, parent_regex=parent_regex, child_regex=child_regex + ) -def config_tree(source='running', with_tags=False): - ''' +def config_tree(source="running", with_tags=False): + """ .. versionadded:: 2019.2.0 Transform Cisco IOS style configuration to structured Python dictionary. @@ -1525,16 +1519,15 @@ def config_tree(source='running', with_tags=False): .. code-block:: bash salt '*' napalm.config_tree - ''' - config_txt = __salt__['net.config'](source=source)['out'][source] - return __salt__['iosconfig.tree'](config=config_txt) + """ + config_txt = __salt__["net.config"](source=source)["out"][source] + return __salt__["iosconfig.tree"](config=config_txt) -def config_merge_tree(source='running', - merge_config=None, - merge_path=None, - saltenv='base'): - ''' +def config_merge_tree( + source="running", merge_config=None, merge_path=None, saltenv="base" +): + """ .. versionadded:: 2019.2.0 Return the merge tree of the ``initial_config`` with the ``merge_config``, @@ -1563,19 +1556,20 @@ def config_merge_tree(source='running', .. code-block:: bash salt '*' napalm.config_merge_tree merge_path=salt://path/to/merge.cfg - ''' - config_txt = __salt__['net.config'](source=source)['out'][source] - return __salt__['iosconfig.merge_tree'](initial_config=config_txt, - merge_config=merge_config, - merge_path=merge_path, - saltenv=saltenv) + """ + config_txt = __salt__["net.config"](source=source)["out"][source] + return __salt__["iosconfig.merge_tree"]( + initial_config=config_txt, + merge_config=merge_config, + merge_path=merge_path, + saltenv=saltenv, + ) -def config_merge_text(source='running', - merge_config=None, - merge_path=None, - saltenv='base'): - ''' +def config_merge_text( + source="running", merge_config=None, merge_path=None, saltenv="base" +): + """ .. versionadded:: 2019.2.0 Return the merge result of the configuration from ``source`` with the @@ -1605,19 +1599,20 @@ def config_merge_text(source='running', .. code-block:: bash salt '*' napalm.config_merge_text merge_path=salt://path/to/merge.cfg - ''' - config_txt = __salt__['net.config'](source=source)['out'][source] - return __salt__['iosconfig.merge_text'](initial_config=config_txt, - merge_config=merge_config, - merge_path=merge_path, - saltenv=saltenv) + """ + config_txt = __salt__["net.config"](source=source)["out"][source] + return __salt__["iosconfig.merge_text"]( + initial_config=config_txt, + merge_config=merge_config, + merge_path=merge_path, + saltenv=saltenv, + ) -def config_merge_diff(source='running', - merge_config=None, - merge_path=None, - saltenv='base'): - ''' +def config_merge_diff( + source="running", merge_config=None, merge_path=None, saltenv="base" +): + """ .. versionadded:: 2019.2.0 Return the merge diff, as text, after merging the merge config into the @@ -1646,19 +1641,20 @@ def config_merge_diff(source='running', .. code-block:: bash salt '*' napalm.config_merge_diff merge_path=salt://path/to/merge.cfg - ''' - config_txt = __salt__['net.config'](source=source)['out'][source] - return __salt__['iosconfig.merge_diff'](initial_config=config_txt, - merge_config=merge_config, - merge_path=merge_path, - saltenv=saltenv) + """ + config_txt = __salt__["net.config"](source=source)["out"][source] + return __salt__["iosconfig.merge_diff"]( + initial_config=config_txt, + merge_config=merge_config, + merge_path=merge_path, + saltenv=saltenv, + ) -def config_diff_tree(source1='candidate', - candidate_path=None, - source2='running', - running_path=None): - ''' +def config_diff_tree( + source1="candidate", candidate_path=None, source2="running", running_path=None +): + """ .. versionadded:: 2019.2.0 Return the diff, as Python dictionary, between two different sources. @@ -1707,21 +1703,22 @@ def config_diff_tree(source1='candidate', salt '*' napalm.config_diff_tree salt '*' napalm.config_diff_tree running startup - ''' - get_config = __salt__['net.config']()['out'] + """ + get_config = __salt__["net.config"]()["out"] candidate_cfg = get_config[source1] running_cfg = get_config[source2] - return __salt__['iosconfig.diff_tree'](candidate_config=candidate_cfg, - candidate_path=candidate_path, - running_config=running_cfg, - running_path=running_path) + return __salt__["iosconfig.diff_tree"]( + candidate_config=candidate_cfg, + candidate_path=candidate_path, + running_config=running_cfg, + running_path=running_path, + ) -def config_diff_text(source1='candidate', - candidate_path=None, - source2='running', - running_path=None): - ''' +def config_diff_text( + source1="candidate", candidate_path=None, source2="running", running_path=None +): + """ .. versionadded:: 2019.2.0 Return the diff, as text, between the two different configuration sources. @@ -1763,23 +1760,23 @@ def config_diff_text(source1='candidate', salt '*' napalm.config_diff_text candidate_path=https://bit.ly/2mAdq7z # Would compare the running config with the configuration available at # https://bit.ly/2mAdq7z - ''' - get_config = __salt__['net.config']()['out'] + """ + get_config = __salt__["net.config"]()["out"] candidate_cfg = get_config[source1] running_cfg = get_config[source2] - return __salt__['iosconfig.diff_text'](candidate_config=candidate_cfg, - candidate_path=candidate_path, - running_config=running_cfg, - running_path=running_path) + return __salt__["iosconfig.diff_text"]( + candidate_config=candidate_cfg, + candidate_path=candidate_path, + running_config=running_cfg, + running_path=running_path, + ) @depends(HAS_SCP) -def scp_get(remote_path, - local_path='', - recursive=False, - preserve_times=False, - **kwargs): - ''' +def scp_get( + remote_path, local_path="", recursive=False, preserve_times=False, **kwargs +): + """ .. versionadded:: 2019.2.0 Transfer files and directories from remote network device to the localhost @@ -1844,25 +1841,29 @@ def scp_get(remote_path, .. code-block:: bash salt '*' napalm.scp_get /var/tmp/file /tmp/file auto_add_policy=True - ''' + """ conn_args = netmiko_args(**kwargs) - conn_args['hostname'] = conn_args['host'] + conn_args["hostname"] = conn_args["host"] kwargs.update(conn_args) - return __salt__['scp.get'](remote_path, - local_path=local_path, - recursive=recursive, - preserve_times=preserve_times, - **kwargs) + return __salt__["scp.get"]( + remote_path, + local_path=local_path, + recursive=recursive, + preserve_times=preserve_times, + **kwargs + ) @depends(HAS_SCP) -def scp_put(files, - remote_path=None, - recursive=False, - preserve_times=False, - saltenv='base', - **kwargs): - ''' +def scp_put( + files, + remote_path=None, + recursive=False, + preserve_times=False, + saltenv="base", + **kwargs +): + """ .. versionadded:: 2019.2.0 Transfer files and directories to remote network device. @@ -1945,13 +1946,15 @@ def scp_put(files, .. code-block:: bash salt '*' napalm.scp_put /path/to/file /var/tmp/file auto_add_policy=True - ''' + """ conn_args = netmiko_args(**kwargs) - conn_args['hostname'] = conn_args['host'] + conn_args["hostname"] = conn_args["host"] kwargs.update(conn_args) - return __salt__['scp.put'](files, - remote_path=remote_path, - recursive=recursive, - preserve_times=preserve_times, - saltenv=saltenv, - **kwargs) + return __salt__["scp.put"]( + files, + remote_path=remote_path, + recursive=recursive, + preserve_times=preserve_times, + saltenv=saltenv, + **kwargs + ) diff --git a/salt/modules/napalm_netacl.py b/salt/modules/napalm_netacl.py index d70d360d73d..06e2adaf2c6 100644 --- a/salt/modules/napalm_netacl.py +++ b/salt/modules/napalm_netacl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM ACL ========== @@ -27,10 +27,15 @@ Please check Installation_ for complete details. .. _NAPALM: https://napalm.readthedocs.io .. _Installation: https://napalm.readthedocs.io/en/latest/installation.html -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals import logging + +# import Salt modules +import salt.utils.napalm +from salt.utils.napalm import proxy_napalm_wrap + log = logging.getLogger(__file__) # Import third party libs @@ -40,21 +45,19 @@ try: import capirca.aclgen import capirca.lib.policy import capirca.lib.aclgenerator + HAS_CAPIRCA = True # pylint: enable=W0611 except ImportError: HAS_CAPIRCA = False -# import Salt modules -import salt.utils.napalm -from salt.utils.napalm import proxy_napalm_wrap # ------------------------------------------------------------------------------ # module properties # ------------------------------------------------------------------------------ -__virtualname__ = 'netacl' -__proxyenabled__ = ['*'] +__virtualname__ = "netacl" +__proxyenabled__ = ["*"] # allow napalm proxy only # ------------------------------------------------------------------------------ @@ -63,14 +66,18 @@ __proxyenabled__ = ['*'] def __virtual__(): - ''' + """ This module requires both NAPALM and Capirca. - ''' + """ if HAS_CAPIRCA and salt.utils.napalm.virtual(__opts__, __virtualname__, __file__): return __virtualname__ else: - return (False, 'The netacl (napalm_acl) module cannot be loaded: \ - Please install capirca and napalm.') + return ( + False, + "The netacl (napalm_acl) module cannot be loaded: \ + Please install capirca and napalm.", + ) + # ------------------------------------------------------------------------------ # helper functions -- will not be exported @@ -78,7 +85,7 @@ def __virtual__(): def _get_capirca_platform(): # pylint: disable=too-many-return-statements - ''' + """ Given the following NAPALM grains, we can determine the Capirca platform name: - vendor @@ -86,51 +93,54 @@ def _get_capirca_platform(): # pylint: disable=too-many-return-statements - operating system Not the most optimal. - ''' - vendor = __grains__['vendor'].lower() - os_ = __grains__['os'].lower() - model = __grains__['model'].lower() - if vendor == 'juniper' and 'srx' in model: - return 'junipersrx' - elif vendor == 'cisco' and os_ == 'ios': - return 'cisco' - elif vendor == 'cisco' and os_ == 'iosxr': - return 'ciscoxr' - elif vendor == 'cisco' and os_ == 'asa': - return 'ciscoasa' - elif os_ == 'linux': - return 'iptables' - elif vendor == 'palo alto networks': - return 'paloaltofw' + """ + vendor = __grains__["vendor"].lower() + os_ = __grains__["os"].lower() + model = __grains__["model"].lower() + if vendor == "juniper" and "srx" in model: + return "junipersrx" + elif vendor == "cisco" and os_ == "ios": + return "cisco" + elif vendor == "cisco" and os_ == "iosxr": + return "ciscoxr" + elif vendor == "cisco" and os_ == "asa": + return "ciscoasa" + elif os_ == "linux": + return "iptables" + elif vendor == "palo alto networks": + return "paloaltofw" # anything else will point to the vendor # i.e.: some of the Capirca platforms are named by the device vendor # e.g.: eOS => arista, junos => juniper, etc. return vendor + # ------------------------------------------------------------------------------ # callable functions # ------------------------------------------------------------------------------ @proxy_napalm_wrap -def load_term_config(filter_name, - term_name, - filter_options=None, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=True, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d', - test=False, - commit=True, - debug=False, - source_service=None, - destination_service=None, - **term_fields): - ''' +def load_term_config( + filter_name, + term_name, + filter_options=None, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=True, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", + test=False, + commit=True, + debug=False, + source_service=None, + destination_service=None, + **term_fields +): + """ Generate and load the configuration of a policy term. filter_name @@ -425,51 +435,59 @@ def load_term_config(filter_name, } result: True - ''' + """ if not filter_options: filter_options = [] platform = _get_capirca_platform() - term_config = __salt__['capirca.get_term_config'](platform, - filter_name, - term_name, - filter_options=filter_options, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar, - revision_id=revision_id, - revision_no=revision_no, - revision_date=revision_date, - revision_date_format=revision_date_format, - source_service=source_service, - destination_service=destination_service, - **term_fields) - return __salt__['net.load_config'](text=term_config, - test=test, - commit=commit, - debug=debug, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + term_config = __salt__["capirca.get_term_config"]( + platform, + filter_name, + term_name, + filter_options=filter_options, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + revision_id=revision_id, + revision_no=revision_no, + revision_date=revision_date, + revision_date_format=revision_date_format, + source_service=source_service, + destination_service=destination_service, + **term_fields + ) + # pylint: disable=undefined-variable + return __salt__["net.load_config"]( + text=term_config, + test=test, + commit=commit, + debug=debug, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable @proxy_napalm_wrap -def load_filter_config(filter_name, - filter_options=None, - terms=None, - prepend=True, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=True, - only_lower_merge=False, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d', - test=False, - commit=True, - debug=False, - **kwargs): # pylint: disable=unused-argument - ''' +def load_filter_config( + filter_name, + filter_options=None, + terms=None, + prepend=True, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=True, + only_lower_merge=False, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", + test=False, + commit=True, + debug=False, + **kwargs +): # pylint: disable=unused-argument + """ Generate and load the configuration of a policy filter. .. note:: @@ -638,50 +656,58 @@ def load_filter_config(filter_name, - 5680 protocol: tcp action: accept - ''' + """ if not filter_options: filter_options = [] if not terms: terms = [] platform = _get_capirca_platform() - filter_config = __salt__['capirca.get_filter_config'](platform, - filter_name, - terms=terms, - prepend=prepend, - filter_options=filter_options, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar, - only_lower_merge=only_lower_merge, - revision_id=revision_id, - revision_no=revision_no, - revision_date=revision_date, - revision_date_format=revision_date_format) - return __salt__['net.load_config'](text=filter_config, - test=test, - commit=commit, - debug=debug, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + filter_config = __salt__["capirca.get_filter_config"]( + platform, + filter_name, + terms=terms, + prepend=prepend, + filter_options=filter_options, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + only_lower_merge=only_lower_merge, + revision_id=revision_id, + revision_no=revision_no, + revision_date=revision_date, + revision_date_format=revision_date_format, + ) + # pylint: disable=undefined-variable + return __salt__["net.load_config"]( + text=filter_config, + test=test, + commit=commit, + debug=debug, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable @proxy_napalm_wrap -def load_policy_config(filters=None, - prepend=True, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=True, - only_lower_merge=False, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d', - test=False, - commit=True, - debug=False, - **kwargs): # pylint: disable=unused-argument - ''' +def load_policy_config( + filters=None, + prepend=True, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=True, + only_lower_merge=False, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", + test=False, + commit=True, + debug=False, + **kwargs +): # pylint: disable=unused-argument + """ Generate and load the configuration of the whole policy. .. note:: @@ -832,34 +858,37 @@ def load_policy_config(filters=None, protocol: - icmp action: reject - ''' + """ if not filters: filters = [] platform = _get_capirca_platform() - policy_config = __salt__['capirca.get_policy_config'](platform, - filters=filters, - prepend=prepend, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar, - only_lower_merge=only_lower_merge, - revision_id=revision_id, - revision_no=revision_no, - revision_date=revision_date, - revision_date_format=revision_date_format) - return __salt__['net.load_config'](text=policy_config, - test=test, - commit=commit, - debug=debug, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + policy_config = __salt__["capirca.get_policy_config"]( + platform, + filters=filters, + prepend=prepend, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + only_lower_merge=only_lower_merge, + revision_id=revision_id, + revision_no=revision_no, + revision_date=revision_date, + revision_date_format=revision_date_format, + ) + # pylint: disable=undefined-variable + return __salt__["net.load_config"]( + text=policy_config, + test=test, + commit=commit, + debug=debug, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable -def get_filter_pillar(filter_name, - pillar_key='acl', - pillarenv=None, - saltenv=None): - ''' +def get_filter_pillar(filter_name, pillar_key="acl", pillarenv=None, saltenv=None): + """ Helper that can be used inside a state SLS, in order to get the filter configuration given its name. @@ -876,19 +905,16 @@ def get_filter_pillar(filter_name, saltenv Included only for compatibility with :conf_minion:`pillarenv_from_saltenv`, and is otherwise ignored. - ''' - return __salt__['capirca.get_filter_pillar'](filter_name, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv) + """ + return __salt__["capirca.get_filter_pillar"]( + filter_name, pillar_key=pillar_key, pillarenv=pillarenv, saltenv=saltenv + ) -def get_term_pillar(filter_name, - term_name, - pillar_key='acl', - pillarenv=None, - saltenv=None): - ''' +def get_term_pillar( + filter_name, term_name, pillar_key="acl", pillarenv=None, saltenv=None +): + """ Helper that can be used inside a state SLS, in order to get the term configuration given its name, under a certain filter uniquely identified by its name. @@ -909,9 +935,11 @@ def get_term_pillar(filter_name, saltenv Included only for compatibility with :conf_minion:`pillarenv_from_saltenv`, and is otherwise ignored. - ''' - return __salt__['capirca.get_term_pillar'](filter_name, - term_name, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv) + """ + return __salt__["capirca.get_term_pillar"]( + filter_name, + term_name, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + ) diff --git a/salt/modules/napalm_network.py b/salt/modules/napalm_network.py index ef15044bb03..2d6305ee1d5 100644 --- a/salt/modules/napalm_network.py +++ b/salt/modules/napalm_network.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM Network ============== @@ -17,27 +17,30 @@ Dependencies .. versionadded:: 2016.11.0 .. versionchanged:: 2017.7.0 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -import time -import logging import datetime +import logging +import time -log = logging.getLogger(__name__) +import salt.utils.files +import salt.utils.napalm +import salt.utils.templates +import salt.utils.versions # Import Salt libs from salt.ext import six -import salt.utils.files -import salt.utils.napalm -import salt.utils.versions -import salt.utils.templates + +log = logging.getLogger(__name__) + # Import 3rd-party libs try: import jxmlease # pylint: disable=unused-import + HAS_JXMLEASE = True except ImportError: HAS_JXMLEASE = False @@ -46,9 +49,9 @@ except ImportError: # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'net' -__proxyenabled__ = ['*'] -__virtual_aliases__ = ('napalm_net',) +__virtualname__ = "net" +__proxyenabled__ = ["*"] +__virtual_aliases__ = ("napalm_net",) # uses NAPALM-based proxy to interact with network devices # ---------------------------------------------------------------------------------------------------------------------- @@ -57,11 +60,12 @@ __virtual_aliases__ = ('napalm_net',) def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -69,14 +73,14 @@ def __virtual__(): def _filter_list(input_list, search_key, search_value): - ''' + """ Filters a list of dictionary by a set of key-value pair. :param input_list: is a list of dictionaries :param search_key: is the key we are looking for :param search_value: is the value we are looking for the key specified in search_key :return: filered list of dictionaries - ''' + """ output_list = list() @@ -89,14 +93,14 @@ def _filter_list(input_list, search_key, search_value): def _filter_dict(input_dict, search_key, search_value): - ''' + """ Filters a dictionary of dictionaries by a key-value pair. :param input_dict: is a dictionary whose values are lists of dictionaries :param search_key: is the key in the leaf dictionaries :param search_values: is the value in the leaf dictionaries :return: filtered dictionary - ''' + """ output_dict = dict() @@ -109,71 +113,80 @@ def _filter_dict(input_dict, search_key, search_value): def _safe_commit_config(loaded_result, napalm_device): - _commit = commit(inherit_napalm_device=napalm_device) # calls the function commit, defined below - if not _commit.get('result', False): + _commit = commit( + inherit_napalm_device=napalm_device + ) # calls the function commit, defined below + if not _commit.get("result", False): # if unable to commit - loaded_result['comment'] += _commit['comment'] if _commit.get('comment') else 'Unable to commit.' - loaded_result['result'] = False + loaded_result["comment"] += ( + _commit["comment"] if _commit.get("comment") else "Unable to commit." + ) + loaded_result["result"] = False # unable to commit, something went wrong discarded = _safe_dicard_config(loaded_result, napalm_device) - if not discarded['result']: + if not discarded["result"]: return loaded_result return _commit def _safe_dicard_config(loaded_result, napalm_device): - ''' - ''' - log.debug('Discarding the config') + """ + """ + log.debug("Discarding the config") log.debug(loaded_result) _discarded = discard_config(inherit_napalm_device=napalm_device) - if not _discarded.get('result', False): - loaded_result['comment'] += _discarded['comment'] if _discarded.get('comment') \ - else 'Unable to discard config.' - loaded_result['result'] = False + if not _discarded.get("result", False): + loaded_result["comment"] += ( + _discarded["comment"] + if _discarded.get("comment") + else "Unable to discard config." + ) + loaded_result["result"] = False # make sure it notifies # that something went wrong _explicit_close(napalm_device) - __context__['retcode'] = 1 + __context__["retcode"] = 1 return loaded_result return _discarded def _explicit_close(napalm_device): - ''' + """ Will explicily close the config session with the network device, when running in a now-always-alive proxy minion or regular minion. This helper must be used in configuration-related functions, as the session is preserved and not closed before making any changes. - ''' + """ if salt.utils.napalm.not_always_alive(__opts__): # force closing the configuration session # when running in a non-always-alive proxy # or regular minion try: - napalm_device['DRIVER'].close() + napalm_device["DRIVER"].close() except Exception as err: # pylint: disable=broad-except - log.error('Unable to close the temp connection with the device:') + log.error("Unable to close the temp connection with the device:") log.error(err) - log.error('Please report.') + log.error("Please report.") -def _config_logic(napalm_device, - loaded_result, - test=False, - debug=False, - replace=False, - commit_config=True, - loaded_config=None, - commit_in=None, - commit_at=None, - revert_in=None, - revert_at=None, - commit_jid=None, - **kwargs): - ''' +def _config_logic( + napalm_device, + loaded_result, + test=False, + debug=False, + replace=False, + commit_config=True, + loaded_config=None, + commit_in=None, + commit_at=None, + revert_in=None, + revert_at=None, + commit_jid=None, + **kwargs +): + """ Builds the config logic for `load_config` and `load_template` functions. - ''' + """ # As the Salt logic is built around independent events # when it comes to configuration changes in the # candidate DB on the network devices, we need to @@ -188,162 +201,186 @@ def _config_logic(napalm_device, # `napalm_device` will be overridden. # See `salt.utils.napalm.proxy_napalm_wrap` decorator. - current_jid = kwargs.get('__pub_jid') + current_jid = kwargs.get("__pub_jid") if not current_jid: - current_jid = '{0:%Y%m%d%H%M%S%f}'.format(datetime.datetime.now()) + current_jid = "{0:%Y%m%d%H%M%S%f}".format(datetime.datetime.now()) - loaded_result['already_configured'] = False + loaded_result["already_configured"] = False - loaded_result['loaded_config'] = '' + loaded_result["loaded_config"] = "" if debug: - loaded_result['loaded_config'] = loaded_config + loaded_result["loaded_config"] = loaded_config _compare = compare_config(inherit_napalm_device=napalm_device) - if _compare.get('result', False): - loaded_result['diff'] = _compare.get('out') - loaded_result.pop('out', '') # not needed + if _compare.get("result", False): + loaded_result["diff"] = _compare.get("out") + loaded_result.pop("out", "") # not needed else: - loaded_result['diff'] = None - loaded_result['result'] = False - loaded_result['comment'] = _compare.get('comment') - __context__['retcode'] = 1 + loaded_result["diff"] = None + loaded_result["result"] = False + loaded_result["comment"] = _compare.get("comment") + __context__["retcode"] = 1 return loaded_result - _loaded_res = loaded_result.get('result', False) + _loaded_res = loaded_result.get("result", False) if not _loaded_res or test: # if unable to load the config (errors / warnings) # or in testing mode, # will discard the config - if loaded_result['comment']: - loaded_result['comment'] += '\n' - if not len(loaded_result.get('diff', '')) > 0: - loaded_result['already_configured'] = True + if loaded_result["comment"]: + loaded_result["comment"] += "\n" + if not len(loaded_result.get("diff", "")) > 0: + loaded_result["already_configured"] = True discarded = _safe_dicard_config(loaded_result, napalm_device) - if not discarded['result']: + if not discarded["result"]: return loaded_result - loaded_result['comment'] += 'Configuration discarded.' + loaded_result["comment"] += "Configuration discarded." # loaded_result['result'] = False not necessary # as the result can be true when test=True _explicit_close(napalm_device) - if not loaded_result['result']: - __context__['retcode'] = 1 + if not loaded_result["result"]: + __context__["retcode"] = 1 return loaded_result if not test and commit_config: # if not in testing mode and trying to commit if commit_jid: - log.info('Committing the JID: %s', str(commit_jid)) + log.info("Committing the JID: %s", str(commit_jid)) removed = cancel_commit(commit_jid) - log.debug('Cleaned up the commit from the schedule') - log.debug(removed['comment']) - if len(loaded_result.get('diff', '')) > 0: + log.debug("Cleaned up the commit from the schedule") + log.debug(removed["comment"]) + if len(loaded_result.get("diff", "")) > 0: # if not testing mode # and also the user wants to commit (default) # and there are changes to commit if commit_in or commit_at: - commit_time = __utils__['timeutil.get_time_at'](time_in=commit_in, - time_at=commit_in) + commit_time = __utils__["timeutil.get_time_at"]( + time_in=commit_in, time_at=commit_in + ) # schedule job - scheduled_job_name = '__napalm_commit_{}'.format(current_jid) + scheduled_job_name = "__napalm_commit_{}".format(current_jid) temp_file = salt.utils.files.mkstemp() - with salt.utils.files.fopen(temp_file, 'w') as fp_: + with salt.utils.files.fopen(temp_file, "w") as fp_: fp_.write(loaded_config) - scheduled = __salt__['schedule.add'](scheduled_job_name, - function='net.load_config', - job_kwargs={ - 'filename': temp_file, - 'commit_jid': current_jid, - 'replace': replace - }, - once=commit_time) - log.debug('Scheduling job') + scheduled = __salt__["schedule.add"]( + scheduled_job_name, + function="net.load_config", + job_kwargs={ + "filename": temp_file, + "commit_jid": current_jid, + "replace": replace, + }, + once=commit_time, + ) + log.debug("Scheduling job") log.debug(scheduled) - saved = __salt__['schedule.save']() # ensure the schedule is + saved = __salt__["schedule.save"]() # ensure the schedule is # persistent cross Minion restart discarded = _safe_dicard_config(loaded_result, napalm_device) # discard the changes - if not discarded['result']: - discarded['comment'] += ('Scheduled the job to be executed at {schedule_ts}, ' - 'but was unable to discard the config: \n').format(schedule_ts=commit_time) + if not discarded["result"]: + discarded["comment"] += ( + "Scheduled the job to be executed at {schedule_ts}, " + "but was unable to discard the config: \n" + ).format(schedule_ts=commit_time) return discarded - loaded_result['comment'] = ('Changes discarded for now, and scheduled commit at: {schedule_ts}.\n' - 'The commit ID is: {current_jid}.\n' - 'To discard this commit, you can execute: \n\n' - 'salt {min_id} net.cancel_commit {current_jid}').format(schedule_ts=commit_time, - min_id=__opts__['id'], - current_jid=current_jid) - loaded_result['commit_id'] = current_jid + loaded_result["comment"] = ( + "Changes discarded for now, and scheduled commit at: {schedule_ts}.\n" + "The commit ID is: {current_jid}.\n" + "To discard this commit, you can execute: \n\n" + "salt {min_id} net.cancel_commit {current_jid}" + ).format( + schedule_ts=commit_time, + min_id=__opts__["id"], + current_jid=current_jid, + ) + loaded_result["commit_id"] = current_jid return loaded_result - log.debug('About to commit:') - log.debug(loaded_result['diff']) + log.debug("About to commit:") + log.debug(loaded_result["diff"]) if revert_in or revert_at: - revert_time = __utils__['timeutil.get_time_at'](time_in=revert_in, - time_at=revert_at) - if __grains__['os'] == 'junos': + revert_time = __utils__["timeutil.get_time_at"]( + time_in=revert_in, time_at=revert_at + ) + if __grains__["os"] == "junos": if not HAS_JXMLEASE: - loaded_result['comment'] = ('This feature requires the library jxmlease to be installed.\n' - 'To install, please execute: ``pip install jxmlease``.') - loaded_result['result'] = False + loaded_result["comment"] = ( + "This feature requires the library jxmlease to be installed.\n" + "To install, please execute: ``pip install jxmlease``." + ) + loaded_result["result"] = False return loaded_result - timestamp_at = __utils__['timeutil.get_timestamp_at'](time_in=revert_in, - time_at=revert_at) - minutes = int((timestamp_at - time.time())/60) - _comm = __salt__['napalm.junos_commit'](confirm=minutes) - if not _comm['out']: + timestamp_at = __utils__["timeutil.get_timestamp_at"]( + time_in=revert_in, time_at=revert_at + ) + minutes = int((timestamp_at - time.time()) / 60) + _comm = __salt__["napalm.junos_commit"](confirm=minutes) + if not _comm["out"]: # If unable to commit confirm, should try to bail out - loaded_result['comment'] = 'Unable to commit confirm: {}'.format(_comm['message']) - loaded_result['result'] = False + loaded_result[ + "comment" + ] = "Unable to commit confirm: {}".format(_comm["message"]) + loaded_result["result"] = False # But before exiting, we must gracefully discard the config discarded = _safe_dicard_config(loaded_result, napalm_device) - if not discarded['result']: + if not discarded["result"]: return loaded_result else: temp_file = salt.utils.files.mkstemp() - running_config = __salt__['net.config'](source='running')['out']['running'] - with salt.utils.files.fopen(temp_file, 'w') as fp_: + running_config = __salt__["net.config"](source="running")["out"][ + "running" + ] + with salt.utils.files.fopen(temp_file, "w") as fp_: fp_.write(running_config) committed = _safe_commit_config(loaded_result, napalm_device) - if not committed['result']: + if not committed["result"]: # If unable to commit, dicard the config (which is # already done by the _safe_commit_config function), and # return with the command and other details. return loaded_result - scheduled_job_name = '__napalm_commit_{}'.format(current_jid) - scheduled = __salt__['schedule.add'](scheduled_job_name, - function='net.load_config', - job_kwargs={ - 'filename': temp_file, - 'commit_jid': current_jid, - 'replace': True - }, - once=revert_time) - log.debug('Scheduling commit confirmed') + scheduled_job_name = "__napalm_commit_{}".format(current_jid) + scheduled = __salt__["schedule.add"]( + scheduled_job_name, + function="net.load_config", + job_kwargs={ + "filename": temp_file, + "commit_jid": current_jid, + "replace": True, + }, + once=revert_time, + ) + log.debug("Scheduling commit confirmed") log.debug(scheduled) - saved = __salt__['schedule.save']() - loaded_result['comment'] = ('The commit ID is: {current_jid}.\n' - 'This commit will be reverted at: {schedule_ts}, unless confirmed.\n' - 'To confirm the commit and avoid reverting, you can execute:\n\n' - 'salt {min_id} net.confirm_commit {current_jid}').format(schedule_ts=revert_time, - min_id=__opts__['id'], - current_jid=current_jid) - loaded_result['commit_id'] = current_jid + saved = __salt__["schedule.save"]() + loaded_result["comment"] = ( + "The commit ID is: {current_jid}.\n" + "This commit will be reverted at: {schedule_ts}, unless confirmed.\n" + "To confirm the commit and avoid reverting, you can execute:\n\n" + "salt {min_id} net.confirm_commit {current_jid}" + ).format( + schedule_ts=revert_time, + min_id=__opts__["id"], + current_jid=current_jid, + ) + loaded_result["commit_id"] = current_jid return loaded_result committed = _safe_commit_config(loaded_result, napalm_device) - if not committed['result']: + if not committed["result"]: return loaded_result else: # would like to commit, but there's no change # need to call discard_config() to release the config DB discarded = _safe_dicard_config(loaded_result, napalm_device) - if not discarded['result']: + if not discarded["result"]: return loaded_result - loaded_result['already_configured'] = True - loaded_result['comment'] = 'Already configured.' + loaded_result["already_configured"] = True + loaded_result["comment"] = "Already configured." _explicit_close(napalm_device) - if not loaded_result['result']: - __context__['retcode'] = 1 + if not loaded_result["result"]: + __context__["retcode"] = 1 return loaded_result + # ---------------------------------------------------------------------------------------------------------------------- # callable functions # ---------------------------------------------------------------------------------------------------------------------- @@ -351,7 +388,7 @@ def _config_logic(napalm_device, @salt.utils.napalm.proxy_napalm_wrap def connected(**kwargs): # pylint: disable=unused-argument - ''' + """ Specifies if the connection to the device succeeded. CLI Example: @@ -359,16 +396,14 @@ def connected(**kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' net.connected - ''' + """ - return { - 'out': napalm_device.get('UP', False) # pylint: disable=undefined-variable - } + return {"out": napalm_device.get("UP", False)} # pylint: disable=undefined-variable @salt.utils.napalm.proxy_napalm_wrap def facts(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns characteristics of the network device. :return: a dictionary with the following keys: @@ -411,19 +446,16 @@ def facts(**kwargs): # pylint: disable=unused-argument 'hostname': 're0.edge05.syd01', 'fqdn': 're0.edge05.syd01' } - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_facts', - **{ - } + napalm_device, "get_facts", **{} # pylint: disable=undefined-variable ) @salt.utils.napalm.proxy_napalm_wrap def environment(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns the environment of the device. CLI Example: @@ -478,19 +510,16 @@ def environment(**kwargs): # pylint: disable=unused-argument } } } - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_environment', - **{ - } + napalm_device, "get_environment", **{} # pylint: disable=undefined-variable ) @salt.utils.napalm.proxy_napalm_wrap def cli(*commands, **kwargs): # pylint: disable=unused-argument - ''' + """ Returns a dictionary with the raw output of all commands passed as arguments. commands @@ -668,125 +697,156 @@ def cli(*commands, **kwargs): # pylint: disable=unused-argument ] } } - ''' + """ raw_cli_outputs = salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'cli', - **{ - 'commands': list(commands) - } + "cli", + **{"commands": list(commands)} ) # thus we can display the output as is # in case of errors, they'll be catched in the proxy - if not raw_cli_outputs['result']: + if not raw_cli_outputs["result"]: # Error -> dispaly the output as-is. return raw_cli_outputs - textfsm_parse = kwargs.get('textfsm_parse') or __opts__.get('napalm_cli_textfsm_parse') or\ - __pillar__.get('napalm_cli_textfsm_parse', False) + textfsm_parse = ( + kwargs.get("textfsm_parse") + or __opts__.get("napalm_cli_textfsm_parse") + or __pillar__.get("napalm_cli_textfsm_parse", False) + ) if not textfsm_parse: # No TextFSM parsing required, return raw commands. - log.debug('No TextFSM parsing requested.') + log.debug("No TextFSM parsing requested.") return raw_cli_outputs - if 'textfsm.extract' not in __salt__ or 'textfsm.index' not in __salt__: - raw_cli_outputs['comment'] += 'Unable to process: is TextFSM installed?' - log.error(raw_cli_outputs['comment']) + if "textfsm.extract" not in __salt__ or "textfsm.index" not in __salt__: + raw_cli_outputs["comment"] += "Unable to process: is TextFSM installed?" + log.error(raw_cli_outputs["comment"]) return raw_cli_outputs - textfsm_template = kwargs.get('textfsm_template') - log.debug('textfsm_template: %s', textfsm_template) - textfsm_path = kwargs.get('textfsm_path') or __opts__.get('textfsm_path') or\ - __pillar__.get('textfsm_path') - log.debug('textfsm_path: %s', textfsm_path) - textfsm_template_dict = kwargs.get('textfsm_template_dict') or __opts__.get('napalm_cli_textfsm_template_dict') or\ - __pillar__.get('napalm_cli_textfsm_template_dict', {}) - log.debug('TextFSM command-template mapping: %s', textfsm_template_dict) - index_file = kwargs.get('index_file') or __opts__.get('textfsm_index_file') or\ - __pillar__.get('textfsm_index_file') - log.debug('index_file: %s', index_file) - platform_grain_name = kwargs.get('platform_grain_name') or __opts__.get('textfsm_platform_grain') or\ - __pillar__.get('textfsm_platform_grain', 'os') - log.debug('platform_grain_name: %s', platform_grain_name) - platform_column_name = kwargs.get('platform_column_name') or __opts__.get('textfsm_platform_column_name') or\ - __pillar__.get('textfsm_platform_column_name', 'Platform') - log.debug('platform_column_name: %s', platform_column_name) - saltenv = kwargs.get('saltenv', 'base') - include_empty = kwargs.get('include_empty', False) - include_pat = kwargs.get('include_pat') - exclude_pat = kwargs.get('exclude_pat') + textfsm_template = kwargs.get("textfsm_template") + log.debug("textfsm_template: %s", textfsm_template) + textfsm_path = ( + kwargs.get("textfsm_path") + or __opts__.get("textfsm_path") + or __pillar__.get("textfsm_path") + ) + log.debug("textfsm_path: %s", textfsm_path) + textfsm_template_dict = ( + kwargs.get("textfsm_template_dict") + or __opts__.get("napalm_cli_textfsm_template_dict") + or __pillar__.get("napalm_cli_textfsm_template_dict", {}) + ) + log.debug("TextFSM command-template mapping: %s", textfsm_template_dict) + index_file = ( + kwargs.get("index_file") + or __opts__.get("textfsm_index_file") + or __pillar__.get("textfsm_index_file") + ) + log.debug("index_file: %s", index_file) + platform_grain_name = ( + kwargs.get("platform_grain_name") + or __opts__.get("textfsm_platform_grain") + or __pillar__.get("textfsm_platform_grain", "os") + ) + log.debug("platform_grain_name: %s", platform_grain_name) + platform_column_name = ( + kwargs.get("platform_column_name") + or __opts__.get("textfsm_platform_column_name") + or __pillar__.get("textfsm_platform_column_name", "Platform") + ) + log.debug("platform_column_name: %s", platform_column_name) + saltenv = kwargs.get("saltenv", "base") + include_empty = kwargs.get("include_empty", False) + include_pat = kwargs.get("include_pat") + exclude_pat = kwargs.get("exclude_pat") processed_cli_outputs = { - 'comment': raw_cli_outputs.get('comment', ''), - 'result': raw_cli_outputs['result'], - 'out': {} + "comment": raw_cli_outputs.get("comment", ""), + "result": raw_cli_outputs["result"], + "out": {}, } - log.debug('Starting to analyse the raw outputs') + log.debug("Starting to analyse the raw outputs") for command in list(commands): - command_output = raw_cli_outputs['out'][command] - log.debug('Output from command: %s', command) + command_output = raw_cli_outputs["out"][command] + log.debug("Output from command: %s", command) log.debug(command_output) processed_command_output = None if textfsm_path: - log.debug('Using the templates under %s', textfsm_path) - processed_cli_output = __salt__['textfsm.index'](command, - platform_grain_name=platform_grain_name, - platform_column_name=platform_column_name, - output=command_output.strip(), - textfsm_path=textfsm_path, - saltenv=saltenv, - include_empty=include_empty, - include_pat=include_pat, - exclude_pat=exclude_pat) - log.debug('Processed CLI output:') + log.debug("Using the templates under %s", textfsm_path) + processed_cli_output = __salt__["textfsm.index"]( + command, + platform_grain_name=platform_grain_name, + platform_column_name=platform_column_name, + output=command_output.strip(), + textfsm_path=textfsm_path, + saltenv=saltenv, + include_empty=include_empty, + include_pat=include_pat, + exclude_pat=exclude_pat, + ) + log.debug("Processed CLI output:") log.debug(processed_cli_output) - if not processed_cli_output['result']: - log.debug('Apparently this didnt work, returnin the raw output') + if not processed_cli_output["result"]: + log.debug("Apparently this didnt work, returnin the raw output") processed_command_output = command_output - processed_cli_outputs['comment'] += '\nUnable to process the output from {0}: {1}.'.format(command, - processed_cli_output['comment']) - log.error(processed_cli_outputs['comment']) - elif processed_cli_output['out']: - log.debug('All good, %s has a nice output!', command) - processed_command_output = processed_cli_output['out'] + processed_cli_outputs[ + "comment" + ] += "\nUnable to process the output from {0}: {1}.".format( + command, processed_cli_output["comment"] + ) + log.error(processed_cli_outputs["comment"]) + elif processed_cli_output["out"]: + log.debug("All good, %s has a nice output!", command) + processed_command_output = processed_cli_output["out"] else: - comment = '''\nProcessing "{}" didn't fail, but didn't return anything either. Dumping raw.'''.format( - command) - processed_cli_outputs['comment'] += comment + comment = """\nProcessing "{}" didn't fail, but didn't return anything either. Dumping raw.""".format( + command + ) + processed_cli_outputs["comment"] += comment log.error(comment) processed_command_output = command_output elif textfsm_template or command in textfsm_template_dict: if command in textfsm_template_dict: textfsm_template = textfsm_template_dict[command] - log.debug('Using %s to process the command: %s', textfsm_template, command) - processed_cli_output = __salt__['textfsm.extract'](textfsm_template, - raw_text=command_output, - saltenv=saltenv) - log.debug('Processed CLI output:') + log.debug("Using %s to process the command: %s", textfsm_template, command) + processed_cli_output = __salt__["textfsm.extract"]( + textfsm_template, raw_text=command_output, saltenv=saltenv + ) + log.debug("Processed CLI output:") log.debug(processed_cli_output) - if not processed_cli_output['result']: - log.debug('Apparently this didnt work, returning ' - 'the raw output') + if not processed_cli_output["result"]: + log.debug("Apparently this didnt work, returning " "the raw output") processed_command_output = command_output - processed_cli_outputs['comment'] += '\nUnable to process the output from {0}: {1}'.format(command, - processed_cli_output['comment']) - log.error(processed_cli_outputs['comment']) - elif processed_cli_output['out']: - log.debug('All good, %s has a nice output!', command) - processed_command_output = processed_cli_output['out'] + processed_cli_outputs[ + "comment" + ] += "\nUnable to process the output from {0}: {1}".format( + command, processed_cli_output["comment"] + ) + log.error(processed_cli_outputs["comment"]) + elif processed_cli_output["out"]: + log.debug("All good, %s has a nice output!", command) + processed_command_output = processed_cli_output["out"] else: - log.debug('Processing %s didnt fail, but didnt return' - ' anything either. Dumping raw.', command) + log.debug( + "Processing %s didnt fail, but didnt return" + " anything either. Dumping raw.", + command, + ) processed_command_output = command_output else: - log.error('No TextFSM template specified, or no TextFSM path defined') + log.error("No TextFSM template specified, or no TextFSM path defined") processed_command_output = command_output - processed_cli_outputs['comment'] += '\nUnable to process the output from {}.'.format(command) - processed_cli_outputs['out'][command] = processed_command_output - processed_cli_outputs['comment'] = processed_cli_outputs['comment'].strip() + processed_cli_outputs[ + "comment" + ] += "\nUnable to process the output from {}.".format(command) + processed_cli_outputs["out"][command] = processed_command_output + processed_cli_outputs["comment"] = processed_cli_outputs["comment"].strip() return processed_cli_outputs @salt.utils.napalm.proxy_napalm_wrap -def traceroute(destination, source=None, ttl=None, timeout=None, vrf=None, **kwargs): # pylint: disable=unused-argument +def traceroute( + destination, source=None, ttl=None, timeout=None, vrf=None, **kwargs +): # pylint: disable=unused-argument - ''' + """ Calls the method traceroute from the NAPALM driver object and returns a dictionary with the result of the traceroute command executed on the device. @@ -813,25 +873,34 @@ def traceroute(destination, source=None, ttl=None, timeout=None, vrf=None, **kwa salt '*' net.traceroute 8.8.8.8 salt '*' net.traceroute 8.8.8.8 source=127.0.0.1 ttl=5 timeout=1 - ''' + """ return salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'traceroute', + "traceroute", **{ - 'destination': destination, - 'source': source, - 'ttl': ttl, - 'timeout': timeout, - 'vrf': vrf + "destination": destination, + "source": source, + "ttl": ttl, + "timeout": timeout, + "vrf": vrf, } ) @salt.utils.napalm.proxy_napalm_wrap -def ping(destination, source=None, ttl=None, timeout=None, size=None, count=None, vrf=None, **kwargs): # pylint: disable=unused-argument +def ping( + destination, + source=None, + ttl=None, + timeout=None, + size=None, + count=None, + vrf=None, + **kwargs +): # pylint: disable=unused-argument - ''' + """ Executes a ping on the network device and returns a dictionary as a result. destination @@ -864,27 +933,29 @@ def ping(destination, source=None, ttl=None, timeout=None, size=None, count=None salt '*' net.ping 8.8.8.8 salt '*' net.ping 8.8.8.8 ttl=3 size=65468 salt '*' net.ping 8.8.8.8 source=127.0.0.1 timeout=1 count=100 - ''' + """ return salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'ping', + "ping", **{ - 'destination': destination, - 'source': source, - 'ttl': ttl, - 'timeout': timeout, - 'size': size, - 'count': count, - 'vrf': vrf + "destination": destination, + "source": source, + "ttl": ttl, + "timeout": timeout, + "size": size, + "count": count, + "vrf": vrf, } ) @salt.utils.napalm.proxy_napalm_wrap -def arp(interface='', ipaddr='', macaddr='', **kwargs): # pylint: disable=unused-argument +def arp( + interface="", ipaddr="", macaddr="", **kwargs +): # pylint: disable=unused-argument - ''' + """ NAPALM returns a list of dictionaries with details of the ARP entries. :param interface: interface name to filter on @@ -917,32 +988,27 @@ def arp(interface='', ipaddr='', macaddr='', **kwargs): # pylint: disable=unuse 'age' : 1435641582.49 } ] - ''' + """ proxy_output = salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_arp_table', - **{ - } + napalm_device, "get_arp_table", **{} # pylint: disable=undefined-variable ) - if not proxy_output.get('result'): + if not proxy_output.get("result"): return proxy_output - arp_table = proxy_output.get('out') + arp_table = proxy_output.get("out") if interface: - arp_table = _filter_list(arp_table, 'interface', interface) + arp_table = _filter_list(arp_table, "interface", interface) if ipaddr: - arp_table = _filter_list(arp_table, 'ip', ipaddr) + arp_table = _filter_list(arp_table, "ip", ipaddr) if macaddr: - arp_table = _filter_list(arp_table, 'mac', macaddr) + arp_table = _filter_list(arp_table, "mac", macaddr) - proxy_output.update({ - 'out': arp_table - }) + proxy_output.update({"out": arp_table}) return proxy_output @@ -950,7 +1016,7 @@ def arp(interface='', ipaddr='', macaddr='', **kwargs): # pylint: disable=unuse @salt.utils.napalm.proxy_napalm_wrap def ipaddrs(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns IP addresses configured on the device. :return: A dictionary with the IPv4 and IPv6 addresses of the interfaces. @@ -997,20 +1063,17 @@ def ipaddrs(**kwargs): # pylint: disable=unused-argument } } } - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_interfaces_ip', - **{ - } + napalm_device, "get_interfaces_ip", **{} # pylint: disable=undefined-variable ) @salt.utils.napalm.proxy_napalm_wrap def interfaces(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns details of the interfaces on the device. :return: Returns a dictionary of dictionaries. The keys for the first @@ -1044,20 +1107,17 @@ def interfaces(**kwargs): # pylint: disable=unused-argument 'mac_address': 'beef:dead:beef', } } - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_interfaces', - **{ - } + napalm_device, "get_interfaces", **{} # pylint: disable=undefined-variable ) @salt.utils.napalm.proxy_napalm_wrap -def lldp(interface='', **kwargs): # pylint: disable=unused-argument +def lldp(interface="", **kwargs): # pylint: disable=unused-argument - ''' + """ Returns a detailed view of the LLDP neighbors. :param interface: interface name to filter on @@ -1093,34 +1153,31 @@ def lldp(interface='', **kwargs): # pylint: disable=unused-argument } ] } - ''' + """ proxy_output = salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'get_lldp_neighbors_detail', - **{ - } + "get_lldp_neighbors_detail", + **{} ) - if not proxy_output.get('result'): + if not proxy_output.get("result"): return proxy_output - lldp_neighbors = proxy_output.get('out') + lldp_neighbors = proxy_output.get("out") if interface: lldp_neighbors = {interface: lldp_neighbors.get(interface)} - proxy_output.update({ - 'out': lldp_neighbors - }) + proxy_output.update({"out": lldp_neighbors}) return proxy_output @salt.utils.napalm.proxy_napalm_wrap -def mac(address='', interface='', vlan=0, **kwargs): # pylint: disable=unused-argument +def mac(address="", interface="", vlan=0, **kwargs): # pylint: disable=unused-argument - ''' + """ Returns the MAC Address Table on the device. :param address: MAC address to filter on @@ -1159,40 +1216,37 @@ def mac(address='', interface='', vlan=0, **kwargs): # pylint: disable=unused-a 'last_move' : 1453191948.11 } ] - ''' + """ proxy_output = salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'get_mac_address_table', - **{ - } + "get_mac_address_table", + **{} ) - if not proxy_output.get('result'): + if not proxy_output.get("result"): # if negative, leave the output unchanged return proxy_output - mac_address_table = proxy_output.get('out') + mac_address_table = proxy_output.get("out") if vlan and isinstance(vlan, int): - mac_address_table = _filter_list(mac_address_table, 'vlan', vlan) + mac_address_table = _filter_list(mac_address_table, "vlan", vlan) if address: - mac_address_table = _filter_list(mac_address_table, 'mac', address) + mac_address_table = _filter_list(mac_address_table, "mac", address) if interface: - mac_address_table = _filter_list(mac_address_table, 'interface', interface) + mac_address_table = _filter_list(mac_address_table, "interface", interface) - proxy_output.update({ - 'out': mac_address_table - }) + proxy_output.update({"out": mac_address_table}) return proxy_output @salt.utils.napalm.proxy_napalm_wrap def config(source=None, **kwargs): # pylint: disable=unused-argument - ''' + """ .. versionadded:: 2017.7.0 Return the whole configuration of the network device. By default, it will @@ -1232,19 +1286,17 @@ def config(source=None, **kwargs): # pylint: disable=unused-argument salt '*' net.config salt '*' net.config source=candidate - ''' + """ return salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'get_config', - **{ - 'retrieve': source - } + "get_config", + **{"retrieve": source} ) @salt.utils.napalm.proxy_napalm_wrap def optics(**kwargs): # pylint: disable=unused-argument - ''' + """ .. versionadded:: 2017.7.0 Fetches the power usage on the various transceivers installed @@ -1279,35 +1331,35 @@ def optics(**kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' net.optics - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_optics', - **{ - } + napalm_device, "get_optics", **{} # pylint: disable=undefined-variable ) + # <---- Call NAPALM getters -------------------------------------------------------------------------------------------- # ----- Configuration specific functions ------------------------------------------------------------------------------> @salt.utils.napalm.proxy_napalm_wrap -def load_config(filename=None, - text=None, - test=False, - commit=True, - debug=False, - replace=False, - commit_in=None, - commit_at=None, - revert_in=None, - revert_at=None, - commit_jid=None, - inherit_napalm_device=None, - saltenv='base', - **kwargs): # pylint: disable=unused-argument - ''' +def load_config( + filename=None, + text=None, + test=False, + commit=True, + debug=False, + replace=False, + commit_in=None, + commit_at=None, + revert_in=None, + revert_at=None, + commit_jid=None, + inherit_napalm_device=None, + saltenv="base", + **kwargs +): # pylint: disable=unused-argument + """ Applies configuration changes on the device. It can be loaded from a file or from inline string. If you send both a filename and a string containing the configuration, the file has higher precedence. @@ -1499,10 +1551,10 @@ def load_config(filename=None, 'result': True, 'diff': '[edit interfaces xe-0/0/5]+ description "Adding a description";' } - ''' - fun = 'load_merge_candidate' + """ + fun = "load_merge_candidate" if replace: - fun = 'load_replace_candidate' + fun = "load_replace_candidate" if salt.utils.napalm.not_always_alive(__opts__): # if a not-always-alive proxy # or regular minion @@ -1511,18 +1563,19 @@ def load_config(filename=None, # after running the other features: # compare_config, discard / commit # which have to be over the same session - napalm_device['CLOSE'] = False # pylint: disable=undefined-variable + napalm_device["CLOSE"] = False # pylint: disable=undefined-variable if filename: - text = __salt__['cp.get_file_str'](filename, saltenv=saltenv) + text = __salt__["cp.get_file_str"](filename, saltenv=saltenv) if text is False: # When using salt:// or https://, if the resource is not available, # it will either raise an exception, or return False. - ret = { - 'result': False, - 'out': None - } - ret['comment'] = 'Unable to read from {}. Please specify a valid file or text.'.format(filename) - log.error(ret['comment']) + ret = {"result": False, "out": None} + ret[ + "comment" + ] = "Unable to read from {}. Please specify a valid file or text.".format( + filename + ) + log.error(ret["comment"]) return ret if commit_jid: # When the commit_jid argument is passed, it probably is a scheduled @@ -1530,48 +1583,48 @@ def load_config(filename=None, # can be removed after reading it. salt.utils.files.safe_rm(filename) _loaded = salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - fun, - **{ - 'config': text - } + napalm_device, fun, **{"config": text} # pylint: disable=undefined-variable + ) + return _config_logic( + napalm_device, # pylint: disable=undefined-variable + _loaded, + test=test, + debug=debug, + replace=replace, + commit_config=commit, + loaded_config=text, + commit_at=commit_at, + commit_in=commit_in, + revert_in=revert_in, + revert_at=revert_at, + commit_jid=commit_jid, + **kwargs ) - return _config_logic(napalm_device, # pylint: disable=undefined-variable - _loaded, - test=test, - debug=debug, - replace=replace, - commit_config=commit, - loaded_config=text, - commit_at=commit_at, - commit_in=commit_in, - revert_in=revert_in, - revert_at=revert_at, - commit_jid=commit_jid, - **kwargs) @salt.utils.napalm.proxy_napalm_wrap -def load_template(template_name=None, - template_source=None, - context=None, - defaults=None, - template_engine='jinja', - saltenv='base', - template_hash=None, - template_hash_name=None, - skip_verify=False, - test=False, - commit=True, - debug=False, - replace=False, - commit_in=None, - commit_at=None, - revert_in=None, - revert_at=None, - inherit_napalm_device=None, # pylint: disable=unused-argument - **template_vars): - ''' +def load_template( + template_name=None, + template_source=None, + context=None, + defaults=None, + template_engine="jinja", + saltenv="base", + template_hash=None, + template_hash_name=None, + skip_verify=False, + test=False, + commit=True, + debug=False, + replace=False, + commit_in=None, + commit_at=None, + revert_in=None, + revert_at=None, + inherit_napalm_device=None, # pylint: disable=unused-argument + **template_vars +): + """ Renders a configuration template (default: Jinja) and loads the result on the device. By default this function will commit the changes. If there are no changes, @@ -1883,44 +1936,58 @@ def load_template(template_name=None, 'diff': '[edit system]+ host-name edge01.bjm01', 'loaded_config': 'system { host-name edge01.bjm01; }'' } - ''' - _rendered = '' - _loaded = { - 'result': True, - 'comment': '', - 'out': None - } + """ + _rendered = "" + _loaded = {"result": True, "comment": "", "out": None} loaded_config = None # prechecks - deprecated_args = ('template_user', 'template_attrs', 'template_group', 'template_mode') + deprecated_args = ( + "template_user", + "template_attrs", + "template_group", + "template_mode", + ) for deprecated_arg in deprecated_args: if template_vars.get(deprecated_arg): del template_vars[deprecated_arg] salt.utils.versions.warn_until( - 'Sodium', - ('The \'{arg}\' argument to \'net.load_template\' is deprecated ' - 'and has been ignored').format(arg=deprecated_arg) + "Sodium", + ( + "The '{arg}' argument to 'net.load_template' is deprecated " + "and has been ignored" + ).format(arg=deprecated_arg), ) if template_engine not in salt.utils.templates.TEMPLATE_REGISTRY: - _loaded.update({ - 'result': False, - 'comment': 'Invalid templating engine! Choose between: {tpl_eng_opts}'.format( - tpl_eng_opts=', '.join(list(salt.utils.templates.TEMPLATE_REGISTRY.keys())) - ) - }) + _loaded.update( + { + "result": False, + "comment": "Invalid templating engine! Choose between: {tpl_eng_opts}".format( + tpl_eng_opts=", ".join( + list(salt.utils.templates.TEMPLATE_REGISTRY.keys()) + ) + ), + } + ) return _loaded # exit # to check if will be rendered by salt or NAPALM - salt_render_prefixes = ('salt://', 'http://', 'https://', 'ftp://') + salt_render_prefixes = ("salt://", "http://", "https://", "ftp://") salt_render = False file_exists = False if not isinstance(template_name, (tuple, list)): for salt_render_prefix in salt_render_prefixes: if not salt_render: - salt_render = salt_render or template_name.startswith(salt_render_prefix) - file_exists = __salt__['file.file_exists'](template_name) + salt_render = salt_render or template_name.startswith( + salt_render_prefix + ) + file_exists = __salt__["file.file_exists"](template_name) - if template_source or file_exists or salt_render or isinstance(template_name, (tuple, list)): + if ( + template_source + or file_exists + or salt_render + or isinstance(template_name, (tuple, list)) + ): # either inline template # either template in a custom path # either abs path send @@ -1933,22 +2000,22 @@ def load_template(template_name=None, # if needed to render the template send as inline arg if template_source: # render the content - _rendered = __salt__['file.apply_template_on_contents']( + _rendered = __salt__["file.apply_template_on_contents"]( contents=template_source, template=template_engine, context=context, defaults=defaults, - saltenv=saltenv + saltenv=saltenv, ) if not isinstance(_rendered, six.string_types): - if 'result' in _rendered: - _loaded['result'] = _rendered['result'] + if "result" in _rendered: + _loaded["result"] = _rendered["result"] else: - _loaded['result'] = False - if 'comment' in _rendered: - _loaded['comment'] = _rendered['comment'] + _loaded["result"] = False + if "comment" in _rendered: + _loaded["comment"] = _rendered["comment"] else: - _loaded['comment'] = 'Error while rendering the template.' + _loaded["comment"] = "Error while rendering the template." return _loaded else: # render the file - either local, either remote @@ -1958,14 +2025,26 @@ def load_template(template_name=None, template_hash_name = [template_hash_name] elif not template_hash_name: template_hash_name = [None] * len(template_name) - if template_hash and isinstance(template_hash, six.string_types) and not\ - (template_hash.startswith('salt://') or template_hash.startswith('file://')): + if ( + template_hash + and isinstance(template_hash, six.string_types) + and not ( + template_hash.startswith("salt://") + or template_hash.startswith("file://") + ) + ): # If the template hash is passed as string, and it's not a file # (starts with the salt:// or file:// URI), then make it a list # of 1 element (for the iteration below) template_hash = [template_hash] - elif template_hash and isinstance(template_hash, six.string_types) and\ - (template_hash.startswith('salt://') or template_hash.startswith('file://')): + elif ( + template_hash + and isinstance(template_hash, six.string_types) + and ( + template_hash.startswith("salt://") + or template_hash.startswith("file://") + ) + ): # If the template hash is a file URI, then provide the same value # for each of the templates in the list, as probably they all # share the same hash file, otherwise the user should provide @@ -1976,47 +2055,51 @@ def load_template(template_name=None, for tpl_index, tpl_name in enumerate(template_name): tpl_hash = template_hash[tpl_index] tpl_hash_name = template_hash_name[tpl_index] - _rand_filename = __salt__['random.hash'](tpl_name, 'md5') - _temp_file = __salt__['file.join']('/tmp', _rand_filename) - _managed = __salt__['file.get_managed'](name=_temp_file, - source=tpl_name, - source_hash=tpl_hash, - source_hash_name=tpl_hash_name, - user=None, - group=None, - mode=None, - attrs=None, - template=template_engine, - context=context, - defaults=defaults, - saltenv=saltenv, - skip_verify=skip_verify) - if not isinstance(_managed, (list, tuple)) and isinstance(_managed, six.string_types): - _loaded['comment'] += _managed - _loaded['result'] = False + _rand_filename = __salt__["random.hash"](tpl_name, "md5") + _temp_file = __salt__["file.join"]("/tmp", _rand_filename) + _managed = __salt__["file.get_managed"]( + name=_temp_file, + source=tpl_name, + source_hash=tpl_hash, + source_hash_name=tpl_hash_name, + user=None, + group=None, + mode=None, + attrs=None, + template=template_engine, + context=context, + defaults=defaults, + saltenv=saltenv, + skip_verify=skip_verify, + ) + if not isinstance(_managed, (list, tuple)) and isinstance( + _managed, six.string_types + ): + _loaded["comment"] += _managed + _loaded["result"] = False elif isinstance(_managed, (list, tuple)) and not len(_managed) > 0: - _loaded['result'] = False - _loaded['comment'] += 'Error while rendering the template.' + _loaded["result"] = False + _loaded["comment"] += "Error while rendering the template." elif isinstance(_managed, (list, tuple)) and not len(_managed[0]) > 0: - _loaded['result'] = False - _loaded['comment'] += _managed[-1] # contains the error message - if _loaded['result']: # all good + _loaded["result"] = False + _loaded["comment"] += _managed[-1] # contains the error message + if _loaded["result"]: # all good _temp_tpl_file = _managed[0] - _temp_tpl_file_exists = __salt__['file.file_exists'](_temp_tpl_file) + _temp_tpl_file_exists = __salt__["file.file_exists"](_temp_tpl_file) if not _temp_tpl_file_exists: - _loaded['result'] = False - _loaded['comment'] += 'Error while rendering the template.' + _loaded["result"] = False + _loaded["comment"] += "Error while rendering the template." return _loaded - _rendered += __salt__['file.read'](_temp_tpl_file) - __salt__['file.remove'](_temp_tpl_file) + _rendered += __salt__["file.read"](_temp_tpl_file) + __salt__["file.remove"](_temp_tpl_file) else: return _loaded # exit loaded_config = _rendered - if _loaded['result']: # all good - fun = 'load_merge_candidate' + if _loaded["result"]: # all good + fun = "load_merge_candidate" if replace: # replace requested - fun = 'load_replace_candidate' + fun = "load_replace_candidate" if salt.utils.napalm.not_always_alive(__opts__): # if a not-always-alive proxy # or regular minion @@ -2025,32 +2108,30 @@ def load_template(template_name=None, # after running the other features: # compare_config, discard / commit # which have to be over the same session - napalm_device['CLOSE'] = False # pylint: disable=undefined-variable + napalm_device["CLOSE"] = False # pylint: disable=undefined-variable _loaded = salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable fun, - **{ - 'config': _rendered - } + **{"config": _rendered} ) else: salt.utils.versions.warn_until( - 'Sodium', - 'Native NAPALM templates support will be removed in the Sodium ' - 'release. Please consider using the Salt rendering pipeline instead.' - 'If you are using the \'netntp\', \'netsnmp\', or \'netusers\' Salt ' - 'State modules, you can ignore this message' + "Sodium", + "Native NAPALM templates support will be removed in the Sodium " + "release. Please consider using the Salt rendering pipeline instead." + "If you are using the 'netntp', 'netsnmp', or 'netusers' Salt " + "State modules, you can ignore this message", ) # otherwise, use NAPALM render system, injecting pillar/grains/opts vars load_templates_params = defaults if defaults else {} load_templates_params.update(template_vars) load_templates_params.update( { - 'template_name': template_name, - 'template_source': template_source, # inline template - 'pillar': __pillar__, # inject pillar content - 'grains': __grains__, # inject grains content - 'opts': __opts__ # inject opts content + "template_name": template_name, + "template_source": template_source, # inline template + "pillar": __pillar__, # inject pillar content + "grains": __grains__, # inject grains content + "opts": __opts__, # inject opts content } ) if salt.utils.napalm.not_always_alive(__opts__): @@ -2062,30 +2143,32 @@ def load_template(template_name=None, # compare_config, discard / commit # which have to be over the same session # so we'll set the CLOSE global explicitly as False - napalm_device['CLOSE'] = False # pylint: disable=undefined-variable + napalm_device["CLOSE"] = False # pylint: disable=undefined-variable _loaded = salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'load_template', + "load_template", **load_templates_params ) - return _config_logic(napalm_device, # pylint: disable=undefined-variable - _loaded, - test=test, - debug=debug, - replace=replace, - commit_config=commit, - loaded_config=loaded_config, - commit_at=commit_at, - commit_in=commit_in, - revert_in=revert_in, - revert_at=revert_at, - **template_vars) + return _config_logic( + napalm_device, # pylint: disable=undefined-variable + _loaded, + test=test, + debug=debug, + replace=replace, + commit_config=commit, + loaded_config=loaded_config, + commit_at=commit_at, + commit_in=commit_in, + revert_in=revert_in, + revert_at=revert_at, + **template_vars + ) @salt.utils.napalm.proxy_napalm_wrap def commit(inherit_napalm_device=None, **kwargs): # pylint: disable=unused-argument - ''' + """ Commits the configuration changes made on the network device. CLI Example: @@ -2093,17 +2176,17 @@ def commit(inherit_napalm_device=None, **kwargs): # pylint: disable=unused-argu .. code-block:: bash salt '*' net.commit - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'commit_config', - **{} + napalm_device, "commit_config", **{} # pylint: disable=undefined-variable ) @salt.utils.napalm.proxy_napalm_wrap -def discard_config(inherit_napalm_device=None, **kwargs): # pylint: disable=unused-argument +def discard_config( + inherit_napalm_device=None, **kwargs +): # pylint: disable=unused-argument """ Discards the changes applied. @@ -2116,16 +2199,16 @@ def discard_config(inherit_napalm_device=None, **kwargs): # pylint: disable=unu """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'discard_config', - **{} + napalm_device, "discard_config", **{} # pylint: disable=undefined-variable ) @salt.utils.napalm.proxy_napalm_wrap -def compare_config(inherit_napalm_device=None, **kwargs): # pylint: disable=unused-argument +def compare_config( + inherit_napalm_device=None, **kwargs +): # pylint: disable=unused-argument - ''' + """ Returns the difference between the running config and the candidate config. CLI Example: @@ -2133,19 +2216,17 @@ def compare_config(inherit_napalm_device=None, **kwargs): # pylint: disable=unu .. code-block:: bash salt '*' net.compare_config - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'compare_config', - **{} + napalm_device, "compare_config", **{} # pylint: disable=undefined-variable ) @salt.utils.napalm.proxy_napalm_wrap def rollback(inherit_napalm_device=None, **kwargs): # pylint: disable=unused-argument - ''' + """ Rollbacks the configuration. CLI Example: @@ -2153,19 +2234,19 @@ def rollback(inherit_napalm_device=None, **kwargs): # pylint: disable=unused-ar .. code-block:: bash salt '*' net.rollback - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'rollback', - **{} + napalm_device, "rollback", **{} # pylint: disable=undefined-variable ) @salt.utils.napalm.proxy_napalm_wrap -def config_changed(inherit_napalm_device=None, **kwargs): # pylint: disable=unused-argument +def config_changed( + inherit_napalm_device=None, **kwargs +): # pylint: disable=unused-argument - ''' + """ Will prompt if the configuration has been changed. :return: A tuple with a boolean that specifies if the config was changed on the device.\ @@ -2176,27 +2257,31 @@ def config_changed(inherit_napalm_device=None, **kwargs): # pylint: disable=unu .. code-block:: bash salt '*' net.config_changed - ''' + """ is_config_changed = False - reason = '' - try_compare = compare_config(inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + reason = "" + # pylint: disable=undefined-variable + try_compare = compare_config(inherit_napalm_device=napalm_device) + # pylint: enable=undefined-variable - if try_compare.get('result'): - if try_compare.get('out'): + if try_compare.get("result"): + if try_compare.get("out"): is_config_changed = True else: - reason = 'Configuration was not changed on the device.' + reason = "Configuration was not changed on the device." else: - reason = try_compare.get('comment') + reason = try_compare.get("comment") return is_config_changed, reason @salt.utils.napalm.proxy_napalm_wrap -def config_control(inherit_napalm_device=None, **kwargs): # pylint: disable=unused-argument +def config_control( + inherit_napalm_device=None, **kwargs +): # pylint: disable=unused-argument - ''' + """ Will check if the configuration was changed. If differences found, will try to commit. In case commit unsuccessful, will try to rollback. @@ -2209,34 +2294,36 @@ def config_control(inherit_napalm_device=None, **kwargs): # pylint: disable=unu .. code-block:: bash salt '*' net.config_control - ''' + """ result = True - comment = '' + comment = "" - changed, not_changed_rsn = config_changed(inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + changed, not_changed_rsn = config_changed(inherit_napalm_device=napalm_device) + # pylint: enable=undefined-variable if not changed: return (changed, not_changed_rsn) # config changed, thus let's try to commit try_commit = commit() - if not try_commit.get('result'): + if not try_commit.get("result"): result = False - comment = 'Unable to commit the changes: {reason}.\n\ - Will try to rollback now!'.format( - reason=try_commit.get('comment') + comment = "Unable to commit the changes: {reason}.\n\ + Will try to rollback now!".format( + reason=try_commit.get("comment") ) try_rollback = rollback() - if not try_rollback.get('result'): - comment += '\nCannot rollback! {reason}'.format( - reason=try_rollback.get('comment') + if not try_rollback.get("result"): + comment += "\nCannot rollback! {reason}".format( + reason=try_rollback.get("comment") ) return result, comment def cancel_commit(jid): - ''' + """ .. versionadded:: 2019.2.0 Cancel a commit scheduled to be executed via the ``commit_in`` and @@ -2251,19 +2338,19 @@ def cancel_commit(jid): .. code-block:: bash salt '*' net.cancel_commit 20180726083540640360 - ''' - job_name = '__napalm_commit_{}'.format(jid) - removed = __salt__['schedule.delete'](job_name) - if removed['result']: - saved = __salt__['schedule.save']() - removed['comment'] = 'Commit #{jid} cancelled.'.format(jid=jid) + """ + job_name = "__napalm_commit_{}".format(jid) + removed = __salt__["schedule.delete"](job_name) + if removed["result"]: + saved = __salt__["schedule.save"]() + removed["comment"] = "Commit #{jid} cancelled.".format(jid=jid) else: - removed['comment'] = 'Unable to find commit #{jid}.'.format(jid=jid) + removed["comment"] = "Unable to find commit #{jid}.".format(jid=jid) return removed def confirm_commit(jid): - ''' + """ .. versionadded:: 2019.2.0 Confirm a commit scheduled to be reverted via the ``revert_in`` and @@ -2278,22 +2365,21 @@ def confirm_commit(jid): .. code-block:: bash salt '*' net.confirm_commit 20180726083540640360 - ''' - if __grains__['os'] == 'junos': + """ + if __grains__["os"] == "junos": # Confirm the commit, by committing (i.e., invoking the RPC call) - confirmed = __salt__['napalm.junos_commit']() - confirmed['result'] = confirmed.pop('out') - confirmed['comment'] = confirmed.pop('message') + confirmed = __salt__["napalm.junos_commit"]() + confirmed["result"] = confirmed.pop("out") + confirmed["comment"] = confirmed.pop("message") else: confirmed = cancel_commit(jid) - if confirmed['result']: - confirmed['comment'] = 'Commit #{jid} confirmed.'.format(jid=jid) + if confirmed["result"]: + confirmed["comment"] = "Commit #{jid} confirmed.".format(jid=jid) return confirmed -def save_config(source=None, - path=None): - ''' +def save_config(source=None, path=None): + """ .. versionadded:: 2019.2.0 Save the configuration to a file on the local file system. @@ -2312,42 +2398,44 @@ def save_config(source=None, .. code-block:: bash salt '*' net.save_config source=running - ''' + """ if not source: - source = 'running' + source = "running" if not path: path = salt.utils.files.mkstemp() - running_config = __salt__['net.config'](source=source) - if not running_config or not running_config['result']: - log.error('Unable to retrieve the config') + running_config = __salt__["net.config"](source=source) + if not running_config or not running_config["result"]: + log.error("Unable to retrieve the config") return running_config - with salt.utils.files.fopen(path, 'w') as fh_: - fh_.write(running_config['out'][source]) + with salt.utils.files.fopen(path, "w") as fh_: + fh_.write(running_config["out"][source]) return { - 'result': True, - 'out': path, - 'comment': '{source} config saved to {path}'.format(source=source, path=path) + "result": True, + "out": path, + "comment": "{source} config saved to {path}".format(source=source, path=path), } -def replace_pattern(pattern, - repl, - count=0, - flags=8, - bufsize=1, - append_if_not_found=False, - prepend_if_not_found=False, - not_found_content=None, - search_only=False, - show_changes=True, - backslash_literal=False, - source=None, - path=None, - test=False, - replace=True, - debug=False, - commit=True): - ''' +def replace_pattern( + pattern, + repl, + count=0, + flags=8, + bufsize=1, + append_if_not_found=False, + prepend_if_not_found=False, + not_found_content=None, + search_only=False, + show_changes=True, + backslash_literal=False, + source=None, + path=None, + test=False, + replace=True, + debug=False, + commit=True, +): + """ .. versionadded:: 2019.2.0 Replace occurrences of a pattern in the configuration source. If @@ -2447,46 +2535,48 @@ def replace_pattern(pattern, salt '*' net.replace_pattern PREFIX-LIST_NAME new-prefix-list-name salt '*' net.replace_pattern bgp-group-name new-bgp-group-name count=1 - ''' + """ config_saved = save_config(source=source, path=path) - if not config_saved or not config_saved['result']: + if not config_saved or not config_saved["result"]: return config_saved - path = config_saved['out'] - replace_pattern = __salt__['file.replace'](path, - pattern, - repl, - count=count, - flags=flags, - bufsize=bufsize, - append_if_not_found=append_if_not_found, - prepend_if_not_found=prepend_if_not_found, - not_found_content=not_found_content, - search_only=search_only, - show_changes=show_changes, - backslash_literal=backslash_literal) - with salt.utils.files.fopen(path, 'r') as fh_: + path = config_saved["out"] + replace_pattern = __salt__["file.replace"]( + path, + pattern, + repl, + count=count, + flags=flags, + bufsize=bufsize, + append_if_not_found=append_if_not_found, + prepend_if_not_found=prepend_if_not_found, + not_found_content=not_found_content, + search_only=search_only, + show_changes=show_changes, + backslash_literal=backslash_literal, + ) + with salt.utils.files.fopen(path, "r") as fh_: updated_config = fh_.read() - return __salt__['net.load_config'](text=updated_config, - test=test, - debug=debug, - replace=replace, - commit=commit) + return __salt__["net.load_config"]( + text=updated_config, test=test, debug=debug, replace=replace, commit=commit + ) -def blockreplace(marker_start, - marker_end, - content='', - append_if_not_found=False, - prepend_if_not_found=False, - show_changes=True, - append_newline=False, - source='running', - path=None, - test=False, - commit=True, - debug=False, - replace=True): - ''' +def blockreplace( + marker_start, + marker_end, + content="", + append_if_not_found=False, + prepend_if_not_found=False, + show_changes=True, + append_newline=False, + source="running", + path=None, + test=False, + commit=True, + debug=False, + replace=True, +): + """ .. versionadded:: 2019.2.0 Replace content of the configuration source, delimited by the line markers. @@ -2560,40 +2650,42 @@ def blockreplace(marker_start, .. code-block:: bash salt '*' net.blockreplace 'ntp' 'interface' '' - ''' + """ config_saved = save_config(source=source, path=path) - if not config_saved or not config_saved['result']: + if not config_saved or not config_saved["result"]: return config_saved - path = config_saved['out'] - replace_pattern = __salt__['file.blockreplace'](path, - marker_start=marker_start, - marker_end=marker_end, - content=content, - append_if_not_found=append_if_not_found, - prepend_if_not_found=prepend_if_not_found, - show_changes=show_changes, - append_newline=append_newline) - with salt.utils.files.fopen(path, 'r') as fh_: + path = config_saved["out"] + replace_pattern = __salt__["file.blockreplace"]( + path, + marker_start=marker_start, + marker_end=marker_end, + content=content, + append_if_not_found=append_if_not_found, + prepend_if_not_found=prepend_if_not_found, + show_changes=show_changes, + append_newline=append_newline, + ) + with salt.utils.files.fopen(path, "r") as fh_: updated_config = fh_.read() - return __salt__['net.load_config'](text=updated_config, - test=test, - debug=debug, - replace=replace, - commit=commit) + return __salt__["net.load_config"]( + text=updated_config, test=test, debug=debug, replace=replace, commit=commit + ) -def patch(patchfile, - options='', - saltenv='base', - source_hash=None, - show_changes=True, - source='running', - path=None, - test=False, - commit=True, - debug=False, - replace=True): - ''' +def patch( + patchfile, + options="", + saltenv="base", + source_hash=None, + show_changes=True, + source="running", + path=None, + test=False, + commit=True, + debug=False, + replace=True, +): + """ .. versionadded:: 2019.2.0 Apply a patch to the configuration source, and load the result into the @@ -2646,27 +2738,24 @@ def patch(patchfile, .. code-block:: bash salt '*' net.patch https://example.com/running_config.patch - ''' + """ config_saved = save_config(source=source, path=path) - if not config_saved or not config_saved['result']: + if not config_saved or not config_saved["result"]: return config_saved - path = config_saved['out'] - patchfile_cache = __salt__['cp.cache_file'](patchfile) + path = config_saved["out"] + patchfile_cache = __salt__["cp.cache_file"](patchfile) if patchfile_cache is False: return { - 'out': None, - 'result': False, - 'comment': 'The file "{}" does not exist.'.format(patchfile) + "out": None, + "result": False, + "comment": 'The file "{}" does not exist.'.format(patchfile), } - replace_pattern = __salt__['file.patch'](path, - patchfile_cache, - options=options) - with salt.utils.files.fopen(path, 'r') as fh_: + replace_pattern = __salt__["file.patch"](path, patchfile_cache, options=options) + with salt.utils.files.fopen(path, "r") as fh_: updated_config = fh_.read() - return __salt__['net.load_config'](text=updated_config, - test=test, - debug=debug, - replace=replace, - commit=commit) + return __salt__["net.load_config"]( + text=updated_config, test=test, debug=debug, replace=replace, commit=commit + ) + # <---- Configuration specific functions ------------------------------------------------------------------------------- diff --git a/salt/modules/napalm_ntp.py b/salt/modules/napalm_ntp.py index 98d3cb66f32..32767d463f6 100644 --- a/salt/modules/napalm_ntp.py +++ b/salt/modules/napalm_ntp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM NTP ========== @@ -19,24 +19,26 @@ Dependencies :mod:`NTP peers management state ` .. versionadded:: 2016.11.0 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import logging -log = logging.getLogger(__file__) # import NAPALM utils import salt.utils.napalm from salt.utils.napalm import proxy_napalm_wrap +log = logging.getLogger(__file__) + + # ---------------------------------------------------------------------------------------------------------------------- # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'ntp' -__proxyenabled__ = ['napalm'] -__virtual_aliases__ = ('napalm_ntp',) +__virtualname__ = "ntp" +__proxyenabled__ = ["napalm"] +__virtual_aliases__ = ("napalm_ntp",) # uses NAPALM-based proxy to interact with network devices # ---------------------------------------------------------------------------------------------------------------------- @@ -45,11 +47,12 @@ __virtual_aliases__ = ('napalm_ntp',) def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -62,7 +65,7 @@ def __virtual__(): @proxy_napalm_wrap def peers(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns a list the NTP peers configured on the network device. :return: configured NTP peers as list. @@ -84,21 +87,18 @@ def peers(**kwargs): # pylint: disable=unused-argument '2400:cb00:6:1024::c71b:840a' ] - ''' + """ ntp_peers = salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_ntp_peers', - **{ - } + napalm_device, "get_ntp_peers", **{} # pylint: disable=undefined-variable ) - if not ntp_peers.get('result'): + if not ntp_peers.get("result"): return ntp_peers - ntp_peers_list = list(ntp_peers.get('out', {}).keys()) + ntp_peers_list = list(ntp_peers.get("out", {}).keys()) - ntp_peers['out'] = ntp_peers_list + ntp_peers["out"] = ntp_peers_list return ntp_peers @@ -106,7 +106,7 @@ def peers(**kwargs): # pylint: disable=unused-argument @proxy_napalm_wrap def servers(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns a list of the configured NTP servers on the device. CLI Example: @@ -125,21 +125,18 @@ def servers(**kwargs): # pylint: disable=unused-argument '172.17.17.2', '2400:cb00:6:1024::c71b:840a' ] - ''' + """ ntp_servers = salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_ntp_servers', - **{ - } + napalm_device, "get_ntp_servers", **{} # pylint: disable=undefined-variable ) - if not ntp_servers.get('result'): + if not ntp_servers.get("result"): return ntp_servers - ntp_servers_list = list(ntp_servers.get('out', {}).keys()) + ntp_servers_list = list(ntp_servers.get("out", {}).keys()) - ntp_servers['out'] = ntp_servers_list + ntp_servers["out"] = ntp_servers_list return ntp_servers @@ -147,7 +144,7 @@ def servers(**kwargs): # pylint: disable=unused-argument @proxy_napalm_wrap def stats(peer=None, **kwargs): # pylint: disable=unused-argument - ''' + """ Returns a dictionary containing synchronization details of the NTP peers. :param peer: Returns only the details of a specific NTP peer. @@ -190,26 +187,23 @@ def stats(peer=None, **kwargs): # pylint: disable=unused-argument 'jitter' : 2.695 } ] - ''' + """ proxy_output = salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_ntp_stats', - **{ - } + napalm_device, "get_ntp_stats", **{} # pylint: disable=undefined-variable ) - if not proxy_output.get('result'): + if not proxy_output.get("result"): return proxy_output - ntp_peers = proxy_output.get('out') + ntp_peers = proxy_output.get("out") if peer: - ntp_peers = [ntp_peer for ntp_peer in ntp_peers if ntp_peer.get('remote', '') == peer] + ntp_peers = [ + ntp_peer for ntp_peer in ntp_peers if ntp_peer.get("remote", "") == peer + ] - proxy_output.update({ - 'out': ntp_peers - }) + proxy_output.update({"out": ntp_peers}) return proxy_output @@ -217,7 +211,7 @@ def stats(peer=None, **kwargs): # pylint: disable=unused-argument @proxy_napalm_wrap def set_peers(*peers, **options): - ''' + """ Configures a list of NTP peers on the device. :param peers: list of IP Addresses/Domain Names @@ -237,21 +231,25 @@ def set_peers(*peers, **options): salt '*' ntp.set_peers 192.168.0.1 172.17.17.1 time.apple.com salt '*' ntp.set_peers 172.17.17.1 test=True # only displays the diff salt '*' ntp.set_peers 192.168.0.1 commit=False # preserves the changes, but does not commit - ''' + """ - test = options.pop('test', False) - commit = options.pop('commit', True) + test = options.pop("test", False) + commit = options.pop("commit", True) - return __salt__['net.load_template']('set_ntp_peers', - peers=peers, - test=test, - commit=commit, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + return __salt__["net.load_template"]( + "set_ntp_peers", + peers=peers, + test=test, + commit=commit, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable @proxy_napalm_wrap def set_servers(*servers, **options): - ''' + """ Configures a list of NTP servers on the device. :param servers: list of IP Addresses/Domain Names @@ -271,22 +269,26 @@ def set_servers(*servers, **options): salt '*' ntp.set_servers 192.168.0.1 172.17.17.1 time.apple.com salt '*' ntp.set_servers 172.17.17.1 test=True # only displays the diff salt '*' ntp.set_servers 192.168.0.1 commit=False # preserves the changes, but does not commit - ''' + """ - test = options.pop('test', False) - commit = options.pop('commit', True) + test = options.pop("test", False) + commit = options.pop("commit", True) - return __salt__['net.load_template']('set_ntp_servers', - servers=servers, - test=test, - commit=commit, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + return __salt__["net.load_template"]( + "set_ntp_servers", + servers=servers, + test=test, + commit=commit, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable @proxy_napalm_wrap def delete_peers(*peers, **options): - ''' + """ Removes NTP peers configured on the device. :param peers: list of IP Addresses/Domain Names to be removed as NTP peers @@ -307,22 +309,26 @@ def delete_peers(*peers, **options): salt '*' ntp.delete_peers 8.8.8.8 time.apple.com salt '*' ntp.delete_peers 172.17.17.1 test=True # only displays the diff salt '*' ntp.delete_peers 192.168.0.1 commit=False # preserves the changes, but does not commit - ''' + """ - test = options.pop('test', False) - commit = options.pop('commit', True) + test = options.pop("test", False) + commit = options.pop("commit", True) - return __salt__['net.load_template']('delete_ntp_peers', - peers=peers, - test=test, - commit=commit, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + return __salt__["net.load_template"]( + "delete_ntp_peers", + peers=peers, + test=test, + commit=commit, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable @proxy_napalm_wrap def delete_servers(*servers, **options): - ''' + """ Removes NTP servers configured on the device. :param servers: list of IP Addresses/Domain Names to be removed as NTP @@ -344,13 +350,17 @@ def delete_servers(*servers, **options): salt '*' ntp.delete_servers 8.8.8.8 time.apple.com salt '*' ntp.delete_servers 172.17.17.1 test=True # only displays the diff salt '*' ntp.delete_servers 192.168.0.1 commit=False # preserves the changes, but does not commit - ''' + """ - test = options.pop('test', False) - commit = options.pop('commit', True) + test = options.pop("test", False) + commit = options.pop("commit", True) - return __salt__['net.load_template']('delete_ntp_servers', - servers=servers, - test=test, - commit=commit, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + return __salt__["net.load_template"]( + "delete_ntp_servers", + servers=servers, + test=test, + commit=commit, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable diff --git a/salt/modules/napalm_probes.py b/salt/modules/napalm_probes.py index 0578a245060..b986c2142f5 100644 --- a/salt/modules/napalm_probes.py +++ b/salt/modules/napalm_probes.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM Probes ============= @@ -20,24 +20,26 @@ Dependencies :mod:`Probes configuration management state ` .. versionadded:: 2016.11.0 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import python lib import logging -log = logging.getLogger(__file__) # import NAPALM utils import salt.utils.napalm from salt.utils.napalm import proxy_napalm_wrap +log = logging.getLogger(__file__) + + # ---------------------------------------------------------------------------------------------------------------------- # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'probes' -__proxyenabled__ = ['napalm'] +__virtualname__ = "probes" +__proxyenabled__ = ["napalm"] # uses NAPALM-based proxy to interact with network devices # ---------------------------------------------------------------------------------------------------------------------- @@ -46,11 +48,12 @@ __proxyenabled__ = ['napalm'] def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -63,7 +66,7 @@ def __virtual__(): @proxy_napalm_wrap def config(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns the configuration of the RPM probes. :return: A dictionary containing the configuration of the RPM/SLA probes. @@ -96,20 +99,17 @@ def config(**kwargs): # pylint: disable=unused-argument } } } - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_probes_config', - **{ - } + napalm_device, "get_probes_config", **{} # pylint: disable=undefined-variable ) @proxy_napalm_wrap def results(**kwargs): # pylint: disable=unused-argument - ''' + """ Provides the results of the measurements of the RPM/SLA probes. :return a dictionary with the results of the probes. @@ -166,19 +166,18 @@ def results(**kwargs): # pylint: disable=unused-argument } } } - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_probes_results', - **{ - } + napalm_device, "get_probes_results", **{} # pylint: disable=undefined-variable ) @proxy_napalm_wrap -def set_probes(probes, test=False, commit=True, **kwargs): # pylint: disable=unused-argument - ''' +def set_probes( + probes, test=False, commit=True, **kwargs +): # pylint: disable=unused-argument + """ Configures RPM/SLA probes on the device. Calls the configuration template 'set_probes' from the NAPALM library, providing as input a rich formatted dictionary with the configuration details of the probes to be configured. @@ -260,19 +259,25 @@ def set_probes(probes, test=False, commit=True, **kwargs): # pylint: disable=un + } result: True - ''' + """ - return __salt__['net.load_template']('set_probes', - probes=probes, - test=test, - commit=commit, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + return __salt__["net.load_template"]( + "set_probes", + probes=probes, + test=test, + commit=commit, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable @proxy_napalm_wrap -def delete_probes(probes, test=False, commit=True, **kwargs): # pylint: disable=unused-argument +def delete_probes( + probes, test=False, commit=True, **kwargs +): # pylint: disable=unused-argument - ''' + """ Removes RPM/SLA probes from the network device. Calls the configuration template 'delete_probes' from the NAPALM library, providing as input a rich formatted dictionary with the configuration details of the probes to be removed @@ -311,19 +316,25 @@ def delete_probes(probes, test=False, commit=True, **kwargs): # pylint: disable } } - ''' + """ - return __salt__['net.load_template']('delete_probes', - probes=probes, - test=test, - commit=commit, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + return __salt__["net.load_template"]( + "delete_probes", + probes=probes, + test=test, + commit=commit, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable @proxy_napalm_wrap -def schedule_probes(probes, test=False, commit=True, **kwargs): # pylint: disable=unused-argument +def schedule_probes( + probes, test=False, commit=True, **kwargs +): # pylint: disable=unused-argument - ''' + """ Will schedule the probes. On Cisco devices, it is not enough to define the probes, it is also necessary to schedule them. @@ -363,10 +374,14 @@ def schedule_probes(probes, test=False, commit=True, **kwargs): # pylint: disab 'new_test2': {} } } - ''' + """ - return __salt__['net.load_template']('schedule_probes', - probes=probes, - test=test, - commit=commit, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + return __salt__["net.load_template"]( + "schedule_probes", + probes=probes, + test=test, + commit=commit, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable diff --git a/salt/modules/napalm_route.py b/salt/modules/napalm_route.py index a79805d04b3..f1a4ea088b5 100644 --- a/salt/modules/napalm_route.py +++ b/salt/modules/napalm_route.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM Route ============ @@ -15,25 +15,27 @@ Dependencies - :mod:`NAPALM proxy minion ` .. versionadded:: 2016.11.0 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import logging -log = logging.getLogger(__file__) # import NAPALM utils import salt.utils.napalm from salt.utils.napalm import proxy_napalm_wrap +log = logging.getLogger(__file__) + + # ---------------------------------------------------------------------------------------------------------------------- # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'route' -__proxyenabled__ = ['napalm'] +__virtualname__ = "route" +__proxyenabled__ = ["napalm"] # uses NAPALM-based proxy to interact with network devices -__virtual_aliases__ = ('napalm_route',) +__virtual_aliases__ = ("napalm_route",) # ---------------------------------------------------------------------------------------------------------------------- # property functions @@ -41,11 +43,12 @@ __virtual_aliases__ = ('napalm_route',) def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -58,7 +61,7 @@ def __virtual__(): @proxy_napalm_wrap def show(destination, protocol=None, **kwargs): # pylint: disable=unused-argument - ''' + """ Displays all details for a certain route learned via a specific protocol. If the protocol is not specified, will return all possible routes. @@ -146,13 +149,10 @@ def show(destination, protocol=None, **kwargs): # pylint: disable=unused-argume } ] } - ''' + """ return salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'get_route_to', - **{ - 'destination': destination, - 'protocol': protocol - } + "get_route_to", + **{"destination": destination, "protocol": protocol} ) diff --git a/salt/modules/napalm_snmp.py b/salt/modules/napalm_snmp.py index d7dd5935af6..e4d5ef34dbb 100644 --- a/salt/modules/napalm_snmp.py +++ b/salt/modules/napalm_snmp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM SNMP =========== @@ -19,25 +19,27 @@ Dependencies :mod:`SNMP configuration management state ` .. versionadded:: 2016.11.0 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import logging -log = logging.getLogger(__file__) # import NAPALM utils import salt.utils.napalm from salt.utils.napalm import proxy_napalm_wrap +log = logging.getLogger(__file__) + + # ---------------------------------------------------------------------------------------------------------------------- # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'snmp' -__proxyenabled__ = ['napalm'] +__virtualname__ = "snmp" +__proxyenabled__ = ["napalm"] # uses NAPALM-based proxy to interact with network devices -__virtual_aliases__ = ('napalm_snmp',) +__virtual_aliases__ = ("napalm_snmp",) # ---------------------------------------------------------------------------------------------------------------------- # property functions @@ -45,11 +47,12 @@ __virtual_aliases__ = ('napalm_snmp',) def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -62,7 +65,7 @@ def __virtual__(): @proxy_napalm_wrap def config(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns the SNMP configuration CLI Example: @@ -70,26 +73,27 @@ def config(**kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' snmp.config - ''' + """ return salt.utils.napalm.call( napalm_device, # pylint: disable=undefined-variable - 'get_snmp_information', - **{ - } + "get_snmp_information", + **{} ) @proxy_napalm_wrap -def remove_config(chassis_id=None, - community=None, - contact=None, - location=None, - test=False, - commit=True, - **kwargs): # pylint: disable=unused-argument +def remove_config( + chassis_id=None, + community=None, + contact=None, + location=None, + test=False, + commit=True, + **kwargs +): # pylint: disable=unused-argument - ''' + """ Removes a configuration element from the SNMP configuration. :param chassis_id: (optional) Chassis ID @@ -128,37 +132,35 @@ def remove_config(chassis_id=None, .. code-block:: bash salt '*' snmp.remove_config community='abcd' - ''' + """ - dic = { - 'template_name': 'delete_snmp_config', - 'test': test, - 'commit': commit - } + dic = {"template_name": "delete_snmp_config", "test": test, "commit": commit} if chassis_id: - dic['chassis_id'] = chassis_id + dic["chassis_id"] = chassis_id if community: - dic['community'] = community + dic["community"] = community if contact: - dic['contact'] = contact + dic["contact"] = contact if location: - dic['location'] = location - dic['inherit_napalm_device'] = napalm_device # pylint: disable=undefined-variable + dic["location"] = location + dic["inherit_napalm_device"] = napalm_device # pylint: disable=undefined-variable - return __salt__['net.load_template'](**dic) + return __salt__["net.load_template"](**dic) @proxy_napalm_wrap -def update_config(chassis_id=None, - community=None, - contact=None, - location=None, - test=False, - commit=True, - **kwargs): # pylint: disable=unused-argument +def update_config( + chassis_id=None, + community=None, + contact=None, + location=None, + test=False, + commit=True, + **kwargs +): # pylint: disable=unused-argument - ''' + """ Updates the SNMP configuration. :param chassis_id: (optional) Chassis ID @@ -208,22 +210,18 @@ def update_config(chassis_id=None, + location "Greenwich, UK"; result: True - ''' + """ - dic = { - 'template_name': 'snmp_config', - 'test': test, - 'commit': commit - } + dic = {"template_name": "snmp_config", "test": test, "commit": commit} if chassis_id: - dic['chassis_id'] = chassis_id + dic["chassis_id"] = chassis_id if community: - dic['community'] = community + dic["community"] = community if contact: - dic['contact'] = contact + dic["contact"] = contact if location: - dic['location'] = location - dic['inherit_napalm_device'] = napalm_device # pylint: disable=undefined-variable + dic["location"] = location + dic["inherit_napalm_device"] = napalm_device # pylint: disable=undefined-variable - return __salt__['net.load_template'](**dic) + return __salt__["net.load_template"](**dic) diff --git a/salt/modules/napalm_users.py b/salt/modules/napalm_users.py index 69296adb978..74938d3d9ab 100644 --- a/salt/modules/napalm_users.py +++ b/salt/modules/napalm_users.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM Users ============ @@ -18,24 +18,26 @@ Dependencies :mod:`Users management state ` .. versionadded:: 2016.11.0 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import logging -log = logging.getLogger(__file__) # import NAPALM utils import salt.utils.napalm from salt.utils.napalm import proxy_napalm_wrap +log = logging.getLogger(__file__) + + # ---------------------------------------------------------------------------------------------------------------------- # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'users' -__proxyenabled__ = ['napalm'] -__virtual_aliases__ = ('napalm_users',) +__virtualname__ = "users" +__proxyenabled__ = ["napalm"] +__virtual_aliases__ = ("napalm_users",) # uses NAPALM-based proxy to interact with network devices # ---------------------------------------------------------------------------------------------------------------------- @@ -44,11 +46,12 @@ __virtual_aliases__ = ('napalm_users',) def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -61,7 +64,7 @@ def __virtual__(): @proxy_napalm_wrap def config(**kwargs): # pylint: disable=unused-argument - ''' + """ Returns the configuration of the users on the device CLI Example: @@ -88,20 +91,19 @@ def config(**kwargs): # pylint: disable=unused-argument ] } } - ''' + """ return salt.utils.napalm.call( - napalm_device, # pylint: disable=undefined-variable - 'get_users', - **{ - } + napalm_device, "get_users", **{} # pylint: disable=undefined-variable ) @proxy_napalm_wrap -def set_users(users, test=False, commit=True, **kwargs): # pylint: disable=unused-argument +def set_users( + users, test=False, commit=True, **kwargs +): # pylint: disable=unused-argument - ''' + """ Configures users on network devices. :param users: Dictionary formatted as the output of the function config() @@ -132,19 +134,25 @@ def set_users(users, test=False, commit=True, **kwargs): # pylint: disable=unus .. code-block:: bash salt '*' users.set_users "{'mircea': {}}" - ''' + """ - return __salt__['net.load_template']('set_users', - users=users, - test=test, - commit=commit, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + return __salt__["net.load_template"]( + "set_users", + users=users, + test=test, + commit=commit, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable @proxy_napalm_wrap -def delete_users(users, test=False, commit=True, **kwargs): # pylint: disable=unused-argument +def delete_users( + users, test=False, commit=True, **kwargs +): # pylint: disable=unused-argument - ''' + """ Removes users from the configuration of network devices. :param users: Dictionary formatted as the output of the function config() @@ -169,10 +177,14 @@ def delete_users(users, test=False, commit=True, **kwargs): # pylint: disable=u .. code-block:: bash salt '*' users.delete_users "{'mircea': {}}" - ''' + """ - return __salt__['net.load_template']('delete_users', - users=users, - test=test, - commit=commit, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + return __salt__["net.load_template"]( + "delete_users", + users=users, + test=test, + commit=commit, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable diff --git a/salt/modules/napalm_yang_mod.py b/salt/modules/napalm_yang_mod.py index ce8f7ce2697..4b2e16817c3 100644 --- a/salt/modules/napalm_yang_mod.py +++ b/salt/modules/napalm_yang_mod.py @@ -1,34 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM YANG =========== NAPALM YANG basic operations. .. versionadded:: 2017.7.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python stdlib import logging -# Import third party libs -try: - import napalm_yang - HAS_NAPALM_YANG = True -except ImportError: - HAS_NAPALM_YANG = False - # import NAPALM utils import salt.utils.napalm from salt.utils.napalm import proxy_napalm_wrap +# Import third party libs +try: + import napalm_yang + + HAS_NAPALM_YANG = True +except ImportError: + HAS_NAPALM_YANG = False + + # ----------------------------------------------------------------------------- # module properties # ----------------------------------------------------------------------------- -__virtualname__ = 'napalm_yang' -__proxyenabled__ = ['*'] +__virtualname__ = "napalm_yang" +__proxyenabled__ = ["*"] # uses NAPALM-based proxy to interact with network devices log = logging.getLogger(__file__) @@ -39,38 +41,43 @@ log = logging.getLogger(__file__) def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. This module in particular requires also napalm-yang. - ''' + """ if not HAS_NAPALM_YANG: - return (False, 'Unable to load napalm_yang execution module: please install napalm-yang!') + return ( + False, + "Unable to load napalm_yang execution module: please install napalm-yang!", + ) return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ----------------------------------------------------------------------------- # helper functions -- will not be exported # ----------------------------------------------------------------------------- def _get_root_object(models): - ''' + """ Read list of models and returns a Root object with the proper models added. - ''' + """ root = napalm_yang.base.Root() for model in models: current = napalm_yang - for part in model.split('.'): + for part in model.split("."): current = getattr(current, part) root.add_model(current) return root + # ----------------------------------------------------------------------------- # callable functions # ----------------------------------------------------------------------------- def diff(candidate, running, *models): - ''' + """ Returns the difference between two configuration entities structured according to the YANG model. @@ -118,7 +125,7 @@ def diff(candidate, running, *models): } } } - ''' + """ if isinstance(models, tuple) and isinstance(models[0], list): models = models[0] @@ -131,7 +138,7 @@ def diff(candidate, running, *models): @proxy_napalm_wrap def parse(*models, **kwargs): - ''' + """ Parse configuration from the device. models @@ -342,20 +349,22 @@ def parse(*models, **kwargs): } } } - ''' + """ if isinstance(models, tuple) and isinstance(models[0], list): models = models[0] - config = kwargs.pop('config', False) - state = kwargs.pop('state', False) - profiles = kwargs.pop('profiles', []) - if not profiles and hasattr(napalm_device, 'profile'): # pylint: disable=undefined-variable + config = kwargs.pop("config", False) + state = kwargs.pop("state", False) + profiles = kwargs.pop("profiles", []) + # pylint: disable=undefined-variable + if not profiles and hasattr(napalm_device, "profile"): profiles = napalm_device.profile # pylint: disable=undefined-variable + # pylint: enable=undefined-variable if not profiles: - profiles = [__grains__.get('os')] + profiles = [__grains__.get("os")] root = _get_root_object(models) parser_kwargs = { - 'device': napalm_device.get('DRIVER'), # pylint: disable=undefined-variable - 'profile': profiles + "device": napalm_device.get("DRIVER"), # pylint: disable=undefined-variable + "profile": profiles, } if config: root.parse_config(**parser_kwargs) @@ -366,7 +375,7 @@ def parse(*models, **kwargs): @proxy_napalm_wrap def get_config(data, *models, **kwargs): - ''' + """ Return the native config. data @@ -397,28 +406,28 @@ def get_config(data, *models, **kwargs): ip address 192.168.2.1/24 description Uplink2 mtu 9000 - ''' + """ if isinstance(models, tuple) and isinstance(models[0], list): models = models[0] - profiles = kwargs.pop('profiles', []) - if not profiles and hasattr(napalm_device, 'profile'): # pylint: disable=undefined-variable + profiles = kwargs.pop("profiles", []) + # pylint: disable=undefined-variable + if not profiles and hasattr(napalm_device, "profile"): + # pylint: enable=undefined-variable profiles = napalm_device.profile # pylint: disable=undefined-variable if not profiles: - profiles = [__grains__.get('os')] - parser_kwargs = { - 'profile': profiles - } + profiles = [__grains__.get("os")] + parser_kwargs = {"profile": profiles} root = _get_root_object(models) root.load_dict(data) native_config = root.translate_config(**parser_kwargs) - log.debug('Generated config') + log.debug("Generated config") log.debug(native_config) return native_config @proxy_napalm_wrap def load_config(data, *models, **kwargs): - ''' + """ Generate and load the config on the device using the OpenConfig or IETF models and device profiles. @@ -551,25 +560,29 @@ def load_config(data, *models, **kwargs): result: True - ''' + """ if isinstance(models, tuple) and isinstance(models[0], list): models = models[0] config = get_config(data, *models, **kwargs) - test = kwargs.pop('test', False) - debug = kwargs.pop('debug', False) - commit = kwargs.pop('commit', True) - replace = kwargs.pop('replace', False) - return __salt__['net.load_config'](text=config, - test=test, - debug=debug, - commit=commit, - replace=replace, - inherit_napalm_device=napalm_device) # pylint: disable=undefined-variable + test = kwargs.pop("test", False) + debug = kwargs.pop("debug", False) + commit = kwargs.pop("commit", True) + replace = kwargs.pop("replace", False) + # pylint: disable=undefined-variable + return __salt__["net.load_config"]( + text=config, + test=test, + debug=debug, + commit=commit, + replace=replace, + inherit_napalm_device=napalm_device, + ) + # pylint: enable=undefined-variable @proxy_napalm_wrap def compliance_report(data, *models, **kwargs): - ''' + """ Return the compliance report using YANG objects. data @@ -606,10 +619,10 @@ def compliance_report(data, *models, **kwargs): "extra": [] } } - ''' + """ if isinstance(models, tuple) and isinstance(models[0], list): models = models[0] - filepath = kwargs.pop('filepath', '') + filepath = kwargs.pop("filepath", "") root = _get_root_object(models) root.load_dict(data) return root.compliance_report(filepath) diff --git a/salt/modules/netaddress.py b/salt/modules/netaddress.py index 8760b5c0812..92c73607e28 100644 --- a/salt/modules/netaddress.py +++ b/salt/modules/netaddress.py @@ -1,80 +1,84 @@ # -*- coding: utf-8 -*- -''' +""" Module for getting information about network addresses. .. versionadded:: 2016.3.0 :depends: netaddr -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs from salt.ext import six -__virtualname__ = 'netaddress' +__virtualname__ = "netaddress" # Import third party libs try: import netaddr + HAS_NETADDR = True except ImportError as e: HAS_NETADDR = False def __virtual__(): - ''' + """ Only load if netaddr library exist. - ''' + """ if not HAS_NETADDR: - return (False, 'The netaddress execution module cannot be loaded: ' - 'netaddr python library is not installed.') + return ( + False, + "The netaddress execution module cannot be loaded: " + "netaddr python library is not installed.", + ) return __virtualname__ def list_cidr_ips(cidr): - ''' + """ Get a list of IP addresses from a CIDR. CLI example:: salt myminion netaddress.list_cidr_ips 192.168.0.0/20 - ''' + """ ips = netaddr.IPNetwork(cidr) return [six.text_type(ip) for ip in list(ips)] def list_cidr_ips_ipv6(cidr): - ''' + """ Get a list of IPv6 addresses from a CIDR. CLI example:: salt myminion netaddress.list_cidr_ips_ipv6 192.168.0.0/20 - ''' + """ ips = netaddr.IPNetwork(cidr) return [six.text_type(ip.ipv6()) for ip in list(ips)] def cidr_netmask(cidr): - ''' + """ Get the netmask address associated with a CIDR address. CLI example:: salt myminion netaddress.cidr_netmask 192.168.0.0/20 - ''' + """ ips = netaddr.IPNetwork(cidr) return six.text_type(ips.netmask) def cidr_broadcast(cidr): - ''' + """ Get the broadcast address associated with a CIDR address. CLI example:: salt myminion netaddress.cidr_netmask 192.168.0.0/20 - ''' + """ ips = netaddr.IPNetwork(cidr) return six.text_type(ips.broadcast) diff --git a/salt/modules/netbox.py b/salt/modules/netbox.py index ff53fb030be..3271aa35fa2 100644 --- a/salt/modules/netbox.py +++ b/salt/modules/netbox.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NetBox ====== @@ -21,66 +21,62 @@ private key file: keyfile: .. versionadded:: 2018.3.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals -import re import logging +import re from salt.exceptions import CommandExecutionError try: import pynetbox + HAS_PYNETBOX = True except ImportError: HAS_PYNETBOX = False log = logging.getLogger(__name__) -AUTH_ENDPOINTS = ( - 'secrets', -) +AUTH_ENDPOINTS = ("secrets",) -__func_alias__ = { - 'filter_': 'filter', - 'get_': 'get' -} +__func_alias__ = {"filter_": "filter", "get_": "get"} def __virtual__(): - ''' + """ pynetbox must be installed. - ''' + """ if not HAS_PYNETBOX: return ( False, - 'The netbox execution module cannot be loaded: ' - 'pynetbox library is not installed.' + "The netbox execution module cannot be loaded: " + "pynetbox library is not installed.", ) else: return True def _config(): - config = __salt__['config.get']('netbox') + config = __salt__["config.get"]("netbox") if not config: raise CommandExecutionError( - 'NetBox execution module configuration could not be found' + "NetBox execution module configuration could not be found" ) return config def _nb_obj(auth_required=False): pynb_kwargs = {} - pynb_kwargs['token'] = _config().get('token') + pynb_kwargs["token"] = _config().get("token") if auth_required: - pynb_kwargs['private_key_file'] = _config().get('keyfile') - return pynetbox.api(_config().get('url'), **pynb_kwargs) + pynb_kwargs["private_key_file"] = _config().get("keyfile") + return pynetbox.api(_config().get("url"), **pynb_kwargs) def _strip_url_field(input_dict): - if 'url' in input_dict.keys(): - del input_dict['url'] + if "url" in input_dict.keys(): + del input_dict["url"] for k, v in input_dict.items(): if isinstance(v, dict): _strip_url_field(v) @@ -95,9 +91,9 @@ def _dict(iterable): def _add(app, endpoint, payload): - ''' + """ POST a payload - ''' + """ nb = _nb_obj(auth_required=True) try: return getattr(getattr(nb, app), endpoint).create(**payload) @@ -107,37 +103,37 @@ def _add(app, endpoint, payload): def slugify(value): - '''' + """' Slugify given value. Credit to Djangoproject https://docs.djangoproject.com/en/2.0/_modules/django/utils/text/#slugify - ''' - value = re.sub(r'[^\w\s-]', '', value).strip().lower() - return re.sub(r'[-\s]+', '-', value) + """ + value = re.sub(r"[^\w\s-]", "", value).strip().lower() + return re.sub(r"[-\s]+", "-", value) def _get(app, endpoint, id=None, auth_required=False, **kwargs): - ''' + """ Helper function to do a GET request to Netbox. Returns the actual pynetbox object, which allows manipulation from other functions. - ''' + """ nb = _nb_obj(auth_required=auth_required) if id: item = getattr(getattr(nb, app), endpoint).get(id) else: - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) item = getattr(getattr(nb, app), endpoint).get(**kwargs) return item def _if_name_unit(if_name): - if_name_split = if_name.split('.') + if_name_split = if_name.split(".") if len(if_name_split) == 2: return if_name_split - return if_name, '0' + return if_name, "0" def filter_(app, endpoint, **kwargs): - ''' + """ Get a list of items from NetBox. app @@ -155,11 +151,11 @@ def filter_(app, endpoint, **kwargs): .. code-block:: bash salt myminion netbox.filter dcim devices status=1 role=router - ''' + """ ret = [] nb = _nb_obj(auth_required=True if app in AUTH_ENDPOINTS else False) nb_query = getattr(getattr(nb, app), endpoint).filter( - **__utils__['args.clean_kwargs'](**kwargs) + **__utils__["args.clean_kwargs"](**kwargs) ) if nb_query: ret = [_strip_url_field(dict(i)) for i in nb_query] @@ -167,7 +163,7 @@ def filter_(app, endpoint, **kwargs): def get_(app, endpoint, id=None, **kwargs): - ''' + """ Get a single item from NetBox. app @@ -189,12 +185,20 @@ def get_(app, endpoint, id=None, **kwargs): .. code-block:: bash salt myminion netbox.get dcim devices name=my-router - ''' - return _dict(_get(app, endpoint, id=id, auth_required=True if app in AUTH_ENDPOINTS else False, **kwargs)) + """ + return _dict( + _get( + app, + endpoint, + id=id, + auth_required=True if app in AUTH_ENDPOINTS else False, + **kwargs + ) + ) def create_manufacturer(name): - ''' + """ .. versionadded:: 2019.2.0 Create a device manufacturer. @@ -207,21 +211,21 @@ def create_manufacturer(name): .. code-block:: bash salt myminion netbox.create_manufacturer Juniper - ''' - nb_man = get_('dcim', 'manufacturers', name=name) + """ + nb_man = get_("dcim", "manufacturers", name=name) if nb_man: return False else: - payload = {'name': name, 'slug': slugify(name)} - man = _add('dcim', 'manufacturers', payload) + payload = {"name": name, "slug": slugify(name)} + man = _add("dcim", "manufacturers", payload) if man: - return {'dcim': {'manufacturers': payload}} + return {"dcim": {"manufacturers": payload}} else: return False def create_device_type(model, manufacturer): - ''' + """ .. versionadded:: 2019.2.0 Create a device type. If the manufacturer doesn't exist, create a new manufacturer. @@ -236,19 +240,19 @@ def create_device_type(model, manufacturer): .. code-block:: bash salt myminion netbox.create_device_type MX480 Juniper - ''' - nb_type = get_('dcim', 'device-types', model=model) + """ + nb_type = get_("dcim", "device-types", model=model) if nb_type: return False - nb_man = get_('dcim', 'manufacturers', name=manufacturer) + nb_man = get_("dcim", "manufacturers", name=manufacturer) new_man = None if not nb_man: new_man = create_manufacturer(manufacturer) - payload = {'model': model, 'manufacturer': nb_man['id'], 'slug': slugify(model)} - typ = _add('dcim', 'device-types', payload) - ret_dict = {'dcim': {'device-types': payload}} + payload = {"model": model, "manufacturer": nb_man["id"], "slug": slugify(model)} + typ = _add("dcim", "device-types", payload) + ret_dict = {"dcim": {"device-types": payload}} if new_man: - ret_dict['dcim'].update(new_man['dcim']) + ret_dict["dcim"].update(new_man["dcim"]) if typ: return ret_dict else: @@ -256,7 +260,7 @@ def create_device_type(model, manufacturer): def create_device_role(role, color): - ''' + """ .. versionadded:: 2019.2.0 Create a device role @@ -269,21 +273,21 @@ def create_device_role(role, color): .. code-block:: bash salt myminion netbox.create_device_role router - ''' - nb_role = get_('dcim', 'device-roles', name=role) + """ + nb_role = get_("dcim", "device-roles", name=role) if nb_role: return False else: - payload = {'name': role, 'slug': slugify(role), 'color': color} - role = _add('dcim', 'device-roles', payload) + payload = {"name": role, "slug": slugify(role), "color": color} + role = _add("dcim", "device-roles", payload) if role: - return{'dcim': {'device-roles': payload}} + return {"dcim": {"device-roles": payload}} else: return False def create_platform(platform): - ''' + """ .. versionadded:: 2019.2.0 Create a new device platform @@ -296,21 +300,21 @@ def create_platform(platform): .. code-block:: bash salt myminion netbox.create_platform junos - ''' - nb_platform = get_('dcim', 'platforms', slug=slugify(platform)) + """ + nb_platform = get_("dcim", "platforms", slug=slugify(platform)) if nb_platform: return False else: - payload = {'name': platform, 'slug': slugify(platform)} - plat = _add('dcim', 'platforms', payload) + payload = {"name": platform, "slug": slugify(platform)} + plat = _add("dcim", "platforms", payload) if plat: - return {'dcim': {'platforms': payload}} + return {"dcim": {"platforms": payload}} else: return False def create_site(site): - ''' + """ .. versionadded:: 2019.2.0 Create a new device site @@ -323,25 +327,21 @@ def create_site(site): .. code-block:: bash salt myminion netbox.create_site BRU - ''' - nb_site = get_('dcim', 'sites', name=site) + """ + nb_site = get_("dcim", "sites", name=site) if nb_site: return False else: - payload = {'name': site, 'slug': slugify(site)} - site = _add('dcim', 'sites', payload) + payload = {"name": site, "slug": slugify(site)} + site = _add("dcim", "sites", payload) if site: - return {'dcim': {'sites': payload}} + return {"dcim": {"sites": payload}} else: return False -def create_device(name, - role, - model, - manufacturer, - site): - ''' +def create_device(name, role, model, manufacturer, site): + """ .. versionadded:: 2019.2.0 Create a new device with a name, role, model, manufacturer and site. @@ -363,35 +363,41 @@ def create_device(name, .. code-block:: bash salt myminion netbox.create_device edge_router router MX480 Juniper BRU - ''' + """ try: - nb_role = get_('dcim', 'device-roles', name=role) + nb_role = get_("dcim", "device-roles", name=role) if not nb_role: return False - nb_type = get_('dcim', 'device-types', model=model) + nb_type = get_("dcim", "device-types", model=model) if not nb_type: return False - nb_site = get_('dcim', 'sites', name=site) + nb_site = get_("dcim", "sites", name=site) if not nb_site: return False - status = {'label': "Active", 'value': 1} + status = {"label": "Active", "value": 1} except pynetbox.RequestError as e: log.error("{}, {}, {}".format(e.req.request.headers, e.request_body, e.error)) return False - payload = {'name': name, 'display_name': name, 'slug': slugify(name), 'device_type': nb_type['id'], - 'device_role': nb_role['id'], 'site': nb_site['id']} - new_dev = _add('dcim', 'devices', payload) + payload = { + "name": name, + "display_name": name, + "slug": slugify(name), + "device_type": nb_type["id"], + "device_role": nb_role["id"], + "site": nb_site["id"], + } + new_dev = _add("dcim", "devices", payload) if new_dev: - return {'dcim': {'devices': payload}} + return {"dcim": {"devices": payload}} else: return False def update_device(name, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Add attributes to an existing device, identified by name. @@ -406,26 +412,28 @@ def update_device(name, **kwargs): .. code-block:: bash salt myminion netbox.update_device edge_router serial=JN2932920 - ''' - kwargs = __utils__['args.clean_kwargs'](**kwargs) - nb_device = _get('dcim', 'devices', auth_required=True, name=name) + """ + kwargs = __utils__["args.clean_kwargs"](**kwargs) + nb_device = _get("dcim", "devices", auth_required=True, name=name) for k, v in kwargs.items(): setattr(nb_device, k, v) try: nb_device.save() - return {'dcim': {'devices': kwargs}} + return {"dcim": {"devices": kwargs}} except pynetbox.RequestError as e: log.error("{}, {}, {}".format(e.req.request.headers, e.request_body, e.error)) return False -def create_inventory_item(device_name, - item_name, - manufacturer_name=None, - serial='', - part_id='', - description=''): - ''' +def create_inventory_item( + device_name, + item_name, + manufacturer_name=None, + serial="", + part_id="", + description="", +): + """ .. versionadded:: 2019.2.0 Add an inventory item to an existing device. @@ -452,28 +460,34 @@ def create_inventory_item(device_name, .. code-block:: bash salt myminion netbox.create_inventory_item edge_router Transceiver part_id=740-01234 - ''' - nb_device = get_('dcim', 'devices', name=device_name) + """ + nb_device = get_("dcim", "devices", name=device_name) if not nb_device: return False if manufacturer_name: - nb_man = get_('dcim', 'manufacturers', name=manufacturer_name) + nb_man = get_("dcim", "manufacturers", name=manufacturer_name) if not nb_man: create_manufacturer(manufacturer_name) - nb_man = get_('dcim', 'manufacturers', name=manufacturer_name) - payload = {'device': nb_device['id'], 'name': item_name, 'description': description, 'serial': serial, - 'part_id': part_id, 'parent': None} + nb_man = get_("dcim", "manufacturers", name=manufacturer_name) + payload = { + "device": nb_device["id"], + "name": item_name, + "description": description, + "serial": serial, + "part_id": part_id, + "parent": None, + } if manufacturer_name: - payload['manufacturer'] = nb_man['id'] - done = _add('dcim', 'inventory-items', payload) + payload["manufacturer"] = nb_man["id"] + done = _add("dcim", "inventory-items", payload) if done: - return {'dcim': {'inventory-items': payload}} + return {"dcim": {"inventory-items": payload}} else: return done def delete_inventory_item(item_id): - ''' + """ .. versionadded:: 2019.2.0 Remove an item from a devices inventory. Identified by the netbox id @@ -486,14 +500,14 @@ def delete_inventory_item(item_id): .. code-block:: bash salt myminion netbox.delete_inventory_item 1354 - ''' - nb_inventory_item = _get('dcim', 'inventory-items', auth_required=True, id=item_id) + """ + nb_inventory_item = _get("dcim", "inventory-items", auth_required=True, id=item_id) nb_inventory_item.delete() - return {'DELETE': {'dcim': {'inventory-items': item_id}}} + return {"DELETE": {"dcim": {"inventory-items": item_id}}} def create_interface_connection(interface_a, interface_b): - ''' + """ .. versionadded:: 2019.2.0 Create an interface connection between 2 interfaces @@ -508,18 +522,17 @@ def create_interface_connection(interface_a, interface_b): .. code-block:: bash salt myminion netbox.create_interface_connection 123 456 - ''' - payload = {'interface_a': interface_a, - 'interface_b': interface_b} - ret = _add('dcim', 'interface-connections', payload) + """ + payload = {"interface_a": interface_a, "interface_b": interface_b} + ret = _add("dcim", "interface-connections", payload) if ret: - return {'dcim': {'interface-connections': {ret['id']: payload}}} + return {"dcim": {"interface-connections": {ret["id"]: payload}}} else: return ret def get_interfaces(device_name=None, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Returns interfaces for a specific device using arbitrary netbox filters @@ -535,18 +548,15 @@ def get_interfaces(device_name=None, **kwargs): salt myminion netbox.get_interfaces edge_router name="et-0/0/5" - ''' + """ if not device_name: - device_name = __opts__['id'] - netbox_device = get_('dcim', 'devices', name=device_name) - return filter_('dcim', - 'interfaces', - device_id=netbox_device['id'], - **kwargs) + device_name = __opts__["id"] + netbox_device = get_("dcim", "devices", name=device_name) + return filter_("dcim", "interfaces", device_id=netbox_device["id"], **kwargs) def openconfig_interfaces(device_name=None): - ''' + """ .. versionadded:: 2019.2.0 Return a dictionary structured as standardised in the @@ -564,84 +574,72 @@ def openconfig_interfaces(device_name=None): salt '*' netbox.openconfig_interfaces salt '*' netbox.openconfig_interfaces device_name=cr1.thn.lon - ''' + """ oc_if = {} interfaces = get_interfaces(device_name=device_name) ipaddresses = get_ipaddresses(device_name=device_name) for interface in interfaces: - if_name, if_unit = _if_name_unit(interface['name']) + if_name, if_unit = _if_name_unit(interface["name"]) if if_name not in oc_if: oc_if[if_name] = { - 'config': { - 'name': if_name - }, - 'subinterfaces': {'subinterface': {}} - } - if if_unit == '0': - oc_if[if_name]['config']['enabled'] = interface['enabled'] - if interface['description']: - if if_name == interface['name']: + "config": {"name": if_name}, + "subinterfaces": {"subinterface": {}}, + } + if if_unit == "0": + oc_if[if_name]["config"]["enabled"] = interface["enabled"] + if interface["description"]: + if if_name == interface["name"]: # When that's a real unit 0 interface # Otherwise it will inherit the description from the subif - oc_if[if_name]['config']['description'] = str(interface['description']) + oc_if[if_name]["config"]["description"] = str( + interface["description"] + ) else: subif_descr = { - 'subinterfaces': { - 'subinterface': { + "subinterfaces": { + "subinterface": { if_unit: { - 'config': { - 'description': str(interface['description']) + "config": { + "description": str(interface["description"]) } } } } } - oc_if[if_name] = __utils__['dictupdate.update'](oc_if[if_name], subif_descr) - if interface['mtu']: - oc_if[if_name]['config']['mtu'] = int(interface['mtu']) + oc_if[if_name] = __utils__["dictupdate.update"]( + oc_if[if_name], subif_descr + ) + if interface["mtu"]: + oc_if[if_name]["config"]["mtu"] = int(interface["mtu"]) else: - oc_if[if_name]['subinterfaces']['subinterface'][if_unit] = { - 'config': { - 'index': int(if_unit), - 'enabled': interface['enabled'] - } + oc_if[if_name]["subinterfaces"]["subinterface"][if_unit] = { + "config": {"index": int(if_unit), "enabled": interface["enabled"]} } - if interface['description']: - oc_if[if_name]['subinterfaces']['subinterface'][if_unit]['config']['description'] =\ - str(interface['description']) + if interface["description"]: + oc_if[if_name]["subinterfaces"]["subinterface"][if_unit]["config"][ + "description" + ] = str(interface["description"]) for ipaddress in ipaddresses: - ip, prefix_length = ipaddress['address'].split('/') - if_name = ipaddress['interface']['name'] + ip, prefix_length = ipaddress["address"].split("/") + if_name = ipaddress["interface"]["name"] if_name, if_unit = _if_name_unit(if_name) - ipvkey = 'ipv{}'.format(ipaddress['family']) - if if_unit not in oc_if[if_name]['subinterfaces']['subinterface']: - oc_if[if_name]['subinterfaces']['subinterface'][if_unit] = { - 'config': { - 'index': int(if_unit), - 'enabled': True - } + ipvkey = "ipv{}".format(ipaddress["family"]) + if if_unit not in oc_if[if_name]["subinterfaces"]["subinterface"]: + oc_if[if_name]["subinterfaces"]["subinterface"][if_unit] = { + "config": {"index": int(if_unit), "enabled": True} } - if ipvkey not in oc_if[if_name]['subinterfaces']['subinterface'][if_unit]: - oc_if[if_name]['subinterfaces']['subinterface'][if_unit][ipvkey] = { - 'addresses': { - 'address': {} - } + if ipvkey not in oc_if[if_name]["subinterfaces"]["subinterface"][if_unit]: + oc_if[if_name]["subinterfaces"]["subinterface"][if_unit][ipvkey] = { + "addresses": {"address": {}} } - oc_if[if_name]['subinterfaces']['subinterface'][if_unit][ipvkey]['addresses']['address'][ip] = { - 'config': { - 'ip': ip, - 'prefix_length': int(prefix_length) - } - } - return { - 'interfaces': { - 'interface': oc_if - } - } + oc_if[if_name]["subinterfaces"]["subinterface"][if_unit][ipvkey]["addresses"][ + "address" + ][ip] = {"config": {"ip": ip, "prefix_length": int(prefix_length)}} + return {"interfaces": {"interface": oc_if}} def openconfig_lacp(device_name=None): - ''' + """ .. versionadded:: 2019.2.0 Return a dictionary structured as standardised in the @@ -667,44 +665,38 @@ def openconfig_lacp(device_name=None): salt '*' netbox.openconfig_lacp salt '*' netbox.openconfig_lacp device_name=cr1.thn.lon - ''' + """ oc_lacp = {} interfaces = get_interfaces(device_name=device_name) for interface in interfaces: - if not interface['lag']: + if not interface["lag"]: continue - if_name, if_unit = _if_name_unit(interface['name']) - parent_if = interface['lag']['name'] + if_name, if_unit = _if_name_unit(interface["name"]) + parent_if = interface["lag"]["name"] if parent_if not in oc_lacp: oc_lacp[parent_if] = { - 'config': { - 'name': parent_if, - 'interval': 'SLOW', - 'lacp_mode': 'ACTIVE' + "config": { + "name": parent_if, + "interval": "SLOW", + "lacp_mode": "ACTIVE", }, - 'members': { - 'member': {} - } + "members": {"member": {}}, } - oc_lacp[parent_if]['members']['member'][if_name] = {} - return { - 'lacp': { - 'interfaces': { - 'interface': oc_lacp - } - } - } + oc_lacp[parent_if]["members"]["member"][if_name] = {} + return {"lacp": {"interfaces": {"interface": oc_lacp}}} -def create_interface(device_name, - interface_name, - mac_address=None, - description=None, - enabled=None, - lag=None, - lag_parent=None, - form_factor=None): - ''' +def create_interface( + device_name, + interface_name, + mac_address=None, + description=None, + enabled=None, + lag=None, + lag_parent=None, + form_factor=None, +): + """ .. versionadded:: 2019.2.0 Attach an interface to a device. If not all arguments are provided, @@ -732,41 +724,50 @@ def create_interface(device_name, .. code-block:: bash salt myminion netbox.create_interface edge_router ae13 description="Core uplink" - ''' - nb_device = get_('dcim', 'devices', name=device_name) + """ + nb_device = get_("dcim", "devices", name=device_name) if not nb_device: return False if lag_parent: - lag_interface = get_('dcim', 'interfaces', device_id=nb_device['id'], name=lag_parent) + lag_interface = get_( + "dcim", "interfaces", device_id=nb_device["id"], name=lag_parent + ) if not lag_interface: return False if not description: - description = '' + description = "" if not enabled: - enabled = 'false' + enabled = "false" # Set default form factor to 1200. This maps to SFP+ (10GE). This should be addressed by # the _choices endpoint. - payload = {'device': nb_device['id'], 'name': interface_name, - 'description': description, 'enabled': enabled, 'form_factor': 1200} + payload = { + "device": nb_device["id"], + "name": interface_name, + "description": description, + "enabled": enabled, + "form_factor": 1200, + } if form_factor is not None: - payload['form_factor'] = form_factor + payload["form_factor"] = form_factor if lag: - payload['form_factor'] = 200 + payload["form_factor"] = 200 if lag_parent: - payload['lag'] = lag_interface['id'] + payload["lag"] = lag_interface["id"] if mac_address: - payload['mac_address'] = mac_address - nb_interface = get_('dcim', 'interfaces', device_id=nb_device['id'], name=interface_name) + payload["mac_address"] = mac_address + nb_interface = get_( + "dcim", "interfaces", device_id=nb_device["id"], name=interface_name + ) if not nb_interface: - nb_interface = _add('dcim', 'interfaces', payload) + nb_interface = _add("dcim", "interfaces", payload) if nb_interface: - return {'dcim': {'interfaces': {nb_interface['id']: payload}}} + return {"dcim": {"interfaces": {nb_interface["id"]: payload}}} else: return nb_interface def update_interface(device_name, interface_name, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Update an existing interface with new attributes. @@ -783,26 +784,34 @@ def update_interface(device_name, interface_name, **kwargs): .. code-block:: bash salt myminion netbox.update_interface edge_router ae13 mac_address=50:87:69:53:32:D0 - ''' - nb_device = get_('dcim', 'devices', name=device_name) - nb_interface = _get('dcim', 'interfaces', auth_required=True, device_id=nb_device['id'], name=interface_name) + """ + nb_device = get_("dcim", "devices", name=device_name) + nb_interface = _get( + "dcim", + "interfaces", + auth_required=True, + device_id=nb_device["id"], + name=interface_name, + ) if not nb_device: return False if not nb_interface: return False else: - for k, v in __utils__['args.clean_kwargs'](**kwargs).items(): + for k, v in __utils__["args.clean_kwargs"](**kwargs).items(): setattr(nb_interface, k, v) try: nb_interface.save() - return {'dcim': {'interfaces': {nb_interface.id: dict(nb_interface)}}} + return {"dcim": {"interfaces": {nb_interface.id: dict(nb_interface)}}} except pynetbox.RequestError as e: - log.error("{}, {}, {}".format(e.req.request.headers, e.request_body, e.error)) + log.error( + "{}, {}, {}".format(e.req.request.headers, e.request_body, e.error) + ) return False def delete_interface(device_name, interface_name): - ''' + """ .. versionadded:: 2019.2.0 Delete an interface from a device. @@ -818,17 +827,25 @@ def delete_interface(device_name, interface_name): .. code-block:: bash salt myminion netbox.delete_interface edge_router ae13 - ''' - nb_device = get_('dcim', 'devices', name=device_name) - nb_interface = _get('dcim', 'interfaces', auth_required=True, device_id=nb_device['id'], name=interface_name) + """ + nb_device = get_("dcim", "devices", name=device_name) + nb_interface = _get( + "dcim", + "interfaces", + auth_required=True, + device_id=nb_device["id"], + name=interface_name, + ) if nb_interface: nb_interface.delete() - return {'DELETE': {'dcim': {'interfaces': {nb_interface.id: nb_interface.name}}}} + return { + "DELETE": {"dcim": {"interfaces": {nb_interface.id: nb_interface.name}}} + } return False def make_interface_lag(device_name, interface_name): - ''' + """ .. versionadded:: 2019.2.0 Update an interface to be a LAG. @@ -844,12 +861,12 @@ def make_interface_lag(device_name, interface_name): .. code-block:: bash salt myminion netbox.make_interface_lag edge_router ae13 - ''' + """ return update_interface(device_name, interface_name, form_factor=200) def make_interface_child(device_name, interface_name, parent_name): - ''' + """ .. versionadded:: 2019.2.0 Set an interface as part of a LAG. @@ -868,17 +885,17 @@ def make_interface_child(device_name, interface_name, parent_name): .. code-block:: bash salt myminion netbox.make_interface_child xe-1/0/2 ae13 - ''' - nb_device = get_('dcim', 'devices', name=device_name) - nb_parent = get_('dcim', 'interfaces', device_id=nb_device['id'], name=parent_name) + """ + nb_device = get_("dcim", "devices", name=device_name) + nb_parent = get_("dcim", "interfaces", device_id=nb_device["id"], name=parent_name) if nb_device and nb_parent: - return update_interface(device_name, interface_name, lag=nb_parent['id']) + return update_interface(device_name, interface_name, lag=nb_parent["id"]) else: return False def get_ipaddresses(device_name=None, **kwargs): - ''' + """ .. versionadded:: 2019.2.0 Filters for an IP address using specified filters @@ -893,18 +910,15 @@ def get_ipaddresses(device_name=None, **kwargs): .. code-block:: bash salt myminion netbox.get_ipaddresses device_name family=4 - ''' + """ if not device_name: - device_name = __opts__['id'] - netbox_device = get_('dcim', 'devices', name=device_name) - return filter_('ipam', - 'ip-addresses', - device_id=netbox_device['id'], - **kwargs) + device_name = __opts__["id"] + netbox_device = get_("dcim", "devices", name=device_name) + return filter_("ipam", "ip-addresses", device_id=netbox_device["id"], **kwargs) def create_ipaddress(ip_address, family, device=None, interface=None): - ''' + """ .. versionadded:: 2019.2.0 Add an IP address, and optionally attach it to an interface. @@ -923,31 +937,39 @@ def create_ipaddress(ip_address, family, device=None, interface=None): .. code-block:: bash salt myminion netbox.create_ipaddress 192.168.1.1/24 4 device=edge_router interface=ae13 - ''' + """ nb_addr = None - payload = {'family': family, 'address': ip_address} + payload = {"family": family, "address": ip_address} if interface and device: - nb_device = get_('dcim', 'devices', name=device) + nb_device = get_("dcim", "devices", name=device) if not nb_device: return False - nb_interface = get_('dcim', 'interfaces', device_id=nb_device['id'], name=interface) + nb_interface = get_( + "dcim", "interfaces", device_id=nb_device["id"], name=interface + ) if not nb_interface: return False - nb_addr = get_('ipam', 'ip-addresses', q=ip_address, interface_id=nb_interface['id'], family=family) + nb_addr = get_( + "ipam", + "ip-addresses", + q=ip_address, + interface_id=nb_interface["id"], + family=family, + ) if nb_addr: log.error(nb_addr) return False else: - payload['interface'] = nb_interface['id'] - ipaddr = _add('ipam', 'ip-addresses', payload) + payload["interface"] = nb_interface["id"] + ipaddr = _add("ipam", "ip-addresses", payload) if ipaddr: - return {'ipam': {'ip-addresses': payload}} + return {"ipam": {"ip-addresses": payload}} else: return ipaddr def delete_ipaddress(ipaddr_id): - ''' + """ .. versionadded:: 2019.2.0 Delete an IP address. IP addresses in Netbox are a combination of address @@ -961,17 +983,17 @@ def delete_ipaddress(ipaddr_id): .. code-block:: bash salt myminion netbox.delete_ipaddress 9002 - ''' + """ - nb_ipaddr = _get('ipam', 'ip-addresses', auth_required=True, id=ipaddr_id) + nb_ipaddr = _get("ipam", "ip-addresses", auth_required=True, id=ipaddr_id) if nb_ipaddr: nb_ipaddr.delete() - return {'DELETE': {'ipam': {'ip-address': ipaddr_id}}} + return {"DELETE": {"ipam": {"ip-address": ipaddr_id}}} return False def create_circuit_provider(name, asn=None): - ''' + """ .. versionadded:: 2019.2.0 Create a new Netbox circuit provider @@ -986,34 +1008,31 @@ def create_circuit_provider(name, asn=None): .. code-block:: bash salt myminion netbox.create_circuit_provider Telia 1299 - ''' + """ - nb_circuit_provider = get_('circuits', 'providers', name=name) + nb_circuit_provider = get_("circuits", "providers", name=name) payload = {} if nb_circuit_provider: - if nb_circuit_provider['asn'] == asn: + if nb_circuit_provider["asn"] == asn: return False else: - log.error('Duplicate provider with different ASN: %s: %s', name, asn) + log.error("Duplicate provider with different ASN: %s: %s", name, asn) raise CommandExecutionError( - 'Duplicate provider with different ASN: {}: {}'.format(name, asn) + "Duplicate provider with different ASN: {}: {}".format(name, asn) ) else: - payload = { - 'name': name, - 'slug': slugify(name) - } + payload = {"name": name, "slug": slugify(name)} if asn: - payload['asn'] = asn - circuit_provider = _add('circuits', 'providers', payload) + payload["asn"] = asn + circuit_provider = _add("circuits", "providers", payload) if circuit_provider: - return {'circuits': {'providers': {circuit_provider['id']: payload}}} + return {"circuits": {"providers": {circuit_provider["id"]: payload}}} else: return circuit_provider def get_circuit_provider(name, asn=None): - ''' + """ .. versionadded:: 2019.2.0 Get a circuit provider with a given name and optional ASN. @@ -1028,16 +1047,16 @@ def get_circuit_provider(name, asn=None): .. code-block:: bash salt myminion netbox.get_circuit_provider Telia 1299 - ''' + """ if asn: - nb_circuit_provider = get_('circuits', 'providers', asn=asn) + nb_circuit_provider = get_("circuits", "providers", asn=asn) else: - nb_circuit_provider = get_('circuits', 'providers', name=name) + nb_circuit_provider = get_("circuits", "providers", name=name) return nb_circuit_provider def create_circuit_type(name): - ''' + """ .. versionadded:: 2019.2.0 Create a new Netbox circuit type. @@ -1050,24 +1069,21 @@ def create_circuit_type(name): .. code-block:: bash salt myminion netbox.create_circuit_type Transit - ''' - nb_circuit_type = get_('circuits', 'circuit-types', slug=slugify(name)) + """ + nb_circuit_type = get_("circuits", "circuit-types", slug=slugify(name)) if nb_circuit_type: return False else: - payload = { - 'name': name, - 'slug': slugify(name) - } - circuit_type = _add('circuits', 'circuit-types', payload) + payload = {"name": name, "slug": slugify(name)} + circuit_type = _add("circuits", "circuit-types", payload) if circuit_type: - return {'circuits': {'circuit-types': {circuit_type['id']: payload}}} + return {"circuits": {"circuit-types": {circuit_type["id"]: payload}}} else: return circuit_type def create_circuit(name, provider_id, circuit_type, description=None): - ''' + """ .. versionadded:: 2019.2.0 Create a new Netbox circuit @@ -1088,32 +1104,34 @@ def create_circuit(name, provider_id, circuit_type, description=None): .. code-block:: bash salt myminion netbox.create_circuit NEW_CIRCUIT_01 Telia Transit 1299 "New Telia circuit" - ''' + """ - nb_circuit_provider = get_('circuits', 'providers', provider_id) - nb_circuit_type = get_('circuits', 'circuit-types', slug=slugify(circuit_type)) + nb_circuit_provider = get_("circuits", "providers", provider_id) + nb_circuit_type = get_("circuits", "circuit-types", slug=slugify(circuit_type)) if nb_circuit_provider and nb_circuit_type: payload = { - 'cid': name, - 'provider': nb_circuit_provider['id'], - 'type': nb_circuit_type['id'] + "cid": name, + "provider": nb_circuit_provider["id"], + "type": nb_circuit_type["id"], } if description: - payload['description'] = description - nb_circuit = get_('circuits', 'circuits', cid=name) + payload["description"] = description + nb_circuit = get_("circuits", "circuits", cid=name) if nb_circuit: return False - circuit = _add('circuits', 'circuits', payload) + circuit = _add("circuits", "circuits", payload) if circuit: - return {'circuits': {'circuits': {circuit['id']: payload}}} + return {"circuits": {"circuits": {circuit["id"]: payload}}} else: return circuit else: return False -def create_circuit_termination(circuit, interface, device, speed, xconnect_id=None, term_side='A'): - ''' +def create_circuit_termination( + circuit, interface, device, speed, xconnect_id=None, term_side="A" +): + """ .. versionadded:: 2019.2.0 Terminate a circuit on an interface @@ -1136,28 +1154,30 @@ def create_circuit_termination(circuit, interface, device, speed, xconnect_id=No .. code-block:: bash salt myminion netbox.create_circuit_termination NEW_CIRCUIT_01 xe-0/0/1 myminion 10000 xconnect_id=XCON01 - ''' + """ - nb_device = get_('dcim', 'devices', name=device) - nb_interface = get_('dcim', 'interfaces', device_id=nb_device['id'], name=interface) - nb_circuit = get_('circuits', 'circuits', cid=circuit) + nb_device = get_("dcim", "devices", name=device) + nb_interface = get_("dcim", "interfaces", device_id=nb_device["id"], name=interface) + nb_circuit = get_("circuits", "circuits", cid=circuit) if nb_circuit and nb_device: - nb_termination = get_('circuits', - 'circuit-terminations', - q=nb_circuit['cid']) + nb_termination = get_("circuits", "circuit-terminations", q=nb_circuit["cid"]) if nb_termination: return False payload = { - 'circuit': nb_circuit['id'], - 'interface': nb_interface['id'], - 'site': nb_device['site']['id'], - 'port_speed': speed, - 'term_side': term_side + "circuit": nb_circuit["id"], + "interface": nb_interface["id"], + "site": nb_device["site"]["id"], + "port_speed": speed, + "term_side": term_side, } if xconnect_id: - payload['xconnect_id'] = xconnect_id - circuit_termination = _add('circuits', 'circuit-terminations', payload) + payload["xconnect_id"] = xconnect_id + circuit_termination = _add("circuits", "circuit-terminations", payload) if circuit_termination: - return {'circuits': {'circuit-terminations': {circuit_termination['id']: payload}}} + return { + "circuits": { + "circuit-terminations": {circuit_termination["id"]: payload} + } + } else: return circuit_termination diff --git a/salt/modules/netbsd_sysctl.py b/salt/modules/netbsd_sysctl.py index 245162c5f7c..58c196006db 100644 --- a/salt/modules/netbsd_sysctl.py +++ b/salt/modules/netbsd_sysctl.py @@ -1,34 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Module for viewing and modifying sysctl parameters -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import os import re -# Import salt libs -from salt.ext import six import salt.utils.data import salt.utils.files import salt.utils.stringutils from salt.exceptions import CommandExecutionError +# Import salt libs +from salt.ext import six + # Define the module's virtual name -__virtualname__ = 'sysctl' +__virtualname__ = "sysctl" def __virtual__(): - ''' + """ Only run on NetBSD systems - ''' - if __grains__['os'] == 'NetBSD': + """ + if __grains__["os"] == "NetBSD": return __virtualname__ - return (False, 'The netbsd_sysctl execution module failed to load: ' - 'only available on NetBSD.') + return ( + False, + "The netbsd_sysctl execution module failed to load: " + "only available on NetBSD.", + ) def show(config_file=False): - ''' + """ Return a list of sysctl parameters for this minion CLI Example: @@ -36,38 +41,38 @@ def show(config_file=False): .. code-block:: bash salt '*' sysctl.show - ''' + """ roots = ( - 'kern', - 'vm', - 'vfs', - 'net', - 'hw', - 'machdep', - 'user', - 'ddb', - 'proc', - 'emul', - 'security', - 'init' + "kern", + "vm", + "vfs", + "net", + "hw", + "machdep", + "user", + "ddb", + "proc", + "emul", + "security", + "init", ) - cmd = 'sysctl -ae' + cmd = "sysctl -ae" ret = {} - out = __salt__['cmd.run'](cmd, output_loglevel='trace') - comps = [''] + out = __salt__["cmd.run"](cmd, output_loglevel="trace") + comps = [""] for line in out.splitlines(): - if any([line.startswith('{0}.'.format(root)) for root in roots]): - comps = re.split('[=:]', line, 1) + if any([line.startswith("{0}.".format(root)) for root in roots]): + comps = re.split("[=:]", line, 1) ret[comps[0]] = comps[1] elif comps[0]: - ret[comps[0]] += '{0}\n'.format(line) + ret[comps[0]] += "{0}\n".format(line) else: continue return ret def get(name): - ''' + """ Return a single sysctl parameter for this minion CLI Example: @@ -75,14 +80,14 @@ def get(name): .. code-block:: bash salt '*' sysctl.get hw.physmem - ''' - cmd = 'sysctl -n {0}'.format(name) - out = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = "sysctl -n {0}".format(name) + out = __salt__["cmd.run"](cmd, python_shell=False) return out def assign(name, value): - ''' + """ Assign a single sysctl parameter for this minion CLI Example: @@ -90,21 +95,20 @@ def assign(name, value): .. code-block:: bash salt '*' sysctl.assign net.inet.icmp.icmplim 50 - ''' + """ ret = {} cmd = 'sysctl -w {0}="{1}"'.format(name, value) - data = __salt__['cmd.run_all'](cmd, python_shell=False) + data = __salt__["cmd.run_all"](cmd, python_shell=False) - if data['retcode'] != 0: - raise CommandExecutionError('sysctl failed: {0}'.format( - data['stderr'])) - new_name, new_value = data['stdout'].split(':', 1) - ret[new_name] = new_value.split(' -> ')[-1] + if data["retcode"] != 0: + raise CommandExecutionError("sysctl failed: {0}".format(data["stderr"])) + new_name, new_value = data["stdout"].split(":", 1) + ret[new_name] = new_value.split(" -> ")[-1] return ret -def persist(name, value, config='/etc/sysctl.conf'): - ''' +def persist(name, value, config="/etc/sysctl.conf"): + """ Assign and persist a simple sysctl parameter for this minion CLI Example: @@ -112,7 +116,7 @@ def persist(name, value, config='/etc/sysctl.conf'): .. code-block:: bash salt '*' sysctl.persist net.inet.icmp.icmplim 50 - ''' + """ nlines = [] edited = False value = six.text_type(value) @@ -120,45 +124,44 @@ def persist(name, value, config='/etc/sysctl.conf'): # create /etc/sysctl.conf if not present if not os.path.isfile(config): try: - with salt.utils.files.fopen(config, 'w+'): + with salt.utils.files.fopen(config, "w+"): pass except (IOError, OSError): - msg = 'Could not create {0}' + msg = "Could not create {0}" raise CommandExecutionError(msg.format(config)) - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) - m = re.match(r'{0}(\??=)'.format(name), line) + m = re.match(r"{0}(\??=)".format(name), line) if not m: nlines.append(line) continue else: - key, rest = line.split('=', 1) + key, rest = line.split("=", 1) if rest.startswith('"'): _, rest_v, rest = rest.split('"', 2) - elif rest.startswith('\''): - _, rest_v, rest = rest.split('\'', 2) + elif rest.startswith("'"): + _, rest_v, rest = rest.split("'", 2) else: rest_v = rest.split()[0] - rest = rest[len(rest_v):] + rest = rest[len(rest_v) :] if rest_v == value: - return 'Already set' - new_line = '{0}{1}{2}{3}'.format(name, m.group(1), value, rest) + return "Already set" + new_line = "{0}{1}{2}{3}".format(name, m.group(1), value, rest) nlines.append(new_line) edited = True if not edited: - newline = '{0}={1}'.format(name, value) + newline = "{0}={1}".format(name, value) nlines.append("{0}\n".format(newline)) - with salt.utils.files.fopen(config, 'wb') as ofile: - ofile.writelines( - salt.utils.data.encode(nlines) - ) + with salt.utils.files.fopen(config, "wb") as ofile: + ofile.writelines(salt.utils.data.encode(nlines)) assign(name, value) - return 'Updated' + return "Updated" + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/netbsdservice.py b/salt/modules/netbsdservice.py index 2ccddc20432..6a77e0f196d 100644 --- a/salt/modules/netbsdservice.py +++ b/salt/modules/netbsdservice.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The service module for NetBSD .. important:: @@ -7,34 +7,36 @@ The service module for NetBSD minion, and it is using a different module (or gives an error similar to *'service.start' is not available*), see :ref:`here `. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import fnmatch +import glob + # Import python libs import os -import glob -import fnmatch import re -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" def __virtual__(): - ''' + """ Only work on NetBSD - ''' - if __grains__['os'] == 'NetBSD' and os.path.exists('/etc/rc.subr'): + """ + if __grains__["os"] == "NetBSD" and os.path.exists("/etc/rc.subr"): return __virtualname__ - return (False, 'The netbsdservice execution module failed to load: only available on NetBSD.') + return ( + False, + "The netbsdservice execution module failed to load: only available on NetBSD.", + ) def start(name): - ''' + """ Start the specified service CLI Example: @@ -42,13 +44,13 @@ def start(name): .. code-block:: bash salt '*' service.start - ''' - cmd = '/etc/rc.d/{0} onestart'.format(name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "/etc/rc.d/{0} onestart".format(name) + return not __salt__["cmd.retcode"](cmd) def stop(name): - ''' + """ Stop the specified service CLI Example: @@ -56,13 +58,13 @@ def stop(name): .. code-block:: bash salt '*' service.stop - ''' - cmd = '/etc/rc.d/{0} onestop'.format(name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "/etc/rc.d/{0} onestop".format(name) + return not __salt__["cmd.retcode"](cmd) def restart(name): - ''' + """ Restart the named service CLI Example: @@ -70,13 +72,13 @@ def restart(name): .. code-block:: bash salt '*' service.restart - ''' - cmd = '/etc/rc.d/{0} onerestart'.format(name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "/etc/rc.d/{0} onerestart".format(name) + return not __salt__["cmd.retcode"](cmd) def reload_(name): - ''' + """ Reload the named service CLI Example: @@ -84,13 +86,13 @@ def reload_(name): .. code-block:: bash salt '*' service.reload - ''' - cmd = '/etc/rc.d/{0} onereload'.format(name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "/etc/rc.d/{0} onereload".format(name) + return not __salt__["cmd.retcode"](cmd) def force_reload(name): - ''' + """ Force-reload the named service CLI Example: @@ -98,13 +100,13 @@ def force_reload(name): .. code-block:: bash salt '*' service.force_reload - ''' - cmd = '/etc/rc.d/{0} forcereload'.format(name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "/etc/rc.d/{0} forcereload".format(name) + return not __salt__["cmd.retcode"](cmd) def status(name, sig=None): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -125,35 +127,35 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status [service signature] - ''' + """ if sig: - return bool(__salt__['status.pid'](sig)) + return bool(__salt__["status.pid"](sig)) - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: services = [name] results = {} for service in services: - cmd = '/etc/rc.d/{0} onestatus'.format(service) - results[service] = not __salt__['cmd.retcode'](cmd, ignore_retcode=True) + cmd = "/etc/rc.d/{0} onestatus".format(service) + results[service] = not __salt__["cmd.retcode"](cmd, ignore_retcode=True) if contains_globbing: return results return results[name] def _get_svc(rcd, service_status): - ''' + """ Returns a unique service status - ''' + """ ena = None - lines = __salt__['cmd.run']('{0} rcvar'.format(rcd)).splitlines() + lines = __salt__["cmd.run"]("{0} rcvar".format(rcd)).splitlines() for rcvar in lines: - if rcvar.startswith('$') and '={0}'.format(service_status) in rcvar: - ena = 'yes' - elif rcvar.startswith('#'): - svc = rcvar.split(' ', 1)[1] + if rcvar.startswith("$") and "={0}".format(service_status) in rcvar: + ena = "yes" + elif rcvar.startswith("#"): + svc = rcvar.split(" ", 1)[1] else: continue @@ -163,12 +165,12 @@ def _get_svc(rcd, service_status): def _get_svc_list(service_status): - ''' + """ Returns all service statuses - ''' - prefix = '/etc/rc.d/' + """ + prefix = "/etc/rc.d/" ret = set() - lines = glob.glob('{0}*'.format(prefix)) + lines = glob.glob("{0}*".format(prefix)) for line in lines: svc = _get_svc(line, service_status) if svc is not None: @@ -178,7 +180,7 @@ def _get_svc_list(service_status): def get_enabled(): - ''' + """ Return a list of service that are enabled on boot CLI Example: @@ -186,12 +188,12 @@ def get_enabled(): .. code-block:: bash salt '*' service.get_enabled - ''' - return _get_svc_list('YES') + """ + return _get_svc_list("YES") def get_disabled(): - ''' + """ Return a set of services that are installed but disabled CLI Example: @@ -199,12 +201,12 @@ def get_disabled(): .. code-block:: bash salt '*' service.get_disabled - ''' - return _get_svc_list('NO') + """ + return _get_svc_list("NO") def available(name): - ''' + """ Returns ``True`` if the specified service is available, otherwise returns ``False``. @@ -213,12 +215,12 @@ def available(name): .. code-block:: bash salt '*' service.available sshd - ''' + """ return name in get_all() def missing(name): - ''' + """ The inverse of service.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -228,12 +230,12 @@ def missing(name): .. code-block:: bash salt '*' service.missing sshd - ''' + """ return name not in get_all() def get_all(): - ''' + """ Return all available boot services CLI Example: @@ -241,29 +243,29 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' - return _get_svc_list('') + """ + return _get_svc_list("") def _rcconf_status(name, service_status): - ''' + """ Modifies /etc/rc.conf so a service is started or not at boot time and can be started via /etc/rc.d/ - ''' - rcconf = '/etc/rc.conf' - rxname = '^{0}=.*'.format(name) - newstatus = '{0}={1}'.format(name, service_status) - ret = __salt__['cmd.retcode']('grep \'{0}\' {1}'.format(rxname, rcconf)) + """ + rcconf = "/etc/rc.conf" + rxname = "^{0}=.*".format(name) + newstatus = "{0}={1}".format(name, service_status) + ret = __salt__["cmd.retcode"]("grep '{0}' {1}".format(rxname, rcconf)) if ret == 0: # service found in rc.conf, modify its status - __salt__['file.replace'](rcconf, rxname, newstatus) + __salt__["file.replace"](rcconf, rxname, newstatus) else: - ret = __salt__['file.append'](rcconf, newstatus) + ret = __salt__["file.append"](rcconf, newstatus) return ret def enable(name, **kwargs): - ''' + """ Enable the named service to start at boot CLI Example: @@ -271,12 +273,12 @@ def enable(name, **kwargs): .. code-block:: bash salt '*' service.enable - ''' - return _rcconf_status(name, 'YES') + """ + return _rcconf_status(name, "YES") def disable(name, **kwargs): - ''' + """ Disable the named service to start at boot CLI Example: @@ -284,12 +286,12 @@ def disable(name, **kwargs): .. code-block:: bash salt '*' service.disable - ''' - return _rcconf_status(name, 'NO') + """ + return _rcconf_status(name, "NO") def enabled(name, **kwargs): - ''' + """ Return True if the named service is enabled, false otherwise CLI Example: @@ -297,12 +299,12 @@ def enabled(name, **kwargs): .. code-block:: bash salt '*' service.enabled - ''' - return _get_svc('/etc/rc.d/{0}'.format(name), 'YES') + """ + return _get_svc("/etc/rc.d/{0}".format(name), "YES") def disabled(name): - ''' + """ Return True if the named service is enabled, false otherwise CLI Example: @@ -310,8 +312,8 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled - ''' - return _get_svc('/etc/rc.d/{0}'.format(name), 'NO') + """ + return _get_svc("/etc/rc.d/{0}".format(name), "NO") # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/netmiko_mod.py b/salt/modules/netmiko_mod.py index 7402cb0c30a..33773af984c 100644 --- a/salt/modules/netmiko_mod.py +++ b/salt/modules/netmiko_mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Netmiko Execution Module ======================== @@ -181,22 +181,25 @@ outside a ``netmiko`` Proxy, e.g.: Remember that the above applies only when not running in a ``netmiko`` Proxy Minion. If you want to use the :mod:``, please follow the documentation notes for a proper setup. -''' +""" from __future__ import absolute_import +import inspect + # Import python stdlib import logging -import inspect + +from salt.exceptions import CommandExecutionError # Import Salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError from salt.utils.args import clean_kwargs # Import third party libs try: from netmiko import ConnectHandler from netmiko import BaseConnection + HAS_NETMIKO = True except ImportError: HAS_NETMIKO = False @@ -205,10 +208,10 @@ except ImportError: # execution module properties # ----------------------------------------------------------------------------- -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] # Any Proxy Minion should be able to execute these (not only netmiko) -__virtualname__ = 'netmiko' +__virtualname__ = "netmiko" # The Execution Module will be identified as ``netmiko`` # ----------------------------------------------------------------------------- @@ -223,28 +226,34 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Execution module available only if Netmiko is installed. - ''' + """ if not HAS_NETMIKO: - return False, 'The netmiko execution module requires netmiko library to be installed.' + return ( + False, + "The netmiko execution module requires netmiko library to be installed.", + ) return __virtualname__ + # ----------------------------------------------------------------------------- # helper functions # ----------------------------------------------------------------------------- def _prepare_connection(**kwargs): - ''' + """ Prepare the connection with the remote network device, and clean up the key value pairs, removing the args used for the connection init. - ''' + """ init_args = {} fun_kwargs = {} - netmiko_kwargs = __salt__['config.get']('netmiko', {}) + netmiko_kwargs = __salt__["config.get"]("netmiko", {}) netmiko_kwargs.update(kwargs) # merge the CLI args with the opts/pillar - netmiko_init_args, _, _, netmiko_defaults = inspect.getargspec(BaseConnection.__init__) + netmiko_init_args, _, _, netmiko_defaults = inspect.getargspec( + BaseConnection.__init__ + ) check_self = netmiko_init_args.pop(0) for karg, warg in six.iteritems(netmiko_kwargs): if karg not in netmiko_init_args: @@ -256,13 +265,14 @@ def _prepare_connection(**kwargs): conn = ConnectHandler(**init_args) return conn, fun_kwargs + # ----------------------------------------------------------------------------- # callable functions # ----------------------------------------------------------------------------- def get_connection(**kwargs): - ''' + """ Return the Netmiko connection object. .. warning:: @@ -283,16 +293,16 @@ def get_connection(**kwargs): password='example') show_if = conn.send_command('show interfaces') conn.disconnect() - ''' + """ kwargs = clean_kwargs(**kwargs) - if 'netmiko.conn' in __proxy__: - return __proxy__['netmiko.conn']() + if "netmiko.conn" in __proxy__: + return __proxy__["netmiko.conn"]() conn, kwargs = _prepare_connection(**kwargs) return conn def call(method, *args, **kwargs): - ''' + """ Invoke an arbitrary Netmiko method. method @@ -303,10 +313,10 @@ def call(method, *args, **kwargs): kwargs Key-value dictionary to send to the method invoked. - ''' + """ kwargs = clean_kwargs(**kwargs) - if 'netmiko.call' in __proxy__: - return __proxy__['netmiko.call'](method, *args, **kwargs) + if "netmiko.call" in __proxy__: + return __proxy__["netmiko.call"](method, *args, **kwargs) conn, kwargs = _prepare_connection(**kwargs) ret = getattr(conn, method)(*args, **kwargs) conn.disconnect() @@ -314,7 +324,7 @@ def call(method, *args, **kwargs): def multi_call(*methods, **kwargs): - ''' + """ Invoke multiple Netmiko methods at once, and return their output, as list. methods @@ -327,26 +337,26 @@ def multi_call(*methods, **kwargs): kwargs Key-value dictionary with the connection details (when not running under a Proxy Minion). - ''' + """ kwargs = clean_kwargs(**kwargs) - if 'netmiko.conn' in __proxy__: - conn = __proxy__['netmiko.conn']() + if "netmiko.conn" in __proxy__: + conn = __proxy__["netmiko.conn"]() else: conn, kwargs = _prepare_connection(**kwargs) ret = [] for method in methods: # Explicit unpacking - method_name = method['name'] - method_args = method.get('args', []) - method_kwargs = method.get('kwargs', []) + method_name = method["name"] + method_args = method.get("args", []) + method_kwargs = method.get("kwargs", []) ret.append(getattr(conn, method_name)(*method_args, **method_kwargs)) - if 'netmiko.conn' not in __proxy__: + if "netmiko.conn" not in __proxy__: conn.disconnect() return ret def send_command(command_string, **kwargs): - ''' + """ Execute command_string on the SSH channel using a pattern-based mechanism. Generally used for show commands. By default this method will keep waiting to receive data until the network device prompt is detected. The current @@ -387,12 +397,12 @@ def send_command(command_string, **kwargs): salt '*' netmiko.send_command 'show version' salt '*' netmiko.send_command 'show_version' host='router1.example.com' username='example' device_type='cisco_ios' - ''' - return call('send_command', command_string, **kwargs) + """ + return call("send_command", command_string, **kwargs) def send_command_timing(command_string, **kwargs): - ''' + """ Execute command_string on the SSH channel using a delay-based mechanism. Generally used for show commands. @@ -424,12 +434,12 @@ def send_command_timing(command_string, **kwargs): salt '*' netmiko.send_command_timing 'show version' salt '*' netmiko.send_command_timing 'show version' host='router1.example.com' username='example' device_type='arista_eos' - ''' - return call('send_command_timing', command_string, **kwargs) + """ + return call("send_command_timing", command_string, **kwargs) def enter_config_mode(**kwargs): - ''' + """ Enter into config mode. config_command @@ -444,12 +454,12 @@ def enter_config_mode(**kwargs): salt '*' netmiko.enter_config_mode salt '*' netmiko.enter_config_mode device_type='juniper_junos' ip='192.168.0.1' username='example' - ''' - return call('config_mode', **kwargs) + """ + return call("config_mode", **kwargs) def exit_config_mode(**kwargs): - ''' + """ Exit from configuration mode. exit_config @@ -464,19 +474,21 @@ def exit_config_mode(**kwargs): salt '*' netmiko.exit_config_mode salt '*' netmiko.exit_config_mode device_type='juniper' ip='192.168.0.1' username='example' - ''' - return call('exit_config_mode', **kwargs) + """ + return call("exit_config_mode", **kwargs) -def send_config(config_file=None, - config_commands=None, - template_engine='jinja', - commit=False, - context=None, - defaults=None, - saltenv='base', - **kwargs): - ''' +def send_config( + config_file=None, + config_commands=None, + template_engine="jinja", + commit=False, + context=None, + defaults=None, + saltenv="base", + **kwargs +): + """ Send configuration commands down the SSH channel. Return the configuration lines sent to the device. @@ -548,31 +560,29 @@ def send_config(config_file=None, salt '*' netmiko.send_config config_commands="['snmp-server location {{ grains.location }}']" salt '*' netmiko.send_config config_file=salt://config.txt salt '*' netmiko.send_config config_file=https://bit.ly/2sgljCB device_type='cisco_ios' ip='1.2.3.4' username='example' - ''' + """ if config_file: - file_str = __salt__['cp.get_file_str'](config_file, saltenv=saltenv) + file_str = __salt__["cp.get_file_str"](config_file, saltenv=saltenv) if file_str is False: - raise CommandExecutionError('Source file {} not found'.format(config_file)) + raise CommandExecutionError("Source file {} not found".format(config_file)) elif config_commands: if isinstance(config_commands, (six.string_types, six.text_type)): config_commands = [config_commands] - file_str = '\n'.join(config_commands) + file_str = "\n".join(config_commands) # unify all the commands in a single file, to render them in a go if template_engine: - file_str = __salt__['file.apply_template_on_contents'](file_str, - template_engine, - context, - defaults, - saltenv) + file_str = __salt__["file.apply_template_on_contents"]( + file_str, template_engine, context, defaults, saltenv + ) # whatever the source of the commands would be, split them line by line config_commands = [line for line in file_str.splitlines() if line.strip()] kwargs = clean_kwargs(**kwargs) - if 'netmiko.conn' in __proxy__: - conn = __proxy__['netmiko.conn']() + if "netmiko.conn" in __proxy__: + conn = __proxy__["netmiko.conn"]() else: conn, kwargs = _prepare_connection(**kwargs) if commit: - kwargs['exit_config_mode'] = False # don't exit config mode after + kwargs["exit_config_mode"] = False # don't exit config mode after # loading the commands, wait for explicit commit ret = conn.send_config_set(config_commands=config_commands, **kwargs) if commit: @@ -581,7 +591,7 @@ def send_config(config_file=None, def commit(**kwargs): - ''' + """ Commit the configuration changes. .. warning:: @@ -594,5 +604,5 @@ def commit(**kwargs): .. code-block:: bash salt '*' netmiko.commit - ''' - return call('commit', **kwargs) + """ + return call("commit", **kwargs) diff --git a/salt/modules/netscaler.py b/salt/modules/netscaler.py index f60ebb6bce2..4b053ab7555 100644 --- a/salt/modules/netscaler.py +++ b/salt/modules/netscaler.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide Citrix Netscaler compatibility to Salt (compatible with netscaler 9.2+) .. versionadded:: 2015.2.0 @@ -42,9 +42,10 @@ Module to provide Citrix Netscaler compatibility to Salt (compatible with netsca salt-call netscaler.server_enable server_name2 netscaler_host=1.2.3.5 salt-call netscaler.server_up server_name3 netscaler_host=1.2.3.6 netscaler_useSSL=False -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -56,10 +57,17 @@ try: from nsnitro.nsresources.nsserver import NSServer from nsnitro.nsresources.nsservice import NSService from nsnitro.nsresources.nsservicegroup import NSServiceGroup - from nsnitro.nsresources.nsservicegroupserverbinding import NSServiceGroupServerBinding + from nsnitro.nsresources.nsservicegroupserverbinding import ( + NSServiceGroupServerBinding, + ) from nsnitro.nsresources.nslbvserver import NSLBVServer - from nsnitro.nsresources.nslbvserverservicegroupbinding import NSLBVServerServiceGroupBinding - from nsnitro.nsresources.nssslvserversslcertkeybinding import NSSSLVServerSSLCertKeyBinding + from nsnitro.nsresources.nslbvserverservicegroupbinding import ( + NSLBVServerServiceGroupBinding, + ) + from nsnitro.nsresources.nssslvserversslcertkeybinding import ( + NSSSLVServerSSLCertKeyBinding, + ) + HAS_NSNITRO = True except ImportError: HAS_NSNITRO = False @@ -68,66 +76,68 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if the nsnitro library is installed - ''' + """ if salt.utils.platform.is_windows(): return ( False, - 'The netscaler execution module failed to load: not available ' - 'on Windows.' + "The netscaler execution module failed to load: not available " + "on Windows.", ) if HAS_NSNITRO: - return 'netscaler' + return "netscaler" return ( False, - 'The netscaler execution module failed to load: the nsnitro python ' - 'library is not available.' + "The netscaler execution module failed to load: the nsnitro python " + "library is not available.", ) def _connect(**kwargs): - ''' + """ Initialise netscaler connection - ''' + """ connargs = dict() # Shamelessy ripped from the mysql module def __connarg(name, key=None, default=None): - ''' + """ Add key to connargs, only if name exists in our kwargs or as netscaler. in __opts__ or __pillar__ Evaluate in said order - kwargs, opts then pillar. To avoid collision with other functions, kwargs-based connection arguments are prefixed with 'netscaler_' (i.e. 'netscaler_host', 'netscaler_user', etc.). - ''' + """ if key is None: key = name if name in kwargs: connargs[key] = kwargs[name] else: - prefix = 'netscaler_' + prefix = "netscaler_" if name.startswith(prefix): try: - name = name[len(prefix):] + name = name[len(prefix) :] except IndexError: return - val = __salt__['config.option']('netscaler.{0}'.format(name), None) + val = __salt__["config.option"]("netscaler.{0}".format(name), None) if val is not None: connargs[key] = val elif default is not None: connargs[key] = default - __connarg('netscaler_host', 'host') - __connarg('netscaler_user', 'user') - __connarg('netscaler_pass', 'pass') - __connarg('netscaler_useSSL', 'useSSL', True) + __connarg("netscaler_host", "host") + __connarg("netscaler_user", "user") + __connarg("netscaler_pass", "pass") + __connarg("netscaler_useSSL", "useSSL", True) - nitro = NSNitro(connargs['host'], connargs['user'], connargs['pass'], connargs['useSSL']) + nitro = NSNitro( + connargs["host"], connargs["user"], connargs["pass"], connargs["useSSL"] + ) try: nitro.login() except NSNitroError as error: - log.debug('netscaler module error - NSNitro.login() failed: %s', error) + log.debug("netscaler module error - NSNitro.login() failed: %s", error) return None return nitro @@ -136,15 +146,15 @@ def _disconnect(nitro): try: nitro.logout() except NSNitroError as error: - log.debug('netscaler module error - NSNitro.logout() failed: %s', error) + log.debug("netscaler module error - NSNitro.logout() failed: %s", error) return None return nitro def _servicegroup_get(sg_name, **connection_args): - ''' + """ Return a service group ressource or None - ''' + """ nitro = _connect(**connection_args) if nitro is None: return None @@ -153,16 +163,16 @@ def _servicegroup_get(sg_name, **connection_args): try: sg = NSServiceGroup.get(nitro, sg) except NSNitroError as error: - log.debug('netscaler module error - NSServiceGroup.get() failed: %s', error) + log.debug("netscaler module error - NSServiceGroup.get() failed: %s", error) sg = None _disconnect(nitro) return sg def _servicegroup_get_servers(sg_name, **connection_args): - ''' + """ Returns a list of members of a servicegroup or None - ''' + """ nitro = _connect(**connection_args) if nitro is None: return None @@ -171,16 +181,18 @@ def _servicegroup_get_servers(sg_name, **connection_args): try: sg = NSServiceGroup.get_servers(nitro, sg) except NSNitroError as error: - log.debug('netscaler module error - NSServiceGroup.get_servers failed(): %s', error) + log.debug( + "netscaler module error - NSServiceGroup.get_servers failed(): %s", error + ) sg = None _disconnect(nitro) return sg def _servicegroup_get_server(sg_name, s_name, s_port=None, **connection_args): - ''' + """ Returns a member of a service group or None - ''' + """ ret = None servers = _servicegroup_get_servers(sg_name, **connection_args) if servers is None: @@ -194,7 +206,7 @@ def _servicegroup_get_server(sg_name, s_name, s_port=None, **connection_args): def servicegroup_exists(sg_name, sg_type=None, **connection_args): - ''' + """ Checks if a service group exists CLI Example: @@ -202,7 +214,7 @@ def servicegroup_exists(sg_name, sg_type=None, **connection_args): .. code-block:: bash salt '*' netscaler.servicegroup_exists 'serviceGroupName' - ''' + """ sg = _servicegroup_get(sg_name, **connection_args) if sg is None: return False @@ -211,8 +223,8 @@ def servicegroup_exists(sg_name, sg_type=None, **connection_args): return True -def servicegroup_add(sg_name, sg_type='HTTP', **connection_args): - ''' +def servicegroup_add(sg_name, sg_type="HTTP", **connection_args): + """ Add a new service group If no service type is specified, HTTP will be used. Most common service types: HTTP, SSL, and SSL_BRIDGE @@ -223,7 +235,7 @@ def servicegroup_add(sg_name, sg_type='HTTP', **connection_args): salt '*' netscaler.servicegroup_add 'serviceGroupName' salt '*' netscaler.servicegroup_add 'serviceGroupName' 'serviceGroupType' - ''' + """ ret = True if servicegroup_exists(sg_name): return False @@ -236,14 +248,14 @@ def servicegroup_add(sg_name, sg_type='HTTP', **connection_args): try: NSServiceGroup.add(nitro, sg) except NSNitroError as error: - log.debug('netscaler module error - NSServiceGroup.add() failed: %s', error) + log.debug("netscaler module error - NSServiceGroup.add() failed: %s", error) ret = False _disconnect(nitro) return ret def servicegroup_delete(sg_name, **connection_args): - ''' + """ Delete a new service group CLI Example: @@ -251,7 +263,7 @@ def servicegroup_delete(sg_name, **connection_args): .. code-block:: bash salt '*' netscaler.servicegroup_delete 'serviceGroupName' - ''' + """ ret = True sg = _servicegroup_get(sg_name, **connection_args) if sg is None: @@ -262,14 +274,14 @@ def servicegroup_delete(sg_name, **connection_args): try: NSServiceGroup.delete(nitro, sg) except NSNitroError as error: - log.debug('netscaler module error - NSServiceGroup.delete() failed: %s', error) + log.debug("netscaler module error - NSServiceGroup.delete() failed: %s", error) ret = False _disconnect(nitro) return ret def servicegroup_server_exists(sg_name, s_name, s_port=None, **connection_args): - ''' + """ Check if a server:port combination is a member of a servicegroup CLI Example: @@ -277,12 +289,14 @@ def servicegroup_server_exists(sg_name, s_name, s_port=None, **connection_args): .. code-block:: bash salt '*' netscaler.servicegroup_server_exists 'serviceGroupName' 'serverName' 'serverPort' - ''' - return _servicegroup_get_server(sg_name, s_name, s_port, **connection_args) is not None + """ + return ( + _servicegroup_get_server(sg_name, s_name, s_port, **connection_args) is not None + ) def servicegroup_server_up(sg_name, s_name, s_port, **connection_args): - ''' + """ Check if a server:port combination is in state UP in a servicegroup CLI Example: @@ -290,13 +304,13 @@ def servicegroup_server_up(sg_name, s_name, s_port, **connection_args): .. code-block:: bash salt '*' netscaler.servicegroup_server_up 'serviceGroupName' 'serverName' 'serverPort' - ''' + """ server = _servicegroup_get_server(sg_name, s_name, s_port, **connection_args) - return server is not None and server.get_svrstate() == 'UP' + return server is not None and server.get_svrstate() == "UP" def servicegroup_server_enable(sg_name, s_name, s_port, **connection_args): - ''' + """ Enable a server:port member of a servicegroup CLI Example: @@ -304,7 +318,7 @@ def servicegroup_server_enable(sg_name, s_name, s_port, **connection_args): .. code-block:: bash salt '*' netscaler.servicegroup_server_enable 'serviceGroupName' 'serverName' 'serverPort' - ''' + """ ret = True server = _servicegroup_get_server(sg_name, s_name, s_port, **connection_args) if server is None: @@ -315,14 +329,16 @@ def servicegroup_server_enable(sg_name, s_name, s_port, **connection_args): try: NSServiceGroup.enable_server(nitro, server) except NSNitroError as error: - log.debug('netscaler module error - NSServiceGroup.enable_server() failed: %s', error) + log.debug( + "netscaler module error - NSServiceGroup.enable_server() failed: %s", error + ) ret = False _disconnect(nitro) return ret def servicegroup_server_disable(sg_name, s_name, s_port, **connection_args): - ''' + """ Disable a server:port member of a servicegroup CLI Example: @@ -330,7 +346,7 @@ def servicegroup_server_disable(sg_name, s_name, s_port, **connection_args): .. code-block:: bash salt '*' netscaler.servicegroup_server_disable 'serviceGroupName' 'serverName' 'serverPort' - ''' + """ ret = True server = _servicegroup_get_server(sg_name, s_name, s_port, **connection_args) if server is None: @@ -341,14 +357,16 @@ def servicegroup_server_disable(sg_name, s_name, s_port, **connection_args): try: NSServiceGroup.disable_server(nitro, server) except NSNitroError as error: - log.debug('netscaler module error - NSServiceGroup.disable_server() failed: %s', error) + log.debug( + "netscaler module error - NSServiceGroup.disable_server() failed: %s", error + ) ret = False _disconnect(nitro) return ret def servicegroup_server_add(sg_name, s_name, s_port, **connection_args): - ''' + """ Add a server:port member to a servicegroup CLI Example: @@ -356,7 +374,7 @@ def servicegroup_server_add(sg_name, s_name, s_port, **connection_args): .. code-block:: bash salt '*' netscaler.servicegroup_server_add 'serviceGroupName' 'serverName' 'serverPort' - ''' + """ # Nitro will throw an error if the server is already present ret = True server = _servicegroup_get_server(sg_name, s_name, s_port, **connection_args) @@ -372,14 +390,16 @@ def servicegroup_server_add(sg_name, s_name, s_port, **connection_args): try: NSServiceGroupServerBinding.add(nitro, sgsb) except NSNitroError as error: - log.debug('netscaler module error - NSServiceGroupServerBinding() failed: %s', error) + log.debug( + "netscaler module error - NSServiceGroupServerBinding() failed: %s", error + ) ret = False _disconnect(nitro) return ret def servicegroup_server_delete(sg_name, s_name, s_port, **connection_args): - ''' + """ Remove a server:port member from a servicegroup CLI Example: @@ -387,7 +407,7 @@ def servicegroup_server_delete(sg_name, s_name, s_port, **connection_args): .. code-block:: bash salt '*' netscaler.servicegroup_server_delete 'serviceGroupName' 'serverName' 'serverPort' - ''' + """ # Nitro will throw an error if the server is already not present ret = True server = _servicegroup_get_server(sg_name, s_name, s_port, **connection_args) @@ -403,16 +423,18 @@ def servicegroup_server_delete(sg_name, s_name, s_port, **connection_args): try: NSServiceGroupServerBinding.delete(nitro, sgsb) except NSNitroError as error: - log.debug('netscaler module error - NSServiceGroupServerBinding() failed: %s', error) + log.debug( + "netscaler module error - NSServiceGroupServerBinding() failed: %s", error + ) ret = False _disconnect(nitro) return ret def _service_get(s_name, **connection_args): - ''' + """ Returns a service ressource or None - ''' + """ nitro = _connect(**connection_args) if nitro is None: return None @@ -421,14 +443,14 @@ def _service_get(s_name, **connection_args): try: service = NSService.get(nitro, service) except NSNitroError as error: - log.debug('netscaler module error - NSService.get() failed: %s', error) + log.debug("netscaler module error - NSService.get() failed: %s", error) service = None _disconnect(nitro) return service def service_exists(s_name, **connection_args): - ''' + """ Checks if a service exists CLI Example: @@ -436,12 +458,12 @@ def service_exists(s_name, **connection_args): .. code-block:: bash salt '*' netscaler.service_exists 'serviceName' - ''' + """ return _service_get(s_name, **connection_args) is not None def service_up(s_name, **connection_args): - ''' + """ Checks if a service is UP CLI Example: @@ -449,13 +471,13 @@ def service_up(s_name, **connection_args): .. code-block:: bash salt '*' netscaler.service_up 'serviceName' - ''' + """ service = _service_get(s_name, **connection_args) - return service is not None and service.get_svrstate() == 'UP' + return service is not None and service.get_svrstate() == "UP" def service_enable(s_name, **connection_args): - ''' + """ Enable a service @@ -464,7 +486,7 @@ def service_enable(s_name, **connection_args): .. code-block:: bash salt '*' netscaler.service_enable 'serviceName' - ''' + """ ret = True service = _service_get(s_name, **connection_args) if service is None: @@ -475,14 +497,14 @@ def service_enable(s_name, **connection_args): try: NSService.enable(nitro, service) except NSNitroError as error: - log.debug('netscaler module error - NSService.enable() failed: %s', error) + log.debug("netscaler module error - NSService.enable() failed: %s", error) ret = False _disconnect(nitro) return ret def service_disable(s_name, s_delay=None, **connection_args): - ''' + """ Disable a service CLI Example: @@ -491,7 +513,7 @@ def service_disable(s_name, s_delay=None, **connection_args): salt '*' netscaler.service_disable 'serviceName' salt '*' netscaler.service_disable 'serviceName' 'delayInSeconds' - ''' + """ ret = True service = _service_get(s_name, **connection_args) if service is None: @@ -504,7 +526,7 @@ def service_disable(s_name, s_delay=None, **connection_args): try: NSService.disable(nitro, service) except NSNitroError as error: - log.debug('netscaler module error - NSService.enable() failed: %s', error) + log.debug("netscaler module error - NSService.enable() failed: %s", error) ret = False _disconnect(nitro) return ret @@ -519,14 +541,14 @@ def _server_get(s_name, **connection_args): try: server = NSServer.get(nitro, server) except NSNitroError as error: - log.debug('netscaler module error - NSServer.get() failed: %s', error) + log.debug("netscaler module error - NSServer.get() failed: %s", error) server = None _disconnect(nitro) return server def server_exists(s_name, ip=None, s_state=None, **connection_args): - ''' + """ Checks if a server exists CLI Example: @@ -534,7 +556,7 @@ def server_exists(s_name, ip=None, s_state=None, **connection_args): .. code-block:: bash salt '*' netscaler.server_exists 'serverName' - ''' + """ server = _server_get(s_name, **connection_args) if server is None: return False @@ -546,7 +568,7 @@ def server_exists(s_name, ip=None, s_state=None, **connection_args): def server_add(s_name, s_ip, s_state=None, **connection_args): - ''' + """ Add a server Note: The default server state is ENABLED @@ -556,7 +578,7 @@ def server_add(s_name, s_ip, s_state=None, **connection_args): salt '*' netscaler.server_add 'serverName' 'serverIpAddress' salt '*' netscaler.server_add 'serverName' 'serverIpAddress' 'serverState' - ''' + """ ret = True if server_exists(s_name, **connection_args): return False @@ -571,14 +593,14 @@ def server_add(s_name, s_ip, s_state=None, **connection_args): try: NSServer.add(nitro, server) except NSNitroError as error: - log.debug('netscaler module error - NSServer.add() failed: %s', error) + log.debug("netscaler module error - NSServer.add() failed: %s", error) ret = False _disconnect(nitro) return ret def server_delete(s_name, **connection_args): - ''' + """ Delete a server CLI Example: @@ -586,7 +608,7 @@ def server_delete(s_name, **connection_args): .. code-block:: bash salt '*' netscaler.server_delete 'serverName' - ''' + """ ret = True server = _server_get(s_name, **connection_args) if server is None: @@ -597,14 +619,14 @@ def server_delete(s_name, **connection_args): try: NSServer.delete(nitro, server) except NSNitroError as error: - log.debug('netscaler module error - NSServer.delete() failed: %s', error) + log.debug("netscaler module error - NSServer.delete() failed: %s", error) ret = False _disconnect(nitro) return ret def server_update(s_name, s_ip, **connection_args): - ''' + """ Update a server's attributes CLI Example: @@ -612,7 +634,7 @@ def server_update(s_name, s_ip, **connection_args): .. code-block:: bash salt '*' netscaler.server_update 'serverName' 'serverIP' - ''' + """ altered = False cur_server = _server_get(s_name, **connection_args) if cur_server is None: @@ -633,14 +655,14 @@ def server_update(s_name, s_ip, **connection_args): try: NSServer.update(nitro, alt_server) except NSNitroError as error: - log.debug('netscaler module error - NSServer.update() failed: %s', error) + log.debug("netscaler module error - NSServer.update() failed: %s", error) ret = False _disconnect(nitro) return ret def server_enabled(s_name, **connection_args): - ''' + """ Check if a server is enabled globally CLI Example: @@ -648,13 +670,13 @@ def server_enabled(s_name, **connection_args): .. code-block:: bash salt '*' netscaler.server_enabled 'serverName' - ''' + """ server = _server_get(s_name, **connection_args) - return server is not None and server.get_state() == 'ENABLED' + return server is not None and server.get_state() == "ENABLED" def server_enable(s_name, **connection_args): - ''' + """ Enables a server globally CLI Example: @@ -662,12 +684,12 @@ def server_enable(s_name, **connection_args): .. code-block:: bash salt '*' netscaler.server_enable 'serverName' - ''' + """ ret = True server = _server_get(s_name, **connection_args) if server is None: return False - if server.get_state() == 'ENABLED': + if server.get_state() == "ENABLED": return True nitro = _connect(**connection_args) if nitro is None: @@ -675,14 +697,14 @@ def server_enable(s_name, **connection_args): try: NSServer.enable(nitro, server) except NSNitroError as error: - log.debug('netscaler module error - NSServer.enable() failed: %s', error) + log.debug("netscaler module error - NSServer.enable() failed: %s", error) ret = False _disconnect(nitro) return ret def server_disable(s_name, **connection_args): - ''' + """ Disable a server globally CLI Example: @@ -690,12 +712,12 @@ def server_disable(s_name, **connection_args): .. code-block:: bash salt '*' netscaler.server_disable 'serverName' - ''' + """ ret = True server = _server_get(s_name, **connection_args) if server is None: return False - if server.get_state() == 'DISABLED': + if server.get_state() == "DISABLED": return True nitro = _connect(**connection_args) if nitro is None: @@ -703,7 +725,7 @@ def server_disable(s_name, **connection_args): try: NSServer.disable(nitro, server) except NSNitroError as error: - log.debug('netscaler module error - NSServer.disable() failed: %s', error) + log.debug("netscaler module error - NSServer.disable() failed: %s", error) ret = False _disconnect(nitro) return ret @@ -718,14 +740,14 @@ def _vserver_get(v_name, **connection_args): try: vserver = NSLBVServer.get(nitro, vserver) except NSNitroError as error: - log.debug('netscaler module error - NSLBVServer.get() failed: %s', error) + log.debug("netscaler module error - NSLBVServer.get() failed: %s", error) vserver = None _disconnect(nitro) return vserver def vserver_exists(v_name, v_ip=None, v_port=None, v_type=None, **connection_args): - ''' + """ Checks if a vserver exists CLI Example: @@ -733,7 +755,7 @@ def vserver_exists(v_name, v_ip=None, v_port=None, v_type=None, **connection_arg .. code-block:: bash salt '*' netscaler.vserver_exists 'vserverName' - ''' + """ vserver = _vserver_get(v_name, **connection_args) if vserver is None: return False @@ -747,7 +769,7 @@ def vserver_exists(v_name, v_ip=None, v_port=None, v_type=None, **connection_arg def vserver_add(v_name, v_ip, v_port, v_type, **connection_args): - ''' + """ Add a new lb vserver CLI Example: @@ -756,7 +778,7 @@ def vserver_add(v_name, v_ip, v_port, v_type, **connection_args): salt '*' netscaler.vserver_add 'vserverName' 'vserverIP' 'vserverPort' 'vserverType' salt '*' netscaler.vserver_add 'alex.patate.chaude.443' '1.2.3.4' '443' 'SSL' - ''' + """ ret = True if vserver_exists(v_name, **connection_args): return False @@ -771,14 +793,14 @@ def vserver_add(v_name, v_ip, v_port, v_type, **connection_args): try: NSLBVServer.add(nitro, vserver) except NSNitroError as error: - log.debug('netscaler module error - NSLBVServer.add() failed: %s', error) + log.debug("netscaler module error - NSLBVServer.add() failed: %s", error) ret = False _disconnect(nitro) return ret def vserver_delete(v_name, **connection_args): - ''' + """ Delete a lb vserver CLI Example: @@ -786,7 +808,7 @@ def vserver_delete(v_name, **connection_args): .. code-block:: bash salt '*' netscaler.vserver_delete 'vserverName' - ''' + """ ret = True vserver = _vserver_get(v_name, **connection_args) if vserver is None: @@ -797,7 +819,7 @@ def vserver_delete(v_name, **connection_args): try: NSLBVServer.delete(nitro, vserver) except NSNitroError as error: - log.debug('netscaler module error - NSVServer.delete() failed: %s', error) + log.debug("netscaler module error - NSVServer.delete() failed: %s", error) ret = False _disconnect(nitro) return ret @@ -813,7 +835,10 @@ def _vserver_servicegroup_get(v_name, sg_name, **connection_args): try: vsgs = NSLBVServerServiceGroupBinding.get(nitro, vsg) except NSNitroError as error: - log.debug('netscaler module error - NSLBVServerServiceGroupBinding.get() failed: %s', error) + log.debug( + "netscaler module error - NSLBVServerServiceGroupBinding.get() failed: %s", + error, + ) return None for vsg in vsgs: if vsg.get_servicegroupname() == sg_name: @@ -823,7 +848,7 @@ def _vserver_servicegroup_get(v_name, sg_name, **connection_args): def vserver_servicegroup_exists(v_name, sg_name, **connection_args): - ''' + """ Checks if a servicegroup is tied to a vserver CLI Example: @@ -831,12 +856,12 @@ def vserver_servicegroup_exists(v_name, sg_name, **connection_args): .. code-block:: bash salt '*' netscaler.vserver_servicegroup_exists 'vserverName' 'serviceGroupName' - ''' + """ return _vserver_servicegroup_get(v_name, sg_name, **connection_args) is not None def vserver_servicegroup_add(v_name, sg_name, **connection_args): - ''' + """ Bind a servicegroup to a vserver CLI Example: @@ -844,7 +869,7 @@ def vserver_servicegroup_add(v_name, sg_name, **connection_args): .. code-block:: bash salt '*' netscaler.vserver_servicegroup_add 'vserverName' 'serviceGroupName' - ''' + """ ret = True if vserver_servicegroup_exists(v_name, sg_name, **connection_args): return False @@ -857,14 +882,17 @@ def vserver_servicegroup_add(v_name, sg_name, **connection_args): try: NSLBVServerServiceGroupBinding.add(nitro, vsg) except NSNitroError as error: - log.debug('netscaler module error - NSLBVServerServiceGroupBinding.add() failed: %s', error) + log.debug( + "netscaler module error - NSLBVServerServiceGroupBinding.add() failed: %s", + error, + ) ret = False _disconnect(nitro) return ret def vserver_servicegroup_delete(v_name, sg_name, **connection_args): - ''' + """ Unbind a servicegroup from a vserver CLI Example: @@ -872,7 +900,7 @@ def vserver_servicegroup_delete(v_name, sg_name, **connection_args): .. code-block:: bash salt '*' netscaler.vserver_servicegroup_delete 'vserverName' 'serviceGroupName' - ''' + """ ret = True if not vserver_servicegroup_exists(v_name, sg_name, **connection_args): return False @@ -885,7 +913,10 @@ def vserver_servicegroup_delete(v_name, sg_name, **connection_args): try: NSLBVServerServiceGroupBinding.delete(nitro, vsg) except NSNitroError as error: - log.debug('netscaler module error - NSLBVServerServiceGroupBinding.delete() failed: %s', error) + log.debug( + "netscaler module error - NSLBVServerServiceGroupBinding.delete() failed: %s", + error, + ) ret = False _disconnect(nitro) return ret @@ -901,7 +932,10 @@ def _vserver_sslcert_get(v_name, sc_name, **connection_args): try: sslcerts = NSSSLVServerSSLCertKeyBinding.get(nitro, sslcert) except NSNitroError as error: - log.debug('netscaler module error - NSSSLVServerSSLCertKeyBinding.get() failed: %s', error) + log.debug( + "netscaler module error - NSSSLVServerSSLCertKeyBinding.get() failed: %s", + error, + ) return None for sslcert in sslcerts: if sslcert.get_certkeyname() == sc_name: @@ -910,7 +944,7 @@ def _vserver_sslcert_get(v_name, sc_name, **connection_args): def vserver_sslcert_exists(v_name, sc_name, **connection_args): - ''' + """ Checks if a SSL certificate is tied to a vserver CLI Example: @@ -918,12 +952,12 @@ def vserver_sslcert_exists(v_name, sc_name, **connection_args): .. code-block:: bash salt '*' netscaler.vserver_sslcert_exists 'vserverName' 'sslCertificateName' - ''' + """ return _vserver_sslcert_get(v_name, sc_name, **connection_args) is not None def vserver_sslcert_add(v_name, sc_name, **connection_args): - ''' + """ Binds a SSL certificate to a vserver CLI Example: @@ -931,7 +965,7 @@ def vserver_sslcert_add(v_name, sc_name, **connection_args): .. code-block:: bash salt '*' netscaler.vserver_sslcert_add 'vserverName' 'sslCertificateName' - ''' + """ ret = True if vserver_sslcert_exists(v_name, sc_name, **connection_args): return False @@ -944,14 +978,17 @@ def vserver_sslcert_add(v_name, sc_name, **connection_args): try: NSSSLVServerSSLCertKeyBinding.add(nitro, sslcert) except NSNitroError as error: - log.debug('netscaler module error - NSSSLVServerSSLCertKeyBinding.add() failed: %s', error) + log.debug( + "netscaler module error - NSSSLVServerSSLCertKeyBinding.add() failed: %s", + error, + ) ret = False _disconnect(nitro) return ret def vserver_sslcert_delete(v_name, sc_name, **connection_args): - ''' + """ Unbinds a SSL certificate from a vserver CLI Example: @@ -959,7 +996,7 @@ def vserver_sslcert_delete(v_name, sc_name, **connection_args): .. code-block:: bash salt '*' netscaler.vserver_sslcert_delete 'vserverName' 'sslCertificateName' - ''' + """ ret = True if not vserver_sslcert_exists(v_name, sc_name, **connection_args): return False @@ -972,7 +1009,10 @@ def vserver_sslcert_delete(v_name, sc_name, **connection_args): try: NSSSLVServerSSLCertKeyBinding.delete(nitro, sslcert) except NSNitroError as error: - log.debug('netscaler module error - NSSSLVServerSSLCertKeyBinding.delete() failed: %s', error) + log.debug( + "netscaler module error - NSSSLVServerSSLCertKeyBinding.delete() failed: %s", + error, + ) ret = False _disconnect(nitro) return ret diff --git a/salt/modules/network.py b/salt/modules/network.py index 38e2bc326ec..8b12f27a85e 100644 --- a/salt/modules/network.py +++ b/salt/modules/network.py @@ -1,48 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" Module for gathering and managing network information -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import datetime import hashlib import logging -import re import os +import re import socket # Import salt libs import salt.utils.decorators.path -import salt.utils.functools import salt.utils.files +import salt.utils.functools import salt.utils.network import salt.utils.path import salt.utils.platform import salt.utils.stringutils import salt.utils.validate.net +from salt._compat import ipaddress from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt._compat import ipaddress +from salt.ext.six.moves import range log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ # Disable on Windows, a specific file module exists: if salt.utils.platform.is_windows(): - return (False, 'The network execution module cannot be loaded on Windows: use win_network instead.') + return ( + False, + "The network execution module cannot be loaded on Windows: use win_network instead.", + ) return True -def wol(mac, bcast='255.255.255.255', destport=9): - ''' +def wol(mac, bcast="255.255.255.255", destport=9): + """ Send Wake On Lan packet to a host CLI Example: @@ -52,16 +56,16 @@ def wol(mac, bcast='255.255.255.255', destport=9): salt '*' network.wol 08-00-27-13-69-77 salt '*' network.wol 080027136977 255.255.255.255 7 salt '*' network.wol 08:00:27:13:69:77 255.255.255.255 7 - ''' + """ dest = salt.utils.network.mac_str_to_bytes(mac) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - sock.sendto(b'\xff' * 6 + dest * 16, (bcast, int(destport))) + sock.sendto(b"\xff" * 6 + dest * 16, (bcast, int(destport))) return True def ping(host, timeout=False, return_boolean=False): - ''' + """ Performs an ICMP ping to a host .. versionchanged:: 2015.8.0 @@ -86,80 +90,90 @@ def ping(host, timeout=False, return_boolean=False): .. code-block:: bash salt '*' network.ping archlinux.org timeout=3 - ''' + """ if timeout: - if __grains__['kernel'] == 'SunOS': - cmd = 'ping -c 4 {1} {0}'.format(timeout, salt.utils.network.sanitize_host(host)) + if __grains__["kernel"] == "SunOS": + cmd = "ping -c 4 {1} {0}".format( + timeout, salt.utils.network.sanitize_host(host) + ) else: - cmd = 'ping -W {0} -c 4 {1}'.format(timeout, salt.utils.network.sanitize_host(host)) + cmd = "ping -W {0} -c 4 {1}".format( + timeout, salt.utils.network.sanitize_host(host) + ) else: - cmd = 'ping -c 4 {0}'.format(salt.utils.network.sanitize_host(host)) + cmd = "ping -c 4 {0}".format(salt.utils.network.sanitize_host(host)) if return_boolean: - ret = __salt__['cmd.run_all'](cmd) - if ret['retcode'] != 0: + ret = __salt__["cmd.run_all"](cmd) + if ret["retcode"] != 0: return False else: return True else: - return __salt__['cmd.run'](cmd) + return __salt__["cmd.run"](cmd) # FIXME: Does not work with: netstat 1.42 (2001-04-15) from net-tools # 1.6.0 (Ubuntu 10.10) def _netstat_linux(): - ''' + """ Return netstat information for Linux distros - ''' + """ ret = [] - cmd = 'netstat -tulpnea' - out = __salt__['cmd.run'](cmd) + cmd = "netstat -tulpnea" + out = __salt__["cmd.run"](cmd) for line in out.splitlines(): comps = line.split() - if line.startswith('tcp'): - ret.append({ - 'proto': comps[0], - 'recv-q': comps[1], - 'send-q': comps[2], - 'local-address': comps[3], - 'remote-address': comps[4], - 'state': comps[5], - 'user': comps[6], - 'inode': comps[7], - 'program': comps[8]}) - if line.startswith('udp'): - ret.append({ - 'proto': comps[0], - 'recv-q': comps[1], - 'send-q': comps[2], - 'local-address': comps[3], - 'remote-address': comps[4], - 'user': comps[5], - 'inode': comps[6], - 'program': comps[7]}) + if line.startswith("tcp"): + ret.append( + { + "proto": comps[0], + "recv-q": comps[1], + "send-q": comps[2], + "local-address": comps[3], + "remote-address": comps[4], + "state": comps[5], + "user": comps[6], + "inode": comps[7], + "program": comps[8], + } + ) + if line.startswith("udp"): + ret.append( + { + "proto": comps[0], + "recv-q": comps[1], + "send-q": comps[2], + "local-address": comps[3], + "remote-address": comps[4], + "user": comps[5], + "inode": comps[6], + "program": comps[7], + } + ) return ret def _ss_linux(): - ''' + """ Return ss information for Linux distros (netstat is deprecated and may not be available) - ''' + """ ret = [] - cmd = 'ss -tulpnea' - out = __salt__['cmd.run'](cmd) + cmd = "ss -tulpnea" + out = __salt__["cmd.run"](cmd) for line in out.splitlines(): comps = line.split() ss_user = 0 ss_inode = 0 - ss_program = '' + ss_program = "" length = len(comps) - if line.startswith('tcp') or line.startswith('udp'): + if line.startswith("tcp") or line.startswith("udp"): i = 6 while i < (length - 1): fields = comps[i].split(":") if fields[0] == "users": users = fields[1].split(",") - ss_program = users[0].split("\"")[1] + ss_program = users[0].split('"')[1] if fields[0] == "uid": ss_user = fields[1] @@ -169,43 +183,49 @@ def _ss_linux(): i += 1 - if line.startswith('tcp'): + if line.startswith("tcp"): ss_state = comps[1] if ss_state == "ESTAB": ss_state = "ESTABLISHED" - ret.append({ - 'proto': comps[0], - 'recv-q': comps[2], - 'send-q': comps[3], - 'local-address': comps[4], - 'remote-address': comps[5], - 'state': ss_state, - 'user': ss_user, - 'inode': ss_inode, - 'program': ss_program}) - if line.startswith('udp'): - ret.append({ - 'proto': comps[0], - 'recv-q': comps[2], - 'send-q': comps[3], - 'local-address': comps[4], - 'remote-address': comps[5], - 'user': ss_user, - 'inode': ss_inode, - 'program': ss_program}) + ret.append( + { + "proto": comps[0], + "recv-q": comps[2], + "send-q": comps[3], + "local-address": comps[4], + "remote-address": comps[5], + "state": ss_state, + "user": ss_user, + "inode": ss_inode, + "program": ss_program, + } + ) + if line.startswith("udp"): + ret.append( + { + "proto": comps[0], + "recv-q": comps[2], + "send-q": comps[3], + "local-address": comps[4], + "remote-address": comps[5], + "user": ss_user, + "inode": ss_inode, + "program": ss_program, + } + ) return ret def _netinfo_openbsd(): - ''' + """ Get process information for network connections using fstat - ''' + """ ret = {} _fstat_re = re.compile( - r'internet(6)? (?:stream tcp 0x\S+ (\S+)|dgram udp (\S+))' - r'(?: [<>=-]+ (\S+))?$' + r"internet(6)? (?:stream tcp 0x\S+ (\S+)|dgram udp (\S+))" + r"(?: [<>=-]+ (\S+))?$" ) - out = __salt__['cmd.run']('fstat') + out = __salt__["cmd.run"]("fstat") for line in out.splitlines(): try: user, cmd, pid, _, details = line.split(None, 4) @@ -217,66 +237,63 @@ def _netinfo_openbsd(): continue if tcp: local_addr = tcp - proto = 'tcp{0}'.format('' if ipv6 is None else ipv6) + proto = "tcp{0}".format("" if ipv6 is None else ipv6) else: local_addr = udp - proto = 'udp{0}'.format('' if ipv6 is None else ipv6) + proto = "udp{0}".format("" if ipv6 is None else ipv6) if ipv6: # IPv6 addresses have the address part enclosed in brackets (if the # address part is not a wildcard) to distinguish the address from # the port number. Remove them. - local_addr = ''.join(x for x in local_addr if x not in '[]') + local_addr = "".join(x for x in local_addr if x not in "[]") # Normalize to match netstat output - local_addr = '.'.join(local_addr.rsplit(':', 1)) + local_addr = ".".join(local_addr.rsplit(":", 1)) if remote_addr is None: - remote_addr = '*.*' + remote_addr = "*.*" else: - remote_addr = '.'.join(remote_addr.rsplit(':', 1)) + remote_addr = ".".join(remote_addr.rsplit(":", 1)) - ret.setdefault( - local_addr, {}).setdefault( - remote_addr, {}).setdefault( - proto, {}).setdefault( - pid, {})['user'] = user - ret[local_addr][remote_addr][proto][pid]['cmd'] = cmd + ret.setdefault(local_addr, {}).setdefault(remote_addr, {}).setdefault( + proto, {} + ).setdefault(pid, {})["user"] = user + ret[local_addr][remote_addr][proto][pid]["cmd"] = cmd return ret def _netinfo_freebsd_netbsd(): - ''' + """ Get process information for network connections using sockstat - ''' + """ ret = {} # NetBSD requires '-n' to disable port-to-service resolution - out = __salt__['cmd.run']( - 'sockstat -46 {0} | tail -n+2'.format( - '-n' if __grains__['kernel'] == 'NetBSD' else '' - ), python_shell=True + out = __salt__["cmd.run"]( + "sockstat -46 {0} | tail -n+2".format( + "-n" if __grains__["kernel"] == "NetBSD" else "" + ), + python_shell=True, ) for line in out.splitlines(): user, cmd, pid, _, proto, local_addr, remote_addr = line.split() - local_addr = '.'.join(local_addr.rsplit(':', 1)) - remote_addr = '.'.join(remote_addr.rsplit(':', 1)) - ret.setdefault( - local_addr, {}).setdefault( - remote_addr, {}).setdefault( - proto, {}).setdefault( - pid, {})['user'] = user - ret[local_addr][remote_addr][proto][pid]['cmd'] = cmd + local_addr = ".".join(local_addr.rsplit(":", 1)) + remote_addr = ".".join(remote_addr.rsplit(":", 1)) + ret.setdefault(local_addr, {}).setdefault(remote_addr, {}).setdefault( + proto, {} + ).setdefault(pid, {})["user"] = user + ret[local_addr][remote_addr][proto][pid]["cmd"] = cmd return ret def _ppid(): - ''' + """ Return a dict of pid to ppid mappings - ''' + """ ret = {} - if __grains__['kernel'] == 'SunOS': - cmd = 'ps -a -o pid,ppid | tail +2' + if __grains__["kernel"] == "SunOS": + cmd = "ps -a -o pid,ppid | tail +2" else: - cmd = 'ps -ax -o pid,ppid | tail -n+2' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "ps -ax -o pid,ppid | tail -n+2" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): pid, ppid = line.split() ret[pid] = ppid @@ -284,61 +301,67 @@ def _ppid(): def _netstat_bsd(): - ''' + """ Return netstat information for BSD flavors - ''' + """ ret = [] - if __grains__['kernel'] == 'NetBSD': - for addr_family in ('inet', 'inet6'): - cmd = 'netstat -f {0} -an | tail -n+3'.format(addr_family) - out = __salt__['cmd.run'](cmd, python_shell=True) + if __grains__["kernel"] == "NetBSD": + for addr_family in ("inet", "inet6"): + cmd = "netstat -f {0} -an | tail -n+3".format(addr_family) + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() entry = { - 'proto': comps[0], - 'recv-q': comps[1], - 'send-q': comps[2], - 'local-address': comps[3], - 'remote-address': comps[4] + "proto": comps[0], + "recv-q": comps[1], + "send-q": comps[2], + "local-address": comps[3], + "remote-address": comps[4], } - if entry['proto'].startswith('tcp'): - entry['state'] = comps[5] + if entry["proto"].startswith("tcp"): + entry["state"] = comps[5] ret.append(entry) else: # Lookup TCP connections - cmd = 'netstat -p tcp -an | tail -n+3' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -p tcp -an | tail -n+3" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'proto': comps[0], - 'recv-q': comps[1], - 'send-q': comps[2], - 'local-address': comps[3], - 'remote-address': comps[4], - 'state': comps[5]}) + ret.append( + { + "proto": comps[0], + "recv-q": comps[1], + "send-q": comps[2], + "local-address": comps[3], + "remote-address": comps[4], + "state": comps[5], + } + ) # Lookup UDP connections - cmd = 'netstat -p udp -an | tail -n+3' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -p udp -an | tail -n+3" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'proto': comps[0], - 'recv-q': comps[1], - 'send-q': comps[2], - 'local-address': comps[3], - 'remote-address': comps[4]}) + ret.append( + { + "proto": comps[0], + "recv-q": comps[1], + "send-q": comps[2], + "local-address": comps[3], + "remote-address": comps[4], + } + ) # Add in user and program info ppid = _ppid() - if __grains__['kernel'] == 'OpenBSD': + if __grains__["kernel"] == "OpenBSD": netinfo = _netinfo_openbsd() - elif __grains__['kernel'] in ('FreeBSD', 'NetBSD'): + elif __grains__["kernel"] in ("FreeBSD", "NetBSD"): netinfo = _netinfo_freebsd_netbsd() for idx in range(len(ret)): - local = ret[idx]['local-address'] - remote = ret[idx]['remote-address'] - proto = ret[idx]['proto'] + local = ret[idx]["local-address"] + remote = ret[idx]["remote-address"] + proto = ret[idx]["proto"] try: # Make a pointer to the info for this connection for easier # reference below @@ -350,60 +373,66 @@ def _netstat_bsd(): try: # Master pid for this connection will be the pid whose ppid isn't # in the subset dict we created above - master_pid = next(iter( - x for x, y in six.iteritems(conn_ppid) if y not in ptr - )) + master_pid = next( + iter(x for x, y in six.iteritems(conn_ppid) if y not in ptr) + ) except StopIteration: continue - ret[idx]['user'] = ptr[master_pid]['user'] - ret[idx]['program'] = '/'.join((master_pid, ptr[master_pid]['cmd'])) + ret[idx]["user"] = ptr[master_pid]["user"] + ret[idx]["program"] = "/".join((master_pid, ptr[master_pid]["cmd"])) return ret def _netstat_sunos(): - ''' + """ Return netstat information for SunOS flavors - ''' - log.warning('User and program not (yet) supported on SunOS') + """ + log.warning("User and program not (yet) supported on SunOS") ret = [] - for addr_family in ('inet', 'inet6'): + for addr_family in ("inet", "inet6"): # Lookup TCP connections - cmd = 'netstat -f {0} -P tcp -an | tail +5'.format(addr_family) - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -f {0} -P tcp -an | tail +5".format(addr_family) + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'proto': 'tcp6' if addr_family == 'inet6' else 'tcp', - 'recv-q': comps[5], - 'send-q': comps[4], - 'local-address': comps[0], - 'remote-address': comps[1], - 'state': comps[6]}) + ret.append( + { + "proto": "tcp6" if addr_family == "inet6" else "tcp", + "recv-q": comps[5], + "send-q": comps[4], + "local-address": comps[0], + "remote-address": comps[1], + "state": comps[6], + } + ) # Lookup UDP connections - cmd = 'netstat -f {0} -P udp -an | tail +5'.format(addr_family) - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -f {0} -P udp -an | tail +5".format(addr_family) + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'proto': 'udp6' if addr_family == 'inet6' else 'udp', - 'local-address': comps[0], - 'remote-address': comps[1] if len(comps) > 2 else ''}) + ret.append( + { + "proto": "udp6" if addr_family == "inet6" else "udp", + "local-address": comps[0], + "remote-address": comps[1] if len(comps) > 2 else "", + } + ) return ret def _netstat_aix(): - ''' + """ Return netstat information for SunOS flavors - ''' + """ ret = [] ## AIX 6.1 - 7.2, appears to ignore addr_family field contents ## for addr_family in ('inet', 'inet6'): - for addr_family in ('inet',): + for addr_family in ("inet",): # Lookup connections - cmd = 'netstat -n -a -f {0} | tail -n +3'.format(addr_family) - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -n -a -f {0} | tail -n +3".format(addr_family) + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() if len(comps) < 5: @@ -411,85 +440,100 @@ def _netstat_aix(): proto_seen = None tcp_flag = True - if 'tcp' == comps[0] or 'tcp4' == comps[0]: - proto_seen = 'tcp' - elif 'tcp6' == comps[0]: - proto_seen = 'tcp6' - elif 'udp' == comps[0] or 'udp4' == comps[0]: - proto_seen = 'udp' + if "tcp" == comps[0] or "tcp4" == comps[0]: + proto_seen = "tcp" + elif "tcp6" == comps[0]: + proto_seen = "tcp6" + elif "udp" == comps[0] or "udp4" == comps[0]: + proto_seen = "udp" tcp_flag = False - elif 'udp6' == comps[0]: - proto_seen = 'udp6' + elif "udp6" == comps[0]: + proto_seen = "udp6" tcp_flag = False if tcp_flag: if len(comps) >= 6: - ret.append({ - 'proto': proto_seen, - 'recv-q': comps[1], - 'send-q': comps[2], - 'local-address': comps[3], - 'remote-address': comps[4], - 'state': comps[5]}) + ret.append( + { + "proto": proto_seen, + "recv-q": comps[1], + "send-q": comps[2], + "local-address": comps[3], + "remote-address": comps[4], + "state": comps[5], + } + ) else: if len(comps) >= 5: - ret.append({ - 'proto': proto_seen, - 'local-address': comps[3], - 'remote-address': comps[4]}) + ret.append( + { + "proto": proto_seen, + "local-address": comps[3], + "remote-address": comps[4], + } + ) return ret def _netstat_route_linux(): - ''' + """ Return netstat routing information for Linux distros - ''' + """ ret = [] - cmd = 'netstat -A inet -rn | tail -n+3' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -A inet -rn | tail -n+3" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': comps[2], - 'flags': comps[3], - 'interface': comps[7]}) - cmd = 'netstat -A inet6 -rn | tail -n+3' - out = __salt__['cmd.run'](cmd, python_shell=True) + ret.append( + { + "addr_family": "inet", + "destination": comps[0], + "gateway": comps[1], + "netmask": comps[2], + "flags": comps[3], + "interface": comps[7], + } + ) + cmd = "netstat -A inet6 -rn | tail -n+3" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() if len(comps) == 6: - ret.append({ - 'addr_family': 'inet6', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[5]}) + ret.append( + { + "addr_family": "inet6", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[5], + } + ) elif len(comps) == 7: - ret.append({ - 'addr_family': 'inet6', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[6]}) + ret.append( + { + "addr_family": "inet6", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[6], + } + ) else: continue return ret def _ip_route_linux(): - ''' + """ Return ip routing information for Linux distros (netstat is deprecated and may not be available) - ''' + """ # table main closest to old netstat inet output ret = [] - cmd = 'ip -4 route show table main' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "ip -4 route show table main" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() @@ -499,34 +543,40 @@ def _ip_route_linux(): continue if comps[0] == "default": - ip_interface = '' + ip_interface = "" if comps[3] == "dev": ip_interface = comps[4] - ret.append({ - 'addr_family': 'inet', - 'destination': '0.0.0.0', - 'gateway': comps[2], - 'netmask': '0.0.0.0', - 'flags': 'UG', - 'interface': ip_interface}) + ret.append( + { + "addr_family": "inet", + "destination": "0.0.0.0", + "gateway": comps[2], + "netmask": "0.0.0.0", + "flags": "UG", + "interface": ip_interface, + } + ) else: address_mask = convert_cidr(comps[0]) - ip_interface = '' + ip_interface = "" if comps[1] == "dev": ip_interface = comps[2] - ret.append({ - 'addr_family': 'inet', - 'destination': address_mask['network'], - 'gateway': '0.0.0.0', - 'netmask': address_mask['netmask'], - 'flags': 'U', - 'interface': ip_interface}) + ret.append( + { + "addr_family": "inet", + "destination": address_mask["network"], + "gateway": "0.0.0.0", + "netmask": address_mask["netmask"], + "flags": "U", + "interface": ip_interface, + } + ) # table all closest to old netstat inet6 output - cmd = 'ip -6 route show table all' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "ip -6 route show table all" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() @@ -536,208 +586,253 @@ def _ip_route_linux(): continue if comps[0] == "default": - ip_interface = '' + ip_interface = "" if comps[3] == "dev": ip_interface = comps[4] - ret.append({ - 'addr_family': 'inet6', - 'destination': '::/0', - 'gateway': comps[2], - 'netmask': '', - 'flags': 'UG', - 'interface': ip_interface}) + ret.append( + { + "addr_family": "inet6", + "destination": "::/0", + "gateway": comps[2], + "netmask": "", + "flags": "UG", + "interface": ip_interface, + } + ) elif comps[0] == "local": - ip_interface = '' + ip_interface = "" if comps[2] == "dev": ip_interface = comps[3] local_address = comps[1] + "/128" - ret.append({ - 'addr_family': 'inet6', - 'destination': local_address, - 'gateway': '::', - 'netmask': '', - 'flags': 'U', - 'interface': ip_interface}) + ret.append( + { + "addr_family": "inet6", + "destination": local_address, + "gateway": "::", + "netmask": "", + "flags": "U", + "interface": ip_interface, + } + ) else: address_mask = convert_cidr(comps[0]) - ip_interface = '' + ip_interface = "" if comps[1] == "dev": ip_interface = comps[2] - ret.append({ - 'addr_family': 'inet6', - 'destination': comps[0], - 'gateway': '::', - 'netmask': '', - 'flags': 'U', - 'interface': ip_interface}) + ret.append( + { + "addr_family": "inet6", + "destination": comps[0], + "gateway": "::", + "netmask": "", + "flags": "U", + "interface": ip_interface, + } + ) return ret def _netstat_route_freebsd(): - ''' + """ Return netstat routing information for FreeBSD and macOS - ''' + """ ret = [] - cmd = 'netstat -f inet -rn | tail -n+5' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -f inet -rn | tail -n+5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - if __grains__['os'] == 'FreeBSD' and int(__grains__.get('osmajorrelease', 0)) < 10: - ret.append({ - 'addr_family': 'inet', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': comps[2], - 'flags': comps[3], - 'interface': comps[5]}) + if ( + __grains__["os"] == "FreeBSD" + and int(__grains__.get("osmajorrelease", 0)) < 10 + ): + ret.append( + { + "addr_family": "inet", + "destination": comps[0], + "gateway": comps[1], + "netmask": comps[2], + "flags": comps[3], + "interface": comps[5], + } + ) else: - ret.append({ - 'addr_family': 'inet', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[3]}) - cmd = 'netstat -f inet6 -rn | tail -n+5' - out = __salt__['cmd.run'](cmd, python_shell=True) + ret.append( + { + "addr_family": "inet", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[3], + } + ) + cmd = "netstat -f inet6 -rn | tail -n+5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet6', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[3]}) + ret.append( + { + "addr_family": "inet6", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[3], + } + ) return ret def _netstat_route_netbsd(): - ''' + """ Return netstat routing information for NetBSD - ''' + """ ret = [] - cmd = 'netstat -f inet -rn | tail -n+5' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -f inet -rn | tail -n+5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[3], - 'interface': comps[6]}) - cmd = 'netstat -f inet6 -rn | tail -n+5' - out = __salt__['cmd.run'](cmd, python_shell=True) + ret.append( + { + "addr_family": "inet", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[3], + "interface": comps[6], + } + ) + cmd = "netstat -f inet6 -rn | tail -n+5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet6', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[3], - 'interface': comps[6]}) + ret.append( + { + "addr_family": "inet6", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[3], + "interface": comps[6], + } + ) return ret def _netstat_route_openbsd(): - ''' + """ Return netstat routing information for OpenBSD - ''' + """ ret = [] - cmd = 'netstat -f inet -rn | tail -n+5' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -f inet -rn | tail -n+5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[7]}) - cmd = 'netstat -f inet6 -rn | tail -n+5' - out = __salt__['cmd.run'](cmd, python_shell=True) + ret.append( + { + "addr_family": "inet", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[7], + } + ) + cmd = "netstat -f inet6 -rn | tail -n+5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet6', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[7]}) + ret.append( + { + "addr_family": "inet6", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[7], + } + ) return ret def _netstat_route_sunos(): - ''' + """ Return netstat routing information for SunOS - ''' + """ ret = [] - cmd = 'netstat -f inet -rn | tail +5' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -f inet -rn | tail +5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[5] if len(comps) >= 6 else ''}) - cmd = 'netstat -f inet6 -rn | tail +5' - out = __salt__['cmd.run'](cmd, python_shell=True) + ret.append( + { + "addr_family": "inet", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[5] if len(comps) >= 6 else "", + } + ) + cmd = "netstat -f inet6 -rn | tail +5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet6', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[5] if len(comps) >= 6 else ''}) + ret.append( + { + "addr_family": "inet6", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[5] if len(comps) >= 6 else "", + } + ) return ret def _netstat_route_aix(): - ''' + """ Return netstat routing information for AIX - ''' + """ ret = [] - cmd = 'netstat -f inet -rn | tail -n +5' - out = __salt__['cmd.run'](cmd, python_shell=True) + cmd = "netstat -f inet -rn | tail -n +5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[5] if len(comps) >= 6 else ''}) - cmd = 'netstat -f inet6 -rn | tail -n +5' - out = __salt__['cmd.run'](cmd, python_shell=True) + ret.append( + { + "addr_family": "inet", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[5] if len(comps) >= 6 else "", + } + ) + cmd = "netstat -f inet6 -rn | tail -n +5" + out = __salt__["cmd.run"](cmd, python_shell=True) for line in out.splitlines(): comps = line.split() - ret.append({ - 'addr_family': 'inet6', - 'destination': comps[0], - 'gateway': comps[1], - 'netmask': '', - 'flags': comps[2], - 'interface': comps[5] if len(comps) >= 6 else ''}) + ret.append( + { + "addr_family": "inet6", + "destination": comps[0], + "gateway": comps[1], + "netmask": "", + "flags": comps[2], + "interface": comps[5] if len(comps) >= 6 else "", + } + ) return ret def netstat(): - ''' + """ Return information on open ports and states .. note:: @@ -758,23 +853,23 @@ def netstat(): .. code-block:: bash salt '*' network.netstat - ''' - if __grains__['kernel'] == 'Linux': - if not salt.utils.path.which('netstat'): + """ + if __grains__["kernel"] == "Linux": + if not salt.utils.path.which("netstat"): return _ss_linux() else: return _netstat_linux() - elif __grains__['kernel'] in ('OpenBSD', 'FreeBSD', 'NetBSD'): + elif __grains__["kernel"] in ("OpenBSD", "FreeBSD", "NetBSD"): return _netstat_bsd() - elif __grains__['kernel'] == 'SunOS': + elif __grains__["kernel"] == "SunOS": return _netstat_sunos() - elif __grains__['kernel'] == 'AIX': + elif __grains__["kernel"] == "AIX": return _netstat_aix() - raise CommandExecutionError('Not yet supported on this platform') + raise CommandExecutionError("Not yet supported on this platform") def active_tcp(): - ''' + """ Return a dict containing information on all of the running TCP connections (currently linux and solaris only) .. versionchanged:: 2015.8.4 @@ -786,46 +881,46 @@ def active_tcp(): .. code-block:: bash salt '*' network.active_tcp - ''' - if __grains__['kernel'] == 'Linux': + """ + if __grains__["kernel"] == "Linux": return salt.utils.network.active_tcp() - elif __grains__['kernel'] == 'SunOS': + elif __grains__["kernel"] == "SunOS": # lets use netstat to mimic linux as close as possible ret = {} for connection in _netstat_sunos(): - if not connection['proto'].startswith('tcp'): + if not connection["proto"].startswith("tcp"): continue - if connection['state'] != 'ESTABLISHED': + if connection["state"] != "ESTABLISHED": continue - ret[len(ret)+1] = { - 'local_addr': '.'.join(connection['local-address'].split('.')[:-1]), - 'local_port': '.'.join(connection['local-address'].split('.')[-1:]), - 'remote_addr': '.'.join(connection['remote-address'].split('.')[:-1]), - 'remote_port': '.'.join(connection['remote-address'].split('.')[-1:]) + ret[len(ret) + 1] = { + "local_addr": ".".join(connection["local-address"].split(".")[:-1]), + "local_port": ".".join(connection["local-address"].split(".")[-1:]), + "remote_addr": ".".join(connection["remote-address"].split(".")[:-1]), + "remote_port": ".".join(connection["remote-address"].split(".")[-1:]), } return ret - elif __grains__['kernel'] == 'AIX': + elif __grains__["kernel"] == "AIX": # lets use netstat to mimic linux as close as possible ret = {} for connection in _netstat_aix(): - if not connection['proto'].startswith('tcp'): + if not connection["proto"].startswith("tcp"): continue - if connection['state'] != 'ESTABLISHED': + if connection["state"] != "ESTABLISHED": continue - ret[len(ret)+1] = { - 'local_addr': '.'.join(connection['local-address'].split('.')[:-1]), - 'local_port': '.'.join(connection['local-address'].split('.')[-1:]), - 'remote_addr': '.'.join(connection['remote-address'].split('.')[:-1]), - 'remote_port': '.'.join(connection['remote-address'].split('.')[-1:]) + ret[len(ret) + 1] = { + "local_addr": ".".join(connection["local-address"].split(".")[:-1]), + "local_port": ".".join(connection["local-address"].split(".")[-1:]), + "remote_addr": ".".join(connection["remote-address"].split(".")[:-1]), + "remote_port": ".".join(connection["remote-address"].split(".")[-1:]), } return ret else: return {} -@salt.utils.decorators.path.which('traceroute') +@salt.utils.decorators.path.which("traceroute") def traceroute(host): - ''' + """ Performs a traceroute to a 3rd party host .. versionchanged:: 2015.8.0 @@ -839,23 +934,25 @@ def traceroute(host): .. code-block:: bash salt '*' network.traceroute archlinux.org - ''' + """ ret = [] - cmd = 'traceroute {0}'.format(salt.utils.network.sanitize_host(host)) - out = __salt__['cmd.run'](cmd) + cmd = "traceroute {0}".format(salt.utils.network.sanitize_host(host)) + out = __salt__["cmd.run"](cmd) # Parse version of traceroute if salt.utils.platform.is_sunos() or salt.utils.platform.is_aix(): traceroute_version = [0, 0, 0] else: - version_out = __salt__['cmd.run']('traceroute --version') + version_out = __salt__["cmd.run"]("traceroute --version") try: # Linux traceroute version looks like: # Modern traceroute for Linux, version 2.0.19, Dec 10 2012 # Darwin and FreeBSD traceroute version looks like: Version 1.4a12+[FreeBSD|Darwin] - version_raw = re.findall(r'.*[Vv]ersion (\d+)\.([\w\+]+)\.*(\w*)', version_out)[0] - log.debug('traceroute_version_raw: %s', version_raw) + version_raw = re.findall( + r".*[Vv]ersion (\d+)\.([\w\+]+)\.*(\w*)", version_out + )[0] + log.debug("traceroute_version_raw: %s", version_raw) traceroute_version = [] for t in version_raw: try: @@ -866,7 +963,7 @@ def traceroute(host): if len(traceroute_version) < 3: traceroute_version.append(0) - log.debug('traceroute_version: %s', traceroute_version) + log.debug("traceroute_version: %s", traceroute_version) except IndexError: traceroute_version = [0, 0, 0] @@ -874,68 +971,70 @@ def traceroute(host): for line in out.splitlines(): # Pre requirements for line parsing skip_line = False - if ' ' not in line: + if " " not in line: skip_line = True - if line.startswith('traceroute'): + if line.startswith("traceroute"): skip_line = True if salt.utils.platform.is_aix(): - if line.startswith('trying to get source for'): + if line.startswith("trying to get source for"): skip_line = True - if line.startswith('source should be'): + if line.startswith("source should be"): skip_line = True - if line.startswith('outgoing MTU'): + if line.startswith("outgoing MTU"): skip_line = True - if line.startswith('fragmentation required'): + if line.startswith("fragmentation required"): skip_line = True if skip_line: - log.debug('Skipping traceroute output line: %s', line) + log.debug("Skipping traceroute output line: %s", line) continue # Parse output from unix variants - if 'Darwin' in six.text_type(traceroute_version[1]) or \ - 'FreeBSD' in six.text_type(traceroute_version[1]) or \ - __grains__['kernel'] in ('SunOS', 'AIX'): + if ( + "Darwin" in six.text_type(traceroute_version[1]) + or "FreeBSD" in six.text_type(traceroute_version[1]) + or __grains__["kernel"] in ("SunOS", "AIX") + ): try: - traceline = re.findall(r'\s*(\d*)\s+(.*)\s+\((.*)\)\s+(.*)$', line)[0] + traceline = re.findall(r"\s*(\d*)\s+(.*)\s+\((.*)\)\s+(.*)$", line)[0] except IndexError: - traceline = re.findall(r'\s*(\d*)\s+(\*\s+\*\s+\*)', line)[0] + traceline = re.findall(r"\s*(\d*)\s+(\*\s+\*\s+\*)", line)[0] - log.debug('traceline: %s', traceline) - delays = re.findall(r'(\d+\.\d+)\s*ms', six.text_type(traceline)) + log.debug("traceline: %s", traceline) + delays = re.findall(r"(\d+\.\d+)\s*ms", six.text_type(traceline)) try: - if traceline[1] == '* * *': - result = { - 'count': traceline[0], - 'hostname': '*' - } + if traceline[1] == "* * *": + result = {"count": traceline[0], "hostname": "*"} else: result = { - 'count': traceline[0], - 'hostname': traceline[1], - 'ip': traceline[2], + "count": traceline[0], + "hostname": traceline[1], + "ip": traceline[2], } for idx in range(0, len(delays)): - result['ms{0}'.format(idx + 1)] = delays[idx] + result["ms{0}".format(idx + 1)] = delays[idx] except IndexError: result = {} # Parse output from specific version ranges - elif (traceroute_version[0] >= 2 and traceroute_version[2] >= 14 - or traceroute_version[0] >= 2 and traceroute_version[1] > 0): - comps = line.split(' ') - if len(comps) >= 2 and comps[1] == '* * *': - result = { - 'count': int(comps[0]), - 'hostname': '*'} + elif ( + traceroute_version[0] >= 2 + and traceroute_version[2] >= 14 + or traceroute_version[0] >= 2 + and traceroute_version[1] > 0 + ): + comps = line.split(" ") + if len(comps) >= 2 and comps[1] == "* * *": + result = {"count": int(comps[0]), "hostname": "*"} elif len(comps) >= 5: result = { - 'count': int(comps[0]), - 'hostname': comps[1].split()[0], - 'ip': comps[1].split()[1].strip('()'), - 'ms1': float(comps[2].split()[0]), - 'ms2': float(comps[3].split()[0]), - 'ms3': float(comps[4].split()[0])} + "count": int(comps[0]), + "hostname": comps[1].split()[0], + "ip": comps[1].split()[1].strip("()"), + "ms1": float(comps[2].split()[0]), + "ms2": float(comps[3].split()[0]), + "ms3": float(comps[4].split()[0]), + } else: result = {} @@ -944,27 +1043,28 @@ def traceroute(host): comps = line.split() if len(comps) >= 8: result = { - 'count': comps[0], - 'hostname': comps[1], - 'ip': comps[2], - 'ms1': comps[4], - 'ms2': comps[6], - 'ms3': comps[8], - 'ping1': comps[3], - 'ping2': comps[5], - 'ping3': comps[7]} + "count": comps[0], + "hostname": comps[1], + "ip": comps[2], + "ms1": comps[4], + "ms2": comps[6], + "ms3": comps[8], + "ping1": comps[3], + "ping2": comps[5], + "ping3": comps[7], + } else: result = {} ret.append(result) if not result: - log.warn('Cannot parse traceroute output line: %s', line) + log.warn("Cannot parse traceroute output line: %s", line) return ret -@salt.utils.decorators.path.which('dig') +@salt.utils.decorators.path.which("dig") def dig(host): - ''' + """ Performs a DNS lookup with dig CLI Example: @@ -972,14 +1072,14 @@ def dig(host): .. code-block:: bash salt '*' network.dig archlinux.org - ''' - cmd = 'dig {0}'.format(salt.utils.network.sanitize_host(host)) - return __salt__['cmd.run'](cmd) + """ + cmd = "dig {0}".format(salt.utils.network.sanitize_host(host)) + return __salt__["cmd.run"](cmd) -@salt.utils.decorators.path.which('arp') +@salt.utils.decorators.path.which("arp") def arp(): - ''' + """ Return the arp table from the minion .. versionchanged:: 2015.8.0 @@ -990,33 +1090,33 @@ def arp(): .. code-block:: bash salt '*' network.arp - ''' + """ ret = {} - out = __salt__['cmd.run']('arp -an') + out = __salt__["cmd.run"]("arp -an") for line in out.splitlines(): comps = line.split() if len(comps) < 4: continue - if __grains__['kernel'] == 'SunOS': - if ':' not in comps[-1]: + if __grains__["kernel"] == "SunOS": + if ":" not in comps[-1]: continue ret[comps[-1]] = comps[1] - elif __grains__['kernel'] == 'OpenBSD': - if comps[0] == 'Host' or comps[1] == '(incomplete)': + elif __grains__["kernel"] == "OpenBSD": + if comps[0] == "Host" or comps[1] == "(incomplete)": continue ret[comps[1]] = comps[0] - elif __grains__['kernel'] == 'AIX': - if comps[0] in ('bucket', 'There'): + elif __grains__["kernel"] == "AIX": + if comps[0] in ("bucket", "There"): continue - ret[comps[3]] = comps[1].strip('(').strip(')') + ret[comps[3]] = comps[1].strip("(").strip(")") else: - ret[comps[3]] = comps[1].strip('(').strip(')') + ret[comps[3]] = comps[1].strip("(").strip(")") return ret def interfaces(): - ''' + """ Return a dictionary of information about all the interfaces on the minion CLI Example: @@ -1024,12 +1124,12 @@ def interfaces(): .. code-block:: bash salt '*' network.interfaces - ''' + """ return salt.utils.network.interfaces() def hw_addr(iface): - ''' + """ Return the hardware address (a.k.a. MAC address) for a given interface CLI Example: @@ -1037,16 +1137,16 @@ def hw_addr(iface): .. code-block:: bash salt '*' network.hw_addr eth0 - ''' + """ return salt.utils.network.hw_addr(iface) # Alias hwaddr to preserve backward compat -hwaddr = salt.utils.functools.alias_function(hw_addr, 'hwaddr') +hwaddr = salt.utils.functools.alias_function(hw_addr, "hwaddr") def interface(iface): - ''' + """ Return the inet address for a given interface .. versionadded:: 2014.7.0 @@ -1056,12 +1156,12 @@ def interface(iface): .. code-block:: bash salt '*' network.interface eth0 - ''' + """ return salt.utils.network.interface(iface) def interface_ip(iface): - ''' + """ Return the inet address for a given interface .. versionadded:: 2014.7.0 @@ -1071,12 +1171,12 @@ def interface_ip(iface): .. code-block:: bash salt '*' network.interface_ip eth0 - ''' + """ return salt.utils.network.interface_ip(iface) def subnets(interfaces=None): - ''' + """ Returns a list of IPv4 subnets to which the host belongs CLI Example: @@ -1085,12 +1185,12 @@ def subnets(interfaces=None): salt '*' network.subnets salt '*' network.subnets interfaces=eth1 - ''' + """ return salt.utils.network.subnets(interfaces) def subnets6(): - ''' + """ Returns a list of IPv6 subnets to which the host belongs CLI Example: @@ -1098,12 +1198,12 @@ def subnets6(): .. code-block:: bash salt '*' network.subnets - ''' + """ return salt.utils.network.subnets6() def in_subnet(cidr): - ''' + """ Returns True if host is within specified subnet, otherwise False. CLI Example: @@ -1111,12 +1211,12 @@ def in_subnet(cidr): .. code-block:: bash salt '*' network.in_subnet 10.0.0.0/16 - ''' + """ return salt.utils.network.in_subnet(cidr) def ip_in_subnet(ip_addr, cidr): - ''' + """ Returns True if given IP is within specified subnet, otherwise False. CLI Example: @@ -1124,12 +1224,12 @@ def ip_in_subnet(ip_addr, cidr): .. code-block:: bash salt '*' network.ip_in_subnet 172.17.0.4 172.16.0.0/12 - ''' + """ return salt.utils.network.in_subnet(cidr, ip_addr) def convert_cidr(cidr): - ''' + """ returns the network and subnet mask of a cidr addr .. versionadded:: 2016.3.0 @@ -1139,18 +1239,17 @@ def convert_cidr(cidr): .. code-block:: bash salt '*' network.convert_cidr 172.31.0.0/16 - ''' - ret = {'network': None, - 'netmask': None} + """ + ret = {"network": None, "netmask": None} cidr = calc_net(cidr) network_info = ipaddress.ip_network(cidr) - ret['network'] = six.text_type(network_info.network_address) - ret['netmask'] = six.text_type(network_info.netmask) + ret["network"] = six.text_type(network_info.network_address) + ret["netmask"] = six.text_type(network_info.netmask) return ret def calc_net(ip_addr, netmask=None): - ''' + """ Returns the CIDR of a subnet based on an IP address (CIDR notation supported) and optional netmask. @@ -1163,12 +1262,12 @@ def calc_net(ip_addr, netmask=None): salt '*' network.calc_net 2a02:f6e:a000:80:84d8:8332:7866:4e07/64 .. versionadded:: 2015.8.0 - ''' + """ return salt.utils.network.calc_net(ip_addr, netmask) def ip_addrs(interface=None, include_loopback=False, cidr=None, type=None): - ''' + """ Returns a list of IPv4 addresses assigned to the host. 127.0.0.1 is ignored, unless 'include_loopback=True' is indicated. If 'interface' is provided, then only IP addresses from that interface will be returned. @@ -1181,25 +1280,26 @@ def ip_addrs(interface=None, include_loopback=False, cidr=None, type=None): .. code-block:: bash salt '*' network.ip_addrs - ''' - addrs = salt.utils.network.ip_addrs(interface=interface, - include_loopback=include_loopback) + """ + addrs = salt.utils.network.ip_addrs( + interface=interface, include_loopback=include_loopback + ) if cidr: return [i for i in addrs if salt.utils.network.in_subnet(cidr, [i])] else: - if type == 'public': + if type == "public": return [i for i in addrs if not is_private(i)] - elif type == 'private': + elif type == "private": return [i for i in addrs if is_private(i)] else: return addrs -ipaddrs = salt.utils.functools.alias_function(ip_addrs, 'ipaddrs') +ipaddrs = salt.utils.functools.alias_function(ip_addrs, "ipaddrs") def ip_addrs6(interface=None, include_loopback=False, cidr=None): - ''' + """ Returns a list of IPv6 addresses assigned to the host. ::1 is ignored, unless 'include_loopback=True' is indicated. If 'interface' is provided, then only IP addresses from that interface will be returned. @@ -1211,20 +1311,21 @@ def ip_addrs6(interface=None, include_loopback=False, cidr=None): .. code-block:: bash salt '*' network.ip_addrs6 - ''' - addrs = salt.utils.network.ip_addrs6(interface=interface, - include_loopback=include_loopback) + """ + addrs = salt.utils.network.ip_addrs6( + interface=interface, include_loopback=include_loopback + ) if cidr: return [i for i in addrs if salt.utils.network.in_subnet(cidr, [i])] else: return addrs -ipaddrs6 = salt.utils.functools.alias_function(ip_addrs6, 'ipaddrs6') +ipaddrs6 = salt.utils.functools.alias_function(ip_addrs6, "ipaddrs6") def get_hostname(): - ''' + """ Get hostname CLI Example: @@ -1232,13 +1333,13 @@ def get_hostname(): .. code-block:: bash salt '*' network.get_hostname - ''' + """ return socket.gethostname() def get_fqdn(): - ''' + """ Get fully qualified domain name CLI Example: @@ -1246,13 +1347,13 @@ def get_fqdn(): .. code-block:: bash salt '*' network.get_fqdn - ''' + """ return socket.getfqdn() def mod_hostname(hostname): - ''' + """ Modify hostname .. versionchanged:: 2015.8.0 @@ -1263,7 +1364,7 @@ def mod_hostname(hostname): .. code-block:: bash salt '*' network.mod_hostname master.saltstack.com - ''' + """ # # SunOS tested on SmartOS and OmniOS (Solaris 10 compatible) # Oracle Solaris 11 uses smf, currently not supported @@ -1276,54 +1377,59 @@ def mod_hostname(hostname): if hostname is None: return False - hostname_cmd = salt.utils.path.which('hostnamectl') or salt.utils.path.which('hostname') + hostname_cmd = salt.utils.path.which("hostnamectl") or salt.utils.path.which( + "hostname" + ) if salt.utils.platform.is_sunos(): - uname_cmd = '/usr/bin/uname' if salt.utils.platform.is_smartos() else salt.utils.path.which('uname') - check_hostname_cmd = salt.utils.path.which('check-hostname') + uname_cmd = ( + "/usr/bin/uname" + if salt.utils.platform.is_smartos() + else salt.utils.path.which("uname") + ) + check_hostname_cmd = salt.utils.path.which("check-hostname") # Grab the old hostname so we know which hostname to change and then # change the hostname using the hostname command - if hostname_cmd.endswith('hostnamectl'): - result = __salt__['cmd.run_all']('{0} status'.format(hostname_cmd)) - if 0 == result['retcode']: - out = result['stdout'] + if hostname_cmd.endswith("hostnamectl"): + result = __salt__["cmd.run_all"]("{0} status".format(hostname_cmd)) + if 0 == result["retcode"]: + out = result["stdout"] for line in out.splitlines(): - line = line.split(':') - if 'Static hostname' in line[0]: + line = line.split(":") + if "Static hostname" in line[0]: o_hostname = line[1].strip() else: - log.debug('{0} was unable to get hostname'.format(hostname_cmd)) - o_hostname = __salt__['network.get_hostname']() + log.debug("{0} was unable to get hostname".format(hostname_cmd)) + o_hostname = __salt__["network.get_hostname"]() elif not salt.utils.platform.is_sunos(): # don't run hostname -f because -f is not supported on all platforms o_hostname = socket.getfqdn() else: # output: Hostname core OK: fully qualified as core.acheron.be - o_hostname = __salt__['cmd.run'](check_hostname_cmd).split(' ')[-1] + o_hostname = __salt__["cmd.run"](check_hostname_cmd).split(" ")[-1] - if hostname_cmd.endswith('hostnamectl'): - result = __salt__['cmd.run_all']('{0} set-hostname {1}'.format( - hostname_cmd, - hostname, - )) - if result['retcode'] != 0: - log.debug('{0} was unable to set hostname. Error: {1}'.format( - hostname_cmd, - result['stderr'], - )) + if hostname_cmd.endswith("hostnamectl"): + result = __salt__["cmd.run_all"]( + "{0} set-hostname {1}".format(hostname_cmd, hostname,) + ) + if result["retcode"] != 0: + log.debug( + "{0} was unable to set hostname. Error: {1}".format( + hostname_cmd, result["stderr"], + ) + ) return False elif not salt.utils.platform.is_sunos(): - __salt__['cmd.run']('{0} {1}'.format(hostname_cmd, hostname)) + __salt__["cmd.run"]("{0} {1}".format(hostname_cmd, hostname)) else: - __salt__['cmd.run']('{0} -S {1}'.format(uname_cmd, hostname.split('.')[0])) + __salt__["cmd.run"]("{0} -S {1}".format(uname_cmd, hostname.split(".")[0])) # Modify the /etc/hosts file to replace the old hostname with the # new hostname - with salt.utils.files.fopen('/etc/hosts', 'r') as fp_: - host_c = [salt.utils.stringutils.to_unicode(_l) - for _l in fp_.readlines()] + with salt.utils.files.fopen("/etc/hosts", "r") as fp_: + host_c = [salt.utils.stringutils.to_unicode(_l) for _l in fp_.readlines()] - with salt.utils.files.fopen('/etc/hosts', 'w') as fh_: + with salt.utils.files.fopen("/etc/hosts", "w") as fh_: for host in host_c: host = host.split() @@ -1331,59 +1437,65 @@ def mod_hostname(hostname): host[host.index(o_hostname)] = hostname if salt.utils.platform.is_sunos(): # also set a copy of the hostname - host[host.index(o_hostname.split('.')[0])] = hostname.split('.')[0] + host[host.index(o_hostname.split(".")[0])] = hostname.split(".")[0] except ValueError: pass - fh_.write(salt.utils.stringutils.to_str('\t'.join(host) + '\n')) + fh_.write(salt.utils.stringutils.to_str("\t".join(host) + "\n")) # Modify the /etc/sysconfig/network configuration file to set the # new hostname - if __grains__['os_family'] == 'RedHat': - with salt.utils.files.fopen('/etc/sysconfig/network', 'r') as fp_: - network_c = [salt.utils.stringutils.to_unicode(_l) - for _l in fp_.readlines()] + if __grains__["os_family"] == "RedHat": + with salt.utils.files.fopen("/etc/sysconfig/network", "r") as fp_: + network_c = [ + salt.utils.stringutils.to_unicode(_l) for _l in fp_.readlines() + ] - with salt.utils.files.fopen('/etc/sysconfig/network', 'w') as fh_: + with salt.utils.files.fopen("/etc/sysconfig/network", "w") as fh_: for net in network_c: - if net.startswith('HOSTNAME'): - old_hostname = net.split('=', 1)[1].rstrip() + if net.startswith("HOSTNAME"): + old_hostname = net.split("=", 1)[1].rstrip() quote_type = salt.utils.stringutils.is_quoted(old_hostname) - fh_.write(salt.utils.stringutils.to_str( - 'HOSTNAME={1}{0}{1}\n'.format( - salt.utils.stringutils.dequote(hostname), - quote_type))) + fh_.write( + salt.utils.stringutils.to_str( + "HOSTNAME={1}{0}{1}\n".format( + salt.utils.stringutils.dequote(hostname), quote_type + ) + ) + ) else: fh_.write(salt.utils.stringutils.to_str(net)) - elif __grains__['os_family'] in ('Debian', 'NILinuxRT'): - with salt.utils.files.fopen('/etc/hostname', 'w') as fh_: - fh_.write(salt.utils.stringutils.to_str(hostname + '\n')) - if __grains__['lsb_distrib_id'] == 'nilrt': + elif __grains__["os_family"] in ("Debian", "NILinuxRT"): + with salt.utils.files.fopen("/etc/hostname", "w") as fh_: + fh_.write(salt.utils.stringutils.to_str(hostname + "\n")) + if __grains__["lsb_distrib_id"] == "nilrt": str_hostname = salt.utils.stringutils.to_str(hostname) - nirtcfg_cmd = '/usr/local/natinst/bin/nirtcfg' - nirtcfg_cmd += ' --set section=SystemSettings,token=\'Host_Name\',value=\'{0}\''.format(str_hostname) - if __salt__['cmd.run_all'](nirtcfg_cmd)['retcode'] != 0: - raise CommandExecutionError('Couldn\'t set hostname to: {0}\n'.format(str_hostname)) - elif __grains__['os_family'] == 'OpenBSD': - with salt.utils.files.fopen('/etc/myname', 'w') as fh_: - fh_.write(salt.utils.stringutils.to_str(hostname + '\n')) + nirtcfg_cmd = "/usr/local/natinst/bin/nirtcfg" + nirtcfg_cmd += " --set section=SystemSettings,token='Host_Name',value='{0}'".format( + str_hostname + ) + if __salt__["cmd.run_all"](nirtcfg_cmd)["retcode"] != 0: + raise CommandExecutionError( + "Couldn't set hostname to: {0}\n".format(str_hostname) + ) + elif __grains__["os_family"] == "OpenBSD": + with salt.utils.files.fopen("/etc/myname", "w") as fh_: + fh_.write(salt.utils.stringutils.to_str(hostname + "\n")) # Update /etc/nodename and /etc/defaultdomain on SunOS if salt.utils.platform.is_sunos(): - with salt.utils.files.fopen('/etc/nodename', 'w') as fh_: - fh_.write(salt.utils.stringutils.to_str( - hostname.split('.')[0] + '\n') - ) - with salt.utils.files.fopen('/etc/defaultdomain', 'w') as fh_: - fh_.write(salt.utils.stringutils.to_str( - ".".join(hostname.split('.')[1:]) + '\n') + with salt.utils.files.fopen("/etc/nodename", "w") as fh_: + fh_.write(salt.utils.stringutils.to_str(hostname.split(".")[0] + "\n")) + with salt.utils.files.fopen("/etc/defaultdomain", "w") as fh_: + fh_.write( + salt.utils.stringutils.to_str(".".join(hostname.split(".")[1:]) + "\n") ) return True def connect(host, port=None, **kwargs): - ''' + """ Test connectivity to a host using a particular port from the minion. @@ -1400,66 +1512,67 @@ def connect(host, port=None, **kwargs): salt '*' network.connect archlinux.org 80 timeout=3 family=ipv4 salt '*' network.connect google-public-dns-a.google.com port=53 proto=udp timeout=3 - ''' + """ - ret = {'result': None, - 'comment': ''} + ret = {"result": None, "comment": ""} if not host: - ret['result'] = False - ret['comment'] = 'Required argument, host, is missing.' + ret["result"] = False + ret["comment"] = "Required argument, host, is missing." return ret if not port: - ret['result'] = False - ret['comment'] = 'Required argument, port, is missing.' + ret["result"] = False + ret["comment"] = "Required argument, port, is missing." return ret - proto = kwargs.get('proto', 'tcp') - timeout = kwargs.get('timeout', 5) - family = kwargs.get('family', None) + proto = kwargs.get("proto", "tcp") + timeout = kwargs.get("timeout", 5) + family = kwargs.get("family", None) - if salt.utils.validate.net.ipv4_addr(host) or salt.utils.validate.net.ipv6_addr(host): + if salt.utils.validate.net.ipv4_addr(host) or salt.utils.validate.net.ipv6_addr( + host + ): address = host else: - address = '{0}'.format(salt.utils.network.sanitize_host(host)) + address = "{0}".format(salt.utils.network.sanitize_host(host)) try: - if proto == 'udp': + if proto == "udp": __proto = socket.SOL_UDP else: __proto = socket.SOL_TCP - proto = 'tcp' + proto = "tcp" if family: - if family == 'ipv4': + if family == "ipv4": __family = socket.AF_INET - elif family == 'ipv6': + elif family == "ipv6": __family = socket.AF_INET6 else: __family = 0 else: __family = 0 - (family, - socktype, - _proto, - garbage, - _address) = socket.getaddrinfo(address, port, __family, 0, __proto)[0] + (family, socktype, _proto, garbage, _address) = socket.getaddrinfo( + address, port, __family, 0, __proto + )[0] except socket.gaierror: - ret['result'] = False - ret['comment'] = 'Unable to resolve host {0} on {1} port {2}'.format(host, proto, port) + ret["result"] = False + ret["comment"] = "Unable to resolve host {0} on {1} port {2}".format( + host, proto, port + ) return ret try: skt = socket.socket(family, socktype, _proto) skt.settimeout(timeout) - if proto == 'udp': + if proto == "udp": # Generate a random string of a # decent size to test UDP connection md5h = hashlib.md5() - md5h.update(datetime.datetime.now().strftime('%s')) + md5h.update(datetime.datetime.now().strftime("%s")) msg = md5h.hexdigest() skt.sendto(msg, _address) recv, svr = skt.recvfrom(255) @@ -1468,17 +1581,21 @@ def connect(host, port=None, **kwargs): skt.connect(_address) skt.shutdown(2) except Exception as exc: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = 'Unable to connect to {0} ({1}) on {2} port {3}'.format(host, _address[0], proto, port) + ret["result"] = False + ret["comment"] = "Unable to connect to {0} ({1}) on {2} port {3}".format( + host, _address[0], proto, port + ) return ret - ret['result'] = True - ret['comment'] = 'Successfully connected to {0} ({1}) on {2} port {3}'.format(host, _address[0], proto, port) + ret["result"] = True + ret["comment"] = "Successfully connected to {0} ({1}) on {2} port {3}".format( + host, _address[0], proto, port + ) return ret def is_private(ip_addr): - ''' + """ Check if the given IP address is a private address .. versionadded:: 2014.7.0 @@ -1490,12 +1607,12 @@ def is_private(ip_addr): .. code-block:: bash salt '*' network.is_private 10.0.0.3 - ''' + """ return ipaddress.ip_address(ip_addr).is_private def is_loopback(ip_addr): - ''' + """ Check if the given IP address is a loopback address .. versionadded:: 2014.7.0 @@ -1507,12 +1624,12 @@ def is_loopback(ip_addr): .. code-block:: bash salt '*' network.is_loopback 127.0.0.1 - ''' + """ return ipaddress.ip_address(ip_addr).is_loopback def reverse_ip(ip_addr): - ''' + """ Returns the reversed IP address .. versionchanged:: 2015.8.0 @@ -1523,40 +1640,40 @@ def reverse_ip(ip_addr): .. code-block:: bash salt '*' network.reverse_ip 172.17.0.4 - ''' + """ return ipaddress.ip_address(ip_addr).reverse_pointer def _get_bufsize_linux(iface): - ''' + """ Return network interface buffer information using ethtool - ''' - ret = {'result': False} + """ + ret = {"result": False} - cmd = '/sbin/ethtool -g {0}'.format(iface) - out = __salt__['cmd.run'](cmd) - pat = re.compile(r'^(.+):\s+(\d+)$') - suffix = 'max-' + cmd = "/sbin/ethtool -g {0}".format(iface) + out = __salt__["cmd.run"](cmd) + pat = re.compile(r"^(.+):\s+(\d+)$") + suffix = "max-" for line in out.splitlines(): res = pat.match(line) if res: - ret[res.group(1).lower().replace(' ', '-') + suffix] = int(res.group(2)) - ret['result'] = True - elif line.endswith('maximums:'): - suffix = '-max' - elif line.endswith('settings:'): - suffix = '' - if not ret['result']: + ret[res.group(1).lower().replace(" ", "-") + suffix] = int(res.group(2)) + ret["result"] = True + elif line.endswith("maximums:"): + suffix = "-max" + elif line.endswith("settings:"): + suffix = "" + if not ret["result"]: parts = out.split() # remove shell cmd prefix from msg - if parts[0].endswith('sh:'): - out = ' '.join(parts[1:]) - ret['comment'] = out + if parts[0].endswith("sh:"): + out = " ".join(parts[1:]) + ret["comment"] = out return ret def get_bufsize(iface): - ''' + """ Return network buffer sizes as a dict (currently linux only) CLI Example: @@ -1564,46 +1681,49 @@ def get_bufsize(iface): .. code-block:: bash salt '*' network.get_bufsize eth0 - ''' - if __grains__['kernel'] == 'Linux': - if os.path.exists('/sbin/ethtool'): + """ + if __grains__["kernel"] == "Linux": + if os.path.exists("/sbin/ethtool"): return _get_bufsize_linux(iface) return {} def _mod_bufsize_linux(iface, *args, **kwargs): - ''' + """ Modify network interface buffer sizes using ethtool - ''' - ret = {'result': False, - 'comment': 'Requires rx= tx== rx-mini= and/or rx-jumbo='} - cmd = '/sbin/ethtool -G ' + iface + """ + ret = { + "result": False, + "comment": "Requires rx= tx== rx-mini= and/or rx-jumbo=", + } + cmd = "/sbin/ethtool -G " + iface if not kwargs: return ret if args: - ret['comment'] = 'Unknown arguments: ' + ' '.join([six.text_type(item) - for item in args]) + ret["comment"] = "Unknown arguments: " + " ".join( + [six.text_type(item) for item in args] + ) return ret - eargs = '' - for kw in ['rx', 'tx', 'rx-mini', 'rx-jumbo']: + eargs = "" + for kw in ["rx", "tx", "rx-mini", "rx-jumbo"]: value = kwargs.get(kw) if value is not None: - eargs += ' ' + kw + ' ' + six.text_type(value) + eargs += " " + kw + " " + six.text_type(value) if not eargs: return ret cmd += eargs - out = __salt__['cmd.run'](cmd) + out = __salt__["cmd.run"](cmd) if out: - ret['comment'] = out + ret["comment"] = out else: - ret['comment'] = eargs.strip() - ret['result'] = True + ret["comment"] = eargs.strip() + ret["result"] = True return ret def mod_bufsize(iface, *args, **kwargs): - ''' + """ Modify network interface buffers (currently linux only) CLI Example: @@ -1611,16 +1731,16 @@ def mod_bufsize(iface, *args, **kwargs): .. code-block:: bash salt '*' network.mod_bufsize tx= rx= rx-mini= rx-jumbo= - ''' - if __grains__['kernel'] == 'Linux': - if os.path.exists('/sbin/ethtool'): + """ + if __grains__["kernel"] == "Linux": + if os.path.exists("/sbin/ethtool"): return _mod_bufsize_linux(iface, *args, **kwargs) return False def routes(family=None): - ''' + """ Return currently configured routes from routing table .. versionchanged:: 2015.8.0 @@ -1634,37 +1754,37 @@ def routes(family=None): .. code-block:: bash salt '*' network.routes - ''' - if family != 'inet' and family != 'inet6' and family is not None: - raise CommandExecutionError('Invalid address family {0}'.format(family)) + """ + if family != "inet" and family != "inet6" and family is not None: + raise CommandExecutionError("Invalid address family {0}".format(family)) - if __grains__['kernel'] == 'Linux': - if not salt.utils.path.which('netstat'): + if __grains__["kernel"] == "Linux": + if not salt.utils.path.which("netstat"): routes_ = _ip_route_linux() else: routes_ = _netstat_route_linux() - elif __grains__['kernel'] == 'SunOS': + elif __grains__["kernel"] == "SunOS": routes_ = _netstat_route_sunos() - elif __grains__['os'] in ['FreeBSD', 'MacOS', 'Darwin']: + elif __grains__["os"] in ["FreeBSD", "MacOS", "Darwin"]: routes_ = _netstat_route_freebsd() - elif __grains__['os'] in ['NetBSD']: + elif __grains__["os"] in ["NetBSD"]: routes_ = _netstat_route_netbsd() - elif __grains__['os'] in ['OpenBSD']: + elif __grains__["os"] in ["OpenBSD"]: routes_ = _netstat_route_openbsd() - elif __grains__['os'] in ['AIX']: + elif __grains__["os"] in ["AIX"]: routes_ = _netstat_route_aix() else: - raise CommandExecutionError('Not yet supported on this platform') + raise CommandExecutionError("Not yet supported on this platform") if not family: return routes_ else: - ret = [route for route in routes_ if route['addr_family'] == family] + ret = [route for route in routes_ if route["addr_family"] == family] return ret def default_route(family=None): - ''' + """ Return default route(s) from routing table .. versionchanged:: 2015.8.0 @@ -1678,40 +1798,47 @@ def default_route(family=None): .. code-block:: bash salt '*' network.default_route - ''' + """ - if family != 'inet' and family != 'inet6' and family is not None: - raise CommandExecutionError('Invalid address family {0}'.format(family)) + if family != "inet" and family != "inet6" and family is not None: + raise CommandExecutionError("Invalid address family {0}".format(family)) _routes = routes() default_route = {} - if __grains__['kernel'] == 'Linux': - default_route['inet'] = ['0.0.0.0', 'default'] - default_route['inet6'] = ['::/0', 'default'] - elif __grains__['os'] in ['FreeBSD', 'NetBSD', 'OpenBSD', 'MacOS', 'Darwin'] or \ - __grains__['kernel'] in ('SunOS', 'AIX'): - default_route['inet'] = ['default'] - default_route['inet6'] = ['default'] + if __grains__["kernel"] == "Linux": + default_route["inet"] = ["0.0.0.0", "default"] + default_route["inet6"] = ["::/0", "default"] + elif __grains__["os"] in [ + "FreeBSD", + "NetBSD", + "OpenBSD", + "MacOS", + "Darwin", + ] or __grains__["kernel"] in ("SunOS", "AIX"): + default_route["inet"] = ["default"] + default_route["inet6"] = ["default"] else: - raise CommandExecutionError('Not yet supported on this platform') + raise CommandExecutionError("Not yet supported on this platform") ret = [] for route in _routes: if family: - if route['destination'] in default_route[family]: - if __grains__['kernel'] == 'SunOS' and route['addr_family'] != family: + if route["destination"] in default_route[family]: + if __grains__["kernel"] == "SunOS" and route["addr_family"] != family: continue ret.append(route) else: - if route['destination'] in default_route['inet'] or \ - route['destination'] in default_route['inet6']: + if ( + route["destination"] in default_route["inet"] + or route["destination"] in default_route["inet6"] + ): ret.append(route) return ret def get_route(ip): - ''' + """ Return routing information for given destination ip .. versionadded:: 2015.5.3 @@ -1726,57 +1853,54 @@ def get_route(ip): CLI Example:: salt '*' network.get_route 10.10.10.10 - ''' + """ - if __grains__['kernel'] == 'Linux': - cmd = 'ip route get {0}'.format(ip) - out = __salt__['cmd.run'](cmd, python_shell=True) - regexp = re.compile(r'(via\s+(?P[\w\.:]+))?\s+dev\s+(?P[\w\.\:\-]+)\s+.*src\s+(?P[\w\.:]+)') + if __grains__["kernel"] == "Linux": + cmd = "ip route get {0}".format(ip) + out = __salt__["cmd.run"](cmd, python_shell=True) + regexp = re.compile( + r"(via\s+(?P[\w\.:]+))?\s+dev\s+(?P[\w\.\:\-]+)\s+.*src\s+(?P[\w\.:]+)" + ) m = regexp.search(out.splitlines()[0]) ret = { - 'destination': ip, - 'gateway': m.group('gateway'), - 'interface': m.group('interface'), - 'source': m.group('source') + "destination": ip, + "gateway": m.group("gateway"), + "interface": m.group("interface"), + "source": m.group("source"), } return ret - if __grains__['kernel'] == 'SunOS': + if __grains__["kernel"] == "SunOS": # [root@nacl ~]# route -n get 172.16.10.123 # route to: 172.16.10.123 - #destination: 172.16.10.0 + # destination: 172.16.10.0 # mask: 255.255.255.0 # interface: net0 # flags: # recvpipe sendpipe ssthresh rtt,ms rttvar,ms hopcount mtu expire # 0 0 0 0 0 0 1500 0 - cmd = '/usr/sbin/route -n get {0}'.format(ip) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "/usr/sbin/route -n get {0}".format(ip) + out = __salt__["cmd.run"](cmd, python_shell=False) - ret = { - 'destination': ip, - 'gateway': None, - 'interface': None, - 'source': None - } + ret = {"destination": ip, "gateway": None, "interface": None, "source": None} for line in out.splitlines(): - line = line.split(':') - if 'route to' in line[0]: - ret['destination'] = line[1].strip() - if 'gateway' in line[0]: - ret['gateway'] = line[1].strip() - if 'interface' in line[0]: - ret['interface'] = line[1].strip() - ret['source'] = salt.utils.network.interface_ip(line[1].strip()) + line = line.split(":") + if "route to" in line[0]: + ret["destination"] = line[1].strip() + if "gateway" in line[0]: + ret["gateway"] = line[1].strip() + if "interface" in line[0]: + ret["interface"] = line[1].strip() + ret["source"] = salt.utils.network.interface_ip(line[1].strip()) return ret - if __grains__['kernel'] == 'OpenBSD': + if __grains__["kernel"] == "OpenBSD": # [root@exosphere] route -n get blackdot.be # route to: 5.135.127.100 - #destination: default + # destination: default # mask: default # gateway: 192.168.0.1 # interface: vio0 @@ -1785,68 +1909,58 @@ def get_route(ip): # flags: # use mtu expire # 8352657 0 0 - cmd = 'route -n get {0}'.format(ip) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "route -n get {0}".format(ip) + out = __salt__["cmd.run"](cmd, python_shell=False) - ret = { - 'destination': ip, - 'gateway': None, - 'interface': None, - 'source': None - } + ret = {"destination": ip, "gateway": None, "interface": None, "source": None} for line in out.splitlines(): - line = line.split(':') - if 'route to' in line[0]: - ret['destination'] = line[1].strip() - if 'gateway' in line[0]: - ret['gateway'] = line[1].strip() - if 'interface' in line[0]: - ret['interface'] = line[1].strip() - if 'if address' in line[0]: - ret['source'] = line[1].strip() + line = line.split(":") + if "route to" in line[0]: + ret["destination"] = line[1].strip() + if "gateway" in line[0]: + ret["gateway"] = line[1].strip() + if "interface" in line[0]: + ret["interface"] = line[1].strip() + if "if address" in line[0]: + ret["source"] = line[1].strip() return ret - if __grains__['kernel'] == 'AIX': + if __grains__["kernel"] == "AIX": # root@la68pp002_pub:~# route -n get 172.29.149.95 # route to: 172.29.149.95 - #destination: 172.29.149.95 + # destination: 172.29.149.95 # gateway: 127.0.0.1 # interface: lo0 - #interf addr: 127.0.0.1 + # interf addr: 127.0.0.1 # flags: - #recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire + # recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire # 0 0 0 0 0 0 0 -68642 - cmd = 'route -n get {0}'.format(ip) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "route -n get {0}".format(ip) + out = __salt__["cmd.run"](cmd, python_shell=False) - ret = { - 'destination': ip, - 'gateway': None, - 'interface': None, - 'source': None - } + ret = {"destination": ip, "gateway": None, "interface": None, "source": None} for line in out.splitlines(): - line = line.split(':') - if 'route to' in line[0]: - ret['destination'] = line[1].strip() - if 'gateway' in line[0]: - ret['gateway'] = line[1].strip() - if 'interface' in line[0]: - ret['interface'] = line[1].strip() - if 'interf addr' in line[0]: - ret['source'] = line[1].strip() + line = line.split(":") + if "route to" in line[0]: + ret["destination"] = line[1].strip() + if "gateway" in line[0]: + ret["gateway"] = line[1].strip() + if "interface" in line[0]: + ret["interface"] = line[1].strip() + if "interf addr" in line[0]: + ret["source"] = line[1].strip() return ret else: - raise CommandExecutionError('Not yet supported on this platform') + raise CommandExecutionError("Not yet supported on this platform") def ifacestartswith(cidr): - ''' + """ Retrieve the interface name from a specific CIDR .. versionadded:: 2016.11.0 @@ -1856,24 +1970,24 @@ def ifacestartswith(cidr): .. code-block:: bash salt '*' network.ifacestartswith 10.0 - ''' + """ net_list = interfaces() intfnames = [] pattern = six.text_type(cidr) size = len(pattern) for ifname, ifval in six.iteritems(net_list): - if 'inet' in ifval: - for inet in ifval['inet']: - if inet['address'][0:size] == pattern: - if 'label' in inet: - intfnames.append(inet['label']) + if "inet" in ifval: + for inet in ifval["inet"]: + if inet["address"][0:size] == pattern: + if "label" in inet: + intfnames.append(inet["label"]) else: intfnames.append(ifname) return intfnames def iphexval(ip): - ''' + """ Retrieve the hexadecimal representation of an IP address .. versionadded:: 2016.11.0 @@ -1883,7 +1997,7 @@ def iphexval(ip): .. code-block:: bash salt '*' network.iphexval 10.0.0.1 - ''' - a = ip.split('.') - hexval = ['%02X' % int(x) for x in a] # pylint: disable=E1321 - return ''.join(hexval) + """ + a = ip.split(".") + hexval = ["%02X" % int(x) for x in a] # pylint: disable=E1321 + return "".join(hexval) diff --git a/salt/modules/neutron.py b/salt/modules/neutron.py index b193944f26b..98d6eb7f712 100644 --- a/salt/modules/neutron.py +++ b/salt/modules/neutron.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for handling OpenStack Neutron calls :depends: - neutronclient Python module @@ -63,15 +63,17 @@ Module for handling OpenStack Neutron calls specify the `verify` option. You can specify True or False to verify (or not) against system certificates, a path to a bundle or CA certs to check against, or None to allow keystoneauth to search for the certificates on its own.(defaults to True) -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs try: import salt.utils.openstack.neutron as suoneu + HAS_NEUTRON = True except NameError as exc: HAS_NEUTRON = False @@ -80,16 +82,14 @@ except NameError as exc: log = logging.getLogger(__name__) # Function alias to not shadow built-ins -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} def __virtual__(): - ''' + """ Only load this module if neutron is installed on this minion. - ''' + """ return HAS_NEUTRON @@ -97,64 +97,64 @@ __opts__ = {} def _auth(profile=None): - ''' + """ Set up neutron credentials - ''' + """ if profile: - credentials = __salt__['config.option'](profile) - user = credentials['keystone.user'] - password = credentials['keystone.password'] - tenant = credentials['keystone.tenant'] - auth_url = credentials['keystone.auth_url'] - region_name = credentials.get('keystone.region_name', None) - service_type = credentials.get('keystone.service_type', 'network') - os_auth_system = credentials.get('keystone.os_auth_system', None) - use_keystoneauth = credentials.get('keystone.use_keystoneauth', False) - verify = credentials.get('keystone.verify', True) + credentials = __salt__["config.option"](profile) + user = credentials["keystone.user"] + password = credentials["keystone.password"] + tenant = credentials["keystone.tenant"] + auth_url = credentials["keystone.auth_url"] + region_name = credentials.get("keystone.region_name", None) + service_type = credentials.get("keystone.service_type", "network") + os_auth_system = credentials.get("keystone.os_auth_system", None) + use_keystoneauth = credentials.get("keystone.use_keystoneauth", False) + verify = credentials.get("keystone.verify", True) else: - user = __salt__['config.option']('keystone.user') - password = __salt__['config.option']('keystone.password') - tenant = __salt__['config.option']('keystone.tenant') - auth_url = __salt__['config.option']('keystone.auth_url') - region_name = __salt__['config.option']('keystone.region_name') - service_type = __salt__['config.option']('keystone.service_type') - os_auth_system = __salt__['config.option']('keystone.os_auth_system') - use_keystoneauth = __salt__['config.option']('keystone.use_keystoneauth') - verify = __salt__['config.option']('keystone.verify') + user = __salt__["config.option"]("keystone.user") + password = __salt__["config.option"]("keystone.password") + tenant = __salt__["config.option"]("keystone.tenant") + auth_url = __salt__["config.option"]("keystone.auth_url") + region_name = __salt__["config.option"]("keystone.region_name") + service_type = __salt__["config.option"]("keystone.service_type") + os_auth_system = __salt__["config.option"]("keystone.os_auth_system") + use_keystoneauth = __salt__["config.option"]("keystone.use_keystoneauth") + verify = __salt__["config.option"]("keystone.verify") if use_keystoneauth is True: - project_domain_name = credentials['keystone.project_domain_name'] - user_domain_name = credentials['keystone.user_domain_name'] + project_domain_name = credentials["keystone.project_domain_name"] + user_domain_name = credentials["keystone.user_domain_name"] kwargs = { - 'username': user, - 'password': password, - 'tenant_name': tenant, - 'auth_url': auth_url, - 'region_name': region_name, - 'service_type': service_type, - 'os_auth_plugin': os_auth_system, - 'use_keystoneauth': use_keystoneauth, - 'verify': verify, - 'project_domain_name': project_domain_name, - 'user_domain_name': user_domain_name + "username": user, + "password": password, + "tenant_name": tenant, + "auth_url": auth_url, + "region_name": region_name, + "service_type": service_type, + "os_auth_plugin": os_auth_system, + "use_keystoneauth": use_keystoneauth, + "verify": verify, + "project_domain_name": project_domain_name, + "user_domain_name": user_domain_name, } else: kwargs = { - 'username': user, - 'password': password, - 'tenant_name': tenant, - 'auth_url': auth_url, - 'region_name': region_name, - 'service_type': service_type, - 'os_auth_plugin': os_auth_system + "username": user, + "password": password, + "tenant_name": tenant, + "auth_url": auth_url, + "region_name": region_name, + "service_type": service_type, + "os_auth_plugin": os_auth_system, } return suoneu.SaltNeutron(**kwargs) def get_quotas_tenant(profile=None): - ''' + """ Fetches tenant info in server's context for following quota operation CLI Example: @@ -166,14 +166,14 @@ def get_quotas_tenant(profile=None): :param profile: Profile to build on (Optional) :return: Quotas information - ''' + """ conn = _auth(profile) return conn.get_quotas_tenant() def list_quotas(profile=None): - ''' + """ Fetches all tenants quotas CLI Example: @@ -185,13 +185,13 @@ def list_quotas(profile=None): :param profile: Profile to build on (Optional) :return: List of quotas - ''' + """ conn = _auth(profile) return conn.list_quotas() def show_quota(tenant_id, profile=None): - ''' + """ Fetches information of a certain tenant's quotas CLI Example: @@ -204,21 +204,23 @@ def show_quota(tenant_id, profile=None): :param tenant_id: ID of tenant :param profile: Profile to build on (Optional) :return: Quota information - ''' + """ conn = _auth(profile) return conn.show_quota(tenant_id) -def update_quota(tenant_id, - subnet=None, - router=None, - network=None, - floatingip=None, - port=None, - security_group=None, - security_group_rule=None, - profile=None): - ''' +def update_quota( + tenant_id, + subnet=None, + router=None, + network=None, + floatingip=None, + port=None, + security_group=None, + security_group_rule=None, + profile=None, +): + """ Update a tenant's quota CLI Example: @@ -238,15 +240,22 @@ def update_quota(tenant_id, :param security_group_rule: Value of security group rule (Optional) :param profile: Profile to build on (Optional) :return: Value of updated quota - ''' + """ conn = _auth(profile) - return conn.update_quota(tenant_id, subnet, router, network, - floatingip, port, security_group, - security_group_rule) + return conn.update_quota( + tenant_id, + subnet, + router, + network, + floatingip, + port, + security_group, + security_group_rule, + ) def delete_quota(tenant_id, profile=None): - ''' + """ Delete the specified tenant's quota value CLI Example: @@ -259,13 +268,13 @@ def delete_quota(tenant_id, profile=None): :param tenant_id: ID of tenant to quota delete :param profile: Profile to build on (Optional) :return: True(Delete succeed) or False(Delete failed) - ''' + """ conn = _auth(profile) return conn.delete_quota(tenant_id) def list_extensions(profile=None): - ''' + """ Fetches a list of all extensions on server side CLI Example: @@ -277,13 +286,13 @@ def list_extensions(profile=None): :param profile: Profile to build on (Optional) :return: List of extensions - ''' + """ conn = _auth(profile) return conn.list_extensions() def list_ports(profile=None): - ''' + """ Fetches a list of all networks for a tenant CLI Example: @@ -295,13 +304,13 @@ def list_ports(profile=None): :param profile: Profile to build on (Optional) :return: List of port - ''' + """ conn = _auth(profile) return conn.list_ports() def show_port(port, profile=None): - ''' + """ Fetches information of a certain port CLI Example: @@ -314,17 +323,13 @@ def show_port(port, profile=None): :param port: ID or name of port to look up :param profile: Profile to build on (Optional) :return: Port information - ''' + """ conn = _auth(profile) return conn.show_port(port) -def create_port(name, - network, - device_id=None, - admin_state_up=True, - profile=None): - ''' +def create_port(name, network, device_id=None, admin_state_up=True, profile=None): + """ Creates a new port CLI Example: @@ -340,13 +345,13 @@ def create_port(name, default: true (Optional) :param profile: Profile to build on (Optional) :return: Created port information - ''' + """ conn = _auth(profile) return conn.create_port(name, network, device_id, admin_state_up) def update_port(port, name, admin_state_up=True, profile=None): - ''' + """ Updates a port CLI Example: @@ -361,13 +366,13 @@ def update_port(port, name, admin_state_up=True, profile=None): default: true (Optional) :param profile: Profile to build on (Optional) :return: Value of updated port information - ''' + """ conn = _auth(profile) return conn.update_port(port, name, admin_state_up) def delete_port(port, profile=None): - ''' + """ Deletes the specified port CLI Example: @@ -380,13 +385,13 @@ def delete_port(port, profile=None): :param port: port name or ID :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_port(port) def list_networks(profile=None): - ''' + """ Fetches a list of all networks for a tenant CLI Example: @@ -398,13 +403,13 @@ def list_networks(profile=None): :param profile: Profile to build on (Optional) :return: List of network - ''' + """ conn = _auth(profile) return conn.list_networks() def show_network(network, profile=None): - ''' + """ Fetches information of a certain network CLI Example: @@ -417,13 +422,22 @@ def show_network(network, profile=None): :param network: ID or name of network to look up :param profile: Profile to build on (Optional) :return: Network information - ''' + """ conn = _auth(profile) return conn.show_network(network) -def create_network(name, router_ext=None, admin_state_up=True, network_type=None, physical_network=None, segmentation_id=None, shared=None, profile=None): - ''' +def create_network( + name, + router_ext=None, + admin_state_up=True, + network_type=None, + physical_network=None, + segmentation_id=None, + shared=None, + profile=None, +): + """ Creates a new network CLI Example: @@ -443,13 +457,21 @@ def create_network(name, router_ext=None, admin_state_up=True, network_type=None :param shared: is the network shared or not (Optional) :param profile: Profile to build on (Optional) :return: Created network information - ''' + """ conn = _auth(profile) - return conn.create_network(name, admin_state_up, router_ext, network_type, physical_network, segmentation_id, shared) + return conn.create_network( + name, + admin_state_up, + router_ext, + network_type, + physical_network, + segmentation_id, + shared, + ) def update_network(network, name, profile=None): - ''' + """ Updates a network CLI Example: @@ -462,13 +484,13 @@ def update_network(network, name, profile=None): :param name: Name of this network :param profile: Profile to build on (Optional) :return: Value of updated network information - ''' + """ conn = _auth(profile) return conn.update_network(network, name) def delete_network(network, profile=None): - ''' + """ Deletes the specified network CLI Example: @@ -481,13 +503,13 @@ def delete_network(network, profile=None): :param network: ID or name of network to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_network(network) def list_subnets(profile=None): - ''' + """ Fetches a list of all networks for a tenant CLI Example: @@ -499,13 +521,13 @@ def list_subnets(profile=None): :param profile: Profile to build on (Optional) :return: List of subnet - ''' + """ conn = _auth(profile) return conn.list_subnets() def show_subnet(subnet, profile=None): - ''' + """ Fetches information of a certain subnet CLI Example: @@ -517,14 +539,13 @@ def show_subnet(subnet, profile=None): :param subnet: ID or name of subnet to look up :param profile: Profile to build on (Optional) :return: Subnet information - ''' + """ conn = _auth(profile) return conn.show_subnet(subnet) -def create_subnet(network, cidr, name=None, - ip_version=4, profile=None): - ''' +def create_subnet(network, cidr, name=None, ip_version=4, profile=None): + """ Creates a new subnet CLI Example: @@ -539,13 +560,13 @@ def create_subnet(network, cidr, name=None, :param ip_version: Version to use, default is 4(IPv4) (Optional) :param profile: Profile to build on (Optional) :return: Created subnet information - ''' + """ conn = _auth(profile) return conn.create_subnet(network, cidr, name, ip_version) def update_subnet(subnet, name, profile=None): - ''' + """ Updates a subnet CLI Example: @@ -558,13 +579,13 @@ def update_subnet(subnet, name, profile=None): :param name: Name of this subnet :param profile: Profile to build on (Optional) :return: Value of updated subnet information - ''' + """ conn = _auth(profile) return conn.update_subnet(subnet, name) def delete_subnet(subnet, profile=None): - ''' + """ Deletes the specified subnet CLI Example: @@ -577,13 +598,13 @@ def delete_subnet(subnet, profile=None): :param subnet: ID or name of subnet to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_subnet(subnet) def list_routers(profile=None): - ''' + """ Fetches a list of all routers for a tenant CLI Example: @@ -595,13 +616,13 @@ def list_routers(profile=None): :param profile: Profile to build on (Optional) :return: List of router - ''' + """ conn = _auth(profile) return conn.list_routers() def show_router(router, profile=None): - ''' + """ Fetches information of a certain router CLI Example: @@ -613,14 +634,13 @@ def show_router(router, profile=None): :param router: ID or name of router to look up :param profile: Profile to build on (Optional) :return: Router information - ''' + """ conn = _auth(profile) return conn.show_router(router) -def create_router(name, ext_network=None, - admin_state_up=True, profile=None): - ''' +def create_router(name, ext_network=None, admin_state_up=True, profile=None): + """ Creates a new router CLI Example: @@ -635,17 +655,13 @@ def create_router(name, ext_network=None, default:true (Optional) :param profile: Profile to build on (Optional) :return: Created router information - ''' + """ conn = _auth(profile) return conn.create_router(name, ext_network, admin_state_up) -def update_router(router, - name=None, - admin_state_up=None, - profile=None, - **kwargs): - ''' +def update_router(router, name=None, admin_state_up=None, profile=None, **kwargs): + """ Updates a router CLI Example: @@ -663,13 +679,13 @@ def update_router(router, :param profile: Profile to build on (Optional) :param kwargs: :return: Value of updated router information - ''' + """ conn = _auth(profile) return conn.update_router(router, name, admin_state_up, **kwargs) def delete_router(router, profile=None): - ''' + """ Delete the specified router CLI Example: @@ -681,13 +697,13 @@ def delete_router(router, profile=None): :param router: ID or name of router to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_router(router) def add_interface_router(router, subnet, profile=None): - ''' + """ Adds an internal network interface to the specified router CLI Example: @@ -700,13 +716,13 @@ def add_interface_router(router, subnet, profile=None): :param subnet: ID or name of the subnet :param profile: Profile to build on (Optional) :return: Added interface information - ''' + """ conn = _auth(profile) return conn.add_interface_router(router, subnet) def remove_interface_router(router, subnet, profile=None): - ''' + """ Removes an internal network interface from the specified router CLI Example: @@ -719,13 +735,13 @@ def remove_interface_router(router, subnet, profile=None): :param subnet: ID or name of the subnet :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.remove_interface_router(router, subnet) def add_gateway_router(router, ext_network, profile=None): - ''' + """ Adds an external network gateway to the specified router CLI Example: @@ -738,13 +754,13 @@ def add_gateway_router(router, ext_network, profile=None): :param ext_network: ID or name of the external network the gateway :param profile: Profile to build on (Optional) :return: Added Gateway router information - ''' + """ conn = _auth(profile) return conn.add_gateway_router(router, ext_network) def remove_gateway_router(router, profile=None): - ''' + """ Removes an external network gateway from the specified router CLI Example: @@ -756,13 +772,13 @@ def remove_gateway_router(router, profile=None): :param router: ID or name of router :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.remove_gateway_router(router) def list_floatingips(profile=None): - ''' + """ Fetch a list of all floatingIPs for a tenant CLI Example: @@ -774,13 +790,13 @@ def list_floatingips(profile=None): :param profile: Profile to build on (Optional) :return: List of floatingIP - ''' + """ conn = _auth(profile) return conn.list_floatingips() def show_floatingip(floatingip_id, profile=None): - ''' + """ Fetches information of a certain floatingIP CLI Example: @@ -792,13 +808,13 @@ def show_floatingip(floatingip_id, profile=None): :param floatingip_id: ID of floatingIP to look up :param profile: Profile to build on (Optional) :return: Floating IP information - ''' + """ conn = _auth(profile) return conn.show_floatingip(floatingip_id) def create_floatingip(floating_network, port=None, profile=None): - ''' + """ Creates a new floatingIP CLI Example: @@ -811,13 +827,13 @@ def create_floatingip(floating_network, port=None, profile=None): :param port: Of the port to be associated with the floatingIP (Optional) :param profile: Profile to build on (Optional) :return: Created floatingIP information - ''' + """ conn = _auth(profile) return conn.create_floatingip(floating_network, port) def update_floatingip(floatingip_id, port=None, profile=None): - ''' + """ Updates a floatingIP CLI Example: @@ -831,13 +847,13 @@ def update_floatingip(floatingip_id, port=None, profile=None): not specify to disassociate the floatingip (Optional) :param profile: Profile to build on (Optional) :return: Value of updated floating IP information - ''' + """ conn = _auth(profile) return conn.update_floatingip(floatingip_id, port) def delete_floatingip(floatingip_id, profile=None): - ''' + """ Deletes the specified floating IP CLI Example: @@ -849,13 +865,13 @@ def delete_floatingip(floatingip_id, profile=None): :param floatingip_id: ID of floatingIP to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_floatingip(floatingip_id) def list_security_groups(profile=None): - ''' + """ Fetches a list of all security groups for a tenant CLI Example: @@ -867,13 +883,13 @@ def list_security_groups(profile=None): :param profile: Profile to build on (Optional) :return: List of security group - ''' + """ conn = _auth(profile) return conn.list_security_groups() def show_security_group(security_group, profile=None): - ''' + """ Fetches information of a certain security group CLI Example: @@ -885,13 +901,13 @@ def show_security_group(security_group, profile=None): :param security_group: ID or name of security group to look up :param profile: Profile to build on (Optional) :return: Security group information - ''' + """ conn = _auth(profile) return conn.show_security_group(security_group) def create_security_group(name=None, description=None, profile=None): - ''' + """ Creates a new security group CLI Example: @@ -905,14 +921,13 @@ def create_security_group(name=None, description=None, profile=None): :param description: Description of security group (Optional) :param profile: Profile to build on (Optional) :return: Created security group information - ''' + """ conn = _auth(profile) return conn.create_security_group(name, description) -def update_security_group(security_group, name=None, description=None, - profile=None): - ''' +def update_security_group(security_group, name=None, description=None, profile=None): + """ Updates a security group CLI Example: @@ -927,13 +942,13 @@ def update_security_group(security_group, name=None, description=None, :param description: Description of security group (Optional) :param profile: Profile to build on (Optional) :return: Value of updated security group information - ''' + """ conn = _auth(profile) return conn.update_security_group(security_group, name, description) def delete_security_group(security_group, profile=None): - ''' + """ Deletes the specified security group CLI Example: @@ -945,13 +960,13 @@ def delete_security_group(security_group, profile=None): :param security_group: ID or name of security group to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_security_group(security_group) def list_security_group_rules(profile=None): - ''' + """ Fetches a list of all security group rules for a tenant CLI Example: @@ -963,13 +978,13 @@ def list_security_group_rules(profile=None): :param profile: Profile to build on (Optional) :return: List of security group rule - ''' + """ conn = _auth(profile) return conn.list_security_group_rules() def show_security_group_rule(security_group_rule_id, profile=None): - ''' + """ Fetches information of a certain security group rule CLI Example: @@ -981,20 +996,22 @@ def show_security_group_rule(security_group_rule_id, profile=None): :param security_group_rule_id: ID of security group rule to look up :param profile: Profile to build on (Optional) :return: Security group rule information - ''' + """ conn = _auth(profile) return conn.show_security_group_rule(security_group_rule_id) -def create_security_group_rule(security_group, - remote_group_id=None, - direction='ingress', - protocol=None, - port_range_min=None, - port_range_max=None, - ethertype='IPv4', - profile=None): - ''' +def create_security_group_rule( + security_group, + remote_group_id=None, + direction="ingress", + protocol=None, + port_range_min=None, + port_range_max=None, + ethertype="IPv4", + profile=None, +): + """ Creates a new security group rule CLI Example: @@ -1015,19 +1032,21 @@ def create_security_group_rule(security_group, :param ethertype: IPv4/IPv6, default: IPv4 (Optional) :param profile: Profile to build on (Optional) :return: Created security group rule information - ''' + """ conn = _auth(profile) - return conn.create_security_group_rule(security_group, - remote_group_id, - direction, - protocol, - port_range_min, - port_range_max, - ethertype) + return conn.create_security_group_rule( + security_group, + remote_group_id, + direction, + protocol, + port_range_min, + port_range_max, + ethertype, + ) def delete_security_group_rule(security_group_rule_id, profile=None): - ''' + """ Deletes the specified security group rule CLI Example: @@ -1039,13 +1058,13 @@ def delete_security_group_rule(security_group_rule_id, profile=None): :param security_group_rule_id: ID of security group rule to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_security_group_rule(security_group_rule_id) def list_vpnservices(retrieve_all=True, profile=None, **kwargs): - ''' + """ Fetches a list of all configured VPN services for a tenant CLI Example: @@ -1057,13 +1076,13 @@ def list_vpnservices(retrieve_all=True, profile=None, **kwargs): :param retrieve_all: True or False, default: True (Optional) :param profile: Profile to build on (Optional) :return: List of VPN service - ''' + """ conn = _auth(profile) return conn.list_vpnservices(retrieve_all, **kwargs) def show_vpnservice(vpnservice, profile=None, **kwargs): - ''' + """ Fetches information of a specific VPN service CLI Example: @@ -1075,13 +1094,13 @@ def show_vpnservice(vpnservice, profile=None, **kwargs): :param vpnservice: ID or name of vpn service to look up :param profile: Profile to build on (Optional) :return: VPN service information - ''' + """ conn = _auth(profile) return conn.show_vpnservice(vpnservice, **kwargs) def create_vpnservice(subnet, router, name, admin_state_up=True, profile=None): - ''' + """ Creates a new VPN service CLI Example: @@ -1097,13 +1116,13 @@ def create_vpnservice(subnet, router, name, admin_state_up=True, profile=None): default:True (Optional) :param profile: Profile to build on (Optional) :return: Created VPN service information - ''' + """ conn = _auth(profile) return conn.create_vpnservice(subnet, router, name, admin_state_up) def update_vpnservice(vpnservice, desc, profile=None): - ''' + """ Updates a VPN service CLI Example: @@ -1116,13 +1135,13 @@ def update_vpnservice(vpnservice, desc, profile=None): :param desc: Set a description for the VPN service :param profile: Profile to build on (Optional) :return: Value of updated VPN service information - ''' + """ conn = _auth(profile) return conn.update_vpnservice(vpnservice, desc) def delete_vpnservice(vpnservice, profile=None): - ''' + """ Deletes the specified VPN service CLI Example: @@ -1134,13 +1153,13 @@ def delete_vpnservice(vpnservice, profile=None): :param vpnservice: ID or name of vpn service to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_vpnservice(vpnservice) def list_ipsec_site_connections(profile=None): - ''' + """ Fetches all configured IPsec Site Connections for a tenant CLI Example: @@ -1152,13 +1171,13 @@ def list_ipsec_site_connections(profile=None): :param profile: Profile to build on (Optional) :return: List of IPSec site connection - ''' + """ conn = _auth(profile) return conn.list_ipsec_site_connections() def show_ipsec_site_connection(ipsec_site_connection, profile=None): - ''' + """ Fetches information of a specific IPsecSiteConnection CLI Example: @@ -1171,23 +1190,25 @@ def show_ipsec_site_connection(ipsec_site_connection, profile=None): to look up :param profile: Profile to build on (Optional) :return: IPSec site connection information - ''' + """ conn = _auth(profile) return conn.show_ipsec_site_connection(ipsec_site_connection) -def create_ipsec_site_connection(name, - ipsecpolicy, - ikepolicy, - vpnservice, - peer_cidrs, - peer_address, - peer_id, - psk, - admin_state_up=True, - profile=None, - **kwargs): - ''' +def create_ipsec_site_connection( + name, + ipsecpolicy, + ikepolicy, + vpnservice, + peer_cidrs, + peer_address, + peer_id, + psk, + admin_state_up=True, + profile=None, + **kwargs +): + """ Creates a new IPsecSiteConnection CLI Example: @@ -1218,22 +1239,24 @@ def create_ipsec_site_connection(name, :param dpd_timeout: Dead Peer Detection attribute (Optional) :param profile: Profile to build on (Optional) :return: Created IPSec site connection information - ''' + """ conn = _auth(profile) - return conn.create_ipsec_site_connection(name, - ipsecpolicy, - ikepolicy, - vpnservice, - peer_cidrs, - peer_address, - peer_id, - psk, - admin_state_up, - **kwargs) + return conn.create_ipsec_site_connection( + name, + ipsecpolicy, + ikepolicy, + vpnservice, + peer_cidrs, + peer_address, + peer_id, + psk, + admin_state_up, + **kwargs + ) def delete_ipsec_site_connection(ipsec_site_connection, profile=None): - ''' + """ Deletes the specified IPsecSiteConnection CLI Example: @@ -1245,13 +1268,13 @@ def delete_ipsec_site_connection(ipsec_site_connection, profile=None): :param ipsec_site_connection: ID or name of ipsec site connection to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_ipsec_site_connection(ipsec_site_connection) def list_ikepolicies(profile=None): - ''' + """ Fetches a list of all configured IKEPolicies for a tenant CLI Example: @@ -1263,13 +1286,13 @@ def list_ikepolicies(profile=None): :param profile: Profile to build on (Optional) :return: List of IKE policy - ''' + """ conn = _auth(profile) return conn.list_ikepolicies() def show_ikepolicy(ikepolicy, profile=None): - ''' + """ Fetches information of a specific IKEPolicy CLI Example: @@ -1281,13 +1304,13 @@ def show_ikepolicy(ikepolicy, profile=None): :param ikepolicy: ID or name of ikepolicy to look up :param profile: Profile to build on (Optional) :return: IKE policy information - ''' + """ conn = _auth(profile) return conn.show_ikepolicy(ikepolicy) def create_ikepolicy(name, profile=None, **kwargs): - ''' + """ Creates a new IKEPolicy CLI Example: @@ -1313,13 +1336,13 @@ def create_ikepolicy(name, profile=None, **kwargs): :param profile: Profile to build on (Optional) :param kwargs: :return: Created IKE policy information - ''' + """ conn = _auth(profile) return conn.create_ikepolicy(name, **kwargs) def delete_ikepolicy(ikepolicy, profile=None): - ''' + """ Deletes the specified IKEPolicy CLI Example: @@ -1331,13 +1354,13 @@ def delete_ikepolicy(ikepolicy, profile=None): :param ikepolicy: ID or name of IKE policy to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_ikepolicy(ikepolicy) def list_ipsecpolicies(profile=None): - ''' + """ Fetches a list of all configured IPsecPolicies for a tenant CLI Example: @@ -1349,13 +1372,13 @@ def list_ipsecpolicies(profile=None): :param profile: Profile to build on (Optional) :return: List of IPSec policy - ''' + """ conn = _auth(profile) return conn.list_ipsecpolicies() def show_ipsecpolicy(ipsecpolicy, profile=None): - ''' + """ Fetches information of a specific IPsecPolicy CLI Example: @@ -1367,13 +1390,13 @@ def show_ipsecpolicy(ipsecpolicy, profile=None): :param ipsecpolicy: ID or name of IPSec policy to look up :param profile: Profile to build on (Optional) :return: IPSec policy information - ''' + """ conn = _auth(profile) return conn.show_ipsecpolicy(ipsecpolicy) def create_ipsecpolicy(name, profile=None, **kwargs): - ''' + """ Creates a new IPsecPolicy CLI Example: @@ -1399,13 +1422,13 @@ def create_ipsecpolicy(name, profile=None, **kwargs): :param value: IPSec lifetime attribute. default: 3600 (Optional) :param profile: Profile to build on (Optional) :return: Created IPSec policy information - ''' + """ conn = _auth(profile) return conn.create_ipsecpolicy(name, **kwargs) def delete_ipsecpolicy(ipsecpolicy, profile=None): - ''' + """ Deletes the specified IPsecPolicy CLI Example: @@ -1417,13 +1440,13 @@ def delete_ipsecpolicy(ipsecpolicy, profile=None): :param ipsecpolicy: ID or name of IPSec policy to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_ipsecpolicy(ipsecpolicy) def list_firewall_rules(profile=None): - ''' + """ Fetches a list of all firewall rules for a tenant CLI Example: @@ -1434,13 +1457,13 @@ def list_firewall_rules(profile=None): :param profile: Profile to build on (Optional) :return: List of firewall rules - ''' + """ conn = _auth(profile) return conn.list_firewall_rules() def show_firewall_rule(firewall_rule, profile=None): - ''' + """ Fetches information of a specific firewall rule CLI Example: @@ -1454,13 +1477,13 @@ def show_firewall_rule(firewall_rule, profile=None): :param profile: Profile to build on (Optional) :return: firewall rule information - ''' + """ conn = _auth(profile) return conn.show_firewall_rule(firewall_rule) def create_firewall_rule(protocol, action, profile=None, **kwargs): - ''' + """ Creates a new firewall rule CLI Example: @@ -1484,13 +1507,13 @@ def create_firewall_rule(protocol, action, profile=None, **kwargs): :param destination_port: Destination port (integer in [1, 65535] or range in a:b). (Optional) :param shared: Set shared to True, default: False. (Optional) :param enabled: To enable this rule, default: True. (Optional) - ''' + """ conn = _auth(profile) return conn.create_firewall_rule(protocol, action, **kwargs) def delete_firewall_rule(firewall_rule, profile=None): - ''' + """ Deletes the specified firewall_rule CLI Example: @@ -1502,25 +1525,27 @@ def delete_firewall_rule(firewall_rule, profile=None): :param firewall_rule: ID or name of firewall rule to delete :param profile: Profile to build on (Optional) :return: True(Succeed) or False - ''' + """ conn = _auth(profile) return conn.delete_firewall_rule(firewall_rule) -def update_firewall_rule(firewall_rule, - protocol=None, - action=None, - name=None, - description=None, - ip_version=None, - source_ip_address=None, - destination_ip_address=None, - source_port=None, - destination_port=None, - shared=None, - enabled=None, - profile=None): - ''' +def update_firewall_rule( + firewall_rule, + protocol=None, + action=None, + name=None, + description=None, + ip_version=None, + source_ip_address=None, + destination_ip_address=None, + source_port=None, + destination_port=None, + shared=None, + enabled=None, + profile=None, +): + """ Update a firewall rule CLI Example: @@ -1545,15 +1570,26 @@ def update_firewall_rule(firewall_rule, :param shared: Set shared to True, default: False. (Optional) :param enabled: To enable this rule, default: True. (Optional) :param profile: Profile to build on (Optional) - ''' + """ conn = _auth(profile) - return conn.update_firewall_rule(firewall_rule, protocol, action, name, description, ip_version, - source_ip_address, destination_ip_address, source_port, destination_port, - shared, enabled) + return conn.update_firewall_rule( + firewall_rule, + protocol, + action, + name, + description, + ip_version, + source_ip_address, + destination_ip_address, + source_port, + destination_port, + shared, + enabled, + ) def list_firewalls(profile=None): - ''' + """ Fetches a list of all firewalls for a tenant CLI Example: @@ -1563,13 +1599,13 @@ def list_firewalls(profile=None): :param profile: Profile to build on (Optional) :return: List of firewalls - ''' + """ conn = _auth(profile) return conn.list_firewalls() def show_firewall(firewall, profile=None): - ''' + """ Fetches information of a specific firewall rule CLI Example: @@ -1581,13 +1617,13 @@ def show_firewall(firewall, profile=None): :param firewall: ID or name of firewall to look up :param profile: Profile to build on (Optional) :return: firewall information - ''' + """ conn = _auth(profile) return conn.show_firewall(firewall) def list_l3_agent_hosting_routers(router, profile=None): - ''' + """ List L3 agents hosting a router. CLI Example: @@ -1599,13 +1635,13 @@ def list_l3_agent_hosting_routers(router, profile=None): :param router:router name or ID to query. :param profile: Profile to build on (Optional) :return: L3 agents message. - ''' + """ conn = _auth(profile) return conn.list_l3_agent_hosting_routers(router) def list_agents(profile=None): - ''' + """ List agents. CLI Example: @@ -1616,6 +1652,6 @@ def list_agents(profile=None): :param profile: Profile to build on (Optional) :return: agents message. - ''' + """ conn = _auth(profile) return conn.list_agents() diff --git a/salt/modules/neutronng.py b/salt/modules/neutronng.py index d0b24f4b9be..65c0a1b98bc 100644 --- a/salt/modules/neutronng.py +++ b/salt/modules/neutronng.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Neutron module for interacting with OpenStack Neutron .. versionadded:: 2018.3.0 @@ -24,35 +24,39 @@ Example configuration project_domain_name: myproject auth_url: https://example.org:5000/v3 identity_api_version: 3 -''' +""" from __future__ import absolute_import, print_function, unicode_literals HAS_SHADE = False try: import shade + HAS_SHADE = True except ImportError: pass -__virtualname__ = 'neutronng' +__virtualname__ = "neutronng" def __virtual__(): - ''' + """ Only load this module if shade python module is installed - ''' + """ if HAS_SHADE: return __virtualname__ - return (False, 'The neutronng execution module failed to \ - load: shade python module is not available') + return ( + False, + "The neutronng execution module failed to \ + load: shade python module is not available", + ) def compare_changes(obj, **kwargs): - ''' + """ Compare two dicts returning only keys that exist in the first dict and are different in the second one - ''' + """ changes = {} for key, value in obj.items(): if key in kwargs: @@ -62,52 +66,52 @@ def compare_changes(obj, **kwargs): def _clean_kwargs(keep_name=False, **kwargs): - ''' + """ Sanatize the the arguments for use with shade - ''' - if 'name' in kwargs and not keep_name: - kwargs['name_or_id'] = kwargs.pop('name') + """ + if "name" in kwargs and not keep_name: + kwargs["name_or_id"] = kwargs.pop("name") - return __utils__['args.clean_kwargs'](**kwargs) + return __utils__["args.clean_kwargs"](**kwargs) def setup_clouds(auth=None): - ''' + """ Call functions to create Shade cloud objects in __context__ to take advantage of Shade's in-memory caching across several states - ''' + """ get_operator_cloud(auth) get_openstack_cloud(auth) def get_operator_cloud(auth=None): - ''' + """ Return an operator_cloud - ''' + """ if auth is None: - auth = __salt__['config.option']('neutron', {}) - if 'shade_opcloud' in __context__: - if __context__['shade_opcloud'].auth == auth: - return __context__['shade_opcloud'] - __context__['shade_opcloud'] = shade.operator_cloud(**auth) - return __context__['shade_opcloud'] + auth = __salt__["config.option"]("neutron", {}) + if "shade_opcloud" in __context__: + if __context__["shade_opcloud"].auth == auth: + return __context__["shade_opcloud"] + __context__["shade_opcloud"] = shade.operator_cloud(**auth) + return __context__["shade_opcloud"] def get_openstack_cloud(auth=None): - ''' + """ Return an openstack_cloud - ''' + """ if auth is None: - auth = __salt__['config.option']('neutron', {}) - if 'shade_oscloud' in __context__: - if __context__['shade_oscloud'].auth == auth: - return __context__['shade_oscloud'] - __context__['shade_oscloud'] = shade.openstack_cloud(**auth) - return __context__['shade_oscloud'] + auth = __salt__["config.option"]("neutron", {}) + if "shade_oscloud" in __context__: + if __context__["shade_oscloud"].auth == auth: + return __context__["shade_oscloud"] + __context__["shade_oscloud"] = shade.openstack_cloud(**auth) + return __context__["shade_oscloud"] def network_create(auth=None, **kwargs): - ''' + """ Create a network name @@ -141,14 +145,14 @@ def network_create(auth=None, **kwargs): "physical_network": "provider"}' \ project_id=1dcac318a83b4610b7a7f7ba01465548 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_network(**kwargs) def network_delete(auth=None, **kwargs): - ''' + """ Delete a network name_or_id @@ -161,14 +165,14 @@ def network_delete(auth=None, **kwargs): salt '*' neutronng.network_delete name_or_id=network1 salt '*' neutronng.network_delete name_or_id=1dcac318a83b4610b7a7f7ba01465548 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_network(**kwargs) def list_networks(auth=None, **kwargs): - ''' + """ List networks filters @@ -182,14 +186,14 @@ def list_networks(auth=None, **kwargs): salt '*' neutronng.list_networks \ filters='{"tenant_id": "1dcac318a83b4610b7a7f7ba01465548"}' - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_networks(**kwargs) def network_get(auth=None, **kwargs): - ''' + """ Get a single network filters @@ -201,14 +205,14 @@ def network_get(auth=None, **kwargs): salt '*' neutronng.network_get name=XLB4 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_network(**kwargs) def subnet_create(auth=None, **kwargs): - ''' + """ Create a subnet network_name_or_id @@ -279,14 +283,14 @@ def subnet_create(auth=None, **kwargs): salt '*' neutronng.subnet_create network_name_or_id=network1 \ subnet_name=subnet1 dns_nameservers='["8.8.8.8", "8.8.8.7"]' - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.create_subnet(**kwargs) def subnet_update(auth=None, **kwargs): - ''' + """ Update a subnet name_or_id @@ -322,14 +326,14 @@ def subnet_update(auth=None, **kwargs): salt '*' neutronng.subnet_update name=subnet1 subnet_name=subnet2 salt '*' neutronng.subnet_update name=subnet1 dns_nameservers='["8.8.8.8", "8.8.8.7"]' - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.update_subnet(**kwargs) def subnet_delete(auth=None, **kwargs): - ''' + """ Delete a subnet name @@ -343,14 +347,14 @@ def subnet_delete(auth=None, **kwargs): salt '*' neutronng.subnet_delete \ name=1dcac318a83b4610b7a7f7ba01465548 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_subnet(**kwargs) def list_subnets(auth=None, **kwargs): - ''' + """ List subnets filters @@ -364,14 +368,14 @@ def list_subnets(auth=None, **kwargs): salt '*' neutronng.list_subnets \ filters='{"tenant_id": "1dcac318a83b4610b7a7f7ba01465548"}' - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.list_subnets(**kwargs) def subnet_get(auth=None, **kwargs): - ''' + """ Get a single subnet filters @@ -383,14 +387,14 @@ def subnet_get(auth=None, **kwargs): salt '*' neutronng.subnet_get name=subnet1 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_subnet(**kwargs) def security_group_create(auth=None, **kwargs): - ''' + """ Create a security group. Use security_group_get to create default. project_id @@ -406,14 +410,14 @@ def security_group_create(auth=None, **kwargs): description="Very secure security group" \ project_id=1dcac318a83b4610b7a7f7ba01465548 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.create_security_group(**kwargs) def security_group_update(secgroup=None, auth=None, **kwargs): - ''' + """ Update a security group secgroup @@ -435,14 +439,14 @@ def security_group_update(secgroup=None, auth=None, **kwargs): description="Very secure security group" \ project_id=1dcac318a83b4610b7a7f7ba01465548 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(keep_name=True, **kwargs) return cloud.update_security_group(secgroup, **kwargs) def security_group_delete(auth=None, **kwargs): - ''' + """ Delete a security group name_or_id @@ -454,14 +458,14 @@ def security_group_delete(auth=None, **kwargs): salt '*' neutronng.security_group_delete name_or_id=secgroup1 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_security_group(**kwargs) def security_group_get(auth=None, **kwargs): - ''' + """ Get a single security group. This will create a default security group if one does not exist yet for a particular project id. @@ -478,14 +482,14 @@ def security_group_get(auth=None, **kwargs): salt '*' neutronng.security_group_get \ name=default\ filters='{"tenant_id":"2e778bb64ca64a199eb526b5958d8710"}' - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.get_security_group(**kwargs) def security_group_rule_create(auth=None, **kwargs): - ''' + """ Create a rule in a security group secgroup_name_or_id @@ -546,14 +550,14 @@ def security_group_rule_create(auth=None, **kwargs): secgroup_name_or_id=c0e1d1ce-7296-405e-919d-1c08217be529\ protocol=icmp project_id=1dcac318a83b4610b7a7f7ba01465548 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.create_security_group_rule(**kwargs) def security_group_rule_delete(auth=None, **kwargs): - ''' + """ Delete a security group name_or_id @@ -565,7 +569,7 @@ def security_group_rule_delete(auth=None, **kwargs): salt '*' neutronng.security_group_rule_delete name_or_id=1dcac318a83b4610b7a7f7ba01465548 - ''' + """ cloud = get_operator_cloud(auth) kwargs = _clean_kwargs(**kwargs) return cloud.delete_security_group_rule(**kwargs) diff --git a/salt/modules/nexus.py b/salt/modules/nexus.py index ef050ad03fc..ff0c31dd58e 100644 --- a/salt/modules/nexus.py +++ b/salt/modules/nexus.py @@ -1,48 +1,70 @@ # -*- coding: utf-8 -*- -''' +""" Module for fetching artifacts from Nexus 3.x .. versionadded:: 2018.3.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import base64 import logging +import os + +import salt.ext.six.moves.http_client # pylint: disable=import-error,redefined-builtin,no-name-in-module # Import Salt libs import salt.utils.files import salt.utils.stringutils -import salt.ext.six.moves.http_client # pylint: disable=import-error,redefined-builtin,no-name-in-module -from salt.ext.six.moves import urllib # pylint: disable=no-name-in-module -from salt.ext.six.moves.urllib.error import HTTPError, URLError # pylint: disable=no-name-in-module from salt.exceptions import CommandExecutionError +from salt.ext.six.moves import urllib # pylint: disable=no-name-in-module +from salt.ext.six.moves.urllib.error import ( # pylint: disable=no-name-in-module + HTTPError, + URLError, +) # Import 3rd party libs try: from salt._compat import ElementTree as ET + HAS_ELEMENT_TREE = True except ImportError: HAS_ELEMENT_TREE = False log = logging.getLogger(__name__) -__virtualname__ = 'nexus' +__virtualname__ = "nexus" def __virtual__(): - ''' + """ Only load if elementtree xml library is available. - ''' + """ if not HAS_ELEMENT_TREE: - return (False, 'Cannot load {0} module: ElementTree library unavailable'.format(__virtualname__)) + return ( + False, + "Cannot load {0} module: ElementTree library unavailable".format( + __virtualname__ + ), + ) else: return True -def get_latest_snapshot(nexus_url, repository, group_id, artifact_id, packaging, target_dir='/tmp', target_file=None, classifier=None, username=None, password=None): - ''' +def get_latest_snapshot( + nexus_url, + repository, + group_id, + artifact_id, + packaging, + target_dir="/tmp", + target_file=None, + classifier=None, + username=None, + password=None, +): + """ Gets latest snapshot of the given artifact nexus_url @@ -65,23 +87,61 @@ def get_latest_snapshot(nexus_url, repository, group_id, artifact_id, packaging, nexus username. Optional parameter. password nexus password. Optional parameter. - ''' - log.debug("======================== MODULE FUNCTION: nexus.get_latest_snapshot, nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, target_dir=%s, classifier=%s)", - nexus_url, repository, group_id, artifact_id, packaging, target_dir, classifier) + """ + log.debug( + "======================== MODULE FUNCTION: nexus.get_latest_snapshot, nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, target_dir=%s, classifier=%s)", + nexus_url, + repository, + group_id, + artifact_id, + packaging, + target_dir, + classifier, + ) headers = {} if username and password: - headers['Authorization'] = 'Basic {0}'.format(base64.encodestring('{0}:{1}'.format(username, password)).replace('\n', '')) - artifact_metadata = _get_artifact_metadata(nexus_url=nexus_url, repository=repository, group_id=group_id, artifact_id=artifact_id, headers=headers) - version = artifact_metadata['latest_version'] - snapshot_url, file_name = _get_snapshot_url(nexus_url=nexus_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, packaging=packaging, classifier=classifier, headers=headers) + headers["Authorization"] = "Basic {0}".format( + base64.encodestring("{0}:{1}".format(username, password)).replace("\n", "") + ) + artifact_metadata = _get_artifact_metadata( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + headers=headers, + ) + version = artifact_metadata["latest_version"] + snapshot_url, file_name = _get_snapshot_url( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + version=version, + packaging=packaging, + classifier=classifier, + headers=headers, + ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(snapshot_url, target_file, headers) -def get_snapshot(nexus_url, repository, group_id, artifact_id, packaging, version, snapshot_version=None, target_dir='/tmp', target_file=None, classifier=None, username=None, password=None): - ''' +def get_snapshot( + nexus_url, + repository, + group_id, + artifact_id, + packaging, + version, + snapshot_version=None, + target_dir="/tmp", + target_file=None, + classifier=None, + username=None, + password=None, +): + """ Gets snapshot of the desired version of the artifact nexus_url @@ -106,20 +166,51 @@ def get_snapshot(nexus_url, repository, group_id, artifact_id, packaging, versio nexus username. Optional parameter. password nexus password. Optional parameter. - ''' - log.debug('======================== MODULE FUNCTION: nexus.get_snapshot(nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)', - nexus_url, repository, group_id, artifact_id, packaging, version, target_dir, classifier) + """ + log.debug( + "======================== MODULE FUNCTION: nexus.get_snapshot(nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)", + nexus_url, + repository, + group_id, + artifact_id, + packaging, + version, + target_dir, + classifier, + ) headers = {} if username and password: - headers['Authorization'] = 'Basic {0}'.format(base64.encodestring('{0}:{1}'.format(username, password)).replace('\n', '')) - snapshot_url, file_name = _get_snapshot_url(nexus_url=nexus_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, packaging=packaging, snapshot_version=snapshot_version, classifier=classifier, headers=headers) + headers["Authorization"] = "Basic {0}".format( + base64.encodestring("{0}:{1}".format(username, password)).replace("\n", "") + ) + snapshot_url, file_name = _get_snapshot_url( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + version=version, + packaging=packaging, + snapshot_version=snapshot_version, + classifier=classifier, + headers=headers, + ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(snapshot_url, target_file, headers) -def get_snapshot_version_string(nexus_url, repository, group_id, artifact_id, packaging, version, classifier=None, username=None, password=None): - ''' +def get_snapshot_version_string( + nexus_url, + repository, + group_id, + artifact_id, + packaging, + version, + classifier=None, + username=None, + password=None, +): + """ Gets the specific version string of a snapshot of the desired version of the artifact nexus_url @@ -140,17 +231,47 @@ def get_snapshot_version_string(nexus_url, repository, group_id, artifact_id, pa nexus username. Optional parameter. password nexus password. Optional parameter. - ''' - log.debug('======================== MODULE FUNCTION: nexus.get_snapshot_version_string(nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, classifier=%s)', - nexus_url, repository, group_id, artifact_id, packaging, version, classifier) + """ + log.debug( + "======================== MODULE FUNCTION: nexus.get_snapshot_version_string(nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, classifier=%s)", + nexus_url, + repository, + group_id, + artifact_id, + packaging, + version, + classifier, + ) headers = {} if username and password: - headers['Authorization'] = 'Basic {0}'.format(base64.encodestring('{0}:{1}'.format(username, password)).replace('\n', '')) - return _get_snapshot_url(nexus_url=nexus_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, packaging=packaging, classifier=classifier, just_get_version_string=True) + headers["Authorization"] = "Basic {0}".format( + base64.encodestring("{0}:{1}".format(username, password)).replace("\n", "") + ) + return _get_snapshot_url( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + version=version, + packaging=packaging, + classifier=classifier, + just_get_version_string=True, + ) -def get_latest_release(nexus_url, repository, group_id, artifact_id, packaging, target_dir='/tmp', target_file=None, classifier=None, username=None, password=None): - ''' +def get_latest_release( + nexus_url, + repository, + group_id, + artifact_id, + packaging, + target_dir="/tmp", + target_file=None, + classifier=None, + username=None, + password=None, +): + """ Gets the latest release of the artifact nexus_url @@ -173,22 +294,52 @@ def get_latest_release(nexus_url, repository, group_id, artifact_id, packaging, nexus username. Optional parameter. password nexus password. Optional parameter. - ''' - log.debug('======================== MODULE FUNCTION: nexus.get_latest_release(nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, target_dir=%s, classifier=%s)', - nexus_url, repository, group_id, artifact_id, packaging, target_dir, classifier) + """ + log.debug( + "======================== MODULE FUNCTION: nexus.get_latest_release(nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, target_dir=%s, classifier=%s)", + nexus_url, + repository, + group_id, + artifact_id, + packaging, + target_dir, + classifier, + ) headers = {} if username and password: - headers['Authorization'] = 'Basic {0}'.format(base64.encodestring('{0}:{1}'.format(username, password)).replace('\n', '')) - artifact_metadata = _get_artifact_metadata(nexus_url=nexus_url, repository=repository, group_id=group_id, artifact_id=artifact_id, headers=headers) - version = artifact_metadata['latest_version'] - release_url, file_name = _get_release_url(repository, group_id, artifact_id, packaging, version, nexus_url, classifier) + headers["Authorization"] = "Basic {0}".format( + base64.encodestring("{0}:{1}".format(username, password)).replace("\n", "") + ) + artifact_metadata = _get_artifact_metadata( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + headers=headers, + ) + version = artifact_metadata["latest_version"] + release_url, file_name = _get_release_url( + repository, group_id, artifact_id, packaging, version, nexus_url, classifier + ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(release_url, target_file, headers) -def get_release(nexus_url, repository, group_id, artifact_id, packaging, version, target_dir='/tmp', target_file=None, classifier=None, username=None, password=None): - ''' +def get_release( + nexus_url, + repository, + group_id, + artifact_id, + packaging, + version, + target_dir="/tmp", + target_file=None, + classifier=None, + username=None, + password=None, +): + """ Gets the specified release of the artifact nexus_url @@ -213,13 +364,26 @@ def get_release(nexus_url, repository, group_id, artifact_id, packaging, version nexus username. Optional parameter. password nexus password. Optional parameter. - ''' - log.debug('======================== MODULE FUNCTION: nexus.get_release(nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)', - nexus_url, repository, group_id, artifact_id, packaging, version, target_dir, classifier) + """ + log.debug( + "======================== MODULE FUNCTION: nexus.get_release(nexus_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)", + nexus_url, + repository, + group_id, + artifact_id, + packaging, + version, + target_dir, + classifier, + ) headers = {} if username and password: - headers['Authorization'] = 'Basic {0}'.format(base64.encodestring('{0}:{1}'.format(username, password)).replace('\n', '')) - release_url, file_name = _get_release_url(repository, group_id, artifact_id, packaging, version, nexus_url, classifier) + headers["Authorization"] = "Basic {0}".format( + base64.encodestring("{0}:{1}".format(username, password)).replace("\n", "") + ) + release_url, file_name = _get_release_url( + repository, group_id, artifact_id, packaging, version, nexus_url, classifier + ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(release_url, target_file, headers) @@ -231,68 +395,93 @@ def __resolve_target_file(file_name, target_dir, target_file=None): return target_file -def _get_snapshot_url(nexus_url, repository, group_id, artifact_id, version, packaging, snapshot_version=None, classifier=None, headers=None, just_get_version_string=None): +def _get_snapshot_url( + nexus_url, + repository, + group_id, + artifact_id, + version, + packaging, + snapshot_version=None, + classifier=None, + headers=None, + just_get_version_string=None, +): if headers is None: headers = {} has_classifier = classifier is not None and classifier != "" if snapshot_version is None: - snapshot_version_metadata = _get_snapshot_version_metadata(nexus_url=nexus_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers) + snapshot_version_metadata = _get_snapshot_version_metadata( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + version=version, + headers=headers, + ) - if packaging not in snapshot_version_metadata['snapshot_versions']: - error_message = '''Cannot find requested packaging '{packaging}' in the snapshot version metadata. + if packaging not in snapshot_version_metadata["snapshot_versions"]: + error_message = """Cannot find requested packaging '{packaging}' in the snapshot version metadata. nexus_url: {nexus_url} repository: {repository} group_id: {group_id} artifact_id: {artifact_id} packaging: {packaging} classifier: {classifier} - version: {version}'''.format( - nexus_url=nexus_url, - repository=repository, - group_id=group_id, - artifact_id=artifact_id, - packaging=packaging, - classifier=classifier, - version=version) + version: {version}""".format( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + packaging=packaging, + classifier=classifier, + version=version, + ) raise nexusError(error_message) - if has_classifier and classifier not in snapshot_version_metadata['snapshot_versions']: - error_message = '''Cannot find requested classifier '{classifier}' in the snapshot version metadata. + if ( + has_classifier + and classifier not in snapshot_version_metadata["snapshot_versions"] + ): + error_message = """Cannot find requested classifier '{classifier}' in the snapshot version metadata. nexus_url: {nexus_url} repository: {repository} group_id: {group_id} artifact_id: {artifact_id} packaging: {packaging} classifier: {classifier} - version: {version}'''.format( - nexus_url=nexus_url, - repository=repository, - group_id=group_id, - artifact_id=artifact_id, - packaging=packaging, - classifier=classifier, - version=version) + version: {version}""".format( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + packaging=packaging, + classifier=classifier, + version=version, + ) raise nexusError(error_message) - snapshot_version = snapshot_version_metadata['snapshot_versions'][packaging] + snapshot_version = snapshot_version_metadata["snapshot_versions"][packaging] group_url = __get_group_id_subpath(group_id) - file_name = '{artifact_id}-{snapshot_version}{classifier}.{packaging}'.format( + file_name = "{artifact_id}-{snapshot_version}{classifier}.{packaging}".format( artifact_id=artifact_id, snapshot_version=snapshot_version, packaging=packaging, - classifier=__get_classifier_url(classifier)) + classifier=__get_classifier_url(classifier), + ) - snapshot_url = '{nexus_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}'.format( - nexus_url=nexus_url, - repository=repository, - group_url=group_url, - artifact_id=artifact_id, - version=version, - file_name=file_name) - log.debug('snapshot_url=%s', snapshot_url) + snapshot_url = "{nexus_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}".format( + nexus_url=nexus_url, + repository=repository, + group_url=group_url, + artifact_id=artifact_id, + version=version, + file_name=file_name, + ) + log.debug("snapshot_url=%s", snapshot_url) if just_get_version_string: return snapshot_version @@ -300,36 +489,41 @@ def _get_snapshot_url(nexus_url, repository, group_id, artifact_id, version, pac return snapshot_url, file_name -def _get_release_url(repository, group_id, artifact_id, packaging, version, nexus_url, classifier=None): +def _get_release_url( + repository, group_id, artifact_id, packaging, version, nexus_url, classifier=None +): group_url = __get_group_id_subpath(group_id) # for released versions the suffix for the file is same as version - file_name = '{artifact_id}-{version}{classifier}.{packaging}'.format( + file_name = "{artifact_id}-{version}{classifier}.{packaging}".format( artifact_id=artifact_id, version=version, packaging=packaging, - classifier=__get_classifier_url(classifier)) + classifier=__get_classifier_url(classifier), + ) - release_url = '{nexus_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}'.format( - nexus_url=nexus_url, - repository=repository, - group_url=group_url, - artifact_id=artifact_id, - version=version, - file_name=file_name) - log.debug('release_url=%s', release_url) + release_url = "{nexus_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}".format( + nexus_url=nexus_url, + repository=repository, + group_url=group_url, + artifact_id=artifact_id, + version=version, + file_name=file_name, + ) + log.debug("release_url=%s", release_url) return release_url, file_name def _get_artifact_metadata_url(nexus_url, repository, group_id, artifact_id): group_url = __get_group_id_subpath(group_id) # for released versions the suffix for the file is same as version - artifact_metadata_url = '{nexus_url}/{repository}/{group_url}/{artifact_id}/maven-metadata.xml'.format( - nexus_url=nexus_url, - repository=repository, - group_url=group_url, - artifact_id=artifact_id) - log.debug('artifact_metadata_url=%s', artifact_metadata_url) + artifact_metadata_url = "{nexus_url}/{repository}/{group_url}/{artifact_id}/maven-metadata.xml".format( + nexus_url=nexus_url, + repository=repository, + group_url=group_url, + artifact_id=artifact_id, + ) + log.debug("artifact_metadata_url=%s", artifact_metadata_url) return artifact_metadata_url @@ -339,156 +533,173 @@ def _get_artifact_metadata_xml(nexus_url, repository, group_id, artifact_id, hea nexus_url=nexus_url, repository=repository, group_id=group_id, - artifact_id=artifact_id + artifact_id=artifact_id, ) try: request = urllib.request.Request(artifact_metadata_url, None, headers) artifact_metadata_xml = urllib.request.urlopen(request).read() except (HTTPError, URLError) as err: - message = 'Could not fetch data from url: {0}. ERROR: {1}'.format( - artifact_metadata_url, - err + message = "Could not fetch data from url: {0}. ERROR: {1}".format( + artifact_metadata_url, err ) raise CommandExecutionError(message) - log.debug('artifact_metadata_xml=%s', artifact_metadata_xml) + log.debug("artifact_metadata_xml=%s", artifact_metadata_xml) return artifact_metadata_xml def _get_artifact_metadata(nexus_url, repository, group_id, artifact_id, headers): - metadata_xml = _get_artifact_metadata_xml(nexus_url=nexus_url, repository=repository, group_id=group_id, artifact_id=artifact_id, headers=headers) + metadata_xml = _get_artifact_metadata_xml( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + headers=headers, + ) root = ET.fromstring(metadata_xml) - assert group_id == root.find('groupId').text - assert artifact_id == root.find('artifactId').text - versions = root.find('versioning').find('versions') + assert group_id == root.find("groupId").text + assert artifact_id == root.find("artifactId").text + versions = root.find("versioning").find("versions") versionList = [] - for version in versions.iter('version'): + for version in versions.iter("version"): versionList.append(version.text) latest_version = max(versionList) - log.debug('latest version=%s', latest_version) - return { - 'latest_version': latest_version - } + log.debug("latest version=%s", latest_version) + return {"latest_version": latest_version} # functions for handling snapshots -def _get_snapshot_version_metadata_url(nexus_url, repository, group_id, artifact_id, version): +def _get_snapshot_version_metadata_url( + nexus_url, repository, group_id, artifact_id, version +): group_url = __get_group_id_subpath(group_id) # for released versions the suffix for the file is same as version - snapshot_version_metadata_url = '{nexus_url}/{repository}/{group_url}/{artifact_id}/{version}/maven-metadata.xml'.format( - nexus_url=nexus_url, - repository=repository, - group_url=group_url, - artifact_id=artifact_id, - version=version) - log.debug('snapshot_version_metadata_url=%s', snapshot_version_metadata_url) + snapshot_version_metadata_url = "{nexus_url}/{repository}/{group_url}/{artifact_id}/{version}/maven-metadata.xml".format( + nexus_url=nexus_url, + repository=repository, + group_url=group_url, + artifact_id=artifact_id, + version=version, + ) + log.debug("snapshot_version_metadata_url=%s", snapshot_version_metadata_url) return snapshot_version_metadata_url -def _get_snapshot_version_metadata_xml(nexus_url, repository, group_id, artifact_id, version, headers): +def _get_snapshot_version_metadata_xml( + nexus_url, repository, group_id, artifact_id, version, headers +): snapshot_version_metadata_url = _get_snapshot_version_metadata_url( nexus_url=nexus_url, repository=repository, group_id=group_id, artifact_id=artifact_id, - version=version + version=version, ) try: request = urllib.request.Request(snapshot_version_metadata_url, None, headers) snapshot_version_metadata_xml = urllib.request.urlopen(request).read() except (HTTPError, URLError) as err: - message = 'Could not fetch data from url: {0}. ERROR: {1}'.format( - snapshot_version_metadata_url, - err + message = "Could not fetch data from url: {0}. ERROR: {1}".format( + snapshot_version_metadata_url, err ) raise CommandExecutionError(message) - log.debug('snapshot_version_metadata_xml=%s', snapshot_version_metadata_xml) + log.debug("snapshot_version_metadata_xml=%s", snapshot_version_metadata_xml) return snapshot_version_metadata_xml -def _get_snapshot_version_metadata(nexus_url, repository, group_id, artifact_id, version, headers): - metadata_xml = _get_snapshot_version_metadata_xml(nexus_url=nexus_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers) +def _get_snapshot_version_metadata( + nexus_url, repository, group_id, artifact_id, version, headers +): + metadata_xml = _get_snapshot_version_metadata_xml( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + version=version, + headers=headers, + ) metadata = ET.fromstring(metadata_xml) - assert group_id == metadata.find('groupId').text - assert artifact_id == metadata.find('artifactId').text - assert version == metadata.find('version').text + assert group_id == metadata.find("groupId").text + assert artifact_id == metadata.find("artifactId").text + assert version == metadata.find("version").text - snapshot_versions = metadata.find('versioning').find('snapshotVersions') + snapshot_versions = metadata.find("versioning").find("snapshotVersions") extension_version_dict = {} for snapshot_version in snapshot_versions: - extension = snapshot_version.find('extension').text - value = snapshot_version.find('value').text + extension = snapshot_version.find("extension").text + value = snapshot_version.find("value").text extension_version_dict[extension] = value - if snapshot_version.find('classifier') is not None: - classifier = snapshot_version.find('classifier').text + if snapshot_version.find("classifier") is not None: + classifier = snapshot_version.find("classifier").text extension_version_dict[classifier] = value - return { - 'snapshot_versions': extension_version_dict - } + return {"snapshot_versions": extension_version_dict} def __save_artifact(artifact_url, target_file, headers): log.debug("__save_artifact(%s, %s)", artifact_url, target_file) - result = { - 'status': False, - 'changes': {}, - 'comment': '' - } + result = {"status": False, "changes": {}, "comment": ""} if os.path.isfile(target_file): log.debug("File %s already exists, checking checksum...", target_file) checksum_url = artifact_url + ".sha1" - checksum_success, artifact_sum, checksum_comment = __download(checksum_url, headers) + checksum_success, artifact_sum, checksum_comment = __download( + checksum_url, headers + ) if checksum_success: log.debug("Downloaded SHA1 SUM: %s", artifact_sum) - file_sum = __salt__['file.get_hash'](path=target_file, form='sha1') + file_sum = __salt__["file.get_hash"](path=target_file, form="sha1") log.debug("Target file (%s) SHA1 SUM: %s", target_file, file_sum) if artifact_sum == file_sum: - result['status'] = True - result['target_file'] = target_file - result['comment'] = 'File {0} already exists, checksum matches with nexus.\n' \ - 'Checksum URL: {1}'.format(target_file, checksum_url) + result["status"] = True + result["target_file"] = target_file + result["comment"] = ( + "File {0} already exists, checksum matches with nexus.\n" + "Checksum URL: {1}".format(target_file, checksum_url) + ) return result else: - result['comment'] = 'File {0} already exists, checksum does not match with nexus!\n'\ - 'Checksum URL: {1}'.format(target_file, checksum_url) + result["comment"] = ( + "File {0} already exists, checksum does not match with nexus!\n" + "Checksum URL: {1}".format(target_file, checksum_url) + ) else: - result['status'] = False - result['comment'] = checksum_comment + result["status"] = False + result["comment"] = checksum_comment return result - log.debug('Downloading: %s -> %s', artifact_url, target_file) + log.debug("Downloading: %s -> %s", artifact_url, target_file) try: request = urllib.request.Request(artifact_url, None, headers) f = urllib.request.urlopen(request) with salt.utils.files.fopen(target_file, "wb") as local_file: - local_file.write( - salt.utils.stringutils.to_bytes(f.read()) - ) - result['status'] = True - result['comment'] = __append_comment(('Artifact downloaded from URL: {0}'.format(artifact_url)), result['comment']) - result['changes']['downloaded_file'] = target_file - result['target_file'] = target_file + local_file.write(salt.utils.stringutils.to_bytes(f.read())) + result["status"] = True + result["comment"] = __append_comment( + ("Artifact downloaded from URL: {0}".format(artifact_url)), + result["comment"], + ) + result["changes"]["downloaded_file"] = target_file + result["target_file"] = target_file except (HTTPError, URLError) as e: - result['status'] = False - result['comment'] = __get_error_comment(e, artifact_url) + result["status"] = False + result["comment"] = __get_error_comment(e, artifact_url) return result def __get_group_id_subpath(group_id): - group_url = group_id.replace('.', '/') + group_url = group_id.replace(".", "/") return group_url @@ -498,7 +709,7 @@ def __get_classifier_url(classifier): def __download(request_url, headers): - log.debug('Downloading content from %s', request_url) + log.debug("Downloading content from %s", request_url) success = False content = None @@ -516,22 +727,27 @@ def __download(request_url, headers): def __get_error_comment(http_error, request_url): if http_error.code == salt.ext.six.moves.http_client.NOT_FOUND: - comment = 'HTTP Error 404. Request URL: ' + request_url + comment = "HTTP Error 404. Request URL: " + request_url elif http_error.code == salt.ext.six.moves.http_client.CONFLICT: - comment = 'HTTP Error 409: Conflict. Requested URL: {0}. \n' \ - 'This error may be caused by reading snapshot artifact from non-snapshot repository.'.format(request_url) + comment = ( + "HTTP Error 409: Conflict. Requested URL: {0}. \n" + "This error may be caused by reading snapshot artifact from non-snapshot repository.".format( + request_url + ) + ) else: - comment = 'HTTP Error {err_code}. Request URL: {url}'.format(err_code=http_error.code, url=request_url) + comment = "HTTP Error {err_code}. Request URL: {url}".format( + err_code=http_error.code, url=request_url + ) return comment -def __append_comment(new_comment, current_comment=''): - return current_comment+'\n'+new_comment +def __append_comment(new_comment, current_comment=""): + return current_comment + "\n" + new_comment class nexusError(Exception): - def __init__(self, value): super(nexusError, self).__init__() self.value = value diff --git a/salt/modules/nfs3.py b/salt/modules/nfs3.py index 21329bb8e5c..97a9783a38f 100644 --- a/salt/modules/nfs3.py +++ b/salt/modules/nfs3.py @@ -1,32 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing NFS version 3. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -# Import salt libs -from salt.ext import six import salt.utils.files import salt.utils.path import salt.utils.stringutils +# Import salt libs +from salt.ext import six + log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' - if not salt.utils.path.which('showmount'): - return (False, 'The nfs3 execution module failed to load: the showmount binary is not in the path.') + """ + if not salt.utils.path.which("showmount"): + return ( + False, + "The nfs3 execution module failed to load: the showmount binary is not in the path.", + ) return True -def list_exports(exports='/etc/exports'): - ''' +def list_exports(exports="/etc/exports"): + """ List configured exports CLI Example: @@ -34,13 +38,13 @@ def list_exports(exports='/etc/exports'): .. code-block:: bash salt '*' nfs.list_exports - ''' + """ ret = {} - with salt.utils.files.fopen(exports, 'r') as efl: + with salt.utils.files.fopen(exports, "r") as efl: for line in salt.utils.stringutils.to_unicode(efl.read()).splitlines(): if not line: continue - if line.startswith('#'): + if line.startswith("#"): continue comps = line.split() @@ -50,24 +54,24 @@ def list_exports(exports='/etc/exports'): newshares = [] for perm in comps[1:]: - if perm.startswith('/'): + if perm.startswith("/"): newshares.append(perm) continue - permcomps = perm.split('(') - permcomps[1] = permcomps[1].replace(')', '') + permcomps = perm.split("(") + permcomps[1] = permcomps[1].replace(")", "") hosts = permcomps[0] if not isinstance(hosts, six.string_types): # Lists, etc would silently mangle /etc/exports - raise TypeError('hosts argument must be a string') - options = permcomps[1].split(',') - ret[comps[0]].append({'hosts': hosts, 'options': options}) + raise TypeError("hosts argument must be a string") + options = permcomps[1].split(",") + ret[comps[0]].append({"hosts": hosts, "options": options}) for share in newshares: ret[share] = ret[comps[0]] return ret -def del_export(exports='/etc/exports', path=None): - ''' +def del_export(exports="/etc/exports", path=None): + """ Remove an export CLI Example: @@ -75,15 +79,15 @@ def del_export(exports='/etc/exports', path=None): .. code-block:: bash salt '*' nfs.del_export /media/storage - ''' + """ edict = list_exports(exports) del edict[path] _write_exports(exports, edict) return edict -def add_export(exports='/etc/exports', path=None, hosts=None, options=None): - ''' +def add_export(exports="/etc/exports", path=None, hosts=None, options=None): + """ Add an export CLI Example: @@ -91,16 +95,16 @@ def add_export(exports='/etc/exports', path=None, hosts=None, options=None): .. code-block:: bash salt '*' nfs3.add_export path='/srv/test' hosts='127.0.0.1' options=['rw'] - ''' + """ if options is None: options = [] if not isinstance(hosts, six.string_types): # Lists, etc would silently mangle /etc/exports - raise TypeError('hosts argument must be a string') + raise TypeError("hosts argument must be a string") edict = list_exports(exports) if path not in edict: edict[path] = [] - new = {'hosts': hosts, 'options': options} + new = {"hosts": hosts, "options": options} edict[path].append(new) _write_exports(exports, edict) @@ -108,7 +112,7 @@ def add_export(exports='/etc/exports', path=None, hosts=None, options=None): def _write_exports(exports, edict): - ''' + """ Write an exports file to disk If multiple shares were initially configured per line, like: @@ -119,19 +123,19 @@ def _write_exports(exports, edict): /media/storage *(ro,sync,no_subtree_check) /media/data *(ro,sync,no_subtree_check) - ''' - with salt.utils.files.fopen(exports, 'w') as efh: + """ + with salt.utils.files.fopen(exports, "w") as efh: for export in edict: line = salt.utils.stringutils.to_str(export) for perms in edict[export]: - hosts = perms['hosts'] - options = ','.join(perms['options']) - line += ' {0}({1})'.format(hosts, options) - efh.write('{0}\n'.format(line)) + hosts = perms["hosts"] + options = ",".join(perms["options"]) + line += " {0}({1})".format(hosts, options) + efh.write("{0}\n".format(line)) def reload_exports(): - ''' + """ Trigger a reload of the exports file to apply changes CLI Example: @@ -139,16 +143,16 @@ def reload_exports(): .. code-block:: bash salt '*' nfs3.reload_exports - ''' + """ ret = {} - command = 'exportfs -r' + command = "exportfs -r" - output = __salt__['cmd.run_all'](command) - ret['stdout'] = output['stdout'] - ret['stderr'] = output['stderr'] + output = __salt__["cmd.run_all"](command) + ret["stdout"] = output["stdout"] + ret["stderr"] = output["stderr"] # exportfs always returns 0, so retcode is useless # We will consider it an error if stderr is nonempty - ret['result'] = output['stderr'] == '' + ret["result"] = output["stderr"] == "" return ret diff --git a/salt/modules/nftables.py b/salt/modules/nftables.py index 2130e9ca805..744b434e879 100644 --- a/salt/modules/nftables.py +++ b/salt/modules/nftables.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Support for nftables -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -9,67 +9,68 @@ import json import logging import re -# Import salt libs -from salt.ext import six import salt.utils.data import salt.utils.files import salt.utils.path -from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS -from salt.exceptions import ( - CommandExecutionError -) +from salt.exceptions import CommandExecutionError +# Import salt libs +from salt.ext import six +from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS # Set up logging log = logging.getLogger(__name__) _NFTABLES_FAMILIES = { - 'ipv4': 'ip', - 'ip4': 'ip', - 'ip': 'ip', - 'ipv6': 'ip6', - 'ip6': 'ip6', - 'inet': 'inet', - 'arp': 'arp', - 'bridge': 'bridge', - 'netdev': 'netdev' - } + "ipv4": "ip", + "ip4": "ip", + "ip": "ip", + "ipv6": "ip6", + "ip6": "ip6", + "inet": "inet", + "arp": "arp", + "bridge": "bridge", + "netdev": "netdev", +} def __virtual__(): - ''' + """ Only load the module if nftables is installed - ''' - if salt.utils.path.which('nft'): - return 'nftables' - return (False, 'The nftables execution module failed to load: nftables is not installed.') + """ + if salt.utils.path.which("nft"): + return "nftables" + return ( + False, + "The nftables execution module failed to load: nftables is not installed.", + ) def _nftables_cmd(): - ''' + """ Return correct command - ''' - return 'nft' + """ + return "nft" -def _conf(family='ip'): - ''' +def _conf(family="ip"): + """ Use the same file for rules for now. - ''' - if __grains__['os_family'] == 'RedHat': - return '/etc/nftables' - elif __grains__['os_family'] == 'Arch': - return '/etc/nftables' - elif __grains__['os_family'] == 'Debian': - return '/etc/nftables' - elif __grains__['os'] == 'Gentoo': - return '/etc/nftables' + """ + if __grains__["os_family"] == "RedHat": + return "/etc/nftables" + elif __grains__["os_family"] == "Arch": + return "/etc/nftables" + elif __grains__["os_family"] == "Debian": + return "/etc/nftables" + elif __grains__["os"] == "Gentoo": + return "/etc/nftables" else: return False def version(): - ''' + """ Return version from nftables --version CLI Example: @@ -78,15 +79,22 @@ def version(): salt '*' nftables.version - ''' - cmd = '{0} --version' . format(_nftables_cmd()) - out = __salt__['cmd.run'](cmd).split() + """ + cmd = "{0} --version".format(_nftables_cmd()) + out = __salt__["cmd.run"](cmd).split() return out[1] -def build_rule(table=None, chain=None, command=None, position='', full=None, family='ipv4', - **kwargs): - ''' +def build_rule( + table=None, + chain=None, + command=None, + position="", + full=None, + family="ipv4", + **kwargs +): + """ Build a well-formatted nftables rule based on kwargs. A `table` and `chain` are not required, unless `full` is True. @@ -117,107 +125,105 @@ def build_rule(table=None, chain=None, command=None, position='', full=None, fam full=True match=state state=related,established jump=accept \\ family=ipv6 - ''' - ret = {'comment': '', - 'rule': '', - 'result': False} + """ + ret = {"comment": "", "rule": "", "result": False} - if 'target' in kwargs: - kwargs['jump'] = kwargs['target'] - del kwargs['target'] + if "target" in kwargs: + kwargs["jump"] = kwargs["target"] + del kwargs["target"] - for ignore in list(_STATE_INTERNAL_KEYWORDS) + ['chain', 'save', 'table']: + for ignore in list(_STATE_INTERNAL_KEYWORDS) + ["chain", "save", "table"]: if ignore in kwargs: del kwargs[ignore] - rule = '' - proto = '' + rule = "" + proto = "" nft_family = _NFTABLES_FAMILIES[family] - if 'if' in kwargs: - rule += 'meta iifname {0} '.format(kwargs['if']) - del kwargs['if'] + if "if" in kwargs: + rule += "meta iifname {0} ".format(kwargs["if"]) + del kwargs["if"] - if 'of' in kwargs: - rule += 'meta oifname {0} '.format(kwargs['of']) - del kwargs['of'] + if "of" in kwargs: + rule += "meta oifname {0} ".format(kwargs["of"]) + del kwargs["of"] - if 'proto' in kwargs: - proto = kwargs['proto'] + if "proto" in kwargs: + proto = kwargs["proto"] - if 'state' in kwargs: - del kwargs['state'] + if "state" in kwargs: + del kwargs["state"] - if 'connstate' in kwargs: - rule += 'ct state {{ {0}}} '.format(kwargs['connstate']) - del kwargs['connstate'] + if "connstate" in kwargs: + rule += "ct state {{ {0}}} ".format(kwargs["connstate"]) + del kwargs["connstate"] - if 'dport' in kwargs: - kwargs['dport'] = six.text_type(kwargs['dport']) - if ':' in kwargs['dport']: - kwargs['dport'] = kwargs['dport'].replace(':', '-') - rule += 'dport {{ {0} }} '.format(kwargs['dport']) - del kwargs['dport'] + if "dport" in kwargs: + kwargs["dport"] = six.text_type(kwargs["dport"]) + if ":" in kwargs["dport"]: + kwargs["dport"] = kwargs["dport"].replace(":", "-") + rule += "dport {{ {0} }} ".format(kwargs["dport"]) + del kwargs["dport"] - if 'sport' in kwargs: - kwargs['sport'] = six.text_type(kwargs['sport']) - if ':' in kwargs['sport']: - kwargs['sport'] = kwargs['sport'].replace(':', '-') - rule += 'sport {{ {0} }} '.format(kwargs['sport']) - del kwargs['sport'] + if "sport" in kwargs: + kwargs["sport"] = six.text_type(kwargs["sport"]) + if ":" in kwargs["sport"]: + kwargs["sport"] = kwargs["sport"].replace(":", "-") + rule += "sport {{ {0} }} ".format(kwargs["sport"]) + del kwargs["sport"] - if 'dports' in kwargs: + if "dports" in kwargs: # nftables reverse sorts the ports from # high to low, create rule like this # so that the check will work - _dports = kwargs['dports'].split(',') + _dports = kwargs["dports"].split(",") _dports = [int(x) for x in _dports] _dports.sort(reverse=True) - kwargs['dports'] = ', '.join(six.text_type(x) for x in _dports) + kwargs["dports"] = ", ".join(six.text_type(x) for x in _dports) - rule += 'dport {{ {0} }} '.format(kwargs['dports']) - del kwargs['dports'] + rule += "dport {{ {0} }} ".format(kwargs["dports"]) + del kwargs["dports"] - if 'sports' in kwargs: + if "sports" in kwargs: # nftables reverse sorts the ports from # high to low, create rule like this # so that the check will work - _sports = kwargs['sports'].split(',') + _sports = kwargs["sports"].split(",") _sports = [int(x) for x in _sports] _sports.sort(reverse=True) - kwargs['sports'] = ', '.join(six.text_type(x) for x in _sports) + kwargs["sports"] = ", ".join(six.text_type(x) for x in _sports) - rule += 'sport {{ {0} }} '.format(kwargs['sports']) - del kwargs['sports'] + rule += "sport {{ {0} }} ".format(kwargs["sports"]) + del kwargs["sports"] # Jumps should appear last, except for any arguments that are passed to # jumps, which of course need to follow. after_jump = [] - if 'jump' in kwargs: - after_jump.append('{0} '.format(kwargs['jump'])) - del kwargs['jump'] + if "jump" in kwargs: + after_jump.append("{0} ".format(kwargs["jump"])) + del kwargs["jump"] - if 'j' in kwargs: - after_jump.append('{0} '.format(kwargs['j'])) - del kwargs['j'] + if "j" in kwargs: + after_jump.append("{0} ".format(kwargs["j"])) + del kwargs["j"] - if 'to-port' in kwargs: - after_jump.append('--to-port {0} '.format(kwargs['to-port'])) - del kwargs['to-port'] + if "to-port" in kwargs: + after_jump.append("--to-port {0} ".format(kwargs["to-port"])) + del kwargs["to-port"] - if 'to-ports' in kwargs: - after_jump.append('--to-ports {0} '.format(kwargs['to-ports'])) - del kwargs['to-ports'] + if "to-ports" in kwargs: + after_jump.append("--to-ports {0} ".format(kwargs["to-ports"])) + del kwargs["to-ports"] - if 'to-destination' in kwargs: - after_jump.append('--to-destination {0} '.format(kwargs['to-destination'])) - del kwargs['to-destination'] + if "to-destination" in kwargs: + after_jump.append("--to-destination {0} ".format(kwargs["to-destination"])) + del kwargs["to-destination"] - if 'reject-with' in kwargs: - after_jump.append('--reject-with {0} '.format(kwargs['reject-with'])) - del kwargs['reject-with'] + if "reject-with" in kwargs: + after_jump.append("--reject-with {0} ".format(kwargs["reject-with"])) + del kwargs["reject-with"] for item in after_jump: rule += item @@ -226,57 +232,47 @@ def build_rule(table=None, chain=None, command=None, position='', full=None, fam rule = rule.strip() # Insert the protocol prior to dport or sport - rule = rule.replace('dport', '{0} dport'.format(proto)) - rule = rule.replace('sport', '{0} sport'.format(proto)) + rule = rule.replace("dport", "{0} dport".format(proto)) + rule = rule.replace("sport", "{0} sport".format(proto)) - ret['rule'] = rule + ret["rule"] = rule - if full in ['True', 'true']: + if full in ["True", "true"]: if not table: - ret['comment'] = 'Table needs to be specified' + ret["comment"] = "Table needs to be specified" return ret if not chain: - ret['comment'] = 'Chain needs to be specified' + ret["comment"] = "Chain needs to be specified" return ret if not command: - ret['comment'] = 'Command needs to be specified' + ret["comment"] = "Command needs to be specified" return ret - if command in ['Insert', 'insert', 'INSERT']: + if command in ["Insert", "insert", "INSERT"]: if position: - ret['rule'] = '{0} insert rule {1} {2} {3} ' \ - 'position {4} {5}'.format(_nftables_cmd(), - nft_family, - table, - chain, - position, - rule) + ret["rule"] = "{0} insert rule {1} {2} {3} " "position {4} {5}".format( + _nftables_cmd(), nft_family, table, chain, position, rule + ) else: - ret['rule'] = '{0} insert rule ' \ - '{1} {2} {3} {4}'.format(_nftables_cmd(), - nft_family, - table, - chain, - rule) + ret["rule"] = "{0} insert rule " "{1} {2} {3} {4}".format( + _nftables_cmd(), nft_family, table, chain, rule + ) else: - ret['rule'] = '{0} {1} rule {2} {3} {4} {5}'.format(_nftables_cmd(), - command, - nft_family, - table, - chain, - rule) + ret["rule"] = "{0} {1} rule {2} {3} {4} {5}".format( + _nftables_cmd(), command, nft_family, table, chain, rule + ) - if ret['rule']: - ret['comment'] = 'Successfully built rule' - ret['result'] = True + if ret["rule"]: + ret["comment"] = "Successfully built rule" + ret["result"] = True return ret def get_saved_rules(conf_file=None): - ''' + """ Return a data structure of the rules in the conf file CLI Example: @@ -285,7 +281,7 @@ def get_saved_rules(conf_file=None): salt '*' nftables.get_saved_rules - ''' + """ if _conf() and not conf_file: conf_file = _conf() @@ -296,14 +292,14 @@ def get_saved_rules(conf_file=None): tmpline = line.strip() if not tmpline: continue - if tmpline.startswith('#'): + if tmpline.startswith("#"): continue rules.append(line) return rules -def list_tables(family='ipv4'): - ''' +def list_tables(family="ipv4"): + """ Return a data structure of the current, in-memory tables CLI Example: @@ -314,26 +310,26 @@ def list_tables(family='ipv4'): salt '*' nftables.list_tables family=ipv6 - ''' + """ nft_family = _NFTABLES_FAMILIES[family] tables = [] - cmd = '{0} --json --numeric --numeric --numeric ' \ - 'list tables {1}'. format(_nftables_cmd(), - nft_family) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} --json --numeric --numeric --numeric " "list tables {1}".format( + _nftables_cmd(), nft_family + ) + out = __salt__["cmd.run"](cmd, python_shell=False) if not out: return tables data = json.loads(out) - for item in data.get('nftables', []): - if 'metainfo' not in item: - tables.append(item['table']) + for item in data.get("nftables", []): + if "metainfo" not in item: + tables.append(item["table"]) log.debug(tables) return tables -def get_rules(family='ipv4'): - ''' +def get_rules(family="ipv4"): + """ Return a data structure of the current, in-memory rules CLI Example: @@ -344,23 +340,23 @@ def get_rules(family='ipv4'): salt '*' nftables.get_rules family=ipv6 - ''' + """ tables = list_tables(family) nft_family = _NFTABLES_FAMILIES[family] rules = [] for table in tables: - table_name = table['name'] - cmd = '{0} --numeric --numeric --numeric ' \ - 'list table {1} {2}'.format(_nftables_cmd(), - nft_family, table_name) - out = __salt__['cmd.run'](cmd, python_shell=False) + table_name = table["name"] + cmd = "{0} --numeric --numeric --numeric " "list table {1} {2}".format( + _nftables_cmd(), nft_family, table_name + ) + out = __salt__["cmd.run"](cmd, python_shell=False) rules.append(out) return rules -def save(filename=None, family='ipv4'): - ''' +def save(filename=None, family="ipv4"): + """ Save the current in-memory rules to disk CLI Example: @@ -368,32 +364,32 @@ def save(filename=None, family='ipv4'): .. code-block:: bash salt '*' nftables.save /etc/nftables - ''' + """ if _conf() and not filename: filename = _conf() - nft_families = ['ip', 'ip6', 'arp', 'bridge'] + nft_families = ["ip", "ip6", "arp", "bridge"] rules = "#! nft -f\n" for family in nft_families: out = get_rules(family) if out: - rules += '\n' - rules = rules + '\n'.join(out) - rules = rules + '\n' + rules += "\n" + rules = rules + "\n".join(out) + rules = rules + "\n" try: - with salt.utils.files.fopen(filename, 'wb') as _fh: + with salt.utils.files.fopen(filename, "wb") as _fh: # Write out any changes _fh.write(salt.utils.data.encode(rules)) except (IOError, OSError) as exc: raise CommandExecutionError( - 'Problem writing to configuration file: {0}'.format(exc) + "Problem writing to configuration file: {0}".format(exc) ) return rules -def get_rule_handle(table='filter', chain=None, rule=None, family='ipv4'): - ''' +def get_rule_handle(table="filter", chain=None, rule=None, family="ipv4"): + """ Get the handle for a particular rule This function accepts a rule in a standard nftables command format, @@ -412,47 +408,46 @@ def get_rule_handle(table='filter', chain=None, rule=None, family='ipv4'): salt '*' nftables.get_rule_handle filter input \\ rule='tcp dport 22 log accept' \\ family=ipv6 - ''' - ret = {'comment': '', - 'result': False} + """ + ret = {"comment": "", "result": False} if not chain: - ret['comment'] = 'Chain needs to be specified' + ret["comment"] = "Chain needs to be specified" return ret if not rule: - ret['comment'] = 'Rule needs to be specified' + ret["comment"] = "Rule needs to be specified" return ret res = check_table(table, family=family) - if not res['result']: + if not res["result"]: return res res = check_chain(table, chain, family=family) - if not res['result']: + if not res["result"]: return res res = check(table, chain, rule, family=family) - if not res['result']: + if not res["result"]: return res nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} --numeric --numeric --numeric --handle list chain {1} {2} {3}'.\ - format(_nftables_cmd(), nft_family, table, chain) - out = __salt__['cmd.run'](cmd, python_shell=False) - rules = re.split('\n+', out) + cmd = "{0} --numeric --numeric --numeric --handle list chain {1} {2} {3}".format( + _nftables_cmd(), nft_family, table, chain + ) + out = __salt__["cmd.run"](cmd, python_shell=False) + rules = re.split("\n+", out) - pat = re.compile(r'{0} # handle (?P\d+)'.format(rule)) + pat = re.compile(r"{0} # handle (?P\d+)".format(rule)) for r in rules: match = pat.search(r) if match: - return {'result': True, 'handle': match.group('handle')} - return {'result': False, - 'comment': 'Could not find rule {0}'.format(rule)} + return {"result": True, "handle": match.group("handle")} + return {"result": False, "comment": "Could not find rule {0}".format(rule)} -def check(table='filter', chain=None, rule=None, family='ipv4'): - ''' +def check(table="filter", chain=None, rule=None, family="ipv4"): + """ Check for the existence of a rule in the table and chain This function accepts a rule in a standard nftables command format, @@ -471,44 +466,50 @@ def check(table='filter', chain=None, rule=None, family='ipv4'): salt '*' nftables.check filter input \\ rule='tcp dport 22 log accept' \\ family=ipv6 - ''' - ret = {'comment': '', - 'result': False} + """ + ret = {"comment": "", "result": False} if not chain: - ret['comment'] = 'Chain needs to be specified' + ret["comment"] = "Chain needs to be specified" return ret if not rule: - ret['comment'] = 'Rule needs to be specified' + ret["comment"] = "Rule needs to be specified" return ret res = check_table(table, family=family) - if not res['result']: + if not res["result"]: return res res = check_chain(table, chain, family=family) - if not res['result']: + if not res["result"]: return res nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} --handle --numeric --numeric --numeric list chain {1} {2} {3}'.\ - format(_nftables_cmd(), nft_family, table, chain) - search_rule = '{0} #'.format(rule) - out = __salt__['cmd.run'](cmd, python_shell=False).find(search_rule) + cmd = "{0} --handle --numeric --numeric --numeric list chain {1} {2} {3}".format( + _nftables_cmd(), nft_family, table, chain + ) + search_rule = "{0} #".format(rule) + out = __salt__["cmd.run"](cmd, python_shell=False).find(search_rule) if out == -1: - ret['comment'] = 'Rule {0} in chain {1} in table {2} in family {3} does not exist'.\ - format(rule, chain, table, family) + ret[ + "comment" + ] = "Rule {0} in chain {1} in table {2} in family {3} does not exist".format( + rule, chain, table, family + ) else: - ret['comment'] = 'Rule {0} in chain {1} in table {2} in family {3} exists'.\ - format(rule, chain, table, family) - ret['result'] = True + ret[ + "comment" + ] = "Rule {0} in chain {1} in table {2} in family {3} exists".format( + rule, chain, table, family + ) + ret["result"] = True return ret -def check_chain(table='filter', chain=None, family='ipv4'): - ''' +def check_chain(table="filter", chain=None, family="ipv4"): + """ .. versionadded:: 2014.7.0 Check for the existence of a chain in the table @@ -521,60 +522,62 @@ def check_chain(table='filter', chain=None, family='ipv4'): IPv6: salt '*' nftables.check_chain filter input family=ipv6 - ''' + """ - ret = {'comment': '', - 'result': False} + ret = {"comment": "", "result": False} if not chain: - ret['comment'] = 'Chain needs to be specified' + ret["comment"] = "Chain needs to be specified" return ret nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} list table {1} {2}' . format(_nftables_cmd(), nft_family, table) - out = __salt__['cmd.run'](cmd, python_shell=False).find('chain {0} {{'.format(chain)) + cmd = "{0} list table {1} {2}".format(_nftables_cmd(), nft_family, table) + out = __salt__["cmd.run"](cmd, python_shell=False).find( + "chain {0} {{".format(chain) + ) if out == -1: - ret['comment'] = 'Chain {0} in table {1} in family {2} does not exist'.\ - format(chain, table, family) + ret["comment"] = "Chain {0} in table {1} in family {2} does not exist".format( + chain, table, family + ) else: - ret['comment'] = 'Chain {0} in table {1} in family {2} exists'.\ - format(chain, table, family) - ret['result'] = True + ret["comment"] = "Chain {0} in table {1} in family {2} exists".format( + chain, table, family + ) + ret["result"] = True return ret -def check_table(table=None, family='ipv4'): - ''' +def check_table(table=None, family="ipv4"): + """ Check for the existence of a table CLI Example:: salt '*' nftables.check_table nat - ''' - ret = {'comment': '', - 'result': False} + """ + ret = {"comment": "", "result": False} if not table: - ret['comment'] = 'Table needs to be specified' + ret["comment"] = "Table needs to be specified" return ret nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} list tables {1}' . format(_nftables_cmd(), nft_family) - out = __salt__['cmd.run'](cmd, python_shell=False).find('table {0} {1}'.format(nft_family, table)) + cmd = "{0} list tables {1}".format(_nftables_cmd(), nft_family) + out = __salt__["cmd.run"](cmd, python_shell=False).find( + "table {0} {1}".format(nft_family, table) + ) if out == -1: - ret['comment'] = 'Table {0} in family {1} does not exist'.\ - format(table, family) + ret["comment"] = "Table {0} in family {1} does not exist".format(table, family) else: - ret['comment'] = 'Table {0} in family {1} exists'.\ - format(table, family) - ret['result'] = True + ret["comment"] = "Table {0} in family {1} exists".format(table, family) + ret["result"] = True return ret -def new_table(table, family='ipv4'): - ''' +def new_table(table, family="ipv4"): + """ .. versionadded:: 2014.7.0 Create new custom table. @@ -587,34 +590,33 @@ def new_table(table, family='ipv4'): IPv6: salt '*' nftables.new_table filter family=ipv6 - ''' - ret = {'comment': '', - 'result': False} + """ + ret = {"comment": "", "result": False} if not table: - ret['comment'] = 'Table needs to be specified' + ret["comment"] = "Table needs to be specified" return ret res = check_table(table, family=family) - if res['result']: + if res["result"]: return res nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} add table {1} {2}'.format(_nftables_cmd(), nft_family, table) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} add table {1} {2}".format(_nftables_cmd(), nft_family, table) + out = __salt__["cmd.run"](cmd, python_shell=False) if not out: - ret['comment'] = 'Table {0} in family {1} created'.\ - format(table, family) - ret['result'] = True + ret["comment"] = "Table {0} in family {1} created".format(table, family) + ret["result"] = True else: - ret['comment'] = 'Table {0} in family {1} could not be created'.\ - format(table, family) + ret["comment"] = "Table {0} in family {1} could not be created".format( + table, family + ) return ret -def delete_table(table, family='ipv4'): - ''' +def delete_table(table, family="ipv4"): + """ .. versionadded:: 2014.7.0 Create new custom table. @@ -627,34 +629,35 @@ def delete_table(table, family='ipv4'): IPv6: salt '*' nftables.delete_table filter family=ipv6 - ''' - ret = {'comment': '', - 'result': False} + """ + ret = {"comment": "", "result": False} if not table: - ret['comment'] = 'Table needs to be specified' + ret["comment"] = "Table needs to be specified" return ret res = check_table(table, family=family) - if not res['result']: + if not res["result"]: return res nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} delete table {1} {2}'.format(_nftables_cmd(), nft_family, table) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} delete table {1} {2}".format(_nftables_cmd(), nft_family, table) + out = __salt__["cmd.run"](cmd, python_shell=False) if not out: - ret['comment'] = 'Table {0} in family {1} deleted'.\ - format(table, family) - ret['result'] = True + ret["comment"] = "Table {0} in family {1} deleted".format(table, family) + ret["result"] = True else: - ret['comment'] = 'Table {0} in family {1} could not be deleted'.\ - format(table, family) + ret["comment"] = "Table {0} in family {1} could not be deleted".format( + table, family + ) return ret -def new_chain(table='filter', chain=None, table_type=None, hook=None, priority=None, family='ipv4'): - ''' +def new_chain( + table="filter", chain=None, table_type=None, hook=None, priority=None, family="ipv4" +): + """ .. versionadded:: 2014.7.0 Create new chain to the specified table. @@ -677,50 +680,54 @@ def new_chain(table='filter', chain=None, table_type=None, hook=None, priority=N table_type=filter hook=input priority=0 family=ipv6 salt '*' nftables.new_chain filter foo family=ipv6 - ''' - ret = {'comment': '', - 'result': False} + """ + ret = {"comment": "", "result": False} if not chain: - ret['comment'] = 'Chain needs to be specified' + ret["comment"] = "Chain needs to be specified" return ret res = check_table(table, family=family) - if not res['result']: + if not res["result"]: return res res = check_chain(table, chain, family=family) - if res['result']: - ret['comment'] = 'Chain {0} in table {1} in family {2} already exists'.\ - format(chain, table, family) + if res["result"]: + ret["comment"] = "Chain {0} in table {1} in family {2} already exists".format( + chain, table, family + ) return ret nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} add chain {1} {2} {3}'.\ - format(_nftables_cmd(), nft_family, table, chain) + cmd = "{0} add chain {1} {2} {3}".format(_nftables_cmd(), nft_family, table, chain) if table_type or hook or priority: if table_type and hook and six.text_type(priority): - cmd = r'{0} \{{ type {1} hook {2} priority {3}\; \}}'.\ - format(cmd, table_type, hook, priority) + cmd = r"{0} \{{ type {1} hook {2} priority {3}\; \}}".format( + cmd, table_type, hook, priority + ) else: # Specify one, require all - ret['comment'] = 'Table_type, hook, and priority required.' + ret["comment"] = "Table_type, hook, and priority required." return ret - out = __salt__['cmd.run'](cmd, python_shell=False) + out = __salt__["cmd.run"](cmd, python_shell=False) if not out: - ret['comment'] = 'Chain {0} in table {1} in family {2} created'.\ - format(chain, table, family) - ret['result'] = True + ret["comment"] = "Chain {0} in table {1} in family {2} created".format( + chain, table, family + ) + ret["result"] = True else: - ret['comment'] = 'Chain {0} in table {1} in family {2} could not be created'.\ - format(chain, table, family) + ret[ + "comment" + ] = "Chain {0} in table {1} in family {2} could not be created".format( + chain, table, family + ) return ret -def delete_chain(table='filter', chain=None, family='ipv4'): - ''' +def delete_chain(table="filter", chain=None, family="ipv4"): + """ .. versionadded:: 2014.7.0 Delete the chain from the specified table. @@ -737,39 +744,43 @@ def delete_chain(table='filter', chain=None, family='ipv4'): salt '*' nftables.delete_chain filter input family=ipv6 salt '*' nftables.delete_chain filter foo family=ipv6 - ''' - ret = {'comment': '', - 'result': False} + """ + ret = {"comment": "", "result": False} if not chain: - ret['comment'] = 'Chain needs to be specified' + ret["comment"] = "Chain needs to be specified" return ret res = check_table(table, family=family) - if not res['result']: + if not res["result"]: return res res = check_chain(table, chain, family=family) - if not res['result']: + if not res["result"]: return res nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} delete chain {1} {2} {3}'.\ - format(_nftables_cmd(), nft_family, table, chain) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} delete chain {1} {2} {3}".format( + _nftables_cmd(), nft_family, table, chain + ) + out = __salt__["cmd.run"](cmd, python_shell=False) if not out: - ret['comment'] = 'Chain {0} in table {1} in family {2} deleted'.\ - format(chain, table, family) - ret['result'] = True + ret["comment"] = "Chain {0} in table {1} in family {2} deleted".format( + chain, table, family + ) + ret["result"] = True else: - ret['comment'] = 'Chain {0} in table {1} in family {2} could not be deleted'.\ - format(chain, table, family) + ret[ + "comment" + ] = "Chain {0} in table {1} in family {2} could not be deleted".format( + chain, table, family + ) return ret -def append(table='filter', chain=None, rule=None, family='ipv4'): - ''' +def append(table="filter", chain=None, rule=None, family="ipv4"): + """ Append a rule to the specified table & chain. This function accepts a rule in a standard nftables command format, @@ -788,49 +799,63 @@ def append(table='filter', chain=None, rule=None, family='ipv4'): salt '*' nftables.append filter input \\ rule='tcp dport 22 log accept' \\ family=ipv6 - ''' - ret = {'comment': 'Failed to append rule {0} to chain {1} in table {2}.'.format(rule, chain, table), - 'result': False} + """ + ret = { + "comment": "Failed to append rule {0} to chain {1} in table {2}.".format( + rule, chain, table + ), + "result": False, + } if not chain: - ret['comment'] = 'Chain needs to be specified' + ret["comment"] = "Chain needs to be specified" return ret if not rule: - ret['comment'] = 'Rule needs to be specified' + ret["comment"] = "Rule needs to be specified" return ret res = check_table(table, family=family) - if not res['result']: + if not res["result"]: return res res = check_chain(table, chain, family=family) - if not res['result']: + if not res["result"]: return res res = check(table, chain, rule, family=family) - if res['result']: - ret['comment'] = 'Rule {0} chain {1} in table {2} in family {3} already exists'.\ - format(rule, chain, table, family) + if res["result"]: + ret[ + "comment" + ] = "Rule {0} chain {1} in table {2} in family {3} already exists".format( + rule, chain, table, family + ) return ret nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} add rule {1} {2} {3} {4}'.\ - format(_nftables_cmd(), nft_family, table, chain, rule) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} add rule {1} {2} {3} {4}".format( + _nftables_cmd(), nft_family, table, chain, rule + ) + out = __salt__["cmd.run"](cmd, python_shell=False) if len(out) == 0: - ret['result'] = True - ret['comment'] = 'Added rule "{0}" chain {1} in table {2} in family {3}.'.\ - format(rule, chain, table, family) + ret["result"] = True + ret[ + "comment" + ] = 'Added rule "{0}" chain {1} in table {2} in family {3}.'.format( + rule, chain, table, family + ) else: - ret['comment'] = 'Failed to add rule "{0}" chain {1} in table {2} in family {3}.'.\ - format(rule, chain, table, family) + ret[ + "comment" + ] = 'Failed to add rule "{0}" chain {1} in table {2} in family {3}.'.format( + rule, chain, table, family + ) return ret -def insert(table='filter', chain=None, position=None, rule=None, family='ipv4'): - ''' +def insert(table="filter", chain=None, position=None, rule=None, family="ipv4"): + """ Insert a rule into the specified table & chain, at the specified position. If position is not specified, rule will be inserted in first position. @@ -858,53 +883,66 @@ def insert(table='filter', chain=None, position=None, rule=None, family='ipv4'): salt '*' nftables.insert filter input position=3 \\ rule='tcp dport 22 log accept' \\ family=ipv6 - ''' - ret = {'comment': 'Failed to insert rule {0} to table {1}.'.format(rule, table), - 'result': False} + """ + ret = { + "comment": "Failed to insert rule {0} to table {1}.".format(rule, table), + "result": False, + } if not chain: - ret['comment'] = 'Chain needs to be specified' + ret["comment"] = "Chain needs to be specified" return ret if not rule: - ret['comment'] = 'Rule needs to be specified' + ret["comment"] = "Rule needs to be specified" return ret res = check_table(table, family=family) - if not res['result']: + if not res["result"]: return res res = check_chain(table, chain, family=family) - if not res['result']: + if not res["result"]: return res res = check(table, chain, rule, family=family) - if res['result']: - ret['comment'] = 'Rule {0} chain {1} in table {2} in family {3} already exists'.\ - format(rule, chain, table, family) + if res["result"]: + ret[ + "comment" + ] = "Rule {0} chain {1} in table {2} in family {3} already exists".format( + rule, chain, table, family + ) return ret nft_family = _NFTABLES_FAMILIES[family] if position: - cmd = '{0} insert rule {1} {2} {3} position {4} {5}'.\ - format(_nftables_cmd(), nft_family, table, chain, position, rule) + cmd = "{0} insert rule {1} {2} {3} position {4} {5}".format( + _nftables_cmd(), nft_family, table, chain, position, rule + ) else: - cmd = '{0} insert rule {1} {2} {3} {4}'.\ - format(_nftables_cmd(), nft_family, table, chain, rule) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} insert rule {1} {2} {3} {4}".format( + _nftables_cmd(), nft_family, table, chain, rule + ) + out = __salt__["cmd.run"](cmd, python_shell=False) if len(out) == 0: - ret['result'] = True - ret['comment'] = 'Added rule "{0}" chain {1} in table {2} in family {3}.'.\ - format(rule, chain, table, family) + ret["result"] = True + ret[ + "comment" + ] = 'Added rule "{0}" chain {1} in table {2} in family {3}.'.format( + rule, chain, table, family + ) else: - ret['comment'] = 'Failed to add rule "{0}" chain {1} in table {2} in family {3}.'.\ - format(rule, chain, table, family) + ret[ + "comment" + ] = 'Failed to add rule "{0}" chain {1} in table {2} in family {3}.'.format( + rule, chain, table, family + ) return ret -def delete(table, chain=None, position=None, rule=None, family='ipv4'): - ''' +def delete(table, chain=None, position=None, rule=None, family="ipv4"): + """ Delete a rule from the specified table & chain, specifying either the rule in its entirety, or the rule's position in the chain. @@ -928,26 +966,31 @@ def delete(table, chain=None, position=None, rule=None, family='ipv4'): salt '*' nftables.delete filter input \\ rule='tcp dport 22 log accept' \\ family=ipv6 - ''' - ret = {'comment': 'Failed to delete rule {0} in table {1}.'.format(rule, table), - 'result': False} + """ + ret = { + "comment": "Failed to delete rule {0} in table {1}.".format(rule, table), + "result": False, + } if position and rule: - ret['comment'] = 'Only specify a position or a rule, not both' + ret["comment"] = "Only specify a position or a rule, not both" return ret res = check_table(table, family=family) - if not res['result']: + if not res["result"]: return res res = check_chain(table, chain, family=family) - if not res['result']: + if not res["result"]: return res res = check(table, chain, rule, family=family) - if not res['result']: - ret['comment'] = 'Rule {0} chain {1} in table {2} in family {3} does not exist'.\ - format(rule, chain, table, family) + if not res["result"]: + ret[ + "comment" + ] = "Rule {0} chain {1} in table {2} in family {3} does not exist".format( + rule, chain, table, family + ) return ret # nftables rules can only be deleted using the handle @@ -956,22 +999,29 @@ def delete(table, chain=None, position=None, rule=None, family='ipv4'): position = get_rule_handle(table, chain, rule, family) nft_family = _NFTABLES_FAMILIES[family] - cmd = '{0} delete rule {1} {2} {3} handle {4}'.\ - format(_nftables_cmd(), nft_family, table, chain, position) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} delete rule {1} {2} {3} handle {4}".format( + _nftables_cmd(), nft_family, table, chain, position + ) + out = __salt__["cmd.run"](cmd, python_shell=False) if len(out) == 0: - ret['result'] = True - ret['comment'] = 'Deleted rule "{0}" in chain {1} in table {2} in family {3}.'.\ - format(rule, chain, table, family) + ret["result"] = True + ret[ + "comment" + ] = 'Deleted rule "{0}" in chain {1} in table {2} in family {3}.'.format( + rule, chain, table, family + ) else: - ret['comment'] = 'Failed to delete rule "{0}" in chain {1} table {2} in family {3}'.\ - format(rule, chain, table, family) + ret[ + "comment" + ] = 'Failed to delete rule "{0}" in chain {1} table {2} in family {3}'.format( + rule, chain, table, family + ) return ret -def flush(table='filter', chain='', family='ipv4'): - ''' +def flush(table="filter", chain="", family="ipv4"): + """ Flush the chain in the specified table, flush all chains in the specified table if chain is not specified. @@ -985,34 +1035,38 @@ def flush(table='filter', chain='', family='ipv4'): IPv6: salt '*' nftables.flush filter input family=ipv6 - ''' - ret = {'comment': 'Failed to flush rules from chain {0} in table {1}.'.format(chain, table), - 'result': False} + """ + ret = { + "comment": "Failed to flush rules from chain {0} in table {1}.".format( + chain, table + ), + "result": False, + } res = check_table(table, family=family) - if not res['result']: + if not res["result"]: return res nft_family = _NFTABLES_FAMILIES[family] if chain: res = check_chain(table, chain, family=family) - if not res['result']: + if not res["result"]: return res - cmd = '{0} flush chain {1} {2} {3}'.\ - format(_nftables_cmd(), nft_family, table, chain) - comment = 'from chain {0} in table {1} in family {2}.'.\ - format(chain, table, family) + cmd = "{0} flush chain {1} {2} {3}".format( + _nftables_cmd(), nft_family, table, chain + ) + comment = "from chain {0} in table {1} in family {2}.".format( + chain, table, family + ) else: - cmd = '{0} flush table {1} {2}'.\ - format(_nftables_cmd(), nft_family, table) - comment = 'from table {0} in family {1}.'.\ - format(table, family) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} flush table {1} {2}".format(_nftables_cmd(), nft_family, table) + comment = "from table {0} in family {1}.".format(table, family) + out = __salt__["cmd.run"](cmd, python_shell=False) if len(out) == 0: - ret['result'] = True - ret['comment'] = 'Flushed rules {0}'.format(comment) + ret["result"] = True + ret["comment"] = "Flushed rules {0}".format(comment) else: - ret['comment'] = 'Failed to flush rules {0}'.format(comment) + ret["comment"] = "Failed to flush rules {0}".format(comment) return ret diff --git a/salt/modules/nginx.py b/salt/modules/nginx.py index b72a1328ce4..a1fba39406f 100644 --- a/salt/modules/nginx.py +++ b/salt/modules/nginx.py @@ -1,17 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Support for nginx -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import 3rd-party libs -from salt.ext.six.moves.urllib.request import urlopen as _urlopen # pylint: disable=no-name-in-module,import-error +import re + +import salt.utils.decorators as decorators # Import salt libs import salt.utils.path -import salt.utils.decorators as decorators -import re +# Import 3rd-party libs +from salt.ext.six.moves.urllib.request import urlopen as _urlopen # Cache the output of running which('nginx') so this module @@ -19,20 +20,23 @@ import re # for nginx over and over and over for each function herein @decorators.memoize def __detect_os(): - return salt.utils.path.which('nginx') + return salt.utils.path.which("nginx") def __virtual__(): - ''' + """ Only load the module if nginx is installed - ''' + """ if __detect_os(): return True - return (False, 'The nginx execution module cannot be loaded: nginx is not installed.') + return ( + False, + "The nginx execution module cannot be loaded: nginx is not installed.", + ) def version(): - ''' + """ Return server version from nginx -v CLI Example: @@ -40,15 +44,15 @@ def version(): .. code-block:: bash salt '*' nginx.version - ''' - cmd = '{0} -v'.format(__detect_os()) - out = __salt__['cmd.run'](cmd).splitlines() - ret = out[0].split(': ') + """ + cmd = "{0} -v".format(__detect_os()) + out = __salt__["cmd.run"](cmd).splitlines() + ret = out[0].split(": ") return ret[-1] def build_info(): - ''' + """ Return server and build arguments CLI Example: @@ -56,22 +60,22 @@ def build_info(): .. code-block:: bash salt '*' nginx.build_info - ''' - ret = {'info': []} - out = __salt__['cmd.run']('{0} -V'.format(__detect_os())) + """ + ret = {"info": []} + out = __salt__["cmd.run"]("{0} -V".format(__detect_os())) for i in out.splitlines(): - if i.startswith('configure argument'): - ret['build arguments'] = re.findall(r"(?:[^\s]*'.*')|(?:[^\s]+)", i)[2:] + if i.startswith("configure argument"): + ret["build arguments"] = re.findall(r"(?:[^\s]*'.*')|(?:[^\s]+)", i)[2:] continue - ret['info'].append(i) + ret["info"].append(i) return ret def configtest(): - ''' + """ test configuration and exit CLI Example: @@ -79,28 +83,28 @@ def configtest(): .. code-block:: bash salt '*' nginx.configtest - ''' + """ ret = {} - cmd = '{0} -t'.format(__detect_os()) - out = __salt__['cmd.run_all'](cmd) + cmd = "{0} -t".format(__detect_os()) + out = __salt__["cmd.run_all"](cmd) - if out['retcode'] != 0: - ret['comment'] = 'Syntax Error' - ret['stderr'] = out['stderr'] - ret['result'] = False + if out["retcode"] != 0: + ret["comment"] = "Syntax Error" + ret["stderr"] = out["stderr"] + ret["result"] = False return ret - ret['comment'] = 'Syntax OK' - ret['stdout'] = out['stderr'] - ret['result'] = True + ret["comment"] = "Syntax OK" + ret["stdout"] = out["stderr"] + ret["result"] = True return ret def signal(signal=None): - ''' + """ Signals nginx to start, reload, reopen or stop. CLI Example: @@ -108,28 +112,28 @@ def signal(signal=None): .. code-block:: bash salt '*' nginx.signal reload - ''' - valid_signals = ('start', 'reopen', 'stop', 'quit', 'reload') + """ + valid_signals = ("start", "reopen", "stop", "quit", "reload") if signal not in valid_signals: return # Make sure you use the right arguments if signal == "start": - arguments = '' + arguments = "" else: - arguments = ' -s {0}'.format(signal) + arguments = " -s {0}".format(signal) cmd = __detect_os() + arguments - out = __salt__['cmd.run_all'](cmd) + out = __salt__["cmd.run_all"](cmd) # A non-zero return code means fail - if out['retcode'] and out['stderr']: - ret = out['stderr'].strip() + if out["retcode"] and out["stderr"]: + ret = out["stderr"].strip() # 'nginxctl configtest' returns 'Syntax OK' to stderr - elif out['stderr']: - ret = out['stderr'].strip() - elif out['stdout']: - ret = out['stdout'].strip() + elif out["stderr"]: + ret = out["stderr"].strip() + elif out["stdout"]: + ret = out["stdout"].strip() # No output for something like: nginxctl graceful else: ret = 'Command: "{0}" completed successfully!'.format(cmd) @@ -165,11 +169,11 @@ def status(url="http://127.0.0.1/status"): # "Reading: 0 Writing: 1 Waiting: 0 " _, reading, _, writing, _, waiting = lines[3].split() return { - 'active connections': int(active_connections), - 'accepted': int(accepted), - 'handled': int(handled), - 'requests': int(requests), - 'reading': int(reading), - 'writing': int(writing), - 'waiting': int(waiting), + "active connections": int(active_connections), + "accepted": int(accepted), + "handled": int(handled), + "requests": int(requests), + "reading": int(reading), + "writing": int(writing), + "waiting": int(waiting), } diff --git a/salt/modules/nilrt_ip.py b/salt/modules/nilrt_ip.py index b1309e278fe..257c2174a09 100644 --- a/salt/modules/nilrt_ip.py +++ b/salt/modules/nilrt_ip.py @@ -1,25 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" The networking module for NI Linux Real-Time distro -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import time import os import re +import time # Import salt libs import salt.exceptions import salt.utils.files import salt.utils.validate.net +from salt.ext import six # Import 3rd-party libs # pylint: disable=import-error,redefined-builtin,no-name-in-module -from salt.ext.six.moves import map, range, configparser -from salt.ext import six +from salt.ext.six.moves import configparser, map, range + # pylint: enable=import-error,redefined-builtin,no-name-in-module try: @@ -45,218 +47,228 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'ip' +__virtualname__ = "ip" -SERVICE_PATH = '/net/connman/service/' -INTERFACES_CONFIG = '/var/lib/connman/interfaces.config' -NIRTCFG_PATH = '/usr/local/natinst/bin/nirtcfg' -INI_FILE = '/etc/natinst/share/ni-rt.ini' -_CONFIG_TRUE = ['yes', 'on', 'true', '1', True] +SERVICE_PATH = "/net/connman/service/" +INTERFACES_CONFIG = "/var/lib/connman/interfaces.config" +NIRTCFG_PATH = "/usr/local/natinst/bin/nirtcfg" +INI_FILE = "/etc/natinst/share/ni-rt.ini" +_CONFIG_TRUE = ["yes", "on", "true", "1", True] IFF_LOOPBACK = 0x8 IFF_RUNNING = 0x40 -NIRTCFG_ETHERCAT = 'EtherCAT' +NIRTCFG_ETHERCAT = "EtherCAT" def _assume_condition(condition, err): - ''' + """ Raise an exception if the condition is false - ''' + """ if not condition: raise RuntimeError(err) def __virtual__(): - ''' + """ Confine this module to NI Linux Real-Time based distros - ''' + """ try: - msg = 'The nilrt_ip module could not be loaded: unsupported OS family' - _assume_condition(__grains__['os_family'] == 'NILinuxRT', msg) - _assume_condition(CaseInsensitiveDict, 'The python package request is not installed') - _assume_condition(pyiface, 'The python pyiface package is not installed') - if __grains__['lsb_distrib_id'] != 'nilrt': - _assume_condition(pyconnman, 'The python package pyconnman is not installed') - _assume_condition(dbus, 'The python DBus package is not installed') - _assume_condition(_get_state() != 'offline', 'Connman is not running') + msg = "The nilrt_ip module could not be loaded: unsupported OS family" + _assume_condition(__grains__["os_family"] == "NILinuxRT", msg) + _assume_condition( + CaseInsensitiveDict, "The python package request is not installed" + ) + _assume_condition(pyiface, "The python pyiface package is not installed") + if __grains__["lsb_distrib_id"] != "nilrt": + _assume_condition( + pyconnman, "The python package pyconnman is not installed" + ) + _assume_condition(dbus, "The python DBus package is not installed") + _assume_condition(_get_state() != "offline", "Connman is not running") except RuntimeError as exc: return False, str(exc) return __virtualname__ def _get_state(): - ''' + """ Returns the state of connman - ''' + """ try: - return pyconnman.ConnManager().get_property('State') + return pyconnman.ConnManager().get_property("State") except KeyError: - return 'offline' + return "offline" except dbus.DBusException as exc: - raise salt.exceptions.CommandExecutionError('Connman daemon error: {0}'.format(exc)) + raise salt.exceptions.CommandExecutionError( + "Connman daemon error: {0}".format(exc) + ) def _get_technologies(): - ''' + """ Returns the technologies of connman - ''' - tech = '' + """ + tech = "" technologies = pyconnman.ConnManager().get_technologies() for path, params in technologies: - tech += '{0}\n\tName = {1}\n\tType = {2}\n\tPowered = {3}\n\tConnected = {4}\n'.format( - path, params['Name'], params['Type'], params['Powered'] == 1, params['Connected'] == 1) + tech += "{0}\n\tName = {1}\n\tType = {2}\n\tPowered = {3}\n\tConnected = {4}\n".format( + path, + params["Name"], + params["Type"], + params["Powered"] == 1, + params["Connected"] == 1, + ) return tech def _get_services(): - ''' + """ Returns a list with all connman services - ''' + """ serv = [] services = pyconnman.ConnManager().get_services() for path, _ in services: - serv.append(six.text_type(path[len(SERVICE_PATH):])) + serv.append(six.text_type(path[len(SERVICE_PATH) :])) return serv def _connected(service): - ''' + """ Verify if a connman service is connected - ''' - state = pyconnman.ConnService(os.path.join(SERVICE_PATH, service)).get_property('State') - return state == 'online' or state == 'ready' + """ + state = pyconnman.ConnService(os.path.join(SERVICE_PATH, service)).get_property( + "State" + ) + return state == "online" or state == "ready" def _space_delimited_list(value): - ''' + """ validate that a value contains one or more space-delimited values - ''' + """ if isinstance(value, str): - items = value.split(' ') + items = value.split(" ") valid = items and all(items) else: - valid = hasattr(value, '__iter__') and (value != []) + valid = hasattr(value, "__iter__") and (value != []) if valid: - return (True, 'space-delimited string') + return (True, "space-delimited string") else: - return (False, '{0} is not a valid list.\n'.format(value)) + return (False, "{0} is not a valid list.\n".format(value)) def _validate_ipv4(value): - ''' + """ validate ipv4 values - ''' + """ if len(value) == 3: if not salt.utils.validate.net.ipv4_addr(value[0].strip()): - return False, 'Invalid ip address: {0} for ipv4 option'.format(value[0]) + return False, "Invalid ip address: {0} for ipv4 option".format(value[0]) if not salt.utils.validate.net.netmask(value[1].strip()): - return False, 'Invalid netmask: {0} for ipv4 option'.format(value[1]) + return False, "Invalid netmask: {0} for ipv4 option".format(value[1]) if not salt.utils.validate.net.ipv4_addr(value[2].strip()): - return False, 'Invalid gateway: {0} for ipv4 option'.format(value[2]) + return False, "Invalid gateway: {0} for ipv4 option".format(value[2]) else: - return False, 'Invalid value: {0} for ipv4 option'.format(value) - return True, '' + return False, "Invalid value: {0} for ipv4 option".format(value) + return True, "" def _interface_to_service(iface): - ''' + """ returns the coresponding service to given interface if exists, otherwise return None - ''' + """ for _service in _get_services(): service_info = pyconnman.ConnService(os.path.join(SERVICE_PATH, _service)) - if service_info.get_property('Ethernet')['Interface'] == iface: + if service_info.get_property("Ethernet")["Interface"] == iface: return _service return None def _get_service_info(service): - ''' + """ return details about given connman service - ''' + """ service_info = pyconnman.ConnService(os.path.join(SERVICE_PATH, service)) data = { - 'label': service, - 'wireless': service_info.get_property('Type') == 'wifi', - 'connectionid': six.text_type(service_info.get_property('Ethernet')['Interface']), - 'hwaddr': six.text_type(service_info.get_property('Ethernet')['Address']) + "label": service, + "wireless": service_info.get_property("Type") == "wifi", + "connectionid": six.text_type( + service_info.get_property("Ethernet")["Interface"] + ), + "hwaddr": six.text_type(service_info.get_property("Ethernet")["Address"]), } - state = service_info.get_property('State') - if state == 'ready' or state == 'online': - data['up'] = True - data['ipv4'] = { - 'gateway': '0.0.0.0' - } - ipv4 = 'IPv4' - if service_info.get_property('IPv4')['Method'] == 'manual': - ipv4 += '.Configuration' + state = service_info.get_property("State") + if state == "ready" or state == "online": + data["up"] = True + data["ipv4"] = {"gateway": "0.0.0.0"} + ipv4 = "IPv4" + if service_info.get_property("IPv4")["Method"] == "manual": + ipv4 += ".Configuration" ipv4_info = service_info.get_property(ipv4) - for info in ['Method', 'Address', 'Netmask', 'Gateway']: + for info in ["Method", "Address", "Netmask", "Gateway"]: value = ipv4_info.get(info) if value is None: - log.warning('Unable to get IPv4 %s for service %s\n', info, service) + log.warning("Unable to get IPv4 %s for service %s\n", info, service) continue - if info == 'Method': - info = 'requestmode' - if value == 'dhcp': - value = 'dhcp_linklocal' - elif value in ('manual', 'fixed'): - value = 'static' - data['ipv4'][info.lower()] = six.text_type(value) + if info == "Method": + info = "requestmode" + if value == "dhcp": + value = "dhcp_linklocal" + elif value in ("manual", "fixed"): + value = "static" + data["ipv4"][info.lower()] = six.text_type(value) - ipv6_info = service_info.get_property('IPv6') - for info in ['Address', 'Prefix', 'Gateway']: + ipv6_info = service_info.get_property("IPv6") + for info in ["Address", "Prefix", "Gateway"]: value = ipv6_info.get(info) if value is None: - log.warning('Unable to get IPv6 %s for service %s\n', info, service) + log.warning("Unable to get IPv6 %s for service %s\n", info, service) continue - data['ipv6'][info.lower()] = [six.text_type(value)] + data["ipv6"][info.lower()] = [six.text_type(value)] nameservers = [] - for nameserver_prop in service_info.get_property('Nameservers'): + for nameserver_prop in service_info.get_property("Nameservers"): nameservers.append(six.text_type(nameserver_prop)) - data['ipv4']['dns'] = nameservers + data["ipv4"]["dns"] = nameservers else: - data['up'] = False + data["up"] = False - if 'ipv4' in data: - data['ipv4']['supportedrequestmodes'] = [ - 'static', - 'dhcp_linklocal' - ] + if "ipv4" in data: + data["ipv4"]["supportedrequestmodes"] = ["static", "dhcp_linklocal"] return data def _get_dns_info(): - ''' + """ return dns list - ''' + """ dns_list = [] try: - with salt.utils.files.fopen('/etc/resolv.conf', 'r+') as dns_info: + with salt.utils.files.fopen("/etc/resolv.conf", "r+") as dns_info: lines = dns_info.readlines() for line in lines: - if 'nameserver' in line: + if "nameserver" in line: dns = line.split()[1].strip() if dns not in dns_list: dns_list.append(dns) except IOError: - log.warning('Could not get domain\n') + log.warning("Could not get domain\n") return dns_list def _remove_quotes(value): - ''' + """ Remove leading and trailing double quotes if they exist. - ''' + """ # nirtcfg writes values with quotes - if len(value) > 1 and value[0] == value[-1] == '\"': + if len(value) > 1 and value[0] == value[-1] == '"': value = value[1:-1] return value -def _load_config(section, options, default_value='', filename=INI_FILE): - ''' +def _load_config(section, options, default_value="", filename=INI_FILE): + """ Get values for some options and a given section from a config file. :param section: Section Name @@ -264,208 +276,222 @@ def _load_config(section, options, default_value='', filename=INI_FILE): :param default_value: Default value if an option doesn't have a value. Default is empty string. :param filename: config file. Default is INI_FILE. :return: - ''' + """ results = {} if not options: return results - with salt.utils.files.fopen(filename, 'r') as config_file: + with salt.utils.files.fopen(filename, "r") as config_file: config_parser = configparser.RawConfigParser(dict_type=CaseInsensitiveDict) config_parser.readfp(config_file) for option in options: if six.PY2: - results[option] = _remove_quotes(config_parser.get(section, option)) \ - if config_parser.has_option(section, option) else default_value + results[option] = ( + _remove_quotes(config_parser.get(section, option)) + if config_parser.has_option(section, option) + else default_value + ) else: - results[option] = _remove_quotes(config_parser.get(section, option, fallback=default_value)) + results[option] = _remove_quotes( + config_parser.get(section, option, fallback=default_value) + ) return results def _get_request_mode_info(interface): - ''' + """ return requestmode for given interface - ''' - settings = _load_config(interface, ['linklocalenabled', 'dhcpenabled'], -1) - link_local_enabled = int(settings['linklocalenabled']) - dhcp_enabled = int(settings['dhcpenabled']) + """ + settings = _load_config(interface, ["linklocalenabled", "dhcpenabled"], -1) + link_local_enabled = int(settings["linklocalenabled"]) + dhcp_enabled = int(settings["dhcpenabled"]) if dhcp_enabled == 1: - return 'dhcp_linklocal' if link_local_enabled == 1 else 'dhcp_only' + return "dhcp_linklocal" if link_local_enabled == 1 else "dhcp_only" else: if link_local_enabled == 1: - return 'linklocal_only' + return "linklocal_only" if link_local_enabled == 0: - return 'static' + return "static" # some versions of nirtcfg don't set the dhcpenabled/linklocalenabled variables # when selecting "DHCP or Link Local" from MAX, so return it by default to avoid # having the requestmode "None" because none of the conditions above matched. - return 'dhcp_linklocal' + return "dhcp_linklocal" def _get_adapter_mode_info(interface): - ''' + """ return adaptermode for given interface - ''' - mode = _load_config(interface, ['mode'])['mode'].lower() - return mode if mode in ['disabled', 'ethercat'] else 'tcpip' + """ + mode = _load_config(interface, ["mode"])["mode"].lower() + return mode if mode in ["disabled", "ethercat"] else "tcpip" def _get_possible_adapter_modes(interface, blacklist): - ''' + """ Return possible adapter modes for a given interface using a blacklist. :param interface: interface name :param blacklist: given blacklist :return: list of possible adapter modes - ''' + """ adapter_modes = [] - protocols = _load_config('lvrt', ['AdditionalNetworkProtocols'])['AdditionalNetworkProtocols'].lower() - sys_interface_path = os.readlink('/sys/class/net/{0}'.format(interface)) - with salt.utils.files.fopen('/sys/class/net/{0}/uevent'.format(interface)) as uevent_file: + protocols = _load_config("lvrt", ["AdditionalNetworkProtocols"])[ + "AdditionalNetworkProtocols" + ].lower() + sys_interface_path = os.readlink("/sys/class/net/{0}".format(interface)) + with salt.utils.files.fopen( + "/sys/class/net/{0}/uevent".format(interface) + ) as uevent_file: uevent_lines = uevent_file.readlines() uevent_devtype = "" for line in uevent_lines: if line.startswith("DEVTYPE="): - uevent_devtype = line.split('=')[1].strip() + uevent_devtype = line.split("=")[1].strip() break for adapter_mode in blacklist: - if adapter_mode == '_': + if adapter_mode == "_": continue value = blacklist.get(adapter_mode, {}) - if value.get('additional_protocol') and adapter_mode not in protocols: + if value.get("additional_protocol") and adapter_mode not in protocols: continue - if interface not in value['name'] \ - and not any((blacklist['_'][iface_type] == 'sys' and iface_type in sys_interface_path) or - (blacklist['_'][iface_type] == 'uevent' and iface_type == uevent_devtype) - for iface_type in value['type']): + if interface not in value["name"] and not any( + (blacklist["_"][iface_type] == "sys" and iface_type in sys_interface_path) + or (blacklist["_"][iface_type] == "uevent" and iface_type == uevent_devtype) + for iface_type in value["type"] + ): adapter_modes += [adapter_mode] return adapter_modes def _get_static_info(interface): - ''' + """ Return information about an interface from config file. :param interface: interface label - ''' + """ data = { - 'connectionid': interface.name, - 'label': interface.name, - 'hwaddr': interface.hwaddr[:-1], - 'up': False, - 'ipv4': { - 'supportedrequestmodes': ['static', 'dhcp_linklocal'], - 'requestmode': 'static' + "connectionid": interface.name, + "label": interface.name, + "hwaddr": interface.hwaddr[:-1], + "up": False, + "ipv4": { + "supportedrequestmodes": ["static", "dhcp_linklocal"], + "requestmode": "static", }, - 'wireless': False + "wireless": False, } - hwaddr_section_number = ''.join(data['hwaddr'].split(':')) + hwaddr_section_number = "".join(data["hwaddr"].split(":")) if os.path.exists(INTERFACES_CONFIG): - information = _load_config(hwaddr_section_number, ['IPv4', 'Nameservers'], filename=INTERFACES_CONFIG) - if information['IPv4'] != '': - ipv4_information = information['IPv4'].split('/') - data['ipv4']['address'] = ipv4_information[0] - data['ipv4']['dns'] = information['Nameservers'].split(',') - data['ipv4']['netmask'] = ipv4_information[1] - data['ipv4']['gateway'] = ipv4_information[2] + information = _load_config( + hwaddr_section_number, ["IPv4", "Nameservers"], filename=INTERFACES_CONFIG + ) + if information["IPv4"] != "": + ipv4_information = information["IPv4"].split("/") + data["ipv4"]["address"] = ipv4_information[0] + data["ipv4"]["dns"] = information["Nameservers"].split(",") + data["ipv4"]["netmask"] = ipv4_information[1] + data["ipv4"]["gateway"] = ipv4_information[2] return data def _get_interface_info(interface): - ''' + """ return details about given interface - ''' + """ blacklist = { - 'tcpip': { - 'name': [], - 'type': [], - 'additional_protocol': False + "tcpip": {"name": [], "type": [], "additional_protocol": False}, + "disabled": { + "name": ["eth0"], + "type": ["gadget"], + "additional_protocol": False, }, - 'disabled': { - 'name': ['eth0'], - 'type': ['gadget'], - 'additional_protocol': False + "ethercat": { + "name": ["eth0"], + "type": ["gadget", "usb", "wlan"], + "additional_protocol": True, }, - 'ethercat': { - 'name': ['eth0'], - 'type': ['gadget', 'usb', 'wlan'], - 'additional_protocol': True - }, - '_': { - 'usb': 'sys', - 'gadget': 'uevent', - 'wlan': 'uevent' - } + "_": {"usb": "sys", "gadget": "uevent", "wlan": "uevent"}, } data = { - 'label': interface.name, - 'connectionid': interface.name, - 'supported_adapter_modes': _get_possible_adapter_modes(interface.name, blacklist), - 'adapter_mode': _get_adapter_mode_info(interface.name), - 'up': False, - 'ipv4': { - 'supportedrequestmodes': ['dhcp_linklocal', 'dhcp_only', 'linklocal_only', 'static'], - 'requestmode': _get_request_mode_info(interface.name) + "label": interface.name, + "connectionid": interface.name, + "supported_adapter_modes": _get_possible_adapter_modes( + interface.name, blacklist + ), + "adapter_mode": _get_adapter_mode_info(interface.name), + "up": False, + "ipv4": { + "supportedrequestmodes": [ + "dhcp_linklocal", + "dhcp_only", + "linklocal_only", + "static", + ], + "requestmode": _get_request_mode_info(interface.name), }, - 'hwaddr': interface.hwaddr[:-1] + "hwaddr": interface.hwaddr[:-1], } needed_settings = [] - if data['ipv4']['requestmode'] == 'static': - needed_settings += ['IP_Address', 'Subnet_Mask', 'Gateway', 'DNS_Address'] - if data['adapter_mode'] == 'ethercat': - needed_settings += ['MasterID'] + if data["ipv4"]["requestmode"] == "static": + needed_settings += ["IP_Address", "Subnet_Mask", "Gateway", "DNS_Address"] + if data["adapter_mode"] == "ethercat": + needed_settings += ["MasterID"] settings = _load_config(interface.name, needed_settings) if interface.flags & IFF_RUNNING != 0: - data['up'] = True - data['ipv4']['address'] = interface.sockaddrToStr(interface.addr) - data['ipv4']['netmask'] = interface.sockaddrToStr(interface.netmask) - data['ipv4']['gateway'] = '0.0.0.0' - data['ipv4']['dns'] = _get_dns_info() - elif data['ipv4']['requestmode'] == 'static': - data['ipv4']['address'] = settings['IP_Address'] - data['ipv4']['netmask'] = settings['Subnet_Mask'] - data['ipv4']['gateway'] = settings['Gateway'] - data['ipv4']['dns'] = [settings['DNS_Address']] + data["up"] = True + data["ipv4"]["address"] = interface.sockaddrToStr(interface.addr) + data["ipv4"]["netmask"] = interface.sockaddrToStr(interface.netmask) + data["ipv4"]["gateway"] = "0.0.0.0" + data["ipv4"]["dns"] = _get_dns_info() + elif data["ipv4"]["requestmode"] == "static": + data["ipv4"]["address"] = settings["IP_Address"] + data["ipv4"]["netmask"] = settings["Subnet_Mask"] + data["ipv4"]["gateway"] = settings["Gateway"] + data["ipv4"]["dns"] = [settings["DNS_Address"]] - with salt.utils.files.fopen('/proc/net/route', 'r') as route_file: - pattern = re.compile(r'^{interface}\t[0]{{8}}\t([0-9A-Z]{{8}})'.format(interface=interface.name), re.MULTILINE) + with salt.utils.files.fopen("/proc/net/route", "r") as route_file: + pattern = re.compile( + r"^{interface}\t[0]{{8}}\t([0-9A-Z]{{8}})".format(interface=interface.name), + re.MULTILINE, + ) match = pattern.search(route_file.read()) iface_gateway_hex = None if not match else match.group(1) if iface_gateway_hex is not None and len(iface_gateway_hex) == 8: - data['ipv4']['gateway'] = '.'.join([str(int(iface_gateway_hex[i:i+2], 16)) for i in range(6, -1, -2)]) - if data['adapter_mode'] == 'ethercat': - data['ethercat'] = { - 'masterid': settings['MasterID'] - } + data["ipv4"]["gateway"] = ".".join( + [str(int(iface_gateway_hex[i : i + 2], 16)) for i in range(6, -1, -2)] + ) + if data["adapter_mode"] == "ethercat": + data["ethercat"] = {"masterid": settings["MasterID"]} return data def _dict_to_string(dictionary): - ''' + """ converts a dictionary object into a list of strings - ''' - ret = '' + """ + ret = "" for key, val in sorted(dictionary.items()): if isinstance(val, dict): for line in _dict_to_string(val): - ret += six.text_type(key) + '-' + line + '\n' + ret += six.text_type(key) + "-" + line + "\n" elif isinstance(val, list): - text = ' '.join([six.text_type(item) for item in val]) - ret += six.text_type(key) + ': ' + text + '\n' + text = " ".join([six.text_type(item) for item in val]) + ret += six.text_type(key) + ": " + text + "\n" else: - ret += six.text_type(key) + ': ' + six.text_type(val) + '\n' + ret += six.text_type(key) + ": " + six.text_type(val) + "\n" return ret.splitlines() def _get_info(interface): - ''' + """ Return information about an interface even if it's not associated with a service. :param interface: interface label - ''' + """ service = _interface_to_service(interface.name) if service is not None: return _get_service_info(service) @@ -473,7 +499,7 @@ def _get_info(interface): def get_interfaces_details(): - ''' + """ Get details about all the interfaces on the minion :return: information about all interfaces omitting loopback @@ -484,15 +510,19 @@ def get_interfaces_details(): .. code-block:: bash salt '*' ip.get_interfaces_details - ''' - _interfaces = [interface for interface in pyiface.getIfaces() if interface.flags & IFF_LOOPBACK == 0] - if __grains__['lsb_distrib_id'] == 'nilrt': - return {'interfaces': list(map(_get_interface_info, _interfaces))} - return {'interfaces': list(map(_get_info, _interfaces))} + """ + _interfaces = [ + interface + for interface in pyiface.getIfaces() + if interface.flags & IFF_LOOPBACK == 0 + ] + if __grains__["lsb_distrib_id"] == "nilrt": + return {"interfaces": list(map(_get_interface_info, _interfaces))} + return {"interfaces": list(map(_get_info, _interfaces))} def _change_state(interface, new_state): - ''' + """ Enable or disable an interface Change adapter mode to TCP/IP. If previous adapter mode was EtherCAT, the target will need reboot. @@ -501,35 +531,45 @@ def _change_state(interface, new_state): :param new_state: up or down :return: True if the service was enabled, otherwise an exception will be thrown. :rtype: bool - ''' - if __grains__['lsb_distrib_id'] == 'nilrt': + """ + if __grains__["lsb_distrib_id"] == "nilrt": initial_mode = _get_adapter_mode_info(interface) - _save_config(interface, 'Mode', 'TCPIP') - if initial_mode == 'ethercat': - __salt__['system.set_reboot_required_witnessed']() + _save_config(interface, "Mode", "TCPIP") + if initial_mode == "ethercat": + __salt__["system.set_reboot_required_witnessed"]() else: - out = __salt__['cmd.run_all']('ip link set {0} {1}'.format(interface, new_state)) - if out['retcode'] != 0: - msg = 'Couldn\'t {0} interface {1}. Error: {2}'.format('enable' if new_state == 'up' else 'disable', - interface, out['stderr']) + out = __salt__["cmd.run_all"]( + "ip link set {0} {1}".format(interface, new_state) + ) + if out["retcode"] != 0: + msg = "Couldn't {0} interface {1}. Error: {2}".format( + "enable" if new_state == "up" else "disable", + interface, + out["stderr"], + ) raise salt.exceptions.CommandExecutionError(msg) return True service = _interface_to_service(interface) if not service: - raise salt.exceptions.CommandExecutionError('Invalid interface name: {0}'.format(interface)) + raise salt.exceptions.CommandExecutionError( + "Invalid interface name: {0}".format(interface) + ) if not _connected(service): service = pyconnman.ConnService(os.path.join(SERVICE_PATH, service)) try: - state = service.connect() if new_state == 'up' else service.disconnect() + state = service.connect() if new_state == "up" else service.disconnect() return state is None except Exception: # pylint: disable=broad-except - raise salt.exceptions.CommandExecutionError('Couldn\'t {0} service: {1}\n' - .format('enable' if new_state == 'up' else 'disable', service)) + raise salt.exceptions.CommandExecutionError( + "Couldn't {0} service: {1}\n".format( + "enable" if new_state == "up" else "disable", service + ) + ) return True def up(interface, iface_type=None): # pylint: disable=invalid-name,unused-argument - ''' + """ Enable the specified interface Change adapter mode to TCP/IP. If previous adapter mode was EtherCAT, the target will need reboot. @@ -543,12 +583,12 @@ def up(interface, iface_type=None): # pylint: disable=invalid-name,unused-argum .. code-block:: bash salt '*' ip.up interface-label - ''' - return _change_state(interface, 'up') + """ + return _change_state(interface, "up") def enable(interface): - ''' + """ Enable the specified interface Change adapter mode to TCP/IP. If previous adapter mode was EtherCAT, the target will need reboot. @@ -562,12 +602,12 @@ def enable(interface): .. code-block:: bash salt '*' ip.enable interface-label - ''' + """ return up(interface) def down(interface, iface_type=None): # pylint: disable=unused-argument - ''' + """ Disable the specified interface Change adapter mode to Disabled. If previous adapter mode was EtherCAT, the target will need reboot. @@ -581,12 +621,12 @@ def down(interface, iface_type=None): # pylint: disable=unused-argument .. code-block:: bash salt '*' ip.down interface-label - ''' - return _change_state(interface, 'down') + """ + return _change_state(interface, "down") def disable(interface): - ''' + """ Disable the specified interface Change adapter mode to Disabled. If previous adapter mode was EtherCAT, the target will need reboot. @@ -600,23 +640,23 @@ def disable(interface): .. code-block:: bash salt '*' ip.disable interface-label - ''' + """ return down(interface) def _save_config(section, token, value): - ''' + """ Helper function to persist a configuration in the ini file - ''' + """ cmd = NIRTCFG_PATH - cmd += ' --set section={0},token=\'{1}\',value=\'{2}\''.format(section, token, value) - if __salt__['cmd.run_all'](cmd)['retcode'] != 0: - exc_msg = 'Error: could not set {} to {} for {}\n'.format(token, value, section) + cmd += " --set section={0},token='{1}',value='{2}'".format(section, token, value) + if __salt__["cmd.run_all"](cmd)["retcode"] != 0: + exc_msg = "Error: could not set {} to {} for {}\n".format(token, value, section) raise salt.exceptions.CommandExecutionError(exc_msg) def set_ethercat(interface, master_id): - ''' + """ Configure specified adapter to use EtherCAT adapter mode. If successful, the target will need reboot if it doesn't already use EtherCAT adapter mode, otherwise will return true. @@ -629,27 +669,27 @@ def set_ethercat(interface, master_id): .. code-block:: bash salt '*' ip.set_ethercat interface-label master-id - ''' - if __grains__['lsb_distrib_id'] == 'nilrt': + """ + if __grains__["lsb_distrib_id"] == "nilrt": initial_mode = _get_adapter_mode_info(interface) - _save_config(interface, 'Mode', NIRTCFG_ETHERCAT) - _save_config(interface, 'MasterID', master_id) - if initial_mode != 'ethercat': - __salt__['system.set_reboot_required_witnessed']() + _save_config(interface, "Mode", NIRTCFG_ETHERCAT) + _save_config(interface, "MasterID", master_id) + if initial_mode != "ethercat": + __salt__["system.set_reboot_required_witnessed"]() return True - raise salt.exceptions.CommandExecutionError('EtherCAT is not supported') + raise salt.exceptions.CommandExecutionError("EtherCAT is not supported") def _restart(interface): - ''' + """ Disable and enable an interface - ''' + """ disable(interface) enable(interface) def set_dhcp_linklocal_all(interface): - ''' + """ Configure specified adapter to use DHCP with linklocal fallback Change adapter mode to TCP/IP. If previous adapter mode was EtherCAT, the target will need reboot. @@ -663,37 +703,43 @@ def set_dhcp_linklocal_all(interface): .. code-block:: bash salt '*' ip.set_dhcp_linklocal_all interface-label - ''' - if __grains__['lsb_distrib_id'] == 'nilrt': + """ + if __grains__["lsb_distrib_id"] == "nilrt": initial_mode = _get_adapter_mode_info(interface) - _save_config(interface, 'Mode', 'TCPIP') - _save_config(interface, 'dhcpenabled', '1') - _save_config(interface, 'linklocalenabled', '1') - if initial_mode == 'ethercat': - __salt__['system.set_reboot_required_witnessed']() + _save_config(interface, "Mode", "TCPIP") + _save_config(interface, "dhcpenabled", "1") + _save_config(interface, "linklocalenabled", "1") + if initial_mode == "ethercat": + __salt__["system.set_reboot_required_witnessed"]() else: _restart(interface) return True service = _interface_to_service(interface) if not service: - raise salt.exceptions.CommandExecutionError('Invalid interface name: {0}'.format(interface)) + raise salt.exceptions.CommandExecutionError( + "Invalid interface name: {0}".format(interface) + ) service = pyconnman.ConnService(os.path.join(SERVICE_PATH, service)) - ipv4 = service.get_property('IPv4.Configuration') - ipv4['Method'] = dbus.String('dhcp', variant_level=1) - ipv4['Address'] = dbus.String('', variant_level=1) - ipv4['Netmask'] = dbus.String('', variant_level=1) - ipv4['Gateway'] = dbus.String('', variant_level=1) + ipv4 = service.get_property("IPv4.Configuration") + ipv4["Method"] = dbus.String("dhcp", variant_level=1) + ipv4["Address"] = dbus.String("", variant_level=1) + ipv4["Netmask"] = dbus.String("", variant_level=1) + ipv4["Gateway"] = dbus.String("", variant_level=1) try: - service.set_property('IPv4.Configuration', ipv4) - service.set_property('Nameservers.Configuration', ['']) # reset nameservers list + service.set_property("IPv4.Configuration", ipv4) + service.set_property( + "Nameservers.Configuration", [""] + ) # reset nameservers list except Exception as exc: # pylint: disable=broad-except - exc_msg = 'Couldn\'t set dhcp linklocal for service: {0}\nError: {1}\n'.format(service, exc) + exc_msg = "Couldn't set dhcp linklocal for service: {0}\nError: {1}\n".format( + service, exc + ) raise salt.exceptions.CommandExecutionError(exc_msg) return True def set_dhcp_only_all(interface): - ''' + """ Configure specified adapter to use DHCP only Change adapter mode to TCP/IP. If previous adapter mode was EtherCAT, the target will need reboot. @@ -707,22 +753,22 @@ def set_dhcp_only_all(interface): .. code-block:: bash salt '*' ip.dhcp_only_all interface-label - ''' - if not __grains__['lsb_distrib_id'] == 'nilrt': - raise salt.exceptions.CommandExecutionError('Not supported in this version') + """ + if not __grains__["lsb_distrib_id"] == "nilrt": + raise salt.exceptions.CommandExecutionError("Not supported in this version") initial_mode = _get_adapter_mode_info(interface) - _save_config(interface, 'Mode', 'TCPIP') - _save_config(interface, 'dhcpenabled', '1') - _save_config(interface, 'linklocalenabled', '0') - if initial_mode == 'ethercat': - __salt__['system.set_reboot_required_witnessed']() + _save_config(interface, "Mode", "TCPIP") + _save_config(interface, "dhcpenabled", "1") + _save_config(interface, "linklocalenabled", "0") + if initial_mode == "ethercat": + __salt__["system.set_reboot_required_witnessed"]() else: _restart(interface) return True def set_linklocal_only_all(interface): - ''' + """ Configure specified adapter to use linklocal only Change adapter mode to TCP/IP. If previous adapter mode was EtherCAT, the target will need reboot. @@ -736,22 +782,22 @@ def set_linklocal_only_all(interface): .. code-block:: bash salt '*' ip.linklocal_only_all interface-label - ''' - if not __grains__['lsb_distrib_id'] == 'nilrt': - raise salt.exceptions.CommandExecutionError('Not supported in this version') + """ + if not __grains__["lsb_distrib_id"] == "nilrt": + raise salt.exceptions.CommandExecutionError("Not supported in this version") initial_mode = _get_adapter_mode_info(interface) - _save_config(interface, 'Mode', 'TCPIP') - _save_config(interface, 'dhcpenabled', '0') - _save_config(interface, 'linklocalenabled', '1') - if initial_mode == 'ethercat': - __salt__['system.set_reboot_required_witnessed']() + _save_config(interface, "Mode", "TCPIP") + _save_config(interface, "dhcpenabled", "0") + _save_config(interface, "linklocalenabled", "1") + if initial_mode == "ethercat": + __salt__["system.set_reboot_required_witnessed"]() else: _restart(interface) return True def _configure_static_interface(interface, **settings): - ''' + """ Configure an interface that is not detected as a service by Connman (i.e. link is down) :param interface: interface label @@ -763,37 +809,42 @@ def _configure_static_interface(interface, **settings): - name :return: True if settings were applied successfully. :rtype: bool - ''' + """ interface = pyiface.Interface(name=interface) parser = configparser.ConfigParser() if os.path.exists(INTERFACES_CONFIG): try: - with salt.utils.files.fopen(INTERFACES_CONFIG, 'r') as config_file: + with salt.utils.files.fopen(INTERFACES_CONFIG, "r") as config_file: parser.readfp(config_file) except configparser.MissingSectionHeaderError: pass hwaddr = interface.hwaddr[:-1] - hwaddr_section_number = ''.join(hwaddr.split(':')) - if not parser.has_section('interface_{0}'.format(hwaddr_section_number)): - parser.add_section('interface_{0}'.format(hwaddr_section_number)) - ip_address = settings.get('ip', '0.0.0.0') - netmask = settings.get('netmask', '0.0.0.0') - gateway = settings.get('gateway', '0.0.0.0') - dns_servers = settings.get('dns', '') - name = settings.get('name', 'ethernet_cable_{0}'.format(hwaddr_section_number)) - parser.set('interface_{0}'.format(hwaddr_section_number), 'IPv4', '{0}/{1}/{2}'. - format(ip_address, netmask, gateway)) - parser.set('interface_{0}'.format(hwaddr_section_number), 'Nameservers', dns_servers) - parser.set('interface_{0}'.format(hwaddr_section_number), 'Name', name) - parser.set('interface_{0}'.format(hwaddr_section_number), 'MAC', hwaddr) - parser.set('interface_{0}'.format(hwaddr_section_number), 'Type', 'ethernet') - with salt.utils.files.fopen(INTERFACES_CONFIG, 'w') as config_file: + hwaddr_section_number = "".join(hwaddr.split(":")) + if not parser.has_section("interface_{0}".format(hwaddr_section_number)): + parser.add_section("interface_{0}".format(hwaddr_section_number)) + ip_address = settings.get("ip", "0.0.0.0") + netmask = settings.get("netmask", "0.0.0.0") + gateway = settings.get("gateway", "0.0.0.0") + dns_servers = settings.get("dns", "") + name = settings.get("name", "ethernet_cable_{0}".format(hwaddr_section_number)) + parser.set( + "interface_{0}".format(hwaddr_section_number), + "IPv4", + "{0}/{1}/{2}".format(ip_address, netmask, gateway), + ) + parser.set( + "interface_{0}".format(hwaddr_section_number), "Nameservers", dns_servers + ) + parser.set("interface_{0}".format(hwaddr_section_number), "Name", name) + parser.set("interface_{0}".format(hwaddr_section_number), "MAC", hwaddr) + parser.set("interface_{0}".format(hwaddr_section_number), "Type", "ethernet") + with salt.utils.files.fopen(INTERFACES_CONFIG, "w") as config_file: parser.write(config_file) return True def set_static_all(interface, address, netmask, gateway, nameservers): - ''' + """ Configure specified adapter to use ipv4 manual settings Change adapter mode to TCP/IP. If previous adapter mode was EtherCAT, the target will need reboot. @@ -811,7 +862,7 @@ def set_static_all(interface, address, netmask, gateway, nameservers): .. code-block:: bash salt '*' ip.set_static_all interface-label address netmask gateway nameservers - ''' + """ validate, msg = _validate_ipv4([address, netmask, gateway]) if not validate: raise salt.exceptions.CommandExecutionError(msg) @@ -819,45 +870,59 @@ def set_static_all(interface, address, netmask, gateway, nameservers): if not validate: raise salt.exceptions.CommandExecutionError(msg) if not isinstance(nameservers, list): - nameservers = nameservers.split(' ') - if __grains__['lsb_distrib_id'] == 'nilrt': + nameservers = nameservers.split(" ") + if __grains__["lsb_distrib_id"] == "nilrt": initial_mode = _get_adapter_mode_info(interface) - _save_config(interface, 'Mode', 'TCPIP') - _save_config(interface, 'dhcpenabled', '0') - _save_config(interface, 'linklocalenabled', '0') - _save_config(interface, 'IP_Address', address) - _save_config(interface, 'Subnet_Mask', netmask) - _save_config(interface, 'Gateway', gateway) + _save_config(interface, "Mode", "TCPIP") + _save_config(interface, "dhcpenabled", "0") + _save_config(interface, "linklocalenabled", "0") + _save_config(interface, "IP_Address", address) + _save_config(interface, "Subnet_Mask", netmask) + _save_config(interface, "Gateway", gateway) if nameservers: - _save_config(interface, 'DNS_Address', nameservers[0]) - if initial_mode == 'ethercat': - __salt__['system.set_reboot_required_witnessed']() + _save_config(interface, "DNS_Address", nameservers[0]) + if initial_mode == "ethercat": + __salt__["system.set_reboot_required_witnessed"]() else: _restart(interface) return True service = _interface_to_service(interface) if not service: if interface in pyiface.getIfaces(): - return _configure_static_interface(interface, **{'ip': address, 'dns': ','.join(nameservers), - 'netmask': netmask, 'gateway': gateway}) - raise salt.exceptions.CommandExecutionError('Invalid interface name: {0}'.format(interface)) + return _configure_static_interface( + interface, + **{ + "ip": address, + "dns": ",".join(nameservers), + "netmask": netmask, + "gateway": gateway, + } + ) + raise salt.exceptions.CommandExecutionError( + "Invalid interface name: {0}".format(interface) + ) service = pyconnman.ConnService(os.path.join(SERVICE_PATH, service)) - ipv4 = service.get_property('IPv4.Configuration') - ipv4['Method'] = dbus.String('manual', variant_level=1) - ipv4['Address'] = dbus.String('{0}'.format(address), variant_level=1) - ipv4['Netmask'] = dbus.String('{0}'.format(netmask), variant_level=1) - ipv4['Gateway'] = dbus.String('{0}'.format(gateway), variant_level=1) + ipv4 = service.get_property("IPv4.Configuration") + ipv4["Method"] = dbus.String("manual", variant_level=1) + ipv4["Address"] = dbus.String("{0}".format(address), variant_level=1) + ipv4["Netmask"] = dbus.String("{0}".format(netmask), variant_level=1) + ipv4["Gateway"] = dbus.String("{0}".format(gateway), variant_level=1) try: - service.set_property('IPv4.Configuration', ipv4) - service.set_property('Nameservers.Configuration', [dbus.String('{0}'.format(d)) for d in nameservers]) + service.set_property("IPv4.Configuration", ipv4) + service.set_property( + "Nameservers.Configuration", + [dbus.String("{0}".format(d)) for d in nameservers], + ) except Exception as exc: # pylint: disable=broad-except - exc_msg = 'Couldn\'t set manual settings for service: {0}\nError: {1}\n'.format(service, exc) + exc_msg = "Couldn't set manual settings for service: {0}\nError: {1}\n".format( + service, exc + ) raise salt.exceptions.CommandExecutionError(exc_msg) return True def get_interface(iface): - ''' + """ Returns details about given interface. CLI Example: @@ -865,16 +930,16 @@ def get_interface(iface): .. code-block:: bash salt '*' ip.get_interface eth0 - ''' + """ _interfaces = get_interfaces_details() - for _interface in _interfaces['interfaces']: - if _interface['connectionid'] == iface: + for _interface in _interfaces["interfaces"]: + if _interface["connectionid"] == iface: return _dict_to_string(_interface) return None def build_interface(iface, iface_type, enabled, **settings): - ''' + """ Build an interface script for a network interface. CLI Example: @@ -882,24 +947,28 @@ def build_interface(iface, iface_type, enabled, **settings): .. code-block:: bash salt '*' ip.build_interface eth0 eth - ''' - if __grains__['lsb_distrib_id'] == 'nilrt': - raise salt.exceptions.CommandExecutionError('Not supported in this version.') - if iface_type != 'eth': - raise salt.exceptions.CommandExecutionError('Interface type not supported: {0}:'.format(iface_type)) + """ + if __grains__["lsb_distrib_id"] == "nilrt": + raise salt.exceptions.CommandExecutionError("Not supported in this version.") + if iface_type != "eth": + raise salt.exceptions.CommandExecutionError( + "Interface type not supported: {0}:".format(iface_type) + ) - if 'proto' not in settings or settings['proto'] == 'dhcp': # default protocol type used is dhcp + if ( + "proto" not in settings or settings["proto"] == "dhcp" + ): # default protocol type used is dhcp set_dhcp_linklocal_all(iface) - elif settings['proto'] != 'static': - exc_msg = 'Protocol type: {0} is not supported'.format(settings['proto']) + elif settings["proto"] != "static": + exc_msg = "Protocol type: {0} is not supported".format(settings["proto"]) raise salt.exceptions.CommandExecutionError(exc_msg) else: - address = settings['ipaddr'] - netmask = settings['netmask'] - gateway = settings['gateway'] + address = settings["ipaddr"] + netmask = settings["netmask"] + gateway = settings["gateway"] dns = [] for key, val in six.iteritems(settings): - if 'dns' in key or 'domain' in key: + if "dns" in key or "domain" in key: dns += val set_static_all(iface, address, netmask, gateway, dns) @@ -910,7 +979,7 @@ def build_interface(iface, iface_type, enabled, **settings): def build_network_settings(**settings): - ''' + """ Build the global network script. CLI Example: @@ -918,29 +987,29 @@ def build_network_settings(**settings): .. code-block:: bash salt '*' ip.build_network_settings - ''' - if __grains__['lsb_distrib_id'] == 'nilrt': - raise salt.exceptions.CommandExecutionError('Not supported in this version.') + """ + if __grains__["lsb_distrib_id"] == "nilrt": + raise salt.exceptions.CommandExecutionError("Not supported in this version.") changes = [] - if 'networking' in settings: - if settings['networking'] in _CONFIG_TRUE: - __salt__['service.enable']('connman') + if "networking" in settings: + if settings["networking"] in _CONFIG_TRUE: + __salt__["service.enable"]("connman") else: - __salt__['service.disable']('connman') + __salt__["service.disable"]("connman") - if 'hostname' in settings: - new_hostname = settings['hostname'].split('.', 1)[0] - settings['hostname'] = new_hostname - old_hostname = __salt__['network.get_hostname'] + if "hostname" in settings: + new_hostname = settings["hostname"].split(".", 1)[0] + settings["hostname"] = new_hostname + old_hostname = __salt__["network.get_hostname"] if new_hostname != old_hostname: - __salt__['network.mod_hostname'](new_hostname) - changes.append('hostname={0}'.format(new_hostname)) + __salt__["network.mod_hostname"](new_hostname) + changes.append("hostname={0}".format(new_hostname)) return changes def get_network_settings(): - ''' + """ Return the contents of the global network script. CLI Example: @@ -948,19 +1017,19 @@ def get_network_settings(): .. code-block:: bash salt '*' ip.get_network_settings - ''' - if __grains__['lsb_distrib_id'] == 'nilrt': - raise salt.exceptions.CommandExecutionError('Not supported in this version.') + """ + if __grains__["lsb_distrib_id"] == "nilrt": + raise salt.exceptions.CommandExecutionError("Not supported in this version.") settings = [] - networking = 'no' if _get_state() == 'offline' else 'yes' - settings.append('networking={0}'.format(networking)) - hostname = __salt__['network.get_hostname'] - settings.append('hostname={0}'.format(hostname)) + networking = "no" if _get_state() == "offline" else "yes" + settings.append("networking={0}".format(networking)) + hostname = __salt__["network.get_hostname"] + settings.append("hostname={0}".format(hostname)) return settings def apply_network_settings(**settings): - ''' + """ Apply global network configuration. CLI Example: @@ -968,36 +1037,36 @@ def apply_network_settings(**settings): .. code-block:: bash salt '*' ip.apply_network_settings - ''' - if __grains__['lsb_distrib_id'] == 'nilrt': - raise salt.exceptions.CommandExecutionError('Not supported in this version.') - if 'require_reboot' not in settings: - settings['require_reboot'] = False + """ + if __grains__["lsb_distrib_id"] == "nilrt": + raise salt.exceptions.CommandExecutionError("Not supported in this version.") + if "require_reboot" not in settings: + settings["require_reboot"] = False - if 'apply_hostname' not in settings: - settings['apply_hostname'] = False + if "apply_hostname" not in settings: + settings["apply_hostname"] = False hostname_res = True - if settings['apply_hostname'] in _CONFIG_TRUE: - if 'hostname' in settings: - hostname_res = __salt__['network.mod_hostname'](settings['hostname']) + if settings["apply_hostname"] in _CONFIG_TRUE: + if "hostname" in settings: + hostname_res = __salt__["network.mod_hostname"](settings["hostname"]) else: log.warning( - 'The network state sls is trying to apply hostname ' - 'changes but no hostname is defined.' + "The network state sls is trying to apply hostname " + "changes but no hostname is defined." ) hostname_res = False res = True - if settings['require_reboot'] in _CONFIG_TRUE: + if settings["require_reboot"] in _CONFIG_TRUE: log.warning( - 'The network state sls is requiring a reboot of the system to ' - 'properly apply network configuration.' + "The network state sls is requiring a reboot of the system to " + "properly apply network configuration." ) res = True else: - stop = __salt__['service.stop']('connman') + stop = __salt__["service.stop"]("connman") time.sleep(2) - res = stop and __salt__['service.start']('connman') + res = stop and __salt__["service.start"]("connman") return hostname_res and res diff --git a/salt/modules/nix.py b/salt/modules/nix.py index 39dd8f63641..da3e0eaf39d 100644 --- a/salt/modules/nix.py +++ b/salt/modules/nix.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Work with Nix packages ====================== @@ -17,101 +17,110 @@ For more information on nix, see the `nix documentation`_. .. _`nix documentation`: https://nixos.org/nix/manual/ .. _`nix-daemon`: https://nixos.org/nix/manual/#ssec-multi-user -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import logging import itertools +import logging import os import salt.utils.itertools import salt.utils.path from salt.ext.six.moves import zip - logger = logging.getLogger(__name__) def __virtual__(): - ''' + """ This only works if we have access to nix-env - ''' - nixhome = os.path.join(os.path.expanduser('~{0}'.format(__opts__['user'])), '.nix-profile/bin/') - if salt.utils.path.which(os.path.join(nixhome, 'nix-env')) and salt.utils.path.which(os.path.join(nixhome, 'nix-collect-garbage')): + """ + nixhome = os.path.join( + os.path.expanduser("~{0}".format(__opts__["user"])), ".nix-profile/bin/" + ) + if salt.utils.path.which( + os.path.join(nixhome, "nix-env") + ) and salt.utils.path.which(os.path.join(nixhome, "nix-collect-garbage")): return True else: - return (False, "The `nix` binaries required cannot be found or are not installed. (`nix-store` and `nix-env`)") + return ( + False, + "The `nix` binaries required cannot be found or are not installed. (`nix-store` and `nix-env`)", + ) def _run(cmd): - ''' + """ Just a convenience function for ``__salt__['cmd.run_all'](cmd)`` - ''' - return __salt__['cmd.run_all'](cmd, env={'HOME': os.path.expanduser('~{0}'.format(__opts__['user']))}) + """ + return __salt__["cmd.run_all"]( + cmd, env={"HOME": os.path.expanduser("~{0}".format(__opts__["user"]))} + ) def _nix_env(): - ''' + """ nix-env with quiet option. By default, nix is extremely verbose and prints the build log of every package to stderr. This tells nix to only show changes. - ''' - nixhome = os.path.join(os.path.expanduser('~{0}'.format(__opts__['user'])), '.nix-profile/bin/') - return [os.path.join(nixhome, 'nix-env')] + """ + nixhome = os.path.join( + os.path.expanduser("~{0}".format(__opts__["user"])), ".nix-profile/bin/" + ) + return [os.path.join(nixhome, "nix-env")] def _nix_collect_garbage(): - ''' + """ Make sure we get the right nix-store, too. - ''' - nixhome = os.path.join(os.path.expanduser('~{0}'.format(__opts__['user'])), '.nix-profile/bin/') - return [os.path.join(nixhome, 'nix-collect-garbage')] + """ + nixhome = os.path.join( + os.path.expanduser("~{0}".format(__opts__["user"])), ".nix-profile/bin/" + ) + return [os.path.join(nixhome, "nix-collect-garbage")] def _quietnix(): - ''' + """ nix-env with quiet option. By default, nix is extremely verbose and prints the build log of every package to stderr. This tells nix to only show changes. - ''' + """ p = _nix_env() - p.append('--no-build-output') + p.append("--no-build-output") return p def _zip_flatten(x, ys): - ''' + """ intersperse x into ys, with an extra element at the beginning. - ''' - return itertools.chain.from_iterable( - zip(itertools.repeat(x), ys)) + """ + return itertools.chain.from_iterable(zip(itertools.repeat(x), ys)) -def _output_format(out, - operation): - ''' +def _output_format(out, operation): + """ gets a list of all the packages that were affected by ``operation``, splits it up (there can be multiple packages on a line), and then flattens that list. We make it to a list for easier parsing. - ''' - return [s.split()[1:] for s in out - if s.startswith(operation)] + """ + return [s.split()[1:] for s in out if s.startswith(operation)] def _format_upgrade(s): - ''' + """ split the ``upgrade`` responses on ``' to '`` - ''' - return s.split(' to ') + """ + return s.split(" to ") def _strip_quotes(s): - ''' + """ nix likes to quote itself in a backtick and a single quote. This just strips those. - ''' + """ return s.strip("'`") def upgrade(*pkgs): - ''' + """ Runs an update operation on the specified packages, or all packages if none is specified. :type pkgs: list(str) @@ -125,23 +134,24 @@ def upgrade(*pkgs): salt '*' nix.update salt '*' nix.update pkgs=one,two - ''' + """ cmd = _quietnix() - cmd.append('--upgrade') + cmd.append("--upgrade") cmd.extend(pkgs) out = _run(cmd) - upgrades = [_format_upgrade(s.split(maxsplit=1)[1]) - for s in out['stderr'].splitlines() - if s.startswith('upgrading')] + upgrades = [ + _format_upgrade(s.split(maxsplit=1)[1]) + for s in out["stderr"].splitlines() + if s.startswith("upgrading") + ] - return [[_strip_quotes(s_) for s_ in s] - for s in upgrades] + return [[_strip_quotes(s_) for s_ in s] for s in upgrades] def install(*pkgs, **kwargs): - ''' + """ Installs a single or multiple packages via nix :type pkgs: list(str) @@ -158,34 +168,38 @@ def install(*pkgs, **kwargs): salt '*' nix.install package [package2 ...] salt '*' nix.install attributes=True attr.name [attr.name2 ...] - ''' + """ - attributes = kwargs.get('attributes', False) + attributes = kwargs.get("attributes", False) if not pkgs: return "Plese specify a package or packages to upgrade" cmd = _quietnix() - cmd.append('--install') + cmd.append("--install") - if kwargs.get('attributes', False): - cmd.extend(_zip_flatten('--attr', pkgs)) + if kwargs.get("attributes", False): + cmd.extend(_zip_flatten("--attr", pkgs)) else: cmd.extend(pkgs) out = _run(cmd) - installs = list(itertools.chain.from_iterable( - [s.split()[1:] for s in out['stderr'].splitlines() - if s.startswith('installing')] - )) + installs = list( + itertools.chain.from_iterable( + [ + s.split()[1:] + for s in out["stderr"].splitlines() + if s.startswith("installing") + ] + ) + ) return [_strip_quotes(s) for s in installs] -def list_pkgs(installed=True, - attributes=True): - ''' +def list_pkgs(installed=True, attributes=True): + """ Lists installed packages. Due to how nix works, it defaults to just doing a ``nix-env -q``. :param bool installed: @@ -203,17 +217,17 @@ def list_pkgs(installed=True, salt '*' nix.list_pkgs salt '*' nix.list_pkgs installed=False - ''' + """ # We don't use -Q here, as it obfuscates the attribute names on full package listings. cmd = _nix_env() - cmd.append('--query') + cmd.append("--query") if installed: # explicitly add this option for consistency, it's normally the default - cmd.append('--installed') + cmd.append("--installed") if not installed: - cmd.append('--available') + cmd.append("--available") # We only show attributes if we're not doing an `installed` run. # The output of `nix-env -qaP` and `nix-env -qP` are vastly different: # `nix-env -qaP` returns a list such as 'attr.path name-version' @@ -221,15 +235,15 @@ def list_pkgs(installed=True, # Install order is useful to unambiguously select packages on a single # machine, but on more than one it can be a bad thing to specify. if attributes: - cmd.append('--attr-path') + cmd.append("--attr-path") out = _run(cmd) - return [s.split() for s in salt.utils.itertools.split(out['stdout'], '\n')] + return [s.split() for s in salt.utils.itertools.split(out["stdout"], "\n")] def uninstall(*pkgs): - ''' + """ Erases a package from the current nix profile. Nix uninstalls work differently than other package managers, and the symlinks in the profile are removed, while the actual package remains. There is also a ``nix.purge`` function, to clear the package cache of unused packages. @@ -244,23 +258,25 @@ def uninstall(*pkgs): .. code-block:: bash salt '*' nix.uninstall pkg1 [pkg2 ...] - ''' + """ cmd = _quietnix() - cmd.append('--uninstall') + cmd.append("--uninstall") cmd.extend(pkgs) out = _run(cmd) - fmtout = out['stderr'].splitlines(), 'uninstalling' + fmtout = out["stderr"].splitlines(), "uninstalling" - return [_strip_quotes(s.split()[1]) - for s in out['stderr'].splitlines() - if s.startswith('uninstalling')] + return [ + _strip_quotes(s.split()[1]) + for s in out["stderr"].splitlines() + if s.startswith("uninstalling") + ] def collect_garbage(): - ''' + """ Completely removed all currently 'uninstalled' packages in the nix store. Tells the user how many store paths were removed and how much space was freed. @@ -274,10 +290,10 @@ def collect_garbage(): .. code-block:: bash salt '*' nix.collect_garbage - ''' + """ cmd = _nix_collect_garbage() - cmd.append('--delete-old') + cmd.append("--delete-old") out = _run(cmd) - return out['stdout'].splitlines() + return out["stdout"].splitlines() diff --git a/salt/modules/nova.py b/salt/modules/nova.py index 10f80d61d85..260f2d8337b 100644 --- a/salt/modules/nova.py +++ b/salt/modules/nova.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for handling OpenStack Nova calls :depends: - novaclient Python module @@ -67,36 +67,34 @@ Module for handling OpenStack Nova calls path to a bundle or CA certs to check against, or None to allow keystoneauth to search for the certificates on its own. (defaults to True) -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging - # Get logging started log = logging.getLogger(__name__) # Function alias to not shadow built-ins -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} try: import salt.utils.openstack.nova as suon + HAS_NOVA = True except NameError as exc: HAS_NOVA = False # Define the module's virtual name -__virtualname__ = 'nova' +__virtualname__ = "nova" def __virtual__(): - ''' + """ Only load this module if nova is installed on this minion. - ''' + """ return HAS_NOVA @@ -104,62 +102,62 @@ __opts__ = {} def _auth(profile=None): - ''' + """ Set up nova credentials - ''' + """ if profile: - credentials = __salt__['config.option'](profile) - user = credentials['keystone.user'] - password = credentials['keystone.password'] - tenant = credentials['keystone.tenant'] - auth_url = credentials['keystone.auth_url'] - region_name = credentials.get('keystone.region_name', None) - api_key = credentials.get('keystone.api_key', None) - os_auth_system = credentials.get('keystone.os_auth_system', None) - use_keystoneauth = credentials.get('keystone.use_keystoneauth', False) - verify = credentials.get('keystone.verify', None) + credentials = __salt__["config.option"](profile) + user = credentials["keystone.user"] + password = credentials["keystone.password"] + tenant = credentials["keystone.tenant"] + auth_url = credentials["keystone.auth_url"] + region_name = credentials.get("keystone.region_name", None) + api_key = credentials.get("keystone.api_key", None) + os_auth_system = credentials.get("keystone.os_auth_system", None) + use_keystoneauth = credentials.get("keystone.use_keystoneauth", False) + verify = credentials.get("keystone.verify", None) else: - user = __salt__['config.option']('keystone.user') - password = __salt__['config.option']('keystone.password') - tenant = __salt__['config.option']('keystone.tenant') - auth_url = __salt__['config.option']('keystone.auth_url') - region_name = __salt__['config.option']('keystone.region_name') - api_key = __salt__['config.option']('keystone.api_key') - os_auth_system = __salt__['config.option']('keystone.os_auth_system') - use_keystoneauth = __salt__['config.option']('keystone.use_keystoneauth') - verify = __salt__['config.option']('keystone.verify') + user = __salt__["config.option"]("keystone.user") + password = __salt__["config.option"]("keystone.password") + tenant = __salt__["config.option"]("keystone.tenant") + auth_url = __salt__["config.option"]("keystone.auth_url") + region_name = __salt__["config.option"]("keystone.region_name") + api_key = __salt__["config.option"]("keystone.api_key") + os_auth_system = __salt__["config.option"]("keystone.os_auth_system") + use_keystoneauth = __salt__["config.option"]("keystone.use_keystoneauth") + verify = __salt__["config.option"]("keystone.verify") if use_keystoneauth is True: - project_domain_name = credentials['keystone.project_domain_name'] - user_domain_name = credentials['keystone.user_domain_name'] + project_domain_name = credentials["keystone.project_domain_name"] + user_domain_name = credentials["keystone.user_domain_name"] kwargs = { - 'username': user, - 'password': password, - 'project_id': tenant, - 'auth_url': auth_url, - 'region_name': region_name, - 'use_keystoneauth': use_keystoneauth, - 'verify': verify, - 'project_domain_name': project_domain_name, - 'user_domain_name': user_domain_name + "username": user, + "password": password, + "project_id": tenant, + "auth_url": auth_url, + "region_name": region_name, + "use_keystoneauth": use_keystoneauth, + "verify": verify, + "project_domain_name": project_domain_name, + "user_domain_name": user_domain_name, } else: kwargs = { - 'username': user, - 'password': password, - 'api_key': api_key, - 'project_id': tenant, - 'auth_url': auth_url, - 'region_name': region_name, - 'os_auth_plugin': os_auth_system + "username": user, + "password": password, + "api_key": api_key, + "project_id": tenant, + "auth_url": auth_url, + "region_name": region_name, + "os_auth_plugin": os_auth_system, } return suon.SaltNova(**kwargs) def boot(name, flavor_id=0, image_id=0, profile=None, timeout=300): - ''' + """ Boot (create) a new instance name @@ -190,13 +188,13 @@ def boot(name, flavor_id=0, image_id=0, profile=None, timeout=300): salt '*' nova.flavor_list salt '*' nova.image_list - ''' + """ conn = _auth(profile) return conn.boot(name, flavor_id, image_id, timeout) def volume_list(search_opts=None, profile=None): - ''' + """ List storage volumes search_opts @@ -211,13 +209,13 @@ def volume_list(search_opts=None, profile=None): salt '*' nova.volume_list search_opts='{"display_name": "myblock"}' profile=openstack - ''' + """ conn = _auth(profile) return conn.volume_list(search_opts=search_opts) def volume_show(name, profile=None): - ''' + """ Create a block storage volume name @@ -232,14 +230,13 @@ def volume_show(name, profile=None): salt '*' nova.volume_show myblock profile=openstack - ''' + """ conn = _auth(profile) return conn.volume_show(name) -def volume_create(name, size=100, snapshot=None, voltype=None, - profile=None): - ''' +def volume_create(name, size=100, snapshot=None, voltype=None, profile=None): + """ Create a block storage volume name @@ -263,18 +260,13 @@ def volume_create(name, size=100, snapshot=None, voltype=None, salt '*' nova.volume_create myblock size=300 profile=openstack - ''' + """ conn = _auth(profile) - return conn.volume_create( - name, - size, - snapshot, - voltype - ) + return conn.volume_create(name, size, snapshot, voltype) def volume_delete(name, profile=None): - ''' + """ Destroy the volume name @@ -289,15 +281,13 @@ def volume_delete(name, profile=None): salt '*' nova.volume_delete myblock profile=openstack - ''' + """ conn = _auth(profile) return conn.volume_delete(name) -def volume_detach(name, - profile=None, - timeout=300): - ''' +def volume_detach(name, profile=None, timeout=300): + """ Attach a block storage volume name @@ -315,20 +305,13 @@ def volume_detach(name, salt '*' nova.volume_detach myblock profile=openstack - ''' + """ conn = _auth(profile) - return conn.volume_detach( - name, - timeout - ) + return conn.volume_detach(name, timeout) -def volume_attach(name, - server_name, - device='/dev/xvdb', - profile=None, - timeout=300): - ''' +def volume_attach(name, server_name, device="/dev/xvdb", profile=None, timeout=300): + """ Attach a block storage volume name @@ -350,18 +333,13 @@ def volume_attach(name, salt '*' nova.volume_attach myblock slice.example.com profile=openstack salt '*' nova.volume_attach myblock server.example.com device='/dev/xvdb' profile=openstack - ''' + """ conn = _auth(profile) - return conn.volume_attach( - name, - server_name, - device, - timeout - ) + return conn.volume_attach(name, server_name, device, timeout) def suspend(instance_id, profile=None): - ''' + """ Suspend an instance instance_id @@ -373,13 +351,13 @@ def suspend(instance_id, profile=None): salt '*' nova.suspend 1138 - ''' + """ conn = _auth(profile) return conn.suspend(instance_id) def resume(instance_id, profile=None): - ''' + """ Resume an instance instance_id @@ -391,13 +369,13 @@ def resume(instance_id, profile=None): salt '*' nova.resume 1138 - ''' + """ conn = _auth(profile) return conn.resume(instance_id) def lock(instance_id, profile=None): - ''' + """ Lock an instance instance_id @@ -409,13 +387,13 @@ def lock(instance_id, profile=None): salt '*' nova.lock 1138 - ''' + """ conn = _auth(profile) return conn.lock(instance_id) def delete(instance_id, profile=None): - ''' + """ Delete an instance instance_id @@ -427,13 +405,13 @@ def delete(instance_id, profile=None): salt '*' nova.delete 1138 - ''' + """ conn = _auth(profile) return conn.delete(instance_id) def flavor_list(profile=None): - ''' + """ Return a list of available flavors (nova flavor-list) CLI Example: @@ -441,18 +419,20 @@ def flavor_list(profile=None): .. code-block:: bash salt '*' nova.flavor_list - ''' + """ conn = _auth(profile) return conn.flavor_list() -def flavor_create(name, # pylint: disable=C0103 - flavor_id=0, # pylint: disable=C0103 - ram=0, - disk=0, - vcpus=1, - profile=None): - ''' +def flavor_create( + name, # pylint: disable=C0103 + flavor_id=0, # pylint: disable=C0103 + ram=0, + disk=0, + vcpus=1, + profile=None, +): + """ Add a flavor to nova (nova flavor-create). The following parameters are required: @@ -472,19 +452,13 @@ def flavor_create(name, # pylint: disable=C0103 .. code-block:: bash salt '*' nova.flavor_create myflavor flavor_id=6 ram=4096 disk=10 vcpus=1 - ''' + """ conn = _auth(profile) - return conn.flavor_create( - name, - flavor_id, - ram, - disk, - vcpus - ) + return conn.flavor_create(name, flavor_id, ram, disk, vcpus) def flavor_delete(flavor_id, profile=None): # pylint: disable=C0103 - ''' + """ Delete a flavor from nova by id (nova flavor-delete) CLI Example: @@ -492,13 +466,13 @@ def flavor_delete(flavor_id, profile=None): # pylint: disable=C0103 .. code-block:: bash salt '*' nova.flavor_delete 7 - ''' + """ conn = _auth(profile) return conn.flavor_delete(flavor_id) def keypair_list(profile=None): - ''' + """ Return a list of available keypairs (nova keypair-list) CLI Example: @@ -506,13 +480,13 @@ def keypair_list(profile=None): .. code-block:: bash salt '*' nova.keypair_list - ''' + """ conn = _auth(profile) return conn.keypair_list() def keypair_add(name, pubfile=None, pubkey=None, profile=None): - ''' + """ Add a keypair to nova (nova keypair-add) CLI Examples: @@ -521,17 +495,13 @@ def keypair_add(name, pubfile=None, pubkey=None, profile=None): salt '*' nova.keypair_add mykey pubfile=/home/myuser/.ssh/id_rsa.pub salt '*' nova.keypair_add mykey pubkey='ssh-rsa myuser@mybox' - ''' + """ conn = _auth(profile) - return conn.keypair_add( - name, - pubfile, - pubkey - ) + return conn.keypair_add(name, pubfile, pubkey) def keypair_delete(name, profile=None): - ''' + """ Add a keypair to nova (nova keypair-delete) CLI Example: @@ -539,13 +509,13 @@ def keypair_delete(name, profile=None): .. code-block:: bash salt '*' nova.keypair_delete mykey - ''' + """ conn = _auth(profile) return conn.keypair_delete(name) def image_list(name=None, profile=None): - ''' + """ Return a list of available images (nova images-list + nova image-show) If a name is provided, only that image will be displayed. @@ -555,16 +525,15 @@ def image_list(name=None, profile=None): salt '*' nova.image_list salt '*' nova.image_list myimage - ''' + """ conn = _auth(profile) return conn.image_list(name) -def image_meta_set(image_id=None, - name=None, - profile=None, - **kwargs): # pylint: disable=C0103 - ''' +def image_meta_set( + image_id=None, name=None, profile=None, **kwargs +): # pylint: disable=C0103 + """ Sets a key=value pair in the metadata for an image (nova image-meta set) CLI Examples: @@ -573,20 +542,15 @@ def image_meta_set(image_id=None, salt '*' nova.image_meta_set 6f52b2ff-0b31-4d84-8fd1-af45b84824f6 cheese=gruyere salt '*' nova.image_meta_set name=myimage salad=pasta beans=baked - ''' + """ conn = _auth(profile) - return conn.image_meta_set( - image_id, - name, - **kwargs - ) + return conn.image_meta_set(image_id, name, **kwargs) -def image_meta_delete(image_id=None, # pylint: disable=C0103 - name=None, - keys=None, - profile=None): - ''' +def image_meta_delete( + image_id=None, name=None, keys=None, profile=None # pylint: disable=C0103 +): + """ Delete a key=value pair from the metadata for an image (nova image-meta set) @@ -596,17 +560,13 @@ def image_meta_delete(image_id=None, # pylint: disable=C0103 salt '*' nova.image_meta_delete 6f52b2ff-0b31-4d84-8fd1-af45b84824f6 keys=cheese salt '*' nova.image_meta_delete name=myimage keys=salad,beans - ''' + """ conn = _auth(profile) - return conn.image_meta_delete( - image_id, - name, - keys - ) + return conn.image_meta_delete(image_id, name, keys) def list_(profile=None): - ''' + """ To maintain the feel of the nova command line, this function simply calls the server_list function. @@ -615,12 +575,12 @@ def list_(profile=None): .. code-block:: bash salt '*' nova.list - ''' + """ return server_list(profile=profile) def server_list(profile=None): - ''' + """ Return list of active servers CLI Example: @@ -628,13 +588,13 @@ def server_list(profile=None): .. code-block:: bash salt '*' nova.server_list - ''' + """ conn = _auth(profile) return conn.server_list() def show(server_id, profile=None): - ''' + """ To maintain the feel of the nova command line, this function simply calls the server_show function. @@ -643,12 +603,12 @@ def show(server_id, profile=None): .. code-block:: bash salt '*' nova.show - ''' + """ return server_show(server_id, profile) def server_list_detailed(profile=None): - ''' + """ Return detailed list of active servers CLI Example: @@ -656,13 +616,13 @@ def server_list_detailed(profile=None): .. code-block:: bash salt '*' nova.server_list_detailed - ''' + """ conn = _auth(profile) return conn.server_list_detailed() def server_show(server_id, profile=None): - ''' + """ Return detailed information for an active server CLI Example: @@ -670,13 +630,13 @@ def server_show(server_id, profile=None): .. code-block:: bash salt '*' nova.server_show - ''' + """ conn = _auth(profile) return conn.server_show(server_id) def secgroup_create(name, description, profile=None): - ''' + """ Add a secgroup to nova (nova secgroup-create) CLI Example: @@ -684,13 +644,13 @@ def secgroup_create(name, description, profile=None): .. code-block:: bash salt '*' nova.secgroup_create mygroup 'This is my security group' - ''' + """ conn = _auth(profile) return conn.secgroup_create(name, description) def secgroup_delete(name, profile=None): - ''' + """ Delete a secgroup to nova (nova secgroup-delete) CLI Example: @@ -698,13 +658,13 @@ def secgroup_delete(name, profile=None): .. code-block:: bash salt '*' nova.secgroup_delete mygroup - ''' + """ conn = _auth(profile) return conn.secgroup_delete(name) def secgroup_list(profile=None): - ''' + """ Return a list of available security groups (nova items-list) CLI Example: @@ -712,13 +672,13 @@ def secgroup_list(profile=None): .. code-block:: bash salt '*' nova.secgroup_list - ''' + """ conn = _auth(profile) return conn.secgroup_list() def server_by_name(name, profile=None): - ''' + """ Return information about a server name @@ -729,99 +689,99 @@ def server_by_name(name, profile=None): .. code-block:: bash salt '*' nova.server_by_name myserver profile=openstack - ''' + """ conn = _auth(profile) return conn.server_by_name(name) -#The following is a list of functions that need to be incorporated in the -#nova module. This list should be updated as functions are added. +# The following is a list of functions that need to be incorporated in the +# nova module. This list should be updated as functions are added. # -#absolute-limits Print a list of absolute limits for a user -#actions Retrieve server actions. -#add-fixed-ip Add new IP address to network. -#add-floating-ip Add a floating IP address to a server. -#aggregate-add-host Add the host to the specified aggregate. -#aggregate-create Create a new aggregate with the specified details. -#aggregate-delete Delete the aggregate by its id. -#aggregate-details Show details of the specified aggregate. -#aggregate-list Print a list of all aggregates. -#aggregate-remove-host +# absolute-limits Print a list of absolute limits for a user +# actions Retrieve server actions. +# add-fixed-ip Add new IP address to network. +# add-floating-ip Add a floating IP address to a server. +# aggregate-add-host Add the host to the specified aggregate. +# aggregate-create Create a new aggregate with the specified details. +# aggregate-delete Delete the aggregate by its id. +# aggregate-details Show details of the specified aggregate. +# aggregate-list Print a list of all aggregates. +# aggregate-remove-host # Remove the specified host from the specified aggregate. -#aggregate-set-metadata +# aggregate-set-metadata # Update the metadata associated with the aggregate. -#aggregate-update Update the aggregate's name and optionally +# aggregate-update Update the aggregate's name and optionally # availability zone. -#cloudpipe-create Create a cloudpipe instance for the given project -#cloudpipe-list Print a list of all cloudpipe instances. -#console-log Get console log output of a server. -#credentials Show user credentials returned from auth -#describe-resource Show details about a resource -#diagnostics Retrieve server diagnostics. -#dns-create Create a DNS entry for domain, name and ip. -#dns-create-private-domain +# cloudpipe-create Create a cloudpipe instance for the given project +# cloudpipe-list Print a list of all cloudpipe instances. +# console-log Get console log output of a server. +# credentials Show user credentials returned from auth +# describe-resource Show details about a resource +# diagnostics Retrieve server diagnostics. +# dns-create Create a DNS entry for domain, name and ip. +# dns-create-private-domain # Create the specified DNS domain. -#dns-create-public-domain +# dns-create-public-domain # Create the specified DNS domain. -#dns-delete Delete the specified DNS entry. -#dns-delete-domain Delete the specified DNS domain. -#dns-domains Print a list of available dns domains. -#dns-list List current DNS entries for domain and ip or domain +# dns-delete Delete the specified DNS entry. +# dns-delete-domain Delete the specified DNS domain. +# dns-domains Print a list of available dns domains. +# dns-list List current DNS entries for domain and ip or domain # and name. -#endpoints Discover endpoints that get returned from the +# endpoints Discover endpoints that get returned from the # authenticate services -#floating-ip-create Allocate a floating IP for the current tenant. -#floating-ip-delete De-allocate a floating IP. -#floating-ip-list List floating ips for this tenant. -#floating-ip-pool-list +# floating-ip-create Allocate a floating IP for the current tenant. +# floating-ip-delete De-allocate a floating IP. +# floating-ip-list List floating ips for this tenant. +# floating-ip-pool-list # List all floating ip pools. -#get-vnc-console Get a vnc console to a server. -#host-action Perform a power action on a host. -#host-update Update host settings. -#image-create Create a new image by taking a snapshot of a running +# get-vnc-console Get a vnc console to a server. +# host-action Perform a power action on a host. +# host-update Update host settings. +# image-create Create a new image by taking a snapshot of a running # server. -#image-delete Delete an image. -#live-migration Migrates a running instance to a new machine. -#meta Set or Delete metadata on a server. -#migrate Migrate a server. -#pause Pause a server. -#rate-limits Print a list of rate limits for a user -#reboot Reboot a server. -#rebuild Shutdown, re-image, and re-boot a server. -#remove-fixed-ip Remove an IP address from a server. -#remove-floating-ip Remove a floating IP address from a server. -#rename Rename a server. -#rescue Rescue a server. -#resize Resize a server. -#resize-confirm Confirm a previous resize. -#resize-revert Revert a previous resize (and return to the previous +# image-delete Delete an image. +# live-migration Migrates a running instance to a new machine. +# meta Set or Delete metadata on a server. +# migrate Migrate a server. +# pause Pause a server. +# rate-limits Print a list of rate limits for a user +# reboot Reboot a server. +# rebuild Shutdown, re-image, and re-boot a server. +# remove-fixed-ip Remove an IP address from a server. +# remove-floating-ip Remove a floating IP address from a server. +# rename Rename a server. +# rescue Rescue a server. +# resize Resize a server. +# resize-confirm Confirm a previous resize. +# resize-revert Revert a previous resize (and return to the previous # VM). -#root-password Change the root password for a server. -#secgroup-add-group-rule +# root-password Change the root password for a server. +# secgroup-add-group-rule # Add a source group rule to a security group. -#secgroup-add-rule Add a rule to a security group. -#secgroup-delete-group-rule +# secgroup-add-rule Add a rule to a security group. +# secgroup-delete-group-rule # Delete a source group rule from a security group. -#secgroup-delete-rule +# secgroup-delete-rule # Delete a rule from a security group. -#secgroup-list-rules +# secgroup-list-rules # List rules for a security group. -#ssh SSH into a server. -#unlock Unlock a server. -#unpause Unpause a server. -#unrescue Unrescue a server. -#usage-list List usage data for all tenants -#volume-list List all the volumes. -#volume-snapshot-create +# ssh SSH into a server. +# unlock Unlock a server. +# unpause Unpause a server. +# unrescue Unrescue a server. +# usage-list List usage data for all tenants +# volume-list List all the volumes. +# volume-snapshot-create # Add a new snapshot. -#volume-snapshot-delete +# volume-snapshot-delete # Remove a snapshot. -#volume-snapshot-list +# volume-snapshot-list # List all the snapshots. -#volume-snapshot-show +# volume-snapshot-show # Show details about a snapshot. -#volume-type-create Create a new volume type. -#volume-type-delete Delete a specific flavor -#volume-type-list Print a list of available 'volume types'. -#x509-create-cert Create x509 cert for a user in tenant -#x509-get-root-cert Fetches the x509 root cert. +# volume-type-create Create a new volume type. +# volume-type-delete Delete a specific flavor +# volume-type-list Print a list of available 'volume types'. +# x509-create-cert Create x509 cert for a user in tenant +# x509-get-root-cert Fetches the x509 root cert. diff --git a/salt/modules/npm.py b/salt/modules/npm.py index aaa3325191b..6e22658e390 100644 --- a/salt/modules/npm.py +++ b/salt/modules/npm.py @@ -1,79 +1,86 @@ # -*- coding: utf-8 -*- -''' +""" Manage and query NPM packages. -''' -from __future__ import absolute_import, unicode_literals, print_function -try: - from shlex import quote as _cmd_quote # pylint: disable=E0611 -except ImportError: - from pipes import quote as _cmd_quote +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging +import salt.modules.cmdmod + # Import salt libs import salt.utils.json import salt.utils.path import salt.utils.user -import salt.modules.cmdmod from salt.exceptions import CommandExecutionError -from salt.utils.versions import LooseVersion as _LooseVersion from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion + +try: + from shlex import quote as _cmd_quote # pylint: disable=E0611 +except ImportError: + from pipes import quote as _cmd_quote log = logging.getLogger(__name__) # Function alias to make sure not to shadow built-in's -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} def __virtual__(): - ''' + """ Only work when npm is installed. - ''' + """ try: - if salt.utils.path.which('npm') is not None: + if salt.utils.path.which("npm") is not None: _check_valid_version() return True else: - return (False, 'npm execution module could not be loaded ' - 'because the npm binary could not be located') + return ( + False, + "npm execution module could not be loaded " + "because the npm binary could not be located", + ) except CommandExecutionError as exc: return (False, six.text_type(exc)) def _check_valid_version(): - ''' + """ Check the version of npm to ensure this module will work. Currently npm must be at least version 1.2. - ''' + """ # Locate the full path to npm - npm_path = salt.utils.path.which('npm') + npm_path = salt.utils.path.which("npm") # pylint: disable=no-member - res = salt.modules.cmdmod.run('{npm} --version'.format(npm=npm_path), output_loglevel='quiet') - npm_version, valid_version = _LooseVersion(res), _LooseVersion('1.2') + res = salt.modules.cmdmod.run( + "{npm} --version".format(npm=npm_path), output_loglevel="quiet" + ) + npm_version, valid_version = _LooseVersion(res), _LooseVersion("1.2") # pylint: enable=no-member if npm_version < valid_version: raise CommandExecutionError( - '\'npm\' is not recent enough({0} < {1}). Please Upgrade.'.format( + "'npm' is not recent enough({0} < {1}). Please Upgrade.".format( npm_version, valid_version ) ) -def install(pkg=None, - pkgs=None, - dir=None, - runas=None, - registry=None, - env=None, - dry_run=False, - silent=True): - ''' +def install( + pkg=None, + pkgs=None, + dir=None, + runas=None, + registry=None, + env=None, + dry_run=False, + silent=True, +): + """ Install an NPM package. If no directory is specified, the package will be installed globally. If @@ -131,7 +138,7 @@ def install(pkg=None, salt '*' npm.install coffee-script@1.0.1 - ''' + """ # Protect against injection if pkg: pkgs = [_cmd_quote(pkg)] @@ -142,18 +149,18 @@ def install(pkg=None, if registry: registry = _cmd_quote(registry) - cmd = ['npm', 'install', '--json'] + cmd = ["npm", "install", "--json"] if silent: - cmd.append('--silent') + cmd.append("--silent") if not dir: - cmd.append('--global') + cmd.append("--global") if registry: cmd.append('--registry="{0}"'.format(registry)) if dry_run: - cmd.append('--dry-run') + cmd.append("--dry-run") cmd.extend(pkgs) @@ -162,20 +169,18 @@ def install(pkg=None, if runas: uid = salt.utils.user.get_uid(runas) if uid: - env.update({'SUDO_UID': uid, 'SUDO_USER': ''}) + env.update({"SUDO_UID": uid, "SUDO_USER": ""}) - cmd = ' '.join(cmd) - result = __salt__['cmd.run_all'](cmd, - python_shell=True, - cwd=dir, - runas=runas, - env=env) + cmd = " ".join(cmd) + result = __salt__["cmd.run_all"]( + cmd, python_shell=True, cwd=dir, runas=runas, env=env + ) - if result['retcode'] != 0: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0: + raise CommandExecutionError(result["stderr"]) # npm >1.2.21 is putting the output to stderr even though retcode is 0 - npm_output = result['stdout'] or result['stderr'] + npm_output = result["stdout"] or result["stderr"] try: return salt.utils.json.find_json(npm_output) except ValueError: @@ -183,7 +188,7 @@ def install(pkg=None, def uninstall(pkg, dir=None, runas=None, env=None): - ''' + """ Uninstall an NPM package. If no directory is specified, the package will be uninstalled globally. @@ -211,7 +216,7 @@ def uninstall(pkg, dir=None, runas=None, env=None): salt '*' npm.uninstall coffee-script - ''' + """ # Protect against injection if pkg: pkg = _cmd_quote(pkg) @@ -221,24 +226,26 @@ def uninstall(pkg, dir=None, runas=None, env=None): if runas: uid = salt.utils.user.get_uid(runas) if uid: - env.update({'SUDO_UID': uid, 'SUDO_USER': ''}) + env.update({"SUDO_UID": uid, "SUDO_USER": ""}) - cmd = ['npm', 'uninstall', '"{0}"'.format(pkg)] + cmd = ["npm", "uninstall", '"{0}"'.format(pkg)] if not dir: - cmd.append('--global') + cmd.append("--global") - cmd = ' '.join(cmd) + cmd = " ".join(cmd) - result = __salt__['cmd.run_all'](cmd, python_shell=True, cwd=dir, runas=runas, env=env) + result = __salt__["cmd.run_all"]( + cmd, python_shell=True, cwd=dir, runas=runas, env=env + ) - if result['retcode'] != 0: - log.error(result['stderr']) + if result["retcode"] != 0: + log.error(result["stderr"]) return False return True def list_(pkg=None, dir=None, runas=None, env=None, depth=None): - ''' + """ List installed NPM packages. If no directory is specified, this will return the list of globally- @@ -274,43 +281,46 @@ def list_(pkg=None, dir=None, runas=None, env=None, depth=None): salt '*' npm.list - ''' + """ env = env or {} if runas: uid = salt.utils.user.get_uid(runas) if uid: - env.update({'SUDO_UID': uid, 'SUDO_USER': ''}) + env.update({"SUDO_UID": uid, "SUDO_USER": ""}) - cmd = ['npm', 'list', '--json', '--silent'] + cmd = ["npm", "list", "--json", "--silent"] if not dir: - cmd.append('--global') + cmd.append("--global") if depth is not None: if not isinstance(depth, (int, float)): - raise salt.exceptions.SaltInvocationError('Error: depth {0} must be a number'.format(depth)) - cmd.append('--depth={0}'.format(int(depth))) + raise salt.exceptions.SaltInvocationError( + "Error: depth {0} must be a number".format(depth) + ) + cmd.append("--depth={0}".format(int(depth))) if pkg: # Protect against injection pkg = _cmd_quote(pkg) cmd.append('"{0}"'.format(pkg)) - cmd = ' '.join(cmd) + cmd = " ".join(cmd) - result = __salt__['cmd.run_all']( - cmd, cwd=dir, runas=runas, env=env, python_shell=True, ignore_retcode=True) + result = __salt__["cmd.run_all"]( + cmd, cwd=dir, runas=runas, env=env, python_shell=True, ignore_retcode=True + ) # npm will return error code 1 for both no packages found and an actual # error. The only difference between the two cases are if stderr is empty - if result['retcode'] != 0 and result['stderr']: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0 and result["stderr"]: + raise CommandExecutionError(result["stderr"]) - return salt.utils.json.loads(result['stdout']).get('dependencies', {}) + return salt.utils.json.loads(result["stdout"]).get("dependencies", {}) def cache_clean(path=None, runas=None, env=None, force=False): - ''' + """ Clean cached NPM packages. If no path for a specific package is provided the entire cache will be cleared. @@ -337,32 +347,33 @@ def cache_clean(path=None, runas=None, env=None, force=False): salt '*' npm.cache_clean force=True - ''' + """ env = env or {} if runas: uid = salt.utils.user.get_uid(runas) if uid: - env.update({'SUDO_UID': uid, 'SUDO_USER': ''}) + env.update({"SUDO_UID": uid, "SUDO_USER": ""}) - cmd = ['npm', 'cache', 'clean'] + cmd = ["npm", "cache", "clean"] if path: cmd.append(path) if force is True: - cmd.append('--force') + cmd.append("--force") - cmd = ' '.join(cmd) - result = __salt__['cmd.run_all']( - cmd, cwd=None, runas=runas, env=env, python_shell=True, ignore_retcode=True) + cmd = " ".join(cmd) + result = __salt__["cmd.run_all"]( + cmd, cwd=None, runas=runas, env=env, python_shell=True, ignore_retcode=True + ) - if result['retcode'] != 0: - log.error(result['stderr']) + if result["retcode"] != 0: + log.error(result["stderr"]) return False return True def cache_list(path=None, runas=None, env=None): - ''' + """ List NPM cached packages. If no path for a specific package is provided this will list all the cached packages. @@ -384,30 +395,31 @@ def cache_list(path=None, runas=None, env=None): salt '*' npm.cache_clean - ''' + """ env = env or {} if runas: uid = salt.utils.user.get_uid(runas) if uid: - env.update({'SUDO_UID': uid, 'SUDO_USER': ''}) + env.update({"SUDO_UID": uid, "SUDO_USER": ""}) - cmd = ['npm', 'cache', 'ls'] + cmd = ["npm", "cache", "ls"] if path: cmd.append(path) - cmd = ' '.join(cmd) - result = __salt__['cmd.run_all']( - cmd, cwd=None, runas=runas, env=env, python_shell=True, ignore_retcode=True) + cmd = " ".join(cmd) + result = __salt__["cmd.run_all"]( + cmd, cwd=None, runas=runas, env=env, python_shell=True, ignore_retcode=True + ) - if result['retcode'] != 0 and result['stderr']: - raise CommandExecutionError(result['stderr']) + if result["retcode"] != 0 and result["stderr"]: + raise CommandExecutionError(result["stderr"]) - return result['stdout'] + return result["stdout"] def cache_path(runas=None, env=None): - ''' + """ List path of the NPM cache directory. runas @@ -424,17 +436,18 @@ def cache_path(runas=None, env=None): salt '*' npm.cache_path - ''' + """ env = env or {} if runas: uid = salt.utils.user.get_uid(runas) if uid: - env.update({'SUDO_UID': uid, 'SUDO_USER': ''}) + env.update({"SUDO_UID": uid, "SUDO_USER": ""}) - cmd = 'npm config get cache' + cmd = "npm config get cache" - result = __salt__['cmd.run_all']( - cmd, cwd=None, runas=runas, env=env, python_shell=True, ignore_retcode=True) + result = __salt__["cmd.run_all"]( + cmd, cwd=None, runas=runas, env=env, python_shell=True, ignore_retcode=True + ) - return result.get('stdout') or result.get('stderr') + return result.get("stdout") or result.get("stderr") diff --git a/salt/modules/nspawn.py b/salt/modules/nspawn.py index 57a817ebc52..e00146b2d12 100644 --- a/salt/modules/nspawn.py +++ b/salt/modules/nspawn.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage nspawn containers .. versionadded:: 2015.8.0 @@ -19,18 +19,19 @@ Minions running systemd >= 219 will place new containers in ``nsenter(1)`` is required to run commands within containers. It should already be present on any systemd host, as part of the **util-linux** package. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import functools import logging import os import re import shutil -import time import tempfile +import time # Import Salt libs import salt.defaults.exitcodes @@ -45,64 +46,68 @@ from salt.ext.six.moves import range # pylint: disable=redefined-builtin log = logging.getLogger(__name__) __func_alias__ = { - 'list_': 'list', + "list_": "list", } -__virtualname__ = 'nspawn' -SEED_MARKER = '/nspawn.initial_seed' -WANT = '/etc/systemd/system/multi-user.target.wants/systemd-nspawn@{0}.service' -EXEC_DRIVER = 'nsenter' +__virtualname__ = "nspawn" +SEED_MARKER = "/nspawn.initial_seed" +WANT = "/etc/systemd/system/multi-user.target.wants/systemd-nspawn@{0}.service" +EXEC_DRIVER = "nsenter" def __virtual__(): - ''' + """ Only work on systems that have been booted with systemd - ''' - if __grains__['kernel'] == 'Linux' \ - and salt.utils.systemd.booted(__context__): + """ + if __grains__["kernel"] == "Linux" and salt.utils.systemd.booted(__context__): if salt.utils.systemd.version() is None: - log.error('nspawn: Unable to determine systemd version') + log.error("nspawn: Unable to determine systemd version") else: return __virtualname__ - return (False, 'The nspawn execution module failed to load: ' - 'only work on systems that have been booted with systemd.') + return ( + False, + "The nspawn execution module failed to load: " + "only work on systems that have been booted with systemd.", + ) def _sd_version(): - ''' + """ Returns __context__.get('systemd.version', 0), avoiding duplication of the call to dict.get and making it easier to change how we handle this context var in the future - ''' + """ return salt.utils.systemd.version(__context__) def _ensure_exists(wrapped): - ''' + """ Decorator to ensure that the named container exists. - ''' + """ + @functools.wraps(wrapped) def check_exists(name, *args, **kwargs): if not exists(name): - raise CommandExecutionError( - 'Container \'{0}\' does not exist'.format(name) - ) + raise CommandExecutionError("Container '{0}' does not exist".format(name)) return wrapped(name, *args, **salt.utils.args.clean_kwargs(**kwargs)) + return check_exists -def _root(name='', all_roots=False): - ''' +def _root(name="", all_roots=False): + """ Return the container root directory. Starting with systemd 219, new images go into /var/lib/machines. - ''' + """ if _sd_version() >= 219: if all_roots: - return [os.path.join(x, name) - for x in ('/var/lib/machines', '/var/lib/container')] + return [ + os.path.join(x, name) + for x in ("/var/lib/machines", "/var/lib/container") + ] else: - return os.path.join('/var/lib/machines', name) + return os.path.join("/var/lib/machines", name) else: - ret = os.path.join('/var/lib/container', name) + ret = os.path.join("/var/lib/container", name) if all_roots: return [ret] else: @@ -110,69 +115,73 @@ def _root(name='', all_roots=False): def _make_container_root(name): - ''' + """ Make the container root directory - ''' + """ path = _root(name) if os.path.exists(path): - __context__['retcode'] = salt.defaults.exitcodes.SALT_BUILD_FAIL - raise CommandExecutionError( - 'Container {0} already exists'.format(name) - ) + __context__["retcode"] = salt.defaults.exitcodes.SALT_BUILD_FAIL + raise CommandExecutionError("Container {0} already exists".format(name)) else: try: os.makedirs(path) return path except OSError as exc: raise CommandExecutionError( - 'Unable to make container root directory {0}: {1}' - .format(name, exc) + "Unable to make container root directory {0}: {1}".format(name, exc) ) def _build_failed(dst, name): try: - __context__['retcode'] = salt.defaults.exitcodes.SALT_BUILD_FAIL + __context__["retcode"] = salt.defaults.exitcodes.SALT_BUILD_FAIL shutil.rmtree(dst) except OSError as exc: if exc.errno != errno.ENOENT: raise CommandExecutionError( - 'Unable to cleanup container root dir {0}'.format(dst) + "Unable to cleanup container root dir {0}".format(dst) ) - raise CommandExecutionError( - 'Container {0} failed to build'.format(name) - ) + raise CommandExecutionError("Container {0} failed to build".format(name)) def _bootstrap_arch(name, **kwargs): - ''' + """ Bootstrap an Arch Linux container - ''' - if not salt.utils.path.which('pacstrap'): + """ + if not salt.utils.path.which("pacstrap"): raise CommandExecutionError( - 'pacstrap not found, is the arch-install-scripts package ' - 'installed?' + "pacstrap not found, is the arch-install-scripts package " "installed?" ) dst = _make_container_root(name) - cmd = 'pacstrap -c -d {0} base'.format(dst) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) - if ret['retcode'] != 0: + cmd = "pacstrap -c -d {0} base".format(dst) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) + if ret["retcode"] != 0: _build_failed(dst, name) return ret def _bootstrap_debian(name, **kwargs): - ''' + """ Bootstrap a Debian Linux container - ''' - version = kwargs.get('version', False) + """ + version = kwargs.get("version", False) if not version: - if __grains__['os'].lower() == 'debian': - version = __grains__['osrelease'] + if __grains__["os"].lower() == "debian": + version = __grains__["osrelease"] else: - version = 'stable' + version = "stable" - release_blacklist = ['hamm', 'slink', 'potato', 'woody', 'sarge', 'etch', 'lenny', 'squeeze', 'wheezy'] + release_blacklist = [ + "hamm", + "slink", + "potato", + "woody", + "sarge", + "etch", + "lenny", + "squeeze", + "wheezy", + ] if version in release_blacklist: raise CommandExecutionError( 'Unsupported Debian version "{0}". ' @@ -180,127 +189,130 @@ def _bootstrap_debian(name, **kwargs): ) dst = _make_container_root(name) - cmd = 'debootstrap --arch=amd64 {0} {1}'.format(version, dst) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) - if ret['retcode'] != 0: + cmd = "debootstrap --arch=amd64 {0} {1}".format(version, dst) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) + if ret["retcode"] != 0: _build_failed(dst, name) return ret def _bootstrap_fedora(name, **kwargs): - ''' + """ Bootstrap a Fedora container - ''' + """ dst = _make_container_root(name) - if not kwargs.get('version', False): - if __grains__['os'].lower() == 'fedora': - version = __grains__['osrelease'] + if not kwargs.get("version", False): + if __grains__["os"].lower() == "fedora": + version = __grains__["osrelease"] else: - version = '21' + version = "21" else: - version = '21' - cmd = ('yum -y --releasever={0} --nogpg --installroot={1} ' - '--disablerepo="*" --enablerepo=fedora install systemd passwd yum ' - 'fedora-release vim-minimal'.format(version, dst)) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) - if ret['retcode'] != 0: + version = "21" + cmd = ( + "yum -y --releasever={0} --nogpg --installroot={1} " + '--disablerepo="*" --enablerepo=fedora install systemd passwd yum ' + "fedora-release vim-minimal".format(version, dst) + ) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) + if ret["retcode"] != 0: _build_failed(dst, name) return ret def _bootstrap_ubuntu(name, **kwargs): - ''' + """ Bootstrap a Ubuntu Linux container - ''' - version = kwargs.get('version', False) + """ + version = kwargs.get("version", False) if not version: - if __grains__['os'].lower() == 'ubuntu': - version = __grains__['oscodename'] + if __grains__["os"].lower() == "ubuntu": + version = __grains__["oscodename"] else: - version = 'xenial' + version = "xenial" dst = _make_container_root(name) - cmd = 'debootstrap --arch=amd64 {0} {1}'.format(version, dst) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) - if ret['retcode'] != 0: + cmd = "debootstrap --arch=amd64 {0} {1}".format(version, dst) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) + if ret["retcode"] != 0: _build_failed(dst, name) return ret def _clear_context(): - ''' + """ Clear any lxc variables set in __context__ - ''' - for var in [x for x in __context__ if x.startswith('nspawn.')]: - log.trace('Clearing __context__[\'%s\']', var) + """ + for var in [x for x in __context__ if x.startswith("nspawn.")]: + log.trace("Clearing __context__['%s']", var) __context__.pop(var, None) def _ensure_running(name): - ''' + """ Raise an exception if the container does not exist - ''' - if state(name) != 'running': + """ + if state(name) != "running": return True else: return start(name) def _ensure_systemd(version): - ''' + """ Raises an exception if the systemd version is not greater than the passed version. - ''' + """ try: version = int(version) except ValueError: - raise CommandExecutionError('Invalid version \'{0}\''.format(version)) + raise CommandExecutionError("Invalid version '{0}'".format(version)) try: installed = _sd_version() - log.debug('nspawn: detected systemd %s', installed) + log.debug("nspawn: detected systemd %s", installed) except (IndexError, ValueError): - raise CommandExecutionError('nspawn: Unable to get systemd version') + raise CommandExecutionError("nspawn: Unable to get systemd version") if installed < version: raise CommandExecutionError( - 'This function requires systemd >= {0} ' - '(Detected version: {1}).'.format(version, installed) + "This function requires systemd >= {0} " + "(Detected version: {1}).".format(version, installed) ) -def _machinectl(cmd, - output_loglevel='debug', - ignore_retcode=False, - use_vt=False): - ''' +def _machinectl(cmd, output_loglevel="debug", ignore_retcode=False, use_vt=False): + """ Helper function to run machinectl - ''' - prefix = 'machinectl --no-legend --no-pager' - return __salt__['cmd.run_all']('{0} {1}'.format(prefix, cmd), - output_loglevel=output_loglevel, - ignore_retcode=ignore_retcode, - use_vt=use_vt) + """ + prefix = "machinectl --no-legend --no-pager" + return __salt__["cmd.run_all"]( + "{0} {1}".format(prefix, cmd), + output_loglevel=output_loglevel, + ignore_retcode=ignore_retcode, + use_vt=use_vt, + ) @_ensure_exists -def _run(name, - cmd, - output=None, - no_start=False, - stdin=None, - python_shell=True, - preserve_state=False, - output_loglevel='debug', - ignore_retcode=False, - use_vt=False, - keep_env=None): - ''' +def _run( + name, + cmd, + output=None, + no_start=False, + stdin=None, + python_shell=True, + preserve_state=False, + output_loglevel="debug", + ignore_retcode=False, + use_vt=False, + keep_env=None, +): + """ Common logic for nspawn.run functions - ''' + """ orig_state = state(name) exc = None try: - ret = __salt__['container_resource.run']( + ret = __salt__["container_resource.run"]( name, cmd, container_type=__virtualname__, @@ -312,16 +324,15 @@ def _run(name, output_loglevel=output_loglevel, ignore_retcode=ignore_retcode, use_vt=use_vt, - keep_env=keep_env) + keep_env=keep_env, + ) finally: # Make sure we stop the container if necessary, even if an exception # was raised. - if preserve_state \ - and orig_state == 'stopped' \ - and state(name) != 'stopped': + if preserve_state and orig_state == "stopped" and state(name) != "stopped": stop(name) - if output in (None, 'all'): + if output in (None, "all"): return ret else: return ret[output] @@ -329,7 +340,7 @@ def _run(name, @_ensure_exists def pid(name): - ''' + """ Returns the PID of a container name @@ -340,26 +351,28 @@ def pid(name): .. code-block:: bash salt myminion nspawn.pid arch1 - ''' + """ try: - return int(info(name).get('PID')) + return int(info(name).get("PID")) except (TypeError, ValueError) as exc: raise CommandExecutionError( - 'Unable to get PID for container \'{0}\': {1}'.format(name, exc) + "Unable to get PID for container '{0}': {1}".format(name, exc) ) -def run(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' +def run( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :mod:`cmd.run ` within a container name @@ -396,31 +409,35 @@ def run(name, .. code-block:: bash salt myminion nspawn.run mycontainer 'ifconfig -a' - ''' - return _run(name, - cmd, - output=None, - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) + """ + return _run( + name, + cmd, + output=None, + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) -def run_stdout(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' +def run_stdout( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :mod:`cmd.run_stdout ` within a container name @@ -459,31 +476,35 @@ def run_stdout(name, .. code-block:: bash salt myminion nspawn.run_stdout mycontainer 'ifconfig -a' - ''' - return _run(name, - cmd, - output='stdout', - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) + """ + return _run( + name, + cmd, + output="stdout", + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) -def run_stderr(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' +def run_stderr( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :mod:`cmd.run_stderr ` within a container name @@ -522,31 +543,35 @@ def run_stderr(name, .. code-block:: bash salt myminion nspawn.run_stderr mycontainer 'ip addr show' - ''' - return _run(name, - cmd, - output='stderr', - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) + """ + return _run( + name, + cmd, + output="stderr", + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) -def retcode(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' +def retcode( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :mod:`cmd.retcode ` within a container name @@ -585,31 +610,35 @@ def retcode(name, .. code-block:: bash salt myminion nspawn.retcode mycontainer 'ip addr show' - ''' - return _run(name, - cmd, - output='retcode', - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) + """ + return _run( + name, + cmd, + output="retcode", + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) -def run_all(name, - cmd, - no_start=False, - preserve_state=True, - stdin=None, - python_shell=True, - output_loglevel='debug', - use_vt=False, - ignore_retcode=False, - keep_env=None): - ''' +def run_all( + name, + cmd, + no_start=False, + preserve_state=True, + stdin=None, + python_shell=True, + output_loglevel="debug", + use_vt=False, + ignore_retcode=False, + keep_env=None, +): + """ Run :mod:`cmd.run_all ` within a container .. note:: @@ -654,22 +683,24 @@ def run_all(name, .. code-block:: bash salt myminion nspawn.run_all mycontainer 'ip addr show' - ''' - return _run(name, - cmd, - output='all', - no_start=no_start, - preserve_state=preserve_state, - stdin=stdin, - python_shell=python_shell, - output_loglevel=output_loglevel, - use_vt=use_vt, - ignore_retcode=ignore_retcode, - keep_env=keep_env) + """ + return _run( + name, + cmd, + output="all", + no_start=no_start, + preserve_state=preserve_state, + stdin=stdin, + python_shell=python_shell, + output_loglevel=output_loglevel, + use_vt=use_vt, + ignore_retcode=ignore_retcode, + keep_env=keep_env, + ) def bootstrap_container(name, dist=None, version=None): - ''' + """ Bootstrap a container from package servers, if dist is None the os the minion is running as will be created, otherwise the needed bootstrapping tools will need to be available on the host. @@ -679,12 +710,12 @@ def bootstrap_container(name, dist=None, version=None): .. code-block:: bash salt myminion nspawn.bootstrap_container - ''' + """ if not dist: - dist = __grains__['os'].lower() - log.debug('nspawn.bootstrap: no dist provided, defaulting to \'%s\'', dist) + dist = __grains__["os"].lower() + log.debug("nspawn.bootstrap: no dist provided, defaulting to '%s'", dist) try: - return globals()['_bootstrap_{0}'.format(dist)](name, version=version) + return globals()["_bootstrap_{0}".format(dist)](name, version=version) except KeyError: raise CommandExecutionError('Unsupported distribution "{0}"'.format(dist)) @@ -697,29 +728,31 @@ def _needs_install(name): # installs where the bootstrap can do much more than installing # the bare salt binaries. if has_minion: - processes = run_stdout(name, 'ps aux') - if 'salt-minion' not in processes: + processes = run_stdout(name, "ps aux") + if "salt-minion" not in processes: ret = 1 else: - retcode(name, 'salt-call --local service.stop salt-minion') + retcode(name, "salt-call --local service.stop salt-minion") else: ret = 1 return ret -def bootstrap_salt(name, - config=None, - approve_key=True, - install=True, - pub_key=None, - priv_key=None, - bootstrap_url=None, - force_install=False, - unconditional_install=False, - bootstrap_delay=None, - bootstrap_args=None, - bootstrap_shell=None): - ''' +def bootstrap_salt( + name, + config=None, + approve_key=True, + install=True, + pub_key=None, + priv_key=None, + bootstrap_url=None, + force_install=False, + unconditional_install=False, + bootstrap_delay=None, + bootstrap_args=None, + bootstrap_shell=None, +): + """ Bootstrap a container from package servers, if dist is None the os the minion is running as will be created, otherwise the needed bootstrapping tools will need to be available on the host. @@ -727,7 +760,7 @@ def bootstrap_salt(name, CLI Example:: salt '*' nspawn.bootstrap_salt arch1 - ''' + """ if bootstrap_delay is not None: try: time.sleep(bootstrap_delay) @@ -746,12 +779,12 @@ def bootstrap_salt(name, # custom bootstrap args can be totally customized, and user could # have inserted the placeholder for the config directory. # For example, some salt bootstrap script do not use at all -c - if '{0}' not in bootstrap_args: - bootstrap_args += ' -c {0}' + if "{0}" not in bootstrap_args: + bootstrap_args += " -c {0}" else: - bootstrap_args = '-c {0}' + bootstrap_args = "-c {0}" if not bootstrap_shell: - bootstrap_shell = 'sh' + bootstrap_shell = "sh" orig_state = _ensure_running(name) if not orig_state: @@ -760,77 +793,77 @@ def bootstrap_salt(name, needs_install = _needs_install(name) else: needs_install = True - seeded = retcode(name, 'test -e \'{0}\''.format(SEED_MARKER)) == 0 + seeded = retcode(name, "test -e '{0}'".format(SEED_MARKER)) == 0 tmp = tempfile.mkdtemp() if seeded and not unconditional_install: ret = True else: ret = False - cfg_files = __salt__['seed.mkconfig']( - config, tmp=tmp, id_=name, approve_key=approve_key, - pub_key=pub_key, priv_key=priv_key) + cfg_files = __salt__["seed.mkconfig"]( + config, + tmp=tmp, + id_=name, + approve_key=approve_key, + pub_key=pub_key, + priv_key=priv_key, + ) if needs_install or force_install or unconditional_install: if install: - rstr = __salt__['test.random_hash']() - configdir = '/tmp/.c_{0}'.format(rstr) - run(name, - 'install -m 0700 -d {0}'.format(configdir), - python_shell=False) - bs_ = __salt__['config.gather_bootstrap_script']( - bootstrap=bootstrap_url) - dest_dir = os.path.join('/tmp', rstr) + rstr = __salt__["test.random_hash"]() + configdir = "/tmp/.c_{0}".format(rstr) + run( + name, "install -m 0700 -d {0}".format(configdir), python_shell=False + ) + bs_ = __salt__["config.gather_bootstrap_script"]( + bootstrap=bootstrap_url + ) + dest_dir = os.path.join("/tmp", rstr) for cmd in [ - 'mkdir -p {0}'.format(dest_dir), - 'chmod 700 {0}'.format(dest_dir), + "mkdir -p {0}".format(dest_dir), + "chmod 700 {0}".format(dest_dir), ]: if run_stdout(name, cmd): - log.error('tmpdir %s creation failed (%s)', dest_dir, cmd) + log.error("tmpdir %s creation failed (%s)", dest_dir, cmd) return False - copy_to(name, - bs_, - '{0}/bootstrap.sh'.format(dest_dir), - makedirs=True) - copy_to(name, cfg_files['config'], - os.path.join(configdir, 'minion')) - copy_to(name, cfg_files['privkey'], - os.path.join(configdir, 'minion.pem')) - copy_to(name, cfg_files['pubkey'], - os.path.join(configdir, 'minion.pub')) + copy_to(name, bs_, "{0}/bootstrap.sh".format(dest_dir), makedirs=True) + copy_to(name, cfg_files["config"], os.path.join(configdir, "minion")) + copy_to( + name, cfg_files["privkey"], os.path.join(configdir, "minion.pem") + ) + copy_to( + name, cfg_files["pubkey"], os.path.join(configdir, "minion.pub") + ) bootstrap_args = bootstrap_args.format(configdir) - cmd = ('{0} {2}/bootstrap.sh {1}' - .format(bootstrap_shell, - bootstrap_args.replace("'", "''"), - dest_dir)) + cmd = "{0} {2}/bootstrap.sh {1}".format( + bootstrap_shell, bootstrap_args.replace("'", "''"), dest_dir + ) # log ASAP the forged bootstrap command which can be wrapped # out of the output in case of unexpected problem - log.info('Running %s in LXC container \'%s\'', cmd, name) - ret = retcode(name, cmd, output_loglevel='info', - use_vt=True) == 0 + log.info("Running %s in LXC container '%s'", cmd, name) + ret = retcode(name, cmd, output_loglevel="info", use_vt=True) == 0 else: ret = False else: - minion_config = salt.config.minion_config(cfg_files['config']) - pki_dir = minion_config['pki_dir'] - copy_to(name, cfg_files['config'], '/etc/salt/minion') - copy_to(name, cfg_files['privkey'], os.path.join(pki_dir, 'minion.pem')) - copy_to(name, cfg_files['pubkey'], os.path.join(pki_dir, 'minion.pub')) - run(name, - 'salt-call --local service.enable salt-minion', - python_shell=False) + minion_config = salt.config.minion_config(cfg_files["config"]) + pki_dir = minion_config["pki_dir"] + copy_to(name, cfg_files["config"], "/etc/salt/minion") + copy_to(name, cfg_files["privkey"], os.path.join(pki_dir, "minion.pem")) + copy_to(name, cfg_files["pubkey"], os.path.join(pki_dir, "minion.pub")) + run( + name, "salt-call --local service.enable salt-minion", python_shell=False + ) ret = True shutil.rmtree(tmp) - if orig_state == 'stopped': + if orig_state == "stopped": stop(name) # mark seeded upon successful install if ret: - run(name, - 'touch \'{0}\''.format(SEED_MARKER), - python_shell=False) + run(name, "touch '{0}'".format(SEED_MARKER), python_shell=False) return ret def list_all(): - ''' + """ Lists all nspawn containers CLI Example: @@ -838,10 +871,10 @@ def list_all(): .. code-block:: bash salt myminion nspawn.list_all - ''' + """ ret = [] if _sd_version() >= 219: - for line in _machinectl('list-images')['stdout'].splitlines(): + for line in _machinectl("list-images")["stdout"].splitlines(): try: ret.append(line.split()[0]) except IndexError: @@ -858,7 +891,7 @@ def list_all(): def list_running(): - ''' + """ Lists running nspawn containers .. note:: @@ -871,9 +904,9 @@ def list_running(): salt myminion nspawn.list_running salt myminion nspawn.list - ''' + """ ret = [] - for line in _machinectl('list')['stdout'].splitlines(): + for line in _machinectl("list")["stdout"].splitlines(): try: ret.append(line.split()[0]) except IndexError: @@ -883,11 +916,11 @@ def list_running(): # 'machinectl list' shows only running containers, so allow this to work as an # alias to nspawn.list_running -list_ = salt.utils.functools.alias_function(list_running, 'list_') +list_ = salt.utils.functools.alias_function(list_running, "list_") def list_stopped(): - ''' + """ Lists stopped nspawn containers CLI Example: @@ -895,12 +928,12 @@ def list_stopped(): .. code-block:: bash salt myminion nspawn.list_stopped - ''' + """ return sorted(set(list_all()) - set(list_running())) def exists(name): - ''' + """ Returns true if the named container exists CLI Example: @@ -908,8 +941,8 @@ def exists(name): .. code-block:: bash salt myminion nspawn.exists - ''' - contextkey = 'nspawn.exists.{0}'.format(name) + """ + contextkey = "nspawn.exists.{0}".format(name) if contextkey in __context__: return __context__[contextkey] __context__[contextkey] = name in list_all() @@ -918,7 +951,7 @@ def exists(name): @_ensure_exists def state(name): - ''' + """ Return state of container (running or stopped) CLI Example: @@ -926,16 +959,16 @@ def state(name): .. code-block:: bash salt myminion nspawn.state - ''' + """ try: - cmd = 'show {0} --property=State'.format(name) - return _machinectl(cmd, ignore_retcode=True)['stdout'].split('=')[-1] + cmd = "show {0} --property=State".format(name) + return _machinectl(cmd, ignore_retcode=True)["stdout"].split("=")[-1] except IndexError: - return 'stopped' + return "stopped" def info(name, **kwargs): - ''' + """ Return info about a container .. note:: @@ -955,9 +988,9 @@ def info(name, **kwargs): salt myminion nspawn.info arch1 salt myminion nspawn.info arch1 force_start=False - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - start_ = kwargs.pop('start', False) + start_ = kwargs.pop("start", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) @@ -968,22 +1001,22 @@ def info(name, **kwargs): # Have to parse 'machinectl status' here since 'machinectl show' doesn't # contain IP address info or OS info. *shakes fist angrily* - c_info = _machinectl('status {0}'.format(name)) - if c_info['retcode'] != 0: + c_info = _machinectl("status {0}".format(name)) + if c_info["retcode"] != 0: raise CommandExecutionError( - 'Unable to get info for container \'{0}\''.format(name) + "Unable to get info for container '{0}'".format(name) ) # Better human-readable names. False means key should be ignored. key_name_map = { - 'Iface': 'Network Interface', - 'Leader': 'PID', - 'Service': False, - 'Since': 'Running Since', + "Iface": "Network Interface", + "Leader": "PID", + "Service": False, + "Since": "Running Since", } ret = {} - kv_pair = re.compile(r'^\s+([A-Za-z]+): (.+)$') - tree = re.compile(r'[|`]') - lines = c_info['stdout'].splitlines() + kv_pair = re.compile(r"^\s+([A-Za-z]+): (.+)$") + tree = re.compile(r"[|`]") + lines = c_info["stdout"].splitlines() multiline = False cur_key = None for idx in range(len(lines)): @@ -994,7 +1027,7 @@ def info(name, **kwargs): key = key_name_map.get(key, key) if key is False: continue - elif key == 'PID': + elif key == "PID": try: val = val.split()[0] except IndexError: @@ -1019,7 +1052,7 @@ def info(name, **kwargs): @_ensure_exists def enable(name): - ''' + """ Set the named container to be launched at boot CLI Example: @@ -1027,17 +1060,17 @@ def enable(name): .. code-block:: bash salt myminion nspawn.enable - ''' - cmd = 'systemctl enable systemd-nspawn@{0}'.format(name) - if __salt__['cmd.retcode'](cmd, python_shell=False) != 0: - __context__['retcode'] = salt.defaults.exitcodes.EX_UNAVAILABLE + """ + cmd = "systemctl enable systemd-nspawn@{0}".format(name) + if __salt__["cmd.retcode"](cmd, python_shell=False) != 0: + __context__["retcode"] = salt.defaults.exitcodes.EX_UNAVAILABLE return False return True @_ensure_exists def disable(name): - ''' + """ Set the named container to *not* be launched at boot CLI Example: @@ -1045,17 +1078,17 @@ def disable(name): .. code-block:: bash salt myminion nspawn.enable - ''' - cmd = 'systemctl disable systemd-nspawn@{0}'.format(name) - if __salt__['cmd.retcode'](cmd, python_shell=False) != 0: - __context__['retcode'] = salt.defaults.exitcodes.EX_UNAVAILABLE + """ + cmd = "systemctl disable systemd-nspawn@{0}".format(name) + if __salt__["cmd.retcode"](cmd, python_shell=False) != 0: + __context__["retcode"] = salt.defaults.exitcodes.EX_UNAVAILABLE return False return True @_ensure_exists def start(name): - ''' + """ Start the named container CLI Example: @@ -1063,15 +1096,15 @@ def start(name): .. code-block:: bash salt myminion nspawn.start - ''' + """ if _sd_version() >= 219: - ret = _machinectl('start {0}'.format(name)) + ret = _machinectl("start {0}".format(name)) else: - cmd = 'systemctl start systemd-nspawn@{0}'.format(name) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "systemctl start systemd-nspawn@{0}".format(name) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - if ret['retcode'] != 0: - __context__['retcode'] = salt.defaults.exitcodes.EX_UNAVAILABLE + if ret["retcode"] != 0: + __context__["retcode"] = salt.defaults.exitcodes.EX_UNAVAILABLE return False return True @@ -1079,28 +1112,28 @@ def start(name): # This function is hidden from sphinx docs @_ensure_exists def stop(name, kill=False): - ''' + """ This is a compatibility function which provides the logic for nspawn.poweroff and nspawn.terminate. - ''' + """ if _sd_version() >= 219: if kill: - action = 'terminate' + action = "terminate" else: - action = 'poweroff' - ret = _machinectl('{0} {1}'.format(action, name)) + action = "poweroff" + ret = _machinectl("{0} {1}".format(action, name)) else: - cmd = 'systemctl stop systemd-nspawn@{0}'.format(name) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "systemctl stop systemd-nspawn@{0}".format(name) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - if ret['retcode'] != 0: - __context__['retcode'] = salt.defaults.exitcodes.EX_UNAVAILABLE + if ret["retcode"] != 0: + __context__["retcode"] = salt.defaults.exitcodes.EX_UNAVAILABLE return False return True def poweroff(name): - ''' + """ Issue a clean shutdown to the container. Equivalent to running ``machinectl poweroff`` on the named container. @@ -1119,12 +1152,12 @@ def poweroff(name): salt myminion nspawn.poweroff arch1 salt myminion nspawn.stop arch1 - ''' + """ return stop(name, kill=False) def terminate(name): - ''' + """ Kill all processes in the container without issuing a clean shutdown. Equivalent to running ``machinectl terminate`` on the named container. @@ -1144,21 +1177,21 @@ def terminate(name): salt myminion nspawn.terminate arch1 salt myminion nspawn.stop arch1 kill=True - ''' + """ return stop(name, kill=True) # This function is hidden from sphinx docs def restart(name): - ''' + """ This is a compatibility function which simply calls nspawn.reboot. - ''' + """ return reboot(name) @_ensure_exists def reboot(name, kill=False): - ''' + """ Reboot the container by sending a SIGINT to its init process. Equivalent to running ``machinectl reboot`` on the named container. @@ -1177,10 +1210,10 @@ def reboot(name, kill=False): salt myminion nspawn.reboot arch1 salt myminion nspawn.restart arch1 - ''' + """ if _sd_version() >= 219: - if state(name) == 'running': - ret = _machinectl('reboot {0}'.format(name)) + if state(name) == "running": + ret = _machinectl("reboot {0}".format(name)) else: # 'machinectl reboot' will fail on a stopped container return start(name) @@ -1190,26 +1223,26 @@ def reboot(name, kill=False): # we need stop and start the container in separate actions. # First stop the container - cmd = 'systemctl stop systemd-nspawn@{0}'.format(name) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "systemctl stop systemd-nspawn@{0}".format(name) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) # Now check if successful - if ret['retcode'] != 0: - __context__['retcode'] = salt.defaults.exitcodes.EX_UNAVAILABLE + if ret["retcode"] != 0: + __context__["retcode"] = salt.defaults.exitcodes.EX_UNAVAILABLE return False # Finally, start the container back up. No need to check the retcode a # second time, it'll be checked below once we exit the if/else block. - cmd = 'systemctl start systemd-nspawn@{0}'.format(name) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd = "systemctl start systemd-nspawn@{0}".format(name) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - if ret['retcode'] != 0: - __context__['retcode'] = salt.defaults.exitcodes.EX_UNAVAILABLE + if ret["retcode"] != 0: + __context__["retcode"] = salt.defaults.exitcodes.EX_UNAVAILABLE return False return True @_ensure_exists def remove(name, stop=False): - ''' + """ Remove the named container .. warning:: @@ -1231,22 +1264,20 @@ def remove(name, stop=False): salt '*' nspawn.remove foo salt '*' nspawn.remove foo stop=True - ''' - if not stop and state(name) != 'stopped': - raise CommandExecutionError( - 'Container \'{0}\' is not stopped'.format(name) - ) + """ + if not stop and state(name) != "stopped": + raise CommandExecutionError("Container '{0}' is not stopped".format(name)) def _failed_remove(name, exc): raise CommandExecutionError( - 'Unable to remove container \'{0}\': {1}'.format(name, exc) + "Unable to remove container '{0}': {1}".format(name, exc) ) if _sd_version() >= 219: - ret = _machinectl('remove {0}'.format(name)) - if ret['retcode'] != 0: - __context__['retcode'] = salt.defaults.exitcodes.EX_UNAVAILABLE - _failed_remove(name, ret['stderr']) + ret = _machinectl("remove {0}".format(name)) + if ret["retcode"] != 0: + __context__["retcode"] = salt.defaults.exitcodes.EX_UNAVAILABLE + _failed_remove(name, ret["stderr"]) else: try: shutil.rmtree(os.path.join(_root(), name)) @@ -1256,12 +1287,12 @@ def remove(name, stop=False): # Compatibility between LXC and nspawn -destroy = salt.utils.functools.alias_function(remove, 'destroy') +destroy = salt.utils.functools.alias_function(remove, "destroy") @_ensure_exists def copy_to(name, source, dest, overwrite=False, makedirs=False): - ''' + """ Copy a file from the host into a container name @@ -1287,60 +1318,58 @@ def copy_to(name, source, dest, overwrite=False, makedirs=False): .. code-block:: bash salt 'minion' nspawn.copy_to /tmp/foo /root/foo - ''' + """ path = source try: - if source.startswith('salt://'): - cached_source = __salt__['cp.cache_file'](source) + if source.startswith("salt://"): + cached_source = __salt__["cp.cache_file"](source) if not cached_source: - raise CommandExecutionError( - 'Unable to cache {0}'.format(source) - ) + raise CommandExecutionError("Unable to cache {0}".format(source)) path = cached_source except AttributeError: - raise SaltInvocationError('Invalid source file {0}'.format(source)) + raise SaltInvocationError("Invalid source file {0}".format(source)) if _sd_version() >= 219: # TODO: Use machinectl copy-to pass - return __salt__['container_resource.copy_to']( + return __salt__["container_resource.copy_to"]( name, path, dest, container_type=__virtualname__, exec_driver=EXEC_DRIVER, overwrite=overwrite, - makedirs=makedirs) + makedirs=makedirs, + ) -cp = salt.utils.functools.alias_function(copy_to, 'cp') +cp = salt.utils.functools.alias_function(copy_to, "cp") # Everything below requres systemd >= 219 # TODO: Write a decorator to keep these functions from being available to older # systemd versions. def _pull_image(pull_type, image, name, **kwargs): - ''' + """ Common logic for machinectl pull-* commands - ''' + """ _ensure_systemd(219) if exists(name): - raise SaltInvocationError( - 'Container \'{0}\' already exists'.format(name) - ) - if pull_type in ('raw', 'tar'): - valid_kwargs = ('verify',) - elif pull_type == 'dkr': - valid_kwargs = ('index',) + raise SaltInvocationError("Container '{0}' already exists".format(name)) + if pull_type in ("raw", "tar"): + valid_kwargs = ("verify",) + elif pull_type == "dkr": + valid_kwargs = ("index",) else: - raise SaltInvocationError( - 'Unsupported image type \'{0}\''.format(pull_type) - ) + raise SaltInvocationError("Unsupported image type '{0}'".format(pull_type)) kwargs = salt.utils.args.clean_kwargs(**kwargs) bad_kwargs = dict( - [(x, y) for x, y in six.iteritems(salt.utils.args.clean_kwargs(**kwargs)) - if x not in valid_kwargs] + [ + (x, y) + for x, y in six.iteritems(salt.utils.args.clean_kwargs(**kwargs)) + if x not in valid_kwargs + ] ) if bad_kwargs: @@ -1348,46 +1377,47 @@ def _pull_image(pull_type, image, name, **kwargs): pull_opts = [] - if pull_type in ('raw', 'tar'): - verify = kwargs.get('verify', False) + if pull_type in ("raw", "tar"): + verify = kwargs.get("verify", False) if not verify: - pull_opts.append('--verify=no') + pull_opts.append("--verify=no") else: + def _bad_verify(): raise SaltInvocationError( - '\'verify\' must be one of the following: ' - 'signature, checksum' + "'verify' must be one of the following: " "signature, checksum" ) + try: verify = verify.lower() except AttributeError: _bad_verify() else: - if verify not in ('signature', 'checksum'): + if verify not in ("signature", "checksum"): _bad_verify() - pull_opts.append('--verify={0}'.format(verify)) + pull_opts.append("--verify={0}".format(verify)) - elif pull_type == 'dkr': + elif pull_type == "dkr": # No need to validate the index URL, machinectl will take care of this # for us. - if 'index' in kwargs: - pull_opts.append('--dkr-index-url={0}'.format(kwargs['index'])) + if "index" in kwargs: + pull_opts.append("--dkr-index-url={0}".format(kwargs["index"])) - cmd = 'pull-{0} {1} {2} {3}'.format( - pull_type, ' '.join(pull_opts), image, name - ) + cmd = "pull-{0} {1} {2} {3}".format(pull_type, " ".join(pull_opts), image, name) result = _machinectl(cmd, use_vt=True) - if result['retcode'] != 0: - msg = 'Error occurred pulling image. Stderr from the pull command ' \ - '(if any) follows: ' - if result['stderr']: - msg += '\n\n{0}'.format(result['stderr']) + if result["retcode"] != 0: + msg = ( + "Error occurred pulling image. Stderr from the pull command " + "(if any) follows: " + ) + if result["stderr"]: + msg += "\n\n{0}".format(result["stderr"]) raise CommandExecutionError(msg) return True def pull_raw(url, name, verify=False): - ''' + """ Execute a ``machinectl pull-raw`` to download a .qcow2 or raw disk image, and add it to /var/lib/machines as a new container. @@ -1414,12 +1444,12 @@ def pull_raw(url, name, verify=False): .. code-block:: bash salt myminion nspawn.pull_raw http://ftp.halifax.rwth-aachen.de/fedora/linux/releases/21/Cloud/Images/x86_64/Fedora-Cloud-Base-20141203-21.x86_64.raw.xz fedora21 - ''' - return _pull_image('raw', url, name, verify=verify) + """ + return _pull_image("raw", url, name, verify=verify) def pull_tar(url, name, verify=False): - ''' + """ Execute a ``machinectl pull-raw`` to download a .tar container image, and add it to /var/lib/machines as a new container. @@ -1446,12 +1476,12 @@ def pull_tar(url, name, verify=False): .. code-block:: bash salt myminion nspawn.pull_tar http://foo.domain.tld/containers/archlinux-2015.02.01.tar.gz arch2 - ''' - return _pull_image('tar', url, name, verify=verify) + """ + return _pull_image("tar", url, name, verify=verify) def pull_dkr(url, name, index): - ''' + """ Execute a ``machinectl pull-dkr`` to download a docker image and add it to /var/lib/machines as a new container. @@ -1475,8 +1505,8 @@ def pull_dkr(url, name, index): salt myminion nspawn.pull_dkr centos/centos6 cent6 index=https://get.docker.com salt myminion nspawn.pull_docker centos/centos6 cent6 index=https://get.docker.com - ''' - return _pull_image('dkr', url, name, index=index) + """ + return _pull_image("dkr", url, name, index=index) -pull_docker = salt.utils.functools.alias_function(pull_dkr, 'pull_docker') +pull_docker = salt.utils.functools.alias_function(pull_dkr, "pull_docker") diff --git a/salt/modules/nxos.py b/salt/modules/nxos.py index d5c9017d17d..7977de77a97 100644 --- a/salt/modules/nxos.py +++ b/salt/modules/nxos.py @@ -1,42 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" Execution module for Cisco NX OS Switches Proxy minions .. versionadded:: 2016.11.0 For documentation on setting up the nxos proxy minion look in the documentation for :mod:`salt.proxy.nxos `. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.platform -__proxyenabled__ = ['nxos'] -__virtualname__ = 'nxos' +__proxyenabled__ = ["nxos"] +__virtualname__ = "nxos" def __virtual__(): if salt.utils.platform.is_proxy(): return __virtualname__ - return (False, 'The nxos execution module failed to load: ' - 'only available on proxy minions.') + return ( + False, + "The nxos execution module failed to load: only available on proxy minions.", + ) def system_info(): - ''' + """ Return system information for grains of the NX OS proxy minion .. code-block:: bash salt '*' nxos.system_info - ''' - return cmd('system_info') + """ + return cmd("system_info") def cmd(command, *args, **kwargs): - ''' + """ run commands from __proxy__ :mod:`salt.proxy.nxos` @@ -54,12 +56,12 @@ def cmd(command, *args, **kwargs): salt '*' nxos.cmd sendline 'show ver' salt '*' nxos.cmd show_run salt '*' nxos.cmd check_password username=admin password='$5$lkjsdfoi$blahblahblah' encrypted=True - ''' - proxy_prefix = __opts__['proxy']['proxytype'] - proxy_cmd = '.'.join([proxy_prefix, command]) + """ + proxy_prefix = __opts__["proxy"]["proxytype"] + proxy_cmd = ".".join([proxy_prefix, command]) if proxy_cmd not in __proxy__: return False for k in list(kwargs): - if k.startswith('__pub_'): + if k.startswith("__pub_"): kwargs.pop(k) return __proxy__[proxy_cmd](*args, **kwargs) diff --git a/salt/modules/nxos_api.py b/salt/modules/nxos_api.py index 0ce1863c629..ac869a12f79 100644 --- a/salt/modules/nxos_api.py +++ b/salt/modules/nxos_api.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Execution module to manage Cisco Nexus Switches (NX-OS) over the NX-API .. versionadded:: 2019.2.0 @@ -121,26 +121,27 @@ outside a ``nxos_api`` Proxy, e.g.: Remember that the above applies only when not running in a ``nxos_api`` Proxy Minion. If you want to use the :mod:`nxos_api Proxy`, please follow the documentation notes for a proper setup. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import difflib + # Import python stdlib import logging -import difflib + +from salt.exceptions import CommandExecutionError, SaltException # Import Salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError -from salt.exceptions import SaltException # ----------------------------------------------------------------------------- # execution module properties # ----------------------------------------------------------------------------- -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] # Any Proxy Minion should be able to execute these -__virtualname__ = 'nxos_api' +__virtualname__ = "nxos_api" # The Execution Module will be identified as ``nxos_api`` # The ``nxos`` namespace is already taken, used for SSH-based connections. @@ -156,52 +157,50 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ This module does not have external dependencies, hence it is widely available. - ''' + """ # No extra requirements, uses Salt native modules. return __virtualname__ + # ----------------------------------------------------------------------------- # helper functions # ----------------------------------------------------------------------------- -def _cli_command(commands, - method='cli', - **kwargs): - ''' +def _cli_command(commands, method="cli", **kwargs): + """ Execute a list of CLI commands. - ''' + """ if not isinstance(commands, (list, tuple)): commands = [commands] - rpc_responses = rpc(commands, - method=method, - **kwargs) + rpc_responses = rpc(commands, method=method, **kwargs) txt_responses = [] for rpc_reponse in rpc_responses: - error = rpc_reponse.get('error') + error = rpc_reponse.get("error") if error: - cmd = rpc_reponse.get('command') - if 'data' in error: - msg = 'The command "{cmd}" raised the error "{err}".'.format(cmd=cmd, err=error['data']['msg']) + cmd = rpc_reponse.get("command") + if "data" in error: + msg = 'The command "{cmd}" raised the error "{err}".'.format( + cmd=cmd, err=error["data"]["msg"] + ) raise SaltException(msg) else: msg = 'Invalid command: "{cmd}".'.format(cmd=cmd) raise SaltException(msg) - txt_responses.append(rpc_reponse['result']) + txt_responses.append(rpc_reponse["result"]) return txt_responses + # ----------------------------------------------------------------------------- # callable functions # ----------------------------------------------------------------------------- -def rpc(commands, - method='cli', - **kwargs): - ''' +def rpc(commands, method="cli", **kwargs): + """ Execute an arbitrary RPC request via the Nexus API. commands @@ -242,22 +241,23 @@ def rpc(commands, .. code-block:: bash salt-call --local nxps_api.rpc 'show version' - ''' - nxos_api_kwargs = __salt__['config.get']('nxos_api', {}) + """ + nxos_api_kwargs = __salt__["config.get"]("nxos_api", {}) nxos_api_kwargs.update(**kwargs) - if 'nxos_api.rpc' in __proxy__ and __salt__['config.get']('proxy:proxytype') == 'nxos_api': + if ( + "nxos_api.rpc" in __proxy__ + and __salt__["config.get"]("proxy:proxytype") == "nxos_api" + ): # If the nxos_api.rpc Proxy function is available and currently running # in a nxos_api Proxy Minion - return __proxy__['nxos_api.rpc'](commands, method=method, **nxos_api_kwargs) - nxos_api_kwargs = __salt__['config.get']('nxos_api', {}) + return __proxy__["nxos_api.rpc"](commands, method=method, **nxos_api_kwargs) + nxos_api_kwargs = __salt__["config.get"]("nxos_api", {}) nxos_api_kwargs.update(**kwargs) - return __utils__['nxos_api.rpc'](commands, method=method, **nxos_api_kwargs) + return __utils__["nxos_api.rpc"](commands, method=method, **nxos_api_kwargs) -def show(commands, - raw_text=True, - **kwargs): - ''' +def show(commands, raw_text=True, **kwargs): + """ Execute one or more show (non-configuration) commands. commands @@ -299,29 +299,29 @@ def show(commands, salt-call --local nxos_api.show 'show version' salt '*' nxos_api.show 'show bgp sessions' 'show processes' raw_text=False salt 'regular-minion' nxos_api.show 'show interfaces' host=sw01.example.com username=test password=test - ''' + """ ret = [] if raw_text: - method = 'cli_ascii' - key = 'msg' + method = "cli_ascii" + key = "msg" else: - method = 'cli' - key = 'body' - response_list = _cli_command(commands, - method=method, - **kwargs) + method = "cli" + key = "body" + response_list = _cli_command(commands, method=method, **kwargs) ret = [response[key] for response in response_list if response] return ret -def config(commands=None, - config_file=None, - template_engine='jinja', - context=None, - defaults=None, - saltenv='base', - **kwargs): - ''' +def config( + commands=None, + config_file=None, + template_engine="jinja", + context=None, + defaults=None, + saltenv="base", + **kwargs +): + """ Configures the Nexus switch with the specified commands. This method is used to send configuration commands to the switch. It @@ -402,26 +402,26 @@ def config(commands=None, salt '*' nxos_api.config commands="['spanning-tree mode mstp']" salt '*' nxos_api.config config_file=salt://config.txt salt '*' nxos_api.config config_file=https://bit.ly/2LGLcDy context="{'servers': ['1.2.3.4']}" - ''' - initial_config = show('show running-config', **kwargs)[0] + """ + initial_config = show("show running-config", **kwargs)[0] if config_file: - file_str = __salt__['cp.get_file_str'](config_file, saltenv=saltenv) + file_str = __salt__["cp.get_file_str"](config_file, saltenv=saltenv) if file_str is False: - raise CommandExecutionError('Source file {} not found'.format(config_file)) + raise CommandExecutionError("Source file {} not found".format(config_file)) elif commands: if isinstance(commands, (six.string_types, six.text_type)): commands = [commands] - file_str = '\n'.join(commands) + file_str = "\n".join(commands) # unify all the commands in a single file, to render them in a go if template_engine: - file_str = __salt__['file.apply_template_on_contents'](file_str, - template_engine, - context, - defaults, - saltenv) + file_str = __salt__["file.apply_template_on_contents"]( + file_str, template_engine, context, defaults, saltenv + ) # whatever the source of the commands would be, split them line by line commands = [line for line in file_str.splitlines() if line.strip()] ret = _cli_command(commands, **kwargs) - current_config = show('show running-config', **kwargs)[0] - diff = difflib.unified_diff(initial_config.splitlines(1)[4:], current_config.splitlines(1)[4:]) - return ''.join([x.replace('\r', '') for x in diff]) + current_config = show("show running-config", **kwargs)[0] + diff = difflib.unified_diff( + initial_config.splitlines(1)[4:], current_config.splitlines(1)[4:] + ) + return "".join([x.replace("\r", "") for x in diff]) diff --git a/salt/modules/omapi.py b/salt/modules/omapi.py index ef04c99edcc..ef3f454909b 100644 --- a/salt/modules/omapi.py +++ b/salt/modules/omapi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This module interacts with an ISC DHCP Server via OMAPI. server_ip and server_port params may be set in the minion config or pillar: @@ -10,46 +10,50 @@ config or pillar: omapi.server_port: 7991 :depends: pypureomapi Python module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import struct # Import salt libs import salt.utils.stringutils - log = logging.getLogger(__name__) try: import pypureomapi as omapi + omapi_support = True except ImportError as e: omapi_support = False def __virtual__(): - ''' + """ Confirm pypureomapi is available. - ''' + """ if omapi_support: - return 'omapi' - return (False, 'The omapi execution module cannot be loaded: ' - 'the pypureomapi python library is not available.') + return "omapi" + return ( + False, + "The omapi execution module cannot be loaded: " + "the pypureomapi python library is not available.", + ) def _conn(): - server_ip = __pillar__.get('omapi.server_ip', - __opts__.get('omapi.server_ip', '127.0.0.1')) - server_port = __pillar__.get('omapi.server_port', - __opts__.get('omapi.server_port', 7991)) - key = __pillar__.get('omapi.key', - __opts__.get('omapi.key', None)) - username = __pillar__.get('omapi.user', - __opts__.get('omapi.user', None)) + server_ip = __pillar__.get( + "omapi.server_ip", __opts__.get("omapi.server_ip", "127.0.0.1") + ) + server_port = __pillar__.get( + "omapi.server_port", __opts__.get("omapi.server_port", 7991) + ) + key = __pillar__.get("omapi.key", __opts__.get("omapi.key", None)) + username = __pillar__.get("omapi.user", __opts__.get("omapi.user", None)) if key: key = salt.utils.stringutils.to_bytes(key) if username: @@ -57,9 +61,8 @@ def _conn(): return omapi.Omapi(server_ip, server_port, username=username, key=key) -def add_host(mac, name=None, ip=None, ddns=False, group=None, - supersede_host=False): - ''' +def add_host(mac, name=None, ip=None, ddns=False, group=None, supersede_host=False): + """ Add a host object for the given mac. CLI Example: @@ -73,26 +76,26 @@ def add_host(mac, name=None, ip=None, ddns=False, group=None, .. code-block:: bash salt dhcp-server omapi.add_host ab:ab:ab:ab:ab:ab name=host1 ip=10.1.1.1 ddns=true - ''' - statements = '' + """ + statements = "" o = _conn() - msg = omapi.OmapiMessage.open(b'host') - msg.message.append((b'create', struct.pack(b'!I', 1))) - msg.message.append((b'exclusive', struct.pack(b'!I', 1))) - msg.obj.append((b'hardware-address', omapi.pack_mac(mac))) - msg.obj.append((b'hardware-type', struct.pack(b'!I', 1))) + msg = omapi.OmapiMessage.open(b"host") + msg.message.append((b"create", struct.pack(b"!I", 1))) + msg.message.append((b"exclusive", struct.pack(b"!I", 1))) + msg.obj.append((b"hardware-address", omapi.pack_mac(mac))) + msg.obj.append((b"hardware-type", struct.pack(b"!I", 1))) if ip: - msg.obj.append((b'ip-address', omapi.pack_ip(ip))) + msg.obj.append((b"ip-address", omapi.pack_ip(ip))) if name: - msg.obj.append((b'name', salt.utils.stringutils.to_bytes(name))) + msg.obj.append((b"name", salt.utils.stringutils.to_bytes(name))) if group: - msg.obj.append((b'group', salt.utils.stringutils.to_bytes(group))) + msg.obj.append((b"group", salt.utils.stringutils.to_bytes(group))) if supersede_host: statements += 'option host-name "{0}"; '.format(name) if ddns and name: statements += 'ddns-hostname "{0}"; '.format(name) if statements: - msg.obj.append((b'statements', salt.utils.stringutils.to_bytes(statements))) + msg.obj.append((b"statements", salt.utils.stringutils.to_bytes(statements))) response = o.query_server(msg) if response.opcode != omapi.OMAPI_OP_UPDATE: return False @@ -100,7 +103,7 @@ def add_host(mac, name=None, ip=None, ddns=False, group=None, def delete_host(mac=None, name=None): - ''' + """ Delete the host with the given mac or name. CLI Examples: @@ -109,16 +112,16 @@ def delete_host(mac=None, name=None): salt dhcp-server omapi.delete_host name=host1 salt dhcp-server omapi.delete_host mac=ab:ab:ab:ab:ab:ab - ''' + """ if not (mac or name): - raise TypeError('At least one argument is required') + raise TypeError("At least one argument is required") o = _conn() - msg = omapi.OmapiMessage.open(b'host') + msg = omapi.OmapiMessage.open(b"host") if mac: - msg.obj.append((b'hardware-address', omapi.pack_mac(mac))) - msg.obj.append((b'hardware-type', struct.pack(b'!I', 1))) + msg.obj.append((b"hardware-address", omapi.pack_mac(mac))) + msg.obj.append((b"hardware-type", struct.pack(b"!I", 1))) if name: - msg.obj.append((b'name', salt.utils.stringutils.to_bytes(name))) + msg.obj.append((b"name", salt.utils.stringutils.to_bytes(name))) response = o.query_server(msg) if response.opcode != omapi.OMAPI_OP_UPDATE: return None diff --git a/salt/modules/openbsd_sysctl.py b/salt/modules/openbsd_sysctl.py index b9409851c8b..982c5d0ca27 100644 --- a/salt/modules/openbsd_sysctl.py +++ b/salt/modules/openbsd_sysctl.py @@ -1,34 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Module for viewing and modifying OpenBSD sysctl parameters -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import os import re -# Import salt libs -from salt.ext import six import salt.utils.data import salt.utils.files import salt.utils.stringutils from salt.exceptions import CommandExecutionError +# Import salt libs +from salt.ext import six + # Define the module's virtual name -__virtualname__ = 'sysctl' +__virtualname__ = "sysctl" def __virtual__(): - ''' + """ Only run on OpenBSD systems - ''' - if __grains__['os'] == 'OpenBSD': + """ + if __grains__["os"] == "OpenBSD": return __virtualname__ - return (False, 'The openbsd_sysctl execution module cannot be loaded: ' - 'only available on OpenBSD systems.') + return ( + False, + "The openbsd_sysctl execution module cannot be loaded: " + "only available on OpenBSD systems.", + ) def show(config_file=False): - ''' + """ Return a list of sysctl parameters for this minion CLI Example: @@ -36,20 +41,20 @@ def show(config_file=False): .. code-block:: bash salt '*' sysctl.show - ''' - cmd = 'sysctl' + """ + cmd = "sysctl" ret = {} - out = __salt__['cmd.run_stdout'](cmd, output_loglevel='trace') + out = __salt__["cmd.run_stdout"](cmd, output_loglevel="trace") for line in out.splitlines(): - if not line or '=' not in line: + if not line or "=" not in line: continue - comps = line.split('=', 1) + comps = line.split("=", 1) ret[comps[0]] = comps[1] return ret def get(name): - ''' + """ Return a single sysctl parameter for this minion CLI Example: @@ -57,14 +62,14 @@ def get(name): .. code-block:: bash salt '*' sysctl.get hw.physmem - ''' - cmd = 'sysctl -n {0}'.format(name) - out = __salt__['cmd.run'](cmd) + """ + cmd = "sysctl -n {0}".format(name) + out = __salt__["cmd.run"](cmd) return out def assign(name, value): - ''' + """ Assign a single sysctl parameter for this minion CLI Example: @@ -72,25 +77,26 @@ def assign(name, value): .. code-block:: bash salt '*' sysctl.assign net.inet.ip.forwarding 1 - ''' + """ ret = {} cmd = 'sysctl {0}="{1}"'.format(name, value) - data = __salt__['cmd.run_all'](cmd) + data = __salt__["cmd.run_all"](cmd) # Certain values cannot be set from this console, at the current # securelevel or there are other restrictions that prevent us # from applying the setting rightaway. - if re.match(r'^sysctl:.*: Operation not permitted$', data['stderr']) or \ - data['retcode'] != 0: - raise CommandExecutionError('sysctl failed: {0}'.format( - data['stderr'])) - new_name, new_value = data['stdout'].split(':', 1) - ret[new_name] = new_value.split(' -> ')[-1] + if ( + re.match(r"^sysctl:.*: Operation not permitted$", data["stderr"]) + or data["retcode"] != 0 + ): + raise CommandExecutionError("sysctl failed: {0}".format(data["stderr"])) + new_name, new_value = data["stdout"].split(":", 1) + ret[new_name] = new_value.split(" -> ")[-1] return ret -def persist(name, value, config='/etc/sysctl.conf'): - ''' +def persist(name, value, config="/etc/sysctl.conf"): + """ Assign and persist a simple sysctl parameter for this minion CLI Example: @@ -98,7 +104,7 @@ def persist(name, value, config='/etc/sysctl.conf'): .. code-block:: bash salt '*' sysctl.persist net.inet.ip.forwarding 1 - ''' + """ nlines = [] edited = False value = six.text_type(value) @@ -106,36 +112,36 @@ def persist(name, value, config='/etc/sysctl.conf'): # create /etc/sysctl.conf if not present if not os.path.isfile(config): try: - with salt.utils.files.fopen(config, 'w+'): + with salt.utils.files.fopen(config, "w+"): pass except (IOError, OSError): - msg = 'Could not create {0}' + msg = "Could not create {0}" raise CommandExecutionError(msg.format(config)) - with salt.utils.files.fopen(config, 'r') as ifile: + with salt.utils.files.fopen(config, "r") as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) - if not line.startswith('{0}='.format(name)): + if not line.startswith("{0}=".format(name)): nlines.append(line) continue else: - key, rest = line.split('=', 1) + key, rest = line.split("=", 1) if rest.startswith('"'): _, rest_v, rest = rest.split('"', 2) - elif rest.startswith('\''): - _, rest_v, rest = rest.split('\'', 2) + elif rest.startswith("'"): + _, rest_v, rest = rest.split("'", 2) else: rest_v = rest.split()[0] - rest = rest[len(rest_v):] + rest = rest[len(rest_v) :] if rest_v == value: - return 'Already set' - new_line = '{0}={1}{2}'.format(key, value, rest) + return "Already set" + new_line = "{0}={1}{2}".format(key, value, rest) nlines.append(new_line) edited = True if not edited: - nlines.append('{0}={1}\n'.format(name, value)) - with salt.utils.files.fopen(config, 'wb') as ofile: + nlines.append("{0}={1}\n".format(name, value)) + with salt.utils.files.fopen(config, "wb") as ofile: ofile.writelines(salt.utils.data.encode(nlines)) assign(name, value) - return 'Updated' + return "Updated" diff --git a/salt/modules/openbsdpkg.py b/salt/modules/openbsdpkg.py index b3b6bab9120..f80c9bc62a9 100644 --- a/salt/modules/openbsdpkg.py +++ b/salt/modules/openbsdpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Package support for OpenBSD .. note:: @@ -21,13 +21,13 @@ Package support for OpenBSD - vim--no_x11 - ruby%2.3 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import copy -import re import logging +import re # Import Salt libs import salt.utils.data @@ -38,24 +38,27 @@ log = logging.getLogger(__name__) # FIXME: replace guesswork with `pkg_info -z` to correctly identify package # flavors and branches -__PKG_RE = re.compile('^((?:[^-]+|-(?![0-9]))+)-([0-9][^-]*)(?:-(.*))?$') +__PKG_RE = re.compile("^((?:[^-]+|-(?![0-9]))+)-([0-9][^-]*)(?:-(.*))?$") # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Set the virtual pkg module if the os is OpenBSD - ''' - if __grains__['os'] == 'OpenBSD': + """ + if __grains__["os"] == "OpenBSD": return __virtualname__ - return (False, 'The openbsdpkg execution module cannot be loaded: ' - 'only available on OpenBSD systems.') + return ( + False, + "The openbsdpkg execution module cannot be loaded: " + "only available on OpenBSD systems.", + ) def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed as a dict:: {'': ''} @@ -65,41 +68,42 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret ret = {} - cmd = 'pkg_info -q -a' - out = __salt__['cmd.run_stdout'](cmd, output_loglevel='trace') + cmd = "pkg_info -q -a" + out = __salt__["cmd.run_stdout"](cmd, output_loglevel="trace") for line in out.splitlines(): try: pkgname, pkgver, flavor = __PKG_RE.match(line).groups() except AttributeError: continue - pkgname += '--{0}'.format(flavor) if flavor else '' - __salt__['pkg_resource.add_pkg'](ret, pkgname, pkgver) + pkgname += "--{0}".format(flavor) if flavor else "" + __salt__["pkg_resource.add_pkg"](ret, pkgname, pkgver) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -112,18 +116,20 @@ def latest_version(*names, **kwargs): .. code-block:: bash salt '*' pkg.latest_version - ''' - kwargs.pop('refresh', True) + """ + kwargs.pop("refresh", True) pkgs = list_pkgs() ret = {} # Initialize the dict with empty strings for name in names: - ret[name] = '' + ret[name] = "" # Query the repository for the package name - cmd = 'pkg_info -Q {0}'.format(name) - out = __salt__['cmd.run_stdout'](cmd, python_shell=False, output_loglevel='trace') + cmd = "pkg_info -Q {0}".format(name) + out = __salt__["cmd.run_stdout"]( + cmd, python_shell=False, output_loglevel="trace" + ) # Since we can only query instead of request the specific package # we'll have to go through the returned list and find what we @@ -135,7 +141,7 @@ def latest_version(*names, **kwargs): except AttributeError: continue - match = re.match(r'.*\(installed\)$', pkgver) + match = re.match(r".*\(installed\)$", pkgver) if match: # Package is explicitly marked as installed already, # so skip any further comparison and move on to the @@ -145,18 +151,15 @@ def latest_version(*names, **kwargs): # First check if we need to look for flavors before # looking at unflavored packages. if "{0}--{1}".format(pkgname, flavor) == name: - pkgname += '--{0}'.format(flavor) + pkgname += "--{0}".format(flavor) elif pkgname == name: pass else: # No match just move on. continue - cur = pkgs.get(pkgname, '') - if not cur or salt.utils.versions.compare( - ver1=cur, - oper='<', - ver2=pkgver): + cur = pkgs.get(pkgname, "") + if not cur or salt.utils.versions.compare(ver1=cur, oper="<", ver2=pkgver): ret[pkgname] = pkgver # Return a string if only one package name passed @@ -166,7 +169,7 @@ def latest_version(*names, **kwargs): def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -177,12 +180,12 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def install(name=None, pkgs=None, sources=None, **kwargs): - ''' + """ Install the passed package Return a dict containing the new package names and versions:: @@ -208,9 +211,9 @@ def install(name=None, pkgs=None, sources=None, **kwargs): .. code-block:: bash salt '*' pkg.install sources='[{"": "salt://pkgs/"}]' - ''' + """ try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: @@ -224,34 +227,30 @@ def install(name=None, pkgs=None, sources=None, **kwargs): for pkg in pkg_params: # A special case for OpenBSD package "branches" is also required in # salt/states/pkg.py - if pkg_type == 'repository': - stem, branch = (pkg.split('%') + [''])[:2] - base, flavor = (stem.split('--') + [''])[:2] - pkg = '{0}--{1}%{2}'.format(base, flavor, branch) - cmd = 'pkg_add -x -I {0}'.format(pkg) - out = __salt__['cmd.run_all']( - cmd, - python_shell=False, - output_loglevel='trace' - ) - if out['retcode'] != 0 and out['stderr']: - errors.append(out['stderr']) + if pkg_type == "repository": + stem, branch = (pkg.split("%") + [""])[:2] + base, flavor = (stem.split("--") + [""])[:2] + pkg = "{0}--{1}%{2}".format(base, flavor, branch) + cmd = "pkg_add -x -I {0}".format(pkg) + out = __salt__["cmd.run_all"](cmd, python_shell=False, output_loglevel="trace") + if out["retcode"] != 0 and out["stderr"]: + errors.append(out["stderr"]) - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def remove(name=None, pkgs=None, purge=False, **kwargs): - ''' + """ Remove a single package with pkg_delete Multiple Package Options: @@ -272,10 +271,12 @@ def remove(name=None, pkgs=None, purge=False, **kwargs): salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ try: - pkg_params = [x.split('--')[0] for x in - __salt__['pkg_resource.parse_targets'](name, pkgs)[0]] + pkg_params = [ + x.split("--")[0] + for x in __salt__["pkg_resource.parse_targets"](name, pkgs)[0] + ] except MinionError as exc: raise CommandExecutionError(exc) @@ -284,38 +285,34 @@ def remove(name=None, pkgs=None, purge=False, **kwargs): if not targets: return {} - cmd = ['pkg_delete', '-Ix', '-Ddependencies'] + cmd = ["pkg_delete", "-Ix", "-Ddependencies"] if purge: - cmd.append('-cqq') + cmd.append("-cqq") cmd.extend(targets) - out = __salt__['cmd.run_all']( - cmd, - python_shell=False, - output_loglevel='trace' - ) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + out = __salt__["cmd.run_all"](cmd, python_shell=False, output_loglevel="trace") + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def purge(name=None, pkgs=None, **kwargs): - ''' + """ Remove a package and extra configuration files. name @@ -340,12 +337,12 @@ def purge(name=None, pkgs=None, **kwargs): salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' + """ return remove(name=name, pkgs=pkgs, purge=True) def upgrade_available(name): - ''' + """ Check whether or not an upgrade is available for a given package .. versionadded:: 2019.2.0 @@ -355,14 +352,12 @@ def upgrade_available(name): .. code-block:: bash salt '*' pkg.upgrade_available - ''' - return latest_version(name) != '' + """ + return latest_version(name) != "" -def upgrade(name=None, - pkgs=None, - **kwargs): - ''' +def upgrade(name=None, pkgs=None, **kwargs): + """ Run a full package upgrade (``pkg_add -u``), or upgrade a specific package if ``name`` or ``pkgs`` is provided. ``name`` is ignored when ``pkgs`` is specified. @@ -382,13 +377,13 @@ def upgrade(name=None, salt '*' pkg.upgrade salt '*' pkg.upgrade python%2.7 - ''' + """ old = list_pkgs() - cmd = ['pkg_add', '-Ix', '-u'] + cmd = ["pkg_add", "-Ix", "-u"] - if kwargs.get('noop', False): - cmd.append('-n') + if kwargs.get("noop", False): + cmd.append("-n") if pkgs: cmd.extend(pkgs) @@ -397,17 +392,16 @@ def upgrade(name=None, # Now run the upgrade, compare the list of installed packages before and # after and we have all the info we need. - result = __salt__['cmd.run_all'](cmd, output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret diff --git a/salt/modules/openbsdrcctl_service.py b/salt/modules/openbsdrcctl_service.py index 5bafc5bb3cd..83909a16c7f 100644 --- a/salt/modules/openbsdrcctl_service.py +++ b/salt/modules/openbsdrcctl_service.py @@ -1,58 +1,58 @@ # -*- coding: utf-8 -*- -''' +""" The rcctl service module for OpenBSD -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os +import salt.utils.decorators as decorators + # Import salt libs import salt.utils.path -import salt.utils.decorators as decorators from salt.exceptions import CommandNotFoundError -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" def __virtual__(): - ''' + """ rcctl(8) is only available on OpenBSD. - ''' - if __grains__['os'] == 'OpenBSD' and os.path.exists('/usr/sbin/rcctl'): + """ + if __grains__["os"] == "OpenBSD" and os.path.exists("/usr/sbin/rcctl"): return __virtualname__ - return (False, 'The openbsdpkg execution module cannot be loaded: ' - 'only available on OpenBSD systems.') + return ( + False, + "The openbsdpkg execution module cannot be loaded: " + "only available on OpenBSD systems.", + ) @decorators.memoize def _cmd(): - ''' + """ Return the full path to the rcctl(8) command. - ''' - rcctl = salt.utils.path.which('rcctl') + """ + rcctl = salt.utils.path.which("rcctl") if not rcctl: raise CommandNotFoundError return rcctl def _get_flags(**kwargs): - ''' + """ Return the configured service flags. - ''' - flags = kwargs.get('flags', - __salt__['config.option']('service.flags', - default='')) + """ + flags = kwargs.get("flags", __salt__["config.option"]("service.flags", default="")) return flags def available(name): - ''' + """ Return True if the named service is available. CLI Example: @@ -60,15 +60,15 @@ def available(name): .. code-block:: bash salt '*' service.available sshd - ''' - cmd = '{0} get {1}'.format(_cmd(), name) - if __salt__['cmd.retcode'](cmd) == 2: + """ + cmd = "{0} get {1}".format(_cmd(), name) + if __salt__["cmd.retcode"](cmd) == 2: return False return True def missing(name): - ''' + """ The inverse of service.available. Return True if the named service is not available. @@ -77,12 +77,12 @@ def missing(name): .. code-block:: bash salt '*' service.missing sshd - ''' + """ return not available(name) def get_all(): - ''' + """ Return all installed services. CLI Example: @@ -90,16 +90,16 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' + """ ret = [] service = _cmd() - for svc in __salt__['cmd.run']('{0} ls all'.format(service)).splitlines(): + for svc in __salt__["cmd.run"]("{0} ls all".format(service)).splitlines(): ret.append(svc) return sorted(ret) def get_disabled(): - ''' + """ Return what services are available but not enabled to start at boot. CLI Example: @@ -107,16 +107,16 @@ def get_disabled(): .. code-block:: bash salt '*' service.get_disabled - ''' + """ ret = [] service = _cmd() - for svc in __salt__['cmd.run']('{0} ls off'.format(service)).splitlines(): + for svc in __salt__["cmd.run"]("{0} ls off".format(service)).splitlines(): ret.append(svc) return sorted(ret) def get_enabled(): - ''' + """ Return what services are set to run on boot. CLI Example: @@ -124,16 +124,16 @@ def get_enabled(): .. code-block:: bash salt '*' service.get_enabled - ''' + """ ret = [] service = _cmd() - for svc in __salt__['cmd.run']('{0} ls on'.format(service)).splitlines(): + for svc in __salt__["cmd.run"]("{0} ls on".format(service)).splitlines(): ret.append(svc) return sorted(ret) def start(name): - ''' + """ Start the named service. CLI Example: @@ -141,13 +141,13 @@ def start(name): .. code-block:: bash salt '*' service.start - ''' - cmd = '{0} -f start {1}'.format(_cmd(), name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "{0} -f start {1}".format(_cmd(), name) + return not __salt__["cmd.retcode"](cmd) def stop(name): - ''' + """ Stop the named service. CLI Example: @@ -155,13 +155,13 @@ def stop(name): .. code-block:: bash salt '*' service.stop - ''' - cmd = '{0} stop {1}'.format(_cmd(), name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "{0} stop {1}".format(_cmd(), name) + return not __salt__["cmd.retcode"](cmd) def restart(name): - ''' + """ Restart the named service. CLI Example: @@ -169,13 +169,13 @@ def restart(name): .. code-block:: bash salt '*' service.restart - ''' - cmd = '{0} -f restart {1}'.format(_cmd(), name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "{0} -f restart {1}".format(_cmd(), name) + return not __salt__["cmd.retcode"](cmd) def reload_(name): - ''' + """ Reload the named service. CLI Example: @@ -183,13 +183,13 @@ def reload_(name): .. code-block:: bash salt '*' service.reload - ''' - cmd = '{0} reload {1}'.format(_cmd(), name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "{0} reload {1}".format(_cmd(), name) + return not __salt__["cmd.retcode"](cmd) def status(name, sig=None): - ''' + """ Return the status for a service, returns a bool whether the service is running. @@ -198,16 +198,16 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status - ''' + """ if sig: - return bool(__salt__['status.pid'](sig)) + return bool(__salt__["status.pid"](sig)) - cmd = '{0} check {1}'.format(_cmd(), name) - return not __salt__['cmd.retcode'](cmd) + cmd = "{0} check {1}".format(_cmd(), name) + return not __salt__["cmd.retcode"](cmd) def enable(name, **kwargs): - ''' + """ Enable the named service to start at boot. flags : None @@ -221,22 +221,22 @@ def enable(name, **kwargs): salt '*' service.enable salt '*' service.enable flags= - ''' - stat_cmd = '{0} set {1} status on'.format(_cmd(), name) - stat_retcode = __salt__['cmd.retcode'](stat_cmd) + """ + stat_cmd = "{0} set {1} status on".format(_cmd(), name) + stat_retcode = __salt__["cmd.retcode"](stat_cmd) flag_retcode = None # only (re)set flags for services that have an rc.d(8) script - if os.path.exists('/etc/rc.d/{0}'.format(name)): + if os.path.exists("/etc/rc.d/{0}".format(name)): flags = _get_flags(**kwargs) - flag_cmd = '{0} set {1} flags {2}'.format(_cmd(), name, flags) - flag_retcode = __salt__['cmd.retcode'](flag_cmd) + flag_cmd = "{0} set {1} flags {2}".format(_cmd(), name, flags) + flag_retcode = __salt__["cmd.retcode"](flag_cmd) return not any([stat_retcode, flag_retcode]) def disable(name, **kwargs): - ''' + """ Disable the named service to not start at boot. CLI Example: @@ -244,13 +244,13 @@ def disable(name, **kwargs): .. code-block:: bash salt '*' service.disable - ''' - cmd = '{0} set {1} status off'.format(_cmd(), name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "{0} set {1} status off".format(_cmd(), name) + return not __salt__["cmd.retcode"](cmd) def disabled(name): - ''' + """ Return True if the named service is disabled at boot, False otherwise. CLI Example: @@ -258,13 +258,13 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled - ''' - cmd = '{0} get {1} status'.format(_cmd(), name) - return not __salt__['cmd.retcode'](cmd) == 0 + """ + cmd = "{0} get {1} status".format(_cmd(), name) + return not __salt__["cmd.retcode"](cmd) == 0 def enabled(name, **kwargs): - ''' + """ Return True if the named service is enabled at boot and the provided flags match the configured ones (if any). Return False otherwise. @@ -277,17 +277,19 @@ def enabled(name, **kwargs): salt '*' service.enabled salt '*' service.enabled flags= - ''' - cmd = '{0} get {1} status'.format(_cmd(), name) - if not __salt__['cmd.retcode'](cmd): + """ + cmd = "{0} get {1} status".format(_cmd(), name) + if not __salt__["cmd.retcode"](cmd): # also consider a service disabled if the current flags are different # than the configured ones so we have a chance to update them flags = _get_flags(**kwargs) - cur_flags = __salt__['cmd.run_stdout']('{0} get {1} flags'.format(_cmd(), name)) + cur_flags = __salt__["cmd.run_stdout"]("{0} get {1} flags".format(_cmd(), name)) if format(flags) == format(cur_flags): return True if not flags: - def_flags = __salt__['cmd.run_stdout']('{0} getdef {1} flags'.format(_cmd(), name)) + def_flags = __salt__["cmd.run_stdout"]( + "{0} getdef {1} flags".format(_cmd(), name) + ) if format(cur_flags) == format(def_flags): return True diff --git a/salt/modules/openbsdservice.py b/salt/modules/openbsdservice.py index 3c88fcdb206..49bf938c10a 100644 --- a/salt/modules/openbsdservice.py +++ b/salt/modules/openbsdservice.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The service module for OpenBSD .. important:: @@ -7,52 +7,55 @@ The service module for OpenBSD minion, and it is using a different module (or gives an error similar to *'service.start' is not available*), see :ref:`here `. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import fnmatch import logging - -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import map # pylint: disable=import-error,redefined-builtin +import os +import re # Import Salt libs import salt.utils.data import salt.utils.files +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import map # pylint: disable=import-error,redefined-builtin + log = logging.getLogger(__name__) # XXX enable/disable support would be nice # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} def __virtual__(): - ''' + """ Only work on OpenBSD - ''' - if __grains__['os'] == 'OpenBSD' and os.path.exists('/etc/rc.d/rc.subr'): - krel = list(list(map(int, __grains__['kernelrelease'].split('.')))) + """ + if __grains__["os"] == "OpenBSD" and os.path.exists("/etc/rc.d/rc.subr"): + krel = list(list(map(int, __grains__["kernelrelease"].split(".")))) # The -f flag, used to force a script to run even if disabled, # was added after the 5.0 release. # the rcctl(8) command is the preferred way to manage services. if krel[0] > 5 or (krel[0] == 5 and krel[1] > 0): - if not os.path.exists('/usr/sbin/rcctl'): + if not os.path.exists("/usr/sbin/rcctl"): return __virtualname__ - return (False, 'The openbsdservice execution module cannot be loaded: ' - 'only available on OpenBSD systems.') + return ( + False, + "The openbsdservice execution module cannot be loaded: " + "only available on OpenBSD systems.", + ) def start(name): - ''' + """ Start the specified service CLI Example: @@ -60,13 +63,13 @@ def start(name): .. code-block:: bash salt '*' service.start - ''' - cmd = '/etc/rc.d/{0} -f start'.format(name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "/etc/rc.d/{0} -f start".format(name) + return not __salt__["cmd.retcode"](cmd) def stop(name): - ''' + """ Stop the specified service CLI Example: @@ -74,13 +77,13 @@ def stop(name): .. code-block:: bash salt '*' service.stop - ''' - cmd = '/etc/rc.d/{0} -f stop'.format(name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "/etc/rc.d/{0} -f stop".format(name) + return not __salt__["cmd.retcode"](cmd) def restart(name): - ''' + """ Restart the named service CLI Example: @@ -88,13 +91,13 @@ def restart(name): .. code-block:: bash salt '*' service.restart - ''' - cmd = '/etc/rc.d/{0} -f restart'.format(name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "/etc/rc.d/{0} -f restart".format(name) + return not __salt__["cmd.retcode"](cmd) def status(name, sig=None): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -115,26 +118,26 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status [service signature] - ''' + """ if sig: - return bool(__salt__['status.pid'](sig)) + return bool(__salt__["status.pid"](sig)) - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: services = [name] results = {} for service in services: - cmd = '/etc/rc.d/{0} -f check'.format(service) - results[service] = not __salt__['cmd.retcode'](cmd, ignore_retcode=True) + cmd = "/etc/rc.d/{0} -f check".format(service) + results[service] = not __salt__["cmd.retcode"](cmd, ignore_retcode=True) if contains_globbing: return results return results[name] def reload_(name): - ''' + """ .. versionadded:: 2014.7.0 Reload the named service @@ -144,42 +147,41 @@ def reload_(name): .. code-block:: bash salt '*' service.reload - ''' - cmd = '/etc/rc.d/{0} -f reload'.format(name) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "/etc/rc.d/{0} -f reload".format(name) + return not __salt__["cmd.retcode"](cmd) -import re -service_flags_regex = re.compile(r'^\s*(\w[\d\w]*)_flags=(?:(NO)|.*)$') -pkg_scripts_regex = re.compile(r'^\s*pkg_scripts=\'(.*)\'$') -start_daemon_call_regex = re.compile(r'(\s*start_daemon(?!\(\)))') -start_daemon_parameter_regex = re.compile(r'(?:\s+(\w[\w\d]*))') +service_flags_regex = re.compile(r"^\s*(\w[\d\w]*)_flags=(?:(NO)|.*)$") +pkg_scripts_regex = re.compile(r"^\s*pkg_scripts=\'(.*)\'$") +start_daemon_call_regex = re.compile(r"(\s*start_daemon(?!\(\)))") +start_daemon_parameter_regex = re.compile(r"(?:\s+(\w[\w\d]*))") def _get_rc(): - ''' + """ Returns a dict where the key is the daemon's name and the value a boolean indicating its status (True: enabled or False: disabled). Check the daemons started by the system in /etc/rc and configured in /etc/rc.conf and /etc/rc.conf.local. Also add to the dict all the localy enabled daemons via $pkg_scripts. - ''' + """ daemons_flags = {} try: # now read the system startup script /etc/rc # to know what are the system enabled daemons - with salt.utils.files.fopen('/etc/rc', 'r') as handle: + with salt.utils.files.fopen("/etc/rc", "r") as handle: lines = salt.utils.data.decode(handle.readlines()) except IOError: - log.error('Unable to read /etc/rc') + log.error("Unable to read /etc/rc") else: for line in lines: match = start_daemon_call_regex.match(line) if match: # the matched line is a call to start_daemon() # we remove the function name - line = line[len(match.group(1)):] + line = line[len(match.group(1)) :] # we retrieve each daemon name from the parameters of start_daemon() for daemon in start_daemon_parameter_regex.findall(line): # mark it as enabled @@ -187,17 +189,19 @@ def _get_rc(): # this will execute rc.conf and rc.conf.local # used in /etc/rc at boot to start the daemons - variables = __salt__['cmd.run']('(. /etc/rc.conf && set)', - clean_env=True, - output_loglevel='quiet', - python_shell=True).split('\n') + variables = __salt__["cmd.run"]( + "(. /etc/rc.conf && set)", + clean_env=True, + output_loglevel="quiet", + python_shell=True, + ).split("\n") for var in variables: match = service_flags_regex.match(var) if match: # the matched var look like daemon_name_flags=, we test its assigned value # NO: disabled, everything else: enabled # do not create a new key if the service hasn't been found in /etc/rc, see $pkg_scripts - if match.group(2) == 'NO': + if match.group(2) == "NO": daemons_flags[match.group(1)] = False else: match = pkg_scripts_regex.match(var) @@ -212,7 +216,7 @@ def _get_rc(): def available(name): - ''' + """ .. versionadded:: 2014.7.0 Returns ``True`` if the specified service is available, otherwise returns @@ -223,13 +227,13 @@ def available(name): .. code-block:: bash salt '*' service.available sshd - ''' - path = '/etc/rc.d/{0}'.format(name) + """ + path = "/etc/rc.d/{0}".format(name) return os.path.isfile(path) and os.access(path, os.X_OK) def missing(name): - ''' + """ .. versionadded:: 2014.7.0 The inverse of service.available. @@ -241,12 +245,12 @@ def missing(name): .. code-block:: bash salt '*' service.missing sshd - ''' + """ return not available(name) def get_all(): - ''' + """ .. versionadded:: 2014.7.0 Return all available boot services @@ -256,11 +260,11 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' + """ services = [] - if not os.path.isdir('/etc/rc.d'): + if not os.path.isdir("/etc/rc.d"): return services - for service in os.listdir('/etc/rc.d'): + for service in os.listdir("/etc/rc.d"): # this will remove rc.subr and all non executable files if available(service): services.append(service) @@ -268,7 +272,7 @@ def get_all(): def get_enabled(): - ''' + """ .. versionadded:: 2014.7.0 Return a list of service that are enabled on boot @@ -278,7 +282,7 @@ def get_enabled(): .. code-block:: bash salt '*' service.get_enabled - ''' + """ services = [] for daemon, is_enabled in six.iteritems(_get_rc()): if is_enabled: @@ -287,7 +291,7 @@ def get_enabled(): def enabled(name, **kwargs): - ''' + """ .. versionadded:: 2014.7.0 Return True if the named service is enabled, false otherwise @@ -297,12 +301,12 @@ def enabled(name, **kwargs): .. code-block:: bash salt '*' service.enabled - ''' + """ return name in get_enabled() def get_disabled(): - ''' + """ .. versionadded:: 2014.7.0 Return a set of services that are installed but disabled @@ -312,7 +316,7 @@ def get_disabled(): .. code-block:: bash salt '*' service.get_disabled - ''' + """ services = [] for daemon, is_enabled in six.iteritems(_get_rc()): if not is_enabled: @@ -321,7 +325,7 @@ def get_disabled(): def disabled(name): - ''' + """ .. versionadded:: 2014.7.0 Return True if the named service is disabled, false otherwise @@ -331,5 +335,5 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled - ''' + """ return name in get_disabled() diff --git a/salt/modules/openscap.py b/salt/modules/openscap.py index 56ced24e4a7..6f8ff4a76db 100644 --- a/salt/modules/openscap.py +++ b/salt/modules/openscap.py @@ -1,24 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" Module for OpenSCAP Management -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import tempfile + import shlex import shutil -from subprocess import Popen, PIPE +import tempfile +from subprocess import PIPE, Popen # Import Salt libs from salt.ext import six - ArgumentParser = object try: import argparse # pylint: disable=minimum-python-version + ArgumentParser = argparse.ArgumentParser HAS_ARGPARSE = True except ImportError: # python 2.6 @@ -26,30 +27,27 @@ except ImportError: # python 2.6 _XCCDF_MAP = { - 'eval': { - 'parser_arguments': [ - (('--profile',), {'required': True}), - ], - 'cmd_pattern': ( + "eval": { + "parser_arguments": [(("--profile",), {"required": True})], + "cmd_pattern": ( "oscap xccdf eval " "--oval-results --results results.xml --report report.html " "--profile {0} {1}" - ) + ), } } def __virtual__(): - return HAS_ARGPARSE, 'argparse module is required.' + return HAS_ARGPARSE, "argparse module is required." class _ArgumentParser(ArgumentParser): - def __init__(self, action=None, *args, **kwargs): - super(_ArgumentParser, self).__init__(*args, prog='oscap', **kwargs) - self.add_argument('action', choices=['eval']) + super(_ArgumentParser, self).__init__(*args, prog="oscap", **kwargs) + self.add_argument("action", choices=["eval"]) add_arg = None - for params, kwparams in _XCCDF_MAP['eval']['parser_arguments']: + for params, kwparams in _XCCDF_MAP["eval"]["parser_arguments"]: self.add_argument(*params, **kwparams) def error(self, message, *args, **kwargs): @@ -59,12 +57,12 @@ class _ArgumentParser(ArgumentParser): _OSCAP_EXIT_CODES_MAP = { 0: True, # all rules pass 1: False, # there is an error during evaluation - 2: True # there is at least one rule with either fail or unknown result + 2: True, # there is at least one rule with either fail or unknown result } def xccdf(params): - ''' + """ Run ``oscap xccdf`` commands on minions. It uses cp.push_dir to upload the generated files to the salt master in the master's minion files cachedir @@ -77,7 +75,7 @@ def xccdf(params): .. code-block:: bash salt '*' openscap.xccdf "eval --profile Default /usr/share/openscap/scap-yast2sec-xccdf.xml" - ''' + """ params = shlex.split(params) policy = params[-1] @@ -96,20 +94,17 @@ def xccdf(params): error = six.text_type(err) if success: - cmd = _XCCDF_MAP[action]['cmd_pattern'].format(args.profile, policy) + cmd = _XCCDF_MAP[action]["cmd_pattern"].format(args.profile, policy) tempdir = tempfile.mkdtemp() - proc = Popen( - shlex.split(cmd), stdout=PIPE, stderr=PIPE, cwd=tempdir) + proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE, cwd=tempdir) (stdoutdata, error) = proc.communicate() success = _OSCAP_EXIT_CODES_MAP[proc.returncode] returncode = proc.returncode if success: - __salt__['cp.push_dir'](tempdir) + __salt__["cp.push_dir"](tempdir) shutil.rmtree(tempdir, ignore_errors=True) upload_dir = tempdir return dict( - success=success, - upload_dir=upload_dir, - error=error, - returncode=returncode) + success=success, upload_dir=upload_dir, error=error, returncode=returncode + ) diff --git a/salt/modules/openstack_config.py b/salt/modules/openstack_config.py index e7ba06d53b5..96df508fc2b 100644 --- a/salt/modules/openstack_config.py +++ b/salt/modules/openstack_config.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Modify, retrieve, or delete values from OpenStack configuration files. :maintainer: Jeffrey C. Ollie @@ -7,38 +7,40 @@ Modify, retrieve, or delete values from OpenStack configuration files. :depends: :platform: linux -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import shlex +import salt.exceptions +import salt.utils.decorators.path + +# Import Salt libs +from salt.ext import six + try: import pipes + HAS_DEPS = True except ImportError: HAS_DEPS = False -if hasattr(shlex, 'quote'): +if hasattr(shlex, "quote"): _quote = shlex.quote -elif HAS_DEPS and hasattr(pipes, 'quote'): +elif HAS_DEPS and hasattr(pipes, "quote"): _quote = pipes.quote else: _quote = None -# Import Salt libs -from salt.ext import six -import salt.utils.decorators.path -import salt.exceptions # Don't shadow built-in's. -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): if _quote is None and not HAS_DEPS: - return False + return (False, "Missing dependencies") return True @@ -46,9 +48,9 @@ def _fallback(*args, **kw): return 'The "openstack-config" command needs to be installed for this function to work. Typically this is included in the "openstack-utils" package.' -@salt.utils.decorators.path.which('openstack-config') +@salt.utils.decorators.path.which("openstack-config") def set_(filename, section, parameter, value): - ''' + """ Set a value in an OpenStack configuration file. filename @@ -68,29 +70,29 @@ def set_(filename, section, parameter, value): .. code-block:: bash salt-call openstack_config.set /etc/keystone/keystone.conf sql connection foo - ''' + """ filename = _quote(filename) section = _quote(section) parameter = _quote(parameter) value = _quote(six.text_type(value)) - result = __salt__['cmd.run_all']( - 'openstack-config --set {0} {1} {2} {3}'.format( - filename, section, parameter, value - ), - python_shell=False, - ) + result = __salt__["cmd.run_all"]( + "openstack-config --set {0} {1} {2} {3}".format( + filename, section, parameter, value + ), + python_shell=False, + ) - if result['retcode'] == 0: - return result['stdout'] + if result["retcode"] == 0: + return result["stdout"] else: - raise salt.exceptions.CommandExecutionError(result['stderr']) + raise salt.exceptions.CommandExecutionError(result["stderr"]) -@salt.utils.decorators.path.which('openstack-config') +@salt.utils.decorators.path.which("openstack-config") def get(filename, section, parameter): - ''' + """ Get a value from an OpenStack configuration file. filename @@ -108,28 +110,26 @@ def get(filename, section, parameter): salt-call openstack_config.get /etc/keystone/keystone.conf sql connection - ''' + """ filename = _quote(filename) section = _quote(section) parameter = _quote(parameter) - result = __salt__['cmd.run_all']( - 'openstack-config --get {0} {1} {2}'.format( - filename, section, parameter - ), - python_shell=False, - ) + result = __salt__["cmd.run_all"]( + "openstack-config --get {0} {1} {2}".format(filename, section, parameter), + python_shell=False, + ) - if result['retcode'] == 0: - return result['stdout'] + if result["retcode"] == 0: + return result["stdout"] else: - raise salt.exceptions.CommandExecutionError(result['stderr']) + raise salt.exceptions.CommandExecutionError(result["stderr"]) -@salt.utils.decorators.path.which('openstack-config') +@salt.utils.decorators.path.which("openstack-config") def delete(filename, section, parameter): - ''' + """ Delete a value from an OpenStack configuration file. filename @@ -146,20 +146,18 @@ def delete(filename, section, parameter): .. code-block:: bash salt-call openstack_config.delete /etc/keystone/keystone.conf sql connection - ''' + """ filename = _quote(filename) section = _quote(section) parameter = _quote(parameter) - result = __salt__['cmd.run_all']( - 'openstack-config --del {0} {1} {2}'.format( - filename, section, parameter - ), - python_shell=False, - ) + result = __salt__["cmd.run_all"]( + "openstack-config --del {0} {1} {2}".format(filename, section, parameter), + python_shell=False, + ) - if result['retcode'] == 0: - return result['stdout'] + if result["retcode"] == 0: + return result["stdout"] else: - raise salt.exceptions.CommandExecutionError(result['stderr']) + raise salt.exceptions.CommandExecutionError(result["stderr"]) diff --git a/salt/modules/openstack_mng.py b/salt/modules/openstack_mng.py index ca7fb6838e0..07f0a292564 100644 --- a/salt/modules/openstack_mng.py +++ b/salt/modules/openstack_mng.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Module for OpenStack Management :codeauthor: Konrad Mosoń :maturity: new :depends: openstack-utils :platform: linux -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os.path @@ -19,21 +20,21 @@ import salt.utils.stringutils log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'openstack_mng' +__virtualname__ = "openstack_mng" def __virtual__(): - ''' + """ Only load this module if openstack-service is installed - ''' - if os.path.isfile('/usr/bin/openstack-service'): + """ + if os.path.isfile("/usr/bin/openstack-service"): return __virtualname__ else: - return (False, 'The openstack-service binary could not be found.') + return (False, "The openstack-service binary could not be found.") def start_service(service_name): - ''' + """ Start OpenStack service immediately CLI Example: @@ -41,14 +42,14 @@ def start_service(service_name): .. code-block:: bash salt '*' openstack_mng.start_service neutron - ''' + """ - os_cmd = ['/usr/bin/openstack-service', 'start', service_name] - return __salt__['cmd.retcode'](os_cmd) == 0 + os_cmd = ["/usr/bin/openstack-service", "start", service_name] + return __salt__["cmd.retcode"](os_cmd) == 0 def stop_service(service_name): - ''' + """ Stop OpenStack service immediately CLI Example: @@ -56,14 +57,14 @@ def stop_service(service_name): .. code-block:: bash salt '*' openstack_mng.stop_service neutron - ''' + """ - os_cmd = ['/usr/bin/openstack-service', 'stop', service_name] - return __salt__['cmd.retcode'](os_cmd) == 0 + os_cmd = ["/usr/bin/openstack-service", "stop", service_name] + return __salt__["cmd.retcode"](os_cmd) == 0 def restart_service(service_name, minimum_running_time=None): - ''' + """ Restart OpenStack service immediately, or only if it's running longer than specified value @@ -73,32 +74,35 @@ def restart_service(service_name, minimum_running_time=None): salt '*' openstack_mng.restart_service neutron salt '*' openstack_mng.restart_service neutron minimum_running_time=600 - ''' + """ if minimum_running_time: ret_code = False # get system services list for interesting openstack service - services = __salt__['cmd.run'](['/usr/bin/openstack-service', 'list', service_name]).split('\n') + services = __salt__["cmd.run"]( + ["/usr/bin/openstack-service", "list", service_name] + ).split("\n") for service in services: - service_info = __salt__['service.show'](service) - with salt.utils.files.fopen('/proc/uptime') as rfh: + service_info = __salt__["service.show"](service) + with salt.utils.files.fopen("/proc/uptime") as rfh: boot_time = float( - salt.utils.stringutils.to_unicode( - rfh.read() - ).split(' ')[0] + salt.utils.stringutils.to_unicode(rfh.read()).split(" ")[0] ) - expr_time = int(service_info.get('ExecMainStartTimestampMonotonic', 0)) / 1000000 < boot_time - minimum_running_time - expr_active = service_info.get('ActiveState') == "active" + expr_time = ( + int(service_info.get("ExecMainStartTimestampMonotonic", 0)) / 1000000 + < boot_time - minimum_running_time + ) + expr_active = service_info.get("ActiveState") == "active" if expr_time or not expr_active: # restart specific system service - ret = __salt__['service.restart'](service) + ret = __salt__["service.restart"](service) if ret: ret_code = True return ret_code else: # just restart - os_cmd = ['/usr/bin/openstack-service', 'restart', service_name] - return __salt__['cmd.retcode'](os_cmd) == 0 + os_cmd = ["/usr/bin/openstack-service", "restart", service_name] + return __salt__["cmd.retcode"](os_cmd) == 0 diff --git a/salt/modules/openvswitch.py b/salt/modules/openvswitch.py index e4f69542a9e..5457a143230 100644 --- a/salt/modules/openvswitch.py +++ b/salt/modules/openvswitch.py @@ -1,34 +1,35 @@ # -*- coding: utf-8 -*- -''' +""" Support for Open vSwitch - module with basic Open vSwitch commands. Suitable for setting up Openstack Neutron. :codeauthor: Jiri Kotlin -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging +import salt.utils.path + # Import salt libs from salt.ext import six -import salt.utils.path log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if Open vSwitch is installed - ''' - if salt.utils.path.which('ovs-vsctl'): - return 'openvswitch' - return False + """ + if salt.utils.path.which("ovs-vsctl"): + return "openvswitch" + return (False, "Missing dependency: ovs-vsctl") def _param_may_exist(may_exist): - ''' + """ Returns --may-exist parameter for Open vSwitch command. Args: @@ -36,15 +37,15 @@ def _param_may_exist(may_exist): Returns: String '--may-exist ' or empty string. - ''' + """ if may_exist: - return '--may-exist ' + return "--may-exist " else: - return '' + return "" def _param_if_exists(if_exists): - ''' + """ Returns --if-exist parameter for Open vSwitch command. Args: @@ -52,15 +53,15 @@ def _param_if_exists(if_exists): Returns: String '--if-exist ' or empty string. - ''' + """ if if_exists: - return '--if-exists ' + return "--if-exists " else: - return '' + return "" def _retcode_to_bool(retcode): - ''' + """ Evaulates Open vSwitch command`s retcode value. Args: @@ -68,15 +69,15 @@ def _retcode_to_bool(retcode): Returns: True on 0, else False - ''' + """ if retcode == 0: return True else: return False -def _stdout_list_split(retcode, stdout='', splitstring='\n'): - ''' +def _stdout_list_split(retcode, stdout="", splitstring="\n"): + """ Evaulates Open vSwitch command`s retcode value. Args: @@ -86,7 +87,7 @@ def _stdout_list_split(retcode, stdout='', splitstring='\n'): Returns: List or False. - ''' + """ if retcode == 0: ret = stdout.split(splitstring) return ret @@ -95,7 +96,7 @@ def _stdout_list_split(retcode, stdout='', splitstring='\n'): def bridge_list(): - ''' + """ Lists all existing real and fake bridges. Returns: @@ -107,16 +108,16 @@ def bridge_list(): .. code-block:: bash salt '*' openvswitch.bridge_list - ''' - cmd = 'ovs-vsctl list-br' - result = __salt__['cmd.run_all'](cmd) - retcode = result['retcode'] - stdout = result['stdout'] + """ + cmd = "ovs-vsctl list-br" + result = __salt__["cmd.run_all"](cmd) + retcode = result["retcode"] + stdout = result["stdout"] return _stdout_list_split(retcode, stdout) def bridge_exists(br): - ''' + """ Tests whether bridge exists as a real or fake bridge. Returns: @@ -128,15 +129,15 @@ def bridge_exists(br): .. code-block:: bash salt '*' openvswitch.bridge_exists br0 - ''' - cmd = 'ovs-vsctl br-exists {0}'.format(br) - result = __salt__['cmd.run_all'](cmd) - retcode = result['retcode'] + """ + cmd = "ovs-vsctl br-exists {0}".format(br) + result = __salt__["cmd.run_all"](cmd) + retcode = result["retcode"] return _retcode_to_bool(retcode) def bridge_create(br, may_exist=True): - ''' + """ Creates a new bridge. Args: @@ -152,15 +153,15 @@ def bridge_create(br, may_exist=True): .. code-block:: bash salt '*' openvswitch.bridge_create br0 - ''' + """ param_may_exist = _param_may_exist(may_exist) - cmd = 'ovs-vsctl {1}add-br {0}'.format(br, param_may_exist) - result = __salt__['cmd.run_all'](cmd) - return _retcode_to_bool(result['retcode']) + cmd = "ovs-vsctl {1}add-br {0}".format(br, param_may_exist) + result = __salt__["cmd.run_all"](cmd) + return _retcode_to_bool(result["retcode"]) def bridge_delete(br, if_exists=True): - ''' + """ Deletes bridge and all of its ports. Args: @@ -176,16 +177,16 @@ def bridge_delete(br, if_exists=True): .. code-block:: bash salt '*' openvswitch.bridge_delete br0 - ''' + """ param_if_exists = _param_if_exists(if_exists) - cmd = 'ovs-vsctl {1}del-br {0}'.format(br, param_if_exists) - result = __salt__['cmd.run_all'](cmd) - retcode = result['retcode'] + cmd = "ovs-vsctl {1}del-br {0}".format(br, param_if_exists) + result = __salt__["cmd.run_all"](cmd) + retcode = result["retcode"] return _retcode_to_bool(retcode) def port_add(br, port, may_exist=False, internal=False): - ''' + """ Creates on bridge a new port named port. Returns: @@ -203,18 +204,18 @@ def port_add(br, port, may_exist=False, internal=False): .. code-block:: bash salt '*' openvswitch.port_add br0 8080 - ''' + """ param_may_exist = _param_may_exist(may_exist) - cmd = 'ovs-vsctl {2}add-port {0} {1}'.format(br, port, param_may_exist) + cmd = "ovs-vsctl {2}add-port {0} {1}".format(br, port, param_may_exist) if internal: - cmd += ' -- set interface {0} type=internal'.format(port) - result = __salt__['cmd.run_all'](cmd) - retcode = result['retcode'] + cmd += " -- set interface {0} type=internal".format(port) + result = __salt__["cmd.run_all"](cmd) + retcode = result["retcode"] return _retcode_to_bool(retcode) def port_remove(br, port, if_exists=True): - ''' + """ Deletes port. Args: @@ -231,20 +232,20 @@ def port_remove(br, port, if_exists=True): .. code-block:: bash salt '*' openvswitch.port_remove br0 8080 - ''' + """ param_if_exists = _param_if_exists(if_exists) if port and not br: - cmd = 'ovs-vsctl {1}del-port {0}'.format(port, param_if_exists) + cmd = "ovs-vsctl {1}del-port {0}".format(port, param_if_exists) else: - cmd = 'ovs-vsctl {2}del-port {0} {1}'.format(br, port, param_if_exists) - result = __salt__['cmd.run_all'](cmd) - retcode = result['retcode'] + cmd = "ovs-vsctl {2}del-port {0} {1}".format(br, port, param_if_exists) + result = __salt__["cmd.run_all"](cmd) + retcode = result["retcode"] return _retcode_to_bool(retcode) def port_list(br): - ''' + """ Lists all of the ports within bridge. Args: @@ -259,16 +260,16 @@ def port_list(br): .. code-block:: bash salt '*' openvswitch.port_list br0 - ''' - cmd = 'ovs-vsctl list-ports {0}'.format(br) - result = __salt__['cmd.run_all'](cmd) - retcode = result['retcode'] - stdout = result['stdout'] + """ + cmd = "ovs-vsctl list-ports {0}".format(br) + result = __salt__["cmd.run_all"](cmd) + retcode = result["retcode"] + stdout = result["stdout"] return _stdout_list_split(retcode, stdout) def port_get_tag(port): - ''' + """ Lists tags of the port. Args: @@ -283,16 +284,16 @@ def port_get_tag(port): .. code-block:: bash salt '*' openvswitch.port_get_tag tap0 - ''' - cmd = 'ovs-vsctl get port {0} tag'.format(port) - result = __salt__['cmd.run_all'](cmd) - retcode = result['retcode'] - stdout = result['stdout'] + """ + cmd = "ovs-vsctl get port {0} tag".format(port) + result = __salt__["cmd.run_all"](cmd) + retcode = result["retcode"] + stdout = result["stdout"] return _stdout_list_split(retcode, stdout) def interface_get_options(port): - ''' + """ Port's interface's optional parameters. Args: @@ -307,16 +308,16 @@ def interface_get_options(port): .. code-block:: bash salt '*' openvswitch.interface_get_options tap0 - ''' - cmd = 'ovs-vsctl get interface {0} options'.format(port) - result = __salt__['cmd.run_all'](cmd) - retcode = result['retcode'] - stdout = result['stdout'] + """ + cmd = "ovs-vsctl get interface {0} options".format(port) + result = __salt__["cmd.run_all"](cmd) + retcode = result["retcode"] + stdout = result["stdout"] return _stdout_list_split(retcode, stdout) def interface_get_type(port): - ''' + """ Type of port's interface. Args: @@ -331,16 +332,16 @@ def interface_get_type(port): .. code-block:: bash salt '*' openvswitch.interface_get_type tap0 - ''' - cmd = 'ovs-vsctl get interface {0} type'.format(port) - result = __salt__['cmd.run_all'](cmd) - retcode = result['retcode'] - stdout = result['stdout'] + """ + cmd = "ovs-vsctl get interface {0} type".format(port) + result = __salt__["cmd.run_all"](cmd) + retcode = result["retcode"] + stdout = result["stdout"] return _stdout_list_split(retcode, stdout) def port_create_vlan(br, port, id, internal=False): - ''' + """ Isolate VM traffic using VLANs. Args: @@ -358,8 +359,8 @@ def port_create_vlan(br, port, id, internal=False): .. code-block:: bash salt '*' openvswitch.port_create_vlan br0 tap0 100 - ''' - interfaces = __salt__['network.interfaces']() + """ + interfaces = __salt__["network.interfaces"]() if not 0 <= id <= 4095: return False elif not bridge_exists(br): @@ -367,21 +368,21 @@ def port_create_vlan(br, port, id, internal=False): elif not internal and port not in interfaces: return False elif port in port_list(br): - cmd = 'ovs-vsctl set port {0} tag={1}'.format(port, id) + cmd = "ovs-vsctl set port {0} tag={1}".format(port, id) if internal: - cmd += ' -- set interface {0} type=internal'.format(port) - result = __salt__['cmd.run_all'](cmd) - return _retcode_to_bool(result['retcode']) + cmd += " -- set interface {0} type=internal".format(port) + result = __salt__["cmd.run_all"](cmd) + return _retcode_to_bool(result["retcode"]) else: - cmd = 'ovs-vsctl add-port {0} {1} tag={2}'.format(br, port, id) + cmd = "ovs-vsctl add-port {0} {1} tag={2}".format(br, port, id) if internal: - cmd += ' -- set interface {0} type=internal'.format(port) - result = __salt__['cmd.run_all'](cmd) - return _retcode_to_bool(result['retcode']) + cmd += " -- set interface {0} type=internal".format(port) + result = __salt__["cmd.run_all"](cmd) + return _retcode_to_bool(result["retcode"]) def port_create_gre(br, port, id, remote): - ''' + """ Generic Routing Encapsulation - creates GRE tunnel between endpoints. Args: @@ -399,26 +400,30 @@ def port_create_gre(br, port, id, remote): .. code-block:: bash salt '*' openvswitch.port_create_gre br0 gre1 5001 192.168.1.10 - ''' - if not 0 <= id < 2**32: + """ + if not 0 <= id < 2 ** 32: return False - elif not __salt__['dig.check_ip'](remote): + elif not __salt__["dig.check_ip"](remote): return False elif not bridge_exists(br): return False elif port in port_list(br): - cmd = 'ovs-vsctl set interface {0} type=gre options:remote_ip={1} options:key={2}'.format(port, remote, id) - result = __salt__['cmd.run_all'](cmd) - return _retcode_to_bool(result['retcode']) + cmd = "ovs-vsctl set interface {0} type=gre options:remote_ip={1} options:key={2}".format( + port, remote, id + ) + result = __salt__["cmd.run_all"](cmd) + return _retcode_to_bool(result["retcode"]) else: - cmd = 'ovs-vsctl add-port {0} {1} -- set interface {1} type=gre options:remote_ip={2} ' \ - 'options:key={3}'.format(br, port, remote, id) - result = __salt__['cmd.run_all'](cmd) - return _retcode_to_bool(result['retcode']) + cmd = ( + "ovs-vsctl add-port {0} {1} -- set interface {1} type=gre options:remote_ip={2} " + "options:key={3}".format(br, port, remote, id) + ) + result = __salt__["cmd.run_all"](cmd) + return _retcode_to_bool(result["retcode"]) def port_create_vxlan(br, port, id, remote, dst_port=None): - ''' + """ Virtual eXtensible Local Area Network - creates VXLAN tunnel between endpoints. Args: @@ -437,21 +442,27 @@ def port_create_vxlan(br, port, id, remote, dst_port=None): .. code-block:: bash salt '*' openvswitch.port_create_vxlan br0 vx1 5001 192.168.1.10 8472 - ''' - dst_port = ' options:dst_port=' + six.text_type(dst_port) if 0 < dst_port <= 65535 else '' - if not 0 <= id < 2**64: + """ + dst_port = ( + " options:dst_port=" + six.text_type(dst_port) if 0 < dst_port <= 65535 else "" + ) + if not 0 <= id < 2 ** 64: return False - elif not __salt__['dig.check_ip'](remote): + elif not __salt__["dig.check_ip"](remote): return False elif not bridge_exists(br): return False elif port in port_list(br): - cmd = 'ovs-vsctl set interface {0} type=vxlan options:remote_ip={1} ' \ - 'options:key={2}{3}'.format(port, remote, id, dst_port) - result = __salt__['cmd.run_all'](cmd) - return _retcode_to_bool(result['retcode']) + cmd = ( + "ovs-vsctl set interface {0} type=vxlan options:remote_ip={1} " + "options:key={2}{3}".format(port, remote, id, dst_port) + ) + result = __salt__["cmd.run_all"](cmd) + return _retcode_to_bool(result["retcode"]) else: - cmd = 'ovs-vsctl add-port {0} {1} -- set interface {1} type=vxlan options:remote_ip={2} ' \ - 'options:key={3}{4}'.format(br, port, remote, id, dst_port) - result = __salt__['cmd.run_all'](cmd) - return _retcode_to_bool(result['retcode']) + cmd = ( + "ovs-vsctl add-port {0} {1} -- set interface {1} type=vxlan options:remote_ip={2} " + "options:key={3}{4}".format(br, port, remote, id, dst_port) + ) + result = __salt__["cmd.run_all"](cmd) + return _retcode_to_bool(result["retcode"]) diff --git a/salt/modules/opkg.py b/salt/modules/opkg.py index dbf27297b1a..b8c3f76bd42 100644 --- a/salt/modules/opkg.py +++ b/salt/modules/opkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for Opkg .. important:: @@ -15,14 +15,15 @@ Support for Opkg For version comparison support on opkg < 0.3.4, the ``opkg-utils`` package must be installed. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy +import errno +import logging import os import re -import logging -import errno # Import salt libs import salt.utils.args @@ -33,34 +34,33 @@ import salt.utils.path import salt.utils.pkg import salt.utils.stringutils import salt.utils.versions -from salt.exceptions import ( - CommandExecutionError, MinionError, SaltInvocationError -) +from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError + # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import shlex_quote as _cmd_quote # pylint: disable=import-error REPO_REGEXP = r'^#?\s*(src|src/gz)\s+([^\s<>]+|"[^<>]+")\s+[^\s<>]+' -OPKG_CONFDIR = '/etc/opkg' +OPKG_CONFDIR = "/etc/opkg" ATTR_MAP = { - 'Architecture': 'arch', - 'Homepage': 'url', - 'Installed-Time': 'install_date_time_t', - 'Maintainer': 'packager', - 'Package': 'name', - 'Section': 'group' + "Architecture": "arch", + "Homepage": "url", + "Installed-Time": "install_date_time_t", + "Maintainer": "packager", + "Package": "name", + "Section": "group", } log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" -NILRT_RESTARTCHECK_STATE_PATH = '/var/lib/salt/restartcheck_state' +NILRT_RESTARTCHECK_STATE_PATH = "/var/lib/salt/restartcheck_state" def _update_nilrt_restart_state(): - ''' + """ NILRT systems determine whether to reboot after various package operations including but not limited to kernel module installs/removals by checking specific file md5sums & timestamps. These files are touched/modified by @@ -69,64 +69,84 @@ def _update_nilrt_restart_state(): The opkg module uses this function to store/update those file timestamps and checksums to be used later by the restartcheck module. - ''' - __salt__['cmd.shell']('stat -c %Y /lib/modules/$(uname -r)/modules.dep >{0}/modules.dep.timestamp' - .format(NILRT_RESTARTCHECK_STATE_PATH)) - __salt__['cmd.shell']('md5sum /lib/modules/$(uname -r)/modules.dep >{0}/modules.dep.md5sum' - .format(NILRT_RESTARTCHECK_STATE_PATH)) + """ + __salt__["cmd.shell"]( + "stat -c %Y /lib/modules/$(uname -r)/modules.dep >{0}/modules.dep.timestamp".format( + NILRT_RESTARTCHECK_STATE_PATH + ) + ) + __salt__["cmd.shell"]( + "md5sum /lib/modules/$(uname -r)/modules.dep >{0}/modules.dep.md5sum".format( + NILRT_RESTARTCHECK_STATE_PATH + ) + ) # We can't assume nisysapi.ini always exists like modules.dep - nisysapi_path = '/usr/local/natinst/share/nisysapi.ini' + nisysapi_path = "/usr/local/natinst/share/nisysapi.ini" if os.path.exists(nisysapi_path): - __salt__['cmd.shell']('stat -c %Y {0} >{1}/nisysapi.ini.timestamp' - .format(nisysapi_path, NILRT_RESTARTCHECK_STATE_PATH)) - __salt__['cmd.shell']('md5sum {0} >{1}/nisysapi.ini.md5sum' - .format(nisysapi_path, NILRT_RESTARTCHECK_STATE_PATH)) + __salt__["cmd.shell"]( + "stat -c %Y {0} >{1}/nisysapi.ini.timestamp".format( + nisysapi_path, NILRT_RESTARTCHECK_STATE_PATH + ) + ) + __salt__["cmd.shell"]( + "md5sum {0} >{1}/nisysapi.ini.md5sum".format( + nisysapi_path, NILRT_RESTARTCHECK_STATE_PATH + ) + ) def _get_restartcheck_result(errors): - ''' + """ Return restartcheck result and append errors (if any) to ``errors`` - ''' - rs_result = __salt__['restartcheck.restartcheck'](verbose=False) - if isinstance(rs_result, dict) and 'comment' in rs_result: - errors.append(rs_result['comment']) + """ + rs_result = __salt__["restartcheck.restartcheck"](verbose=False) + if isinstance(rs_result, dict) and "comment" in rs_result: + errors.append(rs_result["comment"]) return rs_result def _process_restartcheck_result(rs_result): - ''' + """ Check restartcheck output to see if system/service restarts were requested and take appropriate action. - ''' - if 'No packages seem to need to be restarted' in rs_result: + """ + if "No packages seem to need to be restarted" in rs_result: return for rstr in rs_result: - if 'System restart required' in rstr: + if "System restart required" in rstr: _update_nilrt_restart_state() - __salt__['system.set_reboot_required_witnessed']() + __salt__["system.set_reboot_required_witnessed"]() else: - service = os.path.join('/etc/init.d', rstr) + service = os.path.join("/etc/init.d", rstr) if os.path.exists(service): - __salt__['cmd.run']([service, 'restart']) + __salt__["cmd.run"]([service, "restart"]) def __virtual__(): - ''' + """ Confirm this module is on a nilrt based system - ''' - if __grains__.get('os_family') == 'NILinuxRT': + """ + if __grains__.get("os_family") == "NILinuxRT": try: os.makedirs(NILRT_RESTARTCHECK_STATE_PATH) except OSError as exc: if exc.errno != errno.EEXIST: - return False, 'Error creating {0} (-{1}): {2}'.format( - NILRT_RESTARTCHECK_STATE_PATH, - exc.errno, - exc.strerror) + return ( + False, + "Error creating {0} (-{1}): {2}".format( + NILRT_RESTARTCHECK_STATE_PATH, exc.errno, exc.strerror + ), + ) # modules.dep always exists, make sure its restart state files also exist - if not (os.path.exists(os.path.join(NILRT_RESTARTCHECK_STATE_PATH, 'modules.dep.timestamp')) and - os.path.exists(os.path.join(NILRT_RESTARTCHECK_STATE_PATH, 'modules.dep.md5sum'))): + if not ( + os.path.exists( + os.path.join(NILRT_RESTARTCHECK_STATE_PATH, "modules.dep.timestamp") + ) + and os.path.exists( + os.path.join(NILRT_RESTARTCHECK_STATE_PATH, "modules.dep.md5sum") + ) + ): _update_nilrt_restart_state() return __virtualname__ @@ -136,7 +156,7 @@ def __virtual__(): def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -151,27 +171,25 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version salt '*' pkg.latest_version ... - ''' - refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) + """ + refresh = salt.utils.data.is_true(kwargs.pop("refresh", True)) if len(names) == 0: - return '' + return "" ret = {} for name in names: - ret[name] = '' + ret[name] = "" # Refresh before looking for the latest version available if refresh: refresh_db() - cmd = ['opkg', 'list-upgradable'] - out = __salt__['cmd.run_stdout'](cmd, - output_loglevel='trace', - python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): + cmd = ["opkg", "list-upgradable"] + out = __salt__["cmd.run_stdout"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): try: - name, _oldversion, newversion = line.split(' - ') + name, _oldversion, newversion = line.split(" - ") if name in names: ret[name] = newversion except ValueError: @@ -188,7 +206,7 @@ available_version = latest_version def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -199,12 +217,12 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def refresh_db(failhard=False, **kwargs): # pylint: disable=unused-argument - ''' + """ Updates the opkg database to latest packages based upon repositories Returns a dict, with the keys being package databases and the values being @@ -226,64 +244,63 @@ def refresh_db(failhard=False, **kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) ret = {} error_repos = [] - cmd = ['opkg', 'update'] + cmd = ["opkg", "update"] # opkg returns a non-zero retcode when there is a failure to refresh # from one or more repos. Due to this, ignore the retcode. - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False, - ignore_retcode=True, - redirect_stderr=True) + call = __salt__["cmd.run_all"]( + cmd, + output_loglevel="trace", + python_shell=False, + ignore_retcode=True, + redirect_stderr=True, + ) - out = call['stdout'] - prev_line = '' - for line in salt.utils.itertools.split(out, '\n'): - if 'Inflating' in line: + out = call["stdout"] + prev_line = "" + for line in salt.utils.itertools.split(out, "\n"): + if "Inflating" in line: key = line.strip().split()[1][:-1] ret[key] = True - elif 'Updated source' in line: + elif "Updated source" in line: # Use the previous line. key = prev_line.strip().split()[1][:-1] ret[key] = True - elif 'Failed to download' in line: - key = line.strip().split()[5].split(',')[0] + elif "Failed to download" in line: + key = line.strip().split()[5].split(",")[0] ret[key] = False error_repos.append(key) prev_line = line if failhard and error_repos: raise CommandExecutionError( - 'Error getting repos: {0}'.format(', '.join(error_repos)) + "Error getting repos: {0}".format(", ".join(error_repos)) ) # On a non-zero exit code where no failed repos were found, raise an # exception because this appears to be a different kind of error. - if call['retcode'] != 0 and not error_repos: + if call["retcode"] != 0 and not error_repos: raise CommandExecutionError(out) return ret def _append_noaction_if_testmode(cmd, **kwargs): - ''' + """ Adds the --noaction flag to the command if it's running in the test mode. - ''' - if bool(kwargs.get('test') or __opts__.get('test')): - cmd.append('--noaction') + """ + if bool(kwargs.get("test") or __opts__.get("test")): + cmd.append("--noaction") -def install(name=None, - refresh=False, - pkgs=None, - sources=None, - reinstall=False, - **kwargs): - ''' +def install( + name=None, refresh=False, pkgs=None, sources=None, reinstall=False, **kwargs +): + """ Install the passed package, add refresh=True to update the opkg database. name @@ -358,18 +375,18 @@ def install(name=None, {'': {'old': '', 'new': ''}} - ''' + """ refreshdb = salt.utils.data.is_true(refresh) try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: raise CommandExecutionError(exc) old = list_pkgs() - cmd_prefix = ['opkg', 'install'] + cmd_prefix = ["opkg", "install"] to_install = [] to_reinstall = [] to_downgrade = [] @@ -377,21 +394,20 @@ def install(name=None, _append_noaction_if_testmode(cmd_prefix, **kwargs) if pkg_params is None or len(pkg_params) == 0: return {} - elif pkg_type == 'file': + elif pkg_type == "file": if reinstall: - cmd_prefix.append('--force-reinstall') - if not kwargs.get('only_upgrade', False): - cmd_prefix.append('--force-downgrade') + cmd_prefix.append("--force-reinstall") + if not kwargs.get("only_upgrade", False): + cmd_prefix.append("--force-downgrade") to_install.extend(pkg_params) - elif pkg_type == 'repository': - if not kwargs.get('install_recommends', True): - cmd_prefix.append('--no-install-recommends') + elif pkg_type == "repository": + if not kwargs.get("install_recommends", True): + cmd_prefix.append("--no-install-recommends") for pkgname, pkgversion in six.iteritems(pkg_params): - if (name and pkgs is None and kwargs.get('version') and - len(pkg_params) == 1): + if name and pkgs is None and kwargs.get("version") and len(pkg_params) == 1: # Only use the 'version' param if 'name' was not specified as a # comma-separated list - version_num = kwargs['version'] + version_num = kwargs["version"] else: version_num = pkgversion @@ -403,22 +419,22 @@ def install(name=None, else: to_install.append(pkgname) else: - pkgstr = '{0}={1}'.format(pkgname, version_num) - cver = old.get(pkgname, '') - if reinstall and cver and salt.utils.versions.compare( - ver1=version_num, - oper='==', - ver2=cver, - cmp_func=version_cmp): + pkgstr = "{0}={1}".format(pkgname, version_num) + cver = old.get(pkgname, "") + if ( + reinstall + and cver + and salt.utils.versions.compare( + ver1=version_num, oper="==", ver2=cver, cmp_func=version_cmp + ) + ): to_reinstall.append(pkgstr) elif not cver or salt.utils.versions.compare( - ver1=version_num, - oper='>=', - ver2=cver, - cmp_func=version_cmp): + ver1=version_num, oper=">=", ver2=cver, cmp_func=version_cmp + ): to_install.append(pkgstr) else: - if not kwargs.get('only_upgrade', False): + if not kwargs.get("only_upgrade", False): to_downgrade.append(pkgstr) else: # This should cause the command to fail. @@ -433,13 +449,13 @@ def install(name=None, if to_downgrade: cmd = copy.deepcopy(cmd_prefix) - cmd.append('--force-downgrade') + cmd.append("--force-downgrade") cmd.extend(to_downgrade) cmds.append(cmd) if to_reinstall: cmd = copy.deepcopy(cmd_prefix) - cmd.append('--force-reinstall') + cmd.append("--force-reinstall") cmd.extend(to_reinstall) cmds.append(cmd) @@ -451,52 +467,45 @@ def install(name=None, errors = [] for cmd in cmds: - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False - ) - if out['retcode'] != 0: - if out['stderr']: - errors.append(out['stderr']) + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if out["retcode"] != 0: + if out["stderr"]: + errors.append(out["stderr"]) else: - errors.append(out['stdout']) + errors.append(out["stdout"]) - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if pkg_type == 'file' and reinstall: + if pkg_type == "file" and reinstall: # For file-based packages, prepare 'to_reinstall' to have a list # of all the package names that may have been reinstalled. # This way, we could include reinstalled packages in 'ret'. for pkgfile in to_install: # Convert from file name to package name. - cmd = ['opkg', 'info', pkgfile] - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False + cmd = ["opkg", "info", pkgfile] + out = __salt__["cmd.run_all"]( + cmd, output_loglevel="trace", python_shell=False ) - if out['retcode'] == 0: + if out["retcode"] == 0: # Just need the package name. - pkginfo_dict = _process_info_installed_output( - out['stdout'], [] - ) + pkginfo_dict = _process_info_installed_output(out["stdout"], []) if pkginfo_dict: to_reinstall.append(list(pkginfo_dict.keys())[0]) for pkgname in to_reinstall: if pkgname not in ret or pkgname in old: - ret.update({pkgname: {'old': old.get(pkgname, ''), - 'new': new.get(pkgname, '')}}) + ret.update( + {pkgname: {"old": old.get(pkgname, ""), "new": new.get(pkgname, "")}} + ) rs_result = _get_restartcheck_result(errors) if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) _process_restartcheck_result(rs_result) @@ -505,7 +514,7 @@ def install(name=None, def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument - ''' + """ Remove packages using ``opkg remove``. name @@ -538,9 +547,9 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' salt '*' pkg.remove pkgs='["foo", "bar"]' remove_dependencies=True auto_remove_deps=True - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -548,28 +557,24 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument targets = [x for x in pkg_params if x in old] if not targets: return {} - cmd = ['opkg', 'remove'] + cmd = ["opkg", "remove"] _append_noaction_if_testmode(cmd, **kwargs) - if kwargs.get('remove_dependencies', False): - cmd.append('--force-removal-of-dependent-packages') - if kwargs.get('auto_remove_deps', False): - cmd.append('--autoremove') + if kwargs.get("remove_dependencies", False): + cmd.append("--force-removal-of-dependent-packages") + if kwargs.get("auto_remove_deps", False): + cmd.append("--autoremove") cmd.extend(targets) - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False - ) - if out['retcode'] != 0: - if out['stderr']: - errors = [out['stderr']] + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if out["retcode"] != 0: + if out["stderr"]: + errors = [out["stderr"]] else: - errors = [out['stdout']] + errors = [out["stdout"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) @@ -577,8 +582,8 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) _process_restartcheck_result(rs_result) @@ -587,7 +592,7 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument - ''' + """ Package purges are not supported by opkg, this function is identical to :mod:`pkg.remove `. @@ -611,12 +616,12 @@ def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' + """ return remove(name=name, pkgs=pkgs) def upgrade(refresh=True, **kwargs): # pylint: disable=unused-argument - ''' + """ Upgrades all packages via ``opkg upgrade`` Returns a dictionary containing the changes: @@ -632,11 +637,12 @@ def upgrade(refresh=True, **kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' pkg.upgrade - ''' - ret = {'changes': {}, - 'result': True, - 'comment': '', - } + """ + ret = { + "changes": {}, + "result": True, + "comment": "", + } errors = [] @@ -645,23 +651,21 @@ def upgrade(refresh=True, **kwargs): # pylint: disable=unused-argument old = list_pkgs() - cmd = ['opkg', 'upgrade'] - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - __context__.pop('pkg.list_pkgs', None) + cmd = ["opkg", "upgrade"] + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: errors.append(result) rs_result = _get_restartcheck_result(errors) if errors: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'errors': errors, 'changes': ret} + "Problem encountered upgrading packages", + info={"errors": errors, "changes": ret}, ) _process_restartcheck_result(rs_result) @@ -670,7 +674,7 @@ def upgrade(refresh=True, **kwargs): # pylint: disable=unused-argument def hold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 - ''' + """ Set package in 'hold' state, meaning it will not be upgraded. name @@ -690,15 +694,11 @@ def hold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 .. code-block:: bash salt '*' pkg.hold pkgs='["foo", "bar"]' - ''' + """ if not name and not pkgs and not sources: - raise SaltInvocationError( - 'One of name, pkgs, or sources must be specified.' - ) + raise SaltInvocationError("One of name, pkgs, or sources must be specified.") if pkgs and sources: - raise SaltInvocationError( - 'Only one of pkgs or sources can be specified.' - ) + raise SaltInvocationError("Only one of pkgs or sources can be specified.") targets = [] if pkgs: @@ -714,34 +714,29 @@ def hold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 if isinstance(target, dict): target = next(iter(target)) - ret[target] = {'name': target, - 'changes': {}, - 'result': False, - 'comment': ''} + ret[target] = {"name": target, "changes": {}, "result": False, "comment": ""} state = _get_state(target) if not state: - ret[target]['comment'] = ('Package {0} not currently held.' - .format(target)) - elif state != 'hold': - if 'test' in __opts__ and __opts__['test']: + ret[target]["comment"] = "Package {0} not currently held.".format(target) + elif state != "hold": + if "test" in __opts__ and __opts__["test"]: ret[target].update(result=None) - ret[target]['comment'] = ('Package {0} is set to be held.' - .format(target)) + ret[target]["comment"] = "Package {0} is set to be held.".format(target) else: - result = _set_state(target, 'hold') + result = _set_state(target, "hold") ret[target].update(changes=result[target], result=True) - ret[target]['comment'] = ('Package {0} is now being held.' - .format(target)) + ret[target]["comment"] = "Package {0} is now being held.".format(target) else: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is already set to be held.' - .format(target)) + ret[target]["comment"] = "Package {0} is already set to be held.".format( + target + ) return ret def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 - ''' + """ Set package current in 'hold' state to install state, meaning it will be upgraded. @@ -762,15 +757,11 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06 .. code-block:: bash salt '*' pkg.unhold pkgs='["foo", "bar"]' - ''' + """ if not name and not pkgs and not sources: - raise SaltInvocationError( - 'One of name, pkgs, or sources must be specified.' - ) + raise SaltInvocationError("One of name, pkgs, or sources must be specified.") if pkgs and sources: - raise SaltInvocationError( - 'Only one of pkgs or sources can be specified.' - ) + raise SaltInvocationError("Only one of pkgs or sources can be specified.") targets = [] if pkgs: @@ -786,51 +777,48 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06 if isinstance(target, dict): target = next(iter(target)) - ret[target] = {'name': target, - 'changes': {}, - 'result': False, - 'comment': ''} + ret[target] = {"name": target, "changes": {}, "result": False, "comment": ""} state = _get_state(target) if not state: - ret[target]['comment'] = ('Package {0} does not have a state.' - .format(target)) - elif state == 'hold': - if 'test' in __opts__ and __opts__['test']: + ret[target]["comment"] = "Package {0} does not have a state.".format(target) + elif state == "hold": + if "test" in __opts__ and __opts__["test"]: ret[target].update(result=None) - ret['comment'] = ('Package {0} is set not to be held.' - .format(target)) + ret["comment"] = "Package {0} is set not to be held.".format(target) else: - result = _set_state(target, 'ok') + result = _set_state(target, "ok") ret[target].update(changes=result[target], result=True) - ret[target]['comment'] = ('Package {0} is no longer being ' - 'held.'.format(target)) + ret[target][ + "comment" + ] = "Package {0} is no longer being " "held.".format(target) else: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is already set not to be ' - 'held.'.format(target)) + ret[target][ + "comment" + ] = "Package {0} is already set not to be " "held.".format(target) return ret def _get_state(pkg): - ''' + """ View package state from the opkg database Return the state of pkg - ''' - cmd = ['opkg', 'status'] + """ + cmd = ["opkg", "status"] cmd.append(pkg) - out = __salt__['cmd.run'](cmd, python_shell=False) - state_flag = '' - for line in salt.utils.itertools.split(out, '\n'): - if line.startswith('Status'): + out = __salt__["cmd.run"](cmd, python_shell=False) + state_flag = "" + for line in salt.utils.itertools.split(out, "\n"): + if line.startswith("Status"): _status, _state_want, state_flag, _state_status = line.split() return state_flag def _set_state(pkg, state): - ''' + """ Change package state on the opkg database The state can be any of: @@ -847,25 +835,24 @@ def _set_state(pkg, state): Returns a dict containing the package name, and the new and old versions. - ''' + """ ret = {} - valid_states = ('hold', 'noprune', 'user', 'ok', 'installed', 'unpacked') + valid_states = ("hold", "noprune", "user", "ok", "installed", "unpacked") if state not in valid_states: - raise SaltInvocationError('Invalid state: {0}'.format(state)) + raise SaltInvocationError("Invalid state: {0}".format(state)) oldstate = _get_state(pkg) - cmd = ['opkg', 'flag'] + cmd = ["opkg", "flag"] cmd.append(state) cmd.append(pkg) - _out = __salt__['cmd.run'](cmd, python_shell=False) + _out = __salt__["cmd.run"](cmd, python_shell=False) # Missing return value check due to opkg issue 160 - ret[pkg] = {'old': oldstate, - 'new': state} + ret[pkg] = {"old": oldstate, "new": state} return ret def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed in a dict:: {'': ''} @@ -876,43 +863,44 @@ def list_pkgs(versions_as_list=False, **kwargs): salt '*' pkg.list_pkgs salt '*' pkg.list_pkgs versions_as_list=True - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret - cmd = ['opkg', 'list-installed'] + cmd = ["opkg", "list-installed"] ret = {} - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): # This is a continuation of package description - if not line or line[0] == ' ': + if not line or line[0] == " ": continue # This contains package name, version, and description. # Extract the first two. - pkg_name, pkg_version = line.split(' - ', 2)[:2] - __salt__['pkg_resource.add_pkg'](ret, pkg_name, pkg_version) + pkg_name, pkg_version = line.split(" - ", 2)[:2] + __salt__["pkg_resource.add_pkg"](ret, pkg_name, pkg_version) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def list_upgrades(refresh=True, **kwargs): # pylint: disable=unused-argument - ''' + """ List all available package upgrades. CLI Example: @@ -920,40 +908,38 @@ def list_upgrades(refresh=True, **kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ ret = {} if salt.utils.data.is_true(refresh): refresh_db() - cmd = ['opkg', 'list-upgradable'] - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + cmd = ["opkg", "list-upgradable"] + call = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] - if 'stdout' in call: - comment += call['stdout'] + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] + if "stdout" in call: + comment += call["stdout"] raise CommandExecutionError(comment) else: - out = call['stdout'] + out = call["stdout"] for line in out.splitlines(): - name, _oldversion, newversion = line.split(' - ') + name, _oldversion, newversion = line.split(" - ") ret[name] = newversion return ret def _convert_to_standard_attr(attr): - ''' + """ Helper function for _process_info_installed_output() Converts an opkg attribute name to a standard attribute name which is used across 'pkg' modules. - ''' + """ ret_attr = ATTR_MAP.get(attr, None) if ret_attr is None: # All others convert to lowercase @@ -962,25 +948,25 @@ def _convert_to_standard_attr(attr): def _process_info_installed_output(out, filter_attrs): - ''' + """ Helper function for info_installed() Processes stdout output from a single invocation of 'opkg status'. - ''' + """ ret = {} name = None attrs = {} attr = None - for line in salt.utils.itertools.split(out, '\n'): - if line and line[0] == ' ': + for line in salt.utils.itertools.split(out, "\n"): + if line and line[0] == " ": # This is a continuation of the last attr if filter_attrs is None or attr in filter_attrs: line = line.strip() if attrs[attr]: # If attr is empty, don't add leading newline - attrs[attr] += '\n' + attrs[attr] += "\n" attrs[attr] += line continue line = line.strip() @@ -992,10 +978,10 @@ def _process_info_installed_output(out, filter_attrs): attrs = {} attr = None continue - key, value = line.split(':', 1) + key, value = line.split(":", 1) value = value.lstrip() attr = _convert_to_standard_attr(key) - if attr == 'name': + if attr == "name": name = value elif filter_attrs is None or attr in filter_attrs: attrs[attr] = value @@ -1006,7 +992,7 @@ def _process_info_installed_output(out, filter_attrs): def info_installed(*names, **kwargs): - ''' + """ Return the information of the named package(s), installed on the system. .. versionadded:: 2017.7.0 @@ -1033,12 +1019,12 @@ def info_installed(*names, **kwargs): salt '*' pkg.info_installed ... salt '*' pkg.info_installed attr=version,packager salt '*' pkg.info_installed ... attr=version,packager - ''' - attr = kwargs.pop('attr', None) + """ + attr = kwargs.pop("attr", None) if attr is None: filter_attrs = None elif isinstance(attr, six.string_types): - filter_attrs = set(attr.split(',')) + filter_attrs = set(attr.split(",")) else: filter_attrs = set(attr) @@ -1046,40 +1032,38 @@ def info_installed(*names, **kwargs): if names: # Specific list of names of installed packages for name in names: - cmd = ['opkg', 'status', name] - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - if call['retcode'] != 0: - comment = '' - if call['stderr']: - comment += call['stderr'] + cmd = ["opkg", "status", name] + call = __salt__["cmd.run_all"]( + cmd, output_loglevel="trace", python_shell=False + ) + if call["retcode"] != 0: + comment = "" + if call["stderr"]: + comment += call["stderr"] else: - comment += call['stdout'] + comment += call["stdout"] raise CommandExecutionError(comment) - ret.update(_process_info_installed_output(call['stdout'], filter_attrs)) + ret.update(_process_info_installed_output(call["stdout"], filter_attrs)) else: # All installed packages - cmd = ['opkg', 'status'] - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - if call['retcode'] != 0: - comment = '' - if call['stderr']: - comment += call['stderr'] + cmd = ["opkg", "status"] + call = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if call["retcode"] != 0: + comment = "" + if call["stderr"]: + comment += call["stderr"] else: - comment += call['stdout'] + comment += call["stdout"] raise CommandExecutionError(comment) - ret.update(_process_info_installed_output(call['stdout'], filter_attrs)) + ret.update(_process_info_installed_output(call["stdout"], filter_attrs)) return ret def upgrade_available(name, **kwargs): # pylint: disable=unused-argument - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -1087,12 +1071,14 @@ def upgrade_available(name, **kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' pkg.upgrade_available - ''' - return latest_version(name) != '' + """ + return latest_version(name) != "" -def version_cmp(pkg1, pkg2, ignore_epoch=False, **kwargs): # pylint: disable=unused-argument - ''' +def version_cmp( + pkg1, pkg2, ignore_epoch=False, **kwargs +): # pylint: disable=unused-argument + """ Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem making the comparison. @@ -1107,23 +1093,30 @@ def version_cmp(pkg1, pkg2, ignore_epoch=False, **kwargs): # pylint: disable=un .. code-block:: bash salt '*' pkg.version_cmp '0.2.4-0' '0.2.4.1-0' - ''' - normalize = lambda x: six.text_type(x).split(':', 1)[-1] if ignore_epoch else six.text_type(x) + """ + normalize = ( + lambda x: six.text_type(x).split(":", 1)[-1] + if ignore_epoch + else six.text_type(x) + ) pkg1 = normalize(pkg1) pkg2 = normalize(pkg2) - output = __salt__['cmd.run_stdout'](['opkg', '--version'], - output_loglevel='trace', - python_shell=False) - opkg_version = output.split(' ')[2].strip() - if salt.utils.versions.LooseVersion(opkg_version) >= \ - salt.utils.versions.LooseVersion('0.3.4'): - cmd_compare = ['opkg', 'compare-versions'] - elif salt.utils.path.which('opkg-compare-versions'): - cmd_compare = ['opkg-compare-versions'] + output = __salt__["cmd.run_stdout"]( + ["opkg", "--version"], output_loglevel="trace", python_shell=False + ) + opkg_version = output.split(" ")[2].strip() + if salt.utils.versions.LooseVersion( + opkg_version + ) >= salt.utils.versions.LooseVersion("0.3.4"): + cmd_compare = ["opkg", "compare-versions"] + elif salt.utils.path.which("opkg-compare-versions"): + cmd_compare = ["opkg-compare-versions"] else: - log.warning('Unable to find a compare-versions utility installed. Either upgrade opkg to ' - 'version > 0.3.4 (preferred) or install the older opkg-compare-versions script.') + log.warning( + "Unable to find a compare-versions utility installed. Either upgrade opkg to " + "version > 0.3.4 (preferred) or install the older opkg-compare-versions script." + ) return None for oper, ret in (("<<", -1), ("=", 0), (">>", 1)): @@ -1131,17 +1124,16 @@ def version_cmp(pkg1, pkg2, ignore_epoch=False, **kwargs): # pylint: disable=un cmd.append(_cmd_quote(pkg1)) cmd.append(oper) cmd.append(_cmd_quote(pkg2)) - retcode = __salt__['cmd.retcode'](cmd, - output_loglevel='trace', - ignore_retcode=True, - python_shell=False) + retcode = __salt__["cmd.retcode"]( + cmd, output_loglevel="trace", ignore_retcode=True, python_shell=False + ) if retcode == 0: return ret return None def list_repos(**kwargs): # pylint: disable=unused-argument - ''' + """ Lists all repos on ``/etc/opkg/*.conf`` CLI Example: @@ -1149,37 +1141,39 @@ def list_repos(**kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' pkg.list_repos - ''' + """ repos = {} regex = re.compile(REPO_REGEXP) for filename in os.listdir(OPKG_CONFDIR): if filename.endswith(".conf"): - with salt.utils.files.fopen(os.path.join(OPKG_CONFDIR, filename)) as conf_file: + with salt.utils.files.fopen( + os.path.join(OPKG_CONFDIR, filename) + ) as conf_file: for line in conf_file: line = salt.utils.stringutils.to_unicode(line) if regex.search(line): repo = {} - if line.startswith('#'): - repo['enabled'] = False + if line.startswith("#"): + repo["enabled"] = False line = line[1:] else: - repo['enabled'] = True + repo["enabled"] = True cols = salt.utils.args.shlex_split(line.strip()) - if cols[0] in 'src': - repo['compressed'] = False + if cols[0] in "src": + repo["compressed"] = False else: - repo['compressed'] = True - repo['name'] = cols[1] - repo['uri'] = cols[2] - repo['file'] = os.path.join(OPKG_CONFDIR, filename) + repo["compressed"] = True + repo["name"] = cols[1] + repo["uri"] = cols[2] + repo["file"] = os.path.join(OPKG_CONFDIR, filename) # do not store duplicated uri's - if repo['uri'] not in repos: - repos[repo['uri']] = [repo] + if repo["uri"] not in repos: + repos[repo["uri"]] = [repo] return repos def get_repo(repo, **kwargs): # pylint: disable=unused-argument - ''' + """ Display a repo from the ``/etc/opkg/*.conf`` CLI Examples: @@ -1187,57 +1181,57 @@ def get_repo(repo, **kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' pkg.get_repo repo - ''' + """ repos = list_repos() if repos: for source in six.itervalues(repos): for sub in source: - if sub['name'] == repo: + if sub["name"] == repo: return sub return {} def _del_repo_from_file(repo, filepath): - ''' + """ Remove a repo from filepath - ''' + """ with salt.utils.files.fopen(filepath) as fhandle: output = [] regex = re.compile(REPO_REGEXP) for line in fhandle: line = salt.utils.stringutils.to_unicode(line) if regex.search(line): - if line.startswith('#'): + if line.startswith("#"): line = line[1:] cols = salt.utils.args.shlex_split(line.strip()) if repo != cols[1]: output.append(salt.utils.stringutils.to_str(line)) - with salt.utils.files.fopen(filepath, 'w') as fhandle: + with salt.utils.files.fopen(filepath, "w") as fhandle: fhandle.writelines(output) def _add_new_repo(repo, uri, compressed, enabled=True): - ''' + """ Add a new repo entry - ''' - repostr = '# ' if not enabled else '' - repostr += 'src/gz ' if compressed else 'src ' - if ' ' in repo: + """ + repostr = "# " if not enabled else "" + repostr += "src/gz " if compressed else "src " + if " " in repo: repostr += '"' + repo + '" ' else: - repostr += repo + ' ' - repostr += uri + '\n' - conffile = os.path.join(OPKG_CONFDIR, repo + '.conf') + repostr += repo + " " + repostr += uri + "\n" + conffile = os.path.join(OPKG_CONFDIR, repo + ".conf") - with salt.utils.files.fopen(conffile, 'a') as fhandle: + with salt.utils.files.fopen(conffile, "a") as fhandle: fhandle.write(salt.utils.stringutils.to_str(repostr)) def _mod_repo_in_file(repo, repostr, filepath): - ''' + """ Replace a repo entry in filepath with repostr - ''' + """ with salt.utils.files.fopen(filepath) as fhandle: output = [] for line in fhandle: @@ -1247,13 +1241,13 @@ def _mod_repo_in_file(repo, repostr, filepath): if repo not in cols: output.append(line) else: - output.append(salt.utils.stringutils.to_str(repostr + '\n')) - with salt.utils.files.fopen(filepath, 'w') as fhandle: + output.append(salt.utils.stringutils.to_str(repostr + "\n")) + with salt.utils.files.fopen(filepath, "w") as fhandle: fhandle.writelines(output) def del_repo(repo, **kwargs): # pylint: disable=unused-argument - ''' + """ Delete a repo from ``/etc/opkg/*.conf`` If the file does not contain any other repo configuration, the file itself @@ -1264,28 +1258,27 @@ def del_repo(repo, **kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' pkg.del_repo repo - ''' - refresh = salt.utils.data.is_true(kwargs.get('refresh', True)) + """ + refresh = salt.utils.data.is_true(kwargs.get("refresh", True)) repos = list_repos() if repos: deleted_from = dict() for repository in repos: source = repos[repository][0] - if source['name'] == repo: - deleted_from[source['file']] = 0 - _del_repo_from_file(repo, source['file']) + if source["name"] == repo: + deleted_from[source["file"]] = 0 + _del_repo_from_file(repo, source["file"]) if deleted_from: - ret = '' + ret = "" for repository in repos: source = repos[repository][0] - if source['file'] in deleted_from: - deleted_from[source['file']] += 1 + if source["file"] in deleted_from: + deleted_from[source["file"]] += 1 for repo_file, count in six.iteritems(deleted_from): - msg = 'Repo \'{0}\' has been removed from {1}.\n' + msg = "Repo '{0}' has been removed from {1}.\n" if count == 1 and os.path.isfile(repo_file): - msg = ('File {1} containing repo \'{0}\' has been ' - 'removed.\n') + msg = "File {1} containing repo '{0}' has been " "removed.\n" try: os.remove(repo_file) except OSError: @@ -1299,7 +1292,7 @@ def del_repo(repo, **kwargs): # pylint: disable=unused-argument def mod_repo(repo, **kwargs): - ''' + """ Modify one or more values for a repo. If the repo does not exist, it will be created, so long as uri is defined. @@ -1323,52 +1316,58 @@ def mod_repo(repo, **kwargs): salt '*' pkg.mod_repo repo uri=http://new/uri salt '*' pkg.mod_repo repo enabled=False - ''' + """ repos = list_repos() found = False - uri = '' - if 'uri' in kwargs: - uri = kwargs['uri'] + uri = "" + if "uri" in kwargs: + uri = kwargs["uri"] for repository in repos: source = repos[repository][0] - if source['name'] == repo: + if source["name"] == repo: found = True - repostr = '' - if 'enabled' in kwargs and not kwargs['enabled']: - repostr += '# ' - if 'compressed' in kwargs: - repostr += 'src/gz ' if kwargs['compressed'] else 'src' + repostr = "" + if "enabled" in kwargs and not kwargs["enabled"]: + repostr += "# " + if "compressed" in kwargs: + repostr += "src/gz " if kwargs["compressed"] else "src" else: - repostr += 'src/gz' if source['compressed'] else 'src' - repo_alias = kwargs['alias'] if 'alias' in kwargs else repo - if ' ' in repo_alias: + repostr += "src/gz" if source["compressed"] else "src" + repo_alias = kwargs["alias"] if "alias" in kwargs else repo + if " " in repo_alias: repostr += ' "{0}"'.format(repo_alias) else: - repostr += ' {0}'.format(repo_alias) - repostr += ' {0}'.format(kwargs['uri'] if 'uri' in kwargs else source['uri']) - _mod_repo_in_file(repo, repostr, source['file']) - elif uri and source['uri'] == uri: + repostr += " {0}".format(repo_alias) + repostr += " {0}".format( + kwargs["uri"] if "uri" in kwargs else source["uri"] + ) + _mod_repo_in_file(repo, repostr, source["file"]) + elif uri and source["uri"] == uri: raise CommandExecutionError( - 'Repository \'{0}\' already exists as \'{1}\'.'.format(uri, source['name'])) + "Repository '{0}' already exists as '{1}'.".format(uri, source["name"]) + ) if not found: # Need to add a new repo - if 'uri' not in kwargs: + if "uri" not in kwargs: raise CommandExecutionError( - 'Repository \'{0}\' not found and no URI passed to create one.'.format(repo)) + "Repository '{0}' not found and no URI passed to create one.".format( + repo + ) + ) # If compressed is not defined, assume True - compressed = kwargs['compressed'] if 'compressed' in kwargs else True + compressed = kwargs["compressed"] if "compressed" in kwargs else True # If enabled is not defined, assume True - enabled = kwargs['enabled'] if 'enabled' in kwargs else True - _add_new_repo(repo, kwargs['uri'], compressed, enabled) + enabled = kwargs["enabled"] if "enabled" in kwargs else True + _add_new_repo(repo, kwargs["uri"], compressed, enabled) - if 'refresh' in kwargs: + if "refresh" in kwargs: refresh_db() def file_list(*packages, **kwargs): # pylint: disable=unused-argument - ''' + """ List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -1380,16 +1379,16 @@ def file_list(*packages, **kwargs): # pylint: disable=unused-argument salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' + """ output = file_dict(*packages) files = [] - for package in list(output['packages'].values()): + for package in list(output["packages"].values()): files.extend(package) - return {'errors': output['errors'], 'files': files} + return {"errors": output["errors"], "files": files} def file_dict(*packages, **kwargs): # pylint: disable=unused-argument - ''' + """ List the files that belong to a package, grouped by package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -1401,10 +1400,10 @@ def file_dict(*packages, **kwargs): # pylint: disable=unused-argument salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' + """ errors = [] ret = {} - cmd_files = ['opkg', 'files'] + cmd_files = ["opkg", "files"] if not packages: packages = list(list_pkgs().keys()) @@ -1413,13 +1412,11 @@ def file_dict(*packages, **kwargs): # pylint: disable=unused-argument files = [] cmd = cmd_files[:] cmd.append(package) - out = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - for line in out['stdout'].splitlines(): - if line.startswith('/'): + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + for line in out["stdout"].splitlines(): + if line.startswith("/"): files.append(line) - elif line.startswith(' * '): + elif line.startswith(" * "): errors.append(line[3:]) break else: @@ -1427,11 +1424,11 @@ def file_dict(*packages, **kwargs): # pylint: disable=unused-argument if files: ret[package] = files - return {'errors': errors, 'packages': ret} + return {"errors": errors, "packages": ret} def owner(*paths, **kwargs): # pylint: disable=unused-argument - ''' + """ Return the name of the package that owns the file. Multiple file paths can be passed. Like :mod:`pkg.version @@ -31,66 +31,71 @@ Oracle DataBase connection module optional keyword servicename will determine whether it is a sid or service_name : uri: ..... -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import os import logging -from salt.utils.decorators import depends +import os + from salt.ext import six +from salt.utils.decorators import depends log = logging.getLogger(__name__) try: import cx_Oracle - MODE = { - 'sysdba': cx_Oracle.SYSDBA, - 'sysoper': cx_Oracle.SYSOPER - } + + MODE = {"sysdba": cx_Oracle.SYSDBA, "sysoper": cx_Oracle.SYSOPER} HAS_CX_ORACLE = True except ImportError: - MODE = {'sysdba': 2, 'sysoper': 4} + MODE = {"sysdba": 2, "sysoper": 4} HAS_CX_ORACLE = False -__virtualname__ = 'oracle' +__virtualname__ = "oracle" def __virtual__(): - ''' + """ Load module only if cx_Oracle installed - ''' + """ if HAS_CX_ORACLE: return __virtualname__ - return (False, 'The oracle execution module not loaded: ' - 'python oracle library not found.') + return ( + False, + "The oracle execution module not loaded: python oracle library not found.", + ) def _cx_oracle_req(): - ''' + """ Fallback function stub - ''' + """ return 'Need "cx_Oracle" and Oracle Client installed for this function exist' def _unicode_output(cursor, name, default_type, size, precision, scale): - ''' + """ Return strings values as python unicode string http://www.oracle.com/technetwork/articles/dsl/tuininga-cx-oracle-084866.html - ''' - if default_type in (cx_Oracle.STRING, cx_Oracle.LONG_STRING, - cx_Oracle.FIXED_CHAR, cx_Oracle.CLOB): + """ + if default_type in ( + cx_Oracle.STRING, + cx_Oracle.LONG_STRING, + cx_Oracle.FIXED_CHAR, + cx_Oracle.CLOB, + ): return cursor.var(six.text_type, size, cursor.arraysize) def _connect(uri): - ''' + """ uri = user/password@host[:port]/sid[ as {sysdba|sysoper}] Return cx_Oracle.Connection instance - ''' + """ # cx_Oracle.Connection() not support 'as sysdba' syntax - uri_l = uri.rsplit(' as ', 1) + uri_l = uri.rsplit(" as ", 1) if len(uri_l) == 2: credentials, mode = uri_l mode = MODE[mode] @@ -99,36 +104,36 @@ def _connect(uri): mode = 0 serv_name = False - userpass, hostportsid = credentials.split('@') - user, password = userpass.split('/') - hostport, sid = hostportsid.split('/') - if 'servicename' in sid: + userpass, hostportsid = credentials.split("@") + user, password = userpass.split("/") + hostport, sid = hostportsid.split("/") + if "servicename" in sid: serv_name = True - sid = sid.split('servicename')[0].strip() - hostport_l = hostport.split(':') + sid = sid.split("servicename")[0].strip() + hostport_l = hostport.split(":") if len(hostport_l) == 2: host, port = hostport_l else: host = hostport_l[0] port = 1521 - log.debug('connect: %s', (user, password, host, port, sid, mode)) + log.debug("connect: %s", (user, password, host, port, sid, mode)) # force UTF-8 client encoding - os.environ['NLS_LANG'] = '.AL32UTF8' + os.environ["NLS_LANG"] = ".AL32UTF8" if serv_name: - conn = cx_Oracle.connect(user, password, - cx_Oracle.makedsn(host, port, service_name=sid), - mode) + conn = cx_Oracle.connect( + user, password, cx_Oracle.makedsn(host, port, service_name=sid), mode + ) else: - conn = cx_Oracle.connect(user, password, - cx_Oracle.makedsn(host, port, sid), - mode) + conn = cx_Oracle.connect( + user, password, cx_Oracle.makedsn(host, port, sid), mode + ) conn.outputtypehandler = _unicode_output return conn -@depends('cx_Oracle', fallback_function=_cx_oracle_req) +@depends("cx_Oracle", fallback_function=_cx_oracle_req) def run_query(db, query): - ''' + """ Run SQL query and return result CLI Example: @@ -136,14 +141,14 @@ def run_query(db, query): .. code-block:: bash salt '*' oracle.run_query my_db "select * from my_table" - ''' - log.debug('run query on %s: %s', db, query) - conn = _connect(show_dbs(db)[db]['uri']) + """ + log.debug("run query on %s: %s", db, query) + conn = _connect(show_dbs(db)[db]["uri"]) return conn.cursor().execute(query).fetchall() def show_dbs(*dbs): - ''' + """ Show databases configuration from pillar. Filter by `*args` CLI Example: @@ -152,22 +157,22 @@ def show_dbs(*dbs): salt '*' oracle.show_dbs salt '*' oracle.show_dbs my_db - ''' + """ if dbs: - log.debug('get dbs from pillar: %s', dbs) + log.debug("get dbs from pillar: %s", dbs) result = {} for db in dbs: - result[db] = __salt__['pillar.get']('oracle:dbs:' + db) + result[db] = __salt__["pillar.get"]("oracle:dbs:" + db) return result else: - pillar_dbs = __salt__['pillar.get']('oracle:dbs') - log.debug('get all (%s) dbs from pillar', len(pillar_dbs)) + pillar_dbs = __salt__["pillar.get"]("oracle:dbs") + log.debug("get all (%s) dbs from pillar", len(pillar_dbs)) return pillar_dbs -@depends('cx_Oracle', fallback_function=_cx_oracle_req) +@depends("cx_Oracle", fallback_function=_cx_oracle_req) def version(*dbs): - ''' + """ Server Version (select banner from v$version) CLI Example: @@ -176,27 +181,27 @@ def version(*dbs): salt '*' oracle.version salt '*' oracle.version my_db - ''' - pillar_dbs = __salt__['pillar.get']('oracle:dbs') + """ + pillar_dbs = __salt__["pillar.get"]("oracle:dbs") get_version = lambda x: [ r[0] for r in run_query(x, "select banner from v$version order by banner") - ] + ] result = {} if dbs: - log.debug('get db versions for: %s', dbs) + log.debug("get db versions for: %s", dbs) for db in dbs: if db in pillar_dbs: result[db] = get_version(db) else: - log.debug('get all (%s) dbs versions', len(dbs)) + log.debug("get all (%s) dbs versions", len(dbs)) for db in dbs: result[db] = get_version(db) return result -@depends('cx_Oracle', fallback_function=_cx_oracle_req) +@depends("cx_Oracle", fallback_function=_cx_oracle_req) def client_version(): - ''' + """ Oracle Client Version CLI Example: @@ -204,12 +209,12 @@ def client_version(): .. code-block:: bash salt '*' oracle.client_version - ''' - return '.'.join((six.text_type(x) for x in cx_Oracle.clientversion())) + """ + return ".".join((six.text_type(x) for x in cx_Oracle.clientversion())) def show_pillar(item=None): - ''' + """ Show Pillar segment oracle.* and subitem with notation "item:subitem" CLI Example: @@ -218,15 +223,15 @@ def show_pillar(item=None): salt '*' oracle.show_pillar salt '*' oracle.show_pillar dbs:my_db - ''' + """ if item: - return __salt__['pillar.get']('oracle:' + item) + return __salt__["pillar.get"]("oracle:" + item) else: - return __salt__['pillar.get']('oracle') + return __salt__["pillar.get"]("oracle") def show_env(): - ''' + """ Show Environment used by Oracle Client CLI Example: @@ -237,8 +242,8 @@ def show_env(): .. note:: at first _connect() ``NLS_LANG`` will forced to '.AL32UTF8' - ''' - envs = ['PATH', 'ORACLE_HOME', 'TNS_ADMIN', 'NLS_LANG'] + """ + envs = ["PATH", "ORACLE_HOME", "TNS_ADMIN", "NLS_LANG"] result = {} for env in envs: if env in os.environ: diff --git a/salt/modules/osquery.py b/salt/modules/osquery.py index a491ca9c43c..1ce56ba6bf4 100644 --- a/salt/modules/osquery.py +++ b/salt/modules/osquery.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Support for OSQuery - https://osquery.io. .. versionadded:: 2015.8.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -18,61 +18,64 @@ log = logging.getLogger(__name__) __func_alias__ = { - 'file_': 'file', - 'hash_': 'hash', - 'time_': 'time', + "file_": "file", + "hash_": "hash", + "time_": "time", } -__virtualname__ = 'osquery' +__virtualname__ = "osquery" def __virtual__(): - if salt.utils.path.which('osqueryi'): + if salt.utils.path.which("osqueryi"): return __virtualname__ - return (False, 'The osquery execution module cannot be loaded: ' - 'osqueryi binary is not in the path.') + return ( + False, + "The osquery execution module cannot be loaded: " + "osqueryi binary is not in the path.", + ) def _table_attrs(table): - ''' + """ Helper function to find valid table attributes - ''' - cmd = ['osqueryi'] + ['--json'] + ['pragma table_info({0})'.format(table)] - res = __salt__['cmd.run_all'](cmd) - if res['retcode'] == 0: + """ + cmd = ["osqueryi"] + ["--json"] + ["pragma table_info({0})".format(table)] + res = __salt__["cmd.run_all"](cmd) + if res["retcode"] == 0: attrs = [] - text = salt.utils.json.loads(res['stdout']) + text = salt.utils.json.loads(res["stdout"]) for item in text: - attrs.append(item['name']) + attrs.append(item["name"]) return attrs return False -def _osquery(sql, format='json'): - ''' +def _osquery(sql, format="json"): + """ Helper function to run raw osquery queries - ''' + """ ret = { - 'result': True, + "result": True, } - cmd = ['osqueryi'] + ['--json'] + [sql] - res = __salt__['cmd.run_all'](cmd) - if res['stderr']: - ret['result'] = False - ret['error'] = res['stderr'] + cmd = ["osqueryi"] + ["--json"] + [sql] + res = __salt__["cmd.run_all"](cmd) + if res["stderr"]: + ret["result"] = False + ret["error"] = res["stderr"] else: - ret['data'] = salt.utils.json.loads(res['stdout']) - log.debug('== %s ==', ret) + ret["data"] = salt.utils.json.loads(res["stdout"]) + log.debug("== %s ==", ret) return ret -def _osquery_cmd(table, attrs=None, where=None, format='json'): - ''' +def _osquery_cmd(table, attrs=None, where=None, format="json"): + """ Helper function to run osquery queries - ''' + """ ret = { - 'result': True, + "result": True, } if attrs: @@ -81,38 +84,42 @@ def _osquery_cmd(table, attrs=None, where=None, format='json'): if valid_attrs: for a in attrs: if a not in valid_attrs: - ret['result'] = False - ret['comment'] = '{0} is not a valid attribute for table {1}'.format(a, table) + ret["result"] = False + ret[ + "comment" + ] = "{0} is not a valid attribute for table {1}".format( + a, table + ) return ret - _attrs = ','.join(attrs) + _attrs = ",".join(attrs) else: - ret['result'] = False - ret['comment'] = 'Invalid table {0}.'.format(table) + ret["result"] = False + ret["comment"] = "Invalid table {0}.".format(table) return ret else: - ret['comment'] = 'attrs must be specified as a list.' - ret['result'] = False + ret["comment"] = "attrs must be specified as a list." + ret["result"] = False return ret else: - _attrs = '*' + _attrs = "*" - sql = 'select {0} from {1}'.format(_attrs, table) + sql = "select {0} from {1}".format(_attrs, table) if where: - sql = '{0} where {1}'.format(sql, where) + sql = "{0} where {1}".format(sql, where) - sql = '{0};'.format(sql) + sql = "{0};".format(sql) res = _osquery(sql) - if res['result']: - ret['data'] = res['data'] + if res["result"]: + ret["data"] = res["data"] else: - ret['comment'] = res['error'] + ret["comment"] = res["error"] return ret def version(): - ''' + """ Return version of osquery CLI Example: @@ -120,18 +127,17 @@ def version(): .. code-block:: bash salt '*' osquery.version - ''' - _false_return = {'result': False, - 'comment': 'OSQuery version unavailable.'} - res = _osquery_cmd(table='osquery_info', attrs=['version']) - if 'result' in res and res['result']: - if 'data' in res and isinstance(res['data'], list): - return res['data'][0].get('version', '') or _false_return + """ + _false_return = {"result": False, "comment": "OSQuery version unavailable."} + res = _osquery_cmd(table="osquery_info", attrs=["version"]) + if "result" in res and res["result"]: + if "data" in res and isinstance(res["data"], list): + return res["data"][0].get("version", "") or _false_return return _false_return def rpm_packages(attrs=None, where=None): - ''' + """ Return cpuid information from osquery CLI Example: @@ -139,15 +145,14 @@ def rpm_packages(attrs=None, where=None): .. code-block:: bash salt '*' osquery.rpm_packages - ''' - if __grains__['os_family'] == 'RedHat': - return _osquery_cmd(table='rpm_packages', attrs=attrs, where=where) - return {'result': False, - 'comment': 'Only available on Red Hat based systems.'} + """ + if __grains__["os_family"] == "RedHat": + return _osquery_cmd(table="rpm_packages", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on Red Hat based systems."} def kernel_integrity(attrs=None, where=None): - ''' + """ Return kernel_integrity information from osquery CLI Example: @@ -155,14 +160,17 @@ def kernel_integrity(attrs=None, where=None): .. code-block:: bash salt '*' osquery.kernel_integrity - ''' - if __grains__['os_family'] in ['RedHat', 'Debian']: - return _osquery_cmd(table='kernel_integrity', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on Red Hat or Debian based systems.'} + """ + if __grains__["os_family"] in ["RedHat", "Debian"]: + return _osquery_cmd(table="kernel_integrity", attrs=attrs, where=where) + return { + "result": False, + "comment": "Only available on Red Hat or Debian based systems.", + } def kernel_modules(attrs=None, where=None): - ''' + """ Return kernel_modules information from osquery CLI Example: @@ -170,14 +178,17 @@ def kernel_modules(attrs=None, where=None): .. code-block:: bash salt '*' osquery.kernel_modules - ''' - if __grains__['os_family'] in ['RedHat', 'Debian']: - return _osquery_cmd(table='kernel_modules', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on Red Hat or Debian based systems.'} + """ + if __grains__["os_family"] in ["RedHat", "Debian"]: + return _osquery_cmd(table="kernel_modules", attrs=attrs, where=where) + return { + "result": False, + "comment": "Only available on Red Hat or Debian based systems.", + } def memory_map(attrs=None, where=None): - ''' + """ Return memory_map information from osquery CLI Example: @@ -185,14 +196,17 @@ def memory_map(attrs=None, where=None): .. code-block:: bash salt '*' osquery.memory_map - ''' - if __grains__['os_family'] in ['RedHat', 'Debian']: - return _osquery_cmd(table='memory_map', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on Red Hat or Debian based systems.'} + """ + if __grains__["os_family"] in ["RedHat", "Debian"]: + return _osquery_cmd(table="memory_map", attrs=attrs, where=where) + return { + "result": False, + "comment": "Only available on Red Hat or Debian based systems.", + } def process_memory_map(attrs=None, where=None): - ''' + """ Return process_memory_map information from osquery CLI Example: @@ -200,14 +214,17 @@ def process_memory_map(attrs=None, where=None): .. code-block:: bash salt '*' osquery.process_memory_map - ''' - if __grains__['os_family'] in ['RedHat', 'Debian']: - return _osquery_cmd(table='process_memory_map', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on Red Hat or Debian based systems.'} + """ + if __grains__["os_family"] in ["RedHat", "Debian"]: + return _osquery_cmd(table="process_memory_map", attrs=attrs, where=where) + return { + "result": False, + "comment": "Only available on Red Hat or Debian based systems.", + } def shared_memory(attrs=None, where=None): - ''' + """ Return shared_memory information from osquery CLI Example: @@ -215,14 +232,17 @@ def shared_memory(attrs=None, where=None): .. code-block:: bash salt '*' osquery.shared_memory - ''' - if __grains__['os_family'] in ['RedHat', 'Debian']: - return _osquery_cmd(table='shared_memory', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on Red Hat or Debian based systems.'} + """ + if __grains__["os_family"] in ["RedHat", "Debian"]: + return _osquery_cmd(table="shared_memory", attrs=attrs, where=where) + return { + "result": False, + "comment": "Only available on Red Hat or Debian based systems.", + } def apt_sources(attrs=None, where=None): - ''' + """ Return apt_sources information from osquery CLI Example: @@ -230,14 +250,14 @@ def apt_sources(attrs=None, where=None): .. code-block:: bash salt '*' osquery.apt_sources - ''' - if __grains__['os_family'] == 'Debian': - return _osquery_cmd(table='apt_sources', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on Debian based systems.'} + """ + if __grains__["os_family"] == "Debian": + return _osquery_cmd(table="apt_sources", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on Debian based systems."} def deb_packages(attrs=None, where=None): - ''' + """ Return deb_packages information from osquery CLI Example: @@ -245,14 +265,14 @@ def deb_packages(attrs=None, where=None): .. code-block:: bash salt '*' osquery.deb_packages - ''' - if __grains__['os_family'] == 'Debian': - return _osquery_cmd(table='deb_packages', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on Debian based systems.'} + """ + if __grains__["os_family"] == "Debian": + return _osquery_cmd(table="deb_packages", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on Debian based systems."} def acpi_tables(attrs=None, where=None): - ''' + """ Return acpi_tables information from osquery CLI Example: @@ -260,12 +280,12 @@ def acpi_tables(attrs=None, where=None): .. code-block:: bash salt '*' osquery.acpi_tables - ''' - return _osquery_cmd(table='acpi_tables', attrs=attrs, where=where) + """ + return _osquery_cmd(table="acpi_tables", attrs=attrs, where=where) def arp_cache(attrs=None, where=None): - ''' + """ Return arp_cache information from osquery CLI Example: @@ -273,12 +293,12 @@ def arp_cache(attrs=None, where=None): .. code-block:: bash salt '*' osquery.arp_cache - ''' - return _osquery_cmd(table='arp_cache', attrs=attrs, where=where) + """ + return _osquery_cmd(table="arp_cache", attrs=attrs, where=where) def block_devices(attrs=None, where=None): - ''' + """ Return block_devices information from osquery CLI Example: @@ -286,12 +306,12 @@ def block_devices(attrs=None, where=None): .. code-block:: bash salt '*' osquery.block_devices - ''' - return _osquery_cmd(table='block_devices', attrs=attrs, where=where) + """ + return _osquery_cmd(table="block_devices", attrs=attrs, where=where) def cpuid(attrs=None, where=None): - ''' + """ Return cpuid information from osquery CLI Example: @@ -299,12 +319,12 @@ def cpuid(attrs=None, where=None): .. code-block:: bash salt '*' osquery.cpuid - ''' - return _osquery_cmd(table='cpuid', attrs=attrs, where=where) + """ + return _osquery_cmd(table="cpuid", attrs=attrs, where=where) def crontab(attrs=None, where=None): - ''' + """ Return crontab information from osquery CLI Example: @@ -312,12 +332,12 @@ def crontab(attrs=None, where=None): .. code-block:: bash salt '*' osquery.crontab - ''' - return _osquery_cmd(table='crontab', attrs=attrs, where=where) + """ + return _osquery_cmd(table="crontab", attrs=attrs, where=where) def etc_hosts(attrs=None, where=None): - ''' + """ Return etc_hosts information from osquery CLI Example: @@ -325,12 +345,12 @@ def etc_hosts(attrs=None, where=None): .. code-block:: bash salt '*' osquery.etc_hosts - ''' - return _osquery_cmd(table='etc_hosts', attrs=attrs, where=where) + """ + return _osquery_cmd(table="etc_hosts", attrs=attrs, where=where) def etc_services(attrs=None, where=None): - ''' + """ Return etc_services information from osquery CLI Example: @@ -338,12 +358,12 @@ def etc_services(attrs=None, where=None): .. code-block:: bash salt '*' osquery.etc_services - ''' - return _osquery_cmd(table='etc_services', attrs=attrs, where=where) + """ + return _osquery_cmd(table="etc_services", attrs=attrs, where=where) def file_changes(attrs=None, where=None): - ''' + """ Return file_changes information from osquery CLI Example: @@ -351,12 +371,12 @@ def file_changes(attrs=None, where=None): .. code-block:: bash salt '*' osquery.file_changes - ''' - return _osquery_cmd(table='file_changes', attrs=attrs, where=where) + """ + return _osquery_cmd(table="file_changes", attrs=attrs, where=where) def groups(attrs=None, where=None): - ''' + """ Return groups information from osquery CLI Example: @@ -364,12 +384,12 @@ def groups(attrs=None, where=None): .. code-block:: bash salt '*' osquery.groups - ''' - return _osquery_cmd(table='groups', attrs=attrs, where=where) + """ + return _osquery_cmd(table="groups", attrs=attrs, where=where) def hardware_events(attrs=None, where=None): - ''' + """ Return hardware_events information from osquery CLI Example: @@ -377,12 +397,12 @@ def hardware_events(attrs=None, where=None): .. code-block:: bash salt '*' osquery.hardware_events - ''' - return _osquery_cmd(table='hardware_events', attrs=attrs, where=where) + """ + return _osquery_cmd(table="hardware_events", attrs=attrs, where=where) def interface_addresses(attrs=None, where=None): - ''' + """ Return interface_addresses information from osquery CLI Example: @@ -390,12 +410,12 @@ def interface_addresses(attrs=None, where=None): .. code-block:: bash salt '*' osquery.interface_addresses - ''' - return _osquery_cmd(table='interface_addresses', attrs=attrs, where=where) + """ + return _osquery_cmd(table="interface_addresses", attrs=attrs, where=where) def interface_details(attrs=None, where=None): - ''' + """ Return interface_details information from osquery CLI Example: @@ -403,12 +423,12 @@ def interface_details(attrs=None, where=None): .. code-block:: bash salt '*' osquery.interface_details - ''' - return _osquery_cmd(table='interface_details', attrs=attrs, where=where) + """ + return _osquery_cmd(table="interface_details", attrs=attrs, where=where) def kernel_info(attrs=None, where=None): - ''' + """ Return kernel_info information from osquery CLI Example: @@ -416,12 +436,12 @@ def kernel_info(attrs=None, where=None): .. code-block:: bash salt '*' osquery.kernel_info - ''' - return _osquery_cmd(table='kernel_info', attrs=attrs, where=where) + """ + return _osquery_cmd(table="kernel_info", attrs=attrs, where=where) def last(attrs=None, where=None): - ''' + """ Return last information from osquery CLI Example: @@ -429,12 +449,12 @@ def last(attrs=None, where=None): .. code-block:: bash salt '*' osquery.last - ''' - return _osquery_cmd(table='last', attrs=attrs, where=where) + """ + return _osquery_cmd(table="last", attrs=attrs, where=where) def listening_ports(attrs=None, where=None): - r''' + r""" Return listening_ports information from osquery CLI Example: @@ -442,12 +462,12 @@ def listening_ports(attrs=None, where=None): .. code-block:: bash salt '*' osquery.listening_ports - ''' - return _osquery_cmd(table='listening_ports', attrs=attrs, where=where) + """ + return _osquery_cmd(table="listening_ports", attrs=attrs, where=where) def logged_in_users(attrs=None, where=None): - r''' + r""" Return logged_in_users information from osquery CLI Example: @@ -455,12 +475,12 @@ def logged_in_users(attrs=None, where=None): .. code-block:: bash salt '*' osquery.logged_in_users - ''' - return _osquery_cmd(table='logged_in_users', attrs=attrs, where=where) + """ + return _osquery_cmd(table="logged_in_users", attrs=attrs, where=where) def mounts(attrs=None, where=None): - r''' + r""" Return mounts information from osquery CLI Example: @@ -468,12 +488,12 @@ def mounts(attrs=None, where=None): .. code-block:: bash salt '*' osquery.mounts - ''' - return _osquery_cmd(table='mounts', attrs=attrs, where=where) + """ + return _osquery_cmd(table="mounts", attrs=attrs, where=where) def os_version(attrs=None, where=None): - ''' + """ Return os_version information from osquery CLI Example: @@ -481,12 +501,12 @@ def os_version(attrs=None, where=None): .. code-block:: bash salt '*' osquery.os_version - ''' - return _osquery_cmd(table='os_version', attrs=attrs, where=where) + """ + return _osquery_cmd(table="os_version", attrs=attrs, where=where) def passwd_changes(attrs=None, where=None): - ''' + """ Return passwd_changes information from osquery CLI Example: @@ -494,12 +514,12 @@ def passwd_changes(attrs=None, where=None): .. code-block:: bash salt '*' osquery.passwd_changes - ''' - return _osquery_cmd(table='passwd_changes', attrs=attrs, where=where) + """ + return _osquery_cmd(table="passwd_changes", attrs=attrs, where=where) def pci_devices(attrs=None, where=None): - ''' + """ Return pci_devices information from osquery CLI Example: @@ -507,12 +527,12 @@ def pci_devices(attrs=None, where=None): .. code-block:: bash salt '*' osquery.pci_devices - ''' - return _osquery_cmd(table='pci_devices', attrs=attrs, where=where) + """ + return _osquery_cmd(table="pci_devices", attrs=attrs, where=where) def process_envs(attrs=None, where=None): - ''' + """ Return process_envs information from osquery CLI Example: @@ -520,12 +540,12 @@ def process_envs(attrs=None, where=None): .. code-block:: bash salt '*' osquery.process_envs - ''' - return _osquery_cmd(table='process_envs', attrs=attrs, where=where) + """ + return _osquery_cmd(table="process_envs", attrs=attrs, where=where) def process_open_files(attrs=None, where=None): - ''' + """ Return process_open_files information from osquery CLI Example: @@ -533,12 +553,12 @@ def process_open_files(attrs=None, where=None): .. code-block:: bash salt '*' osquery.process_open_files - ''' - return _osquery_cmd(table='process_open_files', attrs=attrs, where=where) + """ + return _osquery_cmd(table="process_open_files", attrs=attrs, where=where) def process_open_sockets(attrs=None, where=None): - ''' + """ Return process_open_sockets information from osquery CLI Example: @@ -546,12 +566,12 @@ def process_open_sockets(attrs=None, where=None): .. code-block:: bash salt '*' osquery.process_open_sockets - ''' - return _osquery_cmd(table='process_open_sockets', attrs=attrs, where=where) + """ + return _osquery_cmd(table="process_open_sockets", attrs=attrs, where=where) def processes(attrs=None, where=None): - ''' + """ Return processes information from osquery CLI Example: @@ -559,12 +579,12 @@ def processes(attrs=None, where=None): .. code-block:: bash salt '*' osquery.processes - ''' - return _osquery_cmd(table='processes', attrs=attrs, where=where) + """ + return _osquery_cmd(table="processes", attrs=attrs, where=where) def routes(attrs=None, where=None): - ''' + """ Return routes information from osquery CLI Example: @@ -572,12 +592,12 @@ def routes(attrs=None, where=None): .. code-block:: bash salt '*' osquery.routes - ''' - return _osquery_cmd(table='routes', attrs=attrs, where=where) + """ + return _osquery_cmd(table="routes", attrs=attrs, where=where) def shell_history(attrs=None, where=None): - ''' + """ Return shell_history information from osquery CLI Example: @@ -585,12 +605,12 @@ def shell_history(attrs=None, where=None): .. code-block:: bash salt '*' osquery.shell_history - ''' - return _osquery_cmd(table='shell_history', attrs=attrs, where=where) + """ + return _osquery_cmd(table="shell_history", attrs=attrs, where=where) def smbios_tables(attrs=None, where=None): - ''' + """ Return smbios_tables information from osquery CLI Example: @@ -598,12 +618,12 @@ def smbios_tables(attrs=None, where=None): .. code-block:: bash salt '*' osquery.smbios_tables - ''' - return _osquery_cmd(table='smbios_tables', attrs=attrs, where=where) + """ + return _osquery_cmd(table="smbios_tables", attrs=attrs, where=where) def suid_bin(attrs=None, where=None): - ''' + """ Return suid_bin information from osquery CLI Example: @@ -611,12 +631,12 @@ def suid_bin(attrs=None, where=None): .. code-block:: bash salt '*' osquery.suid_bin - ''' - return _osquery_cmd(table='suid_bin', attrs=attrs, where=where) + """ + return _osquery_cmd(table="suid_bin", attrs=attrs, where=where) def system_controls(attrs=None, where=None): - ''' + """ Return system_controls information from osquery CLI Example: @@ -624,12 +644,12 @@ def system_controls(attrs=None, where=None): .. code-block:: bash salt '*' osquery.system_controls - ''' - return _osquery_cmd(table='system_controls', attrs=attrs, where=where) + """ + return _osquery_cmd(table="system_controls", attrs=attrs, where=where) def usb_devices(attrs=None, where=None): - ''' + """ Return usb_devices information from osquery CLI Example: @@ -637,12 +657,12 @@ def usb_devices(attrs=None, where=None): .. code-block:: bash salt '*' osquery.usb_devices - ''' - return _osquery_cmd(table='usb_devices', attrs=attrs, where=where) + """ + return _osquery_cmd(table="usb_devices", attrs=attrs, where=where) def users(attrs=None, where=None): - ''' + """ Return users information from osquery CLI Example: @@ -650,12 +670,12 @@ def users(attrs=None, where=None): .. code-block:: bash salt '*' osquery.users - ''' - return _osquery_cmd(table='users', attrs=attrs, where=where) + """ + return _osquery_cmd(table="users", attrs=attrs, where=where) def alf(attrs=None, where=None): - ''' + """ Return alf information from osquery CLI Example: @@ -663,14 +683,14 @@ def alf(attrs=None, where=None): .. code-block:: bash salt '*' osquery.alf - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='alf', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="alf", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def alf_exceptions(attrs=None, where=None): - ''' + """ Return alf_exceptions information from osquery CLI Example: @@ -678,14 +698,14 @@ def alf_exceptions(attrs=None, where=None): .. code-block:: bash salt '*' osquery.alf_exceptions - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='alf_exceptions', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="alf_exceptions", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def alf_explicit_auths(attrs=None, where=None): - ''' + """ Return alf_explicit_auths information from osquery CLI Example: @@ -693,14 +713,14 @@ def alf_explicit_auths(attrs=None, where=None): .. code-block:: bash salt '*' osquery.alf_explicit_auths - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='alf_explicit_auths', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="alf_explicit_auths", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def alf_services(attrs=None, where=None): - ''' + """ Return alf_services information from osquery CLI Example: @@ -708,14 +728,14 @@ def alf_services(attrs=None, where=None): .. code-block:: bash salt '*' osquery.alf_services - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='alf_services', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="alf_services", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def apps(attrs=None, where=None): - ''' + """ Return apps information from osquery CLI Example: @@ -723,14 +743,14 @@ def apps(attrs=None, where=None): .. code-block:: bash salt '*' osquery.apps - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='apps', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="apps", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def certificates(attrs=None, where=None): - ''' + """ Return certificates information from osquery CLI Example: @@ -738,14 +758,14 @@ def certificates(attrs=None, where=None): .. code-block:: bash salt '*' osquery.certificates - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='certificates', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="certificates", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def chrome_extensions(attrs=None, where=None): - ''' + """ Return chrome_extensions information from osquery CLI Example: @@ -753,14 +773,14 @@ def chrome_extensions(attrs=None, where=None): .. code-block:: bash salt '*' osquery.chrome_extensions - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='chrome_extensions', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="chrome_extensions", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def firefox_addons(attrs=None, where=None): - ''' + """ Return firefox_addons information from osquery CLI Example: @@ -768,14 +788,14 @@ def firefox_addons(attrs=None, where=None): .. code-block:: bash salt '*' osquery.firefox_addons - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='firefox_addons', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="firefox_addons", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def homebrew_packages(attrs=None, where=None): - ''' + """ Return homebrew_packages information from osquery CLI Example: @@ -783,14 +803,14 @@ def homebrew_packages(attrs=None, where=None): .. code-block:: bash salt '*' osquery.homebrew_packages - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='homebrew_packages', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="homebrew_packages", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def iokit_devicetree(attrs=None, where=None): - ''' + """ Return iokit_devicetree information from osquery CLI Example: @@ -798,14 +818,14 @@ def iokit_devicetree(attrs=None, where=None): .. code-block:: bash salt '*' osquery.iokit_devicetree - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='iokit_devicetree', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="iokit_devicetree", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def iokit_registry(attrs=None, where=None): - ''' + """ Return iokit_registry information from osquery CLI Example: @@ -813,14 +833,14 @@ def iokit_registry(attrs=None, where=None): .. code-block:: bash salt '*' osquery.iokit_registry - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='iokit_registry', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="iokit_registry", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def kernel_extensions(attrs=None, where=None): - ''' + """ Return kernel_extensions information from osquery CLI Example: @@ -828,14 +848,14 @@ def kernel_extensions(attrs=None, where=None): .. code-block:: bash salt '*' osquery.kernel_extensions - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='kernel_extensions', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="kernel_extensions", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def keychain_items(attrs=None, where=None): - ''' + """ Return keychain_items information from osquery CLI Example: @@ -843,14 +863,14 @@ def keychain_items(attrs=None, where=None): .. code-block:: bash salt '*' osquery.keychain_items - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='keychain_items', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="keychain_items", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def launchd(attrs=None, where=None): - ''' + """ Return launchd information from osquery CLI Example: @@ -858,14 +878,14 @@ def launchd(attrs=None, where=None): .. code-block:: bash salt '*' osquery.launchd - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='launchd', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="launchd", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def nfs_shares(attrs=None, where=None): - ''' + """ Return nfs_shares information from osquery CLI Example: @@ -873,14 +893,14 @@ def nfs_shares(attrs=None, where=None): .. code-block:: bash salt '*' osquery.nfs_shares - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='nfs_shares', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="nfs_shares", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def nvram(attrs=None, where=None): - ''' + """ Return nvram information from osquery CLI Example: @@ -888,14 +908,14 @@ def nvram(attrs=None, where=None): .. code-block:: bash salt '*' osquery.nvram - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='nvram', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="nvram", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def preferences(attrs=None, where=None): - ''' + """ Return preferences information from osquery CLI Example: @@ -903,14 +923,14 @@ def preferences(attrs=None, where=None): .. code-block:: bash salt '*' osquery.preferences - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='preferences', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="preferences", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def quarantine(attrs=None, where=None): - ''' + """ Return quarantine information from osquery CLI Example: @@ -918,14 +938,14 @@ def quarantine(attrs=None, where=None): .. code-block:: bash salt '*' osquery.quarantine - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='quarantine', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="quarantine", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def safari_extensions(attrs=None, where=None): - ''' + """ Return safari_extensions information from osquery CLI Example: @@ -933,14 +953,14 @@ def safari_extensions(attrs=None, where=None): .. code-block:: bash salt '*' osquery.safari_extensions - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='safari_extensions', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="safari_extensions", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def startup_items(attrs=None, where=None): - ''' + """ Return startup_items information from osquery CLI Example: @@ -948,14 +968,14 @@ def startup_items(attrs=None, where=None): .. code-block:: bash salt '*' osquery.startup_items - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='startup_items', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="startup_items", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def xattr_where_from(attrs=None, where=None): - ''' + """ Return xattr_where_from information from osquery CLI Example: @@ -963,14 +983,14 @@ def xattr_where_from(attrs=None, where=None): .. code-block:: bash salt '*' osquery.xattr_where_from - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='xattr_where_from', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="xattr_where_from", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def xprotect_entries(attrs=None, where=None): - ''' + """ Return xprotect_entries information from osquery CLI Example: @@ -978,14 +998,14 @@ def xprotect_entries(attrs=None, where=None): .. code-block:: bash salt '*' osquery.xprotect_entries - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='xprotect_entries', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="xprotect_entries", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def xprotect_reports(attrs=None, where=None): - ''' + """ Return xprotect_reports information from osquery CLI Example: @@ -993,14 +1013,14 @@ def xprotect_reports(attrs=None, where=None): .. code-block:: bash salt '*' osquery.xprotect_reports - ''' + """ if salt.utils.platform.is_darwin(): - return _osquery_cmd(table='xprotect_reports', attrs=attrs, where=where) - return {'result': False, 'comment': 'Only available on macOS systems.'} + return _osquery_cmd(table="xprotect_reports", attrs=attrs, where=where) + return {"result": False, "comment": "Only available on macOS systems."} def file_(attrs=None, where=None): - ''' + """ Return file information from osquery CLI Example: @@ -1008,12 +1028,12 @@ def file_(attrs=None, where=None): .. code-block:: bash salt '*' osquery.file - ''' - return _osquery_cmd(table='file', attrs=attrs, where=where) + """ + return _osquery_cmd(table="file", attrs=attrs, where=where) def hash_(attrs=None, where=None): - ''' + """ Return hash information from osquery CLI Example: @@ -1021,12 +1041,12 @@ def hash_(attrs=None, where=None): .. code-block:: bash salt '*' osquery.hash - ''' - return _osquery_cmd(table='hash', attrs=attrs, where=where) + """ + return _osquery_cmd(table="hash", attrs=attrs, where=where) def osquery_extensions(attrs=None, where=None): - ''' + """ Return osquery_extensions information from osquery CLI Example: @@ -1034,12 +1054,12 @@ def osquery_extensions(attrs=None, where=None): .. code-block:: bash salt '*' osquery.osquery_extensions - ''' - return _osquery_cmd(table='osquery_extensions', attrs=attrs, where=where) + """ + return _osquery_cmd(table="osquery_extensions", attrs=attrs, where=where) def osquery_flags(attrs=None, where=None): - ''' + """ Return osquery_flags information from osquery CLI Example: @@ -1047,12 +1067,12 @@ def osquery_flags(attrs=None, where=None): .. code-block:: bash salt '*' osquery.osquery_flags - ''' - return _osquery_cmd(table='osquery_flags', attrs=attrs, where=where) + """ + return _osquery_cmd(table="osquery_flags", attrs=attrs, where=where) def osquery_info(attrs=None, where=None): - ''' + """ Return osquery_info information from osquery CLI Example: @@ -1060,12 +1080,12 @@ def osquery_info(attrs=None, where=None): .. code-block:: bash salt '*' osquery.osquery_info - ''' - return _osquery_cmd(table='osquery_info', attrs=attrs, where=where) + """ + return _osquery_cmd(table="osquery_info", attrs=attrs, where=where) def osquery_registry(attrs=None, where=None): - ''' + """ Return osquery_registry information from osquery CLI Example: @@ -1073,12 +1093,12 @@ def osquery_registry(attrs=None, where=None): .. code-block:: bash salt '*' osquery.osquery_registry - ''' - return _osquery_cmd(table='osquery_registry', attrs=attrs, where=where) + """ + return _osquery_cmd(table="osquery_registry", attrs=attrs, where=where) def time_(attrs=None): - ''' + """ Return time information from osquery CLI Example: @@ -1086,12 +1106,12 @@ def time_(attrs=None): .. code-block:: bash salt '*' osquery.time - ''' - return _osquery_cmd(table='time', attrs=attrs) + """ + return _osquery_cmd(table="time", attrs=attrs) def query(sql=None): - ''' + """ Return time information from osquery CLI Example: @@ -1099,5 +1119,5 @@ def query(sql=None): .. code-block:: bash salt '*' osquery.query "select * from users;" - ''' + """ return _osquery(sql) diff --git a/salt/modules/out.py b/salt/modules/out.py index 792c0466895..274f1d7c0ca 100644 --- a/salt/modules/out.py +++ b/salt/modules/out.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Output Module ============= @@ -16,26 +16,28 @@ For example, inside a Jinja template: .. code-block:: jinja {{ salt.out.string_format(complex_object, out='highstate') }} -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -log = logging.getLogger(__name__) # Import salt modules import salt.output -__virtualname__ = 'out' -__proxyenabled__ = ['*'] +log = logging.getLogger(__name__) + + +__virtualname__ = "out" +__proxyenabled__ = ["*"] def __virtual__(): return __virtualname__ -def out_format(data, out='nested', opts=None, **kwargs): - ''' +def out_format(data, out="nested", opts=None, **kwargs): + """ Return the formatted outputter string for the Python object. data @@ -55,14 +57,14 @@ def out_format(data, out='nested', opts=None, **kwargs): .. code-block:: bash salt '*' out.out_format "{'key': 'value'}" - ''' + """ if not opts: opts = __opts__ return salt.output.out_format(data, out, opts=opts, **kwargs) -def string_format(data, out='nested', opts=None, **kwargs): - ''' +def string_format(data, out="nested", opts=None, **kwargs): + """ Return the outputter formatted string, removing the ANSI escape sequences. data @@ -82,14 +84,14 @@ def string_format(data, out='nested', opts=None, **kwargs): .. code-block:: bash salt '*' out.string_format "{'key': 'value'}" out=table - ''' + """ if not opts: opts = __opts__ return salt.output.string_format(data, out, opts=opts, **kwargs) -def html_format(data, out='nested', opts=None, **kwargs): - ''' +def html_format(data, out="nested", opts=None, **kwargs): + """ Return the formatted string as HTML. data @@ -109,7 +111,7 @@ def html_format(data, out='nested', opts=None, **kwargs): .. code-block:: bash salt '*' out.html_format "{'key': 'value'}" out=yaml - ''' + """ if not opts: opts = __opts__ return salt.output.html_format(data, out, opts=opts, **kwargs) diff --git a/salt/modules/pacmanpkg.py b/salt/modules/pacmanpkg.py index e30296e8c8c..41eb7b54bb7 100644 --- a/salt/modules/pacmanpkg.py +++ b/salt/modules/pacmanpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A module to wrap pacman calls, since Arch is the best (https://wiki.archlinux.org/index.php/Arch_is_the_best) @@ -8,10 +8,11 @@ A module to wrap pacman calls, since Arch is the best minion, and it is using a different module (or gives an error similar to *'pkg.install' is not available*), see :ref:`here `. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import fnmatch import logging @@ -25,35 +26,35 @@ import salt.utils.itertools import salt.utils.pkg import salt.utils.systemd from salt.exceptions import CommandExecutionError, MinionError -from salt.utils.versions import LooseVersion as _LooseVersion # Import 3rd-party libs from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Set the virtual pkg module if the os is Arch - ''' - if __grains__['os_family'] == 'Arch': + """ + if __grains__["os_family"] == "Arch": return __virtualname__ - return (False, 'The pacman module could not be loaded: unsupported OS family.') + return (False, "The pacman module could not be loaded: unsupported OS family.") def _list_removed(old, new): - ''' + """ List the packages which have been removed between the two package objects - ''' + """ return [x for x in old if x not in new] def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -67,11 +68,11 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version ... - ''' - refresh = salt.utils.data.is_true(kwargs.pop('refresh', False)) + """ + refresh = salt.utils.data.is_true(kwargs.pop("refresh", False)) if len(names) == 0: - return '' + return "" # Refresh before looking for the latest version available if refresh: @@ -80,17 +81,15 @@ def latest_version(*names, **kwargs): ret = {} # Initialize the dict with empty strings for name in names: - ret[name] = '' - cmd = ['pacman', '-Sp', '--needed', '--print-format', '%n %v'] + ret[name] = "" + cmd = ["pacman", "-Sp", "--needed", "--print-format", "%n %v"] cmd.extend(names) - if 'root' in kwargs: - cmd.extend(('-r', kwargs['root'])) + if "root" in kwargs: + cmd.extend(("-r", kwargs["root"])) - out = __salt__['cmd.run_stdout'](cmd, - output_loglevel='trace', - python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): + out = __salt__["cmd.run_stdout"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): try: name, version_num = line.split() # Only add to return dict if package is in the list of packages @@ -108,11 +107,13 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def upgrade_available(name): - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -120,12 +121,12 @@ def upgrade_available(name): .. code-block:: bash salt '*' pkg.upgrade_available - ''' - return latest_version(name) != '' + """ + return latest_version(name) != "" def list_upgrades(refresh=False, root=None, **kwargs): # pylint: disable=W0613 - ''' + """ List all available package upgrades on this system CLI Example: @@ -133,38 +134,36 @@ def list_upgrades(refresh=False, root=None, **kwargs): # pylint: disable=W0613 .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ upgrades = {} - cmd = ['pacman', '-S', '-p', '-u', '--print-format', '%n %v'] + cmd = ["pacman", "-S", "-p", "-u", "--print-format", "%n %v"] if root is not None: - cmd.extend(('-r', root)) + cmd.extend(("-r", root)) if refresh: - cmd.append('-y') + cmd.append("-y") - call = __salt__['cmd.run_all'](cmd, - python_shell=False, - output_loglevel='trace') + call = __salt__["cmd.run_all"](cmd, python_shell=False, output_loglevel="trace") - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] - if 'stdout' in call: - comment += call['stdout'] + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] + if "stdout" in call: + comment += call["stdout"] if comment: - comment = ': ' + comment - raise CommandExecutionError('Error listing upgrades' + comment) + comment = ": " + comment + raise CommandExecutionError("Error listing upgrades" + comment) else: - out = call['stdout'] + out = call["stdout"] - for line in salt.utils.itertools.split(out, '\n'): + for line in salt.utils.itertools.split(out, "\n"): try: pkgname, pkgver = line.split() except ValueError: continue - if pkgname.lower() == 'downloading' and '.db' in pkgver.lower(): + if pkgname.lower() == "downloading" and ".db" in pkgver.lower(): # Antergos (and possibly other Arch derivatives) add lines when pkg # metadata is being downloaded. Because these lines, when split, # contain two columns (i.e. 'downloading community.db...'), we will @@ -175,7 +174,7 @@ def list_upgrades(refresh=False, root=None, **kwargs): # pylint: disable=W0613 def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -186,12 +185,12 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed as a dict:: {'': ''} @@ -201,48 +200,51 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret - cmd = ['pacman', '-Q'] + cmd = ["pacman", "-Q"] - if 'root' in kwargs: - cmd.extend(('-r', kwargs['root'])) + if "root" in kwargs: + cmd.extend(("-r", kwargs["root"])) ret = {} - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): if not line: continue try: name, version_num = line.split()[0:2] except ValueError: - log.error('Problem parsing pacman -Q: Unexpected formatting in ' - 'line: \'%s\'', line) + log.error( + "Problem parsing pacman -Q: Unexpected formatting in " "line: '%s'", + line, + ) else: - __salt__['pkg_resource.add_pkg'](ret, name, version_num) + __salt__["pkg_resource.add_pkg"](ret, name, version_num) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def group_list(): - ''' + """ .. versionadded:: 2016.11.0 Lists all groups known by pacman on this system @@ -252,43 +254,45 @@ def group_list(): .. code-block:: bash salt '*' pkg.group_list - ''' + """ - ret = {'installed': [], - 'partially_installed': [], - 'available': []} + ret = {"installed": [], "partially_installed": [], "available": []} # find out what's available - cmd = ['pacman', '-Sgg'] - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) + cmd = ["pacman", "-Sgg"] + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) available = {} - for line in salt.utils.itertools.split(out, '\n'): + for line in salt.utils.itertools.split(out, "\n"): if not line: continue try: group, pkg = line.split()[0:2] except ValueError: - log.error('Problem parsing pacman -Sgg: Unexpected formatting in ' - 'line: \'%s\'', line) + log.error( + "Problem parsing pacman -Sgg: Unexpected formatting in " "line: '%s'", + line, + ) else: available.setdefault(group, []).append(pkg) # now get what's installed - cmd = ['pacman', '-Qg'] - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) + cmd = ["pacman", "-Qg"] + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) installed = {} - for line in salt.utils.itertools.split(out, '\n'): + for line in salt.utils.itertools.split(out, "\n"): if not line: continue try: group, pkg = line.split()[0:2] except ValueError: - log.error('Problem parsing pacman -Qg: Unexpected formatting in ' - 'line: \'%s\'', line) + log.error( + "Problem parsing pacman -Qg: Unexpected formatting in " "line: '%s'", + line, + ) else: installed.setdefault(group, []).append(pkg) @@ -297,29 +301,31 @@ def group_list(): for group in installed: if group not in available: log.error( - 'Pacman reports group %s installed, but it is not in the ' - 'available list (%s)!', group, available + "Pacman reports group %s installed, but it is not in the " + "available list (%s)!", + group, + available, ) continue if len(installed[group]) == len(available[group]): - ret['installed'].append(group) + ret["installed"].append(group) else: - ret['partially_installed'].append(group) + ret["partially_installed"].append(group) available.pop(group) - ret['installed'].sort() - ret['partially_installed'].sort() + ret["installed"].sort() + ret["partially_installed"].sort() # Now installed and partially installed are set, whatever is left is the available list. # In Python 3, .keys() returns an iterable view instead of a list. sort() cannot be # called on views. Use sorted() instead. Plus it's just as efficient as sort(). - ret['available'] = sorted(available.keys()) + ret["available"] = sorted(available.keys()) return ret def group_info(name): - ''' + """ .. versionadded:: 2016.11.0 Lists all packages in the specified group @@ -329,26 +335,28 @@ def group_info(name): .. code-block:: bash salt '*' pkg.group_info 'xorg' - ''' + """ - pkgtypes = ('mandatory', 'optional', 'default', 'conditional') + pkgtypes = ("mandatory", "optional", "default", "conditional") ret = {} for pkgtype in pkgtypes: ret[pkgtype] = set() - cmd = ['pacman', '-Sgg', name] - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) + cmd = ["pacman", "-Sgg", name] + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): + for line in salt.utils.itertools.split(out, "\n"): if not line: continue try: pkg = line.split()[1] except ValueError: - log.error('Problem parsing pacman -Sgg: Unexpected formatting in ' - 'line: \'%s\'', line) + log.error( + "Problem parsing pacman -Sgg: Unexpected formatting in " "line: '%s'", + line, + ) else: - ret['default'].add(pkg) + ret["default"].add(pkg) for pkgtype in pkgtypes: ret[pkgtype] = sorted(ret[pkgtype]) @@ -358,7 +366,7 @@ def group_info(name): def group_diff(name): - ''' + """ .. versionadded:: 2016.11.0 Lists which of a group's packages are installed and which are not @@ -371,30 +379,30 @@ def group_diff(name): .. code-block:: bash salt '*' pkg.group_diff 'xorg' - ''' + """ # Use a compatible structure with yum, so we can leverage the existing state.group_installed # In pacmanworld, everything is the default, but nothing is mandatory - pkgtypes = ('mandatory', 'optional', 'default', 'conditional') + pkgtypes = ("mandatory", "optional", "default", "conditional") ret = {} for pkgtype in pkgtypes: - ret[pkgtype] = {'installed': [], 'not installed': []} + ret[pkgtype] = {"installed": [], "not installed": []} # use indirect references to simplify unit testing - pkgs = __salt__['pkg.list_pkgs']() - group_pkgs = __salt__['pkg.group_info'](name) + pkgs = __salt__["pkg.list_pkgs"]() + group_pkgs = __salt__["pkg.group_info"](name) for pkgtype in pkgtypes: for member in group_pkgs.get(pkgtype, []): if member in pkgs: - ret[pkgtype]['installed'].append(member) + ret[pkgtype]["installed"].append(member) else: - ret[pkgtype]['not installed'].append(member) + ret[pkgtype]["not installed"].append(member) return ret def refresh_db(root=None): - ''' + """ Just run a ``pacman -Sy``, return a dict:: {'': Bool} @@ -404,50 +412,44 @@ def refresh_db(root=None): .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) - cmd = ['pacman', '-Sy'] + cmd = ["pacman", "-Sy"] if root is not None: - cmd.extend(('-r', root)) + cmd.extend(("-r", root)) ret = {} - call = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - env={'LANG': 'C'}, - python_shell=False) - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += ': ' + call['stderr'] - raise CommandExecutionError( - 'Error refreshing package database' + comment - ) + call = __salt__["cmd.run_all"]( + cmd, output_loglevel="trace", env={"LANG": "C"}, python_shell=False + ) + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += ": " + call["stderr"] + raise CommandExecutionError("Error refreshing package database" + comment) else: - out = call['stdout'] + out = call["stdout"] - for line in salt.utils.itertools.split(out, '\n'): - if line.strip().startswith('::'): + for line in salt.utils.itertools.split(out, "\n"): + if line.strip().startswith("::"): continue if not line: continue key = line.strip().split()[0] - if 'is up to date' in line: + if "is up to date" in line: ret[key] = False - elif 'downloading' in line: - key = line.strip().split()[1].split('.')[0] + elif "downloading" in line: + key = line.strip().split()[1].split(".")[0] ret[key] = True return ret -def install(name=None, - refresh=False, - sysupgrade=None, - pkgs=None, - sources=None, - **kwargs): - ''' +def install( + name=None, refresh=False, sysupgrade=None, pkgs=None, sources=None, **kwargs +): + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -521,9 +523,9 @@ def install(name=None, {'': {'old': '', 'new': ''}} - ''' + """ try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: @@ -532,28 +534,29 @@ def install(name=None, if pkg_params is None or len(pkg_params) == 0: return {} - if 'root' in kwargs: - pkg_params['-r'] = kwargs['root'] + if "root" in kwargs: + pkg_params["-r"] = kwargs["root"] cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) - cmd.append('pacman') + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) + cmd.append("pacman") targets = [] errors = [] targets = [] - if pkg_type == 'file': - cmd.extend(['-U', '--noprogressbar', '--noconfirm']) + if pkg_type == "file": + cmd.extend(["-U", "--noprogressbar", "--noconfirm"]) cmd.extend(pkg_params) - elif pkg_type == 'repository': - cmd.append('-S') + elif pkg_type == "repository": + cmd.append("-S") if refresh is True: - cmd.append('-y') + cmd.append("-y") if sysupgrade is True or (sysupgrade is None and refresh is True): - cmd.append('-u') - cmd.extend(['--noprogressbar', '--noconfirm', '--needed']) + cmd.append("-u") + cmd.extend(["--noprogressbar", "--noconfirm", "--needed"]) wildcards = [] for param, version_num in six.iteritems(pkg_params): if version_num is None: @@ -561,18 +564,18 @@ def install(name=None, else: prefix, verstr = salt.utils.pkg.split_comparison(version_num) if not prefix: - prefix = '=' - if '*' in verstr: - if prefix == '=': + prefix = "=" + if "*" in verstr: + if prefix == "=": wildcards.append((param, verstr)) else: errors.append( - 'Invalid wildcard for {0}{1}{2}'.format( + "Invalid wildcard for {0}{1}{2}".format( param, prefix, verstr ) ) continue - targets.append('{0}{1}{2}'.format(param, prefix, verstr)) + targets.append("{0}{1}{2}".format(param, prefix, verstr)) if wildcards: # Resolve wildcard matches @@ -581,21 +584,21 @@ def install(name=None, candidates = _available.get(pkgname, []) match = salt.utils.itertools.fnmatch_multiple(candidates, verstr) if match is not None: - targets.append('='.join((pkgname, match))) + targets.append("=".join((pkgname, match))) else: errors.append( - 'No version matching \'{0}\' found for package \'{1}\' ' - '(available: {2})'.format( + "No version matching '{0}' found for package '{1}' " + "(available: {2})".format( verstr, pkgname, - ', '.join(candidates) if candidates else 'none' + ", ".join(candidates) if candidates else "none", ) ) if refresh: try: # Prevent a second refresh when we run the install command - cmd.remove('-y') + cmd.remove("-y") except ValueError: # Shouldn't happen since we only add -y when refresh is True, # but just in case that code above is inadvertently changed, @@ -605,18 +608,14 @@ def install(name=None, if not errors: cmd.extend(targets) old = list_pkgs() - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False - ) + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) @@ -628,15 +627,15 @@ def install(name=None, # there are no changes. changes = {} raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': changes} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": changes}, ) return ret def upgrade(refresh=False, root=None, **kwargs): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -669,47 +668,44 @@ def upgrade(refresh=False, root=None, **kwargs): .. code-block:: bash salt '*' pkg.upgrade - ''' - ret = {'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"changes": {}, "result": True, "comment": ""} old = list_pkgs() cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) - cmd.extend(['pacman', '-Su', '--noprogressbar', '--noconfirm']) + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) + cmd.extend(["pacman", "-Su", "--noprogressbar", "--noconfirm"]) if salt.utils.data.is_true(refresh): - cmd.append('-y') + cmd.append("-y") if root is not None: - cmd.extend(('-r', root)) + cmd.extend(("-r", root)) - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - __context__.pop('pkg.list_pkgs', None) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret -def _uninstall(action='remove', name=None, pkgs=None, **kwargs): - ''' +def _uninstall(action="remove", name=None, pkgs=None, **kwargs): + """ remove and purge do identical things but with different pacman commands, this function performs the common logic. - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -718,44 +714,41 @@ def _uninstall(action='remove', name=None, pkgs=None, **kwargs): if not targets: return {} - remove_arg = '-Rsc' if action == 'purge' else '-R' + remove_arg = "-Rsc" if action == "purge" else "-R" cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) - cmd.extend(['pacman', remove_arg, '--noprogressbar', '--noconfirm']) + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) + cmd.extend(["pacman", remove_arg, "--noprogressbar", "--noconfirm"]) cmd.extend(targets) - if 'root' in kwargs: - cmd.extend(('-r', kwargs['root'])) + if "root" in kwargs: + cmd.extend(("-r", kwargs["root"])) - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False - ) + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def remove(name=None, pkgs=None, **kwargs): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -794,12 +787,12 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' - return _uninstall(action='remove', name=name, pkgs=pkgs) + """ + return _uninstall(action="remove", name=name, pkgs=pkgs) def purge(name=None, pkgs=None, **kwargs): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -839,12 +832,12 @@ def purge(name=None, pkgs=None, **kwargs): salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' - return _uninstall(action='purge', name=name, pkgs=pkgs) + """ + return _uninstall(action="purge", name=name, pkgs=pkgs) def file_list(*packages): - ''' + """ List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -856,29 +849,29 @@ def file_list(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' + """ errors = [] ret = [] - cmd = ['pacman', '-Ql'] + cmd = ["pacman", "-Ql"] if len(packages) > 0 and os.path.exists(packages[0]): packages = list(packages) - cmd.extend(('-r', packages.pop(0))) + cmd.extend(("-r", packages.pop(0))) cmd.extend(packages) - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): - if line.startswith('error'): + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): + if line.startswith("error"): errors.append(line) else: comps = line.split() - ret.append(' '.join(comps[1:])) - return {'errors': errors, 'files': ret} + ret.append(" ".join(comps[1:])) + return {"errors": errors, "files": ret} def file_dict(*packages): - ''' + """ List the files that belong to a package, grouped by package. Not specifying any packages will return a list of _every_ file on the system's package database (not generally recommended). @@ -890,31 +883,31 @@ def file_dict(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' + """ errors = [] ret = {} - cmd = ['pacman', '-Ql'] + cmd = ["pacman", "-Ql"] if len(packages) > 0 and os.path.exists(packages[0]): packages = list(packages) - cmd.extend(('-r', packages.pop(0))) + cmd.extend(("-r", packages.pop(0))) cmd.extend(packages) - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): - if line.startswith('error'): + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): + if line.startswith("error"): errors.append(line) else: comps = line.split() if not comps[0] in ret: ret[comps[0]] = [] - ret[comps[0]].append((' '.join(comps[1:]))) - return {'errors': errors, 'packages': ret} + ret[comps[0]].append((" ".join(comps[1:]))) + return {"errors": errors, "packages": ret} def owner(*paths): - ''' + """ .. versionadded:: 2014.7.0 Return the name of the package that owns the file. Multiple file paths can @@ -929,22 +922,21 @@ def owner(*paths): salt '*' pkg.owner /usr/bin/apachectl salt '*' pkg.owner /usr/bin/apachectl /usr/bin/zsh - ''' + """ if not paths: - return '' + return "" ret = {} - cmd_prefix = ['pacman', '-Qqo'] + cmd_prefix = ["pacman", "-Qqo"] for path in paths: - ret[path] = __salt__['cmd.run_stdout'](cmd_prefix + [path], - python_shell=False) + ret[path] = __salt__["cmd.run_stdout"](cmd_prefix + [path], python_shell=False) if len(ret) == 1: return next(six.itervalues(ret)) return ret def list_repo_pkgs(*args, **kwargs): - ''' + """ Returns all available packages. Optionally, package names (and name globs) can be passed and the results will be filtered to packages matching those names. @@ -995,34 +987,34 @@ def list_repo_pkgs(*args, **kwargs): salt '*' pkg.list_repo_pkgs foo bar baz salt '*' pkg.list_repo_pkgs 'samba4*' fromrepo=base,updates salt '*' pkg.list_repo_pkgs 'python2-*' byrepo=True - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - fromrepo = kwargs.pop('fromrepo', '') or '' - byrepo = kwargs.pop('byrepo', False) - refresh = kwargs.pop('refresh', False) + fromrepo = kwargs.pop("fromrepo", "") or "" + byrepo = kwargs.pop("byrepo", False) + refresh = kwargs.pop("refresh", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if fromrepo: try: - repos = [x.strip() for x in fromrepo.split(',')] + repos = [x.strip() for x in fromrepo.split(",")] except AttributeError: - repos = [x.strip() for x in six.text_type(fromrepo).split(',')] + repos = [x.strip() for x in six.text_type(fromrepo).split(",")] else: repos = [] if refresh: refresh_db() - out = __salt__['cmd.run_all']( - ['pacman', '-Sl'], - output_loglevel='trace', + out = __salt__["cmd.run_all"]( + ["pacman", "-Sl"], + output_loglevel="trace", ignore_retcode=True, - python_shell=False + python_shell=False, ) ret = {} - for line in salt.utils.itertools.split(out['stdout'], '\n'): + for line in salt.utils.itertools.split(out["stdout"], "\n"): try: repo, pkg_name, pkg_ver = line.strip().split()[:3] except ValueError: @@ -1047,8 +1039,7 @@ def list_repo_pkgs(*args, **kwargs): # Sort versions newest to oldest for pkgname in ret[reponame]: sorted_versions = sorted( - [_LooseVersion(x) for x in ret[reponame][pkgname]], - reverse=True + [_LooseVersion(x) for x in ret[reponame][pkgname]], reverse=True ) ret[reponame][pkgname] = [x.vstring for x in sorted_versions] return ret @@ -1059,8 +1050,7 @@ def list_repo_pkgs(*args, **kwargs): byrepo_ret.setdefault(pkgname, []).extend(ret[reponame][pkgname]) for pkgname in byrepo_ret: sorted_versions = sorted( - [_LooseVersion(x) for x in byrepo_ret[pkgname]], - reverse=True + [_LooseVersion(x) for x in byrepo_ret[pkgname]], reverse=True ) byrepo_ret[pkgname] = [x.vstring for x in sorted_versions] return byrepo_ret diff --git a/salt/modules/pagerduty.py b/salt/modules/pagerduty.py index 6dacd42e168..04dc6b1e444 100644 --- a/salt/modules/pagerduty.py +++ b/salt/modules/pagerduty.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for Firing Events via PagerDuty .. versionadded:: 2014.1.0 @@ -15,7 +15,7 @@ Module for Firing Events via PagerDuty my-pagerduty-account: pagerduty.api_key: F3Rbyjbve43rfFWf2214 pagerduty.subdomain: mysubdomain -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs @@ -28,127 +28,116 @@ from salt.ext import six def __virtual__(): - ''' + """ No dependencies outside of what Salt itself requires - ''' + """ return True def list_services(profile=None, api_key=None): - ''' + """ List services belonging to this account CLI Example: salt myminion pagerduty.list_services my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'services', - 'name', - __salt__['config.option'](profile), - api_key, - opts=__opts__ + "services", "name", __salt__["config.option"](profile), api_key, opts=__opts__ ) def list_incidents(profile=None, api_key=None): - ''' + """ List incidents belonging to this account CLI Example: salt myminion pagerduty.list_incidents my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'incidents', - 'id', - __salt__['config.option'](profile), - api_key, - opts=__opts__ + "incidents", "id", __salt__["config.option"](profile), api_key, opts=__opts__ ) def list_users(profile=None, api_key=None): - ''' + """ List users belonging to this account CLI Example: salt myminion pagerduty.list_users my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'users', - 'id', - __salt__['config.option'](profile), - api_key, - opts=__opts__ + "users", "id", __salt__["config.option"](profile), api_key, opts=__opts__ ) def list_schedules(profile=None, api_key=None): - ''' + """ List schedules belonging to this account CLI Example: salt myminion pagerduty.list_schedules my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'schedules', - 'id', - __salt__['config.option'](profile), - api_key, - opts=__opts__ + "schedules", "id", __salt__["config.option"](profile), api_key, opts=__opts__ ) def list_windows(profile=None, api_key=None): - ''' + """ List maintenance windows belonging to this account CLI Example: salt myminion pagerduty.list_windows my-pagerduty-account salt myminion pagerduty.list_maintenance_windows my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'maintenance_windows', - 'id', - __salt__['config.option'](profile), + "maintenance_windows", + "id", + __salt__["config.option"](profile), api_key, - opts=__opts__ + opts=__opts__, ) # The long version, added for consistency -list_maintenance_windows = salt.utils.functools.alias_function(list_windows, 'list_maintenance_windows') +list_maintenance_windows = salt.utils.functools.alias_function( + list_windows, "list_maintenance_windows" +) def list_policies(profile=None, api_key=None): - ''' + """ List escalation policies belonging to this account CLI Example: salt myminion pagerduty.list_policies my-pagerduty-account salt myminion pagerduty.list_escalation_policies my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'escalation_policies', - 'id', - __salt__['config.option'](profile), + "escalation_policies", + "id", + __salt__["config.option"](profile), api_key, - opts=__opts__ + opts=__opts__, ) # The long version, added for consistency -list_escalation_policies = salt.utils.functools.alias_function(list_policies, 'list_escalation_policies') +list_escalation_policies = salt.utils.functools.alias_function( + list_policies, "list_escalation_policies" +) -def create_event(service_key=None, description=None, details=None, - incident_key=None, profile=None): - ''' +def create_event( + service_key=None, description=None, details=None, incident_key=None, profile=None +): + """ Create an event in PagerDuty. Designed for use in states. CLI Example: @@ -172,26 +161,28 @@ def create_event(service_key=None, description=None, details=None, profile This refers to the configuration profile to use to connect to the PagerDuty service. - ''' - trigger_url = 'https://events.pagerduty.com/generic/2010-04-15/create_event.json' + """ + trigger_url = "https://events.pagerduty.com/generic/2010-04-15/create_event.json" if isinstance(details, six.string_types): details = salt.utils.yaml.safe_load(details) if isinstance(details, six.string_types): - details = {'details': details} + details = {"details": details} - ret = salt.utils.json.loads(salt.utils.pagerduty.query( - method='POST', - profile_dict=__salt__['config.option'](profile), - api_key=service_key, - data={ - 'service_key': service_key, - 'incident_key': incident_key, - 'event_type': 'trigger', - 'description': description, - 'details': details, - }, - url=trigger_url, - opts=__opts__ - )) + ret = salt.utils.json.loads( + salt.utils.pagerduty.query( + method="POST", + profile_dict=__salt__["config.option"](profile), + api_key=service_key, + data={ + "service_key": service_key, + "incident_key": incident_key, + "event_type": "trigger", + "description": description, + "details": details, + }, + url=trigger_url, + opts=__opts__, + ) + ) return ret diff --git a/salt/modules/pagerduty_util.py b/salt/modules/pagerduty_util.py index 2cb16c4e53e..5b88f45cfa0 100644 --- a/salt/modules/pagerduty_util.py +++ b/salt/modules/pagerduty_util.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for manageing PagerDuty resource :configuration: This module can be used by specifying the name of a @@ -17,7 +17,7 @@ Module for manageing PagerDuty resource For PagerDuty API details, see https://developer.pagerduty.com/documentation/rest -''' +""" from __future__ import absolute_import, print_function, unicode_literals import requests @@ -25,78 +25,66 @@ import salt.utils.json def __virtual__(): - ''' + """ No dependencies outside of what Salt itself requires - ''' + """ return True -def get_users(profile='pagerduty', subdomain=None, api_key=None): - ''' +def get_users(profile="pagerduty", subdomain=None, api_key=None): + """ List users belonging to this account CLI Example: salt myminion pagerduty.get_users - ''' + """ return _list_items( - 'users', - 'id', - profile=profile, - subdomain=subdomain, - api_key=api_key, + "users", "id", profile=profile, subdomain=subdomain, api_key=api_key, ) -def get_services(profile='pagerduty', subdomain=None, api_key=None): - ''' +def get_services(profile="pagerduty", subdomain=None, api_key=None): + """ List services belonging to this account CLI Example: salt myminion pagerduty.get_services - ''' + """ return _list_items( - 'services', - 'id', - profile=profile, - subdomain=subdomain, - api_key=api_key, + "services", "id", profile=profile, subdomain=subdomain, api_key=api_key, ) -def get_schedules(profile='pagerduty', subdomain=None, api_key=None): - ''' +def get_schedules(profile="pagerduty", subdomain=None, api_key=None): + """ List schedules belonging to this account CLI Example: salt myminion pagerduty.get_schedules - ''' + """ return _list_items( - 'schedules', - 'id', - profile=profile, - subdomain=subdomain, - api_key=api_key, + "schedules", "id", profile=profile, subdomain=subdomain, api_key=api_key, ) -def get_escalation_policies(profile='pagerduty', subdomain=None, api_key=None): - ''' +def get_escalation_policies(profile="pagerduty", subdomain=None, api_key=None): + """ List escalation_policies belonging to this account CLI Example: salt myminion pagerduty.get_escalation_policies - ''' + """ return _list_items( - 'escalation_policies', - 'id', + "escalation_policies", + "id", profile=profile, subdomain=subdomain, api_key=api_key, @@ -104,46 +92,49 @@ def get_escalation_policies(profile='pagerduty', subdomain=None, api_key=None): def _list_items(action, key, profile=None, subdomain=None, api_key=None): - ''' + """ List items belonging to an API call. This method should be in utils.pagerduty. - ''' - items = _query( - profile=profile, - subdomain=subdomain, - api_key=api_key, - action=action - ) + """ + items = _query(profile=profile, subdomain=subdomain, api_key=api_key, action=action) ret = {} for item in items[action]: ret[item[key]] = item return ret -def _query(method='GET', profile=None, url=None, path='api/v1', - action=None, api_key=None, service=None, params=None, - data=None, subdomain=None, verify_ssl=True): - ''' +def _query( + method="GET", + profile=None, + url=None, + path="api/v1", + action=None, + api_key=None, + service=None, + params=None, + data=None, + subdomain=None, + verify_ssl=True, +): + """ Query the PagerDuty API. This method should be in utils.pagerduty. - ''' + """ if profile: - creds = __salt__['config.option'](profile) + creds = __salt__["config.option"](profile) else: creds = { - 'pagerduty.api_key': api_key, - 'pagerduty.subdomain': subdomain, + "pagerduty.api_key": api_key, + "pagerduty.subdomain": subdomain, } if url is None: - url = 'https://{0}.pagerduty.com/{1}/{2}'.format( - creds['pagerduty.subdomain'], - path, - action + url = "https://{0}.pagerduty.com/{1}/{2}".format( + creds["pagerduty.subdomain"], path, action ) if params is None: @@ -152,12 +143,10 @@ def _query(method='GET', profile=None, url=None, path='api/v1', if data is None: data = {} - headers = { - 'Authorization': 'Token token={0}'.format(creds['pagerduty.api_key']) - } + headers = {"Authorization": "Token token={0}".format(creds["pagerduty.api_key"])} - if method != 'GET': - headers['Content-type'] = 'application/json' + if method != "GET": + headers["Content-type"] = "application/json" result = requests.request( method, @@ -165,30 +154,32 @@ def _query(method='GET', profile=None, url=None, path='api/v1', headers=headers, params=params, data=salt.utils.json.dumps(data), - verify=verify_ssl + verify=verify_ssl, ) - if result.text is None or result.text == '': + if result.text is None or result.text == "": return None result_json = result.json() # if this query supports pagination, loop and fetch all results, merge them together - if 'total' in result_json and 'offset' in result_json and 'limit' in result_json: - offset = result_json['offset'] - limit = result_json['limit'] - total = result_json['total'] + if "total" in result_json and "offset" in result_json and "limit" in result_json: + offset = result_json["offset"] + limit = result_json["limit"] + total = result_json["total"] while offset + limit < total: offset = offset + limit limit = 100 - data['offset'] = offset - data['limit'] = limit - next_page_results = requests.request(method, - url, - headers=headers, - params=params, - data=data, # Already serialized above, don't do it again - verify=verify_ssl).json() - offset = next_page_results['offset'] - limit = next_page_results['limit'] + data["offset"] = offset + data["limit"] = limit + next_page_results = requests.request( + method, + url, + headers=headers, + params=params, + data=data, # Already serialized above, don't do it again + verify=verify_ssl, + ).json() + offset = next_page_results["offset"] + limit = next_page_results["limit"] # merge results for k, v in result_json.items(): if isinstance(v, list): @@ -197,18 +188,25 @@ def _query(method='GET', profile=None, url=None, path='api/v1', def _get_resource_id(resource): - ''' + """ helper method to find the resource id, since PD API doesn't always return it in the same way - ''' - if 'id' in resource: - return resource['id'] - if 'schedule' in resource: - return resource['schedule']['id'] + """ + if "id" in resource: + return resource["id"] + if "schedule" in resource: + return resource["schedule"]["id"] return None -def get_resource(resource_name, key, identifier_fields, profile='pagerduty', subdomain=None, api_key=None): - ''' +def get_resource( + resource_name, + key, + identifier_fields, + profile="pagerduty", + subdomain=None, + api_key=None, +): + """ Get any single pagerduty resource by key. We allow flexible lookup by any of a list of identifier_fields. @@ -224,36 +222,45 @@ def get_resource(resource_name, key, identifier_fields, profile='pagerduty', sub This method is implemented by getting all objects of the resource type (cached into __context__), then brute force searching through the list and trying to match any of the identifier_fields. The __context__ cache is purged after any create, update or delete to the resource. - ''' + """ # cache the expensive 'get all resources' calls into __context__ so that we do them once per salt run - if 'pagerduty_util.resource_cache' not in __context__: - __context__['pagerduty_util.resource_cache'] = {} - if resource_name not in __context__['pagerduty_util.resource_cache']: - if resource_name == 'services': - action = resource_name + '?include[]=escalation_policy' + if "pagerduty_util.resource_cache" not in __context__: + __context__["pagerduty_util.resource_cache"] = {} + if resource_name not in __context__["pagerduty_util.resource_cache"]: + if resource_name == "services": + action = resource_name + "?include[]=escalation_policy" else: action = resource_name - __context__['pagerduty_util.resource_cache'][resource_name] = _query(action=action, - profile=profile, - subdomain=subdomain, - api_key=api_key)[resource_name] - for resource in __context__['pagerduty_util.resource_cache'][resource_name]: + __context__["pagerduty_util.resource_cache"][resource_name] = _query( + action=action, profile=profile, subdomain=subdomain, api_key=api_key + )[resource_name] + for resource in __context__["pagerduty_util.resource_cache"][resource_name]: for field in identifier_fields: if resource[field] == key: # PagerDuty's /schedules endpoint returns less data than /schedules/:id. # so, now that we found the schedule, we need to get all the data for it. - if resource_name == 'schedules': - full_resource_info = _query(action='{0}/{1}'.format(resource_name, resource['id']), - profile=profile, - subdomain=subdomain, - api_key=api_key) + if resource_name == "schedules": + full_resource_info = _query( + action="{0}/{1}".format(resource_name, resource["id"]), + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) return full_resource_info return resource return None -def create_or_update_resource(resource_name, identifier_fields, data, diff=None, profile='pagerduty', subdomain=None, api_key=None): - ''' +def create_or_update_resource( + resource_name, + identifier_fields, + data, + diff=None, + profile="pagerduty", + subdomain=None, + api_key=None, +): + """ create or update any pagerduty resource Helper method for present(). @@ -267,22 +274,36 @@ def create_or_update_resource(resource_name, identifier_fields, data, diff=None, create_or_update_resource("user", ["id","name","email"]) create_or_update_resource("escalation_policies", ["id","name"], diff=my_diff_function) - ''' + """ # try to locate the resource by any of the identifier_fields that are specified in data resource = None for field in identifier_fields: if field in data: - resource = get_resource(resource_name, data[field], identifier_fields, profile, subdomain, api_key) + resource = get_resource( + resource_name, + data[field], + identifier_fields, + profile, + subdomain, + api_key, + ) if resource is not None: break if resource is None: - if __opts__['test']: - return 'would create' + if __opts__["test"]: + return "would create" # flush the resource_cache, because we're modifying a resource - del __context__['pagerduty_util.resource_cache'][resource_name] + del __context__["pagerduty_util.resource_cache"][resource_name] # create - return _query(method='POST', action=resource_name, data=data, profile=profile, subdomain=subdomain, api_key=api_key) + return _query( + method="POST", + action=resource_name, + data=data, + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) else: # update data_to_update = {} @@ -292,25 +313,38 @@ def create_or_update_resource(resource_name, identifier_fields, data, diff=None, # else default to naive key-value walk of the dicts else: for k, v in data.items(): - if k.startswith('_'): + if k.startswith("_"): continue resource_value = resource.get(k, None) if resource_value is not None and resource_value != v: data_to_update[k] = v if len(data_to_update) > 0: - if __opts__['test']: - return 'would update' + if __opts__["test"]: + return "would update" # flush the resource_cache, because we're modifying a resource - del __context__['pagerduty_util.resource_cache'][resource_name] + del __context__["pagerduty_util.resource_cache"][resource_name] resource_id = _get_resource_id(resource) - return _query(method='PUT', action='{0}/{1}'.format(resource_name, resource_id), data=data_to_update, - profile=profile, subdomain=subdomain, api_key=api_key) + return _query( + method="PUT", + action="{0}/{1}".format(resource_name, resource_id), + data=data_to_update, + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) else: return True -def delete_resource(resource_name, key, identifier_fields, profile='pagerduty', subdomain=None, api_key=None): - ''' +def delete_resource( + resource_name, + key, + identifier_fields, + profile="pagerduty", + subdomain=None, + api_key=None, +): + """ delete any pagerduty resource Helper method for absent() @@ -318,21 +352,37 @@ def delete_resource(resource_name, key, identifier_fields, profile='pagerduty', example: delete_resource("users", key, ["id","name","email"]) # delete by id or name or email - ''' - resource = get_resource(resource_name, key, identifier_fields, profile, subdomain, api_key) + """ + resource = get_resource( + resource_name, key, identifier_fields, profile, subdomain, api_key + ) if resource: - if __opts__['test']: - return 'would delete' + if __opts__["test"]: + return "would delete" # flush the resource_cache, because we're modifying a resource - del __context__['pagerduty_util.resource_cache'][resource_name] + del __context__["pagerduty_util.resource_cache"][resource_name] resource_id = _get_resource_id(resource) - return _query(method='DELETE', action='{0}/{1}'.format(resource_name, resource_id), profile=profile, subdomain=subdomain, api_key=api_key) + return _query( + method="DELETE", + action="{0}/{1}".format(resource_name, resource_id), + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) else: return True -def resource_present(resource, identifier_fields, diff=None, profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def resource_present( + resource, + identifier_fields, + diff=None, + profile="pagerduty", + subdomain=None, + api_key=None, + **kwargs +): + """ Generic resource.present state method. Pagerduty state modules should be a thin wrapper over this method, with a custom diff function. @@ -340,36 +390,42 @@ def resource_present(resource, identifier_fields, diff=None, profile='pagerduty' example: resource_present("users", ["id","name","email"]) - ''' + """ - ret = {'name': kwargs['name'], - 'changes': {}, - 'result': None, - 'comment': ''} - result = create_or_update_resource(resource, - identifier_fields, - kwargs, - diff=diff, - profile=profile, - subdomain=subdomain, - api_key=api_key) + ret = {"name": kwargs["name"], "changes": {}, "result": None, "comment": ""} + result = create_or_update_resource( + resource, + identifier_fields, + kwargs, + diff=diff, + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) if result is True: pass elif result is None: - ret['result'] = True - elif __opts__['test']: - ret['comment'] = result - elif 'error' in result: - ret['result'] = False - ret['comment'] = result + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = result + elif "error" in result: + ret["result"] = False + ret["comment"] = result else: - ret['result'] = True - ret['comment'] = result + ret["result"] = True + ret["comment"] = result return ret -def resource_absent(resource, identifier_fields, profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def resource_absent( + resource, + identifier_fields, + profile="pagerduty", + subdomain=None, + api_key=None, + **kwargs +): + """ Generic resource.absent state method. Pagerduty state modules should be a thin wrapper over this method, with a custom diff function. @@ -377,31 +433,30 @@ def resource_absent(resource, identifier_fields, profile='pagerduty', subdomain= example: resource_absent("users", ["id","name","email"]) - ''' - ret = {'name': kwargs['name'], - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": kwargs["name"], "changes": {}, "result": None, "comment": ""} for k, v in kwargs.items(): if k not in identifier_fields: continue - result = delete_resource(resource, - v, - identifier_fields, - profile=profile, - subdomain=subdomain, - api_key=api_key) + result = delete_resource( + resource, + v, + identifier_fields, + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) if result is None: - ret['result'] = True - ret['comment'] = '{0} deleted'.format(v) + ret["result"] = True + ret["comment"] = "{0} deleted".format(v) return ret elif result is True: continue - elif __opts__['test']: - ret['comment'] = result + elif __opts__["test"]: + ret["comment"] = result return ret - elif 'error' in result: - ret['result'] = False - ret['comment'] = result + elif "error" in result: + ret["result"] = False + ret["comment"] = result return ret return ret diff --git a/salt/modules/pam.py b/salt/modules/pam.py index 8eb82b96c98..a3e3cfd919d 100644 --- a/salt/modules/pam.py +++ b/salt/modules/pam.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Support for pam -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import python libs import os -import logging # Import salt libs import salt.utils.files @@ -14,24 +15,24 @@ import salt.utils.files log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pam' +__virtualname__ = "pam" def __virtual__(): - ''' + """ Set the virtual name for the module - ''' + """ return __virtualname__ def _parse(contents=None, file_name=None): - ''' + """ Parse a standard pam config file - ''' + """ if contents: pass elif file_name and os.path.exists(file_name): - with salt.utils.files.fopen(file_name, 'r') as ifile: + with salt.utils.files.fopen(file_name, "r") as ifile: contents = salt.utils.stringutils.to_unicode(ifile.read()) else: log.error('File "%s" does not exist', file_name) @@ -41,24 +42,24 @@ def _parse(contents=None, file_name=None): for line in contents.splitlines(): if not line: continue - if line.startswith('#'): + if line.startswith("#"): continue - control_flag = '' - module = '' + control_flag = "" + module = "" arguments = [] comps = line.split() interface = comps[0] position = 1 - if comps[1].startswith('['): - control_flag = comps[1].replace('[', '') + if comps[1].startswith("["): + control_flag = comps[1].replace("[", "") for part in comps[2:]: position += 1 - if part.endswith(']'): - control_flag += ' {0}'.format(part.replace(']', '')) + if part.endswith("]"): + control_flag += " {0}".format(part.replace("]", "")) position += 1 break else: - control_flag += ' {0}'.format(part) + control_flag += " {0}".format(part) else: control_flag = comps[1] position += 1 @@ -66,15 +67,19 @@ def _parse(contents=None, file_name=None): if len(comps) > position: position += 1 arguments = comps[position:] - rules.append({'interface': interface, - 'control_flag': control_flag, - 'module': module, - 'arguments': arguments}) + rules.append( + { + "interface": interface, + "control_flag": control_flag, + "module": module, + "arguments": arguments, + } + ) return rules def read_file(file_name): - ''' + """ This is just a test function, to make sure parsing works CLI Example: @@ -82,5 +87,5 @@ def read_file(file_name): .. code-block:: bash salt '*' pam.read_file /etc/pam.d/login - ''' + """ return _parse(file_name=file_name) diff --git a/salt/modules/panos.py b/salt/modules/panos.py index d6176a9e5c8..a1d1ea5f67c 100644 --- a/salt/modules/panos.py +++ b/salt/modules/panos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide Palo Alto compatibility to Salt :codeauthor: ``Spencer Ervin `` @@ -26,52 +26,56 @@ This execution module was designed to handle connections to a Palo Alto based firewall. This module adds support to send connections directly to the device through the XML API or through a brokered connection to Panorama. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time -# Import Salt Libs -from salt.exceptions import CommandExecutionError import salt.proxy.panos import salt.utils.platform +# Import Salt Libs +from salt.exceptions import CommandExecutionError + log = logging.getLogger(__name__) -__virtualname__ = 'panos' +__virtualname__ = "panos" def __virtual__(): - ''' + """ Will load for the panos proxy minions. - ''' + """ try: - if salt.utils.platform.is_proxy() and \ - __opts__['proxy']['proxytype'] == 'panos': + if salt.utils.platform.is_proxy() and __opts__["proxy"]["proxytype"] == "panos": return __virtualname__ except KeyError: pass - return False, 'The panos execution module can only be loaded for panos proxy minions.' + return ( + False, + "The panos execution module can only be loaded for panos proxy minions.", + ) def _get_job_results(query=None): - ''' + """ Executes a query that requires a job for completion. This function will wait for the job to complete and return the results. - ''' + """ if not query: raise CommandExecutionError("Query parameters cannot be empty.") - response = __proxy__['panos.call'](query) + response = __proxy__["panos.call"](query) # If the response contains a job, we will wait for the results - if 'result' in response and 'job' in response['result']: - jid = response['result']['job'] + if "result" in response and "job" in response["result"]: + jid = response["result"]["job"] - while get_job(jid)['result']['job']['status'] != 'FIN': + while get_job(jid)["result"]["job"]["status"] != "FIN": time.sleep(5) return get_job(jid) @@ -80,7 +84,7 @@ def _get_job_results(query=None): def add_config_lock(): - ''' + """ Prevent other users from changing configuration until the lock is released. CLI Example: @@ -89,14 +93,17 @@ def add_config_lock(): salt '*' panos.add_config_lock - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def check_antivirus(): - ''' + """ Get anti-virus information from PaloAlto Networks server CLI Example: @@ -105,14 +112,17 @@ def check_antivirus(): salt '*' panos.check_antivirus - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def check_software(): - ''' + """ Get software information from PaloAlto Networks server. CLI Example: @@ -121,14 +131,17 @@ def check_software(): salt '*' panos.check_software - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def clear_commit_tasks(): - ''' + """ Clear all commit tasks. CLI Example: @@ -137,14 +150,17 @@ def clear_commit_tasks(): salt '*' panos.clear_commit_tasks - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def commit(): - ''' + """ Commits the candidate configuration to the running configuration. CLI Example: @@ -153,14 +169,14 @@ def commit(): salt '*' panos.commit - ''' - query = {'type': 'commit', 'cmd': ''} + """ + query = {"type": "commit", "cmd": ""} return _get_job_results(query) def deactivate_license(key_name=None): - ''' + """ Deactivates an installed license. Required version 7.0.0 or greater. @@ -172,23 +188,31 @@ def deactivate_license(key_name=None): salt '*' panos.deactivate_license key_name=License_File_Name.key - ''' + """ - _required_version = '7.0.0' - if not __proxy__['panos.is_required_version'](_required_version): - return False, 'The panos device requires version {0} or greater for this command.'.format(_required_version) + _required_version = "7.0.0" + if not __proxy__["panos.is_required_version"](_required_version): + return ( + False, + "The panos device requires version {0} or greater for this command.".format( + _required_version + ), + ) if not key_name: - return False, 'You must specify a key_name.' + return False, "You must specify a key_name." else: - query = {'type': 'op', 'cmd': '{0}' - ''.format(key_name)} + query = { + "type": "op", + "cmd": "{0}" + "".format(key_name), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def delete_license(key_name=None): - ''' + """ Remove license keys on disk. key_name(str): The file name of the license key to be deleted. @@ -199,18 +223,23 @@ def delete_license(key_name=None): salt '*' panos.delete_license key_name=License_File_Name.key - ''' + """ if not key_name: - return False, 'You must specify a key_name.' + return False, "You must specify a key_name." else: - query = {'type': 'op', 'cmd': '{0}'.format(key_name)} + query = { + "type": "op", + "cmd": "{0}".format( + key_name + ), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def download_antivirus(): - ''' + """ Download the most recent anti-virus package. CLI Example: @@ -219,16 +248,18 @@ def download_antivirus(): salt '*' panos.download_antivirus - ''' - query = {'type': 'op', - 'cmd': '' - ''} + """ + query = { + "type": "op", + "cmd": "" + "", + } return _get_job_results(query) def download_software_file(filename=None, synch=False): - ''' + """ Download software packages by filename. Args: @@ -243,7 +274,7 @@ def download_software_file(filename=None, synch=False): salt '*' panos.download_software_file PanOS_5000-8.0.0 salt '*' panos.download_software_file PanOS_5000-8.0.0 True - ''' + """ if not filename: raise CommandExecutionError("Filename option must not be none.") @@ -251,19 +282,27 @@ def download_software_file(filename=None, synch=False): raise CommandExecutionError("Synch option must be boolean..") if synch is True: - query = {'type': 'op', - 'cmd': '' - '{0}'.format(filename)} + query = { + "type": "op", + "cmd": "" + "{0}".format( + filename + ), + } else: - query = {'type': 'op', - 'cmd': 'yes' - '{0}'.format(filename)} + query = { + "type": "op", + "cmd": "yes" + "{0}".format( + filename + ), + } return _get_job_results(query) def download_software_version(version=None, synch=False): - ''' + """ Download software packages by version number. Args: @@ -278,7 +317,7 @@ def download_software_version(version=None, synch=False): salt '*' panos.download_software_version 8.0.0 salt '*' panos.download_software_version 8.0.0 True - ''' + """ if not version: raise CommandExecutionError("Version option must not be none.") @@ -286,19 +325,27 @@ def download_software_version(version=None, synch=False): raise CommandExecutionError("Synch option must be boolean..") if synch is True: - query = {'type': 'op', - 'cmd': '' - '{0}'.format(version)} + query = { + "type": "op", + "cmd": "" + "{0}".format( + version + ), + } else: - query = {'type': 'op', - 'cmd': 'yes' - '{0}'.format(version)} + query = { + "type": "op", + "cmd": "yes" + "{0}".format( + version + ), + } return _get_job_results(query) def fetch_license(auth_code=None): - ''' + """ Get new license(s) using from the Palo Alto Network Server. auth_code @@ -311,18 +358,24 @@ def fetch_license(auth_code=None): salt '*' panos.fetch_license salt '*' panos.fetch_license auth_code=foobar - ''' + """ if not auth_code: - query = {'type': 'op', 'cmd': ''} + query = { + "type": "op", + "cmd": "", + } else: - query = {'type': 'op', 'cmd': '{0}' - ''.format(auth_code)} + query = { + "type": "op", + "cmd": "{0}" + "".format(auth_code), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_address(address=None, vsys='1'): - ''' +def get_address(address=None, vsys="1"): + """ Get the candidate configuration for the specified get_address object. This will not return address objects that are marked as pre-defined objects. @@ -337,17 +390,19 @@ def get_address(address=None, vsys='1'): salt '*' panos.get_address myhost salt '*' panos.get_address myhost 3 - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/' - 'address/entry[@name=\'{1}\']'.format(vsys, address)} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/" + "address/entry[@name='{1}']".format(vsys, address), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_address_group(addressgroup=None, vsys='1'): - ''' +def get_address_group(addressgroup=None, vsys="1"): + """ Get the candidate configuration for the specified address group. This will not return address groups that are marked as pre-defined objects. @@ -362,17 +417,19 @@ def get_address_group(addressgroup=None, vsys='1'): salt '*' panos.get_address_group foobar salt '*' panos.get_address_group foobar 3 - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/' - 'address-group/entry[@name=\'{1}\']'.format(vsys, addressgroup)} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/" + "address-group/entry[@name='{1}']".format(vsys, addressgroup), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_admins_active(): - ''' + """ Show active administrators. CLI Example: @@ -381,14 +438,14 @@ def get_admins_active(): salt '*' panos.get_admins_active - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_admins_all(): - ''' + """ Show all administrators. CLI Example: @@ -397,14 +454,14 @@ def get_admins_all(): salt '*' panos.get_admins_all - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_antivirus_info(): - ''' + """ Show information about available anti-virus packages. CLI Example: @@ -413,14 +470,17 @@ def get_antivirus_info(): salt '*' panos.get_antivirus_info - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_arp(): - ''' + """ Show ARP information. CLI Example: @@ -429,14 +489,14 @@ def get_arp(): salt '*' panos.get_arp - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_cli_idle_timeout(): - ''' + """ Show timeout information for this administrative session. CLI Example: @@ -445,14 +505,17 @@ def get_cli_idle_timeout(): salt '*' panos.get_cli_idle_timeout - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_cli_permissions(): - ''' + """ Show cli administrative permissions. CLI Example: @@ -461,14 +524,14 @@ def get_cli_permissions(): salt '*' panos.get_cli_permissions - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_disk_usage(): - ''' + """ Report filesystem disk space usage. CLI Example: @@ -477,14 +540,17 @@ def get_disk_usage(): salt '*' panos.get_disk_usage - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_dns_server_config(): - ''' + """ Get the DNS server configuration from the candidate configuration. CLI Example: @@ -493,16 +559,18 @@ def get_dns_server_config(): salt '*' panos.get_dns_server_config - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/dns-setting/servers'} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/dns-setting/servers", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_domain_config(): - ''' + """ Get the domain name configuration from the candidate configuration. CLI Example: @@ -511,16 +579,18 @@ def get_domain_config(): salt '*' panos.get_domain_config - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/domain'} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/domain", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_dos_blocks(): - ''' + """ Show the DoS block-ip table. CLI Example: @@ -529,14 +599,17 @@ def get_dos_blocks(): salt '*' panos.get_dos_blocks - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_fqdn_cache(): - ''' + """ Print FQDNs used in rules and their IPs. CLI Example: @@ -545,14 +618,17 @@ def get_fqdn_cache(): salt '*' panos.get_fqdn_cache - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_ha_config(): - ''' + """ Get the high availability configuration. CLI Example: @@ -561,16 +637,18 @@ def get_ha_config(): salt '*' panos.get_ha_config - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/high-availability'} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/high-availability", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_ha_link(): - ''' + """ Show high-availability link-monitoring state. CLI Example: @@ -579,15 +657,17 @@ def get_ha_link(): salt '*' panos.get_ha_link - ''' - query = {'type': 'op', - 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_ha_path(): - ''' + """ Show high-availability path-monitoring state. CLI Example: @@ -596,15 +676,17 @@ def get_ha_path(): salt '*' panos.get_ha_path - ''' - query = {'type': 'op', - 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_ha_state(): - ''' + """ Show high-availability state information. CLI Example: @@ -613,16 +695,18 @@ def get_ha_state(): salt '*' panos.get_ha_state - ''' + """ - query = {'type': 'op', - 'cmd': ''} + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_ha_transitions(): - ''' + """ Show high-availability transition statistic information. CLI Example: @@ -631,16 +715,18 @@ def get_ha_transitions(): salt '*' panos.get_ha_transitions - ''' + """ - query = {'type': 'op', - 'cmd': ''} + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_hostname(): - ''' + """ Get the hostname of the device. CLI Example: @@ -649,16 +735,18 @@ def get_hostname(): salt '*' panos.get_hostname - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/hostname'} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/hostname", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_interface_counters(name='all'): - ''' +def get_interface_counters(name="all"): + """ Get the counter statistics for interfaces. Args: @@ -671,15 +759,19 @@ def get_interface_counters(name='all'): salt '*' panos.get_interface_counters salt '*' panos.get_interface_counters ethernet1/1 - ''' - query = {'type': 'op', - 'cmd': '{0}'.format(name)} + """ + query = { + "type": "op", + "cmd": "{0}".format( + name + ), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_interfaces(name='all'): - ''' +def get_interfaces(name="all"): + """ Show interface information. Args: @@ -692,15 +784,17 @@ def get_interfaces(name='all'): salt '*' panos.get_interfaces salt '*' panos.get_interfaces ethernet1/1 - ''' - query = {'type': 'op', - 'cmd': '{0}'.format(name)} + """ + query = { + "type": "op", + "cmd": "{0}".format(name), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_job(jid=None): - ''' + """ List all a single job by ID. jid @@ -712,17 +806,17 @@ def get_job(jid=None): salt '*' panos.get_job jid=15 - ''' + """ if not jid: raise CommandExecutionError("ID option must not be none.") - query = {'type': 'op', 'cmd': '{0}'.format(jid)} + query = {"type": "op", "cmd": "{0}".format(jid)} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_jobs(state='all'): - ''' +def get_jobs(state="all"): + """ List all jobs on the device. state @@ -737,21 +831,26 @@ def get_jobs(state='all'): salt '*' panos.get_jobs salt '*' panos.get_jobs state=pending - ''' - if state.lower() == 'all': - query = {'type': 'op', 'cmd': ''} - elif state.lower() == 'pending': - query = {'type': 'op', 'cmd': ''} - elif state.lower() == 'processed': - query = {'type': 'op', 'cmd': ''} + """ + if state.lower() == "all": + query = {"type": "op", "cmd": ""} + elif state.lower() == "pending": + query = {"type": "op", "cmd": ""} + elif state.lower() == "processed": + query = { + "type": "op", + "cmd": "", + } else: - raise CommandExecutionError("The state parameter must be all, pending, or processed.") + raise CommandExecutionError( + "The state parameter must be all, pending, or processed." + ) - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_lacp(): - ''' + """ Show LACP state. CLI Example: @@ -760,14 +859,17 @@ def get_lacp(): salt '*' panos.get_lacp - ''' - query = {'type': 'op', 'cmd': 'all'} + """ + query = { + "type": "op", + "cmd": "all", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_license_info(): - ''' + """ Show information about owned license(s). CLI Example: @@ -776,14 +878,14 @@ def get_license_info(): salt '*' panos.get_license_info - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_license_tokens(): - ''' + """ Show license token files for manual license deactivation. CLI Example: @@ -792,14 +894,17 @@ def get_license_tokens(): salt '*' panos.get_license_tokens - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_lldp_config(): - ''' + """ Show lldp config for interfaces. CLI Example: @@ -808,14 +913,14 @@ def get_lldp_config(): salt '*' panos.get_lldp_config - ''' - query = {'type': 'op', 'cmd': 'all'} + """ + query = {"type": "op", "cmd": "all"} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_lldp_counters(): - ''' + """ Show lldp counters for interfaces. CLI Example: @@ -824,14 +929,14 @@ def get_lldp_counters(): salt '*' panos.get_lldp_counters - ''' - query = {'type': 'op', 'cmd': 'all'} + """ + query = {"type": "op", "cmd": "all"} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_lldp_local(): - ''' + """ Show lldp local info for interfaces. CLI Example: @@ -840,14 +945,14 @@ def get_lldp_local(): salt '*' panos.get_lldp_local - ''' - query = {'type': 'op', 'cmd': 'all'} + """ + query = {"type": "op", "cmd": "all"} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_lldp_neighbors(): - ''' + """ Show lldp neighbors info for interfaces. CLI Example: @@ -856,14 +961,17 @@ def get_lldp_neighbors(): salt '*' panos.get_lldp_neighbors - ''' - query = {'type': 'op', 'cmd': 'all'} + """ + query = { + "type": "op", + "cmd": "all", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_local_admins(): - ''' + """ Show all local administrator accounts. CLI Example: @@ -872,24 +980,24 @@ def get_local_admins(): salt '*' panos.get_local_admins - ''' + """ admin_list = get_users_config() response = [] - if 'users' not in admin_list['result']: + if "users" not in admin_list["result"]: return response - if isinstance(admin_list['result']['users']['entry'], list): - for entry in admin_list['result']['users']['entry']: - response.append(entry['name']) + if isinstance(admin_list["result"]["users"]["entry"], list): + for entry in admin_list["result"]["users"]["entry"]: + response.append(entry["name"]) else: - response.append(admin_list['result']['users']['entry']['name']) + response.append(admin_list["result"]["users"]["entry"]["name"]) return response def get_logdb_quota(): - ''' + """ Report the logdb quotas. CLI Example: @@ -898,14 +1006,17 @@ def get_logdb_quota(): salt '*' panos.get_logdb_quota - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_master_key(): - ''' + """ Get the master key properties. CLI Example: @@ -914,14 +1025,17 @@ def get_master_key(): salt '*' panos.get_master_key - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_ntp_config(): - ''' + """ Get the NTP configuration from the candidate configuration. CLI Example: @@ -930,16 +1044,18 @@ def get_ntp_config(): salt '*' panos.get_ntp_config - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/ntp-servers'} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/ntp-servers", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_ntp_servers(): - ''' + """ Get list of configured NTP servers. CLI Example: @@ -948,14 +1064,14 @@ def get_ntp_servers(): salt '*' panos.get_ntp_servers - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_operational_mode(): - ''' + """ Show device operational mode setting. CLI Example: @@ -964,14 +1080,14 @@ def get_operational_mode(): salt '*' panos.get_operational_mode - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_panorama_status(): - ''' + """ Show panorama connection status. CLI Example: @@ -980,14 +1096,14 @@ def get_panorama_status(): salt '*' panos.get_panorama_status - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_permitted_ips(): - ''' + """ Get the IP addresses that are permitted to establish management connections to the device. CLI Example: @@ -996,16 +1112,18 @@ def get_permitted_ips(): salt '*' panos.get_permitted_ips - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/permitted-ip'} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/permitted-ip", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_platform(): - ''' + """ Get the platform model information and limitations. CLI Example: @@ -1014,16 +1132,18 @@ def get_platform(): salt '*' panos.get_platform - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/platform'} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/platform", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_predefined_application(application=None): - ''' + """ Get the configuration for the specified pre-defined application object. This will only return pre-defined application objects. @@ -1035,16 +1155,20 @@ def get_predefined_application(application=None): salt '*' panos.get_predefined_application saltstack - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/predefined/application/entry[@name=\'{0}\']'.format(application)} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/predefined/application/entry[@name='{0}']".format( + application + ), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_security_rule(rulename=None, vsys='1'): - ''' +def get_security_rule(rulename=None, vsys="1"): + """ Get the candidate configuration for the specified security rule. rulename(str): The name of the security rule. @@ -1058,17 +1182,19 @@ def get_security_rule(rulename=None, vsys='1'): salt '*' panos.get_security_rule rule01 salt '*' panos.get_security_rule rule01 3 - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/' - 'rulebase/security/rules/entry[@name=\'{1}\']'.format(vsys, rulename)} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/" + "rulebase/security/rules/entry[@name='{1}']".format(vsys, rulename), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_service(service=None, vsys='1'): - ''' +def get_service(service=None, vsys="1"): + """ Get the candidate configuration for the specified service object. This will not return services that are marked as pre-defined objects. @@ -1083,17 +1209,19 @@ def get_service(service=None, vsys='1'): salt '*' panos.get_service tcp-443 salt '*' panos.get_service tcp-443 3 - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/' - 'service/entry[@name=\'{1}\']'.format(vsys, service)} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/" + "service/entry[@name='{1}']".format(vsys, service), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_service_group(servicegroup=None, vsys='1'): - ''' +def get_service_group(servicegroup=None, vsys="1"): + """ Get the candidate configuration for the specified service group. This will not return service groups that are marked as pre-defined objects. @@ -1108,17 +1236,19 @@ def get_service_group(servicegroup=None, vsys='1'): salt '*' panos.get_service_group foobar salt '*' panos.get_service_group foobar 3 - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/' - 'service-group/entry[@name=\'{1}\']'.format(vsys, servicegroup)} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/" + "service-group/entry[@name='{1}']".format(vsys, servicegroup), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_session_info(): - ''' + """ Show device session statistics. CLI Example: @@ -1127,15 +1257,14 @@ def get_session_info(): salt '*' panos.get_session_info - ''' - query = {'type': 'op', - 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_snmp_config(): - ''' + """ Get the SNMP configuration from the device. CLI Example: @@ -1144,16 +1273,18 @@ def get_snmp_config(): salt '*' panos.get_snmp_config - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/snmp-setting'} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/snmp-setting", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_software_info(): - ''' + """ Show information about available software packages. CLI Example: @@ -1162,14 +1293,17 @@ def get_software_info(): salt '*' panos.get_software_info - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_system_date_time(): - ''' + """ Get the system date/time. CLI Example: @@ -1178,14 +1312,14 @@ def get_system_date_time(): salt '*' panos.get_system_date_time - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_system_files(): - ''' + """ List important files in the system. CLI Example: @@ -1194,14 +1328,14 @@ def get_system_files(): salt '*' panos.get_system_files - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_system_info(): - ''' + """ Get the system information. CLI Example: @@ -1210,14 +1344,14 @@ def get_system_info(): salt '*' panos.get_system_info - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_system_services(): - ''' + """ Show system services. CLI Example: @@ -1226,14 +1360,14 @@ def get_system_services(): salt '*' panos.get_system_services - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_system_state(mask=None): - ''' + """ Show the system state variables. mask @@ -1247,18 +1381,22 @@ def get_system_state(mask=None): salt '*' panos.get_system_state mask=cfg.ha.config.enabled salt '*' panos.get_system_state mask=cfg.ha.* - ''' + """ if mask: - query = {'type': 'op', - 'cmd': '{0}'.format(mask)} + query = { + "type": "op", + "cmd": "{0}".format( + mask + ), + } else: - query = {'type': 'op', 'cmd': ''} + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_uncommitted_changes(): - ''' + """ Retrieve a list of all uncommitted changes on the device. Requires PANOS version 8.0.0 or greater. @@ -1268,19 +1406,26 @@ def get_uncommitted_changes(): salt '*' panos.get_uncommitted_changes - ''' - _required_version = '8.0.0' - if not __proxy__['panos.is_required_version'](_required_version): - return False, 'The panos device requires version {0} or greater for this command.'.format(_required_version) + """ + _required_version = "8.0.0" + if not __proxy__["panos.is_required_version"](_required_version): + return ( + False, + "The panos device requires version {0} or greater for this command.".format( + _required_version + ), + ) - query = {'type': 'op', - 'cmd': ''} + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_users_config(): - ''' + """ Get the local administrative user account configuration. CLI Example: @@ -1289,16 +1434,14 @@ def get_users_config(): salt '*' panos.get_users_config - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/mgt-config/users'} + """ + query = {"type": "config", "action": "get", "xpath": "/config/mgt-config/users"} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def get_vlans(): - ''' + """ Show all VLAN information. CLI Example: @@ -1307,14 +1450,14 @@ def get_vlans(): salt '*' panos.get_vlans - ''' - query = {'type': 'op', 'cmd': 'all'} + """ + query = {"type": "op", "cmd": "all"} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_xpath(xpath=''): - ''' +def get_xpath(xpath=""): + """ Retrieve a specified xpath from the candidate configuration. xpath(str): The specified xpath in the candidate configuration. @@ -1325,16 +1468,14 @@ def get_xpath(xpath=''): salt '*' panos.get_xpath /config/shared/service - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': xpath} + """ + query = {"type": "config", "action": "get", "xpath": xpath} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_zone(zone='', vsys='1'): - ''' +def get_zone(zone="", vsys="1"): + """ Get the candidate configuration for the specified zone. zone(str): The name of the zone. @@ -1348,17 +1489,19 @@ def get_zone(zone='', vsys='1'): salt '*' panos.get_zone trust salt '*' panos.get_zone trust 2 - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/' - 'zone/entry[@name=\'{1}\']'.format(vsys, zone)} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/" + "zone/entry[@name='{1}']".format(vsys, zone), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def get_zones(vsys='1'): - ''' +def get_zones(vsys="1"): + """ Get all the zones in the candidate configuration. vsys(str): The string representation of the VSYS ID. @@ -1370,17 +1513,21 @@ def get_zones(vsys='1'): salt '*' panos.get_zones salt '*' panos.get_zones 2 - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/' - 'zone'.format(vsys)} + """ + query = { + "type": "config", + "action": "get", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/" + "zone".format(vsys), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def install_antivirus(version=None, latest=False, synch=False, skip_commit=False,): - ''' +def install_antivirus( + version=None, latest=False, synch=False, skip_commit=False, +): + """ Install anti-virus packages. Args: @@ -1399,7 +1546,7 @@ def install_antivirus(version=None, latest=False, synch=False, skip_commit=False salt '*' panos.install_antivirus 8.0.0 - ''' + """ if not version and latest is False: raise CommandExecutionError("Version option must not be none.") @@ -1414,21 +1561,29 @@ def install_antivirus(version=None, latest=False, synch=False, skip_commit=False c = "no" if latest is True: - query = {'type': 'op', - 'cmd': '' - '{0}{1}' - 'latest'.format(c, s)} + query = { + "type": "op", + "cmd": "" + "{0}{1}" + "latest".format( + c, s + ), + } else: - query = {'type': 'op', - 'cmd': '' - '{0}{1}' - '{2}'.format(c, s, version)} + query = { + "type": "op", + "cmd": "" + "{0}{1}" + "{2}".format( + c, s, version + ), + } return _get_job_results(query) def install_license(): - ''' + """ Install the license key(s). CLI Example: @@ -1437,14 +1592,17 @@ def install_license(): salt '*' panos.install_license - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def install_software(version=None): - ''' + """ Upgrade to a software package by version. Args: @@ -1456,19 +1614,23 @@ def install_software(version=None): salt '*' panos.install_license 8.0.0 - ''' + """ if not version: raise CommandExecutionError("Version option must not be none.") - query = {'type': 'op', - 'cmd': '' - '{0}'.format(version)} + query = { + "type": "op", + "cmd": "" + "{0}".format( + version + ), + } return _get_job_results(query) def reboot(): - ''' + """ Reboot a running system. CLI Example: @@ -1477,14 +1639,17 @@ def reboot(): salt '*' panos.reboot - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def refresh_fqdn_cache(force=False): - ''' + """ Force refreshes all FQDNs used in rules. force @@ -1497,21 +1662,26 @@ def refresh_fqdn_cache(force=False): salt '*' panos.refresh_fqdn_cache salt '*' panos.refresh_fqdn_cache force=True - ''' + """ if not isinstance(force, bool): raise CommandExecutionError("Force option must be boolean.") if force: - query = {'type': 'op', - 'cmd': 'yes'} + query = { + "type": "op", + "cmd": "yes", + } else: - query = {'type': 'op', 'cmd': ''} + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def remove_config_lock(): - ''' + """ Release config lock previously held. CLI Example: @@ -1520,14 +1690,17 @@ def remove_config_lock(): salt '*' panos.remove_config_lock - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def resolve_address(address=None, vsys=None): - ''' + """ Resolve address to ip address. Required version 7.0.0 or greater. @@ -1544,27 +1717,38 @@ def resolve_address(address=None, vsys=None): salt '*' panos.resolve_address foo.bar.com salt '*' panos.resolve_address foo.bar.com vsys=2 - ''' - _required_version = '7.0.0' - if not __proxy__['panos.is_required_version'](_required_version): - return False, 'The panos device requires version {0} or greater for this command.'.format(_required_version) + """ + _required_version = "7.0.0" + if not __proxy__["panos.is_required_version"](_required_version): + return ( + False, + "The panos device requires version {0} or greater for this command.".format( + _required_version + ), + ) if not address: raise CommandExecutionError("FQDN to resolve must be provided as address.") if not vsys: - query = {'type': 'op', - 'cmd': '
{0}
'.format(address)} + query = { + "type": "op", + "cmd": "
{0}
".format( + address + ), + } else: - query = {'type': 'op', - 'cmd': '{0}
{1}
' - '
'.format(vsys, address)} + query = { + "type": "op", + "cmd": "{0}
{1}
" + "
".format(vsys, address), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def save_device_config(filename=None): - ''' + """ Save device configuration to a named file. filename @@ -1576,17 +1760,20 @@ def save_device_config(filename=None): salt '*' panos.save_device_config foo.xml - ''' + """ if not filename: raise CommandExecutionError("Filename must not be empty.") - query = {'type': 'op', 'cmd': '{0}'.format(filename)} + query = { + "type": "op", + "cmd": "{0}".format(filename), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def save_device_state(): - ''' + """ Save files needed to restore device to local disk. CLI Example: @@ -1595,14 +1782,14 @@ def save_device_state(): salt '*' panos.save_device_state - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = {"type": "op", "cmd": ""} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def set_authentication_profile(profile=None, deploy=False): - ''' + """ Set the authentication profile of the Palo Alto proxy minion. A commit will be required before this is processed. CLI Example: @@ -1617,20 +1804,24 @@ def set_authentication_profile(profile=None, deploy=False): salt '*' panos.set_authentication_profile foo salt '*' panos.set_authentication_profile foo deploy=True - ''' + """ if not profile: raise CommandExecutionError("Profile name option must not be none.") ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/' - 'authentication-profile', - 'element': '{0}'.format(profile)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/" + "authentication-profile", + "element": "{0}".format( + profile + ), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -1639,7 +1830,7 @@ def set_authentication_profile(profile=None, deploy=False): def set_hostname(hostname=None, deploy=False): - ''' + """ Set the hostname of the Palo Alto proxy minion. A commit will be required before this is processed. CLI Example: @@ -1654,19 +1845,21 @@ def set_hostname(hostname=None, deploy=False): salt '*' panos.set_hostname newhostname salt '*' panos.set_hostname newhostname deploy=True - ''' + """ if not hostname: raise CommandExecutionError("Hostname option must not be none.") ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system', - 'element': '{0}'.format(hostname)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system", + "element": "{0}".format(hostname), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -1675,7 +1868,7 @@ def set_hostname(hostname=None, deploy=False): def set_management_icmp(enabled=True, deploy=False): - ''' + """ Enables or disables the ICMP management service on the device. CLI Example: @@ -1690,23 +1883,27 @@ def set_management_icmp(enabled=True, deploy=False): salt '*' panos.set_management_icmp salt '*' panos.set_management_icmp enabled=False deploy=True - ''' + """ if enabled is True: value = "no" elif enabled is False: value = "yes" else: - raise CommandExecutionError("Invalid option provided for service enabled option.") + raise CommandExecutionError( + "Invalid option provided for service enabled option." + ) ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/service', - 'element': '{0}'.format(value)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/service", + "element": "{0}".format(value), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -1715,7 +1912,7 @@ def set_management_icmp(enabled=True, deploy=False): def set_management_http(enabled=True, deploy=False): - ''' + """ Enables or disables the HTTP management service on the device. CLI Example: @@ -1730,23 +1927,27 @@ def set_management_http(enabled=True, deploy=False): salt '*' panos.set_management_http salt '*' panos.set_management_http enabled=False deploy=True - ''' + """ if enabled is True: value = "no" elif enabled is False: value = "yes" else: - raise CommandExecutionError("Invalid option provided for service enabled option.") + raise CommandExecutionError( + "Invalid option provided for service enabled option." + ) ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/service', - 'element': '{0}'.format(value)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/service", + "element": "{0}".format(value), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -1755,7 +1956,7 @@ def set_management_http(enabled=True, deploy=False): def set_management_https(enabled=True, deploy=False): - ''' + """ Enables or disables the HTTPS management service on the device. CLI Example: @@ -1770,23 +1971,27 @@ def set_management_https(enabled=True, deploy=False): salt '*' panos.set_management_https salt '*' panos.set_management_https enabled=False deploy=True - ''' + """ if enabled is True: value = "no" elif enabled is False: value = "yes" else: - raise CommandExecutionError("Invalid option provided for service enabled option.") + raise CommandExecutionError( + "Invalid option provided for service enabled option." + ) ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/service', - 'element': '{0}'.format(value)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/service", + "element": "{0}".format(value), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -1795,7 +2000,7 @@ def set_management_https(enabled=True, deploy=False): def set_management_ocsp(enabled=True, deploy=False): - ''' + """ Enables or disables the HTTP OCSP management service on the device. CLI Example: @@ -1810,23 +2015,27 @@ def set_management_ocsp(enabled=True, deploy=False): salt '*' panos.set_management_ocsp salt '*' panos.set_management_ocsp enabled=False deploy=True - ''' + """ if enabled is True: value = "no" elif enabled is False: value = "yes" else: - raise CommandExecutionError("Invalid option provided for service enabled option.") + raise CommandExecutionError( + "Invalid option provided for service enabled option." + ) ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/service', - 'element': '{0}'.format(value)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/service", + "element": "{0}".format(value), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -1835,7 +2044,7 @@ def set_management_ocsp(enabled=True, deploy=False): def set_management_snmp(enabled=True, deploy=False): - ''' + """ Enables or disables the SNMP management service on the device. CLI Example: @@ -1850,23 +2059,27 @@ def set_management_snmp(enabled=True, deploy=False): salt '*' panos.set_management_snmp salt '*' panos.set_management_snmp enabled=False deploy=True - ''' + """ if enabled is True: value = "no" elif enabled is False: value = "yes" else: - raise CommandExecutionError("Invalid option provided for service enabled option.") + raise CommandExecutionError( + "Invalid option provided for service enabled option." + ) ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/service', - 'element': '{0}'.format(value)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/service", + "element": "{0}".format(value), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -1875,7 +2088,7 @@ def set_management_snmp(enabled=True, deploy=False): def set_management_ssh(enabled=True, deploy=False): - ''' + """ Enables or disables the SSH management service on the device. CLI Example: @@ -1890,23 +2103,27 @@ def set_management_ssh(enabled=True, deploy=False): salt '*' panos.set_management_ssh salt '*' panos.set_management_ssh enabled=False deploy=True - ''' + """ if enabled is True: value = "no" elif enabled is False: value = "yes" else: - raise CommandExecutionError("Invalid option provided for service enabled option.") + raise CommandExecutionError( + "Invalid option provided for service enabled option." + ) ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/service', - 'element': '{0}'.format(value)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/service", + "element": "{0}".format(value), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -1915,7 +2132,7 @@ def set_management_ssh(enabled=True, deploy=False): def set_management_telnet(enabled=True, deploy=False): - ''' + """ Enables or disables the Telnet management service on the device. CLI Example: @@ -1930,23 +2147,27 @@ def set_management_telnet(enabled=True, deploy=False): salt '*' panos.set_management_telnet salt '*' panos.set_management_telnet enabled=False deploy=True - ''' + """ if enabled is True: value = "no" elif enabled is False: value = "yes" else: - raise CommandExecutionError("Invalid option provided for service enabled option.") + raise CommandExecutionError( + "Invalid option provided for service enabled option." + ) ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/service', - 'element': '{0}'.format(value)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/service", + "element": "{0}".format(value), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -1954,13 +2175,15 @@ def set_management_telnet(enabled=True, deploy=False): return ret -def set_ntp_authentication(target=None, - authentication_type=None, - key_id=None, - authentication_key=None, - algorithm=None, - deploy=False): - ''' +def set_ntp_authentication( + target=None, + authentication_type=None, + key_id=None, + authentication_key=None, + algorithm=None, + deploy=False, +): + """ Set the NTP authentication of the Palo Alto proxy minion. A commit will be required before this is processed. CLI Example: @@ -1985,80 +2208,101 @@ def set_ntp_authentication(target=None, salt '*' ntp.set_authentication target=both authentication_type=symmetric key_id=15 authentication_key=mykey algorithm=md5 salt '*' ntp.set_authentication target=both authentication_type=symmetric key_id=15 authentication_key=mykey algorithm=md5 deploy=True - ''' + """ ret = {} - if target not in ['primary', 'secondary', 'both']: - raise salt.exceptions.CommandExecutionError("Target option must be primary, secondary, or both.") + if target not in ["primary", "secondary", "both"]: + raise salt.exceptions.CommandExecutionError( + "Target option must be primary, secondary, or both." + ) - if authentication_type not in ['symmetric', 'autokey', 'none']: - raise salt.exceptions.CommandExecutionError("Type option must be symmetric, autokey, or both.") + if authentication_type not in ["symmetric", "autokey", "none"]: + raise salt.exceptions.CommandExecutionError( + "Type option must be symmetric, autokey, or both." + ) if authentication_type == "symmetric" and not authentication_key: - raise salt.exceptions.CommandExecutionError("When using symmetric authentication, authentication_key must be " - "provided.") + raise salt.exceptions.CommandExecutionError( + "When using symmetric authentication, authentication_key must be " + "provided." + ) if authentication_type == "symmetric" and not key_id: - raise salt.exceptions.CommandExecutionError("When using symmetric authentication, key_id must be provided.") + raise salt.exceptions.CommandExecutionError( + "When using symmetric authentication, key_id must be provided." + ) - if authentication_type == "symmetric" and algorithm not in ['md5', 'sha1']: - raise salt.exceptions.CommandExecutionError("When using symmetric authentication, algorithm must be md5 or " - "sha1.") + if authentication_type == "symmetric" and algorithm not in ["md5", "sha1"]: + raise salt.exceptions.CommandExecutionError( + "When using symmetric authentication, algorithm must be md5 or " "sha1." + ) - if authentication_type == 'symmetric': - if target == 'primary' or target == 'both': - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/ntp-servers/' - 'primary-ntp-server/authentication-type', - 'element': '<{0}>{1}' - '{2}'.format(algorithm, - authentication_key, - key_id)} - ret.update({'primary_server': __proxy__['panos.call'](query)}) + if authentication_type == "symmetric": + if target == "primary" or target == "both": + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/ntp-servers/" + "primary-ntp-server/authentication-type", + "element": "<{0}>{1}" + "{2}".format( + algorithm, authentication_key, key_id + ), + } + ret.update({"primary_server": __proxy__["panos.call"](query)}) - if target == 'secondary' or target == 'both': - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/ntp-servers/' - 'secondary-ntp-server/authentication-type', - 'element': '<{0}>{1}' - '{2}'.format(algorithm, - authentication_key, - key_id)} - ret.update({'secondary_server': __proxy__['panos.call'](query)}) - elif authentication_type == 'autokey': - if target == 'primary' or target == 'both': - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/ntp-servers/' - 'primary-ntp-server/authentication-type', - 'element': ''} - ret.update({'primary_server': __proxy__['panos.call'](query)}) + if target == "secondary" or target == "both": + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/ntp-servers/" + "secondary-ntp-server/authentication-type", + "element": "<{0}>{1}" + "{2}".format( + algorithm, authentication_key, key_id + ), + } + ret.update({"secondary_server": __proxy__["panos.call"](query)}) + elif authentication_type == "autokey": + if target == "primary" or target == "both": + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/ntp-servers/" + "primary-ntp-server/authentication-type", + "element": "", + } + ret.update({"primary_server": __proxy__["panos.call"](query)}) - if target == 'secondary' or target == 'both': - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/ntp-servers/' - 'secondary-ntp-server/authentication-type', - 'element': ''} - ret.update({'secondary_server': __proxy__['panos.call'](query)}) - elif authentication_type == 'none': - if target == 'primary' or target == 'both': - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/ntp-servers/' - 'primary-ntp-server/authentication-type', - 'element': ''} - ret.update({'primary_server': __proxy__['panos.call'](query)}) + if target == "secondary" or target == "both": + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/ntp-servers/" + "secondary-ntp-server/authentication-type", + "element": "", + } + ret.update({"secondary_server": __proxy__["panos.call"](query)}) + elif authentication_type == "none": + if target == "primary" or target == "both": + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/ntp-servers/" + "primary-ntp-server/authentication-type", + "element": "", + } + ret.update({"primary_server": __proxy__["panos.call"](query)}) - if target == 'secondary' or target == 'both': - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/ntp-servers/' - 'secondary-ntp-server/authentication-type', - 'element': ''} - ret.update({'secondary_server': __proxy__['panos.call'](query)}) + if target == "secondary" or target == "both": + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/ntp-servers/" + "secondary-ntp-server/authentication-type", + "element": "", + } + ret.update({"secondary_server": __proxy__["panos.call"](query)}) if deploy is True: ret.update(commit()) @@ -2067,7 +2311,7 @@ def set_ntp_authentication(target=None, def set_ntp_servers(primary_server=None, secondary_server=None, deploy=False): - ''' + """ Set the NTP servers of the Palo Alto proxy minion. A commit will be required before this is processed. CLI Example: @@ -2085,24 +2329,32 @@ def set_ntp_servers(primary_server=None, secondary_server=None, deploy=False): salt '*' ntp.set_servers primary_server=0.pool.ntp.org secondary_server=1.pool.ntp.org salt '*' ntp.ser_servers 0.pool.ntp.org 1.pool.ntp.org deploy=True - ''' + """ ret = {} if primary_server: - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/ntp-servers/' - 'primary-ntp-server', - 'element': '{0}'.format(primary_server)} - ret.update({'primary_server': __proxy__['panos.call'](query)}) + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/ntp-servers/" + "primary-ntp-server", + "element": "{0}".format( + primary_server + ), + } + ret.update({"primary_server": __proxy__["panos.call"](query)}) if secondary_server: - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/ntp-servers/' - 'secondary-ntp-server', - 'element': '{0}'.format(secondary_server)} - ret.update({'secondary_server': __proxy__['panos.call'](query)}) + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/ntp-servers/" + "secondary-ntp-server", + "element": "{0}".format( + secondary_server + ), + } + ret.update({"secondary_server": __proxy__["panos.call"](query)}) if deploy is True: ret.update(commit()) @@ -2111,7 +2363,7 @@ def set_ntp_servers(primary_server=None, secondary_server=None, deploy=False): def set_permitted_ip(address=None, deploy=False): - ''' + """ Add an IPv4 address or network to the permitted IP list. CLI Example: @@ -2127,19 +2379,21 @@ def set_permitted_ip(address=None, deploy=False): salt '*' panos.set_permitted_ip 10.0.0.0/24 salt '*' panos.set_permitted_ip 10.0.0.1 deploy=True - ''' + """ if not address: raise CommandExecutionError("Address option must not be empty.") ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/permitted-ip', - 'element': ''.format(address)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/permitted-ip", + "element": "".format(address), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -2148,7 +2402,7 @@ def set_permitted_ip(address=None, deploy=False): def set_timezone(tz=None, deploy=False): - ''' + """ Set the timezone of the Palo Alto proxy minion. A commit will be required before this is processed. CLI Example: @@ -2163,19 +2417,21 @@ def set_timezone(tz=None, deploy=False): salt '*' panos.set_timezone UTC salt '*' panos.set_timezone UTC deploy=True - ''' + """ if not tz: raise CommandExecutionError("Timezone name option must not be none.") ret = {} - query = {'type': 'config', - 'action': 'set', - 'xpath': '/config/devices/entry[@name=\'localhost.localdomain\']/deviceconfig/system/timezone', - 'element': '{0}'.format(tz)} + query = { + "type": "config", + "action": "set", + "xpath": "/config/devices/entry[@name='localhost.localdomain']/deviceconfig/system/timezone", + "element": "{0}".format(tz), + } - ret.update(__proxy__['panos.call'](query)) + ret.update(__proxy__["panos.call"](query)) if deploy is True: ret.update(commit()) @@ -2184,7 +2440,7 @@ def set_timezone(tz=None, deploy=False): def shutdown(): - ''' + """ Shutdown a running system. CLI Example: @@ -2193,15 +2449,17 @@ def shutdown(): salt '*' panos.shutdown - ''' - query = {'type': 'op', 'cmd': ''} + """ + query = { + "type": "op", + "cmd": "", + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def test_fib_route(ip=None, - vr='vr1'): - ''' +def test_fib_route(ip=None, vr="vr1"): + """ Perform a route lookup within active route table (fib). ip (str): The destination IP address to test. @@ -2215,7 +2473,7 @@ def test_fib_route(ip=None, salt '*' panos.test_fib_route 4.2.2.2 salt '*' panos.test_fib_route 4.2.2.2 my-vr - ''' + """ xpath = "" @@ -2227,23 +2485,24 @@ def test_fib_route(ip=None, xpath += "" - query = {'type': 'op', - 'cmd': xpath} + query = {"type": "op", "cmd": xpath} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) -def test_security_policy(sourcezone=None, - destinationzone=None, - source=None, - destination=None, - protocol=None, - port=None, - application=None, - category=None, - vsys='1', - allrules=False): - ''' +def test_security_policy( + sourcezone=None, + destinationzone=None, + source=None, + destination=None, + protocol=None, + port=None, + application=None, + category=None, + vsys="1", + allrules=False, +): + """ Checks which security policy as connection will match on the device. sourcezone (str): The source zone matched against the connection. @@ -2273,7 +2532,7 @@ def test_security_policy(sourcezone=None, salt '*' panos.test_security_policy sourcezone=trust destinationzone=untrust protocol=6 port=22 salt '*' panos.test_security_policy sourcezone=trust destinationzone=untrust protocol=6 port=22 vsys=2 - ''' + """ xpath = "" @@ -2306,15 +2565,13 @@ def test_security_policy(sourcezone=None, xpath += "" - query = {'type': 'op', - 'vsys': "vsys{0}".format(vsys), - 'cmd': xpath} + query = {"type": "op", "vsys": "vsys{0}".format(vsys), "cmd": xpath} - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) def unlock_admin(username=None): - ''' + """ Unlocks a locked administrator account. username @@ -2326,12 +2583,14 @@ def unlock_admin(username=None): salt '*' panos.unlock_admin username=bob - ''' + """ if not username: raise CommandExecutionError("Username option must not be none.") - query = {'type': 'op', - 'cmd': '{0}' - ''.format(username)} + query = { + "type": "op", + "cmd": "{0}" + "".format(username), + } - return __proxy__['panos.call'](query) + return __proxy__["panos.call"](query) diff --git a/salt/modules/parallels.py b/salt/modules/parallels.py index e4b335add01..6a020354f06 100644 --- a/salt/modules/parallels.py +++ b/salt/modules/parallels.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Parallels Desktop VMs with ``prlctl`` and ``prlsrvctl``. Only some of the prlctl commands implemented so far. Of those that have been implemented, not all of the options may have been provided yet. For a complete reference, @@ -20,36 +20,39 @@ necessary): salt -- '*' parallels.prlsrvctl set '--mem-limit auto' runas=macdev .. versionadded:: 2016.3.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import re -import logging import shlex # Import salt libs import salt.utils.data import salt.utils.path import salt.utils.yaml -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import 3rd party libs from salt.ext import six -__virtualname__ = 'parallels' +__virtualname__ = "parallels" __func_alias__ = { - 'exec_': 'exec', + "exec_": "exec", } log = logging.getLogger(__name__) # Match any GUID -GUID_REGEX = re.compile(r'{?([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})}?', re.I) +GUID_REGEX = re.compile( + r"{?([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})}?", re.I +) def _normalize_args(args): - ''' + """ Return args as a list of strings - ''' + """ if isinstance(args, six.string_types): return shlex.split(args) @@ -60,7 +63,7 @@ def _normalize_args(args): def _find_guids(guid_string): - ''' + """ Return the set of GUIDs found in guid_string :param str guid_string: @@ -72,16 +75,16 @@ def _find_guids(guid_string): PARENT_SNAPSHOT_ID SNAPSHOT_ID {a5b8999f-5d95-4aff-82de-e515b0101b66} {a5b8999f-5d95-4aff-82de-e515b0101b66} *{a7345be5-ab66-478c-946e-a6c2caf14909} - ''' + """ guids = [] for found_guid in re.finditer(GUID_REGEX, guid_string): if found_guid.groups(): - guids.append(found_guid.group(0).strip('{}')) + guids.append(found_guid.group(0).strip("{}")) return sorted(list(set(guids))) def prlsrvctl(sub_cmd, args=None, runas=None): - ''' + """ Execute a prlsrvctl command .. versionadded:: 2016.11.0 @@ -102,21 +105,21 @@ def prlsrvctl(sub_cmd, args=None, runas=None): salt '*' parallels.prlsrvctl info runas=macdev salt '*' parallels.prlsrvctl usb list runas=macdev salt -- '*' parallels.prlsrvctl set '--mem-limit auto' runas=macdev - ''' - if not salt.utils.path.which('prlsrvctl'): - raise CommandExecutionError('prlsrvctl utility not available') + """ + if not salt.utils.path.which("prlsrvctl"): + raise CommandExecutionError("prlsrvctl utility not available") # Construct command - cmd = ['prlsrvctl', sub_cmd] + cmd = ["prlsrvctl", sub_cmd] if args: cmd.extend(_normalize_args(args)) # Execute command and return output - return __salt__['cmd.run'](cmd, runas=runas) + return __salt__["cmd.run"](cmd, runas=runas) def prlctl(sub_cmd, args=None, runas=None): - ''' + """ Execute a prlctl command :param str sub_cmd: @@ -135,21 +138,21 @@ def prlctl(sub_cmd, args=None, runas=None): salt '*' parallels.prlctl user list runas=macdev salt '*' parallels.prlctl exec 'macvm uname' runas=macdev salt -- '*' parallels.prlctl capture 'macvm --file macvm.display.png' runas=macdev - ''' - if not salt.utils.path.which('prlctl'): - raise CommandExecutionError('prlctl utility not available') + """ + if not salt.utils.path.which("prlctl"): + raise CommandExecutionError("prlctl utility not available") # Construct command - cmd = ['prlctl', sub_cmd] + cmd = ["prlctl", sub_cmd] if args: cmd.extend(_normalize_args(args)) # Execute command and return output - return __salt__['cmd.run'](cmd, runas=runas) + return __salt__["cmd.run"](cmd, runas=runas) def list_vms(name=None, info=False, all=False, args=None, runas=None, template=False): - ''' + """ List information about the VMs :param str name: @@ -185,7 +188,7 @@ def list_vms(name=None, info=False, all=False, args=None, runas=None, template=F salt '*' parallels.list_vms name=macvm info=True runas=macdev salt '*' parallels.list_vms info=True runas=macdev salt '*' parallels.list_vms ' -o uuid,status' all=True runas=macdev - ''' + """ # Construct argument list if args is None: args = [] @@ -195,18 +198,18 @@ def list_vms(name=None, info=False, all=False, args=None, runas=None, template=F if name: args.extend([name]) if info: - args.append('--info') + args.append("--info") if all: - args.append('--all') + args.append("--all") if template: - args.append('--template') + args.append("--template") # Execute command and return output - return prlctl('list', args, runas=runas) + return prlctl("list", args, runas=runas) def clone(name, new_name, linked=False, template=False, runas=None): - ''' + """ Clone a VM .. versionadded:: 2016.11.0 @@ -232,17 +235,17 @@ def clone(name, new_name, linked=False, template=False, runas=None): salt '*' parallels.clone macvm macvm_new runas=macdev salt '*' parallels.clone macvm macvm_templ template=True runas=macdev - ''' - args = [salt.utils.data.decode(name), '--name', salt.utils.data.decode(new_name)] + """ + args = [salt.utils.data.decode(name), "--name", salt.utils.data.decode(new_name)] if linked: - args.append('--linked') + args.append("--linked") if template: - args.append('--template') - return prlctl('clone', args, runas=runas) + args.append("--template") + return prlctl("clone", args, runas=runas) def delete(name, runas=None): - ''' + """ Delete a VM .. versionadded:: 2016.11.0 @@ -258,12 +261,12 @@ def delete(name, runas=None): .. code-block:: bash salt '*' parallels.exec macvm 'find /etc/paths.d' runas=macdev - ''' - return prlctl('delete', salt.utils.data.decode(name), runas=runas) + """ + return prlctl("delete", salt.utils.data.decode(name), runas=runas) def exists(name, runas=None): - ''' + """ Query whether a VM exists .. versionadded:: 2016.11.0 @@ -279,16 +282,16 @@ def exists(name, runas=None): .. code-block:: bash salt '*' parallels.exists macvm runas=macdev - ''' + """ vm_info = list_vms(name, info=True, runas=runas).splitlines() for info_line in vm_info: - if 'Name: {0}'.format(name) in info_line: + if "Name: {0}".format(name) in info_line: return True return False def start(name, runas=None): - ''' + """ Start a VM :param str name: @@ -302,12 +305,12 @@ def start(name, runas=None): .. code-block:: bash salt '*' parallels.start macvm runas=macdev - ''' - return prlctl('start', salt.utils.data.decode(name), runas=runas) + """ + return prlctl("start", salt.utils.data.decode(name), runas=runas) def stop(name, kill=False, runas=None): - ''' + """ Stop a VM :param str name: @@ -325,18 +328,18 @@ def stop(name, kill=False, runas=None): salt '*' parallels.stop macvm runas=macdev salt '*' parallels.stop macvm kill=True runas=macdev - ''' + """ # Construct argument list args = [salt.utils.data.decode(name)] if kill: - args.append('--kill') + args.append("--kill") # Execute command and return output - return prlctl('stop', args, runas=runas) + return prlctl("stop", args, runas=runas) def restart(name, runas=None): - ''' + """ Restart a VM by gracefully shutting it down and then restarting it @@ -351,12 +354,12 @@ def restart(name, runas=None): .. code-block:: bash salt '*' parallels.restart macvm runas=macdev - ''' - return prlctl('restart', salt.utils.data.decode(name), runas=runas) + """ + return prlctl("restart", salt.utils.data.decode(name), runas=runas) def reset(name, runas=None): - ''' + """ Reset a VM by performing a hard shutdown and then a restart :param str name: @@ -370,12 +373,12 @@ def reset(name, runas=None): .. code-block:: bash salt '*' parallels.reset macvm runas=macdev - ''' - return prlctl('reset', salt.utils.data.decode(name), runas=runas) + """ + return prlctl("reset", salt.utils.data.decode(name), runas=runas) def status(name, runas=None): - ''' + """ Status of a VM :param str name: @@ -389,12 +392,12 @@ def status(name, runas=None): .. code-block:: bash salt '*' parallels.status macvm runas=macdev - ''' - return prlctl('status', salt.utils.data.decode(name), runas=runas) + """ + return prlctl("status", salt.utils.data.decode(name), runas=runas) def exec_(name, command, runas=None): - ''' + """ Run a command on a VM :param str name: @@ -411,17 +414,17 @@ def exec_(name, command, runas=None): .. code-block:: bash salt '*' parallels.exec macvm 'find /etc/paths.d' runas=macdev - ''' + """ # Construct argument list args = [salt.utils.data.decode(name)] args.extend(_normalize_args(command)) # Execute command and return output - return prlctl('exec', args, runas=runas) + return prlctl("exec", args, runas=runas) def snapshot_id_to_name(name, snap_id, strict=False, runas=None): - ''' + """ Attempt to convert a snapshot ID to a snapshot name. If the snapshot has no name or if the ID is not found or invalid, an empty string will be returned @@ -453,7 +456,7 @@ def snapshot_id_to_name(name, snap_id, strict=False, runas=None): .. code-block:: bash salt '*' parallels.snapshot_id_to_name macvm a5b8999f-5d95-4aff-82de-e515b0101b66 runas=macdev - ''' + """ # Validate VM name and snapshot ID name = salt.utils.data.decode(name) if not re.match(GUID_REGEX, snap_id): @@ -462,7 +465,7 @@ def snapshot_id_to_name(name, snap_id, strict=False, runas=None): ) # Get the snapshot information of the snapshot having the requested ID - info = prlctl('snapshot-list', [name, '--id', snap_id], runas=runas) + info = prlctl("snapshot-list", [name, "--id", snap_id], runas=runas) # Parallels desktop returned no information for snap_id if not info: @@ -474,23 +477,22 @@ def snapshot_id_to_name(name, snap_id, strict=False, runas=None): try: data = salt.utils.yaml.safe_load(info) except salt.utils.yaml.YAMLError as err: - log.warning( - 'Could not interpret snapshot data returned from prlctl: %s', err - ) + log.warning("Could not interpret snapshot data returned from prlctl: %s", err) data = {} # Find the snapshot name if isinstance(data, dict): - snap_name = data.get('Name', '') + snap_name = data.get("Name", "") # If snapshot name is of type NoneType, then the snapshot is unnamed if snap_name is None: - snap_name = '' + snap_name = "" else: log.warning( - 'Could not interpret snapshot data returned from prlctl: ' - 'data is not formed as a dictionary: %s', data + "Could not interpret snapshot data returned from prlctl: " + "data is not formed as a dictionary: %s", + data, ) - snap_name = '' + snap_name = "" # Raise or return the result if not snap_name and strict: @@ -502,7 +504,7 @@ def snapshot_id_to_name(name, snap_id, strict=False, runas=None): def snapshot_name_to_id(name, snap_name, strict=False, runas=None): - ''' + """ Attempt to convert a snapshot name to a snapshot ID. If the name is not found an empty string is returned. If multiple snapshots share the same name, a list will be returned @@ -524,13 +526,13 @@ def snapshot_name_to_id(name, snap_name, strict=False, runas=None): .. code-block:: bash salt '*' parallels.snapshot_id_to_name macvm original runas=macdev - ''' + """ # Validate VM and snapshot names name = salt.utils.data.decode(name) snap_name = salt.utils.data.decode(snap_name) # Get a multiline string containing all the snapshot GUIDs - info = prlctl('snapshot-list', name, runas=runas) + info = prlctl("snapshot-list", name, runas=runas) # Get a set of all snapshot GUIDs in the string snap_ids = _find_guids(info) @@ -550,8 +552,9 @@ def snapshot_name_to_id(name, snap_name, strict=False, runas=None): elif len(named_ids) == 1: return named_ids[0] else: - multi_msg = ('Multiple snapshots for VM "{0}" have name ' - '"{1}"'.format(name, snap_name)) + multi_msg = 'Multiple snapshots for VM "{0}" have name ' '"{1}"'.format( + name, snap_name + ) if strict: raise SaltInvocationError(multi_msg) else: @@ -560,7 +563,7 @@ def snapshot_name_to_id(name, snap_name, strict=False, runas=None): def _validate_snap_name(name, snap_name, strict=True, runas=None): - ''' + """ Validate snapshot name and convert to snapshot ID :param str name: @@ -574,18 +577,18 @@ def _validate_snap_name(name, snap_name, strict=True, runas=None): :param str runas: The user that the prlctl command will be run as - ''' + """ snap_name = salt.utils.data.decode(snap_name) # Try to convert snapshot name to an ID without {} if re.match(GUID_REGEX, snap_name): - return snap_name.strip('{}') + return snap_name.strip("{}") else: return snapshot_name_to_id(name, snap_name, strict=strict, runas=runas) def list_snapshots(name, snap_name=None, tree=False, names=False, runas=None): - ''' + """ List the snapshots :param str name: @@ -613,7 +616,7 @@ def list_snapshots(name, snap_name=None, tree=False, names=False, runas=None): salt '*' parallels.list_snapshots macvm tree=True runas=macdev salt '*' parallels.list_snapshots macvm snap_name=original runas=macdev salt '*' parallels.list_snapshots macvm names=True runas=macdev - ''' + """ # Validate VM and snapshot names name = salt.utils.data.decode(name) if snap_name: @@ -622,12 +625,12 @@ def list_snapshots(name, snap_name=None, tree=False, names=False, runas=None): # Construct argument list args = [name] if tree: - args.append('--tree') + args.append("--tree") if snap_name: - args.extend(['--id', snap_name]) + args.extend(["--id", snap_name]) # Execute command - res = prlctl('snapshot-list', args, runas=runas) + res = prlctl("snapshot-list", args, runas=runas) # Construct ID, name pairs if names: @@ -635,10 +638,10 @@ def list_snapshots(name, snap_name=None, tree=False, names=False, runas=None): snap_ids = _find_guids(res) # Try to find the snapshot names - ret = '{0:<38} {1}\n'.format('Snapshot ID', 'Snapshot Name') + ret = "{0:<38} {1}\n".format("Snapshot ID", "Snapshot Name") for snap_id in snap_ids: snap_name = snapshot_id_to_name(name, snap_id, runas=runas) - ret += ('{{{0}}} {1}\n'.format(snap_id, salt.utils.data.decode(snap_name))) + ret += "{{{0}}} {1}\n".format(snap_id, salt.utils.data.decode(snap_name)) return ret # Return information directly from parallels desktop @@ -647,7 +650,7 @@ def list_snapshots(name, snap_name=None, tree=False, names=False, runas=None): def snapshot(name, snap_name=None, desc=None, runas=None): - ''' + """ Create a snapshot :param str name: @@ -668,7 +671,7 @@ def snapshot(name, snap_name=None, desc=None, runas=None): salt '*' parallels.create_snapshot macvm snap_name=macvm-original runas=macdev salt '*' parallels.create_snapshot macvm snap_name=macvm-updates desc='clean install with updates' runas=macdev - ''' + """ # Validate VM and snapshot names name = salt.utils.data.decode(name) if snap_name: @@ -677,16 +680,16 @@ def snapshot(name, snap_name=None, desc=None, runas=None): # Construct argument list args = [name] if snap_name: - args.extend(['--name', snap_name]) + args.extend(["--name", snap_name]) if desc: - args.extend(['--description', desc]) + args.extend(["--description", desc]) # Execute command and return output - return prlctl('snapshot', args, runas=runas) + return prlctl("snapshot", args, runas=runas) def delete_snapshot(name, snap_name, runas=None, all=False): - ''' + """ Delete a snapshot .. note:: @@ -714,7 +717,7 @@ def delete_snapshot(name, snap_name, runas=None, all=False): salt '*' parallels.delete_snapshot macvm 'unneeded snapshot' runas=macdev salt '*' parallels.delete_snapshot macvm 'Snapshot for linked clone' all=True runas=macdev - ''' + """ # strict means raise an error if multiple snapshot IDs found for the name given strict = not all @@ -727,12 +730,12 @@ def delete_snapshot(name, snap_name, runas=None, all=False): # Delete snapshot(s) ret = {} for snap_id in snap_ids: - snap_id = snap_id.strip('{}') + snap_id = snap_id.strip("{}") # Construct argument list - args = [name, '--id', snap_id] + args = [name, "--id", snap_id] # Execute command - ret[snap_id] = prlctl('snapshot-delete', args, runas=runas) + ret[snap_id] = prlctl("snapshot-delete", args, runas=runas) # Return results ret_keys = list(ret.keys()) @@ -743,7 +746,7 @@ def delete_snapshot(name, snap_name, runas=None, all=False): def revert_snapshot(name, snap_name, runas=None): - ''' + """ Revert a VM to a snapshot :param str name: @@ -760,13 +763,13 @@ def revert_snapshot(name, snap_name, runas=None): .. code-block:: bash salt '*' parallels.revert_snapshot macvm base-with-updates runas=macdev - ''' + """ # Validate VM and snapshot names name = salt.utils.data.decode(name) snap_name = _validate_snap_name(name, snap_name, runas=runas) # Construct argument list - args = [name, '--id', snap_name] + args = [name, "--id", snap_name] # Execute command and return output - return prlctl('snapshot-switch', args, runas=runas) + return prlctl("snapshot-switch", args, runas=runas) diff --git a/salt/modules/parted_partition.py b/salt/modules/parted_partition.py index c991530abaa..80402807e4f 100644 --- a/salt/modules/parted_partition.py +++ b/salt/modules/parted_partition.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing partitions on POSIX-like systems. :depends: - parted, partprobe, lsblk (usually parted and util-linux packages) @@ -14,15 +14,16 @@ http://www.gnu.org/software/parted/manual/html_chapter/parted_2.html In light of parted not directly supporting partition IDs, some of this module has been written to utilize sfdisk instead. For further information, please reference the man page for ``sfdisk(8)``. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import python libs import os import re import stat import string -import logging # Import Salt libs import salt.utils.path @@ -33,44 +34,88 @@ from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'partition' +__virtualname__ = "partition" # Define a function alias in order not to shadow built-in's __func_alias__ = { - 'set_': 'set', - 'list_': 'list', + "set_": "set", + "list_": "list", } -VALID_UNITS = set(['s', 'B', 'kB', 'MB', 'MiB', 'GB', 'GiB', 'TB', 'TiB', '%', - 'cyl', 'chs', 'compact']) +VALID_UNITS = set( + [ + "s", + "B", + "kB", + "MB", + "MiB", + "GB", + "GiB", + "TB", + "TiB", + "%", + "cyl", + "chs", + "compact", + ] +) -VALID_DISK_FLAGS = set(['cylinder_alignment', 'pmbr_boot', - 'implicit_partition_table']) +VALID_DISK_FLAGS = set(["cylinder_alignment", "pmbr_boot", "implicit_partition_table"]) -VALID_PARTITION_FLAGS = set(['boot', 'root', 'swap', 'hidden', 'raid', - 'lvm', 'lba', 'hp-service', 'palo', - 'prep', 'msftres', 'bios_grub', 'atvrecv', - 'diag', 'legacy_boot', 'msftdata', 'irst', - 'esp', 'type']) +VALID_PARTITION_FLAGS = set( + [ + "boot", + "root", + "swap", + "hidden", + "raid", + "lvm", + "lba", + "hp-service", + "palo", + "prep", + "msftres", + "bios_grub", + "atvrecv", + "diag", + "legacy_boot", + "msftdata", + "irst", + "esp", + "type", + ] +) def __virtual__(): - ''' + """ Only work on POSIX-like systems, which have parted and lsblk installed. These are usually provided by the ``parted`` and ``util-linux`` packages. - ''' + """ if salt.utils.platform.is_windows(): - return (False, 'The parted execution module failed to load ' - 'Windows systems are not supported.') - if not salt.utils.path.which('parted'): - return (False, 'The parted execution module failed to load ' - 'parted binary is not in the path.') - if not salt.utils.path.which('lsblk'): - return (False, 'The parted execution module failed to load ' - 'lsblk binary is not in the path.') - if not salt.utils.path.which('partprobe'): - return (False, 'The parted execution module failed to load ' - 'partprobe binary is not in the path.') + return ( + False, + "The parted execution module failed to load " + "Windows systems are not supported.", + ) + if not salt.utils.path.which("parted"): + return ( + False, + "The parted execution module failed to load " + "parted binary is not in the path.", + ) + if not salt.utils.path.which("lsblk"): + return ( + False, + "The parted execution module failed to load " + "lsblk binary is not in the path.", + ) + if not salt.utils.path.which("partprobe"): + return ( + False, + "The parted execution module failed to load " + "partprobe binary is not in the path.", + ) return __virtualname__ @@ -78,31 +123,29 @@ def __virtual__(): # validated within each function; collect them into validation functions here, # similar to _validate_device and _validate_partition_boundary def _validate_device(device): - ''' + """ Ensure the device name supplied is valid in a manner similar to the `exists` function, but raise errors on invalid input rather than return False. This function only validates a block device, it does not check if the block device is a drive or a partition or a filesystem, etc. - ''' + """ if os.path.exists(device): dev = os.stat(device).st_mode if stat.S_ISBLK(dev): return - raise CommandExecutionError( - 'Invalid device passed to partition module.' - ) + raise CommandExecutionError("Invalid device passed to partition module.") def _validate_partition_boundary(boundary): - ''' + """ Ensure valid partition boundaries are supplied. - ''' + """ boundary = six.text_type(boundary) - match = re.search(r'^([\d.]+)(\D*)$', boundary) + match = re.search(r"^([\d.]+)(\D*)$", boundary) if match: unit = match.group(2) if not unit or unit in VALID_UNITS: @@ -113,7 +156,7 @@ def _validate_partition_boundary(boundary): def probe(*devices): - ''' + """ Ask the kernel to update its local partition data. When no args are specified all block devices are tried. @@ -127,17 +170,17 @@ def probe(*devices): salt '*' partition.probe salt '*' partition.probe /dev/sda salt '*' partition.probe /dev/sda /dev/sdb - ''' + """ for device in devices: _validate_device(device) - cmd = 'partprobe -- {0}'.format(" ".join(devices)) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "partprobe -- {0}".format(" ".join(devices)) + out = __salt__["cmd.run"](cmd).splitlines() return out def list_(device, unit=None): - ''' + """ Prints partition information of given CLI Examples: @@ -147,45 +190,45 @@ def list_(device, unit=None): salt '*' partition.list /dev/sda salt '*' partition.list /dev/sda unit=s salt '*' partition.list /dev/sda unit=kB - ''' + """ _validate_device(device) if unit: if unit not in VALID_UNITS: - raise CommandExecutionError( - 'Invalid unit passed to partition.part_list' - ) - cmd = 'parted -m -s {0} unit {1} print'.format(device, unit) + raise CommandExecutionError("Invalid unit passed to partition.part_list") + cmd = "parted -m -s {0} unit {1} print".format(device, unit) else: - cmd = 'parted -m -s {0} print'.format(device) + cmd = "parted -m -s {0} print".format(device) - out = __salt__['cmd.run_stdout'](cmd).splitlines() - ret = {'info': {}, 'partitions': {}} - mode = 'info' + out = __salt__["cmd.run_stdout"](cmd).splitlines() + ret = {"info": {}, "partitions": {}} + mode = "info" for line in out: - if line in ('BYT;', 'CHS;', 'CYL;'): + if line in ("BYT;", "CHS;", "CYL;"): continue - cols = line.rstrip(';').split(':') - if mode == 'info': + cols = line.rstrip(";").split(":") + if mode == "info": if 7 <= len(cols) <= 8: - ret['info'] = { - 'disk': cols[0], - 'size': cols[1], - 'interface': cols[2], - 'logical sector': cols[3], - 'physical sector': cols[4], - 'partition table': cols[5], - 'model': cols[6]} + ret["info"] = { + "disk": cols[0], + "size": cols[1], + "interface": cols[2], + "logical sector": cols[3], + "physical sector": cols[4], + "partition table": cols[5], + "model": cols[6], + } if len(cols) == 8: - ret['info']['disk flags'] = cols[7] + ret["info"]["disk flags"] = cols[7] # Older parted (2.x) doesn't show disk flags in the 'print' # output, and will return a 7-column output for the info # line. In these cases we just leave this field out of the # return dict. - mode = 'partitions' + mode = "partitions" else: raise CommandExecutionError( - 'Problem encountered while parsing output from parted') + "Problem encountered while parsing output from parted" + ) else: # Parted (v3.1) have a variable field list in machine # readable output: @@ -197,24 +240,25 @@ def list_(device, unit=None): # set, the last three fields are replaced with the # 'free' text. # - fields = ['number', 'start', 'end'] - if unit != 'chs': - fields.append('size') - if cols[-1] == 'free': + fields = ["number", "start", "end"] + if unit != "chs": + fields.append("size") + if cols[-1] == "free": # Drop the last element from the list cols.pop() else: - fields.extend(['file system', 'name', 'flags']) + fields.extend(["file system", "name", "flags"]) if len(fields) == len(cols): - ret['partitions'][cols[0]] = dict(six.moves.zip(fields, cols)) + ret["partitions"][cols[0]] = dict(six.moves.zip(fields, cols)) else: raise CommandExecutionError( - 'Problem encountered while parsing output from parted') + "Problem encountered while parsing output from parted" + ) return ret def align_check(device, part_type, partition): - ''' + """ Check if partition satisfies the alignment constraint of part_type. Type must be "minimal" or "optimal". @@ -223,30 +267,24 @@ def align_check(device, part_type, partition): .. code-block:: bash salt '*' partition.align_check /dev/sda minimal 1 - ''' + """ _validate_device(device) - if part_type not in set(['minimal', 'optimal']): - raise CommandExecutionError( - 'Invalid part_type passed to partition.align_check' - ) + if part_type not in set(["minimal", "optimal"]): + raise CommandExecutionError("Invalid part_type passed to partition.align_check") try: int(partition) except Exception: # pylint: disable=broad-except - raise CommandExecutionError( - 'Invalid partition passed to partition.align_check' - ) + raise CommandExecutionError("Invalid partition passed to partition.align_check") - cmd = 'parted -m {0} align-check {1} {2}'.format( - device, part_type, partition - ) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "parted -m {0} align-check {1} {2}".format(device, part_type, partition) + out = __salt__["cmd.run"](cmd).splitlines() return out def check(device, minor): - ''' + """ Checks if the file system on partition has any errors. CLI Example: @@ -254,23 +292,21 @@ def check(device, minor): .. code-block:: bash salt '*' partition.check 1 - ''' + """ _validate_device(device) try: int(minor) except Exception: # pylint: disable=broad-except - raise CommandExecutionError( - 'Invalid minor number passed to partition.check' - ) + raise CommandExecutionError("Invalid minor number passed to partition.check") - cmd = 'parted -m -s {0} check {1}'.format(device, minor) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "parted -m -s {0} check {1}".format(device, minor) + out = __salt__["cmd.run"](cmd).splitlines() return out def cp(device, from_minor, to_minor): # pylint: disable=C0103 - ''' + """ Copies the file system on the partition to partition , deleting the original contents of the destination partition. @@ -280,24 +316,22 @@ def cp(device, from_minor, to_minor): # pylint: disable=C0103 .. code-block:: bash salt '*' partition.cp /dev/sda 2 3 - ''' + """ _validate_device(device) try: int(from_minor) int(to_minor) except Exception: # pylint: disable=broad-except - raise CommandExecutionError( - 'Invalid minor number passed to partition.cp' - ) + raise CommandExecutionError("Invalid minor number passed to partition.cp") - cmd = 'parted -m -s {0} cp {1} {2}'.format(device, from_minor, to_minor) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "parted -m -s {0} cp {1} {2}".format(device, from_minor, to_minor) + out = __salt__["cmd.run"](cmd).splitlines() return out def get_id(device, minor): - ''' + """ Prints the system ID for the partition. Some typical values are:: b: FAT32 (vfat) @@ -312,23 +346,21 @@ def get_id(device, minor): .. code-block:: bash salt '*' partition.get_id /dev/sda 1 - ''' + """ _validate_device(device) try: int(minor) except Exception: # pylint: disable=broad-except - raise CommandExecutionError( - 'Invalid minor number passed to partition.get_id' - ) + raise CommandExecutionError("Invalid minor number passed to partition.get_id") - cmd = 'sfdisk --print-id {0} {1}'.format(device, minor) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "sfdisk --print-id {0} {1}".format(device, minor) + out = __salt__["cmd.run"](cmd).splitlines() return out def set_id(device, minor, system_id): - ''' + """ Sets the system ID for the partition. Some typical values are:: b: FAT32 (vfat) @@ -343,28 +375,24 @@ def set_id(device, minor, system_id): .. code-block:: bash salt '*' partition.set_id /dev/sda 1 83 - ''' + """ _validate_device(device) try: int(minor) except Exception: # pylint: disable=broad-except - raise CommandExecutionError( - 'Invalid minor number passed to partition.set_id' - ) + raise CommandExecutionError("Invalid minor number passed to partition.set_id") if system_id not in system_types(): - raise CommandExecutionError( - 'Invalid system_id passed to partition.set_id' - ) + raise CommandExecutionError("Invalid system_id passed to partition.set_id") - cmd = 'sfdisk --change-id {0} {1} {2}'.format(device, minor, system_id) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "sfdisk --change-id {0} {1} {2}".format(device, minor, system_id) + out = __salt__["cmd.run"](cmd).splitlines() return out def system_types(): - ''' + """ List the system types that are supported by the installed version of sfdisk CLI Example: @@ -372,12 +400,12 @@ def system_types(): .. code-block:: bash salt '*' partition.system_types - ''' + """ ret = {} - for line in __salt__['cmd.run']('sfdisk -T').splitlines(): + for line in __salt__["cmd.run"]("sfdisk -T").splitlines(): if not line: continue - if line.startswith('Id'): + if line.startswith("Id"): continue comps = line.strip().split() ret[comps[0]] = comps[1] @@ -385,17 +413,34 @@ def system_types(): def _is_fstype(fs_type): - ''' + """ Check if file system type is supported in module :param fs_type: file system type :return: True if fs_type is supported in this module, False otherwise - ''' - return fs_type in set(['ext2', 'ext3', 'ext4', 'fat32', 'fat16', 'linux-swap', 'reiserfs', - 'hfs', 'hfs+', 'hfsx', 'NTFS', 'ntfs', 'ufs']) + """ + return fs_type in set( + [ + "btrfs", + "ext2", + "ext3", + "ext4", + "fat32", + "fat16", + "linux-swap", + "reiserfs", + "hfs", + "hfs+", + "hfsx", + "NTFS", + "ntfs", + "ufs", + "xfs", + ] + ) def mkfs(device, fs_type): - ''' + """ Makes a file system on partition , destroying all data that resides on that partition. must be one of "ext2", "fat32", "fat16", "linux-swap" or "reiserfs" (if libreiserfs is installed) @@ -405,29 +450,29 @@ def mkfs(device, fs_type): .. code-block:: bash salt '*' partition.mkfs /dev/sda2 fat32 - ''' + """ _validate_device(device) if not _is_fstype(fs_type): - raise CommandExecutionError('Invalid fs_type passed to partition.mkfs') + raise CommandExecutionError("Invalid fs_type passed to partition.mkfs") - if fs_type == 'NTFS': - fs_type = 'ntfs' + if fs_type == "NTFS": + fs_type = "ntfs" - if fs_type == 'linux-swap': - mkfs_cmd = 'mkswap' + if fs_type == "linux-swap": + mkfs_cmd = "mkswap" else: - mkfs_cmd = 'mkfs.{0}'.format(fs_type) + mkfs_cmd = "mkfs.{0}".format(fs_type) if not salt.utils.path.which(mkfs_cmd): - return 'Error: {0} is unavailable.'.format(mkfs_cmd) - cmd = '{0} {1}'.format(mkfs_cmd, device) - out = __salt__['cmd.run'](cmd).splitlines() + return "Error: {0} is unavailable.".format(mkfs_cmd) + cmd = "{0} {1}".format(mkfs_cmd, device) + out = __salt__["cmd.run"](cmd).splitlines() return out def mklabel(device, label_type): - ''' + """ Create a new disklabel (partition table) of label_type. Type should be one of "aix", "amiga", "bsd", "dvh", "gpt", "loop", "mac", @@ -438,21 +483,19 @@ def mklabel(device, label_type): .. code-block:: bash salt '*' partition.mklabel /dev/sda msdos - ''' - if label_type not in set([ - 'aix', 'amiga', 'bsd', 'dvh', 'gpt', 'loop', 'mac', 'msdos', 'pc98', 'sun' - ]): - raise CommandExecutionError( - 'Invalid label_type passed to partition.mklabel' - ) + """ + if label_type not in set( + ["aix", "amiga", "bsd", "dvh", "gpt", "loop", "mac", "msdos", "pc98", "sun"] + ): + raise CommandExecutionError("Invalid label_type passed to partition.mklabel") - cmd = ('parted', '-m', '-s', device, 'mklabel', label_type) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + cmd = ("parted", "-m", "-s", device, "mklabel", label_type) + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() return out def mkpart(device, part_type, fs_type=None, start=None, end=None): - ''' + """ Make a part_type partition for filesystem fs_type, beginning at start and ending at end (by default in megabytes). part_type should be one of "primary", "logical", or "extended". @@ -463,38 +506,45 @@ def mkpart(device, part_type, fs_type=None, start=None, end=None): salt '*' partition.mkpart /dev/sda primary fs_type=fat32 start=0 end=639 salt '*' partition.mkpart /dev/sda primary start=0 end=639 - ''' - if part_type not in set(['primary', 'logical', 'extended']): - raise CommandExecutionError( - 'Invalid part_type passed to partition.mkpart' - ) + """ + if part_type not in set(["primary", "logical", "extended"]): + raise CommandExecutionError("Invalid part_type passed to partition.mkpart") if not _is_fstype(fs_type): - raise CommandExecutionError( - 'Invalid fs_type passed to partition.mkpart' - ) + raise CommandExecutionError("Invalid fs_type passed to partition.mkpart") if start is not None and end is not None: _validate_partition_boundary(start) _validate_partition_boundary(end) if start is None: - start = '' + start = "" if end is None: - end = '' + end = "" if fs_type: - cmd = ('parted', '-m', '-s', '--', device, 'mkpart', part_type, fs_type, start, end) + cmd = ( + "parted", + "-m", + "-s", + "--", + device, + "mkpart", + part_type, + fs_type, + start, + end, + ) else: - cmd = ('parted', '-m', '-s', '--', device, 'mkpart', part_type, start, end) + cmd = ("parted", "-m", "-s", "--", device, "mkpart", part_type, start, end) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() return out def mkpartfs(device, part_type, fs_type, start, end): - ''' + """ Make a partition with a new filesystem of , beginning at and ending at (by default in megabytes). @@ -507,31 +557,27 @@ def mkpartfs(device, part_type, fs_type, start, end): .. code-block:: bash salt '*' partition.mkpartfs /dev/sda logical ext2 440 670 - ''' + """ _validate_device(device) - if part_type not in set(['primary', 'logical', 'extended']): - raise CommandExecutionError( - 'Invalid part_type passed to partition.mkpartfs' - ) + if part_type not in set(["primary", "logical", "extended"]): + raise CommandExecutionError("Invalid part_type passed to partition.mkpartfs") if not _is_fstype(fs_type): - raise CommandExecutionError( - 'Invalid fs_type passed to partition.mkpartfs' - ) + raise CommandExecutionError("Invalid fs_type passed to partition.mkpartfs") _validate_partition_boundary(start) _validate_partition_boundary(end) - cmd = 'parted -m -s -- {0} mkpart {1} {2} {3} {4}'.format( + cmd = "parted -m -s -- {0} mkpart {1} {2} {3} {4}".format( device, part_type, fs_type, start, end ) - out = __salt__['cmd.run'](cmd).splitlines() + out = __salt__["cmd.run"](cmd).splitlines() return out def name(device, partition, name): - ''' + """ Set the name of partition to name. This option works only on Mac, PC98, and GPT disklabels. The name can be placed in quotes, if necessary. @@ -540,30 +586,26 @@ def name(device, partition, name): .. code-block:: bash salt '*' partition.name /dev/sda 1 'My Documents' - ''' + """ _validate_device(device) try: int(partition) except Exception: # pylint: disable=broad-except - raise CommandExecutionError( - 'Invalid partition passed to partition.name' - ) + raise CommandExecutionError("Invalid partition passed to partition.name") - valid = string.ascii_letters + string.digits + ' _-' + valid = string.ascii_letters + string.digits + " _-" for letter in name: if letter not in valid: - raise CommandExecutionError( - 'Invalid characters passed to partition.name' - ) + raise CommandExecutionError("Invalid characters passed to partition.name") cmd = '''parted -m -s {0} name {1} "'{2}'"'''.format(device, partition, name) - out = __salt__['cmd.run'](cmd).splitlines() + out = __salt__["cmd.run"](cmd).splitlines() return out def rescue(device, start, end): - ''' + """ Rescue a lost partition that was located somewhere between start and end. If a partition is found, parted will ask if you want to create an entry for it in the partition table. @@ -573,18 +615,18 @@ def rescue(device, start, end): .. code-block:: bash salt '*' partition.rescue /dev/sda 0 8056 - ''' + """ _validate_device(device) _validate_partition_boundary(start) _validate_partition_boundary(end) - cmd = 'parted -m -s {0} rescue {1} {2}'.format(device, start, end) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "parted -m -s {0} rescue {1} {2}".format(device, start, end) + out = __salt__["cmd.run"](cmd).splitlines() return out def resize(device, minor, start, end): - ''' + """ Resizes the partition with number . The partition will start from the beginning of the disk, and end @@ -597,29 +639,25 @@ def resize(device, minor, start, end): .. code-block:: bash salt '*' partition.resize /dev/sda 3 200 850 - ''' + """ _validate_device(device) try: int(minor) except Exception: # pylint: disable=broad-except - raise CommandExecutionError( - 'Invalid minor number passed to partition.resize' - ) + raise CommandExecutionError("Invalid minor number passed to partition.resize") _validate_partition_boundary(start) _validate_partition_boundary(end) - out = __salt__['cmd.run']( - 'parted -m -s -- {0} resize {1} {2} {3}'.format( - device, minor, start, end - ) + out = __salt__["cmd.run"]( + "parted -m -s -- {0} resize {1} {2} {3}".format(device, minor, start, end) ) return out.splitlines() def rm(device, minor): # pylint: disable=C0103 - ''' + """ Removes the partition with number . CLI Example: @@ -627,23 +665,21 @@ def rm(device, minor): # pylint: disable=C0103 .. code-block:: bash salt '*' partition.rm /dev/sda 5 - ''' + """ _validate_device(device) try: int(minor) except Exception: # pylint: disable=broad-except - raise CommandExecutionError( - 'Invalid minor number passed to partition.rm' - ) + raise CommandExecutionError("Invalid minor number passed to partition.rm") - cmd = 'parted -m -s {0} rm {1}'.format(device, minor) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "parted -m -s {0} rm {1}".format(device, minor) + out = __salt__["cmd.run"](cmd).splitlines() return out def set_(device, minor, flag, state): - ''' + """ Changes a flag on the partition with number . A flag can be either "on" or "off" (make sure to use proper quoting, see @@ -676,29 +712,27 @@ def set_(device, minor, flag, state): .. code-block:: bash salt '*' partition.set /dev/sda 1 boot '"on"' - ''' + """ _validate_device(device) try: int(minor) except Exception: # pylint: disable=broad-except - raise CommandExecutionError( - 'Invalid minor number passed to partition.set' - ) + raise CommandExecutionError("Invalid minor number passed to partition.set") if flag not in VALID_PARTITION_FLAGS: - raise CommandExecutionError('Invalid flag passed to partition.set') + raise CommandExecutionError("Invalid flag passed to partition.set") - if state not in set(['on', 'off']): - raise CommandExecutionError('Invalid state passed to partition.set') + if state not in set(["on", "off"]): + raise CommandExecutionError("Invalid state passed to partition.set") - cmd = 'parted -m -s {0} set {1} {2} {3}'.format(device, minor, flag, state) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "parted -m -s {0} set {1} {2} {3}".format(device, minor, flag, state) + out = __salt__["cmd.run"](cmd).splitlines() return out def toggle(device, partition, flag): - ''' + """ Toggle the state of on . Valid flags are the same as the set command. @@ -707,26 +741,26 @@ def toggle(device, partition, flag): .. code-block:: bash salt '*' partition.toggle /dev/sda 1 boot - ''' + """ _validate_device(device) try: int(partition) except Exception: # pylint: disable=broad-except raise CommandExecutionError( - 'Invalid partition number passed to partition.toggle' + "Invalid partition number passed to partition.toggle" ) if flag not in VALID_PARTITION_FLAGS: - raise CommandExecutionError('Invalid flag passed to partition.toggle') + raise CommandExecutionError("Invalid flag passed to partition.toggle") - cmd = 'parted -m -s {0} toggle {1} {2}'.format(device, partition, flag) - out = __salt__['cmd.run'](cmd).splitlines() + cmd = "parted -m -s {0} toggle {1} {2}".format(device, partition, flag) + out = __salt__["cmd.run"](cmd).splitlines() return out def disk_set(device, flag, state): - ''' + """ Changes a flag on selected device. A flag can be either "on" or "off" (make sure to use proper @@ -744,22 +778,22 @@ def disk_set(device, flag, state): .. code-block:: bash salt '*' partition.disk_set /dev/sda pmbr_boot '"on"' - ''' + """ _validate_device(device) if flag not in VALID_DISK_FLAGS: - raise CommandExecutionError('Invalid flag passed to partition.disk_set') + raise CommandExecutionError("Invalid flag passed to partition.disk_set") - if state not in set(['on', 'off']): - raise CommandExecutionError('Invalid state passed to partition.disk_set') + if state not in set(["on", "off"]): + raise CommandExecutionError("Invalid state passed to partition.disk_set") - cmd = ['parted', '-m', '-s', device, 'disk_set', flag, state] - out = __salt__['cmd.run'](cmd).splitlines() + cmd = ["parted", "-m", "-s", device, "disk_set", flag, state] + out = __salt__["cmd.run"](cmd).splitlines() return out def disk_toggle(device, flag): - ''' + """ Toggle the state of on . Valid flags are the same as the disk_set command. @@ -768,19 +802,19 @@ def disk_toggle(device, flag): .. code-block:: bash salt '*' partition.disk_toggle /dev/sda pmbr_boot - ''' + """ _validate_device(device) if flag not in VALID_DISK_FLAGS: - raise CommandExecutionError('Invalid flag passed to partition.disk_toggle') + raise CommandExecutionError("Invalid flag passed to partition.disk_toggle") - cmd = ['parted', '-m', '-s', device, 'disk_toggle', flag] - out = __salt__['cmd.run'](cmd).splitlines() + cmd = ["parted", "-m", "-s", device, "disk_toggle", flag] + out = __salt__["cmd.run"](cmd).splitlines() return out -def exists(device=''): - ''' +def exists(device=""): + """ Check to see if the partition exists CLI Example: @@ -788,7 +822,7 @@ def exists(device=''): .. code-block:: bash salt '*' partition.exists /dev/sdb1 - ''' + """ if os.path.exists(device): dev = os.stat(device).st_mode @@ -799,7 +833,7 @@ def exists(device=''): def get_block_device(): - ''' + """ Retrieve a list of disk devices .. versionadded:: 2014.7.0 @@ -809,7 +843,7 @@ def get_block_device(): .. code-block:: bash salt '*' partition.get_block_device - ''' - cmd = 'lsblk -n -io KNAME -d -e 1,7,11 -l' - devs = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "lsblk -n -io KNAME -d -e 1,7,11 -l" + devs = __salt__["cmd.run"](cmd).splitlines() return devs diff --git a/salt/modules/pcs.py b/salt/modules/pcs.py index 6900c065eef..bf7711436df 100644 --- a/salt/modules/pcs.py +++ b/salt/modules/pcs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configure a Pacemaker/Corosync cluster with PCS =============================================== @@ -9,7 +9,7 @@ Pacemaker/Cororsync conifguration system (PCS) :depends: pcs .. versionadded:: 2016.3.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -18,16 +18,18 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load if pcs package is installed - ''' - if salt.utils.path.which('pcs'): - return 'pcs' - return False + """ + if salt.utils.path.which("pcs"): + return "pcs" + return (False, "Missing dependency: pcs") -def item_show(item, item_id=None, item_type=None, show='show', extra_args=None, cibfile=None): - ''' +def item_show( + item, item_id=None, item_type=None, show="show", extra_args=None, cibfile=None +): + """ Show an item via pcs command (mainly for use with the pcs state module) @@ -43,11 +45,11 @@ def item_show(item, item_id=None, item_type=None, show='show', extra_args=None, additional options for the pcs command cibfile use cibfile instead of the live CIB - ''' - cmd = ['pcs'] + """ + cmd = ["pcs"] if isinstance(cibfile, six.string_types): - cmd += ['-f', cibfile] + cmd += ["-f", cibfile] if isinstance(item, six.string_types): cmd += [item] @@ -55,7 +57,7 @@ def item_show(item, item_id=None, item_type=None, show='show', extra_args=None, cmd += item # constraint command follows a different order - if item in ['constraint']: + if item in ["constraint"]: cmd += [item_type] if isinstance(show, six.string_types): @@ -70,15 +72,17 @@ def item_show(item, item_id=None, item_type=None, show='show', extra_args=None, cmd += extra_args # constraint command only shows id, when using '--full'-parameter - if item in ['constraint']: - if not isinstance(extra_args, (list, tuple)) or '--full' not in extra_args: - cmd += ['--full'] + if item in ["constraint"]: + if not isinstance(extra_args, (list, tuple)) or "--full" not in extra_args: + cmd += ["--full"] - return __salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=False) + return __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) -def item_create(item, item_id, item_type, create='create', extra_args=None, cibfile=None): - ''' +def item_create( + item, item_id, item_type, create="create", extra_args=None, cibfile=None +): + """ Create an item via pcs command (mainly for use with the pcs state module) @@ -94,10 +98,10 @@ def item_create(item, item_id, item_type, create='create', extra_args=None, cibf additional options for the pcs command cibfile use cibfile instead of the live CIB - ''' - cmd = ['pcs'] + """ + cmd = ["pcs"] if isinstance(cibfile, six.string_types): - cmd += ['-f', cibfile] + cmd += ["-f", cibfile] if isinstance(item, six.string_types): cmd += [item] @@ -105,7 +109,7 @@ def item_create(item, item_id, item_type, create='create', extra_args=None, cibf cmd += item # constraint command follows a different order - if item in ['constraint']: + if item in ["constraint"]: if isinstance(item_type, six.string_types): cmd += [item_type] @@ -116,22 +120,22 @@ def item_create(item, item_id, item_type, create='create', extra_args=None, cibf # constraint command needs item_id in format 'id= @@ -7,59 +7,63 @@ Manage accounts in Samba's passdb using pdbedit :platform: posix .. versionadded:: 2017.7.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import binascii +import hashlib +import logging + # Import Python libs import re -import logging -import hashlib -import binascii + +import salt.modules.cmdmod +import salt.utils.path + +# Import Salt libs +from salt.ext import six + try: from shlex import quote as _quote_args # pylint: disable=e0611 except ImportError: from pipes import quote as _quote_args -# Import Salt libs -from salt.ext import six -import salt.utils.path -import salt.modules.cmdmod log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pdbedit' +__virtualname__ = "pdbedit" # Function aliases __func_alias__ = { - 'list_users': 'list', - 'get_user': 'get', + "list_users": "list", + "get_user": "get", } def __virtual__(): - ''' + """ Provides pdbedit if available - ''' + """ # NOTE: check for pdbedit command - if not salt.utils.path.which('pdbedit'): - return (False, 'pdbedit command is not available') + if not salt.utils.path.which("pdbedit"): + return (False, "pdbedit command is not available") # NOTE: check version is >= 4.8.x - ver = salt.modules.cmdmod.run('pdbedit -V') - ver_regex = re.compile(r'^Version\s(\d+)\.(\d+)\.(\d+)$') + ver = salt.modules.cmdmod.run("pdbedit -V") + ver_regex = re.compile(r"^Version\s(\d+)\.(\d+)\.(\d+)$") ver_match = ver_regex.match(ver) if not ver_match: - return (False, 'pdbedit -V returned an unknown version format') + return (False, "pdbedit -V returned an unknown version format") if not (int(ver_match.group(1)) >= 4 and int(ver_match.group(2)) >= 8): - return (False, 'pdbedit is to old, 4.8.0 or newer is required') + return (False, "pdbedit is to old, 4.8.0 or newer is required") return __virtualname__ def generate_nt_hash(password): - ''' + """ Generate a NT HASH CLI Example: @@ -67,17 +71,14 @@ def generate_nt_hash(password): .. code-block:: bash salt '*' pdbedit.generate_nt_hash my_passwd - ''' + """ return binascii.hexlify( - hashlib.new( - 'md4', - password.encode('utf-16le') - ).digest() + hashlib.new("md4", password.encode("utf-16le")).digest() ).upper() def list_users(verbose=True, hashes=False): - ''' + """ List user accounts verbose : boolean @@ -90,43 +91,45 @@ def list_users(verbose=True, hashes=False): .. code-block:: bash salt '*' pdbedit.list - ''' + """ users = {} if verbose else [] if verbose: # parse detailed user data - res = __salt__['cmd.run_all']( - 'pdbedit --list --verbose {hashes}'.format(hashes="--smbpasswd-style" if hashes else ""), + res = __salt__["cmd.run_all"]( + "pdbedit --list --verbose {hashes}".format( + hashes="--smbpasswd-style" if hashes else "" + ), ) - if res['retcode'] > 0: - log.error(res['stderr'] if 'stderr' in res else res['stdout']) + if res["retcode"] > 0: + log.error(res["stderr"] if "stderr" in res else res["stdout"]) return users user_data = {} - for user in res['stdout'].splitlines(): - if user.startswith('-'): - if 'unix username' in user_data: - users[user_data['unix username']] = user_data + for user in res["stdout"].splitlines(): + if user.startswith("-"): + if "unix username" in user_data: + users[user_data["unix username"]] = user_data user_data = {} - elif ':' in user: - label = user[:user.index(':')].strip().lower() - data = user[(user.index(':')+1):].strip() + elif ":" in user: + label = user[: user.index(":")].strip().lower() + data = user[(user.index(":") + 1) :].strip() user_data[label] = data if user_data: - users[user_data['unix username']] = user_data + users[user_data["unix username"]] = user_data else: # list users - res = __salt__['cmd.run_all']('pdbedit --list') + res = __salt__["cmd.run_all"]("pdbedit --list") - if res['retcode'] > 0: - return {'Error': res['stderr'] if 'stderr' in res else res['stdout']} + if res["retcode"] > 0: + return {"Error": res["stderr"] if "stderr" in res else res["stdout"]} - for user in res['stdout'].splitlines(): - if ':' not in user: + for user in res["stdout"].splitlines(): + if ":" not in user: continue - user_data = user.split(':') + user_data = user.split(":") if len(user_data) >= 3: users.append(user_data[0]) @@ -134,7 +137,7 @@ def list_users(verbose=True, hashes=False): def get_user(login, hashes=False): - ''' + """ Get user account details login : string @@ -147,13 +150,13 @@ def get_user(login, hashes=False): .. code-block:: bash salt '*' pdbedit.get kaylee - ''' + """ users = list_users(verbose=True, hashes=hashes) return users[login] if login in users else {} def delete(login): - ''' + """ Delete user account login : string @@ -164,22 +167,22 @@ def delete(login): .. code-block:: bash salt '*' pdbedit.delete wash - ''' + """ if login in list_users(False): - res = __salt__['cmd.run_all']( - 'pdbedit --delete {login}'.format(login=_quote_args(login)), + res = __salt__["cmd.run_all"]( + "pdbedit --delete {login}".format(login=_quote_args(login)), ) - if res['retcode'] > 0: - return {login: res['stderr'] if 'stderr' in res else res['stdout']} + if res["retcode"] > 0: + return {login: res["stderr"] if "stderr" in res else res["stdout"]} - return {login: 'deleted'} + return {login: "deleted"} - return {login: 'absent'} + return {login: "absent"} def create(login, password, password_hashed=False, machine_account=False): - ''' + """ Create user account login : string @@ -197,8 +200,8 @@ def create(login, password, password_hashed=False, machine_account=False): salt '*' pdbedit.create zoe 9764951149F84E770889011E1DC4A927 nthash salt '*' pdbedit.create river 1sw4ll0w3d4bug - ''' - ret = 'unchanged' + """ + ret = "unchanged" # generate nt hash if needed if password_hashed: @@ -210,47 +213,55 @@ def create(login, password, password_hashed=False, machine_account=False): # create user if login not in list_users(False): # NOTE: --create requires a password, even if blank - res = __salt__['cmd.run_all']( - cmd='pdbedit --create --user {login} -t {machine}'.format( + res = __salt__["cmd.run_all"]( + cmd="pdbedit --create --user {login} -t {machine}".format( login=_quote_args(login), machine="--machine" if machine_account else "", ), stdin="{password}\n{password}\n".format(password=password), ) - if res['retcode'] > 0: - return {login: res['stderr'] if 'stderr' in res else res['stdout']} + if res["retcode"] > 0: + return {login: res["stderr"] if "stderr" in res else res["stdout"]} - ret = 'created' + ret = "created" # update password if needed user = get_user(login, True) - if user['nt hash'] != password_hash: - res = __salt__['cmd.run_all']( - 'pdbedit --modify --user {login} --set-nt-hash={nthash}'.format( - login=_quote_args(login), - nthash=_quote_args(password_hash) + if user["nt hash"] != password_hash: + res = __salt__["cmd.run_all"]( + "pdbedit --modify --user {login} --set-nt-hash={nthash}".format( + login=_quote_args(login), nthash=_quote_args(password_hash) ), ) - if res['retcode'] > 0: - return {login: res['stderr'] if 'stderr' in res else res['stdout']} + if res["retcode"] > 0: + return {login: res["stderr"] if "stderr" in res else res["stdout"]} - if ret != 'created': - ret = 'updated' + if ret != "created": + ret = "updated" return {login: ret} def modify( - login, password=None, password_hashed=False, - domain=None, profile=None, script=None, - drive=None, homedir=None, fullname=None, - account_desc=None, account_control=None, - machine_sid=None, user_sid=None, - reset_login_hours=False, reset_bad_password_count=False, + login, + password=None, + password_hashed=False, + domain=None, + profile=None, + script=None, + drive=None, + homedir=None, + fullname=None, + account_desc=None, + account_control=None, + machine_sid=None, + user_sid=None, + reset_login_hours=False, + reset_bad_password_count=False, ): - ''' + """ Modify user account login : string @@ -303,61 +314,67 @@ def modify( salt '*' pdbedit.modify simon password=r1v3r salt '*' pdbedit.modify jane drive='V:' homedir='\\\\serenity\\jane\\profile' salt '*' pdbedit.modify mal account_control=NX - ''' - ret = 'unchanged' + """ + ret = "unchanged" # flag mapping flags = { - 'domain': '--domain=', - 'full name': '--fullname=', - 'account desc': '--account-desc=', - 'home directory': '--homedir=', - 'homedir drive': '--drive=', - 'profile path': '--profile=', - 'logon script': '--script=', - 'account flags': '--account-control=', - 'user sid': '-U ', - 'machine sid': '-M ', + "domain": "--domain=", + "full name": "--fullname=", + "account desc": "--account-desc=", + "home directory": "--homedir=", + "homedir drive": "--drive=", + "profile path": "--profile=", + "logon script": "--script=", + "account flags": "--account-control=", + "user sid": "-U ", + "machine sid": "-M ", } # field mapping provided = { - 'domain': domain, - 'full name': fullname, - 'account desc': account_desc, - 'home directory': homedir, - 'homedir drive': drive, - 'profile path': profile, - 'logon script': script, - 'account flags': account_control, - 'user sid': user_sid, - 'machine sid': machine_sid, + "domain": domain, + "full name": fullname, + "account desc": account_desc, + "home directory": homedir, + "homedir drive": drive, + "profile path": profile, + "logon script": script, + "account flags": account_control, + "user sid": user_sid, + "machine sid": machine_sid, } # update password if password: ret = create(login, password, password_hashed)[login] - if ret not in ['updated', 'created', 'unchanged']: + if ret not in ["updated", "created", "unchanged"]: return {login: ret} elif login not in list_users(False): - return {login: 'absent'} + return {login: "absent"} # check for changes current = get_user(login, hashes=True) changes = {} for key, val in provided.items(): - if key in ['user sid', 'machine sid']: - if val is not None and key in current and not current[key].endswith(six.text_type(val)): + if key in ["user sid", "machine sid"]: + if ( + val is not None + and key in current + and not current[key].endswith(six.text_type(val)) + ): changes[key] = six.text_type(val) - elif key in ['account flags']: + elif key in ["account flags"]: if val is not None: - if val.startswith('['): + if val.startswith("["): val = val[1:-1] new = [] for f in val.upper(): - if f not in ['N', 'D', 'H', 'L', 'X']: + if f not in ["N", "D", "H", "L", "X"]: log.warning( - 'pdbedit.modify - unknown {f} flag for account_control, ignored'.format(f=f) + "pdbedit.modify - unknown {f} flag for account_control, ignored".format( + f=f + ) ) else: new.append(f) @@ -370,28 +387,29 @@ def modify( if len(changes) > 0 or reset_login_hours or reset_bad_password_count: cmds = [] for change in changes: - cmds.append('{flag}{value}'.format( - flag=flags[change], - value=_quote_args(changes[change]), - )) + cmds.append( + "{flag}{value}".format( + flag=flags[change], value=_quote_args(changes[change]), + ) + ) if reset_login_hours: - cmds.append('--logon-hours-reset') + cmds.append("--logon-hours-reset") if reset_bad_password_count: - cmds.append('--bad-password-count-reset') + cmds.append("--bad-password-count-reset") - res = __salt__['cmd.run_all']( - 'pdbedit --modify --user {login} {changes}'.format( - login=_quote_args(login), - changes=" ".join(cmds), + res = __salt__["cmd.run_all"]( + "pdbedit --modify --user {login} {changes}".format( + login=_quote_args(login), changes=" ".join(cmds), ), ) - if res['retcode'] > 0: - return {login: res['stderr'] if 'stderr' in res else res['stdout']} + if res["retcode"] > 0: + return {login: res["stderr"] if "stderr" in res else res["stdout"]} - if ret != 'created': - ret = 'updated' + if ret != "created": + ret = "updated" return {login: ret} + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/pecl.py b/salt/modules/pecl.py index 73b8346f7c1..546dfed7fb0 100644 --- a/salt/modules/pecl.py +++ b/salt/modules/pecl.py @@ -1,17 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Manage PHP pecl extensions. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import re -import logging - -try: - from shlex import quote as _cmd_quote # pylint: disable=E0611 -except ImportError: - from pipes import quote as _cmd_quote # Import salt libs import salt.utils.data @@ -20,42 +16,48 @@ import salt.utils.path # Import 3rd-party libs from salt.ext import six -__func_alias__ = { - 'list_': 'list' -} +try: + from shlex import quote as _cmd_quote # pylint: disable=E0611 +except ImportError: + from pipes import quote as _cmd_quote + + +__func_alias__ = {"list_": "list"} log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pecl' +__virtualname__ = "pecl" def __virtual__(): - if salt.utils.path.which('pecl'): + if salt.utils.path.which("pecl"): return __virtualname__ - return (False, 'The pecl execution module not loaded: ' - 'pecl binary is not in the path.') + return ( + False, + "The pecl execution module not loaded: pecl binary is not in the path.", + ) def _pecl(command, defaults=False): - ''' + """ Execute the command passed with pecl - ''' - cmdline = 'pecl {0}'.format(command) + """ + cmdline = "pecl {0}".format(command) if salt.utils.data.is_true(defaults): - cmdline = 'yes ' "''" + ' | ' + cmdline + cmdline = "yes " "''" + " | " + cmdline - ret = __salt__['cmd.run_all'](cmdline, python_shell=True) + ret = __salt__["cmd.run_all"](cmdline, python_shell=True) - if ret['retcode'] == 0: - return ret['stdout'] + if ret["retcode"] == 0: + return ret["stdout"] else: - log.error('Problem running pecl. Is php-pear installed?') - return '' + log.error("Problem running pecl. Is php-pear installed?") + return "" -def install(pecls, defaults=False, force=False, preferred_state='stable'): - ''' +def install(pecls, defaults=False, force=False, preferred_state="stable"): + """ .. versionadded:: 0.17.0 Installs one or several pecl extensions. @@ -76,29 +78,32 @@ def install(pecls, defaults=False, force=False, preferred_state='stable'): .. code-block:: bash salt '*' pecl.install fuse - ''' + """ if isinstance(pecls, six.string_types): pecls = [pecls] - preferred_state = '-d preferred_state={0}'.format(_cmd_quote(preferred_state)) + preferred_state = "-d preferred_state={0}".format(_cmd_quote(preferred_state)) if force: - return _pecl('{0} install -f {1}'.format(preferred_state, _cmd_quote(' '.join(pecls))), - defaults=defaults) + return _pecl( + "{0} install -f {1}".format(preferred_state, _cmd_quote(" ".join(pecls))), + defaults=defaults, + ) else: - _pecl('{0} install {1}'.format(preferred_state, _cmd_quote(' '.join(pecls))), - defaults=defaults) + _pecl( + "{0} install {1}".format(preferred_state, _cmd_quote(" ".join(pecls))), + defaults=defaults, + ) if not isinstance(pecls, list): pecls = [pecls] for pecl in pecls: found = False - if '/' in pecl: - channel, pecl = pecl.split('/') + if "/" in pecl: + channel, pecl = pecl.split("/") else: channel = None installed_pecls = list_(channel) for pecl in installed_pecls: - installed_pecl_with_version = '{0}-{1}'.format( - pecl, - installed_pecls.get(pecl)[0] + installed_pecl_with_version = "{0}-{1}".format( + pecl, installed_pecls.get(pecl)[0] ) if pecl in installed_pecl_with_version: found = True @@ -108,7 +113,7 @@ def install(pecls, defaults=False, force=False, preferred_state='stable'): def uninstall(pecls): - ''' + """ Uninstall one or several pecl extensions. pecls @@ -119,14 +124,14 @@ def uninstall(pecls): .. code-block:: bash salt '*' pecl.uninstall fuse - ''' + """ if isinstance(pecls, six.string_types): pecls = [pecls] - return _pecl('uninstall {0}'.format(_cmd_quote(' '.join(pecls)))) + return _pecl("uninstall {0}".format(_cmd_quote(" ".join(pecls)))) def update(pecls): - ''' + """ Update one or several pecl extensions. pecls @@ -137,14 +142,14 @@ def update(pecls): .. code-block:: bash salt '*' pecl.update fuse - ''' + """ if isinstance(pecls, six.string_types): pecls = [pecls] - return _pecl('install -U {0}'.format(_cmd_quote(' '.join(pecls)))) + return _pecl("install -U {0}".format(_cmd_quote(" ".join(pecls)))) def list_(channel=None): - ''' + """ List installed pecl extensions. CLI Example: @@ -152,12 +157,12 @@ def list_(channel=None): .. code-block:: bash salt '*' pecl.list - ''' - pecl_channel_pat = re.compile('^([^ ]+)[ ]+([^ ]+)[ ]+([^ ]+)') + """ + pecl_channel_pat = re.compile("^([^ ]+)[ ]+([^ ]+)[ ]+([^ ]+)") pecls = {} - command = 'list' + command = "list" if channel: - command = '{0} -c {1}'.format(command, _cmd_quote(channel)) + command = "{0} -c {1}".format(command, _cmd_quote(channel)) lines = _pecl(command).splitlines() lines = (l for l in lines if pecl_channel_pat.match(l)) diff --git a/salt/modules/peeringdb.py b/salt/modules/peeringdb.py index bda2e4871b1..8c654c39a8e 100644 --- a/salt/modules/peeringdb.py +++ b/salt/modules/peeringdb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" PeeringDB Module ================ @@ -24,71 +24,68 @@ Configuration (in the opts or Pillar): peeringdb: username: salt password: 5@1t -''' +""" from __future__ import absolute_import # Import python libs import logging -log = logging.getLogger(__name__) # Import salt modules import salt.utils.http from salt.utils.args import clean_kwargs -__virtualname__ = 'peeringdb' -__proxyenabled__ = ['*'] +log = logging.getLogger(__name__) -PEERINGDB_URL = 'https://www.peeringdb.com/api' + +__virtualname__ = "peeringdb" +__proxyenabled__ = ["*"] + +PEERINGDB_URL = "https://www.peeringdb.com/api" def __virtual__(): return __virtualname__ -def _get_auth(username=None, - password=None): - peeringdb_cfg = __salt__['config.merge']('peeringdb', default={}) +def _get_auth(username=None, password=None): + peeringdb_cfg = __salt__["config.merge"]("peeringdb", default={}) if not username: - username = peeringdb_cfg.get('username', username) + username = peeringdb_cfg.get("username", username) if not password: - password = peeringdb_cfg.get('password', password) + password = peeringdb_cfg.get("password", password) return username, password def _build_url(endpoint, id=None): if id: - return '{base}/{endp}/{id}'.format(base=PEERINGDB_URL, endp=endpoint, id=id) - return '{base}/{endp}'.format(base=PEERINGDB_URL, endp=endpoint) + return "{base}/{endp}/{id}".format(base=PEERINGDB_URL, endp=endpoint, id=id) + return "{base}/{endp}".format(base=PEERINGDB_URL, endp=endpoint) def _get_endpoint(endpoint, id=None, **kwargs): - username, password = _get_auth(kwargs.pop('username', None), - kwargs.pop('password', None)) + username, password = _get_auth( + kwargs.pop("username", None), kwargs.pop("password", None) + ) kwargs = clean_kwargs(**kwargs) url = _build_url(endpoint, id=id) - ret = { - 'comment': '', - 'result': True, - 'out': None - } - res = salt.utils.http.query(url, - method='GET', - decode=True, - username=username, - password=password, - params=kwargs) - if 'error' in res: - ret.update({ - 'result': False, - 'comment': res['error'] - }) + ret = {"comment": "", "result": True, "out": None} + res = salt.utils.http.query( + url, + method="GET", + decode=True, + username=username, + password=password, + params=kwargs, + ) + if "error" in res: + ret.update({"result": False, "comment": res["error"]}) return ret - ret['out'] = res['dict']['data'] + ret["out"] = res["dict"]["data"] return ret def get_net(**kwargs): - ''' + """ Return the details of a network identified using the search filters specified in the query. @@ -107,12 +104,12 @@ def get_net(**kwargs): salt '*' peeringdb.get_net asn=13335 salt '*' peeringdb.get_net city='Salt Lake City' salt '*' peeringdb.get_net name__startswith=GTT - ''' - return _get_endpoint('net', **kwargs) + """ + return _get_endpoint("net", **kwargs) def get_fac(**kwargs): - ''' + """ Return the details of the facility identified using the search filters specified in the query. @@ -129,12 +126,12 @@ def get_fac(**kwargs): salt '*' peeringdb.get_fac id=1774 salt '*' peeringdb.get_fac state=UT - ''' - return _get_endpoint('fac', **kwargs) + """ + return _get_endpoint("fac", **kwargs) def get_ix(**kwargs): - ''' + """ Return the details of an IX (Internet Exchange) using the search filters specified in the query. @@ -151,12 +148,12 @@ def get_ix(**kwargs): salt '*' peeringdb.get_ix id=1 salt '*' peeringdb.get_ix city='Milwaukee' - ''' - return _get_endpoint('ix', **kwargs) + """ + return _get_endpoint("ix", **kwargs) def get_ixfac(**kwargs): - ''' + """ Return the details of an IX (Internet Exchange) facility using the search filters specified in the query. @@ -173,12 +170,12 @@ def get_ixfac(**kwargs): salt '*' peeringdb.get_ixfac id=1 salt '*' peeringdb.get_ixfac city='Milwaukee' - ''' - return _get_endpoint('ixfac', **kwargs) + """ + return _get_endpoint("ixfac", **kwargs) def get_ixlan(**kwargs): - ''' + """ Return the details of an IX (Internet Exchange) together with the networks available in this location (and their details), using the search filters specified in the query. @@ -196,12 +193,12 @@ def get_ixlan(**kwargs): salt '*' peeringdb.get_ixlan id=780 salt '*' peeringdb.get_ixlan city='Milwaukee' - ''' - return _get_endpoint('ixlan', **kwargs) + """ + return _get_endpoint("ixlan", **kwargs) def get_ixpfx(**kwargs): - ''' + """ Return the details of an IX (Internet Exchange) together with the PeeringDB IDs of the networks available in this location, using the search filters specified in the query. @@ -219,12 +216,12 @@ def get_ixpfx(**kwargs): salt '*' peeringdb.get_ixpfx id=780 salt '*' peeringdb.get_ixpfx city='Milwaukee' - ''' - return _get_endpoint('ixpfx', **kwargs) + """ + return _get_endpoint("ixpfx", **kwargs) def get_netfac(**kwargs): - ''' + """ Return the list of facilities used by a particular network, given the ``id`` or other filters specified in the query. @@ -241,12 +238,12 @@ def get_netfac(**kwargs): salt '*' peeringdb.get_netfac id=780 salt '*' peeringdb.get_netfac city='Milwaukee' - ''' - return _get_endpoint('netfac', **kwargs) + """ + return _get_endpoint("netfac", **kwargs) def get_netixlan(**kwargs): - ''' + """ Return the IP addresses used by a particular network at all the IXs where it is available. The network is selected either via the ``id`` argument or the other filters specified in the query. @@ -265,12 +262,12 @@ def get_netixlan(**kwargs): salt '*' peeringdb.get_netixlan asn=13335 salt '*' peeringdb.get_netixlan ipaddr4=185.1.114.25 - ''' - return _get_endpoint('netixlan', **kwargs) + """ + return _get_endpoint("netixlan", **kwargs) def get_org(**kwargs): - ''' + """ Return the details of an organisation together with the networks available in this location, using the search filters specified in the query. @@ -287,12 +284,12 @@ def get_org(**kwargs): salt '*' peeringdb.get_org id=2 salt '*' peeringdb.get_org city=Duesseldorf - ''' - return _get_endpoint('org', **kwargs) + """ + return _get_endpoint("org", **kwargs) def get_poc(**kwargs): - ''' + """ Return the details of a person of contact together using the search filters specified in the query. @@ -309,5 +306,5 @@ def get_poc(**kwargs): salt '*' peeringdb.get_poc id=6721 salt '*' peeringdb.get_poc email__contains='@cloudflare.com' - ''' - return _get_endpoint('poc', **kwargs) + """ + return _get_endpoint("poc", **kwargs) diff --git a/salt/modules/pf.py b/salt/modules/pf.py index 67acaf132a4..e66fe591d97 100644 --- a/salt/modules/pf.py +++ b/salt/modules/pf.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Control the OpenBSD packet filter (PF). :codeauthor: Jasper Lievisse Adriaanse .. versionadded:: 2019.2.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -15,24 +15,27 @@ import re # Import salt libs import salt.utils.path -from salt.exceptions import (CommandExecutionError, SaltInvocationError) +from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only works on OpenBSD for now; other systems with pf (macOS, FreeBSD, etc) need to be tested before enabling them. - ''' - if __grains__['os'] == 'OpenBSD' and salt.utils.path.which('pfctl'): + """ + if __grains__["os"] == "OpenBSD" and salt.utils.path.which("pfctl"): return True - return (False, 'The pf execution module cannot be loaded: either the system is not OpenBSD or the pfctl binary was not found') + return ( + False, + "The pf execution module cannot be loaded: either the system is not OpenBSD or the pfctl binary was not found", + ) def enable(): - ''' + """ Enable the Packet Filter. CLI example: @@ -40,30 +43,30 @@ def enable(): .. code-block:: bash salt '*' pf.enable - ''' + """ ret = {} - result = __salt__['cmd.run_all']('pfctl -e', - output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"]( + "pfctl -e", output_loglevel="trace", python_shell=False + ) - if result['retcode'] == 0: - ret = {'comment': 'pf enabled', 'changes': True} + if result["retcode"] == 0: + ret = {"comment": "pf enabled", "changes": True} else: # If pf was already enabled the return code is also non-zero. # Don't raise an exception in that case. - if result['stderr'] == 'pfctl: pf already enabled': - ret = {'comment': 'pf already enabled', 'changes': False} + if result["stderr"] == "pfctl: pf already enabled": + ret = {"comment": "pf already enabled", "changes": False} else: raise CommandExecutionError( - 'Could not enable pf', - info={'errors': [result['stderr']], 'changes': False} + "Could not enable pf", + info={"errors": [result["stderr"]], "changes": False}, ) return ret def disable(): - ''' + """ Disable the Packet Filter. CLI example: @@ -71,30 +74,30 @@ def disable(): .. code-block:: bash salt '*' pf.disable - ''' + """ ret = {} - result = __salt__['cmd.run_all']('pfctl -d', - output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"]( + "pfctl -d", output_loglevel="trace", python_shell=False + ) - if result['retcode'] == 0: - ret = {'comment': 'pf disabled', 'changes': True} + if result["retcode"] == 0: + ret = {"comment": "pf disabled", "changes": True} else: # If pf was already disabled the return code is also non-zero. # Don't raise an exception in that case. - if result['stderr'] == 'pfctl: pf not enabled': - ret = {'comment': 'pf already disabled', 'changes': False} + if result["stderr"] == "pfctl: pf not enabled": + ret = {"comment": "pf already disabled", "changes": False} else: raise CommandExecutionError( - 'Could not disable pf', - info={'errors': [result['stderr']], 'changes': False} + "Could not disable pf", + info={"errors": [result["stderr"]], "changes": False}, ) return ret def loglevel(level): - ''' + """ Set the debug level which limits the severity of log messages printed by ``pf(4)``. level: @@ -106,30 +109,30 @@ def loglevel(level): .. code-block:: bash salt '*' pf.loglevel emerg - ''' + """ # There's no way to getting the previous loglevel so imply we've # always made a change. - ret = {'changes': True} + ret = {"changes": True} - all_levels = ['emerg', 'alert', 'crit', 'err', 'warning', 'notice', 'info', 'debug'] + all_levels = ["emerg", "alert", "crit", "err", "warning", "notice", "info", "debug"] if level not in all_levels: - raise SaltInvocationError('Unknown loglevel: {0}'.format(level)) + raise SaltInvocationError("Unknown loglevel: {0}".format(level)) - result = __salt__['cmd.run_all']('pfctl -x {0}'.format(level), - output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"]( + "pfctl -x {0}".format(level), output_loglevel="trace", python_shell=False + ) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered setting loglevel', - info={'errors': [result['stderr']], 'changes': False} + "Problem encountered setting loglevel", + info={"errors": [result["stderr"]], "changes": False}, ) return ret -def load(file='/etc/pf.conf', noop=False): - ''' +def load(file="/etc/pf.conf", noop=False): + """ Load a ruleset from the specific file, overwriting the currently loaded ruleset. file: @@ -143,31 +146,29 @@ def load(file='/etc/pf.conf', noop=False): .. code-block:: bash salt '*' pf.load /etc/pf.conf.d/lockdown.conf - ''' + """ # We cannot precisely determine if loading the ruleset implied # any changes so assume it always does. - ret = {'changes': True} - cmd = ['pfctl', '-f', file] + ret = {"changes": True} + cmd = ["pfctl", "-f", file] if noop: - ret['changes'] = False - cmd.append('-n') + ret["changes"] = False + cmd.append("-n") - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem loading the ruleset from {0}'.format(file), - info={'errors': [result['stderr']], 'changes': False} + "Problem loading the ruleset from {0}".format(file), + info={"errors": [result["stderr"]], "changes": False}, ) return ret def flush(modifier): - ''' + """ Flush the specified packet filter parameters. modifier: @@ -189,44 +190,42 @@ def flush(modifier): .. code-block:: bash salt '*' pf.flush states - ''' + """ ret = {} - all_modifiers = ['rules', 'states', 'info', 'osfp', 'all', 'sources', 'tables'] + all_modifiers = ["rules", "states", "info", "osfp", "all", "sources", "tables"] # Accept the following two modifiers to allow for a consistent interface between # pfctl(8) and Salt. - capital_modifiers = ['Sources', 'Tables'] + capital_modifiers = ["Sources", "Tables"] all_modifiers += capital_modifiers if modifier.title() in capital_modifiers: modifier = modifier.title() if modifier not in all_modifiers: - raise SaltInvocationError('Unknown modifier: {0}'.format(modifier)) + raise SaltInvocationError("Unknown modifier: {0}".format(modifier)) - cmd = 'pfctl -v -F {0}'.format(modifier) - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + cmd = "pfctl -v -F {0}".format(modifier) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if result['retcode'] == 0: - if re.match(r'^0.*', result['stderr']): - ret['changes'] = False + if result["retcode"] == 0: + if re.match(r"^0.*", result["stderr"]): + ret["changes"] = False else: - ret['changes'] = True + ret["changes"] = True - ret['comment'] = result['stderr'] + ret["comment"] = result["stderr"] else: raise CommandExecutionError( - 'Could not flush {0}'.format(modifier), - info={'errors': [result['stderr']], 'changes': False} + "Could not flush {0}".format(modifier), + info={"errors": [result["stderr"]], "changes": False}, ) return ret def table(command, table, **kwargs): - ''' + """ Apply a command on the specified table. table: @@ -254,57 +253,67 @@ def table(command, table, **kwargs): salt '*' pf.table expire table=spam_hosts number=300 salt '*' pf.table add table=local_hosts addresses='["127.0.0.1", "::1"]' - ''' + """ ret = {} - all_commands = ['kill', 'flush', 'add', 'delete', 'expire', 'replace', 'show', 'test', 'zero'] + all_commands = [ + "kill", + "flush", + "add", + "delete", + "expire", + "replace", + "show", + "test", + "zero", + ] if command not in all_commands: - raise SaltInvocationError('Unknown table command: {0}'.format(command)) + raise SaltInvocationError("Unknown table command: {0}".format(command)) - cmd = ['pfctl', '-t', table, '-T', command] + cmd = ["pfctl", "-t", table, "-T", command] - if command in ['add', 'delete', 'replace', 'test']: - cmd += kwargs.get('addresses', []) - elif command == 'expire': - number = kwargs.get('number', None) + if command in ["add", "delete", "replace", "test"]: + cmd += kwargs.get("addresses", []) + elif command == "expire": + number = kwargs.get("number", None) if not number: - raise SaltInvocationError('need expire_number argument for expire command') + raise SaltInvocationError("need expire_number argument for expire command") else: cmd.append(number) - result = __salt__['cmd.run_all'](cmd, - output_level='trace', - python_shell=False) + result = __salt__["cmd.run_all"](cmd, output_level="trace", python_shell=False) - if result['retcode'] == 0: - if command == 'show': - ret = {'comment': result['stdout'].split()} - elif command == 'test': - ret = {'comment': result['stderr'], 'matches': True} + if result["retcode"] == 0: + if command == "show": + ret = {"comment": result["stdout"].split()} + elif command == "test": + ret = {"comment": result["stderr"], "matches": True} else: - if re.match(r'^(0.*|no changes)', result['stderr']): - ret['changes'] = False + if re.match(r"^(0.*|no changes)", result["stderr"]): + ret["changes"] = False else: - ret['changes'] = True + ret["changes"] = True - ret['comment'] = result['stderr'] + ret["comment"] = result["stderr"] else: # 'test' returns a non-zero code if the address didn't match, even if # the command itself ran fine; also set 'matches' to False since not # everything matched. - if command == 'test' and re.match(r'^\d+/\d+ addresses match.$', result['stderr']): - ret = {'comment': result['stderr'], 'matches': False} + if command == "test" and re.match( + r"^\d+/\d+ addresses match.$", result["stderr"] + ): + ret = {"comment": result["stderr"], "matches": False} else: raise CommandExecutionError( - 'Could not apply {0} on table {1}'.format(command, table), - info={'errors': [result['stderr']], 'changes': False} + "Could not apply {0} on table {1}".format(command, table), + info={"errors": [result["stderr"]], "changes": False}, ) return ret def show(modifier): - ''' + """ Show filter parameters. modifier: @@ -320,30 +329,28 @@ def show(modifier): .. code-block:: bash salt '*' pf.show rules - ''' + """ # By definition showing the parameters makes no changes. - ret = {'changes': False} + ret = {"changes": False} - capital_modifiers = ['Tables'] - all_modifiers = ['rules', 'states', 'tables'] + capital_modifiers = ["Tables"] + all_modifiers = ["rules", "states", "tables"] all_modifiers += capital_modifiers if modifier.title() in capital_modifiers: modifier = modifier.title() if modifier not in all_modifiers: - raise SaltInvocationError('Unknown modifier: {0}'.format(modifier)) + raise SaltInvocationError("Unknown modifier: {0}".format(modifier)) - cmd = 'pfctl -s {0}'.format(modifier) - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + cmd = "pfctl -s {0}".format(modifier) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if result['retcode'] == 0: - ret['comment'] = result['stdout'].split('\n') + if result["retcode"] == 0: + ret["comment"] = result["stdout"].split("\n") else: raise CommandExecutionError( - 'Could not show {0}'.format(modifier), - info={'errors': [result['stderr']], 'changes': False} + "Could not show {0}".format(modifier), + info={"errors": [result["stderr"]], "changes": False}, ) return ret diff --git a/salt/modules/philips_hue.py b/salt/modules/philips_hue.py index 0f732b72d29..ee8bf2c7c87 100644 --- a/salt/modules/philips_hue.py +++ b/salt/modules/philips_hue.py @@ -14,49 +14,57 @@ # See the License for the specific language governing permissions and # limitations under the License. -''' +""" Philips HUE lamps module for proxy. .. versionadded:: 2015.8.3 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import sys -__virtualname__ = 'hue' -__proxyenabled__ = ['philips_hue'] +__virtualname__ = "hue" +__proxyenabled__ = ["philips_hue"] def _proxy(): - ''' + """ Get proxy. - ''' + """ return __proxy__ def __virtual__(): - ''' + """ Start the Philips HUE only for proxies. - ''' + """ if not _proxy(): return False def _mkf(cmd_name, doc): - ''' + """ Nested function to help move proxy functions into sys.modules - ''' + """ + def _cmd(*args, **kw): - ''' + """ Call commands in proxy - ''' - proxyfn = 'philips_hue.'+cmd_name + """ + proxyfn = "philips_hue." + cmd_name return __proxy__[proxyfn](*args, **kw) + return _cmd import salt.proxy.philips_hue as hue + for method in dir(hue): - if method.startswith('call_'): - setattr(sys.modules[__name__], method[5:], _mkf(method, getattr(hue, method).__doc__)) + if method.startswith("call_"): + setattr( + sys.modules[__name__], + method[5:], + _mkf(method, getattr(hue, method).__doc__), + ) del hue return _proxy() and __virtualname__ or False diff --git a/salt/modules/pillar.py b/salt/modules/pillar.py index 4586108a4c9..30a49350b47 100644 --- a/salt/modules/pillar.py +++ b/salt/modules/pillar.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Extract the pillar data for this minion -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -9,9 +9,8 @@ import collections # Import third party libs import copy -import os import logging -from salt.ext import six +import os # Import salt libs import salt.pillar @@ -23,20 +22,23 @@ import salt.utils.odict import salt.utils.yaml from salt.defaults import DEFAULT_TARGET_DELIM from salt.exceptions import CommandExecutionError +from salt.ext import six -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] log = logging.getLogger(__name__) -def get(key, - default=KeyError, - merge=False, - merge_nested_lists=None, - delimiter=DEFAULT_TARGET_DELIM, - pillarenv=None, - saltenv=None): - ''' +def get( + key, + default=KeyError, + merge=False, + merge_nested_lists=None, + delimiter=DEFAULT_TARGET_DELIM, + pillarenv=None, + saltenv=None, +): + """ .. versionadded:: 0.14 Attempt to retrieve the named value from :ref:`in-memory pillar data @@ -126,71 +128,74 @@ def get(key, salt '*' pillar.get pkg:apache salt '*' pillar.get abc::def|ghi delimiter='|' - ''' - if not __opts__.get('pillar_raise_on_missing'): + """ + if not __opts__.get("pillar_raise_on_missing"): if default is KeyError: - default = '' - opt_merge_lists = __opts__.get('pillar_merge_lists', False) if \ - merge_nested_lists is None else merge_nested_lists - pillar_dict = __pillar__ \ - if all(x is None for x in (saltenv, pillarenv)) \ + default = "" + opt_merge_lists = ( + __opts__.get("pillar_merge_lists", False) + if merge_nested_lists is None + else merge_nested_lists + ) + pillar_dict = ( + __pillar__ + if all(x is None for x in (saltenv, pillarenv)) else items(saltenv=saltenv, pillarenv=pillarenv) + ) if merge: if isinstance(default, dict): ret = salt.utils.data.traverse_dict_and_list( - pillar_dict, - key, - {}, - delimiter) + pillar_dict, key, {}, delimiter + ) if isinstance(ret, collections.Mapping): default = copy.deepcopy(default) return salt.utils.dictupdate.update( - default, - ret, - merge_lists=opt_merge_lists) + default, ret, merge_lists=opt_merge_lists + ) else: log.error( - 'pillar.get: Default (%s) is a dict, but the returned ' - 'pillar value (%s) is of type \'%s\'. Merge will be ' - 'skipped.', default, ret, type(ret).__name__ + "pillar.get: Default (%s) is a dict, but the returned " + "pillar value (%s) is of type '%s'. Merge will be " + "skipped.", + default, + ret, + type(ret).__name__, ) elif isinstance(default, list): ret = salt.utils.data.traverse_dict_and_list( - pillar_dict, - key, - [], - delimiter) + pillar_dict, key, [], delimiter + ) if isinstance(ret, list): default = copy.deepcopy(default) default.extend([x for x in ret if x not in default]) return default else: log.error( - 'pillar.get: Default (%s) is a list, but the returned ' - 'pillar value (%s) is of type \'%s\'. Merge will be ' - 'skipped.', default, ret, type(ret).__name__ + "pillar.get: Default (%s) is a list, but the returned " + "pillar value (%s) is of type '%s'. Merge will be " + "skipped.", + default, + ret, + type(ret).__name__, ) else: log.error( - 'pillar.get: Default (%s) is of type \'%s\', must be a dict ' - 'or list to merge. Merge will be skipped.', - default, type(default).__name__ + "pillar.get: Default (%s) is of type '%s', must be a dict " + "or list to merge. Merge will be skipped.", + default, + type(default).__name__, ) - ret = salt.utils.data.traverse_dict_and_list( - pillar_dict, - key, - default, - delimiter) + ret = salt.utils.data.traverse_dict_and_list(pillar_dict, key, default, delimiter) if ret is KeyError: - raise KeyError('Pillar key not found: {0}'.format(key)) + raise KeyError("Pillar key not found: {0}".format(key)) return ret def items(*args, **kwargs): - ''' + """ Calls the master for a fresh pillar and generates the pillar data on the fly @@ -239,20 +244,20 @@ def items(*args, **kwargs): .. code-block:: bash salt '*' pillar.items - ''' + """ # Preserve backwards compatibility if args: return item(*args) - pillarenv = kwargs.get('pillarenv') + pillarenv = kwargs.get("pillarenv") if pillarenv is None: - if __opts__.get('pillarenv_from_saltenv', False): - pillarenv = kwargs.get('saltenv') or __opts__['saltenv'] + if __opts__.get("pillarenv_from_saltenv", False): + pillarenv = kwargs.get("saltenv") or __opts__["saltenv"] else: - pillarenv = __opts__['pillarenv'] + pillarenv = __opts__["pillarenv"] - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") if pillar_override and pillar_enc: try: @@ -261,45 +266,48 @@ def items(*args, **kwargs): pillar_enc, translate_newlines=True, opts=__opts__, - valid_rend=__opts__['decrypt_pillar_renderers']) + valid_rend=__opts__["decrypt_pillar_renderers"], + ) except Exception as exc: # pylint: disable=broad-except raise CommandExecutionError( - 'Failed to decrypt pillar override: {0}'.format(exc) + "Failed to decrypt pillar override: {0}".format(exc) ) pillar = salt.pillar.get_pillar( __opts__, __grains__, - __opts__['id'], + __opts__["id"], pillar_override=pillar_override, - pillarenv=pillarenv) + pillarenv=pillarenv, + ) return pillar.compile_pillar() # Allow pillar.data to also be used to return pillar data -data = salt.utils.functools.alias_function(items, 'data') +data = salt.utils.functools.alias_function(items, "data") def _obfuscate_inner(var): - ''' + """ Recursive obfuscation of collection types. Leaf or unknown Python types get replaced by the type name Known collection types trigger recursion. In the special case of mapping types, keys are not obfuscated - ''' + """ if isinstance(var, (dict, salt.utils.odict.OrderedDict)): - return var.__class__((key, _obfuscate_inner(val)) - for key, val in six.iteritems(var)) + return var.__class__( + (key, _obfuscate_inner(val)) for key, val in six.iteritems(var) + ) elif isinstance(var, (list, set, tuple)): return type(var)(_obfuscate_inner(v) for v in var) else: - return '<{0}>'.format(var.__class__.__name__) + return "<{0}>".format(var.__class__.__name__) def obfuscate(*args): - ''' + """ .. versionadded:: 2015.8.0 Same as :py:func:`items`, but replace pillar values with a simple type indication. @@ -324,14 +332,14 @@ def obfuscate(*args): salt '*' pillar.obfuscate - ''' + """ return _obfuscate_inner(items(*args)) # naming chosen for consistency with grains.ls, although it breaks the short # identifier rule. def ls(*args): - ''' + """ .. versionadded:: 2015.8.0 Calls the master for a fresh pillar, generates the pillar data on the @@ -342,13 +350,13 @@ def ls(*args): .. code-block:: bash salt '*' pillar.ls - ''' + """ return list(items(*args)) def item(*args, **kwargs): - ''' + """ .. versionadded:: 0.16.2 Return one or more pillar entries from the :ref:`in-memory pillar data @@ -399,24 +407,24 @@ def item(*args, **kwargs): salt '*' pillar.item foo salt '*' pillar.item foo:bar salt '*' pillar.item foo bar baz - ''' + """ ret = {} - default = kwargs.get('default', '') - delimiter = kwargs.get('delimiter', DEFAULT_TARGET_DELIM) - pillarenv = kwargs.get('pillarenv', None) - saltenv = kwargs.get('saltenv', None) + default = kwargs.get("default", "") + delimiter = kwargs.get("delimiter", DEFAULT_TARGET_DELIM) + pillarenv = kwargs.get("pillarenv", None) + saltenv = kwargs.get("saltenv", None) - pillar_dict = __pillar__ \ - if all(x is None for x in (saltenv, pillarenv)) \ + pillar_dict = ( + __pillar__ + if all(x is None for x in (saltenv, pillarenv)) else items(saltenv=saltenv, pillarenv=pillarenv) + ) try: for arg in args: ret[arg] = salt.utils.data.traverse_dict_and_list( - pillar_dict, - arg, - default, - delimiter) + pillar_dict, arg, default, delimiter + ) except KeyError: pass @@ -424,7 +432,7 @@ def item(*args, **kwargs): def raw(key=None): - ''' + """ Return the raw pillar data that is currently loaded into the minion. Contrast with :py:func:`items` which calls the master to fetch the most @@ -440,7 +448,7 @@ def raw(key=None): pillar raw data.:: salt '*' pillar.raw key='roles' - ''' + """ if key: ret = __pillar__.get(key, {}) else: @@ -506,10 +514,11 @@ def ext(external, pillar=None): pillar_obj = salt.pillar.get_pillar( __opts__, __grains__, - __opts__['id'], - __opts__['saltenv'], + __opts__["id"], + __opts__["saltenv"], ext=external, - pillar_override=pillar) + pillar_override=pillar, + ) ret = pillar_obj.compile_pillar() @@ -517,7 +526,7 @@ def ext(external, pillar=None): def keys(key, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ .. versionadded:: 2015.8.0 Attempt to retrieve a list of keys from the named value from the pillar. @@ -533,9 +542,8 @@ def keys(key, delimiter=DEFAULT_TARGET_DELIM): .. code-block:: bash salt '*' pillar.keys web:sites - ''' - ret = salt.utils.data.traverse_dict_and_list( - __pillar__, key, KeyError, delimiter) + """ + ret = salt.utils.data.traverse_dict_and_list(__pillar__, key, KeyError, delimiter) if ret is KeyError: raise KeyError("Pillar key not found: {0}".format(key)) @@ -547,7 +555,7 @@ def keys(key, delimiter=DEFAULT_TARGET_DELIM): def file_exists(path, saltenv=None): - ''' + """ .. versionadded:: 2016.3.0 This is a master-only function. Calling from the minion is not supported. @@ -573,11 +581,12 @@ def file_exists(path, saltenv=None): .. code-block:: bash salt '*' pillar.file_exists foo/bar.sls - ''' - pillar_roots = __opts__.get('pillar_roots') + """ + pillar_roots = __opts__.get("pillar_roots") if not pillar_roots: - raise CommandExecutionError('No pillar_roots found. Are you running ' - 'this on the master?') + raise CommandExecutionError( + "No pillar_roots found. Are you running " "this on the master?" + ) if saltenv: if saltenv in pillar_roots: @@ -588,7 +597,7 @@ def file_exists(path, saltenv=None): for env in pillar_roots: for pillar_dir in pillar_roots[env]: full_path = os.path.join(pillar_dir, path) - if __salt__['file.file_exists'](full_path): + if __salt__["file.file_exists"](full_path): return True return False @@ -598,12 +607,8 @@ def file_exists(path, saltenv=None): fetch = get -def filter_by(lookup_dict, - pillar, - merge=None, - default='default', - base=None): - ''' +def filter_by(lookup_dict, pillar, merge=None, default="default", base=None): + """ .. versionadded:: 2017.7.0 Look up the given pillar in a given dictionary and return the result @@ -649,10 +654,12 @@ def filter_by(lookup_dict, .. code-block:: bash salt '*' pillar.filter_by '{web: Serve it up, db: I query, default: x_x}' role - ''' - return salt.utils.data.filter_by(lookup_dict=lookup_dict, - lookup=pillar, - traverse=__pillar__, - merge=merge, - default=default, - base=base) + """ + return salt.utils.data.filter_by( + lookup_dict=lookup_dict, + lookup=pillar, + traverse=__pillar__, + merge=merge, + default=default, + base=base, + ) diff --git a/salt/modules/pip.py b/salt/modules/pip.py index 0a0773a8f4d..e5ad4e696ed 100644 --- a/salt/modules/pip.py +++ b/salt/modules/pip.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Install Python packages with pip to either the system or a virtualenv Windows Support @@ -76,18 +76,18 @@ of the 2015.5 branch: There is a known incompatibility between Python2 pip>=10.* and Salt <=2018.3.0. The issue is decribed here: https://github.com/saltstack/salt/issues/46163 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging import os -import pkg_resources import re import shutil import sys import tempfile +import pkg_resources # Import Salt libs import salt.utils.data @@ -98,30 +98,27 @@ import salt.utils.platform import salt.utils.stringutils import salt.utils.url import salt.utils.versions -from salt.ext import six from salt.exceptions import CommandExecutionError, CommandNotFoundError -import salt.utils.platform +from salt.ext import six # This needs to be named logger so we don't shadow it in pip.install logger = logging.getLogger(__name__) # pylint: disable=C0103 # Don't shadow built-in's. -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} -VALID_PROTOS = ['http', 'https', 'ftp', 'file'] +VALID_PROTOS = ["http", "https", "ftp", "file"] -rex_pip_chain_read = re.compile(r'-r\s(.*)\n?', re.MULTILINE) +rex_pip_chain_read = re.compile(r"-r\s(.*)\n?", re.MULTILINE) def __virtual__(): - ''' + """ There is no way to verify that pip is installed without inspecting the entire filesystem. If it's not installed in a conventional location, the user is required to provide the location of pip each time it is used. - ''' - return 'pip' + """ + return "pip" def _pip_bin_env(cwd, bin_env): @@ -131,41 +128,43 @@ def _pip_bin_env(cwd, bin_env): """ if salt.utils.platform.is_windows(): - if bin_env is not None and cwd is None and 'pip' in os.path.basename(bin_env): + if bin_env is not None and cwd is None and "pip" in os.path.basename(bin_env): cwd = os.path.dirname(bin_env) return cwd def _clear_context(bin_env=None): - ''' + """ Remove the cached pip version - ''' - contextkey = 'pip.version' + """ + contextkey = "pip.version" if bin_env is not None: - contextkey = '{0}.{1}'.format(contextkey, bin_env) + contextkey = "{0}.{1}".format(contextkey, bin_env) __context__.pop(contextkey, None) def _get_pip_bin(bin_env): - ''' + """ Locate the pip binary, either from `bin_env` as a virtualenv, as the executable itself, or from searching conventional filesystem locations - ''' + """ if not bin_env: - logger.debug('pip: Using pip from currently-running Python') - return [os.path.normpath(sys.executable), '-m', 'pip'] + logger.debug("pip: Using pip from currently-running Python") + return [os.path.normpath(sys.executable), "-m", "pip"] - python_bin = 'python.exe' if salt.utils.platform.is_windows() else 'python' + python_bin = "python.exe" if salt.utils.platform.is_windows() else "python" def _search_paths(*basedirs): ret = [] for path in basedirs: - ret.extend([ - os.path.join(path, python_bin), - os.path.join(path, 'bin', python_bin), - os.path.join(path, 'Scripts', python_bin) - ]) + ret.extend( + [ + os.path.join(path, python_bin), + os.path.join(path, "bin", python_bin), + os.path.join(path, "Scripts", python_bin), + ] + ) return ret # try to get python bin from virtualenv (i.e. bin_env) @@ -173,86 +172,82 @@ def _get_pip_bin(bin_env): for bin_path in _search_paths(bin_env): if os.path.isfile(bin_path): if os.access(bin_path, os.X_OK): - logger.debug('pip: Found python binary: %s', bin_path) - return [os.path.normpath(bin_path), '-m', 'pip'] + logger.debug("pip: Found python binary: %s", bin_path) + return [os.path.normpath(bin_path), "-m", "pip"] else: logger.debug( - 'pip: Found python binary by name but it is not ' - 'executable: %s', bin_path + "pip: Found python binary by name but it is not " + "executable: %s", + bin_path, ) raise CommandNotFoundError( - 'Could not find a pip binary in virtualenv {0}'.format(bin_env) + "Could not find a pip binary in virtualenv {0}".format(bin_env) ) # bin_env is the python or pip binary elif os.access(bin_env, os.X_OK): if os.path.isfile(bin_env): # If the python binary was passed, return it - if 'python' in os.path.basename(bin_env): - return [os.path.normpath(bin_env), '-m', 'pip'] + if "python" in os.path.basename(bin_env): + return [os.path.normpath(bin_env), "-m", "pip"] # We have been passed a pip binary, use the pip binary. return [os.path.normpath(bin_env)] raise CommandExecutionError( - 'Could not find a pip binary within {0}'.format(bin_env) + "Could not find a pip binary within {0}".format(bin_env) ) else: raise CommandNotFoundError( - 'Access denied to {0}, could not find a pip binary'.format(bin_env) + "Access denied to {0}, could not find a pip binary".format(bin_env) ) def _get_cached_requirements(requirements, saltenv): - ''' + """ Get the location of a cached requirements file; caching if necessary. - ''' + """ req_file, senv = salt.utils.url.parse(requirements) if senv: saltenv = senv - if req_file not in __salt__['cp.list_master'](saltenv): + if req_file not in __salt__["cp.list_master"](saltenv): # Requirements file does not exist in the given saltenv. return False - cached_requirements = __salt__['cp.is_cached']( - requirements, saltenv - ) + cached_requirements = __salt__["cp.is_cached"](requirements, saltenv) if not cached_requirements: # It's not cached, let's cache it. - cached_requirements = __salt__['cp.cache_file']( - requirements, saltenv - ) + cached_requirements = __salt__["cp.cache_file"](requirements, saltenv) # Check if the master version has changed. - if __salt__['cp.hash_file'](requirements, saltenv) != \ - __salt__['cp.hash_file'](cached_requirements, saltenv): - cached_requirements = __salt__['cp.cache_file']( - requirements, saltenv - ) + if __salt__["cp.hash_file"](requirements, saltenv) != __salt__["cp.hash_file"]( + cached_requirements, saltenv + ): + cached_requirements = __salt__["cp.cache_file"](requirements, saltenv) return cached_requirements def _get_env_activate(bin_env): - ''' + """ Return the path to the activate binary - ''' + """ if not bin_env: - raise CommandNotFoundError('Could not find a `activate` binary') + raise CommandNotFoundError("Could not find a `activate` binary") if os.path.isdir(bin_env): if salt.utils.platform.is_windows(): - activate_bin = os.path.join(bin_env, 'Scripts', 'activate.bat') + activate_bin = os.path.join(bin_env, "Scripts", "activate.bat") else: - activate_bin = os.path.join(bin_env, 'bin', 'activate') + activate_bin = os.path.join(bin_env, "bin", "activate") if os.path.isfile(activate_bin): return activate_bin - raise CommandNotFoundError('Could not find a `activate` binary') + raise CommandNotFoundError("Could not find a `activate` binary") def _find_req(link): - logger.info('_find_req -- link = %s', link) + logger.info("_find_req -- link = %s", link) with salt.utils.files.fopen(link) as fh_link: child_links = rex_pip_chain_read.findall( @@ -266,10 +261,10 @@ def _find_req(link): def _resolve_requirements_chain(requirements): - ''' + """ Return an array of requirements file paths that can be used to complete the no_chown==False && user != None conundrum - ''' + """ chain = [] @@ -284,29 +279,30 @@ def _resolve_requirements_chain(requirements): def _process_requirements(requirements, cmd, cwd, saltenv, user): - ''' + """ Process the requirements argument - ''' + """ cleanup_requirements = [] if requirements is not None: if isinstance(requirements, six.string_types): - requirements = [r.strip() for r in requirements.split(',')] + requirements = [r.strip() for r in requirements.split(",")] elif not isinstance(requirements, list): - raise TypeError('requirements must be a string or list') + raise TypeError("requirements must be a string or list") treq = None for requirement in requirements: - logger.debug('TREQ IS: %s', treq) - if requirement.startswith('salt://'): - cached_requirements = _get_cached_requirements( - requirement, saltenv - ) + logger.debug("TREQ IS: %s", treq) + if requirement.startswith("salt://"): + cached_requirements = _get_cached_requirements(requirement, saltenv) if not cached_requirements: - ret = {'result': False, - 'comment': 'pip requirements file \'{0}\' not found' - .format(requirement)} + ret = { + "result": False, + "comment": "pip requirements file '{0}' not found".format( + requirement + ), + } return None, ret requirement = cached_requirements @@ -317,22 +313,24 @@ def _process_requirements(requirements, cmd, cwd, saltenv, user): if not treq: treq = tempfile.mkdtemp() - __salt__['file.chown'](treq, user, None) + __salt__["file.chown"](treq, user, None) # In Windows, just being owner of a file isn't enough. You also # need permissions if salt.utils.platform.is_windows(): - __utils__['dacl.set_permissions']( - obj_name=treq, - principal=user, - permissions='read_execute') + __utils__["dacl.set_permissions"]( + obj_name=treq, principal=user, permissions="read_execute" + ) current_directory = None if not current_directory: current_directory = os.path.abspath(os.curdir) - logger.info('_process_requirements from directory, ' - '%s -- requirement: %s', cwd, requirement) + logger.info( + "_process_requirements from directory, " "%s -- requirement: %s", + cwd, + requirement, + ) if cwd is None: r = requirement @@ -342,9 +340,13 @@ def _process_requirements(requirements, cmd, cwd, saltenv, user): cwd = os.path.dirname(requirement_abspath) requirement = os.path.basename(requirement) - logger.debug('\n\tcwd: %s -> %s\n\trequirement: %s -> %s\n', - c, cwd, r, requirement - ) + logger.debug( + "\n\tcwd: %s -> %s\n\trequirement: %s -> %s\n", + c, + cwd, + r, + requirement, + ) os.chdir(cwd) @@ -352,43 +354,42 @@ def _process_requirements(requirements, cmd, cwd, saltenv, user): os.chdir(current_directory) - logger.info('request files: %s', reqs) + logger.info("request files: %s", reqs) for req_file in reqs: if not os.path.isabs(req_file): req_file = os.path.join(cwd, req_file) - logger.debug('TREQ N CWD: %s -- %s -- for %s', treq, cwd, req_file) + logger.debug("TREQ N CWD: %s -- %s -- for %s", treq, cwd, req_file) target_path = os.path.join(treq, os.path.basename(req_file)) - logger.debug('S: %s', req_file) - logger.debug('T: %s', target_path) + logger.debug("S: %s", req_file) + logger.debug("T: %s", target_path) target_base = os.path.dirname(target_path) if not os.path.exists(target_base): os.makedirs(target_base, mode=0o755) - __salt__['file.chown'](target_base, user, None) + __salt__["file.chown"](target_base, user, None) if not os.path.exists(target_path): - logger.debug( - 'Copying %s to %s', req_file, target_path - ) - __salt__['file.copy'](req_file, target_path) + logger.debug("Copying %s to %s", req_file, target_path) + __salt__["file.copy"](req_file, target_path) logger.debug( - 'Changing ownership of requirements file \'%s\' to ' - 'user \'%s\'', target_path, user + "Changing ownership of requirements file '%s' to " "user '%s'", + target_path, + user, ) - __salt__['file.chown'](target_path, user, None) + __salt__["file.chown"](target_path, user, None) req_args = os.path.join(treq, requirement) if treq else requirement - cmd.extend(['--requirement', req_args]) + cmd.extend(["--requirement", req_args]) cleanup_requirements.append(treq) - logger.debug('CLEANUP_REQUIREMENTS: %s', cleanup_requirements) + logger.debug("CLEANUP_REQUIREMENTS: %s", cleanup_requirements) return cleanup_requirements, None @@ -404,57 +405,60 @@ def _format_env_vars(env_vars): ret[key] = val else: raise CommandExecutionError( - 'env_vars {0} is not a dictionary'.format(env_vars)) + "env_vars {0} is not a dictionary".format(env_vars) + ) return ret -def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 - requirements=None, - bin_env=None, - use_wheel=False, - no_use_wheel=False, - log=None, - proxy=None, - timeout=None, - editable=None, - find_links=None, - index_url=None, - extra_index_url=None, - no_index=False, - mirrors=None, - build=None, - target=None, - download=None, - download_cache=None, - source=None, - upgrade=False, - force_reinstall=False, - ignore_installed=False, - exists_action=None, - no_deps=False, - no_install=False, - no_download=False, - global_options=None, - install_options=None, - user=None, - cwd=None, - pre_releases=False, - cert=None, - allow_all_external=False, - allow_external=None, - allow_unverified=None, - process_dependency_links=False, - saltenv='base', - env_vars=None, - use_vt=False, - trusted_host=None, - no_cache_dir=False, - extra_args=None, - cache_dir=None, - no_binary=None, - disable_version_check=False, - **kwargs): - ''' +def install( + pkgs=None, # pylint: disable=R0912,R0913,R0914 + requirements=None, + bin_env=None, + use_wheel=False, + no_use_wheel=False, + log=None, + proxy=None, + timeout=None, + editable=None, + find_links=None, + index_url=None, + extra_index_url=None, + no_index=False, + mirrors=None, + build=None, + target=None, + download=None, + download_cache=None, + source=None, + upgrade=False, + force_reinstall=False, + ignore_installed=False, + exists_action=None, + no_deps=False, + no_install=False, + no_download=False, + global_options=None, + install_options=None, + user=None, + cwd=None, + pre_releases=False, + cert=None, + allow_all_external=False, + allow_external=None, + allow_unverified=None, + process_dependency_links=False, + saltenv="base", + env_vars=None, + use_vt=False, + trusted_host=None, + no_cache_dir=False, + extra_args=None, + cache_dir=None, + no_binary=None, + disable_version_check=False, + **kwargs +): + """ Install packages with pip Install packages individually or from a pip requirements file. Install @@ -666,18 +670,14 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 salt '*' pip.install markdown,django \ editable=git+https://github.com/worldcompany/djangoembed.git#egg=djangoembed upgrade=True no_deps=True - ''' + """ cwd = _pip_bin_env(cwd, bin_env) cmd = _get_pip_bin(bin_env) - cmd.append('install') + cmd.append("install") cleanup_requirements, error = _process_requirements( - requirements=requirements, - cmd=cmd, - cwd=cwd, - saltenv=saltenv, - user=user + requirements=requirements, cmd=cmd, cwd=cwd, saltenv=saltenv, user=user ) if error: @@ -686,267 +686,287 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 cur_version = version(bin_env, cwd) if use_wheel: - min_version = '1.4' - max_version = '9.0.3' - too_low = salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version) - too_high = salt.utils.versions.compare(ver1=cur_version, oper='>', ver2=max_version) + min_version = "1.4" + max_version = "9.0.3" + too_low = salt.utils.versions.compare( + ver1=cur_version, oper="<", ver2=min_version + ) + too_high = salt.utils.versions.compare( + ver1=cur_version, oper=">", ver2=max_version + ) if too_low or too_high: logger.error( - 'The --use-wheel option is only supported in pip between %s and ' - '%s. The version of pip detected is %s. This option ' - 'will be ignored.', min_version, max_version, cur_version + "The --use-wheel option is only supported in pip between %s and " + "%s. The version of pip detected is %s. This option " + "will be ignored.", + min_version, + max_version, + cur_version, ) else: - cmd.append('--use-wheel') + cmd.append("--use-wheel") if no_use_wheel: - min_version = '1.4' - max_version = '9.0.3' - too_low = salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version) - too_high = salt.utils.versions.compare(ver1=cur_version, oper='>', ver2=max_version) + min_version = "1.4" + max_version = "9.0.3" + too_low = salt.utils.versions.compare( + ver1=cur_version, oper="<", ver2=min_version + ) + too_high = salt.utils.versions.compare( + ver1=cur_version, oper=">", ver2=max_version + ) if too_low or too_high: logger.error( - 'The --no-use-wheel option is only supported in pip between %s and ' - '%s. The version of pip detected is %s. This option ' - 'will be ignored.', min_version, max_version, cur_version + "The --no-use-wheel option is only supported in pip between %s and " + "%s. The version of pip detected is %s. This option " + "will be ignored.", + min_version, + max_version, + cur_version, ) else: - cmd.append('--no-use-wheel') + cmd.append("--no-use-wheel") if no_binary: - min_version = '7.0.0' - too_low = salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version) + min_version = "7.0.0" + too_low = salt.utils.versions.compare( + ver1=cur_version, oper="<", ver2=min_version + ) if too_low: logger.error( - 'The --no-binary option is only supported in pip %s and ' - 'newer. The version of pip detected is %s. This option ' - 'will be ignored.', min_version, cur_version + "The --no-binary option is only supported in pip %s and " + "newer. The version of pip detected is %s. This option " + "will be ignored.", + min_version, + cur_version, ) else: if isinstance(no_binary, list): - no_binary = ','.join(no_binary) - cmd.extend(['--no-binary', no_binary]) + no_binary = ",".join(no_binary) + cmd.extend(["--no-binary", no_binary]) if log: if os.path.isdir(log): - raise IOError( - '\'{0}\' is a directory. Use --log path_to_file'.format(log)) + raise IOError("'{0}' is a directory. Use --log path_to_file".format(log)) elif not os.access(log, os.W_OK): - raise IOError('\'{0}\' is not writeable'.format(log)) + raise IOError("'{0}' is not writeable".format(log)) - cmd.extend(['--log', log]) + cmd.extend(["--log", log]) config = __opts__ if proxy: - cmd.extend(['--proxy', proxy]) + cmd.extend(["--proxy", proxy]) # If proxy arg is set to False we won't use the global proxy even if it's set. - elif proxy is not False and config.get('proxy_host') and config.get('proxy_port'): - if config.get('proxy_username') and config.get('proxy_password'): - http_proxy_url = 'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'.format(**config) + elif proxy is not False and config.get("proxy_host") and config.get("proxy_port"): + if config.get("proxy_username") and config.get("proxy_password"): + http_proxy_url = "http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}".format( + **config + ) else: - http_proxy_url = 'http://{proxy_host}:{proxy_port}'.format(**config) - cmd.extend(['--proxy', http_proxy_url]) + http_proxy_url = "http://{proxy_host}:{proxy_port}".format(**config) + cmd.extend(["--proxy", http_proxy_url]) if timeout: try: if isinstance(timeout, float): # Catch floating point input, exception will be caught in # exception class below. - raise ValueError('Timeout cannot be a float') + raise ValueError("Timeout cannot be a float") int(timeout) except ValueError: raise ValueError( - '\'{0}\' is not a valid timeout, must be an integer' - .format(timeout) + "'{0}' is not a valid timeout, must be an integer".format(timeout) ) - cmd.extend(['--timeout', timeout]) + cmd.extend(["--timeout", timeout]) if find_links: if isinstance(find_links, six.string_types): - find_links = [l.strip() for l in find_links.split(',')] + find_links = [l.strip() for l in find_links.split(",")] for link in find_links: - if not (salt.utils.url.validate(link, VALID_PROTOS) or os.path.exists(link)): + if not ( + salt.utils.url.validate(link, VALID_PROTOS) or os.path.exists(link) + ): raise CommandExecutionError( - '\'{0}\' is not a valid URL or path'.format(link) + "'{0}' is not a valid URL or path".format(link) ) - cmd.extend(['--find-links', link]) + cmd.extend(["--find-links", link]) if no_index and (index_url or extra_index_url): raise CommandExecutionError( - '\'no_index\' and (\'index_url\' or \'extra_index_url\') are ' - 'mutually exclusive.' + "'no_index' and ('index_url' or 'extra_index_url') are " + "mutually exclusive." ) if index_url: if not salt.utils.url.validate(index_url, VALID_PROTOS): - raise CommandExecutionError( - '\'{0}\' is not a valid URL'.format(index_url) - ) - cmd.extend(['--index-url', index_url]) + raise CommandExecutionError("'{0}' is not a valid URL".format(index_url)) + cmd.extend(["--index-url", index_url]) if extra_index_url: if not salt.utils.url.validate(extra_index_url, VALID_PROTOS): raise CommandExecutionError( - '\'{0}\' is not a valid URL'.format(extra_index_url) + "'{0}' is not a valid URL".format(extra_index_url) ) - cmd.extend(['--extra-index-url', extra_index_url]) + cmd.extend(["--extra-index-url", extra_index_url]) if no_index: - cmd.append('--no-index') + cmd.append("--no-index") if mirrors: # https://github.com/pypa/pip/pull/2641/files#diff-3ef137fb9ffdd400f117a565cd94c188L216 - if salt.utils.versions.compare(ver1=cur_version, oper='>=', ver2='7.0.0'): + if salt.utils.versions.compare(ver1=cur_version, oper=">=", ver2="7.0.0"): raise CommandExecutionError( - 'pip >= 7.0.0 does not support mirror argument:' - ' use index_url and/or extra_index_url instead' + "pip >= 7.0.0 does not support mirror argument:" + " use index_url and/or extra_index_url instead" ) if isinstance(mirrors, six.string_types): - mirrors = [m.strip() for m in mirrors.split(',')] + mirrors = [m.strip() for m in mirrors.split(",")] - cmd.append('--use-mirrors') + cmd.append("--use-mirrors") for mirror in mirrors: - if not mirror.startswith('http://'): - raise CommandExecutionError( - '\'{0}\' is not a valid URL'.format(mirror) - ) - cmd.extend(['--mirrors', mirror]) + if not mirror.startswith("http://"): + raise CommandExecutionError("'{0}' is not a valid URL".format(mirror)) + cmd.extend(["--mirrors", mirror]) if disable_version_check: - cmd.extend(['--disable-pip-version-check']) + cmd.extend(["--disable-pip-version-check"]) if build: - cmd.extend(['--build', build]) + cmd.extend(["--build", build]) if target: - cmd.extend(['--target', target]) + cmd.extend(["--target", target]) if download: - cmd.extend(['--download', download]) + cmd.extend(["--download", download]) if download_cache or cache_dir: - cmd.extend(['--cache-dir' if salt.utils.versions.compare( - ver1=cur_version, oper='>=', ver2='6.0' - ) else '--download-cache', download_cache or cache_dir]) + cmd.extend( + [ + "--cache-dir" + if salt.utils.versions.compare(ver1=cur_version, oper=">=", ver2="6.0") + else "--download-cache", + download_cache or cache_dir, + ] + ) if source: - cmd.extend(['--source', source]) + cmd.extend(["--source", source]) if upgrade: - cmd.append('--upgrade') + cmd.append("--upgrade") if force_reinstall: - cmd.append('--force-reinstall') + cmd.append("--force-reinstall") if ignore_installed: - cmd.append('--ignore-installed') + cmd.append("--ignore-installed") if exists_action: - if exists_action.lower() not in ('s', 'i', 'w', 'b'): + if exists_action.lower() not in ("s", "i", "w", "b"): raise CommandExecutionError( - 'The exists_action pip option only supports the values ' - 's, i, w, and b. \'{0}\' is not valid.'.format(exists_action) + "The exists_action pip option only supports the values " + "s, i, w, and b. '{0}' is not valid.".format(exists_action) ) - cmd.extend(['--exists-action', exists_action]) + cmd.extend(["--exists-action", exists_action]) if no_deps: - cmd.append('--no-deps') + cmd.append("--no-deps") if no_install: - cmd.append('--no-install') + cmd.append("--no-install") if no_download: - cmd.append('--no-download') + cmd.append("--no-download") if no_cache_dir: - cmd.append('--no-cache-dir') + cmd.append("--no-cache-dir") if pre_releases: # Check the locally installed pip version pip_version = cur_version # From pip v1.4 the --pre flag is available - if salt.utils.versions.compare(ver1=pip_version, oper='>=', ver2='1.4'): - cmd.append('--pre') + if salt.utils.versions.compare(ver1=pip_version, oper=">=", ver2="1.4"): + cmd.append("--pre") if cert: - cmd.extend(['--cert', cert]) + cmd.extend(["--cert", cert]) if global_options: if isinstance(global_options, six.string_types): - global_options = [go.strip() for go in global_options.split(',')] + global_options = [go.strip() for go in global_options.split(",")] for opt in global_options: - cmd.extend(['--global-option', opt]) + cmd.extend(["--global-option", opt]) if install_options: if isinstance(install_options, six.string_types): - install_options = [io.strip() for io in install_options.split(',')] + install_options = [io.strip() for io in install_options.split(",")] for opt in install_options: - cmd.extend(['--install-option', opt]) + cmd.extend(["--install-option", opt]) if pkgs: if not isinstance(pkgs, list): try: - pkgs = [p.strip() for p in pkgs.split(',')] + pkgs = [p.strip() for p in pkgs.split(",")] except AttributeError: - pkgs = [p.strip() for p in six.text_type(pkgs).split(',')] + pkgs = [p.strip() for p in six.text_type(pkgs).split(",")] pkgs = salt.utils.data.stringify(salt.utils.data.decode_list(pkgs)) # It's possible we replaced version-range commas with semicolons so # they would survive the previous line (in the pip.installed state). # Put the commas back in while making sure the names are contained in # quotes, this allows for proper version spec passing salt>=0.17.0 - cmd.extend([p.replace(';', ',') for p in pkgs]) + cmd.extend([p.replace(";", ",") for p in pkgs]) elif not any([requirements, editable]): # Starting with pip 10.0.0, if no packages are specified in the # command, it returns a retcode 1. So instead of running the command, # just return the output without running pip. - return {'retcode': 0, 'stdout': 'No packages to install.'} + return {"retcode": 0, "stdout": "No packages to install."} if editable: - egg_match = re.compile(r'(?:#|#.*?&)egg=([^&]*)') + egg_match = re.compile(r"(?:#|#.*?&)egg=([^&]*)") if isinstance(editable, six.string_types): - editable = [e.strip() for e in editable.split(',')] + editable = [e.strip() for e in editable.split(",")] for entry in editable: # Is the editable local? - if not (entry == '.' or entry.startswith(('file://', '/'))): + if not (entry == "." or entry.startswith(("file://", "/"))): match = egg_match.search(entry) if not match or not match.group(1): # Missing #egg=theEggName raise CommandExecutionError( - 'You must specify an egg for this editable' + "You must specify an egg for this editable" ) - cmd.extend(['--editable', entry]) + cmd.extend(["--editable", entry]) if allow_all_external: - cmd.append('--allow-all-external') + cmd.append("--allow-all-external") if allow_external: if isinstance(allow_external, six.string_types): - allow_external = [p.strip() for p in allow_external.split(',')] + allow_external = [p.strip() for p in allow_external.split(",")] for pkg in allow_external: - cmd.extend(['--allow-external', pkg]) + cmd.extend(["--allow-external", pkg]) if allow_unverified: if isinstance(allow_unverified, six.string_types): - allow_unverified = \ - [p.strip() for p in allow_unverified.split(',')] + allow_unverified = [p.strip() for p in allow_unverified.split(",")] for pkg in allow_unverified: - cmd.extend(['--allow-unverified', pkg]) + cmd.extend(["--allow-unverified", pkg]) if process_dependency_links: - cmd.append('--process-dependency-links') + cmd.append("--process-dependency-links") if trusted_host: - cmd.extend(['--trusted-host', trusted_host]) + cmd.extend(["--trusted-host", trusted_host]) if extra_args: # These are arguments from the latest version of pip that @@ -972,21 +992,20 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 cmd_kwargs.update(kwargs) if env_vars: - cmd_kwargs.setdefault('env', {}).update(_format_env_vars(env_vars)) + cmd_kwargs.setdefault("env", {}).update(_format_env_vars(env_vars)) try: if cwd: - cmd_kwargs['cwd'] = cwd + cmd_kwargs["cwd"] = cwd if bin_env and os.path.isdir(bin_env): - cmd_kwargs.setdefault('env', {})['VIRTUAL_ENV'] = bin_env + cmd_kwargs.setdefault("env", {})["VIRTUAL_ENV"] = bin_env logger.debug( - 'TRY BLOCK: end of pip.install -- cmd: %s, cmd_kwargs: %s', - cmd, cmd_kwargs + "TRY BLOCK: end of pip.install -- cmd: %s, cmd_kwargs: %s", cmd, cmd_kwargs ) - return __salt__['cmd.run_all'](cmd, python_shell=False, **cmd_kwargs) + return __salt__["cmd.run_all"](cmd, python_shell=False, **cmd_kwargs) finally: _clear_context(bin_env) for tempdir in [cr for cr in cleanup_requirements if cr is not None]: @@ -994,17 +1013,19 @@ def install(pkgs=None, # pylint: disable=R0912,R0913,R0914 shutil.rmtree(tempdir) -def uninstall(pkgs=None, - requirements=None, - bin_env=None, - log=None, - proxy=None, - timeout=None, - user=None, - cwd=None, - saltenv='base', - use_vt=False): - ''' +def uninstall( + pkgs=None, + requirements=None, + bin_env=None, + log=None, + proxy=None, + timeout=None, + user=None, + cwd=None, + saltenv="base", + use_vt=False, +): + """ Uninstall packages individually or from a pip requirements file pkgs @@ -1053,15 +1074,14 @@ def uninstall(pkgs=None, salt '*' pip.uninstall requirements=/path/to/requirements.txt salt '*' pip.uninstall bin_env=/path/to/virtualenv salt '*' pip.uninstall bin_env=/path/to/pip_bin - ''' + """ cwd = _pip_bin_env(cwd, bin_env) cmd = _get_pip_bin(bin_env) - cmd.extend(['uninstall', '-y']) + cmd.extend(["uninstall", "-y"]) cleanup_requirements, error = _process_requirements( - requirements=requirements, cmd=cmd, saltenv=saltenv, user=user, - cwd=cwd + requirements=requirements, cmd=cmd, saltenv=saltenv, user=user, cwd=cwd ) if error: @@ -1072,58 +1092,60 @@ def uninstall(pkgs=None, # TODO make this check if writeable os.path.exists(log) except IOError: - raise IOError('\'{0}\' is not writeable'.format(log)) + raise IOError("'{0}' is not writeable".format(log)) - cmd.extend(['--log', log]) + cmd.extend(["--log", log]) config = __opts__ if proxy: - cmd.extend(['--proxy', proxy]) + cmd.extend(["--proxy", proxy]) # If proxy arg is set to False we won't use the global proxy even if it's set. - elif proxy is not False and config.get('proxy_host') and config.get('proxy_port'): - if config.get('proxy_username') and config.get('proxy_password'): - http_proxy_url = 'http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}'.format(**config) + elif proxy is not False and config.get("proxy_host") and config.get("proxy_port"): + if config.get("proxy_username") and config.get("proxy_password"): + http_proxy_url = "http://{proxy_username}:{proxy_password}@{proxy_host}:{proxy_port}".format( + **config + ) else: - http_proxy_url = 'http://{proxy_host}:{proxy_port}'.format(**config) - cmd.extend(['--proxy', http_proxy_url]) + http_proxy_url = "http://{proxy_host}:{proxy_port}".format(**config) + cmd.extend(["--proxy", http_proxy_url]) if timeout: try: if isinstance(timeout, float): # Catch floating point input, exception will be caught in # exception class below. - raise ValueError('Timeout cannot be a float') + raise ValueError("Timeout cannot be a float") int(timeout) except ValueError: raise ValueError( - '\'{0}\' is not a valid timeout, must be an integer' - .format(timeout) + "'{0}' is not a valid timeout, must be an integer".format(timeout) ) - cmd.extend(['--timeout', timeout]) + cmd.extend(["--timeout", timeout]) if pkgs: if isinstance(pkgs, six.string_types): - pkgs = [p.strip() for p in pkgs.split(',')] + pkgs = [p.strip() for p in pkgs.split(",")] if requirements: for requirement in requirements: with salt.utils.files.fopen(requirement) as rq_: for req in rq_: req = salt.utils.stringutils.to_unicode(req) try: - req_pkg, _ = req.split('==') + req_pkg, _ = req.split("==") if req_pkg in pkgs: pkgs.remove(req_pkg) except ValueError: pass cmd.extend(pkgs) - cmd_kwargs = dict(python_shell=False, runas=user, - cwd=cwd, saltenv=saltenv, use_vt=use_vt) + cmd_kwargs = dict( + python_shell=False, runas=user, cwd=cwd, saltenv=saltenv, use_vt=use_vt + ) if bin_env and os.path.isdir(bin_env): - cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env} + cmd_kwargs["env"] = {"VIRTUAL_ENV": bin_env} try: - return __salt__['cmd.run_all'](cmd, **cmd_kwargs) + return __salt__["cmd.run_all"](cmd, **cmd_kwargs) finally: _clear_context(bin_env) for requirement in cleanup_requirements: @@ -1134,13 +1156,8 @@ def uninstall(pkgs=None, pass -def freeze(bin_env=None, - user=None, - cwd=None, - use_vt=False, - env_vars=None, - **kwargs): - ''' +def freeze(bin_env=None, user=None, cwd=None, use_vt=False, env_vars=None, **kwargs): + """ Return a list of installed packages either globally or in the specified virtualenv @@ -1166,46 +1183,43 @@ def freeze(bin_env=None, .. code-block:: bash salt '*' pip.freeze bin_env=/home/code/path/to/virtualenv - ''' + """ cwd = _pip_bin_env(cwd, bin_env) cmd = _get_pip_bin(bin_env) - cmd.append('freeze') + cmd.append("freeze") # Include pip, setuptools, distribute, wheel - min_version = '8.0.3' + min_version = "8.0.3" cur_version = version(bin_env, cwd) - if salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version): + if salt.utils.versions.compare(ver1=cur_version, oper="<", ver2=min_version): logger.warning( - 'The version of pip installed is %s, which is older than %s. ' - 'The packages pip, wheel, setuptools, and distribute will not be ' - 'included in the output of pip.freeze', cur_version, min_version + "The version of pip installed is %s, which is older than %s. " + "The packages pip, wheel, setuptools, and distribute will not be " + "included in the output of pip.freeze", + cur_version, + min_version, ) else: - cmd.append('--all') + cmd.append("--all") cmd_kwargs = dict(runas=user, cwd=cwd, use_vt=use_vt, python_shell=False) if kwargs: cmd_kwargs.update(**kwargs) if bin_env and os.path.isdir(bin_env): - cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env} + cmd_kwargs["env"] = {"VIRTUAL_ENV": bin_env} if env_vars: - cmd_kwargs.setdefault('env', {}).update(_format_env_vars(env_vars)) - result = __salt__['cmd.run_all'](cmd, **cmd_kwargs) + cmd_kwargs.setdefault("env", {}).update(_format_env_vars(env_vars)) + result = __salt__["cmd.run_all"](cmd, **cmd_kwargs) - if result['retcode']: - raise CommandExecutionError(result['stderr'], info=result) + if result["retcode"]: + raise CommandExecutionError(result["stderr"], info=result) - return result['stdout'].splitlines() + return result["stdout"].splitlines() -def list_(prefix=None, - bin_env=None, - user=None, - cwd=None, - env_vars=None, - **kwargs): - ''' +def list_(prefix=None, bin_env=None, user=None, cwd=None, env_vars=None, **kwargs): + """ Filter list of installed apps from ``freeze`` and check to see if ``prefix`` exists in the list of packages installed. @@ -1222,45 +1236,43 @@ def list_(prefix=None, .. code-block:: bash salt '*' pip.list salt - ''' + """ cwd = _pip_bin_env(cwd, bin_env) packages = {} - if prefix is None or 'pip'.startswith(prefix): - packages['pip'] = version(bin_env, cwd) + if prefix is None or "pip".startswith(prefix): + packages["pip"] = version(bin_env, cwd) - for line in freeze(bin_env=bin_env, - user=user, - cwd=cwd, - env_vars=env_vars, - **kwargs): - if line.startswith('-f') or line.startswith('#'): + for line in freeze( + bin_env=bin_env, user=user, cwd=cwd, env_vars=env_vars, **kwargs + ): + if line.startswith("-f") or line.startswith("#"): # ignore -f line as it contains --find-links directory # ignore comment lines continue - elif line.startswith('-e hg+not trust'): + elif line.startswith("-e hg+not trust"): # ignore hg + not trust problem continue - elif line.startswith('-e'): - line = line.split('-e ')[1] - if '#egg=' in line: - version_, name = line.split('#egg=') + elif line.startswith("-e"): + line = line.split("-e ")[1] + if "#egg=" in line: + version_, name = line.split("#egg=") else: - if len(line.split('===')) >= 2: - name = line.split('===')[0] - version_ = line.split('===')[1] - elif len(line.split('==')) >= 2: - name = line.split('==')[0] - version_ = line.split('==')[1] - elif len(line.split('===')) >= 2: - name = line.split('===')[0] - version_ = line.split('===')[1] - elif len(line.split('==')) >= 2: - name = line.split('==')[0] - version_ = line.split('==')[1] + if len(line.split("===")) >= 2: + name = line.split("===")[0] + version_ = line.split("===")[1] + elif len(line.split("==")) >= 2: + name = line.split("==")[0] + version_ = line.split("==")[1] + elif len(line.split("===")) >= 2: + name = line.split("===")[0] + version_ = line.split("===")[1] + elif len(line.split("==")) >= 2: + name = line.split("==")[0] + version_ = line.split("==")[1] else: - logger.error('Can\'t parse line \'%s\'', line) + logger.error("Can't parse line '%s'", line) continue if prefix: @@ -1273,7 +1285,7 @@ def list_(prefix=None, def version(bin_env=None, cwd=None): - ''' + """ .. versionadded:: 0.17.0 Returns the version of pip. Use ``bin_env`` to specify the path to a @@ -1286,25 +1298,25 @@ def version(bin_env=None, cwd=None): .. code-block:: bash salt '*' pip.version - ''' + """ cwd = _pip_bin_env(cwd, bin_env) - contextkey = 'pip.version' + contextkey = "pip.version" if bin_env is not None: - contextkey = '{0}.{1}'.format(contextkey, bin_env) + contextkey = "{0}.{1}".format(contextkey, bin_env) if contextkey in __context__: return __context__[contextkey] cmd = _get_pip_bin(bin_env)[:] - cmd.append('--version') + cmd.append("--version") - ret = __salt__['cmd.run_all'](cmd, cwd=cwd, python_shell=False) - if ret['retcode']: - raise CommandNotFoundError('Could not find a `pip` binary') + ret = __salt__["cmd.run_all"](cmd, cwd=cwd, python_shell=False) + if ret["retcode"]: + raise CommandNotFoundError("Could not find a `pip` binary") try: - pip_version = re.match(r'^pip (\S+)', ret['stdout']).group(1) + pip_version = re.match(r"^pip (\S+)", ret["stdout"]).group(1) except AttributeError: pip_version = None @@ -1312,10 +1324,8 @@ def version(bin_env=None, cwd=None): return pip_version -def list_upgrades(bin_env=None, - user=None, - cwd=None): - ''' +def list_upgrades(bin_env=None, user=None, cwd=None): + """ Check whether or not an upgrade is available for all packages CLI Example: @@ -1323,41 +1333,39 @@ def list_upgrades(bin_env=None, .. code-block:: bash salt '*' pip.list_upgrades - ''' + """ cwd = _pip_bin_env(cwd, bin_env) cmd = _get_pip_bin(bin_env) - cmd.extend(['list', '--outdated']) + cmd.extend(["list", "--outdated"]) pip_version = version(bin_env, cwd) # Pip started supporting the ability to output json starting with 9.0.0 - min_version = '9.0' - if salt.utils.versions.compare(ver1=pip_version, - oper='>=', - ver2=min_version): - cmd.append('--format=json') + min_version = "9.0" + if salt.utils.versions.compare(ver1=pip_version, oper=">=", ver2=min_version): + cmd.append("--format=json") cmd_kwargs = dict(cwd=cwd, runas=user) if bin_env and os.path.isdir(bin_env): - cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env} + cmd_kwargs["env"] = {"VIRTUAL_ENV": bin_env} - result = __salt__['cmd.run_all'](cmd, **cmd_kwargs) - if result['retcode']: - raise CommandExecutionError(result['stderr'], info=result) + result = __salt__["cmd.run_all"](cmd, **cmd_kwargs) + if result["retcode"]: + raise CommandExecutionError(result["stderr"], info=result) packages = {} # Pip started supporting the ability to output json starting with 9.0.0 # Older versions will have to parse stdout - if salt.utils.versions.compare(ver1=pip_version, oper='<', ver2='9.0.0'): + if salt.utils.versions.compare(ver1=pip_version, oper="<", ver2="9.0.0"): # Pip versions < 8.0.0 had a different output format # Sample data: # pip (Current: 7.1.2 Latest: 10.0.1 [wheel]) # psutil (Current: 5.2.2 Latest: 5.4.5 [wheel]) # pyasn1 (Current: 0.2.3 Latest: 0.4.2 [wheel]) # pycparser (Current: 2.17 Latest: 2.18 [sdist]) - if salt.utils.versions.compare(ver1=pip_version, oper='<', ver2='8.0.0'): - logger.debug('pip module: Old output format') - pat = re.compile(r'(\S*)\s+\(.*Latest:\s+(.*)\)') + if salt.utils.versions.compare(ver1=pip_version, oper="<", ver2="8.0.0"): + logger.debug("pip module: Old output format") + pat = re.compile(r"(\S*)\s+\(.*Latest:\s+(.*)\)") # New output format for version 8.0.0+ # Sample data: @@ -1366,37 +1374,35 @@ def list_upgrades(bin_env=None, # pyasn1 (0.2.3) - Latest: 0.4.2 [wheel] # pycparser (2.17) - Latest: 2.18 [sdist] else: - logger.debug('pip module: New output format') - pat = re.compile(r'(\S*)\s+\(.*\)\s+-\s+Latest:\s+(.*)') + logger.debug("pip module: New output format") + pat = re.compile(r"(\S*)\s+\(.*\)\s+-\s+Latest:\s+(.*)") - for line in result['stdout'].splitlines(): + for line in result["stdout"].splitlines(): match = pat.search(line) if match: name, version_ = match.groups() else: - logger.error('Can\'t parse line \'{0}\''.format(line)) + logger.error("Can't parse line '{0}'".format(line)) continue packages[name] = version_ else: - logger.debug('pip module: JSON output format') + logger.debug("pip module: JSON output format") try: - pkgs = salt.utils.json.loads(result['stdout'], strict=False) + pkgs = salt.utils.json.loads(result["stdout"], strict=False) except ValueError: - raise CommandExecutionError('Invalid JSON', info=result) + raise CommandExecutionError("Invalid JSON", info=result) for pkg in pkgs: - packages[pkg['name']] = '{0} [{1}]'.format(pkg['latest_version'], - pkg['latest_filetype']) + packages[pkg["name"]] = "{0} [{1}]".format( + pkg["latest_version"], pkg["latest_filetype"] + ) return packages -def is_installed(pkgname=None, - bin_env=None, - user=None, - cwd=None): - ''' +def is_installed(pkgname=None, bin_env=None, user=None, cwd=None): + """ .. versionadded:: 2018.3.0 Filter list of installed apps from ``freeze`` and return True or False if @@ -1414,28 +1420,28 @@ def is_installed(pkgname=None, .. code-block:: bash salt '*' pip.is_installed salt - ''' + """ cwd = _pip_bin_env(cwd, bin_env) for line in freeze(bin_env=bin_env, user=user, cwd=cwd): - if line.startswith('-f') or line.startswith('#'): + if line.startswith("-f") or line.startswith("#"): # ignore -f line as it contains --find-links directory # ignore comment lines continue - elif line.startswith('-e hg+not trust'): + elif line.startswith("-e hg+not trust"): # ignore hg + not trust problem continue - elif line.startswith('-e'): - line = line.split('-e ')[1] - version_, name = line.split('#egg=') - elif len(line.split('===')) >= 2: - name = line.split('===')[0] - version_ = line.split('===')[1] - elif len(line.split('==')) >= 2: - name = line.split('==')[0] - version_ = line.split('==')[1] + elif line.startswith("-e"): + line = line.split("-e ")[1] + version_, name = line.split("#egg=") + elif len(line.split("===")) >= 2: + name = line.split("===")[0] + version_ = line.split("===")[1] + elif len(line.split("==")) >= 2: + name = line.split("==")[0] + version_ = line.split("==")[1] else: - logger.error('Can\'t parse line \'%s\'', line) + logger.error("Can't parse line '%s'", line) continue if pkgname: @@ -1445,11 +1451,8 @@ def is_installed(pkgname=None, return False -def upgrade_available(pkg, - bin_env=None, - user=None, - cwd=None): - ''' +def upgrade_available(pkg, bin_env=None, user=None, cwd=None): + """ .. versionadded:: 2015.5.0 Check whether or not an upgrade is available for a given package @@ -1459,17 +1462,14 @@ def upgrade_available(pkg, .. code-block:: bash salt '*' pip.upgrade_available - ''' + """ cwd = _pip_bin_env(cwd, bin_env) return pkg in list_upgrades(bin_env=bin_env, user=user, cwd=cwd) -def upgrade(bin_env=None, - user=None, - cwd=None, - use_vt=False): - ''' +def upgrade(bin_env=None, user=None, cwd=None, use_vt=False): + """ .. versionadded:: 2015.5.0 Upgrades outdated pip packages. @@ -1489,52 +1489,55 @@ def upgrade(bin_env=None, .. code-block:: bash salt '*' pip.upgrade - ''' + """ cwd = _pip_bin_env(cwd, bin_env) - ret = {'changes': {}, - 'result': True, - 'comment': '', - } + ret = { + "changes": {}, + "result": True, + "comment": "", + } cmd = _get_pip_bin(bin_env) - cmd.extend(['install', '-U']) + cmd.extend(["install", "-U"]) old = list_(bin_env=bin_env, user=user, cwd=cwd) cmd_kwargs = dict(cwd=cwd, use_vt=use_vt) if bin_env and os.path.isdir(bin_env): - cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env} + cmd_kwargs["env"] = {"VIRTUAL_ENV": bin_env} errors = False for pkg in list_upgrades(bin_env=bin_env, user=user, cwd=cwd): - if pkg == 'salt': + if pkg == "salt": if salt.utils.platform.is_windows(): continue - result = __salt__['cmd.run_all'](cmd + [pkg], **cmd_kwargs) - if result['retcode'] != 0: + result = __salt__["cmd.run_all"](cmd + [pkg], **cmd_kwargs) + if result["retcode"] != 0: errors = True - if 'stderr' in result: - ret['comment'] += result['stderr'] + if "stderr" in result: + ret["comment"] += result["stderr"] if errors: - ret['result'] = False + ret["result"] = False _clear_context(bin_env) new = list_(bin_env=bin_env, user=user, cwd=cwd) - ret['changes'] = salt.utils.data.compare_dicts(old, new) + ret["changes"] = salt.utils.data.compare_dicts(old, new) return ret -def list_all_versions(pkg, - bin_env=None, - include_alpha=False, - include_beta=False, - include_rc=False, - user=None, - cwd=None, - index_url=None, - extra_index_url=None): - ''' +def list_all_versions( + pkg, + bin_env=None, + include_alpha=False, + include_beta=False, + include_rc=False, + user=None, + cwd=None, + index_url=None, + extra_index_url=None, +): + """ .. versionadded:: 2017.7.3 List all available versions of a pip package @@ -1576,48 +1579,52 @@ def list_all_versions(pkg, .. code-block:: bash salt '*' pip.list_all_versions - ''' + """ cwd = _pip_bin_env(cwd, bin_env) cmd = _get_pip_bin(bin_env) - cmd.extend(['install', '{0}==versions'.format(pkg)]) + cmd.extend(["install", "{0}==versions".format(pkg)]) if index_url: if not salt.utils.url.validate(index_url, VALID_PROTOS): - raise CommandExecutionError( - '\'{0}\' is not a valid URL'.format(index_url) - ) - cmd.extend(['--index-url', index_url]) + raise CommandExecutionError("'{0}' is not a valid URL".format(index_url)) + cmd.extend(["--index-url", index_url]) if extra_index_url: if not salt.utils.url.validate(extra_index_url, VALID_PROTOS): raise CommandExecutionError( - '\'{0}\' is not a valid URL'.format(extra_index_url) + "'{0}' is not a valid URL".format(extra_index_url) ) - cmd.extend(['--extra-index-url', extra_index_url]) + cmd.extend(["--extra-index-url", extra_index_url]) - cmd_kwargs = dict(cwd=cwd, runas=user, output_loglevel='quiet', redirect_stderr=True) + cmd_kwargs = dict( + cwd=cwd, runas=user, output_loglevel="quiet", redirect_stderr=True + ) if bin_env and os.path.isdir(bin_env): - cmd_kwargs['env'] = {'VIRTUAL_ENV': bin_env} + cmd_kwargs["env"] = {"VIRTUAL_ENV": bin_env} - result = __salt__['cmd.run_all'](cmd, **cmd_kwargs) + result = __salt__["cmd.run_all"](cmd, **cmd_kwargs) filtered = [] if not include_alpha: - filtered.append('a') + filtered.append("a") if not include_beta: - filtered.append('b') + filtered.append("b") if not include_rc: - filtered.append('rc') + filtered.append("rc") if filtered: - excludes = re.compile(r'^((?!{0}).)*$'.format('|'.join(filtered))) + excludes = re.compile(r"^((?!{0}).)*$".format("|".join(filtered))) else: - excludes = re.compile(r'') + excludes = re.compile(r"") versions = [] - for line in result['stdout'].splitlines(): - match = re.search(r'\s*Could not find a version.* \(from versions: (.*)\)', line) + for line in result["stdout"].splitlines(): + match = re.search( + r"\s*Could not find a version.* \(from versions: (.*)\)", line + ) if match: - versions = [v for v in match.group(1).split(', ') if v and excludes.match(v)] + versions = [ + v for v in match.group(1).split(", ") if v and excludes.match(v) + ] versions.sort(key=pkg_resources.parse_version) break if not versions: diff --git a/salt/modules/pkg_resource.py b/salt/modules/pkg_resource.py index 8fa3a074fae..57382d543b2 100644 --- a/salt/modules/pkg_resource.py +++ b/salt/modules/pkg_resource.py @@ -1,48 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" Resources needed by pkg providers -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import fnmatch import logging import os import pprint -# Import third party libs -from salt.ext import six - # Import salt libs import salt.utils.data import salt.utils.versions import salt.utils.yaml from salt.exceptions import SaltInvocationError +# Import third party libs +from salt.ext import six + log = logging.getLogger(__name__) -__SUFFIX_NOT_NEEDED = ('x86_64', 'noarch') +__SUFFIX_NOT_NEEDED = ("x86_64", "noarch") def _repack_pkgs(pkgs, normalize=True): - ''' + """ Repack packages specified using "pkgs" argument to pkg states into a single dictionary - ''' - if normalize and 'pkg.normalize_name' in __salt__: - _normalize_name = __salt__['pkg.normalize_name'] + """ + if normalize and "pkg.normalize_name" in __salt__: + _normalize_name = __salt__["pkg.normalize_name"] else: _normalize_name = lambda pkgname: pkgname return dict( [ - (_normalize_name(six.text_type(x)), six.text_type(y) if y is not None else y) + ( + _normalize_name(six.text_type(x)), + six.text_type(y) if y is not None else y, + ) for x, y in six.iteritems(salt.utils.data.repack_dictlist(pkgs)) ] ) def pack_sources(sources, normalize=True): - ''' + """ Accepts list of dicts (or a string representing a list of dicts) and packs the key/value pairs into a single dict. @@ -64,9 +68,9 @@ def pack_sources(sources, normalize=True): .. code-block:: bash salt '*' pkg_resource.pack_sources '[{"foo": "salt://foo.rpm"}, {"bar": "salt://bar.rpm"}]' - ''' - if normalize and 'pkg.normalize_name' in __salt__: - _normalize_name = __salt__['pkg.normalize_name'] + """ + if normalize and "pkg.normalize_name" in __salt__: + _normalize_name = __salt__["pkg.normalize_name"] else: _normalize_name = lambda pkgname: pkgname @@ -79,8 +83,8 @@ def pack_sources(sources, normalize=True): ret = {} for source in sources: if (not isinstance(source, dict)) or len(source) != 1: - log.error('Invalid input: %s', pprint.pformat(sources)) - log.error('Input must be a list of 1-element dicts') + log.error("Invalid input: %s", pprint.pformat(sources)) + log.error("Input must be a list of 1-element dicts") return {} else: key = next(iter(source)) @@ -88,13 +92,10 @@ def pack_sources(sources, normalize=True): return ret -def parse_targets(name=None, - pkgs=None, - sources=None, - saltenv='base', - normalize=True, - **kwargs): - ''' +def parse_targets( + name=None, pkgs=None, sources=None, saltenv="base", normalize=True, **kwargs +): + """ Parses the input to pkg.install and returns back the package(s) to be installed. Returns a list of packages, as well as a string noting whether the packages are to come from a repository or a binary package. @@ -104,81 +105,84 @@ def parse_targets(name=None, .. code-block:: bash salt '*' pkg_resource.parse_targets - ''' - if '__env__' in kwargs: + """ + if "__env__" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('__env__') + kwargs.pop("__env__") - if __grains__['os'] == 'MacOS' and sources: + if __grains__["os"] == "MacOS" and sources: log.warning('Parameter "sources" ignored on MacOS hosts.') - version = kwargs.get('version') + version = kwargs.get("version") if pkgs and sources: log.error('Only one of "pkgs" and "sources" can be used.') return None, None - elif 'advisory_ids' in kwargs: + elif "advisory_ids" in kwargs: if pkgs: log.error('Cannot use "advisory_ids" and "pkgs" at the same time') return None, None - elif kwargs['advisory_ids']: - return kwargs['advisory_ids'], 'advisory' + elif kwargs["advisory_ids"]: + return kwargs["advisory_ids"], "advisory" else: - return [name], 'advisory' + return [name], "advisory" elif pkgs: if version is not None: - log.warning('\'version\' argument will be ignored for multiple ' - 'package targets') + log.warning( + "'version' argument will be ignored for multiple " "package targets" + ) pkgs = _repack_pkgs(pkgs, normalize=normalize) if not pkgs: return None, None else: - return pkgs, 'repository' + return pkgs, "repository" - elif sources and __grains__['os'] != 'MacOS': + elif sources and __grains__["os"] != "MacOS": if version is not None: - log.warning('\'version\' argument will be ignored for multiple ' - 'package targets') + log.warning( + "'version' argument will be ignored for multiple " "package targets" + ) sources = pack_sources(sources, normalize=normalize) if not sources: return None, None srcinfo = [] for pkg_name, pkg_src in six.iteritems(sources): - if __salt__['config.valid_fileproto'](pkg_src): + if __salt__["config.valid_fileproto"](pkg_src): # Cache package from remote source (salt master, HTTP, FTP) and # append the cached path. - srcinfo.append(__salt__['cp.cache_file'](pkg_src, saltenv)) + srcinfo.append(__salt__["cp.cache_file"](pkg_src, saltenv)) else: # Package file local to the minion, just append the path to the # package file. if not os.path.isabs(pkg_src): raise SaltInvocationError( - 'Path {0} for package {1} is either not absolute or ' - 'an invalid protocol'.format(pkg_src, pkg_name) + "Path {0} for package {1} is either not absolute or " + "an invalid protocol".format(pkg_src, pkg_name) ) srcinfo.append(pkg_src) - return srcinfo, 'file' + return srcinfo, "file" elif name: if normalize: - _normalize_name = \ - __salt__.get('pkg.normalize_name', lambda pkgname: pkgname) - packed = dict([(_normalize_name(x), version) for x in name.split(',')]) + _normalize_name = __salt__.get( + "pkg.normalize_name", lambda pkgname: pkgname + ) + packed = dict([(_normalize_name(x), version) for x in name.split(",")]) else: - packed = dict([(x, version) for x in name.split(',')]) - return packed, 'repository' + packed = dict([(x, version) for x in name.split(",")]) + return packed, "repository" else: - log.error('No package sources provided') + log.error("No package sources provided") return None, None def version(*names, **kwargs): - ''' + """ Common interface for obtaining the version of installed packages. CLI Example: @@ -188,34 +192,33 @@ def version(*names, **kwargs): salt '*' pkg_resource.version vim salt '*' pkg_resource.version foo bar baz salt '*' pkg_resource.version 'python*' - ''' + """ ret = {} - versions_as_list = \ - salt.utils.data.is_true(kwargs.pop('versions_as_list', False)) + versions_as_list = salt.utils.data.is_true(kwargs.pop("versions_as_list", False)) pkg_glob = False if len(names) != 0: - pkgs = __salt__['pkg.list_pkgs'](versions_as_list=True, **kwargs) + pkgs = __salt__["pkg.list_pkgs"](versions_as_list=True, **kwargs) for name in names: - if '*' in name: + if "*" in name: pkg_glob = True for match in fnmatch.filter(pkgs, name): ret[match] = pkgs.get(match, []) else: ret[name] = pkgs.get(name, []) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) # Return a string if no globbing is used, and there is one item in the # return dict if len(ret) == 1 and not pkg_glob: try: return next(six.itervalues(ret)) except StopIteration: - return '' + return "" return ret def add_pkg(pkgs, name, pkgver): - ''' + """ Add a package to a dict of installed packages. CLI Example: @@ -223,7 +226,7 @@ def add_pkg(pkgs, name, pkgver): .. code-block:: bash salt '*' pkg_resource.add_pkg '{}' bind 9 - ''' + """ try: pkgs.setdefault(name, []).append(pkgver) except AttributeError as exc: @@ -231,7 +234,7 @@ def add_pkg(pkgs, name, pkgver): def sort_pkglist(pkgs): - ''' + """ Accepts a dict obtained from pkg.list_pkgs() and sorts in place the list of versions for any packages that have multiple versions installed, so that two package lists can be compared to one another. @@ -241,7 +244,7 @@ def sort_pkglist(pkgs): .. code-block:: bash salt '*' pkg_resource.sort_pkglist '["3.45", "2.13"]' - ''' + """ # It doesn't matter that ['4.9','4.10'] would be sorted to ['4.10','4.9'], # so long as the sorting is consistent. try: @@ -254,7 +257,7 @@ def sort_pkglist(pkgs): def stringify(pkgs): - ''' + """ Takes a dict of package name/version information and joins each list of installed versions into a string. @@ -263,16 +266,16 @@ def stringify(pkgs): .. code-block:: bash salt '*' pkg_resource.stringify 'vim: 7.127' - ''' + """ try: for key in pkgs: - pkgs[key] = ','.join(pkgs[key]) + pkgs[key] = ",".join(pkgs[key]) except AttributeError as exc: log.exception(exc) def version_clean(verstr): - ''' + """ Clean the version string removing extra data. This function will simply try to call ``pkg.version_clean``. @@ -281,14 +284,64 @@ def version_clean(verstr): .. code-block:: bash salt '*' pkg_resource.version_clean - ''' - if verstr and 'pkg.version_clean' in __salt__: - return __salt__['pkg.version_clean'](verstr) + """ + if verstr and "pkg.version_clean" in __salt__: + return __salt__["pkg.version_clean"](verstr) return verstr +def version_compare(ver1, oper, ver2, ignore_epoch=False): + """ + .. versionadded:: Sodium + + Perform a version comparison, using (where available) platform-specific + version comparison tools to make the comparison. + + ver1 + The first version to be compared + + oper + One of `==`, `!=`, `>=`, `<=`, `>`, `<` + + ver2 + The second version to be compared + + .. note:: + To avoid shell interpretation, each of the above values should be + quoted when this function is used on the CLI. + + ignore_epoch : False + If ``True``, both package versions will have their epoch prefix + stripped before comparison. + + This function is useful in Jinja templates, to perform specific actions + when a package's version meets certain criteria. For example: + + .. code-block:: jinja + + {%- set postfix_version = salt.pkg.version('postfix') %} + {%- if postfix_version and salt.pkg_resource.version_compare(postfix_version, '>=', '3.3', ignore_epoch=True) %} + {#- do stuff #} + {%- endif %} + + CLI Examples: + + .. code-block:: bash + + salt myminion pkg_resource.version_compare '3.5' '<=' '2.4' + salt myminion pkg_resource.version_compare '3.5' '<=' '2.4' ignore_epoch=True + """ + return salt.utils.versions.compare( + ver1, + oper, + ver2, + ignore_epoch=ignore_epoch, + cmp_func=__salt__.get("version_cmp"), + ) + + def check_extra_requirements(pkgname, pkgver): - ''' + """ Check if the installed package already has the given requirements. This function will return the result of ``pkg.check_extra_requirements`` if this function exists for the minion, otherwise it will return True. @@ -298,32 +351,39 @@ def check_extra_requirements(pkgname, pkgver): .. code-block:: bash salt '*' pkg_resource.check_extra_requirements - ''' - if pkgver and 'pkg.check_extra_requirements' in __salt__: - return __salt__['pkg.check_extra_requirements'](pkgname, pkgver) + """ + if pkgver and "pkg.check_extra_requirements" in __salt__: + return __salt__["pkg.check_extra_requirements"](pkgname, pkgver) return True def format_pkg_list(packages, versions_as_list, attr): - ''' + """ Formats packages according to parameters for list_pkgs. - ''' + """ ret = copy.deepcopy(packages) if attr: ret_attr = {} - requested_attr = {'epoch', 'version', 'release', 'arch', 'install_date', 'install_date_time_t'} + requested_attr = { + "epoch", + "version", + "release", + "arch", + "install_date", + "install_date_time_t", + } - if attr != 'all': - requested_attr &= set(attr + ['version'] + ['arch']) + if attr != "all": + requested_attr &= set(attr + ["version"] + ["arch"]) for name in ret: - if 'pkg.parse_arch' in __salt__: - _parse_arch = __salt__['pkg.parse_arch'](name) + if "pkg.parse_arch" in __salt__: + _parse_arch = __salt__["pkg.parse_arch"](name) else: - _parse_arch = {'name': name, 'arch': None} - _name = _parse_arch['name'] - _arch = _parse_arch['arch'] + _parse_arch = {"name": name, "arch": None} + _name = _parse_arch["name"] + _arch = _parse_arch["arch"] versions = [] pkgname = None @@ -333,24 +393,25 @@ def format_pkg_list(packages, versions_as_list, attr): if key in all_attr: filtered_attr[key] = all_attr[key] versions.append(filtered_attr) - if _name and filtered_attr.get('arch', None) == _arch: + if _name and filtered_attr.get("arch", None) == _arch: pkgname = _name ret_attr.setdefault(pkgname or name, []).extend(versions) return ret_attr for name in ret: - ret[name] = [format_version(d['epoch'], d['version'], d['release']) - for d in ret[name]] + ret[name] = [ + format_version(d["epoch"], d["version"], d["release"]) for d in ret[name] + ] if not versions_as_list: stringify(ret) return ret def format_version(epoch, version, release): - ''' + """ Formats a version string for list_pkgs. - ''' - full_version = '{0}:{1}'.format(epoch, version) if epoch else version + """ + full_version = "{0}:{1}".format(epoch, version) if epoch else version if release: - full_version += '-{0}'.format(release) + full_version += "-{0}".format(release) return full_version diff --git a/salt/modules/pkgin.py b/salt/modules/pkgin.py index 240f79ca266..e894879a4fc 100644 --- a/salt/modules/pkgin.py +++ b/salt/modules/pkgin.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Package support for pkgin based systems, inspired from freebsdpkg module .. important:: @@ -7,10 +7,11 @@ Package support for pkgin based systems, inspired from freebsdpkg module minion, and it is using a different module (or gives an error similar to *'pkg.install' is not available*), see :ref:`here `. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import os @@ -18,37 +19,36 @@ import re # Import salt libs import salt.utils.data +import salt.utils.decorators as decorators import salt.utils.functools import salt.utils.path import salt.utils.pkg -import salt.utils.decorators as decorators from salt.exceptions import CommandExecutionError, MinionError # Import 3rd-party libs from salt.ext import six -VERSION_MATCH = re.compile(r'pkgin(?:[\s]+)([\d.]+)(?:[\s]+)(?:.*)') +VERSION_MATCH = re.compile(r"pkgin(?:[\s]+)([\d.]+)(?:[\s]+)(?:.*)") log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" @decorators.memoize def _check_pkgin(): - ''' + """ Looks to see if pkgin is present on the system, return full path - ''' - ppath = salt.utils.path.which('pkgin') + """ + ppath = salt.utils.path.which("pkgin") if ppath is None: # pkgin was not found in $PATH, try to find it via LOCALBASE try: - localbase = __salt__['cmd.run']( - 'pkg_info -Q LOCALBASE pkgin', - output_loglevel='trace' + localbase = __salt__["cmd.run"]( + "pkg_info -Q LOCALBASE pkgin", output_loglevel="trace" ) if localbase is not None: - ppath = '{0}/bin/pkgin'.format(localbase) + ppath = "{0}/bin/pkgin".format(localbase) if not os.path.exists(ppath): return None except CommandExecutionError: @@ -58,12 +58,12 @@ def _check_pkgin(): @decorators.memoize def _get_version(): - ''' + """ Get the pkgin version - ''' - version_string = __salt__['cmd.run']( - [_check_pkgin(), '-v'], - output_loglevel='trace') + """ + version_string = __salt__["cmd.run"]( + [_check_pkgin(), "-v"], output_loglevel="trace" + ) if version_string is None: # Dunno why it would, but... return False @@ -72,48 +72,51 @@ def _get_version(): if not version_match: return False - return version_match.group(1).split('.') + return version_match.group(1).split(".") @decorators.memoize def _supports_regex(): - ''' + """ Check support of regexp - ''' + """ return tuple([int(i) for i in _get_version()]) > (0, 5) @decorators.memoize def _supports_parsing(): - ''' + """ Check support of parsing - ''' + """ return tuple([int(i) for i in _get_version()]) > (0, 6) def __virtual__(): - ''' + """ Set the virtual pkg module if the os is supported by pkgin - ''' - supported = ['NetBSD', 'SunOS', 'DragonFly', 'Minix', 'Darwin', 'SmartOS'] + """ + supported = ["NetBSD", "SunOS", "DragonFly", "Minix", "Darwin", "SmartOS"] - if __grains__['os'] in supported and _check_pkgin(): + if __grains__["os"] in supported and _check_pkgin(): return __virtualname__ - return (False, 'The pkgin execution module cannot be loaded: only ' - 'available on {0} systems.'.format(', '.join(supported))) + return ( + False, + "The pkgin execution module cannot be loaded: only " + "available on {0} systems.".format(", ".join(supported)), + ) def _splitpkg(name): - ''' + """ Split package name from versioned string - ''' + """ # name is in the format foobar-1.0nb1, already space-splitted - if name[0].isalnum() and name != 'No': # avoid < > = and 'No result' - return name.split(';', 1)[0].rsplit('-', 1) + if name[0].isalnum() and name != "No": # avoid < > = and 'No result' + return name.split(";", 1)[0].rsplit("-", 1) def search(pkg_name): - ''' + """ Searches for an exact match using pkgin ^package$ CLI Example: @@ -121,7 +124,7 @@ def search(pkg_name): .. code-block:: bash salt '*' pkg.search 'mysql-server' - ''' + """ pkglist = {} pkgin = _check_pkgin() @@ -129,12 +132,9 @@ def search(pkg_name): return pkglist if _supports_regex(): - pkg_name = '^{0}$'.format(pkg_name) + pkg_name = "^{0}$".format(pkg_name) - out = __salt__['cmd.run']( - [pkgin, 'se', pkg_name], - output_loglevel='trace' - ) + out = __salt__["cmd.run"]([pkgin, "se", pkg_name], output_loglevel="trace") for line in out.splitlines(): if line: match = _splitpkg(line.split()[0]) @@ -145,7 +145,7 @@ def search(pkg_name): def latest_version(*names, **kwargs): - ''' + """ .. versionchanged: 2016.3.0 Return the latest version of the named package available for upgrade or @@ -160,9 +160,9 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version ... - ''' + """ - refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) + refresh = salt.utils.data.is_true(kwargs.pop("refresh", True)) pkglist = {} pkgin = _check_pkgin() @@ -173,30 +173,30 @@ def latest_version(*names, **kwargs): if refresh: refresh_db() - cmd_prefix = [pkgin, 'se'] + cmd_prefix = [pkgin, "se"] if _supports_parsing(): - cmd_prefix.insert(1, '-p') + cmd_prefix.insert(1, "-p") for name in names: cmd = copy.deepcopy(cmd_prefix) - cmd.append('^{0}$'.format(name) if _supports_regex() else name) + cmd.append("^{0}$".format(name) if _supports_regex() else name) - out = __salt__['cmd.run'](cmd, output_loglevel='trace') + out = __salt__["cmd.run"](cmd, output_loglevel="trace") for line in out.splitlines(): - if line.startswith('No results found for'): + if line.startswith("No results found for"): return pkglist - p = line.split(';' if _supports_parsing() else None) + p = line.split(";" if _supports_parsing() else None) - if p and p[0] in ('=:', '<:', '>:', ''): + if p and p[0] in ("=:", "<:", ">:", ""): # These are explanation comments continue elif p: s = _splitpkg(p[0]) if s: if not s[0] in pkglist: - if len(p) > 1 and p[1] in ('<', '', '='): + if len(p) > 1 and p[1] in ("<", "", "="): pkglist[s[0]] = s[1] else: - pkglist[s[0]] = '' + pkglist[s[0]] = "" if pkglist and len(names) == 1: if names[0] in pkglist: @@ -206,11 +206,13 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -221,12 +223,12 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def refresh_db(force=False): - ''' + """ Use pkg update to get latest pkg_summary force @@ -240,21 +242,21 @@ def refresh_db(force=False): .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) pkgin = _check_pkgin() if pkgin: - cmd = [pkgin, 'up'] + cmd = [pkgin, "up"] if force: - cmd.insert(1, '-f') - call = __salt__['cmd.run_all'](cmd, output_loglevel='trace') + cmd.insert(1, "-f") + call = __salt__["cmd.run_all"](cmd, output_loglevel="trace") - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] raise CommandExecutionError(comment) @@ -262,7 +264,7 @@ def refresh_db(force=False): def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ .. versionchanged: 2016.3.0 List the packages currently installed as a dict:: @@ -274,46 +276,47 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret pkgin = _check_pkgin() ret = {} - out = __salt__['cmd.run']( - [pkgin, 'ls'] if pkgin else ['pkg_info'], - output_loglevel='trace') + out = __salt__["cmd.run"]( + [pkgin, "ls"] if pkgin else ["pkg_info"], output_loglevel="trace" + ) for line in out.splitlines(): try: # Some versions of pkgin check isatty unfortunately # this results in cases where a ' ' or ';' can be used - pkg, ver = re.split('[; ]', line, 1)[0].rsplit('-', 1) + pkg, ver = re.split("[; ]", line, 1)[0].rsplit("-", 1) except ValueError: continue - __salt__['pkg_resource.add_pkg'](ret, pkg, ver) + __salt__["pkg_resource.add_pkg"](ret, pkg, ver) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def list_upgrades(refresh=True, **kwargs): - ''' + """ List all available package upgrades. .. versionadded:: 2018.3.0 @@ -326,7 +329,7 @@ def list_upgrades(refresh=True, **kwargs): .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ pkgs = {} for pkg in sorted(list_pkgs(refresh=refresh).keys()): # NOTE: we already optionally refreshed in de list_pkg call @@ -336,9 +339,8 @@ def list_upgrades(refresh=True, **kwargs): return pkgs -def install(name=None, refresh=False, fromrepo=None, - pkgs=None, sources=None, **kwargs): - ''' +def install(name=None, refresh=False, fromrepo=None, pkgs=None, sources=None, **kwargs): + """ Install the passed package name @@ -384,16 +386,16 @@ def install(name=None, refresh=False, fromrepo=None, .. code-block:: bash salt '*' pkg.install - ''' + """ try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: raise CommandExecutionError(exc) # Support old "repo" argument - repo = kwargs.get('repo', '') + repo = kwargs.get("repo", "") if not fromrepo and repo: fromrepo = repo @@ -406,42 +408,42 @@ def install(name=None, refresh=False, fromrepo=None, if pkgin: cmd = pkgin if fromrepo: - log.info('Setting PKG_REPOS=%s', fromrepo) - env.append(('PKG_REPOS', fromrepo)) + log.info("Setting PKG_REPOS=%s", fromrepo) + env.append(("PKG_REPOS", fromrepo)) else: - cmd = 'pkg_add' + cmd = "pkg_add" if fromrepo: - log.info('Setting PKG_PATH=%s', fromrepo) - env.append(('PKG_PATH', fromrepo)) + log.info("Setting PKG_PATH=%s", fromrepo) + env.append(("PKG_PATH", fromrepo)) - if pkg_type == 'file': - cmd = 'pkg_add' - elif pkg_type == 'repository': + if pkg_type == "file": + cmd = "pkg_add" + elif pkg_type == "repository": if pkgin: if refresh: - args.append('-f') # update repo db - args.extend(('-y', 'in')) # Assume yes when asked + args.append("-f") # update repo db + args.extend(("-y", "in")) # Assume yes when asked args.insert(0, cmd) args.extend(pkg_params) old = list_pkgs() - out = __salt__['cmd.run_all'](args, env=env, output_loglevel='trace') + out = __salt__["cmd.run_all"](args, env=env, output_loglevel="trace") - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) _rehash() @@ -449,7 +451,7 @@ def install(name=None, refresh=False, fromrepo=None, def upgrade(refresh=True, pkgs=None, **kwargs): - ''' + """ Run pkg upgrade, if pkgin used. Otherwise do nothing refresh @@ -480,7 +482,7 @@ def upgrade(refresh=True, pkgs=None, **kwargs): .. code-block:: bash salt '*' pkg.upgrade - ''' + """ pkgin = _check_pkgin() if not pkgin: # There is not easy way to upgrade packages with old package system @@ -493,36 +495,39 @@ def upgrade(refresh=True, pkgs=None, **kwargs): cmds = [] if not pkgs: - cmds.append([pkgin, '-y', 'full-upgrade']) + cmds.append([pkgin, "-y", "full-upgrade"]) elif salt.utils.data.is_list(pkgs): for pkg in pkgs: - cmds.append([pkgin, '-y', 'install', pkg]) + cmds.append([pkgin, "-y", "install", pkg]) else: - result = {'retcode': 1, 'reason': 'Ignoring the parameter `pkgs` because it is not a list!'} - log.error(result['reason']) + result = { + "retcode": 1, + "reason": "Ignoring the parameter `pkgs` because it is not a list!", + } + log.error(result["reason"]) for cmd in cmds: - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - if result['retcode'] != 0: + result = __salt__["cmd.run_all"]( + cmd, output_loglevel="trace", python_shell=False + ) + if result["retcode"] != 0: break - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret def remove(name=None, pkgs=None, **kwargs): - ''' + """ name The name of the package to be deleted. @@ -545,11 +550,9 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( - name, pkgs - ) + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"](name, pkgs) except MinionError as exc: raise CommandExecutionError(exc) @@ -564,39 +567,39 @@ def remove(name=None, pkgs=None, **kwargs): if not ver: continue if isinstance(ver, list): - args.extend(['{0}-{1}'.format(param, v) for v in ver]) + args.extend(["{0}-{1}".format(param, v) for v in ver]) else: - args.append('{0}-{1}'.format(param, ver)) + args.append("{0}-{1}".format(param, ver)) if not args: return {} pkgin = _check_pkgin() - cmd = [pkgin, '-y', 'remove'] if pkgin else ['pkg_remove'] + cmd = [pkgin, "-y", "remove"] if pkgin else ["pkg_remove"] cmd.extend(args) - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace') + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace") - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def purge(name=None, pkgs=None, **kwargs): - ''' + """ Package purges are not supported, this function is identical to ``remove()``. @@ -622,23 +625,23 @@ def purge(name=None, pkgs=None, **kwargs): salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' + """ return remove(name=name, pkgs=pkgs) def _rehash(): - ''' + """ Recomputes internal hash table for the PATH variable. Use whenever a new command is created during the current session. - ''' - shell = __salt__['environ.get']('SHELL') - if shell.split('/')[-1] in ('csh', 'tcsh'): - __salt__['cmd.run']('rehash', output_loglevel='trace') + """ + shell = __salt__["environ.get"]("SHELL") + if shell.split("/")[-1] in ("csh", "tcsh"): + __salt__["cmd.run"]("rehash", output_loglevel="trace") def file_list(package): - ''' + """ List the files that belong to a package. CLI Examples: @@ -646,17 +649,17 @@ def file_list(package): .. code-block:: bash salt '*' pkg.file_list nginx - ''' + """ ret = file_dict(package) files = [] - for pkg_files in six.itervalues(ret['files']): + for pkg_files in six.itervalues(ret["files"]): files.extend(pkg_files) - ret['files'] = files + ret["files"] = files return ret def file_dict(*packages): - ''' + """ .. versionchanged: 2016.3.0 List the files that belong to a package. @@ -667,27 +670,28 @@ def file_dict(*packages): salt '*' pkg.file_dict nginx salt '*' pkg.file_dict nginx varnish - ''' + """ errors = [] files = {} for package in packages: - cmd = ['pkg_info', '-qL', package] - ret = __salt__['cmd.run_all'](cmd, output_loglevel='trace') + cmd = ["pkg_info", "-qL", package] + ret = __salt__["cmd.run_all"](cmd, output_loglevel="trace") files[package] = [] - for line in ret['stderr'].splitlines(): + for line in ret["stderr"].splitlines(): errors.append(line) - for line in ret['stdout'].splitlines(): - if line.startswith('/'): + for line in ret["stdout"].splitlines(): + if line.startswith("/"): files[package].append(line) else: continue # unexpected string - ret = {'errors': errors, 'files': files} + ret = {"errors": errors, "files": files} for field in list(ret): - if not ret[field] or ret[field] == '': + if not ret[field] or ret[field] == "": del ret[field] return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/pkgng.py b/salt/modules/pkgng.py index 4a908084ea3..e16e7d0b9dd 100644 --- a/salt/modules/pkgng.py +++ b/salt/modules/pkgng.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for ``pkgng``, the new package manager for FreeBSD .. important:: @@ -35,7 +35,7 @@ file, in order to use this module to manage packages, like so: providers: pkg: pkgng -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -58,67 +58,71 @@ from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Load as 'pkg' on FreeBSD 10 and greater. Load as 'pkg' on DragonFly BSD. Load as 'pkg' on FreeBSD 9 when config option ``providers:pkg`` is set to 'pkgng'. - ''' - if __grains__['kernel'] == 'DragonFly': + """ + if __grains__["kernel"] == "DragonFly": return __virtualname__ - if __grains__['os'] == 'FreeBSD' and float(__grains__['osrelease']) >= 10: + if __grains__["os"] == "FreeBSD" and float(__grains__["osrelease"]) >= 10: return __virtualname__ - if __grains__['os'] == 'FreeBSD' and int(__grains__['osmajorrelease']) == 9: + if __grains__["os"] == "FreeBSD" and int(__grains__["osmajorrelease"]) == 9: providers = {} - if 'providers' in __opts__: - providers = __opts__['providers'] - log.debug('__opts__.providers: %s', providers) - if providers and 'pkg' in providers and providers['pkg'] == 'pkgng': - log.debug('Configuration option \'providers:pkg\' is set to ' - '\'pkgng\', using \'pkgng\' in favor of \'freebsdpkg\'.') + if "providers" in __opts__: + providers = __opts__["providers"] + log.debug("__opts__.providers: %s", providers) + if providers and "pkg" in providers and providers["pkg"] == "pkgng": + log.debug( + "Configuration option 'providers:pkg' is set to " + "'pkgng', using 'pkgng' in favor of 'freebsdpkg'." + ) return __virtualname__ - return (False, - 'The pkgng execution module cannot be loaded: only available ' - 'on FreeBSD 10 or FreeBSD 9 with providers.pkg set to pkgng.') + return ( + False, + "The pkgng execution module cannot be loaded: only available " + "on FreeBSD 10 or FreeBSD 9 with providers.pkg set to pkgng.", + ) def _pkg(jail=None, chroot=None, root=None): - ''' + """ Returns the prefix for a pkg command, using -j if a jail is specified, or -c if chroot is specified. - ''' - ret = ['pkg'] + """ + ret = ["pkg"] if jail: - ret.extend(['-j', jail]) + ret.extend(["-j", jail]) elif chroot: - ret.extend(['-c', chroot]) + ret.extend(["-c", chroot]) elif root: - ret.extend(['-r', root]) + ret.extend(["-r", root]) return ret def _get_pkgng_version(jail=None, chroot=None, root=None): - ''' + """ return the version of 'pkg' - ''' - cmd = _pkg(jail, chroot, root) + ['--version'] - return __salt__['cmd.run'](cmd).strip() + """ + cmd = _pkg(jail, chroot, root) + ["--version"] + return __salt__["cmd.run"](cmd).strip() def _get_version(name, results): - ''' + """ ``pkg search`` will return all packages for which the pattern is a match. Narrow this down and return the package version, or None if no exact match. - ''' - for line in salt.utils.itertools.split(results, '\n'): + """ + for line in salt.utils.itertools.split(results, "\n"): if not line: continue try: - pkgname, pkgver = line.rsplit('-', 1) + pkgname, pkgver = line.rsplit("-", 1) except ValueError: continue if pkgname == name: @@ -126,23 +130,23 @@ def _get_version(name, results): return None -def _contextkey(jail=None, chroot=None, root=None, prefix='pkg.list_pkgs'): - ''' +def _contextkey(jail=None, chroot=None, root=None, prefix="pkg.list_pkgs"): + """ As this module is designed to manipulate packages in jails and chroots, use the passed jail/chroot to ensure that a key in the __context__ dict that is unique to that jail/chroot is used. - ''' + """ if jail: - return six.text_type(prefix) + '.jail_{0}'.format(jail) + return six.text_type(prefix) + ".jail_{0}".format(jail) elif chroot: - return six.text_type(prefix) + '.chroot_{0}'.format(chroot) + return six.text_type(prefix) + ".chroot_{0}".format(chroot) elif root: - return six.text_type(prefix) + '.root_{0}'.format(root) + return six.text_type(prefix) + ".root_{0}".format(root) return prefix -def parse_config(file_name='/usr/local/etc/pkg.conf'): - ''' +def parse_config(file_name="/usr/local/etc/pkg.conf"): + """ Return dict of uncommented global variables. CLI Example: @@ -152,25 +156,25 @@ def parse_config(file_name='/usr/local/etc/pkg.conf'): salt '*' pkg.parse_config ``NOTE:`` not working properly right now - ''' + """ ret = {} if not os.path.isfile(file_name): - return 'Unable to find {0} on file system'.format(file_name) + return "Unable to find {0} on file system".format(file_name) with salt.utils.files.fopen(file_name) as ifile: for line in ifile: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('#') or line.startswith('\n'): + if line.startswith("#") or line.startswith("\n"): pass else: - key, value = line.split('\t') + key, value = line.split("\t") ret[key] = value - ret['config_file'] = file_name + ret["config_file"] = file_name return ret def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -205,27 +209,29 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version jail= salt '*' pkg.version ... - ''' - with_origin = kwargs.pop('with_origin', False) - ret = __salt__['pkg_resource.version'](*names, **kwargs) + """ + with_origin = kwargs.pop("with_origin", False) + ret = __salt__["pkg_resource.version"](*names, **kwargs) if not salt.utils.data.is_true(with_origin): return ret # Put the return value back into a dict since we're adding a subdict if len(names) == 1: ret = {names[0]: ret} - origins = __context__.get('pkg.origin', {}) - return dict([ - (x, {'origin': origins.get(x, ''), 'version': y}) - for x, y in six.iteritems(ret) - ]) + origins = __context__.get("pkg.origin", {}) + return dict( + [ + (x, {"origin": origins.get(x, ""), "version": y}) + for x, y in six.iteritems(ret) + ] + ) # Support pkg.info get version info, since this is the CLI usage -info = salt.utils.functools.alias_function(version, 'info') +info = salt.utils.functools.alias_function(version, "info") def refresh_db(jail=None, chroot=None, root=None, force=False): - ''' + """ Refresh PACKAGESITE contents .. note:: @@ -259,22 +265,22 @@ def refresh_db(jail=None, chroot=None, root=None, force=False): .. code-block:: bash salt '*' pkg.refresh_db force=True - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) cmd = _pkg(jail, chroot, root) - cmd.append('update') + cmd.append("update") if force: - cmd.append('-f') - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd.append("-f") + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 # Support pkg.update to refresh the db, since this is the CLI usage -update = salt.utils.functools.alias_function(refresh_db, 'update') +update = salt.utils.functools.alias_function(refresh_db, "update") def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -289,43 +295,54 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version salt '*' pkg.latest_version jail= salt '*' pkg.latest_version chroot=/path/to/chroot - ''' + """ if len(names) == 0: - return '' + return "" ret = {} # Initialize the dict with empty strings for name in names: - ret[name] = '' - jail = kwargs.get('jail') - chroot = kwargs.get('chroot') - refresh = kwargs.get('refresh') - root = kwargs.get('root') + ret[name] = "" + jail = kwargs.get("jail") + chroot = kwargs.get("chroot") + refresh = kwargs.get("refresh") + root = kwargs.get("root") pkgs = list_pkgs(versions_as_list=True, jail=jail, chroot=chroot, root=root) - if salt.utils.versions.compare(_get_pkgng_version(jail, chroot, root), '>=', '1.6.0'): + if salt.utils.versions.compare( + _get_pkgng_version(jail, chroot, root), ">=", "1.6.0" + ): quiet = True else: quiet = False for name in names: # FreeBSD supports packages in format java/openjdk7 - if '/' in name: - cmd = _pkg(jail, chroot, root) + ['search'] + if "/" in name: + cmd = _pkg(jail, chroot, root) + ["search"] else: - cmd = _pkg(jail, chroot, root) + ['search', '-S', 'name', '-Q', 'version', '-e'] + cmd = _pkg(jail, chroot, root) + [ + "search", + "-S", + "name", + "-Q", + "version", + "-e", + ] if quiet: - cmd.append('-q') + cmd.append("-q") if not salt.utils.data.is_true(refresh): - cmd.append('-U') + cmd.append("-U") cmd.append(name) pkgver = _get_version( name, sorted( - __salt__['cmd.run'](cmd, python_shell=False, output_loglevel='trace').splitlines(), - reverse=True - ).pop(0) + __salt__["cmd.run"]( + cmd, python_shell=False, output_loglevel="trace" + ).splitlines(), + reverse=True, + ).pop(0), ) if pkgver is not None: @@ -334,10 +351,10 @@ def latest_version(*names, **kwargs): ret[name] = pkgver else: if not any( - (salt.utils.versions.compare(ver1=x, - oper='>=', - ver2=pkgver) - for x in installed) + ( + salt.utils.versions.compare(ver1=x, oper=">=", ver2=pkgver) + for x in installed + ) ): ret[name] = pkgver @@ -348,16 +365,20 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) -def list_pkgs(versions_as_list=False, - jail=None, - chroot=None, - root=None, - with_origin=False, - **kwargs): - ''' +def list_pkgs( + versions_as_list=False, + jail=None, + chroot=None, + root=None, + with_origin=False, + **kwargs +): + """ List the packages currently installed as a dict:: {'': ''} @@ -386,60 +407,66 @@ def list_pkgs(versions_as_list=False, salt '*' pkg.list_pkgs salt '*' pkg.list_pkgs jail= salt '*' pkg.list_pkgs chroot=/path/to/chroot - ''' + """ # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} versions_as_list = salt.utils.data.is_true(versions_as_list) contextkey_pkg = _contextkey(jail, chroot, root) - contextkey_origins = _contextkey(jail, chroot, root, prefix='pkg.origin') + contextkey_origins = _contextkey(jail, chroot, root, prefix="pkg.origin") if contextkey_pkg in __context__: ret = copy.deepcopy(__context__[contextkey_pkg]) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) if salt.utils.data.is_true(with_origin): origins = __context__.get(contextkey_origins, {}) - return dict([ - (x, {'origin': origins.get(x, ''), 'version': y}) - for x, y in six.iteritems(ret) - ]) + return dict( + [ + (x, {"origin": origins.get(x, ""), "version": y}) + for x, y in six.iteritems(ret) + ] + ) return ret ret = {} origins = {} - out = __salt__['cmd.run_stdout']( - _pkg(jail, chroot, root) + ['info', '-ao'], - output_loglevel='trace', - python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): + out = __salt__["cmd.run_stdout"]( + _pkg(jail, chroot, root) + ["info", "-ao"], + output_loglevel="trace", + python_shell=False, + ) + for line in salt.utils.itertools.split(out, "\n"): if not line: continue try: pkg, origin = line.split() - pkgname, pkgver = pkg.rsplit('-', 1) + pkgname, pkgver = pkg.rsplit("-", 1) except ValueError: continue - __salt__['pkg_resource.add_pkg'](ret, pkgname, pkgver) + __salt__["pkg_resource.add_pkg"](ret, pkgname, pkgver) origins[pkgname] = origin - __salt__['pkg_resource.sort_pkglist'](ret) + __salt__["pkg_resource.sort_pkglist"](ret) __context__[contextkey_pkg] = copy.deepcopy(ret) __context__[contextkey_origins] = origins if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) if salt.utils.data.is_true(with_origin): - return dict([ - (x, {'origin': origins.get(x, ''), 'version': y}) - for x, y in six.iteritems(ret) - ]) + return dict( + [ + (x, {"origin": origins.get(x, ""), "version": y}) + for x, y in six.iteritems(ret) + ] + ) return ret def update_package_site(new_url): - ''' + """ Updates remote package repo URL, PACKAGESITE var to be exact. Must use ``http://``, ``ftp://``, or ``https://`` protocol @@ -449,10 +476,10 @@ def update_package_site(new_url): .. code-block:: bash salt '*' pkg.update_package_site http://127.0.0.1/ - ''' - config_file = parse_config()['config_file'] - __salt__['file.sed']( - config_file, 'PACKAGESITE.*', 'PACKAGESITE\t : {0}'.format(new_url) + """ + config_file = parse_config()["config_file"] + __salt__["file.sed"]( + config_file, "PACKAGESITE.*", "PACKAGESITE\t : {0}".format(new_url) ) # add change return later @@ -460,7 +487,7 @@ def update_package_site(new_url): def stats(local=False, remote=False, jail=None, chroot=None, root=None): - ''' + """ Return pkgng stats. CLI Example: @@ -513,24 +540,24 @@ def stats(local=False, remote=False, jail=None, chroot=None, root=None): salt '*' pkg.stats chroot=/path/to/chroot salt '*' pkg.stats chroot=/path/to/chroot local=True salt '*' pkg.stats chroot=/path/to/chroot remote=True - ''' + """ - opts = '' + opts = "" if local: - opts += 'l' + opts += "l" if remote: - opts += 'r' + opts += "r" cmd = _pkg(jail, chroot, root) - cmd.append('stats') + cmd.append("stats") if opts: - cmd.append('-' + opts) - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) - return [x.strip('\t') for x in salt.utils.itertools.split(out, '\n')] + cmd.append("-" + opts) + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) + return [x.strip("\t") for x in salt.utils.itertools.split(out, "\n")] def backup(file_name, jail=None, chroot=None, root=None): - ''' + """ Export installed packages into yaml+mtree file CLI Example: @@ -567,17 +594,17 @@ def backup(file_name, jail=None, chroot=None, root=None): .. code-block:: bash salt '*' pkg.backup /tmp/pkg chroot=/path/to/chroot - ''' - ret = __salt__['cmd.run']( - _pkg(jail, chroot, root) + ['backup', '-d', file_name], - output_loglevel='trace', - python_shell=False + """ + ret = __salt__["cmd.run"]( + _pkg(jail, chroot, root) + ["backup", "-d", file_name], + output_loglevel="trace", + python_shell=False, ) - return ret.split('...')[1] + return ret.split("...")[1] def restore(file_name, jail=None, chroot=None, root=None): - ''' + """ Reads archive created by pkg backup -d and recreates the database. CLI Example: @@ -614,16 +641,16 @@ def restore(file_name, jail=None, chroot=None, root=None): .. code-block:: bash salt '*' pkg.restore /tmp/pkg chroot=/path/to/chroot - ''' - return __salt__['cmd.run']( - _pkg(jail, chroot, root) + ['backup', '-r', file_name], - output_loglevel='trace', - python_shell=False + """ + return __salt__["cmd.run"]( + _pkg(jail, chroot, root) + ["backup", "-r", file_name], + output_loglevel="trace", + python_shell=False, ) def audit(jail=None, chroot=None, root=None): - ''' + """ Audits installed packages against known vulnerabilities CLI Example: @@ -654,33 +681,35 @@ def audit(jail=None, chroot=None, root=None): .. code-block:: bash salt '*' pkg.audit chroot=/path/to/chroot - ''' - return __salt__['cmd.run']( - _pkg(jail, chroot, root) + ['audit', '-F'], - output_loglevel='trace', - python_shell=False + """ + return __salt__["cmd.run"]( + _pkg(jail, chroot, root) + ["audit", "-F"], + output_loglevel="trace", + python_shell=False, ) -def install(name=None, - fromrepo=None, - pkgs=None, - sources=None, - jail=None, - chroot=None, - root=None, - orphan=False, - force=False, - glob=False, - local=False, - dryrun=False, - quiet=False, - reinstall_requires=False, - regex=False, - pcre=False, - batch=False, - **kwargs): - ''' +def install( + name=None, + fromrepo=None, + pkgs=None, + sources=None, + jail=None, + chroot=None, + root=None, + orphan=False, + force=False, + glob=False, + local=False, + dryrun=False, + quiet=False, + reinstall_requires=False, + regex=False, + pcre=False, + batch=False, + **kwargs +): + """ Install package(s) from a repository name @@ -812,9 +841,9 @@ def install(name=None, .. code-block:: bash salt '*' pkg.install batch=True - ''' + """ try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: @@ -824,105 +853,101 @@ def install(name=None, return {} env = {} - opts = 'y' + opts = "y" if salt.utils.data.is_true(orphan): - opts += 'A' + opts += "A" if salt.utils.data.is_true(force): - opts += 'f' + opts += "f" if salt.utils.data.is_true(glob): - opts += 'g' + opts += "g" if salt.utils.data.is_true(local): - opts += 'U' + opts += "U" if salt.utils.data.is_true(dryrun): - opts += 'n' + opts += "n" if salt.utils.data.is_true(quiet): - opts += 'q' + opts += "q" if salt.utils.data.is_true(reinstall_requires): - opts += 'R' + opts += "R" if salt.utils.data.is_true(regex): - opts += 'x' + opts += "x" if salt.utils.data.is_true(pcre): - opts += 'X' + opts += "X" if salt.utils.data.is_true(batch): - env = { - "BATCH": "true", - "ASSUME_ALWAYS_YES": "YES" - } + env = {"BATCH": "true", "ASSUME_ALWAYS_YES": "YES"} old = list_pkgs(jail=jail, chroot=chroot, root=root) - if pkg_type == 'file': - pkg_cmd = 'add' + if pkg_type == "file": + pkg_cmd = "add" # pkg add has smaller set of options (i.e. no -y or -n), filter below - opts = ''.join([opt for opt in opts if opt in 'AfIMq']) + opts = "".join([opt for opt in opts if opt in "AfIMq"]) targets = pkg_params - elif pkg_type == 'repository': - pkg_cmd = 'install' - if pkgs is None and kwargs.get('version') and len(pkg_params) == 1: + elif pkg_type == "repository": + pkg_cmd = "install" + if pkgs is None and kwargs.get("version") and len(pkg_params) == 1: # Only use the 'version' param if 'name' was not specified as a # comma-separated list - pkg_params = {name: kwargs.get('version')} + pkg_params = {name: kwargs.get("version")} targets = [] for param, version_num in six.iteritems(pkg_params): if version_num is None: targets.append(param) else: - targets.append('{0}-{1}'.format(param, version_num)) + targets.append("{0}-{1}".format(param, version_num)) else: - raise CommandExecutionError('Problem encountered installing package(s)') + raise CommandExecutionError("Problem encountered installing package(s)") cmd = _pkg(jail, chroot, root) cmd.append(pkg_cmd) if fromrepo: - cmd.extend(['-r', fromrepo]) + cmd.extend(["-r", fromrepo]) if opts: - cmd.append('-' + opts) + cmd.append("-" + opts) cmd.extend(targets) - if pkg_cmd == 'add' and salt.utils.data.is_true(dryrun): + if pkg_cmd == "add" and salt.utils.data.is_true(dryrun): # pkg add doesn't have a dry-run mode, so echo out what will be run - return ' '.join(cmd) + return " ".join(cmd) - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False, - env=env + out = __salt__["cmd.run_all"]( + cmd, output_loglevel="trace", python_shell=False, env=env ) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] __context__.pop(_contextkey(jail, chroot, root), None) - __context__.pop(_contextkey(jail, chroot, root, prefix='pkg.origin'), None) + __context__.pop(_contextkey(jail, chroot, root, prefix="pkg.origin"), None) new = list_pkgs(jail=jail, chroot=chroot, root=root) ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) return ret -def remove(name=None, - pkgs=None, - jail=None, - chroot=None, - root=None, - all_installed=False, - force=False, - glob=False, - dryrun=False, - recurse=False, - regex=False, - pcre=False, - **kwargs): - ''' +def remove( + name=None, + pkgs=None, + jail=None, + chroot=None, + root=None, + all_installed=False, + force=False, + glob=False, + dryrun=False, + recurse=False, + regex=False, + pcre=False, + **kwargs +): + """ Remove a package from the database and system .. note:: @@ -1015,11 +1040,11 @@ def remove(name=None, .. code-block:: bash salt '*' pkg.remove pcre=True - ''' + """ del kwargs # Unused parameter try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -1029,7 +1054,7 @@ def remove(name=None, # FreeBSD pkg supports `openjdk` and `java/openjdk7` package names if pkg[0].find("/") > 0: origin = pkg[0] - pkg = [k for k, v in six.iteritems(old) if v['origin'] == origin][0] + pkg = [k for k, v in six.iteritems(old) if v["origin"] == origin][0] if pkg[0] in old: targets.append(pkg[0]) @@ -1037,63 +1062,59 @@ def remove(name=None, if not targets: return {} - opts = '' + opts = "" if salt.utils.data.is_true(all_installed): - opts += 'a' + opts += "a" if salt.utils.data.is_true(force): - opts += 'f' + opts += "f" if salt.utils.data.is_true(glob): - opts += 'g' + opts += "g" if salt.utils.data.is_true(dryrun): - opts += 'n' + opts += "n" if not salt.utils.data.is_true(dryrun): - opts += 'y' + opts += "y" if salt.utils.data.is_true(recurse): - opts += 'R' + opts += "R" if salt.utils.data.is_true(regex): - opts += 'x' + opts += "x" if salt.utils.data.is_true(pcre): - opts += 'X' + opts += "X" cmd = _pkg(jail, chroot, root) - cmd.append('delete') + cmd.append("delete") if opts: - cmd.append('-' + opts) + cmd.append("-" + opts) cmd.extend(targets) - out = __salt__['cmd.run_all']( - cmd, - output_loglevel='trace', - python_shell=False - ) + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] __context__.pop(_contextkey(jail, chroot, root), None) - __context__.pop(_contextkey(jail, chroot, root, prefix='pkg.origin'), None) + __context__.pop(_contextkey(jail, chroot, root, prefix="pkg.origin"), None) new = list_pkgs(jail=jail, chroot=chroot, root=root, with_origin=True) ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret # Support pkg.delete to remove packages, since this is the CLI usage -delete = salt.utils.functools.alias_function(remove, 'delete') +delete = salt.utils.functools.alias_function(remove, "delete") # No equivalent to purge packages, use remove instead -purge = salt.utils.functools.alias_function(remove, 'purge') +purge = salt.utils.functools.alias_function(remove, "purge") def upgrade(*names, **kwargs): - ''' + """ Upgrade named or all packages (run a ``pkg upgrade``). If is omitted, the operation is executed on all packages. @@ -1167,57 +1188,51 @@ def upgrade(*names, **kwargs): .. code-block:: bash salt '*' pkg.upgrade dryrun=True - ''' - jail = kwargs.pop('jail', None) - chroot = kwargs.pop('chroot', None) - root = kwargs.pop('root', None) - force = kwargs.pop('force', False) - local = kwargs.pop('local', False) - dryrun = kwargs.pop('dryrun', False) - pkgs = kwargs.pop('pkgs', []) - opts = '' + """ + jail = kwargs.pop("jail", None) + chroot = kwargs.pop("chroot", None) + root = kwargs.pop("root", None) + force = kwargs.pop("force", False) + local = kwargs.pop("local", False) + dryrun = kwargs.pop("dryrun", False) + pkgs = kwargs.pop("pkgs", []) + opts = "" if force: - opts += 'f' + opts += "f" if local: - opts += 'L' + opts += "L" if dryrun: - opts += 'n' + opts += "n" if not dryrun: - opts += 'y' + opts += "y" cmd = _pkg(jail, chroot, root) - cmd.append('upgrade') + cmd.append("upgrade") if opts: - cmd.append('-' + opts) + cmd.append("-" + opts) if names: cmd.extend(names) if pkgs: cmd.extend(pkgs) old = list_pkgs() - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) __context__.pop(_contextkey(jail, chroot, root), None) - __context__.pop(_contextkey(jail, chroot, root, prefix='pkg.origin'), None) + __context__.pop(_contextkey(jail, chroot, root, prefix="pkg.origin"), None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret -def clean(jail=None, - chroot=None, - root=None, - clean_all=False, - dryrun=False): - ''' +def clean(jail=None, chroot=None, root=None, clean_all=False, dryrun=False): + """ Cleans the local cache of fetched remote packages CLI Example: @@ -1268,28 +1283,24 @@ def clean(jail=None, .. code-block:: bash salt '*' pkg.clean dryrun=True - ''' - opts = '' + """ + opts = "" if clean_all: - opts += 'a' + opts += "a" if dryrun: - opts += 'n' + opts += "n" else: - opts += 'y' + opts += "y" cmd = _pkg(jail, chroot, root) - cmd.append('clean') + cmd.append("clean") if opts: - cmd.append('-' + opts) - return __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False - ) + cmd.append("-" + opts) + return __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) def autoremove(jail=None, chroot=None, root=None, dryrun=False): - ''' + """ Delete packages which were automatically installed as dependencies and are not required anymore. @@ -1305,31 +1316,24 @@ def autoremove(jail=None, chroot=None, root=None, dryrun=False): salt '*' pkg.autoremove jail= salt '*' pkg.autoremove dryrun=True salt '*' pkg.autoremove jail= dryrun=True - ''' - opts = '' + """ + opts = "" if dryrun: - opts += 'n' + opts += "n" else: - opts += 'y' + opts += "y" cmd = _pkg(jail, chroot, root) - cmd.append('autoremove') + cmd.append("autoremove") if opts: - cmd.append('-' + opts) - return __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False - ) + cmd.append("-" + opts) + return __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) -def check(jail=None, - chroot=None, - root=None, - depends=False, - recompute=False, - checksum=False): - ''' +def check( + jail=None, chroot=None, root=None, depends=False, recompute=False, checksum=False +): + """ Sanity checks installed packages jail @@ -1384,31 +1388,27 @@ def check(jail=None, .. code-block:: bash salt '*' pkg.check checksum=True - ''' + """ if not any((depends, recompute, checksum)): - return 'One of depends, recompute, or checksum must be set to True' + return "One of depends, recompute, or checksum must be set to True" - opts = '' + opts = "" if depends: - opts += 'dy' + opts += "dy" if recompute: - opts += 'r' + opts += "r" if checksum: - opts += 's' + opts += "s" cmd = _pkg(jail, chroot, root) - cmd.append('check') + cmd.append("check") if opts: - cmd.append('-' + opts) - return __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False - ) + cmd.append("-" + opts) + return __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) def which(path, jail=None, chroot=None, root=None, origin=False, quiet=False): - ''' + """ Displays which package installed a specific file CLI Example: @@ -1458,42 +1458,40 @@ def which(path, jail=None, chroot=None, root=None, origin=False, quiet=False): .. code-block:: bash salt '*' pkg.which quiet=True - ''' - opts = '' + """ + opts = "" if quiet: - opts += 'q' + opts += "q" if origin: - opts += 'o' + opts += "o" cmd = _pkg(jail, chroot, root) - cmd.append('which') + cmd.append("which") if opts: - cmd.append('-' + opts) + cmd.append("-" + opts) cmd.append(path) - return __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False - ) + return __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) -def search(name, - jail=None, - chroot=None, - root=None, - exact=False, - glob=False, - regex=False, - pcre=False, - comment=False, - desc=False, - full=False, - depends=False, - size=False, - quiet=False, - origin=False, - prefix=False): - ''' +def search( + name, + jail=None, + chroot=None, + root=None, + exact=False, + glob=False, + regex=False, + pcre=False, + comment=False, + desc=False, + full=False, + depends=False, + size=False, + quiet=False, + origin=False, + prefix=False, +): + """ Searches in remote package repositories CLI Example: @@ -1633,59 +1631,57 @@ def search(name, .. code-block:: bash salt '*' pkg.search pattern prefix=True - ''' + """ - opts = '' + opts = "" if exact: - opts += 'e' + opts += "e" if glob: - opts += 'g' + opts += "g" if regex: - opts += 'x' + opts += "x" if pcre: - opts += 'X' + opts += "X" if comment: - opts += 'c' + opts += "c" if desc: - opts += 'D' + opts += "D" if full: - opts += 'f' + opts += "f" if depends: - opts += 'd' + opts += "d" if size: - opts += 's' + opts += "s" if quiet: - opts += 'q' + opts += "q" if origin: - opts += 'o' + opts += "o" if prefix: - opts += 'p' + opts += "p" cmd = _pkg(jail, chroot, root) - cmd.append('search') + cmd.append("search") if opts: - cmd.append('-' + opts) + cmd.append("-" + opts) cmd.append(name) - return __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False - ) + return __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) -def fetch(name, - jail=None, - chroot=None, - root=None, - fetch_all=False, - quiet=False, - fromrepo=None, - glob=True, - regex=False, - pcre=False, - local=False, - depends=False): - ''' +def fetch( + name, + jail=None, + chroot=None, + root=None, + fetch_all=False, + quiet=False, + fromrepo=None, + glob=True, + regex=False, + pcre=False, + local=False, + depends=False, +): + """ Fetches remote packages CLI Example: @@ -1790,44 +1786,35 @@ def fetch(name, .. code-block:: bash salt '*' pkg.fetch depends=True - ''' - opts = '' + """ + opts = "" if fetch_all: - opts += 'a' + opts += "a" if quiet: - opts += 'q' + opts += "q" if glob: - opts += 'g' + opts += "g" if regex: - opts += 'x' + opts += "x" if pcre: - opts += 'X' + opts += "X" if local: - opts += 'L' + opts += "L" if depends: - opts += 'd' + opts += "d" cmd = _pkg(jail, chroot, root) - cmd.extend(['fetch', '-y']) + cmd.extend(["fetch", "-y"]) if fromrepo: - cmd.extend(['-r', fromrepo]) + cmd.extend(["-r", fromrepo]) if opts: - cmd.append('-' + opts) + cmd.append("-" + opts) cmd.append(name) - return __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False - ) + return __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) -def updating(name, - jail=None, - chroot=None, - root=None, - filedate=None, - filename=None): - '''' +def updating(name, jail=None, chroot=None, root=None, filedate=None, filename=None): + """' Displays UPDATING entries of software packages CLI Example: @@ -1876,28 +1863,24 @@ def updating(name, .. code-block:: bash salt '*' pkg.updating foo filename=/tmp/UPDATING - ''' + """ - opts = '' + opts = "" if filedate: - opts += 'd {0}'.format(filedate) + opts += "d {0}".format(filedate) if filename: - opts += 'f {0}'.format(filename) + opts += "f {0}".format(filename) cmd = _pkg(jail, chroot, root) - cmd.append('updating') + cmd.append("updating") if opts: - cmd.append('-' + opts) + cmd.append("-" + opts) cmd.append(name) - return __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False - ) + return __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) def hold(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 - ''' + """ Version-lock packages .. note:: @@ -1922,7 +1905,7 @@ def hold(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 salt '*' pkg.hold salt '*' pkg.hold pkgs='["foo", "bar"]' - ''' + """ targets = [] if pkgs: targets.extend(pkgs) @@ -1934,35 +1917,34 @@ def hold(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 if isinstance(target, dict): target = next(six.iterkeys(target)) - ret[target] = {'name': target, - 'changes': {}, - 'result': False, - 'comment': ''} + ret[target] = {"name": target, "changes": {}, "result": False, "comment": ""} if not locked(target, **kwargs): - if 'test' in __opts__ and __opts__['test']: + if "test" in __opts__ and __opts__["test"]: ret[target].update(result=None) - ret[target]['comment'] = ('Package {0} is set to be held.' - .format(target)) + ret[target]["comment"] = "Package {0} is set to be held.".format(target) else: if lock(target, **kwargs): ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is now being held.' - .format(target)) - ret[target]['changes']['new'] = 'hold' - ret[target]['changes']['old'] = '' + ret[target]["comment"] = "Package {0} is now being held.".format( + target + ) + ret[target]["changes"]["new"] = "hold" + ret[target]["changes"]["old"] = "" else: - ret[target]['comment'] = ('Package {0} was unable to be held.' - .format(target)) + ret[target][ + "comment" + ] = "Package {0} was unable to be held.".format(target) else: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is already set to be held.' - .format(target)) + ret[target]["comment"] = "Package {0} is already set to be held.".format( + target + ) return ret def unhold(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 - ''' + """ Remove version locks .. note:: @@ -1987,7 +1969,7 @@ def unhold(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 salt '*' pkg.unhold salt '*' pkg.unhold pkgs='["foo", "bar"]' - ''' + """ targets = [] if pkgs: targets.extend(pkgs) @@ -1999,35 +1981,34 @@ def unhold(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 if isinstance(target, dict): target = next(six.iterkeys(target)) - ret[target] = {'name': target, - 'changes': {}, - 'result': False, - 'comment': ''} + ret[target] = {"name": target, "changes": {}, "result": False, "comment": ""} if locked(target, **kwargs): - if __opts__['test']: + if __opts__["test"]: ret[target].update(result=None) - ret[target]['comment'] = ('Package {0} is set to be unheld.' - .format(target)) + ret[target]["comment"] = "Package {0} is set to be unheld.".format( + target + ) else: if unlock(target, **kwargs): ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is no longer held.' - .format(target)) - ret[target]['changes']['new'] = '' - ret[target]['changes']['old'] = 'hold' + ret[target]["comment"] = "Package {0} is no longer held.".format( + target + ) + ret[target]["changes"]["new"] = "" + ret[target]["changes"]["old"] = "hold" else: - ret[target]['comment'] = ('Package {0} was unable to be ' - 'unheld.'.format(target)) + ret[target][ + "comment" + ] = "Package {0} was unable to be " "unheld.".format(target) else: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is not being held.' - .format(target)) + ret[target]["comment"] = "Package {0} is not being held.".format(target) return ret def list_locked(**kwargs): - ''' + """ Query the package database those packages which are locked against reinstallation, modification or deletion. @@ -2068,13 +2049,15 @@ def list_locked(**kwargs): salt '*' pkg.list_locked root=/path/to/chroot - ''' - return ['{0}-{1}'.format(pkgname, version(pkgname, **kwargs)) - for pkgname in _lockcmd('lock', name=None, **kwargs)] + """ + return [ + "{0}-{1}".format(pkgname, version(pkgname, **kwargs)) + for pkgname in _lockcmd("lock", name=None, **kwargs) + ] def locked(name, **kwargs): - ''' + """ Query the package database to determine if the named package is locked against reinstallation, modification or deletion. @@ -2115,15 +2098,15 @@ def locked(name, **kwargs): salt '*' pkg.locked root=/path/to/chroot - ''' - if name in _lockcmd('lock', name=None, **kwargs): + """ + if name in _lockcmd("lock", name=None, **kwargs): return True return False def lock(name, **kwargs): - ''' + """ Lock the named package against reinstallation, modification or deletion. Returns True if the named package was successfully locked. @@ -2163,15 +2146,15 @@ def lock(name, **kwargs): salt '*' pkg.lock root=/path/to/chroot - ''' - if name in _lockcmd('lock', name, **kwargs): + """ + if name in _lockcmd("lock", name, **kwargs): return True return False def unlock(name, **kwargs): - ''' + """ Unlock the named package against reinstallation, modification or deletion. Returns True if the named package was successfully unlocked. @@ -2211,58 +2194,57 @@ def unlock(name, **kwargs): salt '*' pkg.unlock root=/path/to/chroot - ''' - if name in _lockcmd('unlock', name, **kwargs): + """ + if name in _lockcmd("unlock", name, **kwargs): return False return True def _lockcmd(subcmd, pkgname=None, **kwargs): - ''' + """ Helper function for lock and unlock commands, because their syntax is identical. Run the lock/unlock command, and return a list of locked packages - ''' + """ - jail = kwargs.pop('jail', None) - chroot = kwargs.pop('chroot', None) - root = kwargs.pop('root', None) + jail = kwargs.pop("jail", None) + chroot = kwargs.pop("chroot", None) + root = kwargs.pop("root", None) locked_pkgs = [] cmd = _pkg(jail, chroot, root) cmd.append(subcmd) - cmd.append('-y') - cmd.append('--quiet') - cmd.append('--show-locked') + cmd.append("-y") + cmd.append("--quiet") + cmd.append("--show-locked") if pkgname: cmd.append(pkgname) - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace', python_shell=False) + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if out['retcode'] != 0: + if out["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered {0}ing packages'.format(subcmd), - info={'result': out} + "Problem encountered {0}ing packages".format(subcmd), info={"result": out} ) - for line in salt.utils.itertools.split(out['stdout'], '\n'): + for line in salt.utils.itertools.split(out["stdout"], "\n"): if not line: continue try: - pkgname = line.rsplit('-', 1)[0] + pkgname = line.rsplit("-", 1)[0] except ValueError: continue locked_pkgs.append(pkgname) - log.debug('Locked packages: %s', ', '.join(locked_pkgs)) + log.debug("Locked packages: %s", ", ".join(locked_pkgs)) return locked_pkgs def list_upgrades(refresh=True, **kwargs): - ''' + """ List those packages for which an upgrade is available The ``fromrepo`` argument is also supported, as used in pkg states. @@ -2302,28 +2284,32 @@ def list_upgrades(refresh=True, **kwargs): salt '*' pkg.list_upgrades root=/path/to/chroot - ''' - jail = kwargs.pop('jail', None) - chroot = kwargs.pop('chroot', None) - root = kwargs.pop('root', None) - fromrepo = kwargs.pop('fromrepo', None) + """ + jail = kwargs.pop("jail", None) + chroot = kwargs.pop("chroot", None) + root = kwargs.pop("root", None) + fromrepo = kwargs.pop("fromrepo", None) cmd = _pkg(jail, chroot, root) - cmd.extend(['upgrade', '--dry-run', '--quiet']) + cmd.extend(["upgrade", "--dry-run", "--quiet"]) if not refresh: - cmd.append('--no-repo-update') + cmd.append("--no-repo-update") if fromrepo: - cmd.extend(['--repository', fromrepo]) + cmd.extend(["--repository", fromrepo]) - out = __salt__['cmd.run_stdout'](cmd, output_loglevel='trace', python_shell=False, ignore_retcode=True) + out = __salt__["cmd.run_stdout"]( + cmd, output_loglevel="trace", python_shell=False, ignore_retcode=True + ) - return {pkgname: pkgstat['version']['new'] for pkgname, pkgstat - in six.iteritems(_parse_upgrade(out)['upgrade'])} + return { + pkgname: pkgstat["version"]["new"] + for pkgname, pkgstat in six.iteritems(_parse_upgrade(out)["upgrade"]) + } def _parse_upgrade(stdout): - ''' + """ Parse the output from the ``pkg upgrade --dry-run`` command Returns a dictionary of the expected actions: @@ -2362,87 +2348,92 @@ def _parse_upgrade(stdout): 'reason': reason 'version': 'current': n.n.n - ''' + """ # Match strings like 'python36: 3.6.3 -> 3.6.4 [FreeBSD]' - upgrade_regex = re.compile(r'^\s+([^:]+):\s([0-9a-z_,.]+)\s+->\s+([0-9a-z_,.]+)\s*(\[([^]]+)\])?\s*(\(([^)]+)\))?') + upgrade_regex = re.compile( + r"^\s+([^:]+):\s([0-9a-z_,.]+)\s+->\s+([0-9a-z_,.]+)\s*(\[([^]]+)\])?\s*(\(([^)]+)\))?" + ) # Match strings like 'rubygem-bcrypt_pbkdf: 1.0.0 [FreeBSD]' - install_regex = re.compile(r'^\s+([^:]+):\s+([0-9a-z_,.]+)\s*(\[([^]]+)\])?\s*(\(([^)]+)\))?') + install_regex = re.compile( + r"^\s+([^:]+):\s+([0-9a-z_,.]+)\s*(\[([^]]+)\])?\s*(\(([^)]+)\))?" + ) # Match strings like 'py27-yaml-3.11_2 [FreeBSD] (direct dependency changed: py27-setuptools)' - reinstall_regex = re.compile(r'^\s+(\S+)-(?<=-)([0-9a-z_,.]+)\s*(\[([^]]+)\])?\s*(\(([^)]+)\))?') + reinstall_regex = re.compile( + r"^\s+(\S+)-(?<=-)([0-9a-z_,.]+)\s*(\[([^]]+)\])?\s*(\(([^)]+)\))?" + ) - result = {'upgrade': {}, 'install': {}, 'reinstall': {}, 'remove': {}, 'downgrade': {}} + result = { + "upgrade": {}, + "install": {}, + "reinstall": {}, + "remove": {}, + "downgrade": {}, + } action = None - for line in salt.utils.itertools.split(stdout, '\n'): + for line in salt.utils.itertools.split(stdout, "\n"): if not line: action = None continue - if line == 'Installed packages to be UPGRADED:': - action = 'upgrade' + if line == "Installed packages to be UPGRADED:": + action = "upgrade" continue - if line == 'New packages to be INSTALLED:': - action = 'install' + if line == "New packages to be INSTALLED:": + action = "install" continue - if line == 'Installed packages to be REINSTALLED:': - action = 'reinstall' + if line == "Installed packages to be REINSTALLED:": + action = "reinstall" continue - if line == 'Installed packages to be REMOVED:': - action = 'remove' + if line == "Installed packages to be REMOVED:": + action = "remove" continue - if line == 'Installed packages to be DOWNGRADED:': - action = 'downgrade' + if line == "Installed packages to be DOWNGRADED:": + action = "downgrade" continue - if action == 'upgrade' or action == 'downgrade': + if action == "upgrade" or action == "downgrade": match = upgrade_regex.match(line) if match: result[action][match.group(1)] = { - 'version': { - 'current': match.group(2), - 'new': match.group(3) - }, - 'repo': match.group(5), - 'reason': match.group(7) + "version": {"current": match.group(2), "new": match.group(3)}, + "repo": match.group(5), + "reason": match.group(7), } else: - log.error('Unable to parse %s: \'%s\'', action, line) + log.error("Unable to parse %s: '%s'", action, line) - if action == 'install': + if action == "install": match = install_regex.match(line) if match: result[action][match.group(1)] = { - 'version': { - 'current': match.group(2) - }, - 'repo': match.group(4), - 'reason': match.group(6) + "version": {"current": match.group(2)}, + "repo": match.group(4), + "reason": match.group(6), } else: - log.error('Unable to parse %s: \'%s\'', action, line) + log.error("Unable to parse %s: '%s'", action, line) - if (action == 'reinstall') or (action == 'remove'): + if (action == "reinstall") or (action == "remove"): match = reinstall_regex.match(line) if match: result[action][match.group(1)] = { - 'version': { - 'current': match.group(2) - }, - 'repo': match.group(4), - 'reason': match.group(6) + "version": {"current": match.group(2)}, + "repo": match.group(4), + "reason": match.group(6), } else: - log.error('Unable to parse %s: \'%s\'', action, line) + log.error("Unable to parse %s: '%s'", action, line) return result def version_cmp(pkg1, pkg2, ignore_epoch=False): - ''' + """ Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if pkg1 == pkg2, and 1 if pkg1 > pkg2. Return None if there was a problem making the comparison. @@ -2452,23 +2443,22 @@ def version_cmp(pkg1, pkg2, ignore_epoch=False): .. code-block:: bash salt '*' pkg.version_cmp '2.1.11' '2.1.12' - ''' + """ del ignore_epoch # Unused parameter # Don't worry about ignore_epoch since we're shelling out to pkg. sym = { - '<': -1, - '>': 1, - '=': 0, + "<": -1, + ">": 1, + "=": 0, } try: - cmd = ['pkg', 'version', '--test-version', pkg1, pkg2] - ret = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False, - ignore_retcode=True) - if ret['stdout'] in sym: - return sym[ret['stdout']] + cmd = ["pkg", "version", "--test-version", pkg1, pkg2] + ret = __salt__["cmd.run_all"]( + cmd, output_loglevel="trace", python_shell=False, ignore_retcode=True + ) + if ret["stdout"] in sym: + return sym[ret["stdout"]] except Exception as exc: # pylint: disable=broad-except log.error(exc) diff --git a/salt/modules/pkgutil.py b/salt/modules/pkgutil.py index 510feaa1469..868e13eff16 100644 --- a/salt/modules/pkgutil.py +++ b/salt/modules/pkgutil.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Pkgutil support for Solaris .. important:: @@ -7,7 +7,7 @@ Pkgutil support for Solaris minion, and it is using a different module (or gives an error similar to *'pkg.install' is not available*), see :ref:`here `. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -22,21 +22,24 @@ from salt.exceptions import CommandExecutionError, MinionError from salt.ext import six # Define the module's virtual name -__virtualname__ = 'pkgutil' +__virtualname__ = "pkgutil" def __virtual__(): - ''' + """ Set the virtual pkg module if the os is Solaris - ''' - if __grains__['os_family'] == 'Solaris': + """ + if __grains__["os_family"] == "Solaris": return __virtualname__ - return (False, 'The pkgutil execution module cannot be loaded: ' - 'only available on Solaris systems.') + return ( + False, + "The pkgutil execution module cannot be loaded: " + "only available on Solaris systems.", + ) def refresh_db(): - ''' + """ Updates the pkgutil repo database (pkgutil -U) CLI Example: @@ -44,14 +47,14 @@ def refresh_db(): .. code-block:: bash salt '*' pkgutil.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) - return __salt__['cmd.retcode']('/opt/csw/bin/pkgutil -U') == 0 + return __salt__["cmd.retcode"]("/opt/csw/bin/pkgutil -U") == 0 def upgrade_available(name): - ''' + """ Check if there is an upgrade available for a certain package CLI Example: @@ -59,23 +62,22 @@ def upgrade_available(name): .. code-block:: bash salt '*' pkgutil.upgrade_available CSWpython - ''' + """ version_num = None - cmd = '/opt/csw/bin/pkgutil -c --parse --single {0}'.format( - name) - out = __salt__['cmd.run_stdout'](cmd) + cmd = "/opt/csw/bin/pkgutil -c --parse --single {0}".format(name) + out = __salt__["cmd.run_stdout"](cmd) if out: version_num = out.split()[2].strip() if version_num: if version_num == "SAME": - return '' + return "" else: return version_num - return '' + return "" def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 - ''' + """ List all available package upgrades on this system CLI Example: @@ -83,14 +85,13 @@ def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 .. code-block:: bash salt '*' pkgutil.list_upgrades - ''' + """ if salt.utils.data.is_true(refresh): refresh_db() upgrades = {} - lines = __salt__['cmd.run_stdout']( - '/opt/csw/bin/pkgutil -A --parse').splitlines() + lines = __salt__["cmd.run_stdout"]("/opt/csw/bin/pkgutil -A --parse").splitlines() for line in lines: - comps = line.split('\t') + comps = line.split("\t") if comps[2] == "SAME": continue if comps[2] == "not installed": @@ -100,7 +101,7 @@ def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 def upgrade(refresh=True): - ''' + """ Upgrade all of the packages to the latest available version. Returns a dict containing the changes:: @@ -113,7 +114,7 @@ def upgrade(refresh=True): .. code-block:: bash salt '*' pkgutil.upgrade - ''' + """ if salt.utils.data.is_true(refresh): refresh_db() @@ -121,15 +122,15 @@ def upgrade(refresh=True): # Install or upgrade the package # If package is already installed - cmd = '/opt/csw/bin/pkgutil -yu' - __salt__['cmd.run_all'](cmd) - __context__.pop('pkg.list_pkgs', None) + cmd = "/opt/csw/bin/pkgutil -yu" + __salt__["cmd.run_all"](cmd) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() return salt.utils.data.compare_dicts(old, new) def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed as a dict:: {'': ''} @@ -140,43 +141,43 @@ def list_pkgs(versions_as_list=False, **kwargs): salt '*' pkg.list_pkgs salt '*' pkg.list_pkgs versions_as_list=True - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # 'removed' not yet implemented or not applicable - if salt.utils.data.is_true(kwargs.get('removed')): + if salt.utils.data.is_true(kwargs.get("removed")): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret ret = {} - cmd = '/usr/bin/pkginfo -x' + cmd = "/usr/bin/pkginfo -x" # Package information returned two lines per package. On even-offset # lines, the package name is in the first column. On odd-offset lines, the # package version is in the second column. - lines = __salt__['cmd.run'](cmd).splitlines() + lines = __salt__["cmd.run"](cmd).splitlines() for index, line in enumerate(lines): if index % 2 == 0: name = line.split()[0].strip() if index % 2 == 1: version_num = line.split()[1].strip() - __salt__['pkg_resource.add_pkg'](ret, name, version_num) + __salt__["pkg_resource.add_pkg"](ret, name, version_num) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def version(*names, **kwargs): - ''' + """ Returns a version if the package is installed, else returns an empty string CLI Example: @@ -184,12 +185,12 @@ def version(*names, **kwargs): .. code-block:: bash salt '*' pkgutil.version CSWpython - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -203,23 +204,23 @@ def latest_version(*names, **kwargs): salt '*' pkgutil.latest_version CSWpython salt '*' pkgutil.latest_version ... - ''' - refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) + """ + refresh = salt.utils.data.is_true(kwargs.pop("refresh", True)) if not names: - return '' + return "" ret = {} # Initialize the dict with empty strings for name in names: - ret[name] = '' + ret[name] = "" # Refresh before looking for the latest version available if refresh: refresh_db() pkgs = list_pkgs() - cmd = '/opt/csw/bin/pkgutil -a --parse {0}'.format(' '.join(names)) - output = __salt__['cmd.run_all'](cmd).get('stdout', '').splitlines() + cmd = "/opt/csw/bin/pkgutil -a --parse {0}".format(" ".join(names)) + output = __salt__["cmd.run_all"](cmd).get("stdout", "").splitlines() for line in output: try: name, version_rev = line.split()[1:3] @@ -227,11 +228,9 @@ def latest_version(*names, **kwargs): continue if name in names: - cver = pkgs.get(name, '') - nver = version_rev.split(',')[0] - if not cver or salt.utils.versions.compare(ver1=cver, - oper='<', - ver2=nver): + cver = pkgs.get(name, "") + nver = version_rev.split(",")[0] + if not cver or salt.utils.versions.compare(ver1=cver, oper="<", ver2=nver): # Remove revision for version comparison ret[name] = version_rev @@ -242,11 +241,13 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def install(name=None, refresh=False, version=None, pkgs=None, **kwargs): - ''' + """ Install packages using the pkgutil tool. CLI Example: @@ -275,15 +276,13 @@ def install(name=None, refresh=False, version=None, pkgs=None, **kwargs): {'': {'old': '', 'new': ''}} - ''' + """ if refresh: refresh_db() try: # Ignore 'sources' argument - pkg_params = __salt__['pkg_resource.parse_targets'](name, - pkgs, - **kwargs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs, **kwargs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -297,18 +296,18 @@ def install(name=None, refresh=False, version=None, pkgs=None, **kwargs): if pkgver is None: targets.append(param) else: - targets.append('{0}-{1}'.format(param, pkgver)) + targets.append("{0}-{1}".format(param, pkgver)) - cmd = '/opt/csw/bin/pkgutil -yu {0}'.format(' '.join(targets)) + cmd = "/opt/csw/bin/pkgutil -yu {0}".format(" ".join(targets)) old = list_pkgs() - __salt__['cmd.run_all'](cmd) - __context__.pop('pkg.list_pkgs', None) + __salt__["cmd.run_all"](cmd) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() return salt.utils.data.compare_dicts(old, new) def remove(name=None, pkgs=None, **kwargs): - ''' + """ Remove a package and all its dependencies which are not in use by other packages. @@ -334,9 +333,9 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove salt '*' pkg.remove ,, salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -344,15 +343,15 @@ def remove(name=None, pkgs=None, **kwargs): targets = [x for x in pkg_params if x in old] if not targets: return {} - cmd = '/opt/csw/bin/pkgutil -yr {0}'.format(' '.join(targets)) - __salt__['cmd.run_all'](cmd) - __context__.pop('pkg.list_pkgs', None) + cmd = "/opt/csw/bin/pkgutil -yr {0}".format(" ".join(targets)) + __salt__["cmd.run_all"](cmd) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() return salt.utils.data.compare_dicts(old, new) def purge(name=None, pkgs=None, **kwargs): - ''' + """ Package purges are not supported, this function is identical to ``remove()``. @@ -378,5 +377,5 @@ def purge(name=None, pkgs=None, **kwargs): salt '*' pkg.purge salt '*' pkg.purge ,, salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' + """ return remove(name=name, pkgs=pkgs) diff --git a/salt/modules/portage_config.py b/salt/modules/portage_config.py index 7e05168d886..67cd1b09a08 100644 --- a/salt/modules/portage_config.py +++ b/salt/modules/portage_config.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Configure ``portage(5)`` -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import shutil @@ -18,86 +19,102 @@ import salt.utils.stringutils # Import third party libs from salt.ext import six + # pylint: disable=import-error try: import portage + HAS_PORTAGE = True except ImportError: HAS_PORTAGE = False import sys - if os.path.isdir('/usr/lib/portage/pym'): + + if os.path.isdir("/usr/lib/portage/pym"): try: # In a virtualenv, the portage python path needs to be manually # added - sys.path.insert(0, '/usr/lib/portage/pym') + sys.path.insert(0, "/usr/lib/portage/pym") import portage + HAS_PORTAGE = True except ImportError: pass # pylint: enable=import-error -BASE_PATH = '/etc/portage/package.{0}' -SUPPORTED_CONFS = ('accept_keywords', 'env', 'license', 'mask', 'properties', - 'unmask', 'use') +BASE_PATH = "/etc/portage/package.{0}" +SUPPORTED_CONFS = ( + "accept_keywords", + "env", + "license", + "mask", + "properties", + "unmask", + "use", +) log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Confirm this module is on a Gentoo based system. - ''' - if HAS_PORTAGE and __grains__['os'] == 'Gentoo': - return 'portage_config' - return (False, 'portage_config execution module cannot be loaded: only available on Gentoo with portage installed.') + """ + if HAS_PORTAGE and __grains__["os"] == "Gentoo": + return "portage_config" + return ( + False, + "portage_config execution module cannot be loaded: only available on Gentoo with portage installed.", + ) def _get_portage(): - ''' + """ portage module must be reloaded or it can't catch the changes in portage.* which had been added after when the module was loaded - ''' + """ return salt.utils.compat.reload(portage) def _porttree(): - return portage.db[portage.root]['porttree'] + return portage.db[portage.root]["porttree"] def _get_config_file(conf, atom): - ''' + """ Parse the given atom, allowing access to its parts Success does not mean that the atom exists, just that it is in the correct format. Returns none if the atom is invalid. - ''' - if '*' in atom: + """ + if "*" in atom: parts = portage.dep.Atom(atom, allow_wildcard=True) if not parts: return - if parts.cp == '*/*': + if parts.cp == "*/*": # parts.repo will be empty if there is no repo part relative_path = parts.repo or "gentoo" - elif six.text_type(parts.cp).endswith('/*'): + elif six.text_type(parts.cp).endswith("/*"): relative_path = six.text_type(parts.cp).split("/")[0] + "_" else: - relative_path = os.path.join(*[x for x in os.path.split(parts.cp) if x != '*']) + relative_path = os.path.join( + *[x for x in os.path.split(parts.cp) if x != "*"] + ) else: relative_path = _p_to_cp(atom) if not relative_path: return - complete_file_path = BASE_PATH.format(conf) + '/' + relative_path + complete_file_path = BASE_PATH.format(conf) + "/" + relative_path return complete_file_path def _p_to_cp(p): - ''' + """ Convert a package name or a DEPEND atom to category/package format. Raises an exception if program name is ambiguous. - ''' + """ try: ret = portage.dep_getkey(p) if ret: @@ -106,7 +123,7 @@ def _p_to_cp(p): pass try: - ret = _porttree().dbapi.xmatch('bestmatch-visible', p) + ret = _porttree().dbapi.xmatch("bestmatch-visible", p) if ret: return portage.dep_getkey(ret) except portage.exception.InvalidAtom: @@ -123,20 +140,20 @@ def _p_to_cp(p): def _get_cpv(cp, installed=True): - ''' + """ add version to category/package @cp - name of package in format category/name @installed - boolean value, if False, function returns cpv for latest available package - ''' + """ if installed: - return _get_portage().db[portage.root]['vartree'].dep_bestmatch(cp) + return _get_portage().db[portage.root]["vartree"].dep_bestmatch(cp) else: return _porttree().dep_bestmatch(cp) def enforce_nice_config(): - ''' + """ Enforce a nice tree structure for /etc/portage/package.* configuration files. @@ -150,69 +167,68 @@ def enforce_nice_config(): .. code-block:: bash salt '*' portage_config.enforce_nice_config - ''' + """ _convert_all_package_confs_to_dir() _order_all_package_confs() def _convert_all_package_confs_to_dir(): - ''' + """ Convert all /etc/portage/package.* configuration files to directories. - ''' + """ for conf_file in SUPPORTED_CONFS: _package_conf_file_to_dir(conf_file) def _order_all_package_confs(): - ''' + """ Place all entries in /etc/portage/package.* config dirs in the correct file. - ''' + """ for conf_file in SUPPORTED_CONFS: _package_conf_ordering(conf_file) _unify_keywords() def _unify_keywords(): - ''' + """ Merge /etc/portage/package.keywords and /etc/portage/package.accept_keywords. - ''' - old_path = BASE_PATH.format('keywords') + """ + old_path = BASE_PATH.format("keywords") if os.path.exists(old_path): if os.path.isdir(old_path): for triplet in salt.utils.path.os_walk(old_path): for file_name in triplet[2]: - file_path = '{0}/{1}'.format(triplet[0], file_name) + file_path = "{0}/{1}".format(triplet[0], file_name) with salt.utils.files.fopen(file_path) as fh_: for line in fh_: line = salt.utils.stringutils.to_unicode(line).strip() - if line and not line.startswith('#'): - append_to_package_conf( - 'accept_keywords', string=line) + if line and not line.startswith("#"): + append_to_package_conf("accept_keywords", string=line) shutil.rmtree(old_path) else: with salt.utils.files.fopen(old_path) as fh_: for line in fh_: line = salt.utils.stringutils.to_unicode(line).strip() - if line and not line.startswith('#'): - append_to_package_conf('accept_keywords', string=line) + if line and not line.startswith("#"): + append_to_package_conf("accept_keywords", string=line) os.remove(old_path) def _package_conf_file_to_dir(file_name): - ''' + """ Convert a config file to a config directory. - ''' + """ if file_name in SUPPORTED_CONFS: path = BASE_PATH.format(file_name) if os.path.exists(path): if os.path.isdir(path): return False else: - os.rename(path, path + '.tmpbak') + os.rename(path, path + ".tmpbak") os.mkdir(path, 0o755) - os.rename(path + '.tmpbak', os.path.join(path, 'tmp')) + os.rename(path + ".tmpbak", os.path.join(path, "tmp")) return True else: os.mkdir(path, 0o755) @@ -220,9 +236,9 @@ def _package_conf_file_to_dir(file_name): def _package_conf_ordering(conf, clean=True, keep_backup=False): - ''' + """ Move entries in the correct file. - ''' + """ if conf in SUPPORTED_CONFS: rearrange = [] path = BASE_PATH.format(conf) @@ -231,19 +247,19 @@ def _package_conf_ordering(conf, clean=True, keep_backup=False): for triplet in salt.utils.path.os_walk(path): for file_name in triplet[2]: - file_path = '{0}/{1}'.format(triplet[0], file_name) - cp = triplet[0][len(path) + 1:] + '/' + file_name + file_path = "{0}/{1}".format(triplet[0], file_name) + cp = triplet[0][len(path) + 1 :] + "/" + file_name - shutil.copy(file_path, file_path + '.bak') - backup_files.append(file_path + '.bak') + shutil.copy(file_path, file_path + ".bak") + backup_files.append(file_path + ".bak") - if cp[0] == '/' or len(cp.split('/')) > 2: + if cp[0] == "/" or len(cp.split("/")) > 2: with salt.utils.files.fopen(file_path) as fp_: rearrange.extend(salt.utils.data.decode(fp_.readlines())) os.remove(file_path) else: - new_contents = '' - with salt.utils.files.fopen(file_path, 'r+') as file_handler: + new_contents = "" + with salt.utils.files.fopen(file_path, "r+") as file_handler: for line in file_handler: line = salt.utils.stringutils.to_unicode(line) try: @@ -251,8 +267,7 @@ def _package_conf_ordering(conf, clean=True, keep_backup=False): except IndexError: new_contents += line else: - if atom[0] == '#' or \ - portage.dep_getkey(atom) == cp: + if atom[0] == "#" or portage.dep_getkey(atom) == cp: new_contents += line else: rearrange.append(line.strip()) @@ -276,38 +291,35 @@ def _package_conf_ordering(conf, clean=True, keep_backup=False): if clean: for triplet in salt.utils.path.os_walk(path): - if len(triplet[1]) == 0 and len(triplet[2]) == 0 and \ - triplet[0] != path: + if len(triplet[1]) == 0 and len(triplet[2]) == 0 and triplet[0] != path: shutil.rmtree(triplet[0]) def _check_accept_keywords(approved, flag): - '''check compatibility of accept_keywords''' + """check compatibility of accept_keywords""" if flag in approved: return False - elif (flag.startswith('~') and flag[1:] in approved) \ - or ('~'+flag in approved): + elif (flag.startswith("~") and flag[1:] in approved) or ("~" + flag in approved): return False else: return True -def _merge_flags(new_flags, old_flags=None, conf='any'): - ''' +def _merge_flags(new_flags, old_flags=None, conf="any"): + """ Merges multiple lists of flags removing duplicates and resolving conflicts giving priority to lasts lists. - ''' + """ if not old_flags: old_flags = [] args = [old_flags, new_flags] - if conf == 'accept_keywords': - tmp = new_flags + \ - [i for i in old_flags if _check_accept_keywords(new_flags, i)] + if conf == "accept_keywords": + tmp = new_flags + [i for i in old_flags if _check_accept_keywords(new_flags, i)] else: tmp = portage.flatten(args) flags = {} for flag in tmp: - if flag[0] == '-': + if flag[0] == "-": flags[flag[1:]] = False else: flags[flag] = True @@ -316,16 +328,16 @@ def _merge_flags(new_flags, old_flags=None, conf='any'): if val: tmp.append(key) else: - tmp.append('-' + key) + tmp.append("-" + key) # Next sort is just aesthetic, can be commented for a small performance # boost - tmp.sort(key=lambda x: x.lstrip('-')) + tmp.sort(key=lambda x: x.lstrip("-")) return tmp -def append_to_package_conf(conf, atom='', flags=None, string='', overwrite=False): - ''' +def append_to_package_conf(conf, atom="", flags=None, string="", overwrite=False): + """ Append a string or a list of flags for a given package or DEPEND atom to a given configuration file. @@ -335,41 +347,41 @@ def append_to_package_conf(conf, atom='', flags=None, string='', overwrite=False salt '*' portage_config.append_to_package_conf use string="app-admin/salt ldap -libvirt" salt '*' portage_config.append_to_package_conf use atom="> = app-admin/salt-0.14.1" flags="['ldap', '-libvirt']" - ''' + """ if flags is None: flags = [] if conf in SUPPORTED_CONFS: if not string: - if '/' not in atom: + if "/" not in atom: atom = _p_to_cp(atom) if not atom: return - string = '{0} {1}'.format(atom, ' '.join(flags)) + string = "{0} {1}".format(atom, " ".join(flags)) new_flags = list(flags) else: atom = string.strip().split()[0] - new_flags = [flag for flag in string.strip().split(' ') if flag][1:] - if '/' not in atom: + new_flags = [flag for flag in string.strip().split(" ") if flag][1:] + if "/" not in atom: atom = _p_to_cp(atom) - string = '{0} {1}'.format(atom, ' '.join(new_flags)) + string = "{0} {1}".format(atom, " ".join(new_flags)) if not atom: return to_delete_if_empty = [] - if conf == 'accept_keywords': - if '-~ARCH' in new_flags: - new_flags.remove('-~ARCH') + if conf == "accept_keywords": + if "-~ARCH" in new_flags: + new_flags.remove("-~ARCH") to_delete_if_empty.append(atom) - if '~ARCH' in new_flags: - new_flags.remove('~ARCH') + if "~ARCH" in new_flags: + new_flags.remove("~ARCH") append_to_package_conf(conf, string=atom, overwrite=overwrite) if not new_flags: return # Next sort is just aesthetic, can be commented for a small performance # boost - new_flags.sort(key=lambda x: x.lstrip('-')) + new_flags.sort(key=lambda x: x.lstrip("-")) complete_file_path = _get_config_file(conf, atom) pdir = os.path.dirname(complete_file_path) @@ -377,34 +389,38 @@ def append_to_package_conf(conf, atom='', flags=None, string='', overwrite=False os.makedirs(pdir, 0o755) try: - shutil.copy(complete_file_path, complete_file_path + '.bak') + shutil.copy(complete_file_path, complete_file_path + ".bak") except IOError: pass try: - file_handler = salt.utils.files.fopen(complete_file_path, 'r+') # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + file_handler = salt.utils.files.fopen(complete_file_path, "r+") + # pylint: enable=resource-leakage except IOError: - file_handler = salt.utils.files.fopen(complete_file_path, 'w+') # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + file_handler = salt.utils.files.fopen(complete_file_path, "w+") + # pylint: enable=resource-leakage - new_contents = '' + new_contents = "" added = False try: for l in file_handler: l_strip = l.strip() - if l_strip == '': - new_contents += '\n' - elif l_strip[0] == '#': + if l_strip == "": + new_contents += "\n" + elif l_strip[0] == "#": new_contents += l elif l_strip.split()[0] == atom: if l_strip in to_delete_if_empty: continue if overwrite: - new_contents += string.strip() + '\n' + new_contents += string.strip() + "\n" added = True else: - old_flags = [flag for flag in l_strip.split(' ') if flag][1:] - if conf == 'accept_keywords': + old_flags = [flag for flag in l_strip.split(" ") if flag][1:] + if conf == "accept_keywords": if not old_flags: new_contents += l if not new_flags: @@ -414,17 +430,18 @@ def append_to_package_conf(conf, atom='', flags=None, string='', overwrite=False continue merged_flags = _merge_flags(new_flags, old_flags, conf) if merged_flags: - new_contents += '{0} {1}\n'.format( - atom, ' '.join(merged_flags)) + new_contents += "{0} {1}\n".format( + atom, " ".join(merged_flags) + ) else: - new_contents += '{0}\n'.format(atom) + new_contents += "{0}\n".format(atom) added = True else: new_contents += l if not added: - new_contents += string.strip() + '\n' + new_contents += string.strip() + "\n" except Exception as exc: # pylint: disable=broad-except - log.error('Failed to write to %s: %s', complete_file_path, exc) + log.error("Failed to write to %s: %s", complete_file_path, exc) else: file_handler.seek(0) file_handler.truncate(len(new_contents)) @@ -433,13 +450,13 @@ def append_to_package_conf(conf, atom='', flags=None, string='', overwrite=False file_handler.close() try: - os.remove(complete_file_path + '.bak') + os.remove(complete_file_path + ".bak") except OSError: pass def append_use_flags(atom, uses=None, overwrite=False): - ''' + """ Append a list of use flags for a given package or DEPEND atom CLI Example: @@ -448,17 +465,17 @@ def append_use_flags(atom, uses=None, overwrite=False): salt '*' portage_config.append_use_flags "app-admin/salt[ldap, -libvirt]" salt '*' portage_config.append_use_flags ">=app-admin/salt-0.14.1" "['ldap', '-libvirt']" - ''' + """ if not uses: uses = portage.dep.dep_getusedeps(atom) if len(uses) == 0: return - atom = atom[:atom.rfind('[')] - append_to_package_conf('use', atom=atom, flags=uses, overwrite=overwrite) + atom = atom[: atom.rfind("[")] + append_to_package_conf("use", atom=atom, flags=uses, overwrite=overwrite) def get_flags_from_package_conf(conf, atom): - ''' + """ Get flags for a given package or DEPEND atom. Warning: This only works if the configuration files tree is in the correct format (the one enforced by enforce_nice_config) @@ -468,13 +485,13 @@ def get_flags_from_package_conf(conf, atom): .. code-block:: bash salt '*' portage_config.get_flags_from_package_conf license salt - ''' + """ if conf in SUPPORTED_CONFS: package_file = _get_config_file(conf, atom) - if '/' not in atom: + if "/" not in atom: atom = _p_to_cp(atom) - has_wildcard = '*' in atom + has_wildcard = "*" in atom if has_wildcard: match_list = set(atom) else: @@ -497,11 +514,11 @@ def get_flags_from_package_conf(conf, atom): found_match = match_list.issubset(line_list) if found_match: - f_tmp = [flag for flag in line.strip().split(' ') if flag][1:] + f_tmp = [flag for flag in line.strip().split(" ") if flag][1:] if f_tmp: flags.extend(f_tmp) else: - flags.append('~ARCH') + flags.append("~ARCH") return _merge_flags(flags) except IOError: @@ -509,7 +526,7 @@ def get_flags_from_package_conf(conf, atom): def has_flag(conf, atom, flag): - ''' + """ Verify if the given package or DEPEND atom has the given flag. Warning: This only works if the configuration files tree is in the correct format (the one enforced by enforce_nice_config) @@ -519,21 +536,21 @@ def has_flag(conf, atom, flag): .. code-block:: bash salt '*' portage_config.has_flag license salt Apache-2.0 - ''' + """ if flag in get_flags_from_package_conf(conf, atom): return True return False def get_missing_flags(conf, atom, flags): - ''' + """ Find out which of the given flags are currently not set. CLI Example: .. code-block:: bash salt '*' portage_config.get_missing_flags use salt "['ldap', '-libvirt', 'openssl']" - ''' + """ new_flags = [] for flag in flags: if not has_flag(conf, atom, flag): @@ -542,7 +559,7 @@ def get_missing_flags(conf, atom, flags): def has_use(atom, use): - ''' + """ Verify if the given package or DEPEND atom has the given use flag. Warning: This only works if the configuration files tree is in the correct format (the one enforced by enforce_nice_config) @@ -552,12 +569,12 @@ def has_use(atom, use): .. code-block:: bash salt '*' portage_config.has_use salt libvirt - ''' - return has_flag('use', atom, use) + """ + return has_flag("use", atom, use) def is_present(conf, atom): - ''' + """ Tell if a given package or DEPEND atom is present in the configuration files tree. Warning: This only works if the configuration files tree is in the correct @@ -568,11 +585,11 @@ def is_present(conf, atom): .. code-block:: bash salt '*' portage_config.is_present unmask salt - ''' + """ if conf in SUPPORTED_CONFS: if not isinstance(atom, portage.dep.Atom): atom = portage.dep.Atom(atom, allow_wildcard=True) - has_wildcard = '*' in atom + has_wildcard = "*" in atom package_file = _get_config_file(conf, six.text_type(atom)) @@ -601,7 +618,7 @@ def is_present(conf, atom): def get_iuse(cp): - ''' + """ .. versionadded:: 2015.8.0 Gets the current IUSE flags from the tree. @@ -610,7 +627,7 @@ def get_iuse(cp): @param cpv: cat/pkg @rtype list @returns [] or the list of IUSE flags - ''' + """ cpv = _get_cpv(cp) try: # aux_get might return dupes, so run them through set() to remove them @@ -621,7 +638,7 @@ def get_iuse(cp): def get_installed_use(cp, use="USE"): - ''' + """ .. versionadded:: 2015.8.0 Gets the installed USE flags from the VARDB. @@ -632,14 +649,14 @@ def get_installed_use(cp, use="USE"): @param use: 1 of ["USE", "PKGUSE"] @rtype list @returns [] or the list of IUSE flags - ''' + """ portage = _get_portage() cpv = _get_cpv(cp) return portage.db[portage.root]["vartree"].dbapi.aux_get(cpv, [use])[0].split() def filter_flags(use, use_expand_hidden, usemasked, useforced): - ''' + """ .. versionadded:: 2015.8.0 Filter function to remove hidden or otherwise not normally @@ -655,12 +672,12 @@ def filter_flags(use, use_expand_hidden, usemasked, useforced): @param useforced: the forced USE flags. @rtype: list @return the filtered USE flags. - ''' + """ portage = _get_portage() # clean out some environment flags, since they will most probably # be confusing for the user for f in use_expand_hidden: - f = f.lower()+ "_" + f = f.lower() + "_" for x in use: if f in x: use.remove(x) @@ -678,7 +695,7 @@ def filter_flags(use, use_expand_hidden, usemasked, useforced): def get_all_cpv_use(cp): - ''' + """ .. versionadded:: 2015.8.0 Uses portage to determine final USE flags and settings for an emerge. @@ -687,14 +704,14 @@ def get_all_cpv_use(cp): @param cp: eg cat/pkg @rtype: lists @return use, use_expand_hidden, usemask, useforce - ''' + """ cpv = _get_cpv(cp) portage = _get_portage() use = None _porttree().dbapi.settings.unlock() try: _porttree().dbapi.settings.setcpv(cpv, mydb=portage.portdb) - use = portage.settings['PORTAGE_USE'].split() + use = portage.settings["PORTAGE_USE"].split() use_expand_hidden = portage.settings["USE_EXPAND_HIDDEN"].split() usemask = list(_porttree().dbapi.settings.usemask) useforce = list(_porttree().dbapi.settings.useforce) @@ -709,7 +726,7 @@ def get_all_cpv_use(cp): def get_cleared_flags(cp): - ''' + """ .. versionadded:: 2015.8.0 Uses portage for compare use flags which is used for installing package @@ -720,18 +737,18 @@ def get_cleared_flags(cp): @rtype: tuple @rparam: tuple with two lists - list of used flags and list of flags which will be used - ''' + """ cpv = _get_cpv(cp) final_use, use_expand_hidden, usemasked, useforced = get_all_cpv_use(cpv) - inst_flags = filter_flags(get_installed_use(cpv), use_expand_hidden, - usemasked, useforced) - final_flags = filter_flags(final_use, use_expand_hidden, - usemasked, useforced) + inst_flags = filter_flags( + get_installed_use(cpv), use_expand_hidden, usemasked, useforced + ) + final_flags = filter_flags(final_use, use_expand_hidden, usemasked, useforced) return inst_flags, final_flags def is_changed_uses(cp): - ''' + """ .. versionadded:: 2015.8.0 Uses portage for determine if the use flags of installed package @@ -739,7 +756,7 @@ def is_changed_uses(cp): @type cp: string @param cp: eg cat/pkg - ''' + """ cpv = _get_cpv(cp) i_flags, conf_flags = get_cleared_flags(cpv) for i in i_flags: diff --git a/salt/modules/postfix.py b/salt/modules/postfix.py index 2557d7b70e8..33ddc7aeb7c 100644 --- a/salt/modules/postfix.py +++ b/salt/modules/postfix.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for Postfix This module is currently little more than a config file viewer and editor. It @@ -10,12 +10,13 @@ configuration files). The design of this module is such that when files are edited, a minimum of changes are made to them. Each file should look as if it has been edited by hand; order, comments and whitespace are all preserved. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import re -import logging # Import salt libs import salt.utils.files @@ -25,25 +26,25 @@ import salt.utils.stringutils # Import 3rd-party libs from salt.ext import six -SWWS = re.compile(r'^\s') +SWWS = re.compile(r"^\s") log = logging.getLogger(__name__) -MAIN_CF = '/etc/postfix/main.cf' -MASTER_CF = '/etc/postfix/master.cf' +MAIN_CF = "/etc/postfix/main.cf" +MASTER_CF = "/etc/postfix/master.cf" def __virtual__(): - ''' + """ Only load the module if Postfix is installed - ''' - if salt.utils.path.which('postfix'): + """ + if salt.utils.path.which("postfix"): return True - return (False, 'postfix execution module not loaded: postfix not installed.') + return (False, "postfix execution module not loaded: postfix not installed.") def _parse_master(path=MASTER_CF): - ''' + """ Parse the master.cf file. This file is essentially a whitespace-delimited columnar file. The columns are: service, type, private (yes), unpriv (yes), chroot (yes), wakeup (never), maxproc (100), command + args. @@ -54,8 +55,8 @@ def _parse_master(path=MASTER_CF): Returns a dict of the active config lines, and a list of the entire file, in order. These compliment each other. - ''' - with salt.utils.files.fopen(path, 'r') as fh_: + """ + with salt.utils.files.fopen(path, "r") as fh_: full_conf = salt.utils.stringutils.to_unicode(fh_.read()) # Condense the file based on line continuations, but keep order, comments @@ -63,21 +64,21 @@ def _parse_master(path=MASTER_CF): conf_list = [] conf_dict = {} for line in full_conf.splitlines(): - if not line.strip() or line.strip().startswith('#'): + if not line.strip() or line.strip().startswith("#"): conf_list.append(line) continue comps = line.strip().split() conf_line = { - 'service': comps[0], - 'conn_type': comps[1], - 'private': comps[2], - 'unpriv': comps[3], - 'chroot': comps[4], - 'wakeup': comps[5], - 'maxproc': comps[6], - 'command': ' '.join(comps[7:]), + "service": comps[0], + "conn_type": comps[1], + "private": comps[2], + "unpriv": comps[3], + "chroot": comps[4], + "wakeup": comps[5], + "maxproc": comps[6], + "command": " ".join(comps[7:]), } - dict_key = '{0} {1}'.format(comps[0], comps[1]) + dict_key = "{0} {1}".format(comps[0], comps[1]) conf_list.append(conf_line) conf_dict[dict_key] = conf_line @@ -85,7 +86,7 @@ def _parse_master(path=MASTER_CF): def show_master(path=MASTER_CF): - ''' + """ Return a dict of active config values. This does not include comments, spacing or order. @@ -96,22 +97,24 @@ def show_master(path=MASTER_CF): salt postfix.show_master salt postfix.show_master path=/path/to/master.cf - ''' + """ conf_dict, conf_list = _parse_master(path) # pylint: disable=W0612 return conf_dict -def set_master(service, - conn_type, - private='y', - unpriv='y', - chroot='y', - wakeup='n', - maxproc='100', - command='', - write_conf=True, - path=MASTER_CF): - ''' +def set_master( + service, + conn_type, + private="y", + unpriv="y", + chroot="y", + wakeup="n", + maxproc="100", + command="", + write_conf=True, + path=MASTER_CF, +): + """ Set a single config value in the master.cf file. If the value does not already exist, it will be appended to the end. @@ -132,24 +135,17 @@ def set_master(service, CLI Example: salt postfix.set_master smtp inet n y n n 100 smtpd - ''' + """ conf_dict, conf_list = _parse_master(path) new_conf = [] - dict_key = '{0} {1}'.format(service, conn_type) + dict_key = "{0} {1}".format(service, conn_type) new_line = _format_master( - service, - conn_type, - private, - unpriv, - chroot, - wakeup, - maxproc, - command, + service, conn_type, private, unpriv, chroot, wakeup, maxproc, command, ) for line in conf_list: if isinstance(line, dict): - if line['service'] == service and line['conn_type'] == conn_type: + if line["service"] == service and line["conn_type"] == conn_type: # This is the one line that we're changing new_conf.append(new_line) else: @@ -166,58 +162,46 @@ def set_master(service, if write_conf: _write_conf(new_conf, path) - return '\n'.join(new_conf) + return "\n".join(new_conf) -def _format_master(service, - conn_type, - private, - unpriv, - chroot, - wakeup, - maxproc, - command): - ''' +def _format_master( + service, conn_type, private, unpriv, chroot, wakeup, maxproc, command +): + """ Format the given values into the style of line normally used in the master.cf file. - ''' - #========================================================================== - #service type private unpriv chroot wakeup maxproc command + args + """ + # ========================================================================== + # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) - #========================================================================== - #smtp inet n - n - - smtpd - if private == 'y': - private = '-' + # ========================================================================== + # smtp inet n - n - - smtpd + if private == "y": + private = "-" - if unpriv == 'y': - unpriv = '-' + if unpriv == "y": + unpriv = "-" - if chroot == 'y': - chroot = '-' + if chroot == "y": + chroot = "-" - if wakeup == 'n': - wakeup = '-' + if wakeup == "n": + wakeup = "-" maxproc = six.text_type(maxproc) - if maxproc == '100': - maxproc = '-' + if maxproc == "100": + maxproc = "-" - conf_line = '{0:9s} {1:5s} {2:7s} {3:7s} {4:7s} {5:7s} {6:7s} {7}'.format( - service, - conn_type, - private, - unpriv, - chroot, - wakeup, - maxproc, - command, + conf_line = "{0:9s} {1:5s} {2:7s} {3:7s} {4:7s} {5:7s} {6:7s} {7}".format( + service, conn_type, private, unpriv, chroot, wakeup, maxproc, command, ) - #print(conf_line) + # print(conf_line) return conf_line def _parse_main(path=MAIN_CF): - ''' + """ Parse files in the style of main.cf. This is not just a "name = value" file; there are other rules: @@ -227,8 +211,8 @@ def _parse_main(path=MAIN_CF): * The whitespace rule applies to comments. * Keys defined in the file may be referred to as variables further down in the file. - ''' - with salt.utils.files.fopen(path, 'r') as fh_: + """ + with salt.utils.files.fopen(path, "r") as fh_: full_conf = salt.utils.stringutils.to_unicode(fh_.read()) # Condense the file based on line continuations, but keep order, comments @@ -244,9 +228,9 @@ def _parse_main(path=MAIN_CF): conf_list.append(line) continue if not isinstance(conf_list[-1], six.string_types): - conf_list[-1] = '' + conf_list[-1] = "" # This line is a continuation of the previous line - conf_list[-1] = '\n'.join([conf_list[-1], line]) + conf_list[-1] = "\n".join([conf_list[-1], line]) else: conf_list.append(line) @@ -255,17 +239,17 @@ def _parse_main(path=MAIN_CF): for line in conf_list: if not line.strip(): continue - if line.startswith('#'): + if line.startswith("#"): continue - comps = line.split('=') - pairs[comps[0].strip()] = '='.join(comps[1:]).strip() + comps = line.split("=") + pairs[comps[0].strip()] = "=".join(comps[1:]).strip() # Return both sets of data, they compliment each other elsewhere return pairs, conf_list def show_main(path=MAIN_CF): - ''' + """ Return a dict of active config values. This does not include comments, spacing or order. Bear in mind that order is functionally important in the main.cf file, since keys can be referred to as variables. This means that @@ -276,20 +260,20 @@ def show_main(path=MAIN_CF): salt postfix.show_main salt postfix.show_main path=/path/to/main.cf - ''' + """ pairs, conf_list = _parse_main(path) # pylint: disable=W0612 return pairs def set_main(key, value, path=MAIN_CF): - ''' + """ Set a single config value in the main.cf file. If the value does not already exist, it will be appended to the end. CLI Example: salt postfix.set_main mailq_path /usr/bin/mailq - ''' + """ pairs, conf_list = _parse_main(path) new_conf = [] @@ -297,11 +281,11 @@ def set_main(key, value, path=MAIN_CF): if key in pairs: for line in conf_list: if re.match(key_line_match, line): - new_conf.append('{0} = {1}'.format(key, value)) + new_conf.append("{0} = {1}".format(key, value)) else: new_conf.append(line) else: - conf_list.append('{0} = {1}'.format(key, value)) + conf_list.append("{0} = {1}".format(key, value)) new_conf = conf_list _write_conf(new_conf, path) @@ -309,21 +293,21 @@ def set_main(key, value, path=MAIN_CF): def _write_conf(conf, path=MAIN_CF): - ''' + """ Write out configuration file. - ''' - with salt.utils.files.fopen(path, 'w') as fh_: + """ + with salt.utils.files.fopen(path, "w") as fh_: for line in conf: line = salt.utils.stringutils.to_str(line) if isinstance(line, dict): - fh_.write(' '.join(line)) + fh_.write(" ".join(line)) else: fh_.write(line) - fh_.write('\n') + fh_.write("\n") def show_queue(): - ''' + """ Show contents of the mail queue CLI Example: @@ -332,33 +316,43 @@ def show_queue(): salt '*' postfix.show_queue - ''' - cmd = 'mailq' - out = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = "mailq" + out = __salt__["cmd.run"](cmd).splitlines() queue = [] - queue_pattern = re.compile(r"(?P^[A-Z0-9]+)\s+(?P\d+)\s(?P\w{3}\s\w{3}\s\d{1,2}\s\d{2}\:\d{2}\:\d{2})\s+(?P.+)") + queue_pattern = re.compile( + r"(?P^[A-Z0-9]+)\s+(?P\d+)\s(?P\w{3}\s\w{3}\s\d{1,2}\s\d{2}\:\d{2}\:\d{2})\s+(?P.+)" + ) recipient_pattern = re.compile(r"^\s+(?P.+)") for line in out: - if re.match('^[-|postqueue:|Mail]', line): + if re.match("^[-|postqueue:|Mail]", line): # discard in-queue wrapper continue if re.match(queue_pattern, line): m = re.match(queue_pattern, line) - queue_id = m.group('queue_id') - size = m.group('size') - timestamp = m.group('timestamp') - sender = m.group('sender') + queue_id = m.group("queue_id") + size = m.group("size") + timestamp = m.group("timestamp") + sender = m.group("sender") elif re.match(recipient_pattern, line): # recipient/s m = re.match(recipient_pattern, line) - recipient = m.group('recipient') + recipient = m.group("recipient") elif not line: # end of record - queue.append({'queue_id': queue_id, 'size': size, 'timestamp': timestamp, 'sender': sender, 'recipient': recipient}) + queue.append( + { + "queue_id": queue_id, + "size": size, + "timestamp": timestamp, + "sender": sender, + "recipient": recipient, + } + ) return queue def delete(queue_id): - ''' + """ Delete message(s) from the mail queue CLI Example: @@ -369,45 +363,47 @@ def delete(queue_id): salt '*' postfix.delete ALL - ''' + """ - ret = {'message': '', - 'result': True - } + ret = {"message": "", "result": True} if not queue_id: - log.error('Require argument queue_id') + log.error("Require argument queue_id") - if not queue_id == 'ALL': + if not queue_id == "ALL": queue = show_queue() _message = None for item in queue: - if item['queue_id'] == queue_id: + if item["queue_id"] == queue_id: _message = item if not _message: - ret['message'] = 'No message in queue with ID {0}'.format(queue_id) - ret['result'] = False + ret["message"] = "No message in queue with ID {0}".format(queue_id) + ret["result"] = False return ret - cmd = 'postsuper -d {0}'.format(queue_id) - result = __salt__['cmd.run_all'](cmd) + cmd = "postsuper -d {0}".format(queue_id) + result = __salt__["cmd.run_all"](cmd) - if result['retcode'] == 0: - if queue_id == 'ALL': - ret['message'] = 'Successfully removed all messages' + if result["retcode"] == 0: + if queue_id == "ALL": + ret["message"] = "Successfully removed all messages" else: - ret['message'] = 'Successfully removed message with queue id {0}'.format(queue_id) + ret["message"] = "Successfully removed message with queue id {0}".format( + queue_id + ) else: - if queue_id == 'ALL': - ret['message'] = 'Unable to removed all messages' + if queue_id == "ALL": + ret["message"] = "Unable to removed all messages" else: - ret['message'] = 'Unable to remove message with queue id {0}: {1}'.format(queue_id, result['stderr']) + ret["message"] = "Unable to remove message with queue id {0}: {1}".format( + queue_id, result["stderr"] + ) return ret def hold(queue_id): - ''' + """ Put message(s) on hold from the mail queue CLI Example: @@ -418,45 +414,49 @@ def hold(queue_id): salt '*' postfix.hold ALL - ''' + """ - ret = {'message': '', - 'result': True - } + ret = {"message": "", "result": True} if not queue_id: - log.error('Require argument queue_id') + log.error("Require argument queue_id") - if not queue_id == 'ALL': + if not queue_id == "ALL": queue = show_queue() _message = None for item in queue: - if item['queue_id'] == queue_id: + if item["queue_id"] == queue_id: _message = item if not _message: - ret['message'] = 'No message in queue with ID {0}'.format(queue_id) - ret['result'] = False + ret["message"] = "No message in queue with ID {0}".format(queue_id) + ret["result"] = False return ret - cmd = 'postsuper -h {0}'.format(queue_id) - result = __salt__['cmd.run_all'](cmd) + cmd = "postsuper -h {0}".format(queue_id) + result = __salt__["cmd.run_all"](cmd) - if result['retcode'] == 0: - if queue_id == 'ALL': - ret['message'] = 'Successfully placed all messages on hold' + if result["retcode"] == 0: + if queue_id == "ALL": + ret["message"] = "Successfully placed all messages on hold" else: - ret['message'] = 'Successfully placed message on hold with queue id {0}'.format(queue_id) + ret[ + "message" + ] = "Successfully placed message on hold with queue id {0}".format(queue_id) else: - if queue_id == 'ALL': - ret['message'] = 'Unable to place all messages on hold' + if queue_id == "ALL": + ret["message"] = "Unable to place all messages on hold" else: - ret['message'] = 'Unable to place message on hold with queue id {0}: {1}'.format(queue_id, result['stderr']) + ret[ + "message" + ] = "Unable to place message on hold with queue id {0}: {1}".format( + queue_id, result["stderr"] + ) return ret def unhold(queue_id): - ''' + """ Set held message(s) in the mail queue to unheld CLI Example: @@ -467,45 +467,49 @@ def unhold(queue_id): salt '*' postfix.unhold ALL - ''' + """ - ret = {'message': '', - 'result': True - } + ret = {"message": "", "result": True} if not queue_id: - log.error('Require argument queue_id') + log.error("Require argument queue_id") - if not queue_id == 'ALL': + if not queue_id == "ALL": queue = show_queue() _message = None for item in queue: - if item['queue_id'] == queue_id: + if item["queue_id"] == queue_id: _message = item if not _message: - ret['message'] = 'No message in queue with ID {0}'.format(queue_id) - ret['result'] = False + ret["message"] = "No message in queue with ID {0}".format(queue_id) + ret["result"] = False return ret - cmd = 'postsuper -H {0}'.format(queue_id) - result = __salt__['cmd.run_all'](cmd) + cmd = "postsuper -H {0}".format(queue_id) + result = __salt__["cmd.run_all"](cmd) - if result['retcode'] == 0: - if queue_id == 'ALL': - ret['message'] = 'Successfully set all message as unheld' + if result["retcode"] == 0: + if queue_id == "ALL": + ret["message"] = "Successfully set all message as unheld" else: - ret['message'] = 'Successfully set message as unheld with queue id {0}'.format(queue_id) + ret[ + "message" + ] = "Successfully set message as unheld with queue id {0}".format(queue_id) else: - if queue_id == 'ALL': - ret['message'] = 'Unable to set all message as unheld.' + if queue_id == "ALL": + ret["message"] = "Unable to set all message as unheld." else: - ret['message'] = 'Unable to set message as unheld with queue id {0}: {1}'.format(queue_id, result['stderr']) + ret[ + "message" + ] = "Unable to set message as unheld with queue id {0}: {1}".format( + queue_id, result["stderr"] + ) return ret def requeue(queue_id): - ''' + """ Requeue message(s) in the mail queue CLI Example: @@ -516,38 +520,40 @@ def requeue(queue_id): salt '*' postfix.requeue ALL - ''' + """ - ret = {'message': '', - 'result': True - } + ret = {"message": "", "result": True} if not queue_id: - log.error('Required argument queue_id') + log.error("Required argument queue_id") - if not queue_id == 'ALL': + if not queue_id == "ALL": queue = show_queue() _message = None for item in queue: - if item['queue_id'] == queue_id: + if item["queue_id"] == queue_id: _message = item if not _message: - ret['message'] = 'No message in queue with ID {0}'.format(queue_id) - ret['result'] = False + ret["message"] = "No message in queue with ID {0}".format(queue_id) + ret["result"] = False return ret - cmd = 'postsuper -r {0}'.format(queue_id) - result = __salt__['cmd.run_all'](cmd) + cmd = "postsuper -r {0}".format(queue_id) + result = __salt__["cmd.run_all"](cmd) - if result['retcode'] == 0: - if queue_id == 'ALL': - ret['message'] = 'Successfully requeued all messages' + if result["retcode"] == 0: + if queue_id == "ALL": + ret["message"] = "Successfully requeued all messages" else: - ret['message'] = 'Successfully requeued message with queue id {0}'.format(queue_id) + ret["message"] = "Successfully requeued message with queue id {0}".format( + queue_id + ) else: - if queue_id == 'ALL': - ret['message'] = 'Unable to requeue all messages' + if queue_id == "ALL": + ret["message"] = "Unable to requeue all messages" else: - ret['message'] = 'Unable to requeue message with queue id {0}: {1}'.format(queue_id, result['stderr']) + ret["message"] = "Unable to requeue message with queue id {0}: {1}".format( + queue_id, result["stderr"] + ) return ret diff --git a/salt/modules/postgres.py b/salt/modules/postgres.py index f0d1b034b94..2867cb14186 100644 --- a/salt/modules/postgres.py +++ b/salt/modules/postgres.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide Postgres compatibility to salt. :configuration: In order to connect to Postgres, certain configuration is @@ -26,25 +26,21 @@ Module to provide Postgres compatibility to salt. of the postgres bin's path to the relevant minion for this module:: postgres.bins_dir: '/usr/pgsql-9.5/bin/' -''' +""" # This pylint error is popping up where there are no colons? # pylint: disable=E8203 # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import datetime -import logging import hashlib +import logging import os -import re import pipes +import re import tempfile -try: - import csv - HAS_CSV = True -except ImportError: - HAS_CSV = False # Import salt libs import salt.utils.files @@ -53,22 +49,29 @@ import salt.utils.odict import salt.utils.path import salt.utils.stringutils from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.utils.versions import LooseVersion as _LooseVersion # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import zip # pylint: disable=import-error,redefined-builtin from salt.ext.six.moves import StringIO +from salt.utils.versions import LooseVersion as _LooseVersion + +try: + import csv + + HAS_CSV = True +except ImportError: + HAS_CSV = False log = logging.getLogger(__name__) _DEFAULT_PASSWORDS_ENCRYPTION = True -_EXTENSION_NOT_INSTALLED = 'EXTENSION NOT INSTALLED' -_EXTENSION_INSTALLED = 'EXTENSION INSTALLED' -_EXTENSION_TO_UPGRADE = 'EXTENSION TO UPGRADE' -_EXTENSION_TO_MOVE = 'EXTENSION TO MOVE' +_EXTENSION_NOT_INSTALLED = "EXTENSION NOT INSTALLED" +_EXTENSION_INSTALLED = "EXTENSION INSTALLED" +_EXTENSION_TO_UPGRADE = "EXTENSION TO UPGRADE" +_EXTENSION_TO_MOVE = "EXTENSION TO MOVE" _EXTENSION_FLAGS = ( _EXTENSION_NOT_INSTALLED, _EXTENSION_INSTALLED, @@ -76,65 +79,65 @@ _EXTENSION_FLAGS = ( _EXTENSION_TO_MOVE, ) _PRIVILEGES_MAP = { - 'a': 'INSERT', - 'C': 'CREATE', - 'D': 'TRUNCATE', - 'c': 'CONNECT', - 't': 'TRIGGER', - 'r': 'SELECT', - 'U': 'USAGE', - 'T': 'TEMPORARY', - 'w': 'UPDATE', - 'X': 'EXECUTE', - 'x': 'REFERENCES', - 'd': 'DELETE', - '*': 'GRANT', + "a": "INSERT", + "C": "CREATE", + "D": "TRUNCATE", + "c": "CONNECT", + "t": "TRIGGER", + "r": "SELECT", + "U": "USAGE", + "T": "TEMPORARY", + "w": "UPDATE", + "X": "EXECUTE", + "x": "REFERENCES", + "d": "DELETE", + "*": "GRANT", } _PRIVILEGES_OBJECTS = frozenset( ( - 'schema', - 'tablespace', - 'language', - 'sequence', - 'table', - 'group', - 'database', - 'function', + "schema", + "tablespace", + "language", + "sequence", + "table", + "group", + "database", + "function", ) ) _PRIVILEGE_TYPE_MAP = { - 'table': 'arwdDxt', - 'tablespace': 'C', - 'language': 'U', - 'sequence': 'rwU', - 'schema': 'UC', - 'database': 'CTc', - 'function': 'X', + "table": "arwdDxt", + "tablespace": "C", + "language": "U", + "sequence": "rwU", + "schema": "UC", + "database": "CTc", + "function": "X", } def __virtual__(): - ''' + """ Only load this module if the psql bin exist. initdb bin might also be used, but its presence will be detected on runtime. - ''' - utils = ['psql'] + """ + utils = ["psql"] if not HAS_CSV: return False for util in utils: if not salt.utils.path.which(util): if not _find_pg_binary(util): - return (False, '{0} was not found'.format(util)) + return (False, "{0} was not found".format(util)) return True def _find_pg_binary(util): - ''' + """ ... versionadded:: 2016.3.2 Helper function to locate various psql related binaries - ''' - pg_bin_dir = __salt__['config.option']('postgres.bins_dir') + """ + pg_bin_dir = __salt__["config.option"]("postgres.bins_dir") util_bin = salt.utils.path.which(util) if not util_bin: if pg_bin_dir: @@ -144,125 +147,130 @@ def _find_pg_binary(util): def _run_psql(cmd, runas=None, password=None, host=None, port=None, user=None): - ''' + """ Helper function to call psql, because the password requirement makes this too much code to be repeated in each function below - ''' + """ kwargs = { - 'reset_system_locale': False, - 'clean_env': True, + "reset_system_locale": False, + "clean_env": True, } if runas is None: if not host: - host = __salt__['config.option']('postgres.host') - if not host or host.startswith('/'): - if 'FreeBSD' in __grains__['os_family']: - runas = 'pgsql' - elif 'OpenBSD' in __grains__['os_family']: - runas = '_postgresql' + host = __salt__["config.option"]("postgres.host") + if not host or host.startswith("/"): + if "FreeBSD" in __grains__["os_family"]: + runas = "pgsql" + elif "OpenBSD" in __grains__["os_family"]: + runas = "_postgresql" else: - runas = 'postgres' + runas = "postgres" if user is None: user = runas if runas: - kwargs['runas'] = runas + kwargs["runas"] = runas if password is None: - password = __salt__['config.option']('postgres.pass') + password = __salt__["config.option"]("postgres.pass") if password is not None: pgpassfile = salt.utils.files.mkstemp(text=True) - with salt.utils.files.fopen(pgpassfile, 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str('{0}:{1}:*:{2}:{3}'.format( - 'localhost' if not host or host.startswith('/') else host, - port if port else '*', - user if user else '*', - password, - ))) - __salt__['file.chown'](pgpassfile, runas, '') - kwargs['env'] = {'PGPASSFILE': pgpassfile} + with salt.utils.files.fopen(pgpassfile, "w") as fp_: + fp_.write( + salt.utils.stringutils.to_str( + "{0}:{1}:*:{2}:{3}".format( + "localhost" if not host or host.startswith("/") else host, + port if port else "*", + user if user else "*", + password, + ) + ) + ) + __salt__["file.chown"](pgpassfile, runas, "") + kwargs["env"] = {"PGPASSFILE": pgpassfile} - ret = __salt__['cmd.run_all'](cmd, python_shell=False, **kwargs) + ret = __salt__["cmd.run_all"](cmd, python_shell=False, **kwargs) - if ret.get('retcode', 0) != 0: - log.error('Error connecting to Postgresql server') - if password is not None and not __salt__['file.remove'](pgpassfile): - log.warning('Remove PGPASSFILE failed') + if ret.get("retcode", 0) != 0: + log.error("Error connecting to Postgresql server") + if password is not None and not __salt__["file.remove"](pgpassfile): + log.warning("Remove PGPASSFILE failed") return ret -def _run_initdb(name, - auth='password', - user=None, - password=None, - encoding='UTF8', - locale=None, - runas=None, - waldir=None, - checksums=False): - ''' +def _run_initdb( + name, + auth="password", + user=None, + password=None, + encoding="UTF8", + locale=None, + runas=None, + waldir=None, + checksums=False, +): + """ Helper function to call initdb - ''' + """ if runas is None: - if 'FreeBSD' in __grains__['os_family']: - runas = 'pgsql' - elif 'OpenBSD' in __grains__['os_family']: - runas = '_postgresql' + if "FreeBSD" in __grains__["os_family"]: + runas = "pgsql" + elif "OpenBSD" in __grains__["os_family"]: + runas = "_postgresql" else: - runas = 'postgres' + runas = "postgres" if user is None: user = runas - _INITDB_BIN = _find_pg_binary('initdb') + _INITDB_BIN = _find_pg_binary("initdb") if not _INITDB_BIN: - raise CommandExecutionError('initdb executable not found.') + raise CommandExecutionError("initdb executable not found.") cmd = [ _INITDB_BIN, - '--pgdata={0}'.format(name), - '--username={0}'.format(user), - '--auth={0}'.format(auth), - '--encoding={0}'.format(encoding), - ] + "--pgdata={0}".format(name), + "--username={0}".format(user), + "--auth={0}".format(auth), + "--encoding={0}".format(encoding), + ] if locale is not None: - cmd.append('--locale={0}'.format(locale)) + cmd.append("--locale={0}".format(locale)) # intentionally use short option, as the long option name has been # renamed from "xlogdir" to "waldir" in PostgreSQL 10 if waldir is not None: - cmd.append('-X') + cmd.append("-X") cmd.append(waldir) if checksums: - cmd.append('--data-checksums') + cmd.append("--data-checksums") if password is not None: pgpassfile = salt.utils.files.mkstemp(text=True) - with salt.utils.files.fopen(pgpassfile, 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str('{0}'.format(password))) - __salt__['file.chown'](pgpassfile, runas, '') - cmd.extend([ - '--pwfile={0}'.format(pgpassfile), - ]) + with salt.utils.files.fopen(pgpassfile, "w") as fp_: + fp_.write(salt.utils.stringutils.to_str("{0}".format(password))) + __salt__["file.chown"](pgpassfile, runas, "") + cmd.extend(["--pwfile={0}".format(pgpassfile)]) kwargs = dict(runas=runas, clean_env=True) - cmdstr = ' '.join([pipes.quote(c) for c in cmd]) - ret = __salt__['cmd.run_all'](cmdstr, python_shell=False, **kwargs) + cmdstr = " ".join([pipes.quote(c) for c in cmd]) + ret = __salt__["cmd.run_all"](cmdstr, python_shell=False, **kwargs) - if ret.get('retcode', 0) != 0: - log.error('Error initilizing the postgres data directory') + if ret.get("retcode", 0) != 0: + log.error("Error initilizing the postgres data directory") - if password is not None and not __salt__['file.remove'](pgpassfile): - log.warning('Removal of PGPASSFILE failed') + if password is not None and not __salt__["file.remove"](pgpassfile): + log.warning("Removal of PGPASSFILE failed") return ret -def version(user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None): - ''' +def version( + user=None, host=None, port=None, maintenance_db=None, password=None, runas=None +): + """ Return the version of a Postgres server. CLI Example: @@ -270,31 +278,37 @@ def version(user=None, host=None, port=None, maintenance_db=None, .. code-block:: bash salt '*' postgres.version - ''' - query = 'SELECT setting FROM pg_catalog.pg_settings ' \ - 'WHERE name = \'server_version\'' - cmd = _psql_cmd('-c', query, - '-t', - host=host, - user=user, - port=port, - maintenance_db=maintenance_db, - password=password) + """ + query = ( + "SELECT setting FROM pg_catalog.pg_settings " "WHERE name = 'server_version'" + ) + cmd = _psql_cmd( + "-c", + query, + "-t", + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) ret = _run_psql( - cmd, runas=runas, password=password, host=host, port=port, user=user) + cmd, runas=runas, password=password, host=host, port=port, user=user + ) - for line in salt.utils.itertools.split(ret['stdout'], '\n'): + for line in salt.utils.itertools.split(ret["stdout"], "\n"): # Just return the first line return line -def _parsed_version(user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None): - ''' +def _parsed_version( + user=None, host=None, port=None, maintenance_db=None, password=None, runas=None +): + """ Returns the server version properly parsed and int casted for internal use. If the Postgres server does not respond, None will be returned. - ''' + """ psql_version = version( user, @@ -308,79 +322,88 @@ def _parsed_version(user=None, host=None, port=None, maintenance_db=None, if psql_version: return _LooseVersion(psql_version) else: - log.warning('Attempt to parse version of Postgres server failed. ' - 'Is the server responding?') + log.warning( + "Attempt to parse version of Postgres server failed. " + "Is the server responding?" + ) return None def _connection_defaults(user=None, host=None, port=None, maintenance_db=None): - ''' + """ Returns a tuple of (user, host, port, db) with config, pillar, or default values assigned to missing values. - ''' + """ if not user: - user = __salt__['config.option']('postgres.user') + user = __salt__["config.option"]("postgres.user") if not host: - host = __salt__['config.option']('postgres.host') + host = __salt__["config.option"]("postgres.host") if not port: - port = __salt__['config.option']('postgres.port') + port = __salt__["config.option"]("postgres.port") if not maintenance_db: - maintenance_db = __salt__['config.option']('postgres.maintenance_db') + maintenance_db = __salt__["config.option"]("postgres.maintenance_db") return (user, host, port, maintenance_db) def _psql_cmd(*args, **kwargs): - ''' + """ Return string with fully composed psql command. Accepts optional keyword arguments: user, host, port and maintenance_db, as well as any number of positional arguments to be added to the end of the command. - ''' + """ (user, host, port, maintenance_db) = _connection_defaults( - kwargs.get('user'), - kwargs.get('host'), - kwargs.get('port'), - kwargs.get('maintenance_db')) - _PSQL_BIN = _find_pg_binary('psql') - cmd = [_PSQL_BIN, - '--no-align', - '--no-readline', - '--no-psqlrc', - '--no-password'] # Never prompt, handled in _run_psql. + kwargs.get("user"), + kwargs.get("host"), + kwargs.get("port"), + kwargs.get("maintenance_db"), + ) + _PSQL_BIN = _find_pg_binary("psql") + cmd = [ + _PSQL_BIN, + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + ] # Never prompt, handled in _run_psql. if user: - cmd += ['--username', user] + cmd += ["--username", user] if host: - cmd += ['--host', host] + cmd += ["--host", host] if port: - cmd += ['--port', six.text_type(port)] + cmd += ["--port", six.text_type(port)] if not maintenance_db: - maintenance_db = 'postgres' - cmd.extend(['--dbname', maintenance_db]) + maintenance_db = "postgres" + cmd.extend(["--dbname", maintenance_db]) cmd.extend(args) return cmd -def _psql_prepare_and_run(cmd, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None, - user=None): +def _psql_prepare_and_run( + cmd, host=None, port=None, maintenance_db=None, password=None, runas=None, user=None +): rcmd = _psql_cmd( - host=host, user=user, port=port, - maintenance_db=maintenance_db, - *cmd) + host=host, user=user, port=port, maintenance_db=maintenance_db, *cmd + ) cmdret = _run_psql( - rcmd, runas=runas, password=password, host=host, port=port, user=user) + rcmd, runas=runas, password=password, host=host, port=port, user=user + ) return cmdret -def psql_query(query, user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None, write=False): - ''' +def psql_query( + query, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, + write=False, +): + """ Run an SQL-Query and return the results as a list. This command only supports SELECT statements. This limitation can be worked around with a query like this: @@ -417,33 +440,39 @@ def psql_query(query, user=None, host=None, port=None, maintenance_db=None, .. code-block:: bash salt '*' postgres.psql_query 'select * from pg_stat_activity' - ''' + """ ret = [] - csv_query = 'COPY ({0}) TO STDOUT WITH CSV HEADER'.format( - query.strip().rstrip(';')) + csv_query = "COPY ({0}) TO STDOUT WITH CSV HEADER".format(query.strip().rstrip(";")) # Mark transaction as R/W to achieve write will be allowed # Commit is necessary due to transaction if write: - csv_query = 'START TRANSACTION READ WRITE; {0}; COMMIT TRANSACTION;'.format(csv_query) + csv_query = "START TRANSACTION READ WRITE; {0}; COMMIT TRANSACTION;".format( + csv_query + ) # always use the same datestyle settings to allow parsing dates # regardless what server settings are configured - cmdret = _psql_prepare_and_run(['-v', 'datestyle=ISO,MDY', - '-c', csv_query], - runas=runas, - host=host, user=user, port=port, - maintenance_db=maintenance_db, - password=password) - if cmdret['retcode'] > 0: + cmdret = _psql_prepare_and_run( + ["-v", "datestyle=ISO,MDY", "-c", csv_query], + runas=runas, + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) + if cmdret["retcode"] > 0: return ret - csv_file = StringIO(cmdret['stdout']) + csv_file = StringIO(cmdret["stdout"]) header = {} - for row in csv.reader(csv_file, - delimiter=salt.utils.stringutils.to_str(','), - quotechar=salt.utils.stringutils.to_str('"')): + for row in csv.reader( + csv_file, + delimiter=salt.utils.stringutils.to_str(","), + quotechar=salt.utils.stringutils.to_str('"'), + ): if not row: continue if not header: @@ -460,9 +489,11 @@ def psql_query(query, user=None, host=None, port=None, maintenance_db=None, # Database related actions -def db_list(user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None): - ''' + +def db_list( + user=None, host=None, port=None, maintenance_db=None, password=None, runas=None +): + """ Return dictionary with information about databases of a Postgres server. CLI Example: @@ -470,7 +501,7 @@ def db_list(user=None, host=None, port=None, maintenance_db=None, .. code-block:: bash salt '*' postgres.db_list - ''' + """ ret = {} @@ -479,24 +510,37 @@ def db_list(user=None, host=None, port=None, maintenance_db=None, 'pg_encoding_to_char(encoding) as "Encoding", ' 'datcollate as "Collate", datctype as "Ctype", ' 'datacl as "Access privileges", spcname as "Tablespace" ' - 'FROM pg_database pgd, pg_roles pga, pg_tablespace pgts ' - 'WHERE pga.oid = pgd.datdba AND pgts.oid = pgd.dattablespace' + "FROM pg_database pgd, pg_roles pga, pg_tablespace pgts " + "WHERE pga.oid = pgd.datdba AND pgts.oid = pgd.dattablespace" ) - rows = psql_query(query, runas=runas, host=host, user=user, - port=port, maintenance_db=maintenance_db, - password=password) + rows = psql_query( + query, + runas=runas, + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) for row in rows: - ret[row['Name']] = row - ret[row['Name']].pop('Name') + ret[row["Name"]] = row + ret[row["Name"]].pop("Name") return ret -def db_exists(name, user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None): - ''' +def db_exists( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Checks if a database exists on the Postgres server. CLI Example: @@ -504,11 +548,16 @@ def db_exists(name, user=None, host=None, port=None, maintenance_db=None, .. code-block:: bash salt '*' postgres.db_exists 'dbname' - ''' + """ - databases = db_list(user=user, host=host, port=port, - maintenance_db=maintenance_db, - password=password, runas=runas) + databases = db_list( + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) return name in databases @@ -518,24 +567,27 @@ def _quote_ddl_value(value, quote="'"): return None if quote in value: # detect trivial sqli raise SaltInvocationError( - 'Unsupported character {0} in value: {1}'.format(quote, value)) + "Unsupported character {0} in value: {1}".format(quote, value) + ) return "{quote}{value}{quote}".format(quote=quote, value=value) -def db_create(name, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - tablespace=None, - encoding=None, - lc_collate=None, - lc_ctype=None, - owner=None, - template=None, - runas=None): - ''' +def db_create( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + tablespace=None, + encoding=None, + lc_collate=None, + lc_ctype=None, + owner=None, + template=None, + runas=None, +): + """ Adds a databases to the Postgres server. CLI Example: @@ -546,43 +598,59 @@ def db_create(name, salt '*' postgres.db_create 'dbname' template=template_postgis - ''' + """ # Base query to create a database query = 'CREATE DATABASE "{0}"'.format(name) # "With"-options to create a database - with_args = salt.utils.odict.OrderedDict([ - ('TABLESPACE', _quote_ddl_value(tablespace, '"')), - # owner needs to be enclosed in double quotes so postgres - # doesn't get thrown by dashes in the name - ('OWNER', _quote_ddl_value(owner, '"')), - ('TEMPLATE', template), - ('ENCODING', _quote_ddl_value(encoding)), - ('LC_COLLATE', _quote_ddl_value(lc_collate)), - ('LC_CTYPE', _quote_ddl_value(lc_ctype)), - ]) + with_args = salt.utils.odict.OrderedDict( + [ + ("TABLESPACE", _quote_ddl_value(tablespace, '"')), + # owner needs to be enclosed in double quotes so postgres + # doesn't get thrown by dashes in the name + ("OWNER", _quote_ddl_value(owner, '"')), + ("TEMPLATE", template), + ("ENCODING", _quote_ddl_value(encoding)), + ("LC_COLLATE", _quote_ddl_value(lc_collate)), + ("LC_CTYPE", _quote_ddl_value(lc_ctype)), + ] + ) with_chunks = [] for key, value in with_args.items(): if value is not None: - with_chunks += [key, '=', value] + with_chunks += [key, "=", value] # Build a final query if with_chunks: - with_chunks.insert(0, ' WITH') - query += ' '.join(with_chunks) + with_chunks.insert(0, " WITH") + query += " ".join(with_chunks) # Execute the command - ret = _psql_prepare_and_run(['-c', query], - user=user, host=host, port=port, - maintenance_db=maintenance_db, - password=password, runas=runas) - return ret['retcode'] == 0 + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) + return ret["retcode"] == 0 -def db_alter(name, user=None, host=None, port=None, maintenance_db=None, - password=None, tablespace=None, owner=None, owner_recurse=False, - runas=None): - ''' +def db_alter( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + tablespace=None, + owner=None, + owner_recurse=False, + runas=None, +): + """ Change tablespace or/and owner of database. CLI Example: @@ -590,42 +658,49 @@ def db_alter(name, user=None, host=None, port=None, maintenance_db=None, .. code-block:: bash salt '*' postgres.db_alter dbname owner=otheruser - ''' + """ if not any((tablespace, owner)): return True # Nothing todo? if owner and owner_recurse: - ret = owner_to(name, owner, - user=user, - host=host, - port=port, - password=password, - runas=runas) + ret = owner_to( + name, owner, user=user, host=host, port=port, password=password, runas=runas + ) else: queries = [] if owner: - queries.append('ALTER DATABASE "{0}" OWNER TO "{1}"'.format( - name, owner - )) + queries.append('ALTER DATABASE "{0}" OWNER TO "{1}"'.format(name, owner)) if tablespace: - queries.append('ALTER DATABASE "{0}" SET TABLESPACE "{1}"'.format( - name, tablespace - )) + queries.append( + 'ALTER DATABASE "{0}" SET TABLESPACE "{1}"'.format(name, tablespace) + ) for query in queries: - ret = _psql_prepare_and_run(['-c', query], - user=user, host=host, port=port, - maintenance_db=maintenance_db, - password=password, runas=runas) + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) - if ret['retcode'] != 0: + if ret["retcode"] != 0: return False return True -def db_remove(name, user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None): - ''' +def db_remove( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Removes a databases from the Postgres server. CLI Example: @@ -633,25 +708,35 @@ def db_remove(name, user=None, host=None, port=None, maintenance_db=None, .. code-block:: bash salt '*' postgres.db_remove 'dbname' - ''' - - # db doesn't exist, proceed - query = 'DROP DATABASE "{0}"'.format(name) - ret = _psql_prepare_and_run(['-c', query], - user=user, - host=host, - port=port, - runas=runas, - maintenance_db=maintenance_db, - password=password) - return ret['retcode'] == 0 + """ + for query in [ + 'REVOKE CONNECT ON DATABASE "{db}" FROM public;'.format(db=name), + "SELECT pid, pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '{db}' AND pid <> pg_backend_pid();".format( + db=name + ), + 'DROP DATABASE "{db}";'.format(db=name), + ]: + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + runas=runas, + maintenance_db=maintenance_db, + password=password, + ) + if ret["retcode"] != 0: + raise Exception("Failed: ret={}".format(ret)) + return True # Tablespace related actions -def tablespace_list(user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None): - ''' + +def tablespace_list( + user=None, host=None, port=None, maintenance_db=None, password=None, runas=None +): + """ Return dictionary with information about tablespaces of a Postgres server. CLI Example: @@ -661,31 +746,43 @@ def tablespace_list(user=None, host=None, port=None, maintenance_db=None, salt '*' postgres.tablespace_list .. versionadded:: 2015.8.0 - ''' + """ ret = {} query = ( 'SELECT spcname as "Name", pga.rolname as "Owner", spcacl as "ACL", ' 'spcoptions as "Opts", pg_tablespace_location(pgts.oid) as "Location" ' - 'FROM pg_tablespace pgts, pg_roles pga WHERE pga.oid = pgts.spcowner' + "FROM pg_tablespace pgts, pg_roles pga WHERE pga.oid = pgts.spcowner" ) - rows = __salt__['postgres.psql_query'](query, runas=runas, host=host, - user=user, port=port, - maintenance_db=maintenance_db, - password=password) + rows = __salt__["postgres.psql_query"]( + query, + runas=runas, + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) for row in rows: - ret[row['Name']] = row - ret[row['Name']].pop('Name') + ret[row["Name"]] = row + ret[row["Name"]].pop("Name") return ret -def tablespace_exists(name, user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None): - ''' +def tablespace_exists( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Checks if a tablespace exists on the Postgres server. CLI Example: @@ -695,18 +792,32 @@ def tablespace_exists(name, user=None, host=None, port=None, maintenance_db=None salt '*' postgres.tablespace_exists 'dbname' .. versionadded:: 2015.8.0 - ''' + """ - tablespaces = tablespace_list(user=user, host=host, port=port, - maintenance_db=maintenance_db, - password=password, runas=runas) + tablespaces = tablespace_list( + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) return name in tablespaces -def tablespace_create(name, location, options=None, owner=None, user=None, - host=None, port=None, maintenance_db=None, password=None, - runas=None): - ''' +def tablespace_create( + name, + location, + options=None, + owner=None, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Adds a tablespace to the Postgres server. CLI Example: @@ -716,33 +827,47 @@ def tablespace_create(name, location, options=None, owner=None, user=None, salt '*' postgres.tablespace_create tablespacename '/path/datadir' .. versionadded:: 2015.8.0 - ''' - owner_query = '' - options_query = '' + """ + owner_query = "" + options_query = "" if owner: owner_query = 'OWNER "{0}"'.format(owner) # should come out looking like: 'OWNER postgres' if options: - optionstext = ['{0} = {1}'.format(k, v) for k, v in six.iteritems(options)] - options_query = 'WITH ( {0} )'.format(', '.join(optionstext)) + optionstext = ["{0} = {1}".format(k, v) for k, v in six.iteritems(options)] + options_query = "WITH ( {0} )".format(", ".join(optionstext)) # should come out looking like: 'WITH ( opt1 = 1.0, opt2 = 4.0 )' - query = 'CREATE TABLESPACE "{0}" {1} LOCATION \'{2}\' {3}'.format(name, - owner_query, - location, - options_query) + query = "CREATE TABLESPACE \"{0}\" {1} LOCATION '{2}' {3}".format( + name, owner_query, location, options_query + ) # Execute the command - ret = _psql_prepare_and_run(['-c', query], - user=user, host=host, port=port, - maintenance_db=maintenance_db, - password=password, runas=runas) - return ret['retcode'] == 0 + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) + return ret["retcode"] == 0 -def tablespace_alter(name, user=None, host=None, port=None, maintenance_db=None, - password=None, new_name=None, new_owner=None, - set_option=None, reset_option=None, runas=None): - ''' +def tablespace_alter( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + new_name=None, + new_owner=None, + set_option=None, + reset_option=None, + runas=None, +): + """ Change tablespace name, owner, or options. CLI Example: @@ -755,39 +880,51 @@ def tablespace_alter(name, user=None, host=None, port=None, maintenance_db=None, salt '*' postgres.tablespace_alter tsname reset_option=seq_page_cost .. versionadded:: 2015.8.0 - ''' + """ if not any([new_name, new_owner, set_option, reset_option]): return True # Nothing todo? queries = [] if new_name: - queries.append('ALTER TABLESPACE "{0}" RENAME TO "{1}"'.format( - name, new_name)) + queries.append('ALTER TABLESPACE "{0}" RENAME TO "{1}"'.format(name, new_name)) if new_owner: - queries.append('ALTER TABLESPACE "{0}" OWNER TO "{1}"'.format( - name, new_owner)) + queries.append('ALTER TABLESPACE "{0}" OWNER TO "{1}"'.format(name, new_owner)) if set_option: - queries.append('ALTER TABLESPACE "{0}" SET ({1} = {2})'.format( - name, set_option.keys()[0], set_option.values()[0])) + queries.append( + 'ALTER TABLESPACE "{0}" SET ({1} = {2})'.format( + name, set_option.keys()[0], set_option.values()[0] + ) + ) if reset_option: - queries.append('ALTER TABLESPACE "{0}" RESET ({1})'.format( - name, reset_option)) + queries.append('ALTER TABLESPACE "{0}" RESET ({1})'.format(name, reset_option)) for query in queries: - ret = _psql_prepare_and_run(['-c', query], - user=user, host=host, port=port, - maintenance_db=maintenance_db, - password=password, runas=runas) - if ret['retcode'] != 0: + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) + if ret["retcode"] != 0: return False return True -def tablespace_remove(name, user=None, host=None, port=None, - maintenance_db=None, password=None, runas=None): - ''' +def tablespace_remove( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Removes a tablespace from the Postgres server. CLI Example: @@ -797,23 +934,33 @@ def tablespace_remove(name, user=None, host=None, port=None, salt '*' postgres.tablespace_remove tsname .. versionadded:: 2015.8.0 - ''' + """ query = 'DROP TABLESPACE "{0}"'.format(name) - ret = _psql_prepare_and_run(['-c', query], - user=user, - host=host, - port=port, - runas=runas, - maintenance_db=maintenance_db, - password=password) - return ret['retcode'] == 0 + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + runas=runas, + maintenance_db=maintenance_db, + password=password, + ) + return ret["retcode"] == 0 # User related actions -def user_list(user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None, return_password=False): - ''' + +def user_list( + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, + return_password=False, +): + """ Return a dict with information about users of a Postgres server. Set return_password to True to get password hash in the result. @@ -823,99 +970,121 @@ def user_list(user=None, host=None, port=None, maintenance_db=None, .. code-block:: bash salt '*' postgres.user_list - ''' + """ ret = {} - ver = _parsed_version(user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + ver = _parsed_version( + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) if ver: - if ver >= _LooseVersion('9.1'): - replication_column = 'pg_roles.rolreplication' + if ver >= _LooseVersion("9.1"): + replication_column = "pg_roles.rolreplication" else: - replication_column = 'NULL' - if ver >= _LooseVersion('9.5'): - rolcatupdate_column = 'NULL' + replication_column = "NULL" + if ver >= _LooseVersion("9.5"): + rolcatupdate_column = "NULL" else: - rolcatupdate_column = 'pg_roles.rolcatupdate' + rolcatupdate_column = "pg_roles.rolcatupdate" else: - log.error('Could not retrieve Postgres version. Is Postgresql server running?') + log.error("Could not retrieve Postgres version. Is Postgresql server running?") return False # will return empty string if return_password = False - _x = lambda s: s if return_password else '' + _x = lambda s: s if return_password else "" - query = (''.join([ - 'SELECT ' - 'pg_roles.rolname as "name",' - 'pg_roles.rolsuper as "superuser", ' - 'pg_roles.rolinherit as "inherits privileges", ' - 'pg_roles.rolcreaterole as "can create roles", ' - 'pg_roles.rolcreatedb as "can create databases", ' - '{0} as "can update system catalogs", ' - 'pg_roles.rolcanlogin as "can login", ' - '{1} as "replication", ' - 'pg_roles.rolconnlimit as "connections", ' - '(SELECT array_agg(pg_roles2.rolname)' - ' FROM pg_catalog.pg_auth_members' - ' JOIN pg_catalog.pg_roles pg_roles2 ON (pg_auth_members.roleid = pg_roles2.oid)' - ' WHERE pg_auth_members.member = pg_roles.oid) as "groups",' - 'pg_roles.rolvaliduntil::timestamp(0) as "expiry time", ' - 'pg_roles.rolconfig as "defaults variables" ' - , _x(', COALESCE(pg_shadow.passwd, pg_authid.rolpassword) as "password" '), - 'FROM pg_roles ' - , _x('LEFT JOIN pg_authid ON pg_roles.oid = pg_authid.oid ') - , _x('LEFT JOIN pg_shadow ON pg_roles.oid = pg_shadow.usesysid') - ]).format(rolcatupdate_column, replication_column)) + query = "".join( + [ + "SELECT " + 'pg_roles.rolname as "name",' + 'pg_roles.rolsuper as "superuser", ' + 'pg_roles.rolinherit as "inherits privileges", ' + 'pg_roles.rolcreaterole as "can create roles", ' + 'pg_roles.rolcreatedb as "can create databases", ' + '{0} as "can update system catalogs", ' + 'pg_roles.rolcanlogin as "can login", ' + '{1} as "replication", ' + 'pg_roles.rolconnlimit as "connections", ' + "(SELECT array_agg(pg_roles2.rolname)" + " FROM pg_catalog.pg_auth_members" + " JOIN pg_catalog.pg_roles pg_roles2 ON (pg_auth_members.roleid = pg_roles2.oid)" + ' WHERE pg_auth_members.member = pg_roles.oid) as "groups",' + 'pg_roles.rolvaliduntil::timestamp(0) as "expiry time", ' + 'pg_roles.rolconfig as "defaults variables" ', + _x(', COALESCE(pg_shadow.passwd, pg_authid.rolpassword) as "password" '), + "FROM pg_roles ", + _x("LEFT JOIN pg_authid ON pg_roles.oid = pg_authid.oid "), + _x("LEFT JOIN pg_shadow ON pg_roles.oid = pg_shadow.usesysid"), + ] + ).format(rolcatupdate_column, replication_column) - rows = psql_query(query, - runas=runas, - host=host, - user=user, - port=port, - maintenance_db=maintenance_db, - password=password) + rows = psql_query( + query, + runas=runas, + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) def get_bool(rowdict, key): - ''' + """ Returns the boolean value of the key, instead of 't' and 'f' strings. - ''' - if rowdict[key] == 't': + """ + if rowdict[key] == "t": return True - elif rowdict[key] == 'f': + elif rowdict[key] == "f": return False else: return None for row in rows: retrow = {} - for key in ('superuser', 'inherits privileges', 'can create roles', - 'can create databases', 'can update system catalogs', - 'can login', 'replication', 'connections'): + for key in ( + "superuser", + "inherits privileges", + "can create roles", + "can create databases", + "can update system catalogs", + "can login", + "replication", + "connections", + ): retrow[key] = get_bool(row, key) - for date_key in ('expiry time',): + for date_key in ("expiry time",): try: retrow[date_key] = datetime.datetime.strptime( - row[date_key], '%Y-%m-%d %H:%M:%S') + row[date_key], "%Y-%m-%d %H:%M:%S" + ) except ValueError: retrow[date_key] = None - retrow['defaults variables'] = row['defaults variables'] + retrow["defaults variables"] = row["defaults variables"] if return_password: - retrow['password'] = row['password'] + retrow["password"] = row["password"] # use csv reader to handle quoted roles correctly - retrow['groups'] = list(csv.reader([row['groups'].strip('{}')]))[0] - ret[row['name']] = retrow + retrow["groups"] = list(csv.reader([row["groups"].strip("{}")]))[0] + ret[row["name"]] = retrow return ret -def role_get(name, user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None, return_password=False): - ''' +def role_get( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, + return_password=False, +): + """ Return a dict with information about users of a Postgres server. Set return_password to True to get password hash in the result. @@ -925,26 +1094,33 @@ def role_get(name, user=None, host=None, port=None, maintenance_db=None, .. code-block:: bash salt '*' postgres.role_get postgres - ''' - all_users = user_list(user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas, - return_password=return_password) + """ + all_users = user_list( + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + return_password=return_password, + ) try: return all_users.get(name, None) except AttributeError: - log.error('Could not retrieve Postgres role. Is Postgres running?') + log.error("Could not retrieve Postgres role. Is Postgres running?") return None -def user_exists(name, - user=None, host=None, port=None, maintenance_db=None, - password=None, - runas=None): - ''' +def user_exists( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Checks if a user exists on the Postgres server. CLI Example: @@ -952,215 +1128,231 @@ def user_exists(name, .. code-block:: bash salt '*' postgres.user_exists 'username' - ''' + """ return bool( - role_get(name, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas, - return_password=False)) + role_get( + name, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + return_password=False, + ) + ) -def _add_role_flag(string, - test, - flag, - cond=None, - prefix='NO', - addtxt='', - skip=False): +def _add_role_flag(string, test, flag, cond=None, prefix="NO", addtxt="", skip=False): if not skip: if cond is None: cond = test if test is not None: if cond: - string = '{0} {1}'.format(string, flag) + string = "{0} {1}".format(string, flag) else: - string = '{0} {2}{1}'.format(string, flag, prefix) + string = "{0} {2}{1}".format(string, flag, prefix) if addtxt: - string = '{0} {1}'.format(string, addtxt) + string = "{0} {1}".format(string, addtxt) return string -def _maybe_encrypt_password(role, - password, - encrypted=_DEFAULT_PASSWORDS_ENCRYPTION): - ''' +def _maybe_encrypt_password(role, password, encrypted=_DEFAULT_PASSWORDS_ENCRYPTION): + """ pgsql passwords are md5 hashes of the string: 'md5{password}{rolename}' - ''' + """ if password is not None: password = six.text_type(password) - if encrypted and password and not password.startswith('md5'): + if encrypted and password and not password.startswith("md5"): password = "md5{0}".format( - hashlib.md5(salt.utils.stringutils.to_bytes('{0}{1}'.format(password, role))).hexdigest()) + hashlib.md5( + salt.utils.stringutils.to_bytes("{0}{1}".format(password, role)) + ).hexdigest() + ) return password -def _role_cmd_args(name, - sub_cmd='', - typ_='role', - encrypted=None, - login=None, - connlimit=None, - inherit=None, - createdb=None, - createroles=None, - superuser=None, - groups=None, - replication=None, - rolepassword=None, - valid_until=None, - db_role=None): +def _role_cmd_args( + name, + sub_cmd="", + typ_="role", + encrypted=None, + login=None, + connlimit=None, + inherit=None, + createdb=None, + createroles=None, + superuser=None, + groups=None, + replication=None, + rolepassword=None, + valid_until=None, + db_role=None, +): if inherit is None: - if typ_ in ['user', 'group']: + if typ_ in ["user", "group"]: inherit = True if login is None: - if typ_ == 'user': + if typ_ == "user": login = True - if typ_ == 'group': + if typ_ == "group": login = False # defaults to encrypted passwords (md5{password}{rolename}) if encrypted is None: encrypted = _DEFAULT_PASSWORDS_ENCRYPTION skip_passwd = False - escaped_password = '' - escaped_valid_until = '' + escaped_password = "" + escaped_valid_until = "" if not ( rolepassword is not None # first is passwd set # second is for handling NOPASSWD - and ( - isinstance(rolepassword, six.string_types) and bool(rolepassword) - ) - or ( - isinstance(rolepassword, bool) - ) + and (isinstance(rolepassword, six.string_types) and bool(rolepassword)) + or (isinstance(rolepassword, bool)) ): skip_passwd = True if isinstance(rolepassword, six.string_types) and bool(rolepassword): - escaped_password = '\'{0}\''.format( - _maybe_encrypt_password(name, - rolepassword.replace('\'', '\'\''), - encrypted=encrypted)) - if isinstance(valid_until, six.string_types) and bool(valid_until): - escaped_valid_until = '\'{0}\''.format( - valid_until.replace('\'', '\'\''), + escaped_password = "'{0}'".format( + _maybe_encrypt_password( + name, rolepassword.replace("'", "''"), encrypted=encrypted + ) ) + if isinstance(valid_until, six.string_types) and bool(valid_until): + escaped_valid_until = "'{0}'".format(valid_until.replace("'", "''"),) skip_superuser = False - if bool(db_role) and bool(superuser) == bool(db_role['superuser']): + if bool(db_role) and bool(superuser) == bool(db_role["superuser"]): skip_superuser = True flags = ( - {'flag': 'INHERIT', 'test': inherit}, - {'flag': 'CREATEDB', 'test': createdb}, - {'flag': 'CREATEROLE', 'test': createroles}, - {'flag': 'SUPERUSER', 'test': superuser, - 'skip': skip_superuser}, - {'flag': 'REPLICATION', 'test': replication}, - {'flag': 'LOGIN', 'test': login}, - {'flag': 'CONNECTION LIMIT', - 'test': bool(connlimit), - 'addtxt': six.text_type(connlimit), - 'skip': connlimit is None}, - {'flag': 'ENCRYPTED', - 'test': (encrypted is not None and bool(rolepassword)), - 'skip': skip_passwd or isinstance(rolepassword, bool), - 'cond': encrypted, - 'prefix': 'UN'}, - {'flag': 'PASSWORD', 'test': bool(rolepassword), - 'skip': skip_passwd, - 'addtxt': escaped_password}, - {'flag': 'VALID UNTIL', - 'test': bool(valid_until), - 'skip': valid_until is None, - 'addtxt': escaped_valid_until}, + {"flag": "INHERIT", "test": inherit}, + {"flag": "CREATEDB", "test": createdb}, + {"flag": "CREATEROLE", "test": createroles}, + {"flag": "SUPERUSER", "test": superuser, "skip": skip_superuser}, + {"flag": "REPLICATION", "test": replication}, + {"flag": "LOGIN", "test": login}, + { + "flag": "CONNECTION LIMIT", + "test": bool(connlimit), + "addtxt": six.text_type(connlimit), + "skip": connlimit is None, + }, + { + "flag": "ENCRYPTED", + "test": (encrypted is not None and bool(rolepassword)), + "skip": skip_passwd or isinstance(rolepassword, bool), + "cond": encrypted, + "prefix": "UN", + }, + { + "flag": "PASSWORD", + "test": bool(rolepassword), + "skip": skip_passwd, + "addtxt": escaped_password, + }, + { + "flag": "VALID UNTIL", + "test": bool(valid_until), + "skip": valid_until is None, + "addtxt": escaped_valid_until, + }, ) for data in flags: sub_cmd = _add_role_flag(sub_cmd, **data) - if sub_cmd.endswith('WITH'): - sub_cmd = sub_cmd.replace(' WITH', '') + if sub_cmd.endswith("WITH"): + sub_cmd = sub_cmd.replace(" WITH", "") if groups: if isinstance(groups, list): - groups = ','.join(groups) - for group in groups.split(','): + groups = ",".join(groups) + for group in groups.split(","): sub_cmd = '{0}; GRANT "{1}" TO "{2}"'.format(sub_cmd, group, name) return sub_cmd -def _role_create(name, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - createdb=None, - createroles=None, - encrypted=None, - superuser=None, - login=None, - connlimit=None, - inherit=None, - replication=None, - rolepassword=None, - valid_until=None, - typ_='role', - groups=None, - runas=None): - ''' +def _role_create( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + createdb=None, + createroles=None, + encrypted=None, + superuser=None, + login=None, + connlimit=None, + inherit=None, + replication=None, + rolepassword=None, + valid_until=None, + typ_="role", + groups=None, + runas=None, +): + """ Creates a Postgres role. Users and Groups are both roles in postgres. However, users can login, groups cannot. - ''' + """ # check if role exists - if user_exists(name, user, host, port, maintenance_db, - password=password, runas=runas): - log.info('%s \'%s\' already exists', typ_.capitalize(), name) + if user_exists( + name, user, host, port, maintenance_db, password=password, runas=runas + ): + log.info("%s '%s' already exists", typ_.capitalize(), name) return False sub_cmd = 'CREATE ROLE "{0}" WITH'.format(name) - sub_cmd = '{0} {1}'.format(sub_cmd, _role_cmd_args( - name, - typ_=typ_, - encrypted=encrypted, - login=login, - connlimit=connlimit, - inherit=inherit, - createdb=createdb, - createroles=createroles, - superuser=superuser, - groups=groups, - replication=replication, - rolepassword=rolepassword, - valid_until=valid_until - )) - ret = _psql_prepare_and_run(['-c', sub_cmd], - runas=runas, host=host, user=user, port=port, - maintenance_db=maintenance_db, - password=password) + sub_cmd = "{0} {1}".format( + sub_cmd, + _role_cmd_args( + name, + typ_=typ_, + encrypted=encrypted, + login=login, + connlimit=connlimit, + inherit=inherit, + createdb=createdb, + createroles=createroles, + superuser=superuser, + groups=groups, + replication=replication, + rolepassword=rolepassword, + valid_until=valid_until, + ), + ) + ret = _psql_prepare_and_run( + ["-c", sub_cmd], + runas=runas, + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) - return ret['retcode'] == 0 + return ret["retcode"] == 0 -def user_create(username, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - createdb=None, - createroles=None, - inherit=None, - login=None, - connlimit=None, - encrypted=None, - superuser=None, - replication=None, - rolepassword=None, - valid_until=None, - groups=None, - runas=None): - ''' +def user_create( + username, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + createdb=None, + createroles=None, + inherit=None, + login=None, + connlimit=None, + encrypted=None, + superuser=None, + replication=None, + rolepassword=None, + valid_until=None, + groups=None, + runas=None, +): + """ Creates a Postgres user. CLI Examples: @@ -1170,109 +1362,123 @@ def user_create(username, salt '*' postgres.user_create 'username' user='user' \\ host='hostname' port='port' password='password' \\ rolepassword='rolepassword' valid_until='valid_until' - ''' - return _role_create(username, - typ_='user', - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - createdb=createdb, - createroles=createroles, - inherit=inherit, - login=login, - connlimit=connlimit, - encrypted=encrypted, - superuser=superuser, - replication=replication, - rolepassword=rolepassword, - valid_until=valid_until, - groups=groups, - runas=runas) - - -def _role_update(name, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - createdb=None, - typ_='role', - createroles=None, - inherit=None, - login=None, - connlimit=None, - encrypted=None, - superuser=None, - replication=None, - rolepassword=None, - valid_until=None, - groups=None, - runas=None): - ''' - Updates a postgres role. - ''' - role = role_get(name, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas, - return_password=False) - - # check if user exists - if not bool(role): - log.info( - '%s \'%s\' could not be found', typ_.capitalize(), name - ) - return False - - sub_cmd = 'ALTER ROLE "{0}" WITH'.format(name) - sub_cmd = '{0} {1}'.format(sub_cmd, _role_cmd_args( - name, - encrypted=encrypted, - login=login, - connlimit=connlimit, - inherit=inherit, + """ + return _role_create( + username, + typ_="user", + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, createdb=createdb, createroles=createroles, + inherit=inherit, + login=login, + connlimit=connlimit, + encrypted=encrypted, superuser=superuser, - groups=groups, replication=replication, rolepassword=rolepassword, valid_until=valid_until, - db_role=role - )) - ret = _psql_prepare_and_run(['-c', sub_cmd], - runas=runas, host=host, user=user, port=port, - maintenance_db=maintenance_db, - password=password) - - return ret['retcode'] == 0 + groups=groups, + runas=runas, + ) -def user_update(username, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - createdb=None, - createroles=None, - encrypted=None, - superuser=None, - inherit=None, - login=None, - connlimit=None, - replication=None, - rolepassword=None, - valid_until=None, - groups=None, - runas=None): - ''' +def _role_update( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + createdb=None, + typ_="role", + createroles=None, + inherit=None, + login=None, + connlimit=None, + encrypted=None, + superuser=None, + replication=None, + rolepassword=None, + valid_until=None, + groups=None, + runas=None, +): + """ + Updates a postgres role. + """ + role = role_get( + name, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + return_password=False, + ) + + # check if user exists + if not bool(role): + log.info("%s '%s' could not be found", typ_.capitalize(), name) + return False + + sub_cmd = 'ALTER ROLE "{0}" WITH'.format(name) + sub_cmd = "{0} {1}".format( + sub_cmd, + _role_cmd_args( + name, + encrypted=encrypted, + login=login, + connlimit=connlimit, + inherit=inherit, + createdb=createdb, + createroles=createroles, + superuser=superuser, + groups=groups, + replication=replication, + rolepassword=rolepassword, + valid_until=valid_until, + db_role=role, + ), + ) + ret = _psql_prepare_and_run( + ["-c", sub_cmd], + runas=runas, + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) + + return ret["retcode"] == 0 + + +def user_update( + username, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + createdb=None, + createroles=None, + encrypted=None, + superuser=None, + inherit=None, + login=None, + connlimit=None, + replication=None, + rolepassword=None, + valid_until=None, + groups=None, + runas=None, +): + """ Updates a Postgres user. CLI Examples: @@ -1282,62 +1488,75 @@ def user_update(username, salt '*' postgres.user_update 'username' user='user' \\ host='hostname' port='port' password='password' \\ rolepassword='rolepassword' valid_until='valid_until' - ''' - return _role_update(username, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - typ_='user', - inherit=inherit, - login=login, - connlimit=connlimit, - createdb=createdb, - createroles=createroles, - encrypted=encrypted, - superuser=superuser, - replication=replication, - rolepassword=rolepassword, - valid_until=valid_until, - groups=groups, - runas=runas) + """ + return _role_update( + username, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + typ_="user", + inherit=inherit, + login=login, + connlimit=connlimit, + createdb=createdb, + createroles=createroles, + encrypted=encrypted, + superuser=superuser, + replication=replication, + rolepassword=rolepassword, + valid_until=valid_until, + groups=groups, + runas=runas, + ) -def _role_remove(name, user=None, host=None, port=None, maintenance_db=None, - password=None, runas=None): - ''' +def _role_remove( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Removes a role from the Postgres Server - ''' + """ # check if user exists - if not user_exists(name, user, host, port, maintenance_db, - password=password, runas=runas): - log.info('User \'%s\' does not exist', name) + if not user_exists( + name, user, host, port, maintenance_db, password=password, runas=runas + ): + log.info("User '%s' does not exist", name) return False # user exists, proceed sub_cmd = 'DROP ROLE "{0}"'.format(name) _psql_prepare_and_run( - ['-c', sub_cmd], - runas=runas, host=host, user=user, port=port, - maintenance_db=maintenance_db, password=password) + ["-c", sub_cmd], + runas=runas, + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) - if not user_exists(name, user, host, port, maintenance_db, - password=password, runas=runas): + if not user_exists( + name, user, host, port, maintenance_db, password=password, runas=runas + ): return True else: - log.info('Failed to delete user \'%s\'.', name) + log.info("Failed to delete user '%s'.", name) return False -def available_extensions(user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def available_extensions( + user=None, host=None, port=None, maintenance_db=None, password=None, runas=None +): + """ List available postgresql extensions CLI Example: @@ -1346,29 +1565,29 @@ def available_extensions(user=None, salt '*' postgres.available_extensions - ''' + """ exts = [] - query = ( - 'select * ' - 'from pg_available_extensions();' + query = "select * " "from pg_available_extensions();" + ret = psql_query( + query, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, ) - ret = psql_query(query, user=user, host=host, port=port, - maintenance_db=maintenance_db, - password=password, runas=runas) exts = {} for row in ret: - if 'default_version' in row and 'name' in row: - exts[row['name']] = row + if "default_version" in row and "name" in row: + exts[row["name"]] = row return exts -def installed_extensions(user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def installed_extensions( + user=None, host=None, port=None, maintenance_db=None, password=None, runas=None +): + """ List installed postgresql extensions CLI Example: @@ -1377,30 +1596,38 @@ def installed_extensions(user=None, salt '*' postgres.installed_extensions - ''' + """ exts = [] query = ( - 'select a.*, b.nspname as schema_name ' - 'from pg_extension a, pg_namespace b where a.extnamespace = b.oid;' + "select a.*, b.nspname as schema_name " + "from pg_extension a, pg_namespace b where a.extnamespace = b.oid;" + ) + ret = psql_query( + query, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, ) - ret = psql_query(query, user=user, host=host, port=port, - maintenance_db=maintenance_db, - password=password, runas=runas) exts = {} for row in ret: - if 'extversion' in row and 'extname' in row: - exts[row['extname']] = row + if "extversion" in row and "extname" in row: + exts[row["extname"]] = row return exts -def get_available_extension(name, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def get_available_extension( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Get info about an available postgresql extension CLI Example: @@ -1409,23 +1636,27 @@ def get_available_extension(name, salt '*' postgres.get_available_extension plpgsql - ''' - return available_extensions(user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas).get(name, None) + """ + return available_extensions( + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ).get(name, None) -def get_installed_extension(name, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def get_installed_extension( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Get info about an installed postgresql extension CLI Example: @@ -1434,23 +1665,27 @@ def get_installed_extension(name, salt '*' postgres.get_installed_extension plpgsql - ''' - return installed_extensions(user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas).get(name, None) + """ + return installed_extensions( + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ).get(name, None) -def is_available_extension(name, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def is_available_extension( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Test if a specific extension is available CLI Example: @@ -1459,38 +1694,39 @@ def is_available_extension(name, salt '*' postgres.is_available_extension - ''' - exts = available_extensions(user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) - if name.lower() in [ - a.lower() - for a in exts - ]: + """ + exts = available_extensions( + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) + if name.lower() in [a.lower() for a in exts]: return True return False def _pg_is_older_ext_ver(a, b): - ''' + """ Compare versions of extensions using salt.utils.versions.LooseVersion. Returns ``True`` if version a is lesser than b. - ''' + """ return _LooseVersion(a) < _LooseVersion(b) -def is_installed_extension(name, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def is_installed_extension( + name, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Test if a specific extension is installed CLI Example: @@ -1499,7 +1735,7 @@ def is_installed_extension(name, salt '*' postgres.is_installed_extension - ''' + """ installed_ext = get_installed_extension( name, user=user, @@ -1507,20 +1743,23 @@ def is_installed_extension(name, port=port, maintenance_db=maintenance_db, password=password, - runas=runas) + runas=runas, + ) return bool(installed_ext) -def create_metadata(name, - ext_version=None, - schema=None, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def create_metadata( + name, + ext_version=None, + schema=None, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Get lifecycle information about an extension CLI Example: @@ -1529,7 +1768,7 @@ def create_metadata(name, salt '*' postgres.create_metadata adminpack - ''' + """ installed_ext = get_installed_extension( name, user=user, @@ -1537,38 +1776,37 @@ def create_metadata(name, port=port, maintenance_db=maintenance_db, password=password, - runas=runas) + runas=runas, + ) ret = [_EXTENSION_NOT_INSTALLED] if installed_ext: ret = [_EXTENSION_INSTALLED] - if ( - ext_version is not None - and _pg_is_older_ext_ver( - installed_ext.get('extversion', ext_version), - ext_version - ) + if ext_version is not None and _pg_is_older_ext_ver( + installed_ext.get("extversion", ext_version), ext_version ): ret.append(_EXTENSION_TO_UPGRADE) if ( schema is not None - and installed_ext.get('extrelocatable', 'f') == 't' - and installed_ext.get('schema_name', schema) != schema + and installed_ext.get("extrelocatable", "f") == "t" + and installed_ext.get("schema_name", schema) != schema ): ret.append(_EXTENSION_TO_MOVE) return ret -def drop_extension(name, - if_exists=None, - restrict=None, - cascade=None, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def drop_extension( + name, + if_exists=None, + restrict=None, + cascade=None, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Drop an installed postgresql extension CLI Example: @@ -1577,58 +1815,69 @@ def drop_extension(name, salt '*' postgres.drop_extension 'adminpack' - ''' + """ if cascade is None: cascade = True if if_exists is None: if_exists = False if restrict is None: restrict = False - args = ['DROP EXTENSION'] + args = ["DROP EXTENSION"] if if_exists: - args.append('IF EXISTS') + args.append("IF EXISTS") args.append(name) if cascade: - args.append('CASCADE') + args.append("CASCADE") if restrict: - args.append('RESTRICT') - args.append(';') - cmd = ' '.join(args) - if is_installed_extension(name, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas): + args.append("RESTRICT") + args.append(";") + cmd = " ".join(args) + if is_installed_extension( + name, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ): _psql_prepare_and_run( - ['-c', cmd], - runas=runas, host=host, user=user, port=port, - maintenance_db=maintenance_db, password=password) - ret = not is_installed_extension(name, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + ["-c", cmd], + runas=runas, + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) + ret = not is_installed_extension( + name, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) if not ret: - log.info('Failed to drop ext: %s', name) + log.info("Failed to drop ext: %s", name) return ret -def create_extension(name, - if_not_exists=None, - schema=None, - ext_version=None, - from_version=None, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def create_extension( + name, + if_not_exists=None, + schema=None, + ext_version=None, + from_version=None, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Install a postgresql extension CLI Example: @@ -1637,84 +1886,99 @@ def create_extension(name, salt '*' postgres.create_extension 'adminpack' - ''' + """ if if_not_exists is None: if_not_exists = True - mtdata = create_metadata(name, - ext_version=ext_version, - schema=schema, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + mtdata = create_metadata( + name, + ext_version=ext_version, + schema=schema, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) installed = _EXTENSION_NOT_INSTALLED not in mtdata - installable = is_available_extension(name, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + installable = is_available_extension( + name, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) if installable: if not installed: - args = ['CREATE EXTENSION'] + args = ["CREATE EXTENSION"] if if_not_exists: - args.append('IF NOT EXISTS') + args.append("IF NOT EXISTS") args.append('"{0}"'.format(name)) sargs = [] if schema: sargs.append('SCHEMA "{0}"'.format(schema)) if ext_version: - sargs.append('VERSION {0}'.format(ext_version)) + sargs.append("VERSION {0}".format(ext_version)) if from_version: - sargs.append('FROM {0}'.format(from_version)) + sargs.append("FROM {0}".format(from_version)) if sargs: - args.append('WITH') + args.append("WITH") args.extend(sargs) - args.append(';') - cmd = ' '.join(args).strip() + args.append(";") + cmd = " ".join(args).strip() else: args = [] if schema and _EXTENSION_TO_MOVE in mtdata: - args.append('ALTER EXTENSION "{0}" SET SCHEMA "{1}";'.format( - name, schema)) + args.append( + 'ALTER EXTENSION "{0}" SET SCHEMA "{1}";'.format(name, schema) + ) if ext_version and _EXTENSION_TO_UPGRADE in mtdata: - args.append('ALTER EXTENSION "{0}" UPDATE TO {1};'.format( - name, ext_version)) - cmd = ' '.join(args).strip() + args.append( + 'ALTER EXTENSION "{0}" UPDATE TO {1};'.format(name, ext_version) + ) + cmd = " ".join(args).strip() if cmd: _psql_prepare_and_run( - ['-c', cmd], - runas=runas, host=host, user=user, port=port, - maintenance_db=maintenance_db, password=password) - mtdata = create_metadata(name, - ext_version=ext_version, - schema=schema, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + ["-c", cmd], + runas=runas, + host=host, + user=user, + port=port, + maintenance_db=maintenance_db, + password=password, + ) + mtdata = create_metadata( + name, + ext_version=ext_version, + schema=schema, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) ret = True for i in _EXTENSION_FLAGS: if (i in mtdata) and (i != _EXTENSION_INSTALLED): ret = False if not ret: - log.info('Failed to create ext: %s', name) + log.info("Failed to create ext: %s", name) return ret -def user_remove(username, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def user_remove( + username, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Removes a user from the Postgres server. CLI Example: @@ -1722,35 +1986,40 @@ def user_remove(username, .. code-block:: bash salt '*' postgres.user_remove 'username' - ''' - return _role_remove(username, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + """ + return _role_remove( + username, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) # Group related actions -def group_create(groupname, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - createdb=None, - createroles=None, - encrypted=None, - login=None, - inherit=None, - superuser=None, - replication=None, - rolepassword=None, - groups=None, - runas=None): - ''' + +def group_create( + groupname, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + createdb=None, + createroles=None, + encrypted=None, + login=None, + inherit=None, + superuser=None, + replication=None, + rolepassword=None, + groups=None, + runas=None, +): + """ Creates a Postgres group. A group is postgres is similar to a user, but cannot login. @@ -1761,43 +2030,47 @@ def group_create(groupname, salt '*' postgres.group_create 'groupname' user='user' \\ host='hostname' port='port' password='password' \\ rolepassword='rolepassword' - ''' - return _role_create(groupname, - user=user, - typ_='group', - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - createdb=createdb, - createroles=createroles, - encrypted=encrypted, - login=login, - inherit=inherit, - superuser=superuser, - replication=replication, - rolepassword=rolepassword, - groups=groups, - runas=runas) + """ + return _role_create( + groupname, + user=user, + typ_="group", + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + createdb=createdb, + createroles=createroles, + encrypted=encrypted, + login=login, + inherit=inherit, + superuser=superuser, + replication=replication, + rolepassword=rolepassword, + groups=groups, + runas=runas, + ) -def group_update(groupname, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - createdb=None, - createroles=None, - encrypted=None, - inherit=None, - login=None, - superuser=None, - replication=None, - rolepassword=None, - groups=None, - runas=None): - ''' +def group_update( + groupname, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + createdb=None, + createroles=None, + encrypted=None, + inherit=None, + login=None, + superuser=None, + replication=None, + rolepassword=None, + groups=None, + runas=None, +): + """ Updates a postgres group CLI Examples: @@ -1807,34 +2080,38 @@ def group_update(groupname, salt '*' postgres.group_update 'username' user='user' \\ host='hostname' port='port' password='password' \\ rolepassword='rolepassword' - ''' - return _role_update(groupname, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - createdb=createdb, - typ_='group', - createroles=createroles, - encrypted=encrypted, - login=login, - inherit=inherit, - superuser=superuser, - replication=replication, - rolepassword=rolepassword, - groups=groups, - runas=runas) + """ + return _role_update( + groupname, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + createdb=createdb, + typ_="group", + createroles=createroles, + encrypted=encrypted, + login=login, + inherit=inherit, + superuser=superuser, + replication=replication, + rolepassword=rolepassword, + groups=groups, + runas=runas, + ) -def group_remove(groupname, - user=None, - host=None, - port=None, - maintenance_db=None, - password=None, - runas=None): - ''' +def group_remove( + groupname, + user=None, + host=None, + port=None, + maintenance_db=None, + password=None, + runas=None, +): + """ Removes a group from the Postgres server. CLI Example: @@ -1842,24 +2119,22 @@ def group_remove(groupname, .. code-block:: bash salt '*' postgres.group_remove 'groupname' - ''' - return _role_remove(groupname, - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + """ + return _role_remove( + groupname, + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) -def owner_to(dbname, - ownername, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' +def owner_to( + dbname, ownername, user=None, host=None, port=None, password=None, runas=None +): + """ Set the owner of all schemas, functions, tables, views and sequences to the given username. @@ -1868,72 +2143,93 @@ def owner_to(dbname, .. code-block:: bash salt '*' postgres.owner_to 'dbname' 'username' - ''' + """ sqlfile = tempfile.NamedTemporaryFile() - sqlfile.write('begin;\n') - sqlfile.write( - 'alter database "{0}" owner to "{1}";\n'.format( - dbname, ownername - ) - ) + sqlfile.write("begin;\n") + sqlfile.write('alter database "{0}" owner to "{1}";\n'.format(dbname, ownername)) queries = ( # schemas - ('alter schema {n} owner to {owner};', - 'select quote_ident(schema_name) as n from ' - 'information_schema.schemata;'), + ( + "alter schema {n} owner to {owner};", + "select quote_ident(schema_name) as n from information_schema.schemata;", + ), # tables and views - ('alter table {n} owner to {owner};', - 'select quote_ident(table_schema)||\'.\'||quote_ident(table_name) as ' - 'n from information_schema.tables where table_schema not in ' - '(\'pg_catalog\', \'information_schema\');'), + ( + "alter table {n} owner to {owner};", + "select quote_ident(table_schema)||'.'||quote_ident(table_name) as " + "n from information_schema.tables where table_schema not in " + "('pg_catalog', 'information_schema');", + ), # functions - ('alter function {n} owner to {owner};', - 'select p.oid::regprocedure::text as n from pg_catalog.pg_proc p ' - 'join pg_catalog.pg_namespace ns on p.pronamespace=ns.oid where ' - 'ns.nspname not in (\'pg_catalog\', \'information_schema\') ' - ' and not p.proisagg;'), + ( + "alter function {n} owner to {owner};", + "select p.oid::regprocedure::text as n from pg_catalog.pg_proc p " + "join pg_catalog.pg_namespace ns on p.pronamespace=ns.oid where " + "ns.nspname not in ('pg_catalog', 'information_schema') " + " and not p.proisagg;", + ), # aggregate functions - ('alter aggregate {n} owner to {owner};', - 'select p.oid::regprocedure::text as n from pg_catalog.pg_proc p ' - 'join pg_catalog.pg_namespace ns on p.pronamespace=ns.oid where ' - 'ns.nspname not in (\'pg_catalog\', \'information_schema\') ' - 'and p.proisagg;'), + ( + "alter aggregate {n} owner to {owner};", + "select p.oid::regprocedure::text as n from pg_catalog.pg_proc p " + "join pg_catalog.pg_namespace ns on p.pronamespace=ns.oid where " + "ns.nspname not in ('pg_catalog', 'information_schema') " + "and p.proisagg;", + ), # sequences - ('alter sequence {n} owner to {owner};', - 'select quote_ident(sequence_schema)||\'.\'||' - 'quote_ident(sequence_name) as n from information_schema.sequences;') + ( + "alter sequence {n} owner to {owner};", + "select quote_ident(sequence_schema)||'.'||" + "quote_ident(sequence_name) as n from information_schema.sequences;", + ), ) for fmt, query in queries: - ret = psql_query(query, user=user, host=host, port=port, - maintenance_db=dbname, password=password, runas=runas) + ret = psql_query( + query, + user=user, + host=host, + port=port, + maintenance_db=dbname, + password=password, + runas=runas, + ) for row in ret: - sqlfile.write(fmt.format(owner=ownername, n=row['n']) + '\n') + sqlfile.write(fmt.format(owner=ownername, n=row["n"]) + "\n") - sqlfile.write('commit;\n') + sqlfile.write("commit;\n") sqlfile.flush() os.chmod(sqlfile.name, 0o644) # ensure psql can read the file # run the generated sqlfile in the db - cmdret = _psql_prepare_and_run(['-f', sqlfile.name], - user=user, - runas=runas, - host=host, - port=port, - password=password, - maintenance_db=dbname) + cmdret = _psql_prepare_and_run( + ["-f", sqlfile.name], + user=user, + runas=runas, + host=host, + port=port, + password=password, + maintenance_db=dbname, + ) return cmdret + # Schema related actions -def schema_create(dbname, name, owner=None, - user=None, - db_user=None, db_password=None, - db_host=None, db_port=None): - ''' +def schema_create( + dbname, + name, + owner=None, + user=None, + db_user=None, + db_password=None, + db_host=None, + db_port=None, +): + """ Creates a Postgres schema. CLI Example: @@ -1944,32 +2240,42 @@ def schema_create(dbname, name, owner=None, user='user' \\ db_user='user' db_password='password' db_host='hostname' db_port='port' - ''' + """ # check if schema exists - if schema_exists(dbname, name, user=user, - db_user=db_user, db_password=db_password, - db_host=db_host, db_port=db_port): - log.info('\'%s\' already exists in \'%s\'', name, dbname) + if schema_exists( + dbname, + name, + user=user, + db_user=db_user, + db_password=db_password, + db_host=db_host, + db_port=db_port, + ): + log.info("'%s' already exists in '%s'", name, dbname) return False sub_cmd = 'CREATE SCHEMA "{0}"'.format(name) if owner is not None: sub_cmd = '{0} AUTHORIZATION "{1}"'.format(sub_cmd, owner) - ret = _psql_prepare_and_run(['-c', sub_cmd], - user=db_user, password=db_password, - port=db_port, host=db_host, - maintenance_db=dbname, runas=user) + ret = _psql_prepare_and_run( + ["-c", sub_cmd], + user=db_user, + password=db_password, + port=db_port, + host=db_host, + maintenance_db=dbname, + runas=user, + ) - return ret['retcode'] == 0 + return ret["retcode"] == 0 -def schema_remove(dbname, name, - user=None, - db_user=None, db_password=None, - db_host=None, db_port=None): - ''' +def schema_remove( + dbname, name, user=None, db_user=None, db_password=None, db_host=None, db_port=None +): + """ Removes a schema from the Postgres server. CLI Example: @@ -1999,36 +2305,52 @@ def schema_remove(dbname, name, db_port Database port if different from config or default - ''' + """ # check if schema exists - if not schema_exists(dbname, name, user=None, - db_user=db_user, db_password=db_password, - db_host=db_host, db_port=db_port): - log.info('Schema \'%s\' does not exist in \'%s\'', name, dbname) + if not schema_exists( + dbname, + name, + user=None, + db_user=db_user, + db_password=db_password, + db_host=db_host, + db_port=db_port, + ): + log.info("Schema '%s' does not exist in '%s'", name, dbname) return False # schema exists, proceed sub_cmd = 'DROP SCHEMA "{0}"'.format(name) _psql_prepare_and_run( - ['-c', sub_cmd], + ["-c", sub_cmd], runas=user, maintenance_db=dbname, - host=db_host, user=db_user, port=db_port, password=db_password) + host=db_host, + user=db_user, + port=db_port, + password=db_password, + ) - if not schema_exists(dbname, name, user, - db_user=db_user, db_password=db_password, - db_host=db_host, db_port=db_port): + if not schema_exists( + dbname, + name, + user, + db_user=db_user, + db_password=db_password, + db_host=db_host, + db_port=db_port, + ): return True else: - log.info('Failed to delete schema \'%s\'.', name) + log.info("Failed to delete schema '%s'.", name) return False -def schema_exists(dbname, name, user=None, - db_user=None, db_password=None, - db_host=None, db_port=None): - ''' +def schema_exists( + dbname, name, user=None, db_user=None, db_password=None, db_host=None, db_port=None +): + """ Checks if a schema exists on the Postgres server. CLI Example: @@ -2058,19 +2380,24 @@ def schema_exists(dbname, name, user=None, db_port Database port if different from config or default - ''' + """ return bool( - schema_get(dbname, name, user=user, - db_user=db_user, - db_host=db_host, - db_port=db_port, - db_password=db_password)) + schema_get( + dbname, + name, + user=user, + db_user=db_user, + db_host=db_host, + db_port=db_port, + db_password=db_password, + ) + ) -def schema_get(dbname, name, user=None, - db_user=None, db_password=None, - db_host=None, db_port=None): - ''' +def schema_get( + dbname, name, user=None, db_user=None, db_password=None, db_host=None, db_port=None +): + """ Return a dict with information about schemas in a database. CLI Example: @@ -2099,23 +2426,26 @@ def schema_get(dbname, name, user=None, db_port Database port if different from config or default - ''' - all_schemas = schema_list(dbname, user=user, - db_user=db_user, - db_host=db_host, - db_port=db_port, - db_password=db_password) + """ + all_schemas = schema_list( + dbname, + user=user, + db_user=db_user, + db_host=db_host, + db_port=db_port, + db_password=db_password, + ) try: return all_schemas.get(name, None) except AttributeError: - log.error('Could not retrieve Postgres schema. Is Postgres running?') + log.error("Could not retrieve Postgres schema. Is Postgres running?") return False -def schema_list(dbname, user=None, - db_user=None, db_password=None, - db_host=None, db_port=None): - ''' +def schema_list( + dbname, user=None, db_user=None, db_password=None, db_host=None, db_port=None +): + """ Return a dict with information about schemas in a Postgres database. CLI Example: @@ -2141,43 +2471,44 @@ def schema_list(dbname, user=None, db_port Database port if different from config or default - ''' + """ ret = {} - query = (''.join([ - 'SELECT ' - 'pg_namespace.nspname as "name",' - 'pg_namespace.nspacl as "acl", ' - 'pg_roles.rolname as "owner" ' - 'FROM pg_namespace ' - 'LEFT JOIN pg_roles ON pg_roles.oid = pg_namespace.nspowner ' - ])) + query = "".join( + [ + "SELECT " + 'pg_namespace.nspname as "name",' + 'pg_namespace.nspacl as "acl", ' + 'pg_roles.rolname as "owner" ' + "FROM pg_namespace " + "LEFT JOIN pg_roles ON pg_roles.oid = pg_namespace.nspowner " + ] + ) - rows = psql_query(query, runas=user, - host=db_host, - user=db_user, - port=db_port, - maintenance_db=dbname, - password=db_password) + rows = psql_query( + query, + runas=user, + host=db_host, + user=db_user, + port=db_port, + maintenance_db=dbname, + password=db_password, + ) for row in rows: retrow = {} - for key in ('owner', 'acl'): + for key in ("owner", "acl"): retrow[key] = row[key] - ret[row['name']] = retrow + ret[row["name"]] = retrow return ret def language_list( - maintenance_db, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' + maintenance_db, user=None, host=None, port=None, password=None, runas=None +): + """ .. versionadded:: 2016.3.0 Return a list of languages in a database. @@ -2205,7 +2536,7 @@ def language_list( runas System user all operations should be performed on behalf of - ''' + """ ret = {} query = 'SELECT lanname AS "Name" FROM pg_language' @@ -2217,23 +2548,19 @@ def language_list( user=user, port=port, maintenance_db=maintenance_db, - password=password) + password=password, + ) for row in rows: - ret[row['Name']] = row['Name'] + ret[row["Name"]] = row["Name"] return ret def language_exists( - name, - maintenance_db, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' + name, maintenance_db, user=None, host=None, port=None, password=None, runas=None +): + """ .. versionadded:: 2016.3.0 Checks if language exists in a database. @@ -2265,24 +2592,19 @@ def language_exists( runas System user all operations should be performed on behalf of - ''' + """ languages = language_list( - maintenance_db, user=user, host=host, - port=port, password=password, - runas=runas) + maintenance_db, user=user, host=host, port=port, password=password, runas=runas + ) return name in languages -def language_create(name, - maintenance_db, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' +def language_create( + name, maintenance_db, user=None, host=None, port=None, password=None, runas=None +): + """ .. versionadded:: 2016.3.0 Installs a language into a database @@ -2313,33 +2635,31 @@ def language_create(name, runas System user all operations should be performed on behalf of - ''' + """ if language_exists(name, maintenance_db): - log.info('Language %s already exists in %s', name, maintenance_db) + log.info("Language %s already exists in %s", name, maintenance_db) return False - query = 'CREATE LANGUAGE {0}'.format(name) + query = "CREATE LANGUAGE {0}".format(name) - ret = _psql_prepare_and_run(['-c', query], - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) - return ret['retcode'] == 0 + return ret["retcode"] == 0 -def language_remove(name, - maintenance_db, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' +def language_remove( + name, maintenance_db, user=None, host=None, port=None, password=None, runas=None +): + """ .. versionadded:: 2016.3.0 Removes a language from a database @@ -2370,178 +2690,242 @@ def language_remove(name, runas System user all operations should be performed on behalf of - ''' + """ if not language_exists(name, maintenance_db): - log.info('Language %s does not exist in %s', name, maintenance_db) + log.info("Language %s does not exist in %s", name, maintenance_db) return False - query = 'DROP LANGUAGE {0}'.format(name) + query = "DROP LANGUAGE {0}".format(name) - ret = _psql_prepare_and_run(['-c', query], - user=user, - host=host, - port=port, - runas=runas, - maintenance_db=maintenance_db, - password=password) + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + runas=runas, + maintenance_db=maintenance_db, + password=password, + ) - return ret['retcode'] == 0 + return ret["retcode"] == 0 def _make_privileges_list_query(name, object_type, prepend): - ''' + """ Generate the SQL required for specific object type - ''' - if object_type == 'table': - query = (' '.join([ - 'SELECT relacl AS name', - 'FROM pg_catalog.pg_class c', - 'JOIN pg_catalog.pg_namespace n', - 'ON n.oid = c.relnamespace', - "WHERE nspname = '{0}'", - "AND relname = '{1}'", - "AND relkind = 'r'", - 'ORDER BY relname', - ])).format(prepend, name) - elif object_type == 'sequence': - query = (' '.join([ - 'SELECT relacl AS name', - 'FROM pg_catalog.pg_class c', - 'JOIN pg_catalog.pg_namespace n', - 'ON n.oid = c.relnamespace', - "WHERE nspname = '{0}'", - "AND relname = '{1}'", - "AND relkind = 'S'", - 'ORDER BY relname', - ])).format(prepend, name) - elif object_type == 'schema': - query = (' '.join([ - 'SELECT nspacl AS name', - 'FROM pg_catalog.pg_namespace', - "WHERE nspname = '{0}'", - 'ORDER BY nspname', - ])).format(name) - elif object_type == 'function': - query = (' '.join([ - 'SELECT proacl AS name', - 'FROM pg_catalog.pg_proc p', - 'JOIN pg_catalog.pg_namespace n', - 'ON n.oid = p.pronamespace', - "WHERE nspname = '{0}'", - "AND p.oid::regprocedure::text = '{1}'", - 'ORDER BY proname, proargtypes', - ])).format(prepend, name) - elif object_type == 'tablespace': - query = (' '.join([ - 'SELECT spcacl AS name', - 'FROM pg_catalog.pg_tablespace', - "WHERE spcname = '{0}'", - 'ORDER BY spcname', - ])).format(name) - elif object_type == 'language': - query = (' '.join([ - 'SELECT lanacl AS name', - 'FROM pg_catalog.pg_language', - "WHERE lanname = '{0}'", - 'ORDER BY lanname', - ])).format(name) - elif object_type == 'database': - query = (' '.join([ - 'SELECT datacl AS name', - 'FROM pg_catalog.pg_database', - "WHERE datname = '{0}'", - 'ORDER BY datname', - ])).format(name) - elif object_type == 'group': - query = (' '.join([ - 'SELECT rolname, admin_option', - 'FROM pg_catalog.pg_auth_members m', - 'JOIN pg_catalog.pg_roles r', - 'ON m.member=r.oid', - 'WHERE m.roleid IN', - '(SELECT oid', - 'FROM pg_catalog.pg_roles', - "WHERE rolname='{0}')", - 'ORDER BY rolname', - ])).format(name) + """ + if object_type == "table": + query = ( + " ".join( + [ + "SELECT relacl AS name", + "FROM pg_catalog.pg_class c", + "JOIN pg_catalog.pg_namespace n", + "ON n.oid = c.relnamespace", + "WHERE nspname = '{0}'", + "AND relname = '{1}'", + "AND relkind = 'r'", + "ORDER BY relname", + ] + ) + ).format(prepend, name) + elif object_type == "sequence": + query = ( + " ".join( + [ + "SELECT relacl AS name", + "FROM pg_catalog.pg_class c", + "JOIN pg_catalog.pg_namespace n", + "ON n.oid = c.relnamespace", + "WHERE nspname = '{0}'", + "AND relname = '{1}'", + "AND relkind = 'S'", + "ORDER BY relname", + ] + ) + ).format(prepend, name) + elif object_type == "schema": + query = ( + " ".join( + [ + "SELECT nspacl AS name", + "FROM pg_catalog.pg_namespace", + "WHERE nspname = '{0}'", + "ORDER BY nspname", + ] + ) + ).format(name) + elif object_type == "function": + query = ( + " ".join( + [ + "SELECT proacl AS name", + "FROM pg_catalog.pg_proc p", + "JOIN pg_catalog.pg_namespace n", + "ON n.oid = p.pronamespace", + "WHERE nspname = '{0}'", + "AND p.oid::regprocedure::text = '{1}'", + "ORDER BY proname, proargtypes", + ] + ) + ).format(prepend, name) + elif object_type == "tablespace": + query = ( + " ".join( + [ + "SELECT spcacl AS name", + "FROM pg_catalog.pg_tablespace", + "WHERE spcname = '{0}'", + "ORDER BY spcname", + ] + ) + ).format(name) + elif object_type == "language": + query = ( + " ".join( + [ + "SELECT lanacl AS name", + "FROM pg_catalog.pg_language", + "WHERE lanname = '{0}'", + "ORDER BY lanname", + ] + ) + ).format(name) + elif object_type == "database": + query = ( + " ".join( + [ + "SELECT datacl AS name", + "FROM pg_catalog.pg_database", + "WHERE datname = '{0}'", + "ORDER BY datname", + ] + ) + ).format(name) + elif object_type == "group": + query = ( + " ".join( + [ + "SELECT rolname, admin_option", + "FROM pg_catalog.pg_auth_members m", + "JOIN pg_catalog.pg_roles r", + "ON m.member=r.oid", + "WHERE m.roleid IN", + "(SELECT oid", + "FROM pg_catalog.pg_roles", + "WHERE rolname='{0}')", + "ORDER BY rolname", + ] + ) + ).format(name) return query -def _get_object_owner(name, - object_type, - prepend='public', - maintenance_db=None, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' +def _get_object_owner( + name, + object_type, + prepend="public", + maintenance_db=None, + user=None, + host=None, + port=None, + password=None, + runas=None, +): + """ Return the owner of a postgres object - ''' - if object_type == 'table': - query = (' '.join([ - 'SELECT tableowner AS name', - 'FROM pg_tables', - "WHERE schemaname = '{0}'", - "AND tablename = '{1}'" - ])).format(prepend, name) - elif object_type == 'sequence': - query = (' '.join([ - 'SELECT rolname AS name', - 'FROM pg_catalog.pg_class c', - 'JOIN pg_roles r', - 'ON c.relowner = r.oid', - 'JOIN pg_catalog.pg_namespace n', - 'ON n.oid = c.relnamespace', - "WHERE relkind='S'", - "AND nspname='{0}'", - "AND relname = '{1}'", - ])).format(prepend, name) - elif object_type == 'schema': - query = (' '.join([ - 'SELECT rolname AS name', - 'FROM pg_namespace n', - 'JOIN pg_roles r', - 'ON n.nspowner = r.oid', - "WHERE nspname = '{0}'", - ])).format(name) - elif object_type == 'function': - query = (' '.join([ - 'SELECT rolname AS name', - 'FROM pg_catalog.pg_proc p', - 'JOIN pg_catalog.pg_namespace n', - 'ON n.oid = p.pronamespace', - "WHERE nspname = '{0}'", - "AND p.oid::regprocedure::text = '{1}'", - 'ORDER BY proname, proargtypes', - ])).format(prepend, name) - elif object_type == 'tablespace': - query = (' '.join([ - 'SELECT rolname AS name', - 'FROM pg_tablespace t', - 'JOIN pg_roles r', - 'ON t.spcowner = r.oid', - "WHERE spcname = '{0}'", - ])).format(name) - elif object_type == 'language': - query = (' '.join([ - 'SELECT rolname AS name', - 'FROM pg_language l', - 'JOIN pg_roles r', - 'ON l.lanowner = r.oid', - "WHERE lanname = '{0}'", - ])).format(name) - elif object_type == 'database': - query = (' '.join([ - 'SELECT rolname AS name', - 'FROM pg_database d', - 'JOIN pg_roles r', - 'ON d.datdba = r.oid', - "WHERE datname = '{0}'", - ])).format(name) + """ + if object_type == "table": + query = ( + " ".join( + [ + "SELECT tableowner AS name", + "FROM pg_tables", + "WHERE schemaname = '{0}'", + "AND tablename = '{1}'", + ] + ) + ).format(prepend, name) + elif object_type == "sequence": + query = ( + " ".join( + [ + "SELECT rolname AS name", + "FROM pg_catalog.pg_class c", + "JOIN pg_roles r", + "ON c.relowner = r.oid", + "JOIN pg_catalog.pg_namespace n", + "ON n.oid = c.relnamespace", + "WHERE relkind='S'", + "AND nspname='{0}'", + "AND relname = '{1}'", + ] + ) + ).format(prepend, name) + elif object_type == "schema": + query = ( + " ".join( + [ + "SELECT rolname AS name", + "FROM pg_namespace n", + "JOIN pg_roles r", + "ON n.nspowner = r.oid", + "WHERE nspname = '{0}'", + ] + ) + ).format(name) + elif object_type == "function": + query = ( + " ".join( + [ + "SELECT rolname AS name", + "FROM pg_catalog.pg_proc p", + "JOIN pg_catalog.pg_namespace n", + "ON n.oid = p.pronamespace", + "WHERE nspname = '{0}'", + "AND p.oid::regprocedure::text = '{1}'", + "ORDER BY proname, proargtypes", + ] + ) + ).format(prepend, name) + elif object_type == "tablespace": + query = ( + " ".join( + [ + "SELECT rolname AS name", + "FROM pg_tablespace t", + "JOIN pg_roles r", + "ON t.spcowner = r.oid", + "WHERE spcname = '{0}'", + ] + ) + ).format(name) + elif object_type == "language": + query = ( + " ".join( + [ + "SELECT rolname AS name", + "FROM pg_language l", + "JOIN pg_roles r", + "ON l.lanowner = r.oid", + "WHERE lanname = '{0}'", + ] + ) + ).format(name) + elif object_type == "database": + query = ( + " ".join( + [ + "SELECT rolname AS name", + "FROM pg_database d", + "JOIN pg_roles r", + "ON d.datdba = r.oid", + "WHERE datname = '{0}'", + ] + ) + ).format(name) rows = psql_query( query, @@ -2550,9 +2934,10 @@ def _get_object_owner(name, user=user, port=port, maintenance_db=maintenance_db, - password=password) + password=password, + ) try: - ret = rows[0]['name'] + ret = rows[0]["name"] except IndexError: ret = None @@ -2560,44 +2945,46 @@ def _get_object_owner(name, def _validate_privileges(object_type, privs, privileges): - ''' + """ Validate the supplied privileges - ''' - if object_type != 'group': - _perms = [_PRIVILEGES_MAP[perm] - for perm in _PRIVILEGE_TYPE_MAP[object_type]] - _perms.append('ALL') + """ + if object_type != "group": + _perms = [_PRIVILEGES_MAP[perm] for perm in _PRIVILEGE_TYPE_MAP[object_type]] + _perms.append("ALL") if object_type not in _PRIVILEGES_OBJECTS: raise SaltInvocationError( - 'Invalid object_type: {0} provided'.format(object_type)) + "Invalid object_type: {0} provided".format(object_type) + ) if not set(privs).issubset(set(_perms)): raise SaltInvocationError( - 'Invalid privilege(s): {0} provided for object {1}'.format( - privileges, object_type)) + "Invalid privilege(s): {0} provided for object {1}".format( + privileges, object_type + ) + ) else: if privileges: raise SaltInvocationError( - 'The privileges option should not ' - 'be set for object_type group') + "The privileges option should not " "be set for object_type group" + ) def _mod_priv_opts(object_type, privileges): - ''' + """ Format options - ''' + """ object_type = object_type.lower() - privileges = '' if privileges is None else privileges - _privs = re.split(r'\s?,\s?', privileges.upper()) + privileges = "" if privileges is None else privileges + _privs = re.split(r"\s?,\s?", privileges.upper()) return object_type, privileges, _privs def _process_priv_part(perms): - ''' + """ Process part - ''' + """ _tmp = {} previous = None for perm in perms: @@ -2605,7 +2992,7 @@ def _process_priv_part(perms): _tmp[_PRIVILEGES_MAP[perm]] = False previous = _PRIVILEGES_MAP[perm] else: - if perm == '*': + if perm == "*": _tmp[previous] = True else: _tmp[_PRIVILEGES_MAP[perm]] = False @@ -2614,16 +3001,17 @@ def _process_priv_part(perms): def privileges_list( - name, - object_type, - prepend='public', - maintenance_db=None, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' + name, + object_type, + prepend="public", + maintenance_db=None, + user=None, + host=None, + port=None, + password=None, + runas=None, +): + """ .. versionadded:: 2016.3.0 Return a list of privileges for the specified object. @@ -2670,13 +3058,14 @@ def privileges_list( runas System user all operations should be performed on behalf of - ''' + """ object_type = object_type.lower() query = _make_privileges_list_query(name, object_type, prepend) if object_type not in _PRIVILEGES_OBJECTS: raise SaltInvocationError( - 'Invalid object_type: {0} provided'.format(object_type)) + "Invalid object_type: {0} provided".format(object_type) + ) rows = psql_query( query, @@ -2685,46 +3074,49 @@ def privileges_list( user=user, port=port, maintenance_db=maintenance_db, - password=password) + password=password, + ) ret = {} for row in rows: - if object_type != 'group': - result = row['name'] - result = result.strip('{}') - parts = result.split(',') + if object_type != "group": + result = row["name"] + result = result.strip("{}") + parts = result.split(",") for part in parts: - perms_part, _ = part.split('/') - rolename, perms = perms_part.split('=') - if rolename == '': - rolename = 'public' + perms_part, _ = part.split("/") + rolename, perms = perms_part.split("=") + if rolename == "": + rolename = "public" _tmp = _process_priv_part(perms) ret[rolename] = _tmp else: - if row['admin_option'] == 't': + if row["admin_option"] == "t": admin_option = True else: admin_option = False - ret[row['rolname']] = admin_option + ret[row["rolname"]] = admin_option return ret -def has_privileges(name, - object_name, - object_type, - privileges=None, - grant_option=None, - prepend='public', - maintenance_db=None, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' +def has_privileges( + name, + object_name, + object_type, + privileges=None, + grant_option=None, + prepend="public", + maintenance_db=None, + user=None, + host=None, + port=None, + password=None, + runas=None, +): + """ .. versionadded:: 2016.3.0 Check if a role has the specified privileges on an object @@ -2795,24 +3187,40 @@ def has_privileges(name, runas System user all operations should be performed on behalf of - ''' + """ object_type, privileges, _privs = _mod_priv_opts(object_type, privileges) _validate_privileges(object_type, _privs, privileges) - if object_type != 'group': - owner = _get_object_owner(object_name, object_type, prepend=prepend, - maintenance_db=maintenance_db, user=user, host=host, port=port, - password=password, runas=runas) + if object_type != "group": + owner = _get_object_owner( + object_name, + object_type, + prepend=prepend, + maintenance_db=maintenance_db, + user=user, + host=host, + port=port, + password=password, + runas=runas, + ) if owner is not None and name == owner: return True - _privileges = privileges_list(object_name, object_type, prepend=prepend, - maintenance_db=maintenance_db, user=user, host=host, port=port, - password=password, runas=runas) + _privileges = privileges_list( + object_name, + object_type, + prepend=prepend, + maintenance_db=maintenance_db, + user=user, + host=host, + port=port, + password=password, + runas=runas, + ) if name in _privileges: - if object_type == 'group': + if object_type == "group": if grant_option: retval = _privileges[name] else: @@ -2825,29 +3233,30 @@ def has_privileges(name, retval = perms == _privileges[name] else: perms = [_PRIVILEGES_MAP[perm] for perm in _perms] - if 'ALL' in _privs: + if "ALL" in _privs: retval = perms.sort() == _privileges[name].keys().sort() else: - retval = set(_privs).issubset( - set(_privileges[name].keys())) + retval = set(_privs).issubset(set(_privileges[name].keys())) return retval return False -def privileges_grant(name, - object_name, - object_type, - privileges=None, - grant_option=None, - prepend='public', - maintenance_db=None, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' +def privileges_grant( + name, + object_name, + object_type, + privileges=None, + grant_option=None, + prepend="public", + maintenance_db=None, + user=None, + host=None, + port=None, + password=None, + runas=None, +): + """ .. versionadded:: 2016.3.0 Grant privileges on a postgres object @@ -2919,73 +3328,94 @@ def privileges_grant(name, runas System user all operations should be performed on behalf of - ''' + """ object_type, privileges, _privs = _mod_priv_opts(object_type, privileges) _validate_privileges(object_type, _privs, privileges) - if has_privileges(name, object_name, object_type, privileges, - prepend=prepend, maintenance_db=maintenance_db, user=user, - host=host, port=port, password=password, runas=runas): - log.info('The object: %s of type: %s already has privileges: %s set', - object_name, object_type, privileges) + if has_privileges( + name, + object_name, + object_type, + privileges, + prepend=prepend, + maintenance_db=maintenance_db, + user=user, + host=host, + port=port, + password=password, + runas=runas, + ): + log.info( + "The object: %s of type: %s already has privileges: %s set", + object_name, + object_type, + privileges, + ) return False - _grants = ','.join(_privs) + _grants = ",".join(_privs) - if object_type in ['table', 'sequence']: + if object_type in ["table", "sequence"]: on_part = '{0}."{1}"'.format(prepend, object_name) - elif object_type == 'function': - on_part = '{0}'.format(object_name) + elif object_type == "function": + on_part = "{0}".format(object_name) else: on_part = '"{0}"'.format(object_name) if grant_option: - if object_type == 'group': - query = 'GRANT {0} TO "{1}" WITH ADMIN OPTION'.format( - object_name, name) - elif (object_type in ('table', 'sequence') and - object_name.upper() == 'ALL'): - query = 'GRANT {0} ON ALL {1}S IN SCHEMA {2} TO ' \ - '"{3}" WITH GRANT OPTION'.format( - _grants, object_type.upper(), prepend, name) + if object_type == "group": + query = 'GRANT {0} TO "{1}" WITH ADMIN OPTION'.format(object_name, name) + elif object_type in ("table", "sequence") and object_name.upper() == "ALL": + query = ( + "GRANT {0} ON ALL {1}S IN SCHEMA {2} TO " + '"{3}" WITH GRANT OPTION'.format( + _grants, object_type.upper(), prepend, name + ) + ) else: query = 'GRANT {0} ON {1} {2} TO "{3}" WITH GRANT OPTION'.format( - _grants, object_type.upper(), on_part, name) + _grants, object_type.upper(), on_part, name + ) else: - if object_type == 'group': + if object_type == "group": query = 'GRANT {0} TO "{1}"'.format(object_name, name) - elif (object_type in ('table', 'sequence') and - object_name.upper() == 'ALL'): + elif object_type in ("table", "sequence") and object_name.upper() == "ALL": query = 'GRANT {0} ON ALL {1}S IN SCHEMA {2} TO "{3}"'.format( - _grants, object_type.upper(), prepend, name) + _grants, object_type.upper(), prepend, name + ) else: query = 'GRANT {0} ON {1} {2} TO "{3}"'.format( - _grants, object_type.upper(), on_part, name) + _grants, object_type.upper(), on_part, name + ) - ret = _psql_prepare_and_run(['-c', query], - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) - return ret['retcode'] == 0 + return ret["retcode"] == 0 -def privileges_revoke(name, - object_name, - object_type, - privileges=None, - prepend='public', - maintenance_db=None, - user=None, - host=None, - port=None, - password=None, - runas=None): - ''' +def privileges_revoke( + name, + object_name, + object_type, + privileges=None, + prepend="public", + maintenance_db=None, + user=None, + host=None, + port=None, + password=None, + runas=None, +): + """ .. versionadded:: 2016.3.0 Revoke privileges on a postgres object @@ -3049,52 +3479,71 @@ def privileges_revoke(name, runas System user all operations should be performed on behalf of - ''' + """ object_type, privileges, _privs = _mod_priv_opts(object_type, privileges) _validate_privileges(object_type, _privs, privileges) - if not has_privileges(name, object_name, object_type, privileges, - prepend=prepend, maintenance_db=maintenance_db, user=user, - host=host, port=port, password=password, runas=runas): - log.info('The object: %s of type: %s does not' - ' have privileges: %s set', object_name, object_type, privileges) + if not has_privileges( + name, + object_name, + object_type, + privileges, + prepend=prepend, + maintenance_db=maintenance_db, + user=user, + host=host, + port=port, + password=password, + runas=runas, + ): + log.info( + "The object: %s of type: %s does not" " have privileges: %s set", + object_name, + object_type, + privileges, + ) return False - _grants = ','.join(_privs) + _grants = ",".join(_privs) - if object_type in ['table', 'sequence']: - on_part = '{0}.{1}'.format(prepend, object_name) + if object_type in ["table", "sequence"]: + on_part = "{0}.{1}".format(prepend, object_name) else: on_part = object_name - if object_type == 'group': - query = 'REVOKE {0} FROM {1}'.format(object_name, name) + if object_type == "group": + query = "REVOKE {0} FROM {1}".format(object_name, name) else: - query = 'REVOKE {0} ON {1} {2} FROM {3}'.format( - _grants, object_type.upper(), on_part, name) + query = "REVOKE {0} ON {1} {2} FROM {3}".format( + _grants, object_type.upper(), on_part, name + ) - ret = _psql_prepare_and_run(['-c', query], - user=user, - host=host, - port=port, - maintenance_db=maintenance_db, - password=password, - runas=runas) + ret = _psql_prepare_and_run( + ["-c", query], + user=user, + host=host, + port=port, + maintenance_db=maintenance_db, + password=password, + runas=runas, + ) - return ret['retcode'] == 0 + return ret["retcode"] == 0 -def datadir_init(name, - auth='password', - user=None, - password=None, - encoding='UTF8', - locale=None, - waldir=None, - checksums=False, - runas=None): - ''' +def datadir_init( + name, + auth="password", + user=None, + password=None, + encoding="UTF8", + locale=None, + waldir=None, + checksums=False, + runas=None, +): + """ .. versionadded:: 2016.3.0 Initializes a postgres data directory @@ -3139,9 +3588,9 @@ def datadir_init(name, runas The system user the operation should be performed on behalf of - ''' + """ if datadir_exists(name): - log.info('%s already exists', name) + log.info("%s already exists", name) return False ret = _run_initdb( @@ -3152,12 +3601,13 @@ def datadir_init(name, encoding=encoding, locale=locale, checksums=checksums, - runas=runas) - return ret['retcode'] == 0 + runas=runas, + ) + return ret["retcode"] == 0 def datadir_exists(name): - ''' + """ .. versionadded:: 2016.3.0 Checks if postgres data directory has been initialized @@ -3170,8 +3620,8 @@ def datadir_exists(name): name Name of the directory to check - ''' - _version_file = os.path.join(name, 'PG_VERSION') - _config_file = os.path.join(name, 'postgresql.conf') + """ + _version_file = os.path.join(name, "PG_VERSION") + _config_file = os.path.join(name, "postgresql.conf") return os.path.isfile(_version_file) and os.path.isfile(_config_file) diff --git a/salt/modules/poudriere.py b/salt/modules/poudriere.py index 4b494cf59a7..e03a4bdd163 100644 --- a/salt/modules/poudriere.py +++ b/salt/modules/poudriere.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Support for poudriere -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging # Import salt libs import salt.utils.files @@ -17,33 +18,36 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Module load on freebsd only and if poudriere installed - ''' - if __grains__['os'] == 'FreeBSD' and salt.utils.path.which('poudriere'): - return 'poudriere' + """ + if __grains__["os"] == "FreeBSD" and salt.utils.path.which("poudriere"): + return "poudriere" else: - return (False, 'The poudriere execution module failed to load: only available on FreeBSD with the poudriere binary in the path.') + return ( + False, + "The poudriere execution module failed to load: only available on FreeBSD with the poudriere binary in the path.", + ) def _config_file(): - ''' + """ Return the config file location to use - ''' - return __salt__['config.option']('poudriere.config') + """ + return __salt__["config.option"]("poudriere.config") def _config_dir(): - ''' + """ Return the configuration directory to use - ''' - return __salt__['config.option']('poudriere.config_dir') + """ + return __salt__["config.option"]("poudriere.config_dir") def _check_config_exists(config_file=None): - ''' + """ Verify the config file is present - ''' + """ if config_file is None: config_file = _config_file() if not os.path.isfile(config_file): @@ -52,7 +56,7 @@ def _check_config_exists(config_file=None): def is_jail(name): - ''' + """ Return True if jail exists False if not CLI Example: @@ -60,7 +64,7 @@ def is_jail(name): .. code-block:: bash salt '*' poudriere.is_jail - ''' + """ jails = list_jails() for jail in jails: if jail.split()[0] == name: @@ -69,7 +73,7 @@ def is_jail(name): def make_pkgng_aware(jname): - ''' + """ Make jail ``jname`` pkgng aware CLI Example: @@ -77,34 +81,35 @@ def make_pkgng_aware(jname): .. code-block:: bash salt '*' poudriere.make_pkgng_aware - ''' - ret = {'changes': {}} + """ + ret = {"changes": {}} cdir = _config_dir() # ensure cdir is there if not os.path.isdir(cdir): os.makedirs(cdir) if os.path.isdir(cdir): - ret['changes'] = 'Created poudriere make file dir {0}'.format(cdir) + ret["changes"] = "Created poudriere make file dir {0}".format(cdir) else: - return 'Could not create or find required directory {0}'.format( - cdir) + return "Could not create or find required directory {0}".format(cdir) # Added args to file - __salt__['file.write']('{0}-make.conf'.format(os.path.join(cdir, jname)), 'WITH_PKGNG=yes') + __salt__["file.write"]( + "{0}-make.conf".format(os.path.join(cdir, jname)), "WITH_PKGNG=yes" + ) - if os.path.isfile(os.path.join(cdir, jname) + '-make.conf'): - ret['changes'] = 'Created {0}'.format( - os.path.join(cdir, '{0}-make.conf'.format(jname)) - ) + if os.path.isfile(os.path.join(cdir, jname) + "-make.conf"): + ret["changes"] = "Created {0}".format( + os.path.join(cdir, "{0}-make.conf".format(jname)) + ) return ret else: - return 'Looks like file {0} could not be created'.format( - os.path.join(cdir, jname + '-make.conf') - ) + return "Looks like file {0} could not be created".format( + os.path.join(cdir, jname + "-make.conf") + ) def parse_config(config_file=None): - ''' + """ Returns a dict of poudriere main configuration definitions CLI Example: @@ -112,22 +117,22 @@ def parse_config(config_file=None): .. code-block:: bash salt '*' poudriere.parse_config - ''' + """ if config_file is None: config_file = _config_file() ret = {} if _check_config_exists(config_file): with salt.utils.files.fopen(config_file) as ifile: for line in ifile: - key, val = salt.utils.stringutils.to_unicode(line).split('=') + key, val = salt.utils.stringutils.to_unicode(line).split("=") ret[key] = val return ret - return 'Could not find {0} on file system'.format(config_file) + return "Could not find {0} on file system".format(config_file) def version(): - ''' + """ Return poudriere version CLI Example: @@ -135,13 +140,13 @@ def version(): .. code-block:: bash salt '*' poudriere.version - ''' + """ cmd = "poudriere version" - return __salt__['cmd.run'](cmd) + return __salt__["cmd.run"](cmd) def list_jails(): - ''' + """ Return a list of current jails managed by poudriere CLI Example: @@ -149,15 +154,15 @@ def list_jails(): .. code-block:: bash salt '*' poudriere.list_jails - ''' + """ _check_config_exists() - cmd = 'poudriere jails -l' - res = __salt__['cmd.run'](cmd) + cmd = "poudriere jails -l" + res = __salt__["cmd.run"](cmd) return res.splitlines() def list_ports(): - ''' + """ Return a list of current port trees managed by poudriere CLI Example: @@ -165,15 +170,15 @@ def list_ports(): .. code-block:: bash salt '*' poudriere.list_ports - ''' + """ _check_config_exists() - cmd = 'poudriere ports -l' - res = __salt__['cmd.run'](cmd).splitlines() + cmd = "poudriere ports -l" + res = __salt__["cmd.run"](cmd).splitlines() return res def create_jail(name, arch, version="9.0-RELEASE"): - ''' + """ Creates a new poudriere jail if one does not exist *NOTE* creating a new jail will take some time the master is not hanging @@ -183,29 +188,29 @@ def create_jail(name, arch, version="9.0-RELEASE"): .. code-block:: bash salt '*' poudriere.create_jail 90amd64 amd64 - ''' + """ # Config file must be on system to create a poudriere jail _check_config_exists() # Check if the jail is there if is_jail(name): - return '{0} already exists'.format(name) + return "{0} already exists".format(name) - cmd = 'poudriere jails -c -j {0} -v {1} -a {2}'.format(name, version, arch) - __salt__['cmd.run'](cmd) + cmd = "poudriere jails -c -j {0} -v {1} -a {2}".format(name, version, arch) + __salt__["cmd.run"](cmd) # Make jail pkgng aware make_pkgng_aware(name) # Make sure the jail was created if is_jail(name): - return 'Created jail {0}'.format(name) + return "Created jail {0}".format(name) - return 'Issue creating jail {0}'.format(name) + return "Issue creating jail {0}".format(name) def update_jail(name): - ''' + """ Run freebsd-update on `name` poudriere jail CLI Example: @@ -213,17 +218,17 @@ def update_jail(name): .. code-block:: bash salt '*' poudriere.update_jail freebsd:10:x86:64 - ''' + """ if is_jail(name): - cmd = 'poudriere jail -u -j {0}'.format(name) - ret = __salt__['cmd.run'](cmd) + cmd = "poudriere jail -u -j {0}".format(name) + ret = __salt__["cmd.run"](cmd) return ret else: - return 'Could not find jail {0}'.format(name) + return "Could not find jail {0}".format(name) def delete_jail(name): - ''' + """ Deletes poudriere jail with `name` CLI Example: @@ -231,44 +236,47 @@ def delete_jail(name): .. code-block:: bash salt '*' poudriere.delete_jail 90amd64 - ''' + """ if is_jail(name): - cmd = 'poudriere jail -d -j {0}'.format(name) - __salt__['cmd.run'](cmd) + cmd = "poudriere jail -d -j {0}".format(name) + __salt__["cmd.run"](cmd) # Make sure jail is gone if is_jail(name): - return 'Looks like there was an issue deleteing jail \ - {0}'.format(name) + return "Looks like there was an issue deleteing jail \ + {0}".format( + name + ) else: # Could not find jail. - return 'Looks like jail {0} has not been created'.format(name) + return "Looks like jail {0} has not been created".format(name) # clean up pkgng make info in config dir - make_file = os.path.join(_config_dir(), '{0}-make.conf'.format(name)) + make_file = os.path.join(_config_dir(), "{0}-make.conf".format(name)) if os.path.isfile(make_file): try: os.remove(make_file) except (IOError, OSError): - return ('Deleted jail "{0}" but was unable to remove jail make ' - 'file').format(name) - __salt__['file.remove'](make_file) + return ( + 'Deleted jail "{0}" but was unable to remove jail make ' "file" + ).format(name) + __salt__["file.remove"](make_file) - return 'Deleted jail {0}'.format(name) + return "Deleted jail {0}".format(name) def create_ports_tree(): - ''' + """ Not working need to run portfetch non interactive - ''' + """ _check_config_exists() - cmd = 'poudriere ports -c' - ret = __salt__['cmd.run'](cmd) + cmd = "poudriere ports -c" + ret = __salt__["cmd.run"](cmd) return ret def update_ports_tree(ports_tree): - ''' + """ Updates the ports tree, either the default or the `ports_tree` specified CLI Example: @@ -276,18 +284,18 @@ def update_ports_tree(ports_tree): .. code-block:: bash salt '*' poudriere.update_ports_tree staging - ''' + """ _check_config_exists() if ports_tree: - cmd = 'poudriere ports -u -p {0}'.format(ports_tree) + cmd = "poudriere ports -u -p {0}".format(ports_tree) else: - cmd = 'poudriere ports -u' - ret = __salt__['cmd.run'](cmd) + cmd = "poudriere ports -u" + ret = __salt__["cmd.run"](cmd) return ret def bulk_build(jail, pkg_file, keep=False): - ''' + """ Run bulk build on poudriere server. Return number of pkg builds, failures, and errors, on error dump to CLI @@ -298,24 +306,25 @@ def bulk_build(jail, pkg_file, keep=False): salt -N buildbox_group poudriere.bulk_build 90amd64 /root/pkg_list - ''' + """ # make sure `pkg file` and jail is on file system if not os.path.isfile(pkg_file): - return 'Could not find file {0} on filesystem'.format(pkg_file) + return "Could not find file {0} on filesystem".format(pkg_file) if not is_jail(jail): - return 'Could not find jail {0}'.format(jail) + return "Could not find jail {0}".format(jail) # Generate command if keep: - cmd = 'poudriere bulk -k -f {0} -j {1}'.format(pkg_file, jail) + cmd = "poudriere bulk -k -f {0} -j {1}".format(pkg_file, jail) else: - cmd = 'poudriere bulk -f {0} -j {1}'.format(pkg_file, jail) + cmd = "poudriere bulk -f {0} -j {1}".format(pkg_file, jail) # Bulk build this can take some time, depending on pkg_file ... hours - res = __salt__['cmd.run'](cmd) + res = __salt__["cmd.run"](cmd) lines = res.splitlines() for line in lines: if "packages built" in line: return line - return ('There may have been an issue building packages dumping output: ' - '{0}').format(res) + return ( + "There may have been an issue building packages dumping output: " "{0}" + ).format(res) diff --git a/salt/modules/powerpath.py b/salt/modules/powerpath.py index b00e46a6078..c68edc24ab2 100644 --- a/salt/modules/powerpath.py +++ b/salt/modules/powerpath.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" powerpath support. Assumes RedHat -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -12,115 +12,116 @@ import os import re POLICY_MAP_DICT = { - 'Adaptive': 'ad', - 'CLAROpt': 'co', - 'LeastBlocks': 'lb', - 'LeastIos': 'li', - 'REquest': 're', - 'RoundRobin': 'rr', - 'StreamIo': 'si', - 'SymmOpt': 'so', + "Adaptive": "ad", + "CLAROpt": "co", + "LeastBlocks": "lb", + "LeastIos": "li", + "REquest": "re", + "RoundRobin": "rr", + "StreamIo": "si", + "SymmOpt": "so", } -POLICY_RE = re.compile('.*policy=([^;]+)') +POLICY_RE = re.compile(".*policy=([^;]+)") def has_powerpath(): - if os.path.exists('/sbin/emcpreg'): + if os.path.exists("/sbin/emcpreg"): return True return False def __virtual__(): - ''' + """ Provide this only on Linux systems until proven to work elsewhere. - ''' + """ try: - kernel_grain = __grains__['kernel'] + kernel_grain = __grains__["kernel"] except Exception: # pylint: disable=broad-except - return (False, 'The powerpath execution module cannot be loaded: unable to detect kernel grain.') + return ( + False, + "The powerpath execution module cannot be loaded: unable to detect kernel grain.", + ) if not has_powerpath(): - return (False, 'The powerpath execution module cannot be loaded: the emcpreg binary is not available.') + return ( + False, + "The powerpath execution module cannot be loaded: the emcpreg binary is not available.", + ) - if kernel_grain == 'Linux': - return 'powerpath' + if kernel_grain == "Linux": + return "powerpath" - return (False, 'The powerpath execution module cannot be loaded: only available on Linux.') + return ( + False, + "The powerpath execution module cannot be loaded: only available on Linux.", + ) def list_licenses(): - ''' + """ returns a list of applied powerpath license keys - ''' - KEY_PATTERN = re.compile('Key (.*)') + """ + KEY_PATTERN = re.compile("Key (.*)") keys = [] - out = __salt__['cmd.run']('/sbin/emcpreg -list') + out = __salt__["cmd.run"]("/sbin/emcpreg -list") for line in out.splitlines(): match = KEY_PATTERN.match(line) if not match: continue - keys.append({'key': match.group(1)}) + keys.append({"key": match.group(1)}) return keys def add_license(key): - ''' + """ Add a license - ''' - result = { - 'result': False, - 'retcode': -1, - 'output': '' - } + """ + result = {"result": False, "retcode": -1, "output": ""} if not has_powerpath(): - result['output'] = 'PowerPath is not installed' + result["output"] = "PowerPath is not installed" return result - cmd = '/sbin/emcpreg -add {0}'.format(key) - ret = __salt__['cmd.run_all'](cmd, python_shell=True) + cmd = "/sbin/emcpreg -add {0}".format(key) + ret = __salt__["cmd.run_all"](cmd, python_shell=True) - result['retcode'] = ret['retcode'] + result["retcode"] = ret["retcode"] - if ret['retcode'] != 0: - result['output'] = ret['stderr'] + if ret["retcode"] != 0: + result["output"] = ret["stderr"] else: - result['output'] = ret['stdout'] - result['result'] = True + result["output"] = ret["stdout"] + result["result"] = True return result def remove_license(key): - ''' + """ Remove a license - ''' - result = { - 'result': False, - 'retcode': -1, - 'output': '' - } + """ + result = {"result": False, "retcode": -1, "output": ""} if not has_powerpath(): - result['output'] = 'PowerPath is not installed' + result["output"] = "PowerPath is not installed" return result - cmd = '/sbin/emcpreg -remove {0}'.format(key) - ret = __salt__['cmd.run_all'](cmd, python_shell=True) + cmd = "/sbin/emcpreg -remove {0}".format(key) + ret = __salt__["cmd.run_all"](cmd, python_shell=True) - result['retcode'] = ret['retcode'] + result["retcode"] = ret["retcode"] - if ret['retcode'] != 0: - result['output'] = ret['stderr'] + if ret["retcode"] != 0: + result["output"] = ret["stderr"] else: - result['output'] = ret['stdout'] - result['result'] = True + result["output"] = ret["stdout"] + result["result"] = True return result diff --git a/salt/modules/proxy.py b/salt/modules/proxy.py index 7cf92c251f0..a3fc71697e6 100644 --- a/salt/modules/proxy.py +++ b/salt/modules/proxy.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" This module allows you to manage proxy settings .. code-block:: bash salt '*' network.get_http_proxy -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import re @@ -16,23 +17,25 @@ import re import salt.utils.platform log = logging.getLogger(__name__) -__virtualname__ = 'proxy' +__virtualname__ = "proxy" def __virtual__(): - ''' + """ Only work on Mac OS and Windows - ''' + """ if salt.utils.platform.is_darwin() or salt.utils.platform.is_windows(): return True - return False, 'Module proxy: module only works on Windows or MacOS systems' + return False, "Module proxy: module only works on Windows or MacOS systems" def _get_proxy_osx(cmd_function, network_service): ret = {} - out = __salt__['cmd.run']('networksetup -{0} {1}'.format(cmd_function, network_service)) - match = re.match('Enabled: (.*)\nServer: (.*)\nPort: (.*)\n', out) + out = __salt__["cmd.run"]( + "networksetup -{0} {1}".format(cmd_function, network_service) + ) + match = re.match("Enabled: (.*)\nServer: (.*)\nPort: (.*)\n", out) if match is not None: g = match.groups() enabled = True if g[0] == "Yes" else False @@ -42,26 +45,29 @@ def _get_proxy_osx(cmd_function, network_service): def _set_proxy_osx(cmd_function, server, port, user, password, network_service): - cmd = 'networksetup -{0} {1} {2} {3}'.format(cmd_function, network_service, server, port) + cmd = "networksetup -{0} {1} {2} {3}".format( + cmd_function, network_service, server, port + ) if user is not None and password is not None: - cmd = cmd + ' On {0} {1}'.format(user, password) + cmd = cmd + " On {0} {1}".format(user, password) - out = __salt__['cmd.run'](cmd) + out = __salt__["cmd.run"](cmd) - return 'error' not in out + return "error" not in out def _get_proxy_windows(types=None): ret = {} if types is None: - types = ['http', 'https', 'ftp'] + types = ["http", "https", "ftp"] - servers = __salt__['reg.read_value']( - hive='HKEY_CURRENT_USER', - key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings', - vname='ProxyServer')['vdata'] + servers = __salt__["reg.read_value"]( + hive="HKEY_CURRENT_USER", + key=r"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings", + vname="ProxyServer", + )["vdata"] if servers and "=" in servers: split = servers.split(";") @@ -87,57 +93,62 @@ def _get_proxy_windows(types=None): del ret[key] # Return enabled info - ret['enabled'] = __salt__['reg.read_value']( - hive='HKEY_CURRENT_USER', - key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings', - vname='ProxyEnable')['vdata'] == 1 + ret["enabled"] = ( + __salt__["reg.read_value"]( + hive="HKEY_CURRENT_USER", + key=r"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings", + vname="ProxyEnable", + )["vdata"] + == 1 + ) return ret -def _set_proxy_windows(server, - port, - types=None, - bypass_hosts=None, - import_winhttp=True): +def _set_proxy_windows( + server, port, types=None, bypass_hosts=None, import_winhttp=True +): if types is None: - types = ['http', 'https', 'ftp'] + types = ["http", "https", "ftp"] - server_str = '' + server_str = "" for t in types: - server_str += '{0}={1}:{2};'.format(t, server, port) + server_str += "{0}={1}:{2};".format(t, server, port) - __salt__['reg.set_value']( - hive='HKEY_CURRENT_USER', - key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings', - vname='ProxyServer', - vdata=server_str) + __salt__["reg.set_value"]( + hive="HKEY_CURRENT_USER", + key=r"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings", + vname="ProxyServer", + vdata=server_str, + ) - __salt__['reg.set_value']( - hive='HKEY_CURRENT_USER', - key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings', - vname='ProxyEnable', + __salt__["reg.set_value"]( + hive="HKEY_CURRENT_USER", + key=r"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings", + vname="ProxyEnable", vdata=1, - vtype='REG_DWORD') + vtype="REG_DWORD", + ) if bypass_hosts is not None: - bypass_hosts_str = ';{0}'.format(';'.join(bypass_hosts)) + bypass_hosts_str = ";{0}".format(";".join(bypass_hosts)) - __salt__['reg.set_value']( - hive='HKEY_CURRENT_USER', - key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings', - vname='ProxyOverride', - vdata=bypass_hosts_str) + __salt__["reg.set_value"]( + hive="HKEY_CURRENT_USER", + key=r"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings", + vname="ProxyOverride", + vdata=bypass_hosts_str, + ) if import_winhttp: - cmd = 'netsh winhttp import proxy source=ie' - __salt__['cmd.run'](cmd) + cmd = "netsh winhttp import proxy source=ie" + __salt__["cmd.run"](cmd) return True def get_http_proxy(network_service="Ethernet"): - ''' + """ Returns the current http proxy settings network_service @@ -149,21 +160,22 @@ def get_http_proxy(network_service="Ethernet"): .. code-block:: bash salt '*' proxy.get_http_proxy Ethernet - ''' - if __grains__['os'] == 'Windows': - return _get_proxy_windows(types=['http']) + """ + if __grains__["os"] == "Windows": + return _get_proxy_windows(types=["http"]) - return _get_proxy_osx(cmd_function="getwebproxy", - network_service=network_service) + return _get_proxy_osx(cmd_function="getwebproxy", network_service=network_service) -def set_http_proxy(server, - port, - user=None, - password=None, - network_service="Ethernet", - bypass_hosts=None): - ''' +def set_http_proxy( + server, + port, + user=None, + password=None, + network_service="Ethernet", + bypass_hosts=None, +): + """ Sets the http proxy settings. Note: On Windows this will override any other proxy settings you have, the preferred method of updating proxies on windows is using set_proxy. @@ -193,23 +205,24 @@ def set_http_proxy(server, .. code-block:: bash salt '*' proxy.set_http_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet - ''' - if __grains__['os'] == 'Windows': - return _set_proxy_windows(server=server, - port=port, - types=['http'], - bypass_hosts=bypass_hosts) + """ + if __grains__["os"] == "Windows": + return _set_proxy_windows( + server=server, port=port, types=["http"], bypass_hosts=bypass_hosts + ) - return _set_proxy_osx(cmd_function="setwebproxy", - server=server, - port=port, - user=user, - password=password, - network_service=network_service) + return _set_proxy_osx( + cmd_function="setwebproxy", + server=server, + port=port, + user=user, + password=password, + network_service=network_service, + ) def get_https_proxy(network_service="Ethernet"): - ''' + """ Returns the current https proxy settings network_service @@ -221,21 +234,24 @@ def get_https_proxy(network_service="Ethernet"): .. code-block:: bash salt '*' proxy.get_https_proxy Ethernet - ''' - if __grains__['os'] == 'Windows': - return _get_proxy_windows(types=['https']) + """ + if __grains__["os"] == "Windows": + return _get_proxy_windows(types=["https"]) - return _get_proxy_osx(cmd_function="getsecurewebproxy", - network_service=network_service) + return _get_proxy_osx( + cmd_function="getsecurewebproxy", network_service=network_service + ) -def set_https_proxy(server, - port, - user=None, - password=None, - network_service="Ethernet", - bypass_hosts=None): - ''' +def set_https_proxy( + server, + port, + user=None, + password=None, + network_service="Ethernet", + bypass_hosts=None, +): + """ Sets the https proxy settings. Note: On Windows this will override any other proxy settings you have, the preferred method of updating proxies on windows is using set_proxy. @@ -265,23 +281,24 @@ def set_https_proxy(server, .. code-block:: bash salt '*' proxy.set_https_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet - ''' - if __grains__['os'] == 'Windows': - return _set_proxy_windows(server=server, - port=port, - types=['https'], - bypass_hosts=bypass_hosts) + """ + if __grains__["os"] == "Windows": + return _set_proxy_windows( + server=server, port=port, types=["https"], bypass_hosts=bypass_hosts + ) - return _set_proxy_osx(cmd_function="setsecurewebproxy", - server=server, - port=port, - user=user, - password=password, - network_service=network_service) + return _set_proxy_osx( + cmd_function="setsecurewebproxy", + server=server, + port=port, + user=user, + password=password, + network_service=network_service, + ) def get_ftp_proxy(network_service="Ethernet"): - ''' + """ Returns the current ftp proxy settings network_service @@ -293,21 +310,22 @@ def get_ftp_proxy(network_service="Ethernet"): .. code-block:: bash salt '*' proxy.get_ftp_proxy Ethernet - ''' - if __grains__['os'] == 'Windows': - return _get_proxy_windows(types=['ftp']) + """ + if __grains__["os"] == "Windows": + return _get_proxy_windows(types=["ftp"]) - return _get_proxy_osx(cmd_function="getftpproxy", - network_service=network_service) + return _get_proxy_osx(cmd_function="getftpproxy", network_service=network_service) -def set_ftp_proxy(server, - port, - user=None, - password=None, - network_service="Ethernet", - bypass_hosts=None): - ''' +def set_ftp_proxy( + server, + port, + user=None, + password=None, + network_service="Ethernet", + bypass_hosts=None, +): + """ Sets the ftp proxy settings server @@ -335,23 +353,24 @@ def set_ftp_proxy(server, .. code-block:: bash salt '*' proxy.set_ftp_proxy example.com 1080 user=proxy_user password=proxy_pass network_service=Ethernet - ''' - if __grains__['os'] == 'Windows': - return _set_proxy_windows(server=server, - port=port, - types=['ftp'], - bypass_hosts=bypass_hosts) + """ + if __grains__["os"] == "Windows": + return _set_proxy_windows( + server=server, port=port, types=["ftp"], bypass_hosts=bypass_hosts + ) - return _set_proxy_osx(cmd_function="setftpproxy", - server=server, - port=port, - user=user, - password=password, - network_service=network_service) + return _set_proxy_osx( + cmd_function="setftpproxy", + server=server, + port=port, + user=user, + password=password, + network_service=network_service, + ) def get_proxy_bypass(network_service="Ethernet"): - ''' + """ Returns the current domains that can bypass the proxy network_service @@ -364,26 +383,29 @@ def get_proxy_bypass(network_service="Ethernet"): salt '*' proxy.get_proxy_bypass - ''' - if __grains__['os'] == 'Windows': - reg_val = __salt__['reg.read_value']( - hive='HKEY_CURRENT_USER', - key=r'SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings', - vname='ProxyOverride')['vdata'] + """ + if __grains__["os"] == "Windows": + reg_val = __salt__["reg.read_value"]( + hive="HKEY_CURRENT_USER", + key=r"SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings", + vname="ProxyOverride", + )["vdata"] # `reg.read_value` returns None if the key doesn't exist if reg_val is None: return [] - return reg_val.replace('', '').split(';') + return reg_val.replace("", "").split(";") - out = __salt__['cmd.run']('networksetup -getproxybypassdomains {0}'.format(network_service)) + out = __salt__["cmd.run"]( + "networksetup -getproxybypassdomains {0}".format(network_service) + ) return out.split("\n") def set_proxy_bypass(domains, network_service="Ethernet"): - ''' + """ Sets the domains that can bypass the proxy domains @@ -399,16 +421,18 @@ def set_proxy_bypass(domains, network_service="Ethernet"): salt '*' proxy.set_proxy_bypass "['127.0.0.1', 'localhost']" - ''' - servers_str = ' '.join(domains) - cmd = 'networksetup -setproxybypassdomains {0} {1}'.format(network_service, servers_str,) - out = __salt__['cmd.run'](cmd) + """ + servers_str = " ".join(domains) + cmd = "networksetup -setproxybypassdomains {0} {1}".format( + network_service, servers_str, + ) + out = __salt__["cmd.run"](cmd) - return 'error' not in out + return "error" not in out def set_proxy_win(server, port, types=None, bypass_hosts=None): - ''' + """ Sets the http proxy settings, only works with Windows. server @@ -433,16 +457,15 @@ def set_proxy_win(server, port, types=None, bypass_hosts=None): .. code-block:: bash salt '*' proxy.set_http_proxy example.com 1080 types="['http', 'https']" - ''' - if __grains__['os'] == 'Windows': - return _set_proxy_windows(server=server, - port=port, - types=types, - bypass_hosts=bypass_hosts) + """ + if __grains__["os"] == "Windows": + return _set_proxy_windows( + server=server, port=port, types=types, bypass_hosts=bypass_hosts + ) def get_proxy_win(): - ''' + """ Gets all of the proxy settings in one call, only available on Windows CLI Example: @@ -450,6 +473,6 @@ def get_proxy_win(): .. code-block:: bash salt '*' proxy.get_proxy_win - ''' - if __grains__['os'] == 'Windows': + """ + if __grains__["os"] == "Windows": return _get_proxy_windows() diff --git a/salt/modules/ps.py b/salt/modules/ps.py index bb37873f48c..ab883519931 100644 --- a/salt/modules/ps.py +++ b/salt/modules/ps.py @@ -1,31 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" A salt interface to psutil, a system and process library. See http://code.google.com/p/psutil. :depends: - psutil Python module, version 0.3.0 or later - python-utmp package (optional) -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import time +from __future__ import absolute_import, print_function, unicode_literals + import datetime import re +import time # Import salt libs import salt.utils.data -from salt.exceptions import SaltInvocationError, CommandExecutionError # Import third party libs import salt.utils.decorators.path +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six + # pylint: disable=import-error try: import salt.utils.psutil_compat as psutil HAS_PSUTIL = True - PSUTIL2 = getattr(psutil, 'version_info', ()) >= (2, 0) + PSUTIL2 = getattr(psutil, "version_info", ()) >= (2, 0) except ImportError: HAS_PSUTIL = False # pylint: enable=import-error @@ -33,7 +35,10 @@ except ImportError: def __virtual__(): if not HAS_PSUTIL: - return False, 'The ps module cannot be loaded: python module psutil not installed.' + return ( + False, + "The ps module cannot be loaded: python module psutil not installed.", + ) # Functions and attributes used in this execution module seem to have been # added as of psutil 0.3.0, from an inspection of the source code. Only @@ -44,15 +49,20 @@ def __virtual__(): # as of Dec. 2013 EPEL is on 0.6.1, Debian 7 is on 0.5.1, etc.). if psutil.version_info >= (0, 3, 0): return True - return (False, 'The ps execution module cannot be loaded: the psutil python module version {0} is less than 0.3.0'.format(psutil.version_info)) + return ( + False, + "The ps execution module cannot be loaded: the psutil python module version {0} is less than 0.3.0".format( + psutil.version_info + ), + ) def _get_proc_cmdline(proc): - ''' + """ Returns the cmdline of a Process instance. It's backward compatible with < 2.0 versions of psutil. - ''' + """ try: return salt.utils.data.decode(proc.cmdline() if PSUTIL2 else proc.cmdline) except (psutil.NoSuchProcess, psutil.AccessDenied): @@ -60,23 +70,25 @@ def _get_proc_cmdline(proc): def _get_proc_create_time(proc): - ''' + """ Returns the create_time of a Process instance. It's backward compatible with < 2.0 versions of psutil. - ''' + """ try: - return salt.utils.data.decode(proc.create_time() if PSUTIL2 else proc.create_time) + return salt.utils.data.decode( + proc.create_time() if PSUTIL2 else proc.create_time + ) except (psutil.NoSuchProcess, psutil.AccessDenied): return None def _get_proc_name(proc): - ''' + """ Returns the name of a Process instance. It's backward compatible with < 2.0 versions of psutil. - ''' + """ try: return salt.utils.data.decode(proc.name() if PSUTIL2 else proc.name) except (psutil.NoSuchProcess, psutil.AccessDenied): @@ -84,11 +96,11 @@ def _get_proc_name(proc): def _get_proc_status(proc): - ''' + """ Returns the status of a Process instance. It's backward compatible with < 2.0 versions of psutil. - ''' + """ try: return salt.utils.data.decode(proc.status() if PSUTIL2 else proc.status) except (psutil.NoSuchProcess, psutil.AccessDenied): @@ -96,11 +108,11 @@ def _get_proc_status(proc): def _get_proc_username(proc): - ''' + """ Returns the username of a Process instance. It's backward compatible with < 2.0 versions of psutil. - ''' + """ try: return salt.utils.data.decode(proc.username() if PSUTIL2 else proc.username) except (psutil.NoSuchProcess, psutil.AccessDenied, KeyError): @@ -108,16 +120,16 @@ def _get_proc_username(proc): def _get_proc_pid(proc): - ''' + """ Returns the pid of a Process instance. It's backward compatible with < 2.0 versions of psutil. - ''' + """ return proc.pid def top(num_processes=5, interval=3): - ''' + """ Return a list of top CPU consuming processes during the interval. num_processes = return the top N CPU consuming processes interval = the number of seconds to sample CPU usage over @@ -129,7 +141,7 @@ def top(num_processes=5, interval=3): salt '*' ps.top salt '*' ps.top 5 10 - ''' + """ result = [] start_usage = {} for pid in psutil.pids(): @@ -161,25 +173,26 @@ def top(num_processes=5, interval=3): cmdline = _get_proc_name(process) else: cmdline = _get_proc_cmdline(process) - info = {'cmd': cmdline, - 'user': _get_proc_username(process), - 'status': _get_proc_status(process), - 'pid': _get_proc_pid(process), - 'create_time': _get_proc_create_time(process), - 'cpu': {}, - 'mem': {}, + info = { + "cmd": cmdline, + "user": _get_proc_username(process), + "status": _get_proc_status(process), + "pid": _get_proc_pid(process), + "create_time": _get_proc_create_time(process), + "cpu": {}, + "mem": {}, } for key, value in six.iteritems(process.cpu_times()._asdict()): - info['cpu'][key] = value + info["cpu"][key] = value for key, value in six.iteritems(process.memory_info()._asdict()): - info['mem'][key] = value + info["mem"][key] = value result.append(info) return result def get_pid_list(): - ''' + """ Return a list of process ids (PIDs) for all running processes. CLI Example: @@ -187,12 +200,12 @@ def get_pid_list(): .. code-block:: bash salt '*' ps.get_pid_list - ''' + """ return psutil.pids() def proc_info(pid, attrs=None): - ''' + """ Return a dictionary of information for a process id (PID). CLI Example: @@ -209,7 +222,7 @@ def proc_info(pid, attrs=None): Optional list of desired process attributes. The list of possible attributes can be found here: http://pythonhosted.org/psutil/#psutil.Process - ''' + """ try: proc = psutil.Process(pid) return proc.as_dict(attrs) @@ -218,7 +231,7 @@ def proc_info(pid, attrs=None): def kill_pid(pid, signal=15): - ''' + """ Kill a process by PID. .. code-block:: bash @@ -239,7 +252,7 @@ def kill_pid(pid, signal=15): .. code-block:: bash salt 'minion' ps.kill_pid 2000 signal=9 - ''' + """ try: psutil.Process(pid).send_signal(signal) return True @@ -248,7 +261,7 @@ def kill_pid(pid, signal=15): def pkill(pattern, user=None, signal=15, full=False): - ''' + """ Kill processes matching a pattern. .. code-block:: bash @@ -283,12 +296,15 @@ def pkill(pattern, user=None, signal=15, full=False): .. code-block:: bash salt '*' ps.pkill bash signal=9 user=tom - ''' + """ killed = [] for proc in psutil.process_iter(): - name_match = pattern in ' '.join(_get_proc_cmdline(proc)) if full \ + name_match = ( + pattern in " ".join(_get_proc_cmdline(proc)) + if full else pattern in _get_proc_name(proc) + ) user_match = True if user is None else user == _get_proc_username(proc) if name_match and user_match: try: @@ -299,11 +315,11 @@ def pkill(pattern, user=None, signal=15, full=False): if not killed: return None else: - return {'killed': killed} + return {"killed": killed} def pgrep(pattern, user=None, full=False): - ''' + """ Return the pids for processes matching a pattern. If full is true, the full command line is searched for a match, @@ -336,12 +352,15 @@ def pgrep(pattern, user=None, full=False): .. code-block:: bash salt '*' ps.pgrep bash user=tom - ''' + """ procs = [] for proc in psutil.process_iter(): - name_match = pattern in ' '.join(_get_proc_cmdline(proc)) if full \ + name_match = ( + pattern in " ".join(_get_proc_cmdline(proc)) + if full else pattern in _get_proc_name(proc) + ) user_match = True if user is None else user == _get_proc_username(proc) if name_match and user_match: procs.append(_get_proc_pid(proc)) @@ -349,7 +368,7 @@ def pgrep(pattern, user=None, full=False): def cpu_percent(interval=0.1, per_cpu=False): - ''' + """ Return the percent of time the CPU is busy. interval @@ -363,7 +382,7 @@ def cpu_percent(interval=0.1, per_cpu=False): .. code-block:: bash salt '*' ps.cpu_percent - ''' + """ if per_cpu: result = list(psutil.cpu_percent(interval, True)) else: @@ -372,7 +391,7 @@ def cpu_percent(interval=0.1, per_cpu=False): def cpu_times(per_cpu=False): - ''' + """ Return the percent of time the CPU spends in each state, e.g. user, system, idle, nice, iowait, irq, softirq. @@ -385,7 +404,7 @@ def cpu_times(per_cpu=False): .. code-block:: bash salt '*' ps.cpu_times - ''' + """ if per_cpu: result = [dict(times._asdict()) for times in psutil.cpu_times(True)] else: @@ -394,7 +413,7 @@ def cpu_times(per_cpu=False): def virtual_memory(): - ''' + """ .. versionadded:: 2014.7.0 Return a dict that describes statistics about system memory usage. @@ -408,15 +427,15 @@ def virtual_memory(): .. code-block:: bash salt '*' ps.virtual_memory - ''' + """ if psutil.version_info < (0, 6, 0): - msg = 'virtual_memory is only available in psutil 0.6.0 or greater' + msg = "virtual_memory is only available in psutil 0.6.0 or greater" raise CommandExecutionError(msg) return dict(psutil.virtual_memory()._asdict()) def swap_memory(): - ''' + """ .. versionadded:: 2014.7.0 Return a dict that describes swap memory statistics. @@ -430,15 +449,15 @@ def swap_memory(): .. code-block:: bash salt '*' ps.swap_memory - ''' + """ if psutil.version_info < (0, 6, 0): - msg = 'swap_memory is only available in psutil 0.6.0 or greater' + msg = "swap_memory is only available in psutil 0.6.0 or greater" raise CommandExecutionError(msg) return dict(psutil.swap_memory()._asdict()) def disk_partitions(all=False): - ''' + """ Return a list of disk partitions and their device, mount point, and filesystem type. @@ -451,14 +470,13 @@ def disk_partitions(all=False): .. code-block:: bash salt '*' ps.disk_partitions - ''' - result = [dict(partition._asdict()) for partition in - psutil.disk_partitions(all)] + """ + result = [dict(partition._asdict()) for partition in psutil.disk_partitions(all)] return result def disk_usage(path): - ''' + """ Given a path, return a dict listing the total available space as well as the free space, and used space. @@ -467,12 +485,12 @@ def disk_usage(path): .. code-block:: bash salt '*' ps.disk_usage /home - ''' + """ return dict(psutil.disk_usage(path)._asdict()) def disk_partition_usage(all=False): - ''' + """ Return a list of disk partitions plus the mount point, filesystem and usage statistics. @@ -481,15 +499,15 @@ def disk_partition_usage(all=False): .. code-block:: bash salt '*' ps.disk_partition_usage - ''' + """ result = disk_partitions(all) for partition in result: - partition.update(disk_usage(partition['mountpoint'])) + partition.update(disk_usage(partition["mountpoint"])) return result def total_physical_memory(): - ''' + """ Return the total number of bytes of physical memory. CLI Example: @@ -497,9 +515,9 @@ def total_physical_memory(): .. code-block:: bash salt '*' ps.total_physical_memory - ''' + """ if psutil.version_info < (0, 6, 0): - msg = 'virtual_memory is only available in psutil 0.6.0 or greater' + msg = "virtual_memory is only available in psutil 0.6.0 or greater" raise CommandExecutionError(msg) try: return psutil.virtual_memory().total @@ -510,7 +528,7 @@ def total_physical_memory(): def num_cpus(): - ''' + """ Return the number of CPUs. CLI Example: @@ -518,7 +536,7 @@ def num_cpus(): .. code-block:: bash salt '*' ps.num_cpus - ''' + """ try: return psutil.cpu_count() except AttributeError: @@ -528,7 +546,7 @@ def num_cpus(): def boot_time(time_format=None): - ''' + """ Return the boot time in number of seconds since the epoch began. CLI Example: @@ -545,7 +563,7 @@ def boot_time(time_format=None): .. code-block:: bash salt '*' ps.boot_time - ''' + """ try: b_time = int(psutil.boot_time()) except AttributeError: @@ -558,12 +576,12 @@ def boot_time(time_format=None): try: return b_time.strftime(time_format) except TypeError as exc: - raise SaltInvocationError('Invalid format string: {0}'.format(exc)) + raise SaltInvocationError("Invalid format string: {0}".format(exc)) return b_time def network_io_counters(interface=None): - ''' + """ Return network I/O statistics. CLI Example: @@ -573,7 +591,7 @@ def network_io_counters(interface=None): salt '*' ps.network_io_counters salt '*' ps.network_io_counters interface=eth0 - ''' + """ if not interface: return dict(psutil.net_io_counters()._asdict()) else: @@ -585,7 +603,7 @@ def network_io_counters(interface=None): def disk_io_counters(device=None): - ''' + """ Return disk I/O statistics. CLI Example: @@ -595,7 +613,7 @@ def disk_io_counters(device=None): salt '*' ps.disk_io_counters salt '*' ps.disk_io_counters device=sda1 - ''' + """ if not device: return dict(psutil.disk_io_counters()._asdict()) else: @@ -607,7 +625,7 @@ def disk_io_counters(device=None): def get_users(): - ''' + """ Return logged-in users. CLI Example: @@ -615,7 +633,7 @@ def get_users(): .. code-block:: bash salt '*' ps.get_users - ''' + """ try: recs = psutil.users() return [dict(x._asdict()) for x in recs] @@ -634,14 +652,20 @@ def get_users(): started = rec[8] if isinstance(started, tuple): started = started[0] - result.append({'name': rec[4], 'terminal': rec[2], - 'started': started, 'host': rec[5]}) + result.append( + { + "name": rec[4], + "terminal": rec[2], + "started": started, + "host": rec[5], + } + ) except ImportError: return False def lsof(name): - ''' + """ Retrieve the lsof information of the given process name. CLI Example: @@ -649,17 +673,17 @@ def lsof(name): .. code-block:: bash salt '*' ps.lsof apache2 - ''' + """ sanitize_name = six.text_type(name) - lsof_infos = __salt__['cmd.run']("lsof -c " + sanitize_name) + lsof_infos = __salt__["cmd.run"]("lsof -c " + sanitize_name) ret = [] ret.extend([sanitize_name, lsof_infos]) return ret -@salt.utils.decorators.path.which('netstat') +@salt.utils.decorators.path.which("netstat") def netstat(name): - ''' + """ Retrieve the netstat information of the given process name. CLI Example: @@ -667,9 +691,9 @@ def netstat(name): .. code-block:: bash salt '*' ps.netstat apache2 - ''' + """ sanitize_name = six.text_type(name) - netstat_infos = __salt__['cmd.run']("netstat -nap") + netstat_infos = __salt__["cmd.run"]("netstat -nap") found_infos = [] ret = [] for info in netstat_infos.splitlines(): @@ -679,9 +703,9 @@ def netstat(name): return ret -@salt.utils.decorators.path.which('ss') +@salt.utils.decorators.path.which("ss") def ss(name): - ''' + """ Retrieve the ss information of the given process name. CLI Example: @@ -692,9 +716,9 @@ def ss(name): .. versionadded:: 2016.11.6 - ''' + """ sanitize_name = six.text_type(name) - ss_infos = __salt__['cmd.run']("ss -neap") + ss_infos = __salt__["cmd.run"]("ss -neap") found_infos = [] ret = [] for info in ss_infos.splitlines(): @@ -705,7 +729,7 @@ def ss(name): def psaux(name): - ''' + """ Retrieve information corresponding to a "ps aux" filtered with the given pattern. It could be just a name or a regular expression (using python search from "re" module). @@ -715,11 +739,11 @@ def psaux(name): .. code-block:: bash salt '*' ps.psaux www-data.+apache2 - ''' + """ sanitize_name = six.text_type(name) pattern = re.compile(sanitize_name) salt_exception_pattern = re.compile("salt.+ps.psaux.+") - ps_aux = __salt__['cmd.run']("ps aux") + ps_aux = __salt__["cmd.run"]("ps aux") found_infos = [] ret = [] nb_lines = 0 diff --git a/salt/modules/publish.py b/salt/modules/publish.py index 8d195c5cc1b..0926f3fa135 100644 --- a/salt/modules/publish.py +++ b/salt/modules/publish.py @@ -1,33 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" Publish a command from a minion to a target -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import python libs import time -import logging # Import salt libs import salt.crypt import salt.payload -import salt.utils.args import salt.transport.client -from salt.exceptions import SaltReqTimeoutError, SaltInvocationError +import salt.utils.args +from salt.exceptions import SaltInvocationError, SaltReqTimeoutError log = logging.getLogger(__name__) -__virtualname__ = 'publish' +__virtualname__ = "publish" def __virtual__(): - return __virtualname__ if __opts__.get('transport', '') in ('zeromq', 'tcp') else False + return ( + __virtualname__ if __opts__.get("transport", "") in ("zeromq", "tcp") else False + ) def _parse_args(arg): - ''' + """ yamlify `arg` and ensure its outermost datatype is a list - ''' + """ yaml_args = salt.utils.args.yamlify_arg(arg) if yaml_args is None: @@ -39,16 +42,17 @@ def _parse_args(arg): def _publish( - tgt, - fun, - arg=None, - tgt_type='glob', - returner='', - timeout=5, - form='clean', - wait=False, - via_master=None): - ''' + tgt, + fun, + arg=None, + tgt_type="glob", + returner="", + timeout=5, + form="clean", + wait=False, + via_master=None, +): + """ Publish a command from the minion out to other minions, publications need to be enabled on the Salt master and the minion needs to have permission to publish the command. The Salt master will also prevent a recursive @@ -66,78 +70,95 @@ def _publish( .. code-block:: bash salt system.example.com publish.publish '*' cmd.run 'ls -la /tmp' - ''' - if 'master_uri' not in __opts__: - log.error('Cannot run publish commands without a connection to a salt master. No command sent.') + """ + if "master_uri" not in __opts__: + log.error( + "Cannot run publish commands without a connection to a salt master. No command sent." + ) return {} - if fun.startswith('publish.'): - log.info('Cannot publish publish calls. Returning {}') + if fun.startswith("publish."): + log.info("Cannot publish publish calls. Returning {}") return {} arg = _parse_args(arg) if via_master: - if 'master_uri_list' not in __opts__: - raise SaltInvocationError(message='Could not find list of masters \ - in minion configuration but `via_master` was specified.') + if "master_uri_list" not in __opts__: + raise SaltInvocationError( + message="Could not find list of masters \ + in minion configuration but `via_master` was specified." + ) else: # Find the master in the list of master_uris generated by the minion base class - matching_master_uris = [master for master - in __opts__['master_uri_list'] - if '//{0}:'.format(via_master) - in master] + matching_master_uris = [ + master + for master in __opts__["master_uri_list"] + if "//{0}:".format(via_master) in master + ] if not matching_master_uris: - raise SaltInvocationError('Could not find match for {0} in \ - list of configured masters {1} when using `via_master` option'.format( - via_master, __opts__['master_uri_list'])) + raise SaltInvocationError( + "Could not find match for {0} in \ + list of configured masters {1} when using `via_master` option".format( + via_master, __opts__["master_uri_list"] + ) + ) if len(matching_master_uris) > 1: # If we have multiple matches, consider this a non-fatal error # and continue with whatever we found first. - log.warning('The `via_master` flag found ' - 'more than one possible match found for %s when ' - 'evaluating list %s', - via_master, __opts__['master_uri_list']) + log.warning( + "The `via_master` flag found " + "more than one possible match found for %s when " + "evaluating list %s", + via_master, + __opts__["master_uri_list"], + ) master_uri = matching_master_uris.pop() else: # If no preference is expressed by the user, just publish to the first master # in the list. - master_uri = __opts__['master_uri'] + master_uri = __opts__["master_uri"] - log.info('Publishing \'%s\' to %s', fun, master_uri) + log.info("Publishing '%s' to %s", fun, master_uri) auth = salt.crypt.SAuth(__opts__) - tok = auth.gen_token(b'salt') - load = {'cmd': 'minion_pub', - 'fun': fun, - 'arg': arg, - 'tgt': tgt, - 'tgt_type': tgt_type, - 'ret': returner, - 'tok': tok, - 'tmo': timeout, - 'form': form, - 'id': __opts__['id'], - 'no_parse': __opts__.get('no_parse', [])} + tok = auth.gen_token(b"salt") + load = { + "cmd": "minion_pub", + "fun": fun, + "arg": arg, + "tgt": tgt, + "tgt_type": tgt_type, + "ret": returner, + "tok": tok, + "tmo": timeout, + "form": form, + "id": __opts__["id"], + "no_parse": __opts__.get("no_parse", []), + } - with salt.transport.client.ReqChannel.factory(__opts__, master_uri=master_uri) as channel: + with salt.transport.client.ReqChannel.factory( + __opts__, master_uri=master_uri + ) as channel: try: peer_data = channel.send(load) except SaltReqTimeoutError: - return '\'{0}\' publish timed out'.format(fun) + return "'{0}' publish timed out".format(fun) if not peer_data: return {} # CLI args are passed as strings, re-cast to keep time.sleep happy if wait: loop_interval = 0.3 - matched_minions = set(peer_data['minions']) + matched_minions = set(peer_data["minions"]) returned_minions = set() loop_counter = 0 while len(returned_minions ^ matched_minions) > 0: - load = {'cmd': 'pub_ret', - 'id': __opts__['id'], - 'tok': tok, - 'jid': peer_data['jid']} + load = { + "cmd": "pub_ret", + "id": __opts__["id"], + "tok": tok, + "jid": peer_data["jid"], + } ret = channel.send(load) returned_minions = set(ret.keys()) @@ -151,10 +172,10 @@ def _publish( end_loop = True if end_loop: - if form == 'clean': + if form == "clean": cret = {} for host in ret: - cret[host] = ret[host]['ret'] + cret[host] = ret[host]["ret"] return cret else: return ret @@ -162,28 +183,26 @@ def _publish( time.sleep(loop_interval) else: time.sleep(float(timeout)) - load = {'cmd': 'pub_ret', - 'id': __opts__['id'], - 'tok': tok, - 'jid': peer_data['jid']} + load = { + "cmd": "pub_ret", + "id": __opts__["id"], + "tok": tok, + "jid": peer_data["jid"], + } ret = channel.send(load) - if form == 'clean': + if form == "clean": cret = {} for host in ret: - cret[host] = ret[host]['ret'] + cret[host] = ret[host]["ret"] return cret else: return ret -def publish(tgt, - fun, - arg=None, - tgt_type='glob', - returner='', - timeout=5, - via_master=None): - ''' +def publish( + tgt, fun, arg=None, tgt_type="glob", returner="", timeout=5, via_master=None +): + """ Publish a command from the minion out to other minions. Publications need to be enabled on the Salt master and the minion @@ -248,25 +267,22 @@ def publish(tgt, When running via salt-call, the `via_master` flag may be set to specific which master the publication should be sent to. Only one master may be specified. If unset, the publication will be sent only to the first master in minion configuration. - ''' - return _publish(tgt, - fun, - arg=arg, - tgt_type=tgt_type, - returner=returner, - timeout=timeout, - form='clean', - wait=True, - via_master=via_master) + """ + return _publish( + tgt, + fun, + arg=arg, + tgt_type=tgt_type, + returner=returner, + timeout=timeout, + form="clean", + wait=True, + via_master=via_master, + ) -def full_data(tgt, - fun, - arg=None, - tgt_type='glob', - returner='', - timeout=5): - ''' +def full_data(tgt, fun, arg=None, tgt_type="glob", returner="", timeout=5): + """ Return the full data about the publication, this is invoked in the same way as the publish function @@ -286,19 +302,21 @@ def full_data(tgt, salt '*' publish.full_data test.kwarg arg='cheese=spam' - ''' - return _publish(tgt, - fun, - arg=arg, - tgt_type=tgt_type, - returner=returner, - timeout=timeout, - form='full', - wait=True) + """ + return _publish( + tgt, + fun, + arg=arg, + tgt_type=tgt_type, + returner=returner, + timeout=timeout, + form="full", + wait=True, + ) def runner(fun, arg=None, timeout=5): - ''' + """ Execute a runner on the master and return the data from the runner function @@ -307,24 +325,26 @@ def runner(fun, arg=None, timeout=5): .. code-block:: bash salt publish.runner manage.down - ''' + """ arg = _parse_args(arg) - if 'master_uri' not in __opts__: - return 'No access to master. If using salt-call with --local, please remove.' - log.info('Publishing runner \'%s\' to %s', fun, __opts__['master_uri']) + if "master_uri" not in __opts__: + return "No access to master. If using salt-call with --local, please remove." + log.info("Publishing runner '%s' to %s", fun, __opts__["master_uri"]) auth = salt.crypt.SAuth(__opts__) - tok = auth.gen_token(b'salt') - load = {'cmd': 'minion_runner', - 'fun': fun, - 'arg': arg, - 'tok': tok, - 'tmo': timeout, - 'id': __opts__['id'], - 'no_parse': __opts__.get('no_parse', [])} + tok = auth.gen_token(b"salt") + load = { + "cmd": "minion_runner", + "fun": fun, + "arg": arg, + "tok": tok, + "tmo": timeout, + "id": __opts__["id"], + "no_parse": __opts__.get("no_parse", []), + } with salt.transport.client.ReqChannel.factory(__opts__) as channel: try: return channel.send(load) except SaltReqTimeoutError: - return '\'{0}\' runner publish timed out'.format(fun) + return "'{0}' runner publish timed out".format(fun) diff --git a/salt/modules/puppet.py b/salt/modules/puppet.py index 11cb841f444..a072e97dfbc 100644 --- a/salt/modules/puppet.py +++ b/salt/modules/puppet.py @@ -1,47 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" Execute puppet routines -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from distutils import version # pylint: disable=no-name-in-module + +import datetime import logging import os -import datetime +from distutils import version # pylint: disable=no-name-in-module # Import salt libs import salt.utils.args import salt.utils.files import salt.utils.path import salt.utils.platform -import salt.utils.yaml import salt.utils.stringutils +import salt.utils.yaml from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range + log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if puppet is installed - ''' - unavailable_exes = ', '.join(exe for exe in ('facter', 'puppet') - if salt.utils.path.which(exe) is None) + """ + unavailable_exes = ", ".join( + exe for exe in ("facter", "puppet") if salt.utils.path.which(exe) is None + ) if unavailable_exes: - return (False, - ('The puppet execution module cannot be loaded: ' - '{0} unavailable.'.format(unavailable_exes))) + return ( + False, + ( + "The puppet execution module cannot be loaded: " + "{0} unavailable.".format(unavailable_exes) + ), + ) else: - return 'puppet' + return "puppet" def _format_fact(output): try: - fact, value = output.split(' => ', 1) + fact, value = output.split(" => ", 1) value = value.strip() except ValueError: fact = None @@ -50,93 +57,94 @@ def _format_fact(output): class _Puppet(object): - ''' + """ Puppet helper class. Used to format command for execution. - ''' + """ + def __init__(self): - ''' + """ Setup a puppet instance, based on the premis that default usage is to run 'puppet agent --test'. Configuration and run states are stored in the default locations. - ''' - self.subcmd = 'agent' + """ + self.subcmd = "agent" self.subcmd_args = [] # e.g. /a/b/manifest.pp - self.kwargs = {'color': 'false'} # e.g. --tags=apache::server - self.args = [] # e.g. --noop + self.kwargs = {"color": "false"} # e.g. --tags=apache::server + self.args = [] # e.g. --noop if salt.utils.platform.is_windows(): - self.vardir = 'C:\\ProgramData\\PuppetLabs\\puppet\\var' - self.rundir = 'C:\\ProgramData\\PuppetLabs\\puppet\\run' - self.confdir = 'C:\\ProgramData\\PuppetLabs\\puppet\\etc' + self.vardir = "C:\\ProgramData\\PuppetLabs\\puppet\\var" + self.rundir = "C:\\ProgramData\\PuppetLabs\\puppet\\run" + self.confdir = "C:\\ProgramData\\PuppetLabs\\puppet\\etc" else: - self.puppet_version = __salt__['cmd.run']('puppet --version') - if 'Enterprise' in self.puppet_version: - self.vardir = '/var/opt/lib/pe-puppet' - self.rundir = '/var/opt/run/pe-puppet' - self.confdir = '/etc/puppetlabs/puppet' - elif self.puppet_version != [] and version.StrictVersion(self.puppet_version) >= version.StrictVersion('4.0.0'): - self.vardir = '/opt/puppetlabs/puppet/cache' - self.rundir = '/var/run/puppetlabs' - self.confdir = '/etc/puppetlabs/puppet' + self.puppet_version = __salt__["cmd.run"]("puppet --version") + if "Enterprise" in self.puppet_version: + self.vardir = "/var/opt/lib/pe-puppet" + self.rundir = "/var/opt/run/pe-puppet" + self.confdir = "/etc/puppetlabs/puppet" + elif self.puppet_version != [] and version.StrictVersion( + self.puppet_version + ) >= version.StrictVersion("4.0.0"): + self.vardir = "/opt/puppetlabs/puppet/cache" + self.rundir = "/var/run/puppetlabs" + self.confdir = "/etc/puppetlabs/puppet" else: - self.vardir = '/var/lib/puppet' - self.rundir = '/var/run/puppet' - self.confdir = '/etc/puppet' + self.vardir = "/var/lib/puppet" + self.rundir = "/var/run/puppet" + self.confdir = "/etc/puppet" - self.disabled_lockfile = self.vardir + '/state/agent_disabled.lock' - self.run_lockfile = self.vardir + '/state/agent_catalog_run.lock' - self.agent_pidfile = self.rundir + '/agent.pid' - self.lastrunfile = self.vardir + '/state/last_run_summary.yaml' + self.disabled_lockfile = self.vardir + "/state/agent_disabled.lock" + self.run_lockfile = self.vardir + "/state/agent_catalog_run.lock" + self.agent_pidfile = self.rundir + "/agent.pid" + self.lastrunfile = self.vardir + "/state/last_run_summary.yaml" def __repr__(self): - ''' + """ Format the command string to executed using cmd.run_all. - ''' - cmd = 'puppet {subcmd} --vardir {vardir} --confdir {confdir}'.format( + """ + cmd = "puppet {subcmd} --vardir {vardir} --confdir {confdir}".format( **self.__dict__ ) - args = ' '.join(self.subcmd_args) - args += ''.join( - [' --{0}'.format(k) for k in self.args] # single spaces - ) - args += ''.join([ - ' --{0} {1}'.format(k, v) for k, v in six.iteritems(self.kwargs)] + args = " ".join(self.subcmd_args) + args += "".join([" --{0}".format(k) for k in self.args]) # single spaces + args += "".join( + [" --{0} {1}".format(k, v) for k, v in six.iteritems(self.kwargs)] ) # Ensure that the puppet call will return 0 in case of exit code 2 if salt.utils.platform.is_windows(): - return 'cmd /V:ON /c {0} {1} ^& if !ERRORLEVEL! EQU 2 (EXIT 0) ELSE (EXIT /B)'.format(cmd, args) - return '({0} {1}) || test $? -eq 2'.format(cmd, args) + return "cmd /V:ON /c {0} {1} ^& if !ERRORLEVEL! EQU 2 (EXIT 0) ELSE (EXIT /B)".format( + cmd, args + ) + return "({0} {1}) || test $? -eq 2".format(cmd, args) def arguments(self, args=None): - ''' + """ Read in arguments for the current subcommand. These are added to the cmd line without '--' appended. Any others are redirected as standard options with the double hyphen prefixed. - ''' + """ # permits deleting elements rather than using slices args = args and list(args) or [] # match against all known/supported subcmds - if self.subcmd == 'apply': + if self.subcmd == "apply": # apply subcommand requires a manifest file to execute self.subcmd_args = [args[0]] del args[0] - if self.subcmd == 'agent': + if self.subcmd == "agent": # no arguments are required - args.extend([ - 'test' - ]) + args.extend(["test"]) # finally do this after subcmd has been matched for all remaining args self.args = args def run(*args, **kwargs): - ''' + """ Execute a puppet run and return a dict with the stderr, stdout, return code, etc. The first positional argument given is checked as a subcommand. Following positional arguments should be ordered with arguments @@ -153,7 +161,7 @@ def run(*args, **kwargs): salt '*' puppet.run agent onetime no-daemonize no-usecacheonfailure no-splay ignorecache salt '*' puppet.run debug salt '*' puppet.run apply /a/b/manifest.pp modulepath=/a/b/modules tags=basefiles::edit,apache::server - ''' + """ puppet = _Puppet() # new args tuple to filter out agent/apply for _Puppet.arguments() @@ -162,7 +170,7 @@ def run(*args, **kwargs): # based on puppet documentation action must come first. making the same # assertion. need to ensure the list of supported cmds here matches # those defined in _Puppet.arguments() - if args[arg] in ['agent', 'apply']: + if args[arg] in ["agent", "apply"]: puppet.subcmd = args[arg] else: buildargs += (args[arg],) @@ -171,12 +179,12 @@ def run(*args, **kwargs): puppet.kwargs.update(salt.utils.args.clean_kwargs(**kwargs)) - ret = __salt__['cmd.run_all'](repr(puppet), python_shell=True) + ret = __salt__["cmd.run_all"](repr(puppet), python_shell=True) return ret def noop(*args, **kwargs): - ''' + """ Execute a puppet noop run and return a dict with the stderr, stdout, return code, etc. Usage is the same as for puppet.run. @@ -188,13 +196,13 @@ def noop(*args, **kwargs): salt '*' puppet.noop tags=basefiles::edit,apache::server salt '*' puppet.noop debug salt '*' puppet.noop apply /a/b/manifest.pp modulepath=/a/b/modules tags=basefiles::edit,apache::server - ''' - args += ('noop',) + """ + args += ("noop",) return run(*args, **kwargs) def enable(): - ''' + """ .. versionadded:: 2014.7.0 Enable the puppet agent @@ -204,14 +212,14 @@ def enable(): .. code-block:: bash salt '*' puppet.enable - ''' + """ puppet = _Puppet() if os.path.isfile(puppet.disabled_lockfile): try: os.remove(puppet.disabled_lockfile) except (IOError, OSError) as exc: - msg = 'Failed to enable: {0}'.format(exc) + msg = "Failed to enable: {0}".format(exc) log.error(msg) raise CommandExecutionError(msg) else: @@ -220,7 +228,7 @@ def enable(): def disable(message=None): - ''' + """ .. versionadded:: 2014.7.0 Disable the puppet agent @@ -236,28 +244,32 @@ def disable(message=None): salt '*' puppet.disable salt '*' puppet.disable 'Disabled, contact XYZ before enabling' - ''' + """ puppet = _Puppet() if os.path.isfile(puppet.disabled_lockfile): return False else: - with salt.utils.files.fopen(puppet.disabled_lockfile, 'w') as lockfile: + with salt.utils.files.fopen(puppet.disabled_lockfile, "w") as lockfile: try: # Puppet chokes when no valid json is found - msg = '{{"disabled_message":"{0}"}}'.format(message) if message is not None else '{}' + msg = ( + '{{"disabled_message":"{0}"}}'.format(message) + if message is not None + else "{}" + ) lockfile.write(salt.utils.stringutils.to_str(msg)) lockfile.close() return True except (IOError, OSError) as exc: - msg = 'Failed to disable: {0}'.format(exc) + msg = "Failed to disable: {0}".format(exc) log.error(msg) raise CommandExecutionError(msg) def status(): - ''' + """ .. versionadded:: 2014.7.0 Display puppet agent status @@ -267,37 +279,37 @@ def status(): .. code-block:: bash salt '*' puppet.status - ''' + """ puppet = _Puppet() if os.path.isfile(puppet.disabled_lockfile): - return 'Administratively disabled' + return "Administratively disabled" if os.path.isfile(puppet.run_lockfile): try: - with salt.utils.files.fopen(puppet.run_lockfile, 'r') as fp_: + with salt.utils.files.fopen(puppet.run_lockfile, "r") as fp_: pid = int(salt.utils.stringutils.to_unicode(fp_.read())) os.kill(pid, 0) # raise an OSError if process doesn't exist except (OSError, ValueError): - return 'Stale lockfile' + return "Stale lockfile" else: - return 'Applying a catalog' + return "Applying a catalog" if os.path.isfile(puppet.agent_pidfile): try: - with salt.utils.files.fopen(puppet.agent_pidfile, 'r') as fp_: + with salt.utils.files.fopen(puppet.agent_pidfile, "r") as fp_: pid = int(salt.utils.stringutils.to_unicode(fp_.read())) os.kill(pid, 0) # raise an OSError if process doesn't exist except (OSError, ValueError): - return 'Stale pidfile' + return "Stale pidfile" else: - return 'Idle daemon' + return "Idle daemon" - return 'Stopped' + return "Stopped" def summary(): - ''' + """ .. versionadded:: 2014.7.0 Show a summary of the last puppet agent run @@ -307,44 +319,45 @@ def summary(): .. code-block:: bash salt '*' puppet.summary - ''' + """ puppet = _Puppet() try: - with salt.utils.files.fopen(puppet.lastrunfile, 'r') as fp_: + with salt.utils.files.fopen(puppet.lastrunfile, "r") as fp_: report = salt.utils.yaml.safe_load(fp_) result = {} - if 'time' in report: + if "time" in report: try: - result['last_run'] = datetime.datetime.fromtimestamp( - int(report['time']['last_run'])).isoformat() + result["last_run"] = datetime.datetime.fromtimestamp( + int(report["time"]["last_run"]) + ).isoformat() except (TypeError, ValueError, KeyError): - result['last_run'] = 'invalid or missing timestamp' + result["last_run"] = "invalid or missing timestamp" - result['time'] = {} - for key in ('total', 'config_retrieval'): - if key in report['time']: - result['time'][key] = report['time'][key] + result["time"] = {} + for key in ("total", "config_retrieval"): + if key in report["time"]: + result["time"][key] = report["time"][key] - if 'resources' in report: - result['resources'] = report['resources'] + if "resources" in report: + result["resources"] = report["resources"] except salt.utils.yaml.YAMLError as exc: raise CommandExecutionError( - 'YAML error parsing puppet run summary: {0}'.format(exc) + "YAML error parsing puppet run summary: {0}".format(exc) ) except IOError as exc: raise CommandExecutionError( - 'Unable to read puppet run summary: {0}'.format(exc) + "Unable to read puppet run summary: {0}".format(exc) ) return result def plugin_sync(): - ''' + """ Runs a plugin sync between the puppet master and agent CLI Example: @@ -352,16 +365,16 @@ def plugin_sync(): .. code-block:: bash salt '*' puppet.plugin_sync - ''' - ret = __salt__['cmd.run']('puppet plugin download') + """ + ret = __salt__["cmd.run"]("puppet plugin download") if not ret: - return '' + return "" return ret def facts(puppet=False): - ''' + """ Run facter and return the results CLI Example: @@ -369,15 +382,15 @@ def facts(puppet=False): .. code-block:: bash salt '*' puppet.facts - ''' + """ ret = {} - opt_puppet = '--puppet' if puppet else '' - cmd_ret = __salt__['cmd.run_all']('facter {0}'.format(opt_puppet)) + opt_puppet = "--puppet" if puppet else "" + cmd_ret = __salt__["cmd.run_all"]("facter {0}".format(opt_puppet)) - if cmd_ret['retcode'] != 0: - raise CommandExecutionError(cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + raise CommandExecutionError(cmd_ret["stderr"]) - output = cmd_ret['stdout'] + output = cmd_ret["stdout"] # Loop over the facter output and properly # parse it into a nice dictionary for using @@ -393,7 +406,7 @@ def facts(puppet=False): def fact(name, puppet=False): - ''' + """ Run facter for a specific fact CLI Example: @@ -401,15 +414,15 @@ def fact(name, puppet=False): .. code-block:: bash salt '*' puppet.fact kernel - ''' - opt_puppet = '--puppet' if puppet else '' - ret = __salt__['cmd.run_all']( - 'facter {0} {1}'.format(opt_puppet, name), - python_shell=False) + """ + opt_puppet = "--puppet" if puppet else "" + ret = __salt__["cmd.run_all"]( + "facter {0} {1}".format(opt_puppet, name), python_shell=False + ) - if ret['retcode'] != 0: - raise CommandExecutionError(ret['stderr']) + if ret["retcode"] != 0: + raise CommandExecutionError(ret["stderr"]) - if not ret['stdout']: - return '' - return ret['stdout'] + if not ret["stdout"]: + return "" + return ret["stdout"] diff --git a/salt/modules/purefa.py b/salt/modules/purefa.py index 475a7c5644c..998c4a567ea 100644 --- a/salt/modules/purefa.py +++ b/salt/modules/purefa.py @@ -14,7 +14,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -''' +""" Management of Pure Storage FlashArray @@ -49,50 +49,58 @@ Installation Prerequisites .. versionadded:: 2018.3.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import platform from datetime import datetime +from salt.exceptions import CommandExecutionError + # Import Salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError # Import 3rd party modules try: import purestorage + HAS_PURESTORAGE = True except ImportError: HAS_PURESTORAGE = False -__docformat__ = 'restructuredtext en' +__docformat__ = "restructuredtext en" -VERSION = '1.0.0' -USER_AGENT_BASE = 'Salt' +VERSION = "1.0.0" +USER_AGENT_BASE = "Salt" -__virtualname__ = 'purefa' +__virtualname__ = "purefa" # Default symbols to use for passwords. Avoids visually confusing characters. # ~6 bits per symbol -DEFAULT_PASSWORD_SYMBOLS = ('23456789', # Removed: 0,1 - 'ABCDEFGHJKLMNPQRSTUVWXYZ', # Removed: I, O - 'abcdefghijkmnopqrstuvwxyz') # Removed: l +DEFAULT_PASSWORD_SYMBOLS = ( + "23456789", # Removed: 0,1 + "ABCDEFGHJKLMNPQRSTUVWXYZ", # Removed: I, O + "abcdefghijkmnopqrstuvwxyz", +) # Removed: l def __virtual__(): - ''' + """ Determine whether or not to load this module - ''' + """ if HAS_PURESTORAGE: return __virtualname__ - return (False, 'purefa execution module not loaded: purestorage python library not available.') + return ( + False, + "purefa execution module not loaded: purestorage python library not available.", + ) def _get_system(): - ''' + """ Get Pure Storage FlashArray configuration 1) From the minion config @@ -103,44 +111,49 @@ def _get_system(): 2) From environment (PUREFA_IP and PUREFA_API) 3) From the pillar (PUREFA_IP and PUREFA_API) - ''' - agent = {'base': USER_AGENT_BASE, - 'class': __name__, - 'version': VERSION, - 'platform': platform.platform() - } + """ + agent = { + "base": USER_AGENT_BASE, + "class": __name__, + "version": VERSION, + "platform": platform.platform(), + } - user_agent = '{base} {class}/{version} ({platform})'.format(**agent) + user_agent = "{base} {class}/{version} ({platform})".format(**agent) try: - array = __opts__['pure_tags']['fa'].get('san_ip') - api = __opts__['pure_tags']['fa'].get('api_token') + array = __opts__["pure_tags"]["fa"].get("san_ip") + api = __opts__["pure_tags"]["fa"].get("api_token") if array and api: system = purestorage.FlashArray(array, api_token=api, user_agent=user_agent) except (KeyError, NameError, TypeError): try: - san_ip = os.environ.get('PUREFA_IP') - api_token = os.environ.get('PUREFA_API') - system = purestorage.FlashArray(san_ip, - api_token=api_token, - user_agent=user_agent) + san_ip = os.environ.get("PUREFA_IP") + api_token = os.environ.get("PUREFA_API") + system = purestorage.FlashArray( + san_ip, api_token=api_token, user_agent=user_agent + ) except (ValueError, KeyError, NameError): try: - system = purestorage.FlashArray(__pillar__['PUREFA_IP'], - api_token=__pillar__['PUREFA_API'], - user_agent=user_agent) + system = purestorage.FlashArray( + __pillar__["PUREFA_IP"], + api_token=__pillar__["PUREFA_API"], + user_agent=user_agent, + ) except (KeyError, NameError): - raise CommandExecutionError('No Pure Storage FlashArray credentials found.') + raise CommandExecutionError( + "No Pure Storage FlashArray credentials found." + ) try: system.get() except Exception: # pylint: disable=broad-except - raise CommandExecutionError('Pure Storage FlashArray authentication failed.') + raise CommandExecutionError("Pure Storage FlashArray authentication failed.") return system def _get_volume(name, array): - '''Private function to check volume''' + """Private function to check volume""" try: return array.get_volume(name) except purestorage.PureError: @@ -148,64 +161,64 @@ def _get_volume(name, array): def _get_snapshot(name, suffix, array): - '''Private function to check snapshot''' - snapshot = name + '.' + suffix + """Private function to check snapshot""" + snapshot = name + "." + suffix try: for snap in array.get_volume(name, snap=True): - if snap['name'] == snapshot: + if snap["name"] == snapshot: return snapshot except purestorage.PureError: return None def _get_deleted_volume(name, array): - '''Private function to check deleted volume''' + """Private function to check deleted volume""" try: - return array.get_volume(name, pending='true') + return array.get_volume(name, pending="true") except purestorage.PureError: return None def _get_pgroup(name, array): - '''Private function to check protection group''' + """Private function to check protection group""" pgroup = None for temp in array.list_pgroups(): - if temp['name'] == name: + if temp["name"] == name: pgroup = temp break return pgroup def _get_deleted_pgroup(name, array): - '''Private function to check deleted protection group''' + """Private function to check deleted protection group""" try: - return array.get_pgroup(name, pending='true') + return array.get_pgroup(name, pending="true") except purestorage.PureError: return None def _get_hgroup(name, array): - '''Private function to check hostgroup''' + """Private function to check hostgroup""" hostgroup = None for temp in array.list_hgroups(): - if temp['name'] == name: + if temp["name"] == name: hostgroup = temp break return hostgroup def _get_host(name, array): - '''Private function to check host''' + """Private function to check host""" host = None for temp in array.list_hosts(): - if temp['name'] == name: + if temp["name"] == name: host = temp break return host def snap_create(name, suffix=None): - ''' + """ Create a volume snapshot on a Pure Storage FlashArray. @@ -225,11 +238,13 @@ def snap_create(name, suffix=None): salt '*' purefa.snap_create foo salt '*' purefa.snap_create foo suffix=bar - ''' + """ array = _get_system() if suffix is None: - suffix = 'snap-' + six.text_type((datetime.utcnow() - datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds()) - suffix = suffix.replace('.', '') + suffix = "snap-" + six.text_type( + (datetime.utcnow() - datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds() + ) + suffix = suffix.replace(".", "") if _get_volume(name, array) is not None: try: array.create_snapshot(name, suffix=suffix) @@ -241,7 +256,7 @@ def snap_create(name, suffix=None): def snap_delete(name, suffix=None, eradicate=False): - ''' + """ Delete a volume snapshot on a Pure Storage FlashArray. @@ -262,11 +277,11 @@ def snap_delete(name, suffix=None, eradicate=False): salt '*' purefa.snap_delete foo suffix=snap eradicate=True - ''' + """ array = _get_system() if _get_snapshot(name, suffix, array) is not None: try: - snapname = name + '.' + suffix + snapname = name + "." + suffix array.destroy_volume(snapname) except purestorage.PureError: return False @@ -283,7 +298,7 @@ def snap_delete(name, suffix=None, eradicate=False): def snap_eradicate(name, suffix=None): - ''' + """ Eradicate a deleted volume snapshot on a Pure Storage FlashArray. @@ -302,10 +317,10 @@ def snap_eradicate(name, suffix=None): salt '*' purefa.snap_eradicate foo suffix=snap - ''' + """ array = _get_system() if _get_snapshot(name, suffix, array) is not None: - snapname = name + '.' + suffix + snapname = name + "." + suffix try: array.eradicate_volume(snapname) return True @@ -316,7 +331,7 @@ def snap_eradicate(name, suffix=None): def volume_create(name, size=None): - ''' + """ Create a volume on a Pure Storage FlashArray. @@ -337,13 +352,13 @@ def volume_create(name, size=None): salt '*' purefa.volume_create foo salt '*' purefa.volume_create foo size=10T - ''' + """ if len(name) > 63: name = name[0:63] array = _get_system() if _get_volume(name, array) is None: if size is None: - size = '1G' + size = "1G" try: array.create_volume(name, size) return True @@ -354,7 +369,7 @@ def volume_create(name, size=None): def volume_delete(name, eradicate=False): - ''' + """ Delete a volume on a Pure Storage FlashArray. @@ -373,7 +388,7 @@ def volume_delete(name, eradicate=False): salt '*' purefa.volume_delete foo eradicate=True - ''' + """ array = _get_system() if _get_volume(name, array) is not None: try: @@ -393,7 +408,7 @@ def volume_delete(name, eradicate=False): def volume_eradicate(name): - ''' + """ Eradicate a deleted volume on a Pure Storage FlashArray. @@ -410,7 +425,7 @@ def volume_eradicate(name): salt '*' purefa.volume_eradicate foo - ''' + """ array = _get_system() if _get_deleted_volume(name, array) is not None: try: @@ -423,7 +438,7 @@ def volume_eradicate(name): def volume_extend(name, size): - ''' + """ Extend an existing volume on a Pure Storage FlashArray. @@ -443,11 +458,11 @@ def volume_extend(name, size): salt '*' purefa.volume_extend foo 10T - ''' + """ array = _get_system() vol = _get_volume(name, array) if vol is not None: - if __utils__['stringutils.human_to_bytes'](size) > vol['size']: + if __utils__["stringutils.human_to_bytes"](size) > vol["size"]: try: array.extend_volume(name, size) return True @@ -460,7 +475,7 @@ def volume_extend(name, size): def snap_volume_create(name, target, overwrite=False): - ''' + """ Create R/W volume from snapshot on a Pure Storage FlashArray. @@ -482,9 +497,9 @@ def snap_volume_create(name, target, overwrite=False): salt '*' purefa.snap_volume_create foo.bar clone overwrite=True - ''' + """ array = _get_system() - source, suffix = name.split('.') + source, suffix = name.split(".") if _get_snapshot(source, suffix, array) is not None: if _get_volume(target, array) is None: try: @@ -506,7 +521,7 @@ def snap_volume_create(name, target, overwrite=False): def volume_clone(name, target, overwrite=False): - ''' + """ Clone an existing volume on a Pure Storage FlashArray. @@ -528,7 +543,7 @@ def volume_clone(name, target, overwrite=False): salt '*' purefa.volume_clone foo bar overwrite=True - ''' + """ array = _get_system() if _get_volume(name, array) is not None: if _get_volume(target, array) is None: @@ -551,7 +566,7 @@ def volume_clone(name, target, overwrite=False): def volume_attach(name, host): - ''' + """ Attach a volume to a host on a Pure Storage FlashArray. @@ -570,7 +585,7 @@ def volume_attach(name, host): salt '*' purefa.volume_attach foo bar - ''' + """ array = _get_system() if _get_volume(name, array) is not None and _get_host(host, array) is not None: try: @@ -583,7 +598,7 @@ def volume_attach(name, host): def volume_detach(name, host): - ''' + """ Detach a volume from a host on a Pure Storage FlashArray. @@ -603,7 +618,7 @@ def volume_detach(name, host): salt '*' purefa.volume_detach foo bar - ''' + """ array = _get_system() if _get_volume(name, array) is None or _get_host(host, array) is None: return False @@ -616,7 +631,7 @@ def volume_detach(name, host): def host_create(name, iqn=None, wwn=None): - ''' + """ Add a host on a Pure Storage FlashArray. @@ -639,7 +654,7 @@ def host_create(name, iqn=None, wwn=None): salt '*' purefa.host_create foo iqn='' wwn='' - ''' + """ array = _get_system() if len(name) > 63: name = name[0:63] @@ -667,7 +682,7 @@ def host_create(name, iqn=None, wwn=None): def host_update(name, iqn=None, wwn=None): - ''' + """ Update a hosts port definitions on a Pure Storage FlashArray. @@ -690,7 +705,7 @@ def host_update(name, iqn=None, wwn=None): salt '*' purefa.host_update foo iqn='' wwn='' - ''' + """ array = _get_system() if _get_host(name, array) is not None: if iqn is not None: @@ -709,7 +724,7 @@ def host_update(name, iqn=None, wwn=None): def host_delete(name): - ''' + """ Delete a host on a Pure Storage FlashArray (detaches all volumes). @@ -726,12 +741,12 @@ def host_delete(name): salt '*' purefa.host_delete foo - ''' + """ array = _get_system() if _get_host(name, array) is not None: for vol in array.list_host_connections(name): try: - array.disconnect_host(name, vol['vol']) + array.disconnect_host(name, vol["vol"]) except purestorage.PureError: return False try: @@ -744,7 +759,7 @@ def host_delete(name): def hg_create(name, host=None, volume=None): - ''' + """ Create a hostgroup on a Pure Storage FlashArray. @@ -766,7 +781,7 @@ def hg_create(name, host=None, volume=None): salt '*' purefa.hg_create foo host=bar volume=vol - ''' + """ array = _get_system() if len(name) > 63: name = name[0:63] @@ -800,7 +815,7 @@ def hg_create(name, host=None, volume=None): def hg_update(name, host=None, volume=None): - ''' + """ Adds entries to a hostgroup on a Pure Storage FlashArray. @@ -822,7 +837,7 @@ def hg_update(name, host=None, volume=None): salt '*' purefa.hg_update foo host=bar volume=vol - ''' + """ array = _get_system() if _get_hgroup(name, array) is not None: if host is not None: @@ -847,7 +862,7 @@ def hg_update(name, host=None, volume=None): def hg_delete(name): - ''' + """ Delete a hostgroup on a Pure Storage FlashArray (removes all volumes and hosts). @@ -864,17 +879,17 @@ def hg_delete(name): salt '*' purefa.hg_delete foo - ''' + """ array = _get_system() if _get_hgroup(name, array) is not None: for vol in array.list_hgroup_connections(name): try: - array.disconnect_hgroup(name, vol['vol']) + array.disconnect_hgroup(name, vol["vol"]) except purestorage.PureError: return False host = array.get_hgroup(name) try: - array.set_hgroup(name, remhostlist=host['hosts']) + array.set_hgroup(name, remhostlist=host["hosts"]) array.delete_hgroup(name) return True except purestorage.PureError: @@ -884,7 +899,7 @@ def hg_delete(name): def hg_remove(name, volume=None, host=None): - ''' + """ Remove a host and/or volume from a hostgroup on a Pure Storage FlashArray. @@ -906,13 +921,13 @@ def hg_remove(name, volume=None, host=None): salt '*' purefa.hg_remove foo volume=test host=bar - ''' + """ array = _get_system() if _get_hgroup(name, array) is not None: if volume is not None: if _get_volume(volume, array): for temp in array.list_hgroup_connections(name): - if temp['vol'] == volume: + if temp["vol"] == volume: try: array.disconnect_hgroup(name, volume) return True @@ -924,7 +939,7 @@ def hg_remove(name, volume=None, host=None): if host is not None: if _get_host(host, array): temp = _get_host(host, array) - if temp['hgroup'] == name: + if temp["hgroup"] == name: try: array.set_hgroup(name, remhostlist=[host]) return True @@ -941,7 +956,7 @@ def hg_remove(name, volume=None, host=None): def pg_create(name, hostgroup=None, host=None, volume=None, enabled=True): - ''' + """ Create a protection group on a Pure Storage FlashArray. @@ -969,7 +984,7 @@ def pg_create(name, hostgroup=None, host=None, volume=None, enabled=True): salt '*' purefa.pg_create foo [hostgroup=foo | host=bar | volume=vol] enabled=[true | false] - ''' + """ array = _get_system() if hostgroup is None and host is None and volume is None: if _get_pgroup(name, array) is None: @@ -985,7 +1000,7 @@ def pg_create(name, hostgroup=None, host=None, volume=None, enabled=True): return False else: return False - elif __utils__['value.xor'](hostgroup, host, volume): + elif __utils__["value.xor"](hostgroup, host, volume): if _get_pgroup(name, array) is None: try: array.create_pgroup(name) @@ -1036,7 +1051,7 @@ def pg_create(name, hostgroup=None, host=None, volume=None, enabled=True): def pg_update(name, hostgroup=None, host=None, volume=None): - ''' + """ Update a protection group on a Pure Storage FlashArray. @@ -1062,11 +1077,11 @@ def pg_update(name, hostgroup=None, host=None, volume=None): salt '*' purefa.pg_update foo [hostgroup=foo | host=bar | volume=vol] - ''' + """ array = _get_system() pgroup = _get_pgroup(name, array) if pgroup is not None: - if hostgroup is not None and pgroup['hgroups'] is not None: + if hostgroup is not None and pgroup["hgroups"] is not None: if _get_hgroup(hostgroup, array) is not None: try: array.add_hgroup(hostgroup, name) @@ -1075,7 +1090,7 @@ def pg_update(name, hostgroup=None, host=None, volume=None): return False else: return False - elif host is not None and pgroup['hosts'] is not None: + elif host is not None and pgroup["hosts"] is not None: if _get_host(host, array) is not None: try: array.add_host(host, name) @@ -1084,7 +1099,7 @@ def pg_update(name, hostgroup=None, host=None, volume=None): return False else: return False - elif volume is not None and pgroup['volumes'] is not None: + elif volume is not None and pgroup["volumes"] is not None: if _get_volume(volume, array) is not None: try: array.add_volume(volume, name) @@ -1094,7 +1109,11 @@ def pg_update(name, hostgroup=None, host=None, volume=None): else: return False else: - if pgroup['hgroups'] is None and pgroup['hosts'] is None and pgroup['volumes'] is None: + if ( + pgroup["hgroups"] is None + and pgroup["hosts"] is None + and pgroup["volumes"] is None + ): if hostgroup is not None: if _get_hgroup(hostgroup, array) is not None: try: @@ -1129,7 +1148,7 @@ def pg_update(name, hostgroup=None, host=None, volume=None): def pg_delete(name, eradicate=False): - ''' + """ Delete a protecton group on a Pure Storage FlashArray. @@ -1146,7 +1165,7 @@ def pg_delete(name, eradicate=False): salt '*' purefa.pg_delete foo - ''' + """ array = _get_system() if _get_pgroup(name, array) is not None: try: @@ -1166,7 +1185,7 @@ def pg_delete(name, eradicate=False): def pg_eradicate(name): - ''' + """ Eradicate a deleted protecton group on a Pure Storage FlashArray. @@ -1183,7 +1202,7 @@ def pg_eradicate(name): salt '*' purefa.pg_eradicate foo - ''' + """ array = _get_system() if _get_deleted_pgroup(name, array) is not None: try: @@ -1196,7 +1215,7 @@ def pg_eradicate(name): def pg_remove(name, hostgroup=None, host=None, volume=None): - ''' + """ Remove a hostgroup, host or volume from a protection group on a Pure Storage FlashArray. @@ -1221,11 +1240,11 @@ def pg_remove(name, hostgroup=None, host=None, volume=None): salt '*' purefa.pg_remove foo [hostgroup=bar | host=test | volume=bar] - ''' + """ array = _get_system() pgroup = _get_pgroup(name, array) if pgroup is not None: - if hostgroup is not None and pgroup['hgroups'] is not None: + if hostgroup is not None and pgroup["hgroups"] is not None: if _get_hgroup(hostgroup, array) is not None: try: array.remove_hgroup(hostgroup, name) @@ -1234,7 +1253,7 @@ def pg_remove(name, hostgroup=None, host=None, volume=None): return False else: return False - elif host is not None and pgroup['hosts'] is not None: + elif host is not None and pgroup["hosts"] is not None: if _get_host(host, array) is not None: try: array.remove_host(host, name) @@ -1243,7 +1262,7 @@ def pg_remove(name, hostgroup=None, host=None, volume=None): return False else: return False - elif volume is not None and pgroup['volumes'] is not None: + elif volume is not None and pgroup["volumes"] is not None: if _get_volume(volume, array) is not None: try: array.remove_volume(volume, name) diff --git a/salt/modules/purefb.py b/salt/modules/purefb.py index 5a8f408dda1..a15d99f458d 100644 --- a/salt/modules/purefb.py +++ b/salt/modules/purefb.py @@ -14,7 +14,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -''' +""" Management of Pure Storage FlashBlade @@ -49,41 +49,47 @@ Installation Prerequisites .. versionadded:: 2019.2.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os from datetime import datetime +from salt.exceptions import CommandExecutionError + # Import Salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError # Import 3rd party modules try: from purity_fb import PurityFb, FileSystem, FileSystemSnapshot, SnapshotSuffix from purity_fb import rest, NfsRule, ProtocolRule + HAS_PURITY_FB = True except ImportError: HAS_PURITY_FB = False -__docformat__ = 'restructuredtext en' +__docformat__ = "restructuredtext en" -__virtualname__ = 'purefb' +__virtualname__ = "purefb" def __virtual__(): - ''' + """ Determine whether or not to load this module - ''' + """ if HAS_PURITY_FB: return __virtualname__ - return (False, 'purefb execution module not loaded: purity_fb python library not available.') + return ( + False, + "purefb execution module not loaded: purity_fb python library not available.", + ) def _get_blade(): - ''' + """ Get Pure Storage FlasBlade configuration 1) From the minion config @@ -94,40 +100,42 @@ def _get_blade(): 2) From environment (PUREFB_IP and PUREFB_API) 3) From the pillar (PUREFB_IP and PUREFB_API) - ''' + """ try: - blade_name = __opts__['pure_tags']['fb'].get('san_ip') - api_token = __opts__['pure_tags']['fb'].get('api_token') + blade_name = __opts__["pure_tags"]["fb"].get("san_ip") + api_token = __opts__["pure_tags"]["fb"].get("api_token") if blade_name and api: blade = PurityFb(blade_name) blade.disable_verify_ssl() except (KeyError, NameError, TypeError): try: - blade_name = os.environ.get('PUREFB_IP') - api_token = os.environ.get('PUREFB_API') + blade_name = os.environ.get("PUREFB_IP") + api_token = os.environ.get("PUREFB_API") if blade_name: blade = PurityFb(blade_name) blade.disable_verify_ssl() except (ValueError, KeyError, NameError): try: - api_token = __pillar__['PUREFB_API'] - blade = PurityFb(__pillar__['PUREFB_IP']) + api_token = __pillar__["PUREFB_API"] + blade = PurityFb(__pillar__["PUREFB_IP"]) blade.disable_verify_ssl() except (KeyError, NameError): - raise CommandExecutionError('No Pure Storage FlashBlade credentials found.') + raise CommandExecutionError( + "No Pure Storage FlashBlade credentials found." + ) try: blade.login(api_token) except Exception: # pylint: disable=broad-except - raise CommandExecutionError('Pure Storage FlashBlade authentication failed.') + raise CommandExecutionError("Pure Storage FlashBlade authentication failed.") return blade def _get_fs(name, blade): - ''' + """ Private function to check for existance of a filesystem - ''' + """ _fs = [] _fs.append(name) try: @@ -138,12 +146,12 @@ def _get_fs(name, blade): def _get_snapshot(name, suffix, blade): - ''' + """ Return name of Snapshot or None - ''' + """ try: - filt = 'source=\'{}\' and suffix=\'{}\''.format(name, suffix) + filt = "source='{}' and suffix='{}'".format(name, suffix) res = blade.file_system_snapshots.list_file_system_snapshots(filter=filt) return res.items[0] except rest.ApiException: @@ -151,10 +159,10 @@ def _get_snapshot(name, suffix, blade): def _get_deleted_fs(name, blade): - ''' + """ Private function to check if a file systeem has already been deleted - ''' + """ try: _fs = _get_fs(name, blade) if _fs and _fs.destroyed: @@ -164,7 +172,7 @@ def _get_deleted_fs(name, blade): def snap_create(name, suffix=None): - ''' + """ Create a filesystem snapshot on a Pure Storage FlashBlade. @@ -184,19 +192,20 @@ def snap_create(name, suffix=None): salt '*' purefb.snap_create foo salt '*' purefb.snap_create foo suffix=bar - ''' + """ blade = _get_blade() if suffix is None: - suffix = ('snap-' + - six.text_type((datetime.utcnow() - - datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds())) - suffix = suffix.replace('.', '') + suffix = "snap-" + six.text_type( + (datetime.utcnow() - datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds() + ) + suffix = suffix.replace(".", "") if _get_fs(name, blade) is not None: try: source = [] source.append(name) - blade.file_system_snapshots.create_file_system_snapshots(sources=source, - suffix=SnapshotSuffix(suffix)) + blade.file_system_snapshots.create_file_system_snapshots( + sources=source, suffix=SnapshotSuffix(suffix) + ) return True except rest.ApiException: return False @@ -205,7 +214,7 @@ def snap_create(name, suffix=None): def snap_delete(name, suffix=None, eradicate=False): - ''' + """ Delete a filesystem snapshot on a Pure Storage FlashBlade. @@ -226,14 +235,15 @@ def snap_delete(name, suffix=None, eradicate=False): salt '*' purefb.snap_delete foo suffix=snap eradicate=True - ''' + """ blade = _get_blade() if _get_snapshot(name, suffix, blade) is not None: try: - snapname = name + '.' + suffix + snapname = name + "." + suffix new_attr = FileSystemSnapshot(destroyed=True) - blade.file_system_snapshots.update_file_system_snapshots(name=snapname, - attributes=new_attr) + blade.file_system_snapshots.update_file_system_snapshots( + name=snapname, attributes=new_attr + ) except rest.ApiException: return False if eradicate is True: @@ -249,7 +259,7 @@ def snap_delete(name, suffix=None, eradicate=False): def snap_eradicate(name, suffix=None): - ''' + """ Eradicate a deleted filesystem snapshot on a Pure Storage FlashBlade. @@ -268,10 +278,10 @@ def snap_eradicate(name, suffix=None): salt '*' purefb.snap_eradicate foo suffix=snap - ''' + """ blade = _get_blade() if _get_snapshot(name, suffix, blade) is not None: - snapname = name + '.' + suffix + snapname = name + "." + suffix try: blade.file_system_snapshots.delete_file_system_snapshots(name=snapname) return True @@ -281,8 +291,10 @@ def snap_eradicate(name, suffix=None): return False -def fs_create(name, size=None, proto='NFS', nfs_rules='*(rw,no_root_squash)', snapshot=False): - ''' +def fs_create( + name, size=None, proto="NFS", nfs_rules="*(rw,no_root_squash)", snapshot=False +): + """ Create a filesystem on a Pure Storage FlashBlade. @@ -311,37 +323,40 @@ def fs_create(name, size=None, proto='NFS', nfs_rules='*(rw,no_root_squash)', sn salt '*' purefb.fs_create foo proto=CIFS salt '*' purefb.fs_create foo size=10T - ''' + """ if len(name) > 63: name = name[0:63] blade = _get_blade() print(proto) if _get_fs(name, blade) is None: if size is None: - size = __utils__['stringutils.human_to_bytes']('32G') + size = __utils__["stringutils.human_to_bytes"]("32G") else: - size = __utils__['stringutils.human_to_bytes'](size) - if proto.lower() == 'nfs': - fs_obj = FileSystem(name=name, - provisioned=size, - fast_remove_directory_enabled=True, - snapshot_directory_enabled=snapshot, - nfs=NfsRule(enabled=True, rules=nfs_rules), - ) - elif proto.lower() == 'cifs': - fs_obj = FileSystem(name=name, - provisioned=size, - fast_remove_directory_enabled=True, - snapshot_directory_enabled=snapshot, - smb=ProtocolRule(enabled=True), - ) - elif proto.lower() == 'http': - fs_obj = FileSystem(name=name, - provisioned=size, - fast_remove_directory_enabled=True, - snapshot_directory_enabled=snapshot, - http=ProtocolRule(enabled=True), - ) + size = __utils__["stringutils.human_to_bytes"](size) + if proto.lower() == "nfs": + fs_obj = FileSystem( + name=name, + provisioned=size, + fast_remove_directory_enabled=True, + snapshot_directory_enabled=snapshot, + nfs=NfsRule(enabled=True, rules=nfs_rules), + ) + elif proto.lower() == "cifs": + fs_obj = FileSystem( + name=name, + provisioned=size, + fast_remove_directory_enabled=True, + snapshot_directory_enabled=snapshot, + smb=ProtocolRule(enabled=True), + ) + elif proto.lower() == "http": + fs_obj = FileSystem( + name=name, + provisioned=size, + fast_remove_directory_enabled=True, + snapshot_directory_enabled=snapshot, + http=ProtocolRule(enabled=True), + ) else: return False try: @@ -354,7 +369,7 @@ def fs_create(name, size=None, proto='NFS', nfs_rules='*(rw,no_root_squash)', sn def fs_delete(name, eradicate=False): - ''' + """ Delete a share on a Pure Storage FlashBlade. @@ -373,16 +388,19 @@ def fs_delete(name, eradicate=False): salt '*' purefb.fs_delete foo eradicate=True - ''' + """ blade = _get_blade() if _get_fs(name, blade) is not None: try: - blade.file_systems.update_file_systems(name=name, - attributes=FileSystem(nfs=NfsRule(enabled=False), - smb=ProtocolRule(enabled=False), - http=ProtocolRule(enabled=False), - destroyed=True) - ) + blade.file_systems.update_file_systems( + name=name, + attributes=FileSystem( + nfs=NfsRule(enabled=False), + smb=ProtocolRule(enabled=False), + http=ProtocolRule(enabled=False), + destroyed=True, + ), + ) except rest.ApiException: return False if eradicate is True: @@ -398,7 +416,7 @@ def fs_delete(name, eradicate=False): def fs_eradicate(name): - ''' + """ Eradicate a deleted filesystem on a Pure Storage FlashBlade. @@ -415,7 +433,7 @@ def fs_eradicate(name): salt '*' purefb.fs_eradicate foo - ''' + """ blade = _get_blade() if _get_deleted_fs(name, blade) is not None: try: @@ -428,7 +446,7 @@ def fs_eradicate(name): def fs_extend(name, size): - ''' + """ Resize an existing filesystem on a Pure Storage FlashBlade. @@ -448,14 +466,14 @@ def fs_extend(name, size): salt '*' purefb.fs_extend foo 10T - ''' + """ attr = {} blade = _get_blade() _fs = _get_fs(name, blade) if _fs is not None: - if __utils__['stringutils.human_to_bytes'](size) > _fs.provisioned: + if __utils__["stringutils.human_to_bytes"](size) > _fs.provisioned: try: - attr['provisioned'] = __utils__['stringutils.human_to_bytes'](size) + attr["provisioned"] = __utils__["stringutils.human_to_bytes"](size) n_attr = FileSystem(**attr) blade.file_systems.update_file_systems(name=name, attributes=n_attr) return True @@ -468,7 +486,7 @@ def fs_extend(name, size): def fs_update(name, rules, snapshot=False): - ''' + """ Update filesystem on a Pure Storage FlashBlade. @@ -491,15 +509,15 @@ def fs_update(name, rules, snapshot=False): salt '*' purefb.fs_nfs_update foo rules='10.234.112.23(ro), 10.234.112.24(rw)' snapshot=True - ''' + """ blade = _get_blade() attr = {} _fs = _get_fs(name, blade) if _fs is not None: try: if _fs.nfs.enabled: - attr['nfs'] = NfsRule(rules=rules) - attr['snapshot_directory_enabled'] = snapshot + attr["nfs"] = NfsRule(rules=rules) + attr["snapshot_directory_enabled"] = snapshot n_attr = FileSystem(**attr) blade.file_systems.update_file_systems(name=name, attributes=n_attr) return True diff --git a/salt/modules/pushbullet.py b/salt/modules/pushbullet.py index 5d6ce446d8c..f933f423b6d 100644 --- a/salt/modules/pushbullet.py +++ b/salt/modules/pushbullet.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for sending messages to Pushbullet (https://www.pushbullet.com) .. versionadded:: 2015.8.0 @@ -20,14 +20,16 @@ For example: title: "Example push message" body: "Message body." -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging try: import pushbullet + HAS_PUSHBULLET = True except ImportError: HAS_PUSHBULLET = False @@ -37,18 +39,19 @@ log = logging.getLogger(__name__) def __virtual__(): if not HAS_PUSHBULLET: - return (False, 'Missing pushbullet library.') - if not __salt__['config.get']('pushbullet.api_key') and \ - not __salt__['config.get']('pushbullet:api_key'): - return (False, 'Pushbullet API Key Unavailable, not loading.') + return (False, "Missing pushbullet library.") + if not __salt__["config.get"]("pushbullet.api_key") and not __salt__["config.get"]( + "pushbullet:api_key" + ): + return (False, "Pushbullet API Key Unavailable, not loading.") return True class _SaltPushbullet(object): - def __init__(self, device_name): - api_key = __salt__['config.get']('pushbullet.api_key') or \ - __salt__['config.get']('pushbullet:api_key') + api_key = __salt__["config.get"]("pushbullet.api_key") or __salt__[ + "config.get" + ]("pushbullet:api_key") self.pb = pushbullet.Pushbullet(api_key) self.target = self._find_device_by_name(device_name) @@ -63,7 +66,7 @@ class _SaltPushbullet(object): def push_note(device=None, title=None, body=None): - ''' + """ Pushing a text note. :param device: Pushbullet target device @@ -77,7 +80,7 @@ def push_note(device=None, title=None, body=None): .. code-block:: bash salt "*" pushbullet.push_note device="Chrome" title="Example title" body="Example body." - ''' + """ spb = _SaltPushbullet(device) res = spb.push_note(title, body) diff --git a/salt/modules/pushover_notify.py b/salt/modules/pushover_notify.py index 39601be296b..162319d7575 100644 --- a/salt/modules/pushover_notify.py +++ b/salt/modules/pushover_notify.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for sending messages to Pushover (https://www.pushover.net) .. versionadded:: 2016.3.0 @@ -14,45 +14,51 @@ Module for sending messages to Pushover (https://www.pushover.net) pushover: token: abAHuZyCLtdH8P4zhmFZmgUHUsv1ei8 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.pushover + +# Import salt libs +from salt.exceptions import SaltInvocationError + # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext.six.moves.urllib.parse import urlencode as _urlencode + # pylint: enable=import-error,no-name-in-module,redefined-builtin -# Import salt libs -from salt.exceptions import SaltInvocationError -import salt.utils.pushover log = logging.getLogger(__name__) -__virtualname__ = 'pushover' +__virtualname__ = "pushover" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ -def post_message(user=None, - device=None, - message=None, - title=None, - priority=None, - expire=None, - retry=None, - sound=None, - api_version=1, - token=None): - ''' +def post_message( + user=None, + device=None, + message=None, + title=None, + priority=None, + expire=None, + retry=None, + sound=None, + api_version=1, + token=None, +): + """ Send a message to a Pushover user or group. :param user: The user or group to send to, must be key of user or group not email address. @@ -74,50 +80,54 @@ def post_message(user=None, salt '*' pushover.post_message user='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' title='Message from Salt' message='Build is done' priority='2' expire='720' retry='5' - ''' + """ if not token: - token = __salt__['config.get']('pushover.token') or \ - __salt__['config.get']('pushover:token') + token = __salt__["config.get"]("pushover.token") or __salt__["config.get"]( + "pushover:token" + ) if not token: - raise SaltInvocationError('Pushover token is unavailable.') + raise SaltInvocationError("Pushover token is unavailable.") if not user: - user = __salt__['config.get']('pushover.user') or \ - __salt__['config.get']('pushover:user') + user = __salt__["config.get"]("pushover.user") or __salt__["config.get"]( + "pushover:user" + ) if not user: - raise SaltInvocationError('Pushover user key is unavailable.') + raise SaltInvocationError("Pushover user key is unavailable.") if not message: raise SaltInvocationError('Required parameter "message" is missing.') user_validate = salt.utils.pushover.validate_user(user, device, token) - if not user_validate['result']: + if not user_validate["result"]: return user_validate if not title: - title = 'Message from SaltStack' + title = "Message from SaltStack" parameters = dict() - parameters['user'] = user - parameters['device'] = device - parameters['token'] = token - parameters['title'] = title - parameters['priority'] = priority - parameters['expire'] = expire - parameters['retry'] = retry - parameters['message'] = message + parameters["user"] = user + parameters["device"] = device + parameters["token"] = token + parameters["title"] = title + parameters["priority"] = priority + parameters["expire"] = expire + parameters["retry"] = retry + parameters["message"] = message - if sound and salt.utils.pushover.validate_sound(sound, token)['res']: - parameters['sound'] = sound + if sound and salt.utils.pushover.validate_sound(sound, token)["res"]: + parameters["sound"] = sound - result = salt.utils.pushover.query(function='message', - method='POST', - header_dict={'Content-Type': 'application/x-www-form-urlencoded'}, - data=_urlencode(parameters), - opts=__opts__) + result = salt.utils.pushover.query( + function="message", + method="POST", + header_dict={"Content-Type": "application/x-www-form-urlencoded"}, + data=_urlencode(parameters), + opts=__opts__, + ) - if result['res']: + if result["res"]: return True else: return result diff --git a/salt/modules/pw_group.py b/salt/modules/pw_group.py index e9f7988c909..b4ff9ae5d51 100644 --- a/salt/modules/pw_group.py +++ b/salt/modules/pw_group.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage groups on FreeBSD .. important:: @@ -7,8 +7,8 @@ Manage groups on FreeBSD minion, and it is using a different module (or gives an error similar to *'group.info' is not available*), see :ref:`here `. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -17,7 +17,6 @@ import logging import salt.utils.args import salt.utils.data - log = logging.getLogger(__name__) @@ -27,21 +26,23 @@ except ImportError: pass # Define the module's virtual name -__virtualname__ = 'group' +__virtualname__ = "group" def __virtual__(): - ''' + """ Set the user module if the kernel is FreeBSD or Dragonfly - ''' - if __grains__['kernel'] in ('FreeBSD', 'DragonFly'): + """ + if __grains__["kernel"] in ("FreeBSD", "DragonFly"): return __virtualname__ - return (False, 'The pw_group execution module cannot be loaded: ' - 'system is not supported.') + return ( + False, + "The pw_group execution module cannot be loaded: system is not supported.", + ) def add(name, gid=None, **kwargs): - ''' + """ Add the specified group CLI Example: @@ -49,24 +50,24 @@ def add(name, gid=None, **kwargs): .. code-block:: bash salt '*' group.add foo 3456 - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - if salt.utils.data.is_true(kwargs.pop('system', False)): - log.warning('pw_group module does not support the \'system\' argument') + if salt.utils.data.is_true(kwargs.pop("system", False)): + log.warning("pw_group module does not support the 'system' argument") if kwargs: - log.warning('Invalid kwargs passed to group.add') + log.warning("Invalid kwargs passed to group.add") - cmd = 'pw groupadd ' + cmd = "pw groupadd " if gid: - cmd += '-g {0} '.format(gid) - cmd = '{0} -n {1}'.format(cmd, name) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + cmd += "-g {0} ".format(gid) + cmd = "{0} -n {1}".format(cmd, name) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - return not ret['retcode'] + return not ret["retcode"] def delete(name): - ''' + """ Remove the named group CLI Example: @@ -74,14 +75,14 @@ def delete(name): .. code-block:: bash salt '*' group.delete foo - ''' - ret = __salt__['cmd.run_all']('pw groupdel {0}'.format(name), python_shell=False) + """ + ret = __salt__["cmd.run_all"]("pw groupdel {0}".format(name), python_shell=False) - return not ret['retcode'] + return not ret["retcode"] def info(name): - ''' + """ Return information about a group CLI Example: @@ -89,20 +90,22 @@ def info(name): .. code-block:: bash salt '*' group.info foo - ''' + """ try: grinfo = grp.getgrnam(name) except KeyError: return {} else: - return {'name': grinfo.gr_name, - 'passwd': grinfo.gr_passwd, - 'gid': grinfo.gr_gid, - 'members': grinfo.gr_mem} + return { + "name": grinfo.gr_name, + "passwd": grinfo.gr_passwd, + "gid": grinfo.gr_gid, + "members": grinfo.gr_mem, + } def getent(refresh=False): - ''' + """ Return info on all groups CLI Example: @@ -110,19 +113,19 @@ def getent(refresh=False): .. code-block:: bash salt '*' group.getent - ''' - if 'group.getent' in __context__ and not refresh: - return __context__['group.getent'] + """ + if "group.getent" in __context__ and not refresh: + return __context__["group.getent"] ret = [] for grinfo in grp.getgrall(): ret.append(info(grinfo.gr_name)) - __context__['group.getent'] = ret + __context__["group.getent"] = ret return ret def chgid(name, gid): - ''' + """ Change the gid for a named group CLI Example: @@ -130,20 +133,20 @@ def chgid(name, gid): .. code-block:: bash salt '*' group.chgid foo 4376 - ''' - pre_gid = __salt__['file.group_to_gid'](name) + """ + pre_gid = __salt__["file.group_to_gid"](name) if gid == pre_gid: return True - cmd = 'pw groupmod {0} -g {1}'.format(name, gid) - __salt__['cmd.run'](cmd, python_shell=False) - post_gid = __salt__['file.group_to_gid'](name) + cmd = "pw groupmod {0} -g {1}".format(name, gid) + __salt__["cmd.run"](cmd, python_shell=False) + post_gid = __salt__["file.group_to_gid"](name) if post_gid != pre_gid: return post_gid == gid return False def adduser(name, username): - ''' + """ Add a user in the group. CLI Example: @@ -154,16 +157,17 @@ def adduser(name, username): Verifies if a valid username 'bar' as a member of an existing group 'foo', if not then adds it. - ''' + """ # Note: pw exits with code 65 if group is unknown - retcode = __salt__['cmd.retcode']('pw groupmod {0} -m {1}'.format( - name, username), python_shell=False) + retcode = __salt__["cmd.retcode"]( + "pw groupmod {0} -m {1}".format(name, username), python_shell=False + ) return not retcode def deluser(name, username): - ''' + """ Remove a user from the group. CLI Example: @@ -174,21 +178,22 @@ def deluser(name, username): Removes a member user 'bar' from a group 'foo'. If group is not present then returns True. - ''' - grp_info = __salt__['group.info'](name) + """ + grp_info = __salt__["group.info"](name) - if username not in grp_info['members']: + if username not in grp_info["members"]: return True # Note: pw exits with code 65 if group is unknown - retcode = __salt__['cmd.retcode']('pw groupmod {0} -d {1}'.format( - name, username), python_shell=False) + retcode = __salt__["cmd.retcode"]( + "pw groupmod {0} -d {1}".format(name, username), python_shell=False + ) return not retcode def members(name, members_list): - ''' + """ Replaces members of the group with a provided list. .. versionadded:: 2015.5.4 @@ -199,9 +204,10 @@ def members(name, members_list): Replaces a membership list for a local group 'foo'. foo:x:1234:user1,user2,user3,... - ''' + """ - retcode = __salt__['cmd.retcode']('pw groupmod {0} -M {1}'.format( - name, members_list), python_shell=False) + retcode = __salt__["cmd.retcode"]( + "pw groupmod {0} -M {1}".format(name, members_list), python_shell=False + ) return not retcode diff --git a/salt/modules/pw_user.py b/salt/modules/pw_user.py index 497bfd3c6a9..598c50b3a01 100644 --- a/salt/modules/pw_user.py +++ b/salt/modules/pw_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage users with the pw command .. important:: @@ -7,7 +7,7 @@ Manage users with the pw command minion, and it is using a different module (or gives an error similar to *'user.info' is not available*), see :ref:`here `. -''' +""" # Notes: # ------ @@ -34,17 +34,10 @@ Manage users with the pw command # someuser:*:1001:1001::0:0:SomeUser Name:/home/someuser:/bin/sh # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import copy import logging -try: - import pwd - HAS_PWD = True -except ImportError: - HAS_PWD = False - -# Import 3rd party libs -from salt.ext import six # Import salt libs import salt.utils.args @@ -52,58 +45,74 @@ import salt.utils.data import salt.utils.user from salt.exceptions import CommandExecutionError +# Import 3rd party libs +from salt.ext import six + +try: + import pwd + + HAS_PWD = True +except ImportError: + HAS_PWD = False + + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'user' +__virtualname__ = "user" def __virtual__(): - ''' + """ Set the user module if the kernel is FreeBSD or DragonFly - ''' - if HAS_PWD and __grains__['kernel'] in ('FreeBSD', 'DragonFly'): + """ + if HAS_PWD and __grains__["kernel"] in ("FreeBSD", "DragonFly"): return __virtualname__ - return (False, 'The pw_user execution module cannot be loaded: the pwd python module is not available or the system is not FreeBSD.') + return ( + False, + "The pw_user execution module cannot be loaded: the pwd python module is not available or the system is not FreeBSD.", + ) def _get_gecos(name): - ''' + """ Retrieve GECOS field info and return it in dictionary form - ''' + """ try: - gecos_field = pwd.getpwnam(name).pw_gecos.split(',', 3) + gecos_field = pwd.getpwnam(name).pw_gecos.split(",", 3) except KeyError: - raise CommandExecutionError( - 'User \'{0}\' does not exist'.format(name) - ) + raise CommandExecutionError("User '{0}' does not exist".format(name)) if not gecos_field: return {} else: # Assign empty strings for any unspecified trailing GECOS fields while len(gecos_field) < 4: - gecos_field.append('') - return {'fullname': salt.utils.data.decode(gecos_field[0]), - 'roomnumber': salt.utils.data.decode(gecos_field[1]), - 'workphone': salt.utils.data.decode(gecos_field[2]), - 'homephone': salt.utils.data.decode(gecos_field[3])} + gecos_field.append("") + return { + "fullname": salt.utils.data.decode(gecos_field[0]), + "roomnumber": salt.utils.data.decode(gecos_field[1]), + "workphone": salt.utils.data.decode(gecos_field[2]), + "homephone": salt.utils.data.decode(gecos_field[3]), + } def _build_gecos(gecos_dict): - ''' + """ Accepts a dictionary entry containing GECOS field names and their values, and returns a full GECOS comment string, to be used with pw usermod. - ''' - return u'{0},{1},{2},{3}'.format(gecos_dict.get('fullname', ''), - gecos_dict.get('roomnumber', ''), - gecos_dict.get('workphone', ''), - gecos_dict.get('homephone', '')) + """ + return "{0},{1},{2},{3}".format( + gecos_dict.get("fullname", ""), + gecos_dict.get("roomnumber", ""), + gecos_dict.get("workphone", ""), + gecos_dict.get("homephone", ""), + ) def _update_gecos(name, key, value): - ''' + """ Common code to change a user's GECOS information - ''' + """ if not isinstance(value, six.string_types): value = six.text_type(value) pre_info = _get_gecos(name) @@ -113,27 +122,29 @@ def _update_gecos(name, key, value): return True gecos_data = copy.deepcopy(pre_info) gecos_data[key] = value - cmd = ['pw', 'usermod', name, '-c', _build_gecos(gecos_data)] - __salt__['cmd.run'](cmd, python_shell=False) + cmd = ["pw", "usermod", name, "-c", _build_gecos(gecos_data)] + __salt__["cmd.run"](cmd, python_shell=False) post_info = info(name) return _get_gecos(name).get(key) == value -def add(name, - uid=None, - gid=None, - groups=None, - home=None, - shell=None, - unique=True, - fullname='', - roomnumber='', - workphone='', - homephone='', - createhome=True, - loginclass=None, - **kwargs): - ''' +def add( + name, + uid=None, + gid=None, + groups=None, + home=None, + shell=None, + unique=True, + fullname="", + roomnumber="", + workphone="", + homephone="", + createhome=True, + loginclass=None, + **kwargs +): + """ Add a user to the minion CLI Example: @@ -141,43 +152,47 @@ def add(name, .. code-block:: bash salt '*' user.add name - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - if salt.utils.data.is_true(kwargs.pop('system', False)): - log.warning('pw_user module does not support the \'system\' argument') + if salt.utils.data.is_true(kwargs.pop("system", False)): + log.warning("pw_user module does not support the 'system' argument") if kwargs: - log.warning('Invalid kwargs passed to user.add') + log.warning("Invalid kwargs passed to user.add") if isinstance(groups, six.string_types): - groups = groups.split(',') - cmd = ['pw', 'useradd'] + groups = groups.split(",") + cmd = ["pw", "useradd"] if uid: - cmd.extend(['-u', uid]) + cmd.extend(["-u", uid]) if gid: - cmd.extend(['-g', gid]) + cmd.extend(["-g", gid]) if groups: - cmd.extend(['-G', ','.join(groups)]) + cmd.extend(["-G", ",".join(groups)]) if home is not None: - cmd.extend(['-d', home]) + cmd.extend(["-d", home]) if createhome is True: - cmd.append('-m') + cmd.append("-m") if loginclass: - cmd.extend(['-L', loginclass]) + cmd.extend(["-L", loginclass]) if shell: - cmd.extend(['-s', shell]) + cmd.extend(["-s", shell]) if not salt.utils.data.is_true(unique): - cmd.append('-o') - gecos_field = _build_gecos({'fullname': fullname, - 'roomnumber': roomnumber, - 'workphone': workphone, - 'homephone': homephone}) - cmd.extend(['-c', gecos_field]) - cmd.extend(['-n', name]) - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd.append("-o") + gecos_field = _build_gecos( + { + "fullname": fullname, + "roomnumber": roomnumber, + "workphone": workphone, + "homephone": homephone, + } + ) + cmd.extend(["-c", gecos_field]) + cmd.extend(["-n", name]) + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 def delete(name, remove=False, force=False): - ''' + """ Remove a user from the minion CLI Example: @@ -185,19 +200,20 @@ def delete(name, remove=False, force=False): .. code-block:: bash salt '*' user.delete name remove=True force=True - ''' + """ if salt.utils.data.is_true(force): - log.error('pw userdel does not support force-deleting user while ' - 'user is logged in') - cmd = ['pw', 'userdel'] + log.error( + "pw userdel does not support force-deleting user while " "user is logged in" + ) + cmd = ["pw", "userdel"] if remove: - cmd.append('-r') - cmd.extend(['-n', name]) - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd.append("-r") + cmd.extend(["-n", name]) + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 def getent(refresh=False): - ''' + """ Return the list of all info for all users CLI Example: @@ -205,19 +221,19 @@ def getent(refresh=False): .. code-block:: bash salt '*' user.getent - ''' - if 'user.getent' in __context__ and not refresh: - return __context__['user.getent'] + """ + if "user.getent" in __context__ and not refresh: + return __context__["user.getent"] ret = [] for data in pwd.getpwall(): ret.append(info(data.pw_name)) - __context__['user.getent'] = ret + __context__["user.getent"] = ret return ret def chuid(name, uid): - ''' + """ Change the uid for a named user CLI Example: @@ -225,21 +241,19 @@ def chuid(name, uid): .. code-block:: bash salt '*' user.chuid foo 4376 - ''' + """ pre_info = info(name) if not pre_info: - raise CommandExecutionError( - 'User \'{0}\' does not exist'.format(name) - ) - if uid == pre_info['uid']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if uid == pre_info["uid"]: return True - cmd = ['pw', 'usermod', '-u', uid, '-n', name] - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('uid') == uid + cmd = ["pw", "usermod", "-u", uid, "-n", name] + __salt__["cmd.run"](cmd, python_shell=False) + return info(name).get("uid") == uid def chgid(name, gid): - ''' + """ Change the default group of the user CLI Example: @@ -247,21 +261,19 @@ def chgid(name, gid): .. code-block:: bash salt '*' user.chgid foo 4376 - ''' + """ pre_info = info(name) if not pre_info: - raise CommandExecutionError( - 'User \'{0}\' does not exist'.format(name) - ) - if gid == pre_info['gid']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if gid == pre_info["gid"]: return True - cmd = ['pw', 'usermod', '-g', gid, '-n', name] - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('gid') == gid + cmd = ["pw", "usermod", "-g", gid, "-n", name] + __salt__["cmd.run"](cmd, python_shell=False) + return info(name).get("gid") == gid def chshell(name, shell): - ''' + """ Change the default shell of the user CLI Example: @@ -269,21 +281,19 @@ def chshell(name, shell): .. code-block:: bash salt '*' user.chshell foo /bin/zsh - ''' + """ pre_info = info(name) if not pre_info: - raise CommandExecutionError( - 'User \'{0}\' does not exist'.format(name) - ) - if shell == pre_info['shell']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if shell == pre_info["shell"]: return True - cmd = ['pw', 'usermod', '-s', shell, '-n', name] - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('shell') == shell + cmd = ["pw", "usermod", "-s", shell, "-n", name] + __salt__["cmd.run"](cmd, python_shell=False) + return info(name).get("shell") == shell def chhome(name, home, persist=False): - ''' + """ Set a new home directory for an existing user name @@ -302,23 +312,21 @@ def chhome(name, home, persist=False): .. code-block:: bash salt '*' user.chhome foo /home/users/foo True - ''' + """ pre_info = info(name) if not pre_info: - raise CommandExecutionError( - 'User \'{0}\' does not exist'.format(name) - ) - if home == pre_info['home']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if home == pre_info["home"]: return True - cmd = ['pw', 'usermod', name, '-d', home] + cmd = ["pw", "usermod", name, "-d", home] if persist: - cmd.append('-m') - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('home') == home + cmd.append("-m") + __salt__["cmd.run"](cmd, python_shell=False) + return info(name).get("home") == home def chgroups(name, groups, append=False): - ''' + """ Change the groups to which a user belongs name @@ -338,20 +346,20 @@ def chgroups(name, groups, append=False): .. code-block:: bash salt '*' user.chgroups foo wheel,root True - ''' + """ if isinstance(groups, six.string_types): - groups = groups.split(',') + groups = groups.split(",") ugrps = set(list_groups(name)) if ugrps == set(groups): return True if append: groups += ugrps - cmd = ['pw', 'usermod', '-G', ','.join(groups), '-n', name] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd = ["pw", "usermod", "-G", ",".join(groups), "-n", name] + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 def chfullname(name, fullname): - ''' + """ Change the user's Full Name CLI Example: @@ -359,12 +367,12 @@ def chfullname(name, fullname): .. code-block:: bash salt '*' user.chfullname foo "Foo Bar" - ''' - return _update_gecos(name, 'fullname', fullname) + """ + return _update_gecos(name, "fullname", fullname) def chroomnumber(name, roomnumber): - ''' + """ Change the user's Room Number CLI Example: @@ -372,12 +380,12 @@ def chroomnumber(name, roomnumber): .. code-block:: bash salt '*' user.chroomnumber foo 123 - ''' - return _update_gecos(name, 'roomnumber', roomnumber) + """ + return _update_gecos(name, "roomnumber", roomnumber) def chworkphone(name, workphone): - ''' + """ Change the user's Work Phone CLI Example: @@ -385,12 +393,12 @@ def chworkphone(name, workphone): .. code-block:: bash salt '*' user.chworkphone foo "7735550123" - ''' - return _update_gecos(name, 'workphone', workphone) + """ + return _update_gecos(name, "workphone", workphone) def chhomephone(name, homephone): - ''' + """ Change the user's Home Phone CLI Example: @@ -398,12 +406,12 @@ def chhomephone(name, homephone): .. code-block:: bash salt '*' user.chhomephone foo "7735551234" - ''' - return _update_gecos(name, 'homephone', homephone) + """ + return _update_gecos(name, "homephone", homephone) def chloginclass(name, loginclass, root=None): - ''' + """ Change the default login class of the user .. versionadded:: 2016.3.5 @@ -413,20 +421,19 @@ def chloginclass(name, loginclass, root=None): .. code-block:: bash salt '*' user.chloginclass foo staff - ''' + """ if loginclass == get_loginclass(name): return True - cmd = ['pw', 'usermod', '-L', '{0}'.format(loginclass), - '-n', '{0}'.format(name)] + cmd = ["pw", "usermod", "-L", "{0}".format(loginclass), "-n", "{0}".format(name)] - __salt__['cmd.run'](cmd, python_shell=False) + __salt__["cmd.run"](cmd, python_shell=False) return get_loginclass(name) == loginclass def info(name): - ''' + """ Return user information CLI Example: @@ -434,33 +441,33 @@ def info(name): .. code-block:: bash salt '*' user.info root - ''' + """ ret = {} try: data = pwd.getpwnam(name) - ret['gid'] = data.pw_gid - ret['groups'] = list_groups(name) - ret['home'] = data.pw_dir - ret['name'] = data.pw_name - ret['passwd'] = data.pw_passwd - ret['shell'] = data.pw_shell - ret['uid'] = data.pw_uid + ret["gid"] = data.pw_gid + ret["groups"] = list_groups(name) + ret["home"] = data.pw_dir + ret["name"] = data.pw_name + ret["passwd"] = data.pw_passwd + ret["shell"] = data.pw_shell + ret["uid"] = data.pw_uid # Put GECOS info into a list - gecos_field = data.pw_gecos.split(',', 3) + gecos_field = data.pw_gecos.split(",", 3) # Assign empty strings for any unspecified GECOS fields while len(gecos_field) < 4: - gecos_field.append('') - ret['fullname'] = gecos_field[0] - ret['roomnumber'] = gecos_field[1] - ret['workphone'] = gecos_field[2] - ret['homephone'] = gecos_field[3] + gecos_field.append("") + ret["fullname"] = gecos_field[0] + ret["roomnumber"] = gecos_field[1] + ret["workphone"] = gecos_field[2] + ret["homephone"] = gecos_field[3] except KeyError: return {} return ret def get_loginclass(name): - ''' + """ Get the login class of the user .. versionadded:: 2016.3.0 @@ -471,16 +478,16 @@ def get_loginclass(name): salt '*' user.get_loginclass foo - ''' + """ - userinfo = __salt__['cmd.run_stdout'](['pw', 'usershow', '-n', name]) - userinfo = userinfo.split(':') + userinfo = __salt__["cmd.run_stdout"](["pw", "usershow", "-n", name]) + userinfo = userinfo.split(":") - return userinfo[4] if len(userinfo) == 10 else '' + return userinfo[4] if len(userinfo) == 10 else "" def list_groups(name): - ''' + """ Return a list of groups the named user belongs to CLI Example: @@ -488,12 +495,12 @@ def list_groups(name): .. code-block:: bash salt '*' user.list_groups foo - ''' + """ return salt.utils.user.get_group_list(name) def list_users(): - ''' + """ Return a list of all users CLI Example: @@ -501,12 +508,12 @@ def list_users(): .. code-block:: bash salt '*' user.list_users - ''' + """ return sorted([user.pw_name for user in pwd.getpwall()]) def rename(name, new_name): - ''' + """ Change the username for a named user CLI Example: @@ -514,18 +521,16 @@ def rename(name, new_name): .. code-block:: bash salt '*' user.rename name new_name - ''' + """ current_info = info(name) if not current_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("User '{0}' does not exist".format(name)) new_info = info(new_name) if new_info: - raise CommandExecutionError( - 'User \'{0}\' already exists'.format(new_name) - ) - cmd = ['pw', 'usermod', '-l', new_name, '-n', name] - __salt__['cmd.run'](cmd) + raise CommandExecutionError("User '{0}' already exists".format(new_name)) + cmd = ["pw", "usermod", "-l", new_name, "-n", name] + __salt__["cmd.run"](cmd) post_info = info(new_name) - if post_info['name'] != current_info['name']: - return post_info['name'] == new_name + if post_info["name"] != current_info["name"]: + return post_info["name"] == new_name return False diff --git a/salt/modules/pyenv.py b/salt/modules/pyenv.py index 1934d18e60c..c1e90dc1cf7 100644 --- a/salt/modules/pyenv.py +++ b/salt/modules/pyenv.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage python installations with pyenv. .. note:: @@ -7,13 +7,14 @@ Manage python installations with pyenv. installed automatically by the module. .. versionadded:: v2014.04 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import python libs import os import re -import logging try: from shlex import quote as _cmd_quote # pylint: disable=E0611 @@ -24,17 +25,15 @@ except ImportError: # Set up logger log = logging.getLogger(__name__) -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} __opts__ = { - 'pyenv.root': None, - 'pyenv.build_env': None, + "pyenv.root": None, + "pyenv.build_env": None, } -def _pyenv_exec(command, args='', env=None, runas=None, ret=None): +def _pyenv_exec(command, args="", env=None, runas=None, ret=None): if not is_installed(runas): return False @@ -42,37 +41,36 @@ def _pyenv_exec(command, args='', env=None, runas=None, ret=None): path = _pyenv_path(runas) if env: - env = ' {0}'.format(env) - env = env or '' + env = " {0}".format(env) + env = env or "" - binary = 'env PYENV_ROOT={0}{1} {2}'.format(path, env, binary) + binary = "env PYENV_ROOT={0}{1} {2}".format(path, env, binary) - result = __salt__['cmd.run_all']( - '{0} {1} {2}'.format(binary, command, args), - runas=runas + result = __salt__["cmd.run_all"]( + "{0} {1} {2}".format(binary, command, args), runas=runas ) if isinstance(ret, dict): ret.update(result) return ret - if result['retcode'] == 0: - return result['stdout'] + if result["retcode"] == 0: + return result["stdout"] else: return False def _pyenv_bin(runas=None): path = _pyenv_path(runas) - return '{0}/bin/pyenv'.format(path) + return "{0}/bin/pyenv".format(path) def _pyenv_path(runas=None): path = None - if runas in (None, 'root'): - path = __salt__['config.option']('pyenv.root') or '/usr/local/pyenv' + if runas in (None, "root"): + path = __salt__["config.option"]("pyenv.root") or "/usr/local/pyenv" else: - path = __salt__['config.option']('pyenv.root') or '~{0}/.pyenv'.format(runas) + path = __salt__["config.option"]("pyenv.root") or "~{0}/.pyenv".format(runas) return os.path.expanduser(path) @@ -81,29 +79,32 @@ def _install_pyenv(path, runas=None): if os.path.isdir(path): return True - return 0 == __salt__['cmd.retcode']( - 'git clone https://github.com/yyuu/pyenv.git {0}'.format(path), runas=runas) + return 0 == __salt__["cmd.retcode"]( + "git clone https://github.com/yyuu/pyenv.git {0}".format(path), runas=runas + ) def _update_pyenv(path, runas=None): if not os.path.isdir(path): return False - return 0 == __salt__['cmd.retcode']( - 'cd {0} && git pull'.format(_cmd_quote(path)), runas=runas) + return 0 == __salt__["cmd.retcode"]( + "cd {0} && git pull".format(_cmd_quote(path)), runas=runas + ) def _update_python_build(path, runas=None): - path = '{0}/plugins/python-build'.format(path) + path = "{0}/plugins/python-build".format(path) if not os.path.isdir(path): return False - return 0 == __salt__['cmd.retcode']( - 'cd {0} && git pull'.format(_cmd_quote(path)), runas=runas) + return 0 == __salt__["cmd.retcode"]( + "cd {0} && git pull".format(_cmd_quote(path)), runas=runas + ) def install(runas=None, path=None): - ''' + """ Install pyenv systemwide CLI Example: @@ -111,14 +112,14 @@ def install(runas=None, path=None): .. code-block:: bash salt '*' pyenv.install - ''' + """ path = path or _pyenv_path(runas) path = os.path.expanduser(path) return _install_pyenv(path, runas) def update(runas=None, path=None): - ''' + """ Updates the current versions of pyenv and python-Build CLI Example: @@ -126,7 +127,7 @@ def update(runas=None, path=None): .. code-block:: bash salt '*' pyenv.update - ''' + """ path = path or _pyenv_path(runas) path = os.path.expanduser(path) @@ -134,7 +135,7 @@ def update(runas=None, path=None): def is_installed(runas=None): - ''' + """ Check if pyenv is installed. CLI Example: @@ -142,12 +143,12 @@ def is_installed(runas=None): .. code-block:: bash salt '*' pyenv.is_installed - ''' - return __salt__['cmd.has_exec'](_pyenv_bin(runas)) + """ + return __salt__["cmd.has_exec"](_pyenv_bin(runas)) def install_python(python, runas=None): - ''' + """ Install a python implementation. python @@ -159,26 +160,26 @@ def install_python(python, runas=None): .. code-block:: bash salt '*' pyenv.install_python 2.0.0-p0 - ''' - python = re.sub(r'^python-', '', python) + """ + python = re.sub(r"^python-", "", python) env = None env_list = [] - if __grains__['os'] in ('FreeBSD', 'NetBSD', 'OpenBSD'): - env_list.append('MAKE=gmake') + if __grains__["os"] in ("FreeBSD", "NetBSD", "OpenBSD"): + env_list.append("MAKE=gmake") - if __salt__['config.option']('pyenv.build_env'): - env_list.append(__salt__['config.option']('pyenv.build_env')) + if __salt__["config.option"]("pyenv.build_env"): + env_list.append(__salt__["config.option"]("pyenv.build_env")) if env_list: - env = ' '.join(env_list) + env = " ".join(env_list) ret = {} - ret = _pyenv_exec('install', python, env=env, runas=runas, ret=ret) - if ret['retcode'] == 0: + ret = _pyenv_exec("install", python, env=env, runas=runas, ret=ret) + if ret["retcode"] == 0: rehash(runas=runas) - return ret['stderr'] + return ret["stderr"] else: # Cleanup the failed installation so it doesn't list as installed uninstall_python(python, runas=runas) @@ -186,7 +187,7 @@ def install_python(python, runas=None): def uninstall_python(python, runas=None): - ''' + """ Uninstall a python implementation. python @@ -198,16 +199,16 @@ def uninstall_python(python, runas=None): .. code-block:: bash salt '*' pyenv.uninstall_python 2.0.0-p0 - ''' - python = re.sub(r'^python-', '', python) + """ + python = re.sub(r"^python-", "", python) - args = '--force {0}'.format(python) - _pyenv_exec('uninstall', args, runas=runas) + args = "--force {0}".format(python) + _pyenv_exec("uninstall", args, runas=runas) return True def versions(runas=None): - ''' + """ List the installed versions of python. CLI Example: @@ -215,13 +216,13 @@ def versions(runas=None): .. code-block:: bash salt '*' pyenv.versions - ''' - ret = _pyenv_exec('versions', '--bare', runas=runas) + """ + ret = _pyenv_exec("versions", "--bare", runas=runas) return [] if ret is False else ret.splitlines() def default(python=None, runas=None): - ''' + """ Returns or sets the currently defined default python. python=None @@ -235,17 +236,17 @@ def default(python=None, runas=None): salt '*' pyenv.default salt '*' pyenv.default 2.0.0-p0 - ''' + """ if python: - _pyenv_exec('global', python, runas=runas) + _pyenv_exec("global", python, runas=runas) return True else: - ret = _pyenv_exec('global', runas=runas) - return '' if ret is False else ret.strip() + ret = _pyenv_exec("global", runas=runas) + return "" if ret is False else ret.strip() def list_(runas=None): - ''' + """ List the installable versions of python. CLI Example: @@ -253,19 +254,19 @@ def list_(runas=None): .. code-block:: bash salt '*' pyenv.list - ''' + """ ret = [] - output = _pyenv_exec('install', '--list', runas=runas) + output = _pyenv_exec("install", "--list", runas=runas) if output: for line in output.splitlines(): - if line == 'Available versions:': + if line == "Available versions:": continue ret.append(line.strip()) return ret def rehash(runas=None): - ''' + """ Run pyenv rehash to update the installed shims. CLI Example: @@ -273,13 +274,13 @@ def rehash(runas=None): .. code-block:: bash salt '*' pyenv.rehash - ''' - _pyenv_exec('rehash', runas=runas) + """ + _pyenv_exec("rehash", runas=runas) return True def do(cmdline=None, runas=None): - ''' + """ Execute a python command with pyenv's shims from the user or the system. CLI Example: @@ -288,27 +289,27 @@ def do(cmdline=None, runas=None): salt '*' pyenv.do 'gem list bundler' salt '*' pyenv.do 'gem list bundler' deploy - ''' + """ path = _pyenv_path(runas) cmd_split = cmdline.split() - quoted_line = '' + quoted_line = "" for cmd in cmd_split: - quoted_line = quoted_line + ' ' + _cmd_quote(cmd) - result = __salt__['cmd.run_all']( - 'env PATH={0}/shims:$PATH {1}'.format(_cmd_quote(path), quoted_line), + quoted_line = quoted_line + " " + _cmd_quote(cmd) + result = __salt__["cmd.run_all"]( + "env PATH={0}/shims:$PATH {1}".format(_cmd_quote(path), quoted_line), runas=runas, - python_shell=True + python_shell=True, ) - if result['retcode'] == 0: + if result["retcode"] == 0: rehash(runas=runas) - return result['stdout'] + return result["stdout"] else: return False def do_with_python(python, cmdline, runas=None): - ''' + """ Execute a python command with pyenv's shims using a specific python version. CLI Example: @@ -317,9 +318,9 @@ def do_with_python(python, cmdline, runas=None): salt '*' pyenv.do_with_python 2.0.0-p0 'gem list bundler' salt '*' pyenv.do_with_python 2.0.0-p0 'gem list bundler' deploy - ''' + """ if python: - cmd = 'PYENV_VERSION={0} {1}'.format(python, cmdline) + cmd = "PYENV_VERSION={0} {1}".format(python, cmdline) else: cmd = cmdline diff --git a/salt/modules/qemu_img.py b/salt/modules/qemu_img.py index 69938f6c5c6..ad2b4e3c779 100644 --- a/salt/modules/qemu_img.py +++ b/salt/modules/qemu_img.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Qemu-img Command Wrapper ======================== The qemu img command is wrapped for specific functions :depends: qemu-img -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -17,16 +17,19 @@ import salt.utils.path def __virtual__(): - ''' + """ Only load if qemu-img is installed - ''' - if salt.utils.path.which('qemu-img'): - return 'qemu_img' - return (False, 'The qemu_img execution module cannot be loaded: the qemu-img binary is not in the path.') + """ + if salt.utils.path.which("qemu-img"): + return "qemu_img" + return ( + False, + "The qemu_img execution module cannot be loaded: the qemu-img binary is not in the path.", + ) def make_image(location, size, fmt): - ''' + """ Create a blank virtual machine image file of the specified size in megabytes. The image can be created in any format supported by qemu @@ -36,23 +39,21 @@ def make_image(location, size, fmt): salt '*' qemu_img.make_image /tmp/image.qcow 2048 qcow2 salt '*' qemu_img.make_image /tmp/image.raw 10240 raw - ''' + """ if not os.path.isabs(location): - return '' + return "" if not os.path.isdir(os.path.dirname(location)): - return '' - if not __salt__['cmd.retcode']( - 'qemu-img create -f {0} {1} {2}M'.format( - fmt, - location, - size), - python_shell=False): + return "" + if not __salt__["cmd.retcode"]( + "qemu-img create -f {0} {1} {2}M".format(fmt, location, size), + python_shell=False, + ): return location - return '' + return "" def convert(orig, dest, fmt): - ''' + """ Convert an existing disk image to another format using qemu-img CLI Example: @@ -60,10 +61,10 @@ def convert(orig, dest, fmt): .. code-block:: bash salt '*' qemu_img.convert /path/to/original.img /path/to/new.img qcow2 - ''' - cmd = ('qemu-img', 'convert', '-O', fmt, orig, dest) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) - if ret['retcode'] == 0: + """ + cmd = ("qemu-img", "convert", "-O", fmt, orig, dest) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) + if ret["retcode"] == 0: return True else: - return ret['stderr'] + return ret["stderr"] diff --git a/salt/modules/qemu_nbd.py b/salt/modules/qemu_nbd.py index 6a9bf3c2cfe..8923122c258 100644 --- a/salt/modules/qemu_nbd.py +++ b/salt/modules/qemu_nbd.py @@ -1,22 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" Qemu Command Wrapper The qemu system comes with powerful tools, such as qemu-img and qemu-nbd which are used here to build up kvm images. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import glob +import logging +import os import tempfile import time -import logging + +import salt.crypt # Import salt libs import salt.utils.path -import salt.crypt # Import 3rd-party libs from salt.ext import six @@ -26,16 +28,19 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if qemu-img and qemu-nbd are installed - ''' - if salt.utils.path.which('qemu-nbd'): - return 'qemu_nbd' - return (False, 'The qemu_nbd execution module cannot be loaded: the qemu-nbd binary is not in the path.') + """ + if salt.utils.path.which("qemu-nbd"): + return "qemu_nbd" + return ( + False, + "The qemu_nbd execution module cannot be loaded: the qemu-nbd binary is not in the path.", + ) def connect(image): - ''' + """ Activate nbd for an image file. CLI Example: @@ -43,33 +48,32 @@ def connect(image): .. code-block:: bash salt '*' qemu_nbd.connect /tmp/image.raw - ''' + """ if not os.path.isfile(image): - log.warning('Could not connect image: %s does not exist', image) - return '' + log.warning("Could not connect image: %s does not exist", image) + return "" - if salt.utils.path.which('sfdisk'): - fdisk = 'sfdisk -d' + if salt.utils.path.which("sfdisk"): + fdisk = "sfdisk -d" else: - fdisk = 'fdisk -l' - __salt__['cmd.run']('modprobe nbd max_part=63') - for nbd in glob.glob('/dev/nbd?'): - if __salt__['cmd.retcode']('{0} {1}'.format(fdisk, nbd)): + fdisk = "fdisk -l" + __salt__["cmd.run"]("modprobe nbd max_part=63") + for nbd in glob.glob("/dev/nbd?"): + if __salt__["cmd.retcode"]("{0} {1}".format(fdisk, nbd)): while True: # Sometimes nbd does not "take hold", loop until we can verify - __salt__['cmd.run']( - 'qemu-nbd -c {0} {1}'.format(nbd, image), - python_shell=False, - ) - if not __salt__['cmd.retcode']('{0} {1}'.format(fdisk, nbd)): + __salt__["cmd.run"]( + "qemu-nbd -c {0} {1}".format(nbd, image), python_shell=False, + ) + if not __salt__["cmd.retcode"]("{0} {1}".format(fdisk, nbd)): break return nbd - log.warning('Could not connect image: %s', image) - return '' + log.warning("Could not connect image: %s", image) + return "" def mount(nbd, root=None): - ''' + """ Pass in the nbd connection device location, mount all partitions and return a dict of mount points @@ -78,22 +82,17 @@ def mount(nbd, root=None): .. code-block:: bash salt '*' qemu_nbd.mount /dev/nbd0 - ''' - __salt__['cmd.run']( - 'partprobe {0}'.format(nbd), - python_shell=False, - ) + """ + __salt__["cmd.run"]( + "partprobe {0}".format(nbd), python_shell=False, + ) ret = {} if root is None: - root = os.path.join( - tempfile.gettempdir(), - 'nbd', - os.path.basename(nbd) - ) - for part in glob.glob('{0}p*'.format(nbd)): + root = os.path.join(tempfile.gettempdir(), "nbd", os.path.basename(nbd)) + for part in glob.glob("{0}p*".format(nbd)): m_pt = os.path.join(root, os.path.basename(part)) time.sleep(1) - mnt = __salt__['mount.mount'](m_pt, part, True) + mnt = __salt__["mount.mount"](m_pt, part, True) if mnt is not True: continue ret[m_pt] = part @@ -101,7 +100,7 @@ def mount(nbd, root=None): def init(image, root=None): - ''' + """ Mount the named image via qemu-nbd and return the mounted roots CLI Example: @@ -109,15 +108,15 @@ def init(image, root=None): .. code-block:: bash salt '*' qemu_nbd.init /srv/image.qcow2 - ''' + """ nbd = connect(image) if not nbd: - return '' + return "" return mount(nbd, root) def clear(mnt): - ''' + """ Pass in the mnt dict returned from nbd_mount to unmount and disconnect the image from nbd. If all of the partitions are unmounted return an empty dict, otherwise return a dict containing the still mounted @@ -128,16 +127,16 @@ def clear(mnt): .. code-block:: bash salt '*' qemu_nbd.clear '{"/mnt/foo": "/dev/nbd0p1"}' - ''' + """ ret = {} nbds = set() for m_pt, dev in six.iteritems(mnt): - mnt_ret = __salt__['mount.umount'](m_pt) + mnt_ret = __salt__["mount.umount"](m_pt) if mnt_ret is not True: ret[m_pt] = dev - nbds.add(dev[:dev.rindex('p')]) + nbds.add(dev[: dev.rindex("p")]) if ret: return ret for nbd in nbds: - __salt__['cmd.run']('qemu-nbd -d {0}'.format(nbd), python_shell=False) + __salt__["cmd.run"]("qemu-nbd -d {0}".format(nbd), python_shell=False) return ret diff --git a/salt/modules/quota.py b/salt/modules/quota.py index 8c299a6a129..b568d1cdbc1 100644 --- a/salt/modules/quota.py +++ b/salt/modules/quota.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing quotas on POSIX-like systems. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -16,27 +16,24 @@ log = logging.getLogger(__name__) # Define a function alias in order not to shadow built-in's -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Only work on POSIX-like systems with setquota binary available - ''' - if not salt.utils.platform.is_windows() \ - and salt.utils.path.which('setquota'): - return 'quota' + """ + if not salt.utils.platform.is_windows() and salt.utils.path.which("setquota"): + return "quota" return ( False, - 'The quota execution module cannot be loaded: the module is only ' - 'available on POSIX-like systems with the setquota binary available.' + "The quota execution module cannot be loaded: the module is only " + "available on POSIX-like systems with the setquota binary available.", ) def report(mount): - ''' + """ Report on quotas for a specific volume CLI Example: @@ -44,58 +41,58 @@ def report(mount): .. code-block:: bash salt '*' quota.report /media/data - ''' + """ ret = {mount: {}} - ret[mount]['User Quotas'] = _parse_quota(mount, '-u') - ret[mount]['Group Quotas'] = _parse_quota(mount, '-g') + ret[mount]["User Quotas"] = _parse_quota(mount, "-u") + ret[mount]["Group Quotas"] = _parse_quota(mount, "-g") return ret def _parse_quota(mount, opts): - ''' + """ Parse the output from repquota. Requires that -u -g are passed in - ''' - cmd = 'repquota -vp {0} {1}'.format(opts, mount) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - mode = 'header' + """ + cmd = "repquota -vp {0} {1}".format(opts, mount) + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() + mode = "header" - if '-u' in opts: - quotatype = 'Users' - elif '-g' in opts: - quotatype = 'Groups' + if "-u" in opts: + quotatype = "Users" + elif "-g" in opts: + quotatype = "Groups" ret = {quotatype: {}} for line in out: if not line: continue comps = line.split() - if mode == 'header': - if 'Block grace time' in line: - blockg, inodeg = line.split(';') - blockgc = blockg.split(': ') - inodegc = inodeg.split(': ') - ret['Block Grace Time'] = blockgc[-1:] - ret['Inode Grace Time'] = inodegc[-1:] - elif line.startswith('-'): - mode = 'quotas' - elif mode == 'quotas': + if mode == "header": + if "Block grace time" in line: + blockg, inodeg = line.split(";") + blockgc = blockg.split(": ") + inodegc = inodeg.split(": ") + ret["Block Grace Time"] = blockgc[-1:] + ret["Inode Grace Time"] = inodegc[-1:] + elif line.startswith("-"): + mode = "quotas" + elif mode == "quotas": if len(comps) < 8: continue if not comps[0] in ret[quotatype]: ret[quotatype][comps[0]] = {} - ret[quotatype][comps[0]]['block-used'] = comps[2] - ret[quotatype][comps[0]]['block-soft-limit'] = comps[3] - ret[quotatype][comps[0]]['block-hard-limit'] = comps[4] - ret[quotatype][comps[0]]['block-grace'] = comps[5] - ret[quotatype][comps[0]]['file-used'] = comps[6] - ret[quotatype][comps[0]]['file-soft-limit'] = comps[7] - ret[quotatype][comps[0]]['file-hard-limit'] = comps[8] - ret[quotatype][comps[0]]['file-grace'] = comps[9] + ret[quotatype][comps[0]]["block-used"] = comps[2] + ret[quotatype][comps[0]]["block-soft-limit"] = comps[3] + ret[quotatype][comps[0]]["block-hard-limit"] = comps[4] + ret[quotatype][comps[0]]["block-grace"] = comps[5] + ret[quotatype][comps[0]]["file-used"] = comps[6] + ret[quotatype][comps[0]]["file-soft-limit"] = comps[7] + ret[quotatype][comps[0]]["file-hard-limit"] = comps[8] + ret[quotatype][comps[0]]["file-grace"] = comps[9] return ret def set_(device, **kwargs): - ''' + """ Calls out to setquota, for a specific user or group CLI Example: @@ -104,59 +101,66 @@ def set_(device, **kwargs): salt '*' quota.set /media/data user=larry block-soft-limit=1048576 salt '*' quota.set /media/data group=painters file-hard-limit=1000 - ''' - empty = {'block-soft-limit': 0, 'block-hard-limit': 0, - 'file-soft-limit': 0, 'file-hard-limit': 0} + """ + empty = { + "block-soft-limit": 0, + "block-hard-limit": 0, + "file-soft-limit": 0, + "file-hard-limit": 0, + } current = None - cmd = 'setquota' - if 'user' in kwargs: - cmd += ' -u {0} '.format(kwargs['user']) - parsed = _parse_quota(device, '-u') - if kwargs['user'] in parsed: - current = parsed['Users'][kwargs['user']] + cmd = "setquota" + if "user" in kwargs: + cmd += " -u {0} ".format(kwargs["user"]) + parsed = _parse_quota(device, "-u") + if kwargs["user"] in parsed: + current = parsed["Users"][kwargs["user"]] else: current = empty - ret = 'User: {0}'.format(kwargs['user']) + ret = "User: {0}".format(kwargs["user"]) - if 'group' in kwargs: - if 'user' in kwargs: - raise SaltInvocationError( - 'Please specify a user or group, not both.' - ) - cmd += ' -g {0} '.format(kwargs['group']) - parsed = _parse_quota(device, '-g') - if kwargs['group'] in parsed: - current = parsed['Groups'][kwargs['group']] + if "group" in kwargs: + if "user" in kwargs: + raise SaltInvocationError("Please specify a user or group, not both.") + cmd += " -g {0} ".format(kwargs["group"]) + parsed = _parse_quota(device, "-g") + if kwargs["group"] in parsed: + current = parsed["Groups"][kwargs["group"]] else: current = empty - ret = 'Group: {0}'.format(kwargs['group']) + ret = "Group: {0}".format(kwargs["group"]) if not current: - raise CommandExecutionError('A valid user or group was not found') + raise CommandExecutionError("A valid user or group was not found") - for limit in ('block-soft-limit', 'block-hard-limit', - 'file-soft-limit', 'file-hard-limit'): + for limit in ( + "block-soft-limit", + "block-hard-limit", + "file-soft-limit", + "file-hard-limit", + ): if limit in kwargs: current[limit] = kwargs[limit] - cmd += '{0} {1} {2} {3} {4}'.format(current['block-soft-limit'], - current['block-hard-limit'], - current['file-soft-limit'], - current['file-hard-limit'], - device) + cmd += "{0} {1} {2} {3} {4}".format( + current["block-soft-limit"], + current["block-hard-limit"], + current["file-soft-limit"], + current["file-hard-limit"], + device, + ) - result = __salt__['cmd.run_all'](cmd, python_shell=False) - if result['retcode'] != 0: + result = __salt__["cmd.run_all"](cmd, python_shell=False) + if result["retcode"] != 0: raise CommandExecutionError( - 'Unable to set desired quota. Error follows: \n{0}' - .format(result['stderr']) + "Unable to set desired quota. Error follows: \n{0}".format(result["stderr"]) ) return {ret: current} def warn(): - ''' + """ Runs the warnquota command, to send warning emails to users who are over their quota limit. @@ -165,12 +169,12 @@ def warn(): .. code-block:: bash salt '*' quota.warn - ''' - __salt__['cmd.run']('quotawarn') + """ + __salt__["cmd.run"]("quotawarn") def stats(): - ''' + """ Runs the quotastats command, and returns the parsed output CLI Example: @@ -178,20 +182,20 @@ def stats(): .. code-block:: bash salt '*' quota.stats - ''' + """ ret = {} - out = __salt__['cmd.run']('quotastats').splitlines() + out = __salt__["cmd.run"]("quotastats").splitlines() for line in out: if not line: continue - comps = line.split(': ') + comps = line.split(": ") ret[comps[0]] = comps[1] return ret def on(device): - ''' + """ Turns on the quota system CLI Example: @@ -199,14 +203,14 @@ def on(device): .. code-block:: bash salt '*' quota.on - ''' - cmd = 'quotaon {0}'.format(device) - __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = "quotaon {0}".format(device) + __salt__["cmd.run"](cmd, python_shell=False) return True def off(device): - ''' + """ Turns off the quota system CLI Example: @@ -214,14 +218,14 @@ def off(device): .. code-block:: bash salt '*' quota.off - ''' - cmd = 'quotaoff {0}'.format(device) - __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = "quotaoff {0}".format(device) + __salt__["cmd.run"](cmd, python_shell=False) return True def get_mode(device): - ''' + """ Report whether the quota system for this device is on or off CLI Example: @@ -229,23 +233,23 @@ def get_mode(device): .. code-block:: bash salt '*' quota.get_mode - ''' + """ ret = {} - cmd = 'quotaon -p {0}'.format(device) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "quotaon -p {0}".format(device) + out = __salt__["cmd.run"](cmd, python_shell=False) for line in out.splitlines(): comps = line.strip().split() if comps[3] not in ret: - if comps[0].startswith('quotaon'): - if comps[1].startswith('Mountpoint'): - ret[comps[4]] = 'disabled' + if comps[0].startswith("quotaon"): + if comps[1].startswith("Mountpoint"): + ret[comps[4]] = "disabled" continue - elif comps[1].startswith('Cannot'): - ret[device] = 'Not found' + elif comps[1].startswith("Cannot"): + ret[device] = "Not found" return ret continue ret[comps[3]] = { - 'device': comps[4].replace('(', '').replace(')', ''), + "device": comps[4].replace("(", "").replace(")", ""), } ret[comps[3]][comps[0]] = comps[6] return ret diff --git a/salt/modules/rabbitmq.py b/salt/modules/rabbitmq.py index 1edc4e24fa3..af50f693879 100644 --- a/salt/modules/rabbitmq.py +++ b/salt/modules/rabbitmq.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide RabbitMQ compatibility to Salt. Todo: A lot, need to add cluster support, logging, and minion configuration data. -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import os import random @@ -19,11 +20,11 @@ import salt.utils.path import salt.utils.platform import salt.utils.user from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.utils.versions import LooseVersion as _LooseVersion # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range +from salt.utils.versions import LooseVersion as _LooseVersion log = logging.getLogger(__name__) @@ -32,185 +33,197 @@ RABBITMQ_PLUGINS = None def __virtual__(): - ''' + """ Verify RabbitMQ is installed. - ''' + """ global RABBITMQCTL global RABBITMQ_PLUGINS if salt.utils.platform.is_windows(): from salt.ext.six.moves import winreg + key = None try: key = winreg.OpenKeyEx( winreg.HKEY_LOCAL_MACHINE, - 'SOFTWARE\\VMware, Inc.\\RabbitMQ Server', + "SOFTWARE\\VMware, Inc.\\RabbitMQ Server", 0, - winreg.KEY_READ | winreg.KEY_WOW64_32KEY - ) - (dir_path, value_type) = winreg.QueryValueEx( - key, - 'Install_Dir' + winreg.KEY_READ | winreg.KEY_WOW64_32KEY, ) + (dir_path, value_type) = winreg.QueryValueEx(key, "Install_Dir") if value_type != winreg.REG_SZ: - raise TypeError('Invalid RabbitMQ Server directory type: {0}'.format(value_type)) + raise TypeError( + "Invalid RabbitMQ Server directory type: {0}".format(value_type) + ) if not os.path.isdir(dir_path): - raise IOError('RabbitMQ directory not found: {0}'.format(dir_path)) - subdir_match = '' + raise IOError("RabbitMQ directory not found: {0}".format(dir_path)) + subdir_match = "" for name in os.listdir(dir_path): - if name.startswith('rabbitmq_server-'): + if name.startswith("rabbitmq_server-"): subdir_path = os.path.join(dir_path, name) # Get the matching entry that is last in ASCII order. if os.path.isdir(subdir_path) and subdir_path > subdir_match: subdir_match = subdir_path if not subdir_match: - raise IOError('"rabbitmq_server-*" subdirectory not found in: {0}'.format(dir_path)) - RABBITMQCTL = os.path.join(subdir_match, 'sbin', 'rabbitmqctl.bat') - RABBITMQ_PLUGINS = os.path.join(subdir_match, 'sbin', 'rabbitmq-plugins.bat') + raise IOError( + '"rabbitmq_server-*" subdirectory not found in: {0}'.format( + dir_path + ) + ) + RABBITMQCTL = os.path.join(subdir_match, "sbin", "rabbitmqctl.bat") + RABBITMQ_PLUGINS = os.path.join( + subdir_match, "sbin", "rabbitmq-plugins.bat" + ) except Exception: # pylint: disable=broad-except pass finally: if key is not None: winreg.CloseKey(key) else: - RABBITMQCTL = salt.utils.path.which('rabbitmqctl') - RABBITMQ_PLUGINS = salt.utils.path.which('rabbitmq-plugins') + RABBITMQCTL = salt.utils.path.which("rabbitmqctl") + RABBITMQ_PLUGINS = salt.utils.path.which("rabbitmq-plugins") if not RABBITMQCTL: - return (False, 'Module rabbitmq: module only works when RabbitMQ is installed') + return (False, "Module rabbitmq: module only works when RabbitMQ is installed") return True def _check_response(response): if isinstance(response, dict): - if response['retcode'] != 0 or response['stderr']: + if response["retcode"] != 0 or response["stderr"]: raise CommandExecutionError( - 'RabbitMQ command failed: {0}'.format(response['stderr']) + "RabbitMQ command failed: {0}".format(response["stderr"]) ) else: - if 'Error' in response: - raise CommandExecutionError( - 'RabbitMQ command failed: {0}'.format(response) - ) + if "Error" in response: + raise CommandExecutionError("RabbitMQ command failed: {0}".format(response)) def _format_response(response, msg): if isinstance(response, dict): - if response['retcode'] != 0 or response['stderr']: + if response["retcode"] != 0 or response["stderr"]: raise CommandExecutionError( - 'RabbitMQ command failed: {0}'.format(response['stderr']) + "RabbitMQ command failed: {0}".format(response["stderr"]) ) else: - response = response['stdout'] + response = response["stdout"] else: - if 'Error' in response: - raise CommandExecutionError( - 'RabbitMQ command failed: {0}'.format(response) - ) - return { - msg: response - } + if "Error" in response: + raise CommandExecutionError("RabbitMQ command failed: {0}".format(response)) + return {msg: response} def _get_rabbitmq_plugin(): - ''' + """ Returns the rabbitmq-plugin command path if we're running an OS that doesn't put it in the standard /usr/bin or /usr/local/bin This works by taking the rabbitmq-server version and looking for where it seems to be hidden in /usr/lib. - ''' + """ global RABBITMQ_PLUGINS if RABBITMQ_PLUGINS is None: - version = __salt__['pkg.version']('rabbitmq-server').split('-')[0] - RABBITMQ_PLUGINS = ('/usr/lib/rabbitmq/lib/rabbitmq_server-{0}' - '/sbin/rabbitmq-plugins').format(version) + version = __salt__["pkg.version"]("rabbitmq-server").split("-")[0] + RABBITMQ_PLUGINS = ( + "/usr/lib/rabbitmq/lib/rabbitmq_server-{0}" "/sbin/rabbitmq-plugins" + ).format(version) return RABBITMQ_PLUGINS def _safe_output(line): - ''' + """ Looks for rabbitmqctl warning, or general formatting, strings that aren't intended to be parsed as output. Returns a boolean whether the line can be parsed as rabbitmqctl output. - ''' - return not any([ - line.startswith('Listing') and line.endswith('...'), - line.startswith('Listing') and '\t' not in line, - '...done' in line, - line.startswith('WARNING:'), - len(line) == 0 - ]) + """ + return not any( + [ + line.startswith("Listing") and line.endswith("..."), + line.startswith("Listing") and "\t" not in line, + "...done" in line, + line.startswith("WARNING:"), + len(line) == 0, + ] + ) def _strip_listing_to_done(output_list): - ''' + """ Conditionally remove non-relevant first and last line, "Listing ..." - "...done". outputlist: rabbitmq command output split by newline return value: list, conditionally modified, may be empty. - ''' + """ return [line for line in output_list if _safe_output(line)] def _output_to_dict(cmdoutput, values_mapper=None): - ''' + """ Convert rabbitmqctl output to a dict of data cmdoutput: string output of rabbitmqctl commands values_mapper: function object to process the values part of each line - ''' + """ if isinstance(cmdoutput, dict): - if cmdoutput['retcode'] != 0 or cmdoutput['stderr']: + if cmdoutput["retcode"] != 0 or cmdoutput["stderr"]: raise CommandExecutionError( - 'RabbitMQ command failed: {0}'.format(cmdoutput['stderr']) + "RabbitMQ command failed: {0}".format(cmdoutput["stderr"]) ) - cmdoutput = cmdoutput['stdout'] + cmdoutput = cmdoutput["stdout"] ret = {} if values_mapper is None: - values_mapper = lambda string: string.split('\t') + values_mapper = lambda string: string.split("\t") # remove first and last line: Listing ... - ...done data_rows = _strip_listing_to_done(cmdoutput.splitlines()) for row in data_rows: try: - key, values = row.split('\t', 1) + key, values = row.split("\t", 1) except ValueError: # If we have reached this far, we've hit an edge case where the row # only has one item: the key. The key doesn't have any values, so we # set it to an empty string to preserve rabbitmq reporting behavior. # e.g. A user's permission string for '/' is set to ['', '', ''], # Rabbitmq reports this only as '/' from the rabbitmqctl command. - log.debug('Could not find any values for key \'%s\'. ' - 'Setting to \'%s\' to an empty string.', row, row) - ret[row] = '' + log.debug( + "Could not find any values for key '%s'. " + "Setting to '%s' to an empty string.", + row, + row, + ) + ret[row] = "" continue ret[key] = values_mapper(values) return ret def _output_to_list(cmdoutput): - ''' + """ Convert rabbitmqctl output to a list of strings (assuming whitespace-delimited output). Ignores output lines that shouldn't be parsed, like warnings. cmdoutput: string output of rabbitmqctl commands - ''' - return [item for line in cmdoutput.splitlines() if _safe_output(line) for item in line.split()] + """ + return [ + item + for line in cmdoutput.splitlines() + if _safe_output(line) + for item in line.split() + ] def _output_lines_to_list(cmdoutput): - ''' + """ Convert rabbitmqctl output to a list of strings (assuming newline-delimited output). Ignores output lines that shouldn't be parsed, like warnings. cmdoutput: string output of rabbitmqctl commands - ''' + """ return [line.strip() for line in cmdoutput.splitlines() if _safe_output(line)] def list_users(runas=None): - ''' + """ Return a list of users based off of rabbitmqctl user_list. CLI Example: @@ -218,26 +231,30 @@ def list_users(runas=None): .. code-block:: bash salt '*' rabbitmq.list_users - ''' + """ # Windows runas currently requires a password. # Due to this, don't use a default value for # runas in Windows. if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'list_users', '-q'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "list_users", "-q"], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) # func to get tags from string such as "[admin, monitoring]" - func = lambda string: [x.strip() for x in string[1:-1].split(',')] if ',' in string else [x for x in - string[1:-1].split(' ')] + func = ( + lambda string: [x.strip() for x in string[1:-1].split(",")] + if "," in string + else [x for x in string[1:-1].split(" ")] + ) return _output_to_dict(res, func) def list_vhosts(runas=None): - ''' + """ Return a list of vhost based on rabbitmqctl list_vhosts. CLI Example: @@ -245,20 +262,21 @@ def list_vhosts(runas=None): .. code-block:: bash salt '*' rabbitmq.list_vhosts - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'list_vhosts', '-q'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "list_vhosts", "-q"], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) - return _output_to_list(res['stdout']) + return _output_to_list(res["stdout"]) def list_upstreams(runas=None): - ''' + """ Returns a dict of upstreams based on rabbitmqctl list_parameters. :param str runas: The name of the user to run this command as. @@ -270,24 +288,25 @@ def list_upstreams(runas=None): salt '*' rabbitmq.list_upstreams .. versionadded:: 3000 - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() ret = {} - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'list_parameters', '-q'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "list_parameters", "-q"], reset_system_locale=False, runas=runas, - python_shell=False) - for raw_line in res['stdout'].split('\n'): + python_shell=False, + ) + for raw_line in res["stdout"].split("\n"): if _safe_output(raw_line): - (_, name, definition) = raw_line.split('\t') + (_, name, definition) = raw_line.split("\t") ret[name] = definition return ret def user_exists(name, runas=None): - ''' + """ Return whether the user exists based on rabbitmqctl list_users. CLI Example: @@ -295,14 +314,14 @@ def user_exists(name, runas=None): .. code-block:: bash salt '*' rabbitmq.user_exists rabbit_user - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() return name in list_users(runas=runas) def vhost_exists(name, runas=None): - ''' + """ Return whether the vhost exists based on rabbitmqctl list_vhosts. CLI Example: @@ -310,14 +329,14 @@ def vhost_exists(name, runas=None): .. code-block:: bash salt '*' rabbitmq.vhost_exists rabbit_host - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() return name in list_vhosts(runas=runas) def upstream_exists(name, runas=None): - ''' + """ Return whether the upstreamexists based on rabbitmqctl list_parameters. :param str name: The name of the upstream to check for. @@ -330,14 +349,14 @@ def upstream_exists(name, runas=None): salt '*' rabbitmq.upstream_exists rabbit_upstream .. versionadded:: 3000 - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() return name in list_upstreams(runas=runas) def add_user(name, password=None, runas=None): - ''' + """ Add a rabbitMQ user via rabbitmqctl user_add CLI Example: @@ -345,14 +364,16 @@ def add_user(name, password=None, runas=None): .. code-block:: bash salt '*' rabbitmq.add_user rabbit_user password - ''' + """ clear_pw = False if password is None: # Generate a random, temporary password. RabbitMQ requires one. clear_pw = True - password = ''.join(random.SystemRandom().choice( - string.ascii_uppercase + string.digits) for x in range(15)) + password = "".join( + random.SystemRandom().choice(string.ascii_uppercase + string.digits) + for x in range(15) + ) if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() @@ -365,19 +386,18 @@ def add_user(name, password=None, runas=None): # command,\r\noperable program or batch file. # Work around this by using a shell and a quoted command. python_shell = True - cmd = '"{0}" add_user "{1}" "{2}"'.format( - RABBITMQCTL, name, password - ) + cmd = '"{0}" add_user "{1}" "{2}"'.format(RABBITMQCTL, name, password) else: python_shell = False - cmd = [RABBITMQCTL, 'add_user', name, password] + cmd = [RABBITMQCTL, "add_user", name, password] - res = __salt__['cmd.run_all']( + res = __salt__["cmd.run_all"]( cmd, reset_system_locale=False, - output_loglevel='quiet', + output_loglevel="quiet", runas=runas, - python_shell=python_shell) + python_shell=python_shell, + ) if clear_pw: # Now, Clear the random password from the account, if necessary @@ -389,12 +409,12 @@ def add_user(name, password=None, runas=None): delete_user(name, runas) raise - msg = 'Added' + msg = "Added" return _format_response(res, msg) def delete_user(name, runas=None): - ''' + """ Deletes a user via rabbitmqctl delete_user. CLI Example: @@ -402,21 +422,22 @@ def delete_user(name, runas=None): .. code-block:: bash salt '*' rabbitmq.delete_user rabbit_user - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'delete_user', name], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "delete_user", name], reset_system_locale=False, python_shell=False, - runas=runas) - msg = 'Deleted' + runas=runas, + ) + msg = "Deleted" return _format_response(res, msg) def change_password(name, password, runas=None): - ''' + """ Changes a user's password. CLI Example: @@ -424,7 +445,7 @@ def change_password(name, password, runas=None): .. code-block:: bash salt '*' rabbitmq.change_password rabbit_user password - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() if salt.utils.platform.is_windows(): @@ -435,25 +456,24 @@ def change_password(name, password, runas=None): # command,\r\noperable program or batch file. # Work around this by using a shell and a quoted command. python_shell = True - cmd = '"{0}" change_password "{1}" "{2}"'.format( - RABBITMQCTL, name, password - ) + cmd = '"{0}" change_password "{1}" "{2}"'.format(RABBITMQCTL, name, password) else: python_shell = False - cmd = [RABBITMQCTL, 'change_password', name, password] - res = __salt__['cmd.run_all']( + cmd = [RABBITMQCTL, "change_password", name, password] + res = __salt__["cmd.run_all"]( cmd, reset_system_locale=False, runas=runas, - output_loglevel='quiet', - python_shell=python_shell) - msg = 'Password Changed' + output_loglevel="quiet", + python_shell=python_shell, + ) + msg = "Password Changed" return _format_response(res, msg) def clear_password(name, runas=None): - ''' + """ Removes a user's password. CLI Example: @@ -461,21 +481,22 @@ def clear_password(name, runas=None): .. code-block:: bash salt '*' rabbitmq.clear_password rabbit_user - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'clear_password', name], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "clear_password", name], reset_system_locale=False, runas=runas, - python_shell=False) - msg = 'Password Cleared' + python_shell=False, + ) + msg = "Password Cleared" return _format_response(res, msg) def check_password(name, password, runas=None): - ''' + """ .. versionadded:: 2016.3.0 Checks if a user's password is valid. @@ -485,25 +506,32 @@ def check_password(name, password, runas=None): .. code-block:: bash salt '*' rabbitmq.check_password rabbit_user password - ''' + """ # try to get the rabbitmq-version - adapted from _get_rabbitmq_plugin if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() try: - res = __salt__['cmd.run']( - [RABBITMQCTL, 'status'], + res = __salt__["cmd.run"]( + [RABBITMQCTL, "status"], reset_system_locale=False, runas=runas, - python_shell=False) - server_version = re.search(r'\{rabbit,"RabbitMQ","(.+)"\}', res) + python_shell=False, + ) + # Check regex against older RabbitMQ version status output + old_server_version = re.search(r'\{rabbit,"RabbitMQ","(.+)"\}', res) + # Check regex against newer RabbitMQ version status output + server_version = re.search(r"RabbitMQ version:\s*(.+)", res) - if server_version is None: + if server_version is None and old_server_version is None: raise ValueError - server_version = server_version.group(1).split('-')[0] - version = [int(i) for i in server_version.split('.')] + if old_server_version: + server_version = old_server_version + server_version = server_version.group(1).split("-")[0] + version = [int(i) for i in server_version.split(".")] + except ValueError: version = (0, 0, 0) if len(version) < 3: @@ -524,43 +552,45 @@ def check_password(name, password, runas=None): ) else: python_shell = False - cmd = [RABBITMQCTL, 'authenticate_user', name, password] + cmd = [RABBITMQCTL, "authenticate_user", name, password] - res = __salt__['cmd.run_all']( + res = __salt__["cmd.run_all"]( cmd, reset_system_locale=False, runas=runas, - output_loglevel='quiet', - python_shell=python_shell) + output_loglevel="quiet", + python_shell=python_shell, + ) - if res['retcode'] != 0 or res['stderr']: + if res["retcode"] != 0 or res["stderr"]: return False return True - cmd = ('rabbit_auth_backend_internal:check_user_login' - '(<<"{0}">>, [{{password, <<"{1}">>}}]).').format( - name.replace('"', '\\"'), - password.replace('"', '\\"')) + cmd = ( + "rabbit_auth_backend_internal:check_user_login" + '(<<"{0}">>, [{{password, <<"{1}">>}}]).' + ).format(name.replace('"', '\\"'), password.replace('"', '\\"')) - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'eval', cmd], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "eval", cmd], reset_system_locale=False, runas=runas, - output_loglevel='quiet', - python_shell=False) - msg = 'password-check' + output_loglevel="quiet", + python_shell=False, + ) + msg = "password-check" _response = _format_response(res, msg) _key = _response.keys()[0] - if 'invalid credentials' in _response[_key]: + if "invalid credentials" in _response[_key]: return False return True def add_vhost(vhost, runas=None): - ''' + """ Adds a vhost via rabbitmqctl add_vhost. CLI Example: @@ -568,21 +598,22 @@ def add_vhost(vhost, runas=None): .. code-block:: bash salt '*' rabbitmq add_vhost '' - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'add_vhost', vhost], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "add_vhost", vhost], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) - msg = 'Added' + msg = "Added" return _format_response(res, msg) def delete_vhost(vhost, runas=None): - ''' + """ Deletes a vhost rabbitmqctl delete_vhost. CLI Example: @@ -590,20 +621,21 @@ def delete_vhost(vhost, runas=None): .. code-block:: bash salt '*' rabbitmq.delete_vhost '' - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'delete_vhost', vhost], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "delete_vhost", vhost], reset_system_locale=False, runas=runas, - python_shell=False) - msg = 'Deleted' + python_shell=False, + ) + msg = "Deleted" return _format_response(res, msg) -def set_permissions(vhost, user, conf='.*', write='.*', read='.*', runas=None): - ''' +def set_permissions(vhost, user, conf=".*", write=".*", read=".*", runas=None): + """ Sets permissions for vhost via rabbitmqctl set_permissions CLI Example: @@ -611,21 +643,21 @@ def set_permissions(vhost, user, conf='.*', write='.*', read='.*', runas=None): .. code-block:: bash salt '*' rabbitmq.set_permissions myvhost myuser - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'set_permissions', '-p', - vhost, user, conf, write, read], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "set_permissions", "-p", vhost, user, conf, write, read], reset_system_locale=False, runas=runas, - python_shell=False) - msg = 'Permissions Set' + python_shell=False, + ) + msg = "Permissions Set" return _format_response(res, msg) def list_permissions(vhost, runas=None): - ''' + """ Lists permissions for vhost via rabbitmqctl list_permissions CLI Example: @@ -633,20 +665,21 @@ def list_permissions(vhost, runas=None): .. code-block:: bash salt '*' rabbitmq.list_permissions /myvhost - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'list_permissions', '-q', '-p', vhost], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "list_permissions", "-q", "-p", vhost], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) return _output_to_dict(res) def list_user_permissions(name, runas=None): - ''' + """ List permissions for a user via rabbitmqctl list_user_permissions CLI Example: @@ -654,44 +687,46 @@ def list_user_permissions(name, runas=None): .. code-block:: bash salt '*' rabbitmq.list_user_permissions user - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'list_user_permissions', name, '-q'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "list_user_permissions", name, "-q"], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) return _output_to_dict(res) def set_user_tags(name, tags, runas=None): - '''Add user tags via rabbitmqctl set_user_tags + """Add user tags via rabbitmqctl set_user_tags CLI Example: .. code-block:: bash salt '*' rabbitmq.set_user_tags myadmin administrator - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() if not isinstance(tags, (list, tuple)): tags = [tags] - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'set_user_tags', name] + list(tags), + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "set_user_tags", name] + list(tags), reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) msg = "Tag(s) set" return _format_response(res, msg) def status(runas=None): - ''' + """ return rabbitmq status CLI Example: @@ -699,20 +734,21 @@ def status(runas=None): .. code-block:: bash salt '*' rabbitmq.status - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'status'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "status"], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) - return res['stdout'] + return res["stdout"] def cluster_status(runas=None): - ''' + """ return rabbitmq cluster_status CLI Example: @@ -720,20 +756,21 @@ def cluster_status(runas=None): .. code-block:: bash salt '*' rabbitmq.cluster_status - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'cluster_status'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "cluster_status"], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) - return res['stdout'] + return res["stdout"] -def join_cluster(host, user='rabbit', ram_node=None, runas=None): - ''' +def join_cluster(host, user="rabbit", ram_node=None, runas=None): + """ Join a rabbit cluster CLI Example: @@ -741,27 +778,25 @@ def join_cluster(host, user='rabbit', ram_node=None, runas=None): .. code-block:: bash salt '*' rabbitmq.join_cluster rabbit.example.com rabbit - ''' - cmd = [RABBITMQCTL, 'join_cluster'] + """ + cmd = [RABBITMQCTL, "join_cluster"] if ram_node: - cmd.append('--ram') - cmd.append('{0}@{1}'.format(user, host)) + cmd.append("--ram") + cmd.append("{0}@{1}".format(user, host)) if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() stop_app(runas) - res = __salt__['cmd.run_all']( - cmd, - reset_system_locale=False, - runas=runas, - python_shell=False) + res = __salt__["cmd.run_all"]( + cmd, reset_system_locale=False, runas=runas, python_shell=False + ) start_app(runas) - return _format_response(res, 'Join') + return _format_response(res, "Join") def stop_app(runas=None): - ''' + """ Stops the RabbitMQ application, leaving the Erlang node running. CLI Example: @@ -769,20 +804,21 @@ def stop_app(runas=None): .. code-block:: bash salt '*' rabbitmq.stop_app - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'stop_app'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "stop_app"], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) - return res['stdout'] + return res["stdout"] def start_app(runas=None): - ''' + """ Start the RabbitMQ application. CLI Example: @@ -790,20 +826,21 @@ def start_app(runas=None): .. code-block:: bash salt '*' rabbitmq.start_app - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'start_app'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "start_app"], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) - return res['stdout'] + return res["stdout"] def reset(runas=None): - ''' + """ Return a RabbitMQ node to its virgin state CLI Example: @@ -811,20 +848,21 @@ def reset(runas=None): .. code-block:: bash salt '*' rabbitmq.reset - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'reset'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "reset"], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) - return res['stdout'] + return res["stdout"] def force_reset(runas=None): - ''' + """ Forcefully Return a RabbitMQ node to its virgin state CLI Example: @@ -832,20 +870,21 @@ def force_reset(runas=None): .. code-block:: bash salt '*' rabbitmq.force_reset - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'force_reset'], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "force_reset"], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) - return res['stdout'] + return res["stdout"] def list_queues(runas=None, *args): - ''' + """ Returns queue details of the / virtual host CLI Example: @@ -853,22 +892,20 @@ def list_queues(runas=None, *args): .. code-block:: bash salt '*' rabbitmq.list_queues messages consumers - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - cmd = [RABBITMQCTL, 'list_queues', '-q'] + cmd = [RABBITMQCTL, "list_queues", "-q"] cmd.extend(args) - res = __salt__['cmd.run_all']( - cmd, - reset_system_locale=False, - runas=runas, - python_shell=False) + res = __salt__["cmd.run_all"]( + cmd, reset_system_locale=False, runas=runas, python_shell=False + ) _check_response(res) - return _output_to_dict(res['stdout']) + return _output_to_dict(res["stdout"]) def list_queues_vhost(vhost, runas=None, *args): - ''' + """ Returns queue details of specified virtual host. This command will consider first parameter as the vhost name and rest will be treated as queueinfoitem. For getting details on vhost ``/``, use :mod:`list_queues @@ -879,22 +916,20 @@ def list_queues_vhost(vhost, runas=None, *args): .. code-block:: bash salt '*' rabbitmq.list_queues messages consumers - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - cmd = [RABBITMQCTL, 'list_queues', '-q', '-p', vhost] + cmd = [RABBITMQCTL, "list_queues", "-q", "-p", vhost] cmd.extend(args) - res = __salt__['cmd.run_all']( - cmd, - reset_system_locale=False, - runas=runas, - python_shell=False) + res = __salt__["cmd.run_all"]( + cmd, reset_system_locale=False, runas=runas, python_shell=False + ) _check_response(res) - return _output_to_dict(res['stdout']) + return _output_to_dict(res["stdout"]) def list_policies(vhost="/", runas=None): - ''' + """ Return a dictionary of policies nested by vhost and name based on the data returned from rabbitmqctl list_policies. @@ -905,25 +940,26 @@ def list_policies(vhost="/", runas=None): .. code-block:: bash salt '*' rabbitmq.list_policies - ''' + """ ret = {} if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'list_policies', '-q', '-p', vhost], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "list_policies", "-q", "-p", vhost], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) - output = res['stdout'] + output = res["stdout"] - if __grains__['os_family'] != 'FreeBSD': - version = __salt__['pkg.version']('rabbitmq-server').split('-')[0] + if __grains__["os_family"] != "FreeBSD": + version = __salt__["pkg.version"]("rabbitmq-server").split("-")[0] else: - version = __salt__['pkg.version']('rabbitmq').split('-')[0] + version = __salt__["pkg.version"]("rabbitmq").split("-")[0] for line in _output_lines_to_list(output): - parts = line.split('\t') + parts = line.split("\t") if len(parts) not in (5, 6): continue @@ -936,10 +972,10 @@ def list_policies(vhost="/", runas=None): if _LooseVersion(version) >= _LooseVersion("3.7"): # in version 3.7 the position of apply_to and pattern has been # switched - ret[vhost][name]['pattern'] = parts[2] - ret[vhost][name]['apply_to'] = parts[3] - ret[vhost][name]['definition'] = parts[4] - ret[vhost][name]['priority'] = parts[5] + ret[vhost][name]["pattern"] = parts[2] + ret[vhost][name]["apply_to"] = parts[3] + ret[vhost][name]["definition"] = parts[4] + ret[vhost][name]["priority"] = parts[5] else: # How many fields are there? - 'apply_to' was inserted in position # 2 at some point @@ -947,24 +983,22 @@ def list_policies(vhost="/", runas=None): # switched offset = len(parts) - 5 if len(parts) == 6: - ret[vhost][name]['apply_to'] = parts[2] - ret[vhost][name].update({ - 'pattern': parts[offset+2], - 'definition': parts[offset+3], - 'priority': parts[offset+4] - }) + ret[vhost][name]["apply_to"] = parts[2] + ret[vhost][name].update( + { + "pattern": parts[offset + 2], + "definition": parts[offset + 3], + "priority": parts[offset + 4], + } + ) return ret -def set_policy(vhost, - name, - pattern, - definition, - priority=None, - runas=None, - apply_to=None): - ''' +def set_policy( + vhost, name, pattern, definition, priority=None, runas=None, apply_to=None +): + """ Set a policy based on rabbitmqctl set_policy. Reference: http://www.rabbitmq.com/ha.html @@ -974,32 +1008,30 @@ def set_policy(vhost, .. code-block:: bash salt '*' rabbitmq.set_policy / HA '.*' '{"ha-mode":"all"}' - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() if isinstance(definition, dict): definition = salt.utils.json.dumps(definition) if not isinstance(definition, six.string_types): raise SaltInvocationError( - 'The \'definition\' argument must be a dictionary or JSON string' + "The 'definition' argument must be a dictionary or JSON string" ) - cmd = [RABBITMQCTL, 'set_policy', '-p', vhost] + cmd = [RABBITMQCTL, "set_policy", "-p", vhost] if priority: - cmd.extend(['--priority', priority]) + cmd.extend(["--priority", priority]) if apply_to: - cmd.extend(['--apply-to', apply_to]) + cmd.extend(["--apply-to", apply_to]) cmd.extend([name, pattern, definition]) - res = __salt__['cmd.run_all']( - cmd, - reset_system_locale=False, - runas=runas, - python_shell=False) - log.debug('Set policy: %s', res['stdout']) - return _format_response(res, 'Set') + res = __salt__["cmd.run_all"]( + cmd, reset_system_locale=False, runas=runas, python_shell=False + ) + log.debug("Set policy: %s", res["stdout"]) + return _format_response(res, "Set") def delete_policy(vhost, name, runas=None): - ''' + """ Delete a policy based on rabbitmqctl clear_policy. Reference: http://www.rabbitmq.com/ha.html @@ -1009,20 +1041,21 @@ def delete_policy(vhost, name, runas=None): .. code-block:: bash salt '*' rabbitmq.delete_policy / HA - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'clear_policy', '-p', vhost, name], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "clear_policy", "-p", vhost, name], reset_system_locale=False, runas=runas, - python_shell=False) - log.debug('Delete policy: %s', res['stdout']) - return _format_response(res, 'Deleted') + python_shell=False, + ) + log.debug("Delete policy: %s", res["stdout"]) + return _format_response(res, "Deleted") def policy_exists(vhost, name, runas=None): - ''' + """ Return whether the policy exists based on rabbitmqctl list_policies. Reference: http://www.rabbitmq.com/ha.html @@ -1032,7 +1065,7 @@ def policy_exists(vhost, name, runas=None): .. code-block:: bash salt '*' rabbitmq.policy_exists / HA - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() policies = list_policies(runas=runas) @@ -1040,7 +1073,7 @@ def policy_exists(vhost, name, runas=None): def list_available_plugins(runas=None): - ''' + """ Returns a list of the names of all available plugins (enabled and disabled). CLI Example: @@ -1048,21 +1081,19 @@ def list_available_plugins(runas=None): .. code-block:: bash salt '*' rabbitmq.list_available_plugins - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - cmd = [_get_rabbitmq_plugin(), 'list', '-m'] - ret = __salt__['cmd.run_all']( - cmd, - reset_system_locale=False, - runas=runas, - python_shell=False) + cmd = [_get_rabbitmq_plugin(), "list", "-m"] + ret = __salt__["cmd.run_all"]( + cmd, reset_system_locale=False, runas=runas, python_shell=False + ) _check_response(ret) - return _output_to_list(ret['stdout']) + return _output_to_list(ret["stdout"]) def list_enabled_plugins(runas=None): - ''' + """ Returns a list of the names of the enabled plugins. CLI Example: @@ -1070,21 +1101,19 @@ def list_enabled_plugins(runas=None): .. code-block:: bash salt '*' rabbitmq.list_enabled_plugins - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - cmd = [_get_rabbitmq_plugin(), 'list', '-m', '-e'] - ret = __salt__['cmd.run_all']( - cmd, - reset_system_locale=False, - runas=runas, - python_shell=False) + cmd = [_get_rabbitmq_plugin(), "list", "-m", "-e"] + ret = __salt__["cmd.run_all"]( + cmd, reset_system_locale=False, runas=runas, python_shell=False + ) _check_response(ret) - return _output_to_list(ret['stdout']) + return _output_to_list(ret["stdout"]) def plugin_is_enabled(name, runas=None): - ''' + """ Return whether the plugin is enabled. CLI Example: @@ -1092,14 +1121,14 @@ def plugin_is_enabled(name, runas=None): .. code-block:: bash salt '*' rabbitmq.plugin_is_enabled rabbitmq_plugin_name - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() return name in list_enabled_plugins(runas) def enable_plugin(name, runas=None): - ''' + """ Enable a RabbitMQ plugin via the rabbitmq-plugins command. CLI Example: @@ -1107,20 +1136,18 @@ def enable_plugin(name, runas=None): .. code-block:: bash salt '*' rabbitmq.enable_plugin foo - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - cmd = [_get_rabbitmq_plugin(), 'enable', name] - ret = __salt__['cmd.run_all']( - cmd, - reset_system_locale=False, - runas=runas, - python_shell=False) - return _format_response(ret, 'Enabled') + cmd = [_get_rabbitmq_plugin(), "enable", name] + ret = __salt__["cmd.run_all"]( + cmd, reset_system_locale=False, runas=runas, python_shell=False + ) + return _format_response(ret, "Enabled") def disable_plugin(name, runas=None): - ''' + """ Disable a RabbitMQ plugin via the rabbitmq-plugins command. CLI Example: @@ -1128,33 +1155,32 @@ def disable_plugin(name, runas=None): .. code-block:: bash salt '*' rabbitmq.disable_plugin foo - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - cmd = [_get_rabbitmq_plugin(), 'disable', name] - ret = __salt__['cmd.run_all']( - cmd, - reset_system_locale=False, - runas=runas, - python_shell=False) - return _format_response(ret, 'Disabled') + cmd = [_get_rabbitmq_plugin(), "disable", name] + ret = __salt__["cmd.run_all"]( + cmd, reset_system_locale=False, runas=runas, python_shell=False + ) + return _format_response(ret, "Disabled") def set_upstream( - name, - uri, - prefetch_count=None, - reconnect_delay=None, - ack_mode=None, - trust_user_id=None, - exchange=None, - max_hops=None, - expires=None, - message_ttl=None, - ha_policy=None, - queue=None, - runas=None): - ''' + name, + uri, + prefetch_count=None, + reconnect_delay=None, + ack_mode=None, + trust_user_id=None, + exchange=None, + max_hops=None, + expires=None, + message_ttl=None, + ha_policy=None, + queue=None, + runas=None, +): + """ Configures an upstream via rabbitmqctl set_parameter. This can be an exchange-upstream, a queue-upstream or both. @@ -1224,33 +1250,42 @@ def set_upstream( trust_user_id=True uri=amqp://hostname .. versionadded:: 3000 - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - params = salt.utils.data.filter_falsey({ - 'uri': uri, - 'prefetch-count': prefetch_count, - 'reconnect-delay': reconnect_delay, - 'ack-mode': ack_mode, - 'trust-user-id': trust_user_id, - 'exchange': exchange, - 'max-hops': max_hops, - 'expires': expires, - 'message-ttl': message_ttl, - 'ha-policy': ha_policy, - 'queue': queue, - }) - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'set_parameter', 'federation-upstream', name, salt.utils.json.dumps(params)], + params = salt.utils.data.filter_falsey( + { + "uri": uri, + "prefetch-count": prefetch_count, + "reconnect-delay": reconnect_delay, + "ack-mode": ack_mode, + "trust-user-id": trust_user_id, + "exchange": exchange, + "max-hops": max_hops, + "expires": expires, + "message-ttl": message_ttl, + "ha-policy": ha_policy, + "queue": queue, + } + ) + res = __salt__["cmd.run_all"]( + [ + RABBITMQCTL, + "set_parameter", + "federation-upstream", + name, + salt.utils.json.dumps(params), + ], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) return True def delete_upstream(name, runas=None): - ''' + """ Deletes an upstream via rabbitmqctl clear_parameter. :param str name: The name of the upstream to delete. @@ -1263,13 +1298,14 @@ def delete_upstream(name, runas=None): salt '*' rabbitmq.delete_upstream upstream_name .. versionadded:: 3000 - ''' + """ if runas is None and not salt.utils.platform.is_windows(): runas = salt.utils.user.get_user() - res = __salt__['cmd.run_all']( - [RABBITMQCTL, 'clear_parameter', 'federation-upstream', name], + res = __salt__["cmd.run_all"]( + [RABBITMQCTL, "clear_parameter", "federation-upstream", name], reset_system_locale=False, runas=runas, - python_shell=False) + python_shell=False, + ) _check_response(res) return True diff --git a/salt/modules/rallydev.py b/salt/modules/rallydev.py index 2bdde56fd35..c9108c9d199 100644 --- a/salt/modules/rallydev.py +++ b/salt/modules/rallydev.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for RallyDev .. versionadded:: 2015.8.0 @@ -11,42 +11,50 @@ Requires a ``username`` and a ``password`` in ``/etc/salt/minion``: rallydev: username: myuser@example.com password: 123pass -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.http +import salt.utils.json + # Import salt libs from salt.exceptions import SaltInvocationError -import salt.utils.http -import salt.utils.json log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if apache is installed - ''' - if not __opts__.get('rallydev', {}).get('username', None): - return (False, 'The rallydev execution module failed to load: rallydev:username not defined in config.') - if not __opts__.get('rallydev', {}).get('password', None): - return (False, 'The rallydev execution module failed to load: rallydev:password not defined in config.') + """ + if not __opts__.get("rallydev", {}).get("username", None): + return ( + False, + "The rallydev execution module failed to load: rallydev:username not defined in config.", + ) + if not __opts__.get("rallydev", {}).get("password", None): + return ( + False, + "The rallydev execution module failed to load: rallydev:password not defined in config.", + ) return True def _get_token(): - ''' + """ Get an auth token - ''' - username = __opts__.get('rallydev', {}).get('username', None) - password = __opts__.get('rallydev', {}).get('password', None) - path = 'https://rally1.rallydev.com/slm/webservice/v2.0/security/authorize' + """ + username = __opts__.get("rallydev", {}).get("username", None) + password = __opts__.get("rallydev", {}).get("password", None) + path = "https://rally1.rallydev.com/slm/webservice/v2.0/security/authorize" result = salt.utils.http.query( path, decode=True, - decode_type='json', + decode_type="json", text=True, status=True, username=username, @@ -55,47 +63,44 @@ def _get_token(): persist_session=True, opts=__opts__, ) - if 'dict' not in result: + if "dict" not in result: return None - return result['dict']['OperationResult']['SecurityToken'] + return result["dict"]["OperationResult"]["SecurityToken"] -def _query(action=None, - command=None, - args=None, - method='GET', - header_dict=None, - data=None): - ''' +def _query( + action=None, command=None, args=None, method="GET", header_dict=None, data=None +): + """ Make a web call to RallyDev. - ''' + """ token = _get_token() - username = __opts__.get('rallydev', {}).get('username', None) - password = __opts__.get('rallydev', {}).get('password', None) - path = 'https://rally1.rallydev.com/slm/webservice/v2.0/' + username = __opts__.get("rallydev", {}).get("username", None) + password = __opts__.get("rallydev", {}).get("password", None) + path = "https://rally1.rallydev.com/slm/webservice/v2.0/" if action: path += action if command: - path += '/{0}'.format(command) + path += "/{0}".format(command) - log.debug('RallyDev URL: %s', path) + log.debug("RallyDev URL: %s", path) if not isinstance(args, dict): args = {} - args['key'] = token + args["key"] = token if header_dict is None: - header_dict = {'Content-type': 'application/json'} + header_dict = {"Content-type": "application/json"} - if method != 'POST': - header_dict['Accept'] = 'application/json' + if method != "POST": + header_dict["Accept"] = "application/json" decode = True - if method == 'DELETE': + if method == "DELETE": decode = False return_content = None @@ -106,7 +111,7 @@ def _query(action=None, data=data, header_dict=header_dict, decode=decode, - decode_type='json', + decode_type="json", text=True, status=True, username=username, @@ -115,16 +120,16 @@ def _query(action=None, persist_session=True, opts=__opts__, ) - log.debug('RallyDev Response Status Code: %s', result['status']) - if 'error' in result: - log.error(result['error']) - return [result['status'], result['error']] + log.debug("RallyDev Response Status Code: %s", result["status"]) + if "error" in result: + log.error(result["error"]) + return [result["status"], result["error"]] - return [result['status'], result.get('dict', {})] + return [result["status"], result.get("dict", {})] def list_items(name): - ''' + """ List items of a particular type CLI Examples: @@ -134,13 +139,13 @@ def list_items(name): salt myminion rallydev.list_s salt myminion rallydev.list_users salt myminion rallydev.list_artifacts - ''' + """ status, result = _query(action=name) return result -def query_item(name, query_string, order='Rank'): - ''' +def query_item(name, query_string, order="Rank"): + """ Query a type of record for one or more items. Requires a valid query string. See https://rally1.rallydev.com/slm/doc/webservice/introduction.jsp for information on query syntax. @@ -152,17 +157,13 @@ def query_item(name, query_string, order='Rank'): salt myminion rallydev.query_ [] salt myminion rallydev.query_task '(Name contains github)' salt myminion rallydev.query_task '(Name contains reactor)' Rank - ''' - status, result = _query( - action=name, - args={'query': query_string, - 'order': order} - ) + """ + status, result = _query(action=name, args={"query": query_string, "order": order}) return result def show_item(name, id_): - ''' + """ Show an item CLI Example: @@ -170,13 +171,13 @@ def show_item(name, id_): .. code-block:: bash salt myminion rallydev.show_ - ''' + """ status, result = _query(action=name, command=id_) return result def update_item(name, id_, field=None, value=None, postdata=None): - ''' + """ Update an item. Either a field and a value, or a chunk of POST data, may be used, but not both. @@ -186,29 +187,29 @@ def update_item(name, id_, field=None, value=None, postdata=None): salt myminion rallydev.update_ field= value= salt myminion rallydev.update_ postdata= - ''' + """ if field and value: if postdata: - raise SaltInvocationError('Either a field and a value, or a chunk ' - 'of POST data, may be specified, but not both.') + raise SaltInvocationError( + "Either a field and a value, or a chunk " + "of POST data, may be specified, but not both." + ) postdata = {name.title(): {field: value}} if postdata is None: - raise SaltInvocationError('Either a field and a value, or a chunk of ' - 'POST data must be specified.') + raise SaltInvocationError( + "Either a field and a value, or a chunk of " "POST data must be specified." + ) status, result = _query( - action=name, - command=id_, - method='POST', - data=salt.utils.json.dumps(postdata), + action=name, command=id_, method="POST", data=salt.utils.json.dumps(postdata), ) return result def show_artifact(id_): - ''' + """ Show an artifact CLI Example: @@ -216,12 +217,12 @@ def show_artifact(id_): .. code-block:: bash salt myminion rallydev.show_artifact - ''' - return show_item('artifact', id_) + """ + return show_item("artifact", id_) def list_users(): - ''' + """ List the users CLI Example: @@ -229,12 +230,12 @@ def list_users(): .. code-block:: bash salt myminion rallydev.list_users - ''' - return list_items('user') + """ + return list_items("user") def show_user(id_): - ''' + """ Show a user CLI Example: @@ -242,12 +243,12 @@ def show_user(id_): .. code-block:: bash salt myminion rallydev.show_user - ''' - return show_item('user', id_) + """ + return show_item("user", id_) def update_user(id_, field, value): - ''' + """ Update a user CLI Example: @@ -255,12 +256,12 @@ def update_user(id_, field, value): .. code-block:: bash salt myminion rallydev.update_user - ''' - return update_item('user', id_, field, value) + """ + return update_item("user", id_, field, value) -def query_user(query_string, order='UserName'): - ''' +def query_user(query_string, order="UserName"): + """ Update a user CLI Example: @@ -268,5 +269,5 @@ def query_user(query_string, order='UserName'): .. code-block:: bash salt myminion rallydev.query_user '(Name contains Jo)' - ''' - return query_item('user', query_string, order) + """ + return query_item("user", query_string, order) diff --git a/salt/modules/random_org.py b/salt/modules/random_org.py index cf95d560465..1741c17865a 100644 --- a/salt/modules/random_org.py +++ b/salt/modules/random_org.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for retrieving random information from Random.org .. versionadded:: 2015.5.0 @@ -15,92 +15,82 @@ Module for retrieving random information from Random.org random_org: api_key: 7be1402d-5719-5bd3-a306-3def9f135da5 api_version: 1 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import 3rd-party libs -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext import six import salt.ext.six.moves.http_client -from salt.ext.six.moves.urllib.parse import urljoin as _urljoin -# pylint: enable=import-error,no-name-in-module,redefined-builtin # Import salt libs import salt.utils.http import salt.utils.json +# Import 3rd-party libs +# pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext import six +from salt.ext.six.moves.urllib.parse import urljoin as _urljoin + +# pylint: enable=import-error,no-name-in-module,redefined-builtin + + log = logging.getLogger(__name__) -__virtualname__ = 'random_org' +__virtualname__ = "random_org" RANDOM_ORG_FUNCTIONS = { - '1': { - 'getUsage': { - 'method': 'getUsage', - }, - 'generateIntegers': { - 'method': 'generateIntegers', - }, - 'generateStrings': { - 'method': 'generateStrings', - }, - 'generateUUIDs': { - 'method': 'generateUUIDs', - }, - 'generateDecimalFractions': { - 'method': 'generateDecimalFractions', - }, - 'generateGaussians': { - 'method': 'generateGaussians', - }, - 'generateBlobs': { - 'method': 'generateBlobs', - }, + "1": { + "getUsage": {"method": "getUsage"}, + "generateIntegers": {"method": "generateIntegers"}, + "generateStrings": {"method": "generateStrings"}, + "generateUUIDs": {"method": "generateUUIDs"}, + "generateDecimalFractions": {"method": "generateDecimalFractions"}, + "generateGaussians": {"method": "generateGaussians"}, + "generateBlobs": {"method": "generateBlobs"}, } } def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ def _numeric(n): - ''' + """ Tell whether an argument is numeric - ''' - return isinstance(n, six.integer_types + (float, )) + """ + return isinstance(n, six.integer_types + (float,)) def _query(api_version=None, data=None): - ''' + """ Slack object method function to construct and execute on the API URL. :param api_key: The Random.org api key. :param api_version: The version of Random.org api. :param data: The data to be sent for POST method. :return: The json response from the API call or False. - ''' + """ if data is None: data = {} - ret = {'res': True} + ret = {"res": True} - api_url = 'https://api.random.org/' - base_url = _urljoin(api_url, 'json-rpc/' + six.text_type(api_version) + '/invoke') + api_url = "https://api.random.org/" + base_url = _urljoin(api_url, "json-rpc/" + six.text_type(api_version) + "/invoke") data = salt.utils.json.dumps(data) result = salt.utils.http.query( base_url, - method='POST', + method="POST", params={}, data=data, decode=True, @@ -109,22 +99,22 @@ def _query(api_version=None, data=None): opts=__opts__, ) - if result.get('status', None) == salt.ext.six.moves.http_client.OK: - _result = result['dict'] - if _result.get('result'): - return _result.get('result') - if _result.get('error'): - return _result.get('error') + if result.get("status", None) == salt.ext.six.moves.http_client.OK: + _result = result["dict"] + if _result.get("result"): + return _result.get("result") + if _result.get("error"): + return _result.get("error") return False - elif result.get('status', None) == salt.ext.six.moves.http_client.NO_CONTENT: + elif result.get("status", None) == salt.ext.six.moves.http_client.NO_CONTENT: return False else: - ret['message'] = result.text if hasattr(result, 'text') else '' + ret["message"] = result.text if hasattr(result, "text") else "" return ret def getUsage(api_key=None, api_version=None): - ''' + """ Show current usages statistics :param api_key: The Random.org api key. @@ -138,49 +128,47 @@ def getUsage(api_key=None, api_version=None): salt '*' random_org.getUsage salt '*' random_org.getUsage api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_version=1 - ''' - ret = {'res': True} + """ + ret = {"res": True} if not api_key or not api_version: try: - options = __salt__['config.option']('random_org') + options = __salt__["config.option"]("random_org") if not api_key: - api_key = options.get('api_key') + api_key = options.get("api_key") if not api_version: - api_version = options.get('api_version') + api_version = options.get("api_version") except (NameError, KeyError, AttributeError): - log.error('No Random.org api key found.') - ret['message'] = 'No Random.org api key or api version found.' - ret['res'] = False + log.error("No Random.org api key found.") + ret["message"] = "No Random.org api key or api version found." + ret["res"] = False return ret if isinstance(api_version, int): api_version = six.text_type(api_version) - _function = RANDOM_ORG_FUNCTIONS.get(api_version).get('getUsage').get('method') + _function = RANDOM_ORG_FUNCTIONS.get(api_version).get("getUsage").get("method") data = {} - data['id'] = 1911220 - data['jsonrpc'] = '2.0' - data['method'] = _function - data['params'] = {'apiKey': api_key} + data["id"] = 1911220 + data["jsonrpc"] = "2.0" + data["method"] = _function + data["params"] = {"apiKey": api_key} result = _query(api_version=api_version, data=data) if result: - ret['bitsLeft'] = result.get('bitsLeft') - ret['requestsLeft'] = result.get('requestsLeft') - ret['totalBits'] = result.get('totalBits') - ret['totalRequests'] = result.get('totalRequests') + ret["bitsLeft"] = result.get("bitsLeft") + ret["requestsLeft"] = result.get("requestsLeft") + ret["totalBits"] = result.get("totalBits") + ret["totalRequests"] = result.get("totalRequests") else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] return ret -def generateIntegers(api_key=None, - api_version=None, - **kwargs): - ''' +def generateIntegers(api_key=None, api_version=None, **kwargs): + """ Generate random integers :param api_key: The Random.org api key. @@ -212,92 +200,103 @@ def generateIntegers(api_key=None, salt '*' random_org.generateIntegers number=5 minimum=2 maximum=255 base=2 - ''' - ret = {'res': True} + """ + ret = {"res": True} if not api_key or not api_version: try: - options = __salt__['config.option']('random_org') + options = __salt__["config.option"]("random_org") if not api_key: - api_key = options.get('api_key') + api_key = options.get("api_key") if not api_version: - api_version = options.get('api_version') + api_version = options.get("api_version") except (NameError, KeyError, AttributeError): - log.error('No Random.org api key found.') - ret['message'] = 'No Random.org api key or api version found.' - ret['res'] = False + log.error("No Random.org api key found.") + ret["message"] = "No Random.org api key or api version found." + ret["res"] = False return ret - for item in ['number', 'minimum', 'maximum']: + for item in ["number", "minimum", "maximum"]: if item not in kwargs: - ret['res'] = False - ret['message'] = 'Rquired argument, {0} is missing.'.format(item) + ret["res"] = False + ret["message"] = "Rquired argument, {0} is missing.".format(item) return ret - if not _numeric(kwargs['number']) or not 1 <= kwargs['number'] <= 10000: - ret['res'] = False - ret['message'] = 'Number of integers must be between 1 and 10000' + if not _numeric(kwargs["number"]) or not 1 <= kwargs["number"] <= 10000: + ret["res"] = False + ret["message"] = "Number of integers must be between 1 and 10000" return ret - if not _numeric(kwargs['minimum']) or not -1000000000 <= kwargs['minimum'] <= 1000000000: - ret['res'] = False - ret['message'] = 'Minimum argument must be between -1,000,000,000 and 1,000,000,000' + if ( + not _numeric(kwargs["minimum"]) + or not -1000000000 <= kwargs["minimum"] <= 1000000000 + ): + ret["res"] = False + ret[ + "message" + ] = "Minimum argument must be between -1,000,000,000 and 1,000,000,000" return ret - if not _numeric(kwargs['maximum']) or not -1000000000 <= kwargs['maximum'] <= 1000000000: - ret['res'] = False - ret['message'] = 'Maximum argument must be between -1,000,000,000 and 1,000,000,000' + if ( + not _numeric(kwargs["maximum"]) + or not -1000000000 <= kwargs["maximum"] <= 1000000000 + ): + ret["res"] = False + ret[ + "message" + ] = "Maximum argument must be between -1,000,000,000 and 1,000,000,000" return ret - if 'base' in kwargs: - base = kwargs['base'] + if "base" in kwargs: + base = kwargs["base"] if base not in [2, 8, 10, 16]: - ret['res'] = False - ret['message'] = 'Base must be either 2, 8, 10 or 16.' + ret["res"] = False + ret["message"] = "Base must be either 2, 8, 10 or 16." return ret else: base = 10 - if 'replacement' not in kwargs: + if "replacement" not in kwargs: replacement = True else: - replacement = kwargs['replacement'] + replacement = kwargs["replacement"] if isinstance(api_version, int): api_version = six.text_type(api_version) - _function = RANDOM_ORG_FUNCTIONS.get(api_version).get('generateIntegers').get('method') + _function = ( + RANDOM_ORG_FUNCTIONS.get(api_version).get("generateIntegers").get("method") + ) data = {} - data['id'] = 1911220 - data['jsonrpc'] = '2.0' - data['method'] = _function - data['params'] = {'apiKey': api_key, - 'n': kwargs['number'], - 'min': kwargs['minimum'], - 'max': kwargs['maximum'], - 'replacement': replacement, - 'base': base - } + data["id"] = 1911220 + data["jsonrpc"] = "2.0" + data["method"] = _function + data["params"] = { + "apiKey": api_key, + "n": kwargs["number"], + "min": kwargs["minimum"], + "max": kwargs["maximum"], + "replacement": replacement, + "base": base, + } result = _query(api_version=api_version, data=data) - log.debug('result %s', result) + log.debug("result %s", result) if result: - if 'random' in result: - random_data = result.get('random').get('data') - ret['data'] = random_data + if "random" in result: + random_data = result.get("random").get("data") + ret["data"] = random_data else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] return ret -def generateStrings(api_key=None, - api_version=None, - **kwargs): - ''' +def generateStrings(api_key=None, api_version=None, **kwargs): + """ Generate random strings. :param api_key: The Random.org api key. @@ -327,81 +326,82 @@ def generateStrings(api_key=None, salt '*' random_org.generateStrings number=10 length=16 characters'abcdefghijklmnopqrstuvwxyz' - ''' - ret = {'res': True} + """ + ret = {"res": True} if not api_key or not api_version: try: - options = __salt__['config.option']('random_org') + options = __salt__["config.option"]("random_org") if not api_key: - api_key = options.get('api_key') + api_key = options.get("api_key") if not api_version: - api_version = options.get('api_version') + api_version = options.get("api_version") except (NameError, KeyError, AttributeError): - log.error('No Random.org api key found.') - ret['message'] = 'No Random.org api key or api version found.' - ret['res'] = False + log.error("No Random.org api key found.") + ret["message"] = "No Random.org api key or api version found." + ret["res"] = False return ret - for item in ['number', 'length', 'characters']: + for item in ["number", "length", "characters"]: if item not in kwargs: - ret['res'] = False - ret['message'] = 'Required argument, {0} is missing.'.format(item) + ret["res"] = False + ret["message"] = "Required argument, {0} is missing.".format(item) return ret - if not _numeric(kwargs['number']) or not 1 <= kwargs['number'] <= 10000: - ret['res'] = False - ret['message'] = 'Number of strings must be between 1 and 10000' + if not _numeric(kwargs["number"]) or not 1 <= kwargs["number"] <= 10000: + ret["res"] = False + ret["message"] = "Number of strings must be between 1 and 10000" return ret - if not _numeric(kwargs['length']) or not 1 <= kwargs['length'] <= 20: - ret['res'] = False - ret['message'] = 'Length of strings must be between 1 and 20' + if not _numeric(kwargs["length"]) or not 1 <= kwargs["length"] <= 20: + ret["res"] = False + ret["message"] = "Length of strings must be between 1 and 20" return ret - if len(kwargs['characters']) >= 80: - ret['res'] = False - ret['message'] = 'Length of characters must be less than 80.' + if len(kwargs["characters"]) >= 80: + ret["res"] = False + ret["message"] = "Length of characters must be less than 80." return ret if isinstance(api_version, int): api_version = six.text_type(api_version) - if 'replacement' not in kwargs: + if "replacement" not in kwargs: replacement = True else: - replacement = kwargs['replacement'] + replacement = kwargs["replacement"] - _function = RANDOM_ORG_FUNCTIONS.get(api_version).get('generateStrings').get('method') + _function = ( + RANDOM_ORG_FUNCTIONS.get(api_version).get("generateStrings").get("method") + ) data = {} - data['id'] = 1911220 - data['jsonrpc'] = '2.0' - data['method'] = _function - data['params'] = {'apiKey': api_key, - 'n': kwargs['number'], - 'length': kwargs['length'], - 'characters': kwargs['characters'], - 'replacement': replacement, - } + data["id"] = 1911220 + data["jsonrpc"] = "2.0" + data["method"] = _function + data["params"] = { + "apiKey": api_key, + "n": kwargs["number"], + "length": kwargs["length"], + "characters": kwargs["characters"], + "replacement": replacement, + } result = _query(api_version=api_version, data=data) if result: - if 'random' in result: - random_data = result.get('random').get('data') - ret['data'] = random_data + if "random" in result: + random_data = result.get("random").get("data") + ret["data"] = random_data else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] return ret -def generateUUIDs(api_key=None, - api_version=None, - **kwargs): - ''' +def generateUUIDs(api_key=None, api_version=None, **kwargs): + """ Generate a list of random UUIDs :param api_key: The Random.org api key. @@ -416,63 +416,62 @@ def generateUUIDs(api_key=None, salt '*' random_org.generateUUIDs number=5 - ''' - ret = {'res': True} + """ + ret = {"res": True} if not api_key or not api_version: try: - options = __salt__['config.option']('random_org') + options = __salt__["config.option"]("random_org") if not api_key: - api_key = options.get('api_key') + api_key = options.get("api_key") if not api_version: - api_version = options.get('api_version') + api_version = options.get("api_version") except (NameError, KeyError, AttributeError): - log.error('No Random.org api key found.') - ret['message'] = 'No Random.org api key or api version found.' - ret['res'] = False + log.error("No Random.org api key found.") + ret["message"] = "No Random.org api key or api version found." + ret["res"] = False return ret - for item in ['number']: + for item in ["number"]: if item not in kwargs: - ret['res'] = False - ret['message'] = 'Required argument, {0} is missing.'.format(item) + ret["res"] = False + ret["message"] = "Required argument, {0} is missing.".format(item) return ret if isinstance(api_version, int): api_version = six.text_type(api_version) - if not _numeric(kwargs['number']) or not 1 <= kwargs['number'] <= 1000: - ret['res'] = False - ret['message'] = 'Number of UUIDs must be between 1 and 1000' + if not _numeric(kwargs["number"]) or not 1 <= kwargs["number"] <= 1000: + ret["res"] = False + ret["message"] = "Number of UUIDs must be between 1 and 1000" return ret - _function = RANDOM_ORG_FUNCTIONS.get(api_version).get('generateUUIDs').get('method') + _function = RANDOM_ORG_FUNCTIONS.get(api_version).get("generateUUIDs").get("method") data = {} - data['id'] = 1911220 - data['jsonrpc'] = '2.0' - data['method'] = _function - data['params'] = {'apiKey': api_key, - 'n': kwargs['number'], - } + data["id"] = 1911220 + data["jsonrpc"] = "2.0" + data["method"] = _function + data["params"] = { + "apiKey": api_key, + "n": kwargs["number"], + } result = _query(api_version=api_version, data=data) if result: - if 'random' in result: - random_data = result.get('random').get('data') - ret['data'] = random_data + if "random" in result: + random_data = result.get("random").get("data") + ret["data"] = random_data else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] return ret -def generateDecimalFractions(api_key=None, - api_version=None, - **kwargs): - ''' +def generateDecimalFractions(api_key=None, api_version=None, **kwargs): + """ Generates true random decimal fractions :param api_key: The Random.org api key. @@ -498,75 +497,78 @@ def generateDecimalFractions(api_key=None, salt '*' random_org.generateDecimalFractions number=10 decimalPlaces=4 replacement=True - ''' - ret = {'res': True} + """ + ret = {"res": True} if not api_key or not api_version: try: - options = __salt__['config.option']('random_org') + options = __salt__["config.option"]("random_org") if not api_key: - api_key = options.get('api_key') + api_key = options.get("api_key") if not api_version: - api_version = options.get('api_version') + api_version = options.get("api_version") except (NameError, KeyError, AttributeError): - log.error('No Random.org api key found.') - ret['message'] = 'No Random.org api key or api version found.' - ret['res'] = False + log.error("No Random.org api key found.") + ret["message"] = "No Random.org api key or api version found." + ret["res"] = False return ret - for item in ['number', 'decimalPlaces']: + for item in ["number", "decimalPlaces"]: if item not in kwargs: - ret['res'] = False - ret['message'] = 'Required argument, {0} is missing.'.format(item) + ret["res"] = False + ret["message"] = "Required argument, {0} is missing.".format(item) return ret - if not isinstance(kwargs['number'], int) or not 1 <= kwargs['number'] <= 10000: - ret['res'] = False - ret['message'] = 'Number of decimal fractions must be between 1 and 10000' + if not isinstance(kwargs["number"], int) or not 1 <= kwargs["number"] <= 10000: + ret["res"] = False + ret["message"] = "Number of decimal fractions must be between 1 and 10000" return ret - if not _numeric(kwargs['decimalPlaces']) or not 1 <= kwargs['decimalPlaces'] <= 20: - ret['res'] = False - ret['message'] = 'Number of decimal places must be between 1 and 20' + if not _numeric(kwargs["decimalPlaces"]) or not 1 <= kwargs["decimalPlaces"] <= 20: + ret["res"] = False + ret["message"] = "Number of decimal places must be between 1 and 20" return ret - if 'replacement' not in kwargs: + if "replacement" not in kwargs: replacement = True else: - replacement = kwargs['replacement'] + replacement = kwargs["replacement"] if isinstance(api_version, int): api_version = six.text_type(api_version) - _function = RANDOM_ORG_FUNCTIONS.get(api_version).get('generateDecimalFractions').get('method') + _function = ( + RANDOM_ORG_FUNCTIONS.get(api_version) + .get("generateDecimalFractions") + .get("method") + ) data = {} - data['id'] = 1911220 - data['jsonrpc'] = '2.0' - data['method'] = _function - data['params'] = {'apiKey': api_key, - 'n': kwargs['number'], - 'decimalPlaces': kwargs['decimalPlaces'], - 'replacement': replacement, - } + data["id"] = 1911220 + data["jsonrpc"] = "2.0" + data["method"] = _function + data["params"] = { + "apiKey": api_key, + "n": kwargs["number"], + "decimalPlaces": kwargs["decimalPlaces"], + "replacement": replacement, + } result = _query(api_version=api_version, data=data) if result: - if 'random' in result: - random_data = result.get('random').get('data') - ret['data'] = random_data + if "random" in result: + random_data = result.get("random").get("data") + ret["data"] = random_data else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] return ret -def generateGaussians(api_key=None, - api_version=None, - **kwargs): - ''' +def generateGaussians(api_key=None, api_version=None, **kwargs): + """ This method generates true random numbers from a Gaussian distribution (also known as a normal distribution). @@ -589,81 +591,90 @@ def generateGaussians(api_key=None, salt '*' random_org.generateGaussians number=10 mean=0.0 standardDeviation=1.0 significantDigits=8 - ''' - ret = {'res': True} + """ + ret = {"res": True} if not api_key or not api_version: try: - options = __salt__['config.option']('random_org') + options = __salt__["config.option"]("random_org") if not api_key: - api_key = options.get('api_key') + api_key = options.get("api_key") if not api_version: - api_version = options.get('api_version') + api_version = options.get("api_version") except (NameError, KeyError, AttributeError): - log.error('No Random.org api key found.') - ret['message'] = 'No Random.org api key or api version found.' - ret['res'] = False + log.error("No Random.org api key found.") + ret["message"] = "No Random.org api key or api version found." + ret["res"] = False return ret - for item in ['number', 'mean', 'standardDeviation', 'significantDigits']: + for item in ["number", "mean", "standardDeviation", "significantDigits"]: if item not in kwargs: - ret['res'] = False - ret['message'] = 'Required argument, {0} is missing.'.format(item) + ret["res"] = False + ret["message"] = "Required argument, {0} is missing.".format(item) return ret - if not _numeric(kwargs['number']) or not 1 <= kwargs['number'] <= 10000: - ret['res'] = False - ret['message'] = 'Number of decimal fractions must be between 1 and 10000' + if not _numeric(kwargs["number"]) or not 1 <= kwargs["number"] <= 10000: + ret["res"] = False + ret["message"] = "Number of decimal fractions must be between 1 and 10000" return ret - if not _numeric(kwargs['mean']) or not -1000000 <= kwargs['mean'] <= 1000000: - ret['res'] = False - ret['message'] = "The distribution's mean must be between -1000000 and 1000000" + if not _numeric(kwargs["mean"]) or not -1000000 <= kwargs["mean"] <= 1000000: + ret["res"] = False + ret["message"] = "The distribution's mean must be between -1000000 and 1000000" return ret - if not _numeric(kwargs['standardDeviation']) or not -1000000 <= kwargs['standardDeviation'] <= 1000000: - ret['res'] = False - ret['message'] = "The distribution's standard deviation must be between -1000000 and 1000000" + if ( + not _numeric(kwargs["standardDeviation"]) + or not -1000000 <= kwargs["standardDeviation"] <= 1000000 + ): + ret["res"] = False + ret[ + "message" + ] = "The distribution's standard deviation must be between -1000000 and 1000000" return ret - if not _numeric(kwargs['significantDigits']) or not 2 <= kwargs['significantDigits'] <= 20: - ret['res'] = False - ret['message'] = 'The number of significant digits must be between 2 and 20' + if ( + not _numeric(kwargs["significantDigits"]) + or not 2 <= kwargs["significantDigits"] <= 20 + ): + ret["res"] = False + ret["message"] = "The number of significant digits must be between 2 and 20" return ret if isinstance(api_version, int): api_version = six.text_type(api_version) - _function = RANDOM_ORG_FUNCTIONS.get(api_version).get('generateGaussians').get('method') + _function = ( + RANDOM_ORG_FUNCTIONS.get(api_version).get("generateGaussians").get("method") + ) data = {} - data['id'] = 1911220 - data['jsonrpc'] = '2.0' - data['method'] = _function - data['params'] = {'apiKey': api_key, - 'n': kwargs['number'], - 'mean': kwargs['mean'], - 'standardDeviation': kwargs['standardDeviation'], - 'significantDigits': kwargs['significantDigits'], - } + data["id"] = 1911220 + data["jsonrpc"] = "2.0" + data["method"] = _function + data["params"] = { + "apiKey": api_key, + "n": kwargs["number"], + "mean": kwargs["mean"], + "standardDeviation": kwargs["standardDeviation"], + "significantDigits": kwargs["significantDigits"], + } result = _query(api_version=api_version, data=data) if result: - if 'random' in result: - random_data = result.get('random').get('data') - ret['data'] = random_data + if "random" in result: + random_data = result.get("random").get("data") + ret["data"] = random_data else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] return ret -def generateBlobs(api_key=None, - api_version=None, - **kwargs): - ''' +def generateBlobs(api_key=None, api_version=None, **kwargs): + """ List all Slack users. :param api_key: The Random.org api key. @@ -680,71 +691,76 @@ def generateBlobs(api_key=None, salt '*' get_integers number=5 min=1 max=6 salt '*' get_integers number=5 min=1 max=6 - ''' - ret = {'res': True} + """ + ret = {"res": True} if not api_key or not api_version: try: - options = __salt__['config.option']('random_org') + options = __salt__["config.option"]("random_org") if not api_key: - api_key = options.get('api_key') + api_key = options.get("api_key") if not api_version: - api_version = options.get('api_version') + api_version = options.get("api_version") except (NameError, KeyError, AttributeError): - log.error('No Random.org api key found.') - ret['message'] = 'No Random.org api key or api version found.' - ret['res'] = False + log.error("No Random.org api key found.") + ret["message"] = "No Random.org api key or api version found." + ret["res"] = False return ret - for item in ['number', 'size']: + for item in ["number", "size"]: if item not in kwargs: - ret['res'] = False - ret['message'] = 'Required argument, {0} is missing.'.format(item) + ret["res"] = False + ret["message"] = "Required argument, {0} is missing.".format(item) return ret - if not _numeric(kwargs['number']) or not 1 <= kwargs['number'] <= 100: - ret['res'] = False - ret['message'] = 'Number of blobs must be between 1 and 100' + if not _numeric(kwargs["number"]) or not 1 <= kwargs["number"] <= 100: + ret["res"] = False + ret["message"] = "Number of blobs must be between 1 and 100" return ret # size should be between range and divisible by 8 - if not _numeric(kwargs['size']) or not 1 <= kwargs['size'] <= 1048576 or kwargs['size'] % 8 != 0: - ret['res'] = False - ret['message'] = 'Number of blobs must be between 1 and 100' + if ( + not _numeric(kwargs["size"]) + or not 1 <= kwargs["size"] <= 1048576 + or kwargs["size"] % 8 != 0 + ): + ret["res"] = False + ret["message"] = "Number of blobs must be between 1 and 100" return ret - if 'format' in kwargs: - _format = kwargs['format'] - if _format not in ['base64', 'hex']: - ret['res'] = False - ret['message'] = 'Format must be either base64 or hex.' + if "format" in kwargs: + _format = kwargs["format"] + if _format not in ["base64", "hex"]: + ret["res"] = False + ret["message"] = "Format must be either base64 or hex." return ret else: - _format = 'base64' + _format = "base64" if isinstance(api_version, int): api_version = six.text_type(api_version) - _function = RANDOM_ORG_FUNCTIONS.get(api_version).get('generateBlobs').get('method') + _function = RANDOM_ORG_FUNCTIONS.get(api_version).get("generateBlobs").get("method") data = {} - data['id'] = 1911220 - data['jsonrpc'] = '2.0' - data['method'] = _function - data['params'] = {'apiKey': api_key, - 'n': kwargs['number'], - 'size': kwargs['size'], - 'format': _format, - } + data["id"] = 1911220 + data["jsonrpc"] = "2.0" + data["method"] = _function + data["params"] = { + "apiKey": api_key, + "n": kwargs["number"], + "size": kwargs["size"], + "format": _format, + } result = _query(api_version=api_version, data=data) if result: - if 'random' in result: - random_data = result.get('random').get('data') - ret['data'] = random_data + if "random" in result: + random_data = result.get("random").get("data") + ret["data"] = random_data else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] else: - ret['res'] = False - ret['message'] = result['message'] + ret["res"] = False + ret["message"] = result["message"] return ret diff --git a/salt/modules/rbac_solaris.py b/salt/modules/rbac_solaris.py index 289954d0208..aa46c8e02bb 100644 --- a/salt/modules/rbac_solaris.py +++ b/salt/modules/rbac_solaris.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Module for Solaris' Role-Based Access Control -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -14,25 +14,25 @@ import salt.utils.path log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'rbac' +__virtualname__ = "rbac" def __virtual__(): - ''' + """ Provides rbac if we are running on a solaris like system - ''' - if __grains__['kernel'] == 'SunOS' and salt.utils.path.which('profiles'): + """ + if __grains__["kernel"] == "SunOS" and salt.utils.path.which("profiles"): return __virtualname__ return ( False, - '{0} module can only be loaded on a solaris like system'.format( + "{0} module can only be loaded on a solaris like system".format( __virtualname__ - ) + ), ) def profile_list(default_only=False): - ''' + """ List all available profiles default_only : boolean @@ -43,23 +43,23 @@ def profile_list(default_only=False): .. code-block:: bash salt '*' rbac.profile_list - ''' + """ profiles = {} - default_profiles = ['All'] + default_profiles = ["All"] ## lookup default profile(s) - with salt.utils.files.fopen('/etc/security/policy.conf', 'r') as policy_conf: + with salt.utils.files.fopen("/etc/security/policy.conf", "r") as policy_conf: for policy in policy_conf: policy = salt.utils.stringutils.to_unicode(policy) - policy = policy.split('=') - if policy[0].strip() == 'PROFS_GRANTED': - default_profiles.extend(policy[1].strip().split(',')) + policy = policy.split("=") + if policy[0].strip() == "PROFS_GRANTED": + default_profiles.extend(policy[1].strip().split(",")) ## read prof_attr file (profname:res1:res2:desc:attr) - with salt.utils.files.fopen('/etc/security/prof_attr', 'r') as prof_attr: + with salt.utils.files.fopen("/etc/security/prof_attr", "r") as prof_attr: for profile in prof_attr: profile = salt.utils.stringutils.to_unicode(profile) - profile = profile.split(':') + profile = profile.split(":") # skip comments and non complaint lines if len(profile) != 5: @@ -77,7 +77,7 @@ def profile_list(default_only=False): def profile_get(user, default_hidden=True): - ''' + """ List profiles for user user : string @@ -91,14 +91,14 @@ def profile_get(user, default_hidden=True): salt '*' rbac.profile_get leo salt '*' rbac.profile_get leo default_hidden=False - ''' + """ user_profiles = [] ## read user_attr file (user:qualifier:res1:res2:attr) - with salt.utils.files.fopen('/etc/user_attr', 'r') as user_attr: + with salt.utils.files.fopen("/etc/user_attr", "r") as user_attr: for profile in user_attr: profile = salt.utils.stringutils.to_unicode(profile) - profile = profile.strip().split(':') + profile = profile.strip().split(":") # skip comments and non complaint lines if len(profile) != 5: @@ -110,14 +110,14 @@ def profile_get(user, default_hidden=True): # parse attr attrs = {} - for attr in profile[4].strip().split(';'): - attr_key, attr_val = attr.strip().split('=') - if attr_key in ['auths', 'profiles', 'roles']: - attrs[attr_key] = attr_val.strip().split(',') + for attr in profile[4].strip().split(";"): + attr_key, attr_val = attr.strip().split("=") + if attr_key in ["auths", "profiles", "roles"]: + attrs[attr_key] = attr_val.strip().split(",") else: attrs[attr_key] = attr_val - if 'profiles' in attrs: - user_profiles.extend(attrs['profiles']) + if "profiles" in attrs: + user_profiles.extend(attrs["profiles"]) ## remove default profiles if default_hidden: @@ -129,7 +129,7 @@ def profile_get(user, default_hidden=True): def profile_add(user, profile): - ''' + """ Add profile to user user : string @@ -143,15 +143,15 @@ def profile_add(user, profile): salt '*' rbac.profile_add martine 'Primary Administrator' salt '*' rbac.profile_add martine 'User Management,User Security' - ''' + """ ret = {} ## validate profiles - profiles = profile.split(',') + profiles = profile.split(",") known_profiles = profile_list().keys() valid_profiles = [p for p in profiles if p in known_profiles] log.debug( - 'rbac.profile_add - profiles=%s, known_profiles=%s, valid_profiles=%s', + "rbac.profile_add - profiles=%s, known_profiles=%s, valid_profiles=%s", profiles, known_profiles, valid_profiles, @@ -159,14 +159,15 @@ def profile_add(user, profile): ## update user profiles if len(valid_profiles) > 0: - res = __salt__['cmd.run_all']('usermod -P "{profiles}" {login}'.format( - login=user, - profiles=','.join(set(profile_get(user) + valid_profiles)), - )) - if res['retcode'] > 0: - ret['Error'] = { - 'retcode': res['retcode'], - 'message': res['stderr'] if 'stderr' in res else res['stdout'] + res = __salt__["cmd.run_all"]( + 'usermod -P "{profiles}" {login}'.format( + login=user, profiles=",".join(set(profile_get(user) + valid_profiles)), + ) + ) + if res["retcode"] > 0: + ret["Error"] = { + "retcode": res["retcode"], + "message": res["stderr"] if "stderr" in res else res["stdout"], } return ret @@ -174,17 +175,17 @@ def profile_add(user, profile): active_profiles = profile_get(user, False) for p in profiles: if p not in valid_profiles: - ret[p] = 'Unknown' + ret[p] = "Unknown" elif p in active_profiles: - ret[p] = 'Added' + ret[p] = "Added" else: - ret[p] = 'Failed' + ret[p] = "Failed" return ret def profile_rm(user, profile): - ''' + """ Remove profile from user user : string @@ -198,15 +199,15 @@ def profile_rm(user, profile): salt '*' rbac.profile_rm jorge 'Primary Administrator' salt '*' rbac.profile_rm jorge 'User Management,User Security' - ''' + """ ret = {} ## validate profiles - profiles = profile.split(',') + profiles = profile.split(",") known_profiles = profile_list().keys() valid_profiles = [p for p in profiles if p in known_profiles] log.debug( - 'rbac.profile_rm - profiles=%s, known_profiles=%s, valid_profiles=%s', + "rbac.profile_rm - profiles=%s, known_profiles=%s, valid_profiles=%s", profiles, known_profiles, valid_profiles, @@ -214,14 +215,18 @@ def profile_rm(user, profile): ## update user profiles if len(valid_profiles) > 0: - res = __salt__['cmd.run_all']('usermod -P "{profiles}" {login}'.format( - login=user, - profiles=','.join([p for p in profile_get(user) if p not in valid_profiles]), - )) - if res['retcode'] > 0: - ret['Error'] = { - 'retcode': res['retcode'], - 'message': res['stderr'] if 'stderr' in res else res['stdout'] + res = __salt__["cmd.run_all"]( + 'usermod -P "{profiles}" {login}'.format( + login=user, + profiles=",".join( + [p for p in profile_get(user) if p not in valid_profiles] + ), + ) + ) + if res["retcode"] > 0: + ret["Error"] = { + "retcode": res["retcode"], + "message": res["stderr"] if "stderr" in res else res["stdout"], } return ret @@ -229,17 +234,17 @@ def profile_rm(user, profile): active_profiles = profile_get(user, False) for p in profiles: if p not in valid_profiles: - ret[p] = 'Unknown' + ret[p] = "Unknown" elif p in active_profiles: - ret[p] = 'Failed' + ret[p] = "Failed" else: - ret[p] = 'Remove' + ret[p] = "Remove" return ret def role_list(): - ''' + """ List all available roles CLI Example: @@ -247,14 +252,14 @@ def role_list(): .. code-block:: bash salt '*' rbac.role_list - ''' + """ roles = {} ## read user_attr file (user:qualifier:res1:res2:attr) - with salt.utils.files.fopen('/etc/user_attr', 'r') as user_attr: + with salt.utils.files.fopen("/etc/user_attr", "r") as user_attr: for role in user_attr: role = salt.utils.stringutils.to_unicode(role) - role = role.split(':') + role = role.split(":") # skip comments and non complaint lines if len(role) != 5: @@ -262,24 +267,24 @@ def role_list(): # parse attr attrs = {} - for attr in role[4].split(';'): - attr_key, attr_val = attr.split('=') - if attr_key in ['auths', 'profiles', 'roles']: - attrs[attr_key] = attr_val.split(',') + for attr in role[4].split(";"): + attr_key, attr_val = attr.split("=") + if attr_key in ["auths", "profiles", "roles"]: + attrs[attr_key] = attr_val.split(",") else: attrs[attr_key] = attr_val role[4] = attrs # add role info to dict - if 'type' in role[4] and role[4]['type'] == 'role': - del role[4]['type'] + if "type" in role[4] and role[4]["type"] == "role": + del role[4]["type"] roles[role[0]] = role[4] return roles def role_get(user): - ''' + """ List roles for user user : string @@ -290,14 +295,14 @@ def role_get(user): .. code-block:: bash salt '*' rbac.role_get leo - ''' + """ user_roles = [] ## read user_attr file (user:qualifier:res1:res2:attr) - with salt.utils.files.fopen('/etc/user_attr', 'r') as user_attr: + with salt.utils.files.fopen("/etc/user_attr", "r") as user_attr: for role in user_attr: role = salt.utils.stringutils.to_unicode(role) - role = role.strip().strip().split(':') + role = role.strip().strip().split(":") # skip comments and non complaint lines if len(role) != 5: @@ -309,20 +314,20 @@ def role_get(user): # parse attr attrs = {} - for attr in role[4].strip().split(';'): - attr_key, attr_val = attr.strip().split('=') - if attr_key in ['auths', 'profiles', 'roles']: - attrs[attr_key] = attr_val.strip().split(',') + for attr in role[4].strip().split(";"): + attr_key, attr_val = attr.strip().split("=") + if attr_key in ["auths", "profiles", "roles"]: + attrs[attr_key] = attr_val.strip().split(",") else: attrs[attr_key] = attr_val - if 'roles' in attrs: - user_roles.extend(attrs['roles']) + if "roles" in attrs: + user_roles.extend(attrs["roles"]) return list(set(user_roles)) def role_add(user, role): - ''' + """ Add role to user user : string @@ -336,15 +341,15 @@ def role_add(user, role): salt '*' rbac.role_add martine netcfg salt '*' rbac.role_add martine netcfg,zfssnap - ''' + """ ret = {} ## validate roles - roles = role.split(',') + roles = role.split(",") known_roles = role_list().keys() valid_roles = [r for r in roles if r in known_roles] log.debug( - 'rbac.role_add - roles=%s, known_roles=%s, valid_roles=%s', + "rbac.role_add - roles=%s, known_roles=%s, valid_roles=%s", roles, known_roles, valid_roles, @@ -352,14 +357,15 @@ def role_add(user, role): ## update user roles if len(valid_roles) > 0: - res = __salt__['cmd.run_all']('usermod -R "{roles}" {login}'.format( - login=user, - roles=','.join(set(role_get(user) + valid_roles)), - )) - if res['retcode'] > 0: - ret['Error'] = { - 'retcode': res['retcode'], - 'message': res['stderr'] if 'stderr' in res else res['stdout'] + res = __salt__["cmd.run_all"]( + 'usermod -R "{roles}" {login}'.format( + login=user, roles=",".join(set(role_get(user) + valid_roles)), + ) + ) + if res["retcode"] > 0: + ret["Error"] = { + "retcode": res["retcode"], + "message": res["stderr"] if "stderr" in res else res["stdout"], } return ret @@ -367,17 +373,17 @@ def role_add(user, role): active_roles = role_get(user) for r in roles: if r not in valid_roles: - ret[r] = 'Unknown' + ret[r] = "Unknown" elif r in active_roles: - ret[r] = 'Added' + ret[r] = "Added" else: - ret[r] = 'Failed' + ret[r] = "Failed" return ret def role_rm(user, role): - ''' + """ Remove role from user user : string @@ -391,15 +397,15 @@ def role_rm(user, role): salt '*' rbac.role_rm jorge netcfg salt '*' rbac.role_rm jorge netcfg,zfssnap - ''' + """ ret = {} ## validate roles - roles = role.split(',') + roles = role.split(",") known_roles = role_list().keys() valid_roles = [r for r in roles if r in known_roles] log.debug( - 'rbac.role_rm - roles=%s, known_roles=%s, valid_roles=%s', + "rbac.role_rm - roles=%s, known_roles=%s, valid_roles=%s", roles, known_roles, valid_roles, @@ -407,14 +413,16 @@ def role_rm(user, role): ## update user roles if len(valid_roles) > 0: - res = __salt__['cmd.run_all']('usermod -R "{roles}" {login}'.format( - login=user, - roles=','.join([r for r in role_get(user) if r not in valid_roles]), - )) - if res['retcode'] > 0: - ret['Error'] = { - 'retcode': res['retcode'], - 'message': res['stderr'] if 'stderr' in res else res['stdout'] + res = __salt__["cmd.run_all"]( + 'usermod -R "{roles}" {login}'.format( + login=user, + roles=",".join([r for r in role_get(user) if r not in valid_roles]), + ) + ) + if res["retcode"] > 0: + ret["Error"] = { + "retcode": res["retcode"], + "message": res["stderr"] if "stderr" in res else res["stdout"], } return ret @@ -422,17 +430,17 @@ def role_rm(user, role): active_roles = role_get(user) for r in roles: if r not in valid_roles: - ret[r] = 'Unknown' + ret[r] = "Unknown" elif r in active_roles: - ret[r] = 'Failed' + ret[r] = "Failed" else: - ret[r] = 'Remove' + ret[r] = "Remove" return ret def auth_list(): - ''' + """ List all available authorization CLI Example: @@ -440,29 +448,29 @@ def auth_list(): .. code-block:: bash salt '*' rbac.auth_list - ''' + """ auths = {} ## read auth_attr file (name:res1:res2:short_desc:long_desc:attr) - with salt.utils.files.fopen('/etc/security/auth_attr', 'r') as auth_attr: + with salt.utils.files.fopen("/etc/security/auth_attr", "r") as auth_attr: for auth in auth_attr: auth = salt.utils.stringutils.to_unicode(auth) - auth = auth.split(':') + auth = auth.split(":") # skip comments and non complaint lines if len(auth) != 6: continue # add auth info to dict - if auth[0][-1:] == '.': - auth[0] = '{0}*'.format(auth[0]) + if auth[0][-1:] == ".": + auth[0] = "{0}*".format(auth[0]) auths[auth[0]] = auth[3] return auths def auth_get(user, computed=True): - ''' + """ List authorization for user user : string @@ -475,14 +483,14 @@ def auth_get(user, computed=True): .. code-block:: bash salt '*' rbac.auth_get leo - ''' + """ user_auths = [] ## read user_attr file (user:qualifier:res1:res2:attr) - with salt.utils.files.fopen('/etc/user_attr', 'r') as user_attr: + with salt.utils.files.fopen("/etc/user_attr", "r") as user_attr: for auth in user_attr: auth = salt.utils.stringutils.to_unicode(auth) - auth = auth.strip().split(':') + auth = auth.strip().split(":") # skip comments and non complaint lines if len(auth) != 5: @@ -494,22 +502,22 @@ def auth_get(user, computed=True): # parse attr attrs = {} - for attr in auth[4].strip().split(';'): - attr_key, attr_val = attr.strip().split('=') - if attr_key in ['auths', 'profiles', 'roles']: - attrs[attr_key] = attr_val.strip().split(',') + for attr in auth[4].strip().split(";"): + attr_key, attr_val = attr.strip().split("=") + if attr_key in ["auths", "profiles", "roles"]: + attrs[attr_key] = attr_val.strip().split(",") else: attrs[attr_key] = attr_val - if 'auths' in attrs: - user_auths.extend(attrs['auths']) + if "auths" in attrs: + user_auths.extend(attrs["auths"]) ## also parse auths command if computed: - res = __salt__['cmd.run_all']('auths {0}'.format(user)) - if res['retcode'] == 0: - for auth in res['stdout'].splitlines(): - if ',' in auth: - user_auths.extend(auth.strip().split(',')) + res = __salt__["cmd.run_all"]("auths {0}".format(user)) + if res["retcode"] == 0: + for auth in res["stdout"].splitlines(): + if "," in auth: + user_auths.extend(auth.strip().split(",")) else: user_auths.append(auth.strip()) @@ -517,7 +525,7 @@ def auth_get(user, computed=True): def auth_add(user, auth): - ''' + """ Add authorization to user user : string @@ -531,15 +539,15 @@ def auth_add(user, auth): salt '*' rbac.auth_add martine solaris.zone.manage salt '*' rbac.auth_add martine solaris.zone.manage,solaris.mail.mailq - ''' + """ ret = {} ## validate auths - auths = auth.split(',') + auths = auth.split(",") known_auths = auth_list().keys() valid_auths = [r for r in auths if r in known_auths] log.debug( - 'rbac.auth_add - auths=%s, known_auths=%s, valid_auths=%s', + "rbac.auth_add - auths=%s, known_auths=%s, valid_auths=%s", auths, known_auths, valid_auths, @@ -547,14 +555,15 @@ def auth_add(user, auth): ## update user auths if len(valid_auths) > 0: - res = __salt__['cmd.run_all']('usermod -A "{auths}" {login}'.format( - login=user, - auths=','.join(set(auth_get(user, False) + valid_auths)), - )) - if res['retcode'] > 0: - ret['Error'] = { - 'retcode': res['retcode'], - 'message': res['stderr'] if 'stderr' in res else res['stdout'] + res = __salt__["cmd.run_all"]( + 'usermod -A "{auths}" {login}'.format( + login=user, auths=",".join(set(auth_get(user, False) + valid_auths)), + ) + ) + if res["retcode"] > 0: + ret["Error"] = { + "retcode": res["retcode"], + "message": res["stderr"] if "stderr" in res else res["stdout"], } return ret @@ -562,17 +571,17 @@ def auth_add(user, auth): active_auths = auth_get(user, False) for a in auths: if a not in valid_auths: - ret[a] = 'Unknown' + ret[a] = "Unknown" elif a in active_auths: - ret[a] = 'Added' + ret[a] = "Added" else: - ret[a] = 'Failed' + ret[a] = "Failed" return ret def auth_rm(user, auth): - ''' + """ Remove authorization from user user : string @@ -586,15 +595,15 @@ def auth_rm(user, auth): salt '*' rbac.auth_rm jorge solaris.zone.manage salt '*' rbac.auth_rm jorge solaris.zone.manage,solaris.mail.mailq - ''' + """ ret = {} ## validate auths - auths = auth.split(',') + auths = auth.split(",") known_auths = auth_list().keys() valid_auths = [a for a in auths if a in known_auths] log.debug( - 'rbac.auth_rm - auths=%s, known_auths=%s, valid_auths=%s', + "rbac.auth_rm - auths=%s, known_auths=%s, valid_auths=%s", auths, known_auths, valid_auths, @@ -602,14 +611,18 @@ def auth_rm(user, auth): ## update user auths if len(valid_auths) > 0: - res = __salt__['cmd.run_all']('usermod -A "{auths}" {login}'.format( - login=user, - auths=','.join([a for a in auth_get(user, False) if a not in valid_auths]), - )) - if res['retcode'] > 0: - ret['Error'] = { - 'retcode': res['retcode'], - 'message': res['stderr'] if 'stderr' in res else res['stdout'] + res = __salt__["cmd.run_all"]( + 'usermod -A "{auths}" {login}'.format( + login=user, + auths=",".join( + [a for a in auth_get(user, False) if a not in valid_auths] + ), + ) + ) + if res["retcode"] > 0: + ret["Error"] = { + "retcode": res["retcode"], + "message": res["stderr"] if "stderr" in res else res["stdout"], } return ret @@ -617,11 +630,11 @@ def auth_rm(user, auth): active_auths = auth_get(user, False) for a in auths: if a not in valid_auths: - ret[a] = 'Unknown' + ret[a] = "Unknown" elif a in active_auths: - ret[a] = 'Failed' + ret[a] = "Failed" else: - ret[a] = 'Remove' + ret[a] = "Remove" return ret diff --git a/salt/modules/rbenv.py b/salt/modules/rbenv.py index 0810b3831ee..a9bb67ee632 100644 --- a/salt/modules/rbenv.py +++ b/salt/modules/rbenv.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage ruby installations with rbenv. rbenv is supported on Linux and macOS. rbenv doesn't work on Windows (and isn't really necessary on Windows as there is no system Ruby on Windows). On Windows, the RubyInstaller and/or Pik are both @@ -8,13 +8,14 @@ good alternatives to work with multiple versions of Ruby on the same box. http://misheska.com/blog/2013/06/15/using-rbenv-to-manage-multiple-versions-of-ruby/ .. versionadded:: 0.16.0 -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import re -import logging # Import Salt libs import salt.utils.args @@ -29,22 +30,23 @@ from salt.ext import six # Set up logger log = logging.getLogger(__name__) -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} __opts__ = { - 'rbenv.root': None, - 'rbenv.build_env': None, + "rbenv.root": None, + "rbenv.build_env": None, } def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ if salt.utils.platform.is_windows(): - return (False, 'The rbenv execution module failed to load: only available on non-Windows systems.') + return ( + False, + "The rbenv execution module failed to load: only available on non-Windows systems.", + ) return True @@ -52,7 +54,7 @@ def _shlex_split(s): # from python:salt.utils.args.shlex_split: passing None for s will read # the string to split from standard input. if s is None: - ret = salt.utils.args.shlex_split('') + ret = salt.utils.args.shlex_split("") else: ret = salt.utils.args.shlex_split(s) @@ -68,25 +70,27 @@ def _parse_env(env): env = {} for bad_env_key in (x for x, y in six.iteritems(env) if y is None): - log.error('Environment variable \'%s\' passed without a value. ' - 'Setting value to an empty string', bad_env_key) - env[bad_env_key] = '' + log.error( + "Environment variable '%s' passed without a value. " + "Setting value to an empty string", + bad_env_key, + ) + env[bad_env_key] = "" return env def _rbenv_bin(runas=None): path = _rbenv_path(runas) - return '{0}/bin/rbenv'.format(path) + return "{0}/bin/rbenv".format(path) def _rbenv_path(runas=None): path = None - if runas in (None, 'root'): - path = __salt__['config.option']('rbenv.root') or '/usr/local/rbenv' + if runas in (None, "root"): + path = __salt__["config.option"]("rbenv.root") or "/usr/local/rbenv" else: - path = __salt__['config.option']('rbenv.root') \ - or '~{0}/.rbenv'.format(runas) + path = __salt__["config.option"]("rbenv.root") or "~{0}/.rbenv".format(runas) return os.path.expanduser(path) @@ -99,20 +103,16 @@ def _rbenv_exec(command, env=None, runas=None, ret=None): path = _rbenv_path(runas) environ = _parse_env(env) - environ['RBENV_ROOT'] = path + environ["RBENV_ROOT"] = path - result = __salt__['cmd.run_all']( - [binary] + command, - runas=runas, - env=environ - ) + result = __salt__["cmd.run_all"]([binary] + command, runas=runas, env=environ) if isinstance(ret, dict): ret.update(result) return ret - if result['retcode'] == 0: - return result['stdout'] + if result["retcode"] == 0: + return result["stdout"] else: return False @@ -121,43 +121,46 @@ def _install_rbenv(path, runas=None): if os.path.isdir(path): return True - cmd = ['git', 'clone', 'https://github.com/rbenv/rbenv.git', path] - return __salt__['cmd.retcode'](cmd, runas=runas, python_shell=False) == 0 + cmd = ["git", "clone", "https://github.com/rbenv/rbenv.git", path] + return __salt__["cmd.retcode"](cmd, runas=runas, python_shell=False) == 0 def _install_ruby_build(path, runas=None): - path = '{0}/plugins/ruby-build'.format(path) + path = "{0}/plugins/ruby-build".format(path) if os.path.isdir(path): return True - cmd = ['git', 'clone', - 'https://github.com/rbenv/ruby-build.git', path] - return __salt__['cmd.retcode'](cmd, runas=runas, python_shell=False) == 0 + cmd = ["git", "clone", "https://github.com/rbenv/ruby-build.git", path] + return __salt__["cmd.retcode"](cmd, runas=runas, python_shell=False) == 0 def _update_rbenv(path, runas=None): if not os.path.isdir(path): return False - return __salt__['cmd.retcode'](['git', 'pull'], - runas=runas, - cwd=path, - python_shell=False) == 0 + return ( + __salt__["cmd.retcode"]( + ["git", "pull"], runas=runas, cwd=path, python_shell=False + ) + == 0 + ) def _update_ruby_build(path, runas=None): - path = '{0}/plugins/ruby-build'.format(path) + path = "{0}/plugins/ruby-build".format(path) if not os.path.isdir(path): return False - return __salt__['cmd.retcode'](['git', 'pull'], - runas=runas, - cwd=path, - python_shell=False) == 0 + return ( + __salt__["cmd.retcode"]( + ["git", "pull"], runas=runas, cwd=path, python_shell=False + ) + == 0 + ) def install(runas=None, path=None): - ''' + """ Install rbenv systemwide CLI Example: @@ -165,14 +168,14 @@ def install(runas=None, path=None): .. code-block:: bash salt '*' rbenv.install - ''' + """ path = path or _rbenv_path(runas) path = os.path.expanduser(path) return _install_rbenv(path, runas) and _install_ruby_build(path, runas) def update(runas=None, path=None): - ''' + """ Updates the current versions of rbenv and ruby-build runas @@ -184,7 +187,7 @@ def update(runas=None, path=None): .. code-block:: bash salt '*' rbenv.update - ''' + """ path = path or _rbenv_path(runas) path = os.path.expanduser(path) @@ -192,7 +195,7 @@ def update(runas=None, path=None): def is_installed(runas=None): - ''' + """ Check if rbenv is installed CLI Example: @@ -200,12 +203,12 @@ def is_installed(runas=None): .. code-block:: bash salt '*' rbenv.is_installed - ''' - return __salt__['cmd.has_exec'](_rbenv_bin(runas)) + """ + return __salt__["cmd.has_exec"](_rbenv_bin(runas)) def install_ruby(ruby, runas=None): - ''' + """ Install a ruby implementation. ruby @@ -229,28 +232,28 @@ def install_ruby(ruby, runas=None): .. code-block:: bash salt '*' rbenv.install_ruby 2.0.0-p0 - ''' - ruby = re.sub(r'^ruby-', '', ruby) + """ + ruby = re.sub(r"^ruby-", "", ruby) env = None env_list = [] - if __grains__['os'] in ('FreeBSD', 'NetBSD', 'OpenBSD'): - env_list.append('MAKE=gmake') + if __grains__["os"] in ("FreeBSD", "NetBSD", "OpenBSD"): + env_list.append("MAKE=gmake") - if __salt__['config.get']('rbenv:build_env'): - env_list.append(__salt__['config.get']('rbenv:build_env')) - elif __salt__['config.option']('rbenv.build_env'): - env_list.append(__salt__['config.option']('rbenv.build_env')) + if __salt__["config.get"]("rbenv:build_env"): + env_list.append(__salt__["config.get"]("rbenv:build_env")) + elif __salt__["config.option"]("rbenv.build_env"): + env_list.append(__salt__["config.option"]("rbenv.build_env")) if env_list: - env = ' '.join(env_list) + env = " ".join(env_list) ret = {} - ret = _rbenv_exec(['install', ruby], env=env, runas=runas, ret=ret) - if ret is not False and ret['retcode'] == 0: + ret = _rbenv_exec(["install", ruby], env=env, runas=runas, ret=ret) + if ret is not False and ret["retcode"] == 0: rehash(runas=runas) - return ret['stderr'] + return ret["stderr"] else: # Cleanup the failed installation so it doesn't list as installed uninstall_ruby(ruby, runas=runas) @@ -258,7 +261,7 @@ def install_ruby(ruby, runas=None): def uninstall_ruby(ruby, runas=None): - ''' + """ Uninstall a ruby implementation. ruby @@ -274,14 +277,14 @@ def uninstall_ruby(ruby, runas=None): .. code-block:: bash salt '*' rbenv.uninstall_ruby 2.0.0-p0 - ''' - ruby = re.sub(r'^ruby-', '', ruby) - _rbenv_exec(['uninstall', '--force', ruby], runas=runas) + """ + ruby = re.sub(r"^ruby-", "", ruby) + _rbenv_exec(["uninstall", "--force", ruby], runas=runas) return True def versions(runas=None): - ''' + """ List the installed versions of ruby CLI Example: @@ -289,13 +292,13 @@ def versions(runas=None): .. code-block:: bash salt '*' rbenv.versions - ''' - ret = _rbenv_exec(['versions', '--bare'], runas=runas) + """ + ret = _rbenv_exec(["versions", "--bare"], runas=runas) return [] if ret is False else ret.splitlines() def default(ruby=None, runas=None): - ''' + """ Returns or sets the currently defined default ruby ruby @@ -309,17 +312,17 @@ def default(ruby=None, runas=None): salt '*' rbenv.default salt '*' rbenv.default 2.0.0-p0 - ''' + """ if ruby: - _rbenv_exec(['global', ruby], runas=runas) + _rbenv_exec(["global", ruby], runas=runas) return True else: - ret = _rbenv_exec(['global'], runas=runas) - return '' if ret is False else ret.strip() + ret = _rbenv_exec(["global"], runas=runas) + return "" if ret is False else ret.strip() def list_(runas=None): - ''' + """ List the installable versions of ruby runas @@ -331,19 +334,19 @@ def list_(runas=None): .. code-block:: bash salt '*' rbenv.list - ''' + """ ret = [] - output = _rbenv_exec(['install', '--list'], runas=runas) + output = _rbenv_exec(["install", "--list"], runas=runas) if output: for line in output.splitlines(): - if line == 'Available versions:': + if line == "Available versions:": continue ret.append(line.strip()) return ret def rehash(runas=None): - ''' + """ Run ``rbenv rehash`` to update the installed shims runas @@ -355,13 +358,13 @@ def rehash(runas=None): .. code-block:: bash salt '*' rbenv.rehash - ''' - _rbenv_exec(['rehash'], runas=runas) + """ + _rbenv_exec(["rehash"], runas=runas) return True def do(cmdline, runas=None, env=None): - ''' + """ Execute a ruby command with rbenv's shims from the user or the system CLI Example: @@ -370,12 +373,12 @@ def do(cmdline, runas=None, env=None): salt '*' rbenv.do 'gem list bundler' salt '*' rbenv.do 'gem list bundler' deploy - ''' + """ if not cmdline: # This is a positional argument so this should never happen, but this # will handle cases where someone explicitly passes a false value for # cmdline. - raise SaltInvocationError('Command must be specified') + raise SaltInvocationError("Command must be specified") path = _rbenv_path(runas) if not env: @@ -384,11 +387,15 @@ def do(cmdline, runas=None, env=None): # NOTE: Env vars (and their values) need to be str type on both Python 2 # and 3. The code below first normalizes all path components to unicode to # stitch them together, and then converts the result back to a str type. - env[str('PATH')] = salt.utils.stringutils.to_str( # future lint: disable=blacklisted-function - os.pathsep.join(( - salt.utils.path.join(path, 'shims'), - salt.utils.stringutils.to_unicode(os.environ['PATH']) - )) + env[ + str("PATH") + ] = salt.utils.stringutils.to_str( # future lint: disable=blacklisted-function + os.pathsep.join( + ( + salt.utils.path.join(path, "shims"), + salt.utils.stringutils.to_unicode(os.environ["PATH"]), + ) + ) ) try: @@ -396,22 +403,17 @@ def do(cmdline, runas=None, env=None): except AttributeError: cmdauth = salt.utils.args.shlex_split(six.text_type(cmdline)) - result = __salt__['cmd.run_all']( - cmdline, - runas=runas, - env=env, - python_shell=False - ) + result = __salt__["cmd.run_all"](cmdline, runas=runas, env=env, python_shell=False) - if result['retcode'] == 0: + if result["retcode"] == 0: rehash(runas=runas) - return result['stdout'] + return result["stdout"] else: return False def do_with_ruby(ruby, cmdline, runas=None): - ''' + """ Execute a ruby command with rbenv's shims using a specific ruby version CLI Example: @@ -420,12 +422,12 @@ def do_with_ruby(ruby, cmdline, runas=None): salt '*' rbenv.do_with_ruby 2.0.0-p0 'gem list bundler' salt '*' rbenv.do_with_ruby 2.0.0-p0 'gem list bundler' runas=deploy - ''' + """ if not cmdline: # This is a positional argument so this should never happen, but this # will handle cases where someone explicitly passes a false value for # cmdline. - raise SaltInvocationError('Command must be specified') + raise SaltInvocationError("Command must be specified") try: cmdline = salt.utils.args.shlex_split(cmdline) @@ -434,7 +436,7 @@ def do_with_ruby(ruby, cmdline, runas=None): env = {} if ruby: - env['RBENV_VERSION'] = ruby + env["RBENV_VERSION"] = ruby cmd = cmdline else: cmd = cmdline diff --git a/salt/modules/rdp.py b/salt/modules/rdp.py index 700019421ee..f38dbe29cea 100644 --- a/salt/modules/rdp.py +++ b/salt/modules/rdp.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Manage RDP Service on Windows servers -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -15,6 +15,7 @@ from salt.utils.decorators import depends try: from pywintypes import error as PyWinError import win32ts + _HAS_WIN32TS_DEPENDENCIES = True except ImportError: _HAS_WIN32TS_DEPENDENCIES = False @@ -23,40 +24,43 @@ _LOG = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if salt.utils.platform.is_windows(): - return 'rdp' - return (False, 'Module only works on Windows.') + return "rdp" + return (False, "Module only works on Windows.") def _parse_return_code_powershell(string): - ''' + """ return from the input string the return code of the powershell command - ''' + """ - regex = re.search(r'ReturnValue\s*: (\d*)', string) + regex = re.search(r"ReturnValue\s*: (\d*)", string) if not regex: - return (False, 'Could not parse PowerShell return code.') + return (False, "Could not parse PowerShell return code.") else: return int(regex.group(1)) def _psrdp(cmd): - ''' + """ Create a Win32_TerminalServiceSetting WMI Object as $RDP and execute the command cmd returns the STDOUT of the command - ''' - rdp = ('$RDP = Get-WmiObject -Class Win32_TerminalServiceSetting ' - '-Namespace root\\CIMV2\\TerminalServices -Computer . ' - '-Authentication 6 -ErrorAction Stop') - return __salt__['cmd.run']('{0} ; {1}'.format(rdp, cmd), - shell='powershell', python_shell=True) + """ + rdp = ( + "$RDP = Get-WmiObject -Class Win32_TerminalServiceSetting " + "-Namespace root\\CIMV2\\TerminalServices -Computer . " + "-Authentication 6 -ErrorAction Stop" + ) + return __salt__["cmd.run"]( + "{0} ; {1}".format(rdp, cmd), shell="powershell", python_shell=True + ) def enable(): - ''' + """ Enable RDP the service on the server CLI Example: @@ -64,14 +68,13 @@ def enable(): .. code-block:: bash salt '*' rdp.enable - ''' + """ - return _parse_return_code_powershell( - _psrdp('$RDP.SetAllowTsConnections(1,1)')) == 0 + return _parse_return_code_powershell(_psrdp("$RDP.SetAllowTsConnections(1,1)")) == 0 def disable(): - ''' + """ Disable RDP the service on the server CLI Example: @@ -79,14 +82,13 @@ def disable(): .. code-block:: bash salt '*' rdp.disable - ''' + """ - return _parse_return_code_powershell( - _psrdp('$RDP.SetAllowTsConnections(0,1)')) == 0 + return _parse_return_code_powershell(_psrdp("$RDP.SetAllowTsConnections(0,1)")) == 0 def status(): - ''' + """ Show if rdp is enabled on the server CLI Example: @@ -94,15 +96,15 @@ def status(): .. code-block:: bash salt '*' rdp.status - ''' + """ - out = int(_psrdp('echo $RDP.AllowTSConnections').strip()) + out = int(_psrdp("echo $RDP.AllowTSConnections").strip()) return out != 0 @depends(_HAS_WIN32TS_DEPENDENCIES) def list_sessions(logged_in_users_only=False): - ''' + """ List information about the sessions. .. versionadded:: 2016.11.0 @@ -115,31 +117,50 @@ def list_sessions(logged_in_users_only=False): .. code-block:: bash salt '*' rdp.list_sessions - ''' + """ ret = list() server = win32ts.WTS_CURRENT_SERVER_HANDLE - protocols = {win32ts.WTS_PROTOCOL_TYPE_CONSOLE: 'console', - win32ts.WTS_PROTOCOL_TYPE_ICA: 'citrix', - win32ts.WTS_PROTOCOL_TYPE_RDP: 'rdp'} - statuses = {win32ts.WTSActive: 'active', win32ts.WTSConnected: 'connected', - win32ts.WTSConnectQuery: 'connect_query', win32ts.WTSShadow: 'shadow', - win32ts.WTSDisconnected: 'disconnected', win32ts.WTSIdle: 'idle', - win32ts.WTSListen: 'listen', win32ts.WTSReset: 'reset', - win32ts.WTSDown: 'down', win32ts.WTSInit: 'init'} + protocols = { + win32ts.WTS_PROTOCOL_TYPE_CONSOLE: "console", + win32ts.WTS_PROTOCOL_TYPE_ICA: "citrix", + win32ts.WTS_PROTOCOL_TYPE_RDP: "rdp", + } + statuses = { + win32ts.WTSActive: "active", + win32ts.WTSConnected: "connected", + win32ts.WTSConnectQuery: "connect_query", + win32ts.WTSShadow: "shadow", + win32ts.WTSDisconnected: "disconnected", + win32ts.WTSIdle: "idle", + win32ts.WTSListen: "listen", + win32ts.WTSReset: "reset", + win32ts.WTSDown: "down", + win32ts.WTSInit: "init", + } for session in win32ts.WTSEnumerateSessions(server): - user = win32ts.WTSQuerySessionInformation(server, session['SessionId'], - win32ts.WTSUserName) or None - protocol_id = win32ts.WTSQuerySessionInformation(server, session['SessionId'], - win32ts.WTSClientProtocolType) - status_id = win32ts.WTSQuerySessionInformation(server, session['SessionId'], - win32ts.WTSConnectState) - protocol = protocols.get(protocol_id, 'unknown') - connection_status = statuses.get(status_id, 'unknown') - station = session['WinStationName'] or 'Disconnected' - connection_info = {'connection_status': connection_status, 'protocol': protocol, - 'session_id': session['SessionId'], 'station': station, - 'user': user} + user = ( + win32ts.WTSQuerySessionInformation( + server, session["SessionId"], win32ts.WTSUserName + ) + or None + ) + protocol_id = win32ts.WTSQuerySessionInformation( + server, session["SessionId"], win32ts.WTSClientProtocolType + ) + status_id = win32ts.WTSQuerySessionInformation( + server, session["SessionId"], win32ts.WTSConnectState + ) + protocol = protocols.get(protocol_id, "unknown") + connection_status = statuses.get(status_id, "unknown") + station = session["WinStationName"] or "Disconnected" + connection_info = { + "connection_status": connection_status, + "protocol": protocol, + "session_id": session["SessionId"], + "station": station, + "user": user, + } if logged_in_users_only: if user: ret.append(connection_info) @@ -147,13 +168,13 @@ def list_sessions(logged_in_users_only=False): ret.append(connection_info) if not ret: - _LOG.warning('No sessions found.') - return sorted(ret, key=lambda k: k['session_id']) + _LOG.warning("No sessions found.") + return sorted(ret, key=lambda k: k["session_id"]) @depends(_HAS_WIN32TS_DEPENDENCIES) def get_session(session_id): - ''' + """ Get information about a session. .. versionadded:: 2016.11.0 @@ -168,22 +189,22 @@ def get_session(session_id): salt '*' rdp.get_session session_id salt '*' rdp.get_session 99 - ''' + """ ret = dict() sessions = list_sessions() - session = [item for item in sessions if item['session_id'] == session_id] + session = [item for item in sessions if item["session_id"] == session_id] if session: ret = session[0] if not ret: - _LOG.warning('No session found for id: %s', session_id) + _LOG.warning("No session found for id: %s", session_id) return ret @depends(_HAS_WIN32TS_DEPENDENCIES) def disconnect_session(session_id): - ''' + """ Disconnect a session. .. versionadded:: 2016.11.0 @@ -198,18 +219,20 @@ def disconnect_session(session_id): salt '*' rdp.disconnect_session session_id salt '*' rdp.disconnect_session 99 - ''' + """ try: - win32ts.WTSDisconnectSession(win32ts.WTS_CURRENT_SERVER_HANDLE, session_id, True) + win32ts.WTSDisconnectSession( + win32ts.WTS_CURRENT_SERVER_HANDLE, session_id, True + ) except PyWinError as error: - _LOG.error('Error calling WTSDisconnectSession: %s', error) + _LOG.error("Error calling WTSDisconnectSession: %s", error) return False return True @depends(_HAS_WIN32TS_DEPENDENCIES) def logoff_session(session_id): - ''' + """ Initiate the logoff of a session. .. versionadded:: 2016.11.0 @@ -224,10 +247,10 @@ def logoff_session(session_id): salt '*' rdp.logoff_session session_id salt '*' rdp.logoff_session 99 - ''' + """ try: win32ts.WTSLogoffSession(win32ts.WTS_CURRENT_SERVER_HANDLE, session_id, True) except PyWinError as error: - _LOG.error('Error calling WTSLogoffSession: %s', error) + _LOG.error("Error calling WTSLogoffSession: %s", error) return False return True diff --git a/salt/modules/redismod.py b/salt/modules/redismod.py index 7a320ef7fdf..709110c7cf8 100644 --- a/salt/modules/redismod.py +++ b/salt/modules/redismod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to provide redis functionality to Salt .. versionadded:: 2014.7.0 @@ -13,67 +13,73 @@ Module to provide redis functionality to Salt redis.port: 6379 redis.db: 0 redis.password: None -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -from salt.ext.six.moves import zip -from salt.ext import six +from __future__ import absolute_import, print_function, unicode_literals + from datetime import datetime + import salt.utils.args +from salt.ext import six +from salt.ext.six.moves import zip # Import third party libs try: import redis + HAS_REDIS = True except ImportError: HAS_REDIS = False -__virtualname__ = 'redis' +__virtualname__ = "redis" def __virtual__(): - ''' + """ Only load this module if redis python module is installed - ''' + """ if HAS_REDIS: return __virtualname__ else: - return (False, 'The redis execution module failed to load: the redis python library is not available.') + return ( + False, + "The redis execution module failed to load: the redis python library is not available.", + ) def _connect(host=None, port=None, db=None, password=None): - ''' + """ Returns an instance of the redis client - ''' + """ if not host: - host = __salt__['config.option']('redis.host') + host = __salt__["config.option"]("redis.host") if not port: - port = __salt__['config.option']('redis.port') + port = __salt__["config.option"]("redis.port") if not db: - db = __salt__['config.option']('redis.db') + db = __salt__["config.option"]("redis.db") if not password: - password = __salt__['config.option']('redis.password') + password = __salt__["config.option"]("redis.password") return redis.StrictRedis(host, port, db, password, decode_responses=True) def _sconnect(host=None, port=None, password=None): - ''' + """ Returns an instance of the redis client - ''' + """ if host is None: - host = __salt__['config.option']('redis_sentinel.host', 'localhost') + host = __salt__["config.option"]("redis_sentinel.host", "localhost") if port is None: - port = __salt__['config.option']('redis_sentinel.port', 26379) + port = __salt__["config.option"]("redis_sentinel.port", 26379) if password is None: - password = __salt__['config.option']('redis_sentinel.password') + password = __salt__["config.option"]("redis_sentinel.password") return redis.StrictRedis(host, port, password=password, decode_responses=True) def bgrewriteaof(host=None, port=None, db=None, password=None): - ''' + """ Asynchronously rewrite the append-only file CLI Example: @@ -81,13 +87,13 @@ def bgrewriteaof(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.bgrewriteaof - ''' + """ server = _connect(host, port, db, password) return server.bgrewriteaof() def bgsave(host=None, port=None, db=None, password=None): - ''' + """ Asynchronously save the dataset to disk CLI Example: @@ -95,13 +101,13 @@ def bgsave(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.bgsave - ''' + """ server = _connect(host, port, db, password) return server.bgsave() -def config_get(pattern='*', host=None, port=None, db=None, password=None): - ''' +def config_get(pattern="*", host=None, port=None, db=None, password=None): + """ Get redis server configuration values CLI Example: @@ -110,13 +116,13 @@ def config_get(pattern='*', host=None, port=None, db=None, password=None): salt '*' redis.config_get salt '*' redis.config_get port - ''' + """ server = _connect(host, port, db, password) return server.config_get(pattern) def config_set(name, value, host=None, port=None, db=None, password=None): - ''' + """ Set redis server configuration values CLI Example: @@ -124,13 +130,13 @@ def config_set(name, value, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.config_set masterauth luv_kittens - ''' + """ server = _connect(host, port, db, password) return server.config_set(name, value) def dbsize(host=None, port=None, db=None, password=None): - ''' + """ Return the number of keys in the selected database CLI Example: @@ -138,13 +144,13 @@ def dbsize(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.dbsize - ''' + """ server = _connect(host, port, db, password) return server.dbsize() def delete(*keys, **connection_args): - ''' + """ Deletes the keys from redis, returns number of keys deleted CLI Example: @@ -152,10 +158,10 @@ def delete(*keys, **connection_args): .. code-block:: bash salt '*' redis.delete foo - ''' + """ # Get connection args from keywords if set conn_args = {} - for arg in ['host', 'port', 'db', 'password']: + for arg in ["host", "port", "db", "password"]: if arg in connection_args: conn_args[arg] = connection_args[arg] @@ -164,7 +170,7 @@ def delete(*keys, **connection_args): def exists(key, host=None, port=None, db=None, password=None): - ''' + """ Return true if the key exists in redis CLI Example: @@ -172,13 +178,13 @@ def exists(key, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.exists foo - ''' + """ server = _connect(host, port, db, password) return server.exists(key) def expire(key, seconds, host=None, port=None, db=None, password=None): - ''' + """ Set a keys time to live in seconds CLI Example: @@ -186,13 +192,13 @@ def expire(key, seconds, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.expire foo 300 - ''' + """ server = _connect(host, port, db, password) return server.expire(key, seconds) def expireat(key, timestamp, host=None, port=None, db=None, password=None): - ''' + """ Set a keys expire at given UNIX time CLI Example: @@ -200,13 +206,13 @@ def expireat(key, timestamp, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.expireat foo 1400000000 - ''' + """ server = _connect(host, port, db, password) return server.expireat(key, timestamp) def flushall(host=None, port=None, db=None, password=None): - ''' + """ Remove all keys from all databases CLI Example: @@ -214,13 +220,13 @@ def flushall(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.flushall - ''' + """ server = _connect(host, port, db, password) return server.flushall() def flushdb(host=None, port=None, db=None, password=None): - ''' + """ Remove all keys from the selected database CLI Example: @@ -228,13 +234,13 @@ def flushdb(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.flushdb - ''' + """ server = _connect(host, port, db, password) return server.flushdb() def get_key(key, host=None, port=None, db=None, password=None): - ''' + """ Get redis key value CLI Example: @@ -242,13 +248,13 @@ def get_key(key, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.get_key foo - ''' + """ server = _connect(host, port, db, password) return server.get(key) def hdel(key, *fields, **options): - ''' + """ Delete one of more hash fields. .. versionadded:: 2017.7.0 @@ -258,17 +264,17 @@ def hdel(key, *fields, **options): .. code-block:: bash salt '*' redis.hdel foo_hash bar_field1 bar_field2 - ''' - host = options.get('host', None) - port = options.get('port', None) - database = options.get('db', None) - password = options.get('password', None) + """ + host = options.get("host", None) + port = options.get("port", None) + database = options.get("db", None) + password = options.get("password", None) server = _connect(host, port, database, password) return server.hdel(key, *fields) def hexists(key, field, host=None, port=None, db=None, password=None): - ''' + """ Determine if a hash fields exists. .. versionadded:: 2017.7.0 @@ -278,13 +284,13 @@ def hexists(key, field, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.hexists foo_hash bar_field - ''' + """ server = _connect(host, port, db, password) return server.hexists(key, field) def hget(key, field, host=None, port=None, db=None, password=None): - ''' + """ Get specific field value from a redis hash, returns dict CLI Example: @@ -292,13 +298,13 @@ def hget(key, field, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.hget foo_hash bar_field - ''' + """ server = _connect(host, port, db, password) return server.hget(key, field) def hgetall(key, host=None, port=None, db=None, password=None): - ''' + """ Get all fields and values from a redis hash, returns dict CLI Example: @@ -306,13 +312,13 @@ def hgetall(key, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.hgetall foo_hash - ''' + """ server = _connect(host, port, db, password) return server.hgetall(key) def hincrby(key, field, increment=1, host=None, port=None, db=None, password=None): - ''' + """ Increment the integer value of a hash field by the given number. .. versionadded:: 2017.7.0 @@ -322,13 +328,15 @@ def hincrby(key, field, increment=1, host=None, port=None, db=None, password=Non .. code-block:: bash salt '*' redis.hincrby foo_hash bar_field 5 - ''' + """ server = _connect(host, port, db, password) return server.hincrby(key, field, amount=increment) -def hincrbyfloat(key, field, increment=1.0, host=None, port=None, db=None, password=None): - ''' +def hincrbyfloat( + key, field, increment=1.0, host=None, port=None, db=None, password=None +): + """ Increment the float value of a hash field by the given number. .. versionadded:: 2017.7.0 @@ -338,13 +346,13 @@ def hincrbyfloat(key, field, increment=1.0, host=None, port=None, db=None, passw .. code-block:: bash salt '*' redis.hincrbyfloat foo_hash bar_field 5.17 - ''' + """ server = _connect(host, port, db, password) return server.hincrbyfloat(key, field, amount=increment) def hlen(key, host=None, port=None, db=None, password=None): - ''' + """ Returns number of fields of a hash. .. versionadded:: 2017.7.0 @@ -354,13 +362,13 @@ def hlen(key, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.hlen foo_hash - ''' + """ server = _connect(host, port, db, password) return server.hlen(key) def hmget(key, *fields, **options): - ''' + """ Returns the values of all the given hash fields. .. versionadded:: 2017.7.0 @@ -370,17 +378,17 @@ def hmget(key, *fields, **options): .. code-block:: bash salt '*' redis.hmget foo_hash bar_field1 bar_field2 - ''' - host = options.get('host', None) - port = options.get('port', None) - database = options.get('db', None) - password = options.get('password', None) + """ + host = options.get("host", None) + port = options.get("port", None) + database = options.get("db", None) + password = options.get("password", None) server = _connect(host, port, database, password) return server.hmget(key, *fields) def hmset(key, **fieldsvals): - ''' + """ Sets multiple hash fields to multiple values. .. versionadded:: 2017.7.0 @@ -390,17 +398,17 @@ def hmset(key, **fieldsvals): .. code-block:: bash salt '*' redis.hmset foo_hash bar_field1=bar_value1 bar_field2=bar_value2 - ''' - host = fieldsvals.pop('host', None) - port = fieldsvals.pop('port', None) - database = fieldsvals.pop('db', None) - password = fieldsvals.pop('password', None) + """ + host = fieldsvals.pop("host", None) + port = fieldsvals.pop("port", None) + database = fieldsvals.pop("db", None) + password = fieldsvals.pop("password", None) server = _connect(host, port, database, password) return server.hmset(key, salt.utils.args.clean_kwargs(**fieldsvals)) def hset(key, field, value, host=None, port=None, db=None, password=None): - ''' + """ Set the value of a hash field. .. versionadded:: 2017.7.0 @@ -410,13 +418,13 @@ def hset(key, field, value, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.hset foo_hash bar_field bar_value - ''' + """ server = _connect(host, port, db, password) return server.hset(key, field, value) def hsetnx(key, field, value, host=None, port=None, db=None, password=None): - ''' + """ Set the value of a hash field only if the field does not exist. .. versionadded:: 2017.7.0 @@ -426,13 +434,13 @@ def hsetnx(key, field, value, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.hsetnx foo_hash bar_field bar_value - ''' + """ server = _connect(host, port, db, password) return server.hsetnx(key, field, value) def hvals(key, host=None, port=None, db=None, password=None): - ''' + """ Return all the values in a hash. .. versionadded:: 2017.7.0 @@ -442,13 +450,15 @@ def hvals(key, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.hvals foo_hash bar_field1 bar_value1 - ''' + """ server = _connect(host, port, db, password) return server.hvals(key) -def hscan(key, cursor=0, match=None, count=None, host=None, port=None, db=None, password=None): - ''' +def hscan( + key, cursor=0, match=None, count=None, host=None, port=None, db=None, password=None +): + """ Incrementally iterate hash fields and associated values. .. versionadded:: 2017.7.0 @@ -458,13 +468,13 @@ def hscan(key, cursor=0, match=None, count=None, host=None, port=None, db=None, .. code-block:: bash salt '*' redis.hscan foo_hash match='field_prefix_*' count=1 - ''' + """ server = _connect(host, port, db, password) return server.hscan(key, cursor=cursor, match=match, count=count) def info(host=None, port=None, db=None, password=None): - ''' + """ Get information and statistics about the server CLI Example: @@ -472,13 +482,13 @@ def info(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.info - ''' + """ server = _connect(host, port, db, password) return server.info() -def keys(pattern='*', host=None, port=None, db=None, password=None): - ''' +def keys(pattern="*", host=None, port=None, db=None, password=None): + """ Get redis keys, supports glob style patterns CLI Example: @@ -487,13 +497,13 @@ def keys(pattern='*', host=None, port=None, db=None, password=None): salt '*' redis.keys salt '*' redis.keys test* - ''' + """ server = _connect(host, port, db, password) return server.keys(pattern) def key_type(key, host=None, port=None, db=None, password=None): - ''' + """ Get redis key type CLI Example: @@ -501,13 +511,13 @@ def key_type(key, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.type foo - ''' + """ server = _connect(host, port, db, password) return server.type(key) def lastsave(host=None, port=None, db=None, password=None): - ''' + """ Get the UNIX time in seconds of the last successful save to disk CLI Example: @@ -515,7 +525,7 @@ def lastsave(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.lastsave - ''' + """ # Use of %s to get the timestamp is not supported by Python. The reason it # works is because it's passed to the system strftime which may not support # it. See: https://stackoverflow.com/a/11743262 @@ -527,7 +537,7 @@ def lastsave(host=None, port=None, db=None, password=None): def llen(key, host=None, port=None, db=None, password=None): - ''' + """ Get the length of a list in Redis CLI Example: @@ -535,13 +545,13 @@ def llen(key, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.llen foo_list - ''' + """ server = _connect(host, port, db, password) return server.llen(key) def lrange(key, start, stop, host=None, port=None, db=None, password=None): - ''' + """ Get a range of values from a list in Redis CLI Example: @@ -549,13 +559,13 @@ def lrange(key, start, stop, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.lrange foo_list 0 10 - ''' + """ server = _connect(host, port, db, password) return server.lrange(key, start, stop) def ping(host=None, port=None, db=None, password=None): - ''' + """ Ping the server, returns False on connection errors CLI Example: @@ -563,7 +573,7 @@ def ping(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.ping - ''' + """ server = _connect(host, port, db, password) try: return server.ping() @@ -572,7 +582,7 @@ def ping(host=None, port=None, db=None, password=None): def save(host=None, port=None, db=None, password=None): - ''' + """ Synchronously save the dataset to disk CLI Example: @@ -580,13 +590,13 @@ def save(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.save - ''' + """ server = _connect(host, port, db, password) return server.save() def set_key(key, value, host=None, port=None, db=None, password=None): - ''' + """ Set redis key value CLI Example: @@ -594,13 +604,13 @@ def set_key(key, value, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.set_key foo bar - ''' + """ server = _connect(host, port, db, password) return server.set(key, value) def shutdown(host=None, port=None, db=None, password=None): - ''' + """ Synchronously save the dataset to disk and then shut down the server CLI Example: @@ -608,7 +618,7 @@ def shutdown(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.shutdown - ''' + """ server = _connect(host, port, db, password) try: # Return false if unable to ping server @@ -625,9 +635,10 @@ def shutdown(host=None, port=None, db=None, password=None): return False -def slaveof(master_host=None, master_port=None, host=None, port=None, db=None, - password=None): - ''' +def slaveof( + master_host=None, master_port=None, host=None, port=None, db=None, password=None +): + """ Make the server a slave of another instance, or promote it as master CLI Example: @@ -639,7 +650,7 @@ def slaveof(master_host=None, master_port=None, host=None, port=None, db=None, salt '*' redis.slaveof redis-n01.example.com # Become master salt '*' redis.slaveof - ''' + """ if master_host and not master_port: master_port = 6379 server = _connect(host, port, db, password) @@ -647,7 +658,7 @@ def slaveof(master_host=None, master_port=None, host=None, port=None, db=None, def smembers(key, host=None, port=None, db=None, password=None): - ''' + """ Get members in a Redis set CLI Example: @@ -655,13 +666,13 @@ def smembers(key, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.smembers foo_set - ''' + """ server = _connect(host, port, db, password) return list(server.smembers(key)) def time(host=None, port=None, db=None, password=None): - ''' + """ Return the current server UNIX time in seconds CLI Example: @@ -669,13 +680,13 @@ def time(host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.time - ''' + """ server = _connect(host, port, db, password) return server.time()[0] def zcard(key, host=None, port=None, db=None, password=None): - ''' + """ Get the length of a sorted set in Redis CLI Example: @@ -683,13 +694,13 @@ def zcard(key, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.zcard foo_sorted - ''' + """ server = _connect(host, port, db, password) return server.zcard(key) def zrange(key, start, stop, host=None, port=None, db=None, password=None): - ''' + """ Get a range of values from a sorted set in Redis by index CLI Example: @@ -697,13 +708,13 @@ def zrange(key, start, stop, host=None, port=None, db=None, password=None): .. code-block:: bash salt '*' redis.zrange foo_sorted 0 10 - ''' + """ server = _connect(host, port, db, password) return server.zrange(key, start, stop) def sentinel_get_master_ip(master, host=None, port=None, password=None): - ''' + """ Get ip for sentinel master .. versionadded: 2016.3.0 @@ -713,14 +724,14 @@ def sentinel_get_master_ip(master, host=None, port=None, password=None): .. code-block:: bash salt '*' redis.sentinel_get_master_ip 'mymaster' - ''' + """ server = _sconnect(host, port, password) ret = server.sentinel_get_master_addr_by_name(master) - return dict(list(zip(('master_host', 'master_port'), ret))) + return dict(list(zip(("master_host", "master_port"), ret))) def get_master_ip(host=None, port=None, password=None): - ''' + """ Get host information about slave .. versionadded: 2016.3.0 @@ -730,8 +741,8 @@ def get_master_ip(host=None, port=None, password=None): .. code-block:: bash salt '*' redis.get_master_ip - ''' + """ server = _connect(host, port, password) srv_info = server.info() - ret = (srv_info.get('master_host', ''), srv_info.get('master_port', '')) - return dict(list(zip(('master_host', 'master_port'), ret))) + ret = (srv_info.get("master_host", ""), srv_info.get("master_port", "")) + return dict(list(zip(("master_host", "master_port"), ret))) diff --git a/salt/modules/reg.py b/salt/modules/reg.py index 0a97db582f8..019ba5d3878 100644 --- a/salt/modules/reg.py +++ b/salt/modules/reg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Manage the Windows registry Hives @@ -64,7 +64,7 @@ Value: - Each value name has a corresponding value :depends: - salt.utils.win_reg -''' +""" # When production windows installer is using Python 3, Python 2 code can be removed from __future__ import absolute_import, print_function, unicode_literals @@ -78,26 +78,31 @@ from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'reg' +__virtualname__ = "reg" def __virtual__(): - ''' + """ Only works on Windows systems with PyWin32 - ''' + """ if not salt.utils.platform.is_windows(): - return (False, 'reg execution module failed to load: ' - 'The module will only run on Windows systems') + return ( + False, + "reg execution module failed to load: " + "The module will only run on Windows systems", + ) - if 'reg.read_value' not in __utils__: - return (False, 'reg execution module failed to load: ' - 'The reg salt util is unavailable') + if "reg.read_value" not in __utils__: + return ( + False, + "reg execution module failed to load: The reg salt util is unavailable", + ) return __virtualname__ def key_exists(hive, key, use_32bit_registry=False): - r''' + r""" Check that the key is found in the registry. This refers to keys and not value/data pairs. @@ -117,14 +122,14 @@ def key_exists(hive, key, use_32bit_registry=False): .. code-block:: bash salt '*' reg.key_exists HKLM SOFTWARE\Microsoft - ''' - return __utils__['reg.key_exists'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry) + """ + return __utils__["reg.key_exists"]( + hive=hive, key=key, use_32bit_registry=use_32bit_registry + ) def value_exists(hive, key, vname, use_32bit_registry=False): - r''' + r""" Check that the value/data pair is found in the registry. .. versionadded:: 3000 @@ -147,15 +152,14 @@ def value_exists(hive, key, vname, use_32bit_registry=False): .. code-block:: bash salt '*' reg.value_exists HKLM SOFTWARE\Microsoft\Windows\CurrentVersion CommonFilesDir - ''' - return __utils__['reg.value_exists'](hive=hive, - key=key, - vname=vname, - use_32bit_registry=use_32bit_registry) + """ + return __utils__["reg.value_exists"]( + hive=hive, key=key, vname=vname, use_32bit_registry=use_32bit_registry + ) def broadcast_change(): - ''' + """ Refresh the windows environment. .. note:: @@ -170,12 +174,12 @@ def broadcast_change(): .. code-block:: bash salt '*' reg.broadcast_change - ''' - return salt.utils.win_functions.broadcast_setting_change('Environment') + """ + return salt.utils.win_functions.broadcast_setting_change("Environment") def list_keys(hive, key=None, use_32bit_registry=False): - ''' + """ Enumerates the subkeys in a registry key or hive. Args: @@ -205,14 +209,14 @@ def list_keys(hive, key=None, use_32bit_registry=False): .. code-block:: bash salt '*' reg.list_keys HKLM 'SOFTWARE' - ''' - return __utils__['reg.list_keys'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry) + """ + return __utils__["reg.list_keys"]( + hive=hive, key=key, use_32bit_registry=use_32bit_registry + ) def list_values(hive, key=None, use_32bit_registry=False): - r''' + r""" Enumerates the values in a registry key or hive. .. note:: @@ -246,14 +250,14 @@ def list_values(hive, key=None, use_32bit_registry=False): .. code-block:: bash salt '*' reg.list_values HKLM 'SYSTEM\\CurrentControlSet\\Services\\Tcpip' - ''' - return __utils__['reg.list_values'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry) + """ + return __utils__["reg.list_values"]( + hive=hive, key=key, use_32bit_registry=use_32bit_registry + ) def read_value(hive, key, vname=None, use_32bit_registry=False): - r''' + r""" Reads a registry value entry or the default value for a key. To read the default value, don't pass ``vname`` @@ -306,21 +310,22 @@ def read_value(hive, key, vname=None, use_32bit_registry=False): .. code-block:: bash salt '*' reg.read_value HKEY_LOCAL_MACHINE 'SOFTWARE\Salt' - ''' - return __utils__['reg.read_value'](hive=hive, - key=key, - vname=vname, - use_32bit_registry=use_32bit_registry) + """ + return __utils__["reg.read_value"]( + hive=hive, key=key, vname=vname, use_32bit_registry=use_32bit_registry + ) -def set_value(hive, - key, - vname=None, - vdata=None, - vtype='REG_SZ', - use_32bit_registry=False, - volatile=False): - ''' +def set_value( + hive, + key, + vname=None, + vdata=None, + vtype="REG_SZ", + use_32bit_registry=False, + volatile=False, +): + """ Sets a value in the registry. If ``vname`` is passed, it will be the value for that value name, otherwise it will be the default value for the specified key @@ -429,18 +434,20 @@ def set_value(hive, .. code-block:: bash salt '*' reg.set_value HKEY_LOCAL_MACHINE 'SOFTWARE\\Salt' 'list_data' vtype=REG_MULTI_SZ vdata='["Salt", "is", "great"]' - ''' - return __utils__['reg.set_value'](hive=hive, - key=key, - vname=vname, - vdata=vdata, - vtype=vtype, - use_32bit_registry=use_32bit_registry, - volatile=volatile) + """ + return __utils__["reg.set_value"]( + hive=hive, + key=key, + vname=vname, + vdata=vdata, + vtype=vtype, + use_32bit_registry=use_32bit_registry, + volatile=volatile, + ) def delete_key_recursive(hive, key, use_32bit_registry=False): - r''' + r""" .. versionadded:: 2015.5.4 Delete a registry key to include all subkeys and value/data pairs. @@ -475,14 +482,14 @@ def delete_key_recursive(hive, key, use_32bit_registry=False): .. code-block:: bash salt '*' reg.delete_key_recursive HKLM SOFTWARE\\delete_me - ''' - return __utils__['reg.delete_key_recursive'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry) + """ + return __utils__["reg.delete_key_recursive"]( + hive=hive, key=key, use_32bit_registry=use_32bit_registry + ) def delete_value(hive, key, vname=None, use_32bit_registry=False): - r''' + r""" Delete a registry value entry or the default value for a key. Args: @@ -515,15 +522,14 @@ def delete_value(hive, key, vname=None, use_32bit_registry=False): .. code-block:: bash salt '*' reg.delete_value HKEY_CURRENT_USER 'SOFTWARE\\Salt' 'version' - ''' - return __utils__['reg.delete_value'](hive=hive, - key=key, - vname=vname, - use_32bit_registry=use_32bit_registry) + """ + return __utils__["reg.delete_value"]( + hive=hive, key=key, vname=vname, use_32bit_registry=use_32bit_registry + ) def import_file(source, use_32bit_registry=False): - ''' + """ Import registry settings from a Windows ``REG`` file by invoking ``REG.EXE``. .. versionadded:: 2018.3.0 @@ -553,8 +559,8 @@ def import_file(source, use_32bit_registry=False): salt machine1 reg.import_file salt://win/printer_config/110_Canon/postinstall_config.reg - ''' - cache_path = __salt__['cp.cache_file'](source) + """ + cache_path = __salt__["cp.cache_file"](source) if not cache_path: error_msg = "File/URL '{0}' probably invalid.".format(source) raise ValueError(error_msg) @@ -563,11 +569,8 @@ def import_file(source, use_32bit_registry=False): else: word_sz_txt = "64" cmd = 'reg import "{0}" /reg:{1}'.format(cache_path, word_sz_txt) - cmd_ret_dict = __salt__['cmd.run_all'](cmd, python_shell=True) - retcode = cmd_ret_dict['retcode'] + cmd_ret_dict = __salt__["cmd.run_all"](cmd, python_shell=True) + retcode = cmd_ret_dict["retcode"] if retcode != 0: - raise CommandExecutionError( - 'reg.exe import failed', - info=cmd_ret_dict - ) + raise CommandExecutionError("reg.exe import failed", info=cmd_ret_dict) return True diff --git a/salt/modules/rest_pkg.py b/salt/modules/rest_pkg.py index 0ca6f08c3a9..ac1ef8798ee 100644 --- a/salt/modules/rest_pkg.py +++ b/salt/modules/rest_pkg.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Package support for the REST example -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -14,50 +14,50 @@ import salt.utils.platform # Import 3rd-party libs from salt.ext import six - log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Only work on systems that are a proxy minion - ''' + """ try: - if salt.utils.platform.is_proxy() \ - and __opts__['proxy']['proxytype'] == 'rest_sample': + if ( + salt.utils.platform.is_proxy() + and __opts__["proxy"]["proxytype"] == "rest_sample" + ): return __virtualname__ except KeyError: return ( False, - 'The rest_package execution module failed to load. Check the ' - 'proxy key in pillar.' + "The rest_package execution module failed to load. Check the " + "proxy key in pillar.", ) return ( False, - 'The rest_package execution module failed to load: only works on a ' - 'rest_sample proxy minion.' + "The rest_package execution module failed to load: only works on a " + "rest_sample proxy minion.", ) def list_pkgs(versions_as_list=False, **kwargs): - return __proxy__['rest_sample.package_list']() + return __proxy__["rest_sample.package_list"]() -def install(name=None, refresh=False, fromrepo=None, - pkgs=None, sources=None, **kwargs): - return __proxy__['rest_sample.package_install'](name, **kwargs) +def install(name=None, refresh=False, fromrepo=None, pkgs=None, sources=None, **kwargs): + return __proxy__["rest_sample.package_install"](name, **kwargs) def remove(name=None, pkgs=None, **kwargs): - return __proxy__['rest_sample.package_remove'](name) + return __proxy__["rest_sample.package_remove"](name) def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -68,32 +68,33 @@ def version(*names, **kwargs): salt '*' pkg.version salt '*' pkg.version ... - ''' + """ if len(names) == 1: - return six.text_type(__proxy__['rest_sample.package_status'](names[0])) + return six.text_type(__proxy__["rest_sample.package_status"](names[0])) def upgrade(refresh=True, skip_verify=True, **kwargs): - old = __proxy__['rest_sample.package_list']() - new = __proxy__['rest_sample.uptodate']() - pkg_installed = __proxy__['rest_sample.upgrade']() + old = __proxy__["rest_sample.package_list"]() + new = __proxy__["rest_sample.uptodate"]() + pkg_installed = __proxy__["rest_sample.upgrade"]() return salt.utils.data.compare_dicts(old, pkg_installed) def installed( - name, - version=None, - refresh=False, - fromrepo=None, - skip_verify=False, - pkgs=None, - sources=None, - **kwargs): + name, + version=None, + refresh=False, + fromrepo=None, + skip_verify=False, + pkgs=None, + sources=None, + **kwargs +): - p = __proxy__['rest_sample.package_status'](name) + p = __proxy__["rest_sample.package_status"](name) if version is None: - if 'ret' in p: - return six.text_type(p['ret']) + if "ret" in p: + return six.text_type(p["ret"]) else: return True else: diff --git a/salt/modules/rest_sample_utils.py b/salt/modules/rest_sample_utils.py index 9925894b3b4..345bb90869d 100644 --- a/salt/modules/rest_sample_utils.py +++ b/salt/modules/rest_sample_utils.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Utility functions for the rest_sample -''' +""" from __future__ import absolute_import -__proxyenabled__ = ['rest_sample'] +__proxyenabled__ = ["rest_sample"] def fix_outage(): - ''' + """ "Fix" the outage CLI Example: @@ -17,12 +17,12 @@ def fix_outage(): salt 'rest-sample-proxy' rest_sample.fix_outage - ''' - return __proxy__['rest_sample.fix_outage']() + """ + return __proxy__["rest_sample.fix_outage"]() def get_test_string(): - ''' + """ Helper function to test cross-calling to the __proxy__ dunder. CLI Example: @@ -30,5 +30,5 @@ def get_test_string(): .. code-block:: bash salt 'rest-sample-proxy' rest_sample.get_test_string - ''' - return __proxy__['rest_sample.test_from_state']() + """ + return __proxy__["rest_sample.test_from_state"]() diff --git a/salt/modules/rest_service.py b/salt/modules/rest_service.py index ad4adc1f030..2a48d24320a 100644 --- a/salt/modules/rest_service.py +++ b/salt/modules/rest_service.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Provide the service module for the proxy-minion REST sample -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import logging +from __future__ import absolute_import, print_function, unicode_literals + import fnmatch +import logging import re # Import Salt libs @@ -13,39 +14,39 @@ import salt.utils.platform log = logging.getLogger(__name__) -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" def __virtual__(): - ''' + """ Only work on systems that are a proxy minion - ''' + """ try: - if salt.utils.platform.is_proxy() \ - and __opts__['proxy']['proxytype'] == 'rest_sample': + if ( + salt.utils.platform.is_proxy() + and __opts__["proxy"]["proxytype"] == "rest_sample" + ): return __virtualname__ except KeyError: return ( False, - 'The rest_service execution module failed to load. Check the ' - 'proxy key in pillar.' + "The rest_service execution module failed to load. Check the " + "proxy key in pillar.", ) return ( False, - 'The rest_service execution module failed to load: only works on a ' - 'rest_sample proxy minion.' + "The rest_service execution module failed to load: only works on a " + "rest_sample proxy minion.", ) def get_all(): - ''' + """ Return a list of all available services .. versionadded:: 2015.8.0 @@ -55,13 +56,13 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' - proxy_fn = 'rest_sample.service_list' + """ + proxy_fn = "rest_sample.service_list" return __proxy__[proxy_fn]() def list_(): - ''' + """ Return a list of all available services. .. versionadded: 2015.8.1 @@ -71,12 +72,12 @@ def list_(): .. code-block:: bash salt '*' service.list - ''' + """ return get_all() def start(name, sig=None): - ''' + """ Start the specified service on the rest_sample .. versionadded:: 2015.8.0 @@ -86,14 +87,14 @@ def start(name, sig=None): .. code-block:: bash salt '*' service.start - ''' + """ - proxy_fn = 'rest_sample.service_start' + proxy_fn = "rest_sample.service_start" return __proxy__[proxy_fn](name) def stop(name, sig=None): - ''' + """ Stop the specified service on the rest_sample .. versionadded:: 2015.8.0 @@ -103,13 +104,13 @@ def stop(name, sig=None): .. code-block:: bash salt '*' service.stop - ''' - proxy_fn = 'rest_sample.service_stop' + """ + proxy_fn = "rest_sample.service_stop" return __proxy__[proxy_fn](name) def restart(name, sig=None): - ''' + """ Restart the specified service with rest_sample .. versionadded:: 2015.8.0 @@ -119,14 +120,14 @@ def restart(name, sig=None): .. code-block:: bash salt '*' service.restart - ''' + """ - proxy_fn = 'rest_sample.service_restart' + proxy_fn = "rest_sample.service_restart" return __proxy__[proxy_fn](name) def status(name, sig=None): - ''' + """ Return the status for a service via rest_sample. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -149,10 +150,10 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status - ''' + """ - proxy_fn = 'rest_sample.service_status' - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + proxy_fn = "rest_sample.service_status" + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: @@ -160,7 +161,7 @@ def status(name, sig=None): results = {} for service in services: resp = __proxy__[proxy_fn](service) - if resp['comment'] == 'running': + if resp["comment"] == "running": results[service] = True else: results[service] = False @@ -170,20 +171,20 @@ def status(name, sig=None): def running(name, sig=None): - ''' + """ Return whether this service is running. .. versionadded:: 2015.8.0 - ''' + """ return status(name).get(name, False) def enabled(name, sig=None): - ''' + """ Only the 'redbull' service is 'enabled' in the test .. versionadded:: 2015.8.1 - ''' - return name == 'redbull' + """ + return name == "redbull" diff --git a/salt/modules/restartcheck.py b/salt/modules/restartcheck.py index 426a90e9701..039ee19bb5d 100644 --- a/salt/modules/restartcheck.py +++ b/salt/modules/restartcheck.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" checkrestart functionality for Debian and Red Hat Based systems Identifies services (processes) that are linked against deleted files (for example after downloading an updated @@ -9,8 +9,8 @@ Based on checkrestart script from debian-goodies (written by Matt Zimmerman for https://packages.debian.org/debian-goodies) and psdel by Sam Morris. :codeauthor: Jiri Kotlin -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os @@ -28,70 +28,74 @@ import salt.utils.path # Import 3rd partylibs from salt.ext import six -NILRT_FAMILY_NAME = 'NILinuxRT' +NILRT_FAMILY_NAME = "NILinuxRT" HAS_PSUTIL = False try: import psutil + HAS_PSUTIL = True except ImportError: pass LIST_DIRS = [ # We don't care about log files - '^/var/log/', - '^/var/local/log/', + "^/var/log/", + "^/var/local/log/", # Or about files under temporary locations - '^/var/run/', - '^/var/local/run/', + "^/var/run/", + "^/var/local/run/", # Or about files under /tmp - '^/tmp/', + "^/tmp/", # Or about files under /dev/shm - '^/dev/shm/', + "^/dev/shm/", # Or about files under /run - '^/run/', + "^/run/", # Or about files under /drm - '^/drm', + "^/drm", # Or about files under /var/tmp and /var/local/tmp - '^/var/tmp/', - '^/var/local/tmp/', + "^/var/tmp/", + "^/var/local/tmp/", # Or /dev/zero - '^/dev/zero', + "^/dev/zero", # Or /dev/pts (used by gpm) - '^/dev/pts/', + "^/dev/pts/", # Or /usr/lib/locale - '^/usr/lib/locale/', + "^/usr/lib/locale/", # Skip files from the user's home directories # many processes hold temporafy files there - '^/home/', + "^/home/", # Skip automatically generated files - '^.*icon-theme.cache', + "^.*icon-theme.cache", # Skip font files - '^/var/cache/fontconfig/', + "^/var/cache/fontconfig/", # Skip Nagios Spool - '^/var/lib/nagios3/spool/', + "^/var/lib/nagios3/spool/", # Skip nagios spool files - '^/var/lib/nagios3/spool/checkresults/', + "^/var/lib/nagios3/spool/checkresults/", # Skip Postgresql files - '^/var/lib/postgresql/', + "^/var/lib/postgresql/", # Skip VDR lib files - '^/var/lib/vdr/', + "^/var/lib/vdr/", # Skip Aio files found in MySQL servers - '^/[aio]', + "^/[aio]", # ignore files under /SYSV - '^/SYSV' + "^/SYSV", ] def __virtual__(): - ''' + """ Only run this module if the psutil python module is installed (package python-psutil). - ''' - return HAS_PSUTIL + """ + if HAS_PSUTIL: + return HAS_PSUTIL + else: + return (False, "Missing dependency: psutil") def _valid_deleted_file(path): - ''' + """ Filters file path against unwanted directories and decides whether file is marked as deleted. Returns: @@ -99,9 +103,9 @@ def _valid_deleted_file(path): Args: path: A string - path to file - ''' + """ ret = False - if path.endswith(' (deleted)'): + if path.endswith(" (deleted)"): ret = True if re.compile(r"\(path inode=[0-9]+\)$").search(path): ret = True @@ -113,29 +117,33 @@ def _valid_deleted_file(path): def _deleted_files(): - ''' + """ Iterates over /proc/PID/maps and /proc/PID/fd links and returns list of desired deleted files. Returns: List of deleted files to analyze, False on failure. - ''' + """ deleted_files = [] for proc in psutil.process_iter(): # pylint: disable=too-many-nested-blocks try: - pinfo = proc.as_dict(attrs=['pid', 'name']) + pinfo = proc.as_dict(attrs=["pid", "name"]) try: - with salt.utils.files.fopen('/proc/{0}/maps'.format(pinfo['pid'])) as maps: # pylint: disable=resource-leakage - dirpath = '/proc/' + six.text_type(pinfo['pid']) + '/fd/' + with salt.utils.files.fopen( + "/proc/{0}/maps".format(pinfo["pid"]) + ) as maps: # pylint: disable=resource-leakage + dirpath = "/proc/" + six.text_type(pinfo["pid"]) + "/fd/" listdir = os.listdir(dirpath) maplines = maps.readlines() except (OSError, IOError): yield False # /proc/PID/maps - mapline = re.compile(r'^[\da-f]+-[\da-f]+ [r-][w-][x-][sp-] ' - r'[\da-f]+ [\da-f]{2}:[\da-f]{2} (\d+) *(.+)( \(deleted\))?\n$') + mapline = re.compile( + r"^[\da-f]+-[\da-f]+ [r-][w-][x-][sp-] " + r"[\da-f]+ [\da-f]{2}:[\da-f]{2} (\d+) *(.+)( \(deleted\))?\n$" + ) for line in maplines: line = salt.utils.stringutils.to_unicode(line) @@ -148,7 +156,7 @@ def _deleted_files(): valid = _valid_deleted_file(path) if not valid: continue - val = (pinfo['name'], pinfo['pid'], path[0:-10]) + val = (pinfo["name"], pinfo["pid"], path[0:-10]) if val not in deleted_files: deleted_files.append(val) yield val @@ -162,8 +170,10 @@ def _deleted_files(): if os.path.isfile(readlink): filenames.append(readlink) - elif os.path.isdir(readlink) and readlink != '/': - for root, dummy_dirs, files in salt.utils.path.os_walk(readlink, followlinks=True): + elif os.path.isdir(readlink) and readlink != "/": + for root, dummy_dirs, files in salt.utils.path.os_walk( + readlink, followlinks=True + ): for name in files: filenames.append(os.path.join(root, name)) @@ -171,7 +181,7 @@ def _deleted_files(): valid = _valid_deleted_file(filename) if not valid: continue - val = (pinfo['name'], pinfo['pid'], filename) + val = (pinfo["name"], pinfo["pid"], filename) if val not in deleted_files: deleted_files.append(val) yield val @@ -182,9 +192,16 @@ def _deleted_files(): pass -def _format_output(kernel_restart, packages, verbose, restartable, nonrestartable, restartservicecommands, - restartinitcommands): - ''' +def _format_output( + kernel_restart, + packages, + verbose, + restartable, + nonrestartable, + restartservicecommands, + restartinitcommands, +): + """ Formats the output of the restartcheck module. Returns: @@ -199,56 +216,62 @@ def _format_output(kernel_restart, packages, verbose, restartable, nonrestartabl restartservicecommands: list of commands to restart services restartinitcommands: list of commands to restart init.d scripts - ''' + """ if not verbose: packages = restartable + nonrestartable if kernel_restart: - packages.append('System restart required.') + packages.append("System restart required.") return packages else: - ret = '' + ret = "" if kernel_restart: - ret = 'System restart required.\n\n' + ret = "System restart required.\n\n" if packages: - ret += "Found {0} processes using old versions of upgraded files.\n".format(len(packages)) + ret += "Found {0} processes using old versions of upgraded files.\n".format( + len(packages) + ) ret += "These are the packages:\n" if restartable: - ret += "Of these, {0} seem to contain systemd service definitions or init scripts " \ - "which can be used to restart them:\n".format(len(restartable)) + ret += ( + "Of these, {0} seem to contain systemd service definitions or init scripts " + "which can be used to restart them:\n".format(len(restartable)) + ) for package in restartable: - ret += package + ':\n' - for program in packages[package]['processes']: - ret += program + '\n' + ret += package + ":\n" + for program in packages[package]["processes"]: + ret += program + "\n" if restartservicecommands: ret += "\n\nThese are the systemd services:\n" - ret += '\n'.join(restartservicecommands) + ret += "\n".join(restartservicecommands) if restartinitcommands: ret += "\n\nThese are the initd scripts:\n" - ret += '\n'.join(restartinitcommands) + ret += "\n".join(restartinitcommands) if nonrestartable: - ret += "\n\nThese processes {0} do not seem to have an associated init script " \ - "to restart them:\n".format(len(nonrestartable)) + ret += ( + "\n\nThese processes {0} do not seem to have an associated init script " + "to restart them:\n".format(len(nonrestartable)) + ) for package in nonrestartable: - ret += package + ':\n' - for program in packages[package]['processes']: - ret += program + '\n' + ret += package + ":\n" + for program in packages[package]["processes"]: + ret += program + "\n" return ret def _kernel_versions_debian(): - ''' + """ Last installed kernel name, for Debian based systems. Returns: List with possible names of last installed kernel as they are probably interpreted in output of `uname -a` command. - ''' - kernel_get_selections = __salt__['cmd.run']('dpkg --get-selections linux-image-*') + """ + kernel_get_selections = __salt__["cmd.run"]("dpkg --get-selections linux-image-*") kernels = [] kernel_versions = [] for line in kernel_get_selections.splitlines(): @@ -259,133 +282,140 @@ def _kernel_versions_debian(): except IndexError: kernel = kernels[0] - kernel = kernel.rstrip('\t\tinstall') + kernel = kernel.rstrip("\t\tinstall") - kernel_get_version = __salt__['cmd.run']('apt-cache policy ' + kernel) + kernel_get_version = __salt__["cmd.run"]("apt-cache policy " + kernel) for line in kernel_get_version.splitlines(): - if line.startswith(' Installed: '): - kernel_v = line.strip(' Installed: ') + if line.startswith(" Installed: "): + kernel_v = line.strip(" Installed: ") kernel_versions.append(kernel_v) break - if __grains__['os'] == 'Ubuntu': - kernel_v = kernel_versions[0].rsplit('.', 1) - kernel_ubuntu_generic = kernel_v[0] + '-generic #' + kernel_v[1] - kernel_ubuntu_lowlatency = kernel_v[0] + '-lowlatency #' + kernel_v[1] + if __grains__["os"] == "Ubuntu": + kernel_v = kernel_versions[0].rsplit(".", 1) + kernel_ubuntu_generic = kernel_v[0] + "-generic #" + kernel_v[1] + kernel_ubuntu_lowlatency = kernel_v[0] + "-lowlatency #" + kernel_v[1] kernel_versions.extend([kernel_ubuntu_generic, kernel_ubuntu_lowlatency]) return kernel_versions def _kernel_versions_redhat(): - ''' + """ Name of the last installed kernel, for Red Hat based systems. Returns: List with name of last installed kernel as it is interpreted in output of `uname -a` command. - ''' - kernel_get_last = __salt__['cmd.run']('rpm -q --last kernel') + """ + kernel_get_last = __salt__["cmd.run"]("rpm -q --last kernel") kernels = [] kernel_versions = [] for line in kernel_get_last.splitlines(): - if 'kernel-' in line: + if "kernel-" in line: kernels.append(line) - kernel = kernels[0].split(' ', 1)[0] - kernel = kernel.strip('kernel-') + kernel = kernels[0].split(" ", 1)[0] + kernel = kernel.strip("kernel-") kernel_versions.append(kernel) return kernel_versions def _kernel_versions_nilrt(): - ''' + """ Last installed kernel name, for Debian based systems. Returns: List with possible names of last installed kernel as they are probably interpreted in output of `uname -a` command. - ''' + """ kver = None def _get_kver_from_bin(kbin): - ''' + """ Get kernel version from a binary image or None if detection fails - ''' - kvregex = r'[0-9]+\.[0-9]+\.[0-9]+-rt\S+' - kernel_strings = __salt__['cmd.run']('strings {0}'.format(kbin)) + """ + kvregex = r"[0-9]+\.[0-9]+\.[0-9]+-rt\S+" + kernel_strings = __salt__["cmd.run"]("strings {0}".format(kbin)) re_result = re.search(kvregex, kernel_strings) return None if re_result is None else re_result.group(0) - if __grains__.get('lsb_distrib_id') == 'nilrt': - if 'arm' in __grains__.get('cpuarch'): + if __grains__.get("lsb_distrib_id") == "nilrt": + if "arm" in __grains__.get("cpuarch"): # the kernel is inside a uboot created itb (FIT) image alongside the # device tree, ramdisk and a bootscript. There is no package management # or any other kind of versioning info, so we need to extract the itb. - itb_path = '/boot/linux_runmode.itb' - compressed_kernel = '/var/volatile/tmp/uImage.gz' - uncompressed_kernel = '/var/volatile/tmp/uImage' - __salt__['cmd.run']('dumpimage -i {0} -T flat_dt -p0 kernel -o {1}' - .format(itb_path, compressed_kernel)) - __salt__['cmd.run']('gunzip -f {0}'.format(compressed_kernel)) + itb_path = "/boot/linux_runmode.itb" + compressed_kernel = "/var/volatile/tmp/uImage.gz" + uncompressed_kernel = "/var/volatile/tmp/uImage" + __salt__["cmd.run"]( + "dumpimage -i {0} -T flat_dt -p0 kernel -o {1}".format( + itb_path, compressed_kernel + ) + ) + __salt__["cmd.run"]("gunzip -f {0}".format(compressed_kernel)) kver = _get_kver_from_bin(uncompressed_kernel) else: # the kernel bzImage is copied to rootfs without package management or # other versioning info. - kver = _get_kver_from_bin('/boot/runmode/bzImage') + kver = _get_kver_from_bin("/boot/runmode/bzImage") else: # kernels in newer NILRT's are installed via package management and # have the version appended to the kernel image filename - if 'arm' in __grains__.get('cpuarch'): - kver = os.path.basename(os.readlink('/boot/uImage')).strip('uImage-') + if "arm" in __grains__.get("cpuarch"): + kver = os.path.basename(os.readlink("/boot/uImage")).strip("uImage-") else: - kver = os.path.basename(os.readlink('/boot/bzImage')).strip('bzImage-') + kver = os.path.basename(os.readlink("/boot/bzImage")).strip("bzImage-") return [] if kver is None else [kver] def _check_timeout(start_time, timeout): - ''' + """ Name of the last installed kernel, for Red Hat based systems. Returns: List with name of last installed kernel as it is interpreted in output of `uname -a` command. - ''' + """ timeout_milisec = timeout * 60000 if timeout_milisec < (int(round(time.time() * 1000)) - start_time): - raise salt.exceptions.TimeoutError('Timeout expired.') + raise salt.exceptions.TimeoutError("Timeout expired.") def _file_changed_nilrt(full_filepath): - ''' + """ Detect whether a file changed in an NILinuxRT system using md5sum and timestamp files from a state directory. Returns: - False if md5sum/timestamp state files don't exist - True/False depending if ``base_filename`` got modified/touched - ''' + """ rs_state_dir = "/var/lib/salt/restartcheck_state" base_filename = os.path.basename(full_filepath) - timestamp_file = os.path.join(rs_state_dir, '{0}.timestamp'.format(base_filename)) - md5sum_file = os.path.join(rs_state_dir, '{0}.md5sum'.format(base_filename)) + timestamp_file = os.path.join(rs_state_dir, "{0}.timestamp".format(base_filename)) + md5sum_file = os.path.join(rs_state_dir, "{0}.md5sum".format(base_filename)) if not os.path.exists(timestamp_file) or not os.path.exists(md5sum_file): return False - prev_timestamp = __salt__['file.read'](timestamp_file).rstrip() + prev_timestamp = __salt__["file.read"](timestamp_file).rstrip() # Need timestamp in seconds so floor it using int() cur_timestamp = str(int(os.path.getmtime(full_filepath))) if prev_timestamp != cur_timestamp: return True - return bool(__salt__['cmd.retcode']('md5sum -cs {0}'.format(md5sum_file), output_loglevel="quiet")) + return bool( + __salt__["cmd.retcode"]( + "md5sum -cs {0}".format(md5sum_file), output_loglevel="quiet" + ) + ) def _kernel_modules_changed_nilrt(kernelversion): - ''' + """ Once a NILRT kernel module is inserted, it can't be rmmod so systems need rebooting (some modules explicitly ask for reboots even on first install), hence this functionality of determining if the module state got modified by @@ -393,14 +423,14 @@ def _kernel_modules_changed_nilrt(kernelversion): Returns: - True/False depending if modules.dep got modified/touched - ''' + """ if kernelversion is not None: - return _file_changed_nilrt('/lib/modules/{0}/modules.dep'.format(kernelversion)) + return _file_changed_nilrt("/lib/modules/{0}/modules.dep".format(kernelversion)) return False def _sysapi_changed_nilrt(): - ''' + """ Besides the normal Linux kernel driver interfaces, NILinuxRT-supported hardware features an extensible, plugin-based device enumeration and configuration interface named "System API". When an installed package is extending the API it is very hard to know all repercurssions and @@ -410,8 +440,8 @@ def _sysapi_changed_nilrt(): Returns: - True/False depending on if nisysapi.ini got modified/touched - False if nisysapi.ini does not exist to avoid triggering unnecessary reboots - ''' - nisysapi_path = '/usr/local/natinst/share/nisysapi.ini' + """ + nisysapi_path = "/usr/local/natinst/share/nisysapi.ini" if os.path.exists(nisysapi_path): return _file_changed_nilrt(nisysapi_path) return False @@ -419,7 +449,7 @@ def _sysapi_changed_nilrt(): # pylint: disable=too-many-locals,too-many-branches,too-many-statements def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs): - ''' + """ Analyzes files openeded by running processes and seeks for packages which need to be restarted. Args: @@ -440,37 +470,43 @@ def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs): .. code-block:: bash salt '*' restartcheck.restartcheck - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) start_time = int(round(time.time() * 1000)) kernel_restart = True - verbose = kwargs.pop('verbose', True) - timeout = kwargs.pop('timeout', 5) - if __grains__.get('os_family') == 'Debian': - cmd_pkg_query = 'dpkg-query --listfiles ' - systemd_folder = '/lib/systemd/system/' - systemd = '/bin/systemd' + verbose = kwargs.pop("verbose", True) + timeout = kwargs.pop("timeout", 5) + if __grains__.get("os_family") == "Debian": + cmd_pkg_query = "dpkg-query --listfiles " + systemd_folder = "/lib/systemd/system/" + systemd = "/bin/systemd" kernel_versions = _kernel_versions_debian() - elif __grains__.get('os_family') == 'RedHat': - cmd_pkg_query = 'repoquery -l ' - systemd_folder = '/usr/lib/systemd/system/' - systemd = '/usr/bin/systemctl' + elif __grains__.get("os_family") == "RedHat": + cmd_pkg_query = "repoquery -l " + systemd_folder = "/usr/lib/systemd/system/" + systemd = "/usr/bin/systemctl" kernel_versions = _kernel_versions_redhat() - elif __grains__.get('os_family') == NILRT_FAMILY_NAME: - cmd_pkg_query = 'opkg files ' - systemd = '' + elif __grains__.get("os_family") == NILRT_FAMILY_NAME: + cmd_pkg_query = "opkg files " + systemd = "" kernel_versions = _kernel_versions_nilrt() else: - return {'result': False, 'comment': 'Only available on Debian, Red Hat and NI Linux Real-Time based systems.'} + return { + "result": False, + "comment": "Only available on Debian, Red Hat and NI Linux Real-Time based systems.", + } # Check kernel versions - kernel_current = __salt__['cmd.run']('uname -a') + kernel_current = __salt__["cmd.run"]("uname -a") for kernel in kernel_versions: _check_timeout(start_time, timeout) if kernel in kernel_current: - if __grains__.get('os_family') == 'NILinuxRT': + if __grains__.get("os_family") == "NILinuxRT": # Check kernel modules and hardware API's for version changes - if not _kernel_modules_changed_nilrt(kernel) and not _sysapi_changed_nilrt(): + if ( + not _kernel_modules_changed_nilrt(kernel) + and not _sysapi_changed_nilrt() + ): kernel_restart = False break else: @@ -485,7 +521,7 @@ def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs): if not isinstance(ignorelist, list): ignorelist = [ignorelist] else: - ignorelist = ['screen', 'systemd'] + ignorelist = ["screen", "systemd"] if blacklist: if not isinstance(blacklist, list): @@ -499,51 +535,69 @@ def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs): else: excludepid = [] - for service in __salt__['service.get_running'](): + for service in __salt__["service.get_running"](): _check_timeout(start_time, timeout) - service_show = __salt__['service.show'](service) - if 'ExecMainPID' in service_show: - running_services[service] = int(service_show['ExecMainPID']) + service_show = __salt__["service.show"](service) + if "ExecMainPID" in service_show: + running_services[service] = int(service_show["ExecMainPID"]) owners_cache = {} for deleted_file in _deleted_files(): if deleted_file is False: - return {'result': False, 'comment': 'Could not get list of processes.' - ' (Do you have root access?)'} + return { + "result": False, + "comment": "Could not get list of processes." + " (Do you have root access?)", + } _check_timeout(start_time, timeout) name, pid, path = deleted_file[0], deleted_file[1], deleted_file[2] if path in blacklist or pid in excludepid: continue try: - readlink = os.readlink('/proc/{0}/exe'.format(pid)) + readlink = os.readlink("/proc/{0}/exe".format(pid)) except OSError: excludepid.append(pid) continue try: packagename = owners_cache[readlink] except KeyError: - packagename = __salt__['pkg.owner'](readlink) + packagename = __salt__["pkg.owner"](readlink) if not packagename: packagename = name owners_cache[readlink] = packagename for running_service in running_services: _check_timeout(start_time, timeout) - if running_service not in restart_services and pid == running_services[running_service]: + if ( + running_service not in restart_services + and pid == running_services[running_service] + ): if packagename and packagename not in ignorelist: restart_services.append(running_service) name = running_service if packagename and packagename not in ignorelist: - program = '\t' + six.text_type(pid) + ' ' + readlink + ' (file: ' + six.text_type(path) + ')' + program = ( + "\t" + + six.text_type(pid) + + " " + + readlink + + " (file: " + + six.text_type(path) + + ")" + ) if packagename not in packages: - packages[packagename] = {'initscripts': [], 'systemdservice': [], 'processes': [program], - 'process_name': name} + packages[packagename] = { + "initscripts": [], + "systemdservice": [], + "processes": [program], + "process_name": name, + } else: - if program not in packages[packagename]['processes']: - packages[packagename]['processes'].append(program) + if program not in packages[packagename]["processes"]: + packages[packagename]["processes"].append(program) if not packages and not kernel_restart: - return 'No packages seem to need to be restarted.' + return "No packages seem to need to be restarted." for package in packages: _check_timeout(start_time, timeout) @@ -556,28 +610,34 @@ def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs): if not line: break pth = line[:-1] - if pth.startswith('/etc/init.d/') and not pth.endswith('.sh'): - packages[package]['initscripts'].append(pth[12:]) + if pth.startswith("/etc/init.d/") and not pth.endswith(".sh"): + packages[package]["initscripts"].append(pth[12:]) - if os.path.exists(systemd) and pth.startswith(systemd_folder) and pth.endswith('.service') and \ - pth.find('.wants') == -1: + if ( + os.path.exists(systemd) + and pth.startswith(systemd_folder) + and pth.endswith(".service") + and pth.find(".wants") == -1 + ): is_oneshot = False try: - servicefile = salt.utils.files.fopen(pth) # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + servicefile = salt.utils.files.fopen(pth) + # pylint: enable=resource-leakage except IOError: continue sysfold_len = len(systemd_folder) for line in servicefile.readlines(): line = salt.utils.stringutils.to_unicode(line) - if line.find('Type=oneshot') > 0: + if line.find("Type=oneshot") > 0: # scripts that does a single job and then exit is_oneshot = True continue servicefile.close() if not is_oneshot: - packages[package]['systemdservice'].append(pth[sysfold_len:]) + packages[package]["systemdservice"].append(pth[sysfold_len:]) sys.stdout.flush() paths.stdout.close() @@ -585,14 +645,21 @@ def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs): # Alternatively, find init.d script or service that match the process name for package in packages: _check_timeout(start_time, timeout) - if not packages[package]['systemdservice'] and not packages[package]['initscripts']: - service = __salt__['service.available'](packages[package]['process_name']) + if ( + not packages[package]["systemdservice"] + and not packages[package]["initscripts"] + ): + service = __salt__["service.available"](packages[package]["process_name"]) if service: - if os.path.exists('/etc/init.d/' + packages[package]['process_name']): - packages[package]['initscripts'].append(packages[package]['process_name']) + if os.path.exists("/etc/init.d/" + packages[package]["process_name"]): + packages[package]["initscripts"].append( + packages[package]["process_name"] + ) else: - packages[package]['systemdservice'].append(packages[package]['process_name']) + packages[package]["systemdservice"].append( + packages[package]["process_name"] + ) restartable = [] nonrestartable = [] @@ -601,21 +668,32 @@ def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs): for package in packages: _check_timeout(start_time, timeout) - if packages[package]['initscripts']: + if packages[package]["initscripts"]: restartable.append(package) - restartinitcommands.extend(['service ' + s + ' restart' for s in packages[package]['initscripts']]) - elif packages[package]['systemdservice']: + restartinitcommands.extend( + ["service " + s + " restart" for s in packages[package]["initscripts"]] + ) + elif packages[package]["systemdservice"]: restartable.append(package) - restartservicecommands.extend(['systemctl restart ' + s for s in packages[package]['systemdservice']]) + restartservicecommands.extend( + ["systemctl restart " + s for s in packages[package]["systemdservice"]] + ) else: nonrestartable.append(package) - if packages[package]['process_name'] in restart_services: - restart_services.remove(packages[package]['process_name']) + if packages[package]["process_name"] in restart_services: + restart_services.remove(packages[package]["process_name"]) for restart_service in restart_services: _check_timeout(start_time, timeout) - restartservicecommands.extend(['systemctl restart ' + restart_service]) + restartservicecommands.extend(["systemctl restart " + restart_service]) - ret = _format_output(kernel_restart, packages, verbose, restartable, nonrestartable, - restartservicecommands, restartinitcommands) + ret = _format_output( + kernel_restart, + packages, + verbose, + restartable, + nonrestartable, + restartservicecommands, + restartinitcommands, + ) return ret diff --git a/salt/modules/ret.py b/salt/modules/ret.py index 6e425eb7864..132f39c3c19 100644 --- a/salt/modules/ret.py +++ b/salt/modules/ret.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Module to integrate with the returner system and retrieve data sent to a salt returner -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.loader def get_jid(returner, jid): - ''' + """ Return the information for a specified job id CLI Example: @@ -17,13 +17,13 @@ def get_jid(returner, jid): .. code-block:: bash salt '*' ret.get_jid redis 20421104181954700505 - ''' + """ returners = salt.loader.returners(__opts__, __salt__) - return returners['{0}.get_jid'.format(returner)](jid) + return returners["{0}.get_jid".format(returner)](jid) def get_fun(returner, fun): - ''' + """ Return info about last time fun was called on each minion CLI Example: @@ -31,13 +31,13 @@ def get_fun(returner, fun): .. code-block:: bash salt '*' ret.get_fun mysql network.interfaces - ''' + """ returners = salt.loader.returners(__opts__, __salt__) - return returners['{0}.get_fun'.format(returner)](fun) + return returners["{0}.get_fun".format(returner)](fun) def get_jids(returner): - ''' + """ Return a list of all job ids CLI Example: @@ -45,13 +45,13 @@ def get_jids(returner): .. code-block:: bash salt '*' ret.get_jids mysql - ''' + """ returners = salt.loader.returners(__opts__, __salt__) - return returners['{0}.get_jids'.format(returner)]() + return returners["{0}.get_jids".format(returner)]() def get_minions(returner): - ''' + """ Return a list of all minions CLI Example: @@ -59,6 +59,6 @@ def get_minions(returner): .. code-block:: bash salt '*' ret.get_minions mysql - ''' + """ returners = salt.loader.returners(__opts__, __salt__) - return returners['{0}.get_minions'.format(returner)]() + return returners["{0}.get_minions".format(returner)]() diff --git a/salt/modules/rh_ip.py b/salt/modules/rh_ip.py index 85b9d5701a1..bd31585f994 100644 --- a/salt/modules/rh_ip.py +++ b/salt/modules/rh_ip.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" The networking module for RHEL/Fedora based distros -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -import os.path import os +import os.path # Import third party libs import jinja2 @@ -27,89 +27,129 @@ log = logging.getLogger(__name__) # Set up template environment JINJA = jinja2.Environment( loader=jinja2.FileSystemLoader( - os.path.join(salt.utils.templates.TEMPLATE_DIRNAME, 'rh_ip') + os.path.join(salt.utils.templates.TEMPLATE_DIRNAME, "rh_ip") ) ) # Define the module's virtual name -__virtualname__ = 'ip' +__virtualname__ = "ip" def __virtual__(): - ''' + """ Confine this module to RHEL/Fedora based distros - ''' - if __grains__['os_family'] == 'RedHat': + """ + if __grains__["os_family"] == "RedHat": return __virtualname__ - return (False, 'The rh_ip execution module cannot be loaded: this module is only available on RHEL/Fedora based distributions.') + return ( + False, + "The rh_ip execution module cannot be loaded: this module is only available on RHEL/Fedora based distributions.", + ) # Setup networking attributes _ETHTOOL_CONFIG_OPTS = [ - 'autoneg', 'speed', 'duplex', - 'rx', 'tx', 'sg', 'tso', 'ufo', - 'gso', 'gro', 'lro', 'advertise' + "autoneg", + "speed", + "duplex", + "rx", + "tx", + "sg", + "tso", + "ufo", + "gso", + "gro", + "lro", + "advertise", ] _RH_CONFIG_OPTS = [ - 'domain', 'peerdns', 'peerntp', 'defroute', - 'mtu', 'static-routes', 'gateway', 'zone' + "domain", + "peerdns", + "peerntp", + "defroute", + "mtu", + "static-routes", + "gateway", + "zone", ] _RH_CONFIG_BONDING_OPTS = [ - 'mode', 'miimon', 'arp_interval', - 'arp_ip_target', 'downdelay', 'updelay', - 'use_carrier', 'lacp_rate', 'hashing-algorithm', - 'max_bonds', 'tx_queues', 'num_grat_arp', - 'num_unsol_na', 'primary', 'primary_reselect', - 'ad_select', 'xmit_hash_policy', 'arp_validate', - 'fail_over_mac', 'all_slaves_active', 'resend_igmp' + "mode", + "miimon", + "arp_interval", + "arp_ip_target", + "downdelay", + "updelay", + "use_carrier", + "lacp_rate", + "hashing-algorithm", + "max_bonds", + "tx_queues", + "num_grat_arp", + "num_unsol_na", + "primary", + "primary_reselect", + "ad_select", + "xmit_hash_policy", + "arp_validate", + "fail_over_mac", + "all_slaves_active", + "resend_igmp", ] -_RH_NETWORK_SCRIPT_DIR = '/etc/sysconfig/network-scripts' -_RH_NETWORK_FILE = '/etc/sysconfig/network' -_RH_NETWORK_CONF_FILES = '/etc/modprobe.d' -_CONFIG_TRUE = ['yes', 'on', 'true', '1', True] -_CONFIG_FALSE = ['no', 'off', 'false', '0', False] +_RH_NETWORK_SCRIPT_DIR = "/etc/sysconfig/network-scripts" +_RH_NETWORK_FILE = "/etc/sysconfig/network" +_RH_NETWORK_CONF_FILES = "/etc/modprobe.d" +_CONFIG_TRUE = ["yes", "on", "true", "1", True] +_CONFIG_FALSE = ["no", "off", "false", "0", False] _IFACE_TYPES = [ - 'eth', 'bond', 'alias', 'clone', - 'ipsec', 'dialup', 'bridge', 'slave', 'vlan', - 'ipip', 'ib', + "eth", + "bond", + "alias", + "clone", + "ipsec", + "dialup", + "bridge", + "slave", + "vlan", + "ipip", + "ib", ] def _error_msg_iface(iface, option, expected): - ''' + """ Build an appropriate error message from a given option and a list of expected values. - ''' - msg = 'Invalid option -- Interface: {0}, Option: {1}, Expected: [{2}]' - return msg.format(iface, option, '|'.join(str(e) for e in expected)) + """ + msg = "Invalid option -- Interface: {0}, Option: {1}, Expected: [{2}]" + return msg.format(iface, option, "|".join(str(e) for e in expected)) def _error_msg_routes(iface, option, expected): - ''' + """ Build an appropriate error message from a given option and a list of expected values. - ''' - msg = 'Invalid option -- Route interface: {0}, Option: {1}, Expected: [{2}]' + """ + msg = "Invalid option -- Route interface: {0}, Option: {1}, Expected: [{2}]" return msg.format(iface, option, expected) def _log_default_iface(iface, opt, value): - log.info('Using default option -- Interface: %s Option: %s Value: %s', - iface, opt, value) + log.info( + "Using default option -- Interface: %s Option: %s Value: %s", iface, opt, value + ) def _error_msg_network(option, expected): - ''' + """ Build an appropriate error message from a given option and a list of expected values. - ''' - msg = 'Invalid network setting -- Setting: {0}, Expected: [{1}]' - return msg.format(option, '|'.join(str(e) for e in expected)) + """ + msg = "Invalid network setting -- Setting: {0}, Expected: [{1}]" + return msg.format(option, "|".join(str(e) for e in expected)) def _log_default_network(opt, value): - log.info('Using existing setting -- Setting: %s Value: %s', - opt, value) + log.info("Using existing setting -- Setting: %s Value: %s", opt, value) def _parse_rh_config(path): @@ -118,9 +158,9 @@ def _parse_rh_config(path): if rh_config: for line in rh_config: line = line.strip() - if len(line) == 0 or line.startswith('!') or line.startswith('#'): + if len(line) == 0 or line.startswith("!") or line.startswith("#"): continue - pair = [p.rstrip() for p in line.split('=', 1)] + pair = [p.rstrip() for p in line.split("=", 1)] if len(pair) != 2: continue name, value = pair @@ -130,55 +170,68 @@ def _parse_rh_config(path): def _parse_ethtool_opts(opts, iface): - ''' + """ Filters given options and outputs valid settings for ETHTOOLS_OPTS If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ config = {} - if 'autoneg' in opts: - if opts['autoneg'] in _CONFIG_TRUE: - config.update({'autoneg': 'on'}) - elif opts['autoneg'] in _CONFIG_FALSE: - config.update({'autoneg': 'off'}) + if "autoneg" in opts: + if opts["autoneg"] in _CONFIG_TRUE: + config.update({"autoneg": "on"}) + elif opts["autoneg"] in _CONFIG_FALSE: + config.update({"autoneg": "off"}) else: - _raise_error_iface(iface, 'autoneg', _CONFIG_TRUE + _CONFIG_FALSE) + _raise_error_iface(iface, "autoneg", _CONFIG_TRUE + _CONFIG_FALSE) - if 'duplex' in opts: - valid = ['full', 'half'] - if opts['duplex'] in valid: - config.update({'duplex': opts['duplex']}) + if "duplex" in opts: + valid = ["full", "half"] + if opts["duplex"] in valid: + config.update({"duplex": opts["duplex"]}) else: - _raise_error_iface(iface, 'duplex', valid) + _raise_error_iface(iface, "duplex", valid) - if 'speed' in opts: - valid = ['10', '100', '1000', '10000'] - if six.text_type(opts['speed']) in valid: - config.update({'speed': opts['speed']}) + if "speed" in opts: + valid = ["10", "100", "1000", "10000"] + if six.text_type(opts["speed"]) in valid: + config.update({"speed": opts["speed"]}) else: - _raise_error_iface(iface, opts['speed'], valid) + _raise_error_iface(iface, opts["speed"], valid) - if 'advertise' in opts: + if "advertise" in opts: valid = [ - '0x001', '0x002', '0x004', '0x008', '0x010', '0x020', - '0x20000', '0x8000', '0x1000', '0x40000', '0x80000', - '0x200000', '0x400000', '0x800000', '0x1000000', - '0x2000000', '0x4000000' + "0x001", + "0x002", + "0x004", + "0x008", + "0x010", + "0x020", + "0x20000", + "0x8000", + "0x1000", + "0x40000", + "0x80000", + "0x200000", + "0x400000", + "0x800000", + "0x1000000", + "0x2000000", + "0x4000000", ] - if six.text_type(opts['advertise']) in valid: - config.update({'advertise': opts['advertise']}) + if six.text_type(opts["advertise"]) in valid: + config.update({"advertise": opts["advertise"]}) else: - _raise_error_iface(iface, 'advertise', valid) + _raise_error_iface(iface, "advertise", valid) valid = _CONFIG_TRUE + _CONFIG_FALSE - for option in ('rx', 'tx', 'sg', 'tso', 'ufo', 'gso', 'gro', 'lro'): + for option in ("rx", "tx", "sg", "tso", "ufo", "gso", "gro", "lro"): if option in opts: if opts[option] in _CONFIG_TRUE: - config.update({option: 'on'}) + config.update({option: "on"}) elif opts[option] in _CONFIG_FALSE: - config.update({option: 'off'}) + config.update({option: "off"}) else: _raise_error_iface(iface, option, valid) @@ -186,279 +239,273 @@ def _parse_ethtool_opts(opts, iface): def _parse_settings_bond(opts, iface): - ''' + """ Filters given options and outputs valid settings for requested operation. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ bond_def = { # 803.ad aggregation selection logic # 0 for stable (default) # 1 for bandwidth # 2 for count - 'ad_select': '0', + "ad_select": "0", # Max number of transmit queues (default = 16) - 'tx_queues': '16', + "tx_queues": "16", # Link monitoring in milliseconds. Most NICs support this - 'miimon': '100', + "miimon": "100", # ARP interval in milliseconds - 'arp_interval': '250', + "arp_interval": "250", # Delay before considering link down in milliseconds (miimon * 2) - 'downdelay': '200', + "downdelay": "200", # lacp_rate 0: Slow - every 30 seconds # lacp_rate 1: Fast - every 1 second - 'lacp_rate': '0', + "lacp_rate": "0", # Max bonds for this driver - 'max_bonds': '1', + "max_bonds": "1", # Specifies the time, in milliseconds, to wait before # enabling a slave after a link recovery has been # detected. Only used with miimon. - 'updelay': '0', + "updelay": "0", # Used with miimon. # On: driver sends mii # Off: ethtool sends mii - 'use_carrier': '0', + "use_carrier": "0", # Default. Don't change unless you know what you are doing. - 'xmit_hash_policy': 'layer2', + "xmit_hash_policy": "layer2", } - if opts['mode'] in ['balance-rr', '0']: - log.info( - 'Device: %s Bonding Mode: load balancing (round-robin)', - iface - ) + if opts["mode"] in ["balance-rr", "0"]: + log.info("Device: %s Bonding Mode: load balancing (round-robin)", iface) return _parse_settings_bond_0(opts, iface, bond_def) - elif opts['mode'] in ['active-backup', '1']: - log.info( - 'Device: %s Bonding Mode: fault-tolerance (active-backup)', - iface - ) + elif opts["mode"] in ["active-backup", "1"]: + log.info("Device: %s Bonding Mode: fault-tolerance (active-backup)", iface) return _parse_settings_bond_1(opts, iface, bond_def) - elif opts['mode'] in ['balance-xor', '2']: - log.info( - 'Device: %s Bonding Mode: load balancing (xor)', - iface - ) + elif opts["mode"] in ["balance-xor", "2"]: + log.info("Device: %s Bonding Mode: load balancing (xor)", iface) return _parse_settings_bond_2(opts, iface, bond_def) - elif opts['mode'] in ['broadcast', '3']: - log.info( - 'Device: %s Bonding Mode: fault-tolerance (broadcast)', - iface - ) + elif opts["mode"] in ["broadcast", "3"]: + log.info("Device: %s Bonding Mode: fault-tolerance (broadcast)", iface) return _parse_settings_bond_3(opts, iface, bond_def) - elif opts['mode'] in ['802.3ad', '4']: + elif opts["mode"] in ["802.3ad", "4"]: log.info( - 'Device: %s Bonding Mode: IEEE 802.3ad Dynamic link ' - 'aggregation', iface + "Device: %s Bonding Mode: IEEE 802.3ad Dynamic link " "aggregation", iface ) return _parse_settings_bond_4(opts, iface, bond_def) - elif opts['mode'] in ['balance-tlb', '5']: - log.info( - 'Device: %s Bonding Mode: transmit load balancing', iface - ) + elif opts["mode"] in ["balance-tlb", "5"]: + log.info("Device: %s Bonding Mode: transmit load balancing", iface) return _parse_settings_bond_5(opts, iface, bond_def) - elif opts['mode'] in ['balance-alb', '6']: - log.info( - 'Device: %s Bonding Mode: adaptive load balancing', iface - ) + elif opts["mode"] in ["balance-alb", "6"]: + log.info("Device: %s Bonding Mode: adaptive load balancing", iface) return _parse_settings_bond_6(opts, iface, bond_def) else: valid = [ - '0', '1', '2', '3', '4', '5', '6', - 'balance-rr', 'active-backup', 'balance-xor', - 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb' + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "balance-rr", + "active-backup", + "balance-xor", + "broadcast", + "802.3ad", + "balance-tlb", + "balance-alb", ] - _raise_error_iface(iface, 'mode', valid) + _raise_error_iface(iface, "mode", valid) def _parse_settings_bond_0(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond0. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ # balance-rr shares miimon settings with balance-xor bond = _parse_settings_bond_1(opts, iface, bond_def) - bond.update({'mode': '0'}) + bond.update({"mode": "0"}) # ARP targets in n.n.n.n form - valid = ['list of ips (up to 16)'] - if 'arp_ip_target' in opts: - if isinstance(opts['arp_ip_target'], list): - if 1 <= len(opts['arp_ip_target']) <= 16: - bond.update({'arp_ip_target': ''}) - for ip in opts['arp_ip_target']: # pylint: disable=C0103 - if len(bond['arp_ip_target']) > 0: - bond['arp_ip_target'] = bond['arp_ip_target'] + ',' + ip + valid = ["list of ips (up to 16)"] + if "arp_ip_target" in opts: + if isinstance(opts["arp_ip_target"], list): + if 1 <= len(opts["arp_ip_target"]) <= 16: + bond.update({"arp_ip_target": ""}) + for ip in opts["arp_ip_target"]: # pylint: disable=C0103 + if len(bond["arp_ip_target"]) > 0: + bond["arp_ip_target"] = bond["arp_ip_target"] + "," + ip else: - bond['arp_ip_target'] = ip + bond["arp_ip_target"] = ip else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) else: - _raise_error_iface(iface, 'arp_ip_target', valid) - elif 'miimon' not in opts: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) + elif "miimon" not in opts: + _raise_error_iface(iface, "arp_ip_target", valid) - if 'arp_interval' in opts: + if "arp_interval" in opts: try: - int(opts['arp_interval']) - bond.update({'arp_interval': opts['arp_interval']}) + int(opts["arp_interval"]) + bond.update({"arp_interval": opts["arp_interval"]}) except Exception: # pylint: disable=broad-except - _raise_error_iface(iface, 'arp_interval', ['integer']) + _raise_error_iface(iface, "arp_interval", ["integer"]) else: - _log_default_iface(iface, 'arp_interval', bond_def['arp_interval']) - bond.update({'arp_interval': bond_def['arp_interval']}) + _log_default_iface(iface, "arp_interval", bond_def["arp_interval"]) + bond.update({"arp_interval": bond_def["arp_interval"]}) return bond def _parse_settings_bond_1(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond1. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' - bond = {'mode': '1'} + """ + bond = {"mode": "1"} - for binding in ['miimon', 'downdelay', 'updelay']: + for binding in ["miimon", "downdelay", "updelay"]: if binding in opts: try: int(opts[binding]) bond.update({binding: opts[binding]}) except Exception: # pylint: disable=broad-except - _raise_error_iface(iface, binding, ['integer']) + _raise_error_iface(iface, binding, ["integer"]) else: _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) - if 'primary' in opts: - bond.update({'primary': opts['primary']}) + if "primary" in opts: + bond.update({"primary": opts["primary"]}) return bond def _parse_settings_bond_2(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond2. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ - bond = {'mode': '2'} + bond = {"mode": "2"} - valid = ['list of ips (up to 16)'] - if 'arp_ip_target' in opts: - if isinstance(opts['arp_ip_target'], list): - if 1 <= len(opts['arp_ip_target']) <= 16: - bond.update({'arp_ip_target': ''}) - for ip in opts['arp_ip_target']: # pylint: disable=C0103 - if len(bond['arp_ip_target']) > 0: - bond['arp_ip_target'] = bond['arp_ip_target'] + ',' + ip + valid = ["list of ips (up to 16)"] + if "arp_ip_target" in opts: + if isinstance(opts["arp_ip_target"], list): + if 1 <= len(opts["arp_ip_target"]) <= 16: + bond.update({"arp_ip_target": ""}) + for ip in opts["arp_ip_target"]: # pylint: disable=C0103 + if len(bond["arp_ip_target"]) > 0: + bond["arp_ip_target"] = bond["arp_ip_target"] + "," + ip else: - bond['arp_ip_target'] = ip + bond["arp_ip_target"] = ip else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) else: - _raise_error_iface(iface, 'arp_ip_target', valid) + _raise_error_iface(iface, "arp_ip_target", valid) - if 'arp_interval' in opts: + if "arp_interval" in opts: try: - int(opts['arp_interval']) - bond.update({'arp_interval': opts['arp_interval']}) + int(opts["arp_interval"]) + bond.update({"arp_interval": opts["arp_interval"]}) except Exception: # pylint: disable=broad-except - _raise_error_iface(iface, 'arp_interval', ['integer']) + _raise_error_iface(iface, "arp_interval", ["integer"]) else: - _log_default_iface(iface, 'arp_interval', bond_def['arp_interval']) - bond.update({'arp_interval': bond_def['arp_interval']}) + _log_default_iface(iface, "arp_interval", bond_def["arp_interval"]) + bond.update({"arp_interval": bond_def["arp_interval"]}) - if 'hashing-algorithm' in opts: - valid = ['layer2', 'layer2+3', 'layer3+4'] - if opts['hashing-algorithm'] in valid: - bond.update({'xmit_hash_policy': opts['hashing-algorithm']}) + if "hashing-algorithm" in opts: + valid = ["layer2", "layer2+3", "layer3+4"] + if opts["hashing-algorithm"] in valid: + bond.update({"xmit_hash_policy": opts["hashing-algorithm"]}) else: - _raise_error_iface(iface, 'hashing-algorithm', valid) + _raise_error_iface(iface, "hashing-algorithm", valid) return bond def _parse_settings_bond_3(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond3. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' - bond = {'mode': '3'} + """ + bond = {"mode": "3"} - for binding in ['miimon', 'downdelay', 'updelay']: + for binding in ["miimon", "downdelay", "updelay"]: if binding in opts: try: int(opts[binding]) bond.update({binding: opts[binding]}) except Exception: # pylint: disable=broad-except - _raise_error_iface(iface, binding, ['integer']) + _raise_error_iface(iface, binding, ["integer"]) else: _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) return bond def _parse_settings_bond_4(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond4. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' + """ - bond = {'mode': '4'} + bond = {"mode": "4"} - for binding in ['miimon', 'downdelay', 'updelay', 'lacp_rate', 'ad_select']: + for binding in ["miimon", "downdelay", "updelay", "lacp_rate", "ad_select"]: if binding in opts: - if binding == 'lacp_rate': - if opts[binding] == 'fast': - opts.update({binding: '1'}) - if opts[binding] == 'slow': - opts.update({binding: '0'}) - valid = ['fast', '1', 'slow', '0'] + if binding == "lacp_rate": + if opts[binding] == "fast": + opts.update({binding: "1"}) + if opts[binding] == "slow": + opts.update({binding: "0"}) + valid = ["fast", "1", "slow", "0"] else: - valid = ['integer'] + valid = ["integer"] try: int(opts[binding]) bond.update({binding: opts[binding]}) @@ -468,203 +515,205 @@ def _parse_settings_bond_4(opts, iface, bond_def): _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) - if 'hashing-algorithm' in opts: - valid = ['layer2', 'layer2+3', 'layer3+4'] - if opts['hashing-algorithm'] in valid: - bond.update({'xmit_hash_policy': opts['hashing-algorithm']}) + if "hashing-algorithm" in opts: + valid = ["layer2", "layer2+3", "layer3+4"] + if opts["hashing-algorithm"] in valid: + bond.update({"xmit_hash_policy": opts["hashing-algorithm"]}) else: - _raise_error_iface(iface, 'hashing-algorithm', valid) + _raise_error_iface(iface, "hashing-algorithm", valid) return bond def _parse_settings_bond_5(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond5. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' - bond = {'mode': '5'} + """ + bond = {"mode": "5"} - for binding in ['miimon', 'downdelay', 'updelay']: + for binding in ["miimon", "downdelay", "updelay"]: if binding in opts: try: int(opts[binding]) bond.update({binding: opts[binding]}) except Exception: # pylint: disable=broad-except - _raise_error_iface(iface, binding, ['integer']) + _raise_error_iface(iface, binding, ["integer"]) else: _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) - if 'primary' in opts: - bond.update({'primary': opts['primary']}) + if "primary" in opts: + bond.update({"primary": opts["primary"]}) return bond def _parse_settings_bond_6(opts, iface, bond_def): - ''' + """ Filters given options and outputs valid settings for bond6. If an option has a value that is not expected, this function will log what the Interface, Setting and what it was expecting. - ''' - bond = {'mode': '6'} + """ + bond = {"mode": "6"} - for binding in ['miimon', 'downdelay', 'updelay']: + for binding in ["miimon", "downdelay", "updelay"]: if binding in opts: try: int(opts[binding]) bond.update({binding: opts[binding]}) except Exception: # pylint: disable=broad-except - _raise_error_iface(iface, binding, ['integer']) + _raise_error_iface(iface, binding, ["integer"]) else: _log_default_iface(iface, binding, bond_def[binding]) bond.update({binding: bond_def[binding]}) - if 'use_carrier' in opts: - if opts['use_carrier'] in _CONFIG_TRUE: - bond.update({'use_carrier': '1'}) - elif opts['use_carrier'] in _CONFIG_FALSE: - bond.update({'use_carrier': '0'}) + if "use_carrier" in opts: + if opts["use_carrier"] in _CONFIG_TRUE: + bond.update({"use_carrier": "1"}) + elif opts["use_carrier"] in _CONFIG_FALSE: + bond.update({"use_carrier": "0"}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'use_carrier', valid) + _raise_error_iface(iface, "use_carrier", valid) else: - _log_default_iface(iface, 'use_carrier', bond_def['use_carrier']) - bond.update({'use_carrier': bond_def['use_carrier']}) + _log_default_iface(iface, "use_carrier", bond_def["use_carrier"]) + bond.update({"use_carrier": bond_def["use_carrier"]}) - if 'primary' in opts: - bond.update({'primary': opts['primary']}) + if "primary" in opts: + bond.update({"primary": opts["primary"]}) return bond def _parse_settings_vlan(opts, iface): - ''' + """ Filters given options and outputs valid settings for a vlan - ''' + """ vlan = {} - if 'reorder_hdr' in opts: - if opts['reorder_hdr'] in _CONFIG_TRUE + _CONFIG_FALSE: - vlan.update({'reorder_hdr': opts['reorder_hdr']}) + if "reorder_hdr" in opts: + if opts["reorder_hdr"] in _CONFIG_TRUE + _CONFIG_FALSE: + vlan.update({"reorder_hdr": opts["reorder_hdr"]}) else: valid = _CONFIG_TRUE + _CONFIG_FALSE - _raise_error_iface(iface, 'reorder_hdr', valid) + _raise_error_iface(iface, "reorder_hdr", valid) - if 'vlan_id' in opts: - if opts['vlan_id'] > 0: - vlan.update({'vlan_id': opts['vlan_id']}) + if "vlan_id" in opts: + if opts["vlan_id"] > 0: + vlan.update({"vlan_id": opts["vlan_id"]}) else: - _raise_error_iface(iface, 'vlan_id', 'Positive integer') + _raise_error_iface(iface, "vlan_id", "Positive integer") - if 'phys_dev' in opts: - if len(opts['phys_dev']) > 0: - vlan.update({'phys_dev': opts['phys_dev']}) + if "phys_dev" in opts: + if len(opts["phys_dev"]) > 0: + vlan.update({"phys_dev": opts["phys_dev"]}) else: - _raise_error_iface(iface, 'phys_dev', 'Non-empty string') + _raise_error_iface(iface, "phys_dev", "Non-empty string") return vlan def _parse_settings_eth(opts, iface_type, enabled, iface): - ''' + """ Filters given options and outputs valid settings for a network interface. - ''' - result = {'name': iface} - if 'proto' in opts: - valid = ['none', 'bootp', 'dhcp'] - if opts['proto'] in valid: - result['proto'] = opts['proto'] + """ + result = {"name": iface} + if "proto" in opts: + valid = ["none", "bootp", "dhcp"] + if opts["proto"] in valid: + result["proto"] = opts["proto"] else: - _raise_error_iface(iface, opts['proto'], valid) + _raise_error_iface(iface, opts["proto"], valid) - if 'dns' in opts: - result['dns'] = opts['dns'] - result['peerdns'] = 'yes' + if "dns" in opts: + result["dns"] = opts["dns"] + result["peerdns"] = "yes" - if 'mtu' in opts: + if "mtu" in opts: try: - result['mtu'] = int(opts['mtu']) + result["mtu"] = int(opts["mtu"]) except ValueError: - _raise_error_iface(iface, 'mtu', ['integer']) + _raise_error_iface(iface, "mtu", ["integer"]) - if iface_type not in ['bridge']: + if iface_type not in ["bridge"]: ethtool = _parse_ethtool_opts(opts, iface) if ethtool: - result['ethtool'] = ethtool + result["ethtool"] = ethtool - if iface_type == 'slave': - result['proto'] = 'none' + if iface_type == "slave": + result["proto"] = "none" - if iface_type == 'bond': + if iface_type == "bond": bonding = _parse_settings_bond(opts, iface) if bonding: - result['bonding'] = bonding - result['devtype'] = "Bond" + result["bonding"] = bonding + result["devtype"] = "Bond" - if iface_type == 'vlan': + if iface_type == "vlan": vlan = _parse_settings_vlan(opts, iface) if vlan: - result['devtype'] = "Vlan" + result["devtype"] = "Vlan" for opt in vlan: result[opt] = opts[opt] - if iface_type not in ['bond', 'vlan', 'bridge', 'ipip']: + if iface_type not in ["bond", "vlan", "bridge", "ipip"]: auto_addr = False - if 'addr' in opts: - if salt.utils.validate.net.mac(opts['addr']): - result['addr'] = opts['addr'] - elif opts['addr'] == 'auto': + if "addr" in opts: + if salt.utils.validate.net.mac(opts["addr"]): + result["addr"] = opts["addr"] + elif opts["addr"] == "auto": auto_addr = True - elif opts['addr'] != 'none': - _raise_error_iface(iface, opts['addr'], ['AA:BB:CC:DD:EE:FF', 'auto', 'none']) + elif opts["addr"] != "none": + _raise_error_iface( + iface, opts["addr"], ["AA:BB:CC:DD:EE:FF", "auto", "none"] + ) else: auto_addr = True if auto_addr: # If interface type is slave for bond, not setting hwaddr - if iface_type != 'slave': - ifaces = __salt__['network.interfaces']() - if iface in ifaces and 'hwaddr' in ifaces[iface]: - result['addr'] = ifaces[iface]['hwaddr'] - if iface_type == 'eth': - result['devtype'] = 'Ethernet' - if iface_type == 'bridge': - result['devtype'] = 'Bridge' + if iface_type != "slave": + ifaces = __salt__["network.interfaces"]() + if iface in ifaces and "hwaddr" in ifaces[iface]: + result["addr"] = ifaces[iface]["hwaddr"] + if iface_type == "eth": + result["devtype"] = "Ethernet" + if iface_type == "bridge": + result["devtype"] = "Bridge" bypassfirewall = True valid = _CONFIG_TRUE + _CONFIG_FALSE - for opt in ['bypassfirewall']: + for opt in ["bypassfirewall"]: if opt in opts: if opts[opt] in _CONFIG_TRUE: bypassfirewall = True @@ -674,10 +723,10 @@ def _parse_settings_eth(opts, iface_type, enabled, iface): _raise_error_iface(iface, opts[opt], valid) bridgectls = [ - 'net.bridge.bridge-nf-call-ip6tables', - 'net.bridge.bridge-nf-call-iptables', - 'net.bridge.bridge-nf-call-arptables', - ] + "net.bridge.bridge-nf-call-ip6tables", + "net.bridge.bridge-nf-call-iptables", + "net.bridge.bridge-nf-call-arptables", + ] if bypassfirewall: sysctl_value = 0 @@ -686,146 +735,171 @@ def _parse_settings_eth(opts, iface_type, enabled, iface): for sysctl in bridgectls: try: - __salt__['sysctl.persist'](sysctl, sysctl_value) + __salt__["sysctl.persist"](sysctl, sysctl_value) except CommandExecutionError: - log.warning('Failed to set sysctl: %s', sysctl) + log.warning("Failed to set sysctl: %s", sysctl) else: - if 'bridge' in opts: - result['bridge'] = opts['bridge'] + if "bridge" in opts: + result["bridge"] = opts["bridge"] - if iface_type == 'ipip': - result['devtype'] = 'IPIP' - for opt in ['my_inner_ipaddr', 'my_outer_ipaddr']: + if iface_type == "ipip": + result["devtype"] = "IPIP" + for opt in ["my_inner_ipaddr", "my_outer_ipaddr"]: if opt not in opts: - _raise_error_iface(iface, opts[opt], ['1.2.3.4']) + _raise_error_iface(iface, opts[opt], ["1.2.3.4"]) else: result[opt] = opts[opt] - if iface_type == 'ib': - result['devtype'] = 'InfiniBand' + if iface_type == "ib": + result["devtype"] = "InfiniBand" - if 'prefix' in opts: - if 'netmask' in opts: - msg = 'Cannot use prefix and netmask together' + if "prefix" in opts: + if "netmask" in opts: + msg = "Cannot use prefix and netmask together" log.error(msg) raise AttributeError(msg) - result['prefix'] = opts['prefix'] - elif 'netmask' in opts: - result['netmask'] = opts['netmask'] + result["prefix"] = opts["prefix"] + elif "netmask" in opts: + result["netmask"] = opts["netmask"] - for opt in ['ipaddr', 'master', 'srcaddr', 'delay', 'domain', 'gateway', 'uuid', 'nickname', 'zone']: + for opt in [ + "ipaddr", + "master", + "srcaddr", + "delay", + "domain", + "gateway", + "uuid", + "nickname", + "zone", + ]: if opt in opts: result[opt] = opts[opt] - for opt in ['ipv6addr', 'ipv6gateway']: + for opt in ["ipv6addr", "ipv6gateway"]: if opt in opts: result[opt] = opts[opt] - if 'ipaddrs' in opts: - result['ipaddrs'] = [] - for opt in opts['ipaddrs']: + if "ipaddrs" in opts: + result["ipaddrs"] = [] + for opt in opts["ipaddrs"]: if salt.utils.validate.net.ipv4_addr(opt): - ip, prefix = [i.strip() for i in opt.split('/')] - result['ipaddrs'].append({'ipaddr': ip, 'prefix': prefix}) + ip, prefix = [i.strip() for i in opt.split("/")] + result["ipaddrs"].append({"ipaddr": ip, "prefix": prefix}) else: - msg = 'ipv4 CIDR is invalid' + msg = "ipv4 CIDR is invalid" log.error(msg) raise AttributeError(msg) - if 'ipv6addrs' in opts: - for opt in opts['ipv6addrs']: + if "ipv6addrs" in opts: + for opt in opts["ipv6addrs"]: if not salt.utils.validate.net.ipv6_addr(opt): - msg = 'ipv6 CIDR is invalid' + msg = "ipv6 CIDR is invalid" log.error(msg) raise AttributeError(msg) - result['ipv6addrs'] = opts['ipv6addrs'] + result["ipv6addrs"] = opts["ipv6addrs"] - if 'enable_ipv6' in opts: - result['enable_ipv6'] = opts['enable_ipv6'] + if "enable_ipv6" in opts: + result["enable_ipv6"] = opts["enable_ipv6"] valid = _CONFIG_TRUE + _CONFIG_FALSE - for opt in ['onparent', 'peerdns', 'peerroutes', 'slave', 'vlan', 'defroute', 'stp', 'ipv6_peerdns', - 'ipv6_defroute', 'ipv6_peerroutes', 'ipv6_autoconf', 'ipv4_failure_fatal', 'dhcpv6c']: + for opt in [ + "onparent", + "peerdns", + "peerroutes", + "slave", + "vlan", + "defroute", + "stp", + "ipv6_peerdns", + "ipv6_defroute", + "ipv6_peerroutes", + "ipv6_autoconf", + "ipv4_failure_fatal", + "dhcpv6c", + ]: if opt in opts: if opts[opt] in _CONFIG_TRUE: - result[opt] = 'yes' + result[opt] = "yes" elif opts[opt] in _CONFIG_FALSE: - result[opt] = 'no' + result[opt] = "no" else: _raise_error_iface(iface, opts[opt], valid) - if 'onboot' in opts: + if "onboot" in opts: log.warning( - 'The \'onboot\' option is controlled by the \'enabled\' option. ' - 'Interface: %s Enabled: %s', iface, enabled + "The 'onboot' option is controlled by the 'enabled' option. " + "Interface: %s Enabled: %s", + iface, + enabled, ) if enabled: - result['onboot'] = 'yes' + result["onboot"] = "yes" else: - result['onboot'] = 'no' + result["onboot"] = "no" # If the interface is defined then we want to always take # control away from non-root users; unless the administrator # wants to allow non-root users to control the device. - if 'userctl' in opts: - if opts['userctl'] in _CONFIG_TRUE: - result['userctl'] = 'yes' - elif opts['userctl'] in _CONFIG_FALSE: - result['userctl'] = 'no' + if "userctl" in opts: + if opts["userctl"] in _CONFIG_TRUE: + result["userctl"] = "yes" + elif opts["userctl"] in _CONFIG_FALSE: + result["userctl"] = "no" else: - _raise_error_iface(iface, opts['userctl'], valid) + _raise_error_iface(iface, opts["userctl"], valid) else: - result['userctl'] = 'no' + result["userctl"] = "no" # This vlan is in opts, and should be only used in range interface # will affect jinja template for interface generating - if 'vlan' in opts: - if opts['vlan'] in _CONFIG_TRUE: - result['vlan'] = 'yes' - elif opts['vlan'] in _CONFIG_FALSE: - result['vlan'] = 'no' + if "vlan" in opts: + if opts["vlan"] in _CONFIG_TRUE: + result["vlan"] = "yes" + elif opts["vlan"] in _CONFIG_FALSE: + result["vlan"] = "no" else: - _raise_error_iface(iface, opts['vlan'], valid) + _raise_error_iface(iface, opts["vlan"], valid) - if 'arpcheck' in opts: - if opts['arpcheck'] in _CONFIG_FALSE: - result['arpcheck'] = 'no' + if "arpcheck" in opts: + if opts["arpcheck"] in _CONFIG_FALSE: + result["arpcheck"] = "no" - if 'ipaddr_start' in opts: - result['ipaddr_start'] = opts['ipaddr_start'] + if "ipaddr_start" in opts: + result["ipaddr_start"] = opts["ipaddr_start"] - if 'ipaddr_end' in opts: - result['ipaddr_end'] = opts['ipaddr_end'] + if "ipaddr_end" in opts: + result["ipaddr_end"] = opts["ipaddr_end"] - if 'clonenum_start' in opts: - result['clonenum_start'] = opts['clonenum_start'] + if "clonenum_start" in opts: + result["clonenum_start"] = opts["clonenum_start"] # If NetworkManager is available, we can control whether we use # it or not - if 'nm_controlled' in opts: - if opts['nm_controlled'] in _CONFIG_TRUE: - result['nm_controlled'] = 'yes' - elif opts['nm_controlled'] in _CONFIG_FALSE: - result['nm_controlled'] = 'no' + if "nm_controlled" in opts: + if opts["nm_controlled"] in _CONFIG_TRUE: + result["nm_controlled"] = "yes" + elif opts["nm_controlled"] in _CONFIG_FALSE: + result["nm_controlled"] = "no" else: - _raise_error_iface(iface, opts['nm_controlled'], valid) + _raise_error_iface(iface, opts["nm_controlled"], valid) else: - result['nm_controlled'] = 'no' + result["nm_controlled"] = "no" return result def _parse_routes(iface, opts): - ''' + """ Filters given options and outputs valid settings for the route settings file. - ''' + """ # Normalize keys opts = dict((k.lower(), v) for (k, v) in six.iteritems(opts)) result = {} - if 'routes' not in opts: - _raise_error_routes(iface, 'routes', 'List of routes') + if "routes" not in opts: + _raise_error_routes(iface, "routes", "List of routes") for opt in opts: result[opt] = opts[opt] @@ -834,111 +908,113 @@ def _parse_routes(iface, opts): def _parse_network_settings(opts, current): - ''' + """ Filters given options and outputs valid settings for the global network settings file. - ''' + """ # Normalize keys opts = dict((k.lower(), v) for (k, v) in six.iteritems(opts)) current = dict((k.lower(), v) for (k, v) in six.iteritems(current)) # Check for supported parameters - retain_settings = opts.get('retain_settings', False) + retain_settings = opts.get("retain_settings", False) result = current if retain_settings else {} # Default quote type is an empty string, which will not quote values - quote_type = '' + quote_type = "" valid = _CONFIG_TRUE + _CONFIG_FALSE - if 'enabled' not in opts: + if "enabled" not in opts: try: - opts['networking'] = current['networking'] + opts["networking"] = current["networking"] # If networking option is quoted, use its quote type - quote_type = salt.utils.stringutils.is_quoted(opts['networking']) - _log_default_network('networking', current['networking']) + quote_type = salt.utils.stringutils.is_quoted(opts["networking"]) + _log_default_network("networking", current["networking"]) except ValueError: - _raise_error_network('networking', valid) + _raise_error_network("networking", valid) else: - opts['networking'] = opts['enabled'] + opts["networking"] = opts["enabled"] - true_val = '{0}yes{0}'.format(quote_type) - false_val = '{0}no{0}'.format(quote_type) + true_val = "{0}yes{0}".format(quote_type) + false_val = "{0}no{0}".format(quote_type) - networking = salt.utils.stringutils.dequote(opts['networking']) + networking = salt.utils.stringutils.dequote(opts["networking"]) if networking in valid: if networking in _CONFIG_TRUE: - result['networking'] = true_val + result["networking"] = true_val elif networking in _CONFIG_FALSE: - result['networking'] = false_val + result["networking"] = false_val else: - _raise_error_network('networking', valid) + _raise_error_network("networking", valid) - if 'hostname' not in opts: + if "hostname" not in opts: try: - opts['hostname'] = current['hostname'] - _log_default_network('hostname', current['hostname']) + opts["hostname"] = current["hostname"] + _log_default_network("hostname", current["hostname"]) except Exception: # pylint: disable=broad-except - _raise_error_network('hostname', ['server1.example.com']) + _raise_error_network("hostname", ["server1.example.com"]) - if opts['hostname']: - result['hostname'] = '{1}{0}{1}'.format( - salt.utils.stringutils.dequote(opts['hostname']), quote_type) + if opts["hostname"]: + result["hostname"] = "{1}{0}{1}".format( + salt.utils.stringutils.dequote(opts["hostname"]), quote_type + ) else: - _raise_error_network('hostname', ['server1.example.com']) + _raise_error_network("hostname", ["server1.example.com"]) - if 'nozeroconf' in opts: - nozeroconf = salt.utils.stringutils.dequote(opts['nozeroconf']) + if "nozeroconf" in opts: + nozeroconf = salt.utils.stringutils.dequote(opts["nozeroconf"]) if nozeroconf in valid: if nozeroconf in _CONFIG_TRUE: - result['nozeroconf'] = true_val + result["nozeroconf"] = true_val elif nozeroconf in _CONFIG_FALSE: - result['nozeroconf'] = false_val + result["nozeroconf"] = false_val else: - _raise_error_network('nozeroconf', valid) + _raise_error_network("nozeroconf", valid) for opt in opts: - if opt not in ['networking', 'hostname', 'nozeroconf']: - result[opt] = '{1}{0}{1}'.format( - salt.utils.stringutils.dequote(opts[opt]), quote_type) + if opt not in ["networking", "hostname", "nozeroconf"]: + result[opt] = "{1}{0}{1}".format( + salt.utils.stringutils.dequote(opts[opt]), quote_type + ) return result def _raise_error_iface(iface, option, expected): - ''' + """ Log and raise an error with a logical formatted message. - ''' + """ msg = _error_msg_iface(iface, option, expected) log.error(msg) raise AttributeError(msg) def _raise_error_network(option, expected): - ''' + """ Log and raise an error with a logical formatted message. - ''' + """ msg = _error_msg_network(option, expected) log.error(msg) raise AttributeError(msg) def _raise_error_routes(iface, option, expected): - ''' + """ Log and raise an error with a logical formatted message. - ''' + """ msg = _error_msg_routes(iface, option, expected) log.error(msg) raise AttributeError(msg) def _read_file(path): - ''' + """ Reads and returns the contents of a file - ''' + """ try: - with salt.utils.files.fopen(path, 'rb') as rfh: + with salt.utils.files.fopen(path, "rb") as rfh: lines = salt.utils.stringutils.to_unicode(rfh.read()).splitlines() try: - lines.remove('') + lines.remove("") except ValueError: pass return lines @@ -947,38 +1023,38 @@ def _read_file(path): def _write_file_iface(iface, data, folder, pattern): - ''' + """ Writes a file to disk - ''' + """ filename = os.path.join(folder, pattern.format(iface)) if not os.path.exists(folder): - msg = '{0} cannot be written. {1} does not exist' + msg = "{0} cannot be written. {1} does not exist" msg = msg.format(filename, folder) log.error(msg) raise AttributeError(msg) - with salt.utils.files.fopen(filename, 'w') as fp_: + with salt.utils.files.fopen(filename, "w") as fp_: fp_.write(salt.utils.stringutils.to_str(data)) def _write_file_network(data, filename): - ''' + """ Writes a file to disk - ''' - with salt.utils.files.fopen(filename, 'w') as fp_: + """ + with salt.utils.files.fopen(filename, "w") as fp_: fp_.write(salt.utils.stringutils.to_str(data)) def _read_temp(data): lines = data.splitlines() try: # Discard newlines if they exist - lines.remove('') + lines.remove("") except ValueError: pass return lines def build_bond(iface, **settings): - ''' + """ Create a bond script in /etc/modprobe.d with the passed settings and load the bonding kernel module. @@ -987,38 +1063,38 @@ def build_bond(iface, **settings): .. code-block:: bash salt '*' ip.build_bond bond0 mode=balance-alb - ''' - rh_major = __grains__['osrelease'][:1] + """ + rh_major = __grains__["osrelease"][:1] opts = _parse_settings_bond(settings, iface) try: - template = JINJA.get_template('conf.jinja') + template = JINJA.get_template("conf.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template conf.jinja') - return '' - data = template.render({'name': iface, 'bonding': opts}) - _write_file_iface(iface, data, _RH_NETWORK_CONF_FILES, '{0}.conf'.format(iface)) - path = os.path.join(_RH_NETWORK_CONF_FILES, '{0}.conf'.format(iface)) - if rh_major == '5': - __salt__['cmd.run']( + log.error("Could not load template conf.jinja") + return "" + data = template.render({"name": iface, "bonding": opts}) + _write_file_iface(iface, data, _RH_NETWORK_CONF_FILES, "{0}.conf".format(iface)) + path = os.path.join(_RH_NETWORK_CONF_FILES, "{0}.conf".format(iface)) + if rh_major == "5": + __salt__["cmd.run"]( 'sed -i -e "/^alias\\s{0}.*/d" /etc/modprobe.conf'.format(iface), - python_shell=False + python_shell=False, ) - __salt__['cmd.run']( + __salt__["cmd.run"]( 'sed -i -e "/^options\\s{0}.*/d" /etc/modprobe.conf'.format(iface), - python_shell=False + python_shell=False, ) - __salt__['file.append']('/etc/modprobe.conf', path) - __salt__['kmod.load']('bonding') + __salt__["file.append"]("/etc/modprobe.conf", path) + __salt__["kmod.load"]("bonding") - if settings['test']: + if settings["test"]: return _read_temp(data) return _read_file(path) def build_interface(iface, iface_type, enabled, **settings): - ''' + """ Build an interface script for a network interface. CLI Example: @@ -1026,64 +1102,61 @@ def build_interface(iface, iface_type, enabled, **settings): .. code-block:: bash salt '*' ip.build_interface eth0 eth - ''' - if __grains__['os'] == 'Fedora': - if __grains__['osmajorrelease'] >= 18: - rh_major = '7' + """ + if __grains__["os"] == "Fedora": + if __grains__["osmajorrelease"] >= 18: + rh_major = "7" else: - rh_major = '6' - elif __grains__['os'] == 'Amazon': + rh_major = "6" + elif __grains__["os"] == "Amazon": # TODO: Is there a better formula for this? -W. Werner, 2019-05-30 # If not, it will need to be updated whenever Amazon releases # Amazon Linux 3 - if __grains__['osmajorrelease'] == 2: - rh_major = '7' + if __grains__["osmajorrelease"] == 2: + rh_major = "7" else: - rh_major = '6' + rh_major = "6" else: - rh_major = __grains__['osrelease'][:1] + rh_major = __grains__["osrelease"][:1] iface_type = iface_type.lower() if iface_type not in _IFACE_TYPES: _raise_error_iface(iface, iface_type, _IFACE_TYPES) - if iface_type == 'slave': - settings['slave'] = 'yes' - if 'master' not in settings: - msg = 'master is a required setting for slave interfaces' + if iface_type == "slave": + settings["slave"] = "yes" + if "master" not in settings: + msg = "master is a required setting for slave interfaces" log.error(msg) raise AttributeError(msg) - if iface_type == 'vlan': - settings['vlan'] = 'yes' + if iface_type == "vlan": + settings["vlan"] = "yes" - if iface_type == 'bridge': - __salt__['pkg.install']('bridge-utils') + if iface_type == "bridge": + __salt__["pkg.install"]("bridge-utils") - if iface_type in ['eth', 'bond', 'bridge', 'slave', 'vlan', 'ipip', 'ib', 'alias']: + if iface_type in ["eth", "bond", "bridge", "slave", "vlan", "ipip", "ib", "alias"]: opts = _parse_settings_eth(settings, iface_type, enabled, iface) try: - template = JINJA.get_template('rh{0}_eth.jinja'.format(rh_major)) + template = JINJA.get_template("rh{0}_eth.jinja".format(rh_major)) except jinja2.exceptions.TemplateNotFound: - log.error( - 'Could not load template rh%s_eth.jinja', - rh_major - ) - return '' + log.error("Could not load template rh%s_eth.jinja", rh_major) + return "" ifcfg = template.render(opts) - if 'test' in settings and settings['test']: + if "test" in settings and settings["test"]: return _read_temp(ifcfg) - _write_file_iface(iface, ifcfg, _RH_NETWORK_SCRIPT_DIR, 'ifcfg-{0}') - path = os.path.join(_RH_NETWORK_SCRIPT_DIR, 'ifcfg-{0}'.format(iface)) + _write_file_iface(iface, ifcfg, _RH_NETWORK_SCRIPT_DIR, "ifcfg-{0}") + path = os.path.join(_RH_NETWORK_SCRIPT_DIR, "ifcfg-{0}".format(iface)) return _read_file(path) def build_routes(iface, **settings): - ''' + """ Build a route script for a network interface. CLI Example: @@ -1091,30 +1164,27 @@ def build_routes(iface, **settings): .. code-block:: bash salt '*' ip.build_routes eth0 - ''' + """ - template = 'rh6_route_eth.jinja' + template = "rh6_route_eth.jinja" try: - if int(__grains__['osrelease'][0]) < 6: - template = 'route_eth.jinja' + if int(__grains__["osrelease"][0]) < 6: + template = "route_eth.jinja" except ValueError: pass - log.debug('Template name: %s', template) + log.debug("Template name: %s", template) opts = _parse_routes(iface, settings) log.debug("Opts: \n %s", opts) try: template = JINJA.get_template(template) except jinja2.exceptions.TemplateNotFound: - log.error( - 'Could not load template %s', - template - ) - return '' + log.error("Could not load template %s", template) + return "" opts6 = [] opts4 = [] - for route in opts['routes']: - ipaddr = route['ipaddr'] + for route in opts["routes"]: + ipaddr = route["ipaddr"] if salt.utils.validate.net.ipv6_addr(ipaddr): opts6.append(route) else: @@ -1125,16 +1195,16 @@ def build_routes(iface, **settings): routecfg = template.render(routes=opts4, iface=iface) routecfg6 = template.render(routes=opts6, iface=iface) - if settings['test']: + if settings["test"]: routes = _read_temp(routecfg) routes.extend(_read_temp(routecfg6)) return routes - _write_file_iface(iface, routecfg, _RH_NETWORK_SCRIPT_DIR, 'route-{0}') - _write_file_iface(iface, routecfg6, _RH_NETWORK_SCRIPT_DIR, 'route6-{0}') + _write_file_iface(iface, routecfg, _RH_NETWORK_SCRIPT_DIR, "route-{0}") + _write_file_iface(iface, routecfg6, _RH_NETWORK_SCRIPT_DIR, "route6-{0}") - path = os.path.join(_RH_NETWORK_SCRIPT_DIR, 'route-{0}'.format(iface)) - path6 = os.path.join(_RH_NETWORK_SCRIPT_DIR, 'route6-{0}'.format(iface)) + path = os.path.join(_RH_NETWORK_SCRIPT_DIR, "route-{0}".format(iface)) + path6 = os.path.join(_RH_NETWORK_SCRIPT_DIR, "route6-{0}".format(iface)) routes = _read_file(path) routes.extend(_read_file(path6)) @@ -1142,7 +1212,7 @@ def build_routes(iface, **settings): def down(iface, iface_type): - ''' + """ Shutdown a network interface CLI Example: @@ -1150,15 +1220,15 @@ def down(iface, iface_type): .. code-block:: bash salt '*' ip.down eth0 - ''' + """ # Slave devices are controlled by the master. - if iface_type not in ['slave']: - return __salt__['cmd.run']('ifdown {0}'.format(iface)) + if iface_type not in ["slave"]: + return __salt__["cmd.run"]("ifdown {0}".format(iface)) return None def get_bond(iface): - ''' + """ Return the content of a bond script CLI Example: @@ -1166,13 +1236,13 @@ def get_bond(iface): .. code-block:: bash salt '*' ip.get_bond bond0 - ''' - path = os.path.join(_RH_NETWORK_CONF_FILES, '{0}.conf'.format(iface)) + """ + path = os.path.join(_RH_NETWORK_CONF_FILES, "{0}.conf".format(iface)) return _read_file(path) def get_interface(iface): - ''' + """ Return the contents of an interface script CLI Example: @@ -1180,13 +1250,13 @@ def get_interface(iface): .. code-block:: bash salt '*' ip.get_interface eth0 - ''' - path = os.path.join(_RH_NETWORK_SCRIPT_DIR, 'ifcfg-{0}'.format(iface)) + """ + path = os.path.join(_RH_NETWORK_SCRIPT_DIR, "ifcfg-{0}".format(iface)) return _read_file(path) def up(iface, iface_type): # pylint: disable=C0103 - ''' + """ Start up a network interface CLI Example: @@ -1194,15 +1264,15 @@ def up(iface, iface_type): # pylint: disable=C0103 .. code-block:: bash salt '*' ip.up eth0 - ''' + """ # Slave devices are controlled by the master. - if iface_type not in ['slave']: - return __salt__['cmd.run']('ifup {0}'.format(iface)) + if iface_type not in ["slave"]: + return __salt__["cmd.run"]("ifup {0}".format(iface)) return None def get_routes(iface): - ''' + """ Return the contents of the interface routes script. CLI Example: @@ -1210,16 +1280,16 @@ def get_routes(iface): .. code-block:: bash salt '*' ip.get_routes eth0 - ''' - path = os.path.join(_RH_NETWORK_SCRIPT_DIR, 'route-{0}'.format(iface)) - path6 = os.path.join(_RH_NETWORK_SCRIPT_DIR, 'route6-{0}'.format(iface)) + """ + path = os.path.join(_RH_NETWORK_SCRIPT_DIR, "route-{0}".format(iface)) + path6 = os.path.join(_RH_NETWORK_SCRIPT_DIR, "route6-{0}".format(iface)) routes = _read_file(path) routes.extend(_read_file(path6)) return routes def get_network_settings(): - ''' + """ Return the contents of the global network script. CLI Example: @@ -1227,12 +1297,12 @@ def get_network_settings(): .. code-block:: bash salt '*' ip.get_network_settings - ''' + """ return _read_file(_RH_NETWORK_FILE) def apply_network_settings(**settings): - ''' + """ Apply global network configuration. CLI Example: @@ -1240,39 +1310,39 @@ def apply_network_settings(**settings): .. code-block:: bash salt '*' ip.apply_network_settings - ''' - if 'require_reboot' not in settings: - settings['require_reboot'] = False + """ + if "require_reboot" not in settings: + settings["require_reboot"] = False - if 'apply_hostname' not in settings: - settings['apply_hostname'] = False + if "apply_hostname" not in settings: + settings["apply_hostname"] = False hostname_res = True - if settings['apply_hostname'] in _CONFIG_TRUE: - if 'hostname' in settings: - hostname_res = __salt__['network.mod_hostname'](settings['hostname']) + if settings["apply_hostname"] in _CONFIG_TRUE: + if "hostname" in settings: + hostname_res = __salt__["network.mod_hostname"](settings["hostname"]) else: log.warning( - 'The network state sls is trying to apply hostname ' - 'changes but no hostname is defined.' + "The network state sls is trying to apply hostname " + "changes but no hostname is defined." ) hostname_res = False res = True - if settings['require_reboot'] in _CONFIG_TRUE: + if settings["require_reboot"] in _CONFIG_TRUE: log.warning( - 'The network state sls is requiring a reboot of the system to ' - 'properly apply network configuration.' + "The network state sls is requiring a reboot of the system to " + "properly apply network configuration." ) res = True else: - res = __salt__['service.restart']('network') + res = __salt__["service.restart"]("network") return hostname_res and res def build_network_settings(**settings): - ''' + """ Build the global network script. CLI Example: @@ -1280,20 +1350,20 @@ def build_network_settings(**settings): .. code-block:: bash salt '*' ip.build_network_settings - ''' + """ # Read current configuration and store default values current_network_settings = _parse_rh_config(_RH_NETWORK_FILE) # Build settings opts = _parse_network_settings(settings, current_network_settings) try: - template = JINJA.get_template('network.jinja') + template = JINJA.get_template("network.jinja") except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template network.jinja') - return '' + log.error("Could not load template network.jinja") + return "" network = template.render(opts) - if settings['test']: + if settings["test"]: return _read_temp(network) # Write settings diff --git a/salt/modules/rh_service.py b/salt/modules/rh_service.py index 7381a209351..21f4b8f0fc0 100644 --- a/salt/modules/rh_service.py +++ b/salt/modules/rh_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Service support for RHEL-based systems, including support for both upstart and sysvinit .. important:: @@ -7,16 +7,17 @@ Service support for RHEL-based systems, including support for both upstart and s minion, and it is using a different module (or gives an error similar to *'service.start' is not available*), see :ref:`here `. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import fnmatch # Import python libs import glob import logging import os -import stat -import fnmatch import re +import stat # Import salt libs import salt.utils.path @@ -24,113 +25,127 @@ from salt.ext import six log = logging.getLogger(__name__) -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" # Import upstart module if needed HAS_UPSTART = False -if salt.utils.path.which('initctl'): +if salt.utils.path.which("initctl"): try: # Don't re-invent the wheel, import the helper functions from the # upstart module. - from salt.modules.upstart_service import _upstart_enable, _upstart_disable, _upstart_is_enabled + from salt.modules.upstart_service import ( + _upstart_enable, + _upstart_disable, + _upstart_is_enabled, + ) except Exception as exc: # pylint: disable=broad-except - log.error('Unable to import helper functions from ' - 'salt.modules.upstart: %s', exc) + log.error( + "Unable to import helper functions from " "salt.modules.upstart: %s", exc + ) else: HAS_UPSTART = True def __virtual__(): - ''' + """ Only work on select distros which still use Red Hat's /usr/bin/service for management of either sysvinit or a hybrid sysvinit/upstart init system. - ''' + """ # Disable when booted with systemd - if __utils__['systemd.booted'](__context__): - return (False, 'The rh_service execution module failed to load: this system was booted with systemd.') + if __utils__["systemd.booted"](__context__): + return ( + False, + "The rh_service execution module failed to load: this system was booted with systemd.", + ) # Enable on these platforms only. - enable = set(( - 'XenServer', - 'XCP-ng', - 'RedHat', - 'CentOS', - 'ScientificLinux', - 'CloudLinux', - 'Amazon', - 'Fedora', - 'ALT', - 'OEL', - 'SUSE Enterprise Server', - 'SUSE', - 'McAfee OS Server', - 'VirtuozzoLinux' - )) - if __grains__['os'] in enable: + enable = set( + ( + "XenServer", + "XCP-ng", + "RedHat", + "CentOS", + "ScientificLinux", + "CloudLinux", + "Amazon", + "Fedora", + "ALT", + "OEL", + "SUSE Enterprise Server", + "SUSE", + "McAfee OS Server", + "VirtuozzoLinux", + ) + ) + if __grains__["os"] in enable: - if __grains__['os'] == 'SUSE': - if six.text_type(__grains__['osrelease']).startswith('11'): + if __grains__["os"] == "SUSE": + if six.text_type(__grains__["osrelease"]).startswith("11"): return __virtualname__ else: - return (False, 'Cannot load rh_service module on SUSE > 11') + return (False, "Cannot load rh_service module on SUSE > 11") - osrelease_major = __grains__.get('osrelease_info', [0])[0] + osrelease_major = __grains__.get("osrelease_info", [0])[0] - if __grains__['os'] in ('XenServer', 'XCP-ng'): + if __grains__["os"] in ("XenServer", "XCP-ng"): if osrelease_major >= 7: return ( False, - 'XenServer and XCP-ng >= 7 use systemd, will not load rh_service.py ' - 'as virtual \'service\'' + "XenServer and XCP-ng >= 7 use systemd, will not load rh_service.py " + "as virtual 'service'", ) return __virtualname__ - if __grains__['os'] == 'Fedora': + if __grains__["os"] == "Fedora": if osrelease_major >= 15: return ( False, - 'Fedora >= 15 uses systemd, will not load rh_service.py ' - 'as virtual \'service\'' + "Fedora >= 15 uses systemd, will not load rh_service.py " + "as virtual 'service'", ) - if __grains__['os'] in ('RedHat', 'CentOS', 'ScientificLinux', 'OEL', 'CloudLinux'): + if __grains__["os"] in ( + "RedHat", + "CentOS", + "ScientificLinux", + "OEL", + "CloudLinux", + ): if osrelease_major >= 7: return ( False, - 'RedHat-based distros >= version 7 use systemd, will not ' - 'load rh_service.py as virtual \'service\'' + "RedHat-based distros >= version 7 use systemd, will not " + "load rh_service.py as virtual 'service'", ) return __virtualname__ - return (False, 'Cannot load rh_service module: OS not in {0}'.format(enable)) + return (False, "Cannot load rh_service module: OS not in {0}".format(enable)) def _runlevel(): - ''' + """ Return the current runlevel - ''' - out = __salt__['cmd.run']('/sbin/runlevel') + """ + out = __salt__["cmd.run"]("/sbin/runlevel") # unknown will be returned while inside a kickstart environment, since # this is usually a server deployment it should be safe to assume runlevel # 3. If not all service related states will throw an out of range # exception here which will cause other functions to fail. - if 'unknown' in out: - return '3' + if "unknown" in out: + return "3" else: return out.split()[1] def _chkconfig_add(name): - ''' + """ Run 'chkconfig --add' for a service whose script is installed in /etc/init.d. The service is initially configured to be disabled at all run-levels. - ''' - cmd = '/sbin/chkconfig --add {0}'.format(name) - if __salt__['cmd.retcode'](cmd, python_shell=False) == 0: + """ + cmd = "/sbin/chkconfig --add {0}".format(name) + if __salt__["cmd.retcode"](cmd, python_shell=False) == 0: log.info('Added initscript "%s" to chkconfig', name) return True else: @@ -139,39 +154,40 @@ def _chkconfig_add(name): def _service_is_upstart(name): - ''' + """ Return True if the service is an upstart service, otherwise return False. - ''' - return HAS_UPSTART and os.path.exists('/etc/init/{0}.conf'.format(name)) + """ + return HAS_UPSTART and os.path.exists("/etc/init/{0}.conf".format(name)) def _service_is_sysv(name): - ''' + """ Return True if the service is a System V service (includes those managed by chkconfig); otherwise return False. - ''' + """ try: # Look for user-execute bit in file mode. - return bool(os.stat( - os.path.join('/etc/init.d', name)).st_mode & stat.S_IXUSR) + return bool(os.stat(os.path.join("/etc/init.d", name)).st_mode & stat.S_IXUSR) except OSError: return False def _service_is_chkconfig(name): - ''' + """ Return True if the service is managed by chkconfig. - ''' - cmdline = '/sbin/chkconfig --list {0}'.format(name) - return __salt__['cmd.retcode'](cmdline, python_shell=False, ignore_retcode=True) == 0 + """ + cmdline = "/sbin/chkconfig --list {0}".format(name) + return ( + __salt__["cmd.retcode"](cmdline, python_shell=False, ignore_retcode=True) == 0 + ) def _sysv_is_enabled(name, runlevel=None): - ''' + """ Return True if the sysv (or chkconfig) service is enabled for the specified runlevel; otherwise return False. If `runlevel` is None, then use the current runlevel. - ''' + """ # Try chkconfig first. result = _chkconfig_is_enabled(name, runlevel) if result: @@ -179,99 +195,99 @@ def _sysv_is_enabled(name, runlevel=None): if runlevel is None: runlevel = _runlevel() - return ( - len(glob.glob('/etc/rc.d/rc{0}.d/S??{1}'.format(runlevel, name))) > 0) + return len(glob.glob("/etc/rc.d/rc{0}.d/S??{1}".format(runlevel, name))) > 0 def _chkconfig_is_enabled(name, runlevel=None): - ''' + """ Return ``True`` if the service is enabled according to chkconfig; otherwise return ``False``. If ``runlevel`` is ``None``, then use the current runlevel. - ''' - cmdline = '/sbin/chkconfig --list {0}'.format(name) - result = __salt__['cmd.run_all'](cmdline, python_shell=False) + """ + cmdline = "/sbin/chkconfig --list {0}".format(name) + result = __salt__["cmd.run_all"](cmdline, python_shell=False) if runlevel is None: runlevel = _runlevel() - if result['retcode'] == 0: - for row in result['stdout'].splitlines(): - if '{0}:on'.format(runlevel) in row: + if result["retcode"] == 0: + for row in result["stdout"].splitlines(): + if "{0}:on".format(runlevel) in row: if row.split()[0] == name: return True - elif row.split() == [name, 'on']: + elif row.split() == [name, "on"]: return True return False def _sysv_enable(name): - ''' + """ Enable the named sysv service to start at boot. The service will be enabled using chkconfig with default run-levels if the service is chkconfig compatible. If chkconfig is not available, then this will fail. - ''' + """ if not _service_is_chkconfig(name) and not _chkconfig_add(name): return False - cmd = '/sbin/chkconfig {0} on'.format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = "/sbin/chkconfig {0} on".format(name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def _sysv_disable(name): - ''' + """ Disable the named sysv service from starting at boot. The service will be disabled using chkconfig with default run-levels if the service is chkconfig compatible; otherwise, the service will be disabled for the current run-level only. - ''' + """ if not _service_is_chkconfig(name) and not _chkconfig_add(name): return False - cmd = '/sbin/chkconfig {0} off'.format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = "/sbin/chkconfig {0} off".format(name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def _sysv_delete(name): - ''' + """ Delete the named sysv service from the system. The service will be deleted using chkconfig. - ''' + """ if not _service_is_chkconfig(name): return False - cmd = '/sbin/chkconfig --del {0}'.format(name) - return not __salt__['cmd.retcode'](cmd) + cmd = "/sbin/chkconfig --del {0}".format(name) + return not __salt__["cmd.retcode"](cmd) def _upstart_delete(name): - ''' + """ Delete an upstart service. This will only rename the .conf file - ''' + """ if HAS_UPSTART: - if os.path.exists('/etc/init/{0}.conf'.format(name)): - os.rename('/etc/init/{0}.conf'.format(name), - '/etc/init/{0}.conf.removed'.format(name)) + if os.path.exists("/etc/init/{0}.conf".format(name)): + os.rename( + "/etc/init/{0}.conf".format(name), + "/etc/init/{0}.conf.removed".format(name), + ) return True def _upstart_services(): - ''' + """ Return list of upstart services. - ''' + """ if HAS_UPSTART: - return [os.path.basename(name)[:-5] - for name in glob.glob('/etc/init/*.conf')] + return [os.path.basename(name)[:-5] for name in glob.glob("/etc/init/*.conf")] else: return [] def _sysv_services(): - ''' + """ Return list of sysv services. - ''' + """ _services = [] - output = __salt__['cmd.run'](['chkconfig', '--list'], python_shell=False) + output = __salt__["cmd.run"](["chkconfig", "--list"], python_shell=False) for line in output.splitlines(): comps = line.split() try: - if comps[1].startswith('0:'): + if comps[1].startswith("0:"): _services.append(comps[0]) except IndexError: continue @@ -279,8 +295,8 @@ def _sysv_services(): return [x for x in _services if _service_is_sysv(x)] -def get_enabled(limit=''): - ''' +def get_enabled(limit=""): + """ Return the enabled services. Use the ``limit`` param to restrict results to services of that type. @@ -291,26 +307,25 @@ def get_enabled(limit=''): salt '*' service.get_enabled salt '*' service.get_enabled limit=upstart salt '*' service.get_enabled limit=sysvinit - ''' + """ limit = limit.lower() - if limit == 'upstart': - return sorted(name for name in _upstart_services() - if _upstart_is_enabled(name)) - elif limit == 'sysvinit': + if limit == "upstart": + return sorted(name for name in _upstart_services() if _upstart_is_enabled(name)) + elif limit == "sysvinit": runlevel = _runlevel() - return sorted(name for name in _sysv_services() - if _sysv_is_enabled(name, runlevel)) + return sorted( + name for name in _sysv_services() if _sysv_is_enabled(name, runlevel) + ) else: runlevel = _runlevel() return sorted( - [name for name in _upstart_services() - if _upstart_is_enabled(name)] - + [name for name in _sysv_services() - if _sysv_is_enabled(name, runlevel)]) + [name for name in _upstart_services() if _upstart_is_enabled(name)] + + [name for name in _sysv_services() if _sysv_is_enabled(name, runlevel)] + ) -def get_disabled(limit=''): - ''' +def get_disabled(limit=""): + """ Return the disabled services. Use the ``limit`` param to restrict results to services of that type. @@ -321,26 +336,31 @@ def get_disabled(limit=''): salt '*' service.get_disabled salt '*' service.get_disabled limit=upstart salt '*' service.get_disabled limit=sysvinit - ''' + """ limit = limit.lower() - if limit == 'upstart': - return sorted(name for name in _upstart_services() - if not _upstart_is_enabled(name)) - elif limit == 'sysvinit': + if limit == "upstart": + return sorted( + name for name in _upstart_services() if not _upstart_is_enabled(name) + ) + elif limit == "sysvinit": runlevel = _runlevel() - return sorted(name for name in _sysv_services() - if not _sysv_is_enabled(name, runlevel)) + return sorted( + name for name in _sysv_services() if not _sysv_is_enabled(name, runlevel) + ) else: runlevel = _runlevel() return sorted( - [name for name in _upstart_services() - if not _upstart_is_enabled(name)] - + [name for name in _sysv_services() - if not _sysv_is_enabled(name, runlevel)]) + [name for name in _upstart_services() if not _upstart_is_enabled(name)] + + [ + name + for name in _sysv_services() + if not _sysv_is_enabled(name, runlevel) + ] + ) -def get_all(limit=''): - ''' +def get_all(limit=""): + """ Return all installed services. Use the ``limit`` param to restrict results to services of that type. @@ -351,18 +371,18 @@ def get_all(limit=''): salt '*' service.get_all salt '*' service.get_all limit=upstart salt '*' service.get_all limit=sysvinit - ''' + """ limit = limit.lower() - if limit == 'upstart': + if limit == "upstart": return sorted(_upstart_services()) - elif limit == 'sysvinit': + elif limit == "sysvinit": return sorted(_sysv_services()) else: return sorted(_sysv_services() + _upstart_services()) -def available(name, limit=''): - ''' +def available(name, limit=""): + """ Return True if the named service is available. Use the ``limit`` param to restrict results to services of that type. @@ -373,17 +393,21 @@ def available(name, limit=''): salt '*' service.available sshd salt '*' service.available sshd limit=upstart salt '*' service.available sshd limit=sysvinit - ''' - if limit == 'upstart': + """ + if limit == "upstart": return _service_is_upstart(name) - elif limit == 'sysvinit': + elif limit == "sysvinit": return _service_is_sysv(name) else: - return _service_is_upstart(name) or _service_is_sysv(name) or _service_is_chkconfig(name) + return ( + _service_is_upstart(name) + or _service_is_sysv(name) + or _service_is_chkconfig(name) + ) -def missing(name, limit=''): - ''' +def missing(name, limit=""): + """ The inverse of service.available. Return True if the named service is not available. Use the ``limit`` param to restrict results to services of that type. @@ -395,10 +419,10 @@ def missing(name, limit=''): salt '*' service.missing sshd salt '*' service.missing sshd limit=upstart salt '*' service.missing sshd limit=sysvinit - ''' - if limit == 'upstart': + """ + if limit == "upstart": return not _service_is_upstart(name) - elif limit == 'sysvinit': + elif limit == "sysvinit": return not _service_is_sysv(name) else: if _service_is_upstart(name) or _service_is_sysv(name): @@ -408,7 +432,7 @@ def missing(name, limit=''): def start(name): - ''' + """ Start the specified service CLI Example: @@ -416,16 +440,16 @@ def start(name): .. code-block:: bash salt '*' service.start - ''' + """ if _service_is_upstart(name): - cmd = 'start {0}'.format(name) + cmd = "start {0}".format(name) else: - cmd = '/sbin/service {0} start'.format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = "/sbin/service {0} start".format(name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def stop(name): - ''' + """ Stop the specified service CLI Example: @@ -433,16 +457,16 @@ def stop(name): .. code-block:: bash salt '*' service.stop - ''' + """ if _service_is_upstart(name): - cmd = 'stop {0}'.format(name) + cmd = "stop {0}".format(name) else: - cmd = '/sbin/service {0} stop'.format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = "/sbin/service {0} stop".format(name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def restart(name): - ''' + """ Restart the named service CLI Example: @@ -450,16 +474,16 @@ def restart(name): .. code-block:: bash salt '*' service.restart - ''' + """ if _service_is_upstart(name): - cmd = 'restart {0}'.format(name) + cmd = "restart {0}".format(name) else: - cmd = '/sbin/service {0} restart'.format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = "/sbin/service {0} restart".format(name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def reload_(name): - ''' + """ Reload the named service CLI Example: @@ -467,16 +491,16 @@ def reload_(name): .. code-block:: bash salt '*' service.reload - ''' + """ if _service_is_upstart(name): - cmd = 'reload {0}'.format(name) + cmd = "reload {0}".format(name) else: - cmd = '/sbin/service {0} reload'.format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = "/sbin/service {0} reload".format(name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def status(name, sig=None): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -497,11 +521,11 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status [service signature] - ''' + """ if sig: - return bool(__salt__['status.pid'](sig)) + return bool(__salt__["status.pid"](sig)) - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: @@ -509,18 +533,23 @@ def status(name, sig=None): results = {} for service in services: if _service_is_upstart(service): - cmd = 'status {0}'.format(service) - results[service] = 'start/running' in __salt__['cmd.run'](cmd, python_shell=False) + cmd = "status {0}".format(service) + results[service] = "start/running" in __salt__["cmd.run"]( + cmd, python_shell=False + ) else: - cmd = '/sbin/service {0} status'.format(service) - results[service] = __salt__['cmd.retcode'](cmd, python_shell=False, ignore_retcode=True) == 0 + cmd = "/sbin/service {0} status".format(service) + results[service] = ( + __salt__["cmd.retcode"](cmd, python_shell=False, ignore_retcode=True) + == 0 + ) if contains_globbing: return results return results[name] def delete(name, **kwargs): - ''' + """ Delete the named service .. versionadded:: 2016.3 @@ -530,7 +559,7 @@ def delete(name, **kwargs): .. code-block:: bash salt '*' service.delete - ''' + """ if _service_is_upstart(name): return _upstart_delete(name) else: @@ -538,7 +567,7 @@ def delete(name, **kwargs): def enable(name, **kwargs): - ''' + """ Enable the named service to start at boot CLI Example: @@ -546,7 +575,7 @@ def enable(name, **kwargs): .. code-block:: bash salt '*' service.enable - ''' + """ if _service_is_upstart(name): return _upstart_enable(name) else: @@ -554,7 +583,7 @@ def enable(name, **kwargs): def disable(name, **kwargs): - ''' + """ Disable the named service to start at boot CLI Example: @@ -562,7 +591,7 @@ def disable(name, **kwargs): .. code-block:: bash salt '*' service.disable - ''' + """ if _service_is_upstart(name): return _upstart_disable(name) else: @@ -570,7 +599,7 @@ def disable(name, **kwargs): def enabled(name, **kwargs): - ''' + """ Check to see if the named service is enabled to start on boot CLI Example: @@ -578,7 +607,7 @@ def enabled(name, **kwargs): .. code-block:: bash salt '*' service.enabled - ''' + """ if _service_is_upstart(name): return _upstart_is_enabled(name) else: @@ -586,7 +615,7 @@ def enabled(name, **kwargs): def disabled(name): - ''' + """ Check to see if the named service is disabled to start on boot CLI Example: @@ -594,7 +623,7 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled - ''' + """ if _service_is_upstart(name): return not _upstart_is_enabled(name) else: diff --git a/salt/modules/riak.py b/salt/modules/riak.py index 75b9d6f7c2a..e006fa96969 100644 --- a/salt/modules/riak.py +++ b/salt/modules/riak.py @@ -1,33 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Riak Salt Module -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.utils.path def __virtual__(): - ''' + """ Only available on systems with Riak installed. - ''' - if salt.utils.path.which('riak'): + """ + if salt.utils.path.which("riak"): return True - return (False, 'The riak execution module failed to load: the riak binary is not in the path.') - - -def __execute_cmd(name, cmd): - ''' - Execute Riak commands - ''' - return __salt__['cmd.run_all']( - '{0} {1}'.format(salt.utils.path.which(name), cmd) + return ( + False, + "The riak execution module failed to load: the riak binary is not in the path.", ) +def __execute_cmd(name, cmd): + """ + Execute Riak commands + """ + return __salt__["cmd.run_all"]("{0} {1}".format(salt.utils.path.which(name), cmd)) + + def start(): - ''' + """ Start Riak CLI Example: @@ -35,22 +36,22 @@ def start(): .. code-block:: bash salt '*' riak.start - ''' - ret = {'comment': '', 'success': False} + """ + ret = {"comment": "", "success": False} - cmd = __execute_cmd('riak', 'start') + cmd = __execute_cmd("riak", "start") - if cmd['retcode'] != 0: - ret['comment'] = cmd['stderr'] + if cmd["retcode"] != 0: + ret["comment"] = cmd["stderr"] else: - ret['comment'] = cmd['stdout'] - ret['success'] = True + ret["comment"] = cmd["stdout"] + ret["success"] = True return ret def stop(): - ''' + """ Stop Riak .. versionchanged:: 2015.8.0 @@ -60,22 +61,22 @@ def stop(): .. code-block:: bash salt '*' riak.stop - ''' - ret = {'comment': '', 'success': False} + """ + ret = {"comment": "", "success": False} - cmd = __execute_cmd('riak', 'stop') + cmd = __execute_cmd("riak", "stop") - if cmd['retcode'] != 0: - ret['comment'] = cmd['stderr'] + if cmd["retcode"] != 0: + ret["comment"] = cmd["stderr"] else: - ret['comment'] = cmd['stdout'] - ret['success'] = True + ret["comment"] = cmd["stdout"] + ret["success"] = True return ret def cluster_join(username, hostname): - ''' + """ Join a Riak cluster .. versionchanged:: 2015.8.0 @@ -88,24 +89,22 @@ def cluster_join(username, hostname): username - The riak username to join the cluster hostname - The riak hostname you are connecting to - ''' - ret = {'comment': '', 'success': False} + """ + ret = {"comment": "", "success": False} - cmd = __execute_cmd( - 'riak-admin', 'cluster join {0}@{1}'.format(username, hostname) - ) + cmd = __execute_cmd("riak-admin", "cluster join {0}@{1}".format(username, hostname)) - if cmd['retcode'] != 0: - ret['comment'] = cmd['stdout'] + if cmd["retcode"] != 0: + ret["comment"] = cmd["stdout"] else: - ret['comment'] = cmd['stdout'] - ret['success'] = True + ret["comment"] = cmd["stdout"] + ret["success"] = True return ret def cluster_leave(username, hostname): - ''' + """ Leave a Riak cluster .. versionadded:: 2015.8.0 @@ -118,24 +117,24 @@ def cluster_leave(username, hostname): username - The riak username to join the cluster hostname - The riak hostname you are connecting to - ''' - ret = {'comment': '', 'success': False} + """ + ret = {"comment": "", "success": False} cmd = __execute_cmd( - 'riak-admin', 'cluster leave {0}@{1}'.format(username, hostname) + "riak-admin", "cluster leave {0}@{1}".format(username, hostname) ) - if cmd['retcode'] != 0: - ret['comment'] = cmd['stdout'] + if cmd["retcode"] != 0: + ret["comment"] = cmd["stdout"] else: - ret['comment'] = cmd['stdout'] - ret['success'] = True + ret["comment"] = cmd["stdout"] + ret["success"] = True return ret def cluster_plan(): - ''' + """ Review Cluster Plan .. versionchanged:: 2015.8.0 @@ -145,17 +144,17 @@ def cluster_plan(): .. code-block:: bash salt '*' riak.cluster_plan - ''' - cmd = __execute_cmd('riak-admin', 'cluster plan') + """ + cmd = __execute_cmd("riak-admin", "cluster plan") - if cmd['retcode'] != 0: + if cmd["retcode"] != 0: return False return True def cluster_commit(): - ''' + """ Commit Cluster Changes .. versionchanged:: 2015.8.0 @@ -165,22 +164,22 @@ def cluster_commit(): .. code-block:: bash salt '*' riak.cluster_commit - ''' - ret = {'comment': '', 'success': False} + """ + ret = {"comment": "", "success": False} - cmd = __execute_cmd('riak-admin', 'cluster commit') + cmd = __execute_cmd("riak-admin", "cluster commit") - if cmd['retcode'] != 0: - ret['comment'] = cmd['stdout'] + if cmd["retcode"] != 0: + ret["comment"] = cmd["stdout"] else: - ret['comment'] = cmd['stdout'] - ret['success'] = True + ret["comment"] = cmd["stdout"] + ret["success"] = True return ret def member_status(): - ''' + """ Get cluster member status .. versionchanged:: 2015.8.0 @@ -190,41 +189,38 @@ def member_status(): .. code-block:: bash salt '*' riak.member_status - ''' - ret = {'membership': {}, - 'summary': {'Valid': 0, - 'Leaving': 0, - 'Exiting': 0, - 'Joining': 0, - 'Down': 0, - }} + """ + ret = { + "membership": {}, + "summary": {"Valid": 0, "Leaving": 0, "Exiting": 0, "Joining": 0, "Down": 0}, + } - out = __execute_cmd('riak-admin', 'member-status')['stdout'].splitlines() + out = __execute_cmd("riak-admin", "member-status")["stdout"].splitlines() for line in out: - if line.startswith(('=', '-', 'Status')): + if line.startswith(("=", "-", "Status")): continue - if '/' in line: + if "/" in line: # We're in the summary line - for item in line.split('/'): - key, val = item.split(':') - ret['summary'][key.strip()] = val.strip() + for item in line.split("/"): + key, val = item.split(":") + ret["summary"][key.strip()] = val.strip() if len(line.split()) == 4: # We're on a node status line (status, ring, pending, node) = line.split() - ret['membership'][node] = { - 'Status': status, - 'Ring': ring, - 'Pending': pending + ret["membership"][node] = { + "Status": status, + "Ring": ring, + "Pending": pending, } return ret def status(): - ''' + """ Current node status .. versionadded:: 2015.8.0 @@ -234,21 +230,21 @@ def status(): .. code-block:: bash salt '*' riak.status - ''' + """ ret = {} - cmd = __execute_cmd('riak-admin', 'status') + cmd = __execute_cmd("riak-admin", "status") - for i in cmd['stdout'].splitlines(): - if ':' in i: - (name, val) = i.split(':', 1) + for i in cmd["stdout"].splitlines(): + if ":" in i: + (name, val) = i.split(":", 1) ret[name.strip()] = val.strip() return ret def test(): - ''' + """ Runs a test of a few standard Riak operations .. versionadded:: 2015.8.0 @@ -258,22 +254,22 @@ def test(): .. code-block:: bash salt '*' riak.test - ''' - ret = {'comment': '', 'success': False} + """ + ret = {"comment": "", "success": False} - cmd = __execute_cmd('riak-admin', 'test') + cmd = __execute_cmd("riak-admin", "test") - if cmd['retcode'] != 0: - ret['comment'] = cmd['stdout'] + if cmd["retcode"] != 0: + ret["comment"] = cmd["stdout"] else: - ret['comment'] = cmd['stdout'] - ret['success'] = True + ret["comment"] = cmd["stdout"] + ret["success"] = True return ret def services(): - ''' + """ List available services on a node .. versionadded:: 2015.8.0 @@ -283,7 +279,7 @@ def services(): .. code-block:: bash salt '*' riak.services - ''' - cmd = __execute_cmd('riak-admin', 'services') + """ + cmd = __execute_cmd("riak-admin", "services") - return cmd['stdout'][1:-1].split(',') + return cmd["stdout"][1:-1].split(",") diff --git a/salt/modules/rpm_lowpkg.py b/salt/modules/rpm_lowpkg.py index 439404ae904..abebbbdab9f 100644 --- a/salt/modules/rpm_lowpkg.py +++ b/salt/modules/rpm_lowpkg.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Support for rpm -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime import logging import os import re -import datetime -from salt.utils.versions import LooseVersion # Import Salt libs import salt.utils.decorators.path @@ -17,53 +17,67 @@ import salt.utils.itertools import salt.utils.path import salt.utils.pkg.rpm import salt.utils.versions + +# pylint: enable=import-error,redefined-builtin +from salt.exceptions import CommandExecutionError, SaltInvocationError +from salt.ext import six + # pylint: disable=import-error,redefined-builtin from salt.ext.six.moves import zip -from salt.ext import six +from salt.utils.versions import LooseVersion try: import rpm + HAS_RPM = True except ImportError: HAS_RPM = False try: import rpmUtils.miscutils + HAS_RPMUTILS = True except ImportError: HAS_RPMUTILS = False -# pylint: enable=import-error,redefined-builtin -from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'lowpkg' +__virtualname__ = "lowpkg" def __virtual__(): - ''' + """ Confine this module to rpm based systems - ''' - if not salt.utils.path.which('rpm'): - return (False, 'The rpm execution module failed to load: rpm binary is not in the path.') + """ + if not salt.utils.path.which("rpm"): + return ( + False, + "The rpm execution module failed to load: rpm binary is not in the path.", + ) try: - os_grain = __grains__['os'].lower() - os_family = __grains__['os_family'].lower() + os_grain = __grains__["os"].lower() + os_family = __grains__["os_family"].lower() except Exception: # pylint: disable=broad-except - return (False, 'The rpm execution module failed to load: failed to detect os or os_family grains.') + return ( + False, + "The rpm execution module failed to load: failed to detect os or os_family grains.", + ) - enabled = ('amazon', 'xcp', 'xenserver', 'VirtuozzoLinux') + enabled = ("amazon", "xcp", "xenserver", "VirtuozzoLinux") - if os_family in ['redhat', 'suse'] or os_grain in enabled: + if os_family in ["redhat", "suse"] or os_grain in enabled: return __virtualname__ - return (False, 'The rpm execution module failed to load: only available on redhat/suse type systems ' - 'or amazon, xcp or xenserver.') + return ( + False, + "The rpm execution module failed to load: only available on redhat/suse type systems " + "or amazon, xcp or xenserver.", + ) -def bin_pkg_info(path, saltenv='base'): - ''' +def bin_pkg_info(path, saltenv="base"): + """ .. versionadded:: 2015.8.0 Parses RPM metadata and returns a dictionary of information about the @@ -85,40 +99,32 @@ def bin_pkg_info(path, saltenv='base'): salt '*' lowpkg.bin_pkg_info /root/salt-2015.5.1-2.el7.noarch.rpm salt '*' lowpkg.bin_pkg_info salt://salt-2015.5.1-2.el7.noarch.rpm - ''' + """ # If the path is a valid protocol, pull it down using cp.cache_file - if __salt__['config.valid_fileproto'](path): - newpath = __salt__['cp.cache_file'](path, saltenv) + if __salt__["config.valid_fileproto"](path): + newpath = __salt__["cp.cache_file"](path, saltenv) if not newpath: raise CommandExecutionError( - 'Unable to retrieve {0} from saltenv \'{1}\'' - .format(path, saltenv) + "Unable to retrieve {0} from saltenv '{1}'".format(path, saltenv) ) path = newpath else: if not os.path.exists(path): - raise CommandExecutionError( - '{0} does not exist on minion'.format(path) - ) + raise CommandExecutionError("{0} does not exist on minion".format(path)) elif not os.path.isabs(path): - raise SaltInvocationError( - '{0} does not exist on minion'.format(path) - ) + raise SaltInvocationError("{0} does not exist on minion".format(path)) # REPOID is not a valid tag for the rpm command. Remove it and replace it # with 'none' - queryformat = salt.utils.pkg.rpm.QUERYFORMAT.replace('%{REPOID}', 'none') - output = __salt__['cmd.run_stdout']( - ['rpm', '-qp', '--queryformat', queryformat, path], - output_loglevel='trace', + queryformat = salt.utils.pkg.rpm.QUERYFORMAT.replace("%{REPOID}", "none") + output = __salt__["cmd.run_stdout"]( + ["rpm", "-qp", "--queryformat", queryformat, path], + output_loglevel="trace", ignore_retcode=True, - python_shell=False + python_shell=False, ) ret = {} - pkginfo = salt.utils.pkg.rpm.parse_pkginfo( - output, - osarch=__grains__['osarch'] - ) + pkginfo = salt.utils.pkg.rpm.parse_pkginfo(output, osarch=__grains__["osarch"]) try: for field in pkginfo._fields: ret[field] = getattr(pkginfo, field) @@ -129,7 +135,7 @@ def bin_pkg_info(path, saltenv='base'): def list_pkgs(*packages): - ''' + """ List the packages currently installed in a dict:: {'': ''} @@ -139,15 +145,14 @@ def list_pkgs(*packages): .. code-block:: bash salt '*' lowpkg.list_pkgs - ''' + """ pkgs = {} - cmd = ['rpm', '-q' if packages else '-qa', - '--queryformat', r'%{NAME} %{VERSION}\n'] + cmd = ["rpm", "-q" if packages else "-qa", "--queryformat", r"%{NAME} %{VERSION}\n"] if packages: cmd.extend(packages) - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): - if 'is not installed' in line: + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): + if "is not installed" in line: continue comps = line.split() pkgs[comps[0]] = comps[1] @@ -155,7 +160,7 @@ def list_pkgs(*packages): def verify(*packages, **kwargs): - ''' + """ Runs an rpm -Va on a system, and returns the results in a dict Files with an attribute of config, doc, ghost, license or readme in the @@ -169,90 +174,87 @@ def verify(*packages, **kwargs): salt '*' lowpkg.verify httpd salt '*' lowpkg.verify httpd postfix salt '*' lowpkg.verify httpd postfix ignore_types=['config','doc'] - ''' - ftypes = {'c': 'config', - 'd': 'doc', - 'g': 'ghost', - 'l': 'license', - 'r': 'readme'} + """ + ftypes = {"c": "config", "d": "doc", "g": "ghost", "l": "license", "r": "readme"} ret = {} - ignore_types = kwargs.get('ignore_types', []) + ignore_types = kwargs.get("ignore_types", []) if not isinstance(ignore_types, (list, six.string_types)): raise SaltInvocationError( - 'ignore_types must be a list or a comma-separated string' + "ignore_types must be a list or a comma-separated string" ) if isinstance(ignore_types, six.string_types): try: - ignore_types = [x.strip() for x in ignore_types.split(',')] + ignore_types = [x.strip() for x in ignore_types.split(",")] except AttributeError: - ignore_types = [x.strip() for x in six.text_type(ignore_types).split(',')] + ignore_types = [x.strip() for x in six.text_type(ignore_types).split(",")] - verify_options = kwargs.get('verify_options', []) + verify_options = kwargs.get("verify_options", []) if not isinstance(verify_options, (list, six.string_types)): raise SaltInvocationError( - 'verify_options must be a list or a comma-separated string' + "verify_options must be a list or a comma-separated string" ) if isinstance(verify_options, six.string_types): try: - verify_options = [x.strip() for x in verify_options.split(',')] + verify_options = [x.strip() for x in verify_options.split(",")] except AttributeError: - verify_options = [x.strip() for x in six.text_type(verify_options).split(',')] + verify_options = [ + x.strip() for x in six.text_type(verify_options).split(",") + ] - cmd = ['rpm'] - cmd.extend(['--' + x for x in verify_options]) + cmd = ["rpm"] + cmd.extend(["--" + x for x in verify_options]) if packages: - cmd.append('-V') + cmd.append("-V") # Can't concatenate a tuple, must do a list.extend() cmd.extend(packages) else: - cmd.append('-Va') - out = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - ignore_retcode=True, - python_shell=False) + cmd.append("-Va") + out = __salt__["cmd.run_all"]( + cmd, output_loglevel="trace", ignore_retcode=True, python_shell=False + ) - if not out['stdout'].strip() and out['retcode'] != 0: + if not out["stdout"].strip() and out["retcode"] != 0: # If there is no stdout and the retcode is 0, then verification # succeeded, but if the retcode is nonzero, then the command failed. - msg = 'Failed to verify package(s)' - if out['stderr']: - msg += ': {0}'.format(out['stderr']) + msg = "Failed to verify package(s)" + if out["stderr"]: + msg += ": {0}".format(out["stderr"]) raise CommandExecutionError(msg) - for line in salt.utils.itertools.split(out['stdout'], '\n'): - fdict = {'mismatch': []} - if 'missing' in line: - line = ' ' + line - fdict['missing'] = True - del fdict['mismatch'] + for line in salt.utils.itertools.split(out["stdout"], "\n"): + fdict = {"mismatch": []} + if "missing" in line: + line = " " + line + fdict["missing"] = True + del fdict["mismatch"] fname = line[13:] if line[11:12] in ftypes: - fdict['type'] = ftypes[line[11:12]] - if 'type' not in fdict or fdict['type'] not in ignore_types: - if line[0:1] == 'S': - fdict['mismatch'].append('size') - if line[1:2] == 'M': - fdict['mismatch'].append('mode') - if line[2:3] == '5': - fdict['mismatch'].append('md5sum') - if line[3:4] == 'D': - fdict['mismatch'].append('device major/minor number') - if line[4:5] == 'L': - fdict['mismatch'].append('readlink path') - if line[5:6] == 'U': - fdict['mismatch'].append('user') - if line[6:7] == 'G': - fdict['mismatch'].append('group') - if line[7:8] == 'T': - fdict['mismatch'].append('mtime') - if line[8:9] == 'P': - fdict['mismatch'].append('capabilities') + fdict["type"] = ftypes[line[11:12]] + if "type" not in fdict or fdict["type"] not in ignore_types: + if line[0:1] == "S": + fdict["mismatch"].append("size") + if line[1:2] == "M": + fdict["mismatch"].append("mode") + if line[2:3] == "5": + fdict["mismatch"].append("md5sum") + if line[3:4] == "D": + fdict["mismatch"].append("device major/minor number") + if line[4:5] == "L": + fdict["mismatch"].append("readlink path") + if line[5:6] == "U": + fdict["mismatch"].append("user") + if line[6:7] == "G": + fdict["mismatch"].append("group") + if line[7:8] == "T": + fdict["mismatch"].append("mtime") + if line[8:9] == "P": + fdict["mismatch"].append("capabilities") ret[fname] = fdict return ret def modified(*packages, **flags): - ''' + """ List the modified files that belong to a package. Not specifying any packages will return a list of _all_ modified files on the system's RPM database. @@ -265,43 +267,51 @@ def modified(*packages, **flags): salt '*' lowpkg.modified httpd salt '*' lowpkg.modified httpd postfix salt '*' lowpkg.modified - ''' - ret = __salt__['cmd.run_all']( - ['rpm', '-Va'] + list(packages), - output_loglevel='trace', - python_shell=False) + """ + ret = __salt__["cmd.run_all"]( + ["rpm", "-Va"] + list(packages), output_loglevel="trace", python_shell=False + ) data = {} # If verification has an output, then it means it failed # and the return code will be 1. We are interested in any bigger # than 1 code. - if ret['retcode'] > 1: - del ret['stdout'] + if ret["retcode"] > 1: + del ret["stdout"] return ret - elif not ret['retcode']: + elif not ret["retcode"]: return data ptrn = re.compile(r"\s+") changes = cfg = f_name = None - for f_info in salt.utils.itertools.split(ret['stdout'], '\n'): + for f_info in salt.utils.itertools.split(ret["stdout"], "\n"): f_info = ptrn.split(f_info) if len(f_info) == 3: # Config file changes, cfg, f_name = f_info else: changes, f_name = f_info cfg = None - keys = ['size', 'mode', 'checksum', 'device', 'symlink', - 'owner', 'group', 'time', 'capabilities'] + keys = [ + "size", + "mode", + "checksum", + "device", + "symlink", + "owner", + "group", + "time", + "capabilities", + ] changes = list(changes) if len(changes) == 8: # Older RPMs do not support capabilities - changes.append('.') + changes.append(".") stats = [] for k, v in zip(keys, changes): - if v != '.': + if v != ".": stats.append(k) if cfg is not None: - stats.append('config') + stats.append("config") data[f_name] = stats if not flags: @@ -314,8 +324,7 @@ def modified(*packages, **flags): for param, pval in flags.items(): if param.startswith("_"): continue - if (not pval and param in stats) or \ - (pval and param not in stats): + if (not pval and param in stats) or (pval and param not in stats): include = False break if include: @@ -325,7 +334,7 @@ def modified(*packages, **flags): def file_list(*packages): - ''' + """ List the files that belong to a package. Not specifying any packages will return a list of _every_ file on the system's rpm database (not generally recommended). @@ -337,22 +346,21 @@ def file_list(*packages): salt '*' lowpkg.file_list httpd salt '*' lowpkg.file_list httpd postfix salt '*' lowpkg.file_list - ''' + """ if not packages: - cmd = ['rpm', '-qla'] + cmd = ["rpm", "-qla"] else: - cmd = ['rpm', '-ql'] + cmd = ["rpm", "-ql"] # Can't concatenate a tuple, must do a list.extend() cmd.extend(packages) - ret = __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False).splitlines() - return {'errors': [], 'files': ret} + ret = __salt__["cmd.run"]( + cmd, output_loglevel="trace", python_shell=False + ).splitlines() + return {"errors": [], "files": ret} def file_dict(*packages): - ''' + """ List the files that belong to a package, sorted by group. Not specifying any packages will return a list of _every_ file on the system's rpm database (not generally recommended). @@ -364,34 +372,32 @@ def file_dict(*packages): salt '*' lowpkg.file_dict httpd salt '*' lowpkg.file_dict httpd postfix salt '*' lowpkg.file_dict - ''' + """ errors = [] ret = {} pkgs = {} - cmd = ['rpm', '-q' if packages else '-qa', - '--queryformat', r'%{NAME} %{VERSION}\n'] + cmd = ["rpm", "-q" if packages else "-qa", "--queryformat", r"%{NAME} %{VERSION}\n"] if packages: cmd.extend(packages) - out = __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): - if 'is not installed' in line: + out = __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) + for line in salt.utils.itertools.split(out, "\n"): + if "is not installed" in line: errors.append(line) continue comps = line.split() - pkgs[comps[0]] = {'version': comps[1]} + pkgs[comps[0]] = {"version": comps[1]} for pkg in pkgs: files = [] - cmd = ['rpm', '-ql', pkg] - out = __salt__['cmd.run']( - ['rpm', '-ql', pkg], - output_loglevel='trace', - python_shell=False) + cmd = ["rpm", "-ql", pkg] + out = __salt__["cmd.run"]( + ["rpm", "-ql", pkg], output_loglevel="trace", python_shell=False + ) ret[pkg] = out.splitlines() - return {'errors': errors, 'packages': ret} + return {"errors": errors, "packages": ret} def owner(*paths): - ''' + """ Return the name of the package that owns the file. Multiple file paths can be passed. If a single path is passed, a string will be returned, and if multiple paths are passed, a dictionary of file/package name pairs @@ -406,27 +412,27 @@ def owner(*paths): salt '*' lowpkg.owner /usr/bin/apachectl salt '*' lowpkg.owner /usr/bin/apachectl /etc/httpd/conf/httpd.conf - ''' + """ if not paths: - return '' + return "" ret = {} for path in paths: - cmd = ['rpm', '-qf', '--queryformat', '%{name}', path] - ret[path] = __salt__['cmd.run_stdout'](cmd, - output_loglevel='trace', - python_shell=False) - if 'not owned' in ret[path].lower(): - ret[path] = '' + cmd = ["rpm", "-qf", "--queryformat", "%{name}", path] + ret[path] = __salt__["cmd.run_stdout"]( + cmd, output_loglevel="trace", python_shell=False + ) + if "not owned" in ret[path].lower(): + ret[path] = "" if len(ret) == 1: return list(ret.values())[0] return ret -@salt.utils.decorators.path.which('rpm2cpio') -@salt.utils.decorators.path.which('cpio') -@salt.utils.decorators.path.which('diff') +@salt.utils.decorators.path.which("rpm2cpio") +@salt.utils.decorators.path.which("cpio") +@salt.utils.decorators.path.which("diff") def diff(package_path, path): - ''' + """ Return a formatted diff between current file and original in a package. NOTE: this function includes all files (configuration and not), but does not work on binary content. @@ -440,22 +446,22 @@ def diff(package_path, path): .. code-block:: bash salt '*' lowpkg.diff /path/to/apache2.rpm /etc/apache2/httpd.conf - ''' + """ - cmd = "rpm2cpio {0} " \ - "| cpio -i --quiet --to-stdout .{1} " \ - "| diff -u --label 'A {1}' --from-file=- --label 'B {1}' {1}" - res = __salt__['cmd.shell'](cmd.format(package_path, path), - output_loglevel='trace') - if res and res.startswith('Binary file'): - return 'File \'{0}\' is binary and its content has been ' \ - 'modified.'.format(path) + cmd = ( + "rpm2cpio {0} " + "| cpio -i --quiet --to-stdout .{1} " + "| diff -u --label 'A {1}' --from-file=- --label 'B {1}' {1}" + ) + res = __salt__["cmd.shell"](cmd.format(package_path, path), output_loglevel="trace") + if res and res.startswith("Binary file"): + return "File '{0}' is binary and its content has been " "modified.".format(path) return res def info(*packages, **kwargs): - ''' + """ Return a detailed package(s) summary information. If no packages specified, all packages will be returned. @@ -481,19 +487,19 @@ def info(*packages, **kwargs): salt '*' lowpkg.info apache2 bash attr=version salt '*' lowpkg.info apache2 bash attr=version,build_date_iso,size salt '*' lowpkg.info apache2 bash attr=version,build_date_iso,size all_versions=True - ''' - all_versions = kwargs.get('all_versions', False) + """ + all_versions = kwargs.get("all_versions", False) # LONGSIZE is not a valid tag for all versions of rpm. If LONGSIZE isn't # available, then we can just use SIZE for older versions. See Issue #31366. - rpm_tags = __salt__['cmd.run_stdout']( - ['rpm', '--querytags'], - python_shell=False).splitlines() - if 'LONGSIZE' in rpm_tags: - size_tag = '%{LONGSIZE}' + rpm_tags = __salt__["cmd.run_stdout"]( + ["rpm", "--querytags"], python_shell=False + ).splitlines() + if "LONGSIZE" in rpm_tags: + size_tag = "%{LONGSIZE}" else: - size_tag = '%{SIZE}' + size_tag = "%{SIZE}" - cmd = packages and "rpm -q {0}".format(' '.join(packages)) or "rpm -qa" + cmd = packages and "rpm -q {0}".format(" ".join(packages)) or "rpm -qa" # Construct query format attr_map = { @@ -514,7 +520,7 @@ def info(*packages, **kwargs): "arch": "arch: %{ARCH}\\n", "license": "%|LICENSE?{license: %{LICENSE}\\n}|", "signature": "signature: %|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:" - "{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|\\n", + "{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|\\n", "packager": "%|PACKAGER?{packager: %{PACKAGER}\\n}|", "url": "%|URL?{url: %{URL}\\n}|", "summary": "summary: %{SUMMARY}\\n", @@ -522,39 +528,43 @@ def info(*packages, **kwargs): "edition": "edition: %|EPOCH?{%{EPOCH}:}|%{VERSION}-%{RELEASE}\\n", } - attr = kwargs.get('attr', None) and kwargs['attr'].split(",") or None + attr = kwargs.get("attr", None) and kwargs["attr"].split(",") or None query = list() if attr: for attr_k in attr: - if attr_k in attr_map and attr_k != 'description': + if attr_k in attr_map and attr_k != "description": query.append(attr_map[attr_k]) if not query: - raise CommandExecutionError('No valid attributes found.') - if 'name' not in attr: - attr.append('name') - query.append(attr_map['name']) - if 'edition' not in attr: - attr.append('edition') - query.append(attr_map['edition']) + raise CommandExecutionError("No valid attributes found.") + if "name" not in attr: + attr.append("name") + query.append(attr_map["name"]) + if "edition" not in attr: + attr.append("edition") + query.append(attr_map["edition"]) else: for attr_k, attr_v in six.iteritems(attr_map): - if attr_k != 'description': + if attr_k != "description": query.append(attr_v) - if attr and 'description' in attr or not attr: - query.append(attr_map['description']) + if attr and "description" in attr or not attr: + query.append(attr_map["description"]) query.append("-----\\n") - call = __salt__['cmd.run_all'](cmd + (" --queryformat '{0}'".format(''.join(query))), - output_loglevel='trace', env={'TZ': 'UTC'}, clean_env=True) - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += (call['stderr'] or call['stdout']) + call = __salt__["cmd.run_all"]( + cmd + (" --queryformat '{0}'".format("".join(query))), + output_loglevel="trace", + env={"TZ": "UTC"}, + clean_env=True, + ) + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] or call["stdout"] raise CommandExecutionError(comment) - elif 'error' in call['stderr']: - raise CommandExecutionError(call['stderr']) + elif "error" in call["stderr"]: + raise CommandExecutionError(call["stderr"]) else: - out = call['stdout'] + out = call["stdout"] _ret = list() for pkg_info in re.split(r"----*", out): @@ -562,7 +572,7 @@ def info(*packages, **kwargs): if not pkg_info: continue pkg_info = pkg_info.split(os.linesep) - if pkg_info[-1].lower().startswith('distribution'): + if pkg_info[-1].lower().startswith("distribution"): pkg_info = pkg_info[:-1] pkg_data = dict() @@ -573,54 +583,56 @@ def info(*packages, **kwargs): if descr_marker: descr.append(line) continue - line = [item.strip() for item in line.split(':', 1)] + line = [item.strip() for item in line.split(":", 1)] if len(line) != 2: continue key, value = line - if key == 'description': + if key == "description": descr_marker = True continue - if key == 'name': + if key == "name": pkg_name = value # Convert Unix ticks into ISO time format - if key in ['build_date', 'install_date']: + if key in ["build_date", "install_date"]: try: - pkg_data[key] = datetime.datetime.utcfromtimestamp(int(value)).isoformat() + "Z" + pkg_data[key] = ( + datetime.datetime.utcfromtimestamp(int(value)).isoformat() + "Z" + ) except ValueError: log.warning('Could not convert "%s" into Unix time', value) continue # Convert Unix ticks into an Integer - if key in ['build_date_time_t', 'install_date_time_t']: + if key in ["build_date_time_t", "install_date_time_t"]: try: pkg_data[key] = int(value) except ValueError: log.warning('Could not convert "%s" into Unix time', value) continue - if key not in ['description', 'name'] and value: + if key not in ["description", "name"] and value: pkg_data[key] = value - if attr and 'description' in attr or not attr: - pkg_data['description'] = os.linesep.join(descr) + if attr and "description" in attr or not attr: + pkg_data["description"] = os.linesep.join(descr) if pkg_name: - pkg_data['name'] = pkg_name + pkg_data["name"] = pkg_name _ret.append(pkg_data) # Force-sort package data by version, # pick only latest versions # (in case multiple packages installed, e.g. kernel) ret = dict() - for pkg_data in reversed(sorted(_ret, key=lambda x: LooseVersion(x['edition']))): - pkg_name = pkg_data.pop('name') + for pkg_data in reversed(sorted(_ret, key=lambda x: LooseVersion(x["edition"]))): + pkg_name = pkg_data.pop("name") # Filter out GPG public keys packages - if pkg_name.startswith('gpg-pubkey'): + if pkg_name.startswith("gpg-pubkey"): continue if pkg_name not in ret: if all_versions: ret[pkg_name] = [pkg_data.copy()] else: ret[pkg_name] = pkg_data.copy() - del ret[pkg_name]['edition'] + del ret[pkg_name]["edition"] elif all_versions: ret[pkg_name].append(pkg_data.copy()) @@ -628,7 +640,7 @@ def info(*packages, **kwargs): def version_cmp(ver1, ver2, ignore_epoch=False): - ''' + """ .. versionadded:: 2015.8.9 Do a cmp-style comparison on two packages. Return -1 if ver1 < ver2, 0 if @@ -645,10 +657,12 @@ def version_cmp(ver1, ver2, ignore_epoch=False): .. code-block:: bash salt '*' pkg.version_cmp '0.2-001' '0.2.0.1-002' - ''' - normalize = lambda x: six.text_type(x).split(':', 1)[-1] \ - if ignore_epoch \ + """ + normalize = ( + lambda x: six.text_type(x).split(":", 1)[-1] + if ignore_epoch else six.text_type(x) + ) ver1 = normalize(ver1) ver2 = normalize(ver2) @@ -661,25 +675,25 @@ def version_cmp(ver1, ver2, ignore_epoch=False): # Catches corner case where someone has a module named "rpm" in # their pythonpath. log.debug( - 'rpm module imported, but it does not have the ' - 'labelCompare function. Not using rpm.labelCompare for ' - 'version comparison.' + "rpm module imported, but it does not have the " + "labelCompare function. Not using rpm.labelCompare for " + "version comparison." ) if cmp_func is None and HAS_RPMUTILS: try: cmp_func = rpmUtils.miscutils.compareEVR except AttributeError: - log.debug('rpmUtils.miscutils.compareEVR is not available') + log.debug("rpmUtils.miscutils.compareEVR is not available") if cmp_func is None: - if salt.utils.path.which('rpmdev-vercmp'): + if salt.utils.path.which("rpmdev-vercmp"): # rpmdev-vercmp always uses epochs, even when zero def _ensure_epoch(ver): def _prepend(ver): - return '0:{0}'.format(ver) + return "0:{0}".format(ver) try: - if ':' not in ver: + if ":" not in ver: return _prepend(ver) except TypeError: return _prepend(ver) @@ -687,32 +701,34 @@ def version_cmp(ver1, ver2, ignore_epoch=False): ver1 = _ensure_epoch(ver1) ver2 = _ensure_epoch(ver2) - result = __salt__['cmd.run_all']( - ['rpmdev-vercmp', ver1, ver2], + result = __salt__["cmd.run_all"]( + ["rpmdev-vercmp", ver1, ver2], python_shell=False, redirect_stderr=True, - ignore_retcode=True) + ignore_retcode=True, + ) # rpmdev-vercmp returns 0 on equal, 11 on greater-than, and # 12 on less-than. - if result['retcode'] == 0: + if result["retcode"] == 0: return 0 - elif result['retcode'] == 11: + elif result["retcode"] == 11: return 1 - elif result['retcode'] == 12: + elif result["retcode"] == 12: return -1 else: # We'll need to fall back to salt.utils.versions.version_cmp() log.warning( - 'Failed to interpret results of rpmdev-vercmp output. ' - 'This is probably a bug, and should be reported. ' - 'Return code was %s. Output: %s', - result['retcode'], result['stdout'] + "Failed to interpret results of rpmdev-vercmp output. " + "This is probably a bug, and should be reported. " + "Return code was %s. Output: %s", + result["retcode"], + result["stdout"], ) else: # We'll need to fall back to salt.utils.versions.version_cmp() log.warning( - 'rpmdevtools is not installed, please install it for ' - 'more accurate version comparisons' + "rpmdevtools is not installed, please install it for " + "more accurate version comparisons" ) else: # If one EVR is missing a release but not the other and they @@ -722,20 +738,18 @@ def version_cmp(ver1, ver2, ignore_epoch=False): (ver1_e, ver1_v, ver1_r) = salt.utils.pkg.rpm.version_to_evr(ver1) (ver2_e, ver2_v, ver2_r) = salt.utils.pkg.rpm.version_to_evr(ver2) if not ver1_r or not ver2_r: - ver1_r = ver2_r = '' + ver1_r = ver2_r = "" - cmp_result = cmp_func((ver1_e, ver1_v, ver1_r), - (ver2_e, ver2_v, ver2_r)) + cmp_result = cmp_func((ver1_e, ver1_v, ver1_r), (ver2_e, ver2_v, ver2_r)) if cmp_result not in (-1, 0, 1): raise CommandExecutionError( - 'Comparison result \'{0}\' is invalid'.format(cmp_result) + "Comparison result '{0}' is invalid".format(cmp_result) ) return cmp_result except Exception as exc: # pylint: disable=broad-except log.warning( - 'Failed to compare version \'%s\' to \'%s\' using RPM: %s', - ver1, ver2, exc + "Failed to compare version '%s' to '%s' using RPM: %s", ver1, ver2, exc ) # We would already have normalized the versions at the beginning of this @@ -745,7 +759,7 @@ def version_cmp(ver1, ver2, ignore_epoch=False): def checksum(*paths): - ''' + """ Return if the signature of a RPM file is valid. CLI Example: @@ -754,17 +768,20 @@ def checksum(*paths): salt '*' lowpkg.checksum /path/to/package1.rpm salt '*' lowpkg.checksum /path/to/package1.rpm /path/to/package2.rpm - ''' + """ ret = dict() if not paths: raise CommandExecutionError("No package files has been specified.") for package_file in paths: - ret[package_file] = (bool(__salt__['file.file_exists'](package_file)) and - not __salt__['cmd.retcode'](["rpm", "-K", "--quiet", package_file], - ignore_retcode=True, - output_loglevel='trace', - python_shell=False)) + ret[package_file] = bool( + __salt__["file.file_exists"](package_file) + ) and not __salt__["cmd.retcode"]( + ["rpm", "-K", "--quiet", package_file], + ignore_retcode=True, + output_loglevel="trace", + python_shell=False, + ) return ret diff --git a/salt/modules/rpmbuild_pkgbuild.py b/salt/modules/rpmbuild_pkgbuild.py index 46f69602133..46f0c42325c 100644 --- a/salt/modules/rpmbuild_pkgbuild.py +++ b/salt/modules/rpmbuild_pkgbuild.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" RPM Package builder system .. versionadded:: 2015.8.0 @@ -8,173 +8,176 @@ This system allows for all of the components to build rpms safely in chrooted environments. This also provides a function to generate yum repositories This module implements the pkgbuild interface -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import errno +import functools import logging import os +import re import shutil import tempfile import time -import re import traceback -import functools -# Import salt libs -from salt.exceptions import SaltInvocationError import salt.utils.files import salt.utils.path import salt.utils.user import salt.utils.vt +# Import salt libs +from salt.exceptions import SaltInvocationError + # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=no-name-in-module,import-error +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse HAS_LIBS = False try: - import gnupg # pylint: disable=unused-import + import gnupg # pylint: disable=unused-import import salt.modules.gpg + HAS_LIBS = True except ImportError: pass log = logging.getLogger(__name__) -__virtualname__ = 'pkgbuild' +__virtualname__ = "pkgbuild" def __virtual__(): - ''' + """ Confirm this module is on a RPM based system, and has required utilities - ''' + """ missing_util = False - utils_reqd = ['gpg', 'rpm', 'rpmbuild', 'mock', 'createrepo'] + utils_reqd = ["gpg", "rpm", "rpmbuild", "mock", "createrepo"] for named_util in utils_reqd: if not salt.utils.path.which(named_util): missing_util = True break if HAS_LIBS and not missing_util: - if __grains__.get('os_family', False) in ('RedHat', 'Suse'): + if __grains__.get("os_family", False) in ("RedHat", "Suse"): return __virtualname__ else: # The module will be exposed as `rpmbuild` on non-RPM based systems - return 'rpmbuild' + return "rpmbuild" else: - return False, 'The rpmbuild module could not be loaded: requires python-gnupg, ' \ - 'gpg, rpm, rpmbuild, mock and createrepo utilities to be installed' - - -def _create_rpmmacros(runas='root'): - ''' - Create the .rpmmacros file in user's home directory - ''' - home = os.path.expanduser('~') - rpmbuilddir = os.path.join(home, 'rpmbuild') - if not os.path.isdir(rpmbuilddir): - __salt__['file.makedirs_perms'](name=rpmbuilddir, user=runas, group='mock') - - mockdir = os.path.join(home, 'mock') - if not os.path.isdir(mockdir): - __salt__['file.makedirs_perms'](name=mockdir, user=runas, group='mock') - - rpmmacros = os.path.join(home, '.rpmmacros') - with salt.utils.files.fopen(rpmmacros, 'w') as afile: - afile.write( - salt.utils.stringutils.to_str('%_topdir {0}\n'.format(rpmbuilddir)) + return ( + False, + "The rpmbuild module could not be loaded: requires python-gnupg, " + "gpg, rpm, rpmbuild, mock and createrepo utilities to be installed", ) - afile.write('%signature gpg\n') - afile.write('%_source_filedigest_algorithm 8\n') - afile.write('%_binary_filedigest_algorithm 8\n') - afile.write('%_gpg_name packaging@saltstack.com\n') -def _mk_tree(runas='root'): - ''' +def _create_rpmmacros(runas="root"): + """ + Create the .rpmmacros file in user's home directory + """ + home = os.path.expanduser("~") + rpmbuilddir = os.path.join(home, "rpmbuild") + if not os.path.isdir(rpmbuilddir): + __salt__["file.makedirs_perms"](name=rpmbuilddir, user=runas, group="mock") + + mockdir = os.path.join(home, "mock") + if not os.path.isdir(mockdir): + __salt__["file.makedirs_perms"](name=mockdir, user=runas, group="mock") + + rpmmacros = os.path.join(home, ".rpmmacros") + with salt.utils.files.fopen(rpmmacros, "w") as afile: + afile.write(salt.utils.stringutils.to_str("%_topdir {0}\n".format(rpmbuilddir))) + afile.write("%signature gpg\n") + afile.write("%_source_filedigest_algorithm 8\n") + afile.write("%_binary_filedigest_algorithm 8\n") + afile.write("%_gpg_name packaging@saltstack.com\n") + + +def _mk_tree(runas="root"): + """ Create the rpm build tree - ''' + """ basedir = tempfile.mkdtemp() - paths = ['BUILD', 'RPMS', 'SOURCES', 'SPECS', 'SRPMS'] + paths = ["BUILD", "RPMS", "SOURCES", "SPECS", "SRPMS"] for path in paths: full = os.path.join(basedir, path) - __salt__['file.makedirs_perms'](name=full, user=runas, group='mock') + __salt__["file.makedirs_perms"](name=full, user=runas, group="mock") return basedir -def _get_spec(tree_base, spec, template, saltenv='base'): - ''' +def _get_spec(tree_base, spec, template, saltenv="base"): + """ Get the spec file and place it in the SPECS dir - ''' + """ spec_tgt = os.path.basename(spec) - dest = os.path.join(tree_base, 'SPECS', spec_tgt) - return __salt__['cp.get_url']( - spec, - dest, - saltenv=saltenv) + dest = os.path.join(tree_base, "SPECS", spec_tgt) + return __salt__["cp.get_url"](spec, dest, saltenv=saltenv) -def _get_src(tree_base, source, saltenv='base', runas='root'): - ''' +def _get_src(tree_base, source, saltenv="base", runas="root"): + """ Get the named sources and place them into the tree_base - ''' + """ parsed = _urlparse(source) sbase = os.path.basename(source) - dest = os.path.join(tree_base, 'SOURCES', sbase) + dest = os.path.join(tree_base, "SOURCES", sbase) if parsed.scheme: - lsrc = __salt__['cp.get_url'](source, dest, saltenv=saltenv) + lsrc = __salt__["cp.get_url"](source, dest, saltenv=saltenv) else: shutil.copy(source, dest) - __salt__['file.chown'](path=dest, user=runas, group='mock') + __salt__["file.chown"](path=dest, user=runas, group="mock") def _get_distset(tgt): - ''' + """ Get the distribution string for use with rpmbuild and mock - ''' + """ # Centos adds 'centos' string to rpm names, removing that to have # consistent naming on Centos and Redhat, and allow for Amazon naming - tgtattrs = tgt.split('-') - if tgtattrs[0] == 'amzn': + tgtattrs = tgt.split("-") + if tgtattrs[0] == "amzn": distset = '--define "dist .{0}1"'.format(tgtattrs[0]) - elif tgtattrs[1] in ['6', '7']: + elif tgtattrs[1] in ["6", "7"]: distset = '--define "dist .el{0}"'.format(tgtattrs[1]) else: - distset = '' + distset = "" return distset -def _get_deps(deps, tree_base, saltenv='base'): - ''' +def _get_deps(deps, tree_base, saltenv="base"): + """ Get include string for list of dependent rpms to build package - ''' - deps_list = '' + """ + deps_list = "" if deps is None: return deps_list if not isinstance(deps, list): raise SaltInvocationError( - '\'deps\' must be a Python list or comma-separated string' + "'deps' must be a Python list or comma-separated string" ) for deprpm in deps: parsed = _urlparse(deprpm) depbase = os.path.basename(deprpm) dest = os.path.join(tree_base, depbase) if parsed.scheme: - __salt__['cp.get_url'](deprpm, dest, saltenv=saltenv) + __salt__["cp.get_url"](deprpm, dest, saltenv=saltenv) else: shutil.copy(deprpm, dest) - deps_list += ' {0}'.format(dest) + deps_list += " {0}".format(dest) return deps_list -def make_src_pkg(dest_dir, spec, sources, env=None, template=None, saltenv='base', runas='root'): - ''' +def make_src_pkg( + dest_dir, spec, sources, env=None, template=None, saltenv="base", runas="root" +): + """ Create a source rpm from the given spec file and sources CLI Example: @@ -220,35 +223,35 @@ def make_src_pkg(dest_dir, spec, sources, env=None, template=None, saltenv='base using SHA256 as digest and minimum level dist el6 - ''' + """ _create_rpmmacros(runas) tree_base = _mk_tree(runas) spec_path = _get_spec(tree_base, spec, template, saltenv) - __salt__['file.chown'](path=spec_path, user=runas, group='mock') - __salt__['file.chown'](path=tree_base, user=runas, group='mock') + __salt__["file.chown"](path=spec_path, user=runas, group="mock") + __salt__["file.chown"](path=tree_base, user=runas, group="mock") if isinstance(sources, six.string_types): - sources = sources.split(',') + sources = sources.split(",") for src in sources: _get_src(tree_base, src, saltenv, runas) # make source rpms for dist el6 with SHA256, usable with mock on other dists - cmd = 'rpmbuild --verbose --define "_topdir {0}" -bs --define "dist .el6" {1}'.format(tree_base, spec_path) - retrc = __salt__['cmd.retcode'](cmd, runas=runas) + cmd = 'rpmbuild --verbose --define "_topdir {0}" -bs --define "dist .el6" {1}'.format( + tree_base, spec_path + ) + retrc = __salt__["cmd.retcode"](cmd, runas=runas) if retrc != 0: raise SaltInvocationError( - 'Make source package for destination directory {0}, spec {1}, sources {2}, failed ' - 'with return error {3}, check logs for further details'.format( - dest_dir, - spec, - sources, - retrc) + "Make source package for destination directory {0}, spec {1}, sources {2}, failed " + "with return error {3}, check logs for further details".format( + dest_dir, spec, sources, retrc + ) ) - srpms = os.path.join(tree_base, 'SRPMS') + srpms = os.path.join(tree_base, "SRPMS") ret = [] if not os.path.isdir(dest_dir): - __salt__['file.makedirs_perms'](name=dest_dir, user=runas, group='mock') + __salt__["file.makedirs_perms"](name=dest_dir, user=runas, group="mock") for fn_ in os.listdir(srpms): full = os.path.join(srpms, fn_) tgt = os.path.join(dest_dir, fn_) @@ -257,17 +260,19 @@ def make_src_pkg(dest_dir, spec, sources, env=None, template=None, saltenv='base return ret -def build(runas, - tgt, - dest_dir, - spec, - sources, - deps, - env, - template, - saltenv='base', - log_dir='/var/log/salt/pkgbuild'): - ''' +def build( + runas, + tgt, + dest_dir, + spec, + sources, + deps, + env, + template, + saltenv="base", + log_dir="/var/log/salt/pkgbuild", +): + """ Given the package destination directory, the spec file source and package sources, use mock to safely build the rpm defined in the spec file @@ -281,26 +286,27 @@ def build(runas, This example command should build the libnacl package for rhel 7 using user mock and place it in /var/www/html/ on the minion - ''' + """ ret = {} try: - __salt__['file.chown'](path=dest_dir, user=runas, group='mock') + __salt__["file.chown"](path=dest_dir, user=runas, group="mock") except OSError as exc: if exc.errno != errno.EEXIST: raise - srpm_dir = os.path.join(dest_dir, 'SRPMS') + srpm_dir = os.path.join(dest_dir, "SRPMS") srpm_build_dir = tempfile.mkdtemp() try: - srpms = make_src_pkg(srpm_build_dir, spec, sources, - env, template, saltenv, runas) + srpms = make_src_pkg( + srpm_build_dir, spec, sources, env, template, saltenv, runas + ) except Exception as exc: # pylint: disable=broad-except shutil.rmtree(srpm_build_dir) - log.error('Failed to make src package') + log.error("Failed to make src package") return ret distset = _get_distset(tgt) - noclean = '' + noclean = "" deps_dir = tempfile.mkdtemp() deps_list = _get_deps(deps, deps_dir, saltenv) @@ -309,79 +315,83 @@ def build(runas, dbase = os.path.dirname(srpm) results_dir = tempfile.mkdtemp() try: - __salt__['file.chown'](path=dbase, user=runas, group='mock') - __salt__['file.chown'](path=results_dir, user=runas, group='mock') - cmd = 'mock --root={0} --resultdir={1} --init'.format(tgt, results_dir) - retrc |= __salt__['cmd.retcode'](cmd, runas=runas) + __salt__["file.chown"](path=dbase, user=runas, group="mock") + __salt__["file.chown"](path=results_dir, user=runas, group="mock") + cmd = "mock --root={0} --resultdir={1} --init".format(tgt, results_dir) + retrc |= __salt__["cmd.retcode"](cmd, runas=runas) if deps_list and not deps_list.isspace(): - cmd = 'mock --root={0} --resultdir={1} --install {2} {3}'.format(tgt, results_dir, deps_list, noclean) - retrc |= __salt__['cmd.retcode'](cmd, runas=runas) - noclean += ' --no-clean' + cmd = "mock --root={0} --resultdir={1} --install {2} {3}".format( + tgt, results_dir, deps_list, noclean + ) + retrc |= __salt__["cmd.retcode"](cmd, runas=runas) + noclean += " --no-clean" - cmd = 'mock --root={0} --resultdir={1} {2} {3} {4}'.format( - tgt, - results_dir, - distset, - noclean, - srpm) - retrc |= __salt__['cmd.retcode'](cmd, runas=runas) + cmd = "mock --root={0} --resultdir={1} {2} {3} {4}".format( + tgt, results_dir, distset, noclean, srpm + ) + retrc |= __salt__["cmd.retcode"](cmd, runas=runas) cmdlist = [ - 'rpm', - '-qp', - '--queryformat', - '{0}/%{{name}}/%{{version}}-%{{release}}'.format(log_dir), - srpm] - log_dest = __salt__['cmd.run_stdout'](cmdlist, python_shell=False) + "rpm", + "-qp", + "--queryformat", + "{0}/%{{name}}/%{{version}}-%{{release}}".format(log_dir), + srpm, + ] + log_dest = __salt__["cmd.run_stdout"](cmdlist, python_shell=False) for filename in os.listdir(results_dir): full = os.path.join(results_dir, filename) - if filename.endswith('src.rpm'): + if filename.endswith("src.rpm"): sdest = os.path.join(srpm_dir, filename) try: - __salt__['file.makedirs_perms'](name=srpm_dir, user=runas, group='mock') + __salt__["file.makedirs_perms"]( + name=srpm_dir, user=runas, group="mock" + ) except OSError as exc: if exc.errno != errno.EEXIST: raise shutil.copy(full, sdest) - ret.setdefault('Source Packages', []).append(sdest) - elif filename.endswith('.rpm'): + ret.setdefault("Source Packages", []).append(sdest) + elif filename.endswith(".rpm"): bdist = os.path.join(dest_dir, filename) shutil.copy(full, bdist) - ret.setdefault('Packages', []).append(bdist) + ret.setdefault("Packages", []).append(bdist) else: log_file = os.path.join(log_dest, filename) try: - __salt__['file.makedirs_perms'](name=log_dest, user=runas, group='mock') + __salt__["file.makedirs_perms"]( + name=log_dest, user=runas, group="mock" + ) except OSError as exc: if exc.errno != errno.EEXIST: raise shutil.copy(full, log_file) - ret.setdefault('Log Files', []).append(log_file) + ret.setdefault("Log Files", []).append(log_file) except Exception as exc: # pylint: disable=broad-except - log.error('Error building from %s: %s', srpm, exc) + log.error("Error building from %s: %s", srpm, exc) finally: shutil.rmtree(results_dir) if retrc != 0: raise SaltInvocationError( - 'Building packages for destination directory {0}, spec {1}, sources {2}, failed ' - 'with return error {3}, check logs for further details'.format( - dest_dir, - spec, - sources, - retrc) + "Building packages for destination directory {0}, spec {1}, sources {2}, failed " + "with return error {3}, check logs for further details".format( + dest_dir, spec, sources, retrc + ) ) shutil.rmtree(deps_dir) shutil.rmtree(srpm_build_dir) return ret -def make_repo(repodir, - keyid=None, - env=None, - use_passphrase=False, - gnupghome='/etc/salt/gpgkeys', - runas='root', - timeout=15.0): - ''' +def make_repo( + repodir, + keyid=None, + env=None, + use_passphrase=False, + gnupghome="/etc/salt/gpgkeys", + runas="root", + timeout=15.0, +): + """ Make a package repository and optionally sign packages present Given the repodir, create a ``yum`` repository out of the rpms therein @@ -482,79 +492,89 @@ def make_repo(repodir, salt '*' pkgbuild.make_repo /var/www/html/ - ''' - SIGN_PROMPT_RE = re.compile(r'Enter pass phrase: ', re.M) + """ + SIGN_PROMPT_RE = re.compile(r"Enter pass phrase: ", re.M) - define_gpg_name = '' + define_gpg_name = "" local_keyid = None local_uids = None - phrase = '' + phrase = "" if keyid is not None: # import_keys - pkg_pub_key_file = '{0}/{1}'.format(gnupghome, __salt__['pillar.get']('gpg_pkg_pub_keyname', None)) - pkg_priv_key_file = '{0}/{1}'.format(gnupghome, __salt__['pillar.get']('gpg_pkg_priv_keyname', None)) + pkg_pub_key_file = "{0}/{1}".format( + gnupghome, __salt__["pillar.get"]("gpg_pkg_pub_keyname", None) + ) + pkg_priv_key_file = "{0}/{1}".format( + gnupghome, __salt__["pillar.get"]("gpg_pkg_priv_keyname", None) + ) if pkg_pub_key_file is None or pkg_priv_key_file is None: raise SaltInvocationError( - 'Pillar data should contain Public and Private keys associated with \'keyid\'' + "Pillar data should contain Public and Private keys associated with 'keyid'" ) try: - __salt__['gpg.import_key'](user=runas, filename=pkg_pub_key_file, gnupghome=gnupghome) - __salt__['gpg.import_key'](user=runas, filename=pkg_priv_key_file, gnupghome=gnupghome) + __salt__["gpg.import_key"]( + user=runas, filename=pkg_pub_key_file, gnupghome=gnupghome + ) + __salt__["gpg.import_key"]( + user=runas, filename=pkg_priv_key_file, gnupghome=gnupghome + ) except SaltInvocationError: raise SaltInvocationError( - 'Public and Private key files associated with Pillar data and \'keyid\' ' - '{0} could not be found' - .format(keyid) + "Public and Private key files associated with Pillar data and 'keyid' " + "{0} could not be found".format(keyid) ) # gpg keys should have been loaded as part of setup # retrieve specified key and preset passphrase - local_keys = __salt__['gpg.list_keys'](user=runas, gnupghome=gnupghome) + local_keys = __salt__["gpg.list_keys"](user=runas, gnupghome=gnupghome) for gpg_key in local_keys: - if keyid == gpg_key['keyid'][8:]: - local_uids = gpg_key['uids'] - local_keyid = gpg_key['keyid'] + if keyid == gpg_key["keyid"][8:]: + local_uids = gpg_key["uids"] + local_keyid = gpg_key["keyid"] break if local_keyid is None: raise SaltInvocationError( - 'The key ID \'{0}\' was not found in GnuPG keyring at \'{1}\'' - .format(keyid, gnupghome) + "The key ID '{0}' was not found in GnuPG keyring at '{1}'".format( + keyid, gnupghome + ) ) if use_passphrase: - phrase = __salt__['pillar.get']('gpg_passphrase') + phrase = __salt__["pillar.get"]("gpg_passphrase") if local_uids: - define_gpg_name = '--define=\'%_signature gpg\' --define=\'%_gpg_name {0}\''.format( + define_gpg_name = "--define='%_signature gpg' --define='%_gpg_name {0}'".format( local_uids[0] ) # need to update rpm with public key - cmd = 'rpm --import {0}'.format(pkg_pub_key_file) - retrc = __salt__['cmd.retcode'](cmd, runas=runas, use_vt=True) + cmd = "rpm --import {0}".format(pkg_pub_key_file) + retrc = __salt__["cmd.retcode"](cmd, runas=runas, use_vt=True) if retrc != 0: raise SaltInvocationError( - 'Failed to import public key from file {0} with return ' - 'error {1}, check logs for further details'.format( - pkg_pub_key_file, - retrc) + "Failed to import public key from file {0} with return " + "error {1}, check logs for further details".format( + pkg_pub_key_file, retrc + ) ) # sign_it_here # interval of 0.125 is really too fast on some systems interval = 0.5 for fileused in os.listdir(repodir): - if fileused.endswith('.rpm'): + if fileused.endswith(".rpm"): abs_file = os.path.join(repodir, fileused) number_retries = timeout / interval times_looped = 0 - error_msg = 'Failed to sign file {0}'.format(abs_file) - cmd = 'rpm {0} --addsign {1}'.format(define_gpg_name, abs_file) - preexec_fn = functools.partial(salt.utils.user.chugid_and_umask, runas, None) + error_msg = "Failed to sign file {0}".format(abs_file) + cmd = "rpm {0} --addsign {1}".format(define_gpg_name, abs_file) + preexec_fn = functools.partial( + salt.utils.user.chugid_and_umask, runas, None + ) try: stdout, stderr = None, None proc = salt.utils.vt.Terminal( @@ -562,7 +582,7 @@ def make_repo(repodir, shell=True, preexec_fn=preexec_fn, stream_stdout=True, - stream_stderr=True + stream_stderr=True, ) while proc.has_unread_data: stdout, stderr = proc.recv() @@ -574,16 +594,18 @@ def make_repo(repodir, if times_looped > number_retries: raise SaltInvocationError( - 'Attemping to sign file {0} failed, timed out after {1} seconds' - .format(abs_file, int(times_looped * interval)) + "Attemping to sign file {0} failed, timed out after {1} seconds".format( + abs_file, int(times_looped * interval) + ) ) time.sleep(interval) proc_exitstatus = proc.exitstatus if proc_exitstatus != 0: raise SaltInvocationError( - 'Signing file {0} failed with proc.status {1}' - .format(abs_file, proc_exitstatus) + "Signing file {0} failed with proc.status {1}".format( + abs_file, proc_exitstatus + ) ) except salt.utils.vt.TerminalException as err: trace = traceback.format_exc() @@ -591,5 +613,5 @@ def make_repo(repodir, finally: proc.close(terminate=True, kill=True) - cmd = 'createrepo --update {0}'.format(repodir) - return __salt__['cmd.run_all'](cmd, runas=runas) + cmd = "createrepo --update {0}".format(repodir) + return __salt__["cmd.run_all"](cmd, runas=runas) diff --git a/salt/modules/rsync.py b/salt/modules/rsync.py index 1e9f361ca48..fbe75537b96 100644 --- a/salt/modules/rsync.py +++ b/salt/modules/rsync.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Wrapper for rsync .. versionadded:: 2014.1.0 This data can also be passed into :ref:`pillar `. Options passed into opts will overwrite options passed into pillar. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -22,63 +22,68 @@ from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) -__virtualname__ = 'rsync' +__virtualname__ = "rsync" def __virtual__(): - ''' + """ Only load module if rsync binary is present - ''' - if salt.utils.path.which('rsync'): + """ + if salt.utils.path.which("rsync"): return __virtualname__ - return (False, 'The rsync execution module cannot be loaded: ' - 'the rsync binary is not in the path.') + return ( + False, + "The rsync execution module cannot be loaded: " + "the rsync binary is not in the path.", + ) def _check(delete, force, update, passwordfile, exclude, excludefrom, dryrun, rsh): - ''' + """ Generate rsync options - ''' - options = ['-avz'] + """ + options = ["-avz"] if delete: - options.append('--delete') + options.append("--delete") if force: - options.append('--force') + options.append("--force") if update: - options.append('--update') + options.append("--update") if rsh: - options.append('--rsh={0}'.format(rsh)) + options.append("--rsh={0}".format(rsh)) if passwordfile: - options.extend(['--password-file', passwordfile]) + options.extend(["--password-file", passwordfile]) if excludefrom: - options.extend(['--exclude-from', excludefrom]) + options.extend(["--exclude-from", excludefrom]) if exclude: exclude = False if exclude: if isinstance(exclude, list): for ex_ in exclude: - options.extend(['--exclude', ex_]) + options.extend(["--exclude", ex_]) else: - options.extend(['--exclude', exclude]) + options.extend(["--exclude", exclude]) if dryrun: - options.append('--dry-run') + options.append("--dry-run") return options -def rsync(src, - dst, - delete=False, - force=False, - update=False, - passwordfile=None, - exclude=None, - excludefrom=None, - dryrun=False, - rsh=None, - additional_opts=None, - saltenv='base'): - ''' +def rsync( + src, + dst, + delete=False, + force=False, + update=False, + passwordfile=None, + exclude=None, + excludefrom=None, + dryrun=False, + rsh=None, + additional_opts=None, + saltenv="base", +): + """ .. versionchanged:: 2016.3.0 Return data now contains just the output of the rsync command, instead of a dictionary as returned from :py:func:`cmd.run_all @@ -140,87 +145,78 @@ def rsync(src, salt '*' rsync.rsync /path/to/src /path/to/dest delete=True update=True passwordfile=/etc/pass.crt exclude=exclude/dir salt '*' rsync.rsync /path/to/src delete=True excludefrom=/xx.ini salt '*' rsync.rsync /path/to/src delete=True exclude='[exclude1/dir,exclude2/dir]' additional_opts='["--partial", "--bwlimit=5000"]' - ''' + """ if not src: - src = __salt__['config.option']('rsync.src') + src = __salt__["config.option"]("rsync.src") if not dst: - dst = __salt__['config.option']('rsync.dst') + dst = __salt__["config.option"]("rsync.dst") if not delete: - delete = __salt__['config.option']('rsync.delete') + delete = __salt__["config.option"]("rsync.delete") if not force: - force = __salt__['config.option']('rsync.force') + force = __salt__["config.option"]("rsync.force") if not update: - update = __salt__['config.option']('rsync.update') + update = __salt__["config.option"]("rsync.update") if not passwordfile: - passwordfile = __salt__['config.option']('rsync.passwordfile') + passwordfile = __salt__["config.option"]("rsync.passwordfile") if not exclude: - exclude = __salt__['config.option']('rsync.exclude') + exclude = __salt__["config.option"]("rsync.exclude") if not excludefrom: - excludefrom = __salt__['config.option']('rsync.excludefrom') + excludefrom = __salt__["config.option"]("rsync.excludefrom") if not dryrun: - dryrun = __salt__['config.option']('rsync.dryrun') + dryrun = __salt__["config.option"]("rsync.dryrun") if not rsh: - rsh = __salt__['config.option']('rsync.rsh') + rsh = __salt__["config.option"]("rsync.rsh") if not src or not dst: - raise SaltInvocationError('src and dst cannot be empty') + raise SaltInvocationError("src and dst cannot be empty") tmp_src = None - if src.startswith('salt://'): + if src.startswith("salt://"): _src = src - _path = re.sub('salt://', '', _src) + _path = re.sub("salt://", "", _src) src_is_dir = False - if _path in __salt__['cp.list_master_dirs'](saltenv=saltenv): + if _path in __salt__["cp.list_master_dirs"](saltenv=saltenv): src_is_dir = True if src_is_dir: tmp_src = tempfile.mkdtemp() - dir_src = __salt__['cp.get_dir'](_src, - tmp_src, - saltenv) + dir_src = __salt__["cp.get_dir"](_src, tmp_src, saltenv) if dir_src: src = tmp_src # Ensure src ends in / so we # get the contents not the tmpdir # itself. - if not src.endswith('/'): - src = '{0}/'.format(src) + if not src.endswith("/"): + src = "{0}/".format(src) else: - raise CommandExecutionError('{0} does not exist'.format(src)) + raise CommandExecutionError("{0} does not exist".format(src)) else: tmp_src = salt.utils.files.mkstemp() - file_src = __salt__['cp.get_file'](_src, - tmp_src, - saltenv) + file_src = __salt__["cp.get_file"](_src, tmp_src, saltenv) if file_src: src = tmp_src else: - raise CommandExecutionError('{0} does not exist'.format(src)) + raise CommandExecutionError("{0} does not exist".format(src)) - option = _check(delete, - force, - update, - passwordfile, - exclude, - excludefrom, - dryrun, - rsh) + option = _check( + delete, force, update, passwordfile, exclude, excludefrom, dryrun, rsh + ) if additional_opts and isinstance(additional_opts, list): option = option + additional_opts - cmd = ['rsync'] + option + [src, dst] - log.debug('Running rsync command: %s', cmd) + cmd = ["rsync"] + option + [src, dst] + log.debug("Running rsync command: %s", cmd) try: - return __salt__['cmd.run_all'](cmd, python_shell=False) + return __salt__["cmd.run_all"](cmd, python_shell=False) except (IOError, OSError) as exc: raise CommandExecutionError(exc.strerror) finally: if tmp_src: - __salt__['file.remove'](tmp_src) + __salt__["file.remove"](tmp_src) def version(): - ''' + """ .. versionchanged:: 2016.3.0 Return data now contains just the version number as a string, instead of a dictionary as returned from :py:func:`cmd.run_all @@ -233,21 +229,19 @@ def version(): .. code-block:: bash salt '*' rsync.version - ''' + """ try: - out = __salt__['cmd.run_stdout']( - ['rsync', '--version'], - python_shell=False) + out = __salt__["cmd.run_stdout"](["rsync", "--version"], python_shell=False) except (IOError, OSError) as exc: raise CommandExecutionError(exc.strerror) try: - return out.split('\n')[0].split()[2] + return out.split("\n")[0].split()[2] except IndexError: - raise CommandExecutionError('Unable to determine rsync version') + raise CommandExecutionError("Unable to determine rsync version") -def config(conf_path='/etc/rsyncd.conf'): - ''' +def config(conf_path="/etc/rsyncd.conf"): + """ .. versionchanged:: 2016.3.0 Return data now contains just the contents of the rsyncd.conf as a string, instead of a dictionary as returned from :py:func:`cmd.run_all @@ -263,26 +257,26 @@ def config(conf_path='/etc/rsyncd.conf'): .. code-block:: bash salt '*' rsync.config - ''' - ret = '' + """ + ret = "" try: - with salt.utils.files.fopen(conf_path, 'r') as fp_: + with salt.utils.files.fopen(conf_path, "r") as fp_: for line in fp_: ret += salt.utils.stringutils.to_unicode(line) except IOError as exc: if exc.errno == errno.ENOENT: - raise CommandExecutionError('{0} does not exist'.format(conf_path)) + raise CommandExecutionError("{0} does not exist".format(conf_path)) elif exc.errno == errno.EACCES: raise CommandExecutionError( - 'Unable to read {0}, access denied'.format(conf_path) + "Unable to read {0}, access denied".format(conf_path) ) elif exc.errno == errno.EISDIR: raise CommandExecutionError( - 'Unable to read {0}, path is a directory'.format(conf_path) + "Unable to read {0}, path is a directory".format(conf_path) ) else: raise CommandExecutionError( - 'Error {0}: {1}'.format(exc.errno, exc.strerror) + "Error {0}: {1}".format(exc.errno, exc.strerror) ) else: return ret diff --git a/salt/modules/runit.py b/salt/modules/runit.py index 3b417aa9e1d..809787116dd 100644 --- a/salt/modules/runit.py +++ b/salt/modules/runit.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" runit service module (http://smarden.org/runit) @@ -44,32 +44,33 @@ Service's alias: XBPS package management uses a service's alias to provides service alternative(s), such as chrony and openntpd both aliased to ntpd. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import glob +import logging # Import python libs import os -import glob -import logging import time -log = logging.getLogger(__name__) - -# Import salt libs -from salt.exceptions import CommandExecutionError import salt.utils.files import salt.utils.path +# Import salt libs +from salt.exceptions import CommandExecutionError + +log = logging.getLogger(__name__) + + # Function alias to not shadow built-ins. -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} # which dir sv works with VALID_SERVICE_DIRS = [ - '/service', - '/var/service', - '/etc/service', + "/service", + "/var/service", + "/etc/service", ] SERVICE_DIR = None for service_dir in VALID_SERVICE_DIRS: @@ -81,41 +82,41 @@ for service_dir in VALID_SERVICE_DIRS: AVAIL_SVR_DIRS = [] # Define the module's virtual name -__virtualname__ = 'runit' -__virtual_aliases__ = ('runit',) +__virtualname__ = "runit" +__virtual_aliases__ = ("runit",) def __virtual__(): - ''' + """ Virtual service only on systems using runit as init process (PID 1). Otherwise, use this module with the provider mechanism. - ''' - if __grains__.get('init') == 'runit': - if __grains__['os'] == 'Void': - add_svc_avail_path('/etc/sv') + """ + if __grains__.get("init") == "runit": + if __grains__["os"] == "Void": + add_svc_avail_path("/etc/sv") global __virtualname__ - __virtualname__ = 'service' + __virtualname__ = "service" return __virtualname__ - if salt.utils.path.which('sv'): + if salt.utils.path.which("sv"): return __virtualname__ - return (False, 'Runit not available. Please install sv') + return (False, "Runit not available. Please install sv") def _service_path(name): - ''' + """ Return SERVICE_DIR+name if possible name the service's name to work on - ''' + """ if not SERVICE_DIR: - raise CommandExecutionError('Could not find service directory.') + raise CommandExecutionError("Could not find service directory.") return os.path.join(SERVICE_DIR, name) -#-- states.service compatible args +# -- states.service compatible args def start(name): - ''' + """ Start service name @@ -126,14 +127,14 @@ def start(name): .. code-block:: bash salt '*' runit.start - ''' - cmd = 'sv start {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "sv start {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd) -#-- states.service compatible args +# -- states.service compatible args def stop(name): - ''' + """ Stop service name @@ -144,14 +145,14 @@ def stop(name): .. code-block:: bash salt '*' runit.stop - ''' - cmd = 'sv stop {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "sv stop {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd) -#-- states.service compatible +# -- states.service compatible def reload_(name): - ''' + """ Reload service name @@ -162,14 +163,14 @@ def reload_(name): .. code-block:: bash salt '*' runit.reload - ''' - cmd = 'sv reload {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "sv reload {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd) -#-- states.service compatible +# -- states.service compatible def restart(name): - ''' + """ Restart service name @@ -180,14 +181,14 @@ def restart(name): .. code-block:: bash salt '*' runit.restart - ''' - cmd = 'sv restart {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "sv restart {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd) -#-- states.service compatible +# -- states.service compatible def full_restart(name): - ''' + """ Calls runit.restart() name @@ -198,13 +199,13 @@ def full_restart(name): .. code-block:: bash salt '*' runit.full_restart - ''' + """ restart(name) -#-- states.service compatible +# -- states.service compatible def status(name, sig=None): - ''' + """ Return ``True`` if service is running name @@ -218,11 +219,11 @@ def status(name, sig=None): .. code-block:: bash salt '*' runit.status - ''' + """ if sig: # usual way to do by others (debian_service, netbsdservice). # XXX probably does not work here (check 'runsv sshd' instead of 'sshd' ?) - return bool(__salt__['status.pid'](sig)) + return bool(__salt__["status.pid"](sig)) svc_path = _service_path(name) if not os.path.exists(svc_path): @@ -231,33 +232,35 @@ def status(name, sig=None): # sv return code is not relevant to get a service status. # Check its output instead. - cmd = 'sv status {0}'.format(svc_path) + cmd = "sv status {0}".format(svc_path) try: - out = __salt__['cmd.run_stdout'](cmd) - return out.startswith('run: ') + out = __salt__["cmd.run_stdout"](cmd) + return out.startswith("run: ") except Exception: # pylint: disable=broad-except # sv (as a command) returned an error return False def _is_svc(svc_path): - ''' + """ Return ``True`` if directory is really a service: file /run exists and is executable svc_path the (absolute) directory to check for compatibility - ''' - run_file = os.path.join(svc_path, 'run') - if (os.path.exists(svc_path) - and os.path.exists(run_file) - and os.access(run_file, os.X_OK)): + """ + run_file = os.path.join(svc_path, "run") + if ( + os.path.exists(svc_path) + and os.path.exists(run_file) + and os.access(run_file, os.X_OK) + ): return True return False def status_autostart(name): - ''' + """ Return ``True`` if service is autostarted by sv (file $service_folder/down does not exist) NB: return ``False`` if the service is not enabled. @@ -270,12 +273,12 @@ def status_autostart(name): .. code-block:: bash salt '*' runit.status_autostart - ''' - return not os.path.exists(os.path.join(_service_path(name), 'down')) + """ + return not os.path.exists(os.path.join(_service_path(name), "down")) -def get_svc_broken_path(name='*'): - ''' +def get_svc_broken_path(name="*"): + """ Return list of broken path(s) in SERVICE_DIR that match ``name`` A path is broken if it is a broken symlink or can not be a runit service @@ -288,9 +291,9 @@ def get_svc_broken_path(name='*'): .. code-block:: bash salt '*' runit.get_svc_broken_path - ''' + """ if not SERVICE_DIR: - raise CommandExecutionError('Could not find service directory.') + raise CommandExecutionError("Could not find service directory.") ret = set() @@ -301,20 +304,20 @@ def get_svc_broken_path(name='*'): def get_svc_avail_path(): - ''' + """ Return list of paths that may contain available services - ''' + """ return AVAIL_SVR_DIRS def add_svc_avail_path(path): - ''' + """ Add a path that may contain available services. Return ``True`` if added (or already present), ``False`` on error. path directory to add to AVAIL_SVR_DIRS - ''' + """ if os.path.exists(path): if path not in AVAIL_SVR_DIRS: AVAIL_SVR_DIRS.append(path) @@ -322,8 +325,8 @@ def add_svc_avail_path(path): return False -def _get_svc_path(name='*', status=None): - ''' +def _get_svc_path(name="*", status=None): + """ Return a list of paths to services with ``name`` that have the specified ``status`` name @@ -333,7 +336,7 @@ def _get_svc_path(name='*', status=None): None : all services (no filter, default choice) 'DISABLED' : available service(s) that is not enabled 'ENABLED' : enabled service (whether started on boot or not) - ''' + """ # This is the core routine to work with services, called by many # other functions of this module. @@ -343,7 +346,7 @@ def _get_svc_path(name='*', status=None): # the targeted service. if not SERVICE_DIR: - raise CommandExecutionError('Could not find service directory.') + raise CommandExecutionError("Could not find service directory.") # path list of enabled services as /AVAIL_SVR_DIRS/$service, # taking care of any service aliases (do not use os.path.realpath()). @@ -351,9 +354,9 @@ def _get_svc_path(name='*', status=None): for el in glob.glob(os.path.join(SERVICE_DIR, name)): if _is_svc(el): ena.add(os.readlink(el)) - log.trace('found enabled service path: %s', el) + log.trace("found enabled service path: %s", el) - if status == 'ENABLED': + if status == "ENABLED": return sorted(ena) # path list of available services as /AVAIL_SVR_DIRS/$service @@ -362,9 +365,9 @@ def _get_svc_path(name='*', status=None): for el in glob.glob(os.path.join(d, name)): if _is_svc(el): ava.add(el) - log.trace('found available service path: %s', el) + log.trace("found available service path: %s", el) - if status == 'DISABLED': + if status == "DISABLED": # service available but not enabled ret = ava.difference(ena) else: @@ -374,8 +377,8 @@ def _get_svc_path(name='*', status=None): return sorted(ret) -def _get_svc_list(name='*', status=None): - ''' +def _get_svc_list(name="*", status=None): + """ Return list of services that have the specified service ``status`` name @@ -385,18 +388,18 @@ def _get_svc_list(name='*', status=None): None : all services (no filter, default choice) 'DISABLED' : available service that is not enabled 'ENABLED' : enabled service (whether started on boot or not) - ''' + """ return sorted([os.path.basename(el) for el in _get_svc_path(name, status)]) def get_svc_alias(): - ''' + """ Returns the list of service's name that are aliased and their alias path(s) - ''' + """ ret = {} for d in AVAIL_SVR_DIRS: - for el in glob.glob(os.path.join(d, '*')): + for el in glob.glob(os.path.join(d, "*")): if not os.path.islink(el): continue psvc = os.readlink(el) @@ -410,7 +413,7 @@ def get_svc_alias(): def available(name): - ''' + """ Returns ``True`` if the specified service is available, otherwise returns ``False``. @@ -422,12 +425,12 @@ def available(name): .. code-block:: bash salt '*' runit.available - ''' + """ return name in _get_svc_list(name) def missing(name): - ''' + """ The inverse of runit.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -440,12 +443,12 @@ def missing(name): .. code-block:: bash salt '*' runit.missing - ''' + """ return name not in _get_svc_list(name) def get_all(): - ''' + """ Return a list of all available services CLI Example: @@ -453,12 +456,12 @@ def get_all(): .. code-block:: bash salt '*' runit.get_all - ''' + """ return _get_svc_list() def get_enabled(): - ''' + """ Return a list of all enabled services CLI Example: @@ -466,12 +469,12 @@ def get_enabled(): .. code-block:: bash salt '*' service.get_enabled - ''' - return _get_svc_list(status='ENABLED') + """ + return _get_svc_list(status="ENABLED") def get_disabled(): - ''' + """ Return a list of all disabled services CLI Example: @@ -479,12 +482,12 @@ def get_disabled(): .. code-block:: bash salt '*' service.get_disabled - ''' - return _get_svc_list(status='DISABLED') + """ + return _get_svc_list(status="DISABLED") def enabled(name): - ''' + """ Return ``True`` if the named service is enabled, ``False`` otherwise name @@ -495,13 +498,13 @@ def enabled(name): .. code-block:: bash salt '*' service.enabled - ''' + """ # exhaustive check instead of (only) os.path.exists(_service_path(name)) - return name in _get_svc_list(name, 'ENABLED') + return name in _get_svc_list(name, "ENABLED") def disabled(name): - ''' + """ Return ``True`` if the named service is disabled, ``False`` otherwise name @@ -512,13 +515,13 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled - ''' + """ # return True for a non-existent service - return name not in _get_svc_list(name, 'ENABLED') + return name not in _get_svc_list(name, "ENABLED") def show(name): - ''' + """ Show properties of one or more units/jobs or the manager name @@ -527,26 +530,26 @@ def show(name): CLI Example: salt '*' service.show - ''' + """ ret = {} - ret['enabled'] = False - ret['disabled'] = True - ret['running'] = False - ret['service_path'] = None - ret['autostart'] = False - ret['command_path'] = None + ret["enabled"] = False + ret["disabled"] = True + ret["running"] = False + ret["service_path"] = None + ret["autostart"] = False + ret["command_path"] = None - ret['available'] = available(name) - if not ret['available']: + ret["available"] = available(name) + if not ret["available"]: return ret - ret['enabled'] = enabled(name) - ret['disabled'] = not ret['enabled'] - ret['running'] = status(name) - ret['autostart'] = status_autostart(name) - ret['service_path'] = _get_svc_path(name)[0] - if ret['service_path']: - ret['command_path'] = os.path.join(ret['service_path'], 'run') + ret["enabled"] = enabled(name) + ret["disabled"] = not ret["enabled"] + ret["running"] = status(name) + ret["autostart"] = status_autostart(name) + ret["service_path"] = _get_svc_path(name)[0] + if ret["service_path"]: + ret["command_path"] = os.path.join(ret["service_path"], "run") # XXX provide info about alias ? @@ -554,7 +557,7 @@ def show(name): def enable(name, start=False, **kwargs): - ''' + """ Start service ``name`` at boot. Returns ``True`` if operation is successful @@ -569,7 +572,7 @@ def enable(name, start=False, **kwargs): .. code-block:: bash salt '*' service.enable [start=True] - ''' + """ # non-existent service if not available(name): @@ -578,12 +581,12 @@ def enable(name, start=False, **kwargs): # if service is aliased, refuse to enable it alias = get_svc_alias() if name in alias: - log.error('This service is aliased, enable its alias instead') + log.error("This service is aliased, enable its alias instead") return False # down_file: file that disables sv autostart svc_realpath = _get_svc_path(name)[0] - down_file = os.path.join(svc_realpath, 'down') + down_file = os.path.join(svc_realpath, "down") # if service already enabled, remove down_file to # let service starts on boot (as requested) @@ -592,7 +595,7 @@ def enable(name, start=False, **kwargs): try: os.unlink(down_file) except OSError: - log.error('Unable to remove file %s', down_file) + log.error("Unable to remove file %s", down_file) return False return True @@ -601,12 +604,14 @@ def enable(name, start=False, **kwargs): if not start: # create a temp 'down' file BEFORE enabling service. # will prevent sv from starting this service automatically. - log.trace('need a temporary file %s', down_file) + log.trace("need a temporary file %s", down_file) if not os.path.exists(down_file): try: - salt.utils.files.fopen(down_file, "w").close() # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + salt.utils.files.fopen(down_file, "w").close() + # pylint: enable=resource-leakage except IOError: - log.error('Unable to create file {0}'.format(down_file)) + log.error("Unable to create file {0}".format(down_file)) return False # enable the service @@ -615,7 +620,7 @@ def enable(name, start=False, **kwargs): except IOError: # (attempt to) remove temp down_file anyway - log.error('Unable to create symlink {0}'.format(down_file)) + log.error("Unable to create symlink {0}".format(down_file)) if not start: os.unlink(down_file) return False @@ -624,21 +629,21 @@ def enable(name, start=False, **kwargs): # if not, down_file might be removed too quickly, # before 'sv' have time to take care about it. # Documentation indicates that a change is handled within 5 seconds. - cmd = 'sv status {0}'.format(_service_path(name)) + cmd = "sv status {0}".format(_service_path(name)) retcode_sv = 1 count_sv = 0 while retcode_sv != 0 and count_sv < 10: time.sleep(0.5) count_sv += 1 - call = __salt__['cmd.run_all'](cmd) - retcode_sv = call['retcode'] + call = __salt__["cmd.run_all"](cmd) + retcode_sv = call["retcode"] # remove the temp down_file in any case. if (not start) and os.path.exists(down_file): try: os.unlink(down_file) except OSError: - log.error('Unable to remove temp file %s', down_file) + log.error("Unable to remove temp file %s", down_file) retcode_sv = 1 # if an error happened, revert our changes @@ -649,7 +654,7 @@ def enable(name, start=False, **kwargs): def disable(name, stop=False, **kwargs): - ''' + """ Don't start service ``name`` at boot Returns ``True`` if operation is successful @@ -664,7 +669,7 @@ def disable(name, stop=False, **kwargs): .. code-block:: bash salt '*' service.disable [stop=True] - ''' + """ # non-existent as registrered service if not enabled(name): @@ -672,23 +677,25 @@ def disable(name, stop=False, **kwargs): # down_file: file that prevent sv autostart svc_realpath = _get_svc_path(name)[0] - down_file = os.path.join(svc_realpath, 'down') + down_file = os.path.join(svc_realpath, "down") if stop: stop(name) if not os.path.exists(down_file): try: - salt.utils.files.fopen(down_file, "w").close() # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + salt.utils.files.fopen(down_file, "w").close() + # pylint: enable=resource-leakage except IOError: - log.error('Unable to create file %s', down_file) + log.error("Unable to create file %s", down_file) return False return True def remove(name): - ''' + """ Remove the service from system. Returns ``True`` if operation is successful. The service will be also stopped. @@ -701,23 +708,23 @@ def remove(name): .. code-block:: bash salt '*' service.remove - ''' + """ if not enabled(name): return False svc_path = _service_path(name) if not os.path.islink(svc_path): - log.error('%s is not a symlink: not removed', svc_path) + log.error("%s is not a symlink: not removed", svc_path) return False if not stop(name): - log.error('Failed to stop service %s', name) + log.error("Failed to stop service %s", name) return False try: os.remove(svc_path) except IOError: - log.error('Unable to remove symlink %s', svc_path) + log.error("Unable to remove symlink %s", svc_path) return False return True diff --git a/salt/modules/rvm.py b/salt/modules/rvm.py index e936fdd904b..a6063d7ff84 100644 --- a/salt/modules/rvm.py +++ b/salt/modules/rvm.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Manage ruby installations and gemsets with RVM, the Ruby Version Manager. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging +import os # Import python libs import re -import os -import logging # Import salt libs import salt.utils.args @@ -19,49 +20,45 @@ from salt.ext import six log = logging.getLogger(__name__) # Don't shadow built-in's. -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} __opts__ = { - 'rvm.runas': None, + "rvm.runas": None, } def _get_rvm_location(runas=None): if runas: - runas_home = os.path.expanduser('~{0}'.format(runas)) - rvmpath = '{0}/.rvm/bin/rvm'.format(runas_home) + runas_home = os.path.expanduser("~{0}".format(runas)) + rvmpath = "{0}/.rvm/bin/rvm".format(runas_home) if os.path.exists(rvmpath): return [rvmpath] - return ['/usr/local/rvm/bin/rvm'] + return ["/usr/local/rvm/bin/rvm"] def _rvm(command, runas=None, cwd=None, env=None): if runas is None: - runas = __salt__['config.option']('rvm.runas') + runas = __salt__["config.option"]("rvm.runas") if not is_installed(runas): return False cmd = _get_rvm_location(runas) + command - ret = __salt__['cmd.run_all'](cmd, - runas=runas, - cwd=cwd, - python_shell=False, - env=env) + ret = __salt__["cmd.run_all"]( + cmd, runas=runas, cwd=cwd, python_shell=False, env=env + ) - if ret['retcode'] == 0: - return ret['stdout'] + if ret["retcode"] == 0: + return ret["stdout"] return False def _rvm_do(ruby, command, runas=None, cwd=None, env=None): - return _rvm([ruby or 'default', 'do'] + command, runas=runas, cwd=cwd, env=env) + return _rvm([ruby or "default", "do"] + command, runas=runas, cwd=cwd, env=env) def is_installed(runas=None): - ''' + """ Check if RVM is installed. CLI Example: @@ -69,15 +66,15 @@ def is_installed(runas=None): .. code-block:: bash salt '*' rvm.is_installed - ''' + """ try: - return __salt__['cmd.has_exec'](_get_rvm_location(runas)[0]) + return __salt__["cmd.has_exec"](_get_rvm_location(runas)[0]) except IndexError: return False def install(runas=None): - ''' + """ Install RVM system-wide runas @@ -89,27 +86,29 @@ def install(runas=None): .. code-block:: bash salt '*' rvm.install - ''' + """ # RVM dependencies on Ubuntu 10.04: # bash coreutils gzip bzip2 gawk sed curl git-core subversion - installer = 'https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer' - ret = __salt__['cmd.run_all']( + installer = ( + "https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer" + ) + ret = __salt__["cmd.run_all"]( # the RVM installer automatically does a multi-user install when it is # invoked with root privileges - 'curl -Ls {installer} | bash -s stable'.format(installer=installer), + "curl -Ls {installer} | bash -s stable".format(installer=installer), runas=runas, - python_shell=True + python_shell=True, ) - if ret['retcode'] > 0: - msg = 'Error encountered while downloading the RVM installer' - if ret['stderr']: - msg += '. stderr follows:\n\n' + ret['stderr'] + if ret["retcode"] > 0: + msg = "Error encountered while downloading the RVM installer" + if ret["stderr"]: + msg += ". stderr follows:\n\n" + ret["stderr"] raise CommandExecutionError(msg) return True def install_ruby(ruby, runas=None, opts=None, env=None): - ''' + """ Install a ruby implementation. ruby @@ -131,7 +130,7 @@ def install_ruby(ruby, runas=None, opts=None, env=None): .. code-block:: bash salt '*' rvm.install_ruby 1.9.3-p385 - ''' + """ # MRI/RBX/REE dependencies for Ubuntu 10.04: # build-essential openssl libreadline6 libreadline6-dev curl # git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-0 @@ -140,14 +139,14 @@ def install_ruby(ruby, runas=None, opts=None, env=None): if opts is None: opts = [] - if runas and runas != 'root': - _rvm(['autolibs', 'disable', ruby] + opts, runas=runas) - opts.append('--disable-binary') - return _rvm(['install', ruby] + opts, runas=runas, env=env) + if runas and runas != "root": + _rvm(["autolibs", "disable", ruby] + opts, runas=runas) + opts.append("--disable-binary") + return _rvm(["install", ruby] + opts, runas=runas, env=env) def reinstall_ruby(ruby, runas=None, env=None): - ''' + """ Reinstall a ruby implementation ruby @@ -162,12 +161,12 @@ def reinstall_ruby(ruby, runas=None, env=None): .. code-block:: bash salt '*' rvm.reinstall_ruby 1.9.3-p385 - ''' - return _rvm(['reinstall', ruby], runas=runas, env=env) + """ + return _rvm(["reinstall", ruby], runas=runas, env=env) def list_(runas=None): - ''' + """ List all rvm-installed rubies runas @@ -179,22 +178,20 @@ def list_(runas=None): .. code-block:: bash salt '*' rvm.list - ''' + """ rubies = [] - output = _rvm(['list'], runas=runas) + output = _rvm(["list"], runas=runas) if output: - regex = re.compile(r'^[= ]([*> ]) ([^- ]+)-([^ ]+) \[ (.*) \]') + regex = re.compile(r"^[= ]([*> ]) ([^- ]+)-([^ ]+) \[ (.*) \]") for line in output.splitlines(): match = regex.match(line) if match: - rubies.append([ - match.group(2), match.group(3), match.group(1) == '*' - ]) + rubies.append([match.group(2), match.group(3), match.group(1) == "*"]) return rubies def set_default(ruby, runas=None): - ''' + """ Set the default ruby ruby @@ -209,12 +206,12 @@ def set_default(ruby, runas=None): .. code-block:: bash salt '*' rvm.set_default 2.0.0 - ''' - return _rvm(['alias', 'create', 'default', ruby], runas=runas) + """ + return _rvm(["alias", "create", "default", ruby], runas=runas) -def get(version='stable', runas=None): - ''' +def get(version="stable", runas=None): + """ Update RVM version : stable @@ -225,12 +222,12 @@ def get(version='stable', runas=None): .. code-block:: bash salt '*' rvm.get - ''' - return _rvm(['get', version], runas=runas) + """ + return _rvm(["get", version], runas=runas) def wrapper(ruby_string, wrapper_prefix, runas=None, *binaries): - ''' + """ Install RVM wrapper scripts ruby_string @@ -253,14 +250,14 @@ def wrapper(ruby_string, wrapper_prefix, runas=None, *binaries): .. code-block:: bash salt '*' rvm.wrapper - ''' - cmd = ['wrapper', ruby_string, wrapper_prefix] + """ + cmd = ["wrapper", ruby_string, wrapper_prefix] cmd.extend(binaries) return _rvm(cmd, runas=runas) def rubygems(ruby, version, runas=None): - ''' + """ Installs a specific rubygems version in the given ruby ruby @@ -279,12 +276,12 @@ def rubygems(ruby, version, runas=None): .. code-block:: bash salt '*' rvm.rubygems 2.0.0 1.8.24 - ''' - return _rvm_do(ruby, ['rubygems', version], runas=runas) + """ + return _rvm_do(ruby, ["rubygems", version], runas=runas) def gemset_create(ruby, gemset, runas=None): - ''' + """ Creates a gemset. ruby @@ -302,12 +299,12 @@ def gemset_create(ruby, gemset, runas=None): .. code-block:: bash salt '*' rvm.gemset_create 2.0.0 foobar - ''' - return _rvm_do(ruby, ['rvm', 'gemset', 'create', gemset], runas=runas) + """ + return _rvm_do(ruby, ["rvm", "gemset", "create", gemset], runas=runas) -def gemset_list(ruby='default', runas=None): - ''' +def gemset_list(ruby="default", runas=None): + """ List all gemsets for the given ruby. ruby : default @@ -322,11 +319,11 @@ def gemset_list(ruby='default', runas=None): .. code-block:: bash salt '*' rvm.gemset_list - ''' + """ gemsets = [] - output = _rvm_do(ruby, ['rvm', 'gemset', 'list'], runas=runas) + output = _rvm_do(ruby, ["rvm", "gemset", "list"], runas=runas) if output: - regex = re.compile('^ ([^ ]+)') + regex = re.compile("^ ([^ ]+)") for line in output.splitlines(): match = regex.match(line) if match: @@ -335,7 +332,7 @@ def gemset_list(ruby='default', runas=None): def gemset_delete(ruby, gemset, runas=None): - ''' + """ Delete a gemset ruby @@ -353,14 +350,12 @@ def gemset_delete(ruby, gemset, runas=None): .. code-block:: bash salt '*' rvm.gemset_delete 2.0.0 foobar - ''' - return _rvm_do(ruby, - ['rvm', '--force', 'gemset', 'delete', gemset], - runas=runas) + """ + return _rvm_do(ruby, ["rvm", "--force", "gemset", "delete", gemset], runas=runas) def gemset_empty(ruby, gemset, runas=None): - ''' + """ Remove all gems from a gemset. ruby @@ -378,14 +373,12 @@ def gemset_empty(ruby, gemset, runas=None): .. code-block:: bash salt '*' rvm.gemset_empty 2.0.0 foobar - ''' - return _rvm_do(ruby, - ['rvm', '--force', 'gemset', 'empty', gemset], - runas=runas) + """ + return _rvm_do(ruby, ["rvm", "--force", "gemset", "empty", gemset], runas=runas) def gemset_copy(source, destination, runas=None): - ''' + """ Copy all gems from one gemset to another. source @@ -403,12 +396,12 @@ def gemset_copy(source, destination, runas=None): .. code-block:: bash salt '*' rvm.gemset_copy foobar bazquo - ''' - return _rvm(['gemset', 'copy', source, destination], runas=runas) + """ + return _rvm(["gemset", "copy", source, destination], runas=runas) def gemset_list_all(runas=None): - ''' + """ List all gemsets for all installed rubies. Note that you must have set a default ruby before this can work. @@ -422,13 +415,13 @@ def gemset_list_all(runas=None): .. code-block:: bash salt '*' rvm.gemset_list_all - ''' + """ gemsets = {} current_ruby = None - output = _rvm_do('default', ['rvm', 'gemset', 'list_all'], runas=runas) + output = _rvm_do("default", ["rvm", "gemset", "list_all"], runas=runas) if output: - gems_regex = re.compile('^ ([^ ]+)') - gemset_regex = re.compile('^gemsets for ([^ ]+)') + gems_regex = re.compile("^ ([^ ]+)") + gemset_regex = re.compile("^gemsets for ([^ ]+)") for line in output.splitlines(): match = gemset_regex.match(line) if match: @@ -441,7 +434,7 @@ def gemset_list_all(runas=None): def do(ruby, command, runas=None, cwd=None, env=None): # pylint: disable=C0103 - ''' + """ Execute a command in an RVM controlled environment. ruby @@ -463,7 +456,7 @@ def do(ruby, command, runas=None, cwd=None, env=None): # pylint: disable=C0103 .. code-block:: bash salt '*' rvm.do 2.0.0 - ''' + """ try: command = salt.utils.args.shlex_split(command) except AttributeError: diff --git a/salt/modules/s3.py b/salt/modules/s3.py index d9508a6c786..e0e85ab56d7 100644 --- a/salt/modules/s3.py +++ b/salt/modules/s3.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon S3 .. highlight:: yaml @@ -70,8 +70,8 @@ Connection module for Amazon S3 .. highlight:: bash :depends: requests -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -80,16 +80,27 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Should work on any modern Python installation - ''' + """ return True -def delete(bucket, path=None, action=None, key=None, keyid=None, - service_url=None, verify_ssl=None, kms_keyid=None, location=None, - role_arn=None, path_style=None, https_enable=None): - ''' +def delete( + bucket, + path=None, + action=None, + key=None, + keyid=None, + service_url=None, + verify_ssl=None, + kms_keyid=None, + location=None, + role_arn=None, + path_style=None, + https_enable=None, +): + """ Delete a bucket, or delete an object from a bucket. @@ -100,8 +111,18 @@ def delete(bucket, path=None, action=None, key=None, keyid=None, CLI Example to delete an object from a bucket:: salt myminion s3.delete mybucket remoteobject - ''' - key, keyid, service_url, verify_ssl, kms_keyid, location, role_arn, path_style, https_enable = _get_key( + """ + ( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + role_arn, + path_style, + https_enable, + ) = _get_key( key, keyid, service_url, @@ -113,26 +134,40 @@ def delete(bucket, path=None, action=None, key=None, keyid=None, https_enable, ) - return __utils__['s3.query'](method='DELETE', - bucket=bucket, - path=path, - action=action, - key=key, - keyid=keyid, - kms_keyid=kms_keyid, - service_url=service_url, - verify_ssl=verify_ssl, - location=location, - role_arn=role_arn, - path_style=path_style, - https_enable=https_enable) + return __utils__["s3.query"]( + method="DELETE", + bucket=bucket, + path=path, + action=action, + key=key, + keyid=keyid, + kms_keyid=kms_keyid, + service_url=service_url, + verify_ssl=verify_ssl, + location=location, + role_arn=role_arn, + path_style=path_style, + https_enable=https_enable, + ) -def get(bucket='', path='', return_bin=False, action=None, - local_file=None, key=None, keyid=None, service_url=None, - verify_ssl=None, kms_keyid=None, location=None, role_arn=None, - path_style=None, https_enable=None): - ''' +def get( + bucket="", + path="", + return_bin=False, + action=None, + local_file=None, + key=None, + keyid=None, + service_url=None, + verify_ssl=None, + kms_keyid=None, + location=None, + role_arn=None, + path_style=None, + https_enable=None, +): + """ List the contents of a bucket, or return an object from a bucket. Set return_bin to True in order to retrieve an object wholesale. Otherwise, Salt will attempt to parse an XML response. @@ -172,8 +207,18 @@ def get(bucket='', path='', return_bin=False, action=None, To perform an action on a bucket: salt myminion s3.get mybucket myfile.png action=acl - ''' - key, keyid, service_url, verify_ssl, kms_keyid, location, role_arn, path_style, https_enable = _get_key( + """ + ( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + role_arn, + path_style, + https_enable, + ) = _get_key( key, keyid, service_url, @@ -185,35 +230,57 @@ def get(bucket='', path='', return_bin=False, action=None, https_enable, ) - return __utils__['s3.query'](method='GET', - bucket=bucket, - path=path, - return_bin=return_bin, - local_file=local_file, - action=action, - key=key, - keyid=keyid, - kms_keyid=kms_keyid, - service_url=service_url, - verify_ssl=verify_ssl, - location=location, - role_arn=role_arn, - path_style=path_style, - https_enable=https_enable) + return __utils__["s3.query"]( + method="GET", + bucket=bucket, + path=path, + return_bin=return_bin, + local_file=local_file, + action=action, + key=key, + keyid=keyid, + kms_keyid=kms_keyid, + service_url=service_url, + verify_ssl=verify_ssl, + location=location, + role_arn=role_arn, + path_style=path_style, + https_enable=https_enable, + ) -def head(bucket, path='', key=None, keyid=None, service_url=None, - verify_ssl=None, kms_keyid=None, location=None, role_arn=None, - path_style=None, https_enable=None): - ''' +def head( + bucket, + path="", + key=None, + keyid=None, + service_url=None, + verify_ssl=None, + kms_keyid=None, + location=None, + role_arn=None, + path_style=None, + https_enable=None, +): + """ Return the metadata for a bucket, or an object in a bucket. CLI Examples: salt myminion s3.head mybucket salt myminion s3.head mybucket myfile.png - ''' - key, keyid, service_url, verify_ssl, kms_keyid, location, role_arn, path_style, https_enable = _get_key( + """ + ( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + role_arn, + path_style, + https_enable, + ) = _get_key( key, keyid, service_url, @@ -225,26 +292,40 @@ def head(bucket, path='', key=None, keyid=None, service_url=None, https_enable, ) - return __utils__['s3.query'](method='HEAD', - bucket=bucket, - path=path, - key=key, - keyid=keyid, - kms_keyid=kms_keyid, - service_url=service_url, - verify_ssl=verify_ssl, - location=location, - full_headers=True, - role_arn=role_arn, - path_style=path_style, - https_enable=https_enable) + return __utils__["s3.query"]( + method="HEAD", + bucket=bucket, + path=path, + key=key, + keyid=keyid, + kms_keyid=kms_keyid, + service_url=service_url, + verify_ssl=verify_ssl, + location=location, + full_headers=True, + role_arn=role_arn, + path_style=path_style, + https_enable=https_enable, + ) -def put(bucket, path=None, return_bin=False, action=None, local_file=None, - key=None, keyid=None, service_url=None, verify_ssl=None, - kms_keyid=None, location=None, role_arn=None, path_style=None, - https_enable=None): - ''' +def put( + bucket, + path=None, + return_bin=False, + action=None, + local_file=None, + key=None, + keyid=None, + service_url=None, + verify_ssl=None, + kms_keyid=None, + location=None, + role_arn=None, + path_style=None, + https_enable=None, +): + """ Create a new bucket, or upload an object to a bucket. CLI Example to create a bucket: @@ -254,8 +335,18 @@ def put(bucket, path=None, return_bin=False, action=None, local_file=None, CLI Example to upload an object to a bucket: salt myminion s3.put mybucket remotepath local_file=/path/to/file - ''' - key, keyid, service_url, verify_ssl, kms_keyid, location, role_arn, path_style, https_enable = _get_key( + """ + ( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + role_arn, + path_style, + https_enable, + ) = _get_key( key, keyid, service_url, @@ -267,64 +358,89 @@ def put(bucket, path=None, return_bin=False, action=None, local_file=None, https_enable, ) - return __utils__['s3.query'](method='PUT', - bucket=bucket, - path=path, - return_bin=return_bin, - local_file=local_file, - action=action, - key=key, - keyid=keyid, - kms_keyid=kms_keyid, - service_url=service_url, - verify_ssl=verify_ssl, - location=location, - role_arn=role_arn, - path_style=path_style, - https_enable=https_enable) + return __utils__["s3.query"]( + method="PUT", + bucket=bucket, + path=path, + return_bin=return_bin, + local_file=local_file, + action=action, + key=key, + keyid=keyid, + kms_keyid=kms_keyid, + service_url=service_url, + verify_ssl=verify_ssl, + location=location, + role_arn=role_arn, + path_style=path_style, + https_enable=https_enable, + ) -def _get_key(key, keyid, service_url, verify_ssl, kms_keyid, location, role_arn, path_style, https_enable): - ''' +def _get_key( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + role_arn, + path_style, + https_enable, +): + """ Examine the keys, and populate as necessary - ''' - if not key and __salt__['config.option']('s3.key'): - key = __salt__['config.option']('s3.key') + """ + if not key and __salt__["config.option"]("s3.key"): + key = __salt__["config.option"]("s3.key") - if not keyid and __salt__['config.option']('s3.keyid'): - keyid = __salt__['config.option']('s3.keyid') + if not keyid and __salt__["config.option"]("s3.keyid"): + keyid = __salt__["config.option"]("s3.keyid") - if not kms_keyid and __salt__['config.option']('aws.kms.keyid'): - kms_keyid = __salt__['config.option']('aws.kms.keyid') + if not kms_keyid and __salt__["config.option"]("aws.kms.keyid"): + kms_keyid = __salt__["config.option"]("aws.kms.keyid") - if not service_url and __salt__['config.option']('s3.service_url'): - service_url = __salt__['config.option']('s3.service_url') + if not service_url and __salt__["config.option"]("s3.service_url"): + service_url = __salt__["config.option"]("s3.service_url") if not service_url: - service_url = 's3.amazonaws.com' + service_url = "s3.amazonaws.com" - if verify_ssl is None and __salt__['config.option']('s3.verify_ssl') is not None: - verify_ssl = __salt__['config.option']('s3.verify_ssl') + if verify_ssl is None and __salt__["config.option"]("s3.verify_ssl") is not None: + verify_ssl = __salt__["config.option"]("s3.verify_ssl") if verify_ssl is None: verify_ssl = True - if location is None and __salt__['config.option']('s3.location') is not None: - location = __salt__['config.option']('s3.location') + if location is None and __salt__["config.option"]("s3.location") is not None: + location = __salt__["config.option"]("s3.location") - if role_arn is None and __salt__['config.option']('s3.role_arn'): - role_arn = __salt__['config.option']('s3.role_arn') + if role_arn is None and __salt__["config.option"]("s3.role_arn"): + role_arn = __salt__["config.option"]("s3.role_arn") - if path_style is None and __salt__['config.option']('s3.path_style') is not None: - path_style = __salt__['config.option']('s3.path_style') + if path_style is None and __salt__["config.option"]("s3.path_style") is not None: + path_style = __salt__["config.option"]("s3.path_style") if path_style is None: path_style = False - if https_enable is None and __salt__['config.option']('s3.https_enable') is not None: - https_enable = __salt__['config.option']('s3.https_enable') + if ( + https_enable is None + and __salt__["config.option"]("s3.https_enable") is not None + ): + https_enable = __salt__["config.option"]("s3.https_enable") if https_enable is None: https_enable = True - return key, keyid, service_url, verify_ssl, kms_keyid, location, role_arn, path_style, https_enable + return ( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + role_arn, + path_style, + https_enable, + ) diff --git a/salt/modules/s6.py b/salt/modules/s6.py index f938ae4142b..5e24a2fa6bc 100644 --- a/salt/modules/s6.py +++ b/salt/modules/s6.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" s6 service module This module is compatible with the :mod:`service ` states, @@ -15,8 +15,8 @@ so it can be used to maintain services using the ``provider`` argument: Note that the ``enabled`` argument is not available with this provider. :codeauthor: Marek Skrobacki -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os @@ -25,13 +25,11 @@ import re # Import salt libs from salt.exceptions import CommandExecutionError -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} VALID_SERVICE_DIRS = [ - '/service', - '/etc/service', + "/service", + "/etc/service", ] SERVICE_DIR = None for service_dir in VALID_SERVICE_DIRS: @@ -41,16 +39,16 @@ for service_dir in VALID_SERVICE_DIRS: def _service_path(name): - ''' + """ build service path - ''' + """ if not SERVICE_DIR: raise CommandExecutionError("Could not find service directory.") - return '{0}/{1}'.format(SERVICE_DIR, name) + return "{0}/{1}".format(SERVICE_DIR, name) def start(name): - ''' + """ Starts service via s6 CLI Example: @@ -58,13 +56,13 @@ def start(name): .. code-block:: bash salt '*' s6.start - ''' - cmd = 's6-svc -u {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "s6-svc -u {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd) def stop(name): - ''' + """ Stops service via s6 CLI Example: @@ -72,13 +70,13 @@ def stop(name): .. code-block:: bash salt '*' s6.stop - ''' - cmd = 's6-svc -d {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "s6-svc -d {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd) def term(name): - ''' + """ Send a TERM to service via s6 CLI Example: @@ -86,13 +84,13 @@ def term(name): .. code-block:: bash salt '*' s6.term - ''' - cmd = 's6-svc -t {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "s6-svc -t {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd) def reload_(name): - ''' + """ Send a HUP to service via s6 CLI Example: @@ -100,13 +98,13 @@ def reload_(name): .. code-block:: bash salt '*' s6.reload - ''' - cmd = 's6-svc -h {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "s6-svc -h {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd) def restart(name): - ''' + """ Restart service via s6. This will stop/start service CLI Example: @@ -114,13 +112,13 @@ def restart(name): .. code-block:: bash salt '*' s6.restart - ''' - cmd = 's6-svc -t {0}'.format(_service_path(name)) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "s6-svc -t {0}".format(_service_path(name)) + return not __salt__["cmd.retcode"](cmd) def full_restart(name): - ''' + """ Calls s6.restart() function CLI Example: @@ -128,12 +126,12 @@ def full_restart(name): .. code-block:: bash salt '*' s6.full_restart - ''' + """ restart(name) def status(name, sig=None): - ''' + """ Return the status for a service via s6, return pid if running CLI Example: @@ -141,18 +139,18 @@ def status(name, sig=None): .. code-block:: bash salt '*' s6.status - ''' - cmd = 's6-svstat {0}'.format(_service_path(name)) - out = __salt__['cmd.run_stdout'](cmd) + """ + cmd = "s6-svstat {0}".format(_service_path(name)) + out = __salt__["cmd.run_stdout"](cmd) try: - pid = re.search(r'up \(pid (\d+)\)', out).group(1) + pid = re.search(r"up \(pid (\d+)\)", out).group(1) except AttributeError: - pid = '' + pid = "" return pid def available(name): - ''' + """ Returns ``True`` if the specified service is available, otherwise returns ``False``. @@ -161,12 +159,12 @@ def available(name): .. code-block:: bash salt '*' s6.available foo - ''' + """ return name in get_all() def missing(name): - ''' + """ The inverse of s6.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -176,12 +174,12 @@ def missing(name): .. code-block:: bash salt '*' s6.missing foo - ''' + """ return name not in get_all() def get_all(): - ''' + """ Return a list of all available services CLI Example: @@ -189,10 +187,10 @@ def get_all(): .. code-block:: bash salt '*' s6.get_all - ''' + """ if not SERVICE_DIR: raise CommandExecutionError("Could not find service directory.") - service_list = [dirname for dirname - in os.listdir(SERVICE_DIR) - if not dirname.startswith('.')] + service_list = [ + dirname for dirname in os.listdir(SERVICE_DIR) if not dirname.startswith(".") + ] return sorted(service_list) diff --git a/salt/modules/salt_proxy.py b/salt/modules/salt_proxy.py index 13bcd261550..c639c6c9282 100644 --- a/salt/modules/salt_proxy.py +++ b/salt/modules/salt_proxy.py @@ -1,51 +1,56 @@ # -*- coding: utf-8 -*- -''' +""" Salt proxy module .. versionadded:: 2015.8.3 Module to deploy and manage salt-proxy processes on a minion. -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import os -import logging +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt libs -import salt.utils.files -import salt.syspaths +import logging +import os # Import 3rd-party libs import salt.ext.six.moves +import salt.syspaths + +# Import Salt libs +import salt.utils.files log = logging.getLogger(__name__) def _write_proxy_conf(proxyfile): - ''' + """ write to file - ''' - msg = 'Invalid value for proxy file provided!, Supplied value = {0}' \ - .format(proxyfile) + """ + msg = "Invalid value for proxy file provided!, Supplied value = {0}".format( + proxyfile + ) - log.trace('Salt Proxy Module: write proxy conf') + log.trace("Salt Proxy Module: write proxy conf") if proxyfile: - log.debug('Writing proxy conf file') - with salt.utils.files.fopen(proxyfile, 'w') as proxy_conf: - proxy_conf.write(salt.utils.stringutils.to_str('master: {0}' - .format(__grains__['master']))) - msg = 'Wrote proxy file {0}'.format(proxyfile) + log.debug("Writing proxy conf file") + with salt.utils.files.fopen(proxyfile, "w") as proxy_conf: + proxy_conf.write( + salt.utils.stringutils.to_str( + "master: {0}".format(__grains__["master"]) + ) + ) + msg = "Wrote proxy file {0}".format(proxyfile) log.debug(msg) return msg def _proxy_conf_file(proxyfile, test): - ''' + """ Check if proxy conf exists and update - ''' + """ changes_old = [] changes_new = [] success = True @@ -53,64 +58,65 @@ def _proxy_conf_file(proxyfile, test): try: if not test: changes_new.append(_write_proxy_conf(proxyfile)) - msg = 'Salt Proxy: Wrote proxy conf {0}'.format(proxyfile) + msg = "Salt Proxy: Wrote proxy conf {0}".format(proxyfile) else: - msg = 'Salt Proxy: Update required to proxy conf {0}' \ - .format(proxyfile) + msg = "Salt Proxy: Update required to proxy conf {0}".format(proxyfile) except (OSError, IOError) as err: success = False - msg = 'Salt Proxy: Error writing proxy file {0}'.format(err) + msg = "Salt Proxy: Error writing proxy file {0}".format(err) log.error(msg) changes_new.append(msg) changes_new.append(msg) log.debug(msg) else: - msg = 'Salt Proxy: {0} already exists, skipping'.format(proxyfile) + msg = "Salt Proxy: {0} already exists, skipping".format(proxyfile) changes_old.append(msg) log.debug(msg) return success, changes_new, changes_old def _is_proxy_running(proxyname): - ''' + """ Check if proxy for this name is running - ''' - cmd = ('ps ax | grep "salt-proxy --proxyid={0}" | grep -v grep' - .format(salt.ext.six.moves.shlex_quote(proxyname))) - cmdout = __salt__['cmd.run_all']( - cmd, - timeout=5, - python_shell=True) - if not cmdout['stdout']: + """ + cmd = 'ps ax | grep "salt-proxy --proxyid={0}" | grep -v grep'.format( + salt.ext.six.moves.shlex_quote(proxyname) + ) + cmdout = __salt__["cmd.run_all"](cmd, timeout=5, python_shell=True) + if not cmdout["stdout"]: return False else: return True def _proxy_process(proxyname, test): - ''' + """ Check and execute proxy process - ''' + """ changes_old = [] changes_new = [] if not _is_proxy_running(proxyname): if not test: - __salt__['cmd.run_all']( - 'salt-proxy --proxyid={0} -l info -d'.format(salt.ext.six.moves.shlex_quote(proxyname)), - timeout=5) - changes_new.append('Salt Proxy: Started proxy process for {0}' - .format(proxyname)) + __salt__["cmd.run_all"]( + "salt-proxy --proxyid={0} -l info -d".format( + salt.ext.six.moves.shlex_quote(proxyname) + ), + timeout=5, + ) + changes_new.append( + "Salt Proxy: Started proxy process for {0}".format(proxyname) + ) else: - changes_new.append('Salt Proxy: process {0} will be started' - .format(proxyname)) + changes_new.append( + "Salt Proxy: process {0} will be started".format(proxyname) + ) else: - changes_old.append('Salt Proxy: already running for {0}' - .format(proxyname)) + changes_old.append("Salt Proxy: already running for {0}".format(proxyname)) return True, changes_new, changes_old def configure_proxy(proxyname, start=True): - ''' + """ Create the salt proxy file and start the proxy process if required @@ -126,14 +132,14 @@ def configure_proxy(proxyname, start=True): .. code-block:: bash salt deviceminion salt_proxy.configure_proxy p8000 - ''' + """ changes_new = [] changes_old = [] status_file = True - test = __opts__['test'] + test = __opts__["test"] # write the proxy file if necessary - proxyfile = os.path.join(salt.syspaths.CONFIG_DIR, 'proxy') + proxyfile = os.path.join(salt.syspaths.CONFIG_DIR, "proxy") status_file, msg_new, msg_old = _proxy_conf_file(proxyfile, test) changes_new.extend(msg_new) changes_old.extend(msg_old) @@ -145,20 +151,17 @@ def configure_proxy(proxyname, start=True): changes_old.extend(msg_old) changes_new.extend(msg_new) else: - changes_old.append('Start is False, not starting salt-proxy process') - log.debug('Process not started') + changes_old.append("Start is False, not starting salt-proxy process") + log.debug("Process not started") return { - 'result': status_file and status_proc, - 'changes': { - 'old': '\n'.join(changes_old), - 'new': '\n'.join(changes_new), - }, + "result": status_file and status_proc, + "changes": {"old": "\n".join(changes_old), "new": "\n".join(changes_new)}, } def is_running(proxyname): - ''' + """ Check if the salt-proxy process associated with this proxy (name) is running. @@ -174,5 +177,5 @@ def is_running(proxyname): .. code-block:: bash salt deviceminion salt_proxy.is_running p8000 - ''' - return {'result': _is_proxy_running(proxyname)} + """ + return {"result": _is_proxy_running(proxyname)} diff --git a/salt/modules/salt_version.py b/salt/modules/salt_version.py index d915f791a42..d5e6df510b9 100644 --- a/salt/modules/salt_version.py +++ b/salt/modules/salt_version.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Access Salt's elemental release code-names. .. versionadded:: 3000 @@ -29,32 +29,33 @@ A simple example might be something like the following: {% endif %} -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.versions +import salt.version + # Import Salt libs from salt.ext import six -import salt.version -import salt.utils.versions - log = logging.getLogger(__name__) -__virtualname__ = 'salt_version' +__virtualname__ = "salt_version" def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ return __virtualname__ def get_release_number(name): - ''' + """ Returns the release number of a given release code name in a ``MAJOR.PATCH`` format. @@ -70,28 +71,30 @@ def get_release_number(name): .. code-block:: bash salt '*' salt_version.get_release_number 'Oxygen' - ''' + """ name = name.lower() version_map = salt.version.SaltStackVersion.LNAMES version = version_map.get(name) if version is None: - log.info('Version {} not found.'.format(name)) + log.info("Version {} not found.".format(name)) return None try: if version[1] == 0: - log.info('Version {} found, but no release number has been assigned ' - 'yet.'.format(name)) - return 'No version assigned.' + log.info( + "Version {} found, but no release number has been assigned " + "yet.".format(name) + ) + return "No version assigned." except IndexError: # The new versioning scheme does not include minor version pass - return '.'.join(str(item) for item in version) + return ".".join(str(item) for item in version) def equal(name): - ''' + """ Returns a boolean (True) if the minion's current version code name matches the named version. @@ -103,18 +106,16 @@ def equal(name): .. code-block:: bash salt '*' salt_version.equal 'Oxygen' - ''' + """ if _check_release_cmp(name) == 0: - log.info( - 'The minion\'s version code name matches \'{}\'.'.format(name) - ) + log.info("The minion's version code name matches '{}'.".format(name)) return True return False def greater_than(name): - ''' + """ Returns a boolean (True) if the minion's current version code name is greater than the named version. @@ -126,18 +127,16 @@ def greater_than(name): .. code-block:: bash salt '*' salt_version.greater_than 'Sodium' - ''' + """ if _check_release_cmp(name) == 1: - log.info( - 'The minion\'s version code name is greater than \'{}\'.'.format(name) - ) + log.info("The minion's version code name is greater than '{}'.".format(name)) return True return False def less_than(name): - ''' + """ Returns a boolean (True) if the minion's current version code name is less than the named version. @@ -149,32 +148,31 @@ def less_than(name): .. code-block:: bash salt '*' salt_version.less_than 'Sodium' - ''' + """ if _check_release_cmp(name) == -1: - log.info( - 'The minion\'s version code name is less than \'{}\'.'.format(name) - ) + log.info("The minion's version code name is less than '{}'.".format(name)) return True return False def _check_release_cmp(name): - ''' + """ Helper function to compare the minion's current Salt version to release code name versions. If release code name isn't found, the function returns None. Otherwise, it returns the results of the version comparison as documented by the ``versions_cmp`` function in ``salt.utils.versions.py``. - ''' + """ map_version = get_release_number(name) if map_version is None: - log.info('Release code name {} was not found.'.format(name)) + log.info("Release code name {} was not found.".format(name)) return None - current_version = six.text_type(salt.version.SaltStackVersion( - *salt.version.__version_info__)) - current_version = current_version.rsplit('.', 1)[0] + current_version = six.text_type( + salt.version.SaltStackVersion(*salt.version.__version_info__) + ) + current_version = current_version.rsplit(".", 1)[0] version_cmp = salt.utils.versions.version_cmp(current_version, map_version) return version_cmp diff --git a/salt/modules/saltcheck.py b/salt/modules/saltcheck.py index f05002fa4be..a5fa25f3ed1 100644 --- a/salt/modules/saltcheck.py +++ b/salt/modules/saltcheck.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A module for testing the logic of states and highstates :codeauthor: William Cannon @@ -275,43 +275,45 @@ Supported assertions :ref:`ACL system ` ``saltcheck.*`` might provide more capability than intended if only ``saltcheck.run_state_tests`` and ``saltcheck.run_highstate_tests`` are needed. -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import copy import logging import os -import copy import time -from salt.utils.json import loads, dumps + +import salt.client +import salt.exceptions +import salt.utils.data # Import Salt libs import salt.utils.files import salt.utils.functools import salt.utils.path import salt.utils.yaml -import salt.utils.data -import salt.client -import salt.exceptions -from salt.utils.odict import OrderedDict -from salt.utils.decorators import memoize -from salt.ext import six from salt.defaults import DEFAULT_TARGET_DELIM +from salt.ext import six +from salt.utils.decorators import memoize +from salt.utils.json import dumps, loads +from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) -__virtualname__ = 'saltcheck' +__virtualname__ = "saltcheck" def __virtual__(): - ''' + """ Check dependencies - ''' + """ return __virtualname__ def run_test(**kwargs): - ''' + """ Execute one saltcheck test and return result :param keyword arg test: @@ -325,10 +327,10 @@ def run_test(**kwargs): "assertion": "assertEqual", "expected_return": "This works!", "args":["This works!"] }' - ''' + """ # salt converts the string to a dictionary auto-magically scheck = SaltCheck() - test = kwargs.get('test', None) + test = kwargs.get("test", None) if test and isinstance(test, dict): return scheck.run_test(test) else: @@ -336,7 +338,7 @@ def run_test(**kwargs): def state_apply(state_name, **kwargs): - ''' + """ Runs :py:func:`state.apply ` with given options to set up test data. Intended to be used for optional test setup or teardown @@ -347,25 +349,25 @@ def state_apply(state_name, **kwargs): .. code-block:: bash salt '*' saltcheck.state_apply postfix - ''' + """ # A new salt client is instantiated with the default configuration because the main module's # client is hardcoded to local # minion is running with a master, a potentially non-local client is needed to lookup states - conf_file = copy.deepcopy(__opts__['conf_file']) + conf_file = copy.deepcopy(__opts__["conf_file"]) local_opts = salt.config.minion_config(conf_file) - if 'running_data/var/run/salt-minion.pid' in __opts__.get('pidfile', False): + if "running_data/var/run/salt-minion.pid" in __opts__.get("pidfile", False): # Force salt-ssh minions to use local - local_opts['file_client'] = 'local' - log.debug('Detected salt-ssh, running as local') + local_opts["file_client"] = "local" + log.debug("Detected salt-ssh, running as local") caller = salt.client.Caller(mopts=local_opts) if kwargs: - return caller.cmd('state.apply', state_name, **kwargs) + return caller.cmd("state.apply", state_name, **kwargs) else: - return caller.cmd('state.apply', state_name) + return caller.cmd("state.apply", state_name) def report_highstate_tests(saltenv=None): - ''' + """ Report on tests for states assigned to the minion through highstate. Quits with the exit code for the number of missing tests. @@ -376,12 +378,12 @@ def report_highstate_tests(saltenv=None): salt '*' saltcheck.report_highstate_tests .. versionadded:: 3000 - ''' + """ if not saltenv: - if 'saltenv' in __opts__ and __opts__['saltenv']: - saltenv = __opts__['saltenv'] + if "saltenv" in __opts__ and __opts__["saltenv"]: + saltenv = __opts__["saltenv"] else: - saltenv = 'base' + saltenv = "base" sls_list = [] sls_list = _get_top_states(saltenv) @@ -393,17 +395,18 @@ def report_highstate_tests(saltenv=None): if state_name not in stl.found_states: missing_tests = missing_tests + 1 states_missing_tests.append(state_name) - __context__['retcode'] = missing_tests - return {'TEST REPORT RESULTS': { - 'Missing Tests': missing_tests, - 'States missing tests': states_missing_tests, - 'States with tests': stl.found_states - } - } + __context__["retcode"] = missing_tests + return { + "TEST REPORT RESULTS": { + "Missing Tests": missing_tests, + "States missing tests": states_missing_tests, + "States with tests": stl.found_states, + } + } def run_state_tests(state, saltenv=None, check_all=False): - ''' + """ Execute tests for a salt state and return results Nested states will also be tested @@ -416,12 +419,12 @@ def run_state_tests(state, saltenv=None, check_all=False): .. code-block:: bash salt '*' saltcheck.run_state_tests postfix,common - ''' + """ if not saltenv: - if 'saltenv' in __opts__ and __opts__['saltenv']: - saltenv = __opts__['saltenv'] + if "saltenv" in __opts__ and __opts__["saltenv"]: + saltenv = __opts__["saltenv"] else: - saltenv = 'base' + saltenv = "base" scheck = SaltCheck(saltenv) stl = StateTestLoader(saltenv) results = OrderedDict() @@ -439,11 +442,13 @@ def run_state_tests(state, saltenv=None, check_all=False): return _generate_out_list(results) -run_state_tests_ssh = salt.utils.functools.alias_function(run_state_tests, 'run_state_tests_ssh') +run_state_tests_ssh = salt.utils.functools.alias_function( + run_state_tests, "run_state_tests_ssh" +) def run_highstate_tests(saltenv=None): - ''' + """ Execute all tests for states assigned to the minion through highstate and return results CLI Example: @@ -451,23 +456,23 @@ def run_highstate_tests(saltenv=None): .. code-block:: bash salt '*' saltcheck.run_highstate_tests - ''' + """ if not saltenv: - if 'saltenv' in __opts__ and __opts__['saltenv']: - saltenv = __opts__['saltenv'] + if "saltenv" in __opts__ and __opts__["saltenv"]: + saltenv = __opts__["saltenv"] else: - saltenv = 'base' + saltenv = "base" sls_list = [] sls_list = _get_top_states(saltenv) - all_states = ','.join(sls_list) + all_states = ",".join(sls_list) return run_state_tests(all_states, saltenv=saltenv) def _generate_out_list(results): - ''' + """ generate test results output list - ''' + """ passed = 0 failed = 0 skipped = 0 @@ -479,88 +484,96 @@ def _generate_out_list(results): else: for dummy, val in results[state].items(): log.info("dummy=%s, val=%s", dummy, val) - if val['status'].startswith('Pass'): + if val["status"].startswith("Pass"): passed = passed + 1 - if val['status'].startswith('Fail'): + if val["status"].startswith("Fail"): failed = failed + 1 - if val['status'].startswith('Skip'): + if val["status"].startswith("Skip"): skipped = skipped + 1 - total_time = total_time + float(val['duration']) + total_time = total_time + float(val["duration"]) out_list = [] for key, value in results.items(): out_list.append({key: value}) out_list = sorted(out_list, key=lambda x: sorted(x.keys())) - out_list.append({'TEST RESULTS': {'Execution Time': round(total_time, 4), - 'Passed': passed, 'Failed': failed, 'Skipped': skipped, - 'Missing Tests': missing_tests}}) + out_list.append( + { + "TEST RESULTS": { + "Execution Time": round(total_time, 4), + "Passed": passed, + "Failed": failed, + "Skipped": skipped, + "Missing Tests": missing_tests, + } + } + ) # Set exist code to 1 if failed tests # Use-cases for exist code handling of missing or skipped? - __context__['retcode'] = (1 if failed else 0) + __context__["retcode"] = 1 if failed else 0 return out_list def _render_file(file_path): - ''' + """ call the salt utility to render a file - ''' + """ # salt-call slsutil.renderer /srv/salt/jinjatest/saltcheck-tests/test1.tst - rendered = __salt__['slsutil.renderer'](file_path) + rendered = __salt__["slsutil.renderer"](file_path) log.info("rendered: %s", rendered) return rendered @memoize def _is_valid_module(module): - ''' + """ Return a list of all modules available on minion - ''' - modules = __salt__['sys.list_modules']() + """ + modules = __salt__["sys.list_modules"]() return bool(module in modules) @memoize def _is_valid_function(module_name, function): - ''' + """ Determine if a function is valid for a module - ''' + """ try: - functions = __salt__['sys.list_functions'](module_name) + functions = __salt__["sys.list_functions"](module_name) except salt.exceptions.SaltException: functions = ["unable to look up functions"] return "{0}.{1}".format(module_name, function) in functions -def _get_top_states(saltenv='base'): - ''' +def _get_top_states(saltenv="base"): + """ Equivalent to a salt cli: salt web state.show_top - ''' + """ top_states = [] - top_states = __salt__['state.show_top']()[saltenv] - log.debug('saltcheck for saltenv: %s found top states: %s', saltenv, top_states) + top_states = __salt__["state.show_top"]()[saltenv] + log.debug("saltcheck for saltenv: %s found top states: %s", saltenv, top_states) return top_states class SaltCheck(object): - ''' + """ This class validates and runs the saltchecks - ''' + """ - def __init__(self, saltenv='base'): + def __init__(self, saltenv="base"): self.sls_list_state = [] self.modules = [] self.results_dict = {} self.results_dict_summary = {} self.saltenv = saltenv - self.assertions_list = '''assertEqual assertNotEqual + self.assertions_list = """assertEqual assertNotEqual assertTrue assertFalse assertIn assertNotIn assertGreater assertGreaterEqual assertLess assertLessEqual - assertEmpty assertNotEmpty'''.split() + assertEmpty assertNotEmpty""".split() def __is_valid_test(self, test_dict): - ''' + """ Determine if a test contains: - a test name @@ -570,77 +583,88 @@ class SaltCheck(object): 6 points needed for standard test 4 points needed for test with assertion not requiring expected return - ''' + """ test_errors = [] tots = 0 # need total of >= 6 to be a valid test - skip = test_dict.get('skip', False) - m_and_f = test_dict.get('module_and_function', None) - assertion = test_dict.get('assertion', None) + skip = test_dict.get("skip", False) + m_and_f = test_dict.get("module_and_function", None) + assertion = test_dict.get("assertion", None) # support old expected-return and newer name normalized expected_return - exp_ret_key = any(key in test_dict.keys() for key in ['expected_return', 'expected-return']) - exp_ret_val = test_dict.get('expected_return', test_dict.get('expected-return', None)) + exp_ret_key = any( + key in test_dict.keys() for key in ["expected_return", "expected-return"] + ) + exp_ret_val = test_dict.get( + "expected_return", test_dict.get("expected-return", None) + ) log.info("__is_valid_test has test: %s", test_dict) if skip: required_total = 0 elif m_and_f in ["saltcheck.state_apply"]: required_total = 2 - elif assertion in ["assertEmpty", - "assertNotEmpty", - "assertTrue", - "assertFalse"]: + elif assertion in [ + "assertEmpty", + "assertNotEmpty", + "assertTrue", + "assertFalse", + ]: required_total = 4 else: required_total = 6 if m_and_f: tots += 1 - module, function = m_and_f.split('.') + module, function = m_and_f.split(".") if _is_valid_module(module): tots += 1 else: - test_errors.append('{0} is not a valid module'.format(module)) + test_errors.append("{0} is not a valid module".format(module)) if _is_valid_function(module, function): tots += 1 else: - test_errors.append('{0} is not a valid function'.format(function)) + test_errors.append("{0} is not a valid function".format(function)) log.info("__is_valid_test has valid m_and_f") if assertion in self.assertions_list: log.info("__is_valid_test has valid_assertion") tots += 1 else: - test_errors.append('{0} is not in the assertions list'.format(assertion)) + test_errors.append("{0} is not in the assertions list".format(assertion)) if exp_ret_key: tots += 1 else: - test_errors.append('No expected return key') + test_errors.append("No expected return key") if exp_ret_val is not None: tots += 1 else: - test_errors.append('expected_return does not have a value') + test_errors.append("expected_return does not have a value") # log the test score for debug purposes log.info("__test score: %s and required: %s", tots, required_total) if not tots >= required_total: - log.error('Test failed with the following test verifications: %s', ', '.join(test_errors)) + log.error( + "Test failed with the following test verifications: %s", + ", ".join(test_errors), + ) return tots >= required_total - def _call_salt_command(self, - fun, - args, - kwargs, - assertion_section=None, - assertion_section_delimiter=DEFAULT_TARGET_DELIM): - ''' + def _call_salt_command( + self, + fun, + args, + kwargs, + assertion_section=None, + assertion_section_delimiter=DEFAULT_TARGET_DELIM, + ): + """ Generic call of salt Caller command - ''' - conf_file = __opts__['conf_file'] + """ + conf_file = __opts__["conf_file"] local_opts = salt.config.minion_config(conf_file) # Save orginal file_client to restore after salt.client.Caller run - orig_file_client = local_opts['file_client'] + orig_file_client = local_opts["file_client"] mlocal_opts = copy.deepcopy(local_opts) - mlocal_opts['file_client'] = 'local' + mlocal_opts["file_client"] = "local" value = False if args and kwargs: value = salt.client.Caller(mopts=mlocal_opts).cmd(fun, *args, **kwargs) @@ -650,63 +674,87 @@ class SaltCheck(object): value = salt.client.Caller(mopts=mlocal_opts).cmd(fun, **kwargs) else: value = salt.client.Caller(mopts=mlocal_opts).cmd(fun) - __opts__['file_client'] = orig_file_client + __opts__["file_client"] = orig_file_client if isinstance(value, dict) and assertion_section: - return_value = salt.utils.data.traverse_dict_and_list(value, - assertion_section, - default=False, - delimiter=assertion_section_delimiter) + return_value = salt.utils.data.traverse_dict_and_list( + value, + assertion_section, + default=False, + delimiter=assertion_section_delimiter, + ) return return_value else: return value def run_test(self, test_dict): - ''' + """ Run a single saltcheck test - ''' + """ start = time.time() - global_output_details = __salt__['config.get']('saltcheck_output_details', False) - output_details = test_dict.get('output_details', global_output_details) + global_output_details = __salt__["config.get"]( + "saltcheck_output_details", False + ) + output_details = test_dict.get("output_details", global_output_details) if self.__is_valid_test(test_dict): - skip = test_dict.get('skip', False) + skip = test_dict.get("skip", False) if skip: - return {'status': 'Skip', 'duration': 0.0} - mod_and_func = test_dict['module_and_function'] - assertion_section = test_dict.get('assertion_section', None) - assertion_section_delimiter = test_dict.get('assertion_section_delimiter', DEFAULT_TARGET_DELIM) - args = test_dict.get('args', None) - kwargs = test_dict.get('kwargs', None) - pillar_data = test_dict.get('pillar_data', test_dict.get('pillar-data', None)) + return {"status": "Skip", "duration": 0.0} + mod_and_func = test_dict["module_and_function"] + assertion_section = test_dict.get("assertion_section", None) + assertion_section_delimiter = test_dict.get( + "assertion_section_delimiter", DEFAULT_TARGET_DELIM + ) + args = test_dict.get("args", None) + kwargs = test_dict.get("kwargs", None) + pillar_data = test_dict.get( + "pillar_data", test_dict.get("pillar-data", None) + ) if pillar_data: if not kwargs: kwargs = {} - kwargs['pillar'] = pillar_data + kwargs["pillar"] = pillar_data else: # make sure we clean pillar from previous test if kwargs: - kwargs.pop('pillar', None) + kwargs.pop("pillar", None) if mod_and_func in ["saltcheck.state_apply"]: assertion = "assertNotEmpty" else: - assertion = test_dict['assertion'] - expected_return = test_dict.get('expected_return', test_dict.get('expected-return', None)) - assert_print_result = test_dict.get('print_result', True) + assertion = test_dict["assertion"] + expected_return = test_dict.get( + "expected_return", test_dict.get("expected-return", None) + ) + assert_print_result = test_dict.get("print_result", True) - actual_return = self._call_salt_command(mod_and_func, - args, - kwargs, - assertion_section, - assertion_section_delimiter) - if assertion not in ["assertIn", "assertNotIn", "assertEmpty", "assertNotEmpty", - "assertTrue", "assertFalse"]: - expected_return = self._cast_expected_to_returned_type(expected_return, actual_return) + actual_return = self._call_salt_command( + mod_and_func, + args, + kwargs, + assertion_section, + assertion_section_delimiter, + ) + if assertion not in [ + "assertIn", + "assertNotIn", + "assertEmpty", + "assertNotEmpty", + "assertTrue", + "assertFalse", + ]: + expected_return = self._cast_expected_to_returned_type( + expected_return, actual_return + ) if assertion == "assertEqual": assertion_desc = "==" - value = self.__assert_equal(expected_return, actual_return, assert_print_result) + value = self.__assert_equal( + expected_return, actual_return, assert_print_result + ) elif assertion == "assertNotEqual": assertion_desc = "!=" - value = self.__assert_not_equal(expected_return, actual_return, assert_print_result) + value = self.__assert_not_equal( + expected_return, actual_return, assert_print_result + ) elif assertion == "assertTrue": assertion_desc = "True is" value = self.__assert_true(actual_return) @@ -715,10 +763,14 @@ class SaltCheck(object): value = self.__assert_false(actual_return) elif assertion == "assertIn": assertion_desc = "IN" - value = self.__assert_in(expected_return, actual_return, assert_print_result) + value = self.__assert_in( + expected_return, actual_return, assert_print_result + ) elif assertion == "assertNotIn": assertion_desc = "NOT IN" - value = self.__assert_not_in(expected_return, actual_return, assert_print_result) + value = self.__assert_not_in( + expected_return, actual_return, assert_print_result + ) elif assertion == "assertGreater": assertion_desc = ">" value = self.__assert_greater(expected_return, actual_return) @@ -743,37 +795,35 @@ class SaltCheck(object): value = "Fail - invalid test" end = time.time() result = {} - result['status'] = value + result["status"] = value if output_details: if assertion_section: - assertion_section_repr_title = '.{0}'.format('assertion_section') - assertion_section_repr_value = '.{0}'.format(assertion_section) + assertion_section_repr_title = ".{0}".format("assertion_section") + assertion_section_repr_value = ".{0}".format(assertion_section) else: - assertion_section_repr_title = '' - assertion_section_repr_value = '' - result['module.function [args]{0}'.format( - assertion_section_repr_title - )] = '{0} {1}{2}'.format( - mod_and_func, - dumps(args), - assertion_section_repr_value, + assertion_section_repr_title = "" + assertion_section_repr_value = "" + result[ + "module.function [args]{0}".format(assertion_section_repr_title) + ] = "{0} {1}{2}".format( + mod_and_func, dumps(args), assertion_section_repr_value, ) - result['saltcheck assertion'] = '{0}{1} {2}'.format( - ('' if expected_return is None else '{0} '.format(expected_return)), + result["saltcheck assertion"] = "{0}{1} {2}".format( + ("" if expected_return is None else "{0} ".format(expected_return)), assertion_desc, - ('hidden' if not assert_print_result else actual_return) + ("hidden" if not assert_print_result else actual_return), ) - result['duration'] = round(end - start, 4) + result["duration"] = round(end - start, 4) return result @staticmethod def _cast_expected_to_returned_type(expected, returned): - ''' + """ Determine the type of variable returned Cast the expected to the type of variable returned - ''' + """ new_expected = expected if returned is not None: ret_type = type(returned) @@ -791,191 +841,201 @@ class SaltCheck(object): @staticmethod def __assert_equal(expected, returned, assert_print_result=True): - ''' + """ Test if two objects are equal - ''' + """ result = "Pass" try: if assert_print_result: - assert (expected == returned), "{0} is not equal to {1}".format(expected, returned) + assert expected == returned, "{0} is not equal to {1}".format( + expected, returned + ) else: - assert (expected == returned), "Result is not equal" + assert expected == returned, "Result is not equal" except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_not_equal(expected, returned, assert_print_result=True): - ''' + """ Test if two objects are not equal - ''' + """ result = "Pass" try: if assert_print_result: - assert (expected != returned), "{0} is equal to {1}".format(expected, returned) + assert expected != returned, "{0} is equal to {1}".format( + expected, returned + ) else: - assert (expected != returned), "Result is equal" + assert expected != returned, "Result is equal" except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_true(returned): - ''' + """ Test if an boolean is True - ''' + """ result = "Pass" try: - assert (returned is True), "{0} not True".format(returned) + assert returned is True, "{0} not True".format(returned) except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_false(returned): - ''' + """ Test if an boolean is False - ''' + """ result = "Pass" if isinstance(returned, str): returned = bool(returned) try: - assert (returned is False), "{0} not False".format(returned) + assert returned is False, "{0} not False".format(returned) except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_in(expected, returned, assert_print_result=True): - ''' + """ Test if a value is in the list of returned values - ''' + """ result = "Pass" try: if assert_print_result: - assert (expected in returned), "{0} not found in {1}".format(expected, returned) + assert expected in returned, "{0} not found in {1}".format( + expected, returned + ) else: - assert (expected in returned), "Result not found" + assert expected in returned, "Result not found" except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_not_in(expected, returned, assert_print_result=True): - ''' + """ Test if a value is not in the list of returned values - ''' + """ result = "Pass" try: if assert_print_result: - assert (expected not in returned), "{0} was found in {1}".format(expected, returned) + assert expected not in returned, "{0} was found in {1}".format( + expected, returned + ) else: - assert (expected not in returned), "Result was found" + assert expected not in returned, "Result was found" except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_greater(expected, returned): - ''' + """ Test if a value is greater than the returned value - ''' + """ result = "Pass" try: - assert (expected > returned), "{0} not False".format(returned) + assert expected > returned, "{0} not False".format(returned) except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_greater_equal(expected, returned): - ''' + """ Test if a value is greater than or equal to the returned value - ''' + """ result = "Pass" try: - assert (expected >= returned), "{0} not False".format(returned) + assert expected >= returned, "{0} not False".format(returned) except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_less(expected, returned): - ''' + """ Test if a value is less than the returned value - ''' + """ result = "Pass" try: - assert (expected < returned), "{0} not False".format(returned) + assert expected < returned, "{0} not False".format(returned) except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_less_equal(expected, returned): - ''' + """ Test if a value is less than or equal to the returned value - ''' + """ result = "Pass" try: - assert (expected <= returned), "{0} not False".format(returned) + assert expected <= returned, "{0} not False".format(returned) except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_empty(returned): - ''' + """ Test if a returned value is empty - ''' + """ result = "Pass" try: - assert (not returned), "{0} is not empty".format(returned) + assert not returned, "{0} is not empty".format(returned) except AssertionError as err: result = "Fail: " + six.text_type(err) return result @staticmethod def __assert_not_empty(returned): - ''' + """ Test if a returned value is not empty - ''' + """ result = "Pass" try: - assert (returned), "value is empty" + assert returned, "value is empty" except AssertionError as err: result = "Fail: " + six.text_type(err) return result class StateTestLoader(object): - ''' + """ Class loads in test files for a state e.g. state_dir/saltcheck-tests/[1.tst, 2.tst, 3.tst] - ''' + """ - def __init__(self, saltenv='base'): + def __init__(self, saltenv="base"): self.path_type = None self.test_files = set([]) # list of file paths self.test_dict = OrderedDict() self.saltenv = saltenv - self.saltcheck_test_location = __salt__['config.get']('saltcheck_test_location', 'saltcheck-tests') + self.saltcheck_test_location = __salt__["config.get"]( + "saltcheck_test_location", "saltcheck-tests" + ) self.found_states = [] def load_test_suite(self): - ''' + """ Load tests either from one file, or a set of files - ''' + """ self.test_dict = OrderedDict() for myfile in self.test_files: self._load_file_salt_rendered(myfile) self.test_files = set([]) def _load_file_salt_rendered(self, filepath): - ''' + """ loads in one test file - ''' + """ # use the salt renderer module to interpret jinja and etc tests = _render_file(filepath) # use json as a convenient way to convert the OrderedDicts from salt renderer @@ -985,30 +1045,30 @@ class StateTestLoader(object): return def _copy_state_files(self, sls_path, state_name, check_all): - ''' + """ Copy tst files for a given path and return results of the copy. If check_all is enabled, also add all tests found - ''' + """ cache_ret = [] if state_name not in self.found_states: - log.debug('looking in %s to cache tests', sls_path) - cache_ret = __salt__['cp.cache_dir'](sls_path, - saltenv=self.saltenv, - include_pat='*.tst') + log.debug("looking in %s to cache tests", sls_path) + cache_ret = __salt__["cp.cache_dir"]( + sls_path, saltenv=self.saltenv, include_pat="*.tst" + ) if cache_ret: if check_all: log.debug("Adding all found test files: %s", cache_ret) self.test_files.update(cache_ret) else: - log.debug('Marking found_state: %s', state_name) + log.debug("Marking found_state: %s", state_name) self.found_states.append(state_name) else: - log.debug('Not copying already found_state: %s', self.found_states) + log.debug("Not copying already found_state: %s", self.found_states) return cache_ret def _generate_sls_path(self, state_name): - ''' + """ For a given state_name, return list of paths to search for .tst files possible formula paths are then @@ -1022,22 +1082,28 @@ class StateTestLoader(object): path/to/forumla.sls with tests of path/saltcheck_test_location/init.tst - ''' + """ all_sls_paths = [] # process /patch/to/formula/saltcheck_test_location - test_path = 'salt://{0}/{1}'.format(state_name.replace('.', '/'), self.saltcheck_test_location) + test_path = "salt://{0}/{1}".format( + state_name.replace(".", "/"), self.saltcheck_test_location + ) all_sls_paths.append(test_path) # process /path/to/saltcheck_test_location - sls_split = state_name.split('.') + sls_split = state_name.split(".") sls_split.pop() - test_path = 'salt://{0}/{1}'.format('/'.join(sls_split), self.saltcheck_test_location) + test_path = "salt://{0}/{1}".format( + "/".join(sls_split), self.saltcheck_test_location + ) all_sls_paths.append(test_path) - state_name_base = state_name.split('.')[0] - test_path = 'salt://{0}/{1}'.format(state_name_base, self.saltcheck_test_location) + state_name_base = state_name.split(".")[0] + test_path = "salt://{0}/{1}".format( + state_name_base, self.saltcheck_test_location + ) all_sls_paths.append(test_path) unique_paths = set(all_sls_paths) @@ -1047,21 +1113,23 @@ class StateTestLoader(object): @memoize def _get_states(self): - ''' + """ Returns (cached) list of states for the minion - ''' - return __salt__['cp.list_states'](saltenv=self.saltenv) + """ + return __salt__["cp.list_states"](saltenv=self.saltenv) def add_test_files_for_sls(self, sls_name, check_all=False): - ''' + """ Detects states used, caches needed files, and adds to test list - ''' + """ salt_ssh = False - if 'running_data/var/run/salt-minion.pid' in __opts__.get('pidfile', False): + if "running_data/var/run/salt-minion.pid" in __opts__.get("pidfile", False): salt_ssh = True - log.debug('Running on salt-ssh minion. Reading file %s', sls_name) - cp_output_file = os.path.join(__opts__['cachedir'], 'files', self.saltenv, 'cp_output.txt') - with salt.utils.files.fopen(cp_output_file, 'r') as fp: + log.debug("Running on salt-ssh minion. Reading file %s", sls_name) + cp_output_file = os.path.join( + __opts__["cachedir"], "files", self.saltenv, "cp_output.txt" + ) + with salt.utils.files.fopen(cp_output_file, "r") as fp: all_states = loads(salt.utils.stringutils.to_unicode(fp.read())) else: all_states = self._get_states() @@ -1070,72 +1138,102 @@ class StateTestLoader(object): cached_copied_files = [] if salt_ssh: # populate cached_copied_files from sent over file rather than attempting to run cp.cache_dir later - log.debug('Running on salt-ssh minion. Populating test file results') - state_copy_file = os.path.join(__opts__['cachedir'], 'files', self.saltenv, sls_name + '.copy') + log.debug("Running on salt-ssh minion. Populating test file results") + state_copy_file = os.path.join( + __opts__["cachedir"], "files", self.saltenv, sls_name + ".copy" + ) try: - with salt.utils.files.fopen(state_copy_file, 'r') as fp: - cached_copied_files.extend(loads(salt.utils.stringutils.to_unicode(fp.read()))) + with salt.utils.files.fopen(state_copy_file, "r") as fp: + cached_copied_files.extend( + loads(salt.utils.stringutils.to_unicode(fp.read())) + ) except IOError: # likely attempting to find state.nested.copy when file was sent as just state.copy - sls_name_list = sls_name.split('.') - sls_root_name = '.'.join(sls_name_list[:-1]) - state_copy_file = os.path.join(__opts__['cachedir'], 'files', self.saltenv, sls_root_name + '.copy') - with salt.utils.files.fopen(state_copy_file, 'r') as fp: - cached_copied_files.extend(loads(salt.utils.stringutils.to_unicode(fp.read()))) + sls_name_list = sls_name.split(".") + sls_root_name = ".".join(sls_name_list[:-1]) + state_copy_file = os.path.join( + __opts__["cachedir"], "files", self.saltenv, sls_root_name + ".copy" + ) + with salt.utils.files.fopen(state_copy_file, "r") as fp: + cached_copied_files.extend( + loads(salt.utils.stringutils.to_unicode(fp.read())) + ) if sls_name in all_states: if salt_ssh: - log.debug('Running on salt-ssh minion. Reading file %s', sls_name + '.low') - state_low_file = os.path.join(__opts__['cachedir'], 'files', self.saltenv, sls_name + '.low') - with salt.utils.files.fopen(state_low_file, 'r') as fp: + log.debug( + "Running on salt-ssh minion. Reading file %s", sls_name + ".low" + ) + state_low_file = os.path.join( + __opts__["cachedir"], "files", self.saltenv, sls_name + ".low" + ) + with salt.utils.files.fopen(state_low_file, "r") as fp: ret = loads(salt.utils.stringutils.to_unicode(fp.read())) else: - ret = __salt__['state.show_low_sls'](sls_name, saltenv=self.saltenv, test=True) + ret = __salt__["state.show_low_sls"]( + sls_name, saltenv=self.saltenv, test=True + ) else: # passed name isn't a state, so we'll assume it is a test definition - ret = [{'__sls__': sls_name}] + ret = [{"__sls__": sls_name}] for low_data in ret: if not isinstance(low_data, dict): - log.error('low data from show_low_sls is not formed as a dict: %s', low_data) + log.error( + "low data from show_low_sls is not formed as a dict: %s", low_data + ) return this_cache_ret = None - if '__sls__' in low_data: + if "__sls__" in low_data: # this low data has an SLS path in it - state_name = low_data['__sls__'] + state_name = low_data["__sls__"] for sls_path in self._generate_sls_path(state_name): - this_cache_ret = self._copy_state_files(sls_path, state_name, check_all) + this_cache_ret = self._copy_state_files( + sls_path, state_name, check_all + ) if this_cache_ret: - log.debug('found tests: %s', this_cache_ret) + log.debug("found tests: %s", this_cache_ret) cached_copied_files.extend(this_cache_ret) if salt_ssh: if check_all: # load all tests for this state on ssh minion - tst_files = [file_string for file_string in cached_copied_files if file_string.endswith('.tst')] + tst_files = [ + file_string + for file_string in cached_copied_files + if file_string.endswith(".tst") + ] self.test_files.update(tst_files) if not check_all: # in check_all case, tests already added - split_sls = low_data['__sls__'].split('.') - sls_path_names = set([ - os.path.join(os.sep.join(split_sls), - os.path.normpath(self.saltcheck_test_location), - 'init.tst'), - os.path.join(os.sep.join(split_sls[:len(split_sls) - 1]), - os.path.normpath(self.saltcheck_test_location), - '{0}.tst'.format(split_sls[-1])), - os.path.join(split_sls[0], - os.path.normpath(self.saltcheck_test_location), - os.sep.join(split_sls[1:-1]), - '{0}.tst'.format(split_sls[-1])) - ]) + split_sls = low_data["__sls__"].split(".") + sls_path_names = set( + [ + os.path.join( + os.sep.join(split_sls), + os.path.normpath(self.saltcheck_test_location), + "init.tst", + ), + os.path.join( + os.sep.join(split_sls[: len(split_sls) - 1]), + os.path.normpath(self.saltcheck_test_location), + "{0}.tst".format(split_sls[-1]), + ), + os.path.join( + split_sls[0], + os.path.normpath(self.saltcheck_test_location), + os.sep.join(split_sls[1:-1]), + "{0}.tst".format(split_sls[-1]), + ), + ] + ) # for this state, find matching test files and load them cached_copied_files = list(set(cached_copied_files)) for this_cached_test_file in cached_copied_files: if this_cached_test_file.endswith(tuple(sls_path_names)): self.test_files.add(this_cached_test_file) cached_copied_files.remove(this_cached_test_file) - log.debug('Adding .tst file: %s', this_cached_test_file) + log.debug("Adding .tst file: %s", this_cached_test_file) diff --git a/salt/modules/saltcloudmod.py b/salt/modules/saltcloudmod.py index 36b997377af..4f47a089434 100644 --- a/salt/modules/saltcloudmod.py +++ b/salt/modules/saltcloudmod.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Control a salt cloud system -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.utils.data @@ -12,25 +12,29 @@ import salt.utils.json HAS_CLOUD = False try: import saltcloud # pylint: disable=W0611 + HAS_CLOUD = True except ImportError: pass # Define the module's virtual name -__virtualname__ = 'saltcloud' +__virtualname__ = "saltcloud" def __virtual__(): - ''' + """ Only load if salt cloud is installed - ''' + """ if HAS_CLOUD: return __virtualname__ - return (False, 'The saltcloudmod execution module failed to load: requires the saltcloud library.') + return ( + False, + "The saltcloudmod execution module failed to load: requires the saltcloud library.", + ) def create(name, profile): - ''' + """ Create the named vm CLI Example: @@ -38,9 +42,9 @@ def create(name, profile): .. code-block:: bash salt saltcloud.create webserver rackspace_centos_512 - ''' - cmd = 'salt-cloud --out json -p {0} {1}'.format(profile, name) - out = __salt__['cmd.run_stdout'](cmd, python_shell=False) + """ + cmd = "salt-cloud --out json -p {0} {1}".format(profile, name) + out = __salt__["cmd.run_stdout"](cmd, python_shell=False) try: ret = salt.utils.json.loads(out) except ValueError: diff --git a/salt/modules/saltutil.py b/salt/modules/saltutil.py index 138a0fcf51c..f32abf201f9 100644 --- a/salt/modules/saltutil.py +++ b/salt/modules/saltutil.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" The Saltutil module is used to manage the state of the salt minion itself. It is used to manage minion modules as well as automate updates to the salt minion. :depends: - esky Python module for update functionality -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -13,38 +13,20 @@ import copy import fnmatch import logging import os +import shutil import signal import sys import time -import shutil - -# Import 3rd-party libs -# pylint: disable=import-error -try: - import esky - from esky import EskyVersionError - HAS_ESKY = True -except ImportError: - HAS_ESKY = False -# pylint: disable=no-name-in-module -from salt.ext import six -from salt.ext.six.moves.urllib.error import URLError -# pylint: enable=import-error,no-name-in-module - -# Fix a nasty bug with Win32 Python not supporting all of the standard signals -try: - salt_SIGKILL = signal.SIGKILL -except AttributeError: - salt_SIGKILL = signal.SIGTERM # Import salt libs import salt -import salt.config import salt.client import salt.client.ssh.client +import salt.config import salt.payload import salt.runner import salt.state +import salt.transport.client import salt.utils.args import salt.utils.event import salt.utils.extmods @@ -55,7 +37,35 @@ import salt.utils.path import salt.utils.process import salt.utils.url import salt.wheel -import salt.transport.client +from salt.exceptions import ( + CommandExecutionError, + SaltInvocationError, + SaltRenderError, + SaltReqTimeoutError, +) + +# pylint: disable=no-name-in-module +from salt.ext import six +from salt.ext.six.moves.urllib.error import URLError + +# Import 3rd-party libs +# pylint: disable=import-error +try: + import esky + from esky import EskyVersionError + + HAS_ESKY = True +except ImportError: + HAS_ESKY = False + +# pylint: enable=import-error,no-name-in-module + +# Fix a nasty bug with Win32 Python not supporting all of the standard signals +try: + salt_SIGKILL = signal.SIGKILL +except AttributeError: + salt_SIGKILL = signal.SIGTERM + HAS_PSUTIL = True try: @@ -63,65 +73,66 @@ try: except ImportError: HAS_PSUTIL = False -from salt.exceptions import ( - SaltReqTimeoutError, SaltRenderError, CommandExecutionError, SaltInvocationError -) -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] log = logging.getLogger(__name__) def _get_top_file_envs(): - ''' + """ Get all environments from the top file - ''' + """ try: - return __context__['saltutil._top_file_envs'] + return __context__["saltutil._top_file_envs"] except KeyError: try: - st_ = salt.state.HighState(__opts__, - initial_pillar=__pillar__) + st_ = salt.state.HighState(__opts__, initial_pillar=__pillar__) top = st_.get_top() if top: - envs = list(st_.top_matches(top).keys()) or 'base' + envs = list(st_.top_matches(top).keys()) or "base" else: - envs = 'base' + envs = "base" except SaltRenderError as exc: - raise CommandExecutionError( - 'Unable to render top file(s): {0}'.format(exc) - ) - __context__['saltutil._top_file_envs'] = envs + raise CommandExecutionError("Unable to render top file(s): {0}".format(exc)) + __context__["saltutil._top_file_envs"] = envs return envs def _sync(form, saltenv=None, extmod_whitelist=None, extmod_blacklist=None): - ''' + """ Sync the given directory in the given environment - ''' + """ if saltenv is None: saltenv = _get_top_file_envs() if isinstance(saltenv, six.string_types): - saltenv = saltenv.split(',') - ret, touched = salt.utils.extmods.sync(__opts__, form, saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist) + saltenv = saltenv.split(",") + ret, touched = salt.utils.extmods.sync( + __opts__, + form, + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) # Dest mod_dir is touched? trigger reload if requested if touched: - mod_file = os.path.join(__opts__['cachedir'], 'module_refresh') - with salt.utils.files.fopen(mod_file, 'a'): + mod_file = os.path.join(__opts__["cachedir"], "module_refresh") + with salt.utils.files.fopen(mod_file, "a"): pass - if form == 'grains' and \ - __opts__.get('grains_cache') and \ - os.path.isfile(os.path.join(__opts__['cachedir'], 'grains.cache.p')): + if ( + form == "grains" + and __opts__.get("grains_cache") + and os.path.isfile(os.path.join(__opts__["cachedir"], "grains.cache.p")) + ): try: - os.remove(os.path.join(__opts__['cachedir'], 'grains.cache.p')) + os.remove(os.path.join(__opts__["cachedir"], "grains.cache.p")) except OSError: - log.error('Could not remove grains cache!') + log.error("Could not remove grains cache!") return ret def update(version=None): - ''' + """ Update the salt minion from the URL defined in opts['update_url'] SaltStack, Inc provides the latest builds here: update_url: https://repo.saltstack.com/windows/ @@ -142,52 +153,54 @@ def update(version=None): salt '*' saltutil.update salt '*' saltutil.update 0.10.3 - ''' + """ ret = {} if not HAS_ESKY: - ret['_error'] = 'Esky not available as import' + ret["_error"] = "Esky not available as import" return ret - if not getattr(sys, 'frozen', False): - ret['_error'] = 'Minion is not running an Esky build' + if not getattr(sys, "frozen", False): + ret["_error"] = "Minion is not running an Esky build" return ret - if not __salt__['config.option']('update_url'): - ret['_error'] = '"update_url" not configured on this minion' + if not __salt__["config.option"]("update_url"): + ret["_error"] = '"update_url" not configured on this minion' return ret - app = esky.Esky(sys.executable, __opts__['update_url']) - oldversion = __grains__['saltversion'] + app = esky.Esky(sys.executable, __opts__["update_url"]) + oldversion = __grains__["saltversion"] if not version: try: version = app.find_update() except URLError as exc: - ret['_error'] = 'Could not connect to update_url. Error: {0}'.format(exc) + ret["_error"] = "Could not connect to update_url. Error: {0}".format(exc) return ret if not version: - ret['_error'] = 'No updates available' + ret["_error"] = "No updates available" return ret try: app.fetch_version(version) except EskyVersionError as exc: - ret['_error'] = 'Unable to fetch version {0}. Error: {1}'.format(version, exc) + ret["_error"] = "Unable to fetch version {0}. Error: {1}".format(version, exc) return ret try: app.install_version(version) except EskyVersionError as exc: - ret['_error'] = 'Unable to install version {0}. Error: {1}'.format(version, exc) + ret["_error"] = "Unable to install version {0}. Error: {1}".format(version, exc) return ret try: app.cleanup() except Exception as exc: # pylint: disable=broad-except - ret['_error'] = 'Unable to cleanup. Error: {0}'.format(exc) + ret["_error"] = "Unable to cleanup. Error: {0}".format(exc) restarted = {} - for service in __opts__['update_restart_services']: - restarted[service] = __salt__['service.restart'](service) - ret['comment'] = 'Updated from {0} to {1}'.format(oldversion, version) - ret['restarted'] = restarted + for service in __opts__["update_restart_services"]: + restarted[service] = __salt__["service.restart"](service) + ret["comment"] = "Updated from {0} to {1}".format(oldversion, version) + ret["restarted"] = restarted return ret -def sync_beacons(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_beacons( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2015.5.1 Sync beacons from ``salt://_beacons`` to the minion @@ -218,15 +231,15 @@ def sync_beacons(saltenv=None, refresh=True, extmod_whitelist=None, extmod_black salt '*' saltutil.sync_beacons salt '*' saltutil.sync_beacons saltenv=dev salt '*' saltutil.sync_beacons saltenv=base,dev - ''' - ret = _sync('beacons', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("beacons", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_beacons() return ret def sync_sdb(saltenv=None, extmod_whitelist=None, extmod_blacklist=None): - ''' + """ .. versionadded:: 2015.5.8,2015.8.3 Sync sdb modules from ``salt://_sdb`` to the minion @@ -256,13 +269,15 @@ def sync_sdb(saltenv=None, extmod_whitelist=None, extmod_blacklist=None): salt '*' saltutil.sync_sdb salt '*' saltutil.sync_sdb saltenv=dev salt '*' saltutil.sync_sdb saltenv=base,dev - ''' - ret = _sync('sdb', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("sdb", saltenv, extmod_whitelist, extmod_blacklist) return ret -def sync_modules(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_modules( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 0.10.0 Sync execution modules from ``salt://_modules`` to the minion @@ -310,15 +325,17 @@ def sync_modules(saltenv=None, refresh=True, extmod_whitelist=None, extmod_black salt '*' saltutil.sync_modules salt '*' saltutil.sync_modules saltenv=dev salt '*' saltutil.sync_modules saltenv=base,dev - ''' - ret = _sync('modules', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("modules", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_states(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_states( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 0.10.0 Sync state modules from ``salt://_states`` to the minion @@ -349,15 +366,15 @@ def sync_states(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blackl salt '*' saltutil.sync_states salt '*' saltutil.sync_states saltenv=dev salt '*' saltutil.sync_states saltenv=base,dev - ''' - ret = _sync('states', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("states", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret def refresh_grains(**kwargs): - ''' + """ .. versionadded:: 2016.3.6,2016.11.4,2017.7.0 Refresh the minion's grains without syncing custom grains modules from @@ -375,9 +392,9 @@ def refresh_grains(**kwargs): .. code-block:: bash salt '*' saltutil.refresh_grains - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - _refresh_pillar = kwargs.pop('refresh_pillar', True) + _refresh_pillar = kwargs.pop("refresh_pillar", True) if kwargs: salt.utils.args.invalid_kwargs(kwargs) # Modules and pillar need to be refreshed in case grains changes affected @@ -391,8 +408,10 @@ def refresh_grains(**kwargs): return True -def sync_grains(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_grains( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 0.10.0 Sync grains modules from ``salt://_grains`` to the minion @@ -424,16 +443,18 @@ def sync_grains(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blackl salt '*' saltutil.sync_grains salt '*' saltutil.sync_grains saltenv=dev salt '*' saltutil.sync_grains saltenv=base,dev - ''' - ret = _sync('grains', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("grains", saltenv, extmod_whitelist, extmod_blacklist) if refresh: # we don't need to call refresh_modules here because it's done by refresh_pillar refresh_pillar() return ret -def sync_renderers(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_renderers( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 0.10.0 Sync renderers from ``salt://_renderers`` to the minion @@ -465,15 +486,17 @@ def sync_renderers(saltenv=None, refresh=True, extmod_whitelist=None, extmod_bla salt '*' saltutil.sync_renderers salt '*' saltutil.sync_renderers saltenv=dev salt '*' saltutil.sync_renderers saltenv=base,dev - ''' - ret = _sync('renderers', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("renderers", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_returners(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_returners( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 0.10.0 Sync returners from ``salt://_returners`` to the minion @@ -503,15 +526,17 @@ def sync_returners(saltenv=None, refresh=True, extmod_whitelist=None, extmod_bla salt '*' saltutil.sync_returners salt '*' saltutil.sync_returners saltenv=dev - ''' - ret = _sync('returners', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("returners", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_proxymodules(saltenv=None, refresh=False, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_proxymodules( + saltenv=None, refresh=False, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2015.8.2 Sync proxy modules from ``salt://_proxy`` to the minion @@ -542,15 +567,17 @@ def sync_proxymodules(saltenv=None, refresh=False, extmod_whitelist=None, extmod salt '*' saltutil.sync_proxymodules salt '*' saltutil.sync_proxymodules saltenv=dev salt '*' saltutil.sync_proxymodules saltenv=base,dev - ''' - ret = _sync('proxy', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("proxy", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_matchers(saltenv=None, refresh=False, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_matchers( + saltenv=None, refresh=False, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2019.2.0 Sync engine modules from ``salt://_matchers`` to the minion @@ -580,15 +607,17 @@ def sync_matchers(saltenv=None, refresh=False, extmod_whitelist=None, extmod_bla salt '*' saltutil.sync_matchers salt '*' saltutil.sync_matchers saltenv=base,dev - ''' - ret = _sync('matchers', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("matchers", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_engines(saltenv=None, refresh=False, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_engines( + saltenv=None, refresh=False, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2016.3.0 Sync engine modules from ``salt://_engines`` to the minion @@ -618,15 +647,17 @@ def sync_engines(saltenv=None, refresh=False, extmod_whitelist=None, extmod_blac salt '*' saltutil.sync_engines salt '*' saltutil.sync_engines saltenv=base,dev - ''' - ret = _sync('engines', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("engines", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_thorium(saltenv=None, refresh=False, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_thorium( + saltenv=None, refresh=False, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2018.3.0 Sync Thorium modules from ``salt://_thorium`` to the minion @@ -656,15 +687,17 @@ def sync_thorium(saltenv=None, refresh=False, extmod_whitelist=None, extmod_blac salt '*' saltutil.sync_thorium salt '*' saltutil.sync_thorium saltenv=base,dev - ''' - ret = _sync('thorium', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("thorium", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_output(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_output( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ Sync outputters from ``salt://_output`` to the minion saltenv @@ -693,18 +726,20 @@ def sync_output(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blackl salt '*' saltutil.sync_output salt '*' saltutil.sync_output saltenv=dev salt '*' saltutil.sync_output saltenv=base,dev - ''' - ret = _sync('output', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("output", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -sync_outputters = salt.utils.functools.alias_function(sync_output, 'sync_outputters') +sync_outputters = salt.utils.functools.alias_function(sync_output, "sync_outputters") -def sync_clouds(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_clouds( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2017.7.0 Sync cloud modules from ``salt://_cloud`` to the minion @@ -731,15 +766,17 @@ def sync_clouds(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blackl salt '*' saltutil.sync_clouds salt '*' saltutil.sync_clouds saltenv=dev salt '*' saltutil.sync_clouds saltenv=base,dev - ''' - ret = _sync('clouds', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("clouds", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_utils(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_utils( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2014.7.0 Sync utility modules from ``salt://_utils`` to the minion @@ -770,15 +807,17 @@ def sync_utils(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blackli salt '*' saltutil.sync_utils salt '*' saltutil.sync_utils saltenv=dev salt '*' saltutil.sync_utils saltenv=base,dev - ''' - ret = _sync('utils', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("utils", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_serializers(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_serializers( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2019.2.0 Sync serializers from ``salt://_serializers`` to the minion @@ -809,15 +848,15 @@ def sync_serializers(saltenv=None, refresh=True, extmod_whitelist=None, extmod_b salt '*' saltutil.sync_serializers salt '*' saltutil.sync_serializers saltenv=dev salt '*' saltutil.sync_serializers saltenv=base,dev - ''' - ret = _sync('serializers', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("serializers", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret def list_extmods(): - ''' + """ .. versionadded:: 2017.7.0 List Salt modules which have been synced externally @@ -827,21 +866,23 @@ def list_extmods(): .. code-block:: bash salt '*' saltutil.list_extmods - ''' + """ ret = {} - ext_dir = os.path.join(__opts__['cachedir'], 'extmods') + ext_dir = os.path.join(__opts__["cachedir"], "extmods") mod_types = os.listdir(ext_dir) for mod_type in mod_types: ret[mod_type] = set() for _, _, files in salt.utils.path.os_walk(os.path.join(ext_dir, mod_type)): for fh_ in files: - ret[mod_type].add(fh_.split('.')[0]) + ret[mod_type].add(fh_.split(".")[0]) ret[mod_type] = list(ret[mod_type]) return ret -def sync_log_handlers(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_log_handlers( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2015.8.0 Sync log handlers from ``salt://_log_handlers`` to the minion @@ -872,15 +913,17 @@ def sync_log_handlers(saltenv=None, refresh=True, extmod_whitelist=None, extmod_ salt '*' saltutil.sync_log_handlers salt '*' saltutil.sync_log_handlers saltenv=dev salt '*' saltutil.sync_log_handlers saltenv=base,dev - ''' - ret = _sync('log_handlers', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("log_handlers", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret -def sync_pillar(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_pillar( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 2015.8.11,2016.3.2 Sync pillar modules from the ``salt://_pillar`` directory on the Salt @@ -908,20 +951,22 @@ def sync_pillar(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blackl salt '*' saltutil.sync_pillar salt '*' saltutil.sync_pillar saltenv=dev - ''' - if __opts__['file_client'] != 'local': + """ + if __opts__["file_client"] != "local": raise CommandExecutionError( - 'Pillar modules can only be synced to masterless minions' + "Pillar modules can only be synced to masterless minions" ) - ret = _sync('pillar', saltenv, extmod_whitelist, extmod_blacklist) + ret = _sync("pillar", saltenv, extmod_whitelist, extmod_blacklist) if refresh: # we don't need to call refresh_modules here because it's done by refresh_pillar refresh_pillar() return ret -def sync_executors(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_executors( + saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None +): + """ .. versionadded:: 3000 Sync executors from ``salt://_executors`` to the minion @@ -952,15 +997,15 @@ def sync_executors(saltenv=None, refresh=True, extmod_whitelist=None, extmod_bla salt '*' saltutil.sync_executors salt '*' saltutil.sync_executors saltenv=dev salt '*' saltutil.sync_executors saltenv=base,dev - ''' - ret = _sync('executors', saltenv, extmod_whitelist, extmod_blacklist) + """ + ret = _sync("executors", saltenv, extmod_whitelist, extmod_blacklist) if refresh: refresh_modules() return ret def sync_all(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None): - ''' + """ .. versionchanged:: 2015.8.11,2016.3.2 On masterless minions, pillar modules are now synced, and refreshed when ``refresh`` is set to ``True``. @@ -1005,28 +1050,40 @@ def sync_all(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist salt '*' saltutil.sync_all saltenv=dev salt '*' saltutil.sync_all saltenv=base,dev salt '*' saltutil.sync_all extmod_whitelist={'modules': ['custom_module']} - ''' - log.debug('Syncing all') + """ + log.debug("Syncing all") ret = {} - ret['clouds'] = sync_clouds(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['beacons'] = sync_beacons(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['modules'] = sync_modules(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['states'] = sync_states(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['sdb'] = sync_sdb(saltenv, extmod_whitelist, extmod_blacklist) - ret['grains'] = sync_grains(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['renderers'] = sync_renderers(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['returners'] = sync_returners(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['output'] = sync_output(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['utils'] = sync_utils(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['log_handlers'] = sync_log_handlers(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['executors'] = sync_executors(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['proxymodules'] = sync_proxymodules(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['engines'] = sync_engines(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['thorium'] = sync_thorium(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['serializers'] = sync_serializers(saltenv, False, extmod_whitelist, extmod_blacklist) - ret['matchers'] = sync_matchers(saltenv, False, extmod_whitelist, extmod_blacklist) - if __opts__['file_client'] == 'local': - ret['pillar'] = sync_pillar(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["clouds"] = sync_clouds(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["beacons"] = sync_beacons(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["modules"] = sync_modules(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["states"] = sync_states(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["sdb"] = sync_sdb(saltenv, extmod_whitelist, extmod_blacklist) + ret["grains"] = sync_grains(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["renderers"] = sync_renderers( + saltenv, False, extmod_whitelist, extmod_blacklist + ) + ret["returners"] = sync_returners( + saltenv, False, extmod_whitelist, extmod_blacklist + ) + ret["output"] = sync_output(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["utils"] = sync_utils(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["log_handlers"] = sync_log_handlers( + saltenv, False, extmod_whitelist, extmod_blacklist + ) + ret["executors"] = sync_executors( + saltenv, False, extmod_whitelist, extmod_blacklist + ) + ret["proxymodules"] = sync_proxymodules( + saltenv, False, extmod_whitelist, extmod_blacklist + ) + ret["engines"] = sync_engines(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["thorium"] = sync_thorium(saltenv, False, extmod_whitelist, extmod_blacklist) + ret["serializers"] = sync_serializers( + saltenv, False, extmod_whitelist, extmod_blacklist + ) + ret["matchers"] = sync_matchers(saltenv, False, extmod_whitelist, extmod_blacklist) + if __opts__["file_client"] == "local": + ret["pillar"] = sync_pillar(saltenv, False, extmod_whitelist, extmod_blacklist) if refresh: # we don't need to call refresh_modules here because it's done by refresh_pillar refresh_pillar() @@ -1034,7 +1091,7 @@ def sync_all(saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist def refresh_beacons(): - ''' + """ Signal the minion to refresh the beacons. CLI Example: @@ -1042,17 +1099,17 @@ def refresh_beacons(): .. code-block:: bash salt '*' saltutil.refresh_beacons - ''' + """ try: - ret = __salt__['event.fire']({}, 'beacons_refresh') + ret = __salt__["event.fire"]({}, "beacons_refresh") except KeyError: - log.error('Event module not available. Module refresh failed.') + log.error("Event module not available. Module refresh failed.") ret = False # Effectively a no-op, since we can't really return without an event system return ret def refresh_matchers(): - ''' + """ Signal the minion to refresh its matchers. CLI Example: @@ -1060,18 +1117,18 @@ def refresh_matchers(): .. code-block:: bash salt '*' saltutil.refresh_matchers - ''' + """ try: - ret = __salt__['event.fire']({}, 'matchers_refresh') + ret = __salt__["event.fire"]({}, "matchers_refresh") except KeyError: - log.error('Event module not available. Matcher refresh failed.') + log.error("Event module not available. Matcher refresh failed.") ret = False # Effectively a no-op, since we can't really return without an event system return ret def refresh_pillar(wait=False, timeout=30): - ''' - Signal the minion to refresh the pillar data. + """ + Signal the minion to refresh the in-memory pillar data. See :ref:`pillar-in-memory`. :param wait: Wait for pillar refresh to complete, defaults to False. :type wait: bool, optional @@ -1084,27 +1141,27 @@ def refresh_pillar(wait=False, timeout=30): .. code-block:: bash salt '*' saltutil.refresh_pillar - ''' + """ try: - ret = __salt__['event.fire']({}, 'pillar_refresh') + ret = __salt__["event.fire"]({}, "pillar_refresh") except KeyError: - log.error('Event module not available. Module refresh failed.') + log.error("Event module not available. Module refresh failed.") ret = False # Effectively a no-op, since we can't really return without an event system if wait: - eventer = salt.utils.event.get_event('minion', opts=__opts__, listen=True) + eventer = salt.utils.event.get_event("minion", opts=__opts__, listen=True) event_ret = eventer.get_event( - tag='/salt/minion/minion_pillar_refresh_complete', - wait=timeout) - if not event_ret or event_ret['complete'] is False: + tag="/salt/minion/minion_pillar_refresh_complete", wait=timeout + ) + if not event_ret or event_ret["complete"] is False: log.warn("Pillar refresh did not complete within timeout %s", timeout) return ret -pillar_refresh = salt.utils.functools.alias_function(refresh_pillar, 'pillar_refresh') +pillar_refresh = salt.utils.functools.alias_function(refresh_pillar, "pillar_refresh") def refresh_modules(**kwargs): - ''' + """ Signal the minion to refresh the module and grain data The default is to refresh module asynchronously. To block @@ -1116,27 +1173,29 @@ def refresh_modules(**kwargs): .. code-block:: bash salt '*' saltutil.refresh_modules - ''' - asynchronous = bool(kwargs.get('async', True)) + """ + asynchronous = bool(kwargs.get("async", True)) try: if asynchronous: # If we're going to block, first setup a listener - ret = __salt__['event.fire']({}, 'module_refresh') + ret = __salt__["event.fire"]({}, "module_refresh") else: - with salt.utils.event.get_event('minion', opts=__opts__, listen=True) as event_bus: - ret = __salt__['event.fire']({'notify': True}, 'module_refresh') + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=True + ) as event_bus: + ret = __salt__["event.fire"]({"notify": True}, "module_refresh") # Wait for the finish event to fire - log.trace('refresh_modules waiting for module refresh to complete') + log.trace("refresh_modules waiting for module refresh to complete") # Blocks until we hear this event or until the timeout expires - event_bus.get_event(tag='/salt/minion/minion_mod_complete', wait=30) + event_bus.get_event(tag="/salt/minion/minion_mod_complete", wait=30) except KeyError: - log.error('Event module not available. Module refresh failed.') + log.error("Event module not available. Module refresh failed.") ret = False # Effectively a no-op, since we can't really return without an event system return ret def is_running(fun): - ''' + """ If the named function is running return the data associated with it/them. The argument can be a glob @@ -1145,17 +1204,17 @@ def is_running(fun): .. code-block:: bash salt '*' saltutil.is_running state.highstate - ''' + """ run = running() ret = [] for data in run: - if fnmatch.fnmatch(data.get('fun', ''), fun): + if fnmatch.fnmatch(data.get("fun", ""), fun): ret.append(data) return ret def running(): - ''' + """ Return the data on all running salt processes on the minion CLI Example: @@ -1163,12 +1222,12 @@ def running(): .. code-block:: bash salt '*' saltutil.running - ''' + """ return salt.utils.minion.running(__opts__) def clear_cache(): - ''' + """ Forcibly removes all caches on a minion. .. versionadded:: 2014.7.0 @@ -1181,22 +1240,25 @@ def clear_cache(): .. code-block:: bash salt '*' saltutil.clear_cache - ''' - for root, dirs, files in salt.utils.files.safe_walk(__opts__['cachedir'], followlinks=False): + """ + for root, dirs, files in salt.utils.files.safe_walk( + __opts__["cachedir"], followlinks=False + ): for name in files: try: os.remove(os.path.join(root, name)) except OSError as exc: log.error( - 'Attempt to clear cache with saltutil.clear_cache ' - 'FAILED with: %s', exc + "Attempt to clear cache with saltutil.clear_cache " + "FAILED with: %s", + exc, ) return False return True def clear_job_cache(hours=24): - ''' + """ Forcibly removes job cache folders and files on a minion. .. versionadded:: 2018.3.0 @@ -1209,10 +1271,11 @@ def clear_job_cache(hours=24): .. code-block:: bash salt '*' saltutil.clear_job_cache hours=12 - ''' + """ threshold = time.time() - hours * 3600 - for root, dirs, files in salt.utils.files.safe_walk(os.path.join(__opts__['cachedir'], 'minion_jobs'), - followlinks=False): + for root, dirs, files in salt.utils.files.safe_walk( + os.path.join(__opts__["cachedir"], "minion_jobs"), followlinks=False + ): for name in dirs: try: directory = os.path.join(root, name) @@ -1220,13 +1283,16 @@ def clear_job_cache(hours=24): if mtime < threshold: shutil.rmtree(directory) except OSError as exc: - log.error('Attempt to clear cache with saltutil.clear_job_cache FAILED with: %s', exc) + log.error( + "Attempt to clear cache with saltutil.clear_job_cache FAILED with: %s", + exc, + ) return False return True def find_job(jid): - ''' + """ Return the data for a specific job id that is currently running. jid @@ -1270,15 +1336,15 @@ def find_job(jid): # salt my-minion saltutil.find_job 20160503150049487736 my-minion: ---------- - ''' + """ for data in running(): - if data['jid'] == jid: + if data["jid"] == jid: return data return {} def find_cached_job(jid): - ''' + """ Return the data for a specific cached job id. Note this only works if cache_jobs has previously been set to True on the minion. @@ -1287,18 +1353,20 @@ def find_cached_job(jid): .. code-block:: bash salt '*' saltutil.find_cached_job - ''' + """ serial = salt.payload.Serial(__opts__) - proc_dir = os.path.join(__opts__['cachedir'], 'minion_jobs') + proc_dir = os.path.join(__opts__["cachedir"], "minion_jobs") job_dir = os.path.join(proc_dir, six.text_type(jid)) if not os.path.isdir(job_dir): - if not __opts__.get('cache_jobs'): - return ('Local jobs cache directory not found; you may need to' - ' enable cache_jobs on this minion') + if not __opts__.get("cache_jobs"): + return ( + "Local jobs cache directory not found; you may need to" + " enable cache_jobs on this minion" + ) else: - return 'Local jobs cache directory {0} not found'.format(job_dir) - path = os.path.join(job_dir, 'return.p') - with salt.utils.files.fopen(path, 'rb') as fp_: + return "Local jobs cache directory {0} not found".format(job_dir) + path = os.path.join(job_dir, "return.p") + with salt.utils.files.fopen(path, "rb") as fp_: buf = fp_.read() if buf: try: @@ -1314,7 +1382,7 @@ def find_cached_job(jid): def signal_job(jid, sig): - ''' + """ Sends a signal to the named salt job's process CLI Example: @@ -1322,37 +1390,40 @@ def signal_job(jid, sig): .. code-block:: bash salt '*' saltutil.signal_job 15 - ''' + """ if HAS_PSUTIL is False: - log.warning('saltutil.signal job called, but psutil is not installed. ' - 'Install psutil to ensure more reliable and accurate PID ' - 'management.') + log.warning( + "saltutil.signal job called, but psutil is not installed. " + "Install psutil to ensure more reliable and accurate PID " + "management." + ) for data in running(): - if data['jid'] == jid: + if data["jid"] == jid: try: if HAS_PSUTIL: - for proc in salt.utils.psutil_compat.Process(pid=data['pid']).children(recursive=True): + for proc in salt.utils.psutil_compat.Process( + pid=data["pid"] + ).children(recursive=True): proc.send_signal(sig) - os.kill(int(data['pid']), sig) - if HAS_PSUTIL is False and 'child_pids' in data: - for pid in data['child_pids']: + os.kill(int(data["pid"]), sig) + if HAS_PSUTIL is False and "child_pids" in data: + for pid in data["child_pids"]: os.kill(int(pid), sig) - return 'Signal {0} sent to job {1} at pid {2}'.format( - int(sig), - jid, - data['pid'] - ) + return "Signal {0} sent to job {1} at pid {2}".format( + int(sig), jid, data["pid"] + ) except OSError: - path = os.path.join(__opts__['cachedir'], 'proc', six.text_type(jid)) + path = os.path.join(__opts__["cachedir"], "proc", six.text_type(jid)) if os.path.isfile(path): os.remove(path) - return ('Job {0} was not running and job data has been ' - ' cleaned up').format(jid) - return '' + return ( + "Job {0} was not running and job data has been " " cleaned up" + ).format(jid) + return "" def term_job(jid): - ''' + """ Sends a termination signal (SIGTERM 15) to the named salt job's process CLI Example: @@ -1360,12 +1431,12 @@ def term_job(jid): .. code-block:: bash salt '*' saltutil.term_job - ''' + """ return signal_job(jid, signal.SIGTERM) def term_all_jobs(): - ''' + """ Sends a termination signal (SIGTERM 15) to all currently running jobs CLI Example: @@ -1373,15 +1444,15 @@ def term_all_jobs(): .. code-block:: bash salt '*' saltutil.term_all_jobs - ''' + """ ret = [] for data in running(): - ret.append(signal_job(data['jid'], signal.SIGTERM)) + ret.append(signal_job(data["jid"], signal.SIGTERM)) return ret def kill_job(jid): - ''' + """ Sends a kill signal (SIGKILL 9) to the named salt job's process CLI Example: @@ -1389,14 +1460,14 @@ def kill_job(jid): .. code-block:: bash salt '*' saltutil.kill_job - ''' + """ # Some OS's (Win32) don't have SIGKILL, so use salt_SIGKILL which is set to # an appropriate value for the operating system this is running on. return signal_job(jid, salt_SIGKILL) def kill_all_jobs(): - ''' + """ Sends a kill signal (SIGKILL 9) to all currently running jobs CLI Example: @@ -1404,17 +1475,17 @@ def kill_all_jobs(): .. code-block:: bash salt '*' saltutil.kill_all_jobs - ''' + """ # Some OS's (Win32) don't have SIGKILL, so use salt_SIGKILL which is set to # an appropriate value for the operating system this is running on. ret = [] for data in running(): - ret.append(signal_job(data['jid'], salt_SIGKILL)) + ret.append(signal_job(data["jid"], salt_SIGKILL)) return ret def regen_keys(): - ''' + """ Used to regenerate the minion keys. CLI Example: @@ -1422,9 +1493,9 @@ def regen_keys(): .. code-block:: bash salt '*' saltutil.regen_keys - ''' - for fn_ in os.listdir(__opts__['pki_dir']): - path = os.path.join(__opts__['pki_dir'], fn_) + """ + for fn_ in os.listdir(__opts__["pki_dir"]): + path = os.path.join(__opts__["pki_dir"], fn_) try: os.remove(path) except os.error: @@ -1432,11 +1503,11 @@ def regen_keys(): # TODO: move this into a channel function? Or auth? # create a channel again, this will force the key regen with salt.transport.client.ReqChannel.factory(__opts__) as channel: - log.debug('Recreating channel to force key regen') + log.debug("Recreating channel to force key regen") def revoke_auth(preserve_minion_cache=False): - ''' + """ The minion sends a request to the master to revoke its own key. Note that the minion session will be revoked and the minion may not be able to return the result of this command back to the master. @@ -1449,22 +1520,26 @@ def revoke_auth(preserve_minion_cache=False): .. code-block:: bash salt '*' saltutil.revoke_auth - ''' + """ masters = list() ret = True - if 'master_uri_list' in __opts__: - for master_uri in __opts__['master_uri_list']: + if "master_uri_list" in __opts__: + for master_uri in __opts__["master_uri_list"]: masters.append(master_uri) else: - masters.append(__opts__['master_uri']) + masters.append(__opts__["master_uri"]) for master in masters: - with salt.transport.client.ReqChannel.factory(__opts__, master_uri=master) as channel: - tok = channel.auth.gen_token(b'salt') - load = {'cmd': 'revoke_auth', - 'id': __opts__['id'], - 'tok': tok, - 'preserve_minion_cache': preserve_minion_cache} + with salt.transport.client.ReqChannel.factory( + __opts__, master_uri=master + ) as channel: + tok = channel.auth.gen_token(b"salt") + load = { + "cmd": "revoke_auth", + "id": __opts__["id"], + "tok": tok, + "preserve_minion_cache": preserve_minion_cache, + } try: channel.send(load) except SaltReqTimeoutError: @@ -1483,48 +1558,66 @@ def _get_ssh_or_api_client(cfgfile, ssh=False): def _exec(client, tgt, fun, arg, timeout, tgt_type, ret, kwarg, **kwargs): fcn_ret = {} seen = 0 - if 'batch' in kwargs: + if "batch" in kwargs: _cmd = client.cmd_batch cmd_kwargs = { - 'tgt': tgt, 'fun': fun, 'arg': arg, 'tgt_type': tgt_type, - 'ret': ret, 'kwarg': kwarg, 'batch': kwargs['batch'], + "tgt": tgt, + "fun": fun, + "arg": arg, + "tgt_type": tgt_type, + "ret": ret, + "kwarg": kwarg, + "batch": kwargs["batch"], } - del kwargs['batch'] - elif 'subset' in kwargs: + del kwargs["batch"] + elif "subset" in kwargs: _cmd = client.cmd_subset cmd_kwargs = { - 'tgt': tgt, 'fun': fun, 'arg': arg, 'tgt_type': tgt_type, - 'ret': ret, 'cli': True, 'kwarg': kwarg, 'sub': kwargs['subset'], + "tgt": tgt, + "fun": fun, + "arg": arg, + "tgt_type": tgt_type, + "ret": ret, + "cli": True, + "kwarg": kwarg, + "sub": kwargs["subset"], } - del kwargs['subset'] + del kwargs["subset"] else: _cmd = client.cmd_iter cmd_kwargs = { - 'tgt': tgt, 'fun': fun, 'arg': arg, 'timeout': timeout, - 'tgt_type': tgt_type, 'ret': ret, 'kwarg': kwarg, + "tgt": tgt, + "fun": fun, + "arg": arg, + "timeout": timeout, + "tgt_type": tgt_type, + "ret": ret, + "kwarg": kwarg, } cmd_kwargs.update(kwargs) for ret_comp in _cmd(**cmd_kwargs): fcn_ret.update(ret_comp) seen += 1 # fcn_ret can be empty, so we cannot len the whole return dict - if tgt_type == 'list' and len(tgt) == seen: + if tgt_type == "list" and len(tgt) == seen: # do not wait for timeout when explicit list matching # and all results are there break return fcn_ret -def cmd(tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - kwarg=None, - ssh=False, - **kwargs): - ''' +def cmd( + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + kwarg=None, + ssh=False, + **kwargs +): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -1536,45 +1629,47 @@ def cmd(tgt, .. code-block:: bash salt '*' saltutil.cmd - ''' - cfgfile = __opts__['conf_file'] + """ + cfgfile = __opts__["conf_file"] client = _get_ssh_or_api_client(cfgfile, ssh) - fcn_ret = _exec( - client, tgt, fun, arg, timeout, tgt_type, ret, kwarg, **kwargs) + fcn_ret = _exec(client, tgt, fun, arg, timeout, tgt_type, ret, kwarg, **kwargs) # if return is empty, we may have not used the right conf, # try with the 'minion relative master configuration counter part # if available - master_cfgfile = '{0}master'.format(cfgfile[:-6]) # remove 'minion' + master_cfgfile = "{0}master".format(cfgfile[:-6]) # remove 'minion' if ( not fcn_ret - and cfgfile.endswith('{0}{1}'.format(os.path.sep, 'minion')) + and cfgfile.endswith("{0}{1}".format(os.path.sep, "minion")) and os.path.exists(master_cfgfile) ): client = _get_ssh_or_api_client(master_cfgfile, ssh) - fcn_ret = _exec( - client, tgt, fun, arg, timeout, tgt_type, ret, kwarg, **kwargs) + fcn_ret = _exec(client, tgt, fun, arg, timeout, tgt_type, ret, kwarg, **kwargs) - if 'batch' in kwargs: + if "batch" in kwargs: old_ret, fcn_ret = fcn_ret, {} for key, value in old_ret.items(): fcn_ret[key] = { - 'out': value.get('out', 'highstate') if isinstance(value, dict) else 'highstate', - 'ret': value, + "out": value.get("out", "highstate") + if isinstance(value, dict) + else "highstate", + "ret": value, } return fcn_ret -def cmd_iter(tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - kwarg=None, - ssh=False, - **kwargs): - ''' +def cmd_iter( + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + kwarg=None, + ssh=False, + **kwargs +): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -1586,25 +1681,19 @@ def cmd_iter(tgt, .. code-block:: bash salt '*' saltutil.cmd_iter - ''' + """ if ssh: - client = salt.client.ssh.client.SSHClient(__opts__['conf_file']) + client = salt.client.ssh.client.SSHClient(__opts__["conf_file"]) else: - client = salt.client.get_local_client(__opts__['conf_file']) - for ret in client.cmd_iter( - tgt, - fun, - arg, - timeout, - tgt_type, - ret, - kwarg, - **kwargs): + client = salt.client.get_local_client(__opts__["conf_file"]) + for ret in client.cmd_iter(tgt, fun, arg, timeout, tgt_type, ret, kwarg, **kwargs): yield ret -def runner(name, arg=None, kwarg=None, full_return=False, saltenv='base', jid=None, **kwargs): - ''' +def runner( + name, arg=None, kwarg=None, full_return=False, saltenv="base", jid=None, **kwargs +): + """ Execute a runner function. This function must be run on the master, either by targeting a minion running on a master or by using salt-call on a master. @@ -1626,20 +1715,19 @@ def runner(name, arg=None, kwarg=None, full_return=False, saltenv='base', jid=No salt master_minion saltutil.runner jobs.list_jobs salt master_minion saltutil.runner test.arg arg="['baz']" kwarg="{'foo': 'bar'}" - ''' + """ if arg is None: arg = [] if kwarg is None: kwarg = {} - jid = kwargs.pop('__orchestration_jid__', jid) - saltenv = kwargs.pop('__env__', saltenv) + jid = kwargs.pop("__orchestration_jid__", jid) + saltenv = kwargs.pop("__env__", saltenv) kwargs = salt.utils.args.clean_kwargs(**kwargs) if kwargs: kwarg.update(kwargs) - if 'master_job_cache' not in __opts__: - master_config = os.path.join(os.path.dirname(__opts__['conf_file']), - 'master') + if "master_job_cache" not in __opts__: + master_config = os.path.join(os.path.dirname(__opts__["conf_file"]), "master") master_opts = salt.config.master_config(master_config) rclient = salt.runner.RunnerClient(master_opts) else: @@ -1647,29 +1735,27 @@ def runner(name, arg=None, kwarg=None, full_return=False, saltenv='base', jid=No if name in rclient.functions: aspec = salt.utils.args.get_function_argspec(rclient.functions[name]) - if 'saltenv' in aspec.args: - kwarg['saltenv'] = saltenv + if "saltenv" in aspec.args: + kwarg["saltenv"] = saltenv - if name in ['state.orchestrate', 'state.orch', 'state.sls']: - kwarg['orchestration_jid'] = jid + if name in ["state.orchestrate", "state.orch", "state.sls"]: + kwarg["orchestration_jid"] = jid if jid: salt.utils.event.fire_args( __opts__, jid, - {'type': 'runner', 'name': name, 'args': arg, 'kwargs': kwarg}, - prefix='run' + {"type": "runner", "name": name, "args": arg, "kwargs": kwarg}, + prefix="run", ) - return rclient.cmd(name, - arg=arg, - kwarg=kwarg, - print_event=False, - full_return=full_return) + return rclient.cmd( + name, arg=arg, kwarg=kwarg, print_event=False, full_return=full_return + ) def wheel(name, *args, **kwargs): - ''' + """ Execute a wheel module and function. This function must be run against a minion that is local to the master. @@ -1702,13 +1788,12 @@ def wheel(name, *args, **kwargs): expected. The remote minion does not have access to wheel functions and their return data. - ''' - jid = kwargs.pop('__orchestration_jid__', None) - saltenv = kwargs.pop('__env__', 'base') + """ + jid = kwargs.pop("__orchestration_jid__", None) + saltenv = kwargs.pop("__env__", "base") - if __opts__['__role'] == 'minion': - master_config = os.path.join(os.path.dirname(__opts__['conf_file']), - 'master') + if __opts__["__role"] == "minion": + master_config = os.path.join(os.path.dirname(__opts__["conf_file"]), "master") master_opts = salt.config.client_config(master_config) wheel_client = salt.wheel.WheelClient(master_opts) else: @@ -1719,37 +1804,37 @@ def wheel(name, *args, **kwargs): pub_data = {} valid_kwargs = {} for key, val in six.iteritems(kwargs): - if key.startswith('__'): + if key.startswith("__"): pub_data[key] = val else: valid_kwargs[key] = val try: if name in wheel_client.functions: - aspec = salt.utils.args.get_function_argspec( - wheel_client.functions[name] - ) - if 'saltenv' in aspec.args: - valid_kwargs['saltenv'] = saltenv + aspec = salt.utils.args.get_function_argspec(wheel_client.functions[name]) + if "saltenv" in aspec.args: + valid_kwargs["saltenv"] = saltenv if jid: salt.utils.event.fire_args( __opts__, jid, - {'type': 'wheel', 'name': name, 'args': valid_kwargs}, - prefix='run' + {"type": "wheel", "name": name, "args": valid_kwargs}, + prefix="run", ) - ret = wheel_client.cmd(name, - arg=args, - pub_data=pub_data, - kwarg=valid_kwargs, - print_event=False, - full_return=True) + ret = wheel_client.cmd( + name, + arg=args, + pub_data=pub_data, + kwarg=valid_kwargs, + print_event=False, + full_return=True, + ) except SaltInvocationError: raise CommandExecutionError( - 'This command can only be executed on a minion that is located on ' - 'the master.' + "This command can only be executed on a minion that is located on " + "the master." ) return ret @@ -1763,11 +1848,11 @@ class _MMinion(object): # hack until https://github.com/saltstack/salt/pull/10273 is resolved # this is starting to look like PHP global _mminions # pylint: disable=W0601 - if '_mminions' not in globals(): + if "_mminions" not in globals(): _mminions = {} if saltenv not in _mminions or reload_env: opts = copy.deepcopy(__opts__) - del opts['file_roots'] + del opts["file_roots"] # grains at this point are in the context of the minion global __grains__ # pylint: disable=W0601 grains = copy.deepcopy(__grains__) @@ -1779,17 +1864,17 @@ class _MMinion(object): # this assignment is so that fxns called by mminion have minion # context - m.opts['grains'] = grains + m.opts["grains"] = grains - env_roots = m.opts['file_roots'][saltenv] - m.opts['module_dirs'] = [fp + '/_modules' for fp in env_roots] + env_roots = m.opts["file_roots"][saltenv] + m.opts["module_dirs"] = [fp + "/_modules" for fp in env_roots] m.gen_modules() _mminions[saltenv] = m return _mminions[saltenv] def mmodule(saltenv, fun, *args, **kwargs): - ''' + """ Loads minion modules from an environment so that they can be used in pillars for that environment @@ -1798,6 +1883,6 @@ def mmodule(saltenv, fun, *args, **kwargs): .. code-block:: bash salt '*' saltutil.mmodule base test.ping - ''' + """ mminion = _MMinion(saltenv) return mminion.functions[fun](*args, **kwargs) diff --git a/salt/modules/schedule.py b/salt/modules/schedule.py index dba96137a1d..d73e102e36a 100644 --- a/salt/modules/schedule.py +++ b/salt/modules/schedule.py @@ -1,27 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing the Salt schedule on a minion .. versionadded:: 2014.7.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy as pycopy import datetime import difflib import logging import os -try: - import dateutil.parser as dateutil_parser - _WHEN_SUPPORTED = True - _RANGE_SUPPORTED = True -except ImportError: - _WHEN_SUPPORTED = False - _RANGE_SUPPORTED = False - # Import salt libs import salt.utils.event import salt.utils.files @@ -31,52 +24,56 @@ import salt.utils.yaml # Import 3rd-party libs from salt.ext import six -__proxyenabled__ = ['*'] +try: + import dateutil.parser as dateutil_parser + + _WHEN_SUPPORTED = True + _RANGE_SUPPORTED = True +except ImportError: + _WHEN_SUPPORTED = False + _RANGE_SUPPORTED = False + + +__proxyenabled__ = ["*"] log = logging.getLogger(__name__) -__func_alias__ = { - 'list_': 'list', - 'reload_': 'reload' -} +__func_alias__ = {"list_": "list", "reload_": "reload"} SCHEDULE_CONF = [ - 'name', - 'maxrunning', - 'function', - 'splay', - 'range', - 'when', - 'once', - 'once_fmt', - 'returner', - 'jid_include', - 'args', - 'kwargs', - '_seconds', - 'seconds', - 'minutes', - 'hours', - 'days', - 'enabled', - 'return_job', - 'metadata', - 'cron', - 'until', - 'after', - 'return_config', - 'return_kwargs', - 'run_on_start', - 'skip_during_range', - 'run_after_skip_range', + "name", + "maxrunning", + "function", + "splay", + "range", + "when", + "once", + "once_fmt", + "returner", + "jid_include", + "args", + "kwargs", + "_seconds", + "seconds", + "minutes", + "hours", + "days", + "enabled", + "return_job", + "metadata", + "cron", + "until", + "after", + "return_config", + "return_kwargs", + "run_on_start", + "skip_during_range", + "run_after_skip_range", ] -def list_(show_all=False, - show_disabled=True, - where=None, - return_yaml=True): - ''' +def list_(show_all=False, show_disabled=True, where=None, return_yaml=True): + """ List the jobs currently scheduled on the minion CLI Example: @@ -91,42 +88,43 @@ def list_(show_all=False, # Hide disabled jobs from list of jobs salt '*' schedule.list show_disabled=False - ''' + """ schedule = {} try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire']({'func': 'list', - 'where': where}, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"]( + {"func": "list", "where": where}, "manage_schedule" + ) if res: - event_ret = event_bus.get_event(tag='/salt/minion/minion_schedule_list_complete', wait=30) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] + event_ret = event_bus.get_event( + tag="/salt/minion/minion_schedule_list_complete", wait=30 + ) + if event_ret and event_ret["complete"]: + schedule = event_ret["schedule"] except KeyError: # Effectively a no-op, since we can't really return without an event system ret = {} - ret['comment'] = 'Event module not available. Schedule list failed.' - ret['result'] = True - log.debug('Event module not available. Schedule list failed.') + ret["comment"] = "Event module not available. Schedule list failed." + ret["result"] = True + log.debug("Event module not available. Schedule list failed.") return ret - _hidden = ['enabled', - 'skip_function', - 'skip_during_range'] + _hidden = ["enabled", "skip_function", "skip_during_range"] for job in list(schedule.keys()): # iterate over a copy since we will mutate it if job in _hidden: continue # Default jobs added by salt begin with __ # by default hide them unless show_all is True. - if job.startswith('__') and not show_all: + if job.startswith("__") and not show_all: del schedule[job] continue # if enabled is not included in the job, # assume job is enabled. - if 'enabled' not in schedule[job]: - schedule[job]['enabled'] = True + if "enabled" not in schedule[job]: + schedule[job]["enabled"] = True for item in pycopy.copy(schedule[job]): if item not in SCHEDULE_CONF: @@ -135,32 +133,32 @@ def list_(show_all=False, if schedule[job][item] is None: del schedule[job][item] continue - if schedule[job][item] == 'true': + if schedule[job][item] == "true": schedule[job][item] = True - if schedule[job][item] == 'false': + if schedule[job][item] == "false": schedule[job][item] = False # if the job is disabled and show_disabled is False, skip job - if not show_disabled and not schedule[job]['enabled']: + if not show_disabled and not schedule[job]["enabled"]: del schedule[job] continue - if '_seconds' in schedule[job]: + if "_seconds" in schedule[job]: # remove _seconds from the listing - del schedule[job]['_seconds'] + del schedule[job]["_seconds"] if schedule: if return_yaml: - tmp = {'schedule': schedule} + tmp = {"schedule": schedule} return salt.utils.yaml.safe_dump(tmp, default_flow_style=False) else: return schedule else: - return {'schedule': {}} + return {"schedule": {}} def is_enabled(name): - ''' + """ List a Job only if its enabled .. versionadded:: 2015.5.3 @@ -170,9 +168,9 @@ def is_enabled(name): .. code-block:: bash salt '*' schedule.is_enabled name=job_name - ''' + """ - current_schedule = __salt__['schedule.list'](show_all=False, return_yaml=False) + current_schedule = __salt__["schedule.list"](show_all=False, return_yaml=False) if name in current_schedule: return current_schedule[name] else: @@ -180,7 +178,7 @@ def is_enabled(name): def purge(**kwargs): - ''' + """ Purge all the jobs currently scheduled on the minion CLI Example: @@ -188,48 +186,58 @@ def purge(**kwargs): .. code-block:: bash salt '*' schedule.purge - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} for name in list_(show_all=True, return_yaml=False): - if name == 'enabled': + if name == "enabled": continue - if name.startswith('__'): + if name.startswith("__"): continue - if 'test' in kwargs and kwargs['test']: - ret['result'] = True - ret['comment'].append('Job: {0} would be deleted from schedule.'.format(name)) + if "test" in kwargs and kwargs["test"]: + ret["result"] = True + ret["comment"].append( + "Job: {0} would be deleted from schedule.".format(name) + ) else: - persist = kwargs.get('persist', True) + persist = kwargs.get("persist", True) try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire']({'name': name, - 'func': 'delete', - 'persist': persist}, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"]( + {"name": name, "func": "delete", "persist": persist}, + "manage_schedule", + ) if res: - event_ret = event_bus.get_event(tag='/salt/minion/minion_schedule_delete_complete', wait=30) - if event_ret and event_ret['complete']: - _schedule_ret = event_ret['schedule'] + event_ret = event_bus.get_event( + tag="/salt/minion/minion_schedule_delete_complete", wait=30 + ) + if event_ret and event_ret["complete"]: + _schedule_ret = event_ret["schedule"] if name not in _schedule_ret: - ret['result'] = True - ret['comment'].append('Deleted job: {0} from schedule.'.format(name)) + ret["result"] = True + ret["comment"].append( + "Deleted job: {0} from schedule.".format(name) + ) else: - ret['comment'].append('Failed to delete job {0} from schedule.'.format(name)) - ret['result'] = True + ret["comment"].append( + "Failed to delete job {0} from schedule.".format( + name + ) + ) + ret["result"] = True except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule add failed.' - ret['result'] = True + ret["comment"] = "Event module not available. Schedule add failed." + ret["result"] = True return ret def delete(name, **kwargs): - ''' + """ Delete a job from the minion's schedule CLI Example: @@ -237,52 +245,62 @@ def delete(name, **kwargs): .. code-block:: bash salt '*' schedule.delete job1 - ''' + """ - ret = {'comment': 'Failed to delete job {0} from schedule.'.format(name), - 'result': False} + ret = { + "comment": "Failed to delete job {0} from schedule.".format(name), + "result": False, + } if not name: - ret['comment'] = 'Job name is required.' + ret["comment"] = "Job name is required." - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Job: {0} would be deleted from schedule.'.format(name) - ret['result'] = True + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Job: {0} would be deleted from schedule.".format(name) + ret["result"] = True else: - persist = kwargs.get('persist', True) + persist = kwargs.get("persist", True) - if name in list_(show_all=True, where='opts', return_yaml=False): - event_data = {'name': name, 'func': 'delete', 'persist': persist} - elif name in list_(show_all=True, where='pillar', return_yaml=False): - event_data = {'name': name, 'where': 'pillar', 'func': 'delete', 'persist': False} + if name in list_(show_all=True, where="opts", return_yaml=False): + event_data = {"name": name, "func": "delete", "persist": persist} + elif name in list_(show_all=True, where="pillar", return_yaml=False): + event_data = { + "name": name, + "where": "pillar", + "func": "delete", + "persist": False, + } else: - ret['comment'] = 'Job {0} does not exist.'.format(name) + ret["comment"] = "Job {0} does not exist.".format(name) return ret try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire'](event_data, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"](event_data, "manage_schedule") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_delete_complete', - wait=30, + tag="/salt/minion/minion_schedule_delete_complete", wait=30, ) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] + if event_ret and event_ret["complete"]: + schedule = event_ret["schedule"] if name not in schedule: - ret['result'] = True - ret['comment'] = 'Deleted Job {0} from schedule.'.format(name) + ret["result"] = True + ret["comment"] = "Deleted Job {0} from schedule.".format( + name + ) else: - ret['comment'] = 'Failed to delete job {0} from schedule.'.format(name) + ret[ + "comment" + ] = "Failed to delete job {0} from schedule.".format(name) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule add failed.' + ret["comment"] = "Event module not available. Schedule add failed." return ret def build_schedule_item(name, **kwargs): - ''' + """ Build a schedule job CLI Example: @@ -290,101 +308,115 @@ def build_schedule_item(name, **kwargs): .. code-block:: bash salt '*' schedule.build_schedule_item job1 function='test.ping' seconds=3600 - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False return ret schedule = {} schedule[name] = salt.utils.odict.OrderedDict() - schedule[name]['function'] = kwargs['function'] + schedule[name]["function"] = kwargs["function"] time_conflict = False - for item in ['seconds', 'minutes', 'hours', 'days']: - if item in kwargs and 'when' in kwargs: + for item in ["seconds", "minutes", "hours", "days"]: + if item in kwargs and "when" in kwargs: time_conflict = True - if item in kwargs and 'cron' in kwargs: + if item in kwargs and "cron" in kwargs: time_conflict = True if time_conflict: - ret['result'] = False - ret['comment'] = 'Unable to use "seconds", "minutes", "hours", or "days" with "when" or "cron" options.' + ret["result"] = False + ret[ + "comment" + ] = 'Unable to use "seconds", "minutes", "hours", or "days" with "when" or "cron" options.' return ret - if 'when' in kwargs and 'cron' in kwargs: - ret['result'] = False - ret['comment'] = 'Unable to use "when" and "cron" options together. Ignoring.' + if "when" in kwargs and "cron" in kwargs: + ret["result"] = False + ret["comment"] = 'Unable to use "when" and "cron" options together. Ignoring.' return ret - for item in ['seconds', 'minutes', 'hours', 'days']: + for item in ["seconds", "minutes", "hours", "days"]: if item in kwargs: schedule[name][item] = kwargs[item] - if 'return_job' in kwargs: - schedule[name]['return_job'] = kwargs['return_job'] + if "return_job" in kwargs: + schedule[name]["return_job"] = kwargs["return_job"] - if 'metadata' in kwargs: - schedule[name]['metadata'] = kwargs['metadata'] + if "metadata" in kwargs: + schedule[name]["metadata"] = kwargs["metadata"] - if 'job_args' in kwargs: - schedule[name]['args'] = kwargs['job_args'] + if "job_args" in kwargs: + schedule[name]["args"] = kwargs["job_args"] - if 'job_kwargs' in kwargs: - schedule[name]['kwargs'] = kwargs['job_kwargs'] + if "job_kwargs" in kwargs: + schedule[name]["kwargs"] = kwargs["job_kwargs"] - if 'maxrunning' in kwargs: - schedule[name]['maxrunning'] = kwargs['maxrunning'] + if "maxrunning" in kwargs: + schedule[name]["maxrunning"] = kwargs["maxrunning"] else: - schedule[name]['maxrunning'] = 1 + schedule[name]["maxrunning"] = 1 - if 'name' in kwargs: - schedule[name]['name'] = kwargs['name'] + if "name" in kwargs: + schedule[name]["name"] = kwargs["name"] else: - schedule[name]['name'] = name + schedule[name]["name"] = name - if 'enabled' in kwargs: - schedule[name]['enabled'] = kwargs['enabled'] + if "enabled" in kwargs: + schedule[name]["enabled"] = kwargs["enabled"] else: - schedule[name]['enabled'] = True + schedule[name]["enabled"] = True - if 'jid_include' not in kwargs or kwargs['jid_include']: - schedule[name]['jid_include'] = True + if "jid_include" not in kwargs or kwargs["jid_include"]: + schedule[name]["jid_include"] = True - if 'splay' in kwargs: - if isinstance(kwargs['splay'], dict): + if "splay" in kwargs: + if isinstance(kwargs["splay"], dict): # Ensure ordering of start and end arguments - schedule[name]['splay'] = salt.utils.odict.OrderedDict() - schedule[name]['splay']['start'] = kwargs['splay']['start'] - schedule[name]['splay']['end'] = kwargs['splay']['end'] + schedule[name]["splay"] = salt.utils.odict.OrderedDict() + schedule[name]["splay"]["start"] = kwargs["splay"]["start"] + schedule[name]["splay"]["end"] = kwargs["splay"]["end"] else: - schedule[name]['splay'] = kwargs['splay'] + schedule[name]["splay"] = kwargs["splay"] - if 'when' in kwargs: + if "when" in kwargs: if not _WHEN_SUPPORTED: - ret['result'] = False - ret['comment'] = 'Missing dateutil.parser, "when" is unavailable.' + ret["result"] = False + ret["comment"] = 'Missing dateutil.parser, "when" is unavailable.' return ret else: - validate_when = kwargs['when'] + validate_when = kwargs["when"] if not isinstance(validate_when, list): validate_when = [validate_when] for _when in validate_when: try: dateutil_parser.parse(_when) except ValueError: - ret['result'] = False - ret['comment'] = 'Schedule item {0} for "when" in invalid.'.format(_when) + ret["result"] = False + ret["comment"] = 'Schedule item {0} for "when" in invalid.'.format( + _when + ) return ret - for item in ['range', 'when', 'once', 'once_fmt', 'cron', - 'returner', 'after', 'return_config', 'return_kwargs', - 'until', 'run_on_start', 'skip_during_range']: + for item in [ + "range", + "when", + "once", + "once_fmt", + "cron", + "returner", + "after", + "return_config", + "return_kwargs", + "until", + "run_on_start", + "skip_during_range", + ]: if item in kwargs: schedule[name][item] = kwargs[item] @@ -392,7 +424,7 @@ def build_schedule_item(name, **kwargs): def add(name, **kwargs): - ''' + """ Add a job to the schedule CLI Example: @@ -402,73 +434,81 @@ def add(name, **kwargs): salt '*' schedule.add job1 function='test.ping' seconds=3600 # If function have some arguments, use job_args salt '*' schedule.add job2 function='cmd.run' job_args="['date >> /tmp/date.log']" seconds=60 - ''' + """ - ret = {'comment': 'Failed to add job {0} to schedule.'.format(name), - 'result': False} + ret = { + "comment": "Failed to add job {0} to schedule.".format(name), + "result": False, + } if name in list_(show_all=True, return_yaml=False): - ret['comment'] = 'Job {0} already exists in schedule.'.format(name) - ret['result'] = False + ret["comment"] = "Job {0} already exists in schedule.".format(name) + ret["result"] = False return ret if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False time_conflict = False - for item in ['seconds', 'minutes', 'hours', 'days']: - if item in kwargs and 'when' in kwargs: + for item in ["seconds", "minutes", "hours", "days"]: + if item in kwargs and "when" in kwargs: time_conflict = True - if item in kwargs and 'cron' in kwargs: + if item in kwargs and "cron" in kwargs: time_conflict = True if time_conflict: - ret['comment'] = 'Error: Unable to use "seconds", "minutes", "hours", or "days" with "when" or "cron" options.' + ret[ + "comment" + ] = 'Error: Unable to use "seconds", "minutes", "hours", or "days" with "when" or "cron" options.' return ret - if 'when' in kwargs and 'cron' in kwargs: - ret['comment'] = 'Unable to use "when" and "cron" options together. Ignoring.' + if "when" in kwargs and "cron" in kwargs: + ret["comment"] = 'Unable to use "when" and "cron" options together. Ignoring.' return ret - persist = kwargs.get('persist', True) + persist = kwargs.get("persist", True) _new = build_schedule_item(name, **kwargs) - if 'result' in _new and not _new['result']: + if "result" in _new and not _new["result"]: return _new schedule_data = {} schedule_data[name] = _new - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Job: {0} would be added to schedule.'.format(name) - ret['result'] = True + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Job: {0} would be added to schedule.".format(name) + ret["result"] = True else: try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire']({'name': name, - 'schedule': schedule_data, - 'func': 'add', - 'persist': persist}, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"]( + { + "name": name, + "schedule": schedule_data, + "func": "add", + "persist": persist, + }, + "manage_schedule", + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_add_complete', - wait=30, + tag="/salt/minion/minion_schedule_add_complete", wait=30, ) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] + if event_ret and event_ret["complete"]: + schedule = event_ret["schedule"] if name in schedule: - ret['result'] = True - ret['comment'] = 'Added job: {0} to schedule.'.format(name) + ret["result"] = True + ret["comment"] = "Added job: {0} to schedule.".format(name) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule add failed.' + ret["comment"] = "Event module not available. Schedule add failed." return ret def modify(name, **kwargs): - ''' + """ Modify an existing job in the schedule CLI Example: @@ -476,89 +516,95 @@ def modify(name, **kwargs): .. code-block:: bash salt '*' schedule.modify job1 function='test.ping' seconds=3600 - ''' + """ - ret = {'comment': '', - 'changes': {}, - 'result': True} + ret = {"comment": "", "changes": {}, "result": True} time_conflict = False - for item in ['seconds', 'minutes', 'hours', 'days']: - if item in kwargs and 'when' in kwargs: + for item in ["seconds", "minutes", "hours", "days"]: + if item in kwargs and "when" in kwargs: time_conflict = True - if item in kwargs and 'cron' in kwargs: + if item in kwargs and "cron" in kwargs: time_conflict = True if time_conflict: - ret['result'] = False - ret['comment'] = 'Error: Unable to use "seconds", "minutes", "hours", or "days" with "when" option.' + ret["result"] = False + ret[ + "comment" + ] = 'Error: Unable to use "seconds", "minutes", "hours", or "days" with "when" option.' return ret - if 'when' in kwargs and 'cron' in kwargs: - ret['result'] = False - ret['comment'] = 'Unable to use "when" and "cron" options together. Ignoring.' + if "when" in kwargs and "cron" in kwargs: + ret["result"] = False + ret["comment"] = 'Unable to use "when" and "cron" options together. Ignoring.' return ret current_schedule = list_(show_all=True, return_yaml=False) if name not in current_schedule: - ret['comment'] = 'Job {0} does not exist in schedule.'.format(name) - ret['result'] = False + ret["comment"] = "Job {0} does not exist in schedule.".format(name) + ret["result"] = False return ret _current = current_schedule[name] - if 'function' not in kwargs: - kwargs['function'] = _current.get('function') + if "function" not in kwargs: + kwargs["function"] = _current.get("function") - if '_seconds' in _current: - _current['seconds'] = _current['_seconds'] - del _current['_seconds'] + if "_seconds" in _current: + _current["seconds"] = _current["_seconds"] + del _current["_seconds"] _new = build_schedule_item(name, **kwargs) - if 'result' in _new and not _new['result']: + if "result" in _new and not _new["result"]: return _new if _new == _current: - ret['comment'] = 'Job {0} in correct state'.format(name) + ret["comment"] = "Job {0} in correct state".format(name) return ret - _current_lines = ['{0}:{1}\n'.format(key, value) - for (key, value) in sorted(_current.items())] - _new_lines = ['{0}:{1}\n'.format(key, value) - for (key, value) in sorted(_new.items())] + _current_lines = [ + "{0}:{1}\n".format(key, value) for (key, value) in sorted(_current.items()) + ] + _new_lines = [ + "{0}:{1}\n".format(key, value) for (key, value) in sorted(_new.items()) + ] _diff = difflib.unified_diff(_current_lines, _new_lines) - ret['changes']['diff'] = ''.join(_diff) + ret["changes"]["diff"] = "".join(_diff) - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Job: {0} would be modified in schedule.'.format(name) + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Job: {0} would be modified in schedule.".format(name) else: - persist = kwargs.get('persist', True) - if name in list_(show_all=True, where='opts', return_yaml=False): - event_data = {'name': name, - 'schedule': _new, - 'func': 'modify', - 'persist': persist} - elif name in list_(show_all=True, where='pillar', return_yaml=False): - event_data = {'name': name, - 'schedule': _new, - 'where': 'pillar', - 'func': 'modify', - 'persist': False} + persist = kwargs.get("persist", True) + if name in list_(show_all=True, where="opts", return_yaml=False): + event_data = { + "name": name, + "schedule": _new, + "func": "modify", + "persist": persist, + } + elif name in list_(show_all=True, where="pillar", return_yaml=False): + event_data = { + "name": name, + "schedule": _new, + "where": "pillar", + "func": "modify", + "persist": False, + } - out = __salt__['event.fire'](event_data, 'manage_schedule') + out = __salt__["event.fire"](event_data, "manage_schedule") if out: - ret['comment'] = 'Modified job: {0} in schedule.'.format(name) + ret["comment"] = "Modified job: {0} in schedule.".format(name) else: - ret['comment'] = 'Failed to modify job {0} in schedule.'.format(name) - ret['result'] = False + ret["comment"] = "Failed to modify job {0} in schedule.".format(name) + ret["result"] = False return ret def run_job(name, force=False): - ''' + """ Run a scheduled job on the minion immediately CLI Example: @@ -569,35 +615,36 @@ def run_job(name, force=False): salt '*' schedule.run_job job1 force=True Force the job to run even if it is disabled. - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False schedule = list_(show_all=True, return_yaml=False) if name in schedule: data = schedule[name] - if 'enabled' in data and not data['enabled'] and not force: - ret['comment'] = 'Job {0} is disabled.'.format(name) + if "enabled" in data and not data["enabled"] and not force: + ret["comment"] = "Job {0} is disabled.".format(name) else: - out = __salt__['event.fire']({'name': name, 'func': 'run_job'}, 'manage_schedule') + out = __salt__["event.fire"]( + {"name": name, "func": "run_job"}, "manage_schedule" + ) if out: - ret['comment'] = 'Scheduling Job {0} on minion.'.format(name) + ret["comment"] = "Scheduling Job {0} on minion.".format(name) else: - ret['comment'] = 'Failed to run job {0} on minion.'.format(name) - ret['result'] = False + ret["comment"] = "Failed to run job {0} on minion.".format(name) + ret["result"] = False else: - ret['comment'] = 'Job {0} does not exist.'.format(name) - ret['result'] = False + ret["comment"] = "Job {0} does not exist.".format(name) + ret["result"] = False return ret def enable_job(name, **kwargs): - ''' + """ Enable a job in the minion's schedule CLI Example: @@ -605,55 +652,61 @@ def enable_job(name, **kwargs): .. code-block:: bash salt '*' schedule.enable_job job1 - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False - if 'test' in __opts__ and __opts__['test']: - ret['comment'] = 'Job: {0} would be enabled in schedule.'.format(name) + if "test" in __opts__ and __opts__["test"]: + ret["comment"] = "Job: {0} would be enabled in schedule.".format(name) else: - persist = kwargs.get('persist', True) + persist = kwargs.get("persist", True) - if name in list_(show_all=True, where='opts', return_yaml=False): - event_data = {'name': name, 'func': 'enable_job', 'persist': persist} - elif name in list_(show_all=True, where='pillar', return_yaml=False): - event_data = {'name': name, 'where': 'pillar', 'func': 'enable_job', 'persist': False} + if name in list_(show_all=True, where="opts", return_yaml=False): + event_data = {"name": name, "func": "enable_job", "persist": persist} + elif name in list_(show_all=True, where="pillar", return_yaml=False): + event_data = { + "name": name, + "where": "pillar", + "func": "enable_job", + "persist": False, + } else: - ret['comment'] = 'Job {0} does not exist.'.format(name) - ret['result'] = False + ret["comment"] = "Job {0} does not exist.".format(name) + ret["result"] = False return ret try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire'](event_data, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"](event_data, "manage_schedule") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_enabled_job_complete', + tag="/salt/minion/minion_schedule_enabled_job_complete", wait=30, ) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] + if event_ret and event_ret["complete"]: + schedule = event_ret["schedule"] # check item exists in schedule and is enabled - if name in schedule and schedule[name]['enabled']: - ret['result'] = True - ret['comment'] = 'Enabled Job {0} in schedule.'.format(name) + if name in schedule and schedule[name]["enabled"]: + ret["result"] = True + ret["comment"] = "Enabled Job {0} in schedule.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to enable job {0} in schedule.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "Failed to enable job {0} in schedule.".format(name) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule enable job failed.' + ret["comment"] = "Event module not available. Schedule enable job failed." return ret def disable_job(name, **kwargs): - ''' + """ Disable a job in the minion's schedule CLI Example: @@ -661,55 +714,63 @@ def disable_job(name, **kwargs): .. code-block:: bash salt '*' schedule.disable_job job1 - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Job: {0} would be disabled in schedule.'.format(name) + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Job: {0} would be disabled in schedule.".format(name) else: - persist = kwargs.get('persist', True) + persist = kwargs.get("persist", True) - if name in list_(show_all=True, where='opts', return_yaml=False): - event_data = {'name': name, 'func': 'disable_job', 'persist': persist} - elif name in list_(show_all=True, where='pillar'): - event_data = {'name': name, 'where': 'pillar', 'func': 'disable_job', 'persist': False} + if name in list_(show_all=True, where="opts", return_yaml=False): + event_data = {"name": name, "func": "disable_job", "persist": persist} + elif name in list_(show_all=True, where="pillar"): + event_data = { + "name": name, + "where": "pillar", + "func": "disable_job", + "persist": False, + } else: - ret['comment'] = 'Job {0} does not exist.'.format(name) - ret['result'] = False + ret["comment"] = "Job {0} does not exist.".format(name) + ret["result"] = False return ret try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire'](event_data, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"](event_data, "manage_schedule") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_disabled_job_complete', + tag="/salt/minion/minion_schedule_disabled_job_complete", wait=30, ) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] + if event_ret and event_ret["complete"]: + schedule = event_ret["schedule"] # check item exists in schedule and is enabled - if name in schedule and not schedule[name]['enabled']: - ret['result'] = True - ret['comment'] = 'Disabled Job {0} in schedule.'.format(name) + if name in schedule and not schedule[name]["enabled"]: + ret["result"] = True + ret["comment"] = "Disabled Job {0} in schedule.".format( + name + ) else: - ret['result'] = False - ret['comment'] = 'Failed to disable job {0} in schedule.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "Failed to disable job {0} in schedule.".format(name) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule enable job failed.' + ret["comment"] = "Event module not available. Schedule enable job failed." return ret def save(**kwargs): - ''' + """ Save all scheduled jobs on the minion CLI Example: @@ -717,36 +778,36 @@ def save(**kwargs): .. code-block:: bash salt '*' schedule.save - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Schedule would be saved.' + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Schedule would be saved." else: try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire']({'func': 'save_schedule'}, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"]( + {"func": "save_schedule"}, "manage_schedule" + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_saved', - wait=30, + tag="/salt/minion/minion_schedule_saved", wait=30, ) - if event_ret and event_ret['complete']: - ret['result'] = True - ret['comment'] = 'Schedule (non-pillar items) saved.' + if event_ret and event_ret["complete"]: + ret["result"] = True + ret["comment"] = "Schedule (non-pillar items) saved." else: - ret['result'] = False - ret['comment'] = 'Failed to save schedule.' + ret["result"] = False + ret["comment"] = "Failed to save schedule." except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule save failed.' + ret["comment"] = "Event module not available. Schedule save failed." return ret def enable(**kwargs): - ''' + """ Enable all scheduled jobs on the minion CLI Example: @@ -754,41 +815,41 @@ def enable(**kwargs): .. code-block:: bash salt '*' schedule.enable - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Schedule would be enabled.' + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Schedule would be enabled." else: - persist = kwargs.get('persist', True) + persist = kwargs.get("persist", True) try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire']({'func': 'enable', 'persist': persist}, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"]( + {"func": "enable", "persist": persist}, "manage_schedule" + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_enabled_complete', - wait=30, + tag="/salt/minion/minion_schedule_enabled_complete", wait=30, ) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - if 'enabled' in schedule and schedule['enabled']: - ret['result'] = True - ret['comment'] = 'Enabled schedule on minion.' + if event_ret and event_ret["complete"]: + schedule = event_ret["schedule"] + if "enabled" in schedule and schedule["enabled"]: + ret["result"] = True + ret["comment"] = "Enabled schedule on minion." else: - ret['result'] = False - ret['comment'] = 'Failed to enable schedule on minion.' + ret["result"] = False + ret["comment"] = "Failed to enable schedule on minion." return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule enable job failed.' + ret["comment"] = "Event module not available. Schedule enable job failed." return ret def disable(**kwargs): - ''' + """ Disable all scheduled jobs on the minion CLI Example: @@ -796,41 +857,41 @@ def disable(**kwargs): .. code-block:: bash salt '*' schedule.disable - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Schedule would be disabled.' + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Schedule would be disabled." else: - persist = kwargs.get('persist', True) + persist = kwargs.get("persist", True) try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire']({'func': 'disable', 'persist': persist}, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"]( + {"func": "disable", "persist": persist}, "manage_schedule" + ) if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_disabled_complete', - wait=30, + tag="/salt/minion/minion_schedule_disabled_complete", wait=30, ) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] - if 'enabled' in schedule and not schedule['enabled']: - ret['result'] = True - ret['comment'] = 'Disabled schedule on minion.' + if event_ret and event_ret["complete"]: + schedule = event_ret["schedule"] + if "enabled" in schedule and not schedule["enabled"]: + ret["result"] = True + ret["comment"] = "Disabled schedule on minion." else: - ret['result'] = False - ret['comment'] = 'Failed to disable schedule on minion.' + ret["result"] = False + ret["comment"] = "Failed to disable schedule on minion." return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule disable job failed.' + ret["comment"] = "Event module not available. Schedule disable job failed." return ret def reload_(): - ''' + """ Reload saved scheduled jobs on the minion CLI Example: @@ -838,48 +899,61 @@ def reload_(): .. code-block:: bash salt '*' schedule.reload - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} # If there a schedule defined in pillar, refresh it. - if 'schedule' in __pillar__: - out = __salt__['event.fire']({}, 'pillar_refresh') + if "schedule" in __pillar__: + out = __salt__["event.fire"]({}, "pillar_refresh") if out: - ret['comment'].append('Reloaded schedule from pillar on minion.') + ret["comment"].append("Reloaded schedule from pillar on minion.") else: - ret['comment'].append('Failed to reload schedule from pillar on minion.') - ret['result'] = False + ret["comment"].append("Failed to reload schedule from pillar on minion.") + ret["result"] = False # move this file into an configurable opt - sfn = '{0}/{1}/schedule.conf'.format(__opts__['config_dir'], os.path.dirname(__opts__['default_include'])) + sfn = "{0}/{1}/schedule.conf".format( + __opts__["config_dir"], os.path.dirname(__opts__["default_include"]) + ) if os.path.isfile(sfn): - with salt.utils.files.fopen(sfn, 'rb') as fp_: + with salt.utils.files.fopen(sfn, "rb") as fp_: try: schedule = salt.utils.yaml.safe_load(fp_) except salt.utils.yaml.YAMLError as exc: - ret['comment'].append('Unable to read existing schedule file: {0}'.format(exc)) + ret["comment"].append( + "Unable to read existing schedule file: {0}".format(exc) + ) if schedule: - if 'schedule' in schedule and schedule['schedule']: - out = __salt__['event.fire']({'func': 'reload', 'schedule': schedule}, 'manage_schedule') + if "schedule" in schedule and schedule["schedule"]: + out = __salt__["event.fire"]( + {"func": "reload", "schedule": schedule}, "manage_schedule" + ) if out: - ret['comment'].append('Reloaded schedule on minion from schedule.conf.') + ret["comment"].append( + "Reloaded schedule on minion from schedule.conf." + ) else: - ret['comment'].append('Failed to reload schedule on minion from schedule.conf.') - ret['result'] = False + ret["comment"].append( + "Failed to reload schedule on minion from schedule.conf." + ) + ret["result"] = False else: - ret['comment'].append('Failed to reload schedule on minion. Saved file is empty or invalid.') - ret['result'] = False + ret["comment"].append( + "Failed to reload schedule on minion. Saved file is empty or invalid." + ) + ret["result"] = False else: - ret['comment'].append('Failed to reload schedule on minion. Saved file is empty or invalid.') - ret['result'] = False + ret["comment"].append( + "Failed to reload schedule on minion. Saved file is empty or invalid." + ) + ret["result"] = False return ret def move(name, target, **kwargs): - ''' + """ Move scheduled job to another minion or minions. CLI Example: @@ -887,37 +961,36 @@ def move(name, target, **kwargs): .. code-block:: bash salt '*' schedule.move jobname target - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Job: {0} would be moved from schedule.'.format(name) + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Job: {0} would be moved from schedule.".format(name) else: - opts_schedule = list_(show_all=True, where='opts', return_yaml=False) - pillar_schedule = list_(show_all=True, where='pillar', return_yaml=False) + opts_schedule = list_(show_all=True, where="opts", return_yaml=False) + pillar_schedule = list_(show_all=True, where="pillar", return_yaml=False) if name in opts_schedule: schedule_data = opts_schedule[name] where = None elif name in pillar_schedule: schedule_data = pillar_schedule[name] - where = 'pillar' + where = "pillar" else: - ret['comment'] = 'Job {0} does not exist.'.format(name) - ret['result'] = False + ret["comment"] = "Job {0} does not exist.".format(name) + ret["result"] = False return ret schedule_opts = [] for key, value in six.iteritems(schedule_data): - temp = '{0}={1}'.format(key, value) + temp = "{0}={1}".format(key, value) schedule_opts.append(temp) - response = __salt__['publish.publish'](target, 'schedule.add', schedule_opts) + response = __salt__["publish.publish"](target, "schedule.add", schedule_opts) # Get errors and list of affeced minions errors = [] @@ -929,23 +1002,23 @@ def move(name, target, **kwargs): # parse response if not response: - ret['comment'] = 'no servers answered the published schedule.add command' + ret["comment"] = "no servers answered the published schedule.add command" return ret elif len(errors) > 0: - ret['comment'] = 'the following minions return False' - ret['minions'] = errors + ret["comment"] = "the following minions return False" + ret["minions"] = errors return ret else: delete(name, where=where) - ret['result'] = True - ret['comment'] = 'Moved Job {0} from schedule.'.format(name) - ret['minions'] = minions + ret["result"] = True + ret["comment"] = "Moved Job {0} from schedule.".format(name) + ret["minions"] = minions return ret return ret def copy(name, target, **kwargs): - ''' + """ Copy scheduled job to another minion or minions. CLI Example: @@ -953,35 +1026,34 @@ def copy(name, target, **kwargs): .. code-block:: bash salt '*' schedule.copy jobname target - ''' + """ - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False - if 'test' in kwargs and kwargs['test']: - ret['comment'] = 'Job: {0} would be copied from schedule.'.format(name) + if "test" in kwargs and kwargs["test"]: + ret["comment"] = "Job: {0} would be copied from schedule.".format(name) else: - opts_schedule = list_(show_all=True, where='opts', return_yaml=False) - pillar_schedule = list_(show_all=True, where='pillar', return_yaml=False) + opts_schedule = list_(show_all=True, where="opts", return_yaml=False) + pillar_schedule = list_(show_all=True, where="pillar", return_yaml=False) if name in opts_schedule: schedule_data = opts_schedule[name] elif name in pillar_schedule: schedule_data = pillar_schedule[name] else: - ret['comment'] = 'Job {0} does not exist.'.format(name) - ret['result'] = False + ret["comment"] = "Job {0} does not exist.".format(name) + ret["result"] = False return ret schedule_opts = [] for key, value in six.iteritems(schedule_data): - temp = '{0}={1}'.format(key, value) + temp = "{0}={1}".format(key, value) schedule_opts.append(temp) - response = __salt__['publish.publish'](target, 'schedule.add', schedule_opts) + response = __salt__["publish.publish"](target, "schedule.add", schedule_opts) # Get errors and list of affeced minions errors = [] @@ -993,25 +1065,22 @@ def copy(name, target, **kwargs): # parse response if not response: - ret['comment'] = 'no servers answered the published schedule.add command' + ret["comment"] = "no servers answered the published schedule.add command" return ret elif len(errors) > 0: - ret['comment'] = 'the following minions return False' - ret['minions'] = errors + ret["comment"] = "the following minions return False" + ret["minions"] = errors return ret else: - ret['result'] = True - ret['comment'] = 'Copied Job {0} from schedule to minion(s).'.format(name) - ret['minions'] = minions + ret["result"] = True + ret["comment"] = "Copied Job {0} from schedule to minion(s).".format(name) + ret["minions"] = minions return ret return ret -def postpone_job(name, - current_time, - new_time, - **kwargs): - ''' +def postpone_job(name, current_time, new_time, **kwargs): + """ Postpone a job in the minion's schedule Current time and new time should be in date string format, @@ -1026,97 +1095,102 @@ def postpone_job(name, salt '*' schedule.postpone_job job current_time new_time salt '*' schedule.postpone_job job current_time new_time time_fmt='%Y-%m-%dT%H:%M:%S' - ''' + """ - time_fmt = kwargs.get('time_fmt') or '%Y-%m-%dT%H:%M:%S' - ret = {'comment': [], - 'result': True} + time_fmt = kwargs.get("time_fmt") or "%Y-%m-%dT%H:%M:%S" + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False return ret if not current_time: - ret['comment'] = 'Job current time is required.' - ret['result'] = False + ret["comment"] = "Job current time is required." + ret["result"] = False return ret else: try: # Validate date string datetime.datetime.strptime(current_time, time_fmt) except (TypeError, ValueError): - log.error('Date string could not be parsed: %s, %s', - new_time, time_fmt) + log.error("Date string could not be parsed: %s, %s", new_time, time_fmt) - ret['comment'] = 'Date string could not be parsed.' - ret['result'] = False + ret["comment"] = "Date string could not be parsed." + ret["result"] = False return ret if not new_time: - ret['comment'] = 'Job new_time is required.' - ret['result'] = False + ret["comment"] = "Job new_time is required." + ret["result"] = False return ret else: try: # Validate date string datetime.datetime.strptime(new_time, time_fmt) except (TypeError, ValueError): - log.error('Date string could not be parsed: %s, %s', - new_time, time_fmt) + log.error("Date string could not be parsed: %s, %s", new_time, time_fmt) - ret['comment'] = 'Date string could not be parsed.' - ret['result'] = False + ret["comment"] = "Date string could not be parsed." + ret["result"] = False return ret - if 'test' in __opts__ and __opts__['test']: - ret['comment'] = 'Job: {0} would be postponed in schedule.'.format(name) + if "test" in __opts__ and __opts__["test"]: + ret["comment"] = "Job: {0} would be postponed in schedule.".format(name) else: - if name in list_(show_all=True, where='opts', return_yaml=False): - event_data = {'name': name, - 'time': current_time, - 'new_time': new_time, - 'time_fmt': time_fmt, - 'func': 'postpone_job'} - elif name in list_(show_all=True, where='pillar', return_yaml=False): - event_data = {'name': name, - 'time': current_time, - 'new_time': new_time, - 'time_fmt': time_fmt, - 'where': 'pillar', - 'func': 'postpone_job'} + if name in list_(show_all=True, where="opts", return_yaml=False): + event_data = { + "name": name, + "time": current_time, + "new_time": new_time, + "time_fmt": time_fmt, + "func": "postpone_job", + } + elif name in list_(show_all=True, where="pillar", return_yaml=False): + event_data = { + "name": name, + "time": current_time, + "new_time": new_time, + "time_fmt": time_fmt, + "where": "pillar", + "func": "postpone_job", + } else: - ret['comment'] = 'Job {0} does not exist.'.format(name) - ret['result'] = False + ret["comment"] = "Job {0} does not exist.".format(name) + ret["result"] = False return ret try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire'](event_data, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"](event_data, "manage_schedule") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_postpone_job_complete', + tag="/salt/minion/minion_schedule_postpone_job_complete", wait=30, ) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] + if event_ret and event_ret["complete"]: + schedule = event_ret["schedule"] # check item exists in schedule and is enabled - if name in schedule and schedule[name]['enabled']: - ret['result'] = True - ret['comment'] = 'Postponed Job {0} in schedule.'.format(name) + if name in schedule and schedule[name]["enabled"]: + ret["result"] = True + ret["comment"] = "Postponed Job {0} in schedule.".format( + name + ) else: - ret['result'] = False - ret['comment'] = 'Failed to postpone job {0} in schedule.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "Failed to postpone job {0} in schedule.".format(name) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule postpone job failed.' + ret["comment"] = "Event module not available. Schedule postpone job failed." return ret def skip_job(name, current_time, **kwargs): - ''' + """ Skip a job in the minion's schedule at specified time. Time to skip should be specified as date string format, @@ -1129,77 +1203,82 @@ def skip_job(name, current_time, **kwargs): .. code-block:: bash salt '*' schedule.skip_job job time - ''' - time_fmt = kwargs.get('time_fmt') or '%Y-%m-%dT%H:%M:%S' + """ + time_fmt = kwargs.get("time_fmt") or "%Y-%m-%dT%H:%M:%S" - ret = {'comment': [], - 'result': True} + ret = {"comment": [], "result": True} if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False if not current_time: - ret['comment'] = 'Job time is required.' - ret['result'] = False + ret["comment"] = "Job time is required." + ret["result"] = False else: # Validate date string try: datetime.datetime.strptime(current_time, time_fmt) except (TypeError, ValueError): - log.error('Date string could not be parsed: %s, %s', - current_time, time_fmt) + log.error("Date string could not be parsed: %s, %s", current_time, time_fmt) - ret['comment'] = 'Date string could not be parsed.' - ret['result'] = False + ret["comment"] = "Date string could not be parsed." + ret["result"] = False return ret - if 'test' in __opts__ and __opts__['test']: - ret['comment'] = 'Job: {0} would be skipped in schedule.'.format(name) + if "test" in __opts__ and __opts__["test"]: + ret["comment"] = "Job: {0} would be skipped in schedule.".format(name) else: - if name in list_(show_all=True, where='opts', return_yaml=False): - event_data = {'name': name, - 'time': current_time, - 'time_fmt': time_fmt, - 'func': 'skip_job'} - elif name in list_(show_all=True, where='pillar', return_yaml=False): - event_data = {'name': name, - 'time': current_time, - 'time_fmt': time_fmt, - 'where': 'pillar', - 'func': 'skip_job'} + if name in list_(show_all=True, where="opts", return_yaml=False): + event_data = { + "name": name, + "time": current_time, + "time_fmt": time_fmt, + "func": "skip_job", + } + elif name in list_(show_all=True, where="pillar", return_yaml=False): + event_data = { + "name": name, + "time": current_time, + "time_fmt": time_fmt, + "where": "pillar", + "func": "skip_job", + } else: - ret['comment'] = 'Job {0} does not exist.'.format(name) - ret['result'] = False + ret["comment"] = "Job {0} does not exist.".format(name) + ret["result"] = False return ret try: - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire'](event_data, 'manage_schedule') + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"](event_data, "manage_schedule") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_skip_job_complete', - wait=30, + tag="/salt/minion/minion_schedule_skip_job_complete", wait=30, ) - if event_ret and event_ret['complete']: - schedule = event_ret['schedule'] + if event_ret and event_ret["complete"]: + schedule = event_ret["schedule"] # check item exists in schedule and is enabled - if name in schedule and schedule[name]['enabled']: - ret['result'] = True - ret['comment'] = 'Added Skip Job {0} in schedule.'.format(name) + if name in schedule and schedule[name]["enabled"]: + ret["result"] = True + ret["comment"] = "Added Skip Job {0} in schedule.".format( + name + ) else: - ret['result'] = False - ret['comment'] = 'Failed to skip job {0} in schedule.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "Failed to skip job {0} in schedule.".format(name) return ret except KeyError: # Effectively a no-op, since we can't really return without an event system - ret['comment'] = 'Event module not available. Schedule skip job failed.' + ret["comment"] = "Event module not available. Schedule skip job failed." return ret def show_next_fire_time(name, **kwargs): - ''' + """ Show the next fire time for scheduled job .. versionadded:: 2018.3.0 @@ -1210,33 +1289,33 @@ def show_next_fire_time(name, **kwargs): salt '*' schedule.show_next_fire_time job_name - ''' + """ - ret = {'result': True} + ret = {"result": True} if not name: - ret['comment'] = 'Job name is required.' - ret['result'] = False + ret["comment"] = "Job name is required." + ret["result"] = False try: - event_data = {'name': name, 'func': 'get_next_fire_time'} - with salt.utils.event.get_event('minion', opts=__opts__) as event_bus: - res = __salt__['event.fire'](event_data, - 'manage_schedule') + event_data = {"name": name, "func": "get_next_fire_time"} + with salt.utils.event.get_event("minion", opts=__opts__) as event_bus: + res = __salt__["event.fire"](event_data, "manage_schedule") if res: event_ret = event_bus.get_event( - tag='/salt/minion/minion_schedule_next_fire_time_complete', - wait=30, + tag="/salt/minion/minion_schedule_next_fire_time_complete", wait=30, ) except KeyError: # Effectively a no-op, since we can't really return without an event system ret = {} - ret['comment'] = 'Event module not available. Schedule show next fire time failed.' - ret['result'] = True + ret[ + "comment" + ] = "Event module not available. Schedule show next fire time failed." + ret["result"] = True return ret - if 'next_fire_time' in event_ret: - ret['next_fire_time'] = event_ret['next_fire_time'] + if "next_fire_time" in event_ret: + ret["next_fire_time"] = event_ret["next_fire_time"] else: - ret['comment'] = 'next fire time not available.' + ret["comment"] = "next fire time not available." return ret diff --git a/salt/modules/scp_mod.py b/salt/modules/scp_mod.py index c8cf7eb68e7..9c2b6ddfda7 100644 --- a/salt/modules/scp_mod.py +++ b/salt/modules/scp_mod.py @@ -1,17 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" SCP Module ========== .. versionadded:: 2019.2.0 Module to copy files via `SCP `_ -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import inspect + # Import python libs import logging -import inspect # Import salt modules from salt.ext import six @@ -19,19 +20,20 @@ from salt.ext import six try: import scp import paramiko + HAS_SCP = True except ImportError: HAS_SCP = False -__proxyenabled__ = ['*'] -__virtualname__ = 'scp' +__proxyenabled__ = ["*"] +__virtualname__ = "scp" log = logging.getLogger(__name__) def __virtual__(): if not HAS_SCP: - return False, 'Please install SCP for this modules: pip install scp' + return False, "Please install SCP for this modules: pip install scp" return __virtualname__ @@ -40,7 +42,7 @@ def _select_kwargs(**kwargs): scp_kwargs = {} PARAMIKO_KWARGS, _, _, _ = inspect.getargspec(paramiko.SSHClient.connect) PARAMIKO_KWARGS.pop(0) # strip self - PARAMIKO_KWARGS.append('auto_add_policy') + PARAMIKO_KWARGS.append("auto_add_policy") SCP_KWARGS, _, _, _ = inspect.getargspec(scp.SCPClient.__init__) SCP_KWARGS.pop(0) # strip self SCP_KWARGS.pop(0) # strip transport arg (it is passed in _prepare_connection) @@ -53,25 +55,20 @@ def _select_kwargs(**kwargs): def _prepare_connection(**kwargs): - ''' + """ Prepare the underlying SSH connection with the remote target. - ''' + """ paramiko_kwargs, scp_kwargs = _select_kwargs(**kwargs) ssh = paramiko.SSHClient() - if paramiko_kwargs.pop('auto_add_policy', False): + if paramiko_kwargs.pop("auto_add_policy", False): ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(**paramiko_kwargs) - scp_client = scp.SCPClient(ssh.get_transport(), - **scp_kwargs) + scp_client = scp.SCPClient(ssh.get_transport(), **scp_kwargs) return scp_client -def get(remote_path, - local_path='', - recursive=False, - preserve_times=False, - **kwargs): - ''' +def get(remote_path, local_path="", recursive=False, preserve_times=False, **kwargs): + """ Transfer files and directories from remote host to the localhost of the Minion. @@ -140,24 +137,23 @@ def get(remote_path, .. code-block:: bash salt '*' scp.get /var/tmp/file /tmp/file hostname=10.10.10.1 auto_add_policy=True - ''' + """ scp_client = _prepare_connection(**kwargs) - get_kwargs = { - 'recursive': recursive, - 'preserve_times': preserve_times - } + get_kwargs = {"recursive": recursive, "preserve_times": preserve_times} if local_path: - get_kwargs['local_path'] = local_path + get_kwargs["local_path"] = local_path return scp_client.get(remote_path, **get_kwargs) -def put(files, - remote_path=None, - recursive=False, - preserve_times=False, - saltenv='base', - **kwargs): - ''' +def put( + files, + remote_path=None, + recursive=False, + preserve_times=False, + saltenv="base", + **kwargs +): + """ Transfer files and directories to remote host. files @@ -227,18 +223,15 @@ def put(files, .. code-block:: bash salt '*' scp.put /path/to/file /var/tmp/file hostname=server1 auto_add_policy=True - ''' + """ scp_client = _prepare_connection(**kwargs) - put_kwargs = { - 'recursive': recursive, - 'preserve_times': preserve_times - } + put_kwargs = {"recursive": recursive, "preserve_times": preserve_times} if remote_path: - put_kwargs['remote_path'] = remote_path + put_kwargs["remote_path"] = remote_path cached_files = [] if not isinstance(files, (list, tuple)): files = [files] for file_ in files: - cached_file = __salt__['cp.cache_file'](file_, saltenv=saltenv) + cached_file = __salt__["cp.cache_file"](file_, saltenv=saltenv) cached_files.append(cached_file) return scp_client.put(cached_files, **put_kwargs) diff --git a/salt/modules/scsi.py b/salt/modules/scsi.py index 27d1aa9d542..1c449c41e70 100644 --- a/salt/modules/scsi.py +++ b/salt/modules/scsi.py @@ -1,22 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" SCSI administration module -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals -import os.path import logging +import os.path + import salt.utils.path log = logging.getLogger(__name__) -__func_alias__ = { - 'ls_': 'ls' -} +__func_alias__ = {"ls_": "ls"} def ls_(get_size=True): - ''' + """ List SCSI devices, with details CLI Examples: @@ -32,30 +31,30 @@ def ls_(get_size=True): due to lack of support for the '-s' option in lsscsi. .. versionadded:: 2015.5.10 - ''' + """ - if not salt.utils.path.which('lsscsi'): - __context__['retcode'] = 1 - return 'scsi.ls not available - lsscsi command not found' + if not salt.utils.path.which("lsscsi"): + __context__["retcode"] = 1 + return "scsi.ls not available - lsscsi command not found" if get_size: - cmd = 'lsscsi -dLsv' + cmd = "lsscsi -dLsv" else: - cmd = 'lsscsi -dLv' + cmd = "lsscsi -dLv" ret = {} - res = __salt__['cmd.run_all'](cmd) - rc = res.get('retcode', -1) + res = __salt__["cmd.run_all"](cmd) + rc = res.get("retcode", -1) if rc != 0: - __context__['retcode'] = rc - error = res.get('stderr', '').split('\n')[0] + __context__["retcode"] = rc + error = res.get("stderr", "").split("\n")[0] if error == "lsscsi: invalid option -- 's'": - return '{0} - try get_size=False'.format(error) - return res.get('stderr', '').split('\n')[0] - data = res.get('stdout', '') + return "{0} - try get_size=False".format(error) + return res.get("stderr", "").split("\n")[0] + data = res.get("stdout", "") for line in data.splitlines(): - if line.startswith('['): + if line.startswith("["): size = None major = None minor = None @@ -64,33 +63,30 @@ def ls_(get_size=True): if get_size: size = comps.pop() majmin = comps.pop() - if majmin.startswith('['): - major, minor = majmin.replace('[', '').replace(']', '').split(':') + if majmin.startswith("["): + major, minor = majmin.replace("[", "").replace("]", "").split(":") device = comps.pop() - model = ' '.join(comps[3:]) + model = " ".join(comps[3:]) ret[key] = { - 'lun': key.replace('[', '').replace(']', ''), - 'size': size, - 'major': major, - 'minor': minor, - 'device': device, - 'model': model, + "lun": key.replace("[", "").replace("]", ""), + "size": size, + "major": major, + "minor": minor, + "device": device, + "model": model, } - elif line.startswith(' '): - if line.strip().startswith('dir'): + elif line.startswith(" "): + if line.strip().startswith("dir"): comps = line.strip().split() - ret[key]['dir'] = [ - comps[1], - comps[2].replace('[', '').replace(']', '') - ] + ret[key]["dir"] = [comps[1], comps[2].replace("[", "").replace("]", "")] else: - comps = line.strip().split('=') + comps = line.strip().split("=") ret[key][comps[0]] = comps[1] return ret def rescan_all(host): - ''' + """ List scsi devices CLI Example: @@ -98,9 +94,9 @@ def rescan_all(host): .. code-block:: bash salt '*' scsi.rescan_all 0 - ''' - if os.path.isdir('/sys/class/scsi_host/host{0}'.format(host)): + """ + if os.path.isdir("/sys/class/scsi_host/host{0}".format(host)): cmd = 'echo "- - -" > /sys/class/scsi_host/host{0}/scan'.format(host) else: - return 'Host {0} does not exist'.format(host) - return __salt__['cmd.run'](cmd).splitlines() + return "Host {0} does not exist".format(host) + return __salt__["cmd.run"](cmd).splitlines() diff --git a/salt/modules/sdb.py b/salt/modules/sdb.py index 539c6487164..03b99d08453 100644 --- a/salt/modules/sdb.py +++ b/salt/modules/sdb.py @@ -1,21 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" Module for Manipulating Data via the Salt DB API ================================================ -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.utils.sdb - __func_alias__ = { - 'set_': 'set', + "set_": "set", } def get(uri): - ''' + """ Get a value from a db, using a uri in the form of sdb:///. If the uri provided does not start with sdb://, then it will be returned as-is. @@ -24,12 +23,12 @@ def get(uri): .. code-block:: bash salt '*' sdb.get sdb://mymemcached/foo - ''' + """ return salt.utils.sdb.sdb_get(uri, __opts__, __utils__) def set_(uri, value): - ''' + """ Set a value in a db, using a uri in the form of ``sdb:///``. If the uri provided does not start with ``sdb://`` or the value is not successfully set, return ``False``. @@ -39,12 +38,12 @@ def set_(uri, value): .. code-block:: bash salt '*' sdb.set sdb://mymemcached/foo bar - ''' + """ return salt.utils.sdb.sdb_set(uri, value, __opts__, __utils__) def delete(uri): - ''' + """ Delete a value from a db, using a uri in the form of ``sdb:///``. If the uri provided does not start with ``sdb://`` or the value is not successfully deleted, return ``False``. @@ -54,14 +53,14 @@ def delete(uri): .. code-block:: bash salt '*' sdb.delete sdb://mymemcached/foo - ''' + """ return salt.utils.sdb.sdb_delete(uri, __opts__, __utils__) -def get_or_set_hash(uri, - length=8, - chars='abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'): - ''' +def get_or_set_hash( + uri, length=8, chars="abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)" +): + """ Perform a one-time generation of a hash and write it to sdb. If that value has already been set return the value instead. @@ -90,5 +89,5 @@ def get_or_set_hash(uri, as directives by the YAML parser, such as strings beginning with ``%``. To avoid issues when using the output of this function in an SLS file containing YAML+Jinja, surround the call with single quotes. - ''' + """ return salt.utils.sdb.sdb_get_or_set_hash(uri, __opts__, length, chars, __utils__) diff --git a/salt/modules/seed.py b/salt/modules/seed.py index 77a571bbe86..1693021f71c 100644 --- a/salt/modules/seed.py +++ b/salt/modules/seed.py @@ -1,32 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Virtual machine image management tools -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import python libs import os import shutil -import logging import tempfile +import uuid + +import salt.config # Import salt libs import salt.crypt +import salt.syspaths import salt.utils.cloud import salt.utils.files -import salt.config -import salt.syspaths -import uuid - # Set up logging log = logging.getLogger(__name__) # Don't shadow built-in's. -__func_alias__ = { - 'apply_': 'apply' -} +__func_alias__ = {"apply_": "apply"} def _file_or_content(file_): @@ -37,7 +36,7 @@ def _file_or_content(file_): def prep_bootstrap(mpt): - ''' + """ Update and get the random script to a random place CLI Example: @@ -46,54 +45,68 @@ def prep_bootstrap(mpt): salt '*' seed.prep_bootstrap /tmp - ''' + """ # Verify that the boostrap script is downloaded - bs_ = __salt__['config.gather_bootstrap_script']() - fpd_ = os.path.join(mpt, 'tmp', "{0}".format( - uuid.uuid4())) + bs_ = __salt__["config.gather_bootstrap_script"]() + fpd_ = os.path.join(mpt, "tmp", "{0}".format(uuid.uuid4())) if not os.path.exists(fpd_): os.makedirs(fpd_) os.chmod(fpd_, 0o700) fp_ = os.path.join(fpd_, os.path.basename(bs_)) # Copy script into tmp shutil.copy(bs_, fp_) - tmppath = fpd_.replace(mpt, '') + tmppath = fpd_.replace(mpt, "") return fp_, tmppath def _mount(path, ftype, root=None): mpt = None - if ftype == 'block': + if ftype == "block": mpt = tempfile.mkdtemp() - if not __salt__['mount.mount'](mpt, path): + if not __salt__["mount.mount"](mpt, path): os.rmdir(mpt) return None - elif ftype == 'dir': + elif ftype == "dir": return path - elif ftype == 'file': - if 'guestfs.mount' in __salt__: - util = 'guestfs' - elif 'qemu_nbd.init' in __salt__: - util = 'qemu_nbd' + elif ftype == "file": + if "guestfs.mount" in __salt__: + util = "guestfs" + elif "qemu_nbd.init" in __salt__: + util = "qemu_nbd" else: return None - mpt = __salt__['mount.mount'](path, device=root, util=util) + mpt = __salt__["mount.mount"](path, device=root, util=util) if not mpt: return None return mpt -def _umount(mpt, ftype): - if ftype == 'block': - __salt__['mount.umount'](mpt) +def _umount(mpt, disk, ftype): + if ftype == "block": + __salt__["mount.umount"](mpt) os.rmdir(mpt) - elif ftype == 'file': - __salt__['mount.umount'](mpt, util='qemu_nbd') + elif ftype == "file": + util = None + if "guestfs.mount" in __salt__: + util = "guestfs" + elif "qemu_nbd.init" in __salt__: + util = "qemu_nbd" + if util: + __salt__["mount.umount"](mpt, device=disk, util=util) -def apply_(path, id_=None, config=None, approve_key=True, install=True, - prep_install=False, pub_key=None, priv_key=None, mount_point=None): - ''' +def apply_( + path, + id_=None, + config=None, + approve_key=True, + install=True, + prep_install=False, + pub_key=None, + priv_key=None, + mount_point=None, +): + """ Seed a location (disk image, directory, or block device) with the minion config, approve the minion's key, and/or install salt-minion. @@ -126,13 +139,13 @@ def apply_(path, id_=None, config=None, approve_key=True, install=True, prep_install Prepare the bootstrap script, but don't run it. Default: false - ''' - stats = __salt__['file.stats'](path, follow_symlinks=True) + """ + stats = __salt__["file.stats"](path, follow_symlinks=True) if not stats: - return '{0} does not exist'.format(path) - ftype = stats['type'] - path = stats['target'] - log.debug('Mounting %s at %s', ftype, path) + return "{0} does not exist".format(path) + ftype = stats["type"] + path = stats["target"] + log.debug("Mounting %s at %s", ftype, path) try: os.makedirs(path) except OSError: @@ -142,58 +155,62 @@ def apply_(path, id_=None, config=None, approve_key=True, install=True, mpt = _mount(path, ftype, mount_point) if not mpt: - return '{0} could not be mounted'.format(path) + return "{0} could not be mounted".format(path) - tmp = os.path.join(mpt, 'tmp') - log.debug('Attempting to create directory %s', tmp) + tmp = os.path.join(mpt, "tmp") + log.debug("Attempting to create directory %s", tmp) try: os.makedirs(tmp) except OSError: if not os.path.isdir(tmp): raise - cfg_files = mkconfig(config, tmp=tmp, id_=id_, approve_key=approve_key, - pub_key=pub_key, priv_key=priv_key) + cfg_files = mkconfig( + config, + tmp=tmp, + id_=id_, + approve_key=approve_key, + pub_key=pub_key, + priv_key=priv_key, + ) if _check_install(mpt): # salt-minion is already installed, just move the config and keys # into place - log.info('salt-minion pre-installed on image, ' - 'configuring as %s', id_) - minion_config = salt.config.minion_config(cfg_files['config']) - pki_dir = minion_config['pki_dir'] - if not os.path.isdir(os.path.join(mpt, pki_dir.lstrip('/'))): - __salt__['file.makedirs']( - os.path.join(mpt, pki_dir.lstrip('/'), '') - ) - os.rename(cfg_files['privkey'], os.path.join( - mpt, pki_dir.lstrip('/'), 'minion.pem')) - os.rename(cfg_files['pubkey'], os.path.join( - mpt, pki_dir.lstrip('/'), 'minion.pub')) - os.rename(cfg_files['config'], os.path.join(mpt, 'etc/salt/minion')) + log.info("salt-minion pre-installed on image, " "configuring as %s", id_) + minion_config = salt.config.minion_config(cfg_files["config"]) + pki_dir = minion_config["pki_dir"] + if not os.path.isdir(os.path.join(mpt, pki_dir.lstrip("/"))): + __salt__["file.makedirs"](os.path.join(mpt, pki_dir.lstrip("/"), "")) + os.rename( + cfg_files["privkey"], os.path.join(mpt, pki_dir.lstrip("/"), "minion.pem") + ) + os.rename( + cfg_files["pubkey"], os.path.join(mpt, pki_dir.lstrip("/"), "minion.pub") + ) + os.rename(cfg_files["config"], os.path.join(mpt, "etc/salt/minion")) res = True elif install: - log.info('Attempting to install salt-minion to %s', mpt) + log.info("Attempting to install salt-minion to %s", mpt) res = _install(mpt) elif prep_install: - log.error('The prep_install option is no longer supported. Please use ' - 'the bootstrap script installed with Salt, located at %s.', - salt.syspaths.BOOTSTRAP) + log.error( + "The prep_install option is no longer supported. Please use " + "the bootstrap script installed with Salt, located at %s.", + salt.syspaths.BOOTSTRAP, + ) res = False else: - log.warning('No useful action performed on %s', mpt) + log.warning("No useful action performed on %s", mpt) res = False - _umount(mpt, ftype) + _umount(mpt, path, ftype) return res -def mkconfig(config=None, - tmp=None, - id_=None, - approve_key=True, - pub_key=None, - priv_key=None): - ''' +def mkconfig( + config=None, tmp=None, id_=None, approve_key=True, pub_key=None, priv_key=None +): + """ Generate keys and config and put them in a tmp directory. pub_key @@ -208,64 +225,63 @@ def mkconfig(config=None, salt 'minion' seed.mkconfig [config=config_data] [tmp=tmp_dir] \\ [id_=minion_id] [approve_key=(true|false)] - ''' + """ if tmp is None: tmp = tempfile.mkdtemp() if config is None: config = {} - if 'master' not in config and __opts__['master'] != 'salt': - config['master'] = __opts__['master'] + if "master" not in config and __opts__["master"] != "salt": + config["master"] = __opts__["master"] if id_: - config['id'] = id_ + config["id"] = id_ # Write the new minion's config to a tmp file - tmp_config = os.path.join(tmp, 'minion') - with salt.utils.files.fopen(tmp_config, 'w+') as fp_: + tmp_config = os.path.join(tmp, "minion") + with salt.utils.files.fopen(tmp_config, "w+") as fp_: fp_.write(salt.utils.cloud.salt_config_to_yaml(config)) # Generate keys for the minion - pubkeyfn = os.path.join(tmp, 'minion.pub') - privkeyfn = os.path.join(tmp, 'minion.pem') + pubkeyfn = os.path.join(tmp, "minion.pub") + privkeyfn = os.path.join(tmp, "minion.pem") preseeded = pub_key and priv_key if preseeded: - log.debug('Writing minion.pub to %s', pubkeyfn) - log.debug('Writing minion.pem to %s', privkeyfn) - with salt.utils.files.fopen(pubkeyfn, 'w') as fic: + log.debug("Writing minion.pub to %s", pubkeyfn) + log.debug("Writing minion.pem to %s", privkeyfn) + with salt.utils.files.fopen(pubkeyfn, "w") as fic: fic.write(salt.utils.stringutils.to_str(_file_or_content(pub_key))) - with salt.utils.files.fopen(privkeyfn, 'w') as fic: + with salt.utils.files.fopen(privkeyfn, "w") as fic: fic.write(salt.utils.stringutils.to_str(_file_or_content(priv_key))) os.chmod(pubkeyfn, 0o600) os.chmod(privkeyfn, 0o600) else: - salt.crypt.gen_keys(tmp, 'minion', 2048) + salt.crypt.gen_keys(tmp, "minion", 2048) if approve_key and not preseeded: with salt.utils.files.fopen(pubkeyfn) as fp_: pubkey = salt.utils.stringutils.to_unicode(fp_.read()) - __salt__['pillar.ext']({'virtkey': [id_, pubkey]}) + __salt__["pillar.ext"]({"virtkey": [id_, pubkey]}) - return {'config': tmp_config, 'pubkey': pubkeyfn, 'privkey': privkeyfn} + return {"config": tmp_config, "pubkey": pubkeyfn, "privkey": privkeyfn} def _install(mpt): - ''' + """ Determine whether salt-minion is installed and, if not, install it. Return True if install is successful or already installed. - ''' + """ _check_resolv(mpt) - boot_, tmppath = (prep_bootstrap(mpt) - or salt.syspaths.BOOTSTRAP) + boot_, tmppath = prep_bootstrap(mpt) or salt.syspaths.BOOTSTRAP # Exec the chroot command - cmd = 'if type salt-minion; then exit 0; ' - cmd += 'else sh {0} -c /tmp; fi'.format(os.path.join(tmppath, 'bootstrap-salt.sh')) - return not __salt__['cmd.run_chroot'](mpt, cmd, python_shell=True)['retcode'] + cmd = "if type salt-minion; then exit 0; " + cmd += "else sh {0} -c /tmp; fi".format(os.path.join(tmppath, "bootstrap-salt.sh")) + return not __salt__["cmd.run_chroot"](mpt, cmd, python_shell=True)["retcode"] def _check_resolv(mpt): - ''' + """ Check that the resolv.conf is present and populated - ''' - resolv = os.path.join(mpt, 'etc/resolv.conf') + """ + resolv = os.path.join(mpt, "etc/resolv.conf") replace = False if os.path.islink(resolv): resolv = os.path.realpath(resolv) @@ -274,27 +290,22 @@ def _check_resolv(mpt): if not os.path.isfile(resolv): replace = True if not replace: - with salt.utils.files.fopen(resolv, 'rb') as fp_: + with salt.utils.files.fopen(resolv, "rb") as fp_: conts = salt.utils.stringutils.to_unicode(fp_.read()) - if 'nameserver' not in conts: + if "nameserver" not in conts: replace = True - if 'nameserver 127.0.0.1' in conts: + if "nameserver 127.0.0.1" in conts: replace = True if replace: - shutil.copy('/etc/resolv.conf', resolv) + shutil.copy("/etc/resolv.conf", resolv) def _check_install(root): - sh_ = '/bin/sh' - if os.path.isfile(os.path.join(root, 'bin/bash')): - sh_ = '/bin/bash' + sh_ = "/bin/sh" + if os.path.isfile(os.path.join(root, "bin/bash")): + sh_ = "/bin/bash" - cmd = ('if ! type salt-minion; then exit 1; fi') - cmd = 'chroot \'{0}\' {1} -c \'{2}\''.format( - root, - sh_, - cmd) + cmd = "if ! type salt-minion; then exit 1; fi" + cmd = "chroot '{0}' {1} -c '{2}'".format(root, sh_, cmd) - return not __salt__['cmd.retcode'](cmd, - output_loglevel='quiet', - python_shell=True) + return not __salt__["cmd.retcode"](cmd, output_loglevel="quiet", python_shell=True) diff --git a/salt/modules/selinux.py b/salt/modules/selinux.py index ac469df36e4..f450fcc75db 100644 --- a/salt/modules/selinux.py +++ b/salt/modules/selinux.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Execute calls on selinux .. note:: @@ -9,58 +9,60 @@ Execute calls on selinux packages are installed. If not on a Fedora or RHEL-based distribution, consult the selinux documentation for your distribution to ensure that the proper packages are installed. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os import re +import salt.utils.decorators as decorators + # Import salt libs import salt.utils.files import salt.utils.path import salt.utils.stringutils -import salt.utils.decorators as decorators import salt.utils.versions from salt.exceptions import CommandExecutionError, SaltInvocationError # Import 3rd-party libs from salt.ext import six - _SELINUX_FILETYPES = { - 'a': 'all files', - 'f': 'regular file', - 'd': 'directory', - 'c': 'character device', - 'b': 'block device', - 's': 'socket', - 'l': 'symbolic link', - 'p': 'named pipe'} + "a": "all files", + "f": "regular file", + "d": "directory", + "c": "character device", + "b": "block device", + "s": "socket", + "l": "symbolic link", + "p": "named pipe", +} def __virtual__(): - ''' + """ Check if the os is Linux, and then if selinux is running in permissive or enforcing mode. - ''' - required_cmds = ('semanage', 'setsebool', 'semodule') + """ + required_cmds = ("semanage", "setsebool", "semodule") # Iterate over all of the commands this module uses and make sure # each of them are available in the standard PATH to prevent breakage for cmd in required_cmds: if not salt.utils.path.which(cmd): - return (False, cmd + ' is not in the path') + return (False, cmd + " is not in the path") # SELinux only makes sense on Linux *obviously* - if __grains__['kernel'] == 'Linux': - return 'selinux' - return (False, 'Module only works on Linux with selinux installed') + if __grains__["kernel"] == "Linux": + return "selinux" + return (False, "Module only works on Linux with selinux installed") # Cache the SELinux directory to not look it up over and over @decorators.memoize def selinux_fs_path(): - ''' + """ Return the location of the SELinux VFS directory CLI Example: @@ -68,13 +70,13 @@ def selinux_fs_path(): .. code-block:: bash salt '*' selinux.selinux_fs_path - ''' + """ # systems running systemd (e.g. Fedora 15 and newer) # have the selinux filesystem in a different location try: - for directory in ('/sys/fs/selinux', '/selinux'): + for directory in ("/sys/fs/selinux", "/selinux"): if os.path.isdir(directory): - if os.path.isfile(os.path.join(directory, 'enforce')): + if os.path.isfile(os.path.join(directory, "enforce")): return directory return None # If selinux is Disabled, the path does not exist. @@ -83,7 +85,7 @@ def selinux_fs_path(): def getenforce(): - ''' + """ Return the mode selinux is running in CLI Example: @@ -91,23 +93,23 @@ def getenforce(): .. code-block:: bash salt '*' selinux.getenforce - ''' + """ _selinux_fs_path = selinux_fs_path() if _selinux_fs_path is None: - return 'Disabled' + return "Disabled" try: - enforce = os.path.join(_selinux_fs_path, 'enforce') - with salt.utils.files.fopen(enforce, 'r') as _fp: - if salt.utils.stringutils.to_unicode(_fp.readline()).strip() == '0': - return 'Permissive' + enforce = os.path.join(_selinux_fs_path, "enforce") + with salt.utils.files.fopen(enforce, "r") as _fp: + if salt.utils.stringutils.to_unicode(_fp.readline()).strip() == "0": + return "Permissive" else: - return 'Enforcing' + return "Enforcing" except (IOError, OSError, AttributeError): - return 'Disabled' + return "Disabled" def getconfig(): - ''' + """ Return the selinux mode from the config file CLI Example: @@ -115,21 +117,21 @@ def getconfig(): .. code-block:: bash salt '*' selinux.getconfig - ''' + """ try: - config = '/etc/selinux/config' - with salt.utils.files.fopen(config, 'r') as _fp: + config = "/etc/selinux/config" + with salt.utils.files.fopen(config, "r") as _fp: for line in _fp: line = salt.utils.stringutils.to_unicode(line) - if line.strip().startswith('SELINUX='): - return line.split('=')[1].capitalize().strip() + if line.strip().startswith("SELINUX="): + return line.split("=")[1].capitalize().strip() except (IOError, OSError, AttributeError): return None return None def setenforce(mode): - ''' + """ Set the SELinux enforcing mode CLI Example: @@ -137,57 +139,57 @@ def setenforce(mode): .. code-block:: bash salt '*' selinux.setenforce enforcing - ''' + """ if isinstance(mode, six.string_types): - if mode.lower() == 'enforcing': - mode = '1' - modestring = 'Enforcing' - elif mode.lower() == 'permissive': - mode = '0' - modestring = 'Permissive' - elif mode.lower() == 'disabled': - mode = '0' - modestring = 'Disabled' + if mode.lower() == "enforcing": + mode = "1" + modestring = "Enforcing" + elif mode.lower() == "permissive": + mode = "0" + modestring = "Permissive" + elif mode.lower() == "disabled": + mode = "0" + modestring = "Disabled" else: - return 'Invalid mode {0}'.format(mode) + return "Invalid mode {0}".format(mode) elif isinstance(mode, int): if mode: - mode = '1' + mode = "1" else: - mode = '0' + mode = "0" else: - return 'Invalid mode {0}'.format(mode) + return "Invalid mode {0}".format(mode) # enforce file does not exist if currently disabled. Only for toggling enforcing/permissive - if getenforce() != 'Disabled': - enforce = os.path.join(selinux_fs_path(), 'enforce') + if getenforce() != "Disabled": + enforce = os.path.join(selinux_fs_path(), "enforce") try: - with salt.utils.files.fopen(enforce, 'w') as _fp: + with salt.utils.files.fopen(enforce, "w") as _fp: _fp.write(salt.utils.stringutils.to_str(mode)) except (IOError, OSError) as exc: - msg = 'Could not write SELinux enforce file: {0}' + msg = "Could not write SELinux enforce file: {0}" raise CommandExecutionError(msg.format(exc)) - config = '/etc/selinux/config' + config = "/etc/selinux/config" try: - with salt.utils.files.fopen(config, 'r') as _cf: + with salt.utils.files.fopen(config, "r") as _cf: conf = _cf.read() try: - with salt.utils.files.fopen(config, 'w') as _cf: + with salt.utils.files.fopen(config, "w") as _cf: conf = re.sub(r"\nSELINUX=.*\n", "\nSELINUX=" + modestring + "\n", conf) _cf.write(salt.utils.stringutils.to_str(conf)) except (IOError, OSError) as exc: - msg = 'Could not write SELinux config file: {0}' + msg = "Could not write SELinux config file: {0}" raise CommandExecutionError(msg.format(exc)) except (IOError, OSError) as exc: - msg = 'Could not read SELinux config file: {0}' + msg = "Could not read SELinux config file: {0}" raise CommandExecutionError(msg.format(exc)) return getenforce() def getsebool(boolean): - ''' + """ Return the information on a specific selinux boolean CLI Example: @@ -195,12 +197,12 @@ def getsebool(boolean): .. code-block:: bash salt '*' selinux.getsebool virt_use_usb - ''' + """ return list_sebool().get(boolean, {}) def setsebool(boolean, value, persist=False): - ''' + """ Set the value for a boolean CLI Example: @@ -208,16 +210,16 @@ def setsebool(boolean, value, persist=False): .. code-block:: bash salt '*' selinux.setsebool virt_use_usb off - ''' + """ if persist: - cmd = 'setsebool -P {0} {1}'.format(boolean, value) + cmd = "setsebool -P {0} {1}".format(boolean, value) else: - cmd = 'setsebool {0} {1}'.format(boolean, value) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = "setsebool {0} {1}".format(boolean, value) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def setsebools(pairs, persist=False): - ''' + """ Set the value of multiple booleans CLI Example: @@ -225,20 +227,20 @@ def setsebools(pairs, persist=False): .. code-block:: bash salt '*' selinux.setsebools '{virt_use_usb: on, squid_use_tproxy: off}' - ''' + """ if not isinstance(pairs, dict): return {} if persist: - cmd = 'setsebool -P ' + cmd = "setsebool -P " else: - cmd = 'setsebool ' + cmd = "setsebool " for boolean, value in six.iteritems(pairs): - cmd = '{0} {1}={2}'.format(cmd, boolean, value) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = "{0} {1}={2}".format(cmd, boolean, value) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def list_sebool(): - ''' + """ Return a structure listing all of the selinux booleans on the system and what state they are in @@ -247,21 +249,23 @@ def list_sebool(): .. code-block:: bash salt '*' selinux.list_sebool - ''' - bdata = __salt__['cmd.run']('semanage boolean -l').splitlines() + """ + bdata = __salt__["cmd.run"]("semanage boolean -l").splitlines() ret = {} for line in bdata[1:]: if not line.strip(): continue comps = line.split() - ret[comps[0]] = {'State': comps[1][1:], - 'Default': comps[3][:-1], - 'Description': ' '.join(comps[4:])} + ret[comps[0]] = { + "State": comps[1][1:], + "Default": comps[3][:-1], + "Description": " ".join(comps[4:]), + } return ret def getsemod(module): - ''' + """ Return the information on a specific selinux module CLI Example: @@ -271,12 +275,12 @@ def getsemod(module): salt '*' selinux.getsemod mysql .. versionadded:: 2016.3.0 - ''' + """ return list_semod().get(module, {}) def setsemod(module, state): - ''' + """ Enable or disable an SELinux module. CLI Example: @@ -286,16 +290,16 @@ def setsemod(module, state): salt '*' selinux.setsemod nagios Enabled .. versionadded:: 2016.3.0 - ''' - if state.lower() == 'enabled': - cmd = 'semodule -e {0}'.format(module) - elif state.lower() == 'disabled': - cmd = 'semodule -d {0}'.format(module) - return not __salt__['cmd.retcode'](cmd) + """ + if state.lower() == "enabled": + cmd = "semodule -e {0}".format(module) + elif state.lower() == "disabled": + cmd = "semodule -d {0}".format(module) + return not __salt__["cmd.retcode"](cmd) def install_semod(module_path): - ''' + """ Install custom SELinux module from file CLI Example: @@ -305,15 +309,15 @@ def install_semod(module_path): salt '*' selinux.install_semod [salt://]path/to/module.pp .. versionadded:: 2016.11.6 - ''' - if module_path.find('salt://') == 0: - module_path = __salt__['cp.cache_file'](module_path) - cmd = 'semodule -i {0}'.format(module_path) - return not __salt__['cmd.retcode'](cmd) + """ + if module_path.find("salt://") == 0: + module_path = __salt__["cp.cache_file"](module_path) + cmd = "semodule -i {0}".format(module_path) + return not __salt__["cmd.retcode"](cmd) def remove_semod(module): - ''' + """ Remove SELinux module CLI Example: @@ -323,13 +327,13 @@ def remove_semod(module): salt '*' selinux.remove_semod module_name .. versionadded:: 2016.11.6 - ''' - cmd = 'semodule -r {0}'.format(module) - return not __salt__['cmd.retcode'](cmd) + """ + cmd = "semodule -r {0}".format(module) + return not __salt__["cmd.retcode"](cmd) def list_semod(): - ''' + """ Return a structure listing all of the selinux modules on the system and what state they are in @@ -340,56 +344,52 @@ def list_semod(): salt '*' selinux.list_semod .. versionadded:: 2016.3.0 - ''' - helptext = __salt__['cmd.run']('semodule -h').splitlines() - semodule_version = '' + """ + helptext = __salt__["cmd.run"]("semodule -h").splitlines() + semodule_version = "" for line in helptext: - if line.strip().startswith('full'): - semodule_version = 'new' + if line.strip().startswith("full"): + semodule_version = "new" - if semodule_version == 'new': - mdata = __salt__['cmd.run']('semodule -lfull').splitlines() + if semodule_version == "new": + mdata = __salt__["cmd.run"]("semodule -lfull").splitlines() ret = {} for line in mdata: if not line.strip(): continue comps = line.split() if len(comps) == 4: - ret[comps[1]] = {'Enabled': False, - 'Version': None} + ret[comps[1]] = {"Enabled": False, "Version": None} else: - ret[comps[1]] = {'Enabled': True, - 'Version': None} + ret[comps[1]] = {"Enabled": True, "Version": None} else: - mdata = __salt__['cmd.run']('semodule -l').splitlines() + mdata = __salt__["cmd.run"]("semodule -l").splitlines() ret = {} for line in mdata: if not line.strip(): continue comps = line.split() if len(comps) == 3: - ret[comps[0]] = {'Enabled': False, - 'Version': comps[1]} + ret[comps[0]] = {"Enabled": False, "Version": comps[1]} else: - ret[comps[0]] = {'Enabled': True, - 'Version': comps[1]} + ret[comps[0]] = {"Enabled": True, "Version": comps[1]} return ret def _validate_filetype(filetype): - ''' + """ .. versionadded:: 2017.7.0 Checks if the given filetype is a valid SELinux filetype specification. Throws an SaltInvocationError if it isn't. - ''' + """ if filetype not in _SELINUX_FILETYPES.keys(): - raise SaltInvocationError('Invalid filetype given: {0}'.format(filetype)) + raise SaltInvocationError("Invalid filetype given: {0}".format(filetype)) return True def _parse_protocol_port(name, protocol, port): - ''' + """ .. versionadded:: 2019.2.0 Validates and parses the protocol and port/port range from the name @@ -398,57 +398,63 @@ def _parse_protocol_port(name, protocol, port): If the name is in a valid format, the protocol and port are ignored if provided Examples: tcp/8080 or udp/20-21 - ''' - protocol_port_pattern = r'^(tcp|udp)\/(([\d]+)\-?[\d]+)$' + """ + protocol_port_pattern = r"^(tcp|udp)\/(([\d]+)\-?[\d]+)$" name_parts = re.match(protocol_port_pattern, name) if not name_parts: - name_parts = re.match(protocol_port_pattern, '{0}/{1}'.format(protocol, port)) + name_parts = re.match(protocol_port_pattern, "{0}/{1}".format(protocol, port)) if not name_parts: raise SaltInvocationError( 'Invalid name "{0}" format and protocol and port not provided or invalid: "{1}" "{2}".'.format( - name, protocol, port)) + name, protocol, port + ) + ) return name_parts.group(1), name_parts.group(2) def _context_dict_to_string(context): - ''' + """ .. versionadded:: 2017.7.0 Converts an SELinux file context from a dict to a string. - ''' - return '{sel_user}:{sel_role}:{sel_type}:{sel_level}'.format(**context) + """ + return "{sel_user}:{sel_role}:{sel_type}:{sel_level}".format(**context) def _context_string_to_dict(context): - ''' + """ .. versionadded:: 2017.7.0 Converts an SELinux file context from string to dict. - ''' - if not re.match('[^:]+:[^:]+:[^:]+:[^:]+$', context): - raise SaltInvocationError('Invalid SELinux context string: {0}. ' + - 'Expected "sel_user:sel_role:sel_type:sel_level"') - context_list = context.split(':', 3) + """ + if not re.match("[^:]+:[^:]+:[^:]+:[^:]+$", context): + raise SaltInvocationError( + "Invalid SELinux context string: {0}. " + + 'Expected "sel_user:sel_role:sel_type:sel_level"' + ) + context_list = context.split(":", 3) ret = {} - for index, value in enumerate(['sel_user', 'sel_role', 'sel_type', 'sel_level']): + for index, value in enumerate(["sel_user", "sel_role", "sel_type", "sel_level"]): ret[value] = context_list[index] return ret -def filetype_id_to_string(filetype='a'): - ''' +def filetype_id_to_string(filetype="a"): + """ .. versionadded:: 2017.7.0 Translates SELinux filetype single-letter representation to a more human-readable version (which is also used in `semanage fcontext -l`). - ''' + """ _validate_filetype(filetype) - return _SELINUX_FILETYPES.get(filetype, 'error') + return _SELINUX_FILETYPES.get(filetype, "error") -def fcontext_get_policy(name, filetype=None, sel_type=None, sel_user=None, sel_level=None): - ''' +def fcontext_get_policy( + name, filetype=None, sel_type=None, sel_user=None, sel_level=None +): + """ .. versionadded:: 2017.7.0 Returns the current entry in the SELinux policy list as a @@ -476,35 +482,48 @@ def fcontext_get_policy(name, filetype=None, sel_type=None, sel_user=None, sel_l .. code-block:: bash salt '*' selinux.fcontext_get_policy my-policy - ''' + """ if filetype: _validate_filetype(filetype) - re_spacer = '[ ]+' - cmd_kwargs = {'spacer': re_spacer, - 'filespec': re.escape(name), - 'sel_user': sel_user or '[^:]+', - 'sel_role': '[^:]+', # se_role for file context is always object_r - 'sel_type': sel_type or '[^:]+', - 'sel_level': sel_level or '[^:]+'} - cmd_kwargs['filetype'] = '[[:alpha:] ]+' if filetype is None else filetype_id_to_string(filetype) - cmd = 'semanage fcontext -l | egrep ' + \ - "'^{filespec}{spacer}{filetype}{spacer}{sel_user}:{sel_role}:{sel_type}:{sel_level}$'".format(**cmd_kwargs) - current_entry_text = __salt__['cmd.shell'](cmd, ignore_retcode=True) - if current_entry_text == '': + re_spacer = "[ ]+" + cmd_kwargs = { + "spacer": re_spacer, + "filespec": re.escape(name), + "sel_user": sel_user or "[^:]+", + "sel_role": "[^:]+", # se_role for file context is always object_r + "sel_type": sel_type or "[^:]+", + "sel_level": sel_level or "[^:]+", + } + cmd_kwargs["filetype"] = ( + "[[:alpha:] ]+" if filetype is None else filetype_id_to_string(filetype) + ) + cmd = ( + "semanage fcontext -l | egrep " + + "'^{filespec}{spacer}{filetype}{spacer}{sel_user}:{sel_role}:{sel_type}:{sel_level}$'".format( + **cmd_kwargs + ) + ) + current_entry_text = __salt__["cmd.shell"](cmd, ignore_retcode=True) + if current_entry_text == "": return None - parts = re.match(r'^({filespec}) +([a-z ]+) (.*)$'.format(**{'filespec': re.escape(name)}), current_entry_text) + parts = re.match( + r"^({filespec}) +([a-z ]+) (.*)$".format(**{"filespec": re.escape(name)}), + current_entry_text, + ) ret = { - 'filespec': parts.group(1).strip(), - 'filetype': parts.group(2).strip(), + "filespec": parts.group(1).strip(), + "filetype": parts.group(2).strip(), } ret.update(_context_string_to_dict(parts.group(3).strip())) return ret -def fcontext_add_policy(name, filetype=None, sel_type=None, sel_user=None, sel_level=None): - ''' +def fcontext_add_policy( + name, filetype=None, sel_type=None, sel_user=None, sel_level=None +): + """ .. versionadded:: 2019.2.0 Adds the SELinux policy for a given filespec and other optional parameters. @@ -538,12 +557,16 @@ def fcontext_add_policy(name, filetype=None, sel_type=None, sel_user=None, sel_l .. code-block:: bash salt '*' selinux.fcontext_add_policy my-policy - ''' - return _fcontext_add_or_delete_policy('add', name, filetype, sel_type, sel_user, sel_level) + """ + return _fcontext_add_or_delete_policy( + "add", name, filetype, sel_type, sel_user, sel_level + ) -def fcontext_delete_policy(name, filetype=None, sel_type=None, sel_user=None, sel_level=None): - ''' +def fcontext_delete_policy( + name, filetype=None, sel_type=None, sel_user=None, sel_level=None +): + """ .. versionadded:: 2019.2.0 Deletes the SELinux policy for a given filespec and other optional parameters. @@ -577,12 +600,16 @@ def fcontext_delete_policy(name, filetype=None, sel_type=None, sel_user=None, se .. code-block:: bash salt '*' selinux.fcontext_delete_policy my-policy - ''' - return _fcontext_add_or_delete_policy('delete', name, filetype, sel_type, sel_user, sel_level) + """ + return _fcontext_add_or_delete_policy( + "delete", name, filetype, sel_type, sel_user, sel_level + ) -def fcontext_add_or_delete_policy(action, name, filetype=None, sel_type=None, sel_user=None, sel_level=None): - ''' +def fcontext_add_or_delete_policy( + action, name, filetype=None, sel_type=None, sel_user=None, sel_level=None +): + """ .. versionadded:: 2017.7.0 Adds or deletes the SELinux policy for a given filespec and other optional parameters. @@ -626,45 +653,51 @@ def fcontext_add_or_delete_policy(action, name, filetype=None, sel_type=None, se .. code-block:: bash salt '*' selinux.fcontext_add_or_delete_policy add my-policy - ''' + """ salt.utils.versions.warn_until( - 'Sodium', - 'The \'selinux.fcontext_add_or_delete_policy\' module has been deprecated. Please use the ' - '\'selinux.fcontext_add_policy\' and \'selinux.fcontext_delete_policy\' modules instead. ' - 'Support for the \'selinux.fcontext_add_or_delete_policy\' module will be removed in Salt ' - '{version}.' + "Sodium", + "The 'selinux.fcontext_add_or_delete_policy' module has been deprecated. Please use the " + "'selinux.fcontext_add_policy' and 'selinux.fcontext_delete_policy' modules instead. " + "Support for the 'selinux.fcontext_add_or_delete_policy' module will be removed in Salt " + "{version}.", + ) + return _fcontext_add_or_delete_policy( + action, name, filetype, sel_type, sel_user, sel_level ) - return _fcontext_add_or_delete_policy(action, name, filetype, sel_type, sel_user, sel_level) -def _fcontext_add_or_delete_policy(action, name, filetype=None, sel_type=None, sel_user=None, sel_level=None): - ''' +def _fcontext_add_or_delete_policy( + action, name, filetype=None, sel_type=None, sel_user=None, sel_level=None +): + """ .. versionadded:: 2019.2.0 Performs the action as called from ``fcontext_add_policy`` or ``fcontext_delete_policy``. Returns the result of the call to semanage. - ''' - if action not in ['add', 'delete']: - raise SaltInvocationError('Actions supported are "add" and "delete", not "{0}".'.format(action)) - cmd = 'semanage fcontext --{0}'.format(action) + """ + if action not in ["add", "delete"]: + raise SaltInvocationError( + 'Actions supported are "add" and "delete", not "{0}".'.format(action) + ) + cmd = "semanage fcontext --{0}".format(action) # "semanage --ftype a" isn't valid on Centos 6, # don't pass --ftype since "a" is the default filetype. - if filetype is not None and filetype != 'a': + if filetype is not None and filetype != "a": _validate_filetype(filetype) - cmd += ' --ftype {0}'.format(filetype) + cmd += " --ftype {0}".format(filetype) if sel_type is not None: - cmd += ' --type {0}'.format(sel_type) + cmd += " --type {0}".format(sel_type) if sel_user is not None: - cmd += ' --seuser {0}'.format(sel_user) + cmd += " --seuser {0}".format(sel_user) if sel_level is not None: - cmd += ' --range {0}'.format(sel_level) - cmd += ' ' + re.escape(name) - return __salt__['cmd.run_all'](cmd) + cmd += " --range {0}".format(sel_level) + cmd += " " + re.escape(name) + return __salt__["cmd.run_all"](cmd) def fcontext_policy_is_applied(name, recursive=False): - ''' + """ .. versionadded:: 2017.7.0 Returns an empty string if the SELinux policy for a given filespec @@ -679,16 +712,16 @@ def fcontext_policy_is_applied(name, recursive=False): .. code-block:: bash salt '*' selinux.fcontext_policy_is_applied my-policy - ''' - cmd = 'restorecon -n -v ' + """ + cmd = "restorecon -n -v " if recursive: - cmd += '-R ' + cmd += "-R " cmd += re.escape(name) - return __salt__['cmd.run_all'](cmd).get('stdout') + return __salt__["cmd.run_all"](cmd).get("stdout") def fcontext_apply_policy(name, recursive=False): - ''' + """ .. versionadded:: 2017.7.0 Applies SElinux policies to filespec using `restorecon [-R] @@ -706,19 +739,21 @@ def fcontext_apply_policy(name, recursive=False): .. code-block:: bash salt '*' selinux.fcontext_apply_policy my-policy - ''' + """ ret = {} changes_text = fcontext_policy_is_applied(name, recursive) - cmd = 'restorecon -v -F ' + cmd = "restorecon -v -F " if recursive: - cmd += '-R ' + cmd += "-R " cmd += re.escape(name) - apply_ret = __salt__['cmd.run_all'](cmd) + apply_ret = __salt__["cmd.run_all"](cmd) ret.update(apply_ret) - if apply_ret['retcode'] == 0: - changes_list = re.findall('restorecon reset (.*) context (.*)->(.*)$', changes_text, re.M) + if apply_ret["retcode"] == 0: + changes_list = re.findall( + "restorecon reset (.*) context (.*)->(.*)$", changes_text, re.M + ) if len(changes_list) > 0: - ret.update({'changes': {}}) + ret.update({"changes": {}}) for item in changes_list: filespec = item[0] old = _context_string_to_dict(item[1]) @@ -730,12 +765,12 @@ def fcontext_apply_policy(name, recursive=False): for key in intersect: del old[key] del new[key] - ret['changes'].update({filespec: {'old': old, 'new': new}}) + ret["changes"].update({filespec: {"old": old, "new": new}}) return ret def port_get_policy(name, sel_type=None, protocol=None, port=None): - ''' + """ .. versionadded:: 2019.2.0 Returns the current entry in the SELinux policy list as a @@ -765,29 +800,36 @@ def port_get_policy(name, sel_type=None, protocol=None, port=None): salt '*' selinux.port_get_policy tcp/80 salt '*' selinux.port_get_policy foobar protocol=tcp port=80 - ''' + """ (protocol, port) = _parse_protocol_port(name, protocol, port) - re_spacer = '[ ]+' - re_sel_type = sel_type if sel_type else r'\w+' - cmd_kwargs = {'spacer': re_spacer, - 'sel_type': re_sel_type, - 'protocol': protocol, - 'port': port, } - cmd = 'semanage port -l | egrep ' + \ - "'^{sel_type}{spacer}{protocol}{spacer}((.*)*)[ ]{port}($|,)'".format(**cmd_kwargs) - port_policy = __salt__['cmd.shell'](cmd, ignore_retcode=True) - if port_policy == '': + re_spacer = "[ ]+" + re_sel_type = sel_type if sel_type else r"\w+" + cmd_kwargs = { + "spacer": re_spacer, + "sel_type": re_sel_type, + "protocol": protocol, + "port": port, + } + cmd = ( + "semanage port -l | egrep " + + "'^{sel_type}{spacer}{protocol}{spacer}((.*)*)[ ]{port}($|,)'".format( + **cmd_kwargs + ) + ) + port_policy = __salt__["cmd.shell"](cmd, ignore_retcode=True) + if port_policy == "": return None - parts = re.match(r'^(\w+)[ ]+(\w+)[ ]+([\d\-, ]+)', port_policy) + parts = re.match(r"^(\w+)[ ]+(\w+)[ ]+([\d\-, ]+)", port_policy) return { - 'sel_type': parts.group(1).strip(), - 'protocol': parts.group(2).strip(), - 'port': parts.group(3).strip(), } + "sel_type": parts.group(1).strip(), + "protocol": parts.group(2).strip(), + "port": parts.group(3).strip(), + } def port_add_policy(name, sel_type=None, protocol=None, port=None, sel_range=None): - ''' + """ .. versionadded:: 2019.2.0 Adds the SELinux policy for a given protocol and port. @@ -815,12 +857,12 @@ def port_add_policy(name, sel_type=None, protocol=None, port=None, sel_range=Non salt '*' selinux.port_add_policy add tcp/8080 http_port_t salt '*' selinux.port_add_policy add foobar http_port_t protocol=tcp port=8091 - ''' - return _port_add_or_delete_policy('add', name, sel_type, protocol, port, sel_range) + """ + return _port_add_or_delete_policy("add", name, sel_type, protocol, port, sel_range) def port_delete_policy(name, protocol=None, port=None): - ''' + """ .. versionadded:: 2019.2.0 Deletes the SELinux policy for a given protocol and port. @@ -842,27 +884,31 @@ def port_delete_policy(name, protocol=None, port=None): salt '*' selinux.port_delete_policy tcp/8080 salt '*' selinux.port_delete_policy foobar protocol=tcp port=8091 - ''' - return _port_add_or_delete_policy('delete', name, None, protocol, port, None) + """ + return _port_add_or_delete_policy("delete", name, None, protocol, port, None) -def _port_add_or_delete_policy(action, name, sel_type=None, protocol=None, port=None, sel_range=None): - ''' +def _port_add_or_delete_policy( + action, name, sel_type=None, protocol=None, port=None, sel_range=None +): + """ .. versionadded:: 2019.2.0 Performs the action as called from ``port_add_policy`` or ``port_delete_policy``. Returns the result of the call to semanage. - ''' - if action not in ['add', 'delete']: - raise SaltInvocationError('Actions supported are "add" and "delete", not "{0}".'.format(action)) - if action == 'add' and not sel_type: - raise SaltInvocationError('SELinux Type is required to add a policy') + """ + if action not in ["add", "delete"]: + raise SaltInvocationError( + 'Actions supported are "add" and "delete", not "{0}".'.format(action) + ) + if action == "add" and not sel_type: + raise SaltInvocationError("SELinux Type is required to add a policy") (protocol, port) = _parse_protocol_port(name, protocol, port) - cmd = 'semanage port --{0} --proto {1}'.format(action, protocol) + cmd = "semanage port --{0} --proto {1}".format(action, protocol) if sel_type: - cmd += ' --type {0}'.format(sel_type) + cmd += " --type {0}".format(sel_type) if sel_range: - cmd += ' --range {0}'.format(sel_range) - cmd += ' {0}'.format(port) - return __salt__['cmd.run_all'](cmd) + cmd += " --range {0}".format(sel_range) + cmd += " {0}".format(port) + return __salt__["cmd.run_all"](cmd) diff --git a/salt/modules/sensehat.py b/salt/modules/sensehat.py index e9e1a7d6a04..4ab605ed0ac 100644 --- a/salt/modules/sensehat.py +++ b/salt/modules/sensehat.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for controlling the LED matrix or reading environment data on the SenseHat of a Raspberry Pi. .. versionadded:: 2017.7.0 @@ -18,13 +18,15 @@ Example: sensehat: rotation: 90 -''' +""" + +from __future__ import absolute_import, print_function, unicode_literals -from __future__ import absolute_import, unicode_literals, print_function import logging try: from sense_hat import SenseHat + has_sense_hat = True except (ImportError, NameError): _sensehat = None @@ -34,39 +36,44 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if SenseHat is available - ''' + """ if has_sense_hat: try: _sensehat = SenseHat() except OSError: - return False, 'This module can only be used on a Raspberry Pi with a SenseHat.' + return ( + False, + "This module can only be used on a Raspberry Pi with a SenseHat.", + ) - rotation = __salt__['pillar.get']('sensehat:rotation', 0) + rotation = __salt__["pillar.get"]("sensehat:rotation", 0) if rotation in [0, 90, 180, 270]: _sensehat.set_rotation(rotation, False) else: - log.error('%s is not a valid rotation. Using default rotation.', - rotation) + log.error("%s is not a valid rotation. Using default rotation.", rotation) return True - return False, 'The SenseHat execution module cannot be loaded: \'sense_hat\' python library unavailable.' + return ( + False, + "The SenseHat execution module cannot be loaded: 'sense_hat' python library unavailable.", + ) def set_pixels(pixels): - ''' + """ Sets the entire LED matrix based on a list of 64 pixel values pixels A list of 64 ``[R, G, B]`` color values. - ''' + """ _sensehat.set_pixels(pixels) - return {'pixels': pixels} + return {"pixels": pixels} def get_pixels(): - ''' + """ Returns a list of 64 smaller lists of ``[R, G, B]`` pixels representing the the currently displayed image on the LED matrix. @@ -82,12 +89,12 @@ def get_pixels(): The ``get_pixels`` method provides an accurate representation of how the pixels end up in frame buffer memory after you have called ``set_pixels``. - ''' + """ return _sensehat.get_pixels() def set_pixel(x, y, color): - ''' + """ Sets a single pixel on the LED matrix to a specified color. x @@ -102,13 +109,13 @@ def set_pixel(x, y, color): .. code-block:: bash salt 'raspberry' sensehat.set_pixel 0 0 '[255, 0, 0]' - ''' + """ _sensehat.set_pixel(x, y, color) - return {'color': color} + return {"color": color} def get_pixel(x, y): - ''' + """ Returns the color of a single pixel on the LED matrix. x @@ -118,12 +125,12 @@ def get_pixel(x, y): .. note:: Please read the note for ``get_pixels`` - ''' + """ return _sensehat.get_pixel(x, y) def low_light(low_light=True): - ''' + """ Sets the LED matrix to low light mode. Useful in a dark environment. CLI Example: @@ -132,14 +139,15 @@ def low_light(low_light=True): salt 'raspberry' sensehat.low_light salt 'raspberry' sensehat.low_light False - ''' + """ _sensehat.low_light = low_light - return {'low_light': low_light} + return {"low_light": low_light} -def show_message(message, msg_type=None, - text_color=None, back_color=None, scroll_speed=0.1): - ''' +def show_message( + message, msg_type=None, text_color=None, back_color=None, scroll_speed=0.1 +): + """ Displays a message on the LED matrix. message @@ -171,26 +179,26 @@ def show_message(message, msg_type=None, salt 'raspberry' sensehat.show_message 'Something went wrong' error salt 'raspberry' sensehat.show_message 'Red' text_color='[255, 0, 0]' salt 'raspberry' sensehat.show_message 'Hello world' None '[0, 0, 255]' '[255, 255, 0]' 0.2 - ''' + """ text_color = text_color or [255, 255, 255] back_color = back_color or [0, 0, 0] color_by_type = { - 'error': [255, 0, 0], - 'warning': [255, 100, 0], - 'success': [0, 255, 0], - 'info': [0, 0, 255] + "error": [255, 0, 0], + "warning": [255, 100, 0], + "success": [0, 255, 0], + "info": [0, 0, 255], } if msg_type in color_by_type: text_color = color_by_type[msg_type] _sensehat.show_message(message, scroll_speed, text_color, back_color) - return {'message': message} + return {"message": message} def show_letter(letter, text_color=None, back_color=None): - ''' + """ Displays a single letter on the LED matrix. letter @@ -207,16 +215,16 @@ def show_letter(letter, text_color=None, back_color=None): salt 'raspberry' sensehat.show_letter O salt 'raspberry' sensehat.show_letter X '[255, 0, 0]' salt 'raspberry' sensehat.show_letter B '[0, 0, 255]' '[255, 255, 0]' - ''' + """ text_color = text_color or [255, 255, 255] back_color = back_color or [0, 0, 0] _sensehat.show_letter(letter, text_color, back_color) - return {'letter': letter} + return {"letter": letter} def show_image(image): - ''' + """ Displays an 8 x 8 image on the LED matrix. image @@ -227,12 +235,12 @@ def show_image(image): .. code-block:: bash salt 'raspberry' sensehat.show_image /tmp/my_image.png - ''' + """ return _sensehat.load_image(image) def clear(color=None): - ''' + """ Sets the LED matrix to a single color or turns all LEDs off. CLI Example: @@ -241,47 +249,47 @@ def clear(color=None): salt 'raspberry' sensehat.clear salt 'raspberry' sensehat.clear '[255, 0, 0]' - ''' + """ if color is None: _sensehat.clear() else: _sensehat.clear(color) - return {'color': color} + return {"color": color} def get_humidity(): - ''' + """ Get the percentage of relative humidity from the humidity sensor. - ''' + """ return _sensehat.get_humidity() def get_pressure(): - ''' + """ Gets the current pressure in Millibars from the pressure sensor. - ''' + """ return _sensehat.get_pressure() def get_temperature(): - ''' + """ Gets the temperature in degrees Celsius from the humidity sensor. Equivalent to calling ``get_temperature_from_humidity``. If you get strange results try using ``get_temperature_from_pressure``. - ''' + """ return _sensehat.get_temperature() def get_temperature_from_humidity(): - ''' + """ Gets the temperature in degrees Celsius from the humidity sensor. - ''' + """ return _sensehat.get_temperature_from_humidity() def get_temperature_from_pressure(): - ''' + """ Gets the temperature in degrees Celsius from the pressure sensor. - ''' + """ return _sensehat.get_temperature_from_pressure() diff --git a/salt/modules/sensors.py b/salt/modules/sensors.py index 66d45a6c42c..14e2e426644 100644 --- a/salt/modules/sensors.py +++ b/salt/modules/sensors.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Read lm-sensors .. versionadded:: 2014.1.3 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -12,18 +12,17 @@ import logging # import Salt libs import salt.utils.path - log = logging.getLogger(__name__) def __virtual__(): - if salt.utils.path.which('sensors'): + if salt.utils.path.which("sensors"): return True - return (False, 'sensors does not exist in the path') + return (False, "sensors does not exist in the path") def sense(chip, fahrenheit=False): - ''' + """ Gather lm-sensors data from a given chip To determine the chip to query, use the 'sensors' command @@ -42,14 +41,16 @@ def sense(chip, fahrenheit=False): Core 3: +53.0°C (high = +87.0°C, crit = +105.0°C) Given the above, the chip is 'coretemp-isa-0000'. - ''' - extra_args = '' + """ + extra_args = "" if fahrenheit is True: - extra_args = '-f' - sensors = __salt__['cmd.run']('/usr/bin/sensors {0} {1}'.format(chip, extra_args), python_shell=False).splitlines() + extra_args = "-f" + sensors = __salt__["cmd.run"]( + "/usr/bin/sensors {0} {1}".format(chip, extra_args), python_shell=False + ).splitlines() ret = {} for sensor in sensors: - sensor_list = sensor.split(':') + sensor_list = sensor.split(":") if len(sensor_list) >= 2: ret[sensor_list[0]] = sensor_list[1].lstrip() return ret diff --git a/salt/modules/serverdensity_device.py b/salt/modules/serverdensity_device.py index 08b3dd2809e..42e26791f77 100644 --- a/salt/modules/serverdensity_device.py +++ b/salt/modules/serverdensity_device.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Wrapper around Server Density API ================================= .. versionadded:: 2014.7.0 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import os import tempfile @@ -18,10 +19,11 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import map # pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext.six.moves import map try: import requests + ENABLED = True except ImportError: ENABLED = False @@ -30,18 +32,18 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ if not ENABLED: - return (False, 'The requests python module cannot be imported') + return (False, "The requests python module cannot be imported") return "serverdensity_device" -def get_sd_auth(val, sd_auth_pillar_name='serverdensity'): - ''' +def get_sd_auth(val, sd_auth_pillar_name="serverdensity"): + """ Returns requested Server Density authentication value from pillar. CLI Example: @@ -49,32 +51,32 @@ def get_sd_auth(val, sd_auth_pillar_name='serverdensity'): .. code-block:: bash salt '*' serverdensity_device.get_sd_auth - ''' + """ sd_pillar = __pillar__.get(sd_auth_pillar_name) - log.debug('Server Density Pillar: %s', sd_pillar) + log.debug("Server Density Pillar: %s", sd_pillar) if not sd_pillar: - log.error('Could not load %s pillar', sd_auth_pillar_name) + log.error("Could not load %s pillar", sd_auth_pillar_name) raise CommandExecutionError( - '{0} pillar is required for authentication'.format(sd_auth_pillar_name) + "{0} pillar is required for authentication".format(sd_auth_pillar_name) ) try: return sd_pillar[val] except KeyError: - log.error('Could not find value %s in pillar', val) - raise CommandExecutionError('{0} value was not found in pillar'.format(val)) + log.error("Could not find value %s in pillar", val) + raise CommandExecutionError("{0} value was not found in pillar".format(val)) def _clean_salt_variables(params, variable_prefix="__"): - ''' + """ Pops out variables from params which starts with `variable_prefix`. - ''' + """ list(list(map(params.pop, [k for k in params if k.startswith(variable_prefix)]))) return params def create(name, **params): - ''' + """ Function to create device in Server Density. For more info, see the `API docs`__. @@ -86,32 +88,32 @@ def create(name, **params): salt '*' serverdensity_device.create lama salt '*' serverdensity_device.create rich_lama group=lama_band installedRAM=32768 - ''' - log.debug('Server Density params: %s', params) + """ + log.debug("Server Density params: %s", params) params = _clean_salt_variables(params) - params['name'] = name + params["name"] = name api_response = requests.post( - 'https://api.serverdensity.io/inventory/devices/', - params={'token': get_sd_auth('api_token')}, - data=params + "https://api.serverdensity.io/inventory/devices/", + params={"token": get_sd_auth("api_token")}, + data=params, ) - log.debug('Server Density API Response: %s', api_response) - log.debug('Server Density API Response content: %s', api_response.content) + log.debug("Server Density API Response: %s", api_response) + log.debug("Server Density API Response content: %s", api_response.content) if api_response.status_code == 200: try: return salt.utils.json.loads(api_response.content) except ValueError: - log.error('Could not parse API Response content: %s', api_response.content) + log.error("Could not parse API Response content: %s", api_response.content) raise CommandExecutionError( - 'Failed to create, API Response: {0}'.format(api_response) + "Failed to create, API Response: {0}".format(api_response) ) else: return None def delete(device_id): - ''' + """ Delete a device from Server Density. For more information, see the `API docs`__. @@ -122,27 +124,27 @@ def delete(device_id): .. code-block:: bash salt '*' serverdensity_device.delete 51f7eafcdba4bb235e000ae4 - ''' + """ api_response = requests.delete( - 'https://api.serverdensity.io/inventory/devices/' + device_id, - params={'token': get_sd_auth('api_token')} + "https://api.serverdensity.io/inventory/devices/" + device_id, + params={"token": get_sd_auth("api_token")}, ) - log.debug('Server Density API Response: %s', api_response) - log.debug('Server Density API Response content: %s', api_response.content) + log.debug("Server Density API Response: %s", api_response) + log.debug("Server Density API Response content: %s", api_response.content) if api_response.status_code == 200: try: return salt.utils.json.loads(api_response.content) except ValueError: - log.error('Could not parse API Response content: %s', api_response.content) + log.error("Could not parse API Response content: %s", api_response.content) raise CommandExecutionError( - 'Failed to create, API Response: {0}'.format(api_response) + "Failed to create, API Response: {0}".format(api_response) ) else: return None def ls(**params): - ''' + """ List devices in Server Density Results will be filtered by any params passed to this function. For more @@ -158,43 +160,47 @@ def ls(**params): salt '*' serverdensity_device.ls salt '*' serverdensity_device.ls name=lama salt '*' serverdensity_device.ls name=lama group=lama_band installedRAM=32768 - ''' + """ params = _clean_salt_variables(params) - endpoint = 'devices' + endpoint = "devices" # Change endpoint if there are params to filter by: if params: - endpoint = 'resources' + endpoint = "resources" # Convert all ints to strings: for key, val in six.iteritems(params): params[key] = six.text_type(val) api_response = requests.get( - 'https://api.serverdensity.io/inventory/{0}'.format(endpoint), - params={'token': get_sd_auth('api_token'), 'filter': salt.utils.json.dumps(params)} + "https://api.serverdensity.io/inventory/{0}".format(endpoint), + params={ + "token": get_sd_auth("api_token"), + "filter": salt.utils.json.dumps(params), + }, ) - log.debug('Server Density API Response: %s', api_response) - log.debug('Server Density API Response content: %s', api_response.content) + log.debug("Server Density API Response: %s", api_response) + log.debug("Server Density API Response content: %s", api_response.content) if api_response.status_code == 200: try: return salt.utils.json.loads(api_response.content) except ValueError: log.error( - 'Could not parse Server Density API Response content: %s', - api_response.content + "Could not parse Server Density API Response content: %s", + api_response.content, ) raise CommandExecutionError( - 'Failed to create, Server Density API Response: {0}' - .format(api_response) + "Failed to create, Server Density API Response: {0}".format( + api_response + ) ) else: return None def update(device_id, **params): - ''' + """ Updates device information in Server Density. For more information see the `API docs`__. @@ -206,33 +212,33 @@ def update(device_id, **params): salt '*' serverdensity_device.update 51f7eafcdba4bb235e000ae4 name=lama group=lama_band salt '*' serverdensity_device.update 51f7eafcdba4bb235e000ae4 name=better_lama group=rock_lamas swapSpace=512 - ''' + """ params = _clean_salt_variables(params) api_response = requests.put( - 'https://api.serverdensity.io/inventory/devices/' + device_id, - params={'token': get_sd_auth('api_token')}, - data=params + "https://api.serverdensity.io/inventory/devices/" + device_id, + params={"token": get_sd_auth("api_token")}, + data=params, ) - log.debug('Server Density API Response: %s', api_response) - log.debug('Server Density API Response content: %s', api_response.content) + log.debug("Server Density API Response: %s", api_response) + log.debug("Server Density API Response content: %s", api_response.content) if api_response.status_code == 200: try: return salt.utils.json.loads(api_response.content) except ValueError: log.error( - 'Could not parse Server Density API Response content: %s', - api_response.content + "Could not parse Server Density API Response content: %s", + api_response.content, ) raise CommandExecutionError( - 'Failed to create, API Response: {0}'.format(api_response) + "Failed to create, API Response: {0}".format(api_response) ) else: return None def install_agent(agent_key, agent_version=1): - ''' + """ Function downloads Server Density installation agent, and installs sd-agent with agent_key. Optionally the agent_version would select the series to use (defaults on the v1 one). @@ -243,32 +249,30 @@ def install_agent(agent_key, agent_version=1): salt '*' serverdensity_device.install_agent c2bbdd6689ff46282bdaa07555641498 salt '*' serverdensity_device.install_agent c2bbdd6689ff46282bdaa07555641498 2 - ''' - work_dir = os.path.join(__opts__['cachedir'], 'tmp') + """ + work_dir = os.path.join(__opts__["cachedir"], "tmp") if not os.path.isdir(work_dir): os.mkdir(work_dir) - install_file = tempfile.NamedTemporaryFile(dir=work_dir, - suffix='.sh', - delete=False) + install_file = tempfile.NamedTemporaryFile(dir=work_dir, suffix=".sh", delete=False) install_filename = install_file.name install_file.close() - account_field = 'account_url' - url = 'https://www.serverdensity.com/downloads/agent-install.sh' + account_field = "account_url" + url = "https://www.serverdensity.com/downloads/agent-install.sh" if agent_version == 2: - account_field = 'account_name' - url = 'https://archive.serverdensity.com/agent-install.sh' + account_field = "account_name" + url = "https://archive.serverdensity.com/agent-install.sh" account = get_sd_auth(account_field) - __salt__['cmd.run']( - cmd='curl -L {0} -o {1}'.format(url, install_filename), - cwd=work_dir + __salt__["cmd.run"]( + cmd="curl -L {0} -o {1}".format(url, install_filename), cwd=work_dir ) - __salt__['cmd.run'](cmd='chmod +x {0}'.format(install_filename), cwd=work_dir) + __salt__["cmd.run"](cmd="chmod +x {0}".format(install_filename), cwd=work_dir) - return __salt__['cmd.run']( - cmd='{filename} -a {account} -k {agent_key}'.format( - filename=install_filename, account=account, agent_key=agent_key), - cwd=work_dir + return __salt__["cmd.run"]( + cmd="{filename} -a {account} -k {agent_key}".format( + filename=install_filename, account=account, agent_key=agent_key + ), + cwd=work_dir, ) diff --git a/salt/modules/servicenow.py b/salt/modules/servicenow.py index 2af4dcfa41a..457e22647f2 100644 --- a/salt/modules/servicenow.py +++ b/salt/modules/servicenow.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for execution of ServiceNow CI (configuration items) .. versionadded:: 2016.11.0 @@ -18,13 +18,15 @@ Module for execution of ServiceNow CI (configuration items) instance_name: '' username: '' password: '' -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import third party libs from salt.ext import six + HAS_LIBS = False try: from servicenow_rest.api import Client @@ -35,31 +37,34 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'servicenow' +__virtualname__ = "servicenow" -SERVICE_NAME = 'servicenow' +SERVICE_NAME = "servicenow" def __virtual__(): - ''' + """ Only load this module if servicenow is installed on this minion. - ''' + """ if HAS_LIBS: return __virtualname__ - return (False, 'The servicenow execution module failed to load: ' - 'requires servicenow_rest python library to be installed.') + return ( + False, + "The servicenow execution module failed to load: " + "requires servicenow_rest python library to be installed.", + ) def _get_client(): - config = __salt__['config.option'](SERVICE_NAME) - instance_name = config['instance_name'] - username = config['username'] - password = config['password'] + config = __salt__["config.option"](SERVICE_NAME) + instance_name = config["instance_name"] + username = config["username"] + password = config["password"] return Client(instance_name, username, password) -def set_change_request_state(change_id, state='approved'): - ''' +def set_change_request_state(change_id, state="approved"): + """ Set the approval state of a change request/record :param change_id: The ID of the change request, e.g. CHG123545 @@ -74,22 +79,22 @@ def set_change_request_state(change_id, state='approved'): salt myminion servicenow.set_change_request_state CHG000123 declined salt myminion servicenow.set_change_request_state CHG000123 approved - ''' + """ client = _get_client() - client.table = 'change_request' + client.table = "change_request" # Get the change record first - record = client.get({'number': change_id}) + record = client.get({"number": change_id}) if record is None or len(record) == 0: - log.error('Failed to fetch change record, maybe it does not exist?') + log.error("Failed to fetch change record, maybe it does not exist?") return False # Use the sys_id as the unique system record - sys_id = record[0]['sys_id'] - response = client.update({'approval': state}, sys_id) + sys_id = record[0]["sys_id"] + response = client.update({"approval": state}, sys_id) return response def delete_record(table, sys_id): - ''' + """ Delete an existing record :param table: The table name, e.g. sys_user @@ -103,7 +108,7 @@ def delete_record(table, sys_id): .. code-block:: bash salt myminion servicenow.delete_record sys_computer 2134566 - ''' + """ client = _get_client() client.table = table response = client.delete(sys_id) @@ -111,7 +116,7 @@ def delete_record(table, sys_id): def non_structured_query(table, query=None, **kwargs): - ''' + """ Run a non-structed (not a dict) query on a servicenow table. See http://wiki.servicenow.com/index.php?title=Encoded_Query_Strings#gsc.tab=0 for help on constructing a non-structured query string. @@ -128,7 +133,7 @@ def non_structured_query(table, query=None, **kwargs): salt myminion servicenow.non_structured_query sys_computer 'role=web' salt myminion servicenow.non_structured_query sys_computer role=web type=computer - ''' + """ client = _get_client() client.table = table # underlying lib doesn't use six or past.basestring, @@ -138,15 +143,15 @@ def non_structured_query(table, query=None, **kwargs): # try and assemble a query by keyword query_parts = [] for key, value in kwargs.items(): - query_parts.append('{0}={1}'.format(key, value)) - query = '^'.join(query_parts) + query_parts.append("{0}={1}".format(key, value)) + query = "^".join(query_parts) query = six.text_type(query) response = client.get(query) return response def update_record_field(table, sys_id, field, value): - ''' + """ Update the value of a record's field in a servicenow table :param table: The table name, e.g. sys_user @@ -166,7 +171,7 @@ def update_record_field(table, sys_id, field, value): .. code-block:: bash salt myminion servicenow.update_record_field sys_user 2348234 first_name jimmy - ''' + """ client = _get_client() client.table = table response = client.update({field: value}, sys_id) diff --git a/salt/modules/slack_notify.py b/salt/modules/slack_notify.py index 28c9c031fca..7bd02c14764 100644 --- a/salt/modules/slack_notify.py +++ b/salt/modules/slack_notify.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for sending messages to Slack .. versionadded:: 2015.5.0 @@ -14,60 +14,63 @@ Module for sending messages to Slack slack: api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.ext.six.moves.http_client + # Import Salt libs import salt.utils.json import salt.utils.slack from salt.exceptions import SaltInvocationError +from salt.ext.six.moves import range # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext.six.moves.urllib.parse import urlencode as _urlencode from salt.ext.six.moves.urllib.parse import urljoin as _urljoin -from salt.ext.six.moves import range -import salt.ext.six.moves.http_client + # pylint: enable=import-error,no-name-in-module log = logging.getLogger(__name__) -__virtualname__ = 'slack' +__virtualname__ = "slack" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ def _get_api_key(): - api_key = __salt__['config.get']('slack.api_key') or \ - __salt__['config.get']('slack:api_key') + api_key = __salt__["config.get"]("slack.api_key") or __salt__["config.get"]( + "slack:api_key" + ) if not api_key: - raise SaltInvocationError('No Slack API key found.') + raise SaltInvocationError("No Slack API key found.") return api_key def _get_hook_id(): - url = __salt__['config.get']('slack.hook') or \ - __salt__['config.get']('slack:hook') + url = __salt__["config.get"]("slack.hook") or __salt__["config.get"]("slack:hook") if not url: - raise SaltInvocationError('No Slack WebHook url found') + raise SaltInvocationError("No Slack WebHook url found") return url def list_rooms(api_key=None): - ''' + """ List all Slack rooms. :param api_key: The Slack admin api key. @@ -80,16 +83,14 @@ def list_rooms(api_key=None): salt '*' slack.list_rooms salt '*' slack.list_rooms api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - ''' + """ if not api_key: api_key = _get_api_key() - return salt.utils.slack.query(function='rooms', - api_key=api_key, - opts=__opts__) + return salt.utils.slack.query(function="rooms", api_key=api_key, opts=__opts__) def list_users(api_key=None): - ''' + """ List all Slack users. :param api_key: The Slack admin api key. @@ -102,16 +103,14 @@ def list_users(api_key=None): salt '*' slack.list_users salt '*' slack.list_users api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - ''' + """ if not api_key: api_key = _get_api_key() - return salt.utils.slack.query(function='users', - api_key=api_key, - opts=__opts__) + return salt.utils.slack.query(function="users", api_key=api_key, opts=__opts__) def find_room(name, api_key=None): - ''' + """ Find a room by name and return it. :param name: The room name. @@ -125,27 +124,27 @@ def find_room(name, api_key=None): salt '*' slack.find_room name="random" salt '*' slack.find_room name="random" api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - ''' + """ if not api_key: api_key = _get_api_key() # search results don't include the name of the # channel with a hash, if the passed channel name # has a hash we remove it. - if name.startswith('#'): + if name.startswith("#"): name = name[1:] ret = list_rooms(api_key) - if ret['res']: - rooms = ret['message'] + if ret["res"]: + rooms = ret["message"] if rooms: for room in range(0, len(rooms)): - if rooms[room]['name'] == name: + if rooms[room]["name"] == name: return rooms[room] return False def find_user(name, api_key=None): - ''' + """ Find a user by name and return it. :param name: The user name. @@ -159,26 +158,22 @@ def find_user(name, api_key=None): salt '*' slack.find_user name="ThomasHatch" salt '*' slack.find_user name="ThomasHatch" api_key=peWcBiMOS9HrZG15peWcBiMOS9HrZG15 - ''' + """ if not api_key: api_key = _get_api_key() ret = list_users(api_key) - if ret['res']: - users = ret['message'] + if ret["res"]: + users = ret["message"] if users: for user in range(0, len(users)): - if users[user]['name'] == name: + if users[user]["name"] == name: return users[user] return False -def post_message(channel, - message, - from_name, - api_key=None, - icon=None): - ''' +def post_message(channel, message, from_name, api_key=None, icon=None): + """ Send a message to a Slack channel. :param channel: The channel name, either will work. @@ -194,62 +189,65 @@ def post_message(channel, salt '*' slack.post_message channel="Development Room" message="Build is done" from_name="Build Server" - ''' + """ if not api_key: api_key = _get_api_key() if not channel: - log.error('channel is a required option.') + log.error("channel is a required option.") # channel must start with a hash or an @ (direct-message channels) - if not channel.startswith('#') and not channel.startswith('@'): - log.warning('Channel name must start with a hash or @. ' - 'Prepending a hash and using "#%s" as ' - 'channel name instead of %s', - channel, channel) - channel = '#{0}'.format(channel) + if not channel.startswith("#") and not channel.startswith("@"): + log.warning( + "Channel name must start with a hash or @. " + 'Prepending a hash and using "#%s" as ' + "channel name instead of %s", + channel, + channel, + ) + channel = "#{0}".format(channel) if not from_name: - log.error('from_name is a required option.') + log.error("from_name is a required option.") if not message: - log.error('message is a required option.') + log.error("message is a required option.") if not from_name: - log.error('from_name is a required option.') + log.error("from_name is a required option.") - parameters = { - 'channel': channel, - 'username': from_name, - 'text': message - } + parameters = {"channel": channel, "username": from_name, "text": message} if icon is not None: - parameters['icon_url'] = icon + parameters["icon_url"] = icon # Slack wants the body on POST to be urlencoded. - result = salt.utils.slack.query(function='message', - api_key=api_key, - method='POST', - header_dict={'Content-Type': 'application/x-www-form-urlencoded'}, - data=_urlencode(parameters), - opts=__opts__) + result = salt.utils.slack.query( + function="message", + api_key=api_key, + method="POST", + header_dict={"Content-Type": "application/x-www-form-urlencoded"}, + data=_urlencode(parameters), + opts=__opts__, + ) - if result['res']: + if result["res"]: return True else: return result -def call_hook(message, - attachment=None, - color='good', - short=False, - identifier=None, - channel=None, - username=None, - icon_emoji=None): - ''' +def call_hook( + message, + attachment=None, + color="good", + short=False, + identifier=None, + channel=None, + username=None, + icon_emoji=None, +): + """ Send message to Slack incoming webhook. :param message: The topic of message. @@ -269,57 +267,45 @@ def call_hook(message, salt '*' slack.call_hook message='Hello, from SaltStack' - ''' - base_url = 'https://hooks.slack.com/services/' + """ + base_url = "https://hooks.slack.com/services/" if not identifier: identifier = _get_hook_id() url = _urljoin(base_url, identifier) if not message: - log.error('message is required option') + log.error("message is required option") if attachment: payload = { - 'attachments': [ + "attachments": [ { - 'fallback': message, - 'color': color, - 'pretext': message, - 'fields': [ - { - "value": attachment, - "short": short, - } - ] + "fallback": message, + "color": color, + "pretext": message, + "fields": [{"value": attachment, "short": short}], } ] } else: payload = { - 'text': message, + "text": message, } if channel: - payload['channel'] = channel + payload["channel"] = channel if username: - payload['username'] = username + payload["username"] = username if icon_emoji: - payload['icon_emoji'] = icon_emoji + payload["icon_emoji"] = icon_emoji - data = _urlencode( - { - 'payload': salt.utils.json.dumps(payload) - } - ) - result = salt.utils.http.query(url, method='POST', data=data, status=True) + data = _urlencode({"payload": salt.utils.json.dumps(payload)}) + result = salt.utils.http.query(url, method="POST", data=data, status=True) - if result['status'] <= 201: + if result["status"] <= 201: return True else: - return { - 'res': False, - 'message': result.get('body', result['status']) - } + return {"res": False, "message": result.get("body", result["status"])} diff --git a/salt/modules/slsutil.py b/salt/modules/slsutil.py index 5ec4fb70e18..d4c2ef2ff4f 100644 --- a/salt/modules/slsutil.py +++ b/salt/modules/slsutil.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Utility functions for use with or in SLS files -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.exceptions @@ -15,7 +15,7 @@ import salt.utils.dictupdate def update(dest, upd, recursive_update=True, merge_lists=False): - ''' + """ Merge ``upd`` recursively into ``dest`` If ``merge_lists=True``, will aggregate list object types instead of @@ -27,13 +27,12 @@ def update(dest, upd, recursive_update=True, merge_lists=False): salt '*' slsutil.update '{foo: Foo}' '{bar: Bar}' - ''' - return salt.utils.dictupdate.update(dest, upd, recursive_update, - merge_lists) + """ + return salt.utils.dictupdate.update(dest, upd, recursive_update, merge_lists) -def merge(obj_a, obj_b, strategy='smart', renderer='yaml', merge_lists=False): - ''' +def merge(obj_a, obj_b, strategy="smart", renderer="yaml", merge_lists=False): + """ Merge a data structure into another by choosing a merge strategy Strategies: @@ -49,13 +48,12 @@ def merge(obj_a, obj_b, strategy='smart', renderer='yaml', merge_lists=False): .. code-block:: shell salt '*' slsutil.merge '{foo: Foo}' '{bar: Bar}' - ''' - return salt.utils.dictupdate.merge(obj_a, obj_b, strategy, renderer, - merge_lists) + """ + return salt.utils.dictupdate.merge(obj_a, obj_b, strategy, renderer, merge_lists) -def merge_all(lst, strategy='smart', renderer='yaml', merge_lists=False): - ''' +def merge_all(lst, strategy="smart", renderer="yaml", merge_lists=False): + """ .. versionadded:: 2019.2.0 Merge a list of objects into each other in order @@ -79,19 +77,17 @@ def merge_all(lst, strategy='smart', renderer='yaml', merge_lists=False): $ salt-call --output=txt slsutil.merge_all '[{foo: Foo}, {foo: Bar}]' local: {u'foo': u'Bar'} - ''' + """ ret = {} for obj in lst: - ret = salt.utils.dictupdate.merge( - ret, obj, strategy, renderer, merge_lists - ) + ret = salt.utils.dictupdate.merge(ret, obj, strategy, renderer, merge_lists) return ret -def renderer(path=None, string=None, default_renderer='jinja|yaml', **kwargs): - ''' +def renderer(path=None, string=None, default_renderer="jinja|yaml", **kwargs): + """ Parse a string or file through Salt's renderer system .. versionchanged:: 2018.3.0 @@ -160,28 +156,29 @@ def renderer(path=None, string=None, default_renderer='jinja|yaml', **kwargs): salt '*' slsutil.renderer /path/to/file.sls 'jinja|yaml' salt '*' slsutil.renderer string='Inline template! {{ saltenv }}' salt '*' slsutil.renderer string='Hello, {{ name }}.' name='world' - ''' + """ if not path and not string: - raise salt.exceptions.SaltInvocationError( - 'Must pass either path or string') + raise salt.exceptions.SaltInvocationError("Must pass either path or string") renderers = salt.loader.render(__opts__, __salt__) if path: - path_or_string = __salt__['cp.get_url'](path, saltenv=kwargs.get('saltenv', 'base')) + path_or_string = __salt__["cp.get_url"]( + path, saltenv=kwargs.get("saltenv", "base") + ) elif string: - path_or_string = ':string:' - kwargs['input_data'] = string + path_or_string = ":string:" + kwargs["input_data"] = string ret = salt.template.compile_template( path_or_string, renderers, default_renderer, - __opts__['renderer_blacklist'], - __opts__['renderer_whitelist'], + __opts__["renderer_blacklist"], + __opts__["renderer_whitelist"], **kwargs ) - return ret.read() if __utils__['stringio.is_readable'](ret) else ret + return ret.read() if __utils__["stringio.is_readable"](ret) else ret def _get_serialize_fn(serializer, fn_name): @@ -191,20 +188,21 @@ def _get_serialize_fn(serializer, fn_name): if not fns: raise salt.exceptions.CommandExecutionError( - "Serializer '{0}' not found.".format(serializer)) + "Serializer '{0}' not found.".format(serializer) + ) if not fn: raise salt.exceptions.CommandExecutionError( - "Serializer '{0}' does not implement {1}.".format(serializer, - fn_name)) + "Serializer '{0}' does not implement {1}.".format(serializer, fn_name) + ) return fn def serialize(serializer, obj, **mod_kwargs): - ''' - Serialize a Python object using a :py:mod:`serializer module - ` + """ + Serialize a Python object using one of the available + :ref:`all-salt.serializers`. CLI Example: @@ -218,15 +216,15 @@ def serialize(serializer, obj, **mod_kwargs): {% set json_string = salt.slsutil.serialize('json', {'foo': 'Foo!'}) %} - ''' + """ kwargs = salt.utils.args.clean_kwargs(**mod_kwargs) - return _get_serialize_fn(serializer, 'serialize')(obj, **kwargs) + return _get_serialize_fn(serializer, "serialize")(obj, **kwargs) def deserialize(serializer, stream_or_string, **mod_kwargs): - ''' - Deserialize a Python object using a :py:mod:`serializer module - ` + """ + Deserialize a Python object using one of the available + :ref:`all-salt.serializers`. CLI Example: @@ -242,7 +240,6 @@ def deserialize(serializer, stream_or_string, **mod_kwargs): {% set python_object = salt.slsutil.deserialize('json', '{"foo": "Foo!"}') %} - ''' + """ kwargs = salt.utils.args.clean_kwargs(**mod_kwargs) - return _get_serialize_fn(serializer, 'deserialize')(stream_or_string, - **kwargs) + return _get_serialize_fn(serializer, "deserialize")(stream_or_string, **kwargs) diff --git a/salt/modules/smartos_imgadm.py b/salt/modules/smartos_imgadm.py index a16f4c3de74..05c9b1ddfa0 100644 --- a/salt/modules/smartos_imgadm.py +++ b/salt/modules/smartos_imgadm.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Module for running imgadm command on SmartOS -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -16,102 +16,97 @@ log = logging.getLogger(__name__) # Function aliases __func_alias__ = { - 'list_installed': 'list', - 'update_installed': 'update', - 'import_image': 'import' + "list_installed": "list", + "update_installed": "update", + "import_image": "import", } # Define the module's virtual name -__virtualname__ = 'imgadm' +__virtualname__ = "imgadm" def __virtual__(): - ''' + """ Provides imgadm only on SmartOS - ''' - if salt.utils.platform.is_smartos_globalzone() and \ - salt.utils.path.which('imgadm'): + """ + if salt.utils.platform.is_smartos_globalzone() and salt.utils.path.which("imgadm"): return __virtualname__ return ( False, - '{0} module can only be loaded on SmartOS compute nodes'.format( + "{0} module can only be loaded on SmartOS compute nodes".format( __virtualname__ - ) + ), ) def _exit_status(retcode, stderr=None): - ''' + """ Translate exit status of imgadm - ''' - ret = {0: 'Successful completion.', - 1: 'An error occurred.' if not stderr else stderr, - 2: 'Usage error.', - 3: 'Image not installed.'}[retcode] + """ + ret = { + 0: "Successful completion.", + 1: "An error occurred." if not stderr else stderr, + 2: "Usage error.", + 3: "Image not installed.", + }[retcode] return ret def _parse_image_meta(image=None, detail=False): ret = None - if image and 'Error' in image: + if image and "Error" in image: ret = image - elif image and 'manifest' in image and 'name' in image['manifest']: - name = image['manifest']['name'] - version = image['manifest']['version'] - os = image['manifest']['os'] - description = image['manifest']['description'] - published = image['manifest']['published_at'] - source = image['source'] - if image['manifest']['name'] == 'docker-layer': + elif image and "manifest" in image and "name" in image["manifest"]: + name = image["manifest"]["name"] + version = image["manifest"]["version"] + os = image["manifest"]["os"] + description = image["manifest"]["description"] + published = image["manifest"]["published_at"] + source = image["source"] + if image["manifest"]["name"] == "docker-layer": # NOTE: skip docker-layer unless it has a docker:repo and docker:tag name = None docker_repo = None docker_tag = None - for tag in image['manifest']['tags']: - if tag.startswith('docker:tag:') and image['manifest']['tags'][tag]: - docker_tag = tag.split(':')[-1] - elif tag == 'docker:repo': - docker_repo = image['manifest']['tags'][tag] + for tag in image["manifest"]["tags"]: + if tag.startswith("docker:tag:") and image["manifest"]["tags"][tag]: + docker_tag = tag.split(":")[-1] + elif tag == "docker:repo": + docker_repo = image["manifest"]["tags"][tag] if docker_repo and docker_tag: - name = '{}:{}'.format(docker_repo, docker_tag) - description = 'Docker image imported from {repo}:{tag} on {date}.'.format( - repo=docker_repo, - tag=docker_tag, - date=published, + name = "{}:{}".format(docker_repo, docker_tag) + description = "Docker image imported from {repo}:{tag} on {date}.".format( + repo=docker_repo, tag=docker_tag, date=published, ) if name and detail: ret = { - 'name': name, - 'version': version, - 'os': os, - 'description': description, - 'published': published, - 'source': source, + "name": name, + "version": version, + "os": os, + "description": description, + "published": published, + "source": source, } elif name: - ret = '{name}@{version} [{published}]'.format( - name=name, - version=version, - published=published, + ret = "{name}@{version} [{published}]".format( + name=name, version=version, published=published, ) else: log.debug("smartos_image - encountered invalid image payload: {}".format(image)) - ret = { - 'Error': 'This looks like an orphaned image, image payload was invalid.' - } + ret = {"Error": "This looks like an orphaned image, image payload was invalid."} return ret def _split_docker_uuid(uuid): - ''' + """ Split a smartos docker uuid into repo and tag - ''' + """ if uuid: - uuid = uuid.split(':') + uuid = uuid.split(":") if len(uuid) == 2: tag = uuid[1] repo = uuid[0] @@ -120,28 +115,28 @@ def _split_docker_uuid(uuid): def _is_uuid(uuid): - ''' + """ Check if uuid is a valid smartos uuid Example: e69a0918-055d-11e5-8912-e3ceb6df4cf8 - ''' - if uuid and list((len(x) for x in uuid.split('-'))) == [8, 4, 4, 4, 12]: + """ + if uuid and list((len(x) for x in uuid.split("-"))) == [8, 4, 4, 4, 12]: return True return False def _is_docker_uuid(uuid): - ''' + """ Check if uuid is a valid smartos docker uuid Example plexinc/pms-docker:plexpass - ''' + """ repo, tag = _split_docker_uuid(uuid) return not (not repo and not tag) def version(): - ''' + """ Return imgadm version CLI Example: @@ -149,34 +144,34 @@ def version(): .. code-block:: bash salt '*' imgadm.version - ''' + """ ret = {} - cmd = 'imgadm --version' - res = __salt__['cmd.run'](cmd).splitlines() + cmd = "imgadm --version" + res = __salt__["cmd.run"](cmd).splitlines() ret = res[0].split() return ret[-1] def docker_to_uuid(uuid): - ''' + """ Get the image uuid from an imported docker image .. versionadded:: 2019.2.0 - ''' + """ if _is_uuid(uuid): return uuid if _is_docker_uuid(uuid): images = list_installed(verbose=True) for image_uuid in images: - if 'name' not in images[image_uuid]: + if "name" not in images[image_uuid]: continue - if images[image_uuid]['name'] == uuid: + if images[image_uuid]["name"] == uuid: return image_uuid return None -def update_installed(uuid=''): - ''' +def update_installed(uuid=""): + """ Gather info on unknown image(s) (locally installed) uuid : string @@ -187,14 +182,14 @@ def update_installed(uuid=''): .. code-block:: bash salt '*' imgadm.update [uuid] - ''' - cmd = 'imgadm update {0}'.format(uuid).rstrip() - __salt__['cmd.run'](cmd) + """ + cmd = "imgadm update {0}".format(uuid).rstrip() + __salt__["cmd.run"](cmd) return {} def avail(search=None, verbose=False): - ''' + """ Return a list of available images search : string @@ -208,22 +203,22 @@ def avail(search=None, verbose=False): salt '*' imgadm.avail [percona] salt '*' imgadm.avail verbose=True - ''' + """ ret = {} - cmd = 'imgadm avail -j' - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "imgadm avail -j" + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) + ret["Error"] = _exit_status(retcode) return ret - for image in salt.utils.json.loads(res['stdout']): - if image['manifest']['disabled'] or not image['manifest']['public']: + for image in salt.utils.json.loads(res["stdout"]): + if image["manifest"]["disabled"] or not image["manifest"]["public"]: continue - if search and search not in image['manifest']['name']: + if search and search not in image["manifest"]["name"]: # we skip if we are searching but don't have a match continue - uuid = image['manifest']['uuid'] + uuid = image["manifest"]["uuid"] data = _parse_image_meta(image, verbose) if data: ret[uuid] = data @@ -232,7 +227,7 @@ def avail(search=None, verbose=False): def list_installed(verbose=False): - ''' + """ Return a list of installed images verbose : boolean (False) @@ -249,17 +244,17 @@ def list_installed(verbose=False): salt '*' imgadm.list salt '*' imgadm.list docker=True salt '*' imgadm.list verbose=True - ''' + """ ret = {} - cmd = 'imgadm list -j' - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "imgadm list -j" + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) + ret["Error"] = _exit_status(retcode) return ret - for image in salt.utils.json.loads(res['stdout']): - uuid = image['manifest']['uuid'] + for image in salt.utils.json.loads(res["stdout"]): + uuid = image["manifest"]["uuid"] data = _parse_image_meta(image, verbose) if data: ret[uuid] = data @@ -268,7 +263,7 @@ def list_installed(verbose=False): def show(uuid): - ''' + """ Show manifest of a given image uuid : string @@ -280,25 +275,25 @@ def show(uuid): salt '*' imgadm.show e42f8c84-bbea-11e2-b920-078fab2aab1f salt '*' imgadm.show plexinc/pms-docker:plexpass - ''' + """ ret = {} if _is_uuid(uuid) or _is_docker_uuid(uuid): - cmd = 'imgadm show {0}'.format(uuid) - res = __salt__['cmd.run_all'](cmd, python_shell=False) - retcode = res['retcode'] + cmd = "imgadm show {0}".format(uuid) + res = __salt__["cmd.run_all"](cmd, python_shell=False) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode, res['stderr']) + ret["Error"] = _exit_status(retcode, res["stderr"]) else: - ret = salt.utils.json.loads(res['stdout']) + ret = salt.utils.json.loads(res["stdout"]) else: - ret['Error'] = "{} is not a valid uuid.".format(uuid) + ret["Error"] = "{} is not a valid uuid.".format(uuid) return ret def get(uuid): - ''' + """ Return info on an installed image uuid : string @@ -310,28 +305,28 @@ def get(uuid): salt '*' imgadm.get e42f8c84-bbea-11e2-b920-078fab2aab1f salt '*' imgadm.get plexinc/pms-docker:plexpass - ''' + """ ret = {} if _is_docker_uuid(uuid): uuid = docker_to_uuid(uuid) if _is_uuid(uuid): - cmd = 'imgadm get {0}'.format(uuid) - res = __salt__['cmd.run_all'](cmd, python_shell=False) - retcode = res['retcode'] + cmd = "imgadm get {0}".format(uuid) + res = __salt__["cmd.run_all"](cmd, python_shell=False) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode, res['stderr']) + ret["Error"] = _exit_status(retcode, res["stderr"]) else: - ret = salt.utils.json.loads(res['stdout']) + ret = salt.utils.json.loads(res["stdout"]) else: - ret['Error'] = "{} is not a valid uuid.".format(uuid) + ret["Error"] = "{} is not a valid uuid.".format(uuid) return ret def import_image(uuid, verbose=False): - ''' + """ Import an image from the repository uuid : string @@ -344,13 +339,13 @@ def import_image(uuid, verbose=False): .. code-block:: bash salt '*' imgadm.import e42f8c84-bbea-11e2-b920-078fab2aab1f [verbose=True] - ''' + """ ret = {} - cmd = 'imgadm import {0}'.format(uuid) - res = __salt__['cmd.run_all'](cmd, python_shell=False) - retcode = res['retcode'] + cmd = "imgadm import {0}".format(uuid) + res = __salt__["cmd.run_all"](cmd, python_shell=False) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) + ret["Error"] = _exit_status(retcode) return ret uuid = docker_to_uuid(uuid) @@ -359,7 +354,7 @@ def import_image(uuid, verbose=False): def delete(uuid): - ''' + """ Remove an installed image uuid : string @@ -370,17 +365,17 @@ def delete(uuid): .. code-block:: bash salt '*' imgadm.delete e42f8c84-bbea-11e2-b920-078fab2aab1f - ''' + """ ret = {} - cmd = 'imgadm delete {0}'.format(uuid) - res = __salt__['cmd.run_all'](cmd, python_shell=False) - retcode = res['retcode'] + cmd = "imgadm delete {0}".format(uuid) + res = __salt__["cmd.run_all"](cmd, python_shell=False) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) + ret["Error"] = _exit_status(retcode) return ret # output: Deleted image d5b3865c-0804-11e5-be21-dbc4ce844ddc result = [] - for image in res['stdout'].splitlines(): + for image in res["stdout"].splitlines(): image = [var for var in image.split(" ") if var] result.append(image[2]) @@ -388,7 +383,7 @@ def delete(uuid): def vacuum(verbose=False): - ''' + """ Remove unused images verbose : boolean (False) @@ -399,21 +394,21 @@ def vacuum(verbose=False): .. code-block:: bash salt '*' imgadm.vacuum [verbose=True] - ''' + """ ret = {} - cmd = 'imgadm vacuum -f' - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "imgadm vacuum -f" + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) + ret["Error"] = _exit_status(retcode) return ret # output: Deleted image d5b3865c-0804-11e5-be21-dbc4ce844ddc (lx-centos-6@20150601) result = {} - for image in res['stdout'].splitlines(): + for image in res["stdout"].splitlines(): image = [var for var in image.split(" ") if var] result[image[2]] = { - 'name': image[3][1:image[3].index('@')], - 'version': image[3][image[3].index('@')+1:-1] + "name": image[3][1 : image[3].index("@")], + "version": image[3][image[3].index("@") + 1 : -1], } if verbose: return result @@ -422,7 +417,7 @@ def vacuum(verbose=False): def sources(verbose=False): - ''' + """ Return a list of available sources verbose : boolean (False) @@ -435,18 +430,18 @@ def sources(verbose=False): .. code-block:: bash salt '*' imgadm.sources - ''' + """ ret = {} - cmd = 'imgadm sources -j' - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "imgadm sources -j" + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) + ret["Error"] = _exit_status(retcode) return ret - for src in salt.utils.json.loads(res['stdout']): - ret[src['url']] = src - del src['url'] + for src in salt.utils.json.loads(res["stdout"]): + ret[src["url"]] = src + del src["url"] if not verbose: ret = list(ret) @@ -455,7 +450,7 @@ def sources(verbose=False): def source_delete(source): - ''' + """ Delete a source source : string @@ -468,20 +463,20 @@ def source_delete(source): .. code-block:: bash salt '*' imgadm.source_delete https://updates.joyent.com - ''' + """ ret = {} - cmd = 'imgadm sources -d {0}'.format(source) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "imgadm sources -d {0}".format(source) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode, res['stderr']) + ret["Error"] = _exit_status(retcode, res["stderr"]) return ret return sources(False) -def source_add(source, source_type='imgapi'): - ''' +def source_add(source, source_type="imgapi"): + """ Add a new source source : string @@ -497,21 +492,22 @@ def source_add(source, source_type='imgapi'): salt '*' imgadm.source_add https://updates.joyent.com salt '*' imgadm.source_add https://docker.io docker - ''' + """ ret = {} # NOTE: there are some undocumented deprecated source types # so we just warn instead of error on those - if source_type not in ['imgapi', 'docker']: - log.warning('Possible unsupported imgage source type specified!') + if source_type not in ["imgapi", "docker"]: + log.warning("Possible unsupported imgage source type specified!") - cmd = 'imgadm sources -a {0} -t {1}'.format(source, source_type) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "imgadm sources -a {0} -t {1}".format(source, source_type) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode, res['stderr']) + ret["Error"] = _exit_status(retcode, res["stderr"]) return ret return sources(False) + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/smartos_nictagadm.py b/salt/modules/smartos_nictagadm.py index 8ed4ee7a12c..ae079e7f374 100644 --- a/salt/modules/smartos_nictagadm.py +++ b/salt/modules/smartos_nictagadm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for running nictagadm command on SmartOS :maintainer: Jorge Schrauwen :maturity: new @@ -8,8 +8,8 @@ Module for running nictagadm command on SmartOS ..versionadded:: 2016.11.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -21,32 +21,32 @@ import salt.utils.platform log = logging.getLogger(__name__) # Function aliases -__func_alias__ = { - 'list_nictags': 'list' -} +__func_alias__ = {"list_nictags": "list"} # Define the module's virtual name -__virtualname__ = 'nictagadm' +__virtualname__ = "nictagadm" def __virtual__(): - ''' + """ Provides nictagadm on SmartOS - ''' - if salt.utils.platform.is_smartos_globalzone() and \ - salt.utils.path.which('dladm') and \ - salt.utils.path.which('nictagadm'): + """ + if ( + salt.utils.platform.is_smartos_globalzone() + and salt.utils.path.which("dladm") + and salt.utils.path.which("nictagadm") + ): return __virtualname__ return ( False, - '{0} module can only be loaded on SmartOS compute nodes'.format( + "{0} module can only be loaded on SmartOS compute nodes".format( __virtualname__ - ) + ), ) def list_nictags(include_etherstubs=True): - ''' + """ List all nictags include_etherstubs : boolean @@ -57,29 +57,29 @@ def list_nictags(include_etherstubs=True): .. code-block:: bash salt '*' nictagadm.list - ''' + """ ret = {} - cmd = 'nictagadm list -d "|" -p{0}'.format( - ' -L' if not include_etherstubs else '' - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = 'nictagadm list -d "|" -p{0}'.format(" -L" if not include_etherstubs else "") + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else 'Failed to get list of nictags.' + ret["Error"] = ( + res["stderr"] if "stderr" in res else "Failed to get list of nictags." + ) else: - header = ['name', 'macaddress', 'link', 'type'] - for nictag in res['stdout'].splitlines(): - nictag = nictag.split('|') + header = ["name", "macaddress", "link", "type"] + for nictag in res["stdout"].splitlines(): + nictag = nictag.split("|") nictag_data = {} for field in header: nictag_data[field] = nictag[header.index(field)] - ret[nictag_data['name']] = nictag_data - del ret[nictag_data['name']]['name'] + ret[nictag_data["name"]] = nictag_data + del ret[nictag_data["name"]]["name"] return ret def vms(nictag): - ''' + """ List all vms connect to nictag nictag : string @@ -90,20 +90,22 @@ def vms(nictag): .. code-block:: bash salt '*' nictagadm.vms admin - ''' + """ ret = {} - cmd = 'nictagadm vms {0}'.format(nictag) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "nictagadm vms {0}".format(nictag) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else 'Failed to get list of vms.' + ret["Error"] = ( + res["stderr"] if "stderr" in res else "Failed to get list of vms." + ) else: - ret = res['stdout'].splitlines() + ret = res["stdout"].splitlines() return ret def exists(*nictag, **kwargs): - ''' + """ Check if nictags exists nictag : string @@ -116,18 +118,18 @@ def exists(*nictag, **kwargs): .. code-block:: bash salt '*' nictagadm.exists admin - ''' + """ ret = {} if not nictag: - return {'Error': 'Please provide at least one nictag to check.'} + return {"Error": "Please provide at least one nictag to check."} - cmd = 'nictagadm exists -l {0}'.format(' '.join(nictag)) - res = __salt__['cmd.run_all'](cmd) + cmd = "nictagadm exists -l {0}".format(" ".join(nictag)) + res = __salt__["cmd.run_all"](cmd) - if not kwargs.get('verbose', False): - ret = res['retcode'] == 0 + if not kwargs.get("verbose", False): + ret = res["retcode"] == 0 else: - missing = res['stderr'].splitlines() + missing = res["stderr"].splitlines() for nt in nictag: ret[nt] = nt not in missing @@ -135,7 +137,7 @@ def exists(*nictag, **kwargs): def add(name, mac, mtu=1500): - ''' + """ Add a new nictag name : string @@ -151,33 +153,37 @@ def add(name, mac, mtu=1500): salt '*' nictagadm.add storage0 etherstub salt '*' nictagadm.add trunk0 'DE:AD:OO:OO:BE:EF' 9000 - ''' + """ ret = {} if mtu > 9000 or mtu < 1500: - return {'Error': 'mtu must be a value between 1500 and 9000.'} - if mac != 'etherstub': - cmd = 'dladm show-phys -m -p -o address' - res = __salt__['cmd.run_all'](cmd) + return {"Error": "mtu must be a value between 1500 and 9000."} + if mac != "etherstub": + cmd = "dladm show-phys -m -p -o address" + res = __salt__["cmd.run_all"](cmd) # dladm prints '00' as '0', so account for that. - if mac.replace('00', '0') not in res['stdout'].splitlines(): - return {'Error': '{0} is not present on this system.'.format(mac)} + if mac.replace("00", "0") not in res["stdout"].splitlines(): + return {"Error": "{0} is not present on this system.".format(mac)} - if mac == 'etherstub': - cmd = 'nictagadm add -l {0}'.format(name) - res = __salt__['cmd.run_all'](cmd) + if mac == "etherstub": + cmd = "nictagadm add -l {0}".format(name) + res = __salt__["cmd.run_all"](cmd) else: - cmd = 'nictagadm add -p mtu={0},mac={1} {2}'.format(mtu, mac, name) - res = __salt__['cmd.run_all'](cmd) + cmd = "nictagadm add -p mtu={0},mac={1} {2}".format(mtu, mac, name) + res = __salt__["cmd.run_all"](cmd) - if res['retcode'] == 0: + if res["retcode"] == 0: return True else: - return {'Error': 'failed to create nictag.' if 'stderr' not in res and res['stderr'] == '' else res['stderr']} + return { + "Error": "failed to create nictag." + if "stderr" not in res and res["stderr"] == "" + else res["stderr"] + } def update(name, mac=None, mtu=None): - ''' + """ Update a nictag name : string @@ -192,25 +198,25 @@ def update(name, mac=None, mtu=None): .. code-block:: bash salt '*' nictagadm.update trunk mtu=9000 - ''' + """ ret = {} if name not in list_nictags(): - return {'Error': 'nictag {0} does not exists.'.format(name)} + return {"Error": "nictag {0} does not exists.".format(name)} if not mtu and not mac: - return {'Error': 'please provide either mac or/and mtu.'} + return {"Error": "please provide either mac or/and mtu."} if mtu: if mtu > 9000 or mtu < 1500: - return {'Error': 'mtu must be a value between 1500 and 9000.'} + return {"Error": "mtu must be a value between 1500 and 9000."} if mac: - if mac == 'etherstub': - return {'Error': 'cannot update a nic with "etherstub".'} + if mac == "etherstub": + return {"Error": 'cannot update a nic with "etherstub".'} else: - cmd = 'dladm show-phys -m -p -o address' - res = __salt__['cmd.run_all'](cmd) + cmd = "dladm show-phys -m -p -o address" + res = __salt__["cmd.run_all"](cmd) # dladm prints '00' as '0', so account for that. - if mac.replace('00', '0') not in res['stdout'].splitlines(): - return {'Error': '{0} is not present on this system.'.format(mac)} + if mac.replace("00", "0") not in res["stdout"].splitlines(): + return {"Error": "{0} is not present on this system.".format(mac)} if mac and mtu: properties = "mtu={0},mac={1}".format(mtu, mac) @@ -219,17 +225,21 @@ def update(name, mac=None, mtu=None): elif mtu: properties = "mtu={0}".format(mtu) if mtu else "" - cmd = 'nictagadm update -p {0} {1}'.format(properties, name) - res = __salt__['cmd.run_all'](cmd) + cmd = "nictagadm update -p {0} {1}".format(properties, name) + res = __salt__["cmd.run_all"](cmd) - if res['retcode'] == 0: + if res["retcode"] == 0: return True else: - return {'Error': 'failed to update nictag.' if 'stderr' not in res and res['stderr'] == '' else res['stderr']} + return { + "Error": "failed to update nictag." + if "stderr" not in res and res["stderr"] == "" + else res["stderr"] + } def delete(name, force=False): - ''' + """ Delete nictag name : string @@ -242,18 +252,23 @@ def delete(name, force=False): .. code-block:: bash salt '*' nictagadm.exists admin - ''' + """ ret = {} if name not in list_nictags(): return True - cmd = 'nictagadm delete {0}{1}'.format("-f " if force else "", name) - res = __salt__['cmd.run_all'](cmd) + cmd = "nictagadm delete {0}{1}".format("-f " if force else "", name) + res = __salt__["cmd.run_all"](cmd) - if res['retcode'] == 0: + if res["retcode"] == 0: return True else: - return {'Error': 'failed to delete nictag.' if 'stderr' not in res and res['stderr'] == '' else res['stderr']} + return { + "Error": "failed to delete nictag." + if "stderr" not in res and res["stderr"] == "" + else res["stderr"] + } + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/smartos_virt.py b/salt/modules/smartos_virt.py index 8fddf5f1aa4..d69f6b17198 100644 --- a/salt/modules/smartos_virt.py +++ b/salt/modules/smartos_virt.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" virst compatibility module for managing VMs on SmartOS -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -15,26 +15,25 @@ from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'virt' +__virtualname__ = "virt" def __virtual__(): - ''' + """ Provides virt on SmartOS - ''' - if salt.utils.platform.is_smartos_globalzone() \ - and salt.utils.path.which('vmadm'): + """ + if salt.utils.platform.is_smartos_globalzone() and salt.utils.path.which("vmadm"): return __virtualname__ return ( False, - '{0} module can only be loaded on SmartOS compute nodes'.format( + "{0} module can only be loaded on SmartOS compute nodes".format( __virtualname__ - ) + ), ) def init(**kwargs): - ''' + """ Initialize a new VM CLI Example: @@ -42,12 +41,12 @@ def init(**kwargs): .. code-block:: bash salt '*' virt.init image_uuid='...' alias='...' [...] - ''' - return __salt__['vmadm.create'](**kwargs) + """ + return __salt__["vmadm.create"](**kwargs) def list_domains(): - ''' + """ Return a list of virtual machine names on the minion CLI Example: @@ -55,22 +54,26 @@ def list_domains(): .. code-block:: bash salt '*' virt.list_domains - ''' - data = __salt__['vmadm.list'](keyed=True) - vms = ["UUID TYPE RAM STATE ALIAS"] + """ + data = __salt__["vmadm.list"](keyed=True) + vms = [ + "UUID TYPE RAM STATE ALIAS" + ] for vm in data: - vms.append("{vmuuid}{vmtype}{vmram}{vmstate}{vmalias}".format( - vmuuid=vm.ljust(38), - vmtype=data[vm]['type'].ljust(6), - vmram=data[vm]['ram'].ljust(9), - vmstate=data[vm]['state'].ljust(18), - vmalias=data[vm]['alias'], - )) + vms.append( + "{vmuuid}{vmtype}{vmram}{vmstate}{vmalias}".format( + vmuuid=vm.ljust(38), + vmtype=data[vm]["type"].ljust(6), + vmram=data[vm]["ram"].ljust(9), + vmstate=data[vm]["state"].ljust(18), + vmalias=data[vm]["alias"], + ) + ) return vms def list_active_vms(): - ''' + """ Return a list of uuids for active virtual machine on the minion CLI Example: @@ -78,12 +81,12 @@ def list_active_vms(): .. code-block:: bash salt '*' virt.list_active_vms - ''' - return __salt__['vmadm.list'](search="state='running'", order='uuid') + """ + return __salt__["vmadm.list"](search="state='running'", order="uuid") def list_inactive_vms(): - ''' + """ Return a list of uuids for inactive virtual machine on the minion CLI Example: @@ -91,12 +94,12 @@ def list_inactive_vms(): .. code-block:: bash salt '*' virt.list_inactive_vms - ''' - return __salt__['vmadm.list'](search="state='stopped'", order='uuid') + """ + return __salt__["vmadm.list"](search="state='stopped'", order="uuid") def vm_info(domain): - ''' + """ Return a dict with information about the specified VM on this CN CLI Example: @@ -104,12 +107,12 @@ def vm_info(domain): .. code-block:: bash salt '*' virt.vm_info - ''' - return __salt__['vmadm.get'](domain) + """ + return __salt__["vmadm.get"](domain) def start(domain): - ''' + """ Start a defined domain CLI Example: @@ -117,17 +120,17 @@ def start(domain): .. code-block:: bash salt '*' virt.start - ''' + """ if domain in list_active_vms(): - raise CommandExecutionError('The specified vm is already running') + raise CommandExecutionError("The specified vm is already running") - __salt__['vmadm.start'](domain) + __salt__["vmadm.start"](domain) return domain in list_active_vms() def shutdown(domain): - ''' + """ Send a soft shutdown signal to the named vm CLI Example: @@ -135,17 +138,17 @@ def shutdown(domain): .. code-block:: bash salt '*' virt.shutdown - ''' + """ if domain in list_inactive_vms(): - raise CommandExecutionError('The specified vm is already stopped') + raise CommandExecutionError("The specified vm is already stopped") - __salt__['vmadm.stop'](domain) + __salt__["vmadm.stop"](domain) return domain in list_inactive_vms() def reboot(domain): - ''' + """ Reboot a domain via ACPI request CLI Example: @@ -153,17 +156,17 @@ def reboot(domain): .. code-block:: bash salt '*' virt.reboot - ''' + """ if domain in list_inactive_vms(): - raise CommandExecutionError('The specified vm is stopped') + raise CommandExecutionError("The specified vm is stopped") - __salt__['vmadm.reboot'](domain) + __salt__["vmadm.reboot"](domain) return domain in list_active_vms() def stop(domain): - ''' + """ Hard power down the virtual machine, this is equivalent to powering off the hardware. CLI Example: @@ -171,15 +174,15 @@ def stop(domain): .. code-block:: bash salt '*' virt.destroy - ''' + """ if domain in list_inactive_vms(): - raise CommandExecutionError('The specified vm is stopped') + raise CommandExecutionError("The specified vm is stopped") - return __salt__['vmadm.delete'](domain) + return __salt__["vmadm.delete"](domain) def vm_virt_type(domain): - ''' + """ Return VM virtualization type : OS or KVM CLI Example: @@ -187,16 +190,18 @@ def vm_virt_type(domain): .. code-block:: bash salt '*' virt.vm_virt_type - ''' - ret = __salt__['vmadm.lookup'](search="uuid={uuid}".format(uuid=domain), order='type') + """ + ret = __salt__["vmadm.lookup"]( + search="uuid={uuid}".format(uuid=domain), order="type" + ) if len(ret) < 1: raise CommandExecutionError("We can't determine the type of this VM") - return ret[0]['type'] + return ret[0]["type"] def setmem(domain, memory): - ''' + """ Change the amount of memory allocated to VM. is to be specified in MB. @@ -207,23 +212,23 @@ def setmem(domain, memory): .. code-block:: bash salt '*' virt.setmem 512 - ''' + """ vmtype = vm_virt_type(domain) - if vmtype == 'OS': - return __salt__['vmadm.update'](vm=domain, max_physical_memory=memory) - elif vmtype == 'LX': - return __salt__['vmadm.update'](vm=domain, max_physical_memory=memory) - elif vmtype == 'KVM': - log.warning('Changes will be applied after the VM restart.') - return __salt__['vmadm.update'](vm=domain, ram=memory) + if vmtype == "OS": + return __salt__["vmadm.update"](vm=domain, max_physical_memory=memory) + elif vmtype == "LX": + return __salt__["vmadm.update"](vm=domain, max_physical_memory=memory) + elif vmtype == "KVM": + log.warning("Changes will be applied after the VM restart.") + return __salt__["vmadm.update"](vm=domain, ram=memory) else: - raise CommandExecutionError('Unknown VM type') + raise CommandExecutionError("Unknown VM type") return False def get_macs(domain): - ''' + """ Return a list off MAC addresses from the named VM CLI Example: @@ -231,12 +236,14 @@ def get_macs(domain): .. code-block:: bash salt '*' virt.get_macs - ''' + """ macs = [] - ret = __salt__['vmadm.lookup'](search="uuid={uuid}".format(uuid=domain), order='nics') + ret = __salt__["vmadm.lookup"]( + search="uuid={uuid}".format(uuid=domain), order="nics" + ) if len(ret) < 1: - raise CommandExecutionError('We can\'t find the MAC address of this VM') + raise CommandExecutionError("We can't find the MAC address of this VM") else: - for nic in ret[0]['nics']: - macs.append(nic['mac']) + for nic in ret[0]["nics"]: + macs.append(nic["mac"]) return macs diff --git a/salt/modules/smartos_vmadm.py b/salt/modules/smartos_vmadm.py index 20fd8178e4e..cc2126fdab2 100644 --- a/salt/modules/smartos_vmadm.py +++ b/salt/modules/smartos_vmadm.py @@ -1,16 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Module for running vmadm command on SmartOS -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging import os -try: - from shlex import quote as _quote_args # pylint: disable=E0611 -except ImportError: - from pipes import quote as _quote_args # Import Salt libs import salt.utils.args @@ -19,150 +15,152 @@ import salt.utils.json import salt.utils.path import salt.utils.platform import salt.utils.stringutils -from salt.utils.odict import OrderedDict # Import 3rd-party libs from salt.ext import six +from salt.utils.odict import OrderedDict + +try: + from shlex import quote as _quote_args # pylint: disable=E0611 +except ImportError: + from pipes import quote as _quote_args + log = logging.getLogger(__name__) # Function aliases -__func_alias__ = { - 'list_vms': 'list' -} +__func_alias__ = {"list_vms": "list"} # Define the module's virtual name -__virtualname__ = 'vmadm' +__virtualname__ = "vmadm" def __virtual__(): - ''' + """ Provides vmadm on SmartOS - ''' - if salt.utils.platform.is_smartos_globalzone() and \ - salt.utils.path.which('vmadm') and \ - salt.utils.path.which('zfs'): + """ + if ( + salt.utils.platform.is_smartos_globalzone() + and salt.utils.path.which("vmadm") + and salt.utils.path.which("zfs") + ): return __virtualname__ return ( False, - '{0} module can only be loaded on SmartOS compute nodes'.format( + "{0} module can only be loaded on SmartOS compute nodes".format( __virtualname__ - ) + ), ) def _exit_status(retcode): - ''' + """ Translate exit status of vmadm - ''' - ret = {0: 'Successful completion.', - 1: 'An error occurred.', - 2: 'Usage error.'}[retcode] + """ + ret = {0: "Successful completion.", 1: "An error occurred.", 2: "Usage error."}[ + retcode + ] return ret -def _create_update_from_file(mode='create', uuid=None, path=None): - ''' +def _create_update_from_file(mode="create", uuid=None, path=None): + """ Create vm from file - ''' + """ ret = {} if not os.path.isfile(path) or path is None: - ret['Error'] = 'File ({0}) does not exists!'.format(path) + ret["Error"] = "File ({0}) does not exists!".format(path) return ret # vmadm validate create|update [-f ] - cmd = 'vmadm validate {mode} {brand} -f {path}'.format( - mode=mode, - brand=get(uuid)['brand'] if uuid is not None else '', - path=path + cmd = "vmadm validate {mode} {brand} -f {path}".format( + mode=mode, brand=get(uuid)["brand"] if uuid is not None else "", path=path ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) - if 'stderr' in res: - if res['stderr'][0] == '{': - ret['Error'] = salt.utils.json.loads(res['stderr']) + ret["Error"] = _exit_status(retcode) + if "stderr" in res: + if res["stderr"][0] == "{": + ret["Error"] = salt.utils.json.loads(res["stderr"]) else: - ret['Error'] = res['stderr'] + ret["Error"] = res["stderr"] return ret # vmadm create|update [-f ] - cmd = 'vmadm {mode} {uuid} -f {path}'.format( - mode=mode, - uuid=uuid if uuid is not None else '', - path=path + cmd = "vmadm {mode} {uuid} -f {path}".format( + mode=mode, uuid=uuid if uuid is not None else "", path=path ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) - if 'stderr' in res: - if res['stderr'][0] == '{': - ret['Error'] = salt.utils.json.loads(res['stderr']) + ret["Error"] = _exit_status(retcode) + if "stderr" in res: + if res["stderr"][0] == "{": + ret["Error"] = salt.utils.json.loads(res["stderr"]) else: - ret['Error'] = res['stderr'] + ret["Error"] = res["stderr"] return ret else: - if res['stderr'].startswith('Successfully created VM'): - return res['stderr'][24:] + if res["stderr"].startswith("Successfully created VM"): + return res["stderr"][24:] return True -def _create_update_from_cfg(mode='create', uuid=None, vmcfg=None): - ''' +def _create_update_from_cfg(mode="create", uuid=None, vmcfg=None): + """ Create vm from configuration - ''' + """ ret = {} # write json file - vmadm_json_file = __salt__['temp.file'](prefix='vmadm-') - with salt.utils.files.fopen(vmadm_json_file, 'w') as vmadm_json: + vmadm_json_file = __salt__["temp.file"](prefix="vmadm-") + with salt.utils.files.fopen(vmadm_json_file, "w") as vmadm_json: salt.utils.json.dump(vmcfg, vmadm_json) # vmadm validate create|update [-f ] - cmd = 'vmadm validate {mode} {brand} -f {vmadm_json_file}'.format( + cmd = "vmadm validate {mode} {brand} -f {vmadm_json_file}".format( mode=mode, - brand=get(uuid)['brand'] if uuid is not None else '', - vmadm_json_file=vmadm_json_file + brand=get(uuid)["brand"] if uuid is not None else "", + vmadm_json_file=vmadm_json_file, ) - res = __salt__['cmd.run_all'](cmd, python_shell=True) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd, python_shell=True) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) - if 'stderr' in res: - if res['stderr'][0] == '{': - ret['Error'] = salt.utils.json.loads(res['stderr']) + ret["Error"] = _exit_status(retcode) + if "stderr" in res: + if res["stderr"][0] == "{": + ret["Error"] = salt.utils.json.loads(res["stderr"]) else: - ret['Error'] = res['stderr'] + ret["Error"] = res["stderr"] return ret # vmadm create|update [-f ] - cmd = 'vmadm {mode} {uuid} -f {vmadm_json_file}'.format( + cmd = "vmadm {mode} {uuid} -f {vmadm_json_file}".format( mode=mode, - uuid=uuid if uuid is not None else '', - vmadm_json_file=vmadm_json_file + uuid=uuid if uuid is not None else "", + vmadm_json_file=vmadm_json_file, ) - res = __salt__['cmd.run_all'](cmd, python_shell=True) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd, python_shell=True) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) - if 'stderr' in res: - if res['stderr'][0] == '{': - ret['Error'] = salt.utils.json.loads(res['stderr']) + ret["Error"] = _exit_status(retcode) + if "stderr" in res: + if res["stderr"][0] == "{": + ret["Error"] = salt.utils.json.loads(res["stderr"]) else: - ret['Error'] = res['stderr'] + ret["Error"] = res["stderr"] return ret else: # cleanup json file (only when successful to help troubleshooting) salt.utils.files.safe_rm(vmadm_json_file) # return uuid - if res['stderr'].startswith('Successfully created VM'): - return res['stderr'][24:] + if res["stderr"].startswith("Successfully created VM"): + return res["stderr"][24:] return True -def start(vm, options=None, key='uuid'): - ''' +def start(vm, options=None, key="uuid"): + """ Start a vm vm : string @@ -180,29 +178,28 @@ def start(vm, options=None, key='uuid'): salt '*' vmadm.start 186da9ab-7392-4f55-91a5-b8f1fe770543 'order=c,once=d cdrom=/path/to/image.iso,ide' salt '*' vmadm.start vm=nacl key=alias salt '*' vmadm.start vm=nina.example.org key=hostname - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm # vmadm start [option=value ...] - cmd = 'vmadm start {uuid} {options}'.format( - uuid=vm, - options=options if options else '' + cmd = "vmadm start {uuid} {options}".format( + uuid=vm, options=options if options else "" ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True -def stop(vm, force=False, key='uuid'): - ''' +def stop(vm, force=False, key="uuid"): + """ Stop a vm vm : string @@ -220,29 +217,26 @@ def stop(vm, force=False, key='uuid'): salt '*' vmadm.stop 186da9ab-7392-4f55-91a5-b8f1fe770543 True salt '*' vmadm.stop vm=nacl key=alias salt '*' vmadm.stop vm=nina.example.org key=hostname - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm # vmadm stop [-F] - cmd = 'vmadm stop {force} {uuid}'.format( - force='-F' if force else '', - uuid=vm - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "vmadm stop {force} {uuid}".format(force="-F" if force else "", uuid=vm) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = _exit_status(retcode) + ret["Error"] = _exit_status(retcode) return ret return True -def reboot(vm, force=False, key='uuid'): - ''' +def reboot(vm, force=False, key="uuid"): + """ Reboot a vm vm : string @@ -260,29 +254,26 @@ def reboot(vm, force=False, key='uuid'): salt '*' vmadm.reboot 186da9ab-7392-4f55-91a5-b8f1fe770543 True salt '*' vmadm.reboot vm=nacl key=alias salt '*' vmadm.reboot vm=nina.example.org key=hostname - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm # vmadm reboot [-F] - cmd = 'vmadm reboot {force} {uuid}'.format( - force='-F' if force else '', - uuid=vm - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "vmadm reboot {force} {uuid}".format(force="-F" if force else "", uuid=vm) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True -def list_vms(search=None, sort=None, order='uuid,type,ram,state,alias', keyed=True): - ''' +def list_vms(search=None, sort=None, order="uuid,type,ram,state,alias", keyed=True): + """ Return a list of VMs search : string @@ -303,26 +294,26 @@ def list_vms(search=None, sort=None, order='uuid,type,ram,state,alias', keyed=Tr salt '*' vmadm.list salt '*' vmadm.list order=alias,ram,cpu_cap sort=-ram,-cpu_cap salt '*' vmadm.list search='type=KVM' - ''' + """ ret = {} # vmadm list [-p] [-H] [-o field,...] [-s field,...] [field=value ...] - cmd = 'vmadm list -p -H {order} {sort} {search}'.format( - order='-o {0}'.format(order) if order else '', - sort='-s {0}'.format(sort) if sort else '', - search=search if search else '' + cmd = "vmadm list -p -H {order} {sort} {search}".format( + order="-o {0}".format(order) if order else "", + sort="-s {0}".format(sort) if sort else "", + search=search if search else "", ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] result = OrderedDict() if keyed else [] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret - fields = order.split(',') + fields = order.split(",") - for vm in res['stdout'].splitlines(): + for vm in res["stdout"].splitlines(): vm_data = OrderedDict() - vm = vm.split(':') + vm = vm.split(":") if keyed: for field in fields: if fields.index(field) == 0: @@ -340,7 +331,7 @@ def list_vms(search=None, sort=None, order='uuid,type,ram,state,alias', keyed=Tr def lookup(search=None, order=None, one=False): - ''' + """ Return a list of VMs using lookup search : string @@ -357,32 +348,32 @@ def lookup(search=None, order=None, one=False): salt '*' vmadm.lookup search='state=running' salt '*' vmadm.lookup search='state=running' order=uuid,alias,hostname salt '*' vmadm.lookup search='alias=nacl' one=True - ''' + """ ret = {} # vmadm lookup [-j|-1] [-o field,...] [field=value ...] - cmd = 'vmadm lookup {one} {order} {search}'.format( - one='-1' if one else '-j', - order='-o {0}'.format(order) if order else '', - search=search if search else '' + cmd = "vmadm lookup {one} {order} {search}".format( + one="-1" if one else "-j", + order="-o {0}".format(order) if order else "", + search=search if search else "", ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] result = [] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret if one: - result = res['stdout'] + result = res["stdout"] else: - for vm in salt.utils.json.loads(res['stdout']): + for vm in salt.utils.json.loads(res["stdout"]): result.append(vm) return result -def sysrq(vm, action='nmi', key='uuid'): - ''' +def sysrq(vm, action="nmi", key="uuid"): + """ Send non-maskable interrupt to vm or capture a screenshot vm : string @@ -399,32 +390,29 @@ def sysrq(vm, action='nmi', key='uuid'): salt '*' vmadm.sysrq 186da9ab-7392-4f55-91a5-b8f1fe770543 nmi salt '*' vmadm.sysrq 186da9ab-7392-4f55-91a5-b8f1fe770543 screenshot salt '*' vmadm.sysrq nacl nmi key=alias - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - if action not in ['nmi', 'screenshot']: - ret['Error'] = 'Action must be either nmi or screenshot' + if action not in ["nmi", "screenshot"]: + ret["Error"] = "Action must be either nmi or screenshot" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm # vmadm sysrq - cmd = 'vmadm sysrq {uuid} {action}'.format( - uuid=vm, - action=action - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "vmadm sysrq {uuid} {action}".format(uuid=vm, action=action) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True -def delete(vm, key='uuid'): - ''' +def delete(vm, key="uuid"): + """ Delete a vm vm : string @@ -438,26 +426,26 @@ def delete(vm, key='uuid'): salt '*' vmadm.delete 186da9ab-7392-4f55-91a5-b8f1fe770543 salt '*' vmadm.delete nacl key=alias - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm # vmadm delete - cmd = 'vmadm delete {0}'.format(vm) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "vmadm delete {0}".format(vm) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True -def get(vm, key='uuid'): - ''' +def get(vm, key="uuid"): + """ Output the JSON object describing a VM vm : string @@ -471,26 +459,26 @@ def get(vm, key='uuid'): salt '*' vmadm.get 186da9ab-7392-4f55-91a5-b8f1fe770543 salt '*' vmadm.get nacl key=alias - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm # vmadm get - cmd = 'vmadm get {0}'.format(vm) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "vmadm get {0}".format(vm) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret - return salt.utils.json.loads(res['stdout']) + return salt.utils.json.loads(res["stdout"]) -def info(vm, info_type='all', key='uuid'): - ''' +def info(vm, info_type="all", key="uuid"): + """ Lookup info on running kvm vm : string @@ -508,32 +496,40 @@ def info(vm, info_type='all', key='uuid'): salt '*' vmadm.info 186da9ab-7392-4f55-91a5-b8f1fe770543 vnc salt '*' vmadm.info nacl key=alias salt '*' vmadm.info nacl vnc key=alias - ''' + """ ret = {} - if info_type not in ['all', 'block', 'blockstats', 'chardev', 'cpus', 'kvm', 'pci', 'spice', 'version', 'vnc']: - ret['Error'] = 'Requested info_type is not available' + if info_type not in [ + "all", + "block", + "blockstats", + "chardev", + "cpus", + "kvm", + "pci", + "spice", + "version", + "vnc", + ]: + ret["Error"] = "Requested info_type is not available" return ret - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm # vmadm info [type,...] - cmd = 'vmadm info {uuid} {type}'.format( - uuid=vm, - type=info_type - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "vmadm info {uuid} {type}".format(uuid=vm, type=info_type) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret - return salt.utils.json.loads(res['stdout']) + return salt.utils.json.loads(res["stdout"]) -def create_snapshot(vm, name, key='uuid'): - ''' +def create_snapshot(vm, name, key="uuid"): + """ Create snapshot of a vm vm : string @@ -552,39 +548,36 @@ def create_snapshot(vm, name, key='uuid'): salt '*' vmadm.create_snapshot 186da9ab-7392-4f55-91a5-b8f1fe770543 baseline salt '*' vmadm.create_snapshot nacl baseline key=alias - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm vmobj = get(vm) - if 'datasets' in vmobj: - ret['Error'] = 'VM cannot have datasets' + if "datasets" in vmobj: + ret["Error"] = "VM cannot have datasets" return ret - if vmobj['brand'] in ['kvm']: - ret['Error'] = 'VM must be of type OS' + if vmobj["brand"] in ["kvm"]: + ret["Error"] = "VM must be of type OS" return ret - if vmobj['zone_state'] not in ['running']: # work around a vmadm bug - ret['Error'] = 'VM must be running to take a snapshot' + if vmobj["zone_state"] not in ["running"]: # work around a vmadm bug + ret["Error"] = "VM must be running to take a snapshot" return ret # vmadm create-snapshot - cmd = 'vmadm create-snapshot {uuid} {snapshot}'.format( - snapshot=name, - uuid=vm - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "vmadm create-snapshot {uuid} {snapshot}".format(snapshot=name, uuid=vm) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True -def delete_snapshot(vm, name, key='uuid'): - ''' +def delete_snapshot(vm, name, key="uuid"): + """ Delete snapshot of a vm vm : string @@ -603,36 +596,33 @@ def delete_snapshot(vm, name, key='uuid'): salt '*' vmadm.delete_snapshot 186da9ab-7392-4f55-91a5-b8f1fe770543 baseline salt '*' vmadm.delete_snapshot nacl baseline key=alias - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm vmobj = get(vm) - if 'datasets' in vmobj: - ret['Error'] = 'VM cannot have datasets' + if "datasets" in vmobj: + ret["Error"] = "VM cannot have datasets" return ret - if vmobj['brand'] in ['kvm']: - ret['Error'] = 'VM must be of type OS' + if vmobj["brand"] in ["kvm"]: + ret["Error"] = "VM must be of type OS" return ret # vmadm delete-snapshot - cmd = 'vmadm delete-snapshot {uuid} {snapshot}'.format( - snapshot=name, - uuid=vm - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "vmadm delete-snapshot {uuid} {snapshot}".format(snapshot=name, uuid=vm) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True -def rollback_snapshot(vm, name, key='uuid'): - ''' +def rollback_snapshot(vm, name, key="uuid"): + """ Rollback snapshot of a vm vm : string @@ -651,36 +641,33 @@ def rollback_snapshot(vm, name, key='uuid'): salt '*' vmadm.rollback_snapshot 186da9ab-7392-4f55-91a5-b8f1fe770543 baseline salt '*' vmadm.rollback_snapshot nacl baseline key=alias - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm vmobj = get(vm) - if 'datasets' in vmobj: - ret['Error'] = 'VM cannot have datasets' + if "datasets" in vmobj: + ret["Error"] = "VM cannot have datasets" return ret - if vmobj['brand'] in ['kvm']: - ret['Error'] = 'VM must be of type OS' + if vmobj["brand"] in ["kvm"]: + ret["Error"] = "VM must be of type OS" return ret # vmadm rollback-snapshot - cmd = 'vmadm rollback-snapshot {uuid} {snapshot}'.format( - snapshot=name, - uuid=vm - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "vmadm rollback-snapshot {uuid} {snapshot}".format(snapshot=name, uuid=vm) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True -def reprovision(vm, image, key='uuid'): - ''' +def reprovision(vm, image, key="uuid"): + """ Reprovision a vm vm : string @@ -696,32 +683,32 @@ def reprovision(vm, image, key='uuid'): salt '*' vmadm.reprovision 186da9ab-7392-4f55-91a5-b8f1fe770543 c02a2044-c1bd-11e4-bd8c-dfc1db8b0182 salt '*' vmadm.reprovision nacl c02a2044-c1bd-11e4-bd8c-dfc1db8b0182 key=alias - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm - if image not in __salt__['imgadm.list'](): - ret['Error'] = 'Image ({0}) is not present on this host'.format(image) + if image not in __salt__["imgadm.list"](): + ret["Error"] = "Image ({0}) is not present on this host".format(image) return ret # vmadm reprovision [-f ] - cmd = six.text_type('echo {image} | vmadm reprovision {uuid}').format( + cmd = six.text_type("echo {image} | vmadm reprovision {uuid}").format( uuid=salt.utils.stringutils.to_unicode(vm), - image=_quote_args(salt.utils.json.dumps({'image_uuid': image})) + image=_quote_args(salt.utils.json.dumps({"image_uuid": image})), ) - res = __salt__['cmd.run_all'](cmd, python_shell=True) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd, python_shell=True) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True def create(from_file=None, **kwargs): - ''' + """ Create a new vm from_file : string @@ -735,7 +722,7 @@ def create(from_file=None, **kwargs): salt '*' vmadm.create from_file=/tmp/new_vm.json salt '*' vmadm.create image_uuid='...' alias='...' nics='[{ "nic_tag": "admin", "ip": "198.51.100.123", ...}, {...}]' [...] - ''' + """ ret = {} # prepare vmcfg vmcfg = {} @@ -744,13 +731,13 @@ def create(from_file=None, **kwargs): vmcfg[k] = v if from_file: - return _create_update_from_file('create', path=from_file) + return _create_update_from_file("create", path=from_file) else: - return _create_update_from_cfg('create', vmcfg=vmcfg) + return _create_update_from_cfg("create", vmcfg=vmcfg) -def update(vm, from_file=None, key='uuid', **kwargs): - ''' +def update(vm, from_file=None, key="uuid", **kwargs): + """ Update a new vm vm : string @@ -769,7 +756,7 @@ def update(vm, from_file=None, key='uuid', **kwargs): salt '*' vmadm.update vm=186da9ab-7392-4f55-91a5-b8f1fe770543 from_file=/tmp/new_vm.json salt '*' vmadm.update vm=nacl key=alias from_file=/tmp/new_vm.json salt '*' vmadm.update vm=186da9ab-7392-4f55-91a5-b8f1fe770543 max_physical_memory=1024 - ''' + """ ret = {} # prepare vmcfg vmcfg = {} @@ -777,21 +764,21 @@ def update(vm, from_file=None, key='uuid', **kwargs): for k, v in six.iteritems(kwargs): vmcfg[k] = v - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret - uuid = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in uuid: + uuid = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in uuid: return uuid if from_file: - return _create_update_from_file('update', uuid, path=from_file) + return _create_update_from_file("update", uuid, path=from_file) else: - return _create_update_from_cfg('update', uuid, vmcfg=vmcfg) + return _create_update_from_cfg("update", uuid, vmcfg=vmcfg) -def send(vm, target, key='uuid'): - ''' +def send(vm, target, key="uuid"): + """ Send a vm to a directory vm : string @@ -807,49 +794,48 @@ def send(vm, target, key='uuid'): salt '*' vmadm.send 186da9ab-7392-4f55-91a5-b8f1fe770543 /opt/backups salt '*' vmadm.send vm=nacl target=/opt/backups key=alias - ''' + """ ret = {} - if key not in ['uuid', 'alias', 'hostname']: - ret['Error'] = 'Key must be either uuid, alias or hostname' + if key not in ["uuid", "alias", "hostname"]: + ret["Error"] = "Key must be either uuid, alias or hostname" return ret if not os.path.isdir(target): - ret['Error'] = 'Target must be a directory or host' + ret["Error"] = "Target must be a directory or host" return ret - vm = lookup('{0}={1}'.format(key, vm), one=True) - if 'Error' in vm: + vm = lookup("{0}={1}".format(key, vm), one=True) + if "Error" in vm: return vm # vmadm send [target] - cmd = 'vmadm send {uuid} > {target}'.format( - uuid=vm, - target=os.path.join(target, '{0}.vmdata'.format(vm)) + cmd = "vmadm send {uuid} > {target}".format( + uuid=vm, target=os.path.join(target, "{0}.vmdata".format(vm)) ) - res = __salt__['cmd.run_all'](cmd, python_shell=True) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd, python_shell=True) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret vmobj = get(vm) - if 'datasets' not in vmobj: + if "datasets" not in vmobj: return True - log.warning('one or more datasets detected, this is not supported!') - log.warning('trying to zfs send datasets...') - for dataset in vmobj['datasets']: - name = dataset.split('/') + log.warning("one or more datasets detected, this is not supported!") + log.warning("trying to zfs send datasets...") + for dataset in vmobj["datasets"]: + name = dataset.split("/") name = name[-1] - cmd = 'zfs send {dataset} > {target}'.format( + cmd = "zfs send {dataset} > {target}".format( dataset=dataset, - target=os.path.join(target, '{0}-{1}.zfsds'.format(vm, name)) + target=os.path.join(target, "{0}-{1}.zfsds".format(vm, name)), ) - res = __salt__['cmd.run_all'](cmd, python_shell=True) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd, python_shell=True) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True def receive(uuid, source): - ''' + """ Receive a vm from a directory uuid : string @@ -862,46 +848,47 @@ def receive(uuid, source): .. code-block:: bash salt '*' vmadm.receive 186da9ab-7392-4f55-91a5-b8f1fe770543 /opt/backups - ''' + """ ret = {} if not os.path.isdir(source): - ret['Error'] = 'Source must be a directory or host' + ret["Error"] = "Source must be a directory or host" return ret - if not os.path.exists(os.path.join(source, '{0}.vmdata'.format(uuid))): - ret['Error'] = 'Unknow vm with uuid in {0}'.format(source) + if not os.path.exists(os.path.join(source, "{0}.vmdata".format(uuid))): + ret["Error"] = "Unknow vm with uuid in {0}".format(source) return ret # vmadm receive - cmd = 'vmadm receive < {source}'.format( - source=os.path.join(source, '{0}.vmdata'.format(uuid)) + cmd = "vmadm receive < {source}".format( + source=os.path.join(source, "{0}.vmdata".format(uuid)) ) - res = __salt__['cmd.run_all'](cmd, python_shell=True) - retcode = res['retcode'] - if retcode != 0 and not res['stderr'].endswith('datasets'): - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + res = __salt__["cmd.run_all"](cmd, python_shell=True) + retcode = res["retcode"] + if retcode != 0 and not res["stderr"].endswith("datasets"): + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret vmobj = get(uuid) - if 'datasets' not in vmobj: + if "datasets" not in vmobj: return True - log.warning('one or more datasets detected, this is not supported!') - log.warning('trying to restore datasets, mountpoints will need to be set again...') - for dataset in vmobj['datasets']: - name = dataset.split('/') + log.warning("one or more datasets detected, this is not supported!") + log.warning("trying to restore datasets, mountpoints will need to be set again...") + for dataset in vmobj["datasets"]: + name = dataset.split("/") name = name[-1] - cmd = 'zfs receive {dataset} < {source}'.format( + cmd = "zfs receive {dataset} < {source}".format( dataset=dataset, - source=os.path.join(source, '{0}-{1}.zfsds'.format(uuid, name)) + source=os.path.join(source, "{0}-{1}.zfsds".format(uuid, name)), ) - res = __salt__['cmd.run_all'](cmd, python_shell=True) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd, python_shell=True) + retcode = res["retcode"] if retcode != 0: - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret - cmd = 'vmadm install {0}'.format(uuid) - res = __salt__['cmd.run_all'](cmd, python_shell=True) - retcode = res['retcode'] - if retcode != 0 and not res['stderr'].endswith('datasets'): - ret['Error'] = res['stderr'] if 'stderr' in res else _exit_status(retcode) + cmd = "vmadm install {0}".format(uuid) + res = __salt__["cmd.run_all"](cmd, python_shell=True) + retcode = res["retcode"] + if retcode != 0 and not res["stderr"].endswith("datasets"): + ret["Error"] = res["stderr"] if "stderr" in res else _exit_status(retcode) return ret return True + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/smbios.py b/salt/modules/smbios.py index f9e51e52f53..baa31fb9537 100644 --- a/salt/modules/smbios.py +++ b/salt/modules/smbios.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Interface to SMBIOS/DMI (Parsing through dmidecode) @@ -10,20 +10,20 @@ External References | `System Management BIOS `_ | `DMIdecode `_ -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import logging -import uuid -import re +from __future__ import absolute_import, print_function, unicode_literals -# Import salt libs -import salt.utils.path +import logging +import re +import uuid # Solve the Chicken and egg problem where grains need to run before any # of the modules are loaded and are generally available for any usage. import salt.modules.cmdmod +# Import salt libs +import salt.utils.path from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin from salt.ext.six.moves import zip # pylint: disable=import-error,redefined-builtin @@ -31,15 +31,17 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work when dmidecode is installed. - ''' - return (bool(salt.utils.path.which_bin(['dmidecode', 'smbios'])), - 'The smbios execution module failed to load: neither dmidecode nor smbios in the path.') + """ + return ( + bool(salt.utils.path.which_bin(["dmidecode", "smbios"])), + "The smbios execution module failed to load: neither dmidecode nor smbios in the path.", + ) def get(string, clean=True): - ''' + """ Get an individual DMI string from SMBIOS info string @@ -77,20 +79,20 @@ def get(string, clean=True): .. code-block:: bash salt '*' smbios.get system-uuid clean=False - ''' + """ - val = _dmidecoder('-s {0}'.format(string)).strip() + val = _dmidecoder("-s {0}".format(string)).strip() # Cleanup possible comments in strings. - val = '\n'.join([v for v in val.split('\n') if not v.startswith('#')]) - if val.startswith('/dev/mem') or clean and not _dmi_isclean(string, val): + val = "\n".join([v for v in val.split("\n") if not v.startswith("#")]) + if val.startswith("/dev/mem") or clean and not _dmi_isclean(string, val): val = None return val def records(rec_type=None, fields=None, clean=True): - ''' + """ Return DMI records from SMBIOS type @@ -158,36 +160,38 @@ def records(rec_type=None, fields=None, clean=True): salt '*' smbios.records 14 salt '*' smbios.records 4 core_count,thread_count,current_speed - ''' + """ if rec_type is None: smbios = _dmi_parse(_dmidecoder(), clean, fields) else: - smbios = _dmi_parse(_dmidecoder('-t {0}'.format(rec_type)), clean, fields) + smbios = _dmi_parse(_dmidecoder("-t {0}".format(rec_type)), clean, fields) return smbios def _dmi_parse(data, clean=True, fields=None): - ''' + """ Structurize DMI records into a nice list Optionally trash bogus entries and filter output - ''' + """ dmi = [] # Detect & split Handle records - dmi_split = re.compile('(handle [0-9]x[0-9a-f]+[^\n]+)\n', re.MULTILINE+re.IGNORECASE) + dmi_split = re.compile( + "(handle [0-9]x[0-9a-f]+[^\n]+)\n", re.MULTILINE + re.IGNORECASE + ) dmi_raw = iter(re.split(dmi_split, data)[1:]) for handle, dmi_raw in zip(dmi_raw, dmi_raw): - handle, htype = [hline.split()[-1] for hline in handle.split(',')][0:2] - dmi_raw = dmi_raw.split('\n') + handle, htype = [hline.split()[-1] for hline in handle.split(",")][0:2] + dmi_raw = dmi_raw.split("\n") # log.debug('%s record contains %s', handle, dmi_raw) - log.debug('Parsing handle %s', handle) + log.debug("Parsing handle %s", handle) # The first line of a handle is a description of the type record = { - 'handle': handle, - 'description': dmi_raw.pop(0).strip(), - 'type': int(htype) + "handle": handle, + "description": dmi_raw.pop(0).strip(), + "type": int(htype), } if not dmi_raw: @@ -199,7 +203,7 @@ def _dmi_parse(data, clean=True, fields=None): # log.debug('%s record contains %s', record, dmi_raw) dmi_data = _dmi_data(dmi_raw, clean, fields) if dmi_data: - record['data'] = dmi_data + record["data"] = dmi_data dmi.append(record) elif not clean: dmi.append(record) @@ -208,16 +212,16 @@ def _dmi_parse(data, clean=True, fields=None): def _dmi_data(dmi_raw, clean, fields): - ''' + """ Parse the raw DMIdecode output of a single handle into a nice dict - ''' + """ dmi_data = {} key = None key_data = [None, []] for line in dmi_raw: - if re.match(r'\t[^\s]+', line): + if re.match(r"\t[^\s]+", line): # Finish previous key if key is not None: # log.debug('Evaluating DMI key {0}: {1}'.format(key, key_data)) @@ -235,17 +239,16 @@ def _dmi_data(dmi_raw, clean, fields): # Family: Core i5 # Keyboard Password Status: Not Implemented - key, val = line.split(':', 1) - key = key.strip().lower().replace(' ', '_') - if (clean and key == 'header_and_data') \ - or (fields and key not in fields): + key, val = line.split(":", 1) + key = key.strip().lower().replace(" ", "_") + if (clean and key == "header_and_data") or (fields and key not in fields): key = None continue else: key_data = [_dmi_cast(key, val.strip(), clean), []] elif key is None: continue - elif re.match(r'\t\t[^\s]+', line): + elif re.match(r"\t\t[^\s]+", line): # Installable Languages: 1 # en-US # Characteristics: @@ -260,14 +263,14 @@ def _dmi_data(dmi_raw, clean, fields): def _dmi_cast(key, val, clean=True): - ''' + """ Simple caster thingy for trying to fish out at least ints & lists from strings - ''' + """ if clean and not _dmi_isclean(key, val): return - elif not re.match(r'serial|part|asset|product', key, flags=re.IGNORECASE): - if ',' in val: - val = [el.strip() for el in val.split(',')] + elif not re.match(r"serial|part|asset|product", key, flags=re.IGNORECASE): + if "," in val: + val = [el.strip() for el in val.split(",")] else: try: val = int(val) @@ -278,13 +281,13 @@ def _dmi_cast(key, val, clean=True): def _dmi_isclean(key, val): - ''' + """ Clean out well-known bogus values - ''' - if val is None or not val or re.match('none', val, flags=re.IGNORECASE): + """ + if val is None or not val or re.match("none", val, flags=re.IGNORECASE): # log.debug('DMI {0} value {1} seems invalid or empty'.format(key, val)) return False - elif 'uuid' in key: + elif "uuid" in key: # Try each version (1-5) of RFC4122 to check if it's actually a UUID for uuidver in range(1, 5): try: @@ -292,36 +295,50 @@ def _dmi_isclean(key, val): return True except ValueError: continue - log.trace('DMI %s value %s is an invalid UUID', key, val.replace('\n', ' ')) + log.trace("DMI %s value %s is an invalid UUID", key, val.replace("\n", " ")) return False - elif re.search('serial|part|version', key): + elif re.search("serial|part|version", key): # 'To be filled by O.E.M. # 'Not applicable' etc. # 'Not specified' etc. # 0000000, 1234667 etc. # begone! - return not re.match(r'^[0]+$', val) \ - and not re.match(r'[0]?1234567[8]?[9]?[0]?', val) \ - and not re.search(r'sernum|part[_-]?number|specified|filled|applicable', val, flags=re.IGNORECASE) - elif re.search('asset|manufacturer', key): + return ( + not re.match(r"^[0]+$", val) + and not re.match(r"[0]?1234567[8]?[9]?[0]?", val) + and not re.search( + r"sernum|part[_-]?number|specified|filled|applicable", + val, + flags=re.IGNORECASE, + ) + ) + elif re.search("asset|manufacturer", key): # AssetTag0. Manufacturer04. Begone. - return not re.search(r'manufacturer|to be filled|available|asset|^no(ne|t)', val, flags=re.IGNORECASE) + return not re.search( + r"manufacturer|to be filled|available|asset|^no(ne|t)", + val, + flags=re.IGNORECASE, + ) else: # map unspecified, undefined, unknown & whatever to None - return not re.search(r'to be filled', val, flags=re.IGNORECASE) \ - and not re.search(r'un(known|specified)|no(t|ne)? (asset|provided|defined|available|present|specified)', - val, flags=re.IGNORECASE) + return not re.search( + r"to be filled", val, flags=re.IGNORECASE + ) and not re.search( + r"un(known|specified)|no(t|ne)? (asset|provided|defined|available|present|specified)", + val, + flags=re.IGNORECASE, + ) def _dmidecoder(args=None): - ''' + """ Call DMIdecode - ''' - dmidecoder = salt.utils.path.which_bin(['dmidecode', 'smbios']) + """ + dmidecoder = salt.utils.path.which_bin(["dmidecode", "smbios"]) if not args: out = salt.modules.cmdmod._run_quiet(dmidecoder) else: - out = salt.modules.cmdmod._run_quiet('{0} {1}'.format(dmidecoder, args)) + out = salt.modules.cmdmod._run_quiet("{0} {1}".format(dmidecoder, args)) return out diff --git a/salt/modules/smf_service.py b/salt/modules/smf_service.py index 837e1463e33..4ad04fe133a 100644 --- a/salt/modules/smf_service.py +++ b/salt/modules/smf_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Service support for Solaris 10 and 11, should work with other systems that use SMF also. (e.g. SmartOS) @@ -8,40 +8,45 @@ that use SMF also. (e.g. SmartOS) minion, and it is using a different module (or gives an error similar to *'service.start' is not available*), see :ref:`here `. -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import fnmatch import re -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" def __virtual__(): - ''' + """ Only work on systems which default to SMF - ''' - if 'Solaris' in __grains__['os_family']: + """ + if "Solaris" in __grains__["os_family"]: # Don't let this work on Solaris 9 since SMF doesn't exist on it. - if __grains__['kernelrelease'] == "5.9": - return (False, 'The smf execution module failed to load: SMF not available on Solaris 9.') + if __grains__["kernelrelease"] == "5.9": + return ( + False, + "The smf execution module failed to load: SMF not available on Solaris 9.", + ) return __virtualname__ - return (False, 'The smf execution module failed to load: only available on Solaris.') + return ( + False, + "The smf execution module failed to load: only available on Solaris.", + ) def _get_enabled_disabled(enabled_prop="true"): - ''' + """ DRY: Get all service FMRIs and their enabled property - ''' + """ ret = set() cmd = '/usr/bin/svcprop -c -p general/enabled "*"' - lines = __salt__['cmd.run_stdout'](cmd, python_shell=False).splitlines() + lines = __salt__["cmd.run_stdout"](cmd, python_shell=False).splitlines() for line in lines: comps = line.split() if not comps: @@ -52,7 +57,7 @@ def _get_enabled_disabled(enabled_prop="true"): def get_running(): - ''' + """ Return the running services CLI Example: @@ -60,21 +65,21 @@ def get_running(): .. code-block:: bash salt '*' service.get_running - ''' + """ ret = set() - cmd = '/usr/bin/svcs -H -o FMRI,STATE -s FMRI' - lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + cmd = "/usr/bin/svcs -H -o FMRI,STATE -s FMRI" + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() for line in lines: comps = line.split() if not comps: continue - if 'online' in line: + if "online" in line: ret.add(comps[0]) return sorted(ret) def get_stopped(): - ''' + """ Return the stopped services CLI Example: @@ -82,21 +87,21 @@ def get_stopped(): .. code-block:: bash salt '*' service.get_stopped - ''' + """ ret = set() - cmd = '/usr/bin/svcs -aH -o FMRI,STATE -s FMRI' - lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + cmd = "/usr/bin/svcs -aH -o FMRI,STATE -s FMRI" + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() for line in lines: comps = line.split() if not comps: continue - if 'online' not in line and 'legacy_run' not in line: + if "online" not in line and "legacy_run" not in line: ret.add(comps[0]) return sorted(ret) def available(name): - ''' + """ Returns ``True`` if the specified service is available, otherwise returns ``False``. @@ -108,14 +113,14 @@ def available(name): .. code-block:: bash salt '*' service.available net-snmp - ''' - cmd = '/usr/bin/svcs -H -o FMRI {0}'.format(name) - name = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = "/usr/bin/svcs -H -o FMRI {0}".format(name) + name = __salt__["cmd.run"](cmd, python_shell=False) return name in get_all() def missing(name): - ''' + """ The inverse of service.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -125,14 +130,14 @@ def missing(name): .. code-block:: bash salt '*' service.missing net-snmp - ''' - cmd = '/usr/bin/svcs -H -o FMRI {0}'.format(name) - name = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = "/usr/bin/svcs -H -o FMRI {0}".format(name) + name = __salt__["cmd.run"](cmd, python_shell=False) return name not in get_all() def get_all(): - ''' + """ Return all installed services CLI Example: @@ -140,10 +145,10 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' + """ ret = set() - cmd = '/usr/bin/svcs -aH -o FMRI,STATE -s FMRI' - lines = __salt__['cmd.run'](cmd).splitlines() + cmd = "/usr/bin/svcs -aH -o FMRI,STATE -s FMRI" + lines = __salt__["cmd.run"](cmd).splitlines() for line in lines: comps = line.split() if not comps: @@ -153,7 +158,7 @@ def get_all(): def start(name): - ''' + """ Start the specified service CLI Example: @@ -161,23 +166,23 @@ def start(name): .. code-block:: bash salt '*' service.start - ''' - cmd = '/usr/sbin/svcadm enable -s -t {0}'.format(name) - retcode = __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "/usr/sbin/svcadm enable -s -t {0}".format(name) + retcode = __salt__["cmd.retcode"](cmd, python_shell=False) if not retcode: return True if retcode == 3: # Return code 3 means there was a problem with the service # A common case is being in the 'maintenance' state # Attempt a clear and try one more time - clear_cmd = '/usr/sbin/svcadm clear {0}'.format(name) - __salt__['cmd.retcode'](clear_cmd, python_shell=False) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + clear_cmd = "/usr/sbin/svcadm clear {0}".format(name) + __salt__["cmd.retcode"](clear_cmd, python_shell=False) + return not __salt__["cmd.retcode"](cmd, python_shell=False) return False def stop(name): - ''' + """ Stop the specified service CLI Example: @@ -185,13 +190,13 @@ def stop(name): .. code-block:: bash salt '*' service.stop - ''' - cmd = '/usr/sbin/svcadm disable -s -t {0}'.format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "/usr/sbin/svcadm disable -s -t {0}".format(name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def restart(name): - ''' + """ Restart the named service CLI Example: @@ -199,9 +204,9 @@ def restart(name): .. code-block:: bash salt '*' service.restart - ''' - cmd = '/usr/sbin/svcadm restart {0}'.format(name) - if not __salt__['cmd.retcode'](cmd, python_shell=False): + """ + cmd = "/usr/sbin/svcadm restart {0}".format(name) + if not __salt__["cmd.retcode"](cmd, python_shell=False): # calling restart doesn't clear maintenance # or tell us that the service is in the 'online' state return start(name) @@ -209,7 +214,7 @@ def restart(name): def reload_(name): - ''' + """ Reload the named service CLI Example: @@ -217,9 +222,9 @@ def reload_(name): .. code-block:: bash salt '*' service.reload - ''' - cmd = '/usr/sbin/svcadm refresh {0}'.format(name) - if not __salt__['cmd.retcode'](cmd, python_shell=False): + """ + cmd = "/usr/sbin/svcadm refresh {0}".format(name) + if not __salt__["cmd.retcode"](cmd, python_shell=False): # calling reload doesn't clear maintenance # or tell us that the service is in the 'online' state return start(name) @@ -227,7 +232,7 @@ def reload_(name): def status(name, sig=None): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -248,24 +253,24 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status - ''' - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + """ + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: services = [name] results = {} for service in services: - cmd = '/usr/bin/svcs -H -o STATE {0}'.format(service) - line = __salt__['cmd.run'](cmd, python_shell=False) - results[service] = line == 'online' + cmd = "/usr/bin/svcs -H -o STATE {0}".format(service) + line = __salt__["cmd.run"](cmd, python_shell=False) + results[service] = line == "online" if contains_globbing: return results return results[name] def enable(name, **kwargs): - ''' + """ Enable the named service to start at boot CLI Example: @@ -273,13 +278,13 @@ def enable(name, **kwargs): .. code-block:: bash salt '*' service.enable - ''' - cmd = '/usr/sbin/svcadm enable {0}'.format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "/usr/sbin/svcadm enable {0}".format(name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def disable(name, **kwargs): - ''' + """ Disable the named service to start at boot CLI Example: @@ -287,13 +292,13 @@ def disable(name, **kwargs): .. code-block:: bash salt '*' service.disable - ''' - cmd = '/usr/sbin/svcadm disable {0}'.format(name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = "/usr/sbin/svcadm disable {0}".format(name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def enabled(name, **kwargs): - ''' + """ Check to see if the named service is enabled to start on boot CLI Example: @@ -301,22 +306,22 @@ def enabled(name, **kwargs): .. code-block:: bash salt '*' service.enabled - ''' + """ # The property that reveals whether a service is enabled # can only be queried using the full FMRI # We extract the FMRI and then do the query - fmri_cmd = '/usr/bin/svcs -H -o FMRI {0}'.format(name) - fmri = __salt__['cmd.run'](fmri_cmd, python_shell=False) - cmd = '/usr/sbin/svccfg -s {0} listprop general/enabled'.format(fmri) - comps = __salt__['cmd.run'](cmd, python_shell=False).split() - if comps[2] == 'true': + fmri_cmd = "/usr/bin/svcs -H -o FMRI {0}".format(name) + fmri = __salt__["cmd.run"](fmri_cmd, python_shell=False) + cmd = "/usr/sbin/svccfg -s {0} listprop general/enabled".format(fmri) + comps = __salt__["cmd.run"](cmd, python_shell=False).split() + if comps[2] == "true": return True else: return False def disabled(name): - ''' + """ Check to see if the named service is disabled to start on boot CLI Example: @@ -324,12 +329,12 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled - ''' + """ return not enabled(name) def get_enabled(): - ''' + """ Return the enabled services CLI Example: @@ -337,13 +342,13 @@ def get_enabled(): .. code-block:: bash salt '*' service.get_enabled - ''' + """ # Note that this returns the full FMRI return _get_enabled_disabled("true") def get_disabled(): - ''' + """ Return the disabled services CLI Example: @@ -351,6 +356,6 @@ def get_disabled(): .. code-block:: bash salt '*' service.get_disabled - ''' + """ # Note that this returns the full FMRI return _get_enabled_disabled("false") diff --git a/salt/modules/smtp.py b/salt/modules/smtp.py index 0da29acbf9e..be5369126f8 100644 --- a/salt/modules/smtp.py +++ b/salt/modules/smtp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for Sending Messages via SMTP .. versionadded:: 2014.7.0 @@ -39,9 +39,10 @@ Module for Sending Messages via SMTP smtp.username: myuser smtp.password: verybadpass -''' +""" + +from __future__ import absolute_import, print_function, unicode_literals -from __future__ import absolute_import, unicode_literals, print_function import logging import os import socket @@ -57,34 +58,37 @@ try: import email.mime.text import email.mime.application import email.mime.multipart + HAS_LIBS = True except ImportError: pass -__virtualname__ = 'smtp' +__virtualname__ = "smtp" def __virtual__(): - ''' + """ Only load this module if smtplib is available on this minion. - ''' + """ if HAS_LIBS: return __virtualname__ - return (False, 'This module is only loaded if smtplib is available') + return (False, "This module is only loaded if smtplib is available") -def send_msg(recipient, - message, - subject='Message from Salt', - sender=None, - server=None, - use_ssl='True', - username=None, - password=None, - profile=None, - attachments=None): - ''' +def send_msg( + recipient, + message, + subject="Message from Salt", + sender=None, + server=None, + use_ssl="True", + username=None, + password=None, + profile=None, + attachments=None, +): + """ Send a message to an SMTP recipient. Designed for use in states. CLI Examples: @@ -94,27 +98,27 @@ def send_msg(recipient, salt '*' smtp.send_msg 'admin@example.com' 'This is a salt module test' profile='my-smtp-account' salt '*' smtp.send_msg 'admin@example.com' 'This is a salt module test' username='myuser' password='verybadpass' sender='admin@example.com' server='smtp.domain.com' salt '*' smtp.send_msg 'admin@example.com' 'This is a salt module test' username='myuser' password='verybadpass' sender='admin@example.com' server='smtp.domain.com' attachments="['/var/log/messages']" - ''' + """ if profile: - creds = __salt__['config.option'](profile) - server = creds.get('smtp.server') - use_ssl = creds.get('smtp.tls') - sender = creds.get('smtp.sender') - username = creds.get('smtp.username') - password = creds.get('smtp.password') + creds = __salt__["config.option"](profile) + server = creds.get("smtp.server") + use_ssl = creds.get("smtp.tls") + sender = creds.get("smtp.sender") + username = creds.get("smtp.username") + password = creds.get("smtp.password") if attachments: msg = email.mime.multipart.MIMEMultipart() msg.attach(email.mime.text.MIMEText(message)) else: msg = email.mime.text.MIMEText(message) - msg['Subject'] = subject - msg['From'] = sender - msg['To'] = recipient - recipients = [r.strip() for r in recipient.split(',')] + msg["Subject"] = subject + msg["From"] = sender + msg["To"] = recipient + recipients = [r.strip() for r in recipient.split(",")] try: - if use_ssl in ['True', 'true']: + if use_ssl in ["True", "true"]: smtpconn = smtplib.SMTP_SSL(server) else: smtpconn = smtplib.SMTP(server) @@ -123,21 +127,25 @@ def send_msg(recipient, log.debug("Exception: %s", _error) return False - if use_ssl not in ('True', 'true'): + if use_ssl not in ("True", "true"): smtpconn.ehlo() - if smtpconn.has_extn('STARTTLS'): + if smtpconn.has_extn("STARTTLS"): try: smtpconn.starttls() except smtplib.SMTPHeloError: - log.debug("The server didn’t reply properly \ - to the HELO greeting.") + log.debug( + "The server didn’t reply properly \ + to the HELO greeting." + ) return False except smtplib.SMTPException: log.debug("The server does not support the STARTTLS extension.") return False except RuntimeError: - log.debug("SSL/TLS support is not available \ - to your Python interpreter.") + log.debug( + "SSL/TLS support is not available \ + to your Python interpreter." + ) return False smtpconn.ehlo() @@ -151,9 +159,9 @@ def send_msg(recipient, if attachments: for f in attachments: name = os.path.basename(f) - with salt.utils.files.fopen(f, 'rb') as fin: + with salt.utils.files.fopen(f, "rb") as fin: att = email.mime.application.MIMEApplication(fin.read(), Name=name) - att['Content-Disposition'] = 'attachment; filename="{0}"'.format(name) + att["Content-Disposition"] = 'attachment; filename="{0}"'.format(name) msg.attach(att) try: diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py index db1b79cf49f..1df3ce9368e 100644 --- a/salt/modules/snapper.py +++ b/salt/modules/snapper.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module to manage filesystem snapshots with snapper .. versionadded:: 2016.11.0 @@ -11,28 +11,32 @@ Module to manage filesystem snapshots with snapper :depends: ``snapper`` http://snapper.io, available in most distros :maturity: new :platform: Linux -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals +import difflib import logging import os import time -import difflib -try: - from pwd import getpwuid - HAS_PWD = True -except ImportError: - HAS_PWD = False -from salt.exceptions import CommandExecutionError import salt.utils.files +from salt.exceptions import CommandExecutionError # import 3rd party libs from salt.ext import six +try: + from pwd import getpwuid + + HAS_PWD = True +except ImportError: + HAS_PWD = False + + try: import dbus # pylint: disable=wrong-import-order + HAS_DBUS = True except ImportError: HAS_DBUS = False @@ -50,9 +54,9 @@ DBUS_STATUS_MAP = { 256: "ACL info changed", } -SNAPPER_DBUS_OBJECT = 'org.opensuse.Snapper' -SNAPPER_DBUS_PATH = '/org/opensuse/Snapper' -SNAPPER_DBUS_INTERFACE = 'org.opensuse.Snapper' +SNAPPER_DBUS_OBJECT = "org.opensuse.Snapper" +SNAPPER_DBUS_PATH = "/org/opensuse/Snapper" +SNAPPER_DBUS_INTERFACE = "org.opensuse.Snapper" # pylint: disable=invalid-name log = logging.getLogger(__name__) @@ -66,38 +70,37 @@ if HAS_DBUS: try: bus = dbus.SystemBus() except dbus.DBusException as exc: - log.warning(exc) system_bus_error = exc else: if SNAPPER_DBUS_OBJECT in bus.list_activatable_names(): try: - snapper = dbus.Interface(bus.get_object(SNAPPER_DBUS_OBJECT, - SNAPPER_DBUS_PATH), - dbus_interface=SNAPPER_DBUS_INTERFACE) + snapper = dbus.Interface( + bus.get_object(SNAPPER_DBUS_OBJECT, SNAPPER_DBUS_PATH), + dbus_interface=SNAPPER_DBUS_INTERFACE, + ) except (dbus.DBusException, ValueError) as exc: - log.warning(exc) snapper_error = exc else: - snapper_error = 'snapper is missing' + snapper_error = "snapper is missing" # pylint: enable=invalid-name def __virtual__(): - error_msg = 'The snapper module cannot be loaded: {0}' + error_msg = "The snapper module cannot be loaded: {0}" if not HAS_DBUS: - return False, error_msg.format('missing python dbus module') + return False, error_msg.format("missing python dbus module") elif not snapper: return False, error_msg.format(snapper_error) elif not bus: return False, error_msg.format(system_bus_error) elif not HAS_PWD: - return False, error_msg.format('pwd module not available') + return False, error_msg.format("pwd module not available") - return 'snapper' + return "snapper" def _snapshot_to_data(snapshot): - ''' + """ Returns snapshot data from a D-Bus response. A snapshot D-Bus response is a dbus.Struct containing the @@ -114,45 +117,45 @@ def _snapshot_to_data(snapshot): description: dbus.String cleaup_algorithm: dbus.String userdata: dbus.Dictionary - ''' + """ data = {} - data['id'] = snapshot[0] - data['type'] = ['single', 'pre', 'post'][snapshot[1]] - if data['type'] == 'post': - data['pre'] = snapshot[2] + data["id"] = snapshot[0] + data["type"] = ["single", "pre", "post"][snapshot[1]] + if data["type"] == "post": + data["pre"] = snapshot[2] if snapshot[3] != -1: - data['timestamp'] = snapshot[3] + data["timestamp"] = snapshot[3] else: - data['timestamp'] = int(time.time()) + data["timestamp"] = int(time.time()) - data['user'] = getpwuid(snapshot[4])[0] - data['description'] = snapshot[5] - data['cleanup'] = snapshot[6] + data["user"] = getpwuid(snapshot[4])[0] + data["description"] = snapshot[5] + data["cleanup"] = snapshot[6] - data['userdata'] = {} + data["userdata"] = {} for key, value in snapshot[7].items(): - data['userdata'][key] = value + data["userdata"][key] = value return data def _dbus_exception_to_reason(exc, args): - ''' + """ Returns a error message from a snapper DBusException - ''' + """ error = exc.get_dbus_name() - if error == 'error.unknown_config': - return "Unknown configuration '{0}'".format(args['config']) - elif error == 'error.illegal_snapshot': - return 'Invalid snapshot' + if error == "error.unknown_config": + return "Unknown configuration '{0}'".format(args["config"]) + elif error == "error.illegal_snapshot": + return "Invalid snapshot" else: return exc.get_dbus_name() -def list_snapshots(config='root'): - ''' +def list_snapshots(config="root"): + """ List available snapshots CLI example: @@ -160,19 +163,20 @@ def list_snapshots(config='root'): .. code-block:: bash salt '*' snapper.list_snapshots config=myconfig - ''' + """ try: snapshots = snapper.ListSnapshots(config) return [_snapshot_to_data(s) for s in snapshots] except dbus.DBusException as exc: raise CommandExecutionError( - 'Error encountered while listing snapshots: {0}' - .format(_dbus_exception_to_reason(exc, locals())) + "Error encountered while listing snapshots: {0}".format( + _dbus_exception_to_reason(exc, locals()) + ) ) -def get_snapshot(number=0, config='root'): - ''' +def get_snapshot(number=0, config="root"): + """ Get detailed information about a given snapshot CLI example: @@ -180,19 +184,20 @@ def get_snapshot(number=0, config='root'): .. code-block:: bash salt '*' snapper.get_snapshot 1 - ''' + """ try: snapshot = snapper.GetSnapshot(config, int(number)) return _snapshot_to_data(snapshot) except dbus.DBusException as exc: raise CommandExecutionError( - 'Error encountered while retrieving snapshot: {0}' - .format(_dbus_exception_to_reason(exc, locals())) + "Error encountered while retrieving snapshot: {0}".format( + _dbus_exception_to_reason(exc, locals()) + ) ) def list_configs(): - ''' + """ List all available configs CLI example: @@ -200,25 +205,26 @@ def list_configs(): .. code-block:: bash salt '*' snapper.list_configs - ''' + """ try: configs = snapper.ListConfigs() return dict((config[0], config[2]) for config in configs) except dbus.DBusException as exc: raise CommandExecutionError( - 'Error encountered while listing configurations: {0}' - .format(_dbus_exception_to_reason(exc, locals())) + "Error encountered while listing configurations: {0}".format( + _dbus_exception_to_reason(exc, locals()) + ) ) def _config_filter(value): if isinstance(value, bool): - return 'yes' if value else 'no' + return "yes" if value else "no" return value -def set_config(name='root', **kwargs): - ''' +def set_config(name="root", **kwargs): + """ Set configuration values CLI example: @@ -233,29 +239,33 @@ def set_config(name='root', **kwargs): .. code-block:: bash salt '*' snapper.set_config sync_acl=True - ''' + """ try: - data = dict((k.upper(), _config_filter(v)) for k, v in - kwargs.items() if not k.startswith('__')) + data = dict( + (k.upper(), _config_filter(v)) + for k, v in kwargs.items() + if not k.startswith("__") + ) snapper.SetConfig(name, data) except dbus.DBusException as exc: raise CommandExecutionError( - 'Error encountered while setting configuration {0}: {1}' - .format(name, _dbus_exception_to_reason(exc, locals())) + "Error encountered while setting configuration {0}: {1}".format( + name, _dbus_exception_to_reason(exc, locals()) + ) ) return True -def _get_last_snapshot(config='root'): - ''' +def _get_last_snapshot(config="root"): + """ Returns the last existing created snapshot - ''' - snapshot_list = sorted(list_snapshots(config), key=lambda x: x['id']) + """ + snapshot_list = sorted(list_snapshots(config), key=lambda x: x["id"]) return snapshot_list[-1] def status_to_string(dbus_status): - ''' + """ Converts a numeric dbus snapper status into a string CLI Example: @@ -263,18 +273,24 @@ def status_to_string(dbus_status): .. code-block:: bash salt '*' snapper.status_to_string - ''' + """ status_tuple = ( - dbus_status & 0b000000001, dbus_status & 0b000000010, dbus_status & 0b000000100, - dbus_status & 0b000001000, dbus_status & 0b000010000, dbus_status & 0b000100000, - dbus_status & 0b001000000, dbus_status & 0b010000000, dbus_status & 0b100000000 + dbus_status & 0b000000001, + dbus_status & 0b000000010, + dbus_status & 0b000000100, + dbus_status & 0b000001000, + dbus_status & 0b000010000, + dbus_status & 0b000100000, + dbus_status & 0b001000000, + dbus_status & 0b010000000, + dbus_status & 0b100000000, ) return [DBUS_STATUS_MAP[status] for status in status_tuple if status] -def get_config(name='root'): - ''' +def get_config(name="root"): + """ Retrieves all values from a given configuration CLI example: @@ -282,23 +298,22 @@ def get_config(name='root'): .. code-block:: bash salt '*' snapper.get_config - ''' + """ try: config = snapper.GetConfig(name) return config except dbus.DBusException as exc: raise CommandExecutionError( - 'Error encountered while retrieving configuration: {0}' - .format(_dbus_exception_to_reason(exc, locals())) + "Error encountered while retrieving configuration: {0}".format( + _dbus_exception_to_reason(exc, locals()) + ) ) -def create_config(name=None, - subvolume=None, - fstype=None, - template=None, - extra_opts=None): - ''' +def create_config( + name=None, subvolume=None, fstype=None, template=None, extra_opts=None +): + """ Creates a new Snapper configuration name @@ -320,7 +335,8 @@ def create_config(name=None, salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs template="default" salt '*' snapper.create_config name=myconfig subvolume=/foo/bar/ fstype=btrfs extra_opts='{"NUMBER_CLEANUP": False}' - ''' + """ + def raise_arg_error(argname): raise CommandExecutionError( 'You must provide a "{0}" for the new configuration'.format(argname) @@ -342,15 +358,22 @@ def create_config(name=None, return get_config(name) except dbus.DBusException as exc: raise CommandExecutionError( - 'Error encountered while creating the new configuration: {0}' - .format(_dbus_exception_to_reason(exc, locals())) + "Error encountered while creating the new configuration: {0}".format( + _dbus_exception_to_reason(exc, locals()) + ) ) -def create_snapshot(config='root', snapshot_type='single', pre_number=None, - description=None, cleanup_algorithm='number', userdata=None, - **kwargs): - ''' +def create_snapshot( + config="root", + snapshot_type="single", + pre_number=None, + description=None, + cleanup_algorithm="number", + userdata=None, + **kwargs +): + """ Creates an snapshot config @@ -384,45 +407,51 @@ def create_snapshot(config='root', snapshot_type='single', pre_number=None, .. code-block:: bash salt '*' snapper.create_snapshot - ''' + """ if not userdata: userdata = {} - jid = kwargs.get('__pub_jid') + jid = kwargs.get("__pub_jid") if description is None and jid is not None: - description = 'salt job {0}'.format(jid) + description = "salt job {0}".format(jid) if jid is not None: - userdata['salt_jid'] = jid + userdata["salt_jid"] = jid new_nr = None try: - if snapshot_type == 'single': - new_nr = snapper.CreateSingleSnapshot(config, description, - cleanup_algorithm, userdata) - elif snapshot_type == 'pre': - new_nr = snapper.CreatePreSnapshot(config, description, - cleanup_algorithm, userdata) - elif snapshot_type == 'post': + if snapshot_type == "single": + new_nr = snapper.CreateSingleSnapshot( + config, description, cleanup_algorithm, userdata + ) + elif snapshot_type == "pre": + new_nr = snapper.CreatePreSnapshot( + config, description, cleanup_algorithm, userdata + ) + elif snapshot_type == "post": if pre_number is None: raise CommandExecutionError( "pre snapshot number 'pre_number' needs to be" - "specified for snapshots of the 'post' type") - new_nr = snapper.CreatePostSnapshot(config, pre_number, description, - cleanup_algorithm, userdata) + "specified for snapshots of the 'post' type" + ) + new_nr = snapper.CreatePostSnapshot( + config, pre_number, description, cleanup_algorithm, userdata + ) else: raise CommandExecutionError( - "Invalid snapshot type '{0}'".format(snapshot_type)) + "Invalid snapshot type '{0}'".format(snapshot_type) + ) except dbus.DBusException as exc: raise CommandExecutionError( - 'Error encountered while listing changed files: {0}' - .format(_dbus_exception_to_reason(exc, locals())) + "Error encountered while listing changed files: {0}".format( + _dbus_exception_to_reason(exc, locals()) + ) ) return new_nr def delete_snapshot(snapshots_ids=None, config="root"): - ''' + """ Deletes an snapshot config @@ -438,18 +467,25 @@ def delete_snapshot(snapshots_ids=None, config="root"): salt '*' snapper.delete_snapshot 54 salt '*' snapper.delete_snapshot config=root 54 salt '*' snapper.delete_snapshot config=root snapshots_ids=[54,55,56] - ''' + """ if not snapshots_ids: - raise CommandExecutionError('Error: No snapshot ID has been provided') + raise CommandExecutionError("Error: No snapshot ID has been provided") try: - current_snapshots_ids = [x['id'] for x in list_snapshots(config)] + current_snapshots_ids = [x["id"] for x in list_snapshots(config)] if not isinstance(snapshots_ids, list): snapshots_ids = [snapshots_ids] if not set(snapshots_ids).issubset(set(current_snapshots_ids)): raise CommandExecutionError( - "Error: Snapshots '{0}' not found".format(", ".join( - [six.text_type(x) for x in set(snapshots_ids).difference( - set(current_snapshots_ids))])) + "Error: Snapshots '{0}' not found".format( + ", ".join( + [ + six.text_type(x) + for x in set(snapshots_ids).difference( + set(current_snapshots_ids) + ) + ] + ) + ) ) snapper.DeleteSnapshots(config, snapshots_ids) return {config: {"ids": snapshots_ids, "status": "deleted"}} @@ -457,12 +493,10 @@ def delete_snapshot(snapshots_ids=None, config="root"): raise CommandExecutionError(_dbus_exception_to_reason(exc, locals())) -def modify_snapshot(snapshot_id=None, - description=None, - userdata=None, - cleanup=None, - config="root"): - ''' +def modify_snapshot( + snapshot_id=None, description=None, userdata=None, cleanup=None, config="root" +): + """ Modify attributes of an existing snapshot. config @@ -488,47 +522,51 @@ def modify_snapshot(snapshot_id=None, salt '*' snapper.modify_snapshot 54 description="my snapshot description" salt '*' snapper.modify_snapshot 54 userdata='{"foo": "bar"}' salt '*' snapper.modify_snapshot snapshot_id=54 cleanup="number" - ''' + """ if not snapshot_id: - raise CommandExecutionError('Error: No snapshot ID has been provided') + raise CommandExecutionError("Error: No snapshot ID has been provided") snapshot = get_snapshot(config=config, number=snapshot_id) try: # Updating only the explicitly provided attributes by the user updated_opts = { - 'description': description if description is not None else snapshot['description'], - 'cleanup': cleanup if cleanup is not None else snapshot['cleanup'], - 'userdata': userdata if userdata is not None else snapshot['userdata'], + "description": description + if description is not None + else snapshot["description"], + "cleanup": cleanup if cleanup is not None else snapshot["cleanup"], + "userdata": userdata if userdata is not None else snapshot["userdata"], } - snapper.SetSnapshot(config, - snapshot_id, - updated_opts['description'], - updated_opts['cleanup'], - updated_opts['userdata']) + snapper.SetSnapshot( + config, + snapshot_id, + updated_opts["description"], + updated_opts["cleanup"], + updated_opts["userdata"], + ) return get_snapshot(config=config, number=snapshot_id) except dbus.DBusException as exc: raise CommandExecutionError(_dbus_exception_to_reason(exc, locals())) def _get_num_interval(config, num_pre, num_post): - ''' + """ Returns numerical interval based on optionals num_pre, num_post values - ''' + """ post = int(num_post) if num_post else 0 - pre = int(num_pre) if num_pre is not None else _get_last_snapshot(config)['id'] + pre = int(num_pre) if num_pre is not None else _get_last_snapshot(config)["id"] return pre, post def _is_text_file(filename): - ''' + """ Checks if a file is a text file - ''' - type_of_file = os.popen('file -bi {0}'.format(filename), 'r').read() - return type_of_file.startswith('text') + """ + type_of_file = os.popen("file -bi {0}".format(filename), "r").read() + return type_of_file.startswith("text") def run(function, *args, **kwargs): - ''' + """ Runs a function from an execution module creating pre and post snapshots and associating the salt job id with those snapshots for easy undo and cleanup. @@ -566,46 +604,46 @@ def run(function, *args, **kwargs): .. code-block:: bash salt '*' snapper.run file.append args='["/etc/motd", "some text"]' - ''' + """ config = kwargs.pop("config", "root") description = kwargs.pop("description", "snapper.run[{0}]".format(function)) cleanup_algorithm = kwargs.pop("cleanup_algorithm", "number") userdata = kwargs.pop("userdata", {}) - func_kwargs = dict((k, v) for k, v in kwargs.items() if not k.startswith('__')) - kwargs = dict((k, v) for k, v in kwargs.items() if k.startswith('__')) + func_kwargs = dict((k, v) for k, v in kwargs.items() if not k.startswith("__")) + kwargs = dict((k, v) for k, v in kwargs.items() if k.startswith("__")) - pre_nr = __salt__['snapper.create_snapshot']( + pre_nr = __salt__["snapper.create_snapshot"]( config=config, - snapshot_type='pre', + snapshot_type="pre", description=description, cleanup_algorithm=cleanup_algorithm, userdata=userdata, - **kwargs) + **kwargs + ) if function not in __salt__: - raise CommandExecutionError( - 'function "{0}" does not exist'.format(function) - ) + raise CommandExecutionError('function "{0}" does not exist'.format(function)) try: ret = __salt__[function](*args, **func_kwargs) except CommandExecutionError as exc: ret = "\n".join([six.text_type(exc), __salt__[function].__doc__]) - __salt__['snapper.create_snapshot']( + __salt__["snapper.create_snapshot"]( config=config, - snapshot_type='post', + snapshot_type="post", pre_number=pre_nr, description=description, cleanup_algorithm=cleanup_algorithm, userdata=userdata, - **kwargs) + **kwargs + ) return ret -def status(config='root', num_pre=None, num_post=None): - ''' +def status(config="root", num_pre=None, num_post=None): + """ Returns a comparison between two snapshots config @@ -623,28 +661,33 @@ def status(config='root', num_pre=None, num_post=None): salt '*' snapper.status salt '*' snapper.status num_pre=19 num_post=20 - ''' + """ try: pre, post = _get_num_interval(config, num_pre, num_post) snapper.CreateComparison(config, int(pre), int(post)) files = snapper.GetFiles(config, int(pre), int(post)) status_ret = {} - subvolume = list_configs()[config]['SUBVOLUME'] + subvolume = list_configs()[config]["SUBVOLUME"] for file in files: # In case of SUBVOLUME is included in filepath we remove it # to prevent from filepath starting with double '/' - _filepath = file[0][len(subvolume):] if file[0].startswith(subvolume) else file[0] - status_ret[os.path.normpath(subvolume + _filepath)] = {'status': status_to_string(file[1])} + _filepath = ( + file[0][len(subvolume) :] if file[0].startswith(subvolume) else file[0] + ) + status_ret[os.path.normpath(subvolume + _filepath)] = { + "status": status_to_string(file[1]) + } return status_ret except dbus.DBusException as exc: raise CommandExecutionError( - 'Error encountered while listing changed files: {0}' - .format(_dbus_exception_to_reason(exc, locals())) + "Error encountered while listing changed files: {0}".format( + _dbus_exception_to_reason(exc, locals()) + ) ) -def changed_files(config='root', num_pre=None, num_post=None): - ''' +def changed_files(config="root", num_pre=None, num_post=None): + """ Returns the files changed between two snapshots config @@ -662,12 +705,12 @@ def changed_files(config='root', num_pre=None, num_post=None): salt '*' snapper.changed_files salt '*' snapper.changed_files num_pre=19 num_post=20 - ''' + """ return status(config, num_pre, num_post).keys() -def undo(config='root', files=None, num_pre=None, num_post=None): - ''' +def undo(config="root", files=None, num_pre=None, num_post=None): + """ Undo all file changes that happened between num_pre and num_post, leaving the files into the state of num_pre. @@ -684,7 +727,7 @@ def undo(config='root', files=None, num_pre=None, num_post=None): .. code-block:: bash salt '*' snapper.undo - ''' + """ pre, post = _get_num_interval(config, num_pre, num_post) changes = status(config, pre, post) @@ -693,46 +736,50 @@ def undo(config='root', files=None, num_pre=None, num_post=None): if not requested.issubset(changed): raise CommandExecutionError( - 'Given file list contains files that are not present' - 'in the changed filelist: {0}'.format(changed - requested)) + "Given file list contains files that are not present" + "in the changed filelist: {0}".format(changed - requested) + ) - cmdret = __salt__['cmd.run']('snapper -c {0} undochange {1}..{2} {3}'.format( - config, pre, post, ' '.join(requested))) + cmdret = __salt__["cmd.run"]( + "snapper -c {0} undochange {1}..{2} {3}".format( + config, pre, post, " ".join(requested) + ) + ) try: - components = cmdret.split(' ') + components = cmdret.split(" ") ret = {} for comp in components: - key, val = comp.split(':') + key, val = comp.split(":") ret[key] = val return ret except ValueError as exc: raise CommandExecutionError( - 'Error while processing Snapper response: {0}'.format(cmdret)) + "Error while processing Snapper response: {0}".format(cmdret) + ) -def _get_jid_snapshots(jid, config='root'): - ''' +def _get_jid_snapshots(jid, config="root"): + """ Returns pre/post snapshots made by a given Salt jid Looks for 'salt_jid' entries into snapshots userdata which are created when 'snapper.run' is executed. - ''' - jid_snapshots = [x for x in list_snapshots(config) if x['userdata'].get("salt_jid") == jid] - pre_snapshot = [x for x in jid_snapshots if x['type'] == "pre"] - post_snapshot = [x for x in jid_snapshots if x['type'] == "post"] + """ + jid_snapshots = [ + x for x in list_snapshots(config) if x["userdata"].get("salt_jid") == jid + ] + pre_snapshot = [x for x in jid_snapshots if x["type"] == "pre"] + post_snapshot = [x for x in jid_snapshots if x["type"] == "post"] if not pre_snapshot or not post_snapshot: raise CommandExecutionError("Jid '{0}' snapshots not found".format(jid)) - return ( - pre_snapshot[0]['id'], - post_snapshot[0]['id'] - ) + return (pre_snapshot[0]["id"], post_snapshot[0]["id"]) -def undo_jid(jid, config='root'): - ''' +def undo_jid(jid, config="root"): + """ Undo the changes applied by a salt job jid @@ -746,13 +793,13 @@ def undo_jid(jid, config='root'): .. code-block:: bash salt '*' snapper.undo_jid jid=20160607130930720112 - ''' + """ pre_snapshot, post_snapshot = _get_jid_snapshots(jid, config=config) return undo(config, num_pre=pre_snapshot, num_post=post_snapshot) -def diff(config='root', filename=None, num_pre=None, num_post=None): - ''' +def diff(config="root", filename=None, num_pre=None, num_post=None): + """ Returns the differences between two snapshots config @@ -774,7 +821,7 @@ def diff(config='root', filename=None, num_pre=None, num_post=None): salt '*' snapper.diff salt '*' snapper.diff filename=/var/log/snapper.log num_pre=19 num_post=20 - ''' + """ try: pre, post = _get_num_interval(config, num_pre, num_post) @@ -782,7 +829,7 @@ def diff(config='root', filename=None, num_pre=None, num_post=None): if filename: files = [filename] if filename in files else [] - subvolume = list_configs()[config]['SUBVOLUME'] + subvolume = list_configs()[config]["SUBVOLUME"] pre_mount = snapper.MountSnapshot(config, pre, False) if pre else subvolume post_mount = snapper.MountSnapshot(config, post, False) if post else subvolume @@ -791,17 +838,20 @@ def diff(config='root', filename=None, num_pre=None, num_post=None): _filepath = filepath if filepath.startswith(subvolume): - _filepath = filepath[len(subvolume):] + _filepath = filepath[len(subvolume) :] # Just in case, removing possible double '/' from the final file paths pre_file = os.path.normpath(pre_mount + "/" + _filepath).replace("//", "/") - post_file = os.path.normpath(post_mount + "/" + _filepath).replace("//", "/") + post_file = os.path.normpath(post_mount + "/" + _filepath).replace( + "//", "/" + ) if os.path.isfile(pre_file): pre_file_exists = True with salt.utils.files.fopen(pre_file) as rfh: - pre_file_content = [salt.utils.stringutils.to_unicode(_l) - for _l in rfh.readlines()] + pre_file_content = [ + salt.utils.stringutils.to_unicode(_l) for _l in rfh.readlines() + ] else: pre_file_content = [] pre_file_exists = False @@ -809,36 +859,46 @@ def diff(config='root', filename=None, num_pre=None, num_post=None): if os.path.isfile(post_file): post_file_exists = True with salt.utils.files.fopen(post_file) as rfh: - post_file_content = [salt.utils.stringutils.to_unicode(_l) - for _l in rfh.readlines()] + post_file_content = [ + salt.utils.stringutils.to_unicode(_l) for _l in rfh.readlines() + ] else: post_file_content = [] post_file_exists = False if _is_text_file(pre_file) or _is_text_file(post_file): files_diff[filepath] = { - 'comment': "text file changed", - 'diff': ''.join(difflib.unified_diff(pre_file_content, - post_file_content, - fromfile=pre_file, - tofile=post_file))} + "comment": "text file changed", + "diff": "".join( + difflib.unified_diff( + pre_file_content, + post_file_content, + fromfile=pre_file, + tofile=post_file, + ) + ), + } if pre_file_exists and not post_file_exists: - files_diff[filepath]['comment'] = "text file deleted" + files_diff[filepath]["comment"] = "text file deleted" if not pre_file_exists and post_file_exists: - files_diff[filepath]['comment'] = "text file created" + files_diff[filepath]["comment"] = "text file created" elif not _is_text_file(pre_file) and not _is_text_file(post_file): # This is a binary file - files_diff[filepath] = {'comment': "binary file changed"} + files_diff[filepath] = {"comment": "binary file changed"} if pre_file_exists: - files_diff[filepath]['old_sha256_digest'] = __salt__['hashutil.sha256_digest'](''.join(pre_file_content)) + files_diff[filepath]["old_sha256_digest"] = __salt__[ + "hashutil.sha256_digest" + ]("".join(pre_file_content)) if post_file_exists: - files_diff[filepath]['new_sha256_digest'] = __salt__['hashutil.sha256_digest'](''.join(post_file_content)) + files_diff[filepath]["new_sha256_digest"] = __salt__[ + "hashutil.sha256_digest" + ]("".join(post_file_content)) if post_file_exists and not pre_file_exists: - files_diff[filepath]['comment'] = "binary file created" + files_diff[filepath]["comment"] = "binary file created" if pre_file_exists and not post_file_exists: - files_diff[filepath]['comment'] = "binary file deleted" + files_diff[filepath]["comment"] = "binary file deleted" if pre: snapper.UmountSnapshot(config, pre, False) @@ -847,13 +907,14 @@ def diff(config='root', filename=None, num_pre=None, num_post=None): return files_diff except dbus.DBusException as exc: raise CommandExecutionError( - 'Error encountered while showing differences between snapshots: {0}' - .format(_dbus_exception_to_reason(exc, locals())) + "Error encountered while showing differences between snapshots: {0}".format( + _dbus_exception_to_reason(exc, locals()) + ) ) -def diff_jid(jid, config='root'): - ''' +def diff_jid(jid, config="root"): + """ Returns the changes applied by a `jid` jid @@ -867,13 +928,13 @@ def diff_jid(jid, config='root'): .. code-block:: bash salt '*' snapper.diff_jid jid=20160607130930720112 - ''' + """ pre_snapshot, post_snapshot = _get_jid_snapshots(jid, config=config) return diff(config, num_pre=pre_snapshot, num_post=post_snapshot) -def create_baseline(tag="baseline", config='root'): - ''' +def create_baseline(tag="baseline", config="root"): + """ Creates a snapshot marked as baseline tag @@ -888,9 +949,11 @@ def create_baseline(tag="baseline", config='root'): salt '*' snapper.create_baseline salt '*' snapper.create_baseline my_custom_baseline - ''' - return __salt__['snapper.create_snapshot'](config=config, - snapshot_type='single', - description="baseline snapshot", - cleanup_algorithm="number", - userdata={"baseline_tag": tag}) + """ + return __salt__["snapper.create_snapshot"]( + config=config, + snapshot_type="single", + description="baseline snapshot", + cleanup_algorithm="number", + userdata={"baseline_tag": tag}, + ) diff --git a/salt/modules/solaris_fmadm.py b/salt/modules/solaris_fmadm.py index 89c82b9c540..d403f5eb50a 100644 --- a/salt/modules/solaris_fmadm.py +++ b/salt/modules/solaris_fmadm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for running fmadm and fmdump on Solaris :maintainer: Jorge Schrauwen @@ -7,63 +7,63 @@ Module for running fmadm and fmdump on Solaris :platform: solaris,illumos .. versionadded:: 2016.3.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging +import salt.utils.decorators as decorators + # Import Salt libs import salt.utils.path import salt.utils.platform -import salt.utils.decorators as decorators from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) # Function aliases __func_alias__ = { - 'list_records': 'list', + "list_records": "list", } # Define the module's virtual name -__virtualname__ = 'fmadm' +__virtualname__ = "fmadm" @decorators.memoize def _check_fmadm(): - ''' + """ Looks to see if fmadm is present on the system - ''' - return salt.utils.path.which('fmadm') + """ + return salt.utils.path.which("fmadm") def _check_fmdump(): - ''' + """ Looks to see if fmdump is present on the system - ''' - return salt.utils.path.which('fmdump') + """ + return salt.utils.path.which("fmdump") def __virtual__(): - ''' + """ Provides fmadm only on Solaris - ''' - if salt.utils.platform.is_sunos() and \ - _check_fmadm() and _check_fmdump(): + """ + if salt.utils.platform.is_sunos() and _check_fmadm() and _check_fmdump(): return __virtualname__ return ( False, - '{0} module can only be loaded on Solaris with the fault management installed'.format( + "{0} module can only be loaded on Solaris with the fault management installed".format( __virtualname__ - ) + ), ) def _parse_fmdump(output): - ''' + """ Parses fmdump output - ''' + """ result = [] output = output.split("\n") @@ -74,7 +74,7 @@ def _parse_fmdump(output): # parse entries for entry in output: entry = [item for item in entry.split(" ") if item] - entry = ['{0} {1} {2}'.format(entry[0], entry[1], entry[2])] + entry[3:] + entry = ["{0} {1} {2}".format(entry[0], entry[1], entry[2])] + entry[3:] # prepare faults fault = OrderedDict() @@ -87,16 +87,16 @@ def _parse_fmdump(output): def _parse_fmdump_verbose(output): - ''' + """ Parses fmdump verbose output - ''' + """ result = [] output = output.split("\n") fault = [] verbose_fault = {} for line in output: - if line.startswith('TIME'): + if line.startswith("TIME"): fault.append(line) if len(verbose_fault) > 0: result.append(verbose_fault) @@ -106,14 +106,11 @@ def _parse_fmdump_verbose(output): verbose_fault = _parse_fmdump("\n".join(fault))[0] fault = [] elif len(verbose_fault) > 0: - if 'details' not in verbose_fault: - verbose_fault['details'] = "" - if line.strip() == '': + if "details" not in verbose_fault: + verbose_fault["details"] = "" + if line.strip() == "": continue - verbose_fault['details'] = '{0}{1}\n'.format( - verbose_fault['details'], - line - ) + verbose_fault["details"] = "{0}{1}\n".format(verbose_fault["details"], line) if len(verbose_fault) > 0: result.append(verbose_fault) @@ -121,9 +118,9 @@ def _parse_fmdump_verbose(output): def _parse_fmadm_config(output): - ''' + """ Parsbb fmdump/fmadm output - ''' + """ result = [] output = output.split("\n") @@ -146,8 +143,8 @@ def _parse_fmadm_config(output): # keying keyed_result = OrderedDict() for component in result: - keyed_result[component['module']] = component - del keyed_result[component['module']]['module'] + keyed_result[component["module"]] = component + del keyed_result[component["module"]]["module"] result = keyed_result @@ -155,21 +152,17 @@ def _parse_fmadm_config(output): def _fmadm_action_fmri(action, fmri): - ''' + """ Internal function for fmadm.repqired, fmadm.replaced, fmadm.flush - ''' + """ ret = {} fmadm = _check_fmadm() - cmd = '{cmd} {action} {fmri}'.format( - cmd=fmadm, - action=action, - fmri=fmri - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "{cmd} {action} {fmri}".format(cmd=fmadm, action=action, fmri=fmri) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] result = {} if retcode != 0: - result['Error'] = res['stderr'] + result["Error"] = res["stderr"] else: result = True @@ -177,17 +170,18 @@ def _fmadm_action_fmri(action, fmri): def _parse_fmadm_faulty(output): - ''' + """ Parse fmadm faulty output - ''' + """ + def _merge_data(summary, fault): result = {} - uuid = summary['event-id'] - del summary['event-id'] + uuid = summary["event-id"] + del summary["event-id"] result[uuid] = OrderedDict() - result[uuid]['summary'] = summary - result[uuid]['fault'] = fault + result[uuid]["summary"] = summary + result[uuid]["fault"] = fault return result result = {} @@ -198,7 +192,7 @@ def _parse_fmadm_faulty(output): for line in output.split("\n"): # we hit a divider - if line.startswith('-'): + if line.startswith("-"): if summary and summary_data and fault_data: # we have data, store it and reset result.update(_merge_data(summary_data, fault_data)) @@ -225,20 +219,25 @@ def _parse_fmadm_faulty(output): # if we have a header and data, assume the other lines are details if summary and summary_data: # if line starts with a whitespace and we already have a key, append - if line.startswith(' ') and data_key: + if line.startswith(" ") and data_key: fault_data[data_key] = "{0}\n{1}".format( - fault_data[data_key], - line.strip() + fault_data[data_key], line.strip() ) # we have a key : value line, parse it - elif ':' in line: - line = line.split(':') + elif ":" in line: + line = line.split(":") data_key = line[0].strip() fault_data[data_key] = ":".join(line[1:]).strip() # note: for some reason Chassis_id is lobbed ofter Platform, fix that here - if data_key == 'Platform': - fault_data['Chassis_id'] = fault_data[data_key][fault_data[data_key].index('Chassis_id'):].split(':')[-1].strip() - fault_data[data_key] = fault_data[data_key][0:fault_data[data_key].index('Chassis_id')].strip() + if data_key == "Platform": + fault_data["Chassis_id"] = ( + fault_data[data_key][fault_data[data_key].index("Chassis_id") :] + .split(":")[-1] + .strip() + ) + fault_data[data_key] = fault_data[data_key][ + 0 : fault_data[data_key].index("Chassis_id") + ].strip() # we have data, store it and reset result.update(_merge_data(summary_data, fault_data)) @@ -247,7 +246,7 @@ def _parse_fmadm_faulty(output): def list_records(after=None, before=None): - ''' + """ Display fault management logs after : string @@ -261,27 +260,27 @@ def list_records(after=None, before=None): .. code-block:: bash salt '*' fmadm.list - ''' + """ ret = {} fmdump = _check_fmdump() - cmd = '{cmd}{after}{before}'.format( + cmd = "{cmd}{after}{before}".format( cmd=fmdump, - after=' -t {0}'.format(after) if after else '', - before=' -T {0}'.format(before) if before else '' + after=" -t {0}".format(after) if after else "", + before=" -T {0}".format(before) if before else "", ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] result = {} if retcode != 0: - result['Error'] = 'error executing fmdump' + result["Error"] = "error executing fmdump" else: - result = _parse_fmdump(res['stdout']) + result = _parse_fmdump(res["stdout"]) return result def show(uuid): - ''' + """ Display log details uuid: string @@ -292,26 +291,23 @@ def show(uuid): .. code-block:: bash salt '*' fmadm.show 11b4070f-4358-62fa-9e1e-998f485977e1 - ''' + """ ret = {} fmdump = _check_fmdump() - cmd = '{cmd} -u {uuid} -V'.format( - cmd=fmdump, - uuid=uuid - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "{cmd} -u {uuid} -V".format(cmd=fmdump, uuid=uuid) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] result = {} if retcode != 0: - result['Error'] = 'error executing fmdump' + result["Error"] = "error executing fmdump" else: - result = _parse_fmdump_verbose(res['stdout']) + result = _parse_fmdump_verbose(res["stdout"]) return result def config(): - ''' + """ Display fault manager configuration CLI Example: @@ -319,25 +315,23 @@ def config(): .. code-block:: bash salt '*' fmadm.config - ''' + """ ret = {} fmadm = _check_fmadm() - cmd = '{cmd} config'.format( - cmd=fmadm - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "{cmd} config".format(cmd=fmadm) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] result = {} if retcode != 0: - result['Error'] = 'error executing fmadm config' + result["Error"] = "error executing fmadm config" else: - result = _parse_fmadm_config(res['stdout']) + result = _parse_fmadm_config(res["stdout"]) return result def load(path): - ''' + """ Load specified fault manager module path: string @@ -348,18 +342,15 @@ def load(path): .. code-block:: bash salt '*' fmadm.load /module/path - ''' + """ ret = {} fmadm = _check_fmadm() - cmd = '{cmd} load {path}'.format( - cmd=fmadm, - path=path - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "{cmd} load {path}".format(cmd=fmadm, path=path) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] result = {} if retcode != 0: - result['Error'] = res['stderr'] + result["Error"] = res["stderr"] else: result = True @@ -367,7 +358,7 @@ def load(path): def unload(module): - ''' + """ Unload specified fault manager module module: string @@ -378,18 +369,15 @@ def unload(module): .. code-block:: bash salt '*' fmadm.unload software-response - ''' + """ ret = {} fmadm = _check_fmadm() - cmd = '{cmd} unload {module}'.format( - cmd=fmadm, - module=module - ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + cmd = "{cmd} unload {module}".format(cmd=fmadm, module=module) + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] result = {} if retcode != 0: - result['Error'] = res['stderr'] + result["Error"] = res["stderr"] else: result = True @@ -397,7 +385,7 @@ def unload(module): def reset(module, serd=None): - ''' + """ Reset module or sub-component module: string @@ -410,19 +398,17 @@ def reset(module, serd=None): .. code-block:: bash salt '*' fmadm.reset software-response - ''' + """ ret = {} fmadm = _check_fmadm() - cmd = '{cmd} reset {serd}{module}'.format( - cmd=fmadm, - serd='-s {0} '.format(serd) if serd else '', - module=module + cmd = "{cmd} reset {serd}{module}".format( + cmd=fmadm, serd="-s {0} ".format(serd) if serd else "", module=module ) - res = __salt__['cmd.run_all'](cmd) - retcode = res['retcode'] + res = __salt__["cmd.run_all"](cmd) + retcode = res["retcode"] result = {} if retcode != 0: - result['Error'] = res['stderr'] + result["Error"] = res["stderr"] else: result = True @@ -430,7 +416,7 @@ def reset(module, serd=None): def flush(fmri): - ''' + """ Flush cached state for resource fmri: string @@ -441,12 +427,12 @@ def flush(fmri): .. code-block:: bash salt '*' fmadm.flush fmri - ''' - return _fmadm_action_fmri('flush', fmri) + """ + return _fmadm_action_fmri("flush", fmri) def repaired(fmri): - ''' + """ Notify fault manager that resource has been repaired fmri: string @@ -457,12 +443,12 @@ def repaired(fmri): .. code-block:: bash salt '*' fmadm.repaired fmri - ''' - return _fmadm_action_fmri('repaired', fmri) + """ + return _fmadm_action_fmri("repaired", fmri) def replaced(fmri): - ''' + """ Notify fault manager that resource has been replaced fmri: string @@ -473,12 +459,12 @@ def replaced(fmri): .. code-block:: bash salt '*' fmadm.repaired fmri - ''' - return _fmadm_action_fmri('replaced', fmri) + """ + return _fmadm_action_fmri("replaced", fmri) def acquit(fmri): - ''' + """ Acquit resource or acquit case fmri: string @@ -489,12 +475,12 @@ def acquit(fmri): .. code-block:: bash salt '*' fmadm.acquit fmri | uuid - ''' - return _fmadm_action_fmri('acquit', fmri) + """ + return _fmadm_action_fmri("acquit", fmri) def faulty(): - ''' + """ Display list of faulty resources CLI Example: @@ -502,23 +488,21 @@ def faulty(): .. code-block:: bash salt '*' fmadm.faulty - ''' + """ fmadm = _check_fmadm() - cmd = '{cmd} faulty'.format( - cmd=fmadm, - ) - res = __salt__['cmd.run_all'](cmd) + cmd = "{cmd} faulty".format(cmd=fmadm,) + res = __salt__["cmd.run_all"](cmd) result = {} - if res['stdout'] == '': + if res["stdout"] == "": result = False else: - result = _parse_fmadm_faulty(res['stdout']) + result = _parse_fmadm_faulty(res["stdout"]) return result def healthy(): - ''' + """ Return whether fmadm is reporting faults CLI Example: @@ -526,7 +510,8 @@ def healthy(): .. code-block:: bash salt '*' fmadm.healthy - ''' + """ return False if faulty() else True + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/solaris_group.py b/salt/modules/solaris_group.py index 4138c3f90fa..85cf65f86a2 100644 --- a/salt/modules/solaris_group.py +++ b/salt/modules/solaris_group.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage groups on Solaris .. important:: @@ -7,8 +7,8 @@ Manage groups on Solaris minion, and it is using a different module (or gives an error similar to *'group.info' is not available*), see :ref:`here `. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -16,7 +16,6 @@ import logging # Import salt libs import salt.utils.data - log = logging.getLogger(__name__) @@ -26,21 +25,24 @@ except ImportError: pass # Define the module's virtual name -__virtualname__ = 'group' +__virtualname__ = "group" def __virtual__(): - ''' + """ Set the group module if the kernel is SunOS - ''' - if __grains__['kernel'] == 'SunOS': + """ + if __grains__["kernel"] == "SunOS": return __virtualname__ - return (False, 'The solaris_group execution module failed to load: ' - 'only available on Solaris systems.') + return ( + False, + "The solaris_group execution module failed to load: " + "only available on Solaris systems.", + ) def add(name, gid=None, **kwargs): - ''' + """ Add the specified group CLI Example: @@ -48,25 +50,24 @@ def add(name, gid=None, **kwargs): .. code-block:: bash salt '*' group.add foo 3456 - ''' - if salt.utils.data.is_true(kwargs.pop('system', False)): - log.warning('solaris_group module does not support the \'system\' ' - 'argument') + """ + if salt.utils.data.is_true(kwargs.pop("system", False)): + log.warning("solaris_group module does not support the 'system' " "argument") if kwargs: - log.warning('Invalid kwargs passed to group.add') + log.warning("Invalid kwargs passed to group.add") - cmd = 'groupadd ' + cmd = "groupadd " if gid: - cmd += '-g {0} '.format(gid) + cmd += "-g {0} ".format(gid) cmd += name - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - return not ret['retcode'] + return not ret["retcode"] def delete(name): - ''' + """ Remove the named group CLI Example: @@ -74,14 +75,14 @@ def delete(name): .. code-block:: bash salt '*' group.delete foo - ''' - ret = __salt__['cmd.run_all']('groupdel {0}'.format(name), python_shell=False) + """ + ret = __salt__["cmd.run_all"]("groupdel {0}".format(name), python_shell=False) - return not ret['retcode'] + return not ret["retcode"] def info(name): - ''' + """ Return information about a group CLI Example: @@ -89,20 +90,22 @@ def info(name): .. code-block:: bash salt '*' group.info foo - ''' + """ try: grinfo = grp.getgrnam(name) except KeyError: return {} else: - return {'name': grinfo.gr_name, - 'passwd': grinfo.gr_passwd, - 'gid': grinfo.gr_gid, - 'members': grinfo.gr_mem} + return { + "name": grinfo.gr_name, + "passwd": grinfo.gr_passwd, + "gid": grinfo.gr_gid, + "members": grinfo.gr_mem, + } def getent(refresh=False): - ''' + """ Return info on all groups CLI Example: @@ -110,20 +113,20 @@ def getent(refresh=False): .. code-block:: bash salt '*' group.getent - ''' - if 'group.getent' in __context__ and not refresh: - return __context__['group.getent'] + """ + if "group.getent" in __context__ and not refresh: + return __context__["group.getent"] ret = [] for grinfo in grp.getgrall(): ret.append(info(grinfo.gr_name)) - __context__['group.getent'] = ret + __context__["group.getent"] = ret return ret def chgid(name, gid): - ''' + """ Change the gid for a named group CLI Example: @@ -131,13 +134,13 @@ def chgid(name, gid): .. code-block:: bash salt '*' group.chgid foo 4376 - ''' - pre_gid = __salt__['file.group_to_gid'](name) + """ + pre_gid = __salt__["file.group_to_gid"](name) if gid == pre_gid: return True - cmd = 'groupmod -g {0} {1}'.format(gid, name) - __salt__['cmd.run'](cmd, python_shell=False) - post_gid = __salt__['file.group_to_gid'](name) + cmd = "groupmod -g {0} {1}".format(gid, name) + __salt__["cmd.run"](cmd, python_shell=False) + post_gid = __salt__["file.group_to_gid"](name) if post_gid != pre_gid: return post_gid == gid return False diff --git a/salt/modules/solaris_shadow.py b/salt/modules/solaris_shadow.py index 13157f0c4a3..01e1eb18e37 100644 --- a/salt/modules/solaris_shadow.py +++ b/salt/modules/solaris_shadow.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage the password database on Solaris systems .. important:: @@ -7,13 +7,19 @@ Manage the password database on Solaris systems minion, and it is using a different module (or gives an error similar to *'shadow.info' is not available*), see :ref:`here `. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os + +# Import salt libs +import salt.utils.files +from salt.exceptions import CommandExecutionError + try: import spwd + HAS_SPWD = True except ImportError: # SmartOS joyent_20130322T181205Z does not have spwd @@ -23,31 +29,33 @@ except ImportError: except ImportError: pass # We're most likely on a Windows machine. -# Import salt libs -import salt.utils.files -from salt.exceptions import CommandExecutionError + try: import salt.utils.pycrypto + HAS_CRYPT = True except ImportError: HAS_CRYPT = False # Define the module's virtual name -__virtualname__ = 'shadow' +__virtualname__ = "shadow" def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' - if __grains__.get('kernel', '') == 'SunOS': + """ + if __grains__.get("kernel", "") == "SunOS": return __virtualname__ - return (False, 'The solaris_shadow execution module failed to load: only available on Solaris systems.') + return ( + False, + "The solaris_shadow execution module failed to load: only available on Solaris systems.", + ) def default_hash(): - ''' + """ Returns the default hash used for unset passwords CLI Example: @@ -55,12 +63,12 @@ def default_hash(): .. code-block:: bash salt '*' shadow.default_hash - ''' - return '!' + """ + return "!" def info(name): - ''' + """ Return information for the specified user CLI Example: @@ -68,60 +76,61 @@ def info(name): .. code-block:: bash salt '*' shadow.info root - ''' + """ if HAS_SPWD: try: data = spwd.getspnam(name) ret = { - 'name': data.sp_nam, - 'passwd': data.sp_pwd, - 'lstchg': data.sp_lstchg, - 'min': data.sp_min, - 'max': data.sp_max, - 'warn': data.sp_warn, - 'inact': data.sp_inact, - 'expire': data.sp_expire} + "name": data.sp_nam, + "passwd": data.sp_pwd, + "lstchg": data.sp_lstchg, + "min": data.sp_min, + "max": data.sp_max, + "warn": data.sp_warn, + "inact": data.sp_inact, + "expire": data.sp_expire, + } except KeyError: ret = { - 'name': '', - 'passwd': '', - 'lstchg': '', - 'min': '', - 'max': '', - 'warn': '', - 'inact': '', - 'expire': ''} + "name": "", + "passwd": "", + "lstchg": "", + "min": "", + "max": "", + "warn": "", + "inact": "", + "expire": "", + } return ret # SmartOS joyent_20130322T181205Z does not have spwd, but not all is lost # Return what we can know ret = { - 'name': '', - 'passwd': '', - 'lstchg': '', - 'min': '', - 'max': '', - 'warn': '', - 'inact': '', - 'expire': ''} + "name": "", + "passwd": "", + "lstchg": "", + "min": "", + "max": "", + "warn": "", + "inact": "", + "expire": "", + } try: data = pwd.getpwnam(name) - ret.update({ - 'name': name - }) + ret.update({"name": name}) except KeyError: return ret # To compensate for lack of spwd module, read in password hash from /etc/shadow - s_file = '/etc/shadow' + s_file = "/etc/shadow" if not os.path.isfile(s_file): return ret - with salt.utils.files.fopen(s_file, 'rb') as ifile: + with salt.utils.files.fopen(s_file, "rb") as ifile: for line in ifile: - comps = line.strip().split(':') + comps = line.strip().split(":") if comps[0] == name: - ret.update({'passwd': comps[1]}) + ret.update({"passwd": comps[1]}) # For SmartOS `passwd -s ` and the output format is: # name status mm/dd/yy min max warn @@ -138,31 +147,33 @@ def info(name): # 5. Maximum age # 6. Warning period - output = __salt__['cmd.run_all']('passwd -s {0}'.format(name), python_shell=False) - if output['retcode'] != 0: + output = __salt__["cmd.run_all"]("passwd -s {0}".format(name), python_shell=False) + if output["retcode"] != 0: return ret - fields = output['stdout'].split() + fields = output["stdout"].split() if len(fields) == 2: # For example: # root NL return ret # We have all fields: # buildbot L 05/09/2013 0 99999 7 - ret.update({ - 'name': data.pw_name, - 'lstchg': fields[2], - 'min': int(fields[3]), - 'max': int(fields[4]), - 'warn': int(fields[5]), - 'inact': '', - 'expire': '' - }) + ret.update( + { + "name": data.pw_name, + "lstchg": fields[2], + "min": int(fields[3]), + "max": int(fields[4]), + "warn": int(fields[5]), + "inact": "", + "expire": "", + } + ) return ret def set_maxdays(name, maxdays): - ''' + """ Set the maximum number of days during which a password is valid. See man passwd. @@ -171,19 +182,19 @@ def set_maxdays(name, maxdays): .. code-block:: bash salt '*' shadow.set_maxdays username 90 - ''' + """ pre_info = info(name) - if maxdays == pre_info['max']: + if maxdays == pre_info["max"]: return True - cmd = 'passwd -x {0} {1}'.format(maxdays, name) - __salt__['cmd.run'](cmd, python_shell=False) + cmd = "passwd -x {0} {1}".format(maxdays, name) + __salt__["cmd.run"](cmd, python_shell=False) post_info = info(name) - if post_info['max'] != pre_info['max']: - return post_info['max'] == maxdays + if post_info["max"] != pre_info["max"]: + return post_info["max"] == maxdays def set_mindays(name, mindays): - ''' + """ Set the minimum number of days between password changes. See man passwd. CLI Example: @@ -191,20 +202,20 @@ def set_mindays(name, mindays): .. code-block:: bash salt '*' shadow.set_mindays username 7 - ''' + """ pre_info = info(name) - if mindays == pre_info['min']: + if mindays == pre_info["min"]: return True - cmd = 'passwd -n {0} {1}'.format(mindays, name) - __salt__['cmd.run'](cmd, python_shell=False) + cmd = "passwd -n {0} {1}".format(mindays, name) + __salt__["cmd.run"](cmd, python_shell=False) post_info = info(name) - if post_info['min'] != pre_info['min']: - return post_info['min'] == mindays + if post_info["min"] != pre_info["min"]: + return post_info["min"] == mindays return False -def gen_password(password, crypt_salt=None, algorithm='sha512'): - ''' +def gen_password(password, crypt_salt=None, algorithm="sha512"): + """ .. versionadded:: 2015.8.8 Generate hashed password @@ -236,17 +247,17 @@ def gen_password(password, crypt_salt=None, algorithm='sha512'): salt '*' shadow.gen_password 'I_am_password' salt '*' shadow.gen_password 'I_am_password' crypt_salt='I_am_salt' algorithm=sha256 - ''' + """ if not HAS_CRYPT: raise CommandExecutionError( - 'gen_password is not available on this operating system ' - 'because the "crypt" python module is not available.' - ) + "gen_password is not available on this operating system " + 'because the "crypt" python module is not available.' + ) return salt.utils.pycrypto.gen_hash(crypt_salt, password, algorithm) def del_password(name): - ''' + """ .. versionadded:: 2015.8.8 Delete the password from name user @@ -256,15 +267,15 @@ def del_password(name): .. code-block:: bash salt '*' shadow.del_password username - ''' - cmd = 'passwd -d {0}'.format(name) - __salt__['cmd.run'](cmd, python_shell=False, output_loglevel='quiet') + """ + cmd = "passwd -d {0}".format(name) + __salt__["cmd.run"](cmd, python_shell=False, output_loglevel="quiet") uinfo = info(name) - return not uinfo['passwd'] + return not uinfo["passwd"] def set_password(name, password): - ''' + """ Set the password for a named user. The password must be a properly defined hash, the password hash can be generated with this command: ``openssl passwd -1 `` @@ -274,30 +285,30 @@ def set_password(name, password): .. code-block:: bash salt '*' shadow.set_password root $1$UYCIxa628.9qXjpQCjM4a.. - ''' - s_file = '/etc/shadow' + """ + s_file = "/etc/shadow" ret = {} if not os.path.isfile(s_file): return ret lines = [] - with salt.utils.files.fopen(s_file, 'rb') as ifile: + with salt.utils.files.fopen(s_file, "rb") as ifile: for line in ifile: - comps = line.strip().split(':') + comps = line.strip().split(":") if comps[0] != name: lines.append(line) continue comps[1] = password - line = ':'.join(comps) - lines.append('{0}\n'.format(line)) - with salt.utils.files.fopen(s_file, 'w+') as ofile: + line = ":".join(comps) + lines.append("{0}\n".format(line)) + with salt.utils.files.fopen(s_file, "w+") as ofile: lines = [salt.utils.stringutils.to_str(_l) for _l in lines] ofile.writelines(lines) uinfo = info(name) - return uinfo['passwd'] == password + return uinfo["passwd"] == password def set_warndays(name, warndays): - ''' + """ Set the number of days of warning before a password change is required. See man passwd. @@ -306,13 +317,13 @@ def set_warndays(name, warndays): .. code-block:: bash salt '*' shadow.set_warndays username 7 - ''' + """ pre_info = info(name) - if warndays == pre_info['warn']: + if warndays == pre_info["warn"]: return True - cmd = 'passwd -w {0} {1}'.format(warndays, name) - __salt__['cmd.run'](cmd, python_shell=False) + cmd = "passwd -w {0} {1}".format(warndays, name) + __salt__["cmd.run"](cmd, python_shell=False) post_info = info(name) - if post_info['warn'] != pre_info['warn']: - return post_info['warn'] == warndays + if post_info["warn"] != pre_info["warn"]: + return post_info["warn"] == warndays return False diff --git a/salt/modules/solaris_system.py b/salt/modules/solaris_system.py index 1d74405c35c..4843465cda0 100644 --- a/salt/modules/solaris_system.py +++ b/salt/modules/solaris_system.py @@ -1,38 +1,37 @@ # -*- coding: utf-8 -*- -''' +""" Support for reboot, shutdown, etc This module is assumes we are using solaris-like shutdown .. versionadded:: 2016.3.0 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.path import salt.utils.platform # Define the module's virtual name -__virtualname__ = 'system' +__virtualname__ = "system" def __virtual__(): - ''' + """ Only supported on Solaris-like systems - ''' - if not salt.utils.platform.is_sunos() \ - or not salt.utils.path.which('shutdown'): + """ + if not salt.utils.platform.is_sunos() or not salt.utils.path.which("shutdown"): return ( False, - 'The system execution module failed to load: only available on ' - 'Solaris-like ystems with shutdown command.' + "The system execution module failed to load: only available on " + "Solaris-like ystems with shutdown command.", ) return __virtualname__ def halt(): - ''' + """ Halt a running system CLI Example: @@ -40,12 +39,12 @@ def halt(): .. code-block:: bash salt '*' system.halt - ''' + """ return shutdown() def init(state): - ''' + """ Change the system runlevel on sysV compatible systems CLI Example: @@ -89,14 +88,14 @@ def init(state): Stop the operating system and reboot to the state defined by the initdefault entry in /etc/inittab. The rc6 procedure is called to perform this task. - ''' - cmd = ['shutdown', '-i', state, '-g', '0', '-y'] - ret = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = ["shutdown", "-i", state, "-g", "0", "-y"] + ret = __salt__["cmd.run"](cmd, python_shell=False) return ret def poweroff(): - ''' + """ Poweroff a running system CLI Example: @@ -104,12 +103,12 @@ def poweroff(): .. code-block:: bash salt '*' system.poweroff - ''' + """ return shutdown() def reboot(delay=0, message=None): - ''' + """ Reboot the system delay : int @@ -123,16 +122,16 @@ def reboot(delay=0, message=None): salt '*' system.reboot salt '*' system.reboot 60 "=== system upgraded ===" - ''' - cmd = ['shutdown', '-i', '6', '-g', delay, '-y'] + """ + cmd = ["shutdown", "-i", "6", "-g", delay, "-y"] if message: cmd.append(message) - ret = __salt__['cmd.run'](cmd, python_shell=False) + ret = __salt__["cmd.run"](cmd, python_shell=False) return ret def shutdown(delay=0, message=None): - ''' + """ Shutdown a running system delay : int @@ -146,9 +145,9 @@ def shutdown(delay=0, message=None): salt '*' system.shutdown salt '*' system.shutdown 60 "=== disk replacement ===" - ''' - cmd = ['shutdown', '-i', '5', '-g', delay, '-y'] + """ + cmd = ["shutdown", "-i", "5", "-g", delay, "-y"] if message: cmd.append(message) - ret = __salt__['cmd.run'](cmd, python_shell=False) + ret = __salt__["cmd.run"](cmd, python_shell=False) return ret diff --git a/salt/modules/solaris_user.py b/salt/modules/solaris_user.py index 5107435cc99..b01c84a2b4e 100644 --- a/salt/modules/solaris_user.py +++ b/salt/modules/solaris_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage users with the useradd command .. important:: @@ -9,72 +9,83 @@ Manage users with the useradd command *'user.info' is not available*), see :ref:`here <module-provider-override>`. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -try: - import pwd - HAS_PWD = True -except ImportError: - HAS_PWD = False +from __future__ import absolute_import, print_function, unicode_literals + import copy import logging # Import salt libs import salt.utils.data import salt.utils.user -from salt.ext import six from salt.exceptions import CommandExecutionError +from salt.ext import six + +try: + import pwd + + HAS_PWD = True +except ImportError: + HAS_PWD = False + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'user' +__virtualname__ = "user" def __virtual__(): - ''' + """ Set the user module if the kernel is SunOS - ''' - if __grains__['kernel'] == 'SunOS' and HAS_PWD: + """ + if __grains__["kernel"] == "SunOS" and HAS_PWD: return __virtualname__ - return (False, 'The solaris_user execution module failed to load: ' - 'only available on Solaris systems with pwd module installed.') + return ( + False, + "The solaris_user execution module failed to load: " + "only available on Solaris systems with pwd module installed.", + ) def _get_gecos(name): - ''' + """ Retrieve GECOS field info and return it in dictionary form - ''' - gecos_field = pwd.getpwnam(name).pw_gecos.split(',', 3) + """ + gecos_field = pwd.getpwnam(name).pw_gecos.split(",", 3) if not gecos_field: return {} else: # Assign empty strings for any unspecified trailing GECOS fields while len(gecos_field) < 4: - gecos_field.append('') - return {'fullname': six.text_type(gecos_field[0]), - 'roomnumber': six.text_type(gecos_field[1]), - 'workphone': six.text_type(gecos_field[2]), - 'homephone': six.text_type(gecos_field[3])} + gecos_field.append("") + return { + "fullname": six.text_type(gecos_field[0]), + "roomnumber": six.text_type(gecos_field[1]), + "workphone": six.text_type(gecos_field[2]), + "homephone": six.text_type(gecos_field[3]), + } def _build_gecos(gecos_dict): - ''' + """ Accepts a dictionary entry containing GECOS field names and their values, and returns a full GECOS comment string, to be used with usermod. - ''' - return '{0},{1},{2},{3}'.format(gecos_dict.get('fullname', ''), - gecos_dict.get('roomnumber', ''), - gecos_dict.get('workphone', ''), - gecos_dict.get('homephone', '')) + """ + return "{0},{1},{2},{3}".format( + gecos_dict.get("fullname", ""), + gecos_dict.get("roomnumber", ""), + gecos_dict.get("workphone", ""), + gecos_dict.get("homephone", ""), + ) def _update_gecos(name, key, value): - ''' + """ Common code to change a user's GECOS information - ''' + """ if not isinstance(value, six.string_types): value = six.text_type(value) pre_info = _get_gecos(name) @@ -84,26 +95,28 @@ def _update_gecos(name, key, value): return True gecos_data = copy.deepcopy(pre_info) gecos_data[key] = value - cmd = ['usermod', '-c', _build_gecos(gecos_data), name] - __salt__['cmd.run'](cmd, python_shell=False) + cmd = ["usermod", "-c", _build_gecos(gecos_data), name] + __salt__["cmd.run"](cmd, python_shell=False) post_info = info(name) return _get_gecos(name).get(key) == value -def add(name, - uid=None, - gid=None, - groups=None, - home=None, - shell=None, - unique=True, - fullname='', - roomnumber='', - workphone='', - homephone='', - createhome=True, - **kwargs): - ''' +def add( + name, + uid=None, + gid=None, + groups=None, + home=None, + shell=None, + unique=True, + fullname="", + roomnumber="", + workphone="", + homephone="", + createhome=True, + **kwargs +): + """ Add a user to the minion CLI Example: @@ -111,33 +124,32 @@ def add(name, .. code-block:: bash salt '*' user.add name <uid> <gid> <groups> <home> <shell> - ''' - if salt.utils.data.is_true(kwargs.pop('system', False)): - log.warning('solaris_user module does not support the \'system\' ' - 'argument') + """ + if salt.utils.data.is_true(kwargs.pop("system", False)): + log.warning("solaris_user module does not support the 'system' " "argument") if kwargs: - log.warning('Invalid kwargs passed to user.add') + log.warning("Invalid kwargs passed to user.add") if isinstance(groups, six.string_types): - groups = groups.split(',') - cmd = ['useradd'] + groups = groups.split(",") + cmd = ["useradd"] if shell: - cmd.extend(['-s', shell]) + cmd.extend(["-s", shell]) if uid: - cmd.extend(['-u', uid]) + cmd.extend(["-u", uid]) if gid: - cmd.extend(['-g', gid]) + cmd.extend(["-g", gid]) if groups: - cmd.extend(['-G', ','.join(groups)]) + cmd.extend(["-G", ",".join(groups)]) if createhome: - cmd.append('-m') + cmd.append("-m") if home is not None: - cmd.extend(['-d', home]) + cmd.extend(["-d", home]) if not unique: - cmd.append('-o') + cmd.append("-o") cmd.append(name) - if __salt__['cmd.retcode'](cmd, python_shell=False) != 0: + if __salt__["cmd.retcode"](cmd, python_shell=False) != 0: return False else: # At this point, the user was successfully created, so return true @@ -160,7 +172,7 @@ def add(name, def delete(name, remove=False, force=False): - ''' + """ Remove a user from the minion CLI Example: @@ -168,21 +180,20 @@ def delete(name, remove=False, force=False): .. code-block:: bash salt '*' user.delete name remove=True force=True - ''' + """ if salt.utils.data.is_true(force): log.warning( - 'userdel does not support force-deleting user while user is ' - 'logged in' + "userdel does not support force-deleting user while user is " "logged in" ) - cmd = ['userdel'] + cmd = ["userdel"] if remove: - cmd.append('-r') + cmd.append("-r") cmd.append(name) - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 def getent(refresh=False): - ''' + """ Return the list of all info for all users CLI Example: @@ -190,19 +201,19 @@ def getent(refresh=False): .. code-block:: bash salt '*' user.getent - ''' - if 'user.getent' in __context__ and not refresh: - return __context__['user.getent'] + """ + if "user.getent" in __context__ and not refresh: + return __context__["user.getent"] ret = [] for data in pwd.getpwall(): ret.append(info(data.pw_name)) - __context__['user.getent'] = ret + __context__["user.getent"] = ret return ret def chuid(name, uid): - ''' + """ Change the uid for a named user CLI Example: @@ -210,21 +221,19 @@ def chuid(name, uid): .. code-block:: bash salt '*' user.chuid foo 4376 - ''' + """ pre_info = info(name) if not pre_info: - raise CommandExecutionError( - 'User \'{0}\' does not exist'.format(name) - ) - if uid == pre_info['uid']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if uid == pre_info["uid"]: return True - cmd = ['usermod', '-u', uid, name] - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('uid') == uid + cmd = ["usermod", "-u", uid, name] + __salt__["cmd.run"](cmd, python_shell=False) + return info(name).get("uid") == uid def chgid(name, gid): - ''' + """ Change the default group of the user CLI Example: @@ -232,21 +241,19 @@ def chgid(name, gid): .. code-block:: bash salt '*' user.chgid foo 4376 - ''' + """ pre_info = info(name) if not pre_info: - raise CommandExecutionError( - 'User \'{0}\' does not exist'.format(name) - ) - if gid == pre_info['gid']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if gid == pre_info["gid"]: return True - cmd = ['usermod', '-g', gid, name] - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('gid') == gid + cmd = ["usermod", "-g", gid, name] + __salt__["cmd.run"](cmd, python_shell=False) + return info(name).get("gid") == gid def chshell(name, shell): - ''' + """ Change the default shell of the user CLI Example: @@ -254,21 +261,19 @@ def chshell(name, shell): .. code-block:: bash salt '*' user.chshell foo /bin/zsh - ''' + """ pre_info = info(name) if not pre_info: - raise CommandExecutionError( - 'User \'{0}\' does not exist'.format(name) - ) - if shell == pre_info['shell']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if shell == pre_info["shell"]: return True - cmd = ['usermod', '-s', shell, name] - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('shell') == shell + cmd = ["usermod", "-s", shell, name] + __salt__["cmd.run"](cmd, python_shell=False) + return info(name).get("shell") == shell def chhome(name, home, persist=False): - ''' + """ Set a new home directory for an existing user name @@ -287,24 +292,22 @@ def chhome(name, home, persist=False): .. code-block:: bash salt '*' user.chhome foo /home/users/foo True - ''' + """ pre_info = info(name) if not pre_info: - raise CommandExecutionError( - 'User \'{0}\' does not exist'.format(name) - ) - if home == pre_info['home']: + raise CommandExecutionError("User '{0}' does not exist".format(name)) + if home == pre_info["home"]: return True - cmd = ['usermod', '-d', home] + cmd = ["usermod", "-d", home] if persist: - cmd.append('-m') + cmd.append("-m") cmd.append(name) - __salt__['cmd.run'](cmd, python_shell=False) - return info(name).get('home') == home + __salt__["cmd.run"](cmd, python_shell=False) + return info(name).get("home") == home def chgroups(name, groups, append=False): - ''' + """ Change the groups to which a user belongs name @@ -324,20 +327,20 @@ def chgroups(name, groups, append=False): .. code-block:: bash salt '*' user.chgroups foo wheel,root True - ''' + """ if isinstance(groups, six.string_types): - groups = groups.split(',') + groups = groups.split(",") ugrps = set(list_groups(name)) if ugrps == set(groups): return True if append: groups.update(ugrps) - cmd = ['usermod', '-G', ','.join(groups), name] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd = ["usermod", "-G", ",".join(groups), name] + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 def chfullname(name, fullname): - ''' + """ Change the user's Full Name CLI Example: @@ -345,12 +348,12 @@ def chfullname(name, fullname): .. code-block:: bash salt '*' user.chfullname foo "Foo Bar" - ''' - return _update_gecos(name, 'fullname', fullname) + """ + return _update_gecos(name, "fullname", fullname) def chroomnumber(name, roomnumber): - ''' + """ Change the user's Room Number CLI Example: @@ -358,12 +361,12 @@ def chroomnumber(name, roomnumber): .. code-block:: bash salt '*' user.chroomnumber foo 123 - ''' - return _update_gecos(name, 'roomnumber', roomnumber) + """ + return _update_gecos(name, "roomnumber", roomnumber) def chworkphone(name, workphone): - ''' + """ Change the user's Work Phone CLI Example: @@ -371,12 +374,12 @@ def chworkphone(name, workphone): .. code-block:: bash salt '*' user.chworkphone foo "7735550123" - ''' - return _update_gecos(name, 'workphone', workphone) + """ + return _update_gecos(name, "workphone", workphone) def chhomephone(name, homephone): - ''' + """ Change the user's Home Phone CLI Example: @@ -384,12 +387,12 @@ def chhomephone(name, homephone): .. code-block:: bash salt '*' user.chhomephone foo "7735551234" - ''' - return _update_gecos(name, 'homephone', homephone) + """ + return _update_gecos(name, "homephone", homephone) def info(name): - ''' + """ Return user information CLI Example: @@ -397,33 +400,33 @@ def info(name): .. code-block:: bash salt '*' user.info root - ''' + """ ret = {} try: data = pwd.getpwnam(name) - ret['gid'] = data.pw_gid - ret['groups'] = list_groups(name) - ret['home'] = data.pw_dir - ret['name'] = data.pw_name - ret['passwd'] = data.pw_passwd - ret['shell'] = data.pw_shell - ret['uid'] = data.pw_uid + ret["gid"] = data.pw_gid + ret["groups"] = list_groups(name) + ret["home"] = data.pw_dir + ret["name"] = data.pw_name + ret["passwd"] = data.pw_passwd + ret["shell"] = data.pw_shell + ret["uid"] = data.pw_uid # Put GECOS info into a list - gecos_field = data.pw_gecos.split(',', 3) + gecos_field = data.pw_gecos.split(",", 3) # Assign empty strings for any unspecified GECOS fields while len(gecos_field) < 4: - gecos_field.append('') - ret['fullname'] = gecos_field[0] - ret['roomnumber'] = gecos_field[1] - ret['workphone'] = gecos_field[2] - ret['homephone'] = gecos_field[3] + gecos_field.append("") + ret["fullname"] = gecos_field[0] + ret["roomnumber"] = gecos_field[1] + ret["workphone"] = gecos_field[2] + ret["homephone"] = gecos_field[3] except KeyError: return {} return ret def list_groups(name): - ''' + """ Return a list of groups the named user belongs to CLI Example: @@ -431,12 +434,12 @@ def list_groups(name): .. code-block:: bash salt '*' user.list_groups foo - ''' + """ return salt.utils.user.get_group_list(name) def list_users(): - ''' + """ Return a list of all users CLI Example: @@ -444,12 +447,12 @@ def list_users(): .. code-block:: bash salt '*' user.list_users - ''' + """ return sorted([user.pw_name for user in pwd.getpwall()]) def rename(name, new_name): - ''' + """ Change the username for a named user CLI Example: @@ -457,15 +460,13 @@ def rename(name, new_name): .. code-block:: bash salt '*' user.rename name new_name - ''' + """ current_info = info(name) if not current_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("User '{0}' does not exist".format(name)) new_info = info(new_name) if new_info: - raise CommandExecutionError( - 'User \'{0}\' already exists'.format(new_name) - ) - cmd = ['usermod', '-l', new_name, name] - __salt__['cmd.run'](cmd, python_shell=False) - return info(new_name).get('name') == new_name + raise CommandExecutionError("User '{0}' already exists".format(new_name)) + cmd = ["usermod", "-l", new_name, name] + __salt__["cmd.run"](cmd, python_shell=False) + return info(new_name).get("name") == new_name diff --git a/salt/modules/solarisipspkg.py b/salt/modules/solarisipspkg.py index 3da1dbe5a23..639e605d9e6 100644 --- a/salt/modules/solarisipspkg.py +++ b/salt/modules/solarisipspkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" IPS pkg support for Solaris .. important:: @@ -34,9 +34,10 @@ Or you can override it globally by setting the :conf_minion:`providers` paramete providers: pkg: pkgutil -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging @@ -45,68 +46,71 @@ import salt.utils.data import salt.utils.functools import salt.utils.path import salt.utils.pkg -from salt.ext.six import string_types from salt.exceptions import CommandExecutionError from salt.ext import six +from salt.ext.six import string_types # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Set the virtual pkg module if the os is Solaris 11 - ''' - if __grains__['os_family'] == 'Solaris' \ - and float(__grains__['kernelrelease']) > 5.10 \ - and salt.utils.path.which('pkg'): + """ + if ( + __grains__["os_family"] == "Solaris" + and float(__grains__["kernelrelease"]) > 5.10 + and salt.utils.path.which("pkg") + ): return __virtualname__ - return (False, - 'The solarisips execution module failed to load: only available ' - 'on Solaris >= 11.') + return ( + False, + "The solarisips execution module failed to load: only available " + "on Solaris >= 11.", + ) ips_pkg_return_values = { - 0: 'Command succeeded.', - 1: 'An error occurred.', - 2: 'Invalid command line options were specified.', - 3: 'Multiple operations were requested, but only some of them succeeded.', - 4: 'No changes were made - nothing to do.', - 5: 'The requested operation cannot be performed on a live image.', - 6: 'The requested operation cannot be completed because the licenses for ' - 'the packages being installed or updated have not been accepted.', - 7: 'The image is currently in use by another process and cannot be ' - 'modified.' + 0: "Command succeeded.", + 1: "An error occurred.", + 2: "Invalid command line options were specified.", + 3: "Multiple operations were requested, but only some of them succeeded.", + 4: "No changes were made - nothing to do.", + 5: "The requested operation cannot be performed on a live image.", + 6: "The requested operation cannot be completed because the licenses for " + "the packages being installed or updated have not been accepted.", + 7: "The image is currently in use by another process and cannot be " "modified.", } def _ips_get_pkgname(line): - ''' + """ Extracts package name from "pkg list -v" output. Input: one line of the command output Output: pkg name (e.g.: "pkg://solaris/x11/library/toolkit/libxt") Example use: line = "pkg://solaris/x11/library/toolkit/libxt@1.1.3,5.11-0.175.1.0.0.24.1317:20120904T180030Z i--" name = _ips_get_pkgname(line) - ''' - return line.split()[0].split('@')[0].strip() + """ + return line.split()[0].split("@")[0].strip() def _ips_get_pkgversion(line): - ''' + """ Extracts package version from "pkg list -v" output. Input: one line of the command output Output: package version (e.g.: "1.1.3,5.11-0.175.1.0.0.24.1317:20120904T180030Z") Example use: line = "pkg://solaris/x11/library/toolkit/libxt@1.1.3,5.11-0.175.1.0.0.24.1317:20120904T180030Z i--" name = _ips_get_pkgversion(line) - ''' - return line.split()[0].split('@')[1].strip() + """ + return line.split()[0].split("@")[1].strip() def refresh_db(full=False): - ''' + """ Updates the remote repos database. full : False @@ -120,17 +124,17 @@ def refresh_db(full=False): salt '*' pkg.refresh_db salt '*' pkg.refresh_db full=True - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) if full: - return __salt__['cmd.retcode']('/bin/pkg refresh --full') == 0 + return __salt__["cmd.retcode"]("/bin/pkg refresh --full") == 0 else: - return __salt__['cmd.retcode']('/bin/pkg refresh') == 0 + return __salt__["cmd.retcode"]("/bin/pkg refresh") == 0 def upgrade_available(name): - ''' + """ Check if there is an upgrade available for a certain package Accepts full or partial FMRI. Returns all matches found. @@ -139,10 +143,10 @@ def upgrade_available(name): .. code-block:: bash salt '*' pkg.upgrade_available apache-22 - ''' + """ version = None - cmd = ['pkg', 'list', '-Huv', name] - lines = __salt__['cmd.run_stdout'](cmd).splitlines() + cmd = ["pkg", "list", "-Huv", name] + lines = __salt__["cmd.run_stdout"](cmd).splitlines() if not lines: return {} ret = {} @@ -152,7 +156,7 @@ def upgrade_available(name): def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 - ''' + """ Lists all packages available for update. When run in global zone, it reports only upgradable packages for the global @@ -181,19 +185,19 @@ def list_upgrades(refresh=True, **kwargs): # pylint: disable=W0613 salt '*' pkg.list_upgrades salt '*' pkg.list_upgrades refresh=False - ''' + """ if salt.utils.data.is_true(refresh): refresh_db(full=True) upgrades = {} # awk is in core-os package so we can use it without checking - lines = __salt__['cmd.run_stdout']("/bin/pkg list -Huv").splitlines() + lines = __salt__["cmd.run_stdout"]("/bin/pkg list -Huv").splitlines() for line in lines: upgrades[_ips_get_pkgname(line)] = _ips_get_pkgversion(line) return upgrades def upgrade(refresh=False, **kwargs): - ''' + """ Upgrade all packages to the latest possible version. When run in global zone, it updates also all non-global zones. In non-global zones upgrade is limited by dependency constrains linked to @@ -215,7 +219,7 @@ def upgrade(refresh=False, **kwargs): .. code-block:: bash salt '*' pkg.upgrade - ''' + """ if salt.utils.data.is_true(refresh): refresh_db() @@ -225,27 +229,27 @@ def upgrade(refresh=False, **kwargs): # Install or upgrade the package # If package is already installed - cmd = ['pkg', 'update', '-v', '--accept'] - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - __context__.pop('pkg.list_pkgs', None) + cmd = ["pkg", "update", "-v", "--accept"] + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, - 'retcode': ips_pkg_return_values[result['retcode']], - 'result': result} + "Problem encountered upgrading packages", + info={ + "changes": ret, + "retcode": ips_pkg_return_values[result["retcode"]], + "result": result, + }, ) return ret def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the currently installed packages as a dict:: {'<package_name>': '<version>'} @@ -255,38 +259,39 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret ret = {} - cmd = '/bin/pkg list -Hv' - lines = __salt__['cmd.run_stdout'](cmd).splitlines() + cmd = "/bin/pkg list -Hv" + lines = __salt__["cmd.run_stdout"](cmd).splitlines() # column 1 is full FMRI name in form pkg://publisher/class/name@version for line in lines: name = _ips_get_pkgname(line) version = _ips_get_pkgversion(line) - __salt__['pkg_resource.add_pkg'](ret, name, version) + __salt__["pkg_resource.add_pkg"](ret, name, version) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def version(*names, **kwargs): - ''' + """ Common interface for obtaining the version of installed packages. Accepts full or partial FMRI. If called using pkg_resource, full FMRI is required. Partial FMRI is returned if the package is not installed. @@ -299,13 +304,13 @@ def version(*names, **kwargs): salt '*' pkg.version foo bar baz salt '*' pkg_resource.version pkg://solaris/entire - ''' + """ if len(names) == 0: - return '' + return "" - cmd = ['/bin/pkg', 'list', '-Hv'] + cmd = ["/bin/pkg", "list", "-Hv"] cmd.extend(names) - lines = __salt__['cmd.run_stdout'](cmd, ignore_retcode=True).splitlines() + lines = __salt__["cmd.run_stdout"](cmd, ignore_retcode=True).splitlines() ret = {} for line in lines: ret[_ips_get_pkgname(line)] = _ips_get_pkgversion(line) @@ -313,20 +318,20 @@ def version(*names, **kwargs): # Append package names which are not installed/found for name in names: if name not in ret: - ret[name] = '' + ret[name] = "" # Return a string if only one package name passed if len(names) == 1: try: return next(six.itervalues(ret)) except StopIteration: - return '' + return "" return ret def latest_version(*names, **kwargs): - ''' + """ The available version of packages in the repository. Accepts full or partial FMRI. Partial FMRI is returned if the full FMRI could not be resolved. @@ -346,14 +351,14 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version bash salt '*' pkg.latest_version pkg://solaris/entire salt '*' pkg.latest_version postfix sendmail - ''' + """ if len(names) == 0: - return '' + return "" - cmd = ['/bin/pkg', 'list', '-Hnv'] + cmd = ["/bin/pkg", "list", "-Hnv"] cmd.extend(names) - lines = __salt__['cmd.run_stdout'](cmd, ignore_retcode=True).splitlines() + lines = __salt__["cmd.run_stdout"](cmd, ignore_retcode=True).splitlines() ret = {} for line in lines: ret[_ips_get_pkgname(line)] = _ips_get_pkgversion(line) @@ -368,29 +373,31 @@ def latest_version(*names, **kwargs): if name not in installed: continue if ret[name] == installed[name]: - ret[name] = '' + ret[name] = "" # Append package names which are not found for name in names: if name not in ret: - ret[name] = '' + ret[name] = "" # Return a string if only one package name passed if len(names) == 1: try: return next(six.itervalues(ret)) except StopIteration: - return '' + return "" return ret # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def get_fmri(name, **kwargs): - ''' + """ Returns FMRI from partial name. Returns empty string ('') if not found. In case of multiple match, the function returns list of all matched packages. @@ -399,16 +406,16 @@ def get_fmri(name, **kwargs): .. code-block:: bash salt '*' pkg.get_fmri bash - ''' - if name.startswith('pkg://'): + """ + if name.startswith("pkg://"): # already full fmri return name - cmd = ['/bin/pkg', 'list', '-aHv', name] + cmd = ["/bin/pkg", "list", "-aHv", name] # there can be more packages matching the name - lines = __salt__['cmd.run_stdout'](cmd).splitlines() + lines = __salt__["cmd.run_stdout"](cmd).splitlines() if not lines: # empty string = package not found - return '' + return "" ret = [] for line in lines: ret.append(_ips_get_pkgname(line)) @@ -417,7 +424,7 @@ def get_fmri(name, **kwargs): def normalize_name(name, **kwargs): - ''' + """ Internal function. Normalizes pkg name to full FMRI before running pkg.install. In case of multiple matches or no match, it returns the name without modifications. @@ -427,13 +434,13 @@ def normalize_name(name, **kwargs): .. code-block:: bash salt '*' pkg.normalize_name vim - ''' - if name.startswith('pkg://'): + """ + if name.startswith("pkg://"): # already full fmri return name - cmd = ['/bin/pkg', 'list', '-aHv', name] + cmd = ["/bin/pkg", "list", "-aHv", name] # there can be more packages matching the name - lines = __salt__['cmd.run_stdout'](cmd).splitlines() + lines = __salt__["cmd.run_stdout"](cmd).splitlines() # if we get more lines, it's multiple match (name not unique) # if we get zero lines, pkg is not installed # in both ways it's safer to return original (unmodified) name and let "pkg install" to deal with it @@ -444,7 +451,7 @@ def normalize_name(name, **kwargs): def is_installed(name, **kwargs): - ''' + """ Returns True if the package is installed. Otherwise returns False. Name can be full or partial FMRI. In case of multiple match from partial FMRI name, it returns True. @@ -454,14 +461,14 @@ def is_installed(name, **kwargs): .. code-block:: bash salt '*' pkg.is_installed bash - ''' + """ - cmd = ['/bin/pkg', 'list', '-Hv', name] - return __salt__['cmd.retcode'](cmd) == 0 + cmd = ["/bin/pkg", "list", "-Hv", name] + return __salt__["cmd.retcode"](cmd) == 0 def search(name, versions_as_list=False, **kwargs): - ''' + """ Searches the repository for given pkg name. The name can be full or partial FMRI. All matches are printed. Globs are also supported. @@ -471,28 +478,28 @@ def search(name, versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.search bash - ''' + """ ret = {} - cmd = ['/bin/pkg', 'list', '-aHv', name] - out = __salt__['cmd.run_all'](cmd, ignore_retcode=True) - if out['retcode'] != 0: + cmd = ["/bin/pkg", "list", "-aHv", name] + out = __salt__["cmd.run_all"](cmd, ignore_retcode=True) + if out["retcode"] != 0: # error = nothing found return {} # no error, processing pkg listing # column 1 is full FMRI name in form pkg://publisher/pkg/name@version - for line in out['stdout'].splitlines(): + for line in out["stdout"].splitlines(): name = _ips_get_pkgname(line) version = _ips_get_pkgversion(line) - __salt__['pkg_resource.add_pkg'](ret, name, version) + __salt__["pkg_resource.add_pkg"](ret, name, version) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def install(name=None, refresh=False, pkgs=None, version=None, test=False, **kwargs): - ''' + """ Install the named package using the IPS pkg command. Accepts full or partial FMRI. @@ -516,7 +523,7 @@ def install(name=None, refresh=False, pkgs=None, version=None, test=False, **kwa salt '*' pkg.install pkg://solaris/editor/vim salt '*' pkg.install pkg://solaris/editor/vim refresh=True salt '*' pkg.install pkgs='["foo", "bar"]' - ''' + """ if not pkgs: if is_installed(name): return {} @@ -524,30 +531,32 @@ def install(name=None, refresh=False, pkgs=None, version=None, test=False, **kwa if refresh: refresh_db(full=True) - pkg2inst = '' - if pkgs: # multiple packages specified + pkg2inst = "" + if pkgs: # multiple packages specified pkg2inst = [] for pkg in pkgs: - if getattr(pkg, 'items', False): - if list(pkg.items())[0][1]: # version specified - pkg2inst.append('{0}@{1}'.format(list(pkg.items())[0][0], - list(pkg.items())[0][1])) + if getattr(pkg, "items", False): + if list(pkg.items())[0][1]: # version specified + pkg2inst.append( + "{0}@{1}".format( + list(pkg.items())[0][0], list(pkg.items())[0][1] + ) + ) else: pkg2inst.append(list(pkg.items())[0][0]) else: pkg2inst.append("{0}".format(pkg)) - log.debug('Installing these packages instead of %s: %s', - name, pkg2inst) + log.debug("Installing these packages instead of %s: %s", name, pkg2inst) - else: # install single package + else: # install single package if version: pkg2inst = "{0}@{1}".format(name, version) else: pkg2inst = "{0}".format(name) - cmd = ['pkg', 'install', '-v', '--accept'] + cmd = ["pkg", "install", "-v", "--accept"] if test: - cmd.append('-n') + cmd.append("-n") # Get a list of the packages before install so we can diff after to see # what got installed. @@ -560,32 +569,32 @@ def install(name=None, refresh=False, pkgs=None, version=None, test=False, **kwa elif isinstance(pkg2inst, list): cmd = cmd + pkg2inst - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace') + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace") # Get a list of the packages again, including newly installed ones. - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if out['retcode'] != 0: + if out["retcode"] != 0: raise CommandExecutionError( - 'Error occurred installing package(s)', + "Error occurred installing package(s)", info={ - 'changes': ret, - 'retcode': ips_pkg_return_values[out['retcode']], - 'errors': [out['stderr']] - } + "changes": ret, + "retcode": ips_pkg_return_values[out["retcode"]], + "errors": [out["stderr"]], + }, ) # No error occurred if test: - return 'Test succeeded.' + return "Test succeeded." return ret def remove(name=None, pkgs=None, **kwargs): - ''' + """ Remove specified package. Accepts full or partial FMRI. In case of multiple match, the command fails and won't modify the OS. @@ -610,41 +619,41 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove tcsh salt '*' pkg.remove pkg://solaris/shell/tcsh salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ targets = salt.utils.args.split_input(pkgs) if pkgs else [name] if not targets: return {} if pkgs: - log.debug('Removing these packages instead of %s: %s', name, targets) + log.debug("Removing these packages instead of %s: %s", name, targets) # Get a list of the currently installed pkgs. old = list_pkgs() # Remove the package(s) - cmd = ['/bin/pkg', 'uninstall', '-v'] + targets - out = __salt__['cmd.run_all'](cmd, output_loglevel='trace') + cmd = ["/bin/pkg", "uninstall", "-v"] + targets + out = __salt__["cmd.run_all"](cmd, output_loglevel="trace") # Get a list of the packages after the uninstall - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if out['retcode'] != 0: + if out["retcode"] != 0: raise CommandExecutionError( - 'Error occurred removing package(s)', + "Error occurred removing package(s)", info={ - 'changes': ret, - 'retcode': ips_pkg_return_values[out['retcode']], - 'errors': [out['stderr']] - } + "changes": ret, + "retcode": ips_pkg_return_values[out["retcode"]], + "errors": [out["stderr"]], + }, ) return ret def purge(name, **kwargs): - ''' + """ Remove specified package. Accepts full or partial FMRI. Returns a list containing the removed packages. @@ -654,5 +663,5 @@ def purge(name, **kwargs): .. code-block:: bash salt '*' pkg.purge <package name> - ''' + """ return remove(name, **kwargs) diff --git a/salt/modules/solarispkg.py b/salt/modules/solarispkg.py index 2a828f6e9c4..032d9db017e 100644 --- a/salt/modules/solarispkg.py +++ b/salt/modules/solarispkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Package support for Solaris .. important:: @@ -7,55 +7,60 @@ Package support for Solaris minion, and it is using a different module (or gives an error similar to *'pkg.install' is not available*), see :ref:`here <module-provider-override>`. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import copy -import os import logging +import os # Import salt libs import salt.utils.data -import salt.utils.functools import salt.utils.files +import salt.utils.functools import salt.utils.stringutils from salt.exceptions import CommandExecutionError, MinionError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Set the virtual pkg module if the os is Solaris - ''' - if __grains__['os_family'] == 'Solaris' and float(__grains__['kernelrelease']) <= 5.10: + """ + if ( + __grains__["os_family"] == "Solaris" + and float(__grains__["kernelrelease"]) <= 5.10 + ): return __virtualname__ - return (False, - 'The solarispkg execution module failed to load: only available ' - 'on Solaris <= 10.') + return ( + False, + "The solarispkg execution module failed to load: only available " + "on Solaris <= 10.", + ) def _write_adminfile(kwargs): - ''' + """ Create a temporary adminfile based on the keyword arguments passed to pkg.install. - ''' + """ # Set the adminfile default variables - email = kwargs.get('email', '') - instance = kwargs.get('instance', 'quit') - partial = kwargs.get('partial', 'nocheck') - runlevel = kwargs.get('runlevel', 'nocheck') - idepend = kwargs.get('idepend', 'nocheck') - rdepend = kwargs.get('rdepend', 'nocheck') - space = kwargs.get('space', 'nocheck') - setuid = kwargs.get('setuid', 'nocheck') - conflict = kwargs.get('conflict', 'nocheck') - action = kwargs.get('action', 'nocheck') - basedir = kwargs.get('basedir', 'default') + email = kwargs.get("email", "") + instance = kwargs.get("instance", "quit") + partial = kwargs.get("partial", "nocheck") + runlevel = kwargs.get("runlevel", "nocheck") + idepend = kwargs.get("idepend", "nocheck") + rdepend = kwargs.get("rdepend", "nocheck") + space = kwargs.get("space", "nocheck") + setuid = kwargs.get("setuid", "nocheck") + conflict = kwargs.get("conflict", "nocheck") + action = kwargs.get("action", "nocheck") + basedir = kwargs.get("basedir", "default") # Make tempfile to hold the adminfile contents. adminfile = salt.utils.files.mkstemp(prefix="salt-") @@ -63,24 +68,24 @@ def _write_adminfile(kwargs): def _write_line(fp_, line): fp_.write(salt.utils.stringutils.to_str(line)) - with salt.utils.files.fopen(adminfile, 'w') as fp_: - _write_line(fp_, 'email={0}\n'.format(email)) - _write_line(fp_, 'instance={0}\n'.format(instance)) - _write_line(fp_, 'partial={0}\n'.format(partial)) - _write_line(fp_, 'runlevel={0}\n'.format(runlevel)) - _write_line(fp_, 'idepend={0}\n'.format(idepend)) - _write_line(fp_, 'rdepend={0}\n'.format(rdepend)) - _write_line(fp_, 'space={0}\n'.format(space)) - _write_line(fp_, 'setuid={0}\n'.format(setuid)) - _write_line(fp_, 'conflict={0}\n'.format(conflict)) - _write_line(fp_, 'action={0}\n'.format(action)) - _write_line(fp_, 'basedir={0}\n'.format(basedir)) + with salt.utils.files.fopen(adminfile, "w") as fp_: + _write_line(fp_, "email={0}\n".format(email)) + _write_line(fp_, "instance={0}\n".format(instance)) + _write_line(fp_, "partial={0}\n".format(partial)) + _write_line(fp_, "runlevel={0}\n".format(runlevel)) + _write_line(fp_, "idepend={0}\n".format(idepend)) + _write_line(fp_, "rdepend={0}\n".format(rdepend)) + _write_line(fp_, "space={0}\n".format(space)) + _write_line(fp_, "setuid={0}\n".format(setuid)) + _write_line(fp_, "conflict={0}\n".format(conflict)) + _write_line(fp_, "action={0}\n".format(action)) + _write_line(fp_, "basedir={0}\n".format(basedir)) return adminfile def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed as a dict: .. code-block:: python @@ -92,47 +97,47 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - if 'pkg.list_pkgs' in __context__: + if "pkg.list_pkgs" in __context__: if versions_as_list: - return __context__['pkg.list_pkgs'] + return __context__["pkg.list_pkgs"] else: - ret = copy.deepcopy(__context__['pkg.list_pkgs']) - __salt__['pkg_resource.stringify'](ret) + ret = copy.deepcopy(__context__["pkg.list_pkgs"]) + __salt__["pkg_resource.stringify"](ret) return ret ret = {} - cmd = '/usr/bin/pkginfo -x' + cmd = "/usr/bin/pkginfo -x" # Package information returned two lines per package. On even-offset # lines, the package name is in the first column. On odd-offset lines, the # package version is in the second column. - lines = __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False).splitlines() + lines = __salt__["cmd.run"]( + cmd, output_loglevel="trace", python_shell=False + ).splitlines() for index, line in enumerate(lines): if index % 2 == 0: name = line.split()[0].strip() if index % 2 == 1: version_num = line.split()[1].strip() - __salt__['pkg_resource.add_pkg'](ret, name, version_num) + __salt__["pkg_resource.add_pkg"](ret, name, version_num) - __salt__['pkg_resource.sort_pkglist'](ret) - __context__['pkg.list_pkgs'] = copy.deepcopy(ret) + __salt__["pkg_resource.sort_pkglist"](ret) + __context__["pkg.list_pkgs"] = copy.deepcopy(ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -150,14 +155,14 @@ def latest_version(*names, **kwargs): NOTE: As package repositories are not presently supported for Solaris pkgadd, this function will always return an empty string for a given package. - ''' - kwargs.pop('refresh', True) + """ + kwargs.pop("refresh", True) ret = {} if not names: - return '' + return "" for name in names: - ret[name] = '' + ret[name] = "" # Return a string if only one package name passed if len(names) == 1: @@ -166,11 +171,13 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def upgrade_available(name): - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -178,12 +185,12 @@ def upgrade_available(name): .. code-block:: bash salt '*' pkg.upgrade_available <package name> - ''' - return latest_version(name) != '' + """ + return latest_version(name) != "" def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -194,12 +201,12 @@ def version(*names, **kwargs): salt '*' pkg.version <package name> salt '*' pkg.version <package1> <package2> <package3> ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) -def install(name=None, sources=None, saltenv='base', **kwargs): - ''' +def install(name=None, sources=None, saltenv="base", **kwargs): + """ Install the passed package. Can install packages from the following sources: @@ -320,15 +327,14 @@ def install(name=None, sources=None, saltenv='base', **kwargs): .. note:: The ID declaration is ignored, as the package name is read from the ``sources`` parameter. - ''' - if salt.utils.data.is_true(kwargs.get('refresh')): - log.warning('\'refresh\' argument not implemented for solarispkg ' - 'module') + """ + if salt.utils.data.is_true(kwargs.get("refresh")): + log.warning("'refresh' argument not implemented for solarispkg " "module") # pkgs is not supported, but must be passed here for API compatibility - pkgs = kwargs.pop('pkgs', None) + pkgs = kwargs.pop("pkgs", None) try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: @@ -342,41 +348,41 @@ def install(name=None, sources=None, saltenv='base', **kwargs): return {} try: - if 'admin_source' in kwargs: - adminfile = __salt__['cp.cache_file'](kwargs['admin_source'], saltenv) + if "admin_source" in kwargs: + adminfile = __salt__["cp.cache_file"](kwargs["admin_source"], saltenv) else: adminfile = _write_adminfile(kwargs) old = list_pkgs() - cmd_prefix = ['/usr/sbin/pkgadd', '-n', '-a', adminfile] + cmd_prefix = ["/usr/sbin/pkgadd", "-n", "-a", adminfile] # Only makes sense in a global zone but works fine in non-globals. - if kwargs.get('current_zone_only') == 'True': - cmd_prefix += '-G ' + if kwargs.get("current_zone_only") == "True": + cmd_prefix += "-G " errors = [] for pkg in pkg_params: - cmd = cmd_prefix + ['-d', pkg, 'all'] + cmd = cmd_prefix + ["-d", pkg, "all"] # Install the package{s} - out = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + out = __salt__["cmd.run_all"]( + cmd, output_loglevel="trace", python_shell=False + ) - if out['retcode'] != 0 and out['stderr']: - errors.append(out['stderr']) + if out["retcode"] != 0 and out["stderr"]: + errors.append(out["stderr"]) - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered installing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered installing package(s)", + info={"errors": errors, "changes": ret}, ) finally: # Remove the temp adminfile - if 'admin_source' not in kwargs: + if "admin_source" not in kwargs: try: os.remove(adminfile) except (NameError, OSError): @@ -385,8 +391,8 @@ def install(name=None, sources=None, saltenv='base', **kwargs): return ret -def remove(name=None, pkgs=None, saltenv='base', **kwargs): - ''' +def remove(name=None, pkgs=None, saltenv="base", **kwargs): + """ Remove packages with pkgrm name @@ -439,9 +445,9 @@ def remove(name=None, pkgs=None, saltenv='base', **kwargs): salt '*' pkg.remove SUNWgit salt '*' pkg.remove <package1>,<package2>,<package3> salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -451,35 +457,33 @@ def remove(name=None, pkgs=None, saltenv='base', **kwargs): return {} try: - if 'admin_source' in kwargs: - adminfile = __salt__['cp.cache_file'](kwargs['admin_source'], saltenv) + if "admin_source" in kwargs: + adminfile = __salt__["cp.cache_file"](kwargs["admin_source"], saltenv) else: # Make tempfile to hold the adminfile contents. adminfile = _write_adminfile(kwargs) # Remove the package - cmd = ['/usr/sbin/pkgrm', '-n', '-a', adminfile] + targets - out = __salt__['cmd.run_all'](cmd, - python_shell=False, - output_loglevel='trace') + cmd = ["/usr/sbin/pkgrm", "-n", "-a", adminfile] + targets + out = __salt__["cmd.run_all"](cmd, python_shell=False, output_loglevel="trace") - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) finally: # Remove the temp adminfile - if 'admin_source' not in kwargs: + if "admin_source" not in kwargs: try: os.remove(adminfile) except (NameError, OSError): @@ -489,7 +493,7 @@ def remove(name=None, pkgs=None, saltenv='base', **kwargs): def purge(name=None, pkgs=None, **kwargs): - ''' + """ Package purges are not supported, this function is identical to ``remove()``. @@ -515,5 +519,5 @@ def purge(name=None, pkgs=None, **kwargs): salt '*' pkg.purge <package name> salt '*' pkg.purge <package1>,<package2>,<package3> salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' + """ return remove(name=name, pkgs=pkgs, **kwargs) diff --git a/salt/modules/solr.py b/salt/modules/solr.py index c977af99322..044349d03eb 100644 --- a/salt/modules/solr.py +++ b/salt/modules/solr.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Apache Solr Salt Module Author: Jed Glazner @@ -57,47 +57,55 @@ optimize : True Optimize the index after commit is complete verbose : True Get verbose output -''' +""" # Import python Libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import 3rd-party libs -# pylint: disable=no-name-in-module,import-error -from salt.ext import six -from salt.ext.six.moves.urllib.request import ( - urlopen as _urlopen, - HTTPBasicAuthHandler as _HTTPBasicAuthHandler, - HTTPDigestAuthHandler as _HTTPDigestAuthHandler, - build_opener as _build_opener, - install_opener as _install_opener -) -# pylint: enable=no-name-in-module,import-error +import os # Import salt libs import salt.utils.json import salt.utils.path +# Import 3rd-party libs +# pylint: disable=no-name-in-module,import-error +from salt.ext import six +from salt.ext.six.moves.urllib.request import ( + HTTPBasicAuthHandler as _HTTPBasicAuthHandler, +) +from salt.ext.six.moves.urllib.request import ( + HTTPDigestAuthHandler as _HTTPDigestAuthHandler, +) +from salt.ext.six.moves.urllib.request import build_opener as _build_opener +from salt.ext.six.moves.urllib.request import install_opener as _install_opener +from salt.ext.six.moves.urllib.request import urlopen as _urlopen + +# pylint: enable=no-name-in-module,import-error + + # ######################### PRIVATE METHODS ############################## def __virtual__(): - ''' + """ PRIVATE METHOD Solr needs to be installed to use this. Return: str/bool - ''' - if salt.utils.path.which('solr'): - return 'solr' - if salt.utils.path.which('apache-solr'): - return 'solr' - return (False, 'The solr execution module failed to load: requires both the solr and apache-solr binaries in the path.') + """ + if salt.utils.path.which("solr"): + return "solr" + if salt.utils.path.which("apache-solr"): + return "solr" + return ( + False, + "The solr execution module failed to load: requires both the solr and apache-solr binaries in the path.", + ) def _get_none_or_value(value): - ''' + """ PRIVATE METHOD Checks to see if the value of a primitive or built-in container such as a list, dict, set, tuple etc is empty or none. None type is returned if the @@ -107,14 +115,14 @@ def _get_none_or_value(value): The primitive or built-in container to evaluate. Return: None or value - ''' + """ if value is None: return None elif not value: return value # if it's a string, and it's not empty check for none elif isinstance(value, six.string_types): - if value.lower() == 'none': + if value.lower() == "none": return None return value # return None @@ -123,7 +131,7 @@ def _get_none_or_value(value): def _check_for_cores(): - ''' + """ PRIVATE METHOD Checks to see if using_cores has been set or not. if it's been set return it, otherwise figure it out and set it. Then return it @@ -131,12 +139,12 @@ def _check_for_cores(): Return: boolean True if one or more cores defined in __opts__['solr.cores'] - ''' - return len(__salt__['config.option']('solr.cores')) > 0 + """ + return len(__salt__["config.option"]("solr.cores")) > 0 def _get_return_dict(success=True, data=None, errors=None, warnings=None): - ''' + """ PRIVATE METHOD Creates a new return dict with default values. Defaults may be overwritten. @@ -152,20 +160,17 @@ def _get_return_dict(success=True, data=None, errors=None, warnings=None): Return: dict<str,obj>:: {'success':boolean, 'data':dict, 'errors':list, 'warnings':list} - ''' + """ data = {} if data is None else data errors = [] if errors is None else errors warnings = [] if warnings is None else warnings - ret = {'success': success, - 'data': data, - 'errors': errors, - 'warnings': warnings} + ret = {"success": success, "data": data, "errors": errors, "warnings": warnings} return ret def _update_return_dict(ret, success, data, errors=None, warnings=None): - ''' + """ PRIVATE METHOD Updates the return dictionary and returns it. @@ -184,18 +189,18 @@ def _update_return_dict(ret, success, data, errors=None, warnings=None): Return: dict<str,obj>:: {'success':boolean, 'data':dict, 'errors':list, 'warnings':list} - ''' + """ errors = [] if errors is None else errors warnings = [] if warnings is None else warnings - ret['success'] = success - ret['data'].update(data) - ret['errors'] = ret['errors'] + errors - ret['warnings'] = ret['warnings'] + warnings + ret["success"] = success + ret["data"].update(data) + ret["errors"] = ret["errors"] + errors + ret["warnings"] = ret["warnings"] + warnings return ret def _format_url(handler, host=None, core_name=None, extra=None): - ''' + """ PRIVATE METHOD Formats the URL based on parameters, and if cores are used or not @@ -211,52 +216,48 @@ def _format_url(handler, host=None, core_name=None, extra=None): Return: str Fully formatted URL (http://<host>:<port>/solr/<handler>?wt=json&<extra>) - ''' + """ extra = [] if extra is None else extra - if _get_none_or_value(host) is None or host == 'None': - host = __salt__['config.option']('solr.host') - port = __salt__['config.option']('solr.port') - baseurl = __salt__['config.option']('solr.baseurl') + if _get_none_or_value(host) is None or host == "None": + host = __salt__["config.option"]("solr.host") + port = __salt__["config.option"]("solr.port") + baseurl = __salt__["config.option"]("solr.baseurl") if _get_none_or_value(core_name) is None: if extra is None or len(extra) == 0: - return "http://{0}:{1}{2}/{3}?wt=json".format( - host, port, baseurl, handler) + return "http://{0}:{1}{2}/{3}?wt=json".format(host, port, baseurl, handler) else: return "http://{0}:{1}{2}/{3}?wt=json&{4}".format( - host, port, baseurl, handler, "&".join(extra)) + host, port, baseurl, handler, "&".join(extra) + ) else: if extra is None or len(extra) == 0: return "http://{0}:{1}{2}/{3}/{4}?wt=json".format( - host, port, baseurl, core_name, handler) + host, port, baseurl, core_name, handler + ) else: return "http://{0}:{1}{2}/{3}/{4}?wt=json&{5}".format( - host, port, baseurl, core_name, handler, "&".join(extra)) + host, port, baseurl, core_name, handler, "&".join(extra) + ) def _auth(url): - ''' + """ Install an auth handler for urllib2 - ''' - user = __salt__['config.get']('solr.user', False) - password = __salt__['config.get']('solr.passwd', False) - realm = __salt__['config.get']('solr.auth_realm', 'Solr') + """ + user = __salt__["config.get"]("solr.user", False) + password = __salt__["config.get"]("solr.passwd", False) + realm = __salt__["config.get"]("solr.auth_realm", "Solr") if user and password: basic = _HTTPBasicAuthHandler() - basic.add_password( - realm=realm, uri=url, user=user, passwd=password - ) + basic.add_password(realm=realm, uri=url, user=user, passwd=password) digest = _HTTPDigestAuthHandler() - digest.add_password( - realm=realm, uri=url, user=user, passwd=password - ) - _install_opener( - _build_opener(basic, digest) - ) + digest.add_password(realm=realm, uri=url, user=user, passwd=password) + _install_opener(_build_opener(basic, digest)) def _http_request(url, request_timeout=None): - ''' + """ PRIVATE METHOD Uses salt.utils.json.load to fetch the JSON results from the solr API. @@ -269,12 +270,12 @@ def _http_request(url, request_timeout=None): Return: dict<str,obj>:: {'success':boolean, 'data':dict, 'errors':list, 'warnings':list} - ''' + """ _auth(url) try: - request_timeout = __salt__['config.option']('solr.request_timeout') - kwargs = {} if request_timeout is None else {'timeout': request_timeout} + request_timeout = __salt__["config.option"]("solr.request_timeout") + kwargs = {} if request_timeout is None else {"timeout": request_timeout} data = salt.utils.json.load(_urlopen(url, **kwargs)) return _get_return_dict(True, data, []) except Exception as err: # pylint: disable=broad-except @@ -282,7 +283,7 @@ def _http_request(url, request_timeout=None): def _replication_request(command, host=None, core_name=None, params=None): - ''' + """ PRIVATE METHOD Performs the requested replication command and returns a dictionary with success, errors and data as keys. The data object will contain the JSON @@ -302,16 +303,15 @@ def _replication_request(command, host=None, core_name=None, params=None): Return: dict<str, obj>:: {'success':boolean, 'data':dict, 'errors':list, 'warnings':list} - ''' + """ params = [] if params is None else params extra = ["command={0}".format(command)] + params - url = _format_url('replication', host=host, core_name=core_name, - extra=extra) + url = _format_url("replication", host=host, core_name=core_name, extra=extra) return _http_request(url) def _get_admin_info(command, host=None, core_name=None): - ''' + """ PRIVATE METHOD Calls the _http_request method and passes the admin command to execute and stores the data. This data is fairly static but should be refreshed @@ -329,26 +329,26 @@ def _get_admin_info(command, host=None, core_name=None): Return: dict<str,obj>:: {'success':boolean, 'data':dict, 'errors':list, 'warnings':list} - ''' + """ url = _format_url("admin/{0}".format(command), host, core_name=core_name) resp = _http_request(url) return resp def _is_master(): - ''' + """ PRIVATE METHOD Simple method to determine if the minion is configured as master or slave Return: boolean:: True if __opts__['solr.type'] = master - ''' - return __salt__['config.option']('solr.type') == 'master' + """ + return __salt__["config.option"]("solr.type") == "master" def _merge_options(options): - ''' + """ PRIVATE METHOD updates the default import options from __opts__['solr.dih.import_options'] with the dictionary passed in. Also converts booleans to strings @@ -361,8 +361,8 @@ def _merge_options(options): Return: dict<str,boolean>:: {option:boolean} - ''' - defaults = __salt__['config.option']('solr.dih.import_options') + """ + defaults = __salt__["config.option"]("solr.dih.import_options") if isinstance(options, dict): defaults.update(options) for key, val in six.iteritems(defaults): @@ -372,7 +372,7 @@ def _merge_options(options): def _pre_index_check(handler, host=None, core_name=None): - ''' + """ PRIVATE METHOD - MASTER CALL Does a pre-check to make sure that all the options are set and that we can talk to solr before trying to send a command to solr. This @@ -390,37 +390,36 @@ def _pre_index_check(handler, host=None, core_name=None): Return: dict<str,obj>:: {'success':boolean, 'data':dict, 'errors':list, 'warnings':list} - ''' + """ # make sure that it's a master minion if _get_none_or_value(host) is None and not _is_master(): - err = [ - 'solr.pre_indexing_check can only be called by "master" minions'] + err = ['solr.pre_indexing_check can only be called by "master" minions'] return _get_return_dict(False, err) # solr can run out of memory quickly if the dih is processing multiple # handlers at the same time, so if it's a multicore setup require a # core_name param. if _get_none_or_value(core_name) is None and _check_for_cores(): - errors = ['solr.full_import is not safe to multiple handlers at once'] + errors = ["solr.full_import is not safe to multiple handlers at once"] return _get_return_dict(False, errors=errors) # check to make sure that we're not already indexing resp = import_status(handler, host, core_name) - if resp['success']: - status = resp['data']['status'] - if status == 'busy': - warn = ['An indexing process is already running.'] + if resp["success"]: + status = resp["data"]["status"] + if status == "busy": + warn = ["An indexing process is already running."] return _get_return_dict(True, warnings=warn) - if status != 'idle': + if status != "idle": errors = ['Unknown status: "{0}"'.format(status)] - return _get_return_dict(False, data=resp['data'], errors=errors) + return _get_return_dict(False, data=resp["data"], errors=errors) else: - errors = ['Status check failed. Response details: {0}'.format(resp)] - return _get_return_dict(False, data=resp['data'], errors=errors) + errors = ["Status check failed. Response details: {0}".format(resp)] + return _get_return_dict(False, data=resp["data"], errors=errors) return resp def _find_value(ret_dict, key, path=None): - ''' + """ PRIVATE METHOD Traverses a dictionary of dictionaries/lists to find key and return the value stored. @@ -437,7 +436,7 @@ def _find_value(ret_dict, key, path=None): Return: list<dict<str,obj>>:: [{path:path, value:value}] - ''' + """ if path is None: path = key else: @@ -458,8 +457,9 @@ def _find_value(ret_dict, key, path=None): # ######################### PUBLIC METHODS ############################## + def lucene_version(core_name=None): - ''' + """ Gets the lucene version that solr is using. If you are running a multi-core setup you should specify a core name since all the cores run under the same servlet container, they will all have the same version. @@ -477,32 +477,32 @@ def lucene_version(core_name=None): .. code-block:: bash salt '*' solr.lucene_version - ''' + """ ret = _get_return_dict() # do we want to check for all the cores? if _get_none_or_value(core_name) is None and _check_for_cores(): success = True - for name in __salt__['config.option']('solr.cores'): - resp = _get_admin_info('system', core_name=name) - if resp['success']: - version_num = resp['data']['lucene']['lucene-spec-version'] - data = {name: {'lucene_version': version_num}} + for name in __salt__["config.option"]("solr.cores"): + resp = _get_admin_info("system", core_name=name) + if resp["success"]: + version_num = resp["data"]["lucene"]["lucene-spec-version"] + data = {name: {"lucene_version": version_num}} else: # generally this means that an exception happened. - data = {name: {'lucene_version': None}} + data = {name: {"lucene_version": None}} success = False - ret = _update_return_dict(ret, success, data, resp['errors']) + ret = _update_return_dict(ret, success, data, resp["errors"]) return ret else: - resp = _get_admin_info('system', core_name=core_name) - if resp['success']: - version_num = resp['data']['lucene']['lucene-spec-version'] - return _get_return_dict(True, {'version': version_num}, resp['errors']) + resp = _get_admin_info("system", core_name=core_name) + if resp["success"]: + version_num = resp["data"]["lucene"]["lucene-spec-version"] + return _get_return_dict(True, {"version": version_num}, resp["errors"]) else: return resp def version(core_name=None): - ''' + """ Gets the solr version for the core specified. You should specify a core here as all the cores will run under the same servlet container and so will all have the same version. @@ -520,34 +520,36 @@ def version(core_name=None): .. code-block:: bash salt '*' solr.version - ''' + """ ret = _get_return_dict() # do we want to check for all the cores? if _get_none_or_value(core_name) is None and _check_for_cores(): success = True - for name in __opts__['solr.cores']: - resp = _get_admin_info('system', core_name=name) - if resp['success']: - lucene = resp['data']['lucene'] - data = {name: {'version': lucene['solr-spec-version']}} + for name in __opts__["solr.cores"]: + resp = _get_admin_info("system", core_name=name) + if resp["success"]: + lucene = resp["data"]["lucene"] + data = {name: {"version": lucene["solr-spec-version"]}} else: success = False - data = {name: {'version': None}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + data = {name: {"version": None}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) return ret else: - resp = _get_admin_info('system', core_name=core_name) - if resp['success']: - version_num = resp['data']['lucene']['solr-spec-version'] - return _get_return_dict(True, {'version': version_num}, - resp['errors'], resp['warnings']) + resp = _get_admin_info("system", core_name=core_name) + if resp["success"]: + version_num = resp["data"]["lucene"]["solr-spec-version"] + return _get_return_dict( + True, {"version": version_num}, resp["errors"], resp["warnings"] + ) else: return resp def optimize(host=None, core_name=None): - ''' + """ Search queries fast, but it is a very expensive operation. The ideal process is to run this with a master/slave configuration. Then you can optimize the master, and push the optimized index to the slaves. @@ -572,33 +574,37 @@ def optimize(host=None, core_name=None): .. code-block:: bash salt '*' solr.optimize music - ''' + """ ret = _get_return_dict() if _get_none_or_value(core_name) is None and _check_for_cores(): success = True - for name in __salt__['config.option']('solr.cores'): - url = _format_url('update', host=host, core_name=name, - extra=["optimize=true"]) + for name in __salt__["config.option"]("solr.cores"): + url = _format_url( + "update", host=host, core_name=name, extra=["optimize=true"] + ) resp = _http_request(url) - if resp['success']: - data = {name: {'data': resp['data']}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + if resp["success"]: + data = {name: {"data": resp["data"]}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) else: success = False - data = {name: {'data': resp['data']}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + data = {name: {"data": resp["data"]}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) return ret else: - url = _format_url('update', host=host, core_name=core_name, - extra=["optimize=true"]) + url = _format_url( + "update", host=host, core_name=core_name, extra=["optimize=true"] + ) return _http_request(url) def ping(host=None, core_name=None): - ''' + """ Does a health check on solr, makes sure solr can talk to the indexes. host : str (None) @@ -616,26 +622,26 @@ def ping(host=None, core_name=None): .. code-block:: bash salt '*' solr.ping music - ''' + """ ret = _get_return_dict() if _get_none_or_value(core_name) is None and _check_for_cores(): success = True - for name in __opts__['solr.cores']: - resp = _get_admin_info('ping', host=host, core_name=name) - if resp['success']: - data = {name: {'status': resp['data']['status']}} + for name in __opts__["solr.cores"]: + resp = _get_admin_info("ping", host=host, core_name=name) + if resp["success"]: + data = {name: {"status": resp["data"]["status"]}} else: success = False - data = {name: {'status': None}} - ret = _update_return_dict(ret, success, data, resp['errors']) + data = {name: {"status": None}} + ret = _update_return_dict(ret, success, data, resp["errors"]) return ret else: - resp = _get_admin_info('ping', host=host, core_name=core_name) + resp = _get_admin_info("ping", host=host, core_name=core_name) return resp def is_replication_enabled(host=None, core_name=None): - ''' + """ SLAVE CALL Check for errors, and determine if a slave is replicating or not. @@ -654,62 +660,60 @@ def is_replication_enabled(host=None, core_name=None): .. code-block:: bash salt '*' solr.is_replication_enabled music - ''' + """ ret = _get_return_dict() success = True # since only slaves can call this let's check the config: if _is_master() and host is None: errors = ['Only "slave" minions can run "is_replication_enabled"'] - return ret.update({'success': False, 'errors': errors}) + return ret.update({"success": False, "errors": errors}) # define a convenience method so we don't duplicate code def _checks(ret, success, resp, core): - if response['success']: - slave = resp['data']['details']['slave'] + if response["success"]: + slave = resp["data"]["details"]["slave"] # we need to initialize this to false in case there is an error # on the master and we can't get this info. - enabled = 'false' - master_url = slave['masterUrl'] + enabled = "false" + master_url = slave["masterUrl"] # check for errors on the slave - if 'ERROR' in slave: + if "ERROR" in slave: success = False - err = "{0}: {1} - {2}".format(core, slave['ERROR'], master_url) - resp['errors'].append(err) + err = "{0}: {1} - {2}".format(core, slave["ERROR"], master_url) + resp["errors"].append(err) # if there is an error return everything - data = slave if core is None else {core: {'data': slave}} + data = slave if core is None else {core: {"data": slave}} else: - enabled = slave['masterDetails']['master'][ - 'replicationEnabled'] + enabled = slave["masterDetails"]["master"]["replicationEnabled"] # if replication is turned off on the master, or polling is # disabled we need to return false. These may not be errors, # but the purpose of this call is to check to see if the slaves # can replicate. - if enabled == 'false': - resp['warnings'].append("Replication is disabled on master.") + if enabled == "false": + resp["warnings"].append("Replication is disabled on master.") success = False - if slave['isPollingDisabled'] == 'true': + if slave["isPollingDisabled"] == "true": success = False - resp['warning'].append("Polling is disabled") + resp["warning"].append("Polling is disabled") # update the return - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) return (ret, success) if _get_none_or_value(core_name) is None and _check_for_cores(): - for name in __opts__['solr.cores']: - response = _replication_request('details', host=host, - core_name=name) + for name in __opts__["solr.cores"]: + response = _replication_request("details", host=host, core_name=name) ret, success = _checks(ret, success, response, name) else: - response = _replication_request('details', host=host, - core_name=core_name) + response = _replication_request("details", host=host, core_name=core_name) ret, success = _checks(ret, success, response, core_name) return ret def match_index_versions(host=None, core_name=None): - ''' + """ SLAVE CALL Verifies that the master and the slave versions are in sync by comparing the index version. If you are constantly pushing updates @@ -732,76 +736,76 @@ def match_index_versions(host=None, core_name=None): .. code-block:: bash salt '*' solr.match_index_versions music - ''' + """ # since only slaves can call this let's check the config: ret = _get_return_dict() success = True if _is_master() and _get_none_or_value(host) is None: - return ret.update({ - 'success': False, - 'errors': [ - 'solr.match_index_versions can only be called by ' - '"slave" minions' - ] - }) + return ret.update( + { + "success": False, + "errors": [ + 'solr.match_index_versions can only be called by "slave" minions' + ], + } + ) # get the default return dict def _match(ret, success, resp, core): - if response['success']: - slave = resp['data']['details']['slave'] - master_url = resp['data']['details']['slave']['masterUrl'] - if 'ERROR' in slave: - error = slave['ERROR'] + if response["success"]: + slave = resp["data"]["details"]["slave"] + master_url = resp["data"]["details"]["slave"]["masterUrl"] + if "ERROR" in slave: + error = slave["ERROR"] success = False err = "{0}: {1} - {2}".format(core, error, master_url) - resp['errors'].append(err) + resp["errors"].append(err) # if there was an error return the entire response so the # alterer can get what it wants - data = slave if core is None else {core: {'data': slave}} + data = slave if core is None else {core: {"data": slave}} else: versions = { - 'master': slave['masterDetails']['master'][ - 'replicatableIndexVersion'], - 'slave': resp['data']['details']['indexVersion'], - 'next_replication': slave['nextExecutionAt'], - 'failed_list': [] - } - if 'replicationFailedAtList' in slave: - versions.update({'failed_list': slave[ - 'replicationFailedAtList']}) + "master": slave["masterDetails"]["master"][ + "replicatableIndexVersion" + ], + "slave": resp["data"]["details"]["indexVersion"], + "next_replication": slave["nextExecutionAt"], + "failed_list": [], + } + if "replicationFailedAtList" in slave: + versions.update({"failed_list": slave["replicationFailedAtList"]}) # check the index versions - if versions['master'] != versions['slave']: + if versions["master"] != versions["slave"]: success = False - resp['errors'].append( - 'Master and Slave index versions do not match.' + resp["errors"].append( + "Master and Slave index versions do not match." ) - data = versions if core is None else {core: {'data': versions}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + data = versions if core is None else {core: {"data": versions}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) else: success = False - err = resp['errors'] - data = resp['data'] + err = resp["errors"] + data = resp["data"] ret = _update_return_dict(ret, success, data, errors=err) return (ret, success) # check all cores? if _get_none_or_value(core_name) is None and _check_for_cores(): success = True - for name in __opts__['solr.cores']: - response = _replication_request('details', host=host, - core_name=name) + for name in __opts__["solr.cores"]: + response = _replication_request("details", host=host, core_name=name) ret, success = _match(ret, success, response, name) else: - response = _replication_request('details', host=host, - core_name=core_name) + response = _replication_request("details", host=host, core_name=core_name) ret, success = _match(ret, success, response, core_name) return ret def replication_details(host=None, core_name=None): - ''' + """ Get the full replication details. host : str (None) @@ -819,27 +823,29 @@ def replication_details(host=None, core_name=None): .. code-block:: bash salt '*' solr.replication_details music - ''' + """ ret = _get_return_dict() if _get_none_or_value(core_name) is None: success = True - for name in __opts__['solr.cores']: - resp = _replication_request('details', host=host, core_name=name) - data = {name: {'data': resp['data']}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + for name in __opts__["solr.cores"]: + resp = _replication_request("details", host=host, core_name=name) + data = {name: {"data": resp["data"]}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) else: - resp = _replication_request('details', host=host, core_name=core_name) - if resp['success']: - ret = _update_return_dict(ret, resp['success'], resp['data'], - resp['errors'], resp['warnings']) + resp = _replication_request("details", host=host, core_name=core_name) + if resp["success"]: + ret = _update_return_dict( + ret, resp["success"], resp["data"], resp["errors"], resp["warnings"] + ) else: return resp return ret def backup(host=None, core_name=None, append_core_to_path=False): - ''' + """ Tell solr make a backup. This method can be mis-leading since it uses the backup API. If an error happens during the backup you are not notified. The status: 'OK' in the response simply means that solr received the @@ -863,9 +869,9 @@ def backup(host=None, core_name=None, append_core_to_path=False): .. code-block:: bash salt '*' solr.backup music - ''' - path = __opts__['solr.backup_path'] - num_backups = __opts__['solr.num_backups'] + """ + path = __opts__["solr.backup_path"] + num_backups = __opts__["solr.num_backups"] if path is not None: if not path.endswith(os.path.sep): path += os.path.sep @@ -873,19 +879,21 @@ def backup(host=None, core_name=None, append_core_to_path=False): ret = _get_return_dict() if _get_none_or_value(core_name) is None and _check_for_cores(): success = True - for name in __opts__['solr.cores']: + for name in __opts__["solr.cores"]: params = [] if path is not None: path = path + name if append_core_to_path else path params.append("&location={0}".format(path + name)) params.append("&numberToKeep={0}".format(num_backups)) - resp = _replication_request('backup', host=host, core_name=name, - params=params) - if not resp['success']: + resp = _replication_request( + "backup", host=host, core_name=name, params=params + ) + if not resp["success"]: success = False - data = {name: {'data': resp['data']}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + data = {name: {"data": resp["data"]}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) return ret else: if core_name is not None and path is not None: @@ -894,13 +902,14 @@ def backup(host=None, core_name=None, append_core_to_path=False): if path is not None: params = ["location={0}".format(path)] params.append("&numberToKeep={0}".format(num_backups)) - resp = _replication_request('backup', host=host, core_name=core_name, - params=params) + resp = _replication_request( + "backup", host=host, core_name=core_name, params=params + ) return resp def set_is_polling(polling, host=None, core_name=None): - ''' + """ SLAVE CALL Prevent the slaves from polling the master for updates. @@ -921,24 +930,25 @@ def set_is_polling(polling, host=None, core_name=None): .. code-block:: bash salt '*' solr.set_is_polling False - ''' + """ ret = _get_return_dict() # since only slaves can call this let's check the config: if _is_master() and _get_none_or_value(host) is None: err = ['solr.set_is_polling can only be called by "slave" minions'] - return ret.update({'success': False, 'errors': err}) + return ret.update({"success": False, "errors": err}) cmd = "enablepoll" if polling else "disapblepoll" if _get_none_or_value(core_name) is None and _check_for_cores(): success = True - for name in __opts__['solr.cores']: + for name in __opts__["solr.cores"]: resp = set_is_polling(cmd, host=host, core_name=name) - if not resp['success']: + if not resp["success"]: success = False - data = {name: {'data': resp['data']}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + data = {name: {"data": resp["data"]}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) return ret else: resp = _replication_request(cmd, host=host, core_name=core_name) @@ -946,7 +956,7 @@ def set_is_polling(polling, host=None, core_name=None): def set_replication_enabled(status, host=None, core_name=None): - ''' + """ MASTER ONLY Sets the master to ignore poll requests from the slaves. Useful when you don't want the slaves replicating during indexing or when clearing the @@ -970,21 +980,23 @@ def set_replication_enabled(status, host=None, core_name=None): .. code-block:: bash salt '*' solr.set_replication_enabled false, None, music - ''' + """ if not _is_master() and _get_none_or_value(host) is None: - return _get_return_dict(False, - errors=['Only minions configured as master can run this']) - cmd = 'enablereplication' if status else 'disablereplication' + return _get_return_dict( + False, errors=["Only minions configured as master can run this"] + ) + cmd = "enablereplication" if status else "disablereplication" if _get_none_or_value(core_name) is None and _check_for_cores(): ret = _get_return_dict() success = True - for name in __opts__['solr.cores']: + for name in __opts__["solr.cores"]: resp = set_replication_enabled(status, host, name) - if not resp['success']: + if not resp["success"]: success = False - data = {name: {'data': resp['data']}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + data = {name: {"data": resp["data"]}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) return ret else: if status: @@ -994,7 +1006,7 @@ def set_replication_enabled(status, host=None, core_name=None): def signal(signal=None): - ''' + """ Signals Apache Solr to start, stop, or restart. Obviously this is only going to work if the minion resides on the solr host. Additionally Solr doesn't ship with an init script so one must be created. @@ -1008,22 +1020,23 @@ def signal(signal=None): .. code-block:: bash salt '*' solr.signal restart - ''' - valid_signals = ('start', 'stop', 'restart') + """ + valid_signals = ("start", "stop", "restart") # Give a friendly error message for invalid signals # TODO: Fix this logic to be reusable and used by apache.signal if signal not in valid_signals: - msg = valid_signals[:-1] + ('or {0}'.format(valid_signals[-1]),) - return '{0} is an invalid signal. Try: one of: {1}'.format( - signal, ', '.join(msg)) + msg = valid_signals[:-1] + ("or {0}".format(valid_signals[-1]),) + return "{0} is an invalid signal. Try: one of: {1}".format( + signal, ", ".join(msg) + ) - cmd = "{0} {1}".format(__opts__['solr.init_script'], signal) - __salt__['cmd.run'](cmd, python_shell=False) + cmd = "{0} {1}".format(__opts__["solr.init_script"], signal) + __salt__["cmd.run"](cmd, python_shell=False) def reload_core(host=None, core_name=None): - ''' + """ MULTI-CORE HOSTS ONLY Load a new core from the same configuration as an existing registered core. While the "new" core is initializing, the "old" one will continue to accept @@ -1048,29 +1061,30 @@ def reload_core(host=None, core_name=None): Return data is in the following format:: {'success':bool, 'data':dict, 'errors':list, 'warnings':list} - ''' + """ ret = _get_return_dict() if not _check_for_cores(): err = ['solr.reload_core can only be called by "multi-core" minions'] - return ret.update({'success': False, 'errors': err}) + return ret.update({"success": False, "errors": err}) if _get_none_or_value(core_name) is None and _check_for_cores(): success = True - for name in __opts__['solr.cores']: + for name in __opts__["solr.cores"]: resp = reload_core(host, name) - if not resp['success']: + if not resp["success"]: success = False - data = {name: {'data': resp['data']}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + data = {name: {"data": resp["data"]}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) return ret - extra = ['action=RELOAD', 'core={0}'.format(core_name)] - url = _format_url('admin/cores', host=host, core_name=None, extra=extra) + extra = ["action=RELOAD", "core={0}".format(core_name)] + url = _format_url("admin/cores", host=host, core_name=None, extra=extra) return _http_request(url) def core_status(host=None, core_name=None): - ''' + """ MULTI-CORE HOSTS ONLY Get the status for a given core or all cores if no core is specified @@ -1088,31 +1102,33 @@ def core_status(host=None, core_name=None): .. code-block:: bash salt '*' solr.core_status None music - ''' + """ ret = _get_return_dict() if not _check_for_cores(): err = ['solr.reload_core can only be called by "multi-core" minions'] - return ret.update({'success': False, 'errors': err}) + return ret.update({"success": False, "errors": err}) if _get_none_or_value(core_name) is None and _check_for_cores(): success = True - for name in __opts__['solr.cores']: + for name in __opts__["solr.cores"]: resp = reload_core(host, name) - if not resp['success']: + if not resp["success"]: success = False - data = {name: {'data': resp['data']}} - ret = _update_return_dict(ret, success, data, - resp['errors'], resp['warnings']) + data = {name: {"data": resp["data"]}} + ret = _update_return_dict( + ret, success, data, resp["errors"], resp["warnings"] + ) return ret - extra = ['action=STATUS', 'core={0}'.format(core_name)] - url = _format_url('admin/cores', host=host, core_name=None, extra=extra) + extra = ["action=STATUS", "core={0}".format(core_name)] + url = _format_url("admin/cores", host=host, core_name=None, extra=extra) return _http_request(url) # ################## DIH (Direct Import Handler) COMMANDS ##################### + def reload_import_config(handler, host=None, core_name=None, verbose=False): - ''' + """ MASTER ONLY re-loads the handler config XML file. This command can only be run if the minion is a 'master' type @@ -1135,19 +1151,18 @@ def reload_import_config(handler, host=None, core_name=None, verbose=False): .. code-block:: bash salt '*' solr.reload_import_config dataimport None music {'clean':True} - ''' + """ # make sure that it's a master minion if not _is_master() and _get_none_or_value(host) is None: - err = [ - 'solr.pre_indexing_check can only be called by "master" minions'] + err = ['solr.pre_indexing_check can only be called by "master" minions'] return _get_return_dict(False, err) if _get_none_or_value(core_name) is None and _check_for_cores(): err = ['No core specified when minion is configured as "multi-core".'] return _get_return_dict(False, err) - params = ['command=reload-config'] + params = ["command=reload-config"] if verbose: params.append("verbose=true") url = _format_url(handler, host=host, core_name=core_name, extra=params) @@ -1155,7 +1170,7 @@ def reload_import_config(handler, host=None, core_name=None, verbose=False): def abort_import(handler, host=None, core_name=None, verbose=False): - ''' + """ MASTER ONLY Aborts an existing import command to the specified handler. This command can only be run if the minion is configured with @@ -1179,7 +1194,7 @@ def abort_import(handler, host=None, core_name=None, verbose=False): .. code-block:: bash salt '*' solr.abort_import dataimport None music {'clean':True} - ''' + """ if not _is_master() and _get_none_or_value(host) is None: err = ['solr.abort_import can only be called on "master" minions'] return _get_return_dict(False, errors=err) @@ -1188,7 +1203,7 @@ def abort_import(handler, host=None, core_name=None, verbose=False): err = ['No core specified when minion is configured as "multi-core".'] return _get_return_dict(False, err) - params = ['command=abort'] + params = ["command=abort"] if verbose: params.append("verbose=true") url = _format_url(handler, host=host, core_name=core_name, extra=params) @@ -1196,7 +1211,7 @@ def abort_import(handler, host=None, core_name=None, verbose=False): def full_import(handler, host=None, core_name=None, options=None, extra=None): - ''' + """ MASTER ONLY Submits an import command to the specified handler using specified options. This command can only be run if the minion is configured with @@ -1224,7 +1239,7 @@ def full_import(handler, host=None, core_name=None, options=None, extra=None): .. code-block:: bash salt '*' solr.full_import dataimport None music {'clean':True} - ''' + """ options = {} if options is None else options extra = [] if extra is None else extra if not _is_master(): @@ -1236,24 +1251,23 @@ def full_import(handler, host=None, core_name=None, options=None, extra=None): return _get_return_dict(False, err) resp = _pre_index_check(handler, host, core_name) - if not resp['success']: + if not resp["success"]: return resp options = _merge_options(options) - if options['clean']: + if options["clean"]: resp = set_replication_enabled(False, host=host, core_name=core_name) - if not resp['success']: - errors = ['Failed to set the replication status on the master.'] + if not resp["success"]: + errors = ["Failed to set the replication status on the master."] return _get_return_dict(False, errors=errors) - params = ['command=full-import'] + params = ["command=full-import"] for key, val in six.iteritems(options): - params.append('&{0}={1}'.format(key, val)) - url = _format_url(handler, host=host, core_name=core_name, - extra=params + extra) + params.append("&{0}={1}".format(key, val)) + url = _format_url(handler, host=host, core_name=core_name, extra=params + extra) return _http_request(url) def delta_import(handler, host=None, core_name=None, options=None, extra=None): - ''' + """ Submits an import command to the specified handler using specified options. This command can only be run if the minion is configured with solr.type=master @@ -1281,7 +1295,7 @@ def delta_import(handler, host=None, core_name=None, options=None, extra=None): .. code-block:: bash salt '*' solr.delta_import dataimport None music {'clean':True} - ''' + """ options = {} if options is None else options extra = [] if extra is None else extra if not _is_master() and _get_none_or_value(host) is None: @@ -1289,25 +1303,24 @@ def delta_import(handler, host=None, core_name=None, options=None, extra=None): return _get_return_dict(False, errors=err) resp = _pre_index_check(handler, host=host, core_name=core_name) - if not resp['success']: + if not resp["success"]: return resp options = _merge_options(options) # if we're nuking data, and we're multi-core disable replication for safety - if options['clean'] and _check_for_cores(): + if options["clean"] and _check_for_cores(): resp = set_replication_enabled(False, host=host, core_name=core_name) - if not resp['success']: - errors = ['Failed to set the replication status on the master.'] + if not resp["success"]: + errors = ["Failed to set the replication status on the master."] return _get_return_dict(False, errors=errors) - params = ['command=delta-import'] + params = ["command=delta-import"] for key, val in six.iteritems(options): params.append("{0}={1}".format(key, val)) - url = _format_url(handler, host=host, core_name=core_name, - extra=params + extra) + url = _format_url(handler, host=host, core_name=core_name, extra=params + extra) return _http_request(url) def import_status(handler, host=None, core_name=None, verbose=False): - ''' + """ Submits an import command to the specified handler using specified options. This command can only be run if the minion is configured with solr.type: 'master' @@ -1330,7 +1343,7 @@ def import_status(handler, host=None, core_name=None, verbose=False): .. code-block:: bash salt '*' solr.import_status dataimport None music False - ''' + """ if not _is_master() and _get_none_or_value(host) is None: errors = ['solr.import_status can only be called by "master" minions'] return _get_return_dict(False, errors=errors) diff --git a/salt/modules/solrcloud.py b/salt/modules/solrcloud.py index 59a4967a279..208b2d8f39f 100644 --- a/salt/modules/solrcloud.py +++ b/salt/modules/solrcloud.py @@ -1,55 +1,77 @@ # -*- coding: utf-8 -*- -''' +""" Module for solrcloud configuration .. versionadded:: 2017.7.0 For now, module is limited to http-exposed API. It doesn't implement config upload via Solr zkCli -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -from salt.exceptions import SaltInvocationError import salt.utils.http as http +from salt.exceptions import SaltInvocationError from salt.ext import six # Import salt libs log = logging.getLogger(__name__) -''' +""" Core properties type definition. Reference: https://cwiki.apache.org/confluence/display/solr/Defining+core.properties -''' -STRING_PROPS_LIST = ['config', 'schema', 'dataDir', 'configSet', 'properties', 'coreNodeName', 'ulogDir', 'shard', - 'collection', 'roles'] -BOOL_PROPS_LIST = ['transient', 'loadOnStartup'] +""" +STRING_PROPS_LIST = [ + "config", + "schema", + "dataDir", + "configSet", + "properties", + "coreNodeName", + "ulogDir", + "shard", + "collection", + "roles", +] +BOOL_PROPS_LIST = ["transient", "loadOnStartup"] -''' +""" Collections options type definition Reference: https://cwiki.apache.org/confluence/display/solr/Collections+API#CollectionsAPI-api1 -''' -STRING_OPTIONS_LIST = ['collection.configName', 'router.field', 'async', 'rule', 'snitch'] -INT_OPTIONS_LIST = ['numShards', 'replicationFactor', 'maxShardsPerNode'] -BOOL_OPTIONS_LIST = ['autoAddReplicas', 'createNodeSet.shuffle'] -LIST_OPTIONS_LIST = ['shards', 'createNodeSet'] -DICT_OPTIONS_LIST = ['properties'] +""" +STRING_OPTIONS_LIST = [ + "collection.configName", + "router.field", + "async", + "rule", + "snitch", +] +INT_OPTIONS_LIST = ["numShards", "replicationFactor", "maxShardsPerNode"] +BOOL_OPTIONS_LIST = ["autoAddReplicas", "createNodeSet.shuffle"] +LIST_OPTIONS_LIST = ["shards", "createNodeSet"] +DICT_OPTIONS_LIST = ["properties"] -''' +""" Collection unmodifiable options Reference: https://cwiki.apache.org/confluence/display/solr/Collections+API#CollectionsAPI-modifycoll -''' -CREATION_ONLY_OPTION = ['maxShardsPerNode', 'replicationFactor', 'autoAddReplicas', 'collection.configName', - 'rule', 'snitch'] +""" +CREATION_ONLY_OPTION = [ + "maxShardsPerNode", + "replicationFactor", + "autoAddReplicas", + "collection.configName", + "rule", + "snitch", +] def __virtual__(): - return 'solrcloud' + return "solrcloud" -def _query(url, solr_url='http://localhost:8983/solr/', **kwargs): - ''' +def _query(url, solr_url="http://localhost:8983/solr/", **kwargs): + """ Internal function to query solrcloud @@ -59,29 +81,31 @@ def _query(url, solr_url='http://localhost:8983/solr/', **kwargs): :return: Query JSON answer converted to python dict :rtype: dict - ''' + """ if not isinstance(solr_url, six.string_types): - raise ValueError('solr_url must be a string') + raise ValueError("solr_url must be a string") - if solr_url[-1:] != '/': - solr_url = solr_url + '/' + if solr_url[-1:] != "/": + solr_url = solr_url + "/" - query_result = http.query(solr_url+url, decode_type='json', decode=True, raise_error=True, **kwargs) - if 'error' in query_result: + query_result = http.query( + solr_url + url, decode_type="json", decode=True, raise_error=True, **kwargs + ) + if "error" in query_result: if query_result["status"] == 404: raise SaltInvocationError( - 'Got a 404 when trying to contact solr at {solr_url}{url}. Please check your solr URL.'. - format(solr_url=solr_url, url=url) + "Got a 404 when trying to contact solr at {solr_url}{url}. Please check your solr URL.".format( + solr_url=solr_url, url=url + ) ) else: raise SaltInvocationError( - 'Got a {status} error when calling {solr_url}{url} : {error}'. - format( + "Got a {status} error when calling {solr_url}{url} : {error}".format( status=six.text_type(query_result["status"]), solr_url=solr_url, url=url, - error=query_result["error"] + error=query_result["error"], ) ) else: @@ -89,79 +113,109 @@ def _query(url, solr_url='http://localhost:8983/solr/', **kwargs): def _validate_core_properties(properties): - ''' + """ Internal function to validate core properties - ''' + """ props_string = "" for prop_name, prop_value in six.iteritems(properties): if prop_name in BOOL_PROPS_LIST: if not isinstance(prop_value, bool): - raise ValueError('Option "'+prop_name+'" value must be an boolean') + raise ValueError('Option "' + prop_name + '" value must be an boolean') - props_string = props_string+"&property."+prop_name+"="+("true" if prop_value else "false") + props_string = ( + props_string + + "&property." + + prop_name + + "=" + + ("true" if prop_value else "false") + ) elif prop_name in STRING_PROPS_LIST: if not isinstance(prop_value, six.string_types): - raise ValueError('In option "properties", core property "'+prop_name+'" value must be a string') + raise ValueError( + 'In option "properties", core property "' + + prop_name + + '" value must be a string' + ) - props_string = props_string+"&property."+prop_name+"="+prop_value + props_string = props_string + "&property." + prop_name + "=" + prop_value else: - props_string = props_string+"&property."+six.text_type(prop_name)+"="+six.text_type(prop_value) + props_string = ( + props_string + + "&property." + + six.text_type(prop_name) + + "=" + + six.text_type(prop_value) + ) return props_string def _validate_collection_options(options): - ''' + """ Internal function to validate collections options - ''' + """ options_string = "" for option_name, option_value in six.iteritems(options): if option_name in STRING_OPTIONS_LIST: if not isinstance(option_value, six.string_types): - raise ValueError('Option "'+option_name+'" value must be a string') + raise ValueError('Option "' + option_name + '" value must be a string') - options_string = options_string+"&"+option_name+"="+option_value + options_string = options_string + "&" + option_name + "=" + option_value elif option_name in INT_OPTIONS_LIST: if not isinstance(option_value, six.integer_types): - raise ValueError('Option "'+option_name+'" value must be an int') + raise ValueError('Option "' + option_name + '" value must be an int') - options_string = options_string+"&"+option_name+"="+six.text_type(option_value) + options_string = ( + options_string + "&" + option_name + "=" + six.text_type(option_value) + ) elif option_name in BOOL_OPTIONS_LIST: if not isinstance(option_value, bool): - raise ValueError('Option "'+option_name+'" value must be an boolean') + raise ValueError( + 'Option "' + option_name + '" value must be an boolean' + ) - options_string = options_string+"&"+option_name+"="+("true" if option_value else "false") + options_string = ( + options_string + + "&" + + option_name + + "=" + + ("true" if option_value else "false") + ) elif option_name in LIST_OPTIONS_LIST: if not isinstance(option_value, list): - raise ValueError('Option "'+option_name+'" value must be a list of strings') + raise ValueError( + 'Option "' + option_name + '" value must be a list of strings' + ) - options_string = options_string+"&"+option_name+"="+(", ".join(option_value)) + options_string = ( + options_string + "&" + option_name + "=" + (", ".join(option_value)) + ) elif option_name in DICT_OPTIONS_LIST: if not isinstance(option_value, dict): - raise ValueError('Option "'+option_name+'" value must be an dict') + raise ValueError('Option "' + option_name + '" value must be an dict') - options_string = options_string+_validate_core_properties(option_value) + options_string = options_string + _validate_core_properties(option_value) else: - raise ValueError('Unknown option "'+option_name+'"') + raise ValueError('Unknown option "' + option_name + '"') return options_string def collection_creation_options(): - ''' + """ Get collection option list that can only be defined at creation @@ -170,13 +224,13 @@ def collection_creation_options(): .. code-block:: bash salt '*' solrcloud.collection_creation_options - ''' + """ return CREATION_ONLY_OPTION def cluster_status(**kwargs): - ''' + """ Get cluster status @@ -187,13 +241,13 @@ def cluster_status(**kwargs): .. code-block:: bash salt '*' solrcloud.cluster_status - ''' + """ - return _query('admin/collections?action=CLUSTERSTATUS&wt=json', **kwargs)["cluster"] + return _query("admin/collections?action=CLUSTERSTATUS&wt=json", **kwargs)["cluster"] def alias_exists(alias_name, **kwargs): - ''' + """ Check alias existence @@ -204,10 +258,10 @@ def alias_exists(alias_name, **kwargs): .. code-block:: bash salt '*' solrcloud.alias_exists my_alias - ''' + """ if not isinstance(alias_name, six.string_types): - raise ValueError('Alias name must be a string') + raise ValueError("Alias name must be a string") cluster = cluster_status(**kwargs) @@ -215,7 +269,7 @@ def alias_exists(alias_name, **kwargs): def alias_get_collections(alias_name, **kwargs): - ''' + """ Get collection list for an alias @@ -226,22 +280,25 @@ def alias_get_collections(alias_name, **kwargs): .. code-block:: bash salt '*' solrcloud.alias_get my_alias - ''' + """ if not isinstance(alias_name, six.string_types): - raise ValueError('Alias name must be a string') + raise ValueError("Alias name must be a string") collection_aliases = [ (k_v[0], k_v[1]["aliases"]) for k_v in six.iteritems(cluster_status(**kwargs)["collections"]) - if "aliases" in k_v[1]] - aliases = [k_v1[0] for k_v1 in [k_v for k_v in collection_aliases if alias_name in k_v[1]]] + if "aliases" in k_v[1] + ] + aliases = [ + k_v1[0] for k_v1 in [k_v for k_v in collection_aliases if alias_name in k_v[1]] + ] return aliases def alias_set_collections(alias_name, collections=None, **kwargs): - ''' + """ Define an alias @@ -252,27 +309,27 @@ def alias_set_collections(alias_name, collections=None, **kwargs): .. code-block:: bash salt '*' solrcloud.alias_set my_alias collections=[collection1, colletion2] - ''' + """ if not isinstance(collections, list): - raise SaltInvocationError('Collection parameter must be defined and contain a list of collection name') + raise SaltInvocationError( + "Collection parameter must be defined and contain a list of collection name" + ) for collection in collections: if not isinstance(collection, six.string_types): - raise ValueError('Collection name must be a string') + raise ValueError("Collection name must be a string") return _query( - 'admin/collections?action=CREATEALIAS&name={alias}&wt=json&collections={collections}'. - format( - alias=alias_name, - collections=', '.join(collections) - ), - **kwargs - ) + "admin/collections?action=CREATEALIAS&name={alias}&wt=json&collections={collections}".format( + alias=alias_name, collections=", ".join(collections) + ), + **kwargs + ) def collection_reload(collection, **kwargs): - ''' + """ Check if a collection exists @@ -284,13 +341,18 @@ def collection_reload(collection, **kwargs): salt '*' solrcloud.collection_reload collection_name - ''' + """ - _query('admin/collections?action=RELOAD&name={collection}&wt=json'.format(collection=collection), **kwargs) + _query( + "admin/collections?action=RELOAD&name={collection}&wt=json".format( + collection=collection + ), + **kwargs + ) def collection_list(**kwargs): - ''' + """ List all collections @@ -302,13 +364,13 @@ def collection_list(**kwargs): salt '*' solrcloud.collection_list - ''' + """ - return _query('admin/collections?action=LIST&wt=json', **kwargs)["collections"] + return _query("admin/collections?action=LIST&wt=json", **kwargs)["collections"] def collection_exists(collection_name, **kwargs): - ''' + """ Check if a collection exists @@ -320,16 +382,16 @@ def collection_exists(collection_name, **kwargs): salt '*' solrcloud.collection_exists collection_name - ''' + """ if not isinstance(collection_name, six.string_types): - raise ValueError('Collection name must be a string') + raise ValueError("Collection name must be a string") return collection_name in collection_list(**kwargs) def collection_backup(collection_name, location, backup_name=None, **kwargs): - ''' + """ Create a backup for a collection. @@ -340,25 +402,26 @@ def collection_backup(collection_name, location, backup_name=None, **kwargs): .. code-block:: bash salt '*' solrcloud.core_backup collection_name /mnt/nfs_backup - ''' + """ if not collection_exists(collection_name, **kwargs): raise ValueError("Collection doesn't exists") if backup_name is not None: - backup_name = '&name={0}'.format(backup_name) + backup_name = "&name={0}".format(backup_name) else: - backup_name = '' + backup_name = "" - _query('{collection}/replication?command=BACKUP&location={location}{backup_name}&wt=json'.format( - collection=collection_name, - backup_name=backup_name, - location=location - ), **kwargs) + _query( + "{collection}/replication?command=BACKUP&location={location}{backup_name}&wt=json".format( + collection=collection_name, backup_name=backup_name, location=location + ), + **kwargs + ) def collection_backup_all(location, backup_name=None, **kwargs): - ''' + """ Create a backup for all collection present on the server. @@ -369,23 +432,26 @@ def collection_backup_all(location, backup_name=None, **kwargs): .. code-block:: bash salt '*' solrcloud.core_backup /mnt/nfs_backup - ''' + """ for collection_name in collection_list(**kwargs): if backup_name is not None: - backup_name = '&name={backup}.{collection}'.format(backup=backup_name, collection=collection_name) + backup_name = "&name={backup}.{collection}".format( + backup=backup_name, collection=collection_name + ) else: - backup_name = '' + backup_name = "" - _query('{collection}/replication?command=BACKUP&location={location}{backup_name}&wt=json'.format( - collection=collection_name, - backup_name=backup_name, - location=location - ), **kwargs) + _query( + "{collection}/replication?command=BACKUP&location={location}{backup_name}&wt=json".format( + collection=collection_name, backup_name=backup_name, location=location + ), + **kwargs + ) def collection_create(collection_name, options=None, **kwargs): - ''' + """ Create a collection, @@ -411,21 +477,26 @@ def collection_create(collection_name, options=None, **kwargs): salt '*' solrcloud.collection_create collection_name options={"replicationFactor":2, "numShards":3, \ "properties":{"dataDir":"/srv/solr/hugePartitionSollection"}} - ''' + """ if options is None: options = {} if not isinstance(options, dict): - raise SaltInvocationError('options parameter must be a dictionary') + raise SaltInvocationError("options parameter must be a dictionary") options_string = _validate_collection_options(options) - _query('admin/collections?action=CREATE&wt=json&name='+collection_name+options_string, **kwargs) + _query( + "admin/collections?action=CREATE&wt=json&name=" + + collection_name + + options_string, + **kwargs + ) def collection_check_options(options): - ''' + """ Check collections options CLI Example: @@ -433,7 +504,7 @@ def collection_check_options(options): .. code-block:: bash salt '*' solrcloud.collection_check_options '{"replicationFactor":4}' - ''' + """ try: _validate_collection_options(options) @@ -443,7 +514,7 @@ def collection_check_options(options): def collection_get_options(collection_name, **kwargs): - ''' + """ Get collection options Additional parameters (kwargs) may be passed, they will be proxied to http.query @@ -453,28 +524,33 @@ def collection_get_options(collection_name, **kwargs): .. code-block:: bash salt '*' solrcloud.collection_get_options collection_name - ''' + """ cluster = cluster_status(**kwargs) options = { "collection.configName": cluster["collections"][collection_name]["configName"], "router.name": cluster["collections"][collection_name]["router"]["name"], - "replicationFactor": int(cluster["collections"][collection_name]["replicationFactor"]), - "maxShardsPerNode": int(cluster["collections"][collection_name]["maxShardsPerNode"]), - "autoAddReplicas": cluster["collections"][collection_name]["autoAddReplicas"] is True + "replicationFactor": int( + cluster["collections"][collection_name]["replicationFactor"] + ), + "maxShardsPerNode": int( + cluster["collections"][collection_name]["maxShardsPerNode"] + ), + "autoAddReplicas": cluster["collections"][collection_name]["autoAddReplicas"] + is True, } - if 'rule' in cluster["collections"][collection_name]: - options['rule'] = cluster["collections"][collection_name]['rule'] - if 'snitch' in cluster["collections"][collection_name]: - options['snitch'] = cluster["collections"][collection_name]['rule'] + if "rule" in cluster["collections"][collection_name]: + options["rule"] = cluster["collections"][collection_name]["rule"] + if "snitch" in cluster["collections"][collection_name]: + options["snitch"] = cluster["collections"][collection_name]["rule"] return options def collection_set_options(collection_name, options, **kwargs): - ''' + """ Change collection options Additional parameters (kwargs) may be passed, they will be proxied to http.query @@ -486,12 +562,19 @@ def collection_set_options(collection_name, options, **kwargs): .. code-block:: bash salt '*' solrcloud.collection_set_options collection_name options={"replicationFactor":4} - ''' + """ for option in list(options.keys()): if option not in CREATION_ONLY_OPTION: - raise ValueError('Option '+option+' can\'t be modified after collection creation.') + raise ValueError( + "Option " + option + " can't be modified after collection creation." + ) options_string = _validate_collection_options(options) - _query('admin/collections?action=MODIFYCOLLECTION&wt=json&collection='+collection_name+options_string, **kwargs) + _query( + "admin/collections?action=MODIFYCOLLECTION&wt=json&collection=" + + collection_name + + options_string, + **kwargs + ) diff --git a/salt/modules/splunk.py b/salt/modules/splunk.py index 6eb14cec06d..8ff139a05db 100644 --- a/salt/modules/splunk.py +++ b/salt/modules/splunk.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for interop with the Splunk API .. versionadded:: 2016.3.0. @@ -19,79 +19,88 @@ Module for interop with the Splunk API password: abc123 host: example.splunkcloud.com port: 8080 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import base64 +import hmac # Import python libs import logging -import hmac -import base64 import subprocess # Import 3rd-party libs from salt.ext import six + HAS_LIBS = False try: import splunklib.client from splunklib.client import AuthenticationError from splunklib.binding import HTTPError + HAS_LIBS = True except ImportError: pass log = logging.getLogger(__name__) -__virtualname__ = 'splunk' +__virtualname__ = "splunk" SERVICE_NAME = "splunk" ALLOWED_FIELDS_FOR_MODIFICATION = [ - 'realname', - 'roles', - 'defaultApp', - 'tz', + "realname", + "roles", + "defaultApp", + "tz", #'capabilities', - 'name' + "name", ] -REQUIRED_FIELDS_FOR_CREATE = [ - 'realname', - 'name', - 'roles' -] +REQUIRED_FIELDS_FOR_CREATE = ["realname", "name", "roles"] def __virtual__(): - ''' + """ Only load this module if splunk is installed on this minion. - ''' + """ if HAS_LIBS: return __virtualname__ - return (False, 'The splunk execution module failed to load: ' - 'requires splunk python library to be installed.') + return ( + False, + "The splunk execution module failed to load: " + "requires splunk python library to be installed.", + ) def _get_secret_key(profile): - config = __salt__['config.option'](profile) - return config.get('password_secret_key') + config = __salt__["config.option"](profile) + return config.get("password_secret_key") def _generate_password(email): - m = hmac.new(base64.b64decode(_get_secret_key('splunk')), six.text_type([email, SERVICE_NAME])) - return base64.urlsafe_b64encode(m.digest()).strip().replace('=', '') + m = hmac.new( + base64.b64decode(_get_secret_key("splunk")), + six.text_type([email, SERVICE_NAME]), + ) + return base64.urlsafe_b64encode(m.digest()).strip().replace("=", "") def _send_email(name, email): "send a email to inform user of account creation" - config = __salt__['config.option']('splunk') - email_object = config.get('email') + config = __salt__["config.option"]("splunk") + email_object = config.get("email") if email_object: - cc = email_object.get('cc') - subject = email_object.get('subject') - message = email_object.get('message').format(name, name, _generate_password(email), name) + cc = email_object.get("cc") + subject = email_object.get("subject") + message = email_object.get("message").format( + name, name, _generate_password(email), name + ) try: - mail_process = subprocess.Popen(['mail', '-s', subject, '-c', cc, email], stdin=subprocess.PIPE) + mail_process = subprocess.Popen( + ["mail", "-s", subject, "-c", cc, email], stdin=subprocess.PIPE + ) except Exception as e: # pylint: disable=broad-except log.error("unable to send email to %s: %s", email, e) @@ -101,15 +110,13 @@ def _send_email(name, email): def _populate_cache(profile="splunk"): - config = __salt__['config.option'](profile) + config = __salt__["config.option"](profile) - key = "splunk.users.{0}".format( - config.get('host') - ) + key = "splunk.users.{0}".format(config.get("host")) if key not in __context__: client = _get_splunk(profile) - kwargs = {'sort_key': 'realname', 'sort_dir': 'asc'} + kwargs = {"sort_key": "realname", "sort_dir": "asc"} users = client.users.list(count=-1, **kwargs) result = {} @@ -122,41 +129,40 @@ def _populate_cache(profile="splunk"): def _get_splunk(profile): - ''' + """ Return the splunk client, cached into __context__ for performance - ''' - config = __salt__['config.option'](profile) + """ + config = __salt__["config.option"](profile) key = "splunk.{0}:{1}:{2}:{3}".format( - config.get('host'), - config.get('port'), - config.get('username'), - config.get('password') + config.get("host"), + config.get("port"), + config.get("username"), + config.get("password"), ) if key not in __context__: __context__[key] = splunklib.client.connect( - host=config.get('host'), - port=config.get('port'), - username=config.get('username'), - password=config.get('password')) + host=config.get("host"), + port=config.get("port"), + username=config.get("username"), + password=config.get("password"), + ) return __context__[key] def list_users(profile="splunk"): - ''' + """ List all users in the splunk DB CLI Example: salt myminion splunk.list_users - ''' + """ - config = __salt__['config.option'](profile) - key = "splunk.users.{0}".format( - config.get('host') - ) + config = __salt__["config.option"](profile) + key = "splunk.users.{0}".format(config.get("host")) if key not in __context__: _populate_cache(profile) @@ -165,31 +171,31 @@ def list_users(profile="splunk"): def get_user(email, profile="splunk", **kwargs): - ''' + """ Get a splunk user by name/email CLI Example: salt myminion splunk.get_user 'user@example.com' user_details=false salt myminion splunk.get_user 'user@example.com' user_details=true - ''' + """ user_map = list_users(profile) user_found = email.lower() in user_map.keys() - if not kwargs.get('user_details', False) and user_found: + if not kwargs.get("user_details", False) and user_found: # The user is in splunk group, just return return True - elif kwargs.get('user_details', False) and user_found: + elif kwargs.get("user_details", False) and user_found: user = user_map[email.lower()] response = {} - for field in ['defaultApp', 'realname', 'name', 'email']: + for field in ["defaultApp", "realname", "name", "email"]: response[field] = user[field] - response['roles'] = [] + response["roles"] = [] for role in user.role_entities: - response['roles'].append(role.name) + response["roles"].append(role.name) return response @@ -197,13 +203,13 @@ def get_user(email, profile="splunk", **kwargs): def create_user(email, profile="splunk", **kwargs): - ''' + """ create a splunk user by name/email CLI Example: salt myminion splunk.create_user user@example.com roles=['user'] realname="Test User" name=testuser - ''' + """ client = _get_splunk(profile) @@ -225,20 +231,24 @@ def create_user(email, profile="splunk", **kwargs): # create for req_field in REQUIRED_FIELDS_FOR_CREATE: if not property_map.get(req_field): - log.error("Missing required params %s", - ', '.join([six.text_type(k) for k in REQUIRED_FIELDS_FOR_CREATE])) + log.error( + "Missing required params %s", + ", ".join([six.text_type(k) for k in REQUIRED_FIELDS_FOR_CREATE]), + ) return False - newuser = client.users.create(username=property_map['name'], - password=_generate_password(email), - roles=property_map['roles'], - email=email, - realname=property_map['realname']) + newuser = client.users.create( + username=property_map["name"], + password=_generate_password(email), + roles=property_map["roles"], + email=email, + realname=property_map["realname"], + ) _send_email(newuser.name, newuser.email) response = {} - for field in ['email', 'password', 'realname', 'roles']: + for field in ["email", "password", "realname", "roles"]: response[field] = newuser[field] except Exception as e: # pylint: disable=broad-except @@ -247,13 +257,13 @@ def create_user(email, profile="splunk", **kwargs): def update_user(email, profile="splunk", **kwargs): - ''' + """ Create a splunk user by email CLI Example: salt myminion splunk.update_user example@domain.com roles=['user'] realname="Test User" - ''' + """ client = _get_splunk(profile) @@ -279,13 +289,13 @@ def update_user(email, profile="splunk", **kwargs): resource_value = user[k] if resource_value is not None: # you can't update the username in update api call - if k.lower() == 'name': + if k.lower() == "name": continue - if k.lower() == 'roles': + if k.lower() == "roles": if isinstance(v, six.string_types): - v = v.split(',') + v = v.split(",") if set(roles) != set(v): - kwargs['roles'] = list(set(v)) + kwargs["roles"] = list(set(v)) elif resource_value != v: kwargs[k] = v @@ -297,18 +307,18 @@ def update_user(email, profile="splunk", **kwargs): fields_modified[field] = user[field] else: - #succeeded, no change + # succeeded, no change return True def delete_user(email, profile="splunk"): - ''' + """ Delete a splunk user by email CLI Example: salt myminion splunk_user.delete 'user@example.com' - ''' + """ client = _get_splunk(profile) @@ -318,7 +328,7 @@ def delete_user(email, profile="splunk"): try: client.users.delete(user.name) except (AuthenticationError, HTTPError) as e: - log.info('Exception: %s', e) + log.info("Exception: %s", e) return False else: return False diff --git a/salt/modules/splunk_search.py b/salt/modules/splunk_search.py index 418fa7ef684..a11bb4f51e5 100644 --- a/salt/modules/splunk_search.py +++ b/salt/modules/splunk_search.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for interop with the Splunk API .. versionadded:: 2015.5.0 @@ -18,71 +18,77 @@ Module for interop with the Splunk API password: abc123 host: example.splunkcloud.com port: 8080 -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import urllib +# Import salt libs +import salt.utils.yaml + # Import third party libs from salt.ext import six +from salt.utils.odict import OrderedDict + HAS_LIBS = False try: import splunklib.client import requests + HAS_LIBS = True except ImportError: pass -# Import salt libs -import salt.utils.yaml -from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) # Don't shadow built-in's. -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} -__virtualname__ = 'splunk_search' +__virtualname__ = "splunk_search" def __virtual__(): - ''' + """ Only load this module if splunk is installed on this minion. - ''' + """ if HAS_LIBS: return __virtualname__ - return (False, 'The splunk_search execution module failed to load: ' - 'requires both the requests and the splunk-sdk python library to be installed.') + return ( + False, + "The splunk_search execution module failed to load: " + "requires both the requests and the splunk-sdk python library to be installed.", + ) def _get_splunk(profile): - ''' + """ Return the splunk client, cached into __context__ for performance - ''' - config = __salt__['config.option'](profile) + """ + config = __salt__["config.option"](profile) key = "splunk_search.{0}:{1}:{2}:{3}".format( - config.get('host'), - config.get('port'), - config.get('username'), - config.get('password') + config.get("host"), + config.get("port"), + config.get("username"), + config.get("password"), ) if key not in __context__: __context__[key] = splunklib.client.connect( - host=config.get('host'), - port=config.get('port'), - username=config.get('username'), - password=config.get('password')) + host=config.get("host"), + port=config.get("port"), + username=config.get("username"), + password=config.get("password"), + ) return __context__[key] def _get_splunk_search_props(search): - ''' + """ Get splunk search properties from an object - ''' + """ props = search.content props["app"] = search.access.app props["sharing"] = search.access.sharing @@ -90,13 +96,13 @@ def _get_splunk_search_props(search): def get(name, profile="splunk"): - ''' + """ Get a splunk search CLI Example: splunk_search.get 'my search name' - ''' + """ client = _get_splunk(profile) search = None # uglyness of splunk lib @@ -108,13 +114,13 @@ def get(name, profile="splunk"): def update(name, profile="splunk", **kwargs): - ''' + """ Update a splunk search CLI Example: splunk_search.update 'my search name' sharing=app - ''' + """ client = _get_splunk(profile) search = client.saved_searches[name] props = _get_splunk_search_props(search) @@ -132,9 +138,7 @@ def update(name, profile="splunk", **kwargs): if old_value != new_value: update_set[key] = new_value update_needed = True - diffs.append("{0}: '{1}' => '{2}'".format( - key, old_value, new_value - )) + diffs.append("{0}: '{1}' => '{2}'".format(key, old_value, new_value)) if update_needed: search.update(**update_set).refresh() return update_set, diffs @@ -142,22 +146,22 @@ def update(name, profile="splunk", **kwargs): def create(name, profile="splunk", **kwargs): - ''' + """ Create a splunk search CLI Example: splunk_search.create 'my search name' search='error msg' - ''' + """ client = _get_splunk(profile) search = client.saved_searches.create(name, **kwargs) # use the REST API to set owner and permissions # this is hard-coded for now; all managed searches are app scope and # readable by all - config = __salt__['config.option'](profile) - url = "https://{0}:{1}".format(config.get('host'), config.get('port')) - auth = (config.get('username'), config.get('password')) + config = __salt__["config.option"](profile) + url = "https://{0}:{1}".format(config.get("host"), config.get("port")) + auth = (config.get("username"), config.get("password")) data = { "owner": config.get("username"), "sharing": "app", @@ -171,13 +175,13 @@ def create(name, profile="splunk", **kwargs): def delete(name, profile="splunk"): - ''' + """ Delete a splunk search CLI Example: splunk_search.delete 'my search name' - ''' + """ client = _get_splunk(profile) try: client.saved_searches.delete(name) @@ -187,20 +191,26 @@ def delete(name, profile="splunk"): def list_(profile="splunk"): - ''' + """ List splunk searches (names only) CLI Example: splunk_search.list - ''' + """ client = _get_splunk(profile) - searches = [x['name'] for x in client.saved_searches] + searches = [x["name"] for x in client.saved_searches] return searches -def list_all(prefix=None, app=None, owner=None, description_contains=None, - name_not_contains=None, profile="splunk"): - ''' +def list_all( + prefix=None, + app=None, + owner=None, + description_contains=None, + name_not_contains=None, + profile="splunk", +): + """ Get all splunk search details. Produces results that can be used to create an sls file. @@ -236,7 +246,7 @@ def list_all(prefix=None, app=None, owner=None, description_contains=None, 6. Get all searches again, verify no changes $ salt-call splunk_search.list_all --out=txt | sed "s/local: //" > final_searches.sls $ diff final_searches.sls managed_searches.sls - ''' + """ client = _get_splunk(profile) # splunklib doesn't provide the default settings for saved searches. @@ -254,14 +264,16 @@ def list_all(prefix=None, app=None, owner=None, description_contains=None, # stuff that splunk returns but that you should not attempt to set. # cf http://dev.splunk.com/view/python-sdk/SP-CAAAEK2 - readonly_keys = ("triggered_alert_count", - "action.email", - "action.populate_lookup", - "action.rss", - "action.script", - "action.summary_index", - "qualifiedSearch", - "next_scheduled_time") + readonly_keys = ( + "triggered_alert_count", + "action.email", + "action.populate_lookup", + "action.rss", + "action.script", + "action.summary_index", + "qualifiedSearch", + "next_scheduled_time", + ) results = OrderedDict() # sort the splunk searches by name, so we get consistent output @@ -280,7 +292,7 @@ def list_all(prefix=None, app=None, owner=None, description_contains=None, # put name in the OrderedDict first d = [{"name": name}] # add the rest of the splunk settings, ignoring any defaults - description = '' + description = "" for (k, v) in sorted(search.content.items()): if k in readonly_keys: continue @@ -291,7 +303,7 @@ def list_all(prefix=None, app=None, owner=None, description_contains=None, if k in defaults and defaults[k] == v: continue d.append({k: v}) - if k == 'description': + if k == "description": description = v if description_contains and description_contains not in description: continue diff --git a/salt/modules/sqlite3.py b/salt/modules/sqlite3.py index 01876e9ce7a..ae1797d75b1 100644 --- a/salt/modules/sqlite3.py +++ b/salt/modules/sqlite3.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Support for SQLite3 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs try: import sqlite3 + HAS_SQLITE3 = True except ImportError: HAS_SQLITE3 = False @@ -15,7 +16,10 @@ except ImportError: # pylint: disable=C0103 def __virtual__(): if not HAS_SQLITE3: - return (False, 'The sqlite3 execution module failed to load: the sqlite3 python library is not available.') + return ( + False, + "The sqlite3 execution module failed to load: the sqlite3 python library is not available.", + ) return True @@ -29,7 +33,7 @@ def _connect(db=None): def version(): - ''' + """ Return version of pysqlite CLI Example: @@ -37,12 +41,12 @@ def version(): .. code-block:: bash salt '*' sqlite3.version - ''' + """ return sqlite3.version def sqlite_version(): - ''' + """ Return version of sqlite CLI Example: @@ -50,12 +54,12 @@ def sqlite_version(): .. code-block:: bash salt '*' sqlite3.sqlite_version - ''' + """ return sqlite3.sqlite_version def modify(db=None, sql=None): - ''' + """ Issue an SQL query to sqlite3 (with no return data), usually used to modify the database in some way (insert, delete, create, etc) @@ -64,7 +68,7 @@ def modify(db=None, sql=None): .. code-block:: bash salt '*' sqlite3.modify /root/test.db 'CREATE TABLE test(id INT, testdata TEXT);' - ''' + """ cur = _connect(db) if not cur: @@ -75,7 +79,7 @@ def modify(db=None, sql=None): def fetch(db=None, sql=None): - ''' + """ Retrieve data from an sqlite3 db (returns all rows, be careful!) CLI Example: @@ -83,7 +87,7 @@ def fetch(db=None, sql=None): .. code-block:: bash salt '*' sqlite3.fetch /root/test.db 'SELECT * FROM test;' - ''' + """ cur = _connect(db) if not cur: @@ -95,7 +99,7 @@ def fetch(db=None, sql=None): def tables(db=None): - ''' + """ Show all tables in the database CLI Example: @@ -103,21 +107,19 @@ def tables(db=None): .. code-block:: bash salt '*' sqlite3.tables /root/test.db - ''' + """ cur = _connect(db) if not cur: return False - cur.execute( - "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;" - ) + cur.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;") rows = cur.fetchall() return rows def indices(db=None): - ''' + """ Show all indices in the database CLI Example: @@ -125,21 +127,19 @@ def indices(db=None): .. code-block:: bash salt '*' sqlite3.indices /root/test.db - ''' + """ cur = _connect(db) if not cur: return False - cur.execute( - "SELECT name FROM sqlite_master WHERE type='index' ORDER BY name;" - ) + cur.execute("SELECT name FROM sqlite_master WHERE type='index' ORDER BY name;") rows = cur.fetchall() return rows def indexes(db=None): - ''' + """ Show all indices in the database, for people with poor spelling skills CLI Example: @@ -147,5 +147,5 @@ def indexes(db=None): .. code-block:: bash salt '*' sqlite3.indexes /root/test.db - ''' + """ return indices(db) diff --git a/salt/modules/ssh.py b/salt/modules/ssh.py index 93204479ca8..5d78c84cddd 100644 --- a/salt/modules/ssh.py +++ b/salt/modules/ssh.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage client ssh components .. note:: @@ -7,10 +7,11 @@ Manage client ssh components This module requires the use of MD5 hashing. Certain security audits may not permit the use of MD5. For those cases, this module should be disabled or removed. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import binascii import hashlib import logging @@ -18,17 +19,15 @@ import os import re import subprocess +import salt.utils.data + # Import salt libs import salt.utils.decorators.path -import salt.utils.data import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils -from salt.exceptions import ( - SaltInvocationError, - CommandExecutionError, -) +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import 3rd-party libs from salt.ext import six @@ -42,73 +41,78 @@ if six.PY3: def __virtual__(): - if not salt.utils.path.which('ssh'): - return False, 'The module requires the ssh binary.' + if not salt.utils.path.which("ssh"): + return False, "The module requires the ssh binary." return True def _refine_enc(enc): - ''' + """ Return the properly formatted ssh value for the authorized encryption key type. ecdsa defaults to 256 bits, must give full ecdsa enc schema string if using higher enc. If the type is not found, raise CommandExecutionError. - ''' + """ - rsa = ['r', 'rsa', 'ssh-rsa'] - dss = ['d', 'dsa', 'dss', 'ssh-dss'] - ecdsa = ['e', 'ecdsa', 'ecdsa-sha2-nistp521', 'ecdsa-sha2-nistp384', - 'ecdsa-sha2-nistp256'] - ed25519 = ['ed25519', 'ssh-ed25519'] + rsa = ["r", "rsa", "ssh-rsa"] + dss = ["d", "dsa", "dss", "ssh-dss"] + ecdsa = [ + "e", + "ecdsa", + "ecdsa-sha2-nistp521", + "ecdsa-sha2-nistp384", + "ecdsa-sha2-nistp256", + ] + ed25519 = ["ed25519", "ssh-ed25519"] if enc in rsa: - return 'ssh-rsa' + return "ssh-rsa" elif enc in dss: - return 'ssh-dss' + return "ssh-dss" elif enc in ecdsa: # ecdsa defaults to ecdsa-sha2-nistp256 # otherwise enc string is actual encoding string - if enc in ['e', 'ecdsa']: - return 'ecdsa-sha2-nistp256' + if enc in ["e", "ecdsa"]: + return "ecdsa-sha2-nistp256" return enc elif enc in ed25519: - return 'ssh-ed25519' + return "ssh-ed25519" else: - raise CommandExecutionError( - 'Incorrect encryption key type \'{0}\'.'.format(enc) - ) + raise CommandExecutionError("Incorrect encryption key type '{0}'.".format(enc)) def _format_auth_line(key, enc, comment, options): - ''' + """ Properly format user input. - ''' - line = '' + """ + line = "" if options: - line += '{0} '.format(','.join(options)) - line += '{0} {1} {2}\n'.format(enc, key, comment) + line += "{0} ".format(",".join(options)) + line += "{0} {1} {2}\n".format(enc, key, comment) return line def _expand_authorized_keys_path(path, user, home): - ''' + """ Expand the AuthorizedKeysFile expression. Defined in man sshd_config(5) - ''' - converted_path = '' + """ + converted_path = "" had_escape = False for char in path: if had_escape: had_escape = False - if char == '%': - converted_path += '%' - elif char == 'u': + if char == "%": + converted_path += "%" + elif char == "u": converted_path += user - elif char == 'h': + elif char == "h": converted_path += home else: - error = 'AuthorizedKeysFile path: unknown token character "%{0}"'.format(char) + error = 'AuthorizedKeysFile path: unknown token character "%{0}"'.format( + char + ) raise CommandExecutionError(error) continue - elif char == '%': + elif char == "%": had_escape = True else: converted_path += char @@ -119,13 +123,13 @@ def _expand_authorized_keys_path(path, user, home): def _get_config_file(user, config): - ''' + """ Get absolute path to a user's ssh_config. - ''' - uinfo = __salt__['user.info'](user) + """ + uinfo = __salt__["user.info"](user) if not uinfo: - raise CommandExecutionError('User \'{0}\' does not exist'.format(user)) - home = uinfo['home'] + raise CommandExecutionError("User '{0}' does not exist".format(user)) + home = uinfo["home"] config = _expand_authorized_keys_path(config, user, home) if not os.path.isabs(config): config = os.path.join(home, config) @@ -133,15 +137,11 @@ def _get_config_file(user, config): def _replace_auth_key( - user, - key, - enc='ssh-rsa', - comment='', - options=None, - config='.ssh/authorized_keys'): - ''' + user, key, enc="ssh-rsa", comment="", options=None, config=".ssh/authorized_keys" +): + """ Replace an existing key - ''' + """ auth_line = _format_auth_line(key, enc, comment, options or []) @@ -150,19 +150,22 @@ def _replace_auth_key( try: # open the file for both reading AND writing - with salt.utils.files.fopen(full, 'r') as _fh: + with salt.utils.files.fopen(full, "r") as _fh: for line in _fh: # We don't need any whitespace-only containing lines or arbitrary doubled newlines line = salt.utils.stringutils.to_unicode(line.strip()) - if line == '': + if line == "": continue - line += '\n' + line += "\n" - if line.startswith('#'): + if line.startswith("#"): # Commented Line lines.append(line) continue - comps = re.findall(r'((.*)\s)?(ssh-[a-z0-9-]+|ecdsa-[a-z0-9-]+)\s([a-zA-Z0-9+/]+={0,2})(\s(.*))?', line) + comps = re.findall( + r"((.*)\s)?(ssh-[a-z0-9-]+|ecdsa-[a-z0-9-]+)\s([a-zA-Z0-9+/]+={0,2})(\s(.*))?", + line, + ) if len(comps) > 0 and len(comps[0]) > 3 and comps[0][3] == key: # Found our key, replace it lines.append(auth_line) @@ -170,32 +173,32 @@ def _replace_auth_key( lines.append(line) _fh.close() # Re-open the file writable after properly closing it - with salt.utils.files.fopen(full, 'wb') as _fh: + with salt.utils.files.fopen(full, "wb") as _fh: # Write out any changes _fh.writelines(salt.utils.data.encode(lines)) except (IOError, OSError) as exc: raise CommandExecutionError( - 'Problem reading or writing to key file: {0}'.format(exc) + "Problem reading or writing to key file: {0}".format(exc) ) def _validate_keys(key_file, fingerprint_hash_type): - ''' + """ Return a dict containing validated keys in the passed file - ''' + """ ret = {} - linere = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$') + linere = re.compile(r"^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$") try: - with salt.utils.files.fopen(key_file, 'r') as _fh: + with salt.utils.files.fopen(key_file, "r") as _fh: for line in _fh: # We don't need any whitespace-only containing lines or arbitrary doubled newlines line = salt.utils.stringutils.to_unicode(line.strip()) - if line == '': + if line == "": continue - line += '\n' + line += "\n" - if line.startswith('#'): + if line.startswith("#"): # Commented Line continue @@ -214,31 +217,31 @@ def _validate_keys(key_file, fingerprint_hash_type): if opts: # It has options, grab them - options = opts.split(',') + options = opts.split(",") else: options = [] enc = comps[0] key = comps[1] - comment = ' '.join(comps[2:]) + comment = " ".join(comps[2:]) fingerprint = _fingerprint(key, fingerprint_hash_type) if fingerprint is None: continue - ret[key] = {'enc': enc, - 'comment': comment, - 'options': options, - 'fingerprint': fingerprint} + ret[key] = { + "enc": enc, + "comment": comment, + "options": options, + "fingerprint": fingerprint, + } except (IOError, OSError): - raise CommandExecutionError( - 'Problem reading ssh key file {0}'.format(key_file) - ) + raise CommandExecutionError("Problem reading ssh key file {0}".format(key_file)) return ret def _fingerprint(public_key, fingerprint_hash_type): - ''' + """ Return a public key fingerprint based on its base64-encoded representation The fingerprint string is formatted according to RFC 4716 (ch.4), that is, @@ -256,61 +259,63 @@ def _fingerprint(public_key, fingerprint_hash_type): .. versionadded:: 2016.11.4 .. versionchanged:: 2017.7.0: default changed from ``md5`` to ``sha256`` - ''' + """ if fingerprint_hash_type: hash_type = fingerprint_hash_type.lower() else: - hash_type = 'sha256' + hash_type = "sha256" try: hash_func = getattr(hashlib, hash_type) except AttributeError: raise CommandExecutionError( - 'The fingerprint_hash_type {0} is not supported.'.format( - hash_type - ) + "The fingerprint_hash_type {0} is not supported.".format(hash_type) ) try: if six.PY2: - raw_key = public_key.decode('base64') + raw_key = public_key.decode("base64") else: - raw_key = base64.b64decode(public_key, validate=True) # pylint: disable=E1123 + raw_key = base64.b64decode( + public_key, validate=True + ) # pylint: disable=E1123 except binascii.Error: return None ret = hash_func(raw_key).hexdigest() - chunks = [ret[i:i + 2] for i in range(0, len(ret), 2)] - return ':'.join(chunks) + chunks = [ret[i : i + 2] for i in range(0, len(ret), 2)] + return ":".join(chunks) def _get_known_hosts_file(config=None, user=None): if user: - config = config or '.ssh/known_hosts' + config = config or ".ssh/known_hosts" else: - config = config or '/etc/ssh/ssh_known_hosts' + config = config or "/etc/ssh/ssh_known_hosts" if os.path.isabs(config): full = config else: if user: - uinfo = __salt__['user.info'](user) + uinfo = __salt__["user.info"](user) if not uinfo: - return {'status': 'error', - 'error': 'User {0} does not exist'.format(user)} - full = os.path.join(uinfo['home'], config) + return { + "status": "error", + "error": "User {0} does not exist".format(user), + } + full = os.path.join(uinfo["home"], config) else: return { - 'status': 'error', - 'error': 'Cannot determine absolute path to file.' + "status": "error", + "error": "Cannot determine absolute path to file.", } return full def host_keys(keydir=None, private=True, certs=True): - ''' + """ Return the minion's host keys CLI Example: @@ -321,37 +326,33 @@ def host_keys(keydir=None, private=True, certs=True): salt '*' ssh.host_keys keydir=/etc/ssh salt '*' ssh.host_keys keydir=/etc/ssh private=False salt '*' ssh.host_keys keydir=/etc/ssh certs=False - ''' + """ # TODO: support parsing sshd_config for the key directory if not keydir: - if __grains__['kernel'] == 'Linux': - keydir = '/etc/ssh' + if __grains__["kernel"] == "Linux": + keydir = "/etc/ssh" else: # If keydir is None, os.listdir() will blow up - raise SaltInvocationError('ssh.host_keys: Please specify a keydir') + raise SaltInvocationError("ssh.host_keys: Please specify a keydir") keys = {} - fnre = re.compile( - r'ssh_host_(?P<type>.+)_key(?P<pub>(?P<cert>-cert)?\.pub)?') + fnre = re.compile(r"ssh_host_(?P<type>.+)_key(?P<pub>(?P<cert>-cert)?\.pub)?") for fn_ in os.listdir(keydir): m = fnre.match(fn_) if m: - if not m.group('pub') and private is False: - log.info('Skipping private key file %s as ' - 'private is set to False', - fn_) - continue - if m.group('cert') and certs is False: + if not m.group("pub") and private is False: log.info( - 'Skipping key file %s as certs is set to False', - fn_ + "Skipping private key file %s as " "private is set to False", fn_ ) continue + if m.group("cert") and certs is False: + log.info("Skipping key file %s as certs is set to False", fn_) + continue - kname = m.group('type') - if m.group('pub'): - kname += m.group('pub') + kname = m.group("type") + if m.group("pub"): + kname += m.group("pub") try: - with salt.utils.files.fopen(os.path.join(keydir, fn_), 'r') as _fh: + with salt.utils.files.fopen(os.path.join(keydir, fn_), "r") as _fh: # As of RFC 4716 "a key file is a text file, containing a # sequence of lines", although some SSH implementations # (e.g. OpenSSH) manage their own format(s). Please see @@ -364,14 +365,12 @@ def host_keys(keydir=None, private=True, certs=True): keys[kname] += salt.utils.stringutils.to_unicode(_fh.read()) keys[kname] = keys[kname].strip() except (IOError, OSError): - keys[kname] = '' + keys[kname] = "" return keys -def auth_keys(user=None, - config='.ssh/authorized_keys', - fingerprint_hash_type=None): - ''' +def auth_keys(user=None, config=".ssh/authorized_keys", fingerprint_hash_type=None): + """ Return the authorized keys for users CLI Example: @@ -382,9 +381,9 @@ def auth_keys(user=None, salt '*' ssh.auth_keys root salt '*' ssh.auth_keys user=root salt '*' ssh.auth_keys user="[user1, user2]" - ''' + """ if not user: - user = __salt__['user.list_users']() + user = __salt__["user.list_users"]() old_output_when_one_user = False if not isinstance(user, list): @@ -411,12 +410,14 @@ def auth_keys(user=None, return keys -def check_key_file(user, - source, - config='.ssh/authorized_keys', - saltenv='base', - fingerprint_hash_type=None): - ''' +def check_key_file( + user, + source, + config=".ssh/authorized_keys", + saltenv="base", + fingerprint_hash_type=None, +): + """ Check a keyfile from a source destination against the local keys and return the keys to change @@ -425,16 +426,15 @@ def check_key_file(user, .. code-block:: bash salt '*' ssh.check_key_file root salt://ssh/keyfile - ''' - keyfile = __salt__['cp.cache_file'](source, saltenv) + """ + keyfile = __salt__["cp.cache_file"](source, saltenv) if not keyfile: return {} s_keys = _validate_keys(keyfile, fingerprint_hash_type) if not s_keys: - err = 'No keys detected in {0}. Is file properly ' \ - 'formatted?'.format(source) + err = "No keys detected in {0}. Is file properly " "formatted?".format(source) log.error(err) - __context__['ssh_auth.error'] = err + __context__["ssh_auth.error"] = err return {} else: ret = {} @@ -442,23 +442,26 @@ def check_key_file(user, ret[key] = check_key( user, key, - s_keys[key]['enc'], - s_keys[key]['comment'], - s_keys[key]['options'], + s_keys[key]["enc"], + s_keys[key]["comment"], + s_keys[key]["options"], config=config, - fingerprint_hash_type=fingerprint_hash_type) + fingerprint_hash_type=fingerprint_hash_type, + ) return ret -def check_key(user, - key, - enc, - comment, - options, - config='.ssh/authorized_keys', - cache_keys=None, - fingerprint_hash_type=None): - ''' +def check_key( + user, + key, + enc, + comment, + options, + config=".ssh/authorized_keys", + cache_keys=None, + fingerprint_hash_type=None, +): + """ Check to see if a key needs updating, returns "update", "add" or "exists" CLI Example: @@ -466,13 +469,13 @@ def check_key(user, .. code-block:: bash salt '*' ssh.check_key <user> <key> <enc> <comment> <options> - ''' + """ if cache_keys is None: cache_keys = [] enc = _refine_enc(enc) - current = auth_keys(user, - config=config, - fingerprint_hash_type=fingerprint_hash_type) + current = auth_keys( + user, config=config, fingerprint_hash_type=fingerprint_hash_type + ) nline = _format_auth_line(key, enc, comment, options) # Removing existing keys from the auth_keys isn't really a good idea @@ -489,23 +492,24 @@ def check_key(user, # rm_auth_key(user, pub_key) if key in current: - cline = _format_auth_line(key, - current[key]['enc'], - current[key]['comment'], - current[key]['options']) + cline = _format_auth_line( + key, current[key]["enc"], current[key]["comment"], current[key]["options"] + ) if cline != nline: - return 'update' + return "update" else: - return 'add' - return 'exists' + return "add" + return "exists" -def rm_auth_key_from_file(user, - source, - config='.ssh/authorized_keys', - saltenv='base', - fingerprint_hash_type=None): - ''' +def rm_auth_key_from_file( + user, + source, + config=".ssh/authorized_keys", + saltenv="base", + fingerprint_hash_type=None, +): + """ Remove an authorized key from the specified user's authorized key file, using a file as source @@ -514,49 +518,37 @@ def rm_auth_key_from_file(user, .. code-block:: bash salt '*' ssh.rm_auth_key_from_file <user> salt://ssh_keys/<user>.id_rsa.pub - ''' - lfile = __salt__['cp.cache_file'](source, saltenv) + """ + lfile = __salt__["cp.cache_file"](source, saltenv) if not os.path.isfile(lfile): - raise CommandExecutionError( - 'Failed to pull key file from salt file server' - ) + raise CommandExecutionError("Failed to pull key file from salt file server") s_keys = _validate_keys(lfile, fingerprint_hash_type) if not s_keys: - err = ( - 'No keys detected in {0}. Is file properly formatted?'.format( - source - ) - ) + err = "No keys detected in {0}. Is file properly formatted?".format(source) log.error(err) - __context__['ssh_auth.error'] = err - return 'fail' + __context__["ssh_auth.error"] = err + return "fail" else: - rval = '' + rval = "" for key in s_keys: rval += rm_auth_key( - user, - key, - config=config, - fingerprint_hash_type=fingerprint_hash_type + user, key, config=config, fingerprint_hash_type=fingerprint_hash_type ) # Due to the ability for a single file to have multiple keys, it's # possible for a single call to this function to have both "replace" # and "new" as possible valid returns. I ordered the following as I # thought best. - if 'Key not removed' in rval: - return 'Key not removed' - elif 'Key removed' in rval: - return 'Key removed' + if "Key not removed" in rval: + return "Key not removed" + elif "Key removed" in rval: + return "Key removed" else: - return 'Key not present' + return "Key not present" -def rm_auth_key(user, - key, - config='.ssh/authorized_keys', - fingerprint_hash_type=None): - ''' +def rm_auth_key(user, key, config=".ssh/authorized_keys", fingerprint_hash_type=None): + """ Remove an authorized key from the specified user's authorized key file CLI Example: @@ -564,32 +556,32 @@ def rm_auth_key(user, .. code-block:: bash salt '*' ssh.rm_auth_key <user> <key> - ''' - current = auth_keys(user, - config=config, - fingerprint_hash_type=fingerprint_hash_type) - linere = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$') + """ + current = auth_keys( + user, config=config, fingerprint_hash_type=fingerprint_hash_type + ) + linere = re.compile(r"^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$") if key in current: # Remove the key full = _get_config_file(user, config) # Return something sensible if the file doesn't exist if not os.path.isfile(full): - return 'Authorized keys file {0} not present'.format(full) + return "Authorized keys file {0} not present".format(full) lines = [] try: # Read every line in the file to find the right ssh key # and then write out the correct one. Open the file once - with salt.utils.files.fopen(full, 'r') as _fh: + with salt.utils.files.fopen(full, "r") as _fh: for line in _fh: # We don't need any whitespace-only containing lines or arbitrary doubled newlines line = salt.utils.stringutils.to_unicode(line.strip()) - if line == '': + if line == "": continue - line += '\n' + line += "\n" - if line.startswith('#'): + if line.startswith("#"): # Commented Line lines.append(line) continue @@ -618,23 +610,24 @@ def rm_auth_key(user, # Let the context manager do the right thing here and then # re-open the file in write mode to save the changes out. - with salt.utils.files.fopen(full, 'wb') as _fh: + with salt.utils.files.fopen(full, "wb") as _fh: _fh.writelines(salt.utils.data.encode(lines)) except (IOError, OSError) as exc: - log.warning('Could not read/write key file: %s', - exc) - return 'Key not removed' - return 'Key removed' + log.warning("Could not read/write key file: %s", exc) + return "Key not removed" + return "Key removed" # TODO: Should this function return a simple boolean? - return 'Key not present' + return "Key not present" -def set_auth_key_from_file(user, - source, - config='.ssh/authorized_keys', - saltenv='base', - fingerprint_hash_type=None): - ''' +def set_auth_key_from_file( + user, + source, + config=".ssh/authorized_keys", + saltenv="base", + fingerprint_hash_type=None, +): + """ Add a key to the authorized_keys file, using a file as the source. CLI Example: @@ -642,61 +635,56 @@ def set_auth_key_from_file(user, .. code-block:: bash salt '*' ssh.set_auth_key_from_file <user> salt://ssh_keys/<user>.id_rsa.pub - ''' + """ # TODO: add support for pulling keys from other file sources as well - lfile = __salt__['cp.cache_file'](source, saltenv) + lfile = __salt__["cp.cache_file"](source, saltenv) if not os.path.isfile(lfile): - raise CommandExecutionError( - 'Failed to pull key file from salt file server' - ) + raise CommandExecutionError("Failed to pull key file from salt file server") s_keys = _validate_keys(lfile, fingerprint_hash_type) if not s_keys: - err = ( - 'No keys detected in {0}. Is file properly formatted?'.format( - source - ) - ) + err = "No keys detected in {0}. Is file properly formatted?".format(source) log.error(err) - __context__['ssh_auth.error'] = err - return 'fail' + __context__["ssh_auth.error"] = err + return "fail" else: - rval = '' + rval = "" for key in s_keys: rval += set_auth_key( user, key, - enc=s_keys[key]['enc'], - comment=s_keys[key]['comment'], - options=s_keys[key]['options'], + enc=s_keys[key]["enc"], + comment=s_keys[key]["comment"], + options=s_keys[key]["options"], config=config, cache_keys=list(s_keys.keys()), - fingerprint_hash_type=fingerprint_hash_type + fingerprint_hash_type=fingerprint_hash_type, ) # Due to the ability for a single file to have multiple keys, it's # possible for a single call to this function to have both "replace" # and "new" as possible valid returns. I ordered the following as I # thought best. - if 'fail' in rval: - return 'fail' - elif 'replace' in rval: - return 'replace' - elif 'new' in rval: - return 'new' + if "fail" in rval: + return "fail" + elif "replace" in rval: + return "replace" + elif "new" in rval: + return "new" else: - return 'no change' + return "no change" def set_auth_key( - user, - key, - enc='ssh-rsa', - comment='', - options=None, - config='.ssh/authorized_keys', - cache_keys=None, - fingerprint_hash_type=None): - ''' + user, + key, + enc="ssh-rsa", + comment="", + options=None, + config=".ssh/authorized_keys", + cache_keys=None, + fingerprint_hash_type=None, +): + """ Add a key to the authorized_keys file. The "key" parameter must only be the string of text that is the encoded key. If the key begins with "ssh-rsa" or ends with user@host, remove those from the key before passing it to this @@ -707,16 +695,16 @@ def set_auth_key( .. code-block:: bash salt '*' ssh.set_auth_key <user> '<key>' enc='dsa' - ''' + """ if cache_keys is None: cache_keys = [] if len(key.split()) > 1: - return 'invalid' + return "invalid" enc = _refine_enc(enc) - uinfo = __salt__['user.info'](user) + uinfo = __salt__["user.info"](user) if not uinfo: - return 'fail' + return "fail" # A 'valid key' to us pretty much means 'decodable as base64', which is # the same filtering done when reading the authorized_keys file. Apply @@ -724,38 +712,40 @@ def set_auth_key( # subsequently be read) key_is_valid = _fingerprint(key, fingerprint_hash_type) is not None if not key_is_valid: - return 'Invalid public key' + return "Invalid public key" - status = check_key(user, - key, - enc, - comment, - options, - config=config, - cache_keys=cache_keys, - fingerprint_hash_type=fingerprint_hash_type) - if status == 'update': + status = check_key( + user, + key, + enc, + comment, + options, + config=config, + cache_keys=cache_keys, + fingerprint_hash_type=fingerprint_hash_type, + ) + if status == "update": _replace_auth_key(user, key, enc, comment, options or [], config) - return 'replace' - elif status == 'exists': - return 'no change' + return "replace" + elif status == "exists": + return "no change" else: auth_line = _format_auth_line(key, enc, comment, options) fconfig = _get_config_file(user, config) # Fail if the key lives under the user's homedir, and the homedir # doesn't exist - udir = uinfo.get('home', '') + udir = uinfo.get("home", "") if fconfig.startswith(udir) and not os.path.isdir(udir): - return 'fail' + return "fail" if not os.path.isdir(os.path.dirname(fconfig)): dpath = os.path.dirname(fconfig) os.makedirs(dpath) if not salt.utils.platform.is_windows(): if os.geteuid() == 0: - os.chown(dpath, uinfo['uid'], uinfo['gid']) + os.chown(dpath, uinfo["uid"], uinfo["gid"]) os.chmod(dpath, 448) # If SELINUX is available run a restorecon on the file - rcon = salt.utils.path.which('restorecon') + rcon = salt.utils.path.which("restorecon") if rcon: cmd = [rcon, dpath] subprocess.call(cmd) @@ -766,7 +756,7 @@ def set_auth_key( new_file = False try: - with salt.utils.files.fopen(fconfig, 'ab+') as _fh: + with salt.utils.files.fopen(fconfig, "ab+") as _fh: if new_file is False: # Let's make sure we have a new line at the end of the file _fh.seek(0, 2) @@ -774,32 +764,32 @@ def set_auth_key( # File isn't empty, check if last byte is a newline # If not, add one _fh.seek(-1, 2) - if _fh.read(1) != b'\n': - _fh.write(b'\n') + if _fh.read(1) != b"\n": + _fh.write(b"\n") _fh.write(salt.utils.stringutils.to_bytes(auth_line)) except (IOError, OSError) as exc: - msg = 'Could not write to key file: {0}' + msg = "Could not write to key file: {0}" raise CommandExecutionError(msg.format(exc)) if new_file: if not salt.utils.platform.is_windows(): if os.geteuid() == 0: - os.chown(fconfig, uinfo['uid'], uinfo['gid']) + os.chown(fconfig, uinfo["uid"], uinfo["gid"]) os.chmod(fconfig, 384) # If SELINUX is available run a restorecon on the file - rcon = salt.utils.path.which('restorecon') + rcon = salt.utils.path.which("restorecon") if rcon: cmd = [rcon, fconfig] subprocess.call(cmd) - return 'new' + return "new" def _get_matched_host_line_numbers(lines, enc): - ''' + """ Helper function which parses ssh-keygen -F function output and yield line number of known_hosts entries with encryption key type matching enc, one by one. - ''' + """ enc = enc if enc else "rsa" for i, line in enumerate(lines): if i % 2 == 0: @@ -811,38 +801,34 @@ def _get_matched_host_line_numbers(lines, enc): def _parse_openssh_output(lines, fingerprint_hash_type=None): - ''' + """ Helper function which parses ssh-keygen -F and ssh-keyscan function output and yield dict with keys information, one by one. - ''' + """ for line in lines: # We don't need any whitespace-only containing lines or arbitrary doubled newlines line = line.strip() - if line == '': + if line == "": continue - line += '\n' + line += "\n" - if line.startswith('#'): + if line.startswith("#"): continue try: hostname, enc, key = line.split() except ValueError: # incorrect format continue - fingerprint = _fingerprint(key, - fingerprint_hash_type=fingerprint_hash_type) + fingerprint = _fingerprint(key, fingerprint_hash_type=fingerprint_hash_type) if not fingerprint: continue - yield {'hostname': hostname, 'key': key, 'enc': enc, - 'fingerprint': fingerprint} + yield {"hostname": hostname, "key": key, "enc": enc, "fingerprint": fingerprint} -@salt.utils.decorators.path.which('ssh-keygen') -def get_known_host_entries(user, - hostname, - config=None, - port=None, - fingerprint_hash_type=None): - ''' +@salt.utils.decorators.path.which("ssh-keygen") +def get_known_host_entries( + user, hostname, config=None, port=None, fingerprint_hash_type=None +): + """ .. versionadded:: 2018.3.0 Return information about known host entries from the configfile, if any. @@ -853,32 +839,33 @@ def get_known_host_entries(user, .. code-block:: bash salt '*' ssh.get_known_host_entries <user> <hostname> - ''' + """ full = _get_known_hosts_file(config=config, user=user) if isinstance(full, dict): return full ssh_hostname = _hostname_and_port_to_ssh_hostname(hostname, port) - cmd = ['ssh-keygen', '-F', ssh_hostname, '-f', full] - lines = __salt__['cmd.run'](cmd, - ignore_retcode=True, - python_shell=False).splitlines() + cmd = ["ssh-keygen", "-F", ssh_hostname, "-f", full] + lines = __salt__["cmd.run"]( + cmd, ignore_retcode=True, python_shell=False + ).splitlines() known_host_entries = list( - _parse_openssh_output(lines, - fingerprint_hash_type=fingerprint_hash_type) + _parse_openssh_output(lines, fingerprint_hash_type=fingerprint_hash_type) ) return known_host_entries if known_host_entries else None -@salt.utils.decorators.path.which('ssh-keyscan') -def recv_known_host_entries(hostname, - enc=None, - port=None, - hash_known_hosts=True, - timeout=5, - fingerprint_hash_type=None): - ''' +@salt.utils.decorators.path.which("ssh-keyscan") +def recv_known_host_entries( + hostname, + enc=None, + port=None, + hash_known_hosts=True, + timeout=5, + fingerprint_hash_type=None, +): + """ .. versionadded:: 2018.3.0 Retrieve information about host public keys from remote server @@ -915,35 +902,43 @@ def recv_known_host_entries(hostname, .. code-block:: bash salt '*' ssh.recv_known_host_entries <hostname> enc=<enc> port=<port> - ''' + """ # The following list of OSes have an old version of openssh-clients # and thus require the '-t' option for ssh-keyscan - need_dash_t = ('CentOS-5',) + need_dash_t = ("CentOS-5",) - cmd = ['ssh-keyscan'] + cmd = ["ssh-keyscan"] if port: - cmd.extend(['-p', port]) + cmd.extend(["-p", port]) if enc: - cmd.extend(['-t', enc]) - if not enc and __grains__.get('osfinger') in need_dash_t: - cmd.extend(['-t', 'rsa']) + cmd.extend(["-t", enc]) + if not enc and __grains__.get("osfinger") in need_dash_t: + cmd.extend(["-t", "rsa"]) if hash_known_hosts: - cmd.append('-H') - cmd.extend(['-T', six.text_type(timeout)]) + cmd.append("-H") + cmd.extend(["-T", six.text_type(timeout)]) cmd.append(hostname) lines = None attempts = 5 while not lines and attempts > 0: attempts = attempts - 1 - lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - known_host_entries = list(_parse_openssh_output(lines, - fingerprint_hash_type=fingerprint_hash_type)) + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() + known_host_entries = list( + _parse_openssh_output(lines, fingerprint_hash_type=fingerprint_hash_type) + ) return known_host_entries if known_host_entries else None -def check_known_host(user=None, hostname=None, key=None, fingerprint=None, - config=None, port=None, fingerprint_hash_type=None): - ''' +def check_known_host( + user=None, + hostname=None, + key=None, + fingerprint=None, + config=None, + port=None, + fingerprint_hash_type=None, +): + """ Check the record in known_hosts file, either by its value or by fingerprint (it's enough to set up either key or fingerprint, you don't need to set up both). @@ -960,36 +955,38 @@ def check_known_host(user=None, hostname=None, key=None, fingerprint=None, .. code-block:: bash salt '*' ssh.check_known_host <user> <hostname> key='AAAA...FAaQ==' - ''' + """ if not hostname: - return {'status': 'error', - 'error': 'hostname argument required'} + return {"status": "error", "error": "hostname argument required"} if not user: - config = config or '/etc/ssh/ssh_known_hosts' + config = config or "/etc/ssh/ssh_known_hosts" else: - config = config or '.ssh/known_hosts' + config = config or ".ssh/known_hosts" - known_host_entries = get_known_host_entries(user, - hostname, - config=config, - port=port, - fingerprint_hash_type=fingerprint_hash_type) - known_keys = [h['key'] for h in known_host_entries] if known_host_entries else [] - known_fingerprints = [h['fingerprint'] for h in known_host_entries] if known_host_entries else [] + known_host_entries = get_known_host_entries( + user, + hostname, + config=config, + port=port, + fingerprint_hash_type=fingerprint_hash_type, + ) + known_keys = [h["key"] for h in known_host_entries] if known_host_entries else [] + known_fingerprints = ( + [h["fingerprint"] for h in known_host_entries] if known_host_entries else [] + ) if not known_host_entries: - return 'add' + return "add" if key: - return 'exists' if key in known_keys else 'update' + return "exists" if key in known_keys else "update" elif fingerprint: - return ('exists' if fingerprint in known_fingerprints - else 'update') + return "exists" if fingerprint in known_fingerprints else "update" else: - return 'exists' + return "exists" def rm_known_host(user=None, hostname=None, config=None, port=None): - ''' + """ Remove all keys belonging to hostname from a known_hosts file. CLI Example: @@ -997,10 +994,9 @@ def rm_known_host(user=None, hostname=None, config=None, port=None): .. code-block:: bash salt '*' ssh.rm_known_host <user> <hostname> - ''' + """ if not hostname: - return {'status': 'error', - 'error': 'hostname argument required'} + return {"status": "error", "error": "hostname argument required"} full = _get_known_hosts_file(config=config, user=user) @@ -1008,31 +1004,35 @@ def rm_known_host(user=None, hostname=None, config=None, port=None): return full if not os.path.isfile(full): - return {'status': 'error', - 'error': 'Known hosts file {0} does not exist'.format(full)} + return { + "status": "error", + "error": "Known hosts file {0} does not exist".format(full), + } ssh_hostname = _hostname_and_port_to_ssh_hostname(hostname, port) - cmd = ['ssh-keygen', '-R', ssh_hostname, '-f', full] - cmd_result = __salt__['cmd.run'](cmd, python_shell=False) + cmd = ["ssh-keygen", "-R", ssh_hostname, "-f", full] + cmd_result = __salt__["cmd.run"](cmd, python_shell=False) if not salt.utils.platform.is_windows(): # ssh-keygen creates a new file, thus a chown is required. if os.geteuid() == 0 and user: - uinfo = __salt__['user.info'](user) - os.chown(full, uinfo['uid'], uinfo['gid']) - return {'status': 'removed', 'comment': cmd_result} + uinfo = __salt__["user.info"](user) + os.chown(full, uinfo["uid"], uinfo["gid"]) + return {"status": "removed", "comment": cmd_result} -def set_known_host(user=None, - hostname=None, - fingerprint=None, - key=None, - port=None, - enc=None, - config=None, - hash_known_hosts=True, - timeout=5, - fingerprint_hash_type=None): - ''' +def set_known_host( + user=None, + hostname=None, + fingerprint=None, + key=None, + port=None, + enc=None, + config=None, + hash_known_hosts=True, + timeout=5, + fingerprint_hash_type=None, +): + """ Download SSH public key from remote host "hostname", optionally validate its fingerprint against "fingerprint" variable and save the record in the known_hosts file. @@ -1091,25 +1091,30 @@ def set_known_host(user=None, .. code-block:: bash salt '*' ssh.set_known_host <user> fingerprint='xx:xx:..:xx' enc='ssh-rsa' config='.ssh/known_hosts' - ''' + """ if not hostname: - return {'status': 'error', - 'error': 'hostname argument required'} + return {"status": "error", "error": "hostname argument required"} if port is not None and port != DEFAULT_SSH_PORT and hash_known_hosts: - return {'status': 'error', - 'error': 'argument port can not be used in ' - 'conjunction with argument hash_known_hosts'} + return { + "status": "error", + "error": "argument port can not be used in " + "conjunction with argument hash_known_hosts", + } update_required = False check_required = False - stored_host_entries = get_known_host_entries(user, - hostname, - config=config, - port=port, - fingerprint_hash_type=fingerprint_hash_type) - stored_keys = [h['key'] for h in stored_host_entries] if stored_host_entries else [] - stored_fingerprints = [h['fingerprint'] for h in stored_host_entries] if stored_host_entries else [] + stored_host_entries = get_known_host_entries( + user, + hostname, + config=config, + port=port, + fingerprint_hash_type=fingerprint_hash_type, + ) + stored_keys = [h["key"] for h in stored_host_entries] if stored_host_entries else [] + stored_fingerprints = ( + [h["fingerprint"] for h in stored_host_entries] if stored_host_entries else [] + ) if not stored_host_entries: update_required = True @@ -1121,32 +1126,43 @@ def set_known_host(user=None, check_required = True if not update_required and not check_required: - return {'status': 'exists', 'keys': stored_keys} + return {"status": "exists", "keys": stored_keys} if not key: - remote_host_entries = recv_known_host_entries(hostname, - enc=enc, - port=port, - hash_known_hosts=hash_known_hosts, - timeout=timeout, - fingerprint_hash_type=fingerprint_hash_type) + remote_host_entries = recv_known_host_entries( + hostname, + enc=enc, + port=port, + hash_known_hosts=hash_known_hosts, + timeout=timeout, + fingerprint_hash_type=fingerprint_hash_type, + ) # pylint: disable=not-an-iterable - known_keys = [h['key'] for h in remote_host_entries] if remote_host_entries else [] - known_fingerprints = [h['fingerprint'] for h in remote_host_entries] if remote_host_entries else [] + known_keys = ( + [h["key"] for h in remote_host_entries] if remote_host_entries else [] + ) + known_fingerprints = ( + [h["fingerprint"] for h in remote_host_entries] + if remote_host_entries + else [] + ) # pylint: enable=not-an-iterable if not remote_host_entries: - return {'status': 'error', - 'error': 'Unable to receive remote host keys'} + return {"status": "error", "error": "Unable to receive remote host keys"} if fingerprint and fingerprint not in known_fingerprints: - return {'status': 'error', - 'error': ('Remote host public keys found but none of their ' - 'fingerprints match the one you have provided')} + return { + "status": "error", + "error": ( + "Remote host public keys found but none of their " + "fingerprints match the one you have provided" + ), + } if check_required: for key in known_keys: if key in stored_keys: - return {'status': 'exists', 'keys': stored_keys} + return {"status": "exists", "keys": stored_keys} full = _get_known_hosts_file(config=config, user=user) @@ -1159,17 +1175,15 @@ def set_known_host(user=None, # remove existing known_host entry with matching hostname and encryption key type # use ssh-keygen -F to find the specific line(s) for this host + enc combo ssh_hostname = _hostname_and_port_to_ssh_hostname(hostname, port) - cmd = ['ssh-keygen', '-F', ssh_hostname, '-f', full] - lines = __salt__['cmd.run'](cmd, - ignore_retcode=True, - python_shell=False).splitlines() - remove_lines = list( - _get_matched_host_line_numbers(lines, enc) - ) + cmd = ["ssh-keygen", "-F", ssh_hostname, "-f", full] + lines = __salt__["cmd.run"]( + cmd, ignore_retcode=True, python_shell=False + ).splitlines() + remove_lines = list(_get_matched_host_line_numbers(lines, enc)) if remove_lines: try: - with salt.utils.files.fopen(full, 'r+') as ofile: + with salt.utils.files.fopen(full, "r+") as ofile: known_hosts_lines = salt.utils.data.decode(list(ofile)) # Delete from last line to first to avoid invalidating earlier indexes for line_no in sorted(remove_lines, reverse=True): @@ -1182,48 +1196,52 @@ def set_known_host(user=None, ) except (IOError, OSError) as exception: raise CommandExecutionError( - "Couldn't remove old entry(ies) from known hosts file: '{0}'".format(exception) + "Couldn't remove old entry(ies) from known hosts file: '{0}'".format( + exception + ) ) else: origmode = None # set up new value if key: - remote_host_entries = [{'hostname': hostname, 'enc': enc, 'key': key}] + remote_host_entries = [{"hostname": hostname, "enc": enc, "key": key}] lines = [] for entry in remote_host_entries: - if hash_known_hosts or port in [DEFAULT_SSH_PORT, None] or ':' in entry['hostname']: - line = '{hostname} {enc} {key}\n'.format(**entry) + if ( + hash_known_hosts + or port in [DEFAULT_SSH_PORT, None] + or ":" in entry["hostname"] + ): + line = "{hostname} {enc} {key}\n".format(**entry) else: - entry['port'] = port - line = '[{hostname}]:{port} {enc} {key}\n'.format(**entry) + entry["port"] = port + line = "[{hostname}]:{port} {enc} {key}\n".format(**entry) lines.append(line) # ensure ~/.ssh exists ssh_dir = os.path.dirname(full) if user: - uinfo = __salt__['user.info'](user) + uinfo = __salt__["user.info"](user) try: log.debug('Ensuring ssh config dir "%s" exists', ssh_dir) os.makedirs(ssh_dir) except OSError as exc: - if exc.args[1] == 'Permission denied': - log.error('Unable to create directory %s: ' - '%s', ssh_dir, exc.args[1]) - elif exc.args[1] == 'File exists': - log.debug('%s already exists, no need to create ' - 'it', ssh_dir) + if exc.args[1] == "Permission denied": + log.error("Unable to create directory %s: " "%s", ssh_dir, exc.args[1]) + elif exc.args[1] == "File exists": + log.debug("%s already exists, no need to create " "it", ssh_dir) else: # set proper ownership/permissions if user: - os.chown(ssh_dir, uinfo['uid'], uinfo['gid']) + os.chown(ssh_dir, uinfo["uid"], uinfo["gid"]) os.chmod(ssh_dir, 0o700) # write line to known_hosts file try: - with salt.utils.files.fopen(full, 'ab') as ofile: + with salt.utils.files.fopen(full, "ab") as ofile: ofile.writelines(salt.utils.data.encode(lines)) except (IOError, OSError) as exception: raise CommandExecutionError( @@ -1232,21 +1250,21 @@ def set_known_host(user=None, if not salt.utils.platform.is_windows(): if os.geteuid() == 0 and user: - os.chown(full, uinfo['uid'], uinfo['gid']) + os.chown(full, uinfo["uid"], uinfo["gid"]) if origmode: os.chmod(full, origmode) else: os.chmod(full, 0o600) if key and hash_known_hosts: - cmd_result = __salt__['ssh.hash_known_hosts'](user=user, config=full) + cmd_result = __salt__["ssh.hash_known_hosts"](user=user, config=full) - rval = {'status': 'updated', 'old': stored_host_entries, 'new': remote_host_entries} + rval = {"status": "updated", "old": stored_host_entries, "new": remote_host_entries} return rval def user_keys(user=None, pubfile=None, prvfile=None): - ''' + """ Return the user's ssh keys on the minion @@ -1265,9 +1283,9 @@ def user_keys(user=None, pubfile=None, prvfile=None): As you can see you can tell Salt not to read from the user's private (or public) key file by setting the file path to ``False``. This can be useful to prevent Salt from publishing private data via Salt Mine or others. - ''' + """ if not user: - user = __salt__['user.list_users']() + user = __salt__["user.list_users"]() if not isinstance(user, list): # only one so convert to list @@ -1276,9 +1294,9 @@ def user_keys(user=None, pubfile=None, prvfile=None): keys = {} for u in user: keys[u] = {} - userinfo = __salt__['user.info'](u) + userinfo = __salt__["user.info"](u) - if 'home' not in userinfo: + if "home" not in userinfo: # no home directory, skip continue @@ -1288,32 +1306,29 @@ def user_keys(user=None, pubfile=None, prvfile=None): userKeys.append(pubfile) elif pubfile is not False: # Add the default public keys - userKeys += ['id_rsa.pub', 'id_dsa.pub', - 'id_ecdsa.pub', 'id_ed25519.pub'] + userKeys += ["id_rsa.pub", "id_dsa.pub", "id_ecdsa.pub", "id_ed25519.pub"] if prvfile: userKeys.append(prvfile) elif prvfile is not False: # Add the default private keys - userKeys += ['id_rsa', 'id_dsa', 'id_ecdsa', 'id_ed25519'] + userKeys += ["id_rsa", "id_dsa", "id_ecdsa", "id_ed25519"] for key in userKeys: - if key.startswith('/'): + if key.startswith("/"): keyname = os.path.basename(key) fn_ = key else: # if not full path, assume key is in .ssh # in user's home directory keyname = key - fn_ = '{0}/.ssh/{1}'.format(userinfo['home'], key) + fn_ = "{0}/.ssh/{1}".format(userinfo["home"], key) if os.path.exists(fn_): try: - with salt.utils.files.fopen(fn_, 'r') as _fh: - keys[u][keyname] = ''.join( - salt.utils.data.decode( - _fh.readlines() - ) + with salt.utils.files.fopen(fn_, "r") as _fh: + keys[u][keyname] = "".join( + salt.utils.data.decode(_fh.readlines()) ).strip() except (IOError, OSError): pass @@ -1326,9 +1341,9 @@ def user_keys(user=None, pubfile=None, prvfile=None): return _keys -@salt.utils.decorators.path.which('ssh-keygen') +@salt.utils.decorators.path.which("ssh-keygen") def hash_known_hosts(user=None, config=None): - ''' + """ Hash all the hostnames in the known hosts file. @@ -1347,36 +1362,38 @@ def hash_known_hosts(user=None, config=None): salt '*' ssh.hash_known_hosts - ''' + """ full = _get_known_hosts_file(config=config, user=user) if isinstance(full, dict): return full # full contains error information if not os.path.isfile(full): - return {'status': 'error', - 'error': 'Known hosts file {0} does not exist'.format(full)} + return { + "status": "error", + "error": "Known hosts file {0} does not exist".format(full), + } origmode = os.stat(full).st_mode - cmd = ['ssh-keygen', '-H', '-f', full] - cmd_result = __salt__['cmd.run'](cmd, python_shell=False) + cmd = ["ssh-keygen", "-H", "-f", full] + cmd_result = __salt__["cmd.run"](cmd, python_shell=False) os.chmod(full, origmode) if not salt.utils.platform.is_windows(): # ssh-keygen creates a new file, thus a chown is required. if os.geteuid() == 0 and user: - uinfo = __salt__['user.info'](user) - os.chown(full, uinfo['uid'], uinfo['gid']) - return {'status': 'updated', 'comment': cmd_result} + uinfo = __salt__["user.info"](user) + os.chown(full, uinfo["uid"], uinfo["gid"]) + return {"status": "updated", "comment": cmd_result} def _hostname_and_port_to_ssh_hostname(hostname, port=DEFAULT_SSH_PORT): if not port or port == DEFAULT_SSH_PORT: return hostname else: - return '[{0}]:{1}'.format(hostname, port) + return "[{0}]:{1}".format(hostname, port) def key_is_encrypted(key): - ''' + """ .. versionadded:: 2015.8.7 Function to determine whether or not a private key is encrypted with a @@ -1390,5 +1407,5 @@ def key_is_encrypted(key): .. code-block:: bash salt '*' ssh.key_is_encrypted /root/id_rsa - ''' - return __utils__['ssh.key_is_encrypted'](key) + """ + return __utils__["ssh.key_is_encrypted"](key) diff --git a/salt/modules/ssh_pkg.py b/salt/modules/ssh_pkg.py index a401c8a8e71..26a53f3519f 100644 --- a/salt/modules/ssh_pkg.py +++ b/salt/modules/ssh_pkg.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Service support for the REST example -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -10,43 +10,43 @@ import logging # Import Salts libs import salt.utils.platform - log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Only work on proxy - ''' + """ try: - if salt.utils.platform.is_proxy() \ - and __opts__['proxy']['proxytype'] == 'ssh_sample': + if ( + salt.utils.platform.is_proxy() + and __opts__["proxy"]["proxytype"] == "ssh_sample" + ): return __virtualname__ except KeyError: return ( False, - 'The ssh_package execution module failed to load. Check the ' - 'proxy key in pillar.' + "The ssh_package execution module failed to load. Check the " + "proxy key in pillar.", ) return ( False, - 'The ssh_package execution module failed to load: only works on an ' - 'ssh_sample proxy minion.' + "The ssh_package execution module failed to load: only works on an " + "ssh_sample proxy minion.", ) def list_pkgs(versions_as_list=False, **kwargs): - return __proxy__['ssh_sample.package_list']() + return __proxy__["ssh_sample.package_list"]() -def install(name=None, refresh=False, fromrepo=None, - pkgs=None, sources=None, **kwargs): - return __proxy__['ssh_sample.package_install'](name, **kwargs) +def install(name=None, refresh=False, fromrepo=None, pkgs=None, sources=None, **kwargs): + return __proxy__["ssh_sample.package_install"](name, **kwargs) def remove(name=None, pkgs=None, **kwargs): - return __proxy__['ssh_sample.package_remove'](name) + return __proxy__["ssh_sample.package_remove"](name) diff --git a/salt/modules/ssh_service.py b/salt/modules/ssh_service.py index 90953114718..d7727703429 100644 --- a/salt/modules/ssh_service.py +++ b/salt/modules/ssh_service.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Provide the service module for the proxy-minion SSH sample .. versionadded:: 2015.8.2 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import logging +from __future__ import absolute_import, print_function, unicode_literals + import fnmatch +import logging import re # Import Salt libs @@ -14,38 +15,38 @@ import salt.utils.platform log = logging.getLogger(__name__) -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" def __virtual__(): - ''' + """ Only work on systems that are a proxy minion - ''' + """ try: - if salt.utils.platform.is_proxy() \ - and __opts__['proxy']['proxytype'] == 'ssh_sample': + if ( + salt.utils.platform.is_proxy() + and __opts__["proxy"]["proxytype"] == "ssh_sample" + ): return __virtualname__ except KeyError: return ( False, - 'The ssh_service execution module failed to load. Check the ' - 'proxy key in pillar.' + "The ssh_service execution module failed to load. Check the " + "proxy key in pillar.", ) return ( False, - 'The ssh_service execution module failed to load: only works on an ' - 'ssh_sample proxy minion.' + "The ssh_service execution module failed to load: only works on an " + "ssh_sample proxy minion.", ) def get_all(): - ''' + """ Return a list of all available services CLI Example: @@ -53,13 +54,13 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' - proxy_fn = 'ssh_sample.service_list' + """ + proxy_fn = "ssh_sample.service_list" return __proxy__[proxy_fn]() def list_(): - ''' + """ Return a list of all available services. CLI Example: @@ -67,12 +68,12 @@ def list_(): .. code-block:: bash salt '*' service.list - ''' + """ return get_all() def start(name, sig=None): - ''' + """ Start the specified service on the ssh_sample CLI Example: @@ -80,14 +81,14 @@ def start(name, sig=None): .. code-block:: bash salt '*' service.start <service name> - ''' + """ - proxy_fn = 'ssh_sample.service_start' + proxy_fn = "ssh_sample.service_start" return __proxy__[proxy_fn](name) def stop(name, sig=None): - ''' + """ Stop the specified service on the rest_sample CLI Example: @@ -95,27 +96,27 @@ def stop(name, sig=None): .. code-block:: bash salt '*' service.stop <service name> - ''' - proxy_fn = 'ssh_sample.service_stop' + """ + proxy_fn = "ssh_sample.service_stop" return __proxy__[proxy_fn](name) def restart(name, sig=None): - ''' + """ Restart the specified service with rest_sample CLI Example: .. code-block:: bash salt '*' service.restart <service name> - ''' + """ - proxy_fn = 'ssh_sample.service_restart' + proxy_fn = "ssh_sample.service_restart" return __proxy__[proxy_fn](name) def status(name, sig=None): - ''' + """ Return the status for a service via ssh_sample. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -136,10 +137,10 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status <service name> - ''' + """ - proxy_fn = 'ssh_sample.service_status' - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + proxy_fn = "ssh_sample.service_status" + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: @@ -147,7 +148,7 @@ def status(name, sig=None): results = {} for service in services: resp = __proxy__[proxy_fn](service) - if resp['comment'] == 'running': + if resp["comment"] == "running": results[service] = True else: results[service] = False @@ -157,14 +158,14 @@ def status(name, sig=None): def running(name, sig=None): - ''' + """ Return whether this service is running. - ''' + """ return status(name).get(name, False) def enabled(name, sig=None): - ''' + """ Only the 'redbull' service is 'enabled' in the test - ''' - return name == 'redbull' + """ + return name == "redbull" diff --git a/salt/modules/state.py b/salt/modules/state.py index ec1e1edb42e..4edca2de1d3 100644 --- a/salt/modules/state.py +++ b/salt/modules/state.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Control the state system on the minion. State Caching @@ -9,10 +9,11 @@ When a highstate is called, the minion automatically caches a copy of the last high data. If you then run a highstate with cache=True it will use that cached highdata and won't hit the fileserver except for ``salt://`` links in the states themselves. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import shutil @@ -23,6 +24,7 @@ import time # Import salt libs import salt.config +import salt.defaults.exitcodes import salt.payload import salt.state import salt.utils.args @@ -39,44 +41,41 @@ import salt.utils.state import salt.utils.stringutils import salt.utils.url import salt.utils.versions -import salt.defaults.exitcodes from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.runners.state import orchestrate as _orchestrate -from salt.utils.odict import OrderedDict # Import 3rd-party libs from salt.ext import six +from salt.runners.state import orchestrate as _orchestrate +from salt.utils.odict import OrderedDict -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] __outputter__ = { - 'sls': 'highstate', - 'sls_id': 'highstate', - 'pkg': 'highstate', - 'top': 'highstate', - 'single': 'highstate', - 'highstate': 'highstate', - 'template': 'highstate', - 'template_str': 'highstate', - 'apply_': 'highstate', - 'request': 'highstate', - 'check_request': 'highstate', - 'run_request': 'highstate', + "sls": "highstate", + "sls_id": "highstate", + "pkg": "highstate", + "top": "highstate", + "single": "highstate", + "highstate": "highstate", + "template": "highstate", + "template_str": "highstate", + "apply_": "highstate", + "request": "highstate", + "check_request": "highstate", + "run_request": "highstate", } -__func_alias__ = { - 'apply_': 'apply' -} +__func_alias__ = {"apply_": "apply"} log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'state' +__virtualname__ = "state" def __virtual__(): - ''' + """ Set the virtualname - ''' + """ # Update global namespace with functions that are cloned in this module global _orchestrate _orchestrate = salt.utils.functools.namespaced_function(_orchestrate, globals()) @@ -85,45 +84,52 @@ def __virtual__(): def _filter_running(runnings): - ''' + """ Filter out the result: True + no changes data - ''' - ret = dict((tag, value) for tag, value in six.iteritems(runnings) - if not value['result'] or value['changes']) + """ + ret = dict( + (tag, value) + for tag, value in six.iteritems(runnings) + if not value["result"] or value["changes"] + ) return ret def _set_retcode(ret, highstate=None): - ''' + """ Set the return code based on the data back from the state system - ''' + """ # Set default retcode to 0 - __context__['retcode'] = salt.defaults.exitcodes.EX_OK + __context__["retcode"] = salt.defaults.exitcodes.EX_OK if isinstance(ret, list): - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return - if not __utils__['state.check_result'](ret, highstate=highstate): - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_FAILURE + if not __utils__["state.check_result"](ret, highstate=highstate): + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_FAILURE def _get_pillar_errors(kwargs, pillar=None): - ''' + """ Checks all pillars (external and internal) for errors. Return an error message, if anywhere or None. :param kwargs: dictionary of options :param pillar: external pillar :return: None or an error message - ''' - return None if kwargs.get('force') else (pillar or {}).get('_errors', __pillar__.get('_errors')) or None + """ + return ( + None + if kwargs.get("force") + else (pillar or {}).get("_errors", __pillar__.get("_errors")) or None + ) def _wait(jid): - ''' + """ Wait for all previously started state jobs to finish running - ''' + """ if jid is None: jid = salt.utils.jid.gen_jid(__opts__) states = _prior_running_states(jid) @@ -133,45 +139,47 @@ def _wait(jid): def _snapper_pre(opts, jid): - ''' + """ Create a snapper pre snapshot - ''' + """ snapper_pre = None try: - if not opts['test'] and __opts__.get('snapper_states'): + if not opts["test"] and __opts__.get("snapper_states"): # Run the snapper pre snapshot - snapper_pre = __salt__['snapper.create_snapshot']( - config=__opts__.get('snapper_states_config', 'root'), - snapshot_type='pre', - description='Salt State run for jid {0}'.format(jid), - __pub_jid=jid) + snapper_pre = __salt__["snapper.create_snapshot"]( + config=__opts__.get("snapper_states_config", "root"), + snapshot_type="pre", + description="Salt State run for jid {0}".format(jid), + __pub_jid=jid, + ) except Exception: # pylint: disable=broad-except - log.error('Failed to create snapper pre snapshot for jid: %s', jid) + log.error("Failed to create snapper pre snapshot for jid: %s", jid) return snapper_pre def _snapper_post(opts, jid, pre_num): - ''' + """ Create the post states snapshot - ''' + """ try: - if not opts['test'] and __opts__.get('snapper_states') and pre_num: + if not opts["test"] and __opts__.get("snapper_states") and pre_num: # Run the snapper pre snapshot - __salt__['snapper.create_snapshot']( - config=__opts__.get('snapper_states_config', 'root'), - snapshot_type='post', - pre_number=pre_num, - description='Salt State run for jid {0}'.format(jid), - __pub_jid=jid) + __salt__["snapper.create_snapshot"]( + config=__opts__.get("snapper_states_config", "root"), + snapshot_type="post", + pre_number=pre_num, + description="Salt State run for jid {0}".format(jid), + __pub_jid=jid, + ) except Exception: # pylint: disable=broad-except - log.error('Failed to create snapper pre snapshot for jid: %s', jid) + log.error("Failed to create snapper pre snapshot for jid: %s", jid) def _get_pause(jid, state_id=None): - ''' + """ Return the pause information for a given jid - ''' - pause_dir = os.path.join(__opts__['cachedir'], 'state_pause') + """ + pause_dir = os.path.join(__opts__["cachedir"], "state_pause") pause_path = os.path.join(pause_dir, jid) if not os.path.exists(pause_dir): try: @@ -184,21 +192,21 @@ def _get_pause(jid, state_id=None): if state_id not in data: data[state_id] = {} if os.path.exists(pause_path): - with salt.utils.files.fopen(pause_path, 'rb') as fp_: + with salt.utils.files.fopen(pause_path, "rb") as fp_: data = salt.utils.msgpack.loads(fp_.read()) return data, pause_path def get_pauses(jid=None): - ''' + """ Get a report on all of the currently paused state runs and pause run settings. Optionally send in a jid if you only desire to see a single pause data set. - ''' + """ ret = {} - active = __salt__['saltutil.is_running']('state.*') - pause_dir = os.path.join(__opts__['cachedir'], 'state_pause') + active = __salt__["saltutil.is_running"]("state.*") + pause_dir = os.path.join(__opts__["cachedir"], "state_pause") if not os.path.exists(pause_dir): return ret if jid is None: @@ -210,7 +218,7 @@ def get_pauses(jid=None): for scan_jid in jids: is_active = False for active_data in active: - if active_data['jid'] == scan_jid: + if active_data["jid"] == scan_jid: is_active = True if not is_active: try: @@ -226,7 +234,7 @@ def get_pauses(jid=None): def soft_kill(jid, state_id=None): - ''' + """ Set up a state run to die before executing the given state id, this instructs a running state to safely exit at a given state id. This needs to pass in the jid of the running state. @@ -249,18 +257,18 @@ def soft_kill(jid, state_id=None): salt '*' state.soft_kill 20171130110407769519 salt '*' state.soft_kill 20171130110407769519 vim - ''' + """ jid = six.text_type(jid) if state_id is None: - state_id = '__all__' + state_id = "__all__" data, pause_path = _get_pause(jid, state_id) - data[state_id]['kill'] = True - with salt.utils.files.fopen(pause_path, 'wb') as fp_: + data[state_id]["kill"] = True + with salt.utils.files.fopen(pause_path, "wb") as fp_: fp_.write(salt.utils.msgpack.dumps(data)) def pause(jid, state_id=None, duration=None): - ''' + """ Set up a state id pause, this instructs a running state to pause at a given state id. This needs to pass in the jid of the running state and can optionally pass in a duration in seconds. If a state_id is not passed then @@ -283,19 +291,19 @@ def pause(jid, state_id=None, duration=None): salt '*' state.pause 20171130110407769519 salt '*' state.pause 20171130110407769519 vim salt '*' state.pause 20171130110407769519 vim 20 - ''' + """ jid = six.text_type(jid) if state_id is None: - state_id = '__all__' + state_id = "__all__" data, pause_path = _get_pause(jid, state_id) if duration: - data[state_id]['duration'] = int(duration) - with salt.utils.files.fopen(pause_path, 'wb') as fp_: + data[state_id]["duration"] = int(duration) + with salt.utils.files.fopen(pause_path, "wb") as fp_: fp_.write(salt.utils.msgpack.dumps(data)) def resume(jid, state_id=None): - ''' + """ Remove a pause from a jid, allowing it to continue. If the state_id is not specified then the a general pause will be resumed. @@ -315,26 +323,23 @@ def resume(jid, state_id=None): salt '*' state.resume 20171130110407769519 salt '*' state.resume 20171130110407769519 vim - ''' + """ jid = six.text_type(jid) if state_id is None: - state_id = '__all__' + state_id = "__all__" data, pause_path = _get_pause(jid, state_id) if state_id in data: data.pop(state_id) - if state_id == '__all__': + if state_id == "__all__": data = {} - with salt.utils.files.fopen(pause_path, 'wb') as fp_: + with salt.utils.files.fopen(pause_path, "wb") as fp_: fp_.write(salt.utils.msgpack.dumps(data)) -def orchestrate(mods, - saltenv='base', - test=None, - exclude=None, - pillar=None, - pillarenv=None): - ''' +def orchestrate( + mods, saltenv="base", test=None, exclude=None, pillar=None, pillarenv=None +): + """ .. versionadded:: 2016.11.0 Execute the orchestrate runner from a masterless minion. @@ -351,17 +356,19 @@ def orchestrate(mods, salt-call --local state.orchestrate webserver salt-call --local state.orchestrate webserver saltenv=dev test=True salt-call --local state.orchestrate webserver saltenv=dev pillarenv=aws - ''' - return _orchestrate(mods=mods, - saltenv=saltenv, - test=test, - exclude=exclude, - pillar=pillar, - pillarenv=pillarenv) + """ + return _orchestrate( + mods=mods, + saltenv=saltenv, + test=test, + exclude=exclude, + pillar=pillar, + pillarenv=pillarenv, + ) def running(concurrent=False): - ''' + """ Return a list of strings that contain state return data if a state function is already running. This function is used to prevent multiple state calls from being run at the same time. @@ -371,36 +378,36 @@ def running(concurrent=False): .. code-block:: bash salt '*' state.running - ''' + """ ret = [] if concurrent: return ret - active = __salt__['saltutil.is_running']('state.*') + active = __salt__["saltutil.is_running"]("state.*") for data in active: err = ( 'The function "{0}" is running as PID {1} and was started at ' - '{2} with jid {3}' + "{2} with jid {3}" ).format( - data['fun'], - data['pid'], - salt.utils.jid.jid_to_time(data['jid']), - data['jid'], + data["fun"], + data["pid"], + salt.utils.jid.jid_to_time(data["jid"]), + data["jid"], ) ret.append(err) return ret def _prior_running_states(jid): - ''' + """ Return a list of dicts of prior calls to state functions. This function is used to queue state calls so only one is run at a time. - ''' + """ ret = [] - active = __salt__['saltutil.is_running']('state.*') + active = __salt__["saltutil.is_running"]("state.*") for data in active: try: - data_jid = int(data['jid']) + data_jid = int(data["jid"]) except ValueError: continue if data_jid < int(jid): @@ -409,27 +416,30 @@ def _prior_running_states(jid): def _check_queue(queue, kwargs): - ''' + """ Utility function to queue the state run if requested and to check for conflicts in currently running states - ''' + """ if queue: - _wait(kwargs.get('__pub_jid')) + _wait(kwargs.get("__pub_jid")) else: - conflict = running(concurrent=kwargs.get('concurrent', False)) + conflict = running(concurrent=kwargs.get("concurrent", False)) if conflict: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return conflict def _get_initial_pillar(opts): - return __pillar__ if __opts__.get('__cli', None) == 'salt-call' \ - and opts['pillarenv'] == __opts__['pillarenv'] \ + return ( + __pillar__ + if __opts__.get("__cli", None) == "salt-call" + and opts["pillarenv"] == __opts__["pillarenv"] else None + ) def low(data, queue=False, **kwargs): - ''' + """ Execute a single low data call This function is mostly intended for testing the state system and is not @@ -440,7 +450,7 @@ def low(data, queue=False, **kwargs): .. code-block:: bash salt '*' state.low '{"state": "pkg", "fun": "installed", "name": "vi"}' - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict @@ -450,35 +460,35 @@ def low(data, queue=False, **kwargs): st_ = salt.state.State(__opts__) err = st_.verify_data(data) if err: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return err ret = st_.call(data) if isinstance(ret, list): - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR - if __utils__['state.check_result'](ret): - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_FAILURE + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + if __utils__["state.check_result"](ret): + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_FAILURE return ret def _get_test_value(test=None, **kwargs): - ''' + """ Determine the correct value for the test flag. - ''' + """ ret = True if test is None: if salt.utils.args.test_mode(test=test, **kwargs): ret = True - elif __salt__['config.get']('test', omit_opts=True) is True: + elif __salt__["config.get"]("test", omit_opts=True) is True: ret = True else: - ret = __opts__.get('test', None) + ret = __opts__.get("test", None) else: ret = test return ret def high(data, test=None, queue=False, **kwargs): - ''' + """ Execute the compound calls stored in a single set of high data This function is mostly intended for testing the state system and is not @@ -489,35 +499,41 @@ def high(data, test=None, queue=False, **kwargs): .. code-block:: bash salt '*' state.high '{"vim": {"pkg": ["installed"]}}' - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - opts['test'] = _get_test_value(test, **kwargs) + opts["test"] = _get_test_value(test, **kwargs) - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar_override is not None \ - and not isinstance(pillar_override, dict): + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if ( + pillar_enc is None + and pillar_override is not None + and not isinstance(pillar_override, dict) + ): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) try: - st_ = salt.state.State(opts, - pillar_override, - pillar_enc=pillar_enc, - proxy=__proxy__, - context=__context__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.State( + opts, + pillar_override, + pillar_enc=pillar_enc, + proxy=__proxy__, + context=__context__, + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.State(opts, - pillar_override, - pillar_enc=pillar_enc, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.State( + opts, + pillar_override, + pillar_enc=pillar_enc, + initial_pillar=_get_initial_pillar(opts), + ) ret = st_.call_high(data) _set_retcode(ret, highstate=data) @@ -525,7 +541,7 @@ def high(data, test=None, queue=False, **kwargs): def template(tem, queue=False, **kwargs): - ''' + """ Execute the information stored in a template file on the minion. This function does not ask a master for a SLS file to render but @@ -536,10 +552,10 @@ def template(tem, queue=False, **kwargs): .. code-block:: bash salt '*' state.template '<Path to template on the minion>' - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") conflict = _check_queue(queue, kwargs) if conflict is not None: @@ -547,29 +563,29 @@ def template(tem, queue=False, **kwargs): opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) try: - st_ = salt.state.HighState(opts, - context=__context__, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + context=__context__, + proxy=__proxy__, + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.HighState(opts, - context=__context__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, context=__context__, initial_pillar=_get_initial_pillar(opts) + ) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - raise CommandExecutionError('Pillar failed to render', info=errors) + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + raise CommandExecutionError("Pillar failed to render", info=errors) - if not tem.endswith('.sls'): - tem = '{sls}.sls'.format(sls=tem) - high_state, errors = st_.render_state(tem, - kwargs.get('saltenv', ''), - '', - None, - local=True) + if not tem.endswith(".sls"): + tem = "{sls}.sls".format(sls=tem) + high_state, errors = st_.render_state( + tem, kwargs.get("saltenv", ""), "", None, local=True + ) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return errors ret = st_.state.call_high(high_state) _set_retcode(ret, highstate=high_state) @@ -577,7 +593,7 @@ def template(tem, queue=False, **kwargs): def template_str(tem, queue=False, **kwargs): - ''' + """ Execute the information stored in a string from an sls template CLI Example: @@ -585,7 +601,7 @@ def template_str(tem, queue=False, **kwargs): .. code-block:: bash salt '*' state.template_str '<Template String>' - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict @@ -593,9 +609,9 @@ def template_str(tem, queue=False, **kwargs): opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) try: - st_ = salt.state.State(opts, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.State( + opts, proxy=__proxy__, initial_pillar=_get_initial_pillar(opts) + ) except NameError: st_ = salt.state.State(opts, initial_pillar=_get_initial_pillar(opts)) ret = st_.call_template_str(tem) @@ -604,7 +620,7 @@ def template_str(tem, queue=False, **kwargs): def apply_(mods=None, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 This function will call :mod:`state.highstate @@ -777,15 +793,14 @@ def apply_(mods=None, **kwargs): module types. .. versionadded:: 2017.7.8,2018.3.3,2019.2.0 - ''' + """ if mods: return sls(mods, **kwargs) return highstate(**kwargs) -def request(mods=None, - **kwargs): - ''' +def request(mods=None, **kwargs): + """ .. versionadded:: 2015.5.0 Request that the local admin execute a state run via @@ -799,35 +814,37 @@ def request(mods=None, salt '*' state.request salt '*' state.request stuff salt '*' state.request stuff,pkgs - ''' - kwargs['test'] = True + """ + kwargs["test"] = True ret = apply_(mods, **kwargs) - notify_path = os.path.join(__opts__['cachedir'], 'req_state.p') + notify_path = os.path.join(__opts__["cachedir"], "req_state.p") serial = salt.payload.Serial(__opts__) req = check_request() - req.update({kwargs.get('name', 'default'): { - 'test_run': ret, - 'mods': mods, - 'kwargs': kwargs + req.update( + { + kwargs.get("name", "default"): { + "test_run": ret, + "mods": mods, + "kwargs": kwargs, } - }) + } + ) with salt.utils.files.set_umask(0o077): try: if salt.utils.platform.is_windows(): # Make sure cache file isn't read-only - __salt__['cmd.run']('attrib -R "{0}"'.format(notify_path)) - with salt.utils.files.fopen(notify_path, 'w+b') as fp_: + __salt__["cmd.run"]('attrib -R "{0}"'.format(notify_path)) + with salt.utils.files.fopen(notify_path, "w+b") as fp_: serial.dump(req, fp_) except (IOError, OSError): log.error( - 'Unable to write state request file %s. Check permission.', - notify_path + "Unable to write state request file %s. Check permission.", notify_path ) return ret def check_request(name=None): - ''' + """ .. versionadded:: 2015.5.0 Return the state request information, if any @@ -837,11 +854,11 @@ def check_request(name=None): .. code-block:: bash salt '*' state.check_request - ''' - notify_path = os.path.join(__opts__['cachedir'], 'req_state.p') + """ + notify_path = os.path.join(__opts__["cachedir"], "req_state.p") serial = salt.payload.Serial(__opts__) if os.path.isfile(notify_path): - with salt.utils.files.fopen(notify_path, 'rb') as fp_: + with salt.utils.files.fopen(notify_path, "rb") as fp_: req = serial.load(fp_) if name: return req[name] @@ -850,7 +867,7 @@ def check_request(name=None): def clear_request(name=None): - ''' + """ .. versionadded:: 2015.5.0 Clear out the state execution request without executing it @@ -860,8 +877,8 @@ def clear_request(name=None): .. code-block:: bash salt '*' state.clear_request - ''' - notify_path = os.path.join(__opts__['cachedir'], 'req_state.p') + """ + notify_path = os.path.join(__opts__["cachedir"], "req_state.p") serial = salt.payload.Serial(__opts__) if not os.path.isfile(notify_path): return True @@ -880,19 +897,19 @@ def clear_request(name=None): try: if salt.utils.platform.is_windows(): # Make sure cache file isn't read-only - __salt__['cmd.run']('attrib -R "{0}"'.format(notify_path)) - with salt.utils.files.fopen(notify_path, 'w+b') as fp_: + __salt__["cmd.run"]('attrib -R "{0}"'.format(notify_path)) + with salt.utils.files.fopen(notify_path, "w+b") as fp_: serial.dump(req, fp_) except (IOError, OSError): log.error( - 'Unable to write state request file %s. Check permission.', - notify_path + "Unable to write state request file %s. Check permission.", + notify_path, ) return True -def run_request(name='default', **kwargs): - ''' +def run_request(name="default", **kwargs): + """ .. versionadded:: 2015.5.0 Execute the pending state request @@ -902,20 +919,20 @@ def run_request(name='default', **kwargs): .. code-block:: bash salt '*' state.run_request - ''' + """ req = check_request() if name not in req: return {} n_req = req[name] - if 'mods' not in n_req or 'kwargs' not in n_req: + if "mods" not in n_req or "kwargs" not in n_req: return {} - req[name]['kwargs'].update(kwargs) - if 'test' in n_req['kwargs']: - n_req['kwargs'].pop('test') + req[name]["kwargs"].update(kwargs) + if "test" in n_req["kwargs"]: + n_req["kwargs"].pop("test") if req: - ret = apply_(n_req['mods'], **n_req['kwargs']) + ret = apply_(n_req["mods"], **n_req["kwargs"]) try: - os.remove(os.path.join(__opts__['cachedir'], 'req_state.p')) + os.remove(os.path.join(__opts__["cachedir"], "req_state.p")) except (IOError, OSError): pass return ret @@ -923,7 +940,7 @@ def run_request(name='default', **kwargs): def highstate(test=None, queue=False, **kwargs): - ''' + """ Retrieve the state data from the salt master for this minion and execute it test @@ -1014,13 +1031,15 @@ def highstate(test=None, queue=False, **kwargs): salt '*' state.highstate exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]" salt '*' state.highstate pillar="{foo: 'Foo!', bar: 'Bar!'}" - ''' - if _disabled(['highstate']): - log.debug('Salt highstate run is disabled. To re-enable, run state.enable highstate') + """ + if _disabled(["highstate"]): + log.debug( + "Salt highstate run is disabled. To re-enable, run state.enable highstate" + ) ret = { - 'name': 'Salt highstate run is disabled. To re-enable, run state.enable highstate', - 'result': 'False', - 'comment': 'Disabled' + "name": "Salt highstate run is disabled. To re-enable, run state.enable highstate", + "result": "False", + "comment": "Disabled", } return ret @@ -1028,82 +1047,90 @@ def highstate(test=None, queue=False, **kwargs): if conflict is not None: return conflict - orig_test = __opts__.get('test', None) + orig_test = __opts__.get("test", None) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - opts['test'] = _get_test_value(test, **kwargs) + opts["test"] = _get_test_value(test, **kwargs) - if 'env' in kwargs: + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") - if 'saltenv' in kwargs: - opts['saltenv'] = kwargs['saltenv'] + if "saltenv" in kwargs: + opts["saltenv"] = kwargs["saltenv"] - if 'pillarenv' in kwargs: - opts['pillarenv'] = kwargs['pillarenv'] + if "pillarenv" in kwargs: + opts["pillarenv"] = kwargs["pillarenv"] - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar_override is not None \ - and not isinstance(pillar_override, dict): + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if ( + pillar_enc is None + and pillar_override is not None + and not isinstance(pillar_override, dict) + ): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) try: - st_ = salt.state.HighState(opts, - pillar_override, - kwargs.get('__pub_jid'), - pillar_enc=pillar_enc, - proxy=__proxy__, - context=__context__, - mocked=kwargs.get('mock', False), - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + kwargs.get("__pub_jid"), + pillar_enc=pillar_enc, + proxy=__proxy__, + context=__context__, + mocked=kwargs.get("mock", False), + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.HighState(opts, - pillar_override, - kwargs.get('__pub_jid'), - pillar_enc=pillar_enc, - mocked=kwargs.get('mock', False), - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + kwargs.get("__pub_jid"), + pillar_enc=pillar_enc, + mocked=kwargs.get("mock", False), + initial_pillar=_get_initial_pillar(opts), + ) - errors = _get_pillar_errors(kwargs, st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - return ['Pillar failed to render with the following messages:'] + errors + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + return ["Pillar failed to render with the following messages:"] + errors st_.push_active() - orchestration_jid = kwargs.get('orchestration_jid') - snapper_pre = _snapper_pre(opts, kwargs.get('__pub_jid', 'called localy')) + orchestration_jid = kwargs.get("orchestration_jid") + snapper_pre = _snapper_pre(opts, kwargs.get("__pub_jid", "called localy")) try: ret = st_.call_highstate( - exclude=kwargs.get('exclude', []), - cache=kwargs.get('cache', None), - cache_name=kwargs.get('cache_name', 'highstate'), - force=kwargs.get('force', False), - whitelist=kwargs.get('whitelist'), - orchestration_jid=orchestration_jid) + exclude=kwargs.get("exclude", []), + cache=kwargs.get("cache", None), + cache_name=kwargs.get("cache_name", "highstate"), + force=kwargs.get("force", False), + whitelist=kwargs.get("whitelist"), + orchestration_jid=orchestration_jid, + ) finally: st_.pop_active() - if isinstance(ret, dict) and (__salt__['config.option']('state_data', '') == 'terse' or - kwargs.get('terse')): + if isinstance(ret, dict) and ( + __salt__["config.option"]("state_data", "") == "terse" or kwargs.get("terse") + ): ret = _filter_running(ret) _set_retcode(ret, highstate=st_.building_highstate) - _snapper_post(opts, kwargs.get('__pub_jid', 'called localy'), snapper_pre) + _snapper_post(opts, kwargs.get("__pub_jid", "called localy"), snapper_pre) # Work around Windows multiprocessing bug, set __opts__['test'] back to # value from before this function was run. - __opts__['test'] = orig_test + __opts__["test"] = orig_test return ret def sls(mods, test=None, exclude=None, queue=False, sync_mods=None, **kwargs): - ''' + """ Execute the states in one or more SLS files test @@ -1220,19 +1247,19 @@ def sls(mods, test=None, exclude=None, queue=False, sync_mods=None, **kwargs): salt '*' state.sls core exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]" salt '*' state.sls myslsfile pillar="{foo: 'Foo!', bar: 'Bar!'}" - ''' - concurrent = kwargs.get('concurrent', False) - if 'env' in kwargs: + """ + concurrent = kwargs.get("concurrent", False) + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") # Modification to __opts__ lost after this if-else if queue: - _wait(kwargs.get('__pub_jid')) + _wait(kwargs.get("__pub_jid")) else: conflict = running(concurrent) if conflict: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return conflict if isinstance(mods, list): @@ -1243,136 +1270,139 @@ def sls(mods, test=None, exclude=None, queue=False, sync_mods=None, **kwargs): if disabled: for state in disabled: log.debug( - 'Salt state %s is disabled. To re-enable, run ' - 'state.enable %s', state, state + "Salt state %s is disabled. To re-enable, run " "state.enable %s", + state, + state, ) - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return disabled - orig_test = __opts__.get('test', None) + orig_test = __opts__.get("test", None) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - opts['test'] = _get_test_value(test, **kwargs) + opts["test"] = _get_test_value(test, **kwargs) # Since this is running a specific SLS file (or files), fall back to the # 'base' saltenv if none is configured and none was passed. - if opts['saltenv'] is None: - opts['saltenv'] = 'base' + if opts["saltenv"] is None: + opts["saltenv"] = "base" - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar_override is not None \ - and not isinstance(pillar_override, dict): + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if ( + pillar_enc is None + and pillar_override is not None + and not isinstance(pillar_override, dict) + ): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) serial = salt.payload.Serial(__opts__) cfn = os.path.join( - __opts__['cachedir'], - '{0}.cache.p'.format(kwargs.get('cache_name', 'highstate')) - ) + __opts__["cachedir"], + "{0}.cache.p".format(kwargs.get("cache_name", "highstate")), + ) if sync_mods is True: - sync_mods = ['all'] + sync_mods = ["all"] if sync_mods is not None: sync_mods = salt.utils.args.split_input(sync_mods) else: sync_mods = [] - if 'all' in sync_mods and sync_mods != ['all']: + if "all" in sync_mods and sync_mods != ["all"]: # Prevent unnecessary extra syncing - sync_mods = ['all'] + sync_mods = ["all"] for module_type in sync_mods: try: - __salt__['saltutil.sync_{0}'.format(module_type)]( - saltenv=opts['saltenv'] - ) + __salt__["saltutil.sync_{0}".format(module_type)](saltenv=opts["saltenv"]) except KeyError: - log.warning( - 'Invalid custom module type \'%s\', ignoring', - module_type - ) + log.warning("Invalid custom module type '%s', ignoring", module_type) try: - st_ = salt.state.HighState(opts, - pillar_override, - kwargs.get('__pub_jid'), - pillar_enc=pillar_enc, - proxy=__proxy__, - context=__context__, - mocked=kwargs.get('mock', False), - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + kwargs.get("__pub_jid"), + pillar_enc=pillar_enc, + proxy=__proxy__, + context=__context__, + mocked=kwargs.get("mock", False), + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.HighState(opts, - pillar_override, - kwargs.get('__pub_jid'), - pillar_enc=pillar_enc, - mocked=kwargs.get('mock', False), - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + kwargs.get("__pub_jid"), + pillar_enc=pillar_enc, + mocked=kwargs.get("mock", False), + initial_pillar=_get_initial_pillar(opts), + ) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - return ['Pillar failed to render with the following messages:'] + errors + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + return ["Pillar failed to render with the following messages:"] + errors - orchestration_jid = kwargs.get('orchestration_jid') + orchestration_jid = kwargs.get("orchestration_jid") with salt.utils.files.set_umask(0o077): - if kwargs.get('cache'): + if kwargs.get("cache"): if os.path.isfile(cfn): - with salt.utils.files.fopen(cfn, 'rb') as fp_: + with salt.utils.files.fopen(cfn, "rb") as fp_: high_ = serial.load(fp_) return st_.state.call_high(high_, orchestration_jid) # If the state file is an integer, convert to a string then to unicode if isinstance(mods, six.integer_types): - mods = salt.utils.stringutils.to_unicode(str(mods)) # future lint: disable=blacklisted-function + mods = salt.utils.stringutils.to_unicode( + str(mods) + ) # future lint: disable=blacklisted-function mods = salt.utils.args.split_input(mods) st_.push_active() try: - high_, errors = st_.render_highstate({opts['saltenv']: mods}) + high_, errors = st_.render_highstate({opts["saltenv"]: mods}) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return errors if exclude: exclude = salt.utils.args.split_input(exclude) - if '__exclude__' in high_: - high_['__exclude__'].extend(exclude) + if "__exclude__" in high_: + high_["__exclude__"].extend(exclude) else: - high_['__exclude__'] = exclude - snapper_pre = _snapper_pre(opts, kwargs.get('__pub_jid', 'called localy')) + high_["__exclude__"] = exclude + snapper_pre = _snapper_pre(opts, kwargs.get("__pub_jid", "called localy")) ret = st_.state.call_high(high_, orchestration_jid) finally: st_.pop_active() - if __salt__['config.option']('state_data', '') == 'terse' or kwargs.get('terse'): + if __salt__["config.option"]("state_data", "") == "terse" or kwargs.get("terse"): ret = _filter_running(ret) - cache_file = os.path.join(__opts__['cachedir'], 'sls.p') + cache_file = os.path.join(__opts__["cachedir"], "sls.p") with salt.utils.files.set_umask(0o077): try: if salt.utils.platform.is_windows(): # Make sure cache file isn't read-only - __salt__['cmd.run'](['attrib', '-R', cache_file], python_shell=False) - with salt.utils.files.fopen(cache_file, 'w+b') as fp_: + __salt__["cmd.run"](["attrib", "-R", cache_file], python_shell=False) + with salt.utils.files.fopen(cache_file, "w+b") as fp_: serial.dump(ret, fp_) except (IOError, OSError): log.error( - 'Unable to write to SLS cache file %s. Check permission.', - cache_file + "Unable to write to SLS cache file %s. Check permission.", cache_file ) _set_retcode(ret, high_) # Work around Windows multiprocessing bug, set __opts__['test'] back to # value from before this function was run. - __opts__['test'] = orig_test + __opts__["test"] = orig_test try: - with salt.utils.files.fopen(cfn, 'w+b') as fp_: + with salt.utils.files.fopen(cfn, "w+b") as fp_: try: serial.dump(high_, fp_) except TypeError: @@ -1380,16 +1410,16 @@ def sls(mods, test=None, exclude=None, queue=False, sync_mods=None, **kwargs): pass except (IOError, OSError): log.error( - 'Unable to write to highstate cache file %s. Do you have permissions?', - cfn + "Unable to write to highstate cache file %s. Do you have permissions?", + cfn, ) - _snapper_post(opts, kwargs.get('__pub_jid', 'called localy'), snapper_pre) + _snapper_post(opts, kwargs.get("__pub_jid", "called localy"), snapper_pre) return ret def top(topfn, test=None, queue=False, **kwargs): - ''' + """ Execute a specific top file instead of the default. This is useful to apply configurations from a different environment (for example, dev or prod), without modifying the default top file. @@ -1420,67 +1450,74 @@ def top(topfn, test=None, queue=False, **kwargs): salt '*' state.top reverse_top.sls salt '*' state.top prod_top.sls exclude=sls_to_exclude salt '*' state.top dev_top.sls exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]" - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict - orig_test = __opts__.get('test', None) + orig_test = __opts__.get("test", None) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - opts['test'] = _get_test_value(test, **kwargs) + opts["test"] = _get_test_value(test, **kwargs) - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar_override is not None \ - and not isinstance(pillar_override, dict): + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if ( + pillar_enc is None + and pillar_override is not None + and not isinstance(pillar_override, dict) + ): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) try: - st_ = salt.state.HighState(opts, - pillar_override, - pillar_enc=pillar_enc, - context=__context__, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + pillar_enc=pillar_enc, + context=__context__, + proxy=__proxy__, + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.HighState(opts, - pillar_override, - pillar_enc=pillar_enc, - context=__context__, - initial_pillar=_get_initial_pillar(opts)) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + st_ = salt.state.HighState( + opts, + pillar_override, + pillar_enc=pillar_enc, + context=__context__, + initial_pillar=_get_initial_pillar(opts), + ) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - return ['Pillar failed to render with the following messages:'] + errors + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + return ["Pillar failed to render with the following messages:"] + errors st_.push_active() - st_.opts['state_top'] = salt.utils.url.create(topfn) + st_.opts["state_top"] = salt.utils.url.create(topfn) ret = {} - orchestration_jid = kwargs.get('orchestration_jid') - if 'saltenv' in kwargs: - st_.opts['state_top_saltenv'] = kwargs['saltenv'] + orchestration_jid = kwargs.get("orchestration_jid") + if "saltenv" in kwargs: + st_.opts["state_top_saltenv"] = kwargs["saltenv"] try: - snapper_pre = _snapper_pre(opts, kwargs.get('__pub_jid', 'called localy')) + snapper_pre = _snapper_pre(opts, kwargs.get("__pub_jid", "called localy")) ret = st_.call_highstate( - exclude=kwargs.get('exclude', []), - cache=kwargs.get('cache', None), - cache_name=kwargs.get('cache_name', 'highstate'), - orchestration_jid=orchestration_jid) + exclude=kwargs.get("exclude", []), + cache=kwargs.get("cache", None), + cache_name=kwargs.get("cache_name", "highstate"), + orchestration_jid=orchestration_jid, + ) finally: st_.pop_active() _set_retcode(ret, highstate=st_.building_highstate) # Work around Windows multiprocessing bug, set __opts__['test'] back to # value from before this function was run. - _snapper_post(opts, kwargs.get('__pub_jid', 'called localy'), snapper_pre) - __opts__['test'] = orig_test + _snapper_post(opts, kwargs.get("__pub_jid", "called localy"), snapper_pre) + __opts__["test"] = orig_test return ret def show_highstate(queue=False, **kwargs): - ''' + """ Retrieve the highstate data from the salt master and display it Custom Pillar data can be passed with the ``pillar`` kwarg. @@ -1490,37 +1527,43 @@ def show_highstate(queue=False, **kwargs): .. code-block:: bash salt '*' state.show_highstate - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar_override is not None \ - and not isinstance(pillar_override, dict): + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if ( + pillar_enc is None + and pillar_override is not None + and not isinstance(pillar_override, dict) + ): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) try: - st_ = salt.state.HighState(opts, - pillar_override, - pillar_enc=pillar_enc, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + pillar_enc=pillar_enc, + proxy=__proxy__, + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.HighState(opts, - pillar_override, - pillar_enc=pillar_enc, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + pillar_enc=pillar_enc, + initial_pillar=_get_initial_pillar(opts), + ) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - raise CommandExecutionError('Pillar failed to render', info=errors) + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + raise CommandExecutionError("Pillar failed to render", info=errors) st_.push_active() try: @@ -1532,7 +1575,7 @@ def show_highstate(queue=False, **kwargs): def show_lowstate(queue=False, **kwargs): - ''' + """ List out the low data that will be applied to this minion CLI Example: @@ -1540,7 +1583,7 @@ def show_lowstate(queue=False, **kwargs): .. code-block:: bash salt '*' state.show_lowstate - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: assert False @@ -1548,17 +1591,16 @@ def show_lowstate(queue=False, **kwargs): opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) try: - st_ = salt.state.HighState(opts, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, proxy=__proxy__, initial_pillar=_get_initial_pillar(opts) + ) except NameError: - st_ = salt.state.HighState(opts, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState(opts, initial_pillar=_get_initial_pillar(opts)) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - raise CommandExecutionError('Pillar failed to render', info=errors) + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + raise CommandExecutionError("Pillar failed to render", info=errors) st_.push_active() try: @@ -1569,7 +1611,7 @@ def show_lowstate(queue=False, **kwargs): def show_state_usage(queue=False, **kwargs): - ''' + """ Retrieve the highstate data from the salt master to analyse used and unused states Custom Pillar data can be passed with the ``pillar`` kwarg. @@ -1579,18 +1621,16 @@ def show_state_usage(queue=False, **kwargs): .. code-block:: bash salt '*' state.show_state_usage - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict - pillar = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar is not None \ - and not isinstance(pillar, dict): + pillar = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if pillar_enc is None and pillar is not None and not isinstance(pillar, dict): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) st_ = salt.state.HighState(__opts__, pillar, pillar_enc=pillar_enc) @@ -1605,7 +1645,7 @@ def show_state_usage(queue=False, **kwargs): def show_states(queue=False, **kwargs): - ''' + """ Returns the list of states that will be applied on highstate. CLI Example: @@ -1616,7 +1656,7 @@ def show_states(queue=False, **kwargs): .. versionadded:: 2019.2.0 - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: assert False @@ -1624,17 +1664,16 @@ def show_states(queue=False, **kwargs): opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) try: - st_ = salt.state.HighState(opts, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, proxy=__proxy__, initial_pillar=_get_initial_pillar(opts) + ) except NameError: - st_ = salt.state.HighState(opts, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState(opts, initial_pillar=_get_initial_pillar(opts)) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - raise CommandExecutionError('Pillar failed to render', info=errors) + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + raise CommandExecutionError("Pillar failed to render", info=errors) st_.push_active() states = OrderedDict() @@ -1648,7 +1687,7 @@ def show_states(queue=False, **kwargs): if not isinstance(s, dict): _set_retcode(result) return result - states[s['__sls__']] = True + states[s["__sls__"]] = True finally: st_.pop_active() @@ -1656,7 +1695,7 @@ def show_states(queue=False, **kwargs): def sls_id(id_, mods, test=None, queue=False, **kwargs): - ''' + """ Call a single ID from the named module(s) and handle all requisites The state ID comes *before* the module ID(s) on the command line. @@ -1700,50 +1739,56 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs): salt '*' state.sls_id my_state my_module salt '*' state.sls_id my_state my_module,a_common_module - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict - orig_test = __opts__.get('test', None) + orig_test = __opts__.get("test", None) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - opts['test'] = _get_test_value(test, **kwargs) + opts["test"] = _get_test_value(test, **kwargs) # Since this is running a specific ID within a specific SLS file, fall back # to the 'base' saltenv if none is configured and none was passed. - if opts['saltenv'] is None: - opts['saltenv'] = 'base' + if opts["saltenv"] is None: + opts["saltenv"] = "base" - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar_override is not None \ - and not isinstance(pillar_override, dict): + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if ( + pillar_enc is None + and pillar_override is not None + and not isinstance(pillar_override, dict) + ): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) try: - st_ = salt.state.HighState(opts, - pillar_override, - pillar_enc=pillar_enc, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + pillar_enc=pillar_enc, + proxy=__proxy__, + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.HighState(opts, - pillar_override, - pillar_enc=pillar_enc, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + pillar_enc=pillar_enc, + initial_pillar=_get_initial_pillar(opts), + ) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - return ['Pillar failed to render with the following messages:'] + errors + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + return ["Pillar failed to render with the following messages:"] + errors split_mods = salt.utils.args.split_input(mods) st_.push_active() try: - high_, errors = st_.render_highstate({opts['saltenv']: split_mods}) + high_, errors = st_.render_highstate({opts["saltenv"]: split_mods}) finally: st_.pop_active() errors += st_.state.verify_high(high_) @@ -1754,28 +1799,28 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs): # but it is required to get the unit tests to pass. errors.extend(req_in_errors) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return errors chunks = st_.state.compile_high_data(high_) ret = {} for chunk in chunks: - if chunk.get('__id__', '') == id_: + if chunk.get("__id__", "") == id_: ret.update(st_.state.call_chunk(chunk, {}, chunks)) _set_retcode(ret, highstate=highstate) # Work around Windows multiprocessing bug, set __opts__['test'] back to # value from before this function was run. - __opts__['test'] = orig_test + __opts__["test"] = orig_test if not ret: raise SaltInvocationError( - 'No matches for ID \'{0}\' found in SLS \'{1}\' within saltenv ' - '\'{2}\''.format(id_, mods, opts['saltenv']) + "No matches for ID '{0}' found in SLS '{1}' within saltenv " + "'{2}'".format(id_, mods, opts["saltenv"]) ) return ret def show_low_sls(mods, test=None, queue=False, **kwargs): - ''' + """ Display the low data from a specific sls. The default environment is ``base``, use ``saltenv`` to specify a different environment. @@ -1806,67 +1851,71 @@ def show_low_sls(mods, test=None, queue=False, **kwargs): salt '*' state.show_low_sls foo salt '*' state.show_low_sls foo saltenv=dev - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict - orig_test = __opts__.get('test', None) + orig_test = __opts__.get("test", None) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - opts['test'] = _get_test_value(test, **kwargs) + opts["test"] = _get_test_value(test, **kwargs) # Since this is dealing with a specific SLS file (or files), fall back to # the 'base' saltenv if none is configured and none was passed. - if opts['saltenv'] is None: - opts['saltenv'] = 'base' + if opts["saltenv"] is None: + opts["saltenv"] = "base" - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar_override is not None \ - and not isinstance(pillar_override, dict): + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if ( + pillar_enc is None + and pillar_override is not None + and not isinstance(pillar_override, dict) + ): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) try: - st_ = salt.state.HighState(opts, - pillar_override, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + proxy=__proxy__, + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.HighState(opts, - pillar_override, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, pillar_override, initial_pillar=_get_initial_pillar(opts) + ) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - raise CommandExecutionError('Pillar failed to render', info=errors) + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + raise CommandExecutionError("Pillar failed to render", info=errors) mods = salt.utils.args.split_input(mods) st_.push_active() try: - high_, errors = st_.render_highstate({opts['saltenv']: mods}) + high_, errors = st_.render_highstate({opts["saltenv"]: mods}) finally: st_.pop_active() errors += st_.state.verify_high(high_) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return errors ret = st_.state.compile_high_data(high_) # Work around Windows multiprocessing bug, set __opts__['test'] back to # value from before this function was run. - __opts__['test'] = orig_test + __opts__["test"] = orig_test return ret def show_sls(mods, test=None, queue=False, **kwargs): - ''' + """ Display the state data from a specific sls or list of sls files on the master. The default environment is ``base``, use ``saltenv`` to specify a different environment. @@ -1891,69 +1940,75 @@ def show_sls(mods, test=None, queue=False, **kwargs): .. code-block:: bash salt '*' state.show_sls core,edit.vim saltenv=dev - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict - orig_test = __opts__.get('test', None) + orig_test = __opts__.get("test", None) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - opts['test'] = _get_test_value(test, **kwargs) + opts["test"] = _get_test_value(test, **kwargs) # Since this is dealing with a specific SLS file (or files), fall back to # the 'base' saltenv if none is configured and none was passed. - if opts['saltenv'] is None: - opts['saltenv'] = 'base' + if opts["saltenv"] is None: + opts["saltenv"] = "base" - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar_override is not None \ - and not isinstance(pillar_override, dict): + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if ( + pillar_enc is None + and pillar_override is not None + and not isinstance(pillar_override, dict) + ): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) try: - st_ = salt.state.HighState(opts, - pillar_override, - pillar_enc=pillar_enc, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + pillar_enc=pillar_enc, + proxy=__proxy__, + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.HighState(opts, - pillar_override, - pillar_enc=pillar_enc, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, + pillar_override, + pillar_enc=pillar_enc, + initial_pillar=_get_initial_pillar(opts), + ) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - raise CommandExecutionError('Pillar failed to render', info=errors) + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + raise CommandExecutionError("Pillar failed to render", info=errors) mods = salt.utils.args.split_input(mods) st_.push_active() try: - high_, errors = st_.render_highstate({opts['saltenv']: mods}) + high_, errors = st_.render_highstate({opts["saltenv"]: mods}) finally: st_.pop_active() errors += st_.state.verify_high(high_) # Work around Windows multiprocessing bug, set __opts__['test'] back to # value from before this function was run. - __opts__['test'] = orig_test + __opts__["test"] = orig_test if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return errors return high_ def sls_exists(mods, test=None, queue=False, **kwargs): - ''' + """ Tests for the existance the of a specific SLS or list of SLS files on the master. Similar to :py:func:`state.show_sls <salt.modules.state.show_sls>`, rather than returning state details, returns True or False. The default @@ -1970,15 +2025,12 @@ def sls_exists(mods, test=None, queue=False, **kwargs): .. code-block:: bash salt '*' state.sls_exists core,edit.vim saltenv=dev - ''' - return isinstance( - show_sls(mods, test=test, queue=queue, **kwargs), - dict - ) + """ + return isinstance(show_sls(mods, test=test, queue=queue, **kwargs), dict) def id_exists(ids, mods, test=None, queue=False, **kwargs): - ''' + """ Tests for the existence of a specific ID or list of IDs within the specified SLS file(s). Similar to :py:func:`state.sls_exists <salt.modules.state.sls_exists>`, returns True or False. The default @@ -1995,15 +2047,17 @@ def id_exists(ids, mods, test=None, queue=False, **kwargs): .. code-block:: bash salt '*' state.id_exists create_myfile,update_template filestate saltenv=dev - ''' + """ ids = salt.utils.args.split_input(ids) ids = set(ids) - sls_ids = set(x['__id__'] for x in show_low_sls(mods, test=test, queue=queue, **kwargs)) + sls_ids = set( + x["__id__"] for x in show_low_sls(mods, test=test, queue=queue, **kwargs) + ) return ids.issubset(sls_ids) def show_top(queue=False, **kwargs): - ''' + """ Return the top data that the minion will use for a highstate CLI Example: @@ -2011,10 +2065,10 @@ def show_top(queue=False, **kwargs): .. code-block:: bash salt '*' state.show_top - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") conflict = _check_queue(queue, kwargs) if conflict is not None: @@ -2022,29 +2076,29 @@ def show_top(queue=False, **kwargs): opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) try: - st_ = salt.state.HighState(opts, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.HighState( + opts, proxy=__proxy__, initial_pillar=_get_initial_pillar(opts) + ) except NameError: st_ = salt.state.HighState(opts, initial_pillar=_get_initial_pillar(opts)) - errors = _get_pillar_errors(kwargs, pillar=st_.opts['pillar']) + errors = _get_pillar_errors(kwargs, pillar=st_.opts["pillar"]) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_PILLAR_FAILURE - raise CommandExecutionError('Pillar failed to render', info=errors) + __context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE + raise CommandExecutionError("Pillar failed to render", info=errors) errors = [] top_ = st_.get_top() errors += st_.verify_tops(top_) if errors: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return errors matches = st_.top_matches(top_) return matches def single(fun, name, test=None, queue=False, **kwargs): - ''' + """ Execute a single state function with the named kwargs, returns False if insufficient data is sent to the command @@ -2059,62 +2113,68 @@ def single(fun, name, test=None, queue=False, **kwargs): salt '*' state.single pkg.installed name=vim - ''' + """ conflict = _check_queue(queue, kwargs) if conflict is not None: return conflict - comps = fun.split('.') + comps = fun.split(".") if len(comps) < 2: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR - return 'Invalid function passed' - kwargs.update({'state': comps[0], - 'fun': comps[1], - '__id__': name, - 'name': name}) - orig_test = __opts__.get('test', None) + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + return "Invalid function passed" + kwargs.update({"state": comps[0], "fun": comps[1], "__id__": name, "name": name}) + orig_test = __opts__.get("test", None) opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - opts['test'] = _get_test_value(test, **kwargs) + opts["test"] = _get_test_value(test, **kwargs) - pillar_override = kwargs.get('pillar') - pillar_enc = kwargs.get('pillar_enc') - if pillar_enc is None \ - and pillar_override is not None \ - and not isinstance(pillar_override, dict): + pillar_override = kwargs.get("pillar") + pillar_enc = kwargs.get("pillar_enc") + if ( + pillar_enc is None + and pillar_override is not None + and not isinstance(pillar_override, dict) + ): raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary, unless pillar_enc ' - 'is specified.' + "Pillar data must be formatted as a dictionary, unless pillar_enc " + "is specified." ) try: - st_ = salt.state.State(opts, - pillar_override, - pillar_enc=pillar_enc, - proxy=__proxy__, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.State( + opts, + pillar_override, + pillar_enc=pillar_enc, + proxy=__proxy__, + initial_pillar=_get_initial_pillar(opts), + ) except NameError: - st_ = salt.state.State(opts, - pillar_override, - pillar_enc=pillar_enc, - initial_pillar=_get_initial_pillar(opts)) + st_ = salt.state.State( + opts, + pillar_override, + pillar_enc=pillar_enc, + initial_pillar=_get_initial_pillar(opts), + ) err = st_.verify_data(kwargs) if err: - __context__['retcode'] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR + __context__["retcode"] = salt.defaults.exitcodes.EX_STATE_COMPILER_ERROR return err st_._mod_init(kwargs) - snapper_pre = _snapper_pre(opts, kwargs.get('__pub_jid', 'called localy')) - ret = {'{0[state]}_|-{0[__id__]}_|-{0[name]}_|-{0[fun]}'.format(kwargs): - st_.call(kwargs)} + snapper_pre = _snapper_pre(opts, kwargs.get("__pub_jid", "called localy")) + ret = { + "{0[state]}_|-{0[__id__]}_|-{0[name]}_|-{0[fun]}".format(kwargs): st_.call( + kwargs + ) + } _set_retcode(ret) # Work around Windows multiprocessing bug, set __opts__['test'] back to # value from before this function was run. - _snapper_post(opts, kwargs.get('__pub_jid', 'called localy'), snapper_pre) - __opts__['test'] = orig_test + _snapper_post(opts, kwargs.get("__pub_jid", "called localy"), snapper_pre) + __opts__["test"] = orig_test return ret def clear_cache(): - ''' + """ Clear out cached state files, forcing even cache runs to refresh the cache on the next state execution. @@ -2126,11 +2186,11 @@ def clear_cache(): .. code-block:: bash salt '*' state.clear_cache - ''' + """ ret = [] - for fn_ in os.listdir(__opts__['cachedir']): - if fn_.endswith('.cache.p'): - path = os.path.join(__opts__['cachedir'], fn_) + for fn_ in os.listdir(__opts__["cachedir"]): + if fn_.endswith(".cache.p"): + path = os.path.join(__opts__["cachedir"], fn_) if not os.path.isfile(path): continue os.remove(path) @@ -2138,12 +2198,8 @@ def clear_cache(): return ret -def pkg(pkg_path, - pkg_sum, - hash_type, - test=None, - **kwargs): - ''' +def pkg(pkg_path, pkg_sum, hash_type, test=None, **kwargs): + """ Execute a packaged state run, the packaged state run will exist in a tarball available locally. This packaged state can be generated using salt-ssh. @@ -2153,7 +2209,7 @@ def pkg(pkg_path, .. code-block:: bash salt '*' state.pkg /tmp/salt_state.tgz 760a9353810e36f6d81416366fc426dc md5 - ''' + """ # TODO - Add ability to download from salt master or other source popts = salt.utils.state.get_sls_opts(__opts__, **kwargs) if not os.path.isfile(pkg_path): @@ -2161,48 +2217,50 @@ def pkg(pkg_path, if not salt.utils.hashutils.get_hash(pkg_path, hash_type) == pkg_sum: return {} root = tempfile.mkdtemp() - s_pkg = tarfile.open(pkg_path, 'r:gz') + s_pkg = tarfile.open(pkg_path, "r:gz") # Verify that the tarball does not extract outside of the intended root members = s_pkg.getmembers() for member in members: - if salt.utils.stringutils.to_unicode(member.path).startswith((os.sep, '..{0}'.format(os.sep))): + if salt.utils.stringutils.to_unicode(member.path).startswith( + (os.sep, "..{0}".format(os.sep)) + ): return {} - elif '..{0}'.format(os.sep) in salt.utils.stringutils.to_unicode(member.path): + elif "..{0}".format(os.sep) in salt.utils.stringutils.to_unicode(member.path): return {} s_pkg.extractall(root) s_pkg.close() - lowstate_json = os.path.join(root, 'lowstate.json') - with salt.utils.files.fopen(lowstate_json, 'r') as fp_: + lowstate_json = os.path.join(root, "lowstate.json") + with salt.utils.files.fopen(lowstate_json, "r") as fp_: lowstate = salt.utils.json.load(fp_) # Check for errors in the lowstate for chunk in lowstate: if not isinstance(chunk, dict): return lowstate - pillar_json = os.path.join(root, 'pillar.json') + pillar_json = os.path.join(root, "pillar.json") if os.path.isfile(pillar_json): - with salt.utils.files.fopen(pillar_json, 'r') as fp_: + with salt.utils.files.fopen(pillar_json, "r") as fp_: pillar_override = salt.utils.json.load(fp_) else: pillar_override = None - roster_grains_json = os.path.join(root, 'roster_grains.json') + roster_grains_json = os.path.join(root, "roster_grains.json") if os.path.isfile(roster_grains_json): - with salt.utils.files.fopen(roster_grains_json, 'r') as fp_: + with salt.utils.files.fopen(roster_grains_json, "r") as fp_: roster_grains = salt.utils.json.load(fp_) if os.path.isfile(roster_grains_json): - popts['grains'] = roster_grains - popts['fileclient'] = 'local' - popts['file_roots'] = {} - popts['test'] = _get_test_value(test, **kwargs) + popts["grains"] = roster_grains + popts["fileclient"] = "local" + popts["file_roots"] = {} + popts["test"] = _get_test_value(test, **kwargs) envs = os.listdir(root) for fn_ in envs: full = os.path.join(root, fn_) if not os.path.isdir(full): continue - popts['file_roots'][fn_] = [full] + popts["file_roots"][fn_] = [full] st_ = salt.state.State(popts, pillar_override=pillar_override) - snapper_pre = _snapper_pre(popts, kwargs.get('__pub_jid', 'called localy')) + snapper_pre = _snapper_pre(popts, kwargs.get("__pub_jid", "called localy")) ret = st_.call_chunks(lowstate) ret = st_.call_listen(lowstate, ret) try: @@ -2210,12 +2268,12 @@ def pkg(pkg_path, except (IOError, OSError): pass _set_retcode(ret) - _snapper_post(popts, kwargs.get('__pub_jid', 'called localy'), snapper_pre) + _snapper_post(popts, kwargs.get("__pub_jid", "called localy"), snapper_pre) return ret def disable(states): - ''' + """ Disable state runs. CLI Example: @@ -2232,41 +2290,38 @@ def disable(states): salt '*' state.disable bind.config - ''' - ret = { - 'res': True, - 'msg': '' - } + """ + ret = {"res": True, "msg": ""} states = salt.utils.args.split_input(states) msg = [] - _disabled = __salt__['grains.get']('state_runs_disabled') + _disabled = __salt__["grains.get"]("state_runs_disabled") if not isinstance(_disabled, list): _disabled = [] _changed = False for _state in states: if _state in _disabled: - msg.append('Info: {0} state already disabled.'.format(_state)) + msg.append("Info: {0} state already disabled.".format(_state)) else: - msg.append('Info: {0} state disabled.'.format(_state)) + msg.append("Info: {0} state disabled.".format(_state)) _disabled.append(_state) _changed = True if _changed: - __salt__['grains.setval']('state_runs_disabled', _disabled) + __salt__["grains.setval"]("state_runs_disabled", _disabled) - ret['msg'] = '\n'.join(msg) + ret["msg"] = "\n".join(msg) # refresh the grains - __salt__['saltutil.refresh_modules']() + __salt__["saltutil.refresh_modules"]() return ret def enable(states): - ''' + """ Enable state function or sls run CLI Example: @@ -2283,43 +2338,40 @@ def enable(states): salt '*' state.disable bind.config - ''' - ret = { - 'res': True, - 'msg': '' - } + """ + ret = {"res": True, "msg": ""} states = salt.utils.args.split_input(states) - log.debug('states %s', states) + log.debug("states %s", states) msg = [] - _disabled = __salt__['grains.get']('state_runs_disabled') + _disabled = __salt__["grains.get"]("state_runs_disabled") if not isinstance(_disabled, list): _disabled = [] _changed = False for _state in states: - log.debug('_state %s', _state) + log.debug("_state %s", _state) if _state not in _disabled: - msg.append('Info: {0} state already enabled.'.format(_state)) + msg.append("Info: {0} state already enabled.".format(_state)) else: - msg.append('Info: {0} state enabled.'.format(_state)) + msg.append("Info: {0} state enabled.".format(_state)) _disabled.remove(_state) _changed = True if _changed: - __salt__['grains.setval']('state_runs_disabled', _disabled) + __salt__["grains.setval"]("state_runs_disabled", _disabled) - ret['msg'] = '\n'.join(msg) + ret["msg"] = "\n".join(msg) # refresh the grains - __salt__['saltutil.refresh_modules']() + __salt__["saltutil.refresh_modules"]() return ret def list_disabled(): - ''' + """ List the states which are currently disabled CLI Example: @@ -2327,52 +2379,48 @@ def list_disabled(): .. code-block:: bash salt '*' state.list_disabled - ''' - return __salt__['grains.get']('state_runs_disabled') + """ + return __salt__["grains.get"]("state_runs_disabled") def _disabled(funs): - ''' + """ Return messages for disabled states that match state functions in funs. - ''' + """ ret = [] - _disabled = __salt__['grains.get']('state_runs_disabled') + _disabled = __salt__["grains.get"]("state_runs_disabled") for state in funs: for _state in _disabled: - if '.*' in _state: - target_state = _state.split('.')[0] - target_state = target_state + '.' if not target_state.endswith('.') else target_state + if ".*" in _state: + target_state = _state.split(".")[0] + target_state = ( + target_state + "." + if not target_state.endswith(".") + else target_state + ) if state.startswith(target_state): err = ( 'The state file "{0}" is currently disabled by "{1}", ' - 'to re-enable, run state.enable {1}.' - ).format( - state, - _state, - ) + "to re-enable, run state.enable {1}." + ).format(state, _state,) ret.append(err) continue else: if _state == state: err = ( 'The state file "{0}" is currently disabled, ' - 'to re-enable, run state.enable {0}.' - ).format( - _state, - ) + "to re-enable, run state.enable {0}." + ).format(_state,) ret.append(err) continue return ret -def event(tagmatch='*', - count=-1, - quiet=False, - sock_dir=None, - pretty=False, - node='minion'): - r''' +def event( + tagmatch="*", count=-1, quiet=False, sock_dir=None, pretty=False, node="minion" +): + r""" Watch Salt's event bus and block until the given tag is matched .. versionadded:: 2016.3.0 @@ -2399,38 +2447,40 @@ def event(tagmatch='*', .. code-block:: bash salt-call --local state.event pretty=True - ''' + """ with salt.utils.event.get_event( - node, - sock_dir or __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=True) as sevent: + node, + sock_dir or __opts__["sock_dir"], + __opts__["transport"], + opts=__opts__, + listen=True, + ) as sevent: while True: ret = sevent.get_event(full=True, auto_reconnect=True) if ret is None: continue - if salt.utils.stringutils.expr_match(ret['tag'], tagmatch): + if salt.utils.stringutils.expr_match(ret["tag"], tagmatch): if not quiet: salt.utils.stringutils.print_cli( - str('{0}\t{1}').format( # future lint: blacklisted-function - salt.utils.stringutils.to_str(ret['tag']), + str("{0}\t{1}").format( # future lint: blacklisted-function + salt.utils.stringutils.to_str(ret["tag"]), salt.utils.json.dumps( - ret['data'], + ret["data"], sort_keys=pretty, - indent=None if not pretty else 4) + indent=None if not pretty else 4, + ), ) ) sys.stdout.flush() if count > 0: count -= 1 - log.debug('Remaining event matches: %s', count) + log.debug("Remaining event matches: %s", count) if count == 0: break else: - log.debug('Skipping event tag: %s', ret['tag']) + log.debug("Skipping event tag: %s", ret["tag"]) continue diff --git a/salt/modules/status.py b/salt/modules/status.py index c8d9f6e0bee..fee16f1925e 100644 --- a/salt/modules/status.py +++ b/salt/modules/status.py @@ -1,24 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" Module for returning various status data about a minion. These data can be useful for compiling into stats later. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import datetime -import os -import re -import logging -import fnmatch + import collections import copy -import time +import datetime +import fnmatch import logging - -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin +import os +import re +import time # Import salt libs import salt.config @@ -29,40 +25,41 @@ import salt.utils.network import salt.utils.path import salt.utils.platform import salt.utils.stringutils -from salt.ext.six.moves import zip from salt.exceptions import CommandExecutionError +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import range, zip + log = logging.getLogger(__file__) -__virtualname__ = 'status' +__virtualname__ = "status" __opts__ = {} # Don't shadow built-in's. -__func_alias__ = { - 'time_': 'time' -} +__func_alias__ = {"time_": "time"} log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Not all functions supported by Windows - ''' + """ if salt.utils.platform.is_windows(): - return False, 'Windows platform is not supported by this module' + return False, "Windows platform is not supported by this module" return __virtualname__ def _number(text): - ''' + """ Convert a string to a number. Returns an integer if the string represents an integer, a floating point number if the string is a real number, or the string unchanged otherwise. - ''' + """ if text.isdigit(): return int(text) try: @@ -72,7 +69,7 @@ def _number(text): def _get_boot_time_aix(): - ''' + """ Return the number of seconds since boot time on AIX t=$(LC_ALL=POSIX ps -o etime= -p 1) @@ -82,41 +79,50 @@ def _get_boot_time_aix(): s=$((d*86400 + h*3600 + ${t%%:*}*60 + ${t#*:})) t is 7-20:46:46 - ''' + """ boot_secs = 0 - res = __salt__['cmd.run_all']('ps -o etime= -p 1') - if res['retcode'] > 0: - raise CommandExecutionError('Unable to find boot_time for pid 1.') - bt_time = res['stdout'] - days = bt_time.split('-') - hms = days[1].split(':') - boot_secs = _number(days[0]) * 86400 + _number(hms[0]) * 3600 + _number(hms[1]) * 60 + _number(hms[2]) + res = __salt__["cmd.run_all"]("ps -o etime= -p 1") + if res["retcode"] > 0: + raise CommandExecutionError("Unable to find boot_time for pid 1.") + bt_time = res["stdout"] + days = bt_time.split("-") + hms = days[1].split(":") + boot_secs = ( + _number(days[0]) * 86400 + + _number(hms[0]) * 3600 + + _number(hms[1]) * 60 + + _number(hms[2]) + ) return boot_secs def _aix_loadavg(): - ''' + """ Return the load average on AIX - ''' + """ # 03:42PM up 9 days, 20:41, 2 users, load average: 0.28, 0.47, 0.69 - uptime = __salt__['cmd.run']('uptime') - ldavg = uptime.split('load average') + uptime = __salt__["cmd.run"]("uptime") + ldavg = uptime.split("load average") load_avg = ldavg[1].split() - return {'1-min': load_avg[1].strip(','), - '5-min': load_avg[2].strip(','), - '15-min': load_avg[3]} + return { + "1-min": load_avg[1].strip(","), + "5-min": load_avg[2].strip(","), + "15-min": load_avg[3], + } def _aix_nproc(): - ''' + """ Return the maximun number of PROCESSES allowed per user on AIX - ''' - nprocs = __salt__['cmd.run']('lsattr -E -l sys0 | grep maxuproc', python_shell=True).split() + """ + nprocs = __salt__["cmd.run"]( + "lsattr -E -l sys0 | grep maxuproc", python_shell=True + ).split() return _number(nprocs[1]) def procs(): - ''' + """ Return the process data .. versionchanged:: 2016.11.4 @@ -127,35 +133,34 @@ def procs(): .. code-block:: bash salt '*' status.procs - ''' + """ # Get the user, pid and cmd ret = {} uind = 0 pind = 0 cind = 0 - plines = __salt__['cmd.run'](__grains__['ps'], python_shell=True).splitlines() + plines = __salt__["cmd.run"](__grains__["ps"], python_shell=True).splitlines() guide = plines.pop(0).split() - if 'USER' in guide: - uind = guide.index('USER') - elif 'UID' in guide: - uind = guide.index('UID') - if 'PID' in guide: - pind = guide.index('PID') - if 'COMMAND' in guide: - cind = guide.index('COMMAND') - elif 'CMD' in guide: - cind = guide.index('CMD') + if "USER" in guide: + uind = guide.index("USER") + elif "UID" in guide: + uind = guide.index("UID") + if "PID" in guide: + pind = guide.index("PID") + if "COMMAND" in guide: + cind = guide.index("COMMAND") + elif "CMD" in guide: + cind = guide.index("CMD") for line in plines: if not line: continue comps = line.split() - ret[comps[pind]] = {'user': comps[uind], - 'cmd': ' '.join(comps[cind:])} + ret[comps[pind]] = {"user": comps[uind], "cmd": " ".join(comps[cind:])} return ret def custom(): - ''' + """ Return a custom composite of status data and info for this minion, based on the minion config file. An example config like might be:: @@ -176,11 +181,11 @@ def custom(): .. code-block:: bash salt '*' status.custom - ''' + """ ret = {} - conf = __salt__['config.dot_vals']('status') + conf = __salt__["config.dot_vals"]("status") for key, val in six.iteritems(conf): - func = '{0}()'.format(key.split('.')[1]) + func = "{0}()".format(key.split(".")[1]) vals = eval(func) # pylint: disable=W0123 for item in val: @@ -190,7 +195,7 @@ def custom(): def uptime(): - ''' + """ Return the uptime for this system. .. versionchanged:: 2015.8.9 @@ -209,40 +214,44 @@ def uptime(): .. code-block:: bash salt '*' status.uptime - ''' + """ curr_seconds = time.time() # Get uptime in seconds if salt.utils.platform.is_linux(): ut_path = "/proc/uptime" if not os.path.exists(ut_path): - raise CommandExecutionError("File {ut_path} was not found.".format(ut_path=ut_path)) + raise CommandExecutionError( + "File {ut_path} was not found.".format(ut_path=ut_path) + ) with salt.utils.files.fopen(ut_path) as rfh: seconds = int(float(rfh.read().split()[0])) elif salt.utils.platform.is_sunos(): # note: some flavors/versions report the host uptime inside a zone # https://support.oracle.com/epmos/faces/BugDisplay?id=15611584 - res = __salt__['cmd.run_all']('kstat -p unix:0:system_misc:boot_time') - if res['retcode'] > 0: - raise CommandExecutionError('The boot_time kstat was not found.') - seconds = int(curr_seconds - int(res['stdout'].split()[-1])) + res = __salt__["cmd.run_all"]("kstat -p unix:0:system_misc:boot_time") + if res["retcode"] > 0: + raise CommandExecutionError("The boot_time kstat was not found.") + seconds = int(curr_seconds - int(res["stdout"].split()[-1])) elif salt.utils.platform.is_openbsd() or salt.utils.platform.is_netbsd(): - bt_data = __salt__['sysctl.get']('kern.boottime') + bt_data = __salt__["sysctl.get"]("kern.boottime") if not bt_data: - raise CommandExecutionError('Cannot find kern.boottime system parameter') + raise CommandExecutionError("Cannot find kern.boottime system parameter") seconds = int(curr_seconds - int(bt_data)) elif salt.utils.platform.is_freebsd() or salt.utils.platform.is_darwin(): # format: { sec = 1477761334, usec = 664698 } Sat Oct 29 17:15:34 2016 - bt_data = __salt__['sysctl.get']('kern.boottime') + bt_data = __salt__["sysctl.get"]("kern.boottime") if not bt_data: - raise CommandExecutionError('Cannot find kern.boottime system parameter') - data = bt_data.split("{")[-1].split("}")[0].strip().replace(' ', '') - uptime = dict([(k, int(v,)) for k, v in [p.strip().split('=') for p in data.split(',')]]) - seconds = int(curr_seconds - uptime['sec']) + raise CommandExecutionError("Cannot find kern.boottime system parameter") + data = bt_data.split("{")[-1].split("}")[0].strip().replace(" ", "") + uptime = dict( + [(k, int(v,)) for k, v in [p.strip().split("=") for p in data.split(",")]] + ) + seconds = int(curr_seconds - uptime["sec"]) elif salt.utils.platform.is_aix(): seconds = _get_boot_time_aix() else: - return __salt__['cmd.run']('uptime') + return __salt__["cmd.run"]("uptime") # Setup datetime and timedelta objects boot_time = datetime.datetime.utcfromtimestamp(curr_seconds - seconds) @@ -251,22 +260,24 @@ def uptime(): # Construct return information ut_ret = { - 'seconds': seconds, - 'since_iso': boot_time.isoformat(), - 'since_t': int(curr_seconds - seconds), - 'days': up_time.days, - 'time': '{0}:{1}'.format(up_time.seconds // 3600, up_time.seconds % 3600 // 60), + "seconds": seconds, + "since_iso": boot_time.isoformat(), + "since_t": int(curr_seconds - seconds), + "days": up_time.days, + "time": "{0}:{1}".format(up_time.seconds // 3600, up_time.seconds % 3600 // 60), } - if salt.utils.path.which('who'): - who_cmd = 'who' if salt.utils.platform.is_openbsd() else 'who -s' # OpenBSD does not support -s - ut_ret['users'] = len(__salt__['cmd.run'](who_cmd).split(os.linesep)) + if salt.utils.path.which("who"): + who_cmd = ( + "who" if salt.utils.platform.is_openbsd() else "who -s" + ) # OpenBSD does not support -s + ut_ret["users"] = len(__salt__["cmd.run"](who_cmd).split(os.linesep)) return ut_ret def loadavg(): - ''' + """ Return the load averages for this minion .. versionchanged:: 2016.11.4 @@ -279,22 +290,22 @@ def loadavg(): salt '*' status.loadavg :raises CommandExecutionError: If the system cannot report loadaverages to Python - ''' - if __grains__['kernel'] == 'AIX': + """ + if __grains__["kernel"] == "AIX": return _aix_loadavg() try: load_avg = os.getloadavg() except AttributeError: # Some UNIX-based operating systems do not have os.getloadavg() - raise salt.exceptions.CommandExecutionError('status.loadavag is not available on your platform') - return {'1-min': load_avg[0], - '5-min': load_avg[1], - '15-min': load_avg[2]} + raise salt.exceptions.CommandExecutionError( + "status.loadavag is not available on your platform" + ) + return {"1-min": load_avg[0], "5-min": load_avg[1], "15-min": load_avg[2]} def cpustats(): - ''' + """ Return the CPU stats for this minion .. versionchanged:: 2016.11.4 @@ -308,14 +319,15 @@ def cpustats(): .. code-block:: bash salt '*' status.cpustats - ''' + """ + def linux_cpustats(): - ''' + """ linux specific implementation of cpustats - ''' + """ ret = {} try: - with salt.utils.files.fopen('/proc/stat', 'r') as fp_: + with salt.utils.files.fopen("/proc/stat", "r") as fp_: stats = salt.utils.stringutils.to_unicode(fp_.read()) except IOError: pass @@ -324,98 +336,106 @@ def cpustats(): if not line: continue comps = line.split() - if comps[0] == 'cpu': - ret[comps[0]] = {'idle': _number(comps[4]), - 'iowait': _number(comps[5]), - 'irq': _number(comps[6]), - 'nice': _number(comps[2]), - 'softirq': _number(comps[7]), - 'steal': _number(comps[8]), - 'system': _number(comps[3]), - 'user': _number(comps[1])} - elif comps[0] == 'intr': - ret[comps[0]] = {'total': _number(comps[1]), - 'irqs': [_number(x) for x in comps[2:]]} - elif comps[0] == 'softirq': - ret[comps[0]] = {'total': _number(comps[1]), - 'softirqs': [_number(x) for x in comps[2:]]} + if comps[0] == "cpu": + ret[comps[0]] = { + "idle": _number(comps[4]), + "iowait": _number(comps[5]), + "irq": _number(comps[6]), + "nice": _number(comps[2]), + "softirq": _number(comps[7]), + "steal": _number(comps[8]), + "system": _number(comps[3]), + "user": _number(comps[1]), + } + elif comps[0] == "intr": + ret[comps[0]] = { + "total": _number(comps[1]), + "irqs": [_number(x) for x in comps[2:]], + } + elif comps[0] == "softirq": + ret[comps[0]] = { + "total": _number(comps[1]), + "softirqs": [_number(x) for x in comps[2:]], + } else: ret[comps[0]] = _number(comps[1]) return ret def freebsd_cpustats(): - ''' + """ freebsd specific implementation of cpustats - ''' - vmstat = __salt__['cmd.run']('vmstat -P').splitlines() + """ + vmstat = __salt__["cmd.run"]("vmstat -P").splitlines() vm0 = vmstat[0].split() - cpu0loc = vm0.index('cpu0') + cpu0loc = vm0.index("cpu0") vm1 = vmstat[1].split() - usloc = vm1.index('us') + usloc = vm1.index("us") vm2 = vmstat[2].split() cpuctr = 0 ret = {} for cpu in vm0[cpu0loc:]: - ret[cpu] = {'us': _number(vm2[usloc + 3 * cpuctr]), - 'sy': _number(vm2[usloc + 1 + 3 * cpuctr]), - 'id': _number(vm2[usloc + 2 + 3 * cpuctr]), } + ret[cpu] = { + "us": _number(vm2[usloc + 3 * cpuctr]), + "sy": _number(vm2[usloc + 1 + 3 * cpuctr]), + "id": _number(vm2[usloc + 2 + 3 * cpuctr]), + } cpuctr += 1 return ret def sunos_cpustats(): - ''' + """ sunos specific implementation of cpustats - ''' - mpstat = __salt__['cmd.run']('mpstat 1 2').splitlines() + """ + mpstat = __salt__["cmd.run"]("mpstat 1 2").splitlines() fields = mpstat[0].split() ret = {} for cpu in mpstat: - if cpu.startswith('CPU'): + if cpu.startswith("CPU"): continue cpu = cpu.split() ret[_number(cpu[0])] = {} - for i in range(1, len(fields)-1): + for i in range(1, len(fields) - 1): ret[_number(cpu[0])][fields[i]] = _number(cpu[i]) return ret def aix_cpustats(): - ''' + """ AIX specific implementation of cpustats - ''' + """ ret = {} - ret['mpstat'] = [] + ret["mpstat"] = [] procn = None fields = [] - for line in __salt__['cmd.run']('mpstat -a').splitlines(): + for line in __salt__["cmd.run"]("mpstat -a").splitlines(): if not line: continue - procn = len(ret['mpstat']) - if line.startswith('System'): - comps = line.split(':') - ret['mpstat'].append({}) - ret['mpstat'][procn]['system'] = {} + procn = len(ret["mpstat"]) + if line.startswith("System"): + comps = line.split(":") + ret["mpstat"].append({}) + ret["mpstat"][procn]["system"] = {} cpu_comps = comps[1].split() for i in range(0, len(cpu_comps)): - cpu_vals = cpu_comps[i].split('=') - ret['mpstat'][procn]['system'][cpu_vals[0]] = cpu_vals[1] + cpu_vals = cpu_comps[i].split("=") + ret["mpstat"][procn]["system"][cpu_vals[0]] = cpu_vals[1] - if line.startswith('cpu'): + if line.startswith("cpu"): fields = line.split() continue if fields: cpustat = line.split() ret[_number(cpustat[0])] = {} - for i in range(1, len(fields)-1): + for i in range(1, len(fields) - 1): ret[_number(cpustat[0])][fields[i]] = _number(cpustat[i]) return ret def openbsd_cpustats(): - ''' + """ openbsd specific implementation of cpustats - ''' - systat = __salt__['cmd.run']('systat -s 2 -B cpu').splitlines() + """ + systat = __salt__["cmd.run"]("systat -s 2 -B cpu").splitlines() fields = systat[3].split() ret = {} for cpu in systat[4:]: @@ -424,25 +444,25 @@ def cpustats(): ret[cpu_idx] = {} for idx, field in enumerate(fields[1:]): - ret[cpu_idx][field] = cpu_line[idx+1] + ret[cpu_idx][field] = cpu_line[idx + 1] return ret # dict that return a function that does the right thing per platform get_version = { - 'Linux': linux_cpustats, - 'FreeBSD': freebsd_cpustats, - 'OpenBSD': openbsd_cpustats, - 'SunOS': sunos_cpustats, - 'AIX': aix_cpustats, + "Linux": linux_cpustats, + "FreeBSD": freebsd_cpustats, + "OpenBSD": openbsd_cpustats, + "SunOS": sunos_cpustats, + "AIX": aix_cpustats, } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def meminfo(): - ''' + """ Return the memory info for this minion .. versionchanged:: 2016.11.4 @@ -456,14 +476,15 @@ def meminfo(): .. code-block:: bash salt '*' status.meminfo - ''' + """ + def linux_meminfo(): - ''' + """ linux specific implementation of meminfo - ''' + """ ret = {} try: - with salt.utils.files.fopen('/proc/meminfo', 'r') as fp_: + with salt.utils.files.fopen("/proc/meminfo", "r") as fp_: stats = salt.utils.stringutils.to_unicode(fp_.read()) except IOError: pass @@ -472,21 +493,21 @@ def meminfo(): if not line: continue comps = line.split() - comps[0] = comps[0].replace(':', '') + comps[0] = comps[0].replace(":", "") ret[comps[0]] = { - 'value': comps[1], + "value": comps[1], } if len(comps) > 2: - ret[comps[0]]['unit'] = comps[2] + ret[comps[0]]["unit"] = comps[2] return ret def freebsd_meminfo(): - ''' + """ freebsd specific implementation of meminfo - ''' - sysctlvm = __salt__['cmd.run']('sysctl vm').splitlines() - sysctlvm = [x for x in sysctlvm if x.startswith('vm')] - sysctlvm = [x.split(':') for x in sysctlvm] + """ + sysctlvm = __salt__["cmd.run"]("sysctl vm").splitlines() + sysctlvm = [x for x in sysctlvm if x.startswith("vm")] + sysctlvm = [x.split(":") for x in sysctlvm] sysctlvm = [[y.strip() for y in x] for x in sysctlvm] sysctlvm = [x for x in sysctlvm if x[1]] # If x[1] not empty @@ -494,118 +515,125 @@ def meminfo(): for line in sysctlvm: ret[line[0]] = line[1] # Special handling for vm.total as it's especially important - sysctlvmtot = __salt__['cmd.run']('sysctl -n vm.vmtotal').splitlines() + sysctlvmtot = __salt__["cmd.run"]("sysctl -n vm.vmtotal").splitlines() sysctlvmtot = [x for x in sysctlvmtot if x] - ret['vm.vmtotal'] = sysctlvmtot + ret["vm.vmtotal"] = sysctlvmtot return ret def aix_meminfo(): - ''' + """ AIX specific implementation of meminfo - ''' + """ ret = {} - ret['svmon'] = [] - ret['vmstat'] = [] + ret["svmon"] = [] + ret["vmstat"] = [] procn = None fields = [] pagesize_flag = False - for line in __salt__['cmd.run']('svmon -G').splitlines(): + for line in __salt__["cmd.run"]("svmon -G").splitlines(): # Note: svmon is per-system # size inuse free pin virtual mmode - #memory 1048576 1039740 8836 285078 474993 Ded - #pg space 917504 2574 + # memory 1048576 1039740 8836 285078 474993 Ded + # pg space 917504 2574 # # work pers clnt other - #pin 248379 0 2107 34592 - #in use 474993 0 564747 + # pin 248379 0 2107 34592 + # in use 474993 0 564747 # - #PageSize PoolSize inuse pgsp pin virtual - #s 4 KB - 666956 2574 60726 102209 - #m 64 KB - 23299 0 14022 23299 + # PageSize PoolSize inuse pgsp pin virtual + # s 4 KB - 666956 2574 60726 102209 + # m 64 KB - 23299 0 14022 23299 if not line: continue - if re.match(r'\s', line): + if re.match(r"\s", line): # assume fields line fields = line.split() continue - if line.startswith('memory') or line.startswith('pin'): - procn = len(ret['svmon']) - ret['svmon'].append({}) + if line.startswith("memory") or line.startswith("pin"): + procn = len(ret["svmon"]) + ret["svmon"].append({}) comps = line.split() - ret['svmon'][procn][comps[0]] = {} + ret["svmon"][procn][comps[0]] = {} for i in range(0, len(fields)): if len(comps) > i + 1: - ret['svmon'][procn][comps[0]][fields[i]] = comps[i+1] + ret["svmon"][procn][comps[0]][fields[i]] = comps[i + 1] continue - if line.startswith('pg space') or line.startswith('in use'): - procn = len(ret['svmon']) - ret['svmon'].append({}) + if line.startswith("pg space") or line.startswith("in use"): + procn = len(ret["svmon"]) + ret["svmon"].append({}) comps = line.split() - pg_space = '{0} {1}'.format(comps[0], comps[1]) - ret['svmon'][procn][pg_space] = {} + pg_space = "{0} {1}".format(comps[0], comps[1]) + ret["svmon"][procn][pg_space] = {} for i in range(0, len(fields)): if len(comps) > i + 2: - ret['svmon'][procn][pg_space][fields[i]] = comps[i+2] + ret["svmon"][procn][pg_space][fields[i]] = comps[i + 2] continue - if line.startswith('PageSize'): + if line.startswith("PageSize"): fields = line.split() pagesize_flag = False continue if pagesize_flag: - procn = len(ret['svmon']) - ret['svmon'].append({}) + procn = len(ret["svmon"]) + ret["svmon"].append({}) comps = line.split() - ret['svmon'][procn][comps[0]] = {} + ret["svmon"][procn][comps[0]] = {} for i in range(0, len(fields)): if len(comps) > i: - ret['svmon'][procn][comps[0]][fields[i]] = comps[i] + ret["svmon"][procn][comps[0]][fields[i]] = comps[i] continue - for line in __salt__['cmd.run']('vmstat -v').splitlines(): + for line in __salt__["cmd.run"]("vmstat -v").splitlines(): # Note: vmstat is per-system if not line: continue - procn = len(ret['vmstat']) - ret['vmstat'].append({}) - comps = line.lstrip().split(' ', 1) - ret['vmstat'][procn][comps[1]] = comps[0] + procn = len(ret["vmstat"]) + ret["vmstat"].append({}) + comps = line.lstrip().split(" ", 1) + ret["vmstat"][procn][comps[1]] = comps[0] return ret def openbsd_meminfo(): - ''' + """ openbsd specific implementation of meminfo - ''' - vmstat = __salt__['cmd.run']('vmstat').splitlines() + """ + vmstat = __salt__["cmd.run"]("vmstat").splitlines() # We're only interested in memory and page values which are printed # as subsequent fields. - fields = ['active virtual pages', 'free list size', 'page faults', - 'pages reclaimed', 'pages paged in', 'pages paged out', - 'pages freed', 'pages scanned'] + fields = [ + "active virtual pages", + "free list size", + "page faults", + "pages reclaimed", + "pages paged in", + "pages paged out", + "pages freed", + "pages scanned", + ] data = vmstat[2].split()[2:10] ret = dict(zip(fields, data)) return ret # dict that return a function that does the right thing per platform get_version = { - 'Linux': linux_meminfo, - 'FreeBSD': freebsd_meminfo, - 'OpenBSD': openbsd_meminfo, - 'AIX': aix_meminfo, + "Linux": linux_meminfo, + "FreeBSD": freebsd_meminfo, + "OpenBSD": openbsd_meminfo, + "AIX": aix_meminfo, } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def cpuinfo(): - ''' + """ .. versionchanged:: 2016.3.2 Return the CPU info for this minion @@ -620,14 +648,15 @@ def cpuinfo(): .. code-block:: bash salt '*' status.cpuinfo - ''' + """ + def linux_cpuinfo(): - ''' + """ linux specific cpuinfo implementation - ''' + """ ret = {} try: - with salt.utils.files.fopen('/proc/cpuinfo', 'r') as fp_: + with salt.utils.files.fopen("/proc/cpuinfo", "r") as fp_: stats = salt.utils.stringutils.to_unicode(fp_.read()) except IOError: pass @@ -635,26 +664,26 @@ def cpuinfo(): for line in stats.splitlines(): if not line: continue - comps = line.split(':') + comps = line.split(":") comps[0] = comps[0].strip() - if comps[0] == 'flags': + if comps[0] == "flags": ret[comps[0]] = comps[1].split() else: ret[comps[0]] = comps[1].strip() return ret def bsd_cpuinfo(): - ''' + """ bsd specific cpuinfo implementation - ''' - bsd_cmd = 'sysctl hw.model hw.ncpu' + """ + bsd_cmd = "sysctl hw.model hw.ncpu" ret = {} - if __grains__['kernel'].lower() in ['netbsd', 'openbsd']: - sep = '=' + if __grains__["kernel"].lower() in ["netbsd", "openbsd"]: + sep = "=" else: - sep = ':' + sep = ":" - for line in __salt__['cmd.run'](bsd_cmd).splitlines(): + for line in __salt__["cmd.run"](bsd_cmd).splitlines(): if not line: continue comps = line.split(sep) @@ -663,26 +692,26 @@ def cpuinfo(): return ret def sunos_cpuinfo(): - ''' + """ sunos specific cpuinfo implementation - ''' + """ ret = {} - ret['isainfo'] = {} - for line in __salt__['cmd.run']('isainfo -x').splitlines(): + ret["isainfo"] = {} + for line in __salt__["cmd.run"]("isainfo -x").splitlines(): # Note: isainfo is per-system and not per-cpu # Output Example: - #amd64: rdrand f16c vmx avx xsave pclmulqdq aes sse4.2 sse4.1 ssse3 popcnt tscp cx16 sse3 sse2 sse fxsr mmx cmov amd_sysc cx8 tsc fpu - #i386: rdrand f16c vmx avx xsave pclmulqdq aes sse4.2 sse4.1 ssse3 popcnt tscp ahf cx16 sse3 sse2 sse fxsr mmx cmov sep cx8 tsc fpu + # amd64: rdrand f16c vmx avx xsave pclmulqdq aes sse4.2 sse4.1 ssse3 popcnt tscp cx16 sse3 sse2 sse fxsr mmx cmov amd_sysc cx8 tsc fpu + # i386: rdrand f16c vmx avx xsave pclmulqdq aes sse4.2 sse4.1 ssse3 popcnt tscp ahf cx16 sse3 sse2 sse fxsr mmx cmov sep cx8 tsc fpu if not line: continue - comps = line.split(':') + comps = line.split(":") comps[0] = comps[0].strip() - ret['isainfo'][comps[0]] = sorted(comps[1].strip().split()) - ret['psrinfo'] = [] + ret["isainfo"][comps[0]] = sorted(comps[1].strip().split()) + ret["psrinfo"] = [] procn = None - for line in __salt__['cmd.run']('psrinfo -v -p').splitlines(): + for line in __salt__["cmd.run"]("psrinfo -v -p").splitlines(): # Output Example: - #The physical processor has 6 cores and 12 virtual processors (0-5 12-17) + # The physical processor has 6 cores and 12 virtual processors (0-5 12-17) # The core has 2 virtual processors (0 12) # The core has 2 virtual processors (1 13) # The core has 2 virtual processors (2 14) @@ -691,7 +720,7 @@ def cpuinfo(): # The core has 2 virtual processors (5 17) # x86 (GenuineIntel 306E4 family 6 model 62 step 4 clock 2100 MHz) # Intel(r) Xeon(r) CPU E5-2620 v2 @ 2.10GHz - #The physical processor has 6 cores and 12 virtual processors (6-11 18-23) + # The physical processor has 6 cores and 12 virtual processors (6-11 18-23) # The core has 2 virtual processors (6 18) # The core has 2 virtual processors (7 19) # The core has 2 virtual processors (8 20) @@ -702,118 +731,126 @@ def cpuinfo(): # Intel(r) Xeon(r) CPU E5-2620 v2 @ 2.10GHz # # Output Example 2: - #The physical processor has 4 virtual processors (0-3) + # The physical processor has 4 virtual processors (0-3) # x86 (GenuineIntel 406D8 family 6 model 77 step 8 clock 2400 MHz) # Intel(r) Atom(tm) CPU C2558 @ 2.40GHz if not line: continue - if line.startswith('The physical processor'): - procn = len(ret['psrinfo']) + if line.startswith("The physical processor"): + procn = len(ret["psrinfo"]) line = line.split() - ret['psrinfo'].append({}) - if 'cores' in line: - ret['psrinfo'][procn]['topology'] = {} - ret['psrinfo'][procn]['topology']['cores'] = _number(line[4]) - ret['psrinfo'][procn]['topology']['threads'] = _number(line[7]) - elif 'virtual' in line: - ret['psrinfo'][procn]['topology'] = {} - ret['psrinfo'][procn]['topology']['threads'] = _number(line[4]) - elif line.startswith(' ' * 6): # 3x2 space indent - ret['psrinfo'][procn]['name'] = line.strip() - elif line.startswith(' ' * 4): # 2x2 space indent + ret["psrinfo"].append({}) + if "cores" in line: + ret["psrinfo"][procn]["topology"] = {} + ret["psrinfo"][procn]["topology"]["cores"] = _number(line[4]) + ret["psrinfo"][procn]["topology"]["threads"] = _number(line[7]) + elif "virtual" in line: + ret["psrinfo"][procn]["topology"] = {} + ret["psrinfo"][procn]["topology"]["threads"] = _number(line[4]) + elif line.startswith(" " * 6): # 3x2 space indent + ret["psrinfo"][procn]["name"] = line.strip() + elif line.startswith(" " * 4): # 2x2 space indent line = line.strip().split() - ret['psrinfo'][procn]['vendor'] = line[1][1:] - ret['psrinfo'][procn]['family'] = _number(line[4]) - ret['psrinfo'][procn]['model'] = _number(line[6]) - ret['psrinfo'][procn]['step'] = _number(line[8]) - ret['psrinfo'][procn]['clock'] = "{0} {1}".format(line[10], line[11][:-1]) + ret["psrinfo"][procn]["vendor"] = line[1][1:] + ret["psrinfo"][procn]["family"] = _number(line[4]) + ret["psrinfo"][procn]["model"] = _number(line[6]) + ret["psrinfo"][procn]["step"] = _number(line[8]) + ret["psrinfo"][procn]["clock"] = "{0} {1}".format( + line[10], line[11][:-1] + ) return ret def aix_cpuinfo(): - ''' + """ AIX specific cpuinfo implementation - ''' + """ ret = {} - ret['prtconf'] = [] - ret['lparstat'] = [] + ret["prtconf"] = [] + ret["lparstat"] = [] procn = None - for line in __salt__['cmd.run']('prtconf | grep -i "Processor"', python_shell=True).splitlines(): + for line in __salt__["cmd.run"]( + 'prtconf | grep -i "Processor"', python_shell=True + ).splitlines(): # Note: prtconf is per-system and not per-cpu # Output Example: - #prtconf | grep -i "Processor" - #Processor Type: PowerPC_POWER7 - #Processor Implementation Mode: POWER 7 - #Processor Version: PV_7_Compat - #Number Of Processors: 2 - #Processor Clock Speed: 3000 MHz + # prtconf | grep -i "Processor" + # Processor Type: PowerPC_POWER7 + # Processor Implementation Mode: POWER 7 + # Processor Version: PV_7_Compat + # Number Of Processors: 2 + # Processor Clock Speed: 3000 MHz # Model Implementation: Multiple Processor, PCI bus # + proc0 Processor # + proc4 Processor if not line: continue - procn = len(ret['prtconf']) - if line.startswith('Processor') or line.startswith('Number'): - ret['prtconf'].append({}) - comps = line.split(':') + procn = len(ret["prtconf"]) + if line.startswith("Processor") or line.startswith("Number"): + ret["prtconf"].append({}) + comps = line.split(":") comps[0] = comps[0].rstrip() - ret['prtconf'][procn][comps[0]] = comps[1] + ret["prtconf"][procn][comps[0]] = comps[1] else: continue - for line in __salt__['cmd.run']('prtconf | grep "CPU"', python_shell=True).splitlines(): + for line in __salt__["cmd.run"]( + 'prtconf | grep "CPU"', python_shell=True + ).splitlines(): # Note: prtconf is per-system and not per-cpu # Output Example: - #CPU Type: 64-bit + # CPU Type: 64-bit if not line: continue - procn = len(ret['prtconf']) - if line.startswith('CPU'): - ret['prtconf'].append({}) - comps = line.split(':') + procn = len(ret["prtconf"]) + if line.startswith("CPU"): + ret["prtconf"].append({}) + comps = line.split(":") comps[0] = comps[0].rstrip() - ret['prtconf'][procn][comps[0]] = comps[1] + ret["prtconf"][procn][comps[0]] = comps[1] else: continue - for line in __salt__['cmd.run']('lparstat -i | grep CPU', python_shell=True).splitlines(): + for line in __salt__["cmd.run"]( + "lparstat -i | grep CPU", python_shell=True + ).splitlines(): # Note: lparstat is per-system and not per-cpu # Output Example: - #Online Virtual CPUs : 2 - #Maximum Virtual CPUs : 2 - #Minimum Virtual CPUs : 1 - #Maximum Physical CPUs in system : 32 - #Active Physical CPUs in system : 32 - #Active CPUs in Pool : 32 - #Shared Physical CPUs in system : 32 - #Physical CPU Percentage : 25.00% - #Desired Virtual CPUs : 2 + # Online Virtual CPUs : 2 + # Maximum Virtual CPUs : 2 + # Minimum Virtual CPUs : 1 + # Maximum Physical CPUs in system : 32 + # Active Physical CPUs in system : 32 + # Active CPUs in Pool : 32 + # Shared Physical CPUs in system : 32 + # Physical CPU Percentage : 25.00% + # Desired Virtual CPUs : 2 if not line: continue - procn = len(ret['lparstat']) - ret['lparstat'].append({}) - comps = line.split(':') + procn = len(ret["lparstat"]) + ret["lparstat"].append({}) + comps = line.split(":") comps[0] = comps[0].rstrip() - ret['lparstat'][procn][comps[0]] = comps[1] + ret["lparstat"][procn][comps[0]] = comps[1] return ret # dict that returns a function that does the right thing per platform get_version = { - 'Linux': linux_cpuinfo, - 'FreeBSD': bsd_cpuinfo, - 'NetBSD': bsd_cpuinfo, - 'OpenBSD': bsd_cpuinfo, - 'SunOS': sunos_cpuinfo, - 'AIX': aix_cpuinfo, + "Linux": linux_cpuinfo, + "FreeBSD": bsd_cpuinfo, + "NetBSD": bsd_cpuinfo, + "OpenBSD": bsd_cpuinfo, + "SunOS": sunos_cpuinfo, + "AIX": aix_cpuinfo, } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def diskstats(): - ''' + """ .. versionchanged:: 2016.3.2 Return the disk stats for this minion @@ -825,14 +862,15 @@ def diskstats(): .. code-block:: bash salt '*' status.diskstats - ''' + """ + def linux_diskstats(): - ''' + """ linux specific implementation of diskstats - ''' + """ ret = {} try: - with salt.utils.files.fopen('/proc/diskstats', 'r') as fp_: + with salt.utils.files.fopen("/proc/diskstats", "r") as fp_: stats = salt.utils.stringutils.to_unicode(fp_.read()) except IOError: pass @@ -842,30 +880,30 @@ def diskstats(): continue comps = line.split() ret[comps[2]] = { - 'major': _number(comps[0]), - 'minor': _number(comps[1]), - 'device': _number(comps[2]), - 'reads_issued': _number(comps[3]), - 'reads_merged': _number(comps[4]), - 'sectors_read': _number(comps[5]), - 'ms_spent_reading': _number(comps[6]), - 'writes_completed': _number(comps[7]), - 'writes_merged': _number(comps[8]), - 'sectors_written': _number(comps[9]), - 'ms_spent_writing': _number(comps[10]), - 'io_in_progress': _number(comps[11]), - 'ms_spent_in_io': _number(comps[12]), - 'weighted_ms_spent_in_io': _number(comps[13]) + "major": _number(comps[0]), + "minor": _number(comps[1]), + "device": _number(comps[2]), + "reads_issued": _number(comps[3]), + "reads_merged": _number(comps[4]), + "sectors_read": _number(comps[5]), + "ms_spent_reading": _number(comps[6]), + "writes_completed": _number(comps[7]), + "writes_merged": _number(comps[8]), + "sectors_written": _number(comps[9]), + "ms_spent_writing": _number(comps[10]), + "io_in_progress": _number(comps[11]), + "ms_spent_in_io": _number(comps[12]), + "weighted_ms_spent_in_io": _number(comps[13]), } return ret def generic_diskstats(): - ''' + """ generic implementation of diskstats note: freebsd and sunos - ''' + """ ret = {} - iostat = __salt__['cmd.run']('iostat -xzd').splitlines() + iostat = __salt__["cmd.run"]("iostat -xzd").splitlines() header = iostat[1] for line in iostat[2:]: comps = line.split() @@ -875,20 +913,20 @@ def diskstats(): return ret def aix_diskstats(): - ''' + """ AIX specific implementation of diskstats - ''' + """ ret = {} procn = None fields = [] - disk_name = '' - disk_mode = '' - for line in __salt__['cmd.run']('iostat -dDV').splitlines(): + disk_name = "" + disk_mode = "" + for line in __salt__["cmd.run"]("iostat -dDV").splitlines(): # Note: iostat -dDV is per-system # - #System configuration: lcpu=8 drives=1 paths=2 vdisks=2 + # System configuration: lcpu=8 drives=1 paths=2 vdisks=2 # - #hdisk0 xfer: %tm_act bps tps bread bwrtn + # hdisk0 xfer: %tm_act bps tps bread bwrtn # 0.0 0.8 0.0 0.0 0.8 # read: rps avgserv minserv maxserv timeouts fails # 0.0 2.5 0.3 12.4 0 0 @@ -896,13 +934,13 @@ def diskstats(): # 0.0 0.3 0.2 0.7 0 0 # queue: avgtime mintime maxtime avgwqsz avgsqsz sqfull # 0.3 0.0 5.3 0.0 0.0 0.0 - #-------------------------------------------------------------------------------- - if not line or line.startswith('System') or line.startswith('-----------'): + # -------------------------------------------------------------------------------- + if not line or line.startswith("System") or line.startswith("-----------"): continue - if not re.match(r'\s', line): - #have new disk - dsk_comps = line.split(':') + if not re.match(r"\s", line): + # have new disk + dsk_comps = line.split(":") dsk_firsts = dsk_comps[0].split() disk_name = dsk_firsts[0] disk_mode = dsk_firsts[1] @@ -914,8 +952,8 @@ def diskstats(): ret[disk_name][procn][disk_mode] = {} continue - if ':' in line: - comps = line.split(':') + if ":" in line: + comps = line.split(":") fields = comps[1].split() disk_mode = comps[0].lstrip() procn = len(ret[disk_name]) @@ -931,18 +969,18 @@ def diskstats(): # dict that return a function that does the right thing per platform get_version = { - 'Linux': linux_diskstats, - 'FreeBSD': generic_diskstats, - 'SunOS': generic_diskstats, - 'AIX': aix_diskstats, + "Linux": linux_diskstats, + "FreeBSD": generic_diskstats, + "SunOS": generic_diskstats, + "AIX": aix_diskstats, } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def diskusage(*args): - ''' + """ Return the disk usage for this minion Usage:: @@ -957,15 +995,15 @@ def diskusage(*args): salt '*' status.diskusage / /tmp # usage for / and /tmp salt '*' status.diskusage ext? # usage for ext[234] filesystems salt '*' status.diskusage / ext? # usage for / and all ext filesystems - ''' + """ selected = set() fstypes = set() if not args: # select all filesystems - fstypes.add('*') + fstypes.add("*") else: for arg in args: - if arg.startswith('/'): + if arg.startswith("/"): # select path selected.add(arg) else: @@ -975,25 +1013,25 @@ def diskusage(*args): if fstypes: # determine which mount points host the specified fstypes regex = re.compile( - '|'.join( - fnmatch.translate(fstype).format('(%s)') for fstype in fstypes - ) + "|".join(fnmatch.translate(fstype).format("(%s)") for fstype in fstypes) ) # ifile source of data varies with OS, otherwise all the same - if __grains__['kernel'] == 'Linux': + if __grains__["kernel"] == "Linux": try: - with salt.utils.files.fopen('/proc/mounts', 'r') as fp_: + with salt.utils.files.fopen("/proc/mounts", "r") as fp_: ifile = salt.utils.stringutils.to_unicode(fp_.read()).splitlines() except OSError: return {} - elif __grains__['kernel'] in ('FreeBSD', 'SunOS'): - ifile = __salt__['cmd.run']('mount -p').splitlines() + elif __grains__["kernel"] in ("FreeBSD", "SunOS"): + ifile = __salt__["cmd.run"]("mount -p").splitlines() else: - raise CommandExecutionError('status.diskusage not yet supported on this platform') + raise CommandExecutionError( + "status.diskusage not yet supported on this platform" + ) for line in ifile: comps = line.split() - if __grains__['kernel'] == 'SunOS': + if __grains__["kernel"] == "SunOS": if len(comps) >= 4: mntpt = comps[2] fstype = comps[3] @@ -1018,7 +1056,7 @@ def diskusage(*args): def vmstats(): - ''' + """ .. versionchanged:: 2016.3.2 Return the virtual memory stats for this minion @@ -1030,14 +1068,15 @@ def vmstats(): .. code-block:: bash salt '*' status.vmstats - ''' + """ + def linux_vmstats(): - ''' + """ linux specific implementation of vmstats - ''' + """ ret = {} try: - with salt.utils.files.fopen('/proc/vmstat', 'r') as fp_: + with salt.utils.files.fopen("/proc/vmstat", "r") as fp_: stats = salt.utils.stringutils.to_unicode(fp_.read()) except IOError: pass @@ -1050,32 +1089,32 @@ def vmstats(): return ret def generic_vmstats(): - ''' + """ generic implementation of vmstats note: works on FreeBSD, SunOS and OpenBSD (possibly others) - ''' + """ ret = {} - for line in __salt__['cmd.run']('vmstat -s').splitlines(): + for line in __salt__["cmd.run"]("vmstat -s").splitlines(): comps = line.split() if comps[0].isdigit(): - ret[' '.join(comps[1:])] = _number(comps[0].strip()) + ret[" ".join(comps[1:])] = _number(comps[0].strip()) return ret # dict that returns a function that does the right thing per platform get_version = { - 'Linux': linux_vmstats, - 'FreeBSD': generic_vmstats, - 'OpenBSD': generic_vmstats, - 'SunOS': generic_vmstats, - 'AIX': generic_vmstats, + "Linux": linux_vmstats, + "FreeBSD": generic_vmstats, + "OpenBSD": generic_vmstats, + "SunOS": generic_vmstats, + "AIX": generic_vmstats, } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def nproc(): - ''' + """ Return the number of processing units available on this system .. versionchanged:: 2016.11.4 @@ -1089,21 +1128,22 @@ def nproc(): .. code-block:: bash salt '*' status.nproc - ''' + """ + def linux_nproc(): - ''' + """ linux specific implementation of nproc - ''' + """ try: - return _number(__salt__['cmd.run']('nproc').strip()) + return _number(__salt__["cmd.run"]("nproc").strip()) except ValueError: return 0 def generic_nproc(): - ''' + """ generic implementation of nproc - ''' - ncpu_data = __salt__['sysctl.get']('hw.ncpu') + """ + ncpu_data = __salt__["sysctl.get"]("hw.ncpu") if not ncpu_data: # We need at least one CPU to run return 1 @@ -1112,19 +1152,19 @@ def nproc(): # dict that returns a function that does the right thing per platform get_version = { - 'Linux': linux_nproc, - 'Darwin': generic_nproc, - 'FreeBSD': generic_nproc, - 'OpenBSD': generic_nproc, - 'AIX': _aix_nproc, + "Linux": linux_nproc, + "Darwin": generic_nproc, + "FreeBSD": generic_nproc, + "OpenBSD": generic_nproc, + "AIX": _aix_nproc, } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def netstats(): - ''' + """ Return the network stats for this minion .. versionchanged:: 2016.11.4 @@ -1138,19 +1178,20 @@ def netstats(): .. code-block:: bash salt '*' status.netstats - ''' + """ + def linux_netstats(): - ''' + """ linux specific netstats implementation - ''' + """ ret = {} try: - with salt.utils.files.fopen('/proc/net/netstat', 'r') as fp_: + with salt.utils.files.fopen("/proc/net/netstat", "r") as fp_: stats = salt.utils.stringutils.to_unicode(fp_.read()) except IOError: pass else: - headers = [''] + headers = [""] for line in stats.splitlines(): if not line: continue @@ -1163,7 +1204,7 @@ def netstats(): continue else: row[headers[field]] = _number(comps[field]) - rowname = headers[0].replace(':', '') + rowname = headers[0].replace(":", "") ret[rowname] = row else: headers = comps @@ -1173,56 +1214,56 @@ def netstats(): return bsd_netstats() def bsd_netstats(): - ''' + """ bsd specific netstats implementation - ''' + """ ret = {} - for line in __salt__['cmd.run']('netstat -s').splitlines(): - if line.startswith('\t\t'): + for line in __salt__["cmd.run"]("netstat -s").splitlines(): + if line.startswith("\t\t"): continue # Skip, too detailed - if not line.startswith('\t'): - key = line.split()[0].replace(':', '') + if not line.startswith("\t"): + key = line.split()[0].replace(":", "") ret[key] = {} else: comps = line.split() if comps[0].isdigit(): - ret[key][' '.join(comps[1:])] = comps[0] + ret[key][" ".join(comps[1:])] = comps[0] return ret def sunos_netstats(): - ''' + """ sunos specific netstats implementation - ''' + """ ret = {} - for line in __salt__['cmd.run']('netstat -s').splitlines(): - line = line.replace('=', ' = ').split() + for line in __salt__["cmd.run"]("netstat -s").splitlines(): + line = line.replace("=", " = ").split() if len(line) > 6: line.pop(0) - if '=' in line: + if "=" in line: if len(line) >= 3: - if line[2].isdigit() or line[2][0] == '-': + if line[2].isdigit() or line[2][0] == "-": line[2] = _number(line[2]) ret[line[0]] = line[2] if len(line) >= 6: - if line[5].isdigit() or line[5][0] == '-': + if line[5].isdigit() or line[5][0] == "-": line[5] = _number(line[5]) ret[line[3]] = line[5] return ret def aix_netstats(): - ''' + """ AIX specific netstats implementation - ''' + """ ret = {} fields = [] procn = None proto_name = None - for line in __salt__['cmd.run']('netstat -s').splitlines(): + for line in __salt__["cmd.run"]("netstat -s").splitlines(): if not line: continue - if not re.match(r'\s', line) and ':' in line: - comps = line.split(':') + if not re.match(r"\s", line) and ":" in line: + comps = line.split(":") proto_name = comps[0] ret[proto_name] = [] procn = len(ret[proto_name]) @@ -1232,7 +1273,7 @@ def netstats(): comps = line.split() comps[0] = comps[0].strip() if comps[0].isdigit(): - ret[proto_name][procn][' '.join(comps[1:])] = _number(comps[0]) + ret[proto_name][procn][" ".join(comps[1:])] = _number(comps[0]) else: continue @@ -1240,19 +1281,19 @@ def netstats(): # dict that returns a function that does the right thing per platform get_version = { - 'Linux': linux_netstats, - 'FreeBSD': bsd_netstats, - 'OpenBSD': bsd_netstats, - 'SunOS': sunos_netstats, - 'AIX': aix_netstats, + "Linux": linux_netstats, + "FreeBSD": bsd_netstats, + "OpenBSD": bsd_netstats, + "SunOS": sunos_netstats, + "AIX": aix_netstats, } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def netdev(): - ''' + """ .. versionchanged:: 2016.3.2 Return the network device stats for this minion @@ -1264,14 +1305,15 @@ def netdev(): .. code-block:: bash salt '*' status.netdev - ''' + """ + def linux_netdev(): - ''' + """ linux specific implementation of netdev - ''' + """ ret = {} try: - with salt.utils.files.fopen('/proc/net/dev', 'r') as fp_: + with salt.utils.files.fopen("/proc/net/dev", "r") as fp_: stats = salt.utils.stringutils.to_unicode(fp_.read()) except IOError: pass @@ -1279,40 +1321,42 @@ def netdev(): for line in stats.splitlines(): if not line: continue - if line.find(':') < 0: + if line.find(":") < 0: continue comps = line.split() # Fix lines like eth0:9999..' - comps[0] = line.split(':')[0].strip() + comps[0] = line.split(":")[0].strip() # Support lines both like eth0:999 and eth0: 9999 - comps.insert(1, line.split(':')[1].strip().split()[0]) - ret[comps[0]] = {'iface': comps[0], - 'rx_bytes': _number(comps[2]), - 'rx_compressed': _number(comps[8]), - 'rx_drop': _number(comps[5]), - 'rx_errs': _number(comps[4]), - 'rx_fifo': _number(comps[6]), - 'rx_frame': _number(comps[7]), - 'rx_multicast': _number(comps[9]), - 'rx_packets': _number(comps[3]), - 'tx_bytes': _number(comps[10]), - 'tx_carrier': _number(comps[16]), - 'tx_colls': _number(comps[15]), - 'tx_compressed': _number(comps[17]), - 'tx_drop': _number(comps[13]), - 'tx_errs': _number(comps[12]), - 'tx_fifo': _number(comps[14]), - 'tx_packets': _number(comps[11])} + comps.insert(1, line.split(":")[1].strip().split()[0]) + ret[comps[0]] = { + "iface": comps[0], + "rx_bytes": _number(comps[2]), + "rx_compressed": _number(comps[8]), + "rx_drop": _number(comps[5]), + "rx_errs": _number(comps[4]), + "rx_fifo": _number(comps[6]), + "rx_frame": _number(comps[7]), + "rx_multicast": _number(comps[9]), + "rx_packets": _number(comps[3]), + "tx_bytes": _number(comps[10]), + "tx_carrier": _number(comps[16]), + "tx_colls": _number(comps[15]), + "tx_compressed": _number(comps[17]), + "tx_drop": _number(comps[13]), + "tx_errs": _number(comps[12]), + "tx_fifo": _number(comps[14]), + "tx_packets": _number(comps[11]), + } return ret def freebsd_netdev(): - ''' + """ freebsd specific implementation of netdev - ''' + """ _dict_tree = lambda: collections.defaultdict(_dict_tree) ret = _dict_tree() - netstat = __salt__['cmd.run']('netstat -i -n -4 -b -d').splitlines() - netstat += __salt__['cmd.run']('netstat -i -n -6 -b -d').splitlines()[1:] + netstat = __salt__["cmd.run"]("netstat -i -n -4 -b -d").splitlines() + netstat += __salt__["cmd.run"]("netstat -i -n -6 -b -d").splitlines()[1:] header = netstat[0].split() for line in netstat[1:]: comps = line.split() @@ -1321,15 +1365,19 @@ def netdev(): return ret def sunos_netdev(): - ''' + """ sunos specific implementation of netdev - ''' + """ ret = {} ##NOTE: we cannot use hwaddr_interfaces here, so we grab both ip4 and ip6 - for dev in __grains__['ip4_interfaces'].keys() + __grains__['ip6_interfaces']: + for dev in __grains__["ip4_interfaces"].keys() + __grains__["ip6_interfaces"]: # fetch device info - netstat_ipv4 = __salt__['cmd.run']('netstat -i -I {dev} -n -f inet'.format(dev=dev)).splitlines() - netstat_ipv6 = __salt__['cmd.run']('netstat -i -I {dev} -n -f inet6'.format(dev=dev)).splitlines() + netstat_ipv4 = __salt__["cmd.run"]( + "netstat -i -I {dev} -n -f inet".format(dev=dev) + ).splitlines() + netstat_ipv6 = __salt__["cmd.run"]( + "netstat -i -I {dev} -n -f inet6".format(dev=dev) + ).splitlines() # prepare data netstat_ipv4[0] = netstat_ipv4[0].split() @@ -1339,100 +1387,118 @@ def netdev(): # add data ret[dev] = {} - for i in range(len(netstat_ipv4[0])-1): - if netstat_ipv4[0][i] == 'Name': + for i in range(len(netstat_ipv4[0]) - 1): + if netstat_ipv4[0][i] == "Name": continue - if netstat_ipv4[0][i] in ['Address', 'Net/Dest']: - ret[dev]['IPv4 {field}'.format(field=netstat_ipv4[0][i])] = netstat_ipv4[1][i] + if netstat_ipv4[0][i] in ["Address", "Net/Dest"]: + ret[dev][ + "IPv4 {field}".format(field=netstat_ipv4[0][i]) + ] = netstat_ipv4[1][i] else: ret[dev][netstat_ipv4[0][i]] = _number(netstat_ipv4[1][i]) - for i in range(len(netstat_ipv6[0])-1): - if netstat_ipv6[0][i] == 'Name': + for i in range(len(netstat_ipv6[0]) - 1): + if netstat_ipv6[0][i] == "Name": continue - if netstat_ipv6[0][i] in ['Address', 'Net/Dest']: - ret[dev]['IPv6 {field}'.format(field=netstat_ipv6[0][i])] = netstat_ipv6[1][i] + if netstat_ipv6[0][i] in ["Address", "Net/Dest"]: + ret[dev][ + "IPv6 {field}".format(field=netstat_ipv6[0][i]) + ] = netstat_ipv6[1][i] else: ret[dev][netstat_ipv6[0][i]] = _number(netstat_ipv6[1][i]) return ret def aix_netdev(): - ''' + """ AIX specific implementation of netdev - ''' + """ ret = {} fields = [] procn = None - for dev in __grains__['ip4_interfaces'].keys() + __grains__['ip6_interfaces'].keys(): + for dev in ( + __grains__["ip4_interfaces"].keys() + __grains__["ip6_interfaces"].keys() + ): # fetch device info - #root@la68pp002_pub:/opt/salt/lib/python2.7/site-packages/salt/modules# netstat -i -n -I en0 -f inet6 - #Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll - #en0 1500 link#3 e2.eb.32.42.84.c 10029668 0 446490 0 0 - #en0 1500 172.29.128 172.29.149.95 10029668 0 446490 0 0 - #root@la68pp002_pub:/opt/salt/lib/python2.7/site-packages/salt/modules# netstat -i -n -I en0 -f inet6 - #Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll - #en0 1500 link#3 e2.eb.32.42.84.c 10029731 0 446499 0 0 + # root@la68pp002_pub:/opt/salt/lib/python2.7/site-packages/salt/modules# netstat -i -n -I en0 -f inet6 + # Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll + # en0 1500 link#3 e2.eb.32.42.84.c 10029668 0 446490 0 0 + # en0 1500 172.29.128 172.29.149.95 10029668 0 446490 0 0 + # root@la68pp002_pub:/opt/salt/lib/python2.7/site-packages/salt/modules# netstat -i -n -I en0 -f inet6 + # Name Mtu Network Address Ipkts Ierrs Opkts Oerrs Coll + # en0 1500 link#3 e2.eb.32.42.84.c 10029731 0 446499 0 0 - netstat_ipv4 = __salt__['cmd.run']('netstat -i -n -I {dev} -f inet'.format(dev=dev)).splitlines() - netstat_ipv6 = __salt__['cmd.run']('netstat -i -n -I {dev} -f inet6'.format(dev=dev)).splitlines() + netstat_ipv4 = __salt__["cmd.run"]( + "netstat -i -n -I {dev} -f inet".format(dev=dev) + ).splitlines() + netstat_ipv6 = __salt__["cmd.run"]( + "netstat -i -n -I {dev} -f inet6".format(dev=dev) + ).splitlines() # add data ret[dev] = [] for line in netstat_ipv4: - if line.startswith('Name'): + if line.startswith("Name"): fields = line.split() continue comps = line.split() if len(comps) < 3: - raise CommandExecutionError('Insufficent data returned by command to process \'{0}\''.format(line)) + raise CommandExecutionError( + "Insufficent data returned by command to process '{0}'".format( + line + ) + ) - if comps[2].startswith('link'): + if comps[2].startswith("link"): continue procn = len(ret[dev]) ret[dev].append({}) - ret[dev][procn]['ipv4'] = {} + ret[dev][procn]["ipv4"] = {} for i in range(1, len(fields)): if len(comps) > i: - ret[dev][procn]['ipv4'][fields[i]] = comps[i] + ret[dev][procn]["ipv4"][fields[i]] = comps[i] for line in netstat_ipv6: - if line.startswith('Name'): + if line.startswith("Name"): fields = line.split() continue comps = line.split() if len(comps) < 3: - raise CommandExecutionError('Insufficent data returned by command to process \'{0}\''.format(line)) + raise CommandExecutionError( + "Insufficent data returned by command to process '{0}'".format( + line + ) + ) - if comps[2].startswith('link'): + if comps[2].startswith("link"): continue procn = len(ret[dev]) ret[dev].append({}) - ret[dev][procn]['ipv6'] = {} + ret[dev][procn]["ipv6"] = {} for i in range(1, len(fields)): if len(comps) > i: - ret[dev][procn]['ipv6'][fields[i]] = comps[i] + ret[dev][procn]["ipv6"][fields[i]] = comps[i] return ret # dict that returns a function that does the right thing per platform get_version = { - 'Linux': linux_netdev, - 'FreeBSD': freebsd_netdev, - 'SunOS': sunos_netdev, - 'AIX': aix_netdev, + "Linux": linux_netdev, + "FreeBSD": freebsd_netdev, + "SunOS": sunos_netdev, + "AIX": aix_netdev, } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def w(): # pylint: disable=C0103 - ''' + """ Return a list of logged in users for this minion, using the w command CLI Example: @@ -1440,60 +1506,65 @@ def w(): # pylint: disable=C0103 .. code-block:: bash salt '*' status.w - ''' + """ + def linux_w(): - ''' + """ Linux specific implementation for w - ''' + """ user_list = [] - users = __salt__['cmd.run']('w -fh').splitlines() + users = __salt__["cmd.run"]("w -fh").splitlines() for row in users: if not row: continue comps = row.split() - rec = {'idle': comps[3], - 'jcpu': comps[4], - 'login': comps[2], - 'pcpu': comps[5], - 'tty': comps[1], - 'user': comps[0], - 'what': ' '.join(comps[6:])} + rec = { + "idle": comps[3], + "jcpu": comps[4], + "login": comps[2], + "pcpu": comps[5], + "tty": comps[1], + "user": comps[0], + "what": " ".join(comps[6:]), + } user_list.append(rec) return user_list def bsd_w(): - ''' + """ Generic BSD implementation for w - ''' + """ user_list = [] - users = __salt__['cmd.run']('w -h').splitlines() + users = __salt__["cmd.run"]("w -h").splitlines() for row in users: if not row: continue comps = row.split() - rec = {'from': comps[2], - 'idle': comps[4], - 'login': comps[3], - 'tty': comps[1], - 'user': comps[0], - 'what': ' '.join(comps[5:])} + rec = { + "from": comps[2], + "idle": comps[4], + "login": comps[3], + "tty": comps[1], + "user": comps[0], + "what": " ".join(comps[5:]), + } user_list.append(rec) return user_list # dict that returns a function that does the right thing per platform get_version = { - 'Darwin': bsd_w, - 'FreeBSD': bsd_w, - 'Linux': linux_w, - 'OpenBSD': bsd_w, + "Darwin": bsd_w, + "FreeBSD": bsd_w, + "Linux": linux_w, + "OpenBSD": bsd_w, } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def all_status(): - ''' + """ Return a composite of all status data and info for this minion. Warning: There is a LOT here! @@ -1502,22 +1573,24 @@ def all_status(): .. code-block:: bash salt '*' status.all_status - ''' - return {'cpuinfo': cpuinfo(), - 'cpustats': cpustats(), - 'diskstats': diskstats(), - 'diskusage': diskusage(), - 'loadavg': loadavg(), - 'meminfo': meminfo(), - 'netdev': netdev(), - 'netstats': netstats(), - 'uptime': uptime(), - 'vmstats': vmstats(), - 'w': w()} + """ + return { + "cpuinfo": cpuinfo(), + "cpustats": cpustats(), + "diskstats": diskstats(), + "diskusage": diskusage(), + "loadavg": loadavg(), + "meminfo": meminfo(), + "netdev": netdev(), + "netstats": netstats(), + "uptime": uptime(), + "vmstats": vmstats(), + "w": w(), + } def pid(sig): - ''' + """ Return the PID or an empty string if the process is running or not. Pass a signature to use to find the process via ps. Note you can pass a Python-compatible regular expression to return all pids of @@ -1531,25 +1604,25 @@ def pid(sig): .. code-block:: bash salt '*' status.pid <sig> - ''' + """ - cmd = __grains__['ps'] - output = __salt__['cmd.run_stdout'](cmd, python_shell=True) + cmd = __grains__["ps"] + output = __salt__["cmd.run_stdout"](cmd, python_shell=True) - pids = '' + pids = "" for line in output.splitlines(): - if 'status.pid' in line: + if "status.pid" in line: continue if re.search(sig, line): if pids: - pids += '\n' + pids += "\n" pids += line.split()[1] return pids def version(): - ''' + """ Return the system version for this minion .. versionchanged:: 2016.11.4 @@ -1563,37 +1636,38 @@ def version(): .. code-block:: bash salt '*' status.version - ''' + """ + def linux_version(): - ''' + """ linux specific implementation of version - ''' + """ try: - with salt.utils.files.fopen('/proc/version', 'r') as fp_: + with salt.utils.files.fopen("/proc/version", "r") as fp_: return salt.utils.stringutils.to_unicode(fp_.read()).strip() except IOError: return {} def bsd_version(): - ''' + """ bsd specific implementation of version - ''' - return __salt__['cmd.run']('sysctl -n kern.version') + """ + return __salt__["cmd.run"]("sysctl -n kern.version") # dict that returns a function that does the right thing per platform get_version = { - 'Linux': linux_version, - 'FreeBSD': bsd_version, - 'OpenBSD': bsd_version, - 'AIX': lambda: __salt__['cmd.run']('oslevel -s'), + "Linux": linux_version, + "FreeBSD": bsd_version, + "OpenBSD": bsd_version, + "AIX": lambda: __salt__["cmd.run"]("oslevel -s"), } - errmsg = 'This method is unsupported on the current operating system!' - return get_version.get(__grains__['kernel'], lambda: errmsg)() + errmsg = "This method is unsupported on the current operating system!" + return get_version.get(__grains__["kernel"], lambda: errmsg)() def master(master=None, connected=True): - ''' + """ .. versionadded:: 2014.7.0 Return the connection status with master. Fire an event if the @@ -1609,7 +1683,7 @@ def master(master=None, connected=True): .. code-block:: bash salt '*' status.master - ''' + """ master_ips = None if master: @@ -1619,7 +1693,7 @@ def master(master=None, connected=True): return master_connection_status = False - port = __salt__['config.get']('publish_port', default=4505) + port = __salt__["config.get"]("publish_port", default=4505) connected_ips = salt.utils.network.remote_port_tcp(port) # Get connection status for master @@ -1630,17 +1704,21 @@ def master(master=None, connected=True): # Connection to master is not as expected if master_connection_status is not connected: - with salt.utils.event.get_event('minion', opts=__opts__, listen=False) as event: + with salt.utils.event.get_event("minion", opts=__opts__, listen=False) as event: if master_connection_status: - event.fire_event({'master': master}, salt.minion.master_event(type='connected')) + event.fire_event( + {"master": master}, salt.minion.master_event(type="connected") + ) else: - event.fire_event({'master': master}, salt.minion.master_event(type='disconnected')) + event.fire_event( + {"master": master}, salt.minion.master_event(type="disconnected") + ) return master_connection_status def ping_master(master): - ''' + """ .. versionadded:: 2016.3.0 Sends ping request to the given master. Fires '__master_failback' event on success. @@ -1651,25 +1729,25 @@ def ping_master(master): .. code-block:: bash salt '*' status.ping_master localhost - ''' - if master is None or master == '': + """ + if master is None or master == "": return False opts = copy.deepcopy(__opts__) - opts['master'] = master - if 'master_ip' in opts: # avoid 'master ip changed' warning - del opts['master_ip'] + opts["master"] = master + if "master_ip" in opts: # avoid 'master ip changed' warning + del opts["master_ip"] opts.update(salt.minion.prep_ip_port(opts)) try: opts.update(salt.minion.resolve_dns(opts, fallback=False)) except Exception: # pylint: disable=broad-except return False - timeout = opts.get('auth_timeout', 60) - load = {'cmd': 'ping'} + timeout = opts.get("auth_timeout", 60) + load = {"cmd": "ping"} result = False - with salt.transport.client.ReqChannel.factory(opts, crypt='clear') as channel: + with salt.transport.client.ReqChannel.factory(opts, crypt="clear") as channel: try: payload = channel.send(load, tries=0, timeout=timeout) result = True @@ -1677,14 +1755,18 @@ def ping_master(master): pass if result: - with salt.utils.event.get_event('minion', opts=__opts__, listen=False) as event: - event.fire_event({'master': master}, salt.minion.master_event(type='failback')) + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=False + ) as event: + event.fire_event( + {"master": master}, salt.minion.master_event(type="failback") + ) return result def proxy_reconnect(proxy_name, opts=None): - ''' + """ Forces proxy minion reconnection when not alive. proxy_name @@ -1696,31 +1778,31 @@ def proxy_reconnect(proxy_name, opts=None): CLI Example: salt '*' status.proxy_reconnect rest_sample - ''' + """ if not opts: opts = __opts__ - if 'proxy' not in opts: + if "proxy" not in opts: return False # fail - proxy_keepalive_fn = proxy_name+'.alive' + proxy_keepalive_fn = proxy_name + ".alive" if proxy_keepalive_fn not in __proxy__: return False # fail is_alive = __proxy__[proxy_keepalive_fn](opts) if not is_alive: - minion_id = opts.get('proxyid', '') or opts.get('id', '') - log.info('%s (%s proxy) is down. Restarting.', minion_id, proxy_name) - __proxy__[proxy_name+'.shutdown'](opts) # safely close connection - __proxy__[proxy_name+'.init'](opts) # reopen connection - log.debug('Restarted %s (%s proxy)!', minion_id, proxy_name) + minion_id = opts.get("proxyid", "") or opts.get("id", "") + log.info("%s (%s proxy) is down. Restarting.", minion_id, proxy_name) + __proxy__[proxy_name + ".shutdown"](opts) # safely close connection + __proxy__[proxy_name + ".init"](opts) # reopen connection + log.debug("Restarted %s (%s proxy)!", minion_id, proxy_name) return True # success -def time_(format='%A, %d. %B %Y %I:%M%p'): - ''' +def time_(format="%A, %d. %B %Y %I:%M%p"): + """ .. versionadded:: 2016.3.0 Return the current time on the minion, @@ -1736,7 +1818,7 @@ def time_(format='%A, %d. %B %Y %I:%M%p'): salt '*' status.time '%s' - ''' + """ dt = datetime.datetime.today() return dt.strftime(format) diff --git a/salt/modules/statuspage.py b/salt/modules/statuspage.py index 3fa5dd0aa43..820057b4829 100644 --- a/salt/modules/statuspage.py +++ b/salt/modules/statuspage.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" StatusPage ========== @@ -17,46 +17,45 @@ In the minion configuration file, the following block is required: page_id: <PAGE_ID> .. versionadded:: 2017.7.0 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # import python std lib import logging +# import salt +from salt.ext import six + # import third party try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False -# import salt -from salt.ext import six # ---------------------------------------------------------------------------------------------------------------------- # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'statuspage' +__virtualname__ = "statuspage" log = logging.getLogger(__file__) -BASE_URL = 'https://api.statuspage.io' +BASE_URL = "https://api.statuspage.io" DEFAULT_VERSION = 1 UPDATE_FORBIDDEN_FILEDS = [ - 'id', # can't rewrite this - 'created_at', - 'updated_at', # updated_at and created_at are handled by the backend framework of the API - 'page_id' # can't move it to a different page + "id", # can't rewrite this + "created_at", + "updated_at", # updated_at and created_at are handled by the backend framework of the API + "page_id", # can't move it to a different page ] INSERT_FORBIDDEN_FILEDS = UPDATE_FORBIDDEN_FILEDS[:] # they are the same for the moment -METHOD_OK_STATUS = { - 'POST': 201, - 'DELETE': 204 -} +METHOD_OK_STATUS = {"POST": 201, "DELETE": 204} # ---------------------------------------------------------------------------------------------------------------------- # property functions @@ -64,83 +63,71 @@ METHOD_OK_STATUS = { def __virtual__(): - ''' + """ Return the execution module virtualname. - ''' + """ if HAS_REQUESTS is False: - return False, 'The requests python package is not installed' + return False, "The requests python package is not installed" return __virtualname__ def _default_ret(): - ''' + """ Default dictionary returned. - ''' - return { - 'result': False, - 'comment': '', - 'out': None - } + """ + return {"result": False, "comment": "", "out": None} -def _get_api_params(api_url=None, - page_id=None, - api_key=None, - api_version=None): - ''' +def _get_api_params(api_url=None, page_id=None, api_key=None, api_version=None): + """ Retrieve the API params from the config file. - ''' - statuspage_cfg = __salt__['config.get']('statuspage') + """ + statuspage_cfg = __salt__["config.get"]("statuspage") if not statuspage_cfg: statuspage_cfg = {} return { - 'api_url': api_url or statuspage_cfg.get('api_url') or BASE_URL, # optional - 'api_page_id': page_id or statuspage_cfg.get('page_id'), # mandatory - 'api_key': api_key or statuspage_cfg.get('api_key'), # mandatory - 'api_version': api_version or statuspage_cfg.get('api_version') or DEFAULT_VERSION + "api_url": api_url or statuspage_cfg.get("api_url") or BASE_URL, # optional + "api_page_id": page_id or statuspage_cfg.get("page_id"), # mandatory + "api_key": api_key or statuspage_cfg.get("api_key"), # mandatory + "api_version": api_version + or statuspage_cfg.get("api_version") + or DEFAULT_VERSION, } def _validate_api_params(params): - ''' + """ Validate the API params as specified in the config file. - ''' + """ # page_id and API key are mandatory and they must be string/unicode - return (isinstance(params['api_page_id'], (six.string_types, six.text_type)) and - isinstance(params['api_key'], (six.string_types, six.text_type))) + return isinstance( + params["api_page_id"], (six.string_types, six.text_type) + ) and isinstance(params["api_key"], (six.string_types, six.text_type)) def _get_headers(params): - ''' + """ Return HTTP headers required. - ''' - return { - 'Authorization': 'OAuth {oauth}'.format(oauth=params['api_key']) - } + """ + return {"Authorization": "OAuth {oauth}".format(oauth=params["api_key"])} -def _http_request(url, - method='GET', - headers=None, - data=None): - ''' +def _http_request(url, method="GET", headers=None, data=None): + """ Make the HTTP request and return the body as python object. - ''' - req = requests.request(method, - url, - headers=headers, - data=data) + """ + req = requests.request(method, url, headers=headers, data=data) ret = _default_ret() ok_status = METHOD_OK_STATUS.get(method, 200) if req.status_code != ok_status: - ret.update({ - 'comment': req.json().get('error', '') - }) + ret.update({"comment": req.json().get("error", "")}) return ret - ret.update({ - 'result': True, - 'out': req.json() if method != 'DELETE' else None # no body when DELETE - }) + ret.update( + { + "result": True, + "out": req.json() if method != "DELETE" else None, # no body when DELETE + } + ) return ret @@ -149,13 +136,15 @@ def _http_request(url, # ---------------------------------------------------------------------------------------------------------------------- -def create(endpoint='incidents', - api_url=None, - page_id=None, - api_key=None, - api_version=None, - **kwargs): - ''' +def create( + endpoint="incidents", + api_url=None, + page_id=None, + api_key=None, + api_version=None, + **kwargs +): + """ Insert a new entry under a specific endpoint. endpoint: incidents @@ -208,47 +197,39 @@ def create(endpoint='incidents', 2017-01-05T19:35:27.135Z result: True - ''' - params = _get_api_params(api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version) + """ + params = _get_api_params( + api_url=api_url, page_id=page_id, api_key=api_key, api_version=api_version + ) if not _validate_api_params(params): - log.error('Invalid API params.') + log.error("Invalid API params.") log.error(params) - return { - 'result': False, - 'comment': 'Invalid API params. See log for details' - } + return {"result": False, "comment": "Invalid API params. See log for details"} endpoint_sg = endpoint[:-1] # singular headers = _get_headers(params) - create_url = '{base_url}/v{version}/pages/{page_id}/{endpoint}.json'.format( - base_url=params['api_url'], - version=params['api_version'], - page_id=params['api_page_id'], - endpoint=endpoint + create_url = "{base_url}/v{version}/pages/{page_id}/{endpoint}.json".format( + base_url=params["api_url"], + version=params["api_version"], + page_id=params["api_page_id"], + endpoint=endpoint, ) change_request = {} for karg, warg in six.iteritems(kwargs): - if warg is None or karg.startswith('__') or karg in INSERT_FORBIDDEN_FILEDS: + if warg is None or karg.startswith("__") or karg in INSERT_FORBIDDEN_FILEDS: continue - change_request_key = '{endpoint_sg}[{karg}]'.format( - endpoint_sg=endpoint_sg, - karg=karg + change_request_key = "{endpoint_sg}[{karg}]".format( + endpoint_sg=endpoint_sg, karg=karg ) change_request[change_request_key] = warg - return _http_request(create_url, - method='POST', - headers=headers, - data=change_request) + return _http_request( + create_url, method="POST", headers=headers, data=change_request + ) -def retrieve(endpoint='incidents', - api_url=None, - page_id=None, - api_key=None, - api_version=None): - ''' +def retrieve( + endpoint="incidents", api_url=None, page_id=None, api_key=None, api_version=None +): + """ Retrieve a specific endpoint from the Statuspage API. endpoint: incidents @@ -355,37 +336,34 @@ def retrieve(endpoint='incidents', 2015-01-26T20:25:13.379Z result: True - ''' - params = _get_api_params(api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version) - if not _validate_api_params(params): - log.error('Invalid API params.') - log.error(params) - return { - 'result': False, - 'comment': 'Invalid API params. See log for details' - } - headers = _get_headers(params) - retrieve_url = '{base_url}/v{version}/pages/{page_id}/{endpoint}.json'.format( - base_url=params['api_url'], - version=params['api_version'], - page_id=params['api_page_id'], - endpoint=endpoint + """ + params = _get_api_params( + api_url=api_url, page_id=page_id, api_key=api_key, api_version=api_version ) - return _http_request(retrieve_url, - headers=headers) + if not _validate_api_params(params): + log.error("Invalid API params.") + log.error(params) + return {"result": False, "comment": "Invalid API params. See log for details"} + headers = _get_headers(params) + retrieve_url = "{base_url}/v{version}/pages/{page_id}/{endpoint}.json".format( + base_url=params["api_url"], + version=params["api_version"], + page_id=params["api_page_id"], + endpoint=endpoint, + ) + return _http_request(retrieve_url, headers=headers) -def update(endpoint='incidents', - id=None, - api_url=None, - page_id=None, - api_key=None, - api_version=None, - **kwargs): - ''' +def update( + endpoint="incidents", + id=None, + api_url=None, + page_id=None, + api_key=None, + api_version=None, + **kwargs +): + """ Update attribute(s) of a specific endpoint. id @@ -441,55 +419,53 @@ def update(endpoint='incidents', 2017-01-05T15:34:27.676Z result: True - ''' + """ endpoint_sg = endpoint[:-1] # singular if not id: - log.error('Invalid %s ID', endpoint_sg) + log.error("Invalid %s ID", endpoint_sg) return { - 'result': False, - 'comment': 'Please specify a valid {endpoint} ID'.format(endpoint=endpoint_sg) + "result": False, + "comment": "Please specify a valid {endpoint} ID".format( + endpoint=endpoint_sg + ), } - params = _get_api_params(api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version) + params = _get_api_params( + api_url=api_url, page_id=page_id, api_key=api_key, api_version=api_version + ) if not _validate_api_params(params): - log.error('Invalid API params.') + log.error("Invalid API params.") log.error(params) - return { - 'result': False, - 'comment': 'Invalid API params. See log for details' - } + return {"result": False, "comment": "Invalid API params. See log for details"} headers = _get_headers(params) - update_url = '{base_url}/v{version}/pages/{page_id}/{endpoint}/{id}.json'.format( - base_url=params['api_url'], - version=params['api_version'], - page_id=params['api_page_id'], + update_url = "{base_url}/v{version}/pages/{page_id}/{endpoint}/{id}.json".format( + base_url=params["api_url"], + version=params["api_version"], + page_id=params["api_page_id"], endpoint=endpoint, - id=id + id=id, ) change_request = {} for karg, warg in six.iteritems(kwargs): - if warg is None or karg.startswith('__') or karg in UPDATE_FORBIDDEN_FILEDS: + if warg is None or karg.startswith("__") or karg in UPDATE_FORBIDDEN_FILEDS: continue - change_request_key = '{endpoint_sg}[{karg}]'.format( - endpoint_sg=endpoint_sg, - karg=karg + change_request_key = "{endpoint_sg}[{karg}]".format( + endpoint_sg=endpoint_sg, karg=karg ) change_request[change_request_key] = warg - return _http_request(update_url, - method='PATCH', - headers=headers, - data=change_request) + return _http_request( + update_url, method="PATCH", headers=headers, data=change_request + ) -def delete(endpoint='incidents', - id=None, - api_url=None, - page_id=None, - api_key=None, - api_version=None): - ''' +def delete( + endpoint="incidents", + id=None, + api_url=None, + page_id=None, + api_key=None, + api_version=None, +): + """ Remove an entry from an endpoint. endpoint: incidents @@ -524,33 +500,29 @@ def delete(endpoint='incidents', None result: True - ''' - params = _get_api_params(api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version) + """ + params = _get_api_params( + api_url=api_url, page_id=page_id, api_key=api_key, api_version=api_version + ) if not _validate_api_params(params): - log.error('Invalid API params.') + log.error("Invalid API params.") log.error(params) - return { - 'result': False, - 'comment': 'Invalid API params. See log for details' - } + return {"result": False, "comment": "Invalid API params. See log for details"} endpoint_sg = endpoint[:-1] # singular if not id: - log.error('Invalid %s ID', endpoint_sg) + log.error("Invalid %s ID", endpoint_sg) return { - 'result': False, - 'comment': 'Please specify a valid {endpoint} ID'.format(endpoint=endpoint_sg) + "result": False, + "comment": "Please specify a valid {endpoint} ID".format( + endpoint=endpoint_sg + ), } headers = _get_headers(params) - delete_url = '{base_url}/v{version}/pages/{page_id}/{endpoint}/{id}.json'.format( - base_url=params['api_url'], - version=params['api_version'], - page_id=params['api_page_id'], + delete_url = "{base_url}/v{version}/pages/{page_id}/{endpoint}/{id}.json".format( + base_url=params["api_url"], + version=params["api_version"], + page_id=params["api_page_id"], endpoint=endpoint, - id=id + id=id, ) - return _http_request(delete_url, - method='DELETE', - headers=headers) + return _http_request(delete_url, method="DELETE", headers=headers) diff --git a/salt/modules/supervisord.py b/salt/modules/supervisord.py index 9eb71418dd1..ed0969d749f 100644 --- a/salt/modules/supervisord.py +++ b/salt/modules/supervisord.py @@ -1,22 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Provide the service module for system supervisord or supervisord in a virtualenv -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import 3rd-party libs -from salt.ext.six import string_types -from salt.ext.six.moves import configparser # pylint: disable=import-error +import os # Import salt libs import salt.utils.stringutils from salt.exceptions import CommandExecutionError, CommandNotFoundError +# Import 3rd-party libs +from salt.ext.six import string_types +from salt.ext.six.moves import configparser # pylint: disable=import-error + def __virtual__(): # We can't decide at load time whether supervisorctl is present. The @@ -26,36 +27,34 @@ def __virtual__(): def _get_supervisorctl_bin(bin_env): - ''' + """ Return supervisorctl command to call, either from a virtualenv, an argument passed in, or from the global modules options - ''' - cmd = 'supervisorctl' + """ + cmd = "supervisorctl" if not bin_env: - which_result = __salt__['cmd.which_bin']([cmd]) + which_result = __salt__["cmd.which_bin"]([cmd]) if which_result is None: - raise CommandNotFoundError( - 'Could not find a `{0}` binary'.format(cmd) - ) + raise CommandNotFoundError("Could not find a `{0}` binary".format(cmd)) return which_result # try to get binary from env if os.path.isdir(bin_env): - cmd_bin = os.path.join(bin_env, 'bin', cmd) + cmd_bin = os.path.join(bin_env, "bin", cmd) if os.path.isfile(cmd_bin): return cmd_bin - raise CommandNotFoundError('Could not find a `{0}` binary'.format(cmd)) + raise CommandNotFoundError("Could not find a `{0}` binary".format(cmd)) return bin_env def _ctl_cmd(cmd, name, conf_file, bin_env): - ''' + """ Return the command list to use - ''' + """ ret = [_get_supervisorctl_bin(bin_env)] if conf_file is not None: - ret += ['-c', conf_file] + ret += ["-c", conf_file] ret.append(cmd) if name: ret.append(name) @@ -63,16 +62,16 @@ def _ctl_cmd(cmd, name, conf_file, bin_env): def _get_return(ret): - retmsg = ret['stdout'] - if ret['retcode'] != 0: + retmsg = ret["stdout"] + if ret["retcode"] != 0: # This is a non 0 exit code - if 'ERROR' not in retmsg: - retmsg = 'ERROR: {}'.format(retmsg) + if "ERROR" not in retmsg: + retmsg = "ERROR: {}".format(retmsg) return retmsg -def start(name='all', user=None, conf_file=None, bin_env=None): - ''' +def start(name="all", user=None, conf_file=None, bin_env=None): + """ Start the named service. Process group names should not include a trailing asterisk. @@ -90,19 +89,17 @@ def start(name='all', user=None, conf_file=None, bin_env=None): salt '*' supervisord.start <service> salt '*' supervisord.start <group>: - ''' - if name.endswith(':*'): + """ + if name.endswith(":*"): name = name[:-1] - ret = __salt__['cmd.run_all']( - _ctl_cmd('start', name, conf_file, bin_env), - runas=user, - python_shell=False, + ret = __salt__["cmd.run_all"]( + _ctl_cmd("start", name, conf_file, bin_env), runas=user, python_shell=False, ) return _get_return(ret) -def restart(name='all', user=None, conf_file=None, bin_env=None): - ''' +def restart(name="all", user=None, conf_file=None, bin_env=None): + """ Restart the named service. Process group names should not include a trailing asterisk. @@ -120,19 +117,17 @@ def restart(name='all', user=None, conf_file=None, bin_env=None): salt '*' supervisord.restart <service> salt '*' supervisord.restart <group>: - ''' - if name.endswith(':*'): + """ + if name.endswith(":*"): name = name[:-1] - ret = __salt__['cmd.run_all']( - _ctl_cmd('restart', name, conf_file, bin_env), - runas=user, - python_shell=False, + ret = __salt__["cmd.run_all"]( + _ctl_cmd("restart", name, conf_file, bin_env), runas=user, python_shell=False, ) return _get_return(ret) -def stop(name='all', user=None, conf_file=None, bin_env=None): - ''' +def stop(name="all", user=None, conf_file=None, bin_env=None): + """ Stop the named service. Process group names should not include a trailing asterisk. @@ -150,19 +145,17 @@ def stop(name='all', user=None, conf_file=None, bin_env=None): salt '*' supervisord.stop <service> salt '*' supervisord.stop <group>: - ''' - if name.endswith(':*'): + """ + if name.endswith(":*"): name = name[:-1] - ret = __salt__['cmd.run_all']( - _ctl_cmd('stop', name, conf_file, bin_env), - runas=user, - python_shell=False, + ret = __salt__["cmd.run_all"]( + _ctl_cmd("stop", name, conf_file, bin_env), runas=user, python_shell=False, ) return _get_return(ret) def add(name, user=None, conf_file=None, bin_env=None): - ''' + """ Activates any updates in config for process/group. user @@ -178,21 +171,19 @@ def add(name, user=None, conf_file=None, bin_env=None): .. code-block:: bash salt '*' supervisord.add <name> - ''' - if name.endswith(':'): + """ + if name.endswith(":"): name = name[:-1] - elif name.endswith(':*'): + elif name.endswith(":*"): name = name[:-2] - ret = __salt__['cmd.run_all']( - _ctl_cmd('add', name, conf_file, bin_env), - runas=user, - python_shell=False, + ret = __salt__["cmd.run_all"]( + _ctl_cmd("add", name, conf_file, bin_env), runas=user, python_shell=False, ) return _get_return(ret) def remove(name, user=None, conf_file=None, bin_env=None): - ''' + """ Removes process/group from active config user @@ -208,21 +199,19 @@ def remove(name, user=None, conf_file=None, bin_env=None): .. code-block:: bash salt '*' supervisord.remove <name> - ''' - if name.endswith(':'): + """ + if name.endswith(":"): name = name[:-1] - elif name.endswith(':*'): + elif name.endswith(":*"): name = name[:-2] - ret = __salt__['cmd.run_all']( - _ctl_cmd('remove', name, conf_file, bin_env), - runas=user, - python_shell=False, + ret = __salt__["cmd.run_all"]( + _ctl_cmd("remove", name, conf_file, bin_env), runas=user, python_shell=False, ) return _get_return(ret) def reread(user=None, conf_file=None, bin_env=None): - ''' + """ Reload the daemon's configuration files user @@ -238,17 +227,15 @@ def reread(user=None, conf_file=None, bin_env=None): .. code-block:: bash salt '*' supervisord.reread - ''' - ret = __salt__['cmd.run_all']( - _ctl_cmd('reread', None, conf_file, bin_env), - runas=user, - python_shell=False, + """ + ret = __salt__["cmd.run_all"]( + _ctl_cmd("reread", None, conf_file, bin_env), runas=user, python_shell=False, ) return _get_return(ret) def update(user=None, conf_file=None, bin_env=None, name=None): - ''' + """ Reload config and add/remove/update as necessary user @@ -267,24 +254,22 @@ def update(user=None, conf_file=None, bin_env=None, name=None): .. code-block:: bash salt '*' supervisord.update - ''' + """ if isinstance(name, string_types): - if name.endswith(':'): + if name.endswith(":"): name = name[:-1] - elif name.endswith(':*'): + elif name.endswith(":*"): name = name[:-2] - ret = __salt__['cmd.run_all']( - _ctl_cmd('update', name, conf_file, bin_env), - runas=user, - python_shell=False, + ret = __salt__["cmd.run_all"]( + _ctl_cmd("update", name, conf_file, bin_env), runas=user, python_shell=False, ) return _get_return(ret) def status(name=None, user=None, conf_file=None, bin_env=None): - ''' + """ List programs and its state user @@ -300,19 +285,19 @@ def status(name=None, user=None, conf_file=None, bin_env=None): .. code-block:: bash salt '*' supervisord.status - ''' + """ all_process = {} for line in status_raw(name, user, conf_file, bin_env).splitlines(): if len(line.split()) > 2: process, state, reason = line.split(None, 2) else: - process, state, reason = line.split() + [''] - all_process[process] = {'state': state, 'reason': reason} + process, state, reason = line.split() + [""] + all_process[process] = {"state": state, "reason": reason} return all_process def status_raw(name=None, user=None, conf_file=None, bin_env=None): - ''' + """ Display the raw output of status user @@ -328,17 +313,15 @@ def status_raw(name=None, user=None, conf_file=None, bin_env=None): .. code-block:: bash salt '*' supervisord.status_raw - ''' - ret = __salt__['cmd.run_all']( - _ctl_cmd('status', name, conf_file, bin_env), - runas=user, - python_shell=False, + """ + ret = __salt__["cmd.run_all"]( + _ctl_cmd("status", name, conf_file, bin_env), runas=user, python_shell=False, ) return _get_return(ret) def custom(command, user=None, conf_file=None, bin_env=None): - ''' + """ Run any custom supervisord command user @@ -354,11 +337,9 @@ def custom(command, user=None, conf_file=None, bin_env=None): .. code-block:: bash salt '*' supervisord.custom "mstop '*gunicorn*'" - ''' - ret = __salt__['cmd.run_all']( - _ctl_cmd(command, None, conf_file, bin_env), - runas=user, - python_shell=False, + """ + ret = __salt__["cmd.run_all"]( + _ctl_cmd(command, None, conf_file, bin_env), runas=user, python_shell=False, ) return _get_return(ret) @@ -366,29 +347,29 @@ def custom(command, user=None, conf_file=None, bin_env=None): # TODO: try to find a way to use the supervisor python module to read the # config information def _read_config(conf_file=None): - ''' + """ Reads the config file using configparser - ''' + """ if conf_file is None: - paths = ('/etc/supervisor/supervisord.conf', '/etc/supervisord.conf') + paths = ("/etc/supervisor/supervisord.conf", "/etc/supervisord.conf") for path in paths: if os.path.exists(path): conf_file = path break if conf_file is None: - raise CommandExecutionError('No suitable config file found') + raise CommandExecutionError("No suitable config file found") config = configparser.ConfigParser() try: config.read(conf_file) except (IOError, OSError) as exc: raise CommandExecutionError( - 'Unable to read from {0}: {1}'.format(conf_file, exc) + "Unable to read from {0}: {1}".format(conf_file, exc) ) return config def options(name, conf_file=None): - ''' + """ .. versionadded:: 2014.1.0 Read the config file and return the config options for a given process @@ -403,19 +384,19 @@ def options(name, conf_file=None): .. code-block:: bash salt '*' supervisord.options foo - ''' + """ config = _read_config(conf_file) - section_name = 'program:{0}'.format(name) + section_name = "program:{0}".format(name) if section_name not in config.sections(): - raise CommandExecutionError('Process \'{0}\' not found'.format(name)) + raise CommandExecutionError("Process '{0}' not found".format(name)) ret = {} for key, val in config.items(section_name): - val = salt.utils.stringutils.to_num(val.split(';')[0].strip()) + val = salt.utils.stringutils.to_num(val.split(";")[0].strip()) # pylint: disable=maybe-no-member if isinstance(val, string_types): - if val.lower() == 'true': + if val.lower() == "true": val = True - elif val.lower() == 'false': + elif val.lower() == "false": val = False # pylint: enable=maybe-no-member ret[key] = val diff --git a/salt/modules/suse_apache.py b/salt/modules/suse_apache.py index 3d357eaed49..885c0d7840a 100644 --- a/salt/modules/suse_apache.py +++ b/salt/modules/suse_apache.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Support for Apache Please note: The functions in here are SUSE-specific. Placing them in this separate file will allow them to load only on SUSE systems, while still loading under the ``apache`` namespace. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -16,20 +16,20 @@ import salt.utils.path log = logging.getLogger(__name__) -__virtualname__ = 'apache' +__virtualname__ = "apache" def __virtual__(): - ''' + """ Only load the module if apache is installed. - ''' - if salt.utils.path.which('apache2ctl') and __grains__['os_family'] == 'Suse': + """ + if salt.utils.path.which("apache2ctl") and __grains__["os_family"] == "Suse": return __virtualname__ - return (False, 'apache execution module not loaded: apache not installed.') + return (False, "apache execution module not loaded: apache not installed.") def check_mod_enabled(mod): - ''' + """ Checks to see if the specific apache mod is enabled. This will only be functional on operating systems that support @@ -40,15 +40,15 @@ def check_mod_enabled(mod): .. code-block:: bash salt '*' apache.check_mod_enabled status - ''' - if mod.endswith('.load') or mod.endswith('.conf'): + """ + if mod.endswith(".load") or mod.endswith(".conf"): mod_name = mod[:-5] else: mod_name = mod - cmd = 'a2enmod -l' + cmd = "a2enmod -l" try: - active_mods = __salt__['cmd.run'](cmd, python_shell=False).split(' ') + active_mods = __salt__["cmd.run"](cmd, python_shell=False).split(" ") except Exception as e: # pylint: disable=broad-except return e @@ -56,7 +56,7 @@ def check_mod_enabled(mod): def a2enmod(mod): - ''' + """ Runs a2enmod for the given mod. CLI Example: @@ -64,30 +64,30 @@ def a2enmod(mod): .. code-block:: bash salt '*' apache.a2enmod vhost_alias - ''' + """ ret = {} - command = ['a2enmod', mod] + command = ["a2enmod", mod] try: - status = __salt__['cmd.retcode'](command, python_shell=False) + status = __salt__["cmd.retcode"](command, python_shell=False) except Exception as e: # pylint: disable=broad-except return e - ret['Name'] = 'Apache2 Enable Mod' - ret['Mod'] = mod + ret["Name"] = "Apache2 Enable Mod" + ret["Mod"] = mod if status == 1: - ret['Status'] = 'Mod {0} Not found'.format(mod) + ret["Status"] = "Mod {0} Not found".format(mod) elif status == 0: - ret['Status'] = 'Mod {0} enabled'.format(mod) + ret["Status"] = "Mod {0} enabled".format(mod) else: - ret['Status'] = status + ret["Status"] = status return ret def a2dismod(mod): - ''' + """ Runs a2dismod for the given mod. CLI Example: @@ -95,23 +95,23 @@ def a2dismod(mod): .. code-block:: bash salt '*' apache.a2dismod vhost_alias - ''' + """ ret = {} - command = ['a2dismod', mod] + command = ["a2dismod", mod] try: - status = __salt__['cmd.retcode'](command, python_shell=False) + status = __salt__["cmd.retcode"](command, python_shell=False) except Exception as e: # pylint: disable=broad-except return e - ret['Name'] = 'Apache2 Disable Mod' - ret['Mod'] = mod + ret["Name"] = "Apache2 Disable Mod" + ret["Mod"] = mod if status == 256: - ret['Status'] = 'Mod {0} Not found'.format(mod) + ret["Status"] = "Mod {0} Not found".format(mod) elif status == 0: - ret['Status'] = 'Mod {0} disabled'.format(mod) + ret["Status"] = "Mod {0} disabled".format(mod) else: - ret['Status'] = status + ret["Status"] = status return ret diff --git a/salt/modules/svn.py b/salt/modules/svn.py index 58d64ab8afc..e3bde722c31 100644 --- a/salt/modules/svn.py +++ b/salt/modules/svn.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Subversion SCM -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import re @@ -17,18 +17,17 @@ _INI_RE = re.compile(r"^([^:]+):\s+(\S.*)$", re.M) def __virtual__(): - ''' + """ Only load if svn is installed - ''' - if salt.utils.path.which('svn') is None: - return (False, - 'The svn execution module cannot be loaded: svn unavailable.') + """ + if salt.utils.path.which("svn") is None: + return (False, "The svn execution module cannot be loaded: svn unavailable.") else: return True def _run_svn(cmd, cwd, user, username, password, opts, **kwargs): - ''' + """ Execute svn return the output of the command @@ -54,32 +53,29 @@ def _run_svn(cmd, cwd, user, username, password, opts, **kwargs): kwargs Additional options to pass to the run-cmd - ''' - cmd = ['svn', '--non-interactive', cmd] + """ + cmd = ["svn", "--non-interactive", cmd] options = list(opts) if username: - options.extend(['--username', username]) + options.extend(["--username", username]) if password: - options.extend(['--password', password]) + options.extend(["--password", password]) cmd.extend(options) - result = __salt__['cmd.run_all'](cmd, python_shell=False, cwd=cwd, runas=user, **kwargs) + result = __salt__["cmd.run_all"]( + cmd, python_shell=False, cwd=cwd, runas=user, **kwargs + ) - retcode = result['retcode'] + retcode = result["retcode"] if retcode == 0: - return result['stdout'] - raise CommandExecutionError(result['stderr'] + '\n\n' + ' '.join(cmd)) + return result["stdout"] + raise CommandExecutionError(result["stderr"] + "\n\n" + " ".join(cmd)) -def info(cwd, - targets=None, - user=None, - username=None, - password=None, - fmt='str'): - ''' +def info(cwd, targets=None, user=None, username=None, password=None, fmt="str"): + """ Display the Subversion information from the checkout. cwd @@ -109,35 +105,29 @@ def info(cwd, .. code-block:: bash salt '*' svn.info /path/to/svn/repo - ''' + """ opts = list() - if fmt == 'xml': - opts.append('--xml') + if fmt == "xml": + opts.append("--xml") if targets: opts += salt.utils.args.shlex_split(targets) - infos = _run_svn('info', cwd, user, username, password, opts) + infos = _run_svn("info", cwd, user, username, password, opts) - if fmt in ('str', 'xml'): + if fmt in ("str", "xml"): return infos info_list = [] - for infosplit in infos.split('\n\n'): + for infosplit in infos.split("\n\n"): info_list.append(_INI_RE.findall(infosplit)) - if fmt == 'list': + if fmt == "list": return info_list - if fmt == 'dict': + if fmt == "dict": return [dict(tmp) for tmp in info_list] -def checkout(cwd, - remote, - target=None, - user=None, - username=None, - password=None, - *opts): - ''' +def checkout(cwd, remote, target=None, user=None, username=None, password=None, *opts): + """ Download a working copy of the remote Subversion repository directory or file @@ -167,16 +157,15 @@ def checkout(cwd, .. code-block:: bash salt '*' svn.checkout /path/to/repo svn://remote/repo - ''' + """ opts += (remote,) if target: opts += (target,) - return _run_svn('checkout', cwd, user, username, password, opts) + return _run_svn("checkout", cwd, user, username, password, opts) -def switch(cwd, remote, target=None, user=None, username=None, - password=None, *opts): - ''' +def switch(cwd, remote, target=None, user=None, username=None, password=None, *opts): + """ .. versionadded:: 2014.1.0 Switch a working copy of a remote Subversion repository @@ -206,15 +195,15 @@ def switch(cwd, remote, target=None, user=None, username=None, .. code-block:: bash salt '*' svn.switch /path/to/repo svn://remote/repo - ''' + """ opts += (remote,) if target: opts += (target,) - return _run_svn('switch', cwd, user, username, password, opts) + return _run_svn("switch", cwd, user, username, password, opts) def update(cwd, targets=None, user=None, username=None, password=None, *opts): - ''' + """ Update the current directory, files, or directories from the remote Subversion repository @@ -241,14 +230,14 @@ def update(cwd, targets=None, user=None, username=None, password=None, *opts): .. code-block:: bash salt '*' svn.update /path/to/repo - ''' + """ if targets: opts += tuple(salt.utils.args.shlex_split(targets)) - return _run_svn('update', cwd, user, username, password, opts) + return _run_svn("update", cwd, user, username, password, opts) def diff(cwd, targets=None, user=None, username=None, password=None, *opts): - ''' + """ Return the diff of the current directory, files, or directories from the remote Subversion repository @@ -275,20 +264,14 @@ def diff(cwd, targets=None, user=None, username=None, password=None, *opts): .. code-block:: bash salt '*' svn.diff /path/to/repo - ''' + """ if targets: opts += tuple(salt.utils.args.shlex_split(targets)) - return _run_svn('diff', cwd, user, username, password, opts) + return _run_svn("diff", cwd, user, username, password, opts) -def commit(cwd, - targets=None, - msg=None, - user=None, - username=None, - password=None, - *opts): - ''' +def commit(cwd, targets=None, msg=None, user=None, username=None, password=None, *opts): + """ Commit the current directory, files, or directories to the remote Subversion repository @@ -318,16 +301,16 @@ def commit(cwd, .. code-block:: bash salt '*' svn.commit /path/to/repo - ''' + """ if msg: - opts += ('-m', msg) + opts += ("-m", msg) if targets: opts += tuple(salt.utils.args.shlex_split(targets)) - return _run_svn('commit', cwd, user, username, password, opts) + return _run_svn("commit", cwd, user, username, password, opts) def add(cwd, targets, user=None, username=None, password=None, *opts): - ''' + """ Add files to be tracked by the Subversion working-copy checkout cwd @@ -352,20 +335,14 @@ def add(cwd, targets, user=None, username=None, password=None, *opts): .. code-block:: bash salt '*' svn.add /path/to/repo /path/to/new/file - ''' + """ if targets: opts += tuple(salt.utils.args.shlex_split(targets)) - return _run_svn('add', cwd, user, username, password, opts) + return _run_svn("add", cwd, user, username, password, opts) -def remove(cwd, - targets, - msg=None, - user=None, - username=None, - password=None, - *opts): - ''' +def remove(cwd, targets, msg=None, user=None, username=None, password=None, *opts): + """ Remove files and directories from the Subversion repository cwd @@ -393,16 +370,16 @@ def remove(cwd, .. code-block:: bash salt '*' svn.remove /path/to/repo /path/to/repo/remove - ''' + """ if msg: - opts += ('-m', msg) + opts += ("-m", msg) if targets: opts += tuple(salt.utils.args.shlex_split(targets)) - return _run_svn('remove', cwd, user, username, password, opts) + return _run_svn("remove", cwd, user, username, password, opts) def status(cwd, targets=None, user=None, username=None, password=None, *opts): - ''' + """ Display the status of the current directory, files, or directories in the Subversion repository @@ -429,21 +406,23 @@ def status(cwd, targets=None, user=None, username=None, password=None, *opts): .. code-block:: bash salt '*' svn.status /path/to/repo - ''' + """ if targets: opts += tuple(salt.utils.args.shlex_split(targets)) - return _run_svn('status', cwd, user, username, password, opts) + return _run_svn("status", cwd, user, username, password, opts) -def export(cwd, - remote, - target=None, - user=None, - username=None, - password=None, - revision='HEAD', - *opts): - ''' +def export( + cwd, + remote, + target=None, + user=None, + username=None, + password=None, + revision="HEAD", + *opts +): + """ Create an unversioned copy of a tree. cwd @@ -472,10 +451,13 @@ def export(cwd, .. code-block:: bash salt '*' svn.export /path/to/repo svn://remote/repo - ''' + """ opts += (remote,) if target: opts += (target,) - revision_args = '-r' - opts += (revision_args, six.text_type(revision),) - return _run_svn('export', cwd, user, username, password, opts) + revision_args = "-r" + opts += ( + revision_args, + six.text_type(revision), + ) + return _run_svn("export", cwd, user, username, password, opts) diff --git a/salt/modules/swarm.py b/salt/modules/swarm.py index ea327ce6400..5829c52781c 100644 --- a/salt/modules/swarm.py +++ b/salt/modules/swarm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Docker Swarm Module using Docker's Python SDK ============================================= @@ -23,39 +23,43 @@ Docker Python SDK pip install -U docker More information: https://docker-py.readthedocs.io/en/stable/ -''' +""" # Import python libraries -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.json try: import docker + HAS_DOCKER = True except ImportError: HAS_DOCKER = False -__virtualname__ = 'swarm' +__virtualname__ = "swarm" def __virtual__(): - ''' + """ Load this module if the docker python module is installed - ''' + """ if HAS_DOCKER: return __virtualname__ - return False, 'The swarm module failed to load: Docker python module is not available.' + return ( + False, + "The swarm module failed to load: Docker python module is not available.", + ) def __init__(self): if HAS_DOCKER: - __context__['client'] = docker.from_env() - __context__['server_name'] = __grains__['id'] + __context__["client"] = docker.from_env() + __context__["server_name"] = __grains__["id"] def swarm_tokens(): - ''' + """ Get the Docker Swarm Manager or Worker join tokens CLI Example: @@ -63,16 +67,14 @@ def swarm_tokens(): .. code-block:: bash salt '*' swarm.swarm_tokens - ''' - client = docker.APIClient(base_url='unix://var/run/docker.sock') + """ + client = docker.APIClient(base_url="unix://var/run/docker.sock") service = client.inspect_swarm() - return service['JoinTokens'] + return service["JoinTokens"] -def swarm_init(advertise_addr=str, - listen_addr=int, - force_new_cluster=bool): - ''' +def swarm_init(advertise_addr=str, listen_addr=int, force_new_cluster=bool): + """ Initalize Docker on Minion as a Swarm Manager advertise_addr @@ -95,27 +97,30 @@ def swarm_init(advertise_addr=str, .. code-block:: bash salt '*' swarm.swarm_init advertise_addr='192.168.50.10' listen_addr='0.0.0.0' force_new_cluster=False - ''' + """ try: salt_return = {} - __context__['client'].swarm.init(advertise_addr, - listen_addr, - force_new_cluster) - output = 'Docker swarm has been initialized on {0} ' \ - 'and the worker/manager Join token is below'.format(__context__['server_name']) - salt_return.update({'Comment': output, - 'Tokens': swarm_tokens()}) + __context__["client"].swarm.init(advertise_addr, listen_addr, force_new_cluster) + output = ( + "Docker swarm has been initialized on {0} " + "and the worker/manager Join token is below".format( + __context__["server_name"] + ) + ) + salt_return.update({"Comment": output, "Tokens": swarm_tokens()}) except TypeError: salt_return = {} - salt_return.update({'Error': 'Please make sure you are passing advertise_addr, ' - 'listen_addr and force_new_cluster correctly.'}) + salt_return.update( + { + "Error": "Please make sure you are passing advertise_addr, " + "listen_addr and force_new_cluster correctly." + } + ) return salt_return -def joinswarm(remote_addr=int, - listen_addr=int, - token=str): - ''' +def joinswarm(remote_addr=int, listen_addr=int, token=str): + """ Join a Swarm Worker to the cluster remote_addr @@ -135,23 +140,27 @@ def joinswarm(remote_addr=int, salt '*' swarm.joinswarm remote_addr=192.168.50.10 listen_addr='0.0.0.0' \ token='SWMTKN-1-64tux2g0701r84ofq93zppcih0pe081akq45owe9ts61f30x4t-06trjugdu7x2z47j938s54il' - ''' + """ try: salt_return = {} - __context__['client'].swarm.join(remote_addrs=[remote_addr], - listen_addr=listen_addr, - join_token=token) - output = __context__['server_name'] + ' has joined the Swarm' - salt_return.update({'Comment': output, 'Manager_Addr': remote_addr}) + __context__["client"].swarm.join( + remote_addrs=[remote_addr], listen_addr=listen_addr, join_token=token + ) + output = __context__["server_name"] + " has joined the Swarm" + salt_return.update({"Comment": output, "Manager_Addr": remote_addr}) except TypeError: salt_return = {} - salt_return.update({'Error': 'Please make sure this minion is not part of a swarm and you are ' - 'passing remote_addr, listen_addr and token correctly.'}) + salt_return.update( + { + "Error": "Please make sure this minion is not part of a swarm and you are " + "passing remote_addr, listen_addr and token correctly." + } + ) return salt_return def leave_swarm(force=bool): - ''' + """ Force the minion to leave the swarm force @@ -162,22 +171,24 @@ def leave_swarm(force=bool): .. code-block:: bash salt '*' swarm.leave_swarm force=False - ''' + """ salt_return = {} - __context__['client'].swarm.leave(force=force) - output = __context__['server_name'] + ' has left the swarm' - salt_return.update({'Comment': output}) + __context__["client"].swarm.leave(force=force) + output = __context__["server_name"] + " has left the swarm" + salt_return.update({"Comment": output}) return salt_return -def service_create(image=str, - name=str, - command=str, - hostname=str, - replicas=int, - target_port=int, - published_port=int): - ''' +def service_create( + image=str, + name=str, + command=str, + hostname=str, + replicas=int, + target_port=int, + published_port=int, +): + """ Create Docker Swarm Service Create image @@ -207,35 +218,49 @@ def service_create(image=str, salt '*' swarm.service_create image=httpd name=Test_Service \ command=None hostname=salthttpd replicas=6 target_port=80 published_port=80 - ''' + """ try: salt_return = {} - replica_mode = docker.types.ServiceMode('replicated', replicas=replicas) + replica_mode = docker.types.ServiceMode("replicated", replicas=replicas) ports = docker.types.EndpointSpec(ports={target_port: published_port}) - __context__['client'].services.create(name=name, - image=image, - command=command, - mode=replica_mode, - endpoint_spec=ports) - echoback = __context__['server_name'] + ' has a Docker Swarm Service running named ' + name - salt_return.update({'Info': echoback, - 'Minion': __context__['server_name'], - 'Name': name, - 'Image': image, - 'Command': command, - 'Hostname': hostname, - 'Replicas': replicas, - 'Target_Port': target_port, - 'Published_Port': published_port}) + __context__["client"].services.create( + name=name, + image=image, + command=command, + mode=replica_mode, + endpoint_spec=ports, + ) + echoback = ( + __context__["server_name"] + + " has a Docker Swarm Service running named " + + name + ) + salt_return.update( + { + "Info": echoback, + "Minion": __context__["server_name"], + "Name": name, + "Image": image, + "Command": command, + "Hostname": hostname, + "Replicas": replicas, + "Target_Port": target_port, + "Published_Port": published_port, + } + ) except TypeError: salt_return = {} - salt_return.update({'Error': 'Please make sure you are passing arguments correctly ' - '[image, name, command, hostname, replicas, target_port and published_port]'}) + salt_return.update( + { + "Error": "Please make sure you are passing arguments correctly " + "[image, name, command, hostname, replicas, target_port and published_port]" + } + ) return salt_return def swarm_service_info(service_name=str): - ''' + """ Swarm Service Information service_name @@ -246,51 +271,55 @@ def swarm_service_info(service_name=str): .. code-block:: bash salt '*' swarm.swarm_service_info service_name=Test_Service - ''' + """ try: salt_return = {} - client = docker.APIClient(base_url='unix://var/run/docker.sock') + client = docker.APIClient(base_url="unix://var/run/docker.sock") service = client.inspect_service(service=service_name) getdata = salt.utils.json.dumps(service) dump = salt.utils.json.loads(getdata) - version = dump['Version']['Index'] - name = dump['Spec']['Name'] - network_mode = dump['Spec']['EndpointSpec']['Mode'] - ports = dump['Spec']['EndpointSpec']['Ports'] - swarm_id = dump['ID'] - create_date = dump['CreatedAt'] - update_date = dump['UpdatedAt'] - labels = dump['Spec']['Labels'] - replicas = dump['Spec']['Mode']['Replicated']['Replicas'] - network = dump['Endpoint']['VirtualIPs'] - image = dump['Spec']['TaskTemplate']['ContainerSpec']['Image'] + version = dump["Version"]["Index"] + name = dump["Spec"]["Name"] + network_mode = dump["Spec"]["EndpointSpec"]["Mode"] + ports = dump["Spec"]["EndpointSpec"]["Ports"] + swarm_id = dump["ID"] + create_date = dump["CreatedAt"] + update_date = dump["UpdatedAt"] + labels = dump["Spec"]["Labels"] + replicas = dump["Spec"]["Mode"]["Replicated"]["Replicas"] + network = dump["Endpoint"]["VirtualIPs"] + image = dump["Spec"]["TaskTemplate"]["ContainerSpec"]["Image"] for items in ports: - published_port = items['PublishedPort'] - target_port = items['TargetPort'] - published_mode = items['PublishMode'] - protocol = items['Protocol'] - salt_return.update({'Service Name': name, - 'Replicas': replicas, - 'Service ID': swarm_id, - 'Network': network, - 'Network Mode': network_mode, - 'Creation Date': create_date, - 'Update Date': update_date, - 'Published Port': published_port, - 'Target Port': target_port, - 'Published Mode': published_mode, - 'Protocol': protocol, - 'Docker Image': image, - 'Minion Id': __context__['server_name'], - 'Version': version}) + published_port = items["PublishedPort"] + target_port = items["TargetPort"] + published_mode = items["PublishMode"] + protocol = items["Protocol"] + salt_return.update( + { + "Service Name": name, + "Replicas": replicas, + "Service ID": swarm_id, + "Network": network, + "Network Mode": network_mode, + "Creation Date": create_date, + "Update Date": update_date, + "Published Port": published_port, + "Target Port": target_port, + "Published Mode": published_mode, + "Protocol": protocol, + "Docker Image": image, + "Minion Id": __context__["server_name"], + "Version": version, + } + ) except TypeError: salt_return = {} - salt_return.update({'Error': 'service_name arg is missing?'}) + salt_return.update({"Error": "service_name arg is missing?"}) return salt_return def remove_service(service=str): - ''' + """ Remove Swarm Service service @@ -301,21 +330,22 @@ def remove_service(service=str): .. code-block:: bash salt '*' swarm.remove_service service=Test_Service - ''' + """ try: salt_return = {} - client = docker.APIClient(base_url='unix://var/run/docker.sock') + client = docker.APIClient(base_url="unix://var/run/docker.sock") service = client.remove_service(service) - salt_return.update({'Service Deleted': service, - 'Minion ID': __context__['server_name']}) + salt_return.update( + {"Service Deleted": service, "Minion ID": __context__["server_name"]} + ) except TypeError: salt_return = {} - salt_return.update({'Error': 'service arg is missing?'}) + salt_return.update({"Error": "service arg is missing?"}) return salt_return def node_ls(server=str): - ''' + """ Displays Information about Swarm Nodes with passing in the server server @@ -326,38 +356,44 @@ def node_ls(server=str): .. code-block:: bash salt '*' swarm.node_ls server=minion1 - ''' + """ try: salt_return = {} - client = docker.APIClient(base_url='unix://var/run/docker.sock') - service = client.nodes(filters=({'name': server})) + client = docker.APIClient(base_url="unix://var/run/docker.sock") + service = client.nodes(filters=({"name": server})) getdata = salt.utils.json.dumps(service) dump = salt.utils.json.loads(getdata) for items in dump: - docker_version = items['Description']['Engine']['EngineVersion'] - platform = items['Description']['Platform'] - hostnames = items['Description']['Hostname'] - ids = items['ID'] - role = items['Spec']['Role'] - availability = items['Spec']['Availability'] - status = items['Status'] - version = items['Version']['Index'] - salt_return.update({'Docker Version': docker_version, - 'Platform': platform, - 'Hostname': hostnames, - 'ID': ids, - 'Roles': role, - 'Availability': availability, - 'Status': status, - 'Version': version}) + docker_version = items["Description"]["Engine"]["EngineVersion"] + platform = items["Description"]["Platform"] + hostnames = items["Description"]["Hostname"] + ids = items["ID"] + role = items["Spec"]["Role"] + availability = items["Spec"]["Availability"] + status = items["Status"] + version = items["Version"]["Index"] + salt_return.update( + { + "Docker Version": docker_version, + "Platform": platform, + "Hostname": hostnames, + "ID": ids, + "Roles": role, + "Availability": availability, + "Status": status, + "Version": version, + } + ) except TypeError: salt_return = {} - salt_return.update({'Error': 'The server arg is missing or you not targeting a Manager node?'}) + salt_return.update( + {"Error": "The server arg is missing or you not targeting a Manager node?"} + ) return salt_return def remove_node(node_id=str, force=bool): - ''' + """ Remove a node from a swarm and the target needs to be a swarm manager node_id @@ -371,10 +407,10 @@ def remove_node(node_id=str, force=bool): .. code-block:: bash salt '*' swarm.remove_node node_id=z4gjbe9rwmqahc2a91snvolm5 force=false - ''' - client = docker.APIClient(base_url='unix://var/run/docker.sock') + """ + client = docker.APIClient(base_url="unix://var/run/docker.sock") try: - if force == 'True': + if force == "True": service = client.remove_node(node_id, force=True) return service else: @@ -382,16 +418,12 @@ def remove_node(node_id=str, force=bool): return service except TypeError: salt_return = {} - salt_return.update({'Error': 'Is the node_id and/or force=True/False missing?'}) + salt_return.update({"Error": "Is the node_id and/or force=True/False missing?"}) return salt_return -def update_node(availability=str, - node_name=str, - role=str, - node_id=str, - version=int): - ''' +def update_node(availability=str, node_name=str, role=str, node_id=str, version=int): + """ Updates docker swarm nodes/needs to target a manager node/minion availability @@ -415,18 +447,18 @@ def update_node(availability=str, salt '*' docker_util.update_node availability=drain node_name=minion2 \ role=worker node_id=3k9x7t8m4pel9c0nqr3iajnzp version=19 - ''' - client = docker.APIClient(base_url='unix://var/run/docker.sock') + """ + client = docker.APIClient(base_url="unix://var/run/docker.sock") try: salt_return = {} - node_spec = {'Availability': availability, - 'Name': node_name, - 'Role': role} - client.update_node(node_id=node_id, - version=version, - node_spec=node_spec) - salt_return.update({'Node Information': node_spec}) + node_spec = {"Availability": availability, "Name": node_name, "Role": role} + client.update_node(node_id=node_id, version=version, node_spec=node_spec) + salt_return.update({"Node Information": node_spec}) except TypeError: salt_return = {} - salt_return.update({'Error': 'Make sure all args are passed [availability, node_name, role, node_id, version]'}) + salt_return.update( + { + "Error": "Make sure all args are passed [availability, node_name, role, node_id, version]" + } + ) return salt_return diff --git a/salt/modules/swift.py b/salt/modules/swift.py index 25436ba8ebb..ef50870831a 100644 --- a/salt/modules/swift.py +++ b/salt/modules/swift.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for handling OpenStack Swift calls Author: Anthony Stanton <anthony.stanton@gmail.com> @@ -44,8 +44,8 @@ Inspired by the S3 and Nova modules salt '*' swift.get mycontainer myfile /tmp/file profile=openstack1 NOTE: For Rackspace cloud files setting keystone.auth_version = 1 is recommended. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -58,10 +58,10 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if swift is installed on this minion. - ''' + """ return suos.check_swift() @@ -69,43 +69,43 @@ __opts__ = {} def _auth(profile=None): - ''' + """ Set up openstack credentials - ''' + """ if profile: - credentials = __salt__['config.option'](profile) - user = credentials['keystone.user'] - password = credentials.get('keystone.password', None) - tenant = credentials['keystone.tenant'] - auth_url = credentials['keystone.auth_url'] - auth_version = credentials.get('keystone.auth_version', 2) - region_name = credentials.get('keystone.region_name', None) - api_key = credentials.get('keystone.api_key', None) - os_auth_system = credentials.get('keystone.os_auth_system', None) + credentials = __salt__["config.option"](profile) + user = credentials["keystone.user"] + password = credentials.get("keystone.password", None) + tenant = credentials["keystone.tenant"] + auth_url = credentials["keystone.auth_url"] + auth_version = credentials.get("keystone.auth_version", 2) + region_name = credentials.get("keystone.region_name", None) + api_key = credentials.get("keystone.api_key", None) + os_auth_system = credentials.get("keystone.os_auth_system", None) else: - user = __salt__['config.option']('keystone.user') - password = __salt__['config.option']('keystone.password', None) - tenant = __salt__['config.option']('keystone.tenant') - auth_url = __salt__['config.option']('keystone.auth_url') - auth_version = __salt__['config.option']('keystone.auth_version', 2) - region_name = __salt__['config.option']('keystone.region_name') - api_key = __salt__['config.option']('keystone.api_key') - os_auth_system = __salt__['config.option']('keystone.os_auth_system') + user = __salt__["config.option"]("keystone.user") + password = __salt__["config.option"]("keystone.password", None) + tenant = __salt__["config.option"]("keystone.tenant") + auth_url = __salt__["config.option"]("keystone.auth_url") + auth_version = __salt__["config.option"]("keystone.auth_version", 2) + region_name = __salt__["config.option"]("keystone.region_name") + api_key = __salt__["config.option"]("keystone.api_key") + os_auth_system = __salt__["config.option"]("keystone.os_auth_system") kwargs = { - 'user': user, - 'password': password, - 'key': api_key, - 'tenant_name': tenant, - 'auth_url': auth_url, - 'auth_version': auth_version, - 'region_name': region_name + "user": user, + "password": password, + "key": api_key, + "tenant_name": tenant, + "auth_url": auth_url, + "auth_version": auth_version, + "region_name": region_name, } return suos.SaltSwift(**kwargs) def delete(cont, path=None, profile=None): - ''' + """ Delete a container, or delete an object from a container. CLI Example to delete a container:: @@ -115,7 +115,7 @@ def delete(cont, path=None, profile=None): CLI Example to delete an object from a container:: salt myminion swift.delete mycontainer remoteobject - ''' + """ swift_conn = _auth(profile) if path is None: @@ -125,7 +125,7 @@ def delete(cont, path=None, profile=None): def get(cont=None, path=None, local_file=None, return_bin=False, profile=None): - ''' + """ List the contents of a container, or return an object from a container. Set return_bin to True in order to retrieve an object wholesale. Otherwise, Salt will attempt to parse an XML response. @@ -154,7 +154,7 @@ def get(cont=None, path=None, local_file=None, return_bin=False, profile=None): salt myminion swift.get mycontainer myfile.png local_file=/tmp/myfile.png - ''' + """ swift_conn = _auth(profile) if cont is None: @@ -177,7 +177,7 @@ def head(): def put(cont, path=None, local_file=None, profile=None): - ''' + """ Create a new container, or upload an object to a container. CLI Example to create a container: @@ -191,7 +191,7 @@ def put(cont, path=None, local_file=None, profile=None): .. code-block:: bash salt myminion swift.put mycontainer remotepath local_file=/path/to/file - ''' + """ swift_conn = _auth(profile) if path is None: diff --git a/salt/modules/sysbench.py b/salt/modules/sysbench.py index 1227f704776..473a0f84c46 100644 --- a/salt/modules/sysbench.py +++ b/salt/modules/sysbench.py @@ -1,39 +1,43 @@ # -*- coding: utf-8 -*- -''' +""" The 'sysbench' module is used to analyze the performance of the minions, right from the master! It measures various system parameters such as CPU, Memory, File I/O, Threads and Mutex. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals import re + import salt.utils.path from salt.ext.six.moves import zip def __virtual__(): - ''' + """ loads the module, if only sysbench is installed - ''' + """ # finding the path of the binary - if salt.utils.path.which('sysbench'): - return 'sysbench' - return (False, 'The sysbench execution module failed to load: the sysbench binary is not in the path.') + if salt.utils.path.which("sysbench"): + return "sysbench" + return ( + False, + "The sysbench execution module failed to load: the sysbench binary is not in the path.", + ) def _parser(result): - ''' + """ parses the output into a dictionary - ''' + """ # regexes to match - _total_time = re.compile(r'total time:\s*(\d*.\d*s)') - _total_execution = re.compile(r'event execution:\s*(\d*.\d*s?)') - _min_response_time = re.compile(r'min:\s*(\d*.\d*ms)') - _max_response_time = re.compile(r'max:\s*(\d*.\d*ms)') - _avg_response_time = re.compile(r'avg:\s*(\d*.\d*ms)') - _per_response_time = re.compile(r'95 percentile:\s*(\d*.\d*ms)') + _total_time = re.compile(r"total time:\s*(\d*.\d*s)") + _total_execution = re.compile(r"event execution:\s*(\d*.\d*s?)") + _min_response_time = re.compile(r"min:\s*(\d*.\d*ms)") + _max_response_time = re.compile(r"max:\s*(\d*.\d*ms)") + _avg_response_time = re.compile(r"avg:\s*(\d*.\d*ms)") + _per_response_time = re.compile(r"95 percentile:\s*(\d*.\d*ms)") # extracting data total_time = re.search(_total_time, result).group(1) @@ -47,17 +51,17 @@ def _parser(result): # returning the data as dictionary return { - 'total time': total_time, - 'total execution time': total_execution, - 'minimum response time': min_response_time, - 'maximum response time': max_response_time, - 'average response time': avg_response_time, - '95 percentile': per_response_time + "total time": total_time, + "total execution time": total_execution, + "minimum response time": min_response_time, + "maximum response time": max_response_time, + "average response time": avg_response_time, + "95 percentile": per_response_time, } def cpu(): - ''' + """ Tests for the CPU performance of minions. CLI Examples: @@ -65,28 +69,28 @@ def cpu(): .. code-block:: bash salt '*' sysbench.cpu - ''' + """ # Test data max_primes = [500, 1000, 2500, 5000] # Initializing the test variables - test_command = 'sysbench --test=cpu --cpu-max-prime={0} run' + test_command = "sysbench --test=cpu --cpu-max-prime={0} run" result = None ret_val = {} # Test beings! for primes in max_primes: - key = 'Prime numbers limit: {0}'.format(primes) + key = "Prime numbers limit: {0}".format(primes) run_command = test_command.format(primes) - result = __salt__['cmd.run'](run_command) + result = __salt__["cmd.run"](run_command) ret_val[key] = _parser(result) return ret_val def threads(): - ''' + """ This tests the performance of the processor's scheduler CLI Example: @@ -94,30 +98,30 @@ def threads(): .. code-block:: bash salt '*' sysbench.threads - ''' + """ # Test data thread_yields = [100, 200, 500, 1000] thread_locks = [2, 4, 8, 16] # Initializing the test variables - test_command = 'sysbench --num-threads=64 --test=threads ' - test_command += '--thread-yields={0} --thread-locks={1} run ' + test_command = "sysbench --num-threads=64 --test=threads " + test_command += "--thread-yields={0} --thread-locks={1} run " result = None ret_val = {} # Test begins! for yields, locks in zip(thread_yields, thread_locks): - key = 'Yields: {0} Locks: {1}'.format(yields, locks) + key = "Yields: {0} Locks: {1}".format(yields, locks) run_command = test_command.format(yields, locks) - result = __salt__['cmd.run'](run_command) + result = __salt__["cmd.run"](run_command) ret_val[key] = _parser(result) return ret_val def mutex(): - ''' + """ Tests the implementation of mutex CLI Examples: @@ -125,7 +129,7 @@ def mutex(): .. code-block:: bash salt '*' sysbench.mutex - ''' + """ # Test options and the values they take # --mutex-num = [50,500,1000] @@ -140,23 +144,23 @@ def mutex(): mutex_loops = [2500, 5000, 10000, 10000, 2500, 5000, 5000, 10000, 2500] # Initializing the test variables - test_command = 'sysbench --num-threads=250 --test=mutex ' - test_command += '--mutex-num={0} --mutex-locks={1} --mutex-loops={2} run ' + test_command = "sysbench --num-threads=250 --test=mutex " + test_command += "--mutex-num={0} --mutex-locks={1} --mutex-loops={2} run " result = None ret_val = {} # Test begins! for num, locks, loops in zip(mutex_num, mutex_locks, mutex_loops): - key = 'Mutex: {0} Locks: {1} Loops: {2}'.format(num, locks, loops) + key = "Mutex: {0} Locks: {1} Loops: {2}".format(num, locks, loops) run_command = test_command.format(num, locks, loops) - result = __salt__['cmd.run'](run_command) + result = __salt__["cmd.run"](run_command) ret_val[key] = _parser(result) return ret_val def memory(): - ''' + """ This tests the memory for read and write operations. CLI Examples: @@ -164,7 +168,7 @@ def memory(): .. code-block:: bash salt '*' sysbench.memory - ''' + """ # test defaults # --memory-block-size = 10M @@ -172,29 +176,29 @@ def memory(): # We test memory read / write against global / local scope of memory # Test data - memory_oper = ['read', 'write'] - memory_scope = ['local', 'global'] + memory_oper = ["read", "write"] + memory_scope = ["local", "global"] # Initializing the test variables - test_command = 'sysbench --num-threads=64 --test=memory ' - test_command += '--memory-oper={0} --memory-scope={1} ' - test_command += '--memory-block-size=1K --memory-total-size=32G run ' + test_command = "sysbench --num-threads=64 --test=memory " + test_command += "--memory-oper={0} --memory-scope={1} " + test_command += "--memory-block-size=1K --memory-total-size=32G run " result = None ret_val = {} # Test begins! for oper in memory_oper: for scope in memory_scope: - key = 'Operation: {0} Scope: {1}'.format(oper, scope) + key = "Operation: {0} Scope: {1}".format(oper, scope) run_command = test_command.format(oper, scope) - result = __salt__['cmd.run'](run_command) + result = __salt__["cmd.run"](run_command) ret_val[key] = _parser(result) return ret_val def fileio(): - ''' + """ This tests for the file read and write operations Various modes of operations are @@ -213,33 +217,33 @@ def fileio(): .. code-block:: bash salt '*' sysbench.fileio - ''' + """ # Test data - test_modes = ['seqwr', 'seqrewr', 'seqrd', 'rndrd', 'rndwr', 'rndrw'] + test_modes = ["seqwr", "seqrewr", "seqrd", "rndrd", "rndwr", "rndrw"] # Initializing the required variables - test_command = 'sysbench --num-threads=16 --test=fileio ' - test_command += '--file-num=32 --file-total-size=1G --file-test-mode={0} ' + test_command = "sysbench --num-threads=16 --test=fileio " + test_command += "--file-num=32 --file-total-size=1G --file-test-mode={0} " result = None ret_val = {} # Test begins! for mode in test_modes: - key = 'Mode: {0}'.format(mode) + key = "Mode: {0}".format(mode) # Prepare phase - run_command = (test_command + 'prepare').format(mode) - __salt__['cmd.run'](run_command) + run_command = (test_command + "prepare").format(mode) + __salt__["cmd.run"](run_command) # Test phase - run_command = (test_command + 'run').format(mode) - result = __salt__['cmd.run'](run_command) + run_command = (test_command + "run").format(mode) + result = __salt__["cmd.run"](run_command) ret_val[key] = _parser(result) # Clean up phase - run_command = (test_command + 'cleanup').format(mode) - __salt__['cmd.run'](run_command) + run_command = (test_command + "cleanup").format(mode) + __salt__["cmd.run"](run_command) return ret_val diff --git a/salt/modules/sysfs.py b/salt/modules/sysfs.py index b10a8623346..1c1e38f8e5b 100644 --- a/salt/modules/sysfs.py +++ b/salt/modules/sysfs.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Module for interfacing with SysFS .. seealso:: https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt .. versionadded:: 2016.3.0 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import os import stat @@ -23,14 +24,14 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work on Linux - ''' + """ return salt.utils.platform.is_linux() def attr(key, value=None): - ''' + """ Access/write a SysFS attribute. If the attribute is a symlink, its destination is returned @@ -40,7 +41,7 @@ def attr(key, value=None): .. code-block:: bash salt '*' sysfs.attr block/sda/queue/logical_block_size - ''' + """ key = target(key) if key is False: @@ -54,28 +55,26 @@ def attr(key, value=None): def write(key, value): - ''' + """ Write a SysFS attribute/action CLI example: .. code-block:: bash salt '*' sysfs.write devices/system/cpu/cpu0/cpufreq/scaling_governor 'performance' - ''' + """ try: key = target(key) - log.trace('Writing %s to %s', value, key) - with salt.utils.files.fopen(key, 'w') as twriter: - twriter.write( - salt.utils.stringutils.to_str('{0}\n'.format(value)) - ) + log.trace("Writing %s to %s", value, key) + with salt.utils.files.fopen(key, "w") as twriter: + twriter.write(salt.utils.stringutils.to_str("{0}\n".format(value))) return True except Exception: # pylint: disable=broad-except return False -def read(key, root=''): - ''' +def read(key, root=""): + """ Read from SysFS :param key: file or path in SysFS; if key is a list then root will be prefixed on each key @@ -86,7 +85,7 @@ def read(key, root=''): .. code-block:: bash salt '*' sysfs.read class/net/em1/statistics - ''' + """ if not isinstance(key, six.string_types): res = {} @@ -102,10 +101,10 @@ def read(key, root=''): elif os.path.isdir(key): keys = interfaces(key) result = {} - for subkey in keys['r'] + keys['rw']: + for subkey in keys["r"] + keys["rw"]: subval = read(os.path.join(key, subkey)) if subval is not False: - subkeys = subkey.split('/') + subkeys = subkey.split("/") subkey = subkeys.pop() subresult = result if subkeys: @@ -117,7 +116,7 @@ def read(key, root=''): return result else: try: - log.trace('Reading %s...', key) + log.trace("Reading %s...", key) # Certain things in SysFS are pipes 'n such. # This opens it non-blocking, which prevents indefinite blocking @@ -140,7 +139,7 @@ def read(key, root=''): def target(key, full=True): - ''' + """ Return the basename of a SysFS key path :param key: the location to resolve within SysFS @@ -153,13 +152,13 @@ def target(key, full=True): salt '*' sysfs.read class/ttyS0 - ''' - if not key.startswith('/sys'): - key = os.path.join('/sys', key) + """ + if not key.startswith("/sys"): + key = os.path.join("/sys", key) key = os.path.realpath(key) if not os.path.exists(key): - log.debug('Unknown SysFS key %s', key) + log.debug("Unknown SysFS key %s", key) return False elif full: return key @@ -168,7 +167,7 @@ def target(key, full=True): def interfaces(root): - ''' + """ Generate a dictionary with all available interfaces relative to root. Symlinks are not followed. @@ -223,11 +222,11 @@ def interfaces(root): * 'r' interfaces are read-only * 'w' interfaces are write-only (e.g. actions) * 'rw' are interfaces that can both be read or written - ''' + """ root = target(root) if root is False or not os.path.isdir(root): - log.error('SysFS %s not a dir', root) + log.error("SysFS %s not a dir", root) return False readwrites = [] @@ -254,10 +253,6 @@ def interfaces(root): elif is_r: reads.append(relpath) else: - log.warning('Unable to find any interfaces in %s', canpath) + log.warning("Unable to find any interfaces in %s", canpath) - return { - 'r': reads, - 'w': writes, - 'rw': readwrites - } + return {"r": reads, "w": writes, "rw": readwrites} diff --git a/salt/modules/syslog_ng.py b/salt/modules/syslog_ng.py index 37d345c4fd3..05b4af25160 100644 --- a/salt/modules/syslog_ng.py +++ b/salt/modules/syslog_ng.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for getting information about syslog-ng :maintainer: Tibor Benke <btibi@sch.bme.hu> @@ -23,16 +23,23 @@ the module will use it. If it is not set, syslog-ng uses the default configuration file. -''' +""" # Import Python libs -from __future__ import absolute_import, generators, with_statement, \ - unicode_literals, print_function -import time +from __future__ import ( + absolute_import, + generators, + print_function, + unicode_literals, + with_statement, +) + import logging -import salt import os import os.path +import time + +import salt # Import Salt libs import salt.utils.files @@ -41,12 +48,11 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin - +from salt.ext.six.moves import range __SYSLOG_NG_BINARY_PATH = None -__SYSLOG_NG_CONFIG_FILE = '/etc/syslog-ng.conf' -__SALT_GENERATED_CONFIG_HEADER = '''#Generated by Salt on {0}''' +__SYSLOG_NG_CONFIG_FILE = "/etc/syslog-ng.conf" +__SALT_GENERATED_CONFIG_HEADER = """#Generated by Salt on {0}""" class SyslogNgError(Exception): @@ -57,12 +63,10 @@ log = logging.getLogger(__name__) log.setLevel(logging.DEBUG) # Don't shadow built-in's. -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} -_INDENT = '' -_INDENT_STEP = ' ' +_INDENT = "" +_INDENT_STEP = " " # These are needed during building of the configuration tree _current_statement = None @@ -72,83 +76,83 @@ _current_parameter_value = None def _increase_indent(): - ''' + """ Increases the indentation level. - ''' + """ global _INDENT _INDENT += _INDENT_STEP def _decrease_indent(): - ''' + """ Decreases the indentation level. - ''' + """ global _INDENT _INDENT = _INDENT[4:] def _indent(value): - ''' + """ Returns the indented parameter. - ''' - return '{0}{1}'.format(_INDENT, value) + """ + return "{0}{1}".format(_INDENT, value) def _indentln(string): - ''' + """ Return the indented parameter with newline. - ''' - return _indent(string + '\n') + """ + return _indent(string + "\n") class Buildable(object): - ''' + """ Base class of most classes, which have a build method. It contains a common build function. Does not need examples. - ''' + """ - def __init__(self, iterable, join_body_on='', append_extra_newline=True): + def __init__(self, iterable, join_body_on="", append_extra_newline=True): self.iterable = iterable self.join_body_on = join_body_on self.append_extra_newline = append_extra_newline def build_header(self): - ''' + """ Builds the header of a syslog-ng configuration object. - ''' - return '' + """ + return "" def build_tail(self): - ''' + """ Builds the tail of a syslog-ng configuration object. - ''' - return '' + """ + return "" def build_body(self): - ''' + """ Builds the body of a syslog-ng configuration object. - ''' + """ _increase_indent() body_array = [x.build() for x in self.iterable] - nl = '\n' if self.append_extra_newline else '' + nl = "\n" if self.append_extra_newline else "" if len(self.iterable) >= 1: body = self.join_body_on.join(body_array) + nl else: - body = '' + body = "" _decrease_indent() return body def build(self): - ''' + """ Builds the textual representation of the whole configuration object with its children. - ''' + """ header = self.build_header() body = self.build_body() tail = self.build_tail() @@ -156,17 +160,17 @@ class Buildable(object): class Statement(Buildable): - ''' + """ It represents a syslog-ng configuration statement, e.g. source, destination, filter. Does not need examples. - ''' + """ - def __init__(self, type, id='', options=None, has_name=True): - super(Statement, self).__init__(options, - join_body_on='', - append_extra_newline=False) + def __init__(self, type, id="", options=None, has_name=True): + super(Statement, self).__init__( + options, join_body_on="", append_extra_newline=False + ) self.type = type self.id = id self.options = options if options else [] @@ -175,49 +179,49 @@ class Statement(Buildable): def build_header(self): if self.has_name: - return _indentln('{0} {1} {{'.format(self.type, self.id)) + return _indentln("{0} {1} {{".format(self.type, self.id)) else: - return _indentln('{0} {{'.format(self.type)) + return _indentln("{0} {{".format(self.type)) def build_tail(self): - return _indentln('};') + return _indentln("};") def add_child(self, option): self.options.append(option) class NamedStatement(Statement): - ''' + """ It represents a configuration statement, which has a name, e.g. a source. Does not need examples. - ''' - def __init__(self, type, id='', options=None): + """ + + def __init__(self, type, id="", options=None): super(NamedStatement, self).__init__(type, id, options, has_name=True) class UnnamedStatement(Statement): - ''' + """ It represents a configuration statement, which doesn't have a name, e.g. a log path. Does not need examples. - ''' + """ def __init__(self, type, options=None): - super(UnnamedStatement, self).__init__(type, - id='', - options=options, - has_name=False) + super(UnnamedStatement, self).__init__( + type, id="", options=options, has_name=False + ) class GivenStatement(Buildable): - ''' + """ This statement returns a string without modification. It can be used to use existing configuration snippets. Does not need examples. - ''' + """ def __init__(self, value, add_newline=True): super(GivenStatement, self).__init__(iterable=None) @@ -226,29 +230,29 @@ class GivenStatement(Buildable): def build(self): if self.add_newline: - return self.value + '\n' + return self.value + "\n" else: return self.value class Option(Buildable): - ''' + """ A Statement class contains Option instances. An instance of Option can represent a file(), tcp(), udp(), etc. option. Does not need examples. - ''' + """ - def __init__(self, type='', params=None): - super(Option, self).__init__(params, ',\n') + def __init__(self, type="", params=None): + super(Option, self).__init__(params, ",\n") self.type = type self.params = params if params else [] self.iterable = self.params def build(self): - header = _indentln('{0}('.format(self.type)) - tail = _indentln(');') + header = _indentln("{0}(".format(self.type)) + tail = _indentln(");") body = self.build_body() return header + body + tail @@ -258,18 +262,18 @@ class Option(Buildable): class Parameter(Buildable): - ''' + """ An Option has one or more Parameter instances. Does not need examples. - ''' + """ - def __init__(self, iterable=None, join_body_on=''): + def __init__(self, iterable=None, join_body_on=""): super(Parameter, self).__init__(iterable, join_body_on) class SimpleParameter(Parameter): - ''' + """ A Parameter is a SimpleParameter, if it's just a simple type, like a string. @@ -286,9 +290,9 @@ class SimpleParameter(Parameter): ``/var/log/messages`` is a SimpleParameter. Does not need examples. - ''' + """ - def __init__(self, value=''): + def __init__(self, value=""): super(SimpleParameter, self).__init__() self.value = value @@ -297,7 +301,7 @@ class SimpleParameter(Parameter): class TypedParameter(Parameter): - ''' + """ A Parameter, which has a type: .. code-block:: text @@ -311,17 +315,17 @@ class TypedParameter(Parameter): ``ip(127.0.0.1)`` is a TypedParameter. Does not need examples. - ''' + """ - def __init__(self, type='', values=None): - super(TypedParameter, self).__init__(values, ',\n') + def __init__(self, type="", values=None): + super(TypedParameter, self).__init__(values, ",\n") self.type = type self.values = values if values else [] self.iterable = self.values def build(self): - header = _indentln('{0}('.format(self.type)) - tail = _indent(')') + header = _indentln("{0}(".format(self.type)) + tail = _indent(")") body = self.build_body() return header + body + tail @@ -331,26 +335,26 @@ class TypedParameter(Parameter): class ParameterValue(Buildable): - ''' + """ A TypedParameter can have one or more values. Does not need examples. - ''' + """ - def __init__(self, iterable=None, join_body_on=''): + def __init__(self, iterable=None, join_body_on=""): super(ParameterValue, self).__init__(iterable, join_body_on) class SimpleParameterValue(ParameterValue): - ''' + """ A ParameterValuem which holds a simple type, like a string or a number. For example in ip(127.0.0.1) 127.0.0.1 is a SimpleParameterValue. Does not need examples. - ''' + """ - def __init__(self, value=''): + def __init__(self, value=""): super(SimpleParameterValue, self).__init__() self.value = value @@ -359,7 +363,7 @@ class SimpleParameterValue(ParameterValue): class TypedParameterValue(ParameterValue): - ''' + """ We have to go deeper... A TypedParameter can have a 'parameter', which also have a type. For example @@ -379,17 +383,17 @@ class TypedParameterValue(ParameterValue): }; Does not need examples. - ''' + """ - def __init__(self, type='', arguments=None): - super(TypedParameterValue, self).__init__(arguments, '\n') + def __init__(self, type="", arguments=None): + super(TypedParameterValue, self).__init__(arguments, "\n") self.type = type self.arguments = arguments if arguments else [] self.iterable = self.arguments def build(self): - header = _indentln('{0}('.format(self.type)) - tail = _indent(')') + header = _indentln("{0}(".format(self.type)) + tail = _indent(")") body = self.build_body() return header + body + tail @@ -399,14 +403,14 @@ class TypedParameterValue(ParameterValue): class Argument(object): - ''' + """ A TypedParameterValue has one or more Arguments. For example this can be the value of key_file. Does not need examples. - ''' + """ - def __init__(self, value=''): + def __init__(self, value=""): self.value = value def build(self): @@ -414,29 +418,34 @@ class Argument(object): def _is_statement_unnamed(statement): - ''' + """ Returns True, if the given statement is an unnamed statement, like log or junction. - ''' - return statement in ('log', 'channel', 'junction', 'options') + """ + return statement in ("log", "channel", "junction", "options") def _is_simple_type(value): - ''' + """ Returns True, if the given parameter value is an instance of either int, str, float or bool. - ''' - return isinstance(value, six.string_types) or isinstance(value, int) or isinstance(value, float) or isinstance(value, bool) + """ + return ( + isinstance(value, six.string_types) + or isinstance(value, int) + or isinstance(value, float) + or isinstance(value, bool) + ) def _get_type_id_options(name, configuration): - ''' + """ Returns the type, id and option of a configuration object. - ''' + """ # it's in a form of source.name - if '.' in name: - type_, sep, id_ = name.partition('.') + if "." in name: + type_, sep, id_ = name.partition(".") options = configuration else: type_ = next(six.iterkeys(configuration)) @@ -447,18 +456,18 @@ def _get_type_id_options(name, configuration): def _expand_one_key_dictionary(_dict): - ''' + """ Returns the only one key and its value from a dictionary. - ''' + """ key = next(six.iterkeys(_dict)) value = _dict[key] return key, value def _parse_typed_parameter_typed_value(values): - ''' + """ Creates Arguments in a TypedParametervalue. - ''' + """ type_, value = _expand_one_key_dictionary(values) _current_parameter_value.type = type_ @@ -472,14 +481,14 @@ def _parse_typed_parameter_typed_value(values): def _parse_typed_parameter(param): - ''' + """ Parses a TypedParameter and fills it with values. - ''' + """ global _current_parameter_value type_, value = _expand_one_key_dictionary(param) _current_parameter.type = type_ - if _is_simple_type(value) and value != '': + if _is_simple_type(value) and value != "": _current_parameter_value = SimpleParameterValue(value) _current_parameter.add_value(_current_parameter_value) elif isinstance(value, list): @@ -494,9 +503,9 @@ def _parse_typed_parameter(param): def _create_and_add_parameters(params): - ''' + """ Parses the configuration and creates Parameter instances. - ''' + """ global _current_parameter if _is_simple_type(params): _current_parameter = SimpleParameter(params) @@ -513,9 +522,9 @@ def _create_and_add_parameters(params): def _create_and_add_option(option): - ''' + """ Parses the configuration and creates an Option instance. - ''' + """ global _current_option _current_option = Option() @@ -526,31 +535,39 @@ def _create_and_add_option(option): def _parse_statement(options): - ''' + """ Parses the configuration and creates options the statement. - ''' + """ for option in options: _create_and_add_option(option) def _is_reference(arg): - ''' + """ Return True, if arg is a reference to a previously defined statement. - ''' - return isinstance(arg, dict) and len(arg) == 1 and isinstance(next(six.itervalues(arg)), six.string_types) + """ + return ( + isinstance(arg, dict) + and len(arg) == 1 + and isinstance(next(six.itervalues(arg)), six.string_types) + ) def _is_junction(arg): - ''' + """ Return True, if arg is a junction statement. - ''' - return isinstance(arg, dict) and len(arg) == 1 and next(six.iterkeys(arg)) == 'junction' + """ + return ( + isinstance(arg, dict) + and len(arg) == 1 + and next(six.iterkeys(arg)) == "junction" + ) def _add_reference(reference, statement): - ''' + """ Adds a reference to statement. - ''' + """ type_, value = _expand_one_key_dictionary(reference) opt = Option(type_) param = SimpleParameter(value) @@ -559,16 +576,20 @@ def _add_reference(reference, statement): def _is_inline_definition(arg): - ''' + """ Returns True, if arg is an inline definition of a statement. - ''' - return isinstance(arg, dict) and len(arg) == 1 and isinstance(next(six.itervalues(arg)), list) + """ + return ( + isinstance(arg, dict) + and len(arg) == 1 + and isinstance(next(six.itervalues(arg)), list) + ) def _add_inline_definition(item, statement): - ''' + """ Adds an inline definition to statement. - ''' + """ global _current_statement backup = _current_statement @@ -581,14 +602,14 @@ def _add_inline_definition(item, statement): def _add_junction(item): - ''' + """ Adds a junction to the _current_statement. - ''' + """ type_, channels = _expand_one_key_dictionary(item) - junction = UnnamedStatement(type='junction') + junction = UnnamedStatement(type="junction") for item in channels: type_, value = _expand_one_key_dictionary(item) - channel = UnnamedStatement(type='channel') + channel = UnnamedStatement(type="channel") for val in value: if _is_reference(val): _add_reference(val, channel) @@ -599,9 +620,9 @@ def _add_junction(item): def _parse_log_statement(options): - ''' + """ Parses a log path. - ''' + """ for i in options: if _is_reference(i): _add_reference(i, _current_statement) @@ -612,18 +633,18 @@ def _parse_log_statement(options): def _build_config_tree(name, configuration): - ''' + """ Build the configuration tree. The root object is _current_statement. - ''' + """ type_, id_, options = _get_type_id_options(name, configuration) global _INDENT, _current_statement - _INDENT = '' - if type_ == 'config': + _INDENT = "" + if type_ == "config": _current_statement = GivenStatement(options) - elif type_ == 'log': - _current_statement = UnnamedStatement(type='log') + elif type_ == "log": + _current_statement = UnnamedStatement(type="log") _parse_log_statement(options) else: if _is_statement_unnamed(type_): @@ -634,18 +655,16 @@ def _build_config_tree(name, configuration): def _render_configuration(): - ''' + """ Renders the configuration tree into syslog-ng's configuration syntax. - ''' + """ text_repr = _current_statement.build() - _INDENT = '' + _INDENT = "" return text_repr -def config(name, - config, - write=True): - ''' +def config(name, config, write=True): + """ Builds syslog-ng configuration. This function is intended to be used from the state module, users should not use it directly! @@ -660,15 +679,14 @@ def config(name, salt '*' syslog_ng.config name='s_local' config="[{'tcp':[{'ip':'127.0.0.1'},{'port':1233}]}]" - ''' + """ _build_config_tree(name, config) configs = _render_configuration() - if __opts__.get('test', False): - comment = 'State syslog_ng will write \'{0}\' into {1}'.format( - configs, - __SYSLOG_NG_CONFIG_FILE + if __opts__.get("test", False): + comment = "State syslog_ng will write '{0}' into {1}".format( + configs, __SYSLOG_NG_CONFIG_FILE ) return _format_state_result(name, result=None, comment=comment) @@ -676,12 +694,11 @@ def config(name, if write: succ = _write_config(config=configs) - return _format_state_result(name, result=succ, - changes={'new': configs, 'old': ''}) + return _format_state_result(name, result=succ, changes={"new": configs, "old": ""}) def set_binary_path(name): - ''' + """ Sets the path, where the syslog-ng binary can be found. This function is intended to be used from states. @@ -694,7 +711,7 @@ def set_binary_path(name): salt '*' syslog_ng.set_binary_path name=/usr/sbin - ''' + """ global __SYSLOG_NG_BINARY_PATH old = __SYSLOG_NG_BINARY_PATH __SYSLOG_NG_BINARY_PATH = name @@ -703,7 +720,7 @@ def set_binary_path(name): def set_config_file(name): - ''' + """ Sets the configuration's name. This function is intended to be used from states. @@ -713,7 +730,7 @@ def set_config_file(name): salt '*' syslog_ng.set_config_file name=/etc/syslog-ng - ''' + """ global __SYSLOG_NG_CONFIG_FILE old = __SYSLOG_NG_CONFIG_FILE __SYSLOG_NG_CONFIG_FILE = name @@ -722,7 +739,7 @@ def set_config_file(name): def get_config_file(): - ''' + """ Returns the configuration directory, which contains syslog-ng.conf. CLI Example: @@ -731,26 +748,26 @@ def get_config_file(): salt '*' syslog_ng.get_config_file - ''' + """ return __SYSLOG_NG_CONFIG_FILE def _run_command(cmd, options=(), env=None): - ''' + """ Runs the command cmd with options as its CLI parameters and returns the result as a dictionary. - ''' + """ params = [cmd] params.extend(options) - return __salt__['cmd.run_all'](params, env=env, python_shell=False) + return __salt__["cmd.run_all"](params, env=env, python_shell=False) def _determine_config_version(syslog_ng_sbin_dir): ret = version(syslog_ng_sbin_dir) - full_version = ret['stdout'] + full_version = ret["stdout"] dot_count = 0 for idx, part in enumerate(full_version): - if part == '.': + if part == ".": dot_count = dot_count + 1 if dot_count == 2: return full_version[0:idx] @@ -758,12 +775,8 @@ def _determine_config_version(syslog_ng_sbin_dir): return full_version[:3] -def set_parameters(version=None, - binary_path=None, - config_file=None, - *args, - **kwargs): - ''' +def set_parameters(version=None, binary_path=None, config_file=None, *args, **kwargs): + """ Sets variables. CLI Example: @@ -773,7 +786,7 @@ def set_parameters(version=None, salt '*' syslog_ng.set_parameters version='3.6' salt '*' syslog_ng.set_parameters binary_path=/home/user/install/syslog-ng/sbin config_file=/home/user/install/syslog-ng/etc/syslog-ng.conf - ''' + """ if binary_path: set_binary_path(binary_path) if config_file: @@ -786,42 +799,40 @@ def set_parameters(version=None, def _run_command_in_extended_path(syslog_ng_sbin_dir, command, params): - ''' + """ Runs the specified command with the syslog_ng_sbin_dir in the PATH - ''' - orig_path = os.environ.get('PATH', '') + """ + orig_path = os.environ.get("PATH", "") env = None if syslog_ng_sbin_dir: # Custom environment variables should be str types. This code # normalizes the paths to unicode to join them together, and then # converts back to a str type. env = { - str('PATH'): salt.utils.stringutils.to_str( # future lint: disable=blacklisted-function - os.pathsep.join( - salt.utils.data.decode( - (orig_path, syslog_ng_sbin_dir) - ) - ) + str( + "PATH" + ): salt.utils.stringutils.to_str( # future lint: disable=blacklisted-function + os.pathsep.join(salt.utils.data.decode((orig_path, syslog_ng_sbin_dir))) ) } return _run_command(command, options=params, env=env) def _format_return_data(retcode, stdout=None, stderr=None): - ''' + """ Creates a dictionary from the parameters, which can be used to return data to Salt. - ''' - ret = {'retcode': retcode} + """ + ret = {"retcode": retcode} if stdout is not None: - ret['stdout'] = stdout + ret["stdout"] = stdout if stderr is not None: - ret['stderr'] = stderr + ret["stderr"] = stderr return ret def config_test(syslog_ng_sbin_dir=None, cfgfile=None): - ''' + """ Runs syntax check against cfgfile. If syslog_ng_sbin_dir is specified, it is added to the PATH during the test. @@ -832,26 +843,24 @@ def config_test(syslog_ng_sbin_dir=None, cfgfile=None): salt '*' syslog_ng.config_test salt '*' syslog_ng.config_test /home/user/install/syslog-ng/sbin salt '*' syslog_ng.config_test /home/user/install/syslog-ng/sbin /etc/syslog-ng/syslog-ng.conf - ''' - params = ['--syntax-only'] + """ + params = ["--syntax-only"] if cfgfile: - params.append('--cfgfile={0}'.format(cfgfile)) + params.append("--cfgfile={0}".format(cfgfile)) try: - ret = _run_command_in_extended_path(syslog_ng_sbin_dir, - 'syslog-ng', - params) + ret = _run_command_in_extended_path(syslog_ng_sbin_dir, "syslog-ng", params) except CommandExecutionError as err: return _format_return_data(retcode=-1, stderr=six.text_type(err)) - retcode = ret.get('retcode', -1) - stderr = ret.get('stderr', None) - stdout = ret.get('stdout', None) + retcode = ret.get("retcode", -1) + stderr = ret.get("stderr", None) + stdout = ret.get("stdout", None) return _format_return_data(retcode, stdout, stderr) def version(syslog_ng_sbin_dir=None): - ''' + """ Returns the version of the installed syslog-ng. If syslog_ng_sbin_dir is specified, it is added to the PATH during the execution of the command syslog-ng. @@ -862,20 +871,18 @@ def version(syslog_ng_sbin_dir=None): salt '*' syslog_ng.version salt '*' syslog_ng.version /home/user/install/syslog-ng/sbin - ''' + """ try: - ret = _run_command_in_extended_path(syslog_ng_sbin_dir, - 'syslog-ng', - ('-V',)) + ret = _run_command_in_extended_path(syslog_ng_sbin_dir, "syslog-ng", ("-V",)) except CommandExecutionError as err: return _format_return_data(retcode=-1, stderr=six.text_type(err)) - if ret['retcode'] != 0: - return _format_return_data(ret['retcode'], - stderr=ret['stderr'], - stdout=ret['stdout']) + if ret["retcode"] != 0: + return _format_return_data( + ret["retcode"], stderr=ret["stderr"], stdout=ret["stdout"] + ) - lines = ret['stdout'].split('\n') + lines = ret["stdout"].split("\n") # The format of the first line in the output is: # syslog-ng 3.6.0alpha0 version_line_index = 0 @@ -885,7 +892,7 @@ def version(syslog_ng_sbin_dir=None): def modules(syslog_ng_sbin_dir=None): - ''' + """ Returns the available modules. If syslog_ng_sbin_dir is specified, it is added to the PATH during the execution of the command syslog-ng. @@ -895,30 +902,25 @@ def modules(syslog_ng_sbin_dir=None): salt '*' syslog_ng.modules salt '*' syslog_ng.modules /home/user/install/syslog-ng/sbin - ''' + """ try: - ret = _run_command_in_extended_path(syslog_ng_sbin_dir, - 'syslog-ng', - ('-V',)) + ret = _run_command_in_extended_path(syslog_ng_sbin_dir, "syslog-ng", ("-V",)) except CommandExecutionError as err: return _format_return_data(retcode=-1, stderr=six.text_type(err)) - if ret['retcode'] != 0: - return _format_return_data(ret['retcode'], - ret.get('stdout'), - ret.get('stderr')) + if ret["retcode"] != 0: + return _format_return_data(ret["retcode"], ret.get("stdout"), ret.get("stderr")) - lines = ret['stdout'].split('\n') + lines = ret["stdout"].split("\n") for line in lines: - if line.startswith('Available-Modules'): + if line.startswith("Available-Modules"): label, installed_modules = line.split() - return _format_return_data(ret['retcode'], - stdout=installed_modules) - return _format_return_data(-1, stderr='Unable to find the modules.') + return _format_return_data(ret["retcode"], stdout=installed_modules) + return _format_return_data(-1, stderr="Unable to find the modules.") def stats(syslog_ng_sbin_dir=None): - ''' + """ Returns statistics from the running syslog-ng instance. If syslog_ng_sbin_dir is specified, it is added to the PATH during the execution of the command syslog-ng-ctl. @@ -929,51 +931,48 @@ def stats(syslog_ng_sbin_dir=None): salt '*' syslog_ng.stats salt '*' syslog_ng.stats /home/user/install/syslog-ng/sbin - ''' + """ try: - ret = _run_command_in_extended_path(syslog_ng_sbin_dir, - 'syslog-ng-ctl', - ('stats',)) + ret = _run_command_in_extended_path( + syslog_ng_sbin_dir, "syslog-ng-ctl", ("stats",) + ) except CommandExecutionError as err: return _format_return_data(retcode=-1, stderr=six.text_type(err)) - return _format_return_data(ret['retcode'], - ret.get('stdout'), - ret.get('stderr')) + return _format_return_data(ret["retcode"], ret.get("stdout"), ret.get("stderr")) -def _format_changes(old='', new=''): - return {'old': old, 'new': new} +def _format_changes(old="", new=""): + return {"old": old, "new": new} -def _format_state_result(name, result, changes=None, comment=''): - ''' +def _format_state_result(name, result, changes=None, comment=""): + """ Creates the state result dictionary. - ''' + """ if changes is None: - changes = {'old': '', 'new': ''} - return {'name': name, 'result': result, - 'changes': changes, 'comment': comment} + changes = {"old": "", "new": ""} + return {"name": name, "result": result, "changes": changes, "comment": comment} def _add_cli_param(params, key, value): - ''' + """ Adds key and value as a command line parameter to params. - ''' + """ if value is not None: - params.append('--{0}={1}'.format(key, value)) + params.append("--{0}={1}".format(key, value)) def _add_boolean_cli_param(params, key, value): - ''' + """ Adds key as a command line parameter to params. - ''' + """ if value is True: - params.append('--{0}'.format(key)) + params.append("--{0}".format(key)) def stop(name=None): - ''' + """ Kills syslog-ng. This function is intended to be used from the state module. Users shouldn't use this function, if the service module is available on @@ -987,45 +986,47 @@ def stop(name=None): salt '*' syslog_ng.stop - ''' - pids = __salt__['ps.pgrep'](pattern='syslog-ng') + """ + pids = __salt__["ps.pgrep"](pattern="syslog-ng") if pids is None or len(pids) == 0: - return _format_state_result(name, - result=False, - comment='Syslog-ng is not running') + return _format_state_result( + name, result=False, comment="Syslog-ng is not running" + ) - if __opts__.get('test', False): - comment = 'Syslog_ng state module will kill {0} pids' + if __opts__.get("test", False): + comment = "Syslog_ng state module will kill {0} pids" return _format_state_result(name, result=None, comment=comment) - res = __salt__['ps.pkill']('syslog-ng') - killed_pids = res['killed'] + res = __salt__["ps.pkill"]("syslog-ng") + killed_pids = res["killed"] if killed_pids == pids: - changes = {'old': killed_pids, 'new': []} + changes = {"old": killed_pids, "new": []} return _format_state_result(name, result=True, changes=changes) else: return _format_state_result(name, result=False) -def start(name=None, - user=None, - group=None, - chroot=None, - caps=None, - no_caps=False, - pidfile=None, - enable_core=False, - fd_limit=None, - verbose=False, - debug=False, - trace=False, - yydebug=False, - persist_file=None, - control=None, - worker_threads=None): - ''' +def start( + name=None, + user=None, + group=None, + chroot=None, + caps=None, + no_caps=False, + pidfile=None, + enable_core=False, + fd_limit=None, + verbose=False, + debug=False, + trace=False, + yydebug=False, + persist_file=None, + control=None, + worker_threads=None, +): + """ Ensures, that syslog-ng is started via the given parameters. This function is intended to be used from the state module. @@ -1040,54 +1041,54 @@ def start(name=None, salt '*' syslog_ng.start - ''' + """ params = [] - _add_cli_param(params, 'user', user) - _add_cli_param(params, 'group', group) - _add_cli_param(params, 'chroot', chroot) - _add_cli_param(params, 'caps', caps) - _add_boolean_cli_param(params, 'no-capse', no_caps) - _add_cli_param(params, 'pidfile', pidfile) - _add_boolean_cli_param(params, 'enable-core', enable_core) - _add_cli_param(params, 'fd-limit', fd_limit) - _add_boolean_cli_param(params, 'verbose', verbose) - _add_boolean_cli_param(params, 'debug', debug) - _add_boolean_cli_param(params, 'trace', trace) - _add_boolean_cli_param(params, 'yydebug', yydebug) - _add_cli_param(params, 'cfgfile', __SYSLOG_NG_CONFIG_FILE) - _add_boolean_cli_param(params, 'persist-file', persist_file) - _add_cli_param(params, 'control', control) - _add_cli_param(params, 'worker-threads', worker_threads) + _add_cli_param(params, "user", user) + _add_cli_param(params, "group", group) + _add_cli_param(params, "chroot", chroot) + _add_cli_param(params, "caps", caps) + _add_boolean_cli_param(params, "no-capse", no_caps) + _add_cli_param(params, "pidfile", pidfile) + _add_boolean_cli_param(params, "enable-core", enable_core) + _add_cli_param(params, "fd-limit", fd_limit) + _add_boolean_cli_param(params, "verbose", verbose) + _add_boolean_cli_param(params, "debug", debug) + _add_boolean_cli_param(params, "trace", trace) + _add_boolean_cli_param(params, "yydebug", yydebug) + _add_cli_param(params, "cfgfile", __SYSLOG_NG_CONFIG_FILE) + _add_boolean_cli_param(params, "persist-file", persist_file) + _add_cli_param(params, "control", control) + _add_cli_param(params, "worker-threads", worker_threads) if __SYSLOG_NG_BINARY_PATH: - syslog_ng_binary = os.path.join(__SYSLOG_NG_BINARY_PATH, 'syslog-ng') + syslog_ng_binary = os.path.join(__SYSLOG_NG_BINARY_PATH, "syslog-ng") command = [syslog_ng_binary] + params - if __opts__.get('test', False): - comment = 'Syslog_ng state module will start {0}'.format(command) + if __opts__.get("test", False): + comment = "Syslog_ng state module will start {0}".format(command) return _format_state_result(name, result=None, comment=comment) - result = __salt__['cmd.run_all'](command, python_shell=False) + result = __salt__["cmd.run_all"](command, python_shell=False) else: - command = ['syslog-ng'] + params + command = ["syslog-ng"] + params - if __opts__.get('test', False): - comment = 'Syslog_ng state module will start {0}'.format(command) + if __opts__.get("test", False): + comment = "Syslog_ng state module will start {0}".format(command) return _format_state_result(name, result=None, comment=comment) - result = __salt__['cmd.run_all'](command, python_shell=False) + result = __salt__["cmd.run_all"](command, python_shell=False) - if result['pid'] > 0: + if result["pid"] > 0: succ = True else: succ = False return _format_state_result( - name, result=succ, changes={'new': ' '.join(command), 'old': ''} + name, result=succ, changes={"new": " ".join(command), "old": ""} ) def reload_(name): - ''' + """ Reloads syslog-ng. This function is intended to be used from states. If :mod:`syslog_ng.set_config_file @@ -1100,30 +1101,29 @@ def reload_(name): salt '*' syslog_ng.reload - ''' + """ if __SYSLOG_NG_BINARY_PATH: - syslog_ng_ctl_binary = os.path.join(__SYSLOG_NG_BINARY_PATH, - 'syslog-ng-ctl') - command = [syslog_ng_ctl_binary, 'reload'] - result = __salt__['cmd.run_all'](command, python_shell=False) + syslog_ng_ctl_binary = os.path.join(__SYSLOG_NG_BINARY_PATH, "syslog-ng-ctl") + command = [syslog_ng_ctl_binary, "reload"] + result = __salt__["cmd.run_all"](command, python_shell=False) else: - command = ['syslog-ng-ctl', 'reload'] - result = __salt__['cmd.run_all'](command, python_shell=False) + command = ["syslog-ng-ctl", "reload"] + result = __salt__["cmd.run_all"](command, python_shell=False) - succ = True if result['retcode'] == 0 else False - return _format_state_result(name, result=succ, comment=result['stdout']) + succ = True if result["retcode"] == 0 else False + return _format_state_result(name, result=succ, comment=result["stdout"]) def _format_generated_config_header(): - ''' + """ Formats a header, which is prepended to all appended config. - ''' - now = time.strftime('%Y-%m-%d %H:%M:%S') + """ + now = time.strftime("%Y-%m-%d %H:%M:%S") return __SALT_GENERATED_CONFIG_HEADER.format(now) def write_config(config, newlines=2): - ''' + """ Writes the given parameter config into the config file. This function is intended to be used from states. @@ -1137,23 +1137,23 @@ def write_config(config, newlines=2): salt '*' syslog_ng.write_config config='# comment' - ''' + """ succ = _write_config(config, newlines) changes = _format_changes(new=config) - return _format_state_result(name='', result=succ, changes=changes) + return _format_state_result(name="", result=succ, changes=changes) def _write_config(config, newlines=2): - ''' + """ Writes the given parameter config into the config file. - ''' + """ text = config if isinstance(config, dict) and len(list(list(config.keys()))) == 1: key = next(six.iterkeys(config)) text = config[key] try: - with salt.utils.files.fopen(__SYSLOG_NG_CONFIG_FILE, 'a') as fha: + with salt.utils.files.fopen(__SYSLOG_NG_CONFIG_FILE, "a") as fha: fha.write(salt.utils.stringutils.to_str(text)) for _ in range(0, newlines): @@ -1165,7 +1165,7 @@ def _write_config(config, newlines=2): def write_version(name): - ''' + """ Removes the previous configuration file, then creates a new one and writes the name line. This function is intended to be used from states. @@ -1179,16 +1179,15 @@ def write_version(name): salt '*' syslog_ng.write_version name="3.6" - ''' - line = '@version: {0}'.format(name) + """ + line = "@version: {0}".format(name) try: if os.path.exists(__SYSLOG_NG_CONFIG_FILE): log.debug( - 'Removing previous configuration file: %s', - __SYSLOG_NG_CONFIG_FILE + "Removing previous configuration file: %s", __SYSLOG_NG_CONFIG_FILE ) os.remove(__SYSLOG_NG_CONFIG_FILE) - log.debug('Configuration file successfully removed') + log.debug("Configuration file successfully removed") header = _format_generated_config_header() _write_config(config=header, newlines=1) @@ -1197,7 +1196,8 @@ def write_version(name): return _format_state_result(name, result=True) except OSError as err: log.error( - 'Failed to remove previous configuration file \'%s\': %s', - __SYSLOG_NG_CONFIG_FILE, err + "Failed to remove previous configuration file '%s': %s", + __SYSLOG_NG_CONFIG_FILE, + err, ) return _format_state_result(name, result=False) diff --git a/salt/modules/sysmod.py b/salt/modules/sysmod.py index 66621318037..f99aae42deb 100644 --- a/salt/modules/sysmod.py +++ b/salt/modules/sysmod.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" The sys module provides information about the available functions on the minion -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import fnmatch @@ -14,29 +14,29 @@ import salt.runner import salt.state import salt.utils.args import salt.utils.schema -from salt.utils.doc import strip_rst as _strip_rst -from salt.ext.six.moves import zip # Import 3rd-party libs from salt.ext import six +from salt.ext.six.moves import zip +from salt.utils.doc import strip_rst as _strip_rst log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'sys' +__virtualname__ = "sys" -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] def __virtual__(): - ''' + """ Return as sys - ''' + """ return __virtualname__ def doc(*args): - ''' + """ Return the docstrings for all modules. Optionally, specify a module or a function to narrow the selection. @@ -62,7 +62,7 @@ def doc(*args): salt '*' sys.doc 'sys.*' salt '*' sys.doc 'sys.list_*' - ''' + """ docs = {} if not args: for fun in __salt__: @@ -71,15 +71,15 @@ def doc(*args): for module in args: _use_fnmatch = False - if '*' in module: + if "*" in module: target_mod = module _use_fnmatch = True elif module: # allow both "sys" and "sys." to match sys, without also matching # sysctl - target_mod = module + '.' if not module.endswith('.') else module + target_mod = module + "." if not module.endswith(".") else module else: - target_mod = '' + target_mod = "" if _use_fnmatch: for fun in fnmatch.filter(__salt__, target_mod): docs[fun] = __salt__[fun].__doc__ @@ -92,7 +92,7 @@ def doc(*args): def state_doc(*args): - ''' + """ Return the docstrings for all states. Optionally, specify a state or a function to narrow the selection. @@ -120,49 +120,49 @@ def state_doc(*args): salt '*' sys.state_doc 'service.*' 'iptables.*' - ''' + """ st_ = salt.state.State(__opts__) docs = {} if not args: for fun in st_.states: - state = fun.split('.')[0] + state = fun.split(".")[0] if state not in docs: - if hasattr(st_.states[fun], '__globals__'): - docs[state] = st_.states[fun].__globals__['__doc__'] + if hasattr(st_.states[fun], "__globals__"): + docs[state] = st_.states[fun].__globals__["__doc__"] docs[fun] = st_.states[fun].__doc__ return _strip_rst(docs) for module in args: _use_fnmatch = False - if '*' in module: + if "*" in module: target_mod = module _use_fnmatch = True elif module: # allow both "sys" and "sys." to match sys, without also matching # sysctl - target_mod = module + '.' if not module.endswith('.') else module + target_mod = module + "." if not module.endswith(".") else module else: - target_mod = '' + target_mod = "" if _use_fnmatch: for fun in fnmatch.filter(st_.states, target_mod): - state = fun.split('.')[0] - if hasattr(st_.states[fun], '__globals__'): - docs[state] = st_.states[fun].__globals__['__doc__'] + state = fun.split(".")[0] + if hasattr(st_.states[fun], "__globals__"): + docs[state] = st_.states[fun].__globals__["__doc__"] docs[fun] = st_.states[fun].__doc__ else: for fun in st_.states: if fun == module or fun.startswith(target_mod): - state = module.split('.')[0] + state = module.split(".")[0] if state not in docs: - if hasattr(st_.states[fun], '__globals__'): - docs[state] = st_.states[fun].__globals__['__doc__'] + if hasattr(st_.states[fun], "__globals__"): + docs[state] = st_.states[fun].__globals__["__doc__"] docs[fun] = st_.states[fun].__doc__ return _strip_rst(docs) def runner_doc(*args): - ''' + """ Return the docstrings for all runners. Optionally, specify a runner or a function to narrow the selection. @@ -190,7 +190,7 @@ def runner_doc(*args): salt '*' sys.runner_doc 'cache.clear_*' - ''' + """ run_ = salt.runner.Runner(__opts__) docs = {} if not args: @@ -200,15 +200,15 @@ def runner_doc(*args): for module in args: _use_fnmatch = False - if '*' in module: + if "*" in module: target_mod = module _use_fnmatch = True elif module: # allow both "sys" and "sys." to match sys, without also matching # sysctl - target_mod = module + '.' if not module.endswith('.') else module + target_mod = module + "." if not module.endswith(".") else module else: - target_mod = '' + target_mod = "" if _use_fnmatch: for fun in fnmatch.filter(run_.functions, target_mod): docs[fun] = run_.functions[fun].__doc__ @@ -220,7 +220,7 @@ def runner_doc(*args): def returner_doc(*args): - ''' + """ Return the docstrings for all returners. Optionally, specify a returner or a function to narrow the selection. @@ -248,7 +248,7 @@ def returner_doc(*args): salt '*' sys.returner_doc 'sqlite3.get_*' - ''' + """ returners_ = salt.loader.returners(__opts__, []) docs = {} @@ -259,15 +259,15 @@ def returner_doc(*args): for module in args: _use_fnmatch = False - if '*' in module: + if "*" in module: target_mod = module _use_fnmatch = True elif module: # allow both "sys" and "sys." to match sys, without also matching # sysctl - target_mod = module + '.' if not module.endswith('.') else module + target_mod = module + "." if not module.endswith(".") else module else: - target_mod = '' + target_mod = "" if _use_fnmatch: for fun in returners_: if fun == module or fun.startswith(target_mod): @@ -280,7 +280,7 @@ def returner_doc(*args): def renderer_doc(*args): - ''' + """ Return the docstrings for all renderers. Optionally, specify a renderer or a function to narrow the selection. @@ -305,7 +305,7 @@ def renderer_doc(*args): salt '*' sys.renderer_doc 'c*' 'j*' - ''' + """ renderers_ = salt.loader.render(__opts__, []) docs = {} if not args: @@ -314,11 +314,11 @@ def renderer_doc(*args): return _strip_rst(docs) for module in args: - if '*' in module or '.' in module: + if "*" in module or "." in module: for func in fnmatch.filter(renderers_, module): docs[func] = renderers_[func].__doc__ else: - moduledot = module + '.' + moduledot = module + "." for func in six.iterkeys(renderers_): if func.startswith(moduledot): docs[func] = renderers_[func].__doc__ @@ -326,7 +326,7 @@ def renderer_doc(*args): def list_functions(*args, **kwargs): # pylint: disable=unused-argument - ''' + """ List the functions for all modules. Optionally, specify a module or modules from which to list. @@ -352,7 +352,7 @@ def list_functions(*args, **kwargs): # pylint: disable=unused-argument salt '*' sys.list_functions 'module.specific_function' - ''' + """ # ## NOTE: **kwargs is used here to prevent a traceback when garbage # ## arguments are tacked on to the end. @@ -362,12 +362,12 @@ def list_functions(*args, **kwargs): # pylint: disable=unused-argument names = set() for module in args: - if '*' in module or '.' in module: + if "*" in module or "." in module: for func in fnmatch.filter(__salt__, module): names.add(func) else: # "sys" should just match sys without also matching sysctl - moduledot = module + '.' + moduledot = module + "." for func in __salt__: if func.startswith(moduledot): names.add(func) @@ -375,7 +375,7 @@ def list_functions(*args, **kwargs): # pylint: disable=unused-argument def list_modules(*args): - ''' + """ List the modules loaded on the minion .. versionadded:: 2015.5.0 @@ -392,27 +392,27 @@ def list_modules(*args): salt '*' sys.list_modules 's*' - ''' + """ modules = set() if not args: for func in __salt__: - modules.add(func.split('.')[0]) + modules.add(func.split(".")[0]) return sorted(modules) for module in args: - if '*' in module: + if "*" in module: for func in fnmatch.filter(__salt__, module): - modules.add(func.split('.')[0]) + modules.add(func.split(".")[0]) else: for func in __salt__: - mod_test = func.split('.')[0] + mod_test = func.split(".")[0] if mod_test == module: modules.add(mod_test) return sorted(modules) def reload_modules(): - ''' + """ Tell the minion to reload the execution modules CLI Example: @@ -420,7 +420,7 @@ def reload_modules(): .. code-block:: bash salt '*' sys.reload_modules - ''' + """ # This function is actually handled inside the minion.py file, the function # is caught before it ever gets here. Therefore, the docstring above is # only for the online docs, and ANY CHANGES made to it must also be made in @@ -428,8 +428,8 @@ def reload_modules(): return True -def argspec(module=''): - ''' +def argspec(module=""): + """ Return the argument specification of functions in Salt execution modules. @@ -449,12 +449,12 @@ def argspec(module=''): salt '*' sys.argspec 'pkg.*' - ''' + """ return salt.utils.args.argspec_report(__salt__, module) -def state_argspec(module=''): - ''' +def state_argspec(module=""): + """ Return the argument specification of functions in Salt state modules. @@ -474,13 +474,13 @@ def state_argspec(module=''): salt '*' sys.state_argspec 'pkg.*' - ''' + """ st_ = salt.state.State(__opts__) return salt.utils.args.argspec_report(st_.states, module) -def returner_argspec(module=''): - ''' +def returner_argspec(module=""): + """ Return the argument specification of functions in Salt returner modules. @@ -500,13 +500,13 @@ def returner_argspec(module=''): salt '*' sys.returner_argspec 'sqlite3.*' - ''' + """ returners_ = salt.loader.returners(__opts__, []) return salt.utils.args.argspec_report(returners_, module) -def runner_argspec(module=''): - ''' +def runner_argspec(module=""): + """ Return the argument specification of functions in Salt runner modules. @@ -525,13 +525,13 @@ def runner_argspec(module=''): .. code-block:: bash salt '*' sys.runner_argspec 'winrepo.*' - ''' + """ run_ = salt.runner.Runner(__opts__) return salt.utils.args.argspec_report(run_.functions, module) def list_state_functions(*args, **kwargs): # pylint: disable=unused-argument - ''' + """ List the functions for all state modules. Optionally, specify a state module or modules from which to list. @@ -560,7 +560,7 @@ def list_state_functions(*args, **kwargs): # pylint: disable=unused-argument salt '*' sys.list_state_functions 'module.specific_function' - ''' + """ # NOTE: **kwargs is used here to prevent a traceback when garbage # arguments are tacked on to the end. @@ -571,12 +571,12 @@ def list_state_functions(*args, **kwargs): # pylint: disable=unused-argument names = set() for module in args: - if '*' in module or '.' in module: + if "*" in module or "." in module: for func in fnmatch.filter(st_.states, module): names.add(func) else: # "sys" should just match sys without also matching sysctl - moduledot = module + '.' + moduledot = module + "." for func in st_.states: if func.startswith(moduledot): names.add(func) @@ -584,7 +584,7 @@ def list_state_functions(*args, **kwargs): # pylint: disable=unused-argument def list_state_modules(*args): - ''' + """ List the modules loaded on the minion .. versionadded:: 2014.7.0 @@ -603,30 +603,30 @@ def list_state_modules(*args): salt '*' sys.list_state_modules 'mysql_*' - ''' + """ st_ = salt.state.State(__opts__) modules = set() if not args: for func in st_.states: - log.debug('func %s', func) - modules.add(func.split('.')[0]) + log.debug("func %s", func) + modules.add(func.split(".")[0]) return sorted(modules) for module in args: - if '*' in module: + if "*" in module: for func in fnmatch.filter(st_.states, module): - modules.add(func.split('.')[0]) + modules.add(func.split(".")[0]) else: for func in st_.states: - mod_test = func.split('.')[0] + mod_test = func.split(".")[0] if mod_test == module: modules.add(mod_test) return sorted(modules) def list_runners(*args): - ''' + """ List the runners loaded on the minion .. versionadded:: 2014.7.0 @@ -645,28 +645,28 @@ def list_runners(*args): salt '*' sys.list_runners 'm*' - ''' + """ run_ = salt.runner.Runner(__opts__) runners = set() if not args: for func in run_.functions: - runners.add(func.split('.')[0]) + runners.add(func.split(".")[0]) return sorted(runners) for module in args: - if '*' in module: + if "*" in module: for func in fnmatch.filter(run_.functions, module): - runners.add(func.split('.')[0]) + runners.add(func.split(".")[0]) else: for func in run_.functions: - mod_test = func.split('.')[0] + mod_test = func.split(".")[0] if mod_test == module: runners.add(mod_test) return sorted(runners) def list_runner_functions(*args, **kwargs): # pylint: disable=unused-argument - ''' + """ List the functions for all runner modules. Optionally, specify a runner module or modules from which to list. @@ -688,7 +688,7 @@ def list_runner_functions(*args, **kwargs): # pylint: disable=unused-argument salt '*' sys.list_runner_functions 'state.*' 'virt.*' - ''' + """ # ## NOTE: **kwargs is used here to prevent a traceback when garbage # ## arguments are tacked on to the end. @@ -699,12 +699,12 @@ def list_runner_functions(*args, **kwargs): # pylint: disable=unused-argument names = set() for module in args: - if '*' in module or '.' in module: + if "*" in module or "." in module: for func in fnmatch.filter(run_.functions, module): names.add(func) else: # "sys" should just match sys without also matching sysctl - moduledot = module + '.' + moduledot = module + "." for func in run_.functions: if func.startswith(moduledot): names.add(func) @@ -712,7 +712,7 @@ def list_runner_functions(*args, **kwargs): # pylint: disable=unused-argument def list_returners(*args): - ''' + """ List the returners loaded on the minion .. versionadded:: 2014.7.0 @@ -731,29 +731,29 @@ def list_returners(*args): salt '*' sys.list_returners 's*' - ''' + """ returners_ = salt.loader.returners(__opts__, []) returners = set() if not args: for func in six.iterkeys(returners_): - returners.add(func.split('.')[0]) + returners.add(func.split(".")[0]) return sorted(returners) for module in args: - if '*' in module: + if "*" in module: for func in fnmatch.filter(returners_, module): - returners.add(func.split('.')[0]) + returners.add(func.split(".")[0]) else: for func in returners_: - mod_test = func.split('.')[0] + mod_test = func.split(".")[0] if mod_test == module: returners.add(mod_test) return sorted(returners) def list_returner_functions(*args, **kwargs): # pylint: disable=unused-argument - ''' + """ List the functions for all returner modules. Optionally, specify a returner module or modules from which to list. @@ -775,7 +775,7 @@ def list_returner_functions(*args, **kwargs): # pylint: disable=unused-argument salt '*' sys.list_returner_functions 'sqlite3.get_*' - ''' + """ # NOTE: **kwargs is used here to prevent a traceback when garbage # arguments are tacked on to the end. @@ -786,12 +786,12 @@ def list_returner_functions(*args, **kwargs): # pylint: disable=unused-argument names = set() for module in args: - if '*' in module or '.' in module: + if "*" in module or "." in module: for func in fnmatch.filter(returners_, module): names.add(func) else: # "sys" should just match sys without also matching sysctl - moduledot = module + '.' + moduledot = module + "." for func in returners_: if func.startswith(moduledot): names.add(func) @@ -799,7 +799,7 @@ def list_returner_functions(*args, **kwargs): # pylint: disable=unused-argument def list_renderers(*args): - ''' + """ List the renderers loaded on the minion .. versionadded:: 2015.5.0 @@ -816,7 +816,7 @@ def list_renderers(*args): salt '*' sys.list_renderers 'yaml*' - ''' + """ renderers_ = salt.loader.render(__opts__, []) renderers = set() @@ -832,44 +832,46 @@ def list_renderers(*args): def _argspec_to_schema(mod, spec): - args = spec['args'] - defaults = spec['defaults'] or [] + args = spec["args"] + defaults = spec["defaults"] or [] - args_req = args[:len(args) - len(defaults)] - args_defaults = list(zip(args[-len(defaults):], defaults)) + args_req = args[: len(args) - len(defaults)] + args_defaults = list(zip(args[-len(defaults) :], defaults)) types = { - 'title': mod, - 'description': mod, + "title": mod, + "description": mod, } for i in args_req: - types[i] = salt.utils.schema.OneOfItem(items=( - salt.utils.schema.BooleanItem(title=i, description=i, required=True), - salt.utils.schema.IntegerItem(title=i, description=i, required=True), - salt.utils.schema.NumberItem(title=i, description=i, required=True), - salt.utils.schema.StringItem(title=i, description=i, required=True), - - # S.ArrayItem(title=i, description=i, required=True), - # S.DictItem(title=i, description=i, required=True), - )) + types[i] = salt.utils.schema.OneOfItem( + items=( + salt.utils.schema.BooleanItem(title=i, description=i, required=True), + salt.utils.schema.IntegerItem(title=i, description=i, required=True), + salt.utils.schema.NumberItem(title=i, description=i, required=True), + salt.utils.schema.StringItem(title=i, description=i, required=True), + # S.ArrayItem(title=i, description=i, required=True), + # S.DictItem(title=i, description=i, required=True), + ) + ) for i, j in args_defaults: - types[i] = salt.utils.schema.OneOfItem(items=( - salt.utils.schema.BooleanItem(title=i, description=i, default=j), - salt.utils.schema.IntegerItem(title=i, description=i, default=j), - salt.utils.schema.NumberItem(title=i, description=i, default=j), - salt.utils.schema.StringItem(title=i, description=i, default=j), - - # S.ArrayItem(title=i, description=i, default=j), - # S.DictItem(title=i, description=i, default=j), - )) + types[i] = salt.utils.schema.OneOfItem( + items=( + salt.utils.schema.BooleanItem(title=i, description=i, default=j), + salt.utils.schema.IntegerItem(title=i, description=i, default=j), + salt.utils.schema.NumberItem(title=i, description=i, default=j), + salt.utils.schema.StringItem(title=i, description=i, default=j), + # S.ArrayItem(title=i, description=i, default=j), + # S.DictItem(title=i, description=i, default=j), + ) + ) return type(mod, (salt.utils.schema.Schema,), types).serialize() -def state_schema(module=''): - ''' +def state_schema(module=""): + """ Return a JSON Schema for the given state function(s) .. versionadded:: 2016.3.0 @@ -880,7 +882,7 @@ def state_schema(module=''): salt '*' sys.state_schema salt '*' sys.state_schema pkg.installed - ''' + """ specs = state_argspec(module) schemas = [] diff --git a/salt/modules/sysrc.py b/salt/modules/sysrc.py index be0cec44169..431f978ef7b 100644 --- a/salt/modules/sysrc.py +++ b/salt/modules/sysrc.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" sysrc module for FreeBSD -''' +""" # Import Python libs from __future__ import absolute_import @@ -10,25 +10,25 @@ from __future__ import absolute_import import salt.utils.path from salt.exceptions import CommandExecutionError +__virtualname__ = "sysrc" -__virtualname__ = 'sysrc' - -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Only runs if sysrc exists - ''' - if salt.utils.path.which('sysrc') is not None: + """ + if salt.utils.path.which("sysrc") is not None: return True - return (False, 'The sysrc execution module failed to load: the sysrc binary is not in the path.') + return ( + False, + "The sysrc execution module failed to load: the sysrc binary is not in the path.", + ) def get(**kwargs): - ''' + """ Return system rc configuration variables CLI Example: @@ -36,38 +36,38 @@ def get(**kwargs): .. code-block:: bash salt '*' sysrc.get includeDefaults=True - ''' + """ - cmd = 'sysrc -v' + cmd = "sysrc -v" - if 'file' in kwargs: - cmd += ' -f '+kwargs['file'] + if "file" in kwargs: + cmd += " -f " + kwargs["file"] - if 'jail' in kwargs: - cmd += ' -j '+kwargs['jail'] + if "jail" in kwargs: + cmd += " -j " + kwargs["jail"] - if 'name' in kwargs: - cmd += ' '+kwargs['name'] - elif kwargs.get('includeDefaults', False): - cmd += ' -A' + if "name" in kwargs: + cmd += " " + kwargs["name"] + elif kwargs.get("includeDefaults", False): + cmd += " -A" else: - cmd += ' -a' + cmd += " -a" - sysrcs = __salt__['cmd.run'](cmd) + sysrcs = __salt__["cmd.run"](cmd) if "sysrc: unknown variable" in sysrcs: # raise CommandExecutionError(sysrcs) return None ret = {} for sysrc in sysrcs.split("\n"): - line_components = sysrc.split(': ') + line_components = sysrc.split(": ") rcfile = line_components[0] if len(line_components) > 2: var = line_components[1] val = line_components[2] else: - var = line_components[1].rstrip(':') - val = '' + var = line_components[1].rstrip(":") + val = "" if rcfile not in ret: ret[rcfile] = {} ret[rcfile][var] = val @@ -75,7 +75,7 @@ def get(**kwargs): def set_(name, value, **kwargs): - ''' + """ Set system rc configuration variables CLI Example: @@ -83,15 +83,15 @@ def set_(name, value, **kwargs): .. code-block:: bash salt '*' sysrc.set name=sshd_flags value="-p 2222" - ''' + """ - cmd = 'sysrc -v' + cmd = "sysrc -v" - if 'file' in kwargs: - cmd += ' -f '+kwargs['file'] + if "file" in kwargs: + cmd += " -f " + kwargs["file"] - if 'jail' in kwargs: - cmd += ' -j '+kwargs['jail'] + if "jail" in kwargs: + cmd += " -j " + kwargs["jail"] # This is here because the YAML parser likes to convert the string literals # YES, NO, Yes, No, True, False, etc. to boolean types. However, in this case, @@ -107,16 +107,16 @@ def set_(name, value, **kwargs): if type(value) == int: value = str(value) - cmd += ' '+name+"=\""+value+"\"" + cmd += " " + name + '="' + value + '"' - sysrcs = __salt__['cmd.run'](cmd) + sysrcs = __salt__["cmd.run"](cmd) ret = {} for sysrc in sysrcs.split("\n"): - rcfile = sysrc.split(': ')[0] - var = sysrc.split(': ')[1] - oldval = sysrc.split(': ')[2].strip().split("->")[0] - newval = sysrc.split(': ')[2].strip().split("->")[1] + rcfile = sysrc.split(": ")[0] + var = sysrc.split(": ")[1] + oldval = sysrc.split(": ")[2].strip().split("->")[0] + newval = sysrc.split(": ")[2].strip().split("->")[1] if rcfile not in ret: ret[rcfile] = {} ret[rcfile][var] = newval @@ -124,7 +124,7 @@ def set_(name, value, **kwargs): def remove(name, **kwargs): - ''' + """ Remove system rc configuration variables CLI Example: @@ -132,20 +132,20 @@ def remove(name, **kwargs): .. code-block:: bash salt '*' sysrc.remove name=sshd_enable - ''' + """ - cmd = 'sysrc -v' + cmd = "sysrc -v" - if 'file' in kwargs: - cmd += ' -f '+kwargs['file'] + if "file" in kwargs: + cmd += " -f " + kwargs["file"] - if 'jail' in kwargs: - cmd += ' -j '+kwargs['jail'] + if "jail" in kwargs: + cmd += " -j " + kwargs["jail"] - cmd += ' -x '+name + cmd += " -x " + name - sysrcs = __salt__['cmd.run'](cmd) + sysrcs = __salt__["cmd.run"](cmd) if "sysrc: unknown variable" in sysrcs: raise CommandExecutionError(sysrcs) else: - return name+" removed" + return name + " removed" diff --git a/salt/modules/system.py b/salt/modules/system.py index 75abaf45537..d9a92a46eb9 100644 --- a/salt/modules/system.py +++ b/salt/modules/system.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for reboot, shutdown, etc on POSIX-like systems. .. note:: @@ -11,13 +11,14 @@ Support for reboot, shutdown, etc on POSIX-like systems. while the wrapper script waits for user input. Calling them with ``salt`` will work as expected. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import os.path +import re # Import Python libs from datetime import datetime, timedelta, tzinfo -import re -import os.path # Import Salt libs import salt.utils.files @@ -26,28 +27,28 @@ import salt.utils.platform from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.utils.decorators import depends -__virtualname__ = 'system' +__virtualname__ = "system" def __virtual__(): - ''' + """ Only supported on POSIX-like systems Windows, Solaris, and Mac have their own modules - ''' + """ if salt.utils.platform.is_windows(): - return (False, 'This module is not available on Windows') + return (False, "This module is not available on Windows") if salt.utils.platform.is_darwin(): - return (False, 'This module is not available on Mac OS') + return (False, "This module is not available on Mac OS") if salt.utils.platform.is_sunos(): - return (False, 'This module is not available on SunOS') + return (False, "This module is not available on SunOS") return __virtualname__ def halt(): - ''' + """ Halt a running system CLI Example: @@ -55,14 +56,14 @@ def halt(): .. code-block:: bash salt '*' system.halt - ''' - cmd = ['halt'] - ret = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = ["halt"] + ret = __salt__["cmd.run"](cmd, python_shell=False) return ret def init(runlevel): - ''' + """ Change the system runlevel on sysV compatible systems CLI Example: @@ -70,14 +71,14 @@ def init(runlevel): .. code-block:: bash salt '*' system.init 3 - ''' - cmd = ['init', '{0}'.format(runlevel)] - ret = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = ["init", "{0}".format(runlevel)] + ret = __salt__["cmd.run"](cmd, python_shell=False) return ret def poweroff(): - ''' + """ Poweroff a running system CLI Example: @@ -85,14 +86,14 @@ def poweroff(): .. code-block:: bash salt '*' system.poweroff - ''' - cmd = ['poweroff'] - ret = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = ["poweroff"] + ret = __salt__["cmd.run"](cmd, python_shell=False) return ret def reboot(at_time=None): - ''' + """ Reboot the system at_time @@ -103,14 +104,14 @@ def reboot(at_time=None): .. code-block:: bash salt '*' system.reboot - ''' - cmd = ['shutdown', '-r', ('{0}'.format(at_time) if at_time else 'now')] - ret = __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = ["shutdown", "-r", ("{0}".format(at_time) if at_time else "now")] + ret = __salt__["cmd.run"](cmd, python_shell=False) return ret def shutdown(at_time=None): - ''' + """ Shutdown a running system at_time @@ -121,35 +122,37 @@ def shutdown(at_time=None): .. code-block:: bash salt '*' system.shutdown 5 - ''' - if (salt.utils.platform.is_freebsd() or - salt.utils.platform.is_netbsd() or - salt.utils.platform.is_openbsd()): + """ + if ( + salt.utils.platform.is_freebsd() + or salt.utils.platform.is_netbsd() + or salt.utils.platform.is_openbsd() + ): # these platforms don't power off by default when halted - flag = '-p' + flag = "-p" else: - flag = '-h' + flag = "-h" - cmd = ['shutdown', flag, ('{0}'.format(at_time) if at_time else 'now')] - ret = __salt__['cmd.run'](cmd, python_shell=False) + cmd = ["shutdown", flag, ("{0}".format(at_time) if at_time else "now")] + ret = __salt__["cmd.run"](cmd, python_shell=False) return ret def _date_bin_set_datetime(new_date): - ''' + """ set the system date/time using the date command Note using a strictly posix-compliant date binary we can only set the date up to the minute. - ''' - cmd = ['date'] + """ + cmd = ["date"] # if there is a timezone in the datetime object use that offset # This will modify the new_date to be the equivalent time in UTC if new_date.utcoffset() is not None: new_date = new_date - new_date.utcoffset() new_date = new_date.replace(tzinfo=_FixedOffset(0)) - cmd.append('-u') + cmd.append("-u") # the date can be set in the following format: # Note that setting the time with a resolution of seconds @@ -157,57 +160,60 @@ def _date_bin_set_datetime(new_date): # fails we will try again only using posix features # date MMDDhhmm[[CC]YY[.ss]] - non_posix = ("{1:02}{2:02}{3:02}{4:02}{0:04}.{5:02}" - .format(*new_date.timetuple())) + non_posix = "{1:02}{2:02}{3:02}{4:02}{0:04}.{5:02}".format(*new_date.timetuple()) non_posix_cmd = cmd + [non_posix] - ret_non_posix = __salt__['cmd.run_all'](non_posix_cmd, python_shell=False) - if ret_non_posix['retcode'] != 0: + ret_non_posix = __salt__["cmd.run_all"](non_posix_cmd, python_shell=False) + if ret_non_posix["retcode"] != 0: # We will now try the command again following posix # date MMDDhhmm[[CC]YY] posix = " {1:02}{2:02}{3:02}{4:02}{0:04}".format(*new_date.timetuple()) posix_cmd = cmd + [posix] - ret_posix = __salt__['cmd.run_all'](posix_cmd, python_shell=False) - if ret_posix['retcode'] != 0: + ret_posix = __salt__["cmd.run_all"](posix_cmd, python_shell=False) + if ret_posix["retcode"] != 0: # if both fail it's likely an invalid date string # so we will give back the error from the first attempt - msg = 'date failed: {0}'.format(ret_non_posix['stderr']) + msg = "date failed: {0}".format(ret_non_posix["stderr"]) raise CommandExecutionError(msg) return True def has_settable_hwclock(): - ''' + """ Returns True if the system has a hardware clock capable of being set from software. CLI Example: salt '*' system.has_settable_hwclock - ''' - if salt.utils.path.which_bin(['hwclock']) is not None: - res = __salt__['cmd.run_all']( - ['hwclock', '--test', '--systohc'], python_shell=False, - output_loglevel='quiet', ignore_retcode=True + """ + if salt.utils.path.which_bin(["hwclock"]) is not None: + res = __salt__["cmd.run_all"]( + ["hwclock", "--test", "--systohc"], + python_shell=False, + output_loglevel="quiet", + ignore_retcode=True, ) - return res['retcode'] == 0 + return res["retcode"] == 0 return False def _swclock_to_hwclock(): - ''' + """ Set hardware clock to value of software clock. - ''' - res = __salt__['cmd.run_all'](['hwclock', '--systohc'], python_shell=False) - if res['retcode'] != 0: - msg = 'hwclock failed to set hardware clock from software clock: {0}'.format(res['stderr']) + """ + res = __salt__["cmd.run_all"](["hwclock", "--systohc"], python_shell=False) + if res["retcode"] != 0: + msg = "hwclock failed to set hardware clock from software clock: {0}".format( + res["stderr"] + ) raise CommandExecutionError(msg) return True def _try_parse_datetime(time_str, fmts): - ''' + """ Attempts to parse the input time_str as a date. :param str time_str: A string representing the time @@ -215,7 +221,7 @@ def _try_parse_datetime(time_str, fmts): :return: Returns a datetime object if parsed properly. Otherwise None :rtype datetime: - ''' + """ result = None for fmt in fmts: try: @@ -227,16 +233,16 @@ def _try_parse_datetime(time_str, fmts): def _offset_to_min(utc_offset): - ''' + """ Helper function that converts the utc offset string into number of minutes offset. Input is in form "[+-]?HHMM". Example valid inputs are "+0500" "-0300" and "0800". These would return -300, 180, 480 respectively. - ''' + """ match = re.match(r"^([+-])?(\d\d)(\d\d)$", utc_offset) if not match: raise SaltInvocationError("Invalid UTC offset") - sign = -1 if match.group(1) == '-' else 1 + sign = -1 if match.group(1) == "-" else 1 hours_offset = int(match.group(2)) minutes_offset = int(match.group(3)) total_offset = sign * (hours_offset * 60 + minutes_offset) @@ -244,11 +250,11 @@ def _offset_to_min(utc_offset): def _get_offset_time(utc_offset): - ''' + """ Will return the current time adjusted using the input timezone offset. :rtype datetime: - ''' + """ if utc_offset is not None: minutes = _offset_to_min(utc_offset) offset = timedelta(minutes=minutes) @@ -260,7 +266,7 @@ def _get_offset_time(utc_offset): def get_system_time(utc_offset=None): - ''' + """ Get the system time. :param str utc_offset: The utc offset in 4 digit (+0600) format with an @@ -276,13 +282,13 @@ def get_system_time(utc_offset=None): .. code-block:: bash salt '*' system.get_system_time - ''' + """ offset_time = _get_offset_time(utc_offset) return datetime.strftime(offset_time, "%I:%M:%S %p") def set_system_time(newtime, utc_offset=None): - ''' + """ Set the system time. :param str newtime: @@ -310,18 +316,22 @@ def set_system_time(newtime, utc_offset=None): .. code-block:: bash salt '*' system.set_system_time "'11:20'" - ''' - fmts = ['%I:%M:%S %p', '%I:%M %p', '%H:%M:%S', '%H:%M'] + """ + fmts = ["%I:%M:%S %p", "%I:%M %p", "%H:%M:%S", "%H:%M"] dt_obj = _try_parse_datetime(newtime, fmts) if dt_obj is None: return False - return set_system_date_time(hours=dt_obj.hour, minutes=dt_obj.minute, - seconds=dt_obj.second, utc_offset=utc_offset) + return set_system_date_time( + hours=dt_obj.hour, + minutes=dt_obj.minute, + seconds=dt_obj.second, + utc_offset=utc_offset, + ) def get_system_date_time(utc_offset=None): - ''' + """ Get the system date/time. :param str utc_offset: The utc offset in 4 digit (+0600) format with an @@ -337,19 +347,21 @@ def get_system_date_time(utc_offset=None): .. code-block:: bash salt '*' system.get_system_date_time "'-0500'" - ''' + """ offset_time = _get_offset_time(utc_offset) return datetime.strftime(offset_time, "%Y-%m-%d %H:%M:%S") -def set_system_date_time(years=None, - months=None, - days=None, - hours=None, - minutes=None, - seconds=None, - utc_offset=None): - ''' +def set_system_date_time( + years=None, + months=None, + days=None, + hours=None, + minutes=None, + seconds=None, + utc_offset=None, +): + """ Set the system date and time. Each argument is an element of the date, but not required. If an element is not passed, the current system value for that element will be used. For example, if you don't pass the year, the @@ -378,7 +390,7 @@ def set_system_date_time(years=None, .. code-block:: bash salt '*' system.set_system_date_time 2015 5 12 11 37 53 "'-0500'" - ''' + """ # Get the current date/time date_time = _get_offset_time(utc_offset) @@ -397,8 +409,9 @@ def set_system_date_time(years=None, seconds = date_time.second try: - new_datetime = datetime(years, months, days, hours, minutes, seconds, 0, - date_time.tzinfo) + new_datetime = datetime( + years, months, days, hours, minutes, seconds, 0, date_time.tzinfo + ) except ValueError as err: raise SaltInvocationError(err.message) @@ -414,7 +427,7 @@ def set_system_date_time(years=None, def get_system_date(utc_offset=None): - ''' + """ Get the system date :param str utc_offset: The utc offset in 4 digit (+0600) format with an @@ -430,13 +443,13 @@ def get_system_date(utc_offset=None): .. code-block:: bash salt '*' system.get_system_date - ''' + """ offset_time = _get_offset_time(utc_offset) return datetime.strftime(offset_time, "%a %m/%d/%Y") def set_system_date(newdate, utc_offset=None): - ''' + """ Set the system date. Use <mm-dd-yy> format for the date. :param str newdate: @@ -454,9 +467,8 @@ def set_system_date(newdate, utc_offset=None): .. code-block:: bash salt '*' system.set_system_date '03-28-13' - ''' - fmts = ['%Y-%m-%d', '%m-%d-%Y', '%m-%d-%y', - '%m/%d/%Y', '%m/%d/%y', '%Y/%m/%d'] + """ + fmts = ["%Y-%m-%d", "%m-%d-%Y", "%m-%d-%y", "%m/%d/%Y", "%m/%d/%y", "%Y/%m/%d"] # Get date/time object from newdate dt_obj = _try_parse_datetime(newdate, fmts) @@ -464,8 +476,9 @@ def set_system_date(newdate, utc_offset=None): raise SaltInvocationError("Invalid date format") # Set time using set_system_date_time() - return set_system_date_time(years=dt_obj.year, months=dt_obj.month, - days=dt_obj.day, utc_offset=utc_offset) + return set_system_date_time( + years=dt_obj.year, months=dt_obj.month, days=dt_obj.day, utc_offset=utc_offset + ) # Class from: <https://docs.python.org/2.7/library/datetime.html> @@ -473,10 +486,11 @@ def set_system_date(newdate, utc_offset=None): # A class building tzinfo objects for fixed-offset time zones. # Note that _FixedOffset(0) is a way to build a UTC tzinfo object. + class _FixedOffset(tzinfo): - ''' + """ Fixed offset in minutes east from UTC. - ''' + """ def __init__(self, offset): super(_FixedOffset, self).__init__() @@ -493,16 +507,16 @@ class _FixedOffset(tzinfo): def _strip_quotes(str_q): - ''' + """ Helper function to strip off the ' or " off of a string - ''' + """ if str_q[0] == str_q[-1] and str_q.startswith(("'", '"')): return str_q[1:-1] return str_q def get_computer_desc(): - ''' + """ Get PRETTY_HOSTNAME value stored in /etc/machine-info If this file doesn't exist or the variable doesn't exist return False. @@ -515,18 +529,17 @@ def get_computer_desc(): .. code-block:: bash salt '*' system.get_computer_desc - ''' - hostname_cmd = salt.utils.path.which('hostnamectl') + """ + hostname_cmd = salt.utils.path.which("hostnamectl") if hostname_cmd: - desc = __salt__['cmd.run']( - [hostname_cmd, 'status', '--pretty'], - python_shell=False + desc = __salt__["cmd.run"]( + [hostname_cmd, "status", "--pretty"], python_shell=False ) else: desc = None - pattern = re.compile(r'^\s*PRETTY_HOSTNAME=(.*)$') + pattern = re.compile(r"^\s*PRETTY_HOSTNAME=(.*)$") try: - with salt.utils.files.fopen('/etc/machine-info', 'r') as mach_info: + with salt.utils.files.fopen("/etc/machine-info", "r") as mach_info: for line in mach_info.readlines(): line = salt.utils.stringutils.to_unicode(line) match = pattern.match(line) @@ -540,11 +553,11 @@ def get_computer_desc(): if desc is None: return False - return desc.replace(r'\"', r'"').replace(r'\n', '\n').replace(r'\t', '\t') + return desc.replace(r"\"", r'"').replace(r"\n", "\n").replace(r"\t", "\t") def set_computer_desc(desc): - ''' + """ Set PRETTY_HOSTNAME value stored in /etc/machine-info This will create the file if it does not exist. If it is unable to create or modify this file returns False. @@ -557,26 +570,29 @@ def set_computer_desc(desc): .. code-block:: bash salt '*' system.set_computer_desc "Michael's laptop" - ''' - desc = salt.utils.stringutils.to_unicode( - desc).replace('"', r'\"').replace('\n', r'\n').replace('\t', r'\t') + """ + desc = ( + salt.utils.stringutils.to_unicode(desc) + .replace('"', r"\"") + .replace("\n", r"\n") + .replace("\t", r"\t") + ) - hostname_cmd = salt.utils.path.which('hostnamectl') + hostname_cmd = salt.utils.path.which("hostnamectl") if hostname_cmd: - result = __salt__['cmd.retcode']( - [hostname_cmd, 'set-hostname', '--pretty', desc], - python_shell=False + result = __salt__["cmd.retcode"]( + [hostname_cmd, "set-hostname", "--pretty", desc], python_shell=False ) return True if result == 0 else False - if not os.path.isfile('/etc/machine-info'): - with salt.utils.files.fopen('/etc/machine-info', 'w'): + if not os.path.isfile("/etc/machine-info"): + with salt.utils.files.fopen("/etc/machine-info", "w"): pass - pattern = re.compile(r'^\s*PRETTY_HOSTNAME=(.*)$') + pattern = re.compile(r"^\s*PRETTY_HOSTNAME=(.*)$") new_line = salt.utils.stringutils.to_str('PRETTY_HOSTNAME="{0}"'.format(desc)) try: - with salt.utils.files.fopen('/etc/machine-info', 'r+') as mach_info: + with salt.utils.files.fopen("/etc/machine-info", "r+") as mach_info: lines = mach_info.readlines() for i, line in enumerate(lines): if pattern.match(salt.utils.stringutils.to_unicode(line)): @@ -595,7 +611,7 @@ def set_computer_desc(desc): def set_computer_name(hostname): - ''' + """ Modify hostname. CLI Example: @@ -603,12 +619,12 @@ def set_computer_name(hostname): .. code-block:: bash salt '*' system.set_computer_name master.saltstack.com - ''' - return __salt__['network.mod_hostname'](hostname) + """ + return __salt__["network.mod_hostname"](hostname) def get_computer_name(): - ''' + """ Get hostname. CLI Example: @@ -616,23 +632,23 @@ def get_computer_name(): .. code-block:: bash salt '*' network.get_hostname - ''' - return __salt__['network.get_hostname']() + """ + return __salt__["network.get_hostname"]() def _is_nilrt_family(): - ''' + """ Determine whether the minion is running on NI Linux RT - ''' - return __grains__.get('os_family') == 'NILinuxRT' + """ + return __grains__.get("os_family") == "NILinuxRT" -NILRT_REBOOT_WITNESS_PATH = '/var/volatile/tmp/salt/reboot_witnessed' +NILRT_REBOOT_WITNESS_PATH = "/var/volatile/tmp/salt/reboot_witnessed" -@depends('_is_nilrt_family') +@depends("_is_nilrt_family") def set_reboot_required_witnessed(): - ''' + """ This function is used to remember that an event indicating that a reboot is required was witnessed. This function writes to a temporary filesystem so the event gets cleared upon reboot. @@ -643,25 +659,26 @@ def set_reboot_required_witnessed(): .. code-block:: bash salt '*' system.set_reboot_required_witnessed - ''' + """ errcode = -1 dir_path = os.path.dirname(NILRT_REBOOT_WITNESS_PATH) if not os.path.exists(dir_path): try: os.makedirs(dir_path) except OSError as ex: - raise SaltInvocationError('Error creating {0} (-{1}): {2}' - .format(dir_path, ex.errno, ex.strerror)) + raise SaltInvocationError( + "Error creating {0} (-{1}): {2}".format(dir_path, ex.errno, ex.strerror) + ) - rdict = __salt__['cmd.run_all']('touch {0}'.format(NILRT_REBOOT_WITNESS_PATH)) - errcode = rdict['retcode'] + rdict = __salt__["cmd.run_all"]("touch {0}".format(NILRT_REBOOT_WITNESS_PATH)) + errcode = rdict["retcode"] return errcode == 0 -@depends('_is_nilrt_family') +@depends("_is_nilrt_family") def get_reboot_required_witnessed(): - ''' + """ Determine if at any time during the current boot session the salt minion witnessed an event indicating that a reboot is required. @@ -673,5 +690,5 @@ def get_reboot_required_witnessed(): .. code-block:: bash salt '*' system.get_reboot_required_witnessed - ''' + """ return os.path.exists(NILRT_REBOOT_WITNESS_PATH) diff --git a/salt/modules/system_profiler.py b/salt/modules/system_profiler.py index 60fa44e4e02..f9062a64844 100644 --- a/salt/modules/system_profiler.py +++ b/salt/modules/system_profiler.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" System Profiler Module Interface with macOS's command-line System Profiler utility to get @@ -7,39 +7,44 @@ information about package receipts and installed applications. .. versionadded:: 2015.5.0 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import plistlib import subprocess + import salt.utils.path from salt.ext import six -PROFILER_BINARY = '/usr/sbin/system_profiler' +PROFILER_BINARY = "/usr/sbin/system_profiler" def __virtual__(): - ''' + """ Check to see if the system_profiler binary is available - ''' - PROFILER_BINARY = salt.utils.path.which('system_profiler') + """ + PROFILER_BINARY = salt.utils.path.which("system_profiler") if PROFILER_BINARY: return True - return (False, 'The system_profiler execution module cannot be loaded: ' - 'system_profiler unavailable.') + return ( + False, + "The system_profiler execution module cannot be loaded: " + "system_profiler unavailable.", + ) def _call_system_profiler(datatype): - ''' + """ Call out to system_profiler. Return a dictionary of the stuff we are interested in. - ''' + """ p = subprocess.Popen( - [PROFILER_BINARY, '-detailLevel', 'full', - '-xml', datatype], stdout=subprocess.PIPE) + [PROFILER_BINARY, "-detailLevel", "full", "-xml", datatype], + stdout=subprocess.PIPE, + ) (sysprofresults, sysprof_stderr) = p.communicate(input=None) if six.PY2: @@ -48,7 +53,7 @@ def _call_system_profiler(datatype): plist = plistlib.readPlistFromBytes(sysprofresults) try: - apps = plist[0]['_items'] + apps = plist[0]["_items"] except (IndexError, KeyError): apps = [] @@ -56,7 +61,7 @@ def _call_system_profiler(datatype): def receipts(): - ''' + """ Return the results of a call to ``system_profiler -xml -detail full SPInstallHistoryDataType`` as a dictionary. Top-level keys of the dictionary @@ -69,34 +74,37 @@ def receipts(): .. code-block:: bash salt '*' systemprofiler.receipts - ''' + """ - apps = _call_system_profiler('SPInstallHistoryDataType') + apps = _call_system_profiler("SPInstallHistoryDataType") appdict = {} for a in apps: details = dict(a) - details.pop('_name') - if 'install_date' in details: - details['install_date'] = details['install_date'].strftime('%Y-%m-%d %H:%M:%S') - if 'info' in details: + details.pop("_name") + if "install_date" in details: + details["install_date"] = details["install_date"].strftime( + "%Y-%m-%d %H:%M:%S" + ) + if "info" in details: try: - details['info'] = '{0}: {1}'.format(details['info'][0], - details['info'][1].strftime('%Y-%m-%d %H:%M:%S')) + details["info"] = "{0}: {1}".format( + details["info"][0], details["info"][1].strftime("%Y-%m-%d %H:%M:%S") + ) except (IndexError, AttributeError): pass - if a['_name'] not in appdict: - appdict[a['_name']] = [] + if a["_name"] not in appdict: + appdict[a["_name"]] = [] - appdict[a['_name']].append(details) + appdict[a["_name"]].append(details) return appdict def applications(): - ''' + """ Return the results of a call to ``system_profiler -xml -detail full SPApplicationsDataType`` as a dictionary. Top-level keys of the dictionary @@ -112,27 +120,30 @@ def applications(): .. code-block:: bash salt '*' systemprofiler.applications - ''' + """ - apps = _call_system_profiler('SPApplicationsDataType') + apps = _call_system_profiler("SPApplicationsDataType") appdict = {} for a in apps: details = dict(a) - details.pop('_name') - if 'lastModified' in details: - details['lastModified'] = details['lastModified'].strftime('%Y-%m-%d %H:%M:%S') - if 'info' in details: + details.pop("_name") + if "lastModified" in details: + details["lastModified"] = details["lastModified"].strftime( + "%Y-%m-%d %H:%M:%S" + ) + if "info" in details: try: - details['info'] = '{0}: {1}'.format(details['info'][0], - details['info'][1].strftime('%Y-%m-%d %H:%M:%S')) + details["info"] = "{0}: {1}".format( + details["info"][0], details["info"][1].strftime("%Y-%m-%d %H:%M:%S") + ) except (IndexError, AttributeError): pass - if a['_name'] not in appdict: - appdict[a['_name']] = [] + if a["_name"] not in appdict: + appdict[a["_name"]] = [] - appdict[a['_name']].append(details) + appdict[a["_name"]].append(details) return appdict diff --git a/salt/modules/systemd_service.py b/salt/modules/systemd_service.py index 743758bf9c5..bb97987fc0e 100644 --- a/salt/modules/systemd_service.py +++ b/salt/modules/systemd_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Provides the service module for systemd .. versionadded:: 0.10.0 @@ -9,14 +9,15 @@ Provides the service module for systemd minion, and it is using a different module (or gives an error similar to *'service.start' is not available*), see :ref:`here <module-provider-override>`. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno +import fnmatch import glob import logging import os -import fnmatch import re import shlex import time @@ -35,41 +36,49 @@ from salt.ext import six log = logging.getLogger(__name__) __func_alias__ = { - 'reload_': 'reload', - 'unmask_': 'unmask', + "reload_": "reload", + "unmask_": "unmask", } -SYSTEM_CONFIG_PATHS = ('/lib/systemd/system', '/usr/lib/systemd/system') -LOCAL_CONFIG_PATH = '/etc/systemd/system' -INITSCRIPT_PATH = '/etc/init.d' -VALID_UNIT_TYPES = ('service', 'socket', 'device', 'mount', 'automount', - 'swap', 'target', 'path', 'timer') +SYSTEM_CONFIG_PATHS = ("/lib/systemd/system", "/usr/lib/systemd/system") +LOCAL_CONFIG_PATH = "/etc/systemd/system" +INITSCRIPT_PATH = "/etc/init.d" +VALID_UNIT_TYPES = ( + "service", + "socket", + "device", + "mount", + "automount", + "swap", + "target", + "path", + "timer", +) # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" # Disable check for string substitution # pylint: disable=E1321 def __virtual__(): - ''' + """ Only work on systems that have been booted with systemd - ''' - if __grains__['kernel'] == 'Linux' \ - and salt.utils.systemd.booted(__context__): + """ + if __grains__["kernel"] == "Linux" and salt.utils.systemd.booted(__context__): return __virtualname__ return ( False, - 'The systemd execution module failed to load: only available on Linux ' - 'systems which have been booted with systemd.' + "The systemd execution module failed to load: only available on Linux " + "systems which have been booted with systemd.", ) def _root(path, root): - ''' + """ Relocate an absolute path to a new root directory. - ''' + """ if root: return os.path.join(root, os.path.relpath(path, os.path.sep)) else: @@ -77,21 +86,21 @@ def _root(path, root): def _canonical_unit_name(name): - ''' + """ Build a canonical unit name treating unit names without one of the valid suffixes as a service. - ''' + """ if not isinstance(name, six.string_types): name = six.text_type(name) if any(name.endswith(suffix) for suffix in VALID_UNIT_TYPES): return name - return '%s.service' % name + return "%s.service" % name def _check_available(name): - ''' + """ Returns boolean telling whether or not the named service is available - ''' + """ _status = _systemctl_status(name) sd_version = salt.utils.systemd.version(__context__) if sd_version is not None and sd_version >= 231: @@ -101,32 +110,30 @@ def _check_available(name): # parsing the "systemctl status" output. # See: https://github.com/systemd/systemd/pull/3385 # Also: https://github.com/systemd/systemd/commit/3dced37 - return 0 <= _status['retcode'] < 4 + return 0 <= _status["retcode"] < 4 - out = _status['stdout'].lower() - if 'could not be found' in out: + out = _status["stdout"].lower() + if "could not be found" in out: # Catch cases where the systemd version is < 231 but the return code # and output changes have been backported (e.g. RHEL 7.3). return False - for line in salt.utils.itertools.split(out, '\n'): - match = re.match(r'\s+loaded:\s+(\S+)', line) + for line in salt.utils.itertools.split(out, "\n"): + match = re.match(r"\s+loaded:\s+(\S+)", line) if match: - ret = match.group(1) != 'not-found' + ret = match.group(1) != "not-found" break else: - raise CommandExecutionError( - 'Failed to get information on unit \'%s\'' % name - ) + raise CommandExecutionError("Failed to get information on unit '%s'" % name) return ret def _check_for_unit_changes(name): - ''' + """ Check for modified/updated unit files, and run a daemon-reload if any are found. - ''' - contextkey = 'systemd._check_for_unit_changes.{0}'.format(name) + """ + contextkey = "systemd._check_for_unit_changes.{0}".format(name) if contextkey not in __context__: if _untracked_custom_unit_found(name) or _unit_file_changed(name): systemctl_reload() @@ -135,10 +142,10 @@ def _check_for_unit_changes(name): def _check_unmask(name, unmask, unmask_runtime, root=None): - ''' + """ Common code for conditionally removing masks before making changes to a service's state. - ''' + """ if unmask: unmask_(name, runtime=False, root=root) if unmask_runtime: @@ -146,43 +153,43 @@ def _check_unmask(name, unmask, unmask_runtime, root=None): def _clear_context(): - ''' + """ Remove context - ''' + """ # Using list() here because modifying a dictionary during iteration will # raise a RuntimeError. for key in list(__context__): try: - if key.startswith('systemd._systemctl_status.'): + if key.startswith("systemd._systemctl_status."): __context__.pop(key) except AttributeError: continue def _default_runlevel(): - ''' + """ Try to figure out the default runlevel. It is kept in /etc/init/rc-sysinit.conf, but can be overridden with entries in /etc/inittab, or via the kernel command-line at boot - ''' + """ # Try to get the "main" default. If this fails, throw up our # hands and just guess "2", because things are horribly broken try: - with salt.utils.files.fopen('/etc/init/rc-sysinit.conf') as fp_: + with salt.utils.files.fopen("/etc/init/rc-sysinit.conf") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('env DEFAULT_RUNLEVEL'): - runlevel = line.split('=')[-1].strip() + if line.startswith("env DEFAULT_RUNLEVEL"): + runlevel = line.split("=")[-1].strip() except Exception: # pylint: disable=broad-except - return '2' + return "2" # Look for an optional "legacy" override in /etc/inittab try: - with salt.utils.files.fopen('/etc/inittab') as fp_: + with salt.utils.files.fopen("/etc/inittab") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if not line.startswith('#') and 'initdefault' in line: - runlevel = line.split(':')[1] + if not line.startswith("#") and "initdefault" in line: + runlevel = line.split(":")[1] except Exception: # pylint: disable=broad-except pass @@ -190,8 +197,9 @@ def _default_runlevel(): # Kinky. try: valid_strings = set( - ('0', '1', '2', '3', '4', '5', '6', 's', 'S', '-s', 'single')) - with salt.utils.files.fopen('/proc/cmdline') as fp_: + ("0", "1", "2", "3", "4", "5", "6", "s", "S", "-s", "single") + ) + with salt.utils.files.fopen("/proc/cmdline") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) for arg in line.strip().split(): @@ -205,9 +213,9 @@ def _default_runlevel(): def _get_systemd_services(root): - ''' + """ Use os.listdir() to get all the unit files - ''' + """ ret = set() for path in SYSTEM_CONFIG_PATHS + (LOCAL_CONFIG_PATH,): # Make sure user has access to the path, and if the path is a @@ -217,18 +225,18 @@ def _get_systemd_services(root): if os.access(path, os.R_OK) and not os.path.islink(path): for fullname in os.listdir(path): try: - unit_name, unit_type = fullname.rsplit('.', 1) + unit_name, unit_type = fullname.rsplit(".", 1) except ValueError: continue if unit_type in VALID_UNIT_TYPES: - ret.add(unit_name if unit_type == 'service' else fullname) + ret.add(unit_name if unit_type == "service" else fullname) return ret def _get_sysv_services(root, systemd_services=None): - ''' + """ Use os.listdir() and os.access() to get all the initscripts - ''' + """ initscript_path = _root(INITSCRIPT_PATH, root) try: sysv_services = os.listdir(initscript_path) @@ -237,14 +245,14 @@ def _get_sysv_services(root, systemd_services=None): pass elif exc.errno == errno.EACCES: log.error( - 'Unable to check sysvinit scripts, permission denied to %s', - initscript_path + "Unable to check sysvinit scripts, permission denied to %s", + initscript_path, ) else: log.error( - 'Error %d encountered trying to check sysvinit scripts: %s', + "Error %d encountered trying to check sysvinit scripts: %s", exc.errno, - exc.strerror + exc.strerror, ) return [] @@ -256,9 +264,10 @@ def _get_sysv_services(root, systemd_services=None): if os.access(os.path.join(initscript_path, sysv_service), os.X_OK): if sysv_service in systemd_services: log.debug( - 'sysvinit script \'%s\' found, but systemd unit ' - '\'%s.service\' already exists', - sysv_service, sysv_service + "sysvinit script '%s' found, but systemd unit " + "'%s.service' already exists", + sysv_service, + sysv_service, ) continue ret.append(sysv_service) @@ -266,21 +275,21 @@ def _get_sysv_services(root, systemd_services=None): def _get_service_exec(): - ''' + """ Returns the path to the sysv service manager (either update-rc.d or chkconfig) - ''' - contextkey = 'systemd._get_service_exec' + """ + contextkey = "systemd._get_service_exec" if contextkey not in __context__: - executables = ('update-rc.d', 'chkconfig') + executables = ("update-rc.d", "chkconfig") for executable in executables: service_exec = salt.utils.path.which(executable) if service_exec is not None: break else: raise CommandExecutionError( - 'Unable to find sysv service manager (tried {0})'.format( - ', '.join(executables) + "Unable to find sysv service manager (tried {0})".format( + ", ".join(executables) ) ) __context__[contextkey] = service_exec @@ -288,13 +297,13 @@ def _get_service_exec(): def _runlevel(): - ''' + """ Return the current runlevel - ''' - contextkey = 'systemd._runlevel' + """ + contextkey = "systemd._runlevel" if contextkey in __context__: return __context__[contextkey] - out = __salt__['cmd.run']('runlevel', python_shell=False, ignore_retcode=True) + out = __salt__["cmd.run"]("runlevel", python_shell=False, ignore_retcode=True) try: ret = out.split()[1] except IndexError: @@ -305,95 +314,96 @@ def _runlevel(): def _strip_scope(msg): - ''' + """ Strip unnecessary message about running the command with --scope from stderr so that we can raise an exception with the remaining stderr text. - ''' + """ ret = [] for line in msg.splitlines(): - if not line.endswith('.scope'): + if not line.endswith(".scope"): ret.append(line) - return '\n'.join(ret).strip() + return "\n".join(ret).strip() -def _systemctl_cmd(action, name=None, systemd_scope=False, no_block=False, - root=None): - ''' +def _systemctl_cmd(action, name=None, systemd_scope=False, no_block=False, root=None): + """ Build a systemctl command line. Treat unit names without one of the valid suffixes as a service. - ''' + """ ret = [] - if systemd_scope \ - and salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - ret.extend(['systemd-run', '--scope']) - ret.append('systemctl') + if ( + systemd_scope + and salt.utils.systemd.has_scope(__context__) + and __salt__["config.get"]("systemd.scope", True) + ): + ret.extend(["systemd-run", "--scope"]) + ret.append("systemctl") if no_block: - ret.append('--no-block') + ret.append("--no-block") if root: - ret.extend(['--root', root]) + ret.extend(["--root", root]) if isinstance(action, six.string_types): action = shlex.split(action) ret.extend(action) if name is not None: ret.append(_canonical_unit_name(name)) - if 'status' in ret: - ret.extend(['-n', '0']) + if "status" in ret: + ret.extend(["-n", "0"]) return ret def _systemctl_status(name): - ''' + """ Helper function which leverages __context__ to keep from running 'systemctl status' more than once. - ''' - contextkey = 'systemd._systemctl_status.%s' % name + """ + contextkey = "systemd._systemctl_status.%s" % name if contextkey in __context__: return __context__[contextkey] - __context__[contextkey] = __salt__['cmd.run_all']( - _systemctl_cmd('status', name), + __context__[contextkey] = __salt__["cmd.run_all"]( + _systemctl_cmd("status", name), python_shell=False, redirect_stderr=True, - ignore_retcode=True + ignore_retcode=True, ) return __context__[contextkey] def _sysv_enabled(name, root): - ''' + """ A System-V style service is assumed disabled if the "startup" symlink (starts with "S") to its script is found in /etc/init.d in the current runlevel. - ''' + """ # Find exact match (disambiguate matches like "S01anacron" for cron) - rc = _root('/etc/rc{}.d/S*{}'.format(_runlevel(), name), root) + rc = _root("/etc/rc{}.d/S*{}".format(_runlevel(), name), root) for match in glob.glob(rc): - if re.match(r'S\d{,2}%s' % name, os.path.basename(match)): + if re.match(r"S\d{,2}%s" % name, os.path.basename(match)): return True return False def _untracked_custom_unit_found(name, root=None): - ''' + """ If the passed service name is not available, but a unit file exist in /etc/systemd/system, return True. Otherwise, return False. - ''' - system = _root('/etc/systemd/system', root) + """ + system = _root("/etc/systemd/system", root) unit_path = os.path.join(system, _canonical_unit_name(name)) return os.access(unit_path, os.R_OK) and not _check_available(name) def _unit_file_changed(name): - ''' + """ Returns True if systemctl reports that the unit file has changed, otherwise returns False. - ''' - status = _systemctl_status(name)['stdout'].lower() + """ + status = _systemctl_status(name)["stdout"].lower() return "'systemctl daemon-reload'" in status def systemctl_reload(): - ''' + """ .. versionadded:: 0.15.0 Reloads systemctl, an action needed whenever unit files are updated. @@ -403,21 +413,22 @@ def systemctl_reload(): .. code-block:: bash salt '*' service.systemctl_reload - ''' - out = __salt__['cmd.run_all']( - _systemctl_cmd('--system daemon-reload'), + """ + out = __salt__["cmd.run_all"]( + _systemctl_cmd("--system daemon-reload"), python_shell=False, - redirect_stderr=True) - if out['retcode'] != 0: + redirect_stderr=True, + ) + if out["retcode"] != 0: raise CommandExecutionError( - 'Problem performing systemctl daemon-reload: %s' % out['stdout'] + "Problem performing systemctl daemon-reload: %s" % out["stdout"] ) _clear_context() return True def get_running(): - ''' + """ Return a list of all running services, so far as systemd is concerned CLI Example: @@ -425,14 +436,15 @@ def get_running(): .. code-block:: bash salt '*' service.get_running - ''' + """ ret = set() # Get running systemd units - out = __salt__['cmd.run']( - _systemctl_cmd('--full --no-legend --no-pager'), + out = __salt__["cmd.run"]( + _systemctl_cmd("--full --no-legend --no-pager"), python_shell=False, - ignore_retcode=True) - for line in salt.utils.itertools.split(out, '\n'): + ignore_retcode=True, + ) + for line in salt.utils.itertools.split(out, "\n"): try: comps = line.strip().split() fullname = comps[0] @@ -442,20 +454,20 @@ def get_running(): log.error(exc) continue else: - if active_state != 'running': + if active_state != "running": continue try: - unit_name, unit_type = fullname.rsplit('.', 1) + unit_name, unit_type = fullname.rsplit(".", 1) except ValueError: continue if unit_type in VALID_UNIT_TYPES: - ret.add(unit_name if unit_type == 'service' else fullname) + ret.add(unit_name if unit_type == "service" else fullname) return sorted(ret) def get_enabled(root=None): - ''' + """ Return a list of all enabled services root @@ -466,39 +478,37 @@ def get_enabled(root=None): .. code-block:: bash salt '*' service.get_enabled - ''' + """ ret = set() # Get enabled systemd units. Can't use --state=enabled here because it's # not present until systemd 216. - out = __salt__['cmd.run']( - _systemctl_cmd('--full --no-legend --no-pager list-unit-files', - root=root), + out = __salt__["cmd.run"]( + _systemctl_cmd("--full --no-legend --no-pager list-unit-files", root=root), python_shell=False, - ignore_retcode=True) - for line in salt.utils.itertools.split(out, '\n'): + ignore_retcode=True, + ) + for line in salt.utils.itertools.split(out, "\n"): try: fullname, unit_state = line.strip().split(None, 1) except ValueError: continue else: - if unit_state != 'enabled': + if unit_state != "enabled": continue try: - unit_name, unit_type = fullname.rsplit('.', 1) + unit_name, unit_type = fullname.rsplit(".", 1) except ValueError: continue if unit_type in VALID_UNIT_TYPES: - ret.add(unit_name if unit_type == 'service' else fullname) + ret.add(unit_name if unit_type == "service" else fullname) # Add in any sysvinit services that are enabled - ret.update(set( - [x for x in _get_sysv_services(root) if _sysv_enabled(x, root)] - )) + ret.update(set([x for x in _get_sysv_services(root) if _sysv_enabled(x, root)])) return sorted(ret) def get_disabled(root=None): - ''' + """ Return a list of all disabled services root @@ -509,39 +519,37 @@ def get_disabled(root=None): .. code-block:: bash salt '*' service.get_disabled - ''' + """ ret = set() # Get disabled systemd units. Can't use --state=disabled here because it's # not present until systemd 216. - out = __salt__['cmd.run']( - _systemctl_cmd('--full --no-legend --no-pager list-unit-files', - root=root), + out = __salt__["cmd.run"]( + _systemctl_cmd("--full --no-legend --no-pager list-unit-files", root=root), python_shell=False, - ignore_retcode=True) - for line in salt.utils.itertools.split(out, '\n'): + ignore_retcode=True, + ) + for line in salt.utils.itertools.split(out, "\n"): try: fullname, unit_state = line.strip().split(None, 1) except ValueError: continue else: - if unit_state != 'disabled': + if unit_state != "disabled": continue try: - unit_name, unit_type = fullname.rsplit('.', 1) + unit_name, unit_type = fullname.rsplit(".", 1) except ValueError: continue if unit_type in VALID_UNIT_TYPES: - ret.add(unit_name if unit_type == 'service' else fullname) + ret.add(unit_name if unit_type == "service" else fullname) # Add in any sysvinit services that are disabled - ret.update(set( - [x for x in _get_sysv_services(root) if not _sysv_enabled(x, root)] - )) + ret.update(set([x for x in _get_sysv_services(root) if not _sysv_enabled(x, root)])) return sorted(ret) def get_static(root=None): - ''' + """ .. versionadded:: 2015.8.5 Return a list of all static services @@ -554,36 +562,36 @@ def get_static(root=None): .. code-block:: bash salt '*' service.get_static - ''' + """ ret = set() # Get static systemd units. Can't use --state=static here because it's # not present until systemd 216. - out = __salt__['cmd.run']( - _systemctl_cmd('--full --no-legend --no-pager list-unit-files', - root=root), + out = __salt__["cmd.run"]( + _systemctl_cmd("--full --no-legend --no-pager list-unit-files", root=root), python_shell=False, - ignore_retcode=True) - for line in salt.utils.itertools.split(out, '\n'): + ignore_retcode=True, + ) + for line in salt.utils.itertools.split(out, "\n"): try: fullname, unit_state = line.strip().split(None, 1) except ValueError: continue else: - if unit_state != 'static': + if unit_state != "static": continue try: - unit_name, unit_type = fullname.rsplit('.', 1) + unit_name, unit_type = fullname.rsplit(".", 1) except ValueError: continue if unit_type in VALID_UNIT_TYPES: - ret.add(unit_name if unit_type == 'service' else fullname) + ret.add(unit_name if unit_type == "service" else fullname) # sysvinit services cannot be static return sorted(ret) def get_all(root=None): - ''' + """ Return a list of all available services root @@ -594,14 +602,14 @@ def get_all(root=None): .. code-block:: bash salt '*' service.get_all - ''' + """ ret = _get_systemd_services(root) ret.update(set(_get_sysv_services(root, systemd_services=ret))) return sorted(ret) def available(name): - ''' + """ .. versionadded:: 0.10.4 Check that the given service is available taking into account template @@ -612,13 +620,13 @@ def available(name): .. code-block:: bash salt '*' service.available sshd - ''' + """ _check_for_unit_changes(name) return _check_available(name) def missing(name): - ''' + """ .. versionadded:: 2014.1.0 The inverse of :py:func:`service.available @@ -630,12 +638,12 @@ def missing(name): .. code-block:: bash salt '*' service.missing sshd - ''' + """ return not available(name) def unmask_(name, runtime=False, root=None): - ''' + """ .. versionadded:: 2015.5.0 .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to @@ -670,27 +678,27 @@ def unmask_(name, runtime=False, root=None): salt '*' service.unmask foo salt '*' service.unmask foo runtime=True - ''' + """ _check_for_unit_changes(name) if not masked(name, runtime, root=root): - log.debug('Service \'%s\' is not %smasked', - name, 'runtime-' if runtime else '') + log.debug("Service '%s' is not %smasked", name, "runtime-" if runtime else "") return True - cmd = 'unmask --runtime' if runtime else 'unmask' - out = __salt__['cmd.run_all']( + cmd = "unmask --runtime" if runtime else "unmask" + out = __salt__["cmd.run_all"]( _systemctl_cmd(cmd, name, systemd_scope=True, root=root), python_shell=False, - redirect_stderr=True) + redirect_stderr=True, + ) - if out['retcode'] != 0: - raise CommandExecutionError('Failed to unmask service \'%s\'' % name) + if out["retcode"] != 0: + raise CommandExecutionError("Failed to unmask service '%s'" % name) return True def mask(name, runtime=False, root=None): - ''' + """ .. versionadded:: 2015.5.0 .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to @@ -719,26 +727,26 @@ def mask(name, runtime=False, root=None): salt '*' service.mask foo salt '*' service.mask foo runtime=True - ''' + """ _check_for_unit_changes(name) - cmd = 'mask --runtime' if runtime else 'mask' - out = __salt__['cmd.run_all']( + cmd = "mask --runtime" if runtime else "mask" + out = __salt__["cmd.run_all"]( _systemctl_cmd(cmd, name, systemd_scope=True, root=root), python_shell=False, - redirect_stderr=True) + redirect_stderr=True, + ) - if out['retcode'] != 0: + if out["retcode"] != 0: raise CommandExecutionError( - 'Failed to mask service \'%s\'' % name, - info=out['stdout'] + "Failed to mask service '%s'" % name, info=out["stdout"] ) return True def masked(name, runtime=False, root=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2015.8.5 The return data for this function has changed. If the service is @@ -776,33 +784,34 @@ def masked(name, runtime=False, root=None): salt '*' service.masked foo salt '*' service.masked foo runtime=True - ''' + """ _check_for_unit_changes(name) - root_dir = _root('/run' if runtime else '/etc', root) - link_path = os.path.join(root_dir, - 'systemd', - 'system', - _canonical_unit_name(name)) + root_dir = _root("/run" if runtime else "/etc", root) + link_path = os.path.join(root_dir, "systemd", "system", _canonical_unit_name(name)) try: - return os.readlink(link_path) == '/dev/null' + return os.readlink(link_path) == "/dev/null" except OSError as exc: if exc.errno == errno.ENOENT: log.trace( - 'Path %s does not exist. This is normal if service \'%s\' is ' - 'not masked or does not exist.', link_path, name + "Path %s does not exist. This is normal if service '%s' is " + "not masked or does not exist.", + link_path, + name, ) elif exc.errno == errno.EINVAL: log.error( - 'Failed to check mask status for service %s. Path %s is a ' - 'file, not a symlink. This could be caused by changes in ' - 'systemd and is probably a bug in Salt. Please report this ' - 'to the developers.', name, link_path + "Failed to check mask status for service %s. Path %s is a " + "file, not a symlink. This could be caused by changes in " + "systemd and is probably a bug in Salt. Please report this " + "to the developers.", + name, + link_path, ) return False def start(name, no_block=False, unmask=False, unmask_runtime=False): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands run by this function from the ``salt-minion`` daemon's @@ -842,23 +851,24 @@ def start(name, no_block=False, unmask=False, unmask_runtime=False): .. code-block:: bash salt '*' service.start <service name> - ''' + """ _check_for_unit_changes(name) _check_unmask(name, unmask, unmask_runtime) - ret = __salt__['cmd.run_all']( - _systemctl_cmd('start', name, systemd_scope=True, no_block=no_block), - python_shell=False) + ret = __salt__["cmd.run_all"]( + _systemctl_cmd("start", name, systemd_scope=True, no_block=no_block), + python_shell=False, + ) - if ret['retcode'] != 0: + if ret["retcode"] != 0: # Instead of returning a bool, raise an exception so that we can # include the error message in the return data. This helps give more # information to the user in instances where the service is masked. - raise CommandExecutionError(_strip_scope(ret['stderr'])) + raise CommandExecutionError(_strip_scope(ret["stderr"])) return True def stop(name, no_block=False): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands run by this function from the ``salt-minion`` daemon's @@ -882,16 +892,20 @@ def stop(name, no_block=False): .. code-block:: bash salt '*' service.stop <service name> - ''' + """ _check_for_unit_changes(name) # Using cmd.run_all instead of cmd.retcode here to make unit tests easier - return __salt__['cmd.run_all']( - _systemctl_cmd('stop', name, systemd_scope=True, no_block=no_block), - python_shell=False)['retcode'] == 0 + return ( + __salt__["cmd.run_all"]( + _systemctl_cmd("stop", name, systemd_scope=True, no_block=no_block), + python_shell=False, + )["retcode"] + == 0 + ) def restart(name, no_block=False, unmask=False, unmask_runtime=False): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands run by this function from the ``salt-minion`` daemon's @@ -931,23 +945,24 @@ def restart(name, no_block=False, unmask=False, unmask_runtime=False): .. code-block:: bash salt '*' service.restart <service name> - ''' + """ _check_for_unit_changes(name) _check_unmask(name, unmask, unmask_runtime) - ret = __salt__['cmd.run_all']( - _systemctl_cmd('restart', name, systemd_scope=True, no_block=no_block), - python_shell=False) + ret = __salt__["cmd.run_all"]( + _systemctl_cmd("restart", name, systemd_scope=True, no_block=no_block), + python_shell=False, + ) - if ret['retcode'] != 0: + if ret["retcode"] != 0: # Instead of returning a bool, raise an exception so that we can # include the error message in the return data. This helps give more # information to the user in instances where the service is masked. - raise CommandExecutionError(_strip_scope(ret['stderr'])) + raise CommandExecutionError(_strip_scope(ret["stderr"])) return True def reload_(name, no_block=False, unmask=False, unmask_runtime=False): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands run by this function from the ``salt-minion`` daemon's @@ -987,23 +1002,24 @@ def reload_(name, no_block=False, unmask=False, unmask_runtime=False): .. code-block:: bash salt '*' service.reload <service name> - ''' + """ _check_for_unit_changes(name) _check_unmask(name, unmask, unmask_runtime) - ret = __salt__['cmd.run_all']( - _systemctl_cmd('reload', name, systemd_scope=True, no_block=no_block), - python_shell=False) + ret = __salt__["cmd.run_all"]( + _systemctl_cmd("reload", name, systemd_scope=True, no_block=no_block), + python_shell=False, + ) - if ret['retcode'] != 0: + if ret["retcode"] != 0: # Instead of returning a bool, raise an exception so that we can # include the error message in the return data. This helps give more # information to the user in instances where the service is masked. - raise CommandExecutionError(_strip_scope(ret['stderr'])) + raise CommandExecutionError(_strip_scope(ret["stderr"])) return True def force_reload(name, no_block=True, unmask=False, unmask_runtime=False): - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands run by this function from the ``salt-minion`` daemon's @@ -1045,26 +1061,26 @@ def force_reload(name, no_block=True, unmask=False, unmask_runtime=False): .. code-block:: bash salt '*' service.force_reload <service name> - ''' + """ _check_for_unit_changes(name) _check_unmask(name, unmask, unmask_runtime) - ret = __salt__['cmd.run_all']( - _systemctl_cmd('force-reload', name, - systemd_scope=True, no_block=no_block), - python_shell=False) + ret = __salt__["cmd.run_all"]( + _systemctl_cmd("force-reload", name, systemd_scope=True, no_block=no_block), + python_shell=False, + ) - if ret['retcode'] != 0: + if ret["retcode"] != 0: # Instead of returning a bool, raise an exception so that we can # include the error message in the return data. This helps give more # information to the user in instances where the service is masked. - raise CommandExecutionError(_strip_scope(ret['stderr'])) + raise CommandExecutionError(_strip_scope(ret["stderr"])) return True # The unused sig argument is required to maintain consistency with the API # established by Salt's service management states. def status(name, sig=None, wait=3): # pylint: disable=unused-argument - ''' + """ Check whether or not a service is active. If the name contains globbing, a dict mapping service names to True/False values is returned. @@ -1092,15 +1108,18 @@ def status(name, sig=None, wait=3): # pylint: disable=unused-argument .. code-block:: bash salt '*' service.status <service name> [service signature] - ''' - def _get_status(service): - ret = __salt__['cmd.run_all'](_systemctl_cmd('is-active', service), - python_shell=False, - ignore_retcode=True, - redirect_stderr=True) - return ret['retcode'] == 0, ret['stdout'] + """ - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + def _get_status(service): + ret = __salt__["cmd.run_all"]( + _systemctl_cmd("is-active", service), + python_shell=False, + ignore_retcode=True, + redirect_stderr=True, + ) + return ret["retcode"] == 0, ret["stdout"] + + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: @@ -1113,8 +1132,7 @@ def status(name, sig=None, wait=3): # pylint: disable=unused-argument # Check if the service is in the process of activating/deactivating start_time = time.time() # match both 'activating' and 'deactivating' - while 'activating' in _message \ - and (time.time() - start_time <= wait): + while "activating" in _message and (time.time() - start_time <= wait): time.sleep(0.5) ret[service], _message = _get_status(service) if ret[service]: @@ -1127,9 +1145,10 @@ def status(name, sig=None, wait=3): # pylint: disable=unused-argument # **kwargs is required to maintain consistency with the API established by # Salt's service management states. -def enable(name, no_block=False, unmask=False, unmask_runtime=False, - root=None, **kwargs): # pylint: disable=unused-argument - ''' +def enable( + name, no_block=False, unmask=False, unmask_runtime=False, root=None, **kwargs +): # pylint: disable=unused-argument + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands run by this function from the ``salt-minion`` daemon's @@ -1172,40 +1191,45 @@ def enable(name, no_block=False, unmask=False, unmask_runtime=False, .. code-block:: bash salt '*' service.enable <service name> - ''' + """ _check_for_unit_changes(name) _check_unmask(name, unmask, unmask_runtime, root) if name in _get_sysv_services(root): cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) service_exec = _get_service_exec() - if service_exec.endswith('/update-rc.d'): - cmd.extend([service_exec, '-f', name, 'defaults', '99']) - elif service_exec.endswith('/chkconfig'): - cmd.extend([service_exec, name, 'on']) - return __salt__['cmd.retcode'](cmd, - python_shell=False, - ignore_retcode=True) == 0 - ret = __salt__['cmd.run_all']( - _systemctl_cmd('enable', name, systemd_scope=True, no_block=no_block, - root=root), + if service_exec.endswith("/update-rc.d"): + cmd.extend([service_exec, "-f", name, "defaults", "99"]) + elif service_exec.endswith("/chkconfig"): + cmd.extend([service_exec, name, "on"]) + return ( + __salt__["cmd.retcode"](cmd, python_shell=False, ignore_retcode=True) == 0 + ) + ret = __salt__["cmd.run_all"]( + _systemctl_cmd( + "enable", name, systemd_scope=True, no_block=no_block, root=root + ), python_shell=False, - ignore_retcode=True) + ignore_retcode=True, + ) - if ret['retcode'] != 0: + if ret["retcode"] != 0: # Instead of returning a bool, raise an exception so that we can # include the error message in the return data. This helps give more # information to the user in instances where the service is masked. - raise CommandExecutionError(_strip_scope(ret['stderr'])) + raise CommandExecutionError(_strip_scope(ret["stderr"])) return True # The unused kwargs argument is required to maintain consistency with the API # established by Salt's service management states. -def disable(name, no_block=False, root=None, **kwargs): # pylint: disable=unused-argument - ''' +def disable( + name, no_block=False, root=None, **kwargs +): # pylint: disable=unused-argument + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands run by this function from the ``salt-minion`` daemon's @@ -1232,33 +1256,39 @@ def disable(name, no_block=False, root=None, **kwargs): # pylint: disable=unuse .. code-block:: bash salt '*' service.disable <service name> - ''' + """ _check_for_unit_changes(name) if name in _get_sysv_services(root): cmd = [] - if salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) service_exec = _get_service_exec() - if service_exec.endswith('/update-rc.d'): - cmd.extend([service_exec, '-f', name, 'remove']) - elif service_exec.endswith('/chkconfig'): - cmd.extend([service_exec, name, 'off']) - return __salt__['cmd.retcode'](cmd, - python_shell=False, - ignore_retcode=True) == 0 + if service_exec.endswith("/update-rc.d"): + cmd.extend([service_exec, "-f", name, "remove"]) + elif service_exec.endswith("/chkconfig"): + cmd.extend([service_exec, name, "off"]) + return ( + __salt__["cmd.retcode"](cmd, python_shell=False, ignore_retcode=True) == 0 + ) # Using cmd.run_all instead of cmd.retcode here to make unit tests easier - return __salt__['cmd.run_all']( - _systemctl_cmd('disable', name, systemd_scope=True, no_block=no_block, - root=root), - python_shell=False, - ignore_retcode=True)['retcode'] == 0 + return ( + __salt__["cmd.run_all"]( + _systemctl_cmd( + "disable", name, systemd_scope=True, no_block=no_block, root=root + ), + python_shell=False, + ignore_retcode=True, + )["retcode"] + == 0 + ) # The unused kwargs argument is required to maintain consistency with the API # established by Salt's service management states. def enabled(name, root=None, **kwargs): # pylint: disable=unused-argument - ''' + """ Return if the named service is enabled to start on boot root @@ -1269,24 +1299,37 @@ def enabled(name, root=None, **kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' service.enabled <service name> - ''' + """ # Try 'systemctl is-enabled' first, then look for a symlink created by # systemctl (older systemd releases did not support using is-enabled to # check templated services), and lastly check for a sysvinit service. - if __salt__['cmd.retcode'](_systemctl_cmd('is-enabled', name, root=root), - python_shell=False, - ignore_retcode=True) == 0: + if ( + __salt__["cmd.retcode"]( + _systemctl_cmd("is-enabled", name, root=root), + python_shell=False, + ignore_retcode=True, + ) + == 0 + ): return True - elif '@' in name: + elif "@" in name: # On older systemd releases, templated services could not be checked # with ``systemctl is-enabled``. As a fallback, look for the symlinks # created by systemctl when enabling templated services. - local_config_path = _root(LOCAL_CONFIG_PATH, '/') - cmd = ['find', local_config_path, '-name', name, - '-type', 'l', '-print', '-quit'] + local_config_path = _root(LOCAL_CONFIG_PATH, "/") + cmd = [ + "find", + local_config_path, + "-name", + name, + "-type", + "l", + "-print", + "-quit", + ] # If the find command returns any matches, there will be output and the # string will be non-empty. - if bool(__salt__['cmd.run'](cmd, python_shell=False)): + if bool(__salt__["cmd.run"](cmd, python_shell=False)): return True elif name in _get_sysv_services(root): return _sysv_enabled(name, root) @@ -1295,7 +1338,7 @@ def enabled(name, root=None, **kwargs): # pylint: disable=unused-argument def disabled(name, root=None): - ''' + """ Return if the named service is disabled from starting on boot root @@ -1306,12 +1349,12 @@ def disabled(name, root=None): .. code-block:: bash salt '*' service.disabled <service name> - ''' + """ return not enabled(name, root=root) def show(name, root=None): - ''' + """ .. versionadded:: 2014.7.0 Show properties of one or more units/jobs or the manager @@ -1322,21 +1365,22 @@ def show(name, root=None): CLI Example: salt '*' service.show <service name> - ''' + """ ret = {} - out = __salt__['cmd.run'](_systemctl_cmd('show', name, root=root), - python_shell=False) - for line in salt.utils.itertools.split(out, '\n'): - comps = line.split('=') + out = __salt__["cmd.run"]( + _systemctl_cmd("show", name, root=root), python_shell=False + ) + for line in salt.utils.itertools.split(out, "\n"): + comps = line.split("=") name = comps[0] - value = '='.join(comps[1:]) - if value.startswith('{'): - value = value.replace('{', '').replace('}', '') + value = "=".join(comps[1:]) + if value.startswith("{"): + value = value.replace("{", "").replace("}", "") ret[name] = {} - for item in value.split(' ; '): - comps = item.split('=') + for item in value.split(" ; "): + comps = item.split("=") ret[name][comps[0].strip()] = comps[1].strip() - elif name in ('Before', 'After', 'Wants'): + elif name in ("Before", "After", "Wants"): ret[name] = value.split() else: ret[name] = value @@ -1345,7 +1389,7 @@ def show(name, root=None): def execs(root=None): - ''' + """ .. versionadded:: 2014.7.0 Return a list of all files specified as ``ExecStart`` for all services. @@ -1356,11 +1400,73 @@ def execs(root=None): CLI Example: salt '*' service.execs - ''' + """ ret = {} for service in get_all(root=root): data = show(service, root=root) - if 'ExecStart' not in data: + if "ExecStart" not in data: continue - ret[service] = data['ExecStart']['path'] + ret[service] = data["ExecStart"]["path"] return ret + + +def firstboot( + locale=None, + locale_message=None, + keymap=None, + timezone=None, + hostname=None, + machine_id=None, + root=None, +): + """ + .. versionadded:: TBD + + Call systemd-firstboot to configure basic settings of the system + + locale + Set primary locale (LANG=) + + locale_message + Set message locale (LC_MESSAGES=) + + keymap + Set keymap + + timezone + Set timezone + + hostname + Set host name + + machine_id + Set machine ID + + root + Operate on an alternative filesystem root + + CLI Example: + + salt '*' service.firstboot keymap=jp locale=en_US.UTF-8 + + """ + cmd = ["systemd-firstboot"] + parameters = [ + ("locale", locale), + ("locale-message", locale_message), + ("keymap", keymap), + ("timezone", timezone), + ("hostname", hostname), + ("machine-ID", machine_id), + ("root", root), + ] + for parameter, value in parameters: + if value: + cmd.extend(["--{}".format(parameter), str(value)]) + + out = __salt__["cmd.run_all"](cmd) + + if out["retcode"] != 0: + raise CommandExecutionError("systemd-firstboot error: {}".format(out["stderr"])) + + return True diff --git a/salt/modules/telegram.py b/salt/modules/telegram.py index 76975af83d3..261b17a64b8 100644 --- a/salt/modules/telegram.py +++ b/salt/modules/telegram.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for sending messages via Telegram. :configuration: In order to send a message via the Telegram, certain @@ -9,7 +9,7 @@ Module for sending messages via Telegram. telegram.chat_id: '123456789' telegram.token: '00000000:xxxxxxxxxxxxxxxxxxxxxxxx' -''' +""" from __future__ import absolute_import # Import Python libs @@ -20,56 +20,59 @@ from salt.exceptions import SaltInvocationError # Import 3rd-party libs try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False log = logging.getLogger(__name__) -__virtualname__ = 'telegram' +__virtualname__ = "telegram" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ if not HAS_REQUESTS: - return False + return (False, "Missing dependency requests") return __virtualname__ def _get_chat_id(): - ''' + """ Retrieves and return the Telegram's configured chat id :return: String: the chat id string - ''' - chat_id = __salt__['config.get']('telegram:chat_id') or \ - __salt__['config.get']('telegram.chat_id') + """ + chat_id = __salt__["config.get"]("telegram:chat_id") or __salt__["config.get"]( + "telegram.chat_id" + ) if not chat_id: - raise SaltInvocationError('No Telegram chat id found') + raise SaltInvocationError("No Telegram chat id found") return chat_id def _get_token(): - ''' + """ Retrieves and return the Telegram's configured token :return: String: the token string - ''' - token = __salt__['config.get']('telegram:token') or \ - __salt__['config.get']('telegram.token') + """ + token = __salt__["config.get"]("telegram:token") or __salt__["config.get"]( + "telegram.token" + ) if not token: - raise SaltInvocationError('No Telegram token found') + raise SaltInvocationError("No Telegram token found") return token def post_message(message, chat_id=None, token=None): - ''' + """ Send a message to a Telegram chat. :param message: The message to send to the Telegram chat. @@ -84,7 +87,7 @@ def post_message(message, chat_id=None, token=None): salt '*' telegram.post_message message="Hello Telegram!" - ''' + """ if not chat_id: chat_id = _get_chat_id() @@ -92,50 +95,43 @@ def post_message(message, chat_id=None, token=None): token = _get_token() if not message: - log.error('message is a required option.') + log.error("message is a required option.") return _post_message(message=message, chat_id=chat_id, token=token) def _post_message(message, chat_id, token): - ''' + """ Send a message to a Telegram chat. :param chat_id: The chat id. :param message: The message to send to the telegram chat. :param token: The Telegram API token. :return: Boolean if message was sent successfully. - ''' - url = 'https://api.telegram.org/bot{0}/sendMessage'.format(token) + """ + url = "https://api.telegram.org/bot{0}/sendMessage".format(token) parameters = dict() if chat_id: - parameters['chat_id'] = chat_id + parameters["chat_id"] = chat_id if message: - parameters['text'] = message + parameters["text"] = message try: - response = requests.post( - url, - data=parameters - ) + response = requests.post(url, data=parameters) result = response.json() - log.debug( - 'Raw response of the telegram request is {0}'.format(response) - ) + log.debug("Raw response of the telegram request is {0}".format(response)) except Exception: # pylint: disable=broad-except - log.exception( - 'Sending telegram api request failed' - ) + log.exception("Sending telegram api request failed") return False # Check if the Telegram Bot API returned successfully. - if not result.get('ok', False): + if not result.get("ok", False): log.debug( - 'Sending telegram api request failed due to error {0} ({1})'.format( - result.get('error_code'), result.get('description') + "Sending telegram api request failed due to error {0} ({1})".format( + result.get("error_code"), result.get("description") ) ) return False diff --git a/salt/modules/telemetry.py b/salt/modules/telemetry.py index 06509a9b87d..8fe5ec5f67c 100644 --- a/salt/modules/telemetry.py +++ b/salt/modules/telemetry.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Telemetry .. versionadded:: 2016.3.0 @@ -23,9 +23,10 @@ In the minion's config file: :depends: requests -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -40,11 +41,12 @@ log = logging.getLogger(__name__) # Import third party libs try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False -__virtualname__ = 'telemetry' +__virtualname__ = "telemetry" def __virtual__(): @@ -55,26 +57,26 @@ def __virtual__(): def _get_telemetry_base(profile): - config = __salt__['config.option'](profile) - return config.get('telemetry_api_base_url') + config = __salt__["config.option"](profile) + return config.get("telemetry_api_base_url") -def _auth(api_key=None, profile='telemetry'): +def _auth(api_key=None, profile="telemetry"): # return telemetry api key in the header if api_key is None and profile is None: raise Exception("Missing api_key and profile") if profile: if isinstance(profile, six.string_types): - _profile = __salt__['config.option'](profile) + _profile = __salt__["config.option"](profile) elif isinstance(profile, dict): _profile = profile if _profile: - api_key = _profile.get('telemetry_api_keys')[0] + api_key = _profile.get("telemetry_api_keys")[0] else: raise Exception("Missing api_key") - return {'Telemetry-API-Key': api_key, 'content-type': 'application/json'} + return {"Telemetry-API-Key": api_key, "content-type": "application/json"} def _update_cache(deployment_id, metric_name, alert): @@ -88,8 +90,8 @@ def _update_cache(deployment_id, metric_name, alert): return __context__.get(key, []) -def _retrieve_channel_id(email, profile='telemetry'): - ''' +def _retrieve_channel_id(email, profile="telemetry"): + """ Given an email address, checks the local cache if corresponding email address: channel_id mapping exists @@ -98,26 +100,31 @@ def _retrieve_channel_id(email, profile='telemetry'): Email escalation policy profile A dict of telemetry config information. - ''' + """ key = "telemetry.channels" auth = _auth(profile=profile) if key not in __context__: - get_url = _get_telemetry_base(profile) + "/notification-channels?_type=EmailNotificationChannel" + get_url = ( + _get_telemetry_base(profile) + + "/notification-channels?_type=EmailNotificationChannel" + ) response = requests.get(get_url, headers=auth) if response.status_code == 200: cache_result = {} for index, alert in enumerate(response.json()): - cache_result[alert.get('email')] = alert.get('_id', 'false') + cache_result[alert.get("email")] = alert.get("_id", "false") __context__[key] = cache_result return __context__[key].get(email, False) -def get_alert_config(deployment_id, metric_name=None, api_key=None, profile="telemetry"): - ''' +def get_alert_config( + deployment_id, metric_name=None, api_key=None, profile="telemetry" +): + """ Get all alert definitions associated with a given deployment or if metric_name is specified, obtain the specific alert config @@ -127,7 +134,7 @@ def get_alert_config(deployment_id, metric_name=None, api_key=None, profile="tel salt myminion telemetry.get_alert_config rs-ds033197 currentConnections profile=telemetry salt myminion telemetry.get_alert_config rs-ds033197 profile=telemetry - ''' + """ auth = _auth(profile=profile) alert = False @@ -136,7 +143,9 @@ def get_alert_config(deployment_id, metric_name=None, api_key=None, profile="tel if key not in __context__: try: - get_url = _get_telemetry_base(profile) + "/alerts?deployment={0}".format(deployment_id) + get_url = _get_telemetry_base(profile) + "/alerts?deployment={0}".format( + deployment_id + ) response = requests.get(get_url, headers=auth) except requests.exceptions.RequestException as e: log.error(six.text_type(e)) @@ -145,7 +154,7 @@ def get_alert_config(deployment_id, metric_name=None, api_key=None, profile="tel http_result = {} if response.status_code == 200: for alert in response.json(): - http_result[alert.get('condition', {}).get('metric')] = alert + http_result[alert.get("condition", {}).get("metric")] = alert __context__[key] = http_result if not __context__.get(key): @@ -156,11 +165,11 @@ def get_alert_config(deployment_id, metric_name=None, api_key=None, profile="tel if metric_name: return __context__[key].get(metric_name) - return [alert['_id'] for alert in alerts if '_id' in alert] + return [alert["_id"] for alert in alerts if "_id" in alert] def get_notification_channel_id(notify_channel, profile="telemetry"): - ''' + """ Given an email address, creates a notification-channels if one is not found and also returns the corresponding notification channel id. @@ -173,7 +182,7 @@ def get_notification_channel_id(notify_channel, profile="telemetry"): CLI Example: salt myminion telemetry.get_notification_channel_id userx@company.com profile=telemetry - ''' + """ # This helper is used to procure the channel ids # used to notify when the alarm threshold is violated @@ -188,23 +197,30 @@ def get_notification_channel_id(notify_channel, profile="telemetry"): post_url = _get_telemetry_base(profile) + "/notification-channels" data = { "_type": "EmailNotificationChannel", - "name": notify_channel[:notify_channel.find('@')] + 'EscalationPolicy', - "email": notify_channel + "name": notify_channel[: notify_channel.find("@")] + "EscalationPolicy", + "email": notify_channel, } - response = requests.post(post_url, data=salt.utils.json.dumps(data), headers=auth) + response = requests.post( + post_url, data=salt.utils.json.dumps(data), headers=auth + ) if response.status_code == 200: - log.info("Successfully created EscalationPolicy %s with EmailNotificationChannel %s", - data.get('name'), notify_channel) - notification_channel_id = response.json().get('_id') + log.info( + "Successfully created EscalationPolicy %s with EmailNotificationChannel %s", + data.get("name"), + notify_channel, + ) + notification_channel_id = response.json().get("_id") __context__["telemetry.channels"][notify_channel] = notification_channel_id else: - raise Exception("Failed to created notification channel {0}".format(notify_channel)) + raise Exception( + "Failed to created notification channel {0}".format(notify_channel) + ) return notification_channel_id def get_alarms(deployment_id, profile="telemetry"): - ''' + """ get all the alarms set up against the current deployment Returns dictionary of alarm information @@ -213,11 +229,15 @@ def get_alarms(deployment_id, profile="telemetry"): salt myminion telemetry.get_alarms rs-ds033197 profile=telemetry - ''' + """ auth = _auth(profile=profile) try: - response = requests.get(_get_telemetry_base(profile) + "/alerts?deployment={0}".format(deployment_id), headers=auth) + response = requests.get( + _get_telemetry_base(profile) + + "/alerts?deployment={0}".format(deployment_id), + headers=auth, + ) except requests.exceptions.RequestException as e: log.error(six.text_type(e)) return False @@ -228,14 +248,17 @@ def get_alarms(deployment_id, profile="telemetry"): if len(alarms) > 0: return alarms - return 'No alarms defined for deployment: {0}'.format(deployment_id) + return "No alarms defined for deployment: {0}".format(deployment_id) else: # Non 200 response, sent back the error response' - return {'err_code': response.status_code, 'err_msg': salt.utils.json.loads(response.text).get('err', '')} + return { + "err_code": response.status_code, + "err_msg": salt.utils.json.loads(response.text).get("err", ""), + } def create_alarm(deployment_id, metric_name, data, api_key=None, profile="telemetry"): - ''' + """ create an telemetry alarms. data is a dict of alert configuration data. @@ -246,7 +269,7 @@ def create_alarm(deployment_id, metric_name, data, api_key=None, profile="teleme salt myminion telemetry.create_alarm rs-ds033197 {} profile=telemetry - ''' + """ auth = _auth(api_key, profile) request_uri = _get_telemetry_base(profile) + "/alerts" @@ -256,39 +279,50 @@ def create_alarm(deployment_id, metric_name, data, api_key=None, profile="teleme # set the notification channels if not already set post_body = { "deployment": deployment_id, - "filter": data.get('filter'), - "notificationChannel": get_notification_channel_id(data.get('escalate_to')).split(), + "filter": data.get("filter"), + "notificationChannel": get_notification_channel_id( + data.get("escalate_to") + ).split(), "condition": { "metric": metric_name, - "max": data.get('max'), - "min": data.get('min') - } + "max": data.get("max"), + "min": data.get("min"), + }, } try: - response = requests.post(request_uri, data=salt.utils.json.dumps(post_body), headers=auth) + response = requests.post( + request_uri, data=salt.utils.json.dumps(post_body), headers=auth + ) except requests.exceptions.RequestException as e: # TODO: May be we should retry? log.error(six.text_type(e)) if response.status_code >= 200 and response.status_code < 300: # update cache - log.info('Created alarm on metric: %s in deployment: %s', metric_name, deployment_id) - log.debug('Updating cache for metric %s in deployment %s: %s', - metric_name, deployment_id, response.json()) + log.info( + "Created alarm on metric: %s in deployment: %s", metric_name, deployment_id + ) + log.debug( + "Updating cache for metric %s in deployment %s: %s", + metric_name, + deployment_id, + response.json(), + ) _update_cache(deployment_id, metric_name, response.json()) else: log.error( - 'Failed to create alarm on metric: %s in ' - 'deployment %s: payload: %s', - metric_name, deployment_id, salt.utils.json.dumps(post_body) + "Failed to create alarm on metric: %s in " "deployment %s: payload: %s", + metric_name, + deployment_id, + salt.utils.json.dumps(post_body), ) return response.status_code >= 200 and response.status_code < 300, response.json() def update_alarm(deployment_id, metric_name, data, api_key=None, profile="telemetry"): - ''' + """ update an telemetry alarms. data is a dict of alert configuration data. Returns (bool success, str message) tuple. @@ -297,54 +331,71 @@ def update_alarm(deployment_id, metric_name, data, api_key=None, profile="teleme salt myminion telemetry.update_alarm rs-ds033197 {} profile=telemetry - ''' + """ auth = _auth(api_key, profile) alert = get_alert_config(deployment_id, metric_name, api_key, profile) if not alert: - return False, "No entity found matching deployment {0} and alarms {1}".format(deployment_id, metric_name) + return ( + False, + "No entity found matching deployment {0} and alarms {1}".format( + deployment_id, metric_name + ), + ) - request_uri = _get_telemetry_base(profile) + '/alerts/' + alert['_id'] + request_uri = _get_telemetry_base(profile) + "/alerts/" + alert["_id"] # set the notification channels if not already set post_body = { "deployment": deployment_id, - "filter": data.get('filter'), - "notificationChannel": get_notification_channel_id(data.get('escalate_to')).split(), + "filter": data.get("filter"), + "notificationChannel": get_notification_channel_id( + data.get("escalate_to") + ).split(), "condition": { "metric": metric_name, - "max": data.get('max'), - "min": data.get('min') - } + "max": data.get("max"), + "min": data.get("min"), + }, } try: - response = requests.put(request_uri, data=salt.utils.json.dumps(post_body), headers=auth) + response = requests.put( + request_uri, data=salt.utils.json.dumps(post_body), headers=auth + ) except requests.exceptions.RequestException as e: - log.error('Update failed: %s', e) + log.error("Update failed: %s", e) return False, six.text_type(e) if response.status_code >= 200 and response.status_code < 300: # Also update cache - log.debug('Updating cache for metric %s in deployment %s: %s', - metric_name, deployment_id, response.json()) + log.debug( + "Updating cache for metric %s in deployment %s: %s", + metric_name, + deployment_id, + response.json(), + ) _update_cache(deployment_id, metric_name, response.json()) - log.info('Updated alarm on metric: %s in deployment: %s', metric_name, deployment_id) + log.info( + "Updated alarm on metric: %s in deployment: %s", metric_name, deployment_id + ) return True, response.json() err_msg = six.text_type( # future lint: disable=blacklisted-function - 'Failed to create alarm on metric: {0} in deployment: {1} ' - 'payload: {2}').format( - salt.utils.stringutils.to_unicode(metric_name), - salt.utils.stringutils.to_unicode(deployment_id), - salt.utils.json.dumps(post_body) - ) + "Failed to create alarm on metric: {0} in deployment: {1} " "payload: {2}" + ).format( + salt.utils.stringutils.to_unicode(metric_name), + salt.utils.stringutils.to_unicode(deployment_id), + salt.utils.json.dumps(post_body), + ) log.error(err_msg) return False, err_msg -def delete_alarms(deployment_id, alert_id=None, metric_name=None, api_key=None, profile='telemetry'): - '''delete an alert specified by alert_id or if not specified blows away all the alerts +def delete_alarms( + deployment_id, alert_id=None, metric_name=None, api_key=None, profile="telemetry" +): + """delete an alert specified by alert_id or if not specified blows away all the alerts in the current deployment. Returns (bool success, str message) tuple. @@ -353,7 +404,7 @@ def delete_alarms(deployment_id, alert_id=None, metric_name=None, api_key=None, salt myminion telemetry.delete_alarms rs-ds033197 profile=telemetry - ''' + """ auth = _auth(profile=profile) if alert_id is None: @@ -363,7 +414,12 @@ def delete_alarms(deployment_id, alert_id=None, metric_name=None, api_key=None, alert_ids = [alert_id] if len(alert_ids) == 0: - return False, "failed to find alert associated with deployment: {0}".format(deployment_id) + return ( + False, + "failed to find alert associated with deployment: {0}".format( + deployment_id + ), + ) failed_to_delete = [] for id in alert_ids: @@ -372,17 +428,30 @@ def delete_alarms(deployment_id, alert_id=None, metric_name=None, api_key=None, try: response = requests.delete(delete_url, headers=auth) if metric_name: - log.debug("updating cache and delete %s key from %s", - metric_name, deployment_id) + log.debug( + "updating cache and delete %s key from %s", + metric_name, + deployment_id, + ) _update_cache(deployment_id, metric_name, None) except requests.exceptions.RequestException as e: - log.error('Delete failed: %s', e) + log.error("Delete failed: %s", e) if response.status_code != 200: failed_to_delete.append(id) if len(failed_to_delete) > 0: - return False, "Failed to delete {0} alarms in deployment: {1}" .format(', '.join(failed_to_delete), deployment_id) + return ( + False, + "Failed to delete {0} alarms in deployment: {1}".format( + ", ".join(failed_to_delete), deployment_id + ), + ) - return True, "Successfully deleted {0} alerts in deployment: {1}".format(', '.join(alert_ids), deployment_id) + return ( + True, + "Successfully deleted {0} alerts in deployment: {1}".format( + ", ".join(alert_ids), deployment_id + ), + ) diff --git a/salt/modules/temp.py b/salt/modules/temp.py index fe3aac59974..3798827257e 100644 --- a/salt/modules/temp.py +++ b/salt/modules/temp.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Simple module for creating temporary directories and files This is a thin wrapper around Pythons tempfile module .. versionadded:: 2015.8.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals import logging import os @@ -16,8 +16,8 @@ import tempfile log = logging.getLogger(__name__) -def dir(suffix='', prefix='tmp', parent=None): - ''' +def dir(suffix="", prefix="tmp", parent=None): + """ Create a temporary directory CLI Example: @@ -26,12 +26,12 @@ def dir(suffix='', prefix='tmp', parent=None): salt '*' temp.dir salt '*' temp.dir prefix='mytemp-' parent='/var/run/' - ''' + """ return tempfile.mkdtemp(suffix, prefix, parent) -def file(suffix='', prefix='tmp', parent=None): - ''' +def file(suffix="", prefix="tmp", parent=None): + """ Create a temporary file CLI Example: @@ -40,7 +40,7 @@ def file(suffix='', prefix='tmp', parent=None): salt '*' temp.file salt '*' temp.file prefix='mytemp-' parent='/var/run/' - ''' + """ fh_, tmp_ = tempfile.mkstemp(suffix, prefix, parent) os.close(fh_) return tmp_ diff --git a/salt/modules/test.py b/salt/modules/test.py index 08110597156..e2123c5a391 100644 --- a/salt/modules/test.py +++ b/salt/modules/test.py @@ -1,48 +1,48 @@ # -*- coding: utf-8 -*- -''' +""" Module for running arbitrary tests -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging import os +import random import sys import time import traceback -import random # Import Salt libs import salt.exceptions +import salt.loader import salt.utils.args import salt.utils.functools import salt.utils.hashutils import salt.utils.platform import salt.version -import salt.loader from salt.ext import six from salt.ext.six.moves import builtins from salt.utils.decorators import depends -__proxyenabled__ = ['*'] +__proxyenabled__ = ["*"] # Don't shadow built-in's. __func_alias__ = { - 'true_': 'true', - 'false_': 'false', - 'try_': 'try', + "true_": "true", + "false_": "false", + "try_": "try", } log = logging.getLogger(__name__) -@depends('non_existantmodulename') +@depends("non_existantmodulename") def missing_func(): - return 'foo' + return "foo" def attr_call(): - ''' + """ Call grains.items via the attribute CLI Example: @@ -50,12 +50,12 @@ def attr_call(): .. code-block:: bash salt '*' test.attr_call - ''' + """ return __salt__.grains.items() def module_report(): - ''' + """ Return a dict containing all of the execution modules with a report on the overall availability via different references @@ -64,37 +64,39 @@ def module_report(): .. code-block:: bash salt '*' test.module_report - ''' - ret = {'functions': [], - 'function_attrs': [], - 'function_subs': [], - 'modules': [], - 'module_attrs': [], - 'missing_attrs': [], - 'missing_subs': []} + """ + ret = { + "functions": [], + "function_attrs": [], + "function_subs": [], + "modules": [], + "module_attrs": [], + "missing_attrs": [], + "missing_subs": [], + } for ref in __salt__: - if '.' in ref: - ret['functions'].append(ref) + if "." in ref: + ret["functions"].append(ref) else: - ret['modules'].append(ref) + ret["modules"].append(ref) if hasattr(__salt__, ref): - ret['module_attrs'].append(ref) + ret["module_attrs"].append(ref) for func in __salt__[ref]: - full = '{0}.{1}'.format(ref, func) + full = "{0}.{1}".format(ref, func) if hasattr(getattr(__salt__, ref), func): - ret['function_attrs'].append(full) + ret["function_attrs"].append(full) if func in __salt__[ref]: - ret['function_subs'].append(full) - for func in ret['functions']: - if func not in ret['function_attrs']: - ret['missing_attrs'].append(func) - if func not in ret['function_subs']: - ret['missing_subs'].append(func) + ret["function_subs"].append(full) + for func in ret["functions"]: + if func not in ret["function_attrs"]: + ret["missing_attrs"].append(func) + if func not in ret["function_subs"]: + ret["missing_subs"].append(func) return ret def echo(text): - ''' + """ Return a string - used for testing the connection CLI Example: @@ -102,12 +104,12 @@ def echo(text): .. code-block:: bash salt '*' test.echo 'foo bar baz quo qux' - ''' + """ return text def ping(): - ''' + """ Used to make sure the minion is up and responding. Not an ICMP ping. Returns ``True``. @@ -117,21 +119,21 @@ def ping(): .. code-block:: bash salt '*' test.ping - ''' + """ if not salt.utils.platform.is_proxy(): - log.debug('test.ping received for minion \'%s\'', __opts__.get('id')) + log.debug("test.ping received for minion '%s'", __opts__.get("id")) return True else: - ping_cmd = __opts__['proxy']['proxytype'] + '.ping' - if __opts__.get('add_proxymodule_to_opts', False): - return __opts__['proxymodule'][ping_cmd]() + ping_cmd = __opts__["proxy"]["proxytype"] + ".ping" + if __opts__.get("add_proxymodule_to_opts", False): + return __opts__["proxymodule"][ping_cmd]() else: return __proxy__[ping_cmd]() def sleep(length): - ''' + """ Instruct the minion to initiate a process that will sleep for a given period of time. @@ -140,13 +142,13 @@ def sleep(length): .. code-block:: bash salt '*' test.sleep 20 - ''' + """ time.sleep(int(length)) return True def rand_sleep(max=60): - ''' + """ Sleep for a random number of seconds, used to test long-running commands and minions returning at differing intervals @@ -155,13 +157,13 @@ def rand_sleep(max=60): .. code-block:: bash salt '*' test.rand_sleep 60 - ''' + """ time.sleep(random.randint(0, max)) return True def version(): - ''' + """ Return the version of salt on the minion CLI Example: @@ -169,12 +171,12 @@ def version(): .. code-block:: bash salt '*' test.version - ''' + """ return salt.version.__version__ def versions_information(): - ''' + """ Report the versions of dependent and system software CLI Example: @@ -182,12 +184,12 @@ def versions_information(): .. code-block:: bash salt '*' test.versions_information - ''' + """ return salt.version.versions_information() def versions_report(): - ''' + """ Returns versions of components used by salt CLI Example: @@ -195,15 +197,15 @@ def versions_report(): .. code-block:: bash salt '*' test.versions_report - ''' - return '\n'.join(salt.version.versions_report()) + """ + return "\n".join(salt.version.versions_report()) -versions = salt.utils.functools.alias_function(versions_report, 'versions') +versions = salt.utils.functools.alias_function(versions_report, "versions") def conf_test(): - ''' + """ Return the value for test.foo in the minion configuration file, or return the default value @@ -212,12 +214,12 @@ def conf_test(): .. code-block:: bash salt '*' test.conf_test - ''' - return __salt__['config.option']('test.foo') + """ + return __salt__["config.option"]("test.foo") def get_opts(): - ''' + """ Return the configuration options passed to this minion CLI Example: @@ -225,12 +227,12 @@ def get_opts(): .. code-block:: bash salt '*' test.get_opts - ''' + """ return __opts__ def cross_test(func, args=None): - ''' + """ Execute a minion function via the __salt__ object in the test module, used to verify that the minion functions can be called via the __salt__ module. @@ -240,14 +242,14 @@ def cross_test(func, args=None): .. code-block:: bash salt '*' test.cross_test file.gid_to_group 0 - ''' + """ if args is None: args = [] return __salt__[func](*args) def kwarg(**kwargs): - ''' + """ Print out the data passed into the function ``**kwargs``, this is used to both test the publication data and cli kwarg passing, but also to display the information available within the publication data. @@ -257,12 +259,12 @@ def kwarg(**kwargs): .. code-block:: bash salt '*' test.kwarg num=1 txt="two" env='{a: 1, b: "hello"}' - ''' + """ return kwargs def arg(*args, **kwargs): - ''' + """ Print out the data passed into the function ``*args`` and ```kwargs``, this is used to both test the publication data and cli argument passing, but also to display the information available within the publication data. @@ -273,12 +275,12 @@ def arg(*args, **kwargs): .. code-block:: bash salt '*' test.arg 1 "two" 3.1 txt="hello" wow='{a: 1, b: "hello"}' - ''' + """ return {"args": args, "kwargs": kwargs} def arg_type(*args, **kwargs): - ''' + """ Print out the types of the args and kwargs. This is used to test the types of the args and kwargs passed down to the minion @@ -287,21 +289,21 @@ def arg_type(*args, **kwargs): .. code-block:: bash salt '*' test.arg_type 1 'int' - ''' - ret = {'args': [], 'kwargs': {}} + """ + ret = {"args": [], "kwargs": {}} # all the args for argument in args: - ret['args'].append(six.text_type(type(argument))) + ret["args"].append(six.text_type(type(argument))) # all the kwargs for key, val in six.iteritems(kwargs): - ret['kwargs'][key] = six.text_type(type(val)) + ret["kwargs"][key] = six.text_type(type(val)) return ret def arg_repr(*args, **kwargs): - ''' + """ Print out the data passed into the function ``*args`` and ```kwargs``, this is used to both test the publication data and cli argument passing, but also to display the information available within the publication data. @@ -312,24 +314,24 @@ def arg_repr(*args, **kwargs): .. code-block:: bash salt '*' test.arg_repr 1 "two" 3.1 txt="hello" wow='{a: 1, b: "hello"}' - ''' + """ return {"args": repr(args), "kwargs": repr(kwargs)} def arg_clean(*args, **kwargs): - ''' + """ Like test.arg but cleans kwargs of the __pub* items CLI Example: .. code-block:: bash salt '*' test.arg_clean 1 "two" 3.1 txt="hello" wow='{a: 1, b: "hello"}' - ''' + """ return dict(args=args, kwargs=salt.utils.args.clean_kwargs(**kwargs)) def fib(num): - ''' + """ Return the num-th Fibonacci number, and the time it took to compute in seconds. Used for performance tests. @@ -340,10 +342,10 @@ def fib(num): .. code-block:: bash salt '*' test.fib 3 - ''' + """ num = int(num) if num < 0: - raise ValueError('Negative number is not allowed!') + raise ValueError("Negative number is not allowed!") start = time.time() if num < 2: return num, time.time() - start @@ -359,7 +361,7 @@ def fib(num): def collatz(start): - ''' + """ Execute the collatz conjecture from the passed starting number, returns the sequence and the time it took to compute. Used for performance tests. @@ -369,7 +371,7 @@ def collatz(start): .. code-block:: bash salt '*' test.collatz 3 - ''' + """ start = int(start) begin = time.time() steps = [] @@ -384,7 +386,7 @@ def collatz(start): def outputter(data): - ''' + """ Test the outputter, pass in data to return CLI Example: @@ -392,12 +394,12 @@ def outputter(data): .. code-block:: bash salt '*' test.outputter foobar - ''' + """ return data def retcode(code=42): - ''' + """ Test that the returncode system is functioning correctly CLI Example: @@ -405,13 +407,13 @@ def retcode(code=42): .. code-block:: bash salt '*' test.retcode 42 - ''' - __context__['retcode'] = code + """ + __context__["retcode"] = code return True def provider(module): - ''' + """ Pass in a function name to discover what provider is being used CLI Example: @@ -419,22 +421,22 @@ def provider(module): .. code-block:: bash salt '*' test.provider service - ''' - func = '' + """ + func = "" for key in __salt__: - if not key.startswith('{0}.'.format(module)): + if not key.startswith("{0}.".format(module)): continue func = key break if not func: - return '' + return "" pfn = sys.modules[__salt__[func].__module__].__file__ pfn = os.path.basename(pfn) - return pfn[:pfn.rindex('.')] + return pfn[: pfn.rindex(".")] def providers(): - ''' + """ Return a dict of the provider names and the files that provided them CLI Example: @@ -442,17 +444,17 @@ def providers(): .. code-block:: bash salt '*' test.providers - ''' + """ ret = {} for funcname in __salt__: - modname = funcname.split('.')[0] + modname = funcname.split(".")[0] if modname not in ret: ret[provider(modname)] = modname return ret def not_loaded(): - ''' + """ List the modules that were not loaded by the salt loader system CLI Example: @@ -460,25 +462,25 @@ def not_loaded(): .. code-block:: bash salt '*' test.not_loaded - ''' + """ prov = providers() ret = set() - for mod_dir in salt.loader._module_dirs(__opts__, 'modules', 'module'): + for mod_dir in salt.loader._module_dirs(__opts__, "modules", "module"): if not os.path.isabs(mod_dir): continue if not os.path.isdir(mod_dir): continue for fn_ in os.listdir(mod_dir): - if fn_.startswith('_'): + if fn_.startswith("_"): continue - name = fn_.split('.')[0] + name = fn_.split(".")[0] if name not in prov: ret.add(name) return sorted(ret) def opts_pkg(): - ''' + """ Return an opts package with the grains and opts for this minion. This is primarily used to create the options used for master side state compiling routines @@ -488,25 +490,25 @@ def opts_pkg(): .. code-block:: bash salt '*' test.opts_pkg - ''' + """ ret = {} ret.update(__opts__) - ret['grains'] = __grains__ + ret["grains"] = __grains__ return ret def rand_str(size=9999999999, hash_type=None): - ''' + """ This function has been renamed to random_hash. This function will stay to ensure backwards compatibility, but please switch to using the prefered name random_hash. - ''' + """ return random_hash(size=size, hash_type=hash_type) def random_hash(size=9999999999, hash_type=None): - ''' + """ .. versionadded:: 2015.5.2 .. versionchanged:: 2018.3.0 Function has been renamed from ``test.rand_str`` to @@ -522,14 +524,14 @@ def random_hash(size=9999999999, hash_type=None): salt '*' test.random_hash salt '*' test.random_hash hash_type=sha512 - ''' + """ if not hash_type: - hash_type = __opts__.get('hash_type', 'md5') + hash_type = __opts__.get("hash_type", "md5") return salt.utils.hashutils.random_hash(size=size, hash_type=hash_type) -def exception(message='Test Exception'): - ''' +def exception(message="Test Exception"): + """ Raise an exception Optionally provide an error message or output the full stack. @@ -539,12 +541,12 @@ def exception(message='Test Exception'): .. code-block:: bash salt '*' test.exception 'Oh noes!' - ''' + """ raise Exception(message) def stack(): - ''' + """ Return the current stack trace CLI Example: @@ -552,12 +554,12 @@ def stack(): .. code-block:: bash salt '*' test.stack - ''' - return ''.join(traceback.format_stack()) + """ + return "".join(traceback.format_stack()) def tty(*args, **kwargs): # pylint: disable=W0613 - ''' + """ Deprecated! Moved to cmdmod. CLI Example: @@ -566,12 +568,12 @@ def tty(*args, **kwargs): # pylint: disable=W0613 salt '*' test.tty tty0 'This is a test' salt '*' test.tty pts3 'This is a test' - ''' - return 'ERROR: This function has been moved to cmd.tty' + """ + return "ERROR: This function has been moved to cmd.tty" def try_(module, return_try_exception=False, **kwargs): - ''' + """ Try to run a module command. On an exception return None. If `return_try_exception` is set True return the exception. This can be helpful in templates where running a module might fail as expected. @@ -585,7 +587,7 @@ def try_(module, return_try_exception=False, **kwargs): {{ salt['test.try'](module='ipmi.get_users', bmc_host='172.2.2.'+i)|yaml(False) }} {% endfor %} </pre> - ''' + """ try: return __salt__[module](**kwargs) except Exception as e: # pylint: disable=broad-except @@ -595,7 +597,7 @@ def try_(module, return_try_exception=False, **kwargs): def assertion(assertion): - ''' + """ Assert the given argument CLI Example: @@ -603,12 +605,12 @@ def assertion(assertion): .. code-block:: bash salt '*' test.assertion False - ''' + """ assert assertion def true_(): - ''' + """ Always return True CLI Example: @@ -616,12 +618,12 @@ def true_(): .. code-block:: bash salt '*' test.true - ''' + """ return True def false_(): - ''' + """ Always return False CLI Example: @@ -629,12 +631,12 @@ def false_(): .. code-block:: bash salt '*' test.false - ''' + """ return False def raise_exception(name, *args, **kwargs): - ''' + """ Raise an exception. Built-in exceptions and those in ``salt.exceptions`` can be raised by this test function. If no matching exception is found, then no exception will be raised and this function will return ``False``. @@ -648,7 +650,8 @@ def raise_exception(name, *args, **kwargs): salt '*' test.raise_exception TypeError "An integer is required" salt '*' test.raise_exception salt.exceptions.CommandExecutionError "Something went wrong" - ''' + """ + def _is_exc(cls): for base in cls.__bases__: if base is BaseException: @@ -660,15 +663,15 @@ def raise_exception(name, *args, **kwargs): return True try: - if name.startswith('salt.exceptions.'): + if name.startswith("salt.exceptions."): exc = getattr(salt.exceptions, name[16:]) else: exc = getattr(builtins, name) if _is_exc(exc): raise exc(*args, **salt.utils.args.clean_kwargs(**kwargs)) else: - log.error('%s is not an exception', name) + log.error("%s is not an exception", name) return False except AttributeError: - log.error('No such exception: %s', name) + log.error("No such exception: %s", name) return False diff --git a/salt/modules/test_virtual.py b/salt/modules/test_virtual.py index 08e0dcbce4e..44017d1356e 100644 --- a/salt/modules/test_virtual.py +++ b/salt/modules/test_virtual.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Module for running arbitrary tests with a __virtual__ function -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - return (False, 'The test_virtual execution module failed to load.') + return (False, "The test_virtual execution module failed to load.") def ping(): diff --git a/salt/modules/testinframod.py b/salt/modules/testinframod.py index 6620cc52e41..42dd3e48965 100644 --- a/salt/modules/testinframod.py +++ b/salt/modules/testinframod.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" This module exposes the functionality of the TestInfra library for use with SaltStack in order to verify the state of your minions. In order to allow for the addition of new resource types in TestInfra this module dynamically generates wrappers for the various resources by iterating over the values in the ``__all__`` variable exposed by the testinfra.modules namespace. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + import inspect import logging import operator @@ -21,6 +22,7 @@ log = logging.getLogger(__name__) try: import testinfra from testinfra import modules + TESTINFRA_PRESENT = True except ImportError: TESTINFRA_PRESENT = False @@ -28,8 +30,8 @@ except ImportError: __all__ = [] -__virtualname__ = 'testinfra' -default_backend = 'local://' +__virtualname__ = "testinfra" +default_backend = "local://" class InvalidArgumentError(Exception): @@ -39,7 +41,7 @@ class InvalidArgumentError(Exception): def __virtual__(): if TESTINFRA_PRESENT: return __virtualname__ - return False, 'The Testinfra package is not available' + return False, "The Testinfra package is not available" def _get_module(module_name, backend=default_backend): @@ -53,7 +55,9 @@ def _get_module(module_name, backend=default_backend): """ backend_instance = testinfra.get_backend(backend) - return backend_instance.get_module(snake_to_camel_case(module_name, uppercamel=True)) + return backend_instance.get_module( + snake_to_camel_case(module_name, uppercamel=True) + ) def _get_method_result(module_, module_instance, method_name, method_arg=None): @@ -69,35 +73,37 @@ def _get_method_result(module_, module_instance, method_name, method_arg=None): :rtype: variable """ - log.debug('Trying to call %s on %s', method_name, module_) + log.debug("Trying to call %s on %s", method_name, module_) try: method_obj = getattr(module_, method_name) except AttributeError: try: method_obj = getattr(module_instance, method_name) except AttributeError: - raise InvalidArgumentError('The {0} module does not have any ' - 'property or method named {1}'.format( - module_, method_name)) + raise InvalidArgumentError( + "The {0} module does not have any " + "property or method named {1}".format(module_, method_name) + ) if isinstance(method_obj, property): return method_obj.fget(module_instance) elif isinstance(method_obj, (types.MethodType, types.FunctionType)): if not method_arg: - raise InvalidArgumentError('{0} is a method of the {1} module. An ' - 'argument dict is required.' - .format(method_name, - module_)) + raise InvalidArgumentError( + "{0} is a method of the {1} module. An " + "argument dict is required.".format(method_name, module_) + ) try: - return getattr(module_instance, - method_name)(method_arg['parameter']) + return getattr(module_instance, method_name)(method_arg["parameter"]) except KeyError: - raise InvalidArgumentError('The argument dict supplied has no ' - 'key named "parameter": {0}' - .format(method_arg)) + raise InvalidArgumentError( + "The argument dict supplied has no " + 'key named "parameter": {0}'.format(method_arg) + ) except AttributeError: - raise InvalidArgumentError('The {0} module does not have any ' - 'property or method named {1}'.format( - module_, method_name)) + raise InvalidArgumentError( + "The {0} module does not have any " + "property or method named {1}".format(module_, method_name) + ) else: return method_obj return None @@ -118,28 +124,30 @@ def _apply_assertion(expected, result): :rtype: bool """ - log.debug('Expected result: %s. Actual result: %s', expected, result) + log.debug("Expected result: %s. Actual result: %s", expected, result) if isinstance(expected, bool): return result is expected elif isinstance(expected, dict): try: - comparison = getattr(operator, expected['comparison']) + comparison = getattr(operator, expected["comparison"]) except AttributeError: - if expected.get('comparison') == 'search': + if expected.get("comparison") == "search": comparison = re.search else: - raise InvalidArgumentError('Comparison {0} is not a valid ' - 'selection.'.format( - expected.get('comparison'))) + raise InvalidArgumentError( + "Comparison {0} is not a valid " + "selection.".format(expected.get("comparison")) + ) except KeyError: - log.exception('The comparison dictionary provided is missing ' - 'expected keys. Either "expected" or "comparison" ' - 'are not present.') + log.exception( + "The comparison dictionary provided is missing " + 'expected keys. Either "expected" or "comparison" ' + "are not present." + ) raise - return comparison(expected['expected'], result) + return comparison(expected["expected"], result) else: - raise TypeError('Expected bool or dict but received {0}' - .format(type(expected))) + raise TypeError("Expected bool or dict but received {0}".format(type(expected))) # This does not currently generate documentation from the underlying modules @@ -188,33 +196,33 @@ def _copy_function(module_name, name=None): comparison: eq ``` """ - log.debug('Generating function for testinfra.%s', module_name) + log.debug("Generating function for testinfra.%s", module_name) def _run_tests(name, **methods): success = True pass_msgs = [] fail_msgs = [] try: - log.debug('Retrieving %s module.', module_name) + log.debug("Retrieving %s module.", module_name) mod = _get_module(module_name) - log.debug('Retrieved module is %s', mod.__dict__) + log.debug("Retrieved module is %s", mod.__dict__) except NotImplementedError: log.exception( - 'The %s module is not supported for this backend and/or ' - 'platform.', module_name) + "The %s module is not supported for this backend and/or " "platform.", + module_name, + ) success = False return success, pass_msgs, fail_msgs - if hasattr(inspect, 'signature'): + if hasattr(inspect, "signature"): mod_sig = inspect.signature(mod) parameters = mod_sig.parameters else: if isinstance(mod.__init__, types.MethodType): mod_sig = inspect.getargspec(mod.__init__) - elif hasattr(mod, '__call__'): + elif hasattr(mod, "__call__"): mod_sig = inspect.getargspec(mod.__call__) parameters = mod_sig.args - log.debug('Parameters accepted by module %s: %s', - module_name, parameters) + log.debug("Parameters accepted by module %s: %s", module_name, parameters) additional_args = {} for arg in set(parameters).intersection(set(methods)): additional_args[arg] = methods.pop(arg) @@ -224,29 +232,34 @@ def _copy_function(module_name, name=None): else: modinstance = mod() except TypeError: - log.exception('Module failed to instantiate') + log.exception("Module failed to instantiate") raise valid_methods = {} - log.debug('Called methods are: %s', methods) + log.debug("Called methods are: %s", methods) for meth_name in methods: - if not meth_name.startswith('_'): + if not meth_name.startswith("_"): valid_methods[meth_name] = methods[meth_name] - log.debug('Valid methods are: %s', valid_methods) + log.debug("Valid methods are: %s", valid_methods) for meth, arg in valid_methods.items(): result = _get_method_result(mod, modinstance, meth, arg) assertion_result = _apply_assertion(arg, result) if not assertion_result: success = False - fail_msgs.append('Assertion failed: {modname} {n} {m} {a}. ' - 'Actual result: {r}'.format( - modname=module_name, n=name, m=meth, a=arg, r=result - )) + fail_msgs.append( + "Assertion failed: {modname} {n} {m} {a}. " + "Actual result: {r}".format( + modname=module_name, n=name, m=meth, a=arg, r=result + ) + ) else: - pass_msgs.append('Assertion passed: {modname} {n} {m} {a}. ' - 'Actual result: {r}'.format( - modname=module_name, n=name, m=meth, a=arg, r=result - )) + pass_msgs.append( + "Assertion passed: {modname} {n} {m} {a}. " + "Actual result: {r}".format( + modname=module_name, n=name, m=meth, a=arg, r=result + ) + ) return success, pass_msgs, fail_msgs + func = _run_tests if name is not None: # types.FunctionType requires a str for __name__ attribute, using a @@ -254,11 +267,9 @@ def _copy_function(module_name, name=None): name = str(name) # future lint: disable=blacklisted-function else: name = func.__name__ - return types.FunctionType(func.__code__, - func.__globals__, - name, - func.__defaults__, - func.__closure__) + return types.FunctionType( + func.__code__, func.__globals__, name, func.__defaults__, func.__closure__ + ) def _register_functions(): diff --git a/salt/modules/textfsm_mod.py b/salt/modules/textfsm_mod.py index 8fe6e438a2a..ee3d5307e59 100644 --- a/salt/modules/textfsm_mod.py +++ b/salt/modules/textfsm_mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" TextFSM ======= @@ -16,64 +16,70 @@ inside the renderer (Jinja, Mako, Genshi, etc.). For Python 2/3 compatibility, it is more recommended to install the ``jtextfsm`` library: ``pip install jtextfsm``. -''' +""" from __future__ import absolute_import +import logging + # Import python libs import os -import logging from salt.utils.files import fopen # Import third party modules try: import textfsm + HAS_TEXTFSM = True except ImportError: HAS_TEXTFSM = False try: import clitable + HAS_CLITABLE = True except ImportError: HAS_CLITABLE = False log = logging.getLogger(__name__) -__virtualname__ = 'textfsm' -__proxyenabled__ = ['*'] +__virtualname__ = "textfsm" +__proxyenabled__ = ["*"] def __virtual__(): - ''' + """ Only load this execution module if TextFSM is installed. - ''' + """ if HAS_TEXTFSM: return __virtualname__ - return (False, 'The textfsm execution module failed to load: requires the textfsm library.') + return ( + False, + "The textfsm execution module failed to load: requires the textfsm library.", + ) def _clitable_to_dict(objects, fsm_handler): - ''' + """ Converts TextFSM cli_table object to list of dictionaries. - ''' + """ objs = [] - log.debug('Cli Table:') + log.debug("Cli Table:") log.debug(objects) - log.debug('FSM handler:') + log.debug("FSM handler:") log.debug(fsm_handler) for row in objects: temp_dict = {} for index, element in enumerate(row): temp_dict[fsm_handler.header[index].lower()] = element objs.append(temp_dict) - log.debug('Extraction result:') + log.debug("Extraction result:") log.debug(objs) return objs -def extract(template_path, raw_text=None, raw_text_file=None, saltenv='base'): - r''' +def extract(template_path, raw_text=None, raw_text_file=None, saltenv="base"): + r""" Extracts the data entities from the unstructured raw text sent as input and returns the data mapping, processing using the TextFSM template. @@ -175,66 +181,75 @@ def extract(template_path, raw_text=None, raw_text_file=None, saltenv='base'): } ] } - ''' - ret = { - 'result': False, - 'comment': '', - 'out': None - } - log.debug('Using the saltenv: {}'.format(saltenv)) - log.debug('Caching {} using the Salt fileserver'.format(template_path)) - tpl_cached_path = __salt__['cp.cache_file'](template_path, saltenv=saltenv) + """ + ret = {"result": False, "comment": "", "out": None} + log.debug("Using the saltenv: {}".format(saltenv)) + log.debug("Caching {} using the Salt fileserver".format(template_path)) + tpl_cached_path = __salt__["cp.cache_file"](template_path, saltenv=saltenv) if tpl_cached_path is False: - ret['comment'] = 'Unable to read the TextFSM template from {}'.format(template_path) - log.error(ret['comment']) + ret["comment"] = "Unable to read the TextFSM template from {}".format( + template_path + ) + log.error(ret["comment"]) return ret try: - log.debug('Reading TextFSM template from cache path: {}'.format(tpl_cached_path)) + log.debug( + "Reading TextFSM template from cache path: {}".format(tpl_cached_path) + ) # Disabling pylint W8470 to nto complain about fopen. # Unfortunately textFSM needs the file handle rather than the content... # pylint: disable=W8470 - tpl_file_handle = fopen(tpl_cached_path, 'r') + tpl_file_handle = fopen(tpl_cached_path, "r") # pylint: disable=W8470 log.debug(tpl_file_handle.read()) tpl_file_handle.seek(0) # move the object position back at the top of the file fsm_handler = textfsm.TextFSM(tpl_file_handle) except textfsm.TextFSMTemplateError as tfte: - log.error('Unable to parse the TextFSM template', exc_info=True) - ret['comment'] = 'Unable to parse the TextFSM template from {}: {}. Please check the logs.'.format( - template_path, tfte) + log.error("Unable to parse the TextFSM template", exc_info=True) + ret[ + "comment" + ] = "Unable to parse the TextFSM template from {}: {}. Please check the logs.".format( + template_path, tfte + ) return ret if not raw_text and raw_text_file: - log.debug('Trying to read the raw input from {}'.format(raw_text_file)) - raw_text = __salt__['cp.get_file_str'](raw_text_file, saltenv=saltenv) + log.debug("Trying to read the raw input from {}".format(raw_text_file)) + raw_text = __salt__["cp.get_file_str"](raw_text_file, saltenv=saltenv) if raw_text is False: - ret['comment'] = 'Unable to read from {}. Please specify a valid input file or text.'.format(raw_text_file) - log.error(ret['comment']) + ret[ + "comment" + ] = "Unable to read from {}. Please specify a valid input file or text.".format( + raw_text_file + ) + log.error(ret["comment"]) return ret if not raw_text: - ret['comment'] = 'Please specify a valid input file or text.' - log.error(ret['comment']) + ret["comment"] = "Please specify a valid input file or text." + log.error(ret["comment"]) return ret - log.debug('Processing the raw text:') + log.debug("Processing the raw text:") log.debug(raw_text) objects = fsm_handler.ParseText(raw_text) - ret['out'] = _clitable_to_dict(objects, fsm_handler) - ret['result'] = True + ret["out"] = _clitable_to_dict(objects, fsm_handler) + ret["result"] = True return ret -def index(command, - platform=None, - platform_grain_name=None, - platform_column_name=None, - output=None, - output_file=None, - textfsm_path=None, - index_file=None, - saltenv='base', - include_empty=False, - include_pat=None, - exclude_pat=None): - ''' +def index( + command, + platform=None, + platform_grain_name=None, + platform_column_name=None, + output=None, + output_file=None, + textfsm_path=None, + index_file=None, + saltenv="base", + include_empty=False, + include_pat=None, + exclude_pat=None, +): + """ Dynamically identify the template required to extract the information from the unstructured raw text. @@ -374,83 +389,109 @@ def index(command, {%- set command = 'sh ver' -%} {%- set output = salt.net.cli(command) -%} {%- set textfsm_extract = salt.textfsm.index(command, output=output) -%} - ''' - ret = { - 'out': None, - 'result': False, - 'comment': '' - } + """ + ret = {"out": None, "result": False, "comment": ""} if not HAS_CLITABLE: - ret['comment'] = 'TextFSM doesnt seem that has clitable embedded.' - log.error(ret['comment']) + ret["comment"] = "TextFSM doesnt seem that has clitable embedded." + log.error(ret["comment"]) return ret if not platform: - platform_grain_name = __opts__.get('textfsm_platform_grain') or\ - __pillar__.get('textfsm_platform_grain', platform_grain_name) + platform_grain_name = __opts__.get("textfsm_platform_grain") or __pillar__.get( + "textfsm_platform_grain", platform_grain_name + ) if platform_grain_name: - log.debug('Using the {} grain to identify the platform name'.format(platform_grain_name)) + log.debug( + "Using the {} grain to identify the platform name".format( + platform_grain_name + ) + ) platform = __grains__.get(platform_grain_name) if not platform: - ret['comment'] = 'Unable to identify the platform name using the {} grain.'.format(platform_grain_name) + ret[ + "comment" + ] = "Unable to identify the platform name using the {} grain.".format( + platform_grain_name + ) return ret - log.info('Using platform: {}'.format(platform)) + log.info("Using platform: {}".format(platform)) else: - ret['comment'] = 'No platform specified, no platform grain identifier configured.' - log.error(ret['comment']) + ret[ + "comment" + ] = "No platform specified, no platform grain identifier configured." + log.error(ret["comment"]) return ret if not textfsm_path: - log.debug('No TextFSM templates path specified, trying to look into the opts and pillar') - textfsm_path = __opts__.get('textfsm_path') or __pillar__.get('textfsm_path') + log.debug( + "No TextFSM templates path specified, trying to look into the opts and pillar" + ) + textfsm_path = __opts__.get("textfsm_path") or __pillar__.get("textfsm_path") if not textfsm_path: - ret['comment'] = 'No TextFSM templates path specified. Please configure in opts/pillar/function args.' - log.error(ret['comment']) + ret[ + "comment" + ] = "No TextFSM templates path specified. Please configure in opts/pillar/function args." + log.error(ret["comment"]) return ret - log.debug('Using the saltenv: {}'.format(saltenv)) - log.debug('Caching {} using the Salt fileserver'.format(textfsm_path)) - textfsm_cachedir_ret = __salt__['cp.cache_dir'](textfsm_path, - saltenv=saltenv, - include_empty=include_empty, - include_pat=include_pat, - exclude_pat=exclude_pat) - log.debug('Cache fun return:') + log.debug("Using the saltenv: {}".format(saltenv)) + log.debug("Caching {} using the Salt fileserver".format(textfsm_path)) + textfsm_cachedir_ret = __salt__["cp.cache_dir"]( + textfsm_path, + saltenv=saltenv, + include_empty=include_empty, + include_pat=include_pat, + exclude_pat=exclude_pat, + ) + log.debug("Cache fun return:") log.debug(textfsm_cachedir_ret) if not textfsm_cachedir_ret: - ret['comment'] = 'Unable to fetch from {}. Is the TextFSM path correctly specified?'.format(textfsm_path) - log.error(ret['comment']) + ret[ + "comment" + ] = "Unable to fetch from {}. Is the TextFSM path correctly specified?".format( + textfsm_path + ) + log.error(ret["comment"]) return ret textfsm_cachedir = os.path.dirname(textfsm_cachedir_ret[0]) # first item - index_file = __opts__.get('textfsm_index_file') or __pillar__.get('textfsm_index_file', 'index') + index_file = __opts__.get("textfsm_index_file") or __pillar__.get( + "textfsm_index_file", "index" + ) index_file_path = os.path.join(textfsm_cachedir, index_file) - log.debug('Using the cached index file: {}'.format(index_file_path)) - log.debug('TextFSM templates cached under: {}'.format(textfsm_cachedir)) + log.debug("Using the cached index file: {}".format(index_file_path)) + log.debug("TextFSM templates cached under: {}".format(textfsm_cachedir)) textfsm_obj = clitable.CliTable(index_file_path, textfsm_cachedir) - attrs = { - 'Command': command - } - platform_column_name = __opts__.get('textfsm_platform_column_name') or\ - __pillar__.get('textfsm_platform_column_name', 'Platform') - log.info('Using the TextFSM platform idenfiticator: {}'.format(platform_column_name)) + attrs = {"Command": command} + platform_column_name = __opts__.get( + "textfsm_platform_column_name" + ) or __pillar__.get("textfsm_platform_column_name", "Platform") + log.info( + "Using the TextFSM platform idenfiticator: {}".format(platform_column_name) + ) attrs[platform_column_name] = platform - log.debug('Processing the TextFSM index file using the attributes: {}'.format(attrs)) + log.debug( + "Processing the TextFSM index file using the attributes: {}".format(attrs) + ) if not output and output_file: - log.debug('Processing the output from {}'.format(output_file)) - output = __salt__['cp.get_file_str'](output_file, saltenv=saltenv) + log.debug("Processing the output from {}".format(output_file)) + output = __salt__["cp.get_file_str"](output_file, saltenv=saltenv) if output is False: - ret['comment'] = 'Unable to read from {}. Please specify a valid file or text.'.format(output_file) - log.error(ret['comment']) + ret[ + "comment" + ] = "Unable to read from {}. Please specify a valid file or text.".format( + output_file + ) + log.error(ret["comment"]) return ret if not output: - ret['comment'] = 'Please specify a valid output text or file' - log.error(ret['comment']) + ret["comment"] = "Please specify a valid output text or file" + log.error(ret["comment"]) return ret - log.debug('Processing the raw text:') + log.debug("Processing the raw text:") log.debug(output) try: # Parse output through template textfsm_obj.ParseCmd(output, attrs) - ret['out'] = _clitable_to_dict(textfsm_obj, textfsm_obj) - ret['result'] = True + ret["out"] = _clitable_to_dict(textfsm_obj, textfsm_obj) + ret["result"] = True except clitable.CliTableError as cterr: - log.error('Unable to proces the CliTable', exc_info=True) - ret['comment'] = 'Unable to process the output: {}'.format(cterr) + log.error("Unable to proces the CliTable", exc_info=True) + ret["comment"] = "Unable to process the output: {}".format(cterr) return ret diff --git a/salt/modules/timezone.py b/salt/modules/timezone.py index 3288d01334f..7ad98a32d9a 100644 --- a/salt/modules/timezone.py +++ b/salt/modules/timezone.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing timezone on POSIX-like systems. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import filecmp -import os + import errno +import filecmp import logging +import os import re import string @@ -19,65 +20,71 @@ import salt.utils.itertools import salt.utils.path import salt.utils.platform import salt.utils.stringutils -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) -__virtualname__ = 'timezone' +__virtualname__ = "timezone" def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' + """ if salt.utils.platform.is_windows(): - return (False, 'The timezone execution module failed to load: ' - 'win_timezone.py should replace this module on Windows.' - 'There was a problem loading win_timezone.py.') + return ( + False, + "The timezone execution module failed to load: " + "win_timezone.py should replace this module on Windows." + "There was a problem loading win_timezone.py.", + ) if salt.utils.platform.is_darwin(): - return (False, 'The timezone execution module failed to load: ' - 'mac_timezone.py should replace this module on macOS.' - 'There was a problem loading mac_timezone.py.') + return ( + False, + "The timezone execution module failed to load: " + "mac_timezone.py should replace this module on macOS." + "There was a problem loading mac_timezone.py.", + ) return __virtualname__ def _timedatectl(): - ''' + """ get the output of timedatectl - ''' - ret = __salt__['cmd.run_all'](['timedatectl'], python_shell=False) + """ + ret = __salt__["cmd.run_all"](["timedatectl"], python_shell=False) - if ret['retcode'] != 0: - msg = 'timedatectl failed: {0}'.format(ret['stderr']) + if ret["retcode"] != 0: + msg = "timedatectl failed: {0}".format(ret["stderr"]) raise CommandExecutionError(msg) return ret def _get_zone_solaris(): - tzfile = '/etc/TIMEZONE' - with salt.utils.files.fopen(tzfile, 'r') as fp_: + tzfile = "/etc/TIMEZONE" + with salt.utils.files.fopen(tzfile, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if 'TZ=' in line: - zonepart = line.rstrip('\n').split('=')[-1] - return zonepart.strip('\'"') or 'UTC' - raise CommandExecutionError('Unable to get timezone from ' + tzfile) + if "TZ=" in line: + zonepart = line.rstrip("\n").split("=")[-1] + return zonepart.strip("'\"") or "UTC" + raise CommandExecutionError("Unable to get timezone from " + tzfile) def _get_adjtime_timezone(): - ''' + """ Return the timezone in /etc/adjtime of the system clock - ''' - adjtime_file = '/etc/adjtime' + """ + adjtime_file = "/etc/adjtime" if os.path.exists(adjtime_file): - cmd = ['tail', '-n', '1', adjtime_file] - return __salt__['cmd.run'](cmd, python_shell=False) - elif os.path.exists('/dev/rtc'): + cmd = ["tail", "-n", "1", adjtime_file] + return __salt__["cmd.run"](cmd, python_shell=False) + elif os.path.exists("/dev/rtc"): raise CommandExecutionError( - 'Unable to get hwclock timezone from ' + adjtime_file + "Unable to get hwclock timezone from " + adjtime_file ) else: # There is no RTC. @@ -85,43 +92,41 @@ def _get_adjtime_timezone(): def _get_zone_sysconfig(): - tzfile = '/etc/sysconfig/clock' - with salt.utils.files.fopen(tzfile, 'r') as fp_: + tzfile = "/etc/sysconfig/clock" + with salt.utils.files.fopen(tzfile, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if re.match(r'^\s*#', line): + if re.match(r"^\s*#", line): continue - if 'ZONE' in line and '=' in line: - zonepart = line.rstrip('\n').split('=')[-1] - return zonepart.strip('\'"') or 'UTC' - raise CommandExecutionError('Unable to get timezone from ' + tzfile) + if "ZONE" in line and "=" in line: + zonepart = line.rstrip("\n").split("=")[-1] + return zonepart.strip("'\"") or "UTC" + raise CommandExecutionError("Unable to get timezone from " + tzfile) def _get_zone_etc_localtime(): tzfile = _get_localtime_path() - tzdir = '/usr/share/zoneinfo/' + tzdir = "/usr/share/zoneinfo/" tzdir_len = len(tzdir) try: - olson_name = os.path.normpath( - os.path.join('/etc', os.readlink(tzfile)) - ) + olson_name = os.path.normpath(os.path.join("/etc", os.readlink(tzfile))) if olson_name.startswith(tzdir): return olson_name[tzdir_len:] except OSError as exc: if exc.errno == errno.ENOENT: - if 'FreeBSD' in __grains__['os_family']: + if "FreeBSD" in __grains__["os_family"]: return get_zonecode() - raise CommandExecutionError(tzfile + ' does not exist') + raise CommandExecutionError(tzfile + " does not exist") elif exc.errno == errno.EINVAL: - if 'FreeBSD' in __grains__['os_family']: + if "FreeBSD" in __grains__["os_family"]: return get_zonecode() log.warning( - '%s is not a symbolic link. Attempting to match ' - 'it to zoneinfo files.', - tzfile + "%s is not a symbolic link. Attempting to match " + "it to zoneinfo files.", + tzfile, ) # Regular file. Try to match the hash. - hash_type = __opts__.get('hash_type', 'md5') + hash_type = __opts__.get("hash_type", "md5") tzfile_hash = salt.utils.hashutils.get_hash(tzfile, hash_type) # Not a link, just a copy of the tzdata file for root, dirs, files in salt.utils.path.os_walk(tzdir): @@ -130,37 +135,37 @@ def _get_zone_etc_localtime(): olson_name = full_path[tzdir_len:] if olson_name[0] in string.ascii_lowercase: continue - if tzfile_hash == \ - salt.utils.hashutils.get_hash(full_path, hash_type): + if tzfile_hash == salt.utils.hashutils.get_hash( + full_path, hash_type + ): return olson_name - raise CommandExecutionError('Unable to determine timezone') + raise CommandExecutionError("Unable to determine timezone") def _get_zone_etc_timezone(): - tzfile = '/etc/timezone' + tzfile = "/etc/timezone" try: - with salt.utils.files.fopen(tzfile, 'r') as fp_: + with salt.utils.files.fopen(tzfile, "r") as fp_: return salt.utils.stringutils.to_unicode(fp_.read()).strip() except IOError as exc: raise CommandExecutionError( - 'Problem reading timezone file {0}: {1}' - .format(tzfile, exc.strerror) + "Problem reading timezone file {0}: {1}".format(tzfile, exc.strerror) ) def _get_zone_aix(): - tzfile = '/etc/environment' - with salt.utils.files.fopen(tzfile, 'r') as fp_: + tzfile = "/etc/environment" + with salt.utils.files.fopen(tzfile, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if 'TZ=' in line: - zonepart = line.rstrip('\n').split('=')[-1] - return zonepart.strip('\'"') or 'UTC' - raise CommandExecutionError('Unable to get timezone from ' + tzfile) + if "TZ=" in line: + zonepart = line.rstrip("\n").split("=")[-1] + return zonepart.strip("'\"") or "UTC" + raise CommandExecutionError("Unable to get timezone from " + tzfile) def get_zone(): - ''' + """ Get current timezone (i.e. America/Denver) .. versionchanged:: 2016.11.4 @@ -175,41 +180,45 @@ def get_zone(): .. code-block:: bash salt '*' timezone.get_zone - ''' - if salt.utils.path.which('timedatectl'): + """ + if salt.utils.path.which("timedatectl"): ret = _timedatectl() - for line in (x.strip() for x in salt.utils.itertools.split(ret['stdout'], '\n')): + for line in ( + x.strip() for x in salt.utils.itertools.split(ret["stdout"], "\n") + ): try: - return re.match(r'Time ?zone:\s+(\S+)', line).group(1) + return re.match(r"Time ?zone:\s+(\S+)", line).group(1) except AttributeError: pass - msg = ('Failed to parse timedatectl output: {0}\n' - 'Please file an issue with SaltStack').format(ret['stdout']) + msg = ( + "Failed to parse timedatectl output: {0}\n" + "Please file an issue with SaltStack" + ).format(ret["stdout"]) raise CommandExecutionError(msg) else: - if __grains__['os'].lower() == 'centos': + if __grains__["os"].lower() == "centos": return _get_zone_etc_localtime() - os_family = __grains__['os_family'] - for family in ('RedHat', 'Suse'): + os_family = __grains__["os_family"] + for family in ("RedHat", "Suse"): if family in os_family: return _get_zone_sysconfig() - for family in ('Debian', 'Gentoo'): + for family in ("Debian", "Gentoo"): if family in os_family: return _get_zone_etc_timezone() - if os_family in ('FreeBSD', 'OpenBSD', 'NetBSD', 'NILinuxRT'): + if os_family in ("FreeBSD", "OpenBSD", "NetBSD", "NILinuxRT"): return _get_zone_etc_localtime() - elif 'Solaris' in os_family: + elif "Solaris" in os_family: return _get_zone_solaris() - elif 'AIX' in os_family: + elif "AIX" in os_family: return _get_zone_aix() - raise CommandExecutionError('Unable to get timezone') + raise CommandExecutionError("Unable to get timezone") def get_zonecode(): - ''' + """ Get current timezone (i.e. PST, MDT, etc) CLI Example: @@ -217,12 +226,12 @@ def get_zonecode(): .. code-block:: bash salt '*' timezone.get_zonecode - ''' - return __salt__['cmd.run'](['date', '+%Z'], python_shell=False) + """ + return __salt__["cmd.run"](["date", "+%Z"], python_shell=False) def get_offset(): - ''' + """ Get current numeric timezone offset from UCT (i.e. -0700) CLI Example: @@ -230,20 +239,20 @@ def get_offset(): .. code-block:: bash salt '*' timezone.get_offset - ''' - if 'AIX' not in __grains__['os_family']: - return __salt__['cmd.run'](['date', '+%z'], python_shell=False) + """ + if "AIX" not in __grains__["os_family"]: + return __salt__["cmd.run"](["date", "+%z"], python_shell=False) - salt_path = '/opt/salt/bin/date' + salt_path = "/opt/salt/bin/date" if not os.path.exists(salt_path): - return 'date in salt binaries does not exist: {0}'.format(salt_path) + return "date in salt binaries does not exist: {0}".format(salt_path) - return __salt__['cmd.run']([salt_path, '+%z'], python_shell=False) + return __salt__["cmd.run"]([salt_path, "+%z"], python_shell=False) def set_zone(timezone): - ''' + """ Unlinks, then symlinks /etc/localtime to the set timezone. The timezone is crucial to several system processes, each of which SHOULD @@ -267,59 +276,60 @@ def set_zone(timezone): salt '*' timezone.set_zone 'CST6CDT,M3.2.0/2:00:00,M11.1.0/2:00:00' - ''' - if salt.utils.path.which('timedatectl'): + """ + if salt.utils.path.which("timedatectl"): try: - __salt__['cmd.run']('timedatectl set-timezone {0}'.format(timezone)) + __salt__["cmd.run"]("timedatectl set-timezone {0}".format(timezone)) except CommandExecutionError: pass - if 'Solaris' in __grains__['os_family'] or 'AIX' in __grains__['os_family']: - zonepath = '/usr/share/lib/zoneinfo/{0}'.format(timezone) + if "Solaris" in __grains__["os_family"] or "AIX" in __grains__["os_family"]: + zonepath = "/usr/share/lib/zoneinfo/{0}".format(timezone) else: - zonepath = '/usr/share/zoneinfo/{0}'.format(timezone) + zonepath = "/usr/share/zoneinfo/{0}".format(timezone) - if not os.path.exists(zonepath) and 'AIX' not in __grains__['os_family']: - return 'Zone does not exist: {0}'.format(zonepath) + if not os.path.exists(zonepath) and "AIX" not in __grains__["os_family"]: + return "Zone does not exist: {0}".format(zonepath) tzfile = _get_localtime_path() if os.path.exists(tzfile): os.unlink(tzfile) - if 'Solaris' in __grains__['os_family']: - __salt__['file.sed']( - '/etc/default/init', '^TZ=.*', 'TZ={0}'.format(timezone)) - elif 'AIX' in __grains__['os_family']: + if "Solaris" in __grains__["os_family"]: + __salt__["file.sed"]("/etc/default/init", "^TZ=.*", "TZ={0}".format(timezone)) + elif "AIX" in __grains__["os_family"]: # timezone could be Olson or Posix curtzstring = get_zone() - cmd = ['chtz', timezone] - result = __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = ["chtz", timezone] + result = __salt__["cmd.retcode"](cmd, python_shell=False) if result == 0: return True # restore orig timezone, since AIX chtz failure sets UTC - cmd = ['chtz', curtzstring] - __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = ["chtz", curtzstring] + __salt__["cmd.retcode"](cmd, python_shell=False) return False else: os.symlink(zonepath, tzfile) - if 'RedHat' in __grains__['os_family']: - __salt__['file.sed']( - '/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="{0}"'.format(timezone)) - elif 'Suse' in __grains__['os_family']: - __salt__['file.sed']( - '/etc/sysconfig/clock', '^TIMEZONE=.*', 'TIMEZONE="{0}"'.format(timezone)) - elif 'Debian' in __grains__['os_family'] or 'Gentoo' in __grains__['os_family']: - with salt.utils.files.fopen('/etc/timezone', 'w') as ofh: + if "RedHat" in __grains__["os_family"]: + __salt__["file.sed"]( + "/etc/sysconfig/clock", "^ZONE=.*", 'ZONE="{0}"'.format(timezone) + ) + elif "Suse" in __grains__["os_family"]: + __salt__["file.sed"]( + "/etc/sysconfig/clock", "^TIMEZONE=.*", 'TIMEZONE="{0}"'.format(timezone) + ) + elif "Debian" in __grains__["os_family"] or "Gentoo" in __grains__["os_family"]: + with salt.utils.files.fopen("/etc/timezone", "w") as ofh: ofh.write(salt.utils.stringutils.to_str(timezone).strip()) - ofh.write('\n') + ofh.write("\n") return True def zone_compare(timezone): - ''' + """ Compares the given timezone name with the system timezone name. Checks the hash sum between the given timezone, and the one set in /etc/localtime. Returns True if names and hash sums match, and False if not. @@ -342,11 +352,11 @@ def zone_compare(timezone): .. code-block:: bash salt '*' timezone.zone_compare 'America/Denver' - ''' - if 'Solaris' in __grains__['os_family'] or 'AIX' in __grains__['os_family']: + """ + if "Solaris" in __grains__["os_family"] or "AIX" in __grains__["os_family"]: return timezone == get_zone() - if 'FreeBSD' in __grains__['os_family']: + if "FreeBSD" in __grains__["os_family"]: if not os.path.isfile(_get_localtime_path()): return timezone == get_zone() @@ -358,26 +368,32 @@ def zone_compare(timezone): problematic_file = exc.filename if problematic_file == zonepath: raise SaltInvocationError( - 'Can\'t find a local timezone "{0}"'.format(timezone)) + 'Can\'t find a local timezone "{0}"'.format(timezone) + ) elif problematic_file == tzfile: raise CommandExecutionError( - 'Failed to read {0} to determine current timezone: {1}' - .format(tzfile, exc.strerror)) + "Failed to read {0} to determine current timezone: {1}".format( + tzfile, exc.strerror + ) + ) raise def _get_localtime_path(): - if 'NILinuxRT' in __grains__['os_family'] and 'nilrt' in __grains__['lsb_distrib_id']: - return '/etc/natinst/share/localtime' - return '/etc/localtime' + if ( + "NILinuxRT" in __grains__["os_family"] + and "nilrt" in __grains__["lsb_distrib_id"] + ): + return "/etc/natinst/share/localtime" + return "/etc/localtime" def _get_zone_file(timezone): - return '/usr/share/zoneinfo/{0}'.format(timezone) + return "/usr/share/zoneinfo/{0}".format(timezone) def get_hwclock(): - ''' + """ Get current hardware clock setting (UTC or localtime) CLI Example: @@ -385,112 +401,116 @@ def get_hwclock(): .. code-block:: bash salt '*' timezone.get_hwclock - ''' - if salt.utils.path.which('timedatectl'): + """ + if salt.utils.path.which("timedatectl"): ret = _timedatectl() - for line in (x.strip() for x in ret['stdout'].splitlines()): - if 'rtc in local tz' in line.lower(): + for line in (x.strip() for x in ret["stdout"].splitlines()): + if "rtc in local tz" in line.lower(): try: - if line.split(':')[-1].strip().lower() == 'yes': - return 'localtime' + if line.split(":")[-1].strip().lower() == "yes": + return "localtime" else: - return 'UTC' + return "UTC" except IndexError: pass - msg = ('Failed to parse timedatectl output: {0}\n' - 'Please file an issue with SaltStack').format(ret['stdout']) + msg = ( + "Failed to parse timedatectl output: {0}\n" + "Please file an issue with SaltStack" + ).format(ret["stdout"]) raise CommandExecutionError(msg) else: - os_family = __grains__['os_family'] - for family in ('RedHat', 'Suse', 'NILinuxRT'): + os_family = __grains__["os_family"] + for family in ("RedHat", "Suse", "NILinuxRT"): if family in os_family: return _get_adjtime_timezone() - if 'Debian' in __grains__['os_family']: + if "Debian" in __grains__["os_family"]: # Original way to look up hwclock on Debian-based systems try: - with salt.utils.files.fopen('/etc/default/rcS', 'r') as fp_: + with salt.utils.files.fopen("/etc/default/rcS", "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if re.match(r'^\s*#', line): + if re.match(r"^\s*#", line): continue - if 'UTC=' in line: - is_utc = line.rstrip('\n').split('=')[-1].lower() - if is_utc == 'yes': - return 'UTC' + if "UTC=" in line: + is_utc = line.rstrip("\n").split("=")[-1].lower() + if is_utc == "yes": + return "UTC" else: - return 'localtime' + return "localtime" except IOError as exc: pass # Since Wheezy return _get_adjtime_timezone() - if 'Gentoo' in __grains__['os_family']: - if not os.path.exists('/etc/adjtime'): - offset_file = '/etc/conf.d/hwclock' + if "Gentoo" in __grains__["os_family"]: + if not os.path.exists("/etc/adjtime"): + offset_file = "/etc/conf.d/hwclock" try: - with salt.utils.files.fopen(offset_file, 'r') as fp_: + with salt.utils.files.fopen(offset_file, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('clock='): - line = line.rstrip('\n') - line = line.split('=')[-1].strip('\'"') - if line == 'UTC': + if line.startswith("clock="): + line = line.rstrip("\n") + line = line.split("=")[-1].strip("'\"") + if line == "UTC": return line - if line == 'local': - return 'LOCAL' + if line == "local": + return "LOCAL" raise CommandExecutionError( - 'Correct offset value not found in {0}' - .format(offset_file) + "Correct offset value not found in {0}".format(offset_file) ) except IOError as exc: raise CommandExecutionError( - 'Problem reading offset file {0}: {1}' - .format(offset_file, exc.strerror) + "Problem reading offset file {0}: {1}".format( + offset_file, exc.strerror + ) ) return _get_adjtime_timezone() - if 'Solaris' in __grains__['os_family']: - offset_file = '/etc/rtc_config' + if "Solaris" in __grains__["os_family"]: + offset_file = "/etc/rtc_config" try: - with salt.utils.files.fopen(offset_file, 'r') as fp_: + with salt.utils.files.fopen(offset_file, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('zone_info=GMT'): - return 'UTC' - return 'localtime' + if line.startswith("zone_info=GMT"): + return "UTC" + return "localtime" except IOError as exc: if exc.errno == errno.ENOENT: # offset file does not exist - return 'UTC' + return "UTC" raise CommandExecutionError( - 'Problem reading offset file {0}: {1}' - .format(offset_file, exc.strerror) + "Problem reading offset file {0}: {1}".format( + offset_file, exc.strerror + ) ) - if 'AIX' in __grains__['os_family']: - offset_file = '/etc/environment' + if "AIX" in __grains__["os_family"]: + offset_file = "/etc/environment" try: - with salt.utils.files.fopen(offset_file, 'r') as fp_: + with salt.utils.files.fopen(offset_file, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('TZ=UTC'): - return 'UTC' - return 'localtime' + if line.startswith("TZ=UTC"): + return "UTC" + return "localtime" except IOError as exc: if exc.errno == errno.ENOENT: # offset file does not exist - return 'UTC' + return "UTC" raise CommandExecutionError( - 'Problem reading offset file {0}: {1}' - .format(offset_file, exc.strerror) + "Problem reading offset file {0}: {1}".format( + offset_file, exc.strerror + ) ) def set_hwclock(clock): - ''' + """ Sets the hardware clock to be either UTC or localtime CLI Example: @@ -498,67 +518,72 @@ def set_hwclock(clock): .. code-block:: bash salt '*' timezone.set_hwclock UTC - ''' - if salt.utils.path.which('timedatectl'): - cmd = ['timedatectl', 'set-local-rtc', - 'true' if clock == 'localtime' else 'false'] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + """ + if salt.utils.path.which("timedatectl"): + cmd = [ + "timedatectl", + "set-local-rtc", + "true" if clock == "localtime" else "false", + ] + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 else: - os_family = __grains__['os_family'] - if os_family in ('AIX', 'NILinuxRT'): - if clock.lower() != 'utc': - raise SaltInvocationError( - 'UTC is the only permitted value' - ) + os_family = __grains__["os_family"] + if os_family in ("AIX", "NILinuxRT"): + if clock.lower() != "utc": + raise SaltInvocationError("UTC is the only permitted value") return True timezone = get_zone() - if 'Solaris' in __grains__['os_family']: - if clock.lower() not in ('localtime', 'utc'): + if "Solaris" in __grains__["os_family"]: + if clock.lower() not in ("localtime", "utc"): raise SaltInvocationError( - 'localtime and UTC are the only permitted values' + "localtime and UTC are the only permitted values" ) - if 'sparc' in __grains__['cpuarch']: + if "sparc" in __grains__["cpuarch"]: raise SaltInvocationError( - 'UTC is the only choice for SPARC architecture' + "UTC is the only choice for SPARC architecture" ) - cmd = ['rtc', '-z', 'GMT' if clock.lower() == 'utc' else timezone] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd = ["rtc", "-z", "GMT" if clock.lower() == "utc" else timezone] + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 - zonepath = '/usr/share/zoneinfo/{0}'.format(timezone) + zonepath = "/usr/share/zoneinfo/{0}".format(timezone) if not os.path.exists(zonepath): - raise CommandExecutionError( - 'Zone \'{0}\' does not exist'.format(zonepath) + raise CommandExecutionError("Zone '{0}' does not exist".format(zonepath)) + + os.unlink("/etc/localtime") + os.symlink(zonepath, "/etc/localtime") + + if "Arch" in __grains__["os_family"]: + cmd = [ + "timezonectl", + "set-local-rtc", + "true" if clock == "localtime" else "false", + ] + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 + elif "RedHat" in __grains__["os_family"]: + __salt__["file.sed"]( + "/etc/sysconfig/clock", "^ZONE=.*", 'ZONE="{0}"'.format(timezone) + ) + elif "Suse" in __grains__["os_family"]: + __salt__["file.sed"]( + "/etc/sysconfig/clock", + "^TIMEZONE=.*", + 'TIMEZONE="{0}"'.format(timezone), + ) + elif "Debian" in __grains__["os_family"]: + if clock == "UTC": + __salt__["file.sed"]("/etc/default/rcS", "^UTC=.*", "UTC=yes") + elif clock == "localtime": + __salt__["file.sed"]("/etc/default/rcS", "^UTC=.*", "UTC=no") + elif "Gentoo" in __grains__["os_family"]: + if clock not in ("UTC", "localtime"): + raise SaltInvocationError("Only 'UTC' and 'localtime' are allowed") + if clock == "localtime": + clock = "local" + __salt__["file.sed"]( + "/etc/conf.d/hwclock", "^clock=.*", 'clock="{0}"'.format(clock) ) - os.unlink('/etc/localtime') - os.symlink(zonepath, '/etc/localtime') - - if 'Arch' in __grains__['os_family']: - cmd = ['timezonectl', 'set-local-rtc', - 'true' if clock == 'localtime' else 'false'] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 - elif 'RedHat' in __grains__['os_family']: - __salt__['file.sed']( - '/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="{0}"'.format(timezone)) - elif 'Suse' in __grains__['os_family']: - __salt__['file.sed']( - '/etc/sysconfig/clock', '^TIMEZONE=.*', 'TIMEZONE="{0}"'.format(timezone)) - elif 'Debian' in __grains__['os_family']: - if clock == 'UTC': - __salt__['file.sed']('/etc/default/rcS', '^UTC=.*', 'UTC=yes') - elif clock == 'localtime': - __salt__['file.sed']('/etc/default/rcS', '^UTC=.*', 'UTC=no') - elif 'Gentoo' in __grains__['os_family']: - if clock not in ('UTC', 'localtime'): - raise SaltInvocationError( - 'Only \'UTC\' and \'localtime\' are allowed' - ) - if clock == 'localtime': - clock = 'local' - __salt__['file.sed']( - '/etc/conf.d/hwclock', '^clock=.*', 'clock="{0}"'.format(clock)) - return True diff --git a/salt/modules/tls.py b/salt/modules/tls.py index af845621a38..686553158b6 100644 --- a/salt/modules/tls.py +++ b/salt/modules/tls.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" A salt module for SSL/TLS. Can create a Certificate Authority (CA) or use Self-Signed certificates. @@ -98,18 +98,18 @@ Create a server req + cert with non-CN filename for the cert # salt-call tls_create_ca_signed_cert my_little CN=www.anothersometh.ing \ cert_type=server cert_filename="something_completely_different" Created Certificate for "www.anothersometh.ing": /etc/pki/my_little/certs/something_completely_different.crt -''' -from __future__ import absolute_import, unicode_literals, print_function -# pylint: disable=C0103 +""" +from __future__ import absolute_import, print_function, unicode_literals + +import binascii +import calendar +import logging +import math # Import Python libs import os import re import time -import calendar -import logging -import math -import binascii from datetime import datetime # Import Salt libs @@ -117,18 +117,22 @@ import salt.utils.data import salt.utils.files import salt.utils.stringutils from salt.exceptions import CommandExecutionError -from salt.utils.versions import LooseVersion as _LooseVersion # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range as _range +from salt.utils.versions import LooseVersion as _LooseVersion + +# pylint: disable=C0103 + HAS_SSL = False X509_EXT_ENABLED = True try: import OpenSSL + HAS_SSL = True - OpenSSL_version = _LooseVersion(OpenSSL.__dict__.get('__version__', '0.0')) + OpenSSL_version = _LooseVersion(OpenSSL.__dict__.get("__version__", "0.0")) except ImportError: pass @@ -140,39 +144,46 @@ four_digit_year_fmt = "%Y%m%d%H%M%SZ" def __virtual__(): - ''' + """ Only load this module if the ca config options are set - ''' + """ global X509_EXT_ENABLED - if HAS_SSL and OpenSSL_version >= _LooseVersion('0.10'): - if OpenSSL_version < _LooseVersion('0.14'): + if HAS_SSL and OpenSSL_version >= _LooseVersion("0.10"): + if OpenSSL_version < _LooseVersion("0.14"): X509_EXT_ENABLED = False - log.debug('You should upgrade pyOpenSSL to at least 0.14.1 to ' - 'enable the use of X509 extensions in the tls module') - elif OpenSSL_version <= _LooseVersion('0.15'): - log.debug('You should upgrade pyOpenSSL to at least 0.15.1 to ' - 'enable the full use of X509 extensions in the tls module') + log.debug( + "You should upgrade pyOpenSSL to at least 0.14.1 to " + "enable the use of X509 extensions in the tls module" + ) + elif OpenSSL_version <= _LooseVersion("0.15"): + log.debug( + "You should upgrade pyOpenSSL to at least 0.15.1 to " + "enable the full use of X509 extensions in the tls module" + ) # NOTE: Not having configured a cert path should not prevent this # module from loading as it provides methods to configure the path. return True else: X509_EXT_ENABLED = False - return (False, 'PyOpenSSL version 0.10 or later must be installed ' - 'before this module can be used.') + return ( + False, + "PyOpenSSL version 0.10 or later must be installed " + "before this module can be used.", + ) def _microtime(): - ''' + """ Return a Unix timestamp as a string of digits :return: - ''' + """ val1, val2 = math.modf(time.time()) val2 = int(val2) - return '{0:f}{1}'.format(val1, val2) + return "{0:f}{1}".format(val1, val2) def cert_base_path(cacert_path=None): - ''' + """ Return the base path for certs from CLI or from options cacert_path @@ -183,27 +194,28 @@ def cert_base_path(cacert_path=None): .. code-block:: bash salt '*' tls.cert_base_path - ''' + """ if not cacert_path: cacert_path = __context__.get( - 'ca.contextual_cert_base_path', - __salt__['config.option']('ca.contextual_cert_base_path')) + "ca.contextual_cert_base_path", + __salt__["config.option"]("ca.contextual_cert_base_path"), + ) if not cacert_path: cacert_path = __context__.get( - 'ca.cert_base_path', - __salt__['config.option']('ca.cert_base_path')) + "ca.cert_base_path", __salt__["config.option"]("ca.cert_base_path") + ) return cacert_path def _cert_base_path(cacert_path=None): - ''' + """ Retrocompatible wrapper - ''' + """ return cert_base_path(cacert_path) def set_ca_path(cacert_path): - ''' + """ If wanted, store the aforementioned cacert_path in context to be used as the basepath for further operations @@ -212,14 +224,14 @@ def set_ca_path(cacert_path): .. code-block:: bash salt '*' tls.set_ca_path /etc/certs - ''' + """ if cacert_path: - __context__['ca.contextual_cert_base_path'] = cacert_path + __context__["ca.contextual_cert_base_path"] = cacert_path return cert_base_path() def _new_serial(ca_name): - ''' + """ Return a serial number in hex using os.urandom() and a Unix timestamp in microseconds. @@ -227,28 +239,30 @@ def _new_serial(ca_name): name of the CA CN common name in the request - ''' + """ hashnum = int( binascii.hexlify( - b'_'.join(( - salt.utils.stringutils.to_bytes(_microtime()), - os.urandom(5) if six.PY3 else os.urandom(5).encode('hex') - )) + b"_".join( + ( + salt.utils.stringutils.to_bytes(_microtime()), + os.urandom(5) if six.PY3 else os.urandom(5).encode("hex"), + ) + ) ), - 16 + 16, ) - log.debug('Hashnum: %s', hashnum) + log.debug("Hashnum: %s", hashnum) # record the hash somewhere - cachedir = __opts__['cachedir'] - log.debug('cachedir: %s', cachedir) - serial_file = '{0}/{1}.serial'.format(cachedir, ca_name) + cachedir = __opts__["cachedir"] + log.debug("cachedir: %s", cachedir) + serial_file = "{0}/{1}.serial".format(cachedir, ca_name) if not os.path.exists(cachedir): os.makedirs(cachedir) if not os.path.exists(serial_file): - mode = 'w' + mode = "w" else: - mode = 'a+' + mode = "a+" with salt.utils.files.fopen(serial_file, mode) as ofile: ofile.write(str(hashnum)) # future lint: disable=blacklisted-function @@ -260,34 +274,32 @@ def _four_digit_year_to_two_digit(datetimeObj): def _get_basic_info(ca_name, cert, ca_dir=None): - ''' + """ Get basic info to write out to the index.txt - ''' + """ if ca_dir is None: - ca_dir = '{0}/{1}'.format(_cert_base_path(), ca_name) + ca_dir = "{0}/{1}".format(_cert_base_path(), ca_name) index_file = "{0}/index.txt".format(ca_dir) cert = _read_cert(cert) expire_date = _four_digit_year_to_two_digit(_get_expiration_date(cert)) - serial_number = format(cert.get_serial_number(), 'X') + serial_number = format(cert.get_serial_number(), "X") # gotta prepend a / - subject = '/' + subject = "/" # then we can add the rest of the subject - subject += '/'.join( - ['{0}={1}'.format( - x, y - ) for x, y in cert.get_subject().get_components()] + subject += "/".join( + ["{0}={1}".format(x, y) for x, y in cert.get_subject().get_components()] ) - subject += '\n' + subject += "\n" return (index_file, expire_date, serial_number, subject) -def _write_cert_to_database(ca_name, cert, cacert_path=None, status='V'): - ''' +def _write_cert_to_database(ca_name, cert, cacert_path=None, status="V"): + """ write out the index.txt database file in the appropriate directory to track certificates @@ -295,27 +307,23 @@ def _write_cert_to_database(ca_name, cert, cacert_path=None, status='V'): name of the CA cert certificate to be recorded - ''' + """ set_ca_path(cacert_path) - ca_dir = '{0}/{1}'.format(cert_base_path(), ca_name) + ca_dir = "{0}/{1}".format(cert_base_path(), ca_name) index_file, expire_date, serial_number, subject = _get_basic_info( - ca_name, - cert, - ca_dir) - - index_data = '{0}\t{1}\t\t{2}\tunknown\t{3}'.format( - status, - expire_date, - serial_number, - subject + ca_name, cert, ca_dir ) - with salt.utils.files.fopen(index_file, 'a+') as ofile: + index_data = "{0}\t{1}\t\t{2}\tunknown\t{3}".format( + status, expire_date, serial_number, subject + ) + + with salt.utils.files.fopen(index_file, "a+") as ofile: ofile.write(salt.utils.stringutils.to_str(index_data)) def maybe_fix_ssl_version(ca_name, cacert_path=None, ca_filename=None): - ''' + """ Check that the X509 version is correct (was incorrectly set in previous salt versions). This will fix the version if needed. @@ -335,36 +343,30 @@ def maybe_fix_ssl_version(ca_name, cacert_path=None, ca_filename=None): .. code-block:: bash salt '*' tls.maybe_fix_ssl_version test_ca /etc/certs - ''' + """ set_ca_path(cacert_path) if not ca_filename: - ca_filename = '{0}_ca_cert'.format(ca_name) - certp = '{0}/{1}/{2}.crt'.format( - cert_base_path(), - ca_name, - ca_filename) - ca_keyp = '{0}/{1}/{2}.key'.format( - cert_base_path(), - ca_name, - ca_filename) + ca_filename = "{0}_ca_cert".format(ca_name) + certp = "{0}/{1}/{2}.crt".format(cert_base_path(), ca_name, ca_filename) + ca_keyp = "{0}/{1}/{2}.key".format(cert_base_path(), ca_name, ca_filename) with salt.utils.files.fopen(certp) as fic: - cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, - fic.read()) + cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, fic.read()) if cert.get_version() == 3: - log.info('Regenerating wrong x509 version ' - 'for certificate %s', certp) + log.info("Regenerating wrong x509 version " "for certificate %s", certp) with salt.utils.files.fopen(ca_keyp) as fic2: try: # try to determine the key bits key = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, fic2.read()) + OpenSSL.crypto.FILETYPE_PEM, fic2.read() + ) bits = key.bits() except Exception: # pylint: disable=broad-except bits = 2048 try: - days = (datetime.strptime( - cert.get_notAfter(), - '%Y%m%d%H%M%SZ') - datetime.utcnow()).days + days = ( + datetime.strptime(cert.get_notAfter(), "%Y%m%d%H%M%SZ") + - datetime.utcnow() + ).days except (ValueError, TypeError): days = 365 subj = cert.get_subject() @@ -379,11 +381,12 @@ def maybe_fix_ssl_version(ca_name, cacert_path=None, ca_filename=None): O=subj.O, OU=subj.OU, emailAddress=subj.emailAddress, - fixmode=True) + fixmode=True, + ) def ca_exists(ca_name, cacert_path=None, ca_filename=None): - ''' + """ Verify whether a Certificate Authority (CA) already exists ca_name @@ -401,29 +404,24 @@ def ca_exists(ca_name, cacert_path=None, ca_filename=None): .. code-block:: bash salt '*' tls.ca_exists test_ca /etc/certs - ''' + """ set_ca_path(cacert_path) if not ca_filename: - ca_filename = '{0}_ca_cert'.format(ca_name) - certp = '{0}/{1}/{2}.crt'.format( - cert_base_path(), - ca_name, - ca_filename) + ca_filename = "{0}_ca_cert".format(ca_name) + certp = "{0}/{1}/{2}.crt".format(cert_base_path(), ca_name, ca_filename) if os.path.exists(certp): - maybe_fix_ssl_version(ca_name, - cacert_path=cacert_path, - ca_filename=ca_filename) + maybe_fix_ssl_version(ca_name, cacert_path=cacert_path, ca_filename=ca_filename) return True return False def _ca_exists(ca_name, cacert_path=None): - '''Retrocompatible wrapper''' + """Retrocompatible wrapper""" return ca_exists(ca_name, cacert_path) def get_ca(ca_name, as_text=False, cacert_path=None): - ''' + """ Get the certificate path or content ca_name @@ -438,13 +436,11 @@ def get_ca(ca_name, as_text=False, cacert_path=None): .. code-block:: bash salt '*' tls.get_ca test_ca as_text=False cacert_path=/etc/certs - ''' + """ set_ca_path(cacert_path) - certp = '{0}/{1}/{1}_ca_cert.crt'.format( - cert_base_path(), - ca_name) + certp = "{0}/{1}/{1}_ca_cert.crt".format(cert_base_path(), ca_name) if not os.path.exists(certp): - raise ValueError('Certificate does not exist for {0}'.format(ca_name)) + raise ValueError("Certificate does not exist for {0}".format(ca_name)) else: if as_text: with salt.utils.files.fopen(certp) as fic: @@ -452,12 +448,10 @@ def get_ca(ca_name, as_text=False, cacert_path=None): return certp -def get_ca_signed_cert(ca_name, - CN='localhost', - as_text=False, - cacert_path=None, - cert_filename=None): - ''' +def get_ca_signed_cert( + ca_name, CN="localhost", as_text=False, cacert_path=None, cert_filename=None +): + """ Get the certificate path or content ca_name @@ -479,17 +473,14 @@ def get_ca_signed_cert(ca_name, .. code-block:: bash salt '*' tls.get_ca_signed_cert test_ca CN=localhost as_text=False cacert_path=/etc/certs - ''' + """ set_ca_path(cacert_path) if not cert_filename: cert_filename = CN - certp = '{0}/{1}/certs/{2}.crt'.format( - cert_base_path(), - ca_name, - cert_filename) + certp = "{0}/{1}/certs/{2}.crt".format(cert_base_path(), ca_name, cert_filename) if not os.path.exists(certp): - raise ValueError('Certificate does not exists for {0}'.format(CN)) + raise ValueError("Certificate does not exists for {0}".format(CN)) else: if as_text: with salt.utils.files.fopen(certp) as fic: @@ -497,12 +488,10 @@ def get_ca_signed_cert(ca_name, return certp -def get_ca_signed_key(ca_name, - CN='localhost', - as_text=False, - cacert_path=None, - key_filename=None): - ''' +def get_ca_signed_key( + ca_name, CN="localhost", as_text=False, cacert_path=None, key_filename=None +): + """ Get the certificate path or content ca_name @@ -528,17 +517,14 @@ def get_ca_signed_key(ca_name, test_ca CN=localhost \ as_text=False \ cacert_path=/etc/certs - ''' + """ set_ca_path(cacert_path) if not key_filename: key_filename = CN - keyp = '{0}/{1}/certs/{2}.key'.format( - cert_base_path(), - ca_name, - key_filename) + keyp = "{0}/{1}/certs/{2}.key".format(cert_base_path(), ca_name, key_filename) if not os.path.exists(keyp): - raise ValueError('Certificate does not exists for {0}'.format(CN)) + raise ValueError("Certificate does not exists for {0}".format(CN)) else: if as_text: with salt.utils.files.fopen(keyp) as fic: @@ -551,39 +537,37 @@ def _read_cert(cert): try: with salt.utils.files.fopen(cert) as rfh: return OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, - rfh.read() + OpenSSL.crypto.FILETYPE_PEM, rfh.read() ) except Exception: # pylint: disable=broad-except - log.exception('Failed to read cert from path %s', cert) + log.exception("Failed to read cert from path %s", cert) return None else: - if not hasattr(cert, 'get_notAfter'): - log.error('%s is not a valid cert path/object', cert) + if not hasattr(cert, "get_notAfter"): + log.error("%s is not a valid cert path/object", cert) return None else: return cert def _get_expiration_date(cert): - ''' + """ Returns a datetime.datetime object - ''' + """ cert_obj = _read_cert(cert) if cert_obj is None: raise CommandExecutionError( - 'Failed to read cert from {0}, see log for details'.format(cert) + "Failed to read cert from {0}, see log for details".format(cert) ) return datetime.strptime( - salt.utils.stringutils.to_str(cert_obj.get_notAfter()), - four_digit_year_fmt + salt.utils.stringutils.to_str(cert_obj.get_notAfter()), four_digit_year_fmt ) -def get_expiration_date(cert, date_format='%Y-%m-%d'): - ''' +def get_expiration_date(cert, date_format="%Y-%m-%d"): + """ .. versionadded:: 2019.2.0 Get a certificate's expiration date @@ -602,50 +586,52 @@ def get_expiration_date(cert, date_format='%Y-%m-%d'): salt '*' tls.get_expiration_date /path/to/foo.crt salt '*' tls.get_expiration_date /path/to/foo.crt date_format='%d/%m/%Y' - ''' + """ return _get_expiration_date(cert).strftime(date_format) def _check_onlyif_unless(onlyif, unless): ret = None - retcode = __salt__['cmd.retcode'] + retcode = __salt__["cmd.retcode"] if onlyif is not None: if not isinstance(onlyif, six.string_types): if not onlyif: - ret = {'comment': 'onlyif condition is false', 'result': True} + ret = {"comment": "onlyif condition is false", "result": True} elif isinstance(onlyif, six.string_types): if retcode(onlyif) != 0: - ret = {'comment': 'onlyif condition is false', 'result': True} - log.debug('onlyif condition is false') + ret = {"comment": "onlyif condition is false", "result": True} + log.debug("onlyif condition is false") if unless is not None: if not isinstance(unless, six.string_types): if unless: - ret = {'comment': 'unless condition is true', 'result': True} + ret = {"comment": "unless condition is true", "result": True} elif isinstance(unless, six.string_types): if retcode(unless) == 0: - ret = {'comment': 'unless condition is true', 'result': True} - log.debug('unless condition is true') + ret = {"comment": "unless condition is true", "result": True} + log.debug("unless condition is true") return ret -def create_ca(ca_name, - bits=2048, - days=365, - CN='localhost', - C='US', - ST='Utah', - L='Salt Lake City', - O='SaltStack', - OU=None, - emailAddress=None, - fixmode=False, - cacert_path=None, - ca_filename=None, - digest='sha256', - onlyif=None, - unless=None, - replace=False): - ''' +def create_ca( + ca_name, + bits=2048, + days=365, + CN="localhost", + C="US", + ST="Utah", + L="Salt Lake City", + O="SaltStack", + OU=None, + emailAddress=None, + fixmode=False, + cacert_path=None, + ca_filename=None, + digest="sha256", + onlyif=None, + unless=None, + replace=False, +): + """ Create a Certificate Authority (CA) ca_name @@ -704,7 +690,7 @@ def create_ca(ca_name, .. code-block:: bash salt '*' tls.create_ca test_ca - ''' + """ status = _check_onlyif_unless(onlyif, unless) if status is not None: return None @@ -712,23 +698,18 @@ def create_ca(ca_name, set_ca_path(cacert_path) if not ca_filename: - ca_filename = '{0}_ca_cert'.format(ca_name) + ca_filename = "{0}_ca_cert".format(ca_name) - certp = '{0}/{1}/{2}.crt'.format( - cert_base_path(), ca_name, ca_filename) - ca_keyp = '{0}/{1}/{2}.key'.format( - cert_base_path(), ca_name, ca_filename) + certp = "{0}/{1}/{2}.crt".format(cert_base_path(), ca_name, ca_filename) + ca_keyp = "{0}/{1}/{2}.key".format(cert_base_path(), ca_name, ca_filename) if not replace and not fixmode and ca_exists(ca_name, ca_filename=ca_filename): return 'Certificate for CA named "{0}" already exists'.format(ca_name) if fixmode and not os.path.exists(certp): - raise ValueError('{0} does not exists, can\'t fix'.format(certp)) + raise ValueError("{0} does not exists, can't fix".format(certp)) - if not os.path.exists('{0}/{1}'.format( - cert_base_path(), ca_name) - ): - os.makedirs('{0}/{1}'.format(cert_base_path(), - ca_name)) + if not os.path.exists("{0}/{1}".format(cert_base_path(), ca_name)): + os.makedirs("{0}/{1}".format(cert_base_path(), ca_name)) # try to reuse existing ssl key key = None @@ -737,14 +718,19 @@ def create_ca(ca_name, # try to determine the key bits try: key = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, fic2.read()) + OpenSSL.crypto.FILETYPE_PEM, fic2.read() + ) except OpenSSL.crypto.Error as err: - log.warning('Error loading existing private key' - ' %s, generating a new key: %s', - ca_keyp, err) - bck = "{0}.unloadable.{1}".format(ca_keyp, - datetime.utcnow().strftime("%Y%m%d%H%M%S")) - log.info('Saving unloadable CA ssl key in %s', bck) + log.warning( + "Error loading existing private key" + " %s, generating a new key: %s", + ca_keyp, + err, + ) + bck = "{0}.unloadable.{1}".format( + ca_keyp, datetime.utcnow().strftime("%Y%m%d%H%M%S") + ) + log.info("Saving unloadable CA ssl key in %s", bck) os.rename(ca_keyp, bck) if not key: @@ -770,64 +756,71 @@ def create_ca(ca_name, ca.set_pubkey(key) if X509_EXT_ENABLED: - ca.add_extensions([ - OpenSSL.crypto.X509Extension( - b'basicConstraints', True, b'CA:TRUE, pathlen:0'), - OpenSSL.crypto.X509Extension( - b'keyUsage', True, b'keyCertSign, cRLSign'), - OpenSSL.crypto.X509Extension( - b'subjectKeyIdentifier', False, b'hash', subject=ca)]) + ca.add_extensions( + [ + OpenSSL.crypto.X509Extension( + b"basicConstraints", True, b"CA:TRUE, pathlen:0" + ), + OpenSSL.crypto.X509Extension( + b"keyUsage", True, b"keyCertSign, cRLSign" + ), + OpenSSL.crypto.X509Extension( + b"subjectKeyIdentifier", False, b"hash", subject=ca + ), + ] + ) - ca.add_extensions([ - OpenSSL.crypto.X509Extension( - b'authorityKeyIdentifier', - False, - b'issuer:always,keyid:always', - issuer=ca)]) + ca.add_extensions( + [ + OpenSSL.crypto.X509Extension( + b"authorityKeyIdentifier", + False, + b"issuer:always,keyid:always", + issuer=ca, + ) + ] + ) ca.sign(key, salt.utils.stringutils.to_str(digest)) # always backup existing keys in case - keycontent = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, - key) + keycontent = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) write_key = True if os.path.exists(ca_keyp): - bck = "{0}.{1}".format(ca_keyp, datetime.utcnow().strftime( - "%Y%m%d%H%M%S")) + bck = "{0}.{1}".format(ca_keyp, datetime.utcnow().strftime("%Y%m%d%H%M%S")) with salt.utils.files.fopen(ca_keyp) as fic: old_key = salt.utils.stringutils.to_unicode(fic.read()).strip() if old_key.strip() == keycontent.strip(): write_key = False else: - log.info('Saving old CA ssl key in %s', bck) - with salt.utils.files.fopen(bck, 'w') as bckf: + log.info("Saving old CA ssl key in %s", bck) + with salt.utils.files.fopen(bck, "w") as bckf: bckf.write(old_key) os.chmod(bck, 0o600) if write_key: - with salt.utils.files.fopen(ca_keyp, 'wb') as ca_key: + with salt.utils.files.fopen(ca_keyp, "wb") as ca_key: ca_key.write(salt.utils.stringutils.to_bytes(keycontent)) - with salt.utils.files.fopen(certp, 'wb') as ca_crt: + with salt.utils.files.fopen(certp, "wb") as ca_crt: ca_crt.write( salt.utils.stringutils.to_bytes( - OpenSSL.crypto.dump_certificate( - OpenSSL.crypto.FILETYPE_PEM, - ca - ) + OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, ca) ) ) _write_cert_to_database(ca_name, ca) ret = ('Created Private Key: "{0}/{1}/{2}.key." ').format( - cert_base_path(), ca_name, ca_filename) + cert_base_path(), ca_name, ca_filename + ) ret += ('Created CA "{0}": "{1}/{0}/{2}.crt."').format( - ca_name, cert_base_path(), ca_filename) + ca_name, cert_base_path(), ca_filename + ) return ret def get_extensions(cert_type): - ''' + """ Fetch X509 and CSR extension definitions from tls:extensions: (common|server|client) or set them to standard defaults. @@ -842,73 +835,77 @@ def get_extensions(cert_type): salt '*' tls.get_extensions client - ''' + """ - assert X509_EXT_ENABLED, ('X509 extensions are not supported in ' - 'pyOpenSSL prior to version 0.15.1. Your ' - 'version: {0}'.format(OpenSSL_version)) + assert X509_EXT_ENABLED, ( + "X509 extensions are not supported in " + "pyOpenSSL prior to version 0.15.1. Your " + "version: {0}".format(OpenSSL_version) + ) ext = {} - if cert_type == '': - log.error('cert_type set to empty in tls_ca.get_extensions(); ' - 'defaulting to ``server``') - cert_type = 'server' + if cert_type == "": + log.error( + "cert_type set to empty in tls_ca.get_extensions(); " + "defaulting to ``server``" + ) + cert_type = "server" try: - ext['common'] = __salt__['pillar.get']('tls.extensions:common', False) + ext["common"] = __salt__["pillar.get"]("tls.extensions:common", False) except NameError as err: log.debug(err) - if not ext['common'] or ext['common'] == '': - ext['common'] = { - 'csr': { - 'basicConstraints': 'CA:FALSE', - }, - 'cert': { - 'authorityKeyIdentifier': 'keyid,issuer:always', - 'subjectKeyIdentifier': 'hash', + if not ext["common"] or ext["common"] == "": + ext["common"] = { + "csr": {"basicConstraints": "CA:FALSE"}, + "cert": { + "authorityKeyIdentifier": "keyid,issuer:always", + "subjectKeyIdentifier": "hash", }, } try: - ext['server'] = __salt__['pillar.get']('tls.extensions:server', False) + ext["server"] = __salt__["pillar.get"]("tls.extensions:server", False) except NameError as err: log.debug(err) - if not ext['server'] or ext['server'] == '': - ext['server'] = { - 'csr': { - 'extendedKeyUsage': 'serverAuth', - 'keyUsage': 'digitalSignature, keyEncipherment', + if not ext["server"] or ext["server"] == "": + ext["server"] = { + "csr": { + "extendedKeyUsage": "serverAuth", + "keyUsage": "digitalSignature, keyEncipherment", }, - 'cert': {}, + "cert": {}, } try: - ext['client'] = __salt__['pillar.get']('tls.extensions:client', False) + ext["client"] = __salt__["pillar.get"]("tls.extensions:client", False) except NameError as err: log.debug(err) - if not ext['client'] or ext['client'] == '': - ext['client'] = { - 'csr': { - 'extendedKeyUsage': 'clientAuth', - 'keyUsage': 'nonRepudiation, digitalSignature, keyEncipherment', + if not ext["client"] or ext["client"] == "": + ext["client"] = { + "csr": { + "extendedKeyUsage": "clientAuth", + "keyUsage": "nonRepudiation, digitalSignature, keyEncipherment", }, - 'cert': {}, + "cert": {}, } # possible user-defined profile or a typo if cert_type not in ext: try: - ext[cert_type] = __salt__['pillar.get']( - 'tls.extensions:{0}'.format(cert_type)) + ext[cert_type] = __salt__["pillar.get"]( + "tls.extensions:{0}".format(cert_type) + ) except NameError as e: log.debug( - 'pillar, tls:extensions:{0} not available or ' - 'not operating in a salt context\n{1}'.format(cert_type, e)) + "pillar, tls:extensions:{0} not available or " + "not operating in a salt context\n{1}".format(cert_type, e) + ) - retval = ext['common'] + retval = ext["common"] for Use in retval: retval[Use].update(ext[cert_type][Use]) @@ -916,25 +913,27 @@ def get_extensions(cert_type): return retval -def create_csr(ca_name, - bits=2048, - CN='localhost', - C='US', - ST='Utah', - L='Salt Lake City', - O='SaltStack', - OU=None, - emailAddress=None, - subjectAltName=None, - cacert_path=None, - ca_filename=None, - csr_path=None, - csr_filename=None, - digest='sha256', - type_ext=False, - cert_type='server', - replace=False): - ''' +def create_csr( + ca_name, + bits=2048, + CN="localhost", + C="US", + ST="Utah", + L="Salt Lake City", + O="SaltStack", + OU=None, + emailAddress=None, + subjectAltName=None, + cacert_path=None, + ca_filename=None, + csr_path=None, + csr_filename=None, + digest="sha256", + type_ext=False, + cert_type="server", + replace=False, +): + """ Create a Certificate Signing Request (CSR) for a particular Certificate Authority (CA) @@ -1030,28 +1029,29 @@ def create_csr(ca_name, .. code-block:: bash salt '*' tls.create_csr test - ''' + """ set_ca_path(cacert_path) if not ca_filename: - ca_filename = '{0}_ca_cert'.format(ca_name) + ca_filename = "{0}_ca_cert".format(ca_name) if not ca_exists(ca_name, ca_filename=ca_filename): - return ('Certificate for CA named "{0}" does not exist, please create ' - 'it first.').format(ca_name) + return ( + 'Certificate for CA named "{0}" does not exist, please create ' "it first." + ).format(ca_name) if not csr_path: - csr_path = '{0}/{1}/certs/'.format(cert_base_path(), ca_name) + csr_path = "{0}/{1}/certs/".format(cert_base_path(), ca_name) if not os.path.exists(csr_path): os.makedirs(csr_path) - CN_ext = '_{0}'.format(cert_type) if type_ext else '' + CN_ext = "_{0}".format(cert_type) if type_ext else "" if not csr_filename: - csr_filename = '{0}{1}'.format(CN, CN_ext) + csr_filename = "{0}{1}".format(CN, CN_ext) - csr_f = '{0}/{1}.csr'.format(csr_path, csr_filename) + csr_f = "{0}/{1}.csr".format(csr_path, csr_filename) if not replace and os.path.exists(csr_f): return 'Certificate Request "{0}" already exists'.format(csr_f) @@ -1072,7 +1072,7 @@ def create_csr(ca_name, req.get_subject().emailAddress = emailAddress try: - extensions = get_extensions(cert_type)['csr'] + extensions = get_extensions(cert_type)["csr"] extension_adds = [] @@ -1081,9 +1081,7 @@ def create_csr(ca_name, value = salt.utils.stringutils.to_bytes(value) extension_adds.append( OpenSSL.crypto.X509Extension( - salt.utils.stringutils.to_bytes(ext), - False, - value + salt.utils.stringutils.to_bytes(ext), False, value ) ) except AssertionError as err: @@ -1097,16 +1095,18 @@ def create_csr(ca_name, extension_adds.append( OpenSSL.crypto.X509Extension( - b'subjectAltName', + b"subjectAltName", False, - b', '.join(salt.utils.data.encode(subjectAltName)) + b", ".join(salt.utils.data.encode(subjectAltName)), ) ) else: - raise ValueError('subjectAltName cannot be set as X509 ' - 'extensions are not supported in pyOpenSSL ' - 'prior to version 0.15.1. Your ' - 'version: {0}.'.format(OpenSSL_version)) + raise ValueError( + "subjectAltName cannot be set as X509 " + "extensions are not supported in pyOpenSSL " + "prior to version 0.15.1. Your " + "version: {0}.".format(OpenSSL_version) + ) if X509_EXT_ENABLED: req.add_extensions(extension_adds) @@ -1115,54 +1115,47 @@ def create_csr(ca_name, req.sign(key, salt.utils.stringutils.to_str(digest)) # Write private key and request - with salt.utils.files.fopen('{0}/{1}.key'.format(csr_path, - csr_filename), - 'wb+') as priv_key: + with salt.utils.files.fopen( + "{0}/{1}.key".format(csr_path, csr_filename), "wb+" + ) as priv_key: priv_key.write( salt.utils.stringutils.to_bytes( - OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, - key) + OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) ) ) - with salt.utils.files.fopen(csr_f, 'wb+') as csr: + with salt.utils.files.fopen(csr_f, "wb+") as csr: csr.write( salt.utils.stringutils.to_bytes( OpenSSL.crypto.dump_certificate_request( - OpenSSL.crypto.FILETYPE_PEM, - req + OpenSSL.crypto.FILETYPE_PEM, req ) ) ) - ret = 'Created Private Key: "{0}{1}.key." '.format( - csr_path, - csr_filename - ) - ret += 'Created CSR for "{0}": "{1}{2}.csr."'.format( - CN, - csr_path, - csr_filename - ) + ret = 'Created Private Key: "{0}{1}.key." '.format(csr_path, csr_filename) + ret += 'Created CSR for "{0}": "{1}{2}.csr."'.format(CN, csr_path, csr_filename) return ret -def create_self_signed_cert(tls_dir='tls', - bits=2048, - days=365, - CN='localhost', - C='US', - ST='Utah', - L='Salt Lake City', - O='SaltStack', - OU=None, - emailAddress=None, - cacert_path=None, - cert_filename=None, - digest='sha256', - replace=False): - ''' +def create_self_signed_cert( + tls_dir="tls", + bits=2048, + days=365, + CN="localhost", + C="US", + ST="Utah", + L="Salt Lake City", + O="SaltStack", + OU=None, + emailAddress=None, + cacert_path=None, + cert_filename=None, + digest="sha256", + replace=False, +): + """ Create a Self-Signed Certificate (CERT) tls_dir @@ -1221,19 +1214,17 @@ def create_self_signed_cert(tls_dir='tls', .. code-block:: bash salt 'minion' tls.create_self_signed_cert CN='test.mysite.org' - ''' + """ set_ca_path(cacert_path) - if not os.path.exists('{0}/{1}/certs/'.format(cert_base_path(), tls_dir)): - os.makedirs("{0}/{1}/certs/".format(cert_base_path(), - tls_dir)) + if not os.path.exists("{0}/{1}/certs/".format(cert_base_path(), tls_dir)): + os.makedirs("{0}/{1}/certs/".format(cert_base_path(), tls_dir)) if not cert_filename: cert_filename = CN if not replace and os.path.exists( - '{0}/{1}/certs/{2}.crt'.format(cert_base_path(), - tls_dir, cert_filename) + "{0}/{1}/certs/{2}.crt".format(cert_base_path(), tls_dir, cert_filename) ): return 'Certificate "{0}" already exists'.format(cert_filename) @@ -1263,58 +1254,50 @@ def create_self_signed_cert(tls_dir='tls', cert.sign(key, salt.utils.stringutils.to_str(digest)) # Write private key and cert - priv_key_path = '{0}/{1}/certs/{2}.key'.format(cert_base_path(), - tls_dir, - cert_filename) - with salt.utils.files.fopen(priv_key_path, 'wb+') as priv_key: + priv_key_path = "{0}/{1}/certs/{2}.key".format( + cert_base_path(), tls_dir, cert_filename + ) + with salt.utils.files.fopen(priv_key_path, "wb+") as priv_key: priv_key.write( salt.utils.stringutils.to_bytes( - OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, - key) + OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key) ) ) - crt_path = '{0}/{1}/certs/{2}.crt'.format(cert_base_path(), - tls_dir, - cert_filename) - with salt.utils.files.fopen(crt_path, 'wb+') as crt: + crt_path = "{0}/{1}/certs/{2}.crt".format(cert_base_path(), tls_dir, cert_filename) + with salt.utils.files.fopen(crt_path, "wb+") as crt: crt.write( salt.utils.stringutils.to_bytes( - OpenSSL.crypto.dump_certificate( - OpenSSL.crypto.FILETYPE_PEM, - cert - ) + OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) ) ) _write_cert_to_database(tls_dir, cert) ret = 'Created Private Key: "{0}/{1}/certs/{2}.key." '.format( - cert_base_path(), - tls_dir, - cert_filename + cert_base_path(), tls_dir, cert_filename ) ret += 'Created Certificate: "{0}/{1}/certs/{2}.crt."'.format( - cert_base_path(), - tls_dir, - cert_filename + cert_base_path(), tls_dir, cert_filename ) return ret -def create_ca_signed_cert(ca_name, - CN, - days=365, - cacert_path=None, - ca_filename=None, - cert_path=None, - cert_filename=None, - digest='sha256', - cert_type=None, - type_ext=False, - replace=False): - ''' +def create_ca_signed_cert( + ca_name, + CN, + days=365, + cacert_path=None, + ca_filename=None, + cert_path=None, + cert_filename=None, + digest="sha256", + cert_type=None, + type_ext=False, + replace=False, +): + """ Create a Certificate (CERT) signed by a named Certificate Authority (CA) If the certificate file already exists, the function just returns assuming @@ -1403,75 +1386,74 @@ def create_ca_signed_cert(ca_name, .. code-block:: bash salt '*' tls.create_ca_signed_cert test localhost - ''' + """ ret = {} set_ca_path(cacert_path) if not ca_filename: - ca_filename = '{0}_ca_cert'.format(ca_name) + ca_filename = "{0}_ca_cert".format(ca_name) if not cert_path: - cert_path = '{0}/{1}/certs'.format(cert_base_path(), ca_name) + cert_path = "{0}/{1}/certs".format(cert_base_path(), ca_name) if type_ext: if not cert_type: - log.error('type_ext = True but cert_type is unset. ' - 'Certificate not written.') + log.error( + "type_ext = True but cert_type is unset. " "Certificate not written." + ) return ret elif cert_type: - CN_ext = '_{0}'.format(cert_type) + CN_ext = "_{0}".format(cert_type) else: - CN_ext = '' + CN_ext = "" - csr_filename = '{0}{1}'.format(CN, CN_ext) + csr_filename = "{0}{1}".format(CN, CN_ext) if not cert_filename: - cert_filename = '{0}{1}'.format(CN, CN_ext) + cert_filename = "{0}{1}".format(CN, CN_ext) if not replace and os.path.exists( - os.path.join( - os.path.sep.join('{0}/{1}/certs/{2}.crt'.format( - cert_base_path(), - ca_name, - cert_filename).split('/') - ) + os.path.join( + os.path.sep.join( + "{0}/{1}/certs/{2}.crt".format( + cert_base_path(), ca_name, cert_filename + ).split("/") ) + ) ): return 'Certificate "{0}" already exists'.format(cert_filename) try: - maybe_fix_ssl_version(ca_name, - cacert_path=cacert_path, - ca_filename=ca_filename) - with salt.utils.files.fopen('{0}/{1}/{2}.crt'.format(cert_base_path(), - ca_name, - ca_filename)) as fhr: + maybe_fix_ssl_version(ca_name, cacert_path=cacert_path, ca_filename=ca_filename) + with salt.utils.files.fopen( + "{0}/{1}/{2}.crt".format(cert_base_path(), ca_name, ca_filename) + ) as fhr: ca_cert = OpenSSL.crypto.load_certificate( OpenSSL.crypto.FILETYPE_PEM, fhr.read() ) - with salt.utils.files.fopen('{0}/{1}/{2}.key'.format(cert_base_path(), - ca_name, - ca_filename)) as fhr: + with salt.utils.files.fopen( + "{0}/{1}/{2}.key".format(cert_base_path(), ca_name, ca_filename) + ) as fhr: ca_key = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, - fhr.read() + OpenSSL.crypto.FILETYPE_PEM, fhr.read() ) except IOError: - ret['retcode'] = 1 - ret['comment'] = 'There is no CA named "{0}"'.format(ca_name) + ret["retcode"] = 1 + ret["comment"] = 'There is no CA named "{0}"'.format(ca_name) return ret try: - csr_path = '{0}/{1}.csr'.format(cert_path, csr_filename) + csr_path = "{0}/{1}.csr".format(cert_path, csr_filename) with salt.utils.files.fopen(csr_path) as fhr: req = OpenSSL.crypto.load_certificate_request( - OpenSSL.crypto.FILETYPE_PEM, - fhr.read()) + OpenSSL.crypto.FILETYPE_PEM, fhr.read() + ) except IOError: - ret['retcode'] = 1 - ret['comment'] = 'There is no CSR that matches the CN "{0}"'.format( - cert_filename) + ret["retcode"] = 1 + ret["comment"] = 'There is no CSR that matches the CN "{0}"'.format( + cert_filename + ) return ret exts = [] @@ -1483,26 +1465,25 @@ def create_ca_signed_cert(ca_name, # support is there from quite a long time, but without API # so we mimic the newly get_extensions method present in ultra # recent pyopenssl distros - log.info('req.get_extensions() not supported in pyOpenSSL versions ' - 'prior to 0.15. Processing extensions internally. ' - ' Your version: {0}'.format( - OpenSSL_version)) + log.info( + "req.get_extensions() not supported in pyOpenSSL versions " + "prior to 0.15. Processing extensions internally. " + " Your version: {0}".format(OpenSSL_version) + ) - native_exts_obj = OpenSSL._util.lib.X509_REQ_get_extensions( - req._req) - for i in _range(OpenSSL._util.lib.sk_X509_EXTENSION_num( - native_exts_obj)): - ext = OpenSSL.crypto.X509Extension.__new__( - OpenSSL.crypto.X509Extension) + native_exts_obj = OpenSSL._util.lib.X509_REQ_get_extensions(req._req) + for i in _range(OpenSSL._util.lib.sk_X509_EXTENSION_num(native_exts_obj)): + ext = OpenSSL.crypto.X509Extension.__new__(OpenSSL.crypto.X509Extension) ext._extension = OpenSSL._util.lib.sk_X509_EXTENSION_value( - native_exts_obj, - i) + native_exts_obj, i + ) exts.append(ext) except Exception: # pylint: disable=broad-except - log.error('X509 extensions are unsupported in pyOpenSSL ' - 'versions prior to 0.14. Upgrade required to ' - 'use extensions. Current version: {0}'.format( - OpenSSL_version)) + log.error( + "X509 extensions are unsupported in pyOpenSSL " + "versions prior to 0.14. Upgrade required to " + "use extensions. Current version: {0}".format(OpenSSL_version) + ) cert = OpenSSL.crypto.X509() cert.set_version(2) @@ -1517,27 +1498,24 @@ def create_ca_signed_cert(ca_name, cert.sign(ca_key, salt.utils.stringutils.to_str(digest)) - cert_full_path = '{0}/{1}.crt'.format(cert_path, cert_filename) + cert_full_path = "{0}/{1}.crt".format(cert_path, cert_filename) - with salt.utils.files.fopen(cert_full_path, 'wb+') as crt: + with salt.utils.files.fopen(cert_full_path, "wb+") as crt: crt.write( salt.utils.stringutils.to_bytes( - OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, - cert) + OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) ) ) _write_cert_to_database(ca_name, cert) return 'Created Certificate for "{0}": "{1}/{2}.crt"'.format( - CN, - cert_path, - cert_filename + CN, cert_path, cert_filename ) -def create_pkcs12(ca_name, CN, passphrase='', cacert_path=None, replace=False): - ''' +def create_pkcs12(ca_name, CN, passphrase="", cacert_path=None, replace=False): + """ Create a PKCS#12 browser certificate for a particular Certificate (CN) ca_name @@ -1569,40 +1547,35 @@ def create_pkcs12(ca_name, CN, passphrase='', cacert_path=None, replace=False): .. code-block:: bash salt '*' tls.create_pkcs12 test localhost - ''' + """ set_ca_path(cacert_path) if not replace and os.path.exists( - '{0}/{1}/certs/{2}.p12'.format( - cert_base_path(), - ca_name, - CN) + "{0}/{1}/certs/{2}.p12".format(cert_base_path(), ca_name, CN) ): return 'Certificate "{0}" already exists'.format(CN) try: - with salt.utils.files.fopen('{0}/{1}/{1}_ca_cert.crt'.format(cert_base_path(), - ca_name)) as fhr: + with salt.utils.files.fopen( + "{0}/{1}/{1}_ca_cert.crt".format(cert_base_path(), ca_name) + ) as fhr: ca_cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, - fhr.read() + OpenSSL.crypto.FILETYPE_PEM, fhr.read() ) except IOError: return 'There is no CA named "{0}"'.format(ca_name) try: - with salt.utils.files.fopen('{0}/{1}/certs/{2}.crt'.format(cert_base_path(), - ca_name, - CN)) as fhr: + with salt.utils.files.fopen( + "{0}/{1}/certs/{2}.crt".format(cert_base_path(), ca_name, CN) + ) as fhr: cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, - fhr.read() + OpenSSL.crypto.FILETYPE_PEM, fhr.read() ) - with salt.utils.files.fopen('{0}/{1}/certs/{2}.key'.format(cert_base_path(), - ca_name, - CN)) as fhr: + with salt.utils.files.fopen( + "{0}/{1}/certs/{2}.key".format(cert_base_path(), ca_name, CN) + ) as fhr: key = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, - fhr.read() + OpenSSL.crypto.FILETYPE_PEM, fhr.read() ) except IOError: return 'There is no certificate that matches the CN "{0}"'.format(CN) @@ -1613,25 +1586,20 @@ def create_pkcs12(ca_name, CN, passphrase='', cacert_path=None, replace=False): pkcs12.set_ca_certificates([ca_cert]) pkcs12.set_privatekey(key) - with salt.utils.files.fopen('{0}/{1}/certs/{2}.p12'.format(cert_base_path(), - ca_name, - CN), 'wb') as ofile: + with salt.utils.files.fopen( + "{0}/{1}/certs/{2}.p12".format(cert_base_path(), ca_name, CN), "wb" + ) as ofile: ofile.write( - pkcs12.export( - passphrase=salt.utils.stringutils.to_bytes(passphrase) - ) + pkcs12.export(passphrase=salt.utils.stringutils.to_bytes(passphrase)) ) - return ('Created PKCS#12 Certificate for "{0}": ' - '"{1}/{2}/certs/{0}.p12"').format( - CN, - cert_base_path(), - ca_name, + return ('Created PKCS#12 Certificate for "{0}": ' '"{1}/{2}/certs/{0}.p12"').format( + CN, cert_base_path(), ca_name, ) -def cert_info(cert, digest='sha256'): - ''' +def cert_info(cert, digest="sha256"): + """ Return information for a particular certificate cert @@ -1648,16 +1616,13 @@ def cert_info(cert, digest='sha256'): salt '*' tls.cert_info /dir/for/certs/cert.pem - ''' + """ # format that OpenSSL returns dates in - date_fmt = '%Y%m%d%H%M%SZ' - if '-----BEGIN' not in cert: + date_fmt = "%Y%m%d%H%M%SZ" + if "-----BEGIN" not in cert: with salt.utils.files.fopen(cert) as cert_file: cert = cert_file.read() - cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, - cert - ) + cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert) issuer = {} for key, value in cert.get_issuer().get_components(): @@ -1676,49 +1641,55 @@ def cert_info(cert, digest='sha256'): subject[key] = value ret = { - 'fingerprint': salt.utils.stringutils.to_unicode( + "fingerprint": salt.utils.stringutils.to_unicode( cert.digest(salt.utils.stringutils.to_str(digest)) - ), - 'subject': subject, - 'issuer': issuer, - 'serial_number': cert.get_serial_number(), - 'not_before': calendar.timegm(time.strptime( - str(cert.get_notBefore().decode(__salt_system_encoding__)), - date_fmt)), - 'not_after': calendar.timegm(time.strptime( - cert.get_notAfter().decode(__salt_system_encoding__), - date_fmt)), + ), + "subject": subject, + "issuer": issuer, + "serial_number": cert.get_serial_number(), + "not_before": calendar.timegm( + time.strptime( + str(cert.get_notBefore().decode(__salt_system_encoding__)), date_fmt + ) + ), + "not_after": calendar.timegm( + time.strptime( + cert.get_notAfter().decode(__salt_system_encoding__), date_fmt + ) + ), } # add additional info if your version of pyOpenSSL supports it - if hasattr(cert, 'get_extension_count'): - ret['extensions'] = {} + if hasattr(cert, "get_extension_count"): + ret["extensions"] = {} for i in _range(cert.get_extension_count()): try: ext = cert.get_extension(i) key = salt.utils.stringutils.to_unicode(ext.get_short_name()) - ret['extensions'][key] = str(ext).strip() + ret["extensions"][key] = str(ext).strip() except AttributeError: continue - if 'subjectAltName' in ret.get('extensions', {}): - valid_entries = ('DNS', 'IP Address') + if "subjectAltName" in ret.get("extensions", {}): + valid_entries = ("DNS", "IP Address") valid_names = set() - for name in str(ret['extensions']['subjectAltName']).split(', '): - entry, name = name.split(':', 1) + for name in str(ret["extensions"]["subjectAltName"]).split(", "): + entry, name = name.split(":", 1) if entry not in valid_entries: - log.error('Cert {0} has an entry ({1}) which does not start ' - 'with {2}'.format(ret['subject'], name, '/'.join(valid_entries))) + log.error( + "Cert {0} has an entry ({1}) which does not start " + "with {2}".format(ret["subject"], name, "/".join(valid_entries)) + ) else: valid_names.add(name) - ret['subject_alt_names'] = list(valid_names) + ret["subject_alt_names"] = list(valid_names) - if hasattr(cert, 'get_signature_algorithm'): + if hasattr(cert, "get_signature_algorithm"): try: value = cert.get_signature_algorithm() if isinstance(value, bytes): value = salt.utils.stringutils.to_unicode(value) - ret['signature_algorithm'] = value + ret["signature_algorithm"] = value except AttributeError: # On py3 at least # AttributeError: cdata 'X509 *' points to an opaque type: cannot read fields @@ -1727,12 +1698,8 @@ def cert_info(cert, digest='sha256'): return ret -def create_empty_crl( - ca_name, - cacert_path=None, - ca_filename=None, - crl_file=None): - ''' +def create_empty_crl(ca_name, cacert_path=None, ca_filename=None, crl_file=None): + """ Create an empty Certificate Revocation List. .. versionadded:: 2015.8.0 @@ -1756,38 +1723,31 @@ def create_empty_crl( salt '*' tls.create_empty_crl ca_name='koji' \ ca_filename='ca' \ crl_file='/etc/openvpn/team1/crl.pem' - ''' + """ set_ca_path(cacert_path) if not ca_filename: - ca_filename = '{0}_ca_cert'.format(ca_name) + ca_filename = "{0}_ca_cert".format(ca_name) if not crl_file: - crl_file = '{0}/{1}/crl.pem'.format( - _cert_base_path(), - ca_name - ) + crl_file = "{0}/{1}/crl.pem".format(_cert_base_path(), ca_name) - if os.path.exists('{0}'.format(crl_file)): + if os.path.exists("{0}".format(crl_file)): return 'CRL "{0}" already exists'.format(crl_file) try: - with salt.utils.files.fopen('{0}/{1}/{2}.crt'.format( - cert_base_path(), - ca_name, - ca_filename)) as fp_: + with salt.utils.files.fopen( + "{0}/{1}/{2}.crt".format(cert_base_path(), ca_name, ca_filename) + ) as fp_: ca_cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, - fp_.read() + OpenSSL.crypto.FILETYPE_PEM, fp_.read() ) - with salt.utils.files.fopen('{0}/{1}/{2}.key'.format( - cert_base_path(), - ca_name, - ca_filename)) as fp_: + with salt.utils.files.fopen( + "{0}/{1}/{2}.key".format(cert_base_path(), ca_name, ca_filename) + ) as fp_: ca_key = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, - fp_.read() + OpenSSL.crypto.FILETYPE_PEM, fp_.read() ) except IOError: return 'There is no CA named "{0}"'.format(ca_name) @@ -1795,21 +1755,22 @@ def create_empty_crl( crl = OpenSSL.crypto.CRL() crl_text = crl.export(ca_cert, ca_key) - with salt.utils.files.fopen(crl_file, 'w') as f: + with salt.utils.files.fopen(crl_file, "w") as f: f.write(salt.utils.stringutils.to_str(crl_text)) return 'Created an empty CRL: "{0}"'.format(crl_file) def revoke_cert( - ca_name, - CN, - cacert_path=None, - ca_filename=None, - cert_path=None, - cert_filename=None, - crl_file=None): - ''' + ca_name, + CN, + cacert_path=None, + ca_filename=None, + cert_path=None, + cert_filename=None, + crl_file=None, +): + """ Revoke a certificate. .. versionadded:: 2015.8.0 @@ -1844,92 +1805,77 @@ def revoke_cert( ca_filename='ca' \ crl_file='/etc/openvpn/team1/crl.pem' - ''' + """ set_ca_path(cacert_path) - ca_dir = '{0}/{1}'.format(cert_base_path(), ca_name) + ca_dir = "{0}/{1}".format(cert_base_path(), ca_name) if ca_filename is None: - ca_filename = '{0}_ca_cert'.format(ca_name) + ca_filename = "{0}_ca_cert".format(ca_name) if cert_path is None: - cert_path = '{0}/{1}/certs'.format(_cert_base_path(), ca_name) + cert_path = "{0}/{1}/certs".format(_cert_base_path(), ca_name) if cert_filename is None: - cert_filename = '{0}'.format(CN) + cert_filename = "{0}".format(CN) try: - with salt.utils.files.fopen('{0}/{1}/{2}.crt'.format( - cert_base_path(), - ca_name, - ca_filename)) as fp_: + with salt.utils.files.fopen( + "{0}/{1}/{2}.crt".format(cert_base_path(), ca_name, ca_filename) + ) as fp_: ca_cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, - fp_.read() + OpenSSL.crypto.FILETYPE_PEM, fp_.read() ) - with salt.utils.files.fopen('{0}/{1}/{2}.key'.format( - cert_base_path(), - ca_name, - ca_filename)) as fp_: + with salt.utils.files.fopen( + "{0}/{1}/{2}.key".format(cert_base_path(), ca_name, ca_filename) + ) as fp_: ca_key = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, - fp_.read() + OpenSSL.crypto.FILETYPE_PEM, fp_.read() ) except IOError: return 'There is no CA named "{0}"'.format(ca_name) - client_cert = _read_cert('{0}/{1}.crt'.format(cert_path, cert_filename)) + client_cert = _read_cert("{0}/{1}.crt".format(cert_path, cert_filename)) if client_cert is None: return 'There is no client certificate named "{0}"'.format(CN) index_file, expire_date, serial_number, subject = _get_basic_info( - ca_name, - client_cert, - ca_dir) + ca_name, client_cert, ca_dir + ) - index_serial_subject = '{0}\tunknown\t{1}'.format( - serial_number, - subject) - index_v_data = 'V\t{0}\t\t{1}'.format( - expire_date, - index_serial_subject) + index_serial_subject = "{0}\tunknown\t{1}".format(serial_number, subject) + index_v_data = "V\t{0}\t\t{1}".format(expire_date, index_serial_subject) index_r_data_pattern = re.compile( - r"R\t" + - expire_date + - r"\t\d{12}Z\t" + - re.escape(index_serial_subject)) - index_r_data = 'R\t{0}\t{1}\t{2}'.format( + r"R\t" + expire_date + r"\t\d{12}Z\t" + re.escape(index_serial_subject) + ) + index_r_data = "R\t{0}\t{1}\t{2}".format( expire_date, _four_digit_year_to_two_digit(datetime.utcnow()), - index_serial_subject) + index_serial_subject, + ) ret = {} with salt.utils.files.fopen(index_file) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) if index_r_data_pattern.match(line): - revoke_date = line.split('\t')[2] + revoke_date = line.split("\t")[2] try: datetime.strptime(revoke_date, two_digit_year_fmt) - return ('"{0}/{1}.crt" was already revoked, ' - 'serial number: {2}').format( - cert_path, - cert_filename, - serial_number - ) + return ( + '"{0}/{1}.crt" was already revoked, ' "serial number: {2}" + ).format(cert_path, cert_filename, serial_number) except ValueError: - ret['retcode'] = 1 - ret['comment'] = ("Revocation date '{0}' does not match" - "format '{1}'".format( - revoke_date, - two_digit_year_fmt)) + ret["retcode"] = 1 + ret["comment"] = ( + "Revocation date '{0}' does not match" + "format '{1}'".format(revoke_date, two_digit_year_fmt) + ) return ret elif index_serial_subject in line: - __salt__['file.replace']( - index_file, - index_v_data, - index_r_data, - backup=False) + __salt__["file.replace"]( + index_file, index_v_data, index_r_data, backup=False + ) break crl = OpenSSL.crypto.CRL() @@ -1937,52 +1883,43 @@ def revoke_cert( with salt.utils.files.fopen(index_file) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('R'): - fields = line.split('\t') + if line.startswith("R"): + fields = line.split("\t") revoked = OpenSSL.crypto.Revoked() revoked.set_serial(fields[3]) - revoke_date_2_digit = datetime.strptime(fields[2], - two_digit_year_fmt) - revoked.set_rev_date(revoke_date_2_digit.strftime( - four_digit_year_fmt)) + revoke_date_2_digit = datetime.strptime(fields[2], two_digit_year_fmt) + revoked.set_rev_date(revoke_date_2_digit.strftime(four_digit_year_fmt)) crl.add_revoked(revoked) crl_text = crl.export(ca_cert, ca_key) if crl_file is None: - crl_file = '{0}/{1}/crl.pem'.format( - _cert_base_path(), - ca_name - ) + crl_file = "{0}/{1}/crl.pem".format(_cert_base_path(), ca_name) if os.path.isdir(crl_file): - ret['retcode'] = 1 - ret['comment'] = 'crl_file "{0}" is an existing directory'.format( - crl_file) + ret["retcode"] = 1 + ret["comment"] = 'crl_file "{0}" is an existing directory'.format(crl_file) return ret - with salt.utils.files.fopen(crl_file, 'w') as fp_: + with salt.utils.files.fopen(crl_file, "w") as fp_: fp_.write(salt.utils.stringutils.to_str(crl_text)) - return ('Revoked Certificate: "{0}/{1}.crt", ' - 'serial number: {2}').format( - cert_path, - cert_filename, - serial_number + return ('Revoked Certificate: "{0}/{1}.crt", ' "serial number: {2}").format( + cert_path, cert_filename, serial_number ) -if __name__ == '__main__': +if __name__ == "__main__": # create_ca('koji', days=365, **cert_sample_meta) create_csr( - 'koji', - CN='test_system', + "koji", + CN="test_system", C="US", ST="Utah", L="Centerville", O="SaltStack", OU=None, - emailAddress='test_system@saltstack.org' + emailAddress="test_system@saltstack.org", ) - create_ca_signed_cert('koji', 'test_system') - create_pkcs12('koji', 'test_system', passphrase='test') + create_ca_signed_cert("koji", "test_system") + create_pkcs12("koji", "test_system", passphrase="test") diff --git a/salt/modules/tomcat.py b/salt/modules/tomcat.py index 7e1b1130312..baca9c1682f 100644 --- a/salt/modules/tomcat.py +++ b/salt/modules/tomcat.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for Tomcat This module uses the manager webapp to manage Apache tomcat webapps. @@ -59,68 +59,67 @@ Also configure a user in the conf/tomcat-users.xml file: 2.6.32-358.el6.x86_64 Tomcat Version: Apache Tomcat/7.0.37 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import glob +import hashlib +import logging # Import python libs import os import re -import glob -import hashlib import tempfile -import logging + +# Import Salt libs +import salt.utils.data # Import 3rd-party libs # pylint: disable=no-name-in-module,import-error from salt.ext.six import string_types as _string_types from salt.ext.six.moves.urllib.parse import urlencode as _urlencode from salt.ext.six.moves.urllib.request import ( - urlopen as _urlopen, HTTPBasicAuthHandler as _HTTPBasicAuthHandler, - HTTPDigestAuthHandler as _HTTPDigestAuthHandler, - build_opener as _build_opener, - install_opener as _install_opener ) +from salt.ext.six.moves.urllib.request import ( + HTTPDigestAuthHandler as _HTTPDigestAuthHandler, +) +from salt.ext.six.moves.urllib.request import build_opener as _build_opener +from salt.ext.six.moves.urllib.request import install_opener as _install_opener +from salt.ext.six.moves.urllib.request import urlopen as _urlopen + # pylint: enable=no-name-in-module,import-error -# Import Salt libs -import salt.utils.data log = logging.getLogger(__name__) -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} # Support old-style grains/pillar # config as well as new. __valid_configs = { - 'user': [ - 'tomcat-manager.user', - 'tomcat-manager:user' - ], - 'passwd': [ - 'tomcat-manager.passwd', - 'tomcat-manager:passwd' - ] + "user": ["tomcat-manager.user", "tomcat-manager:user"], + "passwd": ["tomcat-manager.passwd", "tomcat-manager:passwd"], } def __virtual__(): - ''' + """ Only load tomcat if it is installed or if grains/pillar config exists - ''' - if __catalina_home() or _auth('dummy'): - return 'tomcat' - return (False, - 'Tomcat execution module not loaded: neither Tomcat installed locally nor tomcat-manager credentials set in grains/pillar/config.') + """ + if __catalina_home() or _auth("dummy"): + return "tomcat" + return ( + False, + "Tomcat execution module not loaded: neither Tomcat installed locally nor tomcat-manager credentials set in grains/pillar/config.", + ) def __catalina_home(): - ''' + """ Tomcat paths differ depending on packaging - ''' - locations = ['/usr/share/tomcat*', '/opt/tomcat'] + """ + locations = ["/usr/share/tomcat*", "/opt/tomcat"] for location in locations: folders = glob.glob(location) if folders: @@ -131,13 +130,10 @@ def __catalina_home(): def _get_credentials(): - ''' + """ Get the username and password from opts, grains, or pillar - ''' - ret = { - 'user': False, - 'passwd': False - } + """ + ret = {"user": False, "passwd": False} # Loop through opts, grains, and pillar # Return the first acceptable configuration found @@ -146,40 +142,39 @@ def _get_credentials(): # Look for the config key # Support old-style config format and new for config_key in __valid_configs[item]: - value = salt.utils.data.traverse_dict_and_list( - struct, - config_key, - None) + value = salt.utils.data.traverse_dict_and_list(struct, config_key, None) if value: ret[item] = value break - return ret['user'], ret['passwd'] + return ret["user"], ret["passwd"] def _auth(uri): - ''' + """ returns a authentication handler. Get user & password from grains, if are not set default to modules.config.option If user & pass are missing return False - ''' + """ user, password = _get_credentials() if user is False or password is False: return False basic = _HTTPBasicAuthHandler() - basic.add_password(realm='Tomcat Manager Application', uri=uri, - user=user, passwd=password) + basic.add_password( + realm="Tomcat Manager Application", uri=uri, user=user, passwd=password + ) digest = _HTTPDigestAuthHandler() - digest.add_password(realm='Tomcat Manager Application', uri=uri, - user=user, passwd=password) + digest.add_password( + realm="Tomcat Manager Application", uri=uri, user=user, passwd=password + ) return _build_opener(basic, digest) def extract_war_version(war): - ''' + """ Extract the version from the war file name. There does not seem to be a standard for encoding the version into the `war file name`_ @@ -191,15 +186,15 @@ def extract_war_version(war): /path/salt-2015.8.6.war -> 2015.8.6 /path/V6R2013xD5.war -> None - ''' + """ basename = os.path.basename(war) war_package = os.path.splitext(basename)[0] # remove '.war' version = re.findall("-([\\d.-]+)$", war_package) # try semver return version[0] if version and len(version) == 1 else None # default to none -def _wget(cmd, opts=None, url='http://localhost:8080/manager', timeout=180): - ''' +def _wget(cmd, opts=None, url="http://localhost:8080/manager", timeout=180): + """ A private function used to issue the command to tomcat via the manager webapp @@ -222,67 +217,61 @@ def _wget(cmd, opts=None, url='http://localhost:8080/manager', timeout=180): res: [True|False] msg: list of lines we got back from the manager } - ''' + """ - ret = { - 'res': True, - 'msg': [] - } + ret = {"res": True, "msg": []} # prepare authentication auth = _auth(url) if auth is False: - ret['res'] = False - ret['msg'] = 'missing username and password settings (grain/pillar)' + ret["res"] = False + ret["msg"] = "missing username and password settings (grain/pillar)" return ret # prepare URL - if url[-1] != '/': - url += '/' + if url[-1] != "/": + url += "/" url6 = url - url += 'text/{0}'.format(cmd) - url6 += '{0}'.format(cmd) + url += "text/{0}".format(cmd) + url6 += "{0}".format(cmd) if opts: - url += '?{0}'.format(_urlencode(opts)) - url6 += '?{0}'.format(_urlencode(opts)) + url += "?{0}".format(_urlencode(opts)) + url6 += "?{0}".format(_urlencode(opts)) # Make the HTTP request _install_opener(auth) try: # Trying tomcat >= 7 url - ret['msg'] = _urlopen(url, timeout=timeout).read().splitlines() + ret["msg"] = _urlopen(url, timeout=timeout).read().splitlines() except Exception: # pylint: disable=broad-except try: # Trying tomcat6 url - ret['msg'] = _urlopen(url6, timeout=timeout).read().splitlines() + ret["msg"] = _urlopen(url6, timeout=timeout).read().splitlines() except Exception: # pylint: disable=broad-except - ret['msg'] = 'Failed to create HTTP request' + ret["msg"] = "Failed to create HTTP request" - if not ret['msg'][0].startswith('OK'): - ret['res'] = False + if not ret["msg"][0].startswith("OK"): + ret["res"] = False return ret -def _simple_cmd(cmd, app, url='http://localhost:8080/manager', timeout=180): - ''' +def _simple_cmd(cmd, app, url="http://localhost:8080/manager", timeout=180): + """ Simple command wrapper to commands that need only a path option - ''' + """ try: - opts = { - 'path': app, - 'version': ls(url)[app]['version'] - } - return '\n'.join(_wget(cmd, opts, url, timeout=timeout)['msg']) + opts = {"path": app, "version": ls(url)[app]["version"]} + return "\n".join(_wget(cmd, opts, url, timeout=timeout)["msg"]) except Exception: # pylint: disable=broad-except - return 'FAIL - No context exists for path {0}'.format(app) + return "FAIL - No context exists for path {0}".format(app) # Functions -def leaks(url='http://localhost:8080/manager', timeout=180): - ''' +def leaks(url="http://localhost:8080/manager", timeout=180): + """ Find memory leaks in tomcat url : http://localhost:8080/manager @@ -295,14 +284,13 @@ def leaks(url='http://localhost:8080/manager', timeout=180): .. code-block:: bash salt '*' tomcat.leaks - ''' + """ - return _wget('findleaks', {'statusLine': 'true'}, - url, timeout=timeout)['msg'] + return _wget("findleaks", {"statusLine": "true"}, url, timeout=timeout)["msg"] -def status(url='http://localhost:8080/manager', timeout=180): - ''' +def status(url="http://localhost:8080/manager", timeout=180): + """ Used to test if the tomcat manager is up url : http://localhost:8080/manager @@ -316,13 +304,13 @@ def status(url='http://localhost:8080/manager', timeout=180): salt '*' tomcat.status salt '*' tomcat.status http://localhost:8080/manager - ''' + """ - return _wget('list', {}, url, timeout=timeout)['res'] + return _wget("list", {}, url, timeout=timeout)["res"] -def ls(url='http://localhost:8080/manager', timeout=180): - ''' +def ls(url="http://localhost:8080/manager", timeout=180): + """ list all the deployed webapps url : http://localhost:8080/manager @@ -336,30 +324,30 @@ def ls(url='http://localhost:8080/manager', timeout=180): salt '*' tomcat.ls salt '*' tomcat.ls http://localhost:8080/manager - ''' + """ ret = {} - data = _wget('list', '', url, timeout=timeout) - if data['res'] is False: + data = _wget("list", "", url, timeout=timeout) + if data["res"] is False: return {} - data['msg'].pop(0) - for line in data['msg']: - tmp = line.split(':') + data["msg"].pop(0) + for line in data["msg"]: + tmp = line.split(":") ret[tmp[0]] = { - 'mode': tmp[1], - 'sessions': tmp[2], - 'fullname': tmp[3], - 'version': '', + "mode": tmp[1], + "sessions": tmp[2], + "fullname": tmp[3], + "version": "", } - sliced = tmp[3].split('##') + sliced = tmp[3].split("##") if len(sliced) > 1: - ret[tmp[0]]['version'] = sliced[1] + ret[tmp[0]]["version"] = sliced[1] return ret -def stop(app, url='http://localhost:8080/manager', timeout=180): - ''' +def stop(app, url="http://localhost:8080/manager", timeout=180): + """ Stop the webapp app @@ -375,13 +363,13 @@ def stop(app, url='http://localhost:8080/manager', timeout=180): salt '*' tomcat.stop /jenkins salt '*' tomcat.stop /jenkins http://localhost:8080/manager - ''' + """ - return _simple_cmd('stop', app, url, timeout=timeout) + return _simple_cmd("stop", app, url, timeout=timeout) -def start(app, url='http://localhost:8080/manager', timeout=180): - ''' +def start(app, url="http://localhost:8080/manager", timeout=180): + """ Start the webapp app @@ -397,13 +385,13 @@ def start(app, url='http://localhost:8080/manager', timeout=180): salt '*' tomcat.start /jenkins salt '*' tomcat.start /jenkins http://localhost:8080/manager - ''' + """ - return _simple_cmd('start', app, url, timeout=timeout) + return _simple_cmd("start", app, url, timeout=timeout) -def reload_(app, url='http://localhost:8080/manager', timeout=180): - ''' +def reload_(app, url="http://localhost:8080/manager", timeout=180): + """ Reload the webapp app @@ -419,13 +407,13 @@ def reload_(app, url='http://localhost:8080/manager', timeout=180): salt '*' tomcat.reload /jenkins salt '*' tomcat.reload /jenkins http://localhost:8080/manager - ''' + """ - return _simple_cmd('reload', app, url, timeout=timeout) + return _simple_cmd("reload", app, url, timeout=timeout) -def sessions(app, url='http://localhost:8080/manager', timeout=180): - ''' +def sessions(app, url="http://localhost:8080/manager", timeout=180): + """ return the status of the webapp sessions app @@ -441,13 +429,13 @@ def sessions(app, url='http://localhost:8080/manager', timeout=180): salt '*' tomcat.sessions /jenkins salt '*' tomcat.sessions /jenkins http://localhost:8080/manager - ''' + """ - return _simple_cmd('sessions', app, url, timeout=timeout) + return _simple_cmd("sessions", app, url, timeout=timeout) -def status_webapp(app, url='http://localhost:8080/manager', timeout=180): - ''' +def status_webapp(app, url="http://localhost:8080/manager", timeout=180): + """ return the status of the webapp (stopped | running | missing) app @@ -463,18 +451,18 @@ def status_webapp(app, url='http://localhost:8080/manager', timeout=180): salt '*' tomcat.status_webapp /jenkins salt '*' tomcat.status_webapp /jenkins http://localhost:8080/manager - ''' + """ webapps = ls(url, timeout=timeout) for i in webapps: if i == app: - return webapps[i]['mode'] + return webapps[i]["mode"] - return 'missing' + return "missing" -def serverinfo(url='http://localhost:8080/manager', timeout=180): - ''' +def serverinfo(url="http://localhost:8080/manager", timeout=180): + """ return details about the server url : http://localhost:8080/manager @@ -488,23 +476,23 @@ def serverinfo(url='http://localhost:8080/manager', timeout=180): salt '*' tomcat.serverinfo salt '*' tomcat.serverinfo http://localhost:8080/manager - ''' + """ - data = _wget('serverinfo', {}, url, timeout=timeout) - if data['res'] is False: - return {'error': data['msg']} + data = _wget("serverinfo", {}, url, timeout=timeout) + if data["res"] is False: + return {"error": data["msg"]} ret = {} - data['msg'].pop(0) - for line in data['msg']: - tmp = line.split(':') + data["msg"].pop(0) + for line in data["msg"]: + tmp = line.split(":") ret[tmp[0].strip()] = tmp[1].strip() return ret -def undeploy(app, url='http://localhost:8080/manager', timeout=180): - ''' +def undeploy(app, url="http://localhost:8080/manager", timeout=180): + """ Undeploy a webapp app @@ -520,20 +508,22 @@ def undeploy(app, url='http://localhost:8080/manager', timeout=180): salt '*' tomcat.undeploy /jenkins salt '*' tomcat.undeploy /jenkins http://localhost:8080/manager - ''' + """ - return _simple_cmd('undeploy', app, url, timeout=timeout) + return _simple_cmd("undeploy", app, url, timeout=timeout) -def deploy_war(war, - context, - force='no', - url='http://localhost:8080/manager', - saltenv='base', - timeout=180, - temp_war_location=None, - version=True): - ''' +def deploy_war( + war, + context, + force="no", + url="http://localhost:8080/manager", + saltenv="base", + timeout=180, + temp_war_location=None, + version=True, +): + """ Deploy a WAR file war @@ -582,9 +572,9 @@ def deploy_war(war, salt '*' tomcat.deploy_war /tmp/application.war /api salt '*' tomcat.deploy_war /tmp/application.war /api no salt '*' tomcat.deploy_war /tmp/application.war /api yes http://localhost:8080/manager - ''' + """ # Decide the location to copy the war for the deployment - tfile = 'salt.{0}'.format(os.path.basename(war)) + tfile = "salt.{0}".format(os.path.basename(war)) if temp_war_location is not None: if not os.path.isdir(temp_war_location): return 'Error - "{0}" is not a directory'.format(temp_war_location) @@ -596,11 +586,11 @@ def deploy_war(war, cache = False if not os.path.isfile(war): cache = True - cached = __salt__['cp.get_url'](war, tfile, saltenv) + cached = __salt__["cp.get_url"](war, tfile, saltenv) if not cached: - return 'FAIL - could not cache the WAR file' + return "FAIL - could not cache the WAR file" try: - __salt__['file.set_mode'](cached, '0644') + __salt__["file.set_mode"](cached, "0644") except KeyError: pass else: @@ -608,8 +598,8 @@ def deploy_war(war, # Prepare options opts = { - 'war': 'file:{0}'.format(tfile), - 'path': context, + "war": "file:{0}".format(tfile), + "path": context, } # If parallel versions are desired or not disabled @@ -619,27 +609,24 @@ def deploy_war(war, if isinstance(version, _string_types): # Only pass version to Tomcat if not undefined - opts['version'] = version + opts["version"] = version - if force == 'yes': - opts['update'] = 'true' + if force == "yes": + opts["update"] = "true" # Deploy - deployed = _wget('deploy', opts, url, timeout=timeout) - res = '\n'.join(deployed['msg']) + deployed = _wget("deploy", opts, url, timeout=timeout) + res = "\n".join(deployed["msg"]) # Cleanup if cache: - __salt__['file.remove'](tfile) + __salt__["file.remove"](tfile) return res -def passwd(passwd, - user='', - alg='sha1', - realm=None): - ''' +def passwd(passwd, user="", alg="sha1", realm=None): + """ This function replaces the $CATALINA_HOME/bin/digest.sh script convert a clear-text password to the $CATALINA_BASE/conf/tomcat-users.xml format @@ -651,12 +638,12 @@ def passwd(passwd, salt '*' tomcat.passwd secret salt '*' tomcat.passwd secret tomcat sha1 salt '*' tomcat.passwd secret tomcat sha1 'Protected Realm' - ''' + """ # Shouldn't it be SHA265 instead of SHA1? digest = hasattr(hashlib, alg) and getattr(hashlib, alg) or None if digest: if realm: - digest.update('{0}:{1}:{2}'.format(user, realm, passwd, )) + digest.update("{0}:{1}:{2}".format(user, realm, passwd,)) else: digest.update(passwd) @@ -665,7 +652,7 @@ def passwd(passwd, # Non-Manager functions def version(): - ''' + """ Return server version from catalina.sh version CLI Example: @@ -673,19 +660,19 @@ def version(): .. code-block:: bash salt '*' tomcat.version - ''' - cmd = __catalina_home() + '/bin/catalina.sh version' - out = __salt__['cmd.run'](cmd).splitlines() + """ + cmd = __catalina_home() + "/bin/catalina.sh version" + out = __salt__["cmd.run"](cmd).splitlines() for line in out: if not line: continue - if 'Server version' in line: - comps = line.split(': ') + if "Server version" in line: + comps = line.split(": ") return comps[1] def fullversion(): - ''' + """ Return all server information from catalina.sh version CLI Example: @@ -693,21 +680,21 @@ def fullversion(): .. code-block:: bash salt '*' tomcat.fullversion - ''' - cmd = __catalina_home() + '/bin/catalina.sh version' + """ + cmd = __catalina_home() + "/bin/catalina.sh version" ret = {} - out = __salt__['cmd.run'](cmd).splitlines() + out = __salt__["cmd.run"](cmd).splitlines() for line in out: if not line: continue - if ': ' in line: - comps = line.split(': ') + if ": " in line: + comps = line.split(": ") ret[comps[0]] = comps[1].lstrip() return ret def signal(signal=None): - ''' + """ Signals catalina to start, stop, securestart, forcestop. CLI Example: @@ -715,44 +702,39 @@ def signal(signal=None): .. code-block:: bash salt '*' tomcat.signal start - ''' - valid_signals = {'forcestop': 'stop -force', - 'securestart': 'start -security', - 'start': 'start', - 'stop': 'stop'} + """ + valid_signals = { + "forcestop": "stop -force", + "securestart": "start -security", + "start": "start", + "stop": "stop", + } if signal not in valid_signals: return - cmd = '{0}/bin/catalina.sh {1}'.format( - __catalina_home(), valid_signals[signal] - ) - __salt__['cmd.run'](cmd) + cmd = "{0}/bin/catalina.sh {1}".format(__catalina_home(), valid_signals[signal]) + __salt__["cmd.run"](cmd) -if __name__ == '__main__': - ''' +if __name__ == "__main__": + """ Allow testing from the CLI - ''' # pylint: disable=W0105 + """ # pylint: disable=W0105 __opts__ = {} __grains__ = {} __pillar__ = { - 'tomcat-manager.user': 'foobar', - 'tomcat-manager.passwd': 'barfoo1!', + "tomcat-manager.user": "foobar", + "tomcat-manager.passwd": "barfoo1!", } old_format_creds = _get_credentials() - __pillar__ = { - 'tomcat-manager': { - 'user': 'foobar', - 'passwd': 'barfoo1!' - } - } + __pillar__ = {"tomcat-manager": {"user": "foobar", "passwd": "barfoo1!"}} new_format_creds = _get_credentials() if old_format_creds == new_format_creds: - log.info('Config backwards compatible') + log.info("Config backwards compatible") else: - log.ifno('Config not backwards compatible') + log.ifno("Config not backwards compatible") diff --git a/salt/modules/trafficserver.py b/salt/modules/trafficserver.py index 136d7842d80..7691bff2e99 100644 --- a/salt/modules/trafficserver.py +++ b/salt/modules/trafficserver.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Apache Traffic Server execution module. .. versionadded:: 2015.8.0 ``traffic_ctl`` is used to execute individual Traffic Server commands and to script multiple commands in a shell. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -17,20 +17,23 @@ import subprocess import salt.utils.path import salt.utils.stringutils -__virtualname__ = 'trafficserver' +__virtualname__ = "trafficserver" log = logging.getLogger(__name__) def __virtual__(): - if salt.utils.path.which('traffic_ctl') or salt.utils.path.which('traffic_line'): + if salt.utils.path.which("traffic_ctl") or salt.utils.path.which("traffic_line"): return __virtualname__ - return (False, 'trafficserver execution module not loaded: ' - 'neither traffic_ctl nor traffic_line was found.') + return ( + False, + "trafficserver execution module not loaded: " + "neither traffic_ctl nor traffic_line was found.", + ) -_TRAFFICLINE = salt.utils.path.which('traffic_line') -_TRAFFICCTL = salt.utils.path.which('traffic_ctl') +_TRAFFICLINE = salt.utils.path.which("traffic_line") +_TRAFFICCTL = salt.utils.path.which("traffic_ctl") def _traffic_ctl(*args): @@ -43,19 +46,19 @@ def _traffic_line(*args): def _statuscmd(): if _TRAFFICCTL: - cmd = _traffic_ctl('server', 'status') + cmd = _traffic_ctl("server", "status") else: - cmd = _traffic_line('--status') + cmd = _traffic_line("--status") return _subprocess(cmd) def _subprocess(cmd): - ''' + """ Function to standardize the subprocess call - ''' + """ - log.debug('Running: "%s"', ' '.join(cmd)) + log.debug('Running: "%s"', " ".join(cmd)) try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) ret = salt.utils.stringutils.to_unicode(proc.communicate()[0]).strip() @@ -73,25 +76,25 @@ def _subprocess(cmd): def bounce_cluster(): - ''' + """ Bounce all Traffic Server nodes in the cluster. Bouncing Traffic Server shuts down and immediately restarts Traffic Server, node-by-node. .. code-block:: bash salt '*' trafficserver.bounce_cluster - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('cluster', 'restart') + cmd = _traffic_ctl("cluster", "restart") else: - cmd = _traffic_line('-B') + cmd = _traffic_line("-B") return _subprocess(cmd) def bounce_local(drain=False): - ''' + """ Bounce Traffic Server on the local node. Bouncing Traffic Server shuts down and immediately restarts the Traffic Server node. @@ -106,72 +109,72 @@ def bounce_local(drain=False): salt '*' trafficserver.bounce_local salt '*' trafficserver.bounce_local drain=True - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('server', 'restart') + cmd = _traffic_ctl("server", "restart") else: - cmd = _traffic_line('-b') + cmd = _traffic_line("-b") if drain: - cmd = cmd + ['--drain'] + cmd = cmd + ["--drain"] return _subprocess(cmd) def clear_cluster(): - ''' + """ Clears accumulated statistics on all nodes in the cluster. .. code-block:: bash salt '*' trafficserver.clear_cluster - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('metric', 'clear', '--cluster') + cmd = _traffic_ctl("metric", "clear", "--cluster") else: - cmd = _traffic_line('-C') + cmd = _traffic_line("-C") return _subprocess(cmd) def clear_node(): - ''' + """ Clears accumulated statistics on the local node. .. code-block:: bash salt '*' trafficserver.clear_node - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('metric', 'clear') + cmd = _traffic_ctl("metric", "clear") else: - cmd = _traffic_line('-c') + cmd = _traffic_line("-c") return _subprocess(cmd) def restart_cluster(): - ''' + """ Restart the traffic_manager process and the traffic_server process on all the nodes in a cluster. .. code-block:: bash salt '*' trafficserver.restart_cluster - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('cluster', 'restart', '--manager') + cmd = _traffic_ctl("cluster", "restart", "--manager") else: - cmd = _traffic_line('-M') + cmd = _traffic_line("-M") return _subprocess(cmd) def restart_local(drain=False): - ''' + """ Restart the traffic_manager and traffic_server processes on the local node. drain @@ -185,20 +188,20 @@ def restart_local(drain=False): salt '*' trafficserver.restart_local salt '*' trafficserver.restart_local drain=True - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('server', 'restart', '--manager') + cmd = _traffic_ctl("server", "restart", "--manager") else: - cmd = _traffic_line('-L') + cmd = _traffic_line("-L") if drain: - cmd = cmd + ['--drain'] + cmd = cmd + ["--drain"] return _subprocess(cmd) def match_metric(regex): - ''' + """ Display the current values of all metrics whose names match the given regular expression. @@ -207,17 +210,17 @@ def match_metric(regex): .. code-block:: bash salt '*' trafficserver.match_metric regex - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('metric', 'match', regex) + cmd = _traffic_ctl("metric", "match", regex) else: - cmd = _traffic_ctl('-m', regex) + cmd = _traffic_ctl("-m", regex) return _subprocess(cmd) def match_config(regex): - ''' + """ Display the current values of all configuration variables whose names match the given regular expression. @@ -226,17 +229,17 @@ def match_config(regex): .. code-block:: bash salt '*' trafficserver.match_config regex - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('config', 'match', regex) + cmd = _traffic_ctl("config", "match", regex) else: - cmd = _traffic_line('-m', regex) + cmd = _traffic_line("-m", regex) return _subprocess(cmd) def read_config(*args): - ''' + """ Read Traffic Server configuration variable definitions. .. versionadded:: 2016.11.0 @@ -244,17 +247,17 @@ def read_config(*args): .. code-block:: bash salt '*' trafficserver.read_config proxy.config.http.keep_alive_post_out - ''' + """ ret = {} if _TRAFFICCTL: - cmd = _traffic_ctl('config', 'get') + cmd = _traffic_ctl("config", "get") else: - cmd = _traffic_line('-r') + cmd = _traffic_line("-r") try: for arg in args: - log.debug('Querying: %s', arg) + log.debug("Querying: %s", arg) ret[arg] = _subprocess(cmd + [arg]) except KeyError: pass @@ -263,7 +266,7 @@ def read_config(*args): def read_metric(*args): - ''' + """ Read Traffic Server one or more metrics. .. versionadded:: 2016.11.0 @@ -271,17 +274,17 @@ def read_metric(*args): .. code-block:: bash salt '*' trafficserver.read_metric proxy.process.http.tcp_hit_count_stat - ''' + """ ret = {} if _TRAFFICCTL: - cmd = _traffic_ctl('metric', 'get') + cmd = _traffic_ctl("metric", "get") else: - cmd = _traffic_line('-r') + cmd = _traffic_line("-r") try: for arg in args: - log.debug('Querying: %s', arg) + log.debug("Querying: %s", arg) ret[arg] = _subprocess(cmd + [arg]) except KeyError: pass @@ -290,7 +293,7 @@ def read_metric(*args): def set_config(variable, value): - ''' + """ Set the value of a Traffic Server configuration variable. variable @@ -304,59 +307,59 @@ def set_config(variable, value): .. code-block:: bash salt '*' trafficserver.set_config proxy.config.http.keep_alive_post_out 0 - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('config', 'set', variable, value) + cmd = _traffic_ctl("config", "set", variable, value) else: - cmd = _traffic_line('-s', variable, '-v', value) + cmd = _traffic_line("-s", variable, "-v", value) - log.debug('Setting %s to %s', variable, value) + log.debug("Setting %s to %s", variable, value) return _subprocess(cmd) def shutdown(): - ''' + """ Shut down Traffic Server on the local node. .. code-block:: bash salt '*' trafficserver.shutdown - ''' + """ # Earlier versions of traffic_ctl do not support # "server stop", so we prefer traffic_line here. if _TRAFFICLINE: - cmd = _traffic_line('-S') + cmd = _traffic_line("-S") else: - cmd = _traffic_ctl('server', 'stop') + cmd = _traffic_ctl("server", "stop") _subprocess(cmd) return _statuscmd() def startup(): - ''' + """ Start Traffic Server on the local node. .. code-block:: bash salt '*' trafficserver.start - ''' + """ # Earlier versions of traffic_ctl do not support # "server start", so we prefer traffic_line here. if _TRAFFICLINE: - cmd = _traffic_line('-U') + cmd = _traffic_line("-U") else: - cmd = _traffic_ctl('server', 'start') + cmd = _traffic_ctl("server", "start") _subprocess(cmd) return _statuscmd() def refresh(): - ''' + """ Initiate a Traffic Server configuration file reread. Use this command to update the running configuration after any configuration file modification. @@ -366,50 +369,50 @@ def refresh(): .. code-block:: bash salt '*' trafficserver.refresh - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('config', 'reload') + cmd = _traffic_ctl("config", "reload") else: - cmd = _traffic_line('-x') + cmd = _traffic_line("-x") return _subprocess(cmd) def zero_cluster(): - ''' + """ Reset performance statistics to zero across the cluster. .. code-block:: bash salt '*' trafficserver.zero_cluster - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('metric', 'clear', '--cluster') + cmd = _traffic_ctl("metric", "clear", "--cluster") else: - cmd = _traffic_line('-Z') + cmd = _traffic_line("-Z") return _subprocess(cmd) def zero_node(): - ''' + """ Reset performance statistics to zero on the local node. .. code-block:: bash salt '*' trafficserver.zero_cluster - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('metric', 'clear') + cmd = _traffic_ctl("metric", "clear") else: - cmd = _traffic_line('-z') + cmd = _traffic_line("-z") return _subprocess(cmd) def offline(path): - ''' + """ Mark a cache storage device as offline. The storage is identified by a path which must match exactly a path specified in storage.config. This removes the storage from the cache and redirects requests that would have used this @@ -420,35 +423,35 @@ def offline(path): .. code-block:: bash salt '*' trafficserver.offline /path/to/cache - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('storage', 'offline', path) + cmd = _traffic_ctl("storage", "offline", path) else: - cmd = _traffic_line('--offline', path) + cmd = _traffic_line("--offline", path) return _subprocess(cmd) def alarms(): - ''' + """ List all alarm events that have not been acknowledged (cleared). .. code-block:: bash salt '*' trafficserver.alarms - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('alarm', 'list') + cmd = _traffic_ctl("alarm", "list") else: - cmd = _traffic_line('--alarms') + cmd = _traffic_line("--alarms") return _subprocess(cmd) def clear_alarms(alarm): - ''' + """ Clear (acknowledge) an alarm event. The arguments are “all” for all current alarms, a specific alarm number (e.g. ‘‘1’‘), or an alarm string identifier (e.g. ‘’MGMT_ALARM_PROXY_CONFIG_ERROR’‘). @@ -456,23 +459,23 @@ def clear_alarms(alarm): .. code-block:: bash salt '*' trafficserver.clear_alarms [all | #event | name] - ''' + """ if _TRAFFICCTL: - cmd = _traffic_ctl('alarm', 'clear', alarm) + cmd = _traffic_ctl("alarm", "clear", alarm) else: - cmd = _traffic_line('--clear_alarms', alarm) + cmd = _traffic_line("--clear_alarms", alarm) return _subprocess(cmd) def status(): - ''' + """ Show the current proxy server status, indicating if we’re running or not. .. code-block:: bash salt '*' trafficserver.status - ''' + """ return _statuscmd() diff --git a/salt/modules/travisci.py b/salt/modules/travisci.py index 2704c5f9521..bd28e797e57 100644 --- a/salt/modules/travisci.py +++ b/salt/modules/travisci.py @@ -1,46 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" Commands for working with travisci. :depends: pyOpenSSL >= 16.0.0 -''' +""" # Import python libraries -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import base64 +# Import Salt libraries +import salt.utils.json + +# Import 3rd party libraries +from salt.ext import six +from salt.ext.six.moves.urllib.parse import ( # pylint: disable=import-error,no-name-in-module + parse_qs, +) +from salt.utils.versions import LooseVersion as _LooseVersion + try: import OpenSSL import OpenSSL.crypto + HAS_OPENSSL = True except ImportError: HAS_OPENSSL = False -# Import Salt libraries -import salt.utils.json -from salt.utils.versions import LooseVersion as _LooseVersion -from salt.ext.six.moves.urllib.parse import parse_qs # pylint: disable=import-error,no-name-in-module -# Import 3rd party libraries -from salt.ext import six - - -OPENSSL_MIN_VER = '16.0.0' -__virtualname__ = 'travisci' +OPENSSL_MIN_VER = "16.0.0" +__virtualname__ = "travisci" def __virtual__(): if HAS_OPENSSL is False: - return (False, 'The travisci module was unable to be loaded: Install pyOpenssl >= {0}'.format(OPENSSL_MIN_VER)) + return ( + False, + "The travisci module was unable to be loaded: Install pyOpenssl >= {0}".format( + OPENSSL_MIN_VER + ), + ) cur_version = _LooseVersion(OpenSSL.__version__) min_version = _LooseVersion(OPENSSL_MIN_VER) if cur_version < min_version: - return (False, 'The travisci module was unable to be loaded: Install pyOpenssl >= {0}'.format(OPENSSL_MIN_VER)) + return ( + False, + "The travisci module was unable to be loaded: Install pyOpenssl >= {0}".format( + OPENSSL_MIN_VER + ), + ) return __virtualname__ def verify_webhook(signature, body): - ''' + """ Verify the webhook signature from travisci signature @@ -57,10 +71,14 @@ def verify_webhook(signature, body): salt '*' travisci.verify_webhook 'M6NucCX5722bxisQs7e...' 'payload=%7B%22id%22%3A183791261%2C%22repository...' - ''' + """ # get public key setup - public_key = __utils__['http.query']('https://api.travis-ci.org/config')['config']['notifications']['webhook']['public_key'] - pkey_public_key = OpenSSL.crypto.load_publickey(OpenSSL.crypto.FILETYPE_PEM, public_key) + public_key = __utils__["http.query"]("https://api.travis-ci.org/config")["config"][ + "notifications" + ]["webhook"]["public_key"] + pkey_public_key = OpenSSL.crypto.load_publickey( + OpenSSL.crypto.FILETYPE_PEM, public_key + ) certificate = OpenSSL.crypto.X509() certificate.set_pubkey(pkey_public_key) @@ -68,10 +86,10 @@ def verify_webhook(signature, body): signature = base64.b64decode(signature) # parse the urlencoded payload from travis - payload = salt.utils.json.loads(parse_qs(body)['payload'][0]) + payload = salt.utils.json.loads(parse_qs(body)["payload"][0]) try: - OpenSSL.crypto.verify(certificate, signature, payload, six.text_type('sha1')) + OpenSSL.crypto.verify(certificate, signature, payload, six.text_type("sha1")) except OpenSSL.crypto.Error: return False return True diff --git a/salt/modules/tuned.py b/salt/modules/tuned.py index 5561dc6c3bd..612571615b9 100644 --- a/salt/modules/tuned.py +++ b/salt/modules/tuned.py @@ -1,40 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" Interface to Red Hat tuned-adm module :maintainer: Syed Ali <alicsyed@gmail.com> :maturity: new :depends: tuned-adm :platform: Linux -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import re # Import Salt libs import salt.utils.path __func_alias__ = { - 'list_': 'list', + "list_": "list", } -__virtualname__ = 'tuned' +__virtualname__ = "tuned" def __virtual__(): - ''' + """ Check to see if tuned-adm binary is installed on the system - ''' - tuned_adm = salt.utils.path.which('tuned-adm') + """ + tuned_adm = salt.utils.path.which("tuned-adm") if not tuned_adm: - return (False, 'The tuned execution module failed to load: the tuned-adm binary is not in the path.') + return ( + False, + "The tuned execution module failed to load: the tuned-adm binary is not in the path.", + ) return __virtualname__ def list_(): - ''' + """ List the profiles available CLI Example: @@ -42,21 +46,21 @@ def list_(): .. code-block:: bash salt '*' tuned.list - ''' + """ - result = __salt__['cmd.run']('tuned-adm list').splitlines() + result = __salt__["cmd.run"]("tuned-adm list").splitlines() # Remove "Available profiles:" result.pop(0) # Remove "Current active profile:.*" result.pop() # Output can be : " - <profile name> - <description>" (v2.7.1) # or " - <profile name> " (v2.4.1) - result = [i.split('- ')[1].strip() for i in result] + result = [i.split("- ")[1].strip() for i in result] return result def active(): - ''' + """ Return current active profile CLI Example: @@ -64,19 +68,19 @@ def active(): .. code-block:: bash salt '*' tuned.active - ''' + """ # turn off all profiles - result = __salt__['cmd.run_all']('tuned-adm active', ignore_retcode=True) - if result['retcode'] != 0: + result = __salt__["cmd.run_all"]("tuned-adm active", ignore_retcode=True) + if result["retcode"] != 0: return "none" - pattern = re.compile(r'''(?P<stmt>Current active profile:) (?P<profile>\w+.*)''') - match = re.match(pattern, result['stdout']) - return '{0}'.format(match.group('profile')) + pattern = re.compile(r"""(?P<stmt>Current active profile:) (?P<profile>\w+.*)""") + match = re.match(pattern, result["stdout"]) + return "{0}".format(match.group("profile")) def off(): - ''' + """ Turn off all profiles CLI Example: @@ -84,17 +88,17 @@ def off(): .. code-block:: bash salt '*' tuned.off - ''' + """ # turn off all profiles - result = __salt__['cmd.retcode']('tuned-adm off') + result = __salt__["cmd.retcode"]("tuned-adm off") if int(result) != 0: return False return True def profile(profile_name): - ''' + """ Activate specified profile CLI Example: @@ -102,10 +106,10 @@ def profile(profile_name): .. code-block:: bash salt '*' tuned.profile virtual-guest - ''' + """ # run tuned-adm with the profile specified - result = __salt__['cmd.retcode']('tuned-adm profile {0}'.format(profile_name)) + result = __salt__["cmd.retcode"]("tuned-adm profile {0}".format(profile_name)) if int(result) != 0: return False - return '{0}'.format(profile_name) + return "{0}".format(profile_name) diff --git a/salt/modules/twilio_notify.py b/salt/modules/twilio_notify.py index 93951e188c8..87c68af3cc2 100644 --- a/salt/modules/twilio_notify.py +++ b/salt/modules/twilio_notify.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for notifications via Twilio .. versionadded:: 2014.7.0 @@ -18,8 +18,9 @@ Module for notifications via Twilio my-twilio-account: twilio.account_sid: AC32a3c83990934481addd5ce1659f04d2 twilio.auth_token: mytoken -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + import logging # import 3rd party libs @@ -28,9 +29,10 @@ from salt.ext import six HAS_LIBS = False try: import twilio + # Grab version, ensure elements are ints twilio_version = tuple([int(x) for x in twilio.__version_info__]) - if twilio_version > (5, ): + if twilio_version > (5,): TWILIO_5 = False from twilio.rest import Client as TwilioRestClient from twilio.rest import TwilioException as TwilioRestException @@ -45,42 +47,44 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'twilio' +__virtualname__ = "twilio" def __virtual__(): - ''' + """ Only load this module if twilio is installed on this minion. - ''' + """ if HAS_LIBS: return __virtualname__ - return (False, 'The twilio_notify execution module failed to load: the twilio python library is not installed.') + return ( + False, + "The twilio_notify execution module failed to load: the twilio python library is not installed.", + ) def _get_twilio(profile): - ''' + """ Return the twilio connection - ''' - creds = __salt__['config.option'](profile) + """ + creds = __salt__["config.option"](profile) client = TwilioRestClient( - creds.get('twilio.account_sid'), - creds.get('twilio.auth_token'), + creds.get("twilio.account_sid"), creds.get("twilio.auth_token"), ) return client def send_sms(profile, body, to, from_): - ''' + """ Send an sms CLI Example: twilio.send_sms my-twilio-account 'Test sms' '+18019999999' '+18011111111' - ''' + """ ret = {} - ret['message'] = {} - ret['message']['sid'] = None + ret["message"] = {} + ret["message"]["sid"] = None client = _get_twilio(profile) try: if TWILIO_5: @@ -88,20 +92,20 @@ def send_sms(profile, body, to, from_): else: message = client.messages.create(body=body, to=to, from_=from_) except TwilioRestException as exc: - ret['_error'] = {} - ret['_error']['code'] = exc.code - ret['_error']['msg'] = exc.msg - ret['_error']['status'] = exc.status - log.debug('Could not send sms. Error: %s', ret) + ret["_error"] = {} + ret["_error"]["code"] = exc.code + ret["_error"]["msg"] = exc.msg + ret["_error"]["status"] = exc.status + log.debug("Could not send sms. Error: %s", ret) return ret - ret['message'] = {} - ret['message']['sid'] = message.sid - ret['message']['price'] = message.price - ret['message']['price_unit'] = message.price_unit - ret['message']['status'] = message.status - ret['message']['num_segments'] = message.num_segments - ret['message']['body'] = message.body - ret['message']['date_sent'] = six.text_type(message.date_sent) - ret['message']['date_created'] = six.text_type(message.date_created) + ret["message"] = {} + ret["message"]["sid"] = message.sid + ret["message"]["price"] = message.price + ret["message"]["price_unit"] = message.price_unit + ret["message"]["status"] = message.status + ret["message"]["num_segments"] = message.num_segments + ret["message"]["body"] = message.body + ret["message"]["date_sent"] = six.text_type(message.date_sent) + ret["message"]["date_created"] = six.text_type(message.date_created) log.info(ret) return ret diff --git a/salt/modules/udev.py b/salt/modules/udev.py index 6f459567ee9..c6c53e9a2cc 100644 --- a/salt/modules/udev.py +++ b/salt/modules/udev.py @@ -1,50 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" Manage and query udev info .. versionadded:: 2015.8.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.modules.cmdmod + # Import Salt libs import salt.utils.path -import salt.modules.cmdmod from salt.exceptions import CommandExecutionError __salt__ = { - 'cmd.run_all': salt.modules.cmdmod.run_all, + "cmd.run_all": salt.modules.cmdmod.run_all, } log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work when udevadm is installed. - ''' - return salt.utils.path.which_bin(['udevadm']) is not None + """ + return salt.utils.path.which_bin(["udevadm"]) is not None def _parse_udevadm_info(udev_info): - ''' + """ Parse the info returned by udevadm command. - ''' + """ devices = [] dev = {} for line in (line.strip() for line in udev_info.splitlines()): if line: - line = line.split(':', 1) + line = line.split(":", 1) if len(line) != 2: continue query, data = line - if query == 'E': + if query == "E": if query not in dev: dev[query] = {} - key, val = data.strip().split('=', 1) + key, val = data.strip().split("=", 1) try: val = int(val) @@ -71,12 +73,12 @@ def _parse_udevadm_info(udev_info): def _normalize_info(dev): - ''' + """ Replace list with only one element to the value of the element. :param dev: :return: - ''' + """ for sect, val in dev.items(): if len(val) == 1: dev[sect] = val[0] @@ -85,7 +87,7 @@ def _normalize_info(dev): def info(dev): - ''' + """ Extract all info delivered by udevadm CLI Example: @@ -94,23 +96,23 @@ def info(dev): salt '*' udev.info /dev/sda salt '*' udev.info /sys/class/net/eth0 - ''' - if 'sys' in dev: - qtype = 'path' + """ + if "sys" in dev: + qtype = "path" else: - qtype = 'name' + qtype = "name" - cmd = 'udevadm info --export --query=all --{0}={1}'.format(qtype, dev) - udev_result = __salt__['cmd.run_all'](cmd, output_loglevel='quiet') + cmd = "udevadm info --export --query=all --{0}={1}".format(qtype, dev) + udev_result = __salt__["cmd.run_all"](cmd, output_loglevel="quiet") - if udev_result['retcode'] != 0: - raise CommandExecutionError(udev_result['stderr']) + if udev_result["retcode"] != 0: + raise CommandExecutionError(udev_result["stderr"]) - return _parse_udevadm_info(udev_result['stdout'])[0] + return _parse_udevadm_info(udev_result["stdout"])[0] def env(dev): - ''' + """ Return all environment variables udev has for dev CLI Example: @@ -119,12 +121,12 @@ def env(dev): salt '*' udev.env /dev/sda salt '*' udev.env /sys/class/net/eth0 - ''' - return info(dev).get('E', None) + """ + return info(dev).get("E", None) def name(dev): - ''' + """ Return the actual dev name(s?) according to udev for dev CLI Example: @@ -133,12 +135,12 @@ def name(dev): salt '*' udev.dev /dev/sda salt '*' udev.dev /sys/class/net/eth0 - ''' - return info(dev).get('N', None) + """ + return info(dev).get("N", None) def path(dev): - ''' + """ Return the physical device path(s?) according to udev for dev CLI Example: @@ -147,12 +149,12 @@ def path(dev): salt '*' udev.path /dev/sda salt '*' udev.path /sys/class/net/eth0 - ''' - return info(dev).get('P', None) + """ + return info(dev).get("P", None) def links(dev): - ''' + """ Return all udev-created device symlinks CLI Example: @@ -161,12 +163,12 @@ def links(dev): salt '*' udev.links /dev/sda salt '*' udev.links /sys/class/net/eth0 - ''' - return info(dev).get('S', None) + """ + return info(dev).get("S", None) def exportdb(): - ''' + """ Return all the udev database CLI Example: @@ -174,12 +176,12 @@ def exportdb(): .. code-block:: bash salt '*' udev.exportdb - ''' + """ - cmd = 'udevadm info --export-db' - udev_result = __salt__['cmd.run_all'](cmd, output_loglevel='quiet') + cmd = "udevadm info --export-db" + udev_result = __salt__["cmd.run_all"](cmd, output_loglevel="quiet") - if udev_result['retcode']: - raise CommandExecutionError(udev_result['stderr']) + if udev_result["retcode"]: + raise CommandExecutionError(udev_result["stderr"]) - return _parse_udevadm_info(udev_result['stdout']) + return _parse_udevadm_info(udev_result["stdout"]) diff --git a/salt/modules/upstart_service.py b/salt/modules/upstart_service.py index 60ec032c95b..2038507d468 100644 --- a/salt/modules/upstart_service.py +++ b/salt/modules/upstart_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for the management of upstart systems. The Upstart system only supports service starting, stopping and restarting. @@ -43,14 +43,15 @@ about this, at least. the :mod:`rh_service <salt.modules.rh_service>` module should be used, as it supports the hybrid upstart/sysvinit system used in RHEL/CentOS 6. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import fnmatch # Import python libs import glob import os import re -import fnmatch # Import salt libs import salt.modules.cmdmod @@ -59,41 +60,47 @@ import salt.utils.path import salt.utils.systemd from salt.ext.six.moves import filter # pylint: disable=import-error,redefined-builtin -__func_alias__ = { - 'reload_': 'reload' -} +__func_alias__ = {"reload_": "reload"} # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" def __virtual__(): - ''' + """ Only work on Ubuntu - ''' + """ # Disable on these platforms, specific service modules exist: if salt.utils.systemd.booted(__context__): - return (False, 'The upstart execution module failed to load: this system was booted with systemd.') - elif __grains__['os'] in ('Ubuntu', 'Linaro', 'elementary OS', 'Mint'): + return ( + False, + "The upstart execution module failed to load: this system was booted with systemd.", + ) + elif __grains__["os"] in ("Ubuntu", "Linaro", "elementary OS", "Mint"): return __virtualname__ - elif __grains__['os'] in ('Debian', 'Raspbian'): - debian_initctl = '/sbin/initctl' + elif __grains__["os"] in ("Debian", "Raspbian"): + debian_initctl = "/sbin/initctl" if os.path.isfile(debian_initctl): - initctl_version = salt.modules.cmdmod._run_quiet(debian_initctl + ' version') - if 'upstart' in initctl_version: + initctl_version = salt.modules.cmdmod._run_quiet( + debian_initctl + " version" + ) + if "upstart" in initctl_version: return __virtualname__ - return (False, 'The upstart execution module failed to load: ' - ' the system must be Ubuntu-based, or Debian-based with upstart support.') + return ( + False, + "The upstart execution module failed to load: " + " the system must be Ubuntu-based, or Debian-based with upstart support.", + ) def _find_utmp(): - ''' + """ Figure out which utmp file to use when determining runlevel. Sometimes /var/run/utmp doesn't exist, /run/utmp is the new hotness. - ''' + """ result = {} # These are the likely locations for the file on Ubuntu - for utmp in '/var/run/utmp', '/run/utmp': + for utmp in "/var/run/utmp", "/run/utmp": try: result[os.stat(utmp).st_mtime] = utmp except Exception: # pylint: disable=broad-except @@ -105,29 +112,29 @@ def _find_utmp(): def _default_runlevel(): - ''' + """ Try to figure out the default runlevel. It is kept in /etc/init/rc-sysinit.conf, but can be overridden with entries in /etc/inittab, or via the kernel command-line at boot - ''' + """ # Try to get the "main" default. If this fails, throw up our # hands and just guess "2", because things are horribly broken try: - with salt.utils.files.fopen('/etc/init/rc-sysinit.conf') as fp_: + with salt.utils.files.fopen("/etc/init/rc-sysinit.conf") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('env DEFAULT_RUNLEVEL'): - runlevel = line.split('=')[-1].strip() + if line.startswith("env DEFAULT_RUNLEVEL"): + runlevel = line.split("=")[-1].strip() except Exception: # pylint: disable=broad-except - return '2' + return "2" # Look for an optional "legacy" override in /etc/inittab try: - with salt.utils.files.fopen('/etc/inittab') as fp_: + with salt.utils.files.fopen("/etc/inittab") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if not line.startswith('#') and 'initdefault' in line: - runlevel = line.split(':')[1] + if not line.startswith("#") and "initdefault" in line: + runlevel = line.split(":")[1] except Exception: # pylint: disable=broad-except pass @@ -135,8 +142,9 @@ def _default_runlevel(): # Kinky. try: valid_strings = set( - ('0', '1', '2', '3', '4', '5', '6', 's', 'S', '-s', 'single')) - with salt.utils.files.fopen('/proc/cmdline') as fp_: + ("0", "1", "2", "3", "4", "5", "6", "s", "S", "-s", "single") + ) + with salt.utils.files.fopen("/proc/cmdline") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) for arg in line.strip().split(): @@ -150,20 +158,20 @@ def _default_runlevel(): def _runlevel(): - ''' + """ Return the current runlevel - ''' - if 'upstart._runlevel' in __context__: - return __context__['upstart._runlevel'] + """ + if "upstart._runlevel" in __context__: + return __context__["upstart._runlevel"] ret = _default_runlevel() utmp = _find_utmp() if utmp: - out = __salt__['cmd.run'](['runlevel', '{0}'.format(utmp)], python_shell=False) + out = __salt__["cmd.run"](["runlevel", "{0}".format(utmp)], python_shell=False) try: ret = out.split()[1] except IndexError: pass - __context__['upstart._runlevel'] = ret + __context__["upstart._runlevel"] = ret return ret @@ -172,76 +180,78 @@ def _is_symlink(name): def _service_is_upstart(name): - ''' + """ From "Writing Jobs" at http://upstart.ubuntu.com/getting-started.html: Jobs are defined in files placed in /etc/init, the name of the job is the filename under this directory without the .conf extension. - ''' - return os.access('/etc/init/{0}.conf'.format(name), os.R_OK) + """ + return os.access("/etc/init/{0}.conf".format(name), os.R_OK) def _upstart_is_disabled(name): - ''' + """ An Upstart service is assumed disabled if a manual stanza is placed in /etc/init/[name].override. NOTE: An Upstart service can also be disabled by placing "manual" in /etc/init/[name].conf. - ''' - files = ['/etc/init/{0}.conf'.format(name), '/etc/init/{0}.override'.format(name)] + """ + files = ["/etc/init/{0}.conf".format(name), "/etc/init/{0}.override".format(name)] for file_name in filter(os.path.isfile, files): with salt.utils.files.fopen(file_name) as fp_: - if re.search(r'^\s*manual', - salt.utils.stringutils.to_unicode(fp_.read()), - re.MULTILINE): + if re.search( + r"^\s*manual", + salt.utils.stringutils.to_unicode(fp_.read()), + re.MULTILINE, + ): return True return False def _upstart_is_enabled(name): - ''' + """ Assume that if an Upstart service is not disabled then it must be enabled. - ''' + """ return not _upstart_is_disabled(name) def _service_is_sysv(name): - ''' + """ A System-V style service will have a control script in /etc/init.d. We make sure to skip over symbolic links that point to Upstart's /lib/init/upstart-job, and anything that isn't an executable, like README or skeleton. - ''' - script = '/etc/init.d/{0}'.format(name) + """ + script = "/etc/init.d/{0}".format(name) return not _service_is_upstart(name) and os.access(script, os.X_OK) def _sysv_is_disabled(name): - ''' + """ A System-V style service is assumed disabled if there is no start-up link (starts with "S") to its script in /etc/init.d in the current runlevel. - ''' - return not bool(glob.glob('/etc/rc{0}.d/S*{1}'.format(_runlevel(), name))) + """ + return not bool(glob.glob("/etc/rc{0}.d/S*{1}".format(_runlevel(), name))) def _sysv_is_enabled(name): - ''' + """ Assume that if a System-V style service is not disabled then it must be enabled. - ''' + """ return not _sysv_is_disabled(name) def _iter_service_names(): - ''' + """ Detect all of the service names available to upstart via init configuration files and via classic sysv init scripts - ''' + """ found = set() - for line in glob.glob('/etc/init.d/*'): + for line in glob.glob("/etc/init.d/*"): name = os.path.basename(line) found.add(name) yield name @@ -250,11 +260,11 @@ def _iter_service_names(): # definition 'For example a configuration file /etc/init/rc-sysinit.conf # is named rc-sysinit, while a configuration file /etc/init/net/apache.conf # is named net/apache' - init_root = '/etc/init/' + init_root = "/etc/init/" for root, dirnames, filenames in salt.utils.path.os_walk(init_root): relpath = os.path.relpath(root, init_root) - for filename in fnmatch.filter(filenames, '*.conf'): - if relpath == '.': + for filename in fnmatch.filter(filenames, "*.conf"): + if relpath == ".": # service is defined in the root, no need to append prefix. name = filename[:-5] else: @@ -266,7 +276,7 @@ def _iter_service_names(): def get_enabled(): - ''' + """ Return the enabled services CLI Example: @@ -274,7 +284,7 @@ def get_enabled(): .. code-block:: bash salt '*' service.get_enabled - ''' + """ ret = set() for name in _iter_service_names(): if _service_is_upstart(name): @@ -288,7 +298,7 @@ def get_enabled(): def get_disabled(): - ''' + """ Return the disabled services CLI Example: @@ -296,7 +306,7 @@ def get_disabled(): .. code-block:: bash salt '*' service.get_disabled - ''' + """ ret = set() for name in _iter_service_names(): if _service_is_upstart(name): @@ -310,7 +320,7 @@ def get_disabled(): def available(name): - ''' + """ Returns ``True`` if the specified service is available, otherwise returns ``False``. @@ -319,12 +329,12 @@ def available(name): .. code-block:: bash salt '*' service.available sshd - ''' + """ return name in get_all() def missing(name): - ''' + """ The inverse of service.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. @@ -334,12 +344,12 @@ def missing(name): .. code-block:: bash salt '*' service.missing sshd - ''' + """ return name not in get_all() def get_all(): - ''' + """ Return all installed services CLI Example: @@ -347,12 +357,12 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' + """ return sorted(get_enabled() + get_disabled()) def start(name): - ''' + """ Start the specified service CLI Example: @@ -360,13 +370,13 @@ def start(name): .. code-block:: bash salt '*' service.start <service name> - ''' - cmd = ['service', name, 'start'] - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = ["service", name, "start"] + return not __salt__["cmd.retcode"](cmd, python_shell=False) def stop(name): - ''' + """ Stop the specified service CLI Example: @@ -374,13 +384,13 @@ def stop(name): .. code-block:: bash salt '*' service.stop <service name> - ''' - cmd = ['service', name, 'stop'] - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = ["service", name, "stop"] + return not __salt__["cmd.retcode"](cmd, python_shell=False) def restart(name): - ''' + """ Restart the named service CLI Example: @@ -388,13 +398,13 @@ def restart(name): .. code-block:: bash salt '*' service.restart <service name> - ''' - cmd = ['service', name, 'restart'] - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = ["service", name, "restart"] + return not __salt__["cmd.retcode"](cmd, python_shell=False) def full_restart(name): - ''' + """ Do a full restart (stop/start) of the named service CLI Example: @@ -402,13 +412,13 @@ def full_restart(name): .. code-block:: bash salt '*' service.full_restart <service name> - ''' - cmd = ['service', name, '--full-restart'] - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = ["service", name, "--full-restart"] + return not __salt__["cmd.retcode"](cmd, python_shell=False) def reload_(name): - ''' + """ Reload the named service CLI Example: @@ -416,13 +426,13 @@ def reload_(name): .. code-block:: bash salt '*' service.reload <service name> - ''' - cmd = ['service', name, 'reload'] - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = ["service", name, "reload"] + return not __salt__["cmd.retcode"](cmd, python_shell=False) def force_reload(name): - ''' + """ Force-reload the named service CLI Example: @@ -430,13 +440,13 @@ def force_reload(name): .. code-block:: bash salt '*' service.force_reload <service name> - ''' - cmd = ['service', name, 'force-reload'] - return not __salt__['cmd.retcode'](cmd, python_shell=False) + """ + cmd = ["service", name, "force-reload"] + return not __salt__["cmd.retcode"](cmd, python_shell=False) def status(name, sig=None): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -457,78 +467,79 @@ def status(name, sig=None): .. code-block:: bash salt '*' service.status <service name> [service signature] - ''' + """ if sig: - return bool(__salt__['status.pid'](sig)) + return bool(__salt__["status.pid"](sig)) - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(get_all(), name) else: services = [name] results = {} for service in services: - cmd = ['service', service, 'status'] + cmd = ["service", service, "status"] if _service_is_upstart(service): # decide result base on cmd output, thus ignore retcode, # which makes cmd output not at error lvl even when cmd fail. - results[service] = 'start/running' in __salt__['cmd.run'](cmd, python_shell=False, - ignore_retcode=True) + results[service] = "start/running" in __salt__["cmd.run"]( + cmd, python_shell=False, ignore_retcode=True + ) else: # decide result base on retcode, thus ignore output (set quite) # because there is no way to avoid logging at error lvl when # service is not running - retcode != 0 (which is totally relevant). - results[service] = not bool(__salt__['cmd.retcode'](cmd, python_shell=False, - ignore_retcode=True, - quite=True)) + results[service] = not bool( + __salt__["cmd.retcode"]( + cmd, python_shell=False, ignore_retcode=True, quite=True + ) + ) if contains_globbing: return results return results[name] def _get_service_exec(): - ''' + """ Debian uses update-rc.d to manage System-V style services. http://www.debian.org/doc/debian-policy/ch-opersys.html#s9.3.3 - ''' - executable = 'update-rc.d' + """ + executable = "update-rc.d" salt.utils.path.check_or_die(executable) return executable def _upstart_disable(name): - ''' + """ Disable an Upstart service. - ''' + """ if _upstart_is_disabled(name): return _upstart_is_disabled(name) - override = '/etc/init/{0}.override'.format(name) - with salt.utils.files.fopen(override, 'a') as ofile: - ofile.write(salt.utils.stringutils.to_str('manual\n')) + override = "/etc/init/{0}.override".format(name) + with salt.utils.files.fopen(override, "a") as ofile: + ofile.write(salt.utils.stringutils.to_str("manual\n")) return _upstart_is_disabled(name) def _upstart_enable(name): - ''' + """ Enable an Upstart service. - ''' + """ if _upstart_is_enabled(name): return _upstart_is_enabled(name) - override = '/etc/init/{0}.override'.format(name) - files = ['/etc/init/{0}.conf'.format(name), override] + override = "/etc/init/{0}.override".format(name) + files = ["/etc/init/{0}.conf".format(name), override] for file_name in filter(os.path.isfile, files): - with salt.utils.files.fopen(file_name, 'r+') as fp_: - new_text = re.sub(r'^\s*manual\n?', - '', - salt.utils.stringutils.to_unicode(fp_.read()), - 0, - re.MULTILINE) - fp_.seek(0) - fp_.write( - salt.utils.stringutils.to_str( - new_text - ) + with salt.utils.files.fopen(file_name, "r+") as fp_: + new_text = re.sub( + r"^\s*manual\n?", + "", + salt.utils.stringutils.to_unicode(fp_.read()), + 0, + re.MULTILINE, ) + fp_.seek(0) + fp_.write(salt.utils.stringutils.to_str(new_text)) fp_.truncate() if os.access(override, os.R_OK) and os.path.getsize(override) == 0: os.unlink(override) @@ -536,7 +547,7 @@ def _upstart_enable(name): def enable(name, **kwargs): - ''' + """ Enable the named service to start at boot CLI Example: @@ -544,16 +555,16 @@ def enable(name, **kwargs): .. code-block:: bash salt '*' service.enable <service name> - ''' + """ if _service_is_upstart(name): return _upstart_enable(name) executable = _get_service_exec() - cmd = '{0} -f {1} defaults'.format(executable, name) - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = "{0} -f {1} defaults".format(executable, name) + return not __salt__["cmd.retcode"](cmd, python_shell=False) def disable(name, **kwargs): - ''' + """ Disable the named service from starting on boot CLI Example: @@ -561,16 +572,16 @@ def disable(name, **kwargs): .. code-block:: bash salt '*' service.disable <service name> - ''' + """ if _service_is_upstart(name): return _upstart_disable(name) executable = _get_service_exec() - cmd = [executable, '-f', name, 'remove'] - return not __salt__['cmd.retcode'](cmd, python_shell=False) + cmd = [executable, "-f", name, "remove"] + return not __salt__["cmd.retcode"](cmd, python_shell=False) def enabled(name, **kwargs): - ''' + """ Check to see if the named service is enabled to start on boot CLI Example: @@ -578,7 +589,7 @@ def enabled(name, **kwargs): .. code-block:: bash salt '*' service.enabled <service name> - ''' + """ if _service_is_upstart(name): return _upstart_is_enabled(name) else: @@ -588,7 +599,7 @@ def enabled(name, **kwargs): def disabled(name): - ''' + """ Check to see if the named service is disabled to start on boot CLI Example: @@ -596,7 +607,7 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled <service name> - ''' + """ if _service_is_upstart(name): return _upstart_is_disabled(name) else: diff --git a/salt/modules/uptime.py b/salt/modules/uptime.py index aa31f80ab42..c57bfe76260 100644 --- a/salt/modules/uptime.py +++ b/salt/modules/uptime.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Wrapper around uptime API ========================= -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -13,6 +14,7 @@ from salt.exceptions import CommandExecutionError try: import requests + ENABLED = True except ImportError: ENABLED = False @@ -21,16 +23,16 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if the requests python module is available - ''' + """ if ENABLED: - return 'uptime' - return (False, 'uptime module needs the python requests module to work') + return "uptime" + return (False, "uptime module needs the python requests module to work") def create(name, **params): - '''Create a check on a given URL. + """Create a check on a given URL. Additional parameters can be used and are passed to API (for example interval, maxTime, etc). See the documentation @@ -43,25 +45,23 @@ def create(name, **params): salt '*' uptime.create http://example.org - ''' + """ if check_exists(name): - msg = 'Trying to create check that already exists : {0}'.format(name) + msg = "Trying to create check that already exists : {0}".format(name) log.error(msg) raise CommandExecutionError(msg) application_url = _get_application_url() - log.debug('[uptime] trying PUT request') + log.debug("[uptime] trying PUT request") params.update(url=name) - req = requests.put('{0}/api/checks'.format(application_url), data=params) + req = requests.put("{0}/api/checks".format(application_url), data=params) if not req.ok: - raise CommandExecutionError( - 'request to uptime failed : {0}'.format(req.reason) - ) - log.debug('[uptime] PUT request successful') - return req.json()['_id'] + raise CommandExecutionError("request to uptime failed : {0}".format(req.reason)) + log.debug("[uptime] PUT request successful") + return req.json()["_id"] def delete(name): - ''' + """ Delete a check on a given URL CLI Example: @@ -69,39 +69,37 @@ def delete(name): .. code-block:: bash salt '*' uptime.delete http://example.org - ''' + """ if not check_exists(name): msg = "Trying to delete check that doesn't exists : {0}".format(name) log.error(msg) raise CommandExecutionError(msg) application_url = _get_application_url() - log.debug('[uptime] trying DELETE request') - jcontent = requests.get('{0}/api/checks'.format(application_url)).json() - url_id = [x['_id'] for x in jcontent if x['url'] == name][0] - req = requests.delete('{0}/api/checks/{1}'.format(application_url, url_id)) + log.debug("[uptime] trying DELETE request") + jcontent = requests.get("{0}/api/checks".format(application_url)).json() + url_id = [x["_id"] for x in jcontent if x["url"] == name][0] + req = requests.delete("{0}/api/checks/{1}".format(application_url, url_id)) if not req.ok: - raise CommandExecutionError( - 'request to uptime failed : {0}'.format(req.reason) - ) - log.debug('[uptime] DELETE request successful') + raise CommandExecutionError("request to uptime failed : {0}".format(req.reason)) + log.debug("[uptime] DELETE request successful") return True def _get_application_url(): - ''' + """ Helper function to get application url from pillar - ''' - application_url = __salt__['pillar.get']('uptime:application_url') + """ + application_url = __salt__["pillar.get"]("uptime:application_url") if application_url is None: - log.error('Could not load uptime:application_url pillar') + log.error("Could not load uptime:application_url pillar") raise CommandExecutionError( - 'uptime:application_url pillar is required for authentication' + "uptime:application_url pillar is required for authentication" ) return application_url def checks_list(): - ''' + """ List URL checked by uptime CLI Example: @@ -109,15 +107,15 @@ def checks_list(): .. code-block:: bash salt '*' uptime.checks_list - ''' + """ application_url = _get_application_url() - log.debug('[uptime] get checks') - jcontent = requests.get('{0}/api/checks'.format(application_url)).json() - return [x['url'] for x in jcontent] + log.debug("[uptime] get checks") + jcontent = requests.get("{0}/api/checks".format(application_url)).json() + return [x["url"] for x in jcontent] def check_exists(name): - ''' + """ Check if a given URL is in being monitored by uptime CLI Example: @@ -125,8 +123,8 @@ def check_exists(name): .. code-block:: bash salt '*' uptime.check_exists http://example.org - ''' + """ if name in checks_list(): - log.debug('[uptime] found %s in checks', name) + log.debug("[uptime] found %s in checks", name) return True return False diff --git a/salt/modules/useradd.py b/salt/modules/useradd.py index 00d790e9589..905446b1f87 100644 --- a/salt/modules/useradd.py +++ b/salt/modules/useradd.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage users with the useradd command .. important:: @@ -7,23 +7,18 @@ Manage users with the useradd command minion, and it is using a different module (or gives an error similar to *'user.info' is not available*), see :ref:`here <module-provider-override>`. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -try: - import pwd - HAS_PWD = True -except ImportError: - HAS_PWD = False -import logging import copy import functools +import logging import os # Import salt libs import salt.utils.data -import salt.utils.files import salt.utils.decorators.path +import salt.utils.files import salt.utils.stringutils import salt.utils.user from salt.exceptions import CommandExecutionError @@ -31,26 +26,37 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six +try: + import pwd + + HAS_PWD = True +except ImportError: + HAS_PWD = False + + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'user' +__virtualname__ = "user" def __virtual__(): - ''' + """ Set the user module if the kernel is Linux, OpenBSD, NetBSD or AIX - ''' + """ - if HAS_PWD and __grains__['kernel'] in ('Linux', 'OpenBSD', 'NetBSD', 'AIX'): + if HAS_PWD and __grains__["kernel"] in ("Linux", "OpenBSD", "NetBSD", "AIX"): return __virtualname__ - return (False, 'useradd execution module not loaded: either pwd python library not available or system not one of Linux, OpenBSD, NetBSD or AIX') + return ( + False, + "useradd execution module not loaded: either pwd python library not available or system not one of Linux, OpenBSD, NetBSD or AIX", + ) def _quote_username(name): - ''' + """ Usernames can only contain ascii chars, so make sure we return a str type - ''' + """ if not isinstance(name, six.string_types): return str(name) # future lint: disable=blacklisted-function else: @@ -58,47 +64,52 @@ def _quote_username(name): def _get_gecos(name, root=None): - ''' + """ Retrieve GECOS field info and return it in dictionary form - ''' - if root is not None and __grains__['kernel'] != 'AIX': + """ + if root is not None and __grains__["kernel"] != "AIX": getpwnam = functools.partial(_getpwnam, root=root) else: getpwnam = functools.partial(pwd.getpwnam) gecos_field = salt.utils.stringutils.to_unicode( - getpwnam(_quote_username(name)).pw_gecos).split(',', 4) + getpwnam(_quote_username(name)).pw_gecos + ).split(",", 4) if not gecos_field: return {} else: # Assign empty strings for any unspecified trailing GECOS fields while len(gecos_field) < 5: - gecos_field.append('') - return {'fullname': salt.utils.data.decode(gecos_field[0]), - 'roomnumber': salt.utils.data.decode(gecos_field[1]), - 'workphone': salt.utils.data.decode(gecos_field[2]), - 'homephone': salt.utils.data.decode(gecos_field[3]), - 'other': salt.utils.data.decode(gecos_field[4])} + gecos_field.append("") + return { + "fullname": salt.utils.data.decode(gecos_field[0]), + "roomnumber": salt.utils.data.decode(gecos_field[1]), + "workphone": salt.utils.data.decode(gecos_field[2]), + "homephone": salt.utils.data.decode(gecos_field[3]), + "other": salt.utils.data.decode(gecos_field[4]), + } def _build_gecos(gecos_dict): - ''' + """ Accepts a dictionary entry containing GECOS field names and their values, and returns a full GECOS comment string, to be used with usermod. - ''' - return '{0},{1},{2},{3},{4}'.format(gecos_dict.get('fullname', ''), - gecos_dict.get('roomnumber', ''), - gecos_dict.get('workphone', ''), - gecos_dict.get('homephone', ''), - gecos_dict.get('other', ''),).rstrip(',') + """ + return "{0},{1},{2},{3},{4}".format( + gecos_dict.get("fullname", ""), + gecos_dict.get("roomnumber", ""), + gecos_dict.get("workphone", ""), + gecos_dict.get("homephone", ""), + gecos_dict.get("other", ""), + ).rstrip(",") def _update_gecos(name, key, value, root=None): - ''' + """ Common code to change a user's GECOS information - ''' + """ if value is None: - value = '' + value = "" elif not isinstance(value, six.string_types): value = six.text_type(value) else: @@ -111,33 +122,35 @@ def _update_gecos(name, key, value, root=None): gecos_data = copy.deepcopy(pre_info) gecos_data[key] = value - cmd = ['usermod'] - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) - cmd.extend(('-c', _build_gecos(gecos_data), name)) + cmd = ["usermod"] + if root is not None and __grains__["kernel"] != "AIX": + cmd.extend(("-R", root)) + cmd.extend(("-c", _build_gecos(gecos_data), name)) - __salt__['cmd.run'](cmd, python_shell=False) + __salt__["cmd.run"](cmd, python_shell=False) return _get_gecos(name, root=root).get(key) == value -def add(name, - uid=None, - gid=None, - groups=None, - home=None, - shell=None, - unique=True, - system=False, - fullname='', - roomnumber='', - workphone='', - homephone='', - other='', - createhome=True, - loginclass=None, - nologinit=False, - root=None): - ''' +def add( + name, + uid=None, + gid=None, + groups=None, + home=None, + shell=None, + unique=True, + system=False, + fullname="", + roomnumber="", + workphone="", + homephone="", + other="", + createhome=True, + loginclass=None, + nologinit=False, + root=None, +): + """ Add a user to the minion name @@ -196,49 +209,43 @@ def add(name, .. code-block:: bash salt '*' user.add name <uid> <gid> <groups> <home> <shell> - ''' - cmd = ['useradd'] + """ + cmd = ["useradd"] if shell: - cmd.extend(['-s', shell]) - if uid not in (None, ''): - cmd.extend(['-u', uid]) - if gid not in (None, ''): - cmd.extend(['-g', gid]) + cmd.extend(["-s", shell]) + if uid not in (None, ""): + cmd.extend(["-u", uid]) + if gid not in (None, ""): + cmd.extend(["-g", gid]) elif groups is not None and name in groups: - defs_file = '/etc/login.defs' - if __grains__['kernel'] != 'OpenBSD': + defs_file = "/etc/login.defs" + if __grains__["kernel"] != "OpenBSD": try: with salt.utils.files.fopen(defs_file) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if 'USERGROUPS_ENAB' not in line[:15]: + if "USERGROUPS_ENAB" not in line[:15]: continue - if 'yes' in line: - cmd.extend([ - '-g', __salt__['file.group_to_gid'](name) - ]) + if "yes" in line: + cmd.extend(["-g", __salt__["file.group_to_gid"](name)]) # We found what we wanted, let's break out of the loop break except OSError: log.debug( - 'Error reading %s', - defs_file, - exc_info_on_loglevel=logging.DEBUG + "Error reading %s", defs_file, exc_info_on_loglevel=logging.DEBUG ) else: - usermgmt_file = '/etc/usermgmt.conf' + usermgmt_file = "/etc/usermgmt.conf" try: with salt.utils.files.fopen(usermgmt_file) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if 'group' not in line[:5]: + if "group" not in line[:5]: continue - cmd.extend([ - '-g', line.split()[-1] - ]) + cmd.extend(["-g", line.split()[-1]]) # We found what we wanted, let's break out of the loop break @@ -247,37 +254,38 @@ def add(name, pass if createhome: - cmd.append('-m') - elif (__grains__['kernel'] != 'NetBSD' - and __grains__['kernel'] != 'OpenBSD'): - cmd.append('-M') + cmd.append("-m") + elif __grains__["kernel"] != "NetBSD" and __grains__["kernel"] != "OpenBSD": + cmd.append("-M") if nologinit: - cmd.append('-l') + cmd.append("-l") if home is not None: - cmd.extend(['-d', home]) + cmd.extend(["-d", home]) - if not unique and __grains__['kernel'] != 'AIX': - cmd.append('-o') + if not unique and __grains__["kernel"] != "AIX": + cmd.append("-o") - if (system - and __grains__['kernel'] != 'NetBSD' - and __grains__['kernel'] != 'OpenBSD'): - cmd.append('-r') + if ( + system + and __grains__["kernel"] != "NetBSD" + and __grains__["kernel"] != "OpenBSD" + ): + cmd.append("-r") - if __grains__['kernel'] == 'OpenBSD': + if __grains__["kernel"] == "OpenBSD": if loginclass is not None: - cmd.extend(['-L', loginclass]) + cmd.extend(["-L", loginclass]) cmd.append(name) - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) + if root is not None and __grains__["kernel"] != "AIX": + cmd.extend(("-R", root)) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - if ret['retcode'] != 0: + if ret["retcode"] != 0: return False # At this point, the user was successfully created, so return true @@ -304,7 +312,7 @@ def add(name, def delete(name, remove=False, force=False, root=None): - ''' + """ Remove a user from the minion name @@ -324,38 +332,38 @@ def delete(name, remove=False, force=False, root=None): .. code-block:: bash salt '*' user.delete name remove=True force=True - ''' - cmd = ['userdel'] + """ + cmd = ["userdel"] if remove: - cmd.append('-r') + cmd.append("-r") - if force and __grains__['kernel'] != 'OpenBSD' and __grains__['kernel'] != 'AIX': - cmd.append('-f') + if force and __grains__["kernel"] != "OpenBSD" and __grains__["kernel"] != "AIX": + cmd.append("-f") cmd.append(name) - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) + if root is not None and __grains__["kernel"] != "AIX": + cmd.extend(("-R", root)) - ret = __salt__['cmd.run_all'](cmd, python_shell=False) + ret = __salt__["cmd.run_all"](cmd, python_shell=False) - if ret['retcode'] == 0: + if ret["retcode"] == 0: # Command executed with no errors return True - if ret['retcode'] == 12: + if ret["retcode"] == 12: # There's a known bug in Debian based distributions, at least, that # makes the command exit with 12, see: # https://bugs.launchpad.net/ubuntu/+source/shadow/+bug/1023509 - if __grains__['os_family'] not in ('Debian',): + if __grains__["os_family"] not in ("Debian",): return False - if 'var/mail' in ret['stderr'] or 'var/spool/mail' in ret['stderr']: + if "var/mail" in ret["stderr"] or "var/spool/mail" in ret["stderr"]: # We've hit the bug, let's log it and not fail log.debug( - 'While the userdel exited with code 12, this is a known bug on ' - 'debian based distributions. See http://goo.gl/HH3FzT' + "While the userdel exited with code 12, this is a known bug on " + "debian based distributions. See http://goo.gl/HH3FzT" ) return True @@ -363,7 +371,7 @@ def delete(name, remove=False, force=False, root=None): def getent(refresh=False, root=None): - ''' + """ Return the list of all info for all users refresh @@ -377,49 +385,49 @@ def getent(refresh=False, root=None): .. code-block:: bash salt '*' user.getent - ''' - if 'user.getent' in __context__ and not refresh: - return __context__['user.getent'] + """ + if "user.getent" in __context__ and not refresh: + return __context__["user.getent"] ret = [] - if root is not None and __grains__['kernel'] != 'AIX': + if root is not None and __grains__["kernel"] != "AIX": getpwall = functools.partial(_getpwall, root=root) else: getpwall = functools.partial(pwd.getpwall) for data in getpwall(): ret.append(_format_info(data)) - __context__['user.getent'] = ret + __context__["user.getent"] = ret return ret def _chattrib(name, key, value, param, persist=False, root=None): - ''' + """ Change an attribute for a named user - ''' + """ pre_info = info(name, root=root) if not pre_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("User '{0}' does not exist".format(name)) if value == pre_info[key]: return True - cmd = ['usermod'] + cmd = ["usermod"] - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) + if root is not None and __grains__["kernel"] != "AIX": + cmd.extend(("-R", root)) - if persist and __grains__['kernel'] != 'OpenBSD': - cmd.append('-m') + if persist and __grains__["kernel"] != "OpenBSD": + cmd.append("-m") cmd.extend((param, value, name)) - __salt__['cmd.run'](cmd, python_shell=False) + __salt__["cmd.run"](cmd, python_shell=False) return info(name, root=root).get(key) == value def chuid(name, uid, root=None): - ''' + """ Change the uid for a named user name @@ -436,12 +444,12 @@ def chuid(name, uid, root=None): .. code-block:: bash salt '*' user.chuid foo 4376 - ''' - return _chattrib(name, 'uid', uid, '-u', root=root) + """ + return _chattrib(name, "uid", uid, "-u", root=root) def chgid(name, gid, root=None): - ''' + """ Change the default group of the user name @@ -458,12 +466,12 @@ def chgid(name, gid, root=None): .. code-block:: bash salt '*' user.chgid foo 4376 - ''' - return _chattrib(name, 'gid', gid, '-g', root=root) + """ + return _chattrib(name, "gid", gid, "-g", root=root) def chshell(name, shell, root=None): - ''' + """ Change the default shell of the user name @@ -480,12 +488,12 @@ def chshell(name, shell, root=None): .. code-block:: bash salt '*' user.chshell foo /bin/zsh - ''' - return _chattrib(name, 'shell', shell, '-s', root=root) + """ + return _chattrib(name, "shell", shell, "-s", root=root) def chhome(name, home, persist=False, root=None): - ''' + """ Change the home directory of the user, pass True for persist to move files to the new home directory if the old home directory exist. @@ -506,12 +514,12 @@ def chhome(name, home, persist=False, root=None): .. code-block:: bash salt '*' user.chhome foo /home/users/foo True - ''' - return _chattrib(name, 'home', home, '-d', persist=persist, root=root) + """ + return _chattrib(name, "home", home, "-d", persist=persist, root=root) def chgroups(name, groups, append=False, root=None): - ''' + """ Change the groups to which this user belongs name @@ -533,48 +541,48 @@ def chgroups(name, groups, append=False, root=None): salt '*' user.chgroups foo wheel,root salt '*' user.chgroups foo wheel,root append=True - ''' + """ if isinstance(groups, six.string_types): - groups = groups.split(',') + groups = groups.split(",") ugrps = set(list_groups(name)) if ugrps == set(groups): return True - cmd = ['usermod'] + cmd = ["usermod"] - if __grains__['kernel'] != 'OpenBSD': - if append and __grains__['kernel'] != 'AIX': - cmd.append('-a') - cmd.append('-G') + if __grains__["kernel"] != "OpenBSD": + if append and __grains__["kernel"] != "AIX": + cmd.append("-a") + cmd.append("-G") else: if append: - cmd.append('-G') + cmd.append("-G") else: - cmd.append('-S') + cmd.append("-S") - if append and __grains__['kernel'] == 'AIX': - cmd.extend([','.join(ugrps) + ',' + ','.join(groups), name]) + if append and __grains__["kernel"] == "AIX": + cmd.extend([",".join(ugrps) + "," + ",".join(groups), name]) else: - cmd.extend([','.join(groups), name]) + cmd.extend([",".join(groups), name]) - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) + if root is not None and __grains__["kernel"] != "AIX": + cmd.extend(("-R", root)) - result = __salt__['cmd.run_all'](cmd, python_shell=False) + result = __salt__["cmd.run_all"](cmd, python_shell=False) # try to fallback on gpasswd to add user to localgroups # for old lib-pamldap support - if __grains__['kernel'] != 'OpenBSD' and __grains__['kernel'] != 'AIX': - if result['retcode'] != 0 and 'not found in' in result['stderr']: + if __grains__["kernel"] != "OpenBSD" and __grains__["kernel"] != "AIX": + if result["retcode"] != 0 and "not found in" in result["stderr"]: ret = True for group in groups: - cmd = ['gpasswd', '-a', name, group] - if __salt__['cmd.retcode'](cmd, python_shell=False) != 0: + cmd = ["gpasswd", "-a", name, group] + if __salt__["cmd.retcode"](cmd, python_shell=False) != 0: ret = False return ret - return result['retcode'] == 0 + return result["retcode"] == 0 def chfullname(name, fullname, root=None): - ''' + """ Change the user's Full Name name @@ -591,12 +599,12 @@ def chfullname(name, fullname, root=None): .. code-block:: bash salt '*' user.chfullname foo "Foo Bar" - ''' - return _update_gecos(name, 'fullname', fullname, root=root) + """ + return _update_gecos(name, "fullname", fullname, root=root) def chroomnumber(name, roomnumber, root=None): - ''' + """ Change the user's Room Number CLI Example: @@ -604,12 +612,12 @@ def chroomnumber(name, roomnumber, root=None): .. code-block:: bash salt '*' user.chroomnumber foo 123 - ''' - return _update_gecos(name, 'roomnumber', roomnumber, root=root) + """ + return _update_gecos(name, "roomnumber", roomnumber, root=root) def chworkphone(name, workphone, root=None): - ''' + """ Change the user's Work Phone name @@ -626,12 +634,12 @@ def chworkphone(name, workphone, root=None): .. code-block:: bash salt '*' user.chworkphone foo 7735550123 - ''' - return _update_gecos(name, 'workphone', workphone, root=root) + """ + return _update_gecos(name, "workphone", workphone, root=root) def chhomephone(name, homephone, root=None): - ''' + """ Change the user's Home Phone name @@ -648,12 +656,12 @@ def chhomephone(name, homephone, root=None): .. code-block:: bash salt '*' user.chhomephone foo 7735551234 - ''' - return _update_gecos(name, 'homephone', homephone, root=root) + """ + return _update_gecos(name, "homephone", homephone, root=root) def chother(name, other, root=None): - ''' + """ Change the user's other GECOS attribute name @@ -670,12 +678,12 @@ def chother(name, other, root=None): .. code-block:: bash salt '*' user.chother foobar - ''' - return _update_gecos(name, 'other', other, root=root) + """ + return _update_gecos(name, "other", other, root=root) def chloginclass(name, loginclass, root=None): - ''' + """ Change the default login class of the user name @@ -695,24 +703,24 @@ def chloginclass(name, loginclass, root=None): .. code-block:: bash salt '*' user.chloginclass foo staff - ''' - if __grains__['kernel'] != 'OpenBSD': + """ + if __grains__["kernel"] != "OpenBSD": return False if loginclass == get_loginclass(name): return True - cmd = ['usermod', '-L', loginclass, name] + cmd = ["usermod", "-L", loginclass, name] - if root is not None and __grains__['kernel'] != 'AIX': - cmd.extend(('-R', root)) + if root is not None and __grains__["kernel"] != "AIX": + cmd.extend(("-R", root)) - __salt__['cmd.run'](cmd, python_shell=False) + __salt__["cmd.run"](cmd, python_shell=False) return get_loginclass(name) == loginclass def info(name, root=None): - ''' + """ Return user information name @@ -726,7 +734,7 @@ def info(name, root=None): .. code-block:: bash salt '*' user.info root - ''' + """ # If root is provided, we use a less portable solution that # depends on analyzing /etc/passwd manually. Of course we cannot # find users from NIS nor LDAP, but in those cases do not makes @@ -734,7 +742,7 @@ def info(name, root=None): # # Please, note that if the non-root /etc/passwd file is long the # iteration can be slow. - if root is not None and __grains__['kernel'] != 'AIX': + if root is not None and __grains__["kernel"] != "AIX": getpwnam = functools.partial(_getpwnam, root=root) else: getpwnam = functools.partial(pwd.getpwnam) @@ -748,7 +756,7 @@ def info(name, root=None): def get_loginclass(name): - ''' + """ Get the login class of the user name @@ -762,51 +770,51 @@ def get_loginclass(name): .. code-block:: bash salt '*' user.get_loginclass foo - ''' - if __grains__['kernel'] != 'OpenBSD': + """ + if __grains__["kernel"] != "OpenBSD": return False - userinfo = __salt__['cmd.run_stdout']( - ['userinfo', name], - python_shell=False) + userinfo = __salt__["cmd.run_stdout"](["userinfo", name], python_shell=False) for line in userinfo.splitlines(): - if line.startswith('class'): + if line.startswith("class"): try: ret = line.split(None, 1)[1] break except (ValueError, IndexError): continue else: - ret = '' + ret = "" return ret def _format_info(data): - ''' + """ Return user information in a pretty way - ''' + """ # Put GECOS info into a list - gecos_field = salt.utils.stringutils.to_unicode(data.pw_gecos).split(',', 4) + gecos_field = salt.utils.stringutils.to_unicode(data.pw_gecos).split(",", 4) # Make sure our list has at least five elements while len(gecos_field) < 5: - gecos_field.append('') + gecos_field.append("") - return {'gid': data.pw_gid, - 'groups': list_groups(data.pw_name), - 'home': data.pw_dir, - 'name': data.pw_name, - 'passwd': data.pw_passwd, - 'shell': data.pw_shell, - 'uid': data.pw_uid, - 'fullname': gecos_field[0], - 'roomnumber': gecos_field[1], - 'workphone': gecos_field[2], - 'homephone': gecos_field[3], - 'other': gecos_field[4]} + return { + "gid": data.pw_gid, + "groups": list_groups(data.pw_name), + "home": data.pw_dir, + "name": data.pw_name, + "passwd": data.pw_passwd, + "shell": data.pw_shell, + "uid": data.pw_uid, + "fullname": gecos_field[0], + "roomnumber": gecos_field[1], + "workphone": gecos_field[2], + "homephone": gecos_field[3], + "other": gecos_field[4], + } -@salt.utils.decorators.path.which('id') +@salt.utils.decorators.path.which("id") def primary_group(name): - ''' + """ Return the primary group of the named user .. versionadded:: 2016.3.0 @@ -819,12 +827,12 @@ def primary_group(name): .. code-block:: bash salt '*' user.primary_group saltadmin - ''' - return __salt__['cmd.run'](['id', '-g', '-n', name]) + """ + return __salt__["cmd.run"](["id", "-g", "-n", name]) def list_groups(name): - ''' + """ Return a list of groups the named user belongs to name @@ -835,12 +843,12 @@ def list_groups(name): .. code-block:: bash salt '*' user.list_groups foo - ''' + """ return salt.utils.user.get_group_list(name) def list_users(root=None): - ''' + """ Return a list of all users root @@ -851,8 +859,8 @@ def list_users(root=None): .. code-block:: bash salt '*' user.list_users - ''' - if root is not None and __grains__['kernel'] != 'AIX': + """ + if root is not None and __grains__["kernel"] != "AIX": getpwall = functools.partial(_getpwall, root=root) else: getpwall = functools.partial(pwd.getpwall) @@ -861,7 +869,7 @@ def list_users(root=None): def rename(name, new_name, root=None): - ''' + """ Change the username for a named user name @@ -878,23 +886,23 @@ def rename(name, new_name, root=None): .. code-block:: bash salt '*' user.rename name new_name - ''' + """ if info(new_name, root=root): - raise CommandExecutionError('User \'{0}\' already exists'.format(new_name)) + raise CommandExecutionError("User '{0}' already exists".format(new_name)) - return _chattrib(name, 'name', new_name, '-l', root=root) + return _chattrib(name, "name", new_name, "-l", root=root) def _getpwnam(name, root=None): - ''' + """ Alternative implementation for getpwnam, that use only /etc/passwd - ''' - root = '/' if not root else root - passwd = os.path.join(root, 'etc/passwd') + """ + root = "/" if not root else root + passwd = os.path.join(root, "etc/passwd") with salt.utils.files.fopen(passwd) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - comps = line.strip().split(':') + comps = line.strip().split(":") if comps[0] == name: # Generate a getpwnam compatible output comps[2], comps[3] = int(comps[2]), int(comps[3]) @@ -903,15 +911,15 @@ def _getpwnam(name, root=None): def _getpwall(root=None): - ''' + """ Alternative implemetantion for getpwall, that use only /etc/passwd - ''' - root = '/' if not root else root - passwd = os.path.join(root, 'etc/passwd') + """ + root = "/" if not root else root + passwd = os.path.join(root, "etc/passwd") with salt.utils.files.fopen(passwd) as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - comps = line.strip().split(':') + comps = line.strip().split(":") # Generate a getpwall compatible output comps[2], comps[3] = int(comps[2]), int(comps[3]) yield pwd.struct_passwd(comps) diff --git a/salt/modules/uwsgi.py b/salt/modules/uwsgi.py index 21ed1b686e0..7173603b2f0 100644 --- a/salt/modules/uwsgi.py +++ b/salt/modules/uwsgi.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" uWSGI stats server https://uwsgi-docs.readthedocs.io/en/latest/StatsServer.html :maintainer: Peter Baumgartner <pete@lincolnloop.com> :maturity: new :platform: all -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -15,17 +15,20 @@ import salt.utils.path def __virtual__(): - ''' + """ Only load the module if uwsgi is installed - ''' - cmd = 'uwsgi' + """ + cmd = "uwsgi" if salt.utils.path.which(cmd): return cmd - return (False, 'The uwsgi execution module failed to load: the uwsgi binary is not in the path.') + return ( + False, + "The uwsgi execution module failed to load: the uwsgi binary is not in the path.", + ) def stats(socket): - ''' + """ Return the data from `uwsgi --connect-and-read` as a dictionary. socket @@ -38,8 +41,8 @@ def stats(socket): salt '*' uwsgi.stats /var/run/mystatsserver.sock salt '*' uwsgi.stats 127.0.0.1:5050 - ''' + """ - cmd = ['uwsgi', '--connect-and-read', '{0}'.format(socket)] - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = ["uwsgi", "--connect-and-read", "{0}".format(socket)] + out = __salt__["cmd.run"](cmd, python_shell=False) return salt.utils.json.loads(out) diff --git a/salt/modules/vagrant.py b/salt/modules/vagrant.py index 89fd74eeb23..26da0c7d701 100644 --- a/salt/modules/vagrant.py +++ b/salt/modules/vagrant.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Work with virtual machines managed by Vagrant. .. versionadded:: 2018.3.0 @@ -26,158 +26,163 @@ requirements: table: sdb create_table: True -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os +import salt.ext.six as six + # Import salt libs import salt.utils.files import salt.utils.path import salt.utils.stringutils -from salt.exceptions import CommandExecutionError, SaltInvocationError -import salt.ext.six as six from salt._compat import ipaddress +from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) -__virtualname__ = 'vagrant' +__virtualname__ = "vagrant" -VAGRANT_SDB_URL = 'sdb://vagrant_sdb_data/' +VAGRANT_SDB_URL = "sdb://vagrant_sdb_data/" def __virtual__(): - ''' + """ run Vagrant commands if possible - ''' - if salt.utils.path.which('vagrant') is None: - return False, 'The vagrant module could not be loaded: vagrant command not found' + """ + if salt.utils.path.which("vagrant") is None: + return ( + False, + "The vagrant module could not be loaded: vagrant command not found", + ) return __virtualname__ def _build_sdb_uri(key): - ''' + """ returns string used to fetch data for "key" from the sdb store. Salt node id's are used as the key for vm_ dicts. - ''' - return '{}{}'.format(VAGRANT_SDB_URL, key) + """ + return "{}{}".format(VAGRANT_SDB_URL, key) def _build_machine_uri(machine, cwd): - ''' + """ returns string used to fetch id names from the sdb store. the cwd and machine name are concatenated with '?' which should never collide with a Salt node id -- which is important since we will be storing both in the same table. - ''' - key = '{}?{}'.format(machine, os.path.abspath(cwd)) + """ + key = "{}?{}".format(machine, os.path.abspath(cwd)) return _build_sdb_uri(key) def _update_vm_info(name, vm_): - ''' store the vm_ information keyed by name ''' - __utils__['sdb.sdb_set'](_build_sdb_uri(name), vm_, __opts__) + """ store the vm_ information keyed by name """ + __utils__["sdb.sdb_set"](_build_sdb_uri(name), vm_, __opts__) # store machine-to-name mapping, too - if vm_['machine']: - __utils__['sdb.sdb_set']( - _build_machine_uri(vm_['machine'], vm_.get('cwd', '.')), - name, - __opts__) + if vm_["machine"]: + __utils__["sdb.sdb_set"]( + _build_machine_uri(vm_["machine"], vm_.get("cwd", ".")), name, __opts__ + ) def get_vm_info(name): - ''' + """ get the information for a VM. :param name: salt_id name :return: dictionary of {'machine': x, 'cwd': y, ...}. - ''' + """ try: - vm_ = __utils__['sdb.sdb_get'](_build_sdb_uri(name), __opts__) + vm_ = __utils__["sdb.sdb_get"](_build_sdb_uri(name), __opts__) except KeyError: raise SaltInvocationError( - 'Probable sdb driver not found. Check your configuration.') - if vm_ is None or 'machine' not in vm_: + "Probable sdb driver not found. Check your configuration." + ) + if vm_ is None or "machine" not in vm_: raise SaltInvocationError( - 'No Vagrant machine defined for Salt_id {}'.format(name)) + "No Vagrant machine defined for Salt_id {}".format(name) + ) return vm_ def get_machine_id(machine, cwd): - ''' + """ returns the salt_id name of the Vagrant VM :param machine: the Vagrant machine name :param cwd: the path to Vagrantfile :return: salt_id name - ''' - name = __utils__['sdb.sdb_get'](_build_machine_uri(machine, cwd), __opts__) + """ + name = __utils__["sdb.sdb_get"](_build_machine_uri(machine, cwd), __opts__) return name def _erase_vm_info(name): - ''' + """ erase the information for a VM the we are destroying. some sdb drivers (such as the SQLite driver we expect to use) do not have a `delete` method, so if the delete fails, we have to replace the with a blank entry. - ''' + """ try: # delete the machine record vm_ = get_vm_info(name) - if vm_['machine']: - key = _build_machine_uri(vm_['machine'], vm_.get('cwd', '.')) + if vm_["machine"]: + key = _build_machine_uri(vm_["machine"], vm_.get("cwd", ".")) try: - __utils__['sdb.sdb_delete'](key, __opts__) + __utils__["sdb.sdb_delete"](key, __opts__) except KeyError: # no delete method found -- load a blank value - __utils__['sdb.sdb_set'](key, None, __opts__) + __utils__["sdb.sdb_set"](key, None, __opts__) except Exception: # pylint: disable=broad-except pass uri = _build_sdb_uri(name) try: # delete the name record - __utils__['sdb.sdb_delete'](uri, __opts__) + __utils__["sdb.sdb_delete"](uri, __opts__) except KeyError: # no delete method found -- load an empty dictionary - __utils__['sdb.sdb_set'](uri, {}, __opts__) + __utils__["sdb.sdb_set"](uri, {}, __opts__) except Exception: # pylint: disable=broad-except pass def _vagrant_ssh_config(vm_): - ''' + """ get the information for ssh communication from the new VM :param vm_: the VM's info as we have it now :return: dictionary of ssh stuff - ''' - machine = vm_['machine'] - log.info('requesting vagrant ssh-config for VM %s', machine or '(default)') - cmd = 'vagrant ssh-config {}'.format(machine) - reply = __salt__['cmd.shell'](cmd, - runas=vm_.get('runas'), - cwd=vm_.get('cwd'), - ignore_retcode=True) + """ + machine = vm_["machine"] + log.info("requesting vagrant ssh-config for VM %s", machine or "(default)") + cmd = "vagrant ssh-config {}".format(machine) + reply = __salt__["cmd.shell"]( + cmd, runas=vm_.get("runas"), cwd=vm_.get("cwd"), ignore_retcode=True + ) ssh_config = {} - for line in reply.split('\n'): # build a dictionary of the text reply + for line in reply.split("\n"): # build a dictionary of the text reply tokens = line.strip().split() if len(tokens) == 2: # each two-token line becomes a key:value pair ssh_config[tokens[0]] = tokens[1] - log.debug('ssh_config=%s', repr(ssh_config)) + log.debug("ssh_config=%s", repr(ssh_config)) return ssh_config def version(): - ''' + """ Return the version of Vagrant on the minion CLI Example: @@ -185,13 +190,13 @@ def version(): .. code-block:: bash salt '*' vagrant.version - ''' - cmd = 'vagrant -v' - return __salt__['cmd.shell'](cmd) + """ + cmd = "vagrant -v" + return __salt__["cmd.shell"](cmd) def list_domains(): - ''' + """ Return a list of the salt_id names of all available Vagrant VMs on this host without regard to the path where they are defined. @@ -204,12 +209,12 @@ def list_domains(): The log shows information about all known Vagrant environments on this machine. This data is cached and may not be completely up-to-date. - ''' + """ vms = [] - cmd = 'vagrant global-status' - reply = __salt__['cmd.shell'](cmd) - log.debug('--->\n%s', reply) - for line in reply.split('\n'): # build a list of the text reply + cmd = "vagrant global-status" + reply = __salt__["cmd.shell"](cmd) + log.debug("--->\n%s", reply) + for line in reply.split("\n"): # build a list of the text reply tokens = line.strip().split() try: _ = int(tokens[0], 16) # valid id numbers are hexadecimal @@ -224,7 +229,7 @@ def list_domains(): def list_active_vms(cwd=None): - ''' + """ Return a list of machine names for active virtual machine on the host, which are defined in the Vagrantfile at the indicated path. @@ -233,21 +238,21 @@ def list_active_vms(cwd=None): .. code-block:: bash salt '*' vagrant.list_active_vms cwd=/projects/project_1 - ''' + """ vms = [] - cmd = 'vagrant status' - reply = __salt__['cmd.shell'](cmd, cwd=cwd) - log.info('--->\n%s', reply) - for line in reply.split('\n'): # build a list of the text reply + cmd = "vagrant status" + reply = __salt__["cmd.shell"](cmd, cwd=cwd) + log.info("--->\n%s", reply) + for line in reply.split("\n"): # build a list of the text reply tokens = line.strip().split() if len(tokens) > 1: - if tokens[1] == 'running': + if tokens[1] == "running": vms.append(tokens[0]) return vms def list_inactive_vms(cwd=None): - ''' + """ Return a list of machine names for inactive virtual machine on the host, which are defined in the Vagrantfile at the indicated path. @@ -257,21 +262,21 @@ def list_inactive_vms(cwd=None): .. code-block:: bash salt '*' virt.list_inactive_vms cwd=/projects/project_1 - ''' + """ vms = [] - cmd = 'vagrant status' - reply = __salt__['cmd.shell'](cmd, cwd=cwd) - log.info('--->\n%s', reply) - for line in reply.split('\n'): # build a list of the text reply + cmd = "vagrant status" + reply = __salt__["cmd.shell"](cmd, cwd=cwd) + log.info("--->\n%s", reply) + for line in reply.split("\n"): # build a list of the text reply tokens = line.strip().split() - if len(tokens) > 1 and tokens[-1].endswith(')'): - if tokens[1] != 'running': + if len(tokens) > 1 and tokens[-1].endswith(")"): + if tokens[1] != "running": vms.append(tokens[0]) return vms -def vm_state(name='', cwd=None): - ''' +def vm_state(name="", cwd=None): + """ Return list of information for all the vms indicating their state. If you pass a VM name in as an argument then it will return info @@ -299,46 +304,49 @@ def vm_state(name='', cwd=None): parameter blank, then you may receive the status of all of them. Please specify the actual machine name for each VM if there are more than one. - ''' + """ if name: vm_ = get_vm_info(name) - machine = vm_['machine'] - cwd = vm_['cwd'] or cwd # usually ignore passed-in cwd + machine = vm_["machine"] + cwd = vm_["cwd"] or cwd # usually ignore passed-in cwd else: if not cwd: raise SaltInvocationError( - 'Path to Vagranfile must be defined, but cwd={}'.format(cwd)) - machine = '' + "Path to Vagranfile must be defined, but cwd={}".format(cwd) + ) + machine = "" info = [] - cmd = 'vagrant status {}'.format(machine) - reply = __salt__['cmd.shell'](cmd, cwd) - log.info('--->\n%s', reply) - for line in reply.split('\n'): # build a list of the text reply + cmd = "vagrant status {}".format(machine) + reply = __salt__["cmd.shell"](cmd, cwd) + log.info("--->\n%s", reply) + for line in reply.split("\n"): # build a list of the text reply tokens = line.strip().split() - if len(tokens) > 1 and tokens[-1].endswith(')'): + if len(tokens) > 1 and tokens[-1].endswith(")"): try: - datum = {'machine': tokens[0], - 'state': ' '.join(tokens[1:-1]), - 'provider': tokens[-1].lstrip('(').rstrip(')'), - 'name': get_machine_id(tokens[0], cwd) - } + datum = { + "machine": tokens[0], + "state": " ".join(tokens[1:-1]), + "provider": tokens[-1].lstrip("(").rstrip(")"), + "name": get_machine_id(tokens[0], cwd), + } info.append(datum) except IndexError: pass return info -def init(name, # Salt_id for created VM - cwd=None, # path to find Vagrantfile - machine='', # name of machine in Vagrantfile - runas=None, # username who owns Vagrant box - start=False, # start the machine when initialized - vagrant_provider='', # vagrant provider (default=virtualbox) - vm=None, # a dictionary of VM configuration settings - ): - ''' +def init( + name, # Salt_id for created VM + cwd=None, # path to find Vagrantfile + machine="", # name of machine in Vagrantfile + runas=None, # username who owns Vagrant box + start=False, # start the machine when initialized + vagrant_provider="", # vagrant provider (default=virtualbox) + vm=None, # a dictionary of VM configuration settings +): + """ Initialize a new Vagrant VM. This inputs all the information needed to start a Vagrant VM. These settings are stored in @@ -360,28 +368,30 @@ def init(name, # Salt_id for created VM salt <host> vagrant.init <salt_id> /path/to/Vagrantfile salt my_laptop vagrant.init x1 /projects/bevy_master machine=quail1 - ''' + """ vm_ = {} if vm is None else vm.copy() # passed configuration data - vm_['name'] = name + vm_["name"] = name # passed-in keyword arguments overwrite vm dictionary values - vm_['cwd'] = cwd or vm_.get('cwd') - if not vm_['cwd']: - raise SaltInvocationError('Path to Vagrantfile must be defined by "cwd" argument') - vm_['machine'] = machine or vm_.get('machine', machine) - vm_['runas'] = runas or vm_.get('runas', runas) - vm_['vagrant_provider'] = vagrant_provider or vm_.get('vagrant_provider', '') + vm_["cwd"] = cwd or vm_.get("cwd") + if not vm_["cwd"]: + raise SaltInvocationError( + 'Path to Vagrantfile must be defined by "cwd" argument' + ) + vm_["machine"] = machine or vm_.get("machine", machine) + vm_["runas"] = runas or vm_.get("runas", runas) + vm_["vagrant_provider"] = vagrant_provider or vm_.get("vagrant_provider", "") _update_vm_info(name, vm_) if start: - log.debug('Starting VM %s', name) + log.debug("Starting VM %s", name) ret = _start(name, vm_) else: - ret = 'Name {} defined using VM {}'.format(name, vm_['machine'] or '(default)') + ret = "Name {} defined using VM {}".format(name, vm_["machine"] or "(default)") return ret def start(name): - ''' + """ Start (vagrant up) a virtual machine defined by salt_id name. The machine must have been previously defined using "vagrant.init". @@ -390,38 +400,44 @@ def start(name): .. code-block:: bash salt <host> vagrant.start <salt_id> - ''' + """ vm_ = get_vm_info(name) return _start(name, vm_) -def _start(name, vm_): # internal call name, because "start" is a keyword argument to vagrant.init +def _start( + name, vm_ +): # internal call name, because "start" is a keyword argument to vagrant.init try: - machine = vm_['machine'] + machine = vm_["machine"] except KeyError: - raise SaltInvocationError('No Vagrant machine defined for Salt_id {}'.format(name)) + raise SaltInvocationError( + "No Vagrant machine defined for Salt_id {}".format(name) + ) - vagrant_provider = vm_.get('vagrant_provider', '') - provider_ = '--provider={}'.format(vagrant_provider) if vagrant_provider else '' - cmd = 'vagrant up {} {}'.format(machine, provider_) - ret = __salt__['cmd.run_all'](cmd, runas=vm_.get('runas'), cwd=vm_.get('cwd'), output_loglevel='info') + vagrant_provider = vm_.get("vagrant_provider", "") + provider_ = "--provider={}".format(vagrant_provider) if vagrant_provider else "" + cmd = "vagrant up {} {}".format(machine, provider_) + ret = __salt__["cmd.run_all"]( + cmd, runas=vm_.get("runas"), cwd=vm_.get("cwd"), output_loglevel="info" + ) - if machine == '': # we were called using the default machine - for line in ret['stdout'].split('\n'): # find its actual Vagrant name - if line.startswith('==>'): - machine = line.split()[1].rstrip(':') - vm_['machine'] = machine + if machine == "": # we were called using the default machine + for line in ret["stdout"].split("\n"): # find its actual Vagrant name + if line.startswith("==>"): + machine = line.split()[1].rstrip(":") + vm_["machine"] = machine _update_vm_info(name, vm_) # and remember the true name break - if ret['retcode'] == 0: + if ret["retcode"] == 0: return 'Started "{}" using Vagrant machine "{}".'.format(name, machine) return False def shutdown(name): - ''' + """ Send a soft shutdown (vagrant halt) signal to the named vm. This does the same thing as vagrant.stop. Other-VM control @@ -433,12 +449,12 @@ def shutdown(name): .. code-block:: bash salt <host> vagrant.shutdown <salt_id> - ''' + """ return stop(name) def stop(name): - ''' + """ Hard shutdown the virtual machine. (vagrant halt) CLI Example: @@ -446,19 +462,17 @@ def stop(name): .. code-block:: bash salt <host> vagrant.stop <salt_id> - ''' + """ vm_ = get_vm_info(name) - machine = vm_['machine'] + machine = vm_["machine"] - cmd = 'vagrant halt {}'.format(machine) - ret = __salt__['cmd.retcode'](cmd, - runas=vm_.get('runas'), - cwd=vm_.get('cwd')) + cmd = "vagrant halt {}".format(machine) + ret = __salt__["cmd.retcode"](cmd, runas=vm_.get("runas"), cwd=vm_.get("cwd")) return ret == 0 def pause(name): - ''' + """ Pause (vagrant suspend) the named VM. CLI Example: @@ -466,19 +480,17 @@ def pause(name): .. code-block:: bash salt <host> vagrant.pause <salt_id> - ''' + """ vm_ = get_vm_info(name) - machine = vm_['machine'] + machine = vm_["machine"] - cmd = 'vagrant suspend {}'.format(machine) - ret = __salt__['cmd.retcode'](cmd, - runas=vm_.get('runas'), - cwd=vm_.get('cwd')) + cmd = "vagrant suspend {}".format(machine) + ret = __salt__["cmd.retcode"](cmd, runas=vm_.get("runas"), cwd=vm_.get("cwd")) return ret == 0 def reboot(name, provision=False): - ''' + """ Reboot a VM. (vagrant reload) CLI Example: @@ -489,20 +501,18 @@ def reboot(name, provision=False): :param name: The salt_id name you will use to control this VM :param provision: (False) also re-run the Vagrant provisioning scripts. - ''' + """ vm_ = get_vm_info(name) - machine = vm_['machine'] - prov = '--provision' if provision else '' + machine = vm_["machine"] + prov = "--provision" if provision else "" - cmd = 'vagrant reload {} {}'.format(machine, prov) - ret = __salt__['cmd.retcode'](cmd, - runas=vm_.get('runas'), - cwd=vm_.get('cwd')) + cmd = "vagrant reload {} {}".format(machine, prov) + ret = __salt__["cmd.retcode"](cmd, runas=vm_.get("runas"), cwd=vm_.get("cwd")) return ret == 0 def destroy(name): - ''' + """ Destroy and delete a virtual machine. (vagrant destroy -f) This also removes the salt_id name defined by vagrant.init. @@ -512,24 +522,23 @@ def destroy(name): .. code-block:: bash salt <host> vagrant.destroy <salt_id> - ''' + """ vm_ = get_vm_info(name) - machine = vm_['machine'] + machine = vm_["machine"] - cmd = 'vagrant destroy -f {}'.format(machine) + cmd = "vagrant destroy -f {}".format(machine) - ret = __salt__['cmd.run_all'](cmd, - runas=vm_.get('runas'), - cwd=vm_.get('cwd'), - output_loglevel='info') - if ret['retcode'] == 0: + ret = __salt__["cmd.run_all"]( + cmd, runas=vm_.get("runas"), cwd=vm_.get("cwd"), output_loglevel="info" + ) + if ret["retcode"] == 0: _erase_vm_info(name) - return 'Destroyed VM {0}'.format(name) + return "Destroyed VM {0}".format(name) return False -def get_ssh_config(name, network_mask='', get_private_key=False): - r''' +def get_ssh_config(name, network_mask="", get_private_key=False): + r""" Retrieve hints of how you might connect to a Vagrant VM. :param name: the salt_id of the machine @@ -567,63 +576,74 @@ def get_ssh_config(name, network_mask='', get_private_key=False): If you enter a CIDR network mask, Salt will attempt to find the VM's address for you. The host machine will send an "ifconfig" command to the VM (using ssh to `ssh_host`:`ssh_port`) and return the IP address of the first interface it can find which matches your mask. - ''' + """ vm_ = get_vm_info(name) ssh_config = _vagrant_ssh_config(vm_) try: - ans = {'key_filename': ssh_config['IdentityFile'], - 'ssh_username': ssh_config['User'], - 'ssh_host': ssh_config['HostName'], - 'ssh_port': ssh_config['Port'], - } + ans = { + "key_filename": ssh_config["IdentityFile"], + "ssh_username": ssh_config["User"], + "ssh_host": ssh_config["HostName"], + "ssh_port": ssh_config["Port"], + } except KeyError: raise CommandExecutionError( - 'Insufficient SSH information to contact VM {}. ' - 'Is it running?'.format(vm_.get('machine', '(default)'))) + "Insufficient SSH information to contact VM {}. " + "Is it running?".format(vm_.get("machine", "(default)")) + ) if network_mask: # ask the new VM to report its network address - command = 'ssh -i {IdentityFile} -p {Port} ' \ - '-oStrictHostKeyChecking={StrictHostKeyChecking} ' \ - '-oUserKnownHostsFile={UserKnownHostsFile} ' \ - '-oControlPath=none ' \ - '{User}@{HostName} ifconfig'.format(**ssh_config) + command = ( + "ssh -i {IdentityFile} -p {Port} " + "-oStrictHostKeyChecking={StrictHostKeyChecking} " + "-oUserKnownHostsFile={UserKnownHostsFile} " + "-oControlPath=none " + "{User}@{HostName} ifconfig".format(**ssh_config) + ) - log.info('Trying ssh -p {Port} {User}@{HostName} ifconfig'.format(**ssh_config)) - reply = __salt__['cmd.shell'](command) - log.info('--->\n%s', reply) + log.info("Trying ssh -p {Port} {User}@{HostName} ifconfig".format(**ssh_config)) + reply = __salt__["cmd.shell"](command) + log.info("--->\n%s", reply) target_network_range = ipaddress.ip_network(network_mask, strict=False) - for line in reply.split('\n'): + for line in reply.split("\n"): try: # try to find a bridged network address # the lines we are looking for appear like: # "inet addr:10.124.31.185 Bcast:10.124.31.255 Mask:255.255.248.0" # or "inet6 addr: fe80::a00:27ff:fe04:7aac/64 Scope:Link" - tokens = line.replace('addr:', '', 1).split() # remove "addr:" if it exists, then split + tokens = line.replace( + "addr:", "", 1 + ).split() # remove "addr:" if it exists, then split found_address = None if "inet" in tokens: nxt = tokens.index("inet") + 1 found_address = ipaddress.ip_address(tokens[nxt]) elif "inet6" in tokens: nxt = tokens.index("inet6") + 1 - found_address = ipaddress.ip_address(tokens[nxt].split('/')[0]) + found_address = ipaddress.ip_address(tokens[nxt].split("/")[0]) if found_address in target_network_range: - ans['ip_address'] = six.text_type(found_address) + ans["ip_address"] = six.text_type(found_address) break # we have located a good matching address except (IndexError, AttributeError, TypeError): pass # all syntax and type errors loop here # falling out if the loop leaves us remembering the last candidate - log.info('Network IP address in %s detected as: %s', - target_network_range, ans.get('ip_address', '(not found)')) + log.info( + "Network IP address in %s detected as: %s", + target_network_range, + ans.get("ip_address", "(not found)"), + ) if get_private_key: # retrieve the Vagrant private key from the host try: - with salt.utils.files.fopen(ssh_config['IdentityFile']) as pks: - ans['private_key'] = salt.utils.stringutils.to_unicode(pks.read()) + with salt.utils.files.fopen(ssh_config["IdentityFile"]) as pks: + ans["private_key"] = salt.utils.stringutils.to_unicode(pks.read()) except (OSError, IOError) as e: - raise CommandExecutionError("Error processing Vagrant private key file: {}".format(e)) + raise CommandExecutionError( + "Error processing Vagrant private key file: {}".format(e) + ) return ans diff --git a/salt/modules/varnish.py b/salt/modules/varnish.py index 2505481736f..5ee0e722883 100644 --- a/salt/modules/varnish.py +++ b/salt/modules/varnish.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for Varnish .. versionadded:: 2014.7.0 @@ -8,34 +8,38 @@ Support for Varnish These functions are designed to work with all implementations of Varnish from 3.x onwards -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging import re +import salt.utils.path + # Import salt libs from salt.ext import six -import salt.utils.path log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'varnish' +__virtualname__ = "varnish" def __virtual__(): - ''' + """ Only load the module if varnish is installed - ''' - if salt.utils.path.which('varnishd') and salt.utils.path.which('varnishadm'): + """ + if salt.utils.path.which("varnishd") and salt.utils.path.which("varnishadm"): return __virtualname__ - return (False, 'The varnish execution module failed to load: either varnishd or varnishadm is not in the path.') + return ( + False, + "The varnish execution module failed to load: either varnishd or varnishadm is not in the path.", + ) def _run_varnishadm(cmd, params=(), **kwargs): - ''' + """ Execute varnishadm command return the output of the command @@ -47,15 +51,15 @@ def _run_varnishadm(cmd, params=(), **kwargs): kwargs Additional options to pass to the salt cmd.run_all function - ''' - cmd = ['varnishadm', cmd] + """ + cmd = ["varnishadm", cmd] cmd.extend([param for param in params if param is not None]) - log.debug('Executing: %s', ' '.join(cmd)) - return __salt__['cmd.run_all'](cmd, python_shell=False, **kwargs) + log.debug("Executing: %s", " ".join(cmd)) + return __salt__["cmd.run_all"](cmd, python_shell=False, **kwargs) def version(): - ''' + """ Return server version from varnishd -V CLI Example: @@ -63,15 +67,15 @@ def version(): .. code-block:: bash salt '*' varnish.version - ''' - cmd = ['varnishd', '-V'] - out = __salt__['cmd.run'](cmd, python_shell=False) - ret = re.search(r'\(varnish-([^\)]+)\)', out).group(1) + """ + cmd = ["varnishd", "-V"] + out = __salt__["cmd.run"](cmd, python_shell=False) + ret = re.search(r"\(varnish-([^\)]+)\)", out).group(1) return ret def ban(ban_expression): - ''' + """ Add ban to the varnish cache CLI Example: @@ -79,12 +83,12 @@ def ban(ban_expression): .. code-block:: bash salt '*' varnish.ban ban_expression - ''' - return _run_varnishadm('ban', [ban_expression])['retcode'] == 0 + """ + return _run_varnishadm("ban", [ban_expression])["retcode"] == 0 def ban_list(): - ''' + """ List varnish cache current bans CLI Example: @@ -92,16 +96,16 @@ def ban_list(): .. code-block:: bash salt '*' varnish.ban_list - ''' - ret = _run_varnishadm('ban.list') - if ret['retcode']: + """ + ret = _run_varnishadm("ban.list") + if ret["retcode"]: return False else: - return ret['stdout'].split('\n')[1:] + return ret["stdout"].split("\n")[1:] def purge(): - ''' + """ Purge the varnish cache CLI Example: @@ -109,12 +113,12 @@ def purge(): .. code-block:: bash salt '*' varnish.purge - ''' - return ban('req.url ~ .') + """ + return ban("req.url ~ .") def param_set(param, value): - ''' + """ Set a param in varnish cache CLI Example: @@ -122,12 +126,12 @@ def param_set(param, value): .. code-block:: bash salt '*' varnish.param_set param value - ''' - return _run_varnishadm('param.set', [param, six.text_type(value)])['retcode'] == 0 + """ + return _run_varnishadm("param.set", [param, six.text_type(value)])["retcode"] == 0 def param_show(param=None): - ''' + """ Show params of varnish cache CLI Example: @@ -135,14 +139,14 @@ def param_show(param=None): .. code-block:: bash salt '*' varnish.param_show param - ''' - ret = _run_varnishadm('param.show', [param]) - if ret['retcode']: + """ + ret = _run_varnishadm("param.show", [param]) + if ret["retcode"]: return False else: result = {} - for line in ret['stdout'].split('\n'): - m = re.search(r'^(\w+)\s+(.*)$', line) + for line in ret["stdout"].split("\n"): + m = re.search(r"^(\w+)\s+(.*)$", line) result[m.group(1)] = m.group(2) if param: # When we ask to varnishadm for a specific param, it gives full diff --git a/salt/modules/vault.py b/salt/modules/vault.py index f7b85eea034..791d5ac871b 100644 --- a/salt/modules/vault.py +++ b/salt/modules/vault.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Functions to interact with Hashicorp Vault. :maintainer: SaltStack @@ -57,6 +57,7 @@ Functions to interact with Hashicorp Vault. For details please see: https://www.vaultproject.io/api/auth/token/index.html#create-token + Example configuration: https://www.nomadproject.io/docs/vault-integration/index.html#vault-token-role-configuration @@ -70,7 +71,6 @@ Functions to interact with Hashicorp Vault. The token must be able to create tokens with the policies that should be assigned to minions. - You can still use the token auth via a OS environment variable via this config example: @@ -92,10 +92,9 @@ Functions to interact with Hashicorp Vault. policies Policies that are assigned to minions when requesting a token. These can - either be static, eg saltstack/minions, or templated, eg - ``saltstack/minion/{minion}``. ``{minion}`` is shorthand for grains[id]. - Grains are also available, for example like this: - ``my-policies/{grains[os]}`` + either be static, eg saltstack/minions, or templated with grain values, + eg, ``my-policies/{grains[os]}``. ``{minion}`` is shorthand for grains[id], + ``saltstack/minion/{minion}``. . If a template contains a grain which evaluates to a list, it will be expanded into multiple policies. For example, given the template @@ -109,14 +108,18 @@ Functions to interact with Hashicorp Vault. - database The minion will have the policies ``saltstack/by-role/web`` and - ``saltstack/by-role/database``. Note however that list members which do - not have simple string representations, such as dictionaries or objects, - do not work and will throw an exception. Strings and numbers are - examples of types which work well. + ``saltstack/by-role/database``. Optional. If policies is not configured, ``saltstack/minions`` and ``saltstack/{minion}`` are used as defaults. + .. note:: + + list members which do not have simple string representations, + such as dictionaries or objects, do not work and will + throw an exception. Strings and numbers are examples of + types which work well. + keys List of keys to use to unseal vault server with the vault.unseal runner. @@ -131,50 +134,68 @@ Functions to interact with Hashicorp Vault. - vault.generate_token .. _vault-setup: -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging +import logging log = logging.getLogger(__name__) -def read_secret(path, key=None): - ''' +def read_secret(path, key=None, metadata=False): + """ Return the value of key at path in vault, or entire secret + :param metadata: Optional - If using KV v2 backend, display full results, including metadata + + .. versionadded:: Sodium + Jinja Example: .. code-block:: jinja my-secret: {{ salt['vault'].read_secret('secret/my/secret', 'some-key') }} + {{ salt['vault'].read_secret('/secret/my/secret', 'some-key', metadata=True)['data'] }} + .. code-block:: jinja {% set supersecret = salt['vault'].read_secret('secret/my/secret') %} secrets: first: {{ supersecret.first }} second: {{ supersecret.second }} - ''' - log.debug('Reading Vault secret for %s at %s', __grains__['id'], path) + """ + version2 = __utils__["vault.is_v2"](path) + if version2["v2"]: + path = version2["data"] + log.debug("Reading Vault secret for %s at %s", __grains__["id"], path) try: - url = 'v1/{0}'.format(path) - response = __utils__['vault.make_request']('GET', url) + url = "v1/{0}".format(path) + response = __utils__["vault.make_request"]("GET", url) if response.status_code != 200: response.raise_for_status() - data = response.json()['data'] + data = response.json()["data"] + # Return data of subkey if requested if key is not None: - return data[key] + if version2["v2"]: + return data["data"][key] + else: + return data[key] + # Just return data from KV V2 if metadata isn't needed + if version2["v2"]: + if not metadata: + return data["data"] + return data except Exception as err: # pylint: disable=broad-except - log.error('Failed to read secret! %s: %s', type(err).__name__, err) + log.error("Failed to read secret! %s: %s", type(err).__name__, err) return None def write_secret(path, **kwargs): - ''' + """ Set secret at the path in vault. The vault policy used must allow this. CLI Example: @@ -182,24 +203,28 @@ def write_secret(path, **kwargs): .. code-block:: bash salt '*' vault.write_secret "secret/my/secret" user="foo" password="bar" - ''' - log.debug('Writing vault secrets for %s at %s', __grains__['id'], path) - data = dict([(x, y) for x, y in kwargs.items() if not x.startswith('__')]) + """ + log.debug("Writing vault secrets for %s at %s", __grains__["id"], path) + data = dict([(x, y) for x, y in kwargs.items() if not x.startswith("__")]) + version2 = __utils__["vault.is_v2"](path) + if version2["v2"]: + path = version2["data"] + data = {"data": data} try: - url = 'v1/{0}'.format(path) - response = __utils__['vault.make_request']('POST', url, json=data) + url = "v1/{0}".format(path) + response = __utils__["vault.make_request"]("POST", url, json=data) if response.status_code == 200: - return response.json()['data'] + return response.json()["data"] elif response.status_code != 204: response.raise_for_status() return True except Exception as err: # pylint: disable=broad-except - log.error('Failed to write secret! %s: %s', type(err).__name__, err) + log.error("Failed to write secret! %s: %s", type(err).__name__, err) return False def write_raw(path, raw): - ''' + """ Set raw data at the path in vault. The vault policy used must allow this. CLI Example: @@ -207,23 +232,27 @@ def write_raw(path, raw): .. code-block:: bash salt '*' vault.write_raw "secret/my/secret" '{"user":"foo","password": "bar"}' - ''' - log.debug('Writing vault secrets for %s at %s', __grains__['id'], path) + """ + log.debug("Writing vault secrets for %s at %s", __grains__["id"], path) + version2 = __utils__["vault.is_v2"](path) + if version2["v2"]: + path = version2["data"] + raw = {"data": raw} try: - url = 'v1/{0}'.format(path) - response = __utils__['vault.make_request']('POST', url, json=raw) + url = "v1/{0}".format(path) + response = __utils__["vault.make_request"]("POST", url, json=raw) if response.status_code == 200: - return response.json()['data'] + return response.json()["data"] elif response.status_code != 204: response.raise_for_status() return True except Exception as err: # pylint: disable=broad-except - log.error('Failed to write secret! %s: %s', type(err).__name__, err) + log.error("Failed to write secret! %s: %s", type(err).__name__, err) return False def delete_secret(path): - ''' + """ Delete secret at the path in vault. The vault policy used must allow this. CLI Example: @@ -231,21 +260,56 @@ def delete_secret(path): .. code-block:: bash salt '*' vault.delete_secret "secret/my/secret" - ''' - log.debug('Deleting vault secrets for %s in %s', __grains__['id'], path) + """ + log.debug("Deleting vault secrets for %s in %s", __grains__["id"], path) + version2 = __utils__["vault.is_v2"](path) + if version2["v2"]: + path = version2["data"] try: - url = 'v1/{0}'.format(path) - response = __utils__['vault.make_request']('DELETE', url) + url = "v1/{0}".format(path) + response = __utils__["vault.make_request"]("DELETE", url) if response.status_code != 204: response.raise_for_status() return True except Exception as err: # pylint: disable=broad-except - log.error('Failed to delete secret! %s: %s', type(err).__name__, err) + log.error("Failed to delete secret! %s: %s", type(err).__name__, err) + return False + + +def destroy_secret(path, *args): + """ + Destory specified secret version at the path in vault. The vault policy + used must allow this. Only supported on Vault KV version 2 + + .. versionadded:: Sodium + + CLI Example: + + .. code-block:: bash + + salt '*' vault.destroy_secret "secret/my/secret" 1 2 + """ + log.debug("Destroying vault secrets for %s in %s", __grains__["id"], path) + data = {"versions": list(args)} + version2 = __utils__["vault.is_v2"](path) + if version2["v2"]: + path = version2["destroy"] + else: + log.error("Destroy operation is only supported on KV version 2") + return False + try: + url = "v1/{0}".format(path) + response = __utils__["vault.make_request"]("POST", url, json=data) + if response.status_code != 204: + response.raise_for_status() + return True + except Exception as err: # pylint: disable=broad-except + log.error("Failed to delete secret! %s: %s", type(err).__name__, err) return False def list_secrets(path): - ''' + """ List secret keys at the path in vault. The vault policy used must allow this. The path should end with a trailing slash. @@ -254,14 +318,17 @@ def list_secrets(path): .. code-block:: bash salt '*' vault.list_secrets "secret/my/" - ''' - log.debug('Listing vault secret keys for %s in %s', __grains__['id'], path) + """ + log.debug("Listing vault secret keys for %s in %s", __grains__["id"], path) + version2 = __utils__["vault.is_v2"](path) + if version2["v2"]: + path = version2["metadata"] try: - url = 'v1/{0}'.format(path) - response = __utils__['vault.make_request']('LIST', url) + url = "v1/{0}".format(path) + response = __utils__["vault.make_request"]("LIST", url) if response.status_code != 200: response.raise_for_status() - return response.json()['data'] + return response.json()["data"] except Exception as err: # pylint: disable=broad-except - log.error('Failed to list secrets! %s: %s', type(err).__name__, err) + log.error("Failed to list secrets! %s: %s", type(err).__name__, err) return None diff --git a/salt/modules/vbox_guest.py b/salt/modules/vbox_guest.py index e5215db0c7b..5058a8e1b16 100644 --- a/salt/modules/vbox_guest.py +++ b/salt/modules/vbox_guest.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" VirtualBox Guest Additions installer -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -16,24 +16,26 @@ import tempfile # Import Salt libs from salt.ext import six - log = logging.getLogger(__name__) -__virtualname__ = 'vbox_guest' -_additions_dir_prefix = 'VBoxGuestAdditions' -_shared_folders_group = 'vboxsf' +__virtualname__ = "vbox_guest" +_additions_dir_prefix = "VBoxGuestAdditions" +_shared_folders_group = "vboxsf" def __virtual__(): - ''' + """ Set the vbox_guest module if the OS Linux - ''' - if __grains__.get('kernel', '') not in ('Linux', ): - return (False, 'The vbox_guest execution module failed to load: only available on Linux systems.') + """ + if __grains__.get("kernel", "") not in ("Linux",): + return ( + False, + "The vbox_guest execution module failed to load: only available on Linux systems.", + ) return __virtualname__ def additions_mount(): - ''' + """ Mount VirtualBox Guest Additions CD to the temp directory. To connect VirtualBox Guest Additions via VirtualBox graphical interface @@ -46,9 +48,9 @@ def additions_mount(): salt '*' vbox_guest.additions_mount :return: True or OSError exception - ''' + """ mount_point = tempfile.mkdtemp() - ret = __salt__['mount.mount'](mount_point, '/dev/cdrom') + ret = __salt__["mount.mount"](mount_point, "/dev/cdrom") if ret is True: return mount_point else: @@ -56,7 +58,7 @@ def additions_mount(): def additions_umount(mount_point): - ''' + """ Unmount VirtualBox Guest Additions CD from the temp directory. CLI Example: @@ -67,8 +69,8 @@ def additions_umount(mount_point): :param mount_point: directory VirtualBox Guest Additions is mounted to :return: True or an string with error - ''' - ret = __salt__['mount.umount'](mount_point) + """ + ret = __salt__["mount.umount"](mount_point) if ret: os.rmdir(mount_point) return ret @@ -88,53 +90,56 @@ def _return_mount_error(f): return f(*args, **kwargs) except OSError as e: return six.text_type(e) + return wrapper def _additions_install_program_path(mount_point): - return os.path.join(mount_point, { - 'Linux': 'VBoxLinuxAdditions.run', - 'Solaris': 'VBoxSolarisAdditions.pkg', - 'Windows': 'VBoxWindowsAdditions.exe' - }[__grains__.get('kernel', '')]) + return os.path.join( + mount_point, + { + "Linux": "VBoxLinuxAdditions.run", + "Solaris": "VBoxSolarisAdditions.pkg", + "Windows": "VBoxWindowsAdditions.exe", + }[__grains__.get("kernel", "")], + ) def _additions_install_opensuse(**kwargs): - kernel_type = re.sub( - r'^(\d|\.|-)*', '', __grains__.get('kernelrelease', '')) - kernel_devel = 'kernel-{0}-devel'.format(kernel_type) - return __states__['pkg.installed'](None, pkgs=['make', 'gcc', kernel_devel]) + kernel_type = re.sub(r"^(\d|\.|-)*", "", __grains__.get("kernelrelease", "")) + kernel_devel = "kernel-{0}-devel".format(kernel_type) + return __states__["pkg.installed"](None, pkgs=["make", "gcc", kernel_devel]) def _additions_install_ubuntu(**kwargs): - return __states__['pkg.installed'](None, pkgs=['dkms', ]) + return __states__["pkg.installed"](None, pkgs=["dkms"]) def _additions_install_fedora(**kwargs): - return __states__['pkg.installed'](None, pkgs=['dkms', 'gcc']) + return __states__["pkg.installed"](None, pkgs=["dkms", "gcc"]) def _additions_install_linux(mount_point, **kwargs): - reboot = kwargs.pop('reboot', False) - restart_x11 = kwargs.pop('restart_x11', False) - upgrade_os = kwargs.pop('upgrade_os', False) + reboot = kwargs.pop("reboot", False) + restart_x11 = kwargs.pop("restart_x11", False) + upgrade_os = kwargs.pop("upgrade_os", False) if upgrade_os: - __salt__['pkg.upgrade']() + __salt__["pkg.upgrade"]() # dangerous: do not call variable `os` as it will hide os module - guest_os = __grains__.get('os', '') - if guest_os == 'openSUSE': + guest_os = __grains__.get("os", "") + if guest_os == "openSUSE": _additions_install_opensuse(**kwargs) - elif guest_os == 'ubuntu': + elif guest_os == "ubuntu": _additions_install_ubuntu(**kwargs) - elif guest_os == 'fedora': + elif guest_os == "fedora": _additions_install_fedora(**kwargs) else: log.warning("%s is not fully supported yet.", guest_os) installer_path = _additions_install_program_path(mount_point) - installer_ret = __salt__['cmd.run_all'](installer_path) - if installer_ret['retcode'] in (0, 1): + installer_ret = __salt__["cmd.run_all"](installer_path) + if installer_ret["retcode"] in (0, 1): if reboot: - __salt__['system.reboot']() + __salt__["system.reboot"]() elif restart_x11: raise NotImplementedError("Restarting x11 is not supported yet.") else: @@ -144,17 +149,20 @@ def _additions_install_linux(mount_point, **kwargs): # __salt__['service.start'](service) pass return additions_version() - elif installer_ret['retcode'] in (127, '127'): - return ("'{0}' not found on CD. Make sure that VirtualBox Guest " - "Additions CD is attached to the CD IDE Controller.".format( - os.path.basename(installer_path))) + elif installer_ret["retcode"] in (127, "127"): + return ( + "'{0}' not found on CD. Make sure that VirtualBox Guest " + "Additions CD is attached to the CD IDE Controller.".format( + os.path.basename(installer_path) + ) + ) else: - return installer_ret['stderr'] + return installer_ret["stderr"] @_return_mount_error def additions_install(**kwargs): - ''' + """ Install VirtualBox Guest Additions. Uses the CD, connected by VirtualBox. To connect VirtualBox Guest Additions via VirtualBox graphical interface @@ -175,60 +183,64 @@ def additions_install(**kwargs): :param upgrade_os: upgrade OS (to ensure the latests version of kernel and developer tools are installed) :type upgrade_os: bool :return: version of VirtualBox Guest Additions or string with error - ''' + """ with _additions_mounted() as mount_point: - kernel = __grains__.get('kernel', '') - if kernel == 'Linux': + kernel = __grains__.get("kernel", "") + if kernel == "Linux": return _additions_install_linux(mount_point, **kwargs) def _additions_dir(): - root = '/opt' - dirs = glob.glob(os.path.join(root, _additions_dir_prefix) + '*') + root = "/opt" + dirs = glob.glob(os.path.join(root, _additions_dir_prefix) + "*") if dirs: return dirs[0] else: - raise EnvironmentError('No VirtualBox Guest Additions dirs found!') + raise EnvironmentError("No VirtualBox Guest Additions dirs found!") def _additions_remove_linux_run(cmd): - uninstaller_ret = __salt__['cmd.run_all'](cmd) - return uninstaller_ret['retcode'] in (0, ) + uninstaller_ret = __salt__["cmd.run_all"](cmd) + return uninstaller_ret["retcode"] in (0,) def _additions_remove_linux(**kwargs): try: return _additions_remove_linux_run( - os.path.join(_additions_dir(), 'uninstall.sh')) + os.path.join(_additions_dir(), "uninstall.sh") + ) except EnvironmentError: return False def _additions_remove_linux_use_cd(mount_point, **kwargs): - force = kwargs.pop('force', False) - args = '' + force = kwargs.pop("force", False) + args = "" if force: - args += '--force' - return _additions_remove_linux_run('{program} uninstall {args}'.format( - program=_additions_install_program_path(mount_point), args=args)) + args += "--force" + return _additions_remove_linux_run( + "{program} uninstall {args}".format( + program=_additions_install_program_path(mount_point), args=args + ) + ) @_return_mount_error def _additions_remove_use_cd(**kwargs): - ''' + """ Remove VirtualBox Guest Additions. It uses the CD, connected by VirtualBox. - ''' + """ with _additions_mounted() as mount_point: - kernel = __grains__.get('kernel', '') - if kernel == 'Linux': + kernel = __grains__.get("kernel", "") + if kernel == "Linux": return _additions_remove_linux_use_cd(mount_point, **kwargs) def additions_remove(**kwargs): - ''' + """ Remove VirtualBox Guest Additions. Firstly it tries to uninstall itself by executing @@ -246,9 +258,9 @@ def additions_remove(**kwargs): :type force: bool :return: True if VirtualBox Guest Additions were removed successfully else False - ''' - kernel = __grains__.get('kernel', '') - if kernel == 'Linux': + """ + kernel = __grains__.get("kernel", "") + if kernel == "Linux": ret = _additions_remove_linux() if not ret: ret = _additions_remove_use_cd(**kwargs) @@ -256,7 +268,7 @@ def additions_remove(**kwargs): def additions_version(): - ''' + """ Check VirtualBox Guest Additions version. CLI Example: @@ -266,19 +278,18 @@ def additions_version(): salt '*' vbox_guest.additions_version :return: version of VirtualBox Guest Additions or False if they are not installed - ''' + """ try: d = _additions_dir() except EnvironmentError: return False if d and len(os.listdir(d)) > 0: - return re.sub(r'^{0}-'.format(_additions_dir_prefix), '', - os.path.basename(d)) + return re.sub(r"^{0}-".format(_additions_dir_prefix), "", os.path.basename(d)) return False def grant_access_to_shared_folders_to(name, users=None): - ''' + """ Grant access to auto-mounted shared folders to the users. User is specified by its name. To grant access for several users use argument `users`. @@ -298,17 +309,19 @@ def grant_access_to_shared_folders_to(name, users=None): :param users: list of names of users to grant access to auto-mounted shared folders to (if specified, `name` will not be taken into account) :type users: list of str :return: list of users who have access to auto-mounted shared folders - ''' + """ if users is None: users = [name] - if __salt__['group.members'](_shared_folders_group, ','.join(users)): + if __salt__["group.members"](_shared_folders_group, ",".join(users)): return users else: - if not __salt__['group.info'](_shared_folders_group): + if not __salt__["group.info"](_shared_folders_group): if not additions_version: - return ("VirtualBox Guest Additions are not installed. Ιnstall " - "them firstly. You can do it with the help of command " - "vbox_guest.additions_install.") + return ( + "VirtualBox Guest Additions are not installed. Ιnstall " + "them firstly. You can do it with the help of command " + "vbox_guest.additions_install." + ) else: return ( "VirtualBox Guest Additions seems to be installed, but " @@ -320,14 +333,16 @@ def grant_access_to_shared_folders_to(name, users=None): "it with care) and then install it again. You can do " "it with the help of :py:func:`vbox_guest.additions_install " "<salt.modules.vbox_guest.additions_install>`." - "".format(_shared_folders_group)) + "".format(_shared_folders_group) + ) else: - return ("Cannot replace members of the '{0}' group." - "".format(_shared_folders_group)) + return "Cannot replace members of the '{0}' group." "".format( + _shared_folders_group + ) def list_shared_folders_users(): - ''' + """ List users who have access to auto-mounted shared folders. See https://www.virtualbox.org/manual/ch04.html#sf_mount_auto for more details. @@ -339,8 +354,8 @@ def list_shared_folders_users(): salt '*' vbox_guest.list_shared_folders_users :return: list of users who have access to auto-mounted shared folders - ''' + """ try: - return __salt__['group.info'](_shared_folders_group)['members'] + return __salt__["group.info"](_shared_folders_group)["members"] except KeyError: return [] diff --git a/salt/modules/vboxmanage.py b/salt/modules/vboxmanage.py index f562ff09f49..6d3eae23478 100644 --- a/salt/modules/vboxmanage.py +++ b/salt/modules/vboxmanage.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for VirtualBox using the VBoxManage command .. versionadded:: 2016.3.0 @@ -14,42 +14,48 @@ load it by configuring ``autoload_vboxdrv`` in ``/etc/salt/minion``: The default for this setting is ``False``. :depends: virtualbox -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import re -import os.path + import logging +import os.path +import re # pylint: disable=import-error,no-name-in-module import salt.utils.files import salt.utils.path from salt.exceptions import CommandExecutionError -# pylint: enable=import-error,no-name-in-module # Import 3rd-party libs from salt.ext import six +# pylint: enable=import-error,no-name-in-module + + LOG = logging.getLogger(__name__) -UUID_RE = re.compile('[^{0}]'.format('a-zA-Z0-9._-')) -NAME_RE = re.compile('[^{0}]'.format('a-zA-Z0-9._-')) +UUID_RE = re.compile("[^{0}]".format("a-zA-Z0-9._-")) +NAME_RE = re.compile("[^{0}]".format("a-zA-Z0-9._-")) def __virtual__(): - ''' + """ Only load the module if VBoxManage is installed - ''' + """ if vboxcmd(): - if __opts__.get('autoload_vboxdrv', False) is True: - if not __salt__['kmod.is_loaded']('vboxdrv'): - __salt__['kmod.load']('vboxdrv') + if __opts__.get("autoload_vboxdrv", False) is True: + if not __salt__["kmod.is_loaded"]("vboxdrv"): + __salt__["kmod.load"]("vboxdrv") return True - return (False, 'The vboxmanaged execution module failed to load: VBoxManage is not installed.') + return ( + False, + "The vboxmanaged execution module failed to load: VBoxManage is not installed.", + ) def vboxcmd(): - ''' + """ Return the location of the VBoxManage command CLI Example: @@ -57,12 +63,12 @@ def vboxcmd(): .. code-block:: bash salt '*' vboxmanage.vboxcmd - ''' - return salt.utils.path.which('VBoxManage') + """ + return salt.utils.path.which("VBoxManage") def list_ostypes(): - ''' + """ List the available OS Types CLI Example: @@ -70,12 +76,12 @@ def list_ostypes(): .. code-block:: bash salt '*' vboxmanage.list_ostypes - ''' - return list_items('ostypes', True, 'ID') + """ + return list_items("ostypes", True, "ID") def list_nodes_min(): - ''' + """ Return a list of registered VMs, with minimal information CLI Example: @@ -83,20 +89,20 @@ def list_nodes_min(): .. code-block:: bash salt '*' vboxmanage.list_nodes_min - ''' + """ ret = {} - cmd = '{0} list vms'.format(vboxcmd()) + cmd = "{0} list vms".format(vboxcmd()) for line in salt.modules.cmdmod.run(cmd).splitlines(): if not line.strip(): continue comps = line.split() - name = comps[0].replace('"', '') + name = comps[0].replace('"', "") ret[name] = True return ret def list_nodes_full(): - ''' + """ Return a list of registered VMs, with detailed information CLI Example: @@ -104,12 +110,12 @@ def list_nodes_full(): .. code-block:: bash salt '*' vboxmanage.list_nodes_full - ''' - return list_items('vms', True, 'Name') + """ + return list_items("vms", True, "Name") def list_nodes(): - ''' + """ Return a list of registered VMs CLI Example: @@ -117,27 +123,26 @@ def list_nodes(): .. code-block:: bash salt '*' vboxmanage.list_nodes - ''' + """ ret = {} nodes = list_nodes_full() for node in nodes: ret[node] = { - 'id': nodes[node]['UUID'], - 'image': nodes[node]['Guest OS'], - 'name': nodes[node]['Name'], - 'state': None, - 'private_ips': [], - 'public_ips': [], + "id": nodes[node]["UUID"], + "image": nodes[node]["Guest OS"], + "name": nodes[node]["Name"], + "state": None, + "private_ips": [], + "public_ips": [], } - ret[node]['size'] = '{0} RAM, {1} CPU'.format( - nodes[node]['Memory size'], - nodes[node]['Number of CPUs'], + ret[node]["size"] = "{0} RAM, {1} CPU".format( + nodes[node]["Memory size"], nodes[node]["Number of CPUs"], ) return ret def start(name): - ''' + """ Start a VM CLI Example: @@ -145,15 +150,15 @@ def start(name): .. code-block:: bash salt '*' vboxmanage.start my_vm - ''' + """ ret = {} - cmd = '{0} startvm {1}'.format(vboxcmd(), name) + cmd = "{0} startvm {1}".format(vboxcmd(), name) ret = salt.modules.cmdmod.run(cmd).splitlines() return ret def stop(name): - ''' + """ Stop a VM CLI Example: @@ -161,14 +166,14 @@ def stop(name): .. code-block:: bash salt '*' vboxmanage.stop my_vm - ''' - cmd = '{0} controlvm {1} poweroff'.format(vboxcmd(), name) + """ + cmd = "{0} controlvm {1} poweroff".format(vboxcmd(), name) ret = salt.modules.cmdmod.run(cmd).splitlines() return ret def register(filename): - ''' + """ Register a VM CLI Example: @@ -176,21 +181,21 @@ def register(filename): .. code-block:: bash salt '*' vboxmanage.register my_vm_filename - ''' + """ if not os.path.isfile(filename): raise CommandExecutionError( - 'The specified filename ({0}) does not exist.'.format(filename) + "The specified filename ({0}) does not exist.".format(filename) ) - cmd = '{0} registervm {1}'.format(vboxcmd(), filename) + cmd = "{0} registervm {1}".format(vboxcmd(), filename) ret = salt.modules.cmdmod.run_all(cmd) - if ret['retcode'] == 0: + if ret["retcode"] == 0: return True - return ret['stderr'] + return ret["stderr"] def unregister(name, delete=False): - ''' + """ Unregister a VM CLI Example: @@ -198,24 +203,24 @@ def unregister(name, delete=False): .. code-block:: bash salt '*' vboxmanage.unregister my_vm_filename - ''' + """ nodes = list_nodes_min() if name not in nodes: raise CommandExecutionError( - 'The specified VM ({0}) is not registered.'.format(name) + "The specified VM ({0}) is not registered.".format(name) ) - cmd = '{0} unregistervm {1}'.format(vboxcmd(), name) + cmd = "{0} unregistervm {1}".format(vboxcmd(), name) if delete is True: - cmd += ' --delete' + cmd += " --delete" ret = salt.modules.cmdmod.run_all(cmd) - if ret['retcode'] == 0: + if ret["retcode"] == 0: return True - return ret['stderr'] + return ret["stderr"] def destroy(name): - ''' + """ Unregister and destroy a VM CLI Example: @@ -223,18 +228,20 @@ def destroy(name): .. code-block:: bash salt '*' vboxmanage.destroy my_vm - ''' + """ return unregister(name, True) -def create(name, - groups=None, - ostype=None, - register=True, - basefolder=None, - new_uuid=None, - **kwargs): - ''' +def create( + name, + groups=None, + ostype=None, + register=True, + basefolder=None, + new_uuid=None, + **kwargs +): + """ Create a new VM CLI Example: @@ -242,71 +249,75 @@ def create(name, .. code-block:: bash salt 'hypervisor' vboxmanage.create <name> - ''' + """ nodes = list_nodes_min() if name in nodes: raise CommandExecutionError( - 'The specified VM ({0}) is already registered.'.format(name) + "The specified VM ({0}) is already registered.".format(name) ) - params = '' + params = "" if name: if NAME_RE.search(name): - raise CommandExecutionError('New VM name contains invalid characters') - params += ' --name {0}'.format(name) + raise CommandExecutionError("New VM name contains invalid characters") + params += " --name {0}".format(name) if groups: if isinstance(groups, six.string_types): groups = [groups] if isinstance(groups, list): - params += ' --groups {0}'.format(','.join(groups)) + params += " --groups {0}".format(",".join(groups)) else: raise CommandExecutionError( - 'groups must be either a string or a list of strings' + "groups must be either a string or a list of strings" ) ostypes = list_ostypes() if ostype not in ostypes: raise CommandExecutionError( - 'The specified OS type ({0}) is not available.'.format(name) + "The specified OS type ({0}) is not available.".format(name) ) else: - params += ' --ostype ' + ostype + params += " --ostype " + ostype if register is True: - params += ' --register' + params += " --register" if basefolder: if not os.path.exists(basefolder): - raise CommandExecutionError('basefolder {0} was not found'.format(basefolder)) - params += ' --basefolder {0}'.format(basefolder) + raise CommandExecutionError( + "basefolder {0} was not found".format(basefolder) + ) + params += " --basefolder {0}".format(basefolder) if new_uuid: if NAME_RE.search(new_uuid): - raise CommandExecutionError('New UUID contains invalid characters') - params += ' --uuid {0}'.format(new_uuid) + raise CommandExecutionError("New UUID contains invalid characters") + params += " --uuid {0}".format(new_uuid) - cmd = '{0} create {1}'.format(vboxcmd(), params) + cmd = "{0} create {1}".format(vboxcmd(), params) ret = salt.modules.cmdmod.run_all(cmd) - if ret['retcode'] == 0: + if ret["retcode"] == 0: return True - return ret['stderr'] + return ret["stderr"] -def clonevm(name=None, - uuid=None, - new_name=None, - snapshot_uuid=None, - snapshot_name=None, - mode='machine', - options=None, - basefolder=None, - new_uuid=None, - register=False, - groups=None, - **kwargs): - ''' +def clonevm( + name=None, + uuid=None, + new_name=None, + snapshot_uuid=None, + snapshot_name=None, + mode="machine", + options=None, + basefolder=None, + new_uuid=None, + register=False, + groups=None, + **kwargs +): + """ Clone a new VM from an existing VM CLI Example: @@ -314,103 +325,109 @@ def clonevm(name=None, .. code-block:: bash salt 'hypervisor' vboxmanage.clonevm <name> <new_name> - ''' + """ if (name and uuid) or (not name and not uuid): raise CommandExecutionError( - 'Either a name or a uuid must be specified, but not both.' + "Either a name or a uuid must be specified, but not both." ) - params = '' + params = "" nodes_names = list_nodes_min() - nodes_uuids = list_items('vms', True, 'UUID').keys() + nodes_uuids = list_items("vms", True, "UUID").keys() if name: if name not in nodes_names: raise CommandExecutionError( - 'The specified VM ({0}) is not registered.'.format(name) + "The specified VM ({0}) is not registered.".format(name) ) - params += ' ' + name + params += " " + name elif uuid: if uuid not in nodes_uuids: raise CommandExecutionError( - 'The specified VM ({0}) is not registered.'.format(name) + "The specified VM ({0}) is not registered.".format(name) ) - params += ' ' + uuid + params += " " + uuid if snapshot_name and snapshot_uuid: raise CommandExecutionError( - 'Either a snapshot_name or a snapshot_uuid may be specified, but not both' + "Either a snapshot_name or a snapshot_uuid may be specified, but not both" ) if snapshot_name: if NAME_RE.search(snapshot_name): - raise CommandExecutionError('Snapshot name contains invalid characters') - params += ' --snapshot {0}'.format(snapshot_name) + raise CommandExecutionError("Snapshot name contains invalid characters") + params += " --snapshot {0}".format(snapshot_name) elif snapshot_uuid: if UUID_RE.search(snapshot_uuid): - raise CommandExecutionError('Snapshot name contains invalid characters') - params += ' --snapshot {0}'.format(snapshot_uuid) + raise CommandExecutionError("Snapshot name contains invalid characters") + params += " --snapshot {0}".format(snapshot_uuid) - valid_modes = ('machine', 'machineandchildren', 'all') + valid_modes = ("machine", "machineandchildren", "all") if mode and mode not in valid_modes: raise CommandExecutionError( - 'Mode must be one of: {0} (default "machine")'.format(', '.join(valid_modes)) + 'Mode must be one of: {0} (default "machine")'.format( + ", ".join(valid_modes) + ) ) else: - params += ' --mode ' + mode + params += " --mode " + mode - valid_options = ('link', 'keepallmacs', 'keepnatmacs', 'keepdisknames') + valid_options = ("link", "keepallmacs", "keepnatmacs", "keepdisknames") if options and options not in valid_options: raise CommandExecutionError( - 'If specified, options must be one of: {0}'.format(', '.join(valid_options)) + "If specified, options must be one of: {0}".format(", ".join(valid_options)) ) else: - params += ' --options ' + options + params += " --options " + options if new_name: if NAME_RE.search(new_name): - raise CommandExecutionError('New name contains invalid characters') - params += ' --name {0}'.format(new_name) + raise CommandExecutionError("New name contains invalid characters") + params += " --name {0}".format(new_name) if groups: if isinstance(groups, six.string_types): groups = [groups] if isinstance(groups, list): - params += ' --groups {0}'.format(','.join(groups)) + params += " --groups {0}".format(",".join(groups)) else: raise CommandExecutionError( - 'groups must be either a string or a list of strings' + "groups must be either a string or a list of strings" ) if basefolder: if not os.path.exists(basefolder): - raise CommandExecutionError('basefolder {0} was not found'.format(basefolder)) - params += ' --basefolder {0}'.format(basefolder) + raise CommandExecutionError( + "basefolder {0} was not found".format(basefolder) + ) + params += " --basefolder {0}".format(basefolder) if new_uuid: if NAME_RE.search(new_uuid): - raise CommandExecutionError('New UUID contains invalid characters') - params += ' --uuid {0}'.format(new_uuid) + raise CommandExecutionError("New UUID contains invalid characters") + params += " --uuid {0}".format(new_uuid) if register is True: - params += ' --register' + params += " --register" - cmd = '{0} clonevm {1}'.format(vboxcmd(), name) + cmd = "{0} clonevm {1}".format(vboxcmd(), name) ret = salt.modules.cmdmod.run_all(cmd) - if ret['retcode'] == 0: + if ret["retcode"] == 0: return True - return ret['stderr'] + return ret["stderr"] -def clonemedium(medium, - uuid_in=None, - file_in=None, - uuid_out=None, - file_out=None, - mformat=None, - variant=None, - existing=False, - **kwargs): - ''' +def clonemedium( + medium, + uuid_in=None, + file_in=None, + uuid_out=None, + file_out=None, + mformat=None, + variant=None, + existing=False, + **kwargs +): + """ Clone a new VM from an existing VM CLI Example: @@ -418,84 +435,90 @@ def clonemedium(medium, .. code-block:: bash salt 'hypervisor' vboxmanage.clonemedium <name> <new_name> - ''' - params = '' - valid_mediums = ('disk', 'dvd', 'floppy') + """ + params = "" + valid_mediums = ("disk", "dvd", "floppy") if medium in valid_mediums: params += medium else: raise CommandExecutionError( - 'Medium must be one of: {0}.'.format(', '.join(valid_mediums)) + "Medium must be one of: {0}.".format(", ".join(valid_mediums)) ) if (uuid_in and file_in) or (not uuid_in and not file_in): raise CommandExecutionError( - 'Either uuid_in or file_in must be used, but not both.' + "Either uuid_in or file_in must be used, but not both." ) if uuid_in: - if medium == 'disk': - item = 'hdds' - elif medium == 'dvd': - item = 'dvds' - elif medium == 'floppy': - item = 'floppies' + if medium == "disk": + item = "hdds" + elif medium == "dvd": + item = "dvds" + elif medium == "floppy": + item = "floppies" items = list_items(item) if uuid_in not in items: - raise CommandExecutionError('UUID {0} was not found'.format(uuid_in)) - params += ' ' + uuid_in + raise CommandExecutionError("UUID {0} was not found".format(uuid_in)) + params += " " + uuid_in elif file_in: if not os.path.exists(file_in): - raise CommandExecutionError('File {0} was not found'.format(file_in)) - params += ' ' + file_in + raise CommandExecutionError("File {0} was not found".format(file_in)) + params += " " + file_in if (uuid_out and file_out) or (not uuid_out and not file_out): raise CommandExecutionError( - 'Either uuid_out or file_out must be used, but not both.' + "Either uuid_out or file_out must be used, but not both." ) if uuid_out: - params += ' ' + uuid_out + params += " " + uuid_out elif file_out: try: - salt.utils.files.fopen(file_out, 'w').close() # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + salt.utils.files.fopen(file_out, "w").close() + # pylint: enable=resource-leakage os.unlink(file_out) - params += ' ' + file_out + params += " " + file_out except OSError: - raise CommandExecutionError('{0} is not a valid filename'.format(file_out)) + raise CommandExecutionError("{0} is not a valid filename".format(file_out)) if mformat: - valid_mformat = ('VDI', 'VMDK', 'VHD', 'RAW') + valid_mformat = ("VDI", "VMDK", "VHD", "RAW") if mformat not in valid_mformat: raise CommandExecutionError( - 'If specified, mformat must be one of: {0}'.format(', '.join(valid_mformat)) + "If specified, mformat must be one of: {0}".format( + ", ".join(valid_mformat) + ) ) else: - params += ' --format ' + mformat + params += " --format " + mformat - valid_variant = ('Standard', 'Fixed', 'Split2G', 'Stream', 'ESX') + valid_variant = ("Standard", "Fixed", "Split2G", "Stream", "ESX") if variant and variant not in valid_variant: if not os.path.exists(file_in): raise CommandExecutionError( - 'If specified, variant must be one of: {0}'.format(', '.join(valid_variant)) + "If specified, variant must be one of: {0}".format( + ", ".join(valid_variant) + ) ) else: - params += ' --variant ' + variant + params += " --variant " + variant if existing: - params += ' --existing' + params += " --existing" - cmd = '{0} clonemedium {1}'.format(vboxcmd(), params) + cmd = "{0} clonemedium {1}".format(vboxcmd(), params) ret = salt.modules.cmdmod.run_all(cmd) - if ret['retcode'] == 0: + if ret["retcode"] == 0: return True - return ret['stderr'] + return ret["stderr"] -def list_items(item, details=False, group_by='UUID'): - ''' +def list_items(item, details=False, group_by="UUID"): + """ Return a list of a specific type of item. The following items are available: vms @@ -533,37 +556,55 @@ def list_items(item, details=False, group_by='UUID'): Some items do not display well, or at all, unless ``details`` is set to ``True``. By default, items are grouped by the ``UUID`` field, but not all items contain that field. In those cases, another field must be specified. - ''' + """ types = ( - 'vms', 'runningvms', 'ostypes', 'hostdvds', 'hostfloppies', 'intnets', - 'bridgedifs', 'hostonlyifs', 'natnets', 'dhcpservers', 'hostinfo', - 'hostcpuids', 'hddbackends', 'hdds', 'dvds', 'floppies', 'usbhost', - 'usbfilters', 'systemproperties', 'extpacks', 'groups', 'webcams', - 'screenshotformats' + "vms", + "runningvms", + "ostypes", + "hostdvds", + "hostfloppies", + "intnets", + "bridgedifs", + "hostonlyifs", + "natnets", + "dhcpservers", + "hostinfo", + "hostcpuids", + "hddbackends", + "hdds", + "dvds", + "floppies", + "usbhost", + "usbfilters", + "systemproperties", + "extpacks", + "groups", + "webcams", + "screenshotformats", ) if item not in types: raise CommandExecutionError( - 'Item must be one of: {0}.'.format(', '.join(types)) + "Item must be one of: {0}.".format(", ".join(types)) ) - flag = '' + flag = "" if details is True: - flag = ' -l' + flag = " -l" ret = {} tmp_id = None tmp_dict = {} - cmd = '{0} list{1} {2}'.format(vboxcmd(), flag, item) + cmd = "{0} list{1} {2}".format(vboxcmd(), flag, item) for line in salt.modules.cmdmod.run(cmd).splitlines(): if not line.strip(): continue - comps = line.split(':') + comps = line.split(":") if len(comps) < 1: continue if tmp_id is not None: ret[tmp_id] = tmp_dict - line_val = ':'.join(comps[1:]).strip() + line_val = ":".join(comps[1:]).strip() if comps[0] == group_by: tmp_id = line_val tmp_dict = {} diff --git a/salt/modules/vcenter.py b/salt/modules/vcenter.py index b210bbaf64e..48ca6ef7100 100644 --- a/salt/modules/vcenter.py +++ b/salt/modules/vcenter.py @@ -1,29 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Module used to access the vcenter proxy connection methods -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -import salt.utils.platform +import salt.utils.platform log = logging.getLogger(__name__) -__proxyenabled__ = ['vcenter'] +__proxyenabled__ = ["vcenter"] # Define the module's virtual name -__virtualname__ = 'vcenter' +__virtualname__ = "vcenter" def __virtual__(): - ''' + """ Only work on proxy - ''' + """ if salt.utils.platform.is_proxy(): return __virtualname__ return False def get_details(): - return __proxy__['vcenter.get_details']() + return __proxy__["vcenter.get_details"]() diff --git a/salt/modules/victorops.py b/salt/modules/victorops.py index fa55aabb1e7..ea9cbd35d01 100644 --- a/salt/modules/victorops.py +++ b/salt/modules/victorops.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for VictorOps .. versionadded:: 2015.8.0 @@ -10,70 +10,71 @@ Requires an ``api_key`` in ``/etc/salt/minion``: victorops: api_key: '280d4699-a817-4719-ba6f-ca56e573e44f' -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import datetime import logging import time -# Import salt libs -from salt.exceptions import SaltInvocationError import salt.utils.http import salt.utils.json +# Import salt libs +from salt.exceptions import SaltInvocationError + log = logging.getLogger(__name__) -_api_key_missing_error = 'No VictorOps api key found.' +_api_key_missing_error = "No VictorOps api key found." def __virtual__(): - ''' + """ Only load the module if apache is installed - ''' - if not __salt__['config.get']('victorops.api_key') and \ - not __salt__['config.get']('victorops:api_key'): + """ + if not __salt__["config.get"]("victorops.api_key") and not __salt__["config.get"]( + "victorops:api_key" + ): return (False, _api_key_missing_error) return True -def _query(action=None, - routing_key=None, - args=None, - method='GET', - header_dict=None, - data=None): - ''' +def _query( + action=None, routing_key=None, args=None, method="GET", header_dict=None, data=None +): + """ Make a web call to VictorOps - ''' - api_key = __salt__['config.get']('victorops.api_key') or \ - __salt__['config.get']('victorops:api_key') + """ + api_key = __salt__["config.get"]("victorops.api_key") or __salt__["config.get"]( + "victorops:api_key" + ) - path = 'https://alert.victorops.com/integrations/generic/20131114/' + path = "https://alert.victorops.com/integrations/generic/20131114/" if action: - path += '{0}/'.format(action) + path += "{0}/".format(action) if api_key: - path += '{0}/'.format(api_key) + path += "{0}/".format(api_key) if routing_key: path += routing_key - log.debug('VictorOps URL: %s', path) + log.debug("VictorOps URL: %s", path) if not isinstance(args, dict): args = {} if header_dict is None: - header_dict = {'Content-type': 'application/json'} + header_dict = {"Content-type": "application/json"} - if method != 'POST': - header_dict['Accept'] = 'application/json' + if method != "POST": + header_dict["Accept"] = "application/json" decode = True - if method == 'DELETE': + if method == "DELETE": decode = False result = salt.utils.http.query( @@ -83,22 +84,22 @@ def _query(action=None, data=data, header_dict=header_dict, decode=decode, - decode_type='json', + decode_type="json", text=True, status=True, cookies=True, persist_session=True, opts=__opts__, ) - if 'error' in result: - log.error(result['error']) - return [result['status'], result['error']] + if "error" in result: + log.error(result["error"]) + return [result["status"], result["error"]] - return [result['status'], result.get('dict', {})] + return [result["status"], result.get("dict", {})] -def create_event(message_type=None, routing_key='everybody', **kwargs): - ''' +def create_event(message_type=None, routing_key="everybody", **kwargs): + """ Create an event in VictorOps. Designed for use in states. The following parameters are required: @@ -151,48 +152,63 @@ def create_event(message_type=None, routing_key='everybody', **kwargs): The following parameters are required: message_type - ''' + """ - keyword_args = {'entity_id': str, - 'state_message': str, - 'entity_is_host': bool, - 'entity_display_name': str, - 'ack_message': str, - 'ack_author': str - } + keyword_args = { + "entity_id": str, + "state_message": str, + "entity_is_host": bool, + "entity_display_name": str, + "ack_message": str, + "ack_author": str, + } data = {} if not message_type: raise SaltInvocationError('Required argument "message_type" is missing.') - if message_type.upper() not in ['INFO', 'WARNING', 'ACKNOWLEDGEMENT', 'CRITICAL', 'RECOVERY']: - raise SaltInvocationError('"message_type" must be INFO, WARNING, ACKNOWLEDGEMENT, CRITICAL, or RECOVERY.') + if message_type.upper() not in [ + "INFO", + "WARNING", + "ACKNOWLEDGEMENT", + "CRITICAL", + "RECOVERY", + ]: + raise SaltInvocationError( + '"message_type" must be INFO, WARNING, ACKNOWLEDGEMENT, CRITICAL, or RECOVERY.' + ) - data['message_type'] = message_type + data["message_type"] = message_type - data['monitoring_tool'] = 'SaltStack' + data["monitoring_tool"] = "SaltStack" - if 'timestamp' in kwargs: - timestamp_fmt = kwargs.get('timestamp_fmt', '%Y-%m-%dT%H:%M:%S') + if "timestamp" in kwargs: + timestamp_fmt = kwargs.get("timestamp_fmt", "%Y-%m-%dT%H:%M:%S") try: - timestamp = datetime.datetime.strptime(kwargs['timestamp'], timestamp_fmt) - data['timestamp'] = int(time.mktime(timestamp.timetuple())) + timestamp = datetime.datetime.strptime(kwargs["timestamp"], timestamp_fmt) + data["timestamp"] = int(time.mktime(timestamp.timetuple())) except (TypeError, ValueError): - raise SaltInvocationError('Date string could not be parsed: {0}, {1}'.format( - kwargs['timestamp'], timestamp_fmt) + raise SaltInvocationError( + "Date string could not be parsed: {0}, {1}".format( + kwargs["timestamp"], timestamp_fmt + ) ) - if 'state_start_time' in kwargs: - state_start_time_fmt = kwargs.get('state_start_time_fmt', '%Y-%m-%dT%H:%M:%S') + if "state_start_time" in kwargs: + state_start_time_fmt = kwargs.get("state_start_time_fmt", "%Y-%m-%dT%H:%M:%S") try: - state_start_time = datetime.datetime.strptime(kwargs['state_start_time'], state_start_time_fmt) - data['state_start_time'] = int(time.mktime(state_start_time.timetuple())) + state_start_time = datetime.datetime.strptime( + kwargs["state_start_time"], state_start_time_fmt + ) + data["state_start_time"] = int(time.mktime(state_start_time.timetuple())) except (TypeError, ValueError): - raise SaltInvocationError('Date string could not be parsed: {0}, {1}'.format( - kwargs['state_start_time'], state_start_time_fmt) + raise SaltInvocationError( + "Date string could not be parsed: {0}, {1}".format( + kwargs["state_start_time"], state_start_time_fmt + ) ) for kwarg in keyword_args: @@ -201,11 +217,12 @@ def create_event(message_type=None, routing_key='everybody', **kwargs): data[kwarg] = kwargs[kwarg] else: # Should this faile on the wrong type. - log.error('Wrong type, skipping %s', kwarg) + log.error("Wrong type, skipping %s", kwarg) - status, result = _query(action='alert', - routing_key=routing_key, - data=salt.utils.json.dumps(data), - method='POST' - ) + status, result = _query( + action="alert", + routing_key=routing_key, + data=salt.utils.json.dumps(data), + method="POST", + ) return result diff --git a/salt/modules/virt.py b/salt/modules/virt.py index a2412bb7455..e5d11c332f9 100644 --- a/salt/modules/virt.py +++ b/salt/modules/virt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Work with virtual machines managed by libvirt :depends: libvirt Python module @@ -68,34 +68,29 @@ The calls not using the libvirt connection setup are: - `libvirt URI format <http://libvirt.org/uri.html#URI_config>`_ - `libvirt authentication configuration <http://libvirt.org/auth.html#Auth_client_config>`_ -''' +""" # Special Thanks to Michael Dehann, many of the concepts, and a few structures # of his in the virt func module have been used # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import copy +import datetime +import logging import os import re -import sys import shutil -import subprocess import string # pylint: disable=deprecated-module -import logging +import subprocess +import sys import time -import datetime from xml.etree import ElementTree # Import third party libs import jinja2 import jinja2.exceptions -try: - import libvirt # pylint: disable=import-error - from libvirt import libvirtError - HAS_LIBVIRT = True -except ImportError: - HAS_LIBVIRT = False # Import salt libs import salt.utils.files @@ -106,49 +101,64 @@ import salt.utils.stringutils import salt.utils.templates import salt.utils.validate.net import salt.utils.versions +import salt.utils.xmlutil as xmlutil import salt.utils.yaml - -from salt.utils.virt import check_remote, download_remote +from salt._compat import ipaddress from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -from salt._compat import ipaddress +from salt.utils.virt import check_remote, download_remote + +try: + import libvirt # pylint: disable=import-error + + # pylint: disable=no-name-in-module + from libvirt import libvirtError + + # pylint: enable=no-name-in-module + + HAS_LIBVIRT = True +except ImportError: + HAS_LIBVIRT = False + log = logging.getLogger(__name__) # Set up template environment JINJA = jinja2.Environment( loader=jinja2.FileSystemLoader( - os.path.join(salt.utils.templates.TEMPLATE_DIRNAME, 'virt') + os.path.join(salt.utils.templates.TEMPLATE_DIRNAME, "virt") ) ) -CACHE_DIR = '/var/lib/libvirt/saltinst' +CACHE_DIR = "/var/lib/libvirt/saltinst" -VIRT_STATE_NAME_MAP = {0: 'running', - 1: 'running', - 2: 'running', - 3: 'paused', - 4: 'shutdown', - 5: 'shutdown', - 6: 'crashed'} +VIRT_STATE_NAME_MAP = { + 0: "running", + 1: "running", + 2: "running", + 3: "paused", + 4: "shutdown", + 5: "shutdown", + 6: "crashed", +} def __virtual__(): if not HAS_LIBVIRT: - return (False, 'Unable to locate or import python libvirt library.') - return 'virt' + return (False, "Unable to locate or import python libvirt library.") + return "virt" def __get_request_auth(username, password): - ''' + """ Get libvirt.openAuth callback with username, password values overriding the configuration ones. - ''' + """ # pylint: disable=unused-argument def __request_auth(credentials, user_data): - '''Callback method passed to libvirt.openAuth(). + """Callback method passed to libvirt.openAuth(). The credentials argument is a list of credentials that libvirt would like to request. An element of this list is a list containing @@ -160,21 +170,31 @@ def __get_request_auth(username, password): - a place to store the actual result for the request The user_data argument is currently not set in the openAuth call. - ''' + """ for credential in credentials: if credential[0] == libvirt.VIR_CRED_AUTHNAME: - credential[4] = username if username else \ - __salt__['config.get']('virt:connection:auth:username', credential[3]) + credential[4] = ( + username + if username + else __salt__["config.get"]( + "virt:connection:auth:username", credential[3] + ) + ) elif credential[0] == libvirt.VIR_CRED_NOECHOPROMPT: - credential[4] = password if password else \ - __salt__['config.get']('virt:connection:auth:password', credential[3]) + credential[4] = ( + password + if password + else __salt__["config.get"]( + "virt:connection:auth:password", credential[3] + ) + ) else: - log.info('Unhandled credential type: %s', credential[0]) + log.info("Unhandled credential type: %s", credential[0]) return 0 def __get_conn(**kwargs): - ''' + """ Detects what type of dom this node is and attempts to connect to the correct hypervisor via libvirt. @@ -182,99 +202,64 @@ def __get_conn(**kwargs): :param username: username to connect with, overriding defaults :param password: password to connect with, overriding defaults - ''' + """ # This has only been tested on kvm and xen, it needs to be expanded to # support all vm layers supported by libvirt - username = kwargs.get('username', None) - password = kwargs.get('password', None) - conn_str = kwargs.get('connection', None) + username = kwargs.get("username", None) + password = kwargs.get("password", None) + conn_str = kwargs.get("connection", None) if not conn_str: - conn_str = __salt__['config.get']('virt.connect', None) - if conn_str is not None: - salt.utils.versions.warn_until( - 'Sodium', - '\'virt.connect\' configuration property has been deprecated in favor ' - 'of \'virt:connection:uri\'. \'virt.connect\' will stop being used in ' - '{version}.' - ) - else: - conn_str = __salt__['config.get']('libvirt:connection', None) - if conn_str is not None: - salt.utils.versions.warn_until( - 'Sodium', - '\'libvirt.connection\' configuration property has been deprecated in favor ' - 'of \'virt:connection:uri\'. \'libvirt.connection\' will stop being used in ' - '{version}.' - ) - - conn_str = __salt__['config.get']('virt:connection:uri', conn_str) - - hypervisor = __salt__['config.get']('libvirt:hypervisor', None) - if hypervisor is not None: - salt.utils.versions.warn_until( - 'Sodium', - '\'libvirt.hypervisor\' configuration property has been deprecated. ' - 'Rather use the \'virt:connection:uri\' to properly define the libvirt ' - 'URI or alias of the host to connect to. \'libvirt:hypervisor\' will ' - 'stop being used in {version}.' - ) - - if hypervisor == 'esxi' and conn_str is None: - salt.utils.versions.warn_until( - 'Sodium', - 'esxi hypervisor default with no default connection URI detected, ' - 'please set \'virt:connection:uri\' to \'esx\' for keep the legacy ' - 'behavior. Will default to libvirt guess once \'libvirt:hypervisor\' ' - 'configuration is removed in {version}.' - ) - conn_str = 'esx' + conn_str = __salt__["config.get"]("virt:connection:uri", conn_str) try: - auth_types = [libvirt.VIR_CRED_AUTHNAME, - libvirt.VIR_CRED_NOECHOPROMPT, - libvirt.VIR_CRED_ECHOPROMPT, - libvirt.VIR_CRED_PASSPHRASE, - libvirt.VIR_CRED_EXTERNAL] - conn = libvirt.openAuth(conn_str, [auth_types, __get_request_auth(username, password), None], 0) + auth_types = [ + libvirt.VIR_CRED_AUTHNAME, + libvirt.VIR_CRED_NOECHOPROMPT, + libvirt.VIR_CRED_ECHOPROMPT, + libvirt.VIR_CRED_PASSPHRASE, + libvirt.VIR_CRED_EXTERNAL, + ] + conn = libvirt.openAuth( + conn_str, [auth_types, __get_request_auth(username, password), None], 0 + ) except Exception: # pylint: disable=broad-except raise CommandExecutionError( - 'Sorry, {0} failed to open a connection to the hypervisor ' - 'software at {1}'.format( - __grains__['fqdn'], - conn_str - ) + "Sorry, {0} failed to open a connection to the hypervisor " + "software at {1}".format(__grains__["fqdn"], conn_str) ) return conn def _get_domain(conn, *vms, **kwargs): - ''' + """ Return a domain object for the named VM or return domain object for all VMs. :params conn: libvirt connection object :param vms: list of domain names to look for :param iterable: True to return an array in all cases - ''' + """ ret = list() lookup_vms = list() all_vms = [] - if kwargs.get('active', True): + if kwargs.get("active", True): for id_ in conn.listDomainsID(): all_vms.append(conn.lookupByID(id_).name()) - if kwargs.get('inactive', True): + if kwargs.get("inactive", True): for id_ in conn.listDefinedDomains(): all_vms.append(id_) - if not all_vms: - raise CommandExecutionError('No virtual machines found.') + if vms and not all_vms: + raise CommandExecutionError("No virtual machines found.") if vms: for name in vms: if name not in all_vms: - raise CommandExecutionError('The VM "{name}" is not present'.format(name=name)) + raise CommandExecutionError( + 'The VM "{name}" is not present'.format(name=name) + ) else: lookup_vms.append(name) else: @@ -283,52 +268,69 @@ def _get_domain(conn, *vms, **kwargs): for name in lookup_vms: ret.append(conn.lookupByName(name)) - return len(ret) == 1 and not kwargs.get('iterable') and ret[0] or ret + return len(ret) == 1 and not kwargs.get("iterable") and ret[0] or ret def _parse_qemu_img_info(info): - ''' + """ Parse qemu-img info JSON output into disk infos dictionary - ''' + """ raw_infos = salt.utils.json.loads(info) disks = [] for disk_infos in raw_infos: disk = { - 'file': disk_infos['filename'], - 'file format': disk_infos['format'], - 'disk size': disk_infos['actual-size'], - 'virtual size': disk_infos['virtual-size'], - 'cluster size': disk_infos['cluster-size'] if 'cluster-size' in disk_infos else None, - } + "file": disk_infos["filename"], + "file format": disk_infos["format"], + "disk size": disk_infos["actual-size"], + "virtual size": disk_infos["virtual-size"], + "cluster size": disk_infos["cluster-size"] + if "cluster-size" in disk_infos + else None, + } - if 'full-backing-filename' in disk_infos.keys(): - disk['backing file'] = format(disk_infos['full-backing-filename']) + if "full-backing-filename" in disk_infos.keys(): + disk["backing file"] = format(disk_infos["full-backing-filename"]) - if 'snapshots' in disk_infos.keys(): - disk['snapshots'] = [ - { - 'id': snapshot['id'], - 'tag': snapshot['name'], - 'vmsize': snapshot['vm-state-size'], - 'date': datetime.datetime.fromtimestamp( - float('{}.{}'.format(snapshot['date-sec'], snapshot['date-nsec']))).isoformat(), - 'vmclock': datetime.datetime.utcfromtimestamp( - float('{}.{}'.format(snapshot['vm-clock-sec'], - snapshot['vm-clock-nsec']))).time().isoformat() - } for snapshot in disk_infos['snapshots']] + if "snapshots" in disk_infos.keys(): + disk["snapshots"] = [ + { + "id": snapshot["id"], + "tag": snapshot["name"], + "vmsize": snapshot["vm-state-size"], + "date": datetime.datetime.fromtimestamp( + float( + "{}.{}".format(snapshot["date-sec"], snapshot["date-nsec"]) + ) + ).isoformat(), + "vmclock": datetime.datetime.utcfromtimestamp( + float( + "{}.{}".format( + snapshot["vm-clock-sec"], snapshot["vm-clock-nsec"] + ) + ) + ) + .time() + .isoformat(), + } + for snapshot in disk_infos["snapshots"] + ] disks.append(disk) for disk in disks: - if 'backing file' in disk.keys(): - candidates = [info for info in disks if 'file' in info.keys() and info['file'] == disk['backing file']] + if "backing file" in disk.keys(): + candidates = [ + info + for info in disks + if "file" in info.keys() and info["file"] == disk["backing file"] + ] if candidates: - disk['backing file'] = candidates[0] + disk["backing file"] = candidates[0] return disks[0] def _get_uuid(dom): - ''' + """ Return a uuid from the named vm CLI Example: @@ -336,12 +338,12 @@ def _get_uuid(dom): .. code-block:: bash salt '*' virt.get_uuid <domain> - ''' - return ElementTree.fromstring(get_xml(dom)).find('uuid').text + """ + return ElementTree.fromstring(get_xml(dom)).find("uuid").text def _get_on_poweroff(dom): - ''' + """ Return `on_poweroff` setting from the named vm CLI Example: @@ -349,13 +351,13 @@ def _get_on_poweroff(dom): .. code-block:: bash salt '*' virt.get_on_restart <domain> - ''' - node = ElementTree.fromstring(get_xml(dom)).find('on_poweroff') - return node.text if node is not None else '' + """ + node = ElementTree.fromstring(get_xml(dom)).find("on_poweroff") + return node.text if node is not None else "" def _get_on_reboot(dom): - ''' + """ Return `on_reboot` setting from the named vm CLI Example: @@ -363,13 +365,13 @@ def _get_on_reboot(dom): .. code-block:: bash salt '*' virt.get_on_reboot <domain> - ''' - node = ElementTree.fromstring(get_xml(dom)).find('on_reboot') - return node.text if node is not None else '' + """ + node = ElementTree.fromstring(get_xml(dom)).find("on_reboot") + return node.text if node is not None else "" def _get_on_crash(dom): - ''' + """ Return `on_crash` setting from the named vm CLI Example: @@ -377,460 +379,472 @@ def _get_on_crash(dom): .. code-block:: bash salt '*' virt.get_on_crash <domain> - ''' - node = ElementTree.fromstring(get_xml(dom)).find('on_crash') - return node.text if node is not None else '' + """ + node = ElementTree.fromstring(get_xml(dom)).find("on_crash") + return node.text if node is not None else "" def _get_nics(dom): - ''' + """ Get domain network interfaces from a libvirt domain object. - ''' + """ nics = {} doc = ElementTree.fromstring(dom.XMLDesc(0)) - for iface_node in doc.findall('devices/interface'): + for iface_node in doc.findall("devices/interface"): nic = {} - nic['type'] = iface_node.get('type') + nic["type"] = iface_node.get("type") for v_node in iface_node: - if v_node.tag == 'mac': - nic['mac'] = v_node.get('address') - if v_node.tag == 'model': - nic['model'] = v_node.get('type') - if v_node.tag == 'target': - nic['target'] = v_node.get('dev') + if v_node.tag == "mac": + nic["mac"] = v_node.get("address") + if v_node.tag == "model": + nic["model"] = v_node.get("type") + if v_node.tag == "target": + nic["target"] = v_node.get("dev") # driver, source, and match can all have optional attributes - if re.match('(driver|source|address)', v_node.tag): + if re.match("(driver|source|address)", v_node.tag): temp = {} for key, value in six.iteritems(v_node.attrib): temp[key] = value nic[v_node.tag] = temp # virtualport needs to be handled separately, to pick up the # type attribute of the virtualport itself - if v_node.tag == 'virtualport': + if v_node.tag == "virtualport": temp = {} - temp['type'] = v_node.get('type') + temp["type"] = v_node.get("type") for key, value in six.iteritems(v_node.attrib): temp[key] = value - nic['virtualport'] = temp - if 'mac' not in nic: + nic["virtualport"] = temp + if "mac" not in nic: continue - nics[nic['mac']] = nic + nics[nic["mac"]] = nic return nics def _get_graphics(dom): - ''' + """ Get domain graphics from a libvirt domain object. - ''' - out = {'autoport': 'None', - 'keymap': 'None', - 'listen': 'None', - 'port': 'None', - 'type': 'None'} + """ + out = { + "autoport": "None", + "keymap": "None", + "listen": "None", + "port": "None", + "type": "None", + } doc = ElementTree.fromstring(dom.XMLDesc(0)) - for g_node in doc.findall('devices/graphics'): + for g_node in doc.findall("devices/graphics"): for key, value in six.iteritems(g_node.attrib): out[key] = value return out def _get_disks(dom): - ''' + """ Get domain disks from a libvirt domain object. - ''' + """ disks = {} doc = ElementTree.fromstring(dom.XMLDesc(0)) - for elem in doc.findall('devices/disk'): - source = elem.find('source') + for elem in doc.findall("devices/disk"): + source = elem.find("source") if source is None: continue - target = elem.find('target') + target = elem.find("target") if target is None: continue - if 'dev' in target.attrib: - qemu_target = source.get('file', '') + if "dev" in target.attrib: + qemu_target = source.get("file", "") if not qemu_target: - qemu_target = source.get('dev', '') - if not qemu_target and 'protocol' in source.attrib and 'name' in source.attrib: # for rbd network - qemu_target = '{0}:{1}'.format( - source.get('protocol'), - source.get('name')) + qemu_target = source.get("dev", "") + if ( + not qemu_target + and "protocol" in source.attrib + and "name" in source.attrib + ): # for rbd network + qemu_target = "{0}:{1}".format( + source.get("protocol"), source.get("name") + ) if not qemu_target: continue - disk = {'file': qemu_target, 'type': elem.get('device')} + disk = {"file": qemu_target, "type": elem.get("device")} - driver = elem.find('driver') - if driver is not None and driver.get('type') == 'qcow2': + driver = elem.find("driver") + if driver is not None and driver.get("type") == "qcow2": try: stdout = subprocess.Popen( - ['qemu-img', 'info', '-U', '--output', 'json', '--backing-chain', disk['file']], - shell=False, - stdout=subprocess.PIPE).communicate()[0] + [ + "qemu-img", + "info", + "-U", + "--output", + "json", + "--backing-chain", + disk["file"], + ], + shell=False, + stdout=subprocess.PIPE, + ).communicate()[0] qemu_output = salt.utils.stringutils.to_str(stdout) output = _parse_qemu_img_info(qemu_output) disk.update(output) except TypeError: - disk.update({'file': 'Does not exist'}) + disk.update({"file": "Does not exist"}) - disks[target.get('dev')] = disk + disks[target.get("dev")] = disk return disks def _libvirt_creds(): - ''' + """ Returns the user and group that the disk images should be owned by - ''' - g_cmd = 'grep ^\\s*group /etc/libvirt/qemu.conf' - u_cmd = 'grep ^\\s*user /etc/libvirt/qemu.conf' + """ + g_cmd = "grep ^\\s*group /etc/libvirt/qemu.conf" + u_cmd = "grep ^\\s*user /etc/libvirt/qemu.conf" try: - stdout = subprocess.Popen(g_cmd, - shell=True, - stdout=subprocess.PIPE).communicate()[0] + stdout = subprocess.Popen( + g_cmd, shell=True, stdout=subprocess.PIPE + ).communicate()[0] group = salt.utils.stringutils.to_str(stdout).split('"')[1] except IndexError: - group = 'root' + group = "root" try: - stdout = subprocess.Popen(u_cmd, - shell=True, - stdout=subprocess.PIPE).communicate()[0] + stdout = subprocess.Popen( + u_cmd, shell=True, stdout=subprocess.PIPE + ).communicate()[0] user = salt.utils.stringutils.to_str(stdout).split('"')[1] except IndexError: - user = 'root' - return {'user': user, 'group': group} + user = "root" + return {"user": user, "group": group} def _get_migrate_command(): - ''' + """ Returns the command shared by the different migration types - ''' - tunnel = __salt__['config.option']('virt.tunnel') + """ + tunnel = __salt__["config.get"]("virt:tunnel") if tunnel: - salt.utils.versions.warn_until( - 'Sodium', - '\'virt.tunnel\' has been deprecated in favor of ' - '\'virt:tunnel\'. \'virt.tunnel\' will stop ' - 'being used in {version}.') - else: - tunnel = __salt__['config.get']('virt:tunnel') - if tunnel: - return ('virsh migrate --p2p --tunnelled --live --persistent ' - '--undefinesource ') - return 'virsh migrate --live --persistent --undefinesource ' + return ( + "virsh migrate --p2p --tunnelled --live --persistent " "--undefinesource " + ) + return "virsh migrate --live --persistent --undefinesource " def _get_target(target, ssh): - ''' + """ Compute libvirt URL for target migration host. - ''' - proto = 'qemu' + """ + proto = "qemu" if ssh: - proto += '+ssh' - return ' {0}://{1}/{2}'.format(proto, target, 'system') + proto += "+ssh" + return " {0}://{1}/{2}".format(proto, target, "system") -def _gen_xml(name, - cpu, - mem, - diskp, - nicp, - hypervisor, - os_type, - arch, - graphics=None, - boot=None, - **kwargs): - ''' +def _gen_xml( + name, + cpu, + mem, + diskp, + nicp, + hypervisor, + os_type, + arch, + graphics=None, + boot=None, + **kwargs +): + """ Generate the XML string to define a libvirt VM - ''' + """ mem = int(mem) * 1024 # MB context = { - 'hypervisor': hypervisor, - 'name': name, - 'cpu': six.text_type(cpu), - 'mem': six.text_type(mem), + "hypervisor": hypervisor, + "name": name, + "cpu": six.text_type(cpu), + "mem": six.text_type(mem), } - if hypervisor in ['qemu', 'kvm']: - context['controller_model'] = False - elif hypervisor == 'vmware': + if hypervisor in ["qemu", "kvm"]: + context["controller_model"] = False + elif hypervisor == "vmware": # TODO: make bus and model parameterized, this works for 64-bit Linux - context['controller_model'] = 'lsilogic' + context["controller_model"] = "lsilogic" # By default, set the graphics to listen to all addresses if graphics: - if 'listen' not in graphics: - graphics['listen'] = {'type': 'address', 'address': '0.0.0.0'} - elif 'address' not in graphics['listen'] and graphics['listen']['type'] == 'address': - graphics['listen']['address'] = '0.0.0.0' + if "listen" not in graphics: + graphics["listen"] = {"type": "address", "address": "0.0.0.0"} + elif ( + "address" not in graphics["listen"] + and graphics["listen"]["type"] == "address" + ): + graphics["listen"]["address"] = "0.0.0.0" # Graphics of type 'none' means no graphics device at all - if graphics.get('type', 'none') == 'none': + if graphics.get("type", "none") == "none": graphics = None - context['graphics'] = graphics + context["graphics"] = graphics - if 'boot_dev' in kwargs: - context['boot_dev'] = [] - for dev in kwargs['boot_dev'].split(): - context['boot_dev'].append(dev) + if "boot_dev" in kwargs: + context["boot_dev"] = [] + for dev in kwargs["boot_dev"].split(): + context["boot_dev"].append(dev) else: - context['boot_dev'] = ['hd'] + context["boot_dev"] = ["hd"] - context['boot'] = boot if boot else {} + context["boot"] = boot if boot else {} - if os_type == 'xen': + if os_type == "xen": # Compute the Xen PV boot method - if __grains__['os_family'] == 'Suse': - if not boot or not boot.get('kernel', None): - context['boot']['kernel'] = \ - '/usr/lib/grub2/x86_64-xen/grub.xen' - context['boot_dev'] = [] + if __grains__["os_family"] == "Suse": + if not boot or not boot.get("kernel", None): + context["boot"]["kernel"] = "/usr/lib/grub2/x86_64-xen/grub.xen" + context["boot_dev"] = [] - if 'serial_type' in kwargs: - context['serial_type'] = kwargs['serial_type'] - if 'serial_type' in context and context['serial_type'] == 'tcp': - if 'telnet_port' in kwargs: - context['telnet_port'] = kwargs['telnet_port'] + if "serial_type" in kwargs: + context["serial_type"] = kwargs["serial_type"] + if "serial_type" in context and context["serial_type"] == "tcp": + if "telnet_port" in kwargs: + context["telnet_port"] = kwargs["telnet_port"] else: - context['telnet_port'] = 23023 # FIXME: use random unused port - if 'serial_type' in context: - if 'console' in kwargs: - context['console'] = kwargs['console'] + context["telnet_port"] = 23023 # FIXME: use random unused port + if "serial_type" in context: + if "console" in kwargs: + context["console"] = kwargs["console"] else: - context['console'] = True + context["console"] = True - context['disks'] = [] - disk_bus_map = {'virtio': 'vd', 'xen': 'xvd', 'fdc': 'fd', 'ide': 'hd'} + context["disks"] = [] + disk_bus_map = {"virtio": "vd", "xen": "xvd", "fdc": "fd", "ide": "hd"} for i, disk in enumerate(diskp): - prefix = disk_bus_map.get(disk['model'], 'sd') + prefix = disk_bus_map.get(disk["model"], "sd") disk_context = { - 'device': disk.get('device', 'disk'), - 'target_dev': '{0}{1}'.format(prefix, string.ascii_lowercase[i]), - 'disk_bus': disk['model'], - 'type': disk['format'], - 'index': six.text_type(i), + "device": disk.get("device", "disk"), + "target_dev": "{0}{1}".format(prefix, string.ascii_lowercase[i]), + "disk_bus": disk["model"], + "type": disk["format"], + "index": six.text_type(i), } - if 'source_file' and disk['source_file']: - disk_context['source_file'] = disk['source_file'] + if "source_file" and disk["source_file"]: + disk_context["source_file"] = disk["source_file"] - if hypervisor in ['qemu', 'kvm', 'bhyve', 'xen']: - disk_context['address'] = False - disk_context['driver'] = True - elif hypervisor in ['esxi', 'vmware']: - disk_context['address'] = True - disk_context['driver'] = False - context['disks'].append(disk_context) - context['nics'] = nicp + if hypervisor in ["qemu", "kvm", "bhyve", "xen"]: + disk_context["address"] = False + disk_context["driver"] = True + elif hypervisor in ["esxi", "vmware"]: + disk_context["address"] = True + disk_context["driver"] = False + context["disks"].append(disk_context) + context["nics"] = nicp - context['os_type'] = os_type - context['arch'] = arch + context["os_type"] = os_type + context["arch"] = arch - fn_ = 'libvirt_domain.jinja' + fn_ = "libvirt_domain.jinja" try: template = JINJA.get_template(fn_) except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template %s', fn_) - return '' + log.error("Could not load template %s", fn_) + return "" return template.render(**context) -def _gen_vol_xml(vmname, - diskname, - disktype, - size, - pool): - ''' +def _gen_vol_xml(vmname, diskname, disktype, size, pool): + """ Generate the XML string to define a libvirt storage volume - ''' + """ size = int(size) * 1024 # MB context = { - 'name': vmname, - 'filename': '{0}.{1}'.format(diskname, disktype), - 'volname': diskname, - 'disktype': disktype, - 'size': six.text_type(size), - 'pool': pool, + "name": vmname, + "filename": "{0}.{1}".format(diskname, disktype), + "volname": diskname, + "disktype": disktype, + "size": six.text_type(size), + "pool": pool, } - fn_ = 'libvirt_volume.jinja' + fn_ = "libvirt_volume.jinja" try: template = JINJA.get_template(fn_) except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template %s', fn_) - return '' + log.error("Could not load template %s", fn_) + return "" return template.render(**context) -def _gen_net_xml(name, - bridge, - forward, - vport, - tag=None, - ip_configs=None): - ''' +def _gen_net_xml(name, bridge, forward, vport, tag=None, ip_configs=None): + """ Generate the XML string to define a libvirt network - ''' + """ context = { - 'name': name, - 'bridge': bridge, - 'forward': forward, - 'vport': vport, - 'tag': tag, - 'ip_configs': [{ - 'address': ipaddress.ip_network(config['cidr']), - 'dhcp_ranges': config.get('dhcp_ranges', []), - } for config in ip_configs or []], + "name": name, + "bridge": bridge, + "forward": forward, + "vport": vport, + "tag": tag, + "ip_configs": [ + { + "address": ipaddress.ip_network(config["cidr"]), + "dhcp_ranges": config.get("dhcp_ranges", []), + } + for config in ip_configs or [] + ], } - fn_ = 'libvirt_network.jinja' + fn_ = "libvirt_network.jinja" try: template = JINJA.get_template(fn_) except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template %s', fn_) - return '' + log.error("Could not load template %s", fn_) + return "" return template.render(**context) -def _gen_pool_xml(name, - ptype, - target=None, - permissions=None, - source_devices=None, - source_dir=None, - source_adapter=None, - source_hosts=None, - source_auth=None, - source_name=None, - source_format=None, - source_initiator=None): - ''' +def _gen_pool_xml( + name, + ptype, + target=None, + permissions=None, + source_devices=None, + source_dir=None, + source_adapter=None, + source_hosts=None, + source_auth=None, + source_name=None, + source_format=None, + source_initiator=None, +): + """ Generate the XML string to define a libvirt storage pool - ''' - hosts = [host.split(':') for host in source_hosts or []] + """ + hosts = [host.split(":") for host in source_hosts or []] source = None - if any([source_devices, source_dir, source_adapter, hosts, source_auth, source_name, source_format, - source_initiator]): + if any( + [ + source_devices, + source_dir, + source_adapter, + hosts, + source_auth, + source_name, + source_format, + source_initiator, + ] + ): source = { - 'devices': source_devices or [], - 'dir': source_dir if source_format != 'cifs' or not source_dir else source_dir.lstrip('/'), - 'adapter': source_adapter, - 'hosts': [{'name': host[0], 'port': host[1] if len(host) > 1 else None} for host in hosts], - 'auth': source_auth, - 'name': source_name, - 'format': source_format, - 'initiator': source_initiator, + "devices": source_devices or [], + "dir": source_dir + if source_format != "cifs" or not source_dir + else source_dir.lstrip("/"), + "adapter": source_adapter, + "hosts": [ + {"name": host[0], "port": host[1] if len(host) > 1 else None} + for host in hosts + ], + "auth": source_auth, + "name": source_name, + "format": source_format, + "initiator": source_initiator, } context = { - 'name': name, - 'ptype': ptype, - 'target': {'path': target, 'permissions': permissions}, - 'source': source + "name": name, + "ptype": ptype, + "target": {"path": target, "permissions": permissions}, + "source": source, } - fn_ = 'libvirt_pool.jinja' + fn_ = "libvirt_pool.jinja" try: template = JINJA.get_template(fn_) except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template %s', fn_) - return '' + log.error("Could not load template %s", fn_) + return "" return template.render(**context) def _gen_secret_xml(auth_type, usage, description): - ''' + """ Generate a libvirt secret definition XML - ''' + """ context = { - 'type': auth_type, - 'usage': usage, - 'description': description, + "type": auth_type, + "usage": usage, + "description": description, } - fn_ = 'libvirt_secret.jinja' + fn_ = "libvirt_secret.jinja" try: template = JINJA.get_template(fn_) except jinja2.exceptions.TemplateNotFound: - log.error('Could not load template %s', fn_) - return '' + log.error("Could not load template %s", fn_) + return "" return template.render(**context) def _get_images_dir(): - ''' + """ Extract the images dir from the configuration. First attempts to find legacy virt.images, then tries virt:images. - ''' - img_dir = __salt__['config.option']('virt.images') - if img_dir: - salt.utils.versions.warn_until( - 'Sodium', - '\'virt.images\' has been deprecated in favor of ' - '\'virt:images\'. \'virt.images\' will stop ' - 'being used in {version}.') - else: - img_dir = __salt__['config.get']('virt:images') - - log.debug('Image directory from config option `virt:images`' - ' is %s', img_dir) + """ + img_dir = __salt__["config.get"]("virt:images") + log.debug("Image directory from config option `virt:images`" " is %s", img_dir) return img_dir -def _qemu_image_create(disk, create_overlay=False, saltenv='base'): - ''' +def _qemu_image_create(disk, create_overlay=False, saltenv="base"): + """ Create the image file using specified disk_size or/and disk_image Return path to the created image file - ''' - disk_size = disk.get('size', None) - disk_image = disk.get('image', None) + """ + disk_size = disk.get("size", None) + disk_image = disk.get("image", None) if not disk_size and not disk_image: raise CommandExecutionError( - 'Unable to create new disk {0}, please specify' - ' disk size and/or disk image argument' - .format(disk['filename']) + "Unable to create new disk {0}, please specify" + " disk size and/or disk image argument".format(disk["filename"]) ) - img_dest = disk['source_file'] - log.debug('Image destination will be %s', img_dest) + img_dest = disk["source_file"] + log.debug("Image destination will be %s", img_dest) img_dir = os.path.dirname(img_dest) - log.debug('Image destination directory is %s', img_dir) + log.debug("Image destination directory is %s", img_dir) if not os.path.exists(img_dir): os.makedirs(img_dir) if disk_image: - log.debug('Create disk from specified image %s', disk_image) - sfn = __salt__['cp.cache_file'](disk_image, saltenv) + log.debug("Create disk from specified image %s", disk_image) + sfn = __salt__["cp.cache_file"](disk_image, saltenv) qcow2 = False - if salt.utils.path.which('qemu-img'): - res = __salt__['cmd.run']('qemu-img info "{}"'.format(sfn)) + if salt.utils.path.which("qemu-img"): + res = __salt__["cmd.run"]('qemu-img info "{}"'.format(sfn)) imageinfo = salt.utils.yaml.safe_load(res) - qcow2 = imageinfo['file format'] == 'qcow2' + qcow2 = imageinfo["file format"] == "qcow2" try: if create_overlay and qcow2: - log.info('Cloning qcow2 image %s using copy on write', sfn) - __salt__['cmd.run']( - 'qemu-img create -f qcow2 -o backing_file="{0}" "{1}"' - .format(sfn, img_dest).split()) + log.info("Cloning qcow2 image %s using copy on write", sfn) + __salt__["cmd.run"]( + 'qemu-img create -f qcow2 -o backing_file="{0}" "{1}"'.format( + sfn, img_dest + ).split() + ) else: - log.debug('Copying %s to %s', sfn, img_dest) + log.debug("Copying %s to %s", sfn, img_dest) salt.utils.files.copyfile(sfn, img_dest) mask = salt.utils.files.get_umask() if disk_size and qcow2: - log.debug('Resize qcow2 image to %sM', disk_size) - __salt__['cmd.run']( - 'qemu-img resize "{0}" {1}M' - .format(img_dest, disk_size) + log.debug("Resize qcow2 image to %sM", disk_size) + __salt__["cmd.run"]( + 'qemu-img resize "{0}" {1}M'.format(img_dest, disk_size) ) - log.debug('Apply umask and remove exec bit') + log.debug("Apply umask and remove exec bit") mode = (0o0777 ^ mask) & 0o0666 os.chmod(img_dest, mode) except (IOError, OSError) as err: raise CommandExecutionError( - 'Problem while copying image. {0} - {1}' - .format(disk_image, err) + "Problem while copying image. {0} - {1}".format(disk_image, err) ) else: @@ -839,33 +853,32 @@ def _qemu_image_create(disk, create_overlay=False, saltenv='base'): mask = salt.utils.files.get_umask() if disk_size: - log.debug('Create empty image with size %sM', disk_size) - __salt__['cmd.run']( - 'qemu-img create -f {0} "{1}" {2}M' - .format(disk.get('format', 'qcow2'), img_dest, disk_size) + log.debug("Create empty image with size %sM", disk_size) + __salt__["cmd.run"]( + 'qemu-img create -f {0} "{1}" {2}M'.format( + disk.get("format", "qcow2"), img_dest, disk_size + ) ) else: raise CommandExecutionError( - 'Unable to create new disk {0},' - ' please specify <size> argument' - .format(img_dest) + "Unable to create new disk {0}," + " please specify <size> argument".format(img_dest) ) - log.debug('Apply umask and remove exec bit') + log.debug("Apply umask and remove exec bit") mode = (0o0777 ^ mask) & 0o0666 os.chmod(img_dest, mode) except (IOError, OSError) as err: raise CommandExecutionError( - 'Problem while creating volume {0} - {1}' - .format(img_dest, err) + "Problem while creating volume {0} - {1}".format(img_dest, err) ) return img_dest -def _disk_profile(profile, hypervisor, disks=None, vm_name=None, image=None, pool=None, **kwargs): - ''' +def _disk_profile(profile, hypervisor, disks=None, vm_name=None, **kwargs): + """ Gather the disk profile from the config or apply the default based on the active hypervisor @@ -902,22 +915,19 @@ def _disk_profile(profile, hypervisor, disks=None, vm_name=None, image=None, poo The ``format`` and ``model`` parameters are optional, and will default to whatever is best suitable for the active hypervisor. - ''' - default = [{'system': - {'size': 8192}}] - if hypervisor == 'vmware': - overlay = {'format': 'vmdk', - 'model': 'scsi', - 'device': 'disk', - 'pool': '[{0}] '.format(pool if pool else '0')} - elif hypervisor in ['qemu', 'kvm']: - overlay = {'format': 'qcow2', - 'device': 'disk', - 'model': 'virtio'} - elif hypervisor == 'xen': - overlay = {'format': 'qcow2', - 'device': 'disk', - 'model': 'xen'} + """ + default = [{"system": {"size": 8192}}] + if hypervisor == "vmware": + overlay = { + "format": "vmdk", + "model": "scsi", + "device": "disk", + "pool": "[0] ", + } + elif hypervisor in ["qemu", "kvm"]: + overlay = {"format": "qcow2", "device": "disk", "model": "virtio"} + elif hypervisor == "xen": + overlay = {"format": "qcow2", "device": "disk", "model": "xen"} else: overlay = {} @@ -925,24 +935,17 @@ def _disk_profile(profile, hypervisor, disks=None, vm_name=None, image=None, poo disklist = [] if profile: disklist = copy.deepcopy( - __salt__['config.get']('virt:disk', {}).get(profile, default)) + __salt__["config.get"]("virt:disk", {}).get(profile, default) + ) # Transform the list to remove one level of dictionnary and add the name as a property disklist = [dict(d, name=name) for disk in disklist for name, d in disk.items()] - # Add the image to the first disk if there is one - if image: - # If image is specified in module arguments, then it will be used - # for the first disk instead of the image from the disk profile - log.debug('%s image from module arguments will be used for disk "%s"' - ' instead of %s', image, disklist[0]['name'], disklist[0].get('image', "")) - disklist[0]['image'] = image - # Merge with the user-provided disks definitions if disks: for udisk in disks: - if 'name' in udisk: - found = [disk for disk in disklist if udisk['name'] == disk['name']] + if "name" in udisk: + found = [disk for disk in disklist if udisk["name"] == disk["name"]] if found: found[0].update(udisk) else: @@ -955,59 +958,64 @@ def _disk_profile(profile, hypervisor, disks=None, vm_name=None, image=None, poo disk[key] = val # We may have an already computed source_file (i.e. image not created by our module) - if 'source_file' in disk and disk['source_file']: - disk['filename'] = os.path.basename(disk['source_file']) - elif 'source_file' not in disk: + if "source_file" in disk and disk["source_file"]: + disk["filename"] = os.path.basename(disk["source_file"]) + elif "source_file" not in disk: _fill_disk_filename(vm_name, disk, hypervisor, **kwargs) return disklist def _fill_disk_filename(vm_name, disk, hypervisor, **kwargs): - ''' + """ Compute the disk file name and update it in the disk value. - ''' - base_dir = disk.get('pool', None) - if hypervisor in ['qemu', 'kvm', 'xen']: + """ + base_dir = disk.get("pool", None) + if hypervisor in ["qemu", "kvm", "xen"]: # Compute the base directory from the pool property. We may have either a path # or a libvirt pool name there. # If the pool is a known libvirt one with a target path, use it as target path if not base_dir: base_dir = _get_images_dir() else: - if not base_dir.startswith('/'): + if not base_dir.startswith("/"): # The pool seems not to be a path, lookup for pool infos infos = pool_info(base_dir, **kwargs) pool = infos[base_dir] if base_dir in infos else None - if not pool or not pool['target_path'] or pool['target_path'].startswith('/dev'): + if ( + not pool + or not pool["target_path"] + or pool["target_path"].startswith("/dev") + ): raise CommandExecutionError( - 'Unable to create new disk {0}, specified pool {1} does not exist ' - 'or is unsupported'.format(disk['name'], base_dir)) - base_dir = pool['target_path'] + "Unable to create new disk {0}, specified pool {1} does not exist " + "or is unsupported".format(disk["name"], base_dir) + ) + base_dir = pool["target_path"] # Compute the filename and source file properties if possible if vm_name: - disk['filename'] = '{0}_{1}.{2}'.format(vm_name, disk['name'], disk['format']) - disk['source_file'] = os.path.join(base_dir, disk['filename']) + disk["filename"] = "{0}_{1}.{2}".format(vm_name, disk["name"], disk["format"]) + disk["source_file"] = os.path.join(base_dir, disk["filename"]) -def _complete_nics(interfaces, hypervisor, dmac=None): - ''' +def _complete_nics(interfaces, hypervisor): + """ Complete missing data for network interfaces. - ''' + """ - vmware_overlay = {'type': 'bridge', 'source': 'DEFAULT', 'model': 'e1000'} - kvm_overlay = {'type': 'bridge', 'source': 'br0', 'model': 'virtio'} - xen_overlay = {'type': 'bridge', 'source': 'br0', 'model': None} + vmware_overlay = {"type": "bridge", "source": "DEFAULT", "model": "e1000"} + kvm_overlay = {"type": "bridge", "source": "br0", "model": "virtio"} + xen_overlay = {"type": "bridge", "source": "br0", "model": None} overlays = { - 'xen': xen_overlay, - 'kvm': kvm_overlay, - 'qemu': kvm_overlay, - 'vmware': vmware_overlay, - } + "xen": xen_overlay, + "kvm": kvm_overlay, + "qemu": kvm_overlay, + "vmware": vmware_overlay, + } def _normalize_net_types(attributes): - ''' + """ Guess which style of definition: bridge: br0 @@ -1020,45 +1028,36 @@ def _complete_nics(interfaces, hypervisor, dmac=None): type: network source: net0 - ''' - for type_ in ['bridge', 'network']: + """ + for type_ in ["bridge", "network"]: if type_ in attributes: - attributes['type'] = type_ + attributes["type"] = type_ # we want to discard the original key - attributes['source'] = attributes.pop(type_) + attributes["source"] = attributes.pop(type_) - attributes['type'] = attributes.get('type', None) - attributes['source'] = attributes.get('source', None) + attributes["type"] = attributes.get("type", None) + attributes["source"] = attributes.get("source", None) def _apply_default_overlay(attributes): - ''' + """ Apply the default overlay to attributes - ''' + """ for key, value in six.iteritems(overlays[hypervisor]): if key not in attributes or not attributes[key]: attributes[key] = value def _assign_mac(attributes, hypervisor): - ''' + """ Compute mac address for NIC depending on hypervisor - ''' - if dmac is not None: - log.debug('Default MAC address is %s', dmac) - if salt.utils.validate.net.mac(dmac): - attributes['mac'] = dmac - else: - msg = 'Malformed MAC address: {0}'.format(dmac) - raise CommandExecutionError(msg) + """ + if hypervisor in ["qemu", "kvm"]: + attributes["mac"] = salt.utils.network.gen_mac(prefix="52:54:00") else: - if hypervisor in ['qemu', 'kvm']: - attributes['mac'] = salt.utils.network.gen_mac( - prefix='52:54:00') - else: - attributes['mac'] = salt.utils.network.gen_mac() + attributes["mac"] = salt.utils.network.gen_mac() for interface in interfaces: _normalize_net_types(interface) - if interface.get('mac', None) is None: + if interface.get("mac", None) is None: _assign_mac(interface, hypervisor) if hypervisor in overlays: _apply_default_overlay(interface) @@ -1066,38 +1065,23 @@ def _complete_nics(interfaces, hypervisor, dmac=None): return interfaces -def _nic_profile(profile_name, hypervisor, dmac=None): - ''' +def _nic_profile(profile_name, hypervisor): + """ Compute NIC data based on profile - ''' - - default = [{'eth0': {}}] - - # support old location - config_data = __salt__['config.option']('virt.nic', {}).get( - profile_name, None + """ + config_data = __salt__["config.get"]("virt:nic", {}).get( + profile_name, [{"eth0": {}}] ) - if config_data is not None: - salt.utils.versions.warn_until( - 'Sodium', - '\'virt.nic\' has been deprecated in favor of \'virt:nic\'. ' - '\'virt.nic\' will stop being used in {version}.' - ) - else: - config_data = __salt__['config.get']('virt:nic', {}).get( - profile_name, default - ) - interfaces = [] # pylint: disable=invalid-name def append_dict_profile_to_interface_list(profile_dict): - ''' + """ Append dictionary profile data to interfaces list - ''' + """ for interface_name, attributes in six.iteritems(profile_dict): - attributes['name'] = interface_name + attributes["name"] = interface_name interfaces.append(attributes) # old style dicts (top-level dicts) @@ -1134,25 +1118,24 @@ def _nic_profile(profile_name, hypervisor, dmac=None): else: interfaces.append(interface) - # dmac can only be used from init() - return _complete_nics(interfaces, hypervisor, dmac=dmac) + return _complete_nics(interfaces, hypervisor) -def _get_merged_nics(hypervisor, profile, interfaces=None, dmac=None): - ''' +def _get_merged_nics(hypervisor, profile, interfaces=None): + """ Get network devices from the profile and merge uer defined ones with them. - ''' - nicp = _nic_profile(profile, hypervisor, dmac=dmac) if profile else [] - log.debug('NIC profile is %s', nicp) + """ + nicp = _nic_profile(profile, hypervisor) if profile else [] + log.debug("NIC profile is %s", nicp) if interfaces: users_nics = _complete_nics(interfaces, hypervisor) for unic in users_nics: - found = [nic for nic in nicp if nic['name'] == unic['name']] + found = [nic for nic in nicp if nic["name"] == unic["name"]] if found: found[0].update(unic) else: nicp.append(unic) - log.debug('Merged NICs: %s', nicp) + log.debug("Merged NICs: %s", nicp) return nicp @@ -1168,62 +1151,60 @@ def _handle_remote_boot_params(orig_boot): """ saltinst_dir = None new_boot = orig_boot.copy() + keys = orig_boot.keys() + cases = [ + {"loader", "nvram"}, + {"kernel", "initrd"}, + {"kernel", "initrd", "cmdline"}, + {"loader", "nvram", "kernel", "initrd"}, + {"loader", "nvram", "kernel", "initrd", "cmdline"}, + ] try: - for key in ['kernel', 'initrd']: - if check_remote(orig_boot.get(key)): - if saltinst_dir is None: - os.makedirs(CACHE_DIR) - saltinst_dir = CACHE_DIR - - new_boot[key] = download_remote(orig_boot.get(key), - saltinst_dir) - - return new_boot + if keys in cases: + for key in keys: + if orig_boot.get(key) is not None and check_remote(orig_boot.get(key)): + if saltinst_dir is None: + os.makedirs(CACHE_DIR) + saltinst_dir = CACHE_DIR + new_boot[key] = download_remote(orig_boot.get(key), saltinst_dir) + return new_boot + else: + raise SaltInvocationError( + "Invalid boot parameters, (kernel, initrd) or/and (loader, nvram) must be both present" + ) except Exception as err: # pylint: disable=broad-except raise err -def init(name, - cpu, - mem, - image=None, - nic='default', - interfaces=None, - hypervisor=None, - start=True, # pylint: disable=redefined-outer-name - disk='default', - disks=None, - saltenv='base', - seed=True, - install=True, - pub_key=None, - priv_key=None, - seed_cmd='seed.apply', - enable_vnc=False, - enable_qcow=False, - graphics=None, - os_type=None, - arch=None, - boot=None, - **kwargs): - ''' +def init( + name, + cpu, + mem, + nic="default", + interfaces=None, + hypervisor=None, + start=True, # pylint: disable=redefined-outer-name + disk="default", + disks=None, + saltenv="base", + seed=True, + install=True, + pub_key=None, + priv_key=None, + seed_cmd="seed.apply", + graphics=None, + os_type=None, + arch=None, + boot=None, + **kwargs +): + """ Initialize a new vm :param name: name of the virtual machine to create :param cpu: Number of virtual CPUs to assign to the virtual machine :param mem: Amount of memory to allocate to the virtual machine in MiB. - :param image: Path to a disk image to use as the first disk (Default: ``None``). - Deprecated in favor of the ``disks`` parameter. To set (or change) the image of a - disk, add the following to the disks definitions: - - .. code-block:: python - - { - 'name': 'name_of_disk_to_change', - 'image': '/path/to/the/image' - } - :param nic: NIC profile to use (Default: ``'default'``). The profile interfaces can be customized / extended with the interfaces parameter. If set to ``None``, no profile will be used. @@ -1250,17 +1231,6 @@ def init(name, :param pub_key: public key to seed with (Default: ``None``) :param priv_key: public key to seed with (Default: ``None``) :param seed_cmd: Salt command to execute to seed the image. (Default: ``'seed.apply'``) - :param enable_vnc: - ``True`` to setup a vnc display for the VM (Default: ``False``) - - Deprecated in favor of the ``graphics`` parameter. Could be replaced with - the following: - - .. code-block:: python - - graphics={'type': 'vnc'} - - .. deprecated:: 2019.2.0 :param graphics: Dictionary providing details on the graphics device to create. (Default: ``None``) See :ref:`init-graphics-def` for more details on the possible values. @@ -1276,51 +1246,6 @@ def init(name, but ``x86_64`` is prefed over ``i686``. .. versionadded:: 2019.2.0 - :param enable_qcow: - ``True`` to create a QCOW2 overlay image, rather than copying the image - (Default: ``False``). - - Deprecated in favor of ``disks`` parameter. Add the following to the disks - definitions to create an overlay image of a template disk image with an - image set: - - .. code-block:: python - - { - 'name': 'name_of_disk_to_change', - 'overlay_image': True - } - - .. deprecated:: 2019.2.0 - :param pool: - Path of the folder where the image files are located for vmware/esx hypervisors. - - Deprecated in favor of ``disks`` parameter. Add the following to the disks - definitions to set the vmware datastore of a disk image: - - .. code-block:: python - - { - 'name': 'name_of_disk_to_change', - 'pool': 'mydatastore' - } - - .. deprecated:: Flurorine - :param dmac: - Default MAC address to use for the network interfaces. By default MAC addresses are - automatically generated. - - Deprecated in favor of ``interfaces`` parameter. Add the following to the interfaces - definitions to force the mac address of a NIC: - - .. code-block:: python - - { - 'name': 'name_of_nic_to_change', - 'mac': 'MY:MA:CC:ADD:RE:SS' - } - - .. deprecated:: 2019.2.0 :param config: minion configuration to use when seeding. See :mod:`seed module for more details <salt.modules.seed>` :param boot_dev: String of space-separated devices to boot from (Default: ``'hd'``) @@ -1462,116 +1387,72 @@ def init(name, .. _disk element: https://libvirt.org/formatdomain.html#elementsDisks .. _graphics element: https://libvirt.org/formatdomain.html#elementsGraphics - ''' + """ caps = capabilities(**kwargs) - os_types = sorted({guest['os_type'] for guest in caps['guests']}) - arches = sorted({guest['arch']['name'] for guest in caps['guests']}) - if not hypervisor: - hypervisor = __salt__['config.get']('libvirt:hypervisor', hypervisor) - if hypervisor is not None: - salt.utils.versions.warn_until( - 'Sodium', - '\'libvirt:hypervisor\' configuration property has been deprecated. ' - 'Rather use the \'virt:connection:uri\' to properly define the libvirt ' - 'URI or alias of the host to connect to. \'libvirt:hypervisor\' will ' - 'stop being used in {version}.' - ) - else: - # Use the machine types as possible values - # Prefer 'kvm' over the others if available - hypervisors = sorted({x for y in [guest['arch']['domains'].keys() for guest in caps['guests']] for x in y}) - hypervisor = 'kvm' if 'kvm' in hypervisors else hypervisors[0] + os_types = sorted({guest["os_type"] for guest in caps["guests"]}) + arches = sorted({guest["arch"]["name"] for guest in caps["guests"]}) - # esxi used to be a possible value for the hypervisor: map it to vmware since it's the same - hypervisor = 'vmware' if hypervisor == 'esxi' else hypervisor - - log.debug('Using hypervisor %s', hypervisor) - - # the NICs are computed as follows: - # 1 - get the default NICs from the profile - # 2 - Complete the users NICS - # 3 - Update the default NICS list to the users one, matching key is the name - dmac = kwargs.get('dmac', None) - if dmac: - salt.utils.versions.warn_until( - 'Sodium', - '\'dmac\' parameter has been deprecated. Rather use the \'interfaces\' parameter ' - 'to properly define the desired MAC address. \'dmac\' will be removed in {version}.' + virt_hypervisor = hypervisor + if not virt_hypervisor: + # Use the machine types as possible values + # Prefer "kvm" over the others if available + hypervisors = sorted( + { + x + for y in [guest["arch"]["domains"].keys() for guest in caps["guests"]] + for x in y + } ) - nicp = _get_merged_nics(hypervisor, nic, interfaces, dmac=dmac) + virt_hypervisor = "kvm" if "kvm" in hypervisors else hypervisors[0] + + # esxi used to be a possible value for the hypervisor: map it to vmware since it"s the same + virt_hypervisor = "vmware" if virt_hypervisor == "esxi" else virt_hypervisor + + log.debug("Using hypervisor %s", virt_hypervisor) + + nicp = _get_merged_nics(virt_hypervisor, nic, interfaces) # the disks are computed as follows: # 1 - get the disks defined in the profile - # 2 - set the image on the first disk (will be removed later) # 3 - update the disks from the profile with the ones from the user. The matching key is the name. - pool = kwargs.get('pool', None) - if pool: - salt.utils.versions.warn_until( - 'Sodium', - '\'pool\' parameter has been deprecated. Rather use the \'disks\' parameter ' - 'to properly define the vmware datastore of disks. \'pool\' will be removed in {version}.' - ) - - if image: - salt.utils.versions.warn_until( - 'Sodium', - '\'image\' parameter has been deprecated. Rather use the \'disks\' parameter ' - 'to override or define the image. \'image\' will be removed in {version}.' - ) - - diskp = _disk_profile(disk, hypervisor, disks, name, image=image, pool=pool, **kwargs) + diskp = _disk_profile(disk, virt_hypervisor, disks, name, **kwargs) # Create multiple disks, empty or from specified images. for _disk in diskp: log.debug("Creating disk for VM [ %s ]: %s", name, _disk) - if hypervisor == 'vmware': - if 'image' in _disk: + if virt_hypervisor == "vmware": + if "image" in _disk: # TODO: we should be copying the image file onto the ESX host raise SaltInvocationError( - 'virt.init does not support image ' - 'template in conjunction with esxi hypervisor' + "virt.init does not support image " + "template in conjunction with esxi hypervisor" ) else: # assume libvirt manages disks for us - log.debug('Generating libvirt XML for %s', _disk) + log.debug("Generating libvirt XML for %s", _disk) vol_xml = _gen_vol_xml( - name, - _disk['name'], - _disk['format'], - _disk['size'], - _disk['pool'] + name, _disk["name"], _disk["format"], _disk["size"], _disk["pool"] ) define_vol_xml_str(vol_xml) - elif hypervisor in ['qemu', 'kvm', 'xen']: - - create_overlay = enable_qcow - if create_overlay: - salt.utils.versions.warn_until( - 'Sodium', - '\'enable_qcow\' parameter has been deprecated. Rather use the \'disks\' ' - 'parameter to override or define the image. \'enable_qcow\' will be removed ' - 'in {version}.' - ) - else: - create_overlay = _disk.get('overlay_image', False) - - if _disk['source_file']: - if os.path.exists(_disk['source_file']): - img_dest = _disk['source_file'] + elif virt_hypervisor in ["qemu", "kvm", "xen"]: + create_overlay = _disk.get("overlay_image", False) + if _disk["source_file"]: + if os.path.exists(_disk["source_file"]): + img_dest = _disk["source_file"] else: img_dest = _qemu_image_create(_disk, create_overlay, saltenv) else: img_dest = None # Seed only if there is an image specified - if seed and img_dest and _disk.get('image', None): - log.debug('Seed command is %s', seed_cmd) + if seed and img_dest and _disk.get("image", None): + log.debug("Seed command is %s", seed_cmd) __salt__[seed_cmd]( img_dest, id_=name, - config=kwargs.get('config'), + config=kwargs.get("config"), install=install, pub_key=pub_key, priv_key=priv_key, @@ -1580,30 +1461,33 @@ def init(name, else: # Unknown hypervisor raise SaltInvocationError( - 'Unsupported hypervisor when handling disk image: {0}' - .format(hypervisor) + "Unsupported hypervisor when handling disk image: {0}".format( + virt_hypervisor + ) ) - log.debug('Generating VM XML') - - if enable_vnc: - salt.utils.versions.warn_until( - 'Sodium', - '\'enable_vnc\' parameter has been deprecated in favor of ' - '\'graphics\'. Use graphics={\'type\': \'vnc\'} for the same behavior. ' - '\'enable_vnc\' will be removed in {version}. ') - graphics = {'type': 'vnc'} - + log.debug("Generating VM XML") if os_type is None: - os_type = 'hvm' if 'hvm' in os_types else os_types[0] + os_type = "hvm" if "hvm" in os_types else os_types[0] if arch is None: - arch = 'x86_64' if 'x86_64' in arches else arches[0] + arch = "x86_64" if "x86_64" in arches else arches[0] if boot is not None: boot = _handle_remote_boot_params(boot) - vm_xml = _gen_xml(name, cpu, mem, diskp, nicp, hypervisor, os_type, arch, - graphics, boot, **kwargs) + vm_xml = _gen_xml( + name, + cpu, + mem, + diskp, + nicp, + virt_hypervisor, + os_type, + arch, + graphics, + boot, + **kwargs + ) conn = __get_conn(**kwargs) try: conn.defineXML(vm_xml) @@ -1617,7 +1501,7 @@ def init(name, raise err # a real error we should report upwards if start: - log.debug('Starting VM %s', name) + log.debug("Starting VM %s", name) _get_domain(conn, name).create() conn.close() @@ -1625,66 +1509,91 @@ def init(name, def _disks_equal(disk1, disk2): - ''' + """ Test if two disk elements should be considered like the same device - ''' - target1 = disk1.find('target') - target2 = disk2.find('target') - source1 = ElementTree.tostring(disk1.find('source')) if disk1.find('source') is not None else None - source2 = ElementTree.tostring(disk2.find('source')) if disk2.find('source') is not None else None + """ + target1 = disk1.find("target") + target2 = disk2.find("target") + source1 = ( + disk1.find("source") + if disk1.find("source") is not None + else ElementTree.Element("source") + ) + source2 = ( + disk2.find("source") + if disk2.find("source") is not None + else ElementTree.Element("source") + ) - return source1 == source2 and \ - target1 is not None and target2 is not None and \ - target1.get('bus') == target2.get('bus') and \ - disk1.get('device', 'disk') == disk2.get('device', 'disk') and \ - target1.get('dev') == target2.get('dev') + return ( + xmlutil.to_dict(source1, True) == xmlutil.to_dict(source2, True) + and target1 is not None + and target2 is not None + and target1.get("bus") == target2.get("bus") + and disk1.get("device", "disk") == disk2.get("device", "disk") + and target1.get("dev") == target2.get("dev") + ) def _nics_equal(nic1, nic2): - ''' + """ Test if two interface elements should be considered like the same device - ''' + """ def _filter_nic(nic): - ''' + """ Filter out elements to ignore when comparing nics - ''' + """ return { - 'type': nic.attrib['type'], - 'source': nic.find('source').attrib[nic.attrib['type']] if nic.find('source') is not None else None, - 'mac': nic.find('mac').attrib['address'].lower() if nic.find('mac') is not None else None, - 'model': nic.find('model').attrib['type'] if nic.find('model') is not None else None, + "type": nic.attrib["type"], + "source": nic.find("source").attrib[nic.attrib["type"]] + if nic.find("source") is not None + else None, + "mac": nic.find("mac").attrib["address"].lower() + if nic.find("mac") is not None + else None, + "model": nic.find("model").attrib["type"] + if nic.find("model") is not None + else None, } + return _filter_nic(nic1) == _filter_nic(nic2) def _graphics_equal(gfx1, gfx2): - ''' + """ Test if two graphics devices should be considered the same device - ''' + """ + def _filter_graphics(gfx): - ''' + """ When the domain is running, the graphics element may contain additional properties with the default values. This function will strip down the default values. - ''' + """ gfx_copy = copy.deepcopy(gfx) - defaults = [{'node': '.', 'attrib': 'port', 'values': ['5900', '-1']}, - {'node': '.', 'attrib': 'address', 'values': ['127.0.0.1']}, - {'node': 'listen', 'attrib': 'address', 'values': ['127.0.0.1']}] + defaults = [ + {"node": ".", "attrib": "port", "values": ["5900", "-1"]}, + {"node": ".", "attrib": "address", "values": ["127.0.0.1"]}, + {"node": "listen", "attrib": "address", "values": ["127.0.0.1"]}, + ] for default in defaults: - node = gfx_copy.find(default['node']) - attrib = default['attrib'] - if node is not None and (attrib not in node.attrib or node.attrib[attrib] in default['values']): - node.set(attrib, default['values'][0]) + node = gfx_copy.find(default["node"]) + attrib = default["attrib"] + if node is not None and ( + attrib in node.attrib and node.attrib[attrib] in default["values"] + ): + node.attrib.pop(attrib) return gfx_copy - return ElementTree.tostring(_filter_graphics(gfx1)) == ElementTree.tostring(_filter_graphics(gfx2)) + return xmlutil.to_dict(_filter_graphics(gfx1), True) == xmlutil.to_dict( + _filter_graphics(gfx2), True + ) def _diff_lists(old, new, comparator): - ''' + """ Compare lists to extract the changes :param old: old list @@ -1693,98 +1602,108 @@ def _diff_lists(old, new, comparator): The sorted list is the union of unchanged and new lists, but keeping the original order from the new list. - ''' + """ + def _remove_indent(node): - ''' + """ Remove the XML indentation to compare XML trees more easily - ''' + """ node_copy = copy.deepcopy(node) node_copy.text = None for item in node_copy.iter(): item.tail = None return node_copy - diff = {'unchanged': [], 'new': [], 'deleted': [], 'sorted': []} + diff = {"unchanged": [], "new": [], "deleted": [], "sorted": []} # We don't want to alter old since it may be used later by caller old_devices = copy.deepcopy(old) for new_item in new: - found = [item for item in old_devices if comparator(_remove_indent(item), _remove_indent(new_item))] + found = [ + item + for item in old_devices + if comparator(_remove_indent(item), _remove_indent(new_item)) + ] if found: old_devices.remove(found[0]) - diff['unchanged'].append(found[0]) - diff['sorted'].append(found[0]) + diff["unchanged"].append(found[0]) + diff["sorted"].append(found[0]) else: - diff['new'].append(new_item) - diff['sorted'].append(new_item) - diff['deleted'] = old_devices + diff["new"].append(new_item) + diff["sorted"].append(new_item) + diff["deleted"] = old_devices return diff def _diff_disk_lists(old, new): - ''' + """ Compare disk definitions to extract the changes and fix target devices :param old: list of ElementTree nodes representing the old disks :param new: list of ElementTree nodes representing the new disks - ''' + """ # Change the target device to avoid duplicates before diffing: this may lead # to additional changes. Think of unchanged disk 'hda' and another disk listed # before it becoming 'hda' too... the unchanged need to turn into 'hdb'. targets = [] - prefixes = ['fd', 'hd', 'vd', 'sd', 'xvd', 'ubd'] + prefixes = ["fd", "hd", "vd", "sd", "xvd", "ubd"] for disk in new: - target_node = disk.find('target') - target = target_node.get('dev') + target_node = disk.find("target") + target = target_node.get("dev") prefix = [item for item in prefixes if target.startswith(item)][0] - new_target = ['{0}{1}'.format(prefix, string.ascii_lowercase[i]) for i in range(len(new)) - if '{0}{1}'.format(prefix, string.ascii_lowercase[i]) not in targets][0] - target_node.set('dev', new_target) + new_target = [ + "{0}{1}".format(prefix, string.ascii_lowercase[i]) + for i in range(len(new)) + if "{0}{1}".format(prefix, string.ascii_lowercase[i]) not in targets + ][0] + target_node.set("dev", new_target) targets.append(new_target) return _diff_lists(old, new, _disks_equal) def _diff_interface_lists(old, new): - ''' + """ Compare network interface definitions to extract the changes :param old: list of ElementTree nodes representing the old interfaces :param new: list of ElementTree nodes representing the new interfaces - ''' + """ diff = _diff_lists(old, new, _nics_equal) # Remove duplicated addresses mac addresses and let libvirt generate them for us - macs = [nic.find('mac').get('address') for nic in diff['unchanged']] - for nic in diff['new']: - mac = nic.find('mac') - if mac.get('address') in macs: + macs = [nic.find("mac").get("address") for nic in diff["unchanged"]] + for nic in diff["new"]: + mac = nic.find("mac") + if mac.get("address") in macs: nic.remove(mac) return diff def _diff_graphics_lists(old, new): - ''' + """ Compare graphic devices definitions to extract the changes :param old: list of ElementTree nodes representing the old graphic devices :param new: list of ElementTree nodes representing the new graphic devices - ''' + """ return _diff_lists(old, new, _graphics_equal) -def update(name, - cpu=0, - mem=0, - disk_profile=None, - disks=None, - nic_profile=None, - interfaces=None, - graphics=None, - live=True, - boot=None, - **kwargs): - ''' +def update( + name, + cpu=0, + mem=0, + disk_profile=None, + disks=None, + nic_profile=None, + interfaces=None, + graphics=None, + live=True, + boot=None, + **kwargs +): + """ Update the definition of an existing domain. :param name: Name of the domain to update @@ -1823,7 +1742,9 @@ def update(name, for the virtual machine. This is an optionl parameter, and all of the keys are optional within the dictionary. If a remote path is provided to kernel or initrd, salt will handle the downloading of the specified - remote fild, and will modify the XML accordingly. + remote fild, and will modify the XML accordingly.Boot vm with UEFI, + loader and nvram can be specified to achieve the goal. To remove any + boot parameters, pass an None object to the relevant parameter, for instance: "kernel": None .. code-block:: python @@ -1831,6 +1752,8 @@ def update(name, 'kernel': '/root/f8-i386-vmlinuz', 'initrd': '/root/f8-i386-initrd', 'cmdline': 'console=ttyS0 ks=http://example.com/f8-i386/os/' + 'loader': /usr/share/OVMF/OVMF_CODE.fd + 'nvram': /usr/share/OVMF/OVMF_VARS.ms.fd } .. versionadded:: 3000 @@ -1861,11 +1784,11 @@ def update(name, salt '*' virt.update domain cpu=2 mem=1024 - ''' + """ status = { - 'definition': False, - 'disk': {'attached': [], 'detached': []}, - 'interface': {'attached': [], 'detached': []} + "definition": False, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, } conn = __get_conn(**kwargs) domain = _get_domain(conn, name) @@ -1873,56 +1796,67 @@ def update(name, need_update = False # Compute the XML to get the disks, interfaces and graphics - hypervisor = desc.get('type') + hypervisor = desc.get("type") all_disks = _disk_profile(disk_profile, hypervisor, disks, name, **kwargs) if boot is not None: boot = _handle_remote_boot_params(boot) - new_desc = ElementTree.fromstring(_gen_xml(name, - cpu, - mem, - all_disks, - _get_merged_nics(hypervisor, nic_profile, interfaces), - hypervisor, - domain.OSType(), - desc.find('.//os/type').get('arch'), - graphics, - boot, - **kwargs)) + new_desc = ElementTree.fromstring( + _gen_xml( + name, + cpu, + mem, + all_disks, + _get_merged_nics(hypervisor, nic_profile, interfaces), + hypervisor, + domain.OSType(), + desc.find(".//os/type").get("arch"), + graphics, + boot, + **kwargs + ) + ) # Update the cpu - cpu_node = desc.find('vcpu') + cpu_node = desc.find("vcpu") if cpu and int(cpu_node.text) != cpu: cpu_node.text = six.text_type(cpu) - cpu_node.set('current', six.text_type(cpu)) + cpu_node.set("current", six.text_type(cpu)) need_update = True # Update the kernel boot parameters - boot_tags = ['kernel', 'initrd', 'cmdline'] - parent_tag = desc.find('os') + boot_tags = ["kernel", "initrd", "cmdline", "loader", "nvram"] + parent_tag = desc.find("os") # We need to search for each possible subelement, and update it. for tag in boot_tags: # The Existing Tag... - found_tag = desc.find(tag) + found_tag = parent_tag.find(tag) # The new value boot_tag_value = boot.get(tag, None) if boot else None # Existing tag is found and values don't match - if found_tag and found_tag.text != boot_tag_value: + if found_tag is not None and found_tag.text != boot_tag_value: # If the existing tag is found, but the new value is None # remove it. If the existing tag is found, and the new value # doesn't match update it. In either case, mark for update. - if boot_tag_value is None \ - and boot is not None \ - and parent_tag is not None: - ElementTree.remove(parent_tag, tag) + if boot_tag_value is None and boot is not None and parent_tag is not None: + parent_tag.remove(found_tag) else: found_tag.text = boot_tag_value + # If the existing tag is loader or nvram, we need to update the corresponding attribute + if found_tag.tag == "loader" and boot_tag_value is not None: + found_tag.set("readonly", "yes") + found_tag.set("type", "pflash") + + if found_tag.tag == "nvram" and boot_tag_value is not None: + found_tag.set("template", found_tag.text) + found_tag.text = None + need_update = True # Existing tag is not found, but value is not None @@ -1934,51 +1868,71 @@ def update(name, if parent_tag is not None: child_tag = ElementTree.SubElement(parent_tag, tag) else: - new_parent_tag = ElementTree.Element('os') + new_parent_tag = ElementTree.Element("os") child_tag = ElementTree.SubElement(new_parent_tag, tag) child_tag.text = boot_tag_value + + # If the newly created tag is loader or nvram, we need to update the corresponding attribute + if child_tag.tag == "loader": + child_tag.set("readonly", "yes") + child_tag.set("type", "pflash") + + if child_tag.tag == "nvram": + child_tag.set("template", child_tag.text) + child_tag.text = None + need_update = True # Update the memory, note that libvirt outputs all memory sizes in KiB - for mem_node_name in ['memory', 'currentMemory']: + for mem_node_name in ["memory", "currentMemory"]: mem_node = desc.find(mem_node_name) if mem and int(mem_node.text) != mem * 1024: mem_node.text = six.text_type(mem) - mem_node.set('unit', 'MiB') + mem_node.set("unit", "MiB") need_update = True # Update the XML definition with the new disks and diff changes - devices_node = desc.find('devices') - parameters = {'disk': ['disks', 'disk_profile'], - 'interface': ['interfaces', 'nic_profile'], - 'graphics': ['graphics']} + devices_node = desc.find("devices") + parameters = { + "disk": ["disks", "disk_profile"], + "interface": ["interfaces", "nic_profile"], + "graphics": ["graphics"], + } changes = {} for dev_type in parameters: changes[dev_type] = {} func_locals = locals() - if [param for param in parameters[dev_type] if func_locals.get(param, None) is not None]: + if [ + param + for param in parameters[dev_type] + if func_locals.get(param, None) is not None + ]: old = devices_node.findall(dev_type) - new = new_desc.findall('devices/{0}'.format(dev_type)) - changes[dev_type] = globals()['_diff_{0}_lists'.format(dev_type)](old, new) - if changes[dev_type]['deleted'] or changes[dev_type]['new']: + new = new_desc.findall("devices/{0}".format(dev_type)) + changes[dev_type] = globals()["_diff_{0}_lists".format(dev_type)](old, new) + if changes[dev_type]["deleted"] or changes[dev_type]["new"]: for item in old: devices_node.remove(item) - devices_node.extend(changes[dev_type]['sorted']) + devices_node.extend(changes[dev_type]["sorted"]) need_update = True # Set the new definition if need_update: # Create missing disks if needed - if changes['disk']: - for idx, item in enumerate(changes['disk']['sorted']): - source_file = all_disks[idx]['source_file'] - if item in changes['disk']['new'] and source_file and not os.path.isfile(source_file): + if changes["disk"]: + for idx, item in enumerate(changes["disk"]["sorted"]): + source_file = all_disks[idx]["source_file"] + if ( + item in changes["disk"]["new"] + and source_file + and not os.path.isfile(source_file) + ): _qemu_image_create(all_disks[idx]) try: conn.defineXML(salt.utils.stringutils.to_str(ElementTree.tostring(desc))) - status['definition'] = True + status["definition"] = True except libvirt.libvirtError as err: conn.close() raise err @@ -1989,46 +1943,70 @@ def update(name, commands = [] if domain.isActive() and live: if cpu: - commands.append({'device': 'cpu', - 'cmd': 'setVcpusFlags', - 'args': [cpu, libvirt.VIR_DOMAIN_AFFECT_LIVE]}) + commands.append( + { + "device": "cpu", + "cmd": "setVcpusFlags", + "args": [cpu, libvirt.VIR_DOMAIN_AFFECT_LIVE], + } + ) if mem: - commands.append({'device': 'mem', - 'cmd': 'setMemoryFlags', - 'args': [mem * 1024, libvirt.VIR_DOMAIN_AFFECT_LIVE]}) + commands.append( + { + "device": "mem", + "cmd": "setMemoryFlags", + "args": [mem * 1024, libvirt.VIR_DOMAIN_AFFECT_LIVE], + } + ) - for dev_type in ['disk', 'interface']: - for added in changes[dev_type].get('new', []): - commands.append({'device': dev_type, - 'cmd': 'attachDevice', - 'args': [salt.utils.stringutils.to_str(ElementTree.tostring(added))]}) + for dev_type in ["disk", "interface"]: + for added in changes[dev_type].get("new", []): + commands.append( + { + "device": dev_type, + "cmd": "attachDevice", + "args": [ + salt.utils.stringutils.to_str( + ElementTree.tostring(added) + ) + ], + } + ) - for removed in changes[dev_type].get('deleted', []): - commands.append({'device': dev_type, - 'cmd': 'detachDevice', - 'args': [salt.utils.stringutils.to_str(ElementTree.tostring(removed))]}) + for removed in changes[dev_type].get("deleted", []): + commands.append( + { + "device": dev_type, + "cmd": "detachDevice", + "args": [ + salt.utils.stringutils.to_str( + ElementTree.tostring(removed) + ) + ], + } + ) for cmd in commands: try: - ret = getattr(domain, cmd['cmd'])(*cmd['args']) - device_type = cmd['device'] - if device_type in ['cpu', 'mem']: + ret = getattr(domain, cmd["cmd"])(*cmd["args"]) + device_type = cmd["device"] + if device_type in ["cpu", "mem"]: status[device_type] = not bool(ret) else: - actions = {'attachDevice': 'attached', 'detachDevice': 'detached'} - status[device_type][actions[cmd['cmd']]].append(cmd['args'][0]) + actions = {"attachDevice": "attached", "detachDevice": "detached"} + status[device_type][actions[cmd["cmd"]]].append(cmd["args"][0]) except libvirt.libvirtError as err: - if 'errors' not in status: - status['errors'] = [] - status['errors'].append(six.text_type(err)) + if "errors" not in status: + status["errors"] = [] + status["errors"].append(six.text_type(err)) conn.close() return status def list_domains(**kwargs): - ''' + """ Return a list of available domains. :param connection: libvirt connection URI, overriding defaults @@ -2046,7 +2024,7 @@ def list_domains(**kwargs): .. code-block:: bash salt '*' virt.list_domains - ''' + """ vms = [] conn = __get_conn(**kwargs) for dom in _get_domain(conn, iterable=True): @@ -2056,7 +2034,7 @@ def list_domains(**kwargs): def list_active_vms(**kwargs): - ''' + """ Return a list of names for active virtual machine on the minion :param connection: libvirt connection URI, overriding defaults @@ -2074,7 +2052,7 @@ def list_active_vms(**kwargs): .. code-block:: bash salt '*' virt.list_active_vms - ''' + """ vms = [] conn = __get_conn(**kwargs) for dom in _get_domain(conn, iterable=True, inactive=False): @@ -2084,7 +2062,7 @@ def list_active_vms(**kwargs): def list_inactive_vms(**kwargs): - ''' + """ Return a list of names for inactive virtual machine on the minion :param connection: libvirt connection URI, overriding defaults @@ -2102,7 +2080,7 @@ def list_inactive_vms(**kwargs): .. code-block:: bash salt '*' virt.list_inactive_vms - ''' + """ vms = [] conn = __get_conn(**kwargs) for dom in _get_domain(conn, iterable=True, active=False): @@ -2112,7 +2090,7 @@ def list_inactive_vms(**kwargs): def vm_info(vm_=None, **kwargs): - ''' + """ Return detailed information about the vms on this hyper in a list of dicts: @@ -2148,24 +2126,28 @@ def vm_info(vm_=None, **kwargs): .. code-block:: bash salt '*' virt.vm_info - ''' + """ + def _info(dom): - ''' + """ Compute the infos of a domain - ''' + """ raw = dom.info() - return {'cpu': raw[3], - 'cputime': int(raw[4]), - 'disks': _get_disks(dom), - 'graphics': _get_graphics(dom), - 'nics': _get_nics(dom), - 'uuid': _get_uuid(dom), - 'on_crash': _get_on_crash(dom), - 'on_reboot': _get_on_reboot(dom), - 'on_poweroff': _get_on_poweroff(dom), - 'maxMem': int(raw[1]), - 'mem': int(raw[2]), - 'state': VIRT_STATE_NAME_MAP.get(raw[0], 'unknown')} + return { + "cpu": raw[3], + "cputime": int(raw[4]), + "disks": _get_disks(dom), + "graphics": _get_graphics(dom), + "nics": _get_nics(dom), + "uuid": _get_uuid(dom), + "on_crash": _get_on_crash(dom), + "on_reboot": _get_on_reboot(dom), + "on_poweroff": _get_on_poweroff(dom), + "maxMem": int(raw[1]), + "mem": int(raw[2]), + "state": VIRT_STATE_NAME_MAP.get(raw[0], "unknown"), + } + info = {} conn = __get_conn(**kwargs) if vm_: @@ -2178,7 +2160,7 @@ def vm_info(vm_=None, **kwargs): def vm_state(vm_=None, **kwargs): - ''' + """ Return list of all the vms and their state. If you pass a VM name in as an argument then it will return info @@ -2200,15 +2182,17 @@ def vm_state(vm_=None, **kwargs): .. code-block:: bash salt '*' virt.vm_state <domain> - ''' + """ + def _info(dom): - ''' + """ Compute domain state - ''' - state = '' + """ + state = "" raw = dom.info() - state = VIRT_STATE_NAME_MAP.get(raw[0], 'unknown') + state = VIRT_STATE_NAME_MAP.get(raw[0], "unknown") return state + info = {} conn = __get_conn(**kwargs) if vm_: @@ -2221,23 +2205,25 @@ def vm_state(vm_=None, **kwargs): def _node_info(conn): - ''' + """ Internal variant of node_info taking a libvirt connection as parameter - ''' + """ raw = conn.getInfo() - info = {'cpucores': raw[6], - 'cpumhz': raw[3], - 'cpumodel': six.text_type(raw[0]), - 'cpus': raw[2], - 'cputhreads': raw[7], - 'numanodes': raw[4], - 'phymemory': raw[1], - 'sockets': raw[5]} + info = { + "cpucores": raw[6], + "cpumhz": raw[3], + "cpumodel": six.text_type(raw[0]), + "cpus": raw[2], + "cputhreads": raw[7], + "numanodes": raw[4], + "phymemory": raw[1], + "sockets": raw[5], + } return info def node_info(**kwargs): - ''' + """ Return a dict with information about this node :param connection: libvirt connection URI, overriding defaults @@ -2255,7 +2241,7 @@ def node_info(**kwargs): .. code-block:: bash salt '*' virt.node_info - ''' + """ conn = __get_conn(**kwargs) info = _node_info(conn) conn.close() @@ -2263,7 +2249,7 @@ def node_info(**kwargs): def get_nics(vm_, **kwargs): - ''' + """ Return info about the network interfaces of a named vm :param vm_: name of the domain @@ -2282,7 +2268,7 @@ def get_nics(vm_, **kwargs): .. code-block:: bash salt '*' virt.get_nics <domain> - ''' + """ conn = __get_conn(**kwargs) nics = _get_nics(_get_domain(conn, vm_)) conn.close() @@ -2290,7 +2276,7 @@ def get_nics(vm_, **kwargs): def get_macs(vm_, **kwargs): - ''' + """ Return a list off MAC addresses from the named vm :param vm_: name of the domain @@ -2309,13 +2295,13 @@ def get_macs(vm_, **kwargs): .. code-block:: bash salt '*' virt.get_macs <domain> - ''' + """ doc = ElementTree.fromstring(get_xml(vm_, **kwargs)) - return [node.get('address') for node in doc.findall('devices/interface/mac')] + return [node.get("address") for node in doc.findall("devices/interface/mac")] def get_graphics(vm_, **kwargs): - ''' + """ Returns the information on vnc for a given vm :param vm_: name of the domain @@ -2334,7 +2320,7 @@ def get_graphics(vm_, **kwargs): .. code-block:: bash salt '*' virt.get_graphics <domain> - ''' + """ conn = __get_conn(**kwargs) graphics = _get_graphics(_get_domain(conn, vm_)) conn.close() @@ -2342,7 +2328,7 @@ def get_graphics(vm_, **kwargs): def get_disks(vm_, **kwargs): - ''' + """ Return the disks of a named vm :param vm_: name of the domain @@ -2361,7 +2347,7 @@ def get_disks(vm_, **kwargs): .. code-block:: bash salt '*' virt.get_disks <domain> - ''' + """ conn = __get_conn(**kwargs) disks = _get_disks(_get_domain(conn, vm_)) conn.close() @@ -2369,7 +2355,7 @@ def get_disks(vm_, **kwargs): def setmem(vm_, memory, config=False, **kwargs): - ''' + """ Changes the amount of memory allocated to VM. The VM must be shutdown for this to work. @@ -2392,11 +2378,11 @@ def setmem(vm_, memory, config=False, **kwargs): salt '*' virt.setmem <domain> <size> salt '*' virt.setmem my_domain 768 - ''' + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) - if VIRT_STATE_NAME_MAP.get(dom.info()[0], 'unknown') != 'shutdown': + if VIRT_STATE_NAME_MAP.get(dom.info()[0], "unknown") != "shutdown": return False # libvirt has a funny bitwise system for the flags in that the flag @@ -2416,7 +2402,7 @@ def setmem(vm_, memory, config=False, **kwargs): def setvcpus(vm_, vcpus, config=False, **kwargs): - ''' + """ Changes the amount of vcpus allocated to VM. The VM must be shutdown for this to work. @@ -2441,11 +2427,11 @@ def setvcpus(vm_, vcpus, config=False, **kwargs): salt '*' virt.setvcpus <domain> <amount> salt '*' virt.setvcpus my_domain 4 - ''' + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) - if VIRT_STATE_NAME_MAP.get(dom.info()[0], 'unknown') != 'shutdown': + if VIRT_STATE_NAME_MAP.get(dom.info()[0], "unknown") != "shutdown": return False # see notes in setmem @@ -2462,9 +2448,9 @@ def setvcpus(vm_, vcpus, config=False, **kwargs): def _freemem(conn): - ''' + """ Internal variant of freemem taking a libvirt connection as parameter - ''' + """ mem = conn.getInfo()[1] # Take off just enough to sustain the hypervisor mem -= 256 @@ -2475,7 +2461,7 @@ def _freemem(conn): def freemem(**kwargs): - ''' + """ Return an int representing the amount of memory (in MB) that has not been given to virtual machines on this node @@ -2494,7 +2480,7 @@ def freemem(**kwargs): .. code-block:: bash salt '*' virt.freemem - ''' + """ conn = __get_conn(**kwargs) mem = _freemem(conn) conn.close() @@ -2502,9 +2488,9 @@ def freemem(**kwargs): def _freecpu(conn): - ''' + """ Internal variant of freecpu taking a libvirt connection as parameter - ''' + """ cpus = conn.getInfo()[2] for dom in _get_domain(conn, iterable=True): if dom.ID() > 0: @@ -2513,7 +2499,7 @@ def _freecpu(conn): def freecpu(**kwargs): - ''' + """ Return an int representing the number of unallocated cpus on this hypervisor @@ -2532,7 +2518,7 @@ def freecpu(**kwargs): .. code-block:: bash salt '*' virt.freecpu - ''' + """ conn = __get_conn(**kwargs) cpus = _freecpu(conn) conn.close() @@ -2540,7 +2526,7 @@ def freecpu(**kwargs): def full_info(**kwargs): - ''' + """ Return the node_info, vm_info and freemem :param connection: libvirt connection URI, overriding defaults @@ -2558,18 +2544,20 @@ def full_info(**kwargs): .. code-block:: bash salt '*' virt.full_info - ''' + """ conn = __get_conn(**kwargs) - info = {'freecpu': _freecpu(conn), - 'freemem': _freemem(conn), - 'node_info': _node_info(conn), - 'vm_info': vm_info()} + info = { + "freecpu": _freecpu(conn), + "freemem": _freemem(conn), + "node_info": _node_info(conn), + "vm_info": vm_info(), + } conn.close() return info def get_xml(vm_, **kwargs): - ''' + """ Returns the XML for a given vm :param vm_: domain name @@ -2588,17 +2576,19 @@ def get_xml(vm_, **kwargs): .. code-block:: bash salt '*' virt.get_xml <domain> - ''' + """ conn = __get_conn(**kwargs) - xml_desc = vm_.XMLDesc(0) if isinstance( - vm_, libvirt.virDomain - ) else _get_domain(conn, vm_).XMLDesc(0) + xml_desc = ( + vm_.XMLDesc(0) + if isinstance(vm_, libvirt.virDomain) + else _get_domain(conn, vm_).XMLDesc(0) + ) conn.close() return xml_desc def get_profiles(hypervisor=None, **kwargs): - ''' + """ Return the virt profiles for hypervisor. Currently there are profiles for: @@ -2623,40 +2613,42 @@ def get_profiles(hypervisor=None, **kwargs): salt '*' virt.get_profiles salt '*' virt.get_profiles hypervisor=esxi - ''' + """ ret = {} + # Use the machine types as possible values + # Prefer 'kvm' over the others if available caps = capabilities(**kwargs) - hypervisors = sorted({x for y in [guest['arch']['domains'].keys() for guest in caps['guests']] for x in y}) - default_hypervisor = 'kvm' if 'kvm' in hypervisors else hypervisors[0] + hypervisors = sorted( + { + x + for y in [guest["arch"]["domains"].keys() for guest in caps["guests"]] + for x in y + } + ) + default_hypervisor = "kvm" if "kvm" in hypervisors else hypervisors[0] if not hypervisor: - hypervisor = __salt__['config.get']('libvirt:hypervisor') - if hypervisor is not None: - salt.utils.versions.warn_until( - 'Sodium', - '\'libvirt:hypervisor\' configuration property has been deprecated. ' - 'Rather use the \'virt:connection:uri\' to properly define the libvirt ' - 'URI or alias of the host to connect to. \'libvirt:hypervisor\' will ' - 'stop being used in {version}.' + hypervisor = default_hypervisor + virtconf = __salt__["config.get"]("virt", {}) + for typ in ["disk", "nic"]: + _func = getattr(sys.modules[__name__], "_{0}_profile".format(typ)) + ret[typ] = { + "default": _func( + "default", hypervisor if hypervisor else default_hypervisor ) - else: - # Use the machine types as possible values - # Prefer 'kvm' over the others if available - hypervisor = default_hypervisor - virtconf = __salt__['config.get']('virt', {}) - for typ in ['disk', 'nic']: - _func = getattr(sys.modules[__name__], '_{0}_profile'.format(typ)) - ret[typ] = {'default': _func('default', hypervisor)} + } if typ in virtconf: ret.setdefault(typ, {}) for prf in virtconf[typ]: - ret[typ][prf] = _func(prf, hypervisor) + ret[typ][prf] = _func( + prf, hypervisor if hypervisor else default_hypervisor + ) return ret def shutdown(vm_, **kwargs): - ''' + """ Send a soft shutdown signal to the named vm :param vm_: domain name @@ -2675,7 +2667,7 @@ def shutdown(vm_, **kwargs): .. code-block:: bash salt '*' virt.shutdown <domain> - ''' + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) ret = dom.shutdown() == 0 @@ -2684,7 +2676,7 @@ def shutdown(vm_, **kwargs): def pause(vm_, **kwargs): - ''' + """ Pause the named vm :param vm_: domain name @@ -2703,7 +2695,7 @@ def pause(vm_, **kwargs): .. code-block:: bash salt '*' virt.pause <domain> - ''' + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) ret = dom.suspend() == 0 @@ -2712,7 +2704,7 @@ def pause(vm_, **kwargs): def resume(vm_, **kwargs): - ''' + """ Resume the named vm :param vm_: domain name @@ -2731,7 +2723,7 @@ def resume(vm_, **kwargs): .. code-block:: bash salt '*' virt.resume <domain> - ''' + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) ret = dom.resume() == 0 @@ -2740,7 +2732,7 @@ def resume(vm_, **kwargs): def start(name, **kwargs): - ''' + """ Start a defined domain :param vm_: domain name @@ -2759,7 +2751,7 @@ def start(name, **kwargs): .. code-block:: bash salt '*' virt.start <domain> - ''' + """ conn = __get_conn(**kwargs) ret = _get_domain(conn, name).create() == 0 conn.close() @@ -2767,7 +2759,7 @@ def start(name, **kwargs): def stop(name, **kwargs): - ''' + """ Hard power down the virtual machine, this is equivalent to pulling the power. :param vm_: domain name @@ -2786,7 +2778,7 @@ def stop(name, **kwargs): .. code-block:: bash salt '*' virt.stop <domain> - ''' + """ conn = __get_conn(**kwargs) ret = _get_domain(conn, name).destroy() == 0 conn.close() @@ -2794,7 +2786,7 @@ def stop(name, **kwargs): def reboot(name, **kwargs): - ''' + """ Reboot a domain via ACPI request :param vm_: domain name @@ -2813,7 +2805,7 @@ def reboot(name, **kwargs): .. code-block:: bash salt '*' virt.reboot <domain> - ''' + """ conn = __get_conn(**kwargs) ret = _get_domain(conn, name).reboot(libvirt.VIR_DOMAIN_REBOOT_DEFAULT) == 0 conn.close() @@ -2821,7 +2813,7 @@ def reboot(name, **kwargs): def reset(vm_, **kwargs): - ''' + """ Reset a VM by emulating the reset button on a physical machine :param vm_: domain name @@ -2840,7 +2832,7 @@ def reset(vm_, **kwargs): .. code-block:: bash salt '*' virt.reset <domain> - ''' + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) @@ -2853,7 +2845,7 @@ def reset(vm_, **kwargs): def ctrl_alt_del(vm_, **kwargs): - ''' + """ Sends CTRL+ALT+DEL to a VM :param vm_: domain name @@ -2872,7 +2864,7 @@ def ctrl_alt_del(vm_, **kwargs): .. code-block:: bash salt '*' virt.ctrl_alt_del <domain> - ''' + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) ret = dom.sendKey(0, 0, [29, 56, 111], 3, 0) == 0 @@ -2881,7 +2873,7 @@ def ctrl_alt_del(vm_, **kwargs): def create_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name - ''' + """ Start a transient domain based on the XML passed to the function :param xml: libvirt XML definition of the domain @@ -2900,7 +2892,7 @@ def create_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name .. code-block:: bash salt '*' virt.create_xml_str <XML in string format> - ''' + """ conn = __get_conn(**kwargs) ret = conn.createXML(xml, 0) is not None conn.close() @@ -2908,7 +2900,7 @@ def create_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name def create_xml_path(path, **kwargs): - ''' + """ Start a transient domain based on the XML-file path passed to the function :param path: path to a file containing the libvirt XML definition of the domain @@ -2927,19 +2919,18 @@ def create_xml_path(path, **kwargs): .. code-block:: bash salt '*' virt.create_xml_path <path to XML file on the node> - ''' + """ try: - with salt.utils.files.fopen(path, 'r') as fp_: + with salt.utils.files.fopen(path, "r") as fp_: return create_xml_str( - salt.utils.stringutils.to_unicode(fp_.read()), - **kwargs + salt.utils.stringutils.to_unicode(fp_.read()), **kwargs ) except (OSError, IOError): return False def define_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name - ''' + """ Define a persistent domain based on the XML passed to the function :param xml: libvirt XML definition of the domain @@ -2958,7 +2949,7 @@ def define_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name .. code-block:: bash salt '*' virt.define_xml_str <XML in string format> - ''' + """ conn = __get_conn(**kwargs) ret = conn.defineXML(xml) is not None conn.close() @@ -2966,7 +2957,7 @@ def define_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name def define_xml_path(path, **kwargs): - ''' + """ Define a persistent domain based on the XML-file path passed to the function :param path: path to a file containing the libvirt XML definition of the domain @@ -2986,19 +2977,18 @@ def define_xml_path(path, **kwargs): salt '*' virt.define_xml_path <path to XML file on the node> - ''' + """ try: - with salt.utils.files.fopen(path, 'r') as fp_: + with salt.utils.files.fopen(path, "r") as fp_: return define_xml_str( - salt.utils.stringutils.to_unicode(fp_.read()), - **kwargs + salt.utils.stringutils.to_unicode(fp_.read()), **kwargs ) except (OSError, IOError): return False def define_vol_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name - ''' + """ Define a volume based on the XML passed to the function :param xml: libvirt XML definition of the storage volume @@ -3025,18 +3015,8 @@ def define_vol_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name virt: storagepool: mine - ''' - poolname = __salt__['config.get']('libvirt:storagepool', None) - if poolname is not None: - salt.utils.versions.warn_until( - 'Sodium', - '\'libvirt:storagepool\' has been deprecated in favor of ' - '\'virt:storagepool\'. \'libvirt:storagepool\' will stop ' - 'being used in {version}.' - ) - else: - poolname = __salt__['config.get']('virt:storagepool', 'default') - + """ + poolname = __salt__["config.get"]("virt:storagepool", "default") conn = __get_conn(**kwargs) pool = conn.storagePoolLookupByName(six.text_type(poolname)) ret = pool.createXML(xml, 0) is not None @@ -3045,7 +3025,7 @@ def define_vol_xml_str(xml, **kwargs): # pylint: disable=redefined-outer-name def define_vol_xml_path(path, **kwargs): - ''' + """ Define a volume based on the XML-file path passed to the function :param path: path to a file containing the libvirt XML definition of the volume @@ -3065,19 +3045,18 @@ def define_vol_xml_path(path, **kwargs): salt '*' virt.define_vol_xml_path <path to XML file on the node> - ''' + """ try: - with salt.utils.files.fopen(path, 'r') as fp_: + with salt.utils.files.fopen(path, "r") as fp_: return define_vol_xml_str( - salt.utils.stringutils.to_unicode(fp_.read()), - **kwargs + salt.utils.stringutils.to_unicode(fp_.read()), **kwargs ) except (OSError, IOError): return False def migrate_non_shared(vm_, target, ssh=False): - ''' + """ Attempt to execute non-shared storage "all" migration :param vm_: domain name @@ -3100,18 +3079,17 @@ def migrate_non_shared(vm_, target, ssh=False): For more details on tunnelled data migrations, report to https://libvirt.org/migration.html#transporttunnel - ''' - cmd = _get_migrate_command() + ' --copy-storage-all ' + vm_\ - + _get_target(target, ssh) + """ + cmd = ( + _get_migrate_command() + " --copy-storage-all " + vm_ + _get_target(target, ssh) + ) - stdout = subprocess.Popen(cmd, - shell=True, - stdout=subprocess.PIPE).communicate()[0] + stdout = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0] return salt.utils.stringutils.to_str(stdout) def migrate_non_shared_inc(vm_, target, ssh=False): - ''' + """ Attempt to execute non-shared storage "all" migration :param vm_: domain name @@ -3134,18 +3112,17 @@ def migrate_non_shared_inc(vm_, target, ssh=False): For more details on tunnelled data migrations, report to https://libvirt.org/migration.html#transporttunnel - ''' - cmd = _get_migrate_command() + ' --copy-storage-inc ' + vm_\ - + _get_target(target, ssh) + """ + cmd = ( + _get_migrate_command() + " --copy-storage-inc " + vm_ + _get_target(target, ssh) + ) - stdout = subprocess.Popen(cmd, - shell=True, - stdout=subprocess.PIPE).communicate()[0] + stdout = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0] return salt.utils.stringutils.to_str(stdout) def migrate(vm_, target, ssh=False): - ''' + """ Shared storage migration :param vm_: domain name @@ -3168,18 +3145,15 @@ def migrate(vm_, target, ssh=False): For more details on tunnelled data migrations, report to https://libvirt.org/migration.html#transporttunnel - ''' - cmd = _get_migrate_command() + ' ' + vm_\ - + _get_target(target, ssh) + """ + cmd = _get_migrate_command() + " " + vm_ + _get_target(target, ssh) - stdout = subprocess.Popen(cmd, - shell=True, - stdout=subprocess.PIPE).communicate()[0] + stdout = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0] return salt.utils.stringutils.to_str(stdout) def seed_non_shared_migrate(disks, force=False): - ''' + """ Non shared migration requires that the disks be present on the migration destination, pass the disks information via this function, to the migration destination before executing the migration. @@ -3193,33 +3167,37 @@ def seed_non_shared_migrate(disks, force=False): .. code-block:: bash salt '*' virt.seed_non_shared_migrate <disks> - ''' + """ for _, data in six.iteritems(disks): - fn_ = data['file'] - form = data['file format'] - size = data['virtual size'].split()[1][1:] + fn_ = data["file"] + form = data["file format"] + size = data["virtual size"].split()[1][1:] if os.path.isfile(fn_) and not force: # the target exists, check to see if it is compatible - pre = salt.utils.yaml.safe_load(subprocess.Popen('qemu-img info arch', - shell=True, - stdout=subprocess.PIPE).communicate()[0]) - if pre['file format'] != data['file format']\ - and pre['virtual size'] != data['virtual size']: + pre = salt.utils.yaml.safe_load( + subprocess.Popen( + "qemu-img info arch", shell=True, stdout=subprocess.PIPE + ).communicate()[0] + ) + if ( + pre["file format"] != data["file format"] + and pre["virtual size"] != data["virtual size"] + ): return False if not os.path.isdir(os.path.dirname(fn_)): os.makedirs(os.path.dirname(fn_)) if os.path.isfile(fn_): os.remove(fn_) - cmd = 'qemu-img create -f ' + form + ' ' + fn_ + ' ' + size + cmd = "qemu-img create -f " + form + " " + fn_ + " " + size subprocess.call(cmd, shell=True) creds = _libvirt_creds() - cmd = 'chown ' + creds['user'] + ':' + creds['group'] + ' ' + fn_ + cmd = "chown " + creds["user"] + ":" + creds["group"] + " " + fn_ subprocess.call(cmd, shell=True) return True -def set_autostart(vm_, state='on', **kwargs): - ''' +def set_autostart(vm_, state="on", **kwargs): + """ Set the autostart flag on a VM so that the VM will start with the host system on reboot. @@ -3241,17 +3219,17 @@ def set_autostart(vm_, state='on', **kwargs): .. code-block:: bash salt "*" virt.set_autostart <domain> <on | off> - ''' + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) # return False if state is set to something other then on or off ret = False - if state == 'on': + if state == "on": ret = dom.setAutostart(1) == 0 - elif state == 'off': + elif state == "off": ret = dom.setAutostart(0) == 0 conn.close() @@ -3259,7 +3237,7 @@ def set_autostart(vm_, state='on', **kwargs): def undefine(vm_, **kwargs): - ''' + """ Remove a defined vm, this does not purge the virtual machine image, and this only works if the vm is powered down @@ -3279,10 +3257,10 @@ def undefine(vm_, **kwargs): .. code-block:: bash salt '*' virt.undefine <domain> - ''' + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) - if getattr(libvirt, 'VIR_DOMAIN_UNDEFINE_NVRAM', False): + if getattr(libvirt, "VIR_DOMAIN_UNDEFINE_NVRAM", False): # This one is only in 1.2.8+ ret = dom.undefineFlags(libvirt.VIR_DOMAIN_UNDEFINE_NVRAM) == 0 else: @@ -3291,16 +3269,12 @@ def undefine(vm_, **kwargs): return ret -def purge(vm_, dirs=False, removables=None, **kwargs): - ''' +def purge(vm_, dirs=False, removables=False, **kwargs): + """ Recursively destroy and delete a persistent virtual machine, pass True for dir's to also delete the directories containing the virtual machine disk images - USE WITH EXTREME CAUTION! - Pass removables=False to avoid deleting cdrom and floppy images. To avoid - disruption, the default but dangerous value is True. This will be changed - to the safer False default value in Sodium. - :param vm_: domain name :param dirs: pass True to remove containing directories :param removables: pass True to remove removable devices @@ -3320,31 +3294,26 @@ def purge(vm_, dirs=False, removables=None, **kwargs): .. code-block:: bash - salt '*' virt.purge <domain> removables=False - ''' + salt '*' virt.purge <domain> + """ conn = __get_conn(**kwargs) dom = _get_domain(conn, vm_) disks = _get_disks(dom) - if removables is None: - salt.utils.versions.warn_until( - 'Sodium', - 'removables argument default value is True, but will be changed ' - 'to False by default in {version}. Please set to True to maintain ' - 'the current behavior in the future.' - ) - removables = True - if VIRT_STATE_NAME_MAP.get(dom.info()[0], 'unknown') != 'shutdown' and dom.destroy() != 0: + if ( + VIRT_STATE_NAME_MAP.get(dom.info()[0], "unknown") != "shutdown" + and dom.destroy() != 0 + ): return False directories = set() for disk in disks: - if not removables and disks[disk]['type'] in ['cdrom', 'floppy']: + if not removables and disks[disk]["type"] in ["cdrom", "floppy"]: continue - os.remove(disks[disk]['file']) - directories.add(os.path.dirname(disks[disk]['file'])) + os.remove(disks[disk]["file"]) + directories.add(os.path.dirname(disks[disk]["file"])) if dirs: for dir_ in directories: shutil.rmtree(dir_) - if getattr(libvirt, 'VIR_DOMAIN_UNDEFINE_NVRAM', False): + if getattr(libvirt, "VIR_DOMAIN_UNDEFINE_NVRAM", False): # This one is only in 1.2.8+ try: dom.undefineFlags(libvirt.VIR_DOMAIN_UNDEFINE_NVRAM) @@ -3357,7 +3326,7 @@ def purge(vm_, dirs=False, removables=None, **kwargs): def virt_type(): - ''' + """ Returns the virtual machine type as a string CLI Example: @@ -3365,86 +3334,46 @@ def virt_type(): .. code-block:: bash salt '*' virt.virt_type - ''' - return __grains__['virtual'] + """ + return __grains__["virtual"] def _is_kvm_hyper(): - ''' + """ Returns a bool whether or not this node is a KVM hypervisor - ''' + """ try: - with salt.utils.files.fopen('/proc/modules') as fp_: - if 'kvm_' not in salt.utils.stringutils.to_unicode(fp_.read()): + with salt.utils.files.fopen("/proc/modules") as fp_: + if "kvm_" not in salt.utils.stringutils.to_unicode(fp_.read()): return False except IOError: # No /proc/modules? Are we on Windows? Or Solaris? return False - return 'libvirtd' in __salt__['cmd.run'](__grains__['ps']) - - -def is_kvm_hyper(): - ''' - Returns a bool whether or not this node is a KVM hypervisor - - CLI Example: - - .. code-block:: bash - - salt '*' virt.is_kvm_hyper - - .. deprecated:: 2019.2.0 - ''' - salt.utils.versions.warn_until( - 'Sodium', - '\'is_kvm_hyper\' function has been deprecated. Use the \'get_hypervisor\' == "kvm" instead. ' - '\'is_kvm_hyper\' will be removed in {version}.' - ) - return _is_kvm_hyper() + return "libvirtd" in __salt__["cmd.run"](__grains__["ps"]) def _is_xen_hyper(): - ''' + """ Returns a bool whether or not this node is a XEN hypervisor - ''' + """ try: - if __grains__['virtual_subtype'] != 'Xen Dom0': + if __grains__["virtual_subtype"] != "Xen Dom0": return False except KeyError: # virtual_subtype isn't set everywhere. return False try: - with salt.utils.files.fopen('/proc/modules') as fp_: - if 'xen_' not in salt.utils.stringutils.to_unicode(fp_.read()): + with salt.utils.files.fopen("/proc/modules") as fp_: + if "xen_" not in salt.utils.stringutils.to_unicode(fp_.read()): return False except (OSError, IOError): # No /proc/modules? Are we on Windows? Or Solaris? return False - return 'libvirtd' in __salt__['cmd.run'](__grains__['ps']) - - -def is_xen_hyper(): - ''' - Returns a bool whether or not this node is a XEN hypervisor - - CLI Example: - - .. code-block:: bash - - salt '*' virt.is_xen_hyper - - .. deprecated:: 2019.2.0 - ''' - salt.utils.versions.warn_until( - 'Sodium', - '\'is_xen_hyper\' function has been deprecated. Use the \'get_hypervisor\' == "xen" instead. ' - '\'is_xen_hyper\' will be removed in {version}.' - ) - return _is_xen_hyper() + return "libvirtd" in __salt__["cmd.run"](__grains__["ps"]) def get_hypervisor(): - ''' + """ Returns the name of the hypervisor running on this node or ``None``. Detected hypervisors: @@ -3460,16 +3389,20 @@ def get_hypervisor(): .. versionadded:: 2019.2.0 the function and the ``kvm`` and ``xen`` hypervisors support - ''' + """ # To add a new 'foo' hypervisor, add the _is_foo_hyper function, # add 'foo' to the list below and add it to the docstring with a .. versionadded:: - hypervisors = ['kvm', 'xen'] - result = [hyper for hyper in hypervisors if getattr(sys.modules[__name__], '_is_{}_hyper'.format(hyper))()] + hypervisors = ["kvm", "xen"] + result = [ + hyper + for hyper in hypervisors + if getattr(sys.modules[__name__], "_is_{}_hyper".format(hyper))() + ] return result[0] if result else None def is_hyper(): - ''' + """ Returns a bool whether or not this node is a hypervisor of any kind CLI Example: @@ -3477,14 +3410,14 @@ def is_hyper(): .. code-block:: bash salt '*' virt.is_hyper - ''' + """ if HAS_LIBVIRT: - return is_xen_hyper() or is_kvm_hyper() + return _is_xen_hyper() or _is_kvm_hyper() return False def vm_cputime(vm_=None, **kwargs): - ''' + """ Return cputime used by the vms on this hyper in a list of dicts: @@ -3517,14 +3450,14 @@ def vm_cputime(vm_=None, **kwargs): .. code-block:: bash salt '*' virt.vm_cputime - ''' + """ conn = __get_conn(**kwargs) host_cpus = conn.getInfo()[2] def _info(dom): - ''' + """ Compute cputime info of a domain - ''' + """ raw = dom.info() vcpus = int(raw[3]) cputime = int(raw[4]) @@ -3533,9 +3466,10 @@ def vm_cputime(vm_=None, **kwargs): # Divide by vcpus to always return a number between 0 and 100 cputime_percent = (1.0e-7 * cputime / host_cpus) / vcpus return { - 'cputime': int(raw[4]), - 'cputime_percent': int('{0:.0f}'.format(cputime_percent)) - } + "cputime": int(raw[4]), + "cputime_percent": int("{0:.0f}".format(cputime_percent)), + } + info = {} if vm_: info[vm_] = _info(_get_domain(conn, vm_)) @@ -3547,7 +3481,7 @@ def vm_cputime(vm_=None, **kwargs): def vm_netstats(vm_=None, **kwargs): - ''' + """ Return combined network counters used by the vms on this hyper in a list of dicts: @@ -3586,36 +3520,38 @@ def vm_netstats(vm_=None, **kwargs): .. code-block:: bash salt '*' virt.vm_netstats - ''' + """ + def _info(dom): - ''' + """ Compute network stats of a domain - ''' + """ nics = _get_nics(dom) ret = { - 'rx_bytes': 0, - 'rx_packets': 0, - 'rx_errs': 0, - 'rx_drop': 0, - 'tx_bytes': 0, - 'tx_packets': 0, - 'tx_errs': 0, - 'tx_drop': 0 - } + "rx_bytes": 0, + "rx_packets": 0, + "rx_errs": 0, + "rx_drop": 0, + "tx_bytes": 0, + "tx_packets": 0, + "tx_errs": 0, + "tx_drop": 0, + } for attrs in six.itervalues(nics): - if 'target' in attrs: - dev = attrs['target'] + if "target" in attrs: + dev = attrs["target"] stats = dom.interfaceStats(dev) - ret['rx_bytes'] += stats[0] - ret['rx_packets'] += stats[1] - ret['rx_errs'] += stats[2] - ret['rx_drop'] += stats[3] - ret['tx_bytes'] += stats[4] - ret['tx_packets'] += stats[5] - ret['tx_errs'] += stats[6] - ret['tx_drop'] += stats[7] + ret["rx_bytes"] += stats[0] + ret["rx_packets"] += stats[1] + ret["rx_errs"] += stats[2] + ret["rx_drop"] += stats[3] + ret["tx_bytes"] += stats[4] + ret["tx_packets"] += stats[5] + ret["tx_errs"] += stats[6] + ret["tx_drop"] += stats[7] return ret + info = {} conn = __get_conn(**kwargs) if vm_: @@ -3628,7 +3564,7 @@ def vm_netstats(vm_=None, **kwargs): def vm_diskstats(vm_=None, **kwargs): - ''' + """ Return disk usage counters used by the vms on this hyper in a list of dicts: @@ -3664,36 +3600,33 @@ def vm_diskstats(vm_=None, **kwargs): .. code-block:: bash salt '*' virt.vm_blockstats - ''' + """ + def get_disk_devs(dom): - ''' + """ Extract the disk devices names from the domain XML definition - ''' + """ doc = ElementTree.fromstring(get_xml(dom, **kwargs)) - return [target.get('dev') for target in doc.findall('devices/disk/target')] + return [target.get("dev") for target in doc.findall("devices/disk/target")] def _info(dom): - ''' + """ Compute the disk stats of a domain - ''' + """ # Do not use get_disks, since it uses qemu-img and is very slow # and unsuitable for any sort of real time statistics disks = get_disk_devs(dom) - ret = {'rd_req': 0, - 'rd_bytes': 0, - 'wr_req': 0, - 'wr_bytes': 0, - 'errs': 0 - } + ret = {"rd_req": 0, "rd_bytes": 0, "wr_req": 0, "wr_bytes": 0, "errs": 0} for disk in disks: stats = dom.blockStats(disk) - ret['rd_req'] += stats[0] - ret['rd_bytes'] += stats[1] - ret['wr_req'] += stats[2] - ret['wr_bytes'] += stats[3] - ret['errs'] += stats[4] + ret["rd_req"] += stats[0] + ret["rd_bytes"] += stats[1] + ret["wr_req"] += stats[2] + ret["wr_bytes"] += stats[3] + ret["errs"] += stats[4] return ret + info = {} conn = __get_conn(**kwargs) if vm_: @@ -3707,30 +3640,33 @@ def vm_diskstats(vm_=None, **kwargs): def _parse_snapshot_description(vm_snapshot, unix_time=False): - ''' + """ Parse XML doc and return a dict with the status values. :param xmldoc: :return: - ''' + """ ret = dict() tree = ElementTree.fromstring(vm_snapshot.getXMLDesc()) for node in tree: - if node.tag == 'name': - ret['name'] = node.text - elif node.tag == 'creationTime': - ret['created'] = datetime.datetime.fromtimestamp(float(node.text)).isoformat(' ') \ - if not unix_time else float(node.text) - elif node.tag == 'state': - ret['running'] = node.text == 'running' + if node.tag == "name": + ret["name"] = node.text + elif node.tag == "creationTime": + ret["created"] = ( + datetime.datetime.fromtimestamp(float(node.text)).isoformat(" ") + if not unix_time + else float(node.text) + ) + elif node.tag == "state": + ret["running"] = node.text == "running" - ret['current'] = vm_snapshot.isCurrent() == 1 + ret["current"] = vm_snapshot.isCurrent() == 1 return ret def list_snapshots(domain=None, **kwargs): - ''' + """ List available snapshots for certain vm or for all. :param domain: domain name @@ -3752,18 +3688,20 @@ def list_snapshots(domain=None, **kwargs): salt '*' virt.list_snapshots salt '*' virt.list_snapshots <domain> - ''' + """ ret = dict() conn = __get_conn(**kwargs) for vm_domain in _get_domain(conn, *(domain and [domain] or list()), iterable=True): - ret[vm_domain.name()] = [_parse_snapshot_description(snap) for snap in vm_domain.listAllSnapshots()] or 'N/A' + ret[vm_domain.name()] = [ + _parse_snapshot_description(snap) for snap in vm_domain.listAllSnapshots() + ] or "N/A" conn.close() return ret def snapshot(domain, name=None, suffix=None, **kwargs): - ''' + """ Create a snapshot of a VM. :param domain: domain name @@ -3789,18 +3727,22 @@ def snapshot(domain, name=None, suffix=None, **kwargs): .. code-block:: bash salt '*' virt.snapshot <domain> - ''' + """ if name and name.lower() == domain.lower(): - raise CommandExecutionError('Virtual Machine {name} is already defined. ' - 'Please choose another name for the snapshot'.format(name=name)) + raise CommandExecutionError( + "Virtual Machine {name} is already defined. " + "Please choose another name for the snapshot".format(name=name) + ) if not name: - name = "{domain}-{tsnap}".format(domain=domain, tsnap=time.strftime('%Y%m%d-%H%M%S', time.localtime())) + name = "{domain}-{tsnap}".format( + domain=domain, tsnap=time.strftime("%Y%m%d-%H%M%S", time.localtime()) + ) if suffix: name = "{name}-{suffix}".format(name=name, suffix=suffix) - doc = ElementTree.Element('domainsnapshot') - n_name = ElementTree.SubElement(doc, 'name') + doc = ElementTree.Element("domainsnapshot") + n_name = ElementTree.SubElement(doc, "name") n_name.text = name conn = __get_conn(**kwargs) @@ -3809,11 +3751,11 @@ def snapshot(domain, name=None, suffix=None, **kwargs): ) conn.close() - return {'name': name} + return {"name": name} def delete_snapshots(name, *names, **kwargs): - ''' + """ Delete one or more snapshots of the given VM. :param name: domain name @@ -3837,7 +3779,7 @@ def delete_snapshots(name, *names, **kwargs): salt '*' virt.delete_snapshots <domain> all=True salt '*' virt.delete_snapshots <domain> <snapshot> salt '*' virt.delete_snapshots <domain> <snapshot1> <snapshot2> ... - ''' + """ deleted = dict() conn = __get_conn(**kwargs) domain = _get_domain(conn, name) @@ -3847,13 +3789,16 @@ def delete_snapshots(name, *names, **kwargs): snap.delete() conn.close() - available = {name: [_parse_snapshot_description(snap) for snap in domain.listAllSnapshots()] or 'N/A'} + available = { + name: [_parse_snapshot_description(snap) for snap in domain.listAllSnapshots()] + or "N/A" + } - return {'available': available, 'deleted': deleted} + return {"available": available, "deleted": deleted} def revert_snapshot(name, vm_snapshot=None, cleanup=False, **kwargs): - ''' + """ Revert snapshot to the previous from current (if available) or to the specific. :param name: domain name @@ -3877,7 +3822,7 @@ def revert_snapshot(name, vm_snapshot=None, cleanup=False, **kwargs): salt '*' virt.revert <domain> salt '*' virt.revert <domain> <snapshot> - ''' + """ ret = dict() conn = __get_conn(**kwargs) domain = _get_domain(conn, name) @@ -3885,22 +3830,32 @@ def revert_snapshot(name, vm_snapshot=None, cleanup=False, **kwargs): _snapshots = list() for snap_obj in snapshots: - _snapshots.append({'idx': _parse_snapshot_description(snap_obj, unix_time=True)['created'], 'ptr': snap_obj}) - snapshots = [w_ptr['ptr'] for w_ptr in sorted(_snapshots, key=lambda item: item['idx'], reverse=True)] + _snapshots.append( + { + "idx": _parse_snapshot_description(snap_obj, unix_time=True)["created"], + "ptr": snap_obj, + } + ) + snapshots = [ + w_ptr["ptr"] + for w_ptr in sorted(_snapshots, key=lambda item: item["idx"], reverse=True) + ] del _snapshots if not snapshots: conn.close() - raise CommandExecutionError('No snapshots found') + raise CommandExecutionError("No snapshots found") elif len(snapshots) == 1: conn.close() - raise CommandExecutionError('Cannot revert to itself: only one snapshot is available.') + raise CommandExecutionError( + "Cannot revert to itself: only one snapshot is available." + ) snap = None for p_snap in snapshots: if not vm_snapshot: - if p_snap.isCurrent() and snapshots[snapshots.index(p_snap) + 1:]: - snap = snapshots[snapshots.index(p_snap) + 1:][0] + if p_snap.isCurrent() and snapshots[snapshots.index(p_snap) + 1 :]: + snap = snapshots[snapshots.index(p_snap) + 1 :][0] break elif p_snap.getName() == vm_snapshot: snap = p_snap @@ -3909,13 +3864,16 @@ def revert_snapshot(name, vm_snapshot=None, cleanup=False, **kwargs): if not snap: conn.close() raise CommandExecutionError( - snapshot and 'Snapshot "{0}" not found'.format(vm_snapshot) or 'No more previous snapshots available') + snapshot + and 'Snapshot "{0}" not found'.format(vm_snapshot) + or "No more previous snapshots available" + ) elif snap.isCurrent(): conn.close() - raise CommandExecutionError('Cannot revert to the currently running snapshot.') + raise CommandExecutionError("Cannot revert to the currently running snapshot.") domain.revertToSnapshot(snap) - ret['reverted'] = snap.getName() + ret["reverted"] = snap.getName() if cleanup: delete = list() @@ -3925,9 +3883,9 @@ def revert_snapshot(name, vm_snapshot=None, cleanup=False, **kwargs): p_snap.delete() else: break - ret['deleted'] = delete + ret["deleted"] = delete else: - ret['deleted'] = 'N/A' + ret["deleted"] = "N/A" conn.close() @@ -3935,12 +3893,12 @@ def revert_snapshot(name, vm_snapshot=None, cleanup=False, **kwargs): def _caps_add_machine(machines, node): - ''' + """ Parse the <machine> element of the host capabilities and add it to the machines list. - ''' - maxcpus = node.get('maxCpus') - canonical = node.get('canonical') + """ + maxcpus = node.get("maxCpus") + canonical = node.get("canonical") name = node.text alternate_name = "" @@ -3950,202 +3908,223 @@ def _caps_add_machine(machines, node): machine = machines.get(name) if not machine: - machine = {'alternate_names': []} + machine = {"alternate_names": []} if maxcpus: - machine['maxcpus'] = int(maxcpus) + machine["maxcpus"] = int(maxcpus) machines[name] = machine if alternate_name: - machine['alternate_names'].append(alternate_name) + machine["alternate_names"].append(alternate_name) def _parse_caps_guest(guest): - ''' + """ Parse the <guest> element of the connection capabilities XML - ''' - arch_node = guest.find('arch') + """ + arch_node = guest.find("arch") result = { - 'os_type': guest.find('os_type').text, - 'arch': { - 'name': arch_node.get('name'), - 'machines': {}, - 'domains': {} - }, + "os_type": guest.find("os_type").text, + "arch": {"name": arch_node.get("name"), "machines": {}, "domains": {}}, } for child in arch_node: - if child.tag == 'wordsize': - result['arch']['wordsize'] = int(child.text) - elif child.tag == 'emulator': - result['arch']['emulator'] = child.text - elif child.tag == 'machine': - _caps_add_machine(result['arch']['machines'], child) - elif child.tag == 'domain': - domain_type = child.get('type') - domain = { - 'emulator': None, - 'machines': {} - } - emulator_node = child.find('emulator') + if child.tag == "wordsize": + result["arch"]["wordsize"] = int(child.text) + elif child.tag == "emulator": + result["arch"]["emulator"] = child.text + elif child.tag == "machine": + _caps_add_machine(result["arch"]["machines"], child) + elif child.tag == "domain": + domain_type = child.get("type") + domain = {"emulator": None, "machines": {}} + emulator_node = child.find("emulator") if emulator_node is not None: - domain['emulator'] = emulator_node.text - for machine in child.findall('machine'): - _caps_add_machine(domain['machines'], machine) - result['arch']['domains'][domain_type] = domain + domain["emulator"] = emulator_node.text + for machine in child.findall("machine"): + _caps_add_machine(domain["machines"], machine) + result["arch"]["domains"][domain_type] = domain # Note that some features have no default and toggle attributes. # This may not be a perfect match, but represent them as enabled by default # without possibility to toggle them. # Some guests may also have no feature at all (xen pv for instance) - features_nodes = guest.find('features') + features_nodes = guest.find("features") if features_nodes is not None: - result['features'] = {child.tag: {'toggle': True if child.get('toggle') == 'yes' else False, - 'default': True if child.get('default') == 'no' else True} - for child in features_nodes} + result["features"] = { + child.tag: { + "toggle": True if child.get("toggle") == "yes" else False, + "default": True if child.get("default") == "no" else True, + } + for child in features_nodes + } return result def _parse_caps_cell(cell): - ''' + """ Parse the <cell> nodes of the connection capabilities XML output. - ''' - result = { - 'id': int(cell.get('id')) - } + """ + result = {"id": int(cell.get("id"))} - mem_node = cell.find('memory') + mem_node = cell.find("memory") if mem_node is not None: - unit = mem_node.get('unit', 'KiB') + unit = mem_node.get("unit", "KiB") memory = mem_node.text - result['memory'] = "{} {}".format(memory, unit) + result["memory"] = "{} {}".format(memory, unit) - pages = [{'size': "{} {}".format(page.get('size'), page.get('unit', 'KiB')), - 'available': int(page.text)} - for page in cell.findall('pages')] + pages = [ + { + "size": "{} {}".format(page.get("size"), page.get("unit", "KiB")), + "available": int(page.text), + } + for page in cell.findall("pages") + ] if pages: - result['pages'] = pages + result["pages"] = pages - distances = {int(distance.get('id')): int(distance.get('value')) - for distance in cell.findall('distances/sibling')} + distances = { + int(distance.get("id")): int(distance.get("value")) + for distance in cell.findall("distances/sibling") + } if distances: - result['distances'] = distances + result["distances"] = distances cpus = [] - for cpu_node in cell.findall('cpus/cpu'): - cpu = { - 'id': int(cpu_node.get('id')) - } - socket_id = cpu_node.get('socket_id') + for cpu_node in cell.findall("cpus/cpu"): + cpu = {"id": int(cpu_node.get("id"))} + socket_id = cpu_node.get("socket_id") if socket_id: - cpu['socket_id'] = int(socket_id) + cpu["socket_id"] = int(socket_id) - core_id = cpu_node.get('core_id') + core_id = cpu_node.get("core_id") if core_id: - cpu['core_id'] = int(core_id) - siblings = cpu_node.get('siblings') + cpu["core_id"] = int(core_id) + siblings = cpu_node.get("siblings") if siblings: - cpu['siblings'] = siblings + cpu["siblings"] = siblings cpus.append(cpu) if cpus: - result['cpus'] = cpus + result["cpus"] = cpus return result def _parse_caps_bank(bank): - ''' + """ Parse the <bank> element of the connection capabilities XML. - ''' + """ result = { - 'id': int(bank.get('id')), - 'level': int(bank.get('level')), - 'type': bank.get('type'), - 'size': "{} {}".format(bank.get('size'), bank.get('unit')), - 'cpus': bank.get('cpus') + "id": int(bank.get("id")), + "level": int(bank.get("level")), + "type": bank.get("type"), + "size": "{} {}".format(bank.get("size"), bank.get("unit")), + "cpus": bank.get("cpus"), } controls = [] - for control in bank.findall('control'): - unit = control.get('unit') + for control in bank.findall("control"): + unit = control.get("unit") result_control = { - 'granularity': "{} {}".format(control.get('granularity'), unit), - 'type': control.get('type'), - 'maxAllocs': int(control.get('maxAllocs')) + "granularity": "{} {}".format(control.get("granularity"), unit), + "type": control.get("type"), + "maxAllocs": int(control.get("maxAllocs")), } - minimum = control.get('min') + minimum = control.get("min") if minimum: - result_control['min'] = "{} {}".format(minimum, unit) + result_control["min"] = "{} {}".format(minimum, unit) controls.append(result_control) if controls: - result['controls'] = controls + result["controls"] = controls return result def _parse_caps_host(host): - ''' + """ Parse the <host> element of the connection capabilities XML. - ''' + """ result = {} for child in host: - if child.tag == 'uuid': - result['uuid'] = child.text + if child.tag == "uuid": + result["uuid"] = child.text - elif child.tag == 'cpu': + elif child.tag == "cpu": cpu = { - 'arch': child.find('arch').text if child.find('arch') is not None else None, - 'model': child.find('model').text if child.find('model') is not None else None, - 'vendor': child.find('vendor').text if child.find('vendor') is not None else None, - 'features': [feature.get('name') for feature in child.findall('feature')], - 'pages': [{'size': '{} {}'.format(page.get('size'), page.get('unit', 'KiB'))} - for page in child.findall('pages')] + "arch": child.find("arch").text + if child.find("arch") is not None + else None, + "model": child.find("model").text + if child.find("model") is not None + else None, + "vendor": child.find("vendor").text + if child.find("vendor") is not None + else None, + "features": [ + feature.get("name") for feature in child.findall("feature") + ], + "pages": [ + {"size": "{} {}".format(page.get("size"), page.get("unit", "KiB"))} + for page in child.findall("pages") + ], } # Parse the cpu tag - microcode = child.find('microcode') + microcode = child.find("microcode") if microcode is not None: - cpu['microcode'] = microcode.get('version') + cpu["microcode"] = microcode.get("version") - topology = child.find('topology') + topology = child.find("topology") if topology is not None: - cpu['sockets'] = int(topology.get('sockets')) - cpu['cores'] = int(topology.get('cores')) - cpu['threads'] = int(topology.get('threads')) - result['cpu'] = cpu + cpu["sockets"] = int(topology.get("sockets")) + cpu["cores"] = int(topology.get("cores")) + cpu["threads"] = int(topology.get("threads")) + result["cpu"] = cpu elif child.tag == "power_management": - result['power_management'] = [node.tag for node in child] + result["power_management"] = [node.tag for node in child] elif child.tag == "migration_features": - result['migration'] = { - 'live': child.find('live') is not None, - 'transports': [node.text for node in child.findall('uri_transports/uri_transport')] + result["migration"] = { + "live": child.find("live") is not None, + "transports": [ + node.text for node in child.findall("uri_transports/uri_transport") + ], } elif child.tag == "topology": - result['topology'] = { - 'cells': [_parse_caps_cell(cell) for cell in child.findall('cells/cell')] + result["topology"] = { + "cells": [ + _parse_caps_cell(cell) for cell in child.findall("cells/cell") + ] } - elif child.tag == 'cache': - result['cache'] = { - 'banks': [_parse_caps_bank(bank) for bank in child.findall('bank')] + elif child.tag == "cache": + result["cache"] = { + "banks": [_parse_caps_bank(bank) for bank in child.findall("bank")] } - result['security'] = [{ - 'model': secmodel.find('model').text if secmodel.find('model') is not None else None, - 'doi': secmodel.find('doi').text if secmodel.find('doi') is not None else None, - 'baselabels': [{'type': label.get('type'), 'label': label.text} - for label in secmodel.findall('baselabel')] + result["security"] = [ + { + "model": secmodel.find("model").text + if secmodel.find("model") is not None + else None, + "doi": secmodel.find("doi").text + if secmodel.find("doi") is not None + else None, + "baselabels": [ + {"type": label.get("type"), "label": label.text} + for label in secmodel.findall("baselabel") + ], } - for secmodel in host.findall('secmodel')] + for secmodel in host.findall("secmodel") + ] return result def capabilities(**kwargs): - ''' + """ Return the hypervisor connection capabilities. :param connection: libvirt connection URI, overriding defaults @@ -4159,103 +4138,108 @@ def capabilities(**kwargs): .. code-block:: bash salt '*' virt.capabilities - ''' + """ conn = __get_conn(**kwargs) caps = ElementTree.fromstring(conn.getCapabilities()) conn.close() return { - 'host': _parse_caps_host(caps.find('host')), - 'guests': [_parse_caps_guest(guest) for guest in caps.findall('guest')] + "host": _parse_caps_host(caps.find("host")), + "guests": [_parse_caps_guest(guest) for guest in caps.findall("guest")], } def _parse_caps_enum(node): - ''' + """ Return a tuple containing the name of the enum and the possible values - ''' - return (node.get('name'), [value.text for value in node.findall('value')]) + """ + return (node.get("name"), [value.text for value in node.findall("value")]) def _parse_caps_cpu(node): - ''' + """ Parse the <cpu> element of the domain capabilities - ''' + """ result = {} - for mode in node.findall('mode'): - if not mode.get('supported') == 'yes': + for mode in node.findall("mode"): + if not mode.get("supported") == "yes": continue - name = mode.get('name') - if name == 'host-passthrough': + name = mode.get("name") + if name == "host-passthrough": result[name] = True - elif name == 'host-model': + elif name == "host-model": host_model = {} - model_node = mode.find('model') + model_node = mode.find("model") if model_node is not None: - model = { - 'name': model_node.text - } + model = {"name": model_node.text} - vendor_id = model_node.get('vendor_id') + vendor_id = model_node.get("vendor_id") if vendor_id: - model['vendor_id'] = vendor_id + model["vendor_id"] = vendor_id - fallback = model_node.get('fallback') + fallback = model_node.get("fallback") if fallback: - model['fallback'] = fallback - host_model['model'] = model + model["fallback"] = fallback + host_model["model"] = model - vendor = mode.find('vendor').text if mode.find('vendor') is not None else None + vendor = ( + mode.find("vendor").text if mode.find("vendor") is not None else None + ) if vendor: - host_model['vendor'] = vendor + host_model["vendor"] = vendor - features = {feature.get('name'): feature.get('policy') for feature in mode.findall('feature')} + features = { + feature.get("name"): feature.get("policy") + for feature in mode.findall("feature") + } if features: - host_model['features'] = features + host_model["features"] = features result[name] = host_model - elif name == 'custom': + elif name == "custom": custom_model = {} - models = {model.text: model.get('usable') for model in mode.findall('model')} + models = { + model.text: model.get("usable") for model in mode.findall("model") + } if models: - custom_model['models'] = models + custom_model["models"] = models result[name] = custom_model return result def _parse_caps_devices_features(node): - ''' + """ Parse the devices or features list of the domain capatilities - ''' + """ result = {} for child in node: - if child.get('supported') == 'yes': - enums = [_parse_caps_enum(node) for node in child.findall('enum')] + if child.get("supported") == "yes": + enums = [_parse_caps_enum(node) for node in child.findall("enum")] result[child.tag] = {item[0]: item[1] for item in enums if item[0]} return result def _parse_caps_loader(node): - ''' + """ Parse the <loader> element of the domain capabilities. - ''' - enums = [_parse_caps_enum(enum) for enum in node.findall('enum')] + """ + enums = [_parse_caps_enum(enum) for enum in node.findall("enum")] result = {item[0]: item[1] for item in enums if item[0]} - values = [child.text for child in node.findall('value')] + values = [child.text for child in node.findall("value")] if values: - result['values'] = values + result["values"] = values return result def domain_capabilities(emulator=None, arch=None, machine=None, domain=None, **kwargs): - ''' + """ Return the domain capabilities given an emulator, architecture, machine or virtualization type. .. versionadded:: 2019.2.0 @@ -4280,52 +4264,56 @@ def domain_capabilities(emulator=None, arch=None, machine=None, domain=None, **k salt '*' virt.domain_capabilities arch='x86_64' domain='kvm' - ''' + """ conn = __get_conn(**kwargs) - caps = ElementTree.fromstring(conn.getDomainCapabilities(emulator, arch, machine, domain, 0)) + caps = ElementTree.fromstring( + conn.getDomainCapabilities(emulator, arch, machine, domain, 0) + ) conn.close() result = { - 'emulator': caps.find('path').text if caps.find('path') is not None else None, - 'domain': caps.find('domain').text if caps.find('domain') is not None else None, - 'machine': caps.find('machine').text if caps.find('machine') is not None else None, - 'arch': caps.find('arch').text if caps.find('arch') is not None else None + "emulator": caps.find("path").text if caps.find("path") is not None else None, + "domain": caps.find("domain").text if caps.find("domain") is not None else None, + "machine": caps.find("machine").text + if caps.find("machine") is not None + else None, + "arch": caps.find("arch").text if caps.find("arch") is not None else None, } for child in caps: - if child.tag == 'vcpu' and child.get('max'): - result['max_vcpus'] = int(child.get('max')) + if child.tag == "vcpu" and child.get("max"): + result["max_vcpus"] = int(child.get("max")) - elif child.tag == 'iothreads': - result['iothreads'] = child.get('supported') == 'yes' + elif child.tag == "iothreads": + result["iothreads"] = child.get("supported") == "yes" - elif child.tag == 'os': - result['os'] = {} - loader_node = child.find('loader') - if loader_node is not None and loader_node.get('supported') == 'yes': + elif child.tag == "os": + result["os"] = {} + loader_node = child.find("loader") + if loader_node is not None and loader_node.get("supported") == "yes": loader = _parse_caps_loader(loader_node) - result['os']['loader'] = loader + result["os"]["loader"] = loader - elif child.tag == 'cpu': + elif child.tag == "cpu": cpu = _parse_caps_cpu(child) if cpu: - result['cpu'] = cpu + result["cpu"] = cpu - elif child.tag == 'devices': + elif child.tag == "devices": devices = _parse_caps_devices_features(child) if devices: - result['devices'] = devices + result["devices"] = devices - elif child.tag == 'features': + elif child.tag == "features": features = _parse_caps_devices_features(child) if features: - result['features'] = features + result["features"] = features return result -def cpu_baseline(full=False, migratable=False, out='libvirt', **kwargs): - ''' +def cpu_baseline(full=False, migratable=False, out="libvirt", **kwargs): + """ Return the optimal 'custom' CPU baseline config for VM's on this minion .. versionadded:: 2016.3.0 @@ -4349,72 +4337,80 @@ def cpu_baseline(full=False, migratable=False, out='libvirt', **kwargs): salt '*' virt.cpu_baseline - ''' + """ conn = __get_conn(**kwargs) caps = ElementTree.fromstring(conn.getCapabilities()) - cpu = caps.find('host/cpu') - log.debug('Host CPU model definition: %s', salt.utils.stringutils.to_str(ElementTree.tostring(cpu))) + cpu = caps.find("host/cpu") + log.debug( + "Host CPU model definition: %s", + salt.utils.stringutils.to_str(ElementTree.tostring(cpu)), + ) flags = 0 if migratable: # This one is only in 1.2.14+ - if getattr(libvirt, 'VIR_CONNECT_BASELINE_CPU_MIGRATABLE', False): + if getattr(libvirt, "VIR_CONNECT_BASELINE_CPU_MIGRATABLE", False): flags += libvirt.VIR_CONNECT_BASELINE_CPU_MIGRATABLE else: conn.close() raise ValueError - if full and getattr(libvirt, 'VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES', False): + if full and getattr(libvirt, "VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES", False): # This one is only in 1.1.3+ flags += libvirt.VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES - cpu = ElementTree.fromstring(conn.baselineCPU([salt.utils.stringutils.to_str(ElementTree.tostring(cpu))], flags)) + cpu = ElementTree.fromstring( + conn.baselineCPU( + [salt.utils.stringutils.to_str(ElementTree.tostring(cpu))], flags + ) + ) conn.close() - if full and not getattr(libvirt, 'VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES', False): + if full and not getattr(libvirt, "VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES", False): # Try do it by ourselves # Find the models in cpu_map.xml and iterate over them for as long as entries have submodels - with salt.utils.files.fopen('/usr/share/libvirt/cpu_map.xml', 'r') as cpu_map: + with salt.utils.files.fopen("/usr/share/libvirt/cpu_map.xml", "r") as cpu_map: cpu_map = ElementTree.parse(cpu_map) - cpu_model = cpu.find('model').text + cpu_model = cpu.find("model").text while cpu_model: - cpu_map_models = cpu_map.findall('arch/model') - cpu_specs = [el for el in cpu_map_models if el.get('name') == cpu_model and bool(len(el))] + cpu_map_models = cpu_map.findall("arch/model") + cpu_specs = [ + el + for el in cpu_map_models + if el.get("name") == cpu_model and bool(len(el)) + ] if not cpu_specs: - raise ValueError('Model {0} not found in CPU map'.format(cpu_model)) + raise ValueError("Model {0} not found in CPU map".format(cpu_model)) elif len(cpu_specs) > 1: - raise ValueError('Multiple models {0} found in CPU map'.format(cpu_model)) + raise ValueError( + "Multiple models {0} found in CPU map".format(cpu_model) + ) cpu_specs = cpu_specs[0] # libvirt's cpu map used to nest model elements, to point the parent model. # keep this code for compatibility with old libvirt versions - model_node = cpu_specs.find('model') + model_node = cpu_specs.find("model") if model_node is None: cpu_model = None else: - cpu_model = model_node.get('name') + cpu_model = model_node.get("name") - cpu.extend([feature for feature in cpu_specs.findall('feature')]) + cpu.extend([feature for feature in cpu_specs.findall("feature")]) - if out == 'salt': + if out == "salt": return { - 'model': cpu.find('model').text, - 'vendor': cpu.find('vendor').text, - 'features': [feature.get('name') for feature in cpu.findall('feature')] + "model": cpu.find("model").text, + "vendor": cpu.find("vendor").text, + "features": [feature.get("name") for feature in cpu.findall("feature")], } return cpu.toxml() -def network_define(name, - bridge, - forward, - ipv4_config=None, - ipv6_config=None, - **kwargs): - ''' +def network_define(name, bridge, forward, ipv4_config=None, ipv6_config=None, **kwargs): + """ Create libvirt network. :param name: Network name @@ -4463,12 +4459,12 @@ def network_define(name, salt '*' virt.network_define network main bridge openvswitch .. versionadded:: 2019.2.0 - ''' + """ conn = __get_conn(**kwargs) - vport = kwargs.get('vport', None) - tag = kwargs.get('tag', None) - autostart = kwargs.get('autostart', True) - starting = kwargs.get('start', True) + vport = kwargs.get("vport", None) + tag = kwargs.get("tag", None) + autostart = kwargs.get("autostart", True) + starting = kwargs.get("start", True) net_xml = _gen_net_xml( name, @@ -4510,7 +4506,7 @@ def network_define(name, def list_networks(**kwargs): - ''' + """ List all virtual networks. :param connection: libvirt connection URI, overriding defaults @@ -4524,7 +4520,7 @@ def list_networks(**kwargs): .. code-block:: bash salt '*' virt.list_networks - ''' + """ conn = __get_conn(**kwargs) try: return [net.name() for net in conn.listAllNetworks()] @@ -4533,7 +4529,7 @@ def list_networks(**kwargs): def network_info(name=None, **kwargs): - ''' + """ Return informations on a virtual network provided its name. :param name: virtual network name @@ -4550,42 +4546,48 @@ def network_info(name=None, **kwargs): .. code-block:: bash salt '*' virt.network_info default - ''' + """ result = {} conn = __get_conn(**kwargs) def _net_get_leases(net): - ''' + """ Get all DHCP leases for a network - ''' + """ leases = net.DHCPLeases() for lease in leases: - if lease['type'] == libvirt.VIR_IP_ADDR_TYPE_IPV4: - lease['type'] = 'ipv4' - elif lease['type'] == libvirt.VIR_IP_ADDR_TYPE_IPV6: - lease['type'] = 'ipv6' + if lease["type"] == libvirt.VIR_IP_ADDR_TYPE_IPV4: + lease["type"] = "ipv4" + elif lease["type"] == libvirt.VIR_IP_ADDR_TYPE_IPV6: + lease["type"] = "ipv6" else: - lease['type'] = 'unknown' + lease["type"] = "unknown" return leases try: - nets = [net for net in conn.listAllNetworks() if name is None or net.name() == name] - result = {net.name(): { - 'uuid': net.UUIDString(), - 'bridge': net.bridgeName(), - 'autostart': net.autostart(), - 'active': net.isActive(), - 'persistent': net.isPersistent(), - 'leases': _net_get_leases(net)} for net in nets} + nets = [ + net for net in conn.listAllNetworks() if name is None or net.name() == name + ] + result = { + net.name(): { + "uuid": net.UUIDString(), + "bridge": net.bridgeName(), + "autostart": net.autostart(), + "active": net.isActive(), + "persistent": net.isPersistent(), + "leases": _net_get_leases(net), + } + for net in nets + } except libvirt.libvirtError as err: - log.debug('Silenced libvirt error: %s', str(err)) + log.debug("Silenced libvirt error: %s", str(err)) finally: conn.close() return result def network_get_xml(name, **kwargs): - ''' + """ Return the XML definition of a virtual network :param name: libvirt network name @@ -4600,7 +4602,7 @@ def network_get_xml(name, **kwargs): .. code-block:: bash salt '*' virt.network_get_xml default - ''' + """ conn = __get_conn(**kwargs) try: return conn.networkLookupByName(name).XMLDesc() @@ -4609,7 +4611,7 @@ def network_get_xml(name, **kwargs): def network_start(name, **kwargs): - ''' + """ Start a defined virtual network. :param name: virtual network name @@ -4624,7 +4626,7 @@ def network_start(name, **kwargs): .. code-block:: bash salt '*' virt.network_start default - ''' + """ conn = __get_conn(**kwargs) try: net = conn.networkLookupByName(name) @@ -4634,7 +4636,7 @@ def network_start(name, **kwargs): def network_stop(name, **kwargs): - ''' + """ Stop a defined virtual network. :param name: virtual network name @@ -4649,7 +4651,7 @@ def network_stop(name, **kwargs): .. code-block:: bash salt '*' virt.network_stop default - ''' + """ conn = __get_conn(**kwargs) try: net = conn.networkLookupByName(name) @@ -4659,7 +4661,7 @@ def network_stop(name, **kwargs): def network_undefine(name, **kwargs): - ''' + """ Remove a defined virtual network. This does not stop the virtual network. :param name: virtual network name @@ -4674,7 +4676,7 @@ def network_undefine(name, **kwargs): .. code-block:: bash salt '*' virt.network_undefine default - ''' + """ conn = __get_conn(**kwargs) try: net = conn.networkLookupByName(name) @@ -4683,8 +4685,8 @@ def network_undefine(name, **kwargs): conn.close() -def network_set_autostart(name, state='on', **kwargs): - ''' +def network_set_autostart(name, state="on", **kwargs): + """ Set the autostart flag on a virtual network so that the network will start with the host system on reboot. @@ -4702,45 +4704,50 @@ def network_set_autostart(name, state='on', **kwargs): .. code-block:: bash salt "*" virt.network_set_autostart <pool> <on | off> - ''' + """ conn = __get_conn(**kwargs) try: net = conn.networkLookupByName(name) - return not bool(net.setAutostart(1 if state == 'on' else 0)) + return not bool(net.setAutostart(1 if state == "on" else 0)) finally: conn.close() def _parse_pools_caps(doc): - ''' + """ Parse libvirt pool capabilities XML - ''' + """ + def _parse_pool_caps(pool): pool_caps = { - 'name': pool.get('type'), - 'supported': pool.get('supported', 'no') == 'yes' + "name": pool.get("type"), + "supported": pool.get("supported", "no") == "yes", } - for option_kind in ['pool', 'vol']: + for option_kind in ["pool", "vol"]: options = {} - default_format_node = pool.find('{0}Options/defaultFormat'.format(option_kind)) + default_format_node = pool.find( + "{0}Options/defaultFormat".format(option_kind) + ) if default_format_node is not None: - options['default_format'] = default_format_node.get('type') - options_enums = {enum.get('name'): [value.text for value in enum.findall('value')] - for enum in pool.findall('{0}Options/enum'.format(option_kind))} + options["default_format"] = default_format_node.get("type") + options_enums = { + enum.get("name"): [value.text for value in enum.findall("value")] + for enum in pool.findall("{0}Options/enum".format(option_kind)) + } if options_enums: options.update(options_enums) if options: - if 'options' not in pool_caps: - pool_caps['options'] = {} - kind = option_kind if option_kind is not 'vol' else 'volume' - pool_caps['options'][kind] = options + if "options" not in pool_caps: + pool_caps["options"] = {} + kind = option_kind if option_kind is not "vol" else "volume" + pool_caps["options"][kind] = options return pool_caps - return [_parse_pool_caps(pool) for pool in doc.findall('pool')] + return [_parse_pool_caps(pool) for pool in doc.findall("pool")] def pool_capabilities(**kwargs): - ''' + """ Return the hypervisor connection storage pool capabilities. The returned data are either directly extracted from libvirt or computed. @@ -4759,76 +4766,124 @@ def pool_capabilities(**kwargs): salt '*' virt.pool_capabilities - ''' + """ try: conn = __get_conn(**kwargs) - has_pool_capabilities = bool(getattr(conn, 'getStoragePoolCapabilities', None)) + has_pool_capabilities = bool(getattr(conn, "getStoragePoolCapabilities", None)) if has_pool_capabilities: caps = ElementTree.fromstring(conn.getStoragePoolCapabilities()) pool_types = _parse_pools_caps(caps) else: # Compute reasonable values - all_hypervisors = ['xen', 'kvm', 'bhyve'] - images_formats = ['none', 'raw', 'dir', 'bochs', 'cloop', 'dmg', 'iso', 'vpc', 'vdi', - 'fat', 'vhd', 'ploop', 'cow', 'qcow', 'qcow2', 'qed', 'vmdk'] + all_hypervisors = ["xen", "kvm", "bhyve"] + images_formats = [ + "none", + "raw", + "dir", + "bochs", + "cloop", + "dmg", + "iso", + "vpc", + "vdi", + "fat", + "vhd", + "ploop", + "cow", + "qcow", + "qcow2", + "qed", + "vmdk", + ] common_drivers = [ { - 'name': 'fs', - 'default_source_format': 'auto', - 'source_formats': ['auto', 'ext2', 'ext3', 'ext4', 'ufs', 'iso9660', 'udf', 'gfs', 'gfs2', - 'vfat', 'hfs+', 'xfs', 'ocfs2'], - 'default_target_format': 'raw', - 'target_formats': images_formats + "name": "fs", + "default_source_format": "auto", + "source_formats": [ + "auto", + "ext2", + "ext3", + "ext4", + "ufs", + "iso9660", + "udf", + "gfs", + "gfs2", + "vfat", + "hfs+", + "xfs", + "ocfs2", + ], + "default_target_format": "raw", + "target_formats": images_formats, }, { - 'name': 'dir', - 'default_target_format': 'raw', - 'target_formats': images_formats + "name": "dir", + "default_target_format": "raw", + "target_formats": images_formats, }, - {'name': 'iscsi'}, - {'name': 'scsi'}, + {"name": "iscsi"}, + {"name": "scsi"}, { - 'name': 'logical', - 'default_source_format': 'lvm2', - 'source_formats': ['unknown', 'lvm2'], + "name": "logical", + "default_source_format": "lvm2", + "source_formats": ["unknown", "lvm2"], }, { - 'name': 'netfs', - 'default_source_format': 'auto', - 'source_formats': ['auto', 'nfs', 'glusterfs', 'cifs'], - 'default_target_format': 'raw', - 'target_formats': images_formats + "name": "netfs", + "default_source_format": "auto", + "source_formats": ["auto", "nfs", "glusterfs", "cifs"], + "default_target_format": "raw", + "target_formats": images_formats, }, { - 'name': 'disk', - 'default_source_format': 'unknown', - 'source_formats': ['unknown', 'dos', 'dvh', 'gpt', 'mac', 'bsd', 'pc98', 'sun', 'lvm2'], - 'default_target_format': 'none', - 'target_formats': ['none', 'linux', 'fat16', 'fat32', 'linux-swap', 'linux-lvm', - 'linux-raid', 'extended'] + "name": "disk", + "default_source_format": "unknown", + "source_formats": [ + "unknown", + "dos", + "dvh", + "gpt", + "mac", + "bsd", + "pc98", + "sun", + "lvm2", + ], + "default_target_format": "none", + "target_formats": [ + "none", + "linux", + "fat16", + "fat32", + "linux-swap", + "linux-lvm", + "linux-raid", + "extended", + ], }, - {'name': 'mpath'}, + {"name": "mpath"}, + {"name": "rbd", "default_target_format": "raw", "target_formats": []}, { - 'name': 'rbd', - 'default_target_format': 'raw', - 'target_formats': [] + "name": "sheepdog", + "version": 10000, + "hypervisors": ["kvm"], + "default_target_format": "raw", + "target_formats": images_formats, }, { - 'name': 'sheepdog', - 'version': 10000, - 'hypervisors': ['kvm'], - 'default_target_format': 'raw', - 'target_formats': images_formats + "name": "gluster", + "version": 1002000, + "hypervisors": ["kvm"], + "default_target_format": "raw", + "target_formats": images_formats, }, + {"name": "zfs", "version": 1002008, "hypervisors": ["bhyve"]}, { - 'name': 'gluster', - 'version': 1002000, - 'hypervisors': ['kvm'], - 'default_target_format': 'raw', - 'target_formats': images_formats + "name": "iscsi-direct", + "version": 4007000, + "hypervisors": ["kvm", "xen"], }, - {'name': 'zfs', 'version': 1002008, 'hypervisors': ['bhyve']}, - {'name': 'iscsi-direct', 'version': 4007000, 'hypervisors': ['kvm', 'xen']} ] libvirt_version = conn.getLibVersion() @@ -4836,55 +4891,65 @@ def pool_capabilities(**kwargs): def _get_backend_output(backend): output = { - 'name': backend['name'], - 'supported': (not backend.get('version') or libvirt_version >= backend['version']) and - hypervisor in backend.get('hypervisors', all_hypervisors), - 'options': { - 'pool': { - 'default_format': backend.get('default_source_format'), - 'sourceFormatType': backend.get('source_formats') + "name": backend["name"], + "supported": ( + not backend.get("version") + or libvirt_version >= backend["version"] + ) + and hypervisor in backend.get("hypervisors", all_hypervisors), + "options": { + "pool": { + "default_format": backend.get("default_source_format"), + "sourceFormatType": backend.get("source_formats"), }, - 'volume': { - 'default_format': backend.get('default_target_format'), - 'targetFormatType': backend.get('target_formats') - } - } + "volume": { + "default_format": backend.get("default_target_format"), + "targetFormatType": backend.get("target_formats"), + }, + }, } # Cleanup the empty members to match the libvirt output - for option_kind in ['pool', 'volume']: - if not [value for value in output['options'][option_kind].values() if value is not None]: - del output['options'][option_kind] - if not output['options']: - del output['options'] + for option_kind in ["pool", "volume"]: + if not [ + value + for value in output["options"][option_kind].values() + if value is not None + ]: + del output["options"][option_kind] + if not output["options"]: + del output["options"] return output + pool_types = [_get_backend_output(backend) for backend in common_drivers] finally: conn.close() return { - 'computed': not has_pool_capabilities, - 'pool_types': pool_types, + "computed": not has_pool_capabilities, + "pool_types": pool_types, } -def pool_define(name, - ptype, - target=None, - permissions=None, - source_devices=None, - source_dir=None, - source_initiator=None, - source_adapter=None, - source_hosts=None, - source_auth=None, - source_name=None, - source_format=None, - transient=False, - start=True, # pylint: disable=redefined-outer-name - **kwargs): - ''' +def pool_define( + name, + ptype, + target=None, + permissions=None, + source_devices=None, + source_dir=None, + source_initiator=None, + source_adapter=None, + source_hosts=None, + source_auth=None, + source_name=None, + source_format=None, + transient=False, + start=True, # pylint: disable=redefined-outer-name + **kwargs +): + """ Create libvirt pool. :param name: Pool name @@ -5010,7 +5075,7 @@ def pool_define(name, source_dir=samba_share source_hosts="['example.com']" target=/mnt/cifs .. versionadded:: 2019.2.0 - ''' + """ conn = __get_conn(**kwargs) auth = _pool_set_secret(conn, ptype, name, source_auth) @@ -5026,7 +5091,7 @@ def pool_define(name, source_auth=auth, source_name=source_name, source_format=source_format, - source_initiator=source_initiator + source_initiator=source_initiator, ) try: if transient: @@ -5044,66 +5109,69 @@ def pool_define(name, return True -def _pool_set_secret(conn, pool_type, pool_name, source_auth, uuid=None, usage=None, test=False): - secret_types = { - 'rbd': 'ceph', - 'iscsi': 'chap', - 'iscsi-direct': 'chap' - } +def _pool_set_secret( + conn, pool_type, pool_name, source_auth, uuid=None, usage=None, test=False +): + secret_types = {"rbd": "ceph", "iscsi": "chap", "iscsi-direct": "chap"} secret_type = secret_types.get(pool_type) auth = source_auth - if source_auth and 'username' in source_auth and 'password' in source_auth: + if source_auth and "username" in source_auth and "password" in source_auth: if secret_type: # Get the previously defined secret if any secret = None if usage: - usage_type = libvirt.VIR_SECRET_USAGE_TYPE_CEPH if secret_type == 'ceph' \ - else libvirt.VIR_SECRET_USAGE_TYPE_ISCSI + usage_type = ( + libvirt.VIR_SECRET_USAGE_TYPE_CEPH + if secret_type == "ceph" + else libvirt.VIR_SECRET_USAGE_TYPE_ISCSI + ) secret = conn.secretLookupByUsage(usage_type, usage) elif uuid: secret = conn.secretLookupByUUIDString(uuid) # Create secret if needed if not secret: - description = 'Passphrase for {} pool created by Salt'.format(pool_name) + description = "Passphrase for {} pool created by Salt".format(pool_name) if not usage: - usage = 'pool_{}'.format(pool_name) + usage = "pool_{}".format(pool_name) secret_xml = _gen_secret_xml(secret_type, usage, description) if not test: secret = conn.secretDefineXML(secret_xml) # Assign the password to it - password = auth['password'] - if pool_type == 'rbd': + password = auth["password"] + if pool_type == "rbd": # RBD password are already base64-encoded, but libvirt will base64-encode them later password = base64.b64decode(salt.utils.stringutils.to_bytes(password)) if not test: secret.setValue(password) # update auth with secret reference - auth['type'] = secret_type - auth['secret'] = { - 'type': 'uuid' if uuid else 'usage', - 'value': uuid if uuid else usage, + auth["type"] = secret_type + auth["secret"] = { + "type": "uuid" if uuid else "usage", + "value": uuid if uuid else usage, } return auth -def pool_update(name, - ptype, - target=None, - permissions=None, - source_devices=None, - source_dir=None, - source_initiator=None, - source_adapter=None, - source_hosts=None, - source_auth=None, - source_name=None, - source_format=None, - test=False, - **kwargs): - ''' +def pool_update( + name, + ptype, + target=None, + permissions=None, + source_devices=None, + source_dir=None, + source_initiator=None, + source_adapter=None, + source_hosts=None, + source_auth=None, + source_name=None, + source_format=None, + test=False, + **kwargs +): + """ Update a libvirt storage pool if needed. If called with test=True, this is also reporting whether an update would be performed. @@ -5208,7 +5276,7 @@ def pool_update(name, source_dir=samba_share source_hosts="['example.com']" target=/mnt/cifs .. versionadded:: 3000 - ''' + """ # Get the current definition to compare the two conn = __get_conn(**kwargs) needs_update = False @@ -5218,29 +5286,33 @@ def pool_update(name, # If we have username and password in source_auth generate a new secret # Or change the value of the existing one - secret_node = old_xml.find('source/auth/secret') - usage = secret_node.get('usage') if secret_node is not None else None - uuid = secret_node.get('uuid') if secret_node is not None else None - auth = _pool_set_secret(conn, ptype, name, source_auth, uuid=uuid, usage=usage, test=test) + secret_node = old_xml.find("source/auth/secret") + usage = secret_node.get("usage") if secret_node is not None else None + uuid = secret_node.get("uuid") if secret_node is not None else None + auth = _pool_set_secret( + conn, ptype, name, source_auth, uuid=uuid, usage=usage, test=test + ) # Compute new definition - new_xml = ElementTree.fromstring(_gen_pool_xml( - name, - ptype, - target, - permissions=permissions, - source_devices=source_devices, - source_dir=source_dir, - source_initiator=source_initiator, - source_adapter=source_adapter, - source_hosts=source_hosts, - source_auth=auth, - source_name=source_name, - source_format=source_format - )) + new_xml = ElementTree.fromstring( + _gen_pool_xml( + name, + ptype, + target, + permissions=permissions, + source_devices=source_devices, + source_dir=source_dir, + source_initiator=source_initiator, + source_adapter=source_adapter, + source_hosts=source_hosts, + source_auth=auth, + source_name=source_name, + source_format=source_format, + ) + ) # Copy over the uuid, capacity, allocation, available elements - elements_to_copy = ['available', 'allocation', 'capacity', 'uuid'] + elements_to_copy = ["available", "allocation", "capacity", "uuid"] for to_copy in elements_to_copy: element = old_xml.find(to_copy) new_xml.insert(1, element) @@ -5253,29 +5325,37 @@ def pool_update(name, def space_stripper(node): if node.tail is not None: - node.tail = node.tail.strip(' \t\n') + node.tail = node.tail.strip(" \t\n") if node.text is not None: - node.text = node.text.strip(' \t\n') + node.text = node.text.strip(" \t\n") visit_xml(old_xml, space_stripper) visit_xml(new_xml, space_stripper) def empty_node_remover(node): for child in node: - if not child.tail and not child.text and not child.items() and not child: + if ( + not child.tail + and not child.text + and not child.items() + and not child + ): node.remove(child) + visit_xml(old_xml, empty_node_remover) - needs_update = ElementTree.tostring(old_xml) != ElementTree.tostring(new_xml) + needs_update = xmlutil.to_dict(old_xml, True) != xmlutil.to_dict(new_xml, True) if needs_update and not test: - conn.storagePoolDefineXML(salt.utils.stringutils.to_str(ElementTree.tostring(new_xml))) + conn.storagePoolDefineXML( + salt.utils.stringutils.to_str(ElementTree.tostring(new_xml)) + ) finally: conn.close() return needs_update def list_pools(**kwargs): - ''' + """ List all storage pools. :param connection: libvirt connection URI, overriding defaults @@ -5289,7 +5369,7 @@ def list_pools(**kwargs): .. code-block:: bash salt '*' virt.list_pools - ''' + """ conn = __get_conn(**kwargs) try: return [pool.name() for pool in conn.listAllStoragePools()] @@ -5298,7 +5378,7 @@ def list_pools(**kwargs): def pool_info(name=None, **kwargs): - ''' + """ Return informations on a storage pool provided its name. :param name: libvirt storage pool name @@ -5315,45 +5395,49 @@ def pool_info(name=None, **kwargs): .. code-block:: bash salt '*' virt.pool_info default - ''' + """ result = {} conn = __get_conn(**kwargs) def _pool_extract_infos(pool): - ''' + """ Format the pool info dictionary :param pool: the libvirt pool object - ''' - states = ['inactive', 'building', 'running', 'degraded', 'inaccessible'] + """ + states = ["inactive", "building", "running", "degraded", "inaccessible"] infos = pool.info() - state = states[infos[0]] if infos[0] < len(states) else 'unknown' + state = states[infos[0]] if infos[0] < len(states) else "unknown" desc = ElementTree.fromstring(pool.XMLDesc()) - path_node = desc.find('target/path') + path_node = desc.find("target/path") return { - 'uuid': pool.UUIDString(), - 'state': state, - 'capacity': infos[1], - 'allocation': infos[2], - 'free': infos[3], - 'autostart': pool.autostart(), - 'persistent': pool.isPersistent(), - 'target_path': path_node.text if path_node is not None else None, - 'type': desc.get('type') + "uuid": pool.UUIDString(), + "state": state, + "capacity": infos[1], + "allocation": infos[2], + "free": infos[3], + "autostart": pool.autostart(), + "persistent": pool.isPersistent(), + "target_path": path_node.text if path_node is not None else None, + "type": desc.get("type"), } try: - pools = [pool for pool in conn.listAllStoragePools() if name is None or pool.name() == name] + pools = [ + pool + for pool in conn.listAllStoragePools() + if name is None or pool.name() == name + ] result = {pool.name(): _pool_extract_infos(pool) for pool in pools} except libvirt.libvirtError as err: - log.debug('Silenced libvirt error: %s', str(err)) + log.debug("Silenced libvirt error: %s", str(err)) finally: conn.close() return result def pool_get_xml(name, **kwargs): - ''' + """ Return the XML definition of a virtual storage pool :param name: libvirt storage pool name @@ -5368,7 +5452,7 @@ def pool_get_xml(name, **kwargs): .. code-block:: bash salt '*' virt.pool_get_xml default - ''' + """ conn = __get_conn(**kwargs) try: return conn.storagePoolLookupByName(name).XMLDesc() @@ -5377,7 +5461,7 @@ def pool_get_xml(name, **kwargs): def pool_start(name, **kwargs): - ''' + """ Start a defined libvirt storage pool. :param name: libvirt storage pool name @@ -5392,7 +5476,7 @@ def pool_start(name, **kwargs): .. code-block:: bash salt '*' virt.pool_start default - ''' + """ conn = __get_conn(**kwargs) try: pool = conn.storagePoolLookupByName(name) @@ -5402,7 +5486,7 @@ def pool_start(name, **kwargs): def pool_build(name, **kwargs): - ''' + """ Build a defined libvirt storage pool. :param name: libvirt storage pool name @@ -5417,7 +5501,7 @@ def pool_build(name, **kwargs): .. code-block:: bash salt '*' virt.pool_build default - ''' + """ conn = __get_conn(**kwargs) try: pool = conn.storagePoolLookupByName(name) @@ -5427,7 +5511,7 @@ def pool_build(name, **kwargs): def pool_stop(name, **kwargs): - ''' + """ Stop a defined libvirt storage pool. :param name: libvirt storage pool name @@ -5442,7 +5526,7 @@ def pool_stop(name, **kwargs): .. code-block:: bash salt '*' virt.pool_stop default - ''' + """ conn = __get_conn(**kwargs) try: pool = conn.storagePoolLookupByName(name) @@ -5452,7 +5536,7 @@ def pool_stop(name, **kwargs): def pool_undefine(name, **kwargs): - ''' + """ Remove a defined libvirt storage pool. The pool needs to be stopped before calling. :param name: libvirt storage pool name @@ -5467,7 +5551,7 @@ def pool_undefine(name, **kwargs): .. code-block:: bash salt '*' virt.pool_undefine default - ''' + """ conn = __get_conn(**kwargs) try: pool = conn.storagePoolLookupByName(name) @@ -5477,7 +5561,7 @@ def pool_undefine(name, **kwargs): def pool_delete(name, **kwargs): - ''' + """ Delete the resources of a defined libvirt storage pool. :param name: libvirt storage pool name @@ -5492,7 +5576,7 @@ def pool_delete(name, **kwargs): .. code-block:: bash salt '*' virt.pool_delete default - ''' + """ conn = __get_conn(**kwargs) try: pool = conn.storagePoolLookupByName(name) @@ -5502,7 +5586,7 @@ def pool_delete(name, **kwargs): def pool_refresh(name, **kwargs): - ''' + """ Refresh a defined libvirt storage pool. :param name: libvirt storage pool name @@ -5517,7 +5601,7 @@ def pool_refresh(name, **kwargs): .. code-block:: bash salt '*' virt.pool_refresh default - ''' + """ conn = __get_conn(**kwargs) try: pool = conn.storagePoolLookupByName(name) @@ -5526,8 +5610,8 @@ def pool_refresh(name, **kwargs): conn.close() -def pool_set_autostart(name, state='on', **kwargs): - ''' +def pool_set_autostart(name, state="on", **kwargs): + """ Set the autostart flag on a libvirt storage pool so that the storage pool will start with the host system on reboot. @@ -5545,17 +5629,17 @@ def pool_set_autostart(name, state='on', **kwargs): .. code-block:: bash salt "*" virt.pool_set_autostart <pool> <on | off> - ''' + """ conn = __get_conn(**kwargs) try: pool = conn.storagePoolLookupByName(name) - return not bool(pool.setAutostart(1 if state == 'on' else 0)) + return not bool(pool.setAutostart(1 if state == "on" else 0)) finally: conn.close() def pool_list_volumes(name, **kwargs): - ''' + """ List the volumes contained in a defined libvirt storage pool. :param name: libvirt storage pool name @@ -5570,7 +5654,7 @@ def pool_list_volumes(name, **kwargs): .. code-block:: bash salt "*" virt.pool_list_volumes <pool> - ''' + """ conn = __get_conn(**kwargs) try: pool = conn.storagePoolLookupByName(name) @@ -5580,27 +5664,28 @@ def pool_list_volumes(name, **kwargs): def _get_storage_vol(conn, pool, vol): - ''' + """ Helper function getting a storage volume. Will throw a libvirtError if the pool or the volume couldn't be found. :param conn: libvirt connection object to use :param pool: pool name :param vol: volume name - ''' + """ pool_obj = conn.storagePoolLookupByName(pool) return pool_obj.storageVolLookupByName(vol) def _is_valid_volume(vol): - ''' + """ Checks whether a volume is valid for further use since those may have disappeared since the last pool refresh. - ''' + """ try: # Getting info on an invalid volume raises error and libvirt logs an error def discarder(ctxt, error): # pylint: disable=unused-argument log.debug("Ignore libvirt error: %s", error[2]) + # Disable the libvirt error logging libvirt.registerErrorHandler(discarder, None) vol.info() @@ -5612,20 +5697,34 @@ def _is_valid_volume(vol): def _get_all_volumes_paths(conn): - ''' + """ Extract the path and backing stores path of all volumes. :param conn: libvirt connection to use - ''' - volumes = [vol for l in - [obj.listAllVolumes() for obj in conn.listAllStoragePools() - if obj.info()[0] == libvirt.VIR_STORAGE_POOL_RUNNING] for vol in l] - return {vol.path(): [path.text for path in ElementTree.fromstring(vol.XMLDesc()).findall('.//backingStore/path')] - for vol in volumes if _is_valid_volume(vol)} + """ + volumes = [ + vol + for l in [ + obj.listAllVolumes() + for obj in conn.listAllStoragePools() + if obj.info()[0] == libvirt.VIR_STORAGE_POOL_RUNNING + ] + for vol in l + ] + return { + vol.path(): [ + path.text + for path in ElementTree.fromstring(vol.XMLDesc()).findall( + ".//backingStore/path" + ) + ] + for vol in volumes + if _is_valid_volume(vol) + } def volume_infos(pool=None, volume=None, **kwargs): - ''' + """ Provide details on a storage volume. If no volume name is provided, the infos all the volumes contained in the pool are provided. If no pool is provided, the infos of the volumes of all pools are output. @@ -5643,7 +5742,7 @@ def volume_infos(pool=None, volume=None, **kwargs): .. code-block:: bash salt "*" virt.volume_infos <pool> <volume> - ''' + """ result = {} conn = __get_conn(**kwargs) try: @@ -5654,52 +5753,72 @@ def volume_infos(pool=None, volume=None, **kwargs): except CommandExecutionError: # Having no VM is not an error here. domains_list = [] - disks = {domain.name(): - {node.get('file') for node - in ElementTree.fromstring(domain.XMLDesc(0)).findall('.//disk/source/[@file]')} - for domain in domains_list} + disks = { + domain.name(): { + node.get("file") + for node in ElementTree.fromstring(domain.XMLDesc(0)).findall( + ".//disk/source/[@file]" + ) + } + for domain in domains_list + } def _volume_extract_infos(vol): - ''' + """ Format the volume info dictionary :param vol: the libvirt storage volume object. - ''' - types = ['file', 'block', 'dir', 'network', 'netdir', 'ploop'] + """ + types = ["file", "block", "dir", "network", "netdir", "ploop"] infos = vol.info() # If we have a path, check its use. used_by = [] if vol.path(): - as_backing_store = {path for (path, all_paths) in backing_stores.items() if vol.path() in all_paths} - used_by = [vm_name for (vm_name, vm_disks) in disks.items() - if vm_disks & as_backing_store or vol.path() in vm_disks] + as_backing_store = { + path + for (path, all_paths) in backing_stores.items() + if vol.path() in all_paths + } + used_by = [ + vm_name + for (vm_name, vm_disks) in disks.items() + if vm_disks & as_backing_store or vol.path() in vm_disks + ] return { - 'type': types[infos[0]] if infos[0] < len(types) else 'unknown', - 'key': vol.key(), - 'path': vol.path(), - 'capacity': infos[1], - 'allocation': infos[2], - 'used_by': used_by, + "type": types[infos[0]] if infos[0] < len(types) else "unknown", + "key": vol.key(), + "path": vol.path(), + "capacity": infos[1], + "allocation": infos[2], + "used_by": used_by, } - pools = [obj for obj in conn.listAllStoragePools() - if (pool is None or obj.name() == pool) and obj.info()[0] == libvirt.VIR_STORAGE_POOL_RUNNING] - vols = {pool_obj.name(): {vol.name(): _volume_extract_infos(vol) - for vol in pool_obj.listAllVolumes() - if (volume is None or vol.name() == volume) and _is_valid_volume(vol)} - for pool_obj in pools} + pools = [ + obj + for obj in conn.listAllStoragePools() + if (pool is None or obj.name() == pool) + and obj.info()[0] == libvirt.VIR_STORAGE_POOL_RUNNING + ] + vols = { + pool_obj.name(): { + vol.name(): _volume_extract_infos(vol) + for vol in pool_obj.listAllVolumes() + if (volume is None or vol.name() == volume) and _is_valid_volume(vol) + } + for pool_obj in pools + } return {pool_name: volumes for (pool_name, volumes) in vols.items() if volumes} except libvirt.libvirtError as err: - log.debug('Silenced libvirt error: %s', str(err)) + log.debug("Silenced libvirt error: %s", str(err)) finally: conn.close() return result def volume_delete(pool, volume, **kwargs): - ''' + """ Delete a libvirt managed volume. :param pool: libvirt storage pool name @@ -5715,7 +5834,7 @@ def volume_delete(pool, volume, **kwargs): .. code-block:: bash salt "*" virt.volume_delete <pool> <volume> - ''' + """ conn = __get_conn(**kwargs) try: vol = _get_storage_vol(conn, pool, volume) diff --git a/salt/modules/virtualenv_mod.py b/salt/modules/virtualenv_mod.py index b70f1dac85e..3bf7d8dd8f7 100644 --- a/salt/modules/virtualenv_mod.py +++ b/salt/modules/virtualenv_mod.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Create virtualenv environments. .. versionadded:: 0.17.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import glob -import shutil import logging import os +import shutil import sys # Import salt libs @@ -21,22 +22,22 @@ import salt.utils.verify from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext.six import string_types -KNOWN_BINARY_NAMES = frozenset([ - 'virtualenv-{0}.{1}'.format(*sys.version_info[:2]), - 'virtualenv{0}'.format(sys.version_info[0]), - 'virtualenv', -]) +KNOWN_BINARY_NAMES = frozenset( + [ + "virtualenv-{0}.{1}".format(*sys.version_info[:2]), + "virtualenv{0}".format(sys.version_info[0]), + "virtualenv", + ] +) log = logging.getLogger(__name__) -__opts__ = { - 'venv_bin': salt.utils.path.which_bin(KNOWN_BINARY_NAMES) or 'virtualenv' -} +__opts__ = {"venv_bin": salt.utils.path.which_bin(KNOWN_BINARY_NAMES) or "virtualenv"} __pillar__ = {} # Define the module's virtual name -__virtualname__ = 'virtualenv' +__virtualname__ = "virtualenv" def __virtual__(): @@ -44,53 +45,55 @@ def __virtual__(): def virtualenv_ver(venv_bin, user=None, **kwargs): - ''' + """ return virtualenv version if exists - ''' + """ # Virtualenv package try: import virtualenv - version = getattr(virtualenv, '__version__', None) + + version = getattr(virtualenv, "__version__", None) if not version: version = virtualenv.virtualenv_version virtualenv_version_info = tuple( - [int(i) for i in version.split('rc')[0].split('.')] + [int(i) for i in version.split("rc")[0].split(".")] ) except ImportError: # Unable to import?? Let's parse the version from the console - version_cmd = [venv_bin, '--version'] - ret = __salt__['cmd.run_all']( - version_cmd, runas=user, python_shell=False, **kwargs - ) - if ret['retcode'] > 0 or not ret['stdout'].strip(): + version_cmd = [venv_bin, "--version"] + ret = __salt__["cmd.run_all"]( + version_cmd, runas=user, python_shell=False, **kwargs + ) + if ret["retcode"] > 0 or not ret["stdout"].strip(): raise CommandExecutionError( - 'Unable to get the virtualenv version output using \'{0}\'. ' - 'Returned data: {1}'.format(version_cmd, ret) + "Unable to get the virtualenv version output using '{0}'. " + "Returned data: {1}".format(version_cmd, ret) ) virtualenv_version_info = tuple( - [int(i) for i in - ret['stdout'].strip().split('rc')[0].split('.')] + [int(i) for i in ret["stdout"].strip().split("rc")[0].split(".")] ) return virtualenv_version_info -def create(path, - venv_bin=None, - system_site_packages=False, - distribute=False, - clear=False, - python=None, - extra_search_dir=None, - never_download=None, - prompt=None, - pip=False, - symlinks=None, - upgrade=None, - user=None, - use_vt=False, - saltenv='base', - **kwargs): - ''' +def create( + path, + venv_bin=None, + system_site_packages=False, + distribute=False, + clear=False, + python=None, + extra_search_dir=None, + never_download=None, + prompt=None, + pip=False, + symlinks=None, + upgrade=None, + user=None, + use_vt=False, + saltenv="base", + **kwargs +): + """ Create a virtualenv path @@ -173,25 +176,25 @@ def create(path, - env: - VIRTUALENV_ALWAYS_COPY: 1 - ''' + """ if venv_bin is None: - venv_bin = __opts__.get('venv_bin') or __pillar__.get('venv_bin') + venv_bin = __opts__.get("venv_bin") or __pillar__.get("venv_bin") cmd = [venv_bin] - if 'pyvenv' not in venv_bin: + if "pyvenv" not in venv_bin: # ----- Stop the user if pyvenv only options are used ---------------> # If any of the following values are not None, it means that the user # is actually passing a True or False value. Stop Him! if upgrade is not None: raise CommandExecutionError( - 'The `upgrade`(`--upgrade`) option is not supported ' - 'by \'{0}\''.format(venv_bin) + "The `upgrade`(`--upgrade`) option is not supported " + "by '{0}'".format(venv_bin) ) elif symlinks is not None: raise CommandExecutionError( - 'The `symlinks`(`--symlinks`) option is not supported ' - 'by \'{0}\''.format(venv_bin) + "The `symlinks`(`--symlinks`) option is not supported " + "by '{0}'".format(venv_bin) ) # <---- Stop the user if pyvenv only options are used ---------------- @@ -200,130 +203,138 @@ def create(path, if distribute: if virtualenv_version_info >= (1, 10): log.info( - 'The virtualenv \'--distribute\' option has been ' - 'deprecated in virtualenv(>=1.10), as such, the ' - '\'distribute\' option to `virtualenv.create()` has ' - 'also been deprecated and it\'s not necessary anymore.' + "The virtualenv '--distribute' option has been " + "deprecated in virtualenv(>=1.10), as such, the " + "'distribute' option to `virtualenv.create()` has " + "also been deprecated and it's not necessary anymore." ) else: - cmd.append('--distribute') + cmd.append("--distribute") - if python is not None and python.strip() != '': + if python is not None and python.strip() != "": if not salt.utils.path.which(python): raise CommandExecutionError( - 'Cannot find requested python ({0}).'.format(python) + "Cannot find requested python ({0}).".format(python) ) - cmd.append('--python={0}'.format(python)) + cmd.append("--python={0}".format(python)) if extra_search_dir is not None: - if isinstance(extra_search_dir, string_types) and \ - extra_search_dir.strip() != '': - extra_search_dir = [ - e.strip() for e in extra_search_dir.split(',') - ] + if ( + isinstance(extra_search_dir, string_types) + and extra_search_dir.strip() != "" + ): + extra_search_dir = [e.strip() for e in extra_search_dir.split(",")] for entry in extra_search_dir: - cmd.append('--extra-search-dir={0}'.format(entry)) + cmd.append("--extra-search-dir={0}".format(entry)) if never_download is True: if (1, 10) <= virtualenv_version_info < (14, 0, 0): log.info( - '--never-download was deprecated in 1.10.0, but reimplemented in 14.0.0. ' - 'If this feature is needed, please install a supported virtualenv version.' + "--never-download was deprecated in 1.10.0, but reimplemented in 14.0.0. " + "If this feature is needed, please install a supported virtualenv version." ) else: - cmd.append('--never-download') - if prompt is not None and prompt.strip() != '': - cmd.append('--prompt=\'{0}\''.format(prompt)) + cmd.append("--never-download") + if prompt is not None and prompt.strip() != "": + cmd.append("--prompt='{0}'".format(prompt)) else: # venv module from the Python >= 3.3 standard library # ----- Stop the user if virtualenv only options are being used -----> # If any of the following values are not None, it means that the user # is actually passing a True or False value. Stop Him! - if python is not None and python.strip() != '': + if python is not None and python.strip() != "": raise CommandExecutionError( - 'The `python`(`--python`) option is not supported ' - 'by \'{0}\''.format(venv_bin) + "The `python`(`--python`) option is not supported " + "by '{0}'".format(venv_bin) ) - elif extra_search_dir is not None and extra_search_dir.strip() != '': + elif extra_search_dir is not None and extra_search_dir.strip() != "": raise CommandExecutionError( - 'The `extra_search_dir`(`--extra-search-dir`) option is not ' - 'supported by \'{0}\''.format(venv_bin) + "The `extra_search_dir`(`--extra-search-dir`) option is not " + "supported by '{0}'".format(venv_bin) ) elif never_download is not None: raise CommandExecutionError( - 'The `never_download`(`--never-download`) option is not ' - 'supported by \'{0}\''.format(venv_bin) + "The `never_download`(`--never-download`) option is not " + "supported by '{0}'".format(venv_bin) ) - elif prompt is not None and prompt.strip() != '': + elif prompt is not None and prompt.strip() != "": raise CommandExecutionError( - 'The `prompt`(`--prompt`) option is not supported ' - 'by \'{0}\''.format(venv_bin) + "The `prompt`(`--prompt`) option is not supported " + "by '{0}'".format(venv_bin) ) # <---- Stop the user if virtualenv only options are being used ------ if upgrade is True: - cmd.append('--upgrade') + cmd.append("--upgrade") if symlinks is True: - cmd.append('--symlinks') + cmd.append("--symlinks") # Common options to virtualenv and pyvenv if clear is True: - cmd.append('--clear') + cmd.append("--clear") if system_site_packages is True: - cmd.append('--system-site-packages') + cmd.append("--system-site-packages") # Finally the virtualenv path cmd.append(path) # Let's create the virtualenv - ret = __salt__['cmd.run_all'](cmd, runas=user, python_shell=False, **kwargs) - if ret['retcode'] != 0: + ret = __salt__["cmd.run_all"](cmd, runas=user, python_shell=False, **kwargs) + if ret["retcode"] != 0: # Something went wrong. Let's bail out now! return ret # Check if distribute and pip are already installed if salt.utils.platform.is_windows(): - venv_python = os.path.join(path, 'Scripts', 'python.exe') - venv_pip = os.path.join(path, 'Scripts', 'pip.exe') - venv_setuptools = os.path.join(path, 'Scripts', 'easy_install.exe') + venv_python = os.path.join(path, "Scripts", "python.exe") + venv_pip = os.path.join(path, "Scripts", "pip.exe") + venv_setuptools = os.path.join(path, "Scripts", "easy_install.exe") else: - venv_python = os.path.join(path, 'bin', 'python') - venv_pip = os.path.join(path, 'bin', 'pip') - venv_setuptools = os.path.join(path, 'bin', 'easy_install') + venv_python = os.path.join(path, "bin", "python") + venv_pip = os.path.join(path, "bin", "pip") + venv_setuptools = os.path.join(path, "bin", "easy_install") # Install setuptools if (pip or distribute) and not os.path.exists(venv_setuptools): _install_script( - 'https://bitbucket.org/pypa/setuptools/raw/default/ez_setup.py', - path, venv_python, user, saltenv=saltenv, use_vt=use_vt + "https://bitbucket.org/pypa/setuptools/raw/default/ez_setup.py", + path, + venv_python, + user, + saltenv=saltenv, + use_vt=use_vt, ) # clear up the distribute archive which gets downloaded - for fpath in glob.glob(os.path.join(path, 'distribute-*.tar.gz*')): + for fpath in glob.glob(os.path.join(path, "distribute-*.tar.gz*")): os.unlink(fpath) - if ret['retcode'] != 0: + if ret["retcode"] != 0: # Something went wrong. Let's bail out now! return ret # Install pip if pip and not os.path.exists(venv_pip): _ret = _install_script( - 'https://bootstrap.pypa.io/get-pip.py', - path, venv_python, user, saltenv=saltenv, use_vt=use_vt + "https://bootstrap.pypa.io/get-pip.py", + path, + venv_python, + user, + saltenv=saltenv, + use_vt=use_vt, ) # Let's update the return dictionary with the details from the pip # installation ret.update( - retcode=_ret['retcode'], - stdout='{0}\n{1}'.format(ret['stdout'], _ret['stdout']).strip(), - stderr='{0}\n{1}'.format(ret['stderr'], _ret['stderr']).strip(), + retcode=_ret["retcode"], + stdout="{0}\n{1}".format(ret["stdout"], _ret["stdout"]).strip(), + stderr="{0}\n{1}".format(ret["stderr"], _ret["stderr"]).strip(), ) return ret def get_site_packages(venv): - ''' + """ Return the path to the site-packages directory of a virtualenv venv @@ -334,23 +345,22 @@ def get_site_packages(venv): .. code-block:: bash salt '*' virtualenv.get_site_packages /path/to/my/venv - ''' + """ bin_path = _verify_virtualenv(venv) - ret = __salt__['cmd.exec_code_all']( + ret = __salt__["cmd.exec_code_all"]( bin_path, - 'from distutils import sysconfig; ' - 'print(sysconfig.get_python_lib())' + "from distutils import sysconfig; " "print(sysconfig.get_python_lib())", ) - if ret['retcode'] != 0: - raise CommandExecutionError('{stdout}\n{stderr}'.format(**ret)) + if ret["retcode"] != 0: + raise CommandExecutionError("{stdout}\n{stderr}".format(**ret)) - return ret['stdout'] + return ret["stdout"] def get_distribution_path(venv, distribution): - ''' + """ Return the path to a distribution installed inside a virtualenv .. versionadded:: 2016.3.0 @@ -366,28 +376,24 @@ def get_distribution_path(venv, distribution): .. code-block:: bash salt '*' virtualenv.get_distribution_path /path/to/my/venv my_distribution - ''' + """ _verify_safe_py_code(distribution) bin_path = _verify_virtualenv(venv) - ret = __salt__['cmd.exec_code_all']( + ret = __salt__["cmd.exec_code_all"]( bin_path, - 'import pkg_resources; ' - "print(pkg_resources.get_distribution('{0}').location)".format( - distribution - ) + "import pkg_resources; " + "print(pkg_resources.get_distribution('{0}').location)".format(distribution), ) - if ret['retcode'] != 0: - raise CommandExecutionError('{stdout}\n{stderr}'.format(**ret)) + if ret["retcode"] != 0: + raise CommandExecutionError("{stdout}\n{stderr}".format(**ret)) - return ret['stdout'] + return ret["stdout"] -def get_resource_path(venv, - package=None, - resource=None): - ''' +def get_resource_path(venv, package=None, resource=None): + """ Return the path to a package resource installed inside a virtualenv .. versionadded:: 2015.5.0 @@ -410,29 +416,26 @@ def get_resource_path(venv, .. code-block:: bash salt '*' virtualenv.get_resource_path /path/to/my/venv my_package my/resource.xml - ''' + """ _verify_safe_py_code(package, resource) bin_path = _verify_virtualenv(venv) - ret = __salt__['cmd.exec_code_all']( + ret = __salt__["cmd.exec_code_all"]( bin_path, - 'import pkg_resources; ' - "print(pkg_resources.resource_filename('{0}', '{1}'))".format( - package, - resource - ) + "import pkg_resources; " + "print(pkg_resources.resource_filename('{0}', '{1}'))".format( + package, resource + ), ) - if ret['retcode'] != 0: - raise CommandExecutionError('{stdout}\n{stderr}'.format(**ret)) + if ret["retcode"] != 0: + raise CommandExecutionError("{stdout}\n{stderr}".format(**ret)) - return ret['stdout'] + return ret["stdout"] -def get_resource_content(venv, - package=None, - resource=None): - ''' +def get_resource_content(venv, package=None, resource=None): + """ Return the content of a package resource installed inside a virtualenv .. versionadded:: 2015.5.0 @@ -455,42 +458,39 @@ def get_resource_content(venv, .. code-block:: bash salt '*' virtualenv.get_resource_content /path/to/my/venv my_package my/resource.xml - ''' + """ _verify_safe_py_code(package, resource) bin_path = _verify_virtualenv(venv) - ret = __salt__['cmd.exec_code_all']( + ret = __salt__["cmd.exec_code_all"]( bin_path, - 'import pkg_resources; ' - "print(pkg_resources.resource_string('{0}', '{1}'))".format( - package, - resource - ) + "import pkg_resources; " + "print(pkg_resources.resource_string('{0}', '{1}'))".format(package, resource), ) - if ret['retcode'] != 0: - raise CommandExecutionError('{stdout}\n{stderr}'.format(**ret)) + if ret["retcode"] != 0: + raise CommandExecutionError("{stdout}\n{stderr}".format(**ret)) - return ret['stdout'] + return ret["stdout"] -def _install_script(source, cwd, python, user, saltenv='base', use_vt=False): +def _install_script(source, cwd, python, user, saltenv="base", use_vt=False): if not salt.utils.platform.is_windows(): tmppath = salt.utils.files.mkstemp(dir=cwd) else: - tmppath = __salt__['cp.cache_file'](source, saltenv) + tmppath = __salt__["cp.cache_file"](source, saltenv) if not salt.utils.platform.is_windows(): - fn_ = __salt__['cp.cache_file'](source, saltenv) + fn_ = __salt__["cp.cache_file"](source, saltenv) shutil.copyfile(fn_, tmppath) os.chmod(tmppath, 0o500) - os.chown(tmppath, __salt__['file.user_to_uid'](user), -1) + os.chown(tmppath, __salt__["file.user_to_uid"](user), -1) try: - return __salt__['cmd.run_all']( + return __salt__["cmd.run_all"]( [python, tmppath], runas=user, cwd=cwd, - env={'VIRTUAL_ENV': cwd}, + env={"VIRTUAL_ENV": cwd}, use_vt=use_vt, python_shell=False, ) @@ -502,14 +502,16 @@ def _verify_safe_py_code(*args): for arg in args: if not salt.utils.verify.safe_py_code(arg): raise SaltInvocationError( - 'Unsafe python code detected in \'{0}\''.format(arg) + "Unsafe python code detected in '{0}'".format(arg) ) def _verify_virtualenv(venv_path): - bin_path = os.path.join(venv_path, 'bin/python') + bin_path = os.path.join(venv_path, "bin/python") if not os.path.exists(bin_path): raise CommandExecutionError( - 'Path \'{0}\' does not appear to be a virtualenv: bin/python not found.'.format(venv_path) + "Path '{0}' does not appear to be a virtualenv: bin/python not found.".format( + venv_path + ) ) return bin_path diff --git a/salt/modules/vmctl.py b/salt/modules/vmctl.py index 94be39c329d..9aa19f1483d 100644 --- a/salt/modules/vmctl.py +++ b/salt/modules/vmctl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage vms running on the OpenBSD VMM hypervisor using vmctl(8). .. versionadded:: 2019.2.0 @@ -10,7 +10,7 @@ Manage vms running on the OpenBSD VMM hypervisor using vmctl(8). This module requires the `vmd` service to be running on the OpenBSD target machine. -''' +""" from __future__ import absolute_import @@ -20,35 +20,38 @@ import re # Imoprt salt libs: import salt.utils.path -from salt.exceptions import (CommandExecutionError, SaltInvocationError) +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext.six.moves import zip log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only works on OpenBSD with vmctl(8) present. - ''' - if __grains__['os'] == 'OpenBSD' and salt.utils.path.which('vmctl'): + """ + if __grains__["os"] == "OpenBSD" and salt.utils.path.which("vmctl"): return True - return (False, 'The vmm execution module cannot be loaded: either the system is not OpenBSD or the vmctl binary was not found') + return ( + False, + "The vmm execution module cannot be loaded: either the system is not OpenBSD or the vmctl binary was not found", + ) def _id_to_name(id): - ''' + """ Lookup the name associated with a VM id. - ''' + """ vm = status(id=id) if vm == {}: return None else: - return vm['name'] + return vm["name"] def create_disk(name, size): - ''' + """ Create a VMM disk with the specified `name` and `size`. size: @@ -59,27 +62,25 @@ def create_disk(name, size): .. code-block:: bash salt '*' vmctl.create_disk /path/to/disk.img size=10G - ''' + """ ret = False - cmd = 'vmctl create {0} -s {1}'.format(name, size) + cmd = "vmctl create {0} -s {1}".format(name, size) - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if result['retcode'] == 0: + if result["retcode"] == 0: ret = True else: raise CommandExecutionError( - 'Problem encountered creating disk image', - info={'errors': [result['stderr']], 'changes': ret} + "Problem encountered creating disk image", + info={"errors": [result["stderr"]], "changes": ret}, ) return ret def load(path): - ''' + """ Load additional configuration from the specified file. path @@ -90,25 +91,23 @@ def load(path): .. code-block:: bash salt '*' vmctl.load path=/etc/vm.switches.conf - ''' + """ ret = False - cmd = 'vmctl load {0}'.format(path) - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - if result['retcode'] == 0: + cmd = "vmctl load {0}".format(path) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if result["retcode"] == 0: ret = True else: raise CommandExecutionError( - 'Problem encountered running vmctl', - info={'errors': [result['stderr']], 'changes': ret} + "Problem encountered running vmctl", + info={"errors": [result["stderr"]], "changes": ret}, ) return ret def reload(): - ''' + """ Remove all stopped VMs and reload configuration from the default configuration file. CLI Example: @@ -116,25 +115,23 @@ def reload(): .. code-block:: bash salt '*' vmctl.reload - ''' + """ ret = False - cmd = 'vmctl reload' - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - if result['retcode'] == 0: + cmd = "vmctl reload" + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if result["retcode"] == 0: ret = True else: raise CommandExecutionError( - 'Problem encountered running vmctl', - info={'errors': [result['stderr']], 'changes': ret} + "Problem encountered running vmctl", + info={"errors": [result["stderr"]], "changes": ret}, ) return ret def reset(all=False, vms=False, switches=False): - ''' + """ Reset the running state of VMM or a subsystem. all: @@ -152,34 +149,41 @@ def reset(all=False, vms=False, switches=False): .. code-block:: bash salt '*' vmctl.reset all=True - ''' + """ ret = False - cmd = ['vmctl', 'reset'] + cmd = ["vmctl", "reset"] if all: - cmd.append('all') + cmd.append("all") elif vms: - cmd.append('vms') + cmd.append("vms") elif switches: - cmd.append('switches') + cmd.append("switches") - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - if result['retcode'] == 0: + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + if result["retcode"] == 0: ret = True else: raise CommandExecutionError( - 'Problem encountered running vmctl', - info={'errors': [result['stderr']], 'changes': ret} + "Problem encountered running vmctl", + info={"errors": [result["stderr"]], "changes": ret}, ) return ret -def start(name=None, id=None, bootpath=None, disk=None, disks=None, local_iface=False, - memory=None, nics=0, switch=None): - ''' +def start( + name=None, + id=None, + bootpath=None, + disk=None, + disks=None, + local_iface=False, + memory=None, + nics=0, + switch=None, +): + """ Starts a VM defined by the specified parameters. When both a name and id are provided, the id is ignored. @@ -215,9 +219,9 @@ def start(name=None, id=None, bootpath=None, disk=None, disks=None, local_iface= salt '*' vmctl.start 2 # start VM with id 2 salt '*' vmctl.start name=web1 bootpath='/bsd.rd' nics=2 memory=512M disk='/disk.img' - ''' - ret = {'changes': False, 'console': None} - cmd = ['vmctl', 'start'] + """ + ret = {"changes": False, "console": None} + cmd = ["vmctl", "start"] if not (name or id): raise SaltInvocationError('Must provide either "name" or "id"') @@ -228,63 +232,61 @@ def start(name=None, id=None, bootpath=None, disk=None, disks=None, local_iface= name = _id_to_name(id) if nics > 0: - cmd.append('-i {0}'.format(nics)) + cmd.append("-i {0}".format(nics)) # Paths cannot be appended as otherwise the inserted whitespace is treated by # vmctl as being part of the path. if bootpath: - cmd.extend(['-b', bootpath]) + cmd.extend(["-b", bootpath]) if memory: - cmd.append('-m {0}'.format(memory)) + cmd.append("-m {0}".format(memory)) if switch: - cmd.append('-n {0}'.format(switch)) + cmd.append("-n {0}".format(switch)) if local_iface: - cmd.append('-L') + cmd.append("-L") if disk and (disks and len(disks) > 0): raise SaltInvocationError('Must provide either "disks" or "disk"') if disk: - cmd.extend(['-d', disk]) + cmd.extend(["-d", disk]) if disks and len(disks) > 0: - cmd.extend(['-d', x] for x in disks) + cmd.extend(["-d", x] for x in disks) # Before attempting to define a new VM, make sure it doesn't already exist. # Otherwise return to indicate nothing was changed. if len(cmd) > 3: vmstate = status(name) if vmstate: - ret['comment'] = 'VM already exists and cannot be redefined' + ret["comment"] = "VM already exists and cannot be redefined" return ret - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if result['retcode'] == 0: - ret['changes'] = True - m = re.match(r'.*successfully, tty (\/dev.*)', result['stderr']) + if result["retcode"] == 0: + ret["changes"] = True + m = re.match(r".*successfully, tty (\/dev.*)", result["stderr"]) if m: - ret['console'] = m.groups()[0] + ret["console"] = m.groups()[0] else: - m = re.match(r'.*Operation already in progress$', result['stderr']) + m = re.match(r".*Operation already in progress$", result["stderr"]) if m: - ret['changes'] = False + ret["changes"] = False else: raise CommandExecutionError( - 'Problem encountered running vmctl', - info={'errors': [result['stderr']], 'changes': ret} + "Problem encountered running vmctl", + info={"errors": [result["stderr"]], "changes": ret}, ) return ret def status(name=None, id=None): - ''' + """ List VMs running on the host, or only the VM specified by ``id``. When both a name and id are provided, the id is ignored. @@ -300,22 +302,20 @@ def status(name=None, id=None): salt '*' vmctl.status # to list all VMs salt '*' vmctl.status name=web1 # to get a single VM - ''' + """ ret = {} - cmd = ['vmctl', 'status'] + cmd = ["vmctl", "status"] - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered running vmctl', - info={'error': [result['stderr']], 'changes': ret} + "Problem encountered running vmctl", + info={"error": [result["stderr"]], "changes": ret}, ) # Grab the header and save it with the lowercase names. - header = result['stdout'].splitlines()[0].split() + header = result["stdout"].splitlines()[0].split() header = list([x.lower() for x in header]) # A VM can be in one of the following states (from vmm.c:vcpu_state_decode()) @@ -325,25 +325,25 @@ def status(name=None, id=None): # - terminated # - unknown - for line in result['stdout'].splitlines()[1:]: + for line in result["stdout"].splitlines()[1:]: data = line.split() vm = dict(list(zip(header, data))) - vmname = vm.pop('name') - if vm['pid'] == '-': + vmname = vm.pop("name") + if vm["pid"] == "-": # If the VM has no PID it's not running. - vm['state'] = 'stopped' - elif vmname and data[-2] == '-': + vm["state"] = "stopped" + elif vmname and data[-2] == "-": # When a VM does have a PID and the second to last field is a '-', it's # transitioning to another state. A VM name itself cannot contain a # '-' so it's safe to split on '-'. - vm['state'] = data[-1] + vm["state"] = data[-1] else: - vm['state'] = 'running' + vm["state"] = "running" # When the status is requested of a single VM (by name) which is stopping, # vmctl doesn't print the status line. So we'll parse the full list and # return when we've found the requested VM. - if id and int(vm['id']) == id: + if id and int(vm["id"]) == id: return {vmname: vm} elif name and vmname == name: return {vmname: vm} @@ -359,7 +359,7 @@ def status(name=None, id=None): def stop(name=None, id=None): - ''' + """ Stop (terminate) the VM identified by the given id or name. When both a name and id are provided, the id is ignored. @@ -374,9 +374,9 @@ def stop(name=None, id=None): .. code-block:: bash salt '*' vmctl.stop name=alpine - ''' + """ ret = {} - cmd = ['vmctl', 'stop'] + cmd = ["vmctl", "stop"] if not (name or id): raise SaltInvocationError('Must provide either "name" or "id"') @@ -385,19 +385,17 @@ def stop(name=None, id=None): else: cmd.append(id) - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) - if result['retcode'] == 0: - if re.match('^vmctl: sent request to terminate vm.*', result['stderr']): - ret['changes'] = True + if result["retcode"] == 0: + if re.match("^vmctl: sent request to terminate vm.*", result["stderr"]): + ret["changes"] = True else: - ret['changes'] = False + ret["changes"] = False else: raise CommandExecutionError( - 'Problem encountered running vmctl', - info={'errors': [result['stderr']], 'changes': ret} + "Problem encountered running vmctl", + info={"errors": [result["stderr"]], "changes": ret}, ) return ret diff --git a/salt/modules/vsphere.py b/salt/modules/vsphere.py index c523d2fb94b..135f038a695 100644 --- a/salt/modules/vsphere.py +++ b/salt/modules/vsphere.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage VMware vCenter servers and ESXi hosts. .. versionadded:: 2015.8.4 @@ -180,20 +180,17 @@ connection credentials are used instead of vCenter credentials, the ``host_names coredump-location.example.com port: 6500 -''' +""" # Import Python Libs from __future__ import absolute_import + import datetime +import inspect import logging import sys -import inspect from functools import wraps -# Import Salt Libs -from salt.ext import six -from salt.ext.six.moves import range -from salt.ext.six.moves import zip import salt.utils.args import salt.utils.dictupdate as dictupdate import salt.utils.http @@ -201,38 +198,69 @@ import salt.utils.path import salt.utils.pbm import salt.utils.vmware import salt.utils.vsan -from salt.utils.listdiffer import list_diff -from salt.utils.dictdiffer import recursive_diff -from salt.exceptions import CommandExecutionError, VMwareSaltError, \ - ArgumentValueError, InvalidConfigError, VMwareObjectRetrievalError, \ - VMwareApiError, InvalidEntityError, VMwareObjectExistsError -from salt.utils.decorators import depends, ignores_kwargs -from salt.config.schemas.esxcluster import ESXClusterConfigSchema, \ - ESXClusterEntitySchema +from salt.config.schemas.esxcluster import ( + ESXClusterConfigSchema, + ESXClusterEntitySchema, +) +from salt.config.schemas.esxi import ( + DiskGroupsDiskIdSchema, + SimpleHostCacheSchema, + VmfsDatastoreSchema, +) +from salt.config.schemas.esxvm import ( + ESXVirtualMachineDeleteSchema, + ESXVirtualMachineUnregisterSchema, +) from salt.config.schemas.vcenter import VCenterEntitySchema -from salt.config.schemas.esxi import DiskGroupsDiskIdSchema, \ - VmfsDatastoreSchema, SimpleHostCacheSchema -from salt.config.schemas.esxvm import ESXVirtualMachineDeleteSchema, \ - ESXVirtualMachineUnregisterSchema +from salt.exceptions import ( + ArgumentValueError, + CommandExecutionError, + InvalidConfigError, + InvalidEntityError, + VMwareApiError, + VMwareObjectExistsError, + VMwareObjectRetrievalError, + VMwareSaltError, +) + +# Import Salt Libs +from salt.ext import six +from salt.ext.six.moves import range, zip +from salt.utils.decorators import depends, ignores_kwargs +from salt.utils.dictdiffer import recursive_diff +from salt.utils.listdiffer import list_diff log = logging.getLogger(__name__) # Import Third Party Libs try: import jsonschema + HAS_JSONSCHEMA = True except ImportError: HAS_JSONSCHEMA = False try: - from pyVmomi import vim, vmodl, pbm, VmomiSupport # pylint: disable=no-name-in-module + # pylint: disable=no-name-in-module + from pyVmomi import ( + vim, + vmodl, + pbm, + VmomiSupport, + ) + + # pylint: enable=no-name-in-module # We check the supported vim versions to infer the pyVmomi version - if 'vim25/6.0' in VmomiSupport.versionMap and \ - sys.version_info > (2, 7) and sys.version_info < (2, 7, 9): + if ( + "vim25/6.0" in VmomiSupport.versionMap + and sys.version_info > (2, 7) + and sys.version_info < (2, 7, 9) + ): - log.debug('pyVmomi not loaded: Incompatible versions ' - 'of Python. See Issue #29537.') + log.debug( + "pyVmomi not loaded: Incompatible versions " "of Python. See Issue #29537." + ) raise ImportError() HAS_PYVMOMI = True except ImportError: @@ -248,25 +276,34 @@ try: # Error Handling from com.vmware.vapi.std.errors_client import ( - AlreadyExists, InvalidArgument, - NotFound, Unauthenticated, Unauthorized + AlreadyExists, + InvalidArgument, + NotFound, + Unauthenticated, + Unauthorized, + ) + + vsphere_errors = ( + AlreadyExists, + InvalidArgument, + NotFound, + Unauthenticated, + Unauthorized, ) - vsphere_errors = (AlreadyExists, InvalidArgument, - NotFound, Unauthenticated, Unauthorized,) HAS_VSPHERE_SDK = True except ImportError: HAS_VSPHERE_SDK = False # pylint: enable=unused-import # ESXI -esx_cli = salt.utils.path.which('esxcli') +esx_cli = salt.utils.path.which("esxcli") if esx_cli: HAS_ESX_CLI = True else: HAS_ESX_CLI = False -__virtualname__ = 'vsphere' -__proxyenabled__ = ['esxi', 'esxcluster', 'esxdatacenter', 'vcenter', 'esxvm'] +__virtualname__ = "vsphere" +__proxyenabled__ = ["esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm"] def __virtual__(): @@ -274,7 +311,7 @@ def __virtual__(): def get_proxy_type(): - ''' + """ Returns the proxy type retrieved either from the pillar of from the proxy minion's config. Returns ``<undefined>`` otherwise. @@ -283,63 +320,69 @@ def get_proxy_type(): .. code-block:: bash salt '*' vsphere.get_proxy_type - ''' - if __pillar__.get('proxy', {}).get('proxytype'): - return __pillar__['proxy']['proxytype'] - if __opts__.get('proxy', {}).get('proxytype'): - return __opts__['proxy']['proxytype'] - return '<undefined>' + """ + if __pillar__.get("proxy", {}).get("proxytype"): + return __pillar__["proxy"]["proxytype"] + if __opts__.get("proxy", {}).get("proxytype"): + return __opts__["proxy"]["proxytype"] + return "<undefined>" def _get_proxy_connection_details(): - ''' + """ Returns the connection details of the following proxies: esxi - ''' + """ proxytype = get_proxy_type() - if proxytype == 'esxi': - details = __salt__['esxi.get_details']() - elif proxytype == 'esxcluster': - details = __salt__['esxcluster.get_details']() - elif proxytype == 'esxdatacenter': - details = __salt__['esxdatacenter.get_details']() - elif proxytype == 'vcenter': - details = __salt__['vcenter.get_details']() - elif proxytype == 'esxvm': - details = __salt__['esxvm.get_details']() + if proxytype == "esxi": + details = __salt__["esxi.get_details"]() + elif proxytype == "esxcluster": + details = __salt__["esxcluster.get_details"]() + elif proxytype == "esxdatacenter": + details = __salt__["esxdatacenter.get_details"]() + elif proxytype == "vcenter": + details = __salt__["vcenter.get_details"]() + elif proxytype == "esxvm": + details = __salt__["esxvm.get_details"]() else: - raise CommandExecutionError('\'{0}\' proxy is not supported' - ''.format(proxytype)) - return \ - details.get('vcenter') if 'vcenter' in details \ - else details.get('host'), \ - details.get('username'), \ - details.get('password'), details.get('protocol'), \ - details.get('port'), details.get('mechanism'), \ - details.get('principal'), details.get('domain') + raise CommandExecutionError("'{0}' proxy is not supported" "".format(proxytype)) + return ( + details.get("vcenter") if "vcenter" in details else details.get("host"), + details.get("username"), + details.get("password"), + details.get("protocol"), + details.get("port"), + details.get("mechanism"), + details.get("principal"), + details.get("domain"), + ) def supports_proxies(*proxy_types): - ''' + """ Decorator to specify which proxy types are supported by a function proxy_types: Arbitrary list of strings with the supported types of proxies - ''' + """ + def _supports_proxies(fn): @wraps(fn) def __supports_proxies(*args, **kwargs): proxy_type = get_proxy_type() if proxy_type not in proxy_types: raise CommandExecutionError( - '\'{0}\' proxy is not supported by function {1}' - ''.format(proxy_type, fn.__name__)) + "'{0}' proxy is not supported by function {1}" + "".format(proxy_type, fn.__name__) + ) return fn(*args, **salt.utils.args.clean_kwargs(**kwargs)) + return __supports_proxies + return _supports_proxies def gets_service_instance_via_proxy(fn): - ''' + """ Decorator that connects to a target system (vCenter or ESXi host) using the proxy details and passes the connection (vim.ServiceInstance) to the decorated function. @@ -357,28 +400,35 @@ def gets_service_instance_via_proxy(fn): CLI Example: None, this is a decorator - ''' + """ fn_name = fn.__name__ try: - arg_names, args_name, kwargs_name, default_values, _, _, _ = \ - inspect.getfullargspec(fn) + ( + arg_names, + args_name, + kwargs_name, + default_values, + _, + _, + _, + ) = inspect.getfullargspec(fn) except AttributeError: # Fallback to Python 2.7 - arg_names, args_name, kwargs_name, default_values = \ - inspect.getargspec(fn) + arg_names, args_name, kwargs_name, default_values = inspect.getargspec(fn) default_values = default_values if default_values is not None else [] @wraps(fn) def _gets_service_instance_via_proxy(*args, **kwargs): - if 'service_instance' not in arg_names and not kwargs_name: + if "service_instance" not in arg_names and not kwargs_name: raise CommandExecutionError( - 'Function {0} must have either a \'service_instance\', or a ' - '\'**kwargs\' type parameter'.format(fn_name)) + "Function {0} must have either a 'service_instance', or a " + "'**kwargs' type parameter".format(fn_name) + ) connection_details = _get_proxy_connection_details() # Figure out how to pass in the connection value local_service_instance = None - if 'service_instance' in arg_names: - idx = arg_names.index('service_instance') + if "service_instance" in arg_names: + idx = arg_names.index("service_instance") if idx >= len(arg_names) - len(default_values): # 'service_instance' has a default value: # we check if we need to instantiate it or @@ -395,9 +445,9 @@ 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( - *connection_details) + local_service_instance = salt.utils.vmware.get_service_instance( + *connection_details + ) # Tuples are immutable, so if we want to change what # was passed in, we need to first convert to a list. args = list(args) @@ -405,19 +455,19 @@ def gets_service_instance_via_proxy(fn): else: # 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( - *connection_details) - kwargs['service_instance'] = local_service_instance + if not kwargs.get("service_instance"): + local_service_instance = salt.utils.vmware.get_service_instance( + *connection_details + ) + kwargs["service_instance"] = local_service_instance else: # '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( - *connection_details) - kwargs['service_instance'] = local_service_instance + if not kwargs.get("service_instance"): + local_service_instance = salt.utils.vmware.get_service_instance( + *connection_details + ) + kwargs["service_instance"] = local_service_instance try: ret = fn(*args, **salt.utils.args.clean_kwargs(**kwargs)) # Disconnect if connected in the decorator @@ -430,13 +480,14 @@ def gets_service_instance_via_proxy(fn): salt.utils.vmware.disconnect(local_service_instance) # raise original exception and traceback six.reraise(*sys.exc_info()) + return _gets_service_instance_via_proxy @depends(HAS_PYVMOMI) -@supports_proxies('esxi', 'esxcluster', 'esxdatacenter', 'vcenter', 'esxvm') +@supports_proxies("esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm") def get_service_instance_via_proxy(service_instance=None): - ''' + """ Returns a service instance to the proxied endpoint (vCenter/ESXi host). service_instance @@ -448,15 +499,15 @@ def get_service_instance_via_proxy(service_instance=None): CLI Example: See note above - ''' + """ connection_details = _get_proxy_connection_details() return salt.utils.vmware.get_service_instance(*connection_details) @depends(HAS_PYVMOMI) -@supports_proxies('esxi', 'esxcluster', 'esxdatacenter', 'vcenter', 'esxvm') +@supports_proxies("esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm") def disconnect(service_instance): - ''' + """ Disconnects from a vCenter or ESXi host Note: @@ -468,14 +519,23 @@ def disconnect(service_instance): CLI Example: See note above. - ''' + """ salt.utils.vmware.disconnect(service_instance) return True @depends(HAS_ESX_CLI) -def esxcli_cmd(cmd_str, host=None, username=None, password=None, protocol=None, port=None, esxi_hosts=None, credstore=None): - ''' +def esxcli_cmd( + cmd_str, + host=None, + username=None, + password=None, + protocol=None, + port=None, + esxi_hosts=None, + credstore=None, +): + """ Run an ESXCLI command directly on the host or list of hosts. host @@ -519,27 +579,40 @@ def esxcli_cmd(cmd_str, host=None, username=None, password=None, protocol=None, # Used for connecting to a vCenter Server salt '*' vsphere.esxcli_cmd my.vcenter.location root bad-password \ 'system coredump network get' esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' + """ ret = {} if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = salt.utils.vmware.esxcli(host, username, password, cmd_str, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) - if response['retcode'] != 0: - ret.update({esxi_host: {'Error': response.get('stdout')}}) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd_str, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) + if response["retcode"] != 0: + ret.update({esxi_host: {"Error": response.get("stdout")}}) else: ret.update({esxi_host: response}) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = salt.utils.vmware.esxcli(host, username, password, cmd_str, - protocol=protocol, port=port, - credstore=credstore) - if response['retcode'] != 0: - ret.update({host: {'Error': response.get('stdout')}}) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd_str, + protocol=protocol, + port=port, + credstore=credstore, + ) + if response["retcode"] != 0: + ret.update({host: {"Error": response.get("stdout")}}) else: ret.update({host: response}) @@ -547,8 +620,10 @@ def esxcli_cmd(cmd_str, host=None, username=None, password=None, protocol=None, @depends(HAS_ESX_CLI) -def get_coredump_network_config(host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None): - ''' +def get_coredump_network_config( + host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None +): + """ Retrieve information on ESXi or vCenter network dump collection and format it into a dictionary. @@ -591,40 +666,64 @@ def get_coredump_network_config(host, username, password, protocol=None, port=No salt '*' vsphere.get_coredump_network_config my.vcenter.location root bad-password \ esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' - cmd = 'system coredump network get' + """ + cmd = "system coredump network get" ret = {} if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) - if response['retcode'] != 0: - ret.update({esxi_host: {'Error': response.get('stdout')}}) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) + if response["retcode"] != 0: + ret.update({esxi_host: {"Error": response.get("stdout")}}) else: # format the response stdout into something useful - ret.update({esxi_host: {'Coredump Config': _format_coredump_stdout(response)}}) + ret.update( + {esxi_host: {"Coredump Config": _format_coredump_stdout(response)}} + ) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - credstore=credstore) - if response['retcode'] != 0: - ret.update({host: {'Error': response.get('stdout')}}) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + credstore=credstore, + ) + if response["retcode"] != 0: + ret.update({host: {"Error": response.get("stdout")}}) else: # format the response stdout into something useful stdout = _format_coredump_stdout(response) - ret.update({host: {'Coredump Config': stdout}}) + ret.update({host: {"Coredump Config": stdout}}) return ret @depends(HAS_ESX_CLI) -def coredump_network_enable(host, username, password, enabled, protocol=None, port=None, esxi_hosts=None, credstore=None): - ''' +def coredump_network_enable( + host, + username, + password, + enabled, + protocol=None, + port=None, + esxi_hosts=None, + credstore=None, +): + """ Enable or disable ESXi core dump collection. Returns ``True`` if coredump is enabled and returns ``False`` if core dump is not enabled. If there was an error, the error will be the value printed in the ``Error`` key dictionary for the given host. @@ -666,52 +765,67 @@ def coredump_network_enable(host, username, password, enabled, protocol=None, po # Used for connecting to a vCenter Server salt '*' vsphere.coredump_network_enable my.vcenter.location root bad-password True \ esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' + """ if enabled: enable_it = 1 else: enable_it = 0 - cmd = 'system coredump network set -e {0}'.format(enable_it) + cmd = "system coredump network set -e {0}".format(enable_it) ret = {} if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) - if response['retcode'] != 0: - ret.update({esxi_host: {'Error': response.get('stdout')}}) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) + if response["retcode"] != 0: + ret.update({esxi_host: {"Error": response.get("stdout")}}) else: - ret.update({esxi_host: {'Coredump Enabled': enabled}}) + ret.update({esxi_host: {"Coredump Enabled": enabled}}) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - credstore=credstore) - if response['retcode'] != 0: - ret.update({host: {'Error': response.get('stdout')}}) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + credstore=credstore, + ) + if response["retcode"] != 0: + ret.update({host: {"Error": response.get("stdout")}}) else: - ret.update({host: {'Coredump Enabled': enabled}}) + ret.update({host: {"Coredump Enabled": enabled}}) return ret @depends(HAS_ESX_CLI) -def set_coredump_network_config(host, - username, - password, - dump_ip, - protocol=None, - port=None, - host_vnic='vmk0', - dump_port=6500, - esxi_hosts=None, - credstore=None): - ''' +def set_coredump_network_config( + host, + username, + password, + dump_ip, + protocol=None, + port=None, + host_vnic="vmk0", + dump_port=6500, + esxi_hosts=None, + credstore=None, +): + """ Set the network parameters for a network coredump collection. Note that ESXi requires that the dumps first be enabled (see @@ -763,43 +877,58 @@ def set_coredump_network_config(host, # Used for connecting to a vCenter Server salt '*' vsphere.set_coredump_network_config my.vcenter.location root bad-password 'dump_ip.host.com' \ esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' - cmd = 'system coredump network set -v {0} -i {1} -o {2}'.format(host_vnic, - dump_ip, - dump_port) + """ + cmd = "system coredump network set -v {0} -i {1} -o {2}".format( + host_vnic, dump_ip, dump_port + ) ret = {} if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) - if response['retcode'] != 0: - response['success'] = False + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) + if response["retcode"] != 0: + response["success"] = False else: - response['success'] = True + response["success"] = True # Update the cmd.run_all dictionary for each particular host. ret.update({esxi_host: response}) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - credstore=credstore) - if response['retcode'] != 0: - response['success'] = False + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + credstore=credstore, + ) + if response["retcode"] != 0: + response["success"] = False else: - response['success'] = True + response["success"] = True ret.update({host: response}) return ret @depends(HAS_ESX_CLI) -def get_firewall_status(host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None): - ''' +def get_firewall_status( + host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None +): + """ Show status of all firewall rule sets. host @@ -841,34 +970,59 @@ def get_firewall_status(host, username, password, protocol=None, port=None, esxi # Used for connecting to a vCenter Server salt '*' vsphere.get_firewall_status my.vcenter.location root bad-password \ esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' - cmd = 'network firewall ruleset list' + """ + cmd = "network firewall ruleset list" ret = {} if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) - if response['retcode'] != 0: - ret.update({esxi_host: {'Error': response['stdout'], - 'success': False, - 'rulesets': None}}) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) + if response["retcode"] != 0: + ret.update( + { + esxi_host: { + "Error": response["stdout"], + "success": False, + "rulesets": None, + } + } + ) else: # format the response stdout into something useful ret.update({esxi_host: _format_firewall_stdout(response)}) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - credstore=credstore) - if response['retcode'] != 0: - ret.update({host: {'Error': response['stdout'], - 'success': False, - 'rulesets': None}}) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + credstore=credstore, + ) + if response["retcode"] != 0: + ret.update( + { + host: { + "Error": response["stdout"], + "success": False, + "rulesets": None, + } + } + ) else: # format the response stdout into something useful ret.update({host: _format_firewall_stdout(response)}) @@ -877,16 +1031,18 @@ def get_firewall_status(host, username, password, protocol=None, port=None, esxi @depends(HAS_ESX_CLI) -def enable_firewall_ruleset(host, - username, - password, - ruleset_enable, - ruleset_name, - protocol=None, - port=None, - esxi_hosts=None, - credstore=None): - ''' +def enable_firewall_ruleset( + host, + username, + password, + ruleset_enable, + ruleset_name, + protocol=None, + port=None, + esxi_hosts=None, + credstore=None, +): + """ Enable or disable an ESXi firewall rule set. host @@ -931,34 +1087,49 @@ def enable_firewall_ruleset(host, # Used for connecting to a vCenter Server salt '*' vsphere.enable_firewall_ruleset my.vcenter.location root bad-password True 'syslog' \ esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' - cmd = 'network firewall ruleset set --enabled {0} --ruleset-id={1}'.format( + """ + cmd = "network firewall ruleset set --enabled {0} --ruleset-id={1}".format( ruleset_enable, ruleset_name ) ret = {} if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) ret.update({esxi_host: response}) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - credstore=credstore) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + credstore=credstore, + ) ret.update({host: response}) return ret @depends(HAS_ESX_CLI) -def syslog_service_reload(host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None): - ''' +def syslog_service_reload( + host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None +): + """ Reload the syslog service so it will pick up any changes. host @@ -998,42 +1169,57 @@ def syslog_service_reload(host, username, password, protocol=None, port=None, es # Used for connecting to a vCenter Server salt '*' vsphere.syslog_service_reload my.vcenter.location root bad-password \ esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' - cmd = 'system syslog reload' + """ + cmd = "system syslog reload" ret = {} if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) ret.update({esxi_host: response}) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - credstore=credstore) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + credstore=credstore, + ) ret.update({host: response}) return ret @depends(HAS_ESX_CLI) -def set_syslog_config(host, - username, - password, - syslog_config, - config_value, - protocol=None, - port=None, - firewall=True, - reset_service=True, - esxi_hosts=None, - credstore=None): - ''' +def set_syslog_config( + host, + username, + password, + syslog_config, + config_value, + protocol=None, + port=None, + firewall=True, + reset_service=True, + esxi_hosts=None, + credstore=None, +): + """ Set the specified syslog configuration parameter. By default, this function will reset the syslog service after the configuration is set. @@ -1098,47 +1284,84 @@ def set_syslog_config(host, loghost ssl://localhost:5432,tcp://10.1.0.1:1514 \ esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' + """ ret = {} # First, enable the syslog firewall ruleset, for each host, if needed. - if firewall and syslog_config == 'loghost': + if firewall and syslog_config == "loghost": if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = enable_firewall_ruleset(host, username, password, - ruleset_enable=True, ruleset_name='syslog', - protocol=protocol, port=port, - esxi_hosts=[esxi_host], credstore=credstore).get(esxi_host) - if response['retcode'] != 0: - ret.update({esxi_host: {'enable_firewall': {'message': response['stdout'], - 'success': False}}}) + response = enable_firewall_ruleset( + host, + username, + password, + ruleset_enable=True, + ruleset_name="syslog", + protocol=protocol, + port=port, + esxi_hosts=[esxi_host], + credstore=credstore, + ).get(esxi_host) + if response["retcode"] != 0: + ret.update( + { + esxi_host: { + "enable_firewall": { + "message": response["stdout"], + "success": False, + } + } + } + ) else: - ret.update({esxi_host: {'enable_firewall': {'success': True}}}) + ret.update({esxi_host: {"enable_firewall": {"success": True}}}) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = enable_firewall_ruleset(host, username, password, - ruleset_enable=True, ruleset_name='syslog', - protocol=protocol, port=port, - credstore=credstore).get(host) - if response['retcode'] != 0: - ret.update({host: {'enable_firewall': {'message': response['stdout'], - 'success': False}}}) + response = enable_firewall_ruleset( + host, + username, + password, + ruleset_enable=True, + ruleset_name="syslog", + protocol=protocol, + port=port, + credstore=credstore, + ).get(host) + if response["retcode"] != 0: + ret.update( + { + host: { + "enable_firewall": { + "message": response["stdout"], + "success": False, + } + } + } + ) else: - ret.update({host: {'enable_firewall': {'success': True}}}) + ret.update({host: {"enable_firewall": {"success": True}}}) # Set the config value on each esxi_host, if provided. if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = _set_syslog_config_helper(host, username, password, syslog_config, - config_value, protocol=protocol, port=port, - reset_service=reset_service, esxi_host=esxi_host, - credstore=credstore) + response = _set_syslog_config_helper( + host, + username, + password, + syslog_config, + config_value, + protocol=protocol, + port=port, + reset_service=reset_service, + esxi_host=esxi_host, + credstore=credstore, + ) # Ensure we don't overwrite any dictionary data already set # By updating the esxi_host directly. if ret.get(esxi_host) is None: @@ -1147,9 +1370,17 @@ def set_syslog_config(host, else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = _set_syslog_config_helper(host, username, password, syslog_config, - config_value, protocol=protocol, port=port, - reset_service=reset_service, credstore=credstore) + response = _set_syslog_config_helper( + host, + username, + password, + syslog_config, + config_value, + protocol=protocol, + port=port, + reset_service=reset_service, + credstore=credstore, + ) # Ensure we don't overwrite any dictionary data already set # By updating the host directly. if ret.get(host) is None: @@ -1160,8 +1391,10 @@ def set_syslog_config(host, @depends(HAS_ESX_CLI) -def get_syslog_config(host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None): - ''' +def get_syslog_config( + host, username, password, protocol=None, port=None, esxi_hosts=None, credstore=None +): + """ Retrieve the syslog configuration. host @@ -1201,25 +1434,38 @@ def get_syslog_config(host, username, password, protocol=None, port=None, esxi_h # Used for connecting to a vCenter Server salt '*' vsphere.get_syslog_config my.vcenter.location root bad-password \ esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' - cmd = 'system syslog config get' + """ + cmd = "system syslog config get" ret = {} if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) # format the response stdout into something useful ret.update({esxi_host: _format_syslog_config(response)}) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - credstore=credstore) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + credstore=credstore, + ) # format the response stdout into something useful ret.update({host: _format_syslog_config(response)}) @@ -1227,15 +1473,17 @@ def get_syslog_config(host, username, password, protocol=None, port=None, esxi_h @depends(HAS_ESX_CLI) -def reset_syslog_config(host, - username, - password, - protocol=None, - port=None, - syslog_config=None, - esxi_hosts=None, - credstore=None): - ''' +def reset_syslog_config( + host, + username, + password, + protocol=None, + port=None, + syslog_config=None, + esxi_hosts=None, + credstore=None, +): + """ Reset the syslog service to its default settings. Valid syslog_config values are ``logdir``, ``loghost``, ``logdir-unique``, @@ -1287,17 +1535,24 @@ def reset_syslog_config(host, # Used for connecting to a vCenter Server salt '*' vsphere.reset_syslog_config my.vcenter.location root bad-password \ syslog_config='logdir,loghost' esxi_hosts='[esxi-1.host.com, esxi-2.host.com]' - ''' + """ if not syslog_config: - raise CommandExecutionError('The \'reset_syslog_config\' function requires a ' - '\'syslog_config\' setting.') + raise CommandExecutionError( + "The 'reset_syslog_config' function requires a " "'syslog_config' setting." + ) - valid_resets = ['logdir', 'loghost', 'default-rotate', - 'default-size', 'default-timeout', 'logdir-unique'] - cmd = 'system syslog config set --reset=' - if ',' in syslog_config: - resets = [ind_reset.strip() for ind_reset in syslog_config.split(',')] - elif syslog_config == 'all': + valid_resets = [ + "logdir", + "loghost", + "default-rotate", + "default-size", + "default-timeout", + "logdir-unique", + ] + cmd = "system syslog config set --reset=" + if "," in syslog_config: + resets = [ind_reset.strip() for ind_reset in syslog_config.split(",")] + elif syslog_config == "all": resets = valid_resets else: resets = [syslog_config] @@ -1305,29 +1560,52 @@ def reset_syslog_config(host, ret = {} if esxi_hosts: if not isinstance(esxi_hosts, list): - raise CommandExecutionError('\'esxi_hosts\' must be a list.') + raise CommandExecutionError("'esxi_hosts' must be a list.") for esxi_host in esxi_hosts: - response_dict = _reset_syslog_config_params(host, username, password, - cmd, resets, valid_resets, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) + response_dict = _reset_syslog_config_params( + host, + username, + password, + cmd, + resets, + valid_resets, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) ret.update({esxi_host: response_dict}) else: # Handles a single host or a vCenter connection when no esxi_hosts are provided. - response_dict = _reset_syslog_config_params(host, username, password, - cmd, resets, valid_resets, - protocol=protocol, port=port, - credstore=credstore) + response_dict = _reset_syslog_config_params( + host, + username, + password, + cmd, + resets, + valid_resets, + protocol=protocol, + port=port, + credstore=credstore, + ) ret.update({host: response_dict}) return ret -@ignores_kwargs('credstore') -def upload_ssh_key(host, username, password, ssh_key=None, ssh_key_file=None, - protocol=None, port=None, certificate_verify=False): - ''' +@ignores_kwargs("credstore") +def upload_ssh_key( + host, + username, + password, + ssh_key=None, + ssh_key_file=None, + protocol=None, + port=None, + certificate_verify=False, +): + """ Upload an ssh key for root to an ESXi host via http PUT. This function only works for ESXi, not vCenter. Only one ssh key can be uploaded for root. Uploading a second key will @@ -1353,57 +1631,56 @@ def upload_ssh_key(host, username, password, ssh_key=None, ssh_key_file=None, salt '*' vsphere.upload_ssh_key my.esxi.host root bad-password ssh_key_file='/etc/salt/my_keys/my_key.pub' - ''' + """ if protocol is None: - protocol = 'https' + protocol = "https" if port is None: port = 443 - url = '{0}://{1}:{2}/host/ssh_root_authorized_keys'.format(protocol, - host, - port) + url = "{0}://{1}:{2}/host/ssh_root_authorized_keys".format(protocol, host, port) ret = {} result = None try: if ssh_key: - result = salt.utils.http.query(url, - status=True, - text=True, - method='PUT', - username=username, - password=password, - data=ssh_key, - verify_ssl=certificate_verify) + result = salt.utils.http.query( + url, + status=True, + text=True, + method="PUT", + username=username, + password=password, + data=ssh_key, + verify_ssl=certificate_verify, + ) elif ssh_key_file: - result = salt.utils.http.query(url, - status=True, - text=True, - method='PUT', - username=username, - password=password, - data_file=ssh_key_file, - data_render=False, - verify_ssl=certificate_verify) - if result.get('status') == 200: - ret['status'] = True + result = salt.utils.http.query( + url, + status=True, + text=True, + method="PUT", + username=username, + password=password, + data_file=ssh_key_file, + data_render=False, + verify_ssl=certificate_verify, + ) + if result.get("status") == 200: + ret["status"] = True else: - ret['status'] = False - ret['Error'] = result['error'] + ret["status"] = False + ret["Error"] = result["error"] except Exception as msg: # pylint: disable=broad-except - ret['status'] = False - ret['Error'] = msg + ret["status"] = False + ret["Error"] = msg return ret -@ignores_kwargs('credstore') -def get_ssh_key(host, - username, - password, - protocol=None, - port=None, - certificate_verify=False): - ''' +@ignores_kwargs("credstore") +def get_ssh_key( + host, username, password, protocol=None, port=None, certificate_verify=False +): + """ Retrieve the authorized_keys entry for root. This function only works for ESXi, not vCenter. @@ -1422,41 +1699,43 @@ def get_ssh_key(host, salt '*' vsphere.get_ssh_key my.esxi.host root bad-password certificate_verify=True - ''' + """ if protocol is None: - protocol = 'https' + protocol = "https" if port is None: port = 443 - url = '{0}://{1}:{2}/host/ssh_root_authorized_keys'.format(protocol, - host, - port) + url = "{0}://{1}:{2}/host/ssh_root_authorized_keys".format(protocol, host, port) ret = {} try: - result = salt.utils.http.query(url, - status=True, - text=True, - method='GET', - username=username, - password=password, - verify_ssl=certificate_verify) - if result.get('status') == 200: - ret['status'] = True - ret['key'] = result['text'] + result = salt.utils.http.query( + url, + status=True, + text=True, + method="GET", + username=username, + password=password, + verify_ssl=certificate_verify, + ) + if result.get("status") == 200: + ret["status"] = True + ret["key"] = result["text"] else: - ret['status'] = False - ret['Error'] = result['error'] + ret["status"] = False + ret["Error"] = result["error"] except Exception as msg: # pylint: disable=broad-except - ret['status'] = False - ret['Error'] = msg + ret["status"] = False + ret["Error"] = msg return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def get_host_datetime(host, username, password, protocol=None, port=None, host_names=None): - ''' +@ignores_kwargs("credstore") +def get_host_datetime( + host, username, password, protocol=None, port=None, host_names=None +): + """ Get the date/time information for a given host or list of host_names. host @@ -1495,12 +1774,10 @@ def get_host_datetime(host, username, password, protocol=None, port=None, host_n # Used for connecting to a vCenter Server salt '*' vsphere.get_host_datetime my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) ret = {} for host_name in host_names: @@ -1513,9 +1790,9 @@ def get_host_datetime(host, username, password, protocol=None, port=None, host_n @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def get_ntp_config(host, username, password, protocol=None, port=None, host_names=None): - ''' + """ Get the NTP configuration information for a given host or list of host_names. host @@ -1554,12 +1831,10 @@ def get_ntp_config(host, username, password, protocol=None, port=None, host_name # Used for connecting to a vCenter Server salt '*' vsphere.get_ntp_config my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) ret = {} for host_name in host_names: @@ -1571,9 +1846,11 @@ 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): - ''' +@ignores_kwargs("credstore") +def get_service_policy( + host, username, password, service_name, protocol=None, port=None, host_names=None +): + """ Get the service name's policy for a given host or list of hosts. host @@ -1628,14 +1905,26 @@ def get_service_policy(host, username, password, service_name, protocol=None, po # Used for connecting to a vCenter Server salt '*' vsphere.get_service_policy my.vcenter.location root bad-password 'ntpd' \ 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) - valid_services = ['DCUI', 'TSM', 'SSH', 'ssh', 'lbtd', 'lsassd', 'lwiod', 'netlogond', - 'ntpd', 'sfcbd-watchdog', 'snmpd', 'vprobed', 'vpxa', 'xorg'] + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) + valid_services = [ + "DCUI", + "TSM", + "SSH", + "ssh", + "lbtd", + "lsassd", + "lwiod", + "netlogond", + "ntpd", + "sfcbd-watchdog", + "snmpd", + "vprobed", + "vpxa", + "xorg", + ] host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -1643,44 +1932,52 @@ def get_service_policy(host, username, password, service_name, protocol=None, po # Check if the service_name provided is a valid one. # If we don't have a valid service, return. The service will be invalid for all hosts. if service_name not in valid_services: - ret.update({host_name: {'Error': '{0} is not a valid service name.'.format(service_name)}}) + ret.update( + { + host_name: { + "Error": "{0} is not a valid service name.".format(service_name) + } + } + ) return ret host_ref = _get_host_ref(service_instance, host, host_name=host_name) services = host_ref.configManager.serviceSystem.serviceInfo.service # Don't require users to know that VMware lists the ssh service as TSM-SSH - if service_name == 'SSH' or service_name == 'ssh': - temp_service_name = 'TSM-SSH' + if service_name == "SSH" or service_name == "ssh": + temp_service_name = "TSM-SSH" else: temp_service_name = service_name # Loop through services until we find a matching name for service in services: if service.key == temp_service_name: - ret.update({host_name: - {service_name: service.policy}}) + ret.update({host_name: {service_name: service.policy}}) # We've found a match - break out of the loop so we don't overwrite the # Updated host_name value with an error message. break else: - msg = 'Could not find service \'{0}\' for host \'{1}\'.'.format(service_name, - host_name) - ret.update({host_name: {'Error': msg}}) + msg = "Could not find service '{0}' for host '{1}'.".format( + service_name, host_name + ) + ret.update({host_name: {"Error": msg}}) # If we made it this far, something else has gone wrong. if ret.get(host_name) is None: - msg = '\'vsphere.get_service_policy\' failed for host {0}.'.format(host_name) + msg = "'vsphere.get_service_policy' failed for host {0}.".format(host_name) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def get_service_running(host, username, password, service_name, protocol=None, port=None, host_names=None): - ''' +@ignores_kwargs("credstore") +def get_service_running( + host, username, password, service_name, protocol=None, port=None, host_names=None +): + """ Get the service name's running state for a given host or list of hosts. host @@ -1735,14 +2032,26 @@ def get_service_running(host, username, password, service_name, protocol=None, p # Used for connecting to a vCenter Server salt '*' vsphere.get_service_running my.vcenter.location root bad-password 'ntpd' \ 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) - valid_services = ['DCUI', 'TSM', 'SSH', 'ssh', 'lbtd', 'lsassd', 'lwiod', 'netlogond', - 'ntpd', 'sfcbd-watchdog', 'snmpd', 'vprobed', 'vpxa', 'xorg'] + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) + valid_services = [ + "DCUI", + "TSM", + "SSH", + "ssh", + "lbtd", + "lsassd", + "lwiod", + "netlogond", + "ntpd", + "sfcbd-watchdog", + "snmpd", + "vprobed", + "vpxa", + "xorg", + ] host_names = _check_hosts(service_instance, host, host_names) ret = {} @@ -1750,44 +2059,52 @@ def get_service_running(host, username, password, service_name, protocol=None, p # Check if the service_name provided is a valid one. # If we don't have a valid service, return. The service will be invalid for all hosts. if service_name not in valid_services: - ret.update({host_name: {'Error': '{0} is not a valid service name.'.format(service_name)}}) + ret.update( + { + host_name: { + "Error": "{0} is not a valid service name.".format(service_name) + } + } + ) return ret host_ref = _get_host_ref(service_instance, host, host_name=host_name) services = host_ref.configManager.serviceSystem.serviceInfo.service # Don't require users to know that VMware lists the ssh service as TSM-SSH - if service_name == 'SSH' or service_name == 'ssh': - temp_service_name = 'TSM-SSH' + if service_name == "SSH" or service_name == "ssh": + temp_service_name = "TSM-SSH" else: temp_service_name = service_name # Loop through services until we find a matching name for service in services: if service.key == temp_service_name: - ret.update({host_name: - {service_name: service.running}}) + ret.update({host_name: {service_name: service.running}}) # We've found a match - break out of the loop so we don't overwrite the # Updated host_name value with an error message. break else: - msg = 'Could not find service \'{0}\' for host \'{1}\'.'.format(service_name, - host_name) - ret.update({host_name: {'Error': msg}}) + msg = "Could not find service '{0}' for host '{1}'.".format( + service_name, host_name + ) + ret.update({host_name: {"Error": msg}}) # If we made it this far, something else has gone wrong. if ret.get(host_name) is None: - msg = '\'vsphere.get_service_running\' failed for host {0}.'.format(host_name) + msg = "'vsphere.get_service_running' failed for host {0}.".format(host_name) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def get_vmotion_enabled(host, username, password, protocol=None, port=None, host_names=None): - ''' +@ignores_kwargs("credstore") +def get_vmotion_enabled( + host, username, password, protocol=None, port=None, host_names=None +): + """ Get the VMotion enabled status for a given host or a list of host_names. Returns ``True`` if VMotion is enabled, ``False`` if it is not enabled. @@ -1827,29 +2144,29 @@ def get_vmotion_enabled(host, username, password, protocol=None, port=None, host # Used for connecting to a vCenter Server salt '*' vsphere.get_vmotion_enabled my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) ret = {} for host_name in host_names: host_ref = _get_host_ref(service_instance, host, host_name=host_name) vmotion_vnic = host_ref.configManager.vmotionSystem.netConfig.selectedVnic if vmotion_vnic: - ret.update({host_name: {'VMotion Enabled': True}}) + ret.update({host_name: {"VMotion Enabled": True}}) else: - ret.update({host_name: {'VMotion Enabled': False}}) + ret.update({host_name: {"VMotion Enabled": False}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def get_vsan_enabled(host, username, password, protocol=None, port=None, host_names=None): - ''' +@ignores_kwargs("credstore") +def get_vsan_enabled( + host, username, password, protocol=None, port=None, host_names=None +): + """ Get the VSAN enabled status for a given host or a list of host_names. Returns ``True`` if VSAN is enabled, ``False`` if it is not enabled, and ``None`` if a VSAN Host Config is unset, per host. @@ -1890,12 +2207,10 @@ def get_vsan_enabled(host, username, password, protocol=None, port=None, host_na # Used for connecting to a vCenter Server salt '*' vsphere.get_vsan_enabled my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) ret = {} for host_name in host_names: @@ -1904,19 +2219,23 @@ def get_vsan_enabled(host, username, password, protocol=None, port=None, host_na # We must have a VSAN Config in place get information about VSAN state. if vsan_config is None: - msg = 'VSAN System Config Manager is unset for host \'{0}\'.'.format(host_name) + msg = "VSAN System Config Manager is unset for host '{0}'.".format( + host_name + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) else: - ret.update({host_name: {'VSAN Enabled': vsan_config.enabled}}) + ret.update({host_name: {"VSAN Enabled": vsan_config.enabled}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def get_vsan_eligible_disks(host, username, password, protocol=None, port=None, host_names=None): - ''' +@ignores_kwargs("credstore") +def get_vsan_eligible_disks( + host, username, password, protocol=None, port=None, host_names=None +): + """ Returns a list of VSAN-eligible disks for a given host or list of host_names. host @@ -1955,23 +2274,21 @@ def get_vsan_eligible_disks(host, username, password, protocol=None, port=None, # Used for connecting to a vCenter Server salt '*' vsphere.get_vsan_eligible_disks my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) response = _get_vsan_eligible_disks(service_instance, host, host_names) ret = {} for host_name, value in six.iteritems(response): - error = value.get('Error') + error = value.get("Error") if error: - ret.update({host_name: {'Error': error}}) + ret.update({host_name: {"Error": error}}) continue - disks = value.get('Eligible') + disks = value.get("Eligible") # If we have eligible disks, it will be a list of disk objects if disks and isinstance(disks, list): disk_names = [] @@ -1979,20 +2296,20 @@ def get_vsan_eligible_disks(host, username, password, protocol=None, port=None, # MessagePack can't deserialize the disk objects. for disk in disks: disk_names.append(disk.canonicalName) - ret.update({host_name: {'Eligible': disk_names}}) + ret.update({host_name: {"Eligible": disk_names}}) else: # If we have disks, but it's not a list, it's actually a # string message that we're passing along. - ret.update({host_name: {'Eligible': disks}}) + ret.update({host_name: {"Eligible": disks}}) return ret @depends(HAS_PYVMOMI) -@supports_proxies('esxi', 'esxcluster', 'esxdatacenter', 'vcenter', 'esxvm') +@supports_proxies("esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm") @gets_service_instance_via_proxy def test_vcenter_connection(service_instance=None): - ''' + """ Checks if a connection is to a vCenter CLI Example: @@ -2000,7 +2317,7 @@ def test_vcenter_connection(service_instance=None): .. code-block:: bash salt '*' vsphere.test_vcenter_connection - ''' + """ try: if salt.utils.vmware.is_connection_to_a_vcenter(service_instance): return True @@ -2010,9 +2327,9 @@ def test_vcenter_connection(service_instance=None): @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def system_info(host, username, password, protocol=None, port=None): - ''' + """ Return system information about a VMware environment. host @@ -2037,23 +2354,23 @@ def system_info(host, username, password, protocol=None, port=None): .. code-block:: bash 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) ret = salt.utils.vmware.get_inventory(service_instance).about.__dict__ - if 'apiType' in ret: - if ret['apiType'] == 'HostAgent': - ret = dictupdate.update(ret, salt.utils.vmware.get_hardware_grains(service_instance)) + if "apiType" in ret: + if ret["apiType"] == "HostAgent": + ret = dictupdate.update( + ret, salt.utils.vmware.get_hardware_grains(service_instance) + ) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_datacenters(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of datacenters for the the specified host. host @@ -2079,19 +2396,17 @@ def list_datacenters(host, username, password, protocol=None, port=None): salt '*' vsphere.list_datacenters 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_datacenters(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_clusters(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of clusters for the the specified host. host @@ -2117,19 +2432,17 @@ def list_clusters(host, username, password, protocol=None, port=None): salt '*' vsphere.list_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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_clusters(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_datastore_clusters(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of datastore clusters for the the specified host. host @@ -2154,19 +2467,17 @@ def list_datastore_clusters(host, username, password, protocol=None, port=None): .. code-block:: bash 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_datastore_clusters(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_datastores(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of datastores for the the specified host. host @@ -2191,19 +2502,17 @@ def list_datastores(host, username, password, protocol=None, port=None): .. code-block:: bash 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_datastores(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_hosts(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of hosts for the the specified VMware environment. host @@ -2228,19 +2537,17 @@ def list_hosts(host, username, password, protocol=None, port=None): .. code-block:: bash 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_hosts(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_resourcepools(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of resource pools for the the specified host. host @@ -2265,19 +2572,17 @@ def list_resourcepools(host, username, password, protocol=None, port=None): .. code-block:: bash 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_resourcepools(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_networks(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of networks for the the specified host. host @@ -2302,19 +2607,17 @@ def list_networks(host, username, password, protocol=None, port=None): .. code-block:: bash 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_networks(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_vms(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of VMs for the the specified host. host @@ -2339,19 +2642,17 @@ def list_vms(host, username, password, protocol=None, port=None): .. code-block:: bash 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_vms(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_folders(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of folders for the the specified host. host @@ -2376,19 +2677,17 @@ def list_folders(host, username, password, protocol=None, port=None): .. code-block:: bash 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_folders(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_dvs(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of distributed virtual switches for the the specified host. host @@ -2413,19 +2712,17 @@ def list_dvs(host, username, password, protocol=None, port=None): .. code-block:: bash 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_dvs(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_vapps(host, username, password, protocol=None, port=None): - ''' + """ Returns a list of vApps for the the specified host. host @@ -2451,19 +2748,17 @@ def list_vapps(host, username, password, protocol=None, port=None): # List vapps from all minions 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) return salt.utils.vmware.list_vapps(service_instance) @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_ssds(host, username, password, protocol=None, port=None, host_names=None): - ''' + """ Returns a list of SSDs for the given host or list of host_names. host @@ -2502,12 +2797,10 @@ def list_ssds(host, username, password, protocol=None, port=None, host_names=Non # Used for connecting to a vCenter Server salt '*' vsphere.list_ssds my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) ret = {} names = [] @@ -2522,9 +2815,9 @@ def list_ssds(host, username, password, protocol=None, port=None, host_names=Non @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def list_non_ssds(host, username, password, protocol=None, port=None, host_names=None): - ''' + """ Returns a list of Non-SSD disks for the given host or list of host_names. .. note:: @@ -2570,12 +2863,10 @@ def list_non_ssds(host, username, password, protocol=None, port=None, host_names # Used for connecting to a vCenter Server salt '*' vsphere.list_non_ssds my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) ret = {} names = [] @@ -2590,9 +2881,11 @@ 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): - ''' +@ignores_kwargs("credstore") +def set_ntp_config( + host, username, password, ntp_servers, protocol=None, port=None, host_names=None +): + """ Set NTP configuration for a given host of list of host_names. host @@ -2635,14 +2928,12 @@ def set_ntp_config(host, username, password, ntp_servers, protocol=None, port=No # Used for connecting to a vCenter Server salt '*' vsphere.ntp_configure my.vcenter.location root bad-password '[192.174.1.100, 192.174.1.200]' \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) if not isinstance(ntp_servers, list): - raise CommandExecutionError('\'ntp_servers\' must be a list.') + raise CommandExecutionError("'ntp_servers' must be a list.") # Get NTP Config Object from ntp_servers ntp_config = vim.HostNtpConfig(server=ntp_servers) @@ -2655,30 +2946,30 @@ def set_ntp_config(host, username, password, ntp_servers, protocol=None, port=No for host_name in host_names: host_ref = _get_host_ref(service_instance, host, host_name=host_name) date_time_manager = _get_date_time_mgr(host_ref) - log.debug('Configuring NTP Servers \'{0}\' for host \'{1}\'.'.format(ntp_servers, host_name)) + log.debug( + "Configuring NTP Servers '{0}' for host '{1}'.".format( + ntp_servers, host_name + ) + ) try: date_time_manager.UpdateDateTimeConfig(config=date_config) except vim.fault.HostConfigFault as err: - msg = 'vsphere.ntp_configure_servers failed: {0}'.format(err) + msg = "vsphere.ntp_configure_servers failed: {0}".format(err) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue - ret.update({host_name: {'NTP Servers': ntp_config}}) + ret.update({host_name: {"NTP Servers": ntp_config}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def service_start(host, - username, - password, - service_name, - protocol=None, - port=None, - host_names=None): - ''' +@ignores_kwargs("credstore") +def service_start( + host, username, password, service_name, protocol=None, port=None, host_names=None +): + """ Start the named service for the given host or list of hosts. host @@ -2733,20 +3024,32 @@ def service_start(host, # Used for connecting to a vCenter Server salt '*' vsphere.service_start my.vcenter.location root bad-password 'ntpd' \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) - valid_services = ['DCUI', 'TSM', 'SSH', 'ssh', 'lbtd', 'lsassd', 'lwiod', 'netlogond', - 'ntpd', 'sfcbd-watchdog', 'snmpd', 'vprobed', 'vpxa', 'xorg'] + valid_services = [ + "DCUI", + "TSM", + "SSH", + "ssh", + "lbtd", + "lsassd", + "lwiod", + "netlogond", + "ntpd", + "sfcbd-watchdog", + "snmpd", + "vprobed", + "vpxa", + "xorg", + ] ret = {} # Don't require users to know that VMware lists the ssh service as TSM-SSH - if service_name == 'SSH' or service_name == 'ssh': - temp_service_name = 'TSM-SSH' + if service_name == "SSH" or service_name == "ssh": + temp_service_name = "TSM-SSH" else: temp_service_name = service_name @@ -2754,42 +3057,46 @@ def service_start(host, # Check if the service_name provided is a valid one. # If we don't have a valid service, return. The service will be invalid for all hosts. if service_name not in valid_services: - ret.update({host_name: {'Error': '{0} is not a valid service name.'.format(service_name)}}) + ret.update( + { + host_name: { + "Error": "{0} is not a valid service name.".format(service_name) + } + } + ) return ret host_ref = _get_host_ref(service_instance, host, host_name=host_name) service_manager = _get_service_manager(host_ref) - log.debug('Starting the \'{0}\' service on {1}.'.format(service_name, host_name)) + log.debug("Starting the '{0}' service on {1}.".format(service_name, host_name)) # Start the service try: service_manager.StartService(id=temp_service_name) except vim.fault.HostConfigFault as err: - msg = '\'vsphere.service_start\' failed for host {0}: {1}'.format(host_name, err) + msg = "'vsphere.service_start' failed for host {0}: {1}".format( + host_name, err + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue # Some services are restricted by the vSphere License Level. except vim.fault.RestrictedVersion as err: log.debug(err) - ret.update({host_name: {'Error': err}}) + ret.update({host_name: {"Error": err}}) continue - ret.update({host_name: {'Service Started': True}}) + ret.update({host_name: {"Service Started": True}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def service_stop(host, - username, - password, - service_name, - protocol=None, - port=None, - host_names=None): - ''' +@ignores_kwargs("credstore") +def service_stop( + host, username, password, service_name, protocol=None, port=None, host_names=None +): + """ Stop the named service for the given host or list of hosts. host @@ -2844,20 +3151,32 @@ def service_stop(host, # Used for connecting to a vCenter Server salt '*' vsphere.service_stop my.vcenter.location root bad-password 'ssh' \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) - valid_services = ['DCUI', 'TSM', 'SSH', 'ssh', 'lbtd', 'lsassd', 'lwiod', 'netlogond', - 'ntpd', 'sfcbd-watchdog', 'snmpd', 'vprobed', 'vpxa', 'xorg'] + valid_services = [ + "DCUI", + "TSM", + "SSH", + "ssh", + "lbtd", + "lsassd", + "lwiod", + "netlogond", + "ntpd", + "sfcbd-watchdog", + "snmpd", + "vprobed", + "vpxa", + "xorg", + ] ret = {} # Don't require users to know that VMware lists the ssh service as TSM-SSH - if service_name == 'SSH' or service_name == 'ssh': - temp_service_name = 'TSM-SSH' + if service_name == "SSH" or service_name == "ssh": + temp_service_name = "TSM-SSH" else: temp_service_name = service_name @@ -2865,42 +3184,46 @@ def service_stop(host, # Check if the service_name provided is a valid one. # If we don't have a valid service, return. The service will be invalid for all hosts. if service_name not in valid_services: - ret.update({host_name: {'Error': '{0} is not a valid service name.'.format(service_name)}}) + ret.update( + { + host_name: { + "Error": "{0} is not a valid service name.".format(service_name) + } + } + ) return ret host_ref = _get_host_ref(service_instance, host, host_name=host_name) service_manager = _get_service_manager(host_ref) - log.debug('Stopping the \'{0}\' service on {1}.'.format(service_name, host_name)) + log.debug("Stopping the '{0}' service on {1}.".format(service_name, host_name)) # Stop the service. try: service_manager.StopService(id=temp_service_name) except vim.fault.HostConfigFault as err: - msg = '\'vsphere.service_stop\' failed for host {0}: {1}'.format(host_name, err) + msg = "'vsphere.service_stop' failed for host {0}: {1}".format( + host_name, err + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue # Some services are restricted by the vSphere License Level. except vim.fault.RestrictedVersion as err: log.debug(err) - ret.update({host_name: {'Error': err}}) + ret.update({host_name: {"Error": err}}) continue - ret.update({host_name: {'Service Stopped': True}}) + ret.update({host_name: {"Service Stopped": True}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def service_restart(host, - username, - password, - service_name, - protocol=None, - port=None, - host_names=None): - ''' +@ignores_kwargs("credstore") +def service_restart( + host, username, password, service_name, protocol=None, port=None, host_names=None +): + """ Restart the named service for the given host or list of hosts. host @@ -2955,20 +3278,32 @@ def service_restart(host, # Used for connecting to a vCenter Server salt '*' vsphere.service_restart my.vcenter.location root bad-password 'ntpd' \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) - valid_services = ['DCUI', 'TSM', 'SSH', 'ssh', 'lbtd', 'lsassd', 'lwiod', 'netlogond', - 'ntpd', 'sfcbd-watchdog', 'snmpd', 'vprobed', 'vpxa', 'xorg'] + valid_services = [ + "DCUI", + "TSM", + "SSH", + "ssh", + "lbtd", + "lsassd", + "lwiod", + "netlogond", + "ntpd", + "sfcbd-watchdog", + "snmpd", + "vprobed", + "vpxa", + "xorg", + ] ret = {} # Don't require users to know that VMware lists the ssh service as TSM-SSH - if service_name == 'SSH' or service_name == 'ssh': - temp_service_name = 'TSM-SSH' + if service_name == "SSH" or service_name == "ssh": + temp_service_name = "TSM-SSH" else: temp_service_name = service_name @@ -2976,43 +3311,55 @@ def service_restart(host, # Check if the service_name provided is a valid one. # If we don't have a valid service, return. The service will be invalid for all hosts. if service_name not in valid_services: - ret.update({host_name: {'Error': '{0} is not a valid service name.'.format(service_name)}}) + ret.update( + { + host_name: { + "Error": "{0} is not a valid service name.".format(service_name) + } + } + ) return ret host_ref = _get_host_ref(service_instance, host, host_name=host_name) service_manager = _get_service_manager(host_ref) - log.debug('Restarting the \'{0}\' service on {1}.'.format(service_name, host_name)) + log.debug( + "Restarting the '{0}' service on {1}.".format(service_name, host_name) + ) # Restart the service. try: service_manager.RestartService(id=temp_service_name) except vim.fault.HostConfigFault as err: - msg = '\'vsphere.service_restart\' failed for host {0}: {1}'.format(host_name, err) + msg = "'vsphere.service_restart' failed for host {0}: {1}".format( + host_name, err + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue # Some services are restricted by the vSphere License Level. except vim.fault.RestrictedVersion as err: log.debug(err) - ret.update({host_name: {'Error': err}}) + ret.update({host_name: {"Error": err}}) continue - ret.update({host_name: {'Service Restarted': True}}) + ret.update({host_name: {"Service Restarted": True}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def set_service_policy(host, - username, - password, - service_name, - service_policy, - protocol=None, - port=None, - host_names=None): - ''' +@ignores_kwargs("credstore") +def set_service_policy( + host, + username, + password, + service_name, + service_policy, + protocol=None, + port=None, + host_names=None, +): + """ Set the service name's policy for a given host or list of hosts. host @@ -3070,22 +3417,40 @@ def set_service_policy(host, # Used for connecting to a vCenter Server salt '*' vsphere.set_service_policy my.vcenter.location root bad-password 'ntpd' 'automatic' \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) - valid_services = ['DCUI', 'TSM', 'SSH', 'ssh', 'lbtd', 'lsassd', 'lwiod', 'netlogond', - 'ntpd', 'sfcbd-watchdog', 'snmpd', 'vprobed', 'vpxa', 'xorg'] + valid_services = [ + "DCUI", + "TSM", + "SSH", + "ssh", + "lbtd", + "lsassd", + "lwiod", + "netlogond", + "ntpd", + "sfcbd-watchdog", + "snmpd", + "vprobed", + "vpxa", + "xorg", + ] ret = {} for host_name in host_names: # Check if the service_name provided is a valid one. # If we don't have a valid service, return. The service will be invalid for all hosts. if service_name not in valid_services: - ret.update({host_name: {'Error': '{0} is not a valid service name.'.format(service_name)}}) + ret.update( + { + host_name: { + "Error": "{0} is not a valid service name.".format(service_name) + } + } + ) return ret host_ref = _get_host_ref(service_instance, host, host_name=host_name) @@ -3100,41 +3465,49 @@ def set_service_policy(host, # Find the service key based on the given service_name if service.key == service_name: service_key = service.key - elif service_name == 'ssh' or service_name == 'SSH': - if service.key == 'TSM-SSH': - service_key = 'TSM-SSH' + elif service_name == "ssh" or service_name == "SSH": + if service.key == "TSM-SSH": + service_key = "TSM-SSH" # If we have a service_key, we've found a match. Update the policy. if service_key: try: - service_manager.UpdateServicePolicy(id=service_key, policy=service_policy) + service_manager.UpdateServicePolicy( + id=service_key, policy=service_policy + ) except vim.fault.NotFound: - msg = 'The service name \'{0}\' was not found.'.format(service_name) + msg = "The service name '{0}' was not found.".format(service_name) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue # Some services are restricted by the vSphere License Level. except vim.fault.HostConfigFault as err: - msg = '\'vsphere.set_service_policy\' failed for host {0}: {1}'.format(host_name, err) + msg = "'vsphere.set_service_policy' failed for host {0}: {1}".format( + host_name, err + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue ret.update({host_name: True}) # If we made it this far, something else has gone wrong. if ret.get(host_name) is None: - msg = 'Could not find service \'{0}\' for host \'{1}\'.'.format(service_name, host_name) + msg = "Could not find service '{0}' for host '{1}'.".format( + service_name, host_name + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def update_host_datetime(host, username, password, protocol=None, port=None, host_names=None): - ''' +@ignores_kwargs("credstore") +def update_host_datetime( + host, username, password, protocol=None, port=None, host_names=None +): + """ Update the date/time on the given host or list of host_names. This function should be used with caution since network delays and execution delays can result in time skews. @@ -3174,12 +3547,10 @@ def update_host_datetime(host, username, password, protocol=None, port=None, hos # Used for connecting to a vCenter Server salt '*' vsphere.update_date_time my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) ret = {} for host_name in host_names: @@ -3188,20 +3559,24 @@ def update_host_datetime(host, username, password, protocol=None, port=None, hos try: date_time_manager.UpdateDateTime(datetime.datetime.utcnow()) except vim.fault.HostConfigFault as err: - msg = '\'vsphere.update_date_time\' failed for host {0}: {1}'.format(host_name, err) + msg = "'vsphere.update_date_time' failed for host {0}: {1}".format( + host_name, err + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue - ret.update({host_name: {'Datetime Updated': True}}) + ret.update({host_name: {"Datetime Updated": True}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def update_host_password(host, username, password, new_password, protocol=None, port=None): - ''' +@ignores_kwargs("credstore") +def update_host_password( + host, username, password, new_password, protocol=None, port=None +): + """ Update the password for a given host. .. note:: Currently only works with connections to ESXi hosts. Does not work with vCenter servers. @@ -3232,12 +3607,10 @@ def update_host_password(host, username, password, new_password, protocol=None, salt '*' vsphere.update_host_password my.esxi.host root original-bad-password new-bad-password - ''' - service_instance = salt.utils.vmware.get_service_instance(host=host, - username=username, - password=password, - protocol=protocol, - port=port) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) # Get LocalAccountManager object account_manager = salt.utils.vmware.get_inventory(service_instance).accountManager @@ -3252,8 +3625,10 @@ def update_host_password(host, username, password, new_password, protocol=None, except vmodl.fault.SystemError as err: raise CommandExecutionError(err.msg) except vim.fault.UserNotFound: - raise CommandExecutionError('\'vsphere.update_host_password\' failed for host {0}: ' - 'User was not found.'.format(host)) + raise CommandExecutionError( + "'vsphere.update_host_password' failed for host {0}: " + "User was not found.".format(host) + ) # If the username and password already exist, we don't need to do anything. except vim.fault.AlreadyExists: pass @@ -3262,9 +3637,11 @@ def update_host_password(host, username, password, new_password, protocol=None, @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def vmotion_disable(host, username, password, protocol=None, port=None, host_names=None): - ''' +@ignores_kwargs("credstore") +def vmotion_disable( + host, username, password, protocol=None, port=None, host_names=None +): + """ Disable vMotion for a given host or list of host_names. host @@ -3303,12 +3680,10 @@ def vmotion_disable(host, username, password, protocol=None, port=None, host_nam # Used for connecting to a vCenter Server salt '*' vsphere.vmotion_disable my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) ret = {} for host_name in host_names: @@ -3319,21 +3694,22 @@ def vmotion_disable(host, username, password, protocol=None, port=None, host_nam try: vmotion_system.DeselectVnic() except vim.fault.HostConfigFault as err: - msg = 'vsphere.vmotion_disable failed: {0}'.format(err) + msg = "vsphere.vmotion_disable failed: {0}".format(err) log.debug(msg) - ret.update({host_name: {'Error': msg, - 'VMotion Disabled': False}}) + ret.update({host_name: {"Error": msg, "VMotion Disabled": False}}) continue - ret.update({host_name: {'VMotion Disabled': True}}) + ret.update({host_name: {"VMotion Disabled": True}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def vmotion_enable(host, username, password, protocol=None, port=None, host_names=None, device='vmk0'): - ''' +@ignores_kwargs("credstore") +def vmotion_enable( + host, username, password, protocol=None, port=None, host_names=None, device="vmk0" +): + """ Enable vMotion for a given host or list of host_names. host @@ -3376,12 +3752,10 @@ def vmotion_enable(host, username, password, protocol=None, port=None, host_name # Used for connecting to a vCenter Server salt '*' vsphere.vmotion_enable my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) ret = {} for host_name in host_names: @@ -3392,21 +3766,20 @@ def vmotion_enable(host, username, password, protocol=None, port=None, host_name try: vmotion_system.SelectVnic(device) except vim.fault.HostConfigFault as err: - msg = 'vsphere.vmotion_disable failed: {0}'.format(err) + msg = "vsphere.vmotion_disable failed: {0}".format(err) log.debug(msg) - ret.update({host_name: {'Error': msg, - 'VMotion Enabled': False}}) + ret.update({host_name: {"Error": msg, "VMotion Enabled": False}}) continue - ret.update({host_name: {'VMotion Enabled': True}}) + ret.update({host_name: {"VMotion Enabled": True}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def vsan_add_disks(host, username, password, protocol=None, port=None, host_names=None): - ''' + """ Add any VSAN-eligible disks to the VSAN System for the given host or list of host_names. host @@ -3446,12 +3819,10 @@ def vsan_add_disks(host, username, password, protocol=None, port=None, host_name # Used for connecting to a vCenter Server salt '*' vsphere.vsan_add_disks my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) host_names = _check_hosts(service_instance, host, host_names) response = _get_vsan_eligible_disks(service_instance, host, host_names) @@ -3462,55 +3833,71 @@ def vsan_add_disks(host, username, password, protocol=None, port=None, host_name # We must have a VSAN Config in place before we can manipulate it. if vsan_system is None: - msg = 'VSAN System Config Manager is unset for host \'{0}\'. ' \ - 'VSAN configuration cannot be changed without a configured ' \ - 'VSAN System.'.format(host_name) + msg = ( + "VSAN System Config Manager is unset for host '{0}'. " + "VSAN configuration cannot be changed without a configured " + "VSAN System.".format(host_name) + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) else: - eligible = value.get('Eligible') - error = value.get('Error') + eligible = value.get("Eligible") + error = value.get("Error") if eligible and isinstance(eligible, list): # If we have eligible, matching disks, add them to VSAN. try: task = vsan_system.AddDisks(eligible) - salt.utils.vmware.wait_for_task(task, host_name, 'Adding disks to VSAN', sleep_seconds=3) + salt.utils.vmware.wait_for_task( + task, host_name, "Adding disks to VSAN", sleep_seconds=3 + ) except vim.fault.InsufficientDisks as err: log.debug(err.msg) - ret.update({host_name: {'Error': err.msg}}) + ret.update({host_name: {"Error": err.msg}}) continue except Exception as err: # pylint: disable=broad-except - msg = '\'vsphere.vsan_add_disks\' failed for host {0}: {1}'.format(host_name, err) + msg = "'vsphere.vsan_add_disks' failed for host {0}: {1}".format( + host_name, err + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue - log.debug('Successfully added disks to the VSAN system for host \'{0}\'.'.format(host_name)) + log.debug( + "Successfully added disks to the VSAN system for host '{0}'.".format( + host_name + ) + ) # We need to return ONLY the disk names, otherwise Message Pack can't deserialize the disk objects. disk_names = [] for disk in eligible: disk_names.append(disk.canonicalName) - ret.update({host_name: {'Disks Added': disk_names}}) + ret.update({host_name: {"Disks Added": disk_names}}) elif eligible and isinstance(eligible, six.string_types): # If we have a string type in the eligible value, we don't # have any VSAN-eligible disks. Pull the message through. - ret.update({host_name: {'Disks Added': eligible}}) + ret.update({host_name: {"Disks Added": eligible}}) elif error: # If we hit an error, populate the Error return dict for state functions. - ret.update({host_name: {'Error': error}}) + ret.update({host_name: {"Error": error}}) else: # If we made it this far, we somehow have eligible disks, but they didn't # match the disk list and just got an empty list of matching disks. - ret.update({host_name: {'Disks Added': 'No new VSAN-eligible disks were found to add.'}}) + ret.update( + { + host_name: { + "Disks Added": "No new VSAN-eligible disks were found to add." + } + } + ) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def vsan_disable(host, username, password, protocol=None, port=None, host_names=None): - ''' + """ Disable VSAN for a given host or list of host_names. host @@ -3549,12 +3936,10 @@ def vsan_disable(host, username, password, protocol=None, port=None, host_names= # Used for connecting to a vCenter Server salt '*' vsphere.vsan_disable my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) # Create a VSAN Configuration Object and set the enabled attribute to True vsan_config = vim.vsan.host.ConfigInfo() vsan_config.enabled = False @@ -3567,35 +3952,41 @@ def vsan_disable(host, username, password, protocol=None, port=None, host_names= # We must have a VSAN Config in place before we can manipulate it. if vsan_system is None: - msg = 'VSAN System Config Manager is unset for host \'{0}\'. ' \ - 'VSAN configuration cannot be changed without a configured ' \ - 'VSAN System.'.format(host_name) + msg = ( + "VSAN System Config Manager is unset for host '{0}'. " + "VSAN configuration cannot be changed without a configured " + "VSAN System.".format(host_name) + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) else: try: # Disable vsan on the host task = vsan_system.UpdateVsan_Task(vsan_config) - salt.utils.vmware.wait_for_task(task, host_name, 'Disabling VSAN', sleep_seconds=3) + salt.utils.vmware.wait_for_task( + task, host_name, "Disabling VSAN", sleep_seconds=3 + ) except vmodl.fault.SystemError as err: log.debug(err.msg) - ret.update({host_name: {'Error': err.msg}}) + ret.update({host_name: {"Error": err.msg}}) continue except Exception as err: # pylint: disable=broad-except - msg = '\'vsphere.vsan_disable\' failed for host {0}: {1}'.format(host_name, err) + msg = "'vsphere.vsan_disable' failed for host {0}: {1}".format( + host_name, err + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue - ret.update({host_name: {'VSAN Disabled': True}}) + ret.update({host_name: {"VSAN Disabled": True}}) return ret @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') +@ignores_kwargs("credstore") def vsan_enable(host, username, password, protocol=None, port=None, host_names=None): - ''' + """ Enable VSAN for a given host or list of host_names. host @@ -3634,12 +4025,10 @@ def vsan_enable(host, username, password, protocol=None, port=None, host_names=N # Used for connecting to a vCenter Server salt '*' vsphere.vsan_enable my.vcenter.location root bad-password \ 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) + """ + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) # Create a VSAN Configuration Object and set the enabled attribute to True vsan_config = vim.vsan.host.ConfigInfo() vsan_config.enabled = True @@ -3652,33 +4041,39 @@ def vsan_enable(host, username, password, protocol=None, port=None, host_names=N # We must have a VSAN Config in place before we can manipulate it. if vsan_system is None: - msg = 'VSAN System Config Manager is unset for host \'{0}\'. ' \ - 'VSAN configuration cannot be changed without a configured ' \ - 'VSAN System.'.format(host_name) + msg = ( + "VSAN System Config Manager is unset for host '{0}'. " + "VSAN configuration cannot be changed without a configured " + "VSAN System.".format(host_name) + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) else: try: # Enable vsan on the host task = vsan_system.UpdateVsan_Task(vsan_config) - salt.utils.vmware.wait_for_task(task, host_name, 'Enabling VSAN', sleep_seconds=3) + salt.utils.vmware.wait_for_task( + task, host_name, "Enabling VSAN", sleep_seconds=3 + ) except vmodl.fault.SystemError as err: log.debug(err.msg) - ret.update({host_name: {'Error': err.msg}}) + ret.update({host_name: {"Error": err.msg}}) continue except vim.fault.VsanFault as err: - msg = '\'vsphere.vsan_enable\' failed for host {0}: {1}'.format(host_name, err) + msg = "'vsphere.vsan_enable' failed for host {0}: {1}".format( + host_name, err + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue - ret.update({host_name: {'VSAN Enabled': True}}) + ret.update({host_name: {"VSAN Enabled": True}}) return ret def _get_dvs_config_dict(dvs_name, dvs_config): - ''' + """ Returns the dict representation of the DVS config dvs_name @@ -3686,27 +4081,25 @@ def _get_dvs_config_dict(dvs_name, dvs_config): dvs_config The DVS config - ''' - log.trace('Building the dict of the DVS \'{0}\' config'.format(dvs_name)) - conf_dict = {'name': dvs_name, - 'contact_email': dvs_config.contact.contact, - 'contact_name': dvs_config.contact.name, - 'description': dvs_config.description, - 'lacp_api_version': dvs_config.lacpApiVersion, - 'network_resource_control_version': - dvs_config.networkResourceControlVersion, - 'network_resource_management_enabled': - dvs_config.networkResourceManagementEnabled, - 'max_mtu': dvs_config.maxMtu} - if isinstance(dvs_config.uplinkPortPolicy, - vim.DVSNameArrayUplinkPortPolicy): - conf_dict.update( - {'uplink_names': dvs_config.uplinkPortPolicy.uplinkPortName}) + """ + log.trace("Building the dict of the DVS '{0}' config".format(dvs_name)) + conf_dict = { + "name": dvs_name, + "contact_email": dvs_config.contact.contact, + "contact_name": dvs_config.contact.name, + "description": dvs_config.description, + "lacp_api_version": dvs_config.lacpApiVersion, + "network_resource_control_version": dvs_config.networkResourceControlVersion, + "network_resource_management_enabled": dvs_config.networkResourceManagementEnabled, + "max_mtu": dvs_config.maxMtu, + } + if isinstance(dvs_config.uplinkPortPolicy, vim.DVSNameArrayUplinkPortPolicy): + conf_dict.update({"uplink_names": dvs_config.uplinkPortPolicy.uplinkPortName}) return conf_dict def _get_dvs_link_discovery_protocol(dvs_name, dvs_link_disc_protocol): - ''' + """ Returns the dict representation of the DVS link discovery protocol dvs_name @@ -3714,15 +4107,18 @@ def _get_dvs_link_discovery_protocol(dvs_name, dvs_link_disc_protocol): dvs_link_disc_protocl The DVS link discovery protocol - ''' - log.trace('Building the dict of the DVS \'{0}\' link discovery ' - 'protocol'.format(dvs_name)) - return {'operation': dvs_link_disc_protocol.operation, - 'protocol': dvs_link_disc_protocol.protocol} + """ + log.trace( + "Building the dict of the DVS '{0}' link discovery " "protocol".format(dvs_name) + ) + return { + "operation": dvs_link_disc_protocol.operation, + "protocol": dvs_link_disc_protocol.protocol, + } def _get_dvs_product_info(dvs_name, dvs_product_info): - ''' + """ Returns the dict representation of the DVS product_info dvs_name @@ -3730,16 +4126,17 @@ def _get_dvs_product_info(dvs_name, dvs_product_info): dvs_product_info The DVS product info - ''' - log.trace('Building the dict of the DVS \'{0}\' product ' - 'info'.format(dvs_name)) - return {'name': dvs_product_info.name, - 'vendor': dvs_product_info.vendor, - 'version': dvs_product_info.version} + """ + log.trace("Building the dict of the DVS '{0}' product " "info".format(dvs_name)) + return { + "name": dvs_product_info.name, + "vendor": dvs_product_info.vendor, + "version": dvs_product_info.version, + } def _get_dvs_capability(dvs_name, dvs_capability): - ''' + """ Returns the dict representation of the DVS product_info dvs_name @@ -3747,18 +4144,17 @@ def _get_dvs_capability(dvs_name, dvs_capability): dvs_capability The DVS capability - ''' - log.trace('Building the dict of the DVS \'{0}\' capability' - ''.format(dvs_name)) - return {'operation_supported': dvs_capability.dvsOperationSupported, - 'portgroup_operation_supported': - dvs_capability.dvPortGroupOperationSupported, - 'port_operation_supported': dvs_capability.dvPortOperationSupported} + """ + log.trace("Building the dict of the DVS '{0}' capability" "".format(dvs_name)) + return { + "operation_supported": dvs_capability.dvsOperationSupported, + "portgroup_operation_supported": dvs_capability.dvPortGroupOperationSupported, + "port_operation_supported": dvs_capability.dvPortOperationSupported, + } -def _get_dvs_infrastructure_traffic_resources(dvs_name, - dvs_infra_traffic_ress): - ''' +def _get_dvs_infrastructure_traffic_resources(dvs_name, dvs_infra_traffic_ress): + """ Returns a list of dict representations of the DVS infrastructure traffic resource @@ -3767,26 +4163,34 @@ def _get_dvs_infrastructure_traffic_resources(dvs_name, dvs_infra_traffic_ress The DVS infrastructure traffic resources - ''' - log.trace('Building the dicts of the DVS \'{0}\' infrastructure traffic ' - 'resources'.format(dvs_name)) + """ + log.trace( + "Building the dicts of the DVS '{0}' infrastructure traffic " + "resources".format(dvs_name) + ) res_dicts = [] for res in dvs_infra_traffic_ress: - res_dict = {'key': res.key, - 'limit': res.allocationInfo.limit, - 'reservation': res.allocationInfo.reservation} + res_dict = { + "key": res.key, + "limit": res.allocationInfo.limit, + "reservation": res.allocationInfo.reservation, + } if res.allocationInfo.shares: - res_dict.update({'num_shares': res.allocationInfo.shares.shares, - 'share_level': res.allocationInfo.shares.level}) + res_dict.update( + { + "num_shares": res.allocationInfo.shares.shares, + "share_level": res.allocationInfo.shares.level, + } + ) res_dicts.append(res_dict) return res_dicts @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'esxcluster') +@supports_proxies("esxdatacenter", "esxcluster") @gets_service_instance_via_proxy def list_dvss(datacenter=None, dvs_names=None, service_instance=None): - ''' + """ Returns a list of distributed virtual switches (DVSs). The list can be filtered by the datacenter or DVS names. @@ -3803,14 +4207,14 @@ def list_dvss(datacenter=None, dvs_names=None, service_instance=None): salt '*' vsphere.list_dvss salt '*' vsphere.list_dvss dvs_names=[dvs1,dvs2] - ''' + """ ret_list = [] proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) for dvs in salt.utils.vmware.get_dvss(dc_ref, dvs_names, (not dvs_names)): @@ -3819,170 +4223,181 @@ def list_dvss(datacenter=None, dvs_names=None, service_instance=None): # be more restrictive when retrieving the dvs config, we have to # retrieve the entire object props = salt.utils.vmware.get_properties_of_managed_object( - dvs, ['name', 'config', 'capability', 'networkResourcePool']) - dvs_dict = _get_dvs_config_dict(props['name'], props['config']) + dvs, ["name", "config", "capability", "networkResourcePool"] + ) + dvs_dict = _get_dvs_config_dict(props["name"], props["config"]) # Product info dvs_dict.update( - {'product_info': - _get_dvs_product_info(props['name'], - props['config'].productInfo)}) + { + "product_info": _get_dvs_product_info( + props["name"], props["config"].productInfo + ) + } + ) # Link Discovery Protocol - if props['config'].linkDiscoveryProtocolConfig: + if props["config"].linkDiscoveryProtocolConfig: dvs_dict.update( - {'link_discovery_protocol': - _get_dvs_link_discovery_protocol( - props['name'], - props['config'].linkDiscoveryProtocolConfig)}) + { + "link_discovery_protocol": _get_dvs_link_discovery_protocol( + props["name"], props["config"].linkDiscoveryProtocolConfig + ) + } + ) # Capability - dvs_dict.update({'capability': - _get_dvs_capability(props['name'], - props['capability'])}) + dvs_dict.update( + {"capability": _get_dvs_capability(props["name"], props["capability"])} + ) # InfrastructureTrafficResourceConfig - available with vSphere 6.0 - if hasattr(props['config'], 'infrastructureTrafficResourceConfig'): - dvs_dict.update({ - 'infrastructure_traffic_resource_pools': - _get_dvs_infrastructure_traffic_resources( - props['name'], - props['config'].infrastructureTrafficResourceConfig)}) + if hasattr(props["config"], "infrastructureTrafficResourceConfig"): + dvs_dict.update( + { + "infrastructure_traffic_resource_pools": _get_dvs_infrastructure_traffic_resources( + props["name"], + props["config"].infrastructureTrafficResourceConfig, + ) + } + ) ret_list.append(dvs_dict) return ret_list def _apply_dvs_config(config_spec, config_dict): - ''' + """ Applies the values of the config dict dictionary to a config spec (vim.VMwareDVSConfigSpec) - ''' - if config_dict.get('name'): - config_spec.name = config_dict['name'] - if config_dict.get('contact_email') or config_dict.get('contact_name'): + """ + if config_dict.get("name"): + config_spec.name = config_dict["name"] + if config_dict.get("contact_email") or config_dict.get("contact_name"): if not config_spec.contact: config_spec.contact = vim.DVSContactInfo() - config_spec.contact.contact = config_dict.get('contact_email') - config_spec.contact.name = config_dict.get('contact_name') - if config_dict.get('description'): - config_spec.description = config_dict.get('description') - if config_dict.get('max_mtu'): - config_spec.maxMtu = config_dict.get('max_mtu') - if config_dict.get('lacp_api_version'): - config_spec.lacpApiVersion = config_dict.get('lacp_api_version') - if config_dict.get('network_resource_control_version'): - config_spec.networkResourceControlVersion = \ - config_dict.get('network_resource_control_version') - if config_dict.get('uplink_names'): - if not config_spec.uplinkPortPolicy or \ - not isinstance(config_spec.uplinkPortPolicy, - vim.DVSNameArrayUplinkPortPolicy): + config_spec.contact.contact = config_dict.get("contact_email") + config_spec.contact.name = config_dict.get("contact_name") + if config_dict.get("description"): + config_spec.description = config_dict.get("description") + if config_dict.get("max_mtu"): + config_spec.maxMtu = config_dict.get("max_mtu") + if config_dict.get("lacp_api_version"): + config_spec.lacpApiVersion = config_dict.get("lacp_api_version") + if config_dict.get("network_resource_control_version"): + config_spec.networkResourceControlVersion = config_dict.get( + "network_resource_control_version" + ) + if config_dict.get("uplink_names"): + if not config_spec.uplinkPortPolicy or not isinstance( + config_spec.uplinkPortPolicy, vim.DVSNameArrayUplinkPortPolicy + ): - config_spec.uplinkPortPolicy = \ - vim.DVSNameArrayUplinkPortPolicy() - config_spec.uplinkPortPolicy.uplinkPortName = \ - config_dict['uplink_names'] + config_spec.uplinkPortPolicy = vim.DVSNameArrayUplinkPortPolicy() + config_spec.uplinkPortPolicy.uplinkPortName = config_dict["uplink_names"] def _apply_dvs_link_discovery_protocol(disc_prot_config, disc_prot_dict): - ''' + """ Applies the values of the disc_prot_dict dictionary to a link discovery protocol config object (vim.LinkDiscoveryProtocolConfig) - ''' - disc_prot_config.operation = disc_prot_dict['operation'] - disc_prot_config.protocol = disc_prot_dict['protocol'] + """ + disc_prot_config.operation = disc_prot_dict["operation"] + disc_prot_config.protocol = disc_prot_dict["protocol"] def _apply_dvs_product_info(product_info_spec, product_info_dict): - ''' + """ Applies the values of the product_info_dict dictionary to a product info spec (vim.DistributedVirtualSwitchProductSpec) - ''' - if product_info_dict.get('name'): - product_info_spec.name = product_info_dict['name'] - if product_info_dict.get('vendor'): - product_info_spec.vendor = product_info_dict['vendor'] - if product_info_dict.get('version'): - product_info_spec.version = product_info_dict['version'] + """ + if product_info_dict.get("name"): + product_info_spec.name = product_info_dict["name"] + if product_info_dict.get("vendor"): + product_info_spec.vendor = product_info_dict["vendor"] + if product_info_dict.get("version"): + product_info_spec.version = product_info_dict["version"] def _apply_dvs_capability(capability_spec, capability_dict): - ''' + """ Applies the values of the capability_dict dictionary to a DVS capability object (vim.vim.DVSCapability) - ''' - if 'operation_supported' in capability_dict: - capability_spec.dvsOperationSupported = \ - capability_dict['operation_supported'] - if 'port_operation_supported' in capability_dict: - capability_spec.dvPortOperationSupported = \ - capability_dict['port_operation_supported'] - if 'portgroup_operation_supported' in capability_dict: - capability_spec.dvPortGroupOperationSupported = \ - capability_dict['portgroup_operation_supported'] + """ + if "operation_supported" in capability_dict: + capability_spec.dvsOperationSupported = capability_dict["operation_supported"] + if "port_operation_supported" in capability_dict: + capability_spec.dvPortOperationSupported = capability_dict[ + "port_operation_supported" + ] + if "portgroup_operation_supported" in capability_dict: + capability_spec.dvPortGroupOperationSupported = capability_dict[ + "portgroup_operation_supported" + ] -def _apply_dvs_infrastructure_traffic_resources(infra_traffic_resources, - resource_dicts): - ''' +def _apply_dvs_infrastructure_traffic_resources( + infra_traffic_resources, resource_dicts +): + """ Applies the values of the resource dictionaries to infra traffic resources, creating the infra traffic resource if required (vim.DistributedVirtualSwitchProductSpec) - ''' + """ for res_dict in resource_dicts: - filtered_traffic_resources = \ - [r for r in infra_traffic_resources if r.key == res_dict['key']] + filtered_traffic_resources = [ + r for r in infra_traffic_resources if r.key == res_dict["key"] + ] if filtered_traffic_resources: traffic_res = filtered_traffic_resources[0] else: traffic_res = vim.DvsHostInfrastructureTrafficResource() - traffic_res.key = res_dict['key'] - traffic_res.allocationInfo = \ - vim.DvsHostInfrastructureTrafficResourceAllocation() + traffic_res.key = res_dict["key"] + traffic_res.allocationInfo = ( + vim.DvsHostInfrastructureTrafficResourceAllocation() + ) infra_traffic_resources.append(traffic_res) - if res_dict.get('limit'): - traffic_res.allocationInfo.limit = res_dict['limit'] - if res_dict.get('reservation'): - traffic_res.allocationInfo.reservation = res_dict['reservation'] - if res_dict.get('num_shares') or res_dict.get('share_level'): + if res_dict.get("limit"): + traffic_res.allocationInfo.limit = res_dict["limit"] + if res_dict.get("reservation"): + traffic_res.allocationInfo.reservation = res_dict["reservation"] + if res_dict.get("num_shares") or res_dict.get("share_level"): if not traffic_res.allocationInfo.shares: traffic_res.allocationInfo.shares = vim.SharesInfo() - if res_dict.get('share_level'): - traffic_res.allocationInfo.shares.level = \ - vim.SharesLevel(res_dict['share_level']) - if res_dict.get('num_shares'): - #XXX Even though we always set the number of shares if provided, - #the vCenter will ignore it unless the share level is 'custom'. - traffic_res.allocationInfo.shares.shares = res_dict['num_shares'] + if res_dict.get("share_level"): + traffic_res.allocationInfo.shares.level = vim.SharesLevel( + res_dict["share_level"] + ) + if res_dict.get("num_shares"): + # XXX Even though we always set the number of shares if provided, + # the vCenter will ignore it unless the share level is 'custom'. + traffic_res.allocationInfo.shares.shares = res_dict["num_shares"] def _apply_dvs_network_resource_pools(network_resource_pools, resource_dicts): - ''' + """ Applies the values of the resource dictionaries to network resource pools, creating the resource pools if required (vim.DVSNetworkResourcePoolConfigSpec) - ''' + """ for res_dict in resource_dicts: - ress = [r for r in network_resource_pools if r.key == res_dict['key']] + ress = [r for r in network_resource_pools if r.key == res_dict["key"]] if ress: res = ress[0] else: res = vim.DVSNetworkResourcePoolConfigSpec() - res.key = res_dict['key'] - res.allocationInfo = \ - vim.DVSNetworkResourcePoolAllocationInfo() + res.key = res_dict["key"] + res.allocationInfo = vim.DVSNetworkResourcePoolAllocationInfo() network_resource_pools.append(res) - if res_dict.get('limit'): - res.allocationInfo.limit = res_dict['limit'] - if res_dict.get('num_shares') and res_dict.get('share_level'): + if res_dict.get("limit"): + res.allocationInfo.limit = res_dict["limit"] + if res_dict.get("num_shares") and res_dict.get("share_level"): if not res.allocationInfo.shares: res.allocationInfo.shares = vim.SharesInfo() - res.allocationInfo.shares.shares = res_dict['num_shares'] - res.allocationInfo.shares.level = \ - vim.SharesLevel(res_dict['share_level']) + res.allocationInfo.shares.shares = res_dict["num_shares"] + res.allocationInfo.shares.level = vim.SharesLevel(res_dict["share_level"]) @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'esxcluster') +@supports_proxies("esxdatacenter", "esxcluster") @gets_service_instance_via_proxy def create_dvs(dvs_dict, dvs_name, service_instance=None): - ''' + """ Creates a distributed virtual switch (DVS). Note: The ``dvs_name`` param will override any name set in ``dvs_dict``. @@ -4000,61 +4415,62 @@ def create_dvs(dvs_dict, dvs_name, service_instance=None): .. code-block:: bash salt '*' vsphere.create_dvs dvs dict=$dvs_dict dvs_name=dvs_name - ''' - log.trace('Creating dvs \'{0}\' with dict = {1}'.format(dvs_name, - dvs_dict)) + """ + log.trace("Creating dvs '{0}' with dict = {1}".format(dvs_name, dvs_dict)) proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) # Make the name of the DVS consistent with the call - dvs_dict['name'] = dvs_name + dvs_dict["name"] = dvs_name # Build the config spec from the input dvs_create_spec = vim.DVSCreateSpec() dvs_create_spec.configSpec = vim.VMwareDVSConfigSpec() _apply_dvs_config(dvs_create_spec.configSpec, dvs_dict) - if dvs_dict.get('product_info'): + if dvs_dict.get("product_info"): dvs_create_spec.productInfo = vim.DistributedVirtualSwitchProductSpec() - _apply_dvs_product_info(dvs_create_spec.productInfo, - dvs_dict['product_info']) - if dvs_dict.get('capability'): + _apply_dvs_product_info(dvs_create_spec.productInfo, dvs_dict["product_info"]) + if dvs_dict.get("capability"): dvs_create_spec.capability = vim.DVSCapability() - _apply_dvs_capability(dvs_create_spec.capability, - dvs_dict['capability']) - if dvs_dict.get('link_discovery_protocol'): - dvs_create_spec.configSpec.linkDiscoveryProtocolConfig = \ - vim.LinkDiscoveryProtocolConfig() + _apply_dvs_capability(dvs_create_spec.capability, dvs_dict["capability"]) + if dvs_dict.get("link_discovery_protocol"): + dvs_create_spec.configSpec.linkDiscoveryProtocolConfig = ( + vim.LinkDiscoveryProtocolConfig() + ) _apply_dvs_link_discovery_protocol( dvs_create_spec.configSpec.linkDiscoveryProtocolConfig, - dvs_dict['link_discovery_protocol']) - if dvs_dict.get('infrastructure_traffic_resource_pools'): + dvs_dict["link_discovery_protocol"], + ) + if dvs_dict.get("infrastructure_traffic_resource_pools"): dvs_create_spec.configSpec.infrastructureTrafficResourceConfig = [] _apply_dvs_infrastructure_traffic_resources( dvs_create_spec.configSpec.infrastructureTrafficResourceConfig, - dvs_dict['infrastructure_traffic_resource_pools']) - log.trace('dvs_create_spec = {}'.format(dvs_create_spec)) + dvs_dict["infrastructure_traffic_resource_pools"], + ) + log.trace("dvs_create_spec = {}".format(dvs_create_spec)) salt.utils.vmware.create_dvs(dc_ref, dvs_name, dvs_create_spec) - if 'network_resource_management_enabled' in dvs_dict: - dvs_refs = salt.utils.vmware.get_dvss(dc_ref, - dvs_names=[dvs_name]) + if "network_resource_management_enabled" in dvs_dict: + dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs_name]) if not dvs_refs: raise VMwareObjectRetrievalError( - 'DVS \'{0}\' wasn\'t found in datacenter \'{1}\'' - ''.format(dvs_name, datacenter)) + "DVS '{0}' wasn't found in datacenter '{1}'" + "".format(dvs_name, datacenter) + ) dvs_ref = dvs_refs[0] salt.utils.vmware.set_dvs_network_resource_management_enabled( - dvs_ref, dvs_dict['network_resource_management_enabled']) + dvs_ref, dvs_dict["network_resource_management_enabled"] + ) return True @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'esxcluster') +@supports_proxies("esxdatacenter", "esxcluster") @gets_service_instance_via_proxy def update_dvs(dvs_dict, dvs, service_instance=None): - ''' + """ Updates a distributed virtual switch (DVS). Note: Updating the product info, capability, uplinks of a DVS is not @@ -4075,61 +4491,63 @@ def update_dvs(dvs_dict, dvs, service_instance=None): .. code-block:: bash salt '*' vsphere.update_dvs dvs_dict=$dvs_dict dvs=dvs1 - ''' + """ # Remove ignored properties - log.trace('Updating dvs \'{0}\' with dict = {1}'.format(dvs, dvs_dict)) - for prop in ['product_info', 'capability', 'uplink_names', 'name']: + log.trace("Updating dvs '{0}' with dict = {1}".format(dvs, dvs_dict)) + for prop in ["product_info", "capability", "uplink_names", "name"]: if prop in dvs_dict: del dvs_dict[prop] proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs]) if not dvs_refs: - raise VMwareObjectRetrievalError('DVS \'{0}\' wasn\'t found in ' - 'datacenter \'{1}\'' - ''.format(dvs, datacenter)) + raise VMwareObjectRetrievalError( + "DVS '{0}' wasn't found in " "datacenter '{1}'" "".format(dvs, datacenter) + ) dvs_ref = dvs_refs[0] # Build the config spec from the input dvs_props = salt.utils.vmware.get_properties_of_managed_object( - dvs_ref, ['config', 'capability']) + dvs_ref, ["config", "capability"] + ) dvs_config = vim.VMwareDVSConfigSpec() # Copy all of the properties in the config of the of the DVS to a # DvsConfigSpec - skipped_properties = ['host'] + skipped_properties = ["host"] for prop in dvs_config.__dict__.keys(): if prop in skipped_properties: continue - if hasattr(dvs_props['config'], prop): - setattr(dvs_config, prop, getattr(dvs_props['config'], prop)) + if hasattr(dvs_props["config"], prop): + setattr(dvs_config, prop, getattr(dvs_props["config"], prop)) _apply_dvs_config(dvs_config, dvs_dict) - if dvs_dict.get('link_discovery_protocol'): + if dvs_dict.get("link_discovery_protocol"): if not dvs_config.linkDiscoveryProtocolConfig: - dvs_config.linkDiscoveryProtocolConfig = \ - vim.LinkDiscoveryProtocolConfig() + dvs_config.linkDiscoveryProtocolConfig = vim.LinkDiscoveryProtocolConfig() _apply_dvs_link_discovery_protocol( - dvs_config.linkDiscoveryProtocolConfig, - dvs_dict['link_discovery_protocol']) - if dvs_dict.get('infrastructure_traffic_resource_pools'): + dvs_config.linkDiscoveryProtocolConfig, dvs_dict["link_discovery_protocol"] + ) + if dvs_dict.get("infrastructure_traffic_resource_pools"): if not dvs_config.infrastructureTrafficResourceConfig: dvs_config.infrastructureTrafficResourceConfig = [] _apply_dvs_infrastructure_traffic_resources( dvs_config.infrastructureTrafficResourceConfig, - dvs_dict['infrastructure_traffic_resource_pools']) - log.trace('dvs_config= {}'.format(dvs_config)) + dvs_dict["infrastructure_traffic_resource_pools"], + ) + log.trace("dvs_config= {}".format(dvs_config)) salt.utils.vmware.update_dvs(dvs_ref, dvs_config_spec=dvs_config) - if 'network_resource_management_enabled' in dvs_dict: + if "network_resource_management_enabled" in dvs_dict: salt.utils.vmware.set_dvs_network_resource_management_enabled( - dvs_ref, dvs_dict['network_resource_management_enabled']) + dvs_ref, dvs_dict["network_resource_management_enabled"] + ) return True def _get_dvportgroup_out_shaping(pg_name, pg_default_port_config): - ''' + """ Returns the out shaping policy of a distributed virtual portgroup pg_name @@ -4137,20 +4555,21 @@ def _get_dvportgroup_out_shaping(pg_name, pg_default_port_config): pg_default_port_config The dafault port config of the portgroup - ''' - log.trace('Retrieving portgroup\'s \'{0}\' out shaping ' - 'config'.format(pg_name)) + """ + log.trace("Retrieving portgroup's '{0}' out shaping " "config".format(pg_name)) out_shaping_policy = pg_default_port_config.outShapingPolicy if not out_shaping_policy: return {} - return {'average_bandwidth': out_shaping_policy.averageBandwidth.value, - 'burst_size': out_shaping_policy.burstSize.value, - 'enabled': out_shaping_policy.enabled.value, - 'peak_bandwidth': out_shaping_policy.peakBandwidth.value} + return { + "average_bandwidth": out_shaping_policy.averageBandwidth.value, + "burst_size": out_shaping_policy.burstSize.value, + "enabled": out_shaping_policy.enabled.value, + "peak_bandwidth": out_shaping_policy.peakBandwidth.value, + } def _get_dvportgroup_security_policy(pg_name, pg_default_port_config): - ''' + """ Returns the security policy of a distributed virtual portgroup pg_name @@ -4158,19 +4577,20 @@ def _get_dvportgroup_security_policy(pg_name, pg_default_port_config): pg_default_port_config The dafault port config of the portgroup - ''' - log.trace('Retrieving portgroup\'s \'{0}\' security policy ' - 'config'.format(pg_name)) + """ + log.trace("Retrieving portgroup's '{0}' security policy " "config".format(pg_name)) sec_policy = pg_default_port_config.securityPolicy if not sec_policy: return {} - return {'allow_promiscuous': sec_policy.allowPromiscuous.value, - 'forged_transmits': sec_policy.forgedTransmits.value, - 'mac_changes': sec_policy.macChanges.value} + return { + "allow_promiscuous": sec_policy.allowPromiscuous.value, + "forged_transmits": sec_policy.forgedTransmits.value, + "mac_changes": sec_policy.macChanges.value, + } def _get_dvportgroup_teaming(pg_name, pg_default_port_config): - ''' + """ Returns the teaming of a distributed virtual portgroup pg_name @@ -4178,76 +4598,105 @@ def _get_dvportgroup_teaming(pg_name, pg_default_port_config): pg_default_port_config The dafault port config of the portgroup - ''' - log.trace('Retrieving portgroup\'s \'{0}\' teaming' - 'config'.format(pg_name)) + """ + log.trace("Retrieving portgroup's '{0}' teaming" "config".format(pg_name)) teaming_policy = pg_default_port_config.uplinkTeamingPolicy if not teaming_policy: return {} - ret_dict = {'notify_switches': teaming_policy.notifySwitches.value, - 'policy': teaming_policy.policy.value, - 'reverse_policy': teaming_policy.reversePolicy.value, - 'rolling_order': teaming_policy.rollingOrder.value} + ret_dict = { + "notify_switches": teaming_policy.notifySwitches.value, + "policy": teaming_policy.policy.value, + "reverse_policy": teaming_policy.reversePolicy.value, + "rolling_order": teaming_policy.rollingOrder.value, + } if teaming_policy.failureCriteria: failure_criteria = teaming_policy.failureCriteria - ret_dict.update({'failure_criteria': { - 'check_beacon': failure_criteria.checkBeacon.value, - 'check_duplex': failure_criteria.checkDuplex.value, - 'check_error_percent': failure_criteria.checkErrorPercent.value, - 'check_speed': failure_criteria.checkSpeed.value, - 'full_duplex': failure_criteria.fullDuplex.value, - 'percentage': failure_criteria.percentage.value, - 'speed': failure_criteria.speed.value}}) + ret_dict.update( + { + "failure_criteria": { + "check_beacon": failure_criteria.checkBeacon.value, + "check_duplex": failure_criteria.checkDuplex.value, + "check_error_percent": failure_criteria.checkErrorPercent.value, + "check_speed": failure_criteria.checkSpeed.value, + "full_duplex": failure_criteria.fullDuplex.value, + "percentage": failure_criteria.percentage.value, + "speed": failure_criteria.speed.value, + } + } + ) if teaming_policy.uplinkPortOrder: uplink_order = teaming_policy.uplinkPortOrder - ret_dict.update({'port_order': { - 'active': uplink_order.activeUplinkPort, - 'standby': uplink_order.standbyUplinkPort}}) + ret_dict.update( + { + "port_order": { + "active": uplink_order.activeUplinkPort, + "standby": uplink_order.standbyUplinkPort, + } + } + ) return ret_dict def _get_dvportgroup_dict(pg_ref): - ''' + """ Returns a dictionary with a distributed virtual portgroup data pg_ref Portgroup reference - ''' + """ props = salt.utils.vmware.get_properties_of_managed_object( - pg_ref, ['name', 'config.description', 'config.numPorts', - 'config.type', 'config.defaultPortConfig']) - pg_dict = {'name': props['name'], - 'description': props.get('config.description'), - 'num_ports': props['config.numPorts'], - 'type': props['config.type']} - if props['config.defaultPortConfig']: - dpg = props['config.defaultPortConfig'] - if dpg.vlan and \ - isinstance(dpg.vlan, - vim.VmwareDistributedVirtualSwitchVlanIdSpec): + pg_ref, + [ + "name", + "config.description", + "config.numPorts", + "config.type", + "config.defaultPortConfig", + ], + ) + pg_dict = { + "name": props["name"], + "description": props.get("config.description"), + "num_ports": props["config.numPorts"], + "type": props["config.type"], + } + if props["config.defaultPortConfig"]: + dpg = props["config.defaultPortConfig"] + if dpg.vlan and isinstance( + dpg.vlan, vim.VmwareDistributedVirtualSwitchVlanIdSpec + ): - pg_dict.update({'vlan_id': dpg.vlan.vlanId}) - pg_dict.update({'out_shaping': - _get_dvportgroup_out_shaping( - props['name'], - props['config.defaultPortConfig'])}) - pg_dict.update({'security_policy': - _get_dvportgroup_security_policy( - props['name'], - props['config.defaultPortConfig'])}) - pg_dict.update({'teaming': - _get_dvportgroup_teaming( - props['name'], - props['config.defaultPortConfig'])}) + pg_dict.update({"vlan_id": dpg.vlan.vlanId}) + pg_dict.update( + { + "out_shaping": _get_dvportgroup_out_shaping( + props["name"], props["config.defaultPortConfig"] + ) + } + ) + pg_dict.update( + { + "security_policy": _get_dvportgroup_security_policy( + props["name"], props["config.defaultPortConfig"] + ) + } + ) + pg_dict.update( + { + "teaming": _get_dvportgroup_teaming( + props["name"], props["config.defaultPortConfig"] + ) + } + ) return pg_dict @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'esxcluster') +@supports_proxies("esxdatacenter", "esxcluster") @gets_service_instance_via_proxy def list_dvportgroups(dvs=None, portgroup_names=None, service_instance=None): - ''' + """ Returns a list of distributed virtual switch portgroups. The list can be filtered by the portgroup names or by the DVS. @@ -4273,36 +4722,38 @@ def list_dvportgroups(dvs=None, portgroup_names=None, service_instance=None): salt '*' vsphere.list_dvportgroups portgroup_names=[pg1] salt '*' vsphere.list_dvportgroups dvs=dvs1 portgroup_names=[pg1] - ''' + """ ret_dict = [] proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) if dvs: dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs]) if not dvs_refs: - raise VMwareObjectRetrievalError('DVS \'{0}\' was not ' - 'retrieved'.format(dvs)) + raise VMwareObjectRetrievalError( + "DVS '{0}' was not " "retrieved".format(dvs) + ) dvs_ref = dvs_refs[0] get_all_portgroups = True if not portgroup_names else False for pg_ref in salt.utils.vmware.get_dvportgroups( parent_ref=dvs_ref if dvs else dc_ref, portgroup_names=portgroup_names, - get_all_portgroups=get_all_portgroups): + get_all_portgroups=get_all_portgroups, + ): ret_dict.append(_get_dvportgroup_dict(pg_ref)) return ret_dict @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'esxcluster') +@supports_proxies("esxdatacenter", "esxcluster") @gets_service_instance_via_proxy def list_uplink_dvportgroup(dvs, service_instance=None): - ''' + """ Returns the uplink portgroup of a distributed virtual switch. dvs @@ -4315,24 +4766,23 @@ def list_uplink_dvportgroup(dvs, service_instance=None): .. code-block:: bash salt '*' vsphere.list_uplink_dvportgroup dvs=dvs_name - ''' + """ proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs]) if not dvs_refs: - raise VMwareObjectRetrievalError('DVS \'{0}\' was not ' - 'retrieved'.format(dvs)) + raise VMwareObjectRetrievalError("DVS '{0}' was not " "retrieved".format(dvs)) uplink_pg_ref = salt.utils.vmware.get_uplink_dvportgroup(dvs_refs[0]) return _get_dvportgroup_dict(uplink_pg_ref) def _apply_dvportgroup_out_shaping(pg_name, out_shaping, out_shaping_conf): - ''' + """ Applies the values in out_shaping_conf to an out_shaping object pg_name @@ -4343,26 +4793,24 @@ def _apply_dvportgroup_out_shaping(pg_name, out_shaping, out_shaping_conf): out_shaping_conf The out shaping config - ''' - log.trace('Building portgroup\'s \'{0}\' out shaping ' - 'policy'.format(pg_name)) - if out_shaping_conf.get('average_bandwidth'): + """ + log.trace("Building portgroup's '{0}' out shaping " "policy".format(pg_name)) + if out_shaping_conf.get("average_bandwidth"): out_shaping.averageBandwidth = vim.LongPolicy() - out_shaping.averageBandwidth.value = \ - out_shaping_conf['average_bandwidth'] - if out_shaping_conf.get('burst_size'): + out_shaping.averageBandwidth.value = out_shaping_conf["average_bandwidth"] + if out_shaping_conf.get("burst_size"): out_shaping.burstSize = vim.LongPolicy() - out_shaping.burstSize.value = out_shaping_conf['burst_size'] - if 'enabled' in out_shaping_conf: + out_shaping.burstSize.value = out_shaping_conf["burst_size"] + if "enabled" in out_shaping_conf: out_shaping.enabled = vim.BoolPolicy() - out_shaping.enabled.value = out_shaping_conf['enabled'] - if out_shaping_conf.get('peak_bandwidth'): + out_shaping.enabled.value = out_shaping_conf["enabled"] + if out_shaping_conf.get("peak_bandwidth"): out_shaping.peakBandwidth = vim.LongPolicy() - out_shaping.peakBandwidth.value = out_shaping_conf['peak_bandwidth'] + out_shaping.peakBandwidth.value = out_shaping_conf["peak_bandwidth"] def _apply_dvportgroup_security_policy(pg_name, sec_policy, sec_policy_conf): - ''' + """ Applies the values in sec_policy_conf to a security policy object pg_name @@ -4373,22 +4821,21 @@ def _apply_dvportgroup_security_policy(pg_name, sec_policy, sec_policy_conf): sec_policy_conf The out shaping config - ''' - log.trace('Building portgroup\'s \'{0}\' security policy '.format(pg_name)) - if 'allow_promiscuous' in sec_policy_conf: + """ + log.trace("Building portgroup's '{0}' security policy ".format(pg_name)) + if "allow_promiscuous" in sec_policy_conf: sec_policy.allowPromiscuous = vim.BoolPolicy() - sec_policy.allowPromiscuous.value = \ - sec_policy_conf['allow_promiscuous'] - if 'forged_transmits' in sec_policy_conf: + sec_policy.allowPromiscuous.value = sec_policy_conf["allow_promiscuous"] + if "forged_transmits" in sec_policy_conf: sec_policy.forgedTransmits = vim.BoolPolicy() - sec_policy.forgedTransmits.value = sec_policy_conf['forged_transmits'] - if 'mac_changes' in sec_policy_conf: + sec_policy.forgedTransmits.value = sec_policy_conf["forged_transmits"] + if "mac_changes" in sec_policy_conf: sec_policy.macChanges = vim.BoolPolicy() - sec_policy.macChanges.value = sec_policy_conf['mac_changes'] + sec_policy.macChanges.value = sec_policy_conf["mac_changes"] def _apply_dvportgroup_teaming(pg_name, teaming, teaming_conf): - ''' + """ Applies the values in teaming_conf to a teaming policy object pg_name @@ -4399,65 +4846,72 @@ def _apply_dvportgroup_teaming(pg_name, teaming, teaming_conf): teaming_conf The teaming config - ''' - log.trace('Building portgroup\'s \'{0}\' teaming'.format(pg_name)) - if 'notify_switches' in teaming_conf: + """ + log.trace("Building portgroup's '{0}' teaming".format(pg_name)) + if "notify_switches" in teaming_conf: teaming.notifySwitches = vim.BoolPolicy() - teaming.notifySwitches.value = teaming_conf['notify_switches'] - if 'policy' in teaming_conf: + teaming.notifySwitches.value = teaming_conf["notify_switches"] + if "policy" in teaming_conf: teaming.policy = vim.StringPolicy() - teaming.policy.value = teaming_conf['policy'] - if 'reverse_policy' in teaming_conf: + teaming.policy.value = teaming_conf["policy"] + if "reverse_policy" in teaming_conf: teaming.reversePolicy = vim.BoolPolicy() - teaming.reversePolicy.value = teaming_conf['reverse_policy'] - if 'rolling_order' in teaming_conf: + teaming.reversePolicy.value = teaming_conf["reverse_policy"] + if "rolling_order" in teaming_conf: teaming.rollingOrder = vim.BoolPolicy() - teaming.rollingOrder.value = teaming_conf['rolling_order'] - if 'failure_criteria' in teaming_conf: + teaming.rollingOrder.value = teaming_conf["rolling_order"] + if "failure_criteria" in teaming_conf: if not teaming.failureCriteria: teaming.failureCriteria = vim.DVSFailureCriteria() - failure_criteria_conf = teaming_conf['failure_criteria'] - if 'check_beacon' in failure_criteria_conf: + failure_criteria_conf = teaming_conf["failure_criteria"] + if "check_beacon" in failure_criteria_conf: teaming.failureCriteria.checkBeacon = vim.BoolPolicy() - teaming.failureCriteria.checkBeacon.value = \ - failure_criteria_conf['check_beacon'] - if 'check_duplex' in failure_criteria_conf: + teaming.failureCriteria.checkBeacon.value = failure_criteria_conf[ + "check_beacon" + ] + if "check_duplex" in failure_criteria_conf: teaming.failureCriteria.checkDuplex = vim.BoolPolicy() - teaming.failureCriteria.checkDuplex.value = \ - failure_criteria_conf['check_duplex'] - if 'check_error_percent' in failure_criteria_conf: + teaming.failureCriteria.checkDuplex.value = failure_criteria_conf[ + "check_duplex" + ] + if "check_error_percent" in failure_criteria_conf: teaming.failureCriteria.checkErrorPercent = vim.BoolPolicy() - teaming.failureCriteria.checkErrorPercent.value = \ - failure_criteria_conf['check_error_percent'] - if 'check_speed' in failure_criteria_conf: + teaming.failureCriteria.checkErrorPercent.value = failure_criteria_conf[ + "check_error_percent" + ] + if "check_speed" in failure_criteria_conf: teaming.failureCriteria.checkSpeed = vim.StringPolicy() - teaming.failureCriteria.checkSpeed.value = \ - failure_criteria_conf['check_speed'] - if 'full_duplex' in failure_criteria_conf: + teaming.failureCriteria.checkSpeed.value = failure_criteria_conf[ + "check_speed" + ] + if "full_duplex" in failure_criteria_conf: teaming.failureCriteria.fullDuplex = vim.BoolPolicy() - teaming.failureCriteria.fullDuplex.value = \ - failure_criteria_conf['full_duplex'] - if 'percentage' in failure_criteria_conf: + teaming.failureCriteria.fullDuplex.value = failure_criteria_conf[ + "full_duplex" + ] + if "percentage" in failure_criteria_conf: teaming.failureCriteria.percentage = vim.IntPolicy() - teaming.failureCriteria.percentage.value = \ - failure_criteria_conf['percentage'] - if 'speed' in failure_criteria_conf: + teaming.failureCriteria.percentage.value = failure_criteria_conf[ + "percentage" + ] + if "speed" in failure_criteria_conf: teaming.failureCriteria.speed = vim.IntPolicy() - teaming.failureCriteria.speed.value = \ - failure_criteria_conf['speed'] - if 'port_order' in teaming_conf: + teaming.failureCriteria.speed.value = failure_criteria_conf["speed"] + if "port_order" in teaming_conf: if not teaming.uplinkPortOrder: teaming.uplinkPortOrder = vim.VMwareUplinkPortOrderPolicy() - if 'active' in teaming_conf['port_order']: - teaming.uplinkPortOrder.activeUplinkPort = \ - teaming_conf['port_order']['active'] - if 'standby' in teaming_conf['port_order']: - teaming.uplinkPortOrder.standbyUplinkPort = \ - teaming_conf['port_order']['standby'] + if "active" in teaming_conf["port_order"]: + teaming.uplinkPortOrder.activeUplinkPort = teaming_conf["port_order"][ + "active" + ] + if "standby" in teaming_conf["port_order"]: + teaming.uplinkPortOrder.standbyUplinkPort = teaming_conf["port_order"][ + "standby" + ] def _apply_dvportgroup_config(pg_name, pg_spec, pg_conf): - ''' + """ Applies the values in conf to a distributed portgroup spec pg_name @@ -4468,54 +4922,53 @@ def _apply_dvportgroup_config(pg_name, pg_spec, pg_conf): pg_conf The portgroup config - ''' - log.trace('Building portgroup\'s \'{0}\' spec'.format(pg_name)) - if 'name' in pg_conf: - pg_spec.name = pg_conf['name'] - if 'description' in pg_conf: - pg_spec.description = pg_conf['description'] - if 'num_ports' in pg_conf: - pg_spec.numPorts = pg_conf['num_ports'] - if 'type' in pg_conf: - pg_spec.type = pg_conf['type'] + """ + log.trace("Building portgroup's '{0}' spec".format(pg_name)) + if "name" in pg_conf: + pg_spec.name = pg_conf["name"] + if "description" in pg_conf: + pg_spec.description = pg_conf["description"] + if "num_ports" in pg_conf: + pg_spec.numPorts = pg_conf["num_ports"] + if "type" in pg_conf: + pg_spec.type = pg_conf["type"] if not pg_spec.defaultPortConfig: - for prop in ['vlan_id', 'out_shaping', 'security_policy', 'teaming']: + for prop in ["vlan_id", "out_shaping", "security_policy", "teaming"]: if prop in pg_conf: pg_spec.defaultPortConfig = vim.VMwareDVSPortSetting() - if 'vlan_id' in pg_conf: - pg_spec.defaultPortConfig.vlan = \ - vim.VmwareDistributedVirtualSwitchVlanIdSpec() - pg_spec.defaultPortConfig.vlan.vlanId = pg_conf['vlan_id'] - if 'out_shaping' in pg_conf: + if "vlan_id" in pg_conf: + pg_spec.defaultPortConfig.vlan = vim.VmwareDistributedVirtualSwitchVlanIdSpec() + pg_spec.defaultPortConfig.vlan.vlanId = pg_conf["vlan_id"] + if "out_shaping" in pg_conf: if not pg_spec.defaultPortConfig.outShapingPolicy: - pg_spec.defaultPortConfig.outShapingPolicy = \ - vim.DVSTrafficShapingPolicy() + pg_spec.defaultPortConfig.outShapingPolicy = vim.DVSTrafficShapingPolicy() _apply_dvportgroup_out_shaping( - pg_name, pg_spec.defaultPortConfig.outShapingPolicy, - pg_conf['out_shaping']) - if 'security_policy' in pg_conf: + pg_name, pg_spec.defaultPortConfig.outShapingPolicy, pg_conf["out_shaping"] + ) + if "security_policy" in pg_conf: if not pg_spec.defaultPortConfig.securityPolicy: - pg_spec.defaultPortConfig.securityPolicy = \ - vim.DVSSecurityPolicy() + pg_spec.defaultPortConfig.securityPolicy = vim.DVSSecurityPolicy() _apply_dvportgroup_security_policy( - pg_name, pg_spec.defaultPortConfig.securityPolicy, - pg_conf['security_policy']) - if 'teaming' in pg_conf: + pg_name, + pg_spec.defaultPortConfig.securityPolicy, + pg_conf["security_policy"], + ) + if "teaming" in pg_conf: if not pg_spec.defaultPortConfig.uplinkTeamingPolicy: - pg_spec.defaultPortConfig.uplinkTeamingPolicy = \ - vim.VmwareUplinkPortTeamingPolicy() + pg_spec.defaultPortConfig.uplinkTeamingPolicy = ( + vim.VmwareUplinkPortTeamingPolicy() + ) _apply_dvportgroup_teaming( - pg_name, pg_spec.defaultPortConfig.uplinkTeamingPolicy, - pg_conf['teaming']) + pg_name, pg_spec.defaultPortConfig.uplinkTeamingPolicy, pg_conf["teaming"] + ) @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'esxcluster') +@supports_proxies("esxdatacenter", "esxcluster") @gets_service_instance_via_proxy -def create_dvportgroup(portgroup_dict, portgroup_name, dvs, - service_instance=None): - ''' +def create_dvportgroup(portgroup_dict, portgroup_name, dvs, service_instance=None): + """ Creates a distributed virtual portgroup. Note: The ``portgroup_name`` param will override any name already set @@ -4539,22 +4992,23 @@ def create_dvportgroup(portgroup_dict, portgroup_name, dvs, salt '*' vsphere.create_dvportgroup portgroup_dict=<dict> portgroup_name=pg1 dvs=dvs1 - ''' - log.trace('Creating portgroup\'{0}\' in dvs \'{1}\' ' - 'with dict = {2}'.format(portgroup_name, dvs, portgroup_dict)) + """ + log.trace( + "Creating portgroup'{0}' in dvs '{1}' " + "with dict = {2}".format(portgroup_name, dvs, portgroup_dict) + ) proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs]) if not dvs_refs: - raise VMwareObjectRetrievalError('DVS \'{0}\' was not ' - 'retrieved'.format(dvs)) + raise VMwareObjectRetrievalError("DVS '{0}' was not " "retrieved".format(dvs)) # Make the name of the dvportgroup consistent with the parameter - portgroup_dict['name'] = portgroup_name + portgroup_dict["name"] = portgroup_name spec = vim.DVPortgroupConfigSpec() _apply_dvportgroup_config(portgroup_name, spec, portgroup_dict) salt.utils.vmware.create_dvportgroup(dvs_refs[0], spec) @@ -4562,10 +5016,10 @@ def create_dvportgroup(portgroup_dict, portgroup_name, dvs, @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'esxcluster') +@supports_proxies("esxdatacenter", "esxcluster") @gets_service_instance_via_proxy def update_dvportgroup(portgroup_dict, portgroup, dvs, service_instance=True): - ''' + """ Updates a distributed virtual portgroup. portgroup_dict @@ -4589,43 +5043,57 @@ def update_dvportgroup(portgroup_dict, portgroup, dvs, service_instance=True): salt '*' vsphere.update_dvportgroup portgroup_dict=<dict> portgroup=pg1 dvs=dvs1 - ''' - log.trace('Updating portgroup\'{0}\' in dvs \'{1}\' ' - 'with dict = {2}'.format(portgroup, dvs, portgroup_dict)) + """ + log.trace( + "Updating portgroup'{0}' in dvs '{1}' " + "with dict = {2}".format(portgroup, dvs, portgroup_dict) + ) proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs]) if not dvs_refs: - raise VMwareObjectRetrievalError('DVS \'{0}\' was not ' - 'retrieved'.format(dvs)) - pg_refs = salt.utils.vmware.get_dvportgroups(dvs_refs[0], - portgroup_names=[portgroup]) + raise VMwareObjectRetrievalError("DVS '{0}' was not " "retrieved".format(dvs)) + pg_refs = salt.utils.vmware.get_dvportgroups( + dvs_refs[0], portgroup_names=[portgroup] + ) if not pg_refs: - raise VMwareObjectRetrievalError('Portgroup \'{0}\' was not ' - 'retrieved'.format(portgroup)) - pg_props = salt.utils.vmware.get_properties_of_managed_object(pg_refs[0], - ['config']) + raise VMwareObjectRetrievalError( + "Portgroup '{0}' was not " "retrieved".format(portgroup) + ) + pg_props = salt.utils.vmware.get_properties_of_managed_object( + pg_refs[0], ["config"] + ) spec = vim.DVPortgroupConfigSpec() # Copy existing properties in spec - for prop in ['autoExpand', 'configVersion', 'defaultPortConfig', - 'description', 'name', 'numPorts', 'policy', 'portNameFormat', - 'scope', 'type', 'vendorSpecificConfig']: - setattr(spec, prop, getattr(pg_props['config'], prop)) + for prop in [ + "autoExpand", + "configVersion", + "defaultPortConfig", + "description", + "name", + "numPorts", + "policy", + "portNameFormat", + "scope", + "type", + "vendorSpecificConfig", + ]: + setattr(spec, prop, getattr(pg_props["config"], prop)) _apply_dvportgroup_config(portgroup, spec, portgroup_dict) salt.utils.vmware.update_dvportgroup(pg_refs[0], spec) return True @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'esxcluster') +@supports_proxies("esxdatacenter", "esxcluster") @gets_service_instance_via_proxy def remove_dvportgroup(portgroup, dvs, service_instance=None): - ''' + """ Removes a distributed virtual portgroup. portgroup @@ -4641,71 +5109,70 @@ def remove_dvportgroup(portgroup, dvs, service_instance=None): .. code-block:: bash salt '*' vsphere.remove_dvportgroup portgroup=pg1 dvs=dvs1 - ''' - log.trace('Removing portgroup\'{0}\' in dvs \'{1}\' ' - ''.format(portgroup, dvs)) + """ + log.trace("Removing portgroup'{0}' in dvs '{1}' " "".format(portgroup, dvs)) proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) dvs_refs = salt.utils.vmware.get_dvss(dc_ref, dvs_names=[dvs]) if not dvs_refs: - raise VMwareObjectRetrievalError('DVS \'{0}\' was not ' - 'retrieved'.format(dvs)) - pg_refs = salt.utils.vmware.get_dvportgroups(dvs_refs[0], - portgroup_names=[portgroup]) + raise VMwareObjectRetrievalError("DVS '{0}' was not " "retrieved".format(dvs)) + pg_refs = salt.utils.vmware.get_dvportgroups( + dvs_refs[0], portgroup_names=[portgroup] + ) if not pg_refs: - raise VMwareObjectRetrievalError('Portgroup \'{0}\' was not ' - 'retrieved'.format(portgroup)) + raise VMwareObjectRetrievalError( + "Portgroup '{0}' was not " "retrieved".format(portgroup) + ) salt.utils.vmware.remove_dvportgroup(pg_refs[0]) return True def _get_policy_dict(policy): - '''Returns a dictionary representation of a policy''' - profile_dict = {'name': policy.name, - 'description': policy.description, - 'resource_type': policy.resourceType.resourceType} + """Returns a dictionary representation of a policy""" + profile_dict = { + "name": policy.name, + "description": policy.description, + "resource_type": policy.resourceType.resourceType, + } subprofile_dicts = [] - if isinstance(policy, pbm.profile.CapabilityBasedProfile) and \ - isinstance(policy.constraints, - pbm.profile.SubProfileCapabilityConstraints): + if isinstance(policy, pbm.profile.CapabilityBasedProfile) and isinstance( + policy.constraints, pbm.profile.SubProfileCapabilityConstraints + ): for subprofile in policy.constraints.subProfiles: - subprofile_dict = {'name': subprofile.name, - 'force_provision': subprofile.forceProvision} + subprofile_dict = { + "name": subprofile.name, + "force_provision": subprofile.forceProvision, + } cap_dicts = [] for cap in subprofile.capability: - cap_dict = {'namespace': cap.id.namespace, - 'id': cap.id.id} + cap_dict = {"namespace": cap.id.namespace, "id": cap.id.id} # We assume there is one constraint with one value set val = cap.constraint[0].propertyInstance[0].value if isinstance(val, pbm.capability.types.Range): - val_dict = {'type': 'range', - 'min': val.min, - 'max': val.max} + val_dict = {"type": "range", "min": val.min, "max": val.max} elif isinstance(val, pbm.capability.types.DiscreteSet): - val_dict = {'type': 'set', - 'values': val.values} + val_dict = {"type": "set", "values": val.values} else: - val_dict = {'type': 'scalar', - 'value': val} - cap_dict['setting'] = val_dict + val_dict = {"type": "scalar", "value": val} + cap_dict["setting"] = val_dict cap_dicts.append(cap_dict) - subprofile_dict['capabilities'] = cap_dicts + subprofile_dict["capabilities"] = cap_dicts subprofile_dicts.append(subprofile_dict) - profile_dict['subprofiles'] = subprofile_dicts + profile_dict["subprofiles"] = subprofile_dicts return profile_dict @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'vcenter') +@supports_proxies("esxdatacenter", "vcenter") @gets_service_instance_via_proxy def list_storage_policies(policy_names=None, service_instance=None): - ''' + """ Returns a list of storage policies. policy_names @@ -4721,22 +5188,22 @@ def list_storage_policies(policy_names=None, service_instance=None): salt '*' vsphere.list_storage_policies salt '*' vsphere.list_storage_policy policy_names=[policy_name] - ''' + """ profile_manager = salt.utils.pbm.get_profile_manager(service_instance) if not policy_names: - policies = salt.utils.pbm.get_storage_policies(profile_manager, - get_all_policies=True) + policies = salt.utils.pbm.get_storage_policies( + profile_manager, get_all_policies=True + ) else: - policies = salt.utils.pbm.get_storage_policies(profile_manager, - policy_names) + policies = salt.utils.pbm.get_storage_policies(profile_manager, policy_names) return [_get_policy_dict(p) for p in policies] @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'vcenter') +@supports_proxies("esxdatacenter", "vcenter") @gets_service_instance_via_proxy def list_default_vsan_policy(service_instance=None): - ''' + """ Returns the default vsan storage policy. service_instance @@ -4748,15 +5215,16 @@ def list_default_vsan_policy(service_instance=None): salt '*' vsphere.list_storage_policies salt '*' vsphere.list_storage_policy policy_names=[policy_name] - ''' + """ profile_manager = salt.utils.pbm.get_profile_manager(service_instance) - policies = salt.utils.pbm.get_storage_policies(profile_manager, - get_all_policies=True) - def_policies = [p for p in policies - if p.systemCreatedProfileType == 'VsanDefaultProfile'] + policies = salt.utils.pbm.get_storage_policies( + profile_manager, get_all_policies=True + ) + def_policies = [ + p for p in policies if p.systemCreatedProfileType == "VsanDefaultProfile" + ] if not def_policies: - raise VMwareObjectRetrievalError('Default VSAN policy was not ' - 'retrieved') + raise VMwareObjectRetrievalError("Default VSAN policy was not " "retrieved") return _get_policy_dict(def_policies[0]) @@ -4764,18 +5232,20 @@ def _get_capability_definition_dict(cap_metadata): # We assume each capability definition has one property with the same id # as the capability so we display its type as belonging to the capability # The object model permits multiple properties - return {'namespace': cap_metadata.id.namespace, - 'id': cap_metadata.id.id, - 'mandatory': cap_metadata.mandatory, - 'description': cap_metadata.summary.summary, - 'type': cap_metadata.propertyMetadata[0].type.typeName} + return { + "namespace": cap_metadata.id.namespace, + "id": cap_metadata.id.id, + "mandatory": cap_metadata.mandatory, + "description": cap_metadata.summary.summary, + "type": cap_metadata.propertyMetadata[0].type.typeName, + } @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'vcenter') +@supports_proxies("esxdatacenter", "vcenter") @gets_service_instance_via_proxy def list_capability_definitions(service_instance=None): - ''' + """ Returns a list of the metadata of all capabilities in the vCenter. service_instance @@ -4785,67 +5255,70 @@ def list_capability_definitions(service_instance=None): .. code-block:: bash salt '*' vsphere.list_capabilities - ''' + """ profile_manager = salt.utils.pbm.get_profile_manager(service_instance) - ret_list = [_get_capability_definition_dict(c) for c in - salt.utils.pbm.get_capability_definitions(profile_manager)] + ret_list = [ + _get_capability_definition_dict(c) + for c in salt.utils.pbm.get_capability_definitions(profile_manager) + ] return ret_list def _apply_policy_config(policy_spec, policy_dict): - '''Applies a policy dictionary to a policy spec''' - log.trace('policy_dict = {0}'.format(policy_dict)) - if policy_dict.get('name'): - policy_spec.name = policy_dict['name'] - if policy_dict.get('description'): - policy_spec.description = policy_dict['description'] - if policy_dict.get('subprofiles'): + """Applies a policy dictionary to a policy spec""" + log.trace("policy_dict = {0}".format(policy_dict)) + if policy_dict.get("name"): + policy_spec.name = policy_dict["name"] + if policy_dict.get("description"): + policy_spec.description = policy_dict["description"] + if policy_dict.get("subprofiles"): # Incremental changes to subprofiles and capabilities are not # supported because they would complicate updates too much # The whole configuration of all sub-profiles is expected and applied policy_spec.constraints = pbm.profile.SubProfileCapabilityConstraints() subprofiles = [] - for subprofile_dict in policy_dict['subprofiles']: - subprofile_spec = \ - pbm.profile.SubProfileCapabilityConstraints.SubProfile( - name=subprofile_dict['name']) + for subprofile_dict in policy_dict["subprofiles"]: + subprofile_spec = pbm.profile.SubProfileCapabilityConstraints.SubProfile( + name=subprofile_dict["name"] + ) cap_specs = [] - if subprofile_dict.get('force_provision'): - subprofile_spec.forceProvision = \ - subprofile_dict['force_provision'] - for cap_dict in subprofile_dict['capabilities']: - prop_inst_spec = pbm.capability.PropertyInstance( - id=cap_dict['id'] - ) - setting_type = cap_dict['setting']['type'] - if setting_type == 'set': + if subprofile_dict.get("force_provision"): + subprofile_spec.forceProvision = subprofile_dict["force_provision"] + for cap_dict in subprofile_dict["capabilities"]: + prop_inst_spec = pbm.capability.PropertyInstance(id=cap_dict["id"]) + setting_type = cap_dict["setting"]["type"] + if setting_type == "set": prop_inst_spec.value = pbm.capability.types.DiscreteSet() - prop_inst_spec.value.values = cap_dict['setting']['values'] - elif setting_type == 'range': + prop_inst_spec.value.values = cap_dict["setting"]["values"] + elif setting_type == "range": prop_inst_spec.value = pbm.capability.types.Range() - prop_inst_spec.value.max = cap_dict['setting']['max'] - prop_inst_spec.value.min = cap_dict['setting']['min'] - elif setting_type == 'scalar': - prop_inst_spec.value = cap_dict['setting']['value'] + prop_inst_spec.value.max = cap_dict["setting"]["max"] + prop_inst_spec.value.min = cap_dict["setting"]["min"] + elif setting_type == "scalar": + prop_inst_spec.value = cap_dict["setting"]["value"] cap_spec = pbm.capability.CapabilityInstance( id=pbm.capability.CapabilityMetadata.UniqueId( - id=cap_dict['id'], - namespace=cap_dict['namespace']), - constraint=[pbm.capability.ConstraintInstance( - propertyInstance=[prop_inst_spec])]) + id=cap_dict["id"], namespace=cap_dict["namespace"] + ), + constraint=[ + pbm.capability.ConstraintInstance( + propertyInstance=[prop_inst_spec] + ) + ], + ) cap_specs.append(cap_spec) subprofile_spec.capability = cap_specs subprofiles.append(subprofile_spec) policy_spec.constraints.subProfiles = subprofiles - log.trace('updated policy_spec = {0}'.format(policy_spec)) + log.trace("updated policy_spec = {0}".format(policy_spec)) return policy_spec @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'vcenter') +@supports_proxies("esxdatacenter", "vcenter") @gets_service_instance_via_proxy def create_storage_policy(policy_name, policy_dict, service_instance=None): - ''' + """ Creates a storage policy. Supported capability types: scalar, set, range. @@ -4867,27 +5340,29 @@ def create_storage_policy(policy_name, policy_dict, service_instance=None): salt '*' vsphere.create_storage_policy policy_name='policy name' policy_dict="$policy_dict" - ''' - log.trace('create storage policy \'{0}\', dict = {1}' - ''.format(policy_name, policy_dict)) + """ + log.trace( + "create storage policy '{0}', dict = {1}" "".format(policy_name, policy_dict) + ) profile_manager = salt.utils.pbm.get_profile_manager(service_instance) policy_create_spec = pbm.profile.CapabilityBasedProfileCreateSpec() # Hardcode the storage profile resource type policy_create_spec.resourceType = pbm.profile.ResourceType( - resourceType=pbm.profile.ResourceTypeEnum.STORAGE) + resourceType=pbm.profile.ResourceTypeEnum.STORAGE + ) # Set name argument - policy_dict['name'] = policy_name - log.trace('Setting policy values in policy_update_spec') + policy_dict["name"] = policy_name + log.trace("Setting policy values in policy_update_spec") _apply_policy_config(policy_create_spec, policy_dict) salt.utils.pbm.create_storage_policy(profile_manager, policy_create_spec) - return {'create_storage_policy': True} + return {"create_storage_policy": True} @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'vcenter') +@supports_proxies("esxdatacenter", "vcenter") @gets_service_instance_via_proxy def update_storage_policy(policy, policy_dict, service_instance=None): - ''' + """ Updates a storage policy. Supported capability types: scalar, set, range. @@ -4907,29 +5382,29 @@ def update_storage_policy(policy, policy_dict, service_instance=None): salt '*' vsphere.update_storage_policy policy='policy name' policy_dict="$policy_dict" - ''' - log.trace('updating storage policy, dict = {0}'.format(policy_dict)) + """ + log.trace("updating storage policy, dict = {0}".format(policy_dict)) profile_manager = salt.utils.pbm.get_profile_manager(service_instance) policies = salt.utils.pbm.get_storage_policies(profile_manager, [policy]) if not policies: - raise VMwareObjectRetrievalError('Policy \'{0}\' was not found' - ''.format(policy)) + raise VMwareObjectRetrievalError("Policy '{0}' was not found" "".format(policy)) policy_ref = policies[0] policy_update_spec = pbm.profile.CapabilityBasedProfileUpdateSpec() - log.trace('Setting policy values in policy_update_spec') - for prop in ['description', 'constraints']: + log.trace("Setting policy values in policy_update_spec") + for prop in ["description", "constraints"]: setattr(policy_update_spec, prop, getattr(policy_ref, prop)) _apply_policy_config(policy_update_spec, policy_dict) - salt.utils.pbm.update_storage_policy(profile_manager, policy_ref, - policy_update_spec) - return {'update_storage_policy': True} + salt.utils.pbm.update_storage_policy( + profile_manager, policy_ref, policy_update_spec + ) + return {"update_storage_policy": True} @depends(HAS_PYVMOMI) -@supports_proxies('esxcluster', 'esxdatacenter', 'vcenter') +@supports_proxies("esxcluster", "esxdatacenter", "vcenter") @gets_service_instance_via_proxy def list_default_storage_policy_of_datastore(datastore, service_instance=None): - ''' + """ Returns a list of datastores assign the the storage policies. datastore @@ -4944,28 +5419,33 @@ def list_default_storage_policy_of_datastore(datastore, service_instance=None): .. code-block:: bash salt '*' vsphere.list_default_storage_policy_of_datastore datastore=ds1 - ''' - log.trace('Listing the default storage policy of datastore \'{0}\'' - ''.format(datastore)) + """ + log.trace( + "Listing the default storage policy of datastore '{0}'" "".format(datastore) + ) # Find datastore target_ref = _get_proxy_target(service_instance) - ds_refs = salt.utils.vmware.get_datastores(service_instance, target_ref, - datastore_names=[datastore]) + ds_refs = salt.utils.vmware.get_datastores( + service_instance, target_ref, datastore_names=[datastore] + ) if not ds_refs: - raise VMwareObjectRetrievalError('Datastore \'{0}\' was not ' - 'found'.format(datastore)) + raise VMwareObjectRetrievalError( + "Datastore '{0}' was not " "found".format(datastore) + ) profile_manager = salt.utils.pbm.get_profile_manager(service_instance) policy = salt.utils.pbm.get_default_storage_policy_of_datastore( - profile_manager, ds_refs[0]) + profile_manager, ds_refs[0] + ) return _get_policy_dict(policy) @depends(HAS_PYVMOMI) -@supports_proxies('esxcluster', 'esxdatacenter', 'vcenter') +@supports_proxies("esxcluster", "esxdatacenter", "vcenter") @gets_service_instance_via_proxy -def assign_default_storage_policy_to_datastore(policy, datastore, - service_instance=None): - ''' +def assign_default_storage_policy_to_datastore( + policy, datastore, service_instance=None +): + """ Assigns a storage policy as the default policy to a datastore. policy @@ -4984,34 +5464,35 @@ def assign_default_storage_policy_to_datastore(policy, datastore, salt '*' vsphere.assign_storage_policy_to_datastore policy='policy name' datastore=ds1 - ''' - log.trace('Assigning policy {0} to datastore {1}' - ''.format(policy, datastore)) + """ + log.trace("Assigning policy {0} to datastore {1}" "".format(policy, datastore)) profile_manager = salt.utils.pbm.get_profile_manager(service_instance) # Find policy policies = salt.utils.pbm.get_storage_policies(profile_manager, [policy]) if not policies: - raise VMwareObjectRetrievalError('Policy \'{0}\' was not found' - ''.format(policy)) + raise VMwareObjectRetrievalError("Policy '{0}' was not found" "".format(policy)) policy_ref = policies[0] # Find datastore target_ref = _get_proxy_target(service_instance) - ds_refs = salt.utils.vmware.get_datastores(service_instance, target_ref, - datastore_names=[datastore]) + ds_refs = salt.utils.vmware.get_datastores( + service_instance, target_ref, datastore_names=[datastore] + ) if not ds_refs: - raise VMwareObjectRetrievalError('Datastore \'{0}\' was not ' - 'found'.format(datastore)) + raise VMwareObjectRetrievalError( + "Datastore '{0}' was not " "found".format(datastore) + ) ds_ref = ds_refs[0] salt.utils.pbm.assign_default_storage_policy_to_datastore( - profile_manager, policy_ref, ds_ref) + profile_manager, policy_ref, ds_ref + ) return True @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'esxcluster', 'vcenter', 'esxvm') +@supports_proxies("esxdatacenter", "esxcluster", "vcenter", "esxvm") @gets_service_instance_via_proxy def list_datacenters_via_proxy(datacenter_names=None, service_instance=None): - ''' + """ Returns a list of dict representations of VMware datacenters. Connection is done via the proxy details. @@ -5034,23 +5515,25 @@ def list_datacenters_via_proxy(datacenter_names=None, service_instance=None): salt '*' vsphere.list_datacenters_via_proxy dc1,dc2 salt '*' vsphere.list_datacenters_via_proxy datacenter_names=[dc1, dc2] - ''' + """ if not datacenter_names: - dc_refs = salt.utils.vmware.get_datacenters(service_instance, - get_all_datacenters=True) + dc_refs = salt.utils.vmware.get_datacenters( + service_instance, get_all_datacenters=True + ) else: - dc_refs = salt.utils.vmware.get_datacenters(service_instance, - datacenter_names) + dc_refs = salt.utils.vmware.get_datacenters(service_instance, datacenter_names) - return [{'name': salt.utils.vmware.get_managed_object_name(dc_ref)} - for dc_ref in dc_refs] + return [ + {"name": salt.utils.vmware.get_managed_object_name(dc_ref)} + for dc_ref in dc_refs + ] @depends(HAS_PYVMOMI) -@supports_proxies('esxdatacenter', 'vcenter') +@supports_proxies("esxdatacenter", "vcenter") @gets_service_instance_via_proxy def create_datacenter(datacenter_name, service_instance=None): - ''' + """ Creates a datacenter. Supported proxies: esxdatacenter @@ -5065,13 +5548,13 @@ def create_datacenter(datacenter_name, service_instance=None): .. code-block:: bash salt '*' vsphere.create_datacenter dc1 - ''' + """ salt.utils.vmware.create_datacenter(service_instance, datacenter_name) - return {'create_datacenter': True} + return {"create_datacenter": True} def _get_cluster_dict(cluster_name, cluster_ref): - ''' + """ Returns a cluster dict representation from a vim.ClusterComputeResource object. @@ -5080,49 +5563,54 @@ def _get_cluster_dict(cluster_name, cluster_ref): cluster_ref Reference to the cluster - ''' + """ - log.trace('Building a dictionary representation of cluster ' - '\'{0}\''.format(cluster_name)) + log.trace( + "Building a dictionary representation of cluster " "'{0}'".format(cluster_name) + ) props = salt.utils.vmware.get_properties_of_managed_object( - cluster_ref, - properties=['configurationEx']) - res = {'ha': {'enabled': props['configurationEx'].dasConfig.enabled}, - 'drs': {'enabled': props['configurationEx'].drsConfig.enabled}} + cluster_ref, properties=["configurationEx"] + ) + res = { + "ha": {"enabled": props["configurationEx"].dasConfig.enabled}, + "drs": {"enabled": props["configurationEx"].drsConfig.enabled}, + } # Convert HA properties of interest - ha_conf = props['configurationEx'].dasConfig - log.trace('ha_conf = {0}'.format(ha_conf)) - res['ha']['admission_control_enabled'] = ha_conf.admissionControlEnabled - if ha_conf.admissionControlPolicy and \ - isinstance(ha_conf.admissionControlPolicy, - vim.ClusterFailoverResourcesAdmissionControlPolicy): + ha_conf = props["configurationEx"].dasConfig + log.trace("ha_conf = {0}".format(ha_conf)) + res["ha"]["admission_control_enabled"] = ha_conf.admissionControlEnabled + if ha_conf.admissionControlPolicy and isinstance( + ha_conf.admissionControlPolicy, + vim.ClusterFailoverResourcesAdmissionControlPolicy, + ): pol = ha_conf.admissionControlPolicy - res['ha']['admission_control_policy'] = \ - {'cpu_failover_percent': pol.cpuFailoverResourcesPercent, - 'memory_failover_percent': pol.memoryFailoverResourcesPercent} + res["ha"]["admission_control_policy"] = { + "cpu_failover_percent": pol.cpuFailoverResourcesPercent, + "memory_failover_percent": pol.memoryFailoverResourcesPercent, + } if ha_conf.defaultVmSettings: def_vm_set = ha_conf.defaultVmSettings - res['ha']['default_vm_settings'] = \ - {'isolation_response': def_vm_set.isolationResponse, - 'restart_priority': def_vm_set.restartPriority} - res['ha']['hb_ds_candidate_policy'] = \ - ha_conf.hBDatastoreCandidatePolicy + res["ha"]["default_vm_settings"] = { + "isolation_response": def_vm_set.isolationResponse, + "restart_priority": def_vm_set.restartPriority, + } + res["ha"]["hb_ds_candidate_policy"] = ha_conf.hBDatastoreCandidatePolicy if ha_conf.hostMonitoring: - res['ha']['host_monitoring'] = ha_conf.hostMonitoring + res["ha"]["host_monitoring"] = ha_conf.hostMonitoring if ha_conf.option: - res['ha']['options'] = [{'key': o.key, 'value': o.value} - for o in ha_conf.option] - res['ha']['vm_monitoring'] = ha_conf.vmMonitoring + res["ha"]["options"] = [ + {"key": o.key, "value": o.value} for o in ha_conf.option + ] + res["ha"]["vm_monitoring"] = ha_conf.vmMonitoring # Convert DRS properties - drs_conf = props['configurationEx'].drsConfig - log.trace('drs_conf = {0}'.format(drs_conf)) - res['drs']['vmotion_rate'] = 6 - drs_conf.vmotionRate - res['drs']['default_vm_behavior'] = drs_conf.defaultVmBehavior + drs_conf = props["configurationEx"].drsConfig + log.trace("drs_conf = {0}".format(drs_conf)) + res["drs"]["vmotion_rate"] = 6 - drs_conf.vmotionRate + res["drs"]["default_vm_behavior"] = drs_conf.defaultVmBehavior # vm_swap_placement - res['vm_swap_placement'] = props['configurationEx'].vmSwapPlacement + res["vm_swap_placement"] = props["configurationEx"].vmSwapPlacement # Convert VSAN properties - si = salt.utils.vmware.get_service_instance_from_managed_object( - cluster_ref) + si = salt.utils.vmware.get_service_instance_from_managed_object(cluster_ref) if salt.utils.vsan.vsan_supported(si): # XXX The correct way of retrieving the VSAN data (on the if branch) @@ -5131,32 +5619,35 @@ def _get_cluster_dict(cluster_name, cluster_ref): if int(vcenter_info.build) >= 3634794: # 60u2 # VSAN API is fully supported by the VC starting with 60u2 vsan_conf = salt.utils.vsan.get_cluster_vsan_info(cluster_ref) - log.trace('vsan_conf = {0}'.format(vsan_conf)) - res['vsan'] = {'enabled': vsan_conf.enabled, - 'auto_claim_storage': - vsan_conf.defaultConfig.autoClaimStorage} + log.trace("vsan_conf = {0}".format(vsan_conf)) + res["vsan"] = { + "enabled": vsan_conf.enabled, + "auto_claim_storage": vsan_conf.defaultConfig.autoClaimStorage, + } if vsan_conf.dataEfficiencyConfig: data_eff = vsan_conf.dataEfficiencyConfig - res['vsan'].update({ - # We force compression_enabled to be True/False - 'compression_enabled': - data_eff.compressionEnabled or False, - 'dedup_enabled': data_eff.dedupEnabled}) + res["vsan"].update( + { + # We force compression_enabled to be True/False + "compression_enabled": data_eff.compressionEnabled or False, + "dedup_enabled": data_eff.dedupEnabled, + } + ) else: # before 60u2 (no advanced vsan info) - if props['configurationEx'].vsanConfigInfo: - default_config = \ - props['configurationEx'].vsanConfigInfo.defaultConfig - res['vsan'] = { - 'enabled': props['configurationEx'].vsanConfigInfo.enabled, - 'auto_claim_storage': default_config.autoClaimStorage} + if props["configurationEx"].vsanConfigInfo: + default_config = props["configurationEx"].vsanConfigInfo.defaultConfig + res["vsan"] = { + "enabled": props["configurationEx"].vsanConfigInfo.enabled, + "auto_claim_storage": default_config.autoClaimStorage, + } return res @depends(HAS_PYVMOMI) -@supports_proxies('esxcluster', 'esxdatacenter') +@supports_proxies("esxcluster", "esxdatacenter") @gets_service_instance_via_proxy def list_cluster(datacenter=None, cluster=None, service_instance=None): - ''' + """ Returns a dict representation of an ESX cluster. datacenter @@ -5183,24 +5674,25 @@ def list_cluster(datacenter=None, cluster=None, service_instance=None): # esxcluster proxy salt '*' vsphere.list_cluster - ''' + """ proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': + if proxy_type == "esxdatacenter": dc_ref = _get_proxy_target(service_instance) if not cluster: - raise ArgumentValueError('\'cluster\' needs to be specified') + raise ArgumentValueError("'cluster' needs to be specified") cluster_ref = salt.utils.vmware.get_cluster(dc_ref, cluster) - elif proxy_type == 'esxcluster': + elif proxy_type == "esxcluster": cluster_ref = _get_proxy_target(service_instance) - cluster = __salt__['esxcluster.get_details']()['cluster'] - log.trace('Retrieving representation of cluster \'{0}\' in a ' - '{1} proxy'.format(cluster, proxy_type)) + cluster = __salt__["esxcluster.get_details"]()["cluster"] + log.trace( + "Retrieving representation of cluster '{0}' in a " + "{1} proxy".format(cluster, proxy_type) + ) return _get_cluster_dict(cluster, cluster_ref) -def _apply_cluster_dict(cluster_spec, cluster_dict, vsan_spec=None, - vsan_61=True): - ''' +def _apply_cluster_dict(cluster_spec, cluster_dict, vsan_spec=None, vsan_61=True): + """ Applies the values of cluster_dict dictionary to a cluster spec (vim.ClusterConfigSpecEx). @@ -5210,132 +5702,126 @@ def _apply_cluster_dict(cluster_spec, cluster_dict, vsan_spec=None, VSAN 6.1 config needs to be applied differently than the post VSAN 6.1 way. The type of configuration desired is dictated by the flag vsan_61. - ''' - log.trace('Applying cluster dict {0}'.format(cluster_dict)) - if cluster_dict.get('ha'): - ha_dict = cluster_dict['ha'] + """ + log.trace("Applying cluster dict {0}".format(cluster_dict)) + if cluster_dict.get("ha"): + ha_dict = cluster_dict["ha"] if not cluster_spec.dasConfig: cluster_spec.dasConfig = vim.ClusterDasConfigInfo() das_config = cluster_spec.dasConfig - if 'enabled' in ha_dict: - das_config.enabled = ha_dict['enabled'] - if ha_dict['enabled']: + if "enabled" in ha_dict: + das_config.enabled = ha_dict["enabled"] + if ha_dict["enabled"]: # Default values when ha is enabled das_config.failoverLevel = 1 - if 'admission_control_enabled' in ha_dict: - das_config.admissionControlEnabled = \ - ha_dict['admission_control_enabled'] - if 'admission_control_policy' in ha_dict: - adm_pol_dict = ha_dict['admission_control_policy'] - if not das_config.admissionControlPolicy or \ - not isinstance( - das_config.admissionControlPolicy, - vim.ClusterFailoverResourcesAdmissionControlPolicy): + if "admission_control_enabled" in ha_dict: + das_config.admissionControlEnabled = ha_dict["admission_control_enabled"] + if "admission_control_policy" in ha_dict: + adm_pol_dict = ha_dict["admission_control_policy"] + if not das_config.admissionControlPolicy or not isinstance( + das_config.admissionControlPolicy, + vim.ClusterFailoverResourcesAdmissionControlPolicy, + ): - das_config.admissionControlPolicy = \ - vim.ClusterFailoverResourcesAdmissionControlPolicy( - cpuFailoverResourcesPercent= - adm_pol_dict['cpu_failover_percent'], - memoryFailoverResourcesPercent= - adm_pol_dict['memory_failover_percent']) - if 'default_vm_settings' in ha_dict: - vm_set_dict = ha_dict['default_vm_settings'] + das_config.admissionControlPolicy = vim.ClusterFailoverResourcesAdmissionControlPolicy( + cpuFailoverResourcesPercent=adm_pol_dict["cpu_failover_percent"], + memoryFailoverResourcesPercent=adm_pol_dict[ + "memory_failover_percent" + ], + ) + if "default_vm_settings" in ha_dict: + vm_set_dict = ha_dict["default_vm_settings"] if not das_config.defaultVmSettings: das_config.defaultVmSettings = vim.ClusterDasVmSettings() - if 'isolation_response' in vm_set_dict: - das_config.defaultVmSettings.isolationResponse = \ - vm_set_dict['isolation_response'] - if 'restart_priority' in vm_set_dict: - das_config.defaultVmSettings.restartPriority = \ - vm_set_dict['restart_priority'] - if 'hb_ds_candidate_policy' in ha_dict: - das_config.hBDatastoreCandidatePolicy = \ - ha_dict['hb_ds_candidate_policy'] - if 'host_monitoring' in ha_dict: - das_config.hostMonitoring = ha_dict['host_monitoring'] - if 'options' in ha_dict: + if "isolation_response" in vm_set_dict: + das_config.defaultVmSettings.isolationResponse = vm_set_dict[ + "isolation_response" + ] + if "restart_priority" in vm_set_dict: + das_config.defaultVmSettings.restartPriority = vm_set_dict[ + "restart_priority" + ] + if "hb_ds_candidate_policy" in ha_dict: + das_config.hBDatastoreCandidatePolicy = ha_dict["hb_ds_candidate_policy"] + if "host_monitoring" in ha_dict: + das_config.hostMonitoring = ha_dict["host_monitoring"] + if "options" in ha_dict: das_config.option = [] - for opt_dict in ha_dict['options']: - das_config.option.append( - vim.OptionValue(key=opt_dict['key'])) - if 'value' in opt_dict: - das_config.option[-1].value = opt_dict['value'] - if 'vm_monitoring' in ha_dict: - das_config.vmMonitoring = ha_dict['vm_monitoring'] + for opt_dict in ha_dict["options"]: + das_config.option.append(vim.OptionValue(key=opt_dict["key"])) + if "value" in opt_dict: + das_config.option[-1].value = opt_dict["value"] + if "vm_monitoring" in ha_dict: + das_config.vmMonitoring = ha_dict["vm_monitoring"] cluster_spec.dasConfig = das_config - if cluster_dict.get('drs'): - drs_dict = cluster_dict['drs'] + if cluster_dict.get("drs"): + drs_dict = cluster_dict["drs"] drs_config = vim.ClusterDrsConfigInfo() - if 'enabled' in drs_dict: - drs_config.enabled = drs_dict['enabled'] - if 'vmotion_rate' in drs_dict: - drs_config.vmotionRate = 6 - drs_dict['vmotion_rate'] - if 'default_vm_behavior' in drs_dict: - drs_config.defaultVmBehavior = \ - vim.DrsBehavior(drs_dict['default_vm_behavior']) + if "enabled" in drs_dict: + drs_config.enabled = drs_dict["enabled"] + if "vmotion_rate" in drs_dict: + drs_config.vmotionRate = 6 - drs_dict["vmotion_rate"] + if "default_vm_behavior" in drs_dict: + drs_config.defaultVmBehavior = vim.DrsBehavior( + drs_dict["default_vm_behavior"] + ) cluster_spec.drsConfig = drs_config - if cluster_dict.get('vm_swap_placement'): - cluster_spec.vmSwapPlacement = cluster_dict['vm_swap_placement'] - if cluster_dict.get('vsan'): - vsan_dict = cluster_dict['vsan'] + if cluster_dict.get("vm_swap_placement"): + cluster_spec.vmSwapPlacement = cluster_dict["vm_swap_placement"] + if cluster_dict.get("vsan"): + vsan_dict = cluster_dict["vsan"] if not vsan_61: # VSAN is 6.2 and above - if 'enabled' in vsan_dict: + if "enabled" in vsan_dict: if not vsan_spec.vsanClusterConfig: - vsan_spec.vsanClusterConfig = \ - vim.vsan.cluster.ConfigInfo() - vsan_spec.vsanClusterConfig.enabled = vsan_dict['enabled'] - if 'auto_claim_storage' in vsan_dict: + vsan_spec.vsanClusterConfig = vim.vsan.cluster.ConfigInfo() + vsan_spec.vsanClusterConfig.enabled = vsan_dict["enabled"] + if "auto_claim_storage" in vsan_dict: if not vsan_spec.vsanClusterConfig: - vsan_spec.vsanClusterConfig = \ - vim.vsan.cluster.ConfigInfo() + vsan_spec.vsanClusterConfig = vim.vsan.cluster.ConfigInfo() if not vsan_spec.vsanClusterConfig.defaultConfig: - vsan_spec.vsanClusterConfig.defaultConfig = \ - vim.VsanClusterConfigInfoHostDefaultInfo() + vsan_spec.vsanClusterConfig.defaultConfig = ( + vim.VsanClusterConfigInfoHostDefaultInfo() + ) elif vsan_spec.vsanClusterConfig.defaultConfig.uuid: # If this remains set it caused an error vsan_spec.vsanClusterConfig.defaultConfig.uuid = None - vsan_spec.vsanClusterConfig.defaultConfig.autoClaimStorage = \ - vsan_dict['auto_claim_storage'] - if 'compression_enabled' in vsan_dict: + vsan_spec.vsanClusterConfig.defaultConfig.autoClaimStorage = vsan_dict[ + "auto_claim_storage" + ] + if "compression_enabled" in vsan_dict: if not vsan_spec.dataEfficiencyConfig: - vsan_spec.dataEfficiencyConfig = \ - vim.vsan.DataEfficiencyConfig() - vsan_spec.dataEfficiencyConfig.compressionEnabled = \ - vsan_dict['compression_enabled'] - if 'dedup_enabled' in vsan_dict: + vsan_spec.dataEfficiencyConfig = vim.vsan.DataEfficiencyConfig() + vsan_spec.dataEfficiencyConfig.compressionEnabled = vsan_dict[ + "compression_enabled" + ] + if "dedup_enabled" in vsan_dict: if not vsan_spec.dataEfficiencyConfig: - vsan_spec.dataEfficiencyConfig = \ - vim.vsan.DataEfficiencyConfig() - vsan_spec.dataEfficiencyConfig.dedupEnabled = \ - vsan_dict['dedup_enabled'] + vsan_spec.dataEfficiencyConfig = vim.vsan.DataEfficiencyConfig() + vsan_spec.dataEfficiencyConfig.dedupEnabled = vsan_dict["dedup_enabled"] # In all cases we need to configure the vsan on the cluster # directly so not to have a missmatch between vsan_spec and # cluster_spec if not cluster_spec.vsanConfig: - cluster_spec.vsanConfig = \ - vim.VsanClusterConfigInfo() + cluster_spec.vsanConfig = vim.VsanClusterConfigInfo() vsan_config = cluster_spec.vsanConfig - if 'enabled' in vsan_dict: - vsan_config.enabled = vsan_dict['enabled'] - if 'auto_claim_storage' in vsan_dict: + if "enabled" in vsan_dict: + vsan_config.enabled = vsan_dict["enabled"] + if "auto_claim_storage" in vsan_dict: if not vsan_config.defaultConfig: - vsan_config.defaultConfig = \ - vim.VsanClusterConfigInfoHostDefaultInfo() + vsan_config.defaultConfig = vim.VsanClusterConfigInfoHostDefaultInfo() elif vsan_config.defaultConfig.uuid: # If this remains set it caused an error vsan_config.defaultConfig.uuid = None - vsan_config.defaultConfig.autoClaimStorage = \ - vsan_dict['auto_claim_storage'] - log.trace('cluster_spec = {0}'.format(cluster_spec)) + vsan_config.defaultConfig.autoClaimStorage = vsan_dict["auto_claim_storage"] + log.trace("cluster_spec = {0}".format(cluster_spec)) @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxcluster', 'esxdatacenter') +@supports_proxies("esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def create_cluster(cluster_dict, datacenter=None, cluster=None, - service_instance=None): - ''' +def create_cluster(cluster_dict, datacenter=None, cluster=None, service_instance=None): + """ Creates a cluster. Note: cluster_dict['name'] will be overridden by the cluster param value @@ -5364,7 +5850,7 @@ def create_cluster(cluster_dict, datacenter=None, cluster=None, # esxcluster proxy salt '*' vsphere.create_cluster cluster_dict=$cluster_dict - ''' + """ # Validate cluster dictionary schema = ESXClusterConfigSchema.serialize() try: @@ -5373,38 +5859,40 @@ def create_cluster(cluster_dict, datacenter=None, cluster=None, raise InvalidConfigError(exc) # Get required details from the proxy proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) if not cluster: - raise ArgumentValueError('\'cluster\' needs to be specified') - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + raise ArgumentValueError("'cluster' needs to be specified") + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) - cluster = __salt__['esxcluster.get_details']()['cluster'] + cluster = __salt__["esxcluster.get_details"]()["cluster"] - if cluster_dict.get('vsan') and not \ - salt.utils.vsan.vsan_supported(service_instance): + if cluster_dict.get("vsan") and not salt.utils.vsan.vsan_supported( + service_instance + ): - raise VMwareApiError('VSAN operations are not supported') + raise VMwareApiError("VSAN operations are not supported") si = service_instance cluster_spec = vim.ClusterConfigSpecEx() vsan_spec = None ha_config = None vsan_61 = None - if cluster_dict.get('vsan'): + if cluster_dict.get("vsan"): # XXX The correct way of retrieving the VSAN data (on the if branch) # is not supported before 60u2 vcenter vcenter_info = salt.utils.vmware.get_service_info(si) - if float(vcenter_info.apiVersion) >= 6.0 and \ - int(vcenter_info.build) >= 3634794: # 60u2 + if ( + float(vcenter_info.apiVersion) >= 6.0 and int(vcenter_info.build) >= 3634794 + ): # 60u2 vsan_spec = vim.vsan.ReconfigSpec(modify=True) vsan_61 = False # We need to keep HA disabled and enable it afterwards - if cluster_dict.get('ha', {}).get('enabled'): + if cluster_dict.get("ha", {}).get("enabled"): enable_ha = True - ha_config = cluster_dict['ha'] - del cluster_dict['ha'] + ha_config = cluster_dict["ha"] + del cluster_dict["ha"] else: vsan_61 = True # If VSAN is 6.1 the configuration of VSAN happens when configuring the @@ -5418,20 +5906,19 @@ def create_cluster(cluster_dict, datacenter=None, cluster=None, salt.utils.vsan.reconfigure_cluster_vsan(cluster_ref, vsan_spec) if enable_ha: # Set HA after VSAN has been configured - _apply_cluster_dict(cluster_spec, {'ha': ha_config}) + _apply_cluster_dict(cluster_spec, {"ha": ha_config}) salt.utils.vmware.update_cluster(cluster_ref, cluster_spec) # Set HA back on the object - cluster_dict['ha'] = ha_config - return {'create_cluster': True} + cluster_dict["ha"] = ha_config + return {"create_cluster": True} @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxcluster', 'esxdatacenter') +@supports_proxies("esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def update_cluster(cluster_dict, datacenter=None, cluster=None, - service_instance=None): - ''' +def update_cluster(cluster_dict, datacenter=None, cluster=None, service_instance=None): + """ Updates a cluster. config_dict @@ -5459,7 +5946,7 @@ def update_cluster(cluster_dict, datacenter=None, cluster=None, # esxcluster proxy salt '*' vsphere.update_cluster cluster_dict=$cluster_dict - ''' + """ # Validate cluster dictionary schema = ESXClusterConfigSchema.serialize() try: @@ -5468,38 +5955,41 @@ def update_cluster(cluster_dict, datacenter=None, cluster=None, raise InvalidConfigError(exc) # Get required details from the proxy proxy_type = get_proxy_type() - if proxy_type == 'esxdatacenter': - datacenter = __salt__['esxdatacenter.get_details']()['datacenter'] + if proxy_type == "esxdatacenter": + datacenter = __salt__["esxdatacenter.get_details"]()["datacenter"] dc_ref = _get_proxy_target(service_instance) if not cluster: - raise ArgumentValueError('\'cluster\' needs to be specified') - elif proxy_type == 'esxcluster': - datacenter = __salt__['esxcluster.get_details']()['datacenter'] + raise ArgumentValueError("'cluster' needs to be specified") + elif proxy_type == "esxcluster": + datacenter = __salt__["esxcluster.get_details"]()["datacenter"] dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) - cluster = __salt__['esxcluster.get_details']()['cluster'] + cluster = __salt__["esxcluster.get_details"]()["cluster"] - if cluster_dict.get('vsan') and not \ - salt.utils.vsan.vsan_supported(service_instance): + if cluster_dict.get("vsan") and not salt.utils.vsan.vsan_supported( + service_instance + ): - raise VMwareApiError('VSAN operations are not supported') + raise VMwareApiError("VSAN operations are not supported") cluster_ref = salt.utils.vmware.get_cluster(dc_ref, cluster) cluster_spec = vim.ClusterConfigSpecEx() props = salt.utils.vmware.get_properties_of_managed_object( - cluster_ref, properties=['configurationEx']) + cluster_ref, properties=["configurationEx"] + ) # Copy elements we want to update to spec - for p in ['dasConfig', 'drsConfig']: - setattr(cluster_spec, p, getattr(props['configurationEx'], p)) - if props['configurationEx'].vsanConfigInfo: - cluster_spec.vsanConfig = props['configurationEx'].vsanConfigInfo + for p in ["dasConfig", "drsConfig"]: + setattr(cluster_spec, p, getattr(props["configurationEx"], p)) + if props["configurationEx"].vsanConfigInfo: + cluster_spec.vsanConfig = props["configurationEx"].vsanConfigInfo vsan_spec = None vsan_61 = None - if cluster_dict.get('vsan'): + if cluster_dict.get("vsan"): # XXX The correct way of retrieving the VSAN data (on the if branch) # is not supported before 60u2 vcenter vcenter_info = salt.utils.vmware.get_service_info(service_instance) - if float(vcenter_info.apiVersion) >= 6.0 and \ - int(vcenter_info.build) >= 3634794: # 60u2 + if ( + float(vcenter_info.apiVersion) >= 6.0 and int(vcenter_info.build) >= 3634794 + ): # 60u2 vsan_61 = False vsan_info = salt.utils.vsan.get_cluster_vsan_info(cluster_ref) vsan_spec = vim.vsan.ReconfigSpec(modify=True) @@ -5517,33 +6007,37 @@ def update_cluster(cluster_dict, datacenter=None, cluster=None, # also if HA was previously disabled it can be enabled automatically if # desired if vsan_spec: - log.trace('vsan_spec = {0}'.format(vsan_spec)) + log.trace("vsan_spec = {0}".format(vsan_spec)) salt.utils.vsan.reconfigure_cluster_vsan(cluster_ref, vsan_spec) # We need to retrieve again the properties and reapply them # As the VSAN configuration has changed cluster_spec = vim.ClusterConfigSpecEx() props = salt.utils.vmware.get_properties_of_managed_object( - cluster_ref, properties=['configurationEx']) + cluster_ref, properties=["configurationEx"] + ) # Copy elements we want to update to spec - for p in ['dasConfig', 'drsConfig']: - setattr(cluster_spec, p, getattr(props['configurationEx'], p)) - if props['configurationEx'].vsanConfigInfo: - cluster_spec.vsanConfig = props['configurationEx'].vsanConfigInfo + for p in ["dasConfig", "drsConfig"]: + setattr(cluster_spec, p, getattr(props["configurationEx"], p)) + if props["configurationEx"].vsanConfigInfo: + cluster_spec.vsanConfig = props["configurationEx"].vsanConfigInfo # We only need to configure the cluster_spec, as if it were a vsan_61 # cluster _apply_cluster_dict(cluster_spec, cluster_dict) salt.utils.vmware.update_cluster(cluster_ref, cluster_spec) - return {'update_cluster': True} + return {"update_cluster": True} @depends(HAS_PYVMOMI) -@supports_proxies('esxi', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxi", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def list_datastores_via_proxy(datastore_names=None, backing_disk_ids=None, - backing_disk_scsi_addresses=None, - service_instance=None): - ''' +def list_datastores_via_proxy( + datastore_names=None, + backing_disk_ids=None, + backing_disk_scsi_addresses=None, + service_instance=None, +): + """ Returns a list of dict representations of the datastores visible to the proxy object. The list of datastores can be filtered by datastore names, backing disk ids (canonical names) or backing disk scsi addresses. @@ -5570,65 +6064,82 @@ def list_datastores_via_proxy(datastore_names=None, backing_disk_ids=None, salt '*' vsphere.list_datastores_via_proxy salt '*' vsphere.list_datastores_via_proxy datastore_names=[ds1, ds2] - ''' + """ target = _get_proxy_target(service_instance) target_name = salt.utils.vmware.get_managed_object_name(target) - log.trace('target name = {}'.format(target_name)) + log.trace("target name = {}".format(target_name)) # Default to getting all disks if no filtering is done - get_all_datastores = True if \ - not (datastore_names or backing_disk_ids or - backing_disk_scsi_addresses) else False + get_all_datastores = ( + True + if not (datastore_names or backing_disk_ids or backing_disk_scsi_addresses) + else False + ) # Get the ids of the disks with the scsi addresses if backing_disk_scsi_addresses: - log.debug('Retrieving disk ids for scsi addresses ' - '\'{0}\''.format(backing_disk_scsi_addresses)) - disk_ids = [d.canonicalName for d in - salt.utils.vmware.get_disks( - target, scsi_addresses=backing_disk_scsi_addresses)] - log.debug('Found disk ids \'{}\''.format(disk_ids)) - backing_disk_ids = backing_disk_ids.extend(disk_ids) if \ - backing_disk_ids else disk_ids - datastores = salt.utils.vmware.get_datastores(service_instance, - target, - datastore_names, - backing_disk_ids, - get_all_datastores) + log.debug( + "Retrieving disk ids for scsi addresses " + "'{0}'".format(backing_disk_scsi_addresses) + ) + disk_ids = [ + d.canonicalName + for d in salt.utils.vmware.get_disks( + target, scsi_addresses=backing_disk_scsi_addresses + ) + ] + log.debug("Found disk ids '{}'".format(disk_ids)) + backing_disk_ids = ( + backing_disk_ids.extend(disk_ids) if backing_disk_ids else disk_ids + ) + datastores = salt.utils.vmware.get_datastores( + service_instance, target, datastore_names, backing_disk_ids, get_all_datastores + ) # Search for disk backed datastores if target is host # to be able to add the backing_disk_ids mount_infos = [] if isinstance(target, vim.HostSystem): storage_system = salt.utils.vmware.get_storage_system( - service_instance, target, target_name) + service_instance, target, target_name + ) props = salt.utils.vmware.get_properties_of_managed_object( - storage_system, ['fileSystemVolumeInfo.mountInfo']) - mount_infos = props.get('fileSystemVolumeInfo.mountInfo', []) + storage_system, ["fileSystemVolumeInfo.mountInfo"] + ) + mount_infos = props.get("fileSystemVolumeInfo.mountInfo", []) ret_dict = [] for ds in datastores: - ds_dict = {'name': ds.name, - 'type': ds.summary.type, - 'free_space': ds.summary.freeSpace, - 'capacity': ds.summary.capacity} + ds_dict = { + "name": ds.name, + "type": ds.summary.type, + "free_space": ds.summary.freeSpace, + "capacity": ds.summary.capacity, + } backing_disk_ids = [] - for vol in [i.volume for i in mount_infos if - i.volume.name == ds.name and - isinstance(i.volume, vim.HostVmfsVolume)]: + for vol in [ + i.volume + for i in mount_infos + if i.volume.name == ds.name and isinstance(i.volume, vim.HostVmfsVolume) + ]: backing_disk_ids.extend([e.diskName for e in vol.extent]) if backing_disk_ids: - ds_dict['backing_disk_ids'] = backing_disk_ids + ds_dict["backing_disk_ids"] = backing_disk_ids ret_dict.append(ds_dict) return ret_dict @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy -def create_vmfs_datastore(datastore_name, disk_id, vmfs_major_version, - safety_checks=True, service_instance=None): - ''' +def create_vmfs_datastore( + datastore_name, + disk_id, + vmfs_major_version, + safety_checks=True, + service_instance=None, +): + """ Creates a ESXi host disk group with the specified cache and capacity disks. datastore_name @@ -5652,36 +6163,41 @@ def create_vmfs_datastore(datastore_name, disk_id, vmfs_major_version, salt '*' vsphere.create_vmfs_datastore datastore_name=ds1 disk_id= vmfs_major_version=5 - ''' - log.debug('Validating vmfs datastore input') + """ + log.debug("Validating vmfs datastore input") schema = VmfsDatastoreSchema.serialize() try: jsonschema.validate( - {'datastore': {'name': datastore_name, - 'backing_disk_id': disk_id, - 'vmfs_version': vmfs_major_version}}, - schema) + { + "datastore": { + "name": datastore_name, + "backing_disk_id": disk_id, + "vmfs_version": vmfs_major_version, + } + }, + schema, + ) except jsonschema.exceptions.ValidationError as exc: raise ArgumentValueError(exc) host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] + hostname = __proxy__["esxi.get_details"]()["esxi_host"] if safety_checks: disks = salt.utils.vmware.get_disks(host_ref, disk_ids=[disk_id]) if not disks: raise VMwareObjectRetrievalError( - 'Disk \'{0}\' was not found in host \'{1}\''.format(disk_id, - hostname)) + "Disk '{0}' was not found in host '{1}'".format(disk_id, hostname) + ) ds_ref = salt.utils.vmware.create_vmfs_datastore( - host_ref, datastore_name, disks[0], vmfs_major_version) + host_ref, datastore_name, disks[0], vmfs_major_version + ) return True @depends(HAS_PYVMOMI) -@supports_proxies('esxi', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxi", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def rename_datastore(datastore_name, new_datastore_name, - service_instance=None): - ''' +def rename_datastore(datastore_name, new_datastore_name, service_instance=None): + """ Renames a datastore. The datastore needs to be visible to the proxy. datastore_name @@ -5697,28 +6213,29 @@ def rename_datastore(datastore_name, new_datastore_name, .. code-block:: bash salt '*' vsphere.rename_datastore old_name new_name - ''' + """ # Argument validation - log.trace('Renaming datastore {0} to {1}' - ''.format(datastore_name, new_datastore_name)) + log.trace( + "Renaming datastore {0} to {1}" "".format(datastore_name, new_datastore_name) + ) target = _get_proxy_target(service_instance) datastores = salt.utils.vmware.get_datastores( - service_instance, - target, - datastore_names=[datastore_name]) + service_instance, target, datastore_names=[datastore_name] + ) if not datastores: - raise VMwareObjectRetrievalError('Datastore \'{0}\' was not found' - ''.format(datastore_name)) + raise VMwareObjectRetrievalError( + "Datastore '{0}' was not found" "".format(datastore_name) + ) ds = datastores[0] salt.utils.vmware.rename_datastore(ds, new_datastore_name) return True @depends(HAS_PYVMOMI) -@supports_proxies('esxi', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxi", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy def remove_datastore(datastore, service_instance=None): - ''' + """ Removes a datastore. If multiple datastores an error is raised. datastore @@ -5731,28 +6248,29 @@ def remove_datastore(datastore, service_instance=None): .. code-block:: bash salt '*' vsphere.remove_datastore ds_name - ''' - log.trace('Removing datastore \'{0}\''.format(datastore)) + """ + log.trace("Removing datastore '{0}'".format(datastore)) target = _get_proxy_target(service_instance) datastores = salt.utils.vmware.get_datastores( - service_instance, - reference=target, - datastore_names=[datastore]) + service_instance, reference=target, datastore_names=[datastore] + ) if not datastores: raise VMwareObjectRetrievalError( - 'Datastore \'{0}\' was not found'.format(datastore)) + "Datastore '{0}' was not found".format(datastore) + ) if len(datastores) > 1: raise VMwareObjectRetrievalError( - 'Multiple datastores \'{0}\' were found'.format(datastore)) + "Multiple datastores '{0}' were found".format(datastore) + ) salt.utils.vmware.remove_datastore(service_instance, datastores[0]) return True @depends(HAS_PYVMOMI) -@supports_proxies('esxcluster', 'esxdatacenter') +@supports_proxies("esxcluster", "esxdatacenter") @gets_service_instance_via_proxy def list_licenses(service_instance=None): - ''' + """ Lists all licenses on a vCenter. service_instance @@ -5762,25 +6280,28 @@ def list_licenses(service_instance=None): .. code-block:: bash salt '*' vsphere.list_licenses - ''' - log.trace('Retrieving all licenses') + """ + log.trace("Retrieving all licenses") licenses = salt.utils.vmware.get_licenses(service_instance) - ret_dict = [{'key': l.licenseKey, - 'name': l.name, - 'description': l.labels[0].value if l.labels else None, - # VMware handles unlimited capacity as 0 - 'capacity': l.total if l.total > 0 else sys.maxsize, - 'used': l.used if l.used else 0} - for l in licenses] + ret_dict = [ + { + "key": l.licenseKey, + "name": l.name, + "description": l.labels[0].value if l.labels else None, + # VMware handles unlimited capacity as 0 + "capacity": l.total if l.total > 0 else sys.maxsize, + "used": l.used if l.used else 0, + } + for l in licenses + ] return ret_dict @depends(HAS_PYVMOMI) -@supports_proxies('esxcluster', 'esxdatacenter') +@supports_proxies("esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def add_license(key, description, safety_checks=True, - service_instance=None): - ''' +def add_license(key, description, safety_checks=True, service_instance=None): + """ Adds a license to the vCenter or ESXi host key @@ -5800,14 +6321,14 @@ def add_license(key, description, safety_checks=True, .. code-block:: bash salt '*' vsphere.add_license key=<license_key> desc='License desc' - ''' - log.trace('Adding license \'{0}\''.format(key)) + """ + log.trace("Adding license '{0}'".format(key)) salt.utils.vmware.add_license(service_instance, key, description) return True def _get_entity(service_instance, entity): - ''' + """ Returns the entity associated with the entity dict representation Supported entities: cluster, vcenter @@ -5828,36 +6349,37 @@ def _get_entity(service_instance, entity): entity Entity dict in the format above - ''' + """ - log.trace('Retrieving entity: {0}'.format(entity)) - if entity['type'] == 'cluster': - dc_ref = salt.utils.vmware.get_datacenter(service_instance, - entity['datacenter']) - return salt.utils.vmware.get_cluster(dc_ref, entity['cluster']) - elif entity['type'] == 'vcenter': + log.trace("Retrieving entity: {0}".format(entity)) + if entity["type"] == "cluster": + dc_ref = salt.utils.vmware.get_datacenter( + service_instance, entity["datacenter"] + ) + return salt.utils.vmware.get_cluster(dc_ref, entity["cluster"]) + elif entity["type"] == "vcenter": return None - raise ArgumentValueError('Unsupported entity type \'{0}\'' - ''.format(entity['type'])) + raise ArgumentValueError("Unsupported entity type '{0}'" "".format(entity["type"])) def _validate_entity(entity): - ''' + """ Validates the entity dict representation entity Dictionary representation of an entity. See ``_get_entity`` docstrings for format. - ''' + """ - #Validate entity: - if entity['type'] == 'cluster': + # Validate entity: + if entity["type"] == "cluster": schema = ESXClusterEntitySchema.serialize() - elif entity['type'] == 'vcenter': + elif entity["type"] == "vcenter": schema = VCenterEntitySchema.serialize() else: - raise ArgumentValueError('Unsupported entity type \'{0}\'' - ''.format(entity['type'])) + raise ArgumentValueError( + "Unsupported entity type '{0}'" "".format(entity["type"]) + ) try: jsonschema.validate(entity, schema) except jsonschema.exceptions.ValidationError as exc: @@ -5866,11 +6388,12 @@ def _validate_entity(entity): @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxcluster', 'esxdatacenter') +@supports_proxies("esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def list_assigned_licenses(entity, entity_display_name, license_keys=None, - service_instance=None): - ''' +def list_assigned_licenses( + entity, entity_display_name, license_keys=None, service_instance=None +): + """ Lists the licenses assigned to an entity entity @@ -5892,32 +6415,42 @@ def list_assigned_licenses(entity, entity_display_name, license_keys=None, salt '*' vsphere.list_assigned_licenses entity={type:cluster,datacenter:dc,cluster:cl} entiy_display_name=cl - ''' - log.trace('Listing assigned licenses of entity {0}' - ''.format(entity)) + """ + log.trace("Listing assigned licenses of entity {0}" "".format(entity)) _validate_entity(entity) assigned_licenses = salt.utils.vmware.get_assigned_licenses( service_instance, entity_ref=_get_entity(service_instance, entity), - entity_name=entity_display_name) + entity_name=entity_display_name, + ) - return [{'key': l.licenseKey, - 'name': l.name, - 'description': l.labels[0].value if l.labels else None, - # VMware handles unlimited capacity as 0 - 'capacity': l.total if l.total > 0 else sys.maxsize} - for l in assigned_licenses if (license_keys is None) or - (l.licenseKey in license_keys)] + return [ + { + "key": l.licenseKey, + "name": l.name, + "description": l.labels[0].value if l.labels else None, + # VMware handles unlimited capacity as 0 + "capacity": l.total if l.total > 0 else sys.maxsize, + } + for l in assigned_licenses + if (license_keys is None) or (l.licenseKey in license_keys) + ] @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxcluster', 'esxdatacenter') +@supports_proxies("esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def assign_license(license_key, license_name, entity, entity_display_name, - safety_checks=True, service_instance=None): - ''' +def assign_license( + license_key, + license_name, + entity, + entity_display_name, + safety_checks=True, + service_instance=None, +): + """ Assigns a license to an entity license_key @@ -5945,29 +6478,31 @@ def assign_license(license_key, license_name, entity, entity_display_name, salt '*' vsphere.assign_license license_key=00000:00000 license name=test entity={type:cluster,datacenter:dc,cluster:cl} - ''' - log.trace('Assigning license {0} to entity {1}' - ''.format(license_key, entity)) + """ + log.trace("Assigning license {0} to entity {1}" "".format(license_key, entity)) _validate_entity(entity) if safety_checks: licenses = salt.utils.vmware.get_licenses(service_instance) if not [l for l in licenses if l.licenseKey == license_key]: - raise VMwareObjectRetrievalError('License \'{0}\' wasn\'t found' - ''.format(license_name)) + raise VMwareObjectRetrievalError( + "License '{0}' wasn't found" "".format(license_name) + ) salt.utils.vmware.assign_license( service_instance, license_key, license_name, entity_ref=_get_entity(service_instance, entity), - entity_name=entity_display_name) + entity_name=entity_display_name, + ) @depends(HAS_PYVMOMI) -@supports_proxies('esxi', 'esxcluster', 'esxdatacenter', 'vcenter') +@supports_proxies("esxi", "esxcluster", "esxdatacenter", "vcenter") @gets_service_instance_via_proxy -def list_hosts_via_proxy(hostnames=None, datacenter=None, - cluster=None, service_instance=None): - ''' +def list_hosts_via_proxy( + hostnames=None, datacenter=None, cluster=None, service_instance=None +): + """ Returns a list of hosts for the the specified VMware environment. The list of hosts can be filtered by datacenter name and/or cluster name @@ -5996,27 +6531,30 @@ def list_hosts_via_proxy(hostnames=None, datacenter=None, salt '*' vsphere.list_hosts_via_proxy hostnames=[esxi1.example.com] salt '*' vsphere.list_hosts_via_proxy datacenter=dc1 cluster=cluster1 - ''' + """ if cluster: if not datacenter: raise salt.exceptions.ArgumentValueError( - 'Datacenter is required when cluster is specified') + "Datacenter is required when cluster is specified" + ) get_all_hosts = False if not hostnames: get_all_hosts = True - hosts = salt.utils.vmware.get_hosts(service_instance, - datacenter_name=datacenter, - host_names=hostnames, - cluster_name=cluster, - get_all_hosts=get_all_hosts) + hosts = salt.utils.vmware.get_hosts( + service_instance, + datacenter_name=datacenter, + host_names=hostnames, + cluster_name=cluster, + get_all_hosts=get_all_hosts, + ) return [salt.utils.vmware.get_managed_object_name(h) for h in hosts] @depends(HAS_PYVMOMI) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy def list_disks(disk_ids=None, scsi_addresses=None, service_instance=None): - ''' + """ Returns a list of dict representations of the disks in an ESXi host. The list of disks can be filtered by disk canonical names or scsi addresses. @@ -6040,34 +6578,39 @@ def list_disks(disk_ids=None, scsi_addresses=None, service_instance=None): salt '*' vsphere.list_disks scsi_addresses='[vmhba0:C0:T0:L0, vmhba1:C0:T0:L0]' - ''' + """ host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] - log.trace('Retrieving disks if host \'{0}\''.format(hostname)) - log.trace('disk ids = {0}'.format(disk_ids)) - log.trace('scsi_addresses = {0}'.format(scsi_addresses)) + hostname = __proxy__["esxi.get_details"]()["esxi_host"] + log.trace("Retrieving disks if host '{0}'".format(hostname)) + log.trace("disk ids = {0}".format(disk_ids)) + log.trace("scsi_addresses = {0}".format(scsi_addresses)) # Default to getting all disks if no filtering is done get_all_disks = True if not (disk_ids or scsi_addresses) else False ret_list = [] scsi_address_to_lun = salt.utils.vmware.get_scsi_address_to_lun_map( - host_ref, hostname=hostname) + host_ref, hostname=hostname + ) canonical_name_to_scsi_address = { lun.canonicalName: scsi_addr - for scsi_addr, lun in six.iteritems(scsi_address_to_lun)} - for d in salt.utils.vmware.get_disks(host_ref, disk_ids, scsi_addresses, - get_all_disks): - ret_list.append({'id': d.canonicalName, - 'scsi_address': - canonical_name_to_scsi_address[d.canonicalName]}) + for scsi_addr, lun in six.iteritems(scsi_address_to_lun) + } + for d in salt.utils.vmware.get_disks( + host_ref, disk_ids, scsi_addresses, get_all_disks + ): + ret_list.append( + { + "id": d.canonicalName, + "scsi_address": canonical_name_to_scsi_address[d.canonicalName], + } + ) return ret_list @depends(HAS_PYVMOMI) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy -def erase_disk_partitions(disk_id=None, scsi_address=None, - service_instance=None): - ''' +def erase_disk_partitions(disk_id=None, scsi_address=None, service_instance=None): + """ Erases the partitions on a disk. The disk can be specified either by the canonical name, or by the scsi_address. @@ -6091,38 +6634,44 @@ def erase_disk_partitions(disk_id=None, scsi_address=None, salt '*' vsphere.erase_disk_partitions scsi_address='vmhaba0:C0:T0:L0' salt '*' vsphere.erase_disk_partitions disk_id='naa.000000000000001' - ''' + """ if not disk_id and not scsi_address: - raise ArgumentValueError('Either \'disk_id\' or \'scsi_address\' ' - 'needs to be specified') + raise ArgumentValueError( + "Either 'disk_id' or 'scsi_address' " "needs to be specified" + ) host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] + hostname = __proxy__["esxi.get_details"]()["esxi_host"] if not disk_id: - scsi_address_to_lun = \ - salt.utils.vmware.get_scsi_address_to_lun_map(host_ref) + scsi_address_to_lun = salt.utils.vmware.get_scsi_address_to_lun_map(host_ref) if scsi_address not in scsi_address_to_lun: raise VMwareObjectRetrievalError( - 'Scsi lun with address \'{0}\' was not found on host \'{1}\'' - ''.format(scsi_address, hostname)) + "Scsi lun with address '{0}' was not found on host '{1}'" + "".format(scsi_address, hostname) + ) disk_id = scsi_address_to_lun[scsi_address].canonicalName - log.trace('[{0}] Got disk id \'{1}\' for scsi address \'{2}\'' - ''.format(hostname, disk_id, scsi_address)) - log.trace('Erasing disk partitions on disk \'{0}\' in host \'{1}\'' - ''.format(disk_id, hostname)) - salt.utils.vmware.erase_disk_partitions(service_instance, - host_ref, disk_id, - hostname=hostname) - log.info('Erased disk partitions on disk \'{0}\' on host \'{1}\'' - ''.format(disk_id, hostname)) + log.trace( + "[{0}] Got disk id '{1}' for scsi address '{2}'" + "".format(hostname, disk_id, scsi_address) + ) + log.trace( + "Erasing disk partitions on disk '{0}' in host '{1}'" + "".format(disk_id, hostname) + ) + salt.utils.vmware.erase_disk_partitions( + service_instance, host_ref, disk_id, hostname=hostname + ) + log.info( + "Erased disk partitions on disk '{0}' on host '{1}'" + "".format(disk_id, hostname) + ) return True @depends(HAS_PYVMOMI) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy -def list_disk_partitions(disk_id=None, scsi_address=None, - service_instance=None): - ''' +def list_disk_partitions(disk_id=None, scsi_address=None, service_instance=None): + """ Lists the partitions on a disk. The disk can be specified either by the canonical name, or by the scsi_address. @@ -6146,51 +6695,59 @@ def list_disk_partitions(disk_id=None, scsi_address=None, salt '*' vsphere.list_disk_partitions scsi_address='vmhaba0:C0:T0:L0' salt '*' vsphere.list_disk_partitions disk_id='naa.000000000000001' - ''' + """ if not disk_id and not scsi_address: - raise ArgumentValueError('Either \'disk_id\' or \'scsi_address\' ' - 'needs to be specified') + raise ArgumentValueError( + "Either 'disk_id' or 'scsi_address' " "needs to be specified" + ) host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] + hostname = __proxy__["esxi.get_details"]()["esxi_host"] if not disk_id: - scsi_address_to_lun = \ - salt.utils.vmware.get_scsi_address_to_lun_map(host_ref) + scsi_address_to_lun = salt.utils.vmware.get_scsi_address_to_lun_map(host_ref) if scsi_address not in scsi_address_to_lun: raise VMwareObjectRetrievalError( - 'Scsi lun with address \'{0}\' was not found on host \'{1}\'' - ''.format(scsi_address, hostname)) + "Scsi lun with address '{0}' was not found on host '{1}'" + "".format(scsi_address, hostname) + ) disk_id = scsi_address_to_lun[scsi_address].canonicalName - log.trace('[{0}] Got disk id \'{1}\' for scsi address \'{2}\'' - ''.format(hostname, disk_id, scsi_address)) - log.trace('Listing disk partitions on disk \'{0}\' in host \'{1}\'' - ''.format(disk_id, hostname)) - partition_info = \ - salt.utils.vmware.get_disk_partition_info(host_ref, disk_id) + log.trace( + "[{0}] Got disk id '{1}' for scsi address '{2}'" + "".format(hostname, disk_id, scsi_address) + ) + log.trace( + "Listing disk partitions on disk '{0}' in host '{1}'" + "".format(disk_id, hostname) + ) + partition_info = salt.utils.vmware.get_disk_partition_info(host_ref, disk_id) ret_list = [] # NOTE: 1. The layout view has an extra 'None' partition for free space # 2. The orders in the layout/partition views are not the same for part_spec in partition_info.spec.partition: - part_layout = [p for p in partition_info.layout.partition - if p.partition == part_spec.partition][0] - part_dict = {'hostname': hostname, - 'device': disk_id, - 'format': partition_info.spec.partitionFormat, - 'partition': part_spec.partition, - 'type': part_spec.type, - 'sectors': - part_spec.endSector - part_spec.startSector + 1, - 'size_KB': - (part_layout.end.block - part_layout.start.block + 1) * - part_layout.start.blockSize / 1024} + part_layout = [ + p + for p in partition_info.layout.partition + if p.partition == part_spec.partition + ][0] + part_dict = { + "hostname": hostname, + "device": disk_id, + "format": partition_info.spec.partitionFormat, + "partition": part_spec.partition, + "type": part_spec.type, + "sectors": part_spec.endSector - part_spec.startSector + 1, + "size_KB": (part_layout.end.block - part_layout.start.block + 1) + * part_layout.start.blockSize + / 1024, + } ret_list.append(part_dict) return ret_list @depends(HAS_PYVMOMI) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy def list_diskgroups(cache_disk_ids=None, service_instance=None): - ''' + """ Returns a list of disk group dict representation on an ESXi host. The list of disk groups can be filtered by the cache disks canonical names. If no filtering is applied, all disk groups are returned. @@ -6212,27 +6769,32 @@ def list_diskgroups(cache_disk_ids=None, service_instance=None): salt '*' vsphere.list_diskgroups salt '*' vsphere.list_diskgroups cache_disk_ids='[naa.000000000000001]' - ''' + """ host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] - log.trace('Listing diskgroups in \'{0}\''.format(hostname)) + hostname = __proxy__["esxi.get_details"]()["esxi_host"] + log.trace("Listing diskgroups in '{0}'".format(hostname)) get_all_diskgroups = True if not cache_disk_ids else False ret_list = [] - for dg in salt.utils.vmware.get_diskgroups(host_ref, cache_disk_ids, - get_all_diskgroups): + for dg in salt.utils.vmware.get_diskgroups( + host_ref, cache_disk_ids, get_all_diskgroups + ): ret_list.append( - {'cache_disk': dg.ssd.canonicalName, - 'capacity_disks': [d.canonicalName for d in dg.nonSsd]}) + { + "cache_disk": dg.ssd.canonicalName, + "capacity_disks": [d.canonicalName for d in dg.nonSsd], + } + ) return ret_list @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy -def create_diskgroup(cache_disk_id, capacity_disk_ids, safety_checks=True, - service_instance=None): - ''' +def create_diskgroup( + cache_disk_id, capacity_disk_ids, safety_checks=True, service_instance=None +): + """ Creates disk group on an ESXi host with the specified cache and capacity disks. @@ -6256,52 +6818,57 @@ def create_diskgroup(cache_disk_id, capacity_disk_ids, safety_checks=True, salt '*' vsphere.create_diskgroup cache_disk_id='naa.000000000000001' capacity_disk_ids='[naa.000000000000002, naa.000000000000003]' - ''' - log.trace('Validating diskgroup input') + """ + log.trace("Validating diskgroup input") schema = DiskGroupsDiskIdSchema.serialize() try: jsonschema.validate( - {'diskgroups': [{'cache_id': cache_disk_id, - 'capacity_ids': capacity_disk_ids}]}, - schema) + { + "diskgroups": [ + {"cache_id": cache_disk_id, "capacity_ids": capacity_disk_ids} + ] + }, + schema, + ) except jsonschema.exceptions.ValidationError as exc: raise ArgumentValueError(exc) host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] + hostname = __proxy__["esxi.get_details"]()["esxi_host"] if safety_checks: - diskgroups = \ - salt.utils.vmware.get_diskgroups(host_ref, [cache_disk_id]) + diskgroups = salt.utils.vmware.get_diskgroups(host_ref, [cache_disk_id]) if diskgroups: raise VMwareObjectExistsError( - 'Diskgroup with cache disk id \'{0}\' already exists ESXi ' - 'host \'{1}\''.format(cache_disk_id, hostname)) + "Diskgroup with cache disk id '{0}' already exists ESXi " + "host '{1}'".format(cache_disk_id, hostname) + ) disk_ids = capacity_disk_ids[:] disk_ids.insert(0, cache_disk_id) disks = salt.utils.vmware.get_disks(host_ref, disk_ids=disk_ids) for id in disk_ids: if not [d for d in disks if d.canonicalName == id]: raise VMwareObjectRetrievalError( - 'No disk with id \'{0}\' was found in ESXi host \'{1}\'' - ''.format(id, hostname)) + "No disk with id '{0}' was found in ESXi host '{1}'" + "".format(id, hostname) + ) cache_disk = [d for d in disks if d.canonicalName == cache_disk_id][0] capacity_disks = [d for d in disks if d.canonicalName in capacity_disk_ids] - vsan_disk_mgmt_system = \ - salt.utils.vsan.get_vsan_disk_management_system(service_instance) - dg = salt.utils.vsan.create_diskgroup(service_instance, - vsan_disk_mgmt_system, - host_ref, - cache_disk, - capacity_disks) + vsan_disk_mgmt_system = salt.utils.vsan.get_vsan_disk_management_system( + service_instance + ) + dg = salt.utils.vsan.create_diskgroup( + service_instance, vsan_disk_mgmt_system, host_ref, cache_disk, capacity_disks + ) return True @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy -def add_capacity_to_diskgroup(cache_disk_id, capacity_disk_ids, - safety_checks=True, service_instance=None): - ''' +def add_capacity_to_diskgroup( + cache_disk_id, capacity_disk_ids, safety_checks=True, service_instance=None +): + """ Adds capacity disks to the disk group with the specified cache disk. cache_disk_id @@ -6323,50 +6890,59 @@ def add_capacity_to_diskgroup(cache_disk_id, capacity_disk_ids, salt '*' vsphere.add_capacity_to_diskgroup cache_disk_id='naa.000000000000001' capacity_disk_ids='[naa.000000000000002, naa.000000000000003]' - ''' - log.trace('Validating diskgroup input') + """ + log.trace("Validating diskgroup input") schema = DiskGroupsDiskIdSchema.serialize() try: jsonschema.validate( - {'diskgroups': [{'cache_id': cache_disk_id, - 'capacity_ids': capacity_disk_ids}]}, - schema) + { + "diskgroups": [ + {"cache_id": cache_disk_id, "capacity_ids": capacity_disk_ids} + ] + }, + schema, + ) except jsonschema.exceptions.ValidationError as exc: raise ArgumentValueError(exc) host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] + hostname = __proxy__["esxi.get_details"]()["esxi_host"] disks = salt.utils.vmware.get_disks(host_ref, disk_ids=capacity_disk_ids) if safety_checks: for id in capacity_disk_ids: if not [d for d in disks if d.canonicalName == id]: raise VMwareObjectRetrievalError( - 'No disk with id \'{0}\' was found in ESXi host \'{1}\'' - ''.format(id, hostname)) - diskgroups = \ - salt.utils.vmware.get_diskgroups( - host_ref, cache_disk_ids=[cache_disk_id]) + "No disk with id '{0}' was found in ESXi host '{1}'" + "".format(id, hostname) + ) + diskgroups = salt.utils.vmware.get_diskgroups( + host_ref, cache_disk_ids=[cache_disk_id] + ) if not diskgroups: raise VMwareObjectRetrievalError( - 'No diskgroup with cache disk id \'{0}\' was found in ESXi ' - 'host \'{1}\''.format(cache_disk_id, hostname)) - vsan_disk_mgmt_system = \ - salt.utils.vsan.get_vsan_disk_management_system(service_instance) - salt.utils.vsan.add_capacity_to_diskgroup(service_instance, - vsan_disk_mgmt_system, - host_ref, - diskgroups[0], - disks) + "No diskgroup with cache disk id '{0}' was found in ESXi " + "host '{1}'".format(cache_disk_id, hostname) + ) + vsan_disk_mgmt_system = salt.utils.vsan.get_vsan_disk_management_system( + service_instance + ) + salt.utils.vsan.add_capacity_to_diskgroup( + service_instance, vsan_disk_mgmt_system, host_ref, diskgroups[0], disks + ) return True @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy -def remove_capacity_from_diskgroup(cache_disk_id, capacity_disk_ids, - data_evacuation=True, safety_checks=True, - service_instance=None): - ''' +def remove_capacity_from_diskgroup( + cache_disk_id, + capacity_disk_ids, + data_evacuation=True, + safety_checks=True, + service_instance=None, +): + """ Remove capacity disks from the disk group with the specified cache disk. cache_disk_id @@ -6392,48 +6968,55 @@ def remove_capacity_from_diskgroup(cache_disk_id, capacity_disk_ids, salt '*' vsphere.remove_capacity_from_diskgroup cache_disk_id='naa.000000000000001' capacity_disk_ids='[naa.000000000000002, naa.000000000000003]' - ''' - log.trace('Validating diskgroup input') + """ + log.trace("Validating diskgroup input") schema = DiskGroupsDiskIdSchema.serialize() try: jsonschema.validate( - {'diskgroups': [{'cache_id': cache_disk_id, - 'capacity_ids': capacity_disk_ids}]}, - schema) + { + "diskgroups": [ + {"cache_id": cache_disk_id, "capacity_ids": capacity_disk_ids} + ] + }, + schema, + ) except jsonschema.exceptions.ValidationError as exc: raise ArgumentValueError(str(exc)) host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] + hostname = __proxy__["esxi.get_details"]()["esxi_host"] disks = salt.utils.vmware.get_disks(host_ref, disk_ids=capacity_disk_ids) if safety_checks: for id in capacity_disk_ids: if not [d for d in disks if d.canonicalName == id]: raise VMwareObjectRetrievalError( - 'No disk with id \'{0}\' was found in ESXi host \'{1}\'' - ''.format(id, hostname)) - diskgroups = \ - salt.utils.vmware.get_diskgroups(host_ref, - cache_disk_ids=[cache_disk_id]) + "No disk with id '{0}' was found in ESXi host '{1}'" + "".format(id, hostname) + ) + diskgroups = salt.utils.vmware.get_diskgroups( + host_ref, cache_disk_ids=[cache_disk_id] + ) if not diskgroups: raise VMwareObjectRetrievalError( - 'No diskgroup with cache disk id \'{0}\' was found in ESXi ' - 'host \'{1}\''.format(cache_disk_id, hostname)) - log.trace('data_evacuation = {0}'.format(data_evacuation)) + "No diskgroup with cache disk id '{0}' was found in ESXi " + "host '{1}'".format(cache_disk_id, hostname) + ) + log.trace("data_evacuation = {0}".format(data_evacuation)) salt.utils.vsan.remove_capacity_from_diskgroup( - service_instance, host_ref, diskgroups[0], - capacity_disks=[d for d in disks - if d.canonicalName in capacity_disk_ids], - data_evacuation=data_evacuation) + service_instance, + host_ref, + diskgroups[0], + capacity_disks=[d for d in disks if d.canonicalName in capacity_disk_ids], + data_evacuation=data_evacuation, + ) return True @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy -def remove_diskgroup(cache_disk_id, data_accessibility=True, - service_instance=None): - ''' +def remove_diskgroup(cache_disk_id, data_accessibility=True, service_instance=None): + """ Remove the diskgroup with the specified cache disk. cache_disk_id @@ -6449,29 +7032,30 @@ def remove_diskgroup(cache_disk_id, data_accessibility=True, .. code-block:: bash salt '*' vsphere.remove_diskgroup cache_disk_id='naa.000000000000001' - ''' - log.trace('Validating diskgroup input') + """ + log.trace("Validating diskgroup input") host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] - diskgroups = \ - salt.utils.vmware.get_diskgroups(host_ref, - cache_disk_ids=[cache_disk_id]) + hostname = __proxy__["esxi.get_details"]()["esxi_host"] + diskgroups = salt.utils.vmware.get_diskgroups( + host_ref, cache_disk_ids=[cache_disk_id] + ) if not diskgroups: raise VMwareObjectRetrievalError( - 'No diskgroup with cache disk id \'{0}\' was found in ESXi ' - 'host \'{1}\''.format(cache_disk_id, hostname)) - log.trace('data accessibility = {0}'.format(data_accessibility)) + "No diskgroup with cache disk id '{0}' was found in ESXi " + "host '{1}'".format(cache_disk_id, hostname) + ) + log.trace("data accessibility = {0}".format(data_accessibility)) salt.utils.vsan.remove_diskgroup( - service_instance, host_ref, diskgroups[0], - data_accessibility=data_accessibility) + service_instance, host_ref, diskgroups[0], data_accessibility=data_accessibility + ) return True @depends(HAS_PYVMOMI) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy def get_host_cache(service_instance=None): - ''' + """ Returns the host cache configuration on the proxy host. service_instance @@ -6481,30 +7065,33 @@ def get_host_cache(service_instance=None): .. code-block:: bash salt '*' vsphere.get_host_cache - ''' + """ # Default to getting all disks if no filtering is done ret_dict = {} host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] + hostname = __proxy__["esxi.get_details"]()["esxi_host"] hci = salt.utils.vmware.get_host_cache(host_ref) if not hci: - log.debug('Host cache not configured on host \'{0}\''.format(hostname)) - ret_dict['enabled'] = False + log.debug("Host cache not configured on host '{0}'".format(hostname)) + ret_dict["enabled"] = False return ret_dict # TODO Support multiple host cache info objects (on multiple datastores) - return {'enabled': True, - 'datastore': {'name': hci.key.name}, - 'swap_size': '{}MiB'.format(hci.swapSize)} + return { + "enabled": True, + "datastore": {"name": hci.key.name}, + "swap_size": "{}MiB".format(hci.swapSize), + } @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -@supports_proxies('esxi') +@supports_proxies("esxi") @gets_service_instance_via_proxy -def configure_host_cache(enabled, datastore=None, swap_size_MiB=None, - service_instance=None): - ''' +def configure_host_cache( + enabled, datastore=None, swap_size_MiB=None, service_instance=None +): + """ Configures the host cache on the selected host. enabled @@ -6528,122 +7115,129 @@ def configure_host_cache(enabled, datastore=None, swap_size_MiB=None, salt '*' vsphere.configure_host_cache enabled=True datastore=ds1 swap_size_MiB=1024 - ''' - log.debug('Validating host cache input') + """ + log.debug("Validating host cache input") schema = SimpleHostCacheSchema.serialize() try: - jsonschema.validate({'enabled': enabled, - 'datastore_name': datastore, - 'swap_size_MiB': swap_size_MiB}, - schema) + jsonschema.validate( + { + "enabled": enabled, + "datastore_name": datastore, + "swap_size_MiB": swap_size_MiB, + }, + schema, + ) except jsonschema.exceptions.ValidationError as exc: raise ArgumentValueError(exc) if not enabled: - raise ArgumentValueError('Disabling the host cache is not supported') - ret_dict = {'enabled': False} + raise ArgumentValueError("Disabling the host cache is not supported") + ret_dict = {"enabled": False} host_ref = _get_proxy_target(service_instance) - hostname = __proxy__['esxi.get_details']()['esxi_host'] + hostname = __proxy__["esxi.get_details"]()["esxi_host"] if datastore: ds_refs = salt.utils.vmware.get_datastores( - service_instance, host_ref, datastore_names=[datastore]) + service_instance, host_ref, datastore_names=[datastore] + ) if not ds_refs: raise VMwareObjectRetrievalError( - 'Datastore \'{0}\' was not found on host ' - '\'{1}\''.format(datastore, hostname)) + "Datastore '{0}' was not found on host " + "'{1}'".format(datastore, hostname) + ) ds_ref = ds_refs[0] salt.utils.vmware.configure_host_cache(host_ref, ds_ref, swap_size_MiB) return True def _check_hosts(service_instance, host, host_names): - ''' + """ Helper function that checks to see if the host provided is a vCenter Server or an ESXi host. If it's an ESXi host, returns a list of a single host_name. If a host reference isn't found, we're trying to find a host object for a vCenter server. Raises a CommandExecutionError in this case, as we need host references to check against. - ''' + """ if not host_names: host_name = _get_host_ref(service_instance, host) if host_name: host_names = [host] else: - raise CommandExecutionError('No host reference found. If connecting to a ' - 'vCenter Server, a list of \'host_names\' must be ' - 'provided.') + raise CommandExecutionError( + "No host reference found. If connecting to a " + "vCenter Server, a list of 'host_names' must be " + "provided." + ) elif not isinstance(host_names, list): - raise CommandExecutionError('\'host_names\' must be a list.') + raise CommandExecutionError("'host_names' must be a list.") return host_names def _format_coredump_stdout(cmd_ret): - ''' + """ Helper function to format the stdout from the get_coredump_network_config function. cmd_ret The return dictionary that comes from a cmd.run_all call. - ''' + """ ret_dict = {} - for line in cmd_ret['stdout'].splitlines(): + for line in cmd_ret["stdout"].splitlines(): line = line.strip().lower() - if line.startswith('enabled:'): - enabled = line.split(':') - if 'true' in enabled[1]: - ret_dict['enabled'] = True + if line.startswith("enabled:"): + enabled = line.split(":") + if "true" in enabled[1]: + ret_dict["enabled"] = True else: - ret_dict['enabled'] = False + ret_dict["enabled"] = False break - if line.startswith('host vnic:'): - host_vnic = line.split(':') - ret_dict['host_vnic'] = host_vnic[1].strip() - if line.startswith('network server ip:'): - ip = line.split(':') - ret_dict['ip'] = ip[1].strip() - if line.startswith('network server port:'): - ip_port = line.split(':') - ret_dict['port'] = ip_port[1].strip() + if line.startswith("host vnic:"): + host_vnic = line.split(":") + ret_dict["host_vnic"] = host_vnic[1].strip() + if line.startswith("network server ip:"): + ip = line.split(":") + ret_dict["ip"] = ip[1].strip() + if line.startswith("network server port:"): + ip_port = line.split(":") + ret_dict["port"] = ip_port[1].strip() return ret_dict def _format_firewall_stdout(cmd_ret): - ''' + """ Helper function to format the stdout from the get_firewall_status function. cmd_ret The return dictionary that comes from a cmd.run_all call. - ''' - ret_dict = {'success': True, - 'rulesets': {}} - for line in cmd_ret['stdout'].splitlines(): - if line.startswith('Name'): + """ + ret_dict = {"success": True, "rulesets": {}} + for line in cmd_ret["stdout"].splitlines(): + if line.startswith("Name"): continue - if line.startswith('---'): + if line.startswith("---"): continue ruleset_status = line.split() - ret_dict['rulesets'][ruleset_status[0]] = bool(ruleset_status[1]) + ret_dict["rulesets"][ruleset_status[0]] = bool(ruleset_status[1]) return ret_dict def _format_syslog_config(cmd_ret): - ''' + """ Helper function to format the stdout from the get_syslog_config function. cmd_ret The return dictionary that comes from a cmd.run_all call. - ''' - ret_dict = {'success': cmd_ret['retcode'] == 0} + """ + ret_dict = {"success": cmd_ret["retcode"] == 0} - if cmd_ret['retcode'] != 0: - ret_dict['message'] = cmd_ret['stdout'] + if cmd_ret["retcode"] != 0: + ret_dict["message"] = cmd_ret["stdout"] else: - for line in cmd_ret['stdout'].splitlines(): + for line in cmd_ret["stdout"].splitlines(): line = line.strip() - cfgvars = line.split(': ') + cfgvars = line.split(": ") key = cfgvars[0].strip() value = cfgvars[1].strip() ret_dict[key] = value @@ -6652,20 +7246,20 @@ def _format_syslog_config(cmd_ret): def _get_date_time_mgr(host_reference): - ''' + """ Helper function that returns a dateTimeManager object - ''' + """ return host_reference.configManager.dateTimeSystem def _get_host_ref(service_instance, host, host_name=None): - ''' + """ Helper function that returns a host object either from the host location or the host_name. If host_name is provided, that is the host_object that will be returned. The function will first search for hosts by DNS Name. If no hosts are found, it will try searching by IP Address. - ''' + """ search_index = salt.utils.vmware.get_inventory(service_instance).searchIndex # First, try to find the host reference by DNS Name. @@ -6682,23 +7276,23 @@ def _get_host_ref(service_instance, host, host_name=None): def _get_host_ssds(host_reference): - ''' + """ Helper function that returns a list of ssd objects for a given host. - ''' - return _get_host_disks(host_reference).get('SSDs') + """ + return _get_host_disks(host_reference).get("SSDs") def _get_host_non_ssds(host_reference): - ''' + """ Helper function that returns a list of Non-SSD objects for a given host. - ''' - return _get_host_disks(host_reference).get('Non-SSDs') + """ + return _get_host_disks(host_reference).get("Non-SSDs") def _get_host_disks(host_reference): - ''' + """ Helper function that returns a dictionary containing a list of SSD and Non-SSD disks. - ''' + """ storage_system = host_reference.configManager.storageSystem disks = storage_system.storageDeviceInfo.scsiLun ssds = [] @@ -6714,18 +7308,18 @@ def _get_host_disks(host_reference): else: non_ssds.append(disk) - return {'SSDs': ssds, 'Non-SSDs': non_ssds} + return {"SSDs": ssds, "Non-SSDs": non_ssds} def _get_service_manager(host_reference): - ''' + """ Helper function that returns a service manager object from a given host object. - ''' + """ return host_reference.configManager.serviceSystem def _get_vsan_eligible_disks(service_instance, host, host_names): - ''' + """ Helper function that returns a dictionary of host_name keys with either a list of eligible disks that can be added to VSAN or either an 'Error' message or a message saying no eligible disks were found. Possible keys/values look like: @@ -6734,7 +7328,7 @@ def _get_vsan_eligible_disks(service_instance, host, host_names): 'host_2': {'Eligible': 'The host xxx does not have any VSAN eligible disks.'}, 'host_3': {'Eligible': [disk1, disk2, disk3, disk4], 'host_4': {'Eligible': []}} - ''' + """ ret = {} for host_name in host_names: @@ -6742,26 +7336,30 @@ def _get_vsan_eligible_disks(service_instance, host, host_names): host_ref = _get_host_ref(service_instance, host, host_name=host_name) vsan_system = host_ref.configManager.vsanSystem if vsan_system is None: - msg = 'VSAN System Config Manager is unset for host \'{0}\'. ' \ - 'VSAN configuration cannot be changed without a configured ' \ - 'VSAN System.'.format(host_name) + msg = ( + "VSAN System Config Manager is unset for host '{0}'. " + "VSAN configuration cannot be changed without a configured " + "VSAN System.".format(host_name) + ) log.debug(msg) - ret.update({host_name: {'Error': msg}}) + ret.update({host_name: {"Error": msg}}) continue # Get all VSAN suitable disks for this host. suitable_disks = [] query = vsan_system.QueryDisksForVsan() for item in query: - if item.state == 'eligible': + if item.state == "eligible": suitable_disks.append(item) # No suitable disks were found to add. Warn and move on. # This isn't an error as the state may run repeatedly after all eligible disks are added. if not suitable_disks: - msg = 'The host \'{0}\' does not have any VSAN eligible disks.'.format(host_name) + msg = "The host '{0}' does not have any VSAN eligible disks.".format( + host_name + ) log.warning(msg) - ret.update({host_name: {'Eligible': msg}}) + ret.update({host_name: {"Eligible": msg}}) continue # Get disks for host and combine into one list of Disk Objects @@ -6774,16 +7372,26 @@ def _get_vsan_eligible_disks(service_instance, host, host_names): if disk.canonicalName == suitable_disk.disk.canonicalName: matching.append(disk) - ret.update({host_name: {'Eligible': matching}}) + ret.update({host_name: {"Eligible": matching}}) return ret -def _reset_syslog_config_params(host, username, password, cmd, resets, valid_resets, - protocol=None, port=None, esxi_host=None, credstore=None): - ''' +def _reset_syslog_config_params( + host, + username, + password, + cmd, + resets, + valid_resets, + protocol=None, + port=None, + esxi_host=None, + credstore=None, +): + """ Helper function for reset_syslog_config that resets the config and populates the return dictionary. - ''' + """ ret_dict = {} all_success = True @@ -6792,51 +7400,89 @@ def _reset_syslog_config_params(host, username, password, cmd, resets, valid_res for reset_param in resets: if reset_param in valid_resets: - ret = salt.utils.vmware.esxcli(host, username, password, cmd + reset_param, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) + ret = salt.utils.vmware.esxcli( + host, + username, + password, + cmd + reset_param, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) ret_dict[reset_param] = {} - ret_dict[reset_param]['success'] = ret['retcode'] == 0 - if ret['retcode'] != 0: + ret_dict[reset_param]["success"] = ret["retcode"] == 0 + if ret["retcode"] != 0: all_success = False - ret_dict[reset_param]['message'] = ret['stdout'] + ret_dict[reset_param]["message"] = ret["stdout"] else: all_success = False ret_dict[reset_param] = {} - ret_dict[reset_param]['success'] = False - ret_dict[reset_param]['message'] = 'Invalid syslog ' \ - 'configuration parameter' + ret_dict[reset_param]["success"] = False + ret_dict[reset_param]["message"] = ( + "Invalid syslog " "configuration parameter" + ) - ret_dict['success'] = all_success + ret_dict["success"] = all_success return ret_dict -def _set_syslog_config_helper(host, username, password, syslog_config, config_value, - protocol=None, port=None, reset_service=None, esxi_host=None, credstore=None): - ''' +def _set_syslog_config_helper( + host, + username, + password, + syslog_config, + config_value, + protocol=None, + port=None, + reset_service=None, + esxi_host=None, + credstore=None, +): + """ Helper function for set_syslog_config that sets the config and populates the return dictionary. - ''' - cmd = 'system syslog config set --{0} {1}'.format(syslog_config, config_value) + """ + cmd = "system syslog config set --{0} {1}".format(syslog_config, config_value) ret_dict = {} - valid_resets = ['logdir', 'loghost', 'default-rotate', - 'default-size', 'default-timeout', 'logdir-unique'] + valid_resets = [ + "logdir", + "loghost", + "default-rotate", + "default-size", + "default-timeout", + "logdir-unique", + ] if syslog_config not in valid_resets: - ret_dict.update({'success': False, - 'message': '\'{0}\' is not a valid config variable.'.format(syslog_config)}) + ret_dict.update( + { + "success": False, + "message": "'{0}' is not a valid config variable.".format( + syslog_config + ), + } + ) return ret_dict - response = salt.utils.vmware.esxcli(host, username, password, cmd, - protocol=protocol, port=port, - esxi_host=esxi_host, credstore=credstore) + response = salt.utils.vmware.esxcli( + host, + username, + password, + cmd, + protocol=protocol, + port=port, + esxi_host=esxi_host, + credstore=credstore, + ) # Update the return dictionary for success or error messages. - if response['retcode'] != 0: - ret_dict.update({syslog_config: {'success': False, - 'message': response['stdout']}}) + if response["retcode"] != 0: + ret_dict.update( + {syslog_config: {"success": False, "message": response["stdout"]}} + ) else: - ret_dict.update({syslog_config: {'success': True}}) + ret_dict.update({syslog_config: {"success": True}}) # Restart syslog for each host, if desired. if reset_service: @@ -6845,20 +7491,36 @@ def _set_syslog_config_helper(host, username, password, syslog_config, config_va esxi_host = [esxi_host] else: host_name = host - response = syslog_service_reload(host, username, password, - protocol=protocol, port=port, - esxi_hosts=esxi_host, credstore=credstore).get(host_name) - ret_dict.update({'syslog_restart': {'success': response['retcode'] == 0}}) + response = syslog_service_reload( + host, + username, + password, + protocol=protocol, + port=port, + esxi_hosts=esxi_host, + credstore=credstore, + ).get(host_name) + ret_dict.update({"syslog_restart": {"success": response["retcode"] == 0}}) return ret_dict @depends(HAS_PYVMOMI) -@ignores_kwargs('credstore') -def add_host_to_dvs(host, username, password, vmknic_name, vmnic_name, - dvs_name, target_portgroup_name, uplink_portgroup_name, - protocol=None, port=None, host_names=None): - ''' +@ignores_kwargs("credstore") +def add_host_to_dvs( + host, + username, + password, + vmknic_name, + vmnic_name, + dvs_name, + target_portgroup_name, + uplink_portgroup_name, + protocol=None, + port=None, + host_names=None, +): + """ Adds an ESXi host to a vSphere Distributed Virtual Switch and migrates the desired adapters to the DVS from the standard switch. @@ -7021,108 +7683,116 @@ def add_host_to_dvs(host, username, password, vmknic_name, vmnic_name, The SOAP API maps closely to PyVmomi, so from there it was (relatively) easy to figure out what Python to write. - ''' + """ ret = {} - ret['success'] = True - ret['message'] = [] - service_instance = salt.utils.vmware.get_service_instance(host=host, - username=username, - password=password, - protocol=protocol, - port=port) + ret["success"] = True + ret["message"] = [] + service_instance = salt.utils.vmware.get_service_instance( + host=host, username=username, password=password, protocol=protocol, port=port + ) dvs = salt.utils.vmware._get_dvs(service_instance, dvs_name) if not dvs: - ret['message'].append('No Distributed Virtual Switch found with name {0}'.format(dvs_name)) - ret['success'] = False + ret["message"].append( + "No Distributed Virtual Switch found with name {0}".format(dvs_name) + ) + ret["success"] = False - target_portgroup = salt.utils.vmware._get_dvs_portgroup(dvs, - target_portgroup_name) + target_portgroup = salt.utils.vmware._get_dvs_portgroup(dvs, target_portgroup_name) if not target_portgroup: - ret['message'].append('No target portgroup found with name {0}'.format(target_portgroup_name)) - ret['success'] = False + ret["message"].append( + "No target portgroup found with name {0}".format(target_portgroup_name) + ) + ret["success"] = False - uplink_portgroup = salt.utils.vmware._get_dvs_uplink_portgroup(dvs, - uplink_portgroup_name) + uplink_portgroup = salt.utils.vmware._get_dvs_uplink_portgroup( + dvs, uplink_portgroup_name + ) if not uplink_portgroup: - ret['message'].append('No uplink portgroup found with name {0}'.format(uplink_portgroup_name)) - ret['success'] = False + ret["message"].append( + "No uplink portgroup found with name {0}".format(uplink_portgroup_name) + ) + ret["success"] = False - if len(ret['message']) > 0: + if len(ret["message"]) > 0: return ret dvs_uuid = dvs.config.uuid try: host_names = _check_hosts(service_instance, host, host_names) except CommandExecutionError as e: - ret['message'] = 'Error retrieving hosts: {0}'.format(e.msg) + ret["message"] = "Error retrieving hosts: {0}".format(e.msg) return ret for host_name in host_names: ret[host_name] = {} - ret[host_name].update({'status': False, - 'uplink': uplink_portgroup_name, - 'portgroup': target_portgroup_name, - 'vmknic': vmknic_name, - 'vmnic': vmnic_name, - 'dvs': dvs_name}) + ret[host_name].update( + { + "status": False, + "uplink": uplink_portgroup_name, + "portgroup": target_portgroup_name, + "vmknic": vmknic_name, + "vmnic": vmnic_name, + "dvs": dvs_name, + } + ) host_ref = _get_host_ref(service_instance, host, host_name) if not host_ref: - ret[host_name].update({'message': 'Host {1} not found'.format(host_name)}) - ret['success'] = False + ret[host_name].update({"message": "Host {1} not found".format(host_name)}) + ret["success"] = False continue - dvs_hostmember_config = vim.dvs.HostMember.ConfigInfo( - host=host_ref - ) - dvs_hostmember = vim.dvs.HostMember( - config=dvs_hostmember_config - ) + dvs_hostmember_config = vim.dvs.HostMember.ConfigInfo(host=host_ref) + dvs_hostmember = vim.dvs.HostMember(config=dvs_hostmember_config) p_nics = salt.utils.vmware._get_pnics(host_ref) p_nic = [x for x in p_nics if x.device == vmnic_name] if len(p_nic) == 0: - ret[host_name].update({'message': 'Physical nic {0} not found'.format(vmknic_name)}) - ret['success'] = False + ret[host_name].update( + {"message": "Physical nic {0} not found".format(vmknic_name)} + ) + ret["success"] = False continue v_nics = salt.utils.vmware._get_vnics(host_ref) v_nic = [x for x in v_nics if x.device == vmknic_name] if len(v_nic) == 0: - ret[host_name].update({'message': 'Virtual nic {0} not found'.format(vmnic_name)}) - ret['success'] = False + ret[host_name].update( + {"message": "Virtual nic {0} not found".format(vmnic_name)} + ) + ret["success"] = False continue v_nic_mgr = salt.utils.vmware._get_vnic_manager(host_ref) if not v_nic_mgr: - ret[host_name].update({'message': 'Unable to get the host\'s virtual nic manager.'}) - ret['success'] = False + ret[host_name].update( + {"message": "Unable to get the host's virtual nic manager."} + ) + ret["success"] = False continue dvs_pnic_spec = vim.dvs.HostMember.PnicSpec( - pnicDevice=vmnic_name, - uplinkPortgroupKey=uplink_portgroup.key - ) - pnic_backing = vim.dvs.HostMember.PnicBacking( - pnicSpec=[dvs_pnic_spec] + pnicDevice=vmnic_name, uplinkPortgroupKey=uplink_portgroup.key ) + pnic_backing = vim.dvs.HostMember.PnicBacking(pnicSpec=[dvs_pnic_spec]) dvs_hostmember_config_spec = vim.dvs.HostMember.ConfigSpec( - host=host_ref, - operation='add', + host=host_ref, operation="add", ) dvs_config = vim.DVSConfigSpec( - configVersion=dvs.config.configVersion, - host=[dvs_hostmember_config_spec]) + configVersion=dvs.config.configVersion, host=[dvs_hostmember_config_spec] + ) task = dvs.ReconfigureDvs_Task(spec=dvs_config) try: - salt.utils.vmware.wait_for_task(task, host_name, - 'Adding host to the DVS', - sleep_seconds=3) + salt.utils.vmware.wait_for_task( + task, host_name, "Adding host to the DVS", sleep_seconds=3 + ) except Exception as e: # pylint: disable=broad-except - if hasattr(e, 'message') and hasattr(e.message, 'msg'): - if not (host_name in e.message.msg and 'already exists' in e.message.msg): - ret['success'] = False - ret[host_name].update({'message': e.message.msg}) + if hasattr(e, "message") and hasattr(e.message, "msg"): + if not ( + host_name in e.message.msg and "already exists" in e.message.msg + ): + ret["success"] = False + ret[host_name].update({"message": e.message.msg}) continue else: raise @@ -7136,48 +7806,53 @@ def add_host_to_dvs(host, username, password, vmknic_name, vmnic_name, break if not source_portgroup: - ret[host_name].update({'message': 'No matching portgroup on the vSwitch'}) - ret['success'] = False + ret[host_name].update({"message": "No matching portgroup on the vSwitch"}) + ret["success"] = False continue virtual_nic_config = vim.HostVirtualNicConfig( - changeOperation='edit', + changeOperation="edit", device=v_nic[0].device, portgroup=source_portgroup.spec.name, spec=vim.HostVirtualNicSpec( distributedVirtualPort=vim.DistributedVirtualSwitchPortConnection( - portgroupKey=target_portgroup.key, - switchUuid=target_portgroup.config.distributedVirtualSwitch.uuid - ) - ) + portgroupKey=target_portgroup.key, + switchUuid=target_portgroup.config.distributedVirtualSwitch.uuid, + ) + ), ) current_vswitch_ports = host_ref.config.network.vswitch[0].numPorts vswitch_config = vim.HostVirtualSwitchConfig( - changeOperation='edit', - name='vSwitch0', - spec=vim.HostVirtualSwitchSpec(numPorts=current_vswitch_ports) + changeOperation="edit", + name="vSwitch0", + spec=vim.HostVirtualSwitchSpec(numPorts=current_vswitch_ports), ) proxyswitch_config = vim.HostProxySwitchConfig( - changeOperation='edit', + changeOperation="edit", uuid=dvs_uuid, - spec=vim.HostProxySwitchSpec(backing=pnic_backing) + spec=vim.HostProxySwitchSpec(backing=pnic_backing), ) host_network_config = vim.HostNetworkConfig( vswitch=[vswitch_config], proxySwitch=[proxyswitch_config], - portgroup=[vim.HostPortGroupConfig( - changeOperation='remove', - spec=source_portgroup.spec) - ], - vnic=[virtual_nic_config]) + portgroup=[ + vim.HostPortGroupConfig( + changeOperation="remove", spec=source_portgroup.spec + ) + ], + vnic=[virtual_nic_config], + ) try: - network_system.UpdateNetworkConfig(changeMode='modify', - config=host_network_config) - ret[host_name].update({'status': True}) + network_system.UpdateNetworkConfig( + changeMode="modify", config=host_network_config + ) + ret[host_name].update({"status": True}) except Exception as e: # pylint: disable=broad-except - if hasattr(e, 'msg'): - ret[host_name].update({'message': 'Failed to migrate adapters ({0})'.format(e.msg)}) + if hasattr(e, "msg"): + ret[host_name].update( + {"message": "Failed to migrate adapters ({0})".format(e.msg)} + ) continue else: raise @@ -7186,95 +7861,147 @@ def add_host_to_dvs(host, username, password, vmknic_name, vmnic_name, @depends(HAS_PYVMOMI) -@supports_proxies('esxi', 'esxcluster', 'esxdatacenter', 'vcenter') +@supports_proxies("esxi", "esxcluster", "esxdatacenter", "vcenter") def _get_proxy_target(service_instance): - ''' + """ Returns the target object of a proxy. If the object doesn't exist a VMwareObjectRetrievalError is raised service_instance Service instance (vim.ServiceInstance) of the vCenter/ESXi host. - ''' + """ proxy_type = get_proxy_type() if not salt.utils.vmware.is_connection_to_a_vcenter(service_instance): - raise CommandExecutionError('\'_get_proxy_target\' not supported ' - 'when connected via the ESXi host') + raise CommandExecutionError( + "'_get_proxy_target' not supported " "when connected via the ESXi host" + ) reference = None - if proxy_type == 'esxcluster': - host, username, password, protocol, port, mechanism, principal, \ - domain, datacenter, cluster = _get_esxcluster_proxy_details() + if proxy_type == "esxcluster": + ( + host, + username, + password, + protocol, + port, + mechanism, + principal, + domain, + datacenter, + cluster, + ) = _get_esxcluster_proxy_details() dc_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) reference = salt.utils.vmware.get_cluster(dc_ref, cluster) - elif proxy_type == 'esxdatacenter': + elif proxy_type == "esxdatacenter": # esxdatacenter proxy - host, username, password, protocol, port, mechanism, principal, \ - domain, datacenter = _get_esxdatacenter_proxy_details() + ( + host, + username, + password, + protocol, + port, + mechanism, + principal, + domain, + datacenter, + ) = _get_esxdatacenter_proxy_details() - reference = salt.utils.vmware.get_datacenter(service_instance, - datacenter) - elif proxy_type == 'vcenter': + reference = salt.utils.vmware.get_datacenter(service_instance, datacenter) + elif proxy_type == "vcenter": # vcenter proxy - the target is the root folder reference = salt.utils.vmware.get_root_folder(service_instance) - elif proxy_type == 'esxi': + elif proxy_type == "esxi": # esxi proxy - details = __proxy__['esxi.get_details']() - if 'vcenter' not in details: - raise InvalidEntityError('Proxies connected directly to ESXi ' - 'hosts are not supported') + details = __proxy__["esxi.get_details"]() + if "vcenter" not in details: + raise InvalidEntityError( + "Proxies connected directly to ESXi " "hosts are not supported" + ) references = salt.utils.vmware.get_hosts( - service_instance, host_names=details['esxi_host']) + service_instance, host_names=details["esxi_host"] + ) if not references: raise VMwareObjectRetrievalError( - 'ESXi host \'{0}\' was not found'.format(details['esxi_host'])) + "ESXi host '{0}' was not found".format(details["esxi_host"]) + ) reference = references[0] - log.trace('reference = {0}'.format(reference)) + log.trace("reference = {0}".format(reference)) return reference def _get_esxdatacenter_proxy_details(): - ''' + """ Returns the running esxdatacenter's proxy details - ''' - det = __salt__['esxdatacenter.get_details']() - return det.get('vcenter'), det.get('username'), det.get('password'), \ - det.get('protocol'), det.get('port'), det.get('mechanism'), \ - det.get('principal'), det.get('domain'), det.get('datacenter') + """ + det = __salt__["esxdatacenter.get_details"]() + return ( + det.get("vcenter"), + det.get("username"), + det.get("password"), + det.get("protocol"), + det.get("port"), + det.get("mechanism"), + det.get("principal"), + det.get("domain"), + det.get("datacenter"), + ) def _get_esxcluster_proxy_details(): - ''' + """ Returns the running esxcluster's proxy details - ''' - det = __salt__['esxcluster.get_details']() - return det.get('vcenter'), det.get('username'), det.get('password'), \ - det.get('protocol'), det.get('port'), det.get('mechanism'), \ - det.get('principal'), det.get('domain'), det.get('datacenter'), \ - det.get('cluster') + """ + det = __salt__["esxcluster.get_details"]() + return ( + det.get("vcenter"), + det.get("username"), + det.get("password"), + det.get("protocol"), + det.get("port"), + det.get("mechanism"), + det.get("principal"), + det.get("domain"), + det.get("datacenter"), + det.get("cluster"), + ) def _get_esxi_proxy_details(): - ''' + """ Returns the running esxi's proxy details - ''' - det = __proxy__['esxi.get_details']() - host = det.get('host') - if det.get('vcenter'): - host = det['vcenter'] + """ + det = __proxy__["esxi.get_details"]() + host = det.get("host") + if det.get("vcenter"): + host = det["vcenter"] esxi_hosts = None - if det.get('esxi_host'): - esxi_hosts = [det['esxi_host']] - return host, det.get('username'), det.get('password'), \ - det.get('protocol'), det.get('port'), det.get('mechanism'), \ - det.get('principal'), det.get('domain'), esxi_hosts + if det.get("esxi_host"): + esxi_hosts = [det["esxi_host"]] + return ( + host, + det.get("username"), + det.get("password"), + det.get("protocol"), + det.get("port"), + det.get("mechanism"), + det.get("principal"), + det.get("domain"), + esxi_hosts, + ) @depends(HAS_PYVMOMI) @gets_service_instance_via_proxy -def get_vm(name, datacenter=None, vm_properties=None, traversal_spec=None, - parent_ref=None, service_instance=None): - ''' +def get_vm( + name, + datacenter=None, + vm_properties=None, + traversal_spec=None, + parent_ref=None, + service_instance=None, +): + """ Returns vm object properties. name @@ -7295,22 +8022,22 @@ def get_vm(name, datacenter=None, vm_properties=None, traversal_spec=None, service_instance Service instance (vim.ServiceInstance) of the vCenter. Default is None. - ''' + """ virtual_machine = salt.utils.vmware.get_vm_by_property( service_instance, name, datacenter=datacenter, vm_properties=vm_properties, traversal_spec=traversal_spec, - parent_ref=parent_ref) + parent_ref=parent_ref, + ) return virtual_machine @depends(HAS_PYVMOMI) @gets_service_instance_via_proxy -def get_vm_config_file(name, datacenter, placement, datastore, - service_instance=None): - ''' +def get_vm_config_file(name, datacenter, placement, datastore, service_instance=None): + """ Queries the virtual machine config file and returns vim.host.DatastoreBrowser.SearchResults object on success None on failure @@ -7326,45 +8053,42 @@ def get_vm_config_file(name, datacenter, placement, datastore, service_instance Service instance (vim.ServiceInstance) of the vCenter. Default is None. - ''' + """ browser_spec = vim.host.DatastoreBrowser.SearchSpec() directory = name browser_spec.query = [vim.host.DatastoreBrowser.VmConfigQuery()] - datacenter_object = salt.utils.vmware.get_datacenter(service_instance, - datacenter) - if 'cluster' in placement: - container_object = salt.utils.vmware.get_cluster(datacenter_object, - placement['cluster']) + datacenter_object = salt.utils.vmware.get_datacenter(service_instance, datacenter) + if "cluster" in placement: + container_object = salt.utils.vmware.get_cluster( + datacenter_object, placement["cluster"] + ) else: container_objects = salt.utils.vmware.get_hosts( - service_instance, - datacenter_name=datacenter, - host_names=[placement['host']]) + service_instance, datacenter_name=datacenter, host_names=[placement["host"]] + ) if not container_objects: raise salt.exceptions.VMwareObjectRetrievalError( - 'ESXi host named \'{0}\' wasn\'t ' - 'found.'.format(placement['host'])) + "ESXi host named '{0}' wasn't " "found.".format(placement["host"]) + ) container_object = container_objects[0] # list of vim.host.DatastoreBrowser.SearchResults objects - files = salt.utils.vmware.get_datastore_files(service_instance, - directory, - [datastore], - container_object, - browser_spec) + files = salt.utils.vmware.get_datastore_files( + service_instance, directory, [datastore], container_object, browser_spec + ) if files and len(files[0].file) > 1: raise salt.exceptions.VMwareMultipleObjectsError( - 'Multiple configuration files found in ' - 'the same virtual machine folder') + "Multiple configuration files found in " "the same virtual machine folder" + ) elif files and files[0].file: return files[0] else: return None -def _apply_hardware_version(hardware_version, config_spec, operation='add'): - ''' +def _apply_hardware_version(hardware_version, config_spec, operation="add"): + """ Specifies vm container version or schedules upgrade, returns True on change and False if nothing have been changed. @@ -7377,22 +8101,25 @@ def _apply_hardware_version(hardware_version, config_spec, operation='add'): operation Defines the operation which should be used, the possibles values: 'add' and 'edit', the default value is 'add' - ''' - log.trace('Configuring virtual machine hardware ' - 'version version={0}'.format(hardware_version)) - if operation == 'edit': - log.trace('Scheduling hardware version ' - 'upgrade to {0}'.format(hardware_version)) + """ + log.trace( + "Configuring virtual machine hardware " + "version version={0}".format(hardware_version) + ) + if operation == "edit": + log.trace( + "Scheduling hardware version " "upgrade to {0}".format(hardware_version) + ) scheduled_hardware_upgrade = vim.vm.ScheduledHardwareUpgradeInfo() - scheduled_hardware_upgrade.upgradePolicy = 'always' + scheduled_hardware_upgrade.upgradePolicy = "always" scheduled_hardware_upgrade.versionKey = hardware_version config_spec.scheduledHardwareUpgradeInfo = scheduled_hardware_upgrade - elif operation == 'add': + elif operation == "add": config_spec.version = str(hardware_version) def _apply_cpu_config(config_spec, cpu_props): - ''' + """ Sets CPU core count to the given value config_spec @@ -7400,23 +8127,24 @@ def _apply_cpu_config(config_spec, cpu_props): cpu_props CPU properties dict - ''' - log.trace('Configuring virtual machine CPU ' - 'settings cpu_props={0}'.format(cpu_props)) - if 'count' in cpu_props: - config_spec.numCPUs = int(cpu_props['count']) - if 'cores_per_socket' in cpu_props: - config_spec.numCoresPerSocket = int(cpu_props['cores_per_socket']) - if 'nested' in cpu_props and cpu_props['nested']: - config_spec.nestedHVEnabled = cpu_props['nested'] # True - if 'hotadd' in cpu_props and cpu_props['hotadd']: - config_spec.cpuHotAddEnabled = cpu_props['hotadd'] # True - if 'hotremove' in cpu_props and cpu_props['hotremove']: - config_spec.cpuHotRemoveEnabled = cpu_props['hotremove'] # True + """ + log.trace( + "Configuring virtual machine CPU " "settings cpu_props={0}".format(cpu_props) + ) + if "count" in cpu_props: + config_spec.numCPUs = int(cpu_props["count"]) + if "cores_per_socket" in cpu_props: + config_spec.numCoresPerSocket = int(cpu_props["cores_per_socket"]) + if "nested" in cpu_props and cpu_props["nested"]: + config_spec.nestedHVEnabled = cpu_props["nested"] # True + if "hotadd" in cpu_props and cpu_props["hotadd"]: + config_spec.cpuHotAddEnabled = cpu_props["hotadd"] # True + if "hotremove" in cpu_props and cpu_props["hotremove"]: + config_spec.cpuHotRemoveEnabled = cpu_props["hotremove"] # True def _apply_memory_config(config_spec, memory): - ''' + """ Sets memory size to the given value config_spec @@ -7424,31 +8152,32 @@ def _apply_memory_config(config_spec, memory): memory Memory size and unit - ''' - log.trace('Configuring virtual machine memory ' - 'settings memory={0}'.format(memory)) - if 'size' in memory and 'unit' in memory: + """ + log.trace( + "Configuring virtual machine memory " "settings memory={0}".format(memory) + ) + if "size" in memory and "unit" in memory: try: - if memory['unit'].lower() == 'kb': - memory_mb = memory['size'] / 1024 - elif memory['unit'].lower() == 'mb': - memory_mb = memory['size'] - elif memory['unit'].lower() == 'gb': - memory_mb = int(float(memory['size']) * 1024) + if memory["unit"].lower() == "kb": + memory_mb = memory["size"] / 1024 + elif memory["unit"].lower() == "mb": + memory_mb = memory["size"] + elif memory["unit"].lower() == "gb": + memory_mb = int(float(memory["size"]) * 1024) except (TypeError, ValueError): - memory_mb = int(memory['size']) + memory_mb = int(memory["size"]) config_spec.memoryMB = memory_mb - if 'reservation_max' in memory: - config_spec.memoryReservationLockedToMax = memory['reservation_max'] - if 'hotadd' in memory: - config_spec.memoryHotAddEnabled = memory['hotadd'] + if "reservation_max" in memory: + config_spec.memoryReservationLockedToMax = memory["reservation_max"] + if "hotadd" in memory: + config_spec.memoryHotAddEnabled = memory["hotadd"] @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy def get_advanced_configs(vm_name, datacenter, service_instance=None): - ''' + """ Returns extra config parameters from a virtual machine advanced config list vm_name @@ -7459,16 +8188,15 @@ def get_advanced_configs(vm_name, datacenter, service_instance=None): service_instance vCenter service instance for connection and configuration - ''' - current_config = get_vm_config(vm_name, - datacenter=datacenter, - objects=True, - service_instance=service_instance) - return current_config['advanced_configs'] + """ + current_config = get_vm_config( + vm_name, datacenter=datacenter, objects=True, service_instance=service_instance + ) + return current_config["advanced_configs"] def _apply_advanced_config(config_spec, advanced_config, vm_extra_config=None): - ''' + """ Sets configuration parameters for the vm config_spec @@ -7479,13 +8207,15 @@ def _apply_advanced_config(config_spec, advanced_config, vm_extra_config=None): vm_extra_config Virtual machine vm_ref.config.extraConfig object - ''' - log.trace('Configuring advanced configuration ' - 'parameters {0}'.format(advanced_config)) + """ + log.trace( + "Configuring advanced configuration " "parameters {0}".format(advanced_config) + ) if isinstance(advanced_config, str): raise salt.exceptions.ArgumentValueError( - 'The specified \'advanced_configs\' configuration ' - 'option cannot be parsed, please check the parameters') + "The specified 'advanced_configs' configuration " + "option cannot be parsed, please check the parameters" + ) for key, value in six.iteritems(advanced_config): if vm_extra_config: for option in vm_extra_config: @@ -7497,11 +8227,10 @@ def _apply_advanced_config(config_spec, advanced_config, vm_extra_config=None): @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def set_advanced_configs(vm_name, datacenter, advanced_configs, - service_instance=None): - ''' +def set_advanced_configs(vm_name, datacenter, advanced_configs, service_instance=None): + """ Appends extra config parameters to a virtual machine advanced config list vm_name @@ -7515,32 +8244,33 @@ def set_advanced_configs(vm_name, datacenter, advanced_configs, service_instance vCenter service instance for connection and configuration - ''' - current_config = get_vm_config(vm_name, - datacenter=datacenter, - objects=True, - service_instance=service_instance) - diffs = compare_vm_configs({'name': vm_name, - 'advanced_configs': advanced_configs}, - current_config) - datacenter_ref = salt.utils.vmware.get_datacenter(service_instance, - datacenter) - vm_ref = salt.utils.vmware.get_mor_by_property(service_instance, - vim.VirtualMachine, - vm_name, - property_name='name', - container_ref=datacenter_ref) + """ + current_config = get_vm_config( + vm_name, datacenter=datacenter, objects=True, service_instance=service_instance + ) + diffs = compare_vm_configs( + {"name": vm_name, "advanced_configs": advanced_configs}, current_config + ) + datacenter_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) + vm_ref = salt.utils.vmware.get_mor_by_property( + service_instance, + vim.VirtualMachine, + vm_name, + property_name="name", + container_ref=datacenter_ref, + ) config_spec = vim.vm.ConfigSpec() - changes = diffs['advanced_configs'].diffs - _apply_advanced_config(config_spec, diffs['advanced_configs'].new_values, - vm_ref.config.extraConfig) + changes = diffs["advanced_configs"].diffs + _apply_advanced_config( + config_spec, diffs["advanced_configs"].new_values, vm_ref.config.extraConfig + ) if changes: salt.utils.vmware.update_vm(vm_ref, config_spec) - return {'advanced_config_changes': changes} + return {"advanced_config_changes": changes} def _delete_advanced_config(config_spec, advanced_config, vm_extra_config): - ''' + """ Removes configuration parameters for the vm config_spec @@ -7551,29 +8281,32 @@ def _delete_advanced_config(config_spec, advanced_config, vm_extra_config): vm_extra_config Virtual machine vm_ref.config.extraConfig object - ''' - log.trace('Removing advanced configuration ' - 'parameters {0}'.format(advanced_config)) + """ + log.trace( + "Removing advanced configuration " "parameters {0}".format(advanced_config) + ) if isinstance(advanced_config, str): raise salt.exceptions.ArgumentValueError( - 'The specified \'advanced_configs\' configuration ' - 'option cannot be parsed, please check the parameters') + "The specified 'advanced_configs' configuration " + "option cannot be parsed, please check the parameters" + ) removed_configs = [] for key in advanced_config: for option in vm_extra_config: if option.key == key: - option = vim.option.OptionValue(key=key, value='') + option = vim.option.OptionValue(key=key, value="") config_spec.extraConfig.append(option) removed_configs.append(key) return removed_configs @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def delete_advanced_configs(vm_name, datacenter, advanced_configs, - service_instance=None): - ''' +def delete_advanced_configs( + vm_name, datacenter, advanced_configs, service_instance=None +): + """ Removes extra config parameters from a virtual machine vm_name @@ -7587,24 +8320,26 @@ def delete_advanced_configs(vm_name, datacenter, advanced_configs, service_instance vCenter service instance for connection and configuration - ''' - datacenter_ref = salt.utils.vmware.get_datacenter(service_instance, - datacenter) - vm_ref = salt.utils.vmware.get_mor_by_property(service_instance, - vim.VirtualMachine, - vm_name, - property_name='name', - container_ref=datacenter_ref) + """ + datacenter_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) + vm_ref = salt.utils.vmware.get_mor_by_property( + service_instance, + vim.VirtualMachine, + vm_name, + property_name="name", + container_ref=datacenter_ref, + ) config_spec = vim.vm.ConfigSpec() - removed_configs = _delete_advanced_config(config_spec, advanced_configs, - vm_ref.config.extraConfig) + removed_configs = _delete_advanced_config( + config_spec, advanced_configs, vm_ref.config.extraConfig + ) if removed_configs: salt.utils.vmware.update_vm(vm_ref, config_spec) - return {'removed_configs': removed_configs} + return {"removed_configs": removed_configs} def _get_scsi_controller_key(bus_number, scsi_ctrls): - ''' + """ Returns key number of the SCSI controller keys bus_number @@ -7612,22 +8347,34 @@ def _get_scsi_controller_key(bus_number, scsi_ctrls): scsi_ctrls List of SCSI Controller objects (old+newly created) - ''' + """ # list of new/old VirtualSCSIController objects, both new and old objects # should contain a key attribute key should be a negative integer in case # of a new object - keys = [ctrl.key for ctrl in scsi_ctrls if - scsi_ctrls and ctrl.busNumber == bus_number] + keys = [ + ctrl.key for ctrl in scsi_ctrls if scsi_ctrls and ctrl.busNumber == bus_number + ] if not keys: raise salt.exceptions.VMwareVmCreationError( - 'SCSI controller number {0} doesn\'t exist'.format(bus_number)) + "SCSI controller number {0} doesn't exist".format(bus_number) + ) return keys[0] -def _apply_hard_disk(unit_number, key, operation, disk_label=None, size=None, - unit='GB', controller_key=None, thin_provision=None, - eagerly_scrub=None, datastore=None, filename=None): - ''' +def _apply_hard_disk( + unit_number, + key, + operation, + disk_label=None, + size=None, + unit="GB", + controller_key=None, + thin_provision=None, + eagerly_scrub=None, + datastore=None, + filename=None, +): + """ Returns a vim.vm.device.VirtualDeviceSpec object specifying to add/edit a virtual disk device @@ -7663,14 +8410,22 @@ def _apply_hard_disk(unit_number, key, operation, disk_label=None, size=None, filename Full file name of the vm disk - ''' - log.trace('Configuring hard disk {0} size={1}, unit={2}, ' - 'controller_key={3}, thin_provision={4}, ' - 'eagerly_scrub={5}, datastore={6}, ' - 'filename={7}'.format(disk_label, size, unit, - controller_key, thin_provision, - eagerly_scrub, datastore, - filename)) + """ + log.trace( + "Configuring hard disk {0} size={1}, unit={2}, " + "controller_key={3}, thin_provision={4}, " + "eagerly_scrub={5}, datastore={6}, " + "filename={7}".format( + disk_label, + size, + unit, + controller_key, + thin_provision, + eagerly_scrub, + datastore, + filename, + ) + ) disk_spec = vim.vm.device.VirtualDeviceSpec() disk_spec.device = vim.vm.device.VirtualDisk() disk_spec.device.key = key @@ -7678,33 +8433,31 @@ def _apply_hard_disk(unit_number, key, operation, disk_label=None, size=None, disk_spec.device.deviceInfo = vim.Description() if size: convert_size = salt.utils.vmware.convert_to_kb(unit, size) - disk_spec.device.capacityInKB = convert_size['size'] + disk_spec.device.capacityInKB = convert_size["size"] if disk_label: disk_spec.device.deviceInfo.label = disk_label if thin_provision is not None or eagerly_scrub is not None: - disk_spec.device.backing = \ - vim.vm.device.VirtualDisk.FlatVer2BackingInfo() - disk_spec.device.backing.diskMode = 'persistent' + disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo() + disk_spec.device.backing.diskMode = "persistent" if thin_provision is not None: disk_spec.device.backing.thinProvisioned = thin_provision - if eagerly_scrub is not None and eagerly_scrub != 'None': + if eagerly_scrub is not None and eagerly_scrub != "None": disk_spec.device.backing.eagerlyScrub = eagerly_scrub if controller_key: disk_spec.device.controllerKey = controller_key - if operation == 'add': + if operation == "add": disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add - disk_spec.device.backing.fileName = '[{0}] {1}'.format( - salt.utils.vmware.get_managed_object_name(datastore), filename) - disk_spec.fileOperation = \ - vim.vm.device.VirtualDeviceSpec.FileOperation.create - elif operation == 'edit': + disk_spec.device.backing.fileName = "[{0}] {1}".format( + salt.utils.vmware.get_managed_object_name(datastore), filename + ) + disk_spec.fileOperation = vim.vm.device.VirtualDeviceSpec.FileOperation.create + elif operation == "edit": disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit return disk_spec -def _create_adapter_type(network_adapter, adapter_type, - network_adapter_label=''): - ''' +def _create_adapter_type(network_adapter, adapter_type, network_adapter_label=""): + """ Returns a vim.vm.device.VirtualEthernetCard object specifying a virtual ethernet card information @@ -7716,42 +8469,51 @@ def _create_adapter_type(network_adapter, adapter_type, network_adapter_label string, network adapter name - ''' - log.trace('Configuring virtual machine network ' - 'adapter adapter_type={0}'.format(adapter_type)) - if adapter_type in ['vmxnet', 'vmxnet2', 'vmxnet3', 'e1000', 'e1000e']: + """ + log.trace( + "Configuring virtual machine network " + "adapter adapter_type={0}".format(adapter_type) + ) + if adapter_type in ["vmxnet", "vmxnet2", "vmxnet3", "e1000", "e1000e"]: edited_network_adapter = salt.utils.vmware.get_network_adapter_type( - adapter_type) + adapter_type + ) if isinstance(network_adapter, type(edited_network_adapter)): edited_network_adapter = network_adapter else: if network_adapter: - log.trace('Changing type of \'{0}\' from' - ' \'{1}\' to \'{2}\''.format( - network_adapter.deviceInfo.label, - type(network_adapter).__name__.rsplit(".", 1)[1][7:].lower(), - adapter_type)) + log.trace( + "Changing type of '{0}' from" + " '{1}' to '{2}'".format( + network_adapter.deviceInfo.label, + type(network_adapter).__name__.rsplit(".", 1)[1][7:].lower(), + adapter_type, + ) + ) else: # If device is edited and type not specified or does not match, # don't change adapter type if network_adapter: if adapter_type: log.error( - 'Cannot change type of \'{0}\' to \'{1}\'. ' - 'Not changing type'.format(network_adapter.deviceInfo.label, - adapter_type)) + "Cannot change type of '{0}' to '{1}'. " + "Not changing type".format( + network_adapter.deviceInfo.label, adapter_type + ) + ) edited_network_adapter = network_adapter else: if not adapter_type: - log.trace('The type of \'{0}\' has not been specified. ' - 'Creating of default type \'vmxnet3\''.format( - network_adapter_label)) + log.trace( + "The type of '{0}' has not been specified. " + "Creating of default type 'vmxnet3'".format(network_adapter_label) + ) edited_network_adapter = vim.vm.device.VirtualVmxnet3() return edited_network_adapter def _create_network_backing(network_name, switch_type, parent_ref): - ''' + """ Returns a vim.vm.device.VirtualDevice.BackingInfo object specifying a virtual ethernet card backing information @@ -7763,47 +8525,62 @@ def _create_network_backing(network_name, switch_type, parent_ref): parent_ref Parent reference to search for network - ''' - log.trace('Configuring virtual machine network backing network_name={0} ' - 'switch_type={1} parent={2}'.format( - network_name, switch_type, - salt.utils.vmware.get_managed_object_name(parent_ref))) + """ + log.trace( + "Configuring virtual machine network backing network_name={0} " + "switch_type={1} parent={2}".format( + network_name, + switch_type, + salt.utils.vmware.get_managed_object_name(parent_ref), + ) + ) backing = {} if network_name: - if switch_type == 'standard': + if switch_type == "standard": networks = salt.utils.vmware.get_networks( - parent_ref, - network_names=[network_name]) + parent_ref, network_names=[network_name] + ) if not networks: raise salt.exceptions.VMwareObjectRetrievalError( - 'The network \'{0}\' could not be ' - 'retrieved.'.format(network_name)) + "The network '{0}' could not be " "retrieved.".format(network_name) + ) network_ref = networks[0] backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo() backing.deviceName = network_name backing.network = network_ref - elif switch_type == 'distributed': + elif switch_type == "distributed": networks = salt.utils.vmware.get_dvportgroups( - parent_ref, - portgroup_names=[network_name]) + parent_ref, portgroup_names=[network_name] + ) if not networks: raise salt.exceptions.VMwareObjectRetrievalError( - 'The port group \'{0}\' could not be ' - 'retrieved.'.format(network_name)) + "The port group '{0}' could not be " + "retrieved.".format(network_name) + ) network_ref = networks[0] dvs_port_connection = vim.dvs.PortConnection( portgroupKey=network_ref.key, - switchUuid=network_ref.config.distributedVirtualSwitch.uuid) - backing = \ + switchUuid=network_ref.config.distributedVirtualSwitch.uuid, + ) + backing = ( vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo() + ) backing.port = dvs_port_connection return backing -def _apply_network_adapter_config(key, network_name, adapter_type, switch_type, - network_adapter_label=None, operation='add', - connectable=None, mac=None, parent=None): - ''' +def _apply_network_adapter_config( + key, + network_name, + adapter_type, + switch_type, + network_adapter_label=None, + operation="add", + connectable=None, + mac=None, + parent=None, +): + """ Returns a vim.vm.device.VirtualDeviceSpec object specifying to add/edit a network device @@ -7833,33 +8610,29 @@ def _apply_network_adapter_config(key, network_name, adapter_type, switch_type, parent Parent object reference - ''' + """ adapter_type.strip().lower() switch_type.strip().lower() - log.trace('Configuring virtual machine network adapter ' - 'network_adapter_label={0} network_name={1} ' - 'adapter_type={2} switch_type={3} mac={4}'.format( - network_adapter_label, - network_name, - adapter_type, - switch_type, - mac)) + log.trace( + "Configuring virtual machine network adapter " + "network_adapter_label={0} network_name={1} " + "adapter_type={2} switch_type={3} mac={4}".format( + network_adapter_label, network_name, adapter_type, switch_type, mac + ) + ) network_spec = vim.vm.device.VirtualDeviceSpec() network_spec.device = _create_adapter_type( - network_spec.device, - adapter_type, - network_adapter_label=network_adapter_label) + network_spec.device, adapter_type, network_adapter_label=network_adapter_label + ) network_spec.device.deviceInfo = vim.Description() - if operation == 'add': - network_spec.operation = \ - vim.vm.device.VirtualDeviceSpec.Operation.add - elif operation == 'edit': - network_spec.operation = \ - vim.vm.device.VirtualDeviceSpec.Operation.edit + if operation == "add": + network_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add + elif operation == "edit": + network_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit if switch_type and network_name: - network_spec.device.backing = _create_network_backing(network_name, - switch_type, - parent) + network_spec.device.backing = _create_network_backing( + network_name, switch_type, parent + ) network_spec.device.deviceInfo.summary = network_name if key: # random negative integer for creations, concrete device key @@ -7869,21 +8642,21 @@ def _apply_network_adapter_config(key, network_name, adapter_type, switch_type, network_spec.device.deviceInfo.label = network_adapter_label if mac: network_spec.device.macAddress = mac - network_spec.device.addressType = 'Manual' + network_spec.device.addressType = "Manual" network_spec.device.wakeOnLanEnabled = True if connectable: - network_spec.device.connectable = \ - vim.vm.device.VirtualDevice.ConnectInfo() - network_spec.device.connectable.startConnected = \ - connectable['start_connected'] - network_spec.device.connectable.allowGuestControl = \ - connectable['allow_guest_control'] + network_spec.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo() + network_spec.device.connectable.startConnected = connectable["start_connected"] + network_spec.device.connectable.allowGuestControl = connectable[ + "allow_guest_control" + ] return network_spec -def _apply_scsi_controller(adapter, adapter_type, bus_sharing, key, - bus_number, operation): - ''' +def _apply_scsi_controller( + adapter, adapter_type, bus_sharing, key, bus_number, operation +): + """ Returns a vim.vm.device.VirtualDeviceSpec object specifying to add/edit a SCSI controller @@ -7912,70 +8685,76 @@ def _apply_scsi_controller(adapter, adapter_type, bus_sharing, key, adapter: 'SCSI controller 0' type: paravirtual or lsilogic or lsilogic_sas bus_sharing: 'no_sharing' or 'virtual_sharing' or 'physical_sharing' - ''' - log.trace('Configuring scsi controller adapter={0} adapter_type={1} ' - 'bus_sharing={2} key={3} bus_number={4}'.format( - adapter, adapter_type, bus_sharing, key, bus_number)) + """ + log.trace( + "Configuring scsi controller adapter={0} adapter_type={1} " + "bus_sharing={2} key={3} bus_number={4}".format( + adapter, adapter_type, bus_sharing, key, bus_number + ) + ) scsi_spec = vim.vm.device.VirtualDeviceSpec() - if adapter_type == 'lsilogic': - summary = 'LSI Logic' + if adapter_type == "lsilogic": + summary = "LSI Logic" scsi_spec.device = vim.vm.device.VirtualLsiLogicController() - elif adapter_type == 'lsilogic_sas': - summary = 'LSI Logic Sas' + elif adapter_type == "lsilogic_sas": + summary = "LSI Logic Sas" scsi_spec.device = vim.vm.device.VirtualLsiLogicSASController() - elif adapter_type == 'paravirtual': - summary = 'VMware paravirtual SCSI' + elif adapter_type == "paravirtual": + summary = "VMware paravirtual SCSI" scsi_spec.device = vim.vm.device.ParaVirtualSCSIController() - elif adapter_type == 'buslogic': - summary = 'Bus Logic' + elif adapter_type == "buslogic": + summary = "Bus Logic" scsi_spec.device = vim.vm.device.VirtualBusLogicController() - if operation == 'add': + if operation == "add": scsi_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add - elif operation == 'edit': + elif operation == "edit": scsi_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit scsi_spec.device.key = key scsi_spec.device.busNumber = bus_number scsi_spec.device.deviceInfo = vim.Description() scsi_spec.device.deviceInfo.label = adapter scsi_spec.device.deviceInfo.summary = summary - if bus_sharing == 'virtual_sharing': + if bus_sharing == "virtual_sharing": # Virtual disks can be shared between virtual machines on # the same server - scsi_spec.device.sharedBus = \ + scsi_spec.device.sharedBus = ( vim.vm.device.VirtualSCSIController.Sharing.virtualSharing - elif bus_sharing == 'physical_sharing': + ) + elif bus_sharing == "physical_sharing": # Virtual disks can be shared between virtual machines on any server - scsi_spec.device.sharedBus = \ + scsi_spec.device.sharedBus = ( vim.vm.device.VirtualSCSIController.Sharing.physicalSharing - elif bus_sharing == 'no_sharing': + ) + elif bus_sharing == "no_sharing": # Virtual disks cannot be shared between virtual machines - scsi_spec.device.sharedBus = \ + scsi_spec.device.sharedBus = ( vim.vm.device.VirtualSCSIController.Sharing.noSharing + ) return scsi_spec def _create_ide_controllers(ide_controllers): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec objects representing IDE controllers ide_controllers IDE properties - ''' + """ ide_ctrls = [] keys = range(-200, -250, -1) if ide_controllers: - devs = [ide['adapter'] for ide in ide_controllers] - log.trace('Creating IDE controllers {0}'.format(devs)) + devs = [ide["adapter"] for ide in ide_controllers] + log.trace("Creating IDE controllers {0}".format(devs)) for ide, key in zip(ide_controllers, keys): - ide_ctrls.append(_apply_ide_controller_config( - ide['adapter'], 'add', key, abs(key + 200))) + ide_ctrls.append( + _apply_ide_controller_config(ide["adapter"], "add", key, abs(key + 200)) + ) return ide_ctrls -def _apply_ide_controller_config(ide_controller_label, operation, - key, bus_number=0): - ''' +def _apply_ide_controller_config(ide_controller_label, operation, key, bus_number=0): + """ Returns a vim.vm.device.VirtualDeviceSpec object specifying to add/edit an IDE controller @@ -7990,14 +8769,16 @@ def _apply_ide_controller_config(ide_controller_label, operation, bus_number Device bus number property - ''' - log.trace('Configuring IDE controller ' - 'ide_controller_label={0}'.format(ide_controller_label)) + """ + log.trace( + "Configuring IDE controller " + "ide_controller_label={0}".format(ide_controller_label) + ) ide_spec = vim.vm.device.VirtualDeviceSpec() ide_spec.device = vim.vm.device.VirtualIDEController() - if operation == 'add': + if operation == "add": ide_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add - if operation == 'edit': + if operation == "edit": ide_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit ide_spec.device.key = key ide_spec.device.busNumber = bus_number @@ -8009,27 +8790,29 @@ def _apply_ide_controller_config(ide_controller_label, operation, def _create_sata_controllers(sata_controllers): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec objects representing SATA controllers sata_controllers SATA properties - ''' + """ sata_ctrls = [] keys = range(-15000, -15050, -1) if sata_controllers: - devs = [sata['adapter'] for sata in sata_controllers] - log.trace('Creating SATA controllers {0}'.format(devs)) + devs = [sata["adapter"] for sata in sata_controllers] + log.trace("Creating SATA controllers {0}".format(devs)) for sata, key in zip(sata_controllers, keys): - sata_ctrls.append(_apply_sata_controller_config( - sata['adapter'], 'add', key, sata['bus_number'])) + sata_ctrls.append( + _apply_sata_controller_config( + sata["adapter"], "add", key, sata["bus_number"] + ) + ) return sata_ctrls -def _apply_sata_controller_config(sata_controller_label, operation, - key, bus_number=0): - ''' +def _apply_sata_controller_config(sata_controller_label, operation, key, bus_number=0): + """ Returns a vim.vm.device.VirtualDeviceSpec object specifying to add/edit a SATA controller @@ -8044,14 +8827,16 @@ def _apply_sata_controller_config(sata_controller_label, operation, bus_number Device bus number property - ''' - log.trace('Configuring SATA controller ' - 'sata_controller_label={0}'.format(sata_controller_label)) + """ + log.trace( + "Configuring SATA controller " + "sata_controller_label={0}".format(sata_controller_label) + ) sata_spec = vim.vm.device.VirtualDeviceSpec() sata_spec.device = vim.vm.device.VirtualAHCIController() - if operation == 'add': + if operation == "add": sata_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add - elif operation == 'edit': + elif operation == "edit": sata_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit sata_spec.device.key = key sata_spec.device.controllerKey = 100 @@ -8063,10 +8848,18 @@ def _apply_sata_controller_config(sata_controller_label, operation, return sata_spec -def _apply_cd_drive(drive_label, key, device_type, operation, - client_device=None, datastore_iso_file=None, - connectable=None, controller_key=200, parent_ref=None): - ''' +def _apply_cd_drive( + drive_label, + key, + device_type, + operation, + client_device=None, + datastore_iso_file=None, + connectable=None, + controller_key=200, + parent_ref=None, +): + """ Returns a vim.vm.device.VirtualDeviceSpec object specifying to add/edit a CD/DVD drive @@ -8109,51 +8902,56 @@ def _apply_cd_drive(drive_label, key, device_type, operation, connectable: start_connected: True allow_guest_control: - ''' - log.trace('Configuring CD/DVD drive drive_label={0} ' - 'device_type={1} client_device={2} ' - 'datastore_iso_file={3}'.format( - drive_label, device_type, client_device, datastore_iso_file)) + """ + log.trace( + "Configuring CD/DVD drive drive_label={0} " + "device_type={1} client_device={2} " + "datastore_iso_file={3}".format( + drive_label, device_type, client_device, datastore_iso_file + ) + ) drive_spec = vim.vm.device.VirtualDeviceSpec() drive_spec.device = vim.vm.device.VirtualCdrom() drive_spec.device.deviceInfo = vim.Description() - if operation == 'add': + if operation == "add": drive_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add - elif operation == 'edit': + elif operation == "edit": drive_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit - if device_type == 'datastore_iso_file': + if device_type == "datastore_iso_file": drive_spec.device.backing = vim.vm.device.VirtualCdrom.IsoBackingInfo() - drive_spec.device.backing.fileName = datastore_iso_file['path'] - datastore = \ - datastore_iso_file['path'].partition('[')[-1].rpartition(']')[0] + drive_spec.device.backing.fileName = datastore_iso_file["path"] + datastore = datastore_iso_file["path"].partition("[")[-1].rpartition("]")[0] datastore_object = salt.utils.vmware.get_datastores( salt.utils.vmware.get_service_instance_from_managed_object(parent_ref), - parent_ref, datastore_names=[datastore])[0] + parent_ref, + datastore_names=[datastore], + )[0] if datastore_object: drive_spec.device.backing.datastore = datastore_object - drive_spec.device.deviceInfo.summary = \ - '{0}'.format(datastore_iso_file['path']) - elif device_type == 'client_device': - if client_device['mode'] == 'passthrough': - drive_spec.device.backing = \ + drive_spec.device.deviceInfo.summary = "{0}".format(datastore_iso_file["path"]) + elif device_type == "client_device": + if client_device["mode"] == "passthrough": + drive_spec.device.backing = ( vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo() - elif client_device['mode'] == 'atapi': - drive_spec.device.backing = \ + ) + elif client_device["mode"] == "atapi": + drive_spec.device.backing = ( vim.vm.device.VirtualCdrom.RemoteAtapiBackingInfo() + ) drive_spec.device.key = key drive_spec.device.deviceInfo.label = drive_label drive_spec.device.controllerKey = controller_key drive_spec.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo() if connectable: - drive_spec.device.connectable.startConnected = \ - connectable['start_connected'] - drive_spec.device.connectable.allowGuestControl = \ - connectable['allow_guest_control'] + drive_spec.device.connectable.startConnected = connectable["start_connected"] + drive_spec.device.connectable.allowGuestControl = connectable[ + "allow_guest_control" + ] return drive_spec def _set_network_adapter_mapping(domain, gateway, ip_addr, subnet_mask, mac): - ''' + """ Returns a vim.vm.customization.AdapterMapping object containing the IP properties of a network adapter card @@ -8171,7 +8969,7 @@ def _set_network_adapter_mapping(domain, gateway, ip_addr, subnet_mask, mac): mac MAC address of the guest - ''' + """ adapter_mapping = vim.vm.customization.AdapterMapping() adapter_mapping.macAddress = mac adapter_mapping.adapter = vim.vm.customization.IPSettings() @@ -8180,16 +8978,15 @@ def _set_network_adapter_mapping(domain, gateway, ip_addr, subnet_mask, mac): if gateway: adapter_mapping.adapter.gateway = gateway if ip_addr: - adapter_mapping.adapter.ip = \ - vim.vm.customization.FixedIp(ipAddress=ip_addr) + adapter_mapping.adapter.ip = vim.vm.customization.FixedIp(ipAddress=ip_addr) adapter_mapping.adapter.subnetMask = subnet_mask else: adapter_mapping.adapter.ip = vim.vm.customization.DhcpIpGenerator() return adapter_mapping -def _apply_serial_port(serial_device_spec, key, operation='add'): - ''' +def _apply_serial_port(serial_device_spec, key, operation="add"): + """ Returns a vim.vm.device.VirtualSerialPort representing a serial port component @@ -8215,53 +9012,57 @@ def _apply_serial_port(serial_device_spec, key, operation='add'): allow_guest_control: True start_connected: True yield: False - ''' - log.trace('Creating serial port adapter={0} type={1} connectable={2} ' - 'yield={3}'.format( - serial_device_spec['adapter'], serial_device_spec['type'], - serial_device_spec['connectable'], - serial_device_spec['yield'])) + """ + log.trace( + "Creating serial port adapter={0} type={1} connectable={2} " + "yield={3}".format( + serial_device_spec["adapter"], + serial_device_spec["type"], + serial_device_spec["connectable"], + serial_device_spec["yield"], + ) + ) device_spec = vim.vm.device.VirtualDeviceSpec() device_spec.device = vim.vm.device.VirtualSerialPort() - if operation == 'add': + if operation == "add": device_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add - elif operation == 'edit': + elif operation == "edit": device_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit connect_info = vim.vm.device.VirtualDevice.ConnectInfo() type_backing = None - if serial_device_spec['type'] == 'network': + if serial_device_spec["type"] == "network": type_backing = vim.vm.device.VirtualSerialPort.URIBackingInfo() - if 'uri' not in serial_device_spec['backing'].keys(): - raise ValueError('vSPC proxy URI not specified in config') - if 'uri' not in serial_device_spec['backing'].keys(): - raise ValueError('vSPC Direction not specified in config') - if 'filename' not in serial_device_spec['backing'].keys(): - raise ValueError('vSPC Filename not specified in config') - type_backing.proxyURI = serial_device_spec['backing']['uri'] - type_backing.direction = serial_device_spec['backing']['direction'] - type_backing.serviceURI = serial_device_spec['backing']['filename'] - if serial_device_spec['type'] == 'pipe': + if "uri" not in serial_device_spec["backing"].keys(): + raise ValueError("vSPC proxy URI not specified in config") + if "uri" not in serial_device_spec["backing"].keys(): + raise ValueError("vSPC Direction not specified in config") + if "filename" not in serial_device_spec["backing"].keys(): + raise ValueError("vSPC Filename not specified in config") + type_backing.proxyURI = serial_device_spec["backing"]["uri"] + type_backing.direction = serial_device_spec["backing"]["direction"] + type_backing.serviceURI = serial_device_spec["backing"]["filename"] + if serial_device_spec["type"] == "pipe": type_backing = vim.vm.device.VirtualSerialPort.PipeBackingInfo() - if serial_device_spec['type'] == 'file': + if serial_device_spec["type"] == "file": type_backing = vim.vm.device.VirtualSerialPort.FileBackingInfo() - if serial_device_spec['type'] == 'device': + if serial_device_spec["type"] == "device": type_backing = vim.vm.device.VirtualSerialPort.DeviceBackingInfo() - connect_info.allowGuestControl = \ - serial_device_spec['connectable']['allow_guest_control'] - connect_info.startConnected = \ - serial_device_spec['connectable']['start_connected'] + connect_info.allowGuestControl = serial_device_spec["connectable"][ + "allow_guest_control" + ] + connect_info.startConnected = serial_device_spec["connectable"]["start_connected"] device_spec.device.backing = type_backing device_spec.device.connectable = connect_info device_spec.device.unitNumber = 1 device_spec.device.key = key - device_spec.device.yieldOnPoll = serial_device_spec['yield'] + device_spec.device.yieldOnPoll = serial_device_spec["yield"] return device_spec def _create_disks(service_instance, disks, scsi_controllers=None, parent=None): - ''' + """ Returns a list of disk specs representing the disks to be created for a virtual machine @@ -8290,96 +9091,100 @@ def _create_disks(service_instance, disks, scsi_controllers=None, parent=None): eagerly_scrub: False datastore: 'myshare' filename: 'vm/mydisk.vmdk' - ''' + """ disk_specs = [] keys = range(-2000, -2050, -1) if disks: - devs = [disk['adapter'] for disk in disks] - log.trace('Creating disks {0}'.format(devs)) + devs = [disk["adapter"] for disk in disks] + log.trace("Creating disks {0}".format(devs)) for disk, key in zip(disks, keys): # create the disk filename, datastore, datastore_ref = None, None, None - size = float(disk['size']) + size = float(disk["size"]) # when creating both SCSI controller and Hard disk at the same time # we need the randomly assigned (temporary) key of the newly created # SCSI controller controller_key = 1000 # Default is the first SCSI controller - if 'address' in disk: # 0:0 - controller_bus_number, unit_number = disk['address'].split(':') + if "address" in disk: # 0:0 + controller_bus_number, unit_number = disk["address"].split(":") controller_bus_number = int(controller_bus_number) unit_number = int(unit_number) controller_key = _get_scsi_controller_key( - controller_bus_number, - scsi_ctrls=scsi_controllers) - elif 'controller' in disk: + controller_bus_number, scsi_ctrls=scsi_controllers + ) + elif "controller" in disk: for contr in scsi_controllers: - if contr['label'] == disk['controller']: - controller_key = contr['key'] + if contr["label"] == disk["controller"]: + controller_key = contr["key"] break else: raise salt.exceptions.VMwareObjectNotFoundError( - 'The given controller does not exist: ' - '{0}'.format(disk['controller'])) - if 'datastore' in disk: - datastore_ref = \ - salt.utils.vmware.get_datastores( - service_instance, parent, - datastore_names=[disk['datastore']])[0] - datastore = disk['datastore'] - if 'filename' in disk: - filename = disk['filename'] + "The given controller does not exist: " + "{0}".format(disk["controller"]) + ) + if "datastore" in disk: + datastore_ref = salt.utils.vmware.get_datastores( + service_instance, parent, datastore_names=[disk["datastore"]] + )[0] + datastore = disk["datastore"] + if "filename" in disk: + filename = disk["filename"] # XOR filename, datastore if (not filename and datastore) or (filename and not datastore): raise salt.exceptions.ArgumentValueError( - 'You must specify both filename and datastore attributes' - ' to place your disk to a specific datastore ' - '{0}, {1}'.format(datastore, filename)) + "You must specify both filename and datastore attributes" + " to place your disk to a specific datastore " + "{0}, {1}".format(datastore, filename) + ) disk_spec = _apply_hard_disk( unit_number, key, - disk_label=disk['adapter'], + disk_label=disk["adapter"], size=size, - unit=disk['unit'], + unit=disk["unit"], controller_key=controller_key, - operation='add', - thin_provision=disk['thin_provision'], - eagerly_scrub=disk['eagerly_scrub'] if 'eagerly_scrub' in disk else None, + operation="add", + thin_provision=disk["thin_provision"], + eagerly_scrub=disk["eagerly_scrub"] if "eagerly_scrub" in disk else None, datastore=datastore_ref, - filename=filename) + filename=filename, + ) disk_specs.append(disk_spec) unit_number += 1 return disk_specs def _create_scsi_devices(scsi_devices): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec objects representing SCSI controllers scsi_devices: List of SCSI device properties - ''' + """ keys = range(-1000, -1050, -1) scsi_specs = [] if scsi_devices: - devs = [scsi['adapter'] for scsi in scsi_devices] - log.trace('Creating SCSI devices {0}'.format(devs)) + devs = [scsi["adapter"] for scsi in scsi_devices] + log.trace("Creating SCSI devices {0}".format(devs)) # unitNumber for disk attachment, 0:0 1st 0 is the controller busNumber, # 2nd is the unitNumber for (key, scsi_controller) in zip(keys, scsi_devices): # create the SCSI controller - scsi_spec = _apply_scsi_controller(scsi_controller['adapter'], - scsi_controller['type'], - scsi_controller['bus_sharing'], - key, - scsi_controller['bus_number'], - 'add') + scsi_spec = _apply_scsi_controller( + scsi_controller["adapter"], + scsi_controller["type"], + scsi_controller["bus_sharing"], + key, + scsi_controller["bus_number"], + "add", + ) scsi_specs.append(scsi_spec) return scsi_specs def _create_network_adapters(network_interfaces, parent=None): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec objects representing the interfaces to be created for a virtual machine @@ -8397,55 +9202,61 @@ def _create_network_adapters(network_interfaces, parent=None): switch_type: distributed or standard adapter_type: vmxnet3 or vmxnet, vmxnet2, vmxnet3, e1000, e1000e mac: '00:11:22:33:44:55' - ''' + """ network_specs = [] nics_settings = [] keys = range(-4000, -4050, -1) if network_interfaces: - devs = [inter['adapter'] for inter in network_interfaces] - log.trace('Creating network interfaces {0}'.format(devs)) + devs = [inter["adapter"] for inter in network_interfaces] + log.trace("Creating network interfaces {0}".format(devs)) for interface, key in zip(network_interfaces, keys): network_spec = _apply_network_adapter_config( - key, interface['name'], - interface['adapter_type'], - interface['switch_type'], - network_adapter_label=interface['adapter'], - operation='add', - connectable=interface['connectable'] if 'connectable' in interface else None, - mac=interface['mac'], parent=parent) + key, + interface["name"], + interface["adapter_type"], + interface["switch_type"], + network_adapter_label=interface["adapter"], + operation="add", + connectable=interface["connectable"] + if "connectable" in interface + else None, + mac=interface["mac"], + parent=parent, + ) network_specs.append(network_spec) - if 'mapping' in interface: + if "mapping" in interface: adapter_mapping = _set_network_adapter_mapping( - interface['mapping']['domain'], - interface['mapping']['gateway'], - interface['mapping']['ip_addr'], - interface['mapping']['subnet_mask'], - interface['mac']) + interface["mapping"]["domain"], + interface["mapping"]["gateway"], + interface["mapping"]["ip_addr"], + interface["mapping"]["subnet_mask"], + interface["mac"], + ) nics_settings.append(adapter_mapping) return (network_specs, nics_settings) def _create_serial_ports(serial_ports): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec objects representing the serial ports to be created for a virtual machine serial_ports Serial port properties - ''' + """ ports = [] keys = range(-9000, -9050, -1) if serial_ports: - devs = [serial['adapter'] for serial in serial_ports] - log.trace('Creating serial ports {0}'.format(devs)) + devs = [serial["adapter"] for serial in serial_ports] + log.trace("Creating serial ports {0}".format(devs)) for port, key in zip(serial_ports, keys): - serial_port_device = _apply_serial_port(port, key, 'add') + serial_port_device = _apply_serial_port(port, key, "add") ports.append(serial_port_device) return ports def _create_cd_drives(cd_drives, controllers=None, parent_ref=None): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec objects representing the CD/DVD drives to be created for a virtual machine @@ -8457,36 +9268,44 @@ def _create_cd_drives(cd_drives, controllers=None, parent_ref=None): parent_ref Parent object reference - ''' + """ cd_drive_specs = [] keys = range(-3000, -3050, -1) if cd_drives: - devs = [dvd['adapter'] for dvd in cd_drives] - log.trace('Creating cd/dvd drives {0}'.format(devs)) + devs = [dvd["adapter"] for dvd in cd_drives] + log.trace("Creating cd/dvd drives {0}".format(devs)) for drive, key in zip(cd_drives, keys): # if a controller is not available/cannot be created we should use the # one which is available by default, this is 'IDE 0' controller_key = 200 if controllers: - controller = \ - _get_device_by_label(controllers, drive['controller']) + controller = _get_device_by_label(controllers, drive["controller"]) controller_key = controller.key - cd_drive_specs.append(_apply_cd_drive( - drive['adapter'], - key, - drive['device_type'], - 'add', - client_device=drive['client_device'] if 'client_device' in drive else None, - datastore_iso_file=drive['datastore_iso_file'] if 'datastore_iso_file' in drive else None, - connectable=drive['connectable'] if 'connectable' in drive else None, - controller_key=controller_key, - parent_ref=parent_ref)) + cd_drive_specs.append( + _apply_cd_drive( + drive["adapter"], + key, + drive["device_type"], + "add", + client_device=drive["client_device"] + if "client_device" in drive + else None, + datastore_iso_file=drive["datastore_iso_file"] + if "datastore_iso_file" in drive + else None, + connectable=drive["connectable"] + if "connectable" in drive + else None, + controller_key=controller_key, + parent_ref=parent_ref, + ) + ) return cd_drive_specs def _get_device_by_key(devices, key): - ''' + """ Returns the device with the given key, raises error if the device is not found. @@ -8495,18 +9314,18 @@ def _get_device_by_key(devices, key): key Unique key of device - ''' + """ device_keys = [d for d in devices if d.key == key] if device_keys: return device_keys[0] else: raise salt.exceptions.VMwareObjectNotFoundError( - 'Virtual machine device with unique key ' - '{0} does not exist'.format(key)) + "Virtual machine device with unique key " "{0} does not exist".format(key) + ) def _get_device_by_label(devices, label): - ''' + """ Returns the device with the given label, raises error if the device is not found. @@ -8515,35 +9334,36 @@ def _get_device_by_label(devices, label): key Unique key of device - ''' + """ device_labels = [d for d in devices if d.deviceInfo.label == label] if device_labels: return device_labels[0] else: raise salt.exceptions.VMwareObjectNotFoundError( - 'Virtual machine device with ' - 'label {0} does not exist'.format(label)) + "Virtual machine device with " "label {0} does not exist".format(label) + ) def _convert_units(devices): - ''' + """ Updates the size and unit dictionary values with the new unit values devices List of device data objects - ''' + """ if devices: for device in devices: - if 'unit' in device and 'size' in device: + if "unit" in device and "size" in device: device.update( - salt.utils.vmware.convert_to_kb(device['unit'], device['size'])) + salt.utils.vmware.convert_to_kb(device["unit"], device["size"]) + ) else: return False return True def compare_vm_configs(new_config, current_config): - ''' + """ Compares virtual machine current and new configuration, the current is the one which is deployed now, and the new is the target config. Returns the differences between the objects in a dictionary, the keys are the @@ -8555,83 +9375,82 @@ def compare_vm_configs(new_config, current_config): current_config Currently deployed configuration - ''' + """ diffs = {} keys = set(new_config.keys()) # These values identify the virtual machine, comparison is unnecessary - keys.discard('name') - keys.discard('datacenter') - keys.discard('datastore') - for property_key in ('version', 'image'): + keys.discard("name") + keys.discard("datacenter") + keys.discard("datastore") + for property_key in ("version", "image"): if property_key in keys: single_value_diff = recursive_diff( {property_key: current_config[property_key]}, - {property_key: new_config[property_key]}) + {property_key: new_config[property_key]}, + ) if single_value_diff.diffs: diffs[property_key] = single_value_diff keys.discard(property_key) - if 'cpu' in keys: - keys.remove('cpu') - cpu_diff = recursive_diff(current_config['cpu'], new_config['cpu']) + if "cpu" in keys: + keys.remove("cpu") + cpu_diff = recursive_diff(current_config["cpu"], new_config["cpu"]) if cpu_diff.diffs: - diffs['cpu'] = cpu_diff + diffs["cpu"] = cpu_diff - if 'memory' in keys: - keys.remove('memory') - _convert_units([current_config['memory']]) - _convert_units([new_config['memory']]) - memory_diff = recursive_diff(current_config['memory'], - new_config['memory']) + if "memory" in keys: + keys.remove("memory") + _convert_units([current_config["memory"]]) + _convert_units([new_config["memory"]]) + memory_diff = recursive_diff(current_config["memory"], new_config["memory"]) if memory_diff.diffs: - diffs['memory'] = memory_diff + diffs["memory"] = memory_diff - if 'advanced_configs' in keys: - keys.remove('advanced_configs') - key = 'advanced_configs' + if "advanced_configs" in keys: + keys.remove("advanced_configs") + key = "advanced_configs" advanced_diff = recursive_diff(current_config[key], new_config[key]) if advanced_diff.diffs: diffs[key] = advanced_diff - if 'disks' in keys: - keys.remove('disks') - _convert_units(current_config['disks']) - _convert_units(new_config['disks']) - disk_diffs = list_diff(current_config['disks'], - new_config['disks'], - 'address') + if "disks" in keys: + keys.remove("disks") + _convert_units(current_config["disks"]) + _convert_units(new_config["disks"]) + disk_diffs = list_diff(current_config["disks"], new_config["disks"], "address") # REMOVE UNSUPPORTED DIFFERENCES/CHANGES # If the disk already exist, the backing properties like eagerly scrub # and thin provisioning # cannot be updated, and should not be identified as differences - disk_diffs.remove_diff(diff_key='eagerly_scrub') + disk_diffs.remove_diff(diff_key="eagerly_scrub") # Filename updates are not supported yet, on VSAN datastores the # backing.fileName points to a uid + the vmdk name - disk_diffs.remove_diff(diff_key='filename') + disk_diffs.remove_diff(diff_key="filename") # The adapter name shouldn't be changed - disk_diffs.remove_diff(diff_key='adapter') + disk_diffs.remove_diff(diff_key="adapter") if disk_diffs.diffs: - diffs['disks'] = disk_diffs + diffs["disks"] = disk_diffs - if 'interfaces' in keys: - keys.remove('interfaces') - interface_diffs = list_diff(current_config['interfaces'], - new_config['interfaces'], 'mac') + if "interfaces" in keys: + keys.remove("interfaces") + interface_diffs = list_diff( + current_config["interfaces"], new_config["interfaces"], "mac" + ) # The adapter name shouldn't be changed - interface_diffs.remove_diff(diff_key='adapter') + interface_diffs.remove_diff(diff_key="adapter") if interface_diffs.diffs: - diffs['interfaces'] = interface_diffs + diffs["interfaces"] = interface_diffs # For general items where the identification can be done by adapter for key in keys: if key not in current_config or key not in new_config: - raise ValueError('A general device {0} configuration was ' - 'not supplied or it was not retrieved from ' - 'remote configuration'.format(key)) - device_diffs = list_diff(current_config[key], - new_config[key], - 'adapter') + raise ValueError( + "A general device {0} configuration was " + "not supplied or it was not retrieved from " + "remote configuration".format(key) + ) + device_diffs = list_diff(current_config[key], new_config[key], "adapter") if device_diffs.diffs: diffs[key] = device_diffs @@ -8640,7 +9459,7 @@ def compare_vm_configs(new_config, current_config): @gets_service_instance_via_proxy def get_vm_config(name, datacenter=None, objects=True, service_instance=None): - ''' + """ Queries and converts the virtual machine properties to the available format from the schema. If the objects attribute is True the config objects will have extra properties, like 'object' which will include the @@ -8659,205 +9478,222 @@ def get_vm_config(name, datacenter=None, objects=True, service_instance=None): service_instance vCenter service instance for connection and configuration - ''' - properties = ['config.hardware.device', - 'config.hardware.numCPU', - 'config.hardware.numCoresPerSocket', - 'config.nestedHVEnabled', - 'config.cpuHotAddEnabled', - 'config.cpuHotRemoveEnabled', - 'config.hardware.memoryMB', - 'config.memoryReservationLockedToMax', - 'config.memoryHotAddEnabled', - 'config.version', - 'config.guestId', - 'config.extraConfig', - 'name'] + """ + properties = [ + "config.hardware.device", + "config.hardware.numCPU", + "config.hardware.numCoresPerSocket", + "config.nestedHVEnabled", + "config.cpuHotAddEnabled", + "config.cpuHotRemoveEnabled", + "config.hardware.memoryMB", + "config.memoryReservationLockedToMax", + "config.memoryHotAddEnabled", + "config.version", + "config.guestId", + "config.extraConfig", + "name", + ] virtual_machine = salt.utils.vmware.get_vm_by_property( - service_instance, name, vm_properties=properties, datacenter=datacenter) + service_instance, name, vm_properties=properties, datacenter=datacenter + ) parent_ref = salt.utils.vmware.get_datacenter( - service_instance=service_instance, datacenter_name=datacenter) - current_config = {'name': name} - current_config['cpu'] = { - 'count': virtual_machine['config.hardware.numCPU'], - 'cores_per_socket': virtual_machine['config.hardware.numCoresPerSocket'], - 'nested': virtual_machine['config.nestedHVEnabled'], - 'hotadd': virtual_machine['config.cpuHotAddEnabled'], - 'hotremove': virtual_machine['config.cpuHotRemoveEnabled']} + service_instance=service_instance, datacenter_name=datacenter + ) + current_config = {"name": name} + current_config["cpu"] = { + "count": virtual_machine["config.hardware.numCPU"], + "cores_per_socket": virtual_machine["config.hardware.numCoresPerSocket"], + "nested": virtual_machine["config.nestedHVEnabled"], + "hotadd": virtual_machine["config.cpuHotAddEnabled"], + "hotremove": virtual_machine["config.cpuHotRemoveEnabled"], + } - current_config['memory'] = { - 'size': virtual_machine['config.hardware.memoryMB'], - 'unit': 'MB', - 'reservation_max': virtual_machine['config.memoryReservationLockedToMax'], - 'hotadd': virtual_machine['config.memoryHotAddEnabled']} - current_config['image'] = virtual_machine['config.guestId'] - current_config['version'] = virtual_machine['config.version'] - current_config['advanced_configs'] = {} - for extra_conf in virtual_machine['config.extraConfig']: + current_config["memory"] = { + "size": virtual_machine["config.hardware.memoryMB"], + "unit": "MB", + "reservation_max": virtual_machine["config.memoryReservationLockedToMax"], + "hotadd": virtual_machine["config.memoryHotAddEnabled"], + } + current_config["image"] = virtual_machine["config.guestId"] + current_config["version"] = virtual_machine["config.version"] + current_config["advanced_configs"] = {} + for extra_conf in virtual_machine["config.extraConfig"]: try: - current_config['advanced_configs'][extra_conf.key] = \ - int(extra_conf.value) + current_config["advanced_configs"][extra_conf.key] = int(extra_conf.value) except ValueError: - current_config['advanced_configs'][extra_conf.key] = \ - extra_conf.value + current_config["advanced_configs"][extra_conf.key] = extra_conf.value - current_config['disks'] = [] - current_config['scsi_devices'] = [] - current_config['interfaces'] = [] - current_config['serial_ports'] = [] - current_config['cd_drives'] = [] - current_config['sata_controllers'] = [] + current_config["disks"] = [] + current_config["scsi_devices"] = [] + current_config["interfaces"] = [] + current_config["serial_ports"] = [] + current_config["cd_drives"] = [] + current_config["sata_controllers"] = [] - for device in virtual_machine['config.hardware.device']: + for device in virtual_machine["config.hardware.device"]: if isinstance(device, vim.vm.device.VirtualSCSIController): controller = {} - controller['adapter'] = device.deviceInfo.label - controller['bus_number'] = device.busNumber + controller["adapter"] = device.deviceInfo.label + controller["bus_number"] = device.busNumber bus_sharing = device.sharedBus - if bus_sharing == 'noSharing': - controller['bus_sharing'] = 'no_sharing' - elif bus_sharing == 'virtualSharing': - controller['bus_sharing'] = 'virtual_sharing' - elif bus_sharing == 'physicalSharing': - controller['bus_sharing'] = 'physical_sharing' + if bus_sharing == "noSharing": + controller["bus_sharing"] = "no_sharing" + elif bus_sharing == "virtualSharing": + controller["bus_sharing"] = "virtual_sharing" + elif bus_sharing == "physicalSharing": + controller["bus_sharing"] = "physical_sharing" if isinstance(device, vim.vm.device.ParaVirtualSCSIController): - controller['type'] = 'paravirtual' + controller["type"] = "paravirtual" elif isinstance(device, vim.vm.device.VirtualBusLogicController): - controller['type'] = 'buslogic' + controller["type"] = "buslogic" elif isinstance(device, vim.vm.device.VirtualLsiLogicController): - controller['type'] = 'lsilogic' + controller["type"] = "lsilogic" elif isinstance(device, vim.vm.device.VirtualLsiLogicSASController): - controller['type'] = 'lsilogic_sas' + controller["type"] = "lsilogic_sas" if objects: # int list, stores the keys of the disks which are attached # to this controller - controller['device'] = device.device - controller['key'] = device.key - controller['object'] = device - current_config['scsi_devices'].append(controller) + controller["device"] = device.device + controller["key"] = device.key + controller["object"] = device + current_config["scsi_devices"].append(controller) if isinstance(device, vim.vm.device.VirtualDisk): disk = {} - disk['adapter'] = device.deviceInfo.label - disk['size'] = device.capacityInKB - disk['unit'] = 'KB' + disk["adapter"] = device.deviceInfo.label + disk["size"] = device.capacityInKB + disk["unit"] = "KB" controller = _get_device_by_key( - virtual_machine['config.hardware.device'], - device.controllerKey) - disk['controller'] = controller.deviceInfo.label - disk['address'] = \ - str(controller.busNumber) + ':' + str(device.unitNumber) - disk['datastore'] = salt.utils.vmware.get_managed_object_name( - device.backing.datastore) - disk['thin_provision'] = device.backing.thinProvisioned - disk['eagerly_scrub'] = device.backing.eagerlyScrub + virtual_machine["config.hardware.device"], device.controllerKey + ) + disk["controller"] = controller.deviceInfo.label + disk["address"] = str(controller.busNumber) + ":" + str(device.unitNumber) + disk["datastore"] = salt.utils.vmware.get_managed_object_name( + device.backing.datastore + ) + disk["thin_provision"] = device.backing.thinProvisioned + disk["eagerly_scrub"] = device.backing.eagerlyScrub if objects: - disk['key'] = device.key - disk['unit_number'] = device.unitNumber - disk['bus_number'] = controller.busNumber - disk['controller_key'] = device.controllerKey - disk['object'] = device - current_config['disks'].append(disk) + disk["key"] = device.key + disk["unit_number"] = device.unitNumber + disk["bus_number"] = controller.busNumber + disk["controller_key"] = device.controllerKey + disk["object"] = device + current_config["disks"].append(disk) if isinstance(device, vim.vm.device.VirtualEthernetCard): interface = {} - interface['adapter'] = device.deviceInfo.label - interface['adapter_type'] = \ - salt.utils.vmware.get_network_adapter_object_type(device) - interface['connectable'] = \ - {'allow_guest_control': device.connectable.allowGuestControl, - 'connected': device.connectable.connected, - 'start_connected': device.connectable.startConnected} - interface['mac'] = device.macAddress - if isinstance(device.backing, - vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo): - interface['switch_type'] = 'distributed' + interface["adapter"] = device.deviceInfo.label + interface[ + "adapter_type" + ] = salt.utils.vmware.get_network_adapter_object_type(device) + interface["connectable"] = { + "allow_guest_control": device.connectable.allowGuestControl, + "connected": device.connectable.connected, + "start_connected": device.connectable.startConnected, + } + interface["mac"] = device.macAddress + if isinstance( + device.backing, + vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo, + ): + interface["switch_type"] = "distributed" pg_key = device.backing.port.portgroupKey network_ref = salt.utils.vmware.get_mor_by_property( service_instance, vim.DistributedVirtualPortgroup, pg_key, - property_name='key', - container_ref=parent_ref) - elif isinstance(device.backing, - vim.vm.device.VirtualEthernetCard.NetworkBackingInfo): - interface['switch_type'] = 'standard' + property_name="key", + container_ref=parent_ref, + ) + elif isinstance( + device.backing, vim.vm.device.VirtualEthernetCard.NetworkBackingInfo + ): + interface["switch_type"] = "standard" network_ref = device.backing.network - interface['name'] = \ - salt.utils.vmware.get_managed_object_name(network_ref) + interface["name"] = salt.utils.vmware.get_managed_object_name(network_ref) if objects: - interface['key'] = device.key - interface['object'] = device - current_config['interfaces'].append(interface) + interface["key"] = device.key + interface["object"] = device + current_config["interfaces"].append(interface) if isinstance(device, vim.vm.device.VirtualCdrom): drive = {} - drive['adapter'] = device.deviceInfo.label + drive["adapter"] = device.deviceInfo.label controller = _get_device_by_key( - virtual_machine['config.hardware.device'], - device.controllerKey) - drive['controller'] = controller.deviceInfo.label - if isinstance(device.backing, - vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo): - drive['device_type'] = 'client_device' - drive['client_device'] = {'mode': 'passthrough'} - if isinstance(device.backing, - vim.vm.device.VirtualCdrom.RemoteAtapiBackingInfo): - drive['device_type'] = 'client_device' - drive['client_device'] = {'mode': 'atapi'} - if isinstance(device.backing, - vim.vm.device.VirtualCdrom.IsoBackingInfo): - drive['device_type'] = 'datastore_iso_file' - drive['datastore_iso_file'] = {'path': device.backing.fileName} - drive['connectable'] = \ - {'allow_guest_control': device.connectable.allowGuestControl, - 'connected': device.connectable.connected, - 'start_connected': device.connectable.startConnected} + virtual_machine["config.hardware.device"], device.controllerKey + ) + drive["controller"] = controller.deviceInfo.label + if isinstance( + device.backing, vim.vm.device.VirtualCdrom.RemotePassthroughBackingInfo + ): + drive["device_type"] = "client_device" + drive["client_device"] = {"mode": "passthrough"} + if isinstance( + device.backing, vim.vm.device.VirtualCdrom.RemoteAtapiBackingInfo + ): + drive["device_type"] = "client_device" + drive["client_device"] = {"mode": "atapi"} + if isinstance(device.backing, vim.vm.device.VirtualCdrom.IsoBackingInfo): + drive["device_type"] = "datastore_iso_file" + drive["datastore_iso_file"] = {"path": device.backing.fileName} + drive["connectable"] = { + "allow_guest_control": device.connectable.allowGuestControl, + "connected": device.connectable.connected, + "start_connected": device.connectable.startConnected, + } if objects: - drive['key'] = device.key - drive['controller_key'] = device.controllerKey - drive['object'] = device - current_config['cd_drives'].append(drive) + drive["key"] = device.key + drive["controller_key"] = device.controllerKey + drive["object"] = device + current_config["cd_drives"].append(drive) if isinstance(device, vim.vm.device.VirtualSerialPort): port = {} - port['adapter'] = device.deviceInfo.label - if isinstance(device.backing, - vim.vm.device.VirtualSerialPort.URIBackingInfo): - port['type'] = 'network' - port['backing'] = \ - {'uri': device.backing.proxyURI, - 'direction': device.backing.direction, - 'filename': device.backing.serviceURI} - if isinstance(device.backing, - vim.vm.device.VirtualSerialPort.PipeBackingInfo): - port['type'] = 'pipe' - if isinstance(device.backing, - vim.vm.device.VirtualSerialPort.FileBackingInfo): - port['type'] = 'file' - if isinstance(device.backing, - vim.vm.device.VirtualSerialPort.DeviceBackingInfo): - port['type'] = 'device' - port['yield'] = device.yieldOnPoll - port['connectable'] = \ - {'allow_guest_control': device.connectable.allowGuestControl, - 'connected': device.connectable.connected, - 'start_connected': device.connectable.startConnected} + port["adapter"] = device.deviceInfo.label + if isinstance( + device.backing, vim.vm.device.VirtualSerialPort.URIBackingInfo + ): + port["type"] = "network" + port["backing"] = { + "uri": device.backing.proxyURI, + "direction": device.backing.direction, + "filename": device.backing.serviceURI, + } + if isinstance( + device.backing, vim.vm.device.VirtualSerialPort.PipeBackingInfo + ): + port["type"] = "pipe" + if isinstance( + device.backing, vim.vm.device.VirtualSerialPort.FileBackingInfo + ): + port["type"] = "file" + if isinstance( + device.backing, vim.vm.device.VirtualSerialPort.DeviceBackingInfo + ): + port["type"] = "device" + port["yield"] = device.yieldOnPoll + port["connectable"] = { + "allow_guest_control": device.connectable.allowGuestControl, + "connected": device.connectable.connected, + "start_connected": device.connectable.startConnected, + } if objects: - port['key'] = device.key - port['object'] = device - current_config['serial_ports'].append(port) + port["key"] = device.key + port["object"] = device + current_config["serial_ports"].append(port) if isinstance(device, vim.vm.device.VirtualSATAController): sata = {} - sata['adapter'] = device.deviceInfo.label - sata['bus_number'] = device.busNumber + sata["adapter"] = device.deviceInfo.label + sata["bus_number"] = device.busNumber if objects: - sata['device'] = device.device # keys of the connected devices - sata['key'] = device.key - sata['object'] = device - current_config['sata_controllers'].append(sata) + sata["device"] = device.device # keys of the connected devices + sata["key"] = device.key + sata["object"] = device + current_config["sata_controllers"].append(sata) return current_config def _update_disks(disks_old_new): - ''' + """ Changes the disk size and returns the config spec objects in a list. The controller property cannot be updated, because controller address identifies the disk by the unit and bus number properties. @@ -8865,110 +9701,127 @@ def _update_disks(disks_old_new): disks_diffs List of old and new disk properties, the properties are dictionary objects - ''' + """ disk_changes = [] if disks_old_new: - devs = [disk['old']['address'] for disk in disks_old_new] - log.trace('Updating disks {0}'.format(devs)) + devs = [disk["old"]["address"] for disk in disks_old_new] + log.trace("Updating disks {0}".format(devs)) for item in disks_old_new: - current_disk = item['old'] - next_disk = item['new'] + current_disk = item["old"] + next_disk = item["new"] difference = recursive_diff(current_disk, next_disk) difference.ignore_unset_values = False if difference.changed(): - if next_disk['size'] < current_disk['size']: + if next_disk["size"] < current_disk["size"]: raise salt.exceptions.VMwareSaltError( - 'Disk cannot be downsized size={0} unit={1} ' - 'controller_key={2} ' - 'unit_number={3}'.format( - next_disk['size'], - next_disk['unit'], - current_disk['controller_key'], - current_disk['unit_number'])) - log.trace('Virtual machine disk will be updated ' - 'size={0} unit={1} controller_key={2} ' - 'unit_number={3}'.format( - next_disk['size'], - next_disk['unit'], - current_disk['controller_key'], - current_disk['unit_number'])) + "Disk cannot be downsized size={0} unit={1} " + "controller_key={2} " + "unit_number={3}".format( + next_disk["size"], + next_disk["unit"], + current_disk["controller_key"], + current_disk["unit_number"], + ) + ) + log.trace( + "Virtual machine disk will be updated " + "size={0} unit={1} controller_key={2} " + "unit_number={3}".format( + next_disk["size"], + next_disk["unit"], + current_disk["controller_key"], + current_disk["unit_number"], + ) + ) device_config_spec = _apply_hard_disk( - current_disk['unit_number'], - current_disk['key'], 'edit', - size=next_disk['size'], - unit=next_disk['unit'], - controller_key=current_disk['controller_key']) + current_disk["unit_number"], + current_disk["key"], + "edit", + size=next_disk["size"], + unit=next_disk["unit"], + controller_key=current_disk["controller_key"], + ) # The backing didn't change and we must supply one for # reconfigure - device_config_spec.device.backing = \ - current_disk['object'].backing + device_config_spec.device.backing = current_disk["object"].backing disk_changes.append(device_config_spec) return disk_changes def _update_scsi_devices(scsis_old_new, current_disks): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec specifying the scsi properties as input the old and new configs are defined in a dictionary. scsi_diffs List of old and new scsi properties - ''' + """ device_config_specs = [] if scsis_old_new: - devs = [scsi['old']['adapter'] for scsi in scsis_old_new] - log.trace('Updating SCSI controllers {0}'.format(devs)) + devs = [scsi["old"]["adapter"] for scsi in scsis_old_new] + log.trace("Updating SCSI controllers {0}".format(devs)) for item in scsis_old_new: - next_scsi = item['new'] - current_scsi = item['old'] + next_scsi = item["new"] + current_scsi = item["old"] difference = recursive_diff(current_scsi, next_scsi) difference.ignore_unset_values = False if difference.changed(): - log.trace('Virtual machine scsi device will be updated ' - 'key={0} bus_number={1} type={2} ' - 'bus_sharing={3}'.format(current_scsi['key'], - current_scsi['bus_number'], - next_scsi['type'], - next_scsi['bus_sharing'])) + log.trace( + "Virtual machine scsi device will be updated " + "key={0} bus_number={1} type={2} " + "bus_sharing={3}".format( + current_scsi["key"], + current_scsi["bus_number"], + next_scsi["type"], + next_scsi["bus_sharing"], + ) + ) # The sharedBus property is not optional # The type can only be updated if we delete the original # controller, create a new one with the properties and then # attach the disk object to the newly created controller, even # though the controller key stays the same the last step is # mandatory - if next_scsi['type'] != current_scsi['type']: + if next_scsi["type"] != current_scsi["type"]: + device_config_specs.append(_delete_device(current_scsi["object"])) device_config_specs.append( - _delete_device(current_scsi['object'])) - device_config_specs.append(_apply_scsi_controller( - current_scsi['adapter'], - next_scsi['type'], - next_scsi['bus_sharing'], - current_scsi['key'], - current_scsi['bus_number'], 'add')) + _apply_scsi_controller( + current_scsi["adapter"], + next_scsi["type"], + next_scsi["bus_sharing"], + current_scsi["key"], + current_scsi["bus_number"], + "add", + ) + ) disks_to_update = [] - for disk_key in current_scsi['device']: - disk_objects = \ - [disk['object'] for disk in current_disks] + for disk_key in current_scsi["device"]: + disk_objects = [disk["object"] for disk in current_disks] disks_to_update.append( - _get_device_by_key(disk_objects, disk_key)) + _get_device_by_key(disk_objects, disk_key) + ) for current_disk in disks_to_update: disk_spec = vim.vm.device.VirtualDeviceSpec() disk_spec.device = current_disk - disk_spec.operation = 'edit' + disk_spec.operation = "edit" device_config_specs.append(disk_spec) else: - device_config_specs.append(_apply_scsi_controller( - current_scsi['adapter'], - current_scsi['type'], - next_scsi['bus_sharing'], - current_scsi['key'], - current_scsi['bus_number'], 'edit')) + device_config_specs.append( + _apply_scsi_controller( + current_scsi["adapter"], + current_scsi["type"], + next_scsi["bus_sharing"], + current_scsi["key"], + current_scsi["bus_number"], + "edit", + ) + ) return device_config_specs def _update_network_adapters(interface_old_new, parent): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec specifying configuration(s) for changed network adapters, the adapter type cannot be changed, as input the old and new configs are defined in a dictionary. @@ -8979,62 +9832,67 @@ def _update_network_adapters(interface_old_new, parent): parent Parent managed object reference - ''' + """ network_changes = [] if interface_old_new: - devs = [inter['old']['mac'] for inter in interface_old_new] - log.trace('Updating network interfaces {0}'.format(devs)) + devs = [inter["old"]["mac"] for inter in interface_old_new] + log.trace("Updating network interfaces {0}".format(devs)) for item in interface_old_new: - current_interface = item['old'] - next_interface = item['new'] + current_interface = item["old"] + next_interface = item["new"] difference = recursive_diff(current_interface, next_interface) difference.ignore_unset_values = False if difference.changed(): - log.trace('Virtual machine network adapter will be updated ' - 'switch_type={0} name={1} adapter_type={2} ' - 'mac={3}'.format(next_interface['switch_type'], - next_interface['name'], - current_interface['adapter_type'], - current_interface['mac'])) + log.trace( + "Virtual machine network adapter will be updated " + "switch_type={0} name={1} adapter_type={2} " + "mac={3}".format( + next_interface["switch_type"], + next_interface["name"], + current_interface["adapter_type"], + current_interface["mac"], + ) + ) device_config_spec = _apply_network_adapter_config( - current_interface['key'], - next_interface['name'], - current_interface['adapter_type'], - next_interface['switch_type'], - operation='edit', - mac=current_interface['mac'], - parent=parent) + current_interface["key"], + next_interface["name"], + current_interface["adapter_type"], + next_interface["switch_type"], + operation="edit", + mac=current_interface["mac"], + parent=parent, + ) network_changes.append(device_config_spec) return network_changes def _update_serial_ports(serial_old_new): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec specifying to edit a deployed serial port configuration to the new given config serial_old_new Dictionary with old and new keys which contains the current and the next config for a serial port device - ''' + """ serial_changes = [] if serial_old_new: - devs = [serial['old']['adapter'] for serial in serial_old_new] - log.trace('Updating serial ports {0}'.format(devs)) + devs = [serial["old"]["adapter"] for serial in serial_old_new] + log.trace("Updating serial ports {0}".format(devs)) for item in serial_old_new: - current_serial = item['old'] - next_serial = item['new'] + current_serial = item["old"] + next_serial = item["new"] difference = recursive_diff(current_serial, next_serial) difference.ignore_unset_values = False if difference.changed(): - serial_changes.append(_apply_serial_port(next_serial, - current_serial['key'], - 'edit')) + serial_changes.append( + _apply_serial_port(next_serial, current_serial["key"], "edit") + ) return serial_changes def _update_cd_drives(drives_old_new, controllers=None, parent=None): - ''' + """ Returns a list of vim.vm.device.VirtualDeviceSpec specifying to edit a deployed cd drive configuration to the new given config @@ -9047,44 +9905,53 @@ def _update_cd_drives(drives_old_new, controllers=None, parent=None): parent Managed object reference of the parent object - ''' + """ cd_changes = [] if drives_old_new: - devs = [drive['old']['adapter'] for drive in drives_old_new] - log.trace('Updating cd/dvd drives {0}'.format(devs)) + devs = [drive["old"]["adapter"] for drive in drives_old_new] + log.trace("Updating cd/dvd drives {0}".format(devs)) for item in drives_old_new: - current_drive = item['old'] - new_drive = item['new'] + current_drive = item["old"] + new_drive = item["new"] difference = recursive_diff(current_drive, new_drive) difference.ignore_unset_values = False if difference.changed(): if controllers: - controller = _get_device_by_label(controllers, - new_drive['controller']) + controller = _get_device_by_label( + controllers, new_drive["controller"] + ) controller_key = controller.key else: - controller_key = current_drive['controller_key'] - cd_changes.append(_apply_cd_drive( - current_drive['adapter'], - current_drive['key'], - new_drive['device_type'], 'edit', - client_device=new_drive['client_device'] if 'client_device' in new_drive else None, - datastore_iso_file=new_drive['datastore_iso_file'] if 'datastore_iso_file' in new_drive else None, - connectable=new_drive['connectable'], - controller_key=controller_key, - parent_ref=parent)) + controller_key = current_drive["controller_key"] + cd_changes.append( + _apply_cd_drive( + current_drive["adapter"], + current_drive["key"], + new_drive["device_type"], + "edit", + client_device=new_drive["client_device"] + if "client_device" in new_drive + else None, + datastore_iso_file=new_drive["datastore_iso_file"] + if "datastore_iso_file" in new_drive + else None, + connectable=new_drive["connectable"], + controller_key=controller_key, + parent_ref=parent, + ) + ) return cd_changes def _delete_device(device): - ''' + """ Returns a vim.vm.device.VirtualDeviceSpec specifying to remove a virtual machine device device Device data type object - ''' - log.trace('Deleting device with type {0}'.format(type(device))) + """ + log.trace("Deleting device with type {0}".format(type(device))) device_spec = vim.vm.device.VirtualDeviceSpec() device_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.remove device_spec.device = device @@ -9092,7 +9959,7 @@ def _delete_device(device): def _get_client(server, username, password): - ''' + """ Establish client through proxy or with user provided credentials. :param basestring server: @@ -9105,29 +9972,30 @@ def _get_client(server, username, password): vSphere Client instance. :rtype: vSphere.Client - ''' + """ # Get salted vSphere Client if not (server and username and password): # User didn't provide CLI args so use proxy information - details = __salt__['vcenter.get_details']() - server = details['vcenter'] - username = details['username'] - password = details['password'] + details = __salt__["vcenter.get_details"]() + server = details["vcenter"] + username = details["username"] + password = details["password"] # Establish connection with client - client = salt.utils.vmware.get_vsphere_client(server=server, - username=username, - password=password) + client = salt.utils.vmware.get_vsphere_client( + server=server, username=username, password=password + ) # Will return None if utility function causes Unauthenticated error return client @depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) -@supports_proxies('vcenter') +@supports_proxies("vcenter") @gets_service_instance_via_proxy -def list_tag_categories(server=None, username=None, password=None, - service_instance=None): - ''' +def list_tag_categories( + server=None, username=None, password=None, service_instance=None +): + """ List existing categories a user has access to. CLI Example: @@ -9146,21 +10014,20 @@ def list_tag_categories(server=None, username=None, password=None, Value(s) of category_id. :rtype: list of str - ''' + """ categories = None client = _get_client(server, username, password) if client: categories = client.tagging.Category.list() - return {'Categories': categories} + return {"Categories": categories} @depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) -@supports_proxies('vcenter') +@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): + """ List existing tags a user has access to. CLI Example: @@ -9179,23 +10046,28 @@ def list_tags(server=None, username=None, password=None, Value(s) of tag_id. :rtype: list of str - ''' + """ tags = None client = _get_client(server, username, password) if client: tags = client.tagging.Tag.list() - return {'Tags': tags} + return {"Tags": tags} @depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) -@supports_proxies('vcenter') +@supports_proxies("vcenter") @gets_service_instance_via_proxy -def attach_tag(object_id, tag_id, - managed_obj='ClusterComputeResource', - server=None, username=None, password=None, - service_instance=None): - ''' +def attach_tag( + object_id, + tag_id, + managed_obj="ClusterComputeResource", + server=None, + username=None, + password=None, + service_instance=None, +): + """ Attach an existing tag to an input object. The tag needs to meet the cardinality (`CategoryModel.cardinality`) and @@ -9235,7 +10107,7 @@ def attach_tag(object_id, tag_id, if you do not have the privilege to read the object. :raise: Unauthenticated if the user can not be authenticated. - ''' + """ tag_attached = None client = _get_client(server, username, password) @@ -9250,21 +10122,28 @@ def attach_tag(object_id, tag_id, dynamic_id = DynamicID(type=managed_obj, id=object_id) try: tag_attached = client.tagging.TagAssociation.attach( - tag_id=tag_id, object_id=dynamic_id) + tag_id=tag_id, object_id=dynamic_id + ) except vsphere_errors: - log.warning('Unable to attach tag. Check user privileges and' - ' object_id (must be a string).') - return {'Tag attached': tag_attached} + log.warning( + "Unable to attach tag. Check user privileges and" + " object_id (must be a string)." + ) + return {"Tag attached": tag_attached} @depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) -@supports_proxies('vcenter') +@supports_proxies("vcenter") @gets_service_instance_via_proxy -def list_attached_tags(object_id, - managed_obj='ClusterComputeResource', - server=None, username=None, password=None, - service_instance=None): - ''' +def list_attached_tags( + object_id, + managed_obj="ClusterComputeResource", + server=None, + username=None, + password=None, + service_instance=None, +): + """ List existing tags a user has access to. CLI Example: @@ -9294,7 +10173,7 @@ def list_attached_tags(object_id, if you do not have the privilege to read the object. :raise: Unauthenticated if the user can not be authenticated. - ''' + """ attached_tags = None client = _get_client(server, username, password) @@ -9308,21 +10187,28 @@ def list_attached_tags(object_id, # resource for use by virtual machines. dynamic_id = DynamicID(type=managed_obj, id=object_id) try: - attached_tags = client.tagging.TagAssociation.list_attached_tags( - dynamic_id) + attached_tags = client.tagging.TagAssociation.list_attached_tags(dynamic_id) except vsphere_errors: - log.warning('Unable to list attached tags. Check user privileges' - ' and object_id (must be a string).') - return {'Attached tags': attached_tags} + log.warning( + "Unable to list attached tags. Check user privileges" + " and object_id (must be a string)." + ) + return {"Attached tags": attached_tags} @depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) -@supports_proxies('vcenter') +@supports_proxies("vcenter") @gets_service_instance_via_proxy -def create_tag_category(name, description, cardinality, - server=None, username=None, password=None, - service_instance=None): - ''' +def create_tag_category( + name, + description, + cardinality, + server=None, + username=None, + password=None, + service_instance=None, +): + """ Create a category with given cardinality. CLI Example: @@ -9354,14 +10240,14 @@ def create_tag_category(name, description, cardinality, if any of the information in the create_spec is invalid. :raise: Unauthorized if you do not have the privilege to create a category. - ''' + """ category_created = None client = _get_client(server, username, password) if client: - if cardinality == 'SINGLE': + if cardinality == "SINGLE": cardinality = CategoryModel.Cardinality.SINGLE - elif cardinality == 'MULTIPLE': + elif cardinality == "MULTIPLE": cardinality = CategoryModel.Cardinality.MULTIPLE else: # Cardinality must be supplied correctly @@ -9376,18 +10262,20 @@ def create_tag_category(name, description, cardinality, try: category_created = client.tagging.Category.create(create_spec) except vsphere_errors: - log.warning('Unable to create tag category. Check user privilege' - ' and see if category exists.') - return {'Category created': category_created} + log.warning( + "Unable to create tag category. Check user privilege" + " and see if category exists." + ) + return {"Category created": category_created} @depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) -@supports_proxies('vcenter') +@supports_proxies("vcenter") @gets_service_instance_via_proxy -def delete_tag_category(category_id, - server=None, username=None, password=None, - service_instance=None): - ''' +def delete_tag_category( + category_id, server=None, username=None, password=None, service_instance=None +): + """ Delete a category. CLI Example: @@ -9412,7 +10300,7 @@ def delete_tag_category(category_id, if you do not have the privilege to delete the tag. :raise: Unauthenticated if the user can not be authenticated. - ''' + """ category_deleted = None client = _get_client(server, username, password) @@ -9420,18 +10308,26 @@ def delete_tag_category(category_id, try: category_deleted = client.tagging.Category.delete(category_id) except vsphere_errors: - log.warning('Unable to delete tag category. Check user privilege' - ' and see if category exists.') - return {'Category deleted': category_deleted} + log.warning( + "Unable to delete tag category. Check user privilege" + " and see if category exists." + ) + return {"Category deleted": category_deleted} @depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) -@supports_proxies('vcenter') +@supports_proxies("vcenter") @gets_service_instance_via_proxy -def create_tag(name, description, category_id, - server=None, username=None, password=None, - service_instance=None): - ''' +def create_tag( + name, + description, + category_id, + server=None, + username=None, + password=None, + service_instance=None, +): + """ Create a tag under a category with given description. CLI Example: @@ -9466,7 +10362,7 @@ def create_tag(name, description, category_id, the system. :raise: Unauthorized if you do not have the privilege to create tag. - ''' + """ tag_created = None client = _get_client(server, username, password) @@ -9478,18 +10374,20 @@ def create_tag(name, description, category_id, try: tag_created = client.tagging.Tag.create(create_spec) except vsphere_errors: - log.warning('Unable to create tag. Check user privilege and see' - ' if category exists.') - return {'Tag created': tag_created} + log.warning( + "Unable to create tag. Check user privilege and see" + " if category exists." + ) + return {"Tag created": tag_created} @depends(HAS_PYVMOMI, HAS_VSPHERE_SDK) -@supports_proxies('vcenter') +@supports_proxies("vcenter") @gets_service_instance_via_proxy -def delete_tag(tag_id, - server=None, username=None, password=None, - service_instance=None): - ''' +def delete_tag( + tag_id, server=None, username=None, password=None, service_instance=None +): + """ Delete a tag. CLI Example: @@ -9515,7 +10413,7 @@ def delete_tag(tag_id, if any of the information in the create_spec is invalid. :raise: Unauthorized if you do not have the privilege to create a category. - ''' + """ tag_deleted = None client = _get_client(server, username, password) @@ -9523,19 +10421,36 @@ def delete_tag(tag_id, try: tag_deleted = client.tagging.Tag.delete(tag_id) except vsphere_errors: - log.warning('Unable to delete category. Check user privileges' - ' and that category exists.') - return {'Tag deleted': tag_deleted} + log.warning( + "Unable to delete category. Check user privileges" + " and that category exists." + ) + return {"Tag deleted": tag_deleted} @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def create_vm(vm_name, cpu, memory, image, version, datacenter, datastore, - placement, interfaces, disks, scsi_devices, serial_ports=None, - ide_controllers=None, sata_controllers=None, cd_drives=None, - advanced_configs=None, service_instance=None): - ''' +def create_vm( + vm_name, + cpu, + memory, + image, + version, + datacenter, + datastore, + placement, + interfaces, + disks, + scsi_devices, + serial_ports=None, + ide_controllers=None, + sata_controllers=None, + cd_drives=None, + advanced_configs=None, + service_instance=None, +): + """ Creates a virtual machine container. CLI Example: @@ -9644,16 +10559,16 @@ def create_vm(vm_name, cpu, memory, image, version, datacenter, datastore, advanced_config Advanced config parameters to be set for the virtual machine - ''' + """ # If datacenter is specified, set the container reference to start search # from it instead - container_object = salt.utils.vmware.get_datacenter(service_instance, - datacenter) + container_object = salt.utils.vmware.get_datacenter(service_instance, datacenter) (resourcepool_object, placement_object) = salt.utils.vmware.get_placement( - service_instance, datacenter, placement=placement) - folder_object = salt.utils.vmware.get_folder(service_instance, - datacenter, - placement) + service_instance, datacenter, placement=placement + ) + folder_object = salt.utils.vmware.get_folder( + service_instance, datacenter, placement + ) # Create the config specs config_spec = vim.vm.ConfigSpec() config_spec.name = vm_name @@ -9662,31 +10577,33 @@ def create_vm(vm_name, cpu, memory, image, version, datacenter, datastore, # For VSAN disks we need to specify a different vm path name, the vm file # full path cannot be used - datastore_object = \ - salt.utils.vmware.get_datastores(service_instance, - placement_object, - datastore_names=[datastore])[0] + datastore_object = salt.utils.vmware.get_datastores( + service_instance, placement_object, datastore_names=[datastore] + )[0] if not datastore_object: raise salt.exceptions.ArgumentValueError( - 'Specified datastore: \'{0}\' does not exist.'.format(datastore)) + "Specified datastore: '{0}' does not exist.".format(datastore) + ) try: - ds_summary = \ - salt.utils.vmware.get_properties_of_managed_object(datastore_object, - 'summary.type') - if 'summary.type' in ds_summary and ds_summary['summary.type'] == 'vsan': - log.trace('The vmPathName should be the datastore ' - 'name if the datastore type is vsan') - config_spec.files.vmPathName = '[{0}]'.format(datastore) + ds_summary = salt.utils.vmware.get_properties_of_managed_object( + datastore_object, "summary.type" + ) + if "summary.type" in ds_summary and ds_summary["summary.type"] == "vsan": + log.trace( + "The vmPathName should be the datastore " + "name if the datastore type is vsan" + ) + config_spec.files.vmPathName = "[{0}]".format(datastore) else: - config_spec.files.vmPathName = '[{0}] {1}/{1}.vmx'.format(datastore, - vm_name) + config_spec.files.vmPathName = "[{0}] {1}/{1}.vmx".format( + datastore, vm_name + ) except salt.exceptions.VMwareApiError: - config_spec.files.vmPathName = '[{0}] {1}/{1}.vmx'.format(datastore, - vm_name) + config_spec.files.vmPathName = "[{0}] {1}/{1}.vmx".format(datastore, vm_name) cd_controllers = [] if version: - _apply_hardware_version(version, config_spec, 'add') + _apply_hardware_version(version, config_spec, "add") if cpu: _apply_cpu_config(config_spec, cpu) if memory: @@ -9696,14 +10613,17 @@ def create_vm(vm_name, cpu, memory, image, version, datacenter, datastore, config_spec.deviceChange.extend(scsi_specs) if disks: scsi_controllers = [spec.device for spec in scsi_specs] - disk_specs = _create_disks(service_instance, - disks, - scsi_controllers=scsi_controllers, - parent=container_object) + disk_specs = _create_disks( + service_instance, + disks, + scsi_controllers=scsi_controllers, + parent=container_object, + ) config_spec.deviceChange.extend(disk_specs) if interfaces: (interface_specs, nic_settings) = _create_network_adapters( - interfaces, parent=container_object) + interfaces, parent=container_object + ) config_spec.deviceChange.extend(interface_specs) if serial_ports: serial_port_specs = _create_serial_ports(serial_ports) @@ -9717,27 +10637,40 @@ def create_vm(vm_name, cpu, memory, image, version, datacenter, datastore, config_spec.deviceChange.extend(sata_specs) cd_controllers.extend(sata_specs) if cd_drives: - cd_drive_specs = _create_cd_drives(cd_drives, - controllers=cd_controllers, - parent_ref=container_object) + cd_drive_specs = _create_cd_drives( + cd_drives, controllers=cd_controllers, parent_ref=container_object + ) config_spec.deviceChange.extend(cd_drive_specs) if advanced_configs: _apply_advanced_config(config_spec, advanced_configs) - salt.utils.vmware.create_vm(vm_name, config_spec, folder_object, - resourcepool_object, placement_object) + salt.utils.vmware.create_vm( + vm_name, config_spec, folder_object, resourcepool_object, placement_object + ) - return {'create_vm': True} + return {"create_vm": True} @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def update_vm(vm_name, cpu=None, memory=None, image=None, version=None, - interfaces=None, disks=None, scsi_devices=None, - serial_ports=None, datacenter=None, datastore=None, - cd_dvd_drives=None, sata_controllers=None, advanced_configs=None, - service_instance=None): - ''' +def update_vm( + vm_name, + cpu=None, + memory=None, + image=None, + version=None, + interfaces=None, + disks=None, + scsi_devices=None, + serial_ports=None, + datacenter=None, + datastore=None, + cd_dvd_drives=None, + sata_controllers=None, + advanced_configs=None, + service_instance=None, +): + """ Updates the configuration of the virtual machine if the config differs vm_name @@ -9782,120 +10715,134 @@ def update_vm(vm_name, cpu=None, memory=None, image=None, version=None, service_instance vCenter service instance for connection and configuration - ''' - current_config = get_vm_config(vm_name, - datacenter=datacenter, - objects=True, - service_instance=service_instance) - diffs = compare_vm_configs({'name': vm_name, - 'cpu': cpu, - 'memory': memory, - 'image': image, - 'version': version, - 'interfaces': interfaces, - 'disks': disks, - 'scsi_devices': scsi_devices, - 'serial_ports': serial_ports, - 'datacenter': datacenter, - 'datastore': datastore, - 'cd_drives': cd_dvd_drives, - 'sata_controllers': sata_controllers, - 'advanced_configs': advanced_configs}, - current_config) + """ + current_config = get_vm_config( + vm_name, datacenter=datacenter, objects=True, service_instance=service_instance + ) + diffs = compare_vm_configs( + { + "name": vm_name, + "cpu": cpu, + "memory": memory, + "image": image, + "version": version, + "interfaces": interfaces, + "disks": disks, + "scsi_devices": scsi_devices, + "serial_ports": serial_ports, + "datacenter": datacenter, + "datastore": datastore, + "cd_drives": cd_dvd_drives, + "sata_controllers": sata_controllers, + "advanced_configs": advanced_configs, + }, + current_config, + ) config_spec = vim.vm.ConfigSpec() - datacenter_ref = salt.utils.vmware.get_datacenter(service_instance, - datacenter) - vm_ref = salt.utils.vmware.get_mor_by_property(service_instance, - vim.VirtualMachine, - vm_name, - property_name='name', - container_ref=datacenter_ref) + datacenter_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) + vm_ref = salt.utils.vmware.get_mor_by_property( + service_instance, + vim.VirtualMachine, + vm_name, + property_name="name", + container_ref=datacenter_ref, + ) difference_keys = diffs.keys() - if 'cpu' in difference_keys: - if diffs['cpu'].changed() != set(): - _apply_cpu_config(config_spec, diffs['cpu'].current_dict) - if 'memory' in difference_keys: - if diffs['memory'].changed() != set(): - _apply_memory_config(config_spec, diffs['memory'].current_dict) - if 'advanced_configs' in difference_keys: - _apply_advanced_config(config_spec, - diffs['advanced_configs'].new_values, - vm_ref.config.extraConfig) - if 'version' in difference_keys: - _apply_hardware_version(version, config_spec, 'edit') - if 'image' in difference_keys: + if "cpu" in difference_keys: + if diffs["cpu"].changed() != set(): + _apply_cpu_config(config_spec, diffs["cpu"].current_dict) + if "memory" in difference_keys: + if diffs["memory"].changed() != set(): + _apply_memory_config(config_spec, diffs["memory"].current_dict) + if "advanced_configs" in difference_keys: + _apply_advanced_config( + config_spec, diffs["advanced_configs"].new_values, vm_ref.config.extraConfig + ) + if "version" in difference_keys: + _apply_hardware_version(version, config_spec, "edit") + if "image" in difference_keys: config_spec.guestId = image new_scsi_devices = [] - if 'scsi_devices' in difference_keys and 'disks' in current_config: + if "scsi_devices" in difference_keys and "disks" in current_config: scsi_changes = [] scsi_changes.extend( - _update_scsi_devices(diffs['scsi_devices'].intersect, - current_config['disks'])) - for item in diffs['scsi_devices'].removed: - scsi_changes.append(_delete_device(item['object'])) - new_scsi_devices = _create_scsi_devices(diffs['scsi_devices'].added) + _update_scsi_devices( + diffs["scsi_devices"].intersect, current_config["disks"] + ) + ) + for item in diffs["scsi_devices"].removed: + scsi_changes.append(_delete_device(item["object"])) + new_scsi_devices = _create_scsi_devices(diffs["scsi_devices"].added) scsi_changes.extend(new_scsi_devices) config_spec.deviceChange.extend(scsi_changes) - if 'disks' in difference_keys: + if "disks" in difference_keys: disk_changes = [] - disk_changes.extend(_update_disks(diffs['disks'].intersect)) - for item in diffs['disks'].removed: - disk_changes.append(_delete_device(item['object'])) + disk_changes.extend(_update_disks(diffs["disks"].intersect)) + for item in diffs["disks"].removed: + disk_changes.append(_delete_device(item["object"])) # We will need the existing and new controllers as well - scsi_controllers = [dev['object'] - for dev in current_config['scsi_devices']] - scsi_controllers.extend([device_spec.device - for device_spec in new_scsi_devices]) + scsi_controllers = [dev["object"] for dev in current_config["scsi_devices"]] + scsi_controllers.extend( + [device_spec.device for device_spec in new_scsi_devices] + ) disk_changes.extend( - _create_disks(service_instance, - diffs['disks'].added, - scsi_controllers=scsi_controllers, - parent=datacenter_ref)) + _create_disks( + service_instance, + diffs["disks"].added, + scsi_controllers=scsi_controllers, + parent=datacenter_ref, + ) + ) config_spec.deviceChange.extend(disk_changes) - if 'interfaces' in difference_keys: + if "interfaces" in difference_keys: network_changes = [] network_changes.extend( - _update_network_adapters(diffs['interfaces'].intersect, - datacenter_ref)) - for item in diffs['interfaces'].removed: - network_changes.append(_delete_device(item['object'])) - (adapters, nics) = _create_network_adapters(diffs['interfaces'].added, - datacenter_ref) + _update_network_adapters(diffs["interfaces"].intersect, datacenter_ref) + ) + for item in diffs["interfaces"].removed: + network_changes.append(_delete_device(item["object"])) + (adapters, nics) = _create_network_adapters( + diffs["interfaces"].added, datacenter_ref + ) network_changes.extend(adapters) config_spec.deviceChange.extend(network_changes) - if 'serial_ports' in difference_keys: + if "serial_ports" in difference_keys: serial_changes = [] - serial_changes.extend( - _update_serial_ports(diffs['serial_ports'].intersect)) - for item in diffs['serial_ports'].removed: - serial_changes.append(_delete_device(item['object'])) - serial_changes.extend( - _create_serial_ports(diffs['serial_ports'].added)) + serial_changes.extend(_update_serial_ports(diffs["serial_ports"].intersect)) + for item in diffs["serial_ports"].removed: + serial_changes.append(_delete_device(item["object"])) + serial_changes.extend(_create_serial_ports(diffs["serial_ports"].added)) config_spec.deviceChange.extend(serial_changes) new_controllers = [] - if 'sata_controllers' in difference_keys: + if "sata_controllers" in difference_keys: # SATA controllers don't have many properties, it does not make sense # to update them - sata_specs = _create_sata_controllers(diffs['sata_controllers'].added) - for item in diffs['sata_controllers'].removed: - sata_specs.append(_delete_device(item['object'])) + sata_specs = _create_sata_controllers(diffs["sata_controllers"].added) + for item in diffs["sata_controllers"].removed: + sata_specs.append(_delete_device(item["object"])) new_controllers.extend(sata_specs) config_spec.deviceChange.extend(sata_specs) - if 'cd_drives' in difference_keys: + if "cd_drives" in difference_keys: cd_changes = [] - controllers = [dev['object'] - for dev in current_config['sata_controllers']] - controllers.extend([device_spec.device - for device_spec in new_controllers]) - cd_changes.extend(_update_cd_drives(diffs['cd_drives'].intersect, - controllers=controllers, - parent=datacenter_ref)) - for item in diffs['cd_drives'].removed: - cd_changes.append(_delete_device(item['object'])) - cd_changes.extend(_create_cd_drives(diffs['cd_drives'].added, - controllers=controllers, - parent_ref=datacenter_ref)) + controllers = [dev["object"] for dev in current_config["sata_controllers"]] + controllers.extend([device_spec.device for device_spec in new_controllers]) + cd_changes.extend( + _update_cd_drives( + diffs["cd_drives"].intersect, + controllers=controllers, + parent=datacenter_ref, + ) + ) + for item in diffs["cd_drives"].removed: + cd_changes.append(_delete_device(item["object"])) + cd_changes.extend( + _create_cd_drives( + diffs["cd_drives"].added, + controllers=controllers, + parent_ref=datacenter_ref, + ) + ) config_spec.deviceChange.extend(cd_changes) if difference_keys: @@ -9906,20 +10853,20 @@ def update_vm(vm_name, cpu=None, memory=None, image=None, version=None, # and update actions, we will need to delete these before we summarize # the changes for the users if isinstance(properties, salt.utils.listdiffer.ListDictDiffer): - properties.remove_diff(diff_key='object', diff_list='intersect') - properties.remove_diff(diff_key='key', diff_list='intersect') - properties.remove_diff(diff_key='object', diff_list='removed') - properties.remove_diff(diff_key='key', diff_list='removed') + properties.remove_diff(diff_key="object", diff_list="intersect") + properties.remove_diff(diff_key="key", diff_list="intersect") + properties.remove_diff(diff_key="object", diff_list="removed") + properties.remove_diff(diff_key="key", diff_list="removed") changes[key] = properties.diffs return changes @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy def register_vm(name, datacenter, placement, vmx_path, service_instance=None): - ''' + """ Registers a virtual machine to the inventory with the given vmx file. Returns comments and change list @@ -9938,64 +10885,69 @@ def register_vm(name, datacenter, placement, vmx_path, service_instance=None): service_instance Service instance (vim.ServiceInstance) of the vCenter. Default is None. - ''' - log.trace('Registering virtual machine with properties ' - 'datacenter={0}, placement={1}, ' - 'vmx_path={2}'.format(datacenter, placement, vmx_path)) - datacenter_object = salt.utils.vmware.get_datacenter(service_instance, - datacenter) - if 'cluster' in placement: - cluster_obj = salt.utils.vmware.get_cluster(datacenter_object, - placement['cluster']) - cluster_props = \ - salt.utils.vmware.get_properties_of_managed_object( - cluster_obj, properties=['resourcePool']) - if 'resourcePool' in cluster_props: - resourcepool = cluster_props['resourcePool'] + """ + log.trace( + "Registering virtual machine with properties " + "datacenter={0}, placement={1}, " + "vmx_path={2}".format(datacenter, placement, vmx_path) + ) + datacenter_object = salt.utils.vmware.get_datacenter(service_instance, datacenter) + if "cluster" in placement: + cluster_obj = salt.utils.vmware.get_cluster( + datacenter_object, placement["cluster"] + ) + cluster_props = salt.utils.vmware.get_properties_of_managed_object( + cluster_obj, properties=["resourcePool"] + ) + if "resourcePool" in cluster_props: + resourcepool = cluster_props["resourcePool"] else: raise salt.exceptions.VMwareObjectRetrievalError( - 'The cluster\'s resource pool object could not be retrieved.') - salt.utils.vmware.register_vm(datacenter_object, - name, - vmx_path, - resourcepool) - elif 'host' in placement: - hosts = salt.utils.vmware.get_hosts(service_instance, - datacenter_name=datacenter, - host_names=[placement['host']]) + "The cluster's resource pool object could not be retrieved." + ) + salt.utils.vmware.register_vm(datacenter_object, name, vmx_path, resourcepool) + elif "host" in placement: + hosts = salt.utils.vmware.get_hosts( + service_instance, datacenter_name=datacenter, host_names=[placement["host"]] + ) if not hosts: raise salt.exceptions.VMwareObjectRetrievalError( - 'ESXi host named \'{0}\' wasn\'t found.'.format(placement['host'])) + "ESXi host named '{0}' wasn't found.".format(placement["host"]) + ) host_obj = hosts[0] host_props = salt.utils.vmware.get_properties_of_managed_object( - host_obj, properties=['parent']) - if 'parent' in host_props: - host_parent = host_props['parent'] + host_obj, properties=["parent"] + ) + if "parent" in host_props: + host_parent = host_props["parent"] parent = salt.utils.vmware.get_properties_of_managed_object( - host_parent, properties=['parent']) - if 'parent' in parent: - resourcepool = parent['parent'] + host_parent, properties=["parent"] + ) + if "parent" in parent: + resourcepool = parent["parent"] else: raise salt.exceptions.VMwareObjectRetrievalError( - 'The host parent\'s parent object could not be retrieved.') + "The host parent's parent object could not be retrieved." + ) else: raise salt.exceptions.VMwareObjectRetrievalError( - 'The host\'s parent object could not be retrieved.') - salt.utils.vmware.register_vm(datacenter_object, - name, - vmx_path, - resourcepool, - host_object=host_obj) - result = {'comment': 'Virtual machine registration action succeeded', - 'changes': {'register_vm': True}} + "The host's parent object could not be retrieved." + ) + salt.utils.vmware.register_vm( + datacenter_object, name, vmx_path, resourcepool, host_object=host_obj + ) + result = { + "comment": "Virtual machine registration action succeeded", + "changes": {"register_vm": True}, + } return result @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy def power_on_vm(name, datacenter=None, service_instance=None): - ''' + """ Powers on a virtual machine specified by its name. name @@ -10012,32 +10964,31 @@ def power_on_vm(name, datacenter=None, service_instance=None): salt '*' vsphere.power_on_vm name=my_vm - ''' - log.trace('Powering on virtual machine {0}'.format(name)) - vm_properties = [ - 'name', - 'summary.runtime.powerState' - ] + """ + log.trace("Powering on virtual machine {0}".format(name)) + vm_properties = ["name", "summary.runtime.powerState"] virtual_machine = salt.utils.vmware.get_vm_by_property( - service_instance, - name, - datacenter=datacenter, - vm_properties=vm_properties) - if virtual_machine['summary.runtime.powerState'] == 'poweredOn': - result = {'comment': 'Virtual machine is already powered on', - 'changes': {'power_on': True}} + service_instance, name, datacenter=datacenter, vm_properties=vm_properties + ) + if virtual_machine["summary.runtime.powerState"] == "poweredOn": + result = { + "comment": "Virtual machine is already powered on", + "changes": {"power_on": True}, + } return result - salt.utils.vmware.power_cycle_vm(virtual_machine['object'], action='on') - result = {'comment': 'Virtual machine power on action succeeded', - 'changes': {'power_on': True}} + salt.utils.vmware.power_cycle_vm(virtual_machine["object"], action="on") + result = { + "comment": "Virtual machine power on action succeeded", + "changes": {"power_on": True}, + } return result @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy def power_off_vm(name, datacenter=None, service_instance=None): - ''' + """ Powers off a virtual machine specified by its name. name @@ -10054,30 +11005,28 @@ def power_off_vm(name, datacenter=None, service_instance=None): salt '*' vsphere.power_off_vm name=my_vm - ''' - log.trace('Powering off virtual machine {0}'.format(name)) - vm_properties = [ - 'name', - 'summary.runtime.powerState' - ] + """ + log.trace("Powering off virtual machine {0}".format(name)) + vm_properties = ["name", "summary.runtime.powerState"] virtual_machine = salt.utils.vmware.get_vm_by_property( - service_instance, - name, - datacenter=datacenter, - vm_properties=vm_properties) - if virtual_machine['summary.runtime.powerState'] == 'poweredOff': - result = {'comment': 'Virtual machine is already powered off', - 'changes': {'power_off': True}} + service_instance, name, datacenter=datacenter, vm_properties=vm_properties + ) + if virtual_machine["summary.runtime.powerState"] == "poweredOff": + result = { + "comment": "Virtual machine is already powered off", + "changes": {"power_off": True}, + } return result - salt.utils.vmware.power_cycle_vm(virtual_machine['object'], action='off') - result = {'comment': 'Virtual machine power off action succeeded', - 'changes': {'power_off': True}} + salt.utils.vmware.power_cycle_vm(virtual_machine["object"], action="off") + result = { + "comment": "Virtual machine power off action succeeded", + "changes": {"power_off": True}, + } return result -def _remove_vm(name, datacenter, service_instance, placement=None, - power_off=None): - ''' +def _remove_vm(name, datacenter, service_instance, placement=None, power_off=None): + """ Helper function to remove a virtual machine name @@ -10091,38 +11040,39 @@ def _remove_vm(name, datacenter, service_instance, placement=None, placement Placement information of the virtual machine - ''' + """ results = {} if placement: - (resourcepool_object, placement_object) = \ - salt.utils.vmware.get_placement(service_instance, - datacenter, - placement) + (resourcepool_object, placement_object) = salt.utils.vmware.get_placement( + service_instance, datacenter, placement + ) else: - placement_object = salt.utils.vmware.get_datacenter(service_instance, - datacenter) + placement_object = salt.utils.vmware.get_datacenter( + service_instance, datacenter + ) if power_off: power_off_vm(name, datacenter, service_instance) - results['powered_off'] = True + results["powered_off"] = True vm_ref = salt.utils.vmware.get_mor_by_property( service_instance, vim.VirtualMachine, name, - property_name='name', - container_ref=placement_object) + property_name="name", + container_ref=placement_object, + ) if not vm_ref: raise salt.exceptions.VMwareObjectRetrievalError( - 'The virtual machine object {0} in datacenter ' - '{1} was not found'.format(name, datacenter)) + "The virtual machine object {0} in datacenter " + "{1} was not found".format(name, datacenter) + ) return results, vm_ref @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def delete_vm(name, datacenter, placement=None, power_off=False, - service_instance=None): - ''' +def delete_vm(name, datacenter, placement=None, power_off=False, service_instance=None): + """ Deletes a virtual machine defined by name and placement name @@ -10141,32 +11091,34 @@ def delete_vm(name, datacenter, placement=None, power_off=False, salt '*' vsphere.delete_vm name=my_vm datacenter=my_datacenter - ''' + """ results = {} schema = ESXVirtualMachineDeleteSchema.serialize() try: - jsonschema.validate({'name': name, - 'datacenter': datacenter, - 'placement': placement}, - schema) + jsonschema.validate( + {"name": name, "datacenter": datacenter, "placement": placement}, schema + ) except jsonschema.exceptions.ValidationError as exc: raise InvalidConfigError(exc) - (results, vm_ref) = _remove_vm(name, - datacenter, - service_instance=service_instance, - placement=placement, - power_off=power_off) + (results, vm_ref) = _remove_vm( + name, + datacenter, + service_instance=service_instance, + placement=placement, + power_off=power_off, + ) salt.utils.vmware.delete_vm(vm_ref) - results['deleted_vm'] = True + results["deleted_vm"] = True return results @depends(HAS_PYVMOMI) -@supports_proxies('esxvm', 'esxcluster', 'esxdatacenter') +@supports_proxies("esxvm", "esxcluster", "esxdatacenter") @gets_service_instance_via_proxy -def unregister_vm(name, datacenter, placement=None, power_off=False, - service_instance=None): - ''' +def unregister_vm( + name, datacenter, placement=None, power_off=False, service_instance=None +): + """ Unregisters a virtual machine defined by name and placement name @@ -10185,21 +11137,22 @@ def unregister_vm(name, datacenter, placement=None, power_off=False, salt '*' vsphere.unregister_vm name=my_vm datacenter=my_datacenter - ''' + """ results = {} schema = ESXVirtualMachineUnregisterSchema.serialize() try: - jsonschema.validate({'name': name, - 'datacenter': datacenter, - 'placement': placement}, - schema) + jsonschema.validate( + {"name": name, "datacenter": datacenter, "placement": placement}, schema + ) except jsonschema.exceptions.ValidationError as exc: raise InvalidConfigError(exc) - (results, vm_ref) = _remove_vm(name, - datacenter, - service_instance=service_instance, - placement=placement, - power_off=power_off) + (results, vm_ref) = _remove_vm( + name, + datacenter, + service_instance=service_instance, + placement=placement, + power_off=power_off, + ) salt.utils.vmware.unregister_vm(vm_ref) - results['unregistered_vm'] = True + results["unregistered_vm"] = True return results diff --git a/salt/modules/webutil.py b/salt/modules/webutil.py index 972b21df549..7ddecd6c5c7 100644 --- a/salt/modules/webutil.py +++ b/salt/modules/webutil.py @@ -1,37 +1,41 @@ # -*- coding: utf-8 -*- -''' +""" Support for htpasswd command. Requires the apache2-utils package for Debian-based distros. .. versionadded:: 2014.1.0 The functions here will load inside the webutil module. This allows other functions that don't use htpasswd to use the webutil module name. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import logging +import os # Import salt libs import salt.utils.path log = logging.getLogger(__name__) -__virtualname__ = 'webutil' +__virtualname__ = "webutil" def __virtual__(): - ''' + """ Only load the module if htpasswd is installed - ''' - if salt.utils.path.which('htpasswd'): + """ + if salt.utils.path.which("htpasswd"): return __virtualname__ - return (False, 'The htpasswd execution mdule cannot be loaded: htpasswd binary not in path.') + return ( + False, + "The htpasswd execution mdule cannot be loaded: htpasswd binary not in path.", + ) -def useradd(pwfile, user, password, opts='', runas=None): - ''' +def useradd(pwfile, user, password, opts="", runas=None): + """ Add a user to htpasswd file using the htpasswd command. If the htpasswd file does not exist, it will be created. @@ -62,16 +66,16 @@ def useradd(pwfile, user, password, opts='', runas=None): salt '*' webutil.useradd /etc/httpd/htpasswd larry badpassword salt '*' webutil.useradd /etc/httpd/htpasswd larry badpass opts=ns - ''' + """ if not os.path.exists(pwfile): - opts += 'c' + opts += "c" - cmd = ['htpasswd', '-b{0}'.format(opts), pwfile, user, password] - return __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False) + cmd = ["htpasswd", "-b{0}".format(opts), pwfile, user, password] + return __salt__["cmd.run_all"](cmd, runas=runas, python_shell=False) def userdel(pwfile, user, runas=None, all_results=False): - ''' + """ Delete a user from the specified htpasswd file. pwfile @@ -91,23 +95,22 @@ def userdel(pwfile, user, runas=None, all_results=False): .. code-block:: bash salt '*' webutil.userdel /etc/httpd/htpasswd larry - ''' + """ if not os.path.exists(pwfile): - return 'Error: The specified htpasswd file does not exist' + return "Error: The specified htpasswd file does not exist" - cmd = ['htpasswd', '-D', pwfile, user] + cmd = ["htpasswd", "-D", pwfile, user] if all_results: - out = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False) + out = __salt__["cmd.run_all"](cmd, runas=runas, python_shell=False) else: - out = __salt__['cmd.run'](cmd, runas=runas, - python_shell=False).splitlines() + out = __salt__["cmd.run"](cmd, runas=runas, python_shell=False).splitlines() return out -def verify(pwfile, user, password, opts='', runas=None): - ''' +def verify(pwfile, user, password, opts="", runas=None): + """ Return True if the htpasswd file exists, the user has an entry, and their password matches. @@ -137,12 +140,12 @@ def verify(pwfile, user, password, opts='', runas=None): salt '*' webutil.verify /etc/httpd/htpasswd larry maybepassword salt '*' webutil.verify /etc/httpd/htpasswd larry maybepassword opts=ns - ''' + """ if not os.path.exists(pwfile): return False - cmd = ['htpasswd', '-bv{0}'.format(opts), pwfile, user, password] - ret = __salt__['cmd.run_all'](cmd, runas=runas, python_shell=False) - log.debug('Result of verifying htpasswd for user %s: %s', user, ret) + cmd = ["htpasswd", "-bv{0}".format(opts), pwfile, user, password] + ret = __salt__["cmd.run_all"](cmd, runas=runas, python_shell=False) + log.debug("Result of verifying htpasswd for user %s: %s", user, ret) - return ret['retcode'] == 0 + return ret["retcode"] == 0 diff --git a/salt/modules/win_auditpol.py b/salt/modules/win_auditpol.py index 755b58aed4a..fb2ffbb44dc 100644 --- a/salt/modules/win_auditpol.py +++ b/salt/modules/win_auditpol.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A salt module for modifying the audit policies on the machine Though this module does not set group policy for auditing, it displays how all @@ -53,28 +53,28 @@ CLI Example: # Set the state of the "Credential Validation" setting to No Auditing salt * auditpol.set_setting name="Credential Validation" value="No Auditing" -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.platform # Define the module's virtual name -__virtualname__ = 'auditpol' +__virtualname__ = "auditpol" def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if not salt.utils.platform.is_windows(): return False, "Module win_auditpol: module only available on Windows" return __virtualname__ -def get_settings(category='All'): - ''' +def get_settings(category="All"): + """ Get the current configuration for all audit settings specified in the category @@ -114,12 +114,12 @@ def get_settings(category='All'): # Get the current state of all audit settings in the "Account Logon" # category salt * auditpol.get_settings "Account Logon" - ''' - return __utils__['auditpol.get_settings'](category=category) + """ + return __utils__["auditpol.get_settings"](category=category) def get_setting(name): - ''' + """ Get the current configuration for the named audit setting Args: @@ -138,12 +138,12 @@ def get_setting(name): # Get current state of the "Credential Validation" setting salt * auditpol.get_setting "Credential Validation" - ''' - return __utils__['auditpol.get_setting'](name=name) + """ + return __utils__["auditpol.get_setting"](name=name) def set_setting(name, value): - ''' + """ Set the configuration for the named audit setting Args: @@ -169,11 +169,12 @@ def set_setting(name, value): CLI Example: .. code-block:: bash + # Set the state of the "Credential Validation" setting to Success and # Failure salt * auditpol.set_setting "Credential Validation" "Success and Failure" # Set the state of the "Credential Validation" setting to No Auditing salt * auditpol.set_setting "Credential Validation" "No Auditing" - ''' - return __utils__['auditpol.set_setting'](name=name, value=value) + """ + return __utils__["auditpol.set_setting"](name=name, value=value) diff --git a/salt/modules/win_autoruns.py b/salt/modules/win_autoruns.py index 022abd0c1c1..5f5a0159ad6 100644 --- a/salt/modules/win_autoruns.py +++ b/salt/modules/win_autoruns.py @@ -1,30 +1,28 @@ # -*- coding: utf-8 -*- -''' +""" Module for listing programs that automatically run on startup (very alpha...not tested on anything but my Win 7x64) -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt libs import salt.utils.platform - # Define a function alias in order not to shadow built-in's -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} # Define the module's virtual name -__virtualname__ = 'autoruns' +__virtualname__ = "autoruns" def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ @@ -32,9 +30,9 @@ def __virtual__(): def _get_dirs(user_dir, startup_dir): - ''' + """ Return a list of startup dirs - ''' + """ try: users = os.listdir(user_dir) except WindowsError: # pylint: disable=E0602 @@ -49,7 +47,7 @@ def _get_dirs(user_dir, startup_dir): def list_(): - ''' + """ Get a list of automatically running programs CLI Example: @@ -57,28 +55,33 @@ def list_(): .. code-block:: bash salt '*' autoruns.list - ''' + """ autoruns = {} # Find autoruns in registry - keys = ['HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run', - 'HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run /reg:64', - 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' + keys = [ + "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run /reg:64", + "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", ] for key in keys: autoruns[key] = [] - cmd = ['reg', 'query', key] - for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): - if line and line[0:4] != "HKEY" and line[0:5] != "ERROR": # Remove junk lines + cmd = ["reg", "query", key] + for line in __salt__["cmd.run"](cmd, python_shell=False).splitlines(): + if ( + line and line[0:4] != "HKEY" and line[0:5] != "ERROR" + ): # Remove junk lines autoruns[key].append(line) # Find autoruns in user's startup folder - user_dir = 'C:\\Documents and Settings\\' - startup_dir = '\\Start Menu\\Programs\\Startup' + user_dir = "C:\\Documents and Settings\\" + startup_dir = "\\Start Menu\\Programs\\Startup" full_dirs = _get_dirs(user_dir, startup_dir) if not full_dirs: - user_dir = 'C:\\Users\\' - startup_dir = '\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup' + user_dir = "C:\\Users\\" + startup_dir = ( + "\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup" + ) full_dirs = _get_dirs(user_dir, startup_dir) for full_dir in full_dirs: diff --git a/salt/modules/win_certutil.py b/salt/modules/win_certutil.py index edfb9421467..f2dbcf09c10 100644 --- a/salt/modules/win_certutil.py +++ b/salt/modules/win_certutil.py @@ -1,17 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" This module allows you to install certificates into the windows certificate manager. .. code-block:: bash salt '*' certutil.add_store salt://cert.cer "TrustedPublisher" -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import re +from __future__ import absolute_import, print_function, unicode_literals + import logging +import re # Import Salt Libs import salt.utils.platform @@ -21,16 +22,16 @@ __virtualname__ = "certutil" def __virtual__(): - ''' + """ Only work on Windows - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ - return False + return (False, "Module win_certutil: module only works on Windows systems.") def get_cert_serial(cert_file): - ''' + """ Get the serial number of a certificate file cert_file @@ -41,9 +42,9 @@ def get_cert_serial(cert_file): .. code-block:: bash salt '*' certutil.get_cert_serial <certificate name> - ''' + """ cmd = "certutil.exe -silent -verify {0}".format(cert_file) - out = __salt__['cmd.run'](cmd) + out = __salt__["cmd.run"](cmd) # match serial number by paragraph to work with multiple languages matches = re.search(r":\s*(\w*)\r\n\r\n", out) if matches is not None: @@ -53,7 +54,7 @@ def get_cert_serial(cert_file): def get_stored_cert_serials(store): - ''' + """ Get all of the certificate serials in the specified store store @@ -64,16 +65,16 @@ def get_stored_cert_serials(store): .. code-block:: bash salt '*' certutil.get_stored_cert_serials <store> - ''' + """ cmd = "certutil.exe -store {0}".format(store) - out = __salt__['cmd.run'](cmd) + out = __salt__["cmd.run"](cmd) # match serial numbers by header position to work with multiple languages matches = re.findall(r"={16}\r\n.*:\s*(\w*)\r\n", out) return matches -def add_store(source, store, saltenv='base'): - ''' +def add_store(source, store, saltenv="base"): + """ Add the given cert into the given Certificate Store source @@ -92,14 +93,14 @@ def add_store(source, store, saltenv='base'): .. code-block:: bash salt '*' certutil.add_store salt://cert.cer TrustedPublisher - ''' - cert_file = __salt__['cp.cache_file'](source, saltenv) + """ + cert_file = __salt__["cp.cache_file"](source, saltenv) cmd = "certutil.exe -addstore {0} {1}".format(store, cert_file) - return __salt__['cmd.run'](cmd) + return __salt__["cmd.run"](cmd) -def del_store(source, store, saltenv='base'): - ''' +def del_store(source, store, saltenv="base"): + """ Delete the given cert into the given Certificate Store source @@ -118,8 +119,8 @@ def del_store(source, store, saltenv='base'): .. code-block:: bash salt '*' certutil.del_store salt://cert.cer TrustedPublisher - ''' - cert_file = __salt__['cp.cache_file'](source, saltenv) + """ + cert_file = __salt__["cp.cache_file"](source, saltenv) serial = get_cert_serial(cert_file) cmd = "certutil.exe -delstore {0} {1}".format(store, serial) - return __salt__['cmd.run'](cmd) + return __salt__["cmd.run"](cmd) diff --git a/salt/modules/win_dacl.py b/salt/modules/win_dacl.py index 0bb6e17735a..43a42cfc4b1 100644 --- a/salt/modules/win_dacl.py +++ b/salt/modules/win_dacl.py @@ -1,20 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" Manage DACLs on Windows :depends: - winreg Python module -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import os -import logging -import re +from __future__ import absolute_import, print_function, unicode_literals -# TODO: Figure out the exceptions that could be raised and properly catch -# them instead of a bare except that catches any exception at all -# may also need to add the ability to take ownership of an object to set -# permissions if the minion is running as a user and not LOCALSYSTEM +import logging +import os +import re # Import Salt libs import salt.utils.platform @@ -22,11 +18,18 @@ from salt.exceptions import CommandExecutionError from salt.ext.six import string_types from salt.ext.six.moves import range # pylint: disable=redefined-builtin +# TODO: Figure out the exceptions that could be raised and properly catch +# them instead of a bare except that catches any exception at all +# may also need to add the ability to take ownership of an object to set +# permissions if the minion is running as a user and not LOCALSYSTEM + + # Import third party libs try: import salt.ext.six.moves.winreg # pylint: disable=redefined-builtin,no-name-in-module,import-error import win32security import ntsecuritycon + HAS_WINDOWS_MODULES = True except ImportError: HAS_WINDOWS_MODULES = False @@ -34,334 +37,379 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'win_dacl' +__virtualname__ = "win_dacl" class daclConstants(object): - ''' + """ DACL constants used throughout the module - ''' + """ + # Definition in ntsecuritycon is incorrect (does not match winnt.h). The version # in ntsecuritycon has the extra bits 0x200 enabled. # Note that you when you set this permission what you'll generally get back is it # ORed with 0x200 (SI_NO_ACL_PROTECT), which is what ntsecuritycon incorrectly defines. def __init__(self): - self.FILE_ALL_ACCESS = (ntsecuritycon.STANDARD_RIGHTS_REQUIRED | ntsecuritycon.SYNCHRONIZE | 0x1ff) + self.FILE_ALL_ACCESS = ( + ntsecuritycon.STANDARD_RIGHTS_REQUIRED | ntsecuritycon.SYNCHRONIZE | 0x1FF + ) self.hkeys_security = { - 'HKEY_LOCAL_MACHINE': 'MACHINE', - 'HKEY_USERS': 'USERS', - 'HKEY_CURRENT_USER': 'CURRENT_USER', - 'HKEY_CLASSES_ROOT': 'CLASSES_ROOT', - 'MACHINE': 'MACHINE', - 'USERS': 'USERS', - 'CURRENT_USER': 'CURRENT_USER', - 'CLASSES_ROOT': 'CLASSES_ROOT', - 'HKLM': 'MACHINE', - 'HKU': 'USERS', - 'HKCU': 'CURRENT_USER', - 'HKCR': 'CLASSES_ROOT', - } + "HKEY_LOCAL_MACHINE": "MACHINE", + "HKEY_USERS": "USERS", + "HKEY_CURRENT_USER": "CURRENT_USER", + "HKEY_CLASSES_ROOT": "CLASSES_ROOT", + "MACHINE": "MACHINE", + "USERS": "USERS", + "CURRENT_USER": "CURRENT_USER", + "CLASSES_ROOT": "CLASSES_ROOT", + "HKLM": "MACHINE", + "HKU": "USERS", + "HKCU": "CURRENT_USER", + "HKCR": "CLASSES_ROOT", + } self.rights = { win32security.SE_REGISTRY_KEY: { - 'READ': { - 'BITS': salt.ext.six.moves.winreg.KEY_READ, - 'TEXT': 'read'}, - 'FULLCONTROL': { - 'BITS': salt.ext.six.moves.winreg.KEY_ALL_ACCESS, - 'TEXT': 'full control'} + "READ": {"BITS": salt.ext.six.moves.winreg.KEY_READ, "TEXT": "read"}, + "FULLCONTROL": { + "BITS": salt.ext.six.moves.winreg.KEY_ALL_ACCESS, + "TEXT": "full control", + }, }, win32security.SE_FILE_OBJECT: { - 'READ': { - 'BITS': ntsecuritycon.FILE_GENERIC_READ, - 'TEXT': 'read'}, - 'WRITE': { - 'BITS': ntsecuritycon.FILE_GENERIC_WRITE, - 'TEXT': 'write'}, - 'READ&EXECUTE': { - 'BITS': ntsecuritycon.FILE_GENERIC_EXECUTE | - ntsecuritycon.FILE_GENERIC_READ, - 'TEXT': 'read and execute'}, - 'MODIFY': { - 'BITS': ntsecuritycon.FILE_GENERIC_WRITE | - ntsecuritycon.FILE_GENERIC_READ | - ntsecuritycon.FILE_GENERIC_EXECUTE | - ntsecuritycon.DELETE, - 'TEXT': 'modify'}, - 'FULLCONTROL': { - 'BITS': self.FILE_ALL_ACCESS, - 'TEXT': 'full control'} - } + "READ": {"BITS": ntsecuritycon.FILE_GENERIC_READ, "TEXT": "read"}, + "WRITE": {"BITS": ntsecuritycon.FILE_GENERIC_WRITE, "TEXT": "write"}, + "READ&EXECUTE": { + "BITS": ntsecuritycon.FILE_GENERIC_EXECUTE + | ntsecuritycon.FILE_GENERIC_READ, + "TEXT": "read and execute", + }, + "MODIFY": { + "BITS": ntsecuritycon.FILE_GENERIC_WRITE + | ntsecuritycon.FILE_GENERIC_READ + | ntsecuritycon.FILE_GENERIC_EXECUTE + | ntsecuritycon.DELETE, + "TEXT": "modify", + }, + "FULLCONTROL": {"BITS": self.FILE_ALL_ACCESS, "TEXT": "full control"}, + }, } self.validAceTypes = { - 'ALLOW': {'TEXT': 'allowed', 'BITS': 0}, - 'DENY': {'TEXT': 'denied', 'BITS': 1}} + "ALLOW": {"TEXT": "allowed", "BITS": 0}, + "DENY": {"TEXT": "denied", "BITS": 1}, + } self.validPropagations = { win32security.SE_REGISTRY_KEY: { - 'KEY': { - 'TEXT': 'this key only', - 'BITS': win32security.NO_INHERITANCE}, - 'KEY&SUBKEYS': { - 'TEXT': 'this key and subkeys', - 'BITS': win32security.CONTAINER_INHERIT_ACE}, - 'SUBKEYS': { - 'TEXT': 'subkeys only', - 'BITS': win32security.INHERIT_ONLY_ACE | - win32security.CONTAINER_INHERIT_ACE}, - 'THIS KEY ONLY': { - 'TEXT': 'this key only', - 'BITS': win32security.NO_INHERITANCE}, - 'THIS KEY AND SUBKEYS': { - 'TEXT': 'this key and subkeys', - 'BITS': win32security.CONTAINER_INHERIT_ACE}, - 'SUBKEYS ONLY': { - 'TEXT': 'subkeys only', - 'BITS': win32security.INHERIT_ONLY_ACE | - win32security.CONTAINER_INHERIT_ACE} + "KEY": {"TEXT": "this key only", "BITS": win32security.NO_INHERITANCE}, + "KEY&SUBKEYS": { + "TEXT": "this key and subkeys", + "BITS": win32security.CONTAINER_INHERIT_ACE, + }, + "SUBKEYS": { + "TEXT": "subkeys only", + "BITS": win32security.INHERIT_ONLY_ACE + | win32security.CONTAINER_INHERIT_ACE, + }, + "THIS KEY ONLY": { + "TEXT": "this key only", + "BITS": win32security.NO_INHERITANCE, + }, + "THIS KEY AND SUBKEYS": { + "TEXT": "this key and subkeys", + "BITS": win32security.CONTAINER_INHERIT_ACE, + }, + "SUBKEYS ONLY": { + "TEXT": "subkeys only", + "BITS": win32security.INHERIT_ONLY_ACE + | win32security.CONTAINER_INHERIT_ACE, + }, }, win32security.SE_FILE_OBJECT: { - 'FILE': { - 'TEXT': 'this file/folder only', - 'BITS': win32security.NO_INHERITANCE}, - 'FOLDER': { - 'TEXT': 'this file/folder only', - 'BITS': win32security.NO_INHERITANCE}, - 'FOLDER&SUBFOLDERS&FILES': { - 'TEXT': 'this folder, subfolders, and files', - 'BITS': win32security.CONTAINER_INHERIT_ACE | - win32security.OBJECT_INHERIT_ACE}, - 'FOLDER&SUBFOLDERS': { - 'TEXT': 'this folder and subfolders', - 'BITS': win32security.CONTAINER_INHERIT_ACE}, - 'FOLDER&FILES': { - 'TEXT': 'this folder and files', - 'BITS': win32security.OBJECT_INHERIT_ACE}, - 'SUBFOLDERS&FILES': { - 'TEXT': 'subfolders and files', - 'BITS': win32security.INHERIT_ONLY_ACE | - win32security.CONTAINER_INHERIT_ACE | - win32security.OBJECT_INHERIT_ACE}, - 'SUBFOLDERS': { - 'TEXT': 'subfolders only', - 'BITS': win32security.INHERIT_ONLY_ACE | - win32security.CONTAINER_INHERIT_ACE}, - 'FILES': { - 'TEXT': 'files only', - 'BITS': win32security.INHERIT_ONLY_ACE | - win32security.OBJECT_INHERIT_ACE}, - 'THIS FILE ONLY': { - 'TEXT': 'this file/folder only', - 'BITS': win32security.NO_INHERITANCE}, - 'THIS FOLDER ONLY': { - 'TEXT': 'this file/folder only', - 'BITS': win32security.NO_INHERITANCE}, - 'THIS FOLDER, SUBFOLDERS, AND FILES': { - 'TEXT': 'this folder, subfolders, and files', - 'BITS': win32security.CONTAINER_INHERIT_ACE | - win32security.OBJECT_INHERIT_ACE}, - 'THIS FOLDER AND SUBFOLDERS': { - 'TEXT': 'this folder and subfolders', - 'BITS': win32security.CONTAINER_INHERIT_ACE}, - 'THIS FOLDER AND FILES': { - 'TEXT': 'this folder and files', - 'BITS': win32security.OBJECT_INHERIT_ACE}, - 'SUBFOLDERS AND FILES': { - 'TEXT': 'subfolders and files', - 'BITS': win32security.INHERIT_ONLY_ACE | - win32security.CONTAINER_INHERIT_ACE | - win32security.OBJECT_INHERIT_ACE}, - 'SUBFOLDERS ONLY': { - 'TEXT': 'subfolders only', - 'BITS': win32security.INHERIT_ONLY_ACE | - win32security.CONTAINER_INHERIT_ACE}, - 'FILES ONLY': { - 'TEXT': 'files only', - 'BITS': win32security.INHERIT_ONLY_ACE | - win32security.OBJECT_INHERIT_ACE} - } + "FILE": { + "TEXT": "this file/folder only", + "BITS": win32security.NO_INHERITANCE, + }, + "FOLDER": { + "TEXT": "this file/folder only", + "BITS": win32security.NO_INHERITANCE, + }, + "FOLDER&SUBFOLDERS&FILES": { + "TEXT": "this folder, subfolders, and files", + "BITS": win32security.CONTAINER_INHERIT_ACE + | win32security.OBJECT_INHERIT_ACE, + }, + "FOLDER&SUBFOLDERS": { + "TEXT": "this folder and subfolders", + "BITS": win32security.CONTAINER_INHERIT_ACE, + }, + "FOLDER&FILES": { + "TEXT": "this folder and files", + "BITS": win32security.OBJECT_INHERIT_ACE, + }, + "SUBFOLDERS&FILES": { + "TEXT": "subfolders and files", + "BITS": win32security.INHERIT_ONLY_ACE + | win32security.CONTAINER_INHERIT_ACE + | win32security.OBJECT_INHERIT_ACE, + }, + "SUBFOLDERS": { + "TEXT": "subfolders only", + "BITS": win32security.INHERIT_ONLY_ACE + | win32security.CONTAINER_INHERIT_ACE, + }, + "FILES": { + "TEXT": "files only", + "BITS": win32security.INHERIT_ONLY_ACE + | win32security.OBJECT_INHERIT_ACE, + }, + "THIS FILE ONLY": { + "TEXT": "this file/folder only", + "BITS": win32security.NO_INHERITANCE, + }, + "THIS FOLDER ONLY": { + "TEXT": "this file/folder only", + "BITS": win32security.NO_INHERITANCE, + }, + "THIS FOLDER, SUBFOLDERS, AND FILES": { + "TEXT": "this folder, subfolders, and files", + "BITS": win32security.CONTAINER_INHERIT_ACE + | win32security.OBJECT_INHERIT_ACE, + }, + "THIS FOLDER AND SUBFOLDERS": { + "TEXT": "this folder and subfolders", + "BITS": win32security.CONTAINER_INHERIT_ACE, + }, + "THIS FOLDER AND FILES": { + "TEXT": "this folder and files", + "BITS": win32security.OBJECT_INHERIT_ACE, + }, + "SUBFOLDERS AND FILES": { + "TEXT": "subfolders and files", + "BITS": win32security.INHERIT_ONLY_ACE + | win32security.CONTAINER_INHERIT_ACE + | win32security.OBJECT_INHERIT_ACE, + }, + "SUBFOLDERS ONLY": { + "TEXT": "subfolders only", + "BITS": win32security.INHERIT_ONLY_ACE + | win32security.CONTAINER_INHERIT_ACE, + }, + "FILES ONLY": { + "TEXT": "files only", + "BITS": win32security.INHERIT_ONLY_ACE + | win32security.OBJECT_INHERIT_ACE, + }, + }, } self.reflection_mask = { True: salt.ext.six.moves.winreg.KEY_ALL_ACCESS, - False: salt.ext.six.moves.winreg.KEY_ALL_ACCESS | salt.ext.six.moves.winreg.KEY_WOW64_64KEY, - } + False: salt.ext.six.moves.winreg.KEY_ALL_ACCESS + | salt.ext.six.moves.winreg.KEY_WOW64_64KEY, + } self.objectType = { - 'FILE': win32security.SE_FILE_OBJECT, - 'DIRECTORY': win32security.SE_FILE_OBJECT, - 'REGISTRY': win32security.SE_REGISTRY_KEY} + "FILE": win32security.SE_FILE_OBJECT, + "DIRECTORY": win32security.SE_FILE_OBJECT, + "REGISTRY": win32security.SE_REGISTRY_KEY, + } def getObjectTypeBit(self, t): - ''' + """ returns the bit value of the string object type - ''' + """ if isinstance(t, string_types): t = t.upper() try: return self.objectType[t] except KeyError: - raise CommandExecutionError(( - 'Invalid object type "{0}". It should be one of the following: {1}' - ).format(t, ', '.join(self.objectType))) + raise CommandExecutionError( + ( + 'Invalid object type "{0}". It should be one of the following: {1}' + ).format(t, ", ".join(self.objectType)) + ) else: return t def getSecurityHkey(self, s): - ''' + """ returns the necessary string value for an HKEY for the win32security module - ''' + """ try: return self.hkeys_security[s] except KeyError: - raise CommandExecutionError(( - 'No HKEY named "{0}". It should be one of the following: {1}' - ).format(s, ', '.join(self.hkeys_security))) + raise CommandExecutionError( + ( + 'No HKEY named "{0}". It should be one of the following: {1}' + ).format(s, ", ".join(self.hkeys_security)) + ) def getPermissionBit(self, t, m): - ''' + """ returns a permission bit of the string permission value for the specified object type - ''' + """ try: if isinstance(m, string_types): - return self.rights[t][m]['BITS'] + return self.rights[t][m]["BITS"] else: return m except KeyError: - raise CommandExecutionError(( - 'No right "{0}". It should be one of the following: {1}') - .format(m, ', '.join(self.rights[t]))) + raise CommandExecutionError( + ('No right "{0}". It should be one of the following: {1}').format( + m, ", ".join(self.rights[t]) + ) + ) def getPermissionText(self, t, m): - ''' + """ returns the permission textual representation of a specified permission bit/object type - ''' + """ try: - return self.rights[t][m]['TEXT'] + return self.rights[t][m]["TEXT"] except KeyError: - raise CommandExecutionError(( - 'No right "{0}". It should be one of the following: {1}') - .format(m, ', '.join(self.rights[t]))) + raise CommandExecutionError( + ('No right "{0}". It should be one of the following: {1}').format( + m, ", ".join(self.rights[t]) + ) + ) def getAceTypeBit(self, t): - ''' + """ returns the acetype bit of a text value - ''' + """ try: - return self.validAceTypes[t]['BITS'] + return self.validAceTypes[t]["BITS"] except KeyError: - raise CommandExecutionError(( - 'No ACE type "{0}". It should be one of the following: {1}' - ).format(t, ', '.join(self.validAceTypes))) + raise CommandExecutionError( + ('No ACE type "{0}". It should be one of the following: {1}').format( + t, ", ".join(self.validAceTypes) + ) + ) def getAceTypeText(self, t): - ''' + """ returns the textual representation of a acetype bit - ''' + """ try: - return self.validAceTypes[t]['TEXT'] + return self.validAceTypes[t]["TEXT"] except KeyError: - raise CommandExecutionError(( - 'No ACE type "{0}". It should be one of the following: {1}' - ).format(t, ', '.join(self.validAceTypes))) + raise CommandExecutionError( + ('No ACE type "{0}". It should be one of the following: {1}').format( + t, ", ".join(self.validAceTypes) + ) + ) def getPropagationBit(self, t, p): - ''' + """ returns the propagation bit of a text value - ''' + """ try: - return self.validPropagations[t][p]['BITS'] + return self.validPropagations[t][p]["BITS"] except KeyError: - raise CommandExecutionError(( - 'No propagation type of "{0}". It should be one of the following: {1}' - ).format(p, ', '.join(self.validPropagations[t]))) + raise CommandExecutionError( + ( + 'No propagation type of "{0}". It should be one of the following: {1}' + ).format(p, ", ".join(self.validPropagations[t])) + ) def getPropagationText(self, t, p): - ''' + """ returns the textual representation of a propagation bit - ''' + """ try: - return self.validPropagations[t][p]['TEXT'] + return self.validPropagations[t][p]["TEXT"] except KeyError: - raise CommandExecutionError(( - 'No propagation type of "{0}". It should be one of the following: {1}' - ).format(p, ', '.join(self.validPropagations[t]))) + raise CommandExecutionError( + ( + 'No propagation type of "{0}". It should be one of the following: {1}' + ).format(p, ", ".join(self.validPropagations[t])) + ) def processPath(self, path, objectType): - ''' + """ processes a path/object type combo and returns: registry types with the correct HKEY text representation files/directories with environment variables expanded - ''' + """ if objectType == win32security.SE_REGISTRY_KEY: splt = path.split("\\") hive = self.getSecurityHkey(splt.pop(0).upper()) splt.insert(0, hive) - path = r'\\'.join(splt) + path = r"\\".join(splt) else: path = os.path.expandvars(path) return path def _getUserSid(user): - ''' + """ return a state error dictionary, with 'sid' as a field if it could be returned if user is None, sid will also be None - ''' + """ ret = {} - sid_pattern = r'^S-1(-\d+){1,}$' + sid_pattern = r"^S-1(-\d+){1,}$" if user and re.match(sid_pattern, user, re.I): try: sid = win32security.GetBinarySid(user) except Exception as e: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = 'Unable to obtain the binary security identifier for {0}. The exception was {1}.'.format( - user, e) + ret["result"] = False + ret[ + "comment" + ] = "Unable to obtain the binary security identifier for {0}. The exception was {1}.".format( + user, e + ) else: try: - win32security.LookupAccountSid('', sid) - ret['result'] = True - ret['sid'] = sid + win32security.LookupAccountSid("", sid) + ret["result"] = True + ret["sid"] = sid except Exception as e: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = 'Unable to lookup the account for the security identifier {0}. The exception was {1}.'.format( - user, e) + ret["result"] = False + ret[ + "comment" + ] = "Unable to lookup the account for the security identifier {0}. The exception was {1}.".format( + user, e + ) else: try: - sid = win32security.LookupAccountName('', user)[0] if user else None - ret['result'] = True - ret['sid'] = sid + sid = win32security.LookupAccountName("", user)[0] if user else None + ret["result"] = True + ret["sid"] = sid except Exception as e: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = 'Unable to obtain the security identifier for {0}. The exception was {1}.'.format( - user, e) + ret["result"] = False + ret[ + "comment" + ] = "Unable to obtain the security identifier for {0}. The exception was {1}.".format( + user, e + ) return ret def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if salt.utils.platform.is_windows() and HAS_WINDOWS_MODULES: return __virtualname__ return (False, "Module win_dacl: module only works on Windows systems") def _get_dacl(path, objectType): - ''' + """ Gets the DACL of a path - ''' + """ try: dacl = win32security.GetNamedSecurityInfo( path, objectType, win32security.DACL_SECURITY_INFORMATION - ).GetSecurityDescriptorDacl() + ).GetSecurityDescriptorDacl() except Exception: # pylint: disable=broad-except dacl = None return dacl def get(path, objectType, user=None): - ''' + """ Get the ACL of an object. Will filter by user if one is provided. Args: @@ -376,9 +424,8 @@ def get(path, objectType, user=None): .. code-block:: bash salt 'minion-id' win_dacl.get c:\temp directory - ''' - ret = {'Path': path, - 'ACLs': []} + """ + ret = {"Path": path, "ACLs": []} sidRet = _getUserSid(user) @@ -390,13 +437,13 @@ def get(path, objectType, user=None): if tdacl: for counter in range(0, tdacl.GetAceCount()): tAce = tdacl.GetAce(counter) - if not sidRet['sid'] or (tAce[2] == sidRet['sid']): - ret['ACLs'].append(_ace_to_text(tAce, objectTypeBit)) + if not sidRet["sid"] or (tAce[2] == sidRet["sid"]): + ret["ACLs"].append(_ace_to_text(tAce, objectTypeBit)) return ret def add_ace(path, objectType, user, permission, acetype, propagation): - r''' + r""" add an ace to an object path: path to the object (i.e. c:\\temp\\file, HKEY_LOCAL_MACHINE\\SOFTWARE\\KEY, etc) @@ -411,14 +458,10 @@ def add_ace(path, objectType, user, permission, acetype, propagation): allow domain\fakeuser full control on HKLM\\SOFTWARE\\somekey, propagate to this key and subkeys salt 'myminion' win_dacl.add_ace 'HKEY_LOCAL_MACHINE\\SOFTWARE\\somekey' 'Registry' 'domain\fakeuser' 'FULLCONTROL' 'ALLOW' 'KEY&SUBKEYS' - ''' - ret = {'result': None, - 'changes': {}, - 'comment': ''} + """ + ret = {"result": None, "changes": {}, "comment": ""} - if (path and user and - permission and acetype - and propagation): + if path and user and permission and acetype and propagation: if objectType.upper() == "FILE": propagation = "FILE" dc = daclConstants() @@ -430,7 +473,7 @@ def add_ace(path, objectType, user, permission, acetype, propagation): propagation = propagation.strip().upper() sidRet = _getUserSid(user) - if not sidRet['result']: + if not sidRet["result"]: return sidRet permissionbit = dc.getPermissionBit(objectTypeBit, permission) acetypebit = dc.getAceTypeBit(acetype) @@ -441,34 +484,57 @@ def add_ace(path, objectType, user, permission, acetype, propagation): acesAdded = [] try: if acetypebit == 0: - dacl.AddAccessAllowedAceEx(win32security.ACL_REVISION, propagationbit, permissionbit, sidRet['sid']) + dacl.AddAccessAllowedAceEx( + win32security.ACL_REVISION, + propagationbit, + permissionbit, + sidRet["sid"], + ) elif acetypebit == 1: - dacl.AddAccessDeniedAceEx(win32security.ACL_REVISION, propagationbit, permissionbit, sidRet['sid']) + dacl.AddAccessDeniedAceEx( + win32security.ACL_REVISION, + propagationbit, + permissionbit, + sidRet["sid"], + ) win32security.SetNamedSecurityInfo( - path, objectTypeBit, win32security.DACL_SECURITY_INFORMATION, - None, None, dacl, None) - acesAdded.append(( - '{0} {1} {2} on {3}' - ).format( - user, dc.getAceTypeText(acetype), dc.getPermissionText(objectTypeBit, permission), - dc.getPropagationText(objectTypeBit, propagation))) - ret['result'] = True + path, + objectTypeBit, + win32security.DACL_SECURITY_INFORMATION, + None, + None, + dacl, + None, + ) + acesAdded.append( + ("{0} {1} {2} on {3}").format( + user, + dc.getAceTypeText(acetype), + dc.getPermissionText(objectTypeBit, permission), + dc.getPropagationText(objectTypeBit, propagation), + ) + ) + ret["result"] = True except Exception as e: # pylint: disable=broad-except - ret['comment'] = 'An error occurred attempting to add the ace. The error was {0}'.format(e) - ret['result'] = False + ret[ + "comment" + ] = "An error occurred attempting to add the ace. The error was {0}".format( + e + ) + ret["result"] = False return ret if acesAdded: - ret['changes']['Added ACEs'] = acesAdded + ret["changes"]["Added ACEs"] = acesAdded else: - ret['comment'] = 'Unable to obtain the DACL of {0}'.format(path) + ret["comment"] = "Unable to obtain the DACL of {0}".format(path) else: - ret['comment'] = 'An empty value was specified for a required item.' - ret['result'] = False + ret["comment"] = "An empty value was specified for a required item." + ret["result"] = False return ret def rm_ace(path, objectType, user, permission=None, acetype=None, propagation=None): - r''' + r""" remove an ace to an object path: path to the object (i.e. c:\\temp\\file, HKEY_LOCAL_MACHINE\\SOFTWARE\\KEY, etc) @@ -485,10 +551,8 @@ def rm_ace(path, objectType, user, permission=None, acetype=None, propagation=No remove allow domain\fakeuser full control on HKLM\\SOFTWARE\\somekey propagated to this key and subkeys salt 'myminion' win_dacl.rm_ace 'Registry' 'HKEY_LOCAL_MACHINE\\SOFTWARE\\somekey' 'domain\fakeuser' 'FULLCONTROL' 'ALLOW' 'KEY&SUBKEYS' - ''' - ret = {'result': None, - 'changes': {}, - 'comment': ''} + """ + ret = {"result": None, "changes": {}, "comment": ""} if path and user: dc = daclConstants() @@ -502,84 +566,104 @@ def rm_ace(path, objectType, user, permission=None, acetype=None, propagation=No acetype = acetype.strip().upper() if acetype else None propagation = propagation.strip().upper() if propagation else None - if check_ace(path, objectType, user, permission, acetype, propagation, True)['Exists']: + if check_ace(path, objectType, user, permission, acetype, propagation, True)[ + "Exists" + ]: sidRet = _getUserSid(user) - if not sidRet['result']: + if not sidRet["result"]: return sidRet - permissionbit = dc.getPermissionBit(objectTypeBit, permission) if permission else None + permissionbit = ( + dc.getPermissionBit(objectTypeBit, permission) if permission else None + ) acetypebit = dc.getAceTypeBit(acetype) if acetype else None - propagationbit = dc.getPropagationBit(objectTypeBit, propagation) if propagation else None + propagationbit = ( + dc.getPropagationBit(objectTypeBit, propagation) + if propagation + else None + ) dacl = _get_dacl(path, objectTypeBit) counter = 0 acesRemoved = [] while counter < dacl.GetAceCount(): tAce = dacl.GetAce(counter) - if (tAce[0][1] & win32security.INHERITED_ACE) != win32security.INHERITED_ACE: - if tAce[2] == sidRet['sid']: + if ( + tAce[0][1] & win32security.INHERITED_ACE + ) != win32security.INHERITED_ACE: + if tAce[2] == sidRet["sid"]: if not acetypebit or tAce[0][0] == acetypebit: - if not propagationbit or ((tAce[0][1] & propagationbit) == propagationbit): + if not propagationbit or ( + (tAce[0][1] & propagationbit) == propagationbit + ): if not permissionbit or tAce[1] == permissionbit: dacl.DeleteAce(counter) counter = counter - 1 - acesRemoved.append(_ace_to_text(tAce, objectTypeBit)) + acesRemoved.append( + _ace_to_text(tAce, objectTypeBit) + ) counter = counter + 1 if acesRemoved: try: win32security.SetNamedSecurityInfo( - path, objectTypeBit, win32security.DACL_SECURITY_INFORMATION, - None, None, dacl, None) - ret['changes']['Removed ACEs'] = acesRemoved - ret['result'] = True + path, + objectTypeBit, + win32security.DACL_SECURITY_INFORMATION, + None, + None, + dacl, + None, + ) + ret["changes"]["Removed ACEs"] = acesRemoved + ret["result"] = True except Exception as e: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = 'Error removing ACE. The error was {0}.'.format(e) + ret["result"] = False + ret["comment"] = "Error removing ACE. The error was {0}.".format(e) return ret else: - ret['comment'] = 'The specified ACE was not found on the path.' + ret["comment"] = "The specified ACE was not found on the path." return ret def _ace_to_text(ace, objectType): - ''' + """ helper function to convert an ace to a textual representation - ''' + """ dc = daclConstants() objectType = dc.getObjectTypeBit(objectType) try: - userSid = win32security.LookupAccountSid('', ace[2]) + userSid = win32security.LookupAccountSid("", ace[2]) if userSid[1]: - userSid = '{1}\\{0}'.format(userSid[0], userSid[1]) + userSid = "{1}\\{0}".format(userSid[0], userSid[1]) else: - userSid = '{0}'.format(userSid[0]) + userSid = "{0}".format(userSid[0]) except Exception: # pylint: disable=broad-except userSid = win32security.ConvertSidToStringSid(ace[2]) tPerm = ace[1] tAceType = ace[0][0] tProps = ace[0][1] - tInherited = '' + tInherited = "" for x in dc.validAceTypes: - if dc.validAceTypes[x]['BITS'] == tAceType: - tAceType = dc.validAceTypes[x]['TEXT'] + if dc.validAceTypes[x]["BITS"] == tAceType: + tAceType = dc.validAceTypes[x]["TEXT"] break for x in dc.rights[objectType]: - if dc.rights[objectType][x]['BITS'] == tPerm: - tPerm = dc.rights[objectType][x]['TEXT'] + if dc.rights[objectType][x]["BITS"] == tPerm: + tPerm = dc.rights[objectType][x]["TEXT"] break if (tProps & win32security.INHERITED_ACE) == win32security.INHERITED_ACE: - tInherited = '[Inherited]' - tProps = (tProps ^ win32security.INHERITED_ACE) + tInherited = "[Inherited]" + tProps = tProps ^ win32security.INHERITED_ACE for x in dc.validPropagations[objectType]: - if dc.validPropagations[objectType][x]['BITS'] == tProps: - tProps = dc.validPropagations[objectType][x]['TEXT'] + if dc.validPropagations[objectType][x]["BITS"] == tProps: + tProps = dc.validPropagations[objectType][x]["TEXT"] break - return (( - '{0} {1} {2} on {3} {4}' - ).format(userSid, tAceType, tPerm, tProps, tInherited)) + return ("{0} {1} {2} on {3} {4}").format( + userSid, tAceType, tPerm, tProps, tInherited + ) def _set_dacl_inheritance(path, objectType, inheritance=True, copy=True, clear=False): - ''' + """ helper function to set the inheritance Args: @@ -593,14 +677,14 @@ def _set_dacl_inheritance(path, objectType, inheritance=True, copy=True, clear=F inheritance clear (bool): Remove non-inherited ACEs from the DACL - ''' - ret = {'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"result": False, "comment": "", "changes": {}} if path: try: - sd = win32security.GetNamedSecurityInfo(path, objectType, win32security.DACL_SECURITY_INFORMATION) + sd = win32security.GetNamedSecurityInfo( + path, objectType, win32security.DACL_SECURITY_INFORMATION + ) tdacl = sd.GetSecurityDescriptorDacl() if inheritance: if clear: @@ -608,50 +692,68 @@ def _set_dacl_inheritance(path, objectType, inheritance=True, copy=True, clear=F removedAces = [] while counter < tdacl.GetAceCount(): tAce = tdacl.GetAce(counter) - if (tAce[0][1] & win32security.INHERITED_ACE) != win32security.INHERITED_ACE: + if ( + tAce[0][1] & win32security.INHERITED_ACE + ) != win32security.INHERITED_ACE: tdacl.DeleteAce(counter) removedAces.append(_ace_to_text(tAce, objectType)) else: counter = counter + 1 if removedAces: - ret['changes']['Removed ACEs'] = removedAces + ret["changes"]["Removed ACEs"] = removedAces else: - ret['changes']['Non-Inherited ACEs'] = 'Left in the DACL' + ret["changes"]["Non-Inherited ACEs"] = "Left in the DACL" win32security.SetNamedSecurityInfo( - path, objectType, - win32security.DACL_SECURITY_INFORMATION | win32security.UNPROTECTED_DACL_SECURITY_INFORMATION, - None, None, tdacl, None) - ret['changes']['Inheritance'] = 'Enabled' + path, + objectType, + win32security.DACL_SECURITY_INFORMATION + | win32security.UNPROTECTED_DACL_SECURITY_INFORMATION, + None, + None, + tdacl, + None, + ) + ret["changes"]["Inheritance"] = "Enabled" else: if not copy: counter = 0 inheritedAcesRemoved = [] while counter < tdacl.GetAceCount(): tAce = tdacl.GetAce(counter) - if (tAce[0][1] & win32security.INHERITED_ACE) == win32security.INHERITED_ACE: + if ( + tAce[0][1] & win32security.INHERITED_ACE + ) == win32security.INHERITED_ACE: tdacl.DeleteAce(counter) inheritedAcesRemoved.append(_ace_to_text(tAce, objectType)) else: counter = counter + 1 if inheritedAcesRemoved: - ret['changes']['Removed ACEs'] = inheritedAcesRemoved + ret["changes"]["Removed ACEs"] = inheritedAcesRemoved else: - ret['changes']['Previously Inherited ACEs'] = 'Copied to the DACL' + ret["changes"]["Previously Inherited ACEs"] = "Copied to the DACL" win32security.SetNamedSecurityInfo( - path, objectType, - win32security.DACL_SECURITY_INFORMATION | win32security.PROTECTED_DACL_SECURITY_INFORMATION, - None, None, tdacl, None) - ret['changes']['Inheritance'] = 'Disabled' - ret['result'] = True + path, + objectType, + win32security.DACL_SECURITY_INFORMATION + | win32security.PROTECTED_DACL_SECURITY_INFORMATION, + None, + None, + tdacl, + None, + ) + ret["changes"]["Inheritance"] = "Disabled" + ret["result"] = True except Exception as e: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = 'Error attempting to set the inheritance. The error was {0}.'.format(e) + ret["result"] = False + ret[ + "comment" + ] = "Error attempting to set the inheritance. The error was {0}.".format(e) return ret def enable_inheritance(path, objectType, clear=False): - ''' + """ enable/disable inheritance on an object Args: @@ -666,7 +768,7 @@ def enable_inheritance(path, objectType, clear=False): .. code-block:: bash salt 'minion-id' win_dacl.enable_inheritance c:\temp directory - ''' + """ dc = daclConstants() objectType = dc.getObjectTypeBit(objectType) path = dc.processPath(path, objectType) @@ -675,7 +777,7 @@ def enable_inheritance(path, objectType, clear=False): def disable_inheritance(path, objectType, copy=True): - ''' + """ Disable inheritance on an object Args: @@ -690,7 +792,7 @@ def disable_inheritance(path, objectType, copy=True): .. code-block:: bash salt 'minion-id' win_dacl.disable_inheritance c:\temp directory - ''' + """ dc = daclConstants() objectType = dc.getObjectTypeBit(objectType) path = dc.processPath(path, objectType) @@ -699,7 +801,7 @@ def disable_inheritance(path, objectType, copy=True): def check_inheritance(path, objectType, user=None): - ''' + """ Check a specified path to verify if inheritance is enabled Args: @@ -714,11 +816,9 @@ def check_inheritance(path, objectType, user=None): .. code-block:: bash salt 'minion-id' win_dacl.check_inheritance c:\temp directory <username> - ''' + """ - ret = {'result': False, - 'Inheritance': False, - 'comment': ''} + ret = {"result": False, "Inheritance": False, "comment": ""} sidRet = _getUserSid(user) @@ -727,26 +827,40 @@ def check_inheritance(path, objectType, user=None): path = dc.processPath(path, objectType) try: - sd = win32security.GetNamedSecurityInfo(path, objectType, win32security.DACL_SECURITY_INFORMATION) + sd = win32security.GetNamedSecurityInfo( + path, objectType, win32security.DACL_SECURITY_INFORMATION + ) dacls = sd.GetSecurityDescriptorDacl() except Exception as e: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = 'Error obtaining the Security Descriptor or DACL of the path: {0}.'.format(e) + ret["result"] = False + ret[ + "comment" + ] = "Error obtaining the Security Descriptor or DACL of the path: {0}.".format( + e + ) return ret for counter in range(0, dacls.GetAceCount()): ace = dacls.GetAce(counter) if (ace[0][1] & win32security.INHERITED_ACE) == win32security.INHERITED_ACE: - if not sidRet['sid'] or ace[2] == sidRet['sid']: - ret['Inheritance'] = True + if not sidRet["sid"] or ace[2] == sidRet["sid"]: + ret["Inheritance"] = True break - ret['result'] = True + ret["result"] = True return ret -def check_ace(path, objectType, user, permission=None, acetype=None, propagation=None, exactPermissionMatch=False): - ''' +def check_ace( + path, + objectType, + user, + permission=None, + acetype=None, + propagation=None, + exactPermissionMatch=False, +): + """ Checks a path to verify the ACE (access control entry) specified exists Args: @@ -765,10 +879,8 @@ def check_ace(path, objectType, user, permission=None, acetype=None, propagation .. code-block:: bash salt 'minion-id' win_dacl.check_ace c:\temp directory <username> fullcontrol - ''' - ret = {'result': False, - 'Exists': False, - 'comment': ''} + """ + ret = {"result": False, "Exists": False, "comment": ""} dc = daclConstants() objectTypeBit = dc.getObjectTypeBit(objectType) @@ -778,33 +890,40 @@ def check_ace(path, objectType, user, permission=None, acetype=None, propagation acetype = acetype.upper() if permission else None propagation = propagation.upper() if propagation else None - permissionbit = dc.getPermissionBit(objectTypeBit, permission) if permission else None + permissionbit = ( + dc.getPermissionBit(objectTypeBit, permission) if permission else None + ) acetypebit = dc.getAceTypeBit(acetype) if acetype else None - propagationbit = dc.getPropagationBit(objectTypeBit, propagation) if propagation else None + propagationbit = ( + dc.getPropagationBit(objectTypeBit, propagation) if propagation else None + ) sidRet = _getUserSid(user) - if not sidRet['result']: + if not sidRet["result"]: return sidRet dacls = _get_dacl(path, objectTypeBit) - ret['result'] = True + ret["result"] = True if dacls: for counter in range(0, dacls.GetAceCount()): ace = dacls.GetAce(counter) - if ace[2] == sidRet['sid']: + if ace[2] == sidRet["sid"]: if not acetypebit or ace[0][0] == acetypebit: - if not propagationbit or (ace[0][1] & propagationbit) == propagationbit: + if ( + not propagationbit + or (ace[0][1] & propagationbit) == propagationbit + ): if not permissionbit: - ret['Exists'] = True + ret["Exists"] = True return ret if exactPermissionMatch: if ace[1] == permissionbit: - ret['Exists'] = True + ret["Exists"] = True return ret else: if (ace[1] & permissionbit) == permissionbit: - ret['Exists'] = True + ret["Exists"] = True return ret else: - ret['comment'] = 'No DACL found for object.' + ret["comment"] = "No DACL found for object." return ret diff --git a/salt/modules/win_disk.py b/salt/modules/win_disk.py index 67bd4018f72..b3641d16477 100644 --- a/salt/modules/win_disk.py +++ b/salt/modules/win_disk.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Module for gathering disk information on Windows :depends: - win32api Python module -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import ctypes @@ -22,7 +22,7 @@ except ImportError: pass # Define the module's virtual name -__virtualname__ = 'disk' +__virtualname__ = "disk" if six.PY3: @@ -32,16 +32,16 @@ else: def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ return (False, "Module win_disk: module only works on Windows systems") def usage(): - ''' + """ Return usage information for volumes mounted on this minion CLI Example: @@ -49,7 +49,7 @@ def usage(): .. code-block:: bash salt '*' disk.usage - ''' + """ drives = [] ret = {} drive_bitmask = ctypes.windll.kernel32.GetLogicalDrives() @@ -59,26 +59,26 @@ def usage(): drive_bitmask >>= 1 for drive in drives: try: - (available_bytes, - total_bytes, - total_free_bytes) = win32api.GetDiskFreeSpaceEx( - '{0}:\\'.format(drive) - ) + ( + available_bytes, + total_bytes, + total_free_bytes, + ) = win32api.GetDiskFreeSpaceEx("{0}:\\".format(drive)) used = total_bytes - total_free_bytes capacity = used / float(total_bytes) * 100 - ret['{0}:\\'.format(drive)] = { - 'filesystem': '{0}:\\'.format(drive), - '1K-blocks': total_bytes / 1024, - 'used': used / 1024, - 'available': total_free_bytes / 1024, - 'capacity': '{0:.0f}%'.format(capacity), + ret["{0}:\\".format(drive)] = { + "filesystem": "{0}:\\".format(drive), + "1K-blocks": total_bytes / 1024, + "used": used / 1024, + "available": total_free_bytes / 1024, + "capacity": "{0:.0f}%".format(capacity), } except Exception: # pylint: disable=broad-except - ret['{0}:\\'.format(drive)] = { - 'filesystem': '{0}:\\'.format(drive), - '1K-blocks': None, - 'used': None, - 'available': None, - 'capacity': None, + ret["{0}:\\".format(drive)] = { + "filesystem": "{0}:\\".format(drive), + "1K-blocks": None, + "used": None, + "available": None, + "capacity": None, } return ret diff --git a/salt/modules/win_dism.py b/salt/modules/win_dism.py index 2772ba61b4d..bbb98274522 100644 --- a/salt/modules/win_dism.py +++ b/salt/modules/win_dism.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Install features/packages for Windows using DISM, which is useful for minions not running server versions of Windows. Some functions are only available on Windows 10. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -33,21 +33,21 @@ try: if not salt.utils.platform.is_windows(): raise OSError - if os.path.exists(os.path.join(os.environ.get('SystemRoot'), 'SysNative')): - bin_path = os.path.join(os.environ.get('SystemRoot'), 'SysNative') + if os.path.exists(os.path.join(os.environ.get("SystemRoot"), "SysNative")): + bin_path = os.path.join(os.environ.get("SystemRoot"), "SysNative") else: - bin_path = os.path.join(os.environ.get('SystemRoot'), 'System32') - bin_dism = os.path.join(bin_path, 'dism.exe') + bin_path = os.path.join(os.environ.get("SystemRoot"), "System32") + bin_dism = os.path.join(bin_path, "dism.exe") except OSError: - log.trace('win_dism: Non-Windows system') - bin_dism = 'dism.exe' + log.trace("win_dism: Non-Windows system") + bin_dism = "dism.exe" def __virtual__(): - ''' + """ Only work on Windows - ''' + """ if not salt.utils.platform.is_windows(): return False, "Only available on Windows systems" @@ -55,24 +55,23 @@ def __virtual__(): def _get_components(type_regex, plural_type, install_value, image=None): - cmd = [bin_dism, - '/English', - '/Image:{0}'.format(image) if image else '/Online', - '/Get-{0}'.format(plural_type)] - out = __salt__['cmd.run'](cmd) - pattern = r'{0} : (.*)\r\n.*State : {1}\r\n'\ - .format(type_regex, install_value) + cmd = [ + bin_dism, + "/English", + "/Image:{0}".format(image) if image else "/Online", + "/Get-{0}".format(plural_type), + ] + out = __salt__["cmd.run"](cmd) + pattern = r"{0} : (.*)\r\n.*State : {1}\r\n".format(type_regex, install_value) capabilities = re.findall(pattern, out, re.MULTILINE) capabilities.sort() return capabilities -def add_capability(capability, - source=None, - limit_access=False, - image=None, - restart=False): - ''' +def add_capability( + capability, source=None, limit_access=False, image=None, restart=False +): + """ Install a capability Args: @@ -98,30 +97,33 @@ def add_capability(capability, .. code-block:: bash salt '*' dism.add_capability Tools.Graphics.DirectX~~~~0.0.1.0 - ''' - if salt.utils.versions.version_cmp(__grains__['osversion'], '10') == -1: + """ + if salt.utils.versions.version_cmp(__grains__["osversion"], "10") == -1: raise NotImplementedError( - '`install_capability` is not available on this version of Windows: ' - '{0}'.format(__grains__['osversion'])) + "`install_capability` is not available on this version of Windows: " + "{0}".format(__grains__["osversion"]) + ) - cmd = [bin_dism, - '/Quiet', - '/Image:{0}'.format(image) if image else '/Online', - '/Add-Capability', - '/CapabilityName:{0}'.format(capability)] + cmd = [ + bin_dism, + "/Quiet", + "/Image:{0}".format(image) if image else "/Online", + "/Add-Capability", + "/CapabilityName:{0}".format(capability), + ] if source: - cmd.append('/Source:{0}'.format(source)) + cmd.append("/Source:{0}".format(source)) if limit_access: - cmd.append('/LimitAccess') + cmd.append("/LimitAccess") if not restart: - cmd.append('/NoRestart') + cmd.append("/NoRestart") - return __salt__['cmd.run_all'](cmd) + return __salt__["cmd.run_all"](cmd) def remove_capability(capability, image=None, restart=False): - ''' + """ Uninstall a capability Args: @@ -143,26 +145,29 @@ def remove_capability(capability, image=None, restart=False): .. code-block:: bash salt '*' dism.remove_capability Tools.Graphics.DirectX~~~~0.0.1.0 - ''' - if salt.utils.versions.version_cmp(__grains__['osversion'], '10') == -1: + """ + if salt.utils.versions.version_cmp(__grains__["osversion"], "10") == -1: raise NotImplementedError( - '`uninstall_capability` is not available on this version of ' - 'Windows: {0}'.format(__grains__['osversion'])) + "`uninstall_capability` is not available on this version of " + "Windows: {0}".format(__grains__["osversion"]) + ) - cmd = [bin_dism, - '/Quiet', - '/Image:{0}'.format(image) if image else '/Online', - '/Remove-Capability', - '/CapabilityName:{0}'.format(capability)] + cmd = [ + bin_dism, + "/Quiet", + "/Image:{0}".format(image) if image else "/Online", + "/Remove-Capability", + "/CapabilityName:{0}".format(capability), + ] if not restart: - cmd.append('/NoRestart') + cmd.append("/NoRestart") - return __salt__['cmd.run_all'](cmd) + return __salt__["cmd.run_all"](cmd) def get_capabilities(image=None): - ''' + """ List all capabilities on the system Args: @@ -182,19 +187,22 @@ def get_capabilities(image=None): .. code-block:: bash salt '*' dism.get_capabilities - ''' - if salt.utils.versions.version_cmp(__grains__['osversion'], '10') == -1: + """ + if salt.utils.versions.version_cmp(__grains__["osversion"], "10") == -1: raise NotImplementedError( - '`installed_capabilities` is not available on this version of ' - 'Windows: {0}'.format(__grains__['osversion'])) + "`installed_capabilities` is not available on this version of " + "Windows: {0}".format(__grains__["osversion"]) + ) - cmd = [bin_dism, - '/English', - '/Image:{0}'.format(image) if image else '/Online', - '/Get-Capabilities'] - out = __salt__['cmd.run'](cmd) + cmd = [ + bin_dism, + "/English", + "/Image:{0}".format(image) if image else "/Online", + "/Get-Capabilities", + ] + out = __salt__["cmd.run"](cmd) - pattern = r'Capability Identity : (.*)\r\n' + pattern = r"Capability Identity : (.*)\r\n" capabilities = re.findall(pattern, out, re.MULTILINE) capabilities.sort() @@ -202,7 +210,7 @@ def get_capabilities(image=None): def installed_capabilities(image=None): - ''' + """ List the capabilities installed on the system Args: @@ -222,16 +230,17 @@ def installed_capabilities(image=None): .. code-block:: bash salt '*' dism.installed_capabilities - ''' - if salt.utils.versions.version_cmp(__grains__['osversion'], '10') == -1: + """ + if salt.utils.versions.version_cmp(__grains__["osversion"], "10") == -1: raise NotImplementedError( - '`installed_capabilities` is not available on this version of ' - 'Windows: {0}'.format(__grains__['osversion'])) + "`installed_capabilities` is not available on this version of " + "Windows: {0}".format(__grains__["osversion"]) + ) return _get_components("Capability Identity", "Capabilities", "Installed") def available_capabilities(image=None): - ''' + """ List the capabilities available on the system Args: @@ -251,22 +260,25 @@ def available_capabilities(image=None): .. code-block:: bash salt '*' dism.installed_capabilities - ''' - if salt.utils.versions.version_cmp(__grains__['osversion'], '10') == -1: + """ + if salt.utils.versions.version_cmp(__grains__["osversion"], "10") == -1: raise NotImplementedError( - '`installed_capabilities` is not available on this version of ' - 'Windows: {0}'.format(__grains__['osversion'])) + "`installed_capabilities` is not available on this version of " + "Windows: {0}".format(__grains__["osversion"]) + ) return _get_components("Capability Identity", "Capabilities", "Not Present") -def add_feature(feature, - package=None, - source=None, - limit_access=False, - enable_parent=False, - image=None, - restart=False): - ''' +def add_feature( + feature, + package=None, + source=None, + limit_access=False, + enable_parent=False, + image=None, + restart=False, +): + """ Install a feature using DISM Args: @@ -293,28 +305,30 @@ def add_feature(feature, .. code-block:: bash salt '*' dism.add_feature NetFx3 - ''' - cmd = [bin_dism, - '/Quiet', - '/Image:{0}'.format(image) if image else '/Online', - '/Enable-Feature', - '/FeatureName:{0}'.format(feature)] + """ + cmd = [ + bin_dism, + "/Quiet", + "/Image:{0}".format(image) if image else "/Online", + "/Enable-Feature", + "/FeatureName:{0}".format(feature), + ] if package: - cmd.append('/PackageName:{0}'.format(package)) + cmd.append("/PackageName:{0}".format(package)) if source: - cmd.append('/Source:{0}'.format(source)) + cmd.append("/Source:{0}".format(source)) if limit_access: - cmd.append('/LimitAccess') + cmd.append("/LimitAccess") if enable_parent: - cmd.append('/All') + cmd.append("/All") if not restart: - cmd.append('/NoRestart') + cmd.append("/NoRestart") - return __salt__['cmd.run_all'](cmd) + return __salt__["cmd.run_all"](cmd) def remove_feature(feature, remove_payload=False, image=None, restart=False): - ''' + """ Disables the feature. Args: @@ -334,23 +348,25 @@ def remove_feature(feature, remove_payload=False, image=None, restart=False): .. code-block:: bash salt '*' dism.remove_feature NetFx3 - ''' - cmd = [bin_dism, - '/Quiet', - '/Image:{0}'.format(image) if image else '/Online', - '/Disable-Feature', - '/FeatureName:{0}'.format(feature)] + """ + cmd = [ + bin_dism, + "/Quiet", + "/Image:{0}".format(image) if image else "/Online", + "/Disable-Feature", + "/FeatureName:{0}".format(feature), + ] if remove_payload: - cmd.append('/Remove') + cmd.append("/Remove") if not restart: - cmd.append('/NoRestart') + cmd.append("/NoRestart") - return __salt__['cmd.run_all'](cmd) + return __salt__["cmd.run_all"](cmd) def get_features(package=None, image=None): - ''' + """ List features on the system or in a package Args: @@ -380,21 +396,23 @@ def get_features(package=None, image=None): # Return all features in the calc package salt '*' dism.get_features Microsoft.Windows.Calc.Demo~6595b6144ccf1df~x86~en~1.0.0.0 - ''' - cmd = [bin_dism, - '/English', - '/Image:{0}'.format(image) if image else '/Online', - '/Get-Features'] + """ + cmd = [ + bin_dism, + "/English", + "/Image:{0}".format(image) if image else "/Online", + "/Get-Features", + ] if package: - if '~' in package: - cmd.append('/PackageName:{0}'.format(package)) + if "~" in package: + cmd.append("/PackageName:{0}".format(package)) else: - cmd.append('/PackagePath:{0}'.format(package)) + cmd.append("/PackagePath:{0}".format(package)) - out = __salt__['cmd.run'](cmd) + out = __salt__["cmd.run"](cmd) - pattern = r'Feature Name : (.*)\r\n' + pattern = r"Feature Name : (.*)\r\n" features = re.findall(pattern, out, re.MULTILINE) features.sort() @@ -402,7 +420,7 @@ def get_features(package=None, image=None): def installed_features(image=None): - ''' + """ List the features installed on the system Args: @@ -418,12 +436,12 @@ def installed_features(image=None): .. code-block:: bash salt '*' dism.installed_features - ''' + """ return _get_components("Feature Name", "Features", "Enabled") def available_features(image=None): - ''' + """ List the features available on the system Args: @@ -439,16 +457,14 @@ def available_features(image=None): .. code-block:: bash salt '*' dism.available_features - ''' + """ return _get_components("Feature Name", "Features", "Disabled") -def add_package(package, - ignore_check=False, - prevent_pending=False, - image=None, - restart=False): - ''' +def add_package( + package, ignore_check=False, prevent_pending=False, image=None, restart=False +): + """ Install a package using DISM Args: @@ -482,25 +498,27 @@ def add_package(package, .. code-block:: bash salt '*' dism.add_package C:\\Packages\\package.cab - ''' - cmd = [bin_dism, - '/Quiet', - '/Image:{0}'.format(image) if image else '/Online', - '/Add-Package', - '/PackagePath:{0}'.format(package)] + """ + cmd = [ + bin_dism, + "/Quiet", + "/Image:{0}".format(image) if image else "/Online", + "/Add-Package", + "/PackagePath:{0}".format(package), + ] if ignore_check: - cmd.append('/IgnoreCheck') + cmd.append("/IgnoreCheck") if prevent_pending: - cmd.append('/PreventPending') + cmd.append("/PreventPending") if not restart: - cmd.append('/NoRestart') + cmd.append("/NoRestart") - return __salt__['cmd.run_all'](cmd) + return __salt__["cmd.run_all"](cmd) def remove_package(package, image=None, restart=False): - ''' + """ Uninstall a package Args: @@ -525,25 +543,27 @@ def remove_package(package, image=None, restart=False): # Remove the package.cab (does not remove C:\\packages\\package.cab) salt '*' dism.remove_package C:\\packages\\package.cab - ''' - cmd = [bin_dism, - '/Quiet', - '/Image:{0}'.format(image) if image else '/Online', - '/Remove-Package'] + """ + cmd = [ + bin_dism, + "/Quiet", + "/Image:{0}".format(image) if image else "/Online", + "/Remove-Package", + ] if not restart: - cmd.append('/NoRestart') + cmd.append("/NoRestart") - if '~' in package: - cmd.append('/PackageName:{0}'.format(package)) + if "~" in package: + cmd.append("/PackageName:{0}".format(package)) else: - cmd.append('/PackagePath:{0}'.format(package)) + cmd.append("/PackagePath:{0}".format(package)) - return __salt__['cmd.run_all'](cmd) + return __salt__["cmd.run_all"](cmd) def installed_packages(image=None): - ''' + """ List the packages installed on the system Args: @@ -559,12 +579,12 @@ def installed_packages(image=None): .. code-block:: bash salt '*' dism.installed_packages - ''' + """ return _get_components("Package Identity", "Packages", "Installed") def package_info(package, image=None): - ''' + """ Display information about a package Args: @@ -584,24 +604,26 @@ def package_info(package, image=None): .. code-block:: bash salt '*' dism. package_info C:\\packages\\package.cab - ''' - cmd = [bin_dism, - '/English', - '/Image:{0}'.format(image) if image else '/Online', - '/Get-PackageInfo'] + """ + cmd = [ + bin_dism, + "/English", + "/Image:{0}".format(image) if image else "/Online", + "/Get-PackageInfo", + ] - if '~' in package: - cmd.append('/PackageName:{0}'.format(package)) + if "~" in package: + cmd.append("/PackageName:{0}".format(package)) else: - cmd.append('/PackagePath:{0}'.format(package)) + cmd.append("/PackagePath:{0}".format(package)) - out = __salt__['cmd.run_all'](cmd) + out = __salt__["cmd.run_all"](cmd) - if out['retcode'] == 0: + if out["retcode"] == 0: ret = dict() - for line in six.text_type(out['stdout']).splitlines(): - if ' : ' in line: - info = line.split(' : ') + for line in six.text_type(out["stdout"]).splitlines(): + if " : " in line: + info = line.split(" : ") if len(info) < 2: continue ret[info[0]] = info[1] diff --git a/salt/modules/win_dns_client.py b/salt/modules/win_dns_client.py index c6508f920de..aae1200bf19 100644 --- a/salt/modules/win_dns_client.py +++ b/salt/modules/win_dns_client.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Module for configuring DNS Client on Windows systems -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -13,6 +13,7 @@ import salt.utils.platform try: import wmi import salt.utils.winapi + HAS_LIBS = True except ImportError: HAS_LIBS = False @@ -21,19 +22,18 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Module win_dns_client: module only works on Windows ' \ - 'systems' + return False, "Module win_dns_client: module only works on Windows systems" if not HAS_LIBS: - return False, 'Module win_dns_client: missing required libraries' - return 'win_dns_client' + return False, "Module win_dns_client: missing required libraries" + return "win_dns_client" -def get_dns_servers(interface='Local Area Connection'): - ''' +def get_dns_servers(interface="Local Area Connection"): + """ Return a list of the configured DNS servers of the specified interface Args: @@ -48,16 +48,18 @@ def get_dns_servers(interface='Local Area Connection'): .. code-block:: bash salt '*' win_dns_client.get_dns_servers 'Local Area Connection' - ''' + """ # remove any escape characters - interface = interface.split('\\') - interface = ''.join(interface) + interface = interface.split("\\") + interface = "".join(interface) with salt.utils.winapi.Com(): c = wmi.WMI() for iface in c.Win32_NetworkAdapter(NetEnabled=True): if interface == iface.NetConnectionID: - iface_config = c.Win32_NetworkAdapterConfiguration(Index=iface.Index).pop() + iface_config = c.Win32_NetworkAdapterConfiguration( + Index=iface.Index + ).pop() try: return list(iface_config.DNSServerSearchOrder) except TypeError: @@ -66,8 +68,8 @@ def get_dns_servers(interface='Local Area Connection'): return False -def rm_dns(ip, interface='Local Area Connection'): - ''' +def rm_dns(ip, interface="Local Area Connection"): + """ Remove the DNS server from the network interface CLI Example: @@ -75,13 +77,13 @@ def rm_dns(ip, interface='Local Area Connection'): .. code-block:: bash salt '*' win_dns_client.rm_dns <ip> <interface> - ''' - cmd = ['netsh', 'interface', 'ip', 'delete', 'dns', interface, ip, 'validate=no'] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + """ + cmd = ["netsh", "interface", "ip", "delete", "dns", interface, ip, "validate=no"] + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 -def add_dns(ip, interface='Local Area Connection', index=1): - ''' +def add_dns(ip, interface="Local Area Connection", index=1): + """ Add the DNS server to the network interface (index starts from 1) @@ -93,7 +95,7 @@ def add_dns(ip, interface='Local Area Connection', index=1): .. code-block:: bash salt '*' win_dns_client.add_dns <ip> <interface> <index> - ''' + """ servers = get_dns_servers(interface) # Return False if could not find the interface @@ -111,14 +113,23 @@ def add_dns(ip, interface='Local Area Connection', index=1): if ip in servers: rm_dns(ip, interface) - cmd = ['netsh', 'interface', 'ip', 'add', 'dns', - interface, ip, 'index={0}'.format(index), 'validate=no'] + cmd = [ + "netsh", + "interface", + "ip", + "add", + "dns", + interface, + ip, + "index={0}".format(index), + "validate=no", + ] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 -def dns_dhcp(interface='Local Area Connection'): - ''' +def dns_dhcp(interface="Local Area Connection"): + """ Configure the interface to get its DNS servers from the DHCP server CLI Example: @@ -126,13 +137,13 @@ def dns_dhcp(interface='Local Area Connection'): .. code-block:: bash salt '*' win_dns_client.dns_dhcp <interface> - ''' - cmd = ['netsh', 'interface', 'ip', 'set', 'dns', interface, 'source=dhcp'] - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + """ + cmd = ["netsh", "interface", "ip", "set", "dns", interface, "source=dhcp"] + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 -def get_dns_config(interface='Local Area Connection'): - ''' +def get_dns_config(interface="Local Area Connection"): + """ Get the type of DNS configuration (dhcp / static). Args: @@ -147,10 +158,10 @@ def get_dns_config(interface='Local Area Connection'): .. code-block:: bash salt '*' win_dns_client.get_dns_config 'Local Area Connection' - ''' + """ # remove any escape characters - interface = interface.split('\\') - interface = ''.join(interface) + interface = interface.split("\\") + interface = "".join(interface) with salt.utils.winapi.Com(): c = wmi.WMI() diff --git a/salt/modules/win_dsc.py b/salt/modules/win_dsc.py index fae0317e9c9..70f0cb62a72 100644 --- a/salt/modules/win_dsc.py +++ b/salt/modules/win_dsc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for working with Windows PowerShell DSC (Desired State Configuration) This module is Alpha @@ -14,8 +14,8 @@ the Minion. :depends: - PowerShell 5.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -31,75 +31,81 @@ from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'dsc' +__virtualname__ = "dsc" def __virtual__(): - ''' + """ Set the system module of the kernel is Windows - ''' + """ # Verify Windows if not salt.utils.platform.is_windows(): - log.debug('DSC: Only available on Windows systems') - return False, 'DSC: Only available on Windows systems' + log.debug("DSC: Only available on Windows systems") + return False, "DSC: Only available on Windows systems" # Verify PowerShell - powershell_info = __salt__['cmd.shell_info']('powershell') - if not powershell_info['installed']: - log.debug('DSC: Requires PowerShell') - return False, 'DSC: Requires PowerShell' + powershell_info = __salt__["cmd.shell_info"]("powershell") + if not powershell_info["installed"]: + log.debug("DSC: Requires PowerShell") + return False, "DSC: Requires PowerShell" # Verify PowerShell 5.0 or greater - if salt.utils.versions.compare(powershell_info['version'], '<', '5.0'): - log.debug('DSC: Requires PowerShell 5 or later') - return False, 'DSC: Requires PowerShell 5 or later' + if salt.utils.versions.compare(powershell_info["version"], "<", "5.0"): + log.debug("DSC: Requires PowerShell 5 or later") + return False, "DSC: Requires PowerShell 5 or later" return __virtualname__ def _pshell(cmd, cwd=None, json_depth=2, ignore_retcode=False): - ''' + """ Execute the desired PowerShell command and ensure that it returns data in json format and load that into python. Either return a dict or raise a CommandExecutionError. - ''' - if 'convertto-json' not in cmd.lower(): - cmd = '{0} | ConvertTo-Json -Depth {1}'.format(cmd, json_depth) - log.debug('DSC: %s', cmd) - results = __salt__['cmd.run_all']( - cmd, shell='powershell', cwd=cwd, python_shell=True, - ignore_retcode=ignore_retcode) + """ + if "convertto-json" not in cmd.lower(): + cmd = "{0} | ConvertTo-Json -Depth {1}".format(cmd, json_depth) + log.debug("DSC: %s", cmd) + results = __salt__["cmd.run_all"]( + cmd, + shell="powershell", + cwd=cwd, + python_shell=True, + ignore_retcode=ignore_retcode, + ) - if 'pid' in results: - del results['pid'] + if "pid" in results: + del results["pid"] - if 'retcode' not in results or results['retcode'] != 0: + if "retcode" not in results or results["retcode"] != 0: # run_all logs an error to log.error, fail hard back to the user raise CommandExecutionError( - 'Issue executing PowerShell {0}'.format(cmd), info=results) + "Issue executing PowerShell {0}".format(cmd), info=results + ) # Sometimes Powershell returns an empty string, which isn't valid JSON - if results['stdout'] == '': - results['stdout'] = '{}' + if results["stdout"] == "": + results["stdout"] = "{}" try: - ret = salt.utils.json.loads(results['stdout'], strict=False) + ret = salt.utils.json.loads(results["stdout"], strict=False) except ValueError: - raise CommandExecutionError( - 'No JSON results from PowerShell', info=results) + raise CommandExecutionError("No JSON results from PowerShell", info=results) log.info('DSC: Returning "{0}"'.format(ret)) return ret -def run_config(path, - source=None, - config_name=None, - config_data=None, - config_data_source=None, - script_parameters=None, - salt_env='base'): - r''' +def run_config( + path, + source=None, + config_name=None, + config_data=None, + config_data_source=None, + script_parameters=None, + salt_env="base", +): + r""" Compile a DSC Configuration in the form of a PowerShell script (.ps1) and apply it. The PowerShell script can be cached from the master using the ``source`` option. If there is more than one config within the PowerShell @@ -164,30 +170,34 @@ def run_config(path, .. code-block:: bash salt '*' dsc.run_config C:\\DSC\\WebsiteConfig.ps1 salt://dsc/configs/WebsiteConfig.ps1 - ''' - ret = compile_config(path=path, - source=source, - config_name=config_name, - config_data=config_data, - config_data_source=config_data_source, - script_parameters=script_parameters, - salt_env=salt_env) + """ + ret = compile_config( + path=path, + source=source, + config_name=config_name, + config_data=config_data, + config_data_source=config_data_source, + script_parameters=script_parameters, + salt_env=salt_env, + ) - if ret.get('Exists'): - config_path = os.path.dirname(ret['FullName']) + if ret.get("Exists"): + config_path = os.path.dirname(ret["FullName"]) return apply_config(config_path) else: return False -def compile_config(path, - source=None, - config_name=None, - config_data=None, - config_data_source=None, - script_parameters=None, - salt_env='base'): - r''' +def compile_config( + path, + source=None, + config_name=None, + config_data=None, + config_data_source=None, + script_parameters=None, + salt_env="base", +): + r""" Compile a config from a PowerShell script (``.ps1``) Args: @@ -246,33 +256,31 @@ def compile_config(path, .. code-block:: bash salt '*' dsc.compile_config C:\\DSC\\WebsiteConfig.ps1 salt://dsc/configs/WebsiteConfig.ps1 - ''' + """ if source: - log.info('DSC: Caching %s', source) - cached_files = __salt__['cp.get_file'](path=source, - dest=path, - saltenv=salt_env, - makedirs=True) + log.info("DSC: Caching %s", source) + cached_files = __salt__["cp.get_file"]( + path=source, dest=path, saltenv=salt_env, makedirs=True + ) if not cached_files: - error = 'Failed to cache {0}'.format(source) - log.error('DSC: {0}'.format(error)) + error = "Failed to cache {0}".format(source) + log.error("DSC: {0}".format(error)) raise CommandExecutionError(error) if config_data_source: - log.info('DSC: Caching %s', config_data_source) - cached_files = __salt__['cp.get_file'](path=config_data_source, - dest=config_data, - saltenv=salt_env, - makedirs=True) + log.info("DSC: Caching %s", config_data_source) + cached_files = __salt__["cp.get_file"]( + path=config_data_source, dest=config_data, saltenv=salt_env, makedirs=True + ) if not cached_files: - error = 'Failed to cache {0}'.format(config_data_source) - log.error('DSC: {0}'.format(error)) + error = "Failed to cache {0}".format(config_data_source) + log.error("DSC: {0}".format(error)) raise CommandExecutionError(error) # Make sure the path exists if not os.path.exists(path): error = '"{0}" not found'.format(path) - log.error('DSC: {0}'.format(error)) + log.error("DSC: {0}".format(error)) raise CommandExecutionError(error) if config_name is None: @@ -287,50 +295,54 @@ def compile_config(path, if script_parameters: cmd.append(script_parameters) # Select fields to return - cmd.append('| Select-Object -Property FullName, Extension, Exists, ' - '@{Name="LastWriteTime";Expression={Get-Date ($_.LastWriteTime) ' - '-Format g}}') + cmd.append( + "| Select-Object -Property FullName, Extension, Exists, " + '@{Name="LastWriteTime";Expression={Get-Date ($_.LastWriteTime) ' + "-Format g}}" + ) - cmd = ' '.join(cmd) + cmd = " ".join(cmd) ret = _pshell(cmd, cwd) if ret: # Script compiled, return results - if ret.get('Exists'): - log.info('DSC: Compile Config: %s', ret) + if ret.get("Exists"): + log.info("DSC: Compile Config: %s", ret) return ret # If you get to this point, the script did not contain a compile command # dot source the script to compile the state and generate the mof file - cmd = ['.', path] + cmd = [".", path] if script_parameters: cmd.append(script_parameters) - cmd.extend([';', config_name]) + cmd.extend([";", config_name]) if config_data: - cmd.append(config_data) - cmd.append('| Select-Object -Property FullName, Extension, Exists, ' - '@{Name="LastWriteTime";Expression={Get-Date ($_.LastWriteTime) ' - '-Format g}}') + cmd.extend(["-ConfigurationData", config_data]) + cmd.append( + "| Select-Object -Property FullName, Extension, Exists, " + '@{Name="LastWriteTime";Expression={Get-Date ($_.LastWriteTime) ' + "-Format g}}" + ) - cmd = ' '.join(cmd) + cmd = " ".join(cmd) ret = _pshell(cmd, cwd) if ret: # Script compiled, return results - if ret.get('Exists'): - log.info('DSC: Compile Config: %s', ret) + if ret.get("Exists"): + log.info("DSC: Compile Config: %s", ret) return ret - error = 'Failed to compile config: {0}'.format(path) - error += '\nReturned: {0}'.format(ret) - log.error('DSC: %s', error) + error = "Failed to compile config: {0}".format(path) + error += "\nReturned: {0}".format(ret) + log.error("DSC: %s", error) raise CommandExecutionError(error) -def apply_config(path, source=None, salt_env='base'): - r''' +def apply_config(path, source=None, salt_env="base"): + r""" Run an compiled DSC configuration (a folder containing a .mof file). The folder can be cached from the salt master using the ``source`` option. @@ -365,7 +377,7 @@ def apply_config(path, source=None, salt_env='base'): salt '*' dsc.apply_config C:\\DSC\\WebSiteConfiguration salt://dsc/configs/WebSiteConfiguration - ''' + """ # If you're getting an error along the lines of "The client cannot connect # to the destination specified in the request.", try the following: # Enable-PSRemoting -SkipNetworkProfileCheck @@ -376,24 +388,24 @@ def apply_config(path, source=None, salt_env='base'): source_name = os.path.basename(os.path.normpath(source)) if path_name.lower() != source_name.lower(): # Append the Source name to the Path - path = '{0}\\{1}'.format(path, source_name) - log.debug('DSC: %s appended to the path.', source_name) + path = "{0}\\{1}".format(path, source_name) + log.debug("DSC: %s appended to the path.", source_name) # Destination path minus the basename dest_path = os.path.dirname(os.path.normpath(path)) - log.info('DSC: Caching %s', source) - cached_files = __salt__['cp.get_dir'](source, dest_path, salt_env) + log.info("DSC: Caching %s", source) + cached_files = __salt__["cp.get_dir"](source, dest_path, salt_env) if not cached_files: - error = 'Failed to copy {0}'.format(source) - log.error('DSC: {0}'.format(error)) + error = "Failed to copy {0}".format(source) + log.error("DSC: {0}".format(error)) raise CommandExecutionError(error) else: config = os.path.dirname(cached_files[0]) # Make sure the path exists if not os.path.exists(config): - error = '{0} not found'.format(config) - log.error('DSC: {0}'.format(error)) + error = "{0} not found".format(config) + log.error("DSC: {0}".format(error)) raise CommandExecutionError(error) # Run the DSC Configuration @@ -401,15 +413,15 @@ def apply_config(path, source=None, salt_env='base'): cmd = 'Start-DscConfiguration -Path "{0}" -Wait -Force'.format(config) _pshell(cmd) - cmd = '$status = Get-DscConfigurationStatus; $status.Status' + cmd = "$status = Get-DscConfigurationStatus; $status.Status" ret = _pshell(cmd) - log.info('DSC: Apply Config: %s', ret) + log.info("DSC: Apply Config: %s", ret) - return ret == 'Success' or ret == {} + return ret == "Success" or ret == {} def get_config(): - ''' + """ Get the current DSC Configuration Returns: @@ -423,33 +435,35 @@ def get_config(): .. code-block:: bash salt '*' dsc.get_config - ''' - cmd = 'Get-DscConfiguration | Select-Object * -ExcludeProperty Cim*' + """ + cmd = "Get-DscConfiguration | Select-Object * -ExcludeProperty Cim*" try: raw_config = _pshell(cmd, ignore_retcode=True) except CommandExecutionError as exc: - if 'Current configuration does not exist' in exc.info['stderr']: - raise CommandExecutionError('Not Configured') + if "Current configuration does not exist" in exc.info["stderr"]: + raise CommandExecutionError("Not Configured") raise config = dict() if raw_config: # Get DSC Configuration Name - if 'ConfigurationName' in raw_config[0]: - config[raw_config[0]['ConfigurationName']] = {} + if "ConfigurationName" in raw_config[0]: + config[raw_config[0]["ConfigurationName"]] = {} # Add all DSC Configurations by ResourceId for item in raw_config: - config[item['ConfigurationName']][item['ResourceId']] = {} + config[item["ConfigurationName"]][item["ResourceId"]] = {} for key in item: - if key not in ['ConfigurationName', 'ResourceId']: - config[item['ConfigurationName']][item['ResourceId']][key] = item[key] + if key not in ["ConfigurationName", "ResourceId"]: + config[item["ConfigurationName"]][item["ResourceId"]][key] = item[ + key + ] return config def remove_config(reset=False): - ''' + """ Remove the current DSC Configuration. Removes current, pending, and previous dsc configurations. @@ -482,58 +496,60 @@ def remove_config(reset=False): .. code-block:: bash salt '*' dsc.remove_config True - ''' + """ # Stopping a running config (not likely to occur) - cmd = 'Stop-DscConfiguration' - log.info('DSC: Stopping Running Configuration') + cmd = "Stop-DscConfiguration" + log.info("DSC: Stopping Running Configuration") try: _pshell(cmd) except CommandExecutionError as exc: - if exc.info['retcode'] != 0: - raise CommandExecutionError('Failed to Stop DSC Configuration', - info=exc.info) - log.info('DSC: {0}'.format(exc.info['stdout'])) + if exc.info["retcode"] != 0: + raise CommandExecutionError( + "Failed to Stop DSC Configuration", info=exc.info + ) + log.info("DSC: {0}".format(exc.info["stdout"])) # Remove configuration files - cmd = 'Remove-DscConfigurationDocument -Stage Current, Pending, Previous ' \ - '-Force' - log.info('DSC: Removing Configuration') + cmd = "Remove-DscConfigurationDocument -Stage Current, Pending, Previous " "-Force" + log.info("DSC: Removing Configuration") try: _pshell(cmd) except CommandExecutionError as exc: - if exc.info['retcode'] != 0: - raise CommandExecutionError('Failed to remove DSC Configuration', - info=exc.info) - log.info('DSC: {0}'.format(exc.info['stdout'])) + if exc.info["retcode"] != 0: + raise CommandExecutionError( + "Failed to remove DSC Configuration", info=exc.info + ) + log.info("DSC: {0}".format(exc.info["stdout"])) if not reset: return True def _remove_fs_obj(path): if os.path.exists(path): - log.info('DSC: Removing {0}'.format(path)) - if not __salt__['file.remove'](path): - error = 'Failed to remove {0}'.format(path) - log.error('DSC: {0}'.format(error)) + log.info("DSC: Removing {0}".format(path)) + if not __salt__["file.remove"](path): + error = "Failed to remove {0}".format(path) + log.error("DSC: {0}".format(error)) raise CommandExecutionError(error) - dsc_config_dir = '{0}\\System32\\Configuration' \ - ''.format(os.getenv('SystemRoot', 'C:\\Windows')) + dsc_config_dir = "{0}\\System32\\Configuration" "".format( + os.getenv("SystemRoot", "C:\\Windows") + ) # Remove History - _remove_fs_obj('{0}\\DSCStatusHistory.mof'.format(dsc_config_dir)) + _remove_fs_obj("{0}\\DSCStatusHistory.mof".format(dsc_config_dir)) # Remove Engine Cache - _remove_fs_obj('{0}\\DSCEngineCache.mof'.format(dsc_config_dir)) + _remove_fs_obj("{0}\\DSCEngineCache.mof".format(dsc_config_dir)) # Remove Status Directory - _remove_fs_obj('{0}\\ConfigurationStatus'.format(dsc_config_dir)) + _remove_fs_obj("{0}\\ConfigurationStatus".format(dsc_config_dir)) return True def restore_config(): - ''' + """ Reapplies the previous configuration. .. versionadded:: 2017.7.5 @@ -553,19 +569,19 @@ def restore_config(): .. code-block:: bash salt '*' dsc.restore_config - ''' - cmd = 'Restore-DscConfiguration' + """ + cmd = "Restore-DscConfiguration" try: _pshell(cmd, ignore_retcode=True) except CommandExecutionError as exc: - if 'A previous configuration does not exist' in exc.info['stderr']: - raise CommandExecutionError('Previous Configuration Not Found') + if "A previous configuration does not exist" in exc.info["stderr"]: + raise CommandExecutionError("Previous Configuration Not Found") raise return True def test_config(): - ''' + """ Tests the current applied DSC Configuration Returns: @@ -576,18 +592,18 @@ def test_config(): .. code-block:: bash salt '*' dsc.test_config - ''' - cmd = 'Test-DscConfiguration' + """ + cmd = "Test-DscConfiguration" try: _pshell(cmd, ignore_retcode=True) except CommandExecutionError as exc: - if 'Current configuration does not exist' in exc.info['stderr']: - raise CommandExecutionError('Not Configured') + if "Current configuration does not exist" in exc.info["stderr"]: + raise CommandExecutionError("Not Configured") raise def get_config_status(): - ''' + """ Get the status of the current DSC Configuration Returns: @@ -599,21 +615,23 @@ def get_config_status(): .. code-block:: bash salt '*' dsc.get_config_status - ''' - cmd = 'Get-DscConfigurationStatus | ' \ - 'Select-Object -Property HostName, Status, MetaData, ' \ - '@{Name="StartDate";Expression={Get-Date ($_.StartDate) -Format g}}, ' \ - 'Type, Mode, RebootRequested, NumberofResources' + """ + cmd = ( + "Get-DscConfigurationStatus | " + "Select-Object -Property HostName, Status, MetaData, " + '@{Name="StartDate";Expression={Get-Date ($_.StartDate) -Format g}}, ' + "Type, Mode, RebootRequested, NumberofResources" + ) try: return _pshell(cmd, ignore_retcode=True) except CommandExecutionError as exc: - if 'No status information available' in exc.info['stderr']: - raise CommandExecutionError('Not Configured') + if "No status information available" in exc.info["stderr"]: + raise CommandExecutionError("Not Configured") raise def get_lcm_config(): - ''' + """ Get the current Local Configuration Manager settings Returns: @@ -625,27 +643,31 @@ def get_lcm_config(): .. code-block:: bash salt '*' dsc.get_lcm_config - ''' - cmd = 'Get-DscLocalConfigurationManager | ' \ - 'Select-Object -Property ConfigurationModeFrequencyMins, LCMState, ' \ - 'RebootNodeIfNeeded, ConfigurationMode, ActionAfterReboot, ' \ - 'RefreshMode, CertificateID, ConfigurationID, RefreshFrequencyMins, ' \ - 'AllowModuleOverwrite, DebugMode, StatusRetentionTimeInDays ' + """ + cmd = ( + "Get-DscLocalConfigurationManager | " + "Select-Object -Property ConfigurationModeFrequencyMins, LCMState, " + "RebootNodeIfNeeded, ConfigurationMode, ActionAfterReboot, " + "RefreshMode, CertificateID, ConfigurationID, RefreshFrequencyMins, " + "AllowModuleOverwrite, DebugMode, StatusRetentionTimeInDays " + ) return _pshell(cmd) -def set_lcm_config(config_mode=None, - config_mode_freq=None, - refresh_freq=None, - reboot_if_needed=None, - action_after_reboot=None, - refresh_mode=None, - certificate_id=None, - configuration_id=None, - allow_module_overwrite=None, - debug_mode=False, - status_retention_days=None): - ''' +def set_lcm_config( + config_mode=None, + config_mode_freq=None, + refresh_freq=None, + reboot_if_needed=None, + action_after_reboot=None, + refresh_mode=None, + certificate_id=None, + configuration_id=None, + allow_module_overwrite=None, + debug_mode=False, + status_retention_days=None, +): + """ For detailed descriptions of the parameters see: https://msdn.microsoft.com/en-us/PowerShell/DSC/metaConfig @@ -708,94 +730,99 @@ def set_lcm_config(config_mode=None, .. code-block:: bash salt '*' dsc.set_lcm_config ApplyOnly - ''' - temp_dir = os.getenv('TEMP', '{0}\\temp'.format(os.getenv('WINDIR'))) - cmd = 'Configuration SaltConfig {' - cmd += ' Node localhost {' - cmd += ' LocalConfigurationManager {' + """ + temp_dir = os.getenv("TEMP", "{0}\\temp".format(os.getenv("WINDIR"))) + cmd = "Configuration SaltConfig {" + cmd += " Node localhost {" + cmd += " LocalConfigurationManager {" if config_mode: - if config_mode not in ('ApplyOnly', 'ApplyAndMonitor', - 'ApplyAndAutoCorrect'): - error = 'config_mode must be one of ApplyOnly, ApplyAndMonitor, ' \ - 'or ApplyAndAutoCorrect. Passed {0}'.format(config_mode) + if config_mode not in ("ApplyOnly", "ApplyAndMonitor", "ApplyAndAutoCorrect"): + error = ( + "config_mode must be one of ApplyOnly, ApplyAndMonitor, " + "or ApplyAndAutoCorrect. Passed {0}".format(config_mode) + ) raise SaltInvocationError(error) cmd += ' ConfigurationMode = "{0}";'.format(config_mode) if config_mode_freq: if not isinstance(config_mode_freq, int): - error = 'config_mode_freq must be an integer. Passed {0}'.format( + error = "config_mode_freq must be an integer. Passed {0}".format( config_mode_freq ) raise SaltInvocationError(error) - cmd += ' ConfigurationModeFrequencyMins = {0};'.format(config_mode_freq) + cmd += " ConfigurationModeFrequencyMins = {0};".format( + config_mode_freq + ) if refresh_mode: - if refresh_mode not in ('Disabled', 'Push', 'Pull'): + if refresh_mode not in ("Disabled", "Push", "Pull"): raise SaltInvocationError( - 'refresh_mode must be one of Disabled, Push, or Pull' + "refresh_mode must be one of Disabled, Push, or Pull" ) cmd += ' RefreshMode = "{0}";'.format(refresh_mode) if refresh_freq: if not isinstance(refresh_freq, int): - raise SaltInvocationError('refresh_freq must be an integer') - cmd += ' RefreshFrequencyMins = {0};'.format(refresh_freq) + raise SaltInvocationError("refresh_freq must be an integer") + cmd += " RefreshFrequencyMins = {0};".format(refresh_freq) if reboot_if_needed is not None: if not isinstance(reboot_if_needed, bool): - raise SaltInvocationError('reboot_if_needed must be a boolean value') + raise SaltInvocationError("reboot_if_needed must be a boolean value") if reboot_if_needed: - reboot_if_needed = '$true' + reboot_if_needed = "$true" else: - reboot_if_needed = '$false' - cmd += ' RebootNodeIfNeeded = {0};'.format(reboot_if_needed) + reboot_if_needed = "$false" + cmd += " RebootNodeIfNeeded = {0};".format(reboot_if_needed) if action_after_reboot: - if action_after_reboot not in ('ContinueConfiguration', - 'StopConfiguration'): + if action_after_reboot not in ("ContinueConfiguration", "StopConfiguration"): raise SaltInvocationError( - 'action_after_reboot must be one of ' - 'ContinueConfiguration or StopConfiguration' + "action_after_reboot must be one of " + "ContinueConfiguration or StopConfiguration" ) cmd += ' ActionAfterReboot = "{0}"'.format(action_after_reboot) if certificate_id is not None: - if certificate_id == '': + if certificate_id == "": certificate_id = None cmd += ' CertificateID = "{0}";'.format(certificate_id) if configuration_id is not None: - if configuration_id == '': + if configuration_id == "": configuration_id = None cmd += ' ConfigurationID = "{0}";'.format(configuration_id) if allow_module_overwrite is not None: if not isinstance(allow_module_overwrite, bool): - raise SaltInvocationError('allow_module_overwrite must be a boolean value') + raise SaltInvocationError("allow_module_overwrite must be a boolean value") if allow_module_overwrite: - allow_module_overwrite = '$true' + allow_module_overwrite = "$true" else: - allow_module_overwrite = '$false' - cmd += ' AllowModuleOverwrite = {0};'.format(allow_module_overwrite) + allow_module_overwrite = "$false" + cmd += " AllowModuleOverwrite = {0};".format(allow_module_overwrite) if debug_mode is not False: if debug_mode is None: - debug_mode = 'None' - if debug_mode not in ('None', 'ForceModuleImport', 'All'): + debug_mode = "None" + if debug_mode not in ("None", "ForceModuleImport", "All"): raise SaltInvocationError( - 'debug_mode must be one of None, ForceModuleImport, ' - 'ResourceScriptBreakAll, or All' + "debug_mode must be one of None, ForceModuleImport, " + "ResourceScriptBreakAll, or All" ) cmd += ' DebugMode = "{0}";'.format(debug_mode) if status_retention_days: if not isinstance(status_retention_days, int): - raise SaltInvocationError('status_retention_days must be an integer') - cmd += ' StatusRetentionTimeInDays = {0};'.format(status_retention_days) - cmd += ' }}};' + raise SaltInvocationError("status_retention_days must be an integer") + cmd += " StatusRetentionTimeInDays = {0};".format( + status_retention_days + ) + cmd += " }}};" cmd += r'SaltConfig -OutputPath "{0}\SaltConfig"'.format(temp_dir) # Execute Config to create the .mof _pshell(cmd) # Apply the config - cmd = r'Set-DscLocalConfigurationManager -Path "{0}\SaltConfig"' \ - r''.format(temp_dir) - ret = __salt__['cmd.run_all'](cmd, shell='powershell', python_shell=True) - __salt__['file.remove'](r'{0}\SaltConfig'.format(temp_dir)) - if not ret['retcode']: - log.info('DSC: LCM config applied successfully') + cmd = r'Set-DscLocalConfigurationManager -Path "{0}\SaltConfig"' r"".format( + temp_dir + ) + ret = __salt__["cmd.run_all"](cmd, shell="powershell", python_shell=True) + __salt__["file.remove"](r"{0}\SaltConfig".format(temp_dir)) + if not ret["retcode"]: + log.info("DSC: LCM config applied successfully") return True else: - log.error('DSC: Failed to apply LCM config. Error %s', ret) + log.error("DSC: Failed to apply LCM config. Error %s", ret) return False diff --git a/salt/modules/win_file.py b/salt/modules/win_file.py index 569dd7c88c6..fc2b7bf10c1 100644 --- a/salt/modules/win_file.py +++ b/salt/modules/win_file.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage information about files on the minion, set/read user, group data, modify the ACL of files/directories @@ -7,64 +7,131 @@ data, modify the ACL of files/directories - win32file - win32con - salt.utils.win_dacl -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +# pylint: disable=unused-import +import contextlib # do not remove, used in imported file.py functions +import datetime # do not remove. +import difflib # do not remove, used in imported file.py functions +import errno # do not remove, used in imported file.py functions +import fnmatch # do not remove, used in imported file.py functions +import glob # do not remove, used in imported file.py functions +import hashlib # do not remove, used in imported file.py functions +import io # do not remove, used in imported file.py functions +import itertools # same as above, do not remove, it's used in __clean_tmp +import logging +import mmap # do not remove, used in imported file.py functions +import operator # do not remove # Import python libs import os -import stat import os.path -import logging -# pylint: disable=W0611 -import operator # do not remove -from collections import Iterable, Mapping # do not remove -from functools import reduce # do not remove -import datetime # do not remove. -import tempfile # do not remove. Used in salt.modules.file.__clean_tmp -import itertools # same as above, do not remove, it's used in __clean_tmp -import contextlib # do not remove, used in imported file.py functions -import difflib # do not remove, used in imported file.py functions -import hashlib # do not remove, used in imported file.py functions -import errno # do not remove, used in imported file.py functions -import shutil # do not remove, used in imported file.py functions import re # do not remove, used in imported file.py functions +import shutil # do not remove, used in imported file.py functions +import stat import string # do not remove, used in imported file.py functions import sys # do not remove, used in imported file.py functions -import io # do not remove, used in imported file.py functions -import fnmatch # do not remove, used in imported file.py functions -import mmap # do not remove, used in imported file.py functions -import glob # do not remove, used in imported file.py functions -# do not remove, used in imported file.py functions -from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=import-error,no-name-in-module +import tempfile # do not remove. Used in salt.modules.file.__clean_tmp + +# pylint: disable=no-name-in-module +from collections import Iterable, Mapping # do not remove + +# pylint: enable=no-name-in-module +from functools import reduce # do not remove + import salt.utils.atomicfile # do not remove, used in imported file.py functions -from salt.exceptions import CommandExecutionError, SaltInvocationError -# pylint: enable=W0611 # Import salt libs import salt.utils.path import salt.utils.platform import salt.utils.user -from salt.modules.file import (check_hash, # pylint: disable=W0611 - directory_exists, get_managed, - check_managed, check_managed_changes, source_list, - touch, append, contains, contains_regex, get_source_sum, - contains_glob, find, psed, get_sum, _get_bkroot, _mkstemp_copy, - get_hash, manage_file, file_exists, get_diff, line, list_backups, - __clean_tmp, check_file_meta, _binary_replace, - _splitlines_preserving_trailing_newline, restore_backup, - access, copy, readdir, read, rmdir, truncate, replace, delete_backup, - search, _get_flags, extract_hash, _error, _sed_esc, _psed, - RE_FLAG_TABLE, blockreplace, prepend, seek_read, seek_write, rename, - lstat, path_exists_glob, write, pardir, join, HASHES, HASHES_REVMAP, - comment, uncomment, _add_flags, comment_line, _regex_to_static, - _set_line_indent, apply_template_on_contents, dirname, basename, - list_backups_dir, _assert_occurrence, _starts_till, _set_line_eol, _get_eol, - _insert_line_after, _insert_line_before) -from salt.modules.file import normpath as normpath_ +from salt.exceptions import CommandExecutionError, SaltInvocationError +# do not remove, used in imported file.py functions +from salt.ext import six +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse +from salt.modules.file import check_hash # pylint: disable=W0611 +from salt.modules.file import ( + HASHES, + HASHES_REVMAP, + RE_FLAG_TABLE, + __clean_tmp, + _add_flags, + _assert_occurrence, + _binary_replace, + _error, + _get_bkroot, + _get_eol, + _get_flags, + _insert_line_after, + _insert_line_before, + _mkstemp_copy, + _psed, + _regex_to_static, + _sed_esc, + _set_line_eol, + _set_line_indent, + _splitlines_preserving_trailing_newline, + _starts_till, + access, + append, + apply_template_on_contents, + basename, + blockreplace, + check_file_meta, + check_managed, + check_managed_changes, + comment, + comment_line, + contains, + contains_glob, + contains_regex, + copy, + delete_backup, + directory_exists, + dirname, + extract_hash, + file_exists, + find, + get_diff, + get_hash, + get_managed, + get_source_sum, + get_sum, + join, + line, + list_backups, + list_backups_dir, + lstat, + manage_file, +) +from salt.modules.file import normpath as normpath_ +from salt.modules.file import ( + pardir, + path_exists_glob, + prepend, + psed, + read, + readdir, + rename, + replace, + restore_backup, + rmdir, + search, + seek_read, + seek_write, + source_list, + touch, + truncate, + uncomment, + write, +) from salt.utils.functools import namespaced_function as _namespaced_function +# pylint: enable=W0611 + + HAS_WINDOWS_MODULES = False try: if salt.utils.platform.is_windows(): @@ -73,6 +140,7 @@ try: import win32file import win32security import salt.platform.win + HAS_WINDOWS_MODULES = True except ImportError: HAS_WINDOWS_MODULES = False @@ -81,13 +149,16 @@ except ImportError: try: from exceptions import WindowsError # pylint: disable=no-name-in-module except ImportError: + class WindowsError(OSError): pass + HAS_WIN_DACL = False try: if salt.utils.platform.is_windows(): import salt.utils.win_dacl + HAS_WIN_DACL = True except ImportError: HAS_WIN_DACL = False @@ -95,13 +166,13 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'file' +__virtualname__ = "file" def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if salt.utils.platform.is_windows(): if HAS_WINDOWS_MODULES: # Load functions from file.py @@ -127,7 +198,8 @@ def __virtual__(): _get_flags = _namespaced_function(_get_flags, globals()) _binary_replace = _namespaced_function(_binary_replace, globals()) _splitlines_preserving_trailing_newline = _namespaced_function( - _splitlines_preserving_trailing_newline, globals()) + _splitlines_preserving_trailing_newline, globals() + ) _error = _namespaced_function(_error, globals()) _get_bkroot = _namespaced_function(_get_bkroot, globals()) list_backups = _namespaced_function(list_backups, globals()) @@ -137,7 +209,9 @@ def __virtual__(): append = _namespaced_function(append, globals()) get_managed = _namespaced_function(get_managed, globals()) check_managed = _namespaced_function(check_managed, globals()) - check_managed_changes = _namespaced_function(check_managed_changes, globals()) + check_managed_changes = _namespaced_function( + check_managed_changes, globals() + ) check_file_meta = _namespaced_function(check_file_meta, globals()) manage_file = _namespaced_function(manage_file, globals()) source_list = _namespaced_function(source_list, globals()) @@ -183,7 +257,9 @@ def __virtual__(): _insert_line_before = _namespaced_function(_insert_line_before, globals()) _mkstemp_copy = _namespaced_function(_mkstemp_copy, globals()) _add_flags = _namespaced_function(_add_flags, globals()) - apply_template_on_contents = _namespaced_function(apply_template_on_contents, globals()) + apply_template_on_contents = _namespaced_function( + apply_template_on_contents, globals() + ) dirname = _namespaced_function(dirname, globals()) basename = _namespaced_function(basename, globals()) list_backups_dir = _namespaced_function(list_backups_dir, globals()) @@ -192,52 +268,54 @@ def __virtual__(): _starts_till = _namespaced_function(_starts_till, globals()) else: - return False, 'Module win_file: Missing Win32 modules' + return False, "Module win_file: Missing Win32 modules" if not HAS_WIN_DACL: - return False, 'Module win_file: Unable to load salt.utils.win_dacl' + return False, "Module win_file: Unable to load salt.utils.win_dacl" return __virtualname__ __outputter__ = { - 'touch': 'txt', - 'append': 'txt', + "touch": "txt", + "append": "txt", } __func_alias__ = { - 'makedirs_': 'makedirs', - 'normpath_': 'normpath', + "makedirs_": "makedirs", + "normpath_": "normpath", } def _resolve_symlink(path, max_depth=64): - ''' + """ Resolves the given symlink path to its real path, up to a maximum of the `max_depth` parameter which defaults to 64. If the path is not a symlink path, it is simply returned. - ''' + """ if sys.getwindowsversion().major < 6: - raise SaltInvocationError('Symlinks are only supported on Windows Vista or later.') + raise SaltInvocationError( + "Symlinks are only supported on Windows Vista or later." + ) # make sure we don't get stuck in a symlink loop! - paths_seen = set((path, )) + paths_seen = set((path,)) cur_depth = 0 while is_link(path): path = readlink(path) if path in paths_seen: - raise CommandExecutionError('The given path is involved in a symlink loop.') + raise CommandExecutionError("The given path is involved in a symlink loop.") paths_seen.add(path) cur_depth += 1 if cur_depth > max_depth: - raise CommandExecutionError('Too many levels of symbolic links.') + raise CommandExecutionError("Too many levels of symbolic links.") return path def gid_to_group(gid): - ''' + """ Convert the group id to the group name on this system Under Windows, because groups are just another ACL entity, this function @@ -259,17 +337,20 @@ def gid_to_group(gid): .. code-block:: bash salt '*' file.gid_to_group S-1-5-21-626487655-2533044672-482107328-1010 - ''' - func_name = '{0}.gid_to_group'.format(__virtualname__) - if __opts__.get('fun', '') == func_name: - log.info('The function %s should not be used on Windows systems; ' - 'see function docs for details.', func_name) + """ + func_name = "{0}.gid_to_group".format(__virtualname__) + if __opts__.get("fun", "") == func_name: + log.info( + "The function %s should not be used on Windows systems; " + "see function docs for details.", + func_name, + ) return uid_to_user(gid) def group_to_gid(group): - ''' + """ Convert the group to the gid on this system Under Windows, because groups are just another ACL entity, this function @@ -291,20 +372,23 @@ def group_to_gid(group): .. code-block:: bash salt '*' file.group_to_gid administrators - ''' - func_name = '{0}.group_to_gid'.format(__virtualname__) - if __opts__.get('fun', '') == func_name: - log.info('The function %s should not be used on Windows systems; ' - 'see function docs for details.', func_name) + """ + func_name = "{0}.group_to_gid".format(__virtualname__) + if __opts__.get("fun", "") == func_name: + log.info( + "The function %s should not be used on Windows systems; " + "see function docs for details.", + func_name, + ) if group is None: - return '' + return "" return salt.utils.win_dacl.get_sid_string(group) def get_pgid(path, follow_symlinks=True): - ''' + """ Return the id of the primary group that owns a given file (Windows only) This function will return the rarely used primary group of a file. This @@ -329,9 +413,9 @@ def get_pgid(path, follow_symlinks=True): .. code-block:: bash salt '*' file.get_pgid c:\\temp\\test.txt - ''' + """ if not os.path.exists(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) # Under Windows, if the path is a symlink, the user that owns the symlink is # returned, not the user that owns the file/directory the symlink is @@ -346,7 +430,7 @@ def get_pgid(path, follow_symlinks=True): def get_pgroup(path, follow_symlinks=True): - ''' + """ Return the name of the primary group that owns a given file (Windows only) This function will return the rarely used primary group of a file. This @@ -376,12 +460,12 @@ def get_pgroup(path, follow_symlinks=True): .. code-block:: bash salt '*' file.get_pgroup c:\\temp\\test.txt - ''' + """ return uid_to_user(get_pgid(path, follow_symlinks)) def get_gid(path, follow_symlinks=True): - ''' + """ Return the id of the group that owns a given file Under Windows, this will return the uid of the file. @@ -414,18 +498,21 @@ def get_gid(path, follow_symlinks=True): .. code-block:: bash salt '*' file.get_gid c:\\temp\\test.txt - ''' - func_name = '{0}.get_gid'.format(__virtualname__) - if __opts__.get('fun', '') == func_name: - log.info('The function %s should not be used on Windows systems; ' - 'see function docs for details. The value returned is the ' - 'uid.', func_name) + """ + func_name = "{0}.get_gid".format(__virtualname__) + if __opts__.get("fun", "") == func_name: + log.info( + "The function %s should not be used on Windows systems; " + "see function docs for details. The value returned is the " + "uid.", + func_name, + ) return get_uid(path, follow_symlinks) def get_group(path, follow_symlinks=True): - ''' + """ Return the group that owns a given file Under Windows, this will return the user (owner) of the file. @@ -458,18 +545,21 @@ def get_group(path, follow_symlinks=True): .. code-block:: bash salt '*' file.get_group c:\\temp\\test.txt - ''' - func_name = '{0}.get_group'.format(__virtualname__) - if __opts__.get('fun', '') == func_name: - log.info('The function %s should not be used on Windows systems; ' - 'see function docs for details. The value returned is the ' - 'user (owner).', func_name) + """ + func_name = "{0}.get_group".format(__virtualname__) + if __opts__.get("fun", "") == func_name: + log.info( + "The function %s should not be used on Windows systems; " + "see function docs for details. The value returned is the " + "user (owner).", + func_name, + ) return get_user(path, follow_symlinks) def uid_to_user(uid): - ''' + """ Convert a uid to a user name Args: @@ -483,15 +573,15 @@ def uid_to_user(uid): .. code-block:: bash salt '*' file.uid_to_user S-1-5-21-626487655-2533044672-482107328-1010 - ''' - if uid is None or uid == '': - return '' + """ + if uid is None or uid == "": + return "" return salt.utils.win_dacl.get_name(uid) def user_to_uid(user): - ''' + """ Convert user name to a uid Args: @@ -505,7 +595,7 @@ def user_to_uid(user): .. code-block:: bash salt '*' file.user_to_uid myusername - ''' + """ if user is None: user = salt.utils.user.get_user() @@ -513,7 +603,7 @@ def user_to_uid(user): def get_uid(path, follow_symlinks=True): - ''' + """ Return the id of the user that owns a given file Symlinks are followed by default to mimic Unix behavior. Specify @@ -536,9 +626,9 @@ def get_uid(path, follow_symlinks=True): salt '*' file.get_uid c:\\temp\\test.txt salt '*' file.get_uid c:\\temp\\test.txt follow_symlinks=False - ''' + """ if not os.path.exists(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) # Under Windows, if the path is a symlink, the user that owns the symlink is # returned, not the user that owns the file/directory the symlink is @@ -553,7 +643,7 @@ def get_uid(path, follow_symlinks=True): def get_user(path, follow_symlinks=True): - ''' + """ Return the user that owns a given file Symlinks are followed by default to mimic Unix behavior. Specify @@ -576,9 +666,9 @@ def get_user(path, follow_symlinks=True): salt '*' file.get_user c:\\temp\\test.txt salt '*' file.get_user c:\\temp\\test.txt follow_symlinks=False - ''' + """ if not os.path.exists(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) # Under Windows, if the path is a symlink, the user that owns the symlink is # returned, not the user that owns the file/directory the symlink is @@ -592,7 +682,7 @@ def get_user(path, follow_symlinks=True): def get_mode(path): - ''' + """ Return the mode of a file Right now we're just returning None because Windows' doesn't have a mode @@ -609,21 +699,24 @@ def get_mode(path): .. code-block:: bash salt '*' file.get_mode /etc/passwd - ''' + """ if not os.path.exists(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) - func_name = '{0}.get_mode'.format(__virtualname__) - if __opts__.get('fun', '') == func_name: - log.info('The function %s should not be used on Windows systems; ' - 'see function docs for details. The value returned is ' - 'always None.', func_name) + func_name = "{0}.get_mode".format(__virtualname__) + if __opts__.get("fun", "") == func_name: + log.info( + "The function %s should not be used on Windows systems; " + "see function docs for details. The value returned is " + "always None.", + func_name, + ) return None def lchown(path, user, group=None, pgroup=None): - ''' + """ Chown a file, pass the file the desired user and group without following any symlinks. @@ -657,22 +750,23 @@ def lchown(path, user, group=None, pgroup=None): salt '*' file.lchown c:\\temp\\test.txt myusername salt '*' file.lchown c:\\temp\\test.txt myusername pgroup=Administrators salt '*' file.lchown c:\\temp\\test.txt myusername "pgroup='None'" - ''' + """ if group: - func_name = '{0}.lchown'.format(__virtualname__) - if __opts__.get('fun', '') == func_name: - log.info('The group parameter has no effect when using %s on ' - 'Windows systems; see function docs for details.', - func_name) - log.debug('win_file.py %s Ignoring the group parameter for %s', - func_name, path) + func_name = "{0}.lchown".format(__virtualname__) + if __opts__.get("fun", "") == func_name: + log.info( + "The group parameter has no effect when using %s on " + "Windows systems; see function docs for details.", + func_name, + ) + log.debug("win_file.py %s Ignoring the group parameter for %s", func_name, path) group = None return chown(path, user, group, pgroup, follow_symlinks=False) def chown(path, user, group=None, pgroup=None, follow_symlinks=True): - ''' + """ Chown a file, pass the file the desired user and group Under Windows, the group parameter will be ignored. @@ -704,22 +798,23 @@ def chown(path, user, group=None, pgroup=None, follow_symlinks=True): salt '*' file.chown c:\\temp\\test.txt myusername salt '*' file.chown c:\\temp\\test.txt myusername pgroup=Administrators salt '*' file.chown c:\\temp\\test.txt myusername "pgroup='None'" - ''' + """ # the group parameter is not used; only provided for API compatibility if group is not None: - func_name = '{0}.chown'.format(__virtualname__) - if __opts__.get('fun', '') == func_name: - log.info('The group parameter has no effect when using %s on ' - 'Windows systems; see function docs for details.', - func_name) - log.debug('win_file.py %s Ignoring the group parameter for %s', - func_name, path) + func_name = "{0}.chown".format(__virtualname__) + if __opts__.get("fun", "") == func_name: + log.info( + "The group parameter has no effect when using %s on " + "Windows systems; see function docs for details.", + func_name, + ) + log.debug("win_file.py %s Ignoring the group parameter for %s", func_name, path) if follow_symlinks and sys.getwindowsversion().major >= 6: path = _resolve_symlink(path) if not os.path.exists(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) salt.utils.win_dacl.set_owner(path, user) if pgroup: @@ -729,7 +824,7 @@ def chown(path, user, group=None, pgroup=None, follow_symlinks=True): def chpgrp(path, group): - ''' + """ Change the group of a file Under Windows, this will set the rarely used primary group of a file. @@ -752,12 +847,12 @@ def chpgrp(path, group): salt '*' file.chpgrp c:\\temp\\test.txt Administrators salt '*' file.chpgrp c:\\temp\\test.txt "'None'" - ''' + """ return salt.utils.win_dacl.set_primary_group(path, group) def chgrp(path, group): - ''' + """ Change the group of a file Under Windows, this will do nothing. @@ -789,18 +884,21 @@ def chgrp(path, group): .. code-block:: bash salt '*' file.chpgrp c:\\temp\\test.txt administrators - ''' - func_name = '{0}.chgrp'.format(__virtualname__) - if __opts__.get('fun', '') == func_name: - log.info('The function %s should not be used on Windows systems; see ' - 'function docs for details.', func_name) - log.debug('win_file.py %s Doing nothing for %s', func_name, path) + """ + func_name = "{0}.chgrp".format(__virtualname__) + if __opts__.get("fun", "") == func_name: + log.info( + "The function %s should not be used on Windows systems; see " + "function docs for details.", + func_name, + ) + log.debug("win_file.py %s Doing nothing for %s", func_name, path) return None -def stats(path, hash_type='sha256', follow_symlinks=True): - ''' +def stats(path, hash_type="sha256", follow_symlinks=True): + """ Return a dict containing the stats about a given file Under Windows, `gid` will equal `uid` and `group` will equal `user`. @@ -829,11 +927,11 @@ def stats(path, hash_type='sha256', follow_symlinks=True): .. code-block:: bash salt '*' file.stats /etc/passwd - ''' + """ # This is to mirror the behavior of file.py. `check_file_meta` expects an # empty dictionary when the file does not exist if not os.path.exists(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) if follow_symlinks and sys.getwindowsversion().major >= 6: path = _resolve_symlink(path) @@ -841,43 +939,43 @@ def stats(path, hash_type='sha256', follow_symlinks=True): pstat = os.stat(path) ret = {} - ret['inode'] = pstat.st_ino + ret["inode"] = pstat.st_ino # don't need to resolve symlinks again because we've already done that - ret['uid'] = get_uid(path, follow_symlinks=False) + ret["uid"] = get_uid(path, follow_symlinks=False) # maintain the illusion that group is the same as user as states need this - ret['gid'] = ret['uid'] - ret['user'] = uid_to_user(ret['uid']) - ret['group'] = ret['user'] - ret['pgid'] = get_pgid(path, follow_symlinks) - ret['pgroup'] = gid_to_group(ret['pgid']) - ret['atime'] = pstat.st_atime - ret['mtime'] = pstat.st_mtime - ret['ctime'] = pstat.st_ctime - ret['size'] = pstat.st_size - ret['mode'] = salt.utils.files.normalize_mode(oct(stat.S_IMODE(pstat.st_mode))) + ret["gid"] = ret["uid"] + ret["user"] = uid_to_user(ret["uid"]) + ret["group"] = ret["user"] + ret["pgid"] = get_pgid(path, follow_symlinks) + ret["pgroup"] = gid_to_group(ret["pgid"]) + ret["atime"] = pstat.st_atime + ret["mtime"] = pstat.st_mtime + ret["ctime"] = pstat.st_ctime + ret["size"] = pstat.st_size + ret["mode"] = salt.utils.files.normalize_mode(oct(stat.S_IMODE(pstat.st_mode))) if hash_type: - ret['sum'] = get_sum(path, hash_type) - ret['type'] = 'file' + ret["sum"] = get_sum(path, hash_type) + ret["type"] = "file" if stat.S_ISDIR(pstat.st_mode): - ret['type'] = 'dir' + ret["type"] = "dir" if stat.S_ISCHR(pstat.st_mode): - ret['type'] = 'char' + ret["type"] = "char" if stat.S_ISBLK(pstat.st_mode): - ret['type'] = 'block' + ret["type"] = "block" if stat.S_ISREG(pstat.st_mode): - ret['type'] = 'file' + ret["type"] = "file" if stat.S_ISLNK(pstat.st_mode): - ret['type'] = 'link' + ret["type"] = "link" if stat.S_ISFIFO(pstat.st_mode): - ret['type'] = 'pipe' + ret["type"] = "pipe" if stat.S_ISSOCK(pstat.st_mode): - ret['type'] = 'socket' - ret['target'] = os.path.realpath(path) + ret["type"] = "socket" + ret["target"] = os.path.realpath(path) return ret def get_attributes(path): - ''' + """ Return a dictionary object with the Windows file attributes for a file. @@ -892,9 +990,9 @@ def get_attributes(path): .. code-block:: bash salt '*' file.get_attributes c:\\temp\\a.txt - ''' + """ if not os.path.exists(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) # set up dictionary for attribute values attributes = {} @@ -903,45 +1001,53 @@ def get_attributes(path): intAttributes = win32file.GetFileAttributes(path) # Assign individual attributes - attributes['archive'] = (intAttributes & 32) == 32 - attributes['reparsePoint'] = (intAttributes & 1024) == 1024 - attributes['compressed'] = (intAttributes & 2048) == 2048 - attributes['directory'] = (intAttributes & 16) == 16 - attributes['encrypted'] = (intAttributes & 16384) == 16384 - attributes['hidden'] = (intAttributes & 2) == 2 - attributes['normal'] = (intAttributes & 128) == 128 - attributes['notIndexed'] = (intAttributes & 8192) == 8192 - attributes['offline'] = (intAttributes & 4096) == 4096 - attributes['readonly'] = (intAttributes & 1) == 1 - attributes['system'] = (intAttributes & 4) == 4 - attributes['temporary'] = (intAttributes & 256) == 256 + attributes["archive"] = (intAttributes & 32) == 32 + attributes["reparsePoint"] = (intAttributes & 1024) == 1024 + attributes["compressed"] = (intAttributes & 2048) == 2048 + attributes["directory"] = (intAttributes & 16) == 16 + attributes["encrypted"] = (intAttributes & 16384) == 16384 + attributes["hidden"] = (intAttributes & 2) == 2 + attributes["normal"] = (intAttributes & 128) == 128 + attributes["notIndexed"] = (intAttributes & 8192) == 8192 + attributes["offline"] = (intAttributes & 4096) == 4096 + attributes["readonly"] = (intAttributes & 1) == 1 + attributes["system"] = (intAttributes & 4) == 4 + attributes["temporary"] = (intAttributes & 256) == 256 # check if it's a Mounted Volume - attributes['mountedVolume'] = False - if attributes['reparsePoint'] is True and attributes['directory'] is True: + attributes["mountedVolume"] = False + if attributes["reparsePoint"] is True and attributes["directory"] is True: fileIterator = win32file.FindFilesIterator(path) findDataTuple = next(fileIterator) if findDataTuple[6] == 0xA0000003: - attributes['mountedVolume'] = True + attributes["mountedVolume"] = True # check if it's a soft (symbolic) link # Note: os.path.islink() does not work in # Python 2.7 for the Windows NTFS file system. # The following code does, however, work (tested in Windows 8) - attributes['symbolicLink'] = False - if attributes['reparsePoint'] is True: + attributes["symbolicLink"] = False + if attributes["reparsePoint"] is True: fileIterator = win32file.FindFilesIterator(path) findDataTuple = next(fileIterator) if findDataTuple[6] == 0xA000000C: - attributes['symbolicLink'] = True + attributes["symbolicLink"] = True return attributes -def set_attributes(path, archive=None, hidden=None, normal=None, - notIndexed=None, readonly=None, system=None, temporary=None): - ''' +def set_attributes( + path, + archive=None, + hidden=None, + normal=None, + notIndexed=None, + readonly=None, + system=None, + temporary=None, +): + """ Set file attributes for a file. Note that the normal attribute means that all others are false. So setting it will clear all others. @@ -966,14 +1072,15 @@ def set_attributes(path, archive=None, hidden=None, normal=None, salt '*' file.set_attributes c:\\temp\\a.txt normal=True salt '*' file.set_attributes c:\\temp\\a.txt readonly=True hidden=True - ''' + """ if not os.path.exists(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) if normal: if archive or hidden or notIndexed or readonly or system or temporary: raise CommandExecutionError( - 'Normal attribute may not be used with any other attributes') + "Normal attribute may not be used with any other attributes" + ) ret = win32file.SetFileAttributes(path, 128) return True if ret is None else False @@ -1017,7 +1124,7 @@ def set_attributes(path, archive=None, hidden=None, normal=None, def set_mode(path, mode): - ''' + """ Set the mode of a file This just calls get_mode, which returns None because we don't use mode on @@ -1035,18 +1142,21 @@ def set_mode(path, mode): .. code-block:: bash salt '*' file.set_mode /etc/passwd 0644 - ''' - func_name = '{0}.set_mode'.format(__virtualname__) - if __opts__.get('fun', '') == func_name: - log.info('The function %s should not be used on Windows systems; ' - 'see function docs for details. The value returned is ' - 'always None. Use set_perms instead.', func_name) + """ + func_name = "{0}.set_mode".format(__virtualname__) + if __opts__.get("fun", "") == func_name: + log.info( + "The function %s should not be used on Windows systems; " + "see function docs for details. The value returned is " + "always None. Use set_perms instead.", + func_name, + ) return get_mode(path) def remove(path, force=False): - ''' + """ Remove the named file or directory Args: @@ -1061,7 +1171,7 @@ def remove(path, force=False): .. code-block:: bash salt '*' file.remove C:\\Temp - ''' + """ # This must be a recursive function in windows to properly deal with # Symlinks. The shutil.rmtree function will remove the contents of # the Symlink source in windows. @@ -1069,11 +1179,11 @@ def remove(path, force=False): path = os.path.expanduser(path) if not os.path.isabs(path): - raise SaltInvocationError('File path must be absolute: {0}'.format(path)) + raise SaltInvocationError("File path must be absolute: {0}".format(path)) # Does the file/folder exists if not os.path.exists(path) and not is_link(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) # Remove ReadOnly Attribute if force: @@ -1090,8 +1200,8 @@ def remove(path, force=False): os.rmdir(path) else: for name in os.listdir(path): - item = '{0}\\{1}'.format(path, name) - # If it's a normal directory, recurse to remove its contents + item = "{0}\\{1}".format(path, name) + # If its a normal directory, recurse to remove it's contents remove(item, force) # rmdir will work now because the directory is empty @@ -1100,15 +1210,13 @@ def remove(path, force=False): if force: # Reset attributes to the original if delete fails. win32api.SetFileAttributes(path, file_attributes) - raise CommandExecutionError( - 'Could not remove \'{0}\': {1}'.format(path, exc) - ) + raise CommandExecutionError("Could not remove '{0}': {1}".format(path, exc)) return True def symlink(src, link): - ''' + """ Create a symbolic link to a file This is only supported with Windows Vista or later and must be executed by @@ -1130,17 +1238,19 @@ def symlink(src, link): .. code-block:: bash salt '*' file.symlink /path/to/file /path/to/link - ''' + """ # When Python 3.2 or later becomes the minimum version, this function can be # replaced with the built-in os.symlink function, which supports Windows. if sys.getwindowsversion().major < 6: - raise SaltInvocationError('Symlinks are only supported on Windows Vista or later.') + raise SaltInvocationError( + "Symlinks are only supported on Windows Vista or later." + ) if not os.path.exists(src): - raise SaltInvocationError('The given source path does not exist.') + raise SaltInvocationError("The given source path does not exist.") if not os.path.isabs(src): - raise SaltInvocationError('File path must be absolute.') + raise SaltInvocationError("File path must be absolute.") # ensure paths are using the right slashes src = os.path.normpath(src) @@ -1149,12 +1259,8 @@ def symlink(src, link): is_dir = os.path.isdir(src) # Elevate the token from the current process - desired_access = ( - win32security.TOKEN_QUERY | - win32security.TOKEN_ADJUST_PRIVILEGES - ) - th = win32security.OpenProcessToken(win32api.GetCurrentProcess(), - desired_access) + desired_access = win32security.TOKEN_QUERY | win32security.TOKEN_ADJUST_PRIVILEGES + th = win32security.OpenProcessToken(win32api.GetCurrentProcess(), desired_access) salt.platform.win.elevate_token(th) try: @@ -1162,16 +1268,14 @@ def symlink(src, link): return True except win32file.error as exc: raise CommandExecutionError( - 'Could not create \'{0}\' - [{1}] {2}'.format( - link, - exc.winerror, - exc.strerror + "Could not create '{0}' - [{1}] {2}".format( + link, exc.winerror, exc.strerror ) ) def is_link(path): - ''' + """ Check if the path is a symlink This is only supported on Windows Vista or later. @@ -1191,9 +1295,11 @@ def is_link(path): .. code-block:: bash salt '*' file.is_link /path/to/link - ''' + """ if sys.getwindowsversion().major < 6: - raise SaltInvocationError('Symlinks are only supported on Windows Vista or later.') + raise SaltInvocationError( + "Symlinks are only supported on Windows Vista or later." + ) try: return salt.utils.path.islink(path) @@ -1202,7 +1308,7 @@ def is_link(path): def readlink(path): - ''' + """ Return the path that a symlink points to This is only supported on Windows Vista or later. @@ -1222,27 +1328,26 @@ def readlink(path): .. code-block:: bash salt '*' file.readlink /path/to/link - ''' + """ if sys.getwindowsversion().major < 6: - raise SaltInvocationError('Symlinks are only supported on Windows Vista or later.') + raise SaltInvocationError( + "Symlinks are only supported on Windows Vista or later." + ) try: return salt.utils.path.readlink(path) except OSError as exc: if exc.errno == errno.EINVAL: - raise CommandExecutionError('{0} is not a symbolic link'.format(path)) + raise CommandExecutionError("{0} is not a symbolic link".format(path)) raise CommandExecutionError(exc.__str__()) except Exception as exc: # pylint: disable=broad-except raise CommandExecutionError(exc) -def mkdir(path, - owner=None, - grant_perms=None, - deny_perms=None, - inheritance=True, - reset=False): - ''' +def mkdir( + path, owner=None, grant_perms=None, deny_perms=None, inheritance=True, reset=False +): + """ Ensure that the directory is available and permissions are set. Args: @@ -1308,11 +1413,11 @@ def mkdir(path, # Specify advanced attributes with a list salt '*' file.mkdir C:\\Temp\\ Administrators "{'jsnuffy': {'perms': ['read_attributes', 'read_ea'], 'applies_to': 'this_folder_only'}}" - ''' + """ # Make sure the drive is valid drive = os.path.splitdrive(path)[0] if not os.path.isdir(drive): - raise CommandExecutionError('Drive {0} is not mapped'.format(drive)) + raise CommandExecutionError("Drive {0} is not mapped".format(drive)) path = os.path.expanduser(path) path = os.path.expandvars(path) @@ -1333,7 +1438,8 @@ def mkdir(path, grant_perms=grant_perms, deny_perms=deny_perms, inheritance=inheritance, - reset=reset) + reset=reset, + ) except WindowsError as exc: raise CommandExecutionError(exc) @@ -1341,13 +1447,10 @@ def mkdir(path, return True -def makedirs_(path, - owner=None, - grant_perms=None, - deny_perms=None, - inheritance=True, - reset=False): - ''' +def makedirs_( + path, owner=None, grant_perms=None, deny_perms=None, inheritance=True, reset=False +): + """ Ensure that the parent directory containing this path is available. Args: @@ -1421,7 +1524,7 @@ def makedirs_(path, # Specify advanced attributes with a list salt '*' file.makedirs C:\\Temp\\ Administrators "{'jsnuffy': {'perms': ['read_attributes', 'read_ea'], 'applies_to': 'this_folder_only'}}" - ''' + """ path = os.path.expanduser(path) # walk up the directory structure until we find the first existing @@ -1430,14 +1533,12 @@ def makedirs_(path, if os.path.isdir(dirname): # There's nothing for us to do - msg = 'Directory \'{0}\' already exists'.format(dirname) + msg = "Directory '{0}' already exists".format(dirname) log.debug(msg) return msg if os.path.exists(dirname): - msg = 'The path \'{0}\' already exists and is not a directory'.format( - dirname - ) + msg = "The path '{0}' already exists and is not a directory".format(dirname) log.debug(msg) return msg @@ -1452,33 +1553,31 @@ def makedirs_(path, if current_dirname == dirname: raise SaltInvocationError( - 'Recursive creation for path \'{0}\' would result in an ' - 'infinite loop. Please use an absolute path.'.format(dirname) + "Recursive creation for path '{0}' would result in an " + "infinite loop. Please use an absolute path.".format(dirname) ) # create parent directories from the topmost to the most deeply nested one directories_to_create.reverse() for directory_to_create in directories_to_create: # all directories have the user, group and mode set!! - log.debug('Creating directory: %s', directory_to_create) + log.debug("Creating directory: %s", directory_to_create) mkdir( path=directory_to_create, owner=owner, grant_perms=grant_perms, deny_perms=deny_perms, inheritance=inheritance, - reset=reset) + reset=reset, + ) return True -def makedirs_perms(path, - owner=None, - grant_perms=None, - deny_perms=None, - inheritance=True, - reset=True): - ''' +def makedirs_perms( + path, owner=None, grant_perms=None, deny_perms=None, inheritance=True, reset=True +): + """ Set owner and permissions for each directory created. Args: @@ -1540,7 +1639,7 @@ def makedirs_perms(path, # Specify advanced attributes with a list salt '*' file.makedirs_perms C:\\Temp\\ Administrators "{'jsnuffy': {'perms': ['read_attributes', 'read_ea'], 'applies_to': 'this_folder_files'}}" - ''' + """ # Expand any environment variables path = os.path.expanduser(path) path = os.path.expandvars(path) @@ -1565,7 +1664,8 @@ def makedirs_perms(path, grant_perms=grant_perms, deny_perms=deny_perms, inheritance=True, - reset=False) + reset=False, + ) except OSError as exc: # be happy if someone already created the path if exc.errno != errno.EEXIST: @@ -1580,19 +1680,22 @@ def makedirs_perms(path, grant_perms=grant_perms, deny_perms=deny_perms, inheritance=inheritance, - reset=reset) + reset=reset, + ) return True -def check_perms(path, - ret=None, - owner=None, - grant_perms=None, - deny_perms=None, - inheritance=True, - reset=False): - ''' +def check_perms( + path, + ret=None, + owner=None, + grant_perms=None, + deny_perms=None, + inheritance=True, + reset=False, +): + """ Check owner and permissions for the passed directory. This function checks the permissions and sets them, returning the changes made. Used by the file state to populate the return dict @@ -1642,28 +1745,26 @@ def check_perms(path, # Specify advanced attributes with a list salt '*' file.check_perms C:\\Temp\\ {} Administrators "{'jsnuffy': {'perms': ['read_attributes', 'read_ea'], 'applies_to': 'files_only'}}" - ''' + """ if not os.path.exists(path): - raise CommandExecutionError('Path not found: {0}'.format(path)) + raise CommandExecutionError("Path not found: {0}".format(path)) path = os.path.expanduser(path) - return __utils__['dacl.check_perms'](obj_name=path, - obj_type='file', - ret=ret, - owner=owner, - grant_perms=grant_perms, - deny_perms=deny_perms, - inheritance=inheritance, - reset=reset) + return __utils__["dacl.check_perms"]( + obj_name=path, + obj_type="file", + ret=ret, + owner=owner, + grant_perms=grant_perms, + deny_perms=deny_perms, + inheritance=inheritance, + reset=reset, + ) -def set_perms(path, - grant_perms=None, - deny_perms=None, - inheritance=True, - reset=False): - ''' +def set_perms(path, grant_perms=None, deny_perms=None, inheritance=True, reset=False): + """ Set permissions for the given path Args: @@ -1735,10 +1836,12 @@ def set_perms(path, # Specify advanced attributes with a list salt '*' file.set_perms C:\\Temp\\ "{'jsnuffy': {'perms': ['read_attributes', 'read_ea'], 'applies_to': 'this_folder_only'}}" - ''' - return __utils__['dacl.set_perms'](obj_name=path, - obj_type='file', - grant_perms=grant_perms, - deny_perms=deny_perms, - inheritance=inheritance, - reset=reset) + """ + return __utils__["dacl.set_perms"]( + obj_name=path, + obj_type="file", + grant_perms=grant_perms, + deny_perms=deny_perms, + inheritance=inheritance, + reset=reset, + ) diff --git a/salt/modules/win_firewall.py b/salt/modules/win_firewall.py index 2a721df2325..a35f4465589 100644 --- a/salt/modules/win_firewall.py +++ b/salt/modules/win_firewall.py @@ -1,25 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" Module for configuring Windows Firewall using ``netsh`` -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import re # Import Salt libs import salt.utils.platform -from salt.exceptions import CommandExecutionError import salt.utils.win_lgpo_netsh +from salt.exceptions import CommandExecutionError # Define the module's virtual name -__virtualname__ = 'firewall' +__virtualname__ = "firewall" def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if not salt.utils.platform.is_windows(): return False, "Module win_firewall: module only available on Windows" @@ -27,7 +27,7 @@ def __virtual__(): def get_config(): - ''' + """ Get the status of all the firewall profiles Returns: @@ -41,32 +41,32 @@ def get_config(): .. code-block:: bash salt '*' firewall.get_config - ''' + """ profiles = {} curr = None - cmd = ['netsh', 'advfirewall', 'show', 'allprofiles'] - ret = __salt__['cmd.run_all'](cmd, python_shell=False, ignore_retcode=True) - if ret['retcode'] != 0: - raise CommandExecutionError(ret['stdout']) + cmd = ["netsh", "advfirewall", "show", "allprofiles"] + ret = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=True) + if ret["retcode"] != 0: + raise CommandExecutionError(ret["stdout"]) # There may be some problems with this depending on how `netsh` is localized # It's looking for lines that contain `Profile Settings` or start with # `State` which may be different in different localizations - for line in ret['stdout'].splitlines(): + for line in ret["stdout"].splitlines(): if not curr: - tmp = re.search('(.*) Profile Settings:', line) + tmp = re.search("(.*) Profile Settings:", line) if tmp: curr = tmp.group(1) - elif line.startswith('State'): - profiles[curr] = line.split()[1] == 'ON' + elif line.startswith("State"): + profiles[curr] = line.split()[1] == "ON" curr = None return profiles -def disable(profile='allprofiles'): - ''' +def disable(profile="allprofiles"): + """ Disable firewall profile Args: @@ -89,17 +89,17 @@ def disable(profile='allprofiles'): .. code-block:: bash salt '*' firewall.disable - ''' - cmd = ['netsh', 'advfirewall', 'set', profile, 'state', 'off'] - ret = __salt__['cmd.run_all'](cmd, python_shell=False, ignore_retcode=True) - if ret['retcode'] != 0: - raise CommandExecutionError(ret['stdout']) + """ + cmd = ["netsh", "advfirewall", "set", profile, "state", "off"] + ret = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=True) + if ret["retcode"] != 0: + raise CommandExecutionError(ret["stdout"]) return True -def enable(profile='allprofiles'): - ''' +def enable(profile="allprofiles"): + """ .. versionadded:: 2015.5.0 Enable firewall profile @@ -124,17 +124,17 @@ def enable(profile='allprofiles'): .. code-block:: bash salt '*' firewall.enable - ''' - cmd = ['netsh', 'advfirewall', 'set', profile, 'state', 'on'] - ret = __salt__['cmd.run_all'](cmd, python_shell=False, ignore_retcode=True) - if ret['retcode'] != 0: - raise CommandExecutionError(ret['stdout']) + """ + cmd = ["netsh", "advfirewall", "set", profile, "state", "on"] + ret = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=True) + if ret["retcode"] != 0: + raise CommandExecutionError(ret["stdout"]) return True -def get_rule(name='all'): - ''' +def get_rule(name="all"): + """ .. versionadded:: 2015.5.0 Display all matching rules as specified by name @@ -154,19 +154,17 @@ def get_rule(name='all'): .. code-block:: bash salt '*' firewall.get_rule 'MyAppPort' - ''' - cmd = ['netsh', 'advfirewall', 'firewall', 'show', 'rule', - 'name={0}'.format(name)] - ret = __salt__['cmd.run_all'](cmd, python_shell=False, ignore_retcode=True) - if ret['retcode'] != 0: - raise CommandExecutionError(ret['stdout']) + """ + cmd = ["netsh", "advfirewall", "firewall", "show", "rule", "name={0}".format(name)] + ret = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=True) + if ret["retcode"] != 0: + raise CommandExecutionError(ret["stdout"]) - return {name: ret['stdout']} + return {name: ret["stdout"]} -def add_rule(name, localport, protocol='tcp', action='allow', dir='in', - remoteip='any'): - ''' +def add_rule(name, localport, protocol="tcp", action="allow", dir="in", remoteip="any"): + """ .. versionadded:: 2015.5.0 Add a new inbound or outbound rule to the firewall policy @@ -227,31 +225,32 @@ def add_rule(name, localport, protocol='tcp', action='allow', dir='in', salt '*' firewall.add_rule 'test' '8080' 'tcp' salt '*' firewall.add_rule 'test' '1' 'icmpv4' salt '*' firewall.add_rule 'test_remote_ip' '8000' 'tcp' 'allow' 'in' '192.168.0.1' - ''' - cmd = ['netsh', 'advfirewall', 'firewall', 'add', 'rule', - 'name={0}'.format(name), - 'protocol={0}'.format(protocol), - 'dir={0}'.format(dir), - 'action={0}'.format(action), - 'remoteip={0}'.format(remoteip)] + """ + cmd = [ + "netsh", + "advfirewall", + "firewall", + "add", + "rule", + "name={0}".format(name), + "protocol={0}".format(protocol), + "dir={0}".format(dir), + "action={0}".format(action), + "remoteip={0}".format(remoteip), + ] - if protocol is None \ - or ('icmpv4' not in protocol and 'icmpv6' not in protocol): - cmd.append('localport={0}'.format(localport)) + if protocol is None or ("icmpv4" not in protocol and "icmpv6" not in protocol): + cmd.append("localport={0}".format(localport)) - ret = __salt__['cmd.run_all'](cmd, python_shell=False, ignore_retcode=True) - if ret['retcode'] != 0: - raise CommandExecutionError(ret['stdout']) + ret = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=True) + if ret["retcode"] != 0: + raise CommandExecutionError(ret["stdout"]) return True -def delete_rule(name=None, - localport=None, - protocol=None, - dir=None, - remoteip=None): - ''' +def delete_rule(name=None, localport=None, protocol=None, dir=None, remoteip=None): + """ .. versionadded:: 2015.8.0 Delete an existing firewall rule identified by name and optionally by ports, @@ -294,33 +293,32 @@ def delete_rule(name=None, # Delete a rule called 'allow80': salt '*' firewall.delete_rule allow80 - ''' - cmd = ['netsh', 'advfirewall', 'firewall', 'delete', 'rule'] + """ + cmd = ["netsh", "advfirewall", "firewall", "delete", "rule"] if name: - cmd.append('name={0}'.format(name)) + cmd.append("name={0}".format(name)) if protocol: - cmd.append('protocol={0}'.format(protocol)) + cmd.append("protocol={0}".format(protocol)) if dir: - cmd.append('dir={0}'.format(dir)) + cmd.append("dir={0}".format(dir)) if remoteip: - cmd.append('remoteip={0}'.format(remoteip)) + cmd.append("remoteip={0}".format(remoteip)) - if protocol is None \ - or ('icmpv4' not in protocol and 'icmpv6' not in protocol): + if protocol is None or ("icmpv4" not in protocol and "icmpv6" not in protocol): if localport: if not protocol: - cmd.append('protocol=tcp') - cmd.append('localport={0}'.format(localport)) + cmd.append("protocol=tcp") + cmd.append("localport={0}".format(localport)) - ret = __salt__['cmd.run_all'](cmd, python_shell=False, ignore_retcode=True) - if ret['retcode'] != 0: - raise CommandExecutionError(ret['stdout']) + ret = __salt__["cmd.run_all"](cmd, python_shell=False, ignore_retcode=True) + if ret["retcode"] != 0: + raise CommandExecutionError(ret["stdout"]) return True def rule_exists(name): - ''' + """ .. versionadded:: 2016.11.6 Checks if a firewall rule exists in the firewall policy @@ -337,7 +335,7 @@ def rule_exists(name): # Is there a rule named RemoteDesktop salt '*' firewall.rule_exists RemoteDesktop - ''' + """ try: get_rule(name) return True @@ -345,8 +343,8 @@ def rule_exists(name): return False -def get_settings(profile, section, store='local'): - ''' +def get_settings(profile, section, store="local"): + """ Get the firewall property from the specified profile in the specified store as returned by ``netsh advfirewall``. @@ -398,14 +396,14 @@ def get_settings(profile, section, store='local'): # Get the inbound/outbound firewall settings for connections on the # domain profile as defined by local group policy salt * win_firewall.get_settings domain firewallpolicy lgpo - ''' - return salt.utils.win_lgpo_netsh.get_settings(profile=profile, - section=section, - store=store) + """ + return salt.utils.win_lgpo_netsh.get_settings( + profile=profile, section=section, store=store + ) -def get_all_settings(domain, store='local'): - ''' +def get_all_settings(domain, store="local"): + """ Gets all the properties for the specified profile in the specified store .. versionadded:: 2018.3.4 @@ -442,13 +440,12 @@ def get_all_settings(domain, store='local'): # Get all firewall settings for connections on the domain profile as # defined by local group policy salt * win_firewall.get_all_settings domain lgpo - ''' - return salt.utils.win_lgpo_netsh.get_all_settings(profile=domain, - store=store) + """ + return salt.utils.win_lgpo_netsh.get_all_settings(profile=domain, store=store) -def get_all_profiles(store='local'): - ''' +def get_all_profiles(store="local"): + """ Gets all properties for all profiles in the specified store .. versionadded:: 2018.3.4 @@ -479,12 +476,12 @@ def get_all_profiles(store='local'): # policy salt * firewall.get_all_settings lgpo - ''' + """ return salt.utils.win_lgpo_netsh.get_all_profiles(store=store) -def set_firewall_settings(profile, inbound=None, outbound=None, store='local'): - ''' +def set_firewall_settings(profile, inbound=None, outbound=None, store="local"): + """ Set the firewall inbound/outbound settings for the specified profile and store @@ -552,15 +549,14 @@ def set_firewall_settings(profile, inbound=None, outbound=None, store='local'): # Set inbound/outbound settings for the domain profile in the group # policy to block inbound and allow outbound salt * firewall.set_firewall_settings domain='domain' inbound='blockinbound' outbound='allowoutbound' store='lgpo' - ''' - return salt.utils.win_lgpo_netsh.set_firewall_settings(profile=profile, - inbound=inbound, - outbound=outbound, - store=store) + """ + return salt.utils.win_lgpo_netsh.set_firewall_settings( + profile=profile, inbound=inbound, outbound=outbound, store=store + ) -def set_logging_settings(profile, setting, value, store='local'): - r''' +def set_logging_settings(profile, setting, value, store="local"): + r""" Configure logging settings for the Windows firewall. .. versionadded:: 2018.3.4 @@ -646,15 +642,14 @@ def set_logging_settings(profile, setting, value, store='local'): # Set the max file size of the log to 2048 Kb salt * firewall.set_logging_settings domain maxfilesize 2048 - ''' - return salt.utils.win_lgpo_netsh.set_logging_settings(profile=profile, - setting=setting, - value=value, - store=store) + """ + return salt.utils.win_lgpo_netsh.set_logging_settings( + profile=profile, setting=setting, value=value, store=store + ) -def set_settings(profile, setting, value, store='local'): - ''' +def set_settings(profile, setting, value, store="local"): + """ Configure firewall settings. .. versionadded:: 2018.3.4 @@ -713,15 +708,14 @@ def set_settings(profile, setting, value, store='local'): # Allow remote management of Windows Firewall salt * firewall.set_settings domain remotemanagement enable - ''' - return salt.utils.win_lgpo_netsh.set_settings(profile=profile, - setting=setting, - value=value, - store=store) + """ + return salt.utils.win_lgpo_netsh.set_settings( + profile=profile, setting=setting, value=value, store=store + ) -def set_state(profile, state, store='local'): - ''' +def set_state(profile, state, store="local"): + """ Configure the firewall state. .. versionadded:: 2018.3.4 @@ -772,7 +766,7 @@ def set_state(profile, state, store='local'): # Turn the firewall on when the public profile is active and set that in # the local group policy salt * firewall.set_state public on lgpo - ''' - return salt.utils.win_lgpo_netsh.set_state(profile=profile, - state=state, - store=store) + """ + return salt.utils.win_lgpo_netsh.set_state( + profile=profile, state=state, store=store + ) diff --git a/salt/modules/win_groupadd.py b/salt/modules/win_groupadd.py index 47459671f01..e01123ce3a6 100644 --- a/salt/modules/win_groupadd.py +++ b/salt/modules/win_groupadd.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage groups on Windows .. important:: @@ -7,8 +7,9 @@ Manage groups on Windows minion, and it is using a different module (or gives an error similar to *'group.info' is not available*), see :ref:`here <module-provider-override>`. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -20,6 +21,7 @@ try: import win32api import win32com.client import pywintypes + HAS_DEPENDENCIES = True except ImportError: HAS_DEPENDENCIES = False @@ -27,32 +29,32 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'group' +__virtualname__ = "group" def __virtual__(): - ''' + """ Set the group module if the kernel is Windows - ''' + """ if salt.utils.platform.is_windows() and HAS_DEPENDENCIES: return __virtualname__ return (False, "Module win_groupadd: module only works on Windows systems") def _get_computer_object(): - ''' + """ A helper function to get the object for the local machine Returns: object: Returns the computer object for the local machine - ''' + """ with salt.utils.winapi.Com(): - nt = win32com.client.Dispatch('AdsNameSpaces') - return nt.GetObject('', 'WinNT://.,computer') + nt = win32com.client.Dispatch("AdsNameSpaces") + return nt.GetObject("", "WinNT://.,computer") def _get_group_object(name): - ''' + """ A helper function to get a specified group object Args: @@ -61,40 +63,39 @@ def _get_group_object(name): Returns: object: The specified group object - ''' + """ with salt.utils.winapi.Com(): - nt = win32com.client.Dispatch('AdsNameSpaces') - return nt.GetObject('', 'WinNT://./' + name + ',group') + nt = win32com.client.Dispatch("AdsNameSpaces") + return nt.GetObject("", "WinNT://./" + name + ",group") def _get_all_groups(): - ''' + """ A helper function that gets a list of group objects for all groups on the machine Returns: iter: A list of objects for all groups on the machine - ''' + """ with salt.utils.winapi.Com(): - nt = win32com.client.Dispatch('AdsNameSpaces') - results = nt.GetObject('', 'WinNT://.') - results.Filter = ['group'] + nt = win32com.client.Dispatch("AdsNameSpaces") + results = nt.GetObject("", "WinNT://.") + results.Filter = ["group"] return results def _get_username(member): - ''' + """ Resolve the username from the member object returned from a group query Returns: str: The username converted to domain\\username format - ''' - return member.ADSPath.replace('WinNT://', '').replace( - '/', '\\') + """ + return member.ADSPath.replace("WinNT://", "").replace("/", "\\") def add(name, **kwargs): - ''' + """ Add the specified group Args: @@ -110,26 +111,27 @@ def add(name, **kwargs): .. code-block:: bash salt '*' group.add foo - ''' + """ if not info(name): comp_obj = _get_computer_object() try: - new_group = comp_obj.Create('group', name) + new_group = comp_obj.Create("group", name) new_group.SetInfo() - log.info('Successfully created group {0}'.format(name)) + log.info("Successfully created group {0}".format(name)) except pywintypes.com_error as exc: - msg = 'Failed to create group {0}. {1}'.format( - name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to create group {0}. {1}".format( + name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.error(msg) return False else: - log.warning('The group {0} already exists.'.format(name)) + log.warning("The group {0} already exists.".format(name)) return False return True def delete(name, **kwargs): - ''' + """ Remove the named group Args: @@ -145,26 +147,27 @@ def delete(name, **kwargs): .. code-block:: bash salt '*' group.delete foo - ''' + """ if info(name): comp_obj = _get_computer_object() try: - comp_obj.Delete('group', name) - log.info('Successfully removed group {0}'.format(name)) + comp_obj.Delete("group", name) + log.info("Successfully removed group {0}".format(name)) except pywintypes.com_error as exc: - msg = 'Failed to remove group {0}. {1}'.format( - name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to remove group {0}. {1}".format( + name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.error(msg) return False else: - log.warning('The group {0} does not exists.'.format(name)) + log.warning("The group {0} does not exists.".format(name)) return False return True def info(name): - ''' + """ Return information about a group Args: @@ -180,28 +183,26 @@ def info(name): .. code-block:: bash salt '*' group.info foo - ''' + """ try: groupObj = _get_group_object(name) gr_name = groupObj.Name gr_mem = [_get_username(x) for x in groupObj.members()] except pywintypes.com_error as exc: - msg = 'Failed to access group {0}. {1}'.format( - name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to access group {0}. {1}".format( + name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.debug(msg) return False if not gr_name: return False - return {'name': gr_name, - 'passwd': None, - 'gid': None, - 'members': gr_mem} + return {"name": gr_name, "passwd": None, "gid": None, "members": gr_mem} def getent(refresh=False): - ''' + """ Return info on all groups Args: @@ -220,26 +221,28 @@ def getent(refresh=False): .. code-block:: bash salt '*' group.getent - ''' - if 'group.getent' in __context__ and not refresh: - return __context__['group.getent'] + """ + if "group.getent" in __context__ and not refresh: + return __context__["group.getent"] ret = [] results = _get_all_groups() for result in results: - group = {'gid': __salt__['file.group_to_gid'](result.Name), - 'members': [_get_username(x) for x in result.members()], - 'name': result.Name, - 'passwd': 'x'} + group = { + "gid": __salt__["file.group_to_gid"](result.Name), + "members": [_get_username(x) for x in result.members()], + "name": result.Name, + "passwd": "x", + } ret.append(group) - __context__['group.getent'] = ret + __context__["group.getent"] = ret return ret def adduser(name, username, **kwargs): - ''' + """ Add a user to a group Args: @@ -258,12 +261,13 @@ def adduser(name, username, **kwargs): .. code-block:: bash salt '*' group.adduser foo username - ''' + """ try: group_obj = _get_group_object(name) except pywintypes.com_error as exc: - msg = 'Failed to access group {0}. {1}'.format( - name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to access group {0}. {1}".format( + name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.error(msg) return False @@ -272,14 +276,15 @@ def adduser(name, username, **kwargs): try: if username not in existing_members: - group_obj.Add('WinNT://' + username.replace('\\', '/')) - log.info('Added user {0}'.format(username)) + group_obj.Add("WinNT://" + username.replace("\\", "/")) + log.info("Added user {0}".format(username)) else: - log.warning('User {0} is already a member of {1}'.format(username, name)) + log.warning("User {0} is already a member of {1}".format(username, name)) return False except pywintypes.com_error as exc: - msg = 'Failed to add {0} to group {1}. {2}'.format( - username, name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to add {0} to group {1}. {2}".format( + username, name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.error(msg) return False @@ -287,7 +292,7 @@ def adduser(name, username, **kwargs): def deluser(name, username, **kwargs): - ''' + """ Remove a user from a group Args: @@ -306,12 +311,13 @@ def deluser(name, username, **kwargs): .. code-block:: bash salt '*' group.deluser foo username - ''' + """ try: group_obj = _get_group_object(name) except pywintypes.com_error as exc: - msg = 'Failed to access group {0}. {1}'.format( - name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to access group {0}. {1}".format( + name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.error(msg) return False @@ -319,14 +325,15 @@ def deluser(name, username, **kwargs): try: if salt.utils.win_functions.get_sam_name(username) in existing_members: - group_obj.Remove('WinNT://' + username.replace('\\', '/')) - log.info('Removed user {0}'.format(username)) + group_obj.Remove("WinNT://" + username.replace("\\", "/")) + log.info("Removed user {0}".format(username)) else: - log.warning('User {0} is not a member of {1}'.format(username, name)) + log.warning("User {0} is not a member of {1}".format(username, name)) return False except pywintypes.com_error as exc: - msg = 'Failed to remove {0} from group {1}. {2}'.format( - username, name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to remove {0} from group {1}. {2}".format( + username, name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.error(msg) return False @@ -334,7 +341,7 @@ def deluser(name, username, **kwargs): def members(name, members_list, **kwargs): - ''' + """ Ensure a group contains only the members in the list Args: @@ -354,18 +361,21 @@ def members(name, members_list, **kwargs): .. code-block:: bash salt '*' group.members foo 'user1,user2,user3' - ''' - members_list = [salt.utils.win_functions.get_sam_name(m) for m in members_list.split(",")] + """ + members_list = [ + salt.utils.win_functions.get_sam_name(m) for m in members_list.split(",") + ] if not isinstance(members_list, list): - log.debug('member_list is not a list') + log.debug("member_list is not a list") return False try: obj_group = _get_group_object(name) except pywintypes.com_error as exc: # Group probably doesn't exist, but we'll log the error - msg = 'Failed to access group {0}. {1}'.format( - name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to access group {0}. {1}".format( + name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.error(msg) return False @@ -374,7 +384,7 @@ def members(name, members_list, **kwargs): members_list.sort() if existing_members == members_list: - log.info('{0} membership is correct'.format(name)) + log.info("{0} membership is correct".format(name)) return True # add users @@ -382,11 +392,12 @@ def members(name, members_list, **kwargs): for member in members_list: if member not in existing_members: try: - obj_group.Add('WinNT://' + member.replace('\\', '/')) - log.info('User added: {0}'.format(member)) + obj_group.Add("WinNT://" + member.replace("\\", "/")) + log.info("User added: {0}".format(member)) except pywintypes.com_error as exc: - msg = 'Failed to add {0} to {1}. {2}'.format( - member, name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to add {0} to {1}. {2}".format( + member, name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.error(msg) success = False @@ -394,11 +405,12 @@ def members(name, members_list, **kwargs): for member in existing_members: if member not in members_list: try: - obj_group.Remove('WinNT://' + member.replace('\\', '/')) - log.info('User removed: {0}'.format(member)) + obj_group.Remove("WinNT://" + member.replace("\\", "/")) + log.info("User removed: {0}".format(member)) except pywintypes.com_error as exc: - msg = 'Failed to remove {0} from {1}. {2}'.format( - member, name, win32api.FormatMessage(exc.excepinfo[5])) + msg = "Failed to remove {0} from {1}. {2}".format( + member, name, win32api.FormatMessage(exc.excepinfo[5]) + ) log.error(msg) success = False @@ -406,7 +418,7 @@ def members(name, members_list, **kwargs): def list_groups(refresh=False): - ''' + """ Return a list of groups Args: @@ -425,9 +437,9 @@ def list_groups(refresh=False): .. code-block:: bash salt '*' group.list_groups - ''' - if 'group.list_groups' in __context__ and not refresh: - return __context__['group.list_groups'] + """ + if "group.list_groups" in __context__ and not refresh: + return __context__["group.list_groups"] results = _get_all_groups() @@ -436,6 +448,6 @@ def list_groups(refresh=False): for result in results: ret.append(result.Name) - __context__['group.list_groups'] = ret + __context__["group.list_groups"] = ret return ret diff --git a/salt/modules/win_iis.py b/salt/modules/win_iis.py index 73dc2305da5..acc8ef36fb1 100644 --- a/salt/modules/win_iis.py +++ b/salt/modules/win_iis.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Microsoft IIS site management via WebAdministration powershell module :maintainer: Shane Lee <slee@saltstack.com>, Robert Booth <rbooth@saltstack.com> @@ -8,52 +8,53 @@ Microsoft IIS site management via WebAdministration powershell module :depends: WebAdministration module (PowerShell) (IIS) .. versionadded:: 2016.3.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import decimal import logging -import re import os -import yaml +import re # Import salt libs import salt.utils.json import salt.utils.platform -from salt.ext.six.moves import range, map -from salt.exceptions import SaltInvocationError, CommandExecutionError +import yaml +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six +from salt.ext.six.moves import map, range log = logging.getLogger(__name__) -_DEFAULT_APP = '/' -_VALID_PROTOCOLS = ('ftp', 'http', 'https') +_DEFAULT_APP = "/" +_VALID_PROTOCOLS = ("ftp", "http", "https") _VALID_SSL_FLAGS = tuple(range(0, 4)) # Define the module's virtual name -__virtualname__ = 'win_iis' +__virtualname__ = "win_iis" def __virtual__(): - ''' + """ Load only on Windows Requires PowerShell and the WebAdministration module - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Only available on Windows systems' + return False, "Only available on Windows systems" - powershell_info = __salt__['cmd.shell_info']('powershell', True) - if not powershell_info['installed']: - return False, 'PowerShell not available' + powershell_info = __salt__["cmd.shell_info"]("powershell", True) + if not powershell_info["installed"]: + return False, "PowerShell not available" - if 'WebAdministration' not in powershell_info['modules']: - return False, 'IIS is not installed' + if "WebAdministration" not in powershell_info["modules"]: + return False, "IIS is not installed" return __virtualname__ -def _get_binding_info(host_header='', ip_address='*', port=80): - ''' +def _get_binding_info(host_header="", ip_address="*", port=80): + """ Combine the host header, IP address, and TCP port into bindingInformation format. Binding Information specifies information to communicate with a site. It includes the IP address, the port number, and an optional host @@ -67,13 +68,12 @@ def _get_binding_info(host_header='', ip_address='*', port=80): Returns: str: A properly formatted bindingInformation string (IP:port:hostheader) eg: 192.168.0.12:80:www.contoso.com - ''' - return ':'.join([ip_address, six.text_type(port), - host_header.replace(' ', '')]) + """ + return ":".join([ip_address, six.text_type(port), host_header.replace(" ", "")]) -def _list_certs(certificate_store='My'): - ''' +def _list_certs(certificate_store="My"): + """ List details of available certificates in the LocalMachine certificate store. @@ -83,21 +83,24 @@ def _list_certs(certificate_store='My'): Returns: dict: A dictionary of certificates found in the store - ''' + """ ret = dict() - blacklist_keys = ['DnsNameList', 'Thumbprint'] + blacklist_keys = ["DnsNameList", "Thumbprint"] - ps_cmd = ['Get-ChildItem', - '-Path', r"'Cert:\LocalMachine\{0}'".format(certificate_store), - '|', - 'Select-Object DnsNameList, SerialNumber, Subject, Thumbprint, Version'] + ps_cmd = [ + "Get-ChildItem", + "-Path", + r"'Cert:\LocalMachine\{0}'".format(certificate_store), + "|", + "Select-Object DnsNameList, SerialNumber, Subject, Thumbprint, Version", + ] cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError('Unable to parse return data as Json.') + raise CommandExecutionError("Unable to parse return data as Json.") for item in items: @@ -106,11 +109,11 @@ def _list_certs(certificate_store='My'): if key not in blacklist_keys: cert_info[key.lower()] = item[key] - cert_info['dnsnames'] = [] - if item['DnsNameList']: - cert_info['dnsnames'] = [name['Unicode'] for name in item['DnsNameList']] + cert_info["dnsnames"] = [] + if item["DnsNameList"]: + cert_info["dnsnames"] = [name["Unicode"] for name in item["DnsNameList"]] - ret[item['Thumbprint']] = cert_info + ret[item["Thumbprint"]] = cert_info return ret @@ -118,21 +121,23 @@ def _list_certs(certificate_store='My'): def _iisVersion(): pscmd = [] pscmd.append(r"Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\InetStp\\") - pscmd.append(' | Select-Object MajorVersion, MinorVersion') + pscmd.append(" | Select-Object MajorVersion, MinorVersion") cmd_ret = _srvmgr(pscmd, return_json=True) try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) except ValueError: - log.error('Unable to parse return data as Json.') + log.error("Unable to parse return data as Json.") return -1 - return decimal.Decimal("{0}.{1}".format(items[0]['MajorVersion'], items[0]['MinorVersion'])) + return decimal.Decimal( + "{0}.{1}".format(items[0]["MajorVersion"], items[0]["MinorVersion"]) + ) def _srvmgr(cmd, return_json=False): - ''' + """ Execute a powershell command from the WebAdministration PS module. Args: @@ -142,31 +147,31 @@ def _srvmgr(cmd, return_json=False): Returns: str: The output from the command - ''' + """ if isinstance(cmd, list): - cmd = ' '.join(cmd) + cmd = " ".join(cmd) if return_json: - cmd = 'ConvertTo-Json -Compress -Depth 4 -InputObject @({0})' \ - ''.format(cmd) + cmd = "ConvertTo-Json -Compress -Depth 4 -InputObject @({0})" "".format(cmd) - cmd = 'Import-Module WebAdministration; {0}'.format(cmd) + cmd = "Import-Module WebAdministration; {0}".format(cmd) - ret = __salt__['cmd.run_all'](cmd, shell='powershell', python_shell=True) + ret = __salt__["cmd.run_all"](cmd, shell="powershell", python_shell=True) - if ret['retcode'] != 0: - msg = 'Unable to execute command: {0}\nError: {1}' \ - ''.format(cmd, ret['stderr']) + if ret["retcode"] != 0: + msg = "Unable to execute command: {0}\nError: {1}" "".format(cmd, ret["stderr"]) log.error(msg) return ret def _collection_match_to_index(pspath, colfilter, name, match): - ''' + """ Returns index of collection item matching the match dictionary. - ''' - collection = get_webconfiguration_settings(pspath, [{'name': name, 'filter': colfilter}])[0]['value'] + """ + collection = get_webconfiguration_settings( + pspath, [{"name": name, "filter": colfilter}] + )[0]["value"] for idx, collect_dict in enumerate(collection): if all(item in collect_dict.items() for item in match.items()): return idx @@ -174,27 +179,29 @@ def _collection_match_to_index(pspath, colfilter, name, match): def _prepare_settings(pspath, settings): - ''' + """ Prepare settings before execution with get or set functions. Removes settings with a match parameter when index is not found. - ''' + """ prepared_settings = [] for setting in settings: - if setting.get('name', None) is None: - log.warning('win_iis: Setting has no name: {}'.format(setting)) + if setting.get("name", None) is None: + log.warning("win_iis: Setting has no name: {}".format(setting)) continue - if setting.get('filter', None) is None: - log.warning('win_iis: Setting has no filter: {}'.format(setting)) + if setting.get("filter", None) is None: + log.warning("win_iis: Setting has no filter: {}".format(setting)) continue - match = re.search(r'Collection\[(\{.*\})\]', setting['name']) + match = re.search(r"Collection\[(\{.*\})\]", setting["name"]) if match: - name = setting['name'][:match.start(1)-1] + name = setting["name"][: match.start(1) - 1] match_dict = yaml.load(match.group(1)) - index = _collection_match_to_index(pspath, setting['filter'], name, match_dict) + index = _collection_match_to_index( + pspath, setting["filter"], name, match_dict + ) if index == -1: - log.warning('win_iis: No match found for setting: {}'.format(setting)) + log.warning("win_iis: No match found for setting: {}".format(setting)) else: - setting['name'] = setting['name'].replace(match.group(1), str(index)) + setting["name"] = setting["name"].replace(match.group(1), str(index)) prepared_settings.append(setting) else: prepared_settings.append(setting) @@ -202,7 +209,7 @@ def _prepare_settings(pspath, settings): def list_sites(): - ''' + """ List all the currently deployed websites. Returns: @@ -213,29 +220,32 @@ def list_sites(): .. code-block:: bash salt '*' win_iis.list_sites - ''' + """ ret = dict() - ps_cmd = ['Get-ChildItem', - '-Path', r"'IIS:\Sites'", - '|', - 'Select-Object applicationPool, Bindings, ID, Name, PhysicalPath, State'] + ps_cmd = [ + "Get-ChildItem", + "-Path", + r"'IIS:\Sites'", + "|", + "Select-Object applicationPool, Bindings, ID, Name, PhysicalPath, State", + ] - keep_keys = ('certificateHash', 'certificateStoreName', 'protocol', 'sslFlags') + keep_keys = ("certificateHash", "certificateStoreName", "protocol", "sslFlags") cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError('Unable to parse return data as Json.') + raise CommandExecutionError("Unable to parse return data as Json.") for item in items: bindings = dict() - for binding in item['bindings']['Collection']: + for binding in item["bindings"]["Collection"]: # Ignore bindings which do not have host names - if binding['protocol'] not in ['http', 'https']: + if binding["protocol"] not in ["http", "https"]: continue filtered_binding = dict() @@ -244,28 +254,31 @@ def list_sites(): if key in keep_keys: filtered_binding.update({key.lower(): binding[key]}) - binding_info = binding['bindingInformation'].split(':', 2) + binding_info = binding["bindingInformation"].split(":", 2) ipaddress, port, hostheader = [element.strip() for element in binding_info] - filtered_binding.update({'hostheader': hostheader, - 'ipaddress': ipaddress, - 'port': port}) - bindings[binding['bindingInformation']] = filtered_binding + filtered_binding.update( + {"hostheader": hostheader, "ipaddress": ipaddress, "port": port} + ) + bindings[binding["bindingInformation"]] = filtered_binding - ret[item['name']] = {'apppool': item['applicationPool'], - 'bindings': bindings, - 'id': item['id'], - 'state': item['state'], - 'sourcepath': item['physicalPath']} + ret[item["name"]] = { + "apppool": item["applicationPool"], + "bindings": bindings, + "id": item["id"], + "state": item["state"], + "sourcepath": item["physicalPath"], + } if not ret: - log.warning('No sites found in output: %s', cmd_ret['stdout']) + log.warning("No sites found in output: %s", cmd_ret["stdout"]) return ret -def create_site(name, sourcepath, apppool='', hostheader='', - ipaddress='*', port=80, protocol='http'): - ''' +def create_site( + name, sourcepath, apppool="", hostheader="", ipaddress="*", port=80, protocol="http" +): + """ Create a basic website in IIS. .. note:: @@ -298,9 +311,9 @@ def create_site(name, sourcepath, apppool='', hostheader='', .. code-block:: bash salt '*' win_iis.create_site name='My Test Site' sourcepath='c:\\stage' apppool='TestPool' - ''' + """ protocol = six.text_type(protocol).lower() - site_path = r'IIS:\Sites\{0}'.format(name) + site_path = r"IIS:\Sites\{0}".format(name) binding_info = _get_binding_info(hostheader, ipaddress, port) current_sites = list_sites() @@ -309,42 +322,55 @@ def create_site(name, sourcepath, apppool='', hostheader='', return True if protocol not in _VALID_PROTOCOLS: - message = ("Invalid protocol '{0}' specified. Valid formats:" - ' {1}').format(protocol, _VALID_PROTOCOLS) + message = ("Invalid protocol '{0}' specified. Valid formats:" " {1}").format( + protocol, _VALID_PROTOCOLS + ) raise SaltInvocationError(message) - ps_cmd = ['New-Item', - '-Path', r"'{0}'".format(site_path), - '-PhysicalPath', r"'{0}'".format(sourcepath), - '-Bindings', "@{{ protocol='{0}'; bindingInformation='{1}' }};" - "".format(protocol, binding_info)] + ps_cmd = [ + "New-Item", + "-Path", + r"'{0}'".format(site_path), + "-PhysicalPath", + r"'{0}'".format(sourcepath), + "-Bindings", + "@{{ protocol='{0}'; bindingInformation='{1}' }};" + "".format(protocol, binding_info), + ] if apppool: if apppool in list_apppools(): - log.debug('Utilizing pre-existing application pool: %s', - apppool) + log.debug("Utilizing pre-existing application pool: %s", apppool) else: - log.debug('Application pool will be created: %s', apppool) + log.debug("Application pool will be created: %s", apppool) create_apppool(apppool) - ps_cmd.extend(['Set-ItemProperty', - '-Path', "'{0}'".format(site_path), - '-Name', 'ApplicationPool', - '-Value', "'{0}'".format(apppool)]) + ps_cmd.extend( + [ + "Set-ItemProperty", + "-Path", + "'{0}'".format(site_path), + "-Name", + "ApplicationPool", + "-Value", + "'{0}'".format(apppool), + ] + ) cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to create site: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to create site: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) - log.debug('Site created successfully: %s', name) + log.debug("Site created successfully: %s", name) return True def modify_site(name, sourcepath=None, apppool=None): - ''' + """ Modify a basic website in IIS. .. versionadded:: 2017.7.0 @@ -367,8 +393,8 @@ def modify_site(name, sourcepath=None, apppool=None): .. code-block:: bash salt '*' win_iis.modify_site name='My Test Site' sourcepath='c:\\new_path' apppool='NewTestPool' - ''' - site_path = r'IIS:\Sites\{0}'.format(name) + """ + site_path = r"IIS:\Sites\{0}".format(name) current_sites = list_sites() if name not in current_sites: @@ -378,42 +404,56 @@ def modify_site(name, sourcepath=None, apppool=None): ps_cmd = list() if sourcepath: - ps_cmd.extend(['Set-ItemProperty', - '-Path', r"'{0}'".format(site_path), - '-Name', 'PhysicalPath', - '-Value', r"'{0}'".format(sourcepath)]) + ps_cmd.extend( + [ + "Set-ItemProperty", + "-Path", + r"'{0}'".format(site_path), + "-Name", + "PhysicalPath", + "-Value", + r"'{0}'".format(sourcepath), + ] + ) if apppool: if apppool in list_apppools(): - log.debug('Utilizing pre-existing application pool: {0}' - ''.format(apppool)) + log.debug("Utilizing pre-existing application pool: {0}" "".format(apppool)) else: - log.debug('Application pool will be created: {0}'.format(apppool)) + log.debug("Application pool will be created: {0}".format(apppool)) create_apppool(apppool) # If ps_cmd isn't empty, we need to add a semi-colon to run two commands if ps_cmd: - ps_cmd.append(';') + ps_cmd.append(";") - ps_cmd.extend(['Set-ItemProperty', - '-Path', r"'{0}'".format(site_path), - '-Name', 'ApplicationPool', - '-Value', r"'{0}'".format(apppool)]) + ps_cmd.extend( + [ + "Set-ItemProperty", + "-Path", + r"'{0}'".format(site_path), + "-Name", + "ApplicationPool", + "-Value", + r"'{0}'".format(apppool), + ] + ) cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to modify site: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to modify site: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) - log.debug('Site modified successfully: %s', name) + log.debug("Site modified successfully: %s", name) return True def remove_site(name): - ''' + """ Delete a website from IIS. Args: @@ -432,28 +472,29 @@ def remove_site(name): salt '*' win_iis.remove_site name='My Test Site' - ''' + """ current_sites = list_sites() if name not in current_sites: - log.debug('Site already absent: %s', name) + log.debug("Site already absent: %s", name) return True - ps_cmd = ['Remove-WebSite', '-Name', r"'{0}'".format(name)] + ps_cmd = ["Remove-WebSite", "-Name", r"'{0}'".format(name)] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to remove site: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to remove site: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) - log.debug('Site removed successfully: %s', name) + log.debug("Site removed successfully: %s", name) return True def stop_site(name): - ''' + """ Stop a Web Site in IIS. .. versionadded:: 2017.7.0 @@ -469,16 +510,16 @@ def stop_site(name): .. code-block:: bash salt '*' win_iis.stop_site name='My Test Site' - ''' - ps_cmd = ['Stop-WebSite', r"'{0}'".format(name)] + """ + ps_cmd = ["Stop-WebSite", r"'{0}'".format(name)] cmd_ret = _srvmgr(ps_cmd) - return cmd_ret['retcode'] == 0 + return cmd_ret["retcode"] == 0 def start_site(name): - ''' + """ Start a Web Site in IIS. .. versionadded:: 2017.7.0 @@ -494,16 +535,16 @@ def start_site(name): .. code-block:: bash salt '*' win_iis.start_site name='My Test Site' - ''' - ps_cmd = ['Start-WebSite', r"'{0}'".format(name)] + """ + ps_cmd = ["Start-WebSite", r"'{0}'".format(name)] cmd_ret = _srvmgr(ps_cmd) - return cmd_ret['retcode'] == 0 + return cmd_ret["retcode"] == 0 def restart_site(name): - ''' + """ Restart a Web Site in IIS. .. versionadded:: 2017.7.0 @@ -519,12 +560,12 @@ def restart_site(name): .. code-block:: bash salt '*' win_iis.restart_site name='My Test Site' - ''' + """ return stop_site(name) and start_site(name) def list_bindings(site): - ''' + """ Get all configured IIS bindings for the specified site. Args: @@ -538,25 +579,26 @@ def list_bindings(site): .. code-block:: bash salt '*' win_iis.list_bindings site - ''' + """ ret = dict() sites = list_sites() if site not in sites: - log.warning('Site not found: %s', site) + log.warning("Site not found: %s", site) return ret - ret = sites[site]['bindings'] + ret = sites[site]["bindings"] if not ret: - log.warning('No bindings found for site: %s', site) + log.warning("No bindings found for site: %s", site) return ret -def create_binding(site, hostheader='', ipaddress='*', port=80, protocol='http', - sslflags=None): - ''' +def create_binding( + site, hostheader="", ipaddress="*", port=80, protocol="http", sslflags=None +): + """ Create an IIS Web Binding. .. note:: @@ -583,62 +625,81 @@ def create_binding(site, hostheader='', ipaddress='*', port=80, protocol='http', .. code-block:: bash salt '*' win_iis.create_binding site='site0' hostheader='example.com' ipaddress='*' port='80' - ''' + """ protocol = six.text_type(protocol).lower() name = _get_binding_info(hostheader, ipaddress, port) if protocol not in _VALID_PROTOCOLS: - message = ("Invalid protocol '{0}' specified. Valid formats:" - ' {1}').format(protocol, _VALID_PROTOCOLS) + message = ("Invalid protocol '{0}' specified. Valid formats:" " {1}").format( + protocol, _VALID_PROTOCOLS + ) raise SaltInvocationError(message) if sslflags: sslflags = int(sslflags) if sslflags not in _VALID_SSL_FLAGS: - message = ("Invalid sslflags '{0}' specified. Valid sslflags range:" - ' {1}..{2}').format(sslflags, _VALID_SSL_FLAGS[0], _VALID_SSL_FLAGS[-1]) + message = ( + "Invalid sslflags '{0}' specified. Valid sslflags range:" " {1}..{2}" + ).format(sslflags, _VALID_SSL_FLAGS[0], _VALID_SSL_FLAGS[-1]) raise SaltInvocationError(message) current_bindings = list_bindings(site) if name in current_bindings: - log.debug('Binding already present: %s', name) + log.debug("Binding already present: %s", name) return True if sslflags: - ps_cmd = ['New-WebBinding', - '-Name', "'{0}'".format(site), - '-HostHeader', "'{0}'".format(hostheader), - '-IpAddress', "'{0}'".format(ipaddress), - '-Port', "'{0}'".format(port), - '-Protocol', "'{0}'".format(protocol), - '-SslFlags', '{0}'.format(sslflags)] + ps_cmd = [ + "New-WebBinding", + "-Name", + "'{0}'".format(site), + "-HostHeader", + "'{0}'".format(hostheader), + "-IpAddress", + "'{0}'".format(ipaddress), + "-Port", + "'{0}'".format(port), + "-Protocol", + "'{0}'".format(protocol), + "-SslFlags", + "{0}".format(sslflags), + ] else: - ps_cmd = ['New-WebBinding', - '-Name', "'{0}'".format(site), - '-HostHeader', "'{0}'".format(hostheader), - '-IpAddress', "'{0}'".format(ipaddress), - '-Port', "'{0}'".format(port), - '-Protocol', "'{0}'".format(protocol)] + ps_cmd = [ + "New-WebBinding", + "-Name", + "'{0}'".format(site), + "-HostHeader", + "'{0}'".format(hostheader), + "-IpAddress", + "'{0}'".format(ipaddress), + "-Port", + "'{0}'".format(port), + "-Protocol", + "'{0}'".format(protocol), + ] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to create binding: {0}\nError: {1}' \ - ''.format(site, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to create binding: {0}\nError: {1}" "".format( + site, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) if name in list_bindings(site): - log.debug('Binding created successfully: %s', site) + log.debug("Binding created successfully: %s", site) return True - log.error('Unable to create binding: %s', site) + log.error("Unable to create binding: %s", site) return False -def modify_binding(site, binding, hostheader=None, ipaddress=None, port=None, - sslflags=None): - ''' +def modify_binding( + site, binding, hostheader=None, ipaddress=None, port=None, sslflags=None +): + """ Modify an IIS Web Binding. Use ``site`` and ``binding`` to target the binding. @@ -667,10 +728,11 @@ def modify_binding(site, binding, hostheader=None, ipaddress=None, port=None, .. code-block:: bash salt '*' win_iis.modify_binding site='site0' binding='*:80:' hostheader='example.com' - ''' + """ if sslflags is not None and sslflags not in _VALID_SSL_FLAGS: - message = ("Invalid sslflags '{0}' specified. Valid sslflags range:" - ' {1}..{2}').format(sslflags, _VALID_SSL_FLAGS[0], _VALID_SSL_FLAGS[-1]) + message = ( + "Invalid sslflags '{0}' specified. Valid sslflags range:" " {1}..{2}" + ).format(sslflags, _VALID_SSL_FLAGS[0], _VALID_SSL_FLAGS[-1]) raise SaltInvocationError(message) current_sites = list_sites() @@ -687,46 +749,66 @@ def modify_binding(site, binding, hostheader=None, ipaddress=None, port=None, # Split out the binding so we can insert new ones # Use the existing value if not passed - i, p, h = binding.split(':') - new_binding = ':'.join([ipaddress if ipaddress is not None else i, - six.text_type(port) if port is not None else six.text_type(p), - hostheader if hostheader is not None else h]) + i, p, h = binding.split(":") + new_binding = ":".join( + [ + ipaddress if ipaddress is not None else i, + six.text_type(port) if port is not None else six.text_type(p), + hostheader if hostheader is not None else h, + ] + ) if new_binding != binding: - ps_cmd = ['Set-WebBinding', - '-Name', "'{0}'".format(site), - '-BindingInformation', "'{0}'".format(binding), - '-PropertyName', 'BindingInformation', - '-Value', "'{0}'".format(new_binding)] + ps_cmd = [ + "Set-WebBinding", + "-Name", + "'{0}'".format(site), + "-BindingInformation", + "'{0}'".format(binding), + "-PropertyName", + "BindingInformation", + "-Value", + "'{0}'".format(new_binding), + ] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to modify binding: {0}\nError: {1}' \ - ''.format(binding, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to modify binding: {0}\nError: {1}" "".format( + binding, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) - if sslflags is not None and \ - sslflags != current_sites[site]['bindings'][binding]['sslflags']: - ps_cmd = ['Set-WebBinding', - '-Name', "'{0}'".format(site), - '-BindingInformation', "'{0}'".format(new_binding), - '-PropertyName', 'sslflags', - '-Value', "'{0}'".format(sslflags)] + if ( + sslflags is not None + and sslflags != current_sites[site]["bindings"][binding]["sslflags"] + ): + ps_cmd = [ + "Set-WebBinding", + "-Name", + "'{0}'".format(site), + "-BindingInformation", + "'{0}'".format(new_binding), + "-PropertyName", + "sslflags", + "-Value", + "'{0}'".format(sslflags), + ] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to modify binding SSL Flags: {0}\nError: {1}' \ - ''.format(sslflags, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to modify binding SSL Flags: {0}\nError: {1}" "".format( + sslflags, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) - log.debug('Binding modified successfully: %s', binding) + log.debug("Binding modified successfully: %s", binding) return True -def remove_binding(site, hostheader='', ipaddress='*', port=80): - ''' +def remove_binding(site, hostheader="", ipaddress="*", port=80): + """ Remove an IIS binding. Args: @@ -743,35 +825,41 @@ def remove_binding(site, hostheader='', ipaddress='*', port=80): .. code-block:: bash salt '*' win_iis.remove_binding site='site0' hostheader='example.com' ipaddress='*' port='80' - ''' + """ name = _get_binding_info(hostheader, ipaddress, port) current_bindings = list_bindings(site) if name not in current_bindings: - log.debug('Binding already absent: %s', name) + log.debug("Binding already absent: %s", name) return True - ps_cmd = ['Remove-WebBinding', - '-HostHeader', "'{0}'".format(hostheader), - '-IpAddress', "'{0}'".format(ipaddress), - '-Port', "'{0}'".format(port)] + ps_cmd = [ + "Remove-WebBinding", + "-HostHeader", + "'{0}'".format(hostheader), + "-IpAddress", + "'{0}'".format(ipaddress), + "-Port", + "'{0}'".format(port), + ] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to remove binding: {0}\nError: {1}' \ - ''.format(site, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to remove binding: {0}\nError: {1}" "".format( + site, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) if name not in list_bindings(site): - log.debug('Binding removed successfully: %s', site) + log.debug("Binding removed successfully: %s", site) return True - log.error('Unable to remove binding: %s', site) + log.error("Unable to remove binding: %s", site) return False def list_cert_bindings(site): - ''' + """ List certificate bindings for an IIS site. .. versionadded:: 2016.11.0 @@ -787,27 +875,26 @@ def list_cert_bindings(site): .. code-block:: bash salt '*' win_iis.list_bindings site - ''' + """ ret = dict() sites = list_sites() if site not in sites: - log.warning('Site not found: %s', site) + log.warning("Site not found: %s", site) return ret - for binding in sites[site]['bindings']: - if sites[site]['bindings'][binding]['certificatehash']: - ret[binding] = sites[site]['bindings'][binding] + for binding in sites[site]["bindings"]: + if sites[site]["bindings"][binding]["certificatehash"]: + ret[binding] = sites[site]["bindings"][binding] if not ret: - log.warning('No certificate bindings found for site: %s', site) + log.warning("No certificate bindings found for site: %s", site) return ret -def create_cert_binding(name, site, hostheader='', ipaddress='*', port=443, - sslflags=0): - ''' +def create_cert_binding(name, site, hostheader="", ipaddress="*", port=443, sslflags=0): + """ Assign a certificate to an IIS Web Binding. .. versionadded:: 2016.11.0 @@ -833,27 +920,27 @@ def create_cert_binding(name, site, hostheader='', ipaddress='*', port=443, .. code-block:: bash salt '*' win_iis.create_cert_binding name='AAA000' site='site0' hostheader='example.com' ipaddress='*' port='443' - ''' + """ name = six.text_type(name).upper() binding_info = _get_binding_info(hostheader, ipaddress, port) if _iisVersion() < 8: # IIS 7.5 and earlier don't support SNI for HTTPS, therefore cert bindings don't contain the host header - binding_info = binding_info.rpartition(':')[0] + ':' + binding_info = binding_info.rpartition(":")[0] + ":" - binding_path = r"IIS:\SslBindings\{0}".format(binding_info.replace(':', '!')) + binding_path = r"IIS:\SslBindings\{0}".format(binding_info.replace(":", "!")) if sslflags not in _VALID_SSL_FLAGS: - message = ("Invalid sslflags '{0}' specified. Valid sslflags range: " - "{1}..{2}").format(sslflags, _VALID_SSL_FLAGS[0], - _VALID_SSL_FLAGS[-1]) + message = ( + "Invalid sslflags '{0}' specified. Valid sslflags range: " "{1}..{2}" + ).format(sslflags, _VALID_SSL_FLAGS[0], _VALID_SSL_FLAGS[-1]) raise SaltInvocationError(message) # Verify that the target binding exists. current_bindings = list_bindings(site) if binding_info not in current_bindings: - log.error('Binding not present: %s', binding_info) + log.error("Binding not present: %s", binding_info) return False # Check to see if the certificate is already assigned. @@ -861,20 +948,20 @@ def create_cert_binding(name, site, hostheader='', ipaddress='*', port=443, for current_binding in current_bindings: if binding_info == current_binding: - current_name = current_bindings[current_binding]['certificatehash'] + current_name = current_bindings[current_binding]["certificatehash"] - log.debug('Current certificate thumbprint: %s', current_name) - log.debug('New certificate thumbprint: %s', name) + log.debug("Current certificate thumbprint: %s", current_name) + log.debug("New certificate thumbprint: %s", name) if name == current_name: - log.debug('Certificate already present for binding: %s', name) + log.debug("Certificate already present for binding: %s", name) return True # Verify that the certificate exists. certs = _list_certs() if name not in certs: - log.error('Certificate not present: %s', name) + log.error("Certificate not present: %s", name) return False if _iisVersion() < 8: @@ -885,39 +972,49 @@ def create_cert_binding(name, site, hostheader='', ipaddress='*', port=443, if iis7path.endswith("!"): iis7path = iis7path[:-1] - ps_cmd = ['New-Item', - '-Path', "'{0}'".format(iis7path), - '-Thumbprint', "'{0}'".format(name)] + ps_cmd = [ + "New-Item", + "-Path", + "'{0}'".format(iis7path), + "-Thumbprint", + "'{0}'".format(name), + ] else: - ps_cmd = ['New-Item', - '-Path', "'{0}'".format(binding_path), - '-Thumbprint', "'{0}'".format(name), - '-SSLFlags', '{0}'.format(sslflags)] + ps_cmd = [ + "New-Item", + "-Path", + "'{0}'".format(binding_path), + "-Thumbprint", + "'{0}'".format(name), + "-SSLFlags", + "{0}".format(sslflags), + ] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to create certificate binding: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to create certificate binding: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) new_cert_bindings = list_cert_bindings(site) if binding_info not in new_cert_bindings: - log.error('Binding not present: %s', binding_info) + log.error("Binding not present: %s", binding_info) return False - if name == new_cert_bindings[binding_info]['certificatehash']: - log.debug('Certificate binding created successfully: %s', name) + if name == new_cert_bindings[binding_info]["certificatehash"]: + log.debug("Certificate binding created successfully: %s", name) return True - log.error('Unable to create certificate binding: %s', name) + log.error("Unable to create certificate binding: %s", name) return False -def remove_cert_binding(name, site, hostheader='', ipaddress='*', port=443): - ''' +def remove_cert_binding(name, site, hostheader="", ipaddress="*", port=443): + """ Remove a certificate from an IIS Web Binding. .. versionadded:: 2016.11.0 @@ -942,54 +1039,61 @@ def remove_cert_binding(name, site, hostheader='', ipaddress='*', port=443): .. code-block:: bash salt '*' win_iis.remove_cert_binding name='AAA000' site='site0' hostheader='example.com' ipaddress='*' port='443' - ''' + """ name = six.text_type(name).upper() binding_info = _get_binding_info(hostheader, ipaddress, port) # Child items of IIS:\SslBindings do not return populated host header info # in all circumstances, so it's necessary to use IIS:\Sites instead. - ps_cmd = ['$Site = Get-ChildItem', '-Path', r"'IIS:\Sites'", - '|', 'Where-Object', r" {{ $_.Name -Eq '{0}' }};".format(site), - '$Binding = $Site.Bindings.Collection', - r"| Where-Object { $_.bindingInformation", - r"-Eq '{0}' }};".format(binding_info), - '$Binding.RemoveSslCertificate()'] + ps_cmd = [ + "$Site = Get-ChildItem", + "-Path", + r"'IIS:\Sites'", + "|", + "Where-Object", + r" {{ $_.Name -Eq '{0}' }};".format(site), + "$Binding = $Site.Bindings.Collection", + r"| Where-Object { $_.bindingInformation", + r"-Eq '{0}' }};".format(binding_info), + "$Binding.RemoveSslCertificate()", + ] # Verify that the binding exists for the site, and that the target # certificate is assigned to the binding. current_cert_bindings = list_cert_bindings(site) if binding_info not in current_cert_bindings: - log.warning('Binding not found: %s', binding_info) + log.warning("Binding not found: %s", binding_info) return True - if name != current_cert_bindings[binding_info]['certificatehash']: - log.debug('Certificate binding already absent: %s', name) + if name != current_cert_bindings[binding_info]["certificatehash"]: + log.debug("Certificate binding already absent: %s", name) return True cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to remove certificate binding: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to remove certificate binding: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) new_cert_bindings = list_cert_bindings(site) if binding_info not in new_cert_bindings: - log.warning('Binding not found: %s', binding_info) + log.warning("Binding not found: %s", binding_info) return True - if name != new_cert_bindings[binding_info]['certificatehash']: - log.debug('Certificate binding removed successfully: %s', name) + if name != new_cert_bindings[binding_info]["certificatehash"]: + log.debug("Certificate binding removed successfully: %s", name) return True - log.error('Unable to remove certificate binding: %s', name) + log.error("Unable to remove certificate binding: %s", name) return False def list_apppools(): - ''' + """ List all configured IIS application pools. Returns: @@ -1000,7 +1104,7 @@ def list_apppools(): .. code-block:: bash salt '*' win_iis.list_apppools - ''' + """ ret = dict() ps_cmd = [] ps_cmd.append(r"Get-ChildItem -Path 'IIS:\AppPools' | Select-Object Name, State") @@ -1011,21 +1115,25 @@ def list_apppools(): ps_cmd.append(r", @{ Name = 'Applications'; Expression = { $AppPool = $_.Name;") ps_cmd.append("$AppPath = 'machine/webroot/apphost';") ps_cmd.append("$FilterBase = '/system.applicationHost/sites/site/application';") - ps_cmd.append('$FilterBase += "[@applicationPool = \'$($AppPool)\' and @path";') - ps_cmd.append('$FilterRoot = "$($FilterBase) = \'/\']/parent::*";') - ps_cmd.append('$FilterNonRoot = "$($FilterBase) != \'/\']";') - ps_cmd.append('Get-WebConfigurationProperty -Filter $FilterRoot -PsPath $AppPath -Name Name') - ps_cmd.append(r'| ForEach-Object { $_.Value };') - ps_cmd.append('Get-WebConfigurationProperty -Filter $FilterNonRoot -PsPath $AppPath -Name Path') + ps_cmd.append("$FilterBase += \"[@applicationPool = '$($AppPool)' and @path\";") + ps_cmd.append("$FilterRoot = \"$($FilterBase) = '/']/parent::*\";") + ps_cmd.append("$FilterNonRoot = \"$($FilterBase) != '/']\";") + ps_cmd.append( + "Get-WebConfigurationProperty -Filter $FilterRoot -PsPath $AppPath -Name Name" + ) + ps_cmd.append(r"| ForEach-Object { $_.Value };") + ps_cmd.append( + "Get-WebConfigurationProperty -Filter $FilterNonRoot -PsPath $AppPath -Name Path" + ) ps_cmd.append(r"| ForEach-Object { $_.Value } | Where-Object { $_ -ne '/' }") - ps_cmd.append('} }') + ps_cmd.append("} }") cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError('Unable to parse return data as Json.') + raise CommandExecutionError("Unable to parse return data as Json.") for item in items: applications = list() @@ -1034,23 +1142,22 @@ def list_apppools(): # if there is one app, it will be a string, and if there are multiple, # it will be a dict with 'Count' and 'value' as the keys. - if isinstance(item['Applications'], dict): - if 'value' in item['Applications']: - applications += item['Applications']['value'] + if isinstance(item["Applications"], dict): + if "value" in item["Applications"]: + applications += item["Applications"]["value"] else: - applications.append(item['Applications']) + applications.append(item["Applications"]) - ret[item['name']] = {'state': item['state'], 'applications': applications} + ret[item["name"]] = {"state": item["state"], "applications": applications} if not ret: - log.warning('No application pools found in output: %s', - cmd_ret['stdout']) + log.warning("No application pools found in output: %s", cmd_ret["stdout"]) return ret def create_apppool(name): - ''' + """ Create an IIS application pool. .. note:: @@ -1071,29 +1178,30 @@ def create_apppool(name): .. code-block:: bash salt '*' win_iis.create_apppool name='MyTestPool' - ''' + """ current_apppools = list_apppools() - apppool_path = r'IIS:\AppPools\{0}'.format(name) + apppool_path = r"IIS:\AppPools\{0}".format(name) if name in current_apppools: log.debug("Application pool '%s' already present.", name) return True - ps_cmd = ['New-Item', '-Path', r"'{0}'".format(apppool_path)] + ps_cmd = ["New-Item", "-Path", r"'{0}'".format(apppool_path)] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to create application pool: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to create application pool: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) - log.debug('Application pool created successfully: %s', name) + log.debug("Application pool created successfully: %s", name) return True def remove_apppool(name): - ''' + """ Remove an IIS application pool. Args: @@ -1107,29 +1215,30 @@ def remove_apppool(name): .. code-block:: bash salt '*' win_iis.remove_apppool name='MyTestPool' - ''' + """ current_apppools = list_apppools() - apppool_path = r'IIS:\AppPools\{0}'.format(name) + apppool_path = r"IIS:\AppPools\{0}".format(name) if name not in current_apppools: - log.debug('Application pool already absent: %s', name) + log.debug("Application pool already absent: %s", name) return True - ps_cmd = ['Remove-Item', '-Path', r"'{0}'".format(apppool_path), '-Recurse'] + ps_cmd = ["Remove-Item", "-Path", r"'{0}'".format(apppool_path), "-Recurse"] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to remove application pool: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to remove application pool: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) - log.debug('Application pool removed successfully: %s', name) + log.debug("Application pool removed successfully: %s", name) return True def stop_apppool(name): - ''' + """ Stop an IIS application pool. .. versionadded:: 2017.7.0 @@ -1145,16 +1254,16 @@ def stop_apppool(name): .. code-block:: bash salt '*' win_iis.stop_apppool name='MyTestPool' - ''' - ps_cmd = ['Stop-WebAppPool', r"'{0}'".format(name)] + """ + ps_cmd = ["Stop-WebAppPool", r"'{0}'".format(name)] cmd_ret = _srvmgr(ps_cmd) - return cmd_ret['retcode'] == 0 + return cmd_ret["retcode"] == 0 def start_apppool(name): - ''' + """ Start an IIS application pool. .. versionadded:: 2017.7.0 @@ -1170,16 +1279,16 @@ def start_apppool(name): .. code-block:: bash salt '*' win_iis.start_apppool name='MyTestPool' - ''' - ps_cmd = ['Start-WebAppPool', r"'{0}'".format(name)] + """ + ps_cmd = ["Start-WebAppPool", r"'{0}'".format(name)] cmd_ret = _srvmgr(ps_cmd) - return cmd_ret['retcode'] == 0 + return cmd_ret["retcode"] == 0 def restart_apppool(name): - ''' + """ Restart an IIS application pool. .. versionadded:: 2016.11.0 @@ -1195,16 +1304,16 @@ def restart_apppool(name): .. code-block:: bash salt '*' win_iis.restart_apppool name='MyTestPool' - ''' - ps_cmd = ['Restart-WebAppPool', r"'{0}'".format(name)] + """ + ps_cmd = ["Restart-WebAppPool", r"'{0}'".format(name)] cmd_ret = _srvmgr(ps_cmd) - return cmd_ret['retcode'] == 0 + return cmd_ret["retcode"] == 0 def get_container_setting(name, container, settings): - ''' + """ Get the value of the setting for the IIS container. .. versionadded:: 2016.11.0 @@ -1224,50 +1333,58 @@ def get_container_setting(name, container, settings): salt '*' win_iis.get_container_setting name='MyTestPool' container='AppPools' settings="['processModel.identityType']" - ''' + """ ret = dict() ps_cmd = list() ps_cmd_validate = list() container_path = r"IIS:\{0}\{1}".format(container, name) if not settings: - log.warning('No settings provided') + log.warning("No settings provided") return ret - ps_cmd.append(r'$Settings = @{};') + ps_cmd.append(r"$Settings = @{};") for setting in settings: # Build the commands to verify that the property names are valid. - ps_cmd_validate.extend(['Get-ItemProperty', - '-Path', "'{0}'".format(container_path), - '-Name', "'{0}'".format(setting), - '-ErrorAction', 'Stop', - '|', 'Out-Null;']) + ps_cmd_validate.extend( + [ + "Get-ItemProperty", + "-Path", + "'{0}'".format(container_path), + "-Name", + "'{0}'".format(setting), + "-ErrorAction", + "Stop", + "|", + "Out-Null;", + ] + ) # Some ItemProperties are Strings and others are ConfigurationAttributes. # Since the former doesn't have a Value property, we need to account # for this. ps_cmd.append("$Property = Get-ItemProperty -Path '{0}'".format(container_path)) ps_cmd.append("-Name '{0}' -ErrorAction Stop;".format(setting)) - ps_cmd.append(r'if (([String]::IsNullOrEmpty($Property) -eq $False) -and') + ps_cmd.append(r"if (([String]::IsNullOrEmpty($Property) -eq $False) -and") ps_cmd.append(r"($Property.GetType()).Name -eq 'ConfigurationAttribute') {") - ps_cmd.append(r'$Property = $Property | Select-Object') - ps_cmd.append(r'-ExpandProperty Value };') + ps_cmd.append(r"$Property = $Property | Select-Object") + ps_cmd.append(r"-ExpandProperty Value };") ps_cmd.append("$Settings['{0}'] = [String] $Property;".format(setting)) - ps_cmd.append(r'$Property = $Null;') + ps_cmd.append(r"$Property = $Null;") # Validate the setting names that were passed in. cmd_ret = _srvmgr(cmd=ps_cmd_validate, return_json=True) - if cmd_ret['retcode'] != 0: - message = 'One or more invalid property names were specified for the provided container.' + if cmd_ret["retcode"] != 0: + message = "One or more invalid property names were specified for the provided container." raise SaltInvocationError(message) - ps_cmd.append('$Settings') + ps_cmd.append("$Settings") cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) if isinstance(items, list): ret.update(items[0]) @@ -1275,13 +1392,13 @@ def get_container_setting(name, container, settings): ret.update(items) except ValueError: - raise CommandExecutionError('Unable to parse return data as Json.') + raise CommandExecutionError("Unable to parse return data as Json.") return ret def set_container_setting(name, container, settings): - ''' + """ Set the value of the setting for an IIS container. .. versionadded:: 2016.11.0 @@ -1301,15 +1418,27 @@ def set_container_setting(name, container, settings): salt '*' win_iis.set_container_setting name='MyTestPool' container='AppPools' settings="{'managedPipeLineMode': 'Integrated'}" - ''' + """ - identityType_map2string = {'0': 'LocalSystem', '1': 'LocalService', '2': 'NetworkService', '3': 'SpecificUser', '4': 'ApplicationPoolIdentity'} - identityType_map2numeric = {'LocalSystem': '0', 'LocalService': '1', 'NetworkService': '2', 'SpecificUser': '3', 'ApplicationPoolIdentity': '4'} + identityType_map2string = { + "0": "LocalSystem", + "1": "LocalService", + "2": "NetworkService", + "3": "SpecificUser", + "4": "ApplicationPoolIdentity", + } + identityType_map2numeric = { + "LocalSystem": "0", + "LocalService": "1", + "NetworkService": "2", + "SpecificUser": "3", + "ApplicationPoolIdentity": "4", + } ps_cmd = list() container_path = r"IIS:\{0}\{1}".format(container, name) if not settings: - log.warning('No settings provided') + log.warning("No settings provided") return False # Treat all values as strings for the purpose of comparing them to existing values. @@ -1317,10 +1446,11 @@ def set_container_setting(name, container, settings): settings[setting] = six.text_type(settings[setting]) current_settings = get_container_setting( - name=name, container=container, settings=settings.keys()) + name=name, container=container, settings=settings.keys() + ) if settings == current_settings: - log.debug('Settings already contain the provided values.') + log.debug("Settings already contain the provided values.") return True for setting in settings: @@ -1332,45 +1462,59 @@ def set_container_setting(name, container, settings): value = "'{0}'".format(settings[setting]) # Map to numeric to support server 2008 - if setting == 'processModel.identityType' and settings[setting] in identityType_map2numeric.keys(): + if ( + setting == "processModel.identityType" + and settings[setting] in identityType_map2numeric.keys() + ): value = identityType_map2numeric[settings[setting]] - ps_cmd.extend(['Set-ItemProperty', - '-Path', "'{0}'".format(container_path), - '-Name', "'{0}'".format(setting), - '-Value', '{0};'.format(value)]) + ps_cmd.extend( + [ + "Set-ItemProperty", + "-Path", + "'{0}'".format(container_path), + "-Name", + "'{0}'".format(setting), + "-Value", + "{0};".format(value), + ] + ) cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to set settings for {0}: {1}'.format(container, name) + if cmd_ret["retcode"] != 0: + msg = "Unable to set settings for {0}: {1}".format(container, name) raise CommandExecutionError(msg) # Get the fields post-change so that we can verify tht all values # were modified successfully. Track the ones that weren't. new_settings = get_container_setting( - name=name, container=container, settings=settings.keys()) + name=name, container=container, settings=settings.keys() + ) failed_settings = dict() for setting in settings: # map identity type from numeric to string for comparing - if setting == 'processModel.identityType' and settings[setting] in identityType_map2string.keys(): + if ( + setting == "processModel.identityType" + and settings[setting] in identityType_map2string.keys() + ): settings[setting] = identityType_map2string[settings[setting]] if six.text_type(settings[setting]) != six.text_type(new_settings[setting]): failed_settings[setting] = settings[setting] if failed_settings: - log.error('Failed to change settings: %s', failed_settings) + log.error("Failed to change settings: %s", failed_settings) return False - log.debug('Settings configured successfully: %s', settings.keys()) + log.debug("Settings configured successfully: %s", settings.keys()) return True def list_apps(site): - ''' + """ Get all configured IIS applications for the specified site. Args: @@ -1383,21 +1527,25 @@ def list_apps(site): .. code-block:: bash salt '*' win_iis.list_apps site - ''' + """ ret = dict() ps_cmd = list() ps_cmd.append("Get-WebApplication -Site '{0}'".format(site)) - ps_cmd.append(r"| Select-Object applicationPool, path, PhysicalPath, preloadEnabled,") + ps_cmd.append( + r"| Select-Object applicationPool, path, PhysicalPath, preloadEnabled," + ) ps_cmd.append(r"@{ Name='name'; Expression={ $_.path.Split('/', 2)[-1] } },") - ps_cmd.append(r"@{ Name='protocols'; Expression={ @( $_.enabledProtocols.Split(',')") + ps_cmd.append( + r"@{ Name='protocols'; Expression={ @( $_.enabledProtocols.Split(',')" + ) ps_cmd.append(r"| Foreach-Object { $_.Trim() } ) } }") cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError('Unable to parse return data as Json.') + raise CommandExecutionError("Unable to parse return data as Json.") for item in items: protocols = list() @@ -1406,26 +1554,28 @@ def list_apps(site): # if there is one protocol, it will be a string, and if there are # multiple, it will be a dict with 'Count' and 'value' as the keys. - if isinstance(item['protocols'], dict): - if 'value' in item['protocols']: - protocols += item['protocols']['value'] + if isinstance(item["protocols"], dict): + if "value" in item["protocols"]: + protocols += item["protocols"]["value"] else: - protocols.append(item['protocols']) + protocols.append(item["protocols"]) - ret[item['name']] = {'apppool': item['applicationPool'], - 'path': item['path'], - 'preload': item['preloadEnabled'], - 'protocols': protocols, - 'sourcepath': item['PhysicalPath']} + ret[item["name"]] = { + "apppool": item["applicationPool"], + "path": item["path"], + "preload": item["preloadEnabled"], + "protocols": protocols, + "sourcepath": item["PhysicalPath"], + } if not ret: - log.warning('No apps found in output: %s', cmd_ret) + log.warning("No apps found in output: %s", cmd_ret) return ret def create_app(name, site, sourcepath, apppool=None): - ''' + """ Create an IIS application. .. note:: @@ -1449,45 +1599,51 @@ def create_app(name, site, sourcepath, apppool=None): .. code-block:: bash salt '*' win_iis.create_app name='app0' site='site0' sourcepath='C:\\site0' apppool='site0' - ''' + """ current_apps = list_apps(site) if name in current_apps: - log.debug('Application already present: %s', name) + log.debug("Application already present: %s", name) return True # The target physical path must exist. if not os.path.isdir(sourcepath): - log.error('Path is not present: %s', sourcepath) + log.error("Path is not present: %s", sourcepath) return False - ps_cmd = ['New-WebApplication', - '-Name', "'{0}'".format(name), - '-Site', "'{0}'".format(site), - '-PhysicalPath', "'{0}'".format(sourcepath)] + ps_cmd = [ + "New-WebApplication", + "-Name", + "'{0}'".format(name), + "-Site", + "'{0}'".format(site), + "-PhysicalPath", + "'{0}'".format(sourcepath), + ] if apppool: - ps_cmd.extend(['-ApplicationPool', "'{0}'".format(apppool)]) + ps_cmd.extend(["-ApplicationPool", "'{0}'".format(apppool)]) cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to create application: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to create application: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) new_apps = list_apps(site) if name in new_apps: - log.debug('Application created successfully: %s', name) + log.debug("Application created successfully: %s", name) return True - log.error('Unable to create application: %s', name) + log.error("Unable to create application: %s", name) return False def remove_app(name, site): - ''' + """ Remove an IIS application. Args: @@ -1502,36 +1658,41 @@ def remove_app(name, site): .. code-block:: bash salt '*' win_iis.remove_app name='app0' site='site0' - ''' + """ current_apps = list_apps(site) if name not in current_apps: - log.debug('Application already absent: %s', name) + log.debug("Application already absent: %s", name) return True - ps_cmd = ['Remove-WebApplication', - '-Name', "'{0}'".format(name), - '-Site', "'{0}'".format(site)] + ps_cmd = [ + "Remove-WebApplication", + "-Name", + "'{0}'".format(name), + "-Site", + "'{0}'".format(site), + ] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to remove application: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to remove application: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) new_apps = list_apps(site) if name not in new_apps: - log.debug('Application removed successfully: %s', name) + log.debug("Application removed successfully: %s", name) return True - log.error('Unable to remove application: %s', name) + log.error("Unable to remove application: %s", name) return False def list_vdirs(site, app=_DEFAULT_APP): - ''' + """ Get all configured IIS virtual directories for the specified site, or for the combination of site and application. @@ -1547,33 +1708,38 @@ def list_vdirs(site, app=_DEFAULT_APP): .. code-block:: bash salt '*' win_iis.list_vdirs site - ''' + """ ret = dict() - ps_cmd = ['Get-WebVirtualDirectory', - '-Site', r"'{0}'".format(site), - '-Application', r"'{0}'".format(app), - '|', "Select-Object PhysicalPath, @{ Name = 'name';", - r"Expression = { $_.path.Split('/')[-1] } }"] + ps_cmd = [ + "Get-WebVirtualDirectory", + "-Site", + r"'{0}'".format(site), + "-Application", + r"'{0}'".format(app), + "|", + "Select-Object PhysicalPath, @{ Name = 'name';", + r"Expression = { $_.path.Split('/')[-1] } }", + ] cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError('Unable to parse return data as Json.') + raise CommandExecutionError("Unable to parse return data as Json.") for item in items: - ret[item['name']] = {'sourcepath': item['physicalPath']} + ret[item["name"]] = {"sourcepath": item["physicalPath"]} if not ret: - log.warning('No vdirs found in output: %s', cmd_ret) + log.warning("No vdirs found in output: %s", cmd_ret) return ret def create_vdir(name, site, sourcepath, app=_DEFAULT_APP): - ''' + """ Create an IIS virtual directory. .. note:: @@ -1597,45 +1763,51 @@ def create_vdir(name, site, sourcepath, app=_DEFAULT_APP): .. code-block:: bash salt '*' win_iis.create_vdir name='vd0' site='site0' sourcepath='C:\\inetpub\\vdirs\\vd0' - ''' + """ current_vdirs = list_vdirs(site, app) if name in current_vdirs: - log.debug('Virtual directory already present: %s', name) + log.debug("Virtual directory already present: %s", name) return True # The target physical path must exist. if not os.path.isdir(sourcepath): - log.error('Path is not present: %s', sourcepath) + log.error("Path is not present: %s", sourcepath) return False - ps_cmd = ['New-WebVirtualDirectory', - '-Name', r"'{0}'".format(name), - '-Site', r"'{0}'".format(site), - '-PhysicalPath', r"'{0}'".format(sourcepath)] + ps_cmd = [ + "New-WebVirtualDirectory", + "-Name", + r"'{0}'".format(name), + "-Site", + r"'{0}'".format(site), + "-PhysicalPath", + r"'{0}'".format(sourcepath), + ] if app != _DEFAULT_APP: - ps_cmd.extend(['-Application', r"'{0}'".format(app)]) + ps_cmd.extend(["-Application", r"'{0}'".format(app)]) cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to create virtual directory: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to create virtual directory: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) new_vdirs = list_vdirs(site, app) if name in new_vdirs: - log.debug('Virtual directory created successfully: %s', name) + log.debug("Virtual directory created successfully: %s", name) return True - log.error('Unable to create virtual directory: %s', name) + log.error("Unable to create virtual directory: %s", name) return False def remove_vdir(name, site, app=_DEFAULT_APP): - ''' + """ Remove an IIS virtual directory. Args: @@ -1651,44 +1823,43 @@ def remove_vdir(name, site, app=_DEFAULT_APP): .. code-block:: bash salt '*' win_iis.remove_vdir name='vdir0' site='site0' - ''' + """ current_vdirs = list_vdirs(site, app) - app_path = os.path.join(*app.rstrip('/').split('/')) + app_path = os.path.join(*app.rstrip("/").split("/")) if app_path: - app_path = '{0}\\'.format(app_path) - vdir_path = r'IIS:\Sites\{0}\{1}{2}'.format(site, app_path, name) + app_path = "{0}\\".format(app_path) + vdir_path = r"IIS:\Sites\{0}\{1}{2}".format(site, app_path, name) if name not in current_vdirs: - log.debug('Virtual directory already absent: %s', name) + log.debug("Virtual directory already absent: %s", name) return True # We use Remove-Item here instead of Remove-WebVirtualDirectory, since the # latter has a bug that causes it to always prompt for user input. - ps_cmd = ['Remove-Item', - '-Path', r"'{0}'".format(vdir_path), - '-Recurse'] + ps_cmd = ["Remove-Item", "-Path", r"'{0}'".format(vdir_path), "-Recurse"] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to remove virtual directory: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to remove virtual directory: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) new_vdirs = list_vdirs(site, app) if name not in new_vdirs: - log.debug('Virtual directory removed successfully: %s', name) + log.debug("Virtual directory removed successfully: %s", name) return True - log.error('Unable to remove virtual directory: %s', name) + log.error("Unable to remove virtual directory: %s", name) return False def list_backups(): - r''' + r""" List the IIS Configuration Backups on the System. .. versionadded:: 2017.7.0 @@ -1705,35 +1876,37 @@ def list_backups(): .. code-block:: bash salt '*' win_iis.list_backups - ''' + """ ret = dict() - ps_cmd = ['Get-WebConfigurationBackup', - '|', - 'Select Name, CreationDate,', - '@{N="FormattedDate"; E={$_.CreationDate.ToString("G")}}', ] + ps_cmd = [ + "Get-WebConfigurationBackup", + "|", + "Select Name, CreationDate,", + '@{N="FormattedDate"; E={$_.CreationDate.ToString("G")}}', + ] cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError('Unable to parse return data as Json.') + raise CommandExecutionError("Unable to parse return data as Json.") for item in items: - if item['FormattedDate']: - ret[item['Name']] = item['FormattedDate'] + if item["FormattedDate"]: + ret[item["Name"]] = item["FormattedDate"] else: - ret[item['Name']] = item['CreationDate'] + ret[item["Name"]] = item["CreationDate"] if not ret: - log.warning('No backups found in output: %s', cmd_ret) + log.warning("No backups found in output: %s", cmd_ret) return ret def create_backup(name): - r''' + r""" Backup an IIS Configuration on the System. .. versionadded:: 2017.7.0 @@ -1753,25 +1926,25 @@ def create_backup(name): .. code-block:: bash salt '*' win_iis.create_backup good_config_20170209 - ''' + """ if name in list_backups(): - raise CommandExecutionError('Backup already present: {0}'.format(name)) + raise CommandExecutionError("Backup already present: {0}".format(name)) - ps_cmd = ['Backup-WebConfiguration', - '-Name', "'{0}'".format(name)] + ps_cmd = ["Backup-WebConfiguration", "-Name", "'{0}'".format(name)] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to backup web configuration: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to backup web configuration: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) return name in list_backups() def remove_backup(name): - ''' + """ Remove an IIS Configuration backup from the System. .. versionadded:: 2017.7.0 @@ -1787,26 +1960,26 @@ def remove_backup(name): .. code-block:: bash salt '*' win_iis.remove_backup backup_20170209 - ''' + """ if name not in list_backups(): - log.debug('Backup already removed: %s', name) + log.debug("Backup already removed: %s", name) return True - ps_cmd = ['Remove-WebConfigurationBackup', - '-Name', "'{0}'".format(name)] + ps_cmd = ["Remove-WebConfigurationBackup", "-Name", "'{0}'".format(name)] cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to remove web configuration: {0}\nError: {1}' \ - ''.format(name, cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + msg = "Unable to remove web configuration: {0}\nError: {1}" "".format( + name, cmd_ret["stderr"] + ) raise CommandExecutionError(msg) return name not in list_backups() def list_worker_processes(apppool): - ''' + """ Returns a list of worker processes that correspond to the passed application pool. @@ -1823,29 +1996,28 @@ def list_worker_processes(apppool): .. code-block:: bash salt '*' win_iis.list_worker_processes 'My App Pool' - ''' - ps_cmd = ['Get-ChildItem', - r"'IIS:\AppPools\{0}\WorkerProcesses'".format(apppool)] + """ + ps_cmd = ["Get-ChildItem", r"'IIS:\AppPools\{0}\WorkerProcesses'".format(apppool)] cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError('Unable to parse return data as Json.') + raise CommandExecutionError("Unable to parse return data as Json.") ret = dict() for item in items: - ret[item['processId']] = item['appPoolName'] + ret[item["processId"]] = item["appPoolName"] if not ret: - log.warning('No backups found in output: %s', cmd_ret) + log.warning("No backups found in output: %s", cmd_ret) return ret def get_webapp_settings(name, site, settings): - r''' + r""" .. versionadded:: 2017.7.0 Get the value of the setting for the IIS web application. @@ -1867,61 +2039,76 @@ def get_webapp_settings(name, site, settings): salt '*' win_iis.get_webapp_settings name='app0' site='Default Web Site' settings="['physicalPath','applicationPool']" - ''' + """ ret = dict() pscmd = list() - availableSettings = ('physicalPath', 'applicationPool', 'userName', 'password') + availableSettings = ("physicalPath", "applicationPool", "userName", "password") if not settings: - log.warning('No settings provided') + log.warning("No settings provided") return ret - pscmd.append(r'$Settings = @{};') + pscmd.append(r"$Settings = @{};") # Verify setting is ine predefined settings and append relevant query command per setting key for setting in settings: if setting in availableSettings: if setting == "userName" or setting == "password": - pscmd.append(" $Property = Get-WebConfigurationProperty -Filter \"system.applicationHost/sites/site[@name='{0}']/application[@path='/{1}']/virtualDirectory[@path='/']\"".format(site, name)) - pscmd.append(r' -Name "{0}" -ErrorAction Stop | select Value;'.format(setting)) - pscmd.append(r' $Property = $Property | Select-Object -ExpandProperty Value;') + pscmd.append( + " $Property = Get-WebConfigurationProperty -Filter \"system.applicationHost/sites/site[@name='{0}']/application[@path='/{1}']/virtualDirectory[@path='/']\"".format( + site, name + ) + ) + pscmd.append( + r' -Name "{0}" -ErrorAction Stop | select Value;'.format(setting) + ) + pscmd.append( + r" $Property = $Property | Select-Object -ExpandProperty Value;" + ) pscmd.append(r" $Settings['{0}'] = [String] $Property;".format(setting)) - pscmd.append(r' $Property = $Null;') + pscmd.append(r" $Property = $Null;") if setting == "physicalPath" or setting == "applicationPool": - pscmd.append(r" $Property = (get-webapplication {0}).{1};".format(name, setting)) + pscmd.append( + r" $Property = (get-webapplication {0}).{1};".format(name, setting) + ) pscmd.append(r" $Settings['{0}'] = [String] $Property;".format(setting)) - pscmd.append(r' $Property = $Null;') + pscmd.append(r" $Property = $Null;") else: - availSetStr = ', '.join(availableSettings) - message = 'Unexpected setting:' + setting + '. Available settings are: ' + availSetStr + availSetStr = ", ".join(availableSettings) + message = ( + "Unexpected setting:" + + setting + + ". Available settings are: " + + availSetStr + ) raise SaltInvocationError(message) - pscmd.append(' $Settings') + pscmd.append(" $Settings") # Run commands and return data as json cmd_ret = _srvmgr(cmd=six.text_type().join(pscmd), return_json=True) # Update dict var to return data try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) if isinstance(items, list): ret.update(items[0]) else: ret.update(items) except ValueError: - log.error('Unable to parse return data as Json.') + log.error("Unable to parse return data as Json.") if None in six.viewvalues(ret): - message = 'Some values are empty - please validate site and web application names. Some commands are case sensitive' + message = "Some values are empty - please validate site and web application names. Some commands are case sensitive" raise SaltInvocationError(message) return ret def set_webapp_settings(name, site, settings): - r''' + r""" .. versionadded:: 2017.7.0 Configure an IIS application. @@ -1945,11 +2132,11 @@ def set_webapp_settings(name, site, settings): .. code-block:: bash salt '*' win_iis.set_webapp_settings name='app0' site='site0' settings="{'physicalPath': 'C:\site0', 'apppool': 'site0'}" - ''' + """ pscmd = list() current_apps = list_apps(site) current_sites = list_sites() - availableSettings = ('physicalPath', 'applicationPool', 'userName', 'password') + availableSettings = ("physicalPath", "applicationPool", "userName", "password") # Validate params if name not in current_apps: @@ -1969,7 +2156,7 @@ def set_webapp_settings(name, site, settings): if setting in availableSettings: settings[setting] = six.text_type(settings[setting]) else: - availSetStr = ', '.join(availableSettings) + availSetStr = ", ".join(availableSettings) log.error("Unexpected setting: %s ", setting) log.error("Available settings: %s", availSetStr) msg = "Unexpected setting:" + setting + " Available settings:" + availSetStr @@ -1977,10 +2164,11 @@ def set_webapp_settings(name, site, settings): # Check if settings already configured current_settings = get_webapp_settings( - name=name, site=site, settings=settings.keys()) + name=name, site=site, settings=settings.keys() + ) if settings == current_settings: - log.warning('Settings already contain the provided values.') + log.warning("Settings already contain the provided values.") return True for setting in settings: @@ -1993,27 +2181,34 @@ def set_webapp_settings(name, site, settings): # Append relevant update command per setting key if setting == "userName" or setting == "password": - pscmd.append(" Set-WebConfigurationProperty -Filter \"system.applicationHost/sites/site[@name='{0}']/application[@path='/{1}']/virtualDirectory[@path='/']\"".format(site, name)) - pscmd.append(" -Name \"{0}\" -Value {1};".format(setting, value)) + pscmd.append( + " Set-WebConfigurationProperty -Filter \"system.applicationHost/sites/site[@name='{0}']/application[@path='/{1}']/virtualDirectory[@path='/']\"".format( + site, name + ) + ) + pscmd.append(' -Name "{0}" -Value {1};'.format(setting, value)) if setting == "physicalPath" or setting == "applicationPool": - pscmd.append(r' Set-ItemProperty "IIS:\Sites\{0}\{1}" -Name {2} -Value {3};'.format(site, name, setting, value)) + pscmd.append( + r' Set-ItemProperty "IIS:\Sites\{0}\{1}" -Name {2} -Value {3};'.format( + site, name, setting, value + ) + ) if setting == "physicalPath": if not os.path.isdir(settings[setting]): - msg = 'Path is not present: ' + settings[setting] + msg = "Path is not present: " + settings[setting] raise SaltInvocationError(msg) # Run commands cmd_ret = _srvmgr(pscmd) # Verify commands completed successfully - if cmd_ret['retcode'] != 0: - msg = 'Unable to set settings for web application {0}'.format(name) + if cmd_ret["retcode"] != 0: + msg = "Unable to set settings for web application {0}".format(name) raise SaltInvocationError(msg) # verify changes - new_settings = get_webapp_settings( - name=name, site=site, settings=settings.keys()) + new_settings = get_webapp_settings(name=name, site=site, settings=settings.keys()) failed_settings = dict() for setting in settings: @@ -2021,15 +2216,15 @@ def set_webapp_settings(name, site, settings): failed_settings[setting] = settings[setting] if failed_settings: - log.error('Failed to change settings: %s', failed_settings) + log.error("Failed to change settings: %s", failed_settings) return False - log.debug('Settings configured successfully: {0}'.format(settings.keys())) + log.debug("Settings configured successfully: {0}".format(settings.keys())) return True def get_webconfiguration_settings(name, settings): - r''' + r""" Get the webconfiguration settings for the IIS PSPath. Args: @@ -2044,68 +2239,92 @@ def get_webconfiguration_settings(name, settings): .. code-block:: bash salt '*' win_iis.get_webconfiguration_settings name='IIS:\' settings="[{'name': 'enabled', 'filter': 'system.webServer/security/authentication/anonymousAuthentication'}]" - ''' + """ ret = {} - ps_cmd = [r'$Settings = New-Object System.Collections.ArrayList;'] + ps_cmd = [r"$Settings = New-Object System.Collections.ArrayList;"] ps_cmd_validate = [] settings = _prepare_settings(name, settings) if not settings: - log.warning('No settings provided') + log.warning("No settings provided") return ret for setting in settings: # Build the commands to verify that the property names are valid. - ps_cmd_validate.extend(['Get-WebConfigurationProperty', - '-PSPath', "'{0}'".format(name), - '-Filter', "'{0}'".format(setting['filter']), - '-Name', "'{0}'".format(setting['name']), - '-ErrorAction', 'Stop', - '|', 'Out-Null;']) + ps_cmd_validate.extend( + [ + "Get-WebConfigurationProperty", + "-PSPath", + "'{0}'".format(name), + "-Filter", + "'{0}'".format(setting["filter"]), + "-Name", + "'{0}'".format(setting["name"]), + "-ErrorAction", + "Stop", + "|", + "Out-Null;", + ] + ) # Some ItemProperties are Strings and others are ConfigurationAttributes. # Since the former doesn't have a Value property, we need to account # for this. - ps_cmd.append("$Property = Get-WebConfigurationProperty -PSPath '{0}'".format(name)) - ps_cmd.append("-Name '{0}' -Filter '{1}' -ErrorAction Stop;".format(setting['name'], setting['filter'])) - if setting['name'].split('.')[-1] == 'Collection': - if 'value' in setting: - ps_cmd.append("$Property = $Property | select -Property {0} ;" - .format(",".join(list(setting['value'][0].keys())))) - ps_cmd.append("$Settings.add(@{{filter='{0}';name='{1}';value=[System.Collections.ArrayList] @($Property)}})| Out-Null;" - .format(setting['filter'], setting['name'])) + ps_cmd.append( + "$Property = Get-WebConfigurationProperty -PSPath '{0}'".format(name) + ) + ps_cmd.append( + "-Name '{0}' -Filter '{1}' -ErrorAction Stop;".format( + setting["name"], setting["filter"] + ) + ) + if setting["name"].split(".")[-1] == "Collection": + if "value" in setting: + ps_cmd.append( + "$Property = $Property | select -Property {0} ;".format( + ",".join(list(setting["value"][0].keys())) + ) + ) + ps_cmd.append( + "$Settings.add(@{{filter='{0}';name='{1}';value=[System.Collections.ArrayList] @($Property)}})| Out-Null;".format( + setting["filter"], setting["name"] + ) + ) else: - ps_cmd.append(r'if (([String]::IsNullOrEmpty($Property) -eq $False) -and') + ps_cmd.append(r"if (([String]::IsNullOrEmpty($Property) -eq $False) -and") ps_cmd.append(r"($Property.GetType()).Name -eq 'ConfigurationAttribute') {") - ps_cmd.append(r'$Property = $Property | Select-Object') - ps_cmd.append(r'-ExpandProperty Value };') - ps_cmd.append("$Settings.add(@{{filter='{0}';name='{1}';value=[String] $Property}})| Out-Null;" - .format(setting['filter'], setting['name'])) - ps_cmd.append(r'$Property = $Null;') + ps_cmd.append(r"$Property = $Property | Select-Object") + ps_cmd.append(r"-ExpandProperty Value };") + ps_cmd.append( + "$Settings.add(@{{filter='{0}';name='{1}';value=[String] $Property}})| Out-Null;".format( + setting["filter"], setting["name"] + ) + ) + ps_cmd.append(r"$Property = $Null;") # Validate the setting names that were passed in. cmd_ret = _srvmgr(cmd=ps_cmd_validate, return_json=True) - if cmd_ret['retcode'] != 0: - message = 'One or more invalid property names were specified for the provided container.' + if cmd_ret["retcode"] != 0: + message = "One or more invalid property names were specified for the provided container." raise SaltInvocationError(message) - ps_cmd.append('$Settings') + ps_cmd.append("$Settings") cmd_ret = _srvmgr(cmd=ps_cmd, return_json=True) try: - ret = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + ret = salt.utils.json.loads(cmd_ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError('Unable to parse return data as Json.') + raise CommandExecutionError("Unable to parse return data as Json.") return ret def set_webconfiguration_settings(name, settings): - r''' + r""" Set the value of the setting for an IIS container. Args: @@ -2120,73 +2339,88 @@ def set_webconfiguration_settings(name, settings): .. code-block:: bash salt '*' win_iis.set_webconfiguration_settings name='IIS:\' settings="[{'name': 'enabled', 'filter': 'system.webServer/security/authentication/anonymousAuthentication', 'value': False}]" - ''' + """ ps_cmd = [] settings = _prepare_settings(name, settings) if not settings: - log.warning('No settings provided') + log.warning("No settings provided") return False # Treat all values as strings for the purpose of comparing them to existing values. for idx, setting in enumerate(settings): - if setting['name'].split('.')[-1] != 'Collection': - settings[idx]['value'] = six.text_type(setting['value']) + if setting["name"].split(".")[-1] != "Collection": + settings[idx]["value"] = six.text_type(setting["value"]) - current_settings = get_webconfiguration_settings( - name=name, settings=settings) + current_settings = get_webconfiguration_settings(name=name, settings=settings) if settings == current_settings: - log.debug('Settings already contain the provided values.') + log.debug("Settings already contain the provided values.") return True for setting in settings: # If the value is numeric, don't treat it as a string in PowerShell. - if setting['name'].split('.')[-1] != 'Collection': + if setting["name"].split(".")[-1] != "Collection": try: - complex(setting['value']) - value = setting['value'] + complex(setting["value"]) + value = setting["value"] except ValueError: - value = "'{0}'".format(setting['value']) + value = "'{0}'".format(setting["value"]) else: configelement_list = [] - for value_item in setting['value']: + for value_item in setting["value"]: configelement_construct = [] for key, value in value_item.items(): configelement_construct.append("{0}='{1}'".format(key, value)) - configelement_list.append('@{' + ';'.join(configelement_construct) + '}') - value = ','.join(configelement_list) + configelement_list.append( + "@{" + ";".join(configelement_construct) + "}" + ) + value = ",".join(configelement_list) - ps_cmd.extend(['Set-WebConfigurationProperty', - '-PSPath', "'{0}'".format(name), - '-Filter', "'{0}'".format(setting['filter']), - '-Name', "'{0}'".format(setting['name']), - '-Value', '{0};'.format(value)]) + ps_cmd.extend( + [ + "Set-WebConfigurationProperty", + "-PSPath", + "'{0}'".format(name), + "-Filter", + "'{0}'".format(setting["filter"]), + "-Name", + "'{0}'".format(setting["name"]), + "-Value", + "{0};".format(value), + ] + ) cmd_ret = _srvmgr(ps_cmd) - if cmd_ret['retcode'] != 0: - msg = 'Unable to set settings for {0}'.format(name) + if cmd_ret["retcode"] != 0: + msg = "Unable to set settings for {0}".format(name) raise CommandExecutionError(msg) # Get the fields post-change so that we can verify tht all values # were modified successfully. Track the ones that weren't. - new_settings = get_webconfiguration_settings( - name=name, settings=settings) + new_settings = get_webconfiguration_settings(name=name, settings=settings) failed_settings = [] for idx, setting in enumerate(settings): - is_collection = setting['name'].split('.')[-1] == 'Collection' + is_collection = setting["name"].split(".")[-1] == "Collection" - if ((not is_collection and six.text_type(setting['value']) != six.text_type(new_settings[idx]['value'])) - or (is_collection and list(map(dict, setting['value'])) != list(map(dict, new_settings[idx]['value'])))): + if ( + not is_collection + and six.text_type(setting["value"]) + != six.text_type(new_settings[idx]["value"]) + ) or ( + is_collection + and list(map(dict, setting["value"])) + != list(map(dict, new_settings[idx]["value"])) + ): failed_settings.append(setting) if failed_settings: - log.error('Failed to change settings: %s', failed_settings) + log.error("Failed to change settings: %s", failed_settings) return False - log.debug('Settings configured successfully: %s', settings) + log.debug("Settings configured successfully: %s", settings) return True diff --git a/salt/modules/win_ip.py b/salt/modules/win_ip.py index e69f44211e3..641d7375b3f 100644 --- a/salt/modules/win_ip.py +++ b/salt/modules/win_ip.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" The networking module for Windows based systems -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -12,34 +12,31 @@ import time import salt.utils.network import salt.utils.platform import salt.utils.validate.net -from salt.exceptions import ( - CommandExecutionError, - SaltInvocationError -) +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext.six.moves import range # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'ip' +__virtualname__ = "ip" def __virtual__(): - ''' + """ Confine this module to Windows systems - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ return (False, "Module win_ip: module only works on Windows systems") def _interface_configs(): - ''' + """ Return all interface configs - ''' - cmd = ['netsh', 'interface', 'ip', 'show', 'config'] - lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + """ + cmd = ["netsh", "interface", "ip", "show", "config"] + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() ret = {} current_iface = None current_ip_list = None @@ -52,37 +49,37 @@ def _interface_configs(): current_ip_list = None continue - if 'Configuration for interface' in line: + if "Configuration for interface" in line: _, iface = line.rstrip('"').split('"', 1) # get iface name current_iface = {} ret[iface] = current_iface continue - if ':' not in line: + if ":" not in line: if current_ip_list: current_ip_list.append(line) else: log.warning('Cannot parse "%s"', line) continue - key, val = line.split(':', 1) + key, val = line.split(":", 1) key = key.strip() val = val.strip() lkey = key.lower() - if ('dns servers' in lkey) or ('wins servers' in lkey): + if ("dns servers" in lkey) or ("wins servers" in lkey): current_ip_list = [] current_iface[key] = current_ip_list current_ip_list.append(val) - elif 'ip address' in lkey: - current_iface.setdefault('ip_addrs', []).append({key: val}) + elif "ip address" in lkey: + current_iface.setdefault("ip_addrs", []).append({key: val}) - elif 'subnet prefix' in lkey: - subnet, _, netmask = val.split(' ', 2) - last_ip = current_iface['ip_addrs'][-1] - last_ip['Subnet'] = subnet.strip() - last_ip['Netmask'] = netmask.lstrip().rstrip(')') + elif "subnet prefix" in lkey: + subnet, _, netmask = val.split(" ", 2) + last_ip = current_iface["ip_addrs"][-1] + last_ip["Subnet"] = subnet.strip() + last_ip["Netmask"] = netmask.lstrip().rstrip(")") else: current_iface[key] = val @@ -91,7 +88,7 @@ def _interface_configs(): def raw_interface_configs(): - ''' + """ Return raw configs for all interfaces CLI Example: @@ -99,13 +96,13 @@ def raw_interface_configs(): .. code-block:: bash salt -G 'os_family:Windows' ip.raw_interface_configs - ''' - cmd = ['netsh', 'interface', 'ip', 'show', 'config'] - return __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = ["netsh", "interface", "ip", "show", "config"] + return __salt__["cmd.run"](cmd, python_shell=False) def get_all_interfaces(): - ''' + """ Return configs for all interfaces CLI Example: @@ -113,12 +110,12 @@ def get_all_interfaces(): .. code-block:: bash salt -G 'os_family:Windows' ip.get_all_interfaces - ''' + """ return _interface_configs() def get_interface(iface): - ''' + """ Return the configuration of a network interface CLI Example: @@ -126,12 +123,12 @@ def get_interface(iface): .. code-block:: bash salt -G 'os_family:Windows' ip.get_interface 'Local Area Connection' - ''' + """ return _interface_configs().get(iface, {}) def is_enabled(iface): - ''' + """ Returns ``True`` if interface is enabled, otherwise ``False`` CLI Example: @@ -139,22 +136,20 @@ def is_enabled(iface): .. code-block:: bash salt -G 'os_family:Windows' ip.is_enabled 'Local Area Connection #2' - ''' - cmd = ['netsh', 'interface', 'show', 'interface', 'name={0}'.format(iface)] + """ + cmd = ["netsh", "interface", "show", "interface", "name={0}".format(iface)] iface_found = False - for line in __salt__['cmd.run'](cmd, python_shell=False).splitlines(): - if 'Connect state:' in line: + for line in __salt__["cmd.run"](cmd, python_shell=False).splitlines(): + if "Connect state:" in line: iface_found = True - return line.split()[-1] == 'Connected' + return line.split()[-1] == "Connected" if not iface_found: - raise CommandExecutionError( - 'Interface \'{0}\' not found'.format(iface) - ) + raise CommandExecutionError("Interface '{0}' not found".format(iface)) return False def is_disabled(iface): - ''' + """ Returns ``True`` if interface is disabled, otherwise ``False`` CLI Example: @@ -162,12 +157,12 @@ def is_disabled(iface): .. code-block:: bash salt -G 'os_family:Windows' ip.is_disabled 'Local Area Connection #2' - ''' + """ return not is_enabled(iface) def enable(iface): - ''' + """ Enable an interface CLI Example: @@ -175,18 +170,23 @@ def enable(iface): .. code-block:: bash salt -G 'os_family:Windows' ip.enable 'Local Area Connection #2' - ''' + """ if is_enabled(iface): return True - cmd = ['netsh', 'interface', 'set', 'interface', - 'name={0}'.format(iface), - 'admin=ENABLED'] - __salt__['cmd.run'](cmd, python_shell=False) + cmd = [ + "netsh", + "interface", + "set", + "interface", + "name={0}".format(iface), + "admin=ENABLED", + ] + __salt__["cmd.run"](cmd, python_shell=False) return is_enabled(iface) def disable(iface): - ''' + """ Disable an interface CLI Example: @@ -194,18 +194,23 @@ def disable(iface): .. code-block:: bash salt -G 'os_family:Windows' ip.disable 'Local Area Connection #2' - ''' + """ if is_disabled(iface): return True - cmd = ['netsh', 'interface', 'set', 'interface', - 'name={0}'.format(iface), - 'admin=DISABLED'] - __salt__['cmd.run'](cmd, python_shell=False) + cmd = [ + "netsh", + "interface", + "set", + "interface", + "name={0}".format(iface), + "admin=DISABLED", + ] + __salt__["cmd.run"](cmd, python_shell=False) return is_disabled(iface) def get_subnet_length(mask): - ''' + """ Convenience function to convert the netmask to the CIDR subnet length CLI Example: @@ -213,16 +218,14 @@ def get_subnet_length(mask): .. code-block:: bash salt -G 'os_family:Windows' ip.get_subnet_length 255.255.255.0 - ''' + """ if not salt.utils.validate.net.netmask(mask): - raise SaltInvocationError( - '\'{0}\' is not a valid netmask'.format(mask) - ) + raise SaltInvocationError("'{0}' is not a valid netmask".format(mask)) return salt.utils.network.get_net_size(mask) def set_static_ip(iface, addr, gateway=None, append=False): - ''' + """ Set static IP configuration on a Windows NIC iface @@ -247,65 +250,62 @@ def set_static_ip(iface, addr, gateway=None, append=False): salt -G 'os_family:Windows' ip.set_static_ip 'Local Area Connection' 10.1.2.3/24 gateway=10.1.2.1 salt -G 'os_family:Windows' ip.set_static_ip 'Local Area Connection' 10.1.2.4/24 append=True - ''' + """ + def _find_addr(iface, addr, timeout=1): - ip, cidr = addr.rsplit('/', 1) + ip, cidr = addr.rsplit("/", 1) netmask = salt.utils.network.cidr_to_ipv4_netmask(cidr) for idx in range(timeout): - for addrinfo in get_interface(iface).get('ip_addrs', []): - if addrinfo['IP Address'] == ip \ - and addrinfo['Netmask'] == netmask: + for addrinfo in get_interface(iface).get("ip_addrs", []): + if addrinfo["IP Address"] == ip and addrinfo["Netmask"] == netmask: return addrinfo time.sleep(1) return {} if not salt.utils.validate.net.ipv4_addr(addr): - raise SaltInvocationError('Invalid address \'{0}\''.format(addr)) + raise SaltInvocationError("Invalid address '{0}'".format(addr)) if gateway and not salt.utils.validate.net.ipv4_addr(addr): - raise SaltInvocationError( - 'Invalid default gateway \'{0}\''.format(gateway) - ) + raise SaltInvocationError("Invalid default gateway '{0}'".format(gateway)) - if '/' not in addr: - addr += '/32' + if "/" not in addr: + addr += "/32" if append and _find_addr(iface, addr): raise CommandExecutionError( - 'Address \'{0}\' already exists on interface ' - '\'{1}\''.format(addr, iface) + "Address '{0}' already exists on interface " "'{1}'".format(addr, iface) ) - cmd = ['netsh', 'interface', 'ip'] + cmd = ["netsh", "interface", "ip"] if append: - cmd.append('add') + cmd.append("add") else: - cmd.append('set') - cmd.extend(['address', 'name={0}'.format(iface)]) + cmd.append("set") + cmd.extend(["address", "name={0}".format(iface)]) if not append: - cmd.append('source=static') - cmd.append('address={0}'.format(addr)) + cmd.append("source=static") + cmd.append("address={0}".format(addr)) if gateway: - cmd.append('gateway={0}'.format(gateway)) + cmd.append("gateway={0}".format(gateway)) - result = __salt__['cmd.run_all'](cmd, python_shell=False) - if result['retcode'] != 0: + result = __salt__["cmd.run_all"](cmd, python_shell=False) + if result["retcode"] != 0: raise CommandExecutionError( - 'Unable to set IP address: {0}'.format(result['stderr']) + "Unable to set IP address: {0}".format(result["stderr"]) ) new_addr = _find_addr(iface, addr, timeout=10) if not new_addr: return {} - ret = {'Address Info': new_addr} + ret = {"Address Info": new_addr} if gateway: - ret['Default Gateway'] = gateway + ret["Default Gateway"] = gateway return ret def set_dhcp_ip(iface): - ''' + """ Set Windows NIC to get IP from DHCP CLI Example: @@ -313,14 +313,14 @@ def set_dhcp_ip(iface): .. code-block:: bash salt -G 'os_family:Windows' ip.set_dhcp_ip 'Local Area Connection' - ''' - cmd = ['netsh', 'interface', 'ip', 'set', 'address', iface, 'dhcp'] - __salt__['cmd.run'](cmd, python_shell=False) - return {'Interface': iface, 'DHCP enabled': 'Yes'} + """ + cmd = ["netsh", "interface", "ip", "set", "address", iface, "dhcp"] + __salt__["cmd.run"](cmd, python_shell=False) + return {"Interface": iface, "DHCP enabled": "Yes"} def set_static_dns(iface, *addrs): - ''' + """ Set static DNS configuration on a Windows NIC Args: @@ -341,40 +341,58 @@ def set_static_dns(iface, *addrs): salt -G 'os_family:Windows' ip.set_static_dns 'Local Area Connection' '192.168.1.1' salt -G 'os_family:Windows' ip.set_static_dns 'Local Area Connection' '192.168.1.252' '192.168.1.253' - ''' - if addrs is () or str(addrs[0]).lower() == 'none': - return {'Interface': iface, 'DNS Server': 'No Changes'} + """ + if addrs is () or str(addrs[0]).lower() == "none": + return {"Interface": iface, "DNS Server": "No Changes"} # Clear the list of DNS servers if [] is passed - if str(addrs[0]).lower() == '[]': - log.debug('Clearing list of DNS servers') - cmd = ['netsh', 'interface', 'ip', 'set', 'dns', - 'name={0}'.format(iface), - 'source=static', - 'address=none'] - __salt__['cmd.run'](cmd, python_shell=False) - return {'Interface': iface, 'DNS Server': []} + if str(addrs[0]).lower() == "[]": + log.debug("Clearing list of DNS servers") + cmd = [ + "netsh", + "interface", + "ip", + "set", + "dns", + "name={0}".format(iface), + "source=static", + "address=none", + ] + __salt__["cmd.run"](cmd, python_shell=False) + return {"Interface": iface, "DNS Server": []} addr_index = 1 for addr in addrs: if addr_index == 1: - cmd = ['netsh', 'interface', 'ip', 'set', 'dns', - 'name={0}'.format(iface), - 'source=static', - 'address={0}'.format(addr), - 'register=primary'] - __salt__['cmd.run'](cmd, python_shell=False) + cmd = [ + "netsh", + "interface", + "ip", + "set", + "dns", + "name={0}".format(iface), + "source=static", + "address={0}".format(addr), + "register=primary", + ] + __salt__["cmd.run"](cmd, python_shell=False) addr_index = addr_index + 1 else: - cmd = ['netsh', 'interface', 'ip', 'add', 'dns', - 'name={0}'.format(iface), - 'address={0}'.format(addr), - 'index={0}'.format(addr_index)] - __salt__['cmd.run'](cmd, python_shell=False) + cmd = [ + "netsh", + "interface", + "ip", + "add", + "dns", + "name={0}".format(iface), + "address={0}".format(addr), + "index={0}".format(addr_index), + ] + __salt__["cmd.run"](cmd, python_shell=False) addr_index = addr_index + 1 - return {'Interface': iface, 'DNS Server': addrs} + return {"Interface": iface, "DNS Server": addrs} def set_dhcp_dns(iface): - ''' + """ Set DNS source to DHCP on Windows CLI Example: @@ -382,14 +400,14 @@ def set_dhcp_dns(iface): .. code-block:: bash salt -G 'os_family:Windows' ip.set_dhcp_dns 'Local Area Connection' - ''' - cmd = ['netsh', 'interface', 'ip', 'set', 'dns', iface, 'dhcp'] - __salt__['cmd.run'](cmd, python_shell=False) - return {'Interface': iface, 'DNS Server': 'DHCP'} + """ + cmd = ["netsh", "interface", "ip", "set", "dns", iface, "dhcp"] + __salt__["cmd.run"](cmd, python_shell=False) + return {"Interface": iface, "DNS Server": "DHCP"} def set_dhcp_all(iface): - ''' + """ Set both IP Address and DNS to DHCP CLI Example: @@ -397,14 +415,14 @@ def set_dhcp_all(iface): .. code-block:: bash salt -G 'os_family:Windows' ip.set_dhcp_all 'Local Area Connection' - ''' + """ set_dhcp_ip(iface) set_dhcp_dns(iface) - return {'Interface': iface, 'DNS Server': 'DHCP', 'DHCP enabled': 'Yes'} + return {"Interface": iface, "DNS Server": "DHCP", "DHCP enabled": "Yes"} def get_default_gateway(): - ''' + """ Set DNS source to DHCP on Windows CLI Example: @@ -412,14 +430,16 @@ def get_default_gateway(): .. code-block:: bash salt -G 'os_family:Windows' ip.get_default_gateway - ''' + """ try: - return next(iter( - x.split()[-1] for x in __salt__['cmd.run']( - ['netsh', 'interface', 'ip', 'show', 'config'], - python_shell=False - ).splitlines() - if 'Default Gateway:' in x - )) + return next( + iter( + x.split()[-1] + for x in __salt__["cmd.run"]( + ["netsh", "interface", "ip", "show", "config"], python_shell=False + ).splitlines() + if "Default Gateway:" in x + ) + ) except StopIteration: - raise CommandExecutionError('Unable to find default gateway') + raise CommandExecutionError("Unable to find default gateway") diff --git a/salt/modules/win_lgpo.py b/salt/modules/win_lgpo.py index 045331a3488..d3933189a8f 100644 --- a/salt/modules/win_lgpo.py +++ b/salt/modules/win_lgpo.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Local Policy on Windows This module allows configuring local group policy (i.e. ``gpedit.msc``) on a @@ -36,25 +36,23 @@ Current known limitations - uuid - struct - salt.utils.win_reg -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import csv -import glob -import zlib -import io -import os -import logging -import re -import locale import ctypes +import glob +import io +import locale +import logging +import os +import re import tempfile import time import uuid +import zlib -# Import Salt libs -from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.serializers.configparser import deserialize import salt.utils.dictupdate as dictupdate import salt.utils.files import salt.utils.path @@ -62,14 +60,18 @@ import salt.utils.platform import salt.utils.stringutils import salt.utils.win_lgpo_netsh +# Import Salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError + # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range +from salt.serializers.configparser import deserialize log = logging.getLogger(__name__) -__virtualname__ = 'lgpo' -__func_alias__ = {'set_': 'set'} +__virtualname__ = "lgpo" +__func_alias__ = {"set_": "set"} UUID = uuid.uuid4().hex adm_policy_name_map = {True: {}, False: {}} @@ -105,6 +107,7 @@ try: import struct from lxml import etree from salt.utils.win_reg import Registry + HAS_WINDOWS_MODULES = True TRUE_VALUE_XPATH = etree.XPath('.//*[local-name() = "trueValue"]') FALSE_VALUE_XPATH = etree.XPath('.//*[local-name() = "falseValue"]') @@ -116,16 +119,30 @@ try: VALUE_XPATH = etree.XPath('.//*[local-name() = "value"]') TRUE_LIST_XPATH = etree.XPath('.//*[local-name() = "trueList"]') FALSE_LIST_XPATH = etree.XPath('.//*[local-name() = "falseList"]') - REGKEY_XPATH = etree.XPath('//*[@key = $keyvalue]') + REGKEY_XPATH = etree.XPath("//*[@key = $keyvalue]") POLICY_ANCESTOR_XPATH = etree.XPath('ancestor::*[local-name() = "policy"]') - ALL_CLASS_POLICY_XPATH = etree.XPath('//*[local-name() = "policy" and (@*[local-name() = "class"] = "Both" or @*[local-name() = "class"] = $registry_class)]') - ADML_DISPLAY_NAME_XPATH = etree.XPath('//*[local-name() = $displayNameType and @*[local-name() = "id"] = $displayNameId]') + ALL_CLASS_POLICY_XPATH = etree.XPath( + '//*[local-name() = "policy" and (@*[local-name() = "class"] = "Both" or @*[local-name() = "class"] = $registry_class)]' + ) + ADML_DISPLAY_NAME_XPATH = etree.XPath( + '//*[local-name() = $displayNameType and @*[local-name() = "id"] = $displayNameId]' + ) VALUE_LIST_XPATH = etree.XPath('.//*[local-name() = "valueList"]') - ENUM_ITEM_DISPLAY_NAME_XPATH = etree.XPath('.//*[local-name() = "item" and @*[local-name() = "displayName" = $display_name]]') - ADMX_SEARCH_XPATH = etree.XPath('//*[local-name() = "policy" and @*[local-name() = "name"] = $policy_name and (@*[local-name() = "class"] = "Both" or @*[local-name() = "class"] = $registry_class)]') - ADML_SEARCH_XPATH = etree.XPath('//*[starts-with(text(), $policy_name) and @*[local-name() = "id"]]') - ADMX_DISPLAYNAME_SEARCH_XPATH = etree.XPath('//*[local-name() = "policy" and @*[local-name() = "displayName"] = $display_name and (@*[local-name() = "class"] = "Both" or @*[local-name() = "class"] = $registry_class) ]') - PRESENTATION_ANCESTOR_XPATH = etree.XPath('ancestor::*[local-name() = "presentation"]') + ENUM_ITEM_DISPLAY_NAME_XPATH = etree.XPath( + './/*[local-name() = "item" and @*[local-name() = "displayName" = $display_name]]' + ) + ADMX_SEARCH_XPATH = etree.XPath( + '//*[local-name() = "policy" and @*[local-name() = "name"] = $policy_name and (@*[local-name() = "class"] = "Both" or @*[local-name() = "class"] = $registry_class)]' + ) + ADML_SEARCH_XPATH = etree.XPath( + '//*[starts-with(text(), $policy_name) and @*[local-name() = "id"]]' + ) + ADMX_DISPLAYNAME_SEARCH_XPATH = etree.XPath( + '//*[local-name() = "policy" and @*[local-name() = "displayName"] = $display_name and (@*[local-name() = "class"] = "Both" or @*[local-name() = "class"] = $registry_class) ]' + ) + PRESENTATION_ANCESTOR_XPATH = etree.XPath( + 'ancestor::*[local-name() = "presentation"]' + ) TEXT_ELEMENT_XPATH = etree.XPath('.//*[local-name() = "text"]') # Get the System Install Language # https://msdn.microsoft.com/en-us/library/dd318123(VS.85).aspx @@ -136,13 +153,14 @@ try: # Default to `en-US` (1033) windll = ctypes.windll.kernel32 INSTALL_LANGUAGE = locale.windows_locale.get( - windll.GetSystemDefaultUILanguage(), 'en_US').replace('_', '-') + windll.GetSystemDefaultUILanguage(), "en_US" + ).replace("_", "-") except ImportError: HAS_WINDOWS_MODULES = False class _policy_info(object): - r''' + r""" Policy Helper Class =================== @@ -318,2337 +336,2316 @@ class _policy_info(object): in days. Thus the "Get" and "Put" functions for this policy do these conversions so the user is able to set and view the policy using the same data that is shown in the GUI. - ''' + """ + def __init__(self): self.audit_lookup = { - 0: 'No auditing', - 1: 'Success', - 2: 'Failure', - 3: 'Success, Failure', - 'Not Defined': 'Not Defined', - None: 'Not Defined', + 0: "No auditing", + 1: "Success", + 2: "Failure", + 3: "Success, Failure", + "Not Defined": "Not Defined", + None: "Not Defined", } self.advanced_audit_lookup = { - 0: 'No Auditing', - 1: 'Success', - 2: 'Failure', - 3: 'Success and Failure', - None: 'Not Configured', + 0: "No Auditing", + 1: "Success", + 2: "Failure", + 3: "Success and Failure", + None: "Not Configured", } self.sc_removal_lookup = { - 0: 'No Action', - 1: 'Lock Workstation', - 2: 'Force Logoff', - 3: 'Disconnect if a Remote Desktop Services session', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "No Action", + 1: "Lock Workstation", + 2: "Force Logoff", + 3: "Disconnect if a Remote Desktop Services session", + None: "Not Defined", + "(value not set)": "Not Defined", } self.uac_admin_prompt_lookup = { - 0: 'Elevate without prompting', - 1: 'Prompt for credentials on the secure desktop', - 2: 'Prompt for consent on the secure desktop', - 3: 'Prompt for credentials', - 4: 'Prompt for consent', - 5: 'Prompt for consent for non-Windows binaries', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "Elevate without prompting", + 1: "Prompt for credentials on the secure desktop", + 2: "Prompt for consent on the secure desktop", + 3: "Prompt for credentials", + 4: "Prompt for consent", + 5: "Prompt for consent for non-Windows binaries", + None: "Not Defined", + "(value not set)": "Not Defined", } self.uac_user_prompt_lookup = { - 0: 'Automatically deny elevation requests', - 1: 'Prompt for credentials on the secure desktop', - 3: 'Prompt for credentials', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "Automatically deny elevation requests", + 1: "Prompt for credentials on the secure desktop", + 3: "Prompt for credentials", + None: "Not Defined", + "(value not set)": "Not Defined", } self.enabled_one_disabled_zero = { - 0: 'Disabled', - 1: 'Enabled', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "Disabled", + 1: "Enabled", + None: "Not Defined", + "(value not set)": "Not Defined", } self.enabled_one_disabled_zero_transform = { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.enabled_one_disabled_zero, - 'value_lookup': False, + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.enabled_one_disabled_zero, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.enabled_one_disabled_zero, - 'value_lookup': True, + "PutArgs": { + "lookup": self.enabled_one_disabled_zero, + "value_lookup": True, }, } self.s4u2self_options = { - 0: 'Default', - 1: 'Enabled', - 2: 'Disabled', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "Default", + 1: "Enabled", + 2: "Disabled", + None: "Not Defined", + "(value not set)": "Not Defined", } self.audit_transform = { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.audit_lookup, - 'value_lookup': False, - }, - 'PutArgs': { - 'lookup': self.audit_lookup, - 'value_lookup': True, - }, + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": {"lookup": self.audit_lookup, "value_lookup": False}, + "PutArgs": {"lookup": self.audit_lookup, "value_lookup": True}, } self.advanced_audit_transform = { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.advanced_audit_lookup, - 'value_lookup': False, - }, - 'PutArgs': { - 'lookup': self.advanced_audit_lookup, - 'value_lookup': True, - }, + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": {"lookup": self.advanced_audit_lookup, "value_lookup": False}, + "PutArgs": {"lookup": self.advanced_audit_lookup, "value_lookup": True}, } self.enabled_one_disabled_zero_strings = { - '0': 'Disabled', - '1': 'Enabled', - None: 'Not Defined', - '(value not set)': 'Not Defined', + "0": "Disabled", + "1": "Enabled", + None: "Not Defined", + "(value not set)": "Not Defined", } self.enabled_one_disabled_zero_strings_transform = { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.enabled_one_disabled_zero_strings, - 'value_lookup': False, + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.enabled_one_disabled_zero_strings, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.enabled_one_disabled_zero_strings, - 'value_lookup': True, + "PutArgs": { + "lookup": self.enabled_one_disabled_zero_strings, + "value_lookup": True, }, } self.security_options_gpedit_path = [ - 'Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Security Options' + "Computer Configuration", + "Windows Settings", + "Security Settings", + "Local Policies", + "Security Options", ] self.windows_firewall_gpedit_path = [ - 'Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Windows Firewall with Advanced Security', - 'Windows Firewall with Advanced Security - Local Group Policy Object' + "Computer Configuration", + "Windows Settings", + "Security Settings", + "Windows Firewall with Advanced Security", + "Windows Firewall with Advanced Security - Local Group Policy Object", ] self.password_policy_gpedit_path = [ - 'Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Password Policy' + "Computer Configuration", + "Windows Settings", + "Security Settings", + "Account Policies", + "Password Policy", ] self.audit_policy_gpedit_path = [ - 'Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'Audit Policy' + "Computer Configuration", + "Windows Settings", + "Security Settings", + "Local Policies", + "Audit Policy", ] self.advanced_audit_policy_gpedit_path = [ - 'Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Advanced Audit Policy Configuration', - 'System Audit Policies - Local Group Policy Object' + "Computer Configuration", + "Windows Settings", + "Security Settings", + "Advanced Audit Policy Configuration", + "System Audit Policies - Local Group Policy Object", ] self.account_lockout_policy_gpedit_path = [ - 'Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Account Policies', - 'Account Lockout Policy' + "Computer Configuration", + "Windows Settings", + "Security Settings", + "Account Policies", + "Account Lockout Policy", ] self.user_rights_assignment_gpedit_path = [ - 'Computer Configuration', - 'Windows Settings', - 'Security Settings', - 'Local Policies', - 'User Rights Assignment' + "Computer Configuration", + "Windows Settings", + "Security Settings", + "Local Policies", + "User Rights Assignment", ] self.block_ms_accounts = { - 0: 'This policy is disabled', - 1: 'Users can\'t add Microsoft accounts', - 3: 'Users can\'t add or log on with Microsoft accounts', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "This policy is disabled", + 1: "Users can't add Microsoft accounts", + 3: "Users can't add or log on with Microsoft accounts", + None: "Not Defined", + "(value not set)": "Not Defined", } self.ldap_server_signing_requirements = { - 1: 'None', - 2: 'Require signing', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 1: "None", + 2: "Require signing", + None: "Not Defined", + "(value not set)": "Not Defined", } self.smb_server_name_hardening_levels = { - 0: 'Off', - 1: 'Accept if provided by client', - 2: 'Required from client', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "Off", + 1: "Accept if provided by client", + 2: "Required from client", + None: "Not Defined", + "(value not set)": "Not Defined", } self.locked_session_user_info = { - 1: 'User display name, domain and user names', - 2: 'User display name only', - 3: 'Do not display user information', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 1: "User display name, domain and user names", + 2: "User display name only", + 3: "Do not display user information", + None: "Not Defined", + "(value not set)": "Not Defined", } self.force_guest = { - 0: 'Classic - local users authenticate as themselves', - 1: 'Guest only - local users authenticate as Guest', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "Classic - local users authenticate as themselves", + 1: "Guest only - local users authenticate as Guest", + None: "Not Defined", + "(value not set)": "Not Defined", } self.force_key_protection = { - 0: 'User input is not required when new keys are stored and used', - 1: 'User is prompted when the key is first used', - 2: 'User must enter a password each time they use a key', - None: 'Not Defined', - '(value not set)': 'Not Defined' + 0: "User input is not required when new keys are stored and used", + 1: "User is prompted when the key is first used", + 2: "User must enter a password each time they use a key", + None: "Not Defined", + "(value not set)": "Not Defined", } self.firewall_inbound_connections = { - 'blockinbound': 'Block (default)', - 'blockinboundalways': 'Block all connections', - 'allowinbound': 'Allow', - 'notconfigured': 'Not configured' + "blockinbound": "Block (default)", + "blockinboundalways": "Block all connections", + "allowinbound": "Allow", + "notconfigured": "Not configured", } self.firewall_outbound_connections = { - 'blockoutbound': 'Block', - 'allowoutbound': 'Allow (default)', - 'notconfigured': 'Not configured' + "blockoutbound": "Block", + "allowoutbound": "Allow (default)", + "notconfigured": "Not configured", } self.firewall_rule_merging = { - 'enable': 'Yes (default)', - 'disable': 'No', - 'notconfigured': 'Not configured' + "enable": "Yes (default)", + "disable": "No", + "notconfigured": "Not configured", } self.firewall_log_packets_connections = { - 'enable': 'Yes', - 'disable': 'No (default)', - 'notconfigured': 'Not configured' + "enable": "Yes", + "disable": "No (default)", + "notconfigured": "Not configured", } self.firewall_notification = { - 'enable': 'Yes', - 'disable': 'No', - 'notconfigured': 'Not configured' + "enable": "Yes", + "disable": "No", + "notconfigured": "Not configured", } self.firewall_state = { - 'on': 'On (recommended)', - 'off': 'Off', - 'notconfigured': 'Not configured' + "on": "On (recommended)", + "off": "Off", + "notconfigured": "Not configured", } self.krb_encryption_types = { - 0: 'No minimum', - 1: 'DES_CBC_CRC', - 2: 'DES_CBD_MD5', - 4: 'RC4_HMAC_MD5', - 8: 'AES128_HMAC_SHA1', - 16: 'AES256_HMAC_SHA1', - 2147483616: 'Future Encryption Types', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "No minimum", + 1: "DES_CBC_CRC", + 2: "DES_CBD_MD5", + 4: "RC4_HMAC_MD5", + 8: "AES128_HMAC_SHA1", + 16: "AES256_HMAC_SHA1", + 2147483616: "Future Encryption Types", + None: "Not Defined", + "(value not set)": "Not Defined", } self.lm_compat_levels = { - 0: 'Send LM & NTLM response', - 1: 'Send LM & NTLM - use NTLMv2 session security if negotiated', - 2: 'Send NTLM response only', - 3: 'Send NTLMv2 response only', - 4: 'Send NTLMv2 response only. Refuse LM', - 5: 'Send NTLMv2 response only. Refuse LM & NTLM', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "Send LM & NTLM response", + 1: "Send LM & NTLM - use NTLMv2 session security if negotiated", + 2: "Send NTLM response only", + 3: "Send NTLMv2 response only", + 4: "Send NTLMv2 response only. Refuse LM", + 5: "Send NTLMv2 response only. Refuse LM & NTLM", + None: "Not Defined", + "(value not set)": "Not Defined", } self.ldap_signing_reqs = { - 0: 'None', - 1: 'Negotiate signing', - 2: 'Require signing', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "None", + 1: "Negotiate signing", + 2: "Require signing", + None: "Not Defined", + "(value not set)": "Not Defined", } self.ntlm_session_security_levels = { - 0: 'No minimum', - 524288: 'Require NTLMv2 session security', - 536870912: 'Require 128-bit encryption', - None: 'Not Defined', - '(value not set)': 'Not Defined', + 0: "No minimum", + 524288: "Require NTLMv2 session security", + 536870912: "Require 128-bit encryption", + None: "Not Defined", + "(value not set)": "Not Defined", } self.ntlm_audit_settings = { - 0: 'Disable', - 1: 'Enable auditing for domain accounts', - 2: 'Enable auditing for all accounts', - None: 'Not Defined', - '(value not set)': 'Not Defined' + 0: "Disable", + 1: "Enable auditing for domain accounts", + 2: "Enable auditing for all accounts", + None: "Not Defined", + "(value not set)": "Not Defined", } self.ntlm_domain_audit_settings = { - 0: 'Disable', - 1: 'Enable for domain accounts to domain servers', - 3: 'Enable for domain accounts', - 5: 'Enable for domain servers', - 7: 'Enable all', - None: 'Not Defined', - '(value not set)': 'Not Defined' + 0: "Disable", + 1: "Enable for domain accounts to domain servers", + 3: "Enable for domain accounts", + 5: "Enable for domain servers", + 7: "Enable all", + None: "Not Defined", + "(value not set)": "Not Defined", } self.incoming_ntlm_settings = { - 0: 'Allow all', - 1: 'Deny all domain accounts', - 2: 'Deny all accounts', - None: 'Not Defined', - '(value not set)': 'Not Defined' + 0: "Allow all", + 1: "Deny all domain accounts", + 2: "Deny all accounts", + None: "Not Defined", + "(value not set)": "Not Defined", } self.ntlm_domain_auth_settings = { - 0: 'Disable', - 1: 'Deny for domain accounts to domain servers', - 3: 'Deny for domain accounts', - 5: 'Deny for domain servers', - 7: 'Deny all', - None: 'Not Defined', - '(value not set)': 'Not Defined' + 0: "Disable", + 1: "Deny for domain accounts to domain servers", + 3: "Deny for domain accounts", + 5: "Deny for domain servers", + 7: "Deny all", + None: "Not Defined", + "(value not set)": "Not Defined", } self.outgoing_ntlm_settings = { - 0: 'Allow all', - 1: 'Audit all', - 2: 'Deny all', - None: 'Not Defined', - '(value not set)': 'Not Defined' + 0: "Allow all", + 1: "Audit all", + 2: "Deny all", + None: "Not Defined", + "(value not set)": "Not Defined", } self.enabled_one_disabled_zero_no_not_defined = { - 0: 'Disabled', - 1: 'Enabled', + 0: "Disabled", + 1: "Enabled", } self.enabled_one_disabled_zero_no_not_defined_transform = { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.enabled_one_disabled_zero_no_not_defined, - 'value_lookup': False, + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.enabled_one_disabled_zero_no_not_defined, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.enabled_one_disabled_zero_no_not_defined, - 'value_lookup': True, + "PutArgs": { + "lookup": self.enabled_one_disabled_zero_no_not_defined, + "value_lookup": True, }, } self.policies = { - 'Machine': { - 'lgpo_section': 'Computer Configuration', - 'policies': { - 'StartupScripts': { - 'Policy': 'Startup Scripts', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Scripts (Startup/Shutdown)', - 'Startup'], - 'ScriptIni': { - 'Section': 'Startup', - 'IniPath': os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'Machine', - 'Scripts', - 'scripts.ini'), + "Machine": { + "lgpo_section": "Computer Configuration", + "policies": { + "StartupScripts": { + "Policy": "Startup Scripts", + "lgpo_section": [ + "Computer Configuration", + "Windows Settings", + "Scripts (Startup/Shutdown)", + "Startup", + ], + "ScriptIni": { + "Section": "Startup", + "IniPath": os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "Machine", + "Scripts", + "scripts.ini", + ), }, }, - 'StartupPowershellScripts': { - 'Policy': 'Startup Powershell Scripts', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Scripts (Startup/Shutdown)', - 'Startup'], - 'ScriptIni': { - 'Section': 'Startup', - 'IniPath': os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'Machine', - 'Scripts', - 'psscripts.ini'), + "StartupPowershellScripts": { + "Policy": "Startup Powershell Scripts", + "lgpo_section": [ + "Computer Configuration", + "Windows Settings", + "Scripts (Startup/Shutdown)", + "Startup", + ], + "ScriptIni": { + "Section": "Startup", + "IniPath": os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "Machine", + "Scripts", + "psscripts.ini", + ), }, }, - 'StartupPowershellScriptOrder': { - 'Policy': 'Startup - For this GPO, run scripts in the ' - 'following order', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Scripts (Startup/Shutdown)', - 'Startup'], - 'ScriptIni': { - 'IniPath': os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'Machine', - 'Scripts', - 'psscripts.ini'), - 'Section': 'ScriptsConfig', - 'SettingName': 'StartExecutePSFirst', - 'Settings': ['true', 'false', None], + "StartupPowershellScriptOrder": { + "Policy": "Startup - For this GPO, run scripts in the " + "following order", + "lgpo_section": [ + "Computer Configuration", + "Windows Settings", + "Scripts (Startup/Shutdown)", + "Startup", + ], + "ScriptIni": { + "IniPath": os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "Machine", + "Scripts", + "psscripts.ini", + ), + "Section": "ScriptsConfig", + "SettingName": "StartExecutePSFirst", + "Settings": ["true", "false", None], }, - 'Transform': { - 'Get': '_powershell_script_order_conversion', - 'Put': '_powershell_script_order_reverse_conversion', + "Transform": { + "Get": "_powershell_script_order_conversion", + "Put": "_powershell_script_order_reverse_conversion", }, }, - 'ShutdownScripts': { - 'Policy': 'Shutdown Scripts', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Scripts (Startup/Shutdown)', - 'Shutdown'], - 'ScriptIni': { - 'Section': 'Shutdown', - 'IniPath': os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'Machine', - 'Scripts', - 'scripts.ini'), + "ShutdownScripts": { + "Policy": "Shutdown Scripts", + "lgpo_section": [ + "Computer Configuration", + "Windows Settings", + "Scripts (Startup/Shutdown)", + "Shutdown", + ], + "ScriptIni": { + "Section": "Shutdown", + "IniPath": os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "Machine", + "Scripts", + "scripts.ini", + ), }, }, - 'ShutdownPowershellScripts': { - 'Policy': 'Shutdown Powershell Scripts', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Scripts (Startup/Shutdown)', - 'Shutdown'], - 'ScriptIni': { - 'Section': 'Shutdown', - 'IniPath': os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'Machine', - 'Scripts', - 'psscripts.ini'), + "ShutdownPowershellScripts": { + "Policy": "Shutdown Powershell Scripts", + "lgpo_section": [ + "Computer Configuration", + "Windows Settings", + "Scripts (Startup/Shutdown)", + "Shutdown", + ], + "ScriptIni": { + "Section": "Shutdown", + "IniPath": os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "Machine", + "Scripts", + "psscripts.ini", + ), }, }, - 'ShutdownPowershellScriptOrder': { - 'Policy': 'Shutdown - For this GPO, run scripts in the ' - 'following order', - 'lgpo_section': ['Computer Configuration', - 'Windows Settings', - 'Scripts (Startup/Shutdown)', - 'Shutdown'], - 'ScriptIni': { - 'IniPath': os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'Machine', - 'Scripts', - 'psscripts.ini'), - 'Section': 'ScriptsConfig', - 'SettingName': 'EndExecutePSFirst', - 'Settings': ['true', 'false', None], + "ShutdownPowershellScriptOrder": { + "Policy": "Shutdown - For this GPO, run scripts in the " + "following order", + "lgpo_section": [ + "Computer Configuration", + "Windows Settings", + "Scripts (Startup/Shutdown)", + "Shutdown", + ], + "ScriptIni": { + "IniPath": os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "Machine", + "Scripts", + "psscripts.ini", + ), + "Section": "ScriptsConfig", + "SettingName": "EndExecutePSFirst", + "Settings": ["true", "false", None], }, - 'Transform': { - 'Get': '_powershell_script_order_conversion', - 'Put': '_powershell_script_order_reverse_conversion', + "Transform": { + "Get": "_powershell_script_order_conversion", + "Put": "_powershell_script_order_reverse_conversion", }, }, - 'LSAAnonymousNameLookup': { - 'Policy': 'Network access: Allow anonymous SID/Name ' - 'translation', - 'lgpo_section': self.password_policy_gpedit_path, - 'Settings': self.enabled_one_disabled_zero_no_not_defined.keys(), - 'Secedit': { - 'Option': 'LSAAnonymousNameLookup', - 'Section': 'System Access', + "LSAAnonymousNameLookup": { + "Policy": "Network access: Allow anonymous SID/Name " + "translation", + "lgpo_section": self.password_policy_gpedit_path, + "Settings": self.enabled_one_disabled_zero_no_not_defined.keys(), + "Secedit": { + "Option": "LSAAnonymousNameLookup", + "Section": "System Access", }, - 'Transform': self.enabled_one_disabled_zero_no_not_defined_transform, + "Transform": self.enabled_one_disabled_zero_no_not_defined_transform, }, - 'RestrictAnonymousSam': { - 'Policy': 'Network access: Do not allow anonymous ' - 'enumeration of SAM accounts', - 'lgpo_section': self.security_options_gpedit_path, - 'Settings': self.enabled_one_disabled_zero.keys(), - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'RestrictAnonymousSam', - 'Type': 'REG_DWORD' + "RestrictAnonymousSam": { + "Policy": "Network access: Do not allow anonymous " + "enumeration of SAM accounts", + "lgpo_section": self.security_options_gpedit_path, + "Settings": self.enabled_one_disabled_zero.keys(), + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "RestrictAnonymousSam", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'RestrictRemoteSAM': { - 'Policy': 'Network access: Restrict clients allowed to ' - 'make remote calls to SAM', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\Lsa', - 'Value': 'RestrictRemoteSAM', - 'Type': 'REG_SZ' + "RestrictRemoteSAM": { + "Policy": "Network access: Restrict clients allowed to " + "make remote calls to SAM", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\Lsa", + "Value": "RestrictRemoteSAM", + "Type": "REG_SZ", }, - 'Transform': { - 'Put': '_string_put_transform' - } + "Transform": {"Put": "_string_put_transform"}, }, - 'RestrictAnonymous': { - 'Policy': 'Network access: Do not allow anonymous ' - 'enumeration of SAM accounts and shares', - 'lgpo_section': self.security_options_gpedit_path, - 'Settings': self.enabled_one_disabled_zero.keys(), - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'RestrictAnonymous', - 'Type': 'REG_DWORD' + "RestrictAnonymous": { + "Policy": "Network access: Do not allow anonymous " + "enumeration of SAM accounts and shares", + "lgpo_section": self.security_options_gpedit_path, + "Settings": self.enabled_one_disabled_zero.keys(), + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "RestrictAnonymous", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'DisableDomainCreds': { - 'Policy': 'Network access: Do not allow storage of ' - 'passwords and credentials for network ' - 'authentication', - 'lgpo_section': self.security_options_gpedit_path, - 'Settings': self.enabled_one_disabled_zero.keys(), - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'DisableDomainCreds', - 'Type': 'REG_DWORD' + "DisableDomainCreds": { + "Policy": "Network access: Do not allow storage of " + "passwords and credentials for network " + "authentication", + "lgpo_section": self.security_options_gpedit_path, + "Settings": self.enabled_one_disabled_zero.keys(), + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "DisableDomainCreds", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'EveryoneIncludesAnonymous': { - 'Policy': 'Network access: Let Everyone permissions ' - 'apply to anonymous users', - 'lgpo_section': self.security_options_gpedit_path, - 'Settings': self.enabled_one_disabled_zero.keys(), - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'everyoneincludesanonymous', - 'Type': 'REG_DWORD' + "EveryoneIncludesAnonymous": { + "Policy": "Network access: Let Everyone permissions " + "apply to anonymous users", + "lgpo_section": self.security_options_gpedit_path, + "Settings": self.enabled_one_disabled_zero.keys(), + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "everyoneincludesanonymous", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'NullSessionPipes': { - 'Policy': 'Network access: Named Pipes that can be ' - 'accessed anonymously', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' - 'LanmanServer\\Parameters', - 'Value': 'NullSessionPipes', - 'Type': 'REG_MULTI_SZ' + "NullSessionPipes": { + "Policy": "Network access: Named Pipes that can be " + "accessed anonymously", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\" + "LanmanServer\\Parameters", + "Value": "NullSessionPipes", + "Type": "REG_MULTI_SZ", + }, + "Transform": { + "Put": "_multi_string_put_transform", + "Get": "_multi_string_get_transform", }, - 'Transform': { - 'Put': '_multi_string_put_transform', - 'Get': '_multi_string_get_transform' - } }, - 'RemoteRegistryExactPaths': { - 'Policy': 'Network access: Remotely accessible ' - 'registry paths', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\' - 'SecurePipeServers\\winreg\\' - 'AllowedExactPaths', - 'Value': 'Machine', - 'Type': 'REG_MULTI_SZ' + "RemoteRegistryExactPaths": { + "Policy": "Network access: Remotely accessible " + "registry paths", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\" + "SecurePipeServers\\winreg\\" + "AllowedExactPaths", + "Value": "Machine", + "Type": "REG_MULTI_SZ", + }, + "Transform": { + "Put": "_multi_string_put_transform", + "Get": "_multi_string_get_transform", }, - 'Transform': { - 'Put': '_multi_string_put_transform', - 'Get': '_multi_string_get_transform' - } }, - 'RemoteRegistryPaths': { - 'Policy': 'Network access: Remotely accessible ' - 'registry paths and sub-paths', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\' - 'SecurePipeServers\\winreg\\AllowedPaths', - 'Value': 'Machine', - 'Type': 'REG_MULTI_SZ' + "RemoteRegistryPaths": { + "Policy": "Network access: Remotely accessible " + "registry paths and sub-paths", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\" + "SecurePipeServers\\winreg\\AllowedPaths", + "Value": "Machine", + "Type": "REG_MULTI_SZ", + }, + "Transform": { + "Put": "_multi_string_put_transform", + "Get": "_multi_string_get_transform", }, - 'Transform': { - 'Put': '_multi_string_put_transform', - 'Get': '_multi_string_get_transform' - } }, - 'RestrictNullSessAccess': { - 'Policy': 'Network access: Restrict anonymous access ' - 'to Named Pipes and Shares', - 'lgpo_section': self.security_options_gpedit_path, - 'Settings': self.enabled_one_disabled_zero.keys(), - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'LanmanServer\\Parameters', - 'Value': 'RestrictNullSessAccess', - 'Type': 'REG_DWORD' + "RestrictNullSessAccess": { + "Policy": "Network access: Restrict anonymous access " + "to Named Pipes and Shares", + "lgpo_section": self.security_options_gpedit_path, + "Settings": self.enabled_one_disabled_zero.keys(), + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "LanmanServer\\Parameters", + "Value": "RestrictNullSessAccess", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'NullSessionShares': { - 'Policy': 'Network access: Shares that can be accessed ' - 'anonymously', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' - 'LanmanServer\\Parameters', - 'Value': 'NullSessionShares', - 'Type': 'REG_MULTI_SZ' + "NullSessionShares": { + "Policy": "Network access: Shares that can be accessed " + "anonymously", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\" + "LanmanServer\\Parameters", + "Value": "NullSessionShares", + "Type": "REG_MULTI_SZ", + }, + "Transform": { + "Put": "_multi_string_put_transform", + "Get": "_multi_string_get_transform", }, - 'Transform': { - 'Put': '_multi_string_put_transform', - 'Get': '_multi_string_get_transform' - } }, - 'ForceGuest': { - 'Policy': 'Network access: Sharing and security model ' - 'for local accounts', - 'lgpo_section': self.security_options_gpedit_path, - 'Settings': self.force_guest.keys(), - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'ForceGuest', - 'Type': 'REG_DWORD' + "ForceGuest": { + "Policy": "Network access: Sharing and security model " + "for local accounts", + "lgpo_section": self.security_options_gpedit_path, + "Settings": self.force_guest.keys(), + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "ForceGuest", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.force_guest, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.force_guest, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.force_guest, - 'value_lookup': True, + "PutArgs": { + "lookup": self.force_guest, + "value_lookup": True, }, }, }, - 'WfwDomainState': { - 'Policy': 'Network firewall: Domain: State', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainState": { + "Policy": "Network firewall: Domain: State", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - On (recommended) # - Off # - Not configured - 'Settings': self.firewall_state.keys(), - 'NetSH': { - 'Profile': 'domain', - 'Section': 'state', - 'Option': 'State' # Unused, but needed + "Settings": self.firewall_state.keys(), + "NetSH": { + "Profile": "domain", + "Section": "state", + "Option": "State", # Unused, but needed }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_state, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_state, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_state, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_state, + "value_lookup": True, }, }, }, - 'WfwPrivateState': { - 'Policy': 'Network firewall: Private: State', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateState": { + "Policy": "Network firewall: Private: State", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - On (recommended) # - Off # - Not configured - 'Settings': self.firewall_state.keys(), - 'NetSH': { - 'Profile': 'private', - 'Section': 'state', - 'Option': 'State' # Unused, but needed + "Settings": self.firewall_state.keys(), + "NetSH": { + "Profile": "private", + "Section": "state", + "Option": "State", # Unused, but needed }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_state, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_state, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_state, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_state, + "value_lookup": True, }, }, }, - 'WfwPublicState': { - 'Policy': 'Network firewall: Public: State', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicState": { + "Policy": "Network firewall: Public: State", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - On (recommended) # - Off # - Not configured - 'Settings': self.firewall_state.keys(), - 'NetSH': { - 'Profile': 'public', - 'Section': 'state', - 'Option': 'State' # Unused, but needed + "Settings": self.firewall_state.keys(), + "NetSH": { + "Profile": "public", + "Section": "state", + "Option": "State", # Unused, but needed }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_state, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_state, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_state, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_state, + "value_lookup": True, }, }, }, - 'WfwDomainInboundConnections': { - 'Policy': 'Network firewall: Domain: Inbound connections', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainInboundConnections": { + "Policy": "Network firewall: Domain: Inbound connections", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Block (default) # - Block all connections # - Allow # - Not configured - 'Settings': self.firewall_inbound_connections.keys(), - 'NetSH': { - 'Profile': 'domain', - 'Section': 'firewallpolicy', - 'Option': 'Inbound' + "Settings": self.firewall_inbound_connections.keys(), + "NetSH": { + "Profile": "domain", + "Section": "firewallpolicy", + "Option": "Inbound", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_inbound_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_inbound_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_inbound_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_inbound_connections, + "value_lookup": True, }, }, }, - 'WfwPrivateInboundConnections': { - 'Policy': 'Network firewall: Private: Inbound connections', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateInboundConnections": { + "Policy": "Network firewall: Private: Inbound connections", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Block (default) # - Block all connections # - Allow # - Not configured - 'Settings': self.firewall_inbound_connections.keys(), - 'NetSH': { - 'Profile': 'private', - 'Section': 'firewallpolicy', - 'Option': 'Inbound' + "Settings": self.firewall_inbound_connections.keys(), + "NetSH": { + "Profile": "private", + "Section": "firewallpolicy", + "Option": "Inbound", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_inbound_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_inbound_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_inbound_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_inbound_connections, + "value_lookup": True, }, }, }, - 'WfwPublicInboundConnections': { - 'Policy': 'Network firewall: Public: Inbound connections', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicInboundConnections": { + "Policy": "Network firewall: Public: Inbound connections", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Block (default) # - Block all connections # - Allow # - Not configured - 'Settings': self.firewall_inbound_connections.keys(), - 'NetSH': { - 'Profile': 'public', - 'Section': 'firewallpolicy', - 'Option': 'Inbound' + "Settings": self.firewall_inbound_connections.keys(), + "NetSH": { + "Profile": "public", + "Section": "firewallpolicy", + "Option": "Inbound", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_inbound_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_inbound_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_inbound_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_inbound_connections, + "value_lookup": True, }, }, }, - 'WfwDomainOutboundConnections': { - 'Policy': 'Network firewall: Domain: Outbound connections', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainOutboundConnections": { + "Policy": "Network firewall: Domain: Outbound connections", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Block # - Allow (default) # - Not configured - 'Settings': self.firewall_outbound_connections.keys(), - 'NetSH': { - 'Profile': 'domain', - 'Section': 'firewallpolicy', - 'Option': 'Outbound' + "Settings": self.firewall_outbound_connections.keys(), + "NetSH": { + "Profile": "domain", + "Section": "firewallpolicy", + "Option": "Outbound", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_outbound_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_outbound_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_outbound_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_outbound_connections, + "value_lookup": True, }, }, }, - 'WfwPrivateOutboundConnections': { - 'Policy': 'Network firewall: Private: Outbound connections', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateOutboundConnections": { + "Policy": "Network firewall: Private: Outbound connections", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Block # - Allow (default) # - Not configured - 'Settings': self.firewall_outbound_connections.keys(), - 'NetSH': { - 'Profile': 'private', - 'Section': 'firewallpolicy', - 'Option': 'Outbound' + "Settings": self.firewall_outbound_connections.keys(), + "NetSH": { + "Profile": "private", + "Section": "firewallpolicy", + "Option": "Outbound", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_outbound_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_outbound_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_outbound_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_outbound_connections, + "value_lookup": True, }, }, }, - 'WfwPublicOutboundConnections': { - 'Policy': 'Network firewall: Public: Outbound connections', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicOutboundConnections": { + "Policy": "Network firewall: Public: Outbound connections", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Block # - Allow (default) # - Not configured - 'Settings': self.firewall_outbound_connections.keys(), - 'NetSH': { - 'Profile': 'public', - 'Section': 'firewallpolicy', - 'Option': 'Outbound' + "Settings": self.firewall_outbound_connections.keys(), + "NetSH": { + "Profile": "public", + "Section": "firewallpolicy", + "Option": "Outbound", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_outbound_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_outbound_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_outbound_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_outbound_connections, + "value_lookup": True, }, }, }, - 'WfwDomainSettingsNotification': { - 'Policy': 'Network firewall: Domain: Settings: Display a notification', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainSettingsNotification": { + "Policy": "Network firewall: Domain: Settings: Display a notification", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes # - No # - Not configured - 'Settings': self.firewall_notification.keys(), - 'NetSH': { - 'Profile': 'domain', - 'Section': 'settings', - 'Option': 'InboundUserNotification' + "Settings": self.firewall_notification.keys(), + "NetSH": { + "Profile": "domain", + "Section": "settings", + "Option": "InboundUserNotification", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_notification, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_notification, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_notification, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_notification, + "value_lookup": True, }, }, }, - 'WfwPrivateSettingsNotification': { - 'Policy': 'Network firewall: Private: Settings: Display a notification', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateSettingsNotification": { + "Policy": "Network firewall: Private: Settings: Display a notification", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes # - No # - Not configured - 'Settings': self.firewall_notification.keys(), - 'NetSH': { - 'Profile': 'private', - 'Section': 'settings', - 'Option': 'InboundUserNotification' + "Settings": self.firewall_notification.keys(), + "NetSH": { + "Profile": "private", + "Section": "settings", + "Option": "InboundUserNotification", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_notification, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_notification, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_notification, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_notification, + "value_lookup": True, }, }, }, - 'WfwPublicSettingsNotification': { - 'Policy': 'Network firewall: Public: Settings: Display a notification', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicSettingsNotification": { + "Policy": "Network firewall: Public: Settings: Display a notification", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes # - No # - Not configured - 'Settings': self.firewall_notification.keys(), - 'NetSH': { - 'Profile': 'public', - 'Section': 'settings', - 'Option': 'InboundUserNotification' + "Settings": self.firewall_notification.keys(), + "NetSH": { + "Profile": "public", + "Section": "settings", + "Option": "InboundUserNotification", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_notification, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_notification, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_notification, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_notification, + "value_lookup": True, }, }, }, - 'WfwDomainSettingsLocalFirewallRules': { - 'Policy': 'Network firewall: Domain: Settings: Apply ' - 'local firewall rules', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainSettingsLocalFirewallRules": { + "Policy": "Network firewall: Domain: Settings: Apply " + "local firewall rules", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes (default) # - No # - Not configured - 'Settings': self.firewall_rule_merging.keys(), - 'NetSH': { - 'Profile': 'domain', - 'Section': 'settings', - 'Option': 'LocalFirewallRules' + "Settings": self.firewall_rule_merging.keys(), + "NetSH": { + "Profile": "domain", + "Section": "settings", + "Option": "LocalFirewallRules", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": True, }, }, }, - 'WfwPrivateSettingsLocalFirewallRules': { - 'Policy': 'Network firewall: Private: Settings: Apply ' - 'local firewall rules', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateSettingsLocalFirewallRules": { + "Policy": "Network firewall: Private: Settings: Apply " + "local firewall rules", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes (default) # - No # - Not configured - 'Settings': self.firewall_rule_merging.keys(), - 'NetSH': { - 'Profile': 'private', - 'Section': 'settings', - 'Option': 'LocalFirewallRules' + "Settings": self.firewall_rule_merging.keys(), + "NetSH": { + "Profile": "private", + "Section": "settings", + "Option": "LocalFirewallRules", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": True, }, }, }, - 'WfwPublicSettingsLocalFirewallRules': { - 'Policy': 'Network firewall: Public: Settings: Apply ' - 'local firewall rules', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicSettingsLocalFirewallRules": { + "Policy": "Network firewall: Public: Settings: Apply " + "local firewall rules", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes (default) # - No # - Not configured - 'Settings': self.firewall_rule_merging.keys(), - 'NetSH': { - 'Profile': 'public', - 'Section': 'settings', - 'Option': 'LocalFirewallRules' + "Settings": self.firewall_rule_merging.keys(), + "NetSH": { + "Profile": "public", + "Section": "settings", + "Option": "LocalFirewallRules", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": True, }, }, }, - 'WfwDomainSettingsLocalConnectionRules': { - 'Policy': 'Network firewall: Domain: Settings: Apply ' - 'local connection security rules', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainSettingsLocalConnectionRules": { + "Policy": "Network firewall: Domain: Settings: Apply " + "local connection security rules", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes (default) # - No # - Not configured - 'Settings': self.firewall_rule_merging.keys(), - 'NetSH': { - 'Profile': 'domain', - 'Section': 'settings', - 'Option': 'LocalConSecRules' + "Settings": self.firewall_rule_merging.keys(), + "NetSH": { + "Profile": "domain", + "Section": "settings", + "Option": "LocalConSecRules", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": True, }, }, }, - 'WfwPrivateSettingsLocalConnectionRules': { - 'Policy': 'Network firewall: Private: Settings: Apply ' - 'local connection security rules', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateSettingsLocalConnectionRules": { + "Policy": "Network firewall: Private: Settings: Apply " + "local connection security rules", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes (default) # - No # - Not configured - 'Settings': self.firewall_rule_merging.keys(), - 'NetSH': { - 'Profile': 'private', - 'Section': 'settings', - 'Option': 'LocalConSecRules' + "Settings": self.firewall_rule_merging.keys(), + "NetSH": { + "Profile": "private", + "Section": "settings", + "Option": "LocalConSecRules", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": True, }, }, }, - 'WfwPublicSettingsLocalConnectionRules': { - 'Policy': 'Network firewall: Public: Settings: Apply ' - 'local connection security rules', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicSettingsLocalConnectionRules": { + "Policy": "Network firewall: Public: Settings: Apply " + "local connection security rules", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes (default) # - No # - Not configured - 'Settings': self.firewall_rule_merging.keys(), - 'NetSH': { - 'Profile': 'public', - 'Section': 'settings', - 'Option': 'LocalConSecRules' + "Settings": self.firewall_rule_merging.keys(), + "NetSH": { + "Profile": "public", + "Section": "settings", + "Option": "LocalConSecRules", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_rule_merging, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_rule_merging, + "value_lookup": True, }, }, }, - 'WfwDomainLoggingName': { - 'Policy': 'Network firewall: Domain: Logging: Name', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainLoggingName": { + "Policy": "Network firewall: Domain: Logging: Name", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - <a full path to a file> # - Not configured - 'Settings': None, - 'NetSH': { - 'Profile': 'domain', - 'Section': 'logging', - 'Option': 'FileName' - } + "Settings": None, + "NetSH": { + "Profile": "domain", + "Section": "logging", + "Option": "FileName", + }, }, - 'WfwPrivateLoggingName': { - 'Policy': 'Network firewall: Private: Logging: Name', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateLoggingName": { + "Policy": "Network firewall: Private: Logging: Name", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - <a full path to a file> # - Not configured - 'Settings': None, - 'NetSH': { - 'Profile': 'private', - 'Section': 'logging', - 'Option': 'FileName' - } + "Settings": None, + "NetSH": { + "Profile": "private", + "Section": "logging", + "Option": "FileName", + }, }, - 'WfwPublicLoggingName': { - 'Policy': 'Network firewall: Public: Logging: Name', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicLoggingName": { + "Policy": "Network firewall: Public: Logging: Name", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - <a full path to a file> # - Not configured - 'Settings': None, - 'NetSH': { - 'Profile': 'public', - 'Section': 'logging', - 'Option': 'FileName' - } + "Settings": None, + "NetSH": { + "Profile": "public", + "Section": "logging", + "Option": "FileName", + }, }, - 'WfwDomainLoggingMaxFileSize': { - 'Policy': 'Network firewall: Domain: Logging: Size limit (KB)', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainLoggingMaxFileSize": { + "Policy": "Network firewall: Domain: Logging: Size limit (KB)", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - <int between 1 and 32767> # - Not configured - 'Settings': None, - 'NetSH': { - 'Profile': 'domain', - 'Section': 'logging', - 'Option': 'MaxFileSize' - } + "Settings": None, + "NetSH": { + "Profile": "domain", + "Section": "logging", + "Option": "MaxFileSize", + }, }, - 'WfwPrivateLoggingMaxFileSize': { - 'Policy': 'Network firewall: Private: Logging: Size limit (KB)', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateLoggingMaxFileSize": { + "Policy": "Network firewall: Private: Logging: Size limit (KB)", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - <int between 1 and 32767> # - Not configured - 'Settings': None, - 'NetSH': { - 'Profile': 'private', - 'Section': 'logging', - 'Option': 'MaxFileSize' - } + "Settings": None, + "NetSH": { + "Profile": "private", + "Section": "logging", + "Option": "MaxFileSize", + }, }, - 'WfwPublicLoggingMaxFileSize': { - 'Policy': 'Network firewall: Public: Logging: Size limit (KB)', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicLoggingMaxFileSize": { + "Policy": "Network firewall: Public: Logging: Size limit (KB)", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - <int between 1 and 32767> # - Not configured - 'Settings': None, - 'NetSH': { - 'Profile': 'public', - 'Section': 'logging', - 'Option': 'MaxFileSize' - } + "Settings": None, + "NetSH": { + "Profile": "public", + "Section": "logging", + "Option": "MaxFileSize", + }, }, - 'WfwDomainLoggingAllowedConnections': { - 'Policy': 'Network firewall: Domain: Logging: Log successful connections', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainLoggingAllowedConnections": { + "Policy": "Network firewall: Domain: Logging: Log successful connections", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes # - No (default) # - Not configured - 'Settings': self.firewall_log_packets_connections.keys(), - 'NetSH': { - 'Profile': 'domain', - 'Section': 'logging', - 'Option': 'LogAllowedConnections' + "Settings": self.firewall_log_packets_connections.keys(), + "NetSH": { + "Profile": "domain", + "Section": "logging", + "Option": "LogAllowedConnections", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": True, }, }, }, - 'WfwPrivateLoggingAllowedConnections': { - 'Policy': 'Network firewall: Private: Logging: Log successful connections', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateLoggingAllowedConnections": { + "Policy": "Network firewall: Private: Logging: Log successful connections", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes # - No (default) # - Not configured - 'Settings': self.firewall_log_packets_connections.keys(), - 'NetSH': { - 'Profile': 'private', - 'Section': 'logging', - 'Option': 'LogAllowedConnections' + "Settings": self.firewall_log_packets_connections.keys(), + "NetSH": { + "Profile": "private", + "Section": "logging", + "Option": "LogAllowedConnections", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": True, }, }, }, - 'WfwPublicLoggingAllowedConnections': { - 'Policy': 'Network firewall: Public: Logging: Log successful connections', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicLoggingAllowedConnections": { + "Policy": "Network firewall: Public: Logging: Log successful connections", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes # - No (default) # - Not configured - 'Settings': self.firewall_log_packets_connections.keys(), - 'NetSH': { - 'Profile': 'public', - 'Section': 'logging', - 'Option': 'LogAllowedConnections' + "Settings": self.firewall_log_packets_connections.keys(), + "NetSH": { + "Profile": "public", + "Section": "logging", + "Option": "LogAllowedConnections", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": True, }, }, }, - 'WfwDomainLoggingDroppedConnections': { - 'Policy': 'Network firewall: Domain: Logging: Log dropped packets', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwDomainLoggingDroppedConnections": { + "Policy": "Network firewall: Domain: Logging: Log dropped packets", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes # - No (default) # - Not configured - 'Settings': self.firewall_log_packets_connections.keys(), - 'NetSH': { - 'Profile': 'domain', - 'Section': 'logging', - 'Option': 'LogDroppedConnections' + "Settings": self.firewall_log_packets_connections.keys(), + "NetSH": { + "Profile": "domain", + "Section": "logging", + "Option": "LogDroppedConnections", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": True, }, }, }, - 'WfwPrivateLoggingDroppedConnections': { - 'Policy': 'Network firewall: Private: Logging: Log dropped packets', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPrivateLoggingDroppedConnections": { + "Policy": "Network firewall: Private: Logging: Log dropped packets", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes # - No (default) # - Not configured - 'Settings': self.firewall_log_packets_connections.keys(), - 'NetSH': { - 'Profile': 'private', - 'Section': 'logging', - 'Option': 'LogDroppedConnections' + "Settings": self.firewall_log_packets_connections.keys(), + "NetSH": { + "Profile": "private", + "Section": "logging", + "Option": "LogDroppedConnections", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": True, }, }, }, - 'WfwPublicLoggingDroppedConnections': { - 'Policy': 'Network firewall: Public: Logging: Log dropped packets', - 'lgpo_section': self.windows_firewall_gpedit_path, + "WfwPublicLoggingDroppedConnections": { + "Policy": "Network firewall: Public: Logging: Log dropped packets", + "lgpo_section": self.windows_firewall_gpedit_path, # Settings available are: # - Yes # - No (default) # - Not configured - 'Settings': self.firewall_log_packets_connections.keys(), - 'NetSH': { - 'Profile': 'public', - 'Section': 'logging', - 'Option': 'LogDroppedConnections' + "Settings": self.firewall_log_packets_connections.keys(), + "NetSH": { + "Profile": "public", + "Section": "logging", + "Option": "LogDroppedConnections", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.firewall_log_packets_connections, - 'value_lookup': True, + "PutArgs": { + "lookup": self.firewall_log_packets_connections, + "value_lookup": True, }, }, }, - 'PasswordHistory': { - 'Policy': 'Enforce password history', - 'lgpo_section': self.password_policy_gpedit_path, - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 24} - }, - 'NetUserModal': { - 'Modal': 0, - 'Option': 'password_hist_len' + "PasswordHistory": { + "Policy": "Enforce password history", + "lgpo_section": self.password_policy_gpedit_path, + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 24}, }, + "NetUserModal": {"Modal": 0, "Option": "password_hist_len"}, }, - 'MaxPasswordAge': { - 'Policy': 'Maximum password age', - 'lgpo_section': self.password_policy_gpedit_path, - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 1, - 'max': 86313600, - 'zero_value': 0xffffffff} - }, - 'NetUserModal': { - 'Modal': 0, - 'Option': 'max_passwd_age', - }, - 'Transform': { - 'Get': '_seconds_to_days', - 'Put': '_days_to_seconds', - 'GetArgs': {'zero_value': 0xffffffff}, - 'PutArgs': {'zero_value': 0xffffffff} - }, - }, - 'MinPasswordAge': { - 'Policy': 'Minimum password age', - 'lgpo_section': self.password_policy_gpedit_path, - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 86313600} - }, - 'NetUserModal': { - 'Modal': 0, - 'Option': 'min_passwd_age', - }, - 'Transform': { - 'Get': '_seconds_to_days', - 'Put': '_days_to_seconds' - }, - }, - 'MinPasswordLen': { - 'Policy': 'Minimum password length', - 'lgpo_section': self.password_policy_gpedit_path, - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 14} - }, - 'NetUserModal': { - 'Modal': 0, - 'Option': 'min_passwd_len', - }, - }, - 'PasswordComplexity': { - 'Policy': 'Password must meet complexity requirements', - 'lgpo_section': self.password_policy_gpedit_path, - 'Settings': self.enabled_one_disabled_zero_no_not_defined.keys(), - 'Secedit': { - 'Option': 'PasswordComplexity', - 'Section': 'System Access', - }, - 'Transform': self.enabled_one_disabled_zero_no_not_defined_transform, - }, - 'ClearTextPasswords': { - 'Policy': 'Store passwords using reversible encryption', - 'lgpo_section': self.password_policy_gpedit_path, - 'Settings': self.enabled_one_disabled_zero_no_not_defined.keys(), - 'Secedit': { - 'Option': 'ClearTextPassword', - 'Section': 'System Access', - }, - 'Transform': self.enabled_one_disabled_zero_no_not_defined_transform, - }, - 'AdminAccountStatus': { - 'Policy': 'Accounts: Administrator account status', - 'Settings': self.enabled_one_disabled_zero_no_not_defined.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Secedit': { - 'Option': 'EnableAdminAccount', - 'Section': 'System Access', - }, - 'Transform': self.enabled_one_disabled_zero_no_not_defined_transform, - }, - 'NoConnectedUser': { - 'Policy': 'Accounts: Block Microsoft accounts', - 'Settings': self.block_ms_accounts.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\' - 'CurrentVersion\\policies\\system', - 'Value': 'NoConnectedUser', - 'Type': 'REG_DWORD', - }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.block_ms_accounts, - 'value_lookup': False, + "MaxPasswordAge": { + "Policy": "Maximum password age", + "lgpo_section": self.password_policy_gpedit_path, + "Settings": { + "Function": "_in_range_inclusive", + "Args": { + "min": 1, + "max": 86313600, + "zero_value": 0xFFFFFFFF, }, - 'PutArgs': { - 'lookup': self.block_ms_accounts, - 'value_lookup': True, + }, + "NetUserModal": {"Modal": 0, "Option": "max_passwd_age"}, + "Transform": { + "Get": "_seconds_to_days", + "Put": "_days_to_seconds", + "GetArgs": {"zero_value": 0xFFFFFFFF}, + "PutArgs": {"zero_value": 0xFFFFFFFF}, + }, + }, + "MinPasswordAge": { + "Policy": "Minimum password age", + "lgpo_section": self.password_policy_gpedit_path, + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 86313600}, + }, + "NetUserModal": {"Modal": 0, "Option": "min_passwd_age"}, + "Transform": { + "Get": "_seconds_to_days", + "Put": "_days_to_seconds", + }, + }, + "MinPasswordLen": { + "Policy": "Minimum password length", + "lgpo_section": self.password_policy_gpedit_path, + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 14}, + }, + "NetUserModal": {"Modal": 0, "Option": "min_passwd_len"}, + }, + "PasswordComplexity": { + "Policy": "Password must meet complexity requirements", + "lgpo_section": self.password_policy_gpedit_path, + "Settings": self.enabled_one_disabled_zero_no_not_defined.keys(), + "Secedit": { + "Option": "PasswordComplexity", + "Section": "System Access", + }, + "Transform": self.enabled_one_disabled_zero_no_not_defined_transform, + }, + "ClearTextPasswords": { + "Policy": "Store passwords using reversible encryption", + "lgpo_section": self.password_policy_gpedit_path, + "Settings": self.enabled_one_disabled_zero_no_not_defined.keys(), + "Secedit": { + "Option": "ClearTextPassword", + "Section": "System Access", + }, + "Transform": self.enabled_one_disabled_zero_no_not_defined_transform, + }, + "AdminAccountStatus": { + "Policy": "Accounts: Administrator account status", + "Settings": self.enabled_one_disabled_zero_no_not_defined.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Secedit": { + "Option": "EnableAdminAccount", + "Section": "System Access", + }, + "Transform": self.enabled_one_disabled_zero_no_not_defined_transform, + }, + "NoConnectedUser": { + "Policy": "Accounts: Block Microsoft accounts", + "Settings": self.block_ms_accounts.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SOFTWARE\\Microsoft\\Windows\\" + "CurrentVersion\\policies\\system", + "Value": "NoConnectedUser", + "Type": "REG_DWORD", + }, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.block_ms_accounts, + "value_lookup": False, + }, + "PutArgs": { + "lookup": self.block_ms_accounts, + "value_lookup": True, }, }, }, - 'GuestAccountStatus': { - 'Policy': 'Accounts: Guest account status', - 'Settings': self.enabled_one_disabled_zero_no_not_defined.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Secedit': { - 'Option': 'EnableGuestAccount', - 'Section': 'System Access', + "GuestAccountStatus": { + "Policy": "Accounts: Guest account status", + "Settings": self.enabled_one_disabled_zero_no_not_defined.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Secedit": { + "Option": "EnableGuestAccount", + "Section": "System Access", }, - 'Transform': self.enabled_one_disabled_zero_no_not_defined_transform, + "Transform": self.enabled_one_disabled_zero_no_not_defined_transform, }, - 'LimitBlankPasswordUse': { - 'Policy': 'Accounts: Limit local account use of blank ' - 'passwords to console logon only', - 'lgpo_section': self.security_options_gpedit_path, - 'Settings': self.enabled_one_disabled_zero.keys(), - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'limitblankpassworduse', - 'Type': 'REG_DWORD', + "LimitBlankPasswordUse": { + "Policy": "Accounts: Limit local account use of blank " + "passwords to console logon only", + "lgpo_section": self.security_options_gpedit_path, + "Settings": self.enabled_one_disabled_zero.keys(), + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "limitblankpassworduse", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'RenameAdministratorAccount': { - 'Policy': 'Accounts: Rename administrator account', - 'Settings': None, - 'lgpo_section': self.security_options_gpedit_path, - 'Secedit': { - 'Option': 'NewAdministratorName', - 'Section': 'System Access', + "RenameAdministratorAccount": { + "Policy": "Accounts: Rename administrator account", + "Settings": None, + "lgpo_section": self.security_options_gpedit_path, + "Secedit": { + "Option": "NewAdministratorName", + "Section": "System Access", }, - 'Transform': { - 'Get': '_strip_quotes', - 'Put': '_add_quotes', + "Transform": {"Get": "_strip_quotes", "Put": "_add_quotes"}, + }, + "RenameGuestAccount": { + "Policy": "Accounts: Rename guest account", + "Settings": None, + "lgpo_section": self.security_options_gpedit_path, + "Secedit": { + "Option": "NewGuestName", + "Section": "System Access", + }, + "Transform": {"Get": "_strip_quotes", "Put": "_add_quotes"}, + }, + "AuditBaseObjects": { + "Policy": "Audit: Audit the access of global system " "objects", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "AuditBaseObjects", + "Type": "REG_DWORD", + }, + "Transform": self.enabled_one_disabled_zero_transform, + }, + "SceNoApplyLegacyAuditPolicy": { + "Policy": "Audit: Force audit policy subcategory " + "settings (Windows Vista or later) to " + "override audit policy category settings", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "AuditBaseObjects", + "Type": "REG_DWORD", + }, + "Transform": self.enabled_one_disabled_zero_transform, + }, + "DontDisplayLastUserName": { + "Policy": "Interactive logon: Do not display last user " "name", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "DontDisplayLastUserName", + "Type": "REG_DWORD", + }, + "Transform": self.enabled_one_disabled_zero_transform, + }, + "CachedLogonsCount": { + "Policy": "Interactive logon: Number of previous " + "logons to cache (in case domain controller " + "is not available)", + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 50}, + }, + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows NT\\" + "CurrentVersion\\Winlogon", + "Value": "CachedLogonsCount", + "Type": "REG_SZ", }, }, - 'RenameGuestAccount': { - 'Policy': 'Accounts: Rename guest account', - 'Settings': None, - 'lgpo_section': self.security_options_gpedit_path, - 'Secedit': { - 'Option': 'NewGuestName', - 'Section': 'System Access', - }, - 'Transform': { - 'Get': '_strip_quotes', - 'Put': '_add_quotes', + "ForceUnlockLogon": { + "Policy": "Interactive logon: Require Domain " + "Controller authentication to unlock " + "workstation", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows NT\\" + "CurrentVersion\\Winlogon", + "Value": "ForceUnlockLogon", + "Type": "REG_DWORD", }, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'AuditBaseObjects': { - 'Policy': 'Audit: Audit the access of global system ' - 'objects', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'AuditBaseObjects', - 'Type': 'REG_DWORD', + "ScRemoveOption": { + "Policy": "Interactive logon: Smart card removal " "behavior", + "Settings": self.sc_removal_lookup.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows NT\\" + "CurrentVersion\\Winlogon", + "Value": "ScRemoveOption", + "Type": "REG_SZ", }, - 'Transform': self.enabled_one_disabled_zero_transform - }, - 'SceNoApplyLegacyAuditPolicy': { - 'Policy': 'Audit: Force audit policy subcategory ' - 'settings (Windows Vista or later) to ' - 'override audit policy category settings', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'AuditBaseObjects', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_transform - }, - 'DontDisplayLastUserName': { - 'Policy': 'Interactive logon: Do not display last user ' - 'name', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'DontDisplayLastUserName', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_transform, - }, - 'CachedLogonsCount': { - 'Policy': 'Interactive logon: Number of previous ' - 'logons to cache (in case domain controller ' - 'is not available)', - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 50} - }, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\' - 'CurrentVersion\\Winlogon', - 'Value': 'CachedLogonsCount', - 'Type': 'REG_SZ', - }, - }, - 'ForceUnlockLogon': { - 'Policy': 'Interactive logon: Require Domain ' - 'Controller authentication to unlock ' - 'workstation', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\' - 'CurrentVersion\\Winlogon', - 'Value': 'ForceUnlockLogon', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_transform, - }, - 'ScRemoveOption': { - 'Policy': 'Interactive logon: Smart card removal ' - 'behavior', - 'Settings': self.sc_removal_lookup.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\' - 'CurrentVersion\\Winlogon', - 'Value': 'ScRemoveOption', - 'Type': 'REG_SZ', - }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.sc_removal_lookup, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.sc_removal_lookup, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.sc_removal_lookup, - 'value_lookup': True, + "PutArgs": { + "lookup": self.sc_removal_lookup, + "value_lookup": True, }, }, }, - 'DisableCAD': { - 'Policy': 'Interactive logon: Do not require ' - 'CTRL+ALT+DEL', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'DisableCAD', - 'Type': 'REG_DWORD', + "DisableCAD": { + "Policy": "Interactive logon: Do not require " "CTRL+ALT+DEL", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "DisableCAD", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'FilterAdministratorToken': { - 'Policy': 'User Account Control: Admin Approval Mode ' - 'for the built-in Administrator account', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'FilterAdministratorToken', - 'Type': 'REG_DWORD', + "FilterAdministratorToken": { + "Policy": "User Account Control: Admin Approval Mode " + "for the built-in Administrator account", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "FilterAdministratorToken", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'EnableUIADesktopToggle': { - 'Policy': 'User Account Control: Allow UIAccess ' - 'applications to prompt for elevation ' - 'without using the secure desktop', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'EnableUIADesktopToggle', - 'Type': 'REG_DWORD', + "EnableUIADesktopToggle": { + "Policy": "User Account Control: Allow UIAccess " + "applications to prompt for elevation " + "without using the secure desktop", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "EnableUIADesktopToggle", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'ConsentPromptBehaviorAdmin': { - 'Policy': 'User Account Control: Behavior of the ' - 'elevation prompt for administrators in ' - 'Admin Approval Mode', - 'Settings': self.uac_admin_prompt_lookup.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'ConsentPromptBehaviorAdmin', - 'Type': 'REG_DWORD', + "ConsentPromptBehaviorAdmin": { + "Policy": "User Account Control: Behavior of the " + "elevation prompt for administrators in " + "Admin Approval Mode", + "Settings": self.uac_admin_prompt_lookup.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "ConsentPromptBehaviorAdmin", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.uac_admin_prompt_lookup, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.uac_admin_prompt_lookup, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.uac_admin_prompt_lookup, - 'value_lookup': True, + "PutArgs": { + "lookup": self.uac_admin_prompt_lookup, + "value_lookup": True, }, }, }, - 'ConsentPromptBehaviorUser': { - 'Policy': 'User Account Control: Behavior of the ' - 'elevation prompt for standard users', - 'Settings': self.uac_user_prompt_lookup.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'ConsentPromptBehaviorUser', - 'Type': 'REG_DWORD', + "ConsentPromptBehaviorUser": { + "Policy": "User Account Control: Behavior of the " + "elevation prompt for standard users", + "Settings": self.uac_user_prompt_lookup.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "ConsentPromptBehaviorUser", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.uac_user_prompt_lookup, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.uac_user_prompt_lookup, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.uac_user_prompt_lookup, - 'value_lookup': True, + "PutArgs": { + "lookup": self.uac_user_prompt_lookup, + "value_lookup": True, }, }, }, - 'EnableInstallerDetection': { - 'Policy': 'User Account Control: Detect application ' - 'installations and prompt for elevation', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'EnableInstallerDetection', - 'Type': 'REG_DWORD', + "EnableInstallerDetection": { + "Policy": "User Account Control: Detect application " + "installations and prompt for elevation", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "EnableInstallerDetection", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'ValidateAdminCodeSignatures': { - 'Policy': 'User Account Control: Only elevate ' - 'executables that are signed and validated', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'ValidateAdminCodeSignatures', - 'Type': 'REG_DWORD', + "ValidateAdminCodeSignatures": { + "Policy": "User Account Control: Only elevate " + "executables that are signed and validated", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "ValidateAdminCodeSignatures", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'EnableSecureUIAPaths': { - 'Policy': 'User Account Control: Only elevate UIAccess ' - 'applications that are installed in secure ' - 'locations', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'EnableSecureUIAPaths', - 'Type': 'REG_DWORD', + "EnableSecureUIAPaths": { + "Policy": "User Account Control: Only elevate UIAccess " + "applications that are installed in secure " + "locations", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "EnableSecureUIAPaths", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'EnableLUA': { - 'Policy': 'User Account Control: Run all ' - 'administrators in Admin Approval Mode', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'EnableLUA', - 'Type': 'REG_DWORD', + "EnableLUA": { + "Policy": "User Account Control: Run all " + "administrators in Admin Approval Mode", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "EnableLUA", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'PromptOnSecureDesktop': { - 'Policy': 'User Account Control: Switch to the secure ' - 'desktop when prompting for elevation', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'PromptOnSecureDesktop', - 'Type': 'REG_DWORD', + "PromptOnSecureDesktop": { + "Policy": "User Account Control: Switch to the secure " + "desktop when prompting for elevation", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "PromptOnSecureDesktop", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'EnableVirtualization': { - 'Policy': 'User Account Control: Virtualize file and ' - 'registry write failures to per-user ' - 'locations', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'EnableVirtualization', - 'Type': 'REG_DWORD', + "EnableVirtualization": { + "Policy": "User Account Control: Virtualize file and " + "registry write failures to per-user " + "locations", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "EnableVirtualization", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'PasswordExpiryWarning': { - 'Policy': 'Interactive logon: Prompt user to change ' - 'password before expiration', - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 999} + "PasswordExpiryWarning": { + "Policy": "Interactive logon: Prompt user to change " + "password before expiration", + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 999}, }, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\' - 'CurrentVersion\\Winlogon', - 'Value': 'PasswordExpiryWarning', - 'Type': 'REG_DWORD', + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows NT\\" + "CurrentVersion\\Winlogon", + "Value": "PasswordExpiryWarning", + "Type": "REG_DWORD", }, }, - 'MaxDevicePasswordFailedAttempts': { - 'Policy': 'Interactive logon: Machine account lockout ' - 'threshold', - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 999} + "MaxDevicePasswordFailedAttempts": { + "Policy": "Interactive logon: Machine account lockout " + "threshold", + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 999}, }, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\' - 'CurrentVersion\\policies\\system', - 'Value': 'MaxDevicePasswordFailedAttempts', - 'Type': 'REG_DWORD', + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SOFTWARE\\Microsoft\\Windows\\" + "CurrentVersion\\policies\\system", + "Value": "MaxDevicePasswordFailedAttempts", + "Type": "REG_DWORD", }, }, - 'InactivityTimeoutSecs': { - 'Policy': 'Interactive logon: Machine inactivity limit', - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 599940} + "InactivityTimeoutSecs": { + "Policy": "Interactive logon: Machine inactivity limit", + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 599940}, }, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\' - 'CurrentVersion\\policies\\system', - 'Value': 'InactivityTimeoutSecs', - 'Type': 'REG_DWORD', + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SOFTWARE\\Microsoft\\Windows\\" + "CurrentVersion\\policies\\system", + "Value": "InactivityTimeoutSecs", + "Type": "REG_DWORD", }, }, - 'legalnoticetext': { - 'Policy': 'Interactive logon: Message text for users ' - 'attempting to log on', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\' - 'CurrentVersion\\policies\\system', - 'Value': 'legalnoticetext', - 'Type': 'REG_SZ', + "legalnoticetext": { + "Policy": "Interactive logon: Message text for users " + "attempting to log on", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SOFTWARE\\Microsoft\\Windows\\" + "CurrentVersion\\policies\\system", + "Value": "legalnoticetext", + "Type": "REG_SZ", }, - 'Transform': { - 'Put': '_string_put_transform' - } + "Transform": {"Put": "_string_put_transform"}, }, - 'legalnoticecaption': { - 'Policy': 'Interactive logon: Message title for users ' - 'attempting to log on', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\' - 'CurrentVersion\\policies\\system', - 'Value': 'legalnoticecaption', - 'Type': 'REG_SZ', + "legalnoticecaption": { + "Policy": "Interactive logon: Message title for users " + "attempting to log on", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SOFTWARE\\Microsoft\\Windows\\" + "CurrentVersion\\policies\\system", + "Value": "legalnoticecaption", + "Type": "REG_SZ", }, - 'Transform': { - 'Put': '_string_put_transform' - } + "Transform": {"Put": "_string_put_transform"}, }, - 'DontDisplayLockedUserId': { - 'Policy': 'Interactive logon: Display user information ' - 'when the session is locked', - 'Settings': self.locked_session_user_info.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\' - 'CurrentVersion\\policies\\system', - 'Value': 'DontDisplayLockedUserId', - 'Type': 'REG_DWORD', + "DontDisplayLockedUserId": { + "Policy": "Interactive logon: Display user information " + "when the session is locked", + "Settings": self.locked_session_user_info.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SOFTWARE\\Microsoft\\Windows\\" + "CurrentVersion\\policies\\system", + "Value": "DontDisplayLockedUserId", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.locked_session_user_info, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.locked_session_user_info, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.locked_session_user_info, - 'value_lookup': True, - }, - } - }, - 'ScForceOption': { - 'Policy': 'Interactive logon: Require smart card', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'ScForceOption', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_transform, - }, - 'Client_RequireSecuritySignature': { - 'Policy': 'Microsoft network client: Digitally sign ' - 'communications (always)', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' - 'LanmanWorkstation\\Parameters', - 'Value': 'RequireSecuritySignature', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_transform, - }, - 'Client_EnableSecuritySignature': { - 'Policy': 'Microsoft network client: Digitally sign ' - 'communications (if server agrees)', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' - 'LanmanWorkstation\\Parameters', - 'Value': 'EnableSecuritySignature', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_transform, - }, - 'EnablePlainTextPassword': { - 'Policy': 'Microsoft network client: Send unencrypted ' - 'password to third-party SMB servers', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' - 'LanmanWorkstation\\Parameters', - 'Value': 'EnablePlainTextPassword', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_transform, - }, - 'AutoDisconnect': { - 'Policy': 'Microsoft network server: Amount of idle ' - 'time required before suspending session', - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 99999} - }, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'LanmanServer\\Parameters', - 'Value': 'AutoDisconnect', - 'Type': 'REG_DWORD', - }, - }, - 'EnableS4U2SelfForClaims': { - 'Policy': 'Microsoft network server: Attempt S4U2Self ' - 'to obtain claim information', - 'Settings': self.s4u2self_options.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'LanmanServer\\Parameters', - 'Value': 'EnableS4U2SelfForClaims', - 'Type': 'REG_DWORD', - }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.s4u2self_options, - 'value_lookup': False, - }, - 'PutArgs': { - 'lookup': self.s4u2self_options, - 'value_lookup': True, + "PutArgs": { + "lookup": self.locked_session_user_info, + "value_lookup": True, }, }, }, - 'Server_RequireSecuritySignature': { - 'Policy': 'Microsoft network server: Digitally sign ' - 'communications (always)', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' - 'LanmanServer\\Parameters', - 'Value': 'RequireSecuritySignature', - 'Type': 'REG_DWORD', + "ScForceOption": { + "Policy": "Interactive logon: Require smart card", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "ScForceOption", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'Server_EnableSecuritySignature': { - 'Policy': 'Microsoft network server: Digitally sign ' - 'communications (if client agrees)', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' - 'LanmanServer\\Parameters', - 'Value': 'EnableSecuritySignature', - 'Type': 'REG_DWORD', + "Client_RequireSecuritySignature": { + "Policy": "Microsoft network client: Digitally sign " + "communications (always)", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\" + "LanmanWorkstation\\Parameters", + "Value": "RequireSecuritySignature", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'EnableForcedLogoff': { - 'Policy': 'Microsoft network server: Disconnect ' - 'clients when logon hours expire', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' - 'LanmanServer\\Parameters', - 'Value': 'EnableForcedLogoff', - 'Type': 'REG_DWORD', + "Client_EnableSecuritySignature": { + "Policy": "Microsoft network client: Digitally sign " + "communications (if server agrees)", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\" + "LanmanWorkstation\\Parameters", + "Value": "EnableSecuritySignature", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'SmbServerNameHardeningLevel': { - 'Policy': 'Microsoft network server: Server SPN target ' - 'name validation level', - 'Settings': self.smb_server_name_hardening_levels.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'LanmanServer\\Parameters', - 'Value': 'SmbServerNameHardeningLevel', - 'Type': 'REG_DWORD', + "EnablePlainTextPassword": { + "Policy": "Microsoft network client: Send unencrypted " + "password to third-party SMB servers", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\" + "LanmanWorkstation\\Parameters", + "Value": "EnablePlainTextPassword", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.smb_server_name_hardening_levels, - 'value_lookup': False, + "Transform": self.enabled_one_disabled_zero_transform, + }, + "AutoDisconnect": { + "Policy": "Microsoft network server: Amount of idle " + "time required before suspending session", + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 99999}, + }, + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "LanmanServer\\Parameters", + "Value": "AutoDisconnect", + "Type": "REG_DWORD", + }, + }, + "EnableS4U2SelfForClaims": { + "Policy": "Microsoft network server: Attempt S4U2Self " + "to obtain claim information", + "Settings": self.s4u2self_options.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "LanmanServer\\Parameters", + "Value": "EnableS4U2SelfForClaims", + "Type": "REG_DWORD", + }, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.s4u2self_options, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.smb_server_name_hardening_levels, - 'value_lookup': True, + "PutArgs": { + "lookup": self.s4u2self_options, + "value_lookup": True, }, - } - }, - 'FullPrivilegeAuditing': { - 'Policy': 'Audit: Audit the use of Backup and Restore ' - 'privilege', - 'Settings': [chr(0), chr(1)], - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\Lsa', - 'Value': 'FullPrivilegeAuditing', - 'Type': 'REG_BINARY', - }, - 'Transform': { - 'Get': '_binary_enable_zero_disable_one_conversion', - 'Put': '_binary_enable_zero_disable_one_reverse_conversion', }, }, - 'CrashOnAuditFail': { - 'Policy': 'Audit: Shut down system immediately if ' - 'unable to log security audits', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'CrashOnAuditFail', - 'Type': 'REG_DWORD', + "Server_RequireSecuritySignature": { + "Policy": "Microsoft network server: Digitally sign " + "communications (always)", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\" + "LanmanServer\\Parameters", + "Value": "RequireSecuritySignature", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'UndockWithoutLogon': { - 'Policy': 'Devices: Allow undock without having to log ' - 'on', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Policies\\System', - 'Value': 'UndockWithoutLogon', - 'Type': 'REG_DWORD', + "Server_EnableSecuritySignature": { + "Policy": "Microsoft network server: Digitally sign " + "communications (if client agrees)", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\" + "LanmanServer\\Parameters", + "Value": "EnableSecuritySignature", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'AddPrinterDrivers': { - 'Policy': 'Devices: Prevent users from installing ' - 'printer drivers', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\' - 'Print\\Providers\\LanMan Print Services\\' - 'Servers', - 'Value': 'AddPrinterDrivers', - 'Type': 'REG_DWORD', + "EnableForcedLogoff": { + "Policy": "Microsoft network server: Disconnect " + "clients when logon hours expire", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\" + "LanmanServer\\Parameters", + "Value": "EnableForcedLogoff", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'AllocateDASD': { - 'Policy': 'Devices: Allowed to format and eject ' - 'removable media', - 'Settings': ['9999', '0', '1', '2'], - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\' - 'CurrentVersion\\Winlogon', - 'Value': 'AllocateDASD', - 'Type': 'REG_SZ', + "SmbServerNameHardeningLevel": { + "Policy": "Microsoft network server: Server SPN target " + "name validation level", + "Settings": self.smb_server_name_hardening_levels.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "LanmanServer\\Parameters", + "Value": "SmbServerNameHardeningLevel", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dasd_conversion', - 'Put': '_dasd_reverse_conversion', + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.smb_server_name_hardening_levels, + "value_lookup": False, + }, + "PutArgs": { + "lookup": self.smb_server_name_hardening_levels, + "value_lookup": True, + }, }, }, - 'AllocateCDRoms': { - 'Policy': 'Devices: Restrict CD-ROM access to locally ' - 'logged-on user only', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\' - 'CurrentVersion\\Winlogon', - 'Value': 'AllocateCDRoms', - 'Type': 'REG_SZ', + "FullPrivilegeAuditing": { + "Policy": "Audit: Audit the use of Backup and Restore " + "privilege", + "Settings": [chr(0), chr(1)], + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\Lsa", + "Value": "FullPrivilegeAuditing", + "Type": "REG_BINARY", + }, + "Transform": { + "Get": "_binary_enable_zero_disable_one_conversion", + "Put": "_binary_enable_zero_disable_one_reverse_conversion", }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, }, - 'AllocateFloppies': { - 'Policy': 'Devices: Restrict floppy access to locally ' - 'logged-on user only', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\' - 'CurrentVersion\\Winlogon', - 'Value': 'AllocateFloppies', - 'Type': 'REG_SZ', + "CrashOnAuditFail": { + "Policy": "Audit: Shut down system immediately if " + "unable to log security audits", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "CrashOnAuditFail", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, + "Transform": self.enabled_one_disabled_zero_transform, + }, + "UndockWithoutLogon": { + "Policy": "Devices: Allow undock without having to log " "on", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Policies\\System", + "Value": "UndockWithoutLogon", + "Type": "REG_DWORD", + }, + "Transform": self.enabled_one_disabled_zero_transform, + }, + "AddPrinterDrivers": { + "Policy": "Devices: Prevent users from installing " + "printer drivers", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\" + "Print\\Providers\\LanMan Print Services\\" + "Servers", + "Value": "AddPrinterDrivers", + "Type": "REG_DWORD", + }, + "Transform": self.enabled_one_disabled_zero_strings_transform, + }, + "AllocateDASD": { + "Policy": "Devices: Allowed to format and eject " + "removable media", + "Settings": ["9999", "0", "1", "2"], + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows NT\\" + "CurrentVersion\\Winlogon", + "Value": "AllocateDASD", + "Type": "REG_SZ", + }, + "Transform": { + "Get": "_dasd_conversion", + "Put": "_dasd_reverse_conversion", + }, + }, + "AllocateCDRoms": { + "Policy": "Devices: Restrict CD-ROM access to locally " + "logged-on user only", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows NT\\" + "CurrentVersion\\Winlogon", + "Value": "AllocateCDRoms", + "Type": "REG_SZ", + }, + "Transform": self.enabled_one_disabled_zero_strings_transform, + }, + "AllocateFloppies": { + "Policy": "Devices: Restrict floppy access to locally " + "logged-on user only", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows NT\\" + "CurrentVersion\\Winlogon", + "Value": "AllocateFloppies", + "Type": "REG_SZ", + }, + "Transform": self.enabled_one_disabled_zero_strings_transform, }, # see KB298503 why we aren't just doing this one via the # registry - 'DriverSigningPolicy': { - 'Policy': 'Devices: Unsigned driver installation ' - 'behavior', - 'Settings': ['3,0', '3,' + chr(1), '3,' + chr(2)], - 'lgpo_section': self.security_options_gpedit_path, - 'Secedit': { - 'Option': 'MACHINE\\Software\\Microsoft\\Driver ' - 'Signing\\Policy', - 'Section': 'Registry Values', + "DriverSigningPolicy": { + "Policy": "Devices: Unsigned driver installation " "behavior", + "Settings": ["3,0", "3," + chr(1), "3," + chr(2)], + "lgpo_section": self.security_options_gpedit_path, + "Secedit": { + "Option": "MACHINE\\Software\\Microsoft\\Driver " + "Signing\\Policy", + "Section": "Registry Values", }, - 'Transform': { - 'Get': '_driver_signing_reg_conversion', - 'Put': '_driver_signing_reg_reverse_conversion', + "Transform": { + "Get": "_driver_signing_reg_conversion", + "Put": "_driver_signing_reg_reverse_conversion", }, }, - 'SubmitControl': { - 'Policy': 'Domain controller: Allow server operators ' - 'to schedule tasks', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\Lsa', - 'Value': 'SubmitControl', - 'Type': 'REG_DWORD', + "SubmitControl": { + "Policy": "Domain controller: Allow server operators " + "to schedule tasks", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\Lsa", + "Value": "SubmitControl", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, + "Transform": self.enabled_one_disabled_zero_strings_transform, }, - 'LDAPServerIntegrity': { - 'Policy': 'Domain controller: LDAP server signing ' - 'requirements', - 'Settings': self.ldap_server_signing_requirements.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\NTDS' - '\\Parameters', - 'Value': 'LDAPServerIntegrity', - 'Type': 'REG_DWORD', + "LDAPServerIntegrity": { + "Policy": "Domain controller: LDAP server signing " + "requirements", + "Settings": self.ldap_server_signing_requirements.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\NTDS" + "\\Parameters", + "Value": "LDAPServerIntegrity", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.ldap_server_signing_requirements, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.ldap_server_signing_requirements, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.ldap_server_signing_requirements, - 'value_lookup': True, + "PutArgs": { + "lookup": self.ldap_server_signing_requirements, + "value_lookup": True, }, - } - }, - 'RefusePasswordChange': { - 'Policy': 'Domain controller: Refuse machine account ' - 'password changes', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\' - 'Netlogon\\Parameters', - 'Value': 'RefusePasswordChange', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, - }, - 'RequireSignOrSeal': { - 'Policy': 'Domain member: Digitally encrypt or sign ' - 'secure channel data (always)', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'Netlogon\\Parameters', - 'Value': 'RequireSignOrSeal', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, - }, - 'SealSecureChannel': { - 'Policy': 'Domain member: Digitally encrypt secure ' - 'channel data (when possible)', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'Netlogon\\Parameters', - 'Value': 'SealSecureChannel', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, - }, - 'SignSecureChannel': { - 'Policy': 'Domain member: Digitally sign secure ' - 'channel data (when possible)', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'Netlogon\\Parameters', - 'Value': 'SignSecureChannel', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, - }, - 'DisablePasswordChange': { - 'Policy': 'Domain member: Disable machine account ' - 'password changes', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'Netlogon\\Parameters', - 'Value': 'DisablePasswordChange', - 'Type': 'REG_DWORD', - }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, - }, - 'MaximumPasswordAge': { - 'Policy': 'Domain member: Maximum machine account ' - 'password age', - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 999} - }, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'Netlogon\\Parameters', - 'Value': 'MaximumPasswordAge', - 'Type': 'REG_DWORD', }, }, - 'RequireStrongKey': { - 'Policy': 'Domain member: Require strong (Windows 2000 ' - 'or later) session key', - 'Settings': self.enabled_one_disabled_zero_strings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\' - 'Netlogon\\Parameters', - 'Value': 'RequireStrongKey', - 'Type': 'REG_DWORD', + "RefusePasswordChange": { + "Policy": "Domain controller: Refuse machine account " + "password changes", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\" + "Netlogon\\Parameters", + "Value": "RefusePasswordChange", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_strings_transform, + "Transform": self.enabled_one_disabled_zero_strings_transform, }, - 'LockoutDuration': { - 'Policy': 'Account lockout duration', - 'lgpo_section': self.account_lockout_policy_gpedit_path, - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 6000000} + "RequireSignOrSeal": { + "Policy": "Domain member: Digitally encrypt or sign " + "secure channel data (always)", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "Netlogon\\Parameters", + "Value": "RequireSignOrSeal", + "Type": "REG_DWORD", }, - 'NetUserModal': { - 'Modal': 3, - 'Option': 'lockout_duration', + "Transform": self.enabled_one_disabled_zero_strings_transform, + }, + "SealSecureChannel": { + "Policy": "Domain member: Digitally encrypt secure " + "channel data (when possible)", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "Netlogon\\Parameters", + "Value": "SealSecureChannel", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_seconds_to_minutes', - 'Put': '_minutes_to_seconds', + "Transform": self.enabled_one_disabled_zero_strings_transform, + }, + "SignSecureChannel": { + "Policy": "Domain member: Digitally sign secure " + "channel data (when possible)", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "Netlogon\\Parameters", + "Value": "SignSecureChannel", + "Type": "REG_DWORD", + }, + "Transform": self.enabled_one_disabled_zero_strings_transform, + }, + "DisablePasswordChange": { + "Policy": "Domain member: Disable machine account " + "password changes", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "Netlogon\\Parameters", + "Value": "DisablePasswordChange", + "Type": "REG_DWORD", + }, + "Transform": self.enabled_one_disabled_zero_strings_transform, + }, + "MaximumPasswordAge": { + "Policy": "Domain member: Maximum machine account " + "password age", + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 999}, + }, + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "Netlogon\\Parameters", + "Value": "MaximumPasswordAge", + "Type": "REG_DWORD", }, }, - 'LockoutThreshold': { - 'Policy': 'Account lockout threshold', - 'lgpo_section': self.account_lockout_policy_gpedit_path, - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 1000} + "RequireStrongKey": { + "Policy": "Domain member: Require strong (Windows 2000 " + "or later) session key", + "Settings": self.enabled_one_disabled_zero_strings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\" + "Netlogon\\Parameters", + "Value": "RequireStrongKey", + "Type": "REG_DWORD", }, - 'NetUserModal': { - 'Modal': 3, - 'Option': 'lockout_threshold', - } + "Transform": self.enabled_one_disabled_zero_strings_transform, }, - 'LockoutWindow': { - 'Policy': 'Reset account lockout counter after', - 'lgpo_section': self.account_lockout_policy_gpedit_path, - 'Settings': { - 'Function': '_in_range_inclusive', - 'Args': {'min': 0, 'max': 6000000} + "LockoutDuration": { + "Policy": "Account lockout duration", + "lgpo_section": self.account_lockout_policy_gpedit_path, + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 6000000}, }, - 'NetUserModal': { - 'Modal': 3, - 'Option': 'lockout_observation_window', + "NetUserModal": {"Modal": 3, "Option": "lockout_duration"}, + "Transform": { + "Get": "_seconds_to_minutes", + "Put": "_minutes_to_seconds", }, - 'Transform': { - 'Get': '_seconds_to_minutes', - 'Put': '_minutes_to_seconds' + }, + "LockoutThreshold": { + "Policy": "Account lockout threshold", + "lgpo_section": self.account_lockout_policy_gpedit_path, + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 1000}, + }, + "NetUserModal": {"Modal": 3, "Option": "lockout_threshold"}, + }, + "LockoutWindow": { + "Policy": "Reset account lockout counter after", + "lgpo_section": self.account_lockout_policy_gpedit_path, + "Settings": { + "Function": "_in_range_inclusive", + "Args": {"min": 0, "max": 6000000}, + }, + "NetUserModal": { + "Modal": 3, + "Option": "lockout_observation_window", + }, + "Transform": { + "Get": "_seconds_to_minutes", + "Put": "_minutes_to_seconds", }, }, ########## LEGACY AUDIT POLICIES ########## @@ -2656,95 +2653,95 @@ class _policy_info(object): # "Audit: Force audit policy subcategory settings (Windows Vista or later) to override audit policy category settings" # or its alias... # SceNoApplyLegacyAuditPolicy - 'AuditAccountLogon': { - 'Policy': 'Audit account logon events', - 'lgpo_section': self.audit_policy_gpedit_path, - 'Settings': self.audit_lookup.keys(), - 'Secedit': { - 'Option': 'AuditAccountLogon', - 'Section': 'Event Audit', + "AuditAccountLogon": { + "Policy": "Audit account logon events", + "lgpo_section": self.audit_policy_gpedit_path, + "Settings": self.audit_lookup.keys(), + "Secedit": { + "Option": "AuditAccountLogon", + "Section": "Event Audit", }, - 'Transform': self.audit_transform, + "Transform": self.audit_transform, }, - 'AuditAccountManage': { - 'Policy': 'Audit account management', - 'lgpo_section': self.audit_policy_gpedit_path, - 'Settings': self.audit_lookup.keys(), - 'Secedit': { - 'Option': 'AuditAccountManage', - 'Section': 'Event Audit', + "AuditAccountManage": { + "Policy": "Audit account management", + "lgpo_section": self.audit_policy_gpedit_path, + "Settings": self.audit_lookup.keys(), + "Secedit": { + "Option": "AuditAccountManage", + "Section": "Event Audit", }, - 'Transform': self.audit_transform, + "Transform": self.audit_transform, }, - 'AuditDSAccess': { - 'Policy': 'Audit directory service access', - 'lgpo_section': self.audit_policy_gpedit_path, - 'Settings': self.audit_lookup.keys(), - 'Secedit': { - 'Option': 'AuditDSAccess', - 'Section': 'Event Audit', + "AuditDSAccess": { + "Policy": "Audit directory service access", + "lgpo_section": self.audit_policy_gpedit_path, + "Settings": self.audit_lookup.keys(), + "Secedit": { + "Option": "AuditDSAccess", + "Section": "Event Audit", }, - 'Transform': self.audit_transform, + "Transform": self.audit_transform, }, - 'AuditLogonEvents': { - 'Policy': 'Audit logon events', - 'lgpo_section': self.audit_policy_gpedit_path, - 'Settings': self.audit_lookup.keys(), - 'Secedit': { - 'Option': 'AuditLogonEvents', - 'Section': 'Event Audit', + "AuditLogonEvents": { + "Policy": "Audit logon events", + "lgpo_section": self.audit_policy_gpedit_path, + "Settings": self.audit_lookup.keys(), + "Secedit": { + "Option": "AuditLogonEvents", + "Section": "Event Audit", }, - 'Transform': self.audit_transform, + "Transform": self.audit_transform, }, - 'AuditObjectAccess': { - 'Policy': 'Audit object access', - 'lgpo_section': self.audit_policy_gpedit_path, - 'Settings': self.audit_lookup.keys(), - 'Secedit': { - 'Option': 'AuditObjectAccess', - 'Section': 'Event Audit', + "AuditObjectAccess": { + "Policy": "Audit object access", + "lgpo_section": self.audit_policy_gpedit_path, + "Settings": self.audit_lookup.keys(), + "Secedit": { + "Option": "AuditObjectAccess", + "Section": "Event Audit", }, - 'Transform': self.audit_transform, + "Transform": self.audit_transform, }, - 'AuditPolicyChange': { - 'Policy': 'Audit policy change', - 'lgpo_section': self.audit_policy_gpedit_path, - 'Settings': self.audit_lookup.keys(), - 'Secedit': { - 'Option': 'AuditPolicyChange', - 'Section': 'Event Audit', + "AuditPolicyChange": { + "Policy": "Audit policy change", + "lgpo_section": self.audit_policy_gpedit_path, + "Settings": self.audit_lookup.keys(), + "Secedit": { + "Option": "AuditPolicyChange", + "Section": "Event Audit", }, - 'Transform': self.audit_transform, + "Transform": self.audit_transform, }, - 'AuditPrivilegeUse': { - 'Policy': 'Audit privilege use', - 'lgpo_section': self.audit_policy_gpedit_path, - 'Settings': self.audit_lookup.keys(), - 'Secedit': { - 'Option': 'AuditPrivilegeUse', - 'Section': 'Event Audit', + "AuditPrivilegeUse": { + "Policy": "Audit privilege use", + "lgpo_section": self.audit_policy_gpedit_path, + "Settings": self.audit_lookup.keys(), + "Secedit": { + "Option": "AuditPrivilegeUse", + "Section": "Event Audit", }, - 'Transform': self.audit_transform, + "Transform": self.audit_transform, }, - 'AuditProcessTracking': { - 'Policy': 'Audit process tracking', - 'lgpo_section': self.audit_policy_gpedit_path, - 'Settings': self.audit_lookup.keys(), - 'Secedit': { - 'Option': 'AuditProcessTracking', - 'Section': 'Event Audit', + "AuditProcessTracking": { + "Policy": "Audit process tracking", + "lgpo_section": self.audit_policy_gpedit_path, + "Settings": self.audit_lookup.keys(), + "Secedit": { + "Option": "AuditProcessTracking", + "Section": "Event Audit", }, - 'Transform': self.audit_transform, + "Transform": self.audit_transform, }, - 'AuditSystemEvents': { - 'Policy': 'Audit system events', - 'lgpo_section': self.audit_policy_gpedit_path, - 'Settings': self.audit_lookup.keys(), - 'Secedit': { - 'Option': 'AuditSystemEvents', - 'Section': 'Event Audit', + "AuditSystemEvents": { + "Policy": "Audit system events", + "lgpo_section": self.audit_policy_gpedit_path, + "Settings": self.audit_lookup.keys(), + "Secedit": { + "Option": "AuditSystemEvents", + "Section": "Event Audit", }, - 'Transform': self.audit_transform, + "Transform": self.audit_transform, }, ########## END OF LEGACY AUDIT POLICIES ########## ########## ADVANCED AUDIT POLICIES ########## @@ -2755,1665 +2752,1470 @@ class _policy_info(object): # settings" # or its alias... # SceNoApplyLegacyAuditPolicy - # Account Logon Section - 'AuditCredentialValidation': { - 'Policy': 'Audit Credential Validation', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Credential Validation', - }, - 'Transform': self.advanced_audit_transform, + "AuditCredentialValidation": { + "Policy": "Audit Credential Validation", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Credential Validation"}, + "Transform": self.advanced_audit_transform, }, - 'AuditKerberosAuthenticationService': { - 'Policy': 'Audit Kerberos Authentication Service', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Kerberos Authentication Service', + "AuditKerberosAuthenticationService": { + "Policy": "Audit Kerberos Authentication Service", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": { + "Option": "Audit Kerberos Authentication Service", }, - 'Transform': self.advanced_audit_transform, + "Transform": self.advanced_audit_transform, }, - 'AuditKerberosServiceTicketOperations': { - 'Policy': 'Audit Kerberos Service Ticket Operations', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Kerberos Service Ticket Operations', + "AuditKerberosServiceTicketOperations": { + "Policy": "Audit Kerberos Service Ticket Operations", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": { + "Option": "Audit Kerberos Service Ticket Operations", }, - 'Transform': self.advanced_audit_transform, + "Transform": self.advanced_audit_transform, }, - 'AuditOtherAccountLogonEvents': { - 'Policy': 'Audit Other Account Logon Events', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Other Account Logon Events', - }, - 'Transform': self.advanced_audit_transform, + "AuditOtherAccountLogonEvents": { + "Policy": "Audit Other Account Logon Events", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Other Account Logon Events"}, + "Transform": self.advanced_audit_transform, }, # Account Management Section - 'AuditApplicationGroupManagement': { - 'Policy': 'Audit Application Group Management', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Application Group Management', - }, - 'Transform': self.advanced_audit_transform, + "AuditApplicationGroupManagement": { + "Policy": "Audit Application Group Management", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Application Group Management"}, + "Transform": self.advanced_audit_transform, }, - 'AuditComputerAccountManagement': { - 'Policy': 'Audit Computer Account Management', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Computer Account Management', - }, - 'Transform': self.advanced_audit_transform, + "AuditComputerAccountManagement": { + "Policy": "Audit Computer Account Management", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Computer Account Management"}, + "Transform": self.advanced_audit_transform, }, - 'AuditDistributionGroupManagement': { - 'Policy': 'Audit Distribution Group Management', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Distribution Group Management', - }, - 'Transform': self.advanced_audit_transform, + "AuditDistributionGroupManagement": { + "Policy": "Audit Distribution Group Management", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Distribution Group Management"}, + "Transform": self.advanced_audit_transform, }, - 'AuditOtherAccountManagementEvents': { - 'Policy': 'Audit Other Account Management Events', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Other Account Management Events', + "AuditOtherAccountManagementEvents": { + "Policy": "Audit Other Account Management Events", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": { + "Option": "Audit Other Account Management Events", }, - 'Transform': self.advanced_audit_transform, + "Transform": self.advanced_audit_transform, }, - 'AuditSecurityGroupManagement': { - 'Policy': 'Audit Security Group Management', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Security Group Management', - }, - 'Transform': self.advanced_audit_transform, + "AuditSecurityGroupManagement": { + "Policy": "Audit Security Group Management", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Security Group Management"}, + "Transform": self.advanced_audit_transform, }, - 'AuditUserAccountManagement': { - 'Policy': 'Audit User Account Management', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit User Account Management', - }, - 'Transform': self.advanced_audit_transform, + "AuditUserAccountManagement": { + "Policy": "Audit User Account Management", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit User Account Management"}, + "Transform": self.advanced_audit_transform, }, # Detailed Tracking Settings - 'AuditDPAPIActivity': { - 'Policy': 'Audit DPAPI Activity', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit DPAPI Activity', - }, - 'Transform': self.advanced_audit_transform, + "AuditDPAPIActivity": { + "Policy": "Audit DPAPI Activity", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit DPAPI Activity"}, + "Transform": self.advanced_audit_transform, }, - 'AuditPNPActivity': { - 'Policy': 'Audit PNP Activity', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit PNP Activity', - }, - 'Transform': self.advanced_audit_transform, + "AuditPNPActivity": { + "Policy": "Audit PNP Activity", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit PNP Activity"}, + "Transform": self.advanced_audit_transform, }, - 'AuditProcessCreation': { - 'Policy': 'Audit Process Creation', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Process Creation', - }, - 'Transform': self.advanced_audit_transform, + "AuditProcessCreation": { + "Policy": "Audit Process Creation", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Process Creation"}, + "Transform": self.advanced_audit_transform, }, - 'AuditProcessTermination': { - 'Policy': 'Audit Process Termination', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Process Termination', - }, - 'Transform': self.advanced_audit_transform, + "AuditProcessTermination": { + "Policy": "Audit Process Termination", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Process Termination"}, + "Transform": self.advanced_audit_transform, }, - 'AuditRPCEvents': { - 'Policy': 'Audit RPC Events', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit RPC Events', - }, - 'Transform': self.advanced_audit_transform, + "AuditRPCEvents": { + "Policy": "Audit RPC Events", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit RPC Events"}, + "Transform": self.advanced_audit_transform, }, - 'AuditTokenRightAdjusted': { - 'Policy': 'Audit Token Right Adjusted', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Token Right Adjusted', - }, - 'Transform': self.advanced_audit_transform, + "AuditTokenRightAdjusted": { + "Policy": "Audit Token Right Adjusted", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Token Right Adjusted"}, + "Transform": self.advanced_audit_transform, }, # DS Access Section - 'AuditDetailedDirectoryServiceReplication': { - 'Policy': 'Audit Detailed Directory Service Replication', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Detailed Directory Service Replication', + "AuditDetailedDirectoryServiceReplication": { + "Policy": "Audit Detailed Directory Service Replication", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": { + "Option": "Audit Detailed Directory Service Replication", }, - 'Transform': self.advanced_audit_transform, + "Transform": self.advanced_audit_transform, }, - 'AuditDirectoryServiceAccess': { - 'Policy': 'Audit Directory Service Access', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Directory Service Access', - }, - 'Transform': self.advanced_audit_transform, + "AuditDirectoryServiceAccess": { + "Policy": "Audit Directory Service Access", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Directory Service Access"}, + "Transform": self.advanced_audit_transform, }, - 'AuditDirectoryServiceChanges': { - 'Policy': 'Audit Directory Service Changes', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Directory Service Changes', - }, - 'Transform': self.advanced_audit_transform, + "AuditDirectoryServiceChanges": { + "Policy": "Audit Directory Service Changes", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Directory Service Changes"}, + "Transform": self.advanced_audit_transform, }, - 'AuditDirectoryServiceReplication': { - 'Policy': 'Audit Directory Service Replication', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Directory Service Replication', - }, - 'Transform': self.advanced_audit_transform, + "AuditDirectoryServiceReplication": { + "Policy": "Audit Directory Service Replication", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Directory Service Replication"}, + "Transform": self.advanced_audit_transform, }, # Logon/Logoff Section - 'AuditAccountLockout': { - 'Policy': 'Audit Account Lockout', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Account Lockout', - }, - 'Transform': self.advanced_audit_transform, + "AuditAccountLockout": { + "Policy": "Audit Account Lockout", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Account Lockout"}, + "Transform": self.advanced_audit_transform, }, - 'AuditUserDeviceClaims': { - 'Policy': 'Audit User / Device Claims', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit User / Device Claims', - }, - 'Transform': self.advanced_audit_transform, + "AuditUserDeviceClaims": { + "Policy": "Audit User / Device Claims", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit User / Device Claims"}, + "Transform": self.advanced_audit_transform, }, - 'AuditGroupMembership': { - 'Policy': 'Audit Group Membership', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Group Membership', - }, - 'Transform': self.advanced_audit_transform, + "AuditGroupMembership": { + "Policy": "Audit Group Membership", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Group Membership"}, + "Transform": self.advanced_audit_transform, }, - 'AuditIPsecExtendedMode': { - 'Policy': 'Audit IPsec Extended Mode', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit IPsec Extended Mode', - }, - 'Transform': self.advanced_audit_transform, + "AuditIPsecExtendedMode": { + "Policy": "Audit IPsec Extended Mode", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit IPsec Extended Mode"}, + "Transform": self.advanced_audit_transform, }, - 'AuditIPsecMainMode': { - 'Policy': 'Audit IPsec Main Mode', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit IPsec Main Mode', - }, - 'Transform': self.advanced_audit_transform, + "AuditIPsecMainMode": { + "Policy": "Audit IPsec Main Mode", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit IPsec Main Mode"}, + "Transform": self.advanced_audit_transform, }, - 'AuditIPsecQuickMode': { - 'Policy': 'Audit IPsec Quick Mode', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit IPsec Quick Mode', - }, - 'Transform': self.advanced_audit_transform, + "AuditIPsecQuickMode": { + "Policy": "Audit IPsec Quick Mode", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit IPsec Quick Mode"}, + "Transform": self.advanced_audit_transform, }, - 'AuditLogoff': { - 'Policy': 'Audit Logoff', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Logoff', - }, - 'Transform': self.advanced_audit_transform, + "AuditLogoff": { + "Policy": "Audit Logoff", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Logoff"}, + "Transform": self.advanced_audit_transform, }, - 'AuditLogon': { - 'Policy': 'Audit Logon', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Logon', - }, - 'Transform': self.advanced_audit_transform, + "AuditLogon": { + "Policy": "Audit Logon", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Logon"}, + "Transform": self.advanced_audit_transform, }, - 'AuditNetworkPolicyServer': { - 'Policy': 'Audit Network Policy Server', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Network Policy Server', - }, - 'Transform': self.advanced_audit_transform, + "AuditNetworkPolicyServer": { + "Policy": "Audit Network Policy Server", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Network Policy Server"}, + "Transform": self.advanced_audit_transform, }, - 'AuditOtherLogonLogoffEvents': { - 'Policy': 'Audit Other Logon/Logoff Events', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Other Logon/Logoff Events', - }, - 'Transform': self.advanced_audit_transform, + "AuditOtherLogonLogoffEvents": { + "Policy": "Audit Other Logon/Logoff Events", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Other Logon/Logoff Events"}, + "Transform": self.advanced_audit_transform, }, - 'AuditSpecialLogon': { - 'Policy': 'Audit Special Logon', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Special Logon', - }, - 'Transform': self.advanced_audit_transform, + "AuditSpecialLogon": { + "Policy": "Audit Special Logon", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Special Logon"}, + "Transform": self.advanced_audit_transform, }, # Object Access Section - 'AuditApplicationGenerated': { - 'Policy': 'Audit Application Generated', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Application Generated', - }, - 'Transform': self.advanced_audit_transform, + "AuditApplicationGenerated": { + "Policy": "Audit Application Generated", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Application Generated"}, + "Transform": self.advanced_audit_transform, }, - 'AuditCertificationServices': { - 'Policy': 'Audit Certification Services', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Certification Services', - }, - 'Transform': self.advanced_audit_transform, + "AuditCertificationServices": { + "Policy": "Audit Certification Services", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Certification Services"}, + "Transform": self.advanced_audit_transform, }, - 'AuditDetailedFileShare': { - 'Policy': 'Audit Detailed File Share', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Detailed File Share', - }, - 'Transform': self.advanced_audit_transform, + "AuditDetailedFileShare": { + "Policy": "Audit Detailed File Share", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Detailed File Share"}, + "Transform": self.advanced_audit_transform, }, - 'AuditFileShare': { - 'Policy': 'Audit File Share', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit File Share', - }, - 'Transform': self.advanced_audit_transform, + "AuditFileShare": { + "Policy": "Audit File Share", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit File Share"}, + "Transform": self.advanced_audit_transform, }, - 'AuditFileSystem': { - 'Policy': 'Audit File System', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit File System', - }, - 'Transform': self.advanced_audit_transform, + "AuditFileSystem": { + "Policy": "Audit File System", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit File System"}, + "Transform": self.advanced_audit_transform, }, - 'AuditFilteringPlatformConnection': { - 'Policy': 'Audit Filtering Platform Connection', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Filtering Platform Connection', - }, - 'Transform': self.advanced_audit_transform, + "AuditFilteringPlatformConnection": { + "Policy": "Audit Filtering Platform Connection", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Filtering Platform Connection"}, + "Transform": self.advanced_audit_transform, }, - 'AuditFilteringPlatformPacketDrop': { - 'Policy': 'Audit Filtering Platform Packet Drop', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Filtering Platform Packet Drop', - }, - 'Transform': self.advanced_audit_transform, + "AuditFilteringPlatformPacketDrop": { + "Policy": "Audit Filtering Platform Packet Drop", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Filtering Platform Packet Drop"}, + "Transform": self.advanced_audit_transform, }, - 'AuditHandleManipulation': { - 'Policy': 'Audit Handle Manipulation', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Handle Manipulation', - }, - 'Transform': self.advanced_audit_transform, + "AuditHandleManipulation": { + "Policy": "Audit Handle Manipulation", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Handle Manipulation"}, + "Transform": self.advanced_audit_transform, }, - 'AuditKernelObject': { - 'Policy': 'Audit Kernel Object', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Kernel Object', - }, - 'Transform': self.advanced_audit_transform, + "AuditKernelObject": { + "Policy": "Audit Kernel Object", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Kernel Object"}, + "Transform": self.advanced_audit_transform, }, - 'AuditOtherObjectAccessEvents': { - 'Policy': 'Audit Other Object Access Events', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Other Object Access Events', - }, - 'Transform': self.advanced_audit_transform, + "AuditOtherObjectAccessEvents": { + "Policy": "Audit Other Object Access Events", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Other Object Access Events"}, + "Transform": self.advanced_audit_transform, }, - 'AuditRegistry': { - 'Policy': 'Audit Registry', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Registry', - }, - 'Transform': self.advanced_audit_transform, + "AuditRegistry": { + "Policy": "Audit Registry", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Registry"}, + "Transform": self.advanced_audit_transform, }, - 'AuditRemovableStorage': { - 'Policy': 'Audit Removable Storage', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Removable Storage', - }, - 'Transform': self.advanced_audit_transform, + "AuditRemovableStorage": { + "Policy": "Audit Removable Storage", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Removable Storage"}, + "Transform": self.advanced_audit_transform, }, - 'AuditSAM': { - 'Policy': 'Audit SAM', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit SAM', - }, - 'Transform': self.advanced_audit_transform, + "AuditSAM": { + "Policy": "Audit SAM", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit SAM"}, + "Transform": self.advanced_audit_transform, }, - 'AuditCentralAccessPolicyStaging': { - 'Policy': 'Audit Central Access Policy Staging', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Central Access Policy Staging', - }, - 'Transform': self.advanced_audit_transform, + "AuditCentralAccessPolicyStaging": { + "Policy": "Audit Central Access Policy Staging", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Central Access Policy Staging"}, + "Transform": self.advanced_audit_transform, }, # Policy Change Section - 'AuditAuditPolicyChange': { - 'Policy': 'Audit Audit Policy Change', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Audit Policy Change', - }, - 'Transform': self.advanced_audit_transform, + "AuditAuditPolicyChange": { + "Policy": "Audit Audit Policy Change", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Audit Policy Change"}, + "Transform": self.advanced_audit_transform, }, - 'AuditAuthenticationPolicyChange': { - 'Policy': 'Audit Authentication Policy Change', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Authentication Policy Change', - }, - 'Transform': self.advanced_audit_transform, + "AuditAuthenticationPolicyChange": { + "Policy": "Audit Authentication Policy Change", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Authentication Policy Change"}, + "Transform": self.advanced_audit_transform, }, - 'AuditAuthorizationPolicyChange': { - 'Policy': 'Audit Authorization Policy Change', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Authorization Policy Change', - }, - 'Transform': self.advanced_audit_transform, + "AuditAuthorizationPolicyChange": { + "Policy": "Audit Authorization Policy Change", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Authorization Policy Change"}, + "Transform": self.advanced_audit_transform, }, - 'AuditFilteringPlatformPolicyChange': { - 'Policy': 'Audit Filtering Platform Policy Change', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Filtering Platform Policy Change', + "AuditFilteringPlatformPolicyChange": { + "Policy": "Audit Filtering Platform Policy Change", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": { + "Option": "Audit Filtering Platform Policy Change", }, - 'Transform': self.advanced_audit_transform, + "Transform": self.advanced_audit_transform, }, - 'AuditMPSSVCRuleLevelPolicyChange': { - 'Policy': 'Audit MPSSVC Rule-Level Policy Change', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit MPSSVC Rule-Level Policy Change', + "AuditMPSSVCRuleLevelPolicyChange": { + "Policy": "Audit MPSSVC Rule-Level Policy Change", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": { + "Option": "Audit MPSSVC Rule-Level Policy Change", }, - 'Transform': self.advanced_audit_transform, + "Transform": self.advanced_audit_transform, }, - 'AuditOtherPolicyChangeEvents': { - 'Policy': 'Audit Other Policy Change Events', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Other Policy Change Events', - }, - 'Transform': self.advanced_audit_transform, + "AuditOtherPolicyChangeEvents": { + "Policy": "Audit Other Policy Change Events", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Other Policy Change Events"}, + "Transform": self.advanced_audit_transform, }, # Privilege Use Section - 'AuditNonSensitivePrivilegeUse': { - 'Policy': 'Audit Non Sensitive Privilege Use', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Non Sensitive Privilege Use', - }, - 'Transform': self.advanced_audit_transform, + "AuditNonSensitivePrivilegeUse": { + "Policy": "Audit Non Sensitive Privilege Use", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Non Sensitive Privilege Use"}, + "Transform": self.advanced_audit_transform, }, - 'AuditOtherPrivilegeUseEvents': { - 'Policy': 'Audit Other Privilege Use Events', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Other Privilege Use Events', - }, - 'Transform': self.advanced_audit_transform, + "AuditOtherPrivilegeUseEvents": { + "Policy": "Audit Other Privilege Use Events", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Other Privilege Use Events"}, + "Transform": self.advanced_audit_transform, }, - 'AuditSensitivePrivilegeUse': { - 'Policy': 'Audit Sensitive Privilege Use', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Sensitive Privilege Use', - }, - 'Transform': self.advanced_audit_transform, + "AuditSensitivePrivilegeUse": { + "Policy": "Audit Sensitive Privilege Use", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Sensitive Privilege Use"}, + "Transform": self.advanced_audit_transform, }, # System Section - 'AuditIPsecDriver': { - 'Policy': 'Audit IPsec Driver', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit IPsec Driver', - }, - 'Transform': self.advanced_audit_transform, + "AuditIPsecDriver": { + "Policy": "Audit IPsec Driver", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit IPsec Driver"}, + "Transform": self.advanced_audit_transform, }, - 'AuditOtherSystemEvents': { - 'Policy': 'Audit Other System Events', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Other System Events', - }, - 'Transform': self.advanced_audit_transform, + "AuditOtherSystemEvents": { + "Policy": "Audit Other System Events", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Other System Events"}, + "Transform": self.advanced_audit_transform, }, - 'AuditSecurityStateChange': { - 'Policy': 'Audit Security State Change', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Security State Change', - }, - 'Transform': self.advanced_audit_transform, + "AuditSecurityStateChange": { + "Policy": "Audit Security State Change", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Security State Change"}, + "Transform": self.advanced_audit_transform, }, - 'AuditSecuritySystemExtension': { - 'Policy': 'Audit Security System Extension', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit Security System Extension', - }, - 'Transform': self.advanced_audit_transform, + "AuditSecuritySystemExtension": { + "Policy": "Audit Security System Extension", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit Security System Extension"}, + "Transform": self.advanced_audit_transform, }, - 'AuditSystemIntegrity': { - 'Policy': 'Audit System Integrity', - 'lgpo_section': self.advanced_audit_policy_gpedit_path, - 'Settings': self.advanced_audit_lookup.keys(), - 'AdvAudit': { - 'Option': 'Audit System Integrity', - }, - 'Transform': self.advanced_audit_transform, + "AuditSystemIntegrity": { + "Policy": "Audit System Integrity", + "lgpo_section": self.advanced_audit_policy_gpedit_path, + "Settings": self.advanced_audit_lookup.keys(), + "AdvAudit": {"Option": "Audit System Integrity"}, + "Transform": self.advanced_audit_transform, }, ########## END OF ADVANCED AUDIT POLICIES ########## - 'SeTrustedCredManAccessPrivilege': { - 'Policy': 'Access Credential Manager as a trusted ' - 'caller', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeTrustedCredManAccessPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeTrustedCredManAccessPrivilege": { + "Policy": "Access Credential Manager as a trusted " "caller", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeTrustedCredManAccessPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeNetworkLogonRight': { - 'Policy': 'Access this computer from the network', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeNetworkLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeNetworkLogonRight": { + "Policy": "Access this computer from the network", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeNetworkLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeTcbPrivilege': { - 'Policy': 'Act as part of the operating system', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeTcbPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeTcbPrivilege": { + "Policy": "Act as part of the operating system", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeTcbPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeMachineAccountPrivilege': { - 'Policy': 'Add workstations to domain', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeMachineAccountPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeMachineAccountPrivilege": { + "Policy": "Add workstations to domain", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeMachineAccountPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeIncreaseQuotaPrivilege': { - 'Policy': 'Adjust memory quotas for a process', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeIncreaseQuotaPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeIncreaseQuotaPrivilege": { + "Policy": "Adjust memory quotas for a process", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeIncreaseQuotaPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeInteractiveLogonRight': { - 'Policy': 'Allow log on locally', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeInteractiveLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeInteractiveLogonRight": { + "Policy": "Allow log on locally", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeInteractiveLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeRemoteInteractiveLogonRight': { - 'Policy': 'Allow log on through Remote Desktop Services', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeRemoteInteractiveLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeRemoteInteractiveLogonRight": { + "Policy": "Allow log on through Remote Desktop Services", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeRemoteInteractiveLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeBackupPrivilege': { - 'Policy': 'Backup files and directories', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeBackupPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeBackupPrivilege": { + "Policy": "Backup files and directories", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeBackupPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeChangeNotifyPrivilege': { - 'Policy': 'Bypass traverse checking', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeChangeNotifyPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeChangeNotifyPrivilege": { + "Policy": "Bypass traverse checking", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeChangeNotifyPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeSystemtimePrivilege': { - 'Policy': 'Change the system time', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeSystemtimePrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeSystemtimePrivilege": { + "Policy": "Change the system time", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeSystemtimePrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeTimeZonePrivilege': { - 'Policy': 'Change the time zone', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeTimeZonePrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeTimeZonePrivilege": { + "Policy": "Change the time zone", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeTimeZonePrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeCreatePagefilePrivilege': { - 'Policy': 'Create a pagefile', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeCreatePagefilePrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeCreatePagefilePrivilege": { + "Policy": "Create a pagefile", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeCreatePagefilePrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeCreateTokenPrivilege': { - 'Policy': 'Create a token object', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeCreateTokenPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeCreateTokenPrivilege": { + "Policy": "Create a token object", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeCreateTokenPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeCreateGlobalPrivilege': { - 'Policy': 'Create global objects', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeCreateGlobalPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeCreateGlobalPrivilege": { + "Policy": "Create global objects", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeCreateGlobalPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeCreatePermanentPrivilege': { - 'Policy': 'Create permanent shared objects', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeCreatePermanentPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeCreatePermanentPrivilege": { + "Policy": "Create permanent shared objects", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeCreatePermanentPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeCreateSymbolicLinkPrivilege': { - 'Policy': 'Create symbolic links', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeCreateSymbolicLinkPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeCreateSymbolicLinkPrivilege": { + "Policy": "Create symbolic links", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeCreateSymbolicLinkPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeDebugPrivilege': { - 'Policy': 'Debug programs', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeDebugPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeDebugPrivilege": { + "Policy": "Debug programs", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeDebugPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeDenyNetworkLogonRight': { - 'Policy': 'Deny access to this computer from the ' - 'network', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeDenyNetworkLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeDenyNetworkLogonRight": { + "Policy": "Deny access to this computer from the " "network", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeDenyNetworkLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeDenyBatchLogonRight': { - 'Policy': 'Deny log on as a batch job', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeDenyBatchLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeDenyBatchLogonRight": { + "Policy": "Deny log on as a batch job", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeDenyBatchLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeDenyServiceLogonRight': { - 'Policy': 'Deny log on as a service', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeDenyServiceLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeDenyServiceLogonRight": { + "Policy": "Deny log on as a service", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeDenyServiceLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeDenyInteractiveLogonRight': { - 'Policy': 'Deny log on locally', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeDenyInteractiveLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeDenyInteractiveLogonRight": { + "Policy": "Deny log on locally", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeDenyInteractiveLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeDenyRemoteInteractiveLogonRight': { - 'Policy': 'Deny log on through Remote Desktop Services', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeDenyRemoteInteractiveLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeDenyRemoteInteractiveLogonRight": { + "Policy": "Deny log on through Remote Desktop Services", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeDenyRemoteInteractiveLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeEnableDelegationPrivilege': { - 'Policy': 'Enable computer and user accounts to be ' - 'trusted for delegation', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeEnableDelegationPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeEnableDelegationPrivilege": { + "Policy": "Enable computer and user accounts to be " + "trusted for delegation", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeEnableDelegationPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeRemoteShutdownPrivilege': { - 'Policy': 'Force shutdown from a remote system', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeRemoteShutdownPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeRemoteShutdownPrivilege": { + "Policy": "Force shutdown from a remote system", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeRemoteShutdownPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeAuditPrivilege': { - 'Policy': 'Generate security audits', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeAuditPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeAuditPrivilege": { + "Policy": "Generate security audits", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeAuditPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeImpersonatePrivilege': { - 'Policy': 'Impersonate a client after authentication', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeImpersonatePrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeImpersonatePrivilege": { + "Policy": "Impersonate a client after authentication", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeImpersonatePrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeIncreaseWorkingSetPrivilege': { - 'Policy': 'Increase a process working set', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeIncreaseWorkingSetPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeIncreaseWorkingSetPrivilege": { + "Policy": "Increase a process working set", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeIncreaseWorkingSetPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeIncreaseBasePriorityPrivilege': { - 'Policy': 'Increase scheduling priority', - 'rights_assignment': True, - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeIncreaseBasePriorityPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeIncreaseBasePriorityPrivilege": { + "Policy": "Increase scheduling priority", + "rights_assignment": True, + "lgpo_section": self.user_rights_assignment_gpedit_path, + "Settings": None, + "LsaRights": {"Option": "SeIncreaseBasePriorityPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeLoadDriverPrivilege': { - 'Policy': 'Load and unload device drivers', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeLoadDriverPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeLoadDriverPrivilege": { + "Policy": "Load and unload device drivers", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeLoadDriverPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeLockMemoryPrivilege': { - 'Policy': 'Lock pages in memory', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeLockMemoryPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeLockMemoryPrivilege": { + "Policy": "Lock pages in memory", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeLockMemoryPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeBatchLogonRight': { - 'Policy': 'Log on as a batch job', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeBatchLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeBatchLogonRight": { + "Policy": "Log on as a batch job", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeBatchLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeServiceLogonRight': { - 'Policy': 'Log on as a service', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeServiceLogonRight' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeServiceLogonRight": { + "Policy": "Log on as a service", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeServiceLogonRight"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeSecurityPrivilege': { - 'Policy': 'Manage auditing and security log', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeSecurityPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeSecurityPrivilege": { + "Policy": "Manage auditing and security log", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeSecurityPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeRelabelPrivilege': { - 'Policy': 'Modify an object label', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeRelabelPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeRelabelPrivilege": { + "Policy": "Modify an object label", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeRelabelPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeSystemEnvironmentPrivilege': { - 'Policy': 'Modify firmware environment values', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeSystemEnvironmentPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeSystemEnvironmentPrivilege": { + "Policy": "Modify firmware environment values", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeSystemEnvironmentPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeManageVolumePrivilege': { - 'Policy': 'Perform volume maintenance tasks', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeManageVolumePrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeManageVolumePrivilege": { + "Policy": "Perform volume maintenance tasks", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeManageVolumePrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeProfileSingleProcessPrivilege': { - 'Policy': 'Profile single process', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeProfileSingleProcessPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeProfileSingleProcessPrivilege": { + "Policy": "Profile single process", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeProfileSingleProcessPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeSystemProfilePrivilege': { - 'Policy': 'Profile system performance', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeSystemProfilePrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeSystemProfilePrivilege": { + "Policy": "Profile system performance", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeSystemProfilePrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeUndockPrivilege': { - 'Policy': 'Remove computer from docking station', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeUndockPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeUndockPrivilege": { + "Policy": "Remove computer from docking station", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeUndockPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeAssignPrimaryTokenPrivilege': { - 'Policy': 'Replace a process level token', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeAssignPrimaryTokenPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeAssignPrimaryTokenPrivilege": { + "Policy": "Replace a process level token", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeAssignPrimaryTokenPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeRestorePrivilege': { - 'Policy': 'Restore files and directories', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeRestorePrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeRestorePrivilege": { + "Policy": "Restore files and directories", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeRestorePrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeShutdownPrivilege': { - 'Policy': 'Shut down the system', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeShutdownPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeShutdownPrivilege": { + "Policy": "Shut down the system", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeShutdownPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeSyncAgentPrivilege': { - 'Policy': 'Synchronize directory service data', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeSyncAgentPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeSyncAgentPrivilege": { + "Policy": "Synchronize directory service data", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeSyncAgentPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'SeTakeOwnershipPrivilege': { - 'Policy': 'Take ownership of files or other objects', - 'lgpo_section': self.user_rights_assignment_gpedit_path, - 'rights_assignment': True, - 'Settings': None, - 'LsaRights': { - 'Option': 'SeTakeOwnershipPrivilege' - }, - 'Transform': { - 'Get': '_sidConversion', - 'Put': '_usernamesToSidObjects', + "SeTakeOwnershipPrivilege": { + "Policy": "Take ownership of files or other objects", + "lgpo_section": self.user_rights_assignment_gpedit_path, + "rights_assignment": True, + "Settings": None, + "LsaRights": {"Option": "SeTakeOwnershipPrivilege"}, + "Transform": { + "Get": "_sidConversion", + "Put": "_usernamesToSidObjects", }, }, - 'RecoveryConsoleSecurityLevel': { - 'Policy': 'Recovery console: Allow automatic ' - 'administrative logon', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\' - 'CurrentVersion\\Setup\\RecoveryConsole', - 'Value': 'SecurityLevel', - 'Type': 'REG_DWORD', + "RecoveryConsoleSecurityLevel": { + "Policy": "Recovery console: Allow automatic " + "administrative logon", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows NT\\" + "CurrentVersion\\Setup\\RecoveryConsole", + "Value": "SecurityLevel", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'RecoveryConsoleSetCommand': { - 'Policy': 'Recovery console: Allow floppy copy and ' - 'access to all drives and all folders', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Microsoft\\Windows NT\\' - 'CurrentVersion\\Setup\\RecoveryConsole', - 'Value': 'SetCommand', - 'Type': 'REG_DWORD', + "RecoveryConsoleSetCommand": { + "Policy": "Recovery console: Allow floppy copy and " + "access to all drives and all folders", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Microsoft\\Windows NT\\" + "CurrentVersion\\Setup\\RecoveryConsole", + "Value": "SetCommand", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'ForceKeyProtection': { - 'Policy': 'System Cryptography: Force strong key protection for ' - 'user keys stored on the computer', - 'Settings': self.force_key_protection.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Policies\\Microsoft\\Cryptography', - 'Value': 'ForceKeyProtection', - 'Type': 'REG_DWORD', + "ForceKeyProtection": { + "Policy": "System Cryptography: Force strong key protection for " + "user keys stored on the computer", + "Settings": self.force_key_protection.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Policies\\Microsoft\\Cryptography", + "Value": "ForceKeyProtection", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.force_key_protection, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.force_key_protection, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.force_key_protection, - 'value_lookup': True, + "PutArgs": { + "lookup": self.force_key_protection, + "value_lookup": True, }, }, }, - 'FIPSAlgorithmPolicy': { - 'Policy': 'System Cryptography: Use FIPS compliant algorithms ' - 'for encryption, hashing, and signing', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\Lsa\\FIPSAlgorithmPolicy', - 'Value': 'Enabled', - 'Type': 'REG_DWORD', + "FIPSAlgorithmPolicy": { + "Policy": "System Cryptography: Use FIPS compliant algorithms " + "for encryption, hashing, and signing", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\Lsa\\FIPSAlgorithmPolicy", + "Value": "Enabled", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'MachineAccessRestriction': { - 'Policy': 'DCOM: Machine Access Restrictions in Security Descriptor ' - 'Definition Language (SDDL) syntax', - 'Settings': None, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Policies\\Microsoft\\Windows NT\\DCOM', - 'Value': 'MachineAccessRestriction', - 'Type': 'REG_SZ', + "MachineAccessRestriction": { + "Policy": "DCOM: Machine Access Restrictions in Security Descriptor " + "Definition Language (SDDL) syntax", + "Settings": None, + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Policies\\Microsoft\\Windows NT\\DCOM", + "Value": "MachineAccessRestriction", + "Type": "REG_SZ", }, - 'Transform': { - 'Put': '_string_put_transform' - } + "Transform": {"Put": "_string_put_transform"}, }, - 'MachineLaunchRestriction': { - 'Policy': 'DCOM: Machine Launch Restrictions in Security Descriptor ' - 'Definition Language (SDDL) syntax', - 'Settings': None, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'Software\\Policies\\Microsoft\\Windows NT\\DCOM', - 'Value': 'MachineLaunchRestriction', - 'Type': 'REG_SZ', + "MachineLaunchRestriction": { + "Policy": "DCOM: Machine Launch Restrictions in Security Descriptor " + "Definition Language (SDDL) syntax", + "Settings": None, + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "Software\\Policies\\Microsoft\\Windows NT\\DCOM", + "Value": "MachineLaunchRestriction", + "Type": "REG_SZ", }, - 'Transform': { - 'Put': '_string_put_transform' - } + "Transform": {"Put": "_string_put_transform"}, }, - 'UseMachineId': { - 'Policy': 'Network security: Allow Local System to use computer ' - 'identity for NTLM', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'UseMachineId', - 'Type': 'REG_DWORD', + "UseMachineId": { + "Policy": "Network security: Allow Local System to use computer " + "identity for NTLM", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "UseMachineId", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'allownullsessionfallback': { - 'Policy': 'Network security: Allow LocalSystem NULL session fallback', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0', - 'Value': 'allownullsessionfallback', - 'Type': 'REG_DWORD', + "allownullsessionfallback": { + "Policy": "Network security: Allow LocalSystem NULL session fallback", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0", + "Value": "allownullsessionfallback", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'AllowOnlineID': { - 'Policy': 'Network security: Allow PKU2U authentication requests ' - 'to this computer to use online identities.', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa\\pku2u', - 'Value': 'AllowOnlineID', - 'Type': 'REG_DWORD', + "AllowOnlineID": { + "Policy": "Network security: Allow PKU2U authentication requests " + "to this computer to use online identities.", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa\\pku2u", + "Value": "AllowOnlineID", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'KrbSupportedEncryptionTypes': { - 'Policy': 'Network security: Configure encryption types allowed ' - 'for Kerberos', - 'Settings': None, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies' - '\\system\\Kerberos\\Parameters', - 'Value': 'SupportedEncryptionTypes', - 'Type': 'REG_DWORD', + "KrbSupportedEncryptionTypes": { + "Policy": "Network security: Configure encryption types allowed " + "for Kerberos", + "Settings": None, + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies" + "\\system\\Kerberos\\Parameters", + "Value": "SupportedEncryptionTypes", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup_bitwise_add', - 'Put': '_dict_lookup_bitwise_add', - 'GetArgs': { - 'lookup': self.krb_encryption_types, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup_bitwise_add", + "Put": "_dict_lookup_bitwise_add", + "GetArgs": { + "lookup": self.krb_encryption_types, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.krb_encryption_types, - 'value_lookup': True, + "PutArgs": { + "lookup": self.krb_encryption_types, + "value_lookup": True, }, }, }, - 'NoLMHash': { - 'Policy': 'Network security: Do not store LAN Manager hash value ' - 'on next password change', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'NoLMHash', - 'Type': 'REG_DWORD', + "NoLMHash": { + "Policy": "Network security: Do not store LAN Manager hash value " + "on next password change", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "NoLMHash", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'ForceLogoffWhenHourExpire': { - 'Policy': 'Network security: Force logoff when logon hours expire', - 'lgpo_section': self.security_options_gpedit_path, - 'Settings': self.enabled_one_disabled_zero_no_not_defined.keys(), - 'Secedit': { - 'Option': 'ForceLogoffWhenHourExpire', - 'Section': 'System Access', + "ForceLogoffWhenHourExpire": { + "Policy": "Network security: Force logoff when logon hours expire", + "lgpo_section": self.security_options_gpedit_path, + "Settings": self.enabled_one_disabled_zero_no_not_defined.keys(), + "Secedit": { + "Option": "ForceLogoffWhenHourExpire", + "Section": "System Access", }, - 'Transform': self.enabled_one_disabled_zero_no_not_defined_transform, + "Transform": self.enabled_one_disabled_zero_no_not_defined_transform, }, - 'LmCompatibilityLevel': { - 'Policy': 'Network security: LAN Manager authentication level', - 'Settings': self.lm_compat_levels.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa', - 'Value': 'LmCompatibilityLevel', - 'Type': 'REG_DWORD', + "LmCompatibilityLevel": { + "Policy": "Network security: LAN Manager authentication level", + "Settings": self.lm_compat_levels.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa", + "Value": "LmCompatibilityLevel", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.lm_compat_levels, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.lm_compat_levels, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.lm_compat_levels, - 'value_lookup': True, + "PutArgs": { + "lookup": self.lm_compat_levels, + "value_lookup": True, }, }, }, - 'LDAPClientIntegrity': { - 'Policy': 'Network security: LDAP client signing requirements', - 'Settings': self.ldap_signing_reqs.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\ldap', - 'Value': 'LDAPClientIntegrity', - 'Type': 'REG_DWORD', + "LDAPClientIntegrity": { + "Policy": "Network security: LDAP client signing requirements", + "Settings": self.ldap_signing_reqs.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\ldap", + "Value": "LDAPClientIntegrity", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.ldap_signing_reqs, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.ldap_signing_reqs, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.ldap_signing_reqs, - 'value_lookup': True, + "PutArgs": { + "lookup": self.ldap_signing_reqs, + "value_lookup": True, }, }, }, - 'NTLMMinClientSec': { - 'Policy': 'Network security: Minimum session security for NTLM SSP based ' - '(including secure RPC) clients', - 'Settings': None, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\Lsa\\MSV1_0', - 'Value': 'NTLMMinClientSec', - 'Type': 'REG_DWORD', + "NTLMMinClientSec": { + "Policy": "Network security: Minimum session security for NTLM SSP based " + "(including secure RPC) clients", + "Settings": None, + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\Lsa\\MSV1_0", + "Value": "NTLMMinClientSec", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup_bitwise_add', - 'Put': '_dict_lookup_bitwise_add', - 'GetArgs': { - 'lookup': self.ntlm_session_security_levels, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup_bitwise_add", + "Put": "_dict_lookup_bitwise_add", + "GetArgs": { + "lookup": self.ntlm_session_security_levels, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.ntlm_session_security_levels, - 'value_lookup': True, + "PutArgs": { + "lookup": self.ntlm_session_security_levels, + "value_lookup": True, }, }, }, - 'NTLMMinServerSec': { - 'Policy': 'Network security: Minimum session security for NTLM SSP based ' - '(including secure RPC) servers', - 'Settings': None, - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\Lsa\\MSV1_0', - 'Value': 'NTLMMinServerSec', - 'Type': 'REG_DWORD', + "NTLMMinServerSec": { + "Policy": "Network security: Minimum session security for NTLM SSP based " + "(including secure RPC) servers", + "Settings": None, + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\Lsa\\MSV1_0", + "Value": "NTLMMinServerSec", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup_bitwise_add', - 'Put': '_dict_lookup_bitwise_add', - 'GetArgs': { - 'lookup': self.ntlm_session_security_levels, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup_bitwise_add", + "Put": "_dict_lookup_bitwise_add", + "GetArgs": { + "lookup": self.ntlm_session_security_levels, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.ntlm_session_security_levels, - 'value_lookup': True, + "PutArgs": { + "lookup": self.ntlm_session_security_levels, + "value_lookup": True, }, }, }, - 'ClientAllowedNTLMServers': { - 'Policy': 'Network security: Restrict NTLM: Add remote server' - ' exceptions for NTLM authentication', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\Lsa\\MSV1_0', - 'Value': 'ClientAllowedNTLMServers', - 'Type': 'REG_MULTI_SZ' + "ClientAllowedNTLMServers": { + "Policy": "Network security: Restrict NTLM: Add remote server" + " exceptions for NTLM authentication", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\Lsa\\MSV1_0", + "Value": "ClientAllowedNTLMServers", + "Type": "REG_MULTI_SZ", + }, + "Transform": { + "Put": "_multi_string_put_transform", + "Get": "_multi_string_get_transform", }, - 'Transform': { - 'Put': '_multi_string_put_transform', - 'Get': '_multi_string_get_transform' - } }, - 'DCAllowedNTLMServers': { - 'Policy': 'Network security: Restrict NTLM: Add server exceptions' - ' in this domain', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Services\\Netlogon\\Parameters', - 'Value': 'DCAllowedNTLMServers', - 'Type': 'REG_MULTI_SZ' + "DCAllowedNTLMServers": { + "Policy": "Network security: Restrict NTLM: Add server exceptions" + " in this domain", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Services\\Netlogon\\Parameters", + "Value": "DCAllowedNTLMServers", + "Type": "REG_MULTI_SZ", + }, + "Transform": { + "Put": "_multi_string_put_transform", + "Get": "_multi_string_get_transform", }, - 'Transform': { - 'Put': '_multi_string_put_transform', - 'Get': '_multi_string_get_transform' - } }, - 'AuditReceivingNTLMTraffic': { - 'Policy': 'Network security: Restrict NTLM: Audit Incoming NTLM Traffic', - 'Settings': self.ntlm_audit_settings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\LSA\\MSV1_0', - 'Value': 'AuditReceivingNTLMTraffic', - 'Type': 'REG_DWORD', + "AuditReceivingNTLMTraffic": { + "Policy": "Network security: Restrict NTLM: Audit Incoming NTLM Traffic", + "Settings": self.ntlm_audit_settings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\LSA\\MSV1_0", + "Value": "AuditReceivingNTLMTraffic", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.ntlm_audit_settings, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.ntlm_audit_settings, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.ntlm_audit_settings, - 'value_lookup': True, + "PutArgs": { + "lookup": self.ntlm_audit_settings, + "value_lookup": True, }, }, }, - 'AuditNTLMInDomain': { - 'Policy': 'Network security: Restrict NTLM: Audit NTLM ' - 'authentication in this domain', - 'Settings': self.ntlm_domain_audit_settings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters', - 'Value': 'AuditNTLMInDomain', - 'Type': 'REG_DWORD', + "AuditNTLMInDomain": { + "Policy": "Network security: Restrict NTLM: Audit NTLM " + "authentication in this domain", + "Settings": self.ntlm_domain_audit_settings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters", + "Value": "AuditNTLMInDomain", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.ntlm_domain_audit_settings, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.ntlm_domain_audit_settings, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.ntlm_domain_audit_settings, - 'value_lookup': True, + "PutArgs": { + "lookup": self.ntlm_domain_audit_settings, + "value_lookup": True, }, }, }, - 'RestrictReceivingNTLMTraffic': { - 'Policy': 'Network security: Restrict NTLM: Incoming' - ' NTLM traffic', - 'Settings': self.incoming_ntlm_settings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\LSA\\MSV1_0', - 'Value': 'RestrictReceivingNTLMTraffic', - 'Type': 'REG_DWORD', + "RestrictReceivingNTLMTraffic": { + "Policy": "Network security: Restrict NTLM: Incoming" + " NTLM traffic", + "Settings": self.incoming_ntlm_settings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\LSA\\MSV1_0", + "Value": "RestrictReceivingNTLMTraffic", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.incoming_ntlm_settings, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.incoming_ntlm_settings, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.incoming_ntlm_settings, - 'value_lookup': True, + "PutArgs": { + "lookup": self.incoming_ntlm_settings, + "value_lookup": True, }, }, }, - 'RestrictNTLMInDomain': { - 'Policy': 'Network security: Restrict NTLM: NTLM ' - 'authentication in this domain', - 'Settings': self.ntlm_domain_auth_settings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters', - 'Value': 'RestrictNTLMInDomain', - 'Type': 'REG_DWORD', + "RestrictNTLMInDomain": { + "Policy": "Network security: Restrict NTLM: NTLM " + "authentication in this domain", + "Settings": self.ntlm_domain_auth_settings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters", + "Value": "RestrictNTLMInDomain", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.ntlm_domain_auth_settings, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.ntlm_domain_auth_settings, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.ntlm_domain_auth_settings, - 'value_lookup': True, + "PutArgs": { + "lookup": self.ntlm_domain_auth_settings, + "value_lookup": True, }, }, }, - 'RestrictSendingNTLMTraffic': { - 'Policy': 'Network security: Restrict NTLM: Outgoing NTLM' - ' traffic to remote servers', - 'Settings': self.outgoing_ntlm_settings.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0', - 'Value': 'RestrictSendingNTLMTraffic', - 'Type': 'REG_DWORD', + "RestrictSendingNTLMTraffic": { + "Policy": "Network security: Restrict NTLM: Outgoing NTLM" + " traffic to remote servers", + "Settings": self.outgoing_ntlm_settings.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0", + "Value": "RestrictSendingNTLMTraffic", + "Type": "REG_DWORD", }, - 'Transform': { - 'Get': '_dict_lookup', - 'Put': '_dict_lookup', - 'GetArgs': { - 'lookup': self.outgoing_ntlm_settings, - 'value_lookup': False, + "Transform": { + "Get": "_dict_lookup", + "Put": "_dict_lookup", + "GetArgs": { + "lookup": self.outgoing_ntlm_settings, + "value_lookup": False, }, - 'PutArgs': { - 'lookup': self.outgoing_ntlm_settings, - 'value_lookup': True, + "PutArgs": { + "lookup": self.outgoing_ntlm_settings, + "value_lookup": True, }, }, }, - 'ShutdownWithoutLogon': { - 'Policy': 'Shutdown: Allow system to be shut down ' - 'without having to log on', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system', - 'Value': 'ShutdownWithoutLogon', - 'Type': 'REG_DWORD', + "ShutdownWithoutLogon": { + "Policy": "Shutdown: Allow system to be shut down " + "without having to log on", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\policies\\system", + "Value": "ShutdownWithoutLogon", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'ClearPageFileAtShutdown': { - 'Policy': 'Shutdown: Clear virtual memory pagefile', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\' - 'SESSION MANAGER\\MEMORY MANAGEMENT', - 'Value': 'ClearPageFileAtShutdown', - 'Type': 'REG_DWORD', + "ClearPageFileAtShutdown": { + "Policy": "Shutdown: Clear virtual memory pagefile", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\" + "SESSION MANAGER\\MEMORY MANAGEMENT", + "Value": "ClearPageFileAtShutdown", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'ObCaseInsensitive': { - 'Policy': 'System objects: Require case insensitivity for ' - 'non-Windows subsystems', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\' - 'SESSION MANAGER\\Kernel', - 'Value': 'ObCaseInsensitive', - 'Type': 'REG_DWORD', + "ObCaseInsensitive": { + "Policy": "System objects: Require case insensitivity for " + "non-Windows subsystems", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\" + "SESSION MANAGER\\Kernel", + "Value": "ObCaseInsensitive", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'ProtectionMode': { - 'Policy': 'System objects: Strengthen default permissions of ' - 'internal system objects (e.g. Symbolic Links)', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\' - 'SESSION MANAGER', - 'Value': 'ProtectionMode', - 'Type': 'REG_DWORD', + "ProtectionMode": { + "Policy": "System objects: Strengthen default permissions of " + "internal system objects (e.g. Symbolic Links)", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\" + "SESSION MANAGER", + "Value": "ProtectionMode", + "Type": "REG_DWORD", }, - 'Transform': self.enabled_one_disabled_zero_transform, + "Transform": self.enabled_one_disabled_zero_transform, }, - 'OptionalSubsystems': { - 'Policy': 'System settings: Optional subsystems', - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'System\\CurrentControlSet\\Control\\' - 'SESSION MANAGER\\SubSystems', - 'Value': 'optional', - 'Type': 'REG_MULTI_SZ' + "OptionalSubsystems": { + "Policy": "System settings: Optional subsystems", + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "System\\CurrentControlSet\\Control\\" + "SESSION MANAGER\\SubSystems", + "Value": "optional", + "Type": "REG_MULTI_SZ", }, - 'Transform': { - 'Put': '_multi_string_put_transform', - 'Get': '_multi_string_get_transform' - } - }, - 'AuthenticodeEnabled': { - 'Policy': 'System settings: Use Certificate Rules on Windows' - ' Executables for Software Restriction Policies', - 'Settings': self.enabled_one_disabled_zero.keys(), - 'lgpo_section': self.security_options_gpedit_path, - 'Registry': { - 'Hive': 'HKEY_LOCAL_MACHINE', - 'Path': 'SOFTWARE\\Policies\\Microsoft\\Windows\\safer\\codeidentifiers', - 'Value': 'AuthenticodeEnabled', - 'Type': 'REG_DWORD', + "Transform": { + "Put": "_multi_string_put_transform", + "Get": "_multi_string_get_transform", }, - 'Transform': self.enabled_one_disabled_zero_transform, }, - } - }, - 'User': { - 'lgpo_section': 'User Configuration', - 'policies': {} + "AuthenticodeEnabled": { + "Policy": "System settings: Use Certificate Rules on Windows" + " Executables for Software Restriction Policies", + "Settings": self.enabled_one_disabled_zero.keys(), + "lgpo_section": self.security_options_gpedit_path, + "Registry": { + "Hive": "HKEY_LOCAL_MACHINE", + "Path": "SOFTWARE\\Policies\\Microsoft\\Windows\\safer\\codeidentifiers", + "Value": "AuthenticodeEnabled", + "Type": "REG_DWORD", + }, + "Transform": self.enabled_one_disabled_zero_transform, + }, + }, }, + "User": {"lgpo_section": "User Configuration", "policies": {}}, } self.admx_registry_classes = { - 'User': { - 'policy_path': os.path.join(os.getenv('WINDIR'), 'System32', - 'GroupPolicy', 'User', - 'Registry.pol'), - 'hive': 'HKEY_USERS', - 'lgpo_section': 'User Configuration', - 'gpt_extension_location': 'gPCUserExtensionNames', - 'gpt_extension_guid': '[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F73-3407-48AE-BA88-E8213C6761F1}]' + "User": { + "policy_path": os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "User", + "Registry.pol", + ), + "hive": "HKEY_USERS", + "lgpo_section": "User Configuration", + "gpt_extension_location": "gPCUserExtensionNames", + "gpt_extension_guid": "[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F73-3407-48AE-BA88-E8213C6761F1}]", }, - 'Machine': { - 'policy_path': os.path.join(os.getenv('WINDIR'), 'System32', - 'GroupPolicy', 'Machine', - 'Registry.pol'), - 'hive': 'HKEY_LOCAL_MACHINE', - 'lgpo_section': 'Computer Configuration', - 'gpt_extension_location': 'gPCMachineExtensionNames', - 'gpt_extension_guid': '[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}]' + "Machine": { + "policy_path": os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "Machine", + "Registry.pol", + ), + "hive": "HKEY_LOCAL_MACHINE", + "lgpo_section": "Computer Configuration", + "gpt_extension_location": "gPCMachineExtensionNames", + "gpt_extension_guid": "[{35378EAC-683F-11D2-A89A-00C04FBBCFA2}{D02B1F72-3407-48AE-BA88-E8213C6761F1}]", }, } - self.reg_pol_header = u'\u5250\u6765\x01\x00' - self.gpt_ini_path = os.path.join(os.getenv('WINDIR'), 'System32', - 'GroupPolicy', 'gpt.ini') + self.reg_pol_header = "\u5250\u6765\x01\x00" + self.gpt_ini_path = os.path.join( + os.getenv("WINDIR"), "System32", "GroupPolicy", "gpt.ini" + ) @classmethod def _notEmpty(cls, val, **kwargs): - ''' + """ ensures a value is not empty - ''' + """ if val: return True else: @@ -4421,91 +4223,91 @@ class _policy_info(object): @classmethod def _seconds_to_days(cls, val, **kwargs): - ''' + """ converts a number of seconds to days - ''' - zero_value = kwargs.get('zero_value', 0) + """ + zero_value = kwargs.get("zero_value", 0) if val is not None: if val == zero_value: return 0 return val / 86400 else: - return 'Not Defined' + return "Not Defined" @classmethod def _days_to_seconds(cls, val, **kwargs): - ''' + """ converts a number of days to seconds - ''' - zero_value = kwargs.get('zero_value', 0) + """ + zero_value = kwargs.get("zero_value", 0) if val is not None: if val == 0: return zero_value return val * 86400 else: - return 'Not Defined' + return "Not Defined" @classmethod def _seconds_to_minutes(cls, val, **kwargs): - ''' + """ converts a number of seconds to minutes - ''' + """ if val is not None: return val / 60 else: - return 'Not Defined' + return "Not Defined" @classmethod def _minutes_to_seconds(cls, val, **kwargs): - ''' + """ converts number of minutes to seconds - ''' + """ if val is not None: return val * 60 else: - return 'Not Defined' + return "Not Defined" @classmethod def _strip_quotes(cls, val, **kwargs): - ''' + """ strips quotes from a string - ''' - return val.replace('"', '') + """ + return val.replace('"', "") @classmethod def _add_quotes(cls, val, **kwargs): - ''' + """ add quotes around the string - ''' + """ return '"{0}"'.format(val) @classmethod def _binary_enable_zero_disable_one_conversion(cls, val, **kwargs): - ''' + """ converts a binary 0/1 to Disabled/Enabled - ''' + """ try: if val is not None: if ord(val) == 0: - return 'Disabled' + return "Disabled" elif ord(val) == 1: - return 'Enabled' + return "Enabled" else: - return 'Invalid Value: {0!r}'.format(val) # pylint: disable=repr-flag-used-in-string + return "Invalid Value: {0!r}".format(val) else: - return 'Not Defined' + return "Not Defined" except TypeError: - return 'Invalid Value' + return "Invalid Value" @classmethod def _binary_enable_zero_disable_one_reverse_conversion(cls, val, **kwargs): - ''' + """ converts Enabled/Disabled to unicode char to write to a REG_BINARY value - ''' + """ if val is not None: - if val.upper() == 'DISABLED': + if val.upper() == "DISABLED": return chr(0) - elif val.upper() == 'ENABLED': + elif val.upper() == "ENABLED": return chr(1) else: return None @@ -4514,55 +4316,55 @@ class _policy_info(object): @classmethod def _dasd_conversion(cls, val, **kwargs): - ''' + """ converts 0/1/2 for dasd reg key - ''' + """ if val is not None: - if val == '0' or val == 0 or val == '': - return 'Administrators' - elif val == '1' or val == 1: - return 'Administrators and Power Users' - elif val == '2' or val == 2: - return 'Administrators and Interactive Users' + if val == "0" or val == 0 or val == "": + return "Administrators" + elif val == "1" or val == 1: + return "Administrators and Power Users" + elif val == "2" or val == 2: + return "Administrators and Interactive Users" else: - return 'Not Defined' + return "Not Defined" else: - return 'Not Defined' + return "Not Defined" @classmethod def _dasd_reverse_conversion(cls, val, **kwargs): - ''' + """ converts DASD String values to the reg_sz value - ''' + """ if val is not None: - if val.upper() == 'ADMINISTRATORS': + if val.upper() == "ADMINISTRATORS": # "" also shows 'administrators' in the GUI - return '0' - elif val.upper() == 'ADMINISTRATORS AND POWER USERS': - return '1' - elif val.upper() == 'ADMINISTRATORS AND INTERACTIVE USERS': - return '2' - elif val.upper() == 'NOT DEFINED': + return "0" + elif val.upper() == "ADMINISTRATORS AND POWER USERS": + return "1" + elif val.upper() == "ADMINISTRATORS AND INTERACTIVE USERS": + return "2" + elif val.upper() == "NOT DEFINED": # a setting of anything other than nothing, 0, 1, 2 or if it # doesn't exist show 'not defined' - return '9999' + return "9999" else: - return 'Invalid Value' + return "Invalid Value" else: - return 'Not Defined' + return "Not Defined" @classmethod def _in_range_inclusive(cls, val, **kwargs): - ''' + """ checks that a value is in an inclusive range The value for 0 used by Max Password Age is actually 0xffffffff - ''' - minimum = kwargs.get('min', 0) - maximum = kwargs.get('max', 1) - zero_value = kwargs.get('zero_value', 0) + """ + minimum = kwargs.get("min", 0) + maximum = kwargs.get("max", 1) + zero_value = kwargs.get("zero_value", 0) if isinstance(val, six.string_types): - if val.lower() == 'not defined': + if val.lower() == "not defined": return True else: try: @@ -4579,149 +4381,154 @@ class _policy_info(object): @classmethod def _driver_signing_reg_conversion(cls, val, **kwargs): - ''' + """ converts the binary value in the registry for driver signing into the correct string representation - ''' - log.trace('we have %s for the driver signing value', val) + """ + log.trace("we have %s for the driver signing value", val) if val is not None: # since this is from secedit, it should be 3,<value> - _val = val.split(',') + _val = val.split(",") if len(_val) == 2: - if _val[1] == '0': - return 'Silently Succeed' - elif _val[1] == '1': - return 'Warn but allow installation' - elif _val[1] == '2': - return 'Do not allow installation' - elif _val[1] == 'Not Defined': - return 'Not Defined' + if _val[1] == "0": + return "Silently Succeed" + elif _val[1] == "1": + return "Warn but allow installation" + elif _val[1] == "2": + return "Do not allow installation" + elif _val[1] == "Not Defined": + return "Not Defined" else: - return 'Invalid Value' + return "Invalid Value" else: - return 'Not Defined' + return "Not Defined" else: - return 'Not Defined' + return "Not Defined" @classmethod def _driver_signing_reg_reverse_conversion(cls, val, **kwargs): - ''' + """ converts the string value seen in the GUI to the correct registry value for secedit - ''' + """ if val is not None: - if val.upper() == 'SILENTLY SUCCEED': - return ','.join(['3', '0']) - elif val.upper() == 'WARN BUT ALLOW INSTALLATION': - return ','.join(['3', chr(1)]) - elif val.upper() == 'DO NOT ALLOW INSTALLATION': - return ','.join(['3', chr(2)]) + if val.upper() == "SILENTLY SUCCEED": + return ",".join(["3", "0"]) + elif val.upper() == "WARN BUT ALLOW INSTALLATION": + return ",".join(["3", chr(1)]) + elif val.upper() == "DO NOT ALLOW INSTALLATION": + return ",".join(["3", chr(2)]) else: - return 'Invalid Value' + return "Invalid Value" else: - return 'Not Defined' + return "Not Defined" @classmethod def _sidConversion(cls, val, **kwargs): - ''' + """ converts a list of pysid objects to string representations - ''' + """ if isinstance(val, six.string_types): - val = val.split(',') + val = val.split(",") usernames = [] for _sid in val: try: - userSid = win32security.LookupAccountSid('', _sid) + userSid = win32security.LookupAccountSid("", _sid) if userSid[1]: - userSid = '{1}\\{0}'.format(userSid[0], userSid[1]) + userSid = "{1}\\{0}".format(userSid[0], userSid[1]) else: - userSid = '{0}'.format(userSid[0]) + userSid = "{0}".format(userSid[0]) # TODO: This needs to be more specific except Exception: # pylint: disable=broad-except userSid = win32security.ConvertSidToStringSid(_sid) - log.warning('Unable to convert SID "%s" to a friendly name. The SID will be disaplayed instead of a user/group name.', userSid) + log.warning( + 'Unable to convert SID "%s" to a friendly name. The SID will be disaplayed instead of a user/group name.', + userSid, + ) usernames.append(userSid) return usernames @classmethod def _usernamesToSidObjects(cls, val, **kwargs): - ''' + """ converts a list of usernames to sid objects - ''' + """ if not val: return val if isinstance(val, six.string_types): - val = val.split(',') + val = val.split(",") sids = [] for _user in val: try: - sid = win32security.LookupAccountName('', _user)[0] + sid = win32security.LookupAccountName("", _user)[0] sids.append(sid) # This needs to be more specific except Exception as e: # pylint: disable=broad-except - log.exception('Handle this explicitly') - raise CommandExecutionError(( - 'There was an error obtaining the SID of user "{0}". Error ' - 'returned: {1}' - ).format(_user, e)) + log.exception("Handle this explicitly") + raise CommandExecutionError( + ( + 'There was an error obtaining the SID of user "{0}". Error ' + "returned: {1}" + ).format(_user, e) + ) return sids @classmethod def _powershell_script_order_conversion(cls, val, **kwargs): - ''' + """ converts true/false/None to the GUI representation of the powershell startup/shutdown script order - ''' - log.trace('script order value = %s', val) - if val is None or val == 'None': - return 'Not Configured' - elif val == 'true': - return 'Run Windows PowerShell scripts first' - elif val == 'false': - return 'Run Windows PowerShell scripts last' + """ + log.trace("script order value = %s", val) + if val is None or val == "None": + return "Not Configured" + elif val == "true": + return "Run Windows PowerShell scripts first" + elif val == "false": + return "Run Windows PowerShell scripts last" else: - return 'Invalid Value' + return "Invalid Value" @classmethod def _powershell_script_order_reverse_conversion(cls, val, **kwargs): - ''' + """ converts powershell script GUI strings representations to True/False/None - ''' - if val.upper() == 'Run Windows PowerShell scripts first'.upper(): - return 'true' - elif val.upper() == 'Run Windows PowerShell scripts last'.upper(): - return 'false' - elif val is 'Not Configured': + """ + if val.upper() == "Run Windows PowerShell scripts first".upper(): + return "true" + elif val.upper() == "Run Windows PowerShell scripts last".upper(): + return "false" + elif val is "Not Configured": return None else: - return 'Invalid Value' + return "Invalid Value" @classmethod def _dict_lookup(cls, item, **kwargs): - ''' + """ Retrieves the key or value from a dict based on the item kwarg lookup dict to search for item kwarg value_lookup bool to determine if item should be compared to keys or values - ''' - log.trace('item == %s', item) - value_lookup = kwargs.get('value_lookup', False) - if 'lookup' in kwargs: - for k, v in six.iteritems(kwargs['lookup']): + """ + log.trace("item == %s", item) + value_lookup = kwargs.get("value_lookup", False) + if "lookup" in kwargs: + for k, v in six.iteritems(kwargs["lookup"]): if value_lookup: if six.text_type(v).lower() == six.text_type(item).lower(): - log.trace('returning key %s', k) + log.trace("returning key %s", k) return k else: if six.text_type(k).lower() == six.text_type(item).lower(): - log.trace('returning value %s', v) + log.trace("returning value %s", v) return v - return 'Invalid Value' + return "Invalid Value" @classmethod def _dict_lookup_bitwise_add(cls, item, **kwargs): - ''' + """ kwarg value_lookup bool to determine if item_list should be compared to keys or values @@ -4733,22 +4540,22 @@ class _policy_info(object): if value_lookup is False, item is expected to be an integer the function will return the values for the keys which successfully "bitwise and" with item - ''' - value_lookup = kwargs.get('value_lookup', False) - test_zero = kwargs.get('test_zero', False) + """ + value_lookup = kwargs.get("value_lookup", False) + test_zero = kwargs.get("test_zero", False) ret_val = None - if str(item).lower() == 'not defined': + if str(item).lower() == "not defined": return None if value_lookup: if not isinstance(item, list): - return 'Invalid Value' + return "Invalid Value" ret_val = 0 else: if not isinstance(item, six.integer_types): - return 'Invalid Value' + return "Invalid Value" ret_val = [] - if 'lookup' in kwargs: - for k, v in six.iteritems(kwargs['lookup']): + if "lookup" in kwargs: + for k, v in six.iteritems(kwargs["lookup"]): if value_lookup: if six.text_type(v).lower() in [z.lower() for z in item]: ret_val = ret_val + k @@ -4760,131 +4567,133 @@ class _policy_info(object): if do_test and isinstance(k, int) and item & k == k: ret_val.append(v) else: - return 'Invalid Value' + return "Invalid Value" return ret_val @classmethod def _multi_string_put_transform(cls, item, **kwargs): - ''' + """ transform for setting REG_MULTI_SZ to properly handle "Not Defined" - ''' + """ if isinstance(item, list): return item elif isinstance(item, six.string_types): - if item.lower() == 'not defined': + if item.lower() == "not defined": return None else: - return item.split(',') + return item.split(",") else: - return 'Invalid Value' + return "Invalid Value" @classmethod def _multi_string_get_transform(cls, item, **kwargs): - ''' + """ transform for getting REG_MULTI_SZ to properly handle `None` - ''' + """ if isinstance(item, list): return item elif item is None: - return 'Not Defined' + return "Not Defined" else: - return 'Invalid Value' + return "Invalid Value" @classmethod def _string_put_transform(cls, item, **kwargs): - ''' + """ transform for a REG_SZ to properly handle "Not Defined" - ''' + """ if isinstance(item, six.string_types): - if item.lower() == 'not defined': + if item.lower() == "not defined": return None else: return item def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'win_lgpo: Not a Windows System' + return False, "win_lgpo: Not a Windows System" if not HAS_WINDOWS_MODULES: - return False, 'win_lgpo: Required modules failed to load' + return False, "win_lgpo: Required modules failed to load" return __virtualname__ def _updateNamespace(item, new_namespace): - ''' + """ helper function to recursively update the namespaces of an item - ''' - temp_item = '' - i = item.tag.find('}') + """ + temp_item = "" + i = item.tag.find("}") if i >= 0: - temp_item = item.tag[i+1:] + temp_item = item.tag[i + 1 :] else: temp_item = item.tag - item.tag = '{{{0}}}{1}'.format(new_namespace, temp_item) + item.tag = "{{{0}}}{1}".format(new_namespace, temp_item) for child in item.getiterator(): if isinstance(child.tag, six.string_types): - temp_item = '' - i = child.tag.find('}') + temp_item = "" + i = child.tag.find("}") if i >= 0: - temp_item = child.tag[i+1:] + temp_item = child.tag[i + 1 :] else: temp_item = child.tag - child.tag = '{{{0}}}{1}'.format(new_namespace, temp_item) + child.tag = "{{{0}}}{1}".format(new_namespace, temp_item) return item def _updatePolicyElements(policy_item, regkey): - ''' + """ helper function to add the reg key to each policies element definitions if the key attribute is not defined to make xpath searching easier for each child in the policy <elements> item - ''' + """ for child in policy_item.getiterator(): - if 'valueName' in child.attrib: - if 'key' not in child.attrib: - child.attrib['key'] = regkey + if "valueName" in child.attrib: + if "key" not in child.attrib: + child.attrib["key"] = regkey return policy_item def _remove_unicode_encoding(xml_file): - ''' + """ attempts to remove the "encoding='unicode'" from an xml file as lxml does not support that on a windows node currently see issue #38100 (Search.adml) For some reason this file is encoded 'utf-16' - ''' - with salt.utils.files.fopen(xml_file, 'rb') as f: + """ + with salt.utils.files.fopen(xml_file, "rb") as f: xml_content = f.read() - modified_xml = re.sub(r' encoding=[\'"]+unicode[\'"]+', '', - xml_content.decode('utf-16'), count=1) + modified_xml = re.sub( + r' encoding=[\'"]+unicode[\'"]+', "", xml_content.decode("utf-16"), count=1 + ) xml_tree = lxml.etree.parse(six.StringIO(modified_xml)) return xml_tree def _remove_invalid_xmlns(xml_file): - ''' + """ Attempts to remove an invalid xmlns entry in newer versions of WindowsDefender.adml xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions" For some reason this file is encoded 'utf-8' - ''' - with salt.utils.files.fopen(xml_file, 'rb') as f: + """ + with salt.utils.files.fopen(xml_file, "rb") as f: xml_content = f.read() - modified_xml = re.sub(r' xmlns=[\'"]+.*[\'"]+', '', - xml_content.decode('utf-8'), count=1) + modified_xml = re.sub( + r' xmlns=[\'"]+.*[\'"]+', "", xml_content.decode("utf-8"), count=1 + ) xml_tree = lxml.etree.parse(six.StringIO(modified_xml)) return xml_tree def _parse_xml(adm_file): - ''' + """ Parse the admx/adml file. There are 3 scenarios (so far) that we'll likely encounter: @@ -4893,59 +4702,58 @@ def _parse_xml(adm_file): recognize 3. invalid xmlns entry in the xml header, which the lxml library doesn't recognize - ''' + """ parser = lxml.etree.XMLParser(remove_comments=True) - modified_xml = '' - with salt.utils.files.fopen(adm_file, 'rb') as rfh: - file_hash = '{0:X}'.format(zlib.crc32(rfh.read()) & 0xffffffff) + modified_xml = "" + with salt.utils.files.fopen(adm_file, "rb") as rfh: + file_hash = "{0:X}".format(zlib.crc32(rfh.read()) & 0xFFFFFFFF) name, ext = os.path.splitext(os.path.basename(adm_file)) - hashed_filename = '{0}-{1}{2}'.format(name, file_hash, ext) + hashed_filename = "{0}-{1}{2}".format(name, file_hash, ext) - cache_dir = os.path.join(__opts__['cachedir'], 'lgpo', 'policy_defs') + cache_dir = os.path.join(__opts__["cachedir"], "lgpo", "policy_defs") if not os.path.exists(cache_dir): os.makedirs(cache_dir) out_file = os.path.join(cache_dir, hashed_filename) if not os.path.isfile(out_file): - log.debug('LGPO: Generating policy template cache for %s%s', name, ext) + log.debug("LGPO: Generating policy template cache for %s%s", name, ext) # Remove old files, keep the cache clean - file_list = glob.glob( - os.path.join(cache_dir, '{0}*{1}'.format(name, ext))) + file_list = glob.glob(os.path.join(cache_dir, "{0}*{1}".format(name, ext))) for file_path in file_list: os.remove(file_path) # Lowercase all the keys - with salt.utils.files.fopen(adm_file, 'rb') as rfh: + with salt.utils.files.fopen(adm_file, "rb") as rfh: - encoding = 'utf-8' + encoding = "utf-8" raw = rfh.read() try: raw = raw.decode(encoding) except UnicodeDecodeError: - log.trace('LGPO: Detecting encoding') - encoding = 'utf-16' + log.trace("LGPO: Detecting encoding") + encoding = "utf-16" raw = raw.decode(encoding) - for line in raw.split('\r\n'): + for line in raw.split("\r\n"): if 'key="' in line: start = line.index('key="') q1 = line[start:].index('"') + start - q2 = line[q1 + 1:].index('"') + q1 + 1 + q2 = line[q1 + 1 :].index('"') + q1 + 1 line = line.replace(line[start:q2], line[start:q2].lower()) found_key = True - modified_xml += line + '\r\n' + modified_xml += line + "\r\n" # Convert smart quotes to regular quotes - modified_xml = modified_xml.replace('\u201c', '"').replace('\u201d', '"') - modified_xml = modified_xml.replace('\u2018', '\'').replace('\u2019', '\'') + modified_xml = modified_xml.replace("\u201c", '"').replace("\u201d", '"') + modified_xml = modified_xml.replace("\u2018", "'").replace("\u2019", "'") # Convert em dash and en dash to dash - modified_xml = modified_xml.replace('\u2013', '-').replace('\u2014', '-') + modified_xml = modified_xml.replace("\u2013", "-").replace("\u2014", "-") - with salt.utils.files.fopen(out_file, 'wb') as wfh: + with salt.utils.files.fopen(out_file, "wb") as wfh: wfh.write(modified_xml.encode(encoding)) try: @@ -4962,73 +4770,91 @@ def _parse_xml(adm_file): return xml_tree -def _load_policy_definitions(path='c:\\Windows\\PolicyDefinitions', - language='en-US'): - ''' +def _load_policy_definitions(path="c:\\Windows\\PolicyDefinitions", language="en-US"): + """ helper function to process all ADMX files in the specified policy_def_path and build a single XML doc that we can search/use for ADMX policy processing - ''' + """ # Fallback to the System Install Language display_language_fallback = INSTALL_LANGUAGE - t_policy_definitions = lxml.etree.Element('policyDefinitions') - t_policy_definitions.append(lxml.etree.Element('categories')) - t_policy_definitions.append(lxml.etree.Element('policies')) - t_policy_definitions.append(lxml.etree.Element('policyNamespaces')) - t_policy_definition_resources = lxml.etree.Element( - 'policyDefinitionResources') - policydefs_policies_xpath = etree.XPath('/policyDefinitions/policies') - policydefs_categories_xpath = etree.XPath('/policyDefinitions/categories') - policydefs_policyns_xpath = etree.XPath( - '/policyDefinitions/policyNamespaces') + t_policy_definitions = lxml.etree.Element("policyDefinitions") + t_policy_definitions.append(lxml.etree.Element("categories")) + t_policy_definitions.append(lxml.etree.Element("policies")) + t_policy_definitions.append(lxml.etree.Element("policyNamespaces")) + t_policy_definition_resources = lxml.etree.Element("policyDefinitionResources") + policydefs_policies_xpath = etree.XPath("/policyDefinitions/policies") + policydefs_categories_xpath = etree.XPath("/policyDefinitions/categories") + policydefs_policyns_xpath = etree.XPath("/policyDefinitions/policyNamespaces") policydefs_resources_localname_xpath = etree.XPath( - '//*[local-name() = "policyDefinitionResources"]/*') - policydef_resources_xpath = etree.XPath('/policyDefinitionResources') + '//*[local-name() = "policyDefinitionResources"]/*' + ) + policydef_resources_xpath = etree.XPath("/policyDefinitionResources") for root, dirs, files in salt.utils.path.os_walk(path): if root == path: for t_admx_file in files: admx_file_name, admx_file_ext = os.path.splitext(t_admx_file) # Only process ADMX files, any other file will cause a # stacktrace later on - if not admx_file_ext == '.admx': - log.debug('{0} is not an ADMX file'.format(t_admx_file)) + if not admx_file_ext == ".admx": + log.debug("{0} is not an ADMX file".format(t_admx_file)) continue admx_file = os.path.join(root, t_admx_file) # Parse xml for the ADMX file try: xml_tree = _parse_xml(admx_file) except lxml.etree.XMLSyntaxError: - log.error('An error was found while processing admx ' - 'file %s, all policies from this file will ' - 'be unavailable via this module', admx_file) + log.error( + "An error was found while processing admx " + "file %s, all policies from this file will " + "be unavailable via this module", + admx_file, + ) continue namespaces = xml_tree.getroot().nsmap - namespace_string = '' + namespace_string = "" if None in namespaces: - namespaces['None'] = namespaces[None] + namespaces["None"] = namespaces[None] namespaces.pop(None) - namespace_string = 'None:' + namespace_string = "None:" this_namespace = xml_tree.xpath( - '/{0}policyDefinitions/{0}policyNamespaces/{0}target/@namespace'.format(namespace_string), - namespaces=namespaces)[0] + "/{0}policyDefinitions/{0}policyNamespaces/{0}target/@namespace".format( + namespace_string + ), + namespaces=namespaces, + )[0] categories = xml_tree.xpath( - '/{0}policyDefinitions/{0}categories/{0}category'.format(namespace_string), - namespaces=namespaces) + "/{0}policyDefinitions/{0}categories/{0}category".format( + namespace_string + ), + namespaces=namespaces, + ) for category in categories: temp_cat = category temp_cat = _updateNamespace(temp_cat, this_namespace) - policydefs_categories_xpath(t_policy_definitions)[0].append(temp_cat) - policies = xml_tree.xpath('/{0}policyDefinitions/{0}policies/{0}policy'.format(namespace_string), - namespaces=namespaces) + policydefs_categories_xpath(t_policy_definitions)[0].append( + temp_cat + ) + policies = xml_tree.xpath( + "/{0}policyDefinitions/{0}policies/{0}policy".format( + namespace_string + ), + namespaces=namespaces, + ) for policy in policies: temp_pol = policy temp_pol = _updateNamespace(temp_pol, this_namespace) - if 'key' in temp_pol.attrib: - temp_pol = _updatePolicyElements(temp_pol, temp_pol.attrib['key']) + if "key" in temp_pol.attrib: + temp_pol = _updatePolicyElements( + temp_pol, temp_pol.attrib["key"] + ) policydefs_policies_xpath(t_policy_definitions)[0].append(temp_pol) policy_namespaces = xml_tree.xpath( - '/{0}policyDefinitions/{0}policyNamespaces/{0}*'.format(namespace_string), - namespaces=namespaces) + "/{0}policyDefinitions/{0}policyNamespaces/{0}*".format( + namespace_string + ), + namespaces=namespaces, + ) for policy_ns in policy_namespaces: temp_ns = policy_ns temp_ns = _updateNamespace(temp_ns, this_namespace) @@ -5038,97 +4864,109 @@ def _load_policy_definitions(path='c:\\Windows\\PolicyDefinitions', # the passed language (eg: en-US). Then we'll try the # abbreviated version (en) to account for alternate locations. # We'll do the same for the display_language_fallback (en_US). - adml_file = os.path.join( - root, - language, - admx_file_name + '.adml') - if not __salt__['file.file_exists'](adml_file): - log.info('An ADML file in the specified ADML language ' - '"%s" does not exist for the ADMX "%s", the ' - 'the abbreviated language code will be tried.', - language, t_admx_file) + adml_file = os.path.join(root, language, admx_file_name + ".adml") + if not __salt__["file.file_exists"](adml_file): + log.info( + "An ADML file in the specified ADML language " + '"%s" does not exist for the ADMX "%s", the ' + "the abbreviated language code will be tried.", + language, + t_admx_file, + ) adml_file = os.path.join( - root, - language.split('-')[0], - admx_file_name + '.adml') - if not __salt__['file.file_exists'](adml_file): - log.info('An ADML file in the specified ADML language ' - 'code %s does not exist for the ADMX "%s", ' - 'the fallback language will be tried.', - language[:2], t_admx_file) + root, language.split("-")[0], admx_file_name + ".adml" + ) + if not __salt__["file.file_exists"](adml_file): + log.info( + "An ADML file in the specified ADML language " + 'code %s does not exist for the ADMX "%s", ' + "the fallback language will be tried.", + language[:2], + t_admx_file, + ) adml_file = os.path.join( - root, - display_language_fallback, - admx_file_name + '.adml') - if not __salt__['file.file_exists'](adml_file): - log.info('An ADML file in the specified ADML ' - 'fallback language "%s" ' - 'does not exist for the ADMX "%s" ' - 'the abbreviated fallback language code ' - 'will be tried.', - display_language_fallback, t_admx_file) + root, display_language_fallback, admx_file_name + ".adml" + ) + if not __salt__["file.file_exists"](adml_file): + log.info( + "An ADML file in the specified ADML " + 'fallback language "%s" ' + 'does not exist for the ADMX "%s" ' + "the abbreviated fallback language code " + "will be tried.", + display_language_fallback, + t_admx_file, + ) adml_file = os.path.join( root, - display_language_fallback.split('-')[0], - admx_file_name + '.adml') - if not __salt__['file.file_exists'](adml_file): - msg = ('An ADML file in the specified ADML language ' - '"{0}" and the fallback language "{1}" do not ' - 'exist for the ADMX "{2}".') - raise SaltInvocationError(msg.format(language, - display_language_fallback, - t_admx_file)) + display_language_fallback.split("-")[0], + admx_file_name + ".adml", + ) + if not __salt__["file.file_exists"](adml_file): + msg = ( + "An ADML file in the specified ADML language " + '"{0}" and the fallback language "{1}" do not ' + 'exist for the ADMX "{2}".' + ) + raise SaltInvocationError( + msg.format( + language, display_language_fallback, t_admx_file + ) + ) # Parse xml for the ADML file try: xml_tree = _parse_xml(adml_file) except lxml.etree.XMLSyntaxError: - log.error('An error was found while processing adml ' - 'file %s, all policies from this file will ' - 'be unavailable via this module', adml_file) + log.error( + "An error was found while processing adml " + "file %s, all policies from this file will " + "be unavailable via this module", + adml_file, + ) continue if None in namespaces: - namespaces['None'] = namespaces[None] + namespaces["None"] = namespaces[None] namespaces.pop(None) policydefs_resources = policydefs_resources_localname_xpath(xml_tree) for policydefs_resource in policydefs_resources: t_poldef = policydefs_resource t_poldef = _updateNamespace(t_poldef, this_namespace) - policydef_resources_xpath(t_policy_definition_resources)[0].append(t_poldef) - __context__['lgpo.policy_definitions'] = t_policy_definitions - __context__['lgpo.policy_resources'] = t_policy_definition_resources + policydef_resources_xpath(t_policy_definition_resources)[0].append( + t_poldef + ) + __context__["lgpo.policy_definitions"] = t_policy_definitions + __context__["lgpo.policy_resources"] = t_policy_definition_resources -def _get_policy_definitions(path='c:\\Windows\\PolicyDefinitions', - language='en-US'): - if 'lgpo.policy_definitions' not in __context__: - log.debug('LGPO: Loading policy definitions') +def _get_policy_definitions(path="c:\\Windows\\PolicyDefinitions", language="en-US"): + if "lgpo.policy_definitions" not in __context__: + log.debug("LGPO: Loading policy definitions") _load_policy_definitions(path=path, language=language) - return __context__['lgpo.policy_definitions'] + return __context__["lgpo.policy_definitions"] -def _get_policy_resources(path='c:\\Windows\\PolicyDefinitions', - language='en-US'): - if 'lgpo.policy_resources' not in __context__: - log.debug('LGPO: Loading policy resources') +def _get_policy_resources(path="c:\\Windows\\PolicyDefinitions", language="en-US"): + if "lgpo.policy_resources" not in __context__: + log.debug("LGPO: Loading policy resources") _load_policy_definitions(path=path, language=language) - return __context__['lgpo.policy_resources'] + return __context__["lgpo.policy_resources"] def _buildElementNsmap(using_elements): - ''' + """ build a namespace map for an ADMX element - ''' + """ thisMap = {} for e in using_elements: - thisMap[e.attrib['prefix']] = e.attrib['namespace'] + thisMap[e.attrib["prefix"]] = e.attrib["namespace"] return thisMap def _get_advaudit_defaults(option=None): - ''' + """ Loads audit.csv defaults into a dict in __context__ called 'lgpo.audit_defaults'. The dictionary includes fieldnames and all configurable policies as keys. The values are used to create/modify the @@ -5182,39 +5020,39 @@ def _get_advaudit_defaults(option=None): Returns: dict: If ``None`` or one of the audit settings is passed list: If ``fieldnames`` is passed - ''' - if 'lgpo.audit_defaults' not in __context__: + """ + if "lgpo.audit_defaults" not in __context__: # Get available setting names and GUIDs # This is used to get the fieldnames and GUIDs for individual policies - log.debug('Loading auditpol defaults into __context__') - dump = __utils__['auditpol.get_auditpol_dump']() + log.debug("Loading auditpol defaults into __context__") + dump = __utils__["auditpol.get_auditpol_dump"]() reader = csv.DictReader(dump) - audit_defaults = {'fieldnames': reader.fieldnames} + audit_defaults = {"fieldnames": reader.fieldnames} for row in reader: - row['Machine Name'] = '' - row['Auditpol Name'] = row['Subcategory'] + row["Machine Name"] = "" + row["Auditpol Name"] = row["Subcategory"] # Special handling for snowflake scenarios where the audit.csv names # don't match the auditpol names - if row['Subcategory'] == 'Central Policy Staging': - row['Subcategory'] = 'Audit Central Access Policy Staging' - elif row['Subcategory'] == 'Plug and Play Events': - row['Subcategory'] = 'Audit PNP Activity' - elif row['Subcategory'] == 'Token Right Adjusted Events': - row['Subcategory'] = 'Audit Token Right Adjusted' + if row["Subcategory"] == "Central Policy Staging": + row["Subcategory"] = "Audit Central Access Policy Staging" + elif row["Subcategory"] == "Plug and Play Events": + row["Subcategory"] = "Audit PNP Activity" + elif row["Subcategory"] == "Token Right Adjusted Events": + row["Subcategory"] = "Audit Token Right Adjusted" else: - row['Subcategory'] = 'Audit {0}'.format(row['Subcategory']) - audit_defaults[row['Subcategory']] = row + row["Subcategory"] = "Audit {0}".format(row["Subcategory"]) + audit_defaults[row["Subcategory"]] = row - __context__['lgpo.audit_defaults'] = audit_defaults + __context__["lgpo.audit_defaults"] = audit_defaults if option: - return __context__['lgpo.audit_defaults'][option] + return __context__["lgpo.audit_defaults"][option] else: - return __context__['lgpo.audit_defaults'] + return __context__["lgpo.audit_defaults"] def _get_advaudit_value(option): - ''' + """ Get the Advanced Auditing policy as configured in ``C:\\Windows\\Security\\Audit\\audit.csv`` @@ -5223,41 +5061,47 @@ def _get_advaudit_value(option): Returns: bool: ``True`` if successful, otherwise ``False`` - ''' - if 'lgpo.adv_audit_data' not in __context__: - system_root = os.environ.get('SystemRoot', 'C:\\Windows') - f_audit = os.path.join(system_root, 'security', 'audit', 'audit.csv') - f_audit_gpo = os.path.join(system_root, 'System32', 'GroupPolicy', - 'Machine', 'Microsoft', 'Windows NT', - 'Audit', 'audit.csv') + """ + if "lgpo.adv_audit_data" not in __context__: + system_root = os.environ.get("SystemRoot", "C:\\Windows") + f_audit = os.path.join(system_root, "security", "audit", "audit.csv") + f_audit_gpo = os.path.join( + system_root, + "System32", + "GroupPolicy", + "Machine", + "Microsoft", + "Windows NT", + "Audit", + "audit.csv", + ) # Make sure there is an existing audit.csv file on the machine - if not __salt__['file.file_exists'](f_audit): - if __salt__['file.file_exists'](f_audit_gpo): + if not __salt__["file.file_exists"](f_audit): + if __salt__["file.file_exists"](f_audit_gpo): # If the GPO audit.csv exists, we'll use that one - __salt__['file.copy'](f_audit_gpo, f_audit) + __salt__["file.copy"](f_audit_gpo, f_audit) else: - field_names = _get_advaudit_defaults('fieldnames') + field_names = _get_advaudit_defaults("fieldnames") # If the file doesn't exist anywhere, create it with default # fieldnames - __salt__['file.makedirs'](f_audit) - __salt__['file.write'](f_audit, ','.join(field_names)) + __salt__["file.makedirs"](f_audit) + __salt__["file.write"](f_audit, ",".join(field_names)) audit_settings = {} - with salt.utils.files.fopen(f_audit, mode='r') as csv_file: + with salt.utils.files.fopen(f_audit, mode="r") as csv_file: reader = csv.DictReader(csv_file) for row in reader: - audit_settings.update( - {row['Subcategory']: row['Setting Value']}) + audit_settings.update({row["Subcategory"]: row["Setting Value"]}) - __context__['lgpo.adv_audit_data'] = audit_settings + __context__["lgpo.adv_audit_data"] = audit_settings - return __context__['lgpo.adv_audit_data'].get(option, None) + return __context__["lgpo.adv_audit_data"].get(option, None) def _set_audit_file_data(option, value): - ''' + """ Helper function that sets the Advanced Audit settings in the two .csv files on Windows. Those files are located at: C:\\Windows\\Security\\Audit\\audit.csv @@ -5269,30 +5113,40 @@ def _set_audit_file_data(option, value): Returns: bool: ``True`` if successful, otherwise ``False`` - ''' + """ # Set up some paths here - system_root = os.environ.get('SystemRoot', 'C:\\Windows') - f_audit = os.path.join(system_root, 'security', 'audit', 'audit.csv') - f_audit_gpo = os.path.join(system_root, 'System32', 'GroupPolicy', - 'Machine', 'Microsoft', 'Windows NT', - 'Audit', 'audit.csv') - f_temp = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.csv', - prefix='audit') + system_root = os.environ.get("SystemRoot", "C:\\Windows") + f_audit = os.path.join(system_root, "security", "audit", "audit.csv") + f_audit_gpo = os.path.join( + system_root, + "System32", + "GroupPolicy", + "Machine", + "Microsoft", + "Windows NT", + "Audit", + "audit.csv", + ) + f_temp = tempfile.NamedTemporaryFile( + mode="w", delete=False, suffix=".csv", prefix="audit" + ) # Lookup dict for "Inclusion Setting" field - auditpol_values = {'None': 'No Auditing', - '0': 'No Auditing', - '1': 'Success', - '2': 'Failure', - '3': 'Success and Failure'} + auditpol_values = { + "None": "No Auditing", + "0": "No Auditing", + "1": "Success", + "2": "Failure", + "3": "Success and Failure", + } try: # Open the existing audit.csv and load the csv `reader` - with salt.utils.files.fopen(f_audit, mode='r') as csv_file: + with salt.utils.files.fopen(f_audit, mode="r") as csv_file: reader = csv.DictReader(csv_file) # Open the temporary .csv and load the csv `writer` - with salt.utils.files.fopen(f_temp.name, mode='w') as tmp_file: + with salt.utils.files.fopen(f_temp.name, mode="w") as tmp_file: writer = csv.DictWriter(tmp_file, fieldnames=reader.fieldnames) # Write the header values (labels) @@ -5304,18 +5158,19 @@ def _set_audit_file_data(option, value): for row in reader: # If the row matches the value we're setting, update it with # the new value - if row['Subcategory'] == option: - if not value == 'None': + if row["Subcategory"] == option: + if not value == "None": # The value is not None, make the change - row['Inclusion Setting'] = auditpol_values[value] - row['Setting Value'] = value - log.trace('LGPO: Setting {0} to {1}' - ''.format(option, value)) + row["Inclusion Setting"] = auditpol_values[value] + row["Setting Value"] = value + log.trace( + "LGPO: Setting {0} to {1}" "".format(option, value) + ) writer.writerow(row) else: # value is None, remove it by not writing it to the # temp file - log.trace('LGPO: Removing {0}'.format(option)) + log.trace("LGPO: Removing {0}".format(option)) value_written = True # If it's not the value we're setting, just write it else: @@ -5325,36 +5180,38 @@ def _set_audit_file_data(option, value): # the existing audit.cvs file. Add the new setting with values # from the defaults if not value_written: - if not value == 'None': + if not value == "None": # value is not None, write the new value - log.trace('LGPO: Setting {0} to {1}' - ''.format(option, value)) + log.trace("LGPO: Setting {0} to {1}" "".format(option, value)) defaults = _get_advaudit_defaults(option) - writer.writerow({ - 'Machine Name': defaults['Machine Name'], - 'Policy Target': defaults['Policy Target'], - 'Subcategory': defaults['Subcategory'], - 'Subcategory GUID': defaults['Subcategory GUID'], - 'Inclusion Setting': auditpol_values[value], - 'Exclusion Setting': defaults['Exclusion Setting'], - 'Setting Value': value}) + writer.writerow( + { + "Machine Name": defaults["Machine Name"], + "Policy Target": defaults["Policy Target"], + "Subcategory": defaults["Subcategory"], + "Subcategory GUID": defaults["Subcategory GUID"], + "Inclusion Setting": auditpol_values[value], + "Exclusion Setting": defaults["Exclusion Setting"], + "Setting Value": value, + } + ) value_written = True if value_written: # Copy the temporary csv file over the existing audit.csv in both # locations if a value was written - __salt__['file.copy'](f_temp.name, f_audit, remove_existing=True) - __salt__['file.makedirs'](f_audit_gpo) - __salt__['file.copy'](f_temp.name, f_audit_gpo, remove_existing=True) + __salt__["file.copy"](f_temp.name, f_audit, remove_existing=True) + __salt__["file.makedirs"](f_audit_gpo) + __salt__["file.copy"](f_temp.name, f_audit_gpo, remove_existing=True) finally: f_temp.close() - __salt__['file.remove'](f_temp.name) + __salt__["file.remove"](f_temp.name) return value_written def _set_advaudit_pol_data(option, value): - ''' + """ Helper function that updates the current applied settings to match what has just been set in the audit.csv files. We're doing it this way instead of running `gpupdate` @@ -5365,20 +5222,22 @@ def _set_advaudit_pol_data(option, value): Returns: bool: ``True`` if successful, otherwise ``False`` - ''' - auditpol_values = {'None': 'No Auditing', - '0': 'No Auditing', - '1': 'Success', - '2': 'Failure', - '3': 'Success and Failure'} + """ + auditpol_values = { + "None": "No Auditing", + "0": "No Auditing", + "1": "Success", + "2": "Failure", + "3": "Success and Failure", + } defaults = _get_advaudit_defaults(option) - return __utils__['auditpol.set_setting']( - name=defaults['Auditpol Name'], - value=auditpol_values[value]) + return __utils__["auditpol.set_setting"]( + name=defaults["Auditpol Name"], value=auditpol_values[value] + ) def _set_advaudit_value(option, value): - ''' + """ Helper function to update the Advanced Audit policy on the machine. This function modifies the two ``audit.csv`` files in the following locations: @@ -5395,175 +5254,187 @@ def _set_advaudit_value(option, value): Returns: bool: ``True`` if successful, otherwise ``False`` - ''' + """ # Set the values in both audit.csv files if not _set_audit_file_data(option=option, value=value): - raise CommandExecutionError('Failed to set audit.csv option: {0}' - ''.format(option)) + raise CommandExecutionError( + "Failed to set audit.csv option: {0}" "".format(option) + ) # Apply the settings locally if not _set_advaudit_pol_data(option=option, value=value): # Only log this error, it will be in effect the next time the machine # updates its policy - log.error('Failed to apply audit setting: {0}\n' - 'Policy will take effect on next GPO update'.format(option)) + log.error( + "Failed to apply audit setting: {0}\n" + "Policy will take effect on next GPO update".format(option) + ) # Update __context__ if value is None: - log.debug('LGPO: Removing Advanced Audit data: {0}'.format(option)) - __context__['lgpo.adv_audit_data'].pop(option) + log.debug("LGPO: Removing Advanced Audit data: {0}".format(option)) + __context__["lgpo.adv_audit_data"].pop(option) else: - log.debug('LGPO: Updating Advanced Audit data: {0}: {1}' - ''.format(option, value)) - __context__['lgpo.adv_audit_data'][option] = value + log.debug( + "LGPO: Updating Advanced Audit data: {0}: {1}" "".format(option, value) + ) + __context__["lgpo.adv_audit_data"][option] = value return True def _get_netsh_value(profile, option): - if 'lgpo.netsh_data' not in __context__: - __context__['lgpo.netsh_data'] = {} + if "lgpo.netsh_data" not in __context__: + __context__["lgpo.netsh_data"] = {} - if profile not in __context__['lgpo.netsh_data']: - log.debug('LGPO: Loading netsh data for {0} profile'.format(profile)) - settings = salt.utils.win_lgpo_netsh.get_all_settings(profile=profile, - store='lgpo') - __context__['lgpo.netsh_data'].update({profile: settings}) - log.trace('LGPO: netsh returning value: {0}' - ''.format(__context__['lgpo.netsh_data'][profile][option])) - return __context__['lgpo.netsh_data'][profile][option] + if profile not in __context__["lgpo.netsh_data"]: + log.debug("LGPO: Loading netsh data for {0} profile".format(profile)) + settings = salt.utils.win_lgpo_netsh.get_all_settings( + profile=profile, store="lgpo" + ) + __context__["lgpo.netsh_data"].update({profile: settings}) + log.trace( + "LGPO: netsh returning value: {0}" + "".format(__context__["lgpo.netsh_data"][profile][option]) + ) + return __context__["lgpo.netsh_data"][profile][option] def _set_netsh_value(profile, section, option, value): - if section not in ('firewallpolicy', 'settings', 'logging', 'state'): - raise ValueError('LGPO: Invalid section: {0}'.format(section)) - log.trace('LGPO: Setting the following\n' - 'Profile: {0}\n' - 'Section: {1}\n' - 'Option: {2}\n' - 'Value: {3}'.format(profile, section, option, value)) - if section == 'firewallpolicy': + if section not in ("firewallpolicy", "settings", "logging", "state"): + raise ValueError("LGPO: Invalid section: {0}".format(section)) + log.trace( + "LGPO: Setting the following\n" + "Profile: {0}\n" + "Section: {1}\n" + "Option: {2}\n" + "Value: {3}".format(profile, section, option, value) + ) + if section == "firewallpolicy": salt.utils.win_lgpo_netsh.set_firewall_settings( profile=profile, - inbound=value if option == 'Inbound' else None, - outbound=value if option == 'Outbound' else None, - store='lgpo') - if section == 'settings': + inbound=value if option == "Inbound" else None, + outbound=value if option == "Outbound" else None, + store="lgpo", + ) + if section == "settings": salt.utils.win_lgpo_netsh.set_settings( - profile=profile, setting=option, value=value, store='lgpo') - if section == 'state': - salt.utils.win_lgpo_netsh.set_state( - profile=profile, state=value, store='lgpo') - if section == 'logging': - if option in ('FileName', 'MaxFileSize'): - if value == 'Not configured': - value = 'notconfigured' + profile=profile, setting=option, value=value, store="lgpo" + ) + if section == "state": + salt.utils.win_lgpo_netsh.set_state(profile=profile, state=value, store="lgpo") + if section == "logging": + if option in ("FileName", "MaxFileSize"): + if value == "Not configured": + value = "notconfigured" # Trim log for the two logging options - if option.startswith('Log'): + if option.startswith("Log"): option = option[3:] salt.utils.win_lgpo_netsh.set_logging_settings( - profile=profile, setting=option, value=value, store='lgpo') - log.trace('LGPO: Clearing netsh data for {0} profile'.format(profile)) - __context__['lgpo.netsh_data'].pop(profile) + profile=profile, setting=option, value=value, store="lgpo" + ) + log.trace("LGPO: Clearing netsh data for {0} profile".format(profile)) + __context__["lgpo.netsh_data"].pop(profile) return True def _load_secedit_data(): - ''' + """ Helper function that loads secedit data. It runs `secedit /export /cfg <file_name>` which creates a file that contains the secedit data. Returns: str: The contents of the file generated by the secedit command - ''' + """ try: - f_exp = os.path.join(__opts__['cachedir'], - 'secedit-{0}.txt'.format(UUID)) - __salt__['cmd.run'](['secedit', '/export', '/cfg', f_exp]) - with io.open(f_exp, encoding='utf-16') as fp: + f_exp = os.path.join(__opts__["cachedir"], "secedit-{0}.txt".format(UUID)) + __salt__["cmd.run"](["secedit", "/export", "/cfg", f_exp]) + with io.open(f_exp, encoding="utf-16") as fp: secedit_data = fp.readlines() return secedit_data finally: - if __salt__['file.file_exists'](f_exp): - __salt__['file.remove'](f_exp) + if __salt__["file.file_exists"](f_exp): + __salt__["file.remove"](f_exp) def _get_secedit_data(): - ''' + """ Helper function that returns the secedit data in __context__ if it exists and puts the secedit data in __context__ if it does not. Returns: str: secedit data from __context__ - ''' - if 'lgpo.secedit_data' not in __context__: - log.debug('LGPO: Loading secedit data') - __context__['lgpo.secedit_data'] = _load_secedit_data() - return __context__['lgpo.secedit_data'] + """ + if "lgpo.secedit_data" not in __context__: + log.debug("LGPO: Loading secedit data") + __context__["lgpo.secedit_data"] = _load_secedit_data() + return __context__["lgpo.secedit_data"] def _get_secedit_value(option): - ''' + """ Helper function that looks for the passed option in the secedit data - ''' + """ secedit_data = _get_secedit_data() for _line in secedit_data: if _line.startswith(option): - return _line.split('=')[1].strip() - return 'Not Defined' + return _line.split("=")[1].strip() + return "Not Defined" def _write_secedit_data(inf_data): - ''' + """ Helper function to write secedit data to the database - ''' + """ # Set file names - f_sdb = os.path.join(__opts__['cachedir'], 'secedit-{0}.sdb'.format(UUID)) - f_inf = os.path.join(__opts__['cachedir'], 'secedit-{0}.inf'.format(UUID)) + f_sdb = os.path.join(__opts__["cachedir"], "secedit-{0}.sdb".format(UUID)) + f_inf = os.path.join(__opts__["cachedir"], "secedit-{0}.inf".format(UUID)) try: # Write the changes to the inf file - __salt__['file.write'](f_inf, inf_data) + __salt__["file.write"](f_inf, inf_data) # Run secedit to make the change - cmd = ['secedit', '/configure', '/db', f_sdb, '/cfg', f_inf] - retcode = __salt__['cmd.retcode'](cmd) + cmd = ["secedit", "/configure", "/db", f_sdb, "/cfg", f_inf] + retcode = __salt__["cmd.retcode"](cmd) # Success if retcode == 0: # Pop secedit data so it will always be current - __context__.pop('lgpo.secedit_data', None) + __context__.pop("lgpo.secedit_data", None) return True # Failure return False finally: # Cleanup our scratch files - if __salt__['file.file_exists'](f_sdb): - __salt__['file.remove'](f_sdb) - if __salt__['file.file_exists'](f_inf): - __salt__['file.remove'](f_inf) + if __salt__["file.file_exists"](f_sdb): + __salt__["file.remove"](f_sdb) + if __salt__["file.file_exists"](f_inf): + __salt__["file.remove"](f_inf) def _transform_value(value, policy, transform_type): - ''' + """ helper function to transform the policy value into something that more closely matches how the policy is displayed in the gpedit GUI - ''' + """ t_kwargs = {} - if 'Transform' in policy: - if transform_type in policy['Transform']: + if "Transform" in policy: + if transform_type in policy["Transform"]: _policydata = _policy_info() - if transform_type + 'Args' in policy['Transform']: - t_kwargs = policy['Transform'][transform_type + 'Args'] - return getattr(_policydata, policy['Transform'][transform_type])(value, **t_kwargs) + if transform_type + "Args" in policy["Transform"]: + t_kwargs = policy["Transform"][transform_type + "Args"] + return getattr(_policydata, policy["Transform"][transform_type])( + value, **t_kwargs + ) else: return value else: - if 'Registry' in policy: - if value == '(value not set)': - return 'Not Defined' + if "Registry" in policy: + if value == "(value not set)": + return "Not Defined" return value def _validateSetting(value, policy): - ''' + """ helper function to validate specified value is appropriate for the policy if the 'Settings' key is a list, the value will check that it is in the list if the 'Settings' key is a dict we will try to execute the function name @@ -5572,16 +5443,18 @@ def _validateSetting(value, policy): if the 'Settings' key is None, we won't do any validation and just return True if the Policy has 'Children', we'll validate their settings too - ''' - log.debug('validating {0} for policy {1}'.format(value, policy)) - if 'Settings' in policy: - if policy['Settings']: - if isinstance(policy['Settings'], list): - if value not in policy['Settings']: + """ + log.debug("validating {0} for policy {1}".format(value, policy)) + if "Settings" in policy: + if policy["Settings"]: + if isinstance(policy["Settings"], list): + if value not in policy["Settings"]: return False - elif isinstance(policy['Settings'], dict): + elif isinstance(policy["Settings"], dict): _policydata = _policy_info() - if not getattr(_policydata, policy['Settings']['Function'])(value, **policy['Settings']['Args']): + if not getattr(_policydata, policy["Settings"]["Function"])( + value, **policy["Settings"]["Args"] + ): return False else: return True @@ -5590,40 +5463,45 @@ def _validateSetting(value, policy): def _addAccountRights(sidObject, user_right): - ''' + """ helper function to add an account right to a user - ''' + """ try: if sidObject: - _polHandle = win32security.LsaOpenPolicy(None, win32security.POLICY_ALL_ACCESS) + _polHandle = win32security.LsaOpenPolicy( + None, win32security.POLICY_ALL_ACCESS + ) user_rights_list = [user_right] - _ret = win32security.LsaAddAccountRights(_polHandle, sidObject, user_rights_list) + _ret = win32security.LsaAddAccountRights( + _polHandle, sidObject, user_rights_list + ) return True # TODO: This needs to be more specific except Exception as e: # pylint: disable=broad-except - log.exception('Error attempting to add account right, exception was %s', - e) + log.exception("Error attempting to add account right, exception was %s", e) return False def _delAccountRights(sidObject, user_right): - ''' + """ helper function to remove an account right from a user - ''' + """ try: _polHandle = win32security.LsaOpenPolicy(None, win32security.POLICY_ALL_ACCESS) user_rights_list = [user_right] - _ret = win32security.LsaRemoveAccountRights(_polHandle, sidObject, False, user_rights_list) + _ret = win32security.LsaRemoveAccountRights( + _polHandle, sidObject, False, user_rights_list + ) return True except Exception as e: # pylint: disable=broad-except - log.exception('Error attempting to delete account right') + log.exception("Error attempting to delete account right") return False def _getRightsAssignments(user_right): - ''' + """ helper function to return all the user rights assignments/users - ''' + """ sids = [] polHandle = win32security.LsaOpenPolicy(None, win32security.POLICY_ALL_ACCESS) sids = win32security.LsaEnumerateAccountsWithUserRight(polHandle, user_right) @@ -5631,22 +5509,24 @@ def _getRightsAssignments(user_right): def _getAdmlDisplayName(adml_xml_data, display_name): - ''' + """ helper function to take the 'displayName' attribute of an element and find the value from the ADML data adml_xml_data :: XML data of all ADML files to search display_name :: the value of the displayName attribute from the ADMX entry to search the ADML data for - ''' - if display_name.startswith('$(') and display_name.endswith(')'): - display_name = re.sub(r'(^\$\(|\)$)', '', display_name) - display_name = display_name.split('.') + """ + if display_name.startswith("$(") and display_name.endswith(")"): + display_name = re.sub(r"(^\$\(|\)$)", "", display_name) + display_name = display_name.split(".") displayname_type = display_name[0] displayname_id = display_name[1] - search_results = ADML_DISPLAY_NAME_XPATH(adml_xml_data, - displayNameType=displayname_type, - displayNameId=displayname_id) + search_results = ADML_DISPLAY_NAME_XPATH( + adml_xml_data, + displayNameType=displayname_type, + displayNameId=displayname_id, + ) if search_results: for result in search_results: # Needs the `strip()` because some adml data has an extra space @@ -5657,11 +5537,13 @@ def _getAdmlDisplayName(adml_xml_data, display_name): def _getAdmlPresentationRefId(adml_data, ref_id): - ''' + """ helper function to check for a presentation label for a policy element - ''' - search_results = adml_data.xpath('//*[@*[local-name() = "refId"] = "{0}"]'.format(ref_id)) - alternate_label = '' + """ + search_results = adml_data.xpath( + '//*[@*[local-name() = "refId"] = "{0}"]'.format(ref_id) + ) + alternate_label = "" if search_results: for result in search_results: the_localname = etree.QName(result.tag).localname @@ -5685,56 +5567,63 @@ def _getAdmlPresentationRefId(adml_data, ref_id): for p_item in presentation_element.getchildren(): if p_item == result: break - if etree.QName(p_item.tag).localname == 'text': - if getattr(p_item, 'text'): - alternate_label = getattr(p_item, 'text').rstrip() - if alternate_label.endswith('.'): - alternate_label = '' + if etree.QName(p_item.tag).localname == "text": + if getattr(p_item, "text"): + alternate_label = getattr(p_item, "text").rstrip() + if alternate_label.endswith("."): + alternate_label = "" - if the_localname in ['textBox', 'comboBox']: + if the_localname in ["textBox", "comboBox"]: label_items = result.xpath('.//*[local-name() = "label"]') for label_item in label_items: if label_item.text: - return label_item.text.rstrip().rstrip(':') - elif the_localname in ['decimalTextBox', 'longDecimalTextBox', - 'dropdownList', 'listBox', 'checkBox', - 'text', 'multiTextBox']: + return label_item.text.rstrip().rstrip(":") + elif the_localname in [ + "decimalTextBox", + "longDecimalTextBox", + "dropdownList", + "listBox", + "checkBox", + "text", + "multiTextBox", + ]: if result.text: - return result.text.rstrip().rstrip(':') + return result.text.rstrip().rstrip(":") else: - return alternate_label.rstrip(':') + return alternate_label.rstrip(":") return None -def _getFullPolicyName(policy_item, - policy_name, - return_full_policy_names, - adml_language): - ''' +def _getFullPolicyName( + policy_item, policy_name, return_full_policy_names, adml_language +): + """ helper function to retrieve the full policy name if needed - ''' + """ if policy_name in adm_policy_name_map[return_full_policy_names]: return adm_policy_name_map[return_full_policy_names][policy_name] adml_data = _get_policy_resources(language=adml_language) - if return_full_policy_names and 'displayName' in policy_item.attrib: - fullPolicyName = _getAdmlDisplayName(adml_data, policy_item.attrib['displayName']) + if return_full_policy_names and "displayName" in policy_item.attrib: + fullPolicyName = _getAdmlDisplayName( + adml_data, policy_item.attrib["displayName"] + ) if fullPolicyName: adm_policy_name_map[return_full_policy_names][policy_name] = fullPolicyName policy_name = fullPolicyName - elif return_full_policy_names and 'id' in policy_item.attrib: - fullPolicyName = _getAdmlPresentationRefId(adml_data, policy_item.attrib['id']) + elif return_full_policy_names and "id" in policy_item.attrib: + fullPolicyName = _getAdmlPresentationRefId(adml_data, policy_item.attrib["id"]) if fullPolicyName: adm_policy_name_map[return_full_policy_names][policy_name] = fullPolicyName policy_name = fullPolicyName - policy_name = policy_name.rstrip(':').rstrip() + policy_name = policy_name.rstrip(":").rstrip() return policy_name def _regexSearchRegPolData(search_string, policy_data): - ''' + """ helper function to do a search of Policy data from a registry.pol file returns True if the regex search_string is found, otherwise False - ''' + """ if policy_data: if search_string: match = re.search(search_string, policy_data, re.IGNORECASE) @@ -5744,21 +5633,23 @@ def _regexSearchRegPolData(search_string, policy_data): def _getDataFromRegPolData(search_string, policy_data, return_value_name=False): - ''' + """ helper function to do a search of Policy data from a registry.pol file returns the "data" field https://msdn.microsoft.com/en-us/library/aa374407(VS.85).aspx [key;value;type;size;data] - ''' + """ value = None values = [] - encoded_semicolon = ';'.encode('utf-16-le') + encoded_semicolon = ";".encode("utf-16-le") if return_value_name: values = {} if search_string: registry = Registry() if len(search_string.split(encoded_semicolon)) >= 3: - vtype = registry.vtype_reverse[ord(search_string.split(encoded_semicolon)[2].decode('utf-32-le'))] + vtype = registry.vtype_reverse[ + ord(search_string.split(encoded_semicolon)[2].decode("utf-32-le")) + ] else: vtype = None search_string = re.escape(search_string) @@ -5766,37 +5657,38 @@ def _getDataFromRegPolData(search_string, policy_data, return_value_name=False): matches = [m for m in matches] if matches: for match in matches: - pol_entry = policy_data[match.start():(policy_data.index(']'.encode('utf-16-le'), - match.end()) - ) - ].split(encoded_semicolon) + pol_entry = policy_data[ + match.start() : ( + policy_data.index("]".encode("utf-16-le"), match.end()) + ) + ].split(encoded_semicolon) if len(pol_entry) >= 2: - valueName = pol_entry[1].decode('utf-16-le').rstrip(chr(0)) + valueName = pol_entry[1].decode("utf-16-le").rstrip(chr(0)) if len(pol_entry) >= 5: # Sometimes a semicolon-separated value gets split into # additional elements in the Registry.pol file. For example, # a value of test1;test2;test3 will be 'test1', 'test2', and # 'test3' at the end of the Registry.pol file entry value = encoded_semicolon.join(pol_entry[4:]) - if vtype == 'REG_DWORD' or vtype == 'REG_QWORD': + if vtype == "REG_DWORD" or vtype == "REG_QWORD": if value: - if vtype == 'REG_DWORD': - for v in struct.unpack(b'I', value): + if vtype == "REG_DWORD": + for v in struct.unpack(b"I", value): value = v - elif vtype == 'REG_QWORD': - for v in struct.unpack(b'Q', value): + elif vtype == "REG_QWORD": + for v in struct.unpack(b"Q", value): value = v else: value = 0 - elif vtype == 'REG_MULTI_SZ': - value = value.decode('utf-16-le').rstrip(chr(0)).split(chr(0)) + elif vtype == "REG_MULTI_SZ": + value = value.decode("utf-16-le").rstrip(chr(0)).split(chr(0)) else: - value = value.decode('utf-16-le').rstrip(chr(0)) + value = value.decode("utf-16-le").rstrip(chr(0)) if return_value_name: - log.trace('we want value names and the value') + log.trace("we want value names and the value") values[valueName] = value elif len(matches) > 1: - log.trace('we have multiple matches, we will return a list') + log.trace("we have multiple matches, we will return a list") values.append(value) if values: value = values @@ -5804,8 +5696,15 @@ def _getDataFromRegPolData(search_string, policy_data, return_value_name=False): return value -def _checkListItem(policy_element, policy_name, policy_key, xpath_object, policy_file_data, test_items=True): - ''' +def _checkListItem( + policy_element, + policy_name, + policy_key, + xpath_object, + policy_file_data, + test_items=True, +): + """ helper function to process an enabled/disabled/true/falseList set if test_items is True, it will determine if the policy is enabled or @@ -5817,9 +5716,11 @@ def _checkListItem(policy_element, policy_name, policy_key, xpath_object, policy returns True if the enabled/disabledList is 100% configured in the registry.pol file, otherwise returns False - ''' - xpath_string = ('.//*[local-name() = "decimal" or local-name() = "delete"' - ' or local-name() = "longDecimal" or local-name() = "string"]') + """ + xpath_string = ( + './/*[local-name() = "decimal" or local-name() = "delete"' + ' or local-name() = "longDecimal" or local-name() = "string"]' + ) value_item_child_xpath = etree.XPath(xpath_string) expected_strings = [] for list_element in xpath_object(policy_element): @@ -5827,37 +5728,43 @@ def _checkListItem(policy_element, policy_name, policy_key, xpath_object, policy required_items = 0 for item in list_element.getchildren(): required_items = required_items + 1 - if 'key' in item.attrib: - item_key = item.attrib['key'] + if "key" in item.attrib: + item_key = item.attrib["key"] else: item_key = policy_key - if 'valueName' in item.attrib: - item_valuename = item.attrib['valueName'] + if "valueName" in item.attrib: + item_valuename = item.attrib["valueName"] else: - log.error('%s item with attributes %s in policy %s does not ' - 'have the required "valueName" attribute', - etree.QName(list_element).localname, - item.attrib, policy_element.attrib) + log.error( + "%s item with attributes %s in policy %s does not " + 'have the required "valueName" attribute', + etree.QName(list_element).localname, + item.attrib, + policy_element.attrib, + ) break for value_item in value_item_child_xpath(item): - search_string = _processValueItem(value_item, - item_key, - item_valuename, - policy_element, - item) + search_string = _processValueItem( + value_item, item_key, item_valuename, policy_element, item + ) if test_items: - if _regexSearchRegPolData(re.escape(search_string), policy_file_data): + if _regexSearchRegPolData( + re.escape(search_string), policy_file_data + ): configured_items = configured_items + 1 - log.trace('found the search string in the pol file,' - '%s of %s items for policy %s are ' - 'configured in registry.pol', - configured_items, required_items, - policy_name) + log.trace( + "found the search string in the pol file," + "%s of %s items for policy %s are " + "configured in registry.pol", + configured_items, + required_items, + policy_name, + ) else: expected_strings.append(search_string) if test_items: if required_items > 0 and required_items == configured_items: - log.trace('%s all items are set', policy_name) + log.trace("%s all items are set", policy_name) return True if test_items: return False @@ -5865,10 +5772,17 @@ def _checkListItem(policy_element, policy_name, policy_key, xpath_object, policy return expected_strings -def _checkValueItemParent(policy_element, policy_name, policy_key, - policy_valueName, xpath_object, policy_file_data, - check_deleted=False, test_item=True): - ''' +def _checkValueItemParent( + policy_element, + policy_name, + policy_key, + policy_valueName, + xpath_object, + policy_file_data, + check_deleted=False, + test_item=True, +): + """ helper function to process the parent of a value item object if test_item is True, it will determine if the policy is enabled/disabled returns True if the value is configured in the registry.pol file, otherwise returns False @@ -5880,94 +5794,118 @@ def _checkValueItemParent(policy_element, policy_name, policy_key, enabledValue: https://msdn.microsoft.com/en-us/library/dn606006(v=vs.85).aspx disabledValue: https://msdn.microsoft.com/en-us/library/dn606001(v=vs.85).aspx - ''' + """ for element in xpath_object(policy_element): for value_item in element.getchildren(): - search_string = _processValueItem(value_item, - policy_key, - policy_valueName, - policy_element, - element, - check_deleted=check_deleted) + search_string = _processValueItem( + value_item, + policy_key, + policy_valueName, + policy_element, + element, + check_deleted=check_deleted, + ) if not test_item: return search_string if _regexSearchRegPolData(re.escape(search_string), policy_file_data): - log.trace('found the search string in the pol file, ' - '%s is configured', policy_name) + log.trace( + "found the search string in the pol file, " "%s is configured", + policy_name, + ) return True return False def _encode_string(value): - encoded_null = chr(0).encode('utf-16-le') + encoded_null = chr(0).encode("utf-16-le") if value is None: return encoded_null elif not isinstance(value, six.string_types): # Should we raise an error here, or attempt to cast to a string - raise TypeError('Value {0} is not a string type\n' - 'Type: {1}'.format(repr(value), type(value))) - return b''.join([value.encode('utf-16-le'), encoded_null]) + raise TypeError( + "Value {0} is not a string type\n" + "Type: {1}".format(repr(value), type(value)) + ) + return b"".join([value.encode("utf-16-le"), encoded_null]) -def _buildKnownDataSearchString(reg_key, reg_valueName, reg_vtype, reg_data, - check_deleted=False): - ''' +def _buildKnownDataSearchString( + reg_key, reg_valueName, reg_vtype, reg_data, check_deleted=False +): + """ helper function similar to _processValueItem to build a search string for a known key/value/type/data - ''' + """ registry = Registry() this_element_value = None - expected_string = b'' - encoded_semicolon = ';'.encode('utf-16-le') - encoded_null = chr(0).encode('utf-16-le') + expected_string = b"" + encoded_semicolon = ";".encode("utf-16-le") + encoded_null = chr(0).encode("utf-16-le") if reg_key: - reg_key = reg_key.encode('utf-16-le') + reg_key = reg_key.encode("utf-16-le") if reg_valueName: - reg_valueName = reg_valueName.encode('utf-16-le') + reg_valueName = reg_valueName.encode("utf-16-le") if reg_data and not check_deleted: - if reg_vtype == 'REG_DWORD': - this_element_value = struct.pack(b'I', int(reg_data)) + if reg_vtype == "REG_DWORD": + this_element_value = struct.pack(b"I", int(reg_data)) elif reg_vtype == "REG_QWORD": - this_element_value = struct.pack(b'Q', int(reg_data)) - elif reg_vtype == 'REG_SZ': + this_element_value = struct.pack(b"Q", int(reg_data)) + elif reg_vtype == "REG_SZ": this_element_value = _encode_string(reg_data) if check_deleted: - reg_vtype = 'REG_SZ' - expected_string = b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon, - '**del.'.encode('utf-16-le'), - reg_valueName, - encoded_null, - encoded_semicolon, - chr(registry.vtype[reg_vtype]).encode('utf-32-le'), - encoded_semicolon, - six.unichr(len(' {0}'.format(chr(0)).encode('utf-16-le'))).encode('utf-32-le'), - encoded_semicolon, - ' '.encode('utf-16-le'), - encoded_null, - ']'.encode('utf-16-le')]) + reg_vtype = "REG_SZ" + expected_string = b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + "**del.".encode("utf-16-le"), + reg_valueName, + encoded_null, + encoded_semicolon, + chr(registry.vtype[reg_vtype]).encode("utf-32-le"), + encoded_semicolon, + six.unichr(len(" {0}".format(chr(0)).encode("utf-16-le"))).encode( + "utf-32-le" + ), + encoded_semicolon, + " ".encode("utf-16-le"), + encoded_null, + "]".encode("utf-16-le"), + ] + ) else: - expected_string = b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon, - reg_valueName, - encoded_null, - encoded_semicolon, - chr(registry.vtype[reg_vtype]).encode('utf-32-le'), - encoded_semicolon, - six.unichr(len(this_element_value)).encode('utf-32-le'), - encoded_semicolon, - this_element_value, - ']'.encode('utf-16-le')]) + expected_string = b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + reg_valueName, + encoded_null, + encoded_semicolon, + chr(registry.vtype[reg_vtype]).encode("utf-32-le"), + encoded_semicolon, + six.unichr(len(this_element_value)).encode("utf-32-le"), + encoded_semicolon, + this_element_value, + "]".encode("utf-16-le"), + ] + ) return expected_string -def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, - check_deleted=False, this_element_value=None): - ''' +def _processValueItem( + element, + reg_key, + reg_valuename, + policy, + parent_element, + check_deleted=False, + this_element_value=None, +): + """ helper function to process a value type item and generate the expected string in the Registry.pol file @@ -5984,182 +5922,232 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, this_element_value - a specific value to place into the expected string returned for "elements" children whose values are specified by the user - ''' + """ registry = Registry() expected_string = None # https://msdn.microsoft.com/en-us/library/dn606006(v=vs.85).aspx - this_vtype = 'REG_SZ' - encoded_semicolon = ';'.encode('utf-16-le') - encoded_null = chr(0).encode('utf-16-le') + this_vtype = "REG_SZ" + encoded_semicolon = ";".encode("utf-16-le") + encoded_null = chr(0).encode("utf-16-le") if reg_key: - reg_key = reg_key.encode('utf-16-le') + reg_key = reg_key.encode("utf-16-le") if reg_valuename: - reg_valuename = reg_valuename.encode('utf-16-le') - if etree.QName(element).localname == 'decimal' and etree.QName(parent_element).localname != 'elements': - this_vtype = 'REG_DWORD' - if 'value' in element.attrib: - this_element_value = struct.pack(b'I', int(element.attrib['value'])) + reg_valuename = reg_valuename.encode("utf-16-le") + if ( + etree.QName(element).localname == "decimal" + and etree.QName(parent_element).localname != "elements" + ): + this_vtype = "REG_DWORD" + if "value" in element.attrib: + this_element_value = struct.pack(b"I", int(element.attrib["value"])) else: - log.error('The %s child %s element for the policy with ' - 'attributes: %s does not have the required "value" ' - 'attribute. The element attributes are: %s', - etree.QName(parent_element).localname, - etree.QName(element).localname, - policy.attrib, - element.attrib) + log.error( + "The %s child %s element for the policy with " + 'attributes: %s does not have the required "value" ' + "attribute. The element attributes are: %s", + etree.QName(parent_element).localname, + etree.QName(element).localname, + policy.attrib, + element.attrib, + ) return None - elif etree.QName(element).localname == 'longDecimal' and etree.QName(parent_element).localname != 'elements': + elif ( + etree.QName(element).localname == "longDecimal" + and etree.QName(parent_element).localname != "elements" + ): # WARNING: no longDecimals in current ADMX files included with 2012 # server, so untested/assumed - this_vtype = 'REG_QWORD' - if 'value' in element.attrib: - this_element_value = struct.pack(b'Q', int(element.attrib['value'])) + this_vtype = "REG_QWORD" + if "value" in element.attrib: + this_element_value = struct.pack(b"Q", int(element.attrib["value"])) else: - log.error('The %s child %s element for the policy with ' - 'attributes: %s does not have the required "value" ' - 'attribute. The element attributes are: %s', - etree.QName(parent_element).localname, - etree.QName(element).localname, - policy.attrib, - element.attrib) + log.error( + "The %s child %s element for the policy with " + 'attributes: %s does not have the required "value" ' + "attribute. The element attributes are: %s", + etree.QName(parent_element).localname, + etree.QName(element).localname, + policy.attrib, + element.attrib, + ) return None - elif etree.QName(element).localname == 'string': - this_vtype = 'REG_SZ' + elif etree.QName(element).localname == "string": + this_vtype = "REG_SZ" this_element_value = _encode_string(element.text) - elif etree.QName(parent_element).localname == 'elements': + elif etree.QName(parent_element).localname == "elements": standard_element_expected_string = True - if etree.QName(element).localname == 'boolean': + if etree.QName(element).localname == "boolean": # a boolean element that has no children will add a REG_DWORD == 1 # on true or delete the value on false # https://msdn.microsoft.com/en-us/library/dn605978(v=vs.85).aspx if this_element_value is False: check_deleted = True if not check_deleted: - this_vtype = 'REG_DWORD' - this_element_value = struct.pack('I', 1) + this_vtype = "REG_DWORD" + this_element_value = struct.pack("I", 1) standard_element_expected_string = False - elif etree.QName(element).localname == 'decimal': + elif etree.QName(element).localname == "decimal": # https://msdn.microsoft.com/en-us/library/dn605987(v=vs.85).aspx - this_vtype = 'REG_DWORD' + this_vtype = "REG_DWORD" requested_val = this_element_value if this_element_value is not None: - this_element_value = struct.pack(b'I', int(this_element_value)) - if 'storeAsText' in element.attrib: - if element.attrib['storeAsText'].lower() == 'true': - this_vtype = 'REG_SZ' + this_element_value = struct.pack(b"I", int(this_element_value)) + if "storeAsText" in element.attrib: + if element.attrib["storeAsText"].lower() == "true": + this_vtype = "REG_SZ" if requested_val is not None: - this_element_value = six.text_type(requested_val).encode('utf-16-le') + this_element_value = six.text_type(requested_val).encode( + "utf-16-le" + ) if check_deleted: - this_vtype = 'REG_SZ' - elif etree.QName(element).localname == 'longDecimal': + this_vtype = "REG_SZ" + elif etree.QName(element).localname == "longDecimal": # https://msdn.microsoft.com/en-us/library/dn606015(v=vs.85).aspx - this_vtype = 'REG_QWORD' + this_vtype = "REG_QWORD" requested_val = this_element_value if this_element_value is not None: - this_element_value = struct.pack(b'Q', int(this_element_value)) - if 'storeAsText' in element.attrib: - if element.attrib['storeAsText'].lower() == 'true': - this_vtype = 'REG_SZ' + this_element_value = struct.pack(b"Q", int(this_element_value)) + if "storeAsText" in element.attrib: + if element.attrib["storeAsText"].lower() == "true": + this_vtype = "REG_SZ" if requested_val is not None: - this_element_value = six.text_type(requested_val).encode('utf-16-le') - elif etree.QName(element).localname == 'text': + this_element_value = six.text_type(requested_val).encode( + "utf-16-le" + ) + elif etree.QName(element).localname == "text": # https://msdn.microsoft.com/en-us/library/dn605969(v=vs.85).aspx - this_vtype = 'REG_SZ' - if 'expandable' in element.attrib: - if element.attrib['expandable'].lower() == 'true': - this_vtype = 'REG_EXPAND_SZ' + this_vtype = "REG_SZ" + if "expandable" in element.attrib: + if element.attrib["expandable"].lower() == "true": + this_vtype = "REG_EXPAND_SZ" if this_element_value is not None: this_element_value = _encode_string(this_element_value) - elif etree.QName(element).localname == 'multiText': - this_vtype = 'REG_MULTI_SZ' if not check_deleted else 'REG_SZ' + elif etree.QName(element).localname == "multiText": + this_vtype = "REG_MULTI_SZ" if not check_deleted else "REG_SZ" if this_element_value is not None: - this_element_value = '{0}{1}{1}'.format(chr(0).join(this_element_value), chr(0)) - elif etree.QName(element).localname == 'list': + this_element_value = "{0}{1}{1}".format( + chr(0).join(this_element_value), chr(0) + ) + elif etree.QName(element).localname == "list": standard_element_expected_string = False - del_keys = b'' + del_keys = b"" element_valuenames = [] element_values = this_element_value if this_element_value is not None: - element_valuenames = list([str(z) for z in range(1, len(this_element_value) + 1)]) - if 'additive' in element.attrib: - if element.attrib['additive'].lower() == 'false': + element_valuenames = list( + [str(z) for z in range(1, len(this_element_value) + 1)] + ) + if "additive" in element.attrib: + if element.attrib["additive"].lower() == "false": # a delete values will be added before all the other # value = data pairs - del_keys = b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon, - '**delvals.'.encode('utf-16-le'), - encoded_null, - encoded_semicolon, - chr(registry.vtype[this_vtype]).encode('utf-32-le'), - encoded_semicolon, - six.unichr(len(' {0}'.format(chr(0)).encode('utf-16-le'))).encode('utf-32-le'), - encoded_semicolon, - ' '.encode('utf-16-le'), - encoded_null, - ']'.encode('utf-16-le')]) - if 'expandable' in element.attrib: - this_vtype = 'REG_EXPAND_SZ' - if element.attrib.get('explicitValue', 'false').lower() == 'true': + del_keys = b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + "**delvals.".encode("utf-16-le"), + encoded_null, + encoded_semicolon, + chr(registry.vtype[this_vtype]).encode("utf-32-le"), + encoded_semicolon, + six.unichr( + len(" {0}".format(chr(0)).encode("utf-16-le")) + ).encode("utf-32-le"), + encoded_semicolon, + " ".encode("utf-16-le"), + encoded_null, + "]".encode("utf-16-le"), + ] + ) + if "expandable" in element.attrib: + this_vtype = "REG_EXPAND_SZ" + if element.attrib.get("explicitValue", "false").lower() == "true": if this_element_value is not None: element_valuenames = [str(k) for k in this_element_value.keys()] element_values = [str(v) for v in this_element_value.values()] - elif 'valuePrefix' in element.attrib: + elif "valuePrefix" in element.attrib: # if the valuePrefix attribute exists, the valuenames are <prefix><number> # most prefixes attributes are empty in the admx files, so the valuenames # end up being just numbers - if element.attrib['valuePrefix'] != '': + if element.attrib["valuePrefix"] != "": if this_element_value is not None: - element_valuenames = ['{0}{1}'.format( - element.attrib['valuePrefix'], k) for k in element_valuenames] + element_valuenames = [ + "{0}{1}".format(element.attrib["valuePrefix"], k) + for k in element_valuenames + ] else: # if there is no valuePrefix attribute, the valuename is the value if element_values is not None: element_valuenames = [str(z) for z in element_values] if not check_deleted: if this_element_value is not None: - log.trace('_processValueItem has an explicit ' - 'element_value of %s', this_element_value) + log.trace( + "_processValueItem has an explicit " "element_value of %s", + this_element_value, + ) expected_string = del_keys - log.trace('element_valuenames == %s and element_values ' - '== %s', element_valuenames, element_values) + log.trace( + "element_valuenames == %s and element_values " "== %s", + element_valuenames, + element_values, + ) for i, item in enumerate(element_valuenames): - expected_string = expected_string + b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon, - element_valuenames[i].encode('utf-16-le'), - encoded_null, - encoded_semicolon, - chr(registry.vtype[this_vtype]).encode('utf-32-le'), - encoded_semicolon, - six.unichr(len('{0}{1}'.format(element_values[i], - chr(0)).encode('utf-16-le'))).encode('utf-32-le'), - encoded_semicolon, - _encode_string(element_values[i]), - ']'.encode('utf-16-le')]) + expected_string = expected_string + b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + element_valuenames[i].encode("utf-16-le"), + encoded_null, + encoded_semicolon, + chr(registry.vtype[this_vtype]).encode("utf-32-le"), + encoded_semicolon, + six.unichr( + len( + "{0}{1}".format( + element_values[i], chr(0) + ).encode("utf-16-le") + ) + ).encode("utf-32-le"), + encoded_semicolon, + _encode_string(element_values[i]), + "]".encode("utf-16-le"), + ] + ) else: - expected_string = del_keys + b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon]) + expected_string = del_keys + b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + ] + ) else: - expected_string = b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon, - '**delvals.'.encode('utf-16-le'), - encoded_null, - encoded_semicolon, - chr(registry.vtype[this_vtype]).encode('utf-32-le'), - encoded_semicolon, - six.unichr(len(' {0}'.format(chr(0)).encode('utf-16-le'))).encode('utf-32-le'), - encoded_semicolon, - ' '.encode('utf-16-le'), - encoded_null, - ']'.encode('utf-16-le')]) - elif etree.QName(element).localname == 'enum': + expected_string = b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + "**delvals.".encode("utf-16-le"), + encoded_null, + encoded_semicolon, + chr(registry.vtype[this_vtype]).encode("utf-32-le"), + encoded_semicolon, + six.unichr( + len(" {0}".format(chr(0)).encode("utf-16-le")) + ).encode("utf-32-le"), + encoded_semicolon, + " ".encode("utf-16-le"), + encoded_null, + "]".encode("utf-16-le"), + ] + ) + elif etree.QName(element).localname == "enum": if this_element_value is not None: pass @@ -6167,81 +6155,103 @@ def _processValueItem(element, reg_key, reg_valuename, policy, parent_element, if this_element_value is not None: # Sometimes values come in as strings if isinstance(this_element_value, str): - log.debug('Converting {0} to bytes'.format(this_element_value)) - this_element_value = this_element_value.encode('utf-32-le') - expected_string = b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon, - reg_valuename, - encoded_null, - encoded_semicolon, - chr(registry.vtype[this_vtype]).encode('utf-32-le'), - encoded_semicolon, - six.unichr(len(this_element_value)).encode('utf-32-le'), - encoded_semicolon, - this_element_value, - ']'.encode('utf-16-le')]) + log.debug("Converting {0} to bytes".format(this_element_value)) + this_element_value = this_element_value.encode("utf-32-le") + expected_string = b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + reg_valuename, + encoded_null, + encoded_semicolon, + chr(registry.vtype[this_vtype]).encode("utf-32-le"), + encoded_semicolon, + six.unichr(len(this_element_value)).encode("utf-32-le"), + encoded_semicolon, + this_element_value, + "]".encode("utf-16-le"), + ] + ) else: - expected_string = b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon, - reg_valuename, - encoded_null, - encoded_semicolon, - chr(registry.vtype[this_vtype]).encode('utf-32-le'), - encoded_semicolon]) + expected_string = b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + reg_valuename, + encoded_null, + encoded_semicolon, + chr(registry.vtype[this_vtype]).encode("utf-32-le"), + encoded_semicolon, + ] + ) if not expected_string: if etree.QName(element).localname == "delete" or check_deleted: # delete value - expected_string = b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon, - '**del.'.encode('utf-16-le'), - reg_valuename, - encoded_null, - encoded_semicolon, - chr(registry.vtype[this_vtype]).encode('utf-32-le'), - encoded_semicolon, - six.unichr(len(' {0}'.format(chr(0)).encode('utf-16-le'))).encode('utf-32-le'), - encoded_semicolon, - ' '.encode('utf-16-le'), - encoded_null, - ']'.encode('utf-16-le')]) + expected_string = b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + "**del.".encode("utf-16-le"), + reg_valuename, + encoded_null, + encoded_semicolon, + chr(registry.vtype[this_vtype]).encode("utf-32-le"), + encoded_semicolon, + six.unichr(len(" {0}".format(chr(0)).encode("utf-16-le"))).encode( + "utf-32-le" + ), + encoded_semicolon, + " ".encode("utf-16-le"), + encoded_null, + "]".encode("utf-16-le"), + ] + ) else: - expected_string = b''.join(['['.encode('utf-16-le'), - reg_key, - encoded_null, - encoded_semicolon, - reg_valuename, - encoded_null, - encoded_semicolon, - chr(registry.vtype[this_vtype]).encode('utf-32-le'), - encoded_semicolon, - six.unichr(len(this_element_value)).encode('utf-32-le'), - encoded_semicolon, - this_element_value, - ']'.encode('utf-16-le')]) + expected_string = b"".join( + [ + "[".encode("utf-16-le"), + reg_key, + encoded_null, + encoded_semicolon, + reg_valuename, + encoded_null, + encoded_semicolon, + chr(registry.vtype[this_vtype]).encode("utf-32-le"), + encoded_semicolon, + six.unichr(len(this_element_value)).encode("utf-32-le"), + encoded_semicolon, + this_element_value, + "]".encode("utf-16-le"), + ] + ) return expected_string -def _checkAllAdmxPolicies(policy_class, - adml_language='en-US', - return_full_policy_names=False, - hierarchical_return=False, - return_not_configured=False): - ''' +def _checkAllAdmxPolicies( + policy_class, + adml_language="en-US", + return_full_policy_names=False, + hierarchical_return=False, + return_not_configured=False, +): + """ rewrite of _getAllAdminTemplateSettingsFromRegPolFile where instead of looking only at the contents of the file, we're going to loop through every policy and look in the registry.pol file to determine if it is enabled/disabled/not configured - ''' - log.trace('POLICY CLASS == %s', policy_class) + """ + log.trace("POLICY CLASS == %s", policy_class) module_policy_data = _policy_info() - policy_file_data = _read_regpol_file(module_policy_data.admx_registry_classes[policy_class]['policy_path']) + policy_file_data = _read_regpol_file( + module_policy_data.admx_registry_classes[policy_class]["policy_path"] + ) admx_policies = [] policy_vals = {} hierarchy = {} @@ -6249,28 +6259,38 @@ def _checkAllAdmxPolicies(policy_class, admx_policy_definitions = _get_policy_definitions(language=adml_language) adml_policy_resources = _get_policy_resources(language=adml_language) if policy_file_data: - log.trace('POLICY CLASS {0} has file data'.format(policy_class)) + log.trace("POLICY CLASS {0} has file data".format(policy_class)) policy_filedata_split = re.sub( - salt.utils.stringutils.to_bytes(r'\]{0}$'.format(chr(0))), - b'', - re.sub(salt.utils.stringutils.to_bytes(r'^\[{0}'.format(chr(0))), - b'', - re.sub(re.escape(module_policy_data.reg_pol_header.encode('utf-16-le')), - b'', - policy_file_data))).split(']['.encode('utf-16-le')) - log.trace('Searching %s policies...', len(policy_filedata_split)) + salt.utils.stringutils.to_bytes(r"\]{0}$".format(chr(0))), + b"", + re.sub( + salt.utils.stringutils.to_bytes(r"^\[{0}".format(chr(0))), + b"", + re.sub( + re.escape(module_policy_data.reg_pol_header.encode("utf-16-le")), + b"", + policy_file_data, + ), + ), + ).split("][".encode("utf-16-le")) + log.trace("Searching %s policies...", len(policy_filedata_split)) start_time = time.time() # Get the policy for each item defined in Registry.pol for policy_item in policy_filedata_split: - policy_item_key = policy_item.split('{0};'.format(chr(0)).encode('utf-16-le'))[0].decode('utf-16-le').lower() + policy_item_key = ( + policy_item.split("{0};".format(chr(0)).encode("utf-16-le"))[0] + .decode("utf-16-le") + .lower() + ) if policy_item_key: # Find the policy definitions with this key - admx_items = REGKEY_XPATH(admx_policy_definitions, - keyvalue=policy_item_key) - log.trace('Found %s policies for %s', len(admx_items), policy_item_key) + admx_items = REGKEY_XPATH( + admx_policy_definitions, keyvalue=policy_item_key + ) + log.trace("Found %s policies for %s", len(admx_items), policy_item_key) for admx_item in admx_items: # If this is a policy, append it to admx_policies - if etree.QName(admx_item).localname == 'policy': + if etree.QName(admx_item).localname == "policy": if admx_item not in admx_policies: admx_policies.append(admx_item) else: @@ -6278,145 +6298,232 @@ def _checkAllAdmxPolicies(policy_class, for policy_item in POLICY_ANCESTOR_XPATH(admx_item): if policy_item not in admx_policies: admx_policies.append(policy_item) - log.trace('Search complete: %s seconds', time.time() - start_time) + log.trace("Search complete: %s seconds", time.time() - start_time) if return_not_configured: - log.trace('Gathering non configured policies') + log.trace("Gathering non configured policies") start_time = time.time() - not_configured_policies = ALL_CLASS_POLICY_XPATH(admx_policy_definitions, registry_class=policy_class) + not_configured_policies = ALL_CLASS_POLICY_XPATH( + admx_policy_definitions, registry_class=policy_class + ) for policy_item in admx_policies: if policy_item in not_configured_policies: not_configured_policies.remove(policy_item) for not_configured_policy in not_configured_policies: - not_configured_policy_namespace = not_configured_policy.nsmap[not_configured_policy.prefix] + not_configured_policy_namespace = not_configured_policy.nsmap[ + not_configured_policy.prefix + ] if not_configured_policy_namespace not in policy_vals: policy_vals[not_configured_policy_namespace] = {} - policy_vals[not_configured_policy_namespace][not_configured_policy.attrib['name']] = 'Not Configured' + policy_vals[not_configured_policy_namespace][ + not_configured_policy.attrib["name"] + ] = "Not Configured" if return_full_policy_names: if not_configured_policy_namespace not in full_names: full_names[not_configured_policy_namespace] = {} - full_names[not_configured_policy_namespace][not_configured_policy.attrib['name']] = _getFullPolicyName( - policy_item=not_configured_policy, - policy_name=not_configured_policy.attrib['name'], - return_full_policy_names=return_full_policy_names, - adml_language=adml_language) - log.trace('building hierarchy for non-configured item %s', - not_configured_policy.attrib['name']) + full_names[not_configured_policy_namespace][ + not_configured_policy.attrib["name"] + ] = _getFullPolicyName( + policy_item=not_configured_policy, + policy_name=not_configured_policy.attrib["name"], + return_full_policy_names=return_full_policy_names, + adml_language=adml_language, + ) + log.trace( + "building hierarchy for non-configured item %s", + not_configured_policy.attrib["name"], + ) if not_configured_policy_namespace not in hierarchy: hierarchy[not_configured_policy_namespace] = {} - hierarchy[not_configured_policy_namespace][not_configured_policy.attrib['name']] = _build_parent_list( + hierarchy[not_configured_policy_namespace][ + not_configured_policy.attrib["name"] + ] = _build_parent_list( policy_definition=not_configured_policy, return_full_policy_names=return_full_policy_names, - adml_language=adml_language) - log.trace('Gathering complete: %s seconds', time.time() - start_time) + adml_language=adml_language, + ) + log.trace("Gathering complete: %s seconds", time.time() - start_time) - log.trace('Examining %s policies...', len(admx_policies)) + log.trace("Examining %s policies...", len(admx_policies)) start_time = time.time() for admx_policy in admx_policies: this_valuename = None - this_policy_setting = 'Not Configured' + this_policy_setting = "Not Configured" element_only_enabled_disabled = True explicit_enable_disable_value_setting = False - if 'key' in admx_policy.attrib: - this_key = admx_policy.attrib['key'] + if "key" in admx_policy.attrib: + this_key = admx_policy.attrib["key"] else: - log.error('policy item %s does not have the required "key" ' - 'attribute', admx_policy.attrib) + log.error( + 'policy item %s does not have the required "key" ' "attribute", + admx_policy.attrib, + ) break - if 'valueName' in admx_policy.attrib: - this_valuename = admx_policy.attrib['valueName'] - if 'name' in admx_policy.attrib: - this_policyname = admx_policy.attrib['name'] + if "valueName" in admx_policy.attrib: + this_valuename = admx_policy.attrib["valueName"] + if "name" in admx_policy.attrib: + this_policyname = admx_policy.attrib["name"] else: - log.error('policy item %s does not have the required "name" ' - 'attribute', admx_policy.attrib) + log.error( + 'policy item %s does not have the required "name" ' "attribute", + admx_policy.attrib, + ) break this_policynamespace = admx_policy.nsmap[admx_policy.prefix] - if ENABLED_VALUE_XPATH(admx_policy) and this_policy_setting == 'Not Configured': + if ( + ENABLED_VALUE_XPATH(admx_policy) + and this_policy_setting == "Not Configured" + ): # some policies have a disabled list but not an enabled list # added this to address those issues - if DISABLED_LIST_XPATH(admx_policy) or DISABLED_VALUE_XPATH(admx_policy): + if DISABLED_LIST_XPATH(admx_policy) or DISABLED_VALUE_XPATH( + admx_policy + ): element_only_enabled_disabled = False explicit_enable_disable_value_setting = True - if _checkValueItemParent(admx_policy, - this_policyname, - this_key, - this_valuename, - ENABLED_VALUE_XPATH, - policy_file_data): - this_policy_setting = 'Enabled' - log.trace('%s is enabled by detected ENABLED_VALUE_XPATH', this_policyname) + if _checkValueItemParent( + admx_policy, + this_policyname, + this_key, + this_valuename, + ENABLED_VALUE_XPATH, + policy_file_data, + ): + this_policy_setting = "Enabled" + log.trace( + "%s is enabled by detected ENABLED_VALUE_XPATH", this_policyname + ) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} - policy_vals[this_policynamespace][this_policyname] = this_policy_setting - if DISABLED_VALUE_XPATH(admx_policy) and this_policy_setting == 'Not Configured': + policy_vals[this_policynamespace][ + this_policyname + ] = this_policy_setting + if ( + DISABLED_VALUE_XPATH(admx_policy) + and this_policy_setting == "Not Configured" + ): # some policies have a disabled list but not an enabled list # added this to address those issues if ENABLED_LIST_XPATH(admx_policy) or ENABLED_VALUE_XPATH(admx_policy): element_only_enabled_disabled = False explicit_enable_disable_value_setting = True - if _checkValueItemParent(admx_policy, - this_policyname, - this_key, - this_valuename, - DISABLED_VALUE_XPATH, - policy_file_data): - this_policy_setting = 'Disabled' - log.trace('%s is disabled by detected DISABLED_VALUE_XPATH', this_policyname) + if _checkValueItemParent( + admx_policy, + this_policyname, + this_key, + this_valuename, + DISABLED_VALUE_XPATH, + policy_file_data, + ): + this_policy_setting = "Disabled" + log.trace( + "%s is disabled by detected DISABLED_VALUE_XPATH", + this_policyname, + ) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} - policy_vals[this_policynamespace][this_policyname] = this_policy_setting - if ENABLED_LIST_XPATH(admx_policy) and this_policy_setting == 'Not Configured': - if DISABLED_LIST_XPATH(admx_policy) or DISABLED_VALUE_XPATH(admx_policy): + policy_vals[this_policynamespace][ + this_policyname + ] = this_policy_setting + if ( + ENABLED_LIST_XPATH(admx_policy) + and this_policy_setting == "Not Configured" + ): + if DISABLED_LIST_XPATH(admx_policy) or DISABLED_VALUE_XPATH( + admx_policy + ): element_only_enabled_disabled = False explicit_enable_disable_value_setting = True - if _checkListItem(admx_policy, this_policyname, this_key, ENABLED_LIST_XPATH, policy_file_data): - this_policy_setting = 'Enabled' - log.trace('%s is enabled by detected ENABLED_LIST_XPATH', this_policyname) + if _checkListItem( + admx_policy, + this_policyname, + this_key, + ENABLED_LIST_XPATH, + policy_file_data, + ): + this_policy_setting = "Enabled" + log.trace( + "%s is enabled by detected ENABLED_LIST_XPATH", this_policyname + ) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} - policy_vals[this_policynamespace][this_policyname] = this_policy_setting - if DISABLED_LIST_XPATH(admx_policy) and this_policy_setting == 'Not Configured': + policy_vals[this_policynamespace][ + this_policyname + ] = this_policy_setting + if ( + DISABLED_LIST_XPATH(admx_policy) + and this_policy_setting == "Not Configured" + ): if ENABLED_LIST_XPATH(admx_policy) or ENABLED_VALUE_XPATH(admx_policy): element_only_enabled_disabled = False explicit_enable_disable_value_setting = True - if _checkListItem(admx_policy, this_policyname, this_key, DISABLED_LIST_XPATH, policy_file_data): - this_policy_setting = 'Disabled' - log.trace('%s is disabled by detected DISABLED_LIST_XPATH', this_policyname) + if _checkListItem( + admx_policy, + this_policyname, + this_key, + DISABLED_LIST_XPATH, + policy_file_data, + ): + this_policy_setting = "Disabled" + log.trace( + "%s is disabled by detected DISABLED_LIST_XPATH", + this_policyname, + ) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} - policy_vals[this_policynamespace][this_policyname] = this_policy_setting + policy_vals[this_policynamespace][ + this_policyname + ] = this_policy_setting if not explicit_enable_disable_value_setting and this_valuename: # the policy has a key/valuename but no explicit enabled/Disabled # Value or List # these seem to default to a REG_DWORD 1 = "Enabled" **del. = "Disabled" - if _regexSearchRegPolData(re.escape(_buildKnownDataSearchString(this_key, - this_valuename, - 'REG_DWORD', - '1')), - policy_file_data): - this_policy_setting = 'Enabled' - log.trace('%s is enabled by no explicit enable/disable list or value', this_policyname) + if _regexSearchRegPolData( + re.escape( + _buildKnownDataSearchString( + this_key, this_valuename, "REG_DWORD", "1" + ) + ), + policy_file_data, + ): + this_policy_setting = "Enabled" + log.trace( + "%s is enabled by no explicit enable/disable list or value", + this_policyname, + ) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} - policy_vals[this_policynamespace][this_policyname] = this_policy_setting - elif _regexSearchRegPolData(re.escape(_buildKnownDataSearchString(this_key, - this_valuename, - 'REG_DWORD', - None, - check_deleted=True)), - policy_file_data): - this_policy_setting = 'Disabled' - log.trace('%s is disabled by no explicit enable/disable list or value', this_policyname) + policy_vals[this_policynamespace][ + this_policyname + ] = this_policy_setting + elif _regexSearchRegPolData( + re.escape( + _buildKnownDataSearchString( + this_key, + this_valuename, + "REG_DWORD", + None, + check_deleted=True, + ) + ), + policy_file_data, + ): + this_policy_setting = "Disabled" + log.trace( + "%s is disabled by no explicit enable/disable list or value", + this_policyname, + ) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} - policy_vals[this_policynamespace][this_policyname] = this_policy_setting + policy_vals[this_policynamespace][ + this_policyname + ] = this_policy_setting if ELEMENTS_XPATH(admx_policy): - if element_only_enabled_disabled or this_policy_setting == 'Enabled': + if element_only_enabled_disabled or this_policy_setting == "Enabled": # TODO does this need to be modified based on the 'required' attribute? required_elements = {} configured_elements = {} @@ -6425,268 +6532,456 @@ def _checkAllAdmxPolicies(policy_class, for child_item in elements_item.getchildren(): this_element_name = _getFullPolicyName( policy_item=child_item, - policy_name=child_item.attrib['id'], + policy_name=child_item.attrib["id"], return_full_policy_names=return_full_policy_names, - adml_language=adml_language) + adml_language=adml_language, + ) required_elements[this_element_name] = None - child_key = child_item.attrib.get('key', this_key) + child_key = child_item.attrib.get("key", this_key) child_valuename = child_item.attrib.get( - 'valueName', this_valuename) + "valueName", this_valuename + ) - if etree.QName(child_item).localname == 'boolean': + if etree.QName(child_item).localname == "boolean": # https://msdn.microsoft.com/en-us/library/dn605978(v=vs.85).aspx if child_item.getchildren(): - if TRUE_VALUE_XPATH(child_item) and this_element_name not in configured_elements: - if _checkValueItemParent(child_item, - this_policyname, - child_key, - child_valuename, - TRUE_VALUE_XPATH, - policy_file_data): - configured_elements[this_element_name] = True - log.trace('element %s is configured true', - child_item.attrib['id']) - if FALSE_VALUE_XPATH(child_item) and this_element_name not in configured_elements: - if _checkValueItemParent(child_item, - this_policyname, - child_key, - child_valuename, - FALSE_VALUE_XPATH, - policy_file_data): - configured_elements[this_element_name] = False - policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element %s is configured false', - child_item.attrib['id']) + if ( + TRUE_VALUE_XPATH(child_item) + and this_element_name not in configured_elements + ): + if _checkValueItemParent( + child_item, + this_policyname, + child_key, + child_valuename, + TRUE_VALUE_XPATH, + policy_file_data, + ): + configured_elements[ + this_element_name + ] = True + log.trace( + "element %s is configured true", + child_item.attrib["id"], + ) + if ( + FALSE_VALUE_XPATH(child_item) + and this_element_name not in configured_elements + ): + if _checkValueItemParent( + child_item, + this_policyname, + child_key, + child_valuename, + FALSE_VALUE_XPATH, + policy_file_data, + ): + configured_elements[ + this_element_name + ] = False + policy_disabled_elements = ( + policy_disabled_elements + 1 + ) + log.trace( + "element %s is configured false", + child_item.attrib["id"], + ) # WARNING - no standard ADMX files use true/falseList # so this hasn't actually been tested - if TRUE_LIST_XPATH(child_item) and this_element_name not in configured_elements: - log.trace('checking trueList') - if _checkListItem(child_item, - this_policyname, - this_key, - TRUE_LIST_XPATH, - policy_file_data): - configured_elements[this_element_name] = True - log.trace('element %s is configured true', - child_item.attrib['id']) - if FALSE_LIST_XPATH(child_item) and this_element_name not in configured_elements: - log.trace('checking falseList') - if _checkListItem(child_item, - this_policyname, - this_key, - FALSE_LIST_XPATH, - policy_file_data): - configured_elements[this_element_name] = False - policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element %s is configured false', - child_item.attrib['id']) + if ( + TRUE_LIST_XPATH(child_item) + and this_element_name not in configured_elements + ): + log.trace("checking trueList") + if _checkListItem( + child_item, + this_policyname, + this_key, + TRUE_LIST_XPATH, + policy_file_data, + ): + configured_elements[ + this_element_name + ] = True + log.trace( + "element %s is configured true", + child_item.attrib["id"], + ) + if ( + FALSE_LIST_XPATH(child_item) + and this_element_name not in configured_elements + ): + log.trace("checking falseList") + if _checkListItem( + child_item, + this_policyname, + this_key, + FALSE_LIST_XPATH, + policy_file_data, + ): + configured_elements[ + this_element_name + ] = False + policy_disabled_elements = ( + policy_disabled_elements + 1 + ) + log.trace( + "element %s is configured false", + child_item.attrib["id"], + ) else: - if _regexSearchRegPolData(re.escape(_processValueItem(child_item, - child_key, - child_valuename, - admx_policy, - elements_item, - check_deleted=True)), - policy_file_data): + if _regexSearchRegPolData( + re.escape( + _processValueItem( + child_item, + child_key, + child_valuename, + admx_policy, + elements_item, + check_deleted=True, + ) + ), + policy_file_data, + ): configured_elements[this_element_name] = False - policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element %s is configured false', child_item.attrib['id']) - elif _regexSearchRegPolData(re.escape(_processValueItem(child_item, - child_key, - child_valuename, - admx_policy, - elements_item, - check_deleted=False)), - policy_file_data): + policy_disabled_elements = ( + policy_disabled_elements + 1 + ) + log.trace( + "element %s is configured false", + child_item.attrib["id"], + ) + elif _regexSearchRegPolData( + re.escape( + _processValueItem( + child_item, + child_key, + child_valuename, + admx_policy, + elements_item, + check_deleted=False, + ) + ), + policy_file_data, + ): configured_elements[this_element_name] = True - log.trace('element %s is configured true', - child_item.attrib['id']) - elif etree.QName(child_item).localname == 'decimal' \ - or etree.QName(child_item).localname == 'text' \ - or etree.QName(child_item).localname == 'longDecimal' \ - or etree.QName(child_item).localname == 'multiText': + log.trace( + "element %s is configured true", + child_item.attrib["id"], + ) + elif ( + etree.QName(child_item).localname == "decimal" + or etree.QName(child_item).localname == "text" + or etree.QName(child_item).localname == "longDecimal" + or etree.QName(child_item).localname == "multiText" + ): # https://msdn.microsoft.com/en-us/library/dn605987(v=vs.85).aspx - if _regexSearchRegPolData(re.escape(_processValueItem(child_item, - child_key, - child_valuename, - admx_policy, - elements_item, - check_deleted=True)), - policy_file_data): - configured_elements[this_element_name] = 'Disabled' - policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element %s is disabled', - child_item.attrib['id']) - elif _regexSearchRegPolData(re.escape(_processValueItem(child_item, - child_key, - child_valuename, - admx_policy, - elements_item, - check_deleted=False)), - policy_file_data): - configured_value = _getDataFromRegPolData(_processValueItem(child_item, - child_key, - child_valuename, - admx_policy, - elements_item, - check_deleted=False), - policy_file_data) - configured_elements[this_element_name] = configured_value - log.trace('element %s is enabled, value == %s', - child_item.attrib['id'], - configured_value) - elif etree.QName(child_item).localname == 'enum': - if _regexSearchRegPolData(re.escape(_processValueItem(child_item, - child_key, - child_valuename, - admx_policy, - elements_item, - check_deleted=True)), - policy_file_data): - log.trace('enum element %s is disabled', - child_item.attrib['id']) - configured_elements[this_element_name] = 'Disabled' - policy_disabled_elements = policy_disabled_elements + 1 + if _regexSearchRegPolData( + re.escape( + _processValueItem( + child_item, + child_key, + child_valuename, + admx_policy, + elements_item, + check_deleted=True, + ) + ), + policy_file_data, + ): + configured_elements[this_element_name] = "Disabled" + policy_disabled_elements = ( + policy_disabled_elements + 1 + ) + log.trace( + "element %s is disabled", + child_item.attrib["id"], + ) + elif _regexSearchRegPolData( + re.escape( + _processValueItem( + child_item, + child_key, + child_valuename, + admx_policy, + elements_item, + check_deleted=False, + ) + ), + policy_file_data, + ): + configured_value = _getDataFromRegPolData( + _processValueItem( + child_item, + child_key, + child_valuename, + admx_policy, + elements_item, + check_deleted=False, + ), + policy_file_data, + ) + configured_elements[ + this_element_name + ] = configured_value + log.trace( + "element %s is enabled, value == %s", + child_item.attrib["id"], + configured_value, + ) + elif etree.QName(child_item).localname == "enum": + if _regexSearchRegPolData( + re.escape( + _processValueItem( + child_item, + child_key, + child_valuename, + admx_policy, + elements_item, + check_deleted=True, + ) + ), + policy_file_data, + ): + log.trace( + "enum element %s is disabled", + child_item.attrib["id"], + ) + configured_elements[this_element_name] = "Disabled" + policy_disabled_elements = ( + policy_disabled_elements + 1 + ) else: for enum_item in child_item.getchildren(): - if _checkValueItemParent(enum_item, - child_item.attrib['id'], - child_key, - child_valuename, - VALUE_XPATH, - policy_file_data): + if _checkValueItemParent( + enum_item, + child_item.attrib["id"], + child_key, + child_valuename, + VALUE_XPATH, + policy_file_data, + ): if VALUE_LIST_XPATH(enum_item): - log.trace('enum item has a valueList') - if _checkListItem(enum_item, - this_policyname, - child_key, - VALUE_LIST_XPATH, - policy_file_data): - log.trace('all valueList items exist in file') - configured_elements[this_element_name] = _getAdmlDisplayName( - adml_policy_resources, - enum_item.attrib['displayName']) + log.trace("enum item has a valueList") + if _checkListItem( + enum_item, + this_policyname, + child_key, + VALUE_LIST_XPATH, + policy_file_data, + ): + log.trace( + "all valueList items exist in file" + ) + configured_elements[ + this_element_name + ] = _getAdmlDisplayName( + adml_policy_resources, + enum_item.attrib["displayName"], + ) break else: - configured_elements[this_element_name] = _getAdmlDisplayName( - adml_policy_resources, - enum_item.attrib['displayName']) + configured_elements[ + this_element_name + ] = _getAdmlDisplayName( + adml_policy_resources, + enum_item.attrib["displayName"], + ) break - elif etree.QName(child_item).localname == 'list': + elif etree.QName(child_item).localname == "list": return_value_name = False - if 'explicitValue' in child_item.attrib \ - and child_item.attrib['explicitValue'].lower() == 'true': - log.trace('explicitValue list, we will return value names') + if ( + "explicitValue" in child_item.attrib + and child_item.attrib["explicitValue"].lower() + == "true" + ): + log.trace( + "explicitValue list, we will return value names" + ) return_value_name = True - if _regexSearchRegPolData(re.escape(_processValueItem(child_item, - child_key, - child_valuename, - admx_policy, - elements_item, - check_deleted=False) - ) + salt.utils.stringutils.to_bytes(r'(?!\*\*delvals\.)'), - policy_file_data): - configured_value = _getDataFromRegPolData(_processValueItem(child_item, - child_key, - child_valuename, - admx_policy, - elements_item, - check_deleted=False), - policy_file_data, - return_value_name=return_value_name) - configured_elements[this_element_name] = configured_value - log.trace('element %s is enabled values: %s', - child_item.attrib['id'], - configured_value) - elif _regexSearchRegPolData(re.escape(_processValueItem(child_item, - child_key, - child_valuename, - admx_policy, - elements_item, - check_deleted=True)), - policy_file_data): + if _regexSearchRegPolData( + re.escape( + _processValueItem( + child_item, + child_key, + child_valuename, + admx_policy, + elements_item, + check_deleted=False, + ) + ) + + salt.utils.stringutils.to_bytes( + r"(?!\*\*delvals\.)" + ), + policy_file_data, + ): + configured_value = _getDataFromRegPolData( + _processValueItem( + child_item, + child_key, + child_valuename, + admx_policy, + elements_item, + check_deleted=False, + ), + policy_file_data, + return_value_name=return_value_name, + ) + configured_elements[ + this_element_name + ] = configured_value + log.trace( + "element %s is enabled values: %s", + child_item.attrib["id"], + configured_value, + ) + elif _regexSearchRegPolData( + re.escape( + _processValueItem( + child_item, + child_key, + child_valuename, + admx_policy, + elements_item, + check_deleted=True, + ) + ), + policy_file_data, + ): configured_elements[this_element_name] = "Disabled" - policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element {0} is disabled'.format(child_item.attrib['id'])) + policy_disabled_elements = ( + policy_disabled_elements + 1 + ) + log.trace( + "element {0} is disabled".format( + child_item.attrib["id"] + ) + ) if element_only_enabled_disabled: - if len(required_elements.keys()) > 0 \ - and len(configured_elements.keys()) == len(required_elements.keys()): - if policy_disabled_elements == len(required_elements.keys()): - log.trace('{0} is disabled by all enum elements'.format(this_policyname)) + if len(required_elements.keys()) > 0 and len( + configured_elements.keys() + ) == len(required_elements.keys()): + if policy_disabled_elements == len( + required_elements.keys() + ): + log.trace( + "{0} is disabled by all enum elements".format( + this_policyname + ) + ) if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} - policy_vals[this_policynamespace][this_policyname] = 'Disabled' + policy_vals[this_policynamespace][ + this_policyname + ] = "Disabled" else: if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} - policy_vals[this_policynamespace][this_policyname] = configured_elements - log.trace('{0} is enabled by enum elements'.format(this_policyname)) + policy_vals[this_policynamespace][ + this_policyname + ] = configured_elements + log.trace( + "{0} is enabled by enum elements".format( + this_policyname + ) + ) else: - if this_policy_setting == 'Enabled': + if this_policy_setting == "Enabled": if this_policynamespace not in policy_vals: policy_vals[this_policynamespace] = {} - policy_vals[this_policynamespace][this_policyname] = configured_elements - if return_full_policy_names and this_policynamespace in policy_vals and this_policyname in policy_vals[this_policynamespace]: + policy_vals[this_policynamespace][ + this_policyname + ] = configured_elements + if ( + return_full_policy_names + and this_policynamespace in policy_vals + and this_policyname in policy_vals[this_policynamespace] + ): if this_policynamespace not in full_names: full_names[this_policynamespace] = {} full_names[this_policynamespace][this_policyname] = _getFullPolicyName( - policy_item=admx_policy, - policy_name=admx_policy.attrib['name'], - return_full_policy_names=return_full_policy_names, - adml_language=adml_language) + policy_item=admx_policy, + policy_name=admx_policy.attrib["name"], + return_full_policy_names=return_full_policy_names, + adml_language=adml_language, + ) # Make sure the we're passing the full policy name # This issue was found when setting the `Allow Telemetry` setting # All following states would show a change in this setting # When the state does its first `lgpo.get` it would return `AllowTelemetry` # On the second run, it would return `Allow Telemetry` # This makes sure we're always returning the full_name when required - if this_policyname in policy_vals[this_policynamespace][this_policyname]: + if ( + this_policyname + in policy_vals[this_policynamespace][this_policyname] + ): full_name = full_names[this_policynamespace][this_policyname] - setting = policy_vals[this_policynamespace][this_policyname].pop(this_policyname) - policy_vals[this_policynamespace][this_policyname][full_name] = setting - if this_policynamespace in policy_vals and this_policyname in policy_vals[this_policynamespace]: + setting = policy_vals[this_policynamespace][this_policyname].pop( + this_policyname + ) + policy_vals[this_policynamespace][this_policyname][ + full_name + ] = setting + if ( + this_policynamespace in policy_vals + and this_policyname in policy_vals[this_policynamespace] + ): if this_policynamespace not in hierarchy: hierarchy[this_policynamespace] = {} hierarchy[this_policynamespace][this_policyname] = _build_parent_list( policy_definition=admx_policy, return_full_policy_names=return_full_policy_names, - adml_language=adml_language) - log.trace('Examination complete: %s seconds', time.time() - start_time) + adml_language=adml_language, + ) + log.trace("Examination complete: %s seconds", time.time() - start_time) if policy_vals and return_full_policy_names and not hierarchical_return: - log.debug('Compiling non hierarchical return...') + log.debug("Compiling non hierarchical return...") start_time = time.time() unpathed_dict = {} pathed_dict = {} for policy_namespace in list(policy_vals): for policy_item in list(policy_vals[policy_namespace]): - if full_names[policy_namespace][policy_item] in policy_vals[policy_namespace]: + if ( + full_names[policy_namespace][policy_item] + in policy_vals[policy_namespace] + ): # add this item with the path'd full name full_path_list = hierarchy[policy_namespace][policy_item] full_path_list.reverse() full_path_list.append(full_names[policy_namespace][policy_item]) - policy_vals['\\'.join(full_path_list)] = policy_vals[policy_namespace].pop(policy_item) + policy_vals["\\".join(full_path_list)] = policy_vals[ + policy_namespace + ].pop(policy_item) pathed_dict[full_names[policy_namespace][policy_item]] = True else: - policy_vals[policy_namespace][full_names[policy_namespace][policy_item]] = policy_vals[policy_namespace].pop(policy_item) + policy_vals[policy_namespace][ + full_names[policy_namespace][policy_item] + ] = policy_vals[policy_namespace].pop(policy_item) if policy_namespace not in unpathed_dict: unpathed_dict[policy_namespace] = {} - unpathed_dict[policy_namespace][full_names[policy_namespace][policy_item]] = policy_item + unpathed_dict[policy_namespace][ + full_names[policy_namespace][policy_item] + ] = policy_item # go back and remove any "unpathed" policies that need a full path if policy_namespace in unpathed_dict: for path_needed in unpathed_dict[policy_namespace]: # remove the item with the same full name and re-add it w/a path'd version - full_path_list = hierarchy[policy_namespace][unpathed_dict[policy_namespace][path_needed]] + full_path_list = hierarchy[policy_namespace][ + unpathed_dict[policy_namespace][path_needed] + ] full_path_list.reverse() full_path_list.append(path_needed) - log.trace('full_path_list == %s', full_path_list) - policy_vals['\\'.join(full_path_list)] = policy_vals[policy_namespace].pop(path_needed) - log.trace('Compilation complete: %s seconds', time.time() - start_time) + log.trace("full_path_list == %s", full_path_list) + policy_vals["\\".join(full_path_list)] = policy_vals[ + policy_namespace + ].pop(path_needed) + log.trace("Compilation complete: %s seconds", time.time() - start_time) for policy_namespace in list(policy_vals): if policy_vals[policy_namespace] == {}: policy_vals.pop(policy_namespace) if policy_vals and hierarchical_return: if hierarchy: - log.debug('Compiling hierarchical return...') + log.debug("Compiling hierarchical return...") start_time = time.time() for policy_namespace in hierarchy: for hierarchy_item in hierarchy[policy_namespace]: @@ -6698,140 +6993,193 @@ def _checkAllAdmxPolicies(policy_class, if first_item: h_policy_name = hierarchy_item if return_full_policy_names: - h_policy_name = full_names[policy_namespace][hierarchy_item] - newdict[item] = {h_policy_name: policy_vals[policy_namespace].pop(hierarchy_item)} + h_policy_name = full_names[policy_namespace][ + hierarchy_item + ] + newdict[item] = { + h_policy_name: policy_vals[policy_namespace].pop( + hierarchy_item + ) + } first_item = False else: newdict[item] = tdict tdict = newdict if tdict: policy_vals = dictupdate.update(policy_vals, tdict) - if policy_namespace in policy_vals and policy_vals[policy_namespace] == {}: + if ( + policy_namespace in policy_vals + and policy_vals[policy_namespace] == {} + ): policy_vals.pop(policy_namespace) - log.trace('Compilation complete: %s seconds', time.time() - start_time) + log.trace("Compilation complete: %s seconds", time.time() - start_time) policy_vals = { - module_policy_data.admx_registry_classes[policy_class]['lgpo_section']: { - 'Administrative Templates': policy_vals}} + module_policy_data.admx_registry_classes[policy_class]["lgpo_section"]: { + "Administrative Templates": policy_vals + } + } return policy_vals -def _build_parent_list(policy_definition, - return_full_policy_names, - adml_language): - ''' +def _build_parent_list(policy_definition, return_full_policy_names, adml_language): + """ helper function to build a list containing parent elements of the ADMX policy - ''' + """ parent_list = [] policy_namespace = list(policy_definition.nsmap.keys())[0] parent_category = policy_definition.xpath( - '{0}:parentCategory/@ref'.format(policy_namespace), - namespaces=policy_definition.nsmap) + "{0}:parentCategory/@ref".format(policy_namespace), + namespaces=policy_definition.nsmap, + ) admx_policy_definitions = _get_policy_definitions(language=adml_language) if parent_category: parent_category = parent_category[0] - nsmap_xpath = '/policyDefinitions/policyNamespaces/{0}:*' \ - ''.format(policy_namespace) + nsmap_xpath = "/policyDefinitions/policyNamespaces/{0}:*" "".format( + policy_namespace + ) this_namespace_map = _buildElementNsmap( admx_policy_definitions.xpath( - nsmap_xpath, namespaces=policy_definition.nsmap)) - this_namespace_map = dictupdate.update(this_namespace_map, - policy_definition.nsmap) + nsmap_xpath, namespaces=policy_definition.nsmap + ) + ) + this_namespace_map = dictupdate.update( + this_namespace_map, policy_definition.nsmap + ) parent_list = _admx_policy_parent_walk( path=parent_list, policy_namespace=policy_namespace, parent_category=parent_category, policy_nsmap=this_namespace_map, return_full_policy_names=return_full_policy_names, - adml_language=adml_language) + adml_language=adml_language, + ) return parent_list -def _admx_policy_parent_walk(path, - policy_namespace, - parent_category, - policy_nsmap, - return_full_policy_names, - adml_language): - ''' +def _admx_policy_parent_walk( + path, + policy_namespace, + parent_category, + policy_nsmap, + return_full_policy_names, + adml_language, +): + """ helper function to recursively walk up the ADMX namespaces and build the hierarchy for the policy - ''' + """ admx_policy_definitions = _get_policy_definitions(language=adml_language) category_xpath_string = '/policyDefinitions/categories/{0}:category[@name="{1}"]' - using_xpath_string = '/policyDefinitions/policyNamespaces/{0}:using' - if parent_category.find(':') >= 0: + using_xpath_string = "/policyDefinitions/policyNamespaces/{0}:using" + if parent_category.find(":") >= 0: # the parent is in another namespace - policy_namespace = parent_category.split(':')[0] - parent_category = parent_category.split(':')[1] + policy_namespace = parent_category.split(":")[0] + parent_category = parent_category.split(":")[1] using_xpath_string = using_xpath_string.format(policy_namespace) - policy_nsmap = dictupdate.update(policy_nsmap, - _buildElementNsmap(admx_policy_definitions.xpath(using_xpath_string, - namespaces=policy_nsmap))) - category_xpath_string = category_xpath_string.format(policy_namespace, parent_category) + policy_nsmap = dictupdate.update( + policy_nsmap, + _buildElementNsmap( + admx_policy_definitions.xpath( + using_xpath_string, namespaces=policy_nsmap + ) + ), + ) + category_xpath_string = category_xpath_string.format( + policy_namespace, parent_category + ) if admx_policy_definitions.xpath(category_xpath_string, namespaces=policy_nsmap): - tparent_category = admx_policy_definitions.xpath(category_xpath_string, - namespaces=policy_nsmap)[0] + tparent_category = admx_policy_definitions.xpath( + category_xpath_string, namespaces=policy_nsmap + )[0] this_parent_name = _getFullPolicyName( policy_item=tparent_category, - policy_name=tparent_category.attrib['name'], + policy_name=tparent_category.attrib["name"], return_full_policy_names=return_full_policy_names, - adml_language=adml_language) + adml_language=adml_language, + ) path.append(this_parent_name) - if tparent_category.xpath('{0}:parentCategory/@ref'.format(policy_namespace), namespaces=policy_nsmap): + if tparent_category.xpath( + "{0}:parentCategory/@ref".format(policy_namespace), namespaces=policy_nsmap + ): # parent has a parent path = _admx_policy_parent_walk( path=path, policy_namespace=policy_namespace, - parent_category=tparent_category.xpath('{0}:parentCategory/@ref'.format(policy_namespace), namespaces=policy_nsmap)[0], + parent_category=tparent_category.xpath( + "{0}:parentCategory/@ref".format(policy_namespace), + namespaces=policy_nsmap, + )[0], policy_nsmap=policy_nsmap, return_full_policy_names=return_full_policy_names, - adml_language=adml_language) + adml_language=adml_language, + ) return path def _read_regpol_file(reg_pol_path): - ''' + """ helper function to read a reg policy file and return decoded data - ''' + """ returndata = None if os.path.exists(reg_pol_path): - with salt.utils.files.fopen(reg_pol_path, 'rb') as pol_file: + with salt.utils.files.fopen(reg_pol_path, "rb") as pol_file: returndata = pol_file.read() return returndata def _regexSearchKeyValueCombo(policy_data, policy_regpath, policy_regkey): - ''' + """ helper function to do a search of Policy data from a registry.pol file for a policy_regpath and policy_regkey combo - ''' + """ if policy_data: - regex_str = [r'(\*', r'\*', 'D', 'e', 'l', r'\.', r'|\*', r'\*', 'D', - 'e', 'l', 'V', 'a', 'l', 's', r'\.', '){0,1}'] - specialValueRegex = '\x00'.join(regex_str) + regex_str = [ + r"(\*", + r"\*", + "D", + "e", + "l", + r"\.", + r"|\*", + r"\*", + "D", + "e", + "l", + "V", + "a", + "l", + "s", + r"\.", + "){0,1}", + ] + specialValueRegex = "\x00".join(regex_str) specialValueRegex = salt.utils.stringutils.to_bytes(specialValueRegex) - _thisSearch = b''.join([salt.utils.stringutils.to_bytes(r'\['), - re.escape(policy_regpath), - b'\x00;\x00', - specialValueRegex, - re.escape(policy_regkey.lstrip(b'\x00')), - b'\x00;']) + _thisSearch = b"".join( + [ + salt.utils.stringutils.to_bytes(r"\["), + re.escape(policy_regpath), + b"\x00;\x00", + specialValueRegex, + re.escape(policy_regkey.lstrip(b"\x00")), + b"\x00;", + ] + ) match = re.search(_thisSearch, policy_data, re.IGNORECASE) if match: # add 2 so we get the ']' and the \00 # to return the full policy entry - return policy_data[match.start():(policy_data.index(b']', match.end())) + 2] + return policy_data[ + match.start() : (policy_data.index(b"]", match.end())) + 2 + ] return None -def _write_regpol_data(data_to_write, - policy_file_path, - gpt_ini_path, - gpt_extension, - gpt_extension_guid): - ''' +def _write_regpol_data( + data_to_write, policy_file_path, gpt_ini_path, gpt_extension, gpt_extension_guid +): + """ helper function to actually write the data to a Registry.pol file also updates/edits the gpt.ini file to include the ADM policy extensions @@ -6843,161 +7191,181 @@ def _write_regpol_data(data_to_write, gpt_ini_path: path to gpt.ini file gpt_extension: gpt extension list name from _policy_info class for this registry class gpt_extension_location gpt_extension_guid: admx registry extension guid for the class - ''' + """ # Write Registry.pol file if not os.path.exists(policy_file_path): - __salt__['file.makedirs'](policy_file_path) + __salt__["file.makedirs"](policy_file_path) try: - with salt.utils.files.fopen(policy_file_path, 'wb') as pol_file: - reg_pol_header = '\u5250\u6765\x01\x00'.encode('utf-16-le') + with salt.utils.files.fopen(policy_file_path, "wb") as pol_file: + reg_pol_header = "\u5250\u6765\x01\x00".encode("utf-16-le") if not data_to_write.startswith(reg_pol_header): pol_file.write(reg_pol_header) pol_file.write(data_to_write) # TODO: This needs to be more specific except Exception as e: # pylint: disable=broad-except - msg = 'An error occurred attempting to write to {0}, the exception ' \ - 'was: {1}'.format(policy_file_path, e) + msg = ( + "An error occurred attempting to write to {0}, the exception " + "was: {1}".format(policy_file_path, e) + ) log.exception(msg) raise CommandExecutionError(msg) # Write the gpt.ini file - gpt_ini_data = '' + gpt_ini_data = "" if os.path.exists(gpt_ini_path): - with salt.utils.files.fopen(gpt_ini_path, 'r') as gpt_file: + with salt.utils.files.fopen(gpt_ini_path, "r") as gpt_file: gpt_ini_data = gpt_file.read() - if not _regexSearchRegPolData(r'\[General\]\r\n', gpt_ini_data): - gpt_ini_data = '[General]\r\n' + gpt_ini_data - if _regexSearchRegPolData( - r'{0}='.format(re.escape(gpt_extension)), gpt_ini_data): + if not _regexSearchRegPolData(r"\[General\]\r\n", gpt_ini_data): + gpt_ini_data = "[General]\r\n" + gpt_ini_data + if _regexSearchRegPolData(r"{0}=".format(re.escape(gpt_extension)), gpt_ini_data): # ensure the line contains the ADM guid gpt_ext_loc = re.search( - r'^{0}=.*\r\n'.format(re.escape(gpt_extension)), + r"^{0}=.*\r\n".format(re.escape(gpt_extension)), gpt_ini_data, - re.IGNORECASE | re.MULTILINE) - gpt_ext_str = gpt_ini_data[gpt_ext_loc.start():gpt_ext_loc.end()] + re.IGNORECASE | re.MULTILINE, + ) + gpt_ext_str = gpt_ini_data[gpt_ext_loc.start() : gpt_ext_loc.end()] if not _regexSearchRegPolData( - r'{0}'.format(re.escape(gpt_extension_guid)), gpt_ext_str): - gpt_ext_str = gpt_ext_str.split('=') + r"{0}".format(re.escape(gpt_extension_guid)), gpt_ext_str + ): + gpt_ext_str = gpt_ext_str.split("=") gpt_ext_str[1] = gpt_extension_guid + gpt_ext_str[1] - gpt_ext_str = '='.join(gpt_ext_str) - gpt_ini_data = gpt_ini_data[0:gpt_ext_loc.start()] + \ - gpt_ext_str + gpt_ini_data[gpt_ext_loc.end():] + gpt_ext_str = "=".join(gpt_ext_str) + gpt_ini_data = ( + gpt_ini_data[0 : gpt_ext_loc.start()] + + gpt_ext_str + + gpt_ini_data[gpt_ext_loc.end() :] + ) else: general_location = re.search( - r'^\[General\]\r\n', gpt_ini_data, re.IGNORECASE | re.MULTILINE) - gpt_ini_data = '{0}{1}={2}\r\n{3}'.format( - gpt_ini_data[general_location.start():general_location.end()], + r"^\[General\]\r\n", gpt_ini_data, re.IGNORECASE | re.MULTILINE + ) + gpt_ini_data = "{0}{1}={2}\r\n{3}".format( + gpt_ini_data[general_location.start() : general_location.end()], gpt_extension, gpt_extension_guid, - gpt_ini_data[general_location.end():]) + gpt_ini_data[general_location.end() :], + ) # https://technet.microsoft.com/en-us/library/cc978247.aspx - if _regexSearchRegPolData(r'Version=', gpt_ini_data): + if _regexSearchRegPolData(r"Version=", gpt_ini_data): version_loc = re.search( - r'^Version=.*\r\n', gpt_ini_data, re.IGNORECASE | re.MULTILINE) - version_str = gpt_ini_data[version_loc.start():version_loc.end()] - version_str = version_str.split('=') - version_nums = struct.unpack( - b'>2H', struct.pack(b'>I', int(version_str[1]))) - if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower(): + r"^Version=.*\r\n", gpt_ini_data, re.IGNORECASE | re.MULTILINE + ) + version_str = gpt_ini_data[version_loc.start() : version_loc.end()] + version_str = version_str.split("=") + version_nums = struct.unpack(b">2H", struct.pack(b">I", int(version_str[1]))) + if gpt_extension.lower() == "gPCMachineExtensionNames".lower(): version_nums = (version_nums[0], version_nums[1] + 1) - elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower(): + elif gpt_extension.lower() == "gPCUserExtensionNames".lower(): version_nums = (version_nums[0] + 1, version_nums[1]) - version_num = struct.unpack( - b'>I', struct.pack(b'>2H', *version_nums))[0] - gpt_ini_data = '{0}{1}={2}\r\n{3}'.format( - gpt_ini_data[0:version_loc.start()], - 'Version', + version_num = struct.unpack(b">I", struct.pack(b">2H", *version_nums))[0] + gpt_ini_data = "{0}{1}={2}\r\n{3}".format( + gpt_ini_data[0 : version_loc.start()], + "Version", version_num, - gpt_ini_data[version_loc.end():]) + gpt_ini_data[version_loc.end() :], + ) else: general_location = re.search( - r'^\[General\]\r\n', gpt_ini_data, re.IGNORECASE | re.MULTILINE) - if gpt_extension.lower() == 'gPCMachineExtensionNames'.lower(): + r"^\[General\]\r\n", gpt_ini_data, re.IGNORECASE | re.MULTILINE + ) + if gpt_extension.lower() == "gPCMachineExtensionNames".lower(): version_nums = (0, 1) - elif gpt_extension.lower() == 'gPCUserExtensionNames'.lower(): + elif gpt_extension.lower() == "gPCUserExtensionNames".lower(): version_nums = (1, 0) - gpt_ini_data = '{0}{1}={2}\r\n{3}'.format( - gpt_ini_data[general_location.start():general_location.end()], - 'Version', - int("{0}{1}".format(six.text_type(version_nums[0]).zfill(4), - six.text_type(version_nums[1]).zfill(4)), - 16), - gpt_ini_data[general_location.end():]) + gpt_ini_data = "{0}{1}={2}\r\n{3}".format( + gpt_ini_data[general_location.start() : general_location.end()], + "Version", + int( + "{0}{1}".format( + six.text_type(version_nums[0]).zfill(4), + six.text_type(version_nums[1]).zfill(4), + ), + 16, + ), + gpt_ini_data[general_location.end() :], + ) if gpt_ini_data: try: - with salt.utils.files.fopen(gpt_ini_path, 'w') as gpt_file: + with salt.utils.files.fopen(gpt_ini_path, "w") as gpt_file: gpt_file.write(gpt_ini_data) # TODO: This needs to be more specific except Exception as e: # pylint: disable=broad-except - msg = 'An error occurred attempting to write the gpg.ini file.\n' \ - 'path: {0}\n' \ - 'exception: {1}'.format(gpt_ini_path, e) + msg = ( + "An error occurred attempting to write the gpg.ini file.\n" + "path: {0}\n" + "exception: {1}".format(gpt_ini_path, e) + ) log.exception(msg) raise CommandExecutionError(msg) def _policyFileReplaceOrAppendList(string_list, policy_data): - ''' + """ helper function to take a list of strings for registry.pol file data and update existing strings or append the strings - ''' + """ if not policy_data: - policy_data = b'' + policy_data = b"" # we are going to clean off the special pre-fixes, so we get only the valuename - specialValueRegex = salt.utils.stringutils.to_bytes(r'(\*\*Del\.|\*\*DelVals\.){0,1}') + specialValueRegex = salt.utils.stringutils.to_bytes( + r"(\*\*Del\.|\*\*DelVals\.){0,1}" + ) for this_string in string_list: - list_item_key = this_string.split(b'\00;')[0].lstrip(b'[') - list_item_value_name = re.sub(specialValueRegex, - b'', - this_string.split(b'\00;')[1], - flags=re.IGNORECASE) - log.trace('item value name is %s', list_item_value_name) - data_to_replace = _regexSearchKeyValueCombo(policy_data, - list_item_key, - list_item_value_name) + list_item_key = this_string.split(b"\00;")[0].lstrip(b"[") + list_item_value_name = re.sub( + specialValueRegex, b"", this_string.split(b"\00;")[1], flags=re.IGNORECASE + ) + log.trace("item value name is %s", list_item_value_name) + data_to_replace = _regexSearchKeyValueCombo( + policy_data, list_item_key, list_item_value_name + ) if data_to_replace: - log.trace('replacing %s with %s', data_to_replace, this_string) + log.trace("replacing %s with %s", data_to_replace, this_string) policy_data = policy_data.replace(data_to_replace, this_string) else: - log.trace('appending %s', this_string) - policy_data = b''.join([policy_data, this_string]) + log.trace("appending %s", this_string) + policy_data = b"".join([policy_data, this_string]) return policy_data def _policyFileReplaceOrAppend(this_string, policy_data, append_only=False): - ''' + """ helper function to take a ADMX policy string for registry.pol file data and update existing string or append the string to the data - ''' + """ # we are going to clean off the special pre-fixes, so we get only the valuename if not policy_data: - policy_data = b'' - specialValueRegex = salt.utils.stringutils.to_bytes(r'(\*\*Del\.|\*\*DelVals\.){0,1}') + policy_data = b"" + specialValueRegex = salt.utils.stringutils.to_bytes( + r"(\*\*Del\.|\*\*DelVals\.){0,1}" + ) item_key = None item_value_name = None data_to_replace = None if not append_only: - item_key = this_string.split(b'\00;')[0].lstrip(b'[') - item_value_name = re.sub(specialValueRegex, - b'', - this_string.split(b'\00;')[1], - flags=re.IGNORECASE) - log.trace('item value name is %s', item_value_name) - data_to_replace = _regexSearchKeyValueCombo(policy_data, item_key, item_value_name) + item_key = this_string.split(b"\00;")[0].lstrip(b"[") + item_value_name = re.sub( + specialValueRegex, b"", this_string.split(b"\00;")[1], flags=re.IGNORECASE + ) + log.trace("item value name is %s", item_value_name) + data_to_replace = _regexSearchKeyValueCombo( + policy_data, item_key, item_value_name + ) if data_to_replace: - log.trace('replacing %s with %s', data_to_replace, this_string) + log.trace("replacing %s with %s", data_to_replace, this_string) policy_data = policy_data.replace(data_to_replace, this_string) else: - log.trace('appending %s', this_string) - policy_data = b''.join([policy_data, this_string]) + log.trace("appending %s", this_string) + policy_data = b"".join([policy_data, this_string]) return policy_data -def _writeAdminTemplateRegPolFile(admtemplate_data, - adml_language='en-US', - registry_class='Machine'): - r''' +def _writeAdminTemplateRegPolFile( + admtemplate_data, adml_language="en-US", registry_class="Machine" +): + r""" helper function to prep/write adm template data to the Registry.pol file each file begins with REGFILE_SIGNATURE (u'\u5250\u6765') and @@ -7006,8 +7374,8 @@ def _writeAdminTemplateRegPolFile(admtemplate_data, https://msdn.microsoft.com/en-us/library/aa374407(VS.85).aspx + https://msdn.microsoft.com/en-us/library/cc232696.aspx [Registry Path<NULL>;Reg Value<NULL>;Reg Type;SizeInBytes;Data<NULL>] - ''' - existing_data = b'' + """ + existing_data = b"" policy_data = _policy_info() policySearchXpath = '//ns1:*[@id = "{0}" or @name = "{0}"]' admx_policy_definitions = _get_policy_definitions(language=adml_language) @@ -7017,349 +7385,538 @@ def _writeAdminTemplateRegPolFile(admtemplate_data, adml_language=adml_language, return_full_policy_names=False, hierarchical_return=False, - return_not_configured=False) + return_not_configured=False, + ) for adm_namespace in admtemplate_data: for adm_policy in admtemplate_data[adm_namespace]: - if six.text_type(admtemplate_data[adm_namespace][adm_policy]).lower() == 'not configured': - if base_policy_settings.get(adm_namespace, {}).pop(adm_policy, None) is not None: + if ( + six.text_type(admtemplate_data[adm_namespace][adm_policy]).lower() + == "not configured" + ): + if ( + base_policy_settings.get(adm_namespace, {}).pop(adm_policy, None) + is not None + ): log.trace('Policy "%s" removed', adm_policy) else: - log.trace('adding %s to base_policy_settings', adm_policy) + log.trace("adding %s to base_policy_settings", adm_policy) if adm_namespace not in base_policy_settings: base_policy_settings[adm_namespace] = {} - base_policy_settings[adm_namespace][adm_policy] = admtemplate_data[adm_namespace][adm_policy] + base_policy_settings[adm_namespace][adm_policy] = admtemplate_data[ + adm_namespace + ][adm_policy] for adm_namespace in base_policy_settings: for admPolicy in base_policy_settings[adm_namespace]: - log.trace('working on admPolicy %s', admPolicy) + log.trace("working on admPolicy %s", admPolicy) explicit_enable_disable_value_setting = False this_key = None this_valuename = None - if six.text_type(base_policy_settings[adm_namespace][admPolicy]).lower() == 'disabled': - log.trace('time to disable %s', admPolicy) - this_policy = admx_policy_definitions.xpath(policySearchXpath.format(admPolicy), namespaces={'ns1': adm_namespace}) + if ( + six.text_type(base_policy_settings[adm_namespace][admPolicy]).lower() + == "disabled" + ): + log.trace("time to disable %s", admPolicy) + this_policy = admx_policy_definitions.xpath( + policySearchXpath.format(admPolicy), + namespaces={"ns1": adm_namespace}, + ) if this_policy: this_policy = this_policy[0] - if 'class' in this_policy.attrib: - if this_policy.attrib['class'] == registry_class or this_policy.attrib['class'] == 'Both': - if 'key' in this_policy.attrib: - this_key = this_policy.attrib['key'] + if "class" in this_policy.attrib: + if ( + this_policy.attrib["class"] == registry_class + or this_policy.attrib["class"] == "Both" + ): + if "key" in this_policy.attrib: + this_key = this_policy.attrib["key"] else: - log.error('policy item %s does not have ' - 'the required "key" attribute', - this_policy.attrib) + log.error( + "policy item %s does not have " + 'the required "key" attribute', + this_policy.attrib, + ) break - if 'valueName' in this_policy.attrib: - this_valuename = this_policy.attrib['valueName'] + if "valueName" in this_policy.attrib: + this_valuename = this_policy.attrib["valueName"] if DISABLED_VALUE_XPATH(this_policy): # set the disabled value in the registry.pol file explicit_enable_disable_value_setting = True - disabled_value_string = _checkValueItemParent(this_policy, - admPolicy, - this_key, - this_valuename, - DISABLED_VALUE_XPATH, - None, - check_deleted=False, - test_item=False) - existing_data = _policyFileReplaceOrAppend(disabled_value_string, - existing_data) + disabled_value_string = _checkValueItemParent( + this_policy, + admPolicy, + this_key, + this_valuename, + DISABLED_VALUE_XPATH, + None, + check_deleted=False, + test_item=False, + ) + existing_data = _policyFileReplaceOrAppend( + disabled_value_string, existing_data + ) if DISABLED_LIST_XPATH(this_policy): explicit_enable_disable_value_setting = True - disabled_list_strings = _checkListItem(this_policy, - admPolicy, - this_key, - DISABLED_LIST_XPATH, - None, - test_items=False) - log.trace('working with disabledList ' - 'portion of %s', admPolicy) - existing_data = _policyFileReplaceOrAppendList(disabled_list_strings, - existing_data) - if not explicit_enable_disable_value_setting and this_valuename: - disabled_value_string = _buildKnownDataSearchString(this_key, - this_valuename, - 'REG_DWORD', - None, - check_deleted=True) - existing_data = _policyFileReplaceOrAppend(disabled_value_string, - existing_data) + disabled_list_strings = _checkListItem( + this_policy, + admPolicy, + this_key, + DISABLED_LIST_XPATH, + None, + test_items=False, + ) + log.trace( + "working with disabledList " "portion of %s", + admPolicy, + ) + existing_data = _policyFileReplaceOrAppendList( + disabled_list_strings, existing_data + ) + if ( + not explicit_enable_disable_value_setting + and this_valuename + ): + disabled_value_string = _buildKnownDataSearchString( + this_key, + this_valuename, + "REG_DWORD", + None, + check_deleted=True, + ) + existing_data = _policyFileReplaceOrAppend( + disabled_value_string, existing_data + ) if ELEMENTS_XPATH(this_policy): - log.trace('checking elements of %s', - admPolicy) + log.trace("checking elements of %s", admPolicy) for elements_item in ELEMENTS_XPATH(this_policy): for child_item in elements_item.getchildren(): child_key = this_key child_valuename = this_valuename - if 'key' in child_item.attrib: - child_key = child_item.attrib['key'] - if 'valueName' in child_item.attrib: - child_valuename = child_item.attrib['valueName'] - if etree.QName(child_item).localname == 'boolean' \ - and (TRUE_LIST_XPATH(child_item) or FALSE_LIST_XPATH(child_item)): + if "key" in child_item.attrib: + child_key = child_item.attrib["key"] + if "valueName" in child_item.attrib: + child_valuename = child_item.attrib[ + "valueName" + ] + if etree.QName( + child_item + ).localname == "boolean" and ( + TRUE_LIST_XPATH(child_item) + or FALSE_LIST_XPATH(child_item) + ): # WARNING: no OOB adm files use true/falseList items # this has not been fully vetted - temp_dict = {'trueList': TRUE_LIST_XPATH, 'falseList': FALSE_LIST_XPATH} + temp_dict = { + "trueList": TRUE_LIST_XPATH, + "falseList": FALSE_LIST_XPATH, + } for this_list in temp_dict: disabled_list_strings = _checkListItem( - child_item, - admPolicy, - child_key, - temp_dict[this_list], - None, - test_items=False) - log.trace('working with %s portion of %s', - admPolicy, - this_list) + child_item, + admPolicy, + child_key, + temp_dict[this_list], + None, + test_items=False, + ) + log.trace( + "working with %s portion of %s", + admPolicy, + this_list, + ) existing_data = _policyFileReplaceOrAppendList( - disabled_list_strings, - existing_data) - elif etree.QName(child_item).localname == 'boolean' \ - or etree.QName(child_item).localname == 'decimal' \ - or etree.QName(child_item).localname == 'text' \ - or etree.QName(child_item).localname == 'longDecimal' \ - or etree.QName(child_item).localname == 'multiText' \ - or etree.QName(child_item).localname == 'enum': - disabled_value_string = _processValueItem(child_item, - child_key, - child_valuename, - this_policy, - elements_item, - check_deleted=True) - log.trace('I have disabled value string of %s', - disabled_value_string) + disabled_list_strings, existing_data + ) + elif ( + etree.QName(child_item).localname + == "boolean" + or etree.QName(child_item).localname + == "decimal" + or etree.QName(child_item).localname + == "text" + or etree.QName(child_item).localname + == "longDecimal" + or etree.QName(child_item).localname + == "multiText" + or etree.QName(child_item).localname + == "enum" + ): + disabled_value_string = _processValueItem( + child_item, + child_key, + child_valuename, + this_policy, + elements_item, + check_deleted=True, + ) + log.trace( + "I have disabled value string of %s", + disabled_value_string, + ) existing_data = _policyFileReplaceOrAppend( - disabled_value_string, - existing_data) - elif etree.QName(child_item).localname == 'list': - disabled_value_string = _processValueItem(child_item, - child_key, - child_valuename, - this_policy, - elements_item, - check_deleted=True) - log.trace('I have disabled value string of %s', - disabled_value_string) + disabled_value_string, existing_data + ) + elif ( + etree.QName(child_item).localname == "list" + ): + disabled_value_string = _processValueItem( + child_item, + child_key, + child_valuename, + this_policy, + elements_item, + check_deleted=True, + ) + log.trace( + "I have disabled value string of %s", + disabled_value_string, + ) existing_data = _policyFileReplaceOrAppend( - disabled_value_string, - existing_data) + disabled_value_string, existing_data + ) else: - log.error('policy %s was found but it does not appear to be valid for the class %s', - admPolicy, registry_class) + log.error( + "policy %s was found but it does not appear to be valid for the class %s", + admPolicy, + registry_class, + ) else: - log.error('policy item %s does not have the requried "class" attribute', - this_policy.attrib) + log.error( + 'policy item %s does not have the requried "class" attribute', + this_policy.attrib, + ) else: - log.trace('time to enable and set the policy "%s"', - admPolicy) - this_policy = admx_policy_definitions.xpath(policySearchXpath.format(admPolicy), namespaces={'ns1': adm_namespace}) - log.trace('found this_policy == %s', this_policy) + log.trace('time to enable and set the policy "%s"', admPolicy) + this_policy = admx_policy_definitions.xpath( + policySearchXpath.format(admPolicy), + namespaces={"ns1": adm_namespace}, + ) + log.trace("found this_policy == %s", this_policy) if this_policy: this_policy = this_policy[0] - if 'class' in this_policy.attrib: - if this_policy.attrib['class'] == registry_class or this_policy.attrib['class'] == 'Both': - if 'key' in this_policy.attrib: - this_key = this_policy.attrib['key'] + if "class" in this_policy.attrib: + if ( + this_policy.attrib["class"] == registry_class + or this_policy.attrib["class"] == "Both" + ): + if "key" in this_policy.attrib: + this_key = this_policy.attrib["key"] else: - log.error('policy item %s does not have the required "key" attribute', - this_policy.attrib) + log.error( + 'policy item %s does not have the required "key" attribute', + this_policy.attrib, + ) break - if 'valueName' in this_policy.attrib: - this_valuename = this_policy.attrib['valueName'] + if "valueName" in this_policy.attrib: + this_valuename = this_policy.attrib["valueName"] if ENABLED_VALUE_XPATH(this_policy): explicit_enable_disable_value_setting = True - enabled_value_string = _checkValueItemParent(this_policy, - admPolicy, - this_key, - this_valuename, - ENABLED_VALUE_XPATH, - None, - check_deleted=False, - test_item=False) + enabled_value_string = _checkValueItemParent( + this_policy, + admPolicy, + this_key, + this_valuename, + ENABLED_VALUE_XPATH, + None, + check_deleted=False, + test_item=False, + ) existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data) + enabled_value_string, existing_data + ) if ENABLED_LIST_XPATH(this_policy): explicit_enable_disable_value_setting = True - enabled_list_strings = _checkListItem(this_policy, - admPolicy, - this_key, - ENABLED_LIST_XPATH, - None, - test_items=False) - log.trace('working with enabledList portion of %s', admPolicy) + enabled_list_strings = _checkListItem( + this_policy, + admPolicy, + this_key, + ENABLED_LIST_XPATH, + None, + test_items=False, + ) + log.trace( + "working with enabledList portion of %s", admPolicy + ) existing_data = _policyFileReplaceOrAppendList( - enabled_list_strings, - existing_data) - if not explicit_enable_disable_value_setting and this_valuename: - enabled_value_string = _buildKnownDataSearchString(this_key, - this_valuename, - 'REG_DWORD', - '1', - check_deleted=False) + enabled_list_strings, existing_data + ) + if ( + not explicit_enable_disable_value_setting + and this_valuename + ): + enabled_value_string = _buildKnownDataSearchString( + this_key, + this_valuename, + "REG_DWORD", + "1", + check_deleted=False, + ) existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data) + enabled_value_string, existing_data + ) if ELEMENTS_XPATH(this_policy): for elements_item in ELEMENTS_XPATH(this_policy): for child_item in elements_item.getchildren(): child_key = this_key child_valuename = this_valuename - if 'key' in child_item.attrib: - child_key = child_item.attrib['key'] - if 'valueName' in child_item.attrib: - child_valuename = child_item.attrib['valueName'] - if child_item.attrib['id'] in base_policy_settings[adm_namespace][admPolicy]: - if etree.QName(child_item).localname == 'boolean' and ( - TRUE_LIST_XPATH(child_item) or FALSE_LIST_XPATH(child_item)): + if "key" in child_item.attrib: + child_key = child_item.attrib["key"] + if "valueName" in child_item.attrib: + child_valuename = child_item.attrib[ + "valueName" + ] + if ( + child_item.attrib["id"] + in base_policy_settings[adm_namespace][ + admPolicy + ] + ): + if etree.QName( + child_item + ).localname == "boolean" and ( + TRUE_LIST_XPATH(child_item) + or FALSE_LIST_XPATH(child_item) + ): list_strings = [] - if base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]: - list_strings = _checkListItem(child_item, - admPolicy, - child_key, - TRUE_LIST_XPATH, - None, - test_items=False) - log.trace('working with trueList portion of {0}'.format(admPolicy)) + if base_policy_settings[adm_namespace][ + admPolicy + ][child_item.attrib["id"]]: + list_strings = _checkListItem( + child_item, + admPolicy, + child_key, + TRUE_LIST_XPATH, + None, + test_items=False, + ) + log.trace( + "working with trueList portion of {0}".format( + admPolicy + ) + ) else: - list_strings = _checkListItem(child_item, - admPolicy, - child_key, - FALSE_LIST_XPATH, - None, - test_items=False) + list_strings = _checkListItem( + child_item, + admPolicy, + child_key, + FALSE_LIST_XPATH, + None, + test_items=False, + ) existing_data = _policyFileReplaceOrAppendList( - list_strings, - existing_data) - elif etree.QName(child_item).localname == 'boolean' and ( - TRUE_VALUE_XPATH(child_item) or FALSE_VALUE_XPATH(child_item)): - value_string = '' - if base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]: - value_string = _checkValueItemParent(child_item, - admPolicy, - child_key, - child_valuename, - TRUE_VALUE_XPATH, - None, - check_deleted=False, - test_item=False) + list_strings, existing_data + ) + elif etree.QName( + child_item + ).localname == "boolean" and ( + TRUE_VALUE_XPATH(child_item) + or FALSE_VALUE_XPATH(child_item) + ): + value_string = "" + if base_policy_settings[adm_namespace][ + admPolicy + ][child_item.attrib["id"]]: + value_string = _checkValueItemParent( + child_item, + admPolicy, + child_key, + child_valuename, + TRUE_VALUE_XPATH, + None, + check_deleted=False, + test_item=False, + ) else: - value_string = _checkValueItemParent(child_item, - admPolicy, - child_key, - child_valuename, - FALSE_VALUE_XPATH, - None, - check_deleted=False, - test_item=False) - existing_data = _policyFileReplaceOrAppend( - value_string, - existing_data) - elif etree.QName(child_item).localname == 'boolean' \ - or etree.QName(child_item).localname == 'decimal' \ - or etree.QName(child_item).localname == 'text' \ - or etree.QName(child_item).localname == 'longDecimal' \ - or etree.QName(child_item).localname == 'multiText': - enabled_value_string = _processValueItem( + value_string = _checkValueItemParent( child_item, + admPolicy, child_key, child_valuename, - this_policy, - elements_item, + FALSE_VALUE_XPATH, + None, check_deleted=False, - this_element_value=base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]) - log.trace('I have enabled value string of %s', enabled_value_string) + test_item=False, + ) existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data) - elif etree.QName(child_item).localname == 'enum': - for enum_item in child_item.getchildren(): - if base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']] == \ - _getAdmlDisplayName(adml_policy_resources, - enum_item.attrib['displayName'] - ).strip(): + value_string, existing_data + ) + elif ( + etree.QName(child_item).localname + == "boolean" + or etree.QName(child_item).localname + == "decimal" + or etree.QName(child_item).localname + == "text" + or etree.QName(child_item).localname + == "longDecimal" + or etree.QName(child_item).localname + == "multiText" + ): + enabled_value_string = _processValueItem( + child_item, + child_key, + child_valuename, + this_policy, + elements_item, + check_deleted=False, + this_element_value=base_policy_settings[ + adm_namespace + ][ + admPolicy + ][ + child_item.attrib["id"] + ], + ) + log.trace( + "I have enabled value string of %s", + enabled_value_string, + ) + existing_data = _policyFileReplaceOrAppend( + enabled_value_string, existing_data + ) + elif ( + etree.QName(child_item).localname + == "enum" + ): + for ( + enum_item + ) in child_item.getchildren(): + if ( + base_policy_settings[ + adm_namespace + ][admPolicy][ + child_item.attrib["id"] + ] + == _getAdmlDisplayName( + adml_policy_resources, + enum_item.attrib[ + "displayName" + ], + ).strip() + ): enabled_value_string = _checkValueItemParent( - enum_item, - child_item.attrib['id'], - child_key, - child_valuename, - VALUE_XPATH, - None, - check_deleted=False, - test_item=False) + enum_item, + child_item.attrib["id"], + child_key, + child_valuename, + VALUE_XPATH, + None, + check_deleted=False, + test_item=False, + ) existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data) + enabled_value_string, + existing_data, + ) if VALUE_LIST_XPATH(enum_item): - enabled_list_strings = _checkListItem(enum_item, - admPolicy, - child_key, - VALUE_LIST_XPATH, - None, - test_items=False) - log.trace('working with valueList portion of %s', - child_item.attrib['id']) + enabled_list_strings = _checkListItem( + enum_item, + admPolicy, + child_key, + VALUE_LIST_XPATH, + None, + test_items=False, + ) + log.trace( + "working with valueList portion of %s", + child_item.attrib["id"], + ) existing_data = _policyFileReplaceOrAppendList( - enabled_list_strings, - existing_data) + enabled_list_strings, + existing_data, + ) break - elif etree.QName(child_item).localname == 'list': + elif ( + etree.QName(child_item).localname + == "list" + ): enabled_value_string = _processValueItem( - child_item, - child_key, - child_valuename, - this_policy, - elements_item, - check_deleted=False, - this_element_value=base_policy_settings[adm_namespace][admPolicy][child_item.attrib['id']]) - log.trace('I have enabled value string of %s', - enabled_value_string) + child_item, + child_key, + child_valuename, + this_policy, + elements_item, + check_deleted=False, + this_element_value=base_policy_settings[ + adm_namespace + ][ + admPolicy + ][ + child_item.attrib["id"] + ], + ) + log.trace( + "I have enabled value string of %s", + enabled_value_string, + ) existing_data = _policyFileReplaceOrAppend( - enabled_value_string, - existing_data, - append_only=True) + enabled_value_string, + existing_data, + append_only=True, + ) try: - _write_regpol_data(existing_data, - policy_data.admx_registry_classes[registry_class]['policy_path'], - policy_data.gpt_ini_path, - policy_data.admx_registry_classes[registry_class]['gpt_extension_location'], - policy_data.admx_registry_classes[registry_class]['gpt_extension_guid']) + _write_regpol_data( + existing_data, + policy_data.admx_registry_classes[registry_class]["policy_path"], + policy_data.gpt_ini_path, + policy_data.admx_registry_classes[registry_class]["gpt_extension_location"], + policy_data.admx_registry_classes[registry_class]["gpt_extension_guid"], + ) # TODO: This needs to be more specific or removed except CommandExecutionError as exc: # pylint: disable=broad-except - log.exception('Unhandled exception occurred while attempting to ' - 'write Adm Template Policy File.\nException: %s', exc) + log.exception( + "Unhandled exception occurred while attempting to " + "write Adm Template Policy File.\nException: %s", + exc, + ) return False return True def _getScriptSettingsFromIniFile(policy_info): - ''' + """ helper function to parse/read a GPO Startup/Shutdown script file psscript.ini and script.ini file definitions are here https://msdn.microsoft.com/en-us/library/ff842529.aspx https://msdn.microsoft.com/en-us/library/dd303238.aspx - ''' + """ _existingData = None - if os.path.isfile(policy_info['ScriptIni']['IniPath']): - with salt.utils.files.fopen(policy_info['ScriptIni']['IniPath'], 'rb') as fhr: + if os.path.isfile(policy_info["ScriptIni"]["IniPath"]): + with salt.utils.files.fopen(policy_info["ScriptIni"]["IniPath"], "rb") as fhr: _existingData = fhr.read() if _existingData: try: - _existingData = deserialize(_existingData.decode('utf-16-le').lstrip('\ufeff')) - log.trace('Have deserialized data %s', _existingData) + _existingData = deserialize( + _existingData.decode("utf-16-le").lstrip("\ufeff") + ) + log.trace("Have deserialized data %s", _existingData) except Exception as error: # pylint: disable=broad-except - log.exception('An error occurred attempting to deserialize data for %s', policy_info['Policy']) + log.exception( + "An error occurred attempting to deserialize data for %s", + policy_info["Policy"], + ) raise CommandExecutionError(error) - if 'Section' in policy_info['ScriptIni'] and policy_info['ScriptIni']['Section'].lower() in [z.lower() for z in _existingData.keys()]: - if 'SettingName' in policy_info['ScriptIni']: - log.trace('Need to look for %s', policy_info['ScriptIni']['SettingName']) - if policy_info['ScriptIni']['SettingName'].lower() in [z.lower() for z in _existingData[policy_info['ScriptIni']['Section']].keys()]: - return _existingData[policy_info['ScriptIni']['Section']][policy_info['ScriptIni']['SettingName'].lower()] + if "Section" in policy_info["ScriptIni"] and policy_info["ScriptIni"][ + "Section" + ].lower() in [z.lower() for z in _existingData.keys()]: + if "SettingName" in policy_info["ScriptIni"]: + log.trace( + "Need to look for %s", policy_info["ScriptIni"]["SettingName"] + ) + if policy_info["ScriptIni"]["SettingName"].lower() in [ + z.lower() + for z in _existingData[ + policy_info["ScriptIni"]["Section"] + ].keys() + ]: + return _existingData[policy_info["ScriptIni"]["Section"]][ + policy_info["ScriptIni"]["SettingName"].lower() + ] else: return None else: - return _existingData[policy_info['ScriptIni']['Section']] + return _existingData[policy_info["ScriptIni"]["Section"]] else: return None @@ -7367,7 +7924,7 @@ def _getScriptSettingsFromIniFile(policy_info): def _writeGpoScript(psscript=False): - ''' + """ helper function to write local GPO startup/shutdown script scripts are stored in scripts.ini and psscripts.ini files in @@ -7394,132 +7951,164 @@ def _writeGpoScript(psscript=False): these can be set to True/False to denote if the powershell startup/shutdown scripts execute first (True) or last (False), if the value isn't set, then it is 'Not Configured' in the GUI - ''' - _machineScriptPolicyPath = os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'Machine', - 'Scripts', - 'scripts.ini') - _machinePowershellScriptPolicyPath = os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'Machine', - 'Scripts', - 'psscripts.ini') - _userScriptPolicyPath = os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'User', - 'Scripts', - 'scripts.ini') - _userPowershellScriptPolicyPath = os.path.join(os.getenv('WINDIR'), - 'System32', - 'GroupPolicy', - 'User', - 'Scripts', - 'psscripts.ini') + """ + _machineScriptPolicyPath = os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "Machine", + "Scripts", + "scripts.ini", + ) + _machinePowershellScriptPolicyPath = os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "Machine", + "Scripts", + "psscripts.ini", + ) + _userScriptPolicyPath = os.path.join( + os.getenv("WINDIR"), "System32", "GroupPolicy", "User", "Scripts", "scripts.ini" + ) + _userPowershellScriptPolicyPath = os.path.join( + os.getenv("WINDIR"), + "System32", + "GroupPolicy", + "User", + "Scripts", + "psscripts.ini", + ) -def _lookup_admin_template(policy_name, - policy_class, - adml_language='en-US'): - ''' +def _lookup_admin_template(policy_name, policy_class, adml_language="en-US"): + """ (success_flag, policy_xml_item, policy_name_list, message) - ''' + """ policy_aliases = [] admx_policy_definitions = _get_policy_definitions(language=adml_language) adml_policy_resources = _get_policy_resources(language=adml_language) - admx_search_results = ADMX_SEARCH_XPATH(admx_policy_definitions, - policy_name=policy_name, - registry_class=policy_class) + admx_search_results = ADMX_SEARCH_XPATH( + admx_policy_definitions, policy_name=policy_name, registry_class=policy_class + ) if admx_search_results: if len(admx_search_results) == 1: the_policy = admx_search_results[0] policy_display_name = _getFullPolicyName( - policy_item=the_policy, - policy_name=the_policy.attrib['name'], - return_full_policy_names=True, - adml_language=adml_language) + policy_item=the_policy, + policy_name=the_policy.attrib["name"], + return_full_policy_names=True, + adml_language=adml_language, + ) policy_aliases.append(policy_display_name) - policy_aliases.append(the_policy.attrib['name']) - full_path_list = _build_parent_list(policy_definition=the_policy, - return_full_policy_names=True, - adml_language=adml_language) + policy_aliases.append(the_policy.attrib["name"]) + full_path_list = _build_parent_list( + policy_definition=the_policy, + return_full_policy_names=True, + adml_language=adml_language, + ) full_path_list.reverse() full_path_list.append(policy_display_name) - policy_aliases.append('\\'.join(full_path_list)) + policy_aliases.append("\\".join(full_path_list)) return True, the_policy, policy_aliases, None else: msg = 'ADMX policy name/id "{0}" is used in multiple ADMX files' return False, None, [], msg else: - adml_search_results = ADML_SEARCH_XPATH(adml_policy_resources, - policy_name=policy_name) + adml_search_results = ADML_SEARCH_XPATH( + adml_policy_resources, policy_name=policy_name + ) hierarchy = [] hierarchy_policy_name = policy_name if not adml_search_results: - log.warning('Trying another: %s', policy_name) - if '\\' in policy_name: - hierarchy = policy_name.split('\\') + log.warning("Trying another: %s", policy_name) + if "\\" in policy_name: + hierarchy = policy_name.split("\\") policy_name = hierarchy.pop() - adml_search_results = ADML_SEARCH_XPATH(adml_policy_resources, - policy_name=policy_name) + adml_search_results = ADML_SEARCH_XPATH( + adml_policy_resources, policy_name=policy_name + ) if adml_search_results: multiple_adml_entries = False - suggested_policies = '' + suggested_policies = "" adml_to_remove = [] if len(adml_search_results) > 1: - log.trace('multiple ADML entries found matching the policy name %s', policy_name) + log.trace( + "multiple ADML entries found matching the policy name %s", + policy_name, + ) multiple_adml_entries = True for adml_search_result in adml_search_results: - if not getattr(adml_search_result, 'text', '').strip() == policy_name: + if ( + not getattr(adml_search_result, "text", "").strip() + == policy_name + ): adml_to_remove.append(adml_search_result) else: if hierarchy: - log.trace('we have hierarchy of %s', hierarchy) - display_name_searchval = '$({0}.{1})'.format( - adml_search_result.tag.split('}')[1], - adml_search_result.attrib['id']) - #policy_search_string = '//{0}:policy[@*[local-name() = "displayName"] = "{1}" and (@*[local-name() = "class"] = "Both" or @*[local-name() = "class"] = "{2}") ]'.format( + log.trace("we have hierarchy of %s", hierarchy) + display_name_searchval = "$({0}.{1})".format( + adml_search_result.tag.split("}")[1], + adml_search_result.attrib["id"], + ) + # policy_search_string = '//{0}:policy[@*[local-name() = "displayName"] = "{1}" and (@*[local-name() = "class"] = "Both" or @*[local-name() = "class"] = "{2}") ]'.format( policy_search_string = '//{0}:policy[@displayName = "{1}" and (@class = "Both" or @class = "{2}") ]'.format( adml_search_result.prefix, display_name_searchval, - policy_class) + policy_class, + ) admx_results = [] - these_admx_search_results = admx_policy_definitions.xpath(policy_search_string, namespaces=adml_search_result.nsmap) + these_admx_search_results = admx_policy_definitions.xpath( + policy_search_string, + namespaces=adml_search_result.nsmap, + ) if not these_admx_search_results: - log.trace('No admx was found for the adml entry %s, it will be removed', display_name_searchval) + log.trace( + "No admx was found for the adml entry %s, it will be removed", + display_name_searchval, + ) adml_to_remove.append(adml_search_result) for search_result in these_admx_search_results: - log.trace('policy_name == %s', policy_name) + log.trace("policy_name == %s", policy_name) this_hierarchy = _build_parent_list( policy_definition=search_result, return_full_policy_names=True, - adml_language=adml_language) + adml_language=adml_language, + ) this_hierarchy.reverse() if hierarchy != this_hierarchy: - msg = 'hierarchy %s does not match this item\'s hierarchy of %s' + msg = "hierarchy %s does not match this item's hierarchy of %s" log.trace(msg, hierarchy, this_hierarchy) if len(these_admx_search_results) == 1: - log.trace('only 1 admx was found and it does not match this adml, it is safe to remove from the list') + log.trace( + "only 1 admx was found and it does not match this adml, it is safe to remove from the list" + ) adml_to_remove.append(adml_search_result) else: - log.trace('hierarchy %s matches item\'s hierarchy of %s', hierarchy, this_hierarchy) - log.trace('search_result %s added to results', search_result) + log.trace( + "hierarchy %s matches item's hierarchy of %s", + hierarchy, + this_hierarchy, + ) + log.trace( + "search_result %s added to results", + search_result, + ) admx_results.append(search_result) if len(admx_results) == 1: admx_search_results.append(admx_results[0]) else: # verify the ADMX correlated to this ADML is in the same class # that we are looking for - display_name_searchval = '$({0}.{1})'.format( - adml_search_result.tag.split('}')[1], - adml_search_result.attrib['id']) + display_name_searchval = "$({0}.{1})".format( + adml_search_result.tag.split("}")[1], + adml_search_result.attrib["id"], + ) these_admx_search_results = ADMX_DISPLAYNAME_SEARCH_XPATH( - admx_policy_definitions, - display_name=display_name_searchval, - registry_class=policy_class) + admx_policy_definitions, + display_name=display_name_searchval, + registry_class=policy_class, + ) if not these_admx_search_results: adml_to_remove.append(adml_search_result) for adml in adml_to_remove: @@ -7528,23 +8117,39 @@ def _lookup_admin_template(policy_name, if len(adml_search_results) == 1 and multiple_adml_entries: multiple_adml_entries = False for adml_search_result in adml_search_results: - log.trace('found an ADML entry matching the string! %s -- %s', - adml_search_result.tag, - adml_search_result.attrib) - display_name_searchval = '$({0}.{1})'.format( - adml_search_result.tag.split('}')[1], - adml_search_result.attrib['id']) - log.trace('searching for displayName == %s', display_name_searchval) + log.trace( + "found an ADML entry matching the string! %s -- %s", + adml_search_result.tag, + adml_search_result.attrib, + ) + display_name_searchval = "$({0}.{1})".format( + adml_search_result.tag.split("}")[1], + adml_search_result.attrib["id"], + ) + log.trace("searching for displayName == %s", display_name_searchval) if not admx_search_results: - log.trace('search for an admx entry matching display_name %s and registry_class %s', display_name_searchval, policy_class) + log.trace( + "search for an admx entry matching display_name %s and registry_class %s", + display_name_searchval, + policy_class, + ) admx_search_results = ADMX_DISPLAYNAME_SEARCH_XPATH( - admx_policy_definitions, - display_name=display_name_searchval, - registry_class=policy_class) + admx_policy_definitions, + display_name=display_name_searchval, + registry_class=policy_class, + ) if admx_search_results: - log.trace('processing admx_search_results of {0}'.format(admx_search_results)) - log.trace('multiple_adml_entries is {0}'.format(multiple_adml_entries)) - if (len(admx_search_results) == 1 or hierarchy) and not multiple_adml_entries: + log.trace( + "processing admx_search_results of {0}".format( + admx_search_results + ) + ) + log.trace( + "multiple_adml_entries is {0}".format(multiple_adml_entries) + ) + if ( + len(admx_search_results) == 1 or hierarchy + ) and not multiple_adml_entries: found = False for search_result in admx_search_results: found = False @@ -7552,70 +8157,85 @@ def _lookup_admin_template(policy_name, this_hierarchy = _build_parent_list( policy_definition=search_result, return_full_policy_names=True, - adml_language=adml_language) + adml_language=adml_language, + ) this_hierarchy.reverse() - log.trace('testing %s == %s', hierarchy, this_hierarchy) + log.trace("testing %s == %s", hierarchy, this_hierarchy) if hierarchy == this_hierarchy: found = True else: found = True if found: - log.trace('found the ADMX policy matching ' - 'the display name %s -- %s', - search_result, policy_name) - if 'name' in search_result.attrib: + log.trace( + "found the ADMX policy matching " + "the display name %s -- %s", + search_result, + policy_name, + ) + if "name" in search_result.attrib: policy_display_name = _getFullPolicyName( policy_item=search_result, - policy_name=search_result.attrib['name'], + policy_name=search_result.attrib["name"], return_full_policy_names=True, - adml_language=adml_language) + adml_language=adml_language, + ) policy_aliases.append(policy_display_name) - policy_aliases.append(search_result.attrib['name']) + policy_aliases.append(search_result.attrib["name"]) full_path_list = _build_parent_list( policy_definition=search_result, return_full_policy_names=True, - adml_language=adml_language) + adml_language=adml_language, + ) full_path_list.reverse() full_path_list.append(policy_display_name) - policy_aliases.append('\\'.join(full_path_list)) + policy_aliases.append("\\".join(full_path_list)) return True, search_result, policy_aliases, None else: - msg = ('ADMX policy with the display name {0} does not' - 'have the required name attribute') + msg = ( + "ADMX policy with the display name {0} does not" + "have the required name attribute" + ) msg = msg.format(policy_name) return False, None, [], msg if not found: - msg = 'Unable to correlate {0} to any policy'.format(hierarchy_policy_name) + msg = "Unable to correlate {0} to any policy".format( + hierarchy_policy_name + ) return False, None, [], msg else: for possible_policy in admx_search_results: this_parent_list = _build_parent_list( policy_definition=possible_policy, return_full_policy_names=True, - adml_language=adml_language) + adml_language=adml_language, + ) this_parent_list.reverse() this_parent_list.append(policy_name) if suggested_policies: - suggested_policies = ', '.join([suggested_policies, - '\\'.join(this_parent_list)]) + suggested_policies = ", ".join( + [suggested_policies, "\\".join(this_parent_list)] + ) else: - suggested_policies = '\\'.join(this_parent_list) + suggested_policies = "\\".join(this_parent_list) if suggested_policies: - msg = ('ADML policy name "{0}" is used as the display name' - ' for multiple policies.' - ' These policies matched: {1}' - '. You can utilize these long names to' - ' specify the correct policy') - return False, None, [], \ - msg.format(policy_name, suggested_policies) - return False, None, [], \ - 'Unable to find {0} policy {1}'.format(policy_class, policy_name) + msg = ( + 'ADML policy name "{0}" is used as the display name' + " for multiple policies." + " These policies matched: {1}" + ". You can utilize these long names to" + " specify the correct policy" + ) + return False, None, [], msg.format(policy_name, suggested_policies) + return ( + False, + None, + [], + "Unable to find {0} policy {1}".format(policy_class, policy_name), + ) -def get_policy_info(policy_name, - policy_class, - adml_language='en-US'): - r''' +def get_policy_info(policy_name, policy_class, adml_language="en-US"): + r""" Returns information about a specified policy Args: @@ -7752,69 +8372,84 @@ def get_policy_info(policy_name, respectively) the ``ShellRemoveOrderPrints_2`` is the "short name" we could use to pass through ``get_policy_info`` to see what the module itself is expecting. - ''' + """ # return the possible policy names and element names - ret = {'policy_name': policy_name, - 'policy_class': policy_class, - 'policy_aliases': [], - 'policy_found': False, - 'rights_assignment': False, - 'policy_elements': [], - 'message': 'policy not found'} + ret = { + "policy_name": policy_name, + "policy_class": policy_class, + "policy_aliases": [], + "policy_found": False, + "rights_assignment": False, + "policy_elements": [], + "message": "policy not found", + } policy_class = policy_class.title() policy_data = _policy_info() if policy_class not in policy_data.policies.keys(): - policy_classes = ', '.join(policy_data.policies.keys()) - ret['message'] = 'The requested policy class "{0}" is invalid, ' \ - 'policy_class should be one of: {1}' \ - ''.format(policy_class, policy_classes) + policy_classes = ", ".join(policy_data.policies.keys()) + ret["message"] = ( + 'The requested policy class "{0}" is invalid, ' + "policy_class should be one of: {1}" + "".format(policy_class, policy_classes) + ) return ret - if policy_name in policy_data.policies[policy_class]['policies']: - ret['policy_aliases'].append( - policy_data.policies[policy_class]['policies'][policy_name]['Policy']) - ret['policy_found'] = True - ret['message'] = '' - if 'LsaRights' in policy_data.policies[policy_class]['policies'][policy_name]: - ret['rights_assignment'] = True + if policy_name in policy_data.policies[policy_class]["policies"]: + ret["policy_aliases"].append( + policy_data.policies[policy_class]["policies"][policy_name]["Policy"] + ) + ret["policy_found"] = True + ret["message"] = "" + if "LsaRights" in policy_data.policies[policy_class]["policies"][policy_name]: + ret["rights_assignment"] = True return ret else: - for pol in policy_data.policies[policy_class]['policies']: - if policy_data.policies[policy_class]['policies'][pol]['Policy'].lower() == policy_name.lower(): - ret['policy_aliases'].append(pol) - ret['policy_found'] = True - ret['message'] = '' - if 'LsaRights' in policy_data.policies[policy_class]['policies'][pol]: - ret['rights_assignment'] = True + for pol in policy_data.policies[policy_class]["policies"]: + if ( + policy_data.policies[policy_class]["policies"][pol]["Policy"].lower() + == policy_name.lower() + ): + ret["policy_aliases"].append(pol) + ret["policy_found"] = True + ret["message"] = "" + if "LsaRights" in policy_data.policies[policy_class]["policies"][pol]: + ret["rights_assignment"] = True return ret success, policy_xml_item, policy_name_list, message = _lookup_admin_template( - policy_name=policy_name, - policy_class=policy_class, - adml_language=adml_language) + policy_name=policy_name, policy_class=policy_class, adml_language=adml_language + ) if success: for elements_item in ELEMENTS_XPATH(policy_xml_item): for child_item in elements_item.getchildren(): this_element_name = _getFullPolicyName( policy_item=child_item, - policy_name=child_item.attrib['id'], + policy_name=child_item.attrib["id"], return_full_policy_names=True, - adml_language=adml_language) - ret['policy_elements'].append( - {'element_id': child_item.attrib['id'], - 'element_aliases': [child_item.attrib['id'], this_element_name]}) - ret['policy_aliases'] = policy_name_list - ret['policy_found'] = True - ret['message'] = '' + adml_language=adml_language, + ) + ret["policy_elements"].append( + { + "element_id": child_item.attrib["id"], + "element_aliases": [child_item.attrib["id"], this_element_name], + } + ) + ret["policy_aliases"] = policy_name_list + ret["policy_found"] = True + ret["message"] = "" return ret else: - ret['message'] = message + ret["message"] = message return ret -def get(policy_class=None, return_full_policy_names=True, - hierarchical_return=False, adml_language='en-US', - return_not_configured=False): - ''' +def get( + policy_class=None, + return_full_policy_names=True, + hierarchical_return=False, + adml_language="en-US", + return_not_configured=False, +): + """ Get a policy value Args: @@ -7851,48 +8486,58 @@ def get(policy_class=None, return_full_policy_names=True, .. code-block:: bash salt '*' lgpo.get machine return_full_policy_names=True - ''' + """ vals = {} _policydata = _policy_info() - if policy_class is None or policy_class.lower() == 'both': + if policy_class is None or policy_class.lower() == "both": policy_class = _policydata.policies.keys() elif policy_class.lower() not in [z.lower() for z in _policydata.policies]: - msg = 'The policy_class {0} is not an available policy class, please ' \ - 'use one of the following: {1}, Both' + msg = ( + "The policy_class {0} is not an available policy class, please " + "use one of the following: {1}, Both" + ) raise SaltInvocationError( - msg.format(policy_class, ', '.join(_policydata.policies.keys()))) + msg.format(policy_class, ", ".join(_policydata.policies.keys())) + ) else: policy_class = [policy_class.title()] # handle policies statically defined in this module for p_class in policy_class: - this_class_policy_names = _policydata.policies[p_class]['policies'] + this_class_policy_names = _policydata.policies[p_class]["policies"] class_vals = {} for policy_name in this_class_policy_names: _pol = None - if policy_name in _policydata.policies[p_class]['policies']: - _pol = _policydata.policies[p_class]['policies'][policy_name] + if policy_name in _policydata.policies[p_class]["policies"]: + _pol = _policydata.policies[p_class]["policies"][policy_name] else: - for policy in _policydata.policies[p_class]['policies']: - if _policydata.policies[p_class]['policies'][policy]['Policy'].upper() == policy_name.upper(): - _pol = _policydata.policies[p_class]['policies'][policy] + for policy in _policydata.policies[p_class]["policies"]: + if ( + _policydata.policies[p_class]["policies"][policy][ + "Policy" + ].upper() + == policy_name.upper() + ): + _pol = _policydata.policies[p_class]["policies"][policy] policy_name = policy if _pol: vals_key_name = policy_name class_vals[policy_name] = _get_policy_info_setting(_pol) if return_full_policy_names: - class_vals[_pol['Policy']] = class_vals.pop(policy_name) - vals_key_name = _pol['Policy'] + class_vals[_pol["Policy"]] = class_vals.pop(policy_name) + vals_key_name = _pol["Policy"] if hierarchical_return: - if 'lgpo_section' in _pol: + if "lgpo_section" in _pol: firstItem = True tdict = {} - for level in reversed(_pol['lgpo_section']): + for level in reversed(_pol["lgpo_section"]): newdict = {} if firstItem: - newdict[level] = {vals_key_name: class_vals.pop(vals_key_name)} + newdict[level] = { + vals_key_name: class_vals.pop(vals_key_name) + } firstItem = False else: newdict[level] = tdict @@ -7900,19 +8545,23 @@ def get(policy_class=None, return_full_policy_names=True, if tdict: class_vals = dictupdate.update(class_vals, tdict) else: - msg = 'The specified policy {0} is not currently available ' \ - 'to be configured via this module' + msg = ( + "The specified policy {0} is not currently available " + "to be configured via this module" + ) raise CommandExecutionError(msg.format(policy_name)) class_vals = dictupdate.update( class_vals, - _checkAllAdmxPolicies(policy_class=p_class, - adml_language=adml_language, - return_full_policy_names=return_full_policy_names, - hierarchical_return=hierarchical_return, - return_not_configured=return_not_configured)) - if _policydata.policies[p_class]['lgpo_section'] not in class_vals: - temp_dict = { - _policydata.policies[p_class]['lgpo_section']: class_vals} + _checkAllAdmxPolicies( + policy_class=p_class, + adml_language=adml_language, + return_full_policy_names=return_full_policy_names, + hierarchical_return=hierarchical_return, + return_not_configured=return_not_configured, + ), + ) + if _policydata.policies[p_class]["lgpo_section"] not in class_vals: + temp_dict = {_policydata.policies[p_class]["lgpo_section"]: class_vals} class_vals = temp_dict vals = dictupdate.update(vals, class_vals) @@ -7920,7 +8569,7 @@ def get(policy_class=None, return_full_policy_names=True, def _get_policy_info_setting(policy_definition): - ''' + """ Some policies are defined in this module and others by the ADMX/ADML files on the machine. This function loads the current values for policies defined in this module. @@ -7940,67 +8589,79 @@ def _get_policy_info_setting(policy_definition): policy_name = 'RemoteRegistryExactPaths' policy_definition = policy_data.policies['Machine']['policies'][policy_name] policy_value = _get_policy_info_setting(policy_definition) - ''' - if 'Registry' in policy_definition: + """ + if "Registry" in policy_definition: # Get value using the Registry mechanism - value = __utils__['reg.read_value']( - policy_definition['Registry']['Hive'], - policy_definition['Registry']['Path'], - policy_definition['Registry']['Value'])['vdata'] - log.trace('Value %r found for Regisry policy %s', - value, policy_definition['Policy']) - elif 'Secedit' in policy_definition: + value = __utils__["reg.read_value"]( + policy_definition["Registry"]["Hive"], + policy_definition["Registry"]["Path"], + policy_definition["Registry"]["Value"], + )["vdata"] + log.trace( + "Value %r found for Regisry policy %s", value, policy_definition["Policy"] + ) + elif "Secedit" in policy_definition: # Get value using the Secedit mechanism - value = _get_secedit_value( - option=policy_definition['Secedit']['Option']) - log.trace('Value %r found for Secedit policy %s', - value, policy_definition['Policy']) - elif 'NetSH' in policy_definition: + value = _get_secedit_value(option=policy_definition["Secedit"]["Option"]) + log.trace( + "Value %r found for Secedit policy %s", value, policy_definition["Policy"] + ) + elif "NetSH" in policy_definition: # Get value using the NetSH mechanism value = _get_netsh_value( - profile=policy_definition['NetSH']['Profile'], - option=policy_definition['NetSH']['Option']) - log.trace('Value %r found for NetSH policy %s', - value, policy_definition['Policy']) - elif 'AdvAudit' in policy_definition: + profile=policy_definition["NetSH"]["Profile"], + option=policy_definition["NetSH"]["Option"], + ) + log.trace( + "Value %r found for NetSH policy %s", value, policy_definition["Policy"] + ) + elif "AdvAudit" in policy_definition: # Get value using the AuditPol mechanism - value = _get_advaudit_value( - option=policy_definition['AdvAudit']['Option']) - log.trace('Value %r found for AuditPol policy %s', - value, policy_definition['Policy']) - elif 'NetUserModal' in policy_definition: + value = _get_advaudit_value(option=policy_definition["AdvAudit"]["Option"]) + log.trace( + "Value %r found for AuditPol policy %s", value, policy_definition["Policy"] + ) + elif "NetUserModal" in policy_definition: # Get value using the NetUserModal mechanism modal_return = win32net.NetUserModalsGet( - None, policy_definition['NetUserModal']['Modal']) - value = modal_return[ - policy_definition['NetUserModal']['Option']] - log.trace('Value %r found for NetUserModal policy %s', - value, policy_definition['Policy']) - elif 'LsaRights' in policy_definition: + None, policy_definition["NetUserModal"]["Modal"] + ) + value = modal_return[policy_definition["NetUserModal"]["Option"]] + log.trace( + "Value %r found for NetUserModal policy %s", + value, + policy_definition["Policy"], + ) + elif "LsaRights" in policy_definition: # Get value using the LSARights mechanism - value = _getRightsAssignments(policy_definition['LsaRights']['Option']) - log.trace('Value %r found for LSARights policy %s', - value, policy_definition['Policy']) - elif 'ScriptIni' in policy_definition: + value = _getRightsAssignments(policy_definition["LsaRights"]["Option"]) + log.trace( + "Value %r found for LSARights policy %s", value, policy_definition["Policy"] + ) + elif "ScriptIni" in policy_definition: value = _getScriptSettingsFromIniFile(policy_definition) - log.trace('Value %r found for ScriptIni policy %s', - value, policy_definition['Policy']) + log.trace( + "Value %r found for ScriptIni policy %s", value, policy_definition["Policy"] + ) else: - message = 'Unknown or missing mechanism in policy_definition\n' \ - '{0}'.format(policy_definition) + message = "Unknown or missing mechanism in policy_definition\n" "{0}".format( + policy_definition + ) raise CommandExecutionError(message) - value = _transform_value(value=value, - policy=policy_definition, - transform_type='Get') + value = _transform_value( + value=value, policy=policy_definition, transform_type="Get" + ) return value -def _get_policy_adm_setting(admx_policy, - policy_class, - adml_language='en-US', - return_full_policy_names=False, - hierarchical_return=False): - ''' +def _get_policy_adm_setting( + admx_policy, + policy_class, + adml_language="en-US", + return_full_policy_names=False, + hierarchical_return=False, +): + """ Get the current setting for polices set via the policy templates (ADMX/ADML) files @@ -8042,20 +8703,22 @@ def _get_policy_adm_setting(admx_policy, return_full_policy_names=return_full_policy_names, hierarchical_return=hierarchical_return ) - ''' + """ # TODO: Need to figure out how to get the lgpo.get function to use this code # TODO: as it is very similar # Validate policy Key and Name attributes - this_key = admx_policy.attrib.get('key', None) - this_policy_name = admx_policy.attrib.get('name', None) + this_key = admx_policy.attrib.get("key", None) + this_policy_name = admx_policy.attrib.get("name", None) if this_key is None or this_policy_name is None: - msg = 'Policy is missing the required "key" or "name" attribute:\n' \ - '{0}'.format(admx_policy.attrib) + msg = ( + 'Policy is missing the required "key" or "name" attribute:\n' + "{0}".format(admx_policy.attrib) + ) raise CommandExecutionError(msg) # Get additional settings - this_value_name = admx_policy.attrib.get('valueName', None) - this_policy_setting = 'Not Configured' + this_value_name = admx_policy.attrib.get("valueName", None) + this_policy_setting = "Not Configured" this_policy_namespace = admx_policy.nsmap[admx_policy.prefix] # Set some default values, these will get flipped below @@ -8065,100 +8728,139 @@ def _get_policy_adm_setting(admx_policy, # Load additional data policy_data = _policy_info() policy_file_data = _read_regpol_file( - policy_data.admx_registry_classes[policy_class]['policy_path']) + policy_data.admx_registry_classes[policy_class]["policy_path"] + ) adml_policy_resources = _get_policy_resources(language=adml_language) policy_vals = {} - if ENABLED_VALUE_XPATH(admx_policy) and this_policy_setting == 'Not Configured': + if ENABLED_VALUE_XPATH(admx_policy) and this_policy_setting == "Not Configured": # some policies have a disabled list but not an enabled list # added this to address those issues if DISABLED_LIST_XPATH(admx_policy) or DISABLED_VALUE_XPATH(admx_policy): element_only_enabled_disabled = False explicit_enable_disable_value_setting = True - if _checkValueItemParent(policy_element=admx_policy, - policy_name=this_policy_name, - policy_key=this_key, - policy_valueName=this_value_name, - xpath_object=ENABLED_VALUE_XPATH, - policy_file_data=policy_file_data): - log.trace('%s is enabled by detected ENABLED_VALUE_XPATH', - this_policy_name) - this_policy_setting = 'Enabled' - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting - if DISABLED_VALUE_XPATH(admx_policy) and this_policy_setting == 'Not Configured': + if _checkValueItemParent( + policy_element=admx_policy, + policy_name=this_policy_name, + policy_key=this_key, + policy_valueName=this_value_name, + xpath_object=ENABLED_VALUE_XPATH, + policy_file_data=policy_file_data, + ): + log.trace( + "%s is enabled by detected ENABLED_VALUE_XPATH", this_policy_name + ) + this_policy_setting = "Enabled" + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = this_policy_setting + if DISABLED_VALUE_XPATH(admx_policy) and this_policy_setting == "Not Configured": # some policies have a disabled list but not an enabled list # added this to address those issues if ENABLED_LIST_XPATH(admx_policy) or ENABLED_VALUE_XPATH(admx_policy): element_only_enabled_disabled = False explicit_enable_disable_value_setting = True - if _checkValueItemParent(policy_element=admx_policy, - policy_name=this_policy_name, - policy_key=this_key, - policy_valueName=this_value_name, - xpath_object=DISABLED_VALUE_XPATH, - policy_file_data=policy_file_data): - log.trace('%s is disabled by detected DISABLED_VALUE_XPATH', - this_policy_name) - this_policy_setting = 'Disabled' - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + if _checkValueItemParent( + policy_element=admx_policy, + policy_name=this_policy_name, + policy_key=this_key, + policy_valueName=this_value_name, + xpath_object=DISABLED_VALUE_XPATH, + policy_file_data=policy_file_data, + ): + log.trace( + "%s is disabled by detected DISABLED_VALUE_XPATH", this_policy_name + ) + this_policy_setting = "Disabled" + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = this_policy_setting if ENABLED_LIST_XPATH(admx_policy): if DISABLED_LIST_XPATH(admx_policy) or DISABLED_VALUE_XPATH(admx_policy): element_only_enabled_disabled = False explicit_enable_disable_value_setting = True - if _checkListItem(policy_element=admx_policy, - policy_name=this_policy_name, - policy_key=this_key, - xpath_object=ENABLED_LIST_XPATH, - policy_file_data=policy_file_data): - log.trace('%s is enabled by detected ENABLED_LIST_XPATH', - this_policy_name) - this_policy_setting = 'Enabled' - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + if _checkListItem( + policy_element=admx_policy, + policy_name=this_policy_name, + policy_key=this_key, + xpath_object=ENABLED_LIST_XPATH, + policy_file_data=policy_file_data, + ): + log.trace( + "%s is enabled by detected ENABLED_LIST_XPATH", this_policy_name + ) + this_policy_setting = "Enabled" + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = this_policy_setting if DISABLED_LIST_XPATH(admx_policy): if ENABLED_LIST_XPATH(admx_policy) or ENABLED_VALUE_XPATH(admx_policy): element_only_enabled_disabled = False explicit_enable_disable_value_setting = True - if _checkListItem(policy_element=admx_policy, - policy_name=this_policy_name, - policy_key=this_key, - xpath_object=DISABLED_LIST_XPATH, - policy_file_data=policy_file_data): - log.trace('%s is disabled by detected DISABLED_LIST_XPATH', - this_policy_name) - this_policy_setting = 'Disabled' - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + if _checkListItem( + policy_element=admx_policy, + policy_name=this_policy_name, + policy_key=this_key, + xpath_object=DISABLED_LIST_XPATH, + policy_file_data=policy_file_data, + ): + log.trace( + "%s is disabled by detected DISABLED_LIST_XPATH", this_policy_name + ) + this_policy_setting = "Disabled" + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = this_policy_setting if not explicit_enable_disable_value_setting and this_value_name: # the policy has a key/valuename but no explicit Enabled/Disabled # Value or List # these seem to default to a REG_DWORD 1 = "Enabled" **del. = "Disabled" - if _regexSearchRegPolData(re.escape(_buildKnownDataSearchString( + if _regexSearchRegPolData( + re.escape( + _buildKnownDataSearchString( reg_key=this_key, reg_valueName=this_value_name, - reg_vtype='REG_DWORD', - reg_data='1')), - policy_file_data): - log.trace('%s is enabled by no explicit enable/disable list or ' - 'value', this_policy_name) - this_policy_setting = 'Enabled' - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting - elif _regexSearchRegPolData(re.escape(_buildKnownDataSearchString( + reg_vtype="REG_DWORD", + reg_data="1", + ) + ), + policy_file_data, + ): + log.trace( + "%s is enabled by no explicit enable/disable list or " "value", + this_policy_name, + ) + this_policy_setting = "Enabled" + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = this_policy_setting + elif _regexSearchRegPolData( + re.escape( + _buildKnownDataSearchString( reg_key=this_key, reg_valueName=this_value_name, - reg_vtype='REG_DWORD', + reg_vtype="REG_DWORD", reg_data=None, - check_deleted=True)), - policy_file_data): - log.trace('%s is disabled by no explicit enable/disable list or ' - 'value', this_policy_name) - this_policy_setting = 'Disabled' - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + check_deleted=True, + ) + ), + policy_file_data, + ): + log.trace( + "%s is disabled by no explicit enable/disable list or " "value", + this_policy_name, + ) + this_policy_setting = "Disabled" + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = this_policy_setting full_names = {} hierarchy = {} if ELEMENTS_XPATH(admx_policy): - if element_only_enabled_disabled or this_policy_setting == 'Enabled': + if element_only_enabled_disabled or this_policy_setting == "Enabled": # TODO does this need to be modified based on the 'required' attribute? required_elements = {} configured_elements = {} @@ -8167,183 +8869,168 @@ def _get_policy_adm_setting(admx_policy, for child_item in elements_item.getchildren(): this_element_name = _getFullPolicyName( policy_item=child_item, - policy_name=child_item.attrib['id'], + policy_name=child_item.attrib["id"], return_full_policy_names=return_full_policy_names, - adml_language=adml_language) + adml_language=adml_language, + ) required_elements[this_element_name] = None - child_key = child_item.attrib.get('key', this_key) - child_value_name = child_item.attrib.get('valueName', - this_value_name) - if etree.QName(child_item).localname == 'boolean': + child_key = child_item.attrib.get("key", this_key) + child_value_name = child_item.attrib.get( + "valueName", this_value_name + ) + if etree.QName(child_item).localname == "boolean": # https://msdn.microsoft.com/en-us/library/dn605978(v=vs.85).aspx if child_item.getchildren(): - if TRUE_VALUE_XPATH(child_item) and this_element_name not in configured_elements: + if ( + TRUE_VALUE_XPATH(child_item) + and this_element_name not in configured_elements + ): if _checkValueItemParent( - policy_element=child_item, - policy_name=this_policy_name, - policy_key=child_key, - policy_valueName=child_value_name, - xpath_object=TRUE_VALUE_XPATH, - policy_file_data=policy_file_data): + policy_element=child_item, + policy_name=this_policy_name, + policy_key=child_key, + policy_valueName=child_value_name, + xpath_object=TRUE_VALUE_XPATH, + policy_file_data=policy_file_data, + ): configured_elements[this_element_name] = True - log.trace('element %s is configured true', - child_item.attrib['id']) - if FALSE_VALUE_XPATH(child_item) and this_element_name not in configured_elements: + log.trace( + "element %s is configured true", + child_item.attrib["id"], + ) + if ( + FALSE_VALUE_XPATH(child_item) + and this_element_name not in configured_elements + ): if _checkValueItemParent( - policy_element=child_item, - policy_name=this_policy_name, - policy_key=child_key, - policy_valueName=child_value_name, - xpath_object=FALSE_VALUE_XPATH, - policy_file_data=policy_file_data): + policy_element=child_item, + policy_name=this_policy_name, + policy_key=child_key, + policy_valueName=child_value_name, + xpath_object=FALSE_VALUE_XPATH, + policy_file_data=policy_file_data, + ): configured_elements[this_element_name] = False - policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element %s is configured false', - child_item.attrib['id']) + policy_disabled_elements = ( + policy_disabled_elements + 1 + ) + log.trace( + "element %s is configured false", + child_item.attrib["id"], + ) # WARNING - no standard ADMX files use true/falseList # so this hasn't actually been tested - if TRUE_LIST_XPATH(child_item) and this_element_name not in configured_elements: - log.trace('checking trueList') + if ( + TRUE_LIST_XPATH(child_item) + and this_element_name not in configured_elements + ): + log.trace("checking trueList") if _checkListItem( - policy_element=child_item, - policy_name=this_policy_name, - policy_key=this_key, - xpath_object=TRUE_LIST_XPATH, - policy_file_data=policy_file_data): + policy_element=child_item, + policy_name=this_policy_name, + policy_key=this_key, + xpath_object=TRUE_LIST_XPATH, + policy_file_data=policy_file_data, + ): configured_elements[this_element_name] = True - log.trace('element %s is configured true', - child_item.attrib['id']) - if FALSE_LIST_XPATH(child_item) and this_element_name not in configured_elements: - log.trace('checking falseList') + log.trace( + "element %s is configured true", + child_item.attrib["id"], + ) + if ( + FALSE_LIST_XPATH(child_item) + and this_element_name not in configured_elements + ): + log.trace("checking falseList") if _checkListItem( - policy_element=child_item, - policy_name=this_policy_name, - policy_key=this_key, - xpath_object=FALSE_LIST_XPATH, - policy_file_data=policy_file_data): + policy_element=child_item, + policy_name=this_policy_name, + policy_key=this_key, + xpath_object=FALSE_LIST_XPATH, + policy_file_data=policy_file_data, + ): configured_elements[this_element_name] = False - policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element %s is configured false', - child_item.attrib['id']) + policy_disabled_elements = ( + policy_disabled_elements + 1 + ) + log.trace( + "element %s is configured false", + child_item.attrib["id"], + ) else: if _regexSearchRegPolData( - re.escape(_processValueItem( - element=child_item, - reg_key=child_key, - reg_valuename=child_value_name, - policy=admx_policy, - parent_element=elements_item, - check_deleted=True)), - policy_file_data): - configured_elements[this_element_name] = False - policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element %s is configured false', child_item.attrib['id']) - elif _regexSearchRegPolData( - re.escape(_processValueItem( - element=child_item, - reg_key=child_key, - reg_valuename=child_value_name, - policy=admx_policy, - parent_element=elements_item, - check_deleted=False)), - policy_file_data): - configured_elements[this_element_name] = True - log.trace('element %s is configured true', - child_item.attrib['id']) - elif etree.QName(child_item).localname in [ - 'decimal', 'text', 'longDecimal', 'multiText']: - # https://msdn.microsoft.com/en-us/library/dn605987(v=vs.85).aspx - if _regexSearchRegPolData( - re.escape(_processValueItem( - element=child_item, - reg_key=child_key, - reg_valuename=child_value_name, - policy=admx_policy, - parent_element=elements_item, - check_deleted=True)), - policy_file_data): - configured_elements[this_element_name] = 'Disabled' - policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element %s is disabled', - child_item.attrib['id']) - elif _regexSearchRegPolData( - re.escape(_processValueItem( - element=child_item, - reg_key=child_key, - reg_valuename=child_value_name, - policy=admx_policy, - parent_element=elements_item, - check_deleted=False)), - policy_data=policy_file_data): - configured_value = _getDataFromRegPolData( + re.escape( _processValueItem( element=child_item, reg_key=child_key, reg_valuename=child_value_name, policy=admx_policy, parent_element=elements_item, - check_deleted=False), - policy_data=policy_file_data) - configured_elements[this_element_name] = configured_value - log.trace('element %s is enabled, value == %s', - child_item.attrib['id'], - configured_value) - elif etree.QName(child_item).localname == 'enum': + check_deleted=True, + ) + ), + policy_file_data, + ): + configured_elements[this_element_name] = False + policy_disabled_elements = policy_disabled_elements + 1 + log.trace( + "element %s is configured false", + child_item.attrib["id"], + ) + elif _regexSearchRegPolData( + re.escape( + _processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=False, + ) + ), + policy_file_data, + ): + configured_elements[this_element_name] = True + log.trace( + "element %s is configured true", + child_item.attrib["id"], + ) + elif etree.QName(child_item).localname in [ + "decimal", + "text", + "longDecimal", + "multiText", + ]: + # https://msdn.microsoft.com/en-us/library/dn605987(v=vs.85).aspx if _regexSearchRegPolData( - re.escape(_processValueItem( + re.escape( + _processValueItem( element=child_item, reg_key=child_key, reg_valuename=child_value_name, policy=admx_policy, parent_element=elements_item, - check_deleted=True)), - policy_file_data): - log.trace('enum element %s is disabled', - child_item.attrib['id']) - configured_elements[this_element_name] = 'Disabled' + check_deleted=True, + ) + ), + policy_file_data, + ): + configured_elements[this_element_name] = "Disabled" policy_disabled_elements = policy_disabled_elements + 1 - else: - for enum_item in child_item.getchildren(): - if _checkValueItemParent( - policy_element=enum_item, - policy_name=child_item.attrib['id'], - policy_key=child_key, - policy_valueName=child_value_name, - xpath_object=VALUE_XPATH, - policy_file_data=policy_file_data): - if VALUE_LIST_XPATH(enum_item): - log.trace('enum item has a valueList') - if _checkListItem( - policy_element=enum_item, - policy_name=this_policy_name, - policy_key=child_key, - xpath_object=VALUE_LIST_XPATH, - policy_file_data=policy_file_data): - log.trace('all valueList items exist in file') - configured_elements[this_element_name] = _getAdmlDisplayName( - adml_xml_data=adml_policy_resources, - display_name=enum_item.attrib['displayName']) - break - else: - configured_elements[this_element_name] = _getAdmlDisplayName( - adml_xml_data=adml_policy_resources, - display_name=enum_item.attrib['displayName']) - break - elif etree.QName(child_item).localname == 'list': - return_value_name = False - if 'explicitValue' in child_item.attrib \ - and child_item.attrib['explicitValue'].lower() == 'true': - log.trace('explicitValue list, we will return value names') - return_value_name = True - if _regexSearchRegPolData( - re.escape(_processValueItem( + log.trace("element %s is disabled", child_item.attrib["id"]) + elif _regexSearchRegPolData( + re.escape( + _processValueItem( element=child_item, reg_key=child_key, reg_valuename=child_value_name, policy=admx_policy, parent_element=elements_item, - check_deleted=False)) + salt.utils.stringutils.to_bytes(r'(?!\*\*delvals\.)'), - policy_data=policy_file_data): + check_deleted=False, + ) + ), + policy_data=policy_file_data, + ): configured_value = _getDataFromRegPolData( _processValueItem( element=child_item, @@ -8351,52 +9038,186 @@ def _get_policy_adm_setting(admx_policy, reg_valuename=child_value_name, policy=admx_policy, parent_element=elements_item, - check_deleted=False), + check_deleted=False, + ), policy_data=policy_file_data, - return_value_name=return_value_name) + ) configured_elements[this_element_name] = configured_value - log.trace('element %s is enabled values: %s', - child_item.attrib['id'], - configured_value) - elif _regexSearchRegPolData( - re.escape(_processValueItem( + log.trace( + "element %s is enabled, value == %s", + child_item.attrib["id"], + configured_value, + ) + elif etree.QName(child_item).localname == "enum": + if _regexSearchRegPolData( + re.escape( + _processValueItem( element=child_item, reg_key=child_key, reg_valuename=child_value_name, policy=admx_policy, parent_element=elements_item, - check_deleted=True)), - policy_file_data): + check_deleted=True, + ) + ), + policy_file_data, + ): + log.trace( + "enum element %s is disabled", child_item.attrib["id"] + ) configured_elements[this_element_name] = "Disabled" policy_disabled_elements = policy_disabled_elements + 1 - log.trace('element {0} is disabled'.format(child_item.attrib['id'])) + else: + for enum_item in child_item.getchildren(): + if _checkValueItemParent( + policy_element=enum_item, + policy_name=child_item.attrib["id"], + policy_key=child_key, + policy_valueName=child_value_name, + xpath_object=VALUE_XPATH, + policy_file_data=policy_file_data, + ): + if VALUE_LIST_XPATH(enum_item): + log.trace("enum item has a valueList") + if _checkListItem( + policy_element=enum_item, + policy_name=this_policy_name, + policy_key=child_key, + xpath_object=VALUE_LIST_XPATH, + policy_file_data=policy_file_data, + ): + log.trace( + "all valueList items exist in file" + ) + configured_elements[ + this_element_name + ] = _getAdmlDisplayName( + adml_xml_data=adml_policy_resources, + display_name=enum_item.attrib[ + "displayName" + ], + ) + break + else: + configured_elements[ + this_element_name + ] = _getAdmlDisplayName( + adml_xml_data=adml_policy_resources, + display_name=enum_item.attrib[ + "displayName" + ], + ) + break + elif etree.QName(child_item).localname == "list": + return_value_name = False + if ( + "explicitValue" in child_item.attrib + and child_item.attrib["explicitValue"].lower() == "true" + ): + log.trace("explicitValue list, we will return value names") + return_value_name = True + if _regexSearchRegPolData( + re.escape( + _processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=False, + ) + ) + + salt.utils.stringutils.to_bytes(r"(?!\*\*delvals\.)"), + policy_data=policy_file_data, + ): + configured_value = _getDataFromRegPolData( + _processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=False, + ), + policy_data=policy_file_data, + return_value_name=return_value_name, + ) + configured_elements[this_element_name] = configured_value + log.trace( + "element %s is enabled values: %s", + child_item.attrib["id"], + configured_value, + ) + elif _regexSearchRegPolData( + re.escape( + _processValueItem( + element=child_item, + reg_key=child_key, + reg_valuename=child_value_name, + policy=admx_policy, + parent_element=elements_item, + check_deleted=True, + ) + ), + policy_file_data, + ): + configured_elements[this_element_name] = "Disabled" + policy_disabled_elements = policy_disabled_elements + 1 + log.trace( + "element {0} is disabled".format( + child_item.attrib["id"] + ) + ) if element_only_enabled_disabled: - if len(required_elements.keys()) > 0 and len(configured_elements.keys()) == len(required_elements.keys()): + if len(required_elements.keys()) > 0 and len( + configured_elements.keys() + ) == len(required_elements.keys()): if policy_disabled_elements == len(required_elements.keys()): - log.trace('{0} is disabled by all enum elements'.format(this_policy_name)) - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = 'Disabled' + log.trace( + "{0} is disabled by all enum elements".format( + this_policy_name + ) + ) + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = "Disabled" else: - log.trace('{0} is enabled by enum elements'.format(this_policy_name)) - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = configured_elements + log.trace( + "{0} is enabled by enum elements".format(this_policy_name) + ) + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = configured_elements else: - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = this_policy_setting else: - if this_policy_setting == 'Enabled': - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = configured_elements + if this_policy_setting == "Enabled": + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = configured_elements else: - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = this_policy_setting else: - policy_vals.setdefault(this_policy_namespace, {})[this_policy_name] = this_policy_setting + policy_vals.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = this_policy_setting - if return_full_policy_names and \ - this_policy_namespace in policy_vals and \ - this_policy_name in policy_vals[this_policy_namespace]: + if ( + return_full_policy_names + and this_policy_namespace in policy_vals + and this_policy_name in policy_vals[this_policy_namespace] + ): full_names.setdefault(this_policy_namespace, {}) full_names[this_policy_namespace][this_policy_name] = _getFullPolicyName( policy_item=admx_policy, - policy_name=admx_policy.attrib['name'], + policy_name=admx_policy.attrib["name"], return_full_policy_names=return_full_policy_names, - adml_language=adml_language) + adml_language=adml_language, + ) # Make sure the we're passing the full policy name # This issue was found when setting the `Allow Telemetry` setting # All following states would show a change in this setting @@ -8405,18 +9226,25 @@ def _get_policy_adm_setting(admx_policy, # This makes sure we're always returning the full_name when required if this_policy_name in policy_vals[this_policy_namespace][this_policy_name]: full_name = full_names[this_policy_namespace][this_policy_name] - setting = policy_vals[this_policy_namespace][this_policy_name].pop(this_policy_name) + setting = policy_vals[this_policy_namespace][this_policy_name].pop( + this_policy_name + ) policy_vals[this_policy_namespace][this_policy_name][full_name] = setting - if this_policy_namespace in policy_vals and \ - this_policy_name in policy_vals[this_policy_namespace]: - hierarchy.setdefault(this_policy_namespace, {})[this_policy_name] = _build_parent_list( + if ( + this_policy_namespace in policy_vals + and this_policy_name in policy_vals[this_policy_namespace] + ): + hierarchy.setdefault(this_policy_namespace, {})[ + this_policy_name + ] = _build_parent_list( policy_definition=admx_policy, return_full_policy_names=return_full_policy_names, - adml_language=adml_language) + adml_language=adml_language, + ) if policy_vals and return_full_policy_names and not hierarchical_return: - log.debug('Compiling non hierarchical return...') + log.debug("Compiling non hierarchical return...") unpathed_dict = {} pathed_dict = {} for policy_namespace in list(policy_vals): @@ -8427,19 +9255,29 @@ def _get_policy_adm_setting(admx_policy, full_path_list = hierarchy[policy_namespace][policy_item] full_path_list.reverse() full_path_list.append(full_names[policy_namespace][policy_item]) - policy_vals['\\'.join(full_path_list)] = policy_vals[policy_namespace].pop(policy_item) + policy_vals["\\".join(full_path_list)] = policy_vals[ + policy_namespace + ].pop(policy_item) pathed_dict[full_name] = True else: - policy_vals[policy_namespace][full_name] = policy_vals[policy_namespace].pop(policy_item) - unpathed_dict.setdefault(policy_namespace, {})[full_name] = policy_item + policy_vals[policy_namespace][full_name] = policy_vals[ + policy_namespace + ].pop(policy_item) + unpathed_dict.setdefault(policy_namespace, {})[ + full_name + ] = policy_item # go back and remove any "unpathed" policies that need a full path for path_needed in unpathed_dict[policy_namespace]: # remove the item with the same full name and re-add it w/a path'd version - full_path_list = hierarchy[policy_namespace][unpathed_dict[policy_namespace][path_needed]] + full_path_list = hierarchy[policy_namespace][ + unpathed_dict[policy_namespace][path_needed] + ] full_path_list.reverse() full_path_list.append(path_needed) - log.trace('full_path_list == %s', full_path_list) - policy_vals['\\'.join(full_path_list)] = policy_vals[policy_namespace].pop(path_needed) + log.trace("full_path_list == %s", full_path_list) + policy_vals["\\".join(full_path_list)] = policy_vals[ + policy_namespace + ].pop(path_needed) for policy_namespace in list(policy_vals): # Remove empty entries @@ -8447,14 +9285,13 @@ def _get_policy_adm_setting(admx_policy, policy_vals.pop(policy_namespace) # Remove namespace and keep the values elif isinstance(policy_vals[policy_namespace], dict): - if this_policy_namespace == policy_namespace and \ - not hierarchical_return: + if this_policy_namespace == policy_namespace and not hierarchical_return: policy_vals.update(policy_vals[policy_namespace]) policy_vals.pop(policy_namespace) if policy_vals and hierarchical_return: if hierarchy: - log.debug('Compiling hierarchical return...') + log.debug("Compiling hierarchical return...") for policy_namespace in hierarchy: for hierarchy_item in hierarchy[policy_namespace]: if hierarchy_item in policy_vals[policy_namespace]: @@ -8465,30 +9302,43 @@ def _get_policy_adm_setting(admx_policy, if first_item: h_policy_name = hierarchy_item if return_full_policy_names: - h_policy_name = full_names[policy_namespace][hierarchy_item] - new_dict[item] = {h_policy_name: policy_vals[policy_namespace].pop(hierarchy_item)} + h_policy_name = full_names[policy_namespace][ + hierarchy_item + ] + new_dict[item] = { + h_policy_name: policy_vals[policy_namespace].pop( + hierarchy_item + ) + } first_item = False else: new_dict[item] = t_dict t_dict = new_dict if t_dict: policy_vals = dictupdate.update(policy_vals, t_dict) - if policy_namespace in policy_vals and policy_vals[policy_namespace] == {}: + if ( + policy_namespace in policy_vals + and policy_vals[policy_namespace] == {} + ): policy_vals.pop(policy_namespace) policy_vals = { - policy_data.admx_registry_classes[policy_class]['lgpo_section']: { - 'Administrative Templates': policy_vals}} + policy_data.admx_registry_classes[policy_class]["lgpo_section"]: { + "Administrative Templates": policy_vals + } + } return policy_vals -def get_policy(policy_name, - policy_class, - adml_language='en-US', - return_value_only=True, - return_full_policy_names=True, - hierarchical_return=False): - r''' +def get_policy( + policy_name, + policy_class, + adml_language="en-US", + return_value_only=True, + return_full_policy_names=True, + hierarchical_return=False, +): + r""" Get the current settings for a single policy on the machine Args: @@ -8537,45 +9387,49 @@ def get_policy(policy_name, # Using full path and name salt * lgpo.get_policy "Windows Components\Windows Update\Configure Automatic Updates" machine - ''' + """ if not policy_name: - raise SaltInvocationError('policy_name must be defined') + raise SaltInvocationError("policy_name must be defined") if not policy_class: - raise SaltInvocationError('policy_class must be defined') + raise SaltInvocationError("policy_class must be defined") policy_class = policy_class.title() policy_data = _policy_info() if policy_class not in policy_data.policies.keys(): - policy_classes = ', '.join(policy_data.policies.keys()) - message = 'The requested policy class "{0}" is invalid, policy_class ' \ - 'should be one of: {1}'.format(policy_class, policy_classes) + policy_classes = ", ".join(policy_data.policies.keys()) + message = ( + 'The requested policy class "{0}" is invalid, policy_class ' + "should be one of: {1}".format(policy_class, policy_classes) + ) raise CommandExecutionError(message) # Look in the _policy_data object first policy_definition = None - if policy_name in policy_data.policies[policy_class]['policies']: - policy_definition = policy_data.policies[policy_class]['policies'][policy_name] + if policy_name in policy_data.policies[policy_class]["policies"]: + policy_definition = policy_data.policies[policy_class]["policies"][policy_name] else: - for pol in policy_data.policies[policy_class]['policies']: - if policy_data.policies[policy_class]['policies'][pol]['Policy'].lower() == policy_name.lower(): - policy_definition = policy_data.policies[policy_class]['policies'][pol] + for pol in policy_data.policies[policy_class]["policies"]: + if ( + policy_data.policies[policy_class]["policies"][pol]["Policy"].lower() + == policy_name.lower() + ): + policy_definition = policy_data.policies[policy_class]["policies"][pol] break if policy_definition: if return_value_only: return _get_policy_info_setting(policy_definition) if return_full_policy_names: - key_name = policy_definition['Policy'] + key_name = policy_definition["Policy"] else: key_name = policy_name setting = {key_name: _get_policy_info_setting(policy_definition)} if hierarchical_return: - if 'lgpo_section' in policy_definition: + if "lgpo_section" in policy_definition: first_item = True t_dict = {} - for level in reversed(policy_definition['lgpo_section']): + for level in reversed(policy_definition["lgpo_section"]): new_dict = {} if first_item: - new_dict[level] = { - key_name: setting.pop(key_name)} + new_dict[level] = {key_name: setting.pop(key_name)} first_item = False else: new_dict[level] = t_dict @@ -8586,16 +9440,15 @@ def get_policy(policy_name, return setting success, policy_obj, _, _ = _lookup_admin_template( - policy_name=policy_name, - policy_class=policy_class, - adml_language=adml_language) + policy_name=policy_name, policy_class=policy_class, adml_language=adml_language + ) if success: setting = _get_policy_adm_setting( admx_policy=policy_obj, policy_class=policy_class, adml_language=adml_language, return_full_policy_names=return_full_policy_names, - hierarchical_return=hierarchical_return + hierarchical_return=hierarchical_return, ) if return_value_only: for key in setting: @@ -8603,11 +9456,10 @@ def get_policy(policy_name, return setting -def set_computer_policy(name, - setting, - cumulative_rights_assignments=True, - adml_language='en-US'): - ''' +def set_computer_policy( + name, setting, cumulative_rights_assignments=True, adml_language="en-US" +): + """ Set a single computer policy Args: @@ -8635,20 +9487,20 @@ def set_computer_policy(name, .. code-block:: bash salt '*' lgpo.set_computer_policy LockoutDuration 1440 - ''' + """ pol = {} pol[name] = setting - ret = set_(computer_policy=pol, - user_policy=None, - cumulative_rights_assignments=cumulative_rights_assignments, - adml_language=adml_language) + ret = set_( + computer_policy=pol, + user_policy=None, + cumulative_rights_assignments=cumulative_rights_assignments, + adml_language=adml_language, + ) return ret -def set_user_policy(name, - setting, - adml_language='en-US'): - ''' +def set_user_policy(name, setting, adml_language="en-US"): + """ Set a single user policy Args: @@ -8672,21 +9524,25 @@ def set_user_policy(name, .. code-block:: bash salt '*' lgpo.set_user_policy "Control Panel\\Display\\Disable the Display Control Panel" Enabled - ''' + """ pol = {} pol[name] = setting - ret = set_(user_policy=pol, - computer_policy=None, - cumulative_rights_assignments=True, - adml_language=adml_language) + ret = set_( + user_policy=pol, + computer_policy=None, + cumulative_rights_assignments=True, + adml_language=adml_language, + ) return ret -def set_(computer_policy=None, - user_policy=None, - cumulative_rights_assignments=True, - adml_language='en-US'): - ''' +def set_( + computer_policy=None, + user_policy=None, + cumulative_rights_assignments=True, + adml_language="en-US", +): + """ Set a local server policy. Args: @@ -8759,17 +9615,17 @@ def set_(computer_policy=None, .. code-block:: bash salt '*' lgpo.set computer_policy="{'LockoutDuration': 2, 'RestrictAnonymous': 'Enabled', 'AuditProcessTracking': 'Succes, Failure'}" - ''' + """ if computer_policy and not isinstance(computer_policy, dict): - msg = 'computer_policy must be specified as a dict' + msg = "computer_policy must be specified as a dict" raise SaltInvocationError(msg) if user_policy and not isinstance(user_policy, dict): - msg = 'user_policy must be specified as a dict' + msg = "user_policy must be specified as a dict" raise SaltInvocationError(msg) policies = {} - policies['User'] = user_policy - policies['Machine'] = computer_policy + policies["User"] = user_policy + policies["Machine"] = computer_policy if policies: adml_policy_resources = _get_policy_resources(language=adml_language) for p_class in policies: @@ -8785,253 +9641,497 @@ def set_(computer_policy=None, for policy_name in policies[p_class]: _pol = None policy_key_name = policy_name - if policy_name in _policydata.policies[p_class]['policies']: - _pol = _policydata.policies[p_class]['policies'][policy_name] + if policy_name in _policydata.policies[p_class]["policies"]: + _pol = _policydata.policies[p_class]["policies"][policy_name] else: - for policy in _policydata.policies[p_class]['policies']: - if _policydata.policies[p_class]['policies'][policy]['Policy'].upper() == \ - policy_name.upper(): - _pol = _policydata.policies[p_class]['policies'][policy] + for policy in _policydata.policies[p_class]["policies"]: + if ( + _policydata.policies[p_class]["policies"][policy][ + "Policy" + ].upper() + == policy_name.upper() + ): + _pol = _policydata.policies[p_class]["policies"][policy] policy_key_name = policy if _pol: # transform and validate the setting _value = _transform_value( value=policies[p_class][policy_name], - policy=_policydata.policies[p_class]['policies'][policy_key_name], - transform_type='Put') + policy=_policydata.policies[p_class]["policies"][ + policy_key_name + ], + transform_type="Put", + ) if not _validateSetting( - value=_value, - policy=_policydata.policies[p_class]['policies'][policy_key_name]): - msg = 'The specified value {0} is not an acceptable setting for policy {1}.' - raise SaltInvocationError(msg.format(policies[p_class][policy_name], policy_name)) - if 'Registry' in _pol: + value=_value, + policy=_policydata.policies[p_class]["policies"][ + policy_key_name + ], + ): + msg = "The specified value {0} is not an acceptable setting for policy {1}." + raise SaltInvocationError( + msg.format(policies[p_class][policy_name], policy_name) + ) + if "Registry" in _pol: # set value in registry - log.trace('%s is a registry policy', policy_name) - _regedits[policy_name] = {'policy': _pol, 'value': _value} - elif 'Secedit' in _pol: + log.trace("%s is a registry policy", policy_name) + _regedits[policy_name] = {"policy": _pol, "value": _value} + elif "Secedit" in _pol: # set value with secedit - log.trace('%s is a Secedit policy', policy_name) - if _pol['Secedit']['Section'] not in _secedits: - _secedits[_pol['Secedit']['Section']] = [] - _secedits[_pol['Secedit']['Section']].append( - ' '.join([_pol['Secedit']['Option'], - '=', six.text_type(_value)])) - elif 'NetSH' in _pol: + log.trace("%s is a Secedit policy", policy_name) + if _pol["Secedit"]["Section"] not in _secedits: + _secedits[_pol["Secedit"]["Section"]] = [] + _secedits[_pol["Secedit"]["Section"]].append( + " ".join( + [ + _pol["Secedit"]["Option"], + "=", + six.text_type(_value), + ] + ) + ) + elif "NetSH" in _pol: # set value with netsh - log.trace('%s is a NetSH policy', policy_name) - _netshs.setdefault(policy_name, { - 'profile': _pol['NetSH']['Profile'], - 'section': _pol['NetSH']['Section'], - 'option': _pol['NetSH']['Option'], - 'value': six.text_type(_value) - }) - elif 'AdvAudit' in _pol: + log.trace("%s is a NetSH policy", policy_name) + _netshs.setdefault( + policy_name, + { + "profile": _pol["NetSH"]["Profile"], + "section": _pol["NetSH"]["Section"], + "option": _pol["NetSH"]["Option"], + "value": six.text_type(_value), + }, + ) + elif "AdvAudit" in _pol: # set value with advaudit - _advaudits.setdefault(policy_name, { - 'option': _pol['AdvAudit']['Option'], - 'value': six.text_type(_value) - }) - elif 'NetUserModal' in _pol: + _advaudits.setdefault( + policy_name, + { + "option": _pol["AdvAudit"]["Option"], + "value": six.text_type(_value), + }, + ) + elif "NetUserModal" in _pol: # set value via NetUserModal - log.trace('%s is a NetUserModal policy', policy_name) - if _pol['NetUserModal']['Modal'] not in _modal_sets: - _modal_sets[_pol['NetUserModal']['Modal']] = {} - _modal_sets[_pol['NetUserModal']['Modal']][_pol['NetUserModal']['Option']] = _value - elif 'LsaRights' in _pol: - log.trace('%s is a LsaRights policy', policy_name) - _lsarights[policy_name] = {'policy': _pol, 'value': _value} + log.trace("%s is a NetUserModal policy", policy_name) + if _pol["NetUserModal"]["Modal"] not in _modal_sets: + _modal_sets[_pol["NetUserModal"]["Modal"]] = {} + _modal_sets[_pol["NetUserModal"]["Modal"]][ + _pol["NetUserModal"]["Option"] + ] = _value + elif "LsaRights" in _pol: + log.trace("%s is a LsaRights policy", policy_name) + _lsarights[policy_name] = {"policy": _pol, "value": _value} else: _value = policies[p_class][policy_name] log.trace('searching for "%s" in admx data', policy_name) - success, the_policy, policy_name_list, msg = _lookup_admin_template( - policy_name=policy_name, - policy_class=p_class, - adml_language=adml_language) + ( + success, + the_policy, + policy_name_list, + msg, + ) = _lookup_admin_template( + policy_name=policy_name, + policy_class=p_class, + adml_language=adml_language, + ) if success: - policy_name = the_policy.attrib['name'] + policy_name = the_policy.attrib["name"] policy_namespace = the_policy.nsmap[the_policy.prefix] if policy_namespace not in _admTemplateData: _admTemplateData[policy_namespace] = {} _admTemplateData[policy_namespace][policy_name] = _value else: raise SaltInvocationError(msg) - if policy_namespace and policy_name in _admTemplateData[policy_namespace] and the_policy is not None: - log.trace('setting == %s', six.text_type(_admTemplateData[policy_namespace][policy_name]).lower()) - log.trace(six.text_type(_admTemplateData[policy_namespace][policy_name]).lower()) - if six.text_type(_admTemplateData[policy_namespace][policy_name]).lower() != 'disabled' \ - and six.text_type(_admTemplateData[policy_namespace][policy_name]).lower() != 'not configured': + if ( + policy_namespace + and policy_name in _admTemplateData[policy_namespace] + and the_policy is not None + ): + log.trace( + "setting == %s", + six.text_type( + _admTemplateData[policy_namespace][policy_name] + ).lower(), + ) + log.trace( + six.text_type( + _admTemplateData[policy_namespace][policy_name] + ).lower() + ) + if ( + six.text_type( + _admTemplateData[policy_namespace][policy_name] + ).lower() + != "disabled" + and six.text_type( + _admTemplateData[policy_namespace][policy_name] + ).lower() + != "not configured" + ): if ELEMENTS_XPATH(the_policy): - if isinstance(_admTemplateData[policy_namespace][policy_name], dict): + if isinstance( + _admTemplateData[policy_namespace][policy_name], + dict, + ): for elements_item in ELEMENTS_XPATH(the_policy): - for child_item in elements_item.getchildren(): + for ( + child_item + ) in elements_item.getchildren(): # check each element - log.trace('checking element %s', child_item.attrib['id']) + log.trace( + "checking element %s", + child_item.attrib["id"], + ) temp_element_name = None this_element_name = _getFullPolicyName( policy_item=child_item, - policy_name=child_item.attrib['id'], + policy_name=child_item.attrib["id"], return_full_policy_names=True, - adml_language=adml_language) - log.trace('id attribute == "%s" this_element_name == "%s"', child_item.attrib['id'], this_element_name) - if this_element_name in _admTemplateData[policy_namespace][policy_name]: - temp_element_name = this_element_name - elif child_item.attrib['id'] in _admTemplateData[policy_namespace][policy_name]: - temp_element_name = child_item.attrib['id'] + adml_language=adml_language, + ) + log.trace( + 'id attribute == "%s" this_element_name == "%s"', + child_item.attrib["id"], + this_element_name, + ) + if ( + this_element_name + in _admTemplateData[ + policy_namespace + ][policy_name] + ): + temp_element_name = ( + this_element_name + ) + elif ( + child_item.attrib["id"] + in _admTemplateData[ + policy_namespace + ][policy_name] + ): + temp_element_name = child_item.attrib[ + "id" + ] else: - msg = ('Element "{0}" must be included' - ' in the policy configuration for policy {1}') - raise SaltInvocationError(msg.format(this_element_name, policy_name)) - if 'required' in child_item.attrib \ - and child_item.attrib['required'].lower() == 'true': - if not _admTemplateData[policy_namespace][policy_name][temp_element_name]: + msg = ( + 'Element "{0}" must be included' + " in the policy configuration for policy {1}" + ) + raise SaltInvocationError( + msg.format( + this_element_name, + policy_name, + ) + ) + if ( + "required" in child_item.attrib + and child_item.attrib[ + "required" + ].lower() + == "true" + ): + if not _admTemplateData[ + policy_namespace + ][policy_name][temp_element_name]: msg = 'Element "{0}" requires a value to be specified' - raise SaltInvocationError(msg.format(temp_element_name)) - if etree.QName(child_item).localname == 'boolean': + raise SaltInvocationError( + msg.format( + temp_element_name + ) + ) + if ( + etree.QName(child_item).localname + == "boolean" + ): if not isinstance( - _admTemplateData[policy_namespace][policy_name][temp_element_name], - bool): - msg = 'Element {0} requires a boolean True or False' - raise SaltInvocationError(msg.format(temp_element_name)) - elif etree.QName(child_item).localname == 'decimal' or \ - etree.QName(child_item).localname == 'longDecimal': + _admTemplateData[ + policy_namespace + ][policy_name][ + temp_element_name + ], + bool, + ): + msg = "Element {0} requires a boolean True or False" + raise SaltInvocationError( + msg.format( + temp_element_name + ) + ) + elif ( + etree.QName(child_item).localname + == "decimal" + or etree.QName(child_item).localname + == "longDecimal" + ): min_val = 0 max_val = 9999 - if 'minValue' in child_item.attrib: - min_val = int(child_item.attrib['minValue']) - if 'maxValue' in child_item.attrib: - max_val = int(child_item.attrib['maxValue']) - if int(_admTemplateData[policy_namespace][policy_name][temp_element_name]) \ - < min_val or \ - int(_admTemplateData[policy_namespace][policy_name][temp_element_name]) \ - > max_val: + if "minValue" in child_item.attrib: + min_val = int( + child_item.attrib[ + "minValue" + ] + ) + if "maxValue" in child_item.attrib: + max_val = int( + child_item.attrib[ + "maxValue" + ] + ) + if ( + int( + _admTemplateData[ + policy_namespace + ][policy_name][ + temp_element_name + ] + ) + < min_val + or int( + _admTemplateData[ + policy_namespace + ][policy_name][ + temp_element_name + ] + ) + > max_val + ): msg = 'Element "{0}" value must be between {1} and {2}' - raise SaltInvocationError(msg.format(temp_element_name, - min_val, - max_val)) - elif etree.QName(child_item).localname == 'enum': + raise SaltInvocationError( + msg.format( + temp_element_name, + min_val, + max_val, + ) + ) + elif ( + etree.QName(child_item).localname + == "enum" + ): # make sure the value is in the enumeration found = False - for enum_item in child_item.getchildren(): - if _admTemplateData[policy_namespace][policy_name][temp_element_name] == \ - _getAdmlDisplayName( + for ( + enum_item + ) in child_item.getchildren(): + if ( + _admTemplateData[ + policy_namespace + ][policy_name][ + temp_element_name + ] + == _getAdmlDisplayName( adml_policy_resources, - enum_item.attrib['displayName']).strip(): + enum_item.attrib[ + "displayName" + ], + ).strip() + ): found = True break if not found: msg = 'Element "{0}" does not have a valid value' - raise SaltInvocationError(msg.format(temp_element_name)) - elif etree.QName(child_item).localname == 'list': - if 'explicitValue' in child_item.attrib \ - and child_item.attrib['explicitValue'].lower() == \ - 'true': + raise SaltInvocationError( + msg.format( + temp_element_name + ) + ) + elif ( + etree.QName(child_item).localname + == "list" + ): + if ( + "explicitValue" + in child_item.attrib + and child_item.attrib[ + "explicitValue" + ].lower() + == "true" + ): if not isinstance( - _admTemplateData[policy_namespace][policy_name][temp_element_name], - dict): - msg = ('Each list item of element "{0}" ' - 'requires a dict value') - msg = msg.format(temp_element_name) - raise SaltInvocationError(msg) + _admTemplateData[ + policy_namespace + ][policy_name][ + temp_element_name + ], + dict, + ): + msg = ( + 'Each list item of element "{0}" ' + "requires a dict value" + ) + msg = msg.format( + temp_element_name + ) + raise SaltInvocationError( + msg + ) elif not isinstance( - _admTemplateData[policy_namespace][policy_name][temp_element_name], - list): + _admTemplateData[ + policy_namespace + ][policy_name][ + temp_element_name + ], + list, + ): msg = 'Element "{0}" requires a list value' - msg = msg.format(temp_element_name) + msg = msg.format( + temp_element_name + ) raise SaltInvocationError(msg) - elif etree.QName(child_item).localname == 'multiText': + elif ( + etree.QName(child_item).localname + == "multiText" + ): if not isinstance( - _admTemplateData[policy_namespace][policy_name][temp_element_name], - list): + _admTemplateData[ + policy_namespace + ][policy_name][ + temp_element_name + ], + list, + ): msg = 'Element "{0}" requires a list value' - msg = msg.format(temp_element_name) + msg = msg.format( + temp_element_name + ) raise SaltInvocationError(msg) - _admTemplateData[policy_namespace][policy_name][child_item.attrib['id']] = \ - _admTemplateData[policy_namespace][policy_name].pop(temp_element_name) + _admTemplateData[policy_namespace][ + policy_name + ][ + child_item.attrib["id"] + ] = _admTemplateData[ + policy_namespace + ][ + policy_name + ].pop( + temp_element_name + ) else: msg = 'The policy "{0}" has elements which must be configured' msg = msg.format(policy_name) raise SaltInvocationError(msg) else: - if six.text_type(_admTemplateData[policy_namespace][policy_name]).lower() != 'enabled': - msg = ('The policy {0} must either be "Enabled", ' - '"Disabled", or "Not Configured"') + if ( + six.text_type( + _admTemplateData[policy_namespace][ + policy_name + ] + ).lower() + != "enabled" + ): + msg = ( + 'The policy {0} must either be "Enabled", ' + '"Disabled", or "Not Configured"' + ) msg = msg.format(policy_name) raise SaltInvocationError(msg) if _regedits: for regedit in _regedits: - log.trace('%s is a Registry policy', regedit) + log.trace("%s is a Registry policy", regedit) # if the value setting is None or "(value not set)", we will delete the value from the registry - if _regedits[regedit]['value'] is not None and _regedits[regedit]['value'] != '(value not set)': - _ret = __salt__['reg.set_value']( - _regedits[regedit]['policy']['Registry']['Hive'], - _regedits[regedit]['policy']['Registry']['Path'], - _regedits[regedit]['policy']['Registry']['Value'], - _regedits[regedit]['value'], - _regedits[regedit]['policy']['Registry']['Type']) + if ( + _regedits[regedit]["value"] is not None + and _regedits[regedit]["value"] != "(value not set)" + ): + _ret = __salt__["reg.set_value"]( + _regedits[regedit]["policy"]["Registry"]["Hive"], + _regedits[regedit]["policy"]["Registry"]["Path"], + _regedits[regedit]["policy"]["Registry"]["Value"], + _regedits[regedit]["value"], + _regedits[regedit]["policy"]["Registry"]["Type"], + ) else: - _ret = __salt__['reg.read_value']( - _regedits[regedit]['policy']['Registry']['Hive'], - _regedits[regedit]['policy']['Registry']['Path'], - _regedits[regedit]['policy']['Registry']['Value']) - if _ret['success'] and _ret['vdata'] != '(value not set)': - _ret = __salt__['reg.delete_value']( - _regedits[regedit]['policy']['Registry']['Hive'], - _regedits[regedit]['policy']['Registry']['Path'], - _regedits[regedit]['policy']['Registry']['Value']) + _ret = __salt__["reg.read_value"]( + _regedits[regedit]["policy"]["Registry"]["Hive"], + _regedits[regedit]["policy"]["Registry"]["Path"], + _regedits[regedit]["policy"]["Registry"]["Value"], + ) + if _ret["success"] and _ret["vdata"] != "(value not set)": + _ret = __salt__["reg.delete_value"]( + _regedits[regedit]["policy"]["Registry"]["Hive"], + _regedits[regedit]["policy"]["Registry"]["Path"], + _regedits[regedit]["policy"]["Registry"]["Value"], + ) if not _ret: - msg = ('Error while attempting to set policy {0} via the registry.' - ' Some changes may not be applied as expected') + msg = ( + "Error while attempting to set policy {0} via the registry." + " Some changes may not be applied as expected" + ) raise CommandExecutionError(msg.format(regedit)) if _lsarights: for lsaright in _lsarights: _existingUsers = None if not cumulative_rights_assignments: _existingUsers = _getRightsAssignments( - _lsarights[lsaright]['policy']['LsaRights']['Option']) - if _lsarights[lsaright]['value']: - for acct in _lsarights[lsaright]['value']: - _ret = _addAccountRights(acct, _lsarights[lsaright]['policy']['LsaRights']['Option']) + _lsarights[lsaright]["policy"]["LsaRights"]["Option"] + ) + if _lsarights[lsaright]["value"]: + for acct in _lsarights[lsaright]["value"]: + _ret = _addAccountRights( + acct, + _lsarights[lsaright]["policy"]["LsaRights"][ + "Option" + ], + ) if not _ret: - msg = 'An error occurred attempting to configure the user right {0}.' + msg = "An error occurred attempting to configure the user right {0}." raise SaltInvocationError(msg.format(lsaright)) if _existingUsers: for acct in _existingUsers: - if acct not in _lsarights[lsaright]['value']: + if acct not in _lsarights[lsaright]["value"]: _ret = _delAccountRights( - acct, _lsarights[lsaright]['policy']['LsaRights']['Option']) + acct, + _lsarights[lsaright]["policy"]["LsaRights"][ + "Option" + ], + ) if not _ret: - msg = ('An error occurred attempting to remove previously' - 'configured users with right {0}.') + msg = ( + "An error occurred attempting to remove previously" + "configured users with right {0}." + ) raise SaltInvocationError(msg.format(lsaright)) if _secedits: # we've got secedits to make log.trace(_secedits) - ini_data = '\r\n'.join(['[Unicode]', 'Unicode=yes']) - _seceditSections = ['System Access', 'Event Audit', 'Registry Values', 'Privilege Rights'] + ini_data = "\r\n".join(["[Unicode]", "Unicode=yes"]) + _seceditSections = [ + "System Access", + "Event Audit", + "Registry Values", + "Privilege Rights", + ] for _seceditSection in _seceditSections: if _seceditSection in _secedits: - ini_data = '\r\n'.join([ini_data, ''.join(['[', _seceditSection, ']']), - '\r\n'.join(_secedits[_seceditSection])]) - ini_data = '\r\n'.join([ini_data, '[Version]', - 'signature="$CHICAGO$"', - 'Revision=1']) - log.trace('ini_data == %s', ini_data) + ini_data = "\r\n".join( + [ + ini_data, + "".join(["[", _seceditSection, "]"]), + "\r\n".join(_secedits[_seceditSection]), + ] + ) + ini_data = "\r\n".join( + [ini_data, "[Version]", 'signature="$CHICAGO$"', "Revision=1"] + ) + log.trace("ini_data == %s", ini_data) if not _write_secedit_data(ini_data): - msg = ('Error while attempting to set policies via ' - 'secedit. Some changes may not be applied as ' - 'expected') + msg = ( + "Error while attempting to set policies via " + "secedit. Some changes may not be applied as " + "expected" + ) raise CommandExecutionError(msg) if _netshs: # we've got netsh settings to make for setting in _netshs: - log.trace('Setting firewall policy: {0}'.format(setting)) + log.trace("Setting firewall policy: {0}".format(setting)) log.trace(_netshs[setting]) _set_netsh_value(**_netshs[setting]) if _advaudits: # We've got AdvAudit settings to make for setting in _advaudits: - log.trace('Setting Advanced Audit policy: {0}'.format(setting)) + log.trace("Setting Advanced Audit policy: {0}".format(setting)) log.trace(_advaudits[setting]) _set_advaudit_value(**_advaudits[setting]) @@ -9040,28 +10140,42 @@ def set_(computer_policy=None, log.trace(_modal_sets) for _modal_set in _modal_sets: try: - _existingModalData = win32net.NetUserModalsGet(None, _modal_set) - _newModalSetData = dictupdate.update(_existingModalData, _modal_sets[_modal_set]) - log.trace('NEW MODAL SET = %s', _newModalSetData) - _ret = win32net.NetUserModalsSet(None, _modal_set, _newModalSetData) + _existingModalData = win32net.NetUserModalsGet( + None, _modal_set + ) + _newModalSetData = dictupdate.update( + _existingModalData, _modal_sets[_modal_set] + ) + log.trace("NEW MODAL SET = %s", _newModalSetData) + _ret = win32net.NetUserModalsSet( + None, _modal_set, _newModalSetData + ) # TODO: This needs to be more specific except Exception as exc: # pylint: disable=broad-except - msg = 'An unhandled exception occurred while ' \ - 'attempting to set policy via ' \ - 'NetUserModalSet\n{0}'.format(exc) + msg = ( + "An unhandled exception occurred while " + "attempting to set policy via " + "NetUserModalSet\n{0}".format(exc) + ) log.exception(msg) raise CommandExecutionError(msg) if _admTemplateData: _ret = False - log.trace('going to write some adm template data :: %s', _admTemplateData) - _ret = _writeAdminTemplateRegPolFile(_admTemplateData, - adml_language=adml_language, - registry_class=p_class) + log.trace( + "going to write some adm template data :: %s", _admTemplateData + ) + _ret = _writeAdminTemplateRegPolFile( + _admTemplateData, + adml_language=adml_language, + registry_class=p_class, + ) if not _ret: - msg = ('Error while attempting to write Administrative Template Policy data.' - ' Some changes may not be applied as expected') + msg = ( + "Error while attempting to write Administrative Template Policy data." + " Some changes may not be applied as expected" + ) raise CommandExecutionError(msg) return True else: - msg = 'You have to specify something!' + msg = "You have to specify something!" raise SaltInvocationError(msg) diff --git a/salt/modules/win_license.py b/salt/modules/win_license.py index 3d8d306bcaf..3c46ff4ca67 100644 --- a/salt/modules/win_license.py +++ b/salt/modules/win_license.py @@ -1,35 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" This module allows you to manage windows licensing via slmgr.vbs .. code-block:: bash salt '*' license.install XXXXX-XXXXX-XXXXX-XXXXX-XXXXX -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import re +from __future__ import absolute_import, print_function, unicode_literals + import logging +import re # Import Salt Libs import salt.utils.platform log = logging.getLogger(__name__) -__virtualname__ = 'license' +__virtualname__ = "license" def __virtual__(): - ''' + """ Only work on Windows - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ - return False + return (False, "Module win_license: module only works on Windows systems.") def installed(product_key): - ''' + """ Check to see if the product key is already installed. Note: This is not 100% accurate as we can only see the last @@ -40,14 +41,14 @@ def installed(product_key): .. code-block:: bash salt '*' license.installed XXXXX-XXXXX-XXXXX-XXXXX-XXXXX - ''' - cmd = r'cscript C:\Windows\System32\slmgr.vbs /dli' - out = __salt__['cmd.run'](cmd) + """ + cmd = r"cscript C:\Windows\System32\slmgr.vbs /dli" + out = __salt__["cmd.run"](cmd) return product_key[-5:] in out def install(product_key): - ''' + """ Install the given product key CLI Example: @@ -55,13 +56,13 @@ def install(product_key): .. code-block:: bash salt '*' license.install XXXXX-XXXXX-XXXXX-XXXXX-XXXXX - ''' - cmd = r'cscript C:\Windows\System32\slmgr.vbs /ipk {0}'.format(product_key) - return __salt__['cmd.run'](cmd) + """ + cmd = r"cscript C:\Windows\System32\slmgr.vbs /ipk {0}".format(product_key) + return __salt__["cmd.run"](cmd) def uninstall(): - ''' + """ Uninstall the current product key CLI Example: @@ -69,13 +70,13 @@ def uninstall(): .. code-block:: bash salt '*' license.uninstall - ''' - cmd = r'cscript C:\Windows\System32\slmgr.vbs /upk' - return __salt__['cmd.run'](cmd) + """ + cmd = r"cscript C:\Windows\System32\slmgr.vbs /upk" + return __salt__["cmd.run"](cmd) def activate(): - ''' + """ Attempt to activate the current machine via Windows Activation CLI Example: @@ -83,13 +84,13 @@ def activate(): .. code-block:: bash salt '*' license.activate - ''' - cmd = r'cscript C:\Windows\System32\slmgr.vbs /ato' - return __salt__['cmd.run'](cmd) + """ + cmd = r"cscript C:\Windows\System32\slmgr.vbs /ato" + return __salt__["cmd.run"](cmd) def licensed(): - ''' + """ Return true if the current machine is licensed correctly CLI Example: @@ -97,14 +98,14 @@ def licensed(): .. code-block:: bash salt '*' license.licensed - ''' - cmd = r'cscript C:\Windows\System32\slmgr.vbs /dli' - out = __salt__['cmd.run'](cmd) - return 'License Status: Licensed' in out + """ + cmd = r"cscript C:\Windows\System32\slmgr.vbs /dli" + out = __salt__["cmd.run"](cmd) + return "License Status: Licensed" in out def info(): - ''' + """ Return information about the license, if the license is not correctly activated this will return None. @@ -113,20 +114,23 @@ def info(): .. code-block:: bash salt '*' license.info - ''' - cmd = r'cscript C:\Windows\System32\slmgr.vbs /dli' - out = __salt__['cmd.run'](cmd) + """ + cmd = r"cscript C:\Windows\System32\slmgr.vbs /dli" + out = __salt__["cmd.run"](cmd) - match = re.search(r'Name: (.*)\r\nDescription: (.*)\r\nPartial Product Key: (.*)\r\nLicense Status: (.*)', out, - re.MULTILINE) + match = re.search( + r"Name: (.*)\r\nDescription: (.*)\r\nPartial Product Key: (.*)\r\nLicense Status: (.*)", + out, + re.MULTILINE, + ) if match is not None: groups = match.groups() return { - 'name': groups[0], - 'description': groups[1], - 'partial_key': groups[2], - 'licensed': 'Licensed' in groups[3] + "name": groups[0], + "description": groups[1], + "partial_key": groups[2], + "licensed": "Licensed" in groups[3], } return None diff --git a/salt/modules/win_network.py b/salt/modules/win_network.py index 251980976c2..7bfaeb80ae3 100644 --- a/salt/modules/win_network.py +++ b/salt/modules/win_network.py @@ -1,47 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" Module for gathering and managing network information -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import datetime +import hashlib # Import Python libs import re -import hashlib -import datetime import socket +# Import 3rd party libraries +import salt.ext.six as six # pylint: disable=W0611 + # Import Salt libs import salt.utils.network import salt.utils.platform import salt.utils.validate.net -from salt.modules.network import (wol, get_hostname, interface, interface_ip, - subnets6, ip_in_subnet, convert_cidr, - calc_net, get_fqdn, ifacestartswith, - iphexval) +from salt._compat import ipaddress +from salt.modules.network import ( + calc_net, + convert_cidr, + get_fqdn, + get_hostname, + ifacestartswith, + interface, + interface_ip, + ip_in_subnet, + iphexval, + subnets6, + wol, +) from salt.utils.functools import namespaced_function as _namespaced_function try: import salt.utils.winapi + HAS_DEPENDENCIES = True except ImportError: HAS_DEPENDENCIES = False -# Import 3rd party libraries -import salt.ext.six as six # pylint: disable=W0611 + try: import wmi # pylint: disable=W0611 except ImportError: HAS_DEPENDENCIES = False -from salt._compat import ipaddress # Define the module's virtual name -__virtualname__ = 'network' +__virtualname__ = "network" def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if not salt.utils.platform.is_windows(): return False, "Module win_network: Only available on Windows" @@ -67,7 +80,7 @@ def __virtual__(): def ping(host, timeout=False, return_boolean=False): - ''' + """ Performs a ping to a host CLI Example: @@ -89,26 +102,33 @@ def ping(host, timeout=False, return_boolean=False): .. code-block:: bash salt '*' network.ping archlinux.org timeout=3 - ''' + """ if timeout: # Windows ping differs by having timeout be for individual echo requests.' # Divide timeout by tries to mimic BSD behaviour. timeout = int(timeout) * 1000 // 4 - cmd = ['ping', '-n', '4', '-w', six.text_type(timeout), salt.utils.network.sanitize_host(host)] + cmd = [ + "ping", + "-n", + "4", + "-w", + six.text_type(timeout), + salt.utils.network.sanitize_host(host), + ] else: - cmd = ['ping', '-n', '4', salt.utils.network.sanitize_host(host)] + cmd = ["ping", "-n", "4", salt.utils.network.sanitize_host(host)] if return_boolean: - ret = __salt__['cmd.run_all'](cmd, python_shell=False) - if ret['retcode'] != 0: + ret = __salt__["cmd.run_all"](cmd, python_shell=False) + if ret["retcode"] != 0: return False else: return True else: - return __salt__['cmd.run'](cmd, python_shell=False) + return __salt__["cmd.run"](cmd, python_shell=False) def netstat(): - ''' + """ Return information on open ports and states CLI Example: @@ -116,31 +136,37 @@ def netstat(): .. code-block:: bash salt '*' network.netstat - ''' + """ ret = [] - cmd = ['netstat', '-nao'] - lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + cmd = ["netstat", "-nao"] + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() for line in lines: comps = line.split() - if line.startswith(' TCP'): - ret.append({ - 'local-address': comps[1], - 'proto': comps[0], - 'remote-address': comps[2], - 'state': comps[3], - 'program': comps[4]}) - if line.startswith(' UDP'): - ret.append({ - 'local-address': comps[1], - 'proto': comps[0], - 'remote-address': comps[2], - 'state': None, - 'program': comps[3]}) + if line.startswith(" TCP"): + ret.append( + { + "local-address": comps[1], + "proto": comps[0], + "remote-address": comps[2], + "state": comps[3], + "program": comps[4], + } + ) + if line.startswith(" UDP"): + ret.append( + { + "local-address": comps[1], + "proto": comps[0], + "remote-address": comps[2], + "state": None, + "program": comps[3], + } + ) return ret def traceroute(host): - ''' + """ Performs a traceroute to a 3rd party host CLI Example: @@ -148,16 +174,16 @@ def traceroute(host): .. code-block:: bash salt '*' network.traceroute archlinux.org - ''' + """ ret = [] - cmd = ['tracert', salt.utils.network.sanitize_host(host)] - lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + cmd = ["tracert", salt.utils.network.sanitize_host(host)] + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() for line in lines: - if ' ' not in line: + if " " not in line: continue - if line.startswith('Trac'): + if line.startswith("Trac"): continue - if line.startswith('over'): + if line.startswith("over"): continue comps = line.split() complength = len(comps) @@ -165,36 +191,39 @@ def traceroute(host): # For example if some of the ms returns are '*' if complength == 9: result = { - 'count': comps[0], - 'hostname': comps[7], - 'ip': comps[8], - 'ms1': comps[1], - 'ms2': comps[3], - 'ms3': comps[5]} + "count": comps[0], + "hostname": comps[7], + "ip": comps[8], + "ms1": comps[1], + "ms2": comps[3], + "ms3": comps[5], + } ret.append(result) elif complength == 8: result = { - 'count': comps[0], - 'hostname': None, - 'ip': comps[7], - 'ms1': comps[1], - 'ms2': comps[3], - 'ms3': comps[5]} + "count": comps[0], + "hostname": None, + "ip": comps[7], + "ms1": comps[1], + "ms2": comps[3], + "ms3": comps[5], + } ret.append(result) else: result = { - 'count': comps[0], - 'hostname': None, - 'ip': None, - 'ms1': None, - 'ms2': None, - 'ms3': None} + "count": comps[0], + "hostname": None, + "ip": None, + "ms1": None, + "ms2": None, + "ms3": None, + } ret.append(result) return ret def nslookup(host): - ''' + """ Query DNS for information about a domain or ip address CLI Example: @@ -202,19 +231,19 @@ def nslookup(host): .. code-block:: bash salt '*' network.nslookup archlinux.org - ''' + """ ret = [] addresses = [] - cmd = ['nslookup', salt.utils.network.sanitize_host(host)] - lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + cmd = ["nslookup", salt.utils.network.sanitize_host(host)] + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() for line in lines: if addresses: # We're in the last block listing addresses addresses.append(line.strip()) continue - if line.startswith('Non-authoritative'): + if line.startswith("Non-authoritative"): continue - if 'Addresses' in line: + if "Addresses" in line: comps = line.split(":", 1) addresses.append(comps[1].strip()) continue @@ -222,12 +251,12 @@ def nslookup(host): comps = line.split(":", 1) ret.append({comps[0].strip(): comps[1].strip()}) if addresses: - ret.append({'Addresses': addresses}) + ret.append({"Addresses": addresses}) return ret def get_route(ip): - ''' + """ Return routing information for given destination ip .. versionadded:: 2016.11.5 @@ -235,28 +264,28 @@ def get_route(ip): CLI Example:: salt '*' network.get_route 10.10.10.10 - ''' - cmd = 'Find-NetRoute -RemoteIPAddress {0}'.format(ip) - out = __salt__['cmd.run'](cmd, shell='powershell', python_shell=True) + """ + cmd = "Find-NetRoute -RemoteIPAddress {0}".format(ip) + out = __salt__["cmd.run"](cmd, shell="powershell", python_shell=True) regexp = re.compile( r"^IPAddress\s+:\s(?P<source>[\d\.:]+)?.*" r"^InterfaceAlias\s+:\s(?P<interface>[\w\.\:\-\ ]+)?.*" r"^NextHop\s+:\s(?P<gateway>[\d\.:]+)", - flags=re.MULTILINE | re.DOTALL + flags=re.MULTILINE | re.DOTALL, ) m = regexp.search(out) ret = { - 'destination': ip, - 'gateway': m.group('gateway'), - 'interface': m.group('interface'), - 'source': m.group('source') + "destination": ip, + "gateway": m.group("gateway"), + "interface": m.group("interface"), + "source": m.group("source"), } return ret def dig(host): - ''' + """ Performs a DNS lookup with dig Note: dig must be installed on the Windows minion @@ -266,13 +295,13 @@ def dig(host): .. code-block:: bash salt '*' network.dig archlinux.org - ''' - cmd = ['dig', salt.utils.network.sanitize_host(host)] - return __salt__['cmd.run'](cmd, python_shell=False) + """ + cmd = ["dig", salt.utils.network.sanitize_host(host)] + return __salt__["cmd.run"](cmd, python_shell=False) def interfaces_names(): - ''' + """ Return a list of all the interfaces names CLI Example: @@ -280,7 +309,7 @@ def interfaces_names(): .. code-block:: bash salt '*' network.interfaces_names - ''' + """ ret = [] with salt.utils.winapi.Com(): @@ -291,7 +320,7 @@ def interfaces_names(): def interfaces(): - ''' + """ Return a dictionary of information about all the interfaces on the minion CLI Example: @@ -299,12 +328,12 @@ def interfaces(): .. code-block:: bash salt '*' network.interfaces - ''' + """ return salt.utils.network.win_interfaces() def hw_addr(iface): - ''' + """ Return the hardware address (a.k.a. MAC address) for a given interface CLI Example: @@ -312,16 +341,16 @@ def hw_addr(iface): .. code-block:: bash salt '*' network.hw_addr 'Wireless Connection #1' - ''' + """ return salt.utils.network.hw_addr(iface) # Alias hwaddr to preserve backward compat -hwaddr = salt.utils.functools.alias_function(hw_addr, 'hwaddr') +hwaddr = salt.utils.functools.alias_function(hw_addr, "hwaddr") def subnets(): - ''' + """ Returns a list of subnets to which the host belongs CLI Example: @@ -329,12 +358,12 @@ def subnets(): .. code-block:: bash salt '*' network.subnets - ''' + """ return salt.utils.network.subnets() def in_subnet(cidr): - ''' + """ Returns True if host is within specified subnet, otherwise False CLI Example: @@ -342,12 +371,12 @@ def in_subnet(cidr): .. code-block:: bash salt '*' network.in_subnet 10.0.0.0/16 - ''' + """ return salt.utils.network.in_subnet(cidr) def ip_addrs(interface=None, include_loopback=False, cidr=None, type=None): - ''' + """ Returns a list of IPv4 addresses assigned to the host. interface @@ -375,25 +404,26 @@ def ip_addrs(interface=None, include_loopback=False, cidr=None, type=None): salt '*' network.ip_addrs salt '*' network.ip_addrs cidr=10.0.0.0/8 salt '*' network.ip_addrs cidr=192.168.0.0/16 type=private - ''' - addrs = salt.utils.network.ip_addrs(interface=interface, - include_loopback=include_loopback) + """ + addrs = salt.utils.network.ip_addrs( + interface=interface, include_loopback=include_loopback + ) if cidr: return [i for i in addrs if salt.utils.network.in_subnet(cidr, [i])] else: - if type == 'public': + if type == "public": return [i for i in addrs if not is_private(i)] - elif type == 'private': + elif type == "private": return [i for i in addrs if is_private(i)] else: return addrs -ipaddrs = salt.utils.functools.alias_function(ip_addrs, 'ipaddrs') +ipaddrs = salt.utils.functools.alias_function(ip_addrs, "ipaddrs") def ip_addrs6(interface=None, include_loopback=False, cidr=None): - ''' + """ Returns a list of IPv6 addresses assigned to the host. interface @@ -414,20 +444,21 @@ def ip_addrs6(interface=None, include_loopback=False, cidr=None): salt '*' network.ip_addrs6 salt '*' network.ip_addrs6 cidr=2000::/3 - ''' - addrs = salt.utils.network.ip_addrs6(interface=interface, - include_loopback=include_loopback) + """ + addrs = salt.utils.network.ip_addrs6( + interface=interface, include_loopback=include_loopback + ) if cidr: return [i for i in addrs if salt.utils.network.in_subnet(cidr, [i])] else: return addrs -ipaddrs6 = salt.utils.functools.alias_function(ip_addrs6, 'ipaddrs6') +ipaddrs6 = salt.utils.functools.alias_function(ip_addrs6, "ipaddrs6") def connect(host, port=None, **kwargs): - ''' + """ Test connectivity to a host using a particular port from the minion. @@ -444,61 +475,60 @@ def connect(host, port=None, **kwargs): salt '*' network.connect archlinux.org 80 timeout=3 family=ipv4 salt '*' network.connect google-public-dns-a.google.com port=53 proto=udp timeout=3 - ''' + """ - ret = {'result': None, - 'comment': ''} + ret = {"result": None, "comment": ""} if not host: - ret['result'] = False - ret['comment'] = 'Required argument, host, is missing.' + ret["result"] = False + ret["comment"] = "Required argument, host, is missing." return ret if not port: - ret['result'] = False - ret['comment'] = 'Required argument, port, is missing.' + ret["result"] = False + ret["comment"] = "Required argument, port, is missing." return ret - proto = kwargs.get('proto', 'tcp') - timeout = kwargs.get('timeout', 5) - family = kwargs.get('family', None) + proto = kwargs.get("proto", "tcp") + timeout = kwargs.get("timeout", 5) + family = kwargs.get("family", None) - if salt.utils.validate.net.ipv4_addr(host) or salt.utils.validate.net.ipv6_addr(host): + if salt.utils.validate.net.ipv4_addr(host) or salt.utils.validate.net.ipv6_addr( + host + ): address = host else: - address = '{0}'.format(salt.utils.network.sanitize_host(host)) + address = "{0}".format(salt.utils.network.sanitize_host(host)) try: - if proto == 'udp': + if proto == "udp": __proto = socket.SOL_UDP else: __proto = socket.SOL_TCP - proto = 'tcp' + proto = "tcp" if family: - if family == 'ipv4': + if family == "ipv4": __family = socket.AF_INET - elif family == 'ipv6': + elif family == "ipv6": __family = socket.AF_INET6 else: __family = 0 else: __family = 0 - (family, - socktype, - _proto, - garbage, - _address) = socket.getaddrinfo(address, port, __family, 0, __proto)[0] + (family, socktype, _proto, garbage, _address) = socket.getaddrinfo( + address, port, __family, 0, __proto + )[0] skt = socket.socket(family, socktype, _proto) skt.settimeout(timeout) - if proto == 'udp': + if proto == "udp": # Generate a random string of a # decent size to test UDP connection md5h = hashlib.md5() - md5h.update(datetime.datetime.now().strftime('%s')) + md5h.update(datetime.datetime.now().strftime("%s")) msg = md5h.hexdigest() skt.sendto(msg, _address) recv, svr = skt.recvfrom(255) @@ -507,19 +537,21 @@ def connect(host, port=None, **kwargs): skt.connect(_address) skt.shutdown(2) except Exception as exc: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = 'Unable to connect to {0} ({1}) on {2} port {3}'\ - .format(host, _address[0], proto, port) + ret["result"] = False + ret["comment"] = "Unable to connect to {0} ({1}) on {2} port {3}".format( + host, _address[0], proto, port + ) return ret - ret['result'] = True - ret['comment'] = 'Successfully connected to {0} ({1}) on {2} port {3}'\ - .format(host, _address[0], proto, port) + ret["result"] = True + ret["comment"] = "Successfully connected to {0} ({1}) on {2} port {3}".format( + host, _address[0], proto, port + ) return ret def is_private(ip_addr): - ''' + """ Check if the given IP address is a private address .. versionadded:: 2019.2.0 @@ -529,5 +561,5 @@ def is_private(ip_addr): .. code-block:: bash salt '*' network.is_private 10.0.0.3 - ''' + """ return ipaddress.ip_address(ip_addr).is_private diff --git a/salt/modules/win_ntp.py b/salt/modules/win_ntp.py index 73fa89581d8..78379d3ca9f 100644 --- a/salt/modules/win_ntp.py +++ b/salt/modules/win_ntp.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Management of NTP servers on Windows .. versionadded:: 2014.1.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -15,20 +15,20 @@ import salt.utils.platform log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'ntp' +__virtualname__ = "ntp" def __virtual__(): - ''' + """ This only supports Windows - ''' + """ if not salt.utils.platform.is_windows(): return (False, "Module win_system: module only works on Windows systems") return __virtualname__ def set_servers(*servers): - ''' + """ Set Windows to use a list of NTP servers CLI Example: @@ -36,29 +36,33 @@ def set_servers(*servers): .. code-block:: bash salt '*' ntp.set_servers 'pool.ntp.org' 'us.pool.ntp.org' - ''' - service_name = 'w32time' - if not __salt__['service.status'](service_name): - if not __salt__['service.start'](service_name): + """ + service_name = "w32time" + if not __salt__["service.status"](service_name): + if not __salt__["service.start"](service_name): return False - server_cmd = ['W32tm', '/config', '/syncfromflags:manual', - '/manualpeerlist:{0}'.format(' '.join(servers))] - reliable_cmd = ['W32tm', '/config', '/reliable:yes'] - update_cmd = ['W32tm', '/config', '/update'] + server_cmd = [ + "W32tm", + "/config", + "/syncfromflags:manual", + "/manualpeerlist:{0}".format(" ".join(servers)), + ] + reliable_cmd = ["W32tm", "/config", "/reliable:yes"] + update_cmd = ["W32tm", "/config", "/update"] for cmd in server_cmd, reliable_cmd, update_cmd: - __salt__['cmd.run'](cmd, python_shell=False) + __salt__["cmd.run"](cmd, python_shell=False) if not sorted(list(servers)) == get_servers(): return False - __salt__['service.restart'](service_name) + __salt__["service.restart"](service_name) return True def get_servers(): - ''' + """ Get list of configured NTP servers CLI Example: @@ -66,13 +70,13 @@ def get_servers(): .. code-block:: bash salt '*' ntp.get_servers - ''' - cmd = ['w32tm', '/query', '/configuration'] - lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + """ + cmd = ["w32tm", "/query", "/configuration"] + lines = __salt__["cmd.run"](cmd, python_shell=False).splitlines() for line in lines: try: - if line.startswith('NtpServer:'): - _, ntpsvrs = line.rsplit(' (', 1)[0].split(':', 1) + if line.startswith("NtpServer:"): + _, ntpsvrs = line.rsplit(" (", 1)[0].split(":", 1) return sorted(ntpsvrs.split()) except ValueError as e: return False diff --git a/salt/modules/win_path.py b/salt/modules/win_path.py index 1da944b222d..bd346921ca1 100644 --- a/salt/modules/win_path.py +++ b/salt/modules/win_path.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Manage the Windows System PATH Note that not all Windows applications will rehash the PATH environment variable, Only the ones that listen to the WM_SETTINGCHANGE message http://support.microsoft.com/kb/104011 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -21,6 +21,7 @@ import salt.utils.win_functions # Import 3rd-party libs from salt.ext.six.moves import map + try: HAS_WIN32 = True except ImportError: @@ -29,31 +30,31 @@ except ImportError: # Settings log = logging.getLogger(__name__) -HIVE = 'HKEY_LOCAL_MACHINE' -KEY = 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment' -VNAME = 'PATH' -VTYPE = 'REG_EXPAND_SZ' +HIVE = "HKEY_LOCAL_MACHINE" +KEY = "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment" +VNAME = "PATH" +VTYPE = "REG_EXPAND_SZ" PATHSEP = str(os.pathsep) # future lint: disable=blacklisted-function def __virtual__(): - ''' + """ Load only on Windows - ''' + """ if salt.utils.platform.is_windows() and HAS_WIN32: - return 'win_path' + return "win_path" return (False, "Module win_path: module only works on Windows systems") def _normalize_dir(string_): - ''' + """ Normalize the directory to make comparison possible - ''' + """ return os.path.normpath(salt.utils.stringutils.to_unicode(string_)) def rehash(): - ''' + """ Send a WM_SETTINGCHANGE Broadcast to Windows to refresh the Environment variables for new processes. @@ -69,12 +70,12 @@ def rehash(): .. code-block:: bash salt '*' win_path.rehash - ''' - return salt.utils.win_functions.broadcast_setting_change('Environment') + """ + return salt.utils.win_functions.broadcast_setting_change("Environment") def get_path(): - ''' + """ Returns a list of items in the SYSTEM path CLI Example: @@ -82,20 +83,21 @@ def get_path(): .. code-block:: bash salt '*' win_path.get_path - ''' + """ ret = salt.utils.stringutils.to_unicode( - __salt__['reg.read_value']( - 'HKEY_LOCAL_MACHINE', - 'SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', - 'PATH')['vdata'] - ).split(';') + __salt__["reg.read_value"]( + "HKEY_LOCAL_MACHINE", + "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", + "PATH", + )["vdata"] + ).split(";") # Trim ending backslash return list(map(_normalize_dir, ret)) def exists(path): - ''' + """ Check if the directory is configured in the SYSTEM path Case-insensitive and ignores trailing backslash @@ -109,7 +111,7 @@ def exists(path): salt '*' win_path.exists 'c:\\python27' salt '*' win_path.exists 'c:\\python27\\' salt '*' win_path.exists 'C:\\pyThon27' - ''' + """ path = _normalize_dir(path) sysPath = get_path() @@ -117,11 +119,13 @@ def exists(path): def _update_local_path(local_path): - os.environ[str('PATH')] = PATHSEP.join(local_path) # future lint: disable=blacklisted-function + os.environ[str("PATH")] = PATHSEP.join( + local_path + ) # future lint: disable=blacklisted-function def add(path, index=None, **kwargs): - ''' + """ Add the directory to the SYSTEM path in the index location. Returns ``True`` if successful, otherwise ``False``. @@ -145,9 +149,9 @@ def add(path, index=None, **kwargs): # Will add to the end of the path salt '*' win_path.add 'c:\\python27' index='-1' - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - rehash_ = kwargs.pop('rehash', True) + rehash_ = kwargs.pop("rehash", True) if kwargs: salt.utils.args.invalid_kwargs(kwargs) @@ -158,8 +162,7 @@ def add(path, index=None, **kwargs): # The current path should not have any unicode in it, but don't take any # chances. local_path = [ - salt.utils.stringutils.to_str(x) - for x in os.environ['PATH'].split(PATHSEP) + salt.utils.stringutils.to_str(x) for x in os.environ["PATH"].split(PATHSEP) ] if index is not None: @@ -169,11 +172,11 @@ def add(path, index=None, **kwargs): index = None def _check_path(dirs, path, index): - ''' + """ Check the dir list for the specified path, at the specified index, and make changes to the list if needed. Return True if changes were made to the list, otherwise return False. - ''' + """ dirs_lc = [x.lower() for x in dirs] try: # Check index with case normalized @@ -195,7 +198,7 @@ def add(path, index=None, **kwargs): if index >= num_dirs or index == -1: # Set pos to 'END' so we know that we're moving the directory # if it exists and isn't already at the end. - pos = 'END' + pos = "END" elif index <= -num_dirs: # Negative index is too large, shift index to beginning of list index = pos = 0 @@ -215,7 +218,7 @@ def add(path, index=None, **kwargs): # ['one', 'two', 'three', 'four', 'five'] pos += 1 - if pos == 'END': + if pos == "END": if cur_index is not None: if cur_index == num_dirs - 1: # Directory is already in the desired location, no changes @@ -244,8 +247,9 @@ def add(path, index=None, **kwargs): return True else: if cur_index is not None: - if (index < 0 and cur_index != (num_dirs + index)) \ - or (index >= 0 and cur_index != index): + if (index < 0 and cur_index != (num_dirs + index)) or ( + index >= 0 and cur_index != index + ): # Directory is present, but not at the desired index. # Remove it from the non-normalized path list and insert it # at the correct postition. @@ -270,12 +274,8 @@ def add(path, index=None, **kwargs): return True # Move forward with registry update - result = __salt__['reg.set_value']( - HIVE, - KEY, - VNAME, - ';'.join(salt.utils.data.decode(system_path)), - VTYPE + result = __salt__["reg.set_value"]( + HIVE, KEY, VNAME, ";".join(salt.utils.data.decode(system_path)), VTYPE ) if result and rehash_: @@ -286,7 +286,7 @@ def add(path, index=None, **kwargs): def remove(path, **kwargs): - r''' + r""" Remove the directory from the SYSTEM path Returns: @@ -303,9 +303,9 @@ def remove(path, **kwargs): # Will remove C:\Python27 from the path salt '*' win_path.remove 'c:\\python27' - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - rehash_ = kwargs.pop('rehash', True) + rehash_ = kwargs.pop("rehash", True) if kwargs: salt.utils.args.invalid_kwargs(kwargs) @@ -316,16 +316,15 @@ def remove(path, **kwargs): # The current path should not have any unicode in it, but don't take any # chances. local_path = [ - salt.utils.stringutils.to_str(x) - for x in os.environ['PATH'].split(PATHSEP) + salt.utils.stringutils.to_str(x) for x in os.environ["PATH"].split(PATHSEP) ] def _check_path(dirs, path): - ''' + """ Check the dir list for the specified path, and make changes to the list if needed. Return True if changes were made to the list, otherwise return False. - ''' + """ dirs_lc = [x.lower() for x in dirs] path_lc = path.lower() new_dirs = [] @@ -346,12 +345,8 @@ def remove(path, **kwargs): # No changes necessary return True - result = __salt__['reg.set_value']( - HIVE, - KEY, - VNAME, - ';'.join(salt.utils.data.decode(system_path)), - VTYPE + result = __salt__["reg.set_value"]( + HIVE, KEY, VNAME, ";".join(salt.utils.data.decode(system_path)), VTYPE ) if result and rehash_: diff --git a/salt/modules/win_pkg.py b/salt/modules/win_pkg.py index 622fbb12a20..a8b795b1d84 100644 --- a/salt/modules/win_pkg.py +++ b/salt/modules/win_pkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A module to manage software on Windows .. important:: @@ -35,29 +35,23 @@ old. number in sls definitions file in these cases. Versions numbers are sorted in order of 0, ``Not Found``, ``order version numbers``, ..., ``latest``. -''' +""" # Import python future libs from __future__ import absolute_import, print_function, unicode_literals + import collections import datetime import errno import logging import os import re -import time import sys +import time from functools import cmp_to_key -# Import third party libs -from salt.ext import six -# pylint: disable=import-error,no-name-in-module -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse - -# Import salt libs -from salt.exceptions import (CommandExecutionError, - SaltInvocationError, - SaltRenderError) +import salt.payload +import salt.syspaths import salt.utils.args import salt.utils.data import salt.utils.files @@ -67,27 +61,39 @@ import salt.utils.pkg import salt.utils.platform import salt.utils.versions import salt.utils.win_functions -import salt.syspaths -import salt.payload -from salt.exceptions import MinionError + +# Import salt libs +from salt.exceptions import ( + CommandExecutionError, + MinionError, + SaltInvocationError, + SaltRenderError, +) + +# Import third party libs +from salt.ext import six + +# pylint: disable=import-error,no-name-in-module +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse from salt.utils.versions import LooseVersion + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Set the virtual pkg module if the os is Windows - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ return (False, "Module win_pkg: module only works on Windows systems") def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -116,73 +122,77 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version <package name> salt '*' pkg.latest_version <package1> <package2> <package3> ... - ''' + """ if not names: - return '' + return "" # Initialize the return dict with empty strings ret = {} for name in names: - ret[name] = '' + ret[name] = "" - saltenv = kwargs.get('saltenv', 'base') + saltenv = kwargs.get("saltenv", "base") # Refresh before looking for the latest version available - refresh = salt.utils.data.is_true(kwargs.get('refresh', True)) + refresh = salt.utils.data.is_true(kwargs.get("refresh", True)) # no need to call _refresh_db_conditional as list_pkgs will do it - installed_pkgs = list_pkgs( - versions_as_list=True, saltenv=saltenv, refresh=refresh) - log.trace('List of installed packages: %s', installed_pkgs) + installed_pkgs = list_pkgs(versions_as_list=True, saltenv=saltenv, refresh=refresh) + log.trace("List of installed packages: %s", installed_pkgs) # iterate over all requested package names for name in names: - latest_installed = '0' + latest_installed = "0" # get latest installed version of package if name in installed_pkgs: - log.trace('Determining latest installed version of %s', name) + log.trace("Determining latest installed version of %s", name) try: # installed_pkgs[name] Can be version number or 'Not Found' # 'Not Found' occurs when version number is not found in the registry latest_installed = sorted( - installed_pkgs[name], - key=cmp_to_key(_reverse_cmp_pkg_versions) + installed_pkgs[name], key=cmp_to_key(_reverse_cmp_pkg_versions) ).pop() except IndexError: log.warning( - '%s was empty in pkg.list_pkgs return data, this is ' - 'probably a bug in list_pkgs', name + "%s was empty in pkg.list_pkgs return data, this is " + "probably a bug in list_pkgs", + name, ) else: - log.debug('Latest installed version of %s is %s', - name, latest_installed) + log.debug( + "Latest installed version of %s is %s", name, latest_installed + ) # get latest available (from winrepo_dir) version of package pkg_info = _get_package_info(name, saltenv=saltenv) - log.trace('Raw winrepo pkg_info for %s is %s', name, pkg_info) + log.trace("Raw winrepo pkg_info for %s is %s", name, pkg_info) # latest_available can be version number or 'latest' or even 'Not Found' latest_available = _get_latest_pkg_version(pkg_info) if latest_available: log.debug( - 'Latest available version of package %s is %s', - name, latest_available + "Latest available version of package %s is %s", name, latest_available ) # check, whether latest available version # is newer than latest installed version - if compare_versions(ver1=six.text_type(latest_available), - oper='>', - ver2=six.text_type(latest_installed)): + if compare_versions( + ver1=six.text_type(latest_available), + oper=">", + ver2=six.text_type(latest_installed), + ): log.debug( - 'Upgrade of %s from %s to %s is available', - name, latest_installed, latest_available + "Upgrade of %s from %s to %s is available", + name, + latest_installed, + latest_available, ) ret[name] = latest_available else: log.debug( - 'No newer version than %s of %s is available', - latest_installed, name + "No newer version than %s of %s is available", + latest_installed, + name, ) if len(names) == 1: return ret[names[0]] @@ -190,7 +200,7 @@ def latest_version(*names, **kwargs): def upgrade_available(name, **kwargs): - ''' + """ Check whether or not an upgrade is available for a given package Args: @@ -208,19 +218,19 @@ def upgrade_available(name, **kwargs): .. code-block:: bash salt '*' pkg.upgrade_available <package name> - ''' - saltenv = kwargs.get('saltenv', 'base') + """ + saltenv = kwargs.get("saltenv", "base") # Refresh before looking for the latest version available, # same default as latest_version - refresh = salt.utils.data.is_true(kwargs.get('refresh', True)) + refresh = salt.utils.data.is_true(kwargs.get("refresh", True)) # if latest_version returns blank, the latest version is already installed or # their is no package definition. This is a salt standard which could be improved. - return latest_version(name, saltenv=saltenv, refresh=refresh) != '' + return latest_version(name, saltenv=saltenv, refresh=refresh) != "" def list_upgrades(refresh=True, **kwargs): - ''' + """ List all available package upgrades on this system Args: @@ -237,13 +247,13 @@ def list_upgrades(refresh=True, **kwargs): .. code-block:: bash salt '*' pkg.list_upgrades - ''' - saltenv = kwargs.get('saltenv', 'base') + """ + saltenv = kwargs.get("saltenv", "base") refresh = salt.utils.data.is_true(refresh) _refresh_db_conditional(saltenv, force=refresh) installed_pkgs = list_pkgs(refresh=False, saltenv=saltenv) - available_pkgs = get_repo_data(saltenv).get('repo') + available_pkgs = get_repo_data(saltenv).get("repo") pkgs = {} for pkg in installed_pkgs: if pkg in available_pkgs: @@ -260,7 +270,7 @@ def list_upgrades(refresh=True, **kwargs): def list_available(*names, **kwargs): - ''' + """ Return a list of available versions of the specified package. Args: @@ -288,22 +298,22 @@ def list_available(*names, **kwargs): salt '*' pkg.list_available <package name> return_dict_always=True salt '*' pkg.list_available <package name01> <package name02> - ''' + """ if not names: - return '' + return "" - saltenv = kwargs.get('saltenv', 'base') - refresh = salt.utils.data.is_true(kwargs.get('refresh', False)) + saltenv = kwargs.get("saltenv", "base") + refresh = salt.utils.data.is_true(kwargs.get("refresh", False)) _refresh_db_conditional(saltenv, force=refresh) - return_dict_always = \ - salt.utils.data.is_true(kwargs.get('return_dict_always', False)) + return_dict_always = salt.utils.data.is_true( + kwargs.get("return_dict_always", False) + ) if len(names) == 1 and not return_dict_always: pkginfo = _get_package_info(names[0], saltenv=saltenv) if not pkginfo: - return '' + return "" versions = sorted( - list(pkginfo.keys()), - key=cmp_to_key(_reverse_cmp_pkg_versions) + list(pkginfo.keys()), key=cmp_to_key(_reverse_cmp_pkg_versions) ) else: versions = {} @@ -313,14 +323,14 @@ def list_available(*names, **kwargs): continue verlist = sorted( list(pkginfo.keys()) if pkginfo else [], - key=cmp_to_key(_reverse_cmp_pkg_versions) + key=cmp_to_key(_reverse_cmp_pkg_versions), ) versions[name] = verlist return versions def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -348,7 +358,7 @@ def version(*names, **kwargs): salt '*' pkg.version <package name> salt '*' pkg.version <package name01> <package name02> - ''' + """ # Standard is return empty string even if not a valid name # TODO: Look at returning an error across all platforms with # CommandExecutionError(msg,info={'errors': errors }) @@ -357,23 +367,22 @@ def version(*names, **kwargs): # if name in available_pkgs: # ret[name] = installed_pkgs.get(name, '') - saltenv = kwargs.get('saltenv', 'base') - installed_pkgs = list_pkgs(saltenv=saltenv, refresh=kwargs.get('refresh', False)) + saltenv = kwargs.get("saltenv", "base") + installed_pkgs = list_pkgs(saltenv=saltenv, refresh=kwargs.get("refresh", False)) if len(names) == 1: - return installed_pkgs.get(names[0], '') + return installed_pkgs.get(names[0], "") ret = {} for name in names: - ret[name] = installed_pkgs.get(name, '') + ret[name] = installed_pkgs.get(name, "") return ret -def list_pkgs(versions_as_list=False, - include_components=True, - include_updates=True, - **kwargs): - ''' +def list_pkgs( + versions_as_list=False, include_components=True, include_updates=True, **kwargs +): + """ List the packages currently installed. .. note:: @@ -412,47 +421,49 @@ def list_pkgs(versions_as_list=False, salt '*' pkg.list_pkgs salt '*' pkg.list_pkgs versions_as_list=True - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - saltenv = kwargs.get('saltenv', 'base') - refresh = salt.utils.data.is_true(kwargs.get('refresh', False)) + saltenv = kwargs.get("saltenv", "base") + refresh = salt.utils.data.is_true(kwargs.get("refresh", False)) _refresh_db_conditional(saltenv, force=refresh) ret = {} name_map = _get_name_map(saltenv) for pkg_name, val_list in six.iteritems( - _get_reg_software(include_components=include_components, - include_updates=include_updates)): + _get_reg_software( + include_components=include_components, include_updates=include_updates + ) + ): if pkg_name in name_map: key = name_map[pkg_name] for val in val_list: - if val == 'Not Found': + if val == "Not Found": # Look up version from winrepo pkg_info = _get_package_info(key, saltenv=saltenv) if not pkg_info: continue for pkg_ver in pkg_info.keys(): - if pkg_info[pkg_ver]['full_name'] == pkg_name: + if pkg_info[pkg_ver]["full_name"] == pkg_name: val = pkg_ver - __salt__['pkg_resource.add_pkg'](ret, key, val) + __salt__["pkg_resource.add_pkg"](ret, key, val) else: key = pkg_name for val in val_list: - __salt__['pkg_resource.add_pkg'](ret, key, val) + __salt__["pkg_resource.add_pkg"](ret, key, val) - __salt__['pkg_resource.sort_pkglist'](ret) + __salt__["pkg_resource.sort_pkglist"](ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret -def _get_reg_software(include_components=True, - include_updates=True): - ''' +def _get_reg_software(include_components=True, include_updates=True): + """ This searches the uninstall keys in the registry to find a match in the sub keys, it will return a dict with the display name as the key and the version as the value @@ -471,7 +482,7 @@ def _get_reg_software(include_components=True, .. code-block:: cfg {'<package_name>': '<version>'} - ''' + """ # Logic for this can be found in this question: # https://social.technet.microsoft.com/Forums/windows/en-US/d913471a-d7fb-448d-869b-da9025dcc943/where-does-addremove-programs-get-its-information-from-in-the-registry # and also in the collectPlatformDependentApplicationData function in @@ -479,31 +490,36 @@ def _get_reg_software(include_components=True, reg_software = {} def skip_component(hive, key, sub_key, use_32bit_registry): - ''' + """ 'SystemComponent' must be either absent or present with a value of 0, because this value is usually set on programs that have been installed via a Windows Installer Package (MSI). Returns: bool: True if the package needs to be skipped, otherwise False - ''' + """ if include_components: return False - if __utils__['reg.value_exists']( - hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='SystemComponent', - use_32bit_registry=use_32bit_registry): - if __utils__['reg.read_value']( + if __utils__["reg.value_exists"]( + hive=hive, + key="{0}\\{1}".format(key, sub_key), + vname="SystemComponent", + use_32bit_registry=use_32bit_registry, + ): + if ( + __utils__["reg.read_value"]( hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='SystemComponent', - use_32bit_registry=use_32bit_registry)['vdata'] > 0: + key="{0}\\{1}".format(key, sub_key), + vname="SystemComponent", + use_32bit_registry=use_32bit_registry, + )["vdata"] + > 0 + ): return True return False def skip_win_installer(hive, key, sub_key, use_32bit_registry): - ''' + """ 'WindowsInstaller' must be either absent or present with a value of 0. If the value is set to 1, then the application is included in the list if and only if the corresponding compressed guid is also present in @@ -511,133 +527,155 @@ def _get_reg_software(include_components=True, Returns: bool: True if the package needs to be skipped, otherwise False - ''' - products_key = 'Software\\Classes\\Installer\\Products\\{0}' - if __utils__['reg.value_exists']( - hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='WindowsInstaller', - use_32bit_registry=use_32bit_registry): - if __utils__['reg.read_value']( + """ + products_key = "Software\\Classes\\Installer\\Products\\{0}" + if __utils__["reg.value_exists"]( + hive=hive, + key="{0}\\{1}".format(key, sub_key), + vname="WindowsInstaller", + use_32bit_registry=use_32bit_registry, + ): + if ( + __utils__["reg.read_value"]( hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='WindowsInstaller', - use_32bit_registry=use_32bit_registry)['vdata'] > 0: + key="{0}\\{1}".format(key, sub_key), + vname="WindowsInstaller", + use_32bit_registry=use_32bit_registry, + )["vdata"] + > 0 + ): squid = salt.utils.win_functions.guid_to_squid(sub_key) - if not __utils__['reg.key_exists']( - hive='HKLM', - key=products_key.format(squid), - use_32bit_registry=use_32bit_registry): + if not __utils__["reg.key_exists"]( + hive="HKLM", + key=products_key.format(squid), + use_32bit_registry=use_32bit_registry, + ): return True return False def skip_uninstall_string(hive, key, sub_key, use_32bit_registry): - ''' + """ 'UninstallString' must be present, because it stores the command line that gets executed by Add/Remove programs, when the user tries to uninstall a program. Returns: bool: True if the package needs to be skipped, otherwise False - ''' - if not __utils__['reg.value_exists']( - hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='UninstallString', - use_32bit_registry=use_32bit_registry): + """ + if not __utils__["reg.value_exists"]( + hive=hive, + key="{0}\\{1}".format(key, sub_key), + vname="UninstallString", + use_32bit_registry=use_32bit_registry, + ): return True return False def skip_release_type(hive, key, sub_key, use_32bit_registry): - ''' + """ 'ReleaseType' must either be absent or if present must not have a value set to 'Security Update', 'Update Rollup', or 'Hotfix', because that indicates it's an update to an existing program. Returns: bool: True if the package needs to be skipped, otherwise False - ''' + """ if include_updates: return False - skip_types = ['Hotfix', - 'Security Update', - 'Update Rollup'] - if __utils__['reg.value_exists']( - hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='ReleaseType', - use_32bit_registry=use_32bit_registry): - if __utils__['reg.read_value']( + skip_types = ["Hotfix", "Security Update", "Update Rollup"] + if __utils__["reg.value_exists"]( + hive=hive, + key="{0}\\{1}".format(key, sub_key), + vname="ReleaseType", + use_32bit_registry=use_32bit_registry, + ): + if ( + __utils__["reg.read_value"]( hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='ReleaseType', - use_32bit_registry=use_32bit_registry)['vdata'] in skip_types: + key="{0}\\{1}".format(key, sub_key), + vname="ReleaseType", + use_32bit_registry=use_32bit_registry, + )["vdata"] + in skip_types + ): return True return False def skip_parent_key(hive, key, sub_key, use_32bit_registry): - ''' + """ 'ParentKeyName' must NOT be present, because that indicates it's an update to the parent program. Returns: bool: True if the package needs to be skipped, otherwise False - ''' - if __utils__['reg.value_exists']( - hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='ParentKeyName', - use_32bit_registry=use_32bit_registry): + """ + if __utils__["reg.value_exists"]( + hive=hive, + key="{0}\\{1}".format(key, sub_key), + vname="ParentKeyName", + use_32bit_registry=use_32bit_registry, + ): return True return False def add_software(hive, key, sub_key, use_32bit_registry): - ''' + """ 'DisplayName' must be present with a valid value, as this is reflected as the software name returned by pkg.list_pkgs. Also, its value must not start with 'KB' followed by 6 numbers - as that indicates a Windows update. - ''' - d_name_regdata = __utils__['reg.read_value']( + """ + d_name_regdata = __utils__["reg.read_value"]( hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='DisplayName', - use_32bit_registry=use_32bit_registry) + key="{0}\\{1}".format(key, sub_key), + vname="DisplayName", + use_32bit_registry=use_32bit_registry, + ) - if (not d_name_regdata['success'] or - d_name_regdata['vtype'] not in ['REG_SZ', 'REG_EXPAND_SZ'] or - d_name_regdata['vdata'] in ['(value not set)', None, False]): + if ( + not d_name_regdata["success"] + or d_name_regdata["vtype"] not in ["REG_SZ", "REG_EXPAND_SZ"] + or d_name_regdata["vdata"] in ["(value not set)", None, False] + ): return - d_name = d_name_regdata['vdata'] + d_name = d_name_regdata["vdata"] if not include_updates: - if re.match(r'^KB[0-9]{6}', d_name): + if re.match(r"^KB[0-9]{6}", d_name): return - d_vers_regdata = __utils__['reg.read_value']( + d_vers_regdata = __utils__["reg.read_value"]( hive=hive, - key='{0}\\{1}'.format(key, sub_key), - vname='DisplayVersion', - use_32bit_registry=use_32bit_registry) + key="{0}\\{1}".format(key, sub_key), + vname="DisplayVersion", + use_32bit_registry=use_32bit_registry, + ) - d_vers = 'Not Found' - if (d_vers_regdata['success'] and - d_vers_regdata['vtype'] in ['REG_SZ', 'REG_EXPAND_SZ', 'REG_DWORD']): - if isinstance(d_vers_regdata['vdata'], int): - d_vers = six.text_type(d_vers_regdata['vdata']) - elif d_vers_regdata['vdata'] and d_vers_regdata['vdata'] != '(value not set)': # Check for blank values - d_vers = d_vers_regdata['vdata'] + d_vers = "Not Found" + if d_vers_regdata["success"] and d_vers_regdata["vtype"] in [ + "REG_SZ", + "REG_EXPAND_SZ", + "REG_DWORD", + ]: + if isinstance(d_vers_regdata["vdata"], int): + d_vers = six.text_type(d_vers_regdata["vdata"]) + elif ( + d_vers_regdata["vdata"] and d_vers_regdata["vdata"] != "(value not set)" + ): # Check for blank values + d_vers = d_vers_regdata["vdata"] reg_software.setdefault(d_name, []).append(d_vers) # Start gathering information from the registry # HKLM Uninstall 64 bit - kwargs = {'hive': 'HKLM', - 'key': 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall', - 'use_32bit_registry': False} - for sub_key in __utils__['reg.list_keys'](**kwargs): - kwargs['sub_key'] = sub_key + kwargs = { + "hive": "HKLM", + "key": "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall", + "use_32bit_registry": False, + } + for sub_key in __utils__["reg.list_keys"](**kwargs): + kwargs["sub_key"] = sub_key if skip_component(**kwargs): continue if skip_win_installer(**kwargs): @@ -651,10 +689,10 @@ def _get_reg_software(include_components=True, add_software(**kwargs) # HKLM Uninstall 32 bit - kwargs['use_32bit_registry'] = True - kwargs.pop('sub_key', False) - for sub_key in __utils__['reg.list_keys'](**kwargs): - kwargs['sub_key'] = sub_key + kwargs["use_32bit_registry"] = True + kwargs.pop("sub_key", False) + for sub_key in __utils__["reg.list_keys"](**kwargs): + kwargs["sub_key"] = sub_key if skip_component(**kwargs): continue if skip_win_installer(**kwargs): @@ -668,18 +706,22 @@ def _get_reg_software(include_components=True, add_software(**kwargs) # HKLM Uninstall 64 bit - kwargs = {'hive': 'HKLM', - 'key': 'Software\\Classes\\Installer\\Products', - 'use_32bit_registry': False} - userdata_key = 'Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\' \ - 'UserData\\S-1-5-18\\Products' - for sub_key in __utils__['reg.list_keys'](**kwargs): + kwargs = { + "hive": "HKLM", + "key": "Software\\Classes\\Installer\\Products", + "use_32bit_registry": False, + } + userdata_key = ( + "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\" + "UserData\\S-1-5-18\\Products" + ) + for sub_key in __utils__["reg.list_keys"](**kwargs): # If the key does not exist in userdata, skip it - if not __utils__['reg.key_exists']( - hive=kwargs['hive'], - key='{0}\\{1}'.format(userdata_key, sub_key)): + if not __utils__["reg.key_exists"]( + hive=kwargs["hive"], key="{0}\\{1}".format(userdata_key, sub_key) + ): continue - kwargs['sub_key'] = sub_key + kwargs["sub_key"] = sub_key if skip_component(**kwargs): continue if skip_win_installer(**kwargs): @@ -689,18 +731,22 @@ def _get_reg_software(include_components=True, # Uninstall for each user on the system (HKU), 64 bit # This has a propensity to take a while on a machine where many users have # logged in. Untested in such a scenario - hive_hku = 'HKU' - uninstall_key = '{0}\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall' - product_key = '{0}\\Software\\Microsoft\\Installer\\Products' - user_data_key = 'Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\' \ - 'UserData\\{0}\\Products\\{1}' - for user_guid in __utils__['reg.list_keys'](hive=hive_hku): - kwargs = {'hive': hive_hku, - 'key': uninstall_key.format(user_guid), - 'use_32bit_registry': False} - if __utils__['reg.key_exists'](**kwargs): - for sub_key in __utils__['reg.list_keys'](**kwargs): - kwargs['sub_key'] = sub_key + hive_hku = "HKU" + uninstall_key = "{0}\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall" + product_key = "{0}\\Software\\Microsoft\\Installer\\Products" + user_data_key = ( + "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\" + "UserData\\{0}\\Products\\{1}" + ) + for user_guid in __utils__["reg.list_keys"](hive=hive_hku): + kwargs = { + "hive": hive_hku, + "key": uninstall_key.format(user_guid), + "use_32bit_registry": False, + } + if __utils__["reg.key_exists"](**kwargs): + for sub_key in __utils__["reg.list_keys"](**kwargs): + kwargs["sub_key"] = sub_key if skip_component(**kwargs): continue if skip_win_installer(**kwargs): @@ -714,29 +760,34 @@ def _get_reg_software(include_components=True, add_software(**kwargs) # While we have the user guid, we're gong to check userdata in HKLM - kwargs = {'hive': hive_hku, - 'key': product_key.format(user_guid), - 'use_32bit_registry': False} - if __utils__['reg.key_exists'](**kwargs): - for sub_key in __utils__['reg.list_keys'](**kwargs): - kwargs = {'hive': 'HKLM', - 'key': user_data_key.format(user_guid, sub_key), - 'use_32bit_registry': False} - if __utils__['reg.key_exists'](**kwargs): - kwargs['sub_key'] = 'InstallProperties' + kwargs = { + "hive": hive_hku, + "key": product_key.format(user_guid), + "use_32bit_registry": False, + } + if __utils__["reg.key_exists"](**kwargs): + for sub_key in __utils__["reg.list_keys"](**kwargs): + kwargs = { + "hive": "HKLM", + "key": user_data_key.format(user_guid, sub_key), + "use_32bit_registry": False, + } + if __utils__["reg.key_exists"](**kwargs): + kwargs["sub_key"] = "InstallProperties" if skip_component(**kwargs): continue add_software(**kwargs) # Uninstall for each user on the system (HKU), 32 bit - for user_guid in __utils__['reg.list_keys'](hive=hive_hku, - use_32bit_registry=True): - kwargs = {'hive': hive_hku, - 'key': uninstall_key.format(user_guid), - 'use_32bit_registry': True} - if __utils__['reg.key_exists'](**kwargs): - for sub_key in __utils__['reg.list_keys'](**kwargs): - kwargs['sub_key'] = sub_key + for user_guid in __utils__["reg.list_keys"](hive=hive_hku, use_32bit_registry=True): + kwargs = { + "hive": hive_hku, + "key": uninstall_key.format(user_guid), + "use_32bit_registry": True, + } + if __utils__["reg.key_exists"](**kwargs): + for sub_key in __utils__["reg.list_keys"](**kwargs): + kwargs["sub_key"] = sub_key if skip_component(**kwargs): continue if skip_win_installer(**kwargs): @@ -749,17 +800,21 @@ def _get_reg_software(include_components=True, continue add_software(**kwargs) - kwargs = {'hive': hive_hku, - 'key': product_key.format(user_guid), - 'use_32bit_registry': True} - if __utils__['reg.key_exists'](**kwargs): + kwargs = { + "hive": hive_hku, + "key": product_key.format(user_guid), + "use_32bit_registry": True, + } + if __utils__["reg.key_exists"](**kwargs): # While we have the user guid, we're going to check userdata in HKLM - for sub_key_2 in __utils__['reg.list_keys'](**kwargs): - kwargs = {'hive': 'HKLM', - 'key': user_data_key.format(user_guid, sub_key_2), - 'use_32bit_registry': True} - if __utils__['reg.key_exists'](**kwargs): - kwargs['sub_key'] = 'InstallProperties' + for sub_key_2 in __utils__["reg.list_keys"](**kwargs): + kwargs = { + "hive": "HKLM", + "key": user_data_key.format(user_guid, sub_key_2), + "use_32bit_registry": True, + } + if __utils__["reg.key_exists"](**kwargs): + kwargs["sub_key"] = "InstallProperties" if skip_component(**kwargs): continue add_software(**kwargs) @@ -768,7 +823,7 @@ def _get_reg_software(include_components=True, def _refresh_db_conditional(saltenv, **kwargs): - ''' + """ Internal use only in this module, has a different set of defaults and returns True or False. And supports checking the age of the existing generated metadata db, as well as ensure metadata db exists to begin with @@ -790,54 +845,56 @@ def _refresh_db_conditional(saltenv, **kwargs): bool: True Fetched or Cache uptodate, False to indicate an issue :codeauthor: Damon Atkins <https://github.com/damon-atkins> - ''' - force = salt.utils.data.is_true(kwargs.pop('force', False)) - failhard = salt.utils.data.is_true(kwargs.pop('failhard', False)) - expired_max = __opts__['winrepo_cache_expire_max'] - expired_min = __opts__['winrepo_cache_expire_min'] + """ + force = salt.utils.data.is_true(kwargs.pop("force", False)) + failhard = salt.utils.data.is_true(kwargs.pop("failhard", False)) + expired_max = __opts__["winrepo_cache_expire_max"] + expired_min = __opts__["winrepo_cache_expire_min"] repo_details = _get_repo_details(saltenv) # Skip force if age less than minimum age if force and expired_min > 0 and repo_details.winrepo_age < expired_min: log.info( - 'Refresh skipped, age of winrepo metadata in seconds (%s) is less ' - 'than winrepo_cache_expire_min (%s)', - repo_details.winrepo_age, expired_min + "Refresh skipped, age of winrepo metadata in seconds (%s) is less " + "than winrepo_cache_expire_min (%s)", + repo_details.winrepo_age, + expired_min, ) force = False # winrepo_age is -1 if repo db does not exist - refresh = True if force \ - or repo_details.winrepo_age == -1 \ - or repo_details.winrepo_age > expired_max \ + refresh = ( + True + if force + or repo_details.winrepo_age == -1 + or repo_details.winrepo_age > expired_max else False + ) if not refresh: log.debug( - 'Using existing pkg metadata db for saltenv \'%s\' (age is %s)', - saltenv, datetime.timedelta(seconds=repo_details.winrepo_age) + "Using existing pkg metadata db for saltenv '%s' (age is %s)", + saltenv, + datetime.timedelta(seconds=repo_details.winrepo_age), ) return True if repo_details.winrepo_age == -1: # no repo meta db - log.debug( - 'No winrepo.p cache file for saltenv \'%s\', creating one now', - saltenv - ) + log.debug("No winrepo.p cache file for saltenv '%s', creating one now", saltenv) results = refresh_db(saltenv=saltenv, verbose=False, failhard=failhard) try: # Return True if there were no failed winrepo SLS files, and False if # failures were reported. - return not bool(results.get('failed', 0)) + return not bool(results.get("failed", 0)) except AttributeError: return False def refresh_db(**kwargs): - r''' + r""" Generates the local software metadata database (`winrepo.p`) on the minion. The database is stored in a serialized format located by default at the following location: @@ -920,94 +977,93 @@ def refresh_db(**kwargs): salt '*' pkg.refresh_db salt '*' pkg.refresh_db saltenv=base - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) - saltenv = kwargs.pop('saltenv', 'base') - verbose = salt.utils.data.is_true(kwargs.pop('verbose', False)) - failhard = salt.utils.data.is_true(kwargs.pop('failhard', True)) - __context__.pop('winrepo.data', None) + saltenv = kwargs.pop("saltenv", "base") + verbose = salt.utils.data.is_true(kwargs.pop("verbose", False)) + failhard = salt.utils.data.is_true(kwargs.pop("failhard", True)) + __context__.pop("winrepo.data", None) repo_details = _get_repo_details(saltenv) log.debug( - 'Refreshing pkg metadata db for saltenv \'%s\' (age of existing ' - 'metadata is %s)', - saltenv, datetime.timedelta(seconds=repo_details.winrepo_age) + "Refreshing pkg metadata db for saltenv '%s' (age of existing " + "metadata is %s)", + saltenv, + datetime.timedelta(seconds=repo_details.winrepo_age), ) # Clear minion repo-ng cache see #35342 discussion - log.info('Removing all *.sls files under \'%s\'', repo_details.local_dest) + log.info("Removing all *.sls files under '%s'", repo_details.local_dest) failed = [] - for root, _, files in salt.utils.path.os_walk(repo_details.local_dest, followlinks=False): + for root, _, files in salt.utils.path.os_walk( + repo_details.local_dest, followlinks=False + ): for name in files: - if name.endswith('.sls'): + if name.endswith(".sls"): full_filename = os.path.join(root, name) try: os.remove(full_filename) except OSError as exc: if exc.errno != errno.ENOENT: - log.error('Failed to remove %s: %s', full_filename, exc) + log.error("Failed to remove %s: %s", full_filename, exc) failed.append(full_filename) if failed: raise CommandExecutionError( - 'Failed to clear one or more winrepo cache files', - info={'failed': failed} + "Failed to clear one or more winrepo cache files", info={"failed": failed} ) # Cache repo-ng locally - log.info('Fetching *.sls files from {0}'.format(repo_details.winrepo_source_dir)) - __salt__['cp.cache_dir']( + log.info("Fetching *.sls files from {0}".format(repo_details.winrepo_source_dir)) + __salt__["cp.cache_dir"]( path=repo_details.winrepo_source_dir, saltenv=saltenv, - include_pat='*.sls', - exclude_pat=r'E@\/\..*?\/' # Exclude all hidden directories (.git) + include_pat="*.sls", + exclude_pat=r"E@\/\..*?\/", # Exclude all hidden directories (.git) ) return genrepo(saltenv=saltenv, verbose=verbose, failhard=failhard) def _get_repo_details(saltenv): - ''' + """ Return repo details for the specified saltenv as a namedtuple - ''' - contextkey = 'winrepo._get_repo_details.{0}'.format(saltenv) + """ + contextkey = "winrepo._get_repo_details.{0}".format(saltenv) if contextkey in __context__: (winrepo_source_dir, local_dest, winrepo_file) = __context__[contextkey] else: - winrepo_source_dir = __opts__['winrepo_source_dir'] - dirs = [__opts__['cachedir'], 'files', saltenv] + winrepo_source_dir = __opts__["winrepo_source_dir"] + dirs = [__opts__["cachedir"], "files", saltenv] url_parts = _urlparse(winrepo_source_dir) dirs.append(url_parts.netloc) - dirs.extend(url_parts.path.strip('/').split('/')) + dirs.extend(url_parts.path.strip("/").split("/")) local_dest = os.sep.join(dirs) - winrepo_file = os.path.join(local_dest, 'winrepo.p') # Default + winrepo_file = os.path.join(local_dest, "winrepo.p") # Default # Check for a valid windows file name - if not re.search(r'[\/:*?"<>|]', - __opts__['winrepo_cachefile'], - flags=re.IGNORECASE): - winrepo_file = os.path.join( - local_dest, - __opts__['winrepo_cachefile'] - ) + if not re.search( + r'[\/:*?"<>|]', __opts__["winrepo_cachefile"], flags=re.IGNORECASE + ): + winrepo_file = os.path.join(local_dest, __opts__["winrepo_cachefile"]) else: log.error( - 'minion configuration option \'winrepo_cachefile\' has been ' - 'ignored as its value (%s) is invalid. Please ensure this ' - 'option is set to a valid filename.', - __opts__['winrepo_cachefile'] + "minion configuration option 'winrepo_cachefile' has been " + "ignored as its value (%s) is invalid. Please ensure this " + "option is set to a valid filename.", + __opts__["winrepo_cachefile"], ) # Do some safety checks on the repo_path as its contents can be removed, # this includes check for bad coding - system_root = os.environ.get('SystemRoot', r'C:\Windows') + system_root = os.environ.get("SystemRoot", r"C:\Windows") if not salt.utils.path.safe_path( - path=local_dest, - allow_path='\\'.join([system_root, 'TEMP'])): + path=local_dest, allow_path="\\".join([system_root, "TEMP"]) + ): raise CommandExecutionError( - 'Attempting to delete files from a possibly unsafe location: ' - '{0}'.format(local_dest) + "Attempting to delete files from a possibly unsafe location: " + "{0}".format(local_dest) ) __context__[contextkey] = (winrepo_source_dir, local_dest, winrepo_file) @@ -1017,7 +1073,7 @@ def _get_repo_details(saltenv): except OSError as exc: if exc.errno != errno.EEXIST: raise CommandExecutionError( - 'Failed to create {0}: {1}'.format(local_dest, exc) + "Failed to create {0}: {1}".format(local_dest, exc) ) winrepo_age = -1 @@ -1028,24 +1084,24 @@ def _get_repo_details(saltenv): except OSError as exc: if exc.errno != errno.ENOENT: raise CommandExecutionError( - 'Failed to get age of {0}: {1}'.format(winrepo_file, exc) + "Failed to get age of {0}: {1}".format(winrepo_file, exc) ) except AttributeError: # Shouldn't happen but log if it does - log.warning('st_mtime missing from stat result %s', stat_result) + log.warning("st_mtime missing from stat result %s", stat_result) except TypeError: # Shouldn't happen but log if it does - log.warning('mtime of %s (%s) is an invalid type', winrepo_file, mtime) + log.warning("mtime of %s (%s) is an invalid type", winrepo_file, mtime) repo_details = collections.namedtuple( - 'RepoDetails', - ('winrepo_source_dir', 'local_dest', 'winrepo_file', 'winrepo_age') + "RepoDetails", + ("winrepo_source_dir", "local_dest", "winrepo_file", "winrepo_age"), ) return repo_details(winrepo_source_dir, local_dest, winrepo_file, winrepo_age) def genrepo(**kwargs): - ''' + """ Generate package metadata db based on files within the winrepo_source_dir Kwargs: @@ -1076,72 +1132,73 @@ def genrepo(**kwargs): salt-run pkg.genrepo salt -G 'os:windows' pkg.genrepo verbose=true failhard=false salt -G 'os:windows' pkg.genrepo saltenv=base - ''' - saltenv = kwargs.pop('saltenv', 'base') - verbose = salt.utils.data.is_true(kwargs.pop('verbose', False)) - failhard = salt.utils.data.is_true(kwargs.pop('failhard', True)) + """ + saltenv = kwargs.pop("saltenv", "base") + verbose = salt.utils.data.is_true(kwargs.pop("verbose", False)) + failhard = salt.utils.data.is_true(kwargs.pop("failhard", True)) ret = {} successful_verbose = {} total_files_processed = 0 - ret['repo'] = {} - ret['errors'] = {} + ret["repo"] = {} + ret["errors"] = {} repo_details = _get_repo_details(saltenv) - for root, _, files in salt.utils.path.os_walk(repo_details.local_dest, followlinks=False): + for root, _, files in salt.utils.path.os_walk( + repo_details.local_dest, followlinks=False + ): # Skip hidden directories (.git) - if re.search(r'[\\/]\..*', root): - log.debug('Skipping files in directory: {0}'.format(root)) + if re.search(r"[\\/]\..*", root): + log.debug("Skipping files in directory: {0}".format(root)) continue short_path = os.path.relpath(root, repo_details.local_dest) - if short_path == '.': - short_path = '' + if short_path == ".": + short_path = "" for name in files: - if name.endswith('.sls'): + if name.endswith(".sls"): total_files_processed += 1 _repo_process_pkg_sls( os.path.join(root, name), os.path.join(short_path, name), ret, - successful_verbose - ) + successful_verbose, + ) serial = salt.payload.Serial(__opts__) - with salt.utils.files.fopen(repo_details.winrepo_file, 'wb') as repo_cache: + with salt.utils.files.fopen(repo_details.winrepo_file, "wb") as repo_cache: repo_cache.write(serial.dumps(ret)) # For some reason we can not save ret into __context__['winrepo.data'] as this breaks due to utf8 issues successful_count = len(successful_verbose) - error_count = len(ret['errors']) + error_count = len(ret["errors"]) if verbose: results = { - 'total': total_files_processed, - 'success': successful_count, - 'failed': error_count, - 'success_list': successful_verbose, - 'failed_list': ret['errors'] - } + "total": total_files_processed, + "success": successful_count, + "failed": error_count, + "success_list": successful_verbose, + "failed_list": ret["errors"], + } else: if error_count > 0: results = { - 'total': total_files_processed, - 'success': successful_count, - 'failed': error_count, - 'failed_list': ret['errors'] - } + "total": total_files_processed, + "success": successful_count, + "failed": error_count, + "failed_list": ret["errors"], + } else: results = { - 'total': total_files_processed, - 'success': successful_count, - 'failed': error_count - } + "total": total_files_processed, + "success": successful_count, + "failed": error_count, + } if error_count > 0 and failhard: raise CommandExecutionError( - 'Error occurred while generating repo db', - info=results + "Error occurred while generating repo db", info=results ) else: return results @@ -1151,32 +1208,36 @@ def _repo_process_pkg_sls(filename, short_path_name, ret, successful_verbose): renderers = salt.loader.render(__opts__, __salt__) def _failed_compile(prefix_msg, error_msg): - log.error('{0} \'{1}\': {2} '.format(prefix_msg, short_path_name, error_msg)) - ret.setdefault('errors', {})[short_path_name] = ['{0}, {1} '.format(prefix_msg, error_msg)] + log.error("{0} '{1}': {2} ".format(prefix_msg, short_path_name, error_msg)) + ret.setdefault("errors", {})[short_path_name] = [ + "{0}, {1} ".format(prefix_msg, error_msg) + ] return False try: config = salt.template.compile_template( filename, renderers, - __opts__['renderer'], - __opts__.get('renderer_blacklist', ''), - __opts__.get('renderer_whitelist', '')) + __opts__["renderer"], + __opts__.get("renderer_blacklist", ""), + __opts__.get("renderer_whitelist", ""), + ) except SaltRenderError as exc: - return _failed_compile('Failed to compile', exc) + return _failed_compile("Failed to compile", exc) except Exception as exc: # pylint: disable=broad-except - return _failed_compile('Failed to read', exc) + return _failed_compile("Failed to read", exc) if config and isinstance(config, dict): revmap = {} errors = [] for pkgname, version_list in six.iteritems(config): - if pkgname in ret['repo']: + if pkgname in ret["repo"]: log.error( - 'package \'%s\' within \'%s\' already defined, skipping', - pkgname, short_path_name + "package '%s' within '%s' already defined, skipping", + pkgname, + short_path_name, ) - errors.append('package \'{0}\' already defined'.format(pkgname)) + errors.append("package '{0}' already defined".format(pkgname)) break for version_str, repodata in six.iteritems(version_list): # Ensure version is a string/unicode @@ -1184,99 +1245,108 @@ def _repo_process_pkg_sls(filename, short_path_name, ret, successful_verbose): log.error( "package '%s' within '%s', version number %s' " "is not a string", - pkgname, short_path_name, version_str + pkgname, + short_path_name, + version_str, ) errors.append( - 'package \'{0}\', version number {1} ' - 'is not a string'.format(pkgname, version_str) + "package '{0}', version number {1} " + "is not a string".format(pkgname, version_str) ) continue # Ensure version contains a dict if not isinstance(repodata, dict): log.error( "package '%s' within '%s', repo data for " - 'version number %s is not defined as a dictionary', - pkgname, short_path_name, version_str + "version number %s is not defined as a dictionary", + pkgname, + short_path_name, + version_str, ) errors.append( - 'package \'{0}\', repo data for ' - 'version number {1} is not defined as a dictionary' - .format(pkgname, version_str) + "package '{0}', repo data for " + "version number {1} is not defined as a dictionary".format( + pkgname, version_str + ) ) continue - revmap[repodata['full_name']] = pkgname + revmap[repodata["full_name"]] = pkgname if errors: - ret.setdefault('errors', {})[short_path_name] = errors + ret.setdefault("errors", {})[short_path_name] = errors else: - ret.setdefault('repo', {}).update(config) - ret.setdefault('name_map', {}).update(revmap) + ret.setdefault("repo", {}).update(config) + ret.setdefault("name_map", {}).update(revmap) successful_verbose[short_path_name] = list(config.keys()) elif config: - return _failed_compile('Compiled contents', 'not a dictionary/hash') + return _failed_compile("Compiled contents", "not a dictionary/hash") else: - log.debug('No data within \'%s\' after processing', short_path_name) + log.debug("No data within '%s' after processing", short_path_name) # no pkgname found after render successful_verbose[short_path_name] = [] def _get_source_sum(source_hash, file_path, saltenv): - ''' + """ Extract the hash sum, whether it is in a remote hash file, or just a string. - ''' + """ ret = dict() - schemes = ('salt', 'http', 'https', 'ftp', 'swift', 's3', 'file') - invalid_hash_msg = ("Source hash '{0}' format is invalid. It must be in " - "the format <hash type>=<hash>").format(source_hash) + schemes = ("salt", "http", "https", "ftp", "swift", "s3", "file") + invalid_hash_msg = ( + "Source hash '{0}' format is invalid. It must be in " + "the format <hash type>=<hash>" + ).format(source_hash) source_hash = six.text_type(source_hash) source_hash_scheme = _urlparse(source_hash).scheme if source_hash_scheme in schemes: # The source_hash is a file on a server - cached_hash_file = __salt__['cp.cache_file'](source_hash, saltenv) + cached_hash_file = __salt__["cp.cache_file"](source_hash, saltenv) if not cached_hash_file: - raise CommandExecutionError(('Source hash file {0} not' - ' found').format(source_hash)) + raise CommandExecutionError( + ("Source hash file {0} not" " found").format(source_hash) + ) - ret = __salt__['file.extract_hash'](cached_hash_file, '', file_path) + ret = __salt__["file.extract_hash"](cached_hash_file, "", file_path) if ret is None: raise SaltInvocationError(invalid_hash_msg) else: # The source_hash is a hash string - items = source_hash.split('=', 1) + items = source_hash.split("=", 1) if len(items) != 2: - invalid_hash_msg = ('{0}, or it must be a supported protocol' - ': {1}').format(invalid_hash_msg, - ', '.join(schemes)) + invalid_hash_msg = ( + "{0}, or it must be a supported protocol" ": {1}" + ).format(invalid_hash_msg, ", ".join(schemes)) raise SaltInvocationError(invalid_hash_msg) - ret['hash_type'], ret['hsum'] = [item.strip().lower() for item in items] + ret["hash_type"], ret["hsum"] = [item.strip().lower() for item in items] return ret def _get_msiexec(use_msiexec): - ''' + """ Return if msiexec.exe will be used and the command to invoke it. - ''' + """ if use_msiexec is False: - return False, '' + return False, "" if isinstance(use_msiexec, six.string_types): if os.path.isfile(use_msiexec): return True, use_msiexec else: log.warning( "msiexec path '%s' not found. Using system registered " - "msiexec instead", use_msiexec + "msiexec instead", + use_msiexec, ) use_msiexec = True if use_msiexec is True: - return True, 'msiexec' + return True, "msiexec" def install(name=None, refresh=False, pkgs=None, **kwargs): - r''' + r""" Install the passed package(s) on the system using winrepo Args: @@ -1414,32 +1484,34 @@ def install(name=None, refresh=False, pkgs=None, **kwargs): cache_dir: True install_flags: '/USEFILE=C:\salt\var\cache\salt\minion\files\base\win\repo\ntp\install.ini' uninstaller: 'NTP/uninst.exe' - ''' + """ ret = {} - saltenv = kwargs.pop('saltenv', 'base') + saltenv = kwargs.pop("saltenv", "base") refresh = salt.utils.data.is_true(refresh) # no need to call _refresh_db_conditional as list_pkgs will do it # Make sure name or pkgs is passed if not name and not pkgs: - return 'Must pass a single package or a list of packages' + return "Must pass a single package or a list of packages" # Ignore pkg_type from parse_targets, Windows does not support the # "sources" argument - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs, **kwargs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs, **kwargs)[0] if len(pkg_params) > 1: - if kwargs.get('extra_install_flags') is not None: - log.warning('\'extra_install_flags\' argument will be ignored for ' - 'multiple package targets') + if kwargs.get("extra_install_flags") is not None: + log.warning( + "'extra_install_flags' argument will be ignored for " + "multiple package targets" + ) # Windows expects an Options dictionary containing 'version' for pkg in pkg_params: - pkg_params[pkg] = {'version': pkg_params[pkg]} + pkg_params[pkg] = {"version": pkg_params[pkg]} if not pkg_params: - log.error('No package definition found') + log.error("No package definition found") return {} if not pkgs and len(pkg_params) == 1: @@ -1447,15 +1519,15 @@ def install(name=None, refresh=False, pkgs=None, **kwargs): # parameter pkg_params = { name: { - 'version': kwargs.get('version'), - 'extra_install_flags': kwargs.get('extra_install_flags') + "version": kwargs.get("version"), + "extra_install_flags": kwargs.get("extra_install_flags"), } } elif len(pkg_params) == 1: # A dict of packages was passed, but it contains only 1 key, so we need # to add the 'extra_install_flags' pkg = next(iter(pkg_params)) - pkg_params[pkg]['extra_install_flags'] = kwargs.get('extra_install_flags') + pkg_params[pkg]["extra_install_flags"] = kwargs.get("extra_install_flags") # Get a list of currently installed software for comparison at the end old = list_pkgs(saltenv=saltenv, refresh=refresh, versions_as_list=True) @@ -1469,11 +1541,11 @@ def install(name=None, refresh=False, pkgs=None, **kwargs): # Make sure pkginfo was found if not pkginfo: - log.error('Unable to locate package %s', pkg_name) - ret[pkg_name] = 'Unable to locate package {0}'.format(pkg_name) + log.error("Unable to locate package %s", pkg_name) + ret[pkg_name] = "Unable to locate package {0}".format(pkg_name) continue - version_num = options.get('version') + version_num = options.get("version") # Using the salt cmdline with version=5.3 might be interpreted # as a float it must be converted to a string in order for # string matching to work. @@ -1483,14 +1555,17 @@ def install(name=None, refresh=False, pkgs=None, **kwargs): # If the version was not passed, version_num will be None if not version_num: if pkg_name in old: - log.debug('pkg.install: \'%s\' version \'%s\' is already installed', - pkg_name, old[pkg_name][0]) + log.debug( + "pkg.install: '%s' version '%s' is already installed", + pkg_name, + old[pkg_name][0], + ) continue # Get the most recent version number available from winrepo.p # May also return `latest` or an empty string version_num = _get_latest_pkg_version(pkginfo) - if version_num == 'latest' and 'latest' not in pkginfo: + if version_num == "latest" and "latest" not in pkginfo: # Get the most recent version number available from winrepo.p # May also return `latest` or an empty string version_num = _get_latest_pkg_version(pkginfo) @@ -1498,229 +1573,251 @@ def install(name=None, refresh=False, pkgs=None, **kwargs): # Check if the version is already installed if version_num in old.get(pkg_name, []): # Desired version number already installed - log.debug('pkg.install: \'%s\' version \'%s\' is already installed', - pkg_name, version_num) + log.debug( + "pkg.install: '%s' version '%s' is already installed", + pkg_name, + version_num, + ) continue # If version number not installed, is the version available? - elif version_num != 'latest' and version_num not in pkginfo: - log.error('Version %s not found for package %s', - version_num, pkg_name) - ret[pkg_name] = {'not found': version_num} + elif version_num != "latest" and version_num not in pkginfo: + log.error("Version %s not found for package %s", version_num, pkg_name) + ret[pkg_name] = {"not found": version_num} continue # Get the installer settings from winrepo.p - installer = pkginfo[version_num].get('installer', '') - cache_dir = pkginfo[version_num].get('cache_dir', False) - cache_file = pkginfo[version_num].get('cache_file', '') + installer = pkginfo[version_num].get("installer", "") + cache_dir = pkginfo[version_num].get("cache_dir", False) + cache_file = pkginfo[version_num].get("cache_file", "") # Is there an installer configured? if not installer: - log.error('No installer configured for version %s of package %s', - version_num, pkg_name) - ret[pkg_name] = {'no installer': version_num} + log.error( + "No installer configured for version %s of package %s", + version_num, + pkg_name, + ) + ret[pkg_name] = {"no installer": version_num} continue # Is the installer in a location that requires caching - if __salt__['config.valid_fileproto'](installer): + if __salt__["config.valid_fileproto"](installer): # Check for the 'cache_dir' parameter in the .sls file # If true, the entire directory will be cached instead of the # individual file. This is useful for installations that are not # single files - if cache_dir and installer.startswith('salt:'): + if cache_dir and installer.startswith("salt:"): path, _ = os.path.split(installer) - __salt__['cp.cache_dir'](path=path, - saltenv=saltenv, - include_empty=False, - include_pat=None, - exclude_pat='E@init.sls$') + __salt__["cp.cache_dir"]( + path=path, + saltenv=saltenv, + include_empty=False, + include_pat=None, + exclude_pat="E@init.sls$", + ) # Check to see if the cache_file is cached... if passed - if cache_file and cache_file.startswith('salt:'): + if cache_file and cache_file.startswith("salt:"): # Check to see if the file is cached - cached_file = __salt__['cp.is_cached'](cache_file, saltenv) + cached_file = __salt__["cp.is_cached"](cache_file, saltenv) if not cached_file: - cached_file = __salt__['cp.cache_file'](cache_file, saltenv) + cached_file = __salt__["cp.cache_file"](cache_file, saltenv) # Make sure the cached file is the same as the source - if __salt__['cp.hash_file'](cache_file, saltenv) != \ - __salt__['cp.hash_file'](cached_file): - cached_file = __salt__['cp.cache_file'](cache_file, saltenv) + if __salt__["cp.hash_file"](cache_file, saltenv) != __salt__[ + "cp.hash_file" + ](cached_file): + cached_file = __salt__["cp.cache_file"](cache_file, saltenv) # Check if the cache_file was cached successfully if not cached_file: - log.error('Unable to cache %s', cache_file) - ret[pkg_name] = { - 'failed to cache cache_file': cache_file - } + log.error("Unable to cache %s", cache_file) + ret[pkg_name] = {"failed to cache cache_file": cache_file} continue # Check to see if the installer is cached - cached_pkg = __salt__['cp.is_cached'](installer, saltenv) + cached_pkg = __salt__["cp.is_cached"](installer, saltenv) if not cached_pkg: # It's not cached. Cache it, mate. - cached_pkg = __salt__['cp.cache_file'](installer, saltenv) + cached_pkg = __salt__["cp.cache_file"](installer, saltenv) # Check if the installer was cached successfully if not cached_pkg: log.error( - 'Unable to cache file %s from saltenv: %s', - installer, saltenv + "Unable to cache file %s from saltenv: %s", installer, saltenv ) - ret[pkg_name] = {'unable to cache': installer} + ret[pkg_name] = {"unable to cache": installer} continue # Compare the hash of the cached installer to the source only if the # file is hosted on salt: - if installer.startswith('salt:'): - if __salt__['cp.hash_file'](installer, saltenv) != \ - __salt__['cp.hash_file'](cached_pkg): + if installer.startswith("salt:"): + if __salt__["cp.hash_file"](installer, saltenv) != __salt__[ + "cp.hash_file" + ](cached_pkg): try: - cached_pkg = __salt__['cp.cache_file'](installer, saltenv) + cached_pkg = __salt__["cp.cache_file"](installer, saltenv) except MinionError as exc: - return '{0}: {1}'.format(exc, installer) + return "{0}: {1}".format(exc, installer) # Check if the installer was cached successfully if not cached_pkg: - log.error('Unable to cache %s', installer) - ret[pkg_name] = {'unable to cache': installer} + log.error("Unable to cache %s", installer) + ret[pkg_name] = {"unable to cache": installer} continue else: # Run the installer directly (not hosted on salt:, https:, etc.) cached_pkg = installer # Fix non-windows slashes - cached_pkg = cached_pkg.replace('/', '\\') + cached_pkg = cached_pkg.replace("/", "\\") cache_path = os.path.dirname(cached_pkg) # Compare the hash sums - source_hash = pkginfo[version_num].get('source_hash', False) + source_hash = pkginfo[version_num].get("source_hash", False) if source_hash: source_sum = _get_source_sum(source_hash, cached_pkg, saltenv) - log.debug('pkg.install: Source %s hash: %s', - source_sum['hash_type'], source_sum['hsum']) + log.debug( + "pkg.install: Source %s hash: %s", + source_sum["hash_type"], + source_sum["hsum"], + ) - cached_pkg_sum = salt.utils.hashutils.get_hash(cached_pkg, - source_sum['hash_type']) - log.debug('pkg.install: Package %s hash: %s', - source_sum['hash_type'], cached_pkg_sum) + cached_pkg_sum = salt.utils.hashutils.get_hash( + cached_pkg, source_sum["hash_type"] + ) + log.debug( + "pkg.install: Package %s hash: %s", + source_sum["hash_type"], + cached_pkg_sum, + ) - if source_sum['hsum'] != cached_pkg_sum: + if source_sum["hsum"] != cached_pkg_sum: raise SaltInvocationError( - ("Source hash '{0}' does not match package hash" - " '{1}'").format(source_sum['hsum'], cached_pkg_sum) + ("Source hash '{0}' does not match package hash" " '{1}'").format( + source_sum["hsum"], cached_pkg_sum + ) ) - log.debug('pkg.install: Source hash matches package hash.') + log.debug("pkg.install: Source hash matches package hash.") # Get install flags - install_flags = pkginfo[version_num].get('install_flags', '') - if options and options.get('extra_install_flags'): - install_flags = '{0} {1}'.format( - install_flags, - options.get('extra_install_flags', '') + install_flags = pkginfo[version_num].get("install_flags", "") + if options and options.get("extra_install_flags"): + install_flags = "{0} {1}".format( + install_flags, options.get("extra_install_flags", "") ) # Compute msiexec string - use_msiexec, msiexec = _get_msiexec(pkginfo[version_num].get('msiexec', False)) + use_msiexec, msiexec = _get_msiexec(pkginfo[version_num].get("msiexec", False)) # Build cmd and arguments # cmd and arguments must be separated for use with the task scheduler - cmd_shell = os.getenv('ComSpec', '{0}\\system32\\cmd.exe'.format(os.getenv('WINDIR'))) + cmd_shell = os.getenv( + "ComSpec", "{0}\\system32\\cmd.exe".format(os.getenv("WINDIR")) + ) if use_msiexec: arguments = '"{0}" /I "{1}"'.format(msiexec, cached_pkg) - if pkginfo[version_num].get('allusers', True): - arguments = '{0} ALLUSERS=1'.format(arguments) + if pkginfo[version_num].get("allusers", True): + arguments = "{0} ALLUSERS=1".format(arguments) else: arguments = '"{0}"'.format(cached_pkg) if install_flags: - arguments = '{0} {1}'.format(arguments, install_flags) + arguments = "{0} {1}".format(arguments, install_flags) # Install the software # Check Use Scheduler Option - if pkginfo[version_num].get('use_scheduler', False): + if pkginfo[version_num].get("use_scheduler", False): # Create Scheduled Task - __salt__['task.create_task'](name='update-salt-software', - user_name='System', - force=True, - action_type='Execute', - cmd=cmd_shell, - arguments='/s /c "{0}"'.format(arguments), - start_in=cache_path, - trigger_type='Once', - start_date='1975-01-01', - start_time='01:00', - ac_only=False, - stop_if_on_batteries=False) + __salt__["task.create_task"]( + name="update-salt-software", + user_name="System", + force=True, + action_type="Execute", + cmd=cmd_shell, + arguments='/s /c "{0}"'.format(arguments), + start_in=cache_path, + trigger_type="Once", + start_date="1975-01-01", + start_time="01:00", + ac_only=False, + stop_if_on_batteries=False, + ) # Run Scheduled Task # Special handling for installing salt - if re.search(r'salt[\s_.-]*minion', - pkg_name, - flags=re.IGNORECASE + re.UNICODE) is not None: - ret[pkg_name] = {'install status': 'task started'} - if not __salt__['task.run'](name='update-salt-software'): - log.error('Failed to install %s', pkg_name) - log.error('Scheduled Task failed to run') - ret[pkg_name] = {'install status': 'failed'} + if ( + re.search( + r"salt[\s_.-]*minion", pkg_name, flags=re.IGNORECASE + re.UNICODE + ) + is not None + ): + ret[pkg_name] = {"install status": "task started"} + if not __salt__["task.run"](name="update-salt-software"): + log.error("Failed to install %s", pkg_name) + log.error("Scheduled Task failed to run") + ret[pkg_name] = {"install status": "failed"} else: # Make sure the task is running, try for 5 secs t_end = time.time() + 5 while time.time() < t_end: time.sleep(0.25) - task_running = __salt__['task.status']( - 'update-salt-software') == 'Running' + task_running = ( + __salt__["task.status"]("update-salt-software") == "Running" + ) if task_running: break if not task_running: - log.error('Failed to install %s', pkg_name) - log.error('Scheduled Task failed to run') - ret[pkg_name] = {'install status': 'failed'} + log.error("Failed to install %s", pkg_name) + log.error("Scheduled Task failed to run") + ret[pkg_name] = {"install status": "failed"} # All other packages run with task scheduler else: - if not __salt__['task.run_wait'](name='update-salt-software'): - log.error('Failed to install %s', pkg_name) - log.error('Scheduled Task failed to run') - ret[pkg_name] = {'install status': 'failed'} + if not __salt__["task.run_wait"](name="update-salt-software"): + log.error("Failed to install %s", pkg_name) + log.error("Scheduled Task failed to run") + ret[pkg_name] = {"install status": "failed"} else: # Launch the command - result = __salt__['cmd.run_all']('"{0}" /s /c "{1}"'.format(cmd_shell, arguments), - cache_path, - output_loglevel='trace', - python_shell=False, - redirect_stderr=True) - if not result['retcode']: - ret[pkg_name] = {'install status': 'success'} + result = __salt__["cmd.run_all"]( + '"{0}" /s /c "{1}"'.format(cmd_shell, arguments), + cache_path, + output_loglevel="trace", + python_shell=False, + redirect_stderr=True, + ) + if not result["retcode"]: + ret[pkg_name] = {"install status": "success"} changed.append(pkg_name) - elif result['retcode'] == 3010: + elif result["retcode"] == 3010: # 3010 is ERROR_SUCCESS_REBOOT_REQUIRED - report_reboot_exit_codes = kwargs.pop( - 'report_reboot_exit_codes', True) + report_reboot_exit_codes = kwargs.pop("report_reboot_exit_codes", True) if report_reboot_exit_codes: - __salt__['system.set_reboot_required_witnessed']() - ret[pkg_name] = {'install status': 'success, reboot required'} + __salt__["system.set_reboot_required_witnessed"]() + ret[pkg_name] = {"install status": "success, reboot required"} changed.append(pkg_name) - elif result['retcode'] == 1641: + elif result["retcode"] == 1641: # 1641 is ERROR_SUCCESS_REBOOT_INITIATED - ret[pkg_name] = {'install status': 'success, reboot initiated'} + ret[pkg_name] = {"install status": "success, reboot initiated"} changed.append(pkg_name) else: - log.error('Failed to install %s', pkg_name) - log.error('retcode %s', result['retcode']) - log.error('installer output: %s', result['stdout']) - ret[pkg_name] = {'install status': 'failed'} + log.error("Failed to install %s", pkg_name) + log.error("retcode %s", result["retcode"]) + log.error("installer output: %s", result["stdout"]) + ret[pkg_name] = {"install status": "failed"} # Get a new list of installed software new = list_pkgs(saltenv=saltenv, refresh=False) # Take the "old" package list and convert the values to strings in # preparation for the comparison below. - __salt__['pkg_resource.stringify'](old) + __salt__["pkg_resource.stringify"](old) # Check for changes in the registry difference = salt.utils.data.compare_dicts(old, new) @@ -1733,7 +1830,7 @@ def install(name=None, refresh=False, pkgs=None, **kwargs): def upgrade(**kwargs): - ''' + """ Upgrade all software. Currently not implemented Kwargs: @@ -1751,11 +1848,15 @@ def upgrade(**kwargs): .. code-block:: bash salt '*' pkg.upgrade - ''' - log.warning('pkg.upgrade not implemented on Windows yet') - refresh = salt.utils.data.is_true(kwargs.get('refresh', True)) - saltenv = kwargs.get('saltenv', 'base') - log.warning('pkg.upgrade not implemented on Windows yet refresh:%s saltenv:%s', refresh, saltenv) + """ + log.warning("pkg.upgrade not implemented on Windows yet") + refresh = salt.utils.data.is_true(kwargs.get("refresh", True)) + saltenv = kwargs.get("saltenv", "base") + log.warning( + "pkg.upgrade not implemented on Windows yet refresh:%s saltenv:%s", + refresh, + saltenv, + ) # Uncomment the below once pkg.upgrade has been implemented # if salt.utils.data.is_true(refresh): @@ -1764,7 +1865,7 @@ def upgrade(**kwargs): def remove(name=None, pkgs=None, **kwargs): - ''' + """ Remove the passed package(s) from the system using winrepo .. versionadded:: 0.16.0 @@ -1809,18 +1910,18 @@ def remove(name=None, pkgs=None, **kwargs): salt '*' pkg.remove <package name> salt '*' pkg.remove <package1>,<package2>,<package3> salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' - saltenv = kwargs.get('saltenv', 'base') - refresh = salt.utils.data.is_true(kwargs.get('refresh', False)) + """ + saltenv = kwargs.get("saltenv", "base") + refresh = salt.utils.data.is_true(kwargs.get("refresh", False)) # no need to call _refresh_db_conditional as list_pkgs will do it ret = {} # Make sure name or pkgs is passed if not name and not pkgs: - return 'Must pass a single package or a list of packages' + return "Must pass a single package or a list of packages" # Get package parameters - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs, **kwargs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs, **kwargs)[0] # Get a list of currently installed software for comparison at the end old = list_pkgs(saltenv=saltenv, refresh=refresh, versions_as_list=True) @@ -1834,15 +1935,17 @@ def remove(name=None, pkgs=None, **kwargs): # Make sure pkginfo was found if not pkginfo: - msg = 'Unable to locate package {0}'.format(pkgname) + msg = "Unable to locate package {0}".format(pkgname) log.error(msg) ret[pkgname] = msg continue # Check to see if package is installed on the system if pkgname not in old: - log.debug('%s %s not installed', pkgname, version_num if version_num else '') - ret[pkgname] = {'current': 'not installed'} + log.debug( + "%s %s not installed", pkgname, version_num if version_num else "" + ) + ret[pkgname] = {"current": "not installed"} continue removal_targets = [] @@ -1856,9 +1959,13 @@ def remove(name=None, pkgs=None, **kwargs): # At least one version of the software is installed. if version_num is None: for ver_install in old[pkgname]: - if ver_install not in pkginfo and 'latest' in pkginfo: - log.debug('%s %s using package latest entry to to remove', pkgname, version_num) - removal_targets.append('latest') + if ver_install not in pkginfo and "latest" in pkginfo: + log.debug( + "%s %s using package latest entry to to remove", + pkgname, + version_num, + ) + removal_targets.append("latest") else: removal_targets.append(ver_install) else: @@ -1867,84 +1974,85 @@ def remove(name=None, pkgs=None, **kwargs): if version_num in old[pkgname]: removal_targets.append(version_num) else: - log.debug('%s %s not installed', pkgname, version_num) - ret[pkgname] = {'current': '{0} not installed'.format(version_num)} + log.debug("%s %s not installed", pkgname, version_num) + ret[pkgname] = {"current": "{0} not installed".format(version_num)} continue - elif 'latest' in pkginfo: + elif "latest" in pkginfo: # we do not have version entry, assume software can self upgrade and use latest - log.debug('%s %s using package latest entry to to remove', pkgname, version_num) - removal_targets.append('latest') + log.debug( + "%s %s using package latest entry to to remove", + pkgname, + version_num, + ) + removal_targets.append("latest") if not removal_targets: - log.error('%s %s no definition to remove this version', pkgname, version_num) + log.error( + "%s %s no definition to remove this version", pkgname, version_num + ) ret[pkgname] = { - 'current': '{0} no definition, cannot removed'.format(version_num) - } + "current": "{0} no definition, cannot removed".format(version_num) + } continue for target in removal_targets: # Get the uninstaller - uninstaller = pkginfo[target].get('uninstaller', '') - cache_dir = pkginfo[target].get('cache_dir', False) - uninstall_flags = pkginfo[target].get('uninstall_flags', '') + uninstaller = pkginfo[target].get("uninstaller", "") + cache_dir = pkginfo[target].get("cache_dir", False) + uninstall_flags = pkginfo[target].get("uninstall_flags", "") # If no uninstaller found, use the installer with uninstall flags if not uninstaller and uninstall_flags: - uninstaller = pkginfo[target].get('installer', '') + uninstaller = pkginfo[target].get("installer", "") # If still no uninstaller found, fail if not uninstaller: log.error( - 'No installer or uninstaller configured for package %s', - pkgname, + "No installer or uninstaller configured for package %s", pkgname, ) - ret[pkgname] = {'no uninstaller defined': target} + ret[pkgname] = {"no uninstaller defined": target} continue # Where is the uninstaller - if uninstaller.startswith(('salt:', 'http:', 'https:', 'ftp:')): + if uninstaller.startswith(("salt:", "http:", "https:", "ftp:")): # Check for the 'cache_dir' parameter in the .sls file # If true, the entire directory will be cached instead of the # individual file. This is useful for installations that are not # single files - if cache_dir and uninstaller.startswith('salt:'): + if cache_dir and uninstaller.startswith("salt:"): path, _ = os.path.split(uninstaller) - __salt__['cp.cache_dir'](path, - saltenv, - False, - None, - 'E@init.sls$') + __salt__["cp.cache_dir"](path, saltenv, False, None, "E@init.sls$") # Check to see if the uninstaller is cached - cached_pkg = __salt__['cp.is_cached'](uninstaller, saltenv) + cached_pkg = __salt__["cp.is_cached"](uninstaller, saltenv) if not cached_pkg: # It's not cached. Cache it, mate. - cached_pkg = __salt__['cp.cache_file'](uninstaller, saltenv) + cached_pkg = __salt__["cp.cache_file"](uninstaller, saltenv) # Check if the uninstaller was cached successfully if not cached_pkg: - log.error('Unable to cache %s', uninstaller) - ret[pkgname] = {'unable to cache': uninstaller} + log.error("Unable to cache %s", uninstaller) + ret[pkgname] = {"unable to cache": uninstaller} continue # Compare the hash of the cached installer to the source only if # the file is hosted on salt: # TODO cp.cache_file does cache and hash checking? So why do it again? - if uninstaller.startswith('salt:'): - if __salt__['cp.hash_file'](uninstaller, saltenv) != \ - __salt__['cp.hash_file'](cached_pkg): + if uninstaller.startswith("salt:"): + if __salt__["cp.hash_file"](uninstaller, saltenv) != __salt__[ + "cp.hash_file" + ](cached_pkg): try: - cached_pkg = __salt__['cp.cache_file']( - uninstaller, saltenv) + cached_pkg = __salt__["cp.cache_file"](uninstaller, saltenv) except MinionError as exc: - return '{0}: {1}'.format(exc, uninstaller) + return "{0}: {1}".format(exc, uninstaller) # Check if the installer was cached successfully if not cached_pkg: - log.error('Unable to cache %s', uninstaller) - ret[pkgname] = {'unable to cache': uninstaller} + log.error("Unable to cache %s", uninstaller) + ret[pkgname] = {"unable to cache": uninstaller} continue else: # Run the uninstaller directly @@ -1952,18 +2060,21 @@ def remove(name=None, pkgs=None, **kwargs): cached_pkg = os.path.expandvars(uninstaller) # Fix non-windows slashes - cached_pkg = cached_pkg.replace('/', '\\') + cached_pkg = cached_pkg.replace("/", "\\") cache_path, _ = os.path.split(cached_pkg) # os.path.expandvars is not required as we run everything through cmd.exe /s /c - if kwargs.get('extra_uninstall_flags'): - uninstall_flags = '{0} {1}'.format( - uninstall_flags, kwargs.get('extra_uninstall_flags', '')) + if kwargs.get("extra_uninstall_flags"): + uninstall_flags = "{0} {1}".format( + uninstall_flags, kwargs.get("extra_uninstall_flags", "") + ) # Compute msiexec string - use_msiexec, msiexec = _get_msiexec(pkginfo[target].get('msiexec', False)) - cmd_shell = os.getenv('ComSpec', '{0}\\system32\\cmd.exe'.format(os.getenv('WINDIR'))) + use_msiexec, msiexec = _get_msiexec(pkginfo[target].get("msiexec", False)) + cmd_shell = os.getenv( + "ComSpec", "{0}\\system32\\cmd.exe".format(os.getenv("WINDIR")) + ) # Build cmd and arguments # cmd and arguments must be separated for use with the task scheduler @@ -1975,64 +2086,68 @@ def remove(name=None, pkgs=None, **kwargs): arguments = '"{0}"'.format(cached_pkg) if uninstall_flags: - arguments = '{0} {1}'.format(arguments, uninstall_flags) + arguments = "{0} {1}".format(arguments, uninstall_flags) # Uninstall the software changed.append(pkgname) # Check Use Scheduler Option - if pkginfo[target].get('use_scheduler', False): + if pkginfo[target].get("use_scheduler", False): # Create Scheduled Task - __salt__['task.create_task'](name='update-salt-software', - user_name='System', - force=True, - action_type='Execute', - cmd=cmd_shell, - arguments='/s /c "{0}"'.format(arguments), - start_in=cache_path, - trigger_type='Once', - start_date='1975-01-01', - start_time='01:00', - ac_only=False, - stop_if_on_batteries=False) + __salt__["task.create_task"]( + name="update-salt-software", + user_name="System", + force=True, + action_type="Execute", + cmd=cmd_shell, + arguments='/s /c "{0}"'.format(arguments), + start_in=cache_path, + trigger_type="Once", + start_date="1975-01-01", + start_time="01:00", + ac_only=False, + stop_if_on_batteries=False, + ) # Run Scheduled Task - if not __salt__['task.run_wait'](name='update-salt-software'): - log.error('Failed to remove %s', pkgname) - log.error('Scheduled Task failed to run') - ret[pkgname] = {'uninstall status': 'failed'} + if not __salt__["task.run_wait"](name="update-salt-software"): + log.error("Failed to remove %s", pkgname) + log.error("Scheduled Task failed to run") + ret[pkgname] = {"uninstall status": "failed"} else: # Launch the command - result = __salt__['cmd.run_all']( - '"{0}" /s /c "{1}"'.format(cmd_shell, arguments), - output_loglevel='trace', - python_shell=False, - redirect_stderr=True) - if not result['retcode']: - ret[pkgname] = {'uninstall status': 'success'} + result = __salt__["cmd.run_all"]( + '"{0}" /s /c "{1}"'.format(cmd_shell, arguments), + output_loglevel="trace", + python_shell=False, + redirect_stderr=True, + ) + if not result["retcode"]: + ret[pkgname] = {"uninstall status": "success"} changed.append(pkgname) - elif result['retcode'] == 3010: + elif result["retcode"] == 3010: # 3010 is ERROR_SUCCESS_REBOOT_REQUIRED report_reboot_exit_codes = kwargs.pop( - 'report_reboot_exit_codes', True) + "report_reboot_exit_codes", True + ) if report_reboot_exit_codes: - __salt__['system.set_reboot_required_witnessed']() - ret[pkgname] = {'uninstall status': 'success, reboot required'} + __salt__["system.set_reboot_required_witnessed"]() + ret[pkgname] = {"uninstall status": "success, reboot required"} changed.append(pkgname) - elif result['retcode'] == 1641: + elif result["retcode"] == 1641: # 1641 is ERROR_SUCCESS_REBOOT_INITIATED - ret[pkgname] = {'uninstall status': 'success, reboot initiated'} + ret[pkgname] = {"uninstall status": "success, reboot initiated"} changed.append(pkgname) else: - log.error('Failed to remove %s', pkgname) - log.error('retcode %s', result['retcode']) - log.error('uninstaller output: %s', result['stdout']) - ret[pkgname] = {'uninstall status': 'failed'} + log.error("Failed to remove %s", pkgname) + log.error("retcode %s", result["retcode"]) + log.error("uninstaller output: %s", result["stdout"]) + ret[pkgname] = {"uninstall status": "failed"} # Get a new list of installed software new = list_pkgs(saltenv=saltenv, refresh=False) # Take the "old" package list and convert the values to strings in # preparation for the comparison below. - __salt__['pkg_resource.stringify'](old) + __salt__["pkg_resource.stringify"](old) # Check for changes in the registry difference = salt.utils.data.compare_dicts(old, new) @@ -2045,7 +2160,7 @@ def remove(name=None, pkgs=None, **kwargs): found_chgs = all(name in difference for name in changed) if not found_chgs: - log.warning('Expected changes for package removal may not have occured') + log.warning("Expected changes for package removal may not have occured") # Compare the software list before and after # Add the difference to ret @@ -2054,7 +2169,7 @@ def remove(name=None, pkgs=None, **kwargs): def purge(name=None, pkgs=None, **kwargs): - ''' + """ Package purges are not supported, this function is identical to ``remove()``. @@ -2088,14 +2203,12 @@ def purge(name=None, pkgs=None, **kwargs): salt '*' pkg.purge <package name> salt '*' pkg.purge <package1>,<package2>,<package3> salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' - return remove(name=name, - pkgs=pkgs, - **kwargs) + """ + return remove(name=name, pkgs=pkgs, **kwargs) -def get_repo_data(saltenv='base'): - ''' +def get_repo_data(saltenv="base"): + """ Returns the existing package metadata db. Will create it, if it does not exist, however will not refresh it. @@ -2110,7 +2223,7 @@ def get_repo_data(saltenv='base'): .. code-block:: bash salt '*' pkg.get_repo_data - ''' + """ # we only call refresh_db if it does not exist, as we want to return # the existing data even if its old, other parts of the code call this, # but they will call refresh if they need too. @@ -2118,37 +2231,37 @@ def get_repo_data(saltenv='base'): if repo_details.winrepo_age == -1: # no repo meta db - log.debug('No winrepo.p cache file. Refresh pkg db now.') + log.debug("No winrepo.p cache file. Refresh pkg db now.") refresh_db(saltenv=saltenv) - if 'winrepo.data' in __context__: - log.trace('get_repo_data returning results from __context__') - return __context__['winrepo.data'] + if "winrepo.data" in __context__: + log.trace("get_repo_data returning results from __context__") + return __context__["winrepo.data"] else: - log.trace('get_repo_data called reading from disk') + log.trace("get_repo_data called reading from disk") try: serial = salt.payload.Serial(__opts__) - with salt.utils.files.fopen(repo_details.winrepo_file, 'rb') as repofile: + with salt.utils.files.fopen(repo_details.winrepo_file, "rb") as repofile: try: repodata = salt.utils.data.decode(serial.loads(repofile.read()) or {}) - __context__['winrepo.data'] = repodata + __context__["winrepo.data"] = repodata return repodata except Exception as exc: # pylint: disable=broad-except log.exception(exc) return {} except IOError as exc: - log.error('Not able to read repo file') + log.error("Not able to read repo file") log.exception(exc) return {} -def _get_name_map(saltenv='base'): - ''' +def _get_name_map(saltenv="base"): + """ Return a reverse map of full pkg names to the names recognized by winrepo. - ''' + """ u_name_map = {} - name_map = get_repo_data(saltenv).get('name_map', {}) + name_map = get_repo_data(saltenv).get("name_map", {}) if not six.PY2: return name_map @@ -2158,47 +2271,44 @@ def _get_name_map(saltenv='base'): return u_name_map -def get_package_info(name, saltenv='base'): - ''' +def get_package_info(name, saltenv="base"): + """ Return package info. Returns empty map if package not available. - ''' + """ return _get_package_info(name=name, saltenv=saltenv) -def _get_package_info(name, saltenv='base'): - ''' +def _get_package_info(name, saltenv="base"): + """ Return package info. Returns empty map if package not available TODO: Add option for version - ''' - return get_repo_data(saltenv).get('repo', {}).get(name, {}) + """ + return get_repo_data(saltenv).get("repo", {}).get(name, {}) def _reverse_cmp_pkg_versions(pkg1, pkg2): - ''' + """ Compare software package versions - ''' + """ return 1 if LooseVersion(pkg1) > LooseVersion(pkg2) else -1 def _get_latest_pkg_version(pkginfo): - ''' + """ Returns the latest version of the package. Will return 'latest' or version number string, and 'Not Found' if 'Not Found' is the only entry. - ''' + """ if len(pkginfo) == 1: return next(six.iterkeys(pkginfo)) try: - return sorted( - pkginfo, - key=cmp_to_key(_reverse_cmp_pkg_versions) - ).pop() + return sorted(pkginfo, key=cmp_to_key(_reverse_cmp_pkg_versions)).pop() except IndexError: - return '' + return "" -def compare_versions(ver1='', oper='==', ver2=''): - ''' +def compare_versions(ver1="", oper="==", ver2=""): + """ Compare software package versions. Made public for use with Jinja Args: @@ -2214,21 +2324,21 @@ def compare_versions(ver1='', oper='==', ver2=''): .. code-block:: bash salt '*' pkg.compare_versions 1.2 >= 1.3 - ''' + """ if not ver1: - raise SaltInvocationError('compare_version, ver1 is blank') + raise SaltInvocationError("compare_version, ver1 is blank") if not ver2: - raise SaltInvocationError('compare_version, ver2 is blank') + raise SaltInvocationError("compare_version, ver2 is blank") # Support version being the special meaning of 'latest' - if ver1 == 'latest': + if ver1 == "latest": ver1 = six.text_type(sys.maxsize) - if ver2 == 'latest': + if ver2 == "latest": ver2 = six.text_type(sys.maxsize) # Support version being the special meaning of 'Not Found' - if ver1 == 'Not Found': - ver1 = '0.0.0.0.0' - if ver2 == 'Not Found': - ver2 = '0.0.0.0.0' + if ver1 == "Not Found": + ver1 = "0.0.0.0.0" + if ver2 == "Not Found": + ver2 = "0.0.0.0.0" return salt.utils.versions.compare(ver1, oper, ver2, ignore_epoch=True) diff --git a/salt/modules/win_pki.py b/salt/modules/win_pki.py index 6f90a3a0bc5..d6d2df52dca 100644 --- a/salt/modules/win_pki.py +++ b/salt/modules/win_pki.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Microsoft certificate management via the PKI Client PowerShell module. https://technet.microsoft.com/en-us/itpro/powershell/windows/pkiclient/pkiclient @@ -14,9 +14,10 @@ https://technet.microsoft.com/en-us/library/hh848636(v=wps.620).aspx - PKI Client Module (Windows 8+ / Windows Server 2012+) .. versionadded:: 2016.11.0 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import ast import logging import os @@ -31,70 +32,71 @@ from salt.exceptions import SaltInvocationError # Import 3rd party libs from salt.ext import six -_DEFAULT_CONTEXT = 'LocalMachine' -_DEFAULT_FORMAT = 'cer' -_DEFAULT_STORE = 'My' +_DEFAULT_CONTEXT = "LocalMachine" +_DEFAULT_FORMAT = "cer" +_DEFAULT_STORE = "My" _LOG = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'win_pki' +__virtualname__ = "win_pki" def __virtual__(): - ''' + """ Requires Windows Requires Windows 8+ / Windows Server 2012+ Requires PowerShell Requires PKI Client PowerShell module installed. - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Only available on Windows Systems' + return False, "Only available on Windows Systems" - if salt.utils.versions.version_cmp(__grains__['osversion'], '6.2.9200') == -1: - return False, 'Only available on Windows 8+ / Windows Server 2012 +' + if salt.utils.versions.version_cmp(__grains__["osversion"], "6.2.9200") == -1: + return False, "Only available on Windows 8+ / Windows Server 2012 +" - if not __salt__['cmd.shell_info']('powershell')['installed']: - return False, 'Powershell not available' + if not __salt__["cmd.shell_info"]("powershell")["installed"]: + return False, "Powershell not available" - if not salt.utils.powershell.module_exists('PKI'): - return False, 'PowerShell PKI module not available' + if not salt.utils.powershell.module_exists("PKI"): + return False, "PowerShell PKI module not available" return __virtualname__ def _cmd_run(cmd, as_json=False): - ''' + """ Ensure that the Pki module is loaded, and convert to and extract data from Json as needed. - ''' - cmd_full = ['Import-Module -Name PKI; '] + """ + cmd_full = ["Import-Module -Name PKI; "] if as_json: - cmd_full.append(r'ConvertTo-Json -Compress -Depth 4 -InputObject ' - r'@({0})'.format(cmd)) + cmd_full.append( + r"ConvertTo-Json -Compress -Depth 4 -InputObject " r"@({0})".format(cmd) + ) else: cmd_full.append(cmd) - cmd_ret = __salt__['cmd.run_all']( - six.text_type().join(cmd_full), shell='powershell', python_shell=True) + cmd_ret = __salt__["cmd.run_all"]( + six.text_type().join(cmd_full), shell="powershell", python_shell=True + ) - if cmd_ret['retcode'] != 0: - _LOG.error('Unable to execute command: %s\nError: %s', cmd, - cmd_ret['stderr']) + if cmd_ret["retcode"] != 0: + _LOG.error("Unable to execute command: %s\nError: %s", cmd, cmd_ret["stderr"]) if as_json: try: - items = salt.utils.json.loads(cmd_ret['stdout'], strict=False) + items = salt.utils.json.loads(cmd_ret["stdout"], strict=False) return items except ValueError: - _LOG.error('Unable to parse return data as Json.') + _LOG.error("Unable to parse return data as Json.") - return cmd_ret['stdout'] + return cmd_ret["stdout"] def _validate_cert_path(name): - ''' + """ Ensure that the certificate path, as determind from user input, is valid. - ''' + """ cmd = r"Test-Path -Path '{0}'".format(name) if not ast.literal_eval(_cmd_run(cmd=cmd)): @@ -102,19 +104,20 @@ def _validate_cert_path(name): def _validate_cert_format(name): - ''' + """ Ensure that the certificate format, as determind from user input, is valid. - ''' - cert_formats = ['cer', 'pfx'] + """ + cert_formats = ["cer", "pfx"] if name not in cert_formats: - message = ("Invalid certificate format '{0}' specified. Valid formats:" - ' {1}').format(name, cert_formats) + message = ( + "Invalid certificate format '{0}' specified. Valid formats:" " {1}" + ).format(name, cert_formats) raise SaltInvocationError(message) def get_stores(): - ''' + """ Get the certificate location contexts and their corresponding stores. :return: A dictionary of the certificate location contexts and stores. @@ -125,23 +128,22 @@ def get_stores(): .. code-block:: bash salt '*' win_pki.get_stores - ''' + """ ret = dict() - cmd = r"Get-ChildItem -Path 'Cert:\' | " \ - r"Select-Object LocationName, StoreNames" + cmd = r"Get-ChildItem -Path 'Cert:\' | " r"Select-Object LocationName, StoreNames" items = _cmd_run(cmd=cmd, as_json=True) for item in items: - ret[item['LocationName']] = list() + ret[item["LocationName"]] = list() - for store in item['StoreNames']: - ret[item['LocationName']].append(store) + for store in item["StoreNames"]: + ret[item["LocationName"]].append(store) return ret def get_certs(context=_DEFAULT_CONTEXT, store=_DEFAULT_STORE): - ''' + """ Get the available certificates in the given store. :param str context: The name of the certificate store location context. @@ -155,16 +157,16 @@ def get_certs(context=_DEFAULT_CONTEXT, store=_DEFAULT_STORE): .. code-block:: bash salt '*' win_pki.get_certs - ''' + """ ret = dict() cmd = list() - blacklist_keys = ['DnsNameList'] - store_path = r'Cert:\{0}\{1}'.format(context, store) + blacklist_keys = ["DnsNameList"] + store_path = r"Cert:\{0}\{1}".format(context, store) _validate_cert_path(name=store_path) cmd.append(r"Get-ChildItem -Path '{0}' | Select-Object".format(store_path)) - cmd.append(' DnsNameList, SerialNumber, Subject, Thumbprint, Version') + cmd.append(" DnsNameList, SerialNumber, Subject, Thumbprint, Version") items = _cmd_run(cmd=six.text_type().join(cmd), as_json=True) @@ -174,17 +176,17 @@ def get_certs(context=_DEFAULT_CONTEXT, store=_DEFAULT_STORE): if key not in blacklist_keys: cert_info[key.lower()] = item[key] - names = item.get('DnsNameList', None) + names = item.get("DnsNameList", None) if isinstance(names, list): - cert_info['dnsnames'] = [name.get('Unicode') for name in names] + cert_info["dnsnames"] = [name.get("Unicode") for name in names] else: - cert_info['dnsnames'] = [] - ret[item['Thumbprint']] = cert_info + cert_info["dnsnames"] = [] + ret[item["Thumbprint"]] = cert_info return ret -def get_cert_file(name, cert_format=_DEFAULT_FORMAT, password=''): - ''' +def get_cert_file(name, cert_format=_DEFAULT_FORMAT, password=""): + """ Get the details of the certificate file. :param str name: The filesystem path of the certificate file. @@ -202,37 +204,45 @@ def get_cert_file(name, cert_format=_DEFAULT_FORMAT, password=''): .. code-block:: bash salt '*' win_pki.get_cert_file name='C:\\certs\\example.cer' - ''' + """ ret = dict() cmd = list() - blacklist_keys = ['DnsNameList'] + blacklist_keys = ["DnsNameList"] cert_format = cert_format.lower() _validate_cert_format(name=cert_format) if not name or not os.path.isfile(name): - _LOG.error('Path is not present: %s', name) + _LOG.error("Path is not present: %s", name) return ret - if cert_format == 'pfx': + if cert_format == "pfx": if password: - cmd.append('$CertObject = New-Object') - cmd.append(' System.Security.Cryptography.X509Certificates.X509Certificate2;') + cmd.append("$CertObject = New-Object") + cmd.append( + " System.Security.Cryptography.X509Certificates.X509Certificate2;" + ) cmd.append(r" $CertObject.Import('{0}'".format(name)) cmd.append(",'{0}'".format(password)) cmd.append(",'DefaultKeySet') ; $CertObject") - cmd.append(' | Select-Object DnsNameList, SerialNumber, Subject, ' - 'Thumbprint, Version') + cmd.append( + " | Select-Object DnsNameList, SerialNumber, Subject, " + "Thumbprint, Version" + ) else: cmd.append(r"Get-PfxCertificate -FilePath '{0}'".format(name)) - cmd.append(' | Select-Object DnsNameList, SerialNumber, Subject, ' - 'Thumbprint, Version') + cmd.append( + " | Select-Object DnsNameList, SerialNumber, Subject, " + "Thumbprint, Version" + ) else: - cmd.append('$CertObject = New-Object') - cmd.append(' System.Security.Cryptography.X509Certificates.X509Certificate2;') + cmd.append("$CertObject = New-Object") + cmd.append(" System.Security.Cryptography.X509Certificates.X509Certificate2;") cmd.append(r" $CertObject.Import('{0}'); $CertObject".format(name)) - cmd.append(' | Select-Object DnsNameList, SerialNumber, Subject, ' - 'Thumbprint, Version') + cmd.append( + " | Select-Object DnsNameList, SerialNumber, Subject, " + "Thumbprint, Version" + ) items = _cmd_run(cmd=six.text_type().join(cmd), as_json=True) @@ -241,23 +251,25 @@ def get_cert_file(name, cert_format=_DEFAULT_FORMAT, password=''): if key not in blacklist_keys: ret[key.lower()] = item[key] - ret['dnsnames'] = [name['Unicode'] for name in item['DnsNameList']] + ret["dnsnames"] = [name["Unicode"] for name in item["DnsNameList"]] if ret: - _LOG.debug('Certificate thumbprint obtained successfully: %s', name) + _LOG.debug("Certificate thumbprint obtained successfully: %s", name) else: - _LOG.error('Unable to obtain certificate thumbprint: %s', name) + _LOG.error("Unable to obtain certificate thumbprint: %s", name) return ret -def import_cert(name, - cert_format=_DEFAULT_FORMAT, - context=_DEFAULT_CONTEXT, - store=_DEFAULT_STORE, - exportable=True, - password='', - saltenv='base'): - ''' +def import_cert( + name, + cert_format=_DEFAULT_FORMAT, + context=_DEFAULT_CONTEXT, + store=_DEFAULT_STORE, + exportable=True, + password="", + saltenv="base", +): + """ Import the certificate file into the given certificate store. :param str name: The path of the certificate file to import. @@ -280,53 +292,59 @@ def import_cert(name, .. code-block:: bash salt '*' win_pki.import_cert name='salt://cert.cer' - ''' + """ cmd = list() thumbprint = None - store_path = r'Cert:\{0}\{1}'.format(context, store) + store_path = r"Cert:\{0}\{1}".format(context, store) cert_format = cert_format.lower() _validate_cert_format(name=cert_format) - cached_source_path = __salt__['cp.cache_file'](name, saltenv) + cached_source_path = __salt__["cp.cache_file"](name, saltenv) if not cached_source_path: - _LOG.error('Unable to get cached copy of file: %s', name) + _LOG.error("Unable to get cached copy of file: %s", name) return False if password: - cert_props = get_cert_file(name=cached_source_path, cert_format=cert_format, password=password) + cert_props = get_cert_file( + name=cached_source_path, cert_format=cert_format, password=password + ) else: cert_props = get_cert_file(name=cached_source_path, cert_format=cert_format) current_certs = get_certs(context=context, store=store) - if cert_props['thumbprint'] in current_certs: - _LOG.debug("Certificate thumbprint '%s' already present in store: %s", - cert_props['thumbprint'], store_path) + if cert_props["thumbprint"] in current_certs: + _LOG.debug( + "Certificate thumbprint '%s' already present in store: %s", + cert_props["thumbprint"], + store_path, + ) return True - if cert_format == 'pfx': + if cert_format == "pfx": # In instances where an empty password is needed, we use a # System.Security.SecureString object since ConvertTo-SecureString will # not convert an empty string. if password: - cmd.append(r"$Password = ConvertTo-SecureString " - r"-String '{0}'".format(password)) - cmd.append(' -AsPlainText -Force; ') + cmd.append( + r"$Password = ConvertTo-SecureString " r"-String '{0}'".format(password) + ) + cmd.append(" -AsPlainText -Force; ") else: - cmd.append('$Password = New-Object System.Security.SecureString; ') + cmd.append("$Password = New-Object System.Security.SecureString; ") - cmd.append(r"Import-PfxCertificate " - r"-FilePath '{0}'".format(cached_source_path)) + cmd.append( + r"Import-PfxCertificate " r"-FilePath '{0}'".format(cached_source_path) + ) cmd.append(r" -CertStoreLocation '{0}'".format(store_path)) cmd.append(r" -Password $Password") if exportable: - cmd.append(' -Exportable') + cmd.append(" -Exportable") else: - cmd.append(r"Import-Certificate " - r"-FilePath '{0}'".format(cached_source_path)) + cmd.append(r"Import-Certificate " r"-FilePath '{0}'".format(cached_source_path)) cmd.append(r" -CertStoreLocation '{0}'".format(store_path)) _cmd_run(cmd=six.text_type().join(cmd)) @@ -338,19 +356,21 @@ def import_cert(name, thumbprint = new_cert if thumbprint: - _LOG.debug('Certificate imported successfully: %s', name) + _LOG.debug("Certificate imported successfully: %s", name) return True - _LOG.error('Unable to import certificate: %s', name) + _LOG.error("Unable to import certificate: %s", name) return False -def export_cert(name, - thumbprint, - cert_format=_DEFAULT_FORMAT, - context=_DEFAULT_CONTEXT, - store=_DEFAULT_STORE, - password=''): - ''' +def export_cert( + name, + thumbprint, + cert_format=_DEFAULT_FORMAT, + context=_DEFAULT_CONTEXT, + store=_DEFAULT_STORE, + password="", +): + """ Export the certificate to a file from the given certificate store. :param str name: The destination path for the exported certificate file. @@ -371,51 +391,58 @@ def export_cert(name, .. code-block:: bash salt '*' win_pki.export_cert name='C:\\certs\\example.cer' thumbprint='AAA000' - ''' + """ cmd = list() thumbprint = thumbprint.upper() - cert_path = r'Cert:\{0}\{1}\{2}'.format(context, store, thumbprint) + cert_path = r"Cert:\{0}\{1}\{2}".format(context, store, thumbprint) cert_format = cert_format.lower() _validate_cert_path(name=cert_path) _validate_cert_format(name=cert_format) - if cert_format == 'pfx': + if cert_format == "pfx": # In instances where an empty password is needed, we use a # System.Security.SecureString object since ConvertTo-SecureString will # not convert an empty string. if password: - cmd.append(r"$Password = ConvertTo-SecureString " - r"-String '{0}'".format(password)) - cmd.append(' -AsPlainText -Force; ') + cmd.append( + r"$Password = ConvertTo-SecureString " r"-String '{0}'".format(password) + ) + cmd.append(" -AsPlainText -Force; ") else: - cmd.append('$Password = New-Object System.Security.SecureString; ') + cmd.append("$Password = New-Object System.Security.SecureString; ") - cmd.append(r"Export-PfxCertificate " - r"-Cert '{0}' -FilePath '{1}'".format(cert_path, name)) + cmd.append( + r"Export-PfxCertificate " + r"-Cert '{0}' -FilePath '{1}'".format(cert_path, name) + ) cmd.append(r" -Password $Password") else: - cmd.append(r"Export-Certificate " - r"-Cert '{0}' -FilePath '{1}'".format(cert_path, name)) + cmd.append( + r"Export-Certificate " + r"-Cert '{0}' -FilePath '{1}'".format(cert_path, name) + ) cmd.append(r" | Out-Null; Test-Path -Path '{0}'".format(name)) ret = ast.literal_eval(_cmd_run(cmd=six.text_type().join(cmd))) if ret: - _LOG.debug('Certificate exported successfully: %s', name) + _LOG.debug("Certificate exported successfully: %s", name) else: - _LOG.error('Unable to export certificate: %s', name) + _LOG.error("Unable to export certificate: %s", name) return ret -def test_cert(thumbprint, - context=_DEFAULT_CONTEXT, - store=_DEFAULT_STORE, - untrusted_root=False, - dns_name='', - eku=''): - ''' +def test_cert( + thumbprint, + context=_DEFAULT_CONTEXT, + store=_DEFAULT_STORE, + untrusted_root=False, + dns_name="", + eku="", +): + """ Check the certificate for validity. :param str thumbprint: The thumbprint value of the target certificate. @@ -436,28 +463,28 @@ def test_cert(thumbprint, .. code-block:: bash salt '*' win_pki.test_cert thumbprint='AAA000' dns_name='example.test' - ''' + """ cmd = list() thumbprint = thumbprint.upper() - cert_path = r'Cert:\{0}\{1}\{2}'.format(context, store, thumbprint) + cert_path = r"Cert:\{0}\{1}\{2}".format(context, store, thumbprint) cmd.append(r"Test-Certificate -Cert '{0}'".format(cert_path)) _validate_cert_path(name=cert_path) if untrusted_root: - cmd.append(' -AllowUntrustedRoot') + cmd.append(" -AllowUntrustedRoot") if dns_name: cmd.append(" -DnsName '{0}'".format(dns_name)) if eku: cmd.append(" -EKU '{0}'".format(eku)) - cmd.append(' -ErrorAction SilentlyContinue') + cmd.append(" -ErrorAction SilentlyContinue") return ast.literal_eval(_cmd_run(cmd=six.text_type().join(cmd))) def remove_cert(thumbprint, context=_DEFAULT_CONTEXT, store=_DEFAULT_STORE): - ''' + """ Remove the certificate from the given certificate store. :param str thumbprint: The thumbprint value of the target certificate. @@ -472,17 +499,18 @@ def remove_cert(thumbprint, context=_DEFAULT_CONTEXT, store=_DEFAULT_STORE): .. code-block:: bash salt '*' win_pki.remove_cert thumbprint='AAA000' - ''' + """ thumbprint = thumbprint.upper() - store_path = r'Cert:\{0}\{1}'.format(context, store) - cert_path = r'{0}\{1}'.format(store_path, thumbprint) + store_path = r"Cert:\{0}\{1}".format(context, store) + cert_path = r"{0}\{1}".format(store_path, thumbprint) cmd = r"Remove-Item -Path '{0}'".format(cert_path) current_certs = get_certs(context=context, store=store) if thumbprint not in current_certs: - _LOG.debug("Certificate '%s' already absent in store: %s", thumbprint, - store_path) + _LOG.debug( + "Certificate '%s' already absent in store: %s", thumbprint, store_path + ) return True _validate_cert_path(name=cert_path) @@ -491,7 +519,7 @@ def remove_cert(thumbprint, context=_DEFAULT_CONTEXT, store=_DEFAULT_STORE): new_certs = get_certs(context=context, store=store) if thumbprint in new_certs: - _LOG.error('Unable to remove certificate: %s', cert_path) + _LOG.error("Unable to remove certificate: %s", cert_path) return False - _LOG.debug('Certificate removed successfully: %s', cert_path) + _LOG.debug("Certificate removed successfully: %s", cert_path) return True diff --git a/salt/modules/win_powercfg.py b/salt/modules/win_powercfg.py index 818f979718b..b71f411a81e 100644 --- a/salt/modules/win_powercfg.py +++ b/salt/modules/win_powercfg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This module allows you to control the power settings of a windows minion via powercfg. @@ -11,51 +11,52 @@ powercfg. salt '*' powercfg.set_monitor_timeout 0 power=dc # Set disk timeout to 120 minutes on AC power salt '*' powercfg.set_disk_timeout 120 power=ac -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import re +from __future__ import absolute_import, print_function, unicode_literals + import logging +import re # Import Salt Libs import salt.utils.platform log = logging.getLogger(__name__) -__virtualname__ = 'powercfg' +__virtualname__ = "powercfg" def __virtual__(): - ''' + """ Only work on Windows - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'PowerCFG: Module only works on Windows' + return False, "PowerCFG: Module only works on Windows" return __virtualname__ def _get_current_scheme(): - cmd = 'powercfg /getactivescheme' - out = __salt__['cmd.run'](cmd, python_shell=False) - matches = re.search(r'GUID: (.*) \(', out) + cmd = "powercfg /getactivescheme" + out = __salt__["cmd.run"](cmd, python_shell=False) + matches = re.search(r"GUID: (.*) \(", out) return matches.groups()[0].strip() def _get_powercfg_minute_values(scheme, guid, subguid, safe_name): - ''' + """ Returns the AC/DC values in an dict for a guid and subguid for a the given scheme - ''' + """ if scheme is None: scheme = _get_current_scheme() - if __grains__['osrelease'] == '7': - cmd = 'powercfg /q {0} {1}'.format(scheme, guid) + if __grains__["osrelease"] == "7": + cmd = "powercfg /q {0} {1}".format(scheme, guid) else: - cmd = 'powercfg /q {0} {1} {2}'.format(scheme, guid, subguid) - out = __salt__['cmd.run'](cmd, python_shell=False) + cmd = "powercfg /q {0} {1} {2}".format(scheme, guid, subguid) + out = __salt__["cmd.run"](cmd, python_shell=False) - split = out.split('\r\n\r\n') + split = out.split("\r\n\r\n") if len(split) > 1: for s in split: if safe_name in s or subguid in s: @@ -64,25 +65,25 @@ def _get_powercfg_minute_values(scheme, guid, subguid, safe_name): else: out = split[0] - raw_settings = re.findall(r'Power Setting Index: ([0-9a-fx]+)', out) - return {'ac': int(raw_settings[0], 0) / 60, - 'dc': int(raw_settings[1], 0) / 60} + raw_settings = re.findall(r"Power Setting Index: ([0-9a-fx]+)", out) + return {"ac": int(raw_settings[0], 0) / 60, "dc": int(raw_settings[1], 0) / 60} def _set_powercfg_value(scheme, sub_group, setting_guid, power, value): - ''' + """ Sets the AC/DC values of a setting with the given power for the given scheme - ''' + """ if scheme is None: scheme = _get_current_scheme() - cmd = 'powercfg /set{0}valueindex {1} {2} {3} {4}' \ - ''.format(power, scheme, sub_group, setting_guid, value * 60) - return __salt__['cmd.retcode'](cmd, python_shell=False) == 0 + cmd = "powercfg /set{0}valueindex {1} {2} {3} {4}" "".format( + power, scheme, sub_group, setting_guid, value * 60 + ) + return __salt__["cmd.retcode"](cmd, python_shell=False) == 0 -def set_monitor_timeout(timeout, power='ac', scheme=None): - ''' +def set_monitor_timeout(timeout, power="ac", scheme=None): + """ Set the monitor timeout in minutes for the given power scheme Args: @@ -114,17 +115,18 @@ def set_monitor_timeout(timeout, power='ac', scheme=None): # Sets the monitor timeout to 30 minutes salt '*' powercfg.set_monitor_timeout 30 - ''' + """ return _set_powercfg_value( scheme=scheme, - sub_group='SUB_VIDEO', - setting_guid='VIDEOIDLE', + sub_group="SUB_VIDEO", + setting_guid="VIDEOIDLE", power=power, - value=timeout) + value=timeout, + ) def get_monitor_timeout(scheme=None): - ''' + """ Get the current monitor timeout of the given scheme Args: @@ -145,16 +147,17 @@ def get_monitor_timeout(scheme=None): .. code-block:: bash salt '*' powercfg.get_monitor_timeout - ''' + """ return _get_powercfg_minute_values( scheme=scheme, - guid='SUB_VIDEO', - subguid='VIDEOIDLE', - safe_name='Turn off display after') + guid="SUB_VIDEO", + subguid="VIDEOIDLE", + safe_name="Turn off display after", + ) -def set_disk_timeout(timeout, power='ac', scheme=None): - ''' +def set_disk_timeout(timeout, power="ac", scheme=None): + """ Set the disk timeout in minutes for the given power scheme Args: @@ -186,17 +189,18 @@ def set_disk_timeout(timeout, power='ac', scheme=None): # Sets the disk timeout to 30 minutes on battery salt '*' powercfg.set_disk_timeout 30 power=dc - ''' + """ return _set_powercfg_value( scheme=scheme, - sub_group='SUB_DISK', - setting_guid='DISKIDLE', + sub_group="SUB_DISK", + setting_guid="DISKIDLE", power=power, - value=timeout) + value=timeout, + ) def get_disk_timeout(scheme=None): - ''' + """ Get the current disk timeout of the given scheme Args: @@ -217,16 +221,17 @@ def get_disk_timeout(scheme=None): .. code-block:: bash salt '*' powercfg.get_disk_timeout - ''' + """ return _get_powercfg_minute_values( scheme=scheme, - guid='SUB_DISK', - subguid='DISKIDLE', - safe_name='Turn off hard disk after') + guid="SUB_DISK", + subguid="DISKIDLE", + safe_name="Turn off hard disk after", + ) -def set_standby_timeout(timeout, power='ac', scheme=None): - ''' +def set_standby_timeout(timeout, power="ac", scheme=None): + """ Set the standby timeout in minutes for the given power scheme Args: @@ -258,17 +263,18 @@ def set_standby_timeout(timeout, power='ac', scheme=None): # Sets the system standby timeout to 30 minutes on Battery salt '*' powercfg.set_standby_timeout 30 power=dc - ''' + """ return _set_powercfg_value( scheme=scheme, - sub_group='SUB_SLEEP', - setting_guid='STANDBYIDLE', + sub_group="SUB_SLEEP", + setting_guid="STANDBYIDLE", power=power, - value=timeout) + value=timeout, + ) def get_standby_timeout(scheme=None): - ''' + """ Get the current standby timeout of the given scheme scheme (str): @@ -288,16 +294,14 @@ def get_standby_timeout(scheme=None): .. code-block:: bash salt '*' powercfg.get_standby_timeout - ''' + """ return _get_powercfg_minute_values( - scheme=scheme, - guid='SUB_SLEEP', - subguid='STANDBYIDLE', - safe_name='Sleep after') + scheme=scheme, guid="SUB_SLEEP", subguid="STANDBYIDLE", safe_name="Sleep after" + ) -def set_hibernate_timeout(timeout, power='ac', scheme=None): - ''' +def set_hibernate_timeout(timeout, power="ac", scheme=None): + """ Set the hibernate timeout in minutes for the given power scheme Args: @@ -329,17 +333,18 @@ def set_hibernate_timeout(timeout, power='ac', scheme=None): # Sets the hibernate timeout to 30 minutes on Battery salt '*' powercfg.set_hibernate_timeout 30 power=dc - ''' + """ return _set_powercfg_value( scheme=scheme, - sub_group='SUB_SLEEP', - setting_guid='HIBERNATEIDLE', + sub_group="SUB_SLEEP", + setting_guid="HIBERNATEIDLE", power=power, - value=timeout) + value=timeout, + ) def get_hibernate_timeout(scheme=None): - ''' + """ Get the current hibernate timeout of the given scheme scheme (str): @@ -359,9 +364,10 @@ def get_hibernate_timeout(scheme=None): .. code-block:: bash salt '*' powercfg.get_hibernate_timeout - ''' + """ return _get_powercfg_minute_values( scheme=scheme, - guid='SUB_SLEEP', - subguid='HIBERNATEIDLE', - safe_name='Hibernate after') + guid="SUB_SLEEP", + subguid="HIBERNATEIDLE", + safe_name="Hibernate after", + ) diff --git a/salt/modules/win_psget.py b/salt/modules/win_psget.py index 2eb0e6fafc8..1a19a6c071e 100644 --- a/salt/modules/win_psget.py +++ b/salt/modules/win_psget.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing PowerShell through PowerShellGet (PSGet) :depends: @@ -7,7 +7,7 @@ Module for managing PowerShell through PowerShellGet (PSGet) - PSGet Support for PowerShell -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -23,37 +23,37 @@ from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'psget' +__virtualname__ = "psget" def __virtual__(): - ''' + """ Set the system module of the kernel is Windows - ''' + """ # Verify Windows if not salt.utils.platform.is_windows(): - log.debug('Module PSGet: Only available on Windows systems') - return False, 'Module PSGet: Only available on Windows systems' + log.debug("Module PSGet: Only available on Windows systems") + return False, "Module PSGet: Only available on Windows systems" # Verify PowerShell - powershell_info = __salt__['cmd.shell_info']('powershell') - if not powershell_info['installed']: - log.debug('Module PSGet: Requires PowerShell') - return False, 'Module PSGet: Requires PowerShell' + powershell_info = __salt__["cmd.shell_info"]("powershell") + if not powershell_info["installed"]: + log.debug("Module PSGet: Requires PowerShell") + return False, "Module PSGet: Requires PowerShell" # Verify PowerShell 5.0 or greater - if salt.utils.versions.compare(powershell_info['version'], '<', '5.0'): - log.debug('Module PSGet: Requires PowerShell 5 or newer') - return False, 'Module PSGet: Requires PowerShell 5 or newer.' + if salt.utils.versions.compare(powershell_info["version"], "<", "5.0"): + log.debug("Module PSGet: Requires PowerShell 5 or newer") + return False, "Module PSGet: Requires PowerShell 5 or newer." return __virtualname__ def _ps_xml_to_dict(parent, dic=None): - ''' + """ Formats powershell Xml to a dict. Note: This _ps_xml_to_dict is not perfect with powershell Xml. - ''' + """ if dic is None: dic = {} @@ -65,7 +65,11 @@ def _ps_xml_to_dict(parent, dic=None): dic[new_dic["Name"]] = new_dic else: try: - dic[[name for ps_type, name in child.items() if ps_type == "Type"][0]] = new_dic + dic[ + [name for ps_type, name in child.items() if ps_type == "Type"][ + 0 + ] + ] = new_dic except IndexError: dic[child.text] = new_dic else: @@ -77,34 +81,40 @@ def _ps_xml_to_dict(parent, dic=None): def _pshell(cmd, cwd=None, depth=2): - ''' + """ Execute the desired powershell command and ensure that it returns data in Xml format and load that into python - ''' + """ - cmd = '{0} | ConvertTo-Xml -Depth {1} -As \"stream\"'.format(cmd, depth) - log.debug('DSC: %s', cmd) + cmd = '{0} | ConvertTo-Xml -Depth {1} -As "stream"'.format(cmd, depth) + log.debug("DSC: %s", cmd) - results = __salt__['cmd.run_all'](cmd, shell='powershell', cwd=cwd, python_shell=True) + results = __salt__["cmd.run_all"]( + cmd, shell="powershell", cwd=cwd, python_shell=True + ) - if 'pid' in results: - del results['pid'] + if "pid" in results: + del results["pid"] - if 'retcode' not in results or results['retcode'] != 0: + if "retcode" not in results or results["retcode"] != 0: # run_all logs an error to log.error, fail hard back to the user - raise CommandExecutionError('Issue executing powershell {0}'.format(cmd), info=results) + raise CommandExecutionError( + "Issue executing powershell {0}".format(cmd), info=results + ) try: - ret = _ps_xml_to_dict(xml.etree.ElementTree.fromstring(results['stdout'].encode('utf-8'))) + ret = _ps_xml_to_dict( + xml.etree.ElementTree.fromstring(results["stdout"].encode("utf-8")) + ) except xml.etree.ElementTree.ParseError: - results['stdout'] = results['stdout'][:1000] + ". . ." - raise CommandExecutionError('No XML results from powershell', info=results) + results["stdout"] = results["stdout"][:1000] + ". . ." + raise CommandExecutionError("No XML results from powershell", info=results) return ret def bootstrap(): - ''' + """ Make sure that nuget-anycpu.exe is installed. This will download the official nuget-anycpu.exe from the internet. @@ -113,14 +123,14 @@ def bootstrap(): .. code-block:: bash salt 'win01' psget.bootstrap - ''' - cmd = 'Get-PackageProvider -Name NuGet -ForceBootstrap | Select Name, Version, ProviderPath' + """ + cmd = "Get-PackageProvider -Name NuGet -ForceBootstrap | Select Name, Version, ProviderPath" ret = _pshell(cmd, depth=1) return ret def avail_modules(desc=False): - ''' + """ List available modules in registered Powershell module repositories. :param desc: If ``True``, the verbose description will be returned. @@ -132,8 +142,8 @@ def avail_modules(desc=False): salt 'win01' psget.avail_modules salt 'win01' psget.avail_modules desc=True - ''' - cmd = 'Find-Module | Select Name, Description' + """ + cmd = "Find-Module | Select Name, Description" modules = _pshell(cmd, depth=1) names = [] if desc: @@ -141,14 +151,14 @@ def avail_modules(desc=False): for key in modules: module = modules[key] if desc: - names[module['Name']] = module['Description'] + names[module["Name"]] = module["Description"] continue - names.append(module['Name']) + names.append(module["Name"]) return names def list_modules(desc=False): - ''' + """ List currently installed PSGet Modules on the system. :param desc: If ``True``, the verbose description will be returned. @@ -160,8 +170,8 @@ def list_modules(desc=False): salt 'win01' psget.list_modules salt 'win01' psget.list_modules desc=True - ''' - cmd = 'Get-InstalledModule' + """ + cmd = "Get-InstalledModule" modules = _pshell(cmd) names = [] if desc: @@ -169,15 +179,16 @@ def list_modules(desc=False): for key in modules: module = modules[key] if desc: - names[module['Name']] = module + names[module["Name"]] = module continue - names.append(module['Name']) + names.append(module["Name"]) return names -def install(name, minimum_version=None, required_version=None, scope=None, - repository=None): - ''' +def install( + name, minimum_version=None, required_version=None, scope=None, repository=None +): + """ Install a Powershell module from powershell gallery on the system. :param name: Name of a Powershell module @@ -200,28 +211,28 @@ def install(name, minimum_version=None, required_version=None, scope=None, .. code-block:: bash salt 'win01' psget.install PowerPlan - ''' + """ # Putting quotes around the parameter protects against command injection - flags = [('Name', name)] + flags = [("Name", name)] if minimum_version is not None: - flags.append(('MinimumVersion', minimum_version)) + flags.append(("MinimumVersion", minimum_version)) if required_version is not None: - flags.append(('RequiredVersion', required_version)) + flags.append(("RequiredVersion", required_version)) if scope is not None: - flags.append(('Scope', scope)) + flags.append(("Scope", scope)) if repository is not None: - flags.append(('Repository', repository)) - params = '' + flags.append(("Repository", repository)) + params = "" for flag, value in flags: - params += '-{0} {1} '.format(flag, value) - cmd = 'Install-Module {0} -Force'.format(params) + params += "-{0} {1} ".format(flag, value) + cmd = "Install-Module {0} -Force".format(params) _pshell(cmd) return name in list_modules() def update(name, maximum_version=None, required_version=None): - ''' + """ Update a PowerShell module to a specific version, or the newest :param name: Name of a Powershell module @@ -238,25 +249,25 @@ def update(name, maximum_version=None, required_version=None): .. code-block:: bash salt 'win01' psget.update PowerPlan - ''' + """ # Putting quotes around the parameter protects against command injection - flags = [('Name', name)] + flags = [("Name", name)] if maximum_version is not None: - flags.append(('MaximumVersion', maximum_version)) + flags.append(("MaximumVersion", maximum_version)) if required_version is not None: - flags.append(('RequiredVersion', required_version)) + flags.append(("RequiredVersion", required_version)) - params = '' + params = "" for flag, value in flags: - params += '-{0} {1} '.format(flag, value) - cmd = 'Update-Module {0} -Force'.format(params) + params += "-{0} {1} ".format(flag, value) + cmd = "Update-Module {0} -Force".format(params) _pshell(cmd) return name in list_modules() def remove(name): - ''' + """ Remove a Powershell DSC module from the system. :param name: Name of a Powershell DSC module @@ -267,7 +278,7 @@ def remove(name): .. code-block:: bash salt 'win01' psget.remove PowerPlan - ''' + """ # Putting quotes around the parameter protects against command injection cmd = 'Uninstall-Module "{0}"'.format(name) no_ret = _pshell(cmd) @@ -275,7 +286,7 @@ def remove(name): def register_repository(name, location, installation_policy=None): - ''' + """ Register a PSGet repository on the local machine :param name: The name for the repository @@ -293,24 +304,24 @@ def register_repository(name, location, installation_policy=None): .. code-block:: bash salt 'win01' psget.register_repository MyRepo https://myrepo.mycompany.com/packages - ''' + """ # Putting quotes around the parameter protects against command injection - flags = [('Name', name)] + flags = [("Name", name)] - flags.append(('SourceLocation', location)) + flags.append(("SourceLocation", location)) if installation_policy is not None: - flags.append(('InstallationPolicy', installation_policy)) + flags.append(("InstallationPolicy", installation_policy)) - params = '' + params = "" for flag, value in flags: - params += '-{0} {1} '.format(flag, value) - cmd = 'Register-PSRepository {0}'.format(params) + params += "-{0} {1} ".format(flag, value) + cmd = "Register-PSRepository {0}".format(params) no_ret = _pshell(cmd) return name not in list_modules() def get_repository(name): - ''' + """ Get the details of a local PSGet repository :param name: Name of the repository @@ -321,7 +332,7 @@ def get_repository(name): .. code-block:: bash salt 'win01' psget.get_repository MyRepo - ''' + """ # Putting quotes around the parameter protects against command injection cmd = 'Get-PSRepository "{0}"'.format(name) no_ret = _pshell(cmd) diff --git a/salt/modules/win_servermanager.py b/salt/modules/win_servermanager.py index d7da414f567..c6c2b6285af 100644 --- a/salt/modules/win_servermanager.py +++ b/salt/modules/win_servermanager.py @@ -1,21 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Manage Windows features via the ServerManager powershell module. Can list available and installed roles/features. Can install and remove roles/features. :maintainer: Shane Lee <slee@saltstack.com> :platform: Windows Server 2008R2 or greater :depends: PowerShell module ``ServerManager`` -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import logging +from __future__ import absolute_import, print_function, unicode_literals -try: - from shlex import quote as _cmd_quote # pylint: disable=E0611 -except ImportError: - from pipes import quote as _cmd_quote +import logging # Import Salt libs import salt.utils.json @@ -24,68 +20,83 @@ import salt.utils.powershell import salt.utils.versions from salt.exceptions import CommandExecutionError +try: + from shlex import quote as _cmd_quote # pylint: disable=E0611 +except ImportError: + from pipes import quote as _cmd_quote + + log = logging.getLogger(__name__) -__virtualname__ = 'win_servermanager' +__virtualname__ = "win_servermanager" def __virtual__(): - ''' + """ Load only on windows with servermanager module - ''' + """ if not salt.utils.platform.is_windows(): - return False + return ( + False, + "Module win_servermanager: module only works on Windows systems.", + ) - if salt.utils.versions.version_cmp(__grains__['osversion'], '6.1.7600') == -1: - return False, 'Failed to load win_servermanager module: ' \ - 'Requires Remote Server Administration Tools which ' \ - 'is only available on Windows 2008 R2 and later.' + if salt.utils.versions.version_cmp(__grains__["osversion"], "6.1.7600") == -1: + return ( + False, + "Failed to load win_servermanager module: " + "Requires Remote Server Administration Tools which " + "is only available on Windows 2008 R2 and later.", + ) - if not salt.utils.powershell.module_exists('ServerManager'): - return False, 'Failed to load win_servermanager module: ' \ - 'ServerManager module not available. ' \ - 'May need to install Remote Server Administration Tools.' + if not salt.utils.powershell.module_exists("ServerManager"): + return ( + False, + "Failed to load win_servermanager module: " + "ServerManager module not available. " + "May need to install Remote Server Administration Tools.", + ) return __virtualname__ def _pshell_json(cmd, cwd=None): - ''' + """ Execute the desired powershell command and ensure that it returns data in JSON format and load that into python - ''' - cmd = 'Import-Module ServerManager; {0}'.format(cmd) - if 'convertto-json' not in cmd.lower(): - cmd = '{0} | ConvertTo-Json'.format(cmd) - log.debug('PowerShell: %s', cmd) - ret = __salt__['cmd.run_all'](cmd, shell='powershell', cwd=cwd) + """ + cmd = "Import-Module ServerManager; {0}".format(cmd) + if "convertto-json" not in cmd.lower(): + cmd = "{0} | ConvertTo-Json".format(cmd) + log.debug("PowerShell: %s", cmd) + ret = __salt__["cmd.run_all"](cmd, shell="powershell", cwd=cwd) - if 'pid' in ret: - del ret['pid'] + if "pid" in ret: + del ret["pid"] - if ret.get('stderr', ''): - error = ret['stderr'].splitlines()[0] + if ret.get("stderr", ""): + error = ret["stderr"].splitlines()[0] raise CommandExecutionError(error, info=ret) - if 'retcode' not in ret or ret['retcode'] != 0: + if "retcode" not in ret or ret["retcode"] != 0: # run_all logs an error to log.error, fail hard back to the user raise CommandExecutionError( - 'Issue executing PowerShell {0}'.format(cmd), info=ret) + "Issue executing PowerShell {0}".format(cmd), info=ret + ) # Sometimes Powershell returns an empty string, which isn't valid JSON - if ret['stdout'] == '': - ret['stdout'] = '{}' + if ret["stdout"] == "": + ret["stdout"] = "{}" try: - ret = salt.utils.json.loads(ret['stdout'], strict=False) + ret = salt.utils.json.loads(ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError( - 'No JSON results from PowerShell', info=ret) + raise CommandExecutionError("No JSON results from PowerShell", info=ret) return ret def list_available(): - ''' + """ List available features to install Returns: @@ -97,16 +108,18 @@ def list_available(): .. code-block:: bash salt '*' win_servermanager.list_available - ''' - cmd = 'Import-Module ServerManager; ' \ - 'Get-WindowsFeature ' \ - '-ErrorAction SilentlyContinue ' \ - '-WarningAction SilentlyContinue' - return __salt__['cmd.shell'](cmd, shell='powershell') + """ + cmd = ( + "Import-Module ServerManager; " + "Get-WindowsFeature " + "-ErrorAction SilentlyContinue " + "-WarningAction SilentlyContinue" + ) + return __salt__["cmd.shell"](cmd, shell="powershell") def list_installed(): - ''' + """ List installed features. Supported on Windows Server 2008 and Windows 8 and newer. @@ -118,23 +131,25 @@ def list_installed(): .. code-block:: bash salt '*' win_servermanager.list_installed - ''' - cmd = 'Get-WindowsFeature ' \ - '-ErrorAction SilentlyContinue ' \ - '-WarningAction SilentlyContinue ' \ - '| Select DisplayName,Name,Installed' + """ + cmd = ( + "Get-WindowsFeature " + "-ErrorAction SilentlyContinue " + "-WarningAction SilentlyContinue " + "| Select DisplayName,Name,Installed" + ) features = _pshell_json(cmd) ret = {} for entry in features: - if entry['Installed']: - ret[entry['Name']] = entry['DisplayName'] + if entry["Installed"]: + ret[entry["Name"]] = entry["DisplayName"] return ret def install(feature, recurse=False, restart=False, source=None, exclude=None): - r''' + r""" Install a feature .. note:: @@ -202,26 +217,28 @@ def install(feature, recurse=False, restart=False, source=None, exclude=None): # Install the XPS Viewer, SNMP Service, and Remote Access passing a # list. Install all sub-features, but exclude the Web Server salt '*' win_servermanager.install "['XPS-Viewer', 'SNMP-Service', 'RemoteAccess']" True recurse=True exclude="Web-Server" - ''' + """ # If it is a list of features, make it a comma delimited string if isinstance(feature, list): - feature = ','.join(feature) + feature = ",".join(feature) # Use Install-WindowsFeature on Windows 2012 (osversion 6.2) and later # minions. Default to Add-WindowsFeature for earlier releases of Windows. # The newer command makes management tools optional so add them for parity # with old behavior. - command = 'Add-WindowsFeature' - management_tools = '' - if salt.utils.versions.version_cmp(__grains__['osversion'], '6.2') >= 0: - command = 'Install-WindowsFeature' - management_tools = '-IncludeManagementTools' + command = "Add-WindowsFeature" + management_tools = "" + if salt.utils.versions.version_cmp(__grains__["osversion"], "6.2") >= 0: + command = "Install-WindowsFeature" + management_tools = "-IncludeManagementTools" - cmd = '{0} -Name {1} {2} {3} {4} ' \ - '-WarningAction SilentlyContinue'\ - .format(command, _cmd_quote(feature), management_tools, - '-IncludeAllSubFeature' if recurse else '', - '' if source is None else '-Source {0}'.format(source)) + cmd = "{0} -Name {1} {2} {3} {4} " "-WarningAction SilentlyContinue".format( + command, + _cmd_quote(feature), + management_tools, + "-IncludeAllSubFeature" if recurse else "", + "" if source is None else "-Source {0}".format(source), + ) out = _pshell_json(cmd) # Uninstall items in the exclude list @@ -231,75 +248,79 @@ def install(feature, recurse=False, restart=False, source=None, exclude=None): removed = remove(exclude) # Results are stored in a list of dictionaries in `FeatureResult` - if out['FeatureResult']: - ret = {'ExitCode': out['ExitCode'], - 'RestartNeeded': False, - 'Restarted': False, - 'Features': {}, - 'Success': out['Success']} + if out["FeatureResult"]: + ret = { + "ExitCode": out["ExitCode"], + "RestartNeeded": False, + "Restarted": False, + "Features": {}, + "Success": out["Success"], + } # FeatureResult is a list of dicts, so each item is a dict - for item in out['FeatureResult']: - ret['Features'][item['Name']] = { - 'DisplayName': item['DisplayName'], - 'Message': item['Message'], - 'RestartNeeded': item['RestartNeeded'], - 'SkipReason': item['SkipReason'], - 'Success': item['Success'] + for item in out["FeatureResult"]: + ret["Features"][item["Name"]] = { + "DisplayName": item["DisplayName"], + "Message": item["Message"], + "RestartNeeded": item["RestartNeeded"], + "SkipReason": item["SkipReason"], + "Success": item["Success"], } - if item['RestartNeeded']: - ret['RestartNeeded'] = True + if item["RestartNeeded"]: + ret["RestartNeeded"] = True # Only items that installed are in the list of dictionaries # Add 'Already installed' for features that aren't in the list of dicts - for item in feature.split(','): - if item not in ret['Features']: - ret['Features'][item] = {'Message': 'Already installed'} + for item in feature.split(","): + if item not in ret["Features"]: + ret["Features"][item] = {"Message": "Already installed"} # Some items in the exclude list were removed after installation # Show what was done, update the dict if exclude is not None: # Features is a dict, so it only iterates over the keys - for item in removed['Features']: - if item in ret['Features']: - ret['Features'][item] = { - 'Message': 'Removed after installation (exclude)', - 'DisplayName': removed['Features'][item]['DisplayName'], - 'RestartNeeded': removed['Features'][item]['RestartNeeded'], - 'SkipReason': removed['Features'][item]['SkipReason'], - 'Success': removed['Features'][item]['Success'] + for item in removed["Features"]: + if item in ret["Features"]: + ret["Features"][item] = { + "Message": "Removed after installation (exclude)", + "DisplayName": removed["Features"][item]["DisplayName"], + "RestartNeeded": removed["Features"][item]["RestartNeeded"], + "SkipReason": removed["Features"][item]["SkipReason"], + "Success": removed["Features"][item]["Success"], } # Exclude items might need a restart - if removed['Features'][item]['RestartNeeded']: - ret['RestartNeeded'] = True + if removed["Features"][item]["RestartNeeded"]: + ret["RestartNeeded"] = True # Restart here if needed if restart: - if ret['RestartNeeded']: - if __salt__['system.restart'](in_seconds=True): - ret['Restarted'] = True + if ret["RestartNeeded"]: + if __salt__["system.restart"](in_seconds=True): + ret["Restarted"] = True return ret else: # If we get here then all features were already installed - ret = {'ExitCode': out['ExitCode'], - 'Features': {}, - 'RestartNeeded': False, - 'Restarted': False, - 'Success': out['Success']} + ret = { + "ExitCode": out["ExitCode"], + "Features": {}, + "RestartNeeded": False, + "Restarted": False, + "Success": out["Success"], + } - for item in feature.split(','): - ret['Features'][item] = {'Message': 'Already installed'} + for item in feature.split(","): + ret["Features"][item] = {"Message": "Already installed"} return ret def remove(feature, remove_payload=False, restart=False): - r''' + r""" Remove an installed feature .. note:: @@ -335,73 +356,79 @@ def remove(feature, remove_payload=False, restart=False): .. code-block:: bash salt -t 600 '*' win_servermanager.remove Telnet-Client - ''' + """ # If it is a list of features, make it a comma delimited string if isinstance(feature, list): - feature = ','.join(feature) + feature = ",".join(feature) # Use Uninstall-WindowsFeature on Windows 2012 (osversion 6.2) and later # minions. Default to Remove-WindowsFeature for earlier releases of Windows. # The newer command makes management tools optional so add them for parity # with old behavior. - command = 'Remove-WindowsFeature' - management_tools = '' - _remove_payload = '' - if salt.utils.versions.version_cmp(__grains__['osversion'], '6.2') >= 0: - command = 'Uninstall-WindowsFeature' - management_tools = '-IncludeManagementTools' + command = "Remove-WindowsFeature" + management_tools = "" + _remove_payload = "" + if salt.utils.versions.version_cmp(__grains__["osversion"], "6.2") >= 0: + command = "Uninstall-WindowsFeature" + management_tools = "-IncludeManagementTools" # Only available with the `Uninstall-WindowsFeature` command if remove_payload: - _remove_payload = '-Remove' + _remove_payload = "-Remove" - cmd = '{0} -Name {1} {2} {3} {4} ' \ - '-WarningAction SilentlyContinue'\ - .format(command, _cmd_quote(feature), management_tools, - _remove_payload, - '-Restart' if restart else '') + cmd = "{0} -Name {1} {2} {3} {4} " "-WarningAction SilentlyContinue".format( + command, + _cmd_quote(feature), + management_tools, + _remove_payload, + "-Restart" if restart else "", + ) try: out = _pshell_json(cmd) except CommandExecutionError as exc: - if 'ArgumentNotValid' in exc.message: - raise CommandExecutionError('Invalid Feature Name', info=exc.info) + if "ArgumentNotValid" in exc.message: + raise CommandExecutionError("Invalid Feature Name", info=exc.info) raise # Results are stored in a list of dictionaries in `FeatureResult` - if out['FeatureResult']: - ret = {'ExitCode': out['ExitCode'], - 'RestartNeeded': False, - 'Restarted': False, - 'Features': {}, - 'Success': out['Success']} + if out["FeatureResult"]: + ret = { + "ExitCode": out["ExitCode"], + "RestartNeeded": False, + "Restarted": False, + "Features": {}, + "Success": out["Success"], + } - for item in out['FeatureResult']: - ret['Features'][item['Name']] = { - 'DisplayName': item['DisplayName'], - 'Message': item['Message'], - 'RestartNeeded': item['RestartNeeded'], - 'SkipReason': item['SkipReason'], - 'Success': item['Success'] + for item in out["FeatureResult"]: + ret["Features"][item["Name"]] = { + "DisplayName": item["DisplayName"], + "Message": item["Message"], + "RestartNeeded": item["RestartNeeded"], + "SkipReason": item["SkipReason"], + "Success": item["Success"], } # Only items that installed are in the list of dictionaries # Add 'Not installed' for features that aren't in the list of dicts - for item in feature.split(','): - if item not in ret['Features']: - ret['Features'][item] = {'Message': 'Not installed'} + for item in feature.split(","): + if item not in ret["Features"]: + ret["Features"][item] = {"Message": "Not installed"} return ret else: # If we get here then none of the features were installed - ret = {'ExitCode': out['ExitCode'], - 'Features': {}, - 'RestartNeeded': False, - 'Restarted': False, - 'Success': out['Success']} + ret = { + "ExitCode": out["ExitCode"], + "Features": {}, + "RestartNeeded": False, + "Restarted": False, + "Success": out["Success"], + } - for item in feature.split(','): - ret['Features'][item] = {'Message': 'Not installed'} + for item in feature.split(","): + ret["Features"][item] = {"Message": "Not installed"} return ret diff --git a/salt/modules/win_service.py b/salt/modules/win_service.py index eb642134205..1d54cffe8d6 100644 --- a/salt/modules/win_service.py +++ b/salt/modules/win_service.py @@ -1,20 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Windows Service module. .. versionchanged:: 2016.11.0 - Rewritten to use PyWin32 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import fnmatch import logging import re import time +import salt.utils.path + # Import Salt libs import salt.utils.platform -import salt.utils.path from salt.exceptions import CommandExecutionError # Import 3rd party libs @@ -23,6 +25,7 @@ try: import win32service import win32serviceutil import pywintypes + HAS_WIN32_MODS = True except ImportError: HAS_WIN32_MODS = False @@ -30,81 +33,90 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'service' +__virtualname__ = "service" -SERVICE_TYPE = {1: 'Kernel Driver', - 2: 'File System Driver', - 4: 'Adapter Driver', - 8: 'Recognizer Driver', - 16: 'Win32 Own Process', - 32: 'Win32 Share Process', - 256: 'Interactive', - 'kernel': 1, - 'filesystem': 2, - 'adapter': 4, - 'recognizer': 8, - 'own': 16, - 'share': 32} +SERVICE_TYPE = { + 1: "Kernel Driver", + 2: "File System Driver", + 4: "Adapter Driver", + 8: "Recognizer Driver", + 16: "Win32 Own Process", + 32: "Win32 Share Process", + 256: "Interactive", + "kernel": 1, + "filesystem": 2, + "adapter": 4, + "recognizer": 8, + "own": 16, + "share": 32, +} -SERVICE_CONTROLS = {1: 'Stop', - 2: 'Pause/Continue', - 4: 'Shutdown', - 8: 'Change Parameters', - 16: 'Netbind Change', - 32: 'Hardware Profile Change', - 64: 'Power Event', - 128: 'Session Change', - 256: 'Pre-Shutdown', - 512: 'Time Change', - 1024: 'Trigger Event'} +SERVICE_CONTROLS = { + 1: "Stop", + 2: "Pause/Continue", + 4: "Shutdown", + 8: "Change Parameters", + 16: "Netbind Change", + 32: "Hardware Profile Change", + 64: "Power Event", + 128: "Session Change", + 256: "Pre-Shutdown", + 512: "Time Change", + 1024: "Trigger Event", +} -SERVICE_STATE = {1: 'Stopped', - 2: 'Start Pending', - 3: 'Stop Pending', - 4: 'Running', - 5: 'Continue Pending', - 6: 'Pause Pending', - 7: 'Paused'} +SERVICE_STATE = { + 1: "Stopped", + 2: "Start Pending", + 3: "Stop Pending", + 4: "Running", + 5: "Continue Pending", + 6: "Pause Pending", + 7: "Paused", +} -SERVICE_ERRORS = {0: 'No Error', - 1066: 'Service Specific Error'} +SERVICE_ERRORS = {0: "No Error", 1066: "Service Specific Error"} -SERVICE_START_TYPE = {'boot': 0, - 'system': 1, - 'auto': 2, - 'manual': 3, - 'disabled': 4, - 0: 'Boot', - 1: 'System', - 2: 'Auto', - 3: 'Manual', - 4: 'Disabled'} +SERVICE_START_TYPE = { + "boot": 0, + "system": 1, + "auto": 2, + "manual": 3, + "disabled": 4, + 0: "Boot", + 1: "System", + 2: "Auto", + 3: "Manual", + 4: "Disabled", +} -SERVICE_ERROR_CONTROL = {0: 'Ignore', - 1: 'Normal', - 2: 'Severe', - 3: 'Critical', - 'ignore': 0, - 'normal': 1, - 'severe': 2, - 'critical': 3} +SERVICE_ERROR_CONTROL = { + 0: "Ignore", + 1: "Normal", + 2: "Severe", + 3: "Critical", + "ignore": 0, + "normal": 1, + "severe": 2, + "critical": 3, +} def __virtual__(): - ''' + """ Only works on Windows systems with PyWin32 installed - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Module win_service: module only works on Windows.' + return False, "Module win_service: module only works on Windows." if not HAS_WIN32_MODS: - return False, 'Module win_service: failed to load win32 modules' + return False, "Module win_service: failed to load win32 modules" return __virtualname__ def _status_wait(service_name, end_time, service_states): - ''' + """ Helper function that will wait for the status of the service to match the provided status before an end time expires. Used for service stop and start @@ -124,17 +136,17 @@ def _status_wait(service_name, end_time, service_states): dict: A dictionary containing information about the service. :codeauthor: Damon Atkins <https://github.com/damon-atkins> - ''' + """ info_results = info(service_name) - while info_results['Status'] in service_states and time.time() < end_time: + while info_results["Status"] in service_states and time.time() < end_time: # From Microsoft: Do not wait longer than the wait hint. A good interval # is one-tenth of the wait hint but not less than 1 second and not more # than 10 seconds. # https://docs.microsoft.com/en-us/windows/desktop/services/starting-a-service # https://docs.microsoft.com/en-us/windows/desktop/services/stopping-a-service # Wait hint is in ms - wait_time = info_results['Status_WaitHint'] + wait_time = info_results["Status_WaitHint"] # Convert to seconds or 0 wait_time = wait_time / 1000 if wait_time else 0 if wait_time < 1: @@ -149,7 +161,7 @@ def _status_wait(service_name, end_time, service_states): def _cmd_quote(cmd): - r''' + r""" Helper function to properly format the path to the binary for the service Must be wrapped in double quotes to account for paths that have spaces. For example: @@ -161,11 +173,11 @@ def _cmd_quote(cmd): Returns: str: Properly quoted path to the binary - ''' + """ # Remove all single and double quotes from the beginning and the end - pattern = re.compile('^(\\"|\').*|.*(\\"|\')$') + pattern = re.compile("^(\\\"|').*|.*(\\\"|')$") while pattern.match(cmd) is not None: - cmd = cmd.strip('"').strip('\'') + cmd = cmd.strip('"').strip("'") # Ensure the path to the binary is wrapped in double quotes to account for # spaces in the path cmd = '"{0}"'.format(cmd) @@ -173,7 +185,7 @@ def _cmd_quote(cmd): def get_enabled(): - ''' + """ Return a list of enabled services. Enabled is defined as a service that is marked to Auto Start. @@ -185,18 +197,18 @@ def get_enabled(): .. code-block:: bash salt '*' service.get_enabled - ''' + """ raw_services = _get_services() services = set() for service in raw_services: - if info(service['ServiceName'])['StartType'] in ['Auto']: - services.add(service['ServiceName']) + if info(service["ServiceName"])["StartType"] in ["Auto"]: + services.add(service["ServiceName"]) return sorted(services) def get_disabled(): - ''' + """ Return a list of disabled services. Disabled is defined as a service that is marked 'Disabled' or 'Manual'. @@ -208,18 +220,18 @@ def get_disabled(): .. code-block:: bash salt '*' service.get_disabled - ''' + """ raw_services = _get_services() services = set() for service in raw_services: - if info(service['ServiceName'])['StartType'] in ['Manual', 'Disabled']: - services.add(service['ServiceName']) + if info(service["ServiceName"])["StartType"] in ["Manual", "Disabled"]: + services.add(service["ServiceName"]) return sorted(services) def available(name): - ''' + """ Check if a service is available on the system. Args: @@ -233,7 +245,7 @@ def available(name): .. code-block:: bash salt '*' service.available <service name> - ''' + """ for service in get_all(): if name.lower() == service.lower(): return True @@ -242,7 +254,7 @@ def available(name): def missing(name): - ''' + """ The inverse of service.available. Args: @@ -256,16 +268,17 @@ def missing(name): .. code-block:: bash salt '*' service.missing <service name> - ''' + """ return name not in get_all() def _get_services(): - ''' + """ Returns a list of all services on the system. - ''' + """ handle_scm = win32service.OpenSCManager( - None, None, win32service.SC_MANAGER_ENUMERATE_SERVICE) + None, None, win32service.SC_MANAGER_ENUMERATE_SERVICE + ) try: services = win32service.EnumServicesStatusEx(handle_scm) @@ -278,7 +291,7 @@ def _get_services(): def get_all(): - ''' + """ Return all installed services Returns: @@ -289,18 +302,18 @@ def get_all(): .. code-block:: bash salt '*' service.get_all - ''' + """ services = _get_services() ret = set() for service in services: - ret.add(service['ServiceName']) + ret.add(service["ServiceName"]) return sorted(ret) def get_service_name(*args): - ''' + """ The Display Name is what is displayed in Windows when services.msc is executed. Each Display Name has an associated Service Name which is the actual name of the service. This function allows you to discover the @@ -321,24 +334,26 @@ def get_service_name(*args): salt '*' service.get_service_name salt '*' service.get_service_name 'Google Update Service (gupdate)' 'DHCP Client' - ''' + """ raw_services = _get_services() services = dict() for raw_service in raw_services: if args: - if raw_service['DisplayName'] in args or \ - raw_service['ServiceName'] in args or \ - raw_service['ServiceName'].lower() in args: - services[raw_service['DisplayName']] = raw_service['ServiceName'] + if ( + raw_service["DisplayName"] in args + or raw_service["ServiceName"] in args + or raw_service["ServiceName"].lower() in args + ): + services[raw_service["DisplayName"]] = raw_service["ServiceName"] else: - services[raw_service['DisplayName']] = raw_service['ServiceName'] + services[raw_service["DisplayName"]] = raw_service["ServiceName"] return services def info(name): - ''' + """ Get information about a service on the system Args: @@ -353,24 +368,29 @@ def info(name): .. code-block:: bash salt '*' service.info spooler - ''' + """ try: handle_scm = win32service.OpenSCManager( - None, None, win32service.SC_MANAGER_CONNECT) + None, None, win32service.SC_MANAGER_CONNECT + ) except pywintypes.error as exc: raise CommandExecutionError( - 'Failed to connect to the SCM: {0}'.format(exc.strerror)) + "Failed to connect to the SCM: {0}".format(exc.strerror) + ) try: handle_svc = win32service.OpenService( - handle_scm, name, - win32service.SERVICE_ENUMERATE_DEPENDENTS | - win32service.SERVICE_INTERROGATE | - win32service.SERVICE_QUERY_CONFIG | - win32service.SERVICE_QUERY_STATUS) + handle_scm, + name, + win32service.SERVICE_ENUMERATE_DEPENDENTS + | win32service.SERVICE_INTERROGATE + | win32service.SERVICE_QUERY_CONFIG + | win32service.SERVICE_QUERY_STATUS, + ) except pywintypes.error as exc: raise CommandExecutionError( - 'Failed To Open {0}: {1}'.format(name, exc.strerror)) + "Failed To Open {0}: {1}".format(name, exc.strerror) + ) try: config_info = win32service.QueryServiceConfig(handle_svc) @@ -378,35 +398,36 @@ def info(name): try: description = win32service.QueryServiceConfig2( - handle_svc, win32service.SERVICE_CONFIG_DESCRIPTION) + handle_svc, win32service.SERVICE_CONFIG_DESCRIPTION + ) except pywintypes.error: - description = 'Failed to get description' + description = "Failed to get description" delayed_start = win32service.QueryServiceConfig2( - handle_svc, win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO) + handle_svc, win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO + ) finally: win32service.CloseServiceHandle(handle_scm) win32service.CloseServiceHandle(handle_svc) ret = dict() try: - sid = win32security.LookupAccountName( - '', 'NT Service\\{0}'.format(name))[0] - ret['sid'] = win32security.ConvertSidToStringSid(sid) + sid = win32security.LookupAccountName("", "NT Service\\{0}".format(name))[0] + ret["sid"] = win32security.ConvertSidToStringSid(sid) except pywintypes.error: - ret['sid'] = 'Failed to get SID' + ret["sid"] = "Failed to get SID" - ret['BinaryPath'] = config_info[3] - ret['LoadOrderGroup'] = config_info[4] - ret['TagID'] = config_info[5] - ret['Dependencies'] = config_info[6] - ret['ServiceAccount'] = config_info[7] - ret['DisplayName'] = config_info[8] - ret['Description'] = description - ret['Status_ServiceCode'] = status_info['ServiceSpecificExitCode'] - ret['Status_CheckPoint'] = status_info['CheckPoint'] - ret['Status_WaitHint'] = status_info['WaitHint'] - ret['StartTypeDelayed'] = delayed_start + ret["BinaryPath"] = config_info[3] + ret["LoadOrderGroup"] = config_info[4] + ret["TagID"] = config_info[5] + ret["Dependencies"] = config_info[6] + ret["ServiceAccount"] = config_info[7] + ret["DisplayName"] = config_info[8] + ret["Description"] = description + ret["Status_ServiceCode"] = status_info["ServiceSpecificExitCode"] + ret["Status_CheckPoint"] = status_info["CheckPoint"] + ret["Status_WaitHint"] = status_info["WaitHint"] + ret["StartTypeDelayed"] = delayed_start flags = list() for bit in SERVICE_TYPE: @@ -414,40 +435,40 @@ def info(name): if config_info[0] & bit: flags.append(SERVICE_TYPE[bit]) - ret['ServiceType'] = flags if flags else config_info[0] + ret["ServiceType"] = flags if flags else config_info[0] flags = list() for bit in SERVICE_CONTROLS: - if status_info['ControlsAccepted'] & bit: + if status_info["ControlsAccepted"] & bit: flags.append(SERVICE_CONTROLS[bit]) - ret['ControlsAccepted'] = flags if flags else status_info['ControlsAccepted'] + ret["ControlsAccepted"] = flags if flags else status_info["ControlsAccepted"] try: - ret['Status_ExitCode'] = SERVICE_ERRORS[status_info['Win32ExitCode']] + ret["Status_ExitCode"] = SERVICE_ERRORS[status_info["Win32ExitCode"]] except KeyError: - ret['Status_ExitCode'] = status_info['Win32ExitCode'] + ret["Status_ExitCode"] = status_info["Win32ExitCode"] try: - ret['StartType'] = SERVICE_START_TYPE[config_info[1]] + ret["StartType"] = SERVICE_START_TYPE[config_info[1]] except KeyError: - ret['StartType'] = config_info[1] + ret["StartType"] = config_info[1] try: - ret['ErrorControl'] = SERVICE_ERROR_CONTROL[config_info[2]] + ret["ErrorControl"] = SERVICE_ERROR_CONTROL[config_info[2]] except KeyError: - ret['ErrorControl'] = config_info[2] + ret["ErrorControl"] = config_info[2] try: - ret['Status'] = SERVICE_STATE[status_info['CurrentState']] + ret["Status"] = SERVICE_STATE[status_info["CurrentState"]] except KeyError: - ret['Status'] = status_info['CurrentState'] + ret["Status"] = status_info["CurrentState"] return ret def start(name, timeout=90): - ''' + """ Start the specified service. .. warning:: @@ -472,28 +493,31 @@ def start(name, timeout=90): .. code-block:: bash salt '*' service.start <service name> - ''' + """ # Set the service to manual if disabled if disabled(name): - modify(name, start_type='Manual') + modify(name, start_type="Manual") try: win32serviceutil.StartService(name) except pywintypes.error as exc: if exc.winerror != 1056: raise CommandExecutionError( - 'Failed To Start {0}: {1}'.format(name, exc.strerror)) + "Failed To Start {0}: {1}".format(name, exc.strerror) + ) log.debug('Service "{0}" is running'.format(name)) - srv_status = _status_wait(service_name=name, - end_time=time.time() + int(timeout), - service_states=['Start Pending', 'Stopped']) + srv_status = _status_wait( + service_name=name, + end_time=time.time() + int(timeout), + service_states=["Start Pending", "Stopped"], + ) - return srv_status['Status'] == 'Running' + return srv_status["Status"] == "Running" def stop(name, timeout=90): - ''' + """ Stop the specified service Args: @@ -514,24 +538,27 @@ def stop(name, timeout=90): .. code-block:: bash salt '*' service.stop <service name> - ''' + """ try: win32serviceutil.StopService(name) except pywintypes.error as exc: if exc.winerror != 1062: raise CommandExecutionError( - 'Failed To Stop {0}: {1}'.format(name, exc.strerror)) + "Failed To Stop {0}: {1}".format(name, exc.strerror) + ) log.debug('Service "{0}" is not running'.format(name)) - srv_status = _status_wait(service_name=name, - end_time=time.time() + int(timeout), - service_states=['Running', 'Stop Pending']) + srv_status = _status_wait( + service_name=name, + end_time=time.time() + int(timeout), + service_states=["Running", "Stop Pending"], + ) - return srv_status['Status'] == 'Stopped' + return srv_status["Status"] == "Stopped" def restart(name, timeout=90): - ''' + """ Restart the named service. This issues a stop command followed by a start. Args: @@ -560,17 +587,16 @@ def restart(name, timeout=90): .. code-block:: bash salt '*' service.restart <service name> - ''' - if 'salt-minion' in name: + """ + if "salt-minion" in name: create_win_salt_restart_task() return execute_salt_restart_task() - return stop(name=name, timeout=timeout) and \ - start(name=name, timeout=timeout) + return stop(name=name, timeout=timeout) and start(name=name, timeout=timeout) def create_win_salt_restart_task(): - ''' + """ Create a task in Windows task scheduler to enable restarting the salt-minion Returns: @@ -581,24 +607,25 @@ def create_win_salt_restart_task(): .. code-block:: bash salt '*' service.create_win_salt_restart_task() - ''' + """ # Updated to use full name for Nessus agent - cmd = salt.utils.path.which('cmd') - args = '/c ping -n 3 127.0.0.1 && net stop salt-minion && net start ' \ - 'salt-minion' - return __salt__['task.create_task'](name='restart-salt-minion', - user_name='System', - force=True, - action_type='Execute', - cmd=cmd, - arguments=args, - trigger_type='Once', - start_date='1975-01-01', - start_time='01:00') + cmd = salt.utils.path.which("cmd") + args = "/c ping -n 3 127.0.0.1 && net stop salt-minion && net start " "salt-minion" + return __salt__["task.create_task"]( + name="restart-salt-minion", + user_name="System", + force=True, + action_type="Execute", + cmd=cmd, + arguments=args, + trigger_type="Once", + start_date="1975-01-01", + start_time="01:00", + ) def execute_salt_restart_task(): - ''' + """ Run the Windows Salt restart task Returns: @@ -609,12 +636,12 @@ def execute_salt_restart_task(): .. code-block:: bash salt '*' service.execute_salt_restart_task() - ''' - return __salt__['task.run'](name='restart-salt-minion') + """ + return __salt__["task.run"](name="restart-salt-minion") def status(name, *args, **kwargs): - ''' + """ Return the status for a service. If the name contains globbing, a dict mapping service name to True/False values is returned. @@ -634,24 +661,24 @@ def status(name, *args, **kwargs): .. code-block:: bash salt '*' service.status <service name> - ''' + """ results = {} all_services = get_all() - contains_globbing = bool(re.search(r'\*|\?|\[.+\]', name)) + contains_globbing = bool(re.search(r"\*|\?|\[.+\]", name)) if contains_globbing: services = fnmatch.filter(all_services, name) else: services = [name] for service in services: - results[service] = info(service)['Status'] in ['Running', 'Stop Pending'] + results[service] = info(service)["Status"] in ["Running", "Stop Pending"] if contains_globbing: return results return results[name] def getsid(name): - ''' + """ Return the SID for this windows service Args: @@ -665,26 +692,28 @@ def getsid(name): .. code-block:: bash salt '*' service.getsid <service name> - ''' - return info(name)['sid'] + """ + return info(name)["sid"] -def modify(name, - bin_path=None, - exe_args=None, - display_name=None, - description=None, - service_type=None, - start_type=None, - start_delayed=None, - error_control=None, - load_order_group=None, - dependencies=None, - account_name=None, - account_password=None, - run_interactive=None): +def modify( + name, + bin_path=None, + exe_args=None, + display_name=None, + description=None, + service_type=None, + start_type=None, + start_delayed=None, + error_control=None, + load_order_group=None, + dependencies=None, + account_name=None, + account_password=None, + run_interactive=None, +): # pylint: disable=anomalous-backslash-in-string - ''' + """ Modify a service's parameters. Changes will not be made for parameters that are not passed. @@ -782,22 +811,22 @@ def modify(name, salt '*' service.modify spooler start_type=disabled - ''' + """ # pylint: enable=anomalous-backslash-in-string # https://msdn.microsoft.com/en-us/library/windows/desktop/ms681987(v=vs.85).aspx # https://msdn.microsoft.com/en-us/library/windows/desktop/ms681988(v-vs.85).aspx - handle_scm = win32service.OpenSCManager( - None, None, win32service.SC_MANAGER_CONNECT) + handle_scm = win32service.OpenSCManager(None, None, win32service.SC_MANAGER_CONNECT) try: handle_svc = win32service.OpenService( handle_scm, name, - win32service.SERVICE_CHANGE_CONFIG | - win32service.SERVICE_QUERY_CONFIG) + win32service.SERVICE_CHANGE_CONFIG | win32service.SERVICE_QUERY_CONFIG, + ) except pywintypes.error as exc: raise CommandExecutionError( - 'Failed To Open {0}: {1}'.format(name, exc.strerror)) + "Failed To Open {0}: {1}".format(name, exc.strerror) + ) config_info = win32service.QueryServiceConfig(handle_svc) @@ -808,25 +837,23 @@ def modify(name, # shlex.quote the path to the binary bin_path = _cmd_quote(bin_path) if exe_args is not None: - bin_path = '{0} {1}'.format(bin_path, exe_args) - changes['BinaryPath'] = bin_path + bin_path = "{0} {1}".format(bin_path, exe_args) + changes["BinaryPath"] = bin_path if service_type is not None: if service_type.lower() in SERVICE_TYPE: service_type = SERVICE_TYPE[service_type.lower()] if run_interactive: - service_type = service_type | \ - win32service.SERVICE_INTERACTIVE_PROCESS + service_type = service_type | win32service.SERVICE_INTERACTIVE_PROCESS else: raise CommandExecutionError( - 'Invalid Service Type: {0}'.format(service_type)) + "Invalid Service Type: {0}".format(service_type) + ) else: if run_interactive is True: - service_type = config_info[0] | \ - win32service.SERVICE_INTERACTIVE_PROCESS + service_type = config_info[0] | win32service.SERVICE_INTERACTIVE_PROCESS elif run_interactive is False: - service_type = config_info[0] ^ \ - win32service.SERVICE_INTERACTIVE_PROCESS + service_type = config_info[0] ^ win32service.SERVICE_INTERACTIVE_PROCESS else: service_type = win32service.SERVICE_NO_CHANGE @@ -836,15 +863,14 @@ def modify(name, if isinstance(bit, int) and service_type & bit: flags.append(SERVICE_TYPE[bit]) - changes['ServiceType'] = flags if flags else service_type + changes["ServiceType"] = flags if flags else service_type if start_type is not None: if start_type.lower() in SERVICE_START_TYPE: start_type = SERVICE_START_TYPE[start_type.lower()] else: - raise CommandExecutionError( - 'Invalid Start Type: {0}'.format(start_type)) - changes['StartType'] = SERVICE_START_TYPE[start_type] + raise CommandExecutionError("Invalid Start Type: {0}".format(start_type)) + changes["StartType"] = SERVICE_START_TYPE[start_type] else: start_type = win32service.SERVICE_NO_CHANGE @@ -853,44 +879,48 @@ def modify(name, error_control = SERVICE_ERROR_CONTROL[error_control.lower()] else: raise CommandExecutionError( - 'Invalid Error Control: {0}'.format(error_control)) - changes['ErrorControl'] = SERVICE_ERROR_CONTROL[error_control] + "Invalid Error Control: {0}".format(error_control) + ) + changes["ErrorControl"] = SERVICE_ERROR_CONTROL[error_control] else: error_control = win32service.SERVICE_NO_CHANGE if account_name is not None: - changes['ServiceAccount'] = account_name - if account_name in ['LocalSystem', 'LocalService', 'NetworkService']: - account_password = '' + changes["ServiceAccount"] = account_name + if account_name in ["LocalSystem", "LocalService", "NetworkService"]: + account_password = "" if account_password is not None: - changes['ServiceAccountPassword'] = 'XXX-REDACTED-XXX' + changes["ServiceAccountPassword"] = "XXX-REDACTED-XXX" if load_order_group is not None: - changes['LoadOrderGroup'] = load_order_group + changes["LoadOrderGroup"] = load_order_group if dependencies is not None: - changes['Dependencies'] = dependencies + changes["Dependencies"] = dependencies if display_name is not None: - changes['DisplayName'] = display_name + changes["DisplayName"] = display_name - win32service.ChangeServiceConfig(handle_svc, - service_type, - start_type, - error_control, - bin_path, - load_order_group, - 0, - dependencies, - account_name, - account_password, - display_name) + win32service.ChangeServiceConfig( + handle_svc, + service_type, + start_type, + error_control, + bin_path, + load_order_group, + 0, + dependencies, + account_name, + account_password, + display_name, + ) if description is not None: win32service.ChangeServiceConfig2( - handle_svc, win32service.SERVICE_CONFIG_DESCRIPTION, description) - changes['Description'] = description + handle_svc, win32service.SERVICE_CONFIG_DESCRIPTION, description + ) + changes["Description"] = description if start_delayed is not None: # You can only set delayed start for services that are set to auto start @@ -898,11 +928,13 @@ def modify(name, # Start type -1 is no change if (start_type == -1 and config_info[1] == 2) or start_type == 2: win32service.ChangeServiceConfig2( - handle_svc, win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, - start_delayed) - changes['StartTypeDelayed'] = start_delayed + handle_svc, + win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, + start_delayed, + ) + changes["StartTypeDelayed"] = start_delayed else: - changes['Warning'] = 'start_delayed: Requires start_type "auto"' + changes["Warning"] = 'start_delayed: Requires start_type "auto"' win32service.CloseServiceHandle(handle_scm) win32service.CloseServiceHandle(handle_svc) @@ -910,8 +942,8 @@ def modify(name, return changes -def enable(name, start_type='auto', start_delayed=False, **kwargs): - ''' +def enable(name, start_type="auto", start_delayed=False, **kwargs): + """ Enable the named service to start at boot Args: @@ -939,18 +971,21 @@ def enable(name, start_type='auto', start_delayed=False, **kwargs): .. code-block:: bash salt '*' service.enable <service name> - ''' + """ modify(name, start_type=start_type, start_delayed=start_delayed) svcstat = info(name) - if start_type.lower() == 'auto': - return svcstat['StartType'].lower() == start_type.lower() and svcstat['StartTypeDelayed'] == start_delayed + if start_type.lower() == "auto": + return ( + svcstat["StartType"].lower() == start_type.lower() + and svcstat["StartTypeDelayed"] == start_delayed + ) else: - return svcstat['StartType'].lower() == start_type.lower() + return svcstat["StartType"].lower() == start_type.lower() def disable(name, **kwargs): - ''' + """ Disable the named service to start at boot Args: @@ -964,13 +999,13 @@ def disable(name, **kwargs): .. code-block:: bash salt '*' service.disable <service name> - ''' - modify(name, start_type='Disabled') - return info(name)['StartType'] == 'Disabled' + """ + modify(name, start_type="Disabled") + return info(name)["StartType"] == "Disabled" def enabled(name, **kwargs): - ''' + """ Check to see if the named service is enabled to start on boot Args: @@ -984,12 +1019,12 @@ def enabled(name, **kwargs): .. code-block:: bash salt '*' service.enabled <service name> - ''' - return info(name)['StartType'] == 'Auto' + """ + return info(name)["StartType"] == "Auto" def disabled(name): - ''' + """ Check to see if the named service is disabled to start on boot Args: @@ -1003,26 +1038,28 @@ def disabled(name): .. code-block:: bash salt '*' service.disabled <service name> - ''' + """ return not enabled(name) -def create(name, - bin_path, - exe_args=None, - display_name=None, - description=None, - service_type='own', - start_type='manual', - start_delayed=False, - error_control='normal', - load_order_group=None, - dependencies=None, - account_name='.\\LocalSystem', - account_password=None, - run_interactive=False, - **kwargs): - ''' +def create( + name, + bin_path, + exe_args=None, + display_name=None, + description=None, + service_type="own", + start_type="manual", + start_delayed=False, + error_control="normal", + load_order_group=None, + dependencies=None, + account_name=".\\LocalSystem", + account_password=None, + run_interactive=False, + **kwargs +): + """ Create the named service. .. versionadded:: 2015.8.0 @@ -1120,80 +1157,88 @@ def create(name, .. code-block:: bash salt '*' service.create <service name> <path to exe> display_name='<display name>' - ''' + """ if display_name is None: display_name = name # Test if the service already exists if name in get_all(): - raise CommandExecutionError('Service Already Exists: {0}'.format(name)) + raise CommandExecutionError("Service Already Exists: {0}".format(name)) # shlex.quote the path to the binary bin_path = _cmd_quote(bin_path) if exe_args is not None: - bin_path = '{0} {1}'.format(bin_path, exe_args) + bin_path = "{0} {1}".format(bin_path, exe_args) if service_type.lower() in SERVICE_TYPE: service_type = SERVICE_TYPE[service_type.lower()] if run_interactive: - service_type = service_type | \ - win32service.SERVICE_INTERACTIVE_PROCESS + service_type = service_type | win32service.SERVICE_INTERACTIVE_PROCESS else: - raise CommandExecutionError( - 'Invalid Service Type: {0}'.format(service_type)) + raise CommandExecutionError("Invalid Service Type: {0}".format(service_type)) if start_type.lower() in SERVICE_START_TYPE: start_type = SERVICE_START_TYPE[start_type.lower()] else: - raise CommandExecutionError( - 'Invalid Start Type: {0}'.format(start_type)) + raise CommandExecutionError("Invalid Start Type: {0}".format(start_type)) if error_control.lower() in SERVICE_ERROR_CONTROL: error_control = SERVICE_ERROR_CONTROL[error_control.lower()] else: - raise CommandExecutionError( - 'Invalid Error Control: {0}'.format(error_control)) + raise CommandExecutionError("Invalid Error Control: {0}".format(error_control)) if start_delayed: if start_type != 2: raise CommandExecutionError( - 'Invalid Parameter: start_delayed requires start_type "auto"') + 'Invalid Parameter: start_delayed requires start_type "auto"' + ) - if account_name in ['LocalSystem', '.\\LocalSystem', - 'LocalService', '.\\LocalService', - 'NetworkService', '.\\NetworkService']: - account_password = '' + if account_name in [ + "LocalSystem", + ".\\LocalSystem", + "LocalService", + ".\\LocalService", + "NetworkService", + ".\\NetworkService", + ]: + account_password = "" # Connect to Service Control Manager handle_scm = win32service.OpenSCManager( - None, None, win32service.SC_MANAGER_ALL_ACCESS) + None, None, win32service.SC_MANAGER_ALL_ACCESS + ) # Create the service - handle_svc = win32service.CreateService(handle_scm, - name, - display_name, - win32service.SERVICE_ALL_ACCESS, - service_type, - start_type, - error_control, - bin_path, - load_order_group, - 0, - dependencies, - account_name, - account_password) + handle_svc = win32service.CreateService( + handle_scm, + name, + display_name, + win32service.SERVICE_ALL_ACCESS, + service_type, + start_type, + error_control, + bin_path, + load_order_group, + 0, + dependencies, + account_name, + account_password, + ) if description is not None: win32service.ChangeServiceConfig2( - handle_svc, win32service.SERVICE_CONFIG_DESCRIPTION, description) + handle_svc, win32service.SERVICE_CONFIG_DESCRIPTION, description + ) if start_delayed is not None: # You can only set delayed start for services that are set to auto start # Start type 2 is Auto if start_type == 2: win32service.ChangeServiceConfig2( - handle_svc, win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, - start_delayed) + handle_svc, + win32service.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, + start_delayed, + ) win32service.CloseServiceHandle(handle_scm) win32service.CloseServiceHandle(handle_svc) @@ -1202,7 +1247,7 @@ def create(name, def delete(name, timeout=90): - ''' + """ Delete the named service Args: @@ -1225,18 +1270,19 @@ def delete(name, timeout=90): .. code-block:: bash salt '*' service.delete <service name> - ''' - handle_scm = win32service.OpenSCManager( - None, None, win32service.SC_MANAGER_CONNECT) + """ + handle_scm = win32service.OpenSCManager(None, None, win32service.SC_MANAGER_CONNECT) try: handle_svc = win32service.OpenService( - handle_scm, name, win32service.SERVICE_ALL_ACCESS) + handle_scm, name, win32service.SERVICE_ALL_ACCESS + ) except pywintypes.error as exc: win32service.CloseServiceHandle(handle_scm) if exc.winerror != 1060: raise CommandExecutionError( - 'Failed to open {0}. {1}'.format(name, exc.strerror)) + "Failed to open {0}. {1}".format(name, exc.strerror) + ) log.debug('Service "{0}" is not present'.format(name)) return True @@ -1244,9 +1290,10 @@ def delete(name, timeout=90): win32service.DeleteService(handle_svc) except pywintypes.error as exc: raise CommandExecutionError( - 'Failed to delete {0}. {1}'.format(name, exc.strerror)) + "Failed to delete {0}. {1}".format(name, exc.strerror) + ) finally: - log.debug('Cleaning up') + log.debug("Cleaning up") win32service.CloseServiceHandle(handle_scm) win32service.CloseServiceHandle(handle_svc) diff --git a/salt/modules/win_shadow.py b/salt/modules/win_shadow.py index 8d610846277..14ca08f27db 100644 --- a/salt/modules/win_shadow.py +++ b/salt/modules/win_shadow.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage the shadow file .. important:: @@ -7,28 +7,28 @@ Manage the shadow file minion, and it is using a different module (or gives an error similar to *'shadow.info' is not available*), see :ref:`here <module-provider-override>`. -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.platform # Define the module's virtual name -__virtualname__ = 'shadow' +__virtualname__ = "shadow" def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ - return (False, 'Module win_shadow: module only works on Windows systems.') + return (False, "Module win_shadow: module only works on Windows systems.") def info(name): - ''' + """ Return information for the specified user This is just returns dummy data so that salt states can work. @@ -39,33 +39,37 @@ def info(name): .. code-block:: bash salt '*' shadow.info root - ''' - info = __salt__['user.info'](name=name) + """ + info = __salt__["user.info"](name=name) - ret = {'name': name, - 'passwd': '', - 'lstchg': '', - 'min': '', - 'max': '', - 'warn': '', - 'inact': '', - 'expire': ''} + ret = { + "name": name, + "passwd": "", + "lstchg": "", + "min": "", + "max": "", + "warn": "", + "inact": "", + "expire": "", + } if info: - ret = {'name': info['name'], - 'passwd': 'Unavailable', - 'lstchg': info['password_changed'], - 'min': '', - 'max': '', - 'warn': '', - 'inact': '', - 'expire': info['expiration_date']} + ret = { + "name": info["name"], + "passwd": "Unavailable", + "lstchg": info["password_changed"], + "min": "", + "max": "", + "warn": "", + "inact": "", + "expire": info["expiration_date"], + } return ret def set_expire(name, expire): - ''' + """ Set the expiration date for a user account. :param name: The name of the user account to edit. @@ -80,12 +84,12 @@ def set_expire(name, expire): .. code-block:: bash salt '*' shadow.set_expire <username> 2016/7/1 - ''' - return __salt__['user.update'](name, expiration_date=expire) + """ + return __salt__["user.update"](name, expiration_date=expire) def require_password_change(name): - ''' + """ Require the user to change their password the next time they log in. :param name: The name of the user account to require a password change. @@ -98,12 +102,12 @@ def require_password_change(name): .. code-block:: bash salt '*' shadow.require_password_change <username> - ''' - return __salt__['user.update'](name, expired=True) + """ + return __salt__["user.update"](name, expired=True) def unlock_account(name): - ''' + """ Unlocks a user account. :param name: The name of the user account to unlock. @@ -116,12 +120,12 @@ def unlock_account(name): .. code-block:: bash salt '*' shadow.unlock_account <username> - ''' - return __salt__['user.update'](name, unlock_account=True) + """ + return __salt__["user.update"](name, unlock_account=True) def set_password(name, password): - ''' + """ Set the password for a named user. :param str name: The name of the user account @@ -136,5 +140,5 @@ def set_password(name, password): .. code-block:: bash salt '*' shadow.set_password root mysecretpassword - ''' - return __salt__['user.update'](name=name, password=password) + """ + return __salt__["user.update"](name=name, password=password) diff --git a/salt/modules/win_smtp_server.py b/salt/modules/win_smtp_server.py index 1b15d1cfdf2..b943b5c2beb 100644 --- a/salt/modules/win_smtp_server.py +++ b/salt/modules/win_smtp_server.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing IIS SMTP server configuration on Windows servers. The Windows features 'SMTP-Server' and 'Web-WMI' must be installed. :depends: wmi -''' +""" # IIS metabase configuration settings: # https://goo.gl/XCt1uO @@ -19,46 +19,52 @@ The Windows features 'SMTP-Server' and 'Web-WMI' must be installed. # http://goo.gl/MrybFq # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import re -# Import Salt libs -from salt.exceptions import SaltInvocationError import salt.utils.args import salt.utils.platform +# Import Salt libs +from salt.exceptions import SaltInvocationError + # Import 3rd-party libs from salt.ext import six try: import wmi import salt.utils.winapi + _HAS_MODULE_DEPENDENCIES = True except ImportError: _HAS_MODULE_DEPENDENCIES = False -_DEFAULT_SERVER = 'SmtpSvc/1' -_WMI_NAMESPACE = 'MicrosoftIISv2' +_DEFAULT_SERVER = "SmtpSvc/1" +_WMI_NAMESPACE = "MicrosoftIISv2" _LOG = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'win_smtp_server' +__virtualname__ = "win_smtp_server" def __virtual__(): - ''' + """ Only works on Windows systems. - ''' + """ if salt.utils.platform.is_windows() and _HAS_MODULE_DEPENDENCIES: return __virtualname__ - return False + return ( + False, + "Module win_smtp_server: module only works on Windows systems with wmi.", + ) def _get_wmi_setting(wmi_class_name, setting, server): - ''' + """ Get the value of the setting for the provided class. - ''' + """ with salt.utils.winapi.Com(): try: connection = wmi.WMI(namespace=_WMI_NAMESPACE) @@ -67,16 +73,16 @@ def _get_wmi_setting(wmi_class_name, setting, server): objs = wmi_class([setting], Name=server)[0] ret = getattr(objs, setting) except wmi.x_wmi as error: - _LOG.error('Encountered WMI error: %s', error.com_error) + _LOG.error("Encountered WMI error: %s", error.com_error) except (AttributeError, IndexError) as error: - _LOG.error('Error getting %s: %s', wmi_class_name, error) + _LOG.error("Error getting %s: %s", wmi_class_name, error) return ret def _set_wmi_setting(wmi_class_name, setting, value, server): - ''' + """ Set the value of the setting for the provided class. - ''' + """ with salt.utils.winapi.Com(): try: connection = wmi.WMI(namespace=_WMI_NAMESPACE) @@ -84,30 +90,30 @@ def _set_wmi_setting(wmi_class_name, setting, value, server): objs = wmi_class(Name=server)[0] except wmi.x_wmi as error: - _LOG.error('Encountered WMI error: %s', error.com_error) + _LOG.error("Encountered WMI error: %s", error.com_error) except (AttributeError, IndexError) as error: - _LOG.error('Error getting %s: %s', wmi_class_name, error) + _LOG.error("Error getting %s: %s", wmi_class_name, error) try: setattr(objs, setting, value) return True except wmi.x_wmi as error: - _LOG.error('Encountered WMI error: %s', error.com_error) + _LOG.error("Encountered WMI error: %s", error.com_error) except AttributeError as error: - _LOG.error('Error setting %s: %s', setting, error) + _LOG.error("Error setting %s: %s", setting, error) return False def _normalize_server_settings(**settings): - ''' + """ Convert setting values that had been improperly converted to a dict back to a string. - ''' + """ ret = dict() settings = salt.utils.args.clean_kwargs(**settings) for setting in settings: if isinstance(settings[setting], dict): - _LOG.debug('Fixing value: %s', settings[setting]) + _LOG.debug("Fixing value: %s", settings[setting]) value_from_key = next(six.iterkeys(settings[setting])) ret[setting] = "{{{0}}}".format(value_from_key) @@ -117,7 +123,7 @@ def _normalize_server_settings(**settings): def get_log_format_types(): - ''' + """ Get all available log format names and ids. :return: A dictionary of the log format names and ids. @@ -128,9 +134,9 @@ def get_log_format_types(): .. code-block:: bash salt '*' win_smtp_server.get_log_format_types - ''' + """ ret = dict() - prefix = 'logging/' + prefix = "logging/" with salt.utils.winapi.Com(): try: @@ -139,20 +145,20 @@ def get_log_format_types(): # Remove the prefix from the name. for obj in objs: - name = six.text_type(obj.Name).replace(prefix, '', 1) + name = six.text_type(obj.Name).replace(prefix, "", 1) ret[name] = six.text_type(obj.LogModuleId) except wmi.x_wmi as error: - _LOG.error('Encountered WMI error: %s', error.com_error) + _LOG.error("Encountered WMI error: %s", error.com_error) except (AttributeError, IndexError) as error: - _LOG.error('Error getting IISLogModuleSetting: %s', error) + _LOG.error("Error getting IISLogModuleSetting: %s", error) if not ret: - _LOG.error('Unable to get log format types.') + _LOG.error("Unable to get log format types.") return ret def get_servers(): - ''' + """ Get the SMTP virtual server names. :return: A list of the SMTP virtual servers. @@ -163,7 +169,7 @@ def get_servers(): .. code-block:: bash salt '*' win_smtp_server.get_servers - ''' + """ ret = list() with salt.utils.winapi.Com(): @@ -174,16 +180,16 @@ def get_servers(): for obj in objs: ret.append(six.text_type(obj.Name)) except wmi.x_wmi as error: - _LOG.error('Encountered WMI error: %s', error.com_error) + _LOG.error("Encountered WMI error: %s", error.com_error) except (AttributeError, IndexError) as error: - _LOG.error('Error getting IIsSmtpServerSetting: %s', error) + _LOG.error("Error getting IIsSmtpServerSetting: %s", error) - _LOG.debug('Found SMTP servers: %s', ret) + _LOG.debug("Found SMTP servers: %s", ret) return ret def get_server_setting(settings, server=_DEFAULT_SERVER): - ''' + """ Get the value of the setting for the SMTP virtual server. :param str settings: A list of the setting names. @@ -197,11 +203,11 @@ def get_server_setting(settings, server=_DEFAULT_SERVER): .. code-block:: bash salt '*' win_smtp_server.get_server_setting settings="['MaxRecipients']" - ''' + """ ret = dict() if not settings: - _LOG.warning('No settings provided.') + _LOG.warning("No settings provided.") return ret with salt.utils.winapi.Com(): @@ -212,14 +218,14 @@ def get_server_setting(settings, server=_DEFAULT_SERVER): for setting in settings: ret[setting] = six.text_type(getattr(objs, setting)) except wmi.x_wmi as error: - _LOG.error('Encountered WMI error: %s', error.com_error) + _LOG.error("Encountered WMI error: %s", error.com_error) except (AttributeError, IndexError) as error: - _LOG.error('Error getting IIsSmtpServerSetting: %s', error) + _LOG.error("Error getting IIsSmtpServerSetting: %s", error) return ret def set_server_setting(settings, server=_DEFAULT_SERVER): - ''' + """ Set the value of the setting for the SMTP virtual server. .. note:: @@ -237,9 +243,9 @@ def set_server_setting(settings, server=_DEFAULT_SERVER): .. code-block:: bash salt '*' win_smtp_server.set_server_setting settings="{'MaxRecipients': '500'}" - ''' + """ if not settings: - _LOG.warning('No settings provided') + _LOG.warning("No settings provided") return False # Some fields are formatted like '{data}'. Salt tries to convert these to dicts @@ -249,7 +255,7 @@ def set_server_setting(settings, server=_DEFAULT_SERVER): current_settings = get_server_setting(settings=settings.keys(), server=server) if settings == current_settings: - _LOG.debug('Settings already contain the provided values.') + _LOG.debug("Settings already contain the provided values.") return True # Note that we must fetch all properties of IIsSmtpServerSetting below, since @@ -260,18 +266,20 @@ def set_server_setting(settings, server=_DEFAULT_SERVER): connection = wmi.WMI(namespace=_WMI_NAMESPACE) objs = connection.IIsSmtpServerSetting(Name=server)[0] except wmi.x_wmi as error: - _LOG.error('Encountered WMI error: %s', error.com_error) + _LOG.error("Encountered WMI error: %s", error.com_error) except (AttributeError, IndexError) as error: - _LOG.error('Error getting IIsSmtpServerSetting: %s', error) + _LOG.error("Error getting IIsSmtpServerSetting: %s", error) for setting in settings: - if six.text_type(settings[setting]) != six.text_type(current_settings[setting]): + if six.text_type(settings[setting]) != six.text_type( + current_settings[setting] + ): try: setattr(objs, setting, settings[setting]) except wmi.x_wmi as error: - _LOG.error('Encountered WMI error: %s', error.com_error) + _LOG.error("Encountered WMI error: %s", error.com_error) except AttributeError as error: - _LOG.error('Error setting %s: %s', setting, error) + _LOG.error("Error setting %s: %s", setting, error) # Get the settings post-change so that we can verify tht all properties # were modified successfully. Track the ones that weren't. @@ -282,15 +290,15 @@ def set_server_setting(settings, server=_DEFAULT_SERVER): if six.text_type(settings[setting]) != six.text_type(new_settings[setting]): failed_settings[setting] = settings[setting] if failed_settings: - _LOG.error('Failed to change settings: %s', failed_settings) + _LOG.error("Failed to change settings: %s", failed_settings) return False - _LOG.debug('Settings configured successfully: %s', settings.keys()) + _LOG.debug("Settings configured successfully: %s", settings.keys()) return True def get_log_format(server=_DEFAULT_SERVER): - ''' + """ Get the active log format for the SMTP virtual server. :param str server: The SMTP server name. @@ -303,21 +311,21 @@ def get_log_format(server=_DEFAULT_SERVER): .. code-block:: bash salt '*' win_smtp_server.get_log_format - ''' + """ log_format_types = get_log_format_types() - format_id = _get_wmi_setting('IIsSmtpServerSetting', 'LogPluginClsid', server) + format_id = _get_wmi_setting("IIsSmtpServerSetting", "LogPluginClsid", server) # Since IIsSmtpServerSetting stores the log type as an id, we need # to get the mapping from IISLogModuleSetting and extract the name. for key in log_format_types: if six.text_type(format_id) == log_format_types[key]: return key - _LOG.warning('Unable to determine log format.') + _LOG.warning("Unable to determine log format.") return None def set_log_format(log_format, server=_DEFAULT_SERVER): - ''' + """ Set the active log format for the SMTP virtual server. :param str log_format: The log format name. @@ -331,14 +339,15 @@ def set_log_format(log_format, server=_DEFAULT_SERVER): .. code-block:: bash salt '*' win_smtp_server.set_log_format 'Microsoft IIS Log File Format' - ''' - setting = 'LogPluginClsid' + """ + setting = "LogPluginClsid" log_format_types = get_log_format_types() format_id = log_format_types.get(log_format, None) if not format_id: - message = ("Invalid log format '{0}' specified. Valid formats:" - ' {1}').format(log_format, log_format_types.keys()) + message = ("Invalid log format '{0}' specified. Valid formats:" " {1}").format( + log_format, log_format_types.keys() + ) raise SaltInvocationError(message) _LOG.debug("Id for '%s' found: %s", log_format, format_id) @@ -346,10 +355,10 @@ def set_log_format(log_format, server=_DEFAULT_SERVER): current_log_format = get_log_format(server) if log_format == current_log_format: - _LOG.debug('%s already contains the provided format.', setting) + _LOG.debug("%s already contains the provided format.", setting) return True - _set_wmi_setting('IIsSmtpServerSetting', setting, format_id, server) + _set_wmi_setting("IIsSmtpServerSetting", setting, format_id, server) new_log_format = get_log_format(server) ret = log_format == new_log_format @@ -362,7 +371,7 @@ def set_log_format(log_format, server=_DEFAULT_SERVER): def get_connection_ip_list(as_wmi_format=False, server=_DEFAULT_SERVER): - ''' + """ Get the IPGrant list for the SMTP virtual server. :param bool as_wmi_format: Returns the connection IPs as a list in the format WMI expects. @@ -376,32 +385,34 @@ def get_connection_ip_list(as_wmi_format=False, server=_DEFAULT_SERVER): .. code-block:: bash salt '*' win_smtp_server.get_connection_ip_list - ''' + """ ret = dict() - setting = 'IPGrant' - reg_separator = r',\s*' + setting = "IPGrant" + reg_separator = r",\s*" if as_wmi_format: ret = list() - addresses = _get_wmi_setting('IIsIPSecuritySetting', setting, server) + addresses = _get_wmi_setting("IIsIPSecuritySetting", setting, server) # WMI returns the addresses as a tuple of unicode strings, each representing # an address/subnet pair. Remove extra spaces that may be present. for unnormalized_address in addresses: ip_address, subnet = re.split(reg_separator, unnormalized_address) if as_wmi_format: - ret.append('{0}, {1}'.format(ip_address, subnet)) + ret.append("{0}, {1}".format(ip_address, subnet)) else: ret[ip_address] = subnet if not ret: - _LOG.debug('%s is empty.', setting) + _LOG.debug("%s is empty.", setting) return ret -def set_connection_ip_list(addresses=None, grant_by_default=False, server=_DEFAULT_SERVER): - ''' +def set_connection_ip_list( + addresses=None, grant_by_default=False, server=_DEFAULT_SERVER +): + """ Set the IPGrant list for the SMTP virtual server. :param str addresses: A dictionary of IP + subnet pairs. @@ -416,50 +427,55 @@ def set_connection_ip_list(addresses=None, grant_by_default=False, server=_DEFAU .. code-block:: bash salt '*' win_smtp_server.set_connection_ip_list addresses="{'127.0.0.1': '255.255.255.255'}" - ''' - setting = 'IPGrant' + """ + setting = "IPGrant" formatted_addresses = list() # It's okay to accept an empty list for set_connection_ip_list, # since an empty list may be desirable. if not addresses: addresses = dict() - _LOG.debug('Empty %s specified.', setting) + _LOG.debug("Empty %s specified.", setting) # Convert addresses to the 'ip_address, subnet' format used by # IIsIPSecuritySetting. for address in addresses: - formatted_addresses.append('{0}, {1}'.format(address.strip(), - addresses[address].strip())) + formatted_addresses.append( + "{0}, {1}".format(address.strip(), addresses[address].strip()) + ) current_addresses = get_connection_ip_list(as_wmi_format=True, server=server) # Order is not important, so compare to the current addresses as unordered sets. if set(formatted_addresses) == set(current_addresses): - _LOG.debug('%s already contains the provided addresses.', setting) + _LOG.debug("%s already contains the provided addresses.", setting) return True # First we should check GrantByDefault, and change it if necessary. - current_grant_by_default = _get_wmi_setting('IIsIPSecuritySetting', 'GrantByDefault', server) + current_grant_by_default = _get_wmi_setting( + "IIsIPSecuritySetting", "GrantByDefault", server + ) if grant_by_default != current_grant_by_default: - _LOG.debug('Setting GrantByDefault to: %s', grant_by_default) - _set_wmi_setting('IIsIPSecuritySetting', 'GrantByDefault', grant_by_default, server) + _LOG.debug("Setting GrantByDefault to: %s", grant_by_default) + _set_wmi_setting( + "IIsIPSecuritySetting", "GrantByDefault", grant_by_default, server + ) - _set_wmi_setting('IIsIPSecuritySetting', setting, formatted_addresses, server) + _set_wmi_setting("IIsIPSecuritySetting", setting, formatted_addresses, server) new_addresses = get_connection_ip_list(as_wmi_format=True, server=server) ret = set(formatted_addresses) == set(new_addresses) if ret: - _LOG.debug('%s configured successfully: %s', setting, formatted_addresses) + _LOG.debug("%s configured successfully: %s", setting, formatted_addresses) return ret - _LOG.error('Unable to configure %s with value: %s', setting, formatted_addresses) + _LOG.error("Unable to configure %s with value: %s", setting, formatted_addresses) return ret def get_relay_ip_list(server=_DEFAULT_SERVER): - ''' + """ Get the RelayIpList list for the SMTP virtual server. :param str server: The SMTP server name. @@ -478,14 +494,14 @@ def get_relay_ip_list(server=_DEFAULT_SERVER): .. code-block:: bash salt '*' win_smtp_server.get_relay_ip_list - ''' + """ ret = list() - setting = 'RelayIpList' + setting = "RelayIpList" - lines = _get_wmi_setting('IIsSmtpServerSetting', setting, server) + lines = _get_wmi_setting("IIsSmtpServerSetting", setting, server) if not lines: - _LOG.debug('%s is empty: %s', setting, lines) + _LOG.debug("%s is empty: %s", setting, lines) if lines is None: lines = [None] return list(lines) @@ -494,15 +510,15 @@ def get_relay_ip_list(server=_DEFAULT_SERVER): # need to group them and reassemble them into IP addresses. i = 0 while i < len(lines): - octets = [six.text_type(x) for x in lines[i: i + 4]] - address = '.'.join(octets) + octets = [six.text_type(x) for x in lines[i : i + 4]] + address = ".".join(octets) ret.append(address) i += 4 return ret def set_relay_ip_list(addresses=None, server=_DEFAULT_SERVER): - ''' + """ Set the RelayIpList list for the SMTP virtual server. Due to the unusual way that Windows stores the relay IPs, it is advisable to retrieve @@ -535,14 +551,14 @@ def set_relay_ip_list(addresses=None, server=_DEFAULT_SERVER): .. code-block:: bash salt '*' win_smtp_server.set_relay_ip_list addresses="['192.168.1.1', '172.16.1.1']" - ''' - setting = 'RelayIpList' + """ + setting = "RelayIpList" formatted_addresses = list() current_addresses = get_relay_ip_list(server) if list(addresses) == current_addresses: - _LOG.debug('%s already contains the provided addresses.', setting) + _LOG.debug("%s already contains the provided addresses.", setting) return True if addresses: @@ -553,19 +569,19 @@ def set_relay_ip_list(addresses=None, server=_DEFAULT_SERVER): formatted_addresses = None else: for address in addresses: - for octet in address.split('.'): + for octet in address.split("."): formatted_addresses.append(octet) - _LOG.debug('Formatted %s addresses: %s', setting, formatted_addresses) + _LOG.debug("Formatted %s addresses: %s", setting, formatted_addresses) - _set_wmi_setting('IIsSmtpServerSetting', setting, formatted_addresses, server) + _set_wmi_setting("IIsSmtpServerSetting", setting, formatted_addresses, server) new_addresses = get_relay_ip_list(server) ret = list(addresses) == new_addresses if ret: - _LOG.debug('%s configured successfully: %s', setting, addresses) + _LOG.debug("%s configured successfully: %s", setting, addresses) return ret - _LOG.error('Unable to configure %s with value: %s', setting, addresses) + _LOG.error("Unable to configure %s with value: %s", setting, addresses) return ret diff --git a/salt/modules/win_snmp.py b/salt/modules/win_snmp.py index 86a01a66752..5b5b3de1cf2 100644 --- a/salt/modules/win_snmp.py +++ b/salt/modules/win_snmp.py @@ -1,73 +1,78 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing SNMP service settings on Windows servers. The Windows feature 'SNMP-Service' must be installed. -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.platform -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import 3rd party libs from salt.ext import six -_HKEY = 'HKLM' +_HKEY = "HKLM" -_SNMP_KEY = r'SYSTEM\CurrentControlSet\Services\SNMP\Parameters' -_AGENT_KEY = r'{0}\RFC1156Agent'.format(_SNMP_KEY) -_COMMUNITIES_KEY = r'{0}\ValidCommunities'.format(_SNMP_KEY) +_SNMP_KEY = r"SYSTEM\CurrentControlSet\Services\SNMP\Parameters" +_AGENT_KEY = r"{0}\RFC1156Agent".format(_SNMP_KEY) +_COMMUNITIES_KEY = r"{0}\ValidCommunities".format(_SNMP_KEY) -_SNMP_GPO_KEY = r'SOFTWARE\Policies\SNMP\Parameters' -_COMMUNITIES_GPO_KEY = r'{0}\ValidCommunities'.format(_SNMP_GPO_KEY) +_SNMP_GPO_KEY = r"SOFTWARE\Policies\SNMP\Parameters" +_COMMUNITIES_GPO_KEY = r"{0}\ValidCommunities".format(_SNMP_GPO_KEY) -_PERMISSION_TYPES = {'None': 1, - 'Notify': 2, - 'Read Only': 4, - 'Read Write': 8, - 'Read Create': 16} -_SERVICE_TYPES = {'None': 0, - 'Physical': 1, - 'Datalink and subnetwork': 2, - 'Internet': 4, - 'End-to-end': 8, - 'Applications': 64} +_PERMISSION_TYPES = { + "None": 1, + "Notify": 2, + "Read Only": 4, + "Read Write": 8, + "Read Create": 16, +} +_SERVICE_TYPES = { + "None": 0, + "Physical": 1, + "Datalink and subnetwork": 2, + "Internet": 4, + "End-to-end": 8, + "Applications": 64, +} _LOG = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'win_snmp' +__virtualname__ = "win_snmp" def __virtual__(): - ''' + """ Only works on Windows systems. - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Module win_snmp: Requires Windows' + return False, "Module win_snmp: Requires Windows" - if not __salt__['reg.key_exists'](_HKEY, _SNMP_KEY): - return False, 'Module win_snmp: SNMP not installed' + if not __salt__["reg.key_exists"](_HKEY, _SNMP_KEY): + return False, "Module win_snmp: SNMP not installed" return __virtualname__ def _to_unicode(instr): - ''' + """ Converts from current users character encoding to unicode. When instr has a value of None, the return value of the function will also be None. - ''' + """ if instr is None or isinstance(instr, six.text_type): return instr else: - return six.text_type(instr, 'utf8') + return six.text_type(instr, "utf8") def get_agent_service_types(): - ''' + """ Get the sysServices types that can be configured. Returns: @@ -78,12 +83,12 @@ def get_agent_service_types(): .. code-block:: bash salt '*' win_snmp.get_agent_service_types - ''' + """ return list(_SERVICE_TYPES) def get_permission_types(): - ''' + """ Get the permission types that can be configured for communities. Returns: @@ -94,12 +99,12 @@ def get_permission_types(): .. code-block:: bash salt '*' win_snmp.get_permission_types - ''' + """ return list(_PERMISSION_TYPES) def get_agent_settings(): - ''' + """ Determine the value of the SNMP sysContact, sysLocation, and sysServices settings. @@ -111,22 +116,25 @@ def get_agent_settings(): .. code-block:: bash salt '*' win_snmp.get_agent_settings - ''' + """ ret = dict() sorted_types = sorted(_SERVICE_TYPES.items(), key=lambda x: (-x[1], x[0])) - ret['services'] = list() - ret['contact'] = (__salt__['reg.read_value']( - _HKEY, _AGENT_KEY, 'sysContact'))['vdata'] + ret["services"] = list() + ret["contact"] = (__salt__["reg.read_value"](_HKEY, _AGENT_KEY, "sysContact"))[ + "vdata" + ] - ret['location'] = (__salt__['reg.read_value']( - _HKEY, _AGENT_KEY, 'sysLocation'))['vdata'] + ret["location"] = (__salt__["reg.read_value"](_HKEY, _AGENT_KEY, "sysLocation"))[ + "vdata" + ] - current_bitmask = (__salt__['reg.read_value']( - _HKEY, _AGENT_KEY, 'sysServices'))['vdata'] + current_bitmask = (__salt__["reg.read_value"](_HKEY, _AGENT_KEY, "sysServices"))[ + "vdata" + ] if current_bitmask == 0: - ret['services'].append(sorted_types[-1][0]) + ret["services"].append(sorted_types[-1][0]) else: # sorted_types is sorted from greatest to least bitmask. for service, bitmask in sorted_types: @@ -135,16 +143,16 @@ def get_agent_settings(): if remaining_bitmask >= 0: current_bitmask = remaining_bitmask - ret['services'].append(service) + ret["services"].append(service) else: break - ret['services'] = sorted(ret['services']) + ret["services"] = sorted(ret["services"]) return ret def set_agent_settings(contact=None, location=None, services=None): - ''' + """ Manage the SNMP sysContact, sysLocation, and sysServices settings. Args: @@ -164,7 +172,7 @@ def set_agent_settings(contact=None, location=None, services=None): .. code-block:: bash salt '*' win_snmp.set_agent_settings contact='Contact Name' location='Place' services="['Physical']" - ''' + """ if services is not None: # Filter services for unique items, and sort them for comparison # purposes. @@ -173,42 +181,46 @@ def set_agent_settings(contact=None, location=None, services=None): # Validate the services. for service in services: if service not in _SERVICE_TYPES: - message = ("Invalid service '{0}' specified. Valid services:" - ' {1}').format(service, get_agent_service_types()) + message = ( + "Invalid service '{0}' specified. Valid services:" " {1}" + ).format(service, get_agent_service_types()) raise SaltInvocationError(message) if six.PY2: contact = _to_unicode(contact) location = _to_unicode(location) - settings = {'contact': contact, 'location': location, 'services': services} + settings = {"contact": contact, "location": location, "services": services} current_settings = get_agent_settings() if settings == current_settings: - _LOG.debug('Agent settings already contain the provided values.') + _LOG.debug("Agent settings already contain the provided values.") return True if contact is not None: - if contact != current_settings['contact']: - __salt__['reg.set_value']( - _HKEY, _AGENT_KEY, 'sysContact', contact, 'REG_SZ') + if contact != current_settings["contact"]: + __salt__["reg.set_value"]( + _HKEY, _AGENT_KEY, "sysContact", contact, "REG_SZ" + ) if location is not None: - if location != current_settings['location']: - __salt__['reg.set_value']( - _HKEY, _AGENT_KEY, 'sysLocation', location, 'REG_SZ') + if location != current_settings["location"]: + __salt__["reg.set_value"]( + _HKEY, _AGENT_KEY, "sysLocation", location, "REG_SZ" + ) if services is not None: - if set(services) != set(current_settings['services']): + if set(services) != set(current_settings["services"]): # Calculate the total value. Produces 0 if an empty list was provided, # corresponding to the None _SERVICE_TYPES value. vdata = sum(_SERVICE_TYPES[service] for service in services) - _LOG.debug('Setting sysServices vdata to: %s', vdata) + _LOG.debug("Setting sysServices vdata to: %s", vdata) - __salt__['reg.set_value']( - _HKEY, _AGENT_KEY, 'sysServices', vdata, 'REG_DWORD') + __salt__["reg.set_value"]( + _HKEY, _AGENT_KEY, "sysServices", vdata, "REG_DWORD" + ) # Get the fields post-change so that we can verify tht all values # were modified successfully. Track the ones that weren't. @@ -216,20 +228,19 @@ def set_agent_settings(contact=None, location=None, services=None): failed_settings = dict() for setting in settings: - if settings[setting] is not None and \ - settings[setting] != new_settings[setting]: + if settings[setting] is not None and settings[setting] != new_settings[setting]: failed_settings[setting] = settings[setting] if failed_settings: - _LOG.error('Unable to configure agent settings: %s', failed_settings) + _LOG.error("Unable to configure agent settings: %s", failed_settings) return False - _LOG.debug('Agent settings configured successfully: %s', settings.keys()) + _LOG.debug("Agent settings configured successfully: %s", settings.keys()) return True def get_auth_traps_enabled(): - ''' + """ Determine whether the host is configured to send authentication traps. Returns: @@ -240,17 +251,16 @@ def get_auth_traps_enabled(): .. code-block:: bash salt '*' win_snmp.get_auth_traps_enabled - ''' - reg_ret = __salt__['reg.read_value']( - _HKEY, _SNMP_KEY, 'EnableAuthenticationTraps') + """ + reg_ret = __salt__["reg.read_value"](_HKEY, _SNMP_KEY, "EnableAuthenticationTraps") - if reg_ret['vdata'] == '(value not set)': + if reg_ret["vdata"] == "(value not set)": return False - return bool(reg_ret['vdata'] or 0) + return bool(reg_ret["vdata"] or 0) def set_auth_traps_enabled(status=True): - ''' + """ Manage the sending of authentication traps. Args: @@ -264,28 +274,28 @@ def set_auth_traps_enabled(status=True): .. code-block:: bash salt '*' win_snmp.set_auth_traps_enabled status='True' - ''' - vname = 'EnableAuthenticationTraps' + """ + vname = "EnableAuthenticationTraps" current_status = get_auth_traps_enabled() if bool(status) == current_status: - _LOG.debug('%s already contains the provided value.', vname) + _LOG.debug("%s already contains the provided value.", vname) return True vdata = int(status) - __salt__['reg.set_value'](_HKEY, _SNMP_KEY, vname, vdata, 'REG_DWORD') + __salt__["reg.set_value"](_HKEY, _SNMP_KEY, vname, vdata, "REG_DWORD") new_status = get_auth_traps_enabled() if status == new_status: - _LOG.debug('Setting %s configured successfully: %s', vname, vdata) + _LOG.debug("Setting %s configured successfully: %s", vname, vdata) return True - _LOG.error('Unable to configure %s with value: %s', vname, vdata) + _LOG.error("Unable to configure %s with value: %s", vname, vdata) return False def get_community_names(): - ''' + """ Get the current accepted SNMP community names and their permissions. If community names are being managed by Group Policy, those values will be @@ -311,16 +321,15 @@ def get_community_names(): .. code-block:: bash salt '*' win_snmp.get_community_names - ''' + """ ret = dict() # Look in GPO settings first - if __salt__['reg.key_exists'](_HKEY, _COMMUNITIES_GPO_KEY): + if __salt__["reg.key_exists"](_HKEY, _COMMUNITIES_GPO_KEY): - _LOG.debug('Loading communities from Group Policy settings') + _LOG.debug("Loading communities from Group Policy settings") - current_values = __salt__['reg.list_values']( - _HKEY, _COMMUNITIES_GPO_KEY) + current_values = __salt__["reg.list_values"](_HKEY, _COMMUNITIES_GPO_KEY) # GPO settings are different in that they do not designate permissions # They are a numbered list of communities like so: @@ -341,14 +350,13 @@ def get_community_names(): if not isinstance(current_value, dict): continue - ret[current_value['vdata']] = 'Managed by GPO' + ret[current_value["vdata"]] = "Managed by GPO" if not ret: - _LOG.debug('Loading communities from SNMP settings') + _LOG.debug("Loading communities from SNMP settings") - current_values = __salt__['reg.list_values']( - _HKEY, _COMMUNITIES_KEY) + current_values = __salt__["reg.list_values"](_HKEY, _COMMUNITIES_KEY) # The communities are stored as the community name with a numeric # permission value. Like this (4 = Read Only): @@ -372,18 +380,18 @@ def get_community_names(): permissions = six.text_type() for permission_name in _PERMISSION_TYPES: - if current_value['vdata'] == _PERMISSION_TYPES[permission_name]: + if current_value["vdata"] == _PERMISSION_TYPES[permission_name]: permissions = permission_name break - ret[current_value['vname']] = permissions + ret[current_value["vname"]] = permissions if not ret: - _LOG.debug('Unable to find existing communities.') + _LOG.debug("Unable to find existing communities.") return ret def set_community_names(communities): - ''' + """ Manage the SNMP accepted community names and their permissions. .. note:: @@ -408,29 +416,30 @@ def set_community_names(communities): .. code-block:: bash salt '*' win_snmp.set_community_names communities="{'TestCommunity': 'Read Only'}' - ''' + """ values = dict() - if __salt__['reg.key_exists'](_HKEY, _COMMUNITIES_GPO_KEY): - _LOG.debug('Communities on this system are managed by Group Policy') + if __salt__["reg.key_exists"](_HKEY, _COMMUNITIES_GPO_KEY): + _LOG.debug("Communities on this system are managed by Group Policy") raise CommandExecutionError( - 'Communities on this system are managed by Group Policy') + "Communities on this system are managed by Group Policy" + ) current_communities = get_community_names() if communities == current_communities: - _LOG.debug('Communities already contain the provided values.') + _LOG.debug("Communities already contain the provided values.") return True for vname in communities: if not communities[vname]: - communities[vname] = 'None' + communities[vname] = "None" try: vdata = _PERMISSION_TYPES[communities[vname]] except KeyError: message = ( - "Invalid permission '{0}' specified. Valid permissions: " - "{1}").format(communities[vname], _PERMISSION_TYPES.keys()) + "Invalid permission '{0}' specified. Valid permissions: " "{1}" + ).format(communities[vname], _PERMISSION_TYPES.keys()) raise SaltInvocationError(message) values[vname] = vdata @@ -439,19 +448,23 @@ def set_community_names(communities): if current_vname in values: # Modify existing communities that have a different permission value. if current_communities[current_vname] != values[current_vname]: - __salt__['reg.set_value']( - _HKEY, _COMMUNITIES_KEY, current_vname, - values[current_vname], 'REG_DWORD') + __salt__["reg.set_value"]( + _HKEY, + _COMMUNITIES_KEY, + current_vname, + values[current_vname], + "REG_DWORD", + ) else: # Remove current communities that weren't provided. - __salt__['reg.delete_value']( - _HKEY, _COMMUNITIES_KEY, current_vname) + __salt__["reg.delete_value"](_HKEY, _COMMUNITIES_KEY, current_vname) # Create any new communities. for vname in values: if vname not in current_communities: - __salt__['reg.set_value']( - _HKEY, _COMMUNITIES_KEY, vname, values[vname], 'REG_DWORD') + __salt__["reg.set_value"]( + _HKEY, _COMMUNITIES_KEY, vname, values[vname], "REG_DWORD" + ) # Get the fields post-change so that we can verify tht all values # were modified successfully. Track the ones that weren't. @@ -467,7 +480,7 @@ def set_community_names(communities): failed_communities[vname] = communities[vname] if failed_communities: - _LOG.error('Unable to configure communities: %s', failed_communities) + _LOG.error("Unable to configure communities: %s", failed_communities) return False - _LOG.debug('Communities configured successfully: %s', communities.keys()) + _LOG.debug("Communities configured successfully: %s", communities.keys()) return True diff --git a/salt/modules/win_status.py b/salt/modules/win_status.py index a13b839b385..fef887849f5 100644 --- a/salt/modules/win_status.py +++ b/salt/modules/win_status.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for returning various status data about a minion. These data can be useful for compiling into stats later, or for problem solving if your minion is having problems. @@ -7,23 +7,21 @@ or for problem solving if your minion is having problems. .. versionadded:: 0.12.0 :depends: - wmi -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import ctypes import datetime import logging import subprocess -log = logging.getLogger(__name__) # Import Salt Libs import salt.utils.event import salt.utils.platform import salt.utils.stringutils import salt.utils.win_pdh -from salt.utils.network import host_to_ips as _host_to_ips -from salt.utils.functools import namespaced_function as _namespaced_function # Import 3rd party Libs from salt.ext import six @@ -31,7 +29,12 @@ from salt.ext import six # These imports needed for namespaced functions # pylint: disable=W0611 from salt.modules.status import ping_master, time_ -import copy +from salt.utils.functools import namespaced_function as _namespaced_function +from salt.utils.network import host_to_ips as _host_to_ips + +log = logging.getLogger(__name__) + + # pylint: enable=W0611 # Import 3rd Party Libs @@ -39,6 +42,7 @@ try: if salt.utils.platform.is_windows(): import wmi import salt.utils.winapi + HAS_WMI = True else: HAS_WMI = False @@ -48,108 +52,111 @@ except ImportError: HAS_PSUTIL = False if salt.utils.platform.is_windows(): import psutil + HAS_PSUTIL = True __opts__ = {} -__virtualname__ = 'status' +__virtualname__ = "status" # Taken from https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/performance.htm class SYSTEM_PERFORMANCE_INFORMATION(ctypes.Structure): - _fields_ = [('IdleProcessTime', ctypes.c_int64), - ('IoReadTransferCount', ctypes.c_int64), - ('IoWriteTransferCount', ctypes.c_int64), - ('IoOtherTransferCount', ctypes.c_int64), - ('IoReadOperationCount', ctypes.c_ulong), - ('IoWriteOperationCount', ctypes.c_ulong), - ('IoOtherOperationCount', ctypes.c_ulong), - ('AvailablePages', ctypes.c_ulong), - ('CommittedPages', ctypes.c_ulong), - ('CommitLimit', ctypes.c_ulong), - ('PeakCommitment', ctypes.c_ulong), - ('PageFaultCount', ctypes.c_ulong), - ('CopyOnWriteCount', ctypes.c_ulong), - ('TransitionCount', ctypes.c_ulong), - ('CacheTransitionCount', ctypes.c_ulong), - ('DemandZeroCount', ctypes.c_ulong), - ('PageReadCount', ctypes.c_ulong), - ('PageReadIoCount', ctypes.c_ulong), - ('CacheReadCount', ctypes.c_ulong), # Was c_ulong ** 2 - ('CacheIoCount', ctypes.c_ulong), - ('DirtyPagesWriteCount', ctypes.c_ulong), - ('DirtyWriteIoCount', ctypes.c_ulong), - ('MappedPagesWriteCount', ctypes.c_ulong), - ('MappedWriteIoCount', ctypes.c_ulong), - ('PagedPoolPages', ctypes.c_ulong), - ('NonPagedPoolPages', ctypes.c_ulong), - ('PagedPoolAllocs', ctypes.c_ulong), - ('PagedPoolFrees', ctypes.c_ulong), - ('NonPagedPoolAllocs', ctypes.c_ulong), - ('NonPagedPoolFrees', ctypes.c_ulong), - ('FreeSystemPtes', ctypes.c_ulong), - ('ResidentSystemCodePage', ctypes.c_ulong), - ('TotalSystemDriverPages', ctypes.c_ulong), - ('TotalSystemCodePages', ctypes.c_ulong), - ('NonPagedPoolLookasideHits', ctypes.c_ulong), - ('PagedPoolLookasideHits', ctypes.c_ulong), - ('AvailablePagedPoolPages', ctypes.c_ulong), - ('ResidentSystemCachePage', ctypes.c_ulong), - ('ResidentPagedPoolPage', ctypes.c_ulong), - ('ResidentSystemDriverPage', ctypes.c_ulong), - ('CcFastReadNoWait', ctypes.c_ulong), - ('CcFastReadWait', ctypes.c_ulong), - ('CcFastReadResourceMiss', ctypes.c_ulong), - ('CcFastReadNotPossible', ctypes.c_ulong), - ('CcFastMdlReadNoWait', ctypes.c_ulong), - ('CcFastMdlReadWait', ctypes.c_ulong), - ('CcFastMdlReadResourceMiss', ctypes.c_ulong), - ('CcFastMdlReadNotPossible', ctypes.c_ulong), - ('CcMapDataNoWait', ctypes.c_ulong), - ('CcMapDataWait', ctypes.c_ulong), - ('CcMapDataNoWaitMiss', ctypes.c_ulong), - ('CcMapDataWaitMiss', ctypes.c_ulong), - ('CcPinMappedDataCount', ctypes.c_ulong), - ('CcPinReadNoWait', ctypes.c_ulong), - ('CcPinReadWait', ctypes.c_ulong), - ('CcPinReadNoWaitMiss', ctypes.c_ulong), - ('CcPinReadWaitMiss', ctypes.c_ulong), - ('CcCopyReadNoWait', ctypes.c_ulong), - ('CcCopyReadWait', ctypes.c_ulong), - ('CcCopyReadNoWaitMiss', ctypes.c_ulong), - ('CcCopyReadWaitMiss', ctypes.c_ulong), - ('CcMdlReadNoWait', ctypes.c_ulong), - ('CcMdlReadWait', ctypes.c_ulong), - ('CcMdlReadNoWaitMiss', ctypes.c_ulong), - ('CcMdlReadWaitMiss', ctypes.c_ulong), - ('CcReadAheadIos', ctypes.c_ulong), - ('CcLazyWriteIos', ctypes.c_ulong), - ('CcLazyWritePages', ctypes.c_ulong), - ('CcDataFlushes', ctypes.c_ulong), - ('CcDataPages', ctypes.c_ulong), - ('ContextSwitches', ctypes.c_ulong), - ('FirstLevelTbFills', ctypes.c_ulong), - ('SecondLevelTbFills', ctypes.c_ulong), - ('SystemCalls', ctypes.c_ulong), - # Windows 8 and above - ('CcTotalDirtyPages', ctypes.c_ulonglong), - ('CcDirtyPagesThreshold', ctypes.c_ulonglong), - ('ResidentAvailablePages', ctypes.c_longlong), - # Windows 10 and above - ('SharedCommittedPages', ctypes.c_ulonglong)] + _fields_ = [ + ("IdleProcessTime", ctypes.c_int64), + ("IoReadTransferCount", ctypes.c_int64), + ("IoWriteTransferCount", ctypes.c_int64), + ("IoOtherTransferCount", ctypes.c_int64), + ("IoReadOperationCount", ctypes.c_ulong), + ("IoWriteOperationCount", ctypes.c_ulong), + ("IoOtherOperationCount", ctypes.c_ulong), + ("AvailablePages", ctypes.c_ulong), + ("CommittedPages", ctypes.c_ulong), + ("CommitLimit", ctypes.c_ulong), + ("PeakCommitment", ctypes.c_ulong), + ("PageFaultCount", ctypes.c_ulong), + ("CopyOnWriteCount", ctypes.c_ulong), + ("TransitionCount", ctypes.c_ulong), + ("CacheTransitionCount", ctypes.c_ulong), + ("DemandZeroCount", ctypes.c_ulong), + ("PageReadCount", ctypes.c_ulong), + ("PageReadIoCount", ctypes.c_ulong), + ("CacheReadCount", ctypes.c_ulong), # Was c_ulong ** 2 + ("CacheIoCount", ctypes.c_ulong), + ("DirtyPagesWriteCount", ctypes.c_ulong), + ("DirtyWriteIoCount", ctypes.c_ulong), + ("MappedPagesWriteCount", ctypes.c_ulong), + ("MappedWriteIoCount", ctypes.c_ulong), + ("PagedPoolPages", ctypes.c_ulong), + ("NonPagedPoolPages", ctypes.c_ulong), + ("PagedPoolAllocs", ctypes.c_ulong), + ("PagedPoolFrees", ctypes.c_ulong), + ("NonPagedPoolAllocs", ctypes.c_ulong), + ("NonPagedPoolFrees", ctypes.c_ulong), + ("FreeSystemPtes", ctypes.c_ulong), + ("ResidentSystemCodePage", ctypes.c_ulong), + ("TotalSystemDriverPages", ctypes.c_ulong), + ("TotalSystemCodePages", ctypes.c_ulong), + ("NonPagedPoolLookasideHits", ctypes.c_ulong), + ("PagedPoolLookasideHits", ctypes.c_ulong), + ("AvailablePagedPoolPages", ctypes.c_ulong), + ("ResidentSystemCachePage", ctypes.c_ulong), + ("ResidentPagedPoolPage", ctypes.c_ulong), + ("ResidentSystemDriverPage", ctypes.c_ulong), + ("CcFastReadNoWait", ctypes.c_ulong), + ("CcFastReadWait", ctypes.c_ulong), + ("CcFastReadResourceMiss", ctypes.c_ulong), + ("CcFastReadNotPossible", ctypes.c_ulong), + ("CcFastMdlReadNoWait", ctypes.c_ulong), + ("CcFastMdlReadWait", ctypes.c_ulong), + ("CcFastMdlReadResourceMiss", ctypes.c_ulong), + ("CcFastMdlReadNotPossible", ctypes.c_ulong), + ("CcMapDataNoWait", ctypes.c_ulong), + ("CcMapDataWait", ctypes.c_ulong), + ("CcMapDataNoWaitMiss", ctypes.c_ulong), + ("CcMapDataWaitMiss", ctypes.c_ulong), + ("CcPinMappedDataCount", ctypes.c_ulong), + ("CcPinReadNoWait", ctypes.c_ulong), + ("CcPinReadWait", ctypes.c_ulong), + ("CcPinReadNoWaitMiss", ctypes.c_ulong), + ("CcPinReadWaitMiss", ctypes.c_ulong), + ("CcCopyReadNoWait", ctypes.c_ulong), + ("CcCopyReadWait", ctypes.c_ulong), + ("CcCopyReadNoWaitMiss", ctypes.c_ulong), + ("CcCopyReadWaitMiss", ctypes.c_ulong), + ("CcMdlReadNoWait", ctypes.c_ulong), + ("CcMdlReadWait", ctypes.c_ulong), + ("CcMdlReadNoWaitMiss", ctypes.c_ulong), + ("CcMdlReadWaitMiss", ctypes.c_ulong), + ("CcReadAheadIos", ctypes.c_ulong), + ("CcLazyWriteIos", ctypes.c_ulong), + ("CcLazyWritePages", ctypes.c_ulong), + ("CcDataFlushes", ctypes.c_ulong), + ("CcDataPages", ctypes.c_ulong), + ("ContextSwitches", ctypes.c_ulong), + ("FirstLevelTbFills", ctypes.c_ulong), + ("SecondLevelTbFills", ctypes.c_ulong), + ("SystemCalls", ctypes.c_ulong), + # Windows 8 and above + ("CcTotalDirtyPages", ctypes.c_ulonglong), + ("CcDirtyPagesThreshold", ctypes.c_ulonglong), + ("ResidentAvailablePages", ctypes.c_longlong), + # Windows 10 and above + ("SharedCommittedPages", ctypes.c_ulonglong), + ] def __virtual__(): - ''' + """ Only works on Windows systems with WMI and WinAPI - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'win_status.py: Requires Windows' + return False, "win_status.py: Requires Windows" if not HAS_WMI: - return False, 'win_status.py: Requires WMI and WinAPI' + return False, "win_status.py: Requires WMI and WinAPI" if not HAS_PSUTIL: - return False, 'win_status.py: Requires psutil' + return False, "win_status.py: Requires psutil" # Namespace modules from `status.py` global ping_master, time_ @@ -159,13 +166,11 @@ def __virtual__(): return __virtualname__ -__func_alias__ = { - 'time_': 'time' -} +__func_alias__ = {"time_": "time"} def cpustats(): - ''' + """ Return information about the CPU. Returns @@ -176,34 +181,30 @@ def cpustats(): .. code-block:: bash salt * status.cpustats - ''' + """ # Tries to gather information similar to that returned by a Linux machine # Avoid using WMI as there's a lot of overhead # Time related info user, system, idle, interrupt, dpc = psutil.cpu_times() - cpu = {'user': user, - 'system': system, - 'idle': idle, - 'irq': interrupt, - 'dpc': dpc} + cpu = {"user": user, "system": system, "idle": idle, "irq": interrupt, "dpc": dpc} # Count related info ctx_switches, interrupts, soft_interrupts, sys_calls = psutil.cpu_stats() - intr = {'irqs': {'irqs': [], - 'total': interrupts}} - soft_irq = {'softirqs': [], - 'total': soft_interrupts} - return {'btime': psutil.boot_time(), - 'cpu': cpu, - 'ctxt': ctx_switches, - 'intr': intr, - 'processes': len(psutil.pids()), - 'softirq': soft_irq, - 'syscalls': sys_calls} + intr = {"irqs": {"irqs": [], "total": interrupts}} + soft_irq = {"softirqs": [], "total": soft_interrupts} + return { + "btime": psutil.boot_time(), + "cpu": cpu, + "ctxt": ctx_switches, + "intr": intr, + "processes": len(psutil.pids()), + "softirq": soft_irq, + "syscalls": sys_calls, + } def meminfo(): - ''' + """ Return information about physical and virtual memory on the system Returns: @@ -214,36 +215,36 @@ def meminfo(): .. code-block:: bash salt * status.meminfo - ''' + """ # Get physical memory vm_total, vm_available, vm_percent, vm_used, vm_free = psutil.virtual_memory() # Get swap memory swp_total, swp_used, swp_free, swp_percent, _, _ = psutil.swap_memory() def get_unit_value(memory): - symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') + symbols = ("K", "M", "G", "T", "P", "E", "Z", "Y") prefix = {} for i, s in enumerate(symbols): prefix[s] = 1 << (i + 1) * 10 for s in reversed(symbols): if memory >= prefix[s]: value = float(memory) / prefix[s] - return {'unit': s, - 'value': value} - return {'unit': 'B', - 'value': memory} + return {"unit": s, "value": value} + return {"unit": "B", "value": memory} - return {'VmallocTotal': get_unit_value(vm_total), - 'VmallocUsed': get_unit_value(vm_used), - 'VmallocFree': get_unit_value(vm_free), - 'VmallocAvail': get_unit_value(vm_available), - 'SwapTotal': get_unit_value(swp_total), - 'SwapUsed': get_unit_value(swp_used), - 'SwapFree': get_unit_value(swp_free)} + return { + "VmallocTotal": get_unit_value(vm_total), + "VmallocUsed": get_unit_value(vm_used), + "VmallocFree": get_unit_value(vm_free), + "VmallocAvail": get_unit_value(vm_available), + "SwapTotal": get_unit_value(swp_total), + "SwapUsed": get_unit_value(swp_used), + "SwapFree": get_unit_value(swp_free), + } def vmstats(): - ''' + """ Return information about the virtual memory on the machine Returns: @@ -254,7 +255,7 @@ def vmstats(): .. code-block:: bash salt * status.vmstats - ''' + """ # Setup the SPI Structure spi = SYSTEM_PERFORMANCE_INFORMATION() retlen = ctypes.c_ulong() @@ -262,7 +263,8 @@ def vmstats(): # 2 means to query System Performance Information and return it in a # SYSTEM_PERFORMANCE_INFORMATION Structure ctypes.windll.ntdll.NtQuerySystemInformation( - 2, ctypes.byref(spi), ctypes.sizeof(spi), ctypes.byref(retlen)) + 2, ctypes.byref(spi), ctypes.sizeof(spi), ctypes.byref(retlen) + ) # Return each defined field in a dict ret = {} @@ -273,7 +275,7 @@ def vmstats(): def loadavg(): - ''' + """ Returns counter information related to the load of the machine Returns: @@ -284,28 +286,28 @@ def loadavg(): .. code-block:: bash salt * status.loadavg - ''' + """ # Counter List (obj, instance, counter) counter_list = [ - ('Memory', None, 'Available Bytes'), - ('Memory', None, 'Pages/sec'), - ('Paging File', '*', '% Usage'), - ('Processor', '*', '% Processor Time'), - ('Processor', '*', 'DPCs Queued/sec'), - ('Processor', '*', '% Privileged Time'), - ('Processor', '*', '% User Time'), - ('Processor', '*', '% DPC Time'), - ('Processor', '*', '% Interrupt Time'), - ('Server', None, 'Work Item Shortages'), - ('Server Work Queues', '*', 'Queue Length'), - ('System', None, 'Processor Queue Length'), - ('System', None, 'Context Switches/sec'), + ("Memory", None, "Available Bytes"), + ("Memory", None, "Pages/sec"), + ("Paging File", "*", "% Usage"), + ("Processor", "*", "% Processor Time"), + ("Processor", "*", "DPCs Queued/sec"), + ("Processor", "*", "% Privileged Time"), + ("Processor", "*", "% User Time"), + ("Processor", "*", "% DPC Time"), + ("Processor", "*", "% Interrupt Time"), + ("Server", None, "Work Item Shortages"), + ("Server Work Queues", "*", "Queue Length"), + ("System", None, "Processor Queue Length"), + ("System", None, "Context Switches/sec"), ] return salt.utils.win_pdh.get_counters(counter_list=counter_list) def cpuload(): - ''' + """ .. versionadded:: 2015.8.0 Return the processor load as a percentage @@ -315,12 +317,12 @@ def cpuload(): .. code-block:: bash salt '*' status.cpuload - ''' + """ return psutil.cpu_percent() def diskusage(human_readable=False, path=None): - ''' + """ .. versionadded:: 2015.8.0 Return the disk usage for this minion @@ -333,9 +335,9 @@ def diskusage(human_readable=False, path=None): .. code-block:: bash salt '*' status.diskusage path=c:/salt - ''' + """ if not path: - path = 'c:/' + path = "c:/" disk_stats = psutil.disk_usage(path) @@ -349,14 +351,11 @@ def diskusage(human_readable=False, path=None): used_val = _byte_calc(used_val) free_val = _byte_calc(free_val) - return {'total': total_val, - 'used': used_val, - 'free': free_val, - 'percent': percent} + return {"total": total_val, "used": used_val, "free": free_val, "percent": percent} def procs(count=False): - ''' + """ Return the process data count : False @@ -370,16 +369,16 @@ def procs(count=False): salt '*' status.procs salt '*' status.procs count - ''' + """ with salt.utils.winapi.Com(): wmi_obj = wmi.WMI() processes = wmi_obj.win32_process() - #this short circuit's the function to get a short simple proc count. + # this short circuit's the function to get a short simple proc count. if count: return len(processes) - #a propper run of the function, creating a nonsensically long out put. + # a propper run of the function, creating a nonsensically long out put. process_info = {} for proc in processes: process_info[proc.ProcessId] = _get_process_info(proc) @@ -388,7 +387,7 @@ def procs(count=False): def saltmem(human_readable=False): - ''' + """ .. versionadded:: 2015.8.0 Returns the amount of memory that salt is using @@ -402,7 +401,7 @@ def saltmem(human_readable=False): salt '*' status.saltmem salt '*' status.saltmem human_readable=True - ''' + """ # psutil.Process defaults to current process (`os.getpid()`) p = psutil.Process() @@ -417,7 +416,7 @@ def saltmem(human_readable=False): def uptime(human_readable=False): - ''' + """ .. versionadded:: 2015.8.0 Return the system uptime for the machine @@ -443,7 +442,7 @@ def uptime(human_readable=False): salt '*' status.uptime salt '*' status.uptime human_readable=True - ''' + """ # Get startup time startup_time = datetime.datetime.fromtimestamp(psutil.boot_time()) @@ -454,16 +453,12 @@ def uptime(human_readable=False): def _get_process_info(proc): - ''' + """ Return process information - ''' - cmd = salt.utils.stringutils.to_unicode(proc.CommandLine or '') + """ + cmd = salt.utils.stringutils.to_unicode(proc.CommandLine or "") name = salt.utils.stringutils.to_unicode(proc.Name) - info = dict( - cmd=cmd, - name=name, - **_get_process_owner(proc) - ) + info = dict(cmd=cmd, name=name, **_get_process_owner(proc)) return info @@ -472,39 +467,42 @@ def _get_process_owner(process): domain, error_code, user = None, None, None try: domain, error_code, user = process.GetOwner() - owner['user'] = salt.utils.stringutils.to_unicode(user) - owner['user_domain'] = salt.utils.stringutils.to_unicode(domain) + owner["user"] = salt.utils.stringutils.to_unicode(user) + owner["user_domain"] = salt.utils.stringutils.to_unicode(domain) except Exception as exc: # pylint: disable=broad-except pass if not error_code and all((user, domain)): - owner['user'] = salt.utils.stringutils.to_unicode(user) - owner['user_domain'] = salt.utils.stringutils.to_unicode(domain) + owner["user"] = salt.utils.stringutils.to_unicode(user) + owner["user_domain"] = salt.utils.stringutils.to_unicode(domain) elif process.ProcessId in [0, 4] and error_code == 2: # Access Denied for System Idle Process and System - owner['user'] = 'SYSTEM' - owner['user_domain'] = 'NT AUTHORITY' + owner["user"] = "SYSTEM" + owner["user_domain"] = "NT AUTHORITY" else: - log.warning('Error getting owner of process; PID=\'%s\'; Error: %s', - process.ProcessId, error_code) + log.warning( + "Error getting owner of process; PID='%s'; Error: %s", + process.ProcessId, + error_code, + ) return owner def _byte_calc(val): if val < 1024: - tstr = six.text_type(val)+'B' + tstr = six.text_type(val) + "B" elif val < 1038336: - tstr = six.text_type(val/1024)+'KB' + tstr = six.text_type(val / 1024) + "KB" elif val < 1073741824: - tstr = six.text_type(val/1038336)+'MB' + tstr = six.text_type(val / 1038336) + "MB" elif val < 1099511627776: - tstr = six.text_type(val/1073741824)+'GB' + tstr = six.text_type(val / 1073741824) + "GB" else: - tstr = six.text_type(val/1099511627776)+'TB' + tstr = six.text_type(val / 1099511627776) + "TB" return tstr def master(master=None, connected=True): - ''' + """ .. versionadded:: 2015.5.0 Fire an event if the minion gets disconnected from its master. This @@ -517,10 +515,10 @@ def master(master=None, connected=True): .. code-block:: bash salt '*' status.master - ''' + """ def _win_remotes_on(port): - ''' + """ Windows specific helper function. Returns set of ipv4 host addresses of remote established connections on local or remote tcp port. @@ -537,20 +535,22 @@ def master(master=None, connected=True): TCP 10.1.1.26:56868 169.254.169.254:80 CLOSE_WAIT TCP 127.0.0.1:49197 127.0.0.1:49198 ESTABLISHED TCP 127.0.0.1:49198 127.0.0.1:49197 ESTABLISHED - ''' + """ remotes = set() try: - data = subprocess.check_output(['netstat', '-n', '-p', 'TCP']) # pylint: disable=minimum-python-version + data = subprocess.check_output( + ["netstat", "-n", "-p", "TCP"] + ) # pylint: disable=minimum-python-version except subprocess.CalledProcessError: - log.error('Failed netstat') + log.error("Failed netstat") raise - lines = salt.utils.stringutils.to_unicode(data).split('\n') + lines = salt.utils.stringutils.to_unicode(data).split("\n") for line in lines: - if 'ESTABLISHED' not in line: + if "ESTABLISHED" not in line: continue chunks = line.split() - remote_host, remote_port = chunks[2].rsplit(':', 1) + remote_host, remote_port = chunks[2].rsplit(":", 1) if int(remote_port) != port: continue remotes.add(remote_host) @@ -566,8 +566,8 @@ def master(master=None, connected=True): if not master_ips: return - if __salt__['config.get']('publish_port') != '': - port = int(__salt__['config.get']('publish_port')) + if __salt__["config.get"]("publish_port") != "": + port = int(__salt__["config.get"]("publish_port")) master_connection_status = False connected_ips = _win_remotes_on(port) @@ -580,10 +580,16 @@ def master(master=None, connected=True): # Connection to master is not as expected if master_connection_status is not connected: - with salt.utils.event.get_event('minion', opts=__opts__, listen=False) as event_bus: + with salt.utils.event.get_event( + "minion", opts=__opts__, listen=False + ) as event_bus: if master_connection_status: - event_bus.fire_event({'master': master}, salt.minion.master_event(type='connected')) + event_bus.fire_event( + {"master": master}, salt.minion.master_event(type="connected") + ) else: - event_bus.fire_event({'master': master}, salt.minion.master_event(type='disconnected')) + event_bus.fire_event( + {"master": master}, salt.minion.master_event(type="disconnected") + ) return master_connection_status diff --git a/salt/modules/win_system.py b/salt/modules/win_system.py index 74fa61bc513..a3c0218ec97 100644 --- a/salt/modules/win_system.py +++ b/salt/modules/win_system.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing windows systems. :depends: @@ -10,14 +10,14 @@ Module for managing windows systems. - wmi Support for reboot, shutdown, etc -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import ctypes import logging -import time import platform +import time from datetime import datetime # Import salt libs @@ -29,6 +29,7 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party Libs from salt.ext import six + try: import wmi import win32net @@ -36,6 +37,7 @@ try: import win32con import pywintypes from ctypes import windll + HAS_WIN32NET_MODS = True except ImportError: HAS_WIN32NET_MODS = False @@ -44,52 +46,52 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'system' +__virtualname__ = "system" def __virtual__(): - ''' + """ Only works on Windows Systems with Win32 Modules - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Module win_system: Requires Windows' + return False, "Module win_system: Requires Windows" if not HAS_WIN32NET_MODS: - return False, 'Module win_system: Missing win32 modules' + return False, "Module win_system: Missing win32 modules" return __virtualname__ def _convert_minutes_seconds(timeout, in_seconds=False): - ''' + """ convert timeout to seconds - ''' - return timeout if in_seconds else timeout*60 + """ + return timeout if in_seconds else timeout * 60 def _convert_date_time_string(dt_string): - ''' + """ convert string to date time object - ''' - dt_string = dt_string.split('.')[0] - dt_obj = datetime.strptime(dt_string, '%Y%m%d%H%M%S') - return dt_obj.strftime('%Y-%m-%d %H:%M:%S') + """ + dt_string = dt_string.split(".")[0] + dt_obj = datetime.strptime(dt_string, "%Y%m%d%H%M%S") + return dt_obj.strftime("%Y-%m-%d %H:%M:%S") def _to_unicode(instr): - ''' + """ Converts from current users character encoding to unicode. When instr has a value of None, the return value of the function will also be None. - ''' + """ if instr is None or isinstance(instr, six.text_type): return instr else: - return six.text_type(instr, 'utf8') + return six.text_type(instr, "utf8") def halt(timeout=5, in_seconds=False): - ''' + """ Halt a running system. Args: @@ -110,12 +112,12 @@ def halt(timeout=5, in_seconds=False): .. code-block:: bash salt '*' system.halt 5 True - ''' + """ return shutdown(timeout=timeout, in_seconds=in_seconds) def init(runlevel): # pylint: disable=unused-argument - ''' + """ Change the system runlevel on sysV compatible systems. Not applicable to Windows @@ -124,7 +126,7 @@ def init(runlevel): # pylint: disable=unused-argument .. code-block:: bash salt '*' system.init 3 - ''' + """ # cmd = ['init', runlevel] # ret = __salt__['cmd.run'](cmd, python_shell=False) # return ret @@ -132,11 +134,11 @@ def init(runlevel): # pylint: disable=unused-argument # TODO: Create a mapping of runlevels to # pylint: disable=fixme # corresponding Windows actions - return 'Not implemented on Windows at this time.' + return "Not implemented on Windows at this time." def poweroff(timeout=5, in_seconds=False): - ''' + """ Power off a running system. Args: @@ -158,13 +160,17 @@ def poweroff(timeout=5, in_seconds=False): .. code-block:: bash salt '*' system.poweroff 5 - ''' + """ return shutdown(timeout=timeout, in_seconds=in_seconds) -def reboot(timeout=5, in_seconds=False, wait_for_reboot=False, # pylint: disable=redefined-outer-name - only_on_pending_reboot=False): - ''' +def reboot( + timeout=5, + in_seconds=False, + wait_for_reboot=False, # pylint: disable=redefined-outer-name + only_on_pending_reboot=False, +): + """ Reboot a running system. Args: @@ -214,9 +220,13 @@ def reboot(timeout=5, in_seconds=False, wait_for_reboot=False, # pylint: disabl - name: system.reboot - only_on_pending_reboot: True - order: last - ''' - ret = shutdown(timeout=timeout, reboot=True, in_seconds=in_seconds, - only_on_pending_reboot=only_on_pending_reboot) + """ + ret = shutdown( + timeout=timeout, + reboot=True, + in_seconds=in_seconds, + only_on_pending_reboot=only_on_pending_reboot, + ) if wait_for_reboot: seconds = _convert_minutes_seconds(timeout, in_seconds) @@ -225,9 +235,15 @@ def reboot(timeout=5, in_seconds=False, wait_for_reboot=False, # pylint: disabl return ret -def shutdown(message=None, timeout=5, force_close=True, reboot=False, # pylint: disable=redefined-outer-name - in_seconds=False, only_on_pending_reboot=False): - ''' +def shutdown( + message=None, + timeout=5, + force_close=True, + reboot=False, # pylint: disable=redefined-outer-name + in_seconds=False, + only_on_pending_reboot=False, +): + """ Shutdown a running system. Args: @@ -288,7 +304,7 @@ def shutdown(message=None, timeout=5, force_close=True, reboot=False, # pylint: .. code-block:: bash salt '*' system.shutdown "System will shutdown in 5 minutes" - ''' + """ if six.PY2: message = _to_unicode(message) @@ -298,22 +314,23 @@ def shutdown(message=None, timeout=5, force_close=True, reboot=False, # pylint: return False if message and not isinstance(message, six.string_types): - message = message.decode('utf-8') + message = message.decode("utf-8") try: - win32api.InitiateSystemShutdown('127.0.0.1', message, timeout, - force_close, reboot) + win32api.InitiateSystemShutdown( + "127.0.0.1", message, timeout, force_close, reboot + ) return True except pywintypes.error as exc: (number, context, message) = exc.args - log.error('Failed to shutdown the system') - log.error('nbr: %s', number) - log.error('ctx: %s', context) - log.error('msg: %s', message) + log.error("Failed to shutdown the system") + log.error("nbr: %s", number) + log.error("ctx: %s", context) + log.error("msg: %s", message) return False def shutdown_hard(): - ''' + """ Shutdown a running system with no timeout or warning. Returns: @@ -324,12 +341,12 @@ def shutdown_hard(): .. code-block:: bash salt '*' system.shutdown_hard - ''' + """ return shutdown(timeout=0) def shutdown_abort(): - ''' + """ Abort a shutdown. Only available while the dialog box is being displayed to the user. Once the shutdown has initiated, it cannot be aborted. @@ -342,21 +359,21 @@ def shutdown_abort(): .. code-block:: bash salt 'minion-id' system.shutdown_abort - ''' + """ try: - win32api.AbortSystemShutdown('127.0.0.1') + win32api.AbortSystemShutdown("127.0.0.1") return True except pywintypes.error as exc: (number, context, message) = exc.args - log.error('Failed to abort system shutdown') - log.error('nbr: %s', number) - log.error('ctx: %s', context) - log.error('msg: %s', message) + log.error("Failed to abort system shutdown") + log.error("nbr: %s", number) + log.error("ctx: %s", context) + log.error("msg: %s", message) return False def lock(): - ''' + """ Lock the workstation. Returns: @@ -367,12 +384,12 @@ def lock(): .. code-block:: bash salt 'minion-id' system.lock - ''' + """ return windll.user32.LockWorkStation() def set_computer_name(name): - ''' + """ Set the Windows computer name Args: @@ -390,23 +407,24 @@ def set_computer_name(name): .. code-block:: bash salt 'minion-id' system.set_computer_name 'DavesComputer' - ''' + """ if six.PY2: name = _to_unicode(name) if windll.kernel32.SetComputerNameExW( - win32con.ComputerNamePhysicalDnsHostname, name): - ret = {'Computer Name': {'Current': get_computer_name()}} + win32con.ComputerNamePhysicalDnsHostname, name + ): + ret = {"Computer Name": {"Current": get_computer_name()}} pending = get_pending_computer_name() if pending not in (None, False): - ret['Computer Name']['Pending'] = pending + ret["Computer Name"]["Pending"] = pending return ret return False def get_pending_computer_name(): - ''' + """ Get a pending computer name. If the computer name has been changed, and the change is pending a system reboot, this function will return the pending computer name. Otherwise, ``None`` will be returned. If there was an error @@ -423,19 +441,18 @@ def get_pending_computer_name(): .. code-block:: bash salt 'minion-id' system.get_pending_computer_name - ''' + """ current = get_computer_name() - pending = __salt__['reg.read_value']( - 'HKLM', - r'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters', - 'NV Hostname')['vdata'] + pending = __salt__["reg.read_value"]( + "HKLM", r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters", "NV Hostname" + )["vdata"] if pending: return pending if pending != current else None return False def get_computer_name(): - ''' + """ Get the Windows computer name Returns: @@ -446,13 +463,13 @@ def get_computer_name(): .. code-block:: bash salt 'minion-id' system.get_computer_name - ''' + """ name = win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname) return name if name else False def set_computer_desc(desc=None): - ''' + """ Set the Windows computer description Args: @@ -468,7 +485,7 @@ def set_computer_desc(desc=None): .. code-block:: bash salt 'minion-id' system.set_computer_desc 'This computer belongs to Dave!' - ''' + """ if six.PY2: desc = _to_unicode(desc) @@ -480,27 +497,31 @@ def set_computer_desc(desc=None): if desc is None: return False - system_info['comment'] = desc + system_info["comment"] = desc # Apply new settings try: win32net.NetServerSetInfo(None, 101, system_info) except win32net.error as exc: (number, context, message) = exc.args - log.error('Failed to update system') - log.error('nbr: %s', number) - log.error('ctx: %s', context) - log.error('msg: %s', message) + log.error("Failed to update system") + log.error("nbr: %s", number) + log.error("ctx: %s", context) + log.error("msg: %s", message) return False - return {'Computer Description': get_computer_desc()} + return {"Computer Description": get_computer_desc()} -set_computer_description = salt.utils.functools.alias_function(set_computer_desc, 'set_computer_description') # pylint: disable=invalid-name +# pylint: disable=invalid-name +set_computer_description = salt.utils.functools.alias_function( + set_computer_desc, "set_computer_description" +) +# pylint: enable=invalid-name def get_system_info(): - ''' + """ Get system information. .. note:: @@ -517,133 +538,148 @@ def get_system_info(): .. code-block:: bash salt 'minion-id' system.get_system_info - ''' + """ + def byte_calc(val): val = float(val) - if val < 2**10: - return '{0:.3f}B'.format(val) - elif val < 2**20: - return '{0:.3f}KB'.format(val / 2**10) - elif val < 2**30: - return '{0:.3f}MB'.format(val / 2**20) - elif val < 2**40: - return '{0:.3f}GB'.format(val / 2**30) + if val < 2 ** 10: + return "{0:.3f}B".format(val) + elif val < 2 ** 20: + return "{0:.3f}KB".format(val / 2 ** 10) + elif val < 2 ** 30: + return "{0:.3f}MB".format(val / 2 ** 20) + elif val < 2 ** 40: + return "{0:.3f}GB".format(val / 2 ** 30) else: - return '{0:.3f}TB'.format(val / 2**40) + return "{0:.3f}TB".format(val / 2 ** 40) # Lookup dicts for Win32_OperatingSystem - os_type = {1: 'Work Station', - 2: 'Domain Controller', - 3: 'Server'} + os_type = {1: "Work Station", 2: "Domain Controller", 3: "Server"} # Connect to WMI with salt.utils.winapi.Com(): conn = wmi.WMI() system = conn.Win32_OperatingSystem()[0] - ret = {'name': get_computer_name(), - 'description': system.Description, - 'install_date': system.InstallDate, - 'last_boot': system.LastBootUpTime, - 'os_manufacturer': system.Manufacturer, - 'os_name': system.Caption, - 'users': system.NumberOfUsers, - 'organization': system.Organization, - 'os_architecture': system.OSArchitecture, - 'primary': system.Primary, - 'os_type': os_type[system.ProductType], - 'registered_user': system.RegisteredUser, - 'system_directory': system.SystemDirectory, - 'system_drive': system.SystemDrive, - 'os_version': system.Version, - 'windows_directory': system.WindowsDirectory} + ret = { + "name": get_computer_name(), + "description": system.Description, + "install_date": system.InstallDate, + "last_boot": system.LastBootUpTime, + "os_manufacturer": system.Manufacturer, + "os_name": system.Caption, + "users": system.NumberOfUsers, + "organization": system.Organization, + "os_architecture": system.OSArchitecture, + "primary": system.Primary, + "os_type": os_type[system.ProductType], + "registered_user": system.RegisteredUser, + "system_directory": system.SystemDirectory, + "system_drive": system.SystemDrive, + "os_version": system.Version, + "windows_directory": system.WindowsDirectory, + } # lookup dicts for Win32_ComputerSystem - domain_role = {0: 'Standalone Workstation', - 1: 'Member Workstation', - 2: 'Standalone Server', - 3: 'Member Server', - 4: 'Backup Domain Controller', - 5: 'Primary Domain Controller'} - warning_states = {1: 'Other', - 2: 'Unknown', - 3: 'Safe', - 4: 'Warning', - 5: 'Critical', - 6: 'Non-recoverable'} - pc_system_types = {0: 'Unspecified', - 1: 'Desktop', - 2: 'Mobile', - 3: 'Workstation', - 4: 'Enterprise Server', - 5: 'SOHO Server', - 6: 'Appliance PC', - 7: 'Performance Server', - 8: 'Maximum'} + domain_role = { + 0: "Standalone Workstation", + 1: "Member Workstation", + 2: "Standalone Server", + 3: "Member Server", + 4: "Backup Domain Controller", + 5: "Primary Domain Controller", + } + warning_states = { + 1: "Other", + 2: "Unknown", + 3: "Safe", + 4: "Warning", + 5: "Critical", + 6: "Non-recoverable", + } + pc_system_types = { + 0: "Unspecified", + 1: "Desktop", + 2: "Mobile", + 3: "Workstation", + 4: "Enterprise Server", + 5: "SOHO Server", + 6: "Appliance PC", + 7: "Performance Server", + 8: "Maximum", + } # Must get chassis_sku_number this way for backwards compatibility # system.ChassisSKUNumber is only available on Windows 10/2016 and newer product = conn.Win32_ComputerSystemProduct()[0] - ret.update({'chassis_sku_number': product.SKUNumber}) + ret.update({"chassis_sku_number": product.SKUNumber}) system = conn.Win32_ComputerSystem()[0] # Get pc_system_type depending on Windows version - if platform.release() in ['Vista', '7', '8']: + if platform.release() in ["Vista", "7", "8"]: # Types for Vista, 7, and 8 pc_system_type = pc_system_types[system.PCSystemType] else: # New types were added with 8.1 and newer - pc_system_types.update({8: 'Slate', 9: 'Maximum'}) + pc_system_types.update({8: "Slate", 9: "Maximum"}) pc_system_type = pc_system_types[system.PCSystemType] - ret.update({ - 'bootup_state': system.BootupState, - 'caption': system.Caption, - 'chassis_bootup_state': warning_states[system.ChassisBootupState], - 'dns_hostname': system.DNSHostname, - 'domain': system.Domain, - 'domain_role': domain_role[system.DomainRole], - 'hardware_manufacturer': system.Manufacturer, - 'hardware_model': system.Model, - 'network_server_mode_enabled': system.NetworkServerModeEnabled, - 'part_of_domain': system.PartOfDomain, - 'pc_system_type': pc_system_type, - 'power_state': system.PowerState, - 'status': system.Status, - 'system_type': system.SystemType, - 'total_physical_memory': byte_calc(system.TotalPhysicalMemory), - 'total_physical_memory_raw': system.TotalPhysicalMemory, - 'thermal_state': warning_states[system.ThermalState], - 'workgroup': system.Workgroup - }) + ret.update( + { + "bootup_state": system.BootupState, + "caption": system.Caption, + "chassis_bootup_state": warning_states[system.ChassisBootupState], + "dns_hostname": system.DNSHostname, + "domain": system.Domain, + "domain_role": domain_role[system.DomainRole], + "hardware_manufacturer": system.Manufacturer, + "hardware_model": system.Model, + "network_server_mode_enabled": system.NetworkServerModeEnabled, + "part_of_domain": system.PartOfDomain, + "pc_system_type": pc_system_type, + "power_state": system.PowerState, + "status": system.Status, + "system_type": system.SystemType, + "total_physical_memory": byte_calc(system.TotalPhysicalMemory), + "total_physical_memory_raw": system.TotalPhysicalMemory, + "thermal_state": warning_states[system.ThermalState], + "workgroup": system.Workgroup, + } + ) # Get processor information processors = conn.Win32_Processor() - ret['processors'] = 0 - ret['processors_logical'] = 0 - ret['processor_cores'] = 0 - ret['processor_cores_enabled'] = 0 - ret['processor_manufacturer'] = processors[0].Manufacturer - ret['processor_max_clock_speed'] = six.text_type(processors[0].MaxClockSpeed) + 'MHz' + ret["processors"] = 0 + ret["processors_logical"] = 0 + ret["processor_cores"] = 0 + ret["processor_cores_enabled"] = 0 + ret["processor_manufacturer"] = processors[0].Manufacturer + ret["processor_max_clock_speed"] = ( + six.text_type(processors[0].MaxClockSpeed) + "MHz" + ) for system in processors: - ret['processors'] += 1 - ret['processors_logical'] += system.NumberOfLogicalProcessors - ret['processor_cores'] += system.NumberOfCores + ret["processors"] += 1 + ret["processors_logical"] += system.NumberOfLogicalProcessors + ret["processor_cores"] += system.NumberOfCores try: - ret['processor_cores_enabled'] += system.NumberOfEnabledCore + ret["processor_cores_enabled"] += system.NumberOfEnabledCore except (AttributeError, TypeError): pass - if ret['processor_cores_enabled'] == 0: - ret.pop('processor_cores_enabled', False) + if ret["processor_cores_enabled"] == 0: + ret.pop("processor_cores_enabled", False) system = conn.Win32_BIOS()[0] - ret.update({'hardware_serial': system.SerialNumber, - 'bios_manufacturer': system.Manufacturer, - 'bios_version': system.Version, - 'bios_details': system.BIOSVersion, - 'bios_caption': system.Caption, - 'bios_description': system.Description}) - ret['install_date'] = _convert_date_time_string(ret['install_date']) - ret['last_boot'] = _convert_date_time_string(ret['last_boot']) + ret.update( + { + "hardware_serial": system.SerialNumber, + "bios_manufacturer": system.Manufacturer, + "bios_version": system.Version, + "bios_details": system.BIOSVersion, + "bios_caption": system.Caption, + "bios_description": system.Description, + } + ) + ret["install_date"] = _convert_date_time_string(ret["install_date"]) + ret["last_boot"] = _convert_date_time_string(ret["last_boot"]) return ret def get_computer_desc(): - ''' + """ Get the Windows computer description Returns: @@ -655,16 +691,20 @@ def get_computer_desc(): .. code-block:: bash salt 'minion-id' system.get_computer_desc - ''' - desc = get_system_info()['description'] + """ + desc = get_system_info()["description"] return False if desc is None else desc -get_computer_description = salt.utils.functools.alias_function(get_computer_desc, 'get_computer_description') # pylint: disable=invalid-name +# pylint: disable=invalid-name +get_computer_description = salt.utils.functools.alias_function( + get_computer_desc, "get_computer_description" +) +# pylint: enable=invalid-name def get_hostname(): - ''' + """ Get the hostname of the windows minion .. versionadded:: 2016.3.0 @@ -677,14 +717,14 @@ def get_hostname(): .. code-block:: bash salt 'minion-id' system.get_hostname - ''' - cmd = 'hostname' - ret = __salt__['cmd.run'](cmd=cmd) + """ + cmd = "hostname" + ret = __salt__["cmd.run"](cmd=cmd) return ret def set_hostname(hostname): - ''' + """ Set the hostname of the windows minion, requires a restart before this will be updated. @@ -701,21 +741,25 @@ def set_hostname(hostname): .. code-block:: bash salt 'minion-id' system.set_hostname newhostname - ''' + """ curr_hostname = get_hostname() - cmd = "wmic computersystem where name='{0}' call rename name='{1}'".format(curr_hostname, hostname) - ret = __salt__['cmd.run'](cmd=cmd) + cmd = "wmic computersystem where name='{0}' call rename name='{1}'".format( + curr_hostname, hostname + ) + ret = __salt__["cmd.run"](cmd=cmd) return "successful" in ret -def join_domain(domain, - username=None, - password=None, - account_ou=None, - account_exists=False, - restart=False): - ''' +def join_domain( + domain, + username=None, + password=None, + account_ou=None, + account_exists=False, + restart=False, +): + """ Join a computer to an Active Directory domain. Requires a reboot. Args: @@ -760,7 +804,7 @@ def join_domain(domain, username='joinuser' password='joinpassword' \\ account_ou='ou=clients,ou=org,dc=domain,dc=tld' \\ account_exists=False, restart=True - ''' + """ if six.PY2: domain = _to_unicode(domain) username = _to_unicode(username) @@ -768,40 +812,42 @@ def join_domain(domain, account_ou = _to_unicode(account_ou) status = get_domain_workgroup() - if 'Domain' in status: - if status['Domain'] == domain: - return 'Already joined to {0}'.format(domain) + if "Domain" in status: + if status["Domain"] == domain: + return "Already joined to {0}".format(domain) - if username and '\\' not in username and '@' not in username: - username = '{0}@{1}'.format(username, domain) + if username and "\\" not in username and "@" not in username: + username = "{0}@{1}".format(username, domain) if username and password is None: - return 'Must specify a password if you pass a username' + return "Must specify a password if you pass a username" # remove any escape characters if isinstance(account_ou, six.string_types): - account_ou = account_ou.split('\\') - account_ou = ''.join(account_ou) + account_ou = account_ou.split("\\") + account_ou = "".join(account_ou) - err = _join_domain(domain=domain, username=username, password=password, - account_ou=account_ou, account_exists=account_exists) + err = _join_domain( + domain=domain, + username=username, + password=password, + account_ou=account_ou, + account_exists=account_exists, + ) if not err: - ret = {'Domain': domain, - 'Restart': False} + ret = {"Domain": domain, "Restart": False} if restart: - ret['Restart'] = reboot() + ret["Restart"] = reboot() return ret raise CommandExecutionError(win32api.FormatMessage(err).rstrip()) -def _join_domain(domain, - username=None, - password=None, - account_ou=None, - account_exists=False): - ''' +def _join_domain( + domain, username=None, password=None, account_ou=None, account_exists=False +): + """ Helper function to join the domain. Args: @@ -832,7 +878,7 @@ def _join_domain(domain, :param account_ou: :param account_exists: :return: - ''' + """ NETSETUP_JOIN_DOMAIN = 0x1 # pylint: disable=invalid-name NETSETUP_ACCOUNT_CREATE = 0x2 # pylint: disable=invalid-name NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x20 # pylint: disable=invalid-name @@ -853,18 +899,24 @@ def _join_domain(domain, # JoinDomainOrWorkgroup returns a strangely formatted value that looks like # (0,) so return the first item return comp.JoinDomainOrWorkgroup( - Name=domain, Password=password, UserName=username, AccountOU=account_ou, - FJoinOptions=join_options)[0] + Name=domain, + Password=password, + UserName=username, + AccountOU=account_ou, + FJoinOptions=join_options, + )[0] -def unjoin_domain(username=None, - password=None, - domain=None, - workgroup='WORKGROUP', - disable=False, - restart=False): +def unjoin_domain( + username=None, + password=None, + domain=None, + workgroup="WORKGROUP", + disable=False, + restart=False, +): # pylint: disable=anomalous-backslash-in-string - ''' + """ Unjoin a computer from an Active Directory Domain. Requires a restart. Args: @@ -910,7 +962,7 @@ def unjoin_domain(username=None, salt 'minion-id' system.unjoin_domain username='unjoinuser' \\ password='unjoinpassword' disable=True \\ restart=True - ''' + """ # pylint: enable=anomalous-backslash-in-string if six.PY2: username = _to_unicode(username) @@ -918,18 +970,18 @@ def unjoin_domain(username=None, domain = _to_unicode(domain) status = get_domain_workgroup() - if 'Workgroup' in status: - if status['Workgroup'] == workgroup: - return 'Already joined to {0}'.format(workgroup) + if "Workgroup" in status: + if status["Workgroup"] == workgroup: + return "Already joined to {0}".format(workgroup) - if username and '\\' not in username and '@' not in username: + if username and "\\" not in username and "@" not in username: if domain: - username = '{0}@{1}'.format(username, domain) + username = "{0}@{1}".format(username, domain) else: - return 'Must specify domain if not supplied in username' + return "Must specify domain if not supplied in username" if username and password is None: - return 'Must specify a password if you pass a username' + return "Must specify a password if you pass a username" NETSETUP_ACCT_DELETE = 0x4 # pylint: disable=invalid-name @@ -940,33 +992,32 @@ def unjoin_domain(username=None, with salt.utils.winapi.Com(): conn = wmi.WMI() comp = conn.Win32_ComputerSystem()[0] - err = comp.UnjoinDomainOrWorkgroup(Password=password, - UserName=username, - FUnjoinOptions=unjoin_options) + err = comp.UnjoinDomainOrWorkgroup( + Password=password, UserName=username, FUnjoinOptions=unjoin_options + ) # you have to do this because UnjoinDomainOrWorkgroup returns a # strangely formatted value that looks like (0,) if not err[0]: err = comp.JoinDomainOrWorkgroup(Name=workgroup) if not err[0]: - ret = {'Workgroup': workgroup, - 'Restart': False} + ret = {"Workgroup": workgroup, "Restart": False} if restart: - ret['Restart'] = reboot() + ret["Restart"] = reboot() return ret else: log.error(win32api.FormatMessage(err[0]).rstrip()) - log.error('Failed to join the computer to %s', workgroup) + log.error("Failed to join the computer to %s", workgroup) return False else: log.error(win32api.FormatMessage(err[0]).rstrip()) - log.error('Failed to unjoin computer from %s', status['Domain']) + log.error("Failed to unjoin computer from %s", status["Domain"]) return False def get_domain_workgroup(): - ''' + """ Get the domain or workgroup the computer belongs to. .. versionadded:: 2015.5.7 @@ -980,18 +1031,18 @@ def get_domain_workgroup(): .. code-block:: bash salt 'minion-id' system.get_domain_workgroup - ''' + """ with salt.utils.winapi.Com(): conn = wmi.WMI() for computer in conn.Win32_ComputerSystem(): if computer.PartOfDomain: - return {'Domain': computer.Domain} + return {"Domain": computer.Domain} else: - return {'Workgroup': computer.Domain} + return {"Workgroup": computer.Domain} def _try_parse_datetime(time_str, fmts): - ''' + """ A helper function that attempts to parse the input time_str as a date. Args: @@ -1002,7 +1053,7 @@ def _try_parse_datetime(time_str, fmts): Returns: datetime: Returns a datetime object if parsed properly, otherwise None - ''' + """ result = None for fmt in fmts: try: @@ -1014,7 +1065,7 @@ def _try_parse_datetime(time_str, fmts): def get_system_time(): - ''' + """ Get the system time. Returns: @@ -1025,22 +1076,22 @@ def get_system_time(): .. code-block:: bash salt 'minion-id' system.get_system_time - ''' + """ now = win32api.GetLocalTime() - meridian = 'AM' + meridian = "AM" hours = int(now[4]) if hours == 12: - meridian = 'PM' + meridian = "PM" elif hours == 0: hours = 12 elif hours > 12: hours = hours - 12 - meridian = 'PM' - return '{0:02d}:{1:02d}:{2:02d} {3}'.format(hours, now[5], now[6], meridian) + meridian = "PM" + return "{0:02d}:{1:02d}:{2:02d} {3}".format(hours, now[5], now[6], meridian) def set_system_time(newtime): - ''' + """ Set the system time. Args: @@ -1061,26 +1112,23 @@ def set_system_time(newtime): .. code-block:: bash salt 'minion-id' system.set_system_time 12:01 - ''' + """ # Get date/time object from newtime - fmts = ['%I:%M:%S %p', '%I:%M %p', '%H:%M:%S', '%H:%M'] + fmts = ["%I:%M:%S %p", "%I:%M %p", "%H:%M:%S", "%H:%M"] dt_obj = _try_parse_datetime(newtime, fmts) if dt_obj is None: return False # Set time using set_system_date_time() - return set_system_date_time(hours=dt_obj.hour, - minutes=dt_obj.minute, - seconds=dt_obj.second) + return set_system_date_time( + hours=dt_obj.hour, minutes=dt_obj.minute, seconds=dt_obj.second + ) -def set_system_date_time(years=None, - months=None, - days=None, - hours=None, - minutes=None, - seconds=None): - ''' +def set_system_date_time( + years=None, months=None, days=None, hours=None, minutes=None, seconds=None +): + """ Set the system date and time. Each argument is an element of the date, but not required. If an element is not passed, the current system value for that element will be used. For example, if you don't pass the year, the current @@ -1103,16 +1151,16 @@ def set_system_date_time(years=None, .. code-block:: bash salt '*' system.set_system_date_ time 2015 5 12 11 37 53 - ''' + """ # Get the current date/time try: date_time = win32api.GetLocalTime() except win32api.error as exc: (number, context, message) = exc.args - log.error('Failed to get local time') - log.error('nbr: %s', number) - log.error('ctx: %s', context) - log.error('msg: %s', message) + log.error("Failed to get local time") + log.error("nbr: %s", number) + log.error("ctx: %s", context) + log.error("msg: %s", message) return False # Check for passed values. If not passed, use current values @@ -1130,38 +1178,42 @@ def set_system_date_time(years=None, seconds = date_time[6] try: + class SYSTEMTIME(ctypes.Structure): _fields_ = [ - ('wYear', ctypes.c_int16), - ('wMonth', ctypes.c_int16), - ('wDayOfWeek', ctypes.c_int16), - ('wDay', ctypes.c_int16), - ('wHour', ctypes.c_int16), - ('wMinute', ctypes.c_int16), - ('wSecond', ctypes.c_int16), - ('wMilliseconds', ctypes.c_int16)] + ("wYear", ctypes.c_int16), + ("wMonth", ctypes.c_int16), + ("wDayOfWeek", ctypes.c_int16), + ("wDay", ctypes.c_int16), + ("wHour", ctypes.c_int16), + ("wMinute", ctypes.c_int16), + ("wSecond", ctypes.c_int16), + ("wMilliseconds", ctypes.c_int16), + ] + system_time = SYSTEMTIME() + # pylint: disable=invalid-name system_time.wYear = int(years) system_time.wMonth = int(months) system_time.wDay = int(days) system_time.wHour = int(hours) system_time.wMinute = int(minutes) system_time.wSecond = int(seconds) + # pylint: enable=invalid-name system_time_ptr = ctypes.pointer(system_time) succeeded = ctypes.windll.kernel32.SetLocalTime(system_time_ptr) if succeeded is not 0: return True else: - log.error('Failed to set local time') - raise CommandExecutionError( - win32api.FormatMessage(succeeded).rstrip()) + log.error("Failed to set local time") + raise CommandExecutionError(win32api.FormatMessage(succeeded).rstrip()) except OSError as err: - log.error('Failed to set local time') + log.error("Failed to set local time") raise CommandExecutionError(err) def get_system_date(): - ''' + """ Get the Windows system date Returns: @@ -1172,13 +1224,13 @@ def get_system_date(): .. code-block:: bash salt '*' system.get_system_date - ''' + """ now = win32api.GetLocalTime() - return '{0:02d}/{1:02d}/{2:04d}'.format(now[1], now[3], now[0]) + return "{0:02d}/{1:02d}/{2:04d}".format(now[1], now[3], now[0]) def set_system_date(newdate): - ''' + """ Set the Windows system date. Use <mm-dd-yy> format for the date. Args: @@ -1200,22 +1252,19 @@ def set_system_date(newdate): .. code-block:: bash salt '*' system.set_system_date '03-28-13' - ''' - fmts = ['%Y-%m-%d', '%m-%d-%Y', '%m-%d-%y', - '%m/%d/%Y', '%m/%d/%y', '%Y/%m/%d'] + """ + fmts = ["%Y-%m-%d", "%m-%d-%Y", "%m-%d-%y", "%m/%d/%Y", "%m/%d/%y", "%Y/%m/%d"] # Get date/time object from newdate dt_obj = _try_parse_datetime(newdate, fmts) if dt_obj is None: return False # Set time using set_system_date_time() - return set_system_date_time(years=dt_obj.year, - months=dt_obj.month, - days=dt_obj.day) + return set_system_date_time(years=dt_obj.year, months=dt_obj.month, days=dt_obj.day) def start_time_service(): - ''' + """ Start the Windows time service Returns: @@ -1226,12 +1275,12 @@ def start_time_service(): .. code-block:: bash salt '*' system.start_time_service - ''' - return __salt__['service.start']('w32time') + """ + return __salt__["service.start"]("w32time") def stop_time_service(): - ''' + """ Stop the Windows time service Returns: @@ -1242,12 +1291,12 @@ def stop_time_service(): .. code-block:: bash salt '*' system.stop_time_service - ''' - return __salt__['service.stop']('w32time') + """ + return __salt__["service.stop"]("w32time") def get_pending_component_servicing(): - ''' + """ Determine whether there are pending Component Based Servicing tasks that require a reboot. @@ -1262,21 +1311,21 @@ def get_pending_component_servicing(): .. code-block:: bash salt '*' system.get_pending_component_servicing - ''' - key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending' + """ + key = r"SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" # So long as the registry key exists, a reboot is pending. - if __utils__['reg.key_exists']('HKLM', key): - log.debug('Key exists: %s', key) + if __utils__["reg.key_exists"]("HKLM", key): + log.debug("Key exists: %s", key) return True else: - log.debug('Key does not exist: %s', key) + log.debug("Key does not exist: %s", key) return False def get_pending_domain_join(): - ''' + """ Determine whether there is a pending domain join action that requires a reboot. @@ -1291,30 +1340,30 @@ def get_pending_domain_join(): .. code-block:: bash salt '*' system.get_pending_domain_join - ''' - base_key = r'SYSTEM\CurrentControlSet\Services\Netlogon' - avoid_key = r'{0}\AvoidSpnSet'.format(base_key) - join_key = r'{0}\JoinDomain'.format(base_key) + """ + base_key = r"SYSTEM\CurrentControlSet\Services\Netlogon" + avoid_key = r"{0}\AvoidSpnSet".format(base_key) + join_key = r"{0}\JoinDomain".format(base_key) # If either the avoid_key or join_key is present, # then there is a reboot pending. - if __utils__['reg.key_exists']('HKLM', avoid_key): - log.debug('Key exists: %s', avoid_key) + if __utils__["reg.key_exists"]("HKLM", avoid_key): + log.debug("Key exists: %s", avoid_key) return True else: - log.debug('Key does not exist: %s', avoid_key) + log.debug("Key does not exist: %s", avoid_key) - if __utils__['reg.key_exists']('HKLM', join_key): - log.debug('Key exists: %s', join_key) + if __utils__["reg.key_exists"]("HKLM", join_key): + log.debug("Key exists: %s", join_key) return True else: - log.debug('Key does not exist: %s', join_key) + log.debug("Key does not exist: %s", join_key) return False def get_pending_file_rename(): - ''' + """ Determine whether there are pending file rename operations that require a reboot. @@ -1329,28 +1378,28 @@ def get_pending_file_rename(): .. code-block:: bash salt '*' system.get_pending_file_rename - ''' - vnames = ('PendingFileRenameOperations', 'PendingFileRenameOperations2') - key = r'SYSTEM\CurrentControlSet\Control\Session Manager' + """ + vnames = ("PendingFileRenameOperations", "PendingFileRenameOperations2") + key = r"SYSTEM\CurrentControlSet\Control\Session Manager" # If any of the value names exist and have value data set, # then a reboot is pending. for vname in vnames: - reg_ret = __salt__['reg.read_value']('HKLM', key, vname) + reg_ret = __salt__["reg.read_value"]("HKLM", key, vname) - if reg_ret['success']: - log.debug('Found key: %s', key) + if reg_ret["success"]: + log.debug("Found key: %s", key) - if reg_ret['vdata'] and (reg_ret['vdata'] != '(value not set)'): + if reg_ret["vdata"] and (reg_ret["vdata"] != "(value not set)"): return True else: - log.debug('Unable to access key: %s', key) + log.debug("Unable to access key: %s", key) return False def get_pending_servermanager(): - ''' + """ Determine whether there are pending Server Manager tasks that require a reboot. @@ -1365,31 +1414,31 @@ def get_pending_servermanager(): .. code-block:: bash salt '*' system.get_pending_servermanager - ''' - vname = 'CurrentRebootAttempts' - key = r'SOFTWARE\Microsoft\ServerManager' + """ + vname = "CurrentRebootAttempts" + key = r"SOFTWARE\Microsoft\ServerManager" # There are situations where it's possible to have '(value not set)' as # the value data, and since an actual reboot won't be pending in that # instance, just catch instances where we try unsuccessfully to cast as int. - reg_ret = __salt__['reg.read_value']('HKLM', key, vname) + reg_ret = __salt__["reg.read_value"]("HKLM", key, vname) - if reg_ret['success']: - log.debug('Found key: %s', key) + if reg_ret["success"]: + log.debug("Found key: %s", key) try: - if int(reg_ret['vdata']) > 0: + if int(reg_ret["vdata"]) > 0: return True except ValueError: pass else: - log.debug('Unable to access key: %s', key) + log.debug("Unable to access key: %s", key) return False def get_pending_update(): - ''' + """ Determine whether there are pending updates that require a reboot. .. versionadded:: 2016.11.0 @@ -1402,27 +1451,27 @@ def get_pending_update(): .. code-block:: bash salt '*' system.get_pending_update - ''' - key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired' + """ + key = r"SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" # So long as the registry key exists, a reboot is pending. - if __utils__['reg.key_exists']('HKLM', key): - log.debug('Key exists: %s', key) + if __utils__["reg.key_exists"]("HKLM", key): + log.debug("Key exists: %s", key) return True else: - log.debug('Key does not exist: %s', key) + log.debug("Key does not exist: %s", key) return False -MINION_VOLATILE_KEY = r'SYSTEM\CurrentControlSet\Services\salt-minion\Volatile-Data' +MINION_VOLATILE_KEY = r"SYSTEM\CurrentControlSet\Services\salt-minion\Volatile-Data" -REBOOT_REQUIRED_NAME = 'Reboot required' +REBOOT_REQUIRED_NAME = "Reboot required" def set_reboot_required_witnessed(): - r''' + r""" This function is used to remember that an event indicating that a reboot is required was witnessed. This function relies on the salt-minion's ability to create the following volatile registry key in the *HKLM* hive: @@ -1447,17 +1496,19 @@ def set_reboot_required_witnessed(): .. code-block:: bash salt '*' system.set_reboot_required_witnessed - ''' - return __salt__['reg.set_value'](hive='HKLM', - key=MINION_VOLATILE_KEY, - volatile=True, - vname=REBOOT_REQUIRED_NAME, - vdata=1, - vtype='REG_DWORD') + """ + return __salt__["reg.set_value"]( + hive="HKLM", + key=MINION_VOLATILE_KEY, + volatile=True, + vname=REBOOT_REQUIRED_NAME, + vdata=1, + vtype="REG_DWORD", + ) def get_reboot_required_witnessed(): - ''' + """ Determine if at any time during the current boot session the salt minion witnessed an event indicating that a reboot is required. @@ -1477,15 +1528,15 @@ def get_reboot_required_witnessed(): salt '*' system.get_reboot_required_witnessed - ''' - value_dict = __salt__['reg.read_value'](hive='HKLM', - key=MINION_VOLATILE_KEY, - vname=REBOOT_REQUIRED_NAME) - return value_dict['vdata'] == 1 + """ + value_dict = __salt__["reg.read_value"]( + hive="HKLM", key=MINION_VOLATILE_KEY, vname=REBOOT_REQUIRED_NAME + ) + return value_dict["vdata"] == 1 def get_pending_reboot(): - ''' + """ Determine whether there is a reboot pending. .. versionadded:: 2016.11.0 @@ -1498,16 +1549,18 @@ def get_pending_reboot(): .. code-block:: bash salt '*' system.get_pending_reboot - ''' + """ # Order the checks for reboot pending in most to least likely. - checks = (get_pending_update, - get_pending_file_rename, - get_pending_servermanager, - get_pending_component_servicing, - get_reboot_required_witnessed, - get_pending_computer_name, - get_pending_domain_join) + checks = ( + get_pending_update, + get_pending_file_rename, + get_pending_servermanager, + get_pending_component_servicing, + get_reboot_required_witnessed, + get_pending_computer_name, + get_pending_domain_join, + ) for check in checks: if check(): diff --git a/salt/modules/win_task.py b/salt/modules/win_task.py index c840d366c2b..8507c7af0aa 100644 --- a/salt/modules/win_task.py +++ b/salt/modules/win_task.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # https://msdn.microsoft.com/en-us/library/windows/desktop/aa383608(v=vs.85).aspx -''' +""" Windows Task Scheduler Module .. versionadded:: 2016.3.0 @@ -8,30 +8,32 @@ A module for working with the Windows Task Scheduler. You can add and edit existing tasks. You can add and clear triggers and actions. You can list all tasks, folders, triggers, and actions. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -from datetime import datetime + import logging import time +from datetime import datetime # Import Salt libs import salt.utils.platform import salt.utils.winapi +from salt.ext.six.moves import range # Import 3rd Party Libraries try: import pythoncom import win32com.client + HAS_DEPENDENCIES = True except ImportError: HAS_DEPENDENCIES = False -from salt.ext.six.moves import range log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'task' +__virtualname__ = "task" # Define Constants # TASK_ACTION_TYPE @@ -94,87 +96,98 @@ TASK_TRIGGER_BOOT = 8 TASK_TRIGGER_LOGON = 9 TASK_TRIGGER_SESSION_STATE_CHANGE = 11 -duration = {'Immediately': 'PT0M', - 'Indefinitely': '', - 'Do not wait': 'PT0M', - '15 seconds': 'PT15S', - '30 seconds': 'PT30S', - '1 minute': 'PT1M', - '5 minutes': 'PT5M', - '10 minutes': 'PT10M', - '15 minutes': 'PT15M', - '30 minutes': 'PT30M', - '1 hour': 'PT1H', - '2 hours': 'PT2H', - '4 hours': 'PT4H', - '8 hours': 'PT8H', - '12 hours': 'PT12H', - '1 day': ['P1D', 'PT24H'], - '3 days': ['P3D', 'PT72H'], - '30 days': 'P30D', - '90 days': 'P90D', - '180 days': 'P180D', - '365 days': 'P365D'} +duration = { + "Immediately": "PT0M", + "Indefinitely": "", + "Do not wait": "PT0M", + "15 seconds": "PT15S", + "30 seconds": "PT30S", + "1 minute": "PT1M", + "5 minutes": "PT5M", + "10 minutes": "PT10M", + "15 minutes": "PT15M", + "30 minutes": "PT30M", + "1 hour": "PT1H", + "2 hours": "PT2H", + "4 hours": "PT4H", + "8 hours": "PT8H", + "12 hours": "PT12H", + "1 day": ["P1D", "PT24H"], + "3 days": ["P3D", "PT72H"], + "30 days": "P30D", + "90 days": "P90D", + "180 days": "P180D", + "365 days": "P365D", +} -action_types = {'Execute': TASK_ACTION_EXEC, - 'Email': TASK_ACTION_SEND_EMAIL, - 'Message': TASK_ACTION_SHOW_MESSAGE} +action_types = { + "Execute": TASK_ACTION_EXEC, + "Email": TASK_ACTION_SEND_EMAIL, + "Message": TASK_ACTION_SHOW_MESSAGE, +} -trigger_types = {'Event': TASK_TRIGGER_EVENT, - 'Once': TASK_TRIGGER_TIME, - 'Daily': TASK_TRIGGER_DAILY, - 'Weekly': TASK_TRIGGER_WEEKLY, - 'Monthly': TASK_TRIGGER_MONTHLY, - 'MonthlyDay': TASK_TRIGGER_MONTHLYDOW, - 'OnIdle': TASK_TRIGGER_IDLE, - 'OnTaskCreation': TASK_TRIGGER_REGISTRATION, - 'OnBoot': TASK_TRIGGER_BOOT, - 'OnLogon': TASK_TRIGGER_LOGON, - 'OnSessionChange': TASK_TRIGGER_SESSION_STATE_CHANGE} +trigger_types = { + "Event": TASK_TRIGGER_EVENT, + "Once": TASK_TRIGGER_TIME, + "Daily": TASK_TRIGGER_DAILY, + "Weekly": TASK_TRIGGER_WEEKLY, + "Monthly": TASK_TRIGGER_MONTHLY, + "MonthlyDay": TASK_TRIGGER_MONTHLYDOW, + "OnIdle": TASK_TRIGGER_IDLE, + "OnTaskCreation": TASK_TRIGGER_REGISTRATION, + "OnBoot": TASK_TRIGGER_BOOT, + "OnLogon": TASK_TRIGGER_LOGON, + "OnSessionChange": TASK_TRIGGER_SESSION_STATE_CHANGE, +} -states = {TASK_STATE_UNKNOWN: 'Unknown', - TASK_STATE_DISABLED: 'Disabled', - TASK_STATE_QUEUED: 'Queued', - TASK_STATE_READY: 'Ready', - TASK_STATE_RUNNING: 'Running'} +states = { + TASK_STATE_UNKNOWN: "Unknown", + TASK_STATE_DISABLED: "Disabled", + TASK_STATE_QUEUED: "Queued", + TASK_STATE_READY: "Ready", + TASK_STATE_RUNNING: "Running", +} -instances = {'Parallel': TASK_INSTANCES_PARALLEL, - 'Queue': TASK_INSTANCES_QUEUE, - 'No New Instance': TASK_INSTANCES_IGNORE_NEW, - 'Stop Existing': TASK_INSTANCES_STOP_EXISTING} +instances = { + "Parallel": TASK_INSTANCES_PARALLEL, + "Queue": TASK_INSTANCES_QUEUE, + "No New Instance": TASK_INSTANCES_IGNORE_NEW, + "Stop Existing": TASK_INSTANCES_STOP_EXISTING, +} -results = {0x0: 'The operation completed successfully', - 0x1: 'Incorrect or unknown function called', - 0x2: 'File not found', - 0xA: 'The environment is incorrect', - 0x41300: 'Task is ready to run at its next scheduled time', - 0x41301: 'Task is currently running', - 0x41302: 'Task is disabled', - 0x41303: 'Task has not yet run', - 0x41304: 'There are no more runs scheduled for this task', - 0x41306: 'Task was terminated by the user', - 0x8004130F: 'Credentials became corrupted', - 0x8004131F: 'An instance of this task is already running', - 0x800710E0: 'The operator or administrator has refused the request', - 0x800704DD: 'The service is not available (Run only when logged ' - 'in?)', - 0xC000013A: 'The application terminated as a result of CTRL+C', - 0xC06D007E: 'Unknown software exception'} +results = { + 0x0: "The operation completed successfully", + 0x1: "Incorrect or unknown function called", + 0x2: "File not found", + 0xA: "The environment is incorrect", + 0x41300: "Task is ready to run at its next scheduled time", + 0x41301: "Task is currently running", + 0x41302: "Task is disabled", + 0x41303: "Task has not yet run", + 0x41304: "There are no more runs scheduled for this task", + 0x41306: "Task was terminated by the user", + 0x8004130F: "Credentials became corrupted", + 0x8004131F: "An instance of this task is already running", + 0x800710E0: "The operator or administrator has refused the request", + 0x800704DD: "The service is not available (Run only when logged " "in?)", + 0xC000013A: "The application terminated as a result of CTRL+C", + 0xC06D007E: "Unknown software exception", +} def __virtual__(): - ''' + """ Only works on Windows systems - ''' + """ if salt.utils.platform.is_windows(): if not HAS_DEPENDENCIES: - log.warning('Could not load dependencies for %s', __virtualname__) + log.warning("Could not load dependencies for %s", __virtualname__) return __virtualname__ - return False, 'Module win_task: module only works on Windows systems' + return False, "Module win_task: module only works on Windows systems" def _get_date_time_format(dt_string): - ''' + """ Copied from win_system.py (_get_date_time_format) Function that detects the date/time format for the string passed. @@ -184,18 +197,18 @@ def _get_date_time_format(dt_string): :return: The format of the passed dt_string :rtype: str - ''' + """ valid_formats = [ - '%I:%M:%S %p', - '%I:%M %p', - '%H:%M:%S', - '%H:%M', - '%Y-%m-%d', - '%m-%d-%y', - '%m-%d-%Y', - '%m/%d/%y', - '%m/%d/%Y', - '%Y/%m/%d' + "%I:%M:%S %p", + "%I:%M %p", + "%H:%M:%S", + "%H:%M", + "%Y-%m-%d", + "%m-%d-%y", + "%m-%d-%Y", + "%m/%d/%y", + "%m/%d/%Y", + "%Y/%m/%d", ] for dt_format in valid_formats: try: @@ -207,7 +220,7 @@ def _get_date_time_format(dt_string): def _get_date_value(date): - ''' + """ Function for dealing with PyTime values with invalid dates. ie: 12/30/1899 which is the windows task scheduler value for Never @@ -216,15 +229,15 @@ def _get_date_value(date): :return: A string value representing the date or the word "Never" for invalid date strings :rtype: str - ''' + """ try: - return '{0}'.format(date) + return "{0}".format(date) except ValueError: - return 'Never' + return "Never" def _reverse_lookup(dictionary, value): - ''' + """ Lookup the key in a dictionary by its value. Will return the first match. :param dict dictionary: The dictionary to search @@ -233,7 +246,7 @@ def _reverse_lookup(dictionary, value): :return: Returns the first key to match the value :rtype: str - ''' + """ value_index = -1 for idx, dict_value in enumerate(dictionary.values()): if type(dict_value) == list: @@ -248,7 +261,7 @@ def _reverse_lookup(dictionary, value): def _lookup_first(dictionary, key): - ''' + """ Lookup the first value given a key. Returns the first value if the key refers to a list or the value itself. @@ -258,7 +271,7 @@ def _lookup_first(dictionary, key): :return: Returns the first value available for the key :rtype: str - ''' + """ value = dictionary[key] if type(value) == list: return value[0] @@ -266,13 +279,10 @@ def _lookup_first(dictionary, key): return value -def _save_task_definition(name, - task_folder, - task_definition, - user_name, - password, - logon_type): - ''' +def _save_task_definition( + name, task_folder, task_definition, user_name, password, logon_type +): + """ Internal function to save the task definition. :param str name: The name of the task. @@ -290,37 +300,41 @@ def _save_task_definition(name, :return: True if successful, False if not :rtype: bool - ''' + """ try: - task_folder.RegisterTaskDefinition(name, - task_definition, - TASK_CREATE_OR_UPDATE, - user_name, - password, - logon_type) + task_folder.RegisterTaskDefinition( + name, + task_definition, + TASK_CREATE_OR_UPDATE, + user_name, + password, + logon_type, + ) return True except pythoncom.com_error as error: hr, msg, exc, arg = error.args # pylint: disable=W0633 - fc = {-2147024773: 'The filename, directory name, or volume label ' - 'syntax is incorrect', - -2147024894: 'The system cannot find the file specified', - -2147216615: 'Required element or attribute missing', - -2147216616: 'Value incorrectly formatted or out of range', - -2147352571: 'Access denied'} + fc = { + -2147024773: "The filename, directory name, or volume label " + "syntax is incorrect", + -2147024894: "The system cannot find the file specified", + -2147216615: "Required element or attribute missing", + -2147216616: "Value incorrectly formatted or out of range", + -2147352571: "Access denied", + } try: failure_code = fc[exc[5]] except KeyError: - failure_code = 'Unknown Failure: {0}'.format(error) + failure_code = "Unknown Failure: {0}".format(error) - log.debug('Failed to modify task: %s', failure_code) + log.debug("Failed to modify task: %s", failure_code) - return 'Failed to modify task: {0}'.format(failure_code) + return "Failed to modify task: {0}".format(failure_code) -def list_tasks(location='\\'): - r''' +def list_tasks(location="\\"): + r""" List all tasks located in a specific location in the task scheduler. Args: @@ -342,7 +356,7 @@ def list_tasks(location='\\'): # List all tasks in the Microsoft\XblGameSave Directory salt 'minion-id' task.list_tasks Microsoft\XblGameSave - ''' + """ # Create the task service object with salt.utils.winapi.Com(): task_service = win32com.client.Dispatch("Schedule.Service") @@ -359,8 +373,8 @@ def list_tasks(location='\\'): return ret -def list_folders(location='\\'): - r''' +def list_folders(location="\\"): + r""" List all folders located in a specific location in the task scheduler. Args: @@ -382,7 +396,7 @@ def list_folders(location='\\'): # List all folders in the Microsoft directory salt 'minion-id' task.list_folders Microsoft - ''' + """ # Create the task service object with salt.utils.winapi.Com(): task_service = win32com.client.Dispatch("Schedule.Service") @@ -399,8 +413,8 @@ def list_folders(location='\\'): return ret -def list_triggers(name, location='\\'): - r''' +def list_triggers(name, location="\\"): + r""" List all triggers that pertain to a task in the specified location. Args: @@ -426,7 +440,7 @@ def list_triggers(name, location='\\'): # List all triggers for the XblGameSaveTask in the Microsoft\XblGameSave # location salt '*' task.list_triggers XblGameSaveTask Microsoft\XblGameSave - ''' + """ # Create the task service object with salt.utils.winapi.Com(): task_service = win32com.client.Dispatch("Schedule.Service") @@ -444,8 +458,8 @@ def list_triggers(name, location='\\'): return ret -def list_actions(name, location='\\'): - r''' +def list_actions(name, location="\\"): + r""" List all actions that pertain to a task in the specified location. Args: @@ -471,7 +485,7 @@ def list_actions(name, location='\\'): # List all actions for the XblGameSaveTask in the Microsoft\XblGameSave # location salt 'minion-id' task.list_actions XblGameSaveTask Microsoft\XblGameSave - ''' + """ # Create the task service object with salt.utils.winapi.Com(): task_service = win32com.client.Dispatch("Schedule.Service") @@ -489,13 +503,10 @@ def list_actions(name, location='\\'): return ret -def create_task(name, - location='\\', - user_name='System', - password=None, - force=False, - **kwargs): - r''' +def create_task( + name, location="\\", user_name="System", password=None, force=False, **kwargs +): + r""" Create a new task in the designated location. This function has many keyword arguments that are not listed here. For additional arguments see: @@ -533,11 +544,11 @@ def create_task(name, .. code-block:: bash salt 'minion-id' task.create_task <task_name> user_name=System force=True action_type=Execute cmd='del /Q /S C:\\Temp' trigger_type=Once start_date=2016-12-1 start_time=01:00 - ''' + """ # Check for existing task if name in list_tasks(location) and not force: # Connect to an existing task definition - return '{0} already exists'.format(name) + return "{0} already exists".format(name) # connect to the task scheduler with salt.utils.winapi.Com(): @@ -548,10 +559,12 @@ def create_task(name, task_definition = task_service.NewTask(0) # Modify task settings - edit_task(task_definition=task_definition, - user_name=user_name, - password=password, - **kwargs) + edit_task( + task_definition=task_definition, + user_name=user_name, + password=password, + **kwargs + ) # Add Action add_action(task_definition=task_definition, **kwargs) @@ -563,24 +576,23 @@ def create_task(name, task_folder = task_service.GetFolder(location) # Save the task - _save_task_definition(name=name, - task_folder=task_folder, - task_definition=task_definition, - user_name=task_definition.Principal.UserID, - password=password, - logon_type=task_definition.Principal.LogonType) + _save_task_definition( + name=name, + task_folder=task_folder, + task_definition=task_definition, + user_name=task_definition.Principal.UserID, + password=password, + logon_type=task_definition.Principal.LogonType, + ) # Verify task was created return name in list_tasks(location) -def create_task_from_xml(name, - location='\\', - xml_text=None, - xml_path=None, - user_name='System', - password=None): - r''' +def create_task_from_xml( + name, location="\\", xml_text=None, xml_path=None, user_name="System", password=None +): + r""" Create a task based on XML. Source can be a file or a string of XML. Args: @@ -618,14 +630,14 @@ def create_task_from_xml(name, .. code-block:: bash salt '*' task.create_task_from_xml <task_name> xml_path=C:\task.xml - ''' + """ # Check for existing task if name in list_tasks(location): # Connect to an existing task definition - return '{0} already exists'.format(name) + return "{0} already exists".format(name) if not xml_text and not xml_path: - return 'Must specify either xml_text or xml_path' + return "Must specify either xml_text or xml_path" # Create the task service object with salt.utils.winapi.Com(): @@ -642,9 +654,9 @@ def create_task_from_xml(name, # Determine logon type if user_name: - if user_name.lower() == 'system': + if user_name.lower() == "system": logon_type = TASK_LOGON_SERVICE_ACCOUNT - user_name = 'SYSTEM' + user_name = "SYSTEM" password = None else: if password: @@ -656,31 +668,30 @@ def create_task_from_xml(name, # Save the task try: - task_folder.RegisterTask(name, - xml_text, - TASK_CREATE, - user_name, - password, - logon_type) + task_folder.RegisterTask( + name, xml_text, TASK_CREATE, user_name, password, logon_type + ) except pythoncom.com_error as error: hr, msg, exc, arg = error.args # pylint: disable=W0633 - fc = {-2147216615: 'Required element or attribute missing', - -2147216616: 'Value incorrectly formatted or out of range', - -2147352571: 'Access denied'} + fc = { + -2147216615: "Required element or attribute missing", + -2147216616: "Value incorrectly formatted or out of range", + -2147352571: "Access denied", + } try: failure_code = fc[exc[5]] except KeyError: - failure_code = 'Unknown Failure: {0}'.format(error) + failure_code = "Unknown Failure: {0}".format(error) - log.debug('Failed to create task: %s', failure_code) + log.debug("Failed to create task: %s", failure_code) # Verify creation return name in list_tasks(location) -def create_folder(name, location='\\'): - r''' +def create_folder(name, location="\\"): + r""" Create a folder in which to create tasks. Args: @@ -702,11 +713,11 @@ def create_folder(name, location='\\'): .. code-block:: bash salt 'minion-id' task.create_folder <folder_name> - ''' + """ # Check for existing folder if name in list_folders(location): # Connect to an existing task definition - return '{0} already exists'.format(name) + return "{0} already exists".format(name) # Create the task service object with salt.utils.winapi.Com(): @@ -721,37 +732,39 @@ def create_folder(name, location='\\'): return name in list_folders(location) -def edit_task(name=None, - location='\\', - # General Tab - user_name=None, - password=None, - description=None, - enabled=None, - hidden=None, - # Conditions Tab - run_if_idle=None, - idle_duration=None, - idle_wait_timeout=None, - idle_stop_on_end=None, - idle_restart=None, - ac_only=None, - stop_if_on_batteries=None, - wake_to_run=None, - run_if_network=None, - network_id=None, - network_name=None, - # Settings Tab - allow_demand_start=None, - start_when_available=None, - restart_every=None, - restart_count=3, - execution_time_limit=None, - force_stop=None, - delete_after=None, - multiple_instances=None, - **kwargs): - r''' +def edit_task( + name=None, + location="\\", + # General Tab + user_name=None, + password=None, + description=None, + enabled=None, + hidden=None, + # Conditions Tab + run_if_idle=None, + idle_duration=None, + idle_wait_timeout=None, + idle_stop_on_end=None, + idle_restart=None, + ac_only=None, + stop_if_on_batteries=None, + wake_to_run=None, + run_if_network=None, + network_id=None, + network_name=None, + # Settings Tab + allow_demand_start=None, + start_when_available=None, + restart_every=None, + restart_count=3, + execution_time_limit=None, + force_stop=None, + delete_after=None, + multiple_instances=None, + **kwargs +): + r""" Edit the parameters of a task. Triggers and Actions cannot be edited yet. Args: @@ -922,14 +935,14 @@ def edit_task(name=None, .. code-block:: bash salt '*' task.edit_task <task_name> description='This task is awesome' - ''' + """ # TODO: Add more detailed return for items changed # Check for passed task_definition # If not passed, open a task definition for an existing task save_definition = False - if kwargs.get('task_definition', False): - task_definition = kwargs.get('task_definition') + if kwargs.get("task_definition", False): + task_definition = kwargs.get("task_definition") else: save_definition = True @@ -953,11 +966,11 @@ def edit_task(name=None, else: # Not found and create_new not set, return not found - return '{0} not found'.format(name) + return "{0} not found".format(name) # General Information if save_definition: - task_definition.RegistrationInfo.Author = 'Salt Minion' + task_definition.RegistrationInfo.Author = "Salt Minion" task_definition.RegistrationInfo.Source = "Salt Minion Daemon" if description is not None: @@ -966,9 +979,9 @@ def edit_task(name=None, # General Information: Security Options if user_name: # Determine logon type - if user_name.lower() == 'system': + if user_name.lower() == "system": logon_type = TASK_LOGON_SERVICE_ACCOUNT - user_name = 'SYSTEM' + user_name = "SYSTEM" password = None else: task_definition.Principal.Id = user_name @@ -1005,12 +1018,16 @@ def edit_task(name=None, task_definition.Settings.IdleSettings.RestartOnIdle = idle_restart if idle_duration is not None: if idle_duration in duration: - task_definition.Settings.IdleSettings.IdleDuration = _lookup_first(duration, idle_duration) + task_definition.Settings.IdleSettings.IdleDuration = _lookup_first( + duration, idle_duration + ) else: return 'Invalid value for "idle_duration"' if idle_wait_timeout is not None: if idle_wait_timeout in duration: - task_definition.Settings.IdleSettings.WaitTimeout = _lookup_first(duration, idle_wait_timeout) + task_definition.Settings.IdleSettings.WaitTimeout = _lookup_first( + duration, idle_wait_timeout + ) else: return 'Invalid value for "idle_wait_timeout"' @@ -1039,11 +1056,12 @@ def edit_task(name=None, task_definition.Settings.StartWhenAvailable = start_when_available if restart_every is not None: if restart_every is False: - task_definition.Settings.RestartInterval = '' + task_definition.Settings.RestartInterval = "" else: if restart_every in duration: task_definition.Settings.RestartInterval = _lookup_first( - duration, restart_every) + duration, restart_every + ) else: return 'Invalid value for "restart_every"' if task_definition.Settings.RestartInterval: @@ -1054,11 +1072,12 @@ def edit_task(name=None, return '"restart_count" must be a value between 1 and 999' if execution_time_limit is not None: if execution_time_limit is False: - task_definition.Settings.ExecutionTimeLimit = 'PT0S' + task_definition.Settings.ExecutionTimeLimit = "PT0S" else: if execution_time_limit in duration: task_definition.Settings.ExecutionTimeLimit = _lookup_first( - duration, execution_time_limit) + duration, execution_time_limit + ) else: return 'Invalid value for "execution_time_limit"' if force_stop is not None: @@ -1066,10 +1085,11 @@ def edit_task(name=None, if delete_after is not None: # TODO: Check triggers for end_boundary if delete_after is False: - task_definition.Settings.DeleteExpiredTaskAfter = '' + task_definition.Settings.DeleteExpiredTaskAfter = "" if delete_after in duration: task_definition.Settings.DeleteExpiredTaskAfter = _lookup_first( - duration, delete_after) + duration, delete_after + ) else: return 'Invalid value for "delete_after"' if multiple_instances is not None: @@ -1078,16 +1098,18 @@ def edit_task(name=None, # Save the task if save_definition: # Save the Changes - return _save_task_definition(name=name, - task_folder=task_folder, - task_definition=task_definition, - user_name=user_name, - password=password, - logon_type=task_definition.Principal.LogonType) + return _save_task_definition( + name=name, + task_folder=task_folder, + task_definition=task_definition, + user_name=user_name, + password=password, + logon_type=task_definition.Principal.LogonType, + ) -def delete_task(name, location='\\'): - r''' +def delete_task(name, location="\\"): + r""" Delete a task from the task scheduler. Args: @@ -1107,10 +1129,10 @@ def delete_task(name, location='\\'): .. code-block:: bash salt 'minion-id' task.delete_task <task_name> - ''' + """ # Check for existing task if name not in list_tasks(location): - return '{0} not found in {1}'.format(name, location) + return "{0} not found in {1}".format(name, location) # connect to the task scheduler with salt.utils.winapi.Com(): @@ -1126,8 +1148,8 @@ def delete_task(name, location='\\'): return name not in list_tasks(location) -def delete_folder(name, location='\\'): - r''' +def delete_folder(name, location="\\"): + r""" Delete a folder from the task scheduler. Args: @@ -1148,10 +1170,10 @@ def delete_folder(name, location='\\'): .. code-block:: bash salt 'minion-id' task.delete_folder <folder_name> - ''' + """ # Check for existing folder if name not in list_folders(location): - return '{0} not found in {1}'.format(name, location) + return "{0} not found in {1}".format(name, location) # connect to the task scheduler with salt.utils.winapi.Com(): @@ -1171,8 +1193,8 @@ def delete_folder(name, location='\\'): return False -def run(name, location='\\'): - r''' +def run(name, location="\\"): + r""" Run a scheduled task manually. Args: @@ -1193,10 +1215,10 @@ def run(name, location='\\'): .. code-block:: bash salt 'minion-id' task.list_run <task_name> - ''' + """ # Check for existing folder if name not in list_tasks(location): - return '{0} not found in {1}'.format(name, location) + return "{0} not found in {1}".format(name, location) # connect to the task scheduler with salt.utils.winapi.Com(): @@ -1208,14 +1230,14 @@ def run(name, location='\\'): task = task_folder.GetTask(name) try: - task.Run('') + task.Run("") return True except pythoncom.com_error: return False -def run_wait(name, location='\\'): - r''' +def run_wait(name, location="\\"): + r""" Run a scheduled task and return when the task finishes Args: @@ -1236,10 +1258,10 @@ def run_wait(name, location='\\'): .. code-block:: bash salt 'minion-id' task.list_run_wait <task_name> - ''' + """ # Check for existing folder if name not in list_tasks(location): - return '{0} not found in {1}'.format(name, location) + return "{0} not found in {1}".format(name, location) # connect to the task scheduler with salt.utils.winapi.Com(): @@ -1252,10 +1274,10 @@ def run_wait(name, location='\\'): # Is the task already running if task.State == TASK_STATE_RUNNING: - return 'Task already running' + return "Task already running" try: - task.Run('') + task.Run("") time.sleep(1) running = True except pythoncom.com_error: @@ -1275,8 +1297,8 @@ def run_wait(name, location='\\'): return True -def stop(name, location='\\'): - r''' +def stop(name, location="\\"): + r""" Stop a scheduled task. Args: @@ -1297,10 +1319,10 @@ def stop(name, location='\\'): .. code-block:: bash salt 'minion-id' task.list_stop <task_name> - ''' + """ # Check for existing folder if name not in list_tasks(location): - return '{0} not found in {1}'.format(name, location) + return "{0} not found in {1}".format(name, location) # connect to the task scheduler with salt.utils.winapi.Com(): @@ -1318,8 +1340,8 @@ def stop(name, location='\\'): return False -def status(name, location='\\'): - r''' +def status(name, location="\\"): + r""" Determine the status of a task. Is it Running, Queued, Ready, etc. Args: @@ -1346,10 +1368,10 @@ def status(name, location='\\'): .. code-block:: bash salt 'minion-id' task.list_status <task_name> - ''' + """ # Check for existing folder if name not in list_tasks(location): - return '{0} not found in {1}'.format(name, location) + return "{0} not found in {1}".format(name, location) # connect to the task scheduler with salt.utils.winapi.Com(): @@ -1363,8 +1385,8 @@ def status(name, location='\\'): return states[task.State] -def info(name, location='\\'): - r''' +def info(name, location="\\"): + r""" Get the details about a task in the task scheduler. Args: @@ -1385,10 +1407,10 @@ def info(name, location='\\'): .. code-block:: bash salt 'minion-id' task.info <task_name> - ''' + """ # Check for existing folder if name not in list_tasks(location): - return '{0} not found in {1}'.format(name, location) + return "{0} not found in {1}".format(name, location) # connect to the task scheduler with salt.utils.winapi.Com(): @@ -1399,119 +1421,125 @@ def info(name, location='\\'): task_folder = task_service.GetFolder(location) task = task_folder.GetTask(name) - properties = {'enabled': task.Enabled, - 'last_run': _get_date_value(task.LastRunTime), - 'last_run_result': results[task.LastTaskResult], - 'missed_runs': task.NumberOfMissedRuns, - 'next_run': _get_date_value(task.NextRunTime), - 'status': states[task.State]} + properties = { + "enabled": task.Enabled, + "last_run": _get_date_value(task.LastRunTime), + "last_run_result": results[task.LastTaskResult], + "missed_runs": task.NumberOfMissedRuns, + "next_run": _get_date_value(task.NextRunTime), + "status": states[task.State], + } def_set = task.Definition.Settings settings = { - 'allow_demand_start': def_set.AllowDemandStart, - 'force_stop': def_set.AllowHardTerminate} + "allow_demand_start": def_set.AllowDemandStart, + "force_stop": def_set.AllowHardTerminate, + } - if def_set.DeleteExpiredTaskAfter == '': - settings['delete_after'] = False - elif def_set.DeleteExpiredTaskAfter == 'PT0S': - settings['delete_after'] = 'Immediately' + if def_set.DeleteExpiredTaskAfter == "": + settings["delete_after"] = False + elif def_set.DeleteExpiredTaskAfter == "PT0S": + settings["delete_after"] = "Immediately" else: - settings['delete_after'] = _reverse_lookup( - duration, def_set.DeleteExpiredTaskAfter) + settings["delete_after"] = _reverse_lookup( + duration, def_set.DeleteExpiredTaskAfter + ) - if def_set.ExecutionTimeLimit == '': - settings['execution_time_limit'] = False + if def_set.ExecutionTimeLimit == "": + settings["execution_time_limit"] = False else: - settings['execution_time_limit'] = _reverse_lookup( - duration, def_set.ExecutionTimeLimit) + settings["execution_time_limit"] = _reverse_lookup( + duration, def_set.ExecutionTimeLimit + ) - settings['multiple_instances'] = _reverse_lookup( - instances, def_set.MultipleInstances) + settings["multiple_instances"] = _reverse_lookup( + instances, def_set.MultipleInstances + ) - if def_set.RestartInterval == '': - settings['restart_interval'] = False + if def_set.RestartInterval == "": + settings["restart_interval"] = False else: - settings['restart_interval'] = _reverse_lookup( - duration, def_set.RestartInterval) + settings["restart_interval"] = _reverse_lookup( + duration, def_set.RestartInterval + ) - if settings['restart_interval']: - settings['restart_count'] = def_set.RestartCount - settings['stop_if_on_batteries'] = def_set.StopIfGoingOnBatteries - settings['wake_to_run'] = def_set.WakeToRun + if settings["restart_interval"]: + settings["restart_count"] = def_set.RestartCount + settings["stop_if_on_batteries"] = def_set.StopIfGoingOnBatteries + settings["wake_to_run"] = def_set.WakeToRun conditions = { - 'ac_only': def_set.DisallowStartIfOnBatteries, - 'run_if_idle': def_set.RunOnlyIfIdle, - 'run_if_network': def_set.RunOnlyIfNetworkAvailable, - 'start_when_available': def_set.StartWhenAvailable} + "ac_only": def_set.DisallowStartIfOnBatteries, + "run_if_idle": def_set.RunOnlyIfIdle, + "run_if_network": def_set.RunOnlyIfNetworkAvailable, + "start_when_available": def_set.StartWhenAvailable, + } - if conditions['run_if_idle']: + if conditions["run_if_idle"]: idle_set = def_set.IdleSettings - conditions['idle_duration'] = idle_set.IdleDuration - conditions['idle_restart'] = idle_set.RestartOnIdle - conditions['idle_stop_on_end'] = idle_set.StopOnIdleEnd - conditions['idle_wait_timeout'] = idle_set.WaitTimeout + conditions["idle_duration"] = idle_set.IdleDuration + conditions["idle_restart"] = idle_set.RestartOnIdle + conditions["idle_stop_on_end"] = idle_set.StopOnIdleEnd + conditions["idle_wait_timeout"] = idle_set.WaitTimeout - if conditions['run_if_network']: + if conditions["run_if_network"]: net_set = def_set.NetworkSettings - conditions['network_id'] = net_set.Id - conditions['network_name'] = net_set.Name + conditions["network_id"] = net_set.Id + conditions["network_name"] = net_set.Name actions = [] for actionObj in task.Definition.Actions: - action = {'action_type': _reverse_lookup(action_types, actionObj.Type)} + action = {"action_type": _reverse_lookup(action_types, actionObj.Type)} if actionObj.Path: - action['cmd'] = actionObj.Path + action["cmd"] = actionObj.Path if actionObj.Arguments: - action['arguments'] = actionObj.Arguments + action["arguments"] = actionObj.Arguments if actionObj.WorkingDirectory: - action['working_dir'] = actionObj.WorkingDirectory + action["working_dir"] = actionObj.WorkingDirectory actions.append(action) triggers = [] for triggerObj in task.Definition.Triggers: - trigger = { - 'trigger_type': _reverse_lookup(trigger_types, triggerObj.Type)} + trigger = {"trigger_type": _reverse_lookup(trigger_types, triggerObj.Type)} if triggerObj.ExecutionTimeLimit: - trigger['execution_time_limit'] = _reverse_lookup( - duration, triggerObj.ExecutionTimeLimit) + trigger["execution_time_limit"] = _reverse_lookup( + duration, triggerObj.ExecutionTimeLimit + ) if triggerObj.StartBoundary: - start_date, start_time = triggerObj.StartBoundary.split('T', 1) - trigger['start_date'] = start_date - trigger['start_time'] = start_time + start_date, start_time = triggerObj.StartBoundary.split("T", 1) + trigger["start_date"] = start_date + trigger["start_time"] = start_time if triggerObj.EndBoundary: - end_date, end_time = triggerObj.EndBoundary.split('T', 1) - trigger['end_date'] = end_date - trigger['end_time'] = end_time - trigger['enabled'] = triggerObj.Enabled - if hasattr(triggerObj, 'RandomDelay'): + end_date, end_time = triggerObj.EndBoundary.split("T", 1) + trigger["end_date"] = end_date + trigger["end_time"] = end_time + trigger["enabled"] = triggerObj.Enabled + if hasattr(triggerObj, "RandomDelay"): if triggerObj.RandomDelay: - trigger['random_delay'] = _reverse_lookup( - duration, triggerObj.RandomDelay) + trigger["random_delay"] = _reverse_lookup( + duration, triggerObj.RandomDelay + ) else: - trigger['random_delay'] = False - if hasattr(triggerObj, 'Delay'): + trigger["random_delay"] = False + if hasattr(triggerObj, "Delay"): if triggerObj.Delay: - trigger['delay'] = _reverse_lookup(duration, triggerObj.Delay) + trigger["delay"] = _reverse_lookup(duration, triggerObj.Delay) else: - trigger['delay'] = False + trigger["delay"] = False triggers.append(trigger) - properties['settings'] = settings - properties['conditions'] = conditions - properties['actions'] = actions - properties['triggers'] = triggers + properties["settings"] = settings + properties["conditions"] = conditions + properties["actions"] = actions + properties["triggers"] = triggers ret = properties return ret -def add_action(name=None, - location='\\', - action_type='Execute', - **kwargs): - r''' +def add_action(name=None, location="\\", action_type="Execute", **kwargs): + r""" Add an action to a task. Args: @@ -1602,10 +1630,10 @@ def add_action(name=None, .. code-block:: bash salt 'minion-id' task.add_action <task_name> cmd='del /Q /S C:\\Temp' - ''' + """ save_definition = False - if kwargs.get('task_definition', False): - task_definition = kwargs.get('task_definition') + if kwargs.get("task_definition", False): + task_definition = kwargs.get("task_definition") else: save_definition = True # Make sure a name was passed @@ -1628,79 +1656,81 @@ def add_action(name=None, else: # Not found and create_new not set, return not found - return '{0} not found'.format(name) + return "{0} not found".format(name) # Action Settings task_action = task_definition.Actions.Create(action_types[action_type]) if action_types[action_type] == TASK_ACTION_EXEC: - task_action.Id = 'Execute_ID1' - if kwargs.get('cmd', False): - task_action.Path = kwargs.get('cmd') + task_action.Id = "Execute_ID1" + if kwargs.get("cmd", False): + task_action.Path = kwargs.get("cmd") else: return 'Required parameter "cmd" not found' - task_action.Arguments = kwargs.get('arguments', '') - task_action.WorkingDirectory = kwargs.get('start_in', '') + task_action.Arguments = kwargs.get("arguments", "") + task_action.WorkingDirectory = kwargs.get("start_in", "") elif action_types[action_type] == TASK_ACTION_SEND_EMAIL: - task_action.Id = 'Email_ID1' + task_action.Id = "Email_ID1" # Required Parameters - if kwargs.get('server', False): - task_action.Server = kwargs.get('server') + if kwargs.get("server", False): + task_action.Server = kwargs.get("server") else: return 'Required parameter "server" not found' - if kwargs.get('from', False): - task_action.From = kwargs.get('from') + if kwargs.get("from", False): + task_action.From = kwargs.get("from") else: return 'Required parameter "from" not found' - if kwargs.get('to', False) or kwargs.get('cc', False): - if kwargs.get('to'): - task_action.To = kwargs.get('to') - if kwargs.get('cc'): - task_action.Cc = kwargs.get('cc') + if kwargs.get("to", False) or kwargs.get("cc", False): + if kwargs.get("to"): + task_action.To = kwargs.get("to") + if kwargs.get("cc"): + task_action.Cc = kwargs.get("cc") else: return 'Required parameter "to" or "cc" not found' # Optional Parameters - if kwargs.get('reply_to'): - task_action.ReplyTo = kwargs.get('reply_to') - if kwargs.get('bcc'): - task_action.Bcc = kwargs.get('bcc') - if kwargs.get('subject'): - task_action.Subject = kwargs.get('subject') - if kwargs.get('body'): - task_action.Body = kwargs.get('body') - if kwargs.get('attachments'): - task_action.Attachments = kwargs.get('attachments') + if kwargs.get("reply_to"): + task_action.ReplyTo = kwargs.get("reply_to") + if kwargs.get("bcc"): + task_action.Bcc = kwargs.get("bcc") + if kwargs.get("subject"): + task_action.Subject = kwargs.get("subject") + if kwargs.get("body"): + task_action.Body = kwargs.get("body") + if kwargs.get("attachments"): + task_action.Attachments = kwargs.get("attachments") elif action_types[action_type] == TASK_ACTION_SHOW_MESSAGE: - task_action.Id = 'Message_ID1' + task_action.Id = "Message_ID1" - if kwargs.get('title', False): - task_action.Title = kwargs.get('title') + if kwargs.get("title", False): + task_action.Title = kwargs.get("title") else: return 'Required parameter "title" not found' - if kwargs.get('message', False): - task_action.MessageBody = kwargs.get('message') + if kwargs.get("message", False): + task_action.MessageBody = kwargs.get("message") else: return 'Required parameter "message" not found' # Save the task if save_definition: # Save the Changes - return _save_task_definition(name=name, - task_folder=task_folder, - task_definition=task_definition, - user_name=task_definition.Principal.UserID, - password=None, - logon_type=task_definition.Principal.LogonType) + return _save_task_definition( + name=name, + task_folder=task_folder, + task_definition=task_definition, + user_name=task_definition.Principal.UserID, + password=None, + logon_type=task_definition.Principal.LogonType, + ) -def _clear_actions(name, location='\\'): - r''' +def _clear_actions(name, location="\\"): + r""" Remove all actions from the task. :param str name: The name of the task from which to clear all actions. @@ -1711,7 +1741,7 @@ def _clear_actions(name, location='\\'): :return: True if successful, False if unsuccessful :rtype: bool - ''' + """ # TODO: The problem is, you have to have at least one action for the task to # TODO: be valid, so this will always fail with a 'Required element or # TODO: attribute missing' error. @@ -1720,7 +1750,7 @@ def _clear_actions(name, location='\\'): # TODO: action. # Check for existing task if name not in list_tasks(location): - return '{0} not found in {1}'.format(name, location) + return "{0} not found in {1}".format(name, location) # Create the task service object with salt.utils.winapi.Com(): @@ -1735,30 +1765,34 @@ def _clear_actions(name, location='\\'): actions.Clear() # Save the Changes - return _save_task_definition(name=name, - task_folder=task_folder, - task_definition=task_definition, - user_name=task_definition.Principal.UserID, - password=None, - logon_type=task_definition.Principal.LogonType) + return _save_task_definition( + name=name, + task_folder=task_folder, + task_definition=task_definition, + user_name=task_definition.Principal.UserID, + password=None, + logon_type=task_definition.Principal.LogonType, + ) -def add_trigger(name=None, - location='\\', - trigger_type=None, - trigger_enabled=True, - start_date=None, - start_time=None, - end_date=None, - end_time=None, - random_delay=None, - repeat_interval=None, - repeat_duration=None, - repeat_stop_at_duration_end=False, - execution_time_limit=None, - delay=None, - **kwargs): - r''' +def add_trigger( + name=None, + location="\\", + trigger_type=None, + trigger_enabled=True, + start_date=None, + start_time=None, + end_date=None, + end_time=None, + random_delay=None, + repeat_interval=None, + repeat_duration=None, + repeat_stop_at_duration_end=False, + execution_time_limit=None, + delay=None, + **kwargs +): + r""" Add a trigger to a Windows Scheduled task .. note:: @@ -2064,76 +2098,81 @@ def add_trigger(name=None, .. code-block:: bash salt 'minion-id' task.add_trigger <task_name> trigger_type=Once trigger_enabled=True start_date=2016/12/1 start_time='"12:01"' - ''' + """ if not trigger_type: return 'Required parameter "trigger_type" not specified' # Define lookup dictionaries - state_changes = {'ConsoleConnect': 1, - 'ConsoleDisconnect': 2, - 'RemoteConnect': 3, - 'RemoteDisconnect': 4, - 'SessionLock': 7, - 'SessionUnlock': 8} + state_changes = { + "ConsoleConnect": 1, + "ConsoleDisconnect": 2, + "RemoteConnect": 3, + "RemoteDisconnect": 4, + "SessionLock": 7, + "SessionUnlock": 8, + } - days = {1: 0x1, - 2: 0x2, - 3: 0x4, - 4: 0x8, - 5: 0x10, - 6: 0x20, - 7: 0x40, - 8: 0x80, - 9: 0x100, - 10: 0x200, - 11: 0x400, - 12: 0x800, - 13: 0x1000, - 14: 0x2000, - 15: 0x4000, - 16: 0x8000, - 17: 0x10000, - 18: 0x20000, - 19: 0x40000, - 20: 0x80000, - 21: 0x100000, - 22: 0x200000, - 23: 0x400000, - 24: 0x800000, - 25: 0x1000000, - 26: 0x2000000, - 27: 0x4000000, - 28: 0x8000000, - 29: 0x10000000, - 30: 0x20000000, - 31: 0x40000000, - 'Last': 0x80000000} + days = { + 1: 0x1, + 2: 0x2, + 3: 0x4, + 4: 0x8, + 5: 0x10, + 6: 0x20, + 7: 0x40, + 8: 0x80, + 9: 0x100, + 10: 0x200, + 11: 0x400, + 12: 0x800, + 13: 0x1000, + 14: 0x2000, + 15: 0x4000, + 16: 0x8000, + 17: 0x10000, + 18: 0x20000, + 19: 0x40000, + 20: 0x80000, + 21: 0x100000, + 22: 0x200000, + 23: 0x400000, + 24: 0x800000, + 25: 0x1000000, + 26: 0x2000000, + 27: 0x4000000, + 28: 0x8000000, + 29: 0x10000000, + 30: 0x20000000, + 31: 0x40000000, + "Last": 0x80000000, + } - weekdays = {'Sunday': 0x1, - 'Monday': 0x2, - 'Tuesday': 0x4, - 'Wednesday': 0x8, - 'Thursday': 0x10, - 'Friday': 0x20, - 'Saturday': 0x40} + weekdays = { + "Sunday": 0x1, + "Monday": 0x2, + "Tuesday": 0x4, + "Wednesday": 0x8, + "Thursday": 0x10, + "Friday": 0x20, + "Saturday": 0x40, + } - weeks = {'First': 0x1, - 'Second': 0x2, - 'Third': 0x4, - 'Fourth': 0x8} + weeks = {"First": 0x1, "Second": 0x2, "Third": 0x4, "Fourth": 0x8} - months = {'January': 0x1, - 'February': 0x2, - 'March': 0x4, - 'April': 0x8, - 'May': 0x10, - 'June': 0x20, - 'July': 0x40, - 'August': 0x80, - 'September': 0x100, - 'October': 0x200, - 'November': 0x400, - 'December': 0x800} + months = { + "January": 0x1, + "February": 0x2, + "March": 0x4, + "April": 0x8, + "May": 0x10, + "June": 0x20, + "July": 0x40, + "August": 0x80, + "September": 0x100, + "October": 0x200, + "November": 0x400, + "December": 0x800, + } # Format Date Parameters if start_date: @@ -2141,7 +2180,7 @@ def add_trigger(name=None, if date_format: dt_obj = datetime.strptime(start_date, date_format) else: - return 'Invalid start_date' + return "Invalid start_date" else: dt_obj = datetime.now() @@ -2150,12 +2189,13 @@ def add_trigger(name=None, if time_format: tm_obj = datetime.strptime(start_time, time_format) else: - return 'Invalid start_time' + return "Invalid start_time" else: - tm_obj = datetime.strptime('00:00:00', '%H:%M:%S') + tm_obj = datetime.strptime("00:00:00", "%H:%M:%S") - start_boundary = '{0}T{1}'.format(dt_obj.strftime('%Y-%m-%d'), - tm_obj.strftime('%H:%M:%S')) + start_boundary = "{0}T{1}".format( + dt_obj.strftime("%Y-%m-%d"), tm_obj.strftime("%H:%M:%S") + ) dt_obj = None if end_date: @@ -2163,25 +2203,26 @@ def add_trigger(name=None, if date_format: dt_obj = datetime.strptime(end_date, date_format) else: - return 'Invalid end_date' + return "Invalid end_date" if end_time: time_format = _get_date_time_format(end_time) if time_format: tm_obj = datetime.strptime(end_time, time_format) else: - return 'Invalid end_time' + return "Invalid end_time" else: - tm_obj = datetime.strptime('00:00:00', '%H:%M:%S') + tm_obj = datetime.strptime("00:00:00", "%H:%M:%S") end_boundary = None if dt_obj and tm_obj: - end_boundary = '{0}T{1}'.format(dt_obj.strftime('%Y-%m-%d'), - tm_obj.strftime('%H:%M:%S')) + end_boundary = "{0}T{1}".format( + dt_obj.strftime("%Y-%m-%d"), tm_obj.strftime("%H:%M:%S") + ) save_definition = False - if kwargs.get('task_definition', False): - task_definition = kwargs.get('task_definition') + if kwargs.get("task_definition", False): + task_definition = kwargs.get("task_definition") else: save_definition = True # Make sure a name was passed @@ -2204,7 +2245,7 @@ def add_trigger(name=None, else: # Not found and create_new not set, return not found - return '{0} not found'.format(name) + return "{0} not found".format(name) # Create a New Trigger trigger = task_definition.Triggers.Create(trigger_types[trigger_type]) @@ -2220,12 +2261,10 @@ def add_trigger(name=None, if repeat_interval: trigger.Repetition.Interval = _lookup_first(duration, repeat_interval) if repeat_duration: - trigger.Repetition.Duration = _lookup_first(duration, - repeat_duration) + trigger.Repetition.Duration = _lookup_first(duration, repeat_duration) trigger.Repetition.StopAtDurationEnd = repeat_stop_at_duration_end if execution_time_limit: - trigger.ExecutionTimeLimit = _lookup_first(duration, - execution_time_limit) + trigger.ExecutionTimeLimit = _lookup_first(duration, execution_time_limit) if end_boundary: trigger.EndBoundary = end_boundary trigger.Enabled = trigger_enabled @@ -2234,27 +2273,27 @@ def add_trigger(name=None, # Event Trigger Parameters if trigger_types[trigger_type] == TASK_TRIGGER_EVENT: # Check for required kwargs - if kwargs.get('subscription', False): - trigger.Id = 'Event_ID1' - trigger.Subscription = kwargs.get('subscription') + if kwargs.get("subscription", False): + trigger.Id = "Event_ID1" + trigger.Subscription = kwargs.get("subscription") else: return 'Required parameter "subscription" not passed' elif trigger_types[trigger_type] == TASK_TRIGGER_TIME: - trigger.Id = 'Once_ID1' + trigger.Id = "Once_ID1" # Daily Trigger Parameters elif trigger_types[trigger_type] == TASK_TRIGGER_DAILY: - trigger.Id = 'Daily_ID1' - trigger.DaysInterval = kwargs.get('days_interval', 1) + trigger.Id = "Daily_ID1" + trigger.DaysInterval = kwargs.get("days_interval", 1) # Weekly Trigger Parameters elif trigger_types[trigger_type] == TASK_TRIGGER_WEEKLY: - trigger.Id = 'Weekly_ID1' - trigger.WeeksInterval = kwargs.get('weeks_interval', 1) - if kwargs.get('days_of_week', False): + trigger.Id = "Weekly_ID1" + trigger.WeeksInterval = kwargs.get("weeks_interval", 1) + if kwargs.get("days_of_week", False): bits_days = 0 - for weekday in kwargs.get('days_of_week'): + for weekday in kwargs.get("days_of_week"): bits_days |= weekdays[weekday] trigger.DaysOfWeek = bits_days else: @@ -2262,54 +2301,57 @@ def add_trigger(name=None, # Monthly Trigger Parameters elif trigger_types[trigger_type] == TASK_TRIGGER_MONTHLY: - trigger.Id = 'Monthly_ID1' - if kwargs.get('months_of_year', False): + trigger.Id = "Monthly_ID1" + if kwargs.get("months_of_year", False): bits_months = 0 - for month in kwargs.get('months_of_year'): + for month in kwargs.get("months_of_year"): bits_months |= months[month] trigger.MonthsOfYear = bits_months else: return 'Required parameter "months_of_year" not passed' - if kwargs.get('days_of_month', False) or \ - kwargs.get('last_day_of_month', False): - if kwargs.get('days_of_month', False): + if kwargs.get("days_of_month", False) or kwargs.get("last_day_of_month", False): + if kwargs.get("days_of_month", False): bits_days = 0 - for day in kwargs.get('days_of_month'): + for day in kwargs.get("days_of_month"): bits_days |= days[day] trigger.DaysOfMonth = bits_days - trigger.RunOnLastDayOfMonth = kwargs.get('last_day_of_month', False) + trigger.RunOnLastDayOfMonth = kwargs.get("last_day_of_month", False) else: - return 'Monthly trigger requires "days_of_month" or "last_day_of_' \ - 'month" parameters' + return ( + 'Monthly trigger requires "days_of_month" or "last_day_of_' + 'month" parameters' + ) # Monthly Day Of Week Trigger Parameters elif trigger_types[trigger_type] == TASK_TRIGGER_MONTHLYDOW: - trigger.Id = 'Monthly_DOW_ID1' - if kwargs.get('months_of_year', False): + trigger.Id = "Monthly_DOW_ID1" + if kwargs.get("months_of_year", False): bits_months = 0 - for month in kwargs.get('months_of_year'): + for month in kwargs.get("months_of_year"): bits_months |= months[month] trigger.MonthsOfYear = bits_months else: return 'Required parameter "months_of_year" not passed' - if kwargs.get('weeks_of_month', False) or \ - kwargs.get('last_week_of_month', False): - if kwargs.get('weeks_of_month', False): + if kwargs.get("weeks_of_month", False) or kwargs.get( + "last_week_of_month", False + ): + if kwargs.get("weeks_of_month", False): bits_weeks = 0 - for week in kwargs.get('weeks_of_month'): + for week in kwargs.get("weeks_of_month"): bits_weeks |= weeks[week] trigger.WeeksOfMonth = bits_weeks - trigger.RunOnLastWeekOfMonth = kwargs.get('last_week_of_month', - False) + trigger.RunOnLastWeekOfMonth = kwargs.get("last_week_of_month", False) else: - return 'Monthly DOW trigger requires "weeks_of_month" or "last_' \ - 'week_of_month" parameters' + return ( + 'Monthly DOW trigger requires "weeks_of_month" or "last_' + 'week_of_month" parameters' + ) - if kwargs.get('days_of_week', False): + if kwargs.get("days_of_week", False): bits_days = 0 - for weekday in kwargs.get('days_of_week'): + for weekday in kwargs.get("days_of_week"): bits_days |= weekdays[weekday] trigger.DaysOfWeek = bits_days else: @@ -2317,43 +2359,45 @@ def add_trigger(name=None, # On Idle Trigger Parameters elif trigger_types[trigger_type] == TASK_TRIGGER_IDLE: - trigger.Id = 'OnIdle_ID1' + trigger.Id = "OnIdle_ID1" # On Task Creation Trigger Parameters elif trigger_types[trigger_type] == TASK_TRIGGER_REGISTRATION: - trigger.Id = 'OnTaskCreation_ID1' + trigger.Id = "OnTaskCreation_ID1" # On Boot Trigger Parameters elif trigger_types[trigger_type] == TASK_TRIGGER_BOOT: - trigger.Id = 'OnBoot_ID1' + trigger.Id = "OnBoot_ID1" # On Logon Trigger Parameters elif trigger_types[trigger_type] == TASK_TRIGGER_LOGON: - trigger.Id = 'OnLogon_ID1' + trigger.Id = "OnLogon_ID1" # On Session State Change Trigger Parameters elif trigger_types[trigger_type] == TASK_TRIGGER_SESSION_STATE_CHANGE: - trigger.Id = 'OnSessionStateChange_ID1' - if kwargs.get('session_user_name', False): - trigger.UserId = kwargs.get('session_user_name') - if kwargs.get('state_change', False): - trigger.StateChange = state_changes[kwargs.get('state_change')] + trigger.Id = "OnSessionStateChange_ID1" + if kwargs.get("session_user_name", False): + trigger.UserId = kwargs.get("session_user_name") + if kwargs.get("state_change", False): + trigger.StateChange = state_changes[kwargs.get("state_change")] else: return 'Required parameter "state_change" not passed' # Save the task if save_definition: # Save the Changes - return _save_task_definition(name=name, - task_folder=task_folder, - task_definition=task_definition, - user_name=task_definition.Principal.UserID, - password=None, - logon_type=task_definition.Principal.LogonType) + return _save_task_definition( + name=name, + task_folder=task_folder, + task_definition=task_definition, + user_name=task_definition.Principal.UserID, + password=None, + logon_type=task_definition.Principal.LogonType, + ) -def clear_triggers(name, location='\\'): - r''' +def clear_triggers(name, location="\\"): + r""" Remove all triggers from the task. Args: @@ -2374,10 +2418,10 @@ def clear_triggers(name, location='\\'): .. code-block:: bash salt 'minion-id' task.clear_trigger <task_name> - ''' + """ # Check for existing task if name not in list_tasks(location): - return '{0} not found in {1}'.format(name, location) + return "{0} not found in {1}".format(name, location) # Create the task service object with salt.utils.winapi.Com(): @@ -2392,9 +2436,11 @@ def clear_triggers(name, location='\\'): triggers.Clear() # Save the Changes - return _save_task_definition(name=name, - task_folder=task_folder, - task_definition=task_definition, - user_name=task_definition.Principal.UserID, - password=None, - logon_type=task_definition.Principal.LogonType) + return _save_task_definition( + name=name, + task_folder=task_folder, + task_definition=task_definition, + user_name=task_definition.Principal.UserID, + password=None, + logon_type=task_definition.Principal.LogonType, + ) diff --git a/salt/modules/win_timezone.py b/salt/modules/win_timezone.py index b3eda53c3f2..64c1134ef93 100644 --- a/salt/modules/win_timezone.py +++ b/salt/modules/win_timezone.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing timezone on Windows systems. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -14,6 +14,7 @@ from salt.exceptions import CommandExecutionError # Import 3rd party libs try: import pytz + HAS_PYTZ = True except ImportError: HAS_PYTZ = False @@ -21,7 +22,7 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'timezone' +__virtualname__ = "timezone" class TzMapper(object): @@ -49,157 +50,160 @@ class TzMapper(object): return sorted(self.win_to_unix.values()) -mapper = TzMapper({ - 'AUS Central Standard Time': 'Australia/Darwin', - 'AUS Eastern Standard Time': 'Australia/Sydney', - 'Afghanistan Standard Time': 'Asia/Kabul', - 'Alaskan Standard Time': 'America/Anchorage', - 'Aleutian Standard Time': 'America/Adak', - 'Altai Standard Time': 'Asia/Barnaul', - 'Arab Standard Time': 'Asia/Riyadh', - 'Arabian Standard Time': 'Asia/Dubai', - 'Arabic Standard Time': 'Asia/Baghdad', - 'Argentina Standard Time': 'America/Buenos_Aires', - 'Astrakhan Standard Time': 'Europe/Astrakhan', - 'Atlantic Standard Time': 'America/Halifax', - 'Aus Central W. Standard Time': 'Australia/Eucla', - 'Azerbaijan Standard Time': 'Asia/Baku', - 'Azores Standard Time': 'Atlantic/Azores', - 'Bahia Standard Time': 'America/Bahia', - 'Bangladesh Standard Time': 'Asia/Dhaka', - 'Belarus Standard Time': 'Europe/Minsk', - 'Bougainville Standard Time': 'Pacific/Bougainville', - 'Canada Central Standard Time': 'America/Regina', - 'Cape Verde Standard Time': 'Atlantic/Cape_Verde', - 'Caucasus Standard Time': 'Asia/Yerevan', - 'Cen. Australia Standard Time': 'Australia/Adelaide', - 'Central America Standard Time': 'America/Guatemala', - 'Central Asia Standard Time': 'Asia/Almaty', - 'Central Brazilian Standard Time': 'America/Cuiaba', - 'Central Europe Standard Time': 'Europe/Budapest', - 'Central European Standard Time': 'Europe/Warsaw', - 'Central Pacific Standard Time': 'Pacific/Guadalcanal', - 'Central Standard Time': 'America/Chicago', - 'Central Standard Time (Mexico)': 'America/Mexico_City', - 'Chatham Islands Standard Time': 'Pacific/Chatham', - 'China Standard Time': 'Asia/Shanghai', - 'Cuba Standard Time': 'America/Havana', - 'Dateline Standard Time': 'Etc/GMT+12', - 'E. Africa Standard Time': 'Africa/Nairobi', - 'E. Australia Standard Time': 'Australia/Brisbane', - 'E. Europe Standard Time': 'Europe/Chisinau', - 'E. South America Standard Time': 'America/Sao_Paulo', - 'Easter Island Standard Time': 'Pacific/Easter', - 'Eastern Standard Time': 'America/New_York', - 'Eastern Standard Time (Mexico)': 'America/Cancun', - 'Egypt Standard Time': 'Africa/Cairo', - 'Ekaterinburg Standard Time': 'Asia/Yekaterinburg', - 'FLE Standard Time': 'Europe/Kiev', - 'Fiji Standard Time': 'Pacific/Fiji', - 'GMT Standard Time': 'Europe/London', - 'GTB Standard Time': 'Europe/Bucharest', - 'Georgian Standard Time': 'Asia/Tbilisi', - 'Greenland Standard Time': 'America/Godthab', - 'Greenwich Standard Time': 'Atlantic/Reykjavik', - 'Haiti Standard Time': 'America/Port-au-Prince', - 'Hawaiian Standard Time': 'Pacific/Honolulu', - 'India Standard Time': 'Asia/Calcutta', - 'Iran Standard Time': 'Asia/Tehran', - 'Israel Standard Time': 'Asia/Jerusalem', - 'Jordan Standard Time': 'Asia/Amman', - 'Kaliningrad Standard Time': 'Europe/Kaliningrad', - 'Korea Standard Time': 'Asia/Seoul', - 'Libya Standard Time': 'Africa/Tripoli', - 'Line Islands Standard Time': 'Pacific/Kiritimati', - 'Lord Howe Standard Time': 'Australia/Lord_Howe', - 'Magadan Standard Time': 'Asia/Magadan', - 'Magallanes Standard Time': 'America/Punta_Arenas', - 'Marquesas Standard Time': 'Pacific/Marquesas', - 'Mauritius Standard Time': 'Indian/Mauritius', - 'Middle East Standard Time': 'Asia/Beirut', - 'Montevideo Standard Time': 'America/Montevideo', - 'Morocco Standard Time': 'Africa/Casablanca', - 'Mountain Standard Time': 'America/Denver', - 'Mountain Standard Time (Mexico)': 'America/Chihuahua', - 'Myanmar Standard Time': 'Asia/Rangoon', - 'N. Central Asia Standard Time': 'Asia/Novosibirsk', - 'Namibia Standard Time': 'Africa/Windhoek', - 'Nepal Standard Time': 'Asia/Katmandu', - 'New Zealand Standard Time': 'Pacific/Auckland', - 'Newfoundland Standard Time': 'America/St_Johns', - 'Norfolk Standard Time': 'Pacific/Norfolk', - 'North Asia East Standard Time': 'Asia/Irkutsk', - 'North Asia Standard Time': 'Asia/Krasnoyarsk', - 'North Korea Standard Time': 'Asia/Pyongyang', - 'Omsk Standard Time': 'Asia/Omsk', - 'Pacific SA Standard Time': 'America/Santiago', - 'Pacific Standard Time': 'America/Los_Angeles', - 'Pacific Standard Time (Mexico)': 'America/Tijuana', - 'Pakistan Standard Time': 'Asia/Karachi', - 'Paraguay Standard Time': 'America/Asuncion', - 'Romance Standard Time': 'Europe/Paris', - 'Russia Time Zone 10': 'Asia/Srednekolymsk', - 'Russia Time Zone 11': 'Asia/Kamchatka', - 'Russia Time Zone 3': 'Europe/Samara', - 'Russian Standard Time': 'Europe/Moscow', - 'SA Eastern Standard Time': 'America/Cayenne', - 'SA Pacific Standard Time': 'America/Bogota', - 'SA Western Standard Time': 'America/La_Paz', - 'SE Asia Standard Time': 'Asia/Bangkok', - 'Saint Pierre Standard Time': 'America/Miquelon', - 'Sakhalin Standard Time': 'Asia/Sakhalin', - 'Samoa Standard Time': 'Pacific/Apia', - 'Saratov Standard Time': 'Europe/Saratov', - 'Singapore Standard Time': 'Asia/Singapore', - 'South Africa Standard Time': 'Africa/Johannesburg', - 'Sri Lanka Standard Time': 'Asia/Colombo', - 'Syria Standard Time': 'Asia/Damascus', - 'Taipei Standard Time': 'Asia/Taipei', - 'Tasmania Standard Time': 'Australia/Hobart', - 'Tocantins Standard Time': 'America/Araguaina', - 'Tokyo Standard Time': 'Asia/Tokyo', - 'Tomsk Standard Time': 'Asia/Tomsk', - 'Tonga Standard Time': 'Pacific/Tongatapu', - 'Transbaikal Standard Time': 'Asia/Chita', - 'Turkey Standard Time': 'Europe/Istanbul', - 'Turks And Caicos Standard Time': 'America/Grand_Turk', - 'US Eastern Standard Time': 'America/Indianapolis', - 'US Mountain Standard Time': 'America/Phoenix', - 'UTC': 'Etc/GMT', - 'UTC+12': 'Etc/GMT-12', - 'UTC+13': 'Etc/GMT-13', - 'UTC-02': 'Etc/GMT+2', - 'UTC-08': 'Etc/GMT+8', - 'UTC-09': 'Etc/GMT+9', - 'UTC-11': 'Etc/GMT+11', - 'Ulaanbaatar Standard Time': 'Asia/Ulaanbaatar', - 'Venezuela Standard Time': 'America/Caracas', - 'Vladivostok Standard Time': 'Asia/Vladivostok', - 'W. Australia Standard Time': 'Australia/Perth', - 'W. Central Africa Standard Time': 'Africa/Lagos', - 'W. Europe Standard Time': 'Europe/Berlin', - 'W. Mongolia Standard Time': 'Asia/Hovd', - 'West Asia Standard Time': 'Asia/Tashkent', - 'West Bank Standard Time': 'Asia/Hebron', - 'West Pacific Standard Time': 'Pacific/Port_Moresby', - 'Yakutsk Standard Time': 'Asia/Yakutsk'}) +mapper = TzMapper( + { + "AUS Central Standard Time": "Australia/Darwin", + "AUS Eastern Standard Time": "Australia/Sydney", + "Afghanistan Standard Time": "Asia/Kabul", + "Alaskan Standard Time": "America/Anchorage", + "Aleutian Standard Time": "America/Adak", + "Altai Standard Time": "Asia/Barnaul", + "Arab Standard Time": "Asia/Riyadh", + "Arabian Standard Time": "Asia/Dubai", + "Arabic Standard Time": "Asia/Baghdad", + "Argentina Standard Time": "America/Buenos_Aires", + "Astrakhan Standard Time": "Europe/Astrakhan", + "Atlantic Standard Time": "America/Halifax", + "Aus Central W. Standard Time": "Australia/Eucla", + "Azerbaijan Standard Time": "Asia/Baku", + "Azores Standard Time": "Atlantic/Azores", + "Bahia Standard Time": "America/Bahia", + "Bangladesh Standard Time": "Asia/Dhaka", + "Belarus Standard Time": "Europe/Minsk", + "Bougainville Standard Time": "Pacific/Bougainville", + "Canada Central Standard Time": "America/Regina", + "Cape Verde Standard Time": "Atlantic/Cape_Verde", + "Caucasus Standard Time": "Asia/Yerevan", + "Cen. Australia Standard Time": "Australia/Adelaide", + "Central America Standard Time": "America/Guatemala", + "Central Asia Standard Time": "Asia/Almaty", + "Central Brazilian Standard Time": "America/Cuiaba", + "Central Europe Standard Time": "Europe/Budapest", + "Central European Standard Time": "Europe/Warsaw", + "Central Pacific Standard Time": "Pacific/Guadalcanal", + "Central Standard Time": "America/Chicago", + "Central Standard Time (Mexico)": "America/Mexico_City", + "Chatham Islands Standard Time": "Pacific/Chatham", + "China Standard Time": "Asia/Shanghai", + "Cuba Standard Time": "America/Havana", + "Dateline Standard Time": "Etc/GMT+12", + "E. Africa Standard Time": "Africa/Nairobi", + "E. Australia Standard Time": "Australia/Brisbane", + "E. Europe Standard Time": "Europe/Chisinau", + "E. South America Standard Time": "America/Sao_Paulo", + "Easter Island Standard Time": "Pacific/Easter", + "Eastern Standard Time": "America/New_York", + "Eastern Standard Time (Mexico)": "America/Cancun", + "Egypt Standard Time": "Africa/Cairo", + "Ekaterinburg Standard Time": "Asia/Yekaterinburg", + "FLE Standard Time": "Europe/Kiev", + "Fiji Standard Time": "Pacific/Fiji", + "GMT Standard Time": "Europe/London", + "GTB Standard Time": "Europe/Bucharest", + "Georgian Standard Time": "Asia/Tbilisi", + "Greenland Standard Time": "America/Godthab", + "Greenwich Standard Time": "Atlantic/Reykjavik", + "Haiti Standard Time": "America/Port-au-Prince", + "Hawaiian Standard Time": "Pacific/Honolulu", + "India Standard Time": "Asia/Calcutta", + "Iran Standard Time": "Asia/Tehran", + "Israel Standard Time": "Asia/Jerusalem", + "Jordan Standard Time": "Asia/Amman", + "Kaliningrad Standard Time": "Europe/Kaliningrad", + "Korea Standard Time": "Asia/Seoul", + "Libya Standard Time": "Africa/Tripoli", + "Line Islands Standard Time": "Pacific/Kiritimati", + "Lord Howe Standard Time": "Australia/Lord_Howe", + "Magadan Standard Time": "Asia/Magadan", + "Magallanes Standard Time": "America/Punta_Arenas", + "Marquesas Standard Time": "Pacific/Marquesas", + "Mauritius Standard Time": "Indian/Mauritius", + "Middle East Standard Time": "Asia/Beirut", + "Montevideo Standard Time": "America/Montevideo", + "Morocco Standard Time": "Africa/Casablanca", + "Mountain Standard Time": "America/Denver", + "Mountain Standard Time (Mexico)": "America/Chihuahua", + "Myanmar Standard Time": "Asia/Rangoon", + "N. Central Asia Standard Time": "Asia/Novosibirsk", + "Namibia Standard Time": "Africa/Windhoek", + "Nepal Standard Time": "Asia/Katmandu", + "New Zealand Standard Time": "Pacific/Auckland", + "Newfoundland Standard Time": "America/St_Johns", + "Norfolk Standard Time": "Pacific/Norfolk", + "North Asia East Standard Time": "Asia/Irkutsk", + "North Asia Standard Time": "Asia/Krasnoyarsk", + "North Korea Standard Time": "Asia/Pyongyang", + "Omsk Standard Time": "Asia/Omsk", + "Pacific SA Standard Time": "America/Santiago", + "Pacific Standard Time": "America/Los_Angeles", + "Pacific Standard Time (Mexico)": "America/Tijuana", + "Pakistan Standard Time": "Asia/Karachi", + "Paraguay Standard Time": "America/Asuncion", + "Romance Standard Time": "Europe/Paris", + "Russia Time Zone 10": "Asia/Srednekolymsk", + "Russia Time Zone 11": "Asia/Kamchatka", + "Russia Time Zone 3": "Europe/Samara", + "Russian Standard Time": "Europe/Moscow", + "SA Eastern Standard Time": "America/Cayenne", + "SA Pacific Standard Time": "America/Bogota", + "SA Western Standard Time": "America/La_Paz", + "SE Asia Standard Time": "Asia/Bangkok", + "Saint Pierre Standard Time": "America/Miquelon", + "Sakhalin Standard Time": "Asia/Sakhalin", + "Samoa Standard Time": "Pacific/Apia", + "Saratov Standard Time": "Europe/Saratov", + "Singapore Standard Time": "Asia/Singapore", + "South Africa Standard Time": "Africa/Johannesburg", + "Sri Lanka Standard Time": "Asia/Colombo", + "Syria Standard Time": "Asia/Damascus", + "Taipei Standard Time": "Asia/Taipei", + "Tasmania Standard Time": "Australia/Hobart", + "Tocantins Standard Time": "America/Araguaina", + "Tokyo Standard Time": "Asia/Tokyo", + "Tomsk Standard Time": "Asia/Tomsk", + "Tonga Standard Time": "Pacific/Tongatapu", + "Transbaikal Standard Time": "Asia/Chita", + "Turkey Standard Time": "Europe/Istanbul", + "Turks And Caicos Standard Time": "America/Grand_Turk", + "US Eastern Standard Time": "America/Indianapolis", + "US Mountain Standard Time": "America/Phoenix", + "UTC": "Etc/GMT", + "UTC+12": "Etc/GMT-12", + "UTC+13": "Etc/GMT-13", + "UTC-02": "Etc/GMT+2", + "UTC-08": "Etc/GMT+8", + "UTC-09": "Etc/GMT+9", + "UTC-11": "Etc/GMT+11", + "Ulaanbaatar Standard Time": "Asia/Ulaanbaatar", + "Venezuela Standard Time": "America/Caracas", + "Vladivostok Standard Time": "Asia/Vladivostok", + "W. Australia Standard Time": "Australia/Perth", + "W. Central Africa Standard Time": "Africa/Lagos", + "W. Europe Standard Time": "Europe/Berlin", + "W. Mongolia Standard Time": "Asia/Hovd", + "West Asia Standard Time": "Asia/Tashkent", + "West Bank Standard Time": "Asia/Hebron", + "West Pacific Standard Time": "Pacific/Port_Moresby", + "Yakutsk Standard Time": "Asia/Yakutsk", + } +) def __virtual__(): - ''' + """ Only load on windows - ''' - if not __utils__['platform.is_windows'](): + """ + if not __utils__["platform.is_windows"](): return False, "Module win_timezone: Not on Windows client" if not HAS_PYTZ: return False, "Module win_timezone: pytz not found" - if not __utils__['path.which']('tzutil'): + if not __utils__["path.which"]("tzutil"): return False, "Module win_timezone: tzutil not found" return __virtualname__ def get_zone(): - ''' + """ Get current timezone (i.e. America/Denver) Returns: @@ -210,22 +214,23 @@ def get_zone(): .. code-block:: bash salt '*' timezone.get_zone - ''' - win_zone = __utils__['reg.read_value']( - hive='HKLM', - key='SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation', - vname='TimeZoneKeyName')['vdata'] + """ + win_zone = __utils__["reg.read_value"]( + hive="HKLM", + key="SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", + vname="TimeZoneKeyName", + )["vdata"] # Some data may have null characters. We only need the first portion up to # the first null character. See the following: # https://github.com/saltstack/salt/issues/51940 # https://stackoverflow.com/questions/27716746/hklm-system-currentcontrolset-control-timezoneinformation-timezonekeyname-corrup - if '\0' in win_zone: - win_zone = win_zone.split('\0')[0] - return mapper.get_unix(win_zone.lower(), 'Unknown') + if "\0" in win_zone: + win_zone = win_zone.split("\0")[0] + return mapper.get_unix(win_zone.lower(), "Unknown") def get_offset(): - ''' + """ Get current numeric timezone offset from UTC (i.e. -0700) Returns: @@ -236,17 +241,17 @@ def get_offset(): .. code-block:: bash salt '*' timezone.get_offset - ''' + """ # http://craigglennie.com/programming/python/2013/07/21/working-with-timezones-using-Python-and-pytz-localize-vs-normalize/ tz_object = pytz.timezone(get_zone()) utc_time = pytz.utc.localize(datetime.utcnow()) loc_time = utc_time.astimezone(tz_object) norm_time = tz_object.normalize(loc_time) - return norm_time.strftime('%z') + return norm_time.strftime("%z") def get_zonecode(): - ''' + """ Get current timezone (i.e. PST, MDT, etc) Returns: @@ -257,14 +262,14 @@ def get_zonecode(): .. code-block:: bash salt '*' timezone.get_zonecode - ''' + """ tz_object = pytz.timezone(get_zone()) loc_time = tz_object.localize(datetime.utcnow()) return loc_time.tzname() def set_zone(timezone): - ''' + """ Sets the timezone using the tzutil. Args: @@ -281,7 +286,7 @@ def set_zone(timezone): .. code-block:: bash salt '*' timezone.set_zone 'America/Denver' - ''' + """ # if it's one of the key's just use it if timezone.lower() in mapper.win_to_unix: win_zone = timezone @@ -292,20 +297,21 @@ def set_zone(timezone): else: # Raise error because it's neither key nor value - raise CommandExecutionError('Invalid timezone passed: {0}'.format(timezone)) + raise CommandExecutionError("Invalid timezone passed: {0}".format(timezone)) # Set the value - cmd = ['tzutil', '/s', win_zone] - res = __salt__['cmd.run_all'](cmd, python_shell=False) - if res['retcode']: - raise CommandExecutionError('tzutil encountered an error setting ' - 'timezone: {0}'.format(timezone), - info=res) + cmd = ["tzutil", "/s", win_zone] + res = __salt__["cmd.run_all"](cmd, python_shell=False) + if res["retcode"]: + raise CommandExecutionError( + "tzutil encountered an error setting " "timezone: {0}".format(timezone), + info=res, + ) return zone_compare(timezone) def zone_compare(timezone): - ''' + """ Compares the given timezone with the machine timezone. Mostly useful for running state checks. @@ -322,7 +328,7 @@ def zone_compare(timezone): .. code-block:: bash salt '*' timezone.zone_compare 'America/Denver' - ''' + """ # if it's one of the key's just use it if timezone.lower() in mapper.win_to_unix: check_zone = timezone @@ -333,14 +339,13 @@ def zone_compare(timezone): else: # Raise error because it's neither key nor value - raise CommandExecutionError('Invalid timezone passed: {0}' - ''.format(timezone)) + raise CommandExecutionError("Invalid timezone passed: {0}" "".format(timezone)) - return get_zone() == mapper.get_unix(check_zone, 'Unknown') + return get_zone() == mapper.get_unix(check_zone, "Unknown") def list(unix_style=True): - ''' + """ Return a list of Timezones that this module supports. These can be in either Unix or Windows format. @@ -363,7 +368,7 @@ def list(unix_style=True): # Windows-style timezones salt '*' timezone.list unix_style=False - ''' + """ if unix_style: return mapper.list_unix() else: @@ -371,7 +376,7 @@ def list(unix_style=True): def get_hwclock(): - ''' + """ Get current hardware clock setting (UTC or localtime) .. note:: @@ -383,13 +388,13 @@ def get_hwclock(): .. code-block:: bash salt '*' timezone.get_hwclock - ''' + """ # The hardware clock is always localtime on Windows - return 'localtime' + return "localtime" def set_hwclock(clock): - ''' + """ Sets the hardware clock to be either UTC or localtime .. note:: @@ -401,6 +406,6 @@ def set_hwclock(clock): .. code-block:: bash salt '*' timezone.set_hwclock UTC - ''' + """ # The hardware clock is always localtime on Windows return False diff --git a/salt/modules/win_useradd.py b/salt/modules/win_useradd.py index f96cfb49b16..e87ff189afc 100644 --- a/salt/modules/win_useradd.py +++ b/salt/modules/win_useradd.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing Windows Users .. important:: @@ -21,26 +21,28 @@ Module for managing Windows Users .. note:: This currently only works with local user accounts, not domain accounts -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import time from datetime import datetime -try: - from shlex import quote as _cmd_quote # pylint: disable=E0611 -except Exception: # pylint: disable=broad-except - from pipes import quote as _cmd_quote - # Import Salt libs import salt.utils.args import salt.utils.dateutils import salt.utils.platform import salt.utils.winapi +from salt.exceptions import CommandExecutionError from salt.ext import six from salt.ext.six import string_types -from salt.exceptions import CommandExecutionError + +try: + from shlex import quote as _cmd_quote # pylint: disable=E0611 +except Exception: # pylint: disable=broad-except + from pipes import quote as _cmd_quote + log = logging.getLogger(__name__) @@ -54,29 +56,30 @@ try: import win32profile import win32security import win32ts + HAS_WIN32NET_MODS = True except ImportError: HAS_WIN32NET_MODS = False # Define the module's virtual name -__virtualname__ = 'user' +__virtualname__ = "user" def __virtual__(): - ''' + """ Requires Windows and Windows Modules - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Module win_useradd: Windows Only' + return False, "Module win_useradd: Windows Only" if not HAS_WIN32NET_MODS: - return False, 'Module win_useradd: Missing Win32 Modules' + return False, "Module win_useradd: Missing Win32 Modules" return __virtualname__ def _to_unicode(instr): - ''' + """ Internal function for converting to Unicode Strings The NetUser* series of API calls in this module requires input parameters to @@ -89,23 +92,25 @@ def _to_unicode(instr): Returns: str: Unicode type string - ''' + """ if instr is None or isinstance(instr, six.text_type): return instr else: - return six.text_type(instr, 'utf-8') + return six.text_type(instr, "utf-8") -def add(name, - password=None, - fullname=None, - description=None, - groups=None, - home=None, - homedrive=None, - profile=None, - logonscript=None): - ''' +def add( + name, + password=None, + fullname=None, + description=None, + groups=None, + home=None, + homedrive=None, + profile=None, + logonscript=None, +): + """ Add a user to the minion. Args: @@ -140,7 +145,7 @@ def add(name, .. code-block:: bash salt '*' user.add name password - ''' + """ if six.PY2: name = _to_unicode(name) password = _to_unicode(password) @@ -153,51 +158,50 @@ def add(name, user_info = {} if name: - user_info['name'] = name + user_info["name"] = name else: return False - user_info['password'] = password - user_info['priv'] = win32netcon.USER_PRIV_USER - user_info['home_dir'] = home - user_info['comment'] = description - user_info['flags'] = win32netcon.UF_SCRIPT - user_info['script_path'] = logonscript + user_info["password"] = password + user_info["priv"] = win32netcon.USER_PRIV_USER + user_info["home_dir"] = home + user_info["comment"] = description + user_info["flags"] = win32netcon.UF_SCRIPT + user_info["script_path"] = logonscript try: win32net.NetUserAdd(None, 1, user_info) except win32net.error as exc: - log.error('Failed to create user %s', name) - log.error('nbr: %s', exc.winerror) - log.error('ctx: %s', exc.funcname) - log.error('msg: %s', exc.strerror) + log.error("Failed to create user %s", name) + log.error("nbr: %s", exc.winerror) + log.error("ctx: %s", exc.funcname) + log.error("msg: %s", exc.strerror) return False - update(name=name, - homedrive=homedrive, - profile=profile, - fullname=fullname) + update(name=name, homedrive=homedrive, profile=profile, fullname=fullname) ret = chgroups(name, groups) if groups else True return ret -def update(name, - password=None, - fullname=None, - description=None, - home=None, - homedrive=None, - logonscript=None, - profile=None, - expiration_date=None, - expired=None, - account_disabled=None, - unlock_account=None, - password_never_expires=None, - disallow_change_password=None): +def update( + name, + password=None, + fullname=None, + description=None, + home=None, + homedrive=None, + logonscript=None, + profile=None, + expiration_date=None, + expired=None, + account_disabled=None, + unlock_account=None, + password_never_expires=None, + disallow_change_password=None, +): # pylint: disable=anomalous-backslash-in-string - ''' + """ Updates settings for the windows user. Name is the only required parameter. Settings will only be changed if the parameter is passed a value. @@ -252,7 +256,7 @@ def update(name, salt '*' user.update bob password=secret profile=C:\\Users\\Bob home=\\server\homeshare\bob homedrive=U: - ''' + """ # pylint: enable=anomalous-backslash-in-string if six.PY2: name = _to_unicode(name) @@ -269,78 +273,76 @@ def update(name, try: user_info = win32net.NetUserGetInfo(None, name, 4) except win32net.error as exc: - log.error('Failed to update user %s', name) - log.error('nbr: %s', exc.winerror) - log.error('ctx: %s', exc.funcname) - log.error('msg: %s', exc.strerror) + log.error("Failed to update user %s", name) + log.error("nbr: %s", exc.winerror) + log.error("ctx: %s", exc.funcname) + log.error("msg: %s", exc.strerror) return False # Check parameters to update # Update the user object with new settings if password: - user_info['password'] = password + user_info["password"] = password if home: - user_info['home_dir'] = home + user_info["home_dir"] = home if homedrive: - user_info['home_dir_drive'] = homedrive + user_info["home_dir_drive"] = homedrive if description: - user_info['comment'] = description + user_info["comment"] = description if logonscript: - user_info['script_path'] = logonscript + user_info["script_path"] = logonscript if fullname: - user_info['full_name'] = fullname + user_info["full_name"] = fullname if profile: - user_info['profile'] = profile + user_info["profile"] = profile if expiration_date: - if expiration_date == 'Never': - user_info['acct_expires'] = win32netcon.TIMEQ_FOREVER + if expiration_date == "Never": + user_info["acct_expires"] = win32netcon.TIMEQ_FOREVER else: try: dt_obj = salt.utils.dateutils.date_cast(expiration_date) except (ValueError, RuntimeError): - return 'Invalid Date/Time Format: {0}'.format(expiration_date) - user_info['acct_expires'] = time.mktime(dt_obj.timetuple()) + return "Invalid Date/Time Format: {0}".format(expiration_date) + user_info["acct_expires"] = time.mktime(dt_obj.timetuple()) if expired is not None: if expired: - user_info['password_expired'] = 1 + user_info["password_expired"] = 1 else: - user_info['password_expired'] = 0 + user_info["password_expired"] = 0 if account_disabled is not None: if account_disabled: - user_info['flags'] |= win32netcon.UF_ACCOUNTDISABLE + user_info["flags"] |= win32netcon.UF_ACCOUNTDISABLE else: - user_info['flags'] &= ~win32netcon.UF_ACCOUNTDISABLE + user_info["flags"] &= ~win32netcon.UF_ACCOUNTDISABLE if unlock_account is not None: if unlock_account: - user_info['flags'] &= ~win32netcon.UF_LOCKOUT + user_info["flags"] &= ~win32netcon.UF_LOCKOUT if password_never_expires is not None: if password_never_expires: - user_info['flags'] |= win32netcon.UF_DONT_EXPIRE_PASSWD + user_info["flags"] |= win32netcon.UF_DONT_EXPIRE_PASSWD else: - user_info['flags'] &= ~win32netcon.UF_DONT_EXPIRE_PASSWD + user_info["flags"] &= ~win32netcon.UF_DONT_EXPIRE_PASSWD if disallow_change_password is not None: if disallow_change_password: - user_info['flags'] |= win32netcon.UF_PASSWD_CANT_CHANGE + user_info["flags"] |= win32netcon.UF_PASSWD_CANT_CHANGE else: - user_info['flags'] &= ~win32netcon.UF_PASSWD_CANT_CHANGE + user_info["flags"] &= ~win32netcon.UF_PASSWD_CANT_CHANGE # Apply new settings try: win32net.NetUserSetInfo(None, name, 4, user_info) except win32net.error as exc: - log.error('Failed to update user %s', name) - log.error('nbr: %s', exc.winerror) - log.error('ctx: %s', exc.funcname) - log.error('msg: %s', exc.strerror) + log.error("Failed to update user %s", name) + log.error("nbr: %s", exc.winerror) + log.error("ctx: %s", exc.funcname) + log.error("msg: %s", exc.strerror) return False return True -def delete(name, - purge=False, - force=False): - ''' +def delete(name, purge=False, force=False): + """ Remove a user from the minion Args: @@ -362,7 +364,7 @@ def delete(name, .. code-block:: bash salt '*' user.delete name - ''' + """ if six.PY2: name = _to_unicode(name) @@ -370,10 +372,10 @@ def delete(name, try: user_info = win32net.NetUserGetInfo(None, name, 4) except win32net.error as exc: - log.error('User not found: %s', name) - log.error('nbr: %s', exc.winerror) - log.error('ctx: %s', exc.funcname) - log.error('msg: %s', exc.strerror) + log.error("User not found: %s", name) + log.error("nbr: %s", exc.winerror) + log.error("ctx: %s", exc.funcname) + log.error("msg: %s", exc.strerror) return False # Check if the user is logged in @@ -381,17 +383,22 @@ def delete(name, try: sess_list = win32ts.WTSEnumerateSessions() except win32ts.error as exc: - log.error('No logged in users found') - log.error('nbr: %s', exc.winerror) - log.error('ctx: %s', exc.funcname) - log.error('msg: %s', exc.strerror) + log.error("No logged in users found") + log.error("nbr: %s", exc.winerror) + log.error("ctx: %s", exc.funcname) + log.error("msg: %s", exc.strerror) # Is the user one that is logged in logged_in = False session_id = None for sess in sess_list: - if win32ts.WTSQuerySessionInformation(None, sess['SessionId'], win32ts.WTSUserName) == name: - session_id = sess['SessionId'] + if ( + win32ts.WTSQuerySessionInformation( + None, sess["SessionId"], win32ts.WTSUserName + ) + == name + ): + session_id = sess["SessionId"] logged_in = True # If logged in and set to force, log the user out and continue @@ -399,15 +406,17 @@ def delete(name, if logged_in: if force: try: - win32ts.WTSLogoffSession(win32ts.WTS_CURRENT_SERVER_HANDLE, session_id, True) + win32ts.WTSLogoffSession( + win32ts.WTS_CURRENT_SERVER_HANDLE, session_id, True + ) except win32ts.error as exc: - log.error('User not found: %s', name) - log.error('nbr: %s', exc.winerror) - log.error('ctx: %s', exc.funcname) - log.error('msg: %s', exc.strerror) + log.error("User not found: %s", name) + log.error("nbr: %s", exc.winerror) + log.error("ctx: %s", exc.funcname) + log.error("msg: %s", exc.strerror) return False else: - log.error('User %s is currently logged in.', name) + log.error("User %s is currently logged in.", name) return False # Remove the User Profile directory @@ -420,27 +429,27 @@ def delete(name, if number == 2: # Profile Folder Not Found pass else: - log.error('Failed to remove profile for %s', name) - log.error('nbr: %s', exc.winerror) - log.error('ctx: %s', exc.funcname) - log.error('msg: %s', exc.strerror) + log.error("Failed to remove profile for %s", name) + log.error("nbr: %s", exc.winerror) + log.error("ctx: %s", exc.funcname) + log.error("msg: %s", exc.strerror) return False # And finally remove the user account try: win32net.NetUserDel(None, name) except win32net.error as exc: - log.error('Failed to delete user %s', name) - log.error('nbr: %s', exc.winerror) - log.error('ctx: %s', exc.funcname) - log.error('msg: %s', exc.strerror) + log.error("Failed to delete user %s", name) + log.error("nbr: %s", exc.winerror) + log.error("ctx: %s", exc.funcname) + log.error("msg: %s", exc.strerror) return False return True def getUserSid(username): - ''' + """ Get the Security ID for the user Args: @@ -454,21 +463,22 @@ def getUserSid(username): .. code-block:: bash salt '*' user.getUserSid jsnuffy - ''' + """ if six.PY2: username = _to_unicode(username) domain = win32api.GetComputerName() - if username.find('\\') != -1: - domain = username.split('\\')[0] - username = username.split('\\')[-1] + if username.find("\\") != -1: + domain = username.split("\\")[0] + username = username.split("\\")[-1] domain = domain.upper() return win32security.ConvertSidToStringSid( - win32security.LookupAccountName(None, domain + '\\' + username)[0]) + win32security.LookupAccountName(None, domain + "\\" + username)[0] + ) def setpassword(name, password): - ''' + """ Set the user's password Args: @@ -484,12 +494,12 @@ def setpassword(name, password): .. code-block:: bash salt '*' user.setpassword jsnuffy sup3rs3cr3t - ''' + """ return update(name=name, password=password) def addgroup(name, group): - ''' + """ Add user to a group Args: @@ -505,28 +515,28 @@ def addgroup(name, group): .. code-block:: bash salt '*' user.addgroup jsnuffy 'Power Users' - ''' + """ if six.PY2: name = _to_unicode(name) group = _to_unicode(group) name = _cmd_quote(name) - group = _cmd_quote(group).lstrip('\'').rstrip('\'') + group = _cmd_quote(group).lstrip("'").rstrip("'") user = info(name) if not user: return False - if group in user['groups']: + if group in user["groups"]: return True cmd = 'net localgroup "{0}" {1} /add'.format(group, name) - ret = __salt__['cmd.run_all'](cmd, python_shell=True) + ret = __salt__["cmd.run_all"](cmd, python_shell=True) - return ret['retcode'] == 0 + return ret["retcode"] == 0 def removegroup(name, group): - ''' + """ Remove user from a group Args: @@ -542,30 +552,30 @@ def removegroup(name, group): .. code-block:: bash salt '*' user.removegroup jsnuffy 'Power Users' - ''' + """ if six.PY2: name = _to_unicode(name) group = _to_unicode(group) name = _cmd_quote(name) - group = _cmd_quote(group).lstrip('\'').rstrip('\'') + group = _cmd_quote(group).lstrip("'").rstrip("'") user = info(name) if not user: return False - if group not in user['groups']: + if group not in user["groups"]: return True cmd = 'net localgroup "{0}" {1} /delete'.format(group, name) - ret = __salt__['cmd.run_all'](cmd, python_shell=True) + ret = __salt__["cmd.run_all"](cmd, python_shell=True) - return ret['retcode'] == 0 + return ret["retcode"] == 0 def chhome(name, home, **kwargs): - ''' + """ Change the home directory of the user, pass True for persist to move files to the new home directory if the old home directory exist. @@ -582,38 +592,38 @@ def chhome(name, home, **kwargs): .. code-block:: bash salt '*' user.chhome foo \\\\fileserver\\home\\foo True - ''' + """ if six.PY2: name = _to_unicode(name) home = _to_unicode(home) kwargs = salt.utils.args.clean_kwargs(**kwargs) - persist = kwargs.pop('persist', False) + persist = kwargs.pop("persist", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) if persist: - log.info('Ignoring unsupported \'persist\' argument to user.chhome') + log.info("Ignoring unsupported 'persist' argument to user.chhome") pre_info = info(name) if not pre_info: return False - if home == pre_info['home']: + if home == pre_info["home"]: return True if not update(name=name, home=home): return False post_info = info(name) - if post_info['home'] != pre_info['home']: - return post_info['home'] == home + if post_info["home"] != pre_info["home"]: + return post_info["home"] == home return False def chprofile(name, profile): - ''' + """ Change the profile directory of the user Args: @@ -629,12 +639,12 @@ def chprofile(name, profile): .. code-block:: bash salt '*' user.chprofile foo \\\\fileserver\\profiles\\foo - ''' + """ return update(name=name, profile=profile) def chfullname(name, fullname): - ''' + """ Change the full name of the user Args: @@ -650,12 +660,12 @@ def chfullname(name, fullname): .. code-block:: bash salt '*' user.chfullname user 'First Last' - ''' + """ return update(name=name, fullname=fullname) def chgroups(name, groups, append=True): - ''' + """ Change the groups this user belongs to, add append=False to make the user a member of only the specified groups @@ -678,14 +688,14 @@ def chgroups(name, groups, append=True): .. code-block:: bash salt '*' user.chgroups jsnuffy Administrators,Users True - ''' + """ if six.PY2: name = _to_unicode(name) if isinstance(groups, string_types): - groups = groups.split(',') + groups = groups.split(",") - groups = [x.strip(' *') for x in groups] + groups = [x.strip(" *") for x in groups] if six.PY2: groups = [_to_unicode(x) for x in groups] @@ -697,19 +707,19 @@ def chgroups(name, groups, append=True): if not append: for group in ugrps: - group = _cmd_quote(group).lstrip('\'').rstrip('\'') + group = _cmd_quote(group).lstrip("'").rstrip("'") if group not in groups: cmd = 'net localgroup "{0}" {1} /delete'.format(group, name) - __salt__['cmd.run_all'](cmd, python_shell=True) + __salt__["cmd.run_all"](cmd, python_shell=True) for group in groups: if group in ugrps: continue - group = _cmd_quote(group).lstrip('\'').rstrip('\'') + group = _cmd_quote(group).lstrip("'").rstrip("'") cmd = 'net localgroup "{0}" {1} /add'.format(group, name) - out = __salt__['cmd.run_all'](cmd, python_shell=True) - if out['retcode'] != 0: - log.error(out['stdout']) + out = __salt__["cmd.run_all"](cmd, python_shell=True) + if out["retcode"] != 0: + log.error(out["stdout"]) return False agrps = set(list_groups(name)) @@ -717,7 +727,7 @@ def chgroups(name, groups, append=True): def info(name): - ''' + """ Return user information Args: @@ -752,7 +762,7 @@ def info(name): .. code-block:: bash salt '*' user.info jsnuffy - ''' + """ if six.PY2: name = _to_unicode(name) @@ -770,53 +780,56 @@ def info(name): except win32net.error: pass - ret['fullname'] = items['full_name'] - ret['name'] = items['name'] - ret['uid'] = win32security.ConvertSidToStringSid(items['user_sid']) - ret['passwd'] = items['password'] - ret['comment'] = items['comment'] - ret['description'] = items['comment'] - ret['active'] = (not bool(items['flags'] & win32netcon.UF_ACCOUNTDISABLE)) - ret['logonscript'] = items['script_path'] - ret['profile'] = items['profile'] - ret['failed_logon_attempts'] = items['bad_pw_count'] - ret['successful_logon_attempts'] = items['num_logons'] - secs = time.mktime(datetime.now().timetuple()) - items['password_age'] - ret['password_changed'] = datetime.fromtimestamp(secs). \ - strftime('%Y-%m-%d %H:%M:%S') - if items['last_logon'] == 0: - ret['last_logon'] = 'Never' + ret["fullname"] = items["full_name"] + ret["name"] = items["name"] + ret["uid"] = win32security.ConvertSidToStringSid(items["user_sid"]) + ret["passwd"] = items["password"] + ret["comment"] = items["comment"] + ret["description"] = items["comment"] + ret["active"] = not bool(items["flags"] & win32netcon.UF_ACCOUNTDISABLE) + ret["logonscript"] = items["script_path"] + ret["profile"] = items["profile"] + ret["failed_logon_attempts"] = items["bad_pw_count"] + ret["successful_logon_attempts"] = items["num_logons"] + secs = time.mktime(datetime.now().timetuple()) - items["password_age"] + ret["password_changed"] = datetime.fromtimestamp(secs).strftime( + "%Y-%m-%d %H:%M:%S" + ) + if items["last_logon"] == 0: + ret["last_logon"] = "Never" else: - ret['last_logon'] = datetime.fromtimestamp(items['last_logon']).\ - strftime('%Y-%m-%d %H:%M:%S') - ret['expiration_date'] = datetime.fromtimestamp(items['acct_expires']).\ - strftime('%Y-%m-%d %H:%M:%S') - ret['expired'] = items['password_expired'] == 1 - if not ret['profile']: - ret['profile'] = _get_userprofile_from_registry(name, ret['uid']) - ret['home'] = items['home_dir'] - ret['homedrive'] = items['home_dir_drive'] - if not ret['home']: - ret['home'] = ret['profile'] - ret['groups'] = groups - if items['flags'] & win32netcon.UF_DONT_EXPIRE_PASSWD == 0: - ret['password_never_expires'] = False + ret["last_logon"] = datetime.fromtimestamp(items["last_logon"]).strftime( + "%Y-%m-%d %H:%M:%S" + ) + ret["expiration_date"] = datetime.fromtimestamp(items["acct_expires"]).strftime( + "%Y-%m-%d %H:%M:%S" + ) + ret["expired"] = items["password_expired"] == 1 + if not ret["profile"]: + ret["profile"] = _get_userprofile_from_registry(name, ret["uid"]) + ret["home"] = items["home_dir"] + ret["homedrive"] = items["home_dir_drive"] + if not ret["home"]: + ret["home"] = ret["profile"] + ret["groups"] = groups + if items["flags"] & win32netcon.UF_DONT_EXPIRE_PASSWD == 0: + ret["password_never_expires"] = False else: - ret['password_never_expires'] = True - if items['flags'] & win32netcon.UF_ACCOUNTDISABLE == 0: - ret['account_disabled'] = False + ret["password_never_expires"] = True + if items["flags"] & win32netcon.UF_ACCOUNTDISABLE == 0: + ret["account_disabled"] = False else: - ret['account_disabled'] = True - if items['flags'] & win32netcon.UF_LOCKOUT == 0: - ret['account_locked'] = False + ret["account_disabled"] = True + if items["flags"] & win32netcon.UF_LOCKOUT == 0: + ret["account_locked"] = False else: - ret['account_locked'] = True - if items['flags'] & win32netcon.UF_PASSWD_CANT_CHANGE == 0: - ret['disallow_change_password'] = False + ret["account_locked"] = True + if items["flags"] & win32netcon.UF_PASSWD_CANT_CHANGE == 0: + ret["disallow_change_password"] = False else: - ret['disallow_change_password'] = True + ret["disallow_change_password"] = True - ret['gid'] = '' + ret["gid"] = "" return ret @@ -826,7 +839,7 @@ def info(name): def _get_userprofile_from_registry(user, sid): - ''' + """ In case net user doesn't return the userprofile we can get it from the registry @@ -837,21 +850,18 @@ def _get_userprofile_from_registry(user, sid): Returns: str: Profile directory - ''' - profile_dir = __salt__['reg.read_value']( - 'HKEY_LOCAL_MACHINE', - 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{0}'.format(sid), - 'ProfileImagePath' - )['vdata'] - log.debug( - 'user %s with sid=%s profile is located at "%s"', - user, sid, profile_dir - ) + """ + profile_dir = __salt__["reg.read_value"]( + "HKEY_LOCAL_MACHINE", + "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\{0}".format(sid), + "ProfileImagePath", + )["vdata"] + log.debug('user %s with sid=%s profile is located at "%s"', user, sid, profile_dir) return profile_dir def list_groups(name): - ''' + """ Return a list of groups the named user belongs to Args: @@ -865,23 +875,23 @@ def list_groups(name): .. code-block:: bash salt '*' user.list_groups foo - ''' + """ if six.PY2: name = _to_unicode(name) ugrp = set() try: - user = info(name)['groups'] + user = info(name)["groups"] except KeyError: return False for group in user: - ugrp.add(group.strip(' *')) + ugrp.add(group.strip(" *")) return sorted(list(ugrp)) def getent(refresh=False): - ''' + """ Return the list of all info for all users Args: @@ -896,31 +906,31 @@ def getent(refresh=False): .. code-block:: bash salt '*' user.getent - ''' - if 'user.getent' in __context__ and not refresh: - return __context__['user.getent'] + """ + if "user.getent" in __context__ and not refresh: + return __context__["user.getent"] ret = [] - for user in __salt__['user.list_users'](): + for user in __salt__["user.list_users"](): stuff = {} - user_info = __salt__['user.info'](user) + user_info = __salt__["user.info"](user) - stuff['gid'] = '' - stuff['groups'] = user_info['groups'] - stuff['home'] = user_info['home'] - stuff['name'] = user_info['name'] - stuff['passwd'] = user_info['passwd'] - stuff['shell'] = '' - stuff['uid'] = user_info['uid'] + stuff["gid"] = "" + stuff["groups"] = user_info["groups"] + stuff["home"] = user_info["home"] + stuff["name"] = user_info["name"] + stuff["passwd"] = user_info["passwd"] + stuff["shell"] = "" + stuff["uid"] = user_info["uid"] ret.append(stuff) - __context__['user.getent'] = ret + __context__["user.getent"] = ret return ret def list_users(): - ''' + """ Return a list of all users on Windows Returns: @@ -931,7 +941,7 @@ def list_users(): .. code-block:: bash salt '*' user.list_users - ''' + """ res = 0 user_list = [] dowhile = True @@ -943,17 +953,17 @@ def list_users(): 0, win32netcon.FILTER_NORMAL_ACCOUNT, res, - win32netcon.MAX_PREFERRED_LENGTH + win32netcon.MAX_PREFERRED_LENGTH, ) for user in users: - user_list.append(user['name']) + user_list.append(user["name"]) return user_list except win32net.error: pass def rename(name, new_name): - ''' + """ Change the username for a named user Args: @@ -969,7 +979,7 @@ def rename(name, new_name): .. code-block:: bash salt '*' user.rename jsnuffy jshmoe - ''' + """ if six.PY2: name = _to_unicode(name) new_name = _to_unicode(new_name) @@ -977,14 +987,12 @@ def rename(name, new_name): # Load information for the current name current_info = info(name) if not current_info: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("User '{0}' does not exist".format(name)) # Look for an existing user with the new name new_info = info(new_name) if new_info: - raise CommandExecutionError( - 'User \'{0}\' already exists'.format(new_name) - ) + raise CommandExecutionError("User '{0}' already exists".format(new_name)) # Rename the user account # Connect to WMI @@ -995,7 +1003,7 @@ def rename(name, new_name): try: user = c.Win32_UserAccount(Name=name)[0] except IndexError: - raise CommandExecutionError('User \'{0}\' does not exist'.format(name)) + raise CommandExecutionError("User '{0}' does not exist".format(name)) # Rename the user result = user.Rename(new_name)[0] @@ -1003,27 +1011,30 @@ def rename(name, new_name): # Check the result (0 means success) if not result == 0: # Define Error Dict - error_dict = {0: 'Success', - 1: 'Instance not found', - 2: 'Instance required', - 3: 'Invalid parameter', - 4: 'User not found', - 5: 'Domain not found', - 6: 'Operation is allowed only on the primary domain controller of the domain', - 7: 'Operation is not allowed on the last administrative account', - 8: 'Operation is not allowed on specified special groups: user, admin, local, or guest', - 9: 'Other API error', - 10: 'Internal error'} + error_dict = { + 0: "Success", + 1: "Instance not found", + 2: "Instance required", + 3: "Invalid parameter", + 4: "User not found", + 5: "Domain not found", + 6: "Operation is allowed only on the primary domain controller of the domain", + 7: "Operation is not allowed on the last administrative account", + 8: "Operation is not allowed on specified special groups: user, admin, local, or guest", + 9: "Other API error", + 10: "Internal error", + } raise CommandExecutionError( - 'There was an error renaming \'{0}\' to \'{1}\'. Error: {2}' - .format(name, new_name, error_dict[result]) + "There was an error renaming '{0}' to '{1}'. Error: {2}".format( + name, new_name, error_dict[result] + ) ) - return info(new_name).get('name') == new_name + return info(new_name).get("name") == new_name def current(sam=False): - ''' + """ Get the username that salt-minion is running under. If salt-minion is running as a service it should return the Local System account. If salt is running from a command prompt it should return the username that started the @@ -1044,20 +1055,20 @@ def current(sam=False): .. code-block:: bash salt '*' user.current - ''' + """ try: if sam: user_name = win32api.GetUserNameEx(win32con.NameSamCompatible) else: user_name = win32api.GetUserName() except pywintypes.error as exc: - log.error('Failed to get current user') - log.error('nbr: %s', exc.winerror) - log.error('ctx: %s', exc.funcname) - log.error('msg: %s', exc.strerror) - raise CommandExecutionError('Failed to get current user', info=exc) + log.error("Failed to get current user") + log.error("nbr: %s", exc.winerror) + log.error("ctx: %s", exc.funcname) + log.error("msg: %s", exc.strerror) + raise CommandExecutionError("Failed to get current user", info=exc) if not user_name: - raise CommandExecutionError('Failed to get current user') + raise CommandExecutionError("Failed to get current user") return user_name diff --git a/salt/modules/win_wua.py b/salt/modules/win_wua.py index ab922f7fb13..dad87182926 100644 --- a/salt/modules/win_wua.py +++ b/salt/modules/win_wua.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing Windows Updates using the Windows Update Agent. List updates on the system using the following functions: @@ -54,9 +54,10 @@ Group Policy using the ``lgpo`` module. .. versionadded:: 2015.8.0 :depends: salt.utils.win_update -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -67,8 +68,10 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six + try: import win32com.client + HAS_PYWIN32 = True except ImportError: HAS_PYWIN32 = False @@ -76,36 +79,38 @@ except ImportError: log = logging.getLogger(__name__) __func_alias__ = { - 'list_': 'list', + "list_": "list", } def __virtual__(): - ''' + """ Only works on Windows systems with PyWin32 - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'WUA: Only available on Window systems' + return False, "WUA: Only available on Window systems" if not HAS_PYWIN32: - return False, 'WUA: Requires PyWin32 libraries' + return False, "WUA: Requires PyWin32 libraries" if not salt.utils.win_update.HAS_PYWIN32: - return False, 'WUA: Missing Libraries required by salt.utils.win_update' + return False, "WUA: Missing Libraries required by salt.utils.win_update" return True -def available(software=True, - drivers=True, - summary=False, - skip_installed=True, - skip_hidden=True, - skip_mandatory=False, - skip_reboot=False, - categories=None, - severities=None,): - ''' +def available( + software=True, + drivers=True, + summary=False, + skip_installed=True, + skip_hidden=True, + skip_mandatory=False, + skip_reboot=False, + categories=None, + severities=None, +): + """ .. versionadded:: 2017.7.0 List updates that match the passed criteria. This allows for more filter @@ -114,26 +119,26 @@ def available(software=True, Args: software (bool): - Include software updates in the results (default is True) + Include software updates in the results. Default is ``True`` drivers (bool): - Include driver updates in the results (default is True) + Include driver updates in the results. Default is ``True`` summary (bool): - True: Return a summary of updates available for each category. - False (default): Return a detailed list of available updates. skip_installed (bool): - Skip updates that are already installed. Default is False. + Skip updates that are already installed. Default is ``True`` skip_hidden (bool): - Skip updates that have been hidden. Default is True. + Skip updates that have been hidden. Default is ``True`` skip_mandatory (bool): - Skip mandatory updates. Default is False. + Skip mandatory updates. Default is ``False`` skip_reboot (bool): - Skip updates that require a reboot. Default is False. + Skip updates that require a reboot. Default is ``False`` categories (list): Specify the categories to list. Must be passed as a list. All @@ -220,24 +225,29 @@ def available(software=True, # A summary of all Feature Packs and Windows 8.1 Updates salt '*' win_wua.available categories=["Feature Packs","Windows 8.1"] summary=True - ''' + """ # Create a Windows Update Agent instance wua = salt.utils.win_update.WindowsUpdateAgent() # Look for available updates = wua.available( - skip_hidden=skip_hidden, skip_installed=skip_installed, - skip_mandatory=skip_mandatory, skip_reboot=skip_reboot, - software=software, drivers=drivers, categories=categories, - severities=severities) + skip_hidden=skip_hidden, + skip_installed=skip_installed, + skip_mandatory=skip_mandatory, + skip_reboot=skip_reboot, + software=software, + drivers=drivers, + categories=categories, + severities=severities, + ) # Return results as Summary or Details return updates.summary() if summary else updates.list() def get(name, download=False, install=False): - ''' + """ .. versionadded:: 2017.7.0 Returns details for the named update @@ -308,7 +318,7 @@ def get(name, download=False, install=False): # Could possibly return multiple results # Not all updates have an associated KB salt '*' win_wua.get 'Microsoft Camera Codec Pack' - ''' + """ # Create a Windows Update Agent instance wua = salt.utils.win_update.WindowsUpdateAgent() @@ -319,24 +329,26 @@ def get(name, download=False, install=False): # Download if download or install: - ret['Download'] = wua.download(updates) + ret["Download"] = wua.download(updates) # Install if install: - ret['Install'] = wua.install(updates) + ret["Install"] = wua.install(updates) return ret if ret else updates.list() -def list(software=True, - drivers=False, - summary=False, - skip_installed=True, - categories=None, - severities=None, - download=False, - install=False): - ''' +def list( + software=True, + drivers=False, + summary=False, + skip_installed=True, + categories=None, + severities=None, + download=False, + install=False, +): + """ .. versionadded:: 2017.7.0 Returns a detailed list of available updates or a summary. If download or @@ -345,29 +357,29 @@ def list(software=True, Args: software (bool): - Include software updates in the results (default is True) + Include software updates in the results. Default is ``True`` drivers (bool): - Include driver updates in the results (default is False) + Include driver updates in the results. Default is ``False`` summary (bool): - True: Return a summary of updates available for each category. - False (default): Return a detailed list of available updates. skip_installed (bool): - Skip installed updates in the results (default is False) + Skip installed updates in the results. Default is ``True`` download (bool): (Overrides reporting functionality) Download the list of updates returned by this function. Run this function first with ``download=False`` to see what will be downloaded, then set - ``download=True`` to download the updates. + ``download=True`` to download the updates. Default is ``False`` install (bool): (Overrides reporting functionality) Install the list of updates returned by this function. Run this function first with ``install=False`` to see what will be installed, then set - ``install=True`` to install the updates. + ``install=True`` to install the updates. Default is ``False`` categories (list): Specify the categories to list. Must be passed as a list. All @@ -454,24 +466,28 @@ def list(software=True, # A summary of all Feature Packs and Windows 8.1 Updates salt '*' win_wua.list categories=['Feature Packs','Windows 8.1'] summary=True - ''' + """ # Create a Windows Update Agent instance wua = salt.utils.win_update.WindowsUpdateAgent() # Search for Update - updates = wua.available(skip_installed=skip_installed, software=software, - drivers=drivers, categories=categories, - severities=severities) + updates = wua.available( + skip_installed=skip_installed, + software=software, + drivers=drivers, + categories=categories, + severities=severities, + ) ret = {} # Download if download or install: - ret['Download'] = wua.download(updates) + ret["Download"] = wua.download(updates) # Install if install: - ret['Install'] = wua.install(updates) + ret["Install"] = wua.install(updates) if not ret: return updates.summary() if summary else updates.list() @@ -480,7 +496,7 @@ def list(software=True, def download(names): - ''' + """ .. versionadded:: 2017.7.0 Downloads updates that match the list of passed identifiers. It's easier to @@ -507,7 +523,7 @@ def download(names): # Normal Usage salt '*' win_wua.download names=['12345678-abcd-1234-abcd-1234567890ab', 'KB2131233'] - ''' + """ # Create a Windows Update Agent instance wua = salt.utils.win_update.WindowsUpdateAgent() @@ -515,7 +531,7 @@ def download(names): updates = wua.search(names) if updates.count() == 0: - raise CommandExecutionError('No updates found') + raise CommandExecutionError("No updates found") # Make sure it's a list so count comparison is correct if isinstance(names, six.string_types): @@ -525,14 +541,15 @@ def download(names): names = [six.text_type(names)] if updates.count() > len(names): - raise CommandExecutionError('Multiple updates found, names need to be ' - 'more specific') + raise CommandExecutionError( + "Multiple updates found, names need to be " "more specific" + ) return wua.download(updates) def install(names): - ''' + """ .. versionadded:: 2017.7.0 Installs updates that match the list of identifiers. It may be easier to use @@ -559,7 +576,7 @@ def install(names): # Normal Usage salt '*' win_wua.install KB12323211 - ''' + """ # Create a Windows Update Agent instance wua = salt.utils.win_update.WindowsUpdateAgent() @@ -567,7 +584,7 @@ def install(names): updates = wua.search(names) if updates.count() == 0: - raise CommandExecutionError('No updates found') + raise CommandExecutionError("No updates found") # Make sure it's a list so count comparison is correct if isinstance(names, six.string_types): @@ -577,14 +594,15 @@ def install(names): names = [six.text_type(names)] if updates.count() > len(names): - raise CommandExecutionError('Multiple updates found, names need to be ' - 'more specific') + raise CommandExecutionError( + "Multiple updates found, names need to be " "more specific" + ) return wua.install(updates) def uninstall(names): - ''' + """ .. versionadded:: 2017.7.0 Uninstall updates. @@ -609,7 +627,7 @@ def uninstall(names): # As a list salt '*' win_wua.uninstall guid=['12345678-abcd-1234-abcd-1234567890ab', 'KB1231231'] - ''' + """ # Create a Windows Update Agent instance wua = salt.utils.win_update.WindowsUpdateAgent() @@ -617,19 +635,21 @@ def uninstall(names): updates = wua.search(names) if updates.count() == 0: - raise CommandExecutionError('No updates found') + raise CommandExecutionError("No updates found") return wua.uninstall(updates) -def set_wu_settings(level=None, - recommended=None, - featured=None, - elevated=None, - msupdate=None, - day=None, - time=None): - ''' +def set_wu_settings( + level=None, + recommended=None, + featured=None, + elevated=None, + msupdate=None, + day=None, + time=None, +): + """ Change Windows Update settings. If no parameters are passed, the current value will be returned. @@ -699,7 +719,7 @@ def set_wu_settings(level=None, .. code-block:: bash salt '*' win_wua.set_wu_settings level=4 recommended=True featured=False - ''' + """ # The AutomaticUpdateSettings.Save() method used in this function does not # work on Windows 10 / Server 2016. It is called in throughout this function # like this: @@ -722,13 +742,13 @@ def set_wu_settings(level=None, # commandlet for working with the the UUP. Perhaps there will be something # forthcoming. The `win_lgpo` module might be an option for changing the # Windows Update settings using local group policy. - ret = {'Success': True} + ret = {"Success": True} # Initialize the PyCom system with salt.utils.winapi.Com(): # Create an AutoUpdate object - obj_au = win32com.client.Dispatch('Microsoft.Update.AutoUpdate') + obj_au = win32com.client.Dispatch("Microsoft.Update.AutoUpdate") # Create an AutoUpdate Settings Object obj_au_settings = obj_au.Settings @@ -738,75 +758,83 @@ def set_wu_settings(level=None, obj_au_settings.NotificationLevel = int(level) result = obj_au_settings.Save() if result is None: - ret['Level'] = level + ret["Level"] = level else: - ret['Comment'] = "Settings failed to save. Check permissions." - ret['Success'] = False + ret["Comment"] = "Settings failed to save. Check permissions." + ret["Success"] = False if recommended is not None: obj_au_settings.IncludeRecommendedUpdates = recommended result = obj_au_settings.Save() if result is None: - ret['Recommended'] = recommended + ret["Recommended"] = recommended else: - ret['Comment'] = "Settings failed to save. Check permissions." - ret['Success'] = False + ret["Comment"] = "Settings failed to save. Check permissions." + ret["Success"] = False if featured is not None: obj_au_settings.FeaturedUpdatesEnabled = featured result = obj_au_settings.Save() if result is None: - ret['Featured'] = featured + ret["Featured"] = featured else: - ret['Comment'] = "Settings failed to save. Check permissions." - ret['Success'] = False + ret["Comment"] = "Settings failed to save. Check permissions." + ret["Success"] = False if elevated is not None: obj_au_settings.NonAdministratorsElevated = elevated result = obj_au_settings.Save() if result is None: - ret['Elevated'] = elevated + ret["Elevated"] = elevated else: - ret['Comment'] = "Settings failed to save. Check permissions." - ret['Success'] = False + ret["Comment"] = "Settings failed to save. Check permissions." + ret["Success"] = False if day is not None: # Check that day is valid - days = {'Everyday': 0, - 'Sunday': 1, - 'Monday': 2, - 'Tuesday': 3, - 'Wednesday': 4, - 'Thursday': 5, - 'Friday': 6, - 'Saturday': 7} + days = { + "Everyday": 0, + "Sunday": 1, + "Monday": 2, + "Tuesday": 3, + "Wednesday": 4, + "Thursday": 5, + "Friday": 6, + "Saturday": 7, + } if day not in days: - ret['Comment'] = "Day needs to be one of the following: Everyday," \ - "Monday, Tuesday, Wednesday, Thursday, Friday, " \ - "Saturday" - ret['Success'] = False + ret["Comment"] = ( + "Day needs to be one of the following: Everyday," + "Monday, Tuesday, Wednesday, Thursday, Friday, " + "Saturday" + ) + ret["Success"] = False else: # Set the numeric equivalent for the day setting obj_au_settings.ScheduledInstallationDay = days[day] result = obj_au_settings.Save() if result is None: - ret['Day'] = day + ret["Day"] = day else: - ret['Comment'] = "Settings failed to save. Check permissions." - ret['Success'] = False + ret["Comment"] = "Settings failed to save. Check permissions." + ret["Success"] = False if time is not None: # Check for time as a string: if the time is not quoted, yaml will # treat it as an integer if not isinstance(time, six.string_types): - ret['Comment'] = "Time argument needs to be a string; it may need to"\ - "be quoted. Passed {0}. Time not set.".format(time) - ret['Success'] = False + ret["Comment"] = ( + "Time argument needs to be a string; it may need to" + "be quoted. Passed {0}. Time not set.".format(time) + ) + ret["Success"] = False # Check for colon in the time - elif ':' not in time: - ret['Comment'] = "Time argument needs to be in 00:00 format." \ - " Passed {0}. Time not set.".format(time) - ret['Success'] = False + elif ":" not in time: + ret["Comment"] = ( + "Time argument needs to be in 00:00 format." + " Passed {0}. Time not set.".format(time) + ) + ret["Success"] = False else: # Split the time by : t = time.split(":") @@ -814,16 +842,16 @@ def set_wu_settings(level=None, obj_au_settings.FeaturedUpdatesEnabled = t[0] result = obj_au_settings.Save() if result is None: - ret['Time'] = time + ret["Time"] = time else: - ret['Comment'] = "Settings failed to save. Check permissions." - ret['Success'] = False + ret["Comment"] = "Settings failed to save. Check permissions." + ret["Success"] = False if msupdate is not None: # Microsoft Update requires special handling # First load the MS Update Service Manager with salt.utils.winapi.Com(): - obj_sm = win32com.client.Dispatch('Microsoft.Update.ServiceManager') + obj_sm = win32com.client.Dispatch("Microsoft.Update.ServiceManager") # Give it a bogus name obj_sm.ClientApplicationID = "My App" @@ -831,13 +859,15 @@ def set_wu_settings(level=None, if msupdate: # msupdate is true, so add it to the services try: - obj_sm.AddService2('7971f918-a847-4430-9279-4a52d1efe18d', 7, '') - ret['msupdate'] = msupdate + obj_sm.AddService2("7971f918-a847-4430-9279-4a52d1efe18d", 7, "") + ret["msupdate"] = msupdate except Exception as error: # pylint: disable=broad-except - hr, msg, exc, arg = error.args # pylint: disable=unpacking-non-sequence,unbalanced-tuple-unpacking + # pylint: disable=unpacking-non-sequence,unbalanced-tuple-unpacking + (hr, msg, exc, arg,) = error.args + # pylint: enable=unpacking-non-sequence,unbalanced-tuple-unpacking # Consider checking for -2147024891 (0x80070005) Access Denied - ret['Comment'] = "Failed with failure code: {0}".format(exc[5]) - ret['Success'] = False + ret["Comment"] = "Failed with failure code: {0}".format(exc[5]) + ret["Success"] = False else: # msupdate is false, so remove it from the services # check to see if the update is there or the RemoveService function @@ -845,26 +875,28 @@ def set_wu_settings(level=None, if _get_msupdate_status(): # Service found, remove the service try: - obj_sm.RemoveService('7971f918-a847-4430-9279-4a52d1efe18d') - ret['msupdate'] = msupdate + obj_sm.RemoveService("7971f918-a847-4430-9279-4a52d1efe18d") + ret["msupdate"] = msupdate except Exception as error: # pylint: disable=broad-except - hr, msg, exc, arg = error.args # pylint: disable=unpacking-non-sequence,unbalanced-tuple-unpacking + # pylint: disable=unpacking-non-sequence,unbalanced-tuple-unpacking + (hr, msg, exc, arg,) = error.args + # pylint: enable=unpacking-non-sequence,unbalanced-tuple-unpacking # Consider checking for the following # -2147024891 (0x80070005) Access Denied # -2145091564 (0x80248014) Service Not Found (shouldn't get # this with the check for _get_msupdate_status above - ret['Comment'] = "Failed with failure code: {0}".format(exc[5]) - ret['Success'] = False + ret["Comment"] = "Failed with failure code: {0}".format(exc[5]) + ret["Success"] = False else: - ret['msupdate'] = msupdate + ret["msupdate"] = msupdate - ret['Reboot'] = get_needs_reboot() + ret["Reboot"] = get_needs_reboot() return ret def get_wu_settings(): - ''' + """ Get current Windows Update settings. Returns: @@ -911,59 +943,63 @@ def get_wu_settings(): .. code-block:: bash salt '*' win_wua.get_wu_settings - ''' + """ ret = {} - day = ['Every Day', - 'Sunday', - 'Monday', - 'Tuesday', - 'Wednesday', - 'Thursday', - 'Friday', - 'Saturday'] + day = [ + "Every Day", + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + ] # Initialize the PyCom system with salt.utils.winapi.Com(): # Create an AutoUpdate object - obj_au = win32com.client.Dispatch('Microsoft.Update.AutoUpdate') + obj_au = win32com.client.Dispatch("Microsoft.Update.AutoUpdate") # Create an AutoUpdate Settings Object obj_au_settings = obj_au.Settings # Populate the return dictionary - ret['Featured Updates'] = obj_au_settings.FeaturedUpdatesEnabled - ret['Group Policy Required'] = obj_au_settings.Required - ret['Microsoft Update'] = _get_msupdate_status() - ret['Needs Reboot'] = get_needs_reboot() - ret['Non Admins Elevated'] = obj_au_settings.NonAdministratorsElevated - ret['Notification Level'] = obj_au_settings.NotificationLevel - ret['Read Only'] = obj_au_settings.ReadOnly - ret['Recommended Updates'] = obj_au_settings.IncludeRecommendedUpdates - ret['Scheduled Day'] = day[obj_au_settings.ScheduledInstallationDay] + ret["Featured Updates"] = obj_au_settings.FeaturedUpdatesEnabled + ret["Group Policy Required"] = obj_au_settings.Required + ret["Microsoft Update"] = _get_msupdate_status() + ret["Needs Reboot"] = get_needs_reboot() + ret["Non Admins Elevated"] = obj_au_settings.NonAdministratorsElevated + ret["Notification Level"] = obj_au_settings.NotificationLevel + ret["Read Only"] = obj_au_settings.ReadOnly + ret["Recommended Updates"] = obj_au_settings.IncludeRecommendedUpdates + ret["Scheduled Day"] = day[obj_au_settings.ScheduledInstallationDay] # Scheduled Installation Time requires special handling to return the time # in the right format if obj_au_settings.ScheduledInstallationTime < 10: - ret['Scheduled Time'] = '0{0}:00'.\ - format(obj_au_settings.ScheduledInstallationTime) + ret["Scheduled Time"] = "0{0}:00".format( + obj_au_settings.ScheduledInstallationTime + ) else: - ret['Scheduled Time'] = '{0}:00'.\ - format(obj_au_settings.ScheduledInstallationTime) + ret["Scheduled Time"] = "{0}:00".format( + obj_au_settings.ScheduledInstallationTime + ) return ret def _get_msupdate_status(): - ''' + """ Check to see if Microsoft Update is Enabled Return Boolean - ''' + """ # To get the status of Microsoft Update we actually have to check the # Microsoft Update Service Manager # Initialize the PyCom system with salt.utils.winapi.Com(): # Create a ServiceManager Object - obj_sm = win32com.client.Dispatch('Microsoft.Update.ServiceManager') + obj_sm = win32com.client.Dispatch("Microsoft.Update.ServiceManager") # Return a collection of loaded Services col_services = obj_sm.Services @@ -971,14 +1007,14 @@ def _get_msupdate_status(): # Loop through the collection to find the Microsoft Udpate Service # If it exists return True otherwise False for service in col_services: - if service.name == 'Microsoft Update': + if service.name == "Microsoft Update": return True return False def get_needs_reboot(): - ''' + """ Determines if the system needs to be rebooted. Returns: @@ -990,5 +1026,5 @@ def get_needs_reboot(): .. code-block:: bash salt '*' win_wua.get_needs_reboot - ''' + """ return salt.utils.win_update.needs_reboot() diff --git a/salt/modules/win_wusa.py b/salt/modules/win_wusa.py index c0e13c3f79b..eb02e2838a3 100644 --- a/salt/modules/win_wusa.py +++ b/salt/modules/win_wusa.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Microsoft Update files management via wusa.exe :maintainer: Thomas Lemarchand @@ -7,10 +7,11 @@ Microsoft Update files management via wusa.exe :depends: PowerShell .. versionadded:: 2018.3.4 -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import logging import os @@ -21,60 +22,60 @@ from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'wusa' +__virtualname__ = "wusa" def __virtual__(): - ''' + """ Load only on Windows - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Only available on Windows systems' + return False, "Only available on Windows systems" - powershell_info = __salt__['cmd.shell_info'](shell='powershell', list_modules=False) - if not powershell_info['installed']: - return False, 'PowerShell not available' + powershell_info = __salt__["cmd.shell_info"](shell="powershell", list_modules=False) + if not powershell_info["installed"]: + return False, "PowerShell not available" return __virtualname__ def _pshell_json(cmd, cwd=None): - ''' + """ Execute the desired powershell command and ensure that it returns data in JSON format and load that into python - ''' - if 'convertto-json' not in cmd.lower(): - cmd = '{0} | ConvertTo-Json'.format(cmd) - log.debug('PowerShell: %s', cmd) - ret = __salt__['cmd.run_all'](cmd, shell='powershell', cwd=cwd) + """ + if "convertto-json" not in cmd.lower(): + cmd = "{0} | ConvertTo-Json".format(cmd) + log.debug("PowerShell: %s", cmd) + ret = __salt__["cmd.run_all"](cmd, shell="powershell", cwd=cwd) - if 'pid' in ret: - del ret['pid'] + if "pid" in ret: + del ret["pid"] - if ret.get('stderr', ''): - error = ret['stderr'].splitlines()[0] + if ret.get("stderr", ""): + error = ret["stderr"].splitlines()[0] raise CommandExecutionError(error, info=ret) - if 'retcode' not in ret or ret['retcode'] != 0: + if "retcode" not in ret or ret["retcode"] != 0: # run_all logs an error to log.error, fail hard back to the user raise CommandExecutionError( - 'Issue executing PowerShell {0}'.format(cmd), info=ret) + "Issue executing PowerShell {0}".format(cmd), info=ret + ) # Sometimes Powershell returns an empty string, which isn't valid JSON - if ret['stdout'] == '': - ret['stdout'] = '{}' + if ret["stdout"] == "": + ret["stdout"] = "{}" try: - ret = salt.utils.json.loads(ret['stdout'], strict=False) + ret = salt.utils.json.loads(ret["stdout"], strict=False) except ValueError: - raise CommandExecutionError( - 'No JSON results from PowerShell', info=ret) + raise CommandExecutionError("No JSON results from PowerShell", info=ret) return ret def is_installed(name): - ''' + """ Check if a specific KB is installed. Args: @@ -90,14 +91,19 @@ def is_installed(name): .. code-block:: bash salt '*' wusa.is_installed KB123456 - ''' - return __salt__['cmd.retcode'](cmd='Get-HotFix -Id {0}'.format(name), - shell='powershell', - ignore_retcode=True) == 0 + """ + return ( + __salt__["cmd.retcode"]( + cmd="Get-HotFix -Id {0}".format(name), + shell="powershell", + ignore_retcode=True, + ) + == 0 + ) def install(path, restart=False): - ''' + """ Install a KB from a .msu file. Args: @@ -122,32 +128,36 @@ def install(path, restart=False): .. code-block:: bash salt '*' wusa.install C:/temp/KB123456.msu - ''' + """ # Build the command - cmd = ['wusa.exe', path, '/quiet'] + cmd = ["wusa.exe", path, "/quiet"] if restart: - cmd.append('/forcerestart') + cmd.append("/forcerestart") else: - cmd.append('/norestart') + cmd.append("/norestart") # Run the command - ret_code = __salt__['cmd.retcode'](cmd, ignore_retcode=True) + ret_code = __salt__["cmd.retcode"](cmd, ignore_retcode=True) # Check the ret_code file_name = os.path.basename(path) - errors = {2359302: '{0} is already installed'.format(file_name), - 3010: '{0} correctly installed but server reboot is needed to complete installation'.format(file_name), - 87: 'Unknown error'} + errors = { + 2359302: "{0} is already installed".format(file_name), + 3010: "{0} correctly installed but server reboot is needed to complete installation".format( + file_name + ), + 87: "Unknown error", + } if ret_code in errors: raise CommandExecutionError(errors[ret_code], ret_code) elif ret_code: - raise CommandExecutionError('Unknown error: {0}'.format(ret_code)) + raise CommandExecutionError("Unknown error: {0}".format(ret_code)) return True def uninstall(path, restart=False): - ''' + """ Uninstall a specific KB. Args: @@ -176,39 +186,40 @@ def uninstall(path, restart=False): # or salt '*' wusa.uninstall C:/temp/KB123456.msu - ''' + """ # Build the command - cmd = ['wusa.exe', '/uninstall', '/quiet'] + cmd = ["wusa.exe", "/uninstall", "/quiet"] kb = os.path.splitext(os.path.basename(path))[0] if os.path.exists(path): cmd.append(path) else: - cmd.append( - '/kb:{0}'.format(kb[2:] if kb.lower().startswith('kb') else kb)) + cmd.append("/kb:{0}".format(kb[2:] if kb.lower().startswith("kb") else kb)) if restart: - cmd.append('/forcerestart') + cmd.append("/forcerestart") else: - cmd.append('/norestart') + cmd.append("/norestart") # Run the command - ret_code = __salt__['cmd.retcode'](cmd, ignore_retcode=True) + ret_code = __salt__["cmd.retcode"](cmd, ignore_retcode=True) # Check the ret_code # If you pass /quiet and specify /kb, you'll always get retcode 87 if there # is an error. Use the actual file to get a more descriptive error - errors = {-2145116156: '{0} does not support uninstall'.format(kb), - 2359303: '{0} not installed'.format(kb), - 87: 'Unknown error. Try specifying an .msu file'} + errors = { + -2145116156: "{0} does not support uninstall".format(kb), + 2359303: "{0} not installed".format(kb), + 87: "Unknown error. Try specifying an .msu file", + } if ret_code in errors: raise CommandExecutionError(errors[ret_code], ret_code) elif ret_code: - raise CommandExecutionError('Unknown error: {0}'.format(ret_code)) + raise CommandExecutionError("Unknown error: {0}".format(ret_code)) return True def list(): - ''' + """ Get a list of updates installed on the machine Returns: @@ -219,9 +230,9 @@ def list(): .. code-block:: bash salt '*' wusa.list - ''' + """ kbs = [] - ret = _pshell_json('Get-HotFix | Select HotFixID') + ret = _pshell_json("Get-HotFix | Select HotFixID") for item in ret: - kbs.append(item['HotFixID']) + kbs.append(item["HotFixID"]) return kbs diff --git a/salt/modules/winrepo.py b/salt/modules/winrepo.py index 86570317ef1..bfcf6039f19 100644 --- a/salt/modules/winrepo.py +++ b/salt/modules/winrepo.py @@ -1,72 +1,70 @@ # -*- coding: utf-8 -*- -r''' +r""" Module to manage Windows software repo on a Standalone Minion ``file_client: local`` must be set in the minion config file. For documentation on Salt's Windows Repo feature, see :ref:`here <windows-package-manager>`. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import os +import salt.loader + # Import salt libs import salt.output +import salt.template import salt.utils.functools +import salt.utils.gitfs import salt.utils.path import salt.utils.platform -import salt.loader -import salt.template from salt.exceptions import CommandExecutionError, SaltRenderError # All the "unused" imports here are needed for the imported winrepo runner code # pylint: disable=unused-import -from salt.runners.winrepo import ( - genrepo as _genrepo, - update_git_repos as _update_git_repos, - PER_REMOTE_OVERRIDES, - PER_REMOTE_ONLY, - GLOBAL_ONLY -) -from salt.ext import six -import salt.utils.gitfs -import salt.utils.msgpack as msgpack +from salt.runners.winrepo import GLOBAL_ONLY, PER_REMOTE_ONLY, PER_REMOTE_OVERRIDES +from salt.runners.winrepo import genrepo as _genrepo +from salt.runners.winrepo import update_git_repos as _update_git_repos + # pylint: enable=unused-import log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'winrepo' +__virtualname__ = "winrepo" def __virtual__(): - ''' + """ Set the winrepo module if the OS is Windows - ''' + """ if salt.utils.platform.is_windows(): global _genrepo, _update_git_repos _genrepo = salt.utils.functools.namespaced_function(_genrepo, globals()) - _update_git_repos = \ - salt.utils.functools.namespaced_function(_update_git_repos, globals()) + _update_git_repos = salt.utils.functools.namespaced_function( + _update_git_repos, globals() + ) return __virtualname__ - return (False, 'This module only works on Windows.') + return (False, "This module only works on Windows.") -def _get_local_repo_dir(saltenv='base'): - winrepo_source_dir = __opts__['winrepo_source_dir'] +def _get_local_repo_dir(saltenv="base"): + winrepo_source_dir = __opts__["winrepo_source_dir"] dirs = [] dirs.append(salt.syspaths.CACHE_DIR) - dirs.extend(['minion', 'files']) + dirs.extend(["minion", "files"]) dirs.append(saltenv) - dirs.extend(winrepo_source_dir[7:].strip('/').split('/')) + dirs.extend(winrepo_source_dir[7:].strip("/").split("/")) return os.sep.join(dirs) def genrepo(): - r''' + r""" Generate winrepo_cachefile based on sls files in the winrepo_dir CLI Example: @@ -74,12 +72,12 @@ def genrepo(): .. code-block:: bash salt-call winrepo.genrepo - ''' + """ return _genrepo(opts=__opts__, fire_event=False) def update_git_repos(clean=False): - ''' + """ Checkout git repos containing :ref:`Windows Software Package Definitions <windows-package-manager>`. @@ -113,17 +111,17 @@ def update_git_repos(clean=False): .. code-block:: bash salt-call winrepo.update_git_repos - ''' - if not salt.utils.path.which('git'): + """ + if not salt.utils.path.which("git"): raise CommandExecutionError( - 'Git for Windows is not installed, or not configured to be ' - 'accessible from the Command Prompt' + "Git for Windows is not installed, or not configured to be " + "accessible from the Command Prompt" ) return _update_git_repos(opts=__opts__, clean=clean, masterless=True) -def show_sls(name, saltenv='base'): - r''' +def show_sls(name, saltenv="base"): + r""" .. versionadded:: 2015.8.0 Display the rendered software definition from a specific sls file in the @@ -161,7 +159,7 @@ def show_sls(name, saltenv='base'): salt '*' winrepo.show_sls gvim salt '*' winrepo.show_sls test.npp salt '*' winrepo.show_sls C:\test\gvim.sls - ''' + """ # Passed a filename if os.path.exists(name): sls_file = name @@ -172,20 +170,20 @@ def show_sls(name, saltenv='base'): repo = _get_local_repo_dir(saltenv) # Add the sls file name to the path - repo = repo.split('\\') - definition = name.split('.') + repo = repo.split("\\") + definition = name.split(".") repo.extend(definition) # Check for the sls file by name - sls_file = '{0}.sls'.format(os.sep.join(repo)) + sls_file = "{0}.sls".format(os.sep.join(repo)) if not os.path.exists(sls_file): # Maybe it's a directory with an init.sls - sls_file = '{0}\\init.sls'.format(os.sep.join(repo)) + sls_file = "{0}\\init.sls".format(os.sep.join(repo)) if not os.path.exists(sls_file): # It's neither, return - return 'Software definition {0} not found'.format(name) + return "Software definition {0} not found".format(name) # Load the renderer renderers = salt.loader.render(__opts__, __salt__) @@ -196,15 +194,16 @@ def show_sls(name, saltenv='base'): config = salt.template.compile_template( sls_file, renderers, - __opts__['renderer'], - __opts__['renderer_blacklist'], - __opts__['renderer_whitelist']) + __opts__["renderer"], + __opts__["renderer_blacklist"], + __opts__["renderer_whitelist"], + ) # Return the error if any except SaltRenderError as exc: - log.debug('Failed to compile %s.', sls_file) - log.debug('Error: %s.', exc) - config['Message'] = 'Failed to compile {0}'.format(sls_file) - config['Error'] = '{0}'.format(exc) + log.debug("Failed to compile %s.", sls_file) + log.debug("Error: %s.", exc) + config["Message"] = "Failed to compile {0}".format(sls_file) + config["Error"] = "{0}".format(exc) return config diff --git a/salt/modules/wordpress.py b/salt/modules/wordpress.py index 9b20a25b8e4..a6c319421e7 100644 --- a/salt/modules/wordpress.py +++ b/salt/modules/wordpress.py @@ -1,25 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" This module is used to manage Wordpress installations :depends: wp binary from http://wp-cli.org/ -''' +""" # Import Python Modules from __future__ import absolute_import, print_function, unicode_literals + import collections # Import Salt Modules import salt.utils.path from salt.ext.six.moves import map -Plugin = collections.namedtuple('Plugin', 'name status update versino') +Plugin = collections.namedtuple("Plugin", "name status update versino") def __virtual__(): - if salt.utils.path.which('wp'): + if salt.utils.path.which("wp"): return True - return False + return (False, "Missing dependency: wp") def _get_plugins(stuff): @@ -27,7 +28,7 @@ def _get_plugins(stuff): def list_plugins(path, user): - ''' + """ List plugins in an installed wordpress path path @@ -41,18 +42,16 @@ def list_plugins(path, user): .. code-block:: bash salt '*' wordpress.list_plugins /var/www/html apache - ''' + """ ret = [] - resp = __salt__['cmd.shell'](( - 'wp --path={0} plugin list' - ).format(path), runas=user) - for line in resp.split('\n')[1:]: - ret.append(line.split('\t')) + resp = __salt__["cmd.shell"](("wp --path={0} plugin list").format(path), runas=user) + for line in resp.split("\n")[1:]: + ret.append(line.split("\t")) return [plugin.__dict__ for plugin in map(_get_plugins, ret)] def show_plugin(name, path, user): - ''' + """ Show a plugin in a wordpress install and check if it is installed name @@ -69,21 +68,21 @@ def show_plugin(name, path, user): .. code-block:: bash salt '*' wordpress.show_plugin HyperDB /var/www/html apache - ''' - ret = {'name': name} - resp = __salt__['cmd.shell'](( - 'wp --path={0} plugin status {1}' - ).format(path, name), runas=user).split('\n') + """ + ret = {"name": name} + resp = __salt__["cmd.shell"]( + ("wp --path={0} plugin status {1}").format(path, name), runas=user + ).split("\n") for line in resp: - if 'Status' in line: - ret['status'] = line.split(' ')[-1].lower() - elif 'Version' in line: - ret['version'] = line.split(' ')[-1].lower() + if "Status" in line: + ret["status"] = line.split(" ")[-1].lower() + elif "Version" in line: + ret["version"] = line.split(" ")[-1].lower() return ret def activate(name, path, user): - ''' + """ Activate a wordpress plugin name @@ -100,23 +99,23 @@ def activate(name, path, user): .. code-block:: bash salt '*' wordpress.activate HyperDB /var/www/html apache - ''' + """ check = show_plugin(name, path, user) - if check['status'] == 'active': + if check["status"] == "active": # already active return None - resp = __salt__['cmd.shell'](( - 'wp --path={0} plugin activate {1}' - ).format(path, name), runas=user) - if 'Success' in resp: + resp = __salt__["cmd.shell"]( + ("wp --path={0} plugin activate {1}").format(path, name), runas=user + ) + if "Success" in resp: return True - elif show_plugin(name, path, user)['status'] == 'active': + elif show_plugin(name, path, user)["status"] == "active": return True return False def deactivate(name, path, user): - ''' + """ Deactivate a wordpress plugin name @@ -133,23 +132,23 @@ def deactivate(name, path, user): .. code-block:: bash salt '*' wordpress.deactivate HyperDB /var/www/html apache - ''' + """ check = show_plugin(name, path, user) - if check['status'] == 'inactive': + if check["status"] == "inactive": # already inactive return None - resp = __salt__['cmd.shell'](( - 'wp --path={0} plugin deactivate {1}' - ).format(path, name), runas=user) - if 'Success' in resp: + resp = __salt__["cmd.shell"]( + ("wp --path={0} plugin deactivate {1}").format(path, name), runas=user + ) + if "Success" in resp: return True - elif show_plugin(name, path, user)['status'] == 'inactive': + elif show_plugin(name, path, user)["status"] == "inactive": return True return False def is_installed(path, user=None): - ''' + """ Check if wordpress is installed and setup path @@ -163,17 +162,17 @@ def is_installed(path, user=None): .. code-block:: bash salt '*' wordpress.is_installed /var/www/html apache - ''' - retcode = __salt__['cmd.retcode'](( - 'wp --path={0} core is-installed' - ).format(path), runas=user) + """ + retcode = __salt__["cmd.retcode"]( + ("wp --path={0} core is-installed").format(path), runas=user + ) if retcode == 0: return True return False def install(path, user, admin_user, admin_password, admin_email, title, url): - ''' + """ Run the initial setup functions for a wordpress install path @@ -203,22 +202,18 @@ def install(path, user, admin_user, admin_password, admin_email, title, url): salt '*' wordpress.install /var/www/html apache dwallace password123 \ dwallace@example.com "Daniel's Awesome Blog" https://blog.dwallace.com - ''' - retcode = __salt__['cmd.retcode'](( - 'wp --path={0} core install ' - '--title="{1}" ' - '--admin_user={2} ' - "--admin_password='{3}' " - '--admin_email={4} ' - '--url={5}' - ).format( - path, - title, - admin_user, - admin_password, - admin_email, - url - ), runas=user) + """ + retcode = __salt__["cmd.retcode"]( + ( + "wp --path={0} core install " + '--title="{1}" ' + "--admin_user={2} " + "--admin_password='{3}' " + "--admin_email={4} " + "--url={5}" + ).format(path, title, admin_user, admin_password, admin_email, url), + runas=user, + ) if retcode == 0: return True diff --git a/salt/modules/x509.py b/salt/modules/x509.py index 1cdd912bfb1..c278723038d 100644 --- a/salt/modules/x509.py +++ b/salt/modules/x509.py @@ -1,117 +1,126 @@ # -*- coding: utf-8 -*- -''' +""" Manage X509 certificates .. versionadded:: 2015.8.0 :depends: M2Crypto -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import os -import logging -import hashlib -import glob -import random -import ctypes -import tempfile -import re -import datetime +from __future__ import absolute_import, print_function, unicode_literals + import ast +import ctypes +import datetime +import glob +import hashlib +import logging +import os +import random +import re import sys +import tempfile + +import salt.exceptions +import salt.utils.data # Import salt libs import salt.utils.files import salt.utils.path -import salt.utils.stringutils -import salt.utils.data import salt.utils.platform -import salt.exceptions +import salt.utils.stringutils from salt.ext import six -from salt.utils.odict import OrderedDict + # pylint: disable=import-error,redefined-builtin from salt.ext.six.moves import range + # pylint: enable=import-error,redefined-builtin from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS +from salt.utils.odict import OrderedDict # Import 3rd Party Libs try: import M2Crypto + HAS_M2 = True except ImportError: HAS_M2 = False try: import OpenSSL + HAS_OPENSSL = True except ImportError: HAS_OPENSSL = False -__virtualname__ = 'x509' +__virtualname__ = "x509" log = logging.getLogger(__name__) # pylint: disable=invalid-name -EXT_NAME_MAPPINGS = OrderedDict([ - ('basicConstraints', 'X509v3 Basic Constraints'), - ('keyUsage', 'X509v3 Key Usage'), - ('extendedKeyUsage', 'X509v3 Extended Key Usage'), - ('subjectKeyIdentifier', 'X509v3 Subject Key Identifier'), - ('authorityKeyIdentifier', 'X509v3 Authority Key Identifier'), - ('issuserAltName', 'X509v3 Issuer Alternative Name'), - ('authorityInfoAccess', 'X509v3 Authority Info Access'), - ('subjectAltName', 'X509v3 Subject Alternative Name'), - ('crlDistributionPoints', 'X509v3 CRL Distribution Points'), - ('issuingDistributionPoint', 'X509v3 Issuing Distribution Point'), - ('certificatePolicies', 'X509v3 Certificate Policies'), - ('policyConstraints', 'X509v3 Policy Constraints'), - ('inhibitAnyPolicy', 'X509v3 Inhibit Any Policy'), - ('nameConstraints', 'X509v3 Name Constraints'), - ('noCheck', 'X509v3 OCSP No Check'), - ('nsComment', 'Netscape Comment'), - ('nsCertType', 'Netscape Certificate Type'), -]) +EXT_NAME_MAPPINGS = OrderedDict( + [ + ("basicConstraints", "X509v3 Basic Constraints"), + ("keyUsage", "X509v3 Key Usage"), + ("extendedKeyUsage", "X509v3 Extended Key Usage"), + ("subjectKeyIdentifier", "X509v3 Subject Key Identifier"), + ("authorityKeyIdentifier", "X509v3 Authority Key Identifier"), + ("issuserAltName", "X509v3 Issuer Alternative Name"), + ("authorityInfoAccess", "X509v3 Authority Info Access"), + ("subjectAltName", "X509v3 Subject Alternative Name"), + ("crlDistributionPoints", "X509v3 CRL Distribution Points"), + ("issuingDistributionPoint", "X509v3 Issuing Distribution Point"), + ("certificatePolicies", "X509v3 Certificate Policies"), + ("policyConstraints", "X509v3 Policy Constraints"), + ("inhibitAnyPolicy", "X509v3 Inhibit Any Policy"), + ("nameConstraints", "X509v3 Name Constraints"), + ("noCheck", "X509v3 OCSP No Check"), + ("nsComment", "Netscape Comment"), + ("nsCertType", "Netscape Certificate Type"), + ] +) CERT_DEFAULTS = { - 'days_valid': 365, - 'version': 3, - 'serial_bits': 64, - 'algorithm': 'sha256' + "days_valid": 365, + "version": 3, + "serial_bits": 64, + "algorithm": "sha256", } def __virtual__(): - ''' + """ only load this module if m2crypto is available - ''' + """ if HAS_M2: return __virtualname__ else: - return (False, 'Could not load x509 module, m2crypto unavailable') + return (False, "Could not load x509 module, m2crypto unavailable") class _Ctx(ctypes.Structure): - ''' + """ This is part of an ugly hack to fix an ancient bug in M2Crypto https://bugzilla.osafoundation.org/show_bug.cgi?id=7530#c13 - ''' + """ + # pylint: disable=too-few-public-methods _fields_ = [ - ('flags', ctypes.c_int), - ('issuer_cert', ctypes.c_void_p), - ('subject_cert', ctypes.c_void_p), - ('subject_req', ctypes.c_void_p), - ('crl', ctypes.c_void_p), - ('db_meth', ctypes.c_void_p), - ('db', ctypes.c_void_p), + ("flags", ctypes.c_int), + ("issuer_cert", ctypes.c_void_p), + ("subject_cert", ctypes.c_void_p), + ("subject_req", ctypes.c_void_p), + ("crl", ctypes.c_void_p), + ("db_meth", ctypes.c_void_p), + ("db", ctypes.c_void_p), ] def _fix_ctx(m2_ctx, issuer=None): - ''' + """ This is part of an ugly hack to fix an ancient bug in M2Crypto https://bugzilla.osafoundation.org/show_bug.cgi?id=7530#c13 - ''' + """ ctx = _Ctx.from_address(int(m2_ctx)) # pylint: disable=no-member ctx.flags = 0 @@ -125,15 +134,16 @@ def _fix_ctx(m2_ctx, issuer=None): def _new_extension(name, value, critical=0, issuer=None, _pyfree=1): - ''' + """ Create new X509_Extension, this is required because M2Crypto doesn't support getting the publickeyidentifier from the issuer to create the authoritykeyidentifier extension. - ''' - if name == 'subjectKeyIdentifier' and \ - value.strip('0123456789abcdefABCDEF:') is not '': - raise salt.exceptions.SaltInvocationError( - 'value must be precomputed hash') + """ + if ( + name == "subjectKeyIdentifier" + and value.strip("0123456789abcdefABCDEF:") is not "" + ): + raise salt.exceptions.SaltInvocationError("value must be precomputed hash") # ensure name and value are bytes name = salt.utils.stringutils.to_str(name) @@ -143,23 +153,25 @@ def _new_extension(name, value, critical=0, issuer=None, _pyfree=1): ctx = M2Crypto.m2.x509v3_set_nconf() _fix_ctx(ctx, issuer) if ctx is None: - raise MemoryError( - 'Not enough memory when creating a new X509 extension') + raise MemoryError("Not enough memory when creating a new X509 extension") x509_ext_ptr = M2Crypto.m2.x509v3_ext_conf(None, ctx, name, value) lhash = None except AttributeError: - lhash = M2Crypto.m2.x509v3_lhash() # pylint: disable=no-member - ctx = M2Crypto.m2.x509v3_set_conf_lhash( - lhash) # pylint: disable=no-member + lhash = M2Crypto.m2.x509v3_lhash() # pylint: disable=no-member + ctx = M2Crypto.m2.x509v3_set_conf_lhash(lhash) # pylint: disable=no-member # ctx not zeroed _fix_ctx(ctx, issuer) x509_ext_ptr = M2Crypto.m2.x509v3_ext_conf( - lhash, ctx, name, value) # pylint: disable=no-member + lhash, ctx, name, value + ) # pylint: disable=no-member # ctx,lhash freed if x509_ext_ptr is None: raise M2Crypto.X509.X509Error( - "Cannot create X509_Extension with name '{0}' and value '{1}'".format(name, value)) + "Cannot create X509_Extension with name '{0}' and value '{1}'".format( + name, value + ) + ) x509_ext = M2Crypto.X509.X509_Extension(x509_ext_ptr, _pyfree) x509_ext.set_critical(critical) return x509_ext @@ -169,29 +181,27 @@ def _new_extension(name, value, critical=0, issuer=None, _pyfree=1): # getting Extensions from CSRs. # https://github.com/martinpaljak/M2Crypto/issues/63 def _parse_openssl_req(csr_filename): - ''' + """ Parses openssl command line output, this is a workaround for M2Crypto's inability to get them from CSR objects. - ''' - if not salt.utils.path.which('openssl'): - raise salt.exceptions.SaltInvocationError( - 'openssl binary not found in path' - ) - cmd = ('openssl req -text -noout -in {0}'.format(csr_filename)) + """ + if not salt.utils.path.which("openssl"): + raise salt.exceptions.SaltInvocationError("openssl binary not found in path") + cmd = "openssl req -text -noout -in {0}".format(csr_filename) - output = __salt__['cmd.run_stdout'](cmd) + output = __salt__["cmd.run_stdout"](cmd) - output = re.sub(r': rsaEncryption', ':', output) - output = re.sub(r'[0-9a-f]{2}:', '', output) + output = re.sub(r": rsaEncryption", ":", output) + output = re.sub(r"[0-9a-f]{2}:", "", output) return salt.utils.data.decode(salt.utils.yaml.safe_load(output)) def _get_csr_extensions(csr): - ''' + """ Returns a list of dicts containing the name, value and critical value of any extension contained in a csr object. - ''' + """ ret = OrderedDict() csrtempfile = tempfile.NamedTemporaryFile() @@ -199,10 +209,8 @@ def _get_csr_extensions(csr): csrtempfile.flush() csryaml = _parse_openssl_req(csrtempfile.name) csrtempfile.close() - if csryaml and 'Requested Extensions' in \ - csryaml['Certificate Request']['Data']: - csrexts = \ - csryaml['Certificate Request']['Data']['Requested Extensions'] + if csryaml and "Requested Extensions" in csryaml["Certificate Request"]["Data"]: + csrexts = csryaml["Certificate Request"]["Data"]["Requested Extensions"] if not csrexts: return ret @@ -218,110 +226,110 @@ def _get_csr_extensions(csr): # openssl CLI # pylint: disable=too-many-branches,too-many-locals def _parse_openssl_crl(crl_filename): - ''' + """ Parses openssl command line output, this is a workaround for M2Crypto's inability to get them from CSR objects. - ''' - if not salt.utils.path.which('openssl'): - raise salt.exceptions.SaltInvocationError( - 'openssl binary not found in path' - ) - cmd = ('openssl crl -text -noout -in {0}'.format(crl_filename)) + """ + if not salt.utils.path.which("openssl"): + raise salt.exceptions.SaltInvocationError("openssl binary not found in path") + cmd = "openssl crl -text -noout -in {0}".format(crl_filename) - output = __salt__['cmd.run_stdout'](cmd) + output = __salt__["cmd.run_stdout"](cmd) crl = {} - for line in output.split('\n'): + for line in output.split("\n"): line = line.strip() - if line.startswith('Version '): - crl['Version'] = line.replace('Version ', '') - if line.startswith('Signature Algorithm: '): - crl['Signature Algorithm'] = line.replace( - 'Signature Algorithm: ', '') - if line.startswith('Issuer: '): - line = line.replace('Issuer: ', '') + if line.startswith("Version "): + crl["Version"] = line.replace("Version ", "") + if line.startswith("Signature Algorithm: "): + crl["Signature Algorithm"] = line.replace("Signature Algorithm: ", "") + if line.startswith("Issuer: "): + line = line.replace("Issuer: ", "") subject = {} - for sub_entry in line.split('/'): - if '=' in sub_entry: - sub_entry = sub_entry.split('=') + for sub_entry in line.split("/"): + if "=" in sub_entry: + sub_entry = sub_entry.split("=") subject[sub_entry[0]] = sub_entry[1] - crl['Issuer'] = subject - if line.startswith('Last Update: '): - crl['Last Update'] = line.replace('Last Update: ', '') + crl["Issuer"] = subject + if line.startswith("Last Update: "): + crl["Last Update"] = line.replace("Last Update: ", "") last_update = datetime.datetime.strptime( - crl['Last Update'], "%b %d %H:%M:%S %Y %Z") - crl['Last Update'] = last_update.strftime("%Y-%m-%d %H:%M:%S") - if line.startswith('Next Update: '): - crl['Next Update'] = line.replace('Next Update: ', '') + crl["Last Update"], "%b %d %H:%M:%S %Y %Z" + ) + crl["Last Update"] = last_update.strftime("%Y-%m-%d %H:%M:%S") + if line.startswith("Next Update: "): + crl["Next Update"] = line.replace("Next Update: ", "") next_update = datetime.datetime.strptime( - crl['Next Update'], "%b %d %H:%M:%S %Y %Z") - crl['Next Update'] = next_update.strftime("%Y-%m-%d %H:%M:%S") - if line.startswith('Revoked Certificates:'): + crl["Next Update"], "%b %d %H:%M:%S %Y %Z" + ) + crl["Next Update"] = next_update.strftime("%Y-%m-%d %H:%M:%S") + if line.startswith("Revoked Certificates:"): break - if 'No Revoked Certificates.' in output: - crl['Revoked Certificates'] = [] + if "No Revoked Certificates." in output: + crl["Revoked Certificates"] = [] return crl - output = output.split('Revoked Certificates:')[1] - output = output.split('Signature Algorithm:')[0] + output = output.split("Revoked Certificates:")[1] + output = output.split("Signature Algorithm:")[0] rev = [] - for revoked in output.split('Serial Number: '): + for revoked in output.split("Serial Number: "): if not revoked.strip(): continue - rev_sn = revoked.split('\n')[0].strip() - revoked = rev_sn + ':\n' + '\n'.join(revoked.split('\n')[1:]) + rev_sn = revoked.split("\n")[0].strip() + revoked = rev_sn + ":\n" + "\n".join(revoked.split("\n")[1:]) rev_yaml = salt.utils.data.decode(salt.utils.yaml.safe_load(revoked)) # pylint: disable=unused-variable for rev_item, rev_values in six.iteritems(rev_yaml): # pylint: enable=unused-variable - if 'Revocation Date' in rev_values: + if "Revocation Date" in rev_values: rev_date = datetime.datetime.strptime( - rev_values['Revocation Date'], "%b %d %H:%M:%S %Y %Z") - rev_values['Revocation Date'] = rev_date.strftime( - "%Y-%m-%d %H:%M:%S") + rev_values["Revocation Date"], "%b %d %H:%M:%S %Y %Z" + ) + rev_values["Revocation Date"] = rev_date.strftime("%Y-%m-%d %H:%M:%S") rev.append(rev_yaml) - crl['Revoked Certificates'] = rev + crl["Revoked Certificates"] = rev return crl + + # pylint: enable=too-many-branches def _get_signing_policy(name): - policies = __salt__['pillar.get']('x509_signing_policies', None) + policies = __salt__["pillar.get"]("x509_signing_policies", None) if policies: signing_policy = policies.get(name) if signing_policy: return signing_policy - return __salt__['config.get']('x509_signing_policies', {}).get(name) + return __salt__["config.get"]("x509_signing_policies", {}).get(name) def _pretty_hex(hex_str): - ''' + """ Nicely formats hex strings - ''' + """ if len(hex_str) % 2 != 0: - hex_str = '0' + hex_str - return ':'.join( - [hex_str[i:i + 2] for i in range(0, len(hex_str), 2)]).upper() + hex_str = "0" + hex_str + return ":".join([hex_str[i : i + 2] for i in range(0, len(hex_str), 2)]).upper() def _dec2hex(decval): - ''' + """ Converts decimal values to nicely formatted hex strings - ''' - return _pretty_hex('{0:X}'.format(decval)) + """ + return _pretty_hex("{0:X}".format(decval)) def _isfile(path): - ''' + """ A wrapper around os.path.isfile that ignores ValueError exceptions which can be raised if the input to isfile is too long. - ''' + """ try: return os.path.isfile(path) except ValueError: @@ -330,10 +338,10 @@ def _isfile(path): def _text_or_file(input_): - ''' + """ Determines if input is a path to a file, or a string with the content to be parsed. - ''' + """ if _isfile(input_): with salt.utils.files.fopen(input_) as fp_: return salt.utils.stringutils.to_str(fp_.read()) @@ -342,9 +350,9 @@ def _text_or_file(input_): def _parse_subject(subject): - ''' + """ Returns a dict containing all values in an X509 Subject - ''' + """ ret = {} nids = [] for nid_name, nid_num in six.iteritems(subject.nid): @@ -362,80 +370,83 @@ def _parse_subject(subject): def _get_certificate_obj(cert): - ''' + """ Returns a certificate object based on PEM text. - ''' + """ if isinstance(cert, M2Crypto.X509.X509): return cert text = _text_or_file(cert) - text = get_pem_entry(text, pem_type='CERTIFICATE') + text = get_pem_entry(text, pem_type="CERTIFICATE") return M2Crypto.X509.load_cert_string(text) def _get_private_key_obj(private_key, passphrase=None): - ''' + """ Returns a private key object based on PEM text. - ''' + """ private_key = _text_or_file(private_key) - private_key = get_pem_entry(private_key, pem_type='(?:RSA )?PRIVATE KEY') + private_key = get_pem_entry(private_key, pem_type="(?:RSA )?PRIVATE KEY") rsaprivkey = M2Crypto.RSA.load_key_string( - private_key, callback=_passphrase_callback(passphrase)) + private_key, callback=_passphrase_callback(passphrase) + ) evpprivkey = M2Crypto.EVP.PKey() evpprivkey.assign_rsa(rsaprivkey) return evpprivkey def _passphrase_callback(passphrase): - ''' + """ Returns a callback function used to supply a passphrase for private keys - ''' + """ + def f(*args): return salt.utils.stringutils.to_bytes(passphrase) + return f def _get_request_obj(csr): - ''' + """ Returns a CSR object based on PEM text. - ''' + """ text = _text_or_file(csr) - text = get_pem_entry(text, pem_type='CERTIFICATE REQUEST') + text = get_pem_entry(text, pem_type="CERTIFICATE REQUEST") return M2Crypto.X509.load_request_string(text) def _get_pubkey_hash(cert): - ''' + """ Returns the sha1 hash of the modulus of a public key in a cert Used for generating subject key identifiers - ''' + """ sha_hash = hashlib.sha1(cert.get_pubkey().get_modulus()).hexdigest() return _pretty_hex(sha_hash) def _keygen_callback(): - ''' + """ Replacement keygen callback function which silences the output sent to stdout by the default keygen function - ''' + """ return def _make_regex(pem_type): - ''' + """ Dynamically generate a regex to match pem_type - ''' + """ return re.compile( r"\s*(?P<pem_header>-----BEGIN {0}-----)\s+" r"(?:(?P<proc_type>Proc-Type: 4,ENCRYPTED)\s*)?" r"(?:(?P<dek_info>DEK-Info: (?:DES-[3A-Z\-]+,[0-9A-F]{{16}}|[0-9A-Z\-]+,[0-9A-F]{{32}}))\s*)?" r"(?P<pem_body>.+?)\s+(?P<pem_footer>" r"-----END {0}-----)\s*".format(pem_type), - re.DOTALL + re.DOTALL, ) def get_pem_entry(text, pem_type=None): - ''' + """ Returns a properly formatted PEM string from the input text fixing any whitespace or line-break issues @@ -452,39 +463,43 @@ def get_pem_entry(text, pem_type=None): .. code-block:: bash salt '*' x509.get_pem_entry "-----BEGIN CERTIFICATE REQUEST-----MIICyzCC Ar8CAQI...-----END CERTIFICATE REQUEST" - ''' + """ text = _text_or_file(text) # Replace encoded newlines - text = text.replace('\\n', '\n') + text = text.replace("\\n", "\n") _match = None - if len(text.splitlines()) == 1 and text.startswith( - '-----') and text.endswith('-----'): + if ( + len(text.splitlines()) == 1 + and text.startswith("-----") + and text.endswith("-----") + ): # mine.get returns the PEM on a single line, we fix this pem_fixed = [] pem_temp = text while len(pem_temp) > 0: - if pem_temp.startswith('-----'): + if pem_temp.startswith("-----"): # Grab ----(.*)---- blocks - pem_fixed.append(pem_temp[:pem_temp.index('-----', 5) + 5]) - pem_temp = pem_temp[pem_temp.index('-----', 5) + 5:] + pem_fixed.append(pem_temp[: pem_temp.index("-----", 5) + 5]) + pem_temp = pem_temp[pem_temp.index("-----", 5) + 5 :] else: # grab base64 chunks - if pem_temp[:64].count('-') == 0: + if pem_temp[:64].count("-") == 0: pem_fixed.append(pem_temp[:64]) pem_temp = pem_temp[64:] else: - pem_fixed.append(pem_temp[:pem_temp.index('-')]) - pem_temp = pem_temp[pem_temp.index('-'):] + pem_fixed.append(pem_temp[: pem_temp.index("-")]) + pem_temp = pem_temp[pem_temp.index("-") :] text = "\n".join(pem_fixed) - _dregex = _make_regex('[0-9A-Z ]+') - errmsg = 'PEM text not valid:\n{0}'.format(text) + _dregex = _make_regex("[0-9A-Z ]+") + errmsg = "PEM text not valid:\n{0}".format(text) if pem_type: _dregex = _make_regex(pem_type) - errmsg = ('PEM does not contain a single entry of type {0}:\n' - '{1}'.format(pem_type, text)) + errmsg = "PEM does not contain a single entry of type {0}:\n" "{1}".format( + pem_type, text + ) for _match in _dregex.finditer(text): if _match: @@ -492,30 +507,30 @@ def get_pem_entry(text, pem_type=None): if not _match: raise salt.exceptions.SaltInvocationError(errmsg) _match_dict = _match.groupdict() - pem_header = _match_dict['pem_header'] - proc_type = _match_dict['proc_type'] - dek_info = _match_dict['dek_info'] - pem_footer = _match_dict['pem_footer'] - pem_body = _match_dict['pem_body'] + pem_header = _match_dict["pem_header"] + proc_type = _match_dict["proc_type"] + dek_info = _match_dict["dek_info"] + pem_footer = _match_dict["pem_footer"] + pem_body = _match_dict["pem_body"] # Remove all whitespace from body - pem_body = ''.join(pem_body.split()) + pem_body = "".join(pem_body.split()) # Generate correctly formatted pem - ret = pem_header + '\n' + ret = pem_header + "\n" if proc_type: - ret += proc_type + '\n' + ret += proc_type + "\n" if dek_info: - ret += dek_info + '\n' + '\n' + ret += dek_info + "\n" + "\n" for i in range(0, len(pem_body), 64): - ret += pem_body[i:i + 64] + '\n' - ret += pem_footer + '\n' + ret += pem_body[i : i + 64] + "\n" + ret += pem_footer + "\n" - return salt.utils.stringutils.to_bytes(ret, encoding='ascii') + return salt.utils.stringutils.to_bytes(ret, encoding="ascii") def get_pem_entries(glob_path): - ''' + """ Returns a dict containing PEM entries in files matching a glob glob_path: @@ -526,7 +541,7 @@ def get_pem_entries(glob_path): .. code-block:: bash salt '*' x509.get_pem_entries "/etc/pki/*.crt" - ''' + """ ret = {} for path in glob.glob(glob_path): @@ -540,7 +555,7 @@ def get_pem_entries(glob_path): def read_certificate(certificate): - ''' + """ Returns a dict containing details of a certificate. Input can be a PEM string or file path. @@ -553,29 +568,29 @@ def read_certificate(certificate): .. code-block:: bash salt '*' x509.read_certificate /etc/pki/mycert.crt - ''' + """ cert = _get_certificate_obj(certificate) ret = { # X509 Version 3 has a value of 2 in the field. # Version 2 has a value of 1. # https://tools.ietf.org/html/rfc5280#section-4.1.2.1 - 'Version': cert.get_version() + 1, + "Version": cert.get_version() + 1, # Get size returns in bytes. The world thinks of key sizes in bits. - 'Key Size': cert.get_pubkey().size() * 8, - 'Serial Number': _dec2hex(cert.get_serial_number()), - 'SHA-256 Finger Print': _pretty_hex(cert.get_fingerprint(md='sha256')), - 'MD5 Finger Print': _pretty_hex(cert.get_fingerprint(md='md5')), - 'SHA1 Finger Print': _pretty_hex(cert.get_fingerprint(md='sha1')), - 'Subject': _parse_subject(cert.get_subject()), - 'Subject Hash': _dec2hex(cert.get_subject().as_hash()), - 'Issuer': _parse_subject(cert.get_issuer()), - 'Issuer Hash': _dec2hex(cert.get_issuer().as_hash()), - 'Not Before': - cert.get_not_before().get_datetime().strftime('%Y-%m-%d %H:%M:%S'), - 'Not After': - cert.get_not_after().get_datetime().strftime('%Y-%m-%d %H:%M:%S'), - 'Public Key': get_public_key(cert) + "Key Size": cert.get_pubkey().size() * 8, + "Serial Number": _dec2hex(cert.get_serial_number()), + "SHA-256 Finger Print": _pretty_hex(cert.get_fingerprint(md="sha256")), + "MD5 Finger Print": _pretty_hex(cert.get_fingerprint(md="md5")), + "SHA1 Finger Print": _pretty_hex(cert.get_fingerprint(md="sha1")), + "Subject": _parse_subject(cert.get_subject()), + "Subject Hash": _dec2hex(cert.get_subject().as_hash()), + "Issuer": _parse_subject(cert.get_issuer()), + "Issuer Hash": _dec2hex(cert.get_issuer().as_hash()), + "Not Before": cert.get_not_before() + .get_datetime() + .strftime("%Y-%m-%d %H:%M:%S"), + "Not After": cert.get_not_after().get_datetime().strftime("%Y-%m-%d %H:%M:%S"), + "Public Key": get_public_key(cert), } exts = OrderedDict() @@ -584,17 +599,17 @@ def read_certificate(certificate): name = ext.get_name() val = ext.get_value() if ext.get_critical(): - val = 'critical ' + val + val = "critical " + val exts[name] = val if exts: - ret['X509v3 Extensions'] = exts + ret["X509v3 Extensions"] = exts return ret def read_certificates(glob_path): - ''' + """ Returns a dict containing details of all certificates matching a glob glob_path: @@ -605,7 +620,7 @@ def read_certificates(glob_path): .. code-block:: bash salt '*' x509.read_certificates "/etc/pki/*.crt" - ''' + """ ret = {} for path in glob.glob(glob_path): @@ -619,7 +634,7 @@ def read_certificates(glob_path): def read_csr(csr): - ''' + """ Returns a dict containing details of a certificate request. :depends: - OpenSSL command line tool @@ -632,27 +647,26 @@ def read_csr(csr): .. code-block:: bash salt '*' x509.read_csr /etc/pki/mycert.csr - ''' + """ csr = _get_request_obj(csr) ret = { # X509 Version 3 has a value of 2 in the field. # Version 2 has a value of 1. # https://tools.ietf.org/html/rfc5280#section-4.1.2.1 - 'Version': csr.get_version() + 1, + "Version": csr.get_version() + 1, # Get size returns in bytes. The world thinks of key sizes in bits. - 'Subject': _parse_subject(csr.get_subject()), - 'Subject Hash': _dec2hex(csr.get_subject().as_hash()), - 'Public Key Hash': hashlib.sha1(csr.get_pubkey().get_modulus())\ - .hexdigest() + "Subject": _parse_subject(csr.get_subject()), + "Subject Hash": _dec2hex(csr.get_subject().as_hash()), + "Public Key Hash": hashlib.sha1(csr.get_pubkey().get_modulus()).hexdigest(), } - ret['X509v3 Extensions'] = _get_csr_extensions(csr) + ret["X509v3 Extensions"] = _get_csr_extensions(csr) return ret def read_crl(crl): - ''' + """ Returns a dict containing details of a certificate revocation list. Input can be a PEM string or file path. @@ -666,9 +680,9 @@ def read_crl(crl): .. code-block:: bash salt '*' x509.read_crl /etc/pki/mycrl.crl - ''' + """ text = _text_or_file(crl) - text = get_pem_entry(text, pem_type='X509 CRL') + text = get_pem_entry(text, pem_type="X509 CRL") crltempfile = tempfile.NamedTemporaryFile() crltempfile.write(salt.utils.stringutils.to_str(text)) @@ -680,7 +694,7 @@ def read_crl(crl): def get_public_key(key, passphrase=None, asObj=False): - ''' + """ Returns a string containing the public key in PEM format. key: @@ -692,16 +706,16 @@ def get_public_key(key, passphrase=None, asObj=False): .. code-block:: bash salt '*' x509.get_public_key /etc/pki/mycert.cer - ''' + """ if isinstance(key, M2Crypto.X509.X509): rsa = key.get_pubkey().get_rsa() - text = b'' + text = b"" else: text = _text_or_file(key) text = get_pem_entry(text) - if text.startswith(b'-----BEGIN PUBLIC KEY-----'): + if text.startswith(b"-----BEGIN PUBLIC KEY-----"): if not asObj: return text bio = M2Crypto.BIO.MemoryBuffer() @@ -709,16 +723,18 @@ def get_public_key(key, passphrase=None, asObj=False): rsa = M2Crypto.RSA.load_pub_key_bio(bio) bio = M2Crypto.BIO.MemoryBuffer() - if text.startswith(b'-----BEGIN CERTIFICATE-----'): + if text.startswith(b"-----BEGIN CERTIFICATE-----"): cert = M2Crypto.X509.load_cert_string(text) rsa = cert.get_pubkey().get_rsa() - if text.startswith(b'-----BEGIN CERTIFICATE REQUEST-----'): + if text.startswith(b"-----BEGIN CERTIFICATE REQUEST-----"): csr = M2Crypto.X509.load_request_string(text) rsa = csr.get_pubkey().get_rsa() - if (text.startswith(b'-----BEGIN PRIVATE KEY-----') or - text.startswith(b'-----BEGIN RSA PRIVATE KEY-----')): + if text.startswith(b"-----BEGIN PRIVATE KEY-----") or text.startswith( + b"-----BEGIN RSA PRIVATE KEY-----" + ): rsa = M2Crypto.RSA.load_key_string( - text, callback=_passphrase_callback(passphrase)) + text, callback=_passphrase_callback(passphrase) + ) if asObj: evppubkey = M2Crypto.EVP.PKey() @@ -730,7 +746,7 @@ def get_public_key(key, passphrase=None, asObj=False): def get_private_key_size(private_key, passphrase=None): - ''' + """ Returns the bit length of a private key in PEM format. private_key: @@ -741,12 +757,12 @@ def get_private_key_size(private_key, passphrase=None): .. code-block:: bash salt '*' x509.get_private_key_size /etc/pki/mycert.key - ''' + """ return _get_private_key_obj(private_key, passphrase).size() * 8 def write_pem(text, path, overwrite=True, pem_type=None): - ''' + """ Writes out a PEM string fixing any formatting or whitespace issues before writing. @@ -771,39 +787,46 @@ def write_pem(text, path, overwrite=True, pem_type=None): .. code-block:: bash salt '*' x509.write_pem "-----BEGIN CERTIFICATE-----MIIGMzCCBBugA..." path=/etc/pki/mycert.crt - ''' + """ with salt.utils.files.set_umask(0o077): text = get_pem_entry(text, pem_type=pem_type) - _dhparams = '' - _private_key = '' - if pem_type and pem_type == 'CERTIFICATE' and os.path.isfile(path) and not overwrite: + _dhparams = "" + _private_key = "" + if ( + pem_type + and pem_type == "CERTIFICATE" + and os.path.isfile(path) + and not overwrite + ): _filecontents = _text_or_file(path) try: - _dhparams = get_pem_entry(_filecontents, 'DH PARAMETERS') + _dhparams = get_pem_entry(_filecontents, "DH PARAMETERS") except salt.exceptions.SaltInvocationError as err: log.debug("Error when getting DH PARAMETERS: %s", err) log.trace(err, exc_info=err) try: - _private_key = get_pem_entry(_filecontents, '(?:RSA )?PRIVATE KEY') + _private_key = get_pem_entry(_filecontents, "(?:RSA )?PRIVATE KEY") except salt.exceptions.SaltInvocationError as err: log.debug("Error when getting PRIVATE KEY: %s", err) log.trace(err, exc_info=err) - with salt.utils.files.fopen(path, 'w') as _fp: - if pem_type and pem_type == 'CERTIFICATE' and _private_key: + with salt.utils.files.fopen(path, "w") as _fp: + if pem_type and pem_type == "CERTIFICATE" and _private_key: _fp.write(salt.utils.stringutils.to_str(_private_key)) _fp.write(salt.utils.stringutils.to_str(text)) - if pem_type and pem_type == 'CERTIFICATE' and _dhparams: + if pem_type and pem_type == "CERTIFICATE" and _dhparams: _fp.write(salt.utils.stringutils.to_str(_dhparams)) - return 'PEM written to {0}'.format(path) + return "PEM written to {0}".format(path) -def create_private_key(path=None, - text=False, - bits=2048, - passphrase=None, - cipher='aes_128_cbc', - verbose=True): - ''' +def create_private_key( + path=None, + text=False, + bits=2048, + passphrase=None, + cipher="aes_128_cbc", + verbose=True, +): + """ Creates a private key in PEM format. path: @@ -833,13 +856,15 @@ def create_private_key(path=None, .. code-block:: bash salt '*' x509.create_private_key path=/etc/pki/mykey.key - ''' + """ if not path and not text: raise salt.exceptions.SaltInvocationError( - 'Either path or text must be specified.') + "Either path or text must be specified." + ) if path and text: raise salt.exceptions.SaltInvocationError( - 'Either path or text must be specified, not both.') + "Either path or text must be specified, not both." + ) if verbose: _callback_func = M2Crypto.RSA.keygen_callback @@ -852,27 +877,28 @@ def create_private_key(path=None, bio = M2Crypto.BIO.MemoryBuffer() if passphrase is None: cipher = None - rsa.save_key_bio( - bio, - cipher=cipher, - callback=_passphrase_callback(passphrase)) + rsa.save_key_bio(bio, cipher=cipher, callback=_passphrase_callback(passphrase)) if path: return write_pem( - text=bio.read_all(), - path=path, - pem_type='(?:RSA )?PRIVATE KEY' + text=bio.read_all(), path=path, pem_type="(?:RSA )?PRIVATE KEY" ) else: return salt.utils.stringutils.to_str(bio.read_all()) def create_crl( # pylint: disable=too-many-arguments,too-many-locals - path=None, text=False, signing_private_key=None, - signing_private_key_passphrase=None, - signing_cert=None, revoked=None, include_expired=False, - days_valid=100, digest=''): - ''' + path=None, + text=False, + signing_private_key=None, + signing_private_key_passphrase=None, + signing_cert=None, + revoked=None, + include_expired=False, + days_valid=100, + digest="", +): + """ Create a CRL :depends: - PyOpenSSL Python module @@ -938,7 +964,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals .. code-block:: bash salt '*' x509.create_crl path=/etc/pki/mykey.key signing_private_key=/etc/pki/ca.key signing_cert=/etc/pki/ca.crt revoked="{'compromized-web-key': {'certificate': '/etc/pki/certs/www1.crt', 'revocation_date': '2015-03-01 00:00:00'}}" - ''' + """ # pyOpenSSL is required for dealing with CSLs. Importing inside these # functions because Client operations like creating CRLs shouldn't require # pyOpenSSL Note due to current limitations in pyOpenSSL it is impossible @@ -946,7 +972,7 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals # soon: https://github.com/pyca/pyopenssl/pull/161 if not HAS_OPENSSL: raise salt.exceptions.SaltInvocationError( - 'Could not load OpenSSL module, OpenSSL unavailable' + "Could not load OpenSSL module, OpenSSL unavailable" ) crl = OpenSSL.crypto.CRL() @@ -954,78 +980,83 @@ def create_crl( # pylint: disable=too-many-arguments,too-many-locals revoked = [] for rev_item in revoked: - if 'certificate' in rev_item: - rev_cert = read_certificate(rev_item['certificate']) - rev_item['serial_number'] = rev_cert['Serial Number'] - rev_item['not_after'] = rev_cert['Not After'] + if "certificate" in rev_item: + rev_cert = read_certificate(rev_item["certificate"]) + rev_item["serial_number"] = rev_cert["Serial Number"] + rev_item["not_after"] = rev_cert["Not After"] - serial_number = rev_item['serial_number'].replace(':', '') + serial_number = rev_item["serial_number"].replace(":", "") # OpenSSL bindings requires this to be a non-unicode string serial_number = salt.utils.stringutils.to_bytes(serial_number) - if 'not_after' in rev_item and not include_expired: + if "not_after" in rev_item and not include_expired: not_after = datetime.datetime.strptime( - rev_item['not_after'], '%Y-%m-%d %H:%M:%S') + rev_item["not_after"], "%Y-%m-%d %H:%M:%S" + ) if datetime.datetime.now() > not_after: continue - if 'revocation_date' not in rev_item: - rev_item['revocation_date'] = datetime.datetime\ - .now().strftime('%Y-%m-%d %H:%M:%S') + if "revocation_date" not in rev_item: + rev_item["revocation_date"] = datetime.datetime.now().strftime( + "%Y-%m-%d %H:%M:%S" + ) rev_date = datetime.datetime.strptime( - rev_item['revocation_date'], '%Y-%m-%d %H:%M:%S') - rev_date = rev_date.strftime('%Y%m%d%H%M%SZ') + rev_item["revocation_date"], "%Y-%m-%d %H:%M:%S" + ) + rev_date = rev_date.strftime("%Y%m%d%H%M%SZ") rev_date = salt.utils.stringutils.to_bytes(rev_date) rev = OpenSSL.crypto.Revoked() rev.set_serial(salt.utils.stringutils.to_bytes(serial_number)) rev.set_rev_date(salt.utils.stringutils.to_bytes(rev_date)) - if 'reason' in rev_item: + if "reason" in rev_item: # Same here for OpenSSL bindings and non-unicode strings - reason = salt.utils.stringutils.to_str(rev_item['reason']) + reason = salt.utils.stringutils.to_str(rev_item["reason"]) rev.set_reason(reason) crl.add_revoked(rev) signing_cert = _text_or_file(signing_cert) cert = OpenSSL.crypto.load_certificate( - OpenSSL.crypto.FILETYPE_PEM, - get_pem_entry(signing_cert, pem_type='CERTIFICATE')) - signing_private_key = _get_private_key_obj(signing_private_key, - passphrase=signing_private_key_passphrase).as_pem(cipher=None) + OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_cert, pem_type="CERTIFICATE") + ) + signing_private_key = _get_private_key_obj( + signing_private_key, passphrase=signing_private_key_passphrase + ).as_pem(cipher=None) key = OpenSSL.crypto.load_privatekey( - OpenSSL.crypto.FILETYPE_PEM, - get_pem_entry(signing_private_key)) + OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_private_key) + ) export_kwargs = { - 'cert': cert, - 'key': key, - 'type': OpenSSL.crypto.FILETYPE_PEM, - 'days': days_valid + "cert": cert, + "key": key, + "type": OpenSSL.crypto.FILETYPE_PEM, + "days": days_valid, } if digest: - export_kwargs['digest'] = salt.utils.stringutils.to_bytes(digest) + export_kwargs["digest"] = salt.utils.stringutils.to_bytes(digest) else: - log.warning('No digest specified. The default md5 digest will be used.') + log.warning("No digest specified. The default md5 digest will be used.") try: crltext = crl.export(**export_kwargs) except (TypeError, ValueError): log.warning( - 'Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used.') - export_kwargs.pop('digest', None) + "Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used." + ) + export_kwargs.pop("digest", None) crltext = crl.export(**export_kwargs) if text: return crltext - return write_pem(text=crltext, path=path, pem_type='X509 CRL') + return write_pem(text=crltext, path=path, pem_type="X509 CRL") def sign_remote_certificate(argdic, **kwargs): - ''' + """ Request a certificate to be remotely signed according to a signing policy. argdic: @@ -1041,19 +1072,18 @@ def sign_remote_certificate(argdic, **kwargs): .. code-block:: bash salt '*' x509.sign_remote_certificate argdic="{'public_key': '/etc/pki/www.key', 'signing_policy': 'www'}" __pub_id='www1' - ''' - if 'signing_policy' not in argdic: - return 'signing_policy must be specified' + """ + if "signing_policy" not in argdic: + return "signing_policy must be specified" if not isinstance(argdic, dict): argdic = ast.literal_eval(argdic) signing_policy = {} - if 'signing_policy' in argdic: - signing_policy = _get_signing_policy(argdic['signing_policy']) + if "signing_policy" in argdic: + signing_policy = _get_signing_policy(argdic["signing_policy"]) if not signing_policy: - return 'Signing policy {0} does not exist.'.format( - argdic['signing_policy']) + return "Signing policy {0} does not exist.".format(argdic["signing_policy"]) if isinstance(signing_policy, list): dict_ = {} @@ -1061,16 +1091,16 @@ def sign_remote_certificate(argdic, **kwargs): dict_.update(item) signing_policy = dict_ - if 'minions' in signing_policy: - if '__pub_id' not in kwargs: - return 'minion sending this request could not be identified' - matcher = 'match.glob' - if '@' in signing_policy['minions']: - matcher = 'match.compound' - if not __salt__[matcher]( - signing_policy['minions'], kwargs['__pub_id']): - return '{0} not permitted to use signing policy {1}'.format( - kwargs['__pub_id'], argdic['signing_policy']) + if "minions" in signing_policy: + if "__pub_id" not in kwargs: + return "minion sending this request could not be identified" + matcher = "match.glob" + if "@" in signing_policy["minions"]: + matcher = "match.compound" + if not __salt__[matcher](signing_policy["minions"], kwargs["__pub_id"]): + return "{0} not permitted to use signing policy {1}".format( + kwargs["__pub_id"], argdic["signing_policy"] + ) try: return create_certificate(path=None, text=True, **argdic) @@ -1079,7 +1109,7 @@ def sign_remote_certificate(argdic, **kwargs): def get_signing_policy(signing_policy_name): - ''' + """ Returns the details of a names signing policy, including the text of the public key that will be used to sign it. Does not return the private key. @@ -1089,10 +1119,10 @@ def get_signing_policy(signing_policy_name): .. code-block:: bash salt '*' x509.get_signing_policy www - ''' + """ signing_policy = _get_signing_policy(signing_policy_name) if not signing_policy: - return 'Signing policy {0} does not exist.'.format(signing_policy_name) + return "Signing policy {0} does not exist.".format(signing_policy_name) if isinstance(signing_policy, list): dict_ = {} for item in signing_policy: @@ -1100,13 +1130,14 @@ def get_signing_policy(signing_policy_name): signing_policy = dict_ try: - del signing_policy['signing_private_key'] + del signing_policy["signing_private_key"] except KeyError: pass try: - signing_policy['signing_cert'] = get_pem_entry( - signing_policy['signing_cert'], 'CERTIFICATE') + signing_policy["signing_cert"] = get_pem_entry( + signing_policy["signing_cert"], "CERTIFICATE" + ) except KeyError: pass @@ -1114,9 +1145,8 @@ def get_signing_policy(signing_policy_name): # pylint: disable=too-many-locals,too-many-branches,too-many-statements -def create_certificate( - path=None, text=False, overwrite=True, ca_server=None, **kwargs): - ''' +def create_certificate(path=None, text=False, overwrite=True, ca_server=None, **kwargs): + """ Create an X509 certificate. path: @@ -1356,70 +1386,78 @@ def create_certificate( .. code-block:: bash salt '*' x509.create_certificate path=/etc/pki/myca.crt signing_private_key='/etc/pki/myca.key' csr='/etc/pki/myca.csr'} - ''' + """ - if not path and not text and \ - ('testrun' not in kwargs or kwargs['testrun'] is False): + if ( + not path + and not text + and ("testrun" not in kwargs or kwargs["testrun"] is False) + ): raise salt.exceptions.SaltInvocationError( - 'Either path or text must be specified.') + "Either path or text must be specified." + ) if path and text: raise salt.exceptions.SaltInvocationError( - 'Either path or text must be specified, not both.') + "Either path or text must be specified, not both." + ) - if 'public_key_passphrase' not in kwargs: - kwargs['public_key_passphrase'] = None + if "public_key_passphrase" not in kwargs: + kwargs["public_key_passphrase"] = None if ca_server: - if 'signing_policy' not in kwargs: + if "signing_policy" not in kwargs: raise salt.exceptions.SaltInvocationError( - 'signing_policy must be specified' - 'if requesting remote certificate from ca_server {0}.' - .format(ca_server)) - if 'csr' in kwargs: - kwargs['csr'] = get_pem_entry( - kwargs['csr'], - pem_type='CERTIFICATE REQUEST').replace('\n', '') - if 'public_key' in kwargs: + "signing_policy must be specified" + "if requesting remote certificate from ca_server {0}.".format(ca_server) + ) + if "csr" in kwargs: + kwargs["csr"] = get_pem_entry( + kwargs["csr"], pem_type="CERTIFICATE REQUEST" + ).replace("\n", "") + if "public_key" in kwargs: # Strip newlines to make passing through as cli functions easier - kwargs['public_key'] = salt.utils.stringutils.to_str(get_public_key( - kwargs['public_key'], - passphrase=kwargs['public_key_passphrase'])).replace('\n', '') + kwargs["public_key"] = salt.utils.stringutils.to_str( + get_public_key( + kwargs["public_key"], passphrase=kwargs["public_key_passphrase"] + ) + ).replace("\n", "") # Remove system entries in kwargs # Including listen_in and prerequired because they are not included # in STATE_INTERNAL_KEYWORDS # for salt 2014.7.2 - for ignore in list(_STATE_INTERNAL_KEYWORDS) + \ - ['listen_in', 'prerequired', '__prerequired__']: + for ignore in list(_STATE_INTERNAL_KEYWORDS) + [ + "listen_in", + "prerequired", + "__prerequired__", + ]: kwargs.pop(ignore, None) # TODO: Make timeout configurable in 3000 - certs = __salt__['publish.publish']( + certs = __salt__["publish.publish"]( tgt=ca_server, - fun='x509.sign_remote_certificate', + fun="x509.sign_remote_certificate", arg=salt.utils.data.decode_dict(kwargs, to_str=True), - timeout=30 + timeout=30, ) if not any(certs): raise salt.exceptions.SaltInvocationError( - 'ca_server did not respond' - ' salt master must permit peers to' - ' call the sign_remote_certificate function.') + "ca_server did not respond" + " salt master must permit peers to" + " call the sign_remote_certificate function." + ) cert_txt = certs[ca_server] if path: return write_pem( - text=cert_txt, - overwrite=overwrite, - path=path, - pem_type='CERTIFICATE' + text=cert_txt, overwrite=overwrite, path=path, pem_type="CERTIFICATE" ) else: return cert_txt signing_policy = {} - if 'signing_policy' in kwargs: - signing_policy = _get_signing_policy(kwargs['signing_policy']) + if "signing_policy" in kwargs: + signing_policy = _get_signing_policy(kwargs["signing_policy"]) if isinstance(signing_policy, list): dict_ = {} for item in signing_policy: @@ -1438,13 +1476,12 @@ def create_certificate( # X509 Version 3 has a value of 2 in the field. # Version 2 has a value of 1. # https://tools.ietf.org/html/rfc5280#section-4.1.2.1 - cert.set_version(kwargs['version'] - 1) + cert.set_version(kwargs["version"] - 1) # Random serial number if not specified - if 'serial_number' not in kwargs: - kwargs['serial_number'] = _dec2hex( - random.getrandbits(kwargs['serial_bits'])) - serial_number = int(kwargs['serial_number'].replace(':', ''), 16) + if "serial_number" not in kwargs: + kwargs["serial_number"] = _dec2hex(random.getrandbits(kwargs["serial_bits"])) + serial_number = int(kwargs["serial_number"].replace(":", ""), 16) # With Python3 we occasionally end up with an INT that is greater than a C # long max_value. This causes an overflow error due to a bug in M2Crypto. # See issue: https://gitlab.com/m2crypto/m2crypto/issues/232 @@ -1464,25 +1501,27 @@ def create_certificate( not_before = M2Crypto.m2.x509_get_not_before(cert.x509) not_after = M2Crypto.m2.x509_get_not_after(cert.x509) M2Crypto.m2.x509_gmtime_adj(not_before, 0) - M2Crypto.m2.x509_gmtime_adj(not_after, 60 * 60 * 24 * kwargs['days_valid']) + M2Crypto.m2.x509_gmtime_adj(not_after, 60 * 60 * 24 * kwargs["days_valid"]) # pylint: enable=no-member # If neither public_key or csr are included, this cert is self-signed - if 'public_key' not in kwargs and 'csr' not in kwargs: - kwargs['public_key'] = kwargs['signing_private_key'] - if 'signing_private_key_passphrase' in kwargs: - kwargs['public_key_passphrase'] = kwargs[ - 'signing_private_key_passphrase'] + if "public_key" not in kwargs and "csr" not in kwargs: + kwargs["public_key"] = kwargs["signing_private_key"] + if "signing_private_key_passphrase" in kwargs: + kwargs["public_key_passphrase"] = kwargs["signing_private_key_passphrase"] csrexts = {} - if 'csr' in kwargs: - kwargs['public_key'] = kwargs['csr'] - csr = _get_request_obj(kwargs['csr']) + if "csr" in kwargs: + kwargs["public_key"] = kwargs["csr"] + csr = _get_request_obj(kwargs["csr"]) cert.set_subject(csr.get_subject()) - csrexts = read_csr(kwargs['csr'])['X509v3 Extensions'] + csrexts = read_csr(kwargs["csr"])["X509v3 Extensions"] - cert.set_pubkey(get_public_key(kwargs['public_key'], - passphrase=kwargs['public_key_passphrase'], asObj=True)) + cert.set_pubkey( + get_public_key( + kwargs["public_key"], passphrase=kwargs["public_key_passphrase"], asObj=True + ) + ) subject = cert.get_subject() @@ -1492,99 +1531,114 @@ def create_certificate( setattr(subject, entry, kwargs[entry]) # pylint: enable=unused-variable - if 'signing_cert' in kwargs: - signing_cert = _get_certificate_obj(kwargs['signing_cert']) + if "signing_cert" in kwargs: + signing_cert = _get_certificate_obj(kwargs["signing_cert"]) else: signing_cert = cert cert.set_issuer(signing_cert.get_subject()) for extname, extlongname in six.iteritems(EXT_NAME_MAPPINGS): - if (extname in kwargs or extlongname in kwargs or - extname in csrexts or extlongname in csrexts) is False: + if ( + extname in kwargs + or extlongname in kwargs + or extname in csrexts + or extlongname in csrexts + ) is False: continue # Use explicitly set values first, fall back to CSR values. - extval = kwargs.get(extname) or kwargs.get(extlongname) or \ - csrexts.get(extname) or csrexts.get(extlongname) + extval = ( + kwargs.get(extname) + or kwargs.get(extlongname) + or csrexts.get(extname) + or csrexts.get(extlongname) + ) critical = False - if extval.startswith('critical '): + if extval.startswith("critical "): critical = True extval = extval[9:] - if extname == 'subjectKeyIdentifier' and 'hash' in extval: - extval = extval.replace('hash', _get_pubkey_hash(cert)) + if extname == "subjectKeyIdentifier" and "hash" in extval: + extval = extval.replace("hash", _get_pubkey_hash(cert)) issuer = None - if extname == 'authorityKeyIdentifier': + if extname == "authorityKeyIdentifier": issuer = signing_cert - if extname == 'subjectAltName': - extval = extval.replace('IP Address', 'IP') + if extname == "subjectAltName": + extval = extval.replace("IP Address", "IP") ext = _new_extension( - name=extname, value=extval, critical=critical, issuer=issuer) + name=extname, value=extval, critical=critical, issuer=issuer + ) if not ext.x509_ext: - log.info( - 'Invalid X509v3 Extension. {0}: {1}'.format(extname, extval)) + log.info("Invalid X509v3 Extension. {0}: {1}".format(extname, extval)) continue cert.add_ext(ext) - if 'signing_private_key_passphrase' not in kwargs: - kwargs['signing_private_key_passphrase'] = None - if 'testrun' in kwargs and kwargs['testrun'] is True: + if "signing_private_key_passphrase" not in kwargs: + kwargs["signing_private_key_passphrase"] = None + if "testrun" in kwargs and kwargs["testrun"] is True: cert_props = read_certificate(cert) - cert_props['Issuer Public Key'] = get_public_key( - kwargs['signing_private_key'], - passphrase=kwargs['signing_private_key_passphrase']) + cert_props["Issuer Public Key"] = get_public_key( + kwargs["signing_private_key"], + passphrase=kwargs["signing_private_key_passphrase"], + ) return cert_props - if not verify_private_key(private_key=kwargs['signing_private_key'], - passphrase=kwargs[ - 'signing_private_key_passphrase'], - public_key=signing_cert): + if not verify_private_key( + private_key=kwargs["signing_private_key"], + passphrase=kwargs["signing_private_key_passphrase"], + public_key=signing_cert, + ): raise salt.exceptions.SaltInvocationError( - 'signing_private_key: {0} ' - 'does no match signing_cert: {1}'.format( - kwargs['signing_private_key'], - kwargs.get('signing_cert', '') + "signing_private_key: {0} " + "does no match signing_cert: {1}".format( + kwargs["signing_private_key"], kwargs.get("signing_cert", "") ) ) cert.sign( - _get_private_key_obj(kwargs['signing_private_key'], - passphrase=kwargs['signing_private_key_passphrase']), - kwargs['algorithm'] + _get_private_key_obj( + kwargs["signing_private_key"], + passphrase=kwargs["signing_private_key_passphrase"], + ), + kwargs["algorithm"], ) if not verify_signature(cert, signing_pub_key=signing_cert): raise salt.exceptions.SaltInvocationError( - 'failed to verify certificate signature') + "failed to verify certificate signature" + ) - if 'copypath' in kwargs: - if 'prepend_cn' in kwargs and kwargs['prepend_cn'] is True: - prepend = six.text_type(kwargs['CN']) + '-' + if "copypath" in kwargs: + if "prepend_cn" in kwargs and kwargs["prepend_cn"] is True: + prepend = six.text_type(kwargs["CN"]) + "-" else: - prepend = '' - write_pem(text=cert.as_pem(), path=os.path.join(kwargs['copypath'], - prepend + kwargs['serial_number'] + '.crt'), - pem_type='CERTIFICATE') + prepend = "" + write_pem( + text=cert.as_pem(), + path=os.path.join( + kwargs["copypath"], prepend + kwargs["serial_number"] + ".crt" + ), + pem_type="CERTIFICATE", + ) if path: return write_pem( - text=cert.as_pem(), - overwrite=overwrite, - path=path, - pem_type='CERTIFICATE' + text=cert.as_pem(), overwrite=overwrite, path=path, pem_type="CERTIFICATE" ) else: return salt.utils.stringutils.to_str(cert.as_pem()) + + # pylint: enable=too-many-locals def create_csr(path=None, text=False, **kwargs): - ''' + """ Create a certificate signing request. path: @@ -1607,14 +1661,16 @@ def create_csr(path=None, text=False, **kwargs): .. code-block:: bash salt '*' x509.create_csr path=/etc/pki/myca.csr public_key='/etc/pki/myca.key' CN='My Cert' - ''' + """ if not path and not text: raise salt.exceptions.SaltInvocationError( - 'Either path or text must be specified.') + "Either path or text must be specified." + ) if path and text: raise salt.exceptions.SaltInvocationError( - 'Either path or text must be specified, not both.') + "Either path or text must be specified, not both." + ) csr = M2Crypto.X509.Request() subject = csr.get_subject() @@ -1623,32 +1679,34 @@ def create_csr(path=None, text=False, **kwargs): if prop not in kwargs: kwargs[prop] = default - csr.set_version(kwargs['version'] - 1) + csr.set_version(kwargs["version"] - 1) - if 'private_key' not in kwargs and 'public_key' in kwargs: - kwargs['private_key'] = kwargs['public_key'] + if "private_key" not in kwargs and "public_key" in kwargs: + kwargs["private_key"] = kwargs["public_key"] log.warning( - "OpenSSL no longer allows working with non-signed CSRs. A private_key must be specified. Attempting to use public_key as private_key") + "OpenSSL no longer allows working with non-signed CSRs. A private_key must be specified. Attempting to use public_key as private_key" + ) - if 'private_key' not in kwargs: - raise salt.exceptions.SaltInvocationError('private_key is required') + if "private_key" not in kwargs: + raise salt.exceptions.SaltInvocationError("private_key is required") - if 'public_key' not in kwargs: - kwargs['public_key'] = kwargs['private_key'] + if "public_key" not in kwargs: + kwargs["public_key"] = kwargs["private_key"] - if 'private_key_passphrase' not in kwargs: - kwargs['private_key_passphrase'] = None - if 'public_key_passphrase' not in kwargs: - kwargs['public_key_passphrase'] = None - if kwargs['public_key_passphrase'] and not kwargs[ - 'private_key_passphrase']: - kwargs['private_key_passphrase'] = kwargs['public_key_passphrase'] - if kwargs['private_key_passphrase'] and not kwargs[ - 'public_key_passphrase']: - kwargs['public_key_passphrase'] = kwargs['private_key_passphrase'] + if "private_key_passphrase" not in kwargs: + kwargs["private_key_passphrase"] = None + if "public_key_passphrase" not in kwargs: + kwargs["public_key_passphrase"] = None + if kwargs["public_key_passphrase"] and not kwargs["private_key_passphrase"]: + kwargs["private_key_passphrase"] = kwargs["public_key_passphrase"] + if kwargs["private_key_passphrase"] and not kwargs["public_key_passphrase"]: + kwargs["public_key_passphrase"] = kwargs["private_key_passphrase"] - csr.set_pubkey(get_public_key(kwargs['public_key'], - passphrase=kwargs['public_key_passphrase'], asObj=True)) + csr.set_pubkey( + get_public_key( + kwargs["public_key"], passphrase=kwargs["public_key_passphrase"], asObj=True + ) + ) # pylint: disable=unused-variable for entry, num in six.iteritems(subject.nid): @@ -1664,46 +1722,46 @@ def create_csr(path=None, text=False, **kwargs): extval = kwargs[extname] or kwargs[extlongname] critical = False - if extval.startswith('critical '): + if extval.startswith("critical "): critical = True extval = extval[9:] - if extname == 'subjectKeyIdentifier' and 'hash' in extval: - extval = extval.replace('hash', _get_pubkey_hash(csr)) + if extname == "subjectKeyIdentifier" and "hash" in extval: + extval = extval.replace("hash", _get_pubkey_hash(csr)) - if extname == 'subjectAltName': - extval = extval.replace('IP Address', 'IP') + if extname == "subjectAltName": + extval = extval.replace("IP Address", "IP") - if extname == 'authorityKeyIdentifier': + if extname == "authorityKeyIdentifier": continue issuer = None ext = _new_extension( - name=extname, value=extval, critical=critical, issuer=issuer) + name=extname, value=extval, critical=critical, issuer=issuer + ) if not ext.x509_ext: - log.info( - 'Invalid X509v3 Extension. {0}: {1}'.format(extname, extval)) + log.info("Invalid X509v3 Extension. {0}: {1}".format(extname, extval)) continue extstack.push(ext) csr.add_extensions(extstack) - csr.sign(_get_private_key_obj(kwargs['private_key'], - passphrase=kwargs['private_key_passphrase']), kwargs['algorithm']) + csr.sign( + _get_private_key_obj( + kwargs["private_key"], passphrase=kwargs["private_key_passphrase"] + ), + kwargs["algorithm"], + ) if path: - return write_pem( - text=csr.as_pem(), - path=path, - pem_type='CERTIFICATE REQUEST' - ) + return write_pem(text=csr.as_pem(), path=path, pem_type="CERTIFICATE REQUEST") else: return csr.as_pem() def verify_private_key(private_key, public_key, passphrase=None): - ''' + """ Verify that 'private_key' matches 'public_key' private_key: @@ -1723,14 +1781,14 @@ def verify_private_key(private_key, public_key, passphrase=None): salt '*' x509.verify_private_key private_key=/etc/pki/myca.key \\ public_key=/etc/pki/myca.crt - ''' - return bool(get_public_key(private_key, passphrase) - == get_public_key(public_key)) + """ + return bool(get_public_key(private_key, passphrase) == get_public_key(public_key)) -def verify_signature(certificate, signing_pub_key=None, - signing_pub_key_passphrase=None): - ''' +def verify_signature( + certificate, signing_pub_key=None, signing_pub_key_passphrase=None +): + """ Verify that ``certificate`` has been signed by ``signing_pub_key`` certificate: @@ -1750,18 +1808,19 @@ def verify_signature(certificate, signing_pub_key=None, salt '*' x509.verify_signature /etc/pki/mycert.pem \\ signing_pub_key=/etc/pki/myca.crt - ''' + """ cert = _get_certificate_obj(certificate) if signing_pub_key: - signing_pub_key = get_public_key(signing_pub_key, - passphrase=signing_pub_key_passphrase, asObj=True) + signing_pub_key = get_public_key( + signing_pub_key, passphrase=signing_pub_key_passphrase, asObj=True + ) return bool(cert.verify(pkey=signing_pub_key) == 1) def verify_crl(crl, cert): - ''' + """ Validate a CRL against a certificate. Parses openssl command line output, this is a workaround for M2Crypto's inability to get them from CSR objects. @@ -1777,39 +1836,38 @@ def verify_crl(crl, cert): .. code-block:: bash salt '*' x509.verify_crl crl=/etc/pki/myca.crl cert=/etc/pki/myca.crt - ''' - if not salt.utils.path.which('openssl'): - raise salt.exceptions.SaltInvocationError( - 'openssl binary not found in path' - ) + """ + if not salt.utils.path.which("openssl"): + raise salt.exceptions.SaltInvocationError("openssl binary not found in path") crltext = _text_or_file(crl) - crltext = get_pem_entry(crltext, pem_type='X509 CRL') + crltext = get_pem_entry(crltext, pem_type="X509 CRL") crltempfile = tempfile.NamedTemporaryFile() crltempfile.write(salt.utils.stringutils.to_str(crltext)) crltempfile.flush() certtext = _text_or_file(cert) - certtext = get_pem_entry(certtext, pem_type='CERTIFICATE') + certtext = get_pem_entry(certtext, pem_type="CERTIFICATE") certtempfile = tempfile.NamedTemporaryFile() certtempfile.write(salt.utils.stringutils.to_str(certtext)) certtempfile.flush() - cmd = ('openssl crl -noout -in {0} -CAfile {1}'.format( - crltempfile.name, certtempfile.name)) + cmd = "openssl crl -noout -in {0} -CAfile {1}".format( + crltempfile.name, certtempfile.name + ) - output = __salt__['cmd.run_stderr'](cmd) + output = __salt__["cmd.run_stderr"](cmd) crltempfile.close() certtempfile.close() - if 'verify OK' in output: + if "verify OK" in output: return True else: return False def expired(certificate): - ''' + """ Returns a dict containing limited details of a certificate and whether the certificate has expired. @@ -1824,24 +1882,25 @@ def expired(certificate): .. code-block:: bash salt '*' x509.expired "/etc/pki/mycert.crt" - ''' + """ ret = {} if os.path.isfile(certificate): try: - ret['path'] = certificate + ret["path"] = certificate cert = _get_certificate_obj(certificate) _now = datetime.datetime.utcnow() _expiration_date = cert.get_not_after().get_datetime() - ret['cn'] = _parse_subject(cert.get_subject())['CN'] + ret["cn"] = _parse_subject(cert.get_subject())["CN"] - if _expiration_date.strftime("%Y-%m-%d %H:%M:%S") <= \ - _now.strftime("%Y-%m-%d %H:%M:%S"): - ret['expired'] = True + if _expiration_date.strftime("%Y-%m-%d %H:%M:%S") <= _now.strftime( + "%Y-%m-%d %H:%M:%S" + ): + ret["expired"] = True else: - ret['expired'] = False + ret["expired"] = False except ValueError: pass @@ -1849,7 +1908,7 @@ def expired(certificate): def will_expire(certificate, days): - ''' + """ Returns a dict containing details of a certificate and whether the certificate will expire in the specified number of days. Input can be a PEM string or file path. @@ -1865,27 +1924,27 @@ def will_expire(certificate, days): .. code-block:: bash salt '*' x509.will_expire "/etc/pki/mycert.crt" days=30 - ''' + """ ret = {} if os.path.isfile(certificate): try: - ret['path'] = certificate - ret['check_days'] = days + ret["path"] = certificate + ret["check_days"] = days cert = _get_certificate_obj(certificate) - _check_time = datetime.datetime.utcnow() + \ - datetime.timedelta(days=days) + _check_time = datetime.datetime.utcnow() + datetime.timedelta(days=days) _expiration_date = cert.get_not_after().get_datetime() - ret['cn'] = _parse_subject(cert.get_subject())['CN'] + ret["cn"] = _parse_subject(cert.get_subject())["CN"] - if _expiration_date.strftime("%Y-%m-%d %H:%M:%S") <= \ - _check_time.strftime("%Y-%m-%d %H:%M:%S"): - ret['will_expire'] = True + if _expiration_date.strftime("%Y-%m-%d %H:%M:%S") <= _check_time.strftime( + "%Y-%m-%d %H:%M:%S" + ): + ret["will_expire"] = True else: - ret['will_expire'] = False + ret["will_expire"] = False except ValueError: pass diff --git a/salt/modules/xapi_virt.py b/salt/modules/xapi_virt.py index 0e1bbc0930a..aee0a79bb96 100644 --- a/salt/modules/xapi_virt.py +++ b/salt/modules/xapi_virt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This module (mostly) uses the XenAPI to manage Xen virtual machines. Big fat warning: the XenAPI used in this file is the one bundled with @@ -15,32 +15,35 @@ Useful documentation: . http://docs.vmd.citrix.com/XenServer/6.0.0/1.0/en_gb/api/ . https://github.com/xapi-project/xen-api/tree/master/scripts/examples/python . http://xenbits.xen.org/gitweb/?p=xen.git;a=tree;f=tools/python/xen/xm;hb=HEAD -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import contextlib +import os + # Import python libs import sys -import contextlib -import os -from salt.ext.six.moves import range -from salt.ext.six.moves import map -try: - import importlib # pylint: disable=minimum-python-version - HAS_IMPORTLIB = True -except ImportError: - # Python < 2.7 does not have importlib - HAS_IMPORTLIB = False +import salt.modules.cmdmod # Import salt libs import salt.utils.files import salt.utils.path import salt.utils.stringutils -import salt.modules.cmdmod from salt.exceptions import CommandExecutionError +from salt.ext.six.moves import map, range + +try: + import importlib # pylint: disable=minimum-python-version + + HAS_IMPORTLIB = True +except ImportError: + # Python < 2.7 does not have importlib + HAS_IMPORTLIB = False + # Define the module's virtual name -__virtualname__ = 'virt' +__virtualname__ = "virt" # This module has only been tested on Debian GNU/Linux and NetBSD, it # probably needs more path appending for other distributions. @@ -49,19 +52,19 @@ __virtualname__ = 'virt' def _check_xenapi(): - if __grains__['os'] == 'Debian': - debian_xen_version = '/usr/lib/xen-common/bin/xen-version' + if __grains__["os"] == "Debian": + debian_xen_version = "/usr/lib/xen-common/bin/xen-version" if os.path.isfile(debian_xen_version): # __salt__ is not available in __virtual__ xenversion = salt.modules.cmdmod._run_quiet(debian_xen_version) - xapipath = '/usr/lib/xen-{0}/lib/python'.format(xenversion) + xapipath = "/usr/lib/xen-{0}/lib/python".format(xenversion) if os.path.isdir(xapipath): sys.path.append(xapipath) try: if HAS_IMPORTLIB: - return importlib.import_module('xen.xm.XenAPI') - return __import__('xen.xm.XenAPI').xm.XenAPI + return importlib.import_module("xen.xm.XenAPI") + return __import__("xen.xm.XenAPI").xm.XenAPI except (ImportError, AttributeError): return False @@ -74,22 +77,22 @@ def __virtual__(): @contextlib.contextmanager def _get_xapi_session(): - ''' + """ Get a session to XenAPI. By default, use the local UNIX socket. - ''' + """ _xenapi = _check_xenapi() - xapi_uri = __salt__['config.option']('xapi.uri') - xapi_login = __salt__['config.option']('xapi.login') - xapi_password = __salt__['config.option']('xapi.password') + xapi_uri = __salt__["config.option"]("xapi.uri") + xapi_login = __salt__["config.option"]("xapi.login") + xapi_password = __salt__["config.option"]("xapi.password") if not xapi_uri: # xend local UNIX socket - xapi_uri = 'httpu:///var/run/xend/xen-api.sock' + xapi_uri = "httpu:///var/run/xend/xen-api.sock" if not xapi_login: - xapi_login = '' + xapi_login = "" if not xapi_password: - xapi_password = '' + xapi_password = "" try: session = _xenapi.Session(xapi_uri) @@ -97,7 +100,7 @@ def _get_xapi_session(): yield session.xenapi except Exception: # pylint: disable=broad-except - raise CommandExecutionError('Failed to connect to XenAPI socket.') + raise CommandExecutionError("Failed to connect to XenAPI socket.") finally: session.xenapi.session.logout() @@ -112,26 +115,26 @@ def _get_xapi_session(): def _get_xtool(): - ''' + """ Internal, returns xl or xm command line path - ''' - for xtool in ['xl', 'xm']: + """ + for xtool in ["xl", "xm"]: path = salt.utils.path.which(xtool) if path is not None: return path def _get_all(xapi, rectype): - ''' + """ Internal, returns all members of rectype - ''' + """ return getattr(xapi, rectype).get_all() def _get_label_uuid(xapi, rectype, label): - ''' + """ Internal, returns label's uuid - ''' + """ try: return getattr(xapi, rectype).get_by_name_label(label)[0] except Exception: # pylint: disable=broad-except @@ -139,16 +142,16 @@ def _get_label_uuid(xapi, rectype, label): def _get_record(xapi, rectype, uuid): - ''' + """ Internal, returns a full record for uuid - ''' + """ return getattr(xapi, rectype).get_record(uuid) def _get_record_by_label(xapi, rectype, label): - ''' + """ Internal, returns a full record for uuid - ''' + """ uuid = _get_label_uuid(xapi, rectype, label) if uuid is False: return False @@ -156,17 +159,17 @@ def _get_record_by_label(xapi, rectype, label): def _get_metrics_record(xapi, rectype, record): - ''' + """ Internal, returns metrics record for a rectype - ''' - metrics_id = record['metrics'] - return getattr(xapi, '{0}_metrics'.format(rectype)).get_record(metrics_id) + """ + metrics_id = record["metrics"] + return getattr(xapi, "{0}_metrics".format(rectype)).get_record(metrics_id) def _get_val(record, keys): - ''' + """ Internal, get value from record - ''' + """ data = record for key in keys: if key in data: @@ -177,7 +180,7 @@ def _get_val(record, keys): def list_domains(): - ''' + """ Return a list of virtual machine names on the minion CLI Example: @@ -185,20 +188,20 @@ def list_domains(): .. code-block:: bash salt '*' virt.list_domains - ''' + """ with _get_xapi_session() as xapi: hosts = xapi.VM.get_all() ret = [] for _host in hosts: - if xapi.VM.get_record(_host)['is_control_domain'] is False: + if xapi.VM.get_record(_host)["is_control_domain"] is False: ret.append(xapi.VM.get_name_label(_host)) return ret def vm_info(vm_=None): - ''' + """ Return detailed information about the vms. If you pass a VM name in as an argument then it will return info @@ -209,24 +212,26 @@ def vm_info(vm_=None): .. code-block:: bash salt '*' virt.vm_info - ''' + """ with _get_xapi_session() as xapi: def _info(vm_): - vm_rec = _get_record_by_label(xapi, 'VM', vm_) + vm_rec = _get_record_by_label(xapi, "VM", vm_) if vm_rec is False: return False - vm_metrics_rec = _get_metrics_record(xapi, 'VM', vm_rec) + vm_metrics_rec = _get_metrics_record(xapi, "VM", vm_rec) + + return { + "cpu": vm_metrics_rec["VCPUs_number"], + "maxCPU": _get_val(vm_rec, ["VCPUs_max"]), + "cputime": vm_metrics_rec["VCPUs_utilisation"], + "disks": get_disks(vm_), + "nics": get_nics(vm_), + "maxMem": int(_get_val(vm_rec, ["memory_dynamic_max"])), + "mem": int(vm_metrics_rec["memory_actual"]), + "state": _get_val(vm_rec, ["power_state"]), + } - return {'cpu': vm_metrics_rec['VCPUs_number'], - 'maxCPU': _get_val(vm_rec, ['VCPUs_max']), - 'cputime': vm_metrics_rec['VCPUs_utilisation'], - 'disks': get_disks(vm_), - 'nics': get_nics(vm_), - 'maxMem': int(_get_val(vm_rec, ['memory_dynamic_max'])), - 'mem': int(vm_metrics_rec['memory_actual']), - 'state': _get_val(vm_rec, ['power_state']) - } info = {} if vm_: ret = _info(vm_) @@ -241,7 +246,7 @@ def vm_info(vm_=None): def vm_state(vm_=None): - ''' + """ Return list of all the vms and their state. If you pass a VM name in as an argument then it will return info @@ -252,21 +257,21 @@ def vm_state(vm_=None): .. code-block:: bash salt '*' virt.vm_state <vm name> - ''' + """ with _get_xapi_session() as xapi: info = {} if vm_: - info[vm_] = _get_record_by_label(xapi, 'VM', vm_)['power_state'] + info[vm_] = _get_record_by_label(xapi, "VM", vm_)["power_state"] return info for vm_ in list_domains(): - info[vm_] = _get_record_by_label(xapi, 'VM', vm_)['power_state'] + info[vm_] = _get_record_by_label(xapi, "VM", vm_)["power_state"] return info def node_info(): - ''' + """ Return a dict with information about this node CLI Example: @@ -274,20 +279,22 @@ def node_info(): .. code-block:: bash salt '*' virt.node_info - ''' + """ with _get_xapi_session() as xapi: # get node uuid - host_rec = _get_record(xapi, 'host', _get_all(xapi, 'host')[0]) + host_rec = _get_record(xapi, "host", _get_all(xapi, "host")[0]) # get first CPU (likely to be a core) uuid - host_cpu_rec = _get_record(xapi, 'host_cpu', host_rec['host_CPUs'][0]) + host_cpu_rec = _get_record(xapi, "host_cpu", host_rec["host_CPUs"][0]) # get related metrics - host_metrics_rec = _get_metrics_record(xapi, 'host', host_rec) + host_metrics_rec = _get_metrics_record(xapi, "host", host_rec) # adapted / cleaned up from Xen's xm def getCpuMhz(): - cpu_speeds = [int(host_cpu_rec["speed"]) - for host_cpu_it in host_cpu_rec - if "speed" in host_cpu_it] + cpu_speeds = [ + int(host_cpu_rec["speed"]) + for host_cpu_it in host_cpu_rec + if "speed" in host_cpu_it + ] if cpu_speeds: return sum(cpu_speeds) / len(cpu_speeds) else: @@ -295,63 +302,54 @@ def node_info(): def getCpuFeatures(): if host_cpu_rec: - return host_cpu_rec['features'] + return host_cpu_rec["features"] def getFreeCpuCount(): cnt = 0 for host_cpu_it in host_cpu_rec: - if len(host_cpu_rec['cpu_pool']) == 0: + if len(host_cpu_rec["cpu_pool"]) == 0: cnt += 1 return cnt info = { - 'cpucores': _get_val(host_rec, - ["cpu_configuration", "nr_cpus"]), - 'cpufeatures': getCpuFeatures(), - 'cpumhz': getCpuMhz(), - 'cpuarch': _get_val(host_rec, - ["software_version", "machine"]), - 'cputhreads': _get_val(host_rec, - ["cpu_configuration", "threads_per_core"]), - 'phymemory': int(host_metrics_rec["memory_total"]) / 1024 / 1024, - 'cores_per_sockets': _get_val(host_rec, - ["cpu_configuration", "cores_per_socket"]), - 'free_cpus': getFreeCpuCount(), - 'free_memory': int(host_metrics_rec["memory_free"]) / 1024 / 1024, - 'xen_major': _get_val(host_rec, - ["software_version", "xen_major"]), - 'xen_minor': _get_val(host_rec, - ["software_version", "xen_minor"]), - 'xen_extra': _get_val(host_rec, - ["software_version", "xen_extra"]), - 'xen_caps': " ".join(_get_val(host_rec, ["capabilities"])), - 'xen_scheduler': _get_val(host_rec, - ["sched_policy"]), - 'xen_pagesize': _get_val(host_rec, - ["other_config", "xen_pagesize"]), - 'platform_params': _get_val(host_rec, - ["other_config", "platform_params"]), - 'xen_commandline': _get_val(host_rec, - ["other_config", "xen_commandline"]), - 'xen_changeset': _get_val(host_rec, - ["software_version", "xen_changeset"]), - 'cc_compiler': _get_val(host_rec, - ["software_version", "cc_compiler"]), - 'cc_compile_by': _get_val(host_rec, - ["software_version", "cc_compile_by"]), - 'cc_compile_domain': _get_val(host_rec, - ["software_version", "cc_compile_domain"]), - 'cc_compile_date': _get_val(host_rec, - ["software_version", "cc_compile_date"]), - 'xend_config_format': _get_val(host_rec, - ["software_version", "xend_config_format"]) - } + "cpucores": _get_val(host_rec, ["cpu_configuration", "nr_cpus"]), + "cpufeatures": getCpuFeatures(), + "cpumhz": getCpuMhz(), + "cpuarch": _get_val(host_rec, ["software_version", "machine"]), + "cputhreads": _get_val(host_rec, ["cpu_configuration", "threads_per_core"]), + "phymemory": int(host_metrics_rec["memory_total"]) / 1024 / 1024, + "cores_per_sockets": _get_val( + host_rec, ["cpu_configuration", "cores_per_socket"] + ), + "free_cpus": getFreeCpuCount(), + "free_memory": int(host_metrics_rec["memory_free"]) / 1024 / 1024, + "xen_major": _get_val(host_rec, ["software_version", "xen_major"]), + "xen_minor": _get_val(host_rec, ["software_version", "xen_minor"]), + "xen_extra": _get_val(host_rec, ["software_version", "xen_extra"]), + "xen_caps": " ".join(_get_val(host_rec, ["capabilities"])), + "xen_scheduler": _get_val(host_rec, ["sched_policy"]), + "xen_pagesize": _get_val(host_rec, ["other_config", "xen_pagesize"]), + "platform_params": _get_val(host_rec, ["other_config", "platform_params"]), + "xen_commandline": _get_val(host_rec, ["other_config", "xen_commandline"]), + "xen_changeset": _get_val(host_rec, ["software_version", "xen_changeset"]), + "cc_compiler": _get_val(host_rec, ["software_version", "cc_compiler"]), + "cc_compile_by": _get_val(host_rec, ["software_version", "cc_compile_by"]), + "cc_compile_domain": _get_val( + host_rec, ["software_version", "cc_compile_domain"] + ), + "cc_compile_date": _get_val( + host_rec, ["software_version", "cc_compile_date"] + ), + "xend_config_format": _get_val( + host_rec, ["software_version", "xend_config_format"] + ), + } return info def get_nics(vm_): - ''' + """ Return info about the network interfaces of a named vm CLI Example: @@ -359,26 +357,26 @@ def get_nics(vm_): .. code-block:: bash salt '*' virt.get_nics <vm name> - ''' + """ with _get_xapi_session() as xapi: nic = {} - vm_rec = _get_record_by_label(xapi, 'VM', vm_) + vm_rec = _get_record_by_label(xapi, "VM", vm_) if vm_rec is False: return False - for vif in vm_rec['VIFs']: - vif_rec = _get_record(xapi, 'VIF', vif) - nic[vif_rec['MAC']] = { - 'mac': vif_rec['MAC'], - 'device': vif_rec['device'], - 'mtu': vif_rec['MTU'] + for vif in vm_rec["VIFs"]: + vif_rec = _get_record(xapi, "VIF", vif) + nic[vif_rec["MAC"]] = { + "mac": vif_rec["MAC"], + "device": vif_rec["device"], + "mtu": vif_rec["MTU"], } return nic def get_macs(vm_): - ''' + """ Return a list off MAC addresses from the named vm CLI Example: @@ -386,7 +384,7 @@ def get_macs(vm_): .. code-block:: bash salt '*' virt.get_macs <vm name> - ''' + """ macs = [] nics = get_nics(vm_) if nics is None: @@ -398,7 +396,7 @@ def get_macs(vm_): def get_disks(vm_): - ''' + """ Return the disks of a named vm CLI Example: @@ -406,12 +404,12 @@ def get_disks(vm_): .. code-block:: bash salt '*' virt.get_disks <vm name> - ''' + """ with _get_xapi_session() as xapi: disk = {} - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False for vbd in xapi.VM.get_VBDs(vm_uuid): @@ -420,16 +418,16 @@ def get_disks(vm_): continue prop = xapi.VBD.get_runtime_properties(vbd) disk[dev] = { - 'backend': prop['backend'], - 'type': prop['device-type'], - 'protocol': prop['protocol'] + "backend": prop["backend"], + "type": prop["device-type"], + "protocol": prop["protocol"], } return disk def setmem(vm_, memory): - ''' + """ Changes the amount of memory allocated to VM. Memory is to be specified in MB @@ -439,11 +437,11 @@ def setmem(vm_, memory): .. code-block:: bash salt '*' virt.setmem myvm 768 - ''' + """ with _get_xapi_session() as xapi: mem_target = int(memory) * 1024 * 1024 - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False try: @@ -455,7 +453,7 @@ def setmem(vm_, memory): def setvcpus(vm_, vcpus): - ''' + """ Changes the amount of vcpus allocated to VM. vcpus is an int representing the number to be assigned @@ -465,9 +463,9 @@ def setvcpus(vm_, vcpus): .. code-block:: bash salt '*' virt.setvcpus myvm 2 - ''' + """ with _get_xapi_session() as xapi: - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False try: @@ -478,7 +476,7 @@ def setvcpus(vm_, vcpus): def vcpu_pin(vm_, vcpu, cpus): - ''' + """ Set which CPUs a VCPU can use. CLI Example: @@ -487,53 +485,53 @@ def vcpu_pin(vm_, vcpu, cpus): salt 'foo' virt.vcpu_pin domU-id 2 1 salt 'foo' virt.vcpu_pin domU-id 2 2-6 - ''' + """ with _get_xapi_session() as xapi: - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False # from xm's main def cpu_make_map(cpulist): cpus = [] - for c in cpulist.split(','): - if c == '': + for c in cpulist.split(","): + if c == "": continue - if '-' in c: - (x, y) = c.split('-') + if "-" in c: + (x, y) = c.split("-") for i in range(int(x), int(y) + 1): cpus.append(int(i)) else: # remove this element from the list - if c[0] == '^': + if c[0] == "^": cpus = [x for x in cpus if x != int(c[1:])] else: cpus.append(int(c)) cpus.sort() - return ','.join(map(str, cpus)) + return ",".join(map(str, cpus)) - if cpus == 'all': - cpumap = cpu_make_map('0-63') + if cpus == "all": + cpumap = cpu_make_map("0-63") else: - cpumap = cpu_make_map('{0}'.format(cpus)) + cpumap = cpu_make_map("{0}".format(cpus)) try: - xapi.VM.add_to_VCPUs_params_live(vm_uuid, - 'cpumap{0}'.format(vcpu), cpumap) + xapi.VM.add_to_VCPUs_params_live(vm_uuid, "cpumap{0}".format(vcpu), cpumap) return True # VM.add_to_VCPUs_params_live() implementation in xend 4.1+ has # a bug which makes the client call fail. # That code is accurate for all others XenAPI implementations, but # for that particular one, fallback to xm / xl instead. except Exception: # pylint: disable=broad-except - return __salt__['cmd.run']( - '{0} vcpu-pin {1} {2} {3}'.format(_get_xtool(), vm_, vcpu, cpus), - python_shell=False) + return __salt__["cmd.run"]( + "{0} vcpu-pin {1} {2} {3}".format(_get_xtool(), vm_, vcpu, cpus), + python_shell=False, + ) def freemem(): - ''' + """ Return an int representing the amount of memory that has not been given to virtual machines on this node @@ -542,12 +540,12 @@ def freemem(): .. code-block:: bash salt '*' virt.freemem - ''' - return node_info()['free_memory'] + """ + return node_info()["free_memory"] def freecpu(): - ''' + """ Return an int representing the number of unallocated cpus on this hypervisor @@ -556,12 +554,12 @@ def freecpu(): .. code-block:: bash salt '*' virt.freecpu - ''' - return node_info()['free_cpus'] + """ + return node_info()["free_cpus"] def full_info(): - ''' + """ Return the node_info, vm_info and freemem CLI Example: @@ -569,12 +567,12 @@ def full_info(): .. code-block:: bash salt '*' virt.full_info - ''' - return {'node_info': node_info(), 'vm_info': vm_info()} + """ + return {"node_info": node_info(), "vm_info": vm_info()} def shutdown(vm_): - ''' + """ Send a soft shutdown signal to the named vm CLI Example: @@ -582,9 +580,9 @@ def shutdown(vm_): .. code-block:: bash salt '*' virt.shutdown <vm name> - ''' + """ with _get_xapi_session() as xapi: - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False try: @@ -595,7 +593,7 @@ def shutdown(vm_): def pause(vm_): - ''' + """ Pause the named vm CLI Example: @@ -603,9 +601,9 @@ def pause(vm_): .. code-block:: bash salt '*' virt.pause <vm name> - ''' + """ with _get_xapi_session() as xapi: - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False try: @@ -616,7 +614,7 @@ def pause(vm_): def resume(vm_): - ''' + """ Resume the named vm CLI Example: @@ -624,9 +622,9 @@ def resume(vm_): .. code-block:: bash salt '*' virt.resume <vm name> - ''' + """ with _get_xapi_session() as xapi: - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False try: @@ -637,7 +635,7 @@ def resume(vm_): def start(config_): - ''' + """ Start a defined domain CLI Example: @@ -645,16 +643,18 @@ def start(config_): .. code-block:: bash salt '*' virt.start <path to Xen cfg file> - ''' + """ # FIXME / TODO # This function does NOT use the XenAPI. Instead, it use good old xm / xl. # On Xen Source, creating a virtual machine using XenAPI is really painful. # XCP / XS make it really easy using xapi.Async.VM.start instead. Anyone? - return __salt__['cmd.run']('{0} create {1}'.format(_get_xtool(), config_), python_shell=False) + return __salt__["cmd.run"]( + "{0} create {1}".format(_get_xtool(), config_), python_shell=False + ) def reboot(vm_): - ''' + """ Reboot a domain via ACPI request CLI Example: @@ -662,9 +662,9 @@ def reboot(vm_): .. code-block:: bash salt '*' virt.reboot <vm name> - ''' + """ with _get_xapi_session() as xapi: - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False try: @@ -675,7 +675,7 @@ def reboot(vm_): def reset(vm_): - ''' + """ Reset a VM by emulating the reset button on a physical machine CLI Example: @@ -683,9 +683,9 @@ def reset(vm_): .. code-block:: bash salt '*' virt.reset <vm name> - ''' + """ with _get_xapi_session() as xapi: - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False try: @@ -695,9 +695,8 @@ def reset(vm_): return False -def migrate(vm_, target, - live=1, port=0, node=-1, ssl=None, change_home_server=0): - ''' +def migrate(vm_, target, live=1, port=0, node=-1, ssl=None, change_home_server=0): + """ Migrates the virtual machine to another hypervisor CLI Example: @@ -718,16 +717,16 @@ def migrate(vm_, target, use ssl connection for migration change_home_server change home server for managed domains - ''' + """ with _get_xapi_session() as xapi: - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False other_config = { - 'port': port, - 'node': node, - 'ssl': ssl, - 'change_home_server': change_home_server + "port": port, + "node": node, + "ssl": ssl, + "change_home_server": change_home_server, } try: xapi.VM.migrate(vm_uuid, target, bool(live), other_config) @@ -737,7 +736,7 @@ def migrate(vm_, target, def stop(vm_): - ''' + """ Hard power down the virtual machine, this is equivalent to pulling the power @@ -746,9 +745,9 @@ def stop(vm_): .. code-block:: bash salt '*' virt.stop <vm name> - ''' + """ with _get_xapi_session() as xapi: - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False try: @@ -759,7 +758,7 @@ def stop(vm_): def is_hyper(): - ''' + """ Returns a bool whether or not this node is a hypervisor of any kind CLI Example: @@ -767,25 +766,25 @@ def is_hyper(): .. code-block:: bash salt '*' virt.is_hyper - ''' + """ try: - if __grains__['virtual_subtype'] != 'Xen Dom0': + if __grains__["virtual_subtype"] != "Xen Dom0": return False except KeyError: # virtual_subtype isn't set everywhere. return False try: - with salt.utils.files.fopen('/proc/modules') as fp_: - if 'xen_' not in salt.utils.stringutils.to_unicode(fp_.read()): + with salt.utils.files.fopen("/proc/modules") as fp_: + if "xen_" not in salt.utils.stringutils.to_unicode(fp_.read()): return False except (OSError, IOError): return False # there must be a smarter way... - return 'xenstore' in __salt__['cmd.run'](__grains__['ps']) + return "xenstore" in __salt__["cmd.run"](__grains__["ps"]) def vm_cputime(vm_=None): - ''' + """ Return cputime used by the vms on this hyper in a list of dicts: @@ -807,22 +806,26 @@ def vm_cputime(vm_=None): .. code-block:: bash salt '*' virt.vm_cputime - ''' + """ with _get_xapi_session() as xapi: + def _info(vm_): - host_rec = _get_record_by_label(xapi, 'VM', vm_) - host_cpus = len(host_rec['host_CPUs']) + host_rec = _get_record_by_label(xapi, "VM", vm_) + host_cpus = len(host_rec["host_CPUs"]) if host_rec is False: return False - host_metrics = _get_metrics_record(xapi, 'VM', host_rec) - vcpus = int(host_metrics['VCPUs_number']) - cputime = int(host_metrics['VCPUs_utilisation']['0']) + host_metrics = _get_metrics_record(xapi, "VM", host_rec) + vcpus = int(host_metrics["VCPUs_number"]) + cputime = int(host_metrics["VCPUs_utilisation"]["0"]) cputime_percent = 0 if cputime: # Divide by vcpus to always return a number between 0 and 100 cputime_percent = (1.0e-7 * cputime / host_cpus) / vcpus - return {'cputime': int(cputime), - 'cputime_percent': int('{0:.0f}'.format(cputime_percent))} + return { + "cputime": int(cputime), + "cputime_percent": int("{0:.0f}".format(cputime_percent)), + } + info = {} if vm_: info[vm_] = _info(vm_) @@ -835,7 +838,7 @@ def vm_cputime(vm_=None): def vm_netstats(vm_=None): - ''' + """ Return combined network counters used by the vms on this hyper in a list of dicts: @@ -859,18 +862,18 @@ def vm_netstats(vm_=None): .. code-block:: bash salt '*' virt.vm_netstats - ''' + """ with _get_xapi_session() as xapi: + def _info(vm_): ret = {} - vm_rec = _get_record_by_label(xapi, 'VM', vm_) + vm_rec = _get_record_by_label(xapi, "VM", vm_) if vm_rec is False: return False - for vif in vm_rec['VIFs']: - vif_rec = _get_record(xapi, 'VIF', vif) - ret[vif_rec['device']] = _get_metrics_record(xapi, 'VIF', - vif_rec) - del ret[vif_rec['device']]['last_updated'] + for vif in vm_rec["VIFs"]: + vif_rec = _get_record(xapi, "VIF", vif) + ret[vif_rec["device"]] = _get_metrics_record(xapi, "VIF", vif_rec) + del ret[vif_rec["device"]]["last_updated"] return ret @@ -884,7 +887,7 @@ def vm_netstats(vm_=None): def vm_diskstats(vm_=None): - ''' + """ Return disk usage counters used by the vms on this hyper in a list of dicts: @@ -906,18 +909,18 @@ def vm_diskstats(vm_=None): .. code-block:: bash salt '*' virt.vm_diskstats - ''' + """ with _get_xapi_session() as xapi: + def _info(vm_): ret = {} - vm_uuid = _get_label_uuid(xapi, 'VM', vm_) + vm_uuid = _get_label_uuid(xapi, "VM", vm_) if vm_uuid is False: return False for vbd in xapi.VM.get_VBDs(vm_uuid): - vbd_rec = _get_record(xapi, 'VBD', vbd) - ret[vbd_rec['device']] = _get_metrics_record(xapi, 'VBD', - vbd_rec) - del ret[vbd_rec['device']]['last_updated'] + vbd_rec = _get_record(xapi, "VBD", vbd) + ret[vbd_rec["device"]] = _get_metrics_record(xapi, "VBD", vbd_rec) + del ret[vbd_rec["device"]]["last_updated"] return ret diff --git a/salt/modules/xbpspkg.py b/salt/modules/xbpspkg.py index e493f8c80fd..3da001c750d 100644 --- a/salt/modules/xbpspkg.py +++ b/salt/modules/xbpspkg.py @@ -1,85 +1,86 @@ # -*- coding: utf-8 -*- -''' +""" Package support for XBPS package manager (used by VoidLinux) .. versionadded:: 2016.11.0 -''' +""" # TODO: what about the initial acceptance of repo's fingerprint when adding a # new repo? # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import glob +import logging import os import re -import logging -import glob # Import salt libs import salt.utils.data +import salt.utils.decorators as decorators import salt.utils.files import salt.utils.path import salt.utils.pkg import salt.utils.stringutils -import salt.utils.decorators as decorators from salt.exceptions import CommandExecutionError, MinionError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Set the virtual pkg module if the os is Void and xbps-install found - ''' - if __grains__['os'] in ('Void') and _check_xbps(): + """ + if __grains__["os"] in ("Void") and _check_xbps(): return __virtualname__ - return False + return (False, "Missing dependency: xbps-install") @decorators.memoize def _check_xbps(): - ''' + """ Looks to see if xbps-install is present on the system, return full path - ''' - return salt.utils.path.which('xbps-install') + """ + return salt.utils.path.which("xbps-install") @decorators.memoize def _get_version(): - ''' + """ Get the xbps version - ''' - version_string = __salt__['cmd.run']( - [_check_xbps(), '--version'], - output_loglevel='trace') + """ + version_string = __salt__["cmd.run"]( + [_check_xbps(), "--version"], output_loglevel="trace" + ) if version_string is None: # Dunno why it would, but... return False - VERSION_MATCH = re.compile(r'(?:XBPS:[\s]+)([\d.]+)(?:[\s]+.*)') + VERSION_MATCH = re.compile(r"(?:XBPS:[\s]+)([\d.]+)(?:[\s]+.*)") version_match = VERSION_MATCH.search(version_string) if not version_match: return False - return version_match.group(1).split('.') + return version_match.group(1).split(".") def _rehash(): - ''' + """ Recomputes internal hash table for the PATH variable. Used whenever a new command is created during the current session. - ''' - shell = __salt__['environ.get']('SHELL') - if shell.split('/')[-1] in ('csh', 'tcsh'): - __salt__['cmd.run']('rehash', output_loglevel='trace') + """ + shell = __salt__["environ.get"]("SHELL") + if shell.split("/")[-1] in ("csh", "tcsh"): + __salt__["cmd.run"]("rehash", output_loglevel="trace") def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed as a dict:: {'<package_name>': '<version>'} @@ -89,15 +90,17 @@ def list_pkgs(versions_as_list=False, **kwargs): .. code-block:: bash salt '*' pkg.list_pkgs - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - cmd = 'xbps-query -l' + cmd = "xbps-query -l" ret = {} - out = __salt__['cmd.run'](cmd, output_loglevel='trace') + out = __salt__["cmd.run"](cmd, output_loglevel="trace") for line in out.splitlines(): if not line: continue @@ -106,23 +109,20 @@ def list_pkgs(versions_as_list=False, **kwargs): # ii desktop-file-utils-0.22_4 Utilities to ... # # XXX handle package status (like 'ii') ? - pkg, ver = line.split(None)[1].rsplit('-', 1) + pkg, ver = line.split(None)[1].rsplit("-", 1) except ValueError: - log.error( - 'xbps-query: Unexpected formatting in line: "%s"', - line - ) + log.error('xbps-query: Unexpected formatting in line: "%s"', line) - __salt__['pkg_resource.add_pkg'](ret, pkg, ver) + __salt__["pkg_resource.add_pkg"](ret, pkg, ver) - __salt__['pkg_resource.sort_pkglist'](ret) + __salt__["pkg_resource.sort_pkglist"](ret) if not versions_as_list: - __salt__['pkg_resource.stringify'](ret) + __salt__["pkg_resource.stringify"](ret) return ret def list_upgrades(refresh=True): - ''' + """ Check whether or not an upgrade is available for all packages CLI Example: @@ -130,7 +130,7 @@ def list_upgrades(refresh=True): .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ # sample output of 'xbps-install -un': # fuse-2.9.4_4 update i686 http://repo.voidlinux.eu/current 298133 91688 @@ -145,30 +145,27 @@ def list_upgrades(refresh=True): ret = {} # retrieve list of updatable packages - cmd = 'xbps-install -un' - out = __salt__['cmd.run'](cmd, output_loglevel='trace') + cmd = "xbps-install -un" + out = __salt__["cmd.run"](cmd, output_loglevel="trace") for line in out.splitlines(): if not line: continue pkg = "base-system" ver = "NonNumericValueIsError" try: - pkg, ver = line.split()[0].rsplit('-', 1) + pkg, ver = line.split()[0].rsplit("-", 1) except (ValueError, IndexError): - log.error( - 'xbps-query: Unexpected formatting in line: "%s"', - line - ) + log.error('xbps-query: Unexpected formatting in line: "%s"', line) continue - log.trace('pkg=%s version=%s', pkg, ver) + log.trace("pkg=%s version=%s", pkg, ver) ret[pkg] = ver return ret def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -182,7 +179,7 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version <package name> salt '*' pkg.latest_version <package1> <package2> <package3> ... - ''' + """ # Why using 'xbps-install -un' and not 'xbps-query -R': # if several repos, xbps-query will produces this kind of output, @@ -199,10 +196,10 @@ def latest_version(*names, **kwargs): # xtools-0.34_1 update noarch http://repo.voidlinux.eu/current 21424 10752 # Package 'vim' is up to date. - refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) + refresh = salt.utils.data.is_true(kwargs.pop("refresh", True)) if len(names) == 0: - return '' + return "" # Refresh repo index before checking for latest version available if refresh: @@ -211,29 +208,26 @@ def latest_version(*names, **kwargs): # Initialize the dict with empty strings ret = {} for name in names: - ret[name] = '' + ret[name] = "" # retrieve list of updatable packages # ignore return code since 'is up to date' case produces retcode==17 (xbps 0.51) - cmd = ['xbps-install', '-un'] + cmd = ["xbps-install", "-un"] cmd.extend(names) - out = __salt__['cmd.run'](cmd, ignore_retcode=True, output_loglevel='trace') + out = __salt__["cmd.run"](cmd, ignore_retcode=True, output_loglevel="trace") for line in out.splitlines(): if not line: continue - if line.find(' is up to date.') != -1: + if line.find(" is up to date.") != -1: continue # retrieve tuple pkgname version try: - pkg, ver = line.split()[0].rsplit('-', 1) + pkg, ver = line.split()[0].rsplit("-", 1) except (ValueError, IndexError): - log.error( - 'xbps-query: Unexpected formatting in line: "%s"', - line - ) + log.error('xbps-query: Unexpected formatting in line: "%s"', line) continue - log.trace('pkg=%s version=%s', pkg, ver) + log.trace("pkg=%s version=%s", pkg, ver) if pkg in names: ret[pkg] = ver @@ -248,7 +242,7 @@ available_version = latest_version def upgrade_available(name): - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -256,12 +250,12 @@ def upgrade_available(name): .. code-block:: bash salt '*' pkg.upgrade_available <package name> - ''' - return latest_version(name) != '' + """ + return latest_version(name) != "" def refresh_db(): - ''' + """ Update list of available packages from installed repos CLI Example: @@ -269,15 +263,15 @@ def refresh_db(): .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) - cmd = 'xbps-install -Sy' - call = __salt__['cmd.run_all'](cmd, output_loglevel='trace') - if call['retcode'] != 0: - comment = '' - if 'stderr' in call: - comment += call['stderr'] + cmd = "xbps-install -Sy" + call = __salt__["cmd.run_all"](cmd, output_loglevel="trace") + if call["retcode"] != 0: + comment = "" + if "stderr" in call: + comment += call["stderr"] raise CommandExecutionError(comment) @@ -285,7 +279,7 @@ def refresh_db(): def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -296,12 +290,12 @@ def version(*names, **kwargs): salt '*' pkg.version <package name> salt '*' pkg.version <package1> <package2> <package3> ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def upgrade(refresh=True): - ''' + """ Run a full system upgrade refresh @@ -321,7 +315,7 @@ def upgrade(refresh=True): .. code-block:: bash salt '*' pkg.upgrade - ''' + """ # XXX if xbps has to be upgraded, 2 times is required to fully upgrade # system: one for xbps, a subsequent one for all other packages. Not @@ -329,26 +323,23 @@ def upgrade(refresh=True): old = list_pkgs() - cmd = ['xbps-install', '-{0}yu'.format('S' if refresh else '')] - result = __salt__['cmd.run_all'](cmd, - output_loglevel='trace', - python_shell=False) - __context__.pop('pkg.list_pkgs', None) + cmd = ["xbps-install", "-{0}yu".format("S" if refresh else "")] + result = __salt__["cmd.run_all"](cmd, output_loglevel="trace", python_shell=False) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret -def install(name=None, refresh=False, fromrepo=None, - pkgs=None, sources=None, **kwargs): - ''' +def install(name=None, refresh=False, fromrepo=None, pkgs=None, sources=None, **kwargs): + """ Install the passed package name @@ -394,12 +385,12 @@ def install(name=None, refresh=False, fromrepo=None, .. code-block:: bash salt '*' pkg.install <package name> - ''' + """ # XXX sources is not yet used in this code try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, **kwargs ) except MinionError as exc: @@ -408,22 +399,22 @@ def install(name=None, refresh=False, fromrepo=None, if pkg_params is None or len(pkg_params) == 0: return {} - if pkg_type != 'repository': + if pkg_type != "repository": log.error('xbps: pkg_type "%s" not supported.', pkg_type) return {} - cmd = ['xbps-install'] + cmd = ["xbps-install"] if refresh: - cmd.append('-S') # update repo db + cmd.append("-S") # update repo db if fromrepo: - cmd.append('--repository={0}'.format(fromrepo)) - cmd.append('-y') # assume yes when asked + cmd.append("--repository={0}".format(fromrepo)) + cmd.append("-y") # assume yes when asked cmd.extend(pkg_params) old = list_pkgs() - __salt__['cmd.run'](cmd, output_loglevel='trace') - __context__.pop('pkg.list_pkgs', None) + __salt__["cmd.run"](cmd, output_loglevel="trace") + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() _rehash() @@ -431,7 +422,7 @@ def install(name=None, refresh=False, fromrepo=None, def remove(name=None, pkgs=None, recursive=True, **kwargs): - ''' + """ name The name of the package to be deleted. @@ -454,12 +445,10 @@ def remove(name=None, pkgs=None, recursive=True, **kwargs): salt '*' pkg.remove <package name> [recursive=False] salt '*' pkg.remove <package1>,<package2>,<package3> [recursive=False] salt '*' pkg.remove pkgs='["foo", "bar"]' [recursive=False] - ''' + """ try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( - name, pkgs - ) + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"](name, pkgs) except MinionError as exc: raise CommandExecutionError(exc) @@ -473,19 +462,19 @@ def remove(name=None, pkgs=None, recursive=True, **kwargs): if not targets: return {} - cmd = ['xbps-remove', '-y'] + cmd = ["xbps-remove", "-y"] if recursive: - cmd.append('-R') + cmd.append("-R") cmd.extend(targets) - __salt__['cmd.run'](cmd, output_loglevel='trace') - __context__.pop('pkg.list_pkgs', None) + __salt__["cmd.run"](cmd, output_loglevel="trace") + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() return salt.utils.data.compare_dicts(old, new) def list_repos(): - ''' + """ List all repos known by XBPS CLI Example: @@ -493,29 +482,29 @@ def list_repos(): .. code-block:: bash salt '*' pkg.list_repos - ''' + """ repos = {} - out = __salt__['cmd.run']('xbps-query -L', output_loglevel='trace') + out = __salt__["cmd.run"]("xbps-query -L", output_loglevel="trace") for line in out.splitlines(): repo = {} if not line: continue try: - nb, url, rsa = line.strip().split(' ', 2) + nb, url, rsa = line.strip().split(" ", 2) except ValueError: log.error( - 'Problem parsing xbps-query: ' - 'Unexpected formatting in line: "%s"', line + "Problem parsing xbps-query: " 'Unexpected formatting in line: "%s"', + line, ) - repo['nbpkg'] = int(nb) if nb.isdigit() else 0 - repo['url'] = url - repo['rsasigned'] = True if rsa == '(RSA signed)' else False - repos[repo['url']] = repo + repo["nbpkg"] = int(nb) if nb.isdigit() else 0 + repo["url"] = url + repo["rsasigned"] = True if rsa == "(RSA signed)" else False + repos[repo["url"]] = repo return repos def get_repo(repo, **kwargs): - ''' + """ Display information about the repo. CLI Examples: @@ -523,7 +512,7 @@ def get_repo(repo, **kwargs): .. code-block:: bash salt '*' pkg.get_repo 'repo-url' - ''' + """ repos = list_repos() if repo in repos: return repos[repo] @@ -531,7 +520,7 @@ def get_repo(repo, **kwargs): def _locate_repo_files(repo, rewrite=False): - ''' + """ Find what file a repo is called in. Helper function for add_repo() and del_repo() @@ -543,23 +532,23 @@ def _locate_repo_files(repo, rewrite=False): Whether to remove matching repository settings during this process. Returns a list of absolute paths. - ''' + """ ret_val = [] files = [] - conf_dirs = ['/etc/xbps.d/', '/usr/share/xbps.d/'] - name_glob = '*.conf' + conf_dirs = ["/etc/xbps.d/", "/usr/share/xbps.d/"] + name_glob = "*.conf" # Matches a line where first printing is "repository" and there is an equals # sign before the repo, an optional forwardslash at the end of the repo name, # and it's possible for there to be a comment after repository=repo - regex = re.compile(r'\s*repository\s*=\s*'+repo+r'/?\s*(#.*)?$') + regex = re.compile(r"\s*repository\s*=\s*" + repo + r"/?\s*(#.*)?$") for cur_dir in conf_dirs: - files.extend(glob.glob(cur_dir+name_glob)) + files.extend(glob.glob(cur_dir + name_glob)) for filename in files: write_buff = [] - with salt.utils.files.fopen(filename, 'r') as cur_file: + with salt.utils.files.fopen(filename, "r") as cur_file: for line in cur_file: if regex.match(salt.utils.stringutils.to_unicode(line)): ret_val.append(filename) @@ -567,7 +556,7 @@ def _locate_repo_files(repo, rewrite=False): write_buff.append(line) if rewrite and filename in ret_val: if len(write_buff) > 0: - with salt.utils.files.fopen(filename, 'w') as rewrite_file: + with salt.utils.files.fopen(filename, "w") as rewrite_file: rewrite_file.writelines(write_buff) else: # Prune empty files os.remove(filename) @@ -575,8 +564,8 @@ def _locate_repo_files(repo, rewrite=False): return ret_val -def add_repo(repo, conffile='/usr/share/xbps.d/15-saltstack.conf'): - ''' +def add_repo(repo, conffile="/usr/share/xbps.d/15-saltstack.conf"): + """ Add an XBPS repository to the system. repo @@ -591,15 +580,13 @@ def add_repo(repo, conffile='/usr/share/xbps.d/15-saltstack.conf'): .. code-block:: bash salt '*' pkg.add_repo <repo url> [conffile=/path/to/xbps/repo.conf] - ''' + """ if len(_locate_repo_files(repo)) == 0: try: - with salt.utils.files.fopen(conffile, 'a+') as conf_file: + with salt.utils.files.fopen(conffile, "a+") as conf_file: conf_file.write( - salt.utils.stringutils.to_str( - 'repository={0}\n'.format(repo) - ) + salt.utils.stringutils.to_str("repository={0}\n".format(repo)) ) except IOError: return False @@ -608,7 +595,7 @@ def add_repo(repo, conffile='/usr/share/xbps.d/15-saltstack.conf'): def del_repo(repo): - ''' + """ Remove an XBPS repository from the system. repo @@ -619,7 +606,7 @@ def del_repo(repo): .. code-block:: bash salt '*' pkg.del_repo <repo url> - ''' + """ try: _locate_repo_files(repo, rewrite=True) diff --git a/salt/modules/xfs.py b/salt/modules/xfs.py index ce7bd187fec..f6175876066 100644 --- a/salt/modules/xfs.py +++ b/salt/modules/xfs.py @@ -21,57 +21,58 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -''' +""" Module for managing XFS file systems. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import re import time -import logging + +import salt.utils.data # Import Salt libs import salt.utils.files import salt.utils.path import salt.utils.platform -import salt.utils.data from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext.six.moves import range log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work on POSIX-like systems - ''' - return not salt.utils.platform.is_windows() \ - and __grains__.get('kernel') == 'Linux' + """ + return not salt.utils.platform.is_windows() and __grains__.get("kernel") == "Linux" def _verify_run(out, cmd=None): - ''' + """ Crash to the log if command execution was not successful. - ''' - if out.get("retcode", 0) and out['stderr']: + """ + if out.get("retcode", 0) and out["stderr"]: if cmd: log.debug('Command: "%s"', cmd) - log.debug('Return code: %s', out.get('retcode')) - log.debug('Error output:\n%s', out.get('stderr', "N/A")) + log.debug("Return code: %s", out.get("retcode")) + log.debug("Error output:\n%s", out.get("stderr", "N/A")) - raise CommandExecutionError(out['stderr']) + raise CommandExecutionError(out["stderr"]) def _xfs_info_get_kv(serialized): - ''' + """ Parse one line of the XFS info output. - ''' + """ # No need to know sub-elements here if serialized.startswith("="): serialized = serialized[1:].strip() @@ -91,11 +92,11 @@ def _xfs_info_get_kv(serialized): def _parse_xfs_info(data): - ''' + """ Parse output from "xfs_info" or "xfs_growfs -n". - ''' + """ ret = {} - spr = re.compile(r'\s+') + spr = re.compile(r"\s+") entry = None for line in [spr.sub(" ", l).strip().replace(", ", " ") for l in data.split("\n")]: if not line: @@ -103,14 +104,14 @@ def _parse_xfs_info(data): nfo = _xfs_info_get_kv(line) if not line.startswith("="): entry = nfo.pop(0) - ret[entry[0]] = {'section': entry[(entry[1] != '***' and 1 or 0)]} + ret[entry[0]] = {"section": entry[(entry[1] != "***" and 1 or 0)]} ret[entry[0]].update(dict(nfo)) return ret def info(device): - ''' + """ Get filesystem geometry information. CLI Example: @@ -118,17 +119,17 @@ def info(device): .. code-block:: bash salt '*' xfs.info /dev/sda1 - ''' - out = __salt__['cmd.run_all']("xfs_info {0}".format(device)) - if out.get('stderr'): - raise CommandExecutionError(out['stderr'].replace("xfs_info:", "").strip()) - return _parse_xfs_info(out['stdout']) + """ + out = __salt__["cmd.run_all"]("xfs_info {0}".format(device)) + if out.get("stderr"): + raise CommandExecutionError(out["stderr"].replace("xfs_info:", "").strip()) + return _parse_xfs_info(out["stdout"]) def _xfsdump_output(data): - ''' + """ Parse CLI output of the xfsdump utility. - ''' + """ out = {} summary = [] summary_block = False @@ -136,15 +137,15 @@ def _xfsdump_output(data): for line in [l.strip() for l in data.split("\n") if l.strip()]: line = re.sub("^xfsdump: ", "", line) if line.startswith("session id:"): - out['Session ID'] = line.split(" ")[-1] + out["Session ID"] = line.split(" ")[-1] elif line.startswith("session label:"): - out['Session label'] = re.sub("^session label: ", "", line) + out["Session label"] = re.sub("^session label: ", "", line) elif line.startswith("media file size"): - out['Media size'] = re.sub(r"^media file size\s+", "", line) + out["Media size"] = re.sub(r"^media file size\s+", "", line) elif line.startswith("dump complete:"): - out['Dump complete'] = re.sub(r"^dump complete:\s+", "", line) + out["Dump complete"] = re.sub(r"^dump complete:\s+", "", line) elif line.startswith("Dump Status:"): - out['Status'] = re.sub(r"^Dump Status:\s+", "", line) + out["Status"] = re.sub(r"^Dump Status:\s+", "", line) elif line.startswith("Dump Summary:"): summary_block = True continue @@ -155,13 +156,13 @@ def _xfsdump_output(data): summary_block = False if summary: - out['Summary'] = ' '.join(summary) + out["Summary"] = " ".join(summary) return out def dump(device, destination, level=0, label=None, noerase=None): - ''' + """ Dump filesystem device to the media (file, tape etc). Required parameters: @@ -186,32 +187,37 @@ def dump(device, destination, level=0, label=None, noerase=None): salt '*' xfs.dump /dev/sda1 /detination/on/the/client salt '*' xfs.dump /dev/sda1 /detination/on/the/client label='Company accountancy' salt '*' xfs.dump /dev/sda1 /detination/on/the/client noerase=True - ''' + """ if not salt.utils.path.which("xfsdump"): - raise CommandExecutionError("Utility \"xfsdump\" has to be installed or missing.") + raise CommandExecutionError('Utility "xfsdump" has to be installed or missing.') - label = label and label or time.strftime("XFS dump for \"{0}\" of %Y.%m.%d, %H:%M".format(device), - time.localtime()).replace("'", '"') + label = ( + label + and label + or time.strftime( + 'XFS dump for "{0}" of %Y.%m.%d, %H:%M'.format(device), time.localtime() + ).replace("'", '"') + ) cmd = ["xfsdump"] - cmd.append("-F") # Force + cmd.append("-F") # Force if not noerase: - cmd.append("-E") # pre-erase - cmd.append("-L '{0}'".format(label)) # Label - cmd.append("-l {0}".format(level)) # Dump level + cmd.append("-E") # pre-erase + cmd.append("-L '{0}'".format(label)) # Label + cmd.append("-l {0}".format(level)) # Dump level cmd.append("-f {0}".format(destination)) # Media destination - cmd.append(device) # Device + cmd.append(device) # Device - cmd = ' '.join(cmd) - out = __salt__['cmd.run_all'](cmd) + cmd = " ".join(cmd) + out = __salt__["cmd.run_all"](cmd) _verify_run(out, cmd=cmd) - return _xfsdump_output(out['stdout']) + return _xfsdump_output(out["stdout"]) def _xr_to_keyset(line): - ''' + """ Parse xfsrestore output keyset elements. - ''' + """ tkns = [elm for elm in line.strip().split(":", 1) if elm] if len(tkns) == 1: return "'{0}': ".format(tkns[0]) @@ -221,15 +227,15 @@ def _xr_to_keyset(line): def _xfs_inventory_output(out): - ''' + """ Transform xfsrestore inventory data output to a Python dict source and evaluate it. - ''' + """ data = [] out = [line for line in out.split("\n") if line.strip()] # No inventory yet - if len(out) == 1 and 'restore status' in out[0].lower(): - return {'restore_status': out[0]} + if len(out) == 1 and "restore status" in out[0].lower(): + return {"restore_status": out[0]} ident = 0 data.append("{") @@ -250,14 +256,14 @@ def _xfs_inventory_output(out): # We are evaling into a python dict, a json load # would be safer - data = eval('\n'.join(data))[0] # pylint: disable=W0123 - data['restore_status'] = out[-1] + data = eval("\n".join(data))[0] # pylint: disable=W0123 + data["restore_status"] = out[-1] return data def inventory(): - ''' + """ Display XFS dump inventory without restoration. CLI Example: @@ -265,17 +271,17 @@ def inventory(): .. code-block:: bash salt '*' xfs.inventory - ''' - out = __salt__['cmd.run_all']("xfsrestore -I") + """ + out = __salt__["cmd.run_all"]("xfsrestore -I") _verify_run(out) - return _xfs_inventory_output(out['stdout']) + return _xfs_inventory_output(out["stdout"]) def _xfs_prune_output(out, uuid): - ''' + """ Parse prune output. - ''' + """ data = {} cnt = [] cutpoint = False @@ -290,15 +296,15 @@ def _xfs_prune_output(out, uuid): if cutpoint: cnt.append(line) - for kset in [e for e in cnt[1:] if ':' in e]: + for kset in [e for e in cnt[1:] if ":" in e]: key, val = [t.strip() for t in kset.split(":", 1)] data[key.lower().replace(" ", "_")] = val - return data.get('uuid') == uuid and data or {} + return data.get("uuid") == uuid and data or {} def prune_dump(sessionid): - ''' + """ Prunes the dump session identified by the given session id. CLI Example: @@ -307,21 +313,21 @@ def prune_dump(sessionid): salt '*' xfs.prune_dump b74a3586-e52e-4a4a-8775-c3334fa8ea2c - ''' - out = __salt__['cmd.run_all']("xfsinvutil -s {0} -F".format(sessionid)) + """ + out = __salt__["cmd.run_all"]("xfsinvutil -s {0} -F".format(sessionid)) _verify_run(out) - data = _xfs_prune_output(out['stdout'], sessionid) + data = _xfs_prune_output(out["stdout"], sessionid) if data: return data - raise CommandExecutionError("Session UUID \"{0}\" was not found.".format(sessionid)) + raise CommandExecutionError('Session UUID "{0}" was not found.'.format(sessionid)) def _blkid_output(out): - ''' + """ Parse blkid output. - ''' + """ flt = lambda data: [el for el in data if el.strip()] data = {} for dev_meta in flt(out.split("\n\n")): @@ -330,7 +336,7 @@ def _blkid_output(out): key, val = items.split("=", 1) dev[key.lower()] = val if dev.pop("type") == "xfs": - dev['label'] = dev.get('label') + dev["label"] = dev.get("label") data[dev.pop("devname")] = dev mounts = _get_mounts() @@ -342,7 +348,7 @@ def _blkid_output(out): def devices(): - ''' + """ Get known XFS formatted devices on the system. CLI Example: @@ -350,33 +356,33 @@ def devices(): .. code-block:: bash salt '*' xfs.devices - ''' - out = __salt__['cmd.run_all']("blkid -o export") + """ + out = __salt__["cmd.run_all"]("blkid -o export") _verify_run(out) - return _blkid_output(out['stdout']) + return _blkid_output(out["stdout"]) def _xfs_estimate_output(out): - ''' + """ Parse xfs_estimate output. - ''' + """ spc = re.compile(r"\s+") data = {} for line in [l for l in out.split("\n") if l.strip()][1:]: directory, bsize, blocks, megabytes, logsize = spc.sub(" ", line).split(" ") data[directory] = { - 'block _size': bsize, - 'blocks': blocks, - 'megabytes': megabytes, - 'logsize': logsize, + "block _size": bsize, + "blocks": blocks, + "megabytes": megabytes, + "logsize": logsize, } return data def estimate(path): - ''' + """ Estimate the space that an XFS filesystem will take. For each directory estimate the space that directory would take if it were copied to an XFS filesystem. @@ -388,19 +394,30 @@ def estimate(path): salt '*' xfs.estimate /path/to/file salt '*' xfs.estimate /path/to/dir/* - ''' + """ if not os.path.exists(path): - raise CommandExecutionError("Path \"{0}\" was not found.".format(path)) + raise CommandExecutionError('Path "{0}" was not found.'.format(path)) - out = __salt__['cmd.run_all']("xfs_estimate -v {0}".format(path)) + out = __salt__["cmd.run_all"]("xfs_estimate -v {0}".format(path)) _verify_run(out) return _xfs_estimate_output(out["stdout"]) -def mkfs(device, label=None, ssize=None, noforce=None, - bso=None, gmo=None, ino=None, lso=None, rso=None, nmo=None, dso=None): - ''' +def mkfs( + device, + label=None, + ssize=None, + noforce=None, + bso=None, + gmo=None, + ino=None, + lso=None, + rso=None, + nmo=None, + dso=None, +): + """ Create a file system on the specified device. By default wipes out with force. General options: @@ -429,10 +446,13 @@ def mkfs(device, label=None, ssize=None, noforce=None, salt '*' xfs.mkfs /dev/sda1 salt '*' xfs.mkfs /dev/sda1 dso='su=32k,sw=6' noforce=True salt '*' xfs.mkfs /dev/sda1 dso='su=32k,sw=6' lso='logdev=/dev/sda2,size=10000b' - ''' + """ - getopts = lambda args: dict(((args and ("=" in args) - and args or None)) and [kw.split("=") for kw in args.split(",")] or []) + getopts = lambda args: dict( + ((args and ("=" in args) and args or None)) + and [kw.split("=") for kw in args.split(",")] + or [] + ) cmd = ["mkfs.xfs"] if label: cmd.append("-L") @@ -442,28 +462,37 @@ def mkfs(device, label=None, ssize=None, noforce=None, cmd.append("-s") cmd.append(ssize) - for switch, opts in [("-b", bso), ("-m", gmo), ("-n", nmo), ("-i", ino), - ("-d", dso), ("-l", lso), ("-r", rso)]: + for switch, opts in [ + ("-b", bso), + ("-m", gmo), + ("-n", nmo), + ("-i", ino), + ("-d", dso), + ("-l", lso), + ("-r", rso), + ]: try: if getopts(opts): cmd.append(switch) cmd.append(opts) except Exception: # pylint: disable=broad-except - raise CommandExecutionError("Wrong parameters \"{0}\" for option \"{1}\"".format(opts, switch)) + raise CommandExecutionError( + 'Wrong parameters "{0}" for option "{1}"'.format(opts, switch) + ) if not noforce: cmd.append("-f") cmd.append(device) - cmd = ' '.join(cmd) - out = __salt__['cmd.run_all'](cmd) + cmd = " ".join(cmd) + out = __salt__["cmd.run_all"](cmd) _verify_run(out, cmd=cmd) - return _parse_xfs_info(out['stdout']) + return _parse_xfs_info(out["stdout"]) def modify(device, label=None, lazy_counting=None, uuid=None): - ''' + """ Modify parameters of an XFS filesystem. CLI Example: @@ -473,11 +502,13 @@ def modify(device, label=None, lazy_counting=None, uuid=None): salt '*' xfs.modify /dev/sda1 label='My backup' lazy_counting=False salt '*' xfs.modify /dev/sda1 uuid=False salt '*' xfs.modify /dev/sda1 uuid=True - ''' + """ if not label and lazy_counting is None and uuid is None: - raise CommandExecutionError("Nothing specified for modification for \"{0}\" device".format(device)) + raise CommandExecutionError( + 'Nothing specified for modification for "{0}" device'.format(device) + ) - cmd = ['xfs_admin'] + cmd = ["xfs_admin"] if label: cmd.append("-L") cmd.append("'{0}'".format(label)) @@ -497,35 +528,37 @@ def modify(device, label=None, lazy_counting=None, uuid=None): cmd.append("generate") cmd.append(device) - cmd = ' '.join(cmd) - _verify_run(__salt__['cmd.run_all'](cmd), cmd=cmd) + cmd = " ".join(cmd) + _verify_run(__salt__["cmd.run_all"](cmd), cmd=cmd) - out = __salt__['cmd.run_all']("blkid -o export {0}".format(device)) + out = __salt__["cmd.run_all"]("blkid -o export {0}".format(device)) _verify_run(out) - return _blkid_output(out['stdout']) + return _blkid_output(out["stdout"]) def _get_mounts(): - ''' + """ List mounted filesystems. - ''' + """ mounts = {} with salt.utils.files.fopen("/proc/mounts") as fhr: for line in salt.utils.data.decode(fhr.readlines()): - device, mntpnt, fstype, options, fs_freq, fs_passno = line.strip().split(" ") - if fstype != 'xfs': + device, mntpnt, fstype, options, fs_freq, fs_passno = line.strip().split( + " " + ) + if fstype != "xfs": continue mounts[device] = { - 'mount_point': mntpnt, - 'options': options.split(","), + "mount_point": mntpnt, + "options": options.split(","), } return mounts def defragment(device): - ''' + """ Defragment mounted XFS filesystem. In order to mount a filesystem, device should be properly mounted and writable. @@ -534,16 +567,14 @@ def defragment(device): .. code-block:: bash salt '*' xfs.defragment /dev/sda1 - ''' - if device == '/': + """ + if device == "/": raise CommandExecutionError("Root is not a device.") if not _get_mounts().get(device): - raise CommandExecutionError("Device \"{0}\" is not mounted".format(device)) + raise CommandExecutionError('Device "{0}" is not mounted'.format(device)) - out = __salt__['cmd.run_all']("xfs_fsr {0}".format(device)) + out = __salt__["cmd.run_all"]("xfs_fsr {0}".format(device)) _verify_run(out) - return { - 'log': out['stdout'] - } + return {"log": out["stdout"]} diff --git a/salt/modules/xml.py b/salt/modules/xml.py index 9c0d6b4c107..9e1d1f6e877 100644 --- a/salt/modules/xml.py +++ b/salt/modules/xml.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" XML file manager .. versionadded:: 3000 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging @@ -13,18 +13,18 @@ log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'xml' +__virtualname__ = "xml" def __virtual__(): - ''' + """ Only load the module if all modules are imported correctly. - ''' + """ return __virtualname__ def get_value(file, element): - ''' + """ Returns the value of the matched xpath element CLI Example: @@ -32,7 +32,7 @@ def get_value(file, element): .. code-block:: bash salt '*' xml.get_value /tmp/test.xml ".//element" - ''' + """ try: root = ET.parse(file) element = root.find(element) @@ -43,7 +43,7 @@ def get_value(file, element): def set_value(file, element, value): - ''' + """ Sets the value of the matched xpath element CLI Example: @@ -51,7 +51,7 @@ def set_value(file, element, value): .. code-block:: bash salt '*' xml.set_value /tmp/test.xml ".//element" "new value" - ''' + """ try: root = ET.parse(file) relement = root.find(element) @@ -64,7 +64,7 @@ def set_value(file, element, value): def get_attribute(file, element): - ''' + """ Return the attributes of the matched xpath element. CLI Example: @@ -72,7 +72,7 @@ def get_attribute(file, element): .. code-block:: bash salt '*' xml.get_attribute /tmp/test.xml ".//element[@id='3']" - ''' + """ try: root = ET.parse(file) element = root.find(element) @@ -83,7 +83,7 @@ def get_attribute(file, element): def set_attribute(file, element, key, value): - ''' + """ Set the requested attribute key and value for matched xpath element. CLI Example: @@ -91,7 +91,7 @@ def set_attribute(file, element, key, value): .. code-block:: bash salt '*' xml.set_attribute /tmp/test.xml ".//element[@id='3']" editedby "gal" - ''' + """ try: root = ET.parse(file) element = root.find(element) diff --git a/salt/modules/xmpp.py b/salt/modules/xmpp.py index 40f9bfb89a4..251580a733c 100644 --- a/salt/modules/xmpp.py +++ b/salt/modules/xmpp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for Sending Messages via XMPP (a.k.a. Jabber) .. versionadded:: 2014.1.0 @@ -33,7 +33,7 @@ Module for Sending Messages via XMPP (a.k.a. Jabber) xmpp.jid: myuser@jabber.example.org xmpp.password: verybadpass -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -44,24 +44,27 @@ HAS_LIBS = False try: from sleekxmpp import ClientXMPP as _ClientXMPP from sleekxmpp.exceptions import XMPPError + HAS_LIBS = True except ImportError: + class _ClientXMPP(object): - ''' + """ Fake class in order not to raise errors - ''' + """ + log = logging.getLogger(__name__) -__virtualname__ = 'xmpp' +__virtualname__ = "xmpp" MUC_DEPRECATED = "Use of send mask waiters is deprecated." def __virtual__(): - ''' + """ Only load this module if sleekxmpp is installed on this minion. - ''' + """ if HAS_LIBS: return __virtualname__ return (False, "Module xmpp: required libraries failed to load") @@ -73,7 +76,6 @@ class SleekXMPPMUC(logging.Filter): class SendMsgBot(_ClientXMPP): - def __init__(self, jid, password, recipient, msg): # PyLint wrongly reports an error when calling super, hence the above # disable call @@ -84,14 +86,15 @@ class SendMsgBot(_ClientXMPP): self.msg = msg - self.add_event_handler('session_start', self.start) + self.add_event_handler("session_start", self.start) @classmethod - def create_multi(cls, jid, password, msg, recipients=None, rooms=None, - nick="SaltStack Bot"): - ''' + def create_multi( + cls, jid, password, msg, recipients=None, rooms=None, nick="SaltStack Bot" + ): + """ Alternate constructor that accept multiple recipients and rooms - ''' + """ obj = SendMsgBot(jid, password, None, msg) obj.recipients = [] if recipients is None else recipients obj.rooms = [] if rooms is None else rooms @@ -103,23 +106,17 @@ class SendMsgBot(_ClientXMPP): self.get_roster() for recipient in self.recipients: - self.send_message(mto=recipient, - mbody=self.msg, - mtype='chat') + self.send_message(mto=recipient, mbody=self.msg, mtype="chat") for room in self.rooms: - self.plugin['xep_0045'].joinMUC(room, - self.nick, - wait=True) - self.send_message(mto=room, - mbody=self.msg, - mtype='groupchat') + self.plugin["xep_0045"].joinMUC(room, self.nick, wait=True) + self.send_message(mto=room, mbody=self.msg, mtype="groupchat") self.disconnect(wait=True) def send_msg(recipient, message, jid=None, password=None, profile=None): - ''' + """ Send a message to an XMPP recipient. Designed for use in states. CLI Examples: @@ -130,15 +127,15 @@ def send_msg(recipient, message, jid=None, password=None, profile=None): profile='my-xmpp-account' xmpp.send_msg 'admins@xmpp.example.com' 'This is a salt module test' \ jid='myuser@xmpp.example.com/salt' password='verybadpass' - ''' + """ if profile: - creds = __salt__['config.option'](profile) - jid = creds.get('xmpp.jid') - password = creds.get('xmpp.password') + creds = __salt__["config.option"](profile) + jid = creds.get("xmpp.jid") + password = creds.get("xmpp.password") xmpp = SendMsgBot(jid, password, recipient, message) - xmpp.register_plugin('xep_0030') # Service Discovery - xmpp.register_plugin('xep_0199') # XMPP Ping + xmpp.register_plugin("xep_0030") # Service Discovery + xmpp.register_plugin("xep_0199") # XMPP Ping if xmpp.connect(): xmpp.process(block=True) @@ -146,14 +143,16 @@ def send_msg(recipient, message, jid=None, password=None, profile=None): return False -def send_msg_multi(message, - recipients=None, - rooms=None, - jid=None, - password=None, - nick="SaltStack Bot", - profile=None): - ''' +def send_msg_multi( + message, + recipients=None, + rooms=None, + jid=None, + password=None, + nick="SaltStack Bot", + profile=None, +): + """ Send a message to an XMPP recipient, support send message to multiple recipients or chat room. @@ -170,22 +169,23 @@ def send_msg_multi(message, 'This is a salt module test' \ jid='myuser@xmpp.example.com/salt' password='verybadpass' - ''' + """ # Remove: [WARNING ] Use of send mask waiters is deprecated. for handler in logging.root.handlers: handler.addFilter(SleekXMPPMUC()) if profile: - creds = __salt__['config.option'](profile) - jid = creds.get('xmpp.jid') - password = creds.get('xmpp.password') + creds = __salt__["config.option"](profile) + jid = creds.get("xmpp.jid") + password = creds.get("xmpp.password") xmpp = SendMsgBot.create_multi( - jid, password, message, recipients=recipients, rooms=rooms, nick=nick) + jid, password, message, recipients=recipients, rooms=rooms, nick=nick + ) if rooms: - xmpp.register_plugin('xep_0045') # MUC plugin + xmpp.register_plugin("xep_0045") # MUC plugin if xmpp.connect(): try: xmpp.process(block=True) diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py index f7e4ac9753c..e17655290ee 100644 --- a/salt/modules/yumpkg.py +++ b/salt/modules/yumpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for YUM/DNF .. important:: @@ -12,10 +12,11 @@ Support for YUM/DNF DNF is fully supported as of version 2015.5.10 and 2015.8.4 (partial support for DNF was initially added in 2015.8.0), and DNF is used automatically in place of YUM in Fedora 22 and newer. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import contextlib import datetime import fnmatch @@ -25,25 +26,11 @@ import os import re import string -# pylint: disable=import-error,redefined-builtin -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import zip - -try: - import yum - HAS_YUM = True -except ImportError: - HAS_YUM = False - -from salt.ext.six.moves import configparser - -# pylint: enable=import-error,redefined-builtin - # Import Salt libs import salt.utils.args import salt.utils.data import salt.utils.decorators.path +import salt.utils.environment import salt.utils.files import salt.utils.functools import salt.utils.itertools @@ -53,151 +40,172 @@ import salt.utils.pkg import salt.utils.pkg.rpm import salt.utils.systemd import salt.utils.versions -from salt.utils.versions import LooseVersion as _LooseVersion -import salt.utils.environment -from salt.exceptions import ( - CommandExecutionError, MinionError, SaltInvocationError -) +from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError +# Import 3rd-party libs +# pylint: disable=import-error,redefined-builtin # Import 3rd-party libs from salt.ext import six +from salt.ext.six.moves import configparser, zip +from salt.utils.versions import LooseVersion as _LooseVersion + +try: + import yum + + HAS_YUM = True +except ImportError: + HAS_YUM = False + + +# pylint: enable=import-error,redefined-builtin + log = logging.getLogger(__name__) -__HOLD_PATTERN = r'[\w+]+(?:[.-][^-]+)*' +__HOLD_PATTERN = r"[\w+]+(?:[.-][^-]+)*" -PKG_ARCH_SEPARATOR = '.' +PKG_ARCH_SEPARATOR = "." # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Confine this module to yum based systems - ''' - if __opts__.get('yum_provider') == 'yumpkg_api': + """ + if __opts__.get("yum_provider") == "yumpkg_api": return (False, "Module yumpkg: yumpkg_api provider not available") try: - os_grain = __grains__['os'].lower() - os_family = __grains__['os_family'].lower() + os_grain = __grains__["os"].lower() + os_family = __grains__["os_family"].lower() except Exception: # pylint: disable=broad-except return (False, "Module yumpkg: no yum based system detected") - enabled = ('amazon', 'xcp', 'xenserver', 'virtuozzolinux', 'virtuozzo') + enabled = ("amazon", "xcp", "xenserver", "virtuozzolinux", "virtuozzo") - if os_family == 'redhat' or os_grain in enabled: + if os_family == "redhat" or os_grain in enabled: return __virtualname__ return (False, "Module yumpkg: no yum based system detected") def _strip_headers(output, *args): if not args: - args_lc = ('installed packages', - 'available packages', - 'available upgrades', - 'updated packages', - 'upgraded packages') + args_lc = ( + "installed packages", + "available packages", + "available upgrades", + "updated packages", + "upgraded packages", + ) else: args_lc = [x.lower() for x in args] - ret = '' - for line in salt.utils.itertools.split(output, '\n'): + ret = "" + for line in salt.utils.itertools.split(output, "\n"): if line.lower() not in args_lc: - ret += line + '\n' + ret += line + "\n" return ret def _get_hold(line, pattern=__HOLD_PATTERN, full=True): - ''' + """ Resolve a package name from a line containing the hold expression. If the regex is not matched, None is returned. yum ==> 2:vim-enhanced-7.4.629-5.el6.* dnf ==> vim-enhanced-2:7.4.827-1.fc22.* - ''' + """ if full: - if _yum() == 'dnf': - lock_re = r'({0}-\S+)'.format(pattern) + if _yum() == "dnf": + lock_re = r"({0}-\S+)".format(pattern) else: - lock_re = r'(\d+:{0}-\S+)'.format(pattern) + lock_re = r"(\d+:{0}-\S+)".format(pattern) else: - if _yum() == 'dnf': - lock_re = r'({0}-\S+)'.format(pattern) + if _yum() == "dnf": + lock_re = r"({0}-\S+)".format(pattern) else: - lock_re = r'\d+:({0}-\S+)'.format(pattern) + lock_re = r"\d+:({0}-\S+)".format(pattern) match = re.search(lock_re, line) if match: if not full: - woarch = match.group(1).rsplit('.', 1)[0] - worel = woarch.rsplit('-', 1)[0] - return worel.rsplit('-', 1)[0] + woarch = match.group(1).rsplit(".", 1)[0] + worel = woarch.rsplit("-", 1)[0] + return worel.rsplit("-", 1)[0] else: return match.group(1) return None def _yum(): - ''' + """ Determine package manager name (yum or dnf), depending on the system version. - ''' - contextkey = 'yum_bin' + """ + contextkey = "yum_bin" if contextkey not in __context__: - if ('fedora' in __grains__['os'].lower() - and int(__grains__['osrelease']) >= 22): - __context__[contextkey] = 'dnf' + if ( + "fedora" in __grains__["os"].lower() and int(__grains__["osrelease"]) >= 22 + ) or ( + __grains__["os"].lower() in ("redhat", "centos") + and int(__grains__["osmajorrelease"]) >= 8 + ): + __context__[contextkey] = "dnf" else: - __context__[contextkey] = 'yum' + __context__[contextkey] = "yum" return __context__[contextkey] def _call_yum(args, **kwargs): - ''' + """ Call yum/dnf. - ''' - params = {'output_loglevel': 'trace', - 'python_shell': False, - 'env': salt.utils.environment.get_module_environment(globals())} + """ + params = { + "output_loglevel": "trace", + "python_shell": False, + "env": salt.utils.environment.get_module_environment(globals()), + } params.update(kwargs) cmd = [] - if salt.utils.systemd.has_scope(__context__) and __salt__['config.get']('systemd.scope', True): - cmd.extend(['systemd-run', '--scope']) + if salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ): + cmd.extend(["systemd-run", "--scope"]) cmd.append(_yum()) cmd.extend(args) - return __salt__['cmd.run_all'](cmd, **params) + return __salt__["cmd.run_all"](cmd, **params) def _yum_pkginfo(output): - ''' + """ Parse yum/dnf output (which could contain irregular line breaks if package names are long) retrieving the name, version, etc., and return a list of pkginfo namedtuples. - ''' + """ cur = {} - keys = itertools.cycle(('name', 'version', 'repoid')) + keys = itertools.cycle(("name", "version", "repoid")) values = salt.utils.itertools.split(_strip_headers(output)) - osarch = __grains__['osarch'] + osarch = __grains__["osarch"] for (key, value) in zip(keys, values): - if key == 'name': + if key == "name": try: - cur['name'], cur['arch'] = value.rsplit('.', 1) + cur["name"], cur["arch"] = value.rsplit(".", 1) except ValueError: - cur['name'] = value - cur['arch'] = osarch - cur['name'] = salt.utils.pkg.rpm.resolve_name(cur['name'], - cur['arch'], - osarch) + cur["name"] = value + cur["arch"] = osarch + cur["name"] = salt.utils.pkg.rpm.resolve_name( + cur["name"], cur["arch"], osarch + ) else: - if key == 'version': + if key == "version": # Suppport packages with no 'Release' parameter - value = value.rstrip('-') - elif key == 'repoid': + value = value.rstrip("-") + elif key == "repoid": # Installed packages show a '@' at the beginning - value = value.lstrip('@') + value = value.lstrip("@") cur[key] = value - if key == 'repoid': + if key == "repoid": # We're done with this package, create the pkginfo namedtuple pkginfo = salt.utils.pkg.rpm.pkginfo(**cur) # Clear the dict for the next package @@ -208,49 +216,57 @@ def _yum_pkginfo(output): def _check_versionlock(): - ''' + """ Ensure that the appropriate versionlock plugin is present - ''' - if _yum() == 'dnf': - if int(__grains__.get('osmajorrelease')) >= 26: + """ + if _yum() == "dnf": + if ( + "fedora" in __grains__["os"].lower() + and int(__grains__.get("osrelease")) >= 26 + ) or ( + __grains__.get("os").lower() in ("redhat", "centos") + and int(__grains__.get("osmajorrelease")) >= 8 + ): if six.PY3: - vl_plugin = 'python3-dnf-plugin-versionlock' + vl_plugin = "python3-dnf-plugin-versionlock" else: - vl_plugin = 'python2-dnf-plugin-versionlock' + vl_plugin = "python2-dnf-plugin-versionlock" else: if six.PY3: - vl_plugin = 'python3-dnf-plugins-extras-versionlock' + vl_plugin = "python3-dnf-plugins-extras-versionlock" else: - vl_plugin = 'python-dnf-plugins-extras-versionlock' + vl_plugin = "python-dnf-plugins-extras-versionlock" else: - vl_plugin = 'yum-versionlock' \ - if __grains__.get('osmajorrelease') == '5' \ - else 'yum-plugin-versionlock' + vl_plugin = ( + "yum-versionlock" + if __grains__.get("osmajorrelease") == "5" + else "yum-plugin-versionlock" + ) if vl_plugin not in list_pkgs(): raise SaltInvocationError( - 'Cannot proceed, {0} is not installed.'.format(vl_plugin) + "Cannot proceed, {0} is not installed.".format(vl_plugin) ) def _get_options(**kwargs): - ''' + """ Returns a list of options to be used in the yum/dnf command, based on the kwargs passed. - ''' + """ # Get repo options from the kwargs - fromrepo = kwargs.pop('fromrepo', '') - repo = kwargs.pop('repo', '') - disablerepo = kwargs.pop('disablerepo', '') - enablerepo = kwargs.pop('enablerepo', '') - disableexcludes = kwargs.pop('disableexcludes', '') - branch = kwargs.pop('branch', '') - setopt = kwargs.pop('setopt', None) + fromrepo = kwargs.pop("fromrepo", "") + repo = kwargs.pop("repo", "") + disablerepo = kwargs.pop("disablerepo", "") + enablerepo = kwargs.pop("enablerepo", "") + disableexcludes = kwargs.pop("disableexcludes", "") + branch = kwargs.pop("branch", "") + setopt = kwargs.pop("setopt", None) if setopt is None: setopt = [] else: setopt = salt.utils.args.split_input(setopt) - get_extra_options = kwargs.pop('get_extra_options', False) + get_extra_options = kwargs.pop("get_extra_options", False) # Support old 'repo' argument if repo and not fromrepo: @@ -259,55 +275,51 @@ def _get_options(**kwargs): ret = [] if fromrepo: - log.info('Restricting to repo \'%s\'', fromrepo) - ret.extend(['--disablerepo=*', '--enablerepo={0}'.format(fromrepo)]) + log.info("Restricting to repo '%s'", fromrepo) + ret.extend(["--disablerepo=*", "--enablerepo={0}".format(fromrepo)]) else: if disablerepo: - targets = [disablerepo] \ - if not isinstance(disablerepo, list) \ - else disablerepo - log.info('Disabling repo(s): %s', ', '.join(targets)) - ret.extend( - ['--disablerepo={0}'.format(x) for x in targets] + targets = ( + [disablerepo] if not isinstance(disablerepo, list) else disablerepo ) + log.info("Disabling repo(s): %s", ", ".join(targets)) + ret.extend(["--disablerepo={0}".format(x) for x in targets]) if enablerepo: - targets = [enablerepo] \ - if not isinstance(enablerepo, list) \ - else enablerepo - log.info('Enabling repo(s): %s', ', '.join(targets)) - ret.extend(['--enablerepo={0}'.format(x) for x in targets]) + targets = [enablerepo] if not isinstance(enablerepo, list) else enablerepo + log.info("Enabling repo(s): %s", ", ".join(targets)) + ret.extend(["--enablerepo={0}".format(x) for x in targets]) if disableexcludes: - log.info('Disabling excludes for \'%s\'', disableexcludes) - ret.append('--disableexcludes={0}'.format(disableexcludes)) + log.info("Disabling excludes for '%s'", disableexcludes) + ret.append("--disableexcludes={0}".format(disableexcludes)) if branch: - log.info('Adding branch \'%s\'', branch) - ret.append('--branch={0}'.format(branch)) + log.info("Adding branch '%s'", branch) + ret.append("--branch={0}".format(branch)) for item in setopt: - ret.extend(['--setopt', six.text_type(item)]) + ret.extend(["--setopt", six.text_type(item)]) if get_extra_options: # sorting here to make order uniform, makes unit testing more reliable for key in sorted(kwargs): - if key.startswith('__'): + if key.startswith("__"): continue value = kwargs[key] if isinstance(value, six.string_types): - log.info('Found extra option --%s=%s', key, value) - ret.append('--{0}={1}'.format(key, value)) + log.info("Found extra option --%s=%s", key, value) + ret.append("--{0}={1}".format(key, value)) elif value is True: - log.info('Found extra option --%s', key) - ret.append('--{0}'.format(key)) + log.info("Found extra option --%s", key) + ret.append("--{0}".format(key)) if ret: - log.info('Adding extra options: %s', ret) + log.info("Adding extra options: %s", ret) return ret def _get_yum_config(): - ''' + """ Returns a dict representing the yum config options and values. We try to pull all of the yum config options into a standard dict object. @@ -320,10 +332,10 @@ def _get_yum_config(): If the yum library is not available, we try to read the yum.conf directly ourselves with a minimal set of "defaults". - ''' + """ # in case of any non-fatal failures, these defaults will be used conf = { - 'reposdir': ['/etc/yum/repos.d', '/etc/yum.repos.d'], + "reposdir": ["/etc/yum/repos.d", "/etc/yum.repos.d"], } if HAS_YUM: @@ -333,18 +345,16 @@ def _get_yum_config(): for name, value in six.iteritems(yb.conf): conf[name] = value except (AttributeError, yum.Errors.ConfigError) as exc: - raise CommandExecutionError( - 'Could not query yum config: {0}'.format(exc) - ) + raise CommandExecutionError("Could not query yum config: {0}".format(exc)) except yum.Errors.YumBaseError as yum_base_error: raise CommandExecutionError( - 'Error accessing yum or rpmdb: {0}'.format(yum_base_error) + "Error accessing yum or rpmdb: {0}".format(yum_base_error) ) else: # fall back to parsing the config ourselves # Look for the config the same order yum does fn = None - paths = ('/etc/yum/yum.conf', '/etc/yum.conf', '/etc/dnf/dnf.conf') + paths = ("/etc/yum/yum.conf", "/etc/yum.conf", "/etc/dnf/dnf.conf") for path in paths: if os.path.exists(path): fn = path @@ -352,39 +362,34 @@ def _get_yum_config(): if not fn: raise CommandExecutionError( - 'No suitable yum config file found in: {0}'.format(paths) + "No suitable yum config file found in: {0}".format(paths) ) cp = configparser.ConfigParser() try: cp.read(fn) except (IOError, OSError) as exc: - raise CommandExecutionError( - 'Unable to read from {0}: {1}'.format(fn, exc) - ) + raise CommandExecutionError("Unable to read from {0}: {1}".format(fn, exc)) - if cp.has_section('main'): - for opt in cp.options('main'): - if opt in ('reposdir', 'commands', 'excludes'): + if cp.has_section("main"): + for opt in cp.options("main"): + if opt in ("reposdir", "commands", "excludes"): # these options are expected to be lists - conf[opt] = [x.strip() - for x in cp.get('main', opt).split(',')] + conf[opt] = [x.strip() for x in cp.get("main", opt).split(",")] else: - conf[opt] = cp.get('main', opt) + conf[opt] = cp.get("main", opt) else: log.warning( - 'Could not find [main] section in %s, using internal ' - 'defaults', - fn + "Could not find [main] section in %s, using internal " "defaults", fn ) return conf def _get_yum_config_value(name): - ''' + """ Look for a specific config variable and return its value - ''' + """ conf = _get_yum_config() if name in conf.keys(): return conf.get(name) @@ -392,32 +397,32 @@ def _get_yum_config_value(name): def _normalize_basedir(basedir=None): - ''' + """ Takes a basedir argument as a string or a list. If the string or list is empty, then look up the default from the 'reposdir' option in the yum config. Returns a list of directories. - ''' + """ # if we are passed a string (for backward compatibility), convert to a list if isinstance(basedir, six.string_types): - basedir = [x.strip() for x in basedir.split(',')] + basedir = [x.strip() for x in basedir.split(",")] if basedir is None: basedir = [] # nothing specified, so use the reposdir option as the default if not basedir: - basedir = _get_yum_config_value('reposdir') + basedir = _get_yum_config_value("reposdir") if not isinstance(basedir, list) or not basedir: - raise SaltInvocationError('Could not determine any repo directories') + raise SaltInvocationError("Could not determine any repo directories") return basedir def normalize_name(name): - ''' + """ Strips the architecture from the specified package name, if necessary. Circumstances where this would be done include: @@ -429,21 +434,22 @@ def normalize_name(name): .. code-block:: bash salt '*' pkg.normalize_name zsh.x86_64 - ''' + """ try: arch = name.rsplit(PKG_ARCH_SEPARATOR, 1)[-1] - if arch not in salt.utils.pkg.rpm.ARCHES + ('noarch',): + if arch not in salt.utils.pkg.rpm.ARCHES + ("noarch",): return name except ValueError: return name - if arch in (__grains__['osarch'], 'noarch') \ - or salt.utils.pkg.rpm.check_32(arch, osarch=__grains__['osarch']): - return name[:-(len(arch) + 1)] + if arch in (__grains__["osarch"], "noarch") or salt.utils.pkg.rpm.check_32( + arch, osarch=__grains__["osarch"] + ): + return name[: -(len(arch) + 1)] return name def parse_arch(name): - ''' + """ Parse name and architecture from the specified package name. CLI Example: @@ -451,23 +457,20 @@ def parse_arch(name): .. code-block:: bash salt '*' pkg.parse_arch zsh.x86_64 - ''' + """ _name, _arch = None, None try: _name, _arch = name.rsplit(PKG_ARCH_SEPARATOR, 1) except ValueError: pass - if _arch not in salt.utils.pkg.rpm.ARCHES + ('noarch',): + if _arch not in salt.utils.pkg.rpm.ARCHES + ("noarch",): _name = name _arch = None - return { - 'name': _name, - 'arch': _arch - } + return {"name": _name, "arch": _arch} def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -489,10 +492,10 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version <package name> fromrepo=epel-testing salt '*' pkg.latest_version <package name> disableexcludes=main salt '*' pkg.latest_version <package1> <package2> <package3> ... - ''' - refresh = salt.utils.data.is_true(kwargs.pop('refresh', True)) + """ + refresh = salt.utils.data.is_true(kwargs.pop("refresh", True)) if len(names) == 0: - return '' + return "" options = _get_options(**kwargs) @@ -503,29 +506,29 @@ def latest_version(*names, **kwargs): cur_pkgs = list_pkgs(versions_as_list=True) # Get available versions for specified package(s) - cmd = ['--quiet'] + cmd = ["--quiet"] cmd.extend(options) - cmd.extend(['list', 'available']) + cmd.extend(["list", "available"]) cmd.extend(names) out = _call_yum(cmd, ignore_retcode=True) - if out['retcode'] != 0: - if out['stderr']: + if out["retcode"] != 0: + if out["stderr"]: # Check first if this is just a matter of the packages being # up-to-date. if not all([x in cur_pkgs for x in names]): log.error( - 'Problem encountered getting latest version for the ' - 'following package(s): %s. Stderr follows: \n%s', - ', '.join(names), - out['stderr'] + "Problem encountered getting latest version for the " + "following package(s): %s. Stderr follows: \n%s", + ", ".join(names), + out["stderr"], ) updates = [] else: # Sort by version number (highest to lowest) for loop below updates = sorted( - _yum_pkginfo(out['stdout']), + _yum_pkginfo(out["stdout"]), key=lambda pkginfo: _LooseVersion(pkginfo.version), - reverse=True + reverse=True, ) def _check_cur(pkg): @@ -534,10 +537,12 @@ def latest_version(*names, **kwargs): # If any installed version is greater than (or equal to) the # one found by yum/dnf list available, then it is not an # upgrade. - if salt.utils.versions.compare(ver1=installed_version, - oper='>=', - ver2=pkg.version, - cmp_func=version_cmp): + if salt.utils.versions.compare( + ver1=installed_version, + oper=">=", + ver2=pkg.version, + cmp_func=version_cmp, + ): return False # pkg.version is greater than all installed versions return True @@ -555,11 +560,11 @@ def latest_version(*names, **kwargs): # distinguish an update for a 32-bit version of a package from its # 64-bit counterpart. try: - arch = name.rsplit('.', 1)[-1] + arch = name.rsplit(".", 1)[-1] if arch not in salt.utils.pkg.rpm.ARCHES: - arch = __grains__['osarch'] + arch = __grains__["osarch"] except ValueError: - arch = __grains__['osarch'] + arch = __grains__["osarch"] # This loop will iterate over the updates derived by _yum_pkginfo() # above, which have been sorted descendingly by version number, @@ -571,14 +576,17 @@ def latest_version(*names, **kwargs): for pkg in (x for x in updates if x.name == name): # This if/or statement makes sure that we account for noarch # packages as well as arch-specific packages. - if pkg.arch == 'noarch' or pkg.arch == arch \ - or salt.utils.pkg.rpm.check_32(pkg.arch): + if ( + pkg.arch == "noarch" + or pkg.arch == arch + or salt.utils.pkg.rpm.check_32(pkg.arch) + ): if _check_cur(pkg): ret[name] = pkg.version # no need to check another match, if there was one break else: - ret[name] = '' + ret[name] = "" # Return a string if only one package name passed if len(names) == 1: @@ -587,11 +595,13 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def upgrade_available(name, **kwargs): - ''' + """ Check whether or not an upgrade is available for a given package CLI Example: @@ -599,12 +609,12 @@ def upgrade_available(name, **kwargs): .. code-block:: bash salt '*' pkg.upgrade_available <package name> - ''' - return latest_version(name, **kwargs) != '' + """ + return latest_version(name, **kwargs) != "" def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty string if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -615,12 +625,12 @@ def version(*names, **kwargs): salt '*' pkg.version <package name> salt '*' pkg.version <package1> <package2> <package3> ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) + """ + return __salt__["pkg_resource.version"](*names, **kwargs) def version_cmp(pkg1, pkg2, ignore_epoch=False): - ''' + """ .. versionadded:: 2015.5.4 Do a cmp-style comparison on two packages. Return -1 if pkg1 < pkg2, 0 if @@ -637,13 +647,13 @@ def version_cmp(pkg1, pkg2, ignore_epoch=False): .. code-block:: bash salt '*' pkg.version_cmp '0.2-001' '0.2.0.1-002' - ''' + """ - return __salt__['lowpkg.version_cmp'](pkg1, pkg2, ignore_epoch=ignore_epoch) + return __salt__["lowpkg.version_cmp"](pkg1, pkg2, ignore_epoch=ignore_epoch) def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed as a dict. By default, the dict contains versions as a comma separated string:: @@ -674,63 +684,64 @@ def list_pkgs(versions_as_list=False, **kwargs): salt '*' pkg.list_pkgs salt '*' pkg.list_pkgs attr=version,arch salt '*' pkg.list_pkgs attr='["version", "arch"]' - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - attr = kwargs.get('attr') + attr = kwargs.get("attr") if attr is not None: attr = salt.utils.args.split_input(attr) - contextkey = 'pkg.list_pkgs' + contextkey = "pkg.list_pkgs" if contextkey not in __context__: ret = {} - cmd = ['rpm', '-qa', '--queryformat', - salt.utils.pkg.rpm.QUERYFORMAT.replace('%{REPOID}', '(none)') + '\n'] - output = __salt__['cmd.run'](cmd, - python_shell=False, - output_loglevel='trace') + cmd = [ + "rpm", + "-qa", + "--queryformat", + salt.utils.pkg.rpm.QUERYFORMAT.replace("%{REPOID}", "(none)") + "\n", + ] + output = __salt__["cmd.run"](cmd, python_shell=False, output_loglevel="trace") for line in output.splitlines(): pkginfo = salt.utils.pkg.rpm.parse_pkginfo( - line, - osarch=__grains__['osarch'] + line, osarch=__grains__["osarch"] ) if pkginfo is not None: # see rpm version string rules available at https://goo.gl/UGKPNd pkgver = pkginfo.version epoch = None release = None - if ':' in pkgver: + if ":" in pkgver: epoch, pkgver = pkgver.split(":", 1) - if '-' in pkgver: + if "-" in pkgver: pkgver, release = pkgver.split("-", 1) all_attr = { - 'epoch': epoch, - 'version': pkgver, - 'release': release, - 'arch': pkginfo.arch, - 'install_date': pkginfo.install_date, - 'install_date_time_t': pkginfo.install_date_time_t + "epoch": epoch, + "version": pkgver, + "release": release, + "arch": pkginfo.arch, + "install_date": pkginfo.install_date, + "install_date_time_t": pkginfo.install_date_time_t, } - __salt__['pkg_resource.add_pkg'](ret, pkginfo.name, all_attr) + __salt__["pkg_resource.add_pkg"](ret, pkginfo.name, all_attr) for pkgname in ret: - ret[pkgname] = sorted(ret[pkgname], key=lambda d: d['version']) + ret[pkgname] = sorted(ret[pkgname], key=lambda d: d["version"]) __context__[contextkey] = ret - return __salt__['pkg_resource.format_pkg_list']( - __context__[contextkey], - versions_as_list, - attr) + return __salt__["pkg_resource.format_pkg_list"]( + __context__[contextkey], versions_as_list, attr + ) def list_repo_pkgs(*args, **kwargs): - ''' + """ .. versionadded:: 2014.1.0 .. versionchanged:: 2014.7.0 All available versions of each package are now returned. This required @@ -848,54 +859,57 @@ def list_repo_pkgs(*args, **kwargs): salt '*' pkg.list_repo_pkgs foo bar baz salt '*' pkg.list_repo_pkgs 'samba4*' fromrepo=base,updates salt '*' pkg.list_repo_pkgs 'python2-*' byrepo=True - ''' - byrepo = kwargs.pop('byrepo', False) - cacheonly = kwargs.pop('cacheonly', False) - fromrepo = kwargs.pop('fromrepo', '') or '' - disablerepo = kwargs.pop('disablerepo', '') or '' - enablerepo = kwargs.pop('enablerepo', '') or '' + """ + byrepo = kwargs.pop("byrepo", False) + cacheonly = kwargs.pop("cacheonly", False) + fromrepo = kwargs.pop("fromrepo", "") or "" + disablerepo = kwargs.pop("disablerepo", "") or "" + enablerepo = kwargs.pop("enablerepo", "") or "" repo_arg = _get_options(fromrepo=fromrepo, **kwargs) if fromrepo and not isinstance(fromrepo, list): try: - fromrepo = [x.strip() for x in fromrepo.split(',')] + fromrepo = [x.strip() for x in fromrepo.split(",")] except AttributeError: - fromrepo = [x.strip() for x in six.text_type(fromrepo).split(',')] + fromrepo = [x.strip() for x in six.text_type(fromrepo).split(",")] if disablerepo and not isinstance(disablerepo, list): try: - disablerepo = [x.strip() for x in disablerepo.split(',') - if x != '*'] + disablerepo = [x.strip() for x in disablerepo.split(",") if x != "*"] except AttributeError: - disablerepo = [x.strip() for x in six.text_type(disablerepo).split(',') - if x != '*'] + disablerepo = [ + x.strip() for x in six.text_type(disablerepo).split(",") if x != "*" + ] if enablerepo and not isinstance(enablerepo, list): try: - enablerepo = [x.strip() for x in enablerepo.split(',') - if x != '*'] + enablerepo = [x.strip() for x in enablerepo.split(",") if x != "*"] except AttributeError: - enablerepo = [x.strip() for x in six.text_type(enablerepo).split(',') - if x != '*'] + enablerepo = [ + x.strip() for x in six.text_type(enablerepo).split(",") if x != "*" + ] if fromrepo: repos = fromrepo else: repos = [ - repo_name for repo_name, repo_info in six.iteritems(list_repos()) + repo_name + for repo_name, repo_info in six.iteritems(list_repos()) if repo_name in enablerepo - or (repo_name not in disablerepo - and six.text_type(repo_info.get('enabled', '1')) == '1') + or ( + repo_name not in disablerepo + and six.text_type(repo_info.get("enabled", "1")) == "1" + ) ] ret = {} def _check_args(args, name): - ''' + """ Do glob matching on args and return True if a match was found. Otherwise, return False - ''' + """ for arg in args: if fnmatch.fnmatch(name, arg): return True @@ -903,61 +917,62 @@ def list_repo_pkgs(*args, **kwargs): def _parse_output(output, strict=False): for pkg in _yum_pkginfo(output): - if strict and (pkg.repoid not in repos - or not _check_args(args, pkg.name)): + if strict and (pkg.repoid not in repos or not _check_args(args, pkg.name)): continue repo_dict = ret.setdefault(pkg.repoid, {}) version_list = repo_dict.setdefault(pkg.name, set()) version_list.add(pkg.version) - yum_version = None if _yum() != 'yum' else _LooseVersion( - __salt__['cmd.run']( - ['yum', '--version'], - python_shell=False - ).splitlines()[0].strip() - ) + yum_version = ( + None + if _yum() != "yum" + else _LooseVersion( + __salt__["cmd.run"](["yum", "--version"], python_shell=False) + .splitlines()[0] + .strip() + ) + ) # Really old version of yum; does not even have --showduplicates option - if yum_version and yum_version < _LooseVersion('3.2.13'): - cmd_prefix = ['--quiet'] + if yum_version and yum_version < _LooseVersion("3.2.13"): + cmd_prefix = ["--quiet"] if cacheonly: - cmd_prefix.append('-C') - cmd_prefix.append('list') - for pkg_src in ('installed', 'available'): + cmd_prefix.append("-C") + cmd_prefix.append("list") + for pkg_src in ("installed", "available"): # Check installed packages first out = _call_yum(cmd_prefix + [pkg_src], ignore_retcode=True) - if out['retcode'] == 0: - _parse_output(out['stdout'], strict=True) + if out["retcode"] == 0: + _parse_output(out["stdout"], strict=True) # The --showduplicates option is added in 3.2.13, but the # repository-packages subcommand is only in 3.4.3 and newer - elif yum_version and yum_version < _LooseVersion('3.4.3'): - cmd_prefix = ['--quiet', '--showduplicates'] + elif yum_version and yum_version < _LooseVersion("3.4.3"): + cmd_prefix = ["--quiet", "--showduplicates"] if cacheonly: - cmd_prefix.append('-C') - cmd_prefix.append('list') - for pkg_src in ('installed', 'available'): + cmd_prefix.append("-C") + cmd_prefix.append("list") + for pkg_src in ("installed", "available"): # Check installed packages first out = _call_yum(cmd_prefix + [pkg_src], ignore_retcode=True) - if out['retcode'] == 0: - _parse_output(out['stdout'], strict=True) + if out["retcode"] == 0: + _parse_output(out["stdout"], strict=True) else: for repo in repos: - cmd = ['--quiet', '--showduplicates', 'repository-packages', repo, 'list'] + cmd = ["--quiet", "--showduplicates", "repository-packages", repo, "list"] if cacheonly: - cmd.append('-C') + cmd.append("-C") # Can't concatenate because args is a tuple, using list.extend() cmd.extend(args) out = _call_yum(cmd, ignore_retcode=True) - if out['retcode'] != 0 and 'Error:' in out['stdout']: + if out["retcode"] != 0 and "Error:" in out["stdout"]: continue - _parse_output(out['stdout']) + _parse_output(out["stdout"]) if byrepo: for reponame in ret: # Sort versions newest to oldest for pkgname in ret[reponame]: sorted_versions = sorted( - [_LooseVersion(x) for x in ret[reponame][pkgname]], - reverse=True + [_LooseVersion(x) for x in ret[reponame][pkgname]], reverse=True ) ret[reponame][pkgname] = [x.vstring for x in sorted_versions] return ret @@ -968,15 +983,14 @@ def list_repo_pkgs(*args, **kwargs): byrepo_ret.setdefault(pkgname, []).extend(ret[reponame][pkgname]) for pkgname in byrepo_ret: sorted_versions = sorted( - [_LooseVersion(x) for x in byrepo_ret[pkgname]], - reverse=True + [_LooseVersion(x) for x in byrepo_ret[pkgname]], reverse=True ) byrepo_ret[pkgname] = [x.vstring for x in sorted_versions] return byrepo_ret def list_upgrades(refresh=True, **kwargs): - ''' + """ Check whether or not an upgrade is available for all packages The ``fromrepo``, ``enablerepo``, and ``disablerepo`` arguments are @@ -991,28 +1005,28 @@ def list_upgrades(refresh=True, **kwargs): .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ options = _get_options(**kwargs) if salt.utils.data.is_true(refresh): refresh_db(check_update=False, **kwargs) - cmd = ['--quiet'] + cmd = ["--quiet"] cmd.extend(options) - cmd.extend(['list', 'upgrades' if _yum() == 'dnf' else 'updates']) + cmd.extend(["list", "upgrades" if _yum() == "dnf" else "updates"]) out = _call_yum(cmd, ignore_retcode=True) - if out['retcode'] != 0 and 'Error:' in out: + if out["retcode"] != 0 and "Error:" in out: return {} - return dict([(x.name, x.version) for x in _yum_pkginfo(out['stdout'])]) + return dict([(x.name, x.version) for x in _yum_pkginfo(out["stdout"])]) # Preserve expected CLI usage (yum list updates) -list_updates = salt.utils.functools.alias_function(list_upgrades, 'list_updates') +list_updates = salt.utils.functools.alias_function(list_upgrades, "list_updates") def list_downloaded(): - ''' + """ .. versionadded:: 2017.7.0 List prefetched packages downloaded by Yum in the local disk. @@ -1022,26 +1036,28 @@ def list_downloaded(): .. code-block:: bash salt '*' pkg.list_downloaded - ''' - CACHE_DIR = os.path.join('/var/cache/', _yum()) + """ + CACHE_DIR = os.path.join("/var/cache/", _yum()) ret = {} for root, dirnames, filenames in salt.utils.path.os_walk(CACHE_DIR): - for filename in fnmatch.filter(filenames, '*.rpm'): + for filename in fnmatch.filter(filenames, "*.rpm"): package_path = os.path.join(root, filename) - pkg_info = __salt__['lowpkg.bin_pkg_info'](package_path) + pkg_info = __salt__["lowpkg.bin_pkg_info"](package_path) pkg_timestamp = int(os.path.getctime(package_path)) - ret.setdefault(pkg_info['name'], {})[pkg_info['version']] = { - 'path': package_path, - 'size': os.path.getsize(package_path), - 'creation_date_time_t': pkg_timestamp, - 'creation_date_time': datetime.datetime.fromtimestamp(pkg_timestamp).isoformat(), + ret.setdefault(pkg_info["name"], {})[pkg_info["version"]] = { + "path": package_path, + "size": os.path.getsize(package_path), + "creation_date_time_t": pkg_timestamp, + "creation_date_time": datetime.datetime.fromtimestamp( + pkg_timestamp + ).isoformat(), } return ret def info_installed(*names, **kwargs): - ''' + """ .. versionadded:: 2015.8.1 Return the information of the named package(s), installed on the system. @@ -1056,17 +1072,17 @@ def info_installed(*names, **kwargs): salt '*' pkg.info_installed <package1> salt '*' pkg.info_installed <package1> <package2> <package3> ... salt '*' pkg.info_installed <package1> <package2> <package3> all_versions=True - ''' - all_versions = kwargs.get('all_versions', False) + """ + all_versions = kwargs.get("all_versions", False) ret = dict() - for pkg_name, pkgs_nfo in __salt__['lowpkg.info'](*names, **kwargs).items(): + for pkg_name, pkgs_nfo in __salt__["lowpkg.info"](*names, **kwargs).items(): pkg_nfo = pkgs_nfo if all_versions else [pkgs_nfo] for _nfo in pkg_nfo: t_nfo = dict() # Translate dpkg-specific keys to a common structure for key, value in _nfo.items(): - if key == 'source_rpm': - t_nfo['source'] = value + if key == "source_rpm": + t_nfo["source"] = value else: t_nfo[key] = value if not all_versions: @@ -1077,7 +1093,7 @@ def info_installed(*names, **kwargs): def refresh_db(**kwargs): - ''' + """ Check the yum repos for updated packages Returns: @@ -1117,7 +1133,7 @@ def refresh_db(**kwargs): .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) retcodes = { @@ -1127,28 +1143,30 @@ def refresh_db(**kwargs): } ret = True - check_update_ = kwargs.pop('check_update', True) + check_update_ = kwargs.pop("check_update", True) options = _get_options(**kwargs) - clean_cmd = ['--quiet', '--assumeyes', 'clean', 'expire-cache'] + clean_cmd = ["--quiet", "--assumeyes", "clean", "expire-cache"] clean_cmd.extend(options) _call_yum(clean_cmd, ignore_retcode=True) if check_update_: - update_cmd = ['--quiet', '--assumeyes', 'check-update'] - if (__grains__.get('os_family') == 'RedHat' - and __grains__.get('osmajorrelease') == 7): + update_cmd = ["--quiet", "--assumeyes", "check-update"] + if ( + __grains__.get("os_family") == "RedHat" + and __grains__.get("osmajorrelease") == 7 + ): # This feature is disabled because it is not used by Salt and adds a # lot of extra time to the command with large repos like EPEL - update_cmd.append('--setopt=autocheck_running_kernel=false') + update_cmd.append("--setopt=autocheck_running_kernel=false") update_cmd.extend(options) - ret = retcodes.get(_call_yum(update_cmd, ignore_retcode=True)['retcode'], False) + ret = retcodes.get(_call_yum(update_cmd, ignore_retcode=True)["retcode"], False) return ret def clean_metadata(**kwargs): - ''' + """ .. versionadded:: 2014.1.0 Cleans local yum metadata. Functionally identical to :mod:`refresh_db() @@ -1159,7 +1177,7 @@ def clean_metadata(**kwargs): .. code-block:: bash salt '*' pkg.clean_metadata - ''' + """ return refresh_db(**kwargs) @@ -1178,19 +1196,21 @@ class AvailablePackages(salt.utils.lazy.LazyDict): self.loaded = True -def install(name=None, - refresh=False, - skip_verify=False, - pkgs=None, - sources=None, - downloadonly=False, - reinstall=False, - normalize=True, - update_holds=False, - saltenv='base', - ignore_epoch=False, - **kwargs): - ''' +def install( + name=None, + refresh=False, + skip_verify=False, + pkgs=None, + sources=None, + downloadonly=False, + reinstall=False, + normalize=True, + update_holds=False, + saltenv="base", + ignore_epoch=False, + **kwargs +): + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -1380,7 +1400,7 @@ def install(name=None, 'new': { 'version': '<new-version>', 'arch': '<new-arch>'}}} - ''' + """ options = _get_options(**kwargs) if salt.utils.data.is_true(refresh): @@ -1388,7 +1408,7 @@ def install(name=None, reinstall = salt.utils.data.is_true(reinstall) try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets']( + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( name, pkgs, sources, saltenv=saltenv, normalize=normalize, **kwargs ) except MinionError as exc: @@ -1397,13 +1417,19 @@ def install(name=None, if pkg_params is None or len(pkg_params) == 0: return {} - version_num = kwargs.get('version') + version_num = kwargs.get("version") - diff_attr = kwargs.get('diff_attr') - old = list_pkgs(versions_as_list=False, attr=diff_attr) if not downloadonly else list_downloaded() + diff_attr = kwargs.get("diff_attr") + old = ( + list_pkgs(versions_as_list=False, attr=diff_attr) + if not downloadonly + else list_downloaded() + ) # Use of __context__ means no duplicate work here, just accessing # information already in __context__ from the previous call to list_pkgs() - old_as_list = list_pkgs(versions_as_list=True) if not downloadonly else list_downloaded() + old_as_list = ( + list_pkgs(versions_as_list=True) if not downloadonly else list_downloaded() + ) to_install = [] to_downgrade = [] @@ -1424,23 +1450,22 @@ def install(name=None, # # The reason that we need both items is to be able to modify the installed # version of held packages. - if pkg_type == 'repository': + if pkg_type == "repository": has_wildcards = [] has_comparison = [] for pkgname, pkgver in six.iteritems(pkg_params): try: - if '*' in pkgver: + if "*" in pkgver: has_wildcards.append(pkgname) - elif pkgver.startswith('<') or pkgver.startswith('>'): + elif pkgver.startswith("<") or pkgver.startswith(">"): has_comparison.append(pkgname) except (TypeError, ValueError): continue _available = AvailablePackages( - *has_wildcards + has_comparison, - byrepo=False, - **kwargs) + *has_wildcards + has_comparison, byrepo=False, **kwargs + ) pkg_params_items = six.iteritems(pkg_params) - elif pkg_type == 'advisory': + elif pkg_type == "advisory": pkg_params_items = [] cur_patches = list_patches() for advisory_id in pkg_params: @@ -1453,27 +1478,28 @@ def install(name=None, else: pkg_params_items = [] for pkg_source in pkg_params: - if 'lowpkg.bin_pkg_info' in __salt__: - rpm_info = __salt__['lowpkg.bin_pkg_info'](pkg_source) + if "lowpkg.bin_pkg_info" in __salt__: + rpm_info = __salt__["lowpkg.bin_pkg_info"](pkg_source) else: rpm_info = None if rpm_info is None: log.error( - 'pkg.install: Unable to get rpm information for %s. ' - 'Version comparisons will be unavailable, and return ' - 'data may be inaccurate if reinstall=True.', pkg_source + "pkg.install: Unable to get rpm information for %s. " + "Version comparisons will be unavailable, and return " + "data may be inaccurate if reinstall=True.", + pkg_source, ) pkg_params_items.append([pkg_source]) else: pkg_params_items.append( - [rpm_info['name'], pkg_source, rpm_info['version']] + [rpm_info["name"], pkg_source, rpm_info["version"]] ) errors = [] for pkg_item_list in pkg_params_items: - if pkg_type == 'repository': + if pkg_type == "repository": pkgname, version_num = pkg_item_list - elif pkg_type == 'advisory': + elif pkg_type == "advisory": pkgname = pkg_item_list version_num = None else: @@ -1485,12 +1511,12 @@ def install(name=None, version_num = None if version_num is None: - if pkg_type == 'repository': + if pkg_type == "repository": if reinstall and pkgname in old: to_reinstall.append((pkgname, pkgname)) else: to_install.append((pkgname, pkgname)) - elif pkg_type == 'advisory': + elif pkg_type == "advisory": to_install.append((pkgname, pkgname)) else: to_install.append((pkgname, pkgpath)) @@ -1499,10 +1525,10 @@ def install(name=None, # and version_num is not None, then we can assume that pkgname is # not None, since the only way version_num is not None is if RPM # metadata parsing was successful. - if pkg_type == 'repository': + if pkg_type == "repository": # yum/dnf does not support comparison operators. If the version # starts with an equals sign, ignore it. - version_num = version_num.lstrip('=') + version_num = version_num.lstrip("=") if pkgname in has_comparison: candidates = _available.get(pkgname, []) target = salt.utils.pkg.match_version( @@ -1513,55 +1539,59 @@ def install(name=None, ) if target is None: errors.append( - 'No version matching \'{0}{1}\' could be found ' - '(available: {2})'.format( + "No version matching '{0}{1}' could be found " + "(available: {2})".format( pkgname, version_num, - ', '.join(candidates) if candidates else None + ", ".join(candidates) if candidates else None, ) ) continue else: version_num = target - if _yum() == 'yum': + if _yum() == "yum": # yum install does not support epoch without the arch, and # we won't know what the arch will be when it's not # provided. It could either be the OS architecture, or # 'noarch', and we don't make that distinction in the # pkg.list_pkgs return data. if ignore_epoch is True: - version_num = version_num.split(':', 1)[-1] - arch = '' + version_num = version_num.split(":", 1)[-1] + arch = "" try: - namepart, archpart = pkgname.rsplit('.', 1) + namepart, archpart = pkgname.rsplit(".", 1) except ValueError: pass else: if archpart in salt.utils.pkg.rpm.ARCHES: - arch = '.' + archpart + arch = "." + archpart pkgname = namepart - if '*' in version_num: + if "*" in version_num: # Resolve wildcard matches candidates = _available.get(pkgname, []) - match = salt.utils.itertools.fnmatch_multiple(candidates, version_num) + match = salt.utils.itertools.fnmatch_multiple( + candidates, version_num + ) if match is not None: version_num = match else: errors.append( - 'No version matching \'{0}\' found for package ' - '\'{1}\' (available: {2})'.format( + "No version matching '{0}' found for package " + "'{1}' (available: {2})".format( version_num, pkgname, - ', '.join(candidates) if candidates else 'none' + ", ".join(candidates) if candidates else "none", ) ) continue if ignore_epoch is True: - pkgstr = '{0}-{1}{2}'.format(pkgname, version_num, arch) + pkgstr = "{0}-{1}{2}".format(pkgname, version_num, arch) else: - pkgstr = '{0}-{1}{2}'.format(pkgname, version_num.split(':', 1)[-1], arch) + pkgstr = "{0}-{1}{2}".format( + pkgname, version_num.split(":", 1)[-1], arch + ) else: pkgstr = pkgpath @@ -1571,11 +1601,13 @@ def install(name=None, cver = old_as_list.get(pkgname, []) if reinstall and cver: for ver in cver: - if salt.utils.versions.compare(ver1=version_num, - oper='==', - ver2=ver, - cmp_func=version_cmp, - ignore_epoch=ignore_epoch): + if salt.utils.versions.compare( + ver1=version_num, + oper="==", + ver2=ver, + cmp_func=version_cmp, + ignore_epoch=ignore_epoch, + ): # This version is already installed, so we need to # reinstall. to_reinstall.append((pkgname, pkgstr)) @@ -1585,16 +1617,18 @@ def install(name=None, to_install.append((pkgname, pkgstr)) else: for ver in cver: - if salt.utils.versions.compare(ver1=version_num, - oper='>=', - ver2=ver, - cmp_func=version_cmp, - ignore_epoch=ignore_epoch): + if salt.utils.versions.compare( + ver1=version_num, + oper=">=", + ver2=ver, + cmp_func=version_cmp, + ignore_epoch=ignore_epoch, + ): to_install.append((pkgname, pkgstr)) break else: if pkgname is not None: - if re.match('^kernel(|-devel)$', pkgname): + if re.match("^kernel(|-devel)$", pkgname): # kernel and kernel-devel support multiple # installs as their paths do not conflict. # Performing a yum/dnf downgrade will be a @@ -1615,32 +1649,31 @@ def install(name=None, to_downgrade.append((pkgname, pkgstr)) def _add_common_args(cmd): - ''' + """ DRY function to add args common to all yum/dnf commands - ''' + """ cmd.extend(options) if skip_verify: - cmd.append('--nogpgcheck') + cmd.append("--nogpgcheck") if downloadonly: - cmd.append('--downloadonly') + cmd.append("--downloadonly") try: holds = list_holds(full=False) except SaltInvocationError: holds = [] log.debug( - 'Failed to get holds, versionlock plugin is probably not ' - 'installed' + "Failed to get holds, versionlock plugin is probably not " "installed" ) unhold_prevented = [] @contextlib.contextmanager def _temporarily_unhold(pkgs, targets): - ''' + """ Temporarily unhold packages that need to be updated. Add any successfully-removed ones (and any packages not in the list of current holds) to the list of targets. - ''' + """ to_unhold = {} for pkgname, pkgstr in pkgs: if pkgname in holds: @@ -1654,14 +1687,13 @@ def install(name=None, if not to_unhold: yield else: - log.debug('Unholding packages: %s', ', '.join(to_unhold)) + log.debug("Unholding packages: %s", ", ".join(to_unhold)) try: # Using list() here for python3 compatibility, dict.keys() no # longer returns a list in python3. unhold_names = list(to_unhold.keys()) - for unheld_pkg, outcome in \ - six.iteritems(unhold(pkgs=unhold_names)): - if outcome['result']: + for unheld_pkg, outcome in six.iteritems(unhold(pkgs=unhold_names)): + if outcome["result"]: # Package was successfully unheld, add to targets targets.append(to_unhold[unheld_pkg]) else: @@ -1670,8 +1702,9 @@ def install(name=None, yield except Exception as exc: # pylint: disable=broad-except errors.append( - 'Error encountered unholding packages {0}: {1}' - .format(', '.join(to_unhold), exc) + "Error encountered unholding packages {0}: {1}".format( + ", ".join(to_unhold), exc + ) ) finally: hold(pkgs=unhold_names) @@ -1679,79 +1712,86 @@ def install(name=None, targets = [] with _temporarily_unhold(to_install, targets): if targets: - if pkg_type == 'advisory': + if pkg_type == "advisory": targets = ["--advisory={0}".format(t) for t in targets] - cmd = ['-y'] - if _yum() == 'dnf': - cmd.extend(['--best', '--allowerasing']) + cmd = ["-y"] + if _yum() == "dnf": + cmd.extend(["--best", "--allowerasing"]) _add_common_args(cmd) - cmd.append('install' if pkg_type != 'advisory' else 'update') + cmd.append("install" if pkg_type != "advisory" else "update") cmd.extend(targets) out = _call_yum(cmd, ignore_retcode=False, redirect_stderr=True) - if out['retcode'] != 0: - errors.append(out['stdout']) + if out["retcode"] != 0: + errors.append(out["stdout"]) targets = [] with _temporarily_unhold(to_downgrade, targets): if targets: - cmd = ['-y'] + cmd = ["-y"] _add_common_args(cmd) - cmd.append('downgrade') + cmd.append("downgrade") cmd.extend(targets) out = _call_yum(cmd) - if out['retcode'] != 0: - errors.append(out['stdout']) + if out["retcode"] != 0: + errors.append(out["stdout"]) targets = [] with _temporarily_unhold(to_reinstall, targets): if targets: - cmd = ['-y'] + cmd = ["-y"] _add_common_args(cmd) - cmd.append('reinstall') + cmd.append("reinstall") cmd.extend(targets) out = _call_yum(cmd) - if out['retcode'] != 0: - errors.append(out['stdout']) + if out["retcode"] != 0: + errors.append(out["stdout"]) - __context__.pop('pkg.list_pkgs', None) - new = list_pkgs(versions_as_list=False, attr=diff_attr) if not downloadonly else list_downloaded() + __context__.pop("pkg.list_pkgs", None) + new = ( + list_pkgs(versions_as_list=False, attr=diff_attr) + if not downloadonly + else list_downloaded() + ) ret = salt.utils.data.compare_dicts(old, new) for pkgname, _ in to_reinstall: if pkgname not in ret or pkgname in old: - ret.update({pkgname: {'old': old.get(pkgname, ''), - 'new': new.get(pkgname, '')}}) + ret.update( + {pkgname: {"old": old.get(pkgname, ""), "new": new.get(pkgname, "")}} + ) if unhold_prevented: errors.append( - 'The following package(s) could not be updated because they are ' - 'being held: {0}. Set \'update_holds\' to True to temporarily ' - 'unhold these packages so that they can be updated.'.format( - ', '.join(unhold_prevented) + "The following package(s) could not be updated because they are " + "being held: {0}. Set 'update_holds' to True to temporarily " + "unhold these packages so that they can be updated.".format( + ", ".join(unhold_prevented) ) ) if errors: raise CommandExecutionError( - 'Error occurred installing{0} package(s)'.format( - '/reinstalling' if to_reinstall else '' + "Error occurred installing{0} package(s)".format( + "/reinstalling" if to_reinstall else "" ), - info={'errors': errors, 'changes': ret} + info={"errors": errors, "changes": ret}, ) return ret -def upgrade(name=None, - pkgs=None, - refresh=True, - skip_verify=False, - normalize=True, - minimal=False, - obsoletes=True, - **kwargs): - ''' +def upgrade( + name=None, + pkgs=None, + refresh=True, + skip_verify=False, + normalize=True, + minimal=False, + obsoletes=True, + **kwargs +): + """ Run a full system upgrade (a ``yum upgrade`` or ``dnf upgrade``), or upgrade specified packages. If the packages aren't installed, they will not be installed. @@ -1891,7 +1931,7 @@ def upgrade(name=None, .. code-block:: bash salt '*' pkg.upgrade security=True exclude='kernel*' - ''' + """ options = _get_options(get_extra_options=True, **kwargs) if salt.utils.data.is_true(refresh): @@ -1902,12 +1942,9 @@ def upgrade(name=None, targets = [] if name or pkgs: try: - pkg_params = __salt__['pkg_resource.parse_targets']( - name=name, - pkgs=pkgs, - sources=None, - normalize=normalize, - **kwargs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"]( + name=name, pkgs=pkgs, sources=None, normalize=normalize, **kwargs + )[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -1916,45 +1953,47 @@ def upgrade(name=None, # dictionary's keys. targets.extend(pkg_params) - cmd = ['--quiet', '-y'] + cmd = ["--quiet", "-y"] cmd.extend(options) if skip_verify: - cmd.append('--nogpgcheck') + cmd.append("--nogpgcheck") if obsoletes: - cmd.append('upgrade' if not minimal else 'upgrade-minimal') + cmd.append("upgrade" if not minimal else "upgrade-minimal") else: # do not force the removal of obsolete packages - if _yum() == 'dnf': + if _yum() == "dnf": # for dnf we can just disable obsoletes - cmd.append('--obsoletes=False') - cmd.append('upgrade' if not minimal else 'upgrade-minimal') + cmd.append("--obsoletes=False") + cmd.append("upgrade" if not minimal else "upgrade-minimal") else: # for yum we have to use update instead of upgrade - cmd.append('update' if not minimal else 'update-minimal') + cmd.append("update" if not minimal else "update-minimal") cmd.extend(targets) result = _call_yum(cmd) - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) - if result['retcode'] != 0: + if result["retcode"] != 0: raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) return ret -def update(name=None, - pkgs=None, - refresh=True, - skip_verify=False, - normalize=True, - minimal=False, - obsoletes=False, - **kwargs): - ''' +def update( + name=None, + pkgs=None, + refresh=True, + skip_verify=False, + normalize=True, + minimal=False, + obsoletes=False, + **kwargs +): + """ .. versionadded:: 2019.2.0 Calls :py:func:`pkg.upgrade <salt.modules.yumpkg.upgrade>` with @@ -1965,12 +2004,14 @@ def update(name=None, .. code-block:: bash salt '*' pkg.update - ''' - return upgrade(name, pkgs, refresh, skip_verify, normalize, minimal, obsoletes, **kwargs) + """ + return upgrade( + name, pkgs, refresh, skip_verify, normalize, minimal, obsoletes, **kwargs + ) def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -2009,9 +2050,9 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 salt '*' pkg.remove <package name> salt '*' pkg.remove <package1>,<package2>,<package3> salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -2022,42 +2063,42 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 # old[target] contains a comma-separated list of installed versions if target in old and not pkg_params[target]: targets.append(target) - elif target in old and pkg_params[target] in old[target].split(','): - arch = '' + elif target in old and pkg_params[target] in old[target].split(","): + arch = "" pkgname = target try: - namepart, archpart = target.rsplit('.', 1) + namepart, archpart = target.rsplit(".", 1) except ValueError: pass else: if archpart in salt.utils.pkg.rpm.ARCHES: - arch = '.' + archpart + arch = "." + archpart pkgname = namepart - targets.append('{0}-{1}{2}'.format(pkgname, pkg_params[target], arch)) + targets.append("{0}-{1}{2}".format(pkgname, pkg_params[target], arch)) if not targets: return {} - out = _call_yum(['-y', 'remove'] + targets) - if out['retcode'] != 0 and out['stderr']: - errors = [out['stderr']] + out = _call_yum(["-y", "remove"] + targets) + if out["retcode"] != 0 and out["stderr"]: + errors = [out["stderr"]] else: errors = [] - __context__.pop('pkg.list_pkgs', None) + __context__.pop("pkg.list_pkgs", None) new = list_pkgs() ret = salt.utils.data.compare_dicts(old, new) if errors: raise CommandExecutionError( - 'Error occurred removing package(s)', - info={'errors': errors, 'changes': ret} + "Error occurred removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def purge(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -2097,12 +2138,14 @@ def purge(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 salt '*' pkg.purge <package name> salt '*' pkg.purge <package1>,<package2>,<package3> salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' + """ return remove(name=name, pkgs=pkgs) -def hold(name=None, pkgs=None, sources=None, normalize=True, **kwargs): # pylint: disable=W0613 - ''' +def hold( + name=None, pkgs=None, sources=None, normalize=True, **kwargs +): # pylint: disable=W0613 + """ .. versionadded:: 2014.7.0 Version-lock packages @@ -2132,17 +2175,13 @@ def hold(name=None, pkgs=None, sources=None, normalize=True, **kwargs): # pylin salt '*' pkg.hold <package name> salt '*' pkg.hold pkgs='["foo", "bar"]' - ''' + """ _check_versionlock() if not name and not pkgs and not sources: - raise SaltInvocationError( - 'One of name, pkgs, or sources must be specified.' - ) + raise SaltInvocationError("One of name, pkgs, or sources must be specified.") if pkgs and sources: - raise SaltInvocationError( - 'Only one of pkgs or sources can be specified.' - ) + raise SaltInvocationError("Only one of pkgs or sources can be specified.") targets = [] if pkgs: @@ -2159,36 +2198,35 @@ def hold(name=None, pkgs=None, sources=None, normalize=True, **kwargs): # pylin if isinstance(target, dict): target = next(six.iterkeys(target)) - ret[target] = {'name': target, - 'changes': {}, - 'result': False, - 'comment': ''} + ret[target] = {"name": target, "changes": {}, "result": False, "comment": ""} if target not in current_locks: - if 'test' in __opts__ and __opts__['test']: + if "test" in __opts__ and __opts__["test"]: ret[target].update(result=None) - ret[target]['comment'] = ('Package {0} is set to be held.' - .format(target)) + ret[target]["comment"] = "Package {0} is set to be held.".format(target) else: - out = _call_yum(['versionlock', target]) - if out['retcode'] == 0: + out = _call_yum(["versionlock", target]) + if out["retcode"] == 0: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is now being held.' - .format(target)) - ret[target]['changes']['new'] = 'hold' - ret[target]['changes']['old'] = '' + ret[target]["comment"] = "Package {0} is now being held.".format( + target + ) + ret[target]["changes"]["new"] = "hold" + ret[target]["changes"]["old"] = "" else: - ret[target]['comment'] = ('Package {0} was unable to be held.' - .format(target)) + ret[target][ + "comment" + ] = "Package {0} was unable to be held.".format(target) else: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is already set to be held.' - .format(target)) + ret[target]["comment"] = "Package {0} is already set to be held.".format( + target + ) return ret def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W0613 - ''' + """ .. versionadded:: 2014.7.0 Remove version locks @@ -2218,17 +2256,13 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06 salt '*' pkg.unhold <package name> salt '*' pkg.unhold pkgs='["foo", "bar"]' - ''' + """ _check_versionlock() if not name and not pkgs and not sources: - raise SaltInvocationError( - 'One of name, pkgs, or sources must be specified.' - ) + raise SaltInvocationError("One of name, pkgs, or sources must be specified.") if pkgs and sources: - raise SaltInvocationError( - 'Only one of pkgs or sources can be specified.' - ) + raise SaltInvocationError("Only one of pkgs or sources can be specified.") targets = [] if pkgs: @@ -2243,19 +2277,16 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06 # Yum's versionlock plugin doesn't support passing just the package name # when removing a lock, so we need to get the full list and then use # fnmatch below to find the match. - current_locks = list_holds(full=_yum() == 'yum') + current_locks = list_holds(full=_yum() == "yum") ret = {} for target in targets: if isinstance(target, dict): target = next(six.iterkeys(target)) - ret[target] = {'name': target, - 'changes': {}, - 'result': False, - 'comment': ''} + ret[target] = {"name": target, "changes": {}, "result": False, "comment": ""} - if _yum() == 'dnf': + if _yum() == "dnf": search_locks = [x for x in current_locks if x == target] else: # To accommodate yum versionlock's lack of support for removing @@ -2264,36 +2295,39 @@ def unhold(name=None, pkgs=None, sources=None, **kwargs): # pylint: disable=W06 # expression double-check that the package name (obtained via # _get_hold()) matches the targeted package. search_locks = [ - x for x in current_locks - if fnmatch.fnmatch(x, '*{0}*'.format(target)) + x + for x in current_locks + if fnmatch.fnmatch(x, "*{0}*".format(target)) and target == _get_hold(x, full=False) ] if search_locks: - if __opts__['test']: + if __opts__["test"]: ret[target].update(result=None) - ret[target]['comment'] = ('Package {0} is set to be unheld.' - .format(target)) + ret[target]["comment"] = "Package {0} is set to be unheld.".format( + target + ) else: - out = _call_yum(['versionlock', 'delete'] + search_locks) - if out['retcode'] == 0: + out = _call_yum(["versionlock", "delete"] + search_locks) + if out["retcode"] == 0: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is no longer held.' - .format(target)) - ret[target]['changes']['new'] = '' - ret[target]['changes']['old'] = 'hold' + ret[target]["comment"] = "Package {0} is no longer held.".format( + target + ) + ret[target]["changes"]["new"] = "" + ret[target]["changes"]["old"] = "hold" else: - ret[target]['comment'] = ('Package {0} was unable to be ' - 'unheld.'.format(target)) + ret[target][ + "comment" + ] = "Package {0} was unable to be " "unheld.".format(target) else: ret[target].update(result=True) - ret[target]['comment'] = ('Package {0} is not being held.' - .format(target)) + ret[target]["comment"] = "Package {0} is not being held.".format(target) return ret def list_holds(pattern=__HOLD_PATTERN, full=True): - r''' + r""" .. versionchanged:: 2016.3.0,2015.8.4,2015.5.10 Function renamed from ``pkg.get_locked_pkgs`` to ``pkg.list_holds``. @@ -2320,24 +2354,25 @@ def list_holds(pattern=__HOLD_PATTERN, full=True): salt '*' pkg.list_holds salt '*' pkg.list_holds full=False - ''' + """ _check_versionlock() - out = __salt__['cmd.run']([_yum(), 'versionlock', 'list'], - python_shell=False) + out = __salt__["cmd.run"]([_yum(), "versionlock", "list"], python_shell=False) ret = [] - for line in salt.utils.itertools.split(out, '\n'): + for line in salt.utils.itertools.split(out, "\n"): match = _get_hold(line, pattern=pattern, full=full) if match is not None: ret.append(match) return ret -get_locked_packages = salt.utils.functools.alias_function(list_holds, 'get_locked_packages') +get_locked_packages = salt.utils.functools.alias_function( + list_holds, "get_locked_packages" +) def verify(*names, **kwargs): - ''' + """ .. versionadded:: 2014.1.0 Runs an rpm -Va on a system, and returns the results in a dict @@ -2357,12 +2392,12 @@ def verify(*names, **kwargs): salt '*' pkg.verify 'httpd postfix' salt '*' pkg.verify 'httpd postfix' ignore_types=['config','doc'] salt '*' pkg.verify 'httpd postfix' verify_options=['nodeps','nosize'] - ''' - return __salt__['lowpkg.verify'](*names, **kwargs) + """ + return __salt__["lowpkg.verify"](*names, **kwargs) def group_list(): - ''' + """ .. versionadded:: 2014.1.0 Lists all groups known by yum on this system @@ -2372,30 +2407,30 @@ def group_list(): .. code-block:: bash salt '*' pkg.group_list - ''' - ret = {'installed': [], - 'available': [], - 'installed environments': [], - 'available environments': [], - 'available languages': {}} - - section_map = { - 'installed groups:': 'installed', - 'available groups:': 'available', - 'installed environment groups:': 'installed environments', - 'available environment groups:': 'available environments', - 'available language groups:': 'available languages', + """ + ret = { + "installed": [], + "available": [], + "installed environments": [], + "available environments": [], + "available languages": {}, } - out = __salt__['cmd.run_stdout']( - [_yum(), 'grouplist', 'hidden'], - output_loglevel='trace', - python_shell=False + section_map = { + "installed groups:": "installed", + "available groups:": "available", + "installed environment groups:": "installed environments", + "available environment groups:": "available environments", + "available language groups:": "available languages", + } + + out = __salt__["cmd.run_stdout"]( + [_yum(), "grouplist", "hidden"], output_loglevel="trace", python_shell=False ) key = None - for line in salt.utils.itertools.split(out, '\n'): + for line in salt.utils.itertools.split(out, "\n"): line_lc = line.lower() - if line_lc == 'done': + if line_lc == "done": break section_lookup = section_map.get(line_lc) @@ -2408,18 +2443,18 @@ def group_list(): continue line = line.strip() - if key != 'available languages': + if key != "available languages": ret[key].append(line) else: - match = re.match(r'(.+) \[(.+)\]', line) + match = re.match(r"(.+) \[(.+)\]", line) if match: name, lang = match.groups() - ret[key][line] = {'name': name, 'language': lang} + ret[key][line] = {"name": name, "language": lang} return ret def group_info(name, expand=False): - ''' + """ .. versionadded:: 2014.1.0 .. versionchanged:: 2016.3.0,2015.8.4,2015.5.10 The return data has changed. A new key ``type`` has been added to @@ -2447,47 +2482,42 @@ def group_info(name, expand=False): .. code-block:: bash salt '*' pkg.group_info 'Perl Support' - ''' - pkgtypes = ('mandatory', 'optional', 'default', 'conditional') + """ + pkgtypes = ("mandatory", "optional", "default", "conditional") ret = {} for pkgtype in pkgtypes: ret[pkgtype] = set() - cmd = [_yum(), '--quiet', 'groupinfo', name] - out = __salt__['cmd.run_stdout']( - cmd, - output_loglevel='trace', - python_shell=False - ) + cmd = [_yum(), "--quiet", "groupinfo", name] + out = __salt__["cmd.run_stdout"](cmd, output_loglevel="trace", python_shell=False) g_info = {} - for line in salt.utils.itertools.split(out, '\n'): + for line in salt.utils.itertools.split(out, "\n"): try: - key, value = [x.strip() for x in line.split(':')] + key, value = [x.strip() for x in line.split(":")] g_info[key.lower()] = value except ValueError: continue - if 'environment group' in g_info: - ret['type'] = 'environment group' - elif 'group' in g_info: - ret['type'] = 'package group' + if "environment group" in g_info: + ret["type"] = "environment group" + elif "group" in g_info: + ret["type"] = "package group" - ret['group'] = g_info.get('environment group') or g_info.get('group') - ret['id'] = g_info.get('environment-id') or g_info.get('group-id') - if not ret['group'] and not ret['id']: - raise CommandExecutionError('Group \'{0}\' not found'.format(name)) + ret["group"] = g_info.get("environment group") or g_info.get("group") + ret["id"] = g_info.get("environment-id") or g_info.get("group-id") + if not ret["group"] and not ret["id"]: + raise CommandExecutionError("Group '{0}' not found".format(name)) - ret['description'] = g_info.get('description', '') + ret["description"] = g_info.get("description", "") - pkgtypes_capturegroup = '(' + '|'.join(pkgtypes) + ')' + pkgtypes_capturegroup = "(" + "|".join(pkgtypes) + ")" for pkgtype in pkgtypes: target_found = False - for line in salt.utils.itertools.split(out, '\n'): + for line in salt.utils.itertools.split(out, "\n"): line = line.strip().lstrip(string.punctuation) match = re.match( - pkgtypes_capturegroup + r' (?:groups|packages):\s*$', - line.lower() + pkgtypes_capturegroup + r" (?:groups|packages):\s*$", line.lower() ) if match: if target_found: @@ -2499,7 +2529,7 @@ def group_info(name, expand=False): target_found = True continue if target_found: - if expand and ret['type'] == 'environment group': + if expand and ret["type"] == "environment group": expanded = group_info(line, expand=True) # Don't shadow the pkgtype variable from the outer loop for p_type in pkgtypes: @@ -2514,7 +2544,7 @@ def group_info(name, expand=False): def group_diff(name): - ''' + """ .. versionadded:: 2014.1.0 .. versionchanged:: 2016.3.0,2015.8.4,2015.5.10 Environment groups are now supported. The key names have been renamed, @@ -2529,28 +2559,25 @@ def group_diff(name): .. code-block:: bash salt '*' pkg.group_diff 'Perl Support' - ''' - pkgtypes = ('mandatory', 'optional', 'default', 'conditional') + """ + pkgtypes = ("mandatory", "optional", "default", "conditional") ret = {} for pkgtype in pkgtypes: - ret[pkgtype] = {'installed': [], 'not installed': []} + ret[pkgtype] = {"installed": [], "not installed": []} pkgs = list_pkgs() group_pkgs = group_info(name, expand=True) for pkgtype in pkgtypes: for member in group_pkgs.get(pkgtype, []): if member in pkgs: - ret[pkgtype]['installed'].append(member) + ret[pkgtype]["installed"].append(member) else: - ret[pkgtype]['not installed'].append(member) + ret[pkgtype]["not installed"].append(member) return ret -def group_install(name, - skip=(), - include=(), - **kwargs): - ''' +def group_install(name, skip=(), include=(), **kwargs): + """ .. versionadded:: 2014.1.0 Install the passed package group(s). This is basically a wrapper around @@ -2600,33 +2627,32 @@ def group_install(name, Because this is essentially a wrapper around pkg.install, any argument which can be passed to pkg.install may also be included here, and it will be passed along wholesale. - ''' - groups = name.split(',') if isinstance(name, six.string_types) else name + """ + groups = name.split(",") if isinstance(name, six.string_types) else name if not groups: - raise SaltInvocationError('no groups specified') + raise SaltInvocationError("no groups specified") elif not isinstance(groups, list): - raise SaltInvocationError('\'groups\' must be a list') + raise SaltInvocationError("'groups' must be a list") # pylint: disable=maybe-no-member if isinstance(skip, six.string_types): - skip = skip.split(',') + skip = skip.split(",") if not isinstance(skip, (list, tuple)): - raise SaltInvocationError('\'skip\' must be a list') + raise SaltInvocationError("'skip' must be a list") if isinstance(include, six.string_types): - include = include.split(',') + include = include.split(",") if not isinstance(include, (list, tuple)): - raise SaltInvocationError('\'include\' must be a list') + raise SaltInvocationError("'include' must be a list") # pylint: enable=maybe-no-member targets = [] for group in groups: group_detail = group_info(group) - targets.extend(group_detail.get('mandatory packages', [])) + targets.extend(group_detail.get("mandatory", [])) targets.extend( - [pkg for pkg in group_detail.get('default packages', []) - if pkg not in skip] + [pkg for pkg in group_detail.get("default", []) if pkg not in skip] ) if include: targets.extend(include) @@ -2640,11 +2666,11 @@ def group_install(name, return install(pkgs=pkgs, **kwargs) -groupinstall = salt.utils.functools.alias_function(group_install, 'groupinstall') +groupinstall = salt.utils.functools.alias_function(group_install, "groupinstall") def list_repos(basedir=None): - ''' + """ Lists all repos in <basedir> (default: all dirs in `reposdir` yum option). CLI Example: @@ -2654,28 +2680,28 @@ def list_repos(basedir=None): salt '*' pkg.list_repos salt '*' pkg.list_repos basedir=/path/to/dir salt '*' pkg.list_repos basedir=/path/to/dir,/path/to/another/dir - ''' + """ basedirs = _normalize_basedir(basedir) repos = {} - log.debug('Searching for repos in %s', basedirs) + log.debug("Searching for repos in %s", basedirs) for bdir in basedirs: if not os.path.exists(bdir): continue for repofile in os.listdir(bdir): - repopath = '{0}/{1}'.format(bdir, repofile) - if not repofile.endswith('.repo'): + repopath = "{0}/{1}".format(bdir, repofile) + if not repofile.endswith(".repo"): continue filerepos = _parse_repo_file(repopath)[1] for reponame in filerepos: repo = filerepos[reponame] - repo['file'] = repopath + repo["file"] = repopath repos[reponame] = repo return repos def get_repo(name, basedir=None, **kwargs): # pylint: disable=W0613 - ''' + """ Display a repo from <basedir> (default basedir: all dirs in ``reposdir`` yum option). @@ -2686,14 +2712,14 @@ def get_repo(name, basedir=None, **kwargs): # pylint: disable=W0613 salt '*' pkg.get_repo myrepo salt '*' pkg.get_repo myrepo basedir=/path/to/dir salt '*' pkg.get_repo myrepo basedir=/path/to/dir,/path/to/another/dir - ''' + """ repos = list_repos(basedir) # Find out what file the repo lives in - repofile = '' + repofile = "" for repo in repos: if repo == name: - repofile = repos[repo]['file'] + repofile = repos[repo]["file"] if repofile: # Return just one repo @@ -2703,7 +2729,7 @@ def get_repo(name, basedir=None, **kwargs): # pylint: disable=W0613 def del_repo(repo, basedir=None, **kwargs): # pylint: disable=W0613 - ''' + """ Delete a repo from <basedir> (default basedir: all dirs in `reposdir` yum option). @@ -2717,34 +2743,32 @@ def del_repo(repo, basedir=None, **kwargs): # pylint: disable=W0613 salt '*' pkg.del_repo myrepo salt '*' pkg.del_repo myrepo basedir=/path/to/dir salt '*' pkg.del_repo myrepo basedir=/path/to/dir,/path/to/another/dir - ''' + """ # this is so we know which dirs are searched for our error messages below basedirs = _normalize_basedir(basedir) repos = list_repos(basedirs) if repo not in repos: - return 'Error: the {0} repo does not exist in {1}'.format( - repo, basedirs) + return "Error: the {0} repo does not exist in {1}".format(repo, basedirs) # Find out what file the repo lives in - repofile = '' + repofile = "" for arepo in repos: if arepo == repo: - repofile = repos[arepo]['file'] + repofile = repos[arepo]["file"] # See if the repo is the only one in the file onlyrepo = True for arepo in six.iterkeys(repos): if arepo == repo: continue - if repos[arepo]['file'] == repofile: + if repos[arepo]["file"] == repofile: onlyrepo = False # If this is the only repo in the file, delete the file itself if onlyrepo: os.remove(repofile) - return 'File {0} containing repo {1} has been removed'.format( - repofile, repo) + return "File {0} containing repo {1} has been removed".format(repofile, repo) # There must be other repos in this file, write the file with them header, filerepos = _parse_repo_file(repofile) @@ -2752,29 +2776,30 @@ def del_repo(repo, basedir=None, **kwargs): # pylint: disable=W0613 for stanza in six.iterkeys(filerepos): if stanza == repo: continue - comments = '' - if 'comments' in six.iterkeys(filerepos[stanza]): + comments = "" + if "comments" in six.iterkeys(filerepos[stanza]): comments = salt.utils.pkg.rpm.combine_comments( - filerepos[stanza]['comments']) - del filerepos[stanza]['comments'] - content += '\n[{0}]'.format(stanza) + filerepos[stanza]["comments"] + ) + del filerepos[stanza]["comments"] + content += "\n[{0}]".format(stanza) for line in filerepos[stanza]: # A whitespace is needed at the begining of the new line in order # to avoid breaking multiple line values allowed on repo files. value = filerepos[stanza][line] - if isinstance(value, six.string_types) and '\n' in value: - value = '\n '.join(value.split('\n')) - content += '\n{0}={1}'.format(line, value) - content += '\n{0}\n'.format(comments) + if isinstance(value, six.string_types) and "\n" in value: + value = "\n ".join(value.split("\n")) + content += "\n{0}={1}".format(line, value) + content += "\n{0}\n".format(comments) - with salt.utils.files.fopen(repofile, 'w') as fileout: + with salt.utils.files.fopen(repofile, "w") as fileout: fileout.write(salt.utils.stringutils.to_str(content)) - return 'Repo {0} has been removed from {1}'.format(repo, repofile) + return "Repo {0} has been removed from {1}".format(repo, repofile) def mod_repo(repo, basedir=None, **kwargs): - ''' + """ Modify one or more values for a repo. If the repo does not exist, it will be created, so long as the following values are specified: @@ -2798,16 +2823,17 @@ def mod_repo(repo, basedir=None, **kwargs): salt '*' pkg.mod_repo reponame enabled=1 gpgcheck=1 salt '*' pkg.mod_repo reponame basedir=/path/to/dir enabled=1 salt '*' pkg.mod_repo reponame baseurl= mirrorlist=http://host.com/ - ''' + """ # Filter out '__pub' arguments, as well as saltenv repo_opts = dict( - (x, kwargs[x]) for x in kwargs - if not x.startswith('__') and x not in ('saltenv',) + (x, kwargs[x]) + for x in kwargs + if not x.startswith("__") and x not in ("saltenv",) ) - if all(x in repo_opts for x in ('mirrorlist', 'baseurl')): + if all(x in repo_opts for x in ("mirrorlist", "baseurl")): raise SaltInvocationError( - 'Only one of \'mirrorlist\' and \'baseurl\' can be specified' + "Only one of 'mirrorlist' and 'baseurl' can be specified" ) # Build a list of keys to be deleted @@ -2820,22 +2846,22 @@ def mod_repo(repo, basedir=None, **kwargs): # Add baseurl or mirrorlist to the 'todelete' list if the other was # specified in the repo_opts - if 'mirrorlist' in repo_opts: - todelete.append('baseurl') - elif 'baseurl' in repo_opts: - todelete.append('mirrorlist') + if "mirrorlist" in repo_opts: + todelete.append("baseurl") + elif "baseurl" in repo_opts: + todelete.append("mirrorlist") # Fail if the user tried to delete the name - if 'name' in todelete: - raise SaltInvocationError('The repo name cannot be deleted') + if "name" in todelete: + raise SaltInvocationError("The repo name cannot be deleted") # Give the user the ability to change the basedir repos = {} basedirs = _normalize_basedir(basedir) repos = list_repos(basedirs) - repofile = '' - header = '' + repofile = "" + header = "" filerepos = {} if repo not in repos: # If the repo doesn't exist, create it in a new file in the first @@ -2847,41 +2873,39 @@ def mod_repo(repo, basedir=None, **kwargs): break if not newdir: raise SaltInvocationError( - 'The repo does not exist and needs to be created, but none ' - 'of the following basedir directories exist: {0}'.format(basedirs) + "The repo does not exist and needs to be created, but none " + "of the following basedir directories exist: {0}".format(basedirs) ) - repofile = '{0}/{1}.repo'.format(newdir, repo) + repofile = "{0}/{1}.repo".format(newdir, repo) - if 'name' not in repo_opts: + if "name" not in repo_opts: raise SaltInvocationError( - 'The repo does not exist and needs to be created, but a name ' - 'was not given' + "The repo does not exist and needs to be created, but a name " + "was not given" ) - if 'baseurl' not in repo_opts and 'mirrorlist' not in repo_opts: + if "baseurl" not in repo_opts and "mirrorlist" not in repo_opts: raise SaltInvocationError( - 'The repo does not exist and needs to be created, but either ' - 'a baseurl or a mirrorlist needs to be given' + "The repo does not exist and needs to be created, but either " + "a baseurl or a mirrorlist needs to be given" ) filerepos[repo] = {} else: # The repo does exist, open its file - repofile = repos[repo]['file'] + repofile = repos[repo]["file"] header, filerepos = _parse_repo_file(repofile) # Error out if they tried to delete baseurl or mirrorlist improperly - if 'baseurl' in todelete: - if 'mirrorlist' not in repo_opts and 'mirrorlist' \ - not in filerepos[repo]: + if "baseurl" in todelete: + if "mirrorlist" not in repo_opts and "mirrorlist" not in filerepos[repo]: raise SaltInvocationError( - 'Cannot delete baseurl without specifying mirrorlist' + "Cannot delete baseurl without specifying mirrorlist" ) - if 'mirrorlist' in todelete: - if 'baseurl' not in repo_opts and 'baseurl' \ - not in filerepos[repo]: + if "mirrorlist" in todelete: + if "baseurl" not in repo_opts and "baseurl" not in filerepos[repo]: raise SaltInvocationError( - 'Cannot delete mirrorlist without specifying baseurl' + "Cannot delete mirrorlist without specifying baseurl" ) # Delete anything in the todelete list @@ -2889,84 +2913,82 @@ def mod_repo(repo, basedir=None, **kwargs): if key in six.iterkeys(filerepos[repo].copy()): del filerepos[repo][key] - _bool_to_str = lambda x: '1' if x else '0' + _bool_to_str = lambda x: "1" if x else "0" # Old file or new, write out the repos(s) filerepos[repo].update(repo_opts) content = header for stanza in six.iterkeys(filerepos): comments = salt.utils.pkg.rpm.combine_comments( - filerepos[stanza].pop('comments', []) + filerepos[stanza].pop("comments", []) ) - content += '[{0}]\n'.format(stanza) + content += "[{0}]\n".format(stanza) for line in six.iterkeys(filerepos[stanza]): # A whitespace is needed at the begining of the new line in order # to avoid breaking multiple line values allowed on repo files. value = filerepos[stanza][line] - if isinstance(value, six.string_types) and '\n' in value: - value = '\n '.join(value.split('\n')) - content += '{0}={1}\n'.format( - line, - value if not isinstance(value, bool) else _bool_to_str(value) + if isinstance(value, six.string_types) and "\n" in value: + value = "\n ".join(value.split("\n")) + content += "{0}={1}\n".format( + line, value if not isinstance(value, bool) else _bool_to_str(value) ) - content += comments + '\n' + content += comments + "\n" - with salt.utils.files.fopen(repofile, 'w') as fileout: + with salt.utils.files.fopen(repofile, "w") as fileout: fileout.write(salt.utils.stringutils.to_str(content)) return {repofile: filerepos} def _parse_repo_file(filename): - ''' + """ Turn a single repo file into a dict - ''' + """ parsed = configparser.ConfigParser() config = {} try: parsed.read(filename) except configparser.MissingSectionHeaderError as err: - log.error( - 'Failed to parse file %s, error: %s', - filename, err.message - ) - return ('', {}) + log.error("Failed to parse file %s, error: %s", filename, err.message) + return ("", {}) for section in parsed._sections: section_dict = dict(parsed._sections[section]) - section_dict.pop('__name__', None) + section_dict.pop("__name__", None) config[section] = section_dict # Try to extract header comments, as well as comments for each repo. Read # from the beginning of the file and assume any leading comments are # header comments. Continue to read each section header and then find the # comments for each repo. - headers = '' + headers = "" section = None - with salt.utils.files.fopen(filename, 'r') as repofile: + with salt.utils.files.fopen(filename, "r") as repofile: for line in repofile: line = salt.utils.stringutils.to_unicode(line) line = line.strip() - if line.startswith('#'): + if line.startswith("#"): if section is None: - headers += line + '\n' + headers += line + "\n" else: try: - comments = config[section].setdefault('comments', []) + comments = config[section].setdefault("comments", []) comments.append(line[1:].lstrip()) except KeyError: log.debug( - 'Found comment in %s which does not appear to ' - 'belong to any repo section: %s', filename, line + "Found comment in %s which does not appear to " + "belong to any repo section: %s", + filename, + line, ) - elif line.startswith('[') and line.endswith(']'): + elif line.startswith("[") and line.endswith("]"): section = line[1:-1] return (headers, salt.utils.data.decode(config)) def file_list(*packages): - ''' + """ .. versionadded:: 2014.1.0 List the files that belong to a package. Not specifying any packages will @@ -2980,12 +3002,12 @@ def file_list(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' - return __salt__['lowpkg.file_list'](*packages) + """ + return __salt__["lowpkg.file_list"](*packages) def file_dict(*packages): - ''' + """ .. versionadded:: 2014.1.0 List the files that belong to a package, grouped by package. Not @@ -2999,12 +3021,12 @@ def file_dict(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' - return __salt__['lowpkg.file_dict'](*packages) + """ + return __salt__["lowpkg.file_dict"](*packages) def owner(*paths): - ''' + """ .. versionadded:: 2014.7.0 Return the name of the package that owns the file. Multiple file paths can @@ -3021,26 +3043,24 @@ def owner(*paths): salt '*' pkg.owner /usr/bin/apachectl salt '*' pkg.owner /usr/bin/apachectl /etc/httpd/conf/httpd.conf - ''' + """ if not paths: - return '' + return "" ret = {} - cmd_prefix = ['rpm', '-qf', '--queryformat', '%{name}'] + cmd_prefix = ["rpm", "-qf", "--queryformat", "%{name}"] for path in paths: - ret[path] = __salt__['cmd.run_stdout']( - cmd_prefix + [path], - output_loglevel='trace', - python_shell=False + ret[path] = __salt__["cmd.run_stdout"]( + cmd_prefix + [path], output_loglevel="trace", python_shell=False ) - if 'not owned' in ret[path].lower(): - ret[path] = '' + if "not owned" in ret[path].lower(): + ret[path] = "" if len(ret) == 1: return next(six.itervalues(ret)) return ret def modified(*packages, **flags): - ''' + """ List the modified files that belong to a package. Not specifying any packages will return a list of _all_ modified files on the system's RPM database. @@ -3085,14 +3105,14 @@ def modified(*packages, **flags): salt '*' pkg.modified httpd salt '*' pkg.modified httpd postfix salt '*' pkg.modified httpd owner=True group=False - ''' + """ - return __salt__['lowpkg.modified'](*packages, **flags) + return __salt__["lowpkg.modified"](*packages, **flags) -@salt.utils.decorators.path.which('yumdownloader') +@salt.utils.decorators.path.which("yumdownloader") def download(*packages): - ''' + """ .. versionadded:: 2015.5.0 Download packages to the local disk. Requires ``yumdownloader`` from @@ -3109,41 +3129,41 @@ def download(*packages): salt '*' pkg.download httpd salt '*' pkg.download httpd postfix - ''' + """ if not packages: - raise SaltInvocationError('No packages were specified') + raise SaltInvocationError("No packages were specified") - CACHE_DIR = '/var/cache/yum/packages' + CACHE_DIR = "/var/cache/yum/packages" if not os.path.exists(CACHE_DIR): os.makedirs(CACHE_DIR) cached_pkgs = os.listdir(CACHE_DIR) to_purge = [] for pkg in packages: - to_purge.extend([os.path.join(CACHE_DIR, x) - for x in cached_pkgs - if x.startswith('{0}-'.format(pkg))]) + to_purge.extend( + [ + os.path.join(CACHE_DIR, x) + for x in cached_pkgs + if x.startswith("{0}-".format(pkg)) + ] + ) for purge_target in set(to_purge): - log.debug('Removing cached package %s', purge_target) + log.debug("Removing cached package %s", purge_target) try: os.unlink(purge_target) except OSError as exc: - log.error('Unable to remove %s: %s', purge_target, exc) + log.error("Unable to remove %s: %s", purge_target, exc) - cmd = ['yumdownloader', '-q', '--destdir={0}'.format(CACHE_DIR)] + cmd = ["yumdownloader", "-q", "--destdir={0}".format(CACHE_DIR)] cmd.extend(packages) - __salt__['cmd.run']( - cmd, - output_loglevel='trace', - python_shell=False - ) + __salt__["cmd.run"](cmd, output_loglevel="trace", python_shell=False) ret = {} for dld_result in os.listdir(CACHE_DIR): - if not dld_result.endswith('.rpm'): + if not dld_result.endswith(".rpm"): continue pkg_name = None pkg_file = None for query_pkg in packages: - if dld_result.startswith('{0}-'.format(query_pkg)): + if dld_result.startswith("{0}-".format(query_pkg)): pkg_name = query_pkg pkg_file = dld_result break @@ -3152,19 +3172,21 @@ def download(*packages): if not ret: raise CommandExecutionError( - 'Unable to download any of the following packages: {0}' - .format(', '.join(packages)) + "Unable to download any of the following packages: {0}".format( + ", ".join(packages) + ) ) failed = [x for x in packages if x not in ret] if failed: - ret['_error'] = ('The following package(s) failed to download: {0}' - .format(', '.join(failed))) + ret["_error"] = "The following package(s) failed to download: {0}".format( + ", ".join(failed) + ) return ret def diff(*paths): - ''' + """ Return a formatted diff between current files and original in a package. NOTE: this function includes all files (configuration and not), but does not work on binary content. @@ -3177,60 +3199,60 @@ def diff(*paths): .. code-block:: bash salt '*' pkg.diff /etc/apache2/httpd.conf /etc/sudoers - ''' + """ ret = {} pkg_to_paths = {} for pth in paths: - pth_pkg = __salt__['lowpkg.owner'](pth) + pth_pkg = __salt__["lowpkg.owner"](pth) if not pth_pkg: - ret[pth] = os.path.exists(pth) and 'Not managed' or 'N/A' + ret[pth] = os.path.exists(pth) and "Not managed" or "N/A" else: if pkg_to_paths.get(pth_pkg) is None: pkg_to_paths[pth_pkg] = [] pkg_to_paths[pth_pkg].append(pth) if pkg_to_paths: - local_pkgs = __salt__['pkg.download'](*pkg_to_paths.keys()) + local_pkgs = __salt__["pkg.download"](*pkg_to_paths.keys()) for pkg, files in pkg_to_paths.items(): for path in files: - ret[path] = __salt__['lowpkg.diff']( - local_pkgs[pkg]['path'], path) or 'Unchanged' + ret[path] = ( + __salt__["lowpkg.diff"](local_pkgs[pkg]["path"], path) + or "Unchanged" + ) return ret def _get_patches(installed_only=False): - ''' + """ List all known patches in repos. - ''' + """ patches = {} - cmd = [_yum(), '--quiet', 'updateinfo', 'list', 'all'] - ret = __salt__['cmd.run_stdout']( - cmd, - python_shell=False - ) + cmd = [_yum(), "--quiet", "updateinfo", "list", "all"] + ret = __salt__["cmd.run_stdout"](cmd, python_shell=False) for line in salt.utils.itertools.split(ret, os.linesep): - inst, advisory_id, sev, pkg = re.match(r'([i|\s]) ([^\s]+) +([^\s]+) +([^\s]+)', - line).groups() + inst, advisory_id, sev, pkg = re.match( + r"([i|\s]) ([^\s]+) +([^\s]+) +([^\s]+)", line + ).groups() if advisory_id not in patches: patches[advisory_id] = { - 'installed': True if inst == 'i' else False, - 'summary': [pkg] + "installed": True if inst == "i" else False, + "summary": [pkg], } else: - patches[advisory_id]['summary'].append(pkg) - if inst != 'i': - patches[advisory_id]['installed'] = False + patches[advisory_id]["summary"].append(pkg) + if inst != "i": + patches[advisory_id]["installed"] = False if installed_only: - patches = {k: v for k, v in patches.items() if v['installed']} + patches = {k: v for k, v in patches.items() if v["installed"]} return patches def list_patches(refresh=False): - ''' + """ .. versionadded:: 2017.7.0 List all known advisory patches from available repos. @@ -3245,7 +3267,7 @@ def list_patches(refresh=False): .. code-block:: bash salt '*' pkg.list_patches - ''' + """ if refresh: refresh_db() @@ -3253,7 +3275,7 @@ def list_patches(refresh=False): def list_installed_patches(): - ''' + """ .. versionadded:: 2017.7.0 List installed advisory patches on the system. @@ -3263,5 +3285,5 @@ def list_installed_patches(): .. code-block:: bash salt '*' pkg.list_installed_patches - ''' + """ return _get_patches(installed_only=True) diff --git a/salt/modules/zabbix.py b/salt/modules/zabbix.py index 8942e2081c2..735afcfa536 100644 --- a/salt/modules/zabbix.py +++ b/salt/modules/zabbix.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for Zabbix :optdepends: - zabbix server @@ -22,104 +22,120 @@ Support for Zabbix zabbix.apiinfo_version _connection_user=Admin _connection_password=zabbix _connection_url=http://host/zabbix/ :codeauthor: Jiri Kotlin <jiri.kotlin@ultimum.io> -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging -import socket import os +import socket -# Import Salt libs -from salt.ext import six -from salt.exceptions import SaltException import salt.utils.data import salt.utils.files import salt.utils.http import salt.utils.json -from salt.utils.versions import LooseVersion as _LooseVersion +from salt.exceptions import SaltException + +# Import Salt libs +from salt.ext import six + # pylint: disable=import-error,no-name-in-module,unused-import from salt.ext.six.moves.urllib.error import HTTPError, URLError +from salt.utils.versions import LooseVersion as _LooseVersion + # pylint: enable=import-error,no-name-in-module,unused-import log = logging.getLogger(__name__) INTERFACE_DEFAULT_PORTS = [10050, 161, 623, 12345] -ZABBIX_TOP_LEVEL_OBJECTS = ('hostgroup', 'template', 'host', 'maintenance', 'action', 'drule', 'service', 'proxy', - 'screen', 'usergroup', 'mediatype', 'script', 'valuemap') +ZABBIX_TOP_LEVEL_OBJECTS = ( + "hostgroup", + "template", + "host", + "maintenance", + "action", + "drule", + "service", + "proxy", + "screen", + "usergroup", + "mediatype", + "script", + "valuemap", +) # Zabbix object and its ID name mapping ZABBIX_ID_MAPPER = { - 'action': 'actionid', - 'alert': 'alertid', - 'application': 'applicationid', - 'dhost': 'dhostid', - 'dservice': 'dserviceid', - 'dcheck': 'dcheckid', - 'drule': 'druleid', - 'event': 'eventid', - 'graph': 'graphid', - 'graphitem': 'gitemid', - 'graphprototype': 'graphid', - 'history': 'itemid', - 'host': 'hostid', - 'hostgroup': 'groupid', - 'hostinterface': 'interfaceid', - 'hostprototype': 'hostid', - 'iconmap': 'iconmapid', - 'image': 'imageid', - 'item': 'itemid', - 'itemprototype': 'itemid', - 'service': 'serviceid', - 'discoveryrule': 'itemid', - 'maintenance': 'maintenanceid', - 'map': 'sysmapid', - 'usermedia': 'mediaid', - 'mediatype': 'mediatypeid', - 'proxy': 'proxyid', - 'screen': 'screenid', - 'screenitem': 'screenitemid', - 'script': 'scriptid', - 'template': 'templateid', - 'templatescreen': 'screenid', - 'templatescreenitem': 'screenitemid', - 'trend': 'itemid', - 'trigger': 'triggerid', - 'triggerprototype': 'triggerid', - 'user': 'userid', - 'usergroup': 'usrgrpid', - 'usermacro': 'globalmacroid', - 'valuemap': 'valuemapid', - 'httptest': 'httptestid' + "action": "actionid", + "alert": "alertid", + "application": "applicationid", + "dhost": "dhostid", + "dservice": "dserviceid", + "dcheck": "dcheckid", + "drule": "druleid", + "event": "eventid", + "graph": "graphid", + "graphitem": "gitemid", + "graphprototype": "graphid", + "history": "itemid", + "host": "hostid", + "hostgroup": "groupid", + "hostinterface": "interfaceid", + "hostprototype": "hostid", + "iconmap": "iconmapid", + "image": "imageid", + "item": "itemid", + "itemprototype": "itemid", + "service": "serviceid", + "discoveryrule": "itemid", + "maintenance": "maintenanceid", + "map": "sysmapid", + "usermedia": "mediaid", + "mediatype": "mediatypeid", + "proxy": "proxyid", + "screen": "screenid", + "screenitem": "screenitemid", + "script": "scriptid", + "template": "templateid", + "templatescreen": "screenid", + "templatescreenitem": "screenitemid", + "trend": "itemid", + "trigger": "triggerid", + "triggerprototype": "triggerid", + "user": "userid", + "usergroup": "usrgrpid", + "usermacro": "globalmacroid", + "valuemap": "valuemapid", + "httptest": "httptestid", } # Define the module's virtual name -__virtualname__ = 'zabbix' +__virtualname__ = "zabbix" def __virtual__(): - ''' + """ Only load the module if all modules are imported correctly. - ''' + """ return __virtualname__ def _frontend_url(): - ''' + """ Tries to guess the url of zabbix frontend. .. versionadded:: 2016.3.0 - ''' + """ hostname = socket.gethostname() - frontend_url = 'http://' + hostname + '/zabbix/api_jsonrpc.php' + frontend_url = "http://" + hostname + "/zabbix/api_jsonrpc.php" try: try: response = salt.utils.http.query(frontend_url) - error = response['error'] + error = response["error"] except HTTPError as http_e: error = six.text_type(http_e) - if error.find('412: Precondition Failed'): + if error.find("412: Precondition Failed"): return frontend_url else: raise KeyError @@ -128,7 +144,7 @@ def _frontend_url(): def _query(method, params, url, auth=None): - ''' + """ JSON request to Zabbix API. .. versionadded:: 2016.3.0 @@ -141,44 +157,61 @@ def _query(method, params, url, auth=None): :return: Response from API with desired data in JSON format. In case of error returns more specific description. .. versionchanged:: 2017.7 - ''' + """ - unauthenticated_methods = ['user.login', 'apiinfo.version', ] + unauthenticated_methods = [ + "user.login", + "apiinfo.version", + ] - header_dict = {'Content-type': 'application/json'} - data = {'jsonrpc': '2.0', 'id': 0, 'method': method, 'params': params} + header_dict = {"Content-type": "application/json"} + data = {"jsonrpc": "2.0", "id": 0, "method": method, "params": params} if method not in unauthenticated_methods: - data['auth'] = auth + data["auth"] = auth data = salt.utils.json.dumps(data) - log.info('_QUERY input:\nurl: %s\ndata: %s', six.text_type(url), six.text_type(data)) + log.info( + "_QUERY input:\nurl: %s\ndata: %s", six.text_type(url), six.text_type(data) + ) try: - result = salt.utils.http.query(url, - method='POST', - data=data, - header_dict=header_dict, - decode_type='json', - decode=True, - status=True, - headers=True) - log.info('_QUERY result: %s', six.text_type(result)) - if 'error' in result: - raise SaltException('Zabbix API: Status: {0} ({1})'.format(result['status'], result['error'])) - ret = result.get('dict', {}) - if 'error' in ret: - raise SaltException('Zabbix API: {} ({})'.format(ret['error']['message'], ret['error']['data'])) + result = salt.utils.http.query( + url, + method="POST", + data=data, + header_dict=header_dict, + decode_type="json", + decode=True, + status=True, + headers=True, + ) + log.info("_QUERY result: %s", six.text_type(result)) + if "error" in result: + raise SaltException( + "Zabbix API: Status: {0} ({1})".format( + result["status"], result["error"] + ) + ) + ret = result.get("dict", {}) + if "error" in ret: + raise SaltException( + "Zabbix API: {} ({})".format( + ret["error"]["message"], ret["error"]["data"] + ) + ) return ret except ValueError as err: - raise SaltException('URL or HTTP headers are probably not correct! ({})'.format(err)) + raise SaltException( + "URL or HTTP headers are probably not correct! ({})".format(err) + ) except socket.error as err: - raise SaltException('Check hostname in URL! ({})'.format(err)) + raise SaltException("Check hostname in URL! ({})".format(err)) def _login(**kwargs): - ''' + """ Log in to the API and generate the authentication token. .. versionadded:: 2016.3.0 @@ -189,59 +222,59 @@ def _login(**kwargs): :return: On success connargs dictionary with auth token and frontend url, False on failure. - ''' + """ connargs = dict() def _connarg(name, key=None): - ''' + """ Add key to connargs, only if name exists in our kwargs or, as zabbix.<name> in __opts__ or __pillar__ Evaluate in said order - kwargs, opts, then pillar. To avoid collision with other functions, kwargs-based connection arguments are prefixed with 'connection_' (i.e. '_connection_user', etc.). Inspired by mysql salt module. - ''' + """ if key is None: key = name if name in kwargs: connargs[key] = kwargs[name] else: - prefix = '_connection_' + prefix = "_connection_" if name.startswith(prefix): try: - name = name[len(prefix):] + name = name[len(prefix) :] except IndexError: return - val = __salt__['config.option']('zabbix.{0}'.format(name), None) + val = __salt__["config.option"]("zabbix.{0}".format(name), None) if val is not None: connargs[key] = val - _connarg('_connection_user', 'user') - _connarg('_connection_password', 'password') - _connarg('_connection_url', 'url') + _connarg("_connection_user", "user") + _connarg("_connection_password", "password") + _connarg("_connection_url", "url") - if 'url' not in connargs: - connargs['url'] = _frontend_url() + if "url" not in connargs: + connargs["url"] = _frontend_url() try: - if connargs['user'] and connargs['password'] and connargs['url']: - params = {'user': connargs['user'], 'password': connargs['password']} - method = 'user.login' - ret = _query(method, params, connargs['url']) - auth = ret['result'] - connargs['auth'] = auth - connargs.pop('user', None) - connargs.pop('password', None) + if connargs["user"] and connargs["password"] and connargs["url"]: + params = {"user": connargs["user"], "password": connargs["password"]} + method = "user.login" + ret = _query(method, params, connargs["url"]) + auth = ret["result"] + connargs["auth"] = auth + connargs.pop("user", None) + connargs.pop("password", None) return connargs else: raise KeyError except KeyError as err: - raise SaltException('URL is probably not correct! ({})'.format(err)) + raise SaltException("URL is probably not correct! ({})".format(err)) def _params_extend(params, _ignore_name=False, **kwargs): - ''' + """ Extends the params dictionary by values from keyword arguments. .. versionadded:: 2016.3.0 @@ -256,36 +289,36 @@ def _params_extend(params, _ignore_name=False, **kwargs): :return: Extended params dictionary with parameters. - ''' + """ # extend params value by optional zabbix API parameters for key in kwargs: - if not key.startswith('_'): + if not key.startswith("_"): params.setdefault(key, kwargs[key]) # ignore name parameter passed from Salt state module, use firstname or visible_name instead if _ignore_name: - params.pop('name', None) - if 'firstname' in params: - params['name'] = params.pop('firstname') - elif 'visible_name' in params: - params['name'] = params.pop('visible_name') + params.pop("name", None) + if "firstname" in params: + params["name"] = params.pop("firstname") + elif "visible_name" in params: + params["name"] = params.pop("visible_name") return params def get_zabbix_id_mapper(): - ''' + """ .. versionadded:: 2017.7 Make ZABBIX_ID_MAPPER constant available to state modules. :return: ZABBIX_ID_MAPPER - ''' + """ return ZABBIX_ID_MAPPER -def substitute_params(input_object, extend_params=None, filter_key='name', **kwargs): - ''' +def substitute_params(input_object, extend_params=None, filter_key="name", **kwargs): + """ .. versionadded:: 2017.7 Go through Zabbix object params specification and if needed get given object ID from Zabbix API and put it back @@ -299,25 +332,36 @@ def substitute_params(input_object, extend_params=None, filter_key='name', **kwa :param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring) :return: Params structure with values converted to string for further comparison purposes - ''' + """ if extend_params is None: extend_params = {} if isinstance(input_object, list): - return [substitute_params(oitem, extend_params, filter_key, **kwargs) for oitem in input_object] + return [ + substitute_params(oitem, extend_params, filter_key, **kwargs) + for oitem in input_object + ] elif isinstance(input_object, dict): - if 'query_object' in input_object: + if "query_object" in input_object: query_params = {} - if input_object['query_object'] not in ZABBIX_TOP_LEVEL_OBJECTS: + if input_object["query_object"] not in ZABBIX_TOP_LEVEL_OBJECTS: query_params.update(extend_params) try: - query_params.update({'filter': {filter_key: input_object['query_name']}}) - return get_object_id_by_params(input_object['query_object'], query_params, **kwargs) + query_params.update( + {"filter": {filter_key: input_object["query_name"]}} + ) + return get_object_id_by_params( + input_object["query_object"], query_params, **kwargs + ) except KeyError: - raise SaltException('Qyerying object ID requested ' - 'but object name not provided: {0}'.format(input_object)) + raise SaltException( + "Qyerying object ID requested " + "but object name not provided: {0}".format(input_object) + ) else: - return {key: substitute_params(val, extend_params, filter_key, **kwargs) - for key, val in input_object.items()} + return { + key: substitute_params(val, extend_params, filter_key, **kwargs) + for key, val in input_object.items() + } else: # Zabbix response is always str, return everything in str as well return six.text_type(input_object) @@ -325,7 +369,7 @@ def substitute_params(input_object, extend_params=None, filter_key='name', **kwa # pylint: disable=too-many-return-statements,too-many-nested-blocks def compare_params(defined, existing, return_old_value=False): - ''' + """ .. versionadded:: 2017.7 Compares Zabbix object definition against existing Zabbix object. @@ -335,27 +379,28 @@ def compare_params(defined, existing, return_old_value=False): :param return_old_value: Default False. If True, returns dict("old"=old_val, "new"=new_val) for rollback purpose. :return: Params that are different from existing object. Result extended by object ID can be passed directly to Zabbix API update method. - ''' + """ # Comparison of data types if not isinstance(defined, type(existing)): - raise SaltException('Zabbix object comparison failed (data type mismatch). Expecting {0}, got {1}. ' - 'Existing value: "{2}", defined value: "{3}").'.format(type(existing), - type(defined), - existing, - defined)) + raise SaltException( + "Zabbix object comparison failed (data type mismatch). Expecting {0}, got {1}. " + 'Existing value: "{2}", defined value: "{3}").'.format( + type(existing), type(defined), existing, defined + ) + ) # Comparison of values if not salt.utils.data.is_iter(defined): if six.text_type(defined) != six.text_type(existing) and return_old_value: - return {'new': six.text_type(defined), 'old': six.text_type(existing)} + return {"new": six.text_type(defined), "old": six.text_type(existing)} elif six.text_type(defined) != six.text_type(existing) and not return_old_value: return six.text_type(defined) # Comparison of lists of values or lists of dicts if isinstance(defined, list): if len(defined) != len(existing): - log.info('Different list length!') - return {'new': defined, 'old': existing} if return_old_value else defined + log.info("Different list length!") + return {"new": defined, "old": existing} if return_old_value else defined else: difflist = [] for ditem in defined: @@ -363,14 +408,14 @@ def compare_params(defined, existing, return_old_value=False): for eitem in existing: comp = compare_params(ditem, eitem, return_old_value) if return_old_value: - d_in_e.append(comp['new']) + d_in_e.append(comp["new"]) else: d_in_e.append(comp) if all(d_in_e): difflist.append(ditem) # If there is any difference in a list then whole defined list must be returned and provided for update if any(difflist) and return_old_value: - return {'new': defined, 'old': existing} + return {"new": defined, "old": existing} elif any(difflist) and not return_old_value: return defined @@ -380,30 +425,31 @@ def compare_params(defined, existing, return_old_value=False): # defined must be a subset of existing to be compared if set(defined) <= set(existing): intersection = set(defined) & set(existing) - diffdict = {'new': {}, 'old': {}} if return_old_value else {} + diffdict = {"new": {}, "old": {}} if return_old_value else {} for i in intersection: comp = compare_params(defined[i], existing[i], return_old_value) if return_old_value: if comp or (not comp and isinstance(comp, list)): - diffdict['new'].update({i: defined[i]}) - diffdict['old'].update({i: existing[i]}) + diffdict["new"].update({i: defined[i]}) + diffdict["old"].update({i: existing[i]}) else: if comp or (not comp and isinstance(comp, list)): diffdict.update({i: defined[i]}) return diffdict - return {'new': defined, 'old': existing} if return_old_value else defined + return {"new": defined, "old": existing} if return_old_value else defined except TypeError: - raise SaltException('Zabbix object comparison failed (data type mismatch). Expecting {0}, got {1}. ' - 'Existing value: "{2}", defined value: "{3}").'.format(type(existing), - type(defined), - existing, - defined)) + raise SaltException( + "Zabbix object comparison failed (data type mismatch). Expecting {0}, got {1}. " + 'Existing value: "{2}", defined value: "{3}").'.format( + type(existing), type(defined), existing, defined + ) + ) def get_object_id_by_params(obj, params=None, **connection_args): - ''' + """ .. versionadded:: 2017.7 Get ID of single Zabbix object specified by its name. @@ -415,20 +461,22 @@ def get_object_id_by_params(obj, params=None, **connection_args): :param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring) :return: object ID - ''' + """ if params is None: params = {} - res = run_query(obj + '.get', params, **connection_args) + res = run_query(obj + ".get", params, **connection_args) if res and len(res) == 1: return six.text_type(res[0][ZABBIX_ID_MAPPER[obj]]) else: - raise SaltException('Zabbix API: Object does not exist or bad Zabbix user permissions or other unexpected ' - 'result. Called method {0} with params {1}. ' - 'Result: {2}'.format(obj + '.get', params, res)) + raise SaltException( + "Zabbix API: Object does not exist or bad Zabbix user permissions or other unexpected " + "result. Called method {0} with params {1}. " + "Result: {2}".format(obj + ".get", params, res) + ) def apiinfo_version(**connection_args): - ''' + """ Retrieve the version of the Zabbix API. .. versionadded:: 2016.3.0 @@ -443,15 +491,15 @@ def apiinfo_version(**connection_args): .. code-block:: bash salt '*' zabbix.apiinfo_version - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'apiinfo.version' + method = "apiinfo.version" params = {} - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: @@ -459,7 +507,7 @@ def apiinfo_version(**connection_args): def user_create(alias, passwd, usrgrps, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Create new zabbix user @@ -487,22 +535,22 @@ def user_create(alias, passwd, usrgrps, **connection_args): .. code-block:: bash salt '*' zabbix.user_create james password007 '[7, 12]' firstname='James Bond' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'user.create' + method = "user.create" params = {"alias": alias, "passwd": passwd, "usrgrps": []} # User groups if not isinstance(usrgrps, list): usrgrps = [usrgrps] for usrgrp in usrgrps: - params['usrgrps'].append({"usrgrpid": usrgrp}) + params["usrgrps"].append({"usrgrpid": usrgrp}) params = _params_extend(params, _ignore_name=True, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['userids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["userids"] else: raise KeyError except KeyError: @@ -510,7 +558,7 @@ def user_create(alias, passwd, usrgrps, **connection_args): def user_delete(users, **connection_args): - ''' + """ Delete zabbix users. .. versionadded:: 2016.3.0 @@ -526,19 +574,19 @@ def user_delete(users, **connection_args): .. code-block:: bash salt '*' zabbix.user_delete 15 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'user.delete' + method = "user.delete" if not isinstance(users, list): params = [users] else: params = users - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['userids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["userids"] else: raise KeyError except KeyError: @@ -546,7 +594,7 @@ def user_delete(users, **connection_args): def user_exists(alias, **connection_args): - ''' + """ Checks if user with given alias exists. .. versionadded:: 2016.3.0 @@ -562,15 +610,15 @@ def user_exists(alias, **connection_args): .. code-block:: bash salt '*' zabbix.user_exists james - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'user.get' + method = "user.get" params = {"output": "extend", "filter": {"alias": alias}} - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return True if len(ret['result']) > 0 else False + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return True if len(ret["result"]) > 0 else False else: raise KeyError except KeyError: @@ -578,7 +626,7 @@ def user_exists(alias, **connection_args): def user_get(alias=None, userids=None, **connection_args): - ''' + """ Retrieve users according to the given parameters. .. versionadded:: 2016.3.0 @@ -595,22 +643,25 @@ def user_get(alias=None, userids=None, **connection_args): .. code-block:: bash salt '*' zabbix.user_get james - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'user.get' + method = "user.get" params = {"output": "extend", "filter": {}} if not userids and not alias: - return {'result': False, 'comment': 'Please submit alias or userids parameter to retrieve users.'} + return { + "result": False, + "comment": "Please submit alias or userids parameter to retrieve users.", + } if alias: - params['filter'].setdefault('alias', alias) + params["filter"].setdefault("alias", alias) if userids: - params.setdefault('userids', userids) + params.setdefault("userids", userids) params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] if len(ret['result']) > 0 else False + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] if len(ret["result"]) > 0 else False else: raise KeyError except KeyError: @@ -618,7 +669,7 @@ def user_get(alias=None, userids=None, **connection_args): def user_update(userid, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Update existing users @@ -640,16 +691,18 @@ def user_update(userid, **connection_args): .. code-block:: bash salt '*' zabbix.user_update 16 visible_name='James Brown' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'user.update' - params = {"userid": userid, } + method = "user.update" + params = { + "userid": userid, + } params = _params_extend(params, _ignore_name=True, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['userids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["userids"] else: raise KeyError except KeyError: @@ -657,7 +710,7 @@ def user_update(userid, **connection_args): def user_getmedia(userids=None, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Retrieve media according to the given parameters @@ -680,27 +733,29 @@ def user_getmedia(userids=None, **connection_args): .. code-block:: bash salt '*' zabbix.user_getmedia - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'usermedia.get' + method = "usermedia.get" if userids: params = {"userids": userids} else: params = {} params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: return ret -def user_addmedia(userids, active, mediatypeid, period, sendto, severity, **connection_args): - ''' +def user_addmedia( + userids, active, mediatypeid, period, sendto, severity, **connection_args +): + """ Add new media to multiple users. .. versionadded:: 2016.3.0 @@ -723,24 +778,31 @@ def user_addmedia(userids, active, mediatypeid, period, sendto, severity, **conn salt '*' zabbix.user_addmedia 4 active=0 mediatypeid=1 period='1-7,00:00-24:00' sendto='support2@example.com' severity=63 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'user.addmedia' + method = "user.addmedia" params = {"users": []} # Users if not isinstance(userids, list): userids = [userids] for user in userids: - params['users'].append({"userid": user}) + params["users"].append({"userid": user}) # Medias - params['medias'] = [{"active": active, "mediatypeid": mediatypeid, "period": period, - "sendto": sendto, "severity": severity}, ] + params["medias"] = [ + { + "active": active, + "mediatypeid": mediatypeid, + "period": period, + "sendto": sendto, + "severity": severity, + }, + ] - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['mediaids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["mediaids"] else: raise KeyError except KeyError: @@ -748,7 +810,7 @@ def user_addmedia(userids, active, mediatypeid, period, sendto, severity, **conn def user_deletemedia(mediaids, **connection_args): - ''' + """ Delete media by id. .. versionadded:: 2016.3.0 @@ -764,18 +826,18 @@ def user_deletemedia(mediaids, **connection_args): .. code-block:: bash salt '*' zabbix.user_deletemedia 27 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'user.deletemedia' + method = "user.deletemedia" if not isinstance(mediaids, list): mediaids = [mediaids] params = mediaids - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['mediaids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["mediaids"] else: raise KeyError except KeyError: @@ -783,7 +845,7 @@ def user_deletemedia(mediaids, **connection_args): def user_list(**connection_args): - ''' + """ Retrieve all of the configured users. .. versionadded:: 2016.3.0 @@ -798,15 +860,15 @@ def user_list(**connection_args): .. code-block:: bash salt '*' zabbix.user_list - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'user.get' + method = "user.get" params = {"output": "extend"} - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: @@ -814,7 +876,7 @@ def user_list(**connection_args): def usergroup_create(name, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Create new user group @@ -836,16 +898,16 @@ def usergroup_create(name, **connection_args): .. code-block:: bash salt '*' zabbix.usergroup_create GroupName - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'usergroup.create' + method = "usergroup.create" params = {"name": name} params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['usrgrpids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["usrgrpids"] else: raise KeyError except KeyError: @@ -853,7 +915,7 @@ def usergroup_create(name, **connection_args): def usergroup_delete(usergroupids, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 :param usergroupids: IDs of the user groups to delete @@ -868,17 +930,17 @@ def usergroup_delete(usergroupids, **connection_args): .. code-block:: bash salt '*' zabbix.usergroup_delete 28 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'usergroup.delete' + method = "usergroup.delete" if not isinstance(usergroupids, list): usergroupids = [usergroupids] params = usergroupids - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['usrgrpids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["usrgrpids"] else: raise KeyError except KeyError: @@ -886,7 +948,7 @@ def usergroup_delete(usergroupids, **connection_args): def usergroup_exists(name=None, node=None, nodeids=None, **connection_args): - ''' + """ Checks if at least one user group that matches the given filter criteria exists .. versionadded:: 2016.3.0 @@ -905,7 +967,7 @@ def usergroup_exists(name=None, node=None, nodeids=None, **connection_args): .. code-block:: bash salt '*' zabbix.usergroup_exists Guests - ''' + """ conn_args = _login(**connection_args) zabbix_version = apiinfo_version(**connection_args) ret = False @@ -914,26 +976,29 @@ def usergroup_exists(name=None, node=None, nodeids=None, **connection_args): # usergroup.exists deprecated if _LooseVersion(zabbix_version) > _LooseVersion("2.5"): if not name: - name = '' + name = "" ret = usergroup_get(name, None, **connection_args) return bool(ret) # zabbix 2.4 and earlier else: - method = 'usergroup.exists' + method = "usergroup.exists" params = {} if not name and not node and not nodeids: - return {'result': False, 'comment': 'Please submit name, node or nodeids parameter to check if ' - 'at least one user group exists.'} + return { + "result": False, + "comment": "Please submit name, node or nodeids parameter to check if " + "at least one user group exists.", + } if name: - params['name'] = name + params["name"] = name # deprecated in 2.4 if _LooseVersion(zabbix_version) < _LooseVersion("2.4"): if node: - params['node'] = node + params["node"] = node if nodeids: - params['nodeids'] = nodeids - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + params["nodeids"] = nodeids + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: @@ -941,7 +1006,7 @@ def usergroup_exists(name=None, node=None, nodeids=None, **connection_args): def usergroup_get(name=None, usrgrpids=None, userids=None, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Retrieve user groups according to the given parameters @@ -965,13 +1030,13 @@ def usergroup_get(name=None, usrgrpids=None, userids=None, **connection_args): .. code-block:: bash salt '*' zabbix.usergroup_get Guests - ''' + """ conn_args = _login(**connection_args) zabbix_version = apiinfo_version(**connection_args) ret = False try: if conn_args: - method = 'usergroup.get' + method = "usergroup.get" # Versions above 2.4 allow retrieving user group permissions if _LooseVersion(zabbix_version) > _LooseVersion("2.5"): params = {"selectRights": "extend", "output": "extend", "filter": {}} @@ -980,15 +1045,15 @@ def usergroup_get(name=None, usrgrpids=None, userids=None, **connection_args): if not name and not usrgrpids and not userids: return False if name: - params['filter'].setdefault('name', name) + params["filter"].setdefault("name", name) if usrgrpids: - params.setdefault('usrgrpids', usrgrpids) + params.setdefault("usrgrpids", usrgrpids) if userids: - params.setdefault('userids', userids) + params.setdefault("userids", userids) params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) + ret = _query(method, params, conn_args["url"], conn_args["auth"]) - return False if len(ret['result']) < 1 else ret['result'] + return False if len(ret["result"]) < 1 else ret["result"] else: raise KeyError except KeyError: @@ -996,7 +1061,7 @@ def usergroup_get(name=None, usrgrpids=None, userids=None, **connection_args): def usergroup_update(usrgrpid, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Update existing user group @@ -1018,16 +1083,16 @@ def usergroup_update(usrgrpid, **connection_args): .. code-block:: bash salt '*' zabbix.usergroup_update 8 name=guestsRenamed - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'usergroup.update' + method = "usergroup.update" params = {"usrgrpid": usrgrpid} params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['usrgrpids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["usrgrpids"] else: raise KeyError except KeyError: @@ -1035,7 +1100,7 @@ def usergroup_update(usrgrpid, **connection_args): def usergroup_list(**connection_args): - ''' + """ Retrieve all enabled user groups. .. versionadded:: 2016.3.0 @@ -1050,15 +1115,17 @@ def usergroup_list(**connection_args): .. code-block:: bash salt '*' zabbix.usergroup_list - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'usergroup.get' - params = {"output": "extend", } - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + method = "usergroup.get" + params = { + "output": "extend", + } + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: @@ -1066,7 +1133,7 @@ def usergroup_list(**connection_args): def host_create(host, groups, interfaces, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Create new host @@ -1096,12 +1163,12 @@ def host_create(host, groups, interfaces, **connection_args): salt '*' zabbix.host_create technicalname 4 interfaces='{type: 1, main: 1, useip: 1, ip: "192.168.3.1", dns: "", port: 10050}' visible_name='Host Visible Name' inventory_mode=0 inventory='{"alias": "something"}' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'host.create' + method = "host.create" params = {"host": host} # Groups if not isinstance(groups, list): @@ -1109,14 +1176,14 @@ def host_create(host, groups, interfaces, **connection_args): grps = [] for group in groups: grps.append({"groupid": group}) - params['groups'] = grps + params["groups"] = grps # Interfaces if not isinstance(interfaces, list): interfaces = [interfaces] - params['interfaces'] = interfaces + params["interfaces"] = interfaces params = _params_extend(params, _ignore_name=True, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['hostids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["hostids"] else: raise KeyError except KeyError: @@ -1124,7 +1191,7 @@ def host_create(host, groups, interfaces, **connection_args): def host_delete(hostids, **connection_args): - ''' + """ Delete hosts. .. versionadded:: 2016.3.0 @@ -1140,26 +1207,28 @@ def host_delete(hostids, **connection_args): .. code-block:: bash salt '*' zabbix.host_delete 10106 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'host.delete' + method = "host.delete" if not isinstance(hostids, list): params = [hostids] else: params = hostids - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['hostids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["hostids"] else: raise KeyError except KeyError: return ret -def host_exists(host=None, hostid=None, name=None, node=None, nodeids=None, **connection_args): - ''' +def host_exists( + host=None, hostid=None, name=None, node=None, nodeids=None, **connection_args +): + """ Checks if at least one host that matches the given filter criteria exists. .. versionadded:: 2016.3.0 @@ -1179,7 +1248,7 @@ def host_exists(host=None, hostid=None, name=None, node=None, nodeids=None, **co .. code-block:: bash salt '*' zabbix.host_exists 'Zabbix server' - ''' + """ conn_args = _login(**connection_args) zabbix_version = apiinfo_version(**connection_args) ret = False @@ -1197,26 +1266,29 @@ def host_exists(host=None, hostid=None, name=None, node=None, nodeids=None, **co return bool(ret) # zabbix 2.4 nad earlier else: - method = 'host.exists' + method = "host.exists" params = {} if hostid: - params['hostid'] = hostid + params["hostid"] = hostid if host: - params['host'] = host + params["host"] = host if name: - params['name'] = name + params["name"] = name # deprecated in 2.4 if _LooseVersion(zabbix_version) < _LooseVersion("2.4"): if node: - params['node'] = node + params["node"] = node if nodeids: - params['nodeids'] = nodeids + params["nodeids"] = nodeids if not hostid and not host and not name and not node and not nodeids: - return {'result': False, 'comment': 'Please submit hostid, host, name, node or nodeids parameter to' - 'check if at least one host that matches the given filter ' - 'criteria exists.'} - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + return { + "result": False, + "comment": "Please submit hostid, host, name, node or nodeids parameter to" + "check if at least one host that matches the given filter " + "criteria exists.", + } + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: @@ -1224,7 +1296,7 @@ def host_exists(host=None, hostid=None, name=None, node=None, nodeids=None, **co def host_get(host=None, name=None, hostids=None, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Retrieve hosts according to the given parameters @@ -1249,24 +1321,24 @@ def host_get(host=None, name=None, hostids=None, **connection_args): .. code-block:: bash salt '*' zabbix.host_get 'Zabbix server' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'host.get' + method = "host.get" params = {"output": "extend", "filter": {}} if not name and not hostids and not host: return False if name: - params['filter'].setdefault('name', name) + params["filter"].setdefault("name", name) if hostids: - params.setdefault('hostids', hostids) + params.setdefault("hostids", hostids) if host: - params['filter'].setdefault('host', host) + params["filter"].setdefault("host", host) params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] if len(ret['result']) > 0 else False + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] if len(ret["result"]) > 0 else False else: raise KeyError except KeyError: @@ -1274,7 +1346,7 @@ def host_get(host=None, name=None, hostids=None, **connection_args): def host_update(hostid, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Update existing hosts @@ -1302,16 +1374,16 @@ def host_update(hostid, **connection_args): .. code-block:: bash salt '*' zabbix.host_update 10084 name='Zabbix server2' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'host.update' + method = "host.update" params = {"hostid": hostid} params = _params_extend(params, _ignore_name=True, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['hostids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["hostids"] else: raise KeyError except KeyError: @@ -1319,7 +1391,7 @@ def host_update(hostid, **connection_args): def host_inventory_get(hostids, **connection_args): - ''' + """ Retrieve host inventory according to the given parameters. See: https://www.zabbix.com/documentation/2.4/manual/api/reference/host/object#host_inventory @@ -1336,18 +1408,22 @@ def host_inventory_get(hostids, **connection_args): .. code-block:: bash salt '*' zabbix.host_inventory_get 101054 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'host.get' + method = "host.get" params = {"selectInventory": "extend"} if hostids: - params.setdefault('hostids', hostids) + params.setdefault("hostids", hostids) params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'][0]['inventory'] if len(ret['result'][0]['inventory']) > 0 else False + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ( + ret["result"][0]["inventory"] + if len(ret["result"][0]["inventory"]) > 0 + else False + ) else: raise KeyError except KeyError: @@ -1355,7 +1431,7 @@ def host_inventory_get(hostids, **connection_args): def host_inventory_set(hostid, **connection_args): - ''' + """ Update host inventory items NOTE: This function accepts all standard host: keyword argument names for inventory see: https://www.zabbix.com/documentation/2.4/manual/api/reference/host/object#host_inventory @@ -1374,35 +1450,35 @@ def host_inventory_set(hostid, **connection_args): .. code-block:: bash salt '*' zabbix.host_inventory_set 101054 asset_tag=jml3322 type=vm clear_old=True - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: params = {} clear_old = False - method = 'host.update' + method = "host.update" - if connection_args.get('clear_old'): + if connection_args.get("clear_old"): clear_old = True - connection_args.pop('clear_old', None) + connection_args.pop("clear_old", None) inventory_params = dict(_params_extend(params, **connection_args)) for key in inventory_params: params.pop(key, None) if hostid: - params.setdefault('hostid', hostid) + params.setdefault("hostid", hostid) if clear_old: # Set inventory to disabled in order to clear existing data params["inventory_mode"] = "-1" - ret = _query(method, params, conn_args['url'], conn_args['auth']) + ret = _query(method, params, conn_args["url"], conn_args["auth"]) # Set inventory mode to manual in order to submit inventory data - params['inventory_mode'] = "0" - params['inventory'] = inventory_params - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + params["inventory_mode"] = "0" + params["inventory"] = inventory_params + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: @@ -1410,7 +1486,7 @@ def host_inventory_set(hostid, **connection_args): def host_list(**connection_args): - ''' + """ Retrieve all hosts. .. versionadded:: 2016.3.0 @@ -1425,15 +1501,17 @@ def host_list(**connection_args): .. code-block:: bash salt '*' zabbix.host_list - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'host.get' - params = {"output": "extend", } - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + method = "host.get" + params = { + "output": "extend", + } + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: @@ -1441,7 +1519,7 @@ def host_list(**connection_args): def hostgroup_create(name, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Create a host group @@ -1463,16 +1541,16 @@ def hostgroup_create(name, **connection_args): .. code-block:: bash salt '*' zabbix.hostgroup_create MyNewGroup - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'hostgroup.create' + method = "hostgroup.create" params = {"name": name} params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['groupids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["groupids"] else: raise KeyError except KeyError: @@ -1480,7 +1558,7 @@ def hostgroup_create(name, **connection_args): def hostgroup_delete(hostgroupids, **connection_args): - ''' + """ Delete the host group. .. versionadded:: 2016.3.0 @@ -1496,26 +1574,28 @@ def hostgroup_delete(hostgroupids, **connection_args): .. code-block:: bash salt '*' zabbix.hostgroup_delete 23 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'hostgroup.delete' + method = "hostgroup.delete" if not isinstance(hostgroupids, list): params = [hostgroupids] else: params = hostgroupids - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['groupids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["groupids"] else: raise KeyError except KeyError: return ret -def hostgroup_exists(name=None, groupid=None, node=None, nodeids=None, **connection_args): - ''' +def hostgroup_exists( + name=None, groupid=None, node=None, nodeids=None, **connection_args +): + """ Checks if at least one host group that matches the given filter criteria exists. .. versionadded:: 2016.3.0 @@ -1534,7 +1614,7 @@ def hostgroup_exists(name=None, groupid=None, node=None, nodeids=None, **connect .. code-block:: bash salt '*' zabbix.hostgroup_exists MyNewGroup - ''' + """ conn_args = _login(**connection_args) zabbix_version = apiinfo_version(**connection_args) ret = False @@ -1551,23 +1631,26 @@ def hostgroup_exists(name=None, groupid=None, node=None, nodeids=None, **connect # zabbix 2.4 nad earlier else: params = {} - method = 'hostgroup.exists' + method = "hostgroup.exists" if groupid: - params['groupid'] = groupid + params["groupid"] = groupid if name: - params['name'] = name + params["name"] = name # deprecated in 2.4 if _LooseVersion(zabbix_version) < _LooseVersion("2.4"): if node: - params['node'] = node + params["node"] = node if nodeids: - params['nodeids'] = nodeids + params["nodeids"] = nodeids if not groupid and not name and not node and not nodeids: - return {'result': False, 'comment': 'Please submit groupid, name, node or nodeids parameter to' - 'check if at least one host group that matches the given filter' - ' criteria exists.'} - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + return { + "result": False, + "comment": "Please submit groupid, name, node or nodeids parameter to" + "check if at least one host group that matches the given filter" + " criteria exists.", + } + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: @@ -1575,7 +1658,7 @@ def hostgroup_exists(name=None, groupid=None, node=None, nodeids=None, **connect def hostgroup_get(name=None, groupids=None, hostids=None, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Retrieve host groups according to the given parameters @@ -1602,25 +1685,25 @@ def hostgroup_get(name=None, groupids=None, hostids=None, **connection_args): .. code-block:: bash salt '*' zabbix.hostgroup_get MyNewGroup - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'hostgroup.get' + method = "hostgroup.get" params = {"output": "extend"} if not groupids and not name and not hostids: return False if name: name_dict = {"name": name} - params.setdefault('filter', name_dict) + params.setdefault("filter", name_dict) if groupids: - params.setdefault('groupids', groupids) + params.setdefault("groupids", groupids) if hostids: - params.setdefault('hostids', hostids) + params.setdefault("hostids", hostids) params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] if len(ret['result']) > 0 else False + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] if len(ret["result"]) > 0 else False else: raise KeyError except KeyError: @@ -1628,7 +1711,7 @@ def hostgroup_get(name=None, groupids=None, hostids=None, **connection_args): def hostgroup_update(groupid, name=None, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Update existing hosts group @@ -1651,18 +1734,18 @@ def hostgroup_update(groupid, name=None, **connection_args): .. code-block:: bash salt '*' zabbix.hostgroup_update 24 name='Renamed Name' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'hostgroup.update' + method = "hostgroup.update" params = {"groupid": groupid} if name: - params['name'] = name + params["name"] = name params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['groupids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["groupids"] else: raise KeyError except KeyError: @@ -1670,7 +1753,7 @@ def hostgroup_update(groupid, name=None, **connection_args): def hostgroup_list(**connection_args): - ''' + """ Retrieve all host groups. .. versionadded:: 2016.3.0 @@ -1685,15 +1768,17 @@ def hostgroup_list(**connection_args): .. code-block:: bash salt '*' zabbix.hostgroup_list - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'hostgroup.get' - params = {"output": "extend", } - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] + method = "hostgroup.get" + params = { + "output": "extend", + } + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] else: raise KeyError except KeyError: @@ -1701,7 +1786,7 @@ def hostgroup_list(**connection_args): def hostinterface_get(hostids, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Retrieve host groups according to the given parameters @@ -1727,26 +1812,28 @@ def hostinterface_get(hostids, **connection_args): .. code-block:: bash salt '*' zabbix.hostinterface_get 101054 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'hostinterface.get' + method = "hostinterface.get" params = {"output": "extend"} if hostids: - params.setdefault('hostids', hostids) + params.setdefault("hostids", hostids) params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] if len(ret['result']) > 0 else False + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] if len(ret["result"]) > 0 else False else: raise KeyError except KeyError: return ret -def hostinterface_create(hostid, ip_, dns='', main=1, if_type=1, useip=1, port=None, **connection_args): - ''' +def hostinterface_create( + hostid, ip_, dns="", main=1, if_type=1, useip=1, port=None, **connection_args +): + """ .. versionadded:: 2016.3.0 Create new host interface @@ -1788,7 +1875,7 @@ def hostinterface_create(hostid, ip_, dns='', main=1, if_type=1, useip=1, port=N .. code-block:: bash salt '*' zabbix.hostinterface_create 10105 192.193.194.197 - ''' + """ conn_args = _login(**connection_args) ret = False @@ -1797,17 +1884,19 @@ def hostinterface_create(hostid, ip_, dns='', main=1, if_type=1, useip=1, port=N try: if conn_args: - method = 'hostinterface.create' - params = {"hostid": hostid, - "ip": ip_, - "dns": dns, - "main": main, - "port": port, - "type": if_type, - "useip": useip} + method = "hostinterface.create" + params = { + "hostid": hostid, + "ip": ip_, + "dns": dns, + "main": main, + "port": port, + "type": if_type, + "useip": useip, + } params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['interfaceids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["interfaceids"] else: raise KeyError except KeyError: @@ -1815,7 +1904,7 @@ def hostinterface_create(hostid, ip_, dns='', main=1, if_type=1, useip=1, port=N def hostinterface_delete(interfaceids, **connection_args): - ''' + """ Delete host interface .. versionadded:: 2016.3.0 @@ -1831,18 +1920,18 @@ def hostinterface_delete(interfaceids, **connection_args): .. code-block:: bash salt '*' zabbix.hostinterface_delete 50 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'hostinterface.delete' + method = "hostinterface.delete" if isinstance(interfaceids, list): params = interfaceids else: params = [interfaceids] - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['interfaceids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["interfaceids"] else: raise KeyError except KeyError: @@ -1850,7 +1939,7 @@ def hostinterface_delete(interfaceids, **connection_args): def hostinterface_update(interfaceid, **connection_args): - ''' + """ .. versionadded:: 2016.3.0 Update host interface @@ -1875,25 +1964,32 @@ def hostinterface_update(interfaceid, **connection_args): .. code-block:: bash salt '*' zabbix.hostinterface_update 6 ip_=0.0.0.2 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'hostinterface.update' + method = "hostinterface.update" params = {"interfaceid": interfaceid} params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['interfaceids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["interfaceids"] else: raise KeyError except KeyError: return ret -def usermacro_get(macro=None, hostids=None, templateids=None, hostmacroids=None, - globalmacroids=None, globalmacro=False, **connection_args): - ''' +def usermacro_get( + macro=None, + hostids=None, + templateids=None, + hostmacroids=None, + globalmacroids=None, + globalmacro=False, + **connection_args +): + """ Retrieve user macros according to the given parameters. Args: @@ -1917,34 +2013,34 @@ def usermacro_get(macro=None, hostids=None, templateids=None, hostmacroids=None, .. code-block:: bash salt '*' zabbix.usermacro_get macro='{$SNMP_COMMUNITY}' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'usermacro.get' + method = "usermacro.get" params = {"output": "extend", "filter": {}} if macro: # Python mistakenly interprets macro names starting and ending with '{' and '}' as a dict if isinstance(macro, dict): - macro = "{" + six.text_type(macro.keys()[0]) +"}" - if not macro.startswith('{') and not macro.endswith('}'): + macro = "{" + six.text_type(macro.keys()[0]) + "}" + if not macro.startswith("{") and not macro.endswith("}"): macro = "{" + macro + "}" - params['filter'].setdefault('macro', macro) + params["filter"].setdefault("macro", macro) if hostids: - params.setdefault('hostids', hostids) + params.setdefault("hostids", hostids) elif templateids: - params.setdefault('templateids', hostids) + params.setdefault("templateids", hostids) if hostmacroids: - params.setdefault('hostmacroids', hostmacroids) + params.setdefault("hostmacroids", hostmacroids) elif globalmacroids: globalmacro = True - params.setdefault('globalmacroids', globalmacroids) + params.setdefault("globalmacroids", globalmacroids) if globalmacro: params = _params_extend(params, globalmacro=True) params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] if len(ret['result']) > 0 else False + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] if len(ret["result"]) > 0 else False else: raise KeyError except KeyError: @@ -1952,7 +2048,7 @@ def usermacro_get(macro=None, hostids=None, templateids=None, hostmacroids=None, def usermacro_create(macro, value, hostid, **connection_args): - ''' + """ Create new host usermacro. :param macro: name of the host usermacro @@ -1969,25 +2065,25 @@ def usermacro_create(macro, value, hostid, **connection_args): .. code-block:: bash salt '*' zabbix.usermacro_create '{$SNMP_COMMUNITY}' 'public' 1 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: params = {} - method = 'usermacro.create' + method = "usermacro.create" if macro: # Python mistakenly interprets macro names starting and ending with '{' and '}' as a dict if isinstance(macro, dict): - macro = "{" + six.text_type(macro.keys()[0]) +"}" - if not macro.startswith('{') and not macro.endswith('}'): + macro = "{" + six.text_type(macro.keys()[0]) + "}" + if not macro.startswith("{") and not macro.endswith("}"): macro = "{" + macro + "}" - params['macro'] = macro - params['value'] = value - params['hostid'] = hostid + params["macro"] = macro + params["value"] = value + params["hostid"] = hostid params = _params_extend(params, _ignore_name=True, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['hostmacroids'][0] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["hostmacroids"][0] else: raise KeyError except KeyError: @@ -1995,7 +2091,7 @@ def usermacro_create(macro, value, hostid, **connection_args): def usermacro_createglobal(macro, value, **connection_args): - ''' + """ Create new global usermacro. :param macro: name of the global usermacro @@ -2011,24 +2107,24 @@ def usermacro_createglobal(macro, value, **connection_args): .. code-block:: bash salt '*' zabbix.usermacro_createglobal '{$SNMP_COMMUNITY}' 'public' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: params = {} - method = 'usermacro.createglobal' + method = "usermacro.createglobal" if macro: # Python mistakenly interprets macro names starting and ending with '{' and '}' as a dict if isinstance(macro, dict): - macro = "{" + six.text_type(macro.keys()[0]) +"}" - if not macro.startswith('{') and not macro.endswith('}'): + macro = "{" + six.text_type(macro.keys()[0]) + "}" + if not macro.startswith("{") and not macro.endswith("}"): macro = "{" + macro + "}" - params['macro'] = macro - params['value'] = value + params["macro"] = macro + params["value"] = value params = _params_extend(params, _ignore_name=True, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['globalmacroids'][0] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["globalmacroids"][0] else: raise KeyError except KeyError: @@ -2036,7 +2132,7 @@ def usermacro_createglobal(macro, value, **connection_args): def usermacro_delete(macroids, **connection_args): - ''' + """ Delete host usermacros. :param macroids: macroids of the host usermacros @@ -2052,18 +2148,18 @@ def usermacro_delete(macroids, **connection_args): .. code-block:: bash salt '*' zabbix.usermacro_delete 21 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'usermacro.delete' + method = "usermacro.delete" if isinstance(macroids, list): params = macroids else: params = [macroids] - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['hostmacroids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["hostmacroids"] else: raise KeyError except KeyError: @@ -2071,7 +2167,7 @@ def usermacro_delete(macroids, **connection_args): def usermacro_deleteglobal(macroids, **connection_args): - ''' + """ Delete global usermacros. :param macroids: macroids of the global usermacros @@ -2087,18 +2183,18 @@ def usermacro_deleteglobal(macroids, **connection_args): .. code-block:: bash salt '*' zabbix.usermacro_deleteglobal 21 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'usermacro.deleteglobal' + method = "usermacro.deleteglobal" if isinstance(macroids, list): params = macroids else: params = [macroids] - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['globalmacroids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["globalmacroids"] else: raise KeyError except KeyError: @@ -2106,7 +2202,7 @@ def usermacro_deleteglobal(macroids, **connection_args): def usermacro_update(hostmacroid, value, **connection_args): - ''' + """ Update existing host usermacro. :param hostmacroid: id of the host usermacro @@ -2122,18 +2218,18 @@ def usermacro_update(hostmacroid, value, **connection_args): .. code-block:: bash salt '*' zabbix.usermacro_update 1 'public' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: params = {} - method = 'usermacro.update' - params['hostmacroid'] = hostmacroid - params['value'] = value + method = "usermacro.update" + params["hostmacroid"] = hostmacroid + params["value"] = value params = _params_extend(params, _ignore_name=True, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['hostmacroids'][0] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["hostmacroids"][0] else: raise KeyError except KeyError: @@ -2141,7 +2237,7 @@ def usermacro_update(hostmacroid, value, **connection_args): def usermacro_updateglobal(globalmacroid, value, **connection_args): - ''' + """ Update existing global usermacro. :param globalmacroid: id of the host usermacro @@ -2157,18 +2253,18 @@ def usermacro_updateglobal(globalmacroid, value, **connection_args): .. code-block:: bash salt '*' zabbix.usermacro_updateglobal 1 'public' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: params = {} - method = 'usermacro.updateglobal' - params['globalmacroid'] = globalmacroid - params['value'] = value + method = "usermacro.updateglobal" + params["globalmacroid"] = globalmacroid + params["value"] = value params = _params_extend(params, _ignore_name=True, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['globalmacroids'][0] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["globalmacroids"][0] else: raise KeyError except KeyError: @@ -2176,7 +2272,7 @@ def usermacro_updateglobal(globalmacroid, value, **connection_args): def mediatype_get(name=None, mediatypeids=None, **connection_args): - ''' + """ Retrieve mediatypes according to the given parameters. Args: @@ -2200,20 +2296,20 @@ def mediatype_get(name=None, mediatypeids=None, **connection_args): salt '*' zabbix.mediatype_get name='Email' salt '*' zabbix.mediatype_get mediatypeids="['1', '2', '3']" - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'mediatype.get' + method = "mediatype.get" params = {"output": "extend", "filter": {}} if name: - params['filter'].setdefault('description', name) + params["filter"].setdefault("description", name) if mediatypeids: - params.setdefault('mediatypeids', mediatypeids) + params.setdefault("mediatypeids", mediatypeids) params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] if len(ret['result']) > 0 else False + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] if len(ret["result"]) > 0 else False else: raise KeyError except KeyError: @@ -2221,7 +2317,7 @@ def mediatype_get(name=None, mediatypeids=None, **connection_args): def mediatype_create(name, mediatype, **connection_args): - ''' + """ Create new mediatype .. note:: @@ -2251,17 +2347,17 @@ def mediatype_create(name, mediatype, **connection_args): salt '*' zabbix.mediatype_create 'Email' 0 smtp_email='noreply@example.com' smtp_server='mailserver.example.com' smtp_helo='zabbix.example.com' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'mediatype.create' + method = "mediatype.create" params = {"description": name} - params['type'] = mediatype + params["type"] = mediatype params = _params_extend(params, _ignore_name=True, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['mediatypeid'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["mediatypeid"] else: raise KeyError except KeyError: @@ -2269,7 +2365,7 @@ def mediatype_create(name, mediatype, **connection_args): def mediatype_delete(mediatypeids, **connection_args): - ''' + """ Delete mediatype @@ -2284,18 +2380,18 @@ def mediatype_delete(mediatypeids, **connection_args): .. code-block:: bash salt '*' zabbix.mediatype_delete 3 - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'mediatype.delete' + method = "mediatype.delete" if isinstance(mediatypeids, list): params = mediatypeids else: params = [mediatypeids] - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['mediatypeids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["mediatypeids"] else: raise KeyError except KeyError: @@ -2303,7 +2399,7 @@ def mediatype_delete(mediatypeids, **connection_args): def mediatype_update(mediatypeid, name=False, mediatype=False, **connection_args): - ''' + """ Update existing mediatype .. note:: @@ -2323,20 +2419,20 @@ def mediatype_update(mediatypeid, name=False, mediatype=False, **connection_args .. code-block:: bash salt '*' zabbix.usergroup_update 8 name="Email update" - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'mediatype.update' + method = "mediatype.update" params = {"mediatypeid": mediatypeid} if name: - params['description'] = name + params["description"] = name if mediatype: - params['type'] = mediatype + params["type"] = mediatype params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result']['mediatypeids'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"]["mediatypeids"] else: raise KeyError except KeyError: @@ -2344,7 +2440,7 @@ def mediatype_update(mediatypeid, name=False, mediatype=False, **connection_args def template_get(name=None, host=None, templateids=None, **connection_args): - ''' + """ Retrieve templates according to the given parameters. Args: @@ -2369,22 +2465,22 @@ def template_get(name=None, host=None, templateids=None, **connection_args): salt '*' zabbix.template_get name='Template OS Linux' salt '*' zabbix.template_get templateids="['10050', '10001']" - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: - method = 'template.get' + method = "template.get" params = {"output": "extend", "filter": {}} if name: - params['filter'].setdefault('name', name) + params["filter"].setdefault("name", name) if host: - params['filter'].setdefault('host', host) + params["filter"].setdefault("host", host) if templateids: - params.setdefault('templateids', templateids) + params.setdefault("templateids", templateids) params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - return ret['result'] if len(ret['result']) > 0 else False + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + return ret["result"] if len(ret["result"]) > 0 else False else: raise KeyError except KeyError: @@ -2392,7 +2488,7 @@ def template_get(name=None, host=None, templateids=None, **connection_args): def run_query(method, params, **connection_args): - ''' + """ Send Zabbix API call Args: @@ -2415,17 +2511,17 @@ def run_query(method, params, **connection_args): .. code-block:: bash salt '*' zabbix.run_query proxy.create '{"host": "zabbixproxy.domain.com", "status": "5"}' - ''' + """ conn_args = _login(**connection_args) ret = False try: if conn_args: params = _params_extend(params, **connection_args) - ret = _query(method, params, conn_args['url'], conn_args['auth']) - if isinstance(ret['result'], bool): - return ret['result'] - if ret['result'] is True or len(ret['result']) > 0: - return ret['result'] + ret = _query(method, params, conn_args["url"], conn_args["auth"]) + if isinstance(ret["result"], bool): + return ret["result"] + if ret["result"] is True or len(ret["result"]) > 0: + return ret["result"] else: return False else: @@ -2434,8 +2530,8 @@ def run_query(method, params, **connection_args): return ret -def configuration_import(config_file, rules=None, file_format='xml', **connection_args): - ''' +def configuration_import(config_file, rules=None, file_format="xml", **connection_args): + """ .. versionadded:: 2017.7 Imports Zabbix configuration specified in file to Zabbix server. @@ -2453,23 +2549,49 @@ def configuration_import(config_file, rules=None, file_format='xml', **connectio salt '*' zabbix.configuration_import salt://zabbix/config/zabbix_templates.xml \ "{'screens': {'createMissing': True, 'updateExisting': True}}" - ''' + """ if rules is None: rules = {} - default_rules = {'applications': {'createMissing': True, 'updateExisting': False, 'deleteMissing': False}, - 'discoveryRules': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False}, - 'graphs': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False}, - 'groups': {'createMissing': True}, - 'hosts': {'createMissing': False, 'updateExisting': False}, - 'images': {'createMissing': False, 'updateExisting': False}, - 'items': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False}, - 'maps': {'createMissing': False, 'updateExisting': False}, - 'screens': {'createMissing': False, 'updateExisting': False}, - 'templateLinkage': {'createMissing': True}, - 'templates': {'createMissing': True, 'updateExisting': True}, - 'templateScreens': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False}, - 'triggers': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False}, - 'valueMaps': {'createMissing': True, 'updateExisting': False}} + default_rules = { + "applications": { + "createMissing": True, + "updateExisting": False, + "deleteMissing": False, + }, + "discoveryRules": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": False, + }, + "graphs": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": False, + }, + "groups": {"createMissing": True}, + "hosts": {"createMissing": False, "updateExisting": False}, + "images": {"createMissing": False, "updateExisting": False}, + "items": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": False, + }, + "maps": {"createMissing": False, "updateExisting": False}, + "screens": {"createMissing": False, "updateExisting": False}, + "templateLinkage": {"createMissing": True}, + "templates": {"createMissing": True, "updateExisting": True}, + "templateScreens": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": False, + }, + "triggers": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": False, + }, + "valueMaps": {"createMissing": True, "updateExisting": False}, + } new_rules = dict(default_rules) if rules: @@ -2478,29 +2600,39 @@ def configuration_import(config_file, rules=None, file_format='xml', **connectio new_rules[rule].update(rules[rule]) else: new_rules[rule] = rules[rule] - if 'salt://' in config_file: + if "salt://" in config_file: tmpfile = salt.utils.files.mkstemp() - cfile = __salt__['cp.get_file'](config_file, tmpfile) + cfile = __salt__["cp.get_file"](config_file, tmpfile) if not cfile or os.path.getsize(cfile) == 0: - return {'name': config_file, 'result': False, 'message': 'Failed to fetch config file.'} + return { + "name": config_file, + "result": False, + "message": "Failed to fetch config file.", + } else: cfile = config_file if not os.path.isfile(cfile): - return {'name': config_file, 'result': False, 'message': 'Invalid file path.'} + return { + "name": config_file, + "result": False, + "message": "Invalid file path.", + } - with salt.utils.files.fopen(cfile, mode='r') as fp_: + with salt.utils.files.fopen(cfile, mode="r") as fp_: xml = fp_.read() - if 'salt://' in config_file: + if "salt://" in config_file: salt.utils.files.safe_rm(cfile) - params = {'format': file_format, - 'rules': new_rules, - 'source': xml} - log.info('CONFIGURATION IMPORT: rules: %s', six.text_type(params['rules'])) + params = {"format": file_format, "rules": new_rules, "source": xml} + log.info("CONFIGURATION IMPORT: rules: %s", six.text_type(params["rules"])) try: - run_query('configuration.import', params, **connection_args) - return {'name': config_file, 'result': True, 'message': 'Zabbix API "configuration.import" method ' - 'called successfully.'} + run_query("configuration.import", params, **connection_args) + return { + "name": config_file, + "result": True, + "message": 'Zabbix API "configuration.import" method ' + "called successfully.", + } except SaltException as exc: - return {'name': config_file, 'result': False, 'message': six.text_type(exc)} + return {"name": config_file, "result": False, "message": six.text_type(exc)} diff --git a/salt/modules/zcbuildout.py b/salt/modules/zcbuildout.py index 24d462d99be..2e88a840475 100644 --- a/salt/modules/zcbuildout.py +++ b/salt/modules/zcbuildout.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of zc.buildout .. versionadded:: 2014.1.0 @@ -21,24 +21,17 @@ You have those following methods: * bootstrap * run_buildout * buildout -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import copy +import logging import os import re -import logging import sys import traceback -import copy - -# Import 3rd-party libs -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext import six -from salt.ext.six.moves import range -from salt.ext.six.moves.urllib.request import urlopen as _urlopen -# pylint: enable=import-error,no-name-in-module,redefined-builtin # Import salt libs import salt.utils.files @@ -46,36 +39,44 @@ import salt.utils.path import salt.utils.stringutils from salt.exceptions import CommandExecutionError +# Import 3rd-party libs +# pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext import six +from salt.ext.six.moves import range +from salt.ext.six.moves.urllib.request import urlopen as _urlopen -INVALID_RESPONSE = 'We did not get any expectable answer from buildout' -VALID_RESPONSE = '' +# pylint: enable=import-error,no-name-in-module,redefined-builtin + + +INVALID_RESPONSE = "We did not get any expectable answer from buildout" +VALID_RESPONSE = "" NOTSET = object() -HR = '{0}\n'.format('-' * 80) +HR = "{0}\n".format("-" * 80) RE_F = re.S | re.M | re.U BASE_STATUS = { - 'status': None, - 'logs': [], - 'comment': '', - 'out': None, - 'logs_by_level': {}, - 'outlog': None, - 'outlog_by_level': None, + "status": None, + "logs": [], + "comment": "", + "out": None, + "logs_by_level": {}, + "outlog": None, + "outlog_by_level": None, } _URL_VERSIONS = { - 1: 'http://downloads.buildout.org/1/bootstrap.py', - 2: 'http://downloads.buildout.org/2/bootstrap.py', + 1: "http://downloads.buildout.org/1/bootstrap.py", + 2: "http://downloads.buildout.org/2/bootstrap.py", } DEFAULT_VER = 2 _logger = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'buildout' +__virtualname__ = "buildout" def __virtual__(): - ''' + """ Only load if buildout libs are present - ''' + """ return __virtualname__ @@ -86,58 +87,58 @@ def _salt_callback(func, **kwargs): # cleanup the module kwargs before calling it from the # decorator kw = copy.deepcopy(kw) - for k in [ar for ar in kw if '__pub' in ar]: + for k in [ar for ar in kw if "__pub" in ar]: kw.pop(k, None) st = BASE_STATUS.copy() - directory = kw.get('directory', '.') - onlyif = kw.get('onlyif', None) - unless = kw.get('unless', None) - runas = kw.get('runas', None) - env = kw.get('env', ()) + directory = kw.get("directory", ".") + onlyif = kw.get("onlyif", None) + unless = kw.get("unless", None) + runas = kw.get("runas", None) + env = kw.get("env", ()) status = BASE_STATUS.copy() try: # may rise _ResultTransmission - status = _check_onlyif_unless(onlyif, - unless, - directory=directory, - runas=runas, - env=env) + status = _check_onlyif_unless( + onlyif, unless, directory=directory, runas=runas, env=env + ) # if onlyif/unless returns, we are done if status is None: status = BASE_STATUS.copy() - comment, st = '', True + comment, st = "", True out = func(*a, **kw) # we may have already final statuses not to be touched # merged_statuses flag is there to check that ! if not isinstance(out, dict): status = _valid(status, out=out) else: - if out.get('merged_statuses', False): + if out.get("merged_statuses", False): status = out else: - status = _set_status(status, - status=out.get('status', True), - comment=out.get('comment', ''), - out=out.get('out', out)) + status = _set_status( + status, + status=out.get("status", True), + comment=out.get("comment", ""), + out=out.get("out", out), + ) except Exception: # pylint: disable=broad-except trace = traceback.format_exc(None) LOG.error(trace) _invalid(status) LOG.clear() # before returning, trying to compact the log output - for k in ['comment', 'out', 'outlog']: + for k in ["comment", "out", "outlog"]: if status[k] and isinstance(status[k], six.string_types): - status[k] = '\n'.join([ - log - for log in status[k].split('\n') - if log.strip()]) + status[k] = "\n".join( + [log for log in status[k].split("\n") if log.strip()] + ) return status + _call_callback.__doc__ = func.__doc__ return _call_callback class _Logger(object): - levels = ('info', 'warn', 'debug', 'error') + levels = ("info", "warn", "debug", "error") def __init__(self): self._msgs = [] @@ -145,23 +146,23 @@ class _Logger(object): def _log(self, level, msg): if not isinstance(msg, six.text_type): - msg = msg.decode('utf-8') + msg = msg.decode("utf-8") if level not in self._by_level: self._by_level[level] = [] self._msgs.append((level, msg)) self._by_level[level].append(msg) def debug(self, msg): - self._log('debug', msg) + self._log("debug", msg) def info(self, msg): - self._log('info', msg) + self._log("info", msg) def error(self, msg): - self._log('error', msg) + self._log("error", msg) def warn(self, msg): - self._log('warn', msg) + self._log("warn", msg) warning = warn @@ -187,87 +188,91 @@ LOG = _Logger() def _encode_status(status): - if status['out'] is None: - status['out'] = None + if status["out"] is None: + status["out"] = None else: - status['out'] = salt.utils.stringutils.to_unicode(status['out']) - status['outlog_by_level'] = salt.utils.stringutils.to_unicode(status['outlog_by_level']) - if status['logs']: - for i, data in enumerate(status['logs'][:]): - status['logs'][i] = (data[0], salt.utils.stringutils.to_unicode(data[1])) - for logger in 'error', 'warn', 'info', 'debug': - logs = status['logs_by_level'].get(logger, [])[:] + status["out"] = salt.utils.stringutils.to_unicode(status["out"]) + status["outlog_by_level"] = salt.utils.stringutils.to_unicode( + status["outlog_by_level"] + ) + if status["logs"]: + for i, data in enumerate(status["logs"][:]): + status["logs"][i] = (data[0], salt.utils.stringutils.to_unicode(data[1])) + for logger in "error", "warn", "info", "debug": + logs = status["logs_by_level"].get(logger, [])[:] if logs: for i, log in enumerate(logs): - status['logs_by_level'][logger][i] = salt.utils.stringutils.to_unicode(log) + status["logs_by_level"][logger][ + i + ] = salt.utils.stringutils.to_unicode(log) return status -def _set_status(m, - comment=INVALID_RESPONSE, - status=False, - out=None): - ''' +def _set_status(m, comment=INVALID_RESPONSE, status=False, out=None): + """ Assign status data to a dict. - ''' - m['out'] = out - m['status'] = status - m['logs'] = LOG.messages[:] - m['logs_by_level'] = LOG.by_level.copy() - outlog, outlog_by_level = '', '' - m['comment'] = comment + """ + m["out"] = out + m["status"] = status + m["logs"] = LOG.messages[:] + m["logs_by_level"] = LOG.by_level.copy() + outlog, outlog_by_level = "", "" + m["comment"] = comment if out and isinstance(out, six.string_types): outlog += HR - outlog += 'OUTPUT:\n' - outlog += '{0}\n'.format(salt.utils.stringutils.to_unicode(out)) + outlog += "OUTPUT:\n" + outlog += "{0}\n".format(salt.utils.stringutils.to_unicode(out)) outlog += HR - if m['logs']: + if m["logs"]: outlog += HR - outlog += 'Log summary:\n' + outlog += "Log summary:\n" outlog += HR outlog_by_level += HR - outlog_by_level += 'Log summary by level:\n' + outlog_by_level += "Log summary by level:\n" outlog_by_level += HR - for level, msg in m['logs']: - outlog += '\n{0}: {1}\n'.format(level.upper(), - salt.utils.stringutils.to_unicode(msg)) - for logger in 'error', 'warn', 'info', 'debug': - logs = m['logs_by_level'].get(logger, []) + for level, msg in m["logs"]: + outlog += "\n{0}: {1}\n".format( + level.upper(), salt.utils.stringutils.to_unicode(msg) + ) + for logger in "error", "warn", "info", "debug": + logs = m["logs_by_level"].get(logger, []) if logs: - outlog_by_level += '\n{0}:\n'.format(logger.upper()) + outlog_by_level += "\n{0}:\n".format(logger.upper()) for idx, log in enumerate(logs[:]): logs[idx] = salt.utils.stringutils.to_unicode(log) - outlog_by_level += '\n'.join(logs) - outlog_by_level += '\n' + outlog_by_level += "\n".join(logs) + outlog_by_level += "\n" outlog += HR - m['outlog'] = outlog - m['outlog_by_level'] = outlog_by_level + m["outlog"] = outlog + m["outlog_by_level"] = outlog_by_level return _encode_status(m) def _invalid(m, comment=INVALID_RESPONSE, out=None): - ''' + """ Return invalid status. - ''' + """ return _set_status(m, status=False, comment=comment, out=out) def _valid(m, comment=VALID_RESPONSE, out=None): - ''' + """ Return valid status. - ''' + """ return _set_status(m, status=True, comment=comment, out=out) -def _Popen(command, - output=False, - directory='.', - runas=None, - env=(), - exitcode=0, - use_vt=False, - loglevel=None): - ''' +def _Popen( + command, + output=False, + directory=".", + runas=None, + env=(), + exitcode=0, + use_vt=False, + loglevel=None, +): + """ Run a command. output @@ -289,42 +294,50 @@ def _Popen(command, use_vt Use the new salt VT to stream output [experimental] - ''' + """ ret = None directory = os.path.abspath(directory) if isinstance(command, list): - command = ' '.join(command) - LOG.debug('Running {0}'.format(command)) # pylint: disable=str-format-in-logging + command = " ".join(command) + LOG.debug("Running {0}".format(command)) # pylint: disable=str-format-in-logging if not loglevel: - loglevel = 'debug' - ret = __salt__['cmd.run_all']( - command, cwd=directory, output_loglevel=loglevel, - runas=runas, env=env, use_vt=use_vt, python_shell=False) - out = ret['stdout'] + '\n\n' + ret['stderr'] - if (exitcode is not None) and (ret['retcode'] != exitcode): + loglevel = "debug" + ret = __salt__["cmd.run_all"]( + command, + cwd=directory, + output_loglevel=loglevel, + runas=runas, + env=env, + use_vt=use_vt, + python_shell=False, + ) + out = ret["stdout"] + "\n\n" + ret["stderr"] + if (exitcode is not None) and (ret["retcode"] != exitcode): raise _BuildoutError(out) - ret['output'] = out + ret["output"] = out if output: ret = out return ret class _BuildoutError(CommandExecutionError): - ''' + """ General Buildout Error. - ''' + """ def _has_old_distribute(python=sys.executable, runas=None, env=()): old_distribute = False try: - cmd = [python, - '-c', - '\'import pkg_resources;' - 'print pkg_resources.' - 'get_distribution(\"distribute\").location\''] + cmd = [ + python, + "-c", + "'import pkg_resources;" + "print pkg_resources." + 'get_distribution("distribute").location\'', + ] ret = _Popen(cmd, runas=runas, env=env, output=True) - if 'distribute-0.6' in ret: + if "distribute-0.6" in ret: old_distribute = True except Exception: # pylint: disable=broad-except old_distribute = False @@ -334,13 +347,15 @@ def _has_old_distribute(python=sys.executable, runas=None, env=()): def _has_setuptools7(python=sys.executable, runas=None, env=()): new_st = False try: - cmd = [python, - '-c', - '\'import pkg_resources;' - 'print not pkg_resources.' - 'get_distribution("setuptools").version.startswith("0.6")\''] + cmd = [ + python, + "-c", + "'import pkg_resources;" + "print not pkg_resources." + 'get_distribution("setuptools").version.startswith("0.6")\'', + ] ret = _Popen(cmd, runas=runas, env=env, output=True) - if 'true' in ret.lower(): + if "true" in ret.lower(): new_st = True except Exception: # pylint: disable=broad-except new_st = False @@ -348,7 +363,7 @@ def _has_setuptools7(python=sys.executable, runas=None, env=()): def _find_cfgs(path, cfgs=None): - ''' + """ Find all buildout configs in a subdirectory. only buildout.cfg and etc/buildout.cfg are valid in:: @@ -366,43 +381,41 @@ def _find_cfgs(path, cfgs=None): │   └── buildout.cfg └── var └── buildout.cfg - ''' - ignored = ['var', 'parts'] + """ + ignored = ["var", "parts"] dirs = [] if not cfgs: cfgs = [] for i in os.listdir(path): fi = os.path.join(path, i) - if fi.endswith('.cfg') and os.path.isfile(fi): + if fi.endswith(".cfg") and os.path.isfile(fi): cfgs.append(fi) if os.path.isdir(fi) and (i not in ignored): dirs.append(fi) for fpath in dirs: for p, ids, ifs in salt.utils.path.os_walk(fpath): for i in ifs: - if i.endswith('.cfg'): + if i.endswith(".cfg"): cfgs.append(os.path.join(p, i)) return cfgs -def _get_bootstrap_content(directory='.'): - ''' +def _get_bootstrap_content(directory="."): + """ Get the current bootstrap.py script content - ''' + """ try: - with salt.utils.files.fopen(os.path.join( - os.path.abspath(directory), - 'bootstrap.py')) as fic: - oldcontent = salt.utils.stringutils.to_unicode( - fic.read() - ) + with salt.utils.files.fopen( + os.path.join(os.path.abspath(directory), "bootstrap.py") + ) as fic: + oldcontent = salt.utils.stringutils.to_unicode(fic.read()) except (OSError, IOError): - oldcontent = '' + oldcontent = "" return oldcontent -def _get_buildout_ver(directory='.'): - '''Check for buildout versions. +def _get_buildout_ver(directory="."): + """Check for buildout versions. In any cases, check for a version pinning Also check for buildout.dumppickedversions which is buildout1 specific @@ -411,26 +424,22 @@ def _get_buildout_ver(directory='.'): directory directory to execute in - ''' + """ directory = os.path.abspath(directory) buildoutver = 2 try: files = _find_cfgs(directory) for f in files: with salt.utils.files.fopen(f) as fic: - buildout1re = re.compile(r'^zc\.buildout\s*=\s*1', RE_F) + buildout1re = re.compile(r"^zc\.buildout\s*=\s*1", RE_F) dfic = salt.utils.stringutils.to_unicode(fic.read()) - if ( - ('buildout.dumppick' in dfic) - or - (buildout1re.search(dfic)) - ): + if ("buildout.dumppick" in dfic) or (buildout1re.search(dfic)): buildoutver = 1 bcontent = _get_bootstrap_content(directory) if ( - '--download-base' in bcontent - or '--setup-source' in bcontent - or '--distribute' in bcontent + "--download-base" in bcontent + or "--setup-source" in bcontent + or "--distribute" in bcontent ): buildoutver = 1 except (OSError, IOError): @@ -439,37 +448,38 @@ def _get_buildout_ver(directory='.'): def _get_bootstrap_url(directory): - ''' + """ Get the most appropriate download URL for the bootstrap script. directory directory to execute in - ''' + """ v = _get_buildout_ver(directory) return _URL_VERSIONS.get(v, _URL_VERSIONS[DEFAULT_VER]) def _dot_buildout(directory): - ''' + """ Get the local marker directory. directory directory to execute in - ''' - return os.path.join( - os.path.abspath(directory), '.buildout') + """ + return os.path.join(os.path.abspath(directory), ".buildout") @_salt_callback -def upgrade_bootstrap(directory='.', - onlyif=None, - unless=None, - runas=None, - env=(), - offline=False, - buildout_ver=None): - ''' +def upgrade_bootstrap( + directory=".", + onlyif=None, + unless=None, + runas=None, + env=(), + offline=False, + buildout_ver=None, +): + """ Upgrade current bootstrap.py with the last released one. Indeed, when we first run a buildout, a common source of problem @@ -495,19 +505,19 @@ def upgrade_bootstrap(directory='.', .. code-block:: bash salt '*' buildout.upgrade_bootstrap /srv/mybuildout - ''' + """ if buildout_ver: booturl = _URL_VERSIONS[buildout_ver] else: buildout_ver = _get_buildout_ver(directory) booturl = _get_bootstrap_url(directory) - LOG.debug('Using {0}'.format(booturl)) # pylint: disable=str-format-in-logging + LOG.debug("Using {0}".format(booturl)) # pylint: disable=str-format-in-logging # try to download an up-to-date bootstrap # set defaulttimeout # and add possible content directory = os.path.abspath(directory) - b_py = os.path.join(directory, 'bootstrap.py') - comment = '' + b_py = os.path.join(directory, "bootstrap.py") + comment = "" try: oldcontent = _get_bootstrap_content(directory) dbuild = _dot_buildout(directory) @@ -519,53 +529,55 @@ def upgrade_bootstrap(directory='.', if not os.path.isdir(dbuild): os.makedirs(dbuild) # only try to download once per buildout checkout - with salt.utils.files.fopen(os.path.join( - dbuild, - '{0}.updated_bootstrap'.format(buildout_ver))): + with salt.utils.files.fopen( + os.path.join(dbuild, "{0}.updated_bootstrap".format(buildout_ver)) + ): pass except (OSError, IOError): - LOG.info('Bootstrap updated from repository') + LOG.info("Bootstrap updated from repository") data = _urlopen(booturl).read() updated = True dled = True - if 'socket.setdefaulttimeout' not in data: + if "socket.setdefaulttimeout" not in data: updated = True ldata = data.splitlines() - ldata.insert(1, 'import socket;socket.setdefaulttimeout(2)') - data = '\n'.join(ldata) + ldata.insert(1, "import socket;socket.setdefaulttimeout(2)") + data = "\n".join(ldata) if updated: - comment = 'Bootstrap updated' - with salt.utils.files.fopen(b_py, 'w') as fic: + comment = "Bootstrap updated" + with salt.utils.files.fopen(b_py, "w") as fic: fic.write(salt.utils.stringutils.to_str(data)) if dled: - with salt.utils.files.fopen(os.path.join(dbuild, - '{0}.updated_bootstrap'.format( - buildout_ver)), 'w') as afic: - afic.write('foo') + with salt.utils.files.fopen( + os.path.join(dbuild, "{0}.updated_bootstrap".format(buildout_ver)), "w" + ) as afic: + afic.write("foo") except (OSError, IOError): if oldcontent: - with salt.utils.files.fopen(b_py, 'w') as fic: + with salt.utils.files.fopen(b_py, "w") as fic: fic.write(salt.utils.stringutils.to_str(oldcontent)) - return {'comment': comment} + return {"comment": comment} @_salt_callback -def bootstrap(directory='.', - config='buildout.cfg', - python=sys.executable, - onlyif=None, - unless=None, - runas=None, - env=(), - distribute=None, - buildout_ver=None, - test_release=False, - offline=False, - new_st=None, - use_vt=False, - loglevel=None): - ''' +def bootstrap( + directory=".", + config="buildout.cfg", + python=sys.executable, + onlyif=None, + unless=None, + runas=None, + env=(), + distribute=None, + buildout_ver=None, + test_release=False, + offline=False, + new_st=None, + use_vt=False, + loglevel=None, +): + """ Run the buildout bootstrap dance (python bootstrap.py). directory @@ -612,109 +624,61 @@ def bootstrap(directory='.', .. code-block:: bash salt '*' buildout.bootstrap /srv/mybuildout - ''' + """ directory = os.path.abspath(directory) dbuild = _dot_buildout(directory) - bootstrap_args = '' + bootstrap_args = "" has_distribute = _has_old_distribute(python=python, runas=runas, env=env) has_new_st = _has_setuptools7(python=python, runas=runas, env=env) - if ( - has_distribute and has_new_st - and not distribute and new_st - ): + if has_distribute and has_new_st and not distribute and new_st: new_st = True distribute = False - if ( - has_distribute and has_new_st - and not distribute and new_st - ): + if has_distribute and has_new_st and not distribute and new_st: new_st = True distribute = False - if ( - has_distribute and has_new_st - and distribute and not new_st - ): + if has_distribute and has_new_st and distribute and not new_st: new_st = True distribute = False - if ( - has_distribute and has_new_st - and not distribute and not new_st - ): + if has_distribute and has_new_st and not distribute and not new_st: new_st = True distribute = False - if ( - not has_distribute and has_new_st - and not distribute and new_st - ): + if not has_distribute and has_new_st and not distribute and new_st: new_st = True distribute = False - if ( - not has_distribute and has_new_st - and not distribute and new_st - ): + if not has_distribute and has_new_st and not distribute and new_st: new_st = True distribute = False - if ( - not has_distribute and has_new_st - and distribute and not new_st - ): + if not has_distribute and has_new_st and distribute and not new_st: new_st = True distribute = False - if ( - not has_distribute and has_new_st - and not distribute and not new_st - ): + if not has_distribute and has_new_st and not distribute and not new_st: new_st = True distribute = False - if ( - has_distribute and not has_new_st - and not distribute and new_st - ): + if has_distribute and not has_new_st and not distribute and new_st: new_st = True distribute = False - if ( - has_distribute and not has_new_st - and not distribute and new_st - ): + if has_distribute and not has_new_st and not distribute and new_st: new_st = True distribute = False - if ( - has_distribute and not has_new_st - and distribute and not new_st - ): + if has_distribute and not has_new_st and distribute and not new_st: new_st = False distribute = True - if ( - has_distribute and not has_new_st - and not distribute and not new_st - ): + if has_distribute and not has_new_st and not distribute and not new_st: new_st = False distribute = True - if ( - not has_distribute and not has_new_st - and not distribute and new_st - ): + if not has_distribute and not has_new_st and not distribute and new_st: new_st = True distribute = False - if ( - not has_distribute and not has_new_st - and not distribute and new_st - ): + if not has_distribute and not has_new_st and not distribute and new_st: new_st = True distribute = False - if ( - not has_distribute and not has_new_st - and distribute and not new_st - ): + if not has_distribute and not has_new_st and distribute and not new_st: new_st = False distribute = True - if ( - not has_distribute and not has_new_st - and not distribute and not new_st - ): + if not has_distribute and not has_new_st and not distribute and not new_st: new_st = True distribute = False @@ -722,61 +686,60 @@ def bootstrap(directory='.', distribute = False if new_st: distribute = False - LOG.warning('Forcing to use setuptools as we have setuptools >= 0.7') + LOG.warning("Forcing to use setuptools as we have setuptools >= 0.7") if distribute: new_st = False if buildout_ver == 1: - LOG.warning('Using distribute !') - bootstrap_args += ' --distribute' + LOG.warning("Using distribute !") + bootstrap_args += " --distribute" if not os.path.isdir(dbuild): os.makedirs(dbuild) - upgrade_bootstrap(directory, - offline=offline, - buildout_ver=buildout_ver) + upgrade_bootstrap(directory, offline=offline, buildout_ver=buildout_ver) # be sure which buildout bootstrap we have - b_py = os.path.join(directory, 'bootstrap.py') + b_py = os.path.join(directory, "bootstrap.py") with salt.utils.files.fopen(b_py) as fic: content = salt.utils.stringutils.to_unicode(fic.read()) - if ( - (test_release is not False) - and ' --accept-buildout-test-releases' in content - ): - bootstrap_args += ' --accept-buildout-test-releases' + if (test_release is not False) and " --accept-buildout-test-releases" in content: + bootstrap_args += " --accept-buildout-test-releases" if config and '"-c"' in content: - bootstrap_args += ' -c {0}'.format(config) + bootstrap_args += " -c {0}".format(config) # be sure that the bootstrap belongs to the running user try: if runas: - uid = __salt__['user.info'](runas)['uid'] - gid = __salt__['user.info'](runas)['gid'] - os.chown('bootstrap.py', uid, gid) + uid = __salt__["user.info"](runas)["uid"] + gid = __salt__["user.info"](runas)["gid"] + os.chown("bootstrap.py", uid, gid) except (IOError, OSError) as exc: # don't block here, try to execute it if can pass - _logger.error('BUILDOUT bootstrap permissions error:' - ' {0}'.format(exc), - exc_info=_logger.isEnabledFor(logging.DEBUG)) - cmd = '{0} bootstrap.py {1}'.format(python, bootstrap_args) - ret = _Popen(cmd, directory=directory, runas=runas, loglevel=loglevel, - env=env, use_vt=use_vt) - output = ret['output'] - return {'comment': cmd, 'out': output} + _logger.error( + "BUILDOUT bootstrap permissions error:" " {0}".format(exc), + exc_info=_logger.isEnabledFor(logging.DEBUG), + ) + cmd = "{0} bootstrap.py {1}".format(python, bootstrap_args) + ret = _Popen( + cmd, directory=directory, runas=runas, loglevel=loglevel, env=env, use_vt=use_vt + ) + output = ret["output"] + return {"comment": cmd, "out": output} @_salt_callback -def run_buildout(directory='.', - config='buildout.cfg', - parts=None, - onlyif=None, - unless=None, - offline=False, - newest=True, - runas=None, - env=(), - verbose=False, - debug=False, - use_vt=False, - loglevel=None): - ''' +def run_buildout( + directory=".", + config="buildout.cfg", + parts=None, + onlyif=None, + unless=None, + offline=False, + newest=True, + runas=None, + env=(), + verbose=False, + debug=False, + use_vt=False, + loglevel=None, +): + """ Run a buildout in a directory. directory @@ -817,125 +780,141 @@ def run_buildout(directory='.', .. code-block:: bash salt '*' buildout.run_buildout /srv/mybuildout - ''' + """ directory = os.path.abspath(directory) - bcmd = os.path.join(directory, 'bin', 'buildout') - installed_cfg = os.path.join(directory, '.installed.cfg') + bcmd = os.path.join(directory, "bin", "buildout") + installed_cfg = os.path.join(directory, ".installed.cfg") argv = [] if verbose: - LOG.debug('Buildout is running in verbose mode!') - argv.append('-vvvvvvv') + LOG.debug("Buildout is running in verbose mode!") + argv.append("-vvvvvvv") if not newest and os.path.exists(installed_cfg): - LOG.debug('Buildout is running in non newest mode!') - argv.append('-N') + LOG.debug("Buildout is running in non newest mode!") + argv.append("-N") if newest: - LOG.debug('Buildout is running in newest mode!') - argv.append('-n') + LOG.debug("Buildout is running in newest mode!") + argv.append("-n") if offline: - LOG.debug('Buildout is running in offline mode!') - argv.append('-o') + LOG.debug("Buildout is running in offline mode!") + argv.append("-o") if debug: - LOG.debug('Buildout is running in debug mode!') - argv.append('-D') + LOG.debug("Buildout is running in debug mode!") + argv.append("-D") cmds, outputs = [], [] if parts: for part in parts: - LOG.info('Installing single part: {0}'.format(part)) # pylint: disable=str-format-in-logging - cmd = '{0} -c {1} {2} install {3}'.format( - bcmd, config, ' '.join(argv), part) + LOG.info( + "Installing single part: {0}".format(part) + ) # pylint: disable=str-format-in-logging + cmd = "{0} -c {1} {2} install {3}".format( + bcmd, config, " ".join(argv), part + ) cmds.append(cmd) outputs.append( _Popen( - cmd, directory=directory, + cmd, + directory=directory, runas=runas, env=env, output=True, loglevel=loglevel, - use_vt=use_vt) + use_vt=use_vt, + ) ) else: - LOG.info('Installing all buildout parts') - cmd = '{0} -c {1} {2}'.format( - bcmd, config, ' '.join(argv)) + LOG.info("Installing all buildout parts") + cmd = "{0} -c {1} {2}".format(bcmd, config, " ".join(argv)) cmds.append(cmd) outputs.append( _Popen( - cmd, directory=directory, runas=runas, loglevel=loglevel, - env=env, output=True, use_vt=use_vt) + cmd, + directory=directory, + runas=runas, + loglevel=loglevel, + env=env, + output=True, + use_vt=use_vt, + ) ) - return {'comment': '\n'.join(cmds), - 'out': '\n'.join(outputs)} + return {"comment": "\n".join(cmds), "out": "\n".join(outputs)} def _merge_statuses(statuses): status = BASE_STATUS.copy() - status['status'] = None - status['merged_statuses'] = True - status['out'] = '' + status["status"] = None + status["merged_statuses"] = True + status["out"] = "" for st in statuses: - if status['status'] is not False: - status['status'] = st['status'] - out = st['out'] - comment = salt.utils.stringutils.to_unicode(st['comment']) - logs = st['logs'] - logs_by_level = st['logs_by_level'] - outlog_by_level = st['outlog_by_level'] - outlog = st['outlog'] + if status["status"] is not False: + status["status"] = st["status"] + out = st["out"] + comment = salt.utils.stringutils.to_unicode(st["comment"]) + logs = st["logs"] + logs_by_level = st["logs_by_level"] + outlog_by_level = st["outlog_by_level"] + outlog = st["outlog"] if out: - if not status['out']: - status['out'] = '' - status['out'] += '\n' - status['out'] += HR + if not status["out"]: + status["out"] = "" + status["out"] += "\n" + status["out"] += HR out = salt.utils.stringutils.to_unicode(out) - status['out'] += '{0}\n'.format(out) - status['out'] += HR + status["out"] += "{0}\n".format(out) + status["out"] += HR if comment: - if not status['comment']: - status['comment'] = '' - status['comment'] += '\n{0}\n'.format( - salt.utils.stringutils.to_unicode(comment)) + if not status["comment"]: + status["comment"] = "" + status["comment"] += "\n{0}\n".format( + salt.utils.stringutils.to_unicode(comment) + ) if outlog: - if not status['outlog']: - status['outlog'] = '' + if not status["outlog"]: + status["outlog"] = "" outlog = salt.utils.stringutils.to_unicode(outlog) - status['outlog'] += '\n{0}'.format(HR) - status['outlog'] += outlog + status["outlog"] += "\n{0}".format(HR) + status["outlog"] += outlog if outlog_by_level: - if not status['outlog_by_level']: - status['outlog_by_level'] = '' - status['outlog_by_level'] += '\n{0}'.format(HR) - status['outlog_by_level'] += salt.utils.stringutils.to_unicode(outlog_by_level) - status['logs'].extend([ - (a[0], salt.utils.stringutils.to_unicode(a[1])) for a in logs]) + if not status["outlog_by_level"]: + status["outlog_by_level"] = "" + status["outlog_by_level"] += "\n{0}".format(HR) + status["outlog_by_level"] += salt.utils.stringutils.to_unicode( + outlog_by_level + ) + status["logs"].extend( + [(a[0], salt.utils.stringutils.to_unicode(a[1])) for a in logs] + ) for log in logs_by_level: - if log not in status['logs_by_level']: - status['logs_by_level'][log] = [] - status['logs_by_level'][log].extend( - [salt.utils.stringutils.to_unicode(a) for a in logs_by_level[log]]) + if log not in status["logs_by_level"]: + status["logs_by_level"][log] = [] + status["logs_by_level"][log].extend( + [salt.utils.stringutils.to_unicode(a) for a in logs_by_level[log]] + ) return _encode_status(status) @_salt_callback -def buildout(directory='.', - config='buildout.cfg', - parts=None, - runas=None, - env=(), - buildout_ver=None, - test_release=False, - distribute=None, - new_st=None, - offline=False, - newest=False, - python=sys.executable, - debug=False, - verbose=False, - onlyif=None, - unless=None, - use_vt=False, - loglevel=None): - ''' +def buildout( + directory=".", + config="buildout.cfg", + parts=None, + runas=None, + env=(), + buildout_ver=None, + test_release=False, + distribute=None, + new_st=None, + offline=False, + newest=False, + python=sys.executable, + debug=False, + verbose=False, + onlyif=None, + unless=None, + use_vt=False, + loglevel=None, +): + """ Run buildout in a directory. directory @@ -993,31 +972,37 @@ def buildout(directory='.', .. code-block:: bash salt '*' buildout.buildout /srv/mybuildout - ''' - LOG.info('Running buildout in {0} ({1})'.format(directory, config)) # pylint: disable=str-format-in-logging - boot_ret = bootstrap(directory, - config=config, - buildout_ver=buildout_ver, - test_release=test_release, - offline=offline, - new_st=new_st, - env=env, - runas=runas, - distribute=distribute, - python=python, - use_vt=use_vt, - loglevel=loglevel) - buildout_ret = run_buildout(directory=directory, - config=config, - parts=parts, - offline=offline, - newest=newest, - runas=runas, - env=env, - verbose=verbose, - debug=debug, - use_vt=use_vt, - loglevel=loglevel) + """ + LOG.info( + "Running buildout in {0} ({1})".format(directory, config) + ) # pylint: disable=str-format-in-logging + boot_ret = bootstrap( + directory, + config=config, + buildout_ver=buildout_ver, + test_release=test_release, + offline=offline, + new_st=new_st, + env=env, + runas=runas, + distribute=distribute, + python=python, + use_vt=use_vt, + loglevel=loglevel, + ) + buildout_ret = run_buildout( + directory=directory, + config=config, + parts=parts, + offline=offline, + newest=newest, + runas=runas, + env=env, + verbose=verbose, + debug=debug, + use_vt=use_vt, + loglevel=loglevel, + ) # signal the decorator or our return return _merge_statuses([boot_ret, buildout_ret]) @@ -1027,24 +1012,30 @@ def _check_onlyif_unless(onlyif, unless, directory, runas=None, env=()): status = BASE_STATUS.copy() if os.path.exists(directory): directory = os.path.abspath(directory) - status['status'] = False - retcode = __salt__['cmd.retcode'] + status["status"] = False + retcode = __salt__["cmd.retcode"] if onlyif is not None: if not isinstance(onlyif, six.string_types): if not onlyif: - _valid(status, 'onlyif condition is false') + _valid(status, "onlyif condition is false") elif isinstance(onlyif, six.string_types): if retcode(onlyif, cwd=directory, runas=runas, env=env) != 0: - _valid(status, 'onlyif condition is false') + _valid(status, "onlyif condition is false") if unless is not None: if not isinstance(unless, six.string_types): if unless: - _valid(status, 'unless condition is true') + _valid(status, "unless condition is true") elif isinstance(unless, six.string_types): - if retcode(unless, cwd=directory, runas=runas, env=env, python_shell=False) == 0: - _valid(status, 'unless condition is true') - if status['status']: + if ( + retcode( + unless, cwd=directory, runas=runas, env=env, python_shell=False + ) + == 0 + ): + _valid(status, "unless condition is true") + if status["status"]: ret = status return ret + # vim:set et sts=4 ts=4 tw=80: diff --git a/salt/modules/zenoss.py b/salt/modules/zenoss.py index 50711f34cd0..9c6b7de7b5f 100644 --- a/salt/modules/zenoss.py +++ b/salt/modules/zenoss.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for working with the Zenoss API .. versionadded:: 2016.3.0 @@ -16,107 +16,112 @@ Module for working with the Zenoss API hostname: https://zenoss.example.com username: admin password: admin123 -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import re + import logging +import re + +import salt.utils.json try: import requests + HAS_LIBS = True except ImportError: HAS_LIBS = False -import salt.utils.json # Disable INFO level logs from requests/urllib3 -urllib3_logger = logging.getLogger('urllib3') +urllib3_logger = logging.getLogger("urllib3") urllib3_logger.setLevel(logging.WARNING) log = logging.getLogger(__name__) -__virtualname__ = 'zenoss' +__virtualname__ = "zenoss" def __virtual__(): - ''' + """ Only load if requests is installed - ''' + """ if HAS_LIBS: return __virtualname__ else: - return False, 'The \'{0}\' module could not be loaded: ' \ - '\'requests\' is not installed.'.format(__virtualname__) + return ( + False, + "The '{0}' module could not be loaded: " + "'requests' is not installed.".format(__virtualname__), + ) -ROUTERS = {'MessagingRouter': 'messaging', - 'EventsRouter': 'evconsole', - 'ProcessRouter': 'process', - 'ServiceRouter': 'service', - 'DeviceRouter': 'device', - 'NetworkRouter': 'network', - 'TemplateRouter': 'template', - 'DetailNavRouter': 'detailnav', - 'ReportRouter': 'report', - 'MibRouter': 'mib', - 'ZenPackRouter': 'zenpack'} +ROUTERS = { + "MessagingRouter": "messaging", + "EventsRouter": "evconsole", + "ProcessRouter": "process", + "ServiceRouter": "service", + "DeviceRouter": "device", + "NetworkRouter": "network", + "TemplateRouter": "template", + "DetailNavRouter": "detailnav", + "ReportRouter": "report", + "MibRouter": "mib", + "ZenPackRouter": "zenpack", +} def _session(): - ''' + """ Create a session to be used when connecting to Zenoss. - ''' + """ - config = __salt__['config.option']('zenoss') + config = __salt__["config.option"]("zenoss") session = requests.session() - session.auth = (config.get('username'), config.get('password')) + session.auth = (config.get("username"), config.get("password")) session.verify = False - session.headers.update({'Content-type': 'application/json; charset=utf-8'}) + session.headers.update({"Content-type": "application/json; charset=utf-8"}) return session def _router_request(router, method, data=None): - ''' + """ Make a request to the Zenoss API router - ''' + """ if router not in ROUTERS: return False - req_data = salt.utils.json.dumps([dict( - action=router, - method=method, - data=data, - type='rpc', - tid=1)]) + req_data = salt.utils.json.dumps( + [dict(action=router, method=method, data=data, type="rpc", tid=1)] + ) - config = __salt__['config.option']('zenoss') - log.debug('Making request to router %s with method %s', router, method) - url = '{0}/zport/dmd/{1}_router'.format(config.get('hostname'), ROUTERS[router]) + config = __salt__["config.option"]("zenoss") + log.debug("Making request to router %s with method %s", router, method) + url = "{0}/zport/dmd/{1}_router".format(config.get("hostname"), ROUTERS[router]) response = _session().post(url, data=req_data) # The API returns a 200 response code even whe auth is bad. # With bad auth, the login page is displayed. Here I search for # an element on the login form to determine if auth failed. if re.search('name="__ac_name"', response.content): - log.error('Request failed. Bad username/password.') - raise Exception('Request failed. Bad username/password.') + log.error("Request failed. Bad username/password.") + raise Exception("Request failed. Bad username/password.") - return salt.utils.json.loads(response.content).get('result', None) + return salt.utils.json.loads(response.content).get("result", None) def _determine_device_class(): - ''' + """ If no device class is given when adding a device, this helps determine - ''' - if __salt__['grains.get']('kernel') == 'Linux': - return '/Server/Linux' + """ + if __salt__["grains.get"]("kernel") == "Linux": + return "/Server/Linux" def find_device(device=None): - ''' + """ Find a device in Zenoss. If device not found, returns None. Parameters: @@ -124,23 +129,23 @@ def find_device(device=None): CLI Example: salt '*' zenoss.find_device - ''' + """ - data = [{'uid': '/zport/dmd/Devices', 'params': {}, 'limit': None}] - all_devices = _router_request('DeviceRouter', 'getDevices', data=data) - for dev in all_devices['devices']: - if dev['name'] == device: + data = [{"uid": "/zport/dmd/Devices", "params": {}, "limit": None}] + all_devices = _router_request("DeviceRouter", "getDevices", data=data) + for dev in all_devices["devices"]: + if dev["name"] == device: # We need to save the has for later operations - dev['hash'] = all_devices['hash'] - log.info('Found device %s in Zenoss', device) + dev["hash"] = all_devices["hash"] + log.info("Found device %s in Zenoss", device) return dev - log.info('Unable to find device %s in Zenoss', device) + log.info("Unable to find device %s in Zenoss", device) return None def device_exists(device=None): - ''' + """ Check to see if a device already exists in Zenoss. Parameters: @@ -148,18 +153,18 @@ def device_exists(device=None): CLI Example: salt '*' zenoss.device_exists - ''' + """ if not device: - device = __salt__['grains.get']('fqdn') + device = __salt__["grains.get"]("fqdn") if find_device(device): return True return False -def add_device(device=None, device_class=None, collector='localhost', prod_state=1000): - ''' +def add_device(device=None, device_class=None, collector="localhost", prod_state=1000): + """ A function to connect to a zenoss server and add a new device entry. Parameters: @@ -170,21 +175,27 @@ def add_device(device=None, device_class=None, collector='localhost', prod_state CLI Example: salt '*' zenoss.add_device - ''' + """ if not device: - device = __salt__['grains.get']('fqdn') + device = __salt__["grains.get"]("fqdn") if not device_class: device_class = _determine_device_class() - log.info('Adding device %s to zenoss', device) - data = dict(deviceName=device, deviceClass=device_class, model=True, collector=collector, productionState=prod_state) - response = _router_request('DeviceRouter', 'addDevice', data=[data]) + log.info("Adding device %s to zenoss", device) + data = dict( + deviceName=device, + deviceClass=device_class, + model=True, + collector=collector, + productionState=prod_state, + ) + response = _router_request("DeviceRouter", "addDevice", data=[data]) return response def set_prod_state(prod_state, device=None): - ''' + """ A function to set the prod_state in zenoss. Parameters: @@ -193,16 +204,20 @@ def set_prod_state(prod_state, device=None): CLI Example: salt zenoss.set_prod_state 1000 hostname - ''' + """ if not device: - device = __salt__['grains.get']('fqdn') + device = __salt__["grains.get"]("fqdn") device_object = find_device(device) if not device_object: return "Unable to find a device in Zenoss for {0}".format(device) - log.info('Setting prodState to %d on %s device', prod_state, device) - data = dict(uids=[device_object['uid']], prodState=prod_state, hashcheck=device_object['hash']) - return _router_request('DeviceRouter', 'setProductionState', [data]) + log.info("Setting prodState to %d on %s device", prod_state, device) + data = dict( + uids=[device_object["uid"]], + prodState=prod_state, + hashcheck=device_object["hash"], + ) + return _router_request("DeviceRouter", "setProductionState", [data]) diff --git a/salt/modules/zfs.py b/salt/modules/zfs.py index 98fce2f2ce5..c637b956a25 100644 --- a/salt/modules/zfs.py +++ b/salt/modules/zfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for running ZFS command :codeauthor: Nitin Madhok <nmadhok@clemson.edu>, Jorge Schrauwen <sjorge@blackdot.be> @@ -12,41 +12,42 @@ Module for running ZFS command Big refactor to remove duplicate code, better type converions and improved consistancy in output. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging +import salt.modules.cmdmod + # Import Salt libs import salt.utils.args import salt.utils.path import salt.utils.versions -import salt.modules.cmdmod -from salt.utils.odict import OrderedDict from salt.ext.six.moves import zip +from salt.utils.odict import OrderedDict -__virtualname__ = 'zfs' +__virtualname__ = "zfs" log = logging.getLogger(__name__) # Function alias to set mapping. __func_alias__ = { - 'list_': 'list', + "list_": "list", } def __virtual__(): - ''' + """ Only load when the platform has zfs support - ''' - if __grains__.get('zfs_support'): + """ + if __grains__.get("zfs_support"): return __virtualname__ else: return False, "The zfs module cannot be loaded: zfs not supported" def exists(name, **kwargs): - ''' + """ Check if a ZFS filesystem or volume or snapshot exists. name : string @@ -64,31 +65,27 @@ def exists(name, **kwargs): salt '*' zfs.exists myzpool/mydataset salt '*' zfs.exists myzpool/myvolume type=volume - ''' + """ ## Configure command # NOTE: initialize the defaults opts = {} # NOTE: set extra config from kwargs - if kwargs.get('type', False): - opts['-t'] = kwargs.get('type') + if kwargs.get("type", False): + opts["-t"] = kwargs.get("type") ## Check if 'name' of 'type' exists - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='list', - opts=opts, - target=name, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="list", opts=opts, target=name,), python_shell=False, ignore_retcode=True, ) - return res['retcode'] == 0 + return res["retcode"] == 0 def create(name, **kwargs): - ''' + """ Create a ZFS File System. name : string @@ -122,27 +119,29 @@ def create(name, **kwargs): salt '*' zfs.create myzpool/volume volume_size=1G [sparse=True|False]` salt '*' zfs.create myzpool/volume volume_size=1G properties="{'volblocksize': '512'}" [sparse=True|False] - ''' + """ ## Configure command # NOTE: initialize the defaults flags = [] opts = {} # NOTE: push filesystem properties - filesystem_properties = kwargs.get('properties', {}) + filesystem_properties = kwargs.get("properties", {}) # NOTE: set extra config from kwargs - if kwargs.get('create_parent', False): - flags.append('-p') - if kwargs.get('sparse', False) and kwargs.get('volume_size', None): - flags.append('-s') - if kwargs.get('volume_size', None): - opts['-V'] = __utils__['zfs.to_size'](kwargs.get('volume_size'), convert_to_human=False) + if kwargs.get("create_parent", False): + flags.append("-p") + if kwargs.get("sparse", False) and kwargs.get("volume_size", None): + flags.append("-s") + if kwargs.get("volume_size", None): + opts["-V"] = __utils__["zfs.to_size"]( + kwargs.get("volume_size"), convert_to_human=False + ) ## Create filesystem/volume - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='create', + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"]( + command="create", flags=flags, opts=opts, filesystem_properties=filesystem_properties, @@ -151,11 +150,11 @@ def create(name, **kwargs): python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'created') + return __utils__["zfs.parse_command_result"](res, "created") def destroy(name, **kwargs): - ''' + """ Destroy a ZFS File System. name : string @@ -179,34 +178,30 @@ def destroy(name, **kwargs): salt '*' zfs.destroy myzpool/mydataset [force=True|False] - ''' + """ ## Configure command # NOTE: initialize the defaults flags = [] # NOTE: set extra config from kwargs - if kwargs.get('force', False): - flags.append('-f') - if kwargs.get('recursive_all', False): - flags.append('-R') - if kwargs.get('recursive', False): - flags.append('-r') + if kwargs.get("force", False): + flags.append("-f") + if kwargs.get("recursive_all", False): + flags.append("-R") + if kwargs.get("recursive", False): + flags.append("-r") ## Destroy filesystem/volume/snapshot/... - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='destroy', - flags=flags, - target=name, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="destroy", flags=flags, target=name,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'destroyed') + return __utils__["zfs.parse_command_result"](res, "destroyed") def rename(name, new_name, **kwargs): - ''' + """ Rename or Relocate a ZFS File System. name : string @@ -231,47 +226,45 @@ def rename(name, new_name, **kwargs): salt '*' zfs.rename myzpool/mydataset myzpool/renameddataset - ''' + """ ## Configure command # NOTE: initialize the defaults flags = [] target = [] # NOTE: set extra config from kwargs - if __utils__['zfs.is_snapshot'](name): - if kwargs.get('create_parent', False): - log.warning('zfs.rename - create_parent=True cannot be used with snapshots.') - if kwargs.get('force', False): - log.warning('zfs.rename - force=True cannot be used with snapshots.') - if kwargs.get('recursive', False): - flags.append('-r') + if __utils__["zfs.is_snapshot"](name): + if kwargs.get("create_parent", False): + log.warning( + "zfs.rename - create_parent=True cannot be used with snapshots." + ) + if kwargs.get("force", False): + log.warning("zfs.rename - force=True cannot be used with snapshots.") + if kwargs.get("recursive", False): + flags.append("-r") else: - if kwargs.get('create_parent', False): - flags.append('-p') - if kwargs.get('force', False): - flags.append('-f') - if kwargs.get('recursive', False): - log.warning('zfs.rename - recursive=True can only be used with snapshots.') + if kwargs.get("create_parent", False): + flags.append("-p") + if kwargs.get("force", False): + flags.append("-f") + if kwargs.get("recursive", False): + log.warning("zfs.rename - recursive=True can only be used with snapshots.") # NOTE: update target target.append(name) target.append(new_name) ## Rename filesystem/volume/snapshot/... - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='rename', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="rename", flags=flags, target=target,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'renamed') + return __utils__["zfs.parse_command_result"](res, "renamed") def list_(name=None, **kwargs): - ''' + """ Return a list of all datasets or a specified dataset on the system and the values of their used, available, referenced, and mountpoint properties. @@ -304,78 +297,75 @@ def list_(name=None, **kwargs): salt '*' zfs.list myzpool/mydataset [recursive=True|False] salt '*' zfs.list myzpool/mydataset properties="sharenfs,mountpoint" - ''' + """ ret = OrderedDict() ## update properties # NOTE: properties should be a list - properties = kwargs.get('properties', 'used,avail,refer,mountpoint') + properties = kwargs.get("properties", "used,avail,refer,mountpoint") if not isinstance(properties, list): - properties = properties.split(',') + properties = properties.split(",") # NOTE: name should be first property # we loop here because there 'name' can be in the list # multiple times. - while 'name' in properties: - properties.remove('name') - properties.insert(0, 'name') + while "name" in properties: + properties.remove("name") + properties.insert(0, "name") ## Configure command # NOTE: initialize the defaults - flags = ['-H'] + flags = ["-H"] opts = {} # NOTE: set extra config from kwargs - if kwargs.get('recursive', False): - flags.append('-r') - if kwargs.get('recursive', False) and kwargs.get('depth', False): - opts['-d'] = kwargs.get('depth') - if kwargs.get('type', False): - opts['-t'] = kwargs.get('type') - kwargs_sort = kwargs.get('sort', False) + if kwargs.get("recursive", False): + flags.append("-r") + if kwargs.get("recursive", False) and kwargs.get("depth", False): + opts["-d"] = kwargs.get("depth") + if kwargs.get("type", False): + opts["-t"] = kwargs.get("type") + kwargs_sort = kwargs.get("sort", False) if kwargs_sort and kwargs_sort in properties: - if kwargs.get('order', 'ascending').startswith('a'): - opts['-s'] = kwargs_sort + if kwargs.get("order", "ascending").startswith("a"): + opts["-s"] = kwargs_sort else: - opts['-S'] = kwargs_sort + opts["-S"] = kwargs_sort if isinstance(properties, list): # NOTE: There can be only one -o and it takes a comma-seperated list - opts['-o'] = ','.join(properties) + opts["-o"] = ",".join(properties) else: - opts['-o'] = properties + opts["-o"] = properties ## parse zfs list - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='list', - flags=flags, - opts=opts, - target=name, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"]( + command="list", flags=flags, opts=opts, target=name, ), python_shell=False, ) - if res['retcode'] == 0: - for ds in res['stdout'].splitlines(): - if kwargs.get('parsable', True): - ds_data = __utils__['zfs.from_auto_dict']( + if res["retcode"] == 0: + for ds in res["stdout"].splitlines(): + if kwargs.get("parsable", True): + ds_data = __utils__["zfs.from_auto_dict"]( OrderedDict(list(zip(properties, ds.split("\t")))), ) else: - ds_data = __utils__['zfs.to_auto_dict']( + ds_data = __utils__["zfs.to_auto_dict"]( OrderedDict(list(zip(properties, ds.split("\t")))), convert_to_human=True, ) - ret[ds_data['name']] = ds_data - del ret[ds_data['name']]['name'] + ret[ds_data["name"]] = ds_data + del ret[ds_data["name"]]["name"] else: - return __utils__['zfs.parse_command_result'](res) + return __utils__["zfs.parse_command_result"](res) return ret def list_mount(): - ''' + """ List mounted zfs filesystems .. versionadded:: 2018.3.1 @@ -386,27 +376,24 @@ def list_mount(): salt '*' zfs.list_mount - ''' + """ ## List mounted filesystem - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='mount', - ), - python_shell=False, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="mount",), python_shell=False, ) - if res['retcode'] == 0: + if res["retcode"] == 0: ret = OrderedDict() - for mount in res['stdout'].splitlines(): + for mount in res["stdout"].splitlines(): mount = mount.split() ret[mount[0]] = mount[-1] return ret else: - return __utils__['zfs.parse_command_result'](res) + return __utils__["zfs.parse_command_result"](res) def mount(name=None, **kwargs): - ''' + """ Mounts ZFS file systems name : string @@ -432,47 +419,45 @@ def mount(name=None, **kwargs): salt '*' zfs.mount myzpool/mydataset salt '*' zfs.mount myzpool/mydataset options=ro - ''' + """ ## Configure command # NOTE: initialize the defaults flags = [] opts = {} # NOTE: set extra config from kwargs - if kwargs.get('overlay', False): - flags.append('-O') - if kwargs.get('options', False): - opts['-o'] = kwargs.get('options') - if name in [None, '-a']: + if kwargs.get("overlay", False): + flags.append("-O") + if kwargs.get("options", False): + opts["-o"] = kwargs.get("options") + if name in [None, "-a"]: # NOTE: the new way to mount all filesystems is to have name # set to ```None```. We still accept the old '-a' until # Sodium. After Sodium we can update the if statement # to ```if not name:``` - if name == '-a': + if name == "-a": salt.utils.versions.warn_until( - 'Sodium', - 'Passing \'-a\' as name is deprecated as of Salt 2019.2.0. This ' - 'warning will be removed in Salt Sodium. Please pass name as ' - '\'None\' instead to mount all filesystems.') - flags.append('-a') + "Sodium", + "Passing '-a' as name is deprecated as of Salt 2019.2.0. This " + "warning will be removed in Salt Sodium. Please pass name as " + "'None' instead to mount all filesystems.", + ) + flags.append("-a") name = None ## Mount filesystem - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='mount', - flags=flags, - opts=opts, - target=name, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"]( + command="mount", flags=flags, opts=opts, target=name, ), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'mounted') + return __utils__["zfs.parse_command_result"](res, "mounted") def unmount(name, **kwargs): - ''' + """ Unmounts ZFS file systems name : string @@ -497,36 +482,32 @@ def unmount(name, **kwargs): salt '*' zfs.unmount myzpool/mydataset [force=True|False] - ''' + """ ## Configure command # NOTE: initialize the defaults flags = [] # NOTE: set extra config from kwargs - if kwargs.get('force', False): - flags.append('-f') - if name in [None, '-a']: + if kwargs.get("force", False): + flags.append("-f") + if name in [None, "-a"]: # NOTE: still accept '-a' as name for backwards compatibility # until Salt Sodium this should just simplify # this to just set '-a' if name is not set. - flags.append('-a') + flags.append("-a") name = None ## Unmount filesystem - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='unmount', - flags=flags, - target=name, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="unmount", flags=flags, target=name,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'unmounted') + return __utils__["zfs.parse_command_result"](res, "unmounted") def inherit(prop, name, **kwargs): - ''' + """ Clears the specified property prop : string @@ -547,33 +528,30 @@ def inherit(prop, name, **kwargs): salt '*' zfs.inherit canmount myzpool/mydataset [recursive=True|False] - ''' + """ ## Configure command # NOTE: initialize the defaults flags = [] # NOTE: set extra config from kwargs - if kwargs.get('recursive', False): - flags.append('-r') - if kwargs.get('revert', False): - flags.append('-S') + if kwargs.get("recursive", False): + flags.append("-r") + if kwargs.get("revert", False): + flags.append("-S") ## Inherit property - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='inherit', - flags=flags, - property_name=prop, - target=name, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"]( + command="inherit", flags=flags, property_name=prop, target=name, ), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'inherited') + return __utils__["zfs.parse_command_result"](res, "inherited") def diff(name_a, name_b=None, **kwargs): - ''' + """ Display the difference between a snapshot of a given filesystem and another snapshot of that filesystem from a later time or the current contents of the filesystem. @@ -597,17 +575,17 @@ def diff(name_a, name_b=None, **kwargs): salt '*' zfs.diff myzpool/mydataset@yesterday myzpool/mydataset - ''' + """ ## Configure command # NOTE: initialize the defaults - flags = ['-H'] + flags = ["-H"] target = [] # NOTE: set extra config from kwargs - if kwargs.get('show_changetime', True): - flags.append('-t') - if kwargs.get('show_indication', True): - flags.append('-F') + if kwargs.get("show_changetime", True): + flags.append("-t") + if kwargs.get("show_indication", True): + flags.append("-F") # NOTE: update target target.append(name_a) @@ -615,32 +593,30 @@ def diff(name_a, name_b=None, **kwargs): target.append(name_b) ## Diff filesystem/snapshot - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='diff', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="diff", flags=flags, target=target,), python_shell=False, ) - if res['retcode'] != 0: - return __utils__['zfs.parse_command_result'](res) + if res["retcode"] != 0: + return __utils__["zfs.parse_command_result"](res) else: - if not kwargs.get('parsable', True) and kwargs.get('show_changetime', True): + if not kwargs.get("parsable", True) and kwargs.get("show_changetime", True): ret = OrderedDict() - for entry in res['stdout'].splitlines(): + for entry in res["stdout"].splitlines(): entry = entry.split() - entry_timestamp = __utils__['dateutils.strftime'](entry[0], '%Y-%m-%d.%H:%M:%S.%f') + entry_timestamp = __utils__["dateutils.strftime"]( + entry[0], "%Y-%m-%d.%H:%M:%S.%f" + ) entry_data = "\t\t".join(entry[1:]) ret[entry_timestamp] = entry_data else: - ret = res['stdout'].splitlines() + ret = res["stdout"].splitlines() return ret def rollback(name, **kwargs): - ''' + """ Roll back the given dataset to a previous snapshot. name : string @@ -673,37 +649,35 @@ def rollback(name, **kwargs): salt '*' zfs.rollback myzpool/mydataset@yesterday - ''' + """ ## Configure command # NOTE: initialize the defaults flags = [] # NOTE: set extra config from kwargs - if kwargs.get('recursive_all', False): - flags.append('-R') - if kwargs.get('recursive', False): - flags.append('-r') - if kwargs.get('force', False): - if kwargs.get('recursive_all', False) or kwargs.get('recursive', False): - flags.append('-f') + if kwargs.get("recursive_all", False): + flags.append("-R") + if kwargs.get("recursive", False): + flags.append("-r") + if kwargs.get("force", False): + if kwargs.get("recursive_all", False) or kwargs.get("recursive", False): + flags.append("-f") else: - log.warning('zfs.rollback - force=True can only be used with recursive_all=True or recursive=True') + log.warning( + "zfs.rollback - force=True can only be used with recursive_all=True or recursive=True" + ) ## Rollback to snapshot - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='rollback', - flags=flags, - target=name, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="rollback", flags=flags, target=name,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'rolledback') + return __utils__["zfs.parse_command_result"](res, "rolledback") def clone(name_a, name_b, **kwargs): - ''' + """ Creates a clone of the given snapshot. name_a : string @@ -732,27 +706,27 @@ def clone(name_a, name_b, **kwargs): salt '*' zfs.clone myzpool/mydataset@yesterday myzpool/mydataset_yesterday - ''' + """ ## Configure command # NOTE: initialize the defaults flags = [] target = [] # NOTE: push filesystem properties - filesystem_properties = kwargs.get('properties', {}) + filesystem_properties = kwargs.get("properties", {}) # NOTE: set extra config from kwargs - if kwargs.get('create_parent', False): - flags.append('-p') + if kwargs.get("create_parent", False): + flags.append("-p") # NOTE: update target target.append(name_a) target.append(name_b) ## Clone filesystem/volume - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='clone', + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"]( + command="clone", flags=flags, filesystem_properties=filesystem_properties, target=target, @@ -760,11 +734,11 @@ def clone(name_a, name_b, **kwargs): python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'cloned') + return __utils__["zfs.parse_command_result"](res, "cloned") def promote(name): - ''' + """ Promotes a clone file system to no longer be dependent on its "origin" snapshot. @@ -794,21 +768,18 @@ def promote(name): salt '*' zfs.promote myzpool/myclone - ''' + """ ## Promote clone - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='promote', - target=name, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="promote", target=name,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'promoted') + return __utils__["zfs.parse_command_result"](res, "promoted") def bookmark(snapshot, bookmark): - ''' + """ Creates a bookmark of the given snapshot .. note:: @@ -832,10 +803,10 @@ def bookmark(snapshot, bookmark): salt '*' zfs.bookmark myzpool/mydataset@yesterday myzpool/mydataset#complete - ''' + """ # abort if we do not have feature flags - if not __utils__['zfs.has_feature_flags'](): - return OrderedDict([('error', 'bookmarks are not supported')]) + if not __utils__["zfs.has_feature_flags"](): + return OrderedDict([("error", "bookmarks are not supported")]) ## Configure command # NOTE: initialize the defaults @@ -846,19 +817,16 @@ def bookmark(snapshot, bookmark): target.append(bookmark) ## Bookmark snapshot - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='bookmark', - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="bookmark", target=target,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'bookmarked') + return __utils__["zfs.parse_command_result"](res, "bookmarked") def holds(snapshot, **kwargs): - ''' + """ Lists all existing user references for the given snapshot or snapshots. snapshot : string @@ -874,43 +842,38 @@ def holds(snapshot, **kwargs): salt '*' zfs.holds myzpool/mydataset@baseline - ''' + """ ## Configure command # NOTE: initialize the defaults - flags = ['-H'] + flags = ["-H"] target = [] # NOTE: set extra config from kwargs - if kwargs.get('recursive', False): - flags.append('-r') + if kwargs.get("recursive", False): + flags.append("-r") # NOTE: update target target.append(snapshot) ## Lookup holds - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='holds', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="holds", flags=flags, target=target,), python_shell=False, ) - ret = __utils__['zfs.parse_command_result'](res) - if res['retcode'] == 0: - for hold in res['stdout'].splitlines(): - hold_data = OrderedDict(list(zip( - ['name', 'tag', 'timestamp'], - hold.split("\t"), - ))) - ret[hold_data['tag'].strip()] = hold_data['timestamp'] + ret = __utils__["zfs.parse_command_result"](res) + if res["retcode"] == 0: + for hold in res["stdout"].splitlines(): + hold_data = OrderedDict( + list(zip(["name", "tag", "timestamp"], hold.split("\t"),)) + ) + ret[hold_data["tag"].strip()] = hold_data["timestamp"] return ret def hold(tag, *snapshot, **kwargs): - ''' + """ Adds a single reference, named with the tag argument, to the specified snapshot or snapshots. @@ -944,13 +907,14 @@ def hold(tag, *snapshot, **kwargs): salt '*' zfs.hold mytag myzpool/mydataset@mysnapshot [recursive=True] salt '*' zfs.hold mytag myzpool/mydataset@mysnapshot myzpool/mydataset@myothersnapshot - ''' + """ ## warn about tag change - if ',' in tag: + if "," in tag: salt.utils.versions.warn_until( - 'Sodium', - 'A comma-separated tag is no support as of Salt 2018.3.1 ' - 'This warning will be removed in Salt Sodium.') + "Sodium", + "A comma-separated tag is no support as of Salt 2018.3.1 " + "This warning will be removed in Salt Sodium.", + ) ## Configure command # NOTE: initialize the defaults @@ -958,28 +922,24 @@ def hold(tag, *snapshot, **kwargs): target = [] # NOTE: set extra config from kwargs - if kwargs.get('recursive', False): - flags.append('-r') + if kwargs.get("recursive", False): + flags.append("-r") # NOTE: update target target.append(tag) target.extend(snapshot) ## hold snapshot - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='hold', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="hold", flags=flags, target=target,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'held') + return __utils__["zfs.parse_command_result"](res, "held") def release(tag, *snapshot, **kwargs): - ''' + """ Removes a single reference, named with the tag argument, from the specified snapshot or snapshots. @@ -1012,13 +972,14 @@ def release(tag, *snapshot, **kwargs): salt '*' zfs.release mytag myzpool/mydataset@mysnapshot [recursive=True] salt '*' zfs.release mytag myzpool/mydataset@mysnapshot myzpool/mydataset@myothersnapshot - ''' + """ ## warn about tag change - if ',' in tag: + if "," in tag: salt.utils.versions.warn_until( - 'Sodium', - 'A comma-separated tag is no support as of Salt 2018.3.1 ' - 'This warning will be removed in Salt Sodium.') + "Sodium", + "A comma-separated tag is no support as of Salt 2018.3.1 " + "This warning will be removed in Salt Sodium.", + ) ## Configure command # NOTE: initialize the defaults @@ -1026,28 +987,24 @@ def release(tag, *snapshot, **kwargs): target = [] # NOTE: set extra config from kwargs - if kwargs.get('recursive', False): - flags.append('-r') + if kwargs.get("recursive", False): + flags.append("-r") # NOTE: update target target.append(tag) target.extend(snapshot) ## release snapshot - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='release', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"](command="release", flags=flags, target=target,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'released') + return __utils__["zfs.parse_command_result"](res, "released") def snapshot(*snapshot, **kwargs): - ''' + """ Creates snapshots with the given names. snapshot : string @@ -1074,22 +1031,22 @@ def snapshot(*snapshot, **kwargs): salt '*' zfs.snapshot myzpool/mydataset@yesterday [recursive=True] salt '*' zfs.snapshot myzpool/mydataset@yesterday myzpool/myotherdataset@yesterday [recursive=True] - ''' + """ ## Configure command # NOTE: initialize the defaults flags = [] # NOTE: push filesystem properties - filesystem_properties = kwargs.get('properties', {}) + filesystem_properties = kwargs.get("properties", {}) # NOTE: set extra config from kwargs - if kwargs.get('recursive', False): - flags.append('-r') + if kwargs.get("recursive", False): + flags.append("-r") ## Create snapshot - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='snapshot', + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"]( + command="snapshot", flags=flags, filesystem_properties=filesystem_properties, target=list(snapshot), @@ -1097,11 +1054,11 @@ def snapshot(*snapshot, **kwargs): python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'snapshotted') + return __utils__["zfs.parse_command_result"](res, "snapshotted") def set(*dataset, **kwargs): - ''' + """ Sets the property or list of properties to the given value(s) for each dataset. dataset : string @@ -1136,15 +1093,15 @@ def set(*dataset, **kwargs): salt '*' zfs.set myzpool/mydataset myzpool/myotherdataset compression=off salt '*' zfs.set myzpool/mydataset myzpool/myotherdataset compression=lz4 canmount=off - ''' + """ ## Configure command # NOTE: push filesystem properties filesystem_properties = salt.utils.args.clean_kwargs(**kwargs) ## Set property - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='set', + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"]( + command="set", property_name=list(filesystem_properties.keys()), property_value=list(filesystem_properties.values()), target=list(dataset), @@ -1152,11 +1109,11 @@ def set(*dataset, **kwargs): python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'set') + return __utils__["zfs.parse_command_result"](res, "set") def get(*dataset, **kwargs): - ''' + """ Displays properties for the given datasets. dataset : string @@ -1194,37 +1151,37 @@ def get(*dataset, **kwargs): salt '*' zfs.get myzpool/mydataset properties="sharenfs,mountpoint" [recursive=True|False] salt '*' zfs.get myzpool/mydataset myzpool/myotherdataset properties=available fields=value depth=1 - ''' + """ ## Configure command # NOTE: initialize the defaults - flags = ['-H'] + flags = ["-H"] opts = {} # NOTE: set extra config from kwargs - if kwargs.get('depth', False): - opts['-d'] = kwargs.get('depth') - elif kwargs.get('recursive', False): - flags.append('-r') - fields = kwargs.get('fields', 'value,source').split(',') - if 'name' in fields: # ensure name is first - fields.remove('name') - if 'property' in fields: # ensure property is second - fields.remove('property') - fields.insert(0, 'name') - fields.insert(1, 'property') - opts['-o'] = ",".join(fields) - if kwargs.get('type', False): - opts['-t'] = kwargs.get('type') - if kwargs.get('source', False): - opts['-s'] = kwargs.get('source') + if kwargs.get("depth", False): + opts["-d"] = kwargs.get("depth") + elif kwargs.get("recursive", False): + flags.append("-r") + fields = kwargs.get("fields", "value,source").split(",") + if "name" in fields: # ensure name is first + fields.remove("name") + if "property" in fields: # ensure property is second + fields.remove("property") + fields.insert(0, "name") + fields.insert(1, "property") + opts["-o"] = ",".join(fields) + if kwargs.get("type", False): + opts["-t"] = kwargs.get("type") + if kwargs.get("source", False): + opts["-s"] = kwargs.get("source") # NOTE: set property_name - property_name = kwargs.get('properties', 'all') + property_name = kwargs.get("properties", "all") ## Get properties - res = __salt__['cmd.run_all']( - __utils__['zfs.zfs_command']( - command='get', + res = __salt__["cmd.run_all"]( + __utils__["zfs.zfs_command"]( + command="get", flags=flags, opts=opts, property_name=property_name, @@ -1233,34 +1190,29 @@ def get(*dataset, **kwargs): python_shell=False, ) - ret = __utils__['zfs.parse_command_result'](res) - if res['retcode'] == 0: - for ds in res['stdout'].splitlines(): - ds_data = OrderedDict(list(zip( - fields, - ds.split("\t") - ))) + ret = __utils__["zfs.parse_command_result"](res) + if res["retcode"] == 0: + for ds in res["stdout"].splitlines(): + ds_data = OrderedDict(list(zip(fields, ds.split("\t")))) - if 'value' in ds_data: - if kwargs.get('parsable', True): - ds_data['value'] = __utils__['zfs.from_auto']( - ds_data['property'], - ds_data['value'], + if "value" in ds_data: + if kwargs.get("parsable", True): + ds_data["value"] = __utils__["zfs.from_auto"]( + ds_data["property"], ds_data["value"], ) else: - ds_data['value'] = __utils__['zfs.to_auto']( - ds_data['property'], - ds_data['value'], - convert_to_human=True, + ds_data["value"] = __utils__["zfs.to_auto"]( + ds_data["property"], ds_data["value"], convert_to_human=True, ) - if ds_data['name'] not in ret: - ret[ds_data['name']] = OrderedDict() + if ds_data["name"] not in ret: + ret[ds_data["name"]] = OrderedDict() - ret[ds_data['name']][ds_data['property']] = ds_data - del ds_data['name'] - del ds_data['property'] + ret[ds_data["name"]][ds_data["property"]] = ds_data + del ds_data["name"] + del ds_data["property"] return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/zk_concurrency.py b/salt/modules/zk_concurrency.py index 2a56f9b2920..bff8796781c 100644 --- a/salt/modules/zk_concurrency.py +++ b/salt/modules/zk_concurrency.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Concurrency controls in zookeeper ========================================================================= @@ -9,7 +9,7 @@ Concurrency controls in zookeeper This module allows you to acquire and release a slot. This is primarily useful for ensureing that no more than N hosts take a specific action at once. This can also be used to coordinate between masters. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging @@ -18,9 +18,7 @@ import sys try: import kazoo.client - from kazoo.retry import ( - ForceRetryError - ) + from kazoo.retry import ForceRetryError import kazoo.recipe.lock import kazoo.recipe.barrier import kazoo.recipe.party @@ -31,19 +29,13 @@ try: # TODO: use the kazoo one, waiting for pull req: # https://github.com/python-zk/kazoo/pull/206 class _Semaphore(kazoo.recipe.lock.Semaphore): - def __init__(self, - client, - path, - identifier=None, - max_leases=1, - ephemeral_lease=True, - ): - identifier = (identifier or gethostname()) - kazoo.recipe.lock.Semaphore.__init__(self, - client, - path, - identifier=identifier, - max_leases=max_leases) + def __init__( + self, client, path, identifier=None, max_leases=1, ephemeral_lease=True, + ): + identifier = identifier or gethostname() + kazoo.recipe.lock.Semaphore.__init__( + self, client, path, identifier=identifier, max_leases=max_leases + ) self.ephemeral_lease = ephemeral_lease # if its not ephemeral, make sure we didn't already grab it @@ -52,7 +44,7 @@ try: for child in self.client.get_children(self.path): try: data, stat = self.client.get(self.path + "/" + child) - if identifier == data.decode('utf-8'): + if identifier == data.decode("utf-8"): self.create_path = self.path + "/" + child self.is_acquired = True break @@ -73,12 +65,13 @@ try: # Get a list of the current potential lock holders. If they change, # notify our wake_event object. This is used to unblock a blocking # self._inner_acquire call. - children = self.client.get_children(self.path, - self._watch_lease_change) + children = self.client.get_children(self.path, self._watch_lease_change) # If there are leases available, acquire one if len(children) < self.max_leases: - self.client.create(self.create_path, self.data, ephemeral=self.ephemeral_lease) + self.client.create( + self.create_path, self.data, ephemeral=self.ephemeral_lease + ) # Check if our acquisition was successful or not. Update our state. if self.client.exists(self.create_path): @@ -94,75 +87,83 @@ except ImportError: HAS_DEPS = False -__virtualname__ = 'zk_concurrency' +__virtualname__ = "zk_concurrency" def __virtual__(): if not HAS_DEPS: return (False, "Module zk_concurrency: dependencies failed") - __context__['semaphore_map'] = {} + __context__["semaphore_map"] = {} return __virtualname__ def _get_zk_conn(profile=None, **connection_args): if profile: - prefix = 'zookeeper:' + profile + prefix = "zookeeper:" + profile else: - prefix = 'zookeeper' + prefix = "zookeeper" def get(key, default=None): - ''' + """ look in connection_args first, then default to config file - ''' - return connection_args.get(key) or __salt__['config.get'](':'.join([prefix, key]), default) + """ + return connection_args.get(key) or __salt__["config.get"]( + ":".join([prefix, key]), default + ) - hosts = get('hosts', '127.0.0.1:2181') - scheme = get('scheme', None) - username = get('username', None) - password = get('password', None) - default_acl = get('default_acl', None) + hosts = get("hosts", "127.0.0.1:2181") + scheme = get("scheme", None) + username = get("username", None) + password = get("password", None) + default_acl = get("default_acl", None) if isinstance(hosts, list): - hosts = ','.join(hosts) + hosts = ",".join(hosts) if username is not None and password is not None and scheme is None: - scheme = 'digest' + scheme = "digest" auth_data = None if scheme and username and password: - auth_data = [(scheme, ':'.join([username, password]))] + auth_data = [(scheme, ":".join([username, password]))] if default_acl is not None: if isinstance(default_acl, list): - default_acl = [__salt__['zookeeper.make_digest_acl'](**acl) for acl in default_acl] + default_acl = [ + __salt__["zookeeper.make_digest_acl"](**acl) for acl in default_acl + ] else: - default_acl = [__salt__['zookeeper.make_digest_acl'](**default_acl)] + default_acl = [__salt__["zookeeper.make_digest_acl"](**default_acl)] - __context__.setdefault('zkconnection', {}).setdefault(profile or hosts, - kazoo.client.KazooClient(hosts=hosts, - default_acl=default_acl, - auth_data=auth_data)) + __context__.setdefault("zkconnection", {}).setdefault( + profile or hosts, + kazoo.client.KazooClient( + hosts=hosts, default_acl=default_acl, auth_data=auth_data + ), + ) - if not __context__['zkconnection'][profile or hosts].connected: - __context__['zkconnection'][profile or hosts].start() + if not __context__["zkconnection"][profile or hosts].connected: + __context__["zkconnection"][profile or hosts].start() - return __context__['zkconnection'][profile or hosts] + return __context__["zkconnection"][profile or hosts] -def lock_holders(path, - zk_hosts=None, - identifier=None, - max_concurrency=1, - timeout=None, - ephemeral_lease=False, - profile=None, - scheme=None, - username=None, - password=None, - default_acl=None): - ''' +def lock_holders( + path, + zk_hosts=None, + identifier=None, + max_concurrency=1, + timeout=None, + ephemeral_lease=False, + profile=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Return an un-ordered list of lock holders path @@ -188,30 +189,41 @@ def lock_holders(path, .. code-block:: bash salt minion zk_concurrency.lock_holders /lock/path host1:1234,host2:1234 - ''' - zk = _get_zk_conn(profile=profile, hosts=zk_hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) - if path not in __context__['semaphore_map']: - __context__['semaphore_map'][path] = _Semaphore(zk, path, identifier, - max_leases=max_concurrency, - ephemeral_lease=ephemeral_lease) - return __context__['semaphore_map'][path].lease_holders() + """ + zk = _get_zk_conn( + profile=profile, + hosts=zk_hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) + if path not in __context__["semaphore_map"]: + __context__["semaphore_map"][path] = _Semaphore( + zk, + path, + identifier, + max_leases=max_concurrency, + ephemeral_lease=ephemeral_lease, + ) + return __context__["semaphore_map"][path].lease_holders() -def lock(path, - zk_hosts=None, - identifier=None, - max_concurrency=1, - timeout=None, - ephemeral_lease=False, - force=False, # foricble get the lock regardless of open slots - profile=None, - scheme=None, - username=None, - password=None, - default_acl=None, - ): - ''' +def lock( + path, + zk_hosts=None, + identifier=None, + max_concurrency=1, + timeout=None, + ephemeral_lease=False, + force=False, # foricble get the lock regardless of open slots + profile=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Get lock (with optional timeout) path @@ -240,42 +252,53 @@ def lock(path, .. code-block:: bash salt minion zk_concurrency.lock /lock/path host1:1234,host2:1234 - ''' - zk = _get_zk_conn(profile=profile, hosts=zk_hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) - if path not in __context__['semaphore_map']: - __context__['semaphore_map'][path] = _Semaphore(zk, path, identifier, - max_leases=max_concurrency, - ephemeral_lease=ephemeral_lease) + """ + zk = _get_zk_conn( + profile=profile, + hosts=zk_hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) + if path not in __context__["semaphore_map"]: + __context__["semaphore_map"][path] = _Semaphore( + zk, + path, + identifier, + max_leases=max_concurrency, + ephemeral_lease=ephemeral_lease, + ) # forcibly get the lock regardless of max_concurrency if force: - __context__['semaphore_map'][path].assured_path = True - __context__['semaphore_map'][path].max_leases = sys.maxint + __context__["semaphore_map"][path].assured_path = True + __context__["semaphore_map"][path].max_leases = sys.maxint # block waiting for lock acquisition if timeout: - logging.info('Acquiring lock %s with timeout=%s', path, timeout) - __context__['semaphore_map'][path].acquire(timeout=timeout) + logging.info("Acquiring lock %s with timeout=%s", path, timeout) + __context__["semaphore_map"][path].acquire(timeout=timeout) else: - logging.info('Acquiring lock %s with no timeout', path) - __context__['semaphore_map'][path].acquire() + logging.info("Acquiring lock %s with no timeout", path) + __context__["semaphore_map"][path].acquire() - return __context__['semaphore_map'][path].is_acquired + return __context__["semaphore_map"][path].is_acquired -def unlock(path, - zk_hosts=None, # in case you need to unlock without having run lock (failed execution for example) - identifier=None, - max_concurrency=1, - ephemeral_lease=False, - scheme=None, - profile=None, - username=None, - password=None, - default_acl=None - ): - ''' +def unlock( + path, + zk_hosts=None, # in case you need to unlock without having run lock (failed execution for example) + identifier=None, + max_concurrency=1, + ephemeral_lease=False, + scheme=None, + profile=None, + username=None, + password=None, + default_acl=None, +): + """ Remove lease from semaphore path @@ -301,36 +324,47 @@ def unlock(path, .. code-block:: bash salt minion zk_concurrency.unlock /lock/path host1:1234,host2:1234 - ''' + """ # if someone passed in zk_hosts, and the path isn't in __context__['semaphore_map'], lets # see if we can find it - zk = _get_zk_conn(profile=profile, hosts=zk_hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) - if path not in __context__['semaphore_map']: - __context__['semaphore_map'][path] = _Semaphore(zk, path, identifier, - max_leases=max_concurrency, - ephemeral_lease=ephemeral_lease) + zk = _get_zk_conn( + profile=profile, + hosts=zk_hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) + if path not in __context__["semaphore_map"]: + __context__["semaphore_map"][path] = _Semaphore( + zk, + path, + identifier, + max_leases=max_concurrency, + ephemeral_lease=ephemeral_lease, + ) - if path in __context__['semaphore_map']: - __context__['semaphore_map'][path].release() - del __context__['semaphore_map'][path] + if path in __context__["semaphore_map"]: + __context__["semaphore_map"][path].release() + del __context__["semaphore_map"][path] return True else: - logging.error('Unable to find lease for path %s', path) + logging.error("Unable to find lease for path %s", path) return False -def party_members(path, - zk_hosts=None, - min_nodes=1, - blocking=False, - profile=None, - scheme=None, - username=None, - password=None, - default_acl=None, - ): - ''' +def party_members( + path, + zk_hosts=None, + min_nodes=1, + blocking=False, + profile=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Get the List of identifiers in a particular party, optionally waiting for the specified minimum number of nodes (min_nodes) to appear @@ -352,9 +386,15 @@ def party_members(path, salt minion zk_concurrency.party_members /lock/path host1:1234,host2:1234 salt minion zk_concurrency.party_members /lock/path host1:1234,host2:1234 min_nodes=3 blocking=True - ''' - zk = _get_zk_conn(profile=profile, hosts=zk_hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + """ + zk = _get_zk_conn( + profile=profile, + hosts=zk_hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) party = kazoo.recipe.party.ShallowParty(zk, path) if blocking: barrier = kazoo.recipe.barrier.DoubleBarrier(zk, path, min_nodes) diff --git a/salt/modules/znc.py b/salt/modules/znc.py index 504a891c422..f9feb5ed28b 100644 --- a/salt/modules/znc.py +++ b/salt/modules/znc.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" znc - An advanced IRC bouncer .. versionadded:: 2014.7.0 Provides an interface to basic ZNC functionality -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -23,43 +23,45 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if znc is installed - ''' - if salt.utils.path.which('znc'): - return 'znc' + """ + if salt.utils.path.which("znc"): + return "znc" return (False, "Module znc: znc binary not found") -def _makepass(password, hasher='sha256'): - ''' +def _makepass(password, hasher="sha256"): + """ Create a znc compatible hashed password - ''' + """ # Setup the hasher - if hasher == 'sha256': + if hasher == "sha256": h = hashlib.sha256(password) - elif hasher == 'md5': + elif hasher == "md5": h = hashlib.md5(password) else: return NotImplemented - c = "abcdefghijklmnopqrstuvwxyz" \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ + c = ( + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789!?.,:;/*-+_()" + ) r = { - 'Method': h.name, - 'Salt': ''.join(random.SystemRandom().choice(c) for x in range(20)), + "Method": h.name, + "Salt": "".join(random.SystemRandom().choice(c) for x in range(20)), } # Salt the password hash - h.update(r['Salt']) - r['Hash'] = h.hexdigest() + h.update(r["Salt"]) + r["Hash"] = h.hexdigest() return r def buildmod(*modules): - ''' + """ Build module using znc-buildmod CLI Example: @@ -67,20 +69,20 @@ def buildmod(*modules): .. code-block:: bash salt '*' znc.buildmod module.cpp [...] - ''' + """ # Check if module files are missing missing = [module for module in modules if not os.path.exists(module)] if missing: - return 'Error: The file ({0}) does not exist.'.format(', '.join(missing)) + return "Error: The file ({0}) does not exist.".format(", ".join(missing)) - cmd = ['znc-buildmod'] + cmd = ["znc-buildmod"] cmd.extend(modules) - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() return out[-1] def dumpconf(): - ''' + """ Write the active configuration state to config file CLI Example: @@ -88,12 +90,12 @@ def dumpconf(): .. code-block:: bash salt '*' znc.dumpconf - ''' - return __salt__['ps.pkill']('znc', signal=signal.SIGUSR1) + """ + return __salt__["ps.pkill"]("znc", signal=signal.SIGUSR1) def rehashconf(): - ''' + """ Rehash the active configuration state from config file CLI Example: @@ -101,12 +103,12 @@ def rehashconf(): .. code-block:: bash salt '*' znc.rehashconf - ''' - return __salt__['ps.pkill']('znc', signal=signal.SIGHUP) + """ + return __salt__["ps.pkill"]("znc", signal=signal.SIGHUP) def version(): - ''' + """ Return server version from znc --version CLI Example: @@ -114,8 +116,8 @@ def version(): .. code-block:: bash salt '*' znc.version - ''' - cmd = ['znc', '--version'] - out = __salt__['cmd.run'](cmd, python_shell=False).splitlines() - ret = out[0].split(' - ') + """ + cmd = ["znc", "--version"] + out = __salt__["cmd.run"](cmd, python_shell=False).splitlines() + ret = out[0].split(" - ") return ret[0] diff --git a/salt/modules/zoneadm.py b/salt/modules/zoneadm.py index 52c7ff18415..6bc56633283 100644 --- a/salt/modules/zoneadm.py +++ b/salt/modules/zoneadm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for Solaris 10's zoneadm :maintainer: Jorge Schrauwen <sjorge@blackdot.be> @@ -10,72 +10,74 @@ Module for Solaris 10's zoneadm .. warning:: Oracle Solaris 11's zoneadm is not supported by this module! -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging +import salt.utils.decorators + # Import Salt libs import salt.utils.path -import salt.utils.decorators from salt.ext.six.moves import range log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'zoneadm' +__virtualname__ = "zoneadm" # Function aliases -__func_alias__ = { - 'list_zones': 'list' -} +__func_alias__ = {"list_zones": "list"} @salt.utils.decorators.memoize def _is_globalzone(): - ''' + """ Check if we are running in the globalzone - ''' - if not __grains__['kernel'] == 'SunOS': + """ + if not __grains__["kernel"] == "SunOS": return False - zonename = __salt__['cmd.run_all']('zonename') - if zonename['retcode']: + zonename = __salt__["cmd.run_all"]("zonename") + if zonename["retcode"]: return False - if zonename['stdout'] == 'global': + if zonename["stdout"] == "global": return True return False def _is_uuid(zone): - ''' + """ Check if zone is actually a UUID - ''' - return len(zone) == 36 and zone.index('-') == 8 + """ + return len(zone) == 36 and zone.index("-") == 8 def __virtual__(): - ''' + """ We are available if we are have zoneadm and are the global zone on Solaris 10, OmniOS, OpenIndiana, OpenSolaris, or Smartos. - ''' - if _is_globalzone() and salt.utils.path.which('zoneadm'): - if __grains__['os'] in ['OpenSolaris', 'SmartOS', 'OmniOS', 'OpenIndiana']: + """ + if _is_globalzone() and salt.utils.path.which("zoneadm"): + if __grains__["os"] in ["OpenSolaris", "SmartOS", "OmniOS", "OpenIndiana"]: return __virtualname__ - elif __grains__['os'] == 'Oracle Solaris' and int(__grains__['osmajorrelease']) == 10: + elif ( + __grains__["os"] == "Oracle Solaris" + and int(__grains__["osmajorrelease"]) == 10 + ): return __virtualname__ return ( False, - '{0} module can only be loaded in a solaris globalzone.'.format( + "{0} module can only be loaded in a solaris globalzone.".format( __virtualname__ - ) + ), ) def list_zones(verbose=True, installed=False, configured=False, hide_global=True): - ''' + """ List all zones verbose : boolean @@ -92,15 +94,15 @@ def list_zones(verbose=True, installed=False, configured=False, hide_global=True .. code-block:: bash salt '*' zoneadm.list - ''' + """ zones = {} ## fetch zones - header = 'zoneid:zonename:state:zonepath:uuid:brand:ip-type'.split(':') - zone_data = __salt__['cmd.run_all']('zoneadm list -p -c') - if zone_data['retcode'] == 0: - for zone in zone_data['stdout'].splitlines(): - zone = zone.split(':') + header = "zoneid:zonename:state:zonepath:uuid:brand:ip-type".split(":") + zone_data = __salt__["cmd.run_all"]("zoneadm list -p -c") + if zone_data["retcode"] == 0: + for zone in zone_data["stdout"].splitlines(): + zone = zone.split(":") # create zone_t zone_t = {} @@ -108,24 +110,24 @@ def list_zones(verbose=True, installed=False, configured=False, hide_global=True zone_t[header[i]] = zone[i] # skip if global and hide_global - if hide_global and zone_t['zonename'] == 'global': + if hide_global and zone_t["zonename"] == "global": continue # skip installed and configured - if not installed and zone_t['state'] == 'installed': + if not installed and zone_t["state"] == "installed": continue - if not configured and zone_t['state'] == 'configured': + if not configured and zone_t["state"] == "configured": continue # update dict - zones[zone_t['zonename']] = zone_t - del zones[zone_t['zonename']]['zonename'] + zones[zone_t["zonename"]] = zone_t + del zones[zone_t["zonename"]]["zonename"] return zones if verbose else sorted(zones.keys()) def boot(zone, single=False, altinit=None, smf_options=None): - ''' + """ Boot (or activate) the specified zone. zone : string @@ -145,36 +147,38 @@ def boot(zone, single=False, altinit=None, smf_options=None): salt '*' zoneadm.boot clementine salt '*' zoneadm.boot maeve single=True salt '*' zoneadm.boot teddy single=True smf_options=verbose - ''' - ret = {'status': True} + """ + ret = {"status": True} ## build boot_options - boot_options = '' + boot_options = "" if single: - boot_options = '-s {0}'.format(boot_options) + boot_options = "-s {0}".format(boot_options) if altinit: # note: we cannot validate the path, as this is local to the zonepath. - boot_options = '-i {0} {1}'.format(altinit, boot_options) + boot_options = "-i {0} {1}".format(altinit, boot_options) if smf_options: - boot_options = '-m {0} {1}'.format(smf_options, boot_options) - if boot_options != '': - boot_options = ' -- {0}'.format(boot_options.strip()) + boot_options = "-m {0} {1}".format(smf_options, boot_options) + if boot_options != "": + boot_options = " -- {0}".format(boot_options.strip()) ## execute boot - res = __salt__['cmd.run_all']('zoneadm {zone} boot{boot_opts}'.format( - zone='-u {0}'.format(zone) if _is_uuid(zone) else '-z {0}'.format(zone), - boot_opts=boot_options, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm {zone} boot{boot_opts}".format( + zone="-u {0}".format(zone) if _is_uuid(zone) else "-z {0}".format(zone), + boot_opts=boot_options, + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def reboot(zone, single=False, altinit=None, smf_options=None): - ''' + """ Restart the zone. This is equivalent to a halt boot sequence. zone : string @@ -193,36 +197,38 @@ def reboot(zone, single=False, altinit=None, smf_options=None): salt '*' zoneadm.reboot dolores salt '*' zoneadm.reboot teddy single=True - ''' - ret = {'status': True} + """ + ret = {"status": True} ## build boot_options - boot_options = '' + boot_options = "" if single: - boot_options = '-s {0}'.format(boot_options) + boot_options = "-s {0}".format(boot_options) if altinit: # note: we cannot validate the path, as this is local to the zonepath. - boot_options = '-i {0} {1}'.format(altinit, boot_options) + boot_options = "-i {0} {1}".format(altinit, boot_options) if smf_options: - boot_options = '-m {0} {1}'.format(smf_options, boot_options) - if boot_options != '': - boot_options = ' -- {0}'.format(boot_options.strip()) + boot_options = "-m {0} {1}".format(smf_options, boot_options) + if boot_options != "": + boot_options = " -- {0}".format(boot_options.strip()) ## execute reboot - res = __salt__['cmd.run_all']('zoneadm {zone} reboot{boot_opts}'.format( - zone='-u {0}'.format(zone) if _is_uuid(zone) else '-z {0}'.format(zone), - boot_opts=boot_options, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm {zone} reboot{boot_opts}".format( + zone="-u {0}".format(zone) if _is_uuid(zone) else "-z {0}".format(zone), + boot_opts=boot_options, + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def halt(zone): - ''' + """ Halt the specified zone. zone : string @@ -236,24 +242,26 @@ def halt(zone): .. code-block:: bash salt '*' zoneadm.halt hector - ''' - ret = {'status': True} + """ + ret = {"status": True} ## halt zone - res = __salt__['cmd.run_all']('zoneadm {zone} halt'.format( - zone='-u {0}'.format(zone) if _is_uuid(zone) else '-z {0}'.format(zone), - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm {zone} halt".format( + zone="-u {0}".format(zone) if _is_uuid(zone) else "-z {0}".format(zone), + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def shutdown(zone, reboot=False, single=False, altinit=None, smf_options=None): - ''' + """ Gracefully shutdown the specified zone. zone : string @@ -274,37 +282,39 @@ def shutdown(zone, reboot=False, single=False, altinit=None, smf_options=None): salt '*' zoneadm.shutdown peter salt '*' zoneadm.shutdown armistice reboot=True - ''' - ret = {'status': True} + """ + ret = {"status": True} ## build boot_options - boot_options = '' + boot_options = "" if single: - boot_options = '-s {0}'.format(boot_options) + boot_options = "-s {0}".format(boot_options) if altinit: # note: we cannot validate the path, as this is local to the zonepath. - boot_options = '-i {0} {1}'.format(altinit, boot_options) + boot_options = "-i {0} {1}".format(altinit, boot_options) if smf_options: - boot_options = '-m {0} {1}'.format(smf_options, boot_options) - if boot_options != '': - boot_options = ' -- {0}'.format(boot_options.strip()) + boot_options = "-m {0} {1}".format(smf_options, boot_options) + if boot_options != "": + boot_options = " -- {0}".format(boot_options.strip()) ## shutdown zone - res = __salt__['cmd.run_all']('zoneadm {zone} shutdown{reboot}{boot_opts}'.format( - zone='-u {0}'.format(zone) if _is_uuid(zone) else '-z {0}'.format(zone), - reboot=' -r' if reboot else '', - boot_opts=boot_options, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm {zone} shutdown{reboot}{boot_opts}".format( + zone="-u {0}".format(zone) if _is_uuid(zone) else "-z {0}".format(zone), + reboot=" -r" if reboot else "", + boot_opts=boot_options, + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def detach(zone): - ''' + """ Detach the specified zone. zone : string @@ -315,24 +325,26 @@ def detach(zone): .. code-block:: bash salt '*' zoneadm.detach kissy - ''' - ret = {'status': True} + """ + ret = {"status": True} ## detach zone - res = __salt__['cmd.run_all']('zoneadm {zone} detach'.format( - zone='-u {0}'.format(zone) if _is_uuid(zone) else '-z {0}'.format(zone), - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm {zone} detach".format( + zone="-u {0}".format(zone) if _is_uuid(zone) else "-z {0}".format(zone), + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def attach(zone, force=False, brand_opts=None): - ''' + """ Attach the specified zone. zone : string @@ -348,26 +360,28 @@ def attach(zone, force=False, brand_opts=None): salt '*' zoneadm.attach lawrence salt '*' zoneadm.attach lawrence True - ''' - ret = {'status': True} + """ + ret = {"status": True} ## attach zone - res = __salt__['cmd.run_all']('zoneadm -z {zone} attach{force}{brand_opts}'.format( - zone=zone, - force=' -F' if force else '', - brand_opts=' {0}'.format(brand_opts) if brand_opts else '', - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm -z {zone} attach{force}{brand_opts}".format( + zone=zone, + force=" -F" if force else "", + brand_opts=" {0}".format(brand_opts) if brand_opts else "", + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def ready(zone): - ''' + """ Prepares a zone for running applications. zone : string @@ -378,24 +392,26 @@ def ready(zone): .. code-block:: bash salt '*' zoneadm.ready clementine - ''' - ret = {'status': True} + """ + ret = {"status": True} ## ready zone - res = __salt__['cmd.run_all']('zoneadm {zone} ready'.format( - zone='-u {0}'.format(zone) if _is_uuid(zone) else '-z {0}'.format(zone), - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm {zone} ready".format( + zone="-u {0}".format(zone) if _is_uuid(zone) else "-z {0}".format(zone), + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def verify(zone): - ''' + """ Check to make sure the configuration of the specified zone can safely be installed on the machine. @@ -407,24 +423,22 @@ def verify(zone): .. code-block:: bash salt '*' zoneadm.verify dolores - ''' - ret = {'status': True} + """ + ret = {"status": True} ## verify zone - res = __salt__['cmd.run_all']('zoneadm -z {zone} verify'.format( - zone=zone, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]("zoneadm -z {zone} verify".format(zone=zone,)) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def move(zone, zonepath): - ''' + """ Move zone to new zonepath. zone : string @@ -437,25 +451,27 @@ def move(zone, zonepath): .. code-block:: bash salt '*' zoneadm.move meave /sweetwater/meave - ''' - ret = {'status': True} + """ + ret = {"status": True} ## verify zone - res = __salt__['cmd.run_all']('zoneadm {zone} move {path}'.format( - zone='-u {0}'.format(zone) if _is_uuid(zone) else '-z {0}'.format(zone), - path=zonepath, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm {zone} move {path}".format( + zone="-u {0}".format(zone) if _is_uuid(zone) else "-z {0}".format(zone), + path=zonepath, + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def uninstall(zone): - ''' + """ Uninstall the specified zone from the system. zone : string @@ -469,24 +485,26 @@ def uninstall(zone): .. code-block:: bash salt '*' zoneadm.uninstall teddy - ''' - ret = {'status': True} + """ + ret = {"status": True} ## uninstall zone - res = __salt__['cmd.run_all']('zoneadm {zone} uninstall -F'.format( - zone='-u {0}'.format(zone) if _is_uuid(zone) else '-z {0}'.format(zone), - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm {zone} uninstall -F".format( + zone="-u {0}".format(zone) if _is_uuid(zone) else "-z {0}".format(zone), + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def install(zone, nodataset=False, brand_opts=None): - ''' + """ Install the specified zone from the system. zone : string @@ -502,26 +520,28 @@ def install(zone, nodataset=False, brand_opts=None): salt '*' zoneadm.install dolores salt '*' zoneadm.install teddy True - ''' - ret = {'status': True} + """ + ret = {"status": True} ## install zone - res = __salt__['cmd.run_all']('zoneadm -z {zone} install{nodataset}{brand_opts}'.format( - zone=zone, - nodataset=' -x nodataset' if nodataset else '', - brand_opts=' {0}'.format(brand_opts) if brand_opts else '', - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm -z {zone} install{nodataset}{brand_opts}".format( + zone=zone, + nodataset=" -x nodataset" if nodataset else "", + brand_opts=" {0}".format(brand_opts) if brand_opts else "", + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret def clone(zone, source, snapshot=None): - ''' + """ Install a zone by copying an existing installed zone. zone : string @@ -536,21 +556,24 @@ def clone(zone, source, snapshot=None): .. code-block:: bash salt '*' zoneadm.clone clementine dolores - ''' - ret = {'status': True} + """ + ret = {"status": True} ## install zone - res = __salt__['cmd.run_all']('zoneadm -z {zone} clone {snapshot}{source}'.format( - zone=zone, - source=source, - snapshot='-s {0} '.format(snapshot) if snapshot else '', - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - ret['message'] = ret['message'].replace('zoneadm: ', '') - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zoneadm -z {zone} clone {snapshot}{source}".format( + zone=zone, + source=source, + snapshot="-s {0} ".format(snapshot) if snapshot else "", + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + ret["message"] = ret["message"].replace("zoneadm: ", "") + if ret["message"] == "": + del ret["message"] return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/zonecfg.py b/salt/modules/zonecfg.py index 456dfad9a99..71c948babb6 100644 --- a/salt/modules/zonecfg.py +++ b/salt/modules/zonecfg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for Solaris 10's zonecfg :maintainer: Jorge Schrauwen <sjorge@blackdot.be> @@ -11,7 +11,7 @@ Module for Solaris 10's zonecfg .. warning:: Oracle Solaris 11's zonecfg is not supported by this module! -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -24,140 +24,149 @@ import salt.utils.data import salt.utils.decorators import salt.utils.files import salt.utils.path -from salt.utils.odict import OrderedDict # Import 3rd-party libs from salt.ext import six +from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'zonecfg' +__virtualname__ = "zonecfg" # Function aliases -__func_alias__ = { - 'import_': 'import' -} +__func_alias__ = {"import_": "import"} # Global data _zonecfg_info_resources = [ - 'rctl', - 'net', - 'fs', - 'device', - 'dedicated-cpu', - 'dataset', - 'attr', + "rctl", + "net", + "fs", + "device", + "dedicated-cpu", + "dataset", + "attr", ] _zonecfg_info_resources_calculated = [ - 'capped-cpu', - 'capped-memory', + "capped-cpu", + "capped-memory", ] _zonecfg_resource_setters = { - 'fs': ['dir', 'special', 'raw', 'type', 'options'], - 'net': ['address', 'allowed-address', 'global-nic', 'mac-addr', 'physical', 'property', 'vlan-id defrouter'], - 'device': ['match', 'property'], - 'rctl': ['name', 'value'], - 'attr': ['name', 'type', 'value'], - 'dataset': ['name'], - 'dedicated-cpu': ['ncpus', 'importance'], - 'capped-cpu': ['ncpus'], - 'capped-memory': ['physical', 'swap', 'locked'], - 'admin': ['user', 'auths'], + "fs": ["dir", "special", "raw", "type", "options"], + "net": [ + "address", + "allowed-address", + "global-nic", + "mac-addr", + "physical", + "property", + "vlan-id defrouter", + ], + "device": ["match", "property"], + "rctl": ["name", "value"], + "attr": ["name", "type", "value"], + "dataset": ["name"], + "dedicated-cpu": ["ncpus", "importance"], + "capped-cpu": ["ncpus"], + "capped-memory": ["physical", "swap", "locked"], + "admin": ["user", "auths"], } _zonecfg_resource_default_selectors = { - 'fs': 'dir', - 'net': 'mac-addr', - 'device': 'match', - 'rctl': 'name', - 'attr': 'name', - 'dataset': 'name', - 'admin': 'user', + "fs": "dir", + "net": "mac-addr", + "device": "match", + "rctl": "name", + "attr": "name", + "dataset": "name", + "admin": "user", } @salt.utils.decorators.memoize def _is_globalzone(): - ''' + """ Check if we are running in the globalzone - ''' - if not __grains__['kernel'] == 'SunOS': + """ + if not __grains__["kernel"] == "SunOS": return False - zonename = __salt__['cmd.run_all']('zonename') - if zonename['retcode']: + zonename = __salt__["cmd.run_all"]("zonename") + if zonename["retcode"]: return False - if zonename['stdout'] == 'global': + if zonename["stdout"] == "global": return True return False def __virtual__(): - ''' + """ We are available if we are have zonecfg and are the global zone on Solaris 10, OmniOS, OpenIndiana, OpenSolaris, or Smartos. - ''' - if _is_globalzone() and salt.utils.path.which('zonecfg'): - if __grains__['os'] in ['OpenSolaris', 'SmartOS', 'OmniOS', 'OpenIndiana']: + """ + if _is_globalzone() and salt.utils.path.which("zonecfg"): + if __grains__["os"] in ["OpenSolaris", "SmartOS", "OmniOS", "OpenIndiana"]: return __virtualname__ - elif __grains__['os'] == 'Oracle Solaris' and int(__grains__['osmajorrelease']) == 10: + elif ( + __grains__["os"] == "Oracle Solaris" + and int(__grains__["osmajorrelease"]) == 10 + ): return __virtualname__ return ( False, - '{0} module can only be loaded in a solaris globalzone.'.format( + "{0} module can only be loaded in a solaris globalzone.".format( __virtualname__ - ) + ), ) def _clean_message(message): - '''Internal helper to sanitize message output''' - message = message.replace('zonecfg: ', '') + """Internal helper to sanitize message output""" + message = message.replace("zonecfg: ", "") message = message.splitlines() for line in message: - if line.startswith('On line'): + if line.startswith("On line"): message.remove(line) return "\n".join(message) def _parse_value(value): - '''Internal helper for parsing configuration values into python values''' + """Internal helper for parsing configuration values into python values""" if isinstance(value, bool): - return 'true' if value else 'false' + return "true" if value else "false" elif isinstance(value, six.string_types): # parse compacted notation to dict - listparser = re.compile(r'''((?:[^,"']|"[^"]*"|'[^']*')+)''') + listparser = re.compile(r"""((?:[^,"']|"[^"]*"|'[^']*')+)""") value = value.strip() - if value.startswith('[') and value.endswith(']'): + if value.startswith("[") and value.endswith("]"): return listparser.split(value[1:-1])[1::2] - elif value.startswith('(') and value.endswith(')'): + elif value.startswith("(") and value.endswith(")"): rval = {} for pair in listparser.split(value[1:-1])[1::2]: - pair = pair.split('=') + pair = pair.split("=") if '"' in pair[1]: - pair[1] = pair[1].replace('"', '') + pair[1] = pair[1].replace('"', "") if pair[1].isdigit(): rval[pair[0]] = int(pair[1]) - elif pair[1] == 'true': + elif pair[1] == "true": rval[pair[0]] = True - elif pair[1] == 'false': + elif pair[1] == "false": rval[pair[0]] = False else: rval[pair[0]] = pair[1] return rval else: if '"' in value: - value = value.replace('"', '') + value = value.replace('"', "") if value.isdigit(): return int(value) - elif value == 'true': + elif value == "true": return True - elif value == 'false': + elif value == "false": return False else: return value @@ -166,50 +175,50 @@ def _parse_value(value): def _sanitize_value(value): - '''Internal helper for converting pythonic values to configuration file values''' + """Internal helper for converting pythonic values to configuration file values""" # dump dict into compated if isinstance(value, dict): new_value = [] - new_value.append('(') + new_value.append("(") for k, v in value.items(): new_value.append(k) - new_value.append('=') + new_value.append("=") new_value.append(v) - new_value.append(',') - new_value.append(')') - return "".join(six.text_type(v) for v in new_value).replace(',)', ')') + new_value.append(",") + new_value.append(")") + return "".join(six.text_type(v) for v in new_value).replace(",)", ")") elif isinstance(value, list): new_value = [] - new_value.append('(') + new_value.append("(") for item in value: if isinstance(item, OrderedDict): item = dict(item) for k, v in item.items(): new_value.append(k) - new_value.append('=') + new_value.append("=") new_value.append(v) else: new_value.append(item) - new_value.append(',') - new_value.append(')') - return "".join(six.text_type(v) for v in new_value).replace(',)', ')') + new_value.append(",") + new_value.append(")") + return "".join(six.text_type(v) for v in new_value).replace(",)", ")") else: # note: we can't use shelx or pipes quote here because it makes zonecfg barf - return '"{0}"'.format(value) if ' ' in value else value + return '"{0}"'.format(value) if " " in value else value def _dump_cfg(cfg_file): - '''Internal helper for debugging cfg files''' - if __salt__['file.file_exists'](cfg_file): - with salt.utils.files.fopen(cfg_file, 'r') as fp_: + """Internal helper for debugging cfg files""" + if __salt__["file.file_exists"](cfg_file): + with salt.utils.files.fopen(cfg_file, "r") as fp_: log.debug( "zonecfg - configuration file:\n%s", - "".join(salt.utils.data.decode(fp_.readlines())) + "".join(salt.utils.data.decode(fp_.readlines())), ) def create(zone, brand, zonepath, force=False): - ''' + """ Create an in-memory configuration for the specified zone. zone : string @@ -226,41 +235,42 @@ def create(zone, brand, zonepath, force=False): .. code-block:: bash salt '*' zonecfg.create deathscythe ipkg /zones/deathscythe - ''' - ret = {'status': True} + """ + ret = {"status": True} # write config cfg_file = salt.utils.files.mkstemp() - with salt.utils.files.fpopen(cfg_file, 'w+', mode=0o600) as fp_: + with salt.utils.files.fpopen(cfg_file, "w+", mode=0o600) as fp_: fp_.write("create -b -F\n" if force else "create -b\n") fp_.write("set brand={0}\n".format(_sanitize_value(brand))) fp_.write("set zonepath={0}\n".format(_sanitize_value(zonepath))) # create - if not __salt__['file.directory_exists'](zonepath): - __salt__['file.makedirs_perms'](zonepath if zonepath[-1] == '/' else '{0}/'.format(zonepath), mode='0700') + if not __salt__["file.directory_exists"](zonepath): + __salt__["file.makedirs_perms"]( + zonepath if zonepath[-1] == "/" else "{0}/".format(zonepath), mode="0700" + ) _dump_cfg(cfg_file) - res = __salt__['cmd.run_all']('zonecfg -z {zone} -f {cfg}'.format( - zone=zone, - cfg=cfg_file, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zonecfg -z {zone} -f {cfg}".format(zone=zone, cfg=cfg_file,) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + if ret["message"] == "": + del ret["message"] else: - ret['message'] = _clean_message(ret['message']) + ret["message"] = _clean_message(ret["message"]) # cleanup config file - if __salt__['file.file_exists'](cfg_file): - __salt__['file.remove'](cfg_file) + if __salt__["file.file_exists"](cfg_file): + __salt__["file.remove"](cfg_file) return ret def create_from_template(zone, template): - ''' + """ Create an in-memory configuration from a template for the specified zone. zone : string @@ -276,27 +286,26 @@ def create_from_template(zone, template): .. code-block:: bash salt '*' zonecfg.create_from_template leo tallgeese - ''' - ret = {'status': True} + """ + ret = {"status": True} # create from template _dump_cfg(template) - res = __salt__['cmd.run_all']('zonecfg -z {zone} create -t {tmpl} -F'.format( - zone=zone, - tmpl=template, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zonecfg -z {zone} create -t {tmpl} -F".format(zone=zone, tmpl=template,) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + if ret["message"] == "": + del ret["message"] else: - ret['message'] = _clean_message(ret['message']) + ret["message"] = _clean_message(ret["message"]) return ret def delete(zone): - ''' + """ Delete the specified configuration from memory and stable storage. zone : string @@ -307,25 +316,23 @@ def delete(zone): .. code-block:: bash salt '*' zonecfg.delete epyon - ''' - ret = {'status': True} + """ + ret = {"status": True} # delete zone - res = __salt__['cmd.run_all']('zonecfg -z {zone} delete -F'.format( - zone=zone, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]("zonecfg -z {zone} delete -F".format(zone=zone,)) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + if ret["message"] == "": + del ret["message"] else: - ret['message'] = _clean_message(ret['message']) + ret["message"] = _clean_message(ret["message"]) return ret def export(zone, path=None): - ''' + """ Export the configuration from memory to stable storage. zone : string @@ -339,26 +346,27 @@ def export(zone, path=None): salt '*' zonecfg.export epyon salt '*' zonecfg.export epyon /zones/epyon.cfg - ''' - ret = {'status': True} + """ + ret = {"status": True} # export zone - res = __salt__['cmd.run_all']('zonecfg -z {zone} export{path}'.format( - zone=zone, - path=' -f {0}'.format(path) if path else '', - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zonecfg -z {zone} export{path}".format( + zone=zone, path=" -f {0}".format(path) if path else "", + ) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + if ret["message"] == "": + del ret["message"] else: - ret['message'] = _clean_message(ret['message']) + ret["message"] = _clean_message(ret["message"]) return ret def import_(zone, path): - ''' + """ Import the configuration to memory from stable storage. zone : string @@ -371,27 +379,26 @@ def import_(zone, path): .. code-block:: bash salt '*' zonecfg.import epyon /zones/epyon.cfg - ''' - ret = {'status': True} + """ + ret = {"status": True} # create from file _dump_cfg(path) - res = __salt__['cmd.run_all']('zonecfg -z {zone} -f {path}'.format( - zone=zone, - path=path, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zonecfg -z {zone} -f {path}".format(zone=zone, path=path,) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + if ret["message"] == "": + del ret["message"] else: - ret['message'] = _clean_message(ret['message']) + ret["message"] = _clean_message(ret["message"]) return ret def _property(methode, zone, key, value): - ''' + """ internal handler for set and clear_property methode : string @@ -403,48 +410,51 @@ def _property(methode, zone, key, value): value : string value of property - ''' - ret = {'status': True} + """ + ret = {"status": True} # generate update script cfg_file = None - if methode not in ['set', 'clear']: - ret['status'] = False - ret['message'] = 'unkown methode {0}!'.format(methode) + if methode not in ["set", "clear"]: + ret["status"] = False + ret["message"] = "unkown methode {0}!".format(methode) else: cfg_file = salt.utils.files.mkstemp() - with salt.utils.files.fpopen(cfg_file, 'w+', mode=0o600) as fp_: - if methode == 'set': + with salt.utils.files.fpopen(cfg_file, "w+", mode=0o600) as fp_: + if methode == "set": if isinstance(value, dict) or isinstance(value, list): value = _sanitize_value(value) - value = six.text_type(value).lower() if isinstance(value, bool) else six.text_type(value) + value = ( + six.text_type(value).lower() + if isinstance(value, bool) + else six.text_type(value) + ) fp_.write("{0} {1}={2}\n".format(methode, key, _sanitize_value(value))) - elif methode == 'clear': + elif methode == "clear": fp_.write("{0} {1}\n".format(methode, key)) # update property if cfg_file: _dump_cfg(cfg_file) - res = __salt__['cmd.run_all']('zonecfg -z {zone} -f {path}'.format( - zone=zone, - path=cfg_file, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zonecfg -z {zone} -f {path}".format(zone=zone, path=cfg_file,) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + if ret["message"] == "": + del ret["message"] else: - ret['message'] = _clean_message(ret['message']) + ret["message"] = _clean_message(ret["message"]) # cleanup config file - if __salt__['file.file_exists'](cfg_file): - __salt__['file.remove'](cfg_file) + if __salt__["file.file_exists"](cfg_file): + __salt__["file.remove"](cfg_file) return ret def set_property(zone, key, value): - ''' + """ Set a property zone : string @@ -459,17 +469,12 @@ def set_property(zone, key, value): .. code-block:: bash salt '*' zonecfg.set_property deathscythe cpu-shares 100 - ''' - return _property( - 'set', - zone, - key, - value, - ) + """ + return _property("set", zone, key, value,) def clear_property(zone, key): - ''' + """ Clear a property zone : string @@ -482,17 +487,12 @@ def clear_property(zone, key): .. code-block:: bash salt '*' zonecfg.clear_property deathscythe cpu-shares - ''' - return _property( - 'clear', - zone, - key, - None, - ) + """ + return _property("clear", zone, key, None,) def _resource(methode, zone, resource_type, resource_selector, **kwargs): - ''' + """ internal resource hanlder methode : string @@ -506,43 +506,55 @@ def _resource(methode, zone, resource_type, resource_selector, **kwargs): **kwargs : string|int|... resource properties - ''' - ret = {'status': True} + """ + ret = {"status": True} # parse kwargs kwargs = salt.utils.args.clean_kwargs(**kwargs) for k in kwargs: if isinstance(kwargs[k], dict) or isinstance(kwargs[k], list): kwargs[k] = _sanitize_value(kwargs[k]) - if methode not in ['add', 'update']: - ret['status'] = False - ret['message'] = 'unknown methode {0}'.format(methode) + if methode not in ["add", "update"]: + ret["status"] = False + ret["message"] = "unknown methode {0}".format(methode) return ret - if methode in ['update'] and resource_selector and resource_selector not in kwargs: - ret['status'] = False - ret['message'] = 'resource selector {0} not found in parameters'.format(resource_selector) + if methode in ["update"] and resource_selector and resource_selector not in kwargs: + ret["status"] = False + ret["message"] = "resource selector {0} not found in parameters".format( + resource_selector + ) return ret # generate update script cfg_file = salt.utils.files.mkstemp() - with salt.utils.files.fpopen(cfg_file, 'w+', mode=0o600) as fp_: - if methode in ['add']: + with salt.utils.files.fpopen(cfg_file, "w+", mode=0o600) as fp_: + if methode in ["add"]: fp_.write("add {0}\n".format(resource_type)) - elif methode in ['update']: + elif methode in ["update"]: if resource_selector: value = kwargs[resource_selector] if isinstance(value, dict) or isinstance(value, list): value = _sanitize_value(value) - value = six.text_type(value).lower() if isinstance(value, bool) else six.text_type(value) - fp_.write("select {0} {1}={2}\n".format(resource_type, resource_selector, _sanitize_value(value))) + value = ( + six.text_type(value).lower() + if isinstance(value, bool) + else six.text_type(value) + ) + fp_.write( + "select {0} {1}={2}\n".format( + resource_type, resource_selector, _sanitize_value(value) + ) + ) else: fp_.write("select {0}\n".format(resource_type)) for k, v in six.iteritems(kwargs): - if methode in ['update'] and k == resource_selector: + if methode in ["update"] and k == resource_selector: continue if isinstance(v, dict) or isinstance(v, list): value = _sanitize_value(value) - value = six.text_type(v).lower() if isinstance(v, bool) else six.text_type(v) + value = ( + six.text_type(v).lower() if isinstance(v, bool) else six.text_type(v) + ) if k in _zonecfg_resource_setters[resource_type]: fp_.write("set {0}={1}\n".format(k, _sanitize_value(value))) else: @@ -552,26 +564,25 @@ def _resource(methode, zone, resource_type, resource_selector, **kwargs): # update property if cfg_file: _dump_cfg(cfg_file) - res = __salt__['cmd.run_all']('zonecfg -z {zone} -f {path}'.format( - zone=zone, - path=cfg_file, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zonecfg -z {zone} -f {path}".format(zone=zone, path=cfg_file,) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + if ret["message"] == "": + del ret["message"] else: - ret['message'] = _clean_message(ret['message']) + ret["message"] = _clean_message(ret["message"]) # cleanup config file - if __salt__['file.file_exists'](cfg_file): - __salt__['file.remove'](cfg_file) + if __salt__["file.file_exists"](cfg_file): + __salt__["file.remove"](cfg_file) return ret def add_resource(zone, resource_type, **kwargs): - ''' + """ Add a resource zone : string @@ -586,12 +597,12 @@ def add_resource(zone, resource_type, **kwargs): .. code-block:: bash salt '*' zonecfg.add_resource tallgeese rctl name=zone.max-locked-memory value='(priv=privileged,limit=33554432,action=deny)' - ''' - return _resource('add', zone, resource_type, None, **kwargs) + """ + return _resource("add", zone, resource_type, None, **kwargs) def update_resource(zone, resource_type, resource_selector, **kwargs): - ''' + """ Add a resource zone : string @@ -611,12 +622,12 @@ def update_resource(zone, resource_type, resource_selector, **kwargs): .. code-block:: bash salt '*' zonecfg.update_resource tallgeese rctl name name=zone.max-locked-memory value='(priv=privileged,limit=33554432,action=deny)' - ''' - return _resource('update', zone, resource_type, resource_selector, **kwargs) + """ + return _resource("update", zone, resource_type, resource_selector, **kwargs) def remove_resource(zone, resource_type, resource_key, resource_value): - ''' + """ Remove a resource zone : string @@ -636,40 +647,43 @@ def remove_resource(zone, resource_type, resource_key, resource_value): .. code-block:: bash salt '*' zonecfg.remove_resource tallgeese rctl name zone.max-locked-memory - ''' - ret = {'status': True} + """ + ret = {"status": True} # generate update script cfg_file = salt.utils.files.mkstemp() - with salt.utils.files.fpopen(cfg_file, 'w+', mode=0o600) as fp_: + with salt.utils.files.fpopen(cfg_file, "w+", mode=0o600) as fp_: if resource_key: - fp_.write("remove {0} {1}={2}\n".format(resource_type, resource_key, _sanitize_value(resource_value))) + fp_.write( + "remove {0} {1}={2}\n".format( + resource_type, resource_key, _sanitize_value(resource_value) + ) + ) else: fp_.write("remove {0}\n".format(resource_type)) # update property if cfg_file: _dump_cfg(cfg_file) - res = __salt__['cmd.run_all']('zonecfg -z {zone} -f {path}'.format( - zone=zone, - path=cfg_file, - )) - ret['status'] = res['retcode'] == 0 - ret['message'] = res['stdout'] if ret['status'] else res['stderr'] - if ret['message'] == '': - del ret['message'] + res = __salt__["cmd.run_all"]( + "zonecfg -z {zone} -f {path}".format(zone=zone, path=cfg_file,) + ) + ret["status"] = res["retcode"] == 0 + ret["message"] = res["stdout"] if ret["status"] else res["stderr"] + if ret["message"] == "": + del ret["message"] else: - ret['message'] = _clean_message(ret['message']) + ret["message"] = _clean_message(ret["message"]) # cleanup config file - if __salt__['file.file_exists'](cfg_file): - __salt__['file.remove'](cfg_file) + if __salt__["file.file_exists"](cfg_file): + __salt__["file.remove"](cfg_file) return ret def info(zone, show_all=False): - ''' + """ Display the configuration from memory zone : string @@ -682,31 +696,29 @@ def info(zone, show_all=False): .. code-block:: bash salt '*' zonecfg.info tallgeese - ''' + """ ret = {} # dump zone - res = __salt__['cmd.run_all']('zonecfg -z {zone} info'.format( - zone=zone, - )) - if res['retcode'] == 0: + res = __salt__["cmd.run_all"]("zonecfg -z {zone} info".format(zone=zone,)) + if res["retcode"] == 0: # parse output resname = None resdata = {} - for line in res['stdout'].split("\n"): + for line in res["stdout"].split("\n"): # skip some bad data - if ':' not in line: + if ":" not in line: continue # skip calculated values (if requested) - if line.startswith('['): + if line.startswith("["): if not show_all: continue line = line.rstrip()[1:-1] # extract key - key = line.strip().split(':')[0] - if '[' in key: + key = line.strip().split(":")[0] + if "[" in key: key = key[1:] # parse calculated resource (if requested) @@ -732,40 +744,43 @@ def info(zone, show_all=False): # store resource property elif line.startswith("\t"): # ship calculated values (if requested) - if line.strip().startswith('['): + if line.strip().startswith("["): if not show_all: continue line = line.strip()[1:-1] - if key == 'property': # handle special 'property' keys - if 'property' not in resdata: + if key == "property": # handle special 'property' keys + if "property" not in resdata: resdata[key] = {} - kv = _parse_value(line.strip()[line.strip().index(':')+1:]) - if 'name' in kv and 'value' in kv: - resdata[key][kv['name']] = kv['value'] + kv = _parse_value(line.strip()[line.strip().index(":") + 1 :]) + if "name" in kv and "value" in kv: + resdata[key][kv["name"]] = kv["value"] else: - log.warning('zonecfg.info - not sure how to deal with: %s', kv) + log.warning("zonecfg.info - not sure how to deal with: %s", kv) else: - resdata[key] = _parse_value(line.strip()[line.strip().index(':')+1:]) + resdata[key] = _parse_value( + line.strip()[line.strip().index(":") + 1 :] + ) # store property else: if resname: ret[resname].append(resdata) resname = None resdata = {} - if key == 'property': # handle special 'property' keys - if 'property' not in ret: + if key == "property": # handle special 'property' keys + if "property" not in ret: ret[key] = {} - kv = _parse_value(line.strip()[line.strip().index(':')+1:]) - if 'name' in kv and 'value' in kv: - res[key][kv['name']] = kv['value'] + kv = _parse_value(line.strip()[line.strip().index(":") + 1 :]) + if "name" in kv and "value" in kv: + res[key][kv["name"]] = kv["value"] else: - log.warning('zonecfg.info - not sure how to deal with: %s', kv) + log.warning("zonecfg.info - not sure how to deal with: %s", kv) else: - ret[key] = _parse_value(line.strip()[line.strip().index(':')+1:]) + ret[key] = _parse_value(line.strip()[line.strip().index(":") + 1 :]) # store hanging resource if resname: ret[resname].append(resdata) return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/modules/zookeeper.py b/salt/modules/zookeeper.py index 7f9014d10f4..7ea1ad96d48 100644 --- a/salt/modules/zookeeper.py +++ b/salt/modules/zookeeper.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Zookeeper Module ~~~~~~~~~~~~~~~~ :maintainer: SaltStack @@ -63,56 +63,60 @@ Configuration admin: true username: daniel password: test -''' +""" from __future__ import absolute_import, print_function, unicode_literals +# Import Salt libraries +import salt.utils.stringutils + # Import python libraries try: import kazoo.client import kazoo.security + HAS_KAZOO = True except ImportError: HAS_KAZOO = False -# Import Salt libraries -import salt.utils.stringutils -__virtualname__ = 'zookeeper' +__virtualname__ = "zookeeper" def __virtual__(): if HAS_KAZOO: return __virtualname__ - return False + return (False, "Missing dependency: kazoo") def _get_zk_conn(profile=None, **connection_args): if profile: - prefix = 'zookeeper:' + profile + prefix = "zookeeper:" + profile else: - prefix = 'zookeeper' + prefix = "zookeeper" def get(key, default=None): - ''' + """ look in connection_args first, then default to config file - ''' - return connection_args.get(key) or __salt__['config.get'](':'.join([prefix, key]), default) + """ + return connection_args.get(key) or __salt__["config.get"]( + ":".join([prefix, key]), default + ) - hosts = get('hosts', '127.0.0.1:2181') - scheme = get('scheme', None) - username = get('username', None) - password = get('password', None) - default_acl = get('default_acl', None) + hosts = get("hosts", "127.0.0.1:2181") + scheme = get("scheme", None) + username = get("username", None) + password = get("password", None) + default_acl = get("default_acl", None) if isinstance(hosts, list): - hosts = ','.join(hosts) + hosts = ",".join(hosts) if username is not None and password is not None and scheme is None: - scheme = 'digest' + scheme = "digest" auth_data = None if scheme and username and password: - auth_data = [(scheme, ':'.join([username, password]))] + auth_data = [(scheme, ":".join([username, password]))] if default_acl is not None: if isinstance(default_acl, list): @@ -120,20 +124,34 @@ def _get_zk_conn(profile=None, **connection_args): else: default_acl = [make_digest_acl(**default_acl)] - __context__.setdefault('zkconnection', {}).setdefault(profile or hosts, - kazoo.client.KazooClient(hosts=hosts, - default_acl=default_acl, - auth_data=auth_data)) + __context__.setdefault("zkconnection", {}).setdefault( + profile or hosts, + kazoo.client.KazooClient( + hosts=hosts, default_acl=default_acl, auth_data=auth_data + ), + ) - if not __context__['zkconnection'][profile or hosts].connected: - __context__['zkconnection'][profile or hosts].start() + if not __context__["zkconnection"][profile or hosts].connected: + __context__["zkconnection"][profile or hosts].start() - return __context__['zkconnection'][profile or hosts] + return __context__["zkconnection"][profile or hosts] -def create(path, value='', acls=None, ephemeral=False, sequence=False, makepath=False, profile=None, - hosts=None, scheme=None, username=None, password=None, default_acl=None): - ''' +def create( + path, + value="", + acls=None, + ephemeral=False, + sequence=False, + makepath=False, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Create Znode path @@ -178,18 +196,39 @@ def create(path, value='', acls=None, ephemeral=False, sequence=False, makepath= salt minion1 zookeeper.create /test/name daniel profile=prod - ''' + """ if acls is None: acls = [] acls = [make_digest_acl(**acl) for acl in acls] - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) - return conn.create(path, salt.utils.stringutils.to_bytes(value), acls, ephemeral, sequence, makepath) + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) + return conn.create( + path, + salt.utils.stringutils.to_bytes(value), + acls, + ephemeral, + sequence, + makepath, + ) -def ensure_path(path, acls=None, profile=None, hosts=None, scheme=None, - username=None, password=None, default_acl=None): - ''' +def ensure_path( + path, + acls=None, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Ensure Znode path exists path @@ -222,17 +261,31 @@ def ensure_path(path, acls=None, profile=None, hosts=None, scheme=None, salt minion1 zookeeper.ensure_path /test/name profile=prod - ''' + """ if acls is None: acls = [] acls = [make_digest_acl(**acl) for acl in acls] - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) return conn.ensure_path(path, acls) -def exists(path, profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None): - ''' +def exists( + path, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Check if path exists path @@ -262,14 +315,28 @@ def exists(path, profile=None, hosts=None, scheme=None, username=None, password= salt minion1 zookeeper.exists /test/name profile=prod - ''' - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + """ + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) return bool(conn.exists(path)) -def get(path, profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None): - ''' +def get( + path, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Get value saved in znode path @@ -299,15 +366,29 @@ def get(path, profile=None, hosts=None, scheme=None, username=None, password=Non salt minion1 zookeeper.get /test/name profile=prod - ''' - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + """ + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) ret, _ = conn.get(path) return salt.utils.stringutils.to_str(ret) -def get_children(path, profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None): - ''' +def get_children( + path, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Get children in znode path path @@ -337,16 +418,31 @@ def get_children(path, profile=None, hosts=None, scheme=None, username=None, pas salt minion1 zookeeper.get_children /test profile=prod - ''' - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + """ + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) ret = conn.get_children(path) return ret or [] -def set(path, value, version=-1, profile=None, hosts=None, scheme=None, - username=None, password=None, default_acl=None): - ''' +def set( + path, + value, + version=-1, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Update znode with new value path @@ -382,14 +478,28 @@ def set(path, value, version=-1, profile=None, hosts=None, scheme=None, salt minion1 zookeeper.set /test/name gtmanfred profile=prod - ''' - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + """ + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) return conn.set(path, salt.utils.stringutils.to_bytes(value), version=version) -def get_acls(path, profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None): - ''' +def get_acls( + path, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Get acls on a znode path @@ -419,15 +529,30 @@ def get_acls(path, profile=None, hosts=None, scheme=None, username=None, passwor salt minion1 zookeeper.get_acls /test/name profile=prod - ''' - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + """ + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) return conn.get_acls(path)[0] -def set_acls(path, acls, version=-1, profile=None, hosts=None, scheme=None, - username=None, password=None, default_acl=None): - ''' +def set_acls( + path, + acls, + version=-1, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Set acls on a znode path @@ -463,20 +588,41 @@ def set_acls(path, acls, version=-1, profile=None, hosts=None, scheme=None, salt minion1 zookeeper.set_acls /test/name acls='[{"username": "gtmanfred", "password": "test", "all": True}]' profile=prod - ''' - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + """ + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) if acls is None: acls = [] acls = [make_digest_acl(**acl) for acl in acls] - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) return conn.set_acls(path, acls, version) -def delete(path, version=-1, recursive=False, profile=None, hosts=None, scheme=None, - username=None, password=None, default_acl=None): - ''' +def delete( + path, + version=-1, + recursive=False, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Delete znode path @@ -509,15 +655,29 @@ def delete(path, version=-1, recursive=False, profile=None, hosts=None, scheme=N salt minion1 zookeeper.delete /test/name profile=prod - ''' - conn = _get_zk_conn(profile=profile, hosts=hosts, scheme=scheme, - username=username, password=password, default_acl=default_acl) + """ + conn = _get_zk_conn( + profile=profile, + hosts=hosts, + scheme=scheme, + username=username, + password=password, + default_acl=default_acl, + ) return conn.delete(path, version, recursive) -def make_digest_acl(username, password, read=False, write=False, create=False, delete=False, admin=False, - allperms=False): - ''' +def make_digest_acl( + username, + password, + read=False, + write=False, + create=False, + delete=False, + admin=False, + allperms=False, +): + """ Generate acl object .. note:: This is heavily used in the zookeeper state and probably is not useful as a cli module @@ -551,5 +711,7 @@ def make_digest_acl(username, password, read=False, write=False, create=False, d .. code-block:: bash salt minion1 zookeeper.make_digest_acl username=daniel password=mypass allperms=True - ''' - return kazoo.security.make_digest_acl(username, password, read, write, create, delete, admin, allperms) + """ + return kazoo.security.make_digest_acl( + username, password, read, write, create, delete, admin, allperms + ) diff --git a/salt/modules/zpool.py b/salt/modules/zpool.py index bfd879d352d..e308d7475a9 100644 --- a/salt/modules/zpool.py +++ b/salt/modules/zpool.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for running ZFS zpool command :codeauthor: Nitin Madhok <nmadhok@clemson.edu>, Jorge Schrauwen <sjorge@blackdot.be> @@ -12,12 +12,13 @@ Module for running ZFS zpool command Big refactor to remove duplicate code, better type conversions and improved consistency in output. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import Python libs import os -import logging # Import Salt libs import salt.utils.decorators @@ -28,30 +29,30 @@ from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) -__virtualname__ = 'zpool' +__virtualname__ = "zpool" __func_alias__ = { - 'import_': 'import', - 'list_': 'list', + "import_": "import", + "list_": "list", } def __virtual__(): - ''' + """ Only load when the platform has zfs support - ''' - if __grains__.get('zfs_support'): + """ + if __grains__.get("zfs_support"): return __virtualname__ else: return False, "The zpool module cannot be loaded: zfs not supported" def _clean_vdev_config(config): - ''' + """ Return a simple vdev tree from zpool.status' config section - ''' + """ cln_config = OrderedDict() for label, sub_config in config.items(): - if label not in ['state', 'read', 'write', 'cksum']: + if label not in ["state", "read", "write", "cksum"]: sub_config = _clean_vdev_config(sub_config) if sub_config and isinstance(cln_config, list): @@ -73,7 +74,7 @@ def _clean_vdev_config(config): def healthy(): - ''' + """ Check if all zpools are healthy .. versionadded:: 2016.3.0 @@ -84,21 +85,20 @@ def healthy(): salt '*' zpool.healthy - ''' + """ ## collect status output # NOTE: we pass the -x flag, by doing this # we will get 'all pools are healthy' on stdout # if all pools are healthy, otherwise we will get # the same output that we expect from zpool status - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']('status', flags=['-x']), - python_shell=False, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]("status", flags=["-x"]), python_shell=False, ) - return res['stdout'] == 'all pools are healthy' + return res["stdout"] == "all pools are healthy" def status(zpool=None): - ''' + """ Return the status of the named zpool zpool : string @@ -112,17 +112,16 @@ def status(zpool=None): salt '*' zpool.status myzpool - ''' + """ ret = OrderedDict() ## collect status output - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']('status', target=zpool), - python_shell=False, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]("status", target=zpool), python_shell=False, ) - if res['retcode'] != 0: - return __utils__['zfs.parse_command_result'](res) + if res["retcode"] != 0: + return __utils__["zfs.parse_command_result"](res) # NOTE: command output for reference # ===================================================================== @@ -147,17 +146,17 @@ def status(zpool=None): # we see 'pool:' current_pool = None current_prop = None - for zpd in res['stdout'].splitlines(): - if zpd.strip() == '': + for zpd in res["stdout"].splitlines(): + if zpd.strip() == "": continue - if ':' in zpd: + if ":" in zpd: # NOTE: line is 'key: value' format, we just update a dict - prop = zpd.split(':')[0].strip() - value = ":".join(zpd.split(':')[1:]).strip() - if prop == 'pool' and current_pool != value: + prop = zpd.split(":")[0].strip() + value = ":".join(zpd.split(":")[1:]).strip() + if prop == "pool" and current_pool != value: current_pool = value ret[current_pool] = OrderedDict() - if prop != 'pool': + if prop != "pool": ret[current_pool][prop] = value current_prop = prop @@ -166,8 +165,7 @@ def status(zpool=None): # this should only happens once we hit the config # section ret[current_pool][current_prop] = "{0}\n{1}".format( - ret[current_pool][current_prop], - zpd + ret[current_pool][current_prop], zpd ) ## parse config property for each pool @@ -175,21 +173,21 @@ def status(zpool=None): # sadly this data is in a different format than # the rest and it needs further processing for pool in ret: - if 'config' not in ret[pool]: + if "config" not in ret[pool]: continue header = None root_vdev = None vdev = None dev = None rdev = None - config = ret[pool]['config'] + config = ret[pool]["config"] config_data = OrderedDict() for line in config.splitlines(): # NOTE: the first line is the header # we grab all the none whitespace values if not header: header = line.strip().lower() - header = [x for x in header.split(' ') if x not in ['']] + header = [x for x in header.split(" ") if x not in [""]] continue # NOTE: data is indented by 1 tab, then multiples of 2 spaces @@ -200,41 +198,42 @@ def status(zpool=None): line = line[1:] # NOTE: transform data into dict - stat_data = OrderedDict(list(zip( - header, - [x for x in line.strip().split(' ') if x not in ['']], - ))) + stat_data = OrderedDict( + list( + zip(header, [x for x in line.strip().split(" ") if x not in [""]],) + ) + ) # NOTE: decode the zfs values properly - stat_data = __utils__['zfs.from_auto_dict'](stat_data) + stat_data = __utils__["zfs.from_auto_dict"](stat_data) # NOTE: store stat_data in the proper location - if line.startswith(' ' * 6): - rdev = stat_data['name'] + if line.startswith(" " * 6): + rdev = stat_data["name"] config_data[root_vdev][vdev][dev][rdev] = stat_data - elif line.startswith(' ' * 4): + elif line.startswith(" " * 4): rdev = None - dev = stat_data['name'] + dev = stat_data["name"] config_data[root_vdev][vdev][dev] = stat_data - elif line.startswith(' ' * 2): + elif line.startswith(" " * 2): rdev = dev = None - vdev = stat_data['name'] + vdev = stat_data["name"] config_data[root_vdev][vdev] = stat_data else: rdev = dev = vdev = None - root_vdev = stat_data['name'] + root_vdev = stat_data["name"] config_data[root_vdev] = stat_data # NOTE: name already used as identifier, drop duplicate data - del stat_data['name'] + del stat_data["name"] - ret[pool]['config'] = config_data + ret[pool]["config"] = config_data return ret def iostat(zpool=None, sample_time=5, parsable=True): - ''' + """ Display I/O statistics for the given pools zpool : string @@ -257,21 +256,19 @@ def iostat(zpool=None, sample_time=5, parsable=True): salt '*' zpool.iostat myzpool - ''' + """ ret = OrderedDict() ## get iostat output - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='iostat', - flags=['-v'], - target=[zpool, sample_time, 2] + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="iostat", flags=["-v"], target=[zpool, sample_time, 2] ), python_shell=False, ) - if res['retcode'] != 0: - return __utils__['zfs.parse_command_result'](res) + if res["retcode"] != 0: + return __utils__["zfs.parse_command_result"](res) # NOTE: command output for reference # ===================================================================== @@ -291,62 +288,63 @@ def iostat(zpool=None, sample_time=5, parsable=True): # the double header line is hard to parse, we opt to # hardcode the header fields header = [ - 'name', - 'capacity-alloc', 'capacity-free', - 'operations-read', 'operations-write', - 'bandwith-read', 'bandwith-write', + "name", + "capacity-alloc", + "capacity-free", + "operations-read", + "operations-write", + "bandwith-read", + "bandwith-write", ] root_vdev = None vdev = None dev = None current_data = OrderedDict() - for line in res['stdout'].splitlines(): + for line in res["stdout"].splitlines(): # NOTE: skip header - if line.strip() == '' or \ - line.strip().split()[-1] in ['write', 'bandwidth']: + if line.strip() == "" or line.strip().split()[-1] in ["write", "bandwidth"]: continue # NOTE: reset pool on line separator - if line.startswith('-') and line.endswith('-'): + if line.startswith("-") and line.endswith("-"): ret.update(current_data) current_data = OrderedDict() continue # NOTE: transform data into dict - io_data = OrderedDict(list(zip( - header, - [x for x in line.strip().split(' ') if x not in ['']], - ))) + io_data = OrderedDict( + list(zip(header, [x for x in line.strip().split(" ") if x not in [""]],)) + ) # NOTE: normalize values if parsable: # NOTE: raw numbers and pythonic types - io_data = __utils__['zfs.from_auto_dict'](io_data) + io_data = __utils__["zfs.from_auto_dict"](io_data) else: # NOTE: human readable zfs types - io_data = __utils__['zfs.to_auto_dict'](io_data) + io_data = __utils__["zfs.to_auto_dict"](io_data) # NOTE: store io_data in the proper location - if line.startswith(' ' * 4): - dev = io_data['name'] + if line.startswith(" " * 4): + dev = io_data["name"] current_data[root_vdev][vdev][dev] = io_data - elif line.startswith(' ' * 2): + elif line.startswith(" " * 2): dev = None - vdev = io_data['name'] + vdev = io_data["name"] current_data[root_vdev][vdev] = io_data else: dev = vdev = None - root_vdev = io_data['name'] + root_vdev = io_data["name"] current_data[root_vdev] = io_data # NOTE: name already used as identifier, drop duplicate data - del io_data['name'] + del io_data["name"] return ret -def list_(properties='size,alloc,free,cap,frag,health', zpool=None, parsable=True): - ''' +def list_(properties="size,alloc,free,cap,frag,health", zpool=None, parsable=True): + """ .. versionadded:: 2015.5.0 Return information about (all) storage pools @@ -383,37 +381,37 @@ def list_(properties='size,alloc,free,cap,frag,health', zpool=None, parsable=Tru salt '*' zpool.list 'size,free' salt '*' zpool.list 'size,free' tank - ''' + """ ret = OrderedDict() ## update properties # NOTE: properties should be a list if not isinstance(properties, list): - properties = properties.split(',') + properties = properties.split(",") # NOTE: name should be first property - while 'name' in properties: - properties.remove('name') - properties.insert(0, 'name') + while "name" in properties: + properties.remove("name") + properties.insert(0, "name") # NOTE: remove 'frags' if we don't have feature flags - if not __utils__['zfs.has_feature_flags'](): - while 'frag' in properties: - properties.remove('frag') + if not __utils__["zfs.has_feature_flags"](): + while "frag" in properties: + properties.remove("frag") ## collect list output - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='list', - flags=['-H'], - opts={'-o': ','.join(properties)}, - target=zpool + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="list", + flags=["-H"], + opts={"-o": ",".join(properties)}, + target=zpool, ), python_shell=False, ) - if res['retcode'] != 0: - return __utils__['zfs.parse_command_result'](res) + if res["retcode"] != 0: + return __utils__["zfs.parse_command_result"](res) # NOTE: command output for reference # ======================================================================== @@ -421,29 +419,26 @@ def list_(properties='size,alloc,free,cap,frag,health', zpool=None, parsable=Tru # ========================================================================= ## parse list output - for line in res['stdout'].splitlines(): + for line in res["stdout"].splitlines(): # NOTE: transform data into dict - zpool_data = OrderedDict(list(zip( - properties, - line.strip().split('\t'), - ))) + zpool_data = OrderedDict(list(zip(properties, line.strip().split("\t"),))) # NOTE: normalize values if parsable: # NOTE: raw numbers and pythonic types - zpool_data = __utils__['zfs.from_auto_dict'](zpool_data) + zpool_data = __utils__["zfs.from_auto_dict"](zpool_data) else: # NOTE: human readable zfs types - zpool_data = __utils__['zfs.to_auto_dict'](zpool_data) + zpool_data = __utils__["zfs.to_auto_dict"](zpool_data) - ret[zpool_data['name']] = zpool_data - del ret[zpool_data['name']]['name'] + ret[zpool_data["name"]] = zpool_data + del ret[zpool_data["name"]]["name"] return ret def get(zpool, prop=None, show_source=False, parsable=True): - ''' + """ .. versionadded:: 2016.3.0 Retrieves the given list of properties @@ -468,23 +463,23 @@ def get(zpool, prop=None, show_source=False, parsable=True): salt '*' zpool.get myzpool - ''' + """ ret = OrderedDict() - value_properties = ['name', 'property', 'value', 'source'] + value_properties = ["name", "property", "value", "source"] ## collect get output - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='get', - flags=['-H'], - property_name=prop if prop else 'all', + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="get", + flags=["-H"], + property_name=prop if prop else "all", target=zpool, ), python_shell=False, ) - if res['retcode'] != 0: - return __utils__['zfs.parse_command_result'](res) + if res["retcode"] != 0: + return __utils__["zfs.parse_command_result"](res) # NOTE: command output for reference # ======================================================================== @@ -495,36 +490,44 @@ def get(zpool, prop=None, show_source=False, parsable=True): # ========================================================================= # parse get output - for line in res['stdout'].splitlines(): + for line in res["stdout"].splitlines(): # NOTE: transform data into dict - prop_data = OrderedDict(list(zip( - value_properties, - [x for x in line.strip().split('\t') if x not in ['']], - ))) + prop_data = OrderedDict( + list( + zip( + value_properties, + [x for x in line.strip().split("\t") if x not in [""]], + ) + ) + ) # NOTE: older zfs does not have -o, fall back to manually stipping the name field - del prop_data['name'] + del prop_data["name"] # NOTE: normalize values if parsable: # NOTE: raw numbers and pythonic types - prop_data['value'] = __utils__['zfs.from_auto'](prop_data['property'], prop_data['value']) + prop_data["value"] = __utils__["zfs.from_auto"]( + prop_data["property"], prop_data["value"] + ) else: # NOTE: human readable zfs types - prop_data['value'] = __utils__['zfs.to_auto'](prop_data['property'], prop_data['value']) + prop_data["value"] = __utils__["zfs.to_auto"]( + prop_data["property"], prop_data["value"] + ) # NOTE: show source if requested if show_source: - ret[prop_data['property']] = prop_data - del ret[prop_data['property']]['property'] + ret[prop_data["property"]] = prop_data + del ret[prop_data["property"]]["property"] else: - ret[prop_data['property']] = prop_data['value'] + ret[prop_data["property"]] = prop_data["value"] return ret def set(zpool, prop, value): - ''' + """ Sets the given property on the specified pool zpool : string @@ -544,25 +547,22 @@ def set(zpool, prop, value): salt '*' zpool.set myzpool readonly yes - ''' + """ ret = OrderedDict() # set property - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='set', - property_name=prop, - property_value=value, - target=zpool, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="set", property_name=prop, property_value=value, target=zpool, ), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'set') + return __utils__["zfs.parse_command_result"](res, "set") def exists(zpool): - ''' + """ Check if a ZFS storage pool is active zpool : string @@ -574,23 +574,20 @@ def exists(zpool): salt '*' zpool.exists myzpool - ''' + """ # list for zpool # NOTE: retcode > 0 if zpool does not exists - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='list', - target=zpool, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="list", target=zpool,), python_shell=False, ignore_retcode=True, ) - return res['retcode'] == 0 + return res["retcode"] == 0 def destroy(zpool, force=False): - ''' + """ Destroys a storage pool zpool : string @@ -605,22 +602,20 @@ def destroy(zpool, force=False): salt '*' zpool.destroy myzpool - ''' + """ # destroy zpool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='destroy', - flags=['-f'] if force else None, - target=zpool, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="destroy", flags=["-f"] if force else None, target=zpool, ), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'destroyed') + return __utils__["zfs.parse_command_result"](res, "destroyed") def scrub(zpool, stop=False, pause=False): - ''' + """ Scrub a storage pool zpool : string @@ -647,38 +642,34 @@ def scrub(zpool, stop=False, pause=False): salt '*' zpool.scrub myzpool - ''' + """ ## select correct action if stop: - action = ['-s'] + action = ["-s"] elif pause: - action = ['-p'] + action = ["-p"] else: action = None ## Scrub storage pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='scrub', - flags=action, - target=zpool, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="scrub", flags=action, target=zpool,), python_shell=False, ) - if res['retcode'] != 0: - return __utils__['zfs.parse_command_result'](res, 'scrubbing') + if res["retcode"] != 0: + return __utils__["zfs.parse_command_result"](res, "scrubbing") ret = OrderedDict() if stop or pause: - ret['scrubbing'] = False + ret["scrubbing"] = False else: - ret['scrubbing'] = True + ret["scrubbing"] = True return ret def create(zpool, *vdevs, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 Create a simple zpool, a mirrored zpool, a zpool having nested VDEVs, a hybrid zpool with cache, spare and log drives or a zpool with RAIDZ-1, RAIDZ-2 or RAIDZ-3 @@ -759,7 +750,7 @@ def create(zpool, *vdevs, **kwargs): salt '*' zpool.create myzpool mirror /path/to/vdev1 [...] mirror /path/to/vdev2 /path/to/vdev3 [...] [force=True|False] salt '*' zpool.create myhybridzpool mirror /tmp/file1 [...] log mirror /path/to/vdev1 [...] cache /path/to/vdev2 [...] spare /path/to/vdev3 [...] [force=True|False] - ''' + """ ## Configure pool # NOTE: initialize the defaults flags = [] @@ -767,27 +758,27 @@ def create(zpool, *vdevs, **kwargs): target = [] # NOTE: push pool and filesystem properties - pool_properties = kwargs.get('properties', {}) - filesystem_properties = kwargs.get('filesystem_properties', {}) + pool_properties = kwargs.get("properties", {}) + filesystem_properties = kwargs.get("filesystem_properties", {}) # NOTE: set extra config based on kwargs - if kwargs.get('force', False): - flags.append('-f') - if kwargs.get('createboot', False) or 'bootsize' in pool_properties: - flags.append('-B') - if kwargs.get('altroot', False): - opts['-R'] = kwargs.get('altroot') - if kwargs.get('mountpoint', False): - opts['-m'] = kwargs.get('mountpoint') + if kwargs.get("force", False): + flags.append("-f") + if kwargs.get("createboot", False) or "bootsize" in pool_properties: + flags.append("-B") + if kwargs.get("altroot", False): + opts["-R"] = kwargs.get("altroot") + if kwargs.get("mountpoint", False): + opts["-m"] = kwargs.get("mountpoint") # NOTE: append the pool name and specifications target.append(zpool) target.extend(vdevs) ## Create storage pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='create', + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="create", flags=flags, opts=opts, pool_properties=pool_properties, @@ -797,18 +788,18 @@ def create(zpool, *vdevs, **kwargs): python_shell=False, ) - ret = __utils__['zfs.parse_command_result'](res, 'created') - if ret['created']: + ret = __utils__["zfs.parse_command_result"](res, "created") + if ret["created"]: ## NOTE: lookup zpool status for vdev config - ret['vdevs'] = _clean_vdev_config( - __salt__['zpool.status'](zpool=zpool)[zpool]['config'][zpool], + ret["vdevs"] = _clean_vdev_config( + __salt__["zpool.status"](zpool=zpool)[zpool]["config"][zpool], ) return ret def add(zpool, *vdevs, **kwargs): - ''' + """ Add the specified vdev\'s to the given storage pool zpool : string @@ -826,42 +817,38 @@ def add(zpool, *vdevs, **kwargs): salt '*' zpool.add myzpool /path/to/vdev1 /path/to/vdev2 [...] - ''' + """ ## Configure pool # NOTE: initialize the defaults flags = [] target = [] # NOTE: set extra config based on kwargs - if kwargs.get('force', False): - flags.append('-f') + if kwargs.get("force", False): + flags.append("-f") # NOTE: append the pool name and specifications target.append(zpool) target.extend(vdevs) ## Update storage pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='add', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="add", flags=flags, target=target,), python_shell=False, ) - ret = __utils__['zfs.parse_command_result'](res, 'added') - if ret['added']: + ret = __utils__["zfs.parse_command_result"](res, "added") + if ret["added"]: ## NOTE: lookup zpool status for vdev config - ret['vdevs'] = _clean_vdev_config( - __salt__['zpool.status'](zpool=zpool)[zpool]['config'][zpool], + ret["vdevs"] = _clean_vdev_config( + __salt__["zpool.status"](zpool=zpool)[zpool]["config"][zpool], ) return ret def attach(zpool, device, new_device, force=False): - ''' + """ Attach specified device to zpool zpool : string @@ -882,7 +869,7 @@ def attach(zpool, device, new_device, force=False): salt '*' zpool.attach myzpool /path/to/vdev1 /path/to/vdev2 [...] - ''' + """ ## Configure pool # NOTE: initialize the defaults flags = [] @@ -890,7 +877,7 @@ def attach(zpool, device, new_device, force=False): # NOTE: set extra config if force: - flags.append('-f') + flags.append("-f") # NOTE: append the pool name and specifications target.append(zpool) @@ -898,27 +885,23 @@ def attach(zpool, device, new_device, force=False): target.append(new_device) ## Update storage pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='attach', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="attach", flags=flags, target=target,), python_shell=False, ) - ret = __utils__['zfs.parse_command_result'](res, 'attached') - if ret['attached']: + ret = __utils__["zfs.parse_command_result"](res, "attached") + if ret["attached"]: ## NOTE: lookup zpool status for vdev config - ret['vdevs'] = _clean_vdev_config( - __salt__['zpool.status'](zpool=zpool)[zpool]['config'][zpool], + ret["vdevs"] = _clean_vdev_config( + __salt__["zpool.status"](zpool=zpool)[zpool]["config"][zpool], ) return ret def detach(zpool, device): - ''' + """ Detach specified device to zpool zpool : string @@ -933,28 +916,25 @@ def detach(zpool, device): salt '*' zpool.detach myzpool /path/to/vdev1 - ''' + """ ## Update storage pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='detach', - target=[zpool, device], - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="detach", target=[zpool, device],), python_shell=False, ) - ret = __utils__['zfs.parse_command_result'](res, 'detatched') - if ret['detatched']: + ret = __utils__["zfs.parse_command_result"](res, "detatched") + if ret["detatched"]: ## NOTE: lookup zpool status for vdev config - ret['vdevs'] = _clean_vdev_config( - __salt__['zpool.status'](zpool=zpool)[zpool]['config'][zpool], + ret["vdevs"] = _clean_vdev_config( + __salt__["zpool.status"](zpool=zpool)[zpool]["config"][zpool], ) return ret def split(zpool, newzpool, **kwargs): - ''' + """ .. versionadded:: 2018.3.0 Splits devices off pool creating newpool. @@ -1012,22 +992,22 @@ def split(zpool, newzpool, **kwargs): salt '*' zpool.split datamirror databackup salt '*' zpool.split datamirror databackup altroot=/backup - ''' + """ ## Configure pool # NOTE: initialize the defaults opts = {} # NOTE: push pool and filesystem properties - pool_properties = kwargs.get('properties', {}) + pool_properties = kwargs.get("properties", {}) # NOTE: set extra config based on kwargs - if kwargs.get('altroot', False): - opts['-R'] = kwargs.get('altroot') + if kwargs.get("altroot", False): + opts["-R"] = kwargs.get("altroot") ## Split storage pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='split', + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="split", opts=opts, pool_properties=pool_properties, target=[zpool, newzpool], @@ -1035,11 +1015,11 @@ def split(zpool, newzpool, **kwargs): python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'split') + return __utils__["zfs.parse_command_result"](res, "split") def replace(zpool, old_device, new_device=None, force=False): - ''' + """ Replaces ``old_device`` with ``new_device`` .. note:: @@ -1068,7 +1048,7 @@ def replace(zpool, old_device, new_device=None, force=False): salt '*' zpool.replace myzpool /path/to/vdev1 /path/to/vdev2 - ''' + """ ## Configure pool # NOTE: initialize the defaults flags = [] @@ -1076,7 +1056,7 @@ def replace(zpool, old_device, new_device=None, force=False): # NOTE: set extra config if force: - flags.append('-f') + flags.append("-f") # NOTE: append the pool name and specifications target.append(zpool) @@ -1085,28 +1065,24 @@ def replace(zpool, old_device, new_device=None, force=False): target.append(new_device) ## Replace device - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='replace', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="replace", flags=flags, target=target,), python_shell=False, ) - ret = __utils__['zfs.parse_command_result'](res, 'replaced') - if ret['replaced']: + ret = __utils__["zfs.parse_command_result"](res, "replaced") + if ret["replaced"]: ## NOTE: lookup zpool status for vdev config - ret['vdevs'] = _clean_vdev_config( - __salt__['zpool.status'](zpool=zpool)[zpool]['config'][zpool], + ret["vdevs"] = _clean_vdev_config( + __salt__["zpool.status"](zpool=zpool)[zpool]["config"][zpool], ) return ret -@salt.utils.decorators.path.which('mkfile') +@salt.utils.decorators.path.which("mkfile") def create_file_vdev(size, *vdevs): - ''' + """ Creates file based virtual devices for a zpool CLI Example: @@ -1119,37 +1095,35 @@ def create_file_vdev(size, *vdevs): Depending on file size, the above command may take a while to return. - ''' + """ ret = OrderedDict() err = OrderedDict() - _mkfile_cmd = salt.utils.path.which('mkfile') + _mkfile_cmd = salt.utils.path.which("mkfile") for vdev in vdevs: if os.path.isfile(vdev): - ret[vdev] = 'existed' + ret[vdev] = "existed" else: - res = __salt__['cmd.run_all']( - '{mkfile} {size} {vdev}'.format( - mkfile=_mkfile_cmd, - size=size, - vdev=vdev, + res = __salt__["cmd.run_all"]( + "{mkfile} {size} {vdev}".format( + mkfile=_mkfile_cmd, size=size, vdev=vdev, ), python_shell=False, ) - if res['retcode'] != 0: - if 'stderr' in res and ':' in res['stderr']: - ret[vdev] = 'failed' - err[vdev] = ":".join(res['stderr'].strip().split(':')[1:]) + if res["retcode"] != 0: + if "stderr" in res and ":" in res["stderr"]: + ret[vdev] = "failed" + err[vdev] = ":".join(res["stderr"].strip().split(":")[1:]) else: - ret[vdev] = 'created' + ret[vdev] = "created" if err: - ret['error'] = err + ret["error"] = err return ret def export(*pools, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 Export storage pools @@ -1167,34 +1141,30 @@ def export(*pools, **kwargs): salt '*' zpool.export myzpool ... [force=True|False] salt '*' zpool.export myzpool2 myzpool2 ... [force=True|False] - ''' + """ ## Configure pool # NOTE: initialize the defaults flags = [] targets = [] # NOTE: set extra config based on kwargs - if kwargs.get('force', False): - flags.append('-f') + if kwargs.get("force", False): + flags.append("-f") # NOTE: append the pool name and specifications targets = list(pools) ## Export pools - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='export', - flags=flags, - target=targets, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="export", flags=flags, target=targets,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'exported') + return __utils__["zfs.parse_command_result"](res, "exported") def import_(zpool=None, new_name=None, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 Import storage pools or list pools available for import @@ -1260,7 +1230,7 @@ def import_(zpool=None, new_name=None, **kwargs): salt '*' zpool.import myzpool [mynewzpool] [force=True|False] salt '*' zpool.import myzpool dir='/tmp' - ''' + """ ## Configure pool # NOTE: initialize the defaults flags = [] @@ -1268,43 +1238,43 @@ def import_(zpool=None, new_name=None, **kwargs): target = [] # NOTE: push pool and filesystem properties - pool_properties = kwargs.get('properties', {}) + pool_properties = kwargs.get("properties", {}) # NOTE: set extra config based on kwargs - if kwargs.get('force', False) or kwargs.get('only_destroyed', False): - flags.append('-f') - if kwargs.get('only_destroyed', False): - flags.append('-D') - if kwargs.get('no_mount', False): - flags.append('-N') - if kwargs.get('altroot', False): - opts['-R'] = kwargs.get('altroot') - if kwargs.get('mntopts', False): + if kwargs.get("force", False) or kwargs.get("only_destroyed", False): + flags.append("-f") + if kwargs.get("only_destroyed", False): + flags.append("-D") + if kwargs.get("no_mount", False): + flags.append("-N") + if kwargs.get("altroot", False): + opts["-R"] = kwargs.get("altroot") + if kwargs.get("mntopts", False): # NOTE: -o is used for both mount options and pool properties! # ```-o nodevices,noexec,nosetuid,ro``` vs ```-o prop=val``` - opts['-o'] = kwargs.get('mntopts') - if kwargs.get('dir', False): - opts['-d'] = kwargs.get('dir').split(',') - if kwargs.get('recovery', False) and __utils__['zfs.has_feature_flags'](): - recovery = kwargs.get('recovery') - if recovery in [True, 'test']: - flags.append('-F') - if recovery == 'test': - flags.append('-n') - if recovery == 'nolog': - flags.append('-m') + opts["-o"] = kwargs.get("mntopts") + if kwargs.get("dir", False): + opts["-d"] = kwargs.get("dir").split(",") + if kwargs.get("recovery", False) and __utils__["zfs.has_feature_flags"](): + recovery = kwargs.get("recovery") + if recovery in [True, "test"]: + flags.append("-F") + if recovery == "test": + flags.append("-n") + if recovery == "nolog": + flags.append("-m") # NOTE: append the pool name and specifications if zpool: target.append(zpool) target.append(new_name) else: - flags.append('-a') + flags.append("-a") ## Import storage pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='import', + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="import", flags=flags, opts=opts, pool_properties=pool_properties, @@ -1313,11 +1283,11 @@ def import_(zpool=None, new_name=None, **kwargs): python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'imported') + return __utils__["zfs.parse_command_result"](res, "imported") def online(zpool, *vdevs, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 Ensure that the specified devices are online @@ -1342,15 +1312,15 @@ def online(zpool, *vdevs, **kwargs): salt '*' zpool.online myzpool /path/to/vdev1 [...] - ''' + """ ## Configure pool # default options flags = [] target = [] # set flags and options - if kwargs.get('expand', False): - flags.append('-e') + if kwargs.get("expand", False): + flags.append("-e") target.append(zpool) if vdevs: target.extend(vdevs) @@ -1361,28 +1331,24 @@ def online(zpool, *vdevs, **kwargs): target = [] # NOTE: set extra config based on kwargs - if kwargs.get('expand', False): - flags.append('-e') + if kwargs.get("expand", False): + flags.append("-e") # NOTE: append the pool name and specifications target.append(zpool) target.extend(vdevs) ## Bring online device - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='online', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="online", flags=flags, target=target,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'onlined') + return __utils__["zfs.parse_command_result"](res, "onlined") def offline(zpool, *vdevs, **kwargs): - ''' + """ .. versionadded:: 2015.5.0 Ensure that the specified devices are offline @@ -1408,35 +1374,31 @@ def offline(zpool, *vdevs, **kwargs): salt '*' zpool.offline myzpool /path/to/vdev1 [...] [temporary=True|False] - ''' + """ ## Configure pool # NOTE: initialize the defaults flags = [] target = [] # NOTE: set extra config based on kwargs - if kwargs.get('temporary', False): - flags.append('-t') + if kwargs.get("temporary", False): + flags.append("-t") # NOTE: append the pool name and specifications target.append(zpool) target.extend(vdevs) ## Take a device offline - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='offline', - flags=flags, - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="offline", flags=flags, target=target,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'offlined') + return __utils__["zfs.parse_command_result"](res, "offlined") def labelclear(device, force=False): - ''' + """ .. versionadded:: 2018.3.0 Removes ZFS label information from the specified device @@ -1453,22 +1415,20 @@ def labelclear(device, force=False): salt '*' zpool.labelclear /path/to/dev - ''' + """ ## clear label for all specified device - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='labelclear', - flags=['-f'] if force else None, - target=device, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="labelclear", flags=["-f"] if force else None, target=device, ), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'labelcleared') + return __utils__["zfs.parse_command_result"](res, "labelcleared") def clear(zpool, device=None): - ''' + """ Clears device errors in a pool. .. warning:: @@ -1489,7 +1449,7 @@ def clear(zpool, device=None): salt '*' zpool.clear mypool salt '*' zpool.clear mypool /path/to/dev - ''' + """ ## Configure pool # NOTE: initialize the defaults target = [] @@ -1499,19 +1459,16 @@ def clear(zpool, device=None): target.append(device) ## clear storage pool errors - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='clear', - target=target, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="clear", target=target,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'cleared') + return __utils__["zfs.parse_command_result"](res, "cleared") def reguid(zpool): - ''' + """ Generates a new unique identifier for the pool .. warning:: @@ -1528,21 +1485,18 @@ def reguid(zpool): .. code-block:: bash salt '*' zpool.reguid myzpool - ''' + """ ## generate new GUID for pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='reguid', - target=zpool, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="reguid", target=zpool,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'reguided') + return __utils__["zfs.parse_command_result"](res, "reguided") def reopen(zpool): - ''' + """ Reopen all the vdevs associated with the pool zpool : string @@ -1556,21 +1510,18 @@ def reopen(zpool): salt '*' zpool.reopen myzpool - ''' + """ ## reopen all devices fro pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='reopen', - target=zpool, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="reopen", target=zpool,), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'reopened') + return __utils__["zfs.parse_command_result"](res, "reopened") def upgrade(zpool=None, version=None): - ''' + """ .. versionadded:: 2016.3.0 Enables all supported features on the given pool @@ -1592,7 +1543,7 @@ def upgrade(zpool=None, version=None): salt '*' zpool.upgrade myzpool - ''' + """ ## Configure pool # NOTE: initialize the defaults flags = [] @@ -1600,26 +1551,23 @@ def upgrade(zpool=None, version=None): # NOTE: set extra config if version: - opts['-V'] = version + opts["-V"] = version if not zpool: - flags.append('-a') + flags.append("-a") ## Upgrade pool - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='upgrade', - flags=flags, - opts=opts, - target=zpool, + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"]( + command="upgrade", flags=flags, opts=opts, target=zpool, ), python_shell=False, ) - return __utils__['zfs.parse_command_result'](res, 'upgraded') + return __utils__["zfs.parse_command_result"](res, "upgraded") def history(zpool=None, internal=False, verbose=False): - ''' + """ .. versionadded:: 2016.3.0 Displays the command history of the specified pools, or all pools if no @@ -1641,7 +1589,7 @@ def history(zpool=None, internal=False, verbose=False): salt '*' zpool.upgrade myzpool - ''' + """ ret = OrderedDict() ## Configure pool @@ -1650,30 +1598,26 @@ def history(zpool=None, internal=False, verbose=False): # NOTE: set extra config if verbose: - flags.append('-l') + flags.append("-l") if internal: - flags.append('-i') + flags.append("-i") ## Lookup history - res = __salt__['cmd.run_all']( - __utils__['zfs.zpool_command']( - command='history', - flags=flags, - target=zpool, - ), + res = __salt__["cmd.run_all"]( + __utils__["zfs.zpool_command"](command="history", flags=flags, target=zpool,), python_shell=False, ) - if res['retcode'] != 0: - return __utils__['zfs.parse_command_result'](res) + if res["retcode"] != 0: + return __utils__["zfs.parse_command_result"](res) else: - pool = 'unknown' - for line in res['stdout'].splitlines(): - if line.startswith('History for'): + pool = "unknown" + for line in res["stdout"].splitlines(): + if line.startswith("History for"): pool = line[13:-2] ret[pool] = OrderedDict() else: - if line == '': + if line == "": continue log_timestamp = line[0:19] log_command = line[20:] diff --git a/salt/modules/zypperpkg.py b/salt/modules/zypperpkg.py index 50279ccbd13..128e15de160 100644 --- a/salt/modules/zypperpkg.py +++ b/salt/modules/zypperpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Package support for openSUSE via the zypper package manager :depends: - ``rpm`` Python module. Install with ``zypper install rpm-python`` @@ -10,29 +10,23 @@ Package support for openSUSE via the zypper package manager *'pkg.install' is not available*), see :ref:`here <module-provider-override>`. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime import fnmatch import logging -import re import os +import re import time -import datetime - -# Import 3rd-party libs -# pylint: disable=import-error,redefined-builtin,no-name-in-module -from salt.ext import six -from salt.ext.six.moves import configparser -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse -# pylint: enable=import-error,redefined-builtin,no-name-in-module - from xml.dom import minidom as dom from xml.parsers.expat import ExpatError # Import salt libs import salt.utils.data +import salt.utils.environment import salt.utils.event import salt.utils.files import salt.utils.functools @@ -41,84 +35,95 @@ import salt.utils.pkg import salt.utils.pkg.rpm import salt.utils.stringutils import salt.utils.systemd -from salt.utils.versions import LooseVersion -import salt.utils.environment from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError +# Import 3rd-party libs +# pylint: disable=import-error,redefined-builtin,no-name-in-module +from salt.ext import six +from salt.ext.six.moves import configparser +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse +from salt.utils.versions import LooseVersion + +# pylint: enable=import-error,redefined-builtin,no-name-in-module + + log = logging.getLogger(__name__) HAS_ZYPP = False -ZYPP_HOME = '/etc/zypp' -LOCKS = '{0}/locks'.format(ZYPP_HOME) -REPOS = '{0}/repos.d'.format(ZYPP_HOME) +ZYPP_HOME = "/etc/zypp" +LOCKS = "{0}/locks".format(ZYPP_HOME) +REPOS = "{0}/repos.d".format(ZYPP_HOME) DEFAULT_PRIORITY = 99 -PKG_ARCH_SEPARATOR = '.' +PKG_ARCH_SEPARATOR = "." # Define the module's virtual name -__virtualname__ = 'pkg' +__virtualname__ = "pkg" def __virtual__(): - ''' + """ Set the virtual pkg module if the os is openSUSE - ''' - if __grains__.get('os_family', '') != 'Suse': - return (False, "Module zypper: non SUSE OS not suppored by zypper package manager") + """ + if __grains__.get("os_family", "") != "Suse": + return ( + False, + "Module zypper: non SUSE OS not suppored by zypper package manager", + ) # Not all versions of SUSE use zypper, check that it is available - if not salt.utils.path.which('zypper'): + if not salt.utils.path.which("zypper"): return (False, "Module zypper: zypper package manager not found") return __virtualname__ class _Zypper(object): - ''' + """ Zypper parallel caller. Validates the result and either raises an exception or reports an error. Allows serial zypper calls (first came, first won). - ''' + """ SUCCESS_EXIT_CODES = { - 0: 'Successful run of zypper with no special info.', - 100: 'Patches are available for installation.', - 101: 'Security patches are available for installation.', - 102: 'Installation successful, reboot required.', - 103: 'Installation succesful, restart of the package manager itself required.', + 0: "Successful run of zypper with no special info.", + 100: "Patches are available for installation.", + 101: "Security patches are available for installation.", + 102: "Installation successful, reboot required.", + 103: "Installation succesful, restart of the package manager itself required.", } WARNING_EXIT_CODES = { - 6: 'No repositories are defined.', - 7: 'The ZYPP library is locked.', - 106: 'Some repository had to be disabled temporarily because it failed to refresh. ' - 'You should check your repository configuration (e.g. zypper ref -f).', - 107: 'Installation basically succeeded, but some of the packages %post install scripts returned an error. ' - 'These packages were successfully unpacked to disk and are registered in the rpm database, ' - 'but due to the failed install script they may not work as expected. The failed scripts output might ' - 'reveal what actually went wrong. Any scripts output is also logged to /var/log/zypp/history.' + 6: "No repositories are defined.", + 7: "The ZYPP library is locked.", + 106: "Some repository had to be disabled temporarily because it failed to refresh. " + "You should check your repository configuration (e.g. zypper ref -f).", + 107: "Installation basically succeeded, but some of the packages %post install scripts returned an error. " + "These packages were successfully unpacked to disk and are registered in the rpm database, " + "but due to the failed install script they may not work as expected. The failed scripts output might " + "reveal what actually went wrong. Any scripts output is also logged to /var/log/zypp/history.", } LOCK_EXIT_CODE = 7 - XML_DIRECTIVES = ['-x', '--xmlout'] - ZYPPER_LOCK = '/var/run/zypp.pid' - TAG_RELEASED = 'zypper/released' - TAG_BLOCKED = 'zypper/blocked' + XML_DIRECTIVES = ["-x", "--xmlout"] + ZYPPER_LOCK = "/var/run/zypp.pid" + TAG_RELEASED = "zypper/released" + TAG_BLOCKED = "zypper/blocked" def __init__(self): - ''' + """ Constructor - ''' + """ self.__called = False self._reset() def _reset(self): - ''' + """ Resets values of the call setup. :return: - ''' - self.__cmd = ['zypper', '--non-interactive'] + """ + self.__cmd = ["zypper", "--non-interactive"] self.__exit_code = 0 self.__call_result = dict() - self.__error_msg = '' + self.__error_msg = "" self.__env = salt.utils.environment.get_module_environment(globals()) # Call config @@ -130,39 +135,39 @@ class _Zypper(object): self.__systemd_scope = False def __call__(self, *args, **kwargs): - ''' + """ :param args: :param kwargs: :return: - ''' + """ # Ignore exit code for 106 (repo is not available) - if 'no_repo_failure' in kwargs: - self.__ignore_repo_failure = kwargs['no_repo_failure'] - if 'systemd_scope' in kwargs: - self.__systemd_scope = kwargs['systemd_scope'] + if "no_repo_failure" in kwargs: + self.__ignore_repo_failure = kwargs["no_repo_failure"] + if "systemd_scope" in kwargs: + self.__systemd_scope = kwargs["systemd_scope"] return self def __getattr__(self, item): - ''' + """ Call configurator. :param item: :return: - ''' + """ # Reset after the call if self.__called: self._reset() self.__called = False - if item == 'xml': + if item == "xml": self.__xml = True - elif item == 'nolock': + elif item == "nolock": self.__no_lock = True - elif item == 'noraise': + elif item == "noraise": self.__no_raise = True - elif item == 'refreshable': + elif item == "refreshable": self.__refresh = True - elif item == 'call': + elif item == "call": return self.__call else: return self.__dict__[item] @@ -179,7 +184,7 @@ class _Zypper(object): @exit_code.setter def exit_code(self, exit_code): - self.__exit_code = int(exit_code or '0') + self.__exit_code = int(exit_code or "0") @property def error_msg(self): @@ -192,22 +197,22 @@ class _Zypper(object): @property def stdout(self): - return self.__call_result.get('stdout', '') + return self.__call_result.get("stdout", "") @property def stderr(self): - return self.__call_result.get('stderr', '') + return self.__call_result.get("stderr", "") @property def pid(self): - return self.__call_result.get('pid', '') + return self.__call_result.get("pid", "") def _is_error(self): - ''' + """ Is this is an error code? :return: - ''' + """ if self.exit_code: msg = self.SUCCESS_EXIT_CODES.get(self.exit_code) if msg: @@ -216,80 +221,91 @@ class _Zypper(object): if msg: log.warning(msg) - return self.exit_code not in self.SUCCESS_EXIT_CODES and self.exit_code not in self.WARNING_EXIT_CODES + return ( + self.exit_code not in self.SUCCESS_EXIT_CODES + and self.exit_code not in self.WARNING_EXIT_CODES + ) def _is_lock(self): - ''' + """ Is this is a lock error code? :return: - ''' + """ return self.exit_code == self.LOCK_EXIT_CODE def _is_xml_mode(self): - ''' + """ Is Zypper's output is in XML format? :return: - ''' - return [itm for itm in self.XML_DIRECTIVES if itm in self.__cmd] and True or False + """ + return ( + [itm for itm in self.XML_DIRECTIVES if itm in self.__cmd] and True or False + ) def _check_result(self): - ''' + """ Check and set the result of a zypper command. In case of an error, either raise a CommandExecutionError or extract the error. result The result of a zypper command called with cmd.run_all - ''' + """ if not self.__call_result: - raise CommandExecutionError('No output result from Zypper?') + raise CommandExecutionError("No output result from Zypper?") - self.exit_code = self.__call_result['retcode'] + self.exit_code = self.__call_result["retcode"] if self._is_lock(): return False if self._is_error(): _error_msg = list() if not self._is_xml_mode(): - msg = self.__call_result['stderr'] and self.__call_result['stderr'].strip() or "" + msg = ( + self.__call_result["stderr"] + and self.__call_result["stderr"].strip() + or "" + ) if msg: _error_msg.append(msg) else: try: - doc = dom.parseString(self.__call_result['stdout']) + doc = dom.parseString(self.__call_result["stdout"]) except ExpatError as err: log.error(err) doc = None if doc: - msg_nodes = doc.getElementsByTagName('message') + msg_nodes = doc.getElementsByTagName("message") for node in msg_nodes: - if node.getAttribute('type') == 'error': + if node.getAttribute("type") == "error": _error_msg.append(node.childNodes[0].nodeValue) - elif self.__call_result['stderr'].strip(): - _error_msg.append(self.__call_result['stderr'].strip()) + elif self.__call_result["stderr"].strip(): + _error_msg.append(self.__call_result["stderr"].strip()) self.error_msg = _error_msg return True def __call(self, *args, **kwargs): - ''' + """ Call Zypper. :param state: :return: - ''' + """ self.__called = True if self.__xml: - self.__cmd.append('--xmlout') + self.__cmd.append("--xmlout") if not self.__refresh: - self.__cmd.append('--no-refresh') + self.__cmd.append("--no-refresh") self.__cmd.extend(args) - kwargs['output_loglevel'] = 'trace' - kwargs['python_shell'] = False - kwargs['env'] = self.__env.copy() + kwargs["output_loglevel"] = "trace" + kwargs["python_shell"] = False + kwargs["env"] = self.__env.copy() if self.__no_lock: - kwargs['env']['ZYPP_READONLY_HACK'] = "1" # Disables locking for read-only operations. Do not try that at home! + kwargs["env"][ + "ZYPP_READONLY_HACK" + ] = "1" # Disables locking for read-only operations. Do not try that at home! # Zypper call will stuck here waiting, if another zypper hangs until forever. # However, Zypper lock needs to be always respected. @@ -297,51 +313,73 @@ class _Zypper(object): while True: cmd = [] if self.__systemd_scope: - cmd.extend(['systemd-run', '--scope']) + cmd.extend(["systemd-run", "--scope"]) cmd.extend(self.__cmd) - log.debug("Calling Zypper: %s", ' '.join(cmd)) - self.__call_result = __salt__['cmd.run_all'](cmd, **kwargs) + log.debug("Calling Zypper: %s", " ".join(cmd)) + self.__call_result = __salt__["cmd.run_all"](cmd, **kwargs) if self._check_result(): break if os.path.exists(self.ZYPPER_LOCK): try: with salt.utils.files.fopen(self.ZYPPER_LOCK) as rfh: - data = __salt__['ps.proc_info'](int(rfh.readline()), - attrs=['pid', 'name', 'cmdline', 'create_time']) - data['cmdline'] = ' '.join(data['cmdline']) - data['info'] = 'Blocking process created at {0}.'.format( - datetime.datetime.utcfromtimestamp(data['create_time']).isoformat()) - data['success'] = True + data = __salt__["ps.proc_info"]( + int(rfh.readline()), + attrs=["pid", "name", "cmdline", "create_time"], + ) + data["cmdline"] = " ".join(data["cmdline"]) + data["info"] = "Blocking process created at {0}.".format( + datetime.datetime.utcfromtimestamp( + data["create_time"] + ).isoformat() + ) + data["success"] = True except Exception as err: # pylint: disable=broad-except - data = {'info': 'Unable to retrieve information about blocking process: {0}'.format(err.message), - 'success': False} + data = { + "info": "Unable to retrieve information about blocking process: {0}".format( + err.message + ), + "success": False, + } else: - data = {'info': 'Zypper is locked, but no Zypper lock has been found.', 'success': False} + data = { + "info": "Zypper is locked, but no Zypper lock has been found.", + "success": False, + } - if not data['success']: + if not data["success"]: log.debug("Unable to collect data about blocking process.") else: log.debug("Collected data about blocking process.") - __salt__['event.fire_master'](data, self.TAG_BLOCKED) - log.debug("Fired a Zypper blocked event to the master with the data: %s", data) + __salt__["event.fire_master"](data, self.TAG_BLOCKED) + log.debug( + "Fired a Zypper blocked event to the master with the data: %s", data + ) log.debug("Waiting 5 seconds for Zypper gets released...") time.sleep(5) if not was_blocked: was_blocked = True if was_blocked: - __salt__['event.fire_master']({'success': not self.error_msg, - 'info': self.error_msg or 'Zypper has been released'}, - self.TAG_RELEASED) + __salt__["event.fire_master"]( + { + "success": not self.error_msg, + "info": self.error_msg or "Zypper has been released", + }, + self.TAG_RELEASED, + ) if self.error_msg and not self.__no_raise and not self.__ignore_repo_failure: - raise CommandExecutionError('Zypper command failure: {0}'.format(self.error_msg)) + raise CommandExecutionError( + "Zypper command failure: {0}".format(self.error_msg) + ) return ( - self._is_xml_mode() and - dom.parseString(salt.utils.stringutils.to_str(self.__call_result['stdout'])) or - self.__call_result['stdout'] + self._is_xml_mode() + and dom.parseString( + salt.utils.stringutils.to_str(self.__call_result["stdout"]) + ) + or self.__call_result["stdout"] ) @@ -349,7 +387,7 @@ __zypper__ = _Zypper() class Wildcard(object): - ''' + """ .. versionadded:: 2017.7.0 Converts string wildcard to a zypper query. @@ -359,53 +397,68 @@ class Wildcard(object): :param ptn: Pattern :return: Query range - ''' + """ - Z_OP = ['<', '<=', '=', '>=', '>'] + Z_OP = ["<", "<=", "=", ">=", ">"] def __init__(self, zypper): - ''' + """ :type zypper: a reference to an instance of a _Zypper class. - ''' + """ self.name = None self.version = None self.zypper = zypper - self._attr_solvable_version = 'edition' + self._attr_solvable_version = "edition" self._op = None def __call__(self, pkg_name, pkg_version): - ''' + """ Convert a string wildcard to a zypper query. :param pkg_name: :param pkg_version: :return: - ''' + """ if pkg_version: self.name = pkg_name self._set_version(pkg_version) # Dissects possible operator - versions = sorted([LooseVersion(vrs) - for vrs in self._get_scope_versions(self._get_available_versions())]) - return versions and '{0}{1}'.format(self._op or '', versions[-1]) or None + versions = sorted( + [ + LooseVersion(vrs) + for vrs in self._get_scope_versions(self._get_available_versions()) + ] + ) + return versions and "{0}{1}".format(self._op or "", versions[-1]) or None def _get_available_versions(self): - ''' + """ Get available versions of the package. :return: - ''' - solvables = self.zypper.nolock.xml.call('se', '-xv', self.name).getElementsByTagName('solvable') + """ + solvables = self.zypper.nolock.xml.call( + "se", "-xv", self.name + ).getElementsByTagName("solvable") if not solvables: - raise CommandExecutionError('No packages found matching \'{0}\''.format(self.name)) + raise CommandExecutionError( + "No packages found matching '{0}'".format(self.name) + ) - return sorted(set([slv.getAttribute(self._attr_solvable_version) - for slv in solvables if slv.getAttribute(self._attr_solvable_version)])) + return sorted( + set( + [ + slv.getAttribute(self._attr_solvable_version) + for slv in solvables + if slv.getAttribute(self._attr_solvable_version) + ] + ) + ) def _get_scope_versions(self, pkg_versions): - ''' + """ Get available difference between next possible matches. :return: - ''' + """ get_in_versions = [] for p_version in pkg_versions: if fnmatch.fnmatch(p_version, self.version): @@ -413,36 +466,39 @@ class Wildcard(object): return get_in_versions def _set_version(self, version): - ''' + """ Stash operator from the version, if any. :return: - ''' + """ if not version: return - exact_version = re.sub(r'[<>=+]*', '', version) - self._op = version.replace(exact_version, '') or None + exact_version = re.sub(r"[<>=+]*", "", version) + self._op = version.replace(exact_version, "") or None if self._op and self._op not in self.Z_OP: - raise CommandExecutionError('Zypper do not supports operator "{0}".'.format(self._op)) + raise CommandExecutionError( + 'Zypper do not supports operator "{0}".'.format(self._op) + ) self.version = exact_version def _systemd_scope(): - return salt.utils.systemd.has_scope(__context__) \ - and __salt__['config.get']('systemd.scope', True) + return salt.utils.systemd.has_scope(__context__) and __salt__["config.get"]( + "systemd.scope", True + ) def _clean_cache(): - ''' + """ Clean cached results - ''' - for cache_name in ['pkg.list_pkgs', 'pkg.list_provides']: + """ + for cache_name in ["pkg.list_pkgs", "pkg.list_provides"]: __context__.pop(cache_name, None) def list_upgrades(refresh=True, **kwargs): - ''' + """ List all available package upgrades on this system refresh @@ -455,32 +511,37 @@ def list_upgrades(refresh=True, **kwargs): .. code-block:: bash salt '*' pkg.list_upgrades - ''' + """ if refresh: refresh_db() ret = dict() - cmd = ['list-updates'] - if 'fromrepo' in kwargs: - repos = kwargs['fromrepo'] + cmd = ["list-updates"] + if "fromrepo" in kwargs: + repos = kwargs["fromrepo"] if isinstance(repos, six.string_types): repos = [repos] for repo in repos: - cmd.extend(['--repo', repo if isinstance(repo, six.string_types) else six.text_type(repo)]) - log.debug('Targeting repos: %s', repos) - for update_node in __zypper__.nolock.xml.call(*cmd).getElementsByTagName('update'): - if update_node.getAttribute('kind') == 'package': - ret[update_node.getAttribute('name')] = update_node.getAttribute('edition') + cmd.extend( + [ + "--repo", + repo if isinstance(repo, six.string_types) else six.text_type(repo), + ] + ) + log.debug("Targeting repos: %s", repos) + for update_node in __zypper__.nolock.xml.call(*cmd).getElementsByTagName("update"): + if update_node.getAttribute("kind") == "package": + ret[update_node.getAttribute("name")] = update_node.getAttribute("edition") return ret # Provide a list_updates function for those used to using zypper list-updates -list_updates = salt.utils.functools.alias_function(list_upgrades, 'list_updates') +list_updates = salt.utils.functools.alias_function(list_upgrades, "list_updates") def info_installed(*names, **kwargs): - ''' + """ Return the information of the named package(s), installed on the system. :param names: @@ -516,17 +577,17 @@ def info_installed(*names, **kwargs): salt '*' pkg.info_installed <package1> <package2> <package3> ... attr=version,vendor salt '*' pkg.info_installed <package1> <package2> <package3> ... attr=version,vendor errors=ignore salt '*' pkg.info_installed <package1> <package2> <package3> ... attr=version,vendor errors=report - ''' - all_versions = kwargs.get('all_versions', False) + """ + all_versions = kwargs.get("all_versions", False) ret = dict() - for pkg_name, pkgs_nfo in __salt__['lowpkg.info'](*names, **kwargs).items(): + for pkg_name, pkgs_nfo in __salt__["lowpkg.info"](*names, **kwargs).items(): pkg_nfo = pkgs_nfo if all_versions else [pkgs_nfo] for _nfo in pkg_nfo: t_nfo = dict() # Translate dpkg-specific keys to a common structure for key, value in six.iteritems(_nfo): - if key == 'source_rpm': - t_nfo['source'] = value + if key == "source_rpm": + t_nfo["source"] = value else: t_nfo[key] = value if not all_versions: @@ -537,7 +598,7 @@ def info_installed(*names, **kwargs): def info_available(*names, **kwargs): - ''' + """ Return the information of the named package available for the system. refresh @@ -551,7 +612,7 @@ def info_available(*names, **kwargs): salt '*' pkg.info_available <package1> salt '*' pkg.info_available <package1> <package2> <package3> ... - ''' + """ ret = {} if not names: @@ -560,7 +621,7 @@ def info_available(*names, **kwargs): names = sorted(list(set(names))) # Refresh db before extracting the latest package - if kwargs.get('refresh', True): + if kwargs.get("refresh", True): refresh_db() pkg_info = [] @@ -569,31 +630,35 @@ def info_available(*names, **kwargs): # Run in batches while batch: - pkg_info.extend(re.split(r"Information for package*", - __zypper__.nolock.call('info', '-t', 'package', *batch[:batch_size]))) + pkg_info.extend( + re.split( + r"Information for package*", + __zypper__.nolock.call("info", "-t", "package", *batch[:batch_size]), + ) + ) batch = batch[batch_size:] for pkg_data in pkg_info: nfo = {} - for line in [data for data in pkg_data.split('\n') if ':' in data]: - if line.startswith('-----'): + for line in [data for data in pkg_data.split("\n") if ":" in data]: + if line.startswith("-----"): continue - kw = [data.strip() for data in line.split(':', 1)] + kw = [data.strip() for data in line.split(":", 1)] if len(kw) == 2 and kw[1]: nfo[kw[0].lower()] = kw[1] - if nfo.get('name'): - name = nfo.pop('name') + if nfo.get("name"): + name = nfo.pop("name") ret[name] = nfo - if nfo.get('status'): - nfo['status'] = nfo.get('status') - if nfo.get('installed'): - nfo['installed'] = nfo.get('installed').lower().startswith('yes') + if nfo.get("status"): + nfo["status"] = nfo.get("status") + if nfo.get("installed"): + nfo["installed"] = nfo.get("installed").lower().startswith("yes") return ret def parse_arch(name): - ''' + """ Parse name and architecture from the specified package name. CLI Example: @@ -601,23 +666,20 @@ def parse_arch(name): .. code-block:: bash salt '*' pkg.parse_arch zsh.x86_64 - ''' + """ _name, _arch = None, None try: _name, _arch = name.rsplit(PKG_ARCH_SEPARATOR, 1) except ValueError: pass - if _arch not in salt.utils.pkg.rpm.ARCHES + ('noarch',): + if _arch not in salt.utils.pkg.rpm.ARCHES + ("noarch",): _name = name _arch = None - return { - 'name': _name, - 'arch': _arch - } + return {"name": _name, "arch": _arch} def latest_version(*names, **kwargs): - ''' + """ Return the latest version of the named package available for upgrade or installation. If more than one package name is specified, a dict of name/version pairs is returned. @@ -636,7 +698,7 @@ def latest_version(*names, **kwargs): salt '*' pkg.latest_version <package name> salt '*' pkg.latest_version <package1> <package2> <package3> ... - ''' + """ ret = dict() if not names: @@ -646,11 +708,11 @@ def latest_version(*names, **kwargs): package_info = info_available(*names, **kwargs) for name in names: pkg_info = package_info.get(name, {}) - status = pkg_info.get('status', '').lower() - if status.find('not installed') > -1 or status.find('out-of-date') > -1: - ret[name] = pkg_info.get('version') + status = pkg_info.get("status", "").lower() + if status.find("not installed") > -1 or status.find("out-of-date") > -1: + ret[name] = pkg_info.get("version") else: - ret[name] = '' + ret[name] = "" # Return a string if only one package name passed if len(names) == 1 and ret: @@ -660,11 +722,13 @@ def latest_version(*names, **kwargs): # available_version is being deprecated -available_version = salt.utils.functools.alias_function(latest_version, 'available_version') +available_version = salt.utils.functools.alias_function( + latest_version, "available_version" +) def upgrade_available(name, **kwargs): - ''' + """ Check whether or not an upgrade is available for a given package refresh @@ -677,13 +741,13 @@ def upgrade_available(name, **kwargs): .. code-block:: bash salt '*' pkg.upgrade_available <package name> - ''' + """ # The "not not" tactic is intended here as it forces the return to be False. return not not latest_version(name, **kwargs) # pylint: disable=C0113 def version(*names, **kwargs): - ''' + """ Returns a string representing the package version or an empty dict if not installed. If more than one package name is specified, a dict of name/version pairs is returned. @@ -694,12 +758,12 @@ def version(*names, **kwargs): salt '*' pkg.version <package name> salt '*' pkg.version <package1> <package2> <package3> ... - ''' - return __salt__['pkg_resource.version'](*names, **kwargs) or {} + """ + return __salt__["pkg_resource.version"](*names, **kwargs) or {} def version_cmp(ver1, ver2, ignore_epoch=False): - ''' + """ .. versionadded:: 2015.5.4 Do a cmp-style comparison on two packages. Return -1 if ver1 < ver2, 0 if @@ -716,12 +780,12 @@ def version_cmp(ver1, ver2, ignore_epoch=False): .. code-block:: bash salt '*' pkg.version_cmp '0.2-001' '0.2.0.1-002' - ''' - return __salt__['lowpkg.version_cmp'](ver1, ver2, ignore_epoch=ignore_epoch) + """ + return __salt__["lowpkg.version_cmp"](ver1, ver2, ignore_epoch=ignore_epoch) def list_pkgs(versions_as_list=False, **kwargs): - ''' + """ List the packages currently installed as a dict. By default, the dict contains versions as a comma separated string:: @@ -758,67 +822,68 @@ def list_pkgs(versions_as_list=False, **kwargs): salt '*' pkg.list_pkgs salt '*' pkg.list_pkgs attr=version,arch salt '*' pkg.list_pkgs attr='["version", "arch"]' - ''' + """ versions_as_list = salt.utils.data.is_true(versions_as_list) # not yet implemented or not applicable - if any([salt.utils.data.is_true(kwargs.get(x)) - for x in ('removed', 'purge_desired')]): + if any( + [salt.utils.data.is_true(kwargs.get(x)) for x in ("removed", "purge_desired")] + ): return {} - attr = kwargs.get('attr') + attr = kwargs.get("attr") if attr is not None: attr = salt.utils.args.split_input(attr) - contextkey = 'pkg.list_pkgs' + contextkey = "pkg.list_pkgs" if contextkey not in __context__: ret = {} - cmd = ['rpm', '-qa', '--queryformat', - salt.utils.pkg.rpm.QUERYFORMAT.replace('%{REPOID}', '(none)') + '\n'] - output = __salt__['cmd.run'](cmd, - python_shell=False, - output_loglevel='trace') + cmd = [ + "rpm", + "-qa", + "--queryformat", + salt.utils.pkg.rpm.QUERYFORMAT.replace("%{REPOID}", "(none)") + "\n", + ] + output = __salt__["cmd.run"](cmd, python_shell=False, output_loglevel="trace") for line in output.splitlines(): pkginfo = salt.utils.pkg.rpm.parse_pkginfo( - line, - osarch=__grains__['osarch'] + line, osarch=__grains__["osarch"] ) if pkginfo: # see rpm version string rules available at https://goo.gl/UGKPNd pkgver = pkginfo.version epoch = None release = None - if ':' in pkgver: + if ":" in pkgver: epoch, pkgver = pkgver.split(":", 1) - if '-' in pkgver: + if "-" in pkgver: pkgver, release = pkgver.split("-", 1) all_attr = { - 'epoch': epoch, - 'version': pkgver, - 'release': release, - 'arch': pkginfo.arch, - 'install_date': pkginfo.install_date, - 'install_date_time_t': pkginfo.install_date_time_t + "epoch": epoch, + "version": pkgver, + "release": release, + "arch": pkginfo.arch, + "install_date": pkginfo.install_date, + "install_date_time_t": pkginfo.install_date_time_t, } - __salt__['pkg_resource.add_pkg'](ret, pkginfo.name, all_attr) + __salt__["pkg_resource.add_pkg"](ret, pkginfo.name, all_attr) _ret = {} for pkgname in ret: # Filter out GPG public keys packages - if pkgname.startswith('gpg-pubkey'): + if pkgname.startswith("gpg-pubkey"): continue - _ret[pkgname] = sorted(ret[pkgname], key=lambda d: d['version']) + _ret[pkgname] = sorted(ret[pkgname], key=lambda d: d["version"]) __context__[contextkey] = _ret - return __salt__['pkg_resource.format_pkg_list']( - __context__[contextkey], - versions_as_list, - attr) + return __salt__["pkg_resource.format_pkg_list"]( + __context__[contextkey], versions_as_list, attr + ) def list_repo_pkgs(*args, **kwargs): - ''' + """ .. versionadded:: 2017.7.5,2018.3.1 Returns all available packages. Optionally, package names (and name globs) @@ -868,22 +933,21 @@ def list_repo_pkgs(*args, **kwargs): salt '*' pkg.list_repo_pkgs foo bar baz salt '*' pkg.list_repo_pkgs 'python2-*' byrepo=True salt '*' pkg.list_repo_pkgs 'python2-*' fromrepo='OSS Updates' - ''' - byrepo = kwargs.pop('byrepo', False) - fromrepo = kwargs.pop('fromrepo', '') or '' + """ + byrepo = kwargs.pop("byrepo", False) + fromrepo = kwargs.pop("fromrepo", "") or "" ret = {} targets = [ - arg if isinstance(arg, six.string_types) else six.text_type(arg) - for arg in args + arg if isinstance(arg, six.string_types) else six.text_type(arg) for arg in args ] def _is_match(pkgname): - ''' + """ When package names are passed to a zypper search, they will be matched anywhere in the package name. This makes sure that only exact or fnmatch matches are identified. - ''' + """ if not args: # No package names passed, everyone's a winner! return True @@ -892,16 +956,18 @@ def list_repo_pkgs(*args, **kwargs): return True return False - for node in __zypper__.xml.call('se', '-s', *targets).getElementsByTagName('solvable'): + for node in __zypper__.xml.call("se", "-s", *targets).getElementsByTagName( + "solvable" + ): pkginfo = dict(node.attributes.items()) try: - if pkginfo['kind'] != 'package': + if pkginfo["kind"] != "package": continue - reponame = pkginfo['repository'] + reponame = pkginfo["repository"] if fromrepo and reponame != fromrepo: continue - pkgname = pkginfo['name'] - pkgversion = pkginfo['edition'] + pkgname = pkginfo["name"] + pkgversion = pkginfo["edition"] except KeyError: continue else: @@ -915,8 +981,7 @@ def list_repo_pkgs(*args, **kwargs): # Sort versions newest to oldest for pkgname in ret[reponame]: sorted_versions = sorted( - [LooseVersion(x) for x in ret[reponame][pkgname]], - reverse=True + [LooseVersion(x) for x in ret[reponame][pkgname]], reverse=True ) ret[reponame][pkgname] = [x.vstring for x in sorted_versions] return ret @@ -927,35 +992,36 @@ def list_repo_pkgs(*args, **kwargs): byrepo_ret.setdefault(pkgname, []).extend(ret[reponame][pkgname]) for pkgname in byrepo_ret: sorted_versions = sorted( - [LooseVersion(x) for x in byrepo_ret[pkgname]], - reverse=True + [LooseVersion(x) for x in byrepo_ret[pkgname]], reverse=True ) byrepo_ret[pkgname] = [x.vstring for x in sorted_versions] return byrepo_ret def _get_configured_repos(): - ''' + """ Get all the info about repositories from the configurations. - ''' + """ repos_cfg = configparser.ConfigParser() - repos_cfg.read([REPOS + '/' + fname for fname in os.listdir(REPOS) if fname.endswith(".repo")]) + repos_cfg.read( + [REPOS + "/" + fname for fname in os.listdir(REPOS) if fname.endswith(".repo")] + ) return repos_cfg def _get_repo_info(alias, repos_cfg=None): - ''' + """ Get one repo meta-data. - ''' + """ try: meta = dict((repos_cfg or _get_configured_repos()).items(alias)) - meta['alias'] = alias + meta["alias"] = alias for key, val in six.iteritems(meta): - if val in ['0', '1']: + if val in ["0", "1"]: meta[key] = int(meta[key]) == 1 - elif val == 'NONE': + elif val == "NONE": meta[key] = None return meta except (ValueError, configparser.NoSectionError): @@ -963,7 +1029,7 @@ def _get_repo_info(alias, repos_cfg=None): def get_repo(repo, **kwargs): # pylint: disable=unused-argument - ''' + """ Display a repo. CLI Example: @@ -971,12 +1037,12 @@ def get_repo(repo, **kwargs): # pylint: disable=unused-argument .. code-block:: bash salt '*' pkg.get_repo alias - ''' + """ return _get_repo_info(repo) def list_repos(): - ''' + """ Lists all repos. CLI Example: @@ -984,7 +1050,7 @@ def list_repos(): .. code-block:: bash salt '*' pkg.list_repos - ''' + """ repos_cfg = _get_configured_repos() all_repos = {} for alias in repos_cfg.sections(): @@ -994,7 +1060,7 @@ def list_repos(): def del_repo(repo): - ''' + """ Delete a repo. CLI Examples: @@ -1002,23 +1068,23 @@ def del_repo(repo): .. code-block:: bash salt '*' pkg.del_repo alias - ''' + """ repos_cfg = _get_configured_repos() for alias in repos_cfg.sections(): if alias == repo: - doc = __zypper__.xml.call('rr', '--loose-auth', '--loose-query', alias) - msg = doc.getElementsByTagName('message') - if doc.getElementsByTagName('progress') and msg: + doc = __zypper__.xml.call("rr", "--loose-auth", "--loose-query", alias) + msg = doc.getElementsByTagName("message") + if doc.getElementsByTagName("progress") and msg: return { repo: True, - 'message': msg[0].childNodes[0].nodeValue, + "message": msg[0].childNodes[0].nodeValue, } - raise CommandExecutionError('Repository \'{0}\' not found.'.format(repo)) + raise CommandExecutionError("Repository '{0}' not found.".format(repo)) def mod_repo(repo, **kwargs): - ''' + """ Modify one or more values for a repo. If the repo does not exist, it will be created, so long as the following values are specified: @@ -1055,24 +1121,24 @@ def mod_repo(repo, **kwargs): salt '*' pkg.mod_repo alias alias=new_alias salt '*' pkg.mod_repo alias url= mirrorlist=http://host.com/ - ''' + """ repos_cfg = _get_configured_repos() added = False # An attempt to add new one? if repo not in repos_cfg.sections(): - url = kwargs.get('url', kwargs.get('mirrorlist', kwargs.get('baseurl'))) + url = kwargs.get("url", kwargs.get("mirrorlist", kwargs.get("baseurl"))) if not url: raise CommandExecutionError( - 'Repository \'{0}\' not found, and neither \'baseurl\' nor ' - '\'mirrorlist\' was specified'.format(repo) + "Repository '{0}' not found, and neither 'baseurl' nor " + "'mirrorlist' was specified".format(repo) ) if not _urlparse(url).scheme: raise CommandExecutionError( - 'Repository \'{0}\' not found and URL for baseurl/mirrorlist ' - 'is malformed'.format(repo) + "Repository '{0}' not found and URL for baseurl/mirrorlist " + "is malformed".format(repo) ) # Is there already such repo under different alias? @@ -1082,42 +1148,43 @@ def mod_repo(repo, **kwargs): # Complete user URL, in case it is not new_url = _urlparse(url) if not new_url.path: - new_url = _urlparse.ParseResult(scheme=new_url.scheme, # pylint: disable=E1123 - netloc=new_url.netloc, - path='/', - params=new_url.params, - query=new_url.query, - fragment=new_url.fragment) - base_url = _urlparse(repo_meta['baseurl']) + new_url = _urlparse.ParseResult( + scheme=new_url.scheme, # pylint: disable=E1123 + netloc=new_url.netloc, + path="/", + params=new_url.params, + query=new_url.query, + fragment=new_url.fragment, + ) + base_url = _urlparse(repo_meta["baseurl"]) if new_url == base_url: raise CommandExecutionError( - 'Repository \'{0}\' already exists as \'{1}\'.'.format( - repo, - alias - ) + "Repository '{0}' already exists as '{1}'.".format(repo, alias) ) # Add new repo - __zypper__.xml.call('ar', url, repo) + __zypper__.xml.call("ar", url, repo) # Verify the repository has been added repos_cfg = _get_configured_repos() if repo not in repos_cfg.sections(): raise CommandExecutionError( - 'Failed add new repository \'{0}\' for unspecified reason. ' - 'Please check zypper logs.'.format(repo)) + "Failed add new repository '{0}' for unspecified reason. " + "Please check zypper logs.".format(repo) + ) added = True repo_info = _get_repo_info(repo) if ( - not added and 'baseurl' in kwargs and - not (kwargs['baseurl'] == repo_info['baseurl']) + not added + and "baseurl" in kwargs + and not (kwargs["baseurl"] == repo_info["baseurl"]) ): # Note: zypper does not support changing the baseurl # we need to remove the repository and add it again with the new baseurl repo_info.update(kwargs) - repo_info.setdefault('cache', False) + repo_info.setdefault("cache", False) del_repo(repo) return mod_repo(repo, **repo_info) @@ -1126,32 +1193,32 @@ def mod_repo(repo, **kwargs): global_cmd_opt = [] call_refresh = False - if 'enabled' in kwargs: - cmd_opt.append(kwargs['enabled'] and '--enable' or '--disable') + if "enabled" in kwargs: + cmd_opt.append(kwargs["enabled"] and "--enable" or "--disable") - if 'refresh' in kwargs: - cmd_opt.append(kwargs['refresh'] and '--refresh' or '--no-refresh') + if "refresh" in kwargs: + cmd_opt.append(kwargs["refresh"] and "--refresh" or "--no-refresh") - if 'cache' in kwargs: + if "cache" in kwargs: + cmd_opt.append(kwargs["cache"] and "--keep-packages" or "--no-keep-packages") + + if "gpgcheck" in kwargs: + cmd_opt.append(kwargs["gpgcheck"] and "--gpgcheck" or "--no-gpgcheck") + + if "priority" in kwargs: cmd_opt.append( - kwargs['cache'] and '--keep-packages' or '--no-keep-packages' + "--priority={0}".format(kwargs.get("priority", DEFAULT_PRIORITY)) ) - if 'gpgcheck' in kwargs: - cmd_opt.append(kwargs['gpgcheck'] and '--gpgcheck' or '--no-gpgcheck') + if "humanname" in kwargs: + cmd_opt.append("--name='{0}'".format(kwargs.get("humanname"))) - if 'priority' in kwargs: - cmd_opt.append("--priority={0}".format(kwargs.get('priority', DEFAULT_PRIORITY))) - - if 'humanname' in kwargs: - cmd_opt.append("--name='{0}'".format(kwargs.get('humanname'))) - - if kwargs.get('gpgautoimport') is True: - global_cmd_opt.append('--gpg-auto-import-keys') + if kwargs.get("gpgautoimport") is True: + global_cmd_opt.append("--gpg-auto-import-keys") call_refresh = True if cmd_opt: - cmd_opt = global_cmd_opt + ['mr'] + cmd_opt + [repo] + cmd_opt = global_cmd_opt + ["mr"] + cmd_opt + [repo] __zypper__.refreshable.xml.call(*cmd_opt) comment = None @@ -1159,20 +1226,20 @@ def mod_repo(repo, **kwargs): # when used with "zypper ar --refresh" or "zypper mr --refresh" # --gpg-auto-import-keys is not doing anything # so we need to specifically refresh here with --gpg-auto-import-keys - refresh_opts = global_cmd_opt + ['refresh'] + [repo] + refresh_opts = global_cmd_opt + ["refresh"] + [repo] __zypper__.xml.call(*refresh_opts) elif not added and not cmd_opt: - comment = 'Specified arguments did not result in modification of repo' + comment = "Specified arguments did not result in modification of repo" repo = get_repo(repo) if comment: - repo['comment'] = comment + repo["comment"] = comment return repo def refresh_db(): - ''' + """ Force a repository refresh by calling ``zypper refresh --force``, return a dict:: {'<database name>': Bool} @@ -1182,40 +1249,42 @@ def refresh_db(): .. code-block:: bash salt '*' pkg.refresh_db - ''' + """ # Remove rtag file to keep multiple refreshes from happening in pkg states salt.utils.pkg.clear_rtag(__opts__) ret = {} - out = __zypper__.refreshable.call('refresh', '--force') + out = __zypper__.refreshable.call("refresh", "--force") for line in out.splitlines(): if not line: continue - if line.strip().startswith('Repository') and '\'' in line: + if line.strip().startswith("Repository") and "'" in line: try: - key = line.split('\'')[1].strip() - if 'is up to date' in line: + key = line.split("'")[1].strip() + if "is up to date" in line: ret[key] = False except IndexError: continue - elif line.strip().startswith('Building') and '\'' in line: - key = line.split('\'')[1].strip() - if 'done' in line: + elif line.strip().startswith("Building") and "'" in line: + key = line.split("'")[1].strip() + if "done" in line: ret[key] = True return ret -def install(name=None, - refresh=False, - fromrepo=None, - pkgs=None, - sources=None, - downloadonly=None, - skip_verify=False, - version=None, - ignore_repo_failure=False, - **kwargs): - ''' +def install( + name=None, + refresh=False, + fromrepo=None, + pkgs=None, + sources=None, + downloadonly=None, + skip_verify=False, + version=None, + ignore_repo_failure=False, + **kwargs +): + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -1339,12 +1408,14 @@ def install(name=None, 'new': { 'version': '<new-version>', 'arch': '<new-arch>'}}} - ''' + """ if refresh: refresh_db() try: - pkg_params, pkg_type = __salt__['pkg_resource.parse_targets'](name, pkgs, sources, **kwargs) + pkg_params, pkg_type = __salt__["pkg_resource.parse_targets"]( + name, pkgs, sources, **kwargs + ) except MinionError as exc: raise CommandExecutionError(exc) @@ -1358,28 +1429,31 @@ def install(name=None, # Allow "version" to work for single package target pkg_params = {name: version_num} else: - log.warning('"version" parameter will be ignored for multiple ' - 'package targets') + log.warning( + '"version" parameter will be ignored for multiple ' "package targets" + ) - if pkg_type == 'repository': + if pkg_type == "repository": targets = [] for param, version_num in six.iteritems(pkg_params): if version_num is None: - log.debug('targeting package: %s', param) + log.debug("targeting package: %s", param) targets.append(param) else: prefix, verstr = salt.utils.pkg.split_comparison(version_num) if not prefix: - prefix = '=' - target = '{0}{1}{2}'.format(param, prefix, verstr) - log.debug('targeting package: %s', target) + prefix = "=" + target = "{0}{1}{2}".format(param, prefix, verstr) + log.debug("targeting package: %s", target) targets.append(target) - elif pkg_type == 'advisory': + elif pkg_type == "advisory": targets = [] cur_patches = list_patches() for advisory_id in pkg_params: if advisory_id not in cur_patches: - raise CommandExecutionError('Advisory id "{0}" not found'.format(advisory_id)) + raise CommandExecutionError( + 'Advisory id "{0}" not found'.format(advisory_id) + ) else: targets.append(advisory_id) else: @@ -1389,25 +1463,27 @@ def install(name=None, old = list_pkgs(attr=diff_attr) if not downloadonly else list_downloaded() downgrades = [] if fromrepo: - fromrepoopt = ['--force', '--force-resolution', '--from', fromrepo] - log.info('Targeting repo \'%s\'', fromrepo) + fromrepoopt = ["--force", "--force-resolution", "--from", fromrepo] + log.info("Targeting repo '%s'", fromrepo) else: - fromrepoopt = '' - cmd_install = ['install', '--auto-agree-with-licenses'] + fromrepoopt = "" + cmd_install = ["install", "--auto-agree-with-licenses"] - cmd_install.append(kwargs.get('resolve_capabilities') and '--capability' or '--name') + cmd_install.append( + kwargs.get("resolve_capabilities") and "--capability" or "--name" + ) if not refresh: - cmd_install.insert(0, '--no-refresh') + cmd_install.insert(0, "--no-refresh") if skip_verify: - cmd_install.insert(0, '--no-gpg-checks') + cmd_install.insert(0, "--no-gpg-checks") if downloadonly: - cmd_install.append('--download-only') + cmd_install.append("--download-only") if fromrepo: cmd_install.extend(fromrepoopt) errors = [] - if pkg_type == 'advisory': + if pkg_type == "advisory": targets = ["patch:{0}".format(t) for t in targets] # Split the targets into batches of 500 packages each, so that @@ -1416,13 +1492,19 @@ def install(name=None, while targets: cmd = cmd_install + targets[:500] targets = targets[500:] - for line in __zypper__(no_repo_failure=ignore_repo_failure, systemd_scope=systemd_scope).call(*cmd).splitlines(): - match = re.match(r"^The selected package '([^']+)'.+has lower version", line) + for line in ( + __zypper__(no_repo_failure=ignore_repo_failure, systemd_scope=systemd_scope) + .call(*cmd) + .splitlines() + ): + match = re.match( + r"^The selected package '([^']+)'.+has lower version", line + ) if match: downgrades.append(match.group(1)) while downgrades: - cmd = cmd_install + ['--force'] + downgrades[:500] + cmd = cmd_install + ["--force"] + downgrades[:500] downgrades = downgrades[500:] __zypper__(no_repo_failure=ignore_repo_failure).call(*cmd) @@ -1432,23 +1514,25 @@ def install(name=None, if errors: raise CommandExecutionError( - 'Problem encountered {0} package(s)'.format( - 'downloading' if downloadonly else 'installing' + "Problem encountered {0} package(s)".format( + "downloading" if downloadonly else "installing" ), - info={'errors': errors, 'changes': ret} + info={"errors": errors, "changes": ret}, ) return ret -def upgrade(refresh=True, - dryrun=False, - dist_upgrade=False, - fromrepo=None, - novendorchange=False, - skip_verify=False, - **kwargs): # pylint: disable=unused-argument - ''' +def upgrade( + refresh=True, + dryrun=False, + dist_upgrade=False, + fromrepo=None, + novendorchange=False, + skip_verify=False, + **kwargs +): # pylint: disable=unused-argument + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -1500,39 +1584,45 @@ def upgrade(refresh=True, salt '*' pkg.upgrade salt '*' pkg.upgrade dist_upgrade=True fromrepo='["MyRepoName"]' novendorchange=True salt '*' pkg.upgrade dist_upgrade=True dryrun=True - ''' - cmd_update = (['dist-upgrade'] if dist_upgrade else ['update']) + ['--auto-agree-with-licenses'] + """ + cmd_update = (["dist-upgrade"] if dist_upgrade else ["update"]) + [ + "--auto-agree-with-licenses" + ] if skip_verify: # The '--no-gpg-checks' needs to be placed before the Zypper command. - cmd_update.insert(0, '--no-gpg-checks') + cmd_update.insert(0, "--no-gpg-checks") if refresh: refresh_db() if dryrun: - cmd_update.append('--dry-run') + cmd_update.append("--dry-run") if fromrepo: if isinstance(fromrepo, six.string_types): fromrepo = [fromrepo] for repo in fromrepo: - cmd_update.extend(['--from' if dist_upgrade else '--repo', repo]) - log.info('Targeting repos: %s', fromrepo) + cmd_update.extend(["--from" if dist_upgrade else "--repo", repo]) + log.info("Targeting repos: %s", fromrepo) if dist_upgrade: if novendorchange: # TODO: Grains validation should be moved to Zypper class - if __grains__['osrelease_info'][0] > 11: - cmd_update.append('--no-allow-vendor-change') - log.info('Disabling vendor changes') + if __grains__["osrelease_info"][0] > 11: + cmd_update.append("--no-allow-vendor-change") + log.info("Disabling vendor changes") else: - log.warning('Disabling vendor changes is not supported on this Zypper version') + log.warning( + "Disabling vendor changes is not supported on this Zypper version" + ) if dryrun: # Creates a solver test case for debugging. - log.info('Executing debugsolver and performing a dry-run dist-upgrade') - __zypper__(systemd_scope=_systemd_scope()).noraise.call(*cmd_update + ['--debug-solver']) + log.info("Executing debugsolver and performing a dry-run dist-upgrade") + __zypper__(systemd_scope=_systemd_scope()).noraise.call( + *cmd_update + ["--debug-solver"] + ) old = list_pkgs() @@ -1543,14 +1633,14 @@ def upgrade(refresh=True, if __zypper__.exit_code not in __zypper__.SUCCESS_EXIT_CODES: result = { - 'retcode': __zypper__.exit_code, - 'stdout': __zypper__.stdout, - 'stderr': __zypper__.stderr, - 'pid': __zypper__.pid, + "retcode": __zypper__.exit_code, + "stdout": __zypper__.stdout, + "stderr": __zypper__.stderr, + "pid": __zypper__.pid, } raise CommandExecutionError( - 'Problem encountered upgrading packages', - info={'changes': ret, 'result': result} + "Problem encountered upgrading packages", + info={"changes": ret, "result": result}, ) if dryrun: @@ -1560,12 +1650,12 @@ def upgrade(refresh=True, def _uninstall(name=None, pkgs=None): - ''' + """ Remove and purge do identical things but with different Zypper commands, this function performs the common logic. - ''' + """ try: - pkg_params = __salt__['pkg_resource.parse_targets'](name, pkgs)[0] + pkg_params = __salt__["pkg_resource.parse_targets"](name, pkgs)[0] except MinionError as exc: raise CommandExecutionError(exc) @@ -1574,7 +1664,7 @@ def _uninstall(name=None, pkgs=None): for target in pkg_params: # Check if package version set to be removed is actually installed: # old[target] contains a comma-separated list of installed versions - if target in old and pkg_params[target] in old[target].split(','): + if target in old and pkg_params[target] in old[target].split(","): targets.append(target + "-" + pkg_params[target]) elif target in old and not pkg_params[target]: targets.append(target) @@ -1585,7 +1675,7 @@ def _uninstall(name=None, pkgs=None): errors = [] while targets: - __zypper__(systemd_scope=systemd_scope).call('remove', *targets[:500]) + __zypper__(systemd_scope=systemd_scope).call("remove", *targets[:500]) targets = targets[500:] _clean_cache() @@ -1593,15 +1683,15 @@ def _uninstall(name=None, pkgs=None): if errors: raise CommandExecutionError( - 'Problem encountered removing package(s)', - info={'errors': errors, 'changes': ret} + "Problem encountered removing package(s)", + info={"errors": errors, "changes": ret}, ) return ret def normalize_name(name): - ''' + """ Strips the architecture from the specified package name, if necessary. Circumstances where this would be done include: @@ -1613,21 +1703,22 @@ def normalize_name(name): .. code-block:: bash salt '*' pkg.normalize_name zsh.x86_64 - ''' + """ try: - arch = name.rsplit('.', 1)[-1] - if arch not in salt.utils.pkg.rpm.ARCHES + ('noarch',): + arch = name.rsplit(".", 1)[-1] + if arch not in salt.utils.pkg.rpm.ARCHES + ("noarch",): return name except ValueError: return name - if arch in (__grains__['osarch'], 'noarch') \ - or salt.utils.pkg.rpm.check_32(arch, osarch=__grains__['osarch']): - return name[:-(len(arch) + 1)] + if arch in (__grains__["osarch"], "noarch") or salt.utils.pkg.rpm.check_32( + arch, osarch=__grains__["osarch"] + ): + return name[: -(len(arch) + 1)] return name def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -1666,12 +1757,12 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument salt '*' pkg.remove <package name> salt '*' pkg.remove <package1>,<package2>,<package3> salt '*' pkg.remove pkgs='["foo", "bar"]' - ''' + """ return _uninstall(name=name, pkgs=pkgs) def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument - ''' + """ .. versionchanged:: 2015.8.12,2016.3.3,2016.11.0 On minions running systemd>=205, `systemd-run(1)`_ is now used to isolate commands which modify installed packages from the @@ -1711,12 +1802,12 @@ def purge(name=None, pkgs=None, **kwargs): # pylint: disable=unused-argument salt '*' pkg.purge <package name> salt '*' pkg.purge <package1>,<package2>,<package3> salt '*' pkg.purge pkgs='["foo", "bar"]' - ''' + """ return _uninstall(name=name, pkgs=pkgs) def list_locks(): - ''' + """ List current package locks. Return a dict containing the locked package with attributes:: @@ -1730,24 +1821,26 @@ def list_locks(): .. code-block:: bash salt '*' pkg.list_locks - ''' + """ locks = {} if os.path.exists(LOCKS): with salt.utils.files.fopen(LOCKS) as fhr: - items = salt.utils.stringutils.to_unicode(fhr.read()).split('\n\n') - for meta in [item.split('\n') for item in items]: + items = salt.utils.stringutils.to_unicode(fhr.read()).split("\n\n") + for meta in [item.split("\n") for item in items]: lock = {} for element in [el for el in meta if el]: - if ':' in element: - lock.update(dict([tuple([i.strip() for i in element.split(':', 1)]), ])) - if lock.get('solvable_name'): - locks[lock.pop('solvable_name')] = lock + if ":" in element: + lock.update( + dict([tuple([i.strip() for i in element.split(":", 1)])]) + ) + if lock.get("solvable_name"): + locks[lock.pop("solvable_name")] = lock return locks def clean_locks(): - ''' + """ Remove unused locks that do not currently (with regard to repositories used) lock any package. @@ -1756,13 +1849,13 @@ def clean_locks(): .. code-block:: bash salt '*' pkg.clean_locks - ''' + """ LCK = "removed" out = {LCK: 0} if not os.path.exists("/etc/zypp/locks"): return out - for node in __zypper__.xml.call('cl').getElementsByTagName("message"): + for node in __zypper__.xml.call("cl").getElementsByTagName("message"): text = node.childNodes[0].nodeValue.lower() if text.startswith(LCK): out[LCK] = text.split(" ")[1] @@ -1772,7 +1865,7 @@ def clean_locks(): def remove_lock(packages, **kwargs): # pylint: disable=unused-argument - ''' + """ Remove specified package lock. CLI Example: @@ -1782,11 +1875,11 @@ def remove_lock(packages, **kwargs): # pylint: disable=unused-argument salt '*' pkg.remove_lock <package name> salt '*' pkg.remove_lock <package1>,<package2>,<package3> salt '*' pkg.remove_lock pkgs='["foo", "bar"]' - ''' + """ locks = list_locks() try: - packages = list(__salt__['pkg_resource.parse_targets'](packages)[0].keys()) + packages = list(__salt__["pkg_resource.parse_targets"](packages)[0].keys()) except MinionError as exc: raise CommandExecutionError(exc) @@ -1799,13 +1892,13 @@ def remove_lock(packages, **kwargs): # pylint: disable=unused-argument missing.append(pkg) if removed: - __zypper__.call('rl', *removed) + __zypper__.call("rl", *removed) - return {'removed': len(removed), 'not_found': missing} + return {"removed": len(removed), "not_found": missing} def add_lock(packages, **kwargs): # pylint: disable=unused-argument - ''' + """ Add a package lock. Specify packages to lock by exact name. CLI Example: @@ -1815,11 +1908,11 @@ def add_lock(packages, **kwargs): # pylint: disable=unused-argument salt '*' pkg.add_lock <package name> salt '*' pkg.add_lock <package1>,<package2>,<package3> salt '*' pkg.add_lock pkgs='["foo", "bar"]' - ''' + """ locks = list_locks() added = [] try: - packages = list(__salt__['pkg_resource.parse_targets'](packages)[0].keys()) + packages = list(__salt__["pkg_resource.parse_targets"](packages)[0].keys()) except MinionError as exc: raise CommandExecutionError(exc) @@ -1828,13 +1921,13 @@ def add_lock(packages, **kwargs): # pylint: disable=unused-argument added.append(pkg) if added: - __zypper__.call('al', *added) + __zypper__.call("al", *added) - return {'added': len(added), 'packages': added} + return {"added": len(added), "packages": added} def verify(*names, **kwargs): - ''' + """ Runs an rpm -Va on a system, and returns the results in a dict Files with an attribute of config, doc, ghost, license or readme in the @@ -1848,12 +1941,12 @@ def verify(*names, **kwargs): salt '*' pkg.verify httpd salt '*' pkg.verify 'httpd postfix' salt '*' pkg.verify 'httpd postfix' ignore_types=['config','doc'] - ''' - return __salt__['lowpkg.verify'](*names, **kwargs) + """ + return __salt__["lowpkg.verify"](*names, **kwargs) def file_list(*packages): - ''' + """ List the files that belong to a package. Not specifying any packages will return a list of *every* file on the system's rpm database (not generally recommended). @@ -1865,12 +1958,12 @@ def file_list(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' - return __salt__['lowpkg.file_list'](*packages) + """ + return __salt__["lowpkg.file_list"](*packages) def file_dict(*packages): - ''' + """ List the files that belong to a package, grouped by package. Not specifying any packages will return a list of *every* file on the system's rpm database (not generally recommended). @@ -1882,12 +1975,12 @@ def file_dict(*packages): salt '*' pkg.file_list httpd salt '*' pkg.file_list httpd postfix salt '*' pkg.file_list - ''' - return __salt__['lowpkg.file_dict'](*packages) + """ + return __salt__["lowpkg.file_dict"](*packages) def modified(*packages, **flags): - ''' + """ List the modified files that belong to a package. Not specifying any packages will return a list of _all_ modified files on the system's RPM database. @@ -1930,13 +2023,13 @@ def modified(*packages, **flags): salt '*' pkg.modified httpd salt '*' pkg.modified httpd postfix salt '*' pkg.modified httpd owner=True group=False - ''' + """ - return __salt__['lowpkg.modified'](*packages, **flags) + return __salt__["lowpkg.modified"](*packages, **flags) def owner(*paths): - ''' + """ Return the name of the package that owns the file. Multiple file paths can be passed. If a single path is passed, a string will be returned, and if multiple paths are passed, a dictionary of file/package name @@ -1951,28 +2044,30 @@ def owner(*paths): salt '*' pkg.owner /usr/bin/apachectl salt '*' pkg.owner /usr/bin/apachectl /etc/httpd/conf/httpd.conf - ''' - return __salt__['lowpkg.owner'](*paths) + """ + return __salt__["lowpkg.owner"](*paths) def _get_patterns(installed_only=None): - ''' + """ List all known patterns in repos. - ''' + """ patterns = {} - for element in __zypper__.nolock.xml.call('se', '-t', 'pattern').getElementsByTagName('solvable'): - installed = element.getAttribute('status') == 'installed' + for element in __zypper__.nolock.xml.call( + "se", "-t", "pattern" + ).getElementsByTagName("solvable"): + installed = element.getAttribute("status") == "installed" if (installed_only and installed) or not installed_only: - patterns[element.getAttribute('name')] = { - 'installed': installed, - 'summary': element.getAttribute('summary'), + patterns[element.getAttribute("name")] = { + "installed": installed, + "summary": element.getAttribute("summary"), } return patterns def list_patterns(refresh=False): - ''' + """ List all known patterns from available repos. refresh @@ -1985,7 +2080,7 @@ def list_patterns(refresh=False): .. code-block:: bash salt '*' pkg.list_patterns - ''' + """ if refresh: refresh_db() @@ -1993,7 +2088,7 @@ def list_patterns(refresh=False): def list_installed_patterns(): - ''' + """ List installed patterns on the system. CLI Examples: @@ -2001,12 +2096,12 @@ def list_installed_patterns(): .. code-block:: bash salt '*' pkg.list_installed_patterns - ''' + """ return _get_patterns(installed_only=True) def search(criteria, refresh=False, **kwargs): - ''' + """ List known packags, available to the system. refresh @@ -2060,68 +2155,68 @@ def search(criteria, refresh=False, **kwargs): .. code-block:: bash salt '*' pkg.search <criteria> - ''' + """ ALLOWED_SEARCH_OPTIONS = { - 'provides': '--provides', - 'recommends': '--recommends', - 'requires': '--requires', - 'suggests': '--suggests', - 'conflicts': '--conflicts', - 'obsoletes': '--obsoletes', - 'file_list': '--file-list', - 'search_descriptions': '--search-descriptions', - 'case_sensitive': '--case-sensitive', - 'installed_only': '--installed-only', - 'not_installed_only': '-u', - 'details': '--details' - } + "provides": "--provides", + "recommends": "--recommends", + "requires": "--requires", + "suggests": "--suggests", + "conflicts": "--conflicts", + "obsoletes": "--obsoletes", + "file_list": "--file-list", + "search_descriptions": "--search-descriptions", + "case_sensitive": "--case-sensitive", + "installed_only": "--installed-only", + "not_installed_only": "-u", + "details": "--details", + } if refresh: refresh_db() - cmd = ['search'] - if kwargs.get('match') == 'exact': - cmd.append('--match-exact') - elif kwargs.get('match') == 'words': - cmd.append('--match-words') - elif kwargs.get('match') == 'substrings': - cmd.append('--match-substrings') + cmd = ["search"] + if kwargs.get("match") == "exact": + cmd.append("--match-exact") + elif kwargs.get("match") == "words": + cmd.append("--match-words") + elif kwargs.get("match") == "substrings": + cmd.append("--match-substrings") for opt in kwargs: if opt in ALLOWED_SEARCH_OPTIONS: cmd.append(ALLOWED_SEARCH_OPTIONS.get(opt)) cmd.append(criteria) - solvables = __zypper__.nolock.noraise.xml.call(*cmd).getElementsByTagName('solvable') + solvables = __zypper__.nolock.noraise.xml.call(*cmd).getElementsByTagName( + "solvable" + ) if not solvables: - raise CommandExecutionError( - 'No packages found matching \'{0}\''.format(criteria) - ) + raise CommandExecutionError("No packages found matching '{0}'".format(criteria)) out = {} for solvable in solvables: - out[solvable.getAttribute('name')] = dict() + out[solvable.getAttribute("name")] = dict() for k, v in solvable.attributes.items(): - out[solvable.getAttribute('name')][k] = v + out[solvable.getAttribute("name")][k] = v return out def _get_first_aggregate_text(node_list): - ''' + """ Extract text from the first occurred DOM aggregate. - ''' + """ if not node_list: - return '' + return "" out = [] for node in node_list[0].childNodes: if node.nodeType == dom.Document.TEXT_NODE: out.append(node.nodeValue) - return '\n'.join(out) + return "\n".join(out) def list_products(all=False, refresh=False): - ''' + """ List all available or installed SUSE products. all @@ -2141,7 +2236,7 @@ def list_products(all=False, refresh=False): salt '*' pkg.list_products salt '*' pkg.list_products all=True - ''' + """ if refresh: refresh_db() @@ -2149,46 +2244,51 @@ def list_products(all=False, refresh=False): OEM_PATH = "/var/lib/suseRegister/OEM" cmd = list() if not all: - cmd.append('--disable-repos') - cmd.append('products') + cmd.append("--disable-repos") + cmd.append("products") if not all: - cmd.append('-i') + cmd.append("-i") - product_list = __zypper__.nolock.xml.call(*cmd).getElementsByTagName('product-list') + product_list = __zypper__.nolock.xml.call(*cmd).getElementsByTagName("product-list") if not product_list: return ret # No products found - for prd in product_list[0].getElementsByTagName('product'): + for prd in product_list[0].getElementsByTagName("product"): p_nfo = dict() for k_p_nfo, v_p_nfo in prd.attributes.items(): - if k_p_nfo in ['isbase', 'installed']: - p_nfo[k_p_nfo] = bool(v_p_nfo in ['true', '1']) + if k_p_nfo in ["isbase", "installed"]: + p_nfo[k_p_nfo] = bool(v_p_nfo in ["true", "1"]) elif v_p_nfo: p_nfo[k_p_nfo] = v_p_nfo - eol = prd.getElementsByTagName('endoflife') + eol = prd.getElementsByTagName("endoflife") if eol: - p_nfo['eol'] = eol[0].getAttribute('text') - p_nfo['eol_t'] = int(eol[0].getAttribute('time_t') or 0) - p_nfo['description'] = " ".join( - [line.strip() for line in _get_first_aggregate_text( - prd.getElementsByTagName('description') - ).split(os.linesep)] + p_nfo["eol"] = eol[0].getAttribute("text") + p_nfo["eol_t"] = int(eol[0].getAttribute("time_t") or 0) + p_nfo["description"] = " ".join( + [ + line.strip() + for line in _get_first_aggregate_text( + prd.getElementsByTagName("description") + ).split(os.linesep) + ] ) - if 'productline' in p_nfo and p_nfo['productline']: - oem_file = os.path.join(OEM_PATH, p_nfo['productline']) + if "productline" in p_nfo and p_nfo["productline"]: + oem_file = os.path.join(OEM_PATH, p_nfo["productline"]) if os.path.isfile(oem_file): - with salt.utils.files.fopen(oem_file, 'r') as rfile: - oem_release = salt.utils.stringutils.to_unicode(rfile.readline()).strip() + with salt.utils.files.fopen(oem_file, "r") as rfile: + oem_release = salt.utils.stringutils.to_unicode( + rfile.readline() + ).strip() if oem_release: - p_nfo['release'] = oem_release + p_nfo["release"] = oem_release ret.append(p_nfo) return ret def download(*packages, **kwargs): - ''' + """ Download packages to the local disk. refresh @@ -2202,42 +2302,46 @@ def download(*packages, **kwargs): salt '*' pkg.download httpd salt '*' pkg.download httpd postfix - ''' + """ if not packages: - raise SaltInvocationError('No packages specified') + raise SaltInvocationError("No packages specified") - refresh = kwargs.get('refresh', False) + refresh = kwargs.get("refresh", False) if refresh: refresh_db() pkg_ret = {} - for dld_result in __zypper__.xml.call('download', *packages).getElementsByTagName("download-result"): + for dld_result in __zypper__.xml.call("download", *packages).getElementsByTagName( + "download-result" + ): repo = dld_result.getElementsByTagName("repository")[0] path = dld_result.getElementsByTagName("localfile")[0].getAttribute("path") pkg_info = { - 'repository-name': repo.getAttribute('name'), - 'repository-alias': repo.getAttribute('alias'), - 'path': path, + "repository-name": repo.getAttribute("name"), + "repository-alias": repo.getAttribute("alias"), + "path": path, } - key = _get_first_aggregate_text( - dld_result.getElementsByTagName('name') - ) - if __salt__['lowpkg.checksum'](pkg_info['path']): + key = _get_first_aggregate_text(dld_result.getElementsByTagName("name")) + if __salt__["lowpkg.checksum"](pkg_info["path"]): pkg_ret[key] = pkg_info if pkg_ret: failed = [pkg for pkg in packages if pkg not in pkg_ret] if failed: - pkg_ret['_error'] = ('The following package(s) failed to download: {0}'.format(', '.join(failed))) + pkg_ret[ + "_error" + ] = "The following package(s) failed to download: {0}".format( + ", ".join(failed) + ) return pkg_ret raise CommandExecutionError( - 'Unable to download packages: {0}'.format(', '.join(packages)) + "Unable to download packages: {0}".format(", ".join(packages)) ) def list_downloaded(): - ''' + """ .. versionadded:: 2017.7.0 List prefetched packages downloaded by Zypper in the local disk. @@ -2247,26 +2351,28 @@ def list_downloaded(): .. code-block:: bash salt '*' pkg.list_downloaded - ''' - CACHE_DIR = '/var/cache/zypp/packages/' + """ + CACHE_DIR = "/var/cache/zypp/packages/" ret = {} for root, dirnames, filenames in salt.utils.path.os_walk(CACHE_DIR): - for filename in fnmatch.filter(filenames, '*.rpm'): + for filename in fnmatch.filter(filenames, "*.rpm"): package_path = os.path.join(root, filename) - pkg_info = __salt__['lowpkg.bin_pkg_info'](package_path) + pkg_info = __salt__["lowpkg.bin_pkg_info"](package_path) pkg_timestamp = int(os.path.getctime(package_path)) - ret.setdefault(pkg_info['name'], {})[pkg_info['version']] = { - 'path': package_path, - 'size': os.path.getsize(package_path), - 'creation_date_time_t': pkg_timestamp, - 'creation_date_time': datetime.datetime.utcfromtimestamp(pkg_timestamp).isoformat(), + ret.setdefault(pkg_info["name"], {})[pkg_info["version"]] = { + "path": package_path, + "size": os.path.getsize(package_path), + "creation_date_time_t": pkg_timestamp, + "creation_date_time": datetime.datetime.utcfromtimestamp( + pkg_timestamp + ).isoformat(), } return ret def diff(*paths): - ''' + """ Return a formatted diff between current files and original in a package. NOTE: this function includes all files (configuration and not), but does not work on binary content. @@ -2279,49 +2385,51 @@ def diff(*paths): .. code-block:: bash salt '*' pkg.diff /etc/apache2/httpd.conf /etc/sudoers - ''' + """ ret = {} pkg_to_paths = {} for pth in paths: - pth_pkg = __salt__['lowpkg.owner'](pth) + pth_pkg = __salt__["lowpkg.owner"](pth) if not pth_pkg: - ret[pth] = os.path.exists(pth) and 'Not managed' or 'N/A' + ret[pth] = os.path.exists(pth) and "Not managed" or "N/A" else: if pkg_to_paths.get(pth_pkg) is None: pkg_to_paths[pth_pkg] = [] pkg_to_paths[pth_pkg].append(pth) if pkg_to_paths: - local_pkgs = __salt__['pkg.download'](*pkg_to_paths.keys()) + local_pkgs = __salt__["pkg.download"](*pkg_to_paths.keys()) for pkg, files in six.iteritems(pkg_to_paths): for path in files: - ret[path] = __salt__['lowpkg.diff']( - local_pkgs[pkg]['path'], - path - ) or 'Unchanged' + ret[path] = ( + __salt__["lowpkg.diff"](local_pkgs[pkg]["path"], path) + or "Unchanged" + ) return ret def _get_patches(installed_only=False): - ''' + """ List all known patches in repos. - ''' + """ patches = {} - for element in __zypper__.nolock.xml.call('se', '-t', 'patch').getElementsByTagName('solvable'): - installed = element.getAttribute('status') == 'installed' + for element in __zypper__.nolock.xml.call("se", "-t", "patch").getElementsByTagName( + "solvable" + ): + installed = element.getAttribute("status") == "installed" if (installed_only and installed) or not installed_only: - patches[element.getAttribute('name')] = { - 'installed': installed, - 'summary': element.getAttribute('summary'), + patches[element.getAttribute("name")] = { + "installed": installed, + "summary": element.getAttribute("summary"), } return patches def list_patches(refresh=False): - ''' + """ .. versionadded:: 2017.7.0 List all known advisory patches from available repos. @@ -2336,7 +2444,7 @@ def list_patches(refresh=False): .. code-block:: bash salt '*' pkg.list_patches - ''' + """ if refresh: refresh_db() @@ -2344,7 +2452,7 @@ def list_patches(refresh=False): def list_installed_patches(): - ''' + """ .. versionadded:: 2017.7.0 List installed advisory patches on the system. @@ -2354,12 +2462,12 @@ def list_installed_patches(): .. code-block:: bash salt '*' pkg.list_installed_patches - ''' + """ return _get_patches(installed_only=True) def list_provides(**kwargs): - ''' + """ .. versionadded:: 2018.3.0 List package provides of installed packages as a dict. @@ -2370,13 +2478,15 @@ def list_provides(**kwargs): .. code-block:: bash salt '*' pkg.list_provides - ''' - ret = __context__.get('pkg.list_provides') + """ + ret = __context__.get("pkg.list_provides") if not ret: - cmd = ['rpm', '-qa', '--queryformat', '%{PROVIDES}_|-%{NAME}\n'] + cmd = ["rpm", "-qa", "--queryformat", "%{PROVIDES}_|-%{NAME}\n"] ret = dict() - for line in __salt__['cmd.run'](cmd, output_loglevel='trace', python_shell=False).splitlines(): - provide, realname = line.split('_|-') + for line in __salt__["cmd.run"]( + cmd, output_loglevel="trace", python_shell=False + ).splitlines(): + provide, realname = line.split("_|-") if provide == realname: continue @@ -2384,13 +2494,13 @@ def list_provides(**kwargs): ret[provide] = list() ret[provide].append(realname) - __context__['pkg.list_provides'] = ret + __context__["pkg.list_provides"] = ret return ret def resolve_capabilities(pkgs, refresh, **kwargs): - ''' + """ .. versionadded:: 2018.3.0 Convert name provides in ``pkgs`` into real package names if @@ -2416,7 +2526,7 @@ def resolve_capabilities(pkgs, refresh, **kwargs): .. code-block:: bash salt '*' pkg.resolve_capabilities resolve_capabilities=True w3m_ssl - ''' + """ if refresh: refresh_db() @@ -2429,14 +2539,14 @@ def resolve_capabilities(pkgs, refresh, **kwargs): name = pkg version = None - if kwargs.get('resolve_capabilities', False): + if kwargs.get("resolve_capabilities", False): try: - search(name, match='exact') + search(name, match="exact") except CommandExecutionError: # no package this such a name found # search for a package which provides this name try: - result = search(name, provides=True, match='exact') + result = search(name, provides=True, match="exact") if len(result) == 1: name = next(iter(result.keys())) elif len(result) > 1: diff --git a/salt/netapi/__init__.py b/salt/netapi/__init__.py index 88d550f27f8..bcbd4a76502 100644 --- a/salt/netapi/__init__.py +++ b/salt/netapi/__init__.py @@ -1,102 +1,103 @@ # encoding: utf-8 -''' +""" Make api awesomeness -''' +""" from __future__ import absolute_import, print_function, unicode_literals + # Import Python libs import inspect import os +import salt.client +import salt.client.ssh.client +import salt.config +import salt.exceptions + # Import Salt libs import salt.log # pylint: disable=W0611 -import salt.client -import salt.config import salt.runner import salt.syspaths -import salt.wheel import salt.utils.args -import salt.client.ssh.client -import salt.exceptions +import salt.wheel # Import third party libs from salt.ext import six class NetapiClient(object): - ''' + """ Provide a uniform method of accessing the various client interfaces in Salt in the form of low-data data structures. For example: >>> client = NetapiClient(__opts__) >>> lowstate = {'client': 'local', 'tgt': '*', 'fun': 'test.ping', 'arg': ''} >>> client.run(lowstate) - ''' + """ def __init__(self, opts): self.opts = opts def _is_master_running(self): - ''' + """ Perform a lightweight check to see if the master daemon is running Note, this will return an invalid success if the master crashed or was not shut down cleanly. - ''' + """ # Windows doesn't have IPC. Assume the master is running. # At worse, it will error 500. if salt.utils.platform.is_windows(): return True - if self.opts['transport'] == 'tcp': - ipc_file = 'publish_pull.ipc' + if self.opts["transport"] == "tcp": + ipc_file = "publish_pull.ipc" else: - ipc_file = 'workers.ipc' - return os.path.exists(os.path.join( - self.opts['sock_dir'], - ipc_file)) + ipc_file = "workers.ipc" + return os.path.exists(os.path.join(self.opts["sock_dir"], ipc_file)) def run(self, low): - ''' + """ Execute the specified function in the specified client by passing the lowstate - ''' + """ # Eauth currently requires a running daemon and commands run through # this method require eauth so perform a quick check to raise a # more meaningful error. if not self._is_master_running(): - raise salt.exceptions.SaltDaemonNotRunning( - 'Salt Master is not available.') + raise salt.exceptions.SaltDaemonNotRunning("Salt Master is not available.") - if low.get('client') not in CLIENTS: + if low.get("client") not in CLIENTS: raise salt.exceptions.SaltInvocationError( - 'Invalid client specified: \'{0}\''.format(low.get('client'))) + "Invalid client specified: '{0}'".format(low.get("client")) + ) - if not ('token' in low or 'eauth' in low): + if not ("token" in low or "eauth" in low): raise salt.exceptions.EauthAuthenticationError( - 'No authentication credentials given') + "No authentication credentials given" + ) - if low.get('raw_shell') and \ - not self.opts.get('netapi_allow_raw_shell'): + if low.get("raw_shell") and not self.opts.get("netapi_allow_raw_shell"): raise salt.exceptions.EauthAuthenticationError( - 'Raw shell option not allowed.') + "Raw shell option not allowed." + ) - l_fun = getattr(self, low['client']) + l_fun = getattr(self, low["client"]) f_call = salt.utils.args.format_call(l_fun, low) - return l_fun(*f_call.get('args', ()), **f_call.get('kwargs', {})) + return l_fun(*f_call.get("args", ()), **f_call.get("kwargs", {})) def local_async(self, *args, **kwargs): - ''' + """ Run :ref:`execution modules <all-salt.modules>` asynchronously Wraps :py:meth:`salt.client.LocalClient.run_job`. :return: job ID - ''' + """ local = salt.client.get_local_client(mopts=self.opts) return local.run_job(*args, **kwargs) def local(self, *args, **kwargs): - ''' + """ Run :ref:`execution modules <all-salt.modules>` synchronously See :py:meth:`salt.client.LocalClient.cmd` for all available @@ -108,23 +109,23 @@ class NetapiClient(object): ``fun``, is called with those parameters. :return: Returns the result from the execution module - ''' + """ local = salt.client.get_local_client(mopts=self.opts) return local.cmd(*args, **kwargs) def local_subset(self, *args, **kwargs): - ''' + """ Run :ref:`execution modules <all-salt.modules>` against subsets of minions .. versionadded:: 2016.3.0 Wraps :py:meth:`salt.client.LocalClient.cmd_subset` - ''' + """ local = salt.client.get_local_client(mopts=self.opts) return local.cmd_subset(*args, **kwargs) def local_batch(self, *args, **kwargs): - ''' + """ Run :ref:`execution modules <all-salt.modules>` against batches of minions .. versionadded:: 0.8.4 @@ -133,24 +134,25 @@ class NetapiClient(object): :return: Returns the result from the exeuction module for each batch of returns - ''' + """ local = salt.client.get_local_client(mopts=self.opts) return local.cmd_batch(*args, **kwargs) def ssh(self, *args, **kwargs): - ''' + """ Run salt-ssh commands synchronously Wraps :py:meth:`salt.client.ssh.client.SSHClient.cmd_sync`. :return: Returns the result from the salt-ssh command - ''' - ssh_client = salt.client.ssh.client.SSHClient(mopts=self.opts, - disable_custom_roster=True) + """ + ssh_client = salt.client.ssh.client.SSHClient( + mopts=self.opts, disable_custom_roster=True + ) return ssh_client.cmd_sync(kwargs) def runner(self, fun, timeout=None, full_return=False, **kwargs): - ''' + """ Run `runner modules <all-salt.runners>` synchronously Wraps :py:meth:`salt.runner.RunnerClient.cmd_sync`. @@ -159,13 +161,13 @@ class NetapiClient(object): Positional arguments are not supported. :return: Returns the result from the runner module - ''' - kwargs['fun'] = fun + """ + kwargs["fun"] = fun runner = salt.runner.RunnerClient(self.opts) return runner.cmd_sync(kwargs, timeout=timeout, full_return=full_return) def runner_async(self, fun, **kwargs): - ''' + """ Run `runner modules <all-salt.runners>` asynchronously Wraps :py:meth:`salt.runner.RunnerClient.cmd_async`. @@ -174,13 +176,13 @@ class NetapiClient(object): Positional arguments are not supported. :return: event data and a job ID for the executed function. - ''' - kwargs['fun'] = fun + """ + kwargs["fun"] = fun runner = salt.runner.RunnerClient(self.opts) return runner.cmd_async(kwargs) def wheel(self, fun, **kwargs): - ''' + """ Run :ref:`wheel modules <all-salt.wheel>` synchronously Wraps :py:meth:`salt.wheel.WheelClient.master_call`. @@ -189,13 +191,13 @@ class NetapiClient(object): Positional arguments are not supported. :return: Returns the result from the wheel module - ''' - kwargs['fun'] = fun + """ + kwargs["fun"] = fun wheel = salt.wheel.WheelClient(self.opts) return wheel.cmd_sync(kwargs) def wheel_async(self, fun, **kwargs): - ''' + """ Run :ref:`wheel modules <all-salt.wheel>` asynchronously Wraps :py:meth:`salt.wheel.WheelClient.master_call`. @@ -204,14 +206,16 @@ class NetapiClient(object): Positional arguments are not supported. :return: Returns the result from the wheel module - ''' - kwargs['fun'] = fun + """ + kwargs["fun"] = fun wheel = salt.wheel.WheelClient(self.opts) return wheel.cmd_async(kwargs) CLIENTS = [ - name for name, _ - in inspect.getmembers(NetapiClient, predicate=inspect.ismethod if six.PY2 else None) - if not (name == 'run' or name.startswith('_')) + name + for name, _ in inspect.getmembers( + NetapiClient, predicate=inspect.ismethod if six.PY2 else None + ) + if not (name == "run" or name.startswith("_")) ] diff --git a/salt/netapi/rest_cherrypy/__init__.py b/salt/netapi/rest_cherrypy/__init__.py index e5af3173228..eb83b26adb8 100644 --- a/salt/netapi/rest_cherrypy/__init__.py +++ b/salt/netapi/rest_cherrypy/__init__.py @@ -1,16 +1,18 @@ # encoding: utf-8 -''' +""" A script to start the CherryPy WSGI server This is run by ``salt-api`` and started in a multiprocess. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# pylint: disable=C0103 # Import Python libs import logging import os +# pylint: disable=C0103 + + # Import CherryPy without traceback so we can provide an intelligent log # message in the __virtual__ function try: @@ -20,14 +22,14 @@ try: except ImportError as exc: cpy_error = exc -__virtualname__ = os.path.abspath(__file__).rsplit(os.sep)[-2] or 'rest_cherrypy' +__virtualname__ = os.path.abspath(__file__).rsplit(os.sep)[-2] or "rest_cherrypy" logger = logging.getLogger(__virtualname__) -cpy_min = '3.2.2' +cpy_min = "3.2.2" def __virtual__(): - short_name = __name__.rsplit('.')[-1] + short_name = __name__.rsplit(".")[-1] mod_opts = __opts__.get(short_name, {}) if mod_opts: @@ -35,37 +37,40 @@ def __virtual__(): # run the module and increase logging severity to be helpful # Everything looks good; return the module name - if not cpy_error and 'port' in mod_opts: + if not cpy_error and "port" in mod_opts: return __virtualname__ # CherryPy wasn't imported; explain why if cpy_error: from salt.utils.versions import LooseVersion as V - if 'cherrypy' in globals() and V(cherrypy.__version__) < V(cpy_min): - error_msg = ("Required version of CherryPy is {0} or " - "greater.".format(cpy_min)) + if "cherrypy" in globals() and V(cherrypy.__version__) < V(cpy_min): + error_msg = "Required version of CherryPy is {0} or " "greater.".format( + cpy_min + ) else: error_msg = cpy_error - logger.error("Not loading '%s'. Error loading CherryPy: %s", - __name__, error_msg) + logger.error( + "Not loading '%s'. Error loading CherryPy: %s", __name__, error_msg + ) # Missing port config - if 'port' not in mod_opts: - logger.error("Not loading '%s'. 'port' not specified in config", - __name__) + if "port" not in mod_opts: + logger.error("Not loading '%s'. 'port' not specified in config", __name__) return False def verify_certs(*args): - ''' + """ Sanity checking for the specified SSL certificates - ''' - msg = ("Could not find a certificate: {0}\n" - "If you want to quickly generate a self-signed certificate, " - "use the tls.create_self_signed_cert function in Salt") + """ + msg = ( + "Could not find a certificate: {0}\n" + "If you want to quickly generate a self-signed certificate, " + "use the tls.create_self_signed_cert function in Salt" + ) for arg in args: if not os.path.exists(arg): @@ -73,26 +78,29 @@ def verify_certs(*args): def start(): - ''' + """ Start the server loop - ''' + """ from . import app + root, apiopts, conf = app.get_app(__opts__) - if not apiopts.get('disable_ssl', False): - if 'ssl_crt' not in apiopts or 'ssl_key' not in apiopts: - logger.error("Not starting '%s'. Options 'ssl_crt' and " - "'ssl_key' are required if SSL is not disabled.", - __name__) + if not apiopts.get("disable_ssl", False): + if "ssl_crt" not in apiopts or "ssl_key" not in apiopts: + logger.error( + "Not starting '%s'. Options 'ssl_crt' and " + "'ssl_key' are required if SSL is not disabled.", + __name__, + ) return None - verify_certs(apiopts['ssl_crt'], apiopts['ssl_key']) + verify_certs(apiopts["ssl_crt"], apiopts["ssl_key"]) - cherrypy.server.ssl_module = 'builtin' - cherrypy.server.ssl_certificate = apiopts['ssl_crt'] - cherrypy.server.ssl_private_key = apiopts['ssl_key'] - if 'ssl_chain' in apiopts.keys(): - cherrypy.server.ssl_certificate_chain = apiopts['ssl_chain'] + cherrypy.server.ssl_module = "builtin" + cherrypy.server.ssl_certificate = apiopts["ssl_crt"] + cherrypy.server.ssl_private_key = apiopts["ssl_key"] + if "ssl_chain" in apiopts.keys(): + cherrypy.server.ssl_certificate_chain = apiopts["ssl_chain"] - cherrypy.quickstart(root, apiopts.get('root_prefix', '/'), conf) + cherrypy.quickstart(root, apiopts.get("root_prefix", "/"), conf) diff --git a/salt/netapi/rest_cherrypy/app.py b/salt/netapi/rest_cherrypy/app.py index fa1b540e5f9..60992ebae59 100644 --- a/salt/netapi/rest_cherrypy/app.py +++ b/salt/netapi/rest_cherrypy/app.py @@ -1,5 +1,5 @@ # encoding: utf-8 -''' +""" A REST API for Salt =================== @@ -575,43 +575,33 @@ rest_cherrypy will remain the officially recommended REST API. .. |401| replace:: authentication required .. |406| replace:: requested Content-Type not available -''' +""" # We need a custom pylintrc here... # pylint: disable=W0212,E1101,C0103,R0201,W0221,W0613 # Import Python libs from __future__ import absolute_import + import collections -import itertools import functools +import itertools import logging import os import signal import tarfile -from multiprocessing import Process, Pipe - -logger = logging.getLogger(__name__) +from multiprocessing import Pipe, Process # Import third-party libs # pylint: disable=import-error, 3rd-party-module-not-gated import cherrypy -try: - from cherrypy.lib import cpstats -except AttributeError: - cpstats = None - logger.warn('Import of cherrypy.cpstats failed. ' - 'Possible upstream bug: ' - 'https://github.com/cherrypy/cherrypy/issues/1444') -except ImportError: - cpstats = None - logger.warn('Import of cherrypy.cpstats failed.') - -# pylint: enable=import-error, 3rd-party-module-not-gated # Import Salt libs import salt import salt.auth import salt.exceptions + +# Import salt-api libs +import salt.netapi import salt.utils.event import salt.utils.json import salt.utils.stringutils @@ -620,8 +610,24 @@ import salt.utils.yaml from salt.ext import six from salt.ext.six import BytesIO -# Import salt-api libs -import salt.netapi +logger = logging.getLogger(__name__) + + +try: + from cherrypy.lib import cpstats +except AttributeError: + cpstats = None + logger.warn( + "Import of cherrypy.cpstats failed. " + "Possible upstream bug: " + "https://github.com/cherrypy/cherrypy/issues/1444" + ) +except ImportError: + cpstats = None + logger.warn("Import of cherrypy.cpstats failed.") + +# pylint: enable=import-error, 3rd-party-module-not-gated + # Imports related to websocket try: @@ -630,62 +636,60 @@ try: HAS_WEBSOCKETS = True except ImportError: - websockets = type('websockets', (object,), { - 'SynchronizingWebsocket': None, - }) + websockets = type("websockets", (object,), {"SynchronizingWebsocket": None}) HAS_WEBSOCKETS = False def html_override_tool(): - ''' + """ Bypass the normal handler and serve HTML for all URLs The ``app_path`` setting must be non-empty and the request must ask for ``text/html`` in the ``Accept`` header. - ''' - apiopts = cherrypy.config['apiopts'] + """ + apiopts = cherrypy.config["apiopts"] request = cherrypy.request url_blacklist = ( - apiopts.get('app_path', '/app'), - apiopts.get('static_path', '/static'), + apiopts.get("app_path", "/app"), + apiopts.get("static_path", "/static"), ) - if 'app' not in cherrypy.config['apiopts']: + if "app" not in cherrypy.config["apiopts"]: return if request.path_info.startswith(url_blacklist): return - if request.headers.get('Accept') == '*/*': + if request.headers.get("Accept") == "*/*": return try: - wants_html = cherrypy.lib.cptools.accept('text/html') + wants_html = cherrypy.lib.cptools.accept("text/html") except cherrypy.HTTPError: return else: - if wants_html != 'text/html': + if wants_html != "text/html": return - raise cherrypy.InternalRedirect(apiopts.get('app_path', '/app')) + raise cherrypy.InternalRedirect(apiopts.get("app_path", "/app")) def salt_token_tool(): - ''' + """ If the custom authentication header is supplied, put it in the cookie dict so the rest of the session-based auth works as intended - ''' - x_auth = cherrypy.request.headers.get('X-Auth-Token', None) + """ + x_auth = cherrypy.request.headers.get("X-Auth-Token", None) # X-Auth-Token header trumps session cookie if x_auth: - cherrypy.request.cookie['session_id'] = x_auth + cherrypy.request.cookie["session_id"] = x_auth def salt_api_acl_tool(username, request): - ''' + """ ..versionadded:: 2016.3.0 Verifies user requests against the API whitelist. (User/IP pair) @@ -709,37 +713,34 @@ def salt_api_acl_tool(username, request): :type username: str :param request: Cherrypy request to check against the API. :type request: cherrypy.request - ''' - failure_str = ("[api_acl] Authentication failed for " - "user {0} from IP {1}") - success_str = ("[api_acl] Authentication sucessful for " - "user {0} from IP {1}") - pass_str = ("[api_acl] Authentication not checked for " - "user {0} from IP {1}") + """ + failure_str = "[api_acl] Authentication failed for " "user {0} from IP {1}" + success_str = "[api_acl] Authentication sucessful for " "user {0} from IP {1}" + pass_str = "[api_acl] Authentication not checked for " "user {0} from IP {1}" acl = None # Salt Configuration - salt_config = cherrypy.config.get('saltopts', None) + salt_config = cherrypy.config.get("saltopts", None) if salt_config: # Cherrypy Config. - cherrypy_conf = salt_config.get('rest_cherrypy', None) + cherrypy_conf = salt_config.get("rest_cherrypy", None) if cherrypy_conf: # ACL Config. - acl = cherrypy_conf.get('api_acl', None) + acl = cherrypy_conf.get("api_acl", None) ip = request.remote.ip if acl: - users = acl.get('users', {}) + users = acl.get("users", {}) if users: if username in users: - if ip in users[username] or '*' in users[username]: + if ip in users[username] or "*" in users[username]: logger.info(success_str.format(username, ip)) return True else: logger.info(failure_str.format(username, ip)) return False - elif username not in users and '*' in users: - if ip in users['*'] or '*' in users['*']: + elif username not in users and "*" in users: + if ip in users["*"] or "*" in users["*"]: logger.info(success_str.format(username, ip)) return True else: @@ -754,82 +755,82 @@ def salt_api_acl_tool(username, request): def salt_ip_verify_tool(): - ''' + """ If there is a list of restricted IPs, verify current client is coming from one of those IPs. - ''' + """ # This is overly cumbersome and crude, # But, it's also safe... ish... - salt_config = cherrypy.config.get('saltopts', None) + salt_config = cherrypy.config.get("saltopts", None) if salt_config: - cherrypy_conf = salt_config.get('rest_cherrypy', None) + cherrypy_conf = salt_config.get("rest_cherrypy", None) if cherrypy_conf: - auth_ip_list = cherrypy_conf.get('authorized_ips', None) + auth_ip_list = cherrypy_conf.get("authorized_ips", None) if auth_ip_list: logger.debug("Found IP list: {0}".format(auth_ip_list)) - rem_ip = cherrypy.request.headers.get('Remote-Addr', None) + rem_ip = cherrypy.request.headers.get("Remote-Addr", None) logger.debug("Request from IP: {0}".format(rem_ip)) if rem_ip not in auth_ip_list: logger.error("Blocked IP: {0}".format(rem_ip)) - raise cherrypy.HTTPError(403, 'Bad IP') + raise cherrypy.HTTPError(403, "Bad IP") def salt_auth_tool(): - ''' + """ Redirect all unauthenticated requests to the login page - ''' + """ # Redirect to the login page if the session hasn't been authed - if 'token' not in cherrypy.session: # pylint: disable=W8601 + if "token" not in cherrypy.session: # pylint: disable=W8601 raise cherrypy.HTTPError(401) # Session is authenticated; inform caches - cherrypy.response.headers['Cache-Control'] = 'private' + cherrypy.response.headers["Cache-Control"] = "private" def cors_tool(): - ''' + """ Handle both simple and complex CORS requests Add CORS headers to each response. If the request is a CORS preflight request swap out the default handler with a simple, single-purpose handler that verifies the request and provides a valid CORS response. - ''' + """ req_head = cherrypy.request.headers resp_head = cherrypy.response.headers # Always set response headers necessary for 'simple' CORS. - resp_head['Access-Control-Allow-Origin'] = req_head.get('Origin', '*') - resp_head['Access-Control-Expose-Headers'] = 'GET, POST' - resp_head['Access-Control-Allow-Credentials'] = 'true' + resp_head["Access-Control-Allow-Origin"] = req_head.get("Origin", "*") + resp_head["Access-Control-Expose-Headers"] = "GET, POST" + resp_head["Access-Control-Allow-Credentials"] = "true" # Non-simple CORS preflight request; short-circuit the normal handler. - if cherrypy.request.method == 'OPTIONS': - ac_method = req_head.get('Access-Control-Request-Method', None) + if cherrypy.request.method == "OPTIONS": + ac_method = req_head.get("Access-Control-Request-Method", None) - allowed_methods = ['GET', 'POST'] + allowed_methods = ["GET", "POST"] allowed_headers = [ - 'Content-Type', - 'X-Auth-Token', - 'X-Requested-With', + "Content-Type", + "X-Auth-Token", + "X-Requested-With", ] if ac_method and ac_method in allowed_methods: - resp_head['Access-Control-Allow-Methods'] = ', '.join(allowed_methods) - resp_head['Access-Control-Allow-Headers'] = ', '.join(allowed_headers) + resp_head["Access-Control-Allow-Methods"] = ", ".join(allowed_methods) + resp_head["Access-Control-Allow-Headers"] = ", ".join(allowed_headers) - resp_head['Connection'] = 'keep-alive' - resp_head['Access-Control-Max-Age'] = '1400' + resp_head["Connection"] = "keep-alive" + resp_head["Access-Control-Max-Age"] = "1400" # Note: CherryPy on Py3 uses binary objects for the response # Python 2.6 also supports the byte prefix, so no need for conditionals - cherrypy.response.body = b'' + cherrypy.response.body = b"" cherrypy.response.status = 200 # CORS requests should short-circuit the other tools. cherrypy.serving.request.handler = None # Needed to avoid the auth_tool check. - if cherrypy.request.config.get('tools.sessions.on', False): - cherrypy.session['token'] = True + if cherrypy.request.config.get("tools.sessions.on", False): + cherrypy.session["token"] = True return True @@ -837,36 +838,42 @@ def cors_tool(): # Maps Content-Type to serialization functions; this is a tuple of tuples to # preserve order of preference. ct_out_map = ( - ('application/json', salt.utils.json.dumps), - ('application/x-yaml', functools.partial( - salt.utils.yaml.safe_dump, default_flow_style=False)), + ("application/json", salt.utils.json.dumps), + ( + "application/x-yaml", + functools.partial(salt.utils.yaml.safe_dump, default_flow_style=False), + ), ) def hypermedia_handler(*args, **kwargs): - ''' + """ Determine the best output format based on the Accept header, execute the regular handler, and transform the output to the request content type (even if it's an error). :param args: Pass args through to the main handler :param kwargs: Pass kwargs through to the main handler - ''' + """ # Execute the real handler. Handle or pass-through any errors we know how # to handle (auth & HTTP errors). Reformat any errors we don't know how to # handle as a data structure. try: cherrypy.response.processors = dict(ct_out_map) ret = cherrypy.serving.request._hypermedia_inner_handler(*args, **kwargs) - except (salt.exceptions.AuthenticationError, - salt.exceptions.AuthorizationError, - salt.exceptions.EauthAuthenticationError, - salt.exceptions.TokenAuthenticationError): + except ( + salt.exceptions.AuthenticationError, + salt.exceptions.AuthorizationError, + salt.exceptions.EauthAuthenticationError, + salt.exceptions.TokenAuthenticationError, + ): raise cherrypy.HTTPError(401) except salt.exceptions.SaltInvocationError: raise cherrypy.HTTPError(400) - except (salt.exceptions.SaltDaemonNotRunning, - salt.exceptions.SaltReqTimeoutError) as exc: + except ( + salt.exceptions.SaltDaemonNotRunning, + salt.exceptions.SaltReqTimeoutError, + ) as exc: raise cherrypy.HTTPError(503, exc.strerror) except salt.exceptions.SaltClientTimeout: raise cherrypy.HTTPError(504) @@ -877,28 +884,31 @@ def hypermedia_handler(*args, **kwargs): # Still check existence of TimeoutError and handle in CherryPy < 12. # The check was moved down from the SaltClientTimeout error line because # A one-line if statement throws a BaseException inheritance TypeError. - if hasattr(cherrypy, 'TimeoutError') and isinstance(exc, cherrypy.TimeoutError): + if hasattr(cherrypy, "TimeoutError") and isinstance(exc, cherrypy.TimeoutError): raise cherrypy.HTTPError(504) import traceback - logger.debug("Error while processing request for: %s", - cherrypy.request.path_info, - exc_info=True) + logger.debug( + "Error while processing request for: %s", + cherrypy.request.path_info, + exc_info=True, + ) cherrypy.response.status = 500 ret = { - 'status': cherrypy.response.status, - 'return': '{0}'.format(traceback.format_exc(exc)) - if cherrypy.config['debug'] - else "An unexpected error occurred"} + "status": cherrypy.response.status, + "return": "{0}".format(traceback.format_exc(exc)) + if cherrypy.config["debug"] + else "An unexpected error occurred", + } # Raises 406 if requested content-type is not supported best = cherrypy.lib.cptools.accept([i for (i, _) in ct_out_map]) # Transform the output from the handler into the requested output format - cherrypy.response.headers['Content-Type'] = best + cherrypy.response.headers["Content-Type"] = best out = cherrypy.response.processors[best] try: response = out(ret) @@ -906,18 +916,18 @@ def hypermedia_handler(*args, **kwargs): response = salt.utils.stringutils.to_bytes(response) return response except Exception: # pylint: disable=broad-except - msg = 'Could not serialize the return data from Salt.' + msg = "Could not serialize the return data from Salt." logger.debug(msg, exc_info=True) raise cherrypy.HTTPError(500, msg) def hypermedia_out(): - ''' + """ Determine the best handler for the requested content type Wrap the normal handler and transform the output from that handler into the requested content type - ''' + """ request = cherrypy.serving.request request._hypermedia_inner_handler = request.handler @@ -927,18 +937,20 @@ def hypermedia_out(): def process_request_body(fn): - ''' + """ A decorator to skip a processor function if process_request_body is False - ''' + """ + @functools.wraps(fn) def wrapped(*args, **kwargs): # pylint: disable=C0111 if cherrypy.request.process_request_body is not False: fn(*args, **kwargs) + return wrapped def urlencoded_processor(entity): - ''' + """ Accept x-www-form-urlencoded data (run through CherryPy's formatter) and reformat it into a Low State data structure. @@ -954,21 +966,21 @@ def urlencoded_processor(entity): -d fun='test.kwarg' -d arg='one=1' -d arg='two=2' :param entity: raw POST data - ''' + """ # First call out to CherryPy's default processor cherrypy._cpreqbody.process_urlencoded(entity) cherrypy._cpreqbody.process_urlencoded(entity) cherrypy.serving.request.unserialized_data = entity.params - cherrypy.serving.request.raw_body = '' + cherrypy.serving.request.raw_body = "" @process_request_body def json_processor(entity): - ''' + """ Unserialize raw POST data in JSON format to a Python data structure. :param entity: raw POST data - ''' + """ if six.PY2: body = entity.fp.read() else: @@ -981,18 +993,18 @@ def json_processor(entity): try: cherrypy.serving.request.unserialized_data = salt.utils.json.loads(body) except ValueError: - raise cherrypy.HTTPError(400, 'Invalid JSON document') + raise cherrypy.HTTPError(400, "Invalid JSON document") cherrypy.serving.request.raw_body = body @process_request_body def yaml_processor(entity): - ''' + """ Unserialize raw POST data in YAML format to a Python data structure. :param entity: raw POST data - ''' + """ if six.PY2: body = entity.fp.read() else: @@ -1004,21 +1016,21 @@ def yaml_processor(entity): try: cherrypy.serving.request.unserialized_data = salt.utils.yaml.safe_load(body) except ValueError: - raise cherrypy.HTTPError(400, 'Invalid YAML document') + raise cherrypy.HTTPError(400, "Invalid YAML document") cherrypy.serving.request.raw_body = body @process_request_body def text_processor(entity): - ''' + """ Attempt to unserialize plain text as JSON Some large services still send JSON with a text/plain Content-Type. Those services are bad and should feel bad. :param entity: raw POST data - ''' + """ if six.PY2: body = entity.fp.read() else: @@ -1036,7 +1048,7 @@ def text_processor(entity): def hypermedia_in(): - ''' + """ Unserialize POST/PUT data of a specified Content-Type. The following custom processors all are intended to format Low State data @@ -1044,37 +1056,40 @@ def hypermedia_in(): :raises HTTPError: if the request contains a Content-Type that we do not have a processor for - ''' + """ # Be liberal in what you accept ct_in_map = { - 'application/x-www-form-urlencoded': urlencoded_processor, - 'application/json': json_processor, - 'application/x-yaml': yaml_processor, - 'text/yaml': yaml_processor, - 'text/plain': text_processor, + "application/x-www-form-urlencoded": urlencoded_processor, + "application/json": json_processor, + "application/x-yaml": yaml_processor, + "text/yaml": yaml_processor, + "text/plain": text_processor, } # Do not process the body for POST requests that have specified no content # or have not specified Content-Length - if (cherrypy.request.method.upper() == 'POST' - and cherrypy.request.headers.get('Content-Length', '0') == '0'): + if ( + cherrypy.request.method.upper() == "POST" + and cherrypy.request.headers.get("Content-Length", "0") == "0" + ): cherrypy.request.process_request_body = False cherrypy.request.unserialized_data = None cherrypy.request.body.processors.clear() cherrypy.request.body.default_proc = cherrypy.HTTPError( - 406, 'Content type not supported') + 406, "Content type not supported" + ) cherrypy.request.body.processors = ct_in_map def lowdata_fmt(): - ''' + """ Validate and format lowdata from incoming unserialized request data This tool requires that the hypermedia_in tool has already been run. - ''' + """ - if cherrypy.request.method.upper() != 'POST': + if cherrypy.request.method.upper() != "POST": return data = cherrypy.request.unserialized_data @@ -1084,8 +1099,10 @@ def lowdata_fmt(): # headers for form encoded data (including charset or something similar) if data and isinstance(data, collections.Mapping): # Make the 'arg' param a list if not already - if 'arg' in data and not isinstance(data['arg'], list): # pylint: disable=unsupported-membership-test - data['arg'] = [data['arg']] + if "arg" in data and not isinstance( + data["arg"], list + ): # pylint: disable=unsupported-membership-test + data["arg"] = [data["arg"]] # Finally, make a Low State and put it in request cherrypy.request.lowstate = [data] @@ -1094,102 +1111,102 @@ def lowdata_fmt(): tools_config = { - 'on_start_resource': [ - ('html_override', html_override_tool), - ('salt_token', salt_token_tool), + "on_start_resource": [ + ("html_override", html_override_tool), + ("salt_token", salt_token_tool), ], - 'before_request_body': [ - ('cors_tool', cors_tool), - ('salt_auth', salt_auth_tool), - ('hypermedia_in', hypermedia_in), + "before_request_body": [ + ("cors_tool", cors_tool), + ("salt_auth", salt_auth_tool), + ("hypermedia_in", hypermedia_in), ], - 'before_handler': [ - ('lowdata_fmt', lowdata_fmt), - ('hypermedia_out', hypermedia_out), - ('salt_ip_verify', salt_ip_verify_tool), + "before_handler": [ + ("lowdata_fmt", lowdata_fmt), + ("hypermedia_out", hypermedia_out), + ("salt_ip_verify", salt_ip_verify_tool), ], } for hook, tool_list in tools_config.items(): for idx, tool_config in enumerate(tool_list): tool_name, tool_fn = tool_config - setattr(cherrypy.tools, tool_name, cherrypy.Tool( - hook, tool_fn, priority=(50 + idx))) + setattr( + cherrypy.tools, tool_name, cherrypy.Tool(hook, tool_fn, priority=(50 + idx)) + ) ############################################################################### class LowDataAdapter(object): - ''' + """ The primary entry point to Salt's REST API - ''' + """ + exposed = True _cp_config = { - 'tools.salt_token.on': True, - 'tools.sessions.on': True, - 'tools.sessions.timeout': 60 * 10, # 10 hours - + "tools.salt_token.on": True, + "tools.sessions.on": True, + "tools.sessions.timeout": 60 * 10, # 10 hours # 'tools.autovary.on': True, - - 'tools.hypermedia_out.on': True, - 'tools.hypermedia_in.on': True, - 'tools.lowdata_fmt.on': True, - 'tools.salt_ip_verify.on': True, + "tools.hypermedia_out.on": True, + "tools.hypermedia_in.on": True, + "tools.lowdata_fmt.on": True, + "tools.salt_ip_verify.on": True, } def __init__(self): - self.opts = cherrypy.config['saltopts'] - self.apiopts = cherrypy.config['apiopts'] + self.opts = cherrypy.config["saltopts"] + self.apiopts = cherrypy.config["apiopts"] self.api = salt.netapi.NetapiClient(self.opts) def exec_lowstate(self, client=None, token=None): - ''' + """ Pull a Low State data structure from request and execute the low-data chunks through Salt. The low-data chunks will be updated to include the authorization token for the current session. - ''' + """ lowstate = cherrypy.request.lowstate # Release the session lock before executing any potentially # long-running Salt commands. This allows different threads to execute # Salt commands concurrently without blocking. - if cherrypy.request.config.get('tools.sessions.on', False): + if cherrypy.request.config.get("tools.sessions.on", False): cherrypy.session.release_lock() # if the lowstate loaded isn't a list, lets notify the client if not isinstance(lowstate, list): - raise cherrypy.HTTPError(400, 'Lowstates must be a list') + raise cherrypy.HTTPError(400, "Lowstates must be a list") # Make any requested additions or modifications to each lowstate, then # execute each one and yield the result. for chunk in lowstate: if token: - chunk['token'] = token + chunk["token"] = token - if 'token' in chunk: + if "token" in chunk: # Make sure that auth token is hex try: - int(chunk['token'], 16) + int(chunk["token"], 16) except (TypeError, ValueError): - raise cherrypy.HTTPError(401, 'Invalid token') + raise cherrypy.HTTPError(401, "Invalid token") - if 'token' in chunk: + if "token" in chunk: # Make sure that auth token is hex try: - int(chunk['token'], 16) + int(chunk["token"], 16) except (TypeError, ValueError): - raise cherrypy.HTTPError(401, 'Invalid token') + raise cherrypy.HTTPError(401, "Invalid token") if client: - chunk['client'] = client + chunk["client"] = client # Make any 'arg' params a list if not already. # This is largely to fix a deficiency in the urlencoded format. - if 'arg' in chunk and not isinstance(chunk['arg'], list): - chunk['arg'] = [chunk['arg']] + if "arg" in chunk and not isinstance(chunk["arg"], list): + chunk["arg"] = [chunk["arg"]] ret = self.api.run(chunk) @@ -1200,9 +1217,9 @@ class LowDataAdapter(object): else: yield ret - @cherrypy.config(**{'tools.sessions.on': False}) + @cherrypy.config(**{"tools.sessions.on": False}) def GET(self): - ''' + """ An explanation of the API with links of where to go next .. http:get:: / @@ -1231,16 +1248,16 @@ class LowDataAdapter(object): HTTP/1.1 200 OK Content-Type: application/json - ''' + """ return { - 'return': "Welcome", - 'clients': salt.netapi.CLIENTS, + "return": "Welcome", + "clients": salt.netapi.CLIENTS, } @cherrypy.tools.salt_token() @cherrypy.tools.salt_auth() def POST(self, **kwargs): - ''' + """ Send one or more Salt commands in the request body .. http:post:: / @@ -1294,23 +1311,19 @@ class LowDataAdapter(object): ms-2: true ms-3: true ms-4: true - ''' - return { - 'return': list(self.exec_lowstate( - token=cherrypy.session.get('token'))) - } + """ + return {"return": list(self.exec_lowstate(token=cherrypy.session.get("token")))} class Minions(LowDataAdapter): - ''' + """ Convenience URLs for working with minions - ''' - _cp_config = dict(LowDataAdapter._cp_config, **{ - 'tools.salt_auth.on': True, - }) + """ + + _cp_config = dict(LowDataAdapter._cp_config, **{"tools.salt_auth.on": True}) def GET(self, mid=None): - ''' + """ A convenience URL for getting lists of minions or getting minion details @@ -1347,17 +1360,16 @@ class Minions(LowDataAdapter): - ms-3: grains.items: ... - ''' - cherrypy.request.lowstate = [{ - 'client': 'local', 'tgt': mid or '*', 'fun': 'grains.items', - }] + """ + cherrypy.request.lowstate = [ + {"client": "local", "tgt": mid or "*", "fun": "grains.items"} + ] return { - 'return': list(self.exec_lowstate( - token=cherrypy.session.get('token'))), + "return": list(self.exec_lowstate(token=cherrypy.session.get("token"))), } def POST(self, **kwargs): - ''' + """ Start an execution command and immediately return the job id .. http:post:: /minions @@ -1409,27 +1421,27 @@ class Minions(LowDataAdapter): _links: jobs: - href: /jobs/20130603122505459265 - ''' - job_data = list(self.exec_lowstate(client='local_async', - token=cherrypy.session.get('token'))) + """ + job_data = list( + self.exec_lowstate( + client="local_async", token=cherrypy.session.get("token") + ) + ) cherrypy.response.status = 202 return { - 'return': job_data, - '_links': { - 'jobs': [{'href': '/jobs/{0}'.format(i['jid'])} - for i in job_data if i], + "return": job_data, + "_links": { + "jobs": [{"href": "/jobs/{0}".format(i["jid"])} for i in job_data if i], }, } class Jobs(LowDataAdapter): - _cp_config = dict(LowDataAdapter._cp_config, **{ - 'tools.salt_auth.on': True, - }) + _cp_config = dict(LowDataAdapter._cp_config, **{"tools.salt_auth.on": True}) - def GET(self, jid=None, timeout=''): - ''' + def GET(self, jid=None, timeout=""): + """ A convenience URL for getting lists of previously run jobs or getting the return from a single job @@ -1511,46 +1523,45 @@ class Jobs(LowDataAdapter): - 1 - 2 - 6.9141387939453125e-06 - ''' - lowstate = {'client': 'runner'} + """ + lowstate = {"client": "runner"} if jid: - lowstate.update({'fun': 'jobs.list_job', 'jid': jid}) + lowstate.update({"fun": "jobs.list_job", "jid": jid}) else: - lowstate.update({'fun': 'jobs.list_jobs'}) + lowstate.update({"fun": "jobs.list_jobs"}) cherrypy.request.lowstate = [lowstate] - job_ret_info = list(self.exec_lowstate( - token=cherrypy.session.get('token'))) + job_ret_info = list(self.exec_lowstate(token=cherrypy.session.get("token"))) ret = {} if jid: - ret['info'] = [job_ret_info[0]] + ret["info"] = [job_ret_info[0]] minion_ret = {} - returns = job_ret_info[0].get('Result') + returns = job_ret_info[0].get("Result") for minion in returns: - if u'return' in returns[minion]: - minion_ret[minion] = returns[minion].get(u'return') + if u"return" in returns[minion]: + minion_ret[minion] = returns[minion].get(u"return") else: - minion_ret[minion] = returns[minion].get('return') - ret['return'] = [minion_ret] + minion_ret[minion] = returns[minion].get("return") + ret["return"] = [minion_ret] else: - ret['return'] = [job_ret_info[0]] + ret["return"] = [job_ret_info[0]] return ret class Keys(LowDataAdapter): - ''' + """ Convenience URLs for working with minion keys .. versionadded:: 2014.7.0 These URLs wrap the functionality provided by the :py:mod:`key wheel module <salt.wheel.key>` functions. - ''' + """ def GET(self, mid=None): - ''' + """ Show the list of minion keys or detail on a specific key .. versionadded:: 2014.7.0 @@ -1618,27 +1629,20 @@ class Keys(LowDataAdapter): return: minions: jerry: 51:93:b3:d0:9f:3a:6d:e5:28:67:c2:4b:27:d6:cd:2b - ''' + """ if mid: - lowstate = [{ - 'client': 'wheel', - 'fun': 'key.finger', - 'match': mid, - }] + lowstate = [{"client": "wheel", "fun": "key.finger", "match": mid}] else: - lowstate = [{ - 'client': 'wheel', - 'fun': 'key.list_all', - }] + lowstate = [{"client": "wheel", "fun": "key.list_all"}] cherrypy.request.lowstate = lowstate - result = self.exec_lowstate(token=cherrypy.session.get('token')) + result = self.exec_lowstate(token=cherrypy.session.get("token")) - return {'return': next(result, {}).get('data', {}).get('return', {})} + return {"return": next(result, {}).get("data", {}).get("return", {})} - @cherrypy.config(**{'tools.hypermedia_out.on': False, 'tools.sessions.on': False}) + @cherrypy.config(**{"tools.hypermedia_out.on": False, "tools.sessions.on": False}) def POST(self, **kwargs): - r''' + r""" Easily generate keys for a minion and auto-accept the new key Accepts all the same parameters as the :py:func:`key.gen_accept @@ -1701,29 +1705,26 @@ class Keys(LowDataAdapter): Content-Type: application/x-tar jerry.pub0000644000000000000000000000070300000000000010730 0ustar 00000000000000 - ''' + """ lowstate = cherrypy.request.lowstate - lowstate[0].update({ - 'client': 'wheel', - 'fun': 'key.gen_accept', - }) + lowstate[0].update({"client": "wheel", "fun": "key.gen_accept"}) - if 'mid' in lowstate[0]: - lowstate[0]['id_'] = lowstate[0].pop('mid') + if "mid" in lowstate[0]: + lowstate[0]["id_"] = lowstate[0].pop("mid") result = self.exec_lowstate() - ret = next(result, {}).get('data', {}).get('return', {}) + ret = next(result, {}).get("data", {}).get("return", {}) - pub_key = ret.get('pub', '') - pub_key_file = tarfile.TarInfo('minion.pub') + pub_key = ret.get("pub", "") + pub_key_file = tarfile.TarInfo("minion.pub") pub_key_file.size = len(pub_key) - priv_key = ret.get('priv', '') - priv_key_file = tarfile.TarInfo('minion.pem') + priv_key = ret.get("priv", "") + priv_key_file = tarfile.TarInfo("minion.pem") priv_key_file.size = len(priv_key) fileobj = BytesIO() - tarball = tarfile.open(fileobj=fileobj, mode='w') + tarball = tarfile.open(fileobj=fileobj, mode="w") if six.PY3: pub_key = pub_key.encode(__salt_system_encoding__) @@ -1734,21 +1735,23 @@ class Keys(LowDataAdapter): tarball.close() headers = cherrypy.response.headers - headers['Content-Disposition'] = 'attachment; filename="saltkeys-{0}.tar"'.format(lowstate[0]['id_']) - headers['Content-Type'] = 'application/x-tar' - headers['Content-Length'] = len(fileobj.getvalue()) - headers['Cache-Control'] = 'no-cache' + headers[ + "Content-Disposition" + ] = 'attachment; filename="saltkeys-{0}.tar"'.format(lowstate[0]["id_"]) + headers["Content-Type"] = "application/x-tar" + headers["Content-Length"] = len(fileobj.getvalue()) + headers["Cache-Control"] = "no-cache" fileobj.seek(0) return fileobj class Login(LowDataAdapter): - ''' + """ Log in to receive a session token :ref:`Authentication information <rest_cherrypy-auth>`. - ''' + """ def __init__(self, *args, **kwargs): super(Login, self).__init__(*args, **kwargs) @@ -1756,7 +1759,7 @@ class Login(LowDataAdapter): self.auth = salt.auth.Resolver(self.opts) def GET(self): - ''' + """ Present the login interface .. http:get:: /login @@ -1785,16 +1788,16 @@ class Login(LowDataAdapter): HTTP/1.1 200 OK Content-Type: text/html - ''' - cherrypy.response.headers['WWW-Authenticate'] = 'Session' + """ + cherrypy.response.headers["WWW-Authenticate"] = "Session" return { - 'status': cherrypy.response.status, - 'return': "Please log in", + "status": cherrypy.response.status, + "return": "Please log in", } def POST(self, **kwargs): - ''' + """ :ref:`Authenticate <rest_cherrypy-auth>` against Salt's eauth system .. http:post:: /login @@ -1859,10 +1862,9 @@ class Login(LowDataAdapter): "test.*" ] }} - ''' + """ if not self.api._is_master_running(): - raise salt.exceptions.SaltDaemonNotRunning( - 'Salt Master is not available.') + raise salt.exceptions.SaltDaemonNotRunning("Salt Master is not available.") # the urlencoded_processor will wrap this in a list if isinstance(cherrypy.serving.request.lowstate, list): @@ -1870,88 +1872,100 @@ class Login(LowDataAdapter): else: creds = cherrypy.serving.request.lowstate - username = creds.get('username', None) + username = creds.get("username", None) # Validate against the whitelist. if not salt_api_acl_tool(username, cherrypy.request): raise cherrypy.HTTPError(401) # Mint token. token = self.auth.mk_token(creds) - if 'token' not in token: - raise cherrypy.HTTPError(401, - 'Could not authenticate using provided credentials') + if "token" not in token: + raise cherrypy.HTTPError( + 401, "Could not authenticate using provided credentials" + ) - cherrypy.response.headers['X-Auth-Token'] = cherrypy.session.id - cherrypy.session['token'] = token['token'] - cherrypy.session['timeout'] = (token['expire'] - token['start']) / 60 + cherrypy.response.headers["X-Auth-Token"] = cherrypy.session.id + cherrypy.session["token"] = token["token"] + cherrypy.session["timeout"] = (token["expire"] - token["start"]) / 60 # Grab eauth config for the current backend for the current user try: - eauth = self.opts.get('external_auth', {}).get(token['eauth'], {}) + eauth = self.opts.get("external_auth", {}).get(token["eauth"], {}) - if token['eauth'] == 'django' and '^model' in eauth: - perms = token['auth_list'] + if token["eauth"] == "django" and "^model" in eauth: + perms = token["auth_list"] else: # Get sum of '*' perms, user-specific perms, and group-specific perms - perms = eauth.get(token['name'], []) - perms.extend(eauth.get('*', [])) + perms = eauth.get(token["name"], []) + perms.extend(eauth.get("*", [])) - if 'groups' in token and token['groups']: - user_groups = set(token['groups']) - eauth_groups = set([i.rstrip('%') for i in eauth.keys() if i.endswith('%')]) + if "groups" in token and token["groups"]: + user_groups = set(token["groups"]) + eauth_groups = set( + [i.rstrip("%") for i in eauth.keys() if i.endswith("%")] + ) for group in user_groups & eauth_groups: - perms.extend(eauth['{0}%'.format(group)]) + perms.extend(eauth["{0}%".format(group)]) if not perms: logger.debug("Eauth permission list not found.") except Exception: # pylint: disable=broad-except - logger.debug("Configuration for external_auth malformed for " - "eauth '{0}', and user '{1}'." - .format(token.get('eauth'), token.get('name')), exc_info=True) + logger.debug( + "Configuration for external_auth malformed for " + "eauth '{0}', and user '{1}'.".format( + token.get("eauth"), token.get("name") + ), + exc_info=True, + ) perms = None - return {'return': [{ - 'token': cherrypy.session.id, - 'expire': token['expire'], - 'start': token['start'], - 'user': token['name'], - 'eauth': token['eauth'], - 'perms': perms or {}, - }]} + return { + "return": [ + { + "token": cherrypy.session.id, + "expire": token["expire"], + "start": token["start"], + "user": token["name"], + "eauth": token["eauth"], + "perms": perms or {}, + } + ] + } class Logout(LowDataAdapter): - ''' + """ Class to remove or invalidate sessions - ''' - _cp_config = dict(LowDataAdapter._cp_config, **{ - 'tools.salt_auth.on': True, + """ - 'tools.lowdata_fmt.on': False, - }) + _cp_config = dict( + LowDataAdapter._cp_config, + **{"tools.salt_auth.on": True, "tools.lowdata_fmt.on": False} + ) def POST(self): - ''' + """ Destroy the currently active session and expire the session cookie - ''' + """ cherrypy.lib.sessions.expire() # set client-side to expire cherrypy.session.regenerate() # replace server-side with new - return {'return': "Your token has been cleared"} + return {"return": "Your token has been cleared"} class Token(LowDataAdapter): - ''' + """ Generate a Salt token from eauth credentials Wraps functionality in the :py:mod:`auth Runner <salt.runners.auth>`. .. versionadded:: 2017.7.0 - ''' - @cherrypy.config(**{'tools.sessions.on': False}) + """ + + @cherrypy.config(**{"tools.sessions.on": False}) def POST(self, **kwargs): - r''' + r""" .. http:post:: /token Generate a Salt eauth token @@ -1986,27 +2000,30 @@ class Token(LowDataAdapter): "name": "saltdev", "eauth": "auto" }] - ''' + """ for creds in cherrypy.request.lowstate: try: - creds.update({ - 'client': 'runner', - 'fun': 'auth.mk_token', - 'kwarg': { - 'username': creds['username'], - 'password': creds['password'], - 'eauth': creds['eauth'], - }, - }) + creds.update( + { + "client": "runner", + "fun": "auth.mk_token", + "kwarg": { + "username": creds["username"], + "password": creds["password"], + "eauth": creds["eauth"], + }, + } + ) except KeyError: - raise cherrypy.HTTPError(400, - 'Require "username", "password", and "eauth" params') + raise cherrypy.HTTPError( + 400, 'Require "username", "password", and "eauth" params' + ) return list(self.exec_lowstate()) class Run(LowDataAdapter): - ''' + """ Run commands bypassing the :ref:`normal session handling <rest_cherrypy-auth>` @@ -2022,13 +2039,12 @@ class Run(LowDataAdapter): This endpoint accepts either a ``username``, ``password``, ``eauth`` trio, **or** a ``token`` kwarg and does not make use of sessions at all. - ''' - _cp_config = dict(LowDataAdapter._cp_config, **{ - 'tools.sessions.on': False, - }) + """ + + _cp_config = dict(LowDataAdapter._cp_config, **{"tools.sessions.on": False}) def POST(self, **kwargs): - ''' + """ Run commands bypassing the :ref:`normal session handling <rest_cherrypy-auth>` Other than that this URL is identical to the :py:meth:`root URL (/) <LowDataAdapter.POST>`. @@ -2142,14 +2158,14 @@ class Run(LowDataAdapter): retcode: 0 return: true success: true - ''' + """ return { - 'return': list(self.exec_lowstate()), + "return": list(self.exec_lowstate()), } class Events(object): - ''' + """ Expose the Salt event bus The event bus on the Salt master exposes a large variety of things, notably @@ -2159,33 +2175,35 @@ class Events(object): .. seealso:: :ref:`events` - ''' + """ + exposed = True - _cp_config = dict(LowDataAdapter._cp_config, **{ - 'response.stream': True, - 'tools.encode.encoding': 'utf-8', - - # Auth handled manually below - 'tools.salt_auth.on': False, - - 'tools.hypermedia_in.on': False, - 'tools.hypermedia_out.on': False, - }) + _cp_config = dict( + LowDataAdapter._cp_config, + **{ + "response.stream": True, + "tools.encode.encoding": "utf-8", + # Auth handled manually below + "tools.salt_auth.on": False, + "tools.hypermedia_in.on": False, + "tools.hypermedia_out.on": False, + } + ) def __init__(self): - self.opts = cherrypy.config['saltopts'] + self.opts = cherrypy.config["saltopts"] self.resolver = salt.auth.Resolver(self.opts) def _is_valid_token(self, auth_token): - ''' + """ Check if this is a valid salt-api token or valid Salt token salt-api tokens are regular session tokens that tie back to a real Salt token. Salt tokens are tokens generated by Salt's eauth system. :return bool: True if valid, False if not valid. - ''' + """ # Make sure that auth token is hex. If it's None, or something other # than hex, this will raise a ValueError. try: @@ -2197,7 +2215,7 @@ class Events(object): # salt-api token and we need to get the Salt token from there. orig_session, _ = cherrypy.session.cache.get(auth_token, ({}, None)) # If it's not in the session table, assume it's a regular Salt token. - salt_token = orig_session.get('token', auth_token) + salt_token = orig_session.get("token", auth_token) # The eauth system does not currently support perms for the event # stream, so we're just checking if the token exists not if the token @@ -2208,7 +2226,7 @@ class Events(object): return False def GET(self, token=None, salt_token=None): - r''' + r""" An HTTP stream of the Salt master event bus This stream is formatted per the Server Sent Events (SSE) spec. Each @@ -2332,10 +2350,13 @@ class Events(object): data: {"tag": "salt/job/20140112010149808995/new", "data": {"tgt_type": "glob", "jid": "20140112010149808995", "tgt": "jerry", "_stamp": "2014-01-12_01:01:49.809617", "user": "shouse", "arg": [], "fun": "test.ping", "minions": ["jerry"]}} tag: 20140112010149808995 data: {"tag": "20140112010149808995", "data": {"fun_args": [], "jid": "20140112010149808995", "return": true, "retcode": 0, "success": true, "cmd": "_return", "_stamp": "2014-01-12_01:01:49.819316", "fun": "test.ping", "id": "jerry"}} - ''' + """ cookies = cherrypy.request.cookie - auth_token = token or salt_token or ( - cookies['session_id'].value if 'session_id' in cookies else None) + auth_token = ( + token + or salt_token + or (cookies["session_id"].value if "session_id" in cookies else None) + ) if not self._is_valid_token(auth_token): raise cherrypy.HTTPError(401) @@ -2343,34 +2364,39 @@ class Events(object): # Release the session lock before starting the long-running response cherrypy.session.release_lock() - cherrypy.response.headers['Content-Type'] = 'text/event-stream' - cherrypy.response.headers['Cache-Control'] = 'no-cache' - cherrypy.response.headers['Connection'] = 'keep-alive' + cherrypy.response.headers["Content-Type"] = "text/event-stream" + cherrypy.response.headers["Cache-Control"] = "no-cache" + cherrypy.response.headers["Connection"] = "keep-alive" def listen(): - ''' + """ An iterator to yield Salt events - ''' + """ event = salt.utils.event.get_event( - 'master', - sock_dir=self.opts['sock_dir'], - transport=self.opts['transport'], - opts=self.opts, - listen=True) + "master", + sock_dir=self.opts["sock_dir"], + transport=self.opts["transport"], + opts=self.opts, + listen=True, + ) stream = event.iter_events(full=True, auto_reconnect=True) - yield str('retry: 400\n') # future lint: disable=blacklisted-function + yield str("retry: 400\n") # future lint: disable=blacklisted-function while True: data = next(stream) - yield str('tag: {0}\n').format(data.get('tag', '')) # future lint: disable=blacklisted-function - yield str('data: {0}\n\n').format(salt.utils.json.dumps(data)) # future lint: disable=blacklisted-function + yield str("tag: {0}\n").format( + data.get("tag", "") + ) # future lint: disable=blacklisted-function + yield str("data: {0}\n\n").format( + salt.utils.json.dumps(data) + ) # future lint: disable=blacklisted-function return listen() class WebsocketEndpoint(object): - ''' + """ Open a WebSocket connection to Salt's event bus The event bus on the Salt master exposes a large variety of things, notably @@ -2379,28 +2405,30 @@ class WebsocketEndpoint(object): Salt infrastructure. Uses websocket as the transport mechanism. .. seealso:: :ref:`events` - ''' + """ + exposed = True - _cp_config = dict(LowDataAdapter._cp_config, **{ - 'response.stream': True, - 'tools.encode.encoding': 'utf-8', - - # Auth handled manually below - 'tools.salt_auth.on': False, - - 'tools.hypermedia_in.on': False, - 'tools.hypermedia_out.on': False, - 'tools.websocket.on': True, - 'tools.websocket.handler_cls': websockets.SynchronizingWebsocket, - }) + _cp_config = dict( + LowDataAdapter._cp_config, + **{ + "response.stream": True, + "tools.encode.encoding": "utf-8", + # Auth handled manually below + "tools.salt_auth.on": False, + "tools.hypermedia_in.on": False, + "tools.hypermedia_out.on": False, + "tools.websocket.on": True, + "tools.websocket.handler_cls": websockets.SynchronizingWebsocket, + } + ) def __init__(self): - self.opts = cherrypy.config['saltopts'] + self.opts = cherrypy.config["saltopts"] self.auth = salt.auth.LoadAuth(self.opts) def GET(self, token=None, **kwargs): - ''' + """ Return a websocket connection of Salt's event stream .. http:get:: /ws/(token) @@ -2497,14 +2525,14 @@ class WebsocketEndpoint(object): Above examples show how to establish a websocket connection to Salt and activating real time updates from Salt's event stream by signaling ``websocket client ready``. - ''' + """ # Pulling the session token from an URL param is a workaround for # browsers not supporting CORS in the EventSource API. if token: orig_session, _ = cherrypy.session.cache.get(token, ({}, None)) - salt_token = orig_session.get('token') + salt_token = orig_session.get("token") else: - salt_token = cherrypy.session.get('token') + salt_token = cherrypy.session.get("token") # Manually verify the token if not salt_token or not self.auth.get_tok(salt_token): @@ -2518,18 +2546,19 @@ class WebsocketEndpoint(object): handler = cherrypy.request.ws_handler def event_stream(handler, pipe): - ''' + """ An iterator to return Salt events (and optionally format them) - ''' + """ # blocks until send is called on the parent end of this pipe. pipe.recv() event = salt.utils.event.get_event( - 'master', - sock_dir=self.opts['sock_dir'], - transport=self.opts['transport'], - opts=self.opts, - listen=True) + "master", + sock_dir=self.opts["sock_dir"], + transport=self.opts["transport"], + opts=self.opts, + listen=True, + ) stream = event.iter_events(full=True, auto_reconnect=True) SaltInfo = event_processor.SaltInfo(handler) @@ -2542,17 +2571,19 @@ class WebsocketEndpoint(object): data = next(stream) if data: try: # work around try to decode catch unicode errors - if 'format_events' in kwargs: + if "format_events" in kwargs: SaltInfo.process(data, salt_token, self.opts) else: handler.send( - str('data: {0}\n\n').format(salt.utils.json.dumps(data)), # future lint: disable=blacklisted-function - False + str("data: {0}\n\n").format( + salt.utils.json.dumps(data) + ), # future lint: disable=blacklisted-function + False, ) except UnicodeDecodeError: logger.error( - "Error: Salt event has non UTF-8 data:\n{0}" - .format(data)) + "Error: Salt event has non UTF-8 data:\n{0}".format(data) + ) parent_pipe, child_pipe = Pipe() handler.pipe = parent_pipe @@ -2564,7 +2595,7 @@ class WebsocketEndpoint(object): class Webhook(object): - ''' + """ A generic web hook entry point that fires an event on Salt's event bus External services can POST data to this URL to trigger an event in Salt. @@ -2606,32 +2637,36 @@ class Webhook(object): -d commit="${TRAVIS_COMMIT}" .. seealso:: :ref:`events`, :ref:`reactor <reactor>` - ''' + """ + exposed = True - tag_base = ['salt', 'netapi', 'hook'] + tag_base = ["salt", "netapi", "hook"] - _cp_config = dict(LowDataAdapter._cp_config, **{ - # Don't do any lowdata processing on the POST data - 'tools.lowdata_fmt.on': True, - - # Auth can be overridden in __init__(). - 'tools.salt_auth.on': True, - }) + _cp_config = dict( + LowDataAdapter._cp_config, + **{ + # Don't do any lowdata processing on the POST data + "tools.lowdata_fmt.on": True, + # Auth can be overridden in __init__(). + "tools.salt_auth.on": True, + } + ) def __init__(self): - self.opts = cherrypy.config['saltopts'] + self.opts = cherrypy.config["saltopts"] self.event = salt.utils.event.get_event( - 'master', - sock_dir=self.opts['sock_dir'], - transport=self.opts['transport'], - opts=self.opts, - listen=False) + "master", + sock_dir=self.opts["sock_dir"], + transport=self.opts["transport"], + opts=self.opts, + listen=False, + ) - if cherrypy.config['apiopts'].get('webhook_disable_auth'): - self._cp_config['tools.salt_auth.on'] = False + if cherrypy.config["apiopts"].get("webhook_disable_auth"): + self._cp_config["tools.salt_auth.on"] = False def POST(self, *args, **kwargs): - ''' + """ Fire an event in Salt with a custom event tag and data .. http:post:: /hook @@ -2713,38 +2748,35 @@ class Webhook(object): pillar: revision: {{ revision }} {% endif %} - ''' - tag = '/'.join(itertools.chain(self.tag_base, args)) + """ + tag = "/".join(itertools.chain(self.tag_base, args)) data = cherrypy.serving.request.unserialized_data if not data: data = {} - raw_body = getattr(cherrypy.serving.request, 'raw_body', '') + raw_body = getattr(cherrypy.serving.request, "raw_body", "") headers = dict(cherrypy.request.headers) - ret = self.event.fire_event({ - 'body': raw_body, - 'post': data, - 'headers': headers, - }, tag) - return {'success': ret} + ret = self.event.fire_event( + {"body": raw_body, "post": data, "headers": headers}, tag + ) + return {"success": ret} class Stats(object): - ''' + """ Expose statistics on the running CherryPy server - ''' + """ + exposed = True - _cp_config = dict(LowDataAdapter._cp_config, **{ - 'tools.salt_auth.on': True, - }) + _cp_config = dict(LowDataAdapter._cp_config, **{"tools.salt_auth.on": True}) def __init__(self): - if cherrypy.config['apiopts'].get('stats_disable_auth'): - self._cp_config['tools.salt_auth.on'] = False + if cherrypy.config["apiopts"].get("stats_disable_auth"): + self._cp_config["tools.salt_auth.on"] = False def GET(self): - ''' + """ Return a dump of statistics collected from the CherryPy server .. http:get:: /stats @@ -2757,21 +2789,22 @@ class Stats(object): :status 200: |200| :status 401: |401| :status 406: |406| - ''' - if hasattr(logging, 'statistics'): + """ + if hasattr(logging, "statistics"): return cpstats.extrapolate_statistics(logging.statistics) return {} class App(object): - ''' + """ Class to serve HTML5 apps - ''' + """ + exposed = True def GET(self, *args): - ''' + """ Serve a single static file ignoring the remaining path This is useful in combination with a browser-based app using the HTML5 @@ -2783,146 +2816,145 @@ class App(object): :status 200: |200| :status 401: |401| - ''' - apiopts = cherrypy.config['apiopts'] + """ + apiopts = cherrypy.config["apiopts"] - default_index = os.path.abspath(os.path.join( - os.path.dirname(__file__), 'index.html')) + default_index = os.path.abspath( + os.path.join(os.path.dirname(__file__), "index.html") + ) - return cherrypy.lib.static.serve_file( - apiopts.get('app', default_index)) + return cherrypy.lib.static.serve_file(apiopts.get("app", default_index)) class API(object): - ''' + """ Collect configuration and URL map for building the CherryPy app - ''' + """ + url_map = { - 'index': LowDataAdapter, - 'login': Login, - 'logout': Logout, - 'token': Token, - 'minions': Minions, - 'run': Run, - 'jobs': Jobs, - 'keys': Keys, - 'events': Events, - 'stats': Stats, + "index": LowDataAdapter, + "login": Login, + "logout": Logout, + "token": Token, + "minions": Minions, + "run": Run, + "jobs": Jobs, + "keys": Keys, + "events": Events, + "stats": Stats, } def _setattr_url_map(self): - ''' + """ Set an attribute on the local instance for each key/val in url_map CherryPy uses class attributes to resolve URLs. - ''' - if self.apiopts.get('enable_sessions', True) is False: - url_blacklist = ['login', 'logout', 'minions', 'jobs'] + """ + if self.apiopts.get("enable_sessions", True) is False: + url_blacklist = ["login", "logout", "minions", "jobs"] else: url_blacklist = [] - urls = ((url, cls) for url, cls in six.iteritems(self.url_map) - if url not in url_blacklist) + urls = ( + (url, cls) + for url, cls in six.iteritems(self.url_map) + if url not in url_blacklist + ) for url, cls in urls: setattr(self, url, cls()) def _update_url_map(self): - ''' + """ Assemble any dynamic or configurable URLs - ''' + """ if HAS_WEBSOCKETS: - self.url_map.update({ - 'ws': WebsocketEndpoint, - }) + self.url_map.update({"ws": WebsocketEndpoint}) # Allow the Webhook URL to be overridden from the conf. - self.url_map.update({ - self.apiopts.get('webhook_url', 'hook').lstrip('/'): Webhook, - }) + self.url_map.update( + {self.apiopts.get("webhook_url", "hook").lstrip("/"): Webhook} + ) # Enable the single-page JS app URL. - self.url_map.update({ - self.apiopts.get('app_path', 'app').lstrip('/'): App, - }) + self.url_map.update({self.apiopts.get("app_path", "app").lstrip("/"): App}) def __init__(self): - self.opts = cherrypy.config['saltopts'] - self.apiopts = cherrypy.config['apiopts'] + self.opts = cherrypy.config["saltopts"] + self.apiopts = cherrypy.config["apiopts"] self._update_url_map() self._setattr_url_map() def get_conf(self): - ''' + """ Combine the CherryPy configuration with the rest_cherrypy config values pulled from the master config and return the CherryPy configuration - ''' + """ conf = { - 'global': { - 'server.socket_host': self.apiopts.get('host', '0.0.0.0'), - 'server.socket_port': self.apiopts.get('port', 8000), - 'server.thread_pool': self.apiopts.get('thread_pool', 100), - 'server.socket_queue_size': self.apiopts.get('queue_size', 30), - 'max_request_body_size': self.apiopts.get( - 'max_request_body_size', 1048576), - 'debug': self.apiopts.get('debug', False), - 'log.access_file': self.apiopts.get('log_access_file', ''), - 'log.error_file': self.apiopts.get('log_error_file', ''), + "global": { + "server.socket_host": self.apiopts.get("host", "0.0.0.0"), + "server.socket_port": self.apiopts.get("port", 8000), + "server.thread_pool": self.apiopts.get("thread_pool", 100), + "server.socket_queue_size": self.apiopts.get("queue_size", 30), + "max_request_body_size": self.apiopts.get( + "max_request_body_size", 1048576 + ), + "debug": self.apiopts.get("debug", False), + "log.access_file": self.apiopts.get("log_access_file", ""), + "log.error_file": self.apiopts.get("log_error_file", ""), }, - '/': { - 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), - - 'tools.trailing_slash.on': True, - 'tools.gzip.on': True, - - 'tools.html_override.on': True, - 'tools.cors_tool.on': True, + "/": { + "request.dispatch": cherrypy.dispatch.MethodDispatcher(), + "tools.trailing_slash.on": True, + "tools.gzip.on": True, + "tools.html_override.on": True, + "tools.cors_tool.on": True, }, } - if salt.utils.versions.version_cmp(cherrypy.__version__, '12.0.0') < 0: + if salt.utils.versions.version_cmp(cherrypy.__version__, "12.0.0") < 0: # CherryPy >= 12.0 no longer supports "timeout_monitor", only set # this config option when using an older version of CherryPy. # See Issue #44601 for more information. - conf['global']['engine.timeout_monitor.on'] = self.apiopts.get( - 'expire_responses', True + conf["global"]["engine.timeout_monitor.on"] = self.apiopts.get( + "expire_responses", True ) - if cpstats and self.apiopts.get('collect_stats', False): - conf['/']['tools.cpstats.on'] = True + if cpstats and self.apiopts.get("collect_stats", False): + conf["/"]["tools.cpstats.on"] = True - if 'favicon' in self.apiopts: - conf['/favicon.ico'] = { - 'tools.staticfile.on': True, - 'tools.staticfile.filename': self.apiopts['favicon'], + if "favicon" in self.apiopts: + conf["/favicon.ico"] = { + "tools.staticfile.on": True, + "tools.staticfile.filename": self.apiopts["favicon"], } - if self.apiopts.get('debug', False) is False: - conf['global']['environment'] = 'production' + if self.apiopts.get("debug", False) is False: + conf["global"]["environment"] = "production" # Serve static media if the directory has been set in the configuration - if 'static' in self.apiopts: - conf[self.apiopts.get('static_path', '/static')] = { - 'tools.staticdir.on': True, - 'tools.staticdir.dir': self.apiopts['static'], + if "static" in self.apiopts: + conf[self.apiopts.get("static_path", "/static")] = { + "tools.staticdir.on": True, + "tools.staticdir.dir": self.apiopts["static"], } # Add to global config - cherrypy.config.update(conf['global']) + cherrypy.config.update(conf["global"]) return conf def get_app(opts): - ''' + """ Returns a WSGI app and a configuration dictionary - ''' - apiopts = opts.get(__name__.rsplit('.', 2)[-2], {}) # rest_cherrypy opts + """ + apiopts = opts.get(__name__.rsplit(".", 2)[-2], {}) # rest_cherrypy opts # Add Salt and salt-api config options to the main CherryPy config dict - cherrypy.config['saltopts'] = opts - cherrypy.config['apiopts'] = apiopts + cherrypy.config["saltopts"] = opts + cherrypy.config["apiopts"] = apiopts root = API() # cherrypy app cpyopts = root.get_conf() # cherrypy app opts diff --git a/salt/netapi/rest_cherrypy/event_processor.py b/salt/netapi/rest_cherrypy/event_processor.py index f0cf6d361a8..690477e7ec9 100644 --- a/salt/netapi/rest_cherrypy/event_processor.py +++ b/salt/netapi/rest_cherrypy/event_processor.py @@ -2,28 +2,29 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -# Import 3rd-party libs -from salt.ext import six +import logging # Import Salt libs import salt.netapi import salt.utils.json +# Import 3rd-party libs +from salt.ext import six + logger = logging.getLogger(__name__) class SaltInfo(object): - ''' + """ Class to handle processing and publishing of "real time" Salt upates. - ''' + """ def __init__(self, handler): - ''' + """ handler is expected to be the server side end of a websocket connection. - ''' + """ self.handler = handler # These represent a "real time" view into Salt's jobs. @@ -33,71 +34,72 @@ class SaltInfo(object): self.minions = {} def publish_minions(self): - ''' + """ Publishes minions as a list of dicts. - ''' + """ minions = [] for minion, minion_info in six.iteritems(self.minions): curr_minion = {} curr_minion.update(minion_info) - curr_minion.update({'id': minion}) + curr_minion.update({"id": minion}) minions.append(curr_minion) - ret = {'minions': minions} + ret = {"minions": minions} self.handler.send(salt.utils.json.dumps(ret), False) def publish(self, key, data): - ''' + """ Publishes the data to the event stream. - ''' + """ publish_data = {key: data} self.handler.send(salt.utils.json.dumps(publish_data), False) def process_minion_update(self, event_data): - ''' + """ Associate grains data with a minion and publish minion update - ''' - tag = event_data['tag'] - event_info = event_data['data'] + """ + tag = event_data["tag"] + event_info = event_data["data"] - _, _, _, _, mid = tag.split('/') + _, _, _, _, mid = tag.split("/") if not self.minions.get(mid, None): self.minions[mid] = {} minion = self.minions[mid] - minion.update({'grains': event_info['return']}) + minion.update({"grains": event_info["return"]}) self.publish_minions() def process_ret_job_event(self, event_data): - ''' + """ Process a /ret event returned by Salt for a particular minion. These events contain the returned results from a particular execution. - ''' - tag = event_data['tag'] - event_info = event_data['data'] + """ + tag = event_data["tag"] + event_info = event_data["data"] - _, _, jid, _, mid = tag.split('/') + _, _, jid, _, mid = tag.split("/") job = self.jobs.setdefault(jid, {}) - minion = job.setdefault('minions', {}).setdefault(mid, {}) - minion.update({'return': event_info['return']}) - minion.update({'retcode': event_info['retcode']}) - minion.update({'success': event_info['success']}) + minion = job.setdefault("minions", {}).setdefault(mid, {}) + minion.update({"return": event_info["return"]}) + minion.update({"retcode": event_info["retcode"]}) + minion.update({"success": event_info["success"]}) - job_complete = all([minion['success'] for mid, minion - in six.iteritems(job['minions'])]) + job_complete = all( + [minion["success"] for mid, minion in six.iteritems(job["minions"])] + ) if job_complete: - job['state'] = 'complete' + job["state"] = "complete" - self.publish('jobs', self.jobs) + self.publish("jobs", self.jobs) def process_new_job_event(self, event_data): - ''' + """ Creates a new job with properties from the event data like jid, function, args, timestamp. @@ -105,55 +107,55 @@ class SaltInfo(object): Minions that are participating in this job are also noted. - ''' + """ job = None - tag = event_data['tag'] - event_info = event_data['data'] + tag = event_data["tag"] + event_info = event_data["data"] minions = {} - for mid in event_info['minions']: - minions[mid] = {'success': False} + for mid in event_info["minions"]: + minions[mid] = {"success": False} job = { - 'jid': event_info['jid'], - 'start_time': event_info['_stamp'], - 'minions': minions, # is a dictionary keyed by mids - 'fun': event_info['fun'], - 'tgt': event_info['tgt'], - 'tgt_type': event_info['tgt_type'], - 'state': 'running', + "jid": event_info["jid"], + "start_time": event_info["_stamp"], + "minions": minions, # is a dictionary keyed by mids + "fun": event_info["fun"], + "tgt": event_info["tgt"], + "tgt_type": event_info["tgt_type"], + "state": "running", } - self.jobs[event_info['jid']] = job - self.publish('jobs', self.jobs) + self.jobs[event_info["jid"]] = job + self.publish("jobs", self.jobs) def process_key_event(self, event_data): - ''' + """ Tag: salt/key Data: {'_stamp': '2014-05-20T22:45:04.345583', 'act': 'delete', 'id': 'compute.home', 'result': True} - ''' + """ - tag = event_data['tag'] - event_info = event_data['data'] + tag = event_data["tag"] + event_info = event_data["data"] - if event_info['act'] == 'delete': - self.minions.pop(event_info['id'], None) - elif event_info['act'] == 'accept': - self.minions.setdefault(event_info['id'], {}) + if event_info["act"] == "delete": + self.minions.pop(event_info["id"], None) + elif event_info["act"] == "accept": + self.minions.setdefault(event_info["id"], {}) self.publish_minions() def process_presence_events(self, event_data, token, opts): - ''' + """ Check if any minions have connected or dropped. Send a message to the client if they have. - ''' - tag = event_data['tag'] - event_info = event_data['data'] + """ + tag = event_data["tag"] + event_info = event_data["data"] - minions_detected = event_info['present'] + minions_detected = event_info["present"] curr_minions = six.iterkeys(self.minions) changed = False @@ -168,44 +170,45 @@ class SaltInfo(object): # check if any new connections were made new_minions = set(minions_detected) - set(curr_minions) - tgt = ','.join(new_minions) + tgt = ",".join(new_minions) if tgt: changed = True client = salt.netapi.NetapiClient(opts) client.run( { - 'fun': 'grains.items', - 'tgt': tgt, - 'expr_type': 'list', - 'mode': 'client', - 'client': 'local', - 'asynchronous': 'local_async', - 'token': token, - }) + "fun": "grains.items", + "tgt": tgt, + "expr_type": "list", + "mode": "client", + "client": "local", + "asynchronous": "local_async", + "token": token, + } + ) if changed: self.publish_minions() def process(self, salt_data, token, opts): - ''' + """ Process events and publish data - ''' - parts = salt_data['tag'].split('/') + """ + parts = salt_data["tag"].split("/") if len(parts) < 2: return # TBD: Simplify these conditional expressions - if parts[1] == 'job': - if parts[3] == 'new': + if parts[1] == "job": + if parts[3] == "new": self.process_new_job_event(salt_data) - if salt_data['data']['fun'] == 'grains.items': + if salt_data["data"]["fun"] == "grains.items": self.minions = {} - elif parts[3] == 'ret': + elif parts[3] == "ret": self.process_ret_job_event(salt_data) - if salt_data['data']['fun'] == 'grains.items': + if salt_data["data"]["fun"] == "grains.items": self.process_minion_update(salt_data) - if parts[1] == 'key': + if parts[1] == "key": self.process_key_event(salt_data) - if parts[1] == 'presence': + if parts[1] == "presence": self.process_presence_events(salt_data, token, opts) diff --git a/salt/netapi/rest_cherrypy/tools/websockets.py b/salt/netapi/rest_cherrypy/tools/websockets.py index 941a9fb40e0..3573909ea26 100644 --- a/salt/netapi/rest_cherrypy/tools/websockets.py +++ b/salt/netapi/rest_cherrypy/tools/websockets.py @@ -5,6 +5,7 @@ from __future__ import absolute_import, print_function, unicode_literals import cherrypy from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool from ws4py.websocket import WebSocket + # pylint: enable=3rd-party-module-not-gated cherrypy.tools.websocket = WebSocketTool() @@ -12,7 +13,7 @@ WebSocketPlugin(cherrypy.engine).subscribe() class SynchronizingWebsocket(WebSocket): - ''' + """ Class to handle requests sent to this websocket connection. Each instance of this class represents a Salt websocket connection. Waits to receive a ``ready`` message from the client. @@ -22,7 +23,8 @@ class SynchronizingWebsocket(WebSocket): This class also kicks off initial information probing jobs when clients initially connect. These jobs help gather information about minions, jobs, and documentation. - ''' + """ + def __init__(self, *args, **kwargs): super(SynchronizingWebsocket, self).__init__(*args, **kwargs) @@ -41,7 +43,7 @@ class SynchronizingWebsocket(WebSocket): self.opts = None def received_message(self, message): - ''' + """ Checks if the client has sent a ready message. A ready message causes ``send()`` to be called on the ``parent end`` of the pipe. @@ -51,7 +53,7 @@ class SynchronizingWebsocket(WebSocket): This ensures completion of the underlying websocket connection and can be used to synchronize parallel senders. - ''' - if message.data.decode('utf-8') == 'websocket client ready': + """ + if message.data.decode("utf-8") == "websocket client ready": self.pipe.send(message) - self.send('server received message', False) + self.send("server received message", False) diff --git a/salt/netapi/rest_cherrypy/wsgi.py b/salt/netapi/rest_cherrypy/wsgi.py index 799f1ed0b59..97843dcdf43 100644 --- a/salt/netapi/rest_cherrypy/wsgi.py +++ b/salt/netapi/rest_cherrypy/wsgi.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -''' +""" Deployment ========== @@ -43,40 +43,42 @@ An example Apache virtual host configuration:: WSGIScriptAlias / /path/to/salt/netapi/rest_cherrypy/wsgi.py </VirtualHost> -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# pylint: disable=C0103 import os import cherrypy # pylint: disable=3rd-party-module-not-gated +# pylint: disable=C0103 + def bootstrap_app(): - ''' + """ Grab the opts dict of the master config by trying to import Salt - ''' + """ from salt.netapi.rest_cherrypy import app import salt.config __opts__ = salt.config.client_config( - os.environ.get('SALT_MASTER_CONFIG', '/etc/salt/master')) + os.environ.get("SALT_MASTER_CONFIG", "/etc/salt/master") + ) return app.get_app(__opts__) def get_application(*args): - ''' + """ Returns a WSGI application function. If you supply the WSGI app and config it will use that, otherwise it will try to obtain them from a local Salt installation - ''' + """ opts_tuple = args def wsgi_app(environ, start_response): root, _, conf = opts_tuple or bootstrap_app() - cherrypy.config.update({'environment': 'embedded'}) + cherrypy.config.update({"environment": "embedded"}) - cherrypy.tree.mount(root, '/', conf) + cherrypy.tree.mount(root, "/", conf) return cherrypy.tree(environ, start_response) return wsgi_app diff --git a/salt/netapi/rest_tornado/__init__.py b/salt/netapi/rest_tornado/__init__.py index d0521cb237b..8161b81cccb 100644 --- a/salt/netapi/rest_tornado/__init__.py +++ b/salt/netapi/rest_tornado/__init__.py @@ -2,6 +2,7 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import hashlib import logging import os @@ -10,28 +11,29 @@ import os import salt.auth from salt.utils.versions import StrictVersion as _StrictVersion -__virtualname__ = os.path.abspath(__file__).rsplit(os.sep)[-2] or 'rest_tornado' +__virtualname__ = os.path.abspath(__file__).rsplit(os.sep)[-2] or "rest_tornado" log = logging.getLogger(__virtualname__) # we require at least 4.0, as that includes all the Future's stuff we use -min_tornado_version = '4.0' +min_tornado_version = "4.0" has_tornado = False try: import salt.ext.tornado + if _StrictVersion(salt.ext.tornado.version) >= _StrictVersion(min_tornado_version): has_tornado = True else: - log.error('rest_tornado requires at least tornado %s', min_tornado_version) + log.error("rest_tornado requires at least tornado %s", min_tornado_version) except (ImportError, TypeError) as err: has_tornado = False - log.error('ImportError! %s', err) + log.error("ImportError! %s", err) def __virtual__(): mod_opts = __opts__.get(__virtualname__, {}) - if has_tornado and 'port' in mod_opts: + if has_tornado and "port" in mod_opts: return __virtualname__ return False @@ -41,7 +43,7 @@ def get_application(opts): try: from . import saltnado except ImportError as err: - log.error('ImportError! %s', err) + log.error("ImportError! %s", err) return None mod_opts = opts.get(__virtualname__, {}) @@ -59,10 +61,12 @@ def get_application(opts): ] # if you have enabled websockets, add them! - if mod_opts.get('websockets', False): + if mod_opts.get("websockets", False): from . import saltnado_websockets - token_pattern = r"([0-9A-Fa-f]{{{0}}})".format(len(getattr(hashlib, opts.get('hash_type', 'md5'))().hexdigest())) + token_pattern = r"([0-9A-Fa-f]{{{0}}})".format( + len(getattr(hashlib, opts.get("hash_type", "md5"))().hexdigest()) + ) all_events_pattern = r"/all_events/{0}".format(token_pattern) formatted_events_pattern = r"/formatted_events/{0}".format(token_pattern) log.debug("All events URL pattern is %s", all_events_pattern) @@ -76,7 +80,9 @@ def get_application(opts): (formatted_events_pattern, saltnado_websockets.FormattedEventsHandler), ] - application = salt.ext.tornado.web.Application(paths, debug=mod_opts.get('debug', False)) + application = salt.ext.tornado.web.Application( + paths, debug=mod_opts.get("debug", False) + ) application.opts = opts application.mod_opts = mod_opts @@ -85,46 +91,56 @@ def get_application(opts): def start(): - ''' + """ Start the saltnado! - ''' + """ mod_opts = __opts__.get(__virtualname__, {}) - if 'num_processes' not in mod_opts: - mod_opts['num_processes'] = 1 + if "num_processes" not in mod_opts: + mod_opts["num_processes"] = 1 - if mod_opts['num_processes'] > 1 and mod_opts.get('debug', False) is True: - raise Exception(( - 'Tornado\'s debug implementation is not compatible with multiprocess. ' - 'Either disable debug, or set num_processes to 1.' - )) + if mod_opts["num_processes"] > 1 and mod_opts.get("debug", False) is True: + raise Exception( + ( + "Tornado's debug implementation is not compatible with multiprocess. " + "Either disable debug, or set num_processes to 1." + ) + ) # the kwargs for the HTTPServer kwargs = {} - if not mod_opts.get('disable_ssl', False): - if 'ssl_crt' not in mod_opts: - log.error("Not starting '%s'. Options 'ssl_crt' and " - "'ssl_key' are required if SSL is not disabled.", - __name__) + if not mod_opts.get("disable_ssl", False): + if "ssl_crt" not in mod_opts: + log.error( + "Not starting '%s'. Options 'ssl_crt' and " + "'ssl_key' are required if SSL is not disabled.", + __name__, + ) return None # cert is required, key may be optional # https://docs.python.org/2/library/ssl.html#ssl.wrap_socket - ssl_opts = {'certfile': mod_opts['ssl_crt']} - if mod_opts.get('ssl_key', False): - ssl_opts.update({'keyfile': mod_opts['ssl_key']}) - kwargs['ssl_options'] = ssl_opts + ssl_opts = {"certfile": mod_opts["ssl_crt"]} + if mod_opts.get("ssl_key", False): + ssl_opts.update({"keyfile": mod_opts["ssl_key"]}) + kwargs["ssl_options"] = ssl_opts import salt.ext.tornado.httpserver - http_server = salt.ext.tornado.httpserver.HTTPServer(get_application(__opts__), **kwargs) + + http_server = salt.ext.tornado.httpserver.HTTPServer( + get_application(__opts__), **kwargs + ) try: - http_server.bind(mod_opts['port'], - address=mod_opts.get('address'), - backlog=mod_opts.get('backlog', 128), - ) - http_server.start(mod_opts['num_processes']) + http_server.bind( + mod_opts["port"], + address=mod_opts.get("address"), + backlog=mod_opts.get("backlog", 128), + ) + http_server.start(mod_opts["num_processes"]) except Exception: # pylint: disable=broad-except - log.error('Rest_tornado unable to bind to port %s', mod_opts['port'], exc_info=True) + log.error( + "Rest_tornado unable to bind to port %s", mod_opts["port"], exc_info=True + ) raise SystemExit(1) try: diff --git a/salt/netapi/rest_tornado/event_processor.py b/salt/netapi/rest_tornado/event_processor.py index 70a379e2c5c..1bffe65ed51 100644 --- a/salt/netapi/rest_tornado/event_processor.py +++ b/salt/netapi/rest_tornado/event_processor.py @@ -1,25 +1,26 @@ # encoding: utf-8 from __future__ import absolute_import, print_function, unicode_literals + import logging import threading -from salt.ext import six import salt.netapi import salt.utils.json +from salt.ext import six log = logging.getLogger(__name__) class SaltInfo(object): - ''' + """ Class to handle processing and publishing of "real time" Salt upates. - ''' + """ def __init__(self, handler): - ''' + """ handler is expected to be the server side end of a websocket connection. - ''' + """ self.handler = handler # These represent a "real time" view into Salt's jobs. @@ -30,77 +31,81 @@ class SaltInfo(object): self.minions = {} def publish_minions(self): - ''' + """ Publishes minions as a list of dicts. - ''' - log.debug('in publish minions') + """ + log.debug("in publish minions") minions = {} - log.debug('starting loop') + log.debug("starting loop") for minion, minion_info in six.iteritems(self.minions): log.debug(minion) # log.debug(minion_info) curr_minion = {} curr_minion.update(minion_info) - curr_minion.update({'id': minion}) + curr_minion.update({"id": minion}) minions[minion] = curr_minion - log.debug('ended loop') - ret = {'minions': minions} + log.debug("ended loop") + ret = {"minions": minions} self.handler.write_message( - salt.utils.json.dumps(ret) + str('\n\n')) # future lint: disable=blacklisted-function + salt.utils.json.dumps(ret) + str("\n\n") + ) # future lint: disable=blacklisted-function def publish(self, key, data): - ''' + """ Publishes the data to the event stream. - ''' + """ publish_data = {key: data} - pub = salt.utils.json.dumps(publish_data) + str('\n\n') # future lint: disable=blacklisted-function + pub = salt.utils.json.dumps(publish_data) + str( + "\n\n" + ) # future lint: disable=blacklisted-function self.handler.write_message(pub) def process_minion_update(self, event_data): - ''' + """ Associate grains data with a minion and publish minion update - ''' - tag = event_data['tag'] - event_info = event_data['data'] + """ + tag = event_data["tag"] + event_info = event_data["data"] - mid = tag.split('/')[-1] + mid = tag.split("/")[-1] if not self.minions.get(mid, None): self.minions[mid] = {} minion = self.minions[mid] - minion.update({'grains': event_info['return']}) + minion.update({"grains": event_info["return"]}) log.debug("In process minion grains update with minions=%s", self.minions) self.publish_minions() def process_ret_job_event(self, event_data): - ''' + """ Process a /ret event returned by Salt for a particular minion. These events contain the returned results from a particular execution. - ''' - tag = event_data['tag'] - event_info = event_data['data'] + """ + tag = event_data["tag"] + event_info = event_data["data"] - _, _, jid, _, mid = tag.split('/') + _, _, jid, _, mid = tag.split("/") job = self.jobs.setdefault(jid, {}) - minion = job.setdefault('minions', {}).setdefault(mid, {}) - minion.update({'return': event_info['return']}) - minion.update({'retcode': event_info['retcode']}) - minion.update({'success': event_info['success']}) + minion = job.setdefault("minions", {}).setdefault(mid, {}) + minion.update({"return": event_info["return"]}) + minion.update({"retcode": event_info["retcode"]}) + minion.update({"success": event_info["success"]}) - job_complete = all([minion['success'] for mid, minion - in six.iteritems(job['minions'])]) + job_complete = all( + [minion["success"] for mid, minion in six.iteritems(job["minions"])] + ) if job_complete: - job['state'] = 'complete' + job["state"] = "complete" - self.publish('jobs', self.jobs) + self.publish("jobs", self.jobs) def process_new_job_event(self, event_data): - ''' + """ Creates a new job with properties from the event data like jid, function, args, timestamp. @@ -108,80 +113,82 @@ class SaltInfo(object): Minions that are participating in this job are also noted. - ''' + """ job = None - tag = event_data['tag'] - event_info = event_data['data'] + tag = event_data["tag"] + event_info = event_data["data"] minions = {} - for mid in event_info['minions']: - minions[mid] = {'success': False} + for mid in event_info["minions"]: + minions[mid] = {"success": False} job = { - 'jid': event_info['jid'], - 'start_time': event_info['_stamp'], - 'minions': minions, # is a dictionary keyed by mids - 'fun': event_info['fun'], - 'tgt': event_info['tgt'], - 'tgt_type': event_info['tgt_type'], - 'state': 'running', + "jid": event_info["jid"], + "start_time": event_info["_stamp"], + "minions": minions, # is a dictionary keyed by mids + "fun": event_info["fun"], + "tgt": event_info["tgt"], + "tgt_type": event_info["tgt_type"], + "state": "running", } - self.jobs[event_info['jid']] = job - self.publish('jobs', self.jobs) + self.jobs[event_info["jid"]] = job + self.publish("jobs", self.jobs) def process_key_event(self, event_data): - ''' + """ Tag: salt/key Data: {'_stamp': '2014-05-20T22:45:04.345583', 'act': 'delete', 'id': 'compute.home', 'result': True} - ''' + """ - tag = event_data['tag'] - event_info = event_data['data'] + tag = event_data["tag"] + event_info = event_data["data"] - if event_info['act'] == 'delete': - self.minions.pop(event_info['id'], None) - elif event_info['act'] == 'accept': - self.minions.setdefault(event_info['id'], {}) + if event_info["act"] == "delete": + self.minions.pop(event_info["id"], None) + elif event_info["act"] == "accept": + self.minions.setdefault(event_info["id"], {}) self.publish_minions() def process_presence_events(self, salt_data, token, opts): - ''' + """ Check if any minions have connected or dropped. Send a message to the client if they have. - ''' - log.debug('In presence') + """ + log.debug("In presence") changed = False # check if any connections were dropped - if set(salt_data['data'].get('lost', [])): - dropped_minions = set(salt_data['data'].get('lost', [])) + if set(salt_data["data"].get("lost", [])): + dropped_minions = set(salt_data["data"].get("lost", [])) else: - dropped_minions = set(self.minions) - set(salt_data['data'].get('present', [])) + dropped_minions = set(self.minions) - set( + salt_data["data"].get("present", []) + ) for minion in dropped_minions: changed = True - log.debug('Popping %s', minion) + log.debug("Popping %s", minion) self.minions.pop(minion, None) # check if any new connections were made - if set(salt_data['data'].get('new', [])): - log.debug('got new minions') - new_minions = set(salt_data['data'].get('new', [])) + if set(salt_data["data"].get("new", [])): + log.debug("got new minions") + new_minions = set(salt_data["data"].get("new", [])) changed = True - elif set(salt_data['data'].get('present', [])) - set(self.minions): - log.debug('detected new minions') - new_minions = set(salt_data['data'].get('present', [])) - set(self.minions) + elif set(salt_data["data"].get("present", [])) - set(self.minions): + log.debug("detected new minions") + new_minions = set(salt_data["data"].get("present", [])) - set(self.minions) changed = True else: new_minions = [] - tgt = ','.join(new_minions) + tgt = ",".join(new_minions) for mid in new_minions: - log.debug('Adding minion') + log.debug("Adding minion") self.minions[mid] = {} if tgt: @@ -189,46 +196,47 @@ class SaltInfo(object): client = salt.netapi.NetapiClient(opts) client.run( { - 'fun': 'grains.items', - 'tgt': tgt, - 'expr_type': 'list', - 'mode': 'client', - 'client': 'local', - 'asynchronous': 'local_async', - 'token': token, - }) + "fun": "grains.items", + "tgt": tgt, + "expr_type": "list", + "mode": "client", + "client": "local", + "asynchronous": "local_async", + "token": token, + } + ) if changed: self.publish_minions() def process(self, salt_data, token, opts): - ''' + """ Process events and publish data - ''' - log.debug('In process %s', threading.current_thread()) - log.debug(salt_data['tag']) + """ + log.debug("In process %s", threading.current_thread()) + log.debug(salt_data["tag"]) log.debug(salt_data) - parts = salt_data['tag'].split('/') + parts = salt_data["tag"].split("/") if len(parts) < 2: return # TBD: Simplify these conditional expressions - if parts[1] == 'job': - log.debug('In job part 1') - if parts[3] == 'new': - log.debug('In new job') + if parts[1] == "job": + log.debug("In job part 1") + if parts[3] == "new": + log.debug("In new job") self.process_new_job_event(salt_data) # if salt_data['data']['fun'] == 'grains.items': # self.minions = {} - elif parts[3] == 'ret': - log.debug('In ret') + elif parts[3] == "ret": + log.debug("In ret") self.process_ret_job_event(salt_data) - if salt_data['data']['fun'] == 'grains.items': + if salt_data["data"]["fun"] == "grains.items": self.process_minion_update(salt_data) - elif parts[1] == 'key': - log.debug('In key') + elif parts[1] == "key": + log.debug("In key") self.process_key_event(salt_data) - elif parts[1] == 'presence': + elif parts[1] == "presence": self.process_presence_events(salt_data, token, opts) # log.debug('In presence') diff --git a/salt/netapi/rest_tornado/saltnado.py b/salt/netapi/rest_tornado/saltnado.py index 14e0b21d95d..ccd0e1ab517 100644 --- a/salt/netapi/rest_tornado/saltnado.py +++ b/salt/netapi/rest_tornado/saltnado.py @@ -1,5 +1,5 @@ # encoding: utf-8 -''' +""" A non-blocking REST API for Salt ================================ @@ -184,43 +184,46 @@ a return like:: .. |401| replace:: authentication required .. |406| replace:: requested Content-Type not available .. |500| replace:: internal server error -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import time -import fnmatch -import logging -from copy import copy -from collections import defaultdict # pylint: disable=import-error import cgi -import salt.ext.tornado.escape -import salt.ext.tornado.httpserver -import salt.ext.tornado.ioloop -import salt.ext.tornado.web -import salt.ext.tornado.gen -from salt.ext.tornado.concurrent import Future -# pylint: enable=import-error +import fnmatch +import logging +import time +from collections import defaultdict +from copy import copy + +import salt.auth +import salt.client # salt imports import salt.ext.six as six +import salt.ext.tornado.escape +import salt.ext.tornado.gen +import salt.ext.tornado.httpserver +import salt.ext.tornado.ioloop +import salt.ext.tornado.web import salt.netapi +import salt.runner import salt.utils.args import salt.utils.event import salt.utils.json import salt.utils.minions import salt.utils.yaml import salt.utils.zeromq -from salt.utils.event import tagify -import salt.client -import salt.runner -import salt.auth from salt.exceptions import ( AuthenticationError, AuthorizationError, - EauthAuthenticationError + EauthAuthenticationError, ) +from salt.ext.tornado.concurrent import Future +from salt.utils.event import tagify + +# pylint: enable=import-error + salt.utils.zeromq.install_zmq() _json = salt.utils.json.import_json() @@ -228,13 +231,14 @@ log = logging.getLogger(__name__) def _json_dumps(obj, **kwargs): - ''' + """ Invoke salt.utils.json.dumps using the alternate json module loaded using salt.utils.json.import_json(). This ensures that we properly encode any strings in the object before we perform the serialization. - ''' + """ return salt.utils.json.dumps(obj, _json_module=_json, **kwargs) + # The clients rest_cherrypi supports. We want to mimic the interface, but not # necessarily use the same API under the hood # # all of these require coordinating minion stuff @@ -246,8 +250,8 @@ def _json_dumps(obj, **kwargs): # - "wheel" (need asynchronous api...) -AUTH_TOKEN_HEADER = 'X-Auth-Token' -AUTH_COOKIE_NAME = 'session_id' +AUTH_TOKEN_HEADER = "X-Auth-Token" +AUTH_COOKIE_NAME = "session_id" class TimeoutException(Exception): @@ -255,9 +259,10 @@ class TimeoutException(Exception): class Any(Future): - ''' + """ Future that wraps other futures to "block" until one is done - ''' + """ + def __init__(self, futures): super(Any, self).__init__() for future in futures: @@ -270,22 +275,22 @@ class Any(Future): class EventListener(object): - ''' + """ Class responsible for listening to the salt master event bus and updating futures. This is the core of what makes this asynchronous, this allows us to do non-blocking work in the main processes and "wait" for an event to happen - ''' + """ def __init__(self, mod_opts, opts): self.mod_opts = mod_opts self.opts = opts self.event = salt.utils.event.get_event( - 'master', - opts['sock_dir'], - opts['transport'], + "master", + opts["sock_dir"], + opts["transport"], opts=opts, listen=True, - io_loop=salt.ext.tornado.ioloop.IOLoop.current() + io_loop=salt.ext.tornado.ioloop.IOLoop.current(), ) # tag -> list of futures @@ -300,9 +305,9 @@ class EventListener(object): self.event.set_event_handler(self._handle_event_socket_recv) def clean_by_request(self, request): - ''' + """ Remove all futures that were waiting for request `request` since it is done waiting - ''' + """ if request not in self.request_map: return for tag, matcher, future in self.request_map[request]: @@ -310,7 +315,9 @@ class EventListener(object): self._timeout_future(tag, matcher, future) # remove the timeout if future in self.timeout_map: - salt.ext.tornado.ioloop.IOLoop.current().remove_timeout(self.timeout_map[future]) + salt.ext.tornado.ioloop.IOLoop.current().remove_timeout( + self.timeout_map[future] + ) del self.timeout_map[future] del self.request_map[request] @@ -318,25 +325,26 @@ class EventListener(object): @staticmethod def prefix_matcher(mtag, tag): if mtag is None or tag is None: - raise TypeError('mtag or tag can not be None') + raise TypeError("mtag or tag can not be None") return mtag.startswith(tag) @staticmethod def exact_matcher(mtag, tag): if mtag is None or tag is None: - raise TypeError('mtag or tag can not be None') + raise TypeError("mtag or tag can not be None") return mtag == tag - def get_event(self, - request, - tag='', - matcher=prefix_matcher.__func__, - callback=None, - timeout=None - ): - ''' + def get_event( + self, + request, + tag="", + matcher=prefix_matcher.__func__, + callback=None, + timeout=None, + ): + """ Get an event (asynchronous of course) return a future that will get it later - ''' + """ # if the request finished, no reason to allow event fetching, since we # can't send back to the client if request._finished: @@ -346,23 +354,29 @@ class EventListener(object): future = Future() if callback is not None: + def handle_future(future): - salt.ext.tornado.ioloop.IOLoop.current().add_callback(callback, future) # pylint: disable=E1102 + salt.ext.tornado.ioloop.IOLoop.current().add_callback( + callback, future + ) # pylint: disable=E1102 + future.add_done_callback(handle_future) # add this tag and future to the callbacks self.tag_map[(tag, matcher)].append(future) self.request_map[request].append((tag, matcher, future)) if timeout: - timeout_future = salt.ext.tornado.ioloop.IOLoop.current().call_later(timeout, self._timeout_future, tag, matcher, future) + timeout_future = salt.ext.tornado.ioloop.IOLoop.current().call_later( + timeout, self._timeout_future, tag, matcher, future + ) self.timeout_map[future] = timeout_future return future def _timeout_future(self, tag, matcher, future): - ''' + """ Timeout a specific future - ''' + """ if (tag, matcher) not in self.tag_map: return if not future.done(): @@ -372,9 +386,9 @@ class EventListener(object): del self.tag_map[(tag, matcher)] def _handle_event_socket_recv(self, raw): - ''' + """ Callback for events on the event sub socket - ''' + """ mtag, data = self.event.unpack(raw, self.event.serial) # see if we have any futures that need this info: @@ -382,7 +396,7 @@ class EventListener(object): try: is_matched = matcher(mtag, tag) except Exception: # pylint: disable=broad-except - log.error('Failed to run a matcher.', exc_info=True) + log.error("Failed to run a matcher.", exc_info=True) is_matched = False if not is_matched: @@ -391,24 +405,26 @@ class EventListener(object): for future in futures: if future.done(): continue - future.set_result({'data': data, 'tag': mtag}) + future.set_result({"data": data, "tag": mtag}) self.tag_map[(tag, matcher)].remove(future) if future in self.timeout_map: - salt.ext.tornado.ioloop.IOLoop.current().remove_timeout(self.timeout_map[future]) + salt.ext.tornado.ioloop.IOLoop.current().remove_timeout( + self.timeout_map[future] + ) del self.timeout_map[future] class BaseSaltAPIHandler(salt.ext.tornado.web.RequestHandler): # pylint: disable=W0223 ct_out_map = ( - ('application/json', _json_dumps), - ('application/x-yaml', salt.utils.yaml.safe_dump), + ("application/json", _json_dumps), + ("application/x-yaml", salt.utils.yaml.safe_dump), ) def _verify_client(self, low): - ''' + """ Verify that the client is in fact one we have - ''' - if 'client' not in low or low.get('client') not in self.saltclients: + """ + if "client" not in low or low.get("client") not in self.saltclients: self.set_status(400) self.write("400 Invalid Client: Client not found in salt clients") self.finish() @@ -416,34 +432,35 @@ class BaseSaltAPIHandler(salt.ext.tornado.web.RequestHandler): # pylint: disabl return True def initialize(self): - ''' + """ Initialize the handler before requests are called - ''' - if not hasattr(self.application, 'event_listener'): - log.debug('init a listener') + """ + if not hasattr(self.application, "event_listener"): + log.debug("init a listener") self.application.event_listener = EventListener( - self.application.mod_opts, - self.application.opts, + self.application.mod_opts, self.application.opts, ) - if not hasattr(self, 'saltclients'): + if not hasattr(self, "saltclients"): local_client = salt.client.get_local_client(mopts=self.application.opts) self.saltclients = { - 'local': local_client.run_job_async, + "local": local_client.run_job_async, # not the actual client we'll use.. but its what we'll use to get args - 'local_async': local_client.run_job_async, - 'runner': salt.runner.RunnerClient(opts=self.application.opts).cmd_async, - 'runner_async': None, # empty, since we use the same client as `runner` - } + "local_async": local_client.run_job_async, + "runner": salt.runner.RunnerClient( + opts=self.application.opts + ).cmd_async, + "runner_async": None, # empty, since we use the same client as `runner` + } - if not hasattr(self, 'ckminions'): + if not hasattr(self, "ckminions"): self.ckminions = salt.utils.minions.CkMinions(self.application.opts) @property def token(self): - ''' + """ The token used for the request - ''' + """ # find the token (cookie or headers) if AUTH_TOKEN_HEADER in self.request.headers: return self.request.headers[AUTH_TOKEN_HEADER] @@ -451,21 +468,23 @@ class BaseSaltAPIHandler(salt.ext.tornado.web.RequestHandler): # pylint: disabl return self.get_cookie(AUTH_COOKIE_NAME) def _verify_auth(self): - ''' + """ Boolean whether the request is auth'd - ''' + """ return self.token and bool(self.application.auth.get_tok(self.token)) def prepare(self): - ''' + """ Run before get/posts etc. Pre-flight checks: - verify that we can speak back to them (compatible accept header) - ''' + """ # Find an acceptable content-type - accept_header = self.request.headers.get('Accept', '*/*') + accept_header = self.request.headers.get("Accept", "*/*") # Ignore any parameter, including q (quality) one - parsed_accept_header = [cgi.parse_header(h)[0] for h in accept_header.split(',')] + parsed_accept_header = [ + cgi.parse_header(h)[0] for h in accept_header.split(",") + ] def find_acceptable_content_type(parsed_accept_header): for media_range in parsed_accept_header: @@ -490,40 +509,40 @@ class BaseSaltAPIHandler(salt.ext.tornado.web.RequestHandler): # pylint: disabl self.lowstate = self._get_lowstate() def timeout_futures(self): - ''' + """ timeout a session - ''' + """ # TODO: set a header or something??? so we know it was a timeout self.application.event_listener.clean_by_request(self) def on_finish(self): - ''' + """ When the job has been done, lets cleanup - ''' + """ # timeout all the futures self.timeout_futures() # clear local_client objects to disconnect event publisher's IOStream connections del self.saltclients def on_connection_close(self): - ''' + """ If the client disconnects, lets close out - ''' + """ self.finish() def serialize(self, data): - ''' + """ Serlialize the output based on the Accept header - ''' - self.set_header('Content-Type', self.content_type) + """ + self.set_header("Content-Type", self.content_type) return self.dumper(data) def _form_loader(self, _): - ''' + """ function to get the data from the urlencoded forms ignore the data passed in and just get the args from wherever they are - ''' + """ data = {} for key in self.request.arguments: val = self.get_arguments(key) @@ -534,21 +553,21 @@ class BaseSaltAPIHandler(salt.ext.tornado.web.RequestHandler): # pylint: disabl return data def deserialize(self, data): - ''' + """ Deserialize the data based on request content type headers - ''' + """ ct_in_map = { - 'application/x-www-form-urlencoded': self._form_loader, - 'application/json': salt.utils.json.loads, - 'application/x-yaml': salt.utils.yaml.safe_load, - 'text/yaml': salt.utils.yaml.safe_load, + "application/x-www-form-urlencoded": self._form_loader, + "application/json": salt.utils.json.loads, + "application/x-yaml": salt.utils.yaml.safe_load, + "text/yaml": salt.utils.yaml.safe_load, # because people are terrible and don't mean what they say - 'text/plain': salt.utils.json.loads + "text/plain": salt.utils.json.loads, } try: # Use cgi.parse_header to correctly separate parameters from value - value, parameters = cgi.parse_header(self.request.headers['Content-Type']) + value, parameters = cgi.parse_header(self.request.headers["Content-Type"]) return ct_in_map[value](salt.ext.tornado.escape.native_str(data)) except KeyError: self.send_error(406) @@ -556,16 +575,16 @@ class BaseSaltAPIHandler(salt.ext.tornado.web.RequestHandler): # pylint: disabl self.send_error(400) def _get_lowstate(self): - ''' + """ Format the incoming data into a lowstate object - ''' + """ if not self.request.body: return data = self.deserialize(self.request.body) self.request_payload = copy(data) - if data and 'arg' in data and not isinstance(data['arg'], list): - data['arg'] = [data['arg']] + if data and "arg" in data and not isinstance(data["arg"], list): + data["arg"] = [data["arg"]] if not isinstance(data, list): lowstate = [data] @@ -575,48 +594,49 @@ class BaseSaltAPIHandler(salt.ext.tornado.web.RequestHandler): # pylint: disabl return lowstate def set_default_headers(self): - ''' + """ Set default CORS headers - ''' + """ mod_opts = self.application.mod_opts - if mod_opts.get('cors_origin'): - origin = self.request.headers.get('Origin') + if mod_opts.get("cors_origin"): + origin = self.request.headers.get("Origin") - allowed_origin = _check_cors_origin(origin, mod_opts['cors_origin']) + allowed_origin = _check_cors_origin(origin, mod_opts["cors_origin"]) if allowed_origin: self.set_header("Access-Control-Allow-Origin", allowed_origin) def options(self, *args, **kwargs): - ''' + """ Return CORS headers for preflight requests - ''' + """ # Allow X-Auth-Token in requests - request_headers = self.request.headers.get('Access-Control-Request-Headers') - allowed_headers = request_headers.split(',') + request_headers = self.request.headers.get("Access-Control-Request-Headers") + allowed_headers = request_headers.split(",") # Filter allowed header here if needed. # Allow request headers - self.set_header('Access-Control-Allow-Headers', ','.join(allowed_headers)) + self.set_header("Access-Control-Allow-Headers", ",".join(allowed_headers)) # Allow X-Auth-Token in responses - self.set_header('Access-Control-Expose-Headers', 'X-Auth-Token') + self.set_header("Access-Control-Expose-Headers", "X-Auth-Token") # Allow all methods - self.set_header('Access-Control-Allow-Methods', 'OPTIONS, GET, POST') + self.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST") self.set_status(204) self.finish() class SaltAuthHandler(BaseSaltAPIHandler): # pylint: disable=W0223 - ''' + """ Handler for login requests - ''' + """ + def get(self): # pylint: disable=arguments-differ - ''' + """ All logins are done over post, this is a parked endpoint .. http:get:: /login @@ -645,18 +665,17 @@ class SaltAuthHandler(BaseSaltAPIHandler): # pylint: disable=W0223 Content-Length: 58 {"status": "401 Unauthorized", "return": "Please log in"} - ''' + """ self.set_status(401) - self.set_header('WWW-Authenticate', 'Session') + self.set_header("WWW-Authenticate", "Session") - ret = {'status': '401 Unauthorized', - 'return': 'Please log in'} + ret = {"status": "401 Unauthorized", "return": "Please log in"} self.write(self.serialize(ret)) # TODO: make asynchronous? Underlying library isn't... and we ARE making disk calls :( def post(self): # pylint: disable=arguments-differ - ''' + """ :ref:`Authenticate <rest_tornado-auth>` against Salt's eauth system .. http:post:: /login @@ -718,43 +737,46 @@ class SaltAuthHandler(BaseSaltAPIHandler): # pylint: disable=W0223 "test.*" ] }} - ''' + """ try: if not isinstance(self.request_payload, dict): self.send_error(400) return - creds = {'username': self.request_payload['username'], - 'password': self.request_payload['password'], - 'eauth': self.request_payload['eauth'], - } + creds = { + "username": self.request_payload["username"], + "password": self.request_payload["password"], + "eauth": self.request_payload["eauth"], + } # if any of the args are missing, its a bad request except KeyError: self.send_error(400) return token = self.application.auth.mk_token(creds) - if 'token' not in token: + if "token" not in token: # TODO: nicer error message # 'Could not authenticate using provided credentials') self.send_error(401) # return since we don't want to execute any more return - self.set_cookie(AUTH_COOKIE_NAME, token['token']) + self.set_cookie(AUTH_COOKIE_NAME, token["token"]) # Grab eauth config for the current backend for the current user try: - eauth = self.application.opts['external_auth'][token['eauth']] + eauth = self.application.opts["external_auth"][token["eauth"]] # Get sum of '*' perms, user-specific perms, and group-specific perms - perms = eauth.get(token['name'], []) - perms.extend(eauth.get('*', [])) + perms = eauth.get(token["name"], []) + perms.extend(eauth.get("*", [])) - if 'groups' in token and token['groups']: - user_groups = set(token['groups']) - eauth_groups = set([i.rstrip('%') for i in eauth.keys() if i.endswith('%')]) + if "groups" in token and token["groups"]: + user_groups = set(token["groups"]) + eauth_groups = set( + [i.rstrip("%") for i in eauth.keys() if i.endswith("%")] + ) for group in user_groups & eauth_groups: - perms.extend(eauth['{0}%'.format(group)]) + perms.extend(eauth["{0}%".format(group)]) perms = sorted(list(set(perms))) # If we can't find the creds, then they aren't authorized @@ -765,31 +787,38 @@ class SaltAuthHandler(BaseSaltAPIHandler): # pylint: disable=W0223 except (AttributeError, IndexError): log.debug( "Configuration for external_auth malformed for eauth '%s', " - "and user '%s'.", token.get('eauth'), token.get('name'), - exc_info=True + "and user '%s'.", + token.get("eauth"), + token.get("name"), + exc_info=True, ) # TODO better error -- 'Configuration for external_auth could not be read.' self.send_error(500) return - ret = {'return': [{ - 'token': token['token'], - 'expire': token['expire'], - 'start': token['start'], - 'user': token['name'], - 'eauth': token['eauth'], - 'perms': perms, - }]} + ret = { + "return": [ + { + "token": token["token"], + "expire": token["expire"], + "start": token["start"], + "user": token["name"], + "eauth": token["eauth"], + "perms": perms, + } + ] + } self.write(self.serialize(ret)) class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 - ''' + """ Main API handler for base "/" - ''' + """ + def get(self): # pylint: disable=arguments-differ - ''' + """ An endpoint to determine salt-api capabilities .. http:get:: / @@ -821,14 +850,13 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 Content-Legnth: 83 {"clients": ["local", "local_async", "runner", "runner_async"], "return": "Welcome"} - ''' - ret = {"clients": list(self.saltclients.keys()), - "return": "Welcome"} + """ + ret = {"clients": list(self.saltclients.keys()), "return": "Welcome"} self.write(self.serialize(ret)) @salt.ext.tornado.web.asynchronous def post(self): # pylint: disable=arguments-differ - ''' + """ Send one or more Salt commands (lowstates) in the request body .. http:post:: / @@ -896,19 +924,19 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 of a previous job. If you need to have commands executed in order and stop on failure please use compound-command-execution. - ''' + """ # if you aren't authenticated, redirect to login if not self._verify_auth(): - self.redirect('/login') + self.redirect("/login") return self.disbatch() @salt.ext.tornado.gen.coroutine def disbatch(self): - ''' + """ Disbatch all lowstates to the appropriate clients - ''' + """ ret = [] # check clients before going, we want to throw 400 if one is bad @@ -918,46 +946,60 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 # Make sure we have 'token' or 'username'/'password' in each low chunk. # Salt will verify the credentials are correct. - if self.token is not None and 'token' not in low: - low['token'] = self.token + if self.token is not None and "token" not in low: + low["token"] = self.token - if not (('token' in low) - or ('username' in low and 'password' in low and 'eauth' in low)): - ret.append('Failed to authenticate') + if not ( + ("token" in low) + or ("username" in low and "password" in low and "eauth" in low) + ): + ret.append("Failed to authenticate") break # disbatch to the correct handler try: - chunk_ret = yield getattr(self, '_disbatch_{0}'.format(low['client']))(low) + chunk_ret = yield getattr(self, "_disbatch_{0}".format(low["client"]))( + low + ) ret.append(chunk_ret) except (AuthenticationError, AuthorizationError, EauthAuthenticationError): - ret.append('Failed to authenticate') + ret.append("Failed to authenticate") break except Exception as ex: # pylint: disable=broad-except - ret.append('Unexpected exception while handling request: {0}'.format(ex)) - log.error('Unexpected exception while handling request:', exc_info=True) + ret.append( + "Unexpected exception while handling request: {0}".format(ex) + ) + log.error("Unexpected exception while handling request:", exc_info=True) - self.write(self.serialize({'return': ret})) + self.write(self.serialize({"return": ret})) self.finish() @salt.ext.tornado.gen.coroutine def _disbatch_local(self, chunk): - ''' + """ Dispatch local client commands - ''' + """ # Generate jid and find all minions before triggering a job to subscribe all returns from minions - chunk['jid'] = salt.utils.jid.gen_jid(self.application.opts) if not chunk.get('jid', None) else chunk['jid'] - minions = set(self.ckminions.check_minions(chunk['tgt'], chunk.get('tgt_type', 'glob'))) + chunk["jid"] = ( + salt.utils.jid.gen_jid(self.application.opts) + if not chunk.get("jid", None) + else chunk["jid"] + ) + minions = set( + self.ckminions.check_minions(chunk["tgt"], chunk.get("tgt_type", "glob")) + ) def subscribe_minion(minion): salt_evt = self.application.event_listener.get_event( - self, - tag='salt/job/{}/ret/{}'.format(chunk['jid'], minion), - matcher=EventListener.exact_matcher) + self, + tag="salt/job/{}/ret/{}".format(chunk["jid"], minion), + matcher=EventListener.exact_matcher, + ) syndic_evt = self.application.event_listener.get_event( - self, - tag='syndic/job/{}/ret/{}'.format(chunk['jid'], minion), - matcher=EventListener.exact_matcher) + self, + tag="syndic/job/{}/ret/{}".format(chunk["jid"], minion), + matcher=EventListener.exact_matcher, + ) return salt_evt, syndic_evt # start listening for the event before we fire the job to avoid races @@ -969,26 +1011,30 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 f_call = self._format_call_run_job_async(chunk) # fire a job off - pub_data = yield self.saltclients['local'](*f_call.get('args', ()), **f_call.get('kwargs', {})) + pub_data = yield self.saltclients["local"]( + *f_call.get("args", ()), **f_call.get("kwargs", {}) + ) # if the job didn't publish, lets not wait around for nothing # TODO: set header?? - if 'jid' not in pub_data: + if "jid" not in pub_data: for future in events: try: future.set_result(None) except Exception: # pylint: disable=broad-except pass - raise salt.ext.tornado.gen.Return('No minions matched the target. No command was sent, no jid was assigned.') + raise salt.ext.tornado.gen.Return( + "No minions matched the target. No command was sent, no jid was assigned." + ) # get_event for missing minion - for minion in list(set(pub_data['minions']) - set(minions)): + for minion in list(set(pub_data["minions"]) - set(minions)): salt_evt, syndic_evt = subscribe_minion(minion) events.append(salt_evt) events.append(syndic_evt) # Map of minion_id -> returned for all minions we think we need to wait on - minions = {m: False for m in pub_data['minions']} + minions = {m: False for m in pub_data["minions"]} # minimum time required for return to complete. By default no waiting, if # we are a syndic then we must wait syndic_wait at a minimum @@ -996,24 +1042,31 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 min_wait_time.set_result(True) # wait syndic a while to avoid missing published events - if self.application.opts['order_masters']: - min_wait_time = salt.ext.tornado.gen.sleep(self.application.opts['syndic_wait']) + if self.application.opts["order_masters"]: + min_wait_time = salt.ext.tornado.gen.sleep( + self.application.opts["syndic_wait"] + ) # To ensure job_not_running and all_return are terminated by each other, communicate using a future - is_finished = salt.ext.tornado.gen.sleep(self.application.opts['gather_job_timeout']) + is_finished = salt.ext.tornado.gen.sleep( + self.application.opts["gather_job_timeout"] + ) # ping until the job is not running, while doing so, if we see new minions returning # that they are running the job, add them to the list - salt.ext.tornado.ioloop.IOLoop.current().spawn_callback(self.job_not_running, pub_data['jid'], - chunk['tgt'], - f_call['kwargs']['tgt_type'], - minions, - is_finished) + salt.ext.tornado.ioloop.IOLoop.current().spawn_callback( + self.job_not_running, + pub_data["jid"], + chunk["tgt"], + f_call["kwargs"]["tgt_type"], + minions, + is_finished, + ) def more_todo(): - ''' + """ Check if there are any more minions we are waiting on returns from - ''' + """ return any(x is False for x in six.itervalues(minions)) # here we want to follow the behavior of LocalClient.get_iter_returns @@ -1022,7 +1075,7 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 # early if gather_job_timeout has been exceeded chunk_ret = {} while True: - to_wait = events+[is_finished] + to_wait = events + [is_finished] if not min_wait_time.done(): to_wait += [min_wait_time] @@ -1030,6 +1083,7 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 for event in to_wait: if not event.done(): event.set_result(None) + f = yield Any(to_wait) try: # When finished entire routine, cleanup other futures and return result @@ -1046,14 +1100,14 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 if f in events: events.remove(f) # if this is a start, then we need to add it to the pile - if f_result['tag'].endswith('/new'): - for minion_id in f_result['data']['minions']: + if f_result["tag"].endswith("/new"): + for minion_id in f_result["data"]["minions"]: if minion_id not in minions: minions[minion_id] = False else: - chunk_ret[f_result['data']['id']] = f_result['data']['return'] + chunk_ret[f_result["data"]["id"]] = f_result["data"]["return"] # clear finished event future - minions[f_result['data']['id']] = True + minions[f_result["data"]["id"]] = True # if there are no more minions to wait for, then we are done if not more_todo() and min_wait_time.done(): cancel_inflight_futures() @@ -1064,22 +1118,23 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 @salt.ext.tornado.gen.coroutine def job_not_running(self, jid, tgt, tgt_type, minions, is_finished): - ''' + """ Return a future which will complete once jid (passed in) is no longer running on tgt - ''' - ping_pub_data = yield self.saltclients['local'](tgt, - 'saltutil.find_job', - [jid], - tgt_type=tgt_type) - ping_tag = tagify([ping_pub_data['jid'], 'ret'], 'job') + """ + ping_pub_data = yield self.saltclients["local"]( + tgt, "saltutil.find_job", [jid], tgt_type=tgt_type + ) + ping_tag = tagify([ping_pub_data["jid"], "ret"], "job") minion_running = False while True: try: - event = self.application.event_listener.get_event(self, - tag=ping_tag, - timeout=self.application.opts['gather_job_timeout']) + event = self.application.event_listener.get_event( + self, + tag=ping_tag, + timeout=self.application.opts["gather_job_timeout"], + ) f = yield Any([event, is_finished]) # When finished entire routine, cleanup other futures and return result if f is is_finished: @@ -1091,75 +1146,76 @@ class SaltAPIHandler(BaseSaltAPIHandler): # pylint: disable=W0223 if not minion_running: raise salt.ext.tornado.gen.Return(True) else: - ping_pub_data = yield self.saltclients['local'](tgt, - 'saltutil.find_job', - [jid], - tgt_type=tgt_type) - ping_tag = tagify([ping_pub_data['jid'], 'ret'], 'job') + ping_pub_data = yield self.saltclients["local"]( + tgt, "saltutil.find_job", [jid], tgt_type=tgt_type + ) + ping_tag = tagify([ping_pub_data["jid"], "ret"], "job") minion_running = False continue # Minions can return, we want to see if the job is running... - if event['data'].get('return', {}) == {}: + if event["data"].get("return", {}) == {}: continue - if event['data']['id'] not in minions: - minions[event['data']['id']] = False + if event["data"]["id"] not in minions: + minions[event["data"]["id"]] = False minion_running = True @salt.ext.tornado.gen.coroutine def _disbatch_local_async(self, chunk): - ''' + """ Disbatch local client_async commands - ''' + """ f_call = self._format_call_run_job_async(chunk) # fire a job off - pub_data = yield self.saltclients['local_async'](*f_call.get('args', ()), **f_call.get('kwargs', {})) + pub_data = yield self.saltclients["local_async"]( + *f_call.get("args", ()), **f_call.get("kwargs", {}) + ) raise salt.ext.tornado.gen.Return(pub_data) @salt.ext.tornado.gen.coroutine def _disbatch_runner(self, chunk): - ''' + """ Disbatch runner client commands - ''' - full_return = chunk.pop('full_return', False) - pub_data = self.saltclients['runner'](chunk) - tag = pub_data['tag'] + '/ret' + """ + full_return = chunk.pop("full_return", False) + pub_data = self.saltclients["runner"](chunk) + tag = pub_data["tag"] + "/ret" try: event = yield self.application.event_listener.get_event(self, tag=tag) # only return the return data - ret = event if full_return else event['data']['return'] + ret = event if full_return else event["data"]["return"] raise salt.ext.tornado.gen.Return(ret) except TimeoutException: - raise salt.ext.tornado.gen.Return('Timeout waiting for runner to execute') + raise salt.ext.tornado.gen.Return("Timeout waiting for runner to execute") @salt.ext.tornado.gen.coroutine def _disbatch_runner_async(self, chunk): - ''' + """ Disbatch runner client_async commands - ''' - pub_data = self.saltclients['runner'](chunk) + """ + pub_data = self.saltclients["runner"](chunk) raise salt.ext.tornado.gen.Return(pub_data) # salt.utils.args.format_call doesn't work for functions having the # annotation salt.ext.tornado.gen.coroutine def _format_call_run_job_async(self, chunk): f_call = salt.utils.args.format_call( - salt.client.LocalClient.run_job, - chunk, - is_class_method=True) - f_call.get('kwargs', {})['io_loop'] = salt.ext.tornado.ioloop.IOLoop.current() + salt.client.LocalClient.run_job, chunk, is_class_method=True + ) + f_call.get("kwargs", {})["io_loop"] = salt.ext.tornado.ioloop.IOLoop.current() return f_call class MinionSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 - ''' + """ A convenience endpoint for minion related functions - ''' + """ + @salt.ext.tornado.web.asynchronous def get(self, mid=None): # pylint: disable=W0221 - ''' + """ A convenience URL for getting lists of minions or getting minion details @@ -1196,22 +1252,18 @@ class MinionSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 - ms-3: grains.items: ... - ''' + """ # if you aren't authenticated, redirect to login if not self._verify_auth(): - self.redirect('/login') + self.redirect("/login") return - self.lowstate = [{ - 'client': 'local', - 'tgt': mid or '*', - 'fun': 'grains.items', - }] + self.lowstate = [{"client": "local", "tgt": mid or "*", "fun": "grains.items"}] self.disbatch() @salt.ext.tornado.web.asynchronous def post(self): - ''' + """ Start an execution command and immediately return the job id .. http:post:: /minions @@ -1260,22 +1312,22 @@ class MinionSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 return: - jid: '20130603122505459265' minions: [ms-4, ms-3, ms-2, ms-1, ms-0] - ''' + """ # if you aren't authenticated, redirect to login if not self._verify_auth(): - self.redirect('/login') + self.redirect("/login") return # verify that all lowstates are the correct client type for low in self.lowstate: # if you didn't specify, its fine - if 'client' not in low: - low['client'] = 'local_async' + if "client" not in low: + low["client"] = "local_async" continue # if you specified something else, we don't do that - if low.get('client') != 'local_async': + if low.get("client") != "local_async": self.set_status(400) - self.write('We don\'t serve your kind here') + self.write("We don't serve your kind here") self.finish() return @@ -1283,12 +1335,13 @@ class MinionSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 class JobsSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 - ''' + """ A convenience endpoint for job cache data - ''' + """ + @salt.ext.tornado.web.asynchronous def get(self, jid=None): # pylint: disable=W0221 - ''' + """ A convenience URL for getting lists of previously run jobs or getting the return from a single job @@ -1367,34 +1420,28 @@ class JobsSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 - 1 - 2 - 6.9141387939453125e-06 - ''' + """ # if you aren't authenticated, redirect to login if not self._verify_auth(): - self.redirect('/login') + self.redirect("/login") return if jid: - self.lowstate = [{ - 'fun': 'jobs.list_job', - 'jid': jid, - 'client': 'runner', - }] + self.lowstate = [{"fun": "jobs.list_job", "jid": jid, "client": "runner"}] else: - self.lowstate = [{ - 'fun': 'jobs.list_jobs', - 'client': 'runner', - }] + self.lowstate = [{"fun": "jobs.list_jobs", "client": "runner"}] self.disbatch() class RunSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 - ''' + """ Endpoint to run commands without normal session handling - ''' + """ + @salt.ext.tornado.web.asynchronous def post(self): - ''' + """ Run commands bypassing the :ref:`normal session handling <rest_cherrypy-auth>` @@ -1448,12 +1495,12 @@ class RunSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 ms-2: true ms-3: true ms-4: true - ''' + """ self.disbatch() class EventsSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 - ''' + """ Expose the Salt event bus The event bus on the Salt master exposes a large variety of things, notably @@ -1462,10 +1509,11 @@ class EventsSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 Salt infrastructure. .. seealso:: :ref:`events` - ''' + """ + @salt.ext.tornado.gen.coroutine def get(self): - r''' + r""" An HTTP stream of the Salt master event bus This stream is formatted per the Server Sent Events (SSE) spec. Each @@ -1557,31 +1605,33 @@ class EventsSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 data: {"tag": "salt/job/20140112010149808995/new", "data": {"tgt_type": "glob", "jid": "20140112010149808995", "tgt": "jerry", "_stamp": "2014-01-12_01:01:49.809617", "user": "shouse", "arg": [], "fun": "test.ping", "minions": ["jerry"]}} tag: 20140112010149808995 data: {"tag": "20140112010149808995", "data": {"fun_args": [], "jid": "20140112010149808995", "return": true, "retcode": 0, "success": true, "cmd": "_return", "_stamp": "2014-01-12_01:01:49.819316", "fun": "test.ping", "id": "jerry"}} - ''' + """ # if you aren't authenticated, redirect to login if not self._verify_auth(): - self.redirect('/login') + self.redirect("/login") return # set the streaming headers - self.set_header('Content-Type', 'text/event-stream') - self.set_header('Cache-Control', 'no-cache') - self.set_header('Connection', 'keep-alive') + self.set_header("Content-Type", "text/event-stream") + self.set_header("Cache-Control", "no-cache") + self.set_header("Connection", "keep-alive") - self.write('retry: {0}\n'.format(400)) + self.write("retry: {0}\n".format(400)) self.flush() while True: try: event = yield self.application.event_listener.get_event(self) - self.write('tag: {0}\n'.format(event.get('tag', ''))) - self.write(str('data: {0}\n\n').format(_json_dumps(event))) # future lint: disable=blacklisted-function + self.write("tag: {0}\n".format(event.get("tag", ""))) + self.write( + str("data: {0}\n\n").format(_json_dumps(event)) + ) # future lint: disable=blacklisted-function self.flush() except TimeoutException: break class WebhookSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 - ''' + """ A generic web hook entry point that fires an event on Salt's event bus External services can POST data to this URL to trigger an event in Salt. @@ -1620,9 +1670,10 @@ class WebhookSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 - 'curl -sS http://saltapi-url.example.com:8000/hook/travis/build/success -d branch="${TRAVIS_BRANCH}" -d commit="${TRAVIS_COMMIT}"' .. seealso:: :ref:`Events <events>`, :ref:`Reactor <reactor>` - ''' + """ + def post(self, tag_suffix=None): # pylint: disable=W0221 - ''' + """ Fire an event in Salt with a custom event tag and data .. http:post:: /hook @@ -1704,24 +1755,25 @@ class WebhookSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 pillar: revision: {{ revision }} {% endif %} - ''' - disable_auth = self.application.mod_opts.get('webhook_disable_auth') + """ + disable_auth = self.application.mod_opts.get("webhook_disable_auth") if not disable_auth and not self._verify_auth(): - self.redirect('/login') + self.redirect("/login") return # if you have the tag, prefix - tag = 'salt/netapi/hook' + tag = "salt/netapi/hook" if tag_suffix: tag += tag_suffix # TODO: consolidate?? self.event = salt.utils.event.get_event( - 'master', - self.application.opts['sock_dir'], - self.application.opts['transport'], + "master", + self.application.opts["sock_dir"], + self.application.opts["transport"], opts=self.application.opts, - listen=False) + listen=False, + ) arguments = {} for argname in self.request.query_arguments: @@ -1729,28 +1781,31 @@ class WebhookSaltAPIHandler(SaltAPIHandler): # pylint: disable=W0223 if len(value) == 1: value = value[0] arguments[argname] = value - ret = self.event.fire_event({ - 'post': self.request_payload, - 'get': arguments, - # In Tornado >= v4.0.3, the headers come - # back as an HTTPHeaders instance, which - # is a dictionary. We must cast this as - # a dictionary in order for msgpack to - # serialize it. - 'headers': dict(self.request.headers), - }, tag) + ret = self.event.fire_event( + { + "post": self.request_payload, + "get": arguments, + # In Tornado >= v4.0.3, the headers come + # back as an HTTPHeaders instance, which + # is a dictionary. We must cast this as + # a dictionary in order for msgpack to + # serialize it. + "headers": dict(self.request.headers), + }, + tag, + ) - self.write(self.serialize({'success': ret})) + self.write(self.serialize({"success": ret})) def _check_cors_origin(origin, allowed_origins): - ''' + """ Check if an origin match cors allowed origins - ''' + """ if isinstance(allowed_origins, list): if origin in allowed_origins: return origin - elif allowed_origins == '*': + elif allowed_origins == "*": return allowed_origins elif allowed_origins == origin: # Cors origin is either * or specific origin diff --git a/salt/netapi/rest_tornado/saltnado_websockets.py b/salt/netapi/rest_tornado/saltnado_websockets.py index 00060763ab6..55c814a2398 100644 --- a/salt/netapi/rest_tornado/saltnado_websockets.py +++ b/salt/netapi/rest_tornado/saltnado_websockets.py @@ -1,5 +1,5 @@ # encoding: utf-8 -''' +""" A Websockets add-on to saltnado =============================== @@ -288,50 +288,53 @@ in which each job's information is keyed by salt's ``jid``. Setup ===== -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + +import salt.ext.tornado.gen import salt.ext.tornado.websocket +import salt.netapi +import salt.utils.json + from . import event_processor from .saltnado import _check_cors_origin -import salt.ext.tornado.gen - -import salt.utils.json -import salt.netapi - _json = salt.utils.json.import_json() -import logging + log = logging.getLogger(__name__) -class AllEventsHandler(salt.ext.tornado.websocket.WebSocketHandler): # pylint: disable=W0223,W0232 - ''' +class AllEventsHandler( + salt.ext.tornado.websocket.WebSocketHandler +): # pylint: disable=W0223,W0232 + """ Server side websocket handler. - ''' + """ # pylint: disable=W0221 def get(self, token): - ''' + """ Check the token, returns a 401 if the token is invalid. Else open the websocket connection - ''' - log.debug('In the websocket get method') + """ + log.debug("In the websocket get method") self.token = token # close the connection, if not authenticated if not self.application.auth.get_tok(token): - log.debug('Refusing websocket connection, bad token!') + log.debug("Refusing websocket connection, bad token!") self.send_error(401) return super(AllEventsHandler, self).get(token) def open(self, token): # pylint: disable=W0221 - ''' + """ Return a websocket connection to Salt representing Salt's "real time" event stream. - ''' + """ self.connected = False @salt.ext.tornado.gen.coroutine @@ -342,11 +345,11 @@ class AllEventsHandler(salt.ext.tornado.websocket.WebSocketHandler): # pylint: These messages make up salt's "real time" event stream. """ - log.debug('Got websocket message %s', message) - if message == 'websocket client ready': + log.debug("Got websocket message %s", message) + if message == "websocket client ready": if self.connected: # TBD: Add ability to run commands in this branch - log.debug('Websocket already connected, returning') + log.debug("Websocket already connected, returning") return self.connected = True @@ -354,10 +357,12 @@ class AllEventsHandler(salt.ext.tornado.websocket.WebSocketHandler): # pylint: while True: try: event = yield self.application.event_listener.get_event(self) - self.write_message( - salt.utils.json.dumps(event, _json_module=_json)) + self.write_message(salt.utils.json.dumps(event, _json_module=_json)) except Exception as err: # pylint: disable=broad-except - log.info('Error! Ending server side websocket connection. Reason = %s', err) + log.info( + "Error! Ending server side websocket connection. Reason = %s", + err, + ) break self.close() @@ -366,10 +371,10 @@ class AllEventsHandler(salt.ext.tornado.websocket.WebSocketHandler): # pylint: pass def on_close(self, *args, **kwargs): - '''Cleanup. + """Cleanup. - ''' - log.debug('In the websocket close method') + """ + log.debug("In the websocket close method") self.close() def check_origin(self, origin): @@ -379,14 +384,13 @@ class AllEventsHandler(salt.ext.tornado.websocket.WebSocketHandler): # pylint: mod_opts = self.application.mod_opts - if mod_opts.get('cors_origin'): - return bool(_check_cors_origin(origin, mod_opts['cors_origin'])) + if mod_opts.get("cors_origin"): + return bool(_check_cors_origin(origin, mod_opts["cors_origin"])) else: return super(AllEventsHandler, self).check_origin(origin) class FormattedEventsHandler(AllEventsHandler): # pylint: disable=W0223,W0232 - @salt.ext.tornado.gen.coroutine def on_message(self, message): """Listens for a "websocket client ready" message. @@ -395,32 +399,37 @@ class FormattedEventsHandler(AllEventsHandler): # pylint: disable=W0223,W0232 These messages make up salt's "real time" event stream. """ - log.debug('Got websocket message %s', message) - if message == 'websocket client ready': + log.debug("Got websocket message %s", message) + if message == "websocket client ready": if self.connected: # TBD: Add ability to run commands in this branch - log.debug('Websocket already connected, returning') + log.debug("Websocket already connected, returning") return self.connected = True evt_processor = event_processor.SaltInfo(self) client = salt.netapi.NetapiClient(self.application.opts) - client.run({ - 'fun': 'grains.items', - 'tgt': '*', - 'token': self.token, - 'mode': 'client', - 'asynchronous': 'local_async', - 'client': 'local' - }) + client.run( + { + "fun": "grains.items", + "tgt": "*", + "token": self.token, + "mode": "client", + "asynchronous": "local_async", + "client": "local", + } + ) while True: try: event = yield self.application.event_listener.get_event(self) evt_processor.process(event, self.token, self.application.opts) # self.write_message('data: {0}\n\n'.format(salt.utils.json.dumps(event, _json_module=_json))) except Exception as err: # pylint: disable=broad-except - log.debug('Error! Ending server side websocket connection. Reason = %s', err) + log.debug( + "Error! Ending server side websocket connection. Reason = %s", + err, + ) break self.close() diff --git a/salt/netapi/rest_wsgi.py b/salt/netapi/rest_wsgi.py index 933e78b2309..31b58b7c28c 100644 --- a/salt/netapi/rest_wsgi.py +++ b/salt/netapi/rest_wsgi.py @@ -1,5 +1,5 @@ # encoding: utf-8 -''' +""" A minimalist REST API for Salt ============================== @@ -120,8 +120,9 @@ Usage examples :status 200: success :status 401: authentication required -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import errno import logging import os @@ -133,16 +134,16 @@ import salt.utils.json # HTTP response codes to response headers map H = { - 200: '200 OK', - 400: '400 BAD REQUEST', - 401: '401 UNAUTHORIZED', - 404: '404 NOT FOUND', - 405: '405 METHOD NOT ALLOWED', - 406: '406 NOT ACCEPTABLE', - 500: '500 INTERNAL SERVER ERROR', + 200: "200 OK", + 400: "400 BAD REQUEST", + 401: "401 UNAUTHORIZED", + 404: "404 NOT FOUND", + 405: "405 METHOD NOT ALLOWED", + 406: "406 NOT ACCEPTABLE", + 500: "500 INTERNAL SERVER ERROR", } -__virtualname__ = 'rest_wsgi' +__virtualname__ = "rest_wsgi" logger = logging.getLogger(__virtualname__) @@ -150,26 +151,27 @@ logger = logging.getLogger(__virtualname__) def __virtual__(): mod_opts = __opts__.get(__virtualname__, {}) - if 'port' in mod_opts: + if "port" in mod_opts: return __virtualname__ return False class HTTPError(Exception): - ''' + """ A custom exception that can take action based on an HTTP error code - ''' + """ + def __init__(self, code, message): self.code = code - Exception.__init__(self, '{0}: {1}'.format(code, message)) + Exception.__init__(self, "{0}: {1}".format(code, message)) def mkdir_p(path): - ''' + """ mkdir -p http://stackoverflow.com/a/600612/127816 - ''' + """ try: os.makedirs(path) except OSError as exc: # Python >2.5 @@ -180,22 +182,22 @@ def mkdir_p(path): def read_body(environ): - ''' + """ Pull the body from the request and return it - ''' - length = environ.get('CONTENT_LENGTH', '0') - length = 0 if length == '' else int(length) + """ + length = environ.get("CONTENT_LENGTH", "0") + length = 0 if length == "" else int(length) - return environ['wsgi.input'].read(length) + return environ["wsgi.input"].read(length) def get_json(environ): - ''' + """ Return the request body as JSON - ''' - content_type = environ.get('CONTENT_TYPE', '') - if content_type != 'application/json': - raise HTTPError(406, 'JSON required') + """ + content_type = environ.get("CONTENT_TYPE", "") + if content_type != "application/json": + raise HTTPError(406, "JSON required") try: return salt.utils.json.loads(read_body(environ)) @@ -204,12 +206,12 @@ def get_json(environ): def get_headers(data, extra_headers=None): - ''' + """ Takes the response data as well as any additional headers and returns a tuple of tuples of headers suitable for passing to start_response() - ''' + """ response_headers = { - 'Content-Length': str(len(data)), + "Content-Length": str(len(data)), } if extra_headers: @@ -219,51 +221,54 @@ def get_headers(data, extra_headers=None): def run_chunk(environ, lowstate): - ''' + """ Expects a list of lowstate dictionaries that are executed and returned in order - ''' - client = environ['SALT_APIClient'] + """ + client = environ["SALT_APIClient"] for chunk in lowstate: yield client.run(chunk) def dispatch(environ): - ''' + """ Do any path/method dispatching here and return a JSON-serializable data structure appropriate for the response - ''' - method = environ['REQUEST_METHOD'].upper() + """ + method = environ["REQUEST_METHOD"].upper() - if method == 'GET': - return ("They found me. I don't know how, but they found me. " - "Run for it, Marty!") - elif method == 'POST': + if method == "GET": + return ( + "They found me. I don't know how, but they found me. " "Run for it, Marty!" + ) + elif method == "POST": data = get_json(environ) return run_chunk(environ, data) else: - raise HTTPError(405, 'Method Not Allowed') + raise HTTPError(405, "Method Not Allowed") def saltenviron(environ): - ''' + """ Make Salt's opts dict and the APIClient available in the WSGI environ - ''' - if '__opts__' not in locals(): + """ + if "__opts__" not in locals(): import salt.config - __opts__ = salt.config.client_config( - os.environ.get('SALT_MASTER_CONFIG', '/etc/salt/master')) - environ['SALT_OPTS'] = __opts__ - environ['SALT_APIClient'] = salt.netapi.NetapiClient(__opts__) + __opts__ = salt.config.client_config( + os.environ.get("SALT_MASTER_CONFIG", "/etc/salt/master") + ) + + environ["SALT_OPTS"] = __opts__ + environ["SALT_APIClient"] = salt.netapi.NetapiClient(__opts__) def application(environ, start_response): - ''' + """ Process the request and return a JSON response. Catch errors and return the appropriate HTTP code. - ''' + """ # Instantiate APIClient once for the whole app saltenviron(environ) @@ -283,37 +288,36 @@ def application(environ, start_response): # Convert the response to JSON try: - ret = salt.utils.json.dumps({'return': resp}) + ret = salt.utils.json.dumps({"return": resp}) except TypeError as exc: code = 500 ret = str(exc) # future lint: disable=blacklisted-function # Return the response - start_response(H[code], get_headers(ret, { - 'Content-Type': 'application/json', - })) + start_response(H[code], get_headers(ret, {"Content-Type": "application/json"})) return (ret,) def get_opts(): - ''' + """ Return the Salt master config as __opts__ - ''' + """ import salt.config return salt.config.client_config( - os.environ.get('SALT_MASTER_CONFIG', '/etc/salt/master')) + os.environ.get("SALT_MASTER_CONFIG", "/etc/salt/master") + ) def start(): - ''' + """ Start simple_server() - ''' + """ from wsgiref.simple_server import make_server # When started outside of salt-api __opts__ will not be injected - if '__opts__' not in globals(): - globals()['__opts__'] = get_opts() + if "__opts__" not in globals(): + globals()["__opts__"] = get_opts() if __virtual__() is False: raise SystemExit(1) @@ -321,7 +325,7 @@ def start(): mod_opts = __opts__.get(__virtualname__, {}) # pylint: disable=C0103 - httpd = make_server('localhost', mod_opts['port'], application) + httpd = make_server("localhost", mod_opts["port"], application) try: httpd.serve_forever() @@ -329,5 +333,5 @@ def start(): raise SystemExit(0) -if __name__ == '__main__': +if __name__ == "__main__": start() diff --git a/salt/output/__init__.py b/salt/output/__init__.py index f2a022cd11e..f58f3711913 100644 --- a/salt/output/__init__.py +++ b/salt/output/__init__.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Used to manage the outputter system. This package is the modular system used for managing outputters. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals import errno -import logging import io +import logging import os import re import sys @@ -35,10 +35,10 @@ log = logging.getLogger(__name__) def try_printout(data, out, opts, **kwargs): - ''' + """ Safely get the string to print out, try the configured outputter, then fall back to nested and then to raw - ''' + """ try: printout = get_printout(out, opts)(data, **kwargs) if printout is not None: @@ -46,35 +46,34 @@ def try_printout(data, out, opts, **kwargs): except (KeyError, AttributeError, TypeError): log.debug(traceback.format_exc()) try: - printout = get_printout('nested', opts)(data, **kwargs) + printout = get_printout("nested", opts)(data, **kwargs) if printout is not None: return printout.rstrip() except (KeyError, AttributeError, TypeError): - log.error('Nested output failed: ', exc_info=True) - printout = get_printout('raw', opts)(data, **kwargs) + log.error("Nested output failed: ", exc_info=True) + printout = get_printout("raw", opts)(data, **kwargs) if printout is not None: return printout.rstrip() def get_progress(opts, out, progress): - ''' + """ Get the progress bar from the given outputter - ''' - return salt.loader.raw_mod(opts, - out, - 'rawmodule', - mod='output')['{0}.progress_iter'.format(out)](progress) + """ + return salt.loader.raw_mod(opts, out, "rawmodule", mod="output")[ + "{0}.progress_iter".format(out) + ](progress) def update_progress(opts, progress, progress_iter, out): - ''' + """ Update the progress iterator for the given outputter - ''' + """ # Look up the outputter try: progress_outputter = salt.loader.outputters(opts)[out] except KeyError: # Outputter is not loaded - log.warning('Progress outputter not available.') + log.warning("Progress outputter not available.") return False progress_outputter(progress, progress_iter) @@ -88,20 +87,22 @@ def progress_end(progress_iter): def display_output(data, out=None, opts=None, **kwargs): - ''' + """ Print the passed data using the desired output - ''' + """ if opts is None: opts = {} display_data = try_printout(data, out, opts, **kwargs) - output_filename = opts.get('output_file', None) - log.trace('data = %s', data) + output_filename = opts.get("output_file", None) + log.trace("data = %s", data) try: # output filename can be either '' or None if output_filename: - if not hasattr(output_filename, 'write'): - ofh = salt.utils.files.fopen(output_filename, 'a') # pylint: disable=resource-leakage + if not hasattr(output_filename, "write"): + # pylint: disable=resource-leakage + ofh = salt.utils.files.fopen(output_filename, "a") + # pylint: enable=resource-leakage fh_opened = True else: # Filehandle/file-like object @@ -112,14 +113,14 @@ def display_output(data, out=None, opts=None, **kwargs): fdata = display_data if isinstance(fdata, six.text_type): try: - fdata = fdata.encode('utf-8') + fdata = fdata.encode("utf-8") except (UnicodeDecodeError, UnicodeEncodeError): # try to let the stream write # even if we didn't encode it pass if fdata: ofh.write(salt.utils.stringutils.to_str(fdata)) - ofh.write('\n') + ofh.write("\n") finally: if fh_opened: ofh.close() @@ -133,58 +134,61 @@ def display_output(data, out=None, opts=None, **kwargs): def get_printout(out, opts=None, **kwargs): - ''' + """ Return a printer function - ''' + """ if opts is None: opts = {} - if 'output' in opts and opts['output'] != 'highstate': + if "output" in opts and opts["output"] != "highstate": # new --out option, but don't choke when using --out=highstate at CLI # See Issue #29796 for more information. - out = opts['output'] + out = opts["output"] # Handle setting the output when --static is passed. - if not out and opts.get('static'): - if opts.get('output'): - out = opts['output'] - elif opts.get('fun', '').split('.')[0] == 'state': + if not out and opts.get("static"): + if opts.get("output"): + out = opts["output"] + elif opts.get("fun", "").split(".")[0] == "state": # --static doesn't have an output set at this point, but if we're # running a state function and "out" hasn't already been set, we # should set the out variable to "highstate". Otherwise state runs # are set to "nested" below. See Issue #44556 for more information. - out = 'highstate' + out = "highstate" - if out == 'text': - out = 'txt' - elif out is None or out == '': - out = 'nested' - if opts.get('progress', False): - out = 'progress' + if out == "text": + out = "txt" + elif out is None or out == "": + out = "nested" + if opts.get("progress", False): + out = "progress" opts.update(kwargs) - if 'color' not in opts: + if "color" not in opts: + def is_pipe(): - ''' + """ Check if sys.stdout is a pipe or not - ''' + """ try: fileno = sys.stdout.fileno() except (AttributeError, io.UnsupportedOperation): fileno = -1 # sys.stdout is StringIO or fake return not os.isatty(fileno) - if opts.get('force_color', False): - opts['color'] = True - elif opts.get('no_color', False) or is_pipe() or salt.utils.platform.is_windows(): - opts['color'] = False + if opts.get("force_color", False): + opts["color"] = True + elif ( + opts.get("no_color", False) or is_pipe() or salt.utils.platform.is_windows() + ): + opts["color"] = False else: - opts['color'] = True + opts["color"] = True else: - if opts.get('force_color', False): - opts['color'] = True - elif opts.get('no_color', False) or salt.utils.platform.is_windows(): - opts['color'] = False + if opts.get("force_color", False): + opts["color"] = True + elif opts.get("no_color", False) or salt.utils.platform.is_windows(): + opts["color"] = False else: pass @@ -192,48 +196,49 @@ def get_printout(out, opts=None, **kwargs): if out not in outputters: # Since the grains outputter was removed we don't need to fire this # error when old minions are asking for it - if out != 'grains': + if out != "grains": log.error( - 'Invalid outputter %s specified, fall back to nested', - out, + "Invalid outputter %s specified, fall back to nested", out, ) - return outputters['nested'] + return outputters["nested"] return outputters[out] def out_format(data, out, opts=None, **kwargs): - ''' + """ Return the formatted outputter string for the passed data - ''' + """ return try_printout(data, out, opts, **kwargs) def string_format(data, out, opts=None, **kwargs): - ''' + """ Return the formatted outputter string, removing the ANSI escape sequences. - ''' + """ raw_output = try_printout(data, out, opts, **kwargs) - ansi_escape = re.compile(r'\x1b[^m]*m') - return ansi_escape.sub('', raw_output) + ansi_escape = re.compile(r"\x1b[^m]*m") + return ansi_escape.sub("", raw_output) def html_format(data, out, opts=None, **kwargs): - ''' + """ Return the formatted string as HTML. - ''' + """ ansi_escaped_string = string_format(data, out, opts, **kwargs) - return ansi_escaped_string.replace(' ', '&nbsp;').replace('\n', '<br />') + return ansi_escaped_string.replace(" ", "&nbsp;").replace("\n", "<br />") def strip_esc_sequence(txt): - ''' + """ Replace ESC (ASCII 27/Oct 33) to prevent unsafe strings from writing their own terminal manipulation commands - ''' + """ if isinstance(txt, six.string_types): try: - return txt.replace('\033', '?') + return txt.replace("\033", "?") except UnicodeDecodeError: - return txt.replace(str('\033'), str('?')) # future lint: disable=blacklisted-function + return txt.replace( + str("\033"), str("?") + ) # future lint: disable=blacklisted-function else: return txt diff --git a/salt/output/dson.py b/salt/output/dson.py index c2e5131c731..94af8b46d8d 100644 --- a/salt/output/dson.py +++ b/salt/output/dson.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Display return data in DSON format ================================== @@ -11,42 +11,43 @@ spec can be found `here`__. This outputter requires `Dogeon`__ (installable via pip) .. __: https://github.com/soasme/dogeon -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging +from salt.ext import six + # Import 3rd-party libs try: import dson except ImportError: dson = None -from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): if dson is None: - return (False, 'The dogeon Python package is not installed') + return (False, "The dogeon Python package is not installed") return True def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Print the output data in JSON - ''' + """ try: - dump_opts = {'indent': 4, 'default': repr} + dump_opts = {"indent": 4, "default": repr} - if 'output_indent' in __opts__: + if "output_indent" in __opts__: - indent = __opts__.get('output_indent') + indent = __opts__.get("output_indent") sort_keys = False - if indent == 'pretty': + if indent == "pretty": indent = 4 sort_keys = True @@ -54,19 +55,21 @@ def output(data, **kwargs): # pylint: disable=unused-argument if indent < 0: indent = None - dump_opts['indent'] = indent - dump_opts['sort_keys'] = sort_keys + dump_opts["indent"] = indent + dump_opts["sort_keys"] = sort_keys return dson.dumps(data, **dump_opts) except UnicodeDecodeError as exc: - log.error('Unable to serialize output to dson') + log.error("Unable to serialize output to dson") return dson.dumps( - {'error': 'Unable to serialize output to DSON', - 'message': six.text_type(exc)} + { + "error": "Unable to serialize output to DSON", + "message": six.text_type(exc), + } ) except TypeError: - log.debug('An error occurred while outputting DSON', exc_info=True) + log.debug("An error occurred while outputting DSON", exc_info=True) # Return valid JSON for unserializable objects return dson.dumps({}) diff --git a/salt/output/highstate.py b/salt/output/highstate.py index 1f2f9452fab..d27f63b8ba0 100644 --- a/salt/output/highstate.py +++ b/salt/output/highstate.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Outputter for displaying results of state runs ============================================== @@ -111,49 +111,50 @@ Example output with no special settings in configuration files: Failed: 0 ------------ Total: 0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import pprint import re import textwrap +import salt.output + # Import salt libs import salt.utils.color import salt.utils.data import salt.utils.stringutils -import salt.output # Import 3rd-party libs from salt.ext import six -import logging - log = logging.getLogger(__name__) def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ The HighState Outputter is only meant to be used with the state.highstate function, or a function that returns highstate return data. - ''' + """ # Discard retcode in dictionary as present in orchestrate data - local_masters = [key for key in data.keys() if key.endswith('.local_master')] - orchestrator_output = 'retcode' in data.keys() and len(local_masters) == 1 + local_masters = [key for key in data.keys() if key.endswith(".local_master")] + orchestrator_output = "retcode" in data.keys() and len(local_masters) == 1 if orchestrator_output: - del data['retcode'] + del data["retcode"] # If additional information is passed through via the "data" dictionary to # the highstate outputter, such as "outputter" or "retcode", discard it. # We only want the state data that was passed through, if it is wrapped up # in the "data" key, as the orchestrate runner does. See Issue #31330, # pull request #27838, and pull request #27175 for more information. - if 'data' in data: - data = data.pop('data') + if "data" in data: + data = data.pop("data") - indent_level = kwargs.get('indent_level', 1) + indent_level = kwargs.get("indent_level", 1) ret = [ _format_host(host, hostdata, indent_level=indent_level)[0] for host, hostdata in six.iteritems(data) @@ -161,119 +162,135 @@ def output(data, **kwargs): # pylint: disable=unused-argument if ret: return "\n".join(ret) log.error( - 'Data passed to highstate outputter is not a valid highstate return: %s', - data + "Data passed to highstate outputter is not a valid highstate return: %s", data ) # We should not reach here, but if we do return empty string - return '' + return "" def _format_host(host, data, indent_level=1): - ''' + """ Main highstate formatter. can be called recursively if a nested highstate contains other highstates (ie in an orchestration) - ''' + """ host = salt.utils.data.decode(host) colors = salt.utils.color.get_colors( - __opts__.get('color'), - __opts__.get('color_theme')) - tabular = __opts__.get('state_tabular', False) + __opts__.get("color"), __opts__.get("color_theme") + ) + tabular = __opts__.get("state_tabular", False) rcounts = {} rdurations = [] - hcolor = colors['GREEN'] + hcolor = colors["GREEN"] hstrs = [] nchanges = 0 - strip_colors = __opts__.get('strip_colors', True) + strip_colors = __opts__.get("strip_colors", True) - if isinstance(data, int) or isinstance(data, six.string_types): + if isinstance(data, int): + nchanges = 1 + hstrs.append(("{0} {1}{2[ENDC]}".format(hcolor, data, colors))) + hcolor = colors["CYAN"] # Print the minion name in cyan + elif isinstance(data, six.string_types): # Data in this format is from saltmod.function, # so it is always a 'change' nchanges = 1 - hstrs.append(('{0} {1}{2[ENDC]}' - .format(hcolor, data, colors))) - hcolor = colors['CYAN'] # Print the minion name in cyan - if isinstance(data, list): + for data in data.splitlines(): + hstrs.append(("{0} {1}{2[ENDC]}".format(hcolor, data, colors))) + hcolor = colors["CYAN"] # Print the minion name in cyan + elif isinstance(data, list): # Errors have been detected, list them in RED! - hcolor = colors['LIGHT_RED'] - hstrs.append((' {0}Data failed to compile:{1[ENDC]}' - .format(hcolor, colors))) + hcolor = colors["LIGHT_RED"] + hstrs.append((" {0}Data failed to compile:{1[ENDC]}".format(hcolor, colors))) for err in data: if strip_colors: - err = salt.output.strip_esc_sequence( - salt.utils.data.decode(err) - ) - hstrs.append(('{0}----------\n {1}{2[ENDC]}' - .format(hcolor, err, colors))) - if isinstance(data, dict): + err = salt.output.strip_esc_sequence(salt.utils.data.decode(err)) + hstrs.append( + ("{0}----------\n {1}{2[ENDC]}".format(hcolor, err, colors)) + ) + elif isinstance(data, dict): # Verify that the needed data is present data_tmp = {} for tname, info in six.iteritems(data): - if isinstance(info, dict) and tname is not 'changes' and info and '__run_num__' not in info: - err = ('The State execution failed to record the order ' - 'in which all states were executed. The state ' - 'return missing data is:') + if ( + isinstance(info, dict) + and tname is not "changes" + and info + and "__run_num__" not in info + ): + err = ( + "The State execution failed to record the order " + "in which all states were executed. The state " + "return missing data is:" + ) hstrs.insert(0, pprint.pformat(info)) hstrs.insert(0, err) - if isinstance(info, dict) and 'result' in info: + if isinstance(info, dict) and "result" in info: data_tmp[tname] = info data = data_tmp # Everything rendered as it should display the output - for tname in sorted( - data, - key=lambda k: data[k].get('__run_num__', 0)): + for tname in sorted(data, key=lambda k: data[k].get("__run_num__", 0)): ret = data[tname] # Increment result counts - rcounts.setdefault(ret['result'], 0) - rcounts[ret['result']] += 1 - rduration = ret.get('duration', 0) + rcounts.setdefault(ret["result"], 0) + rcounts[ret["result"]] += 1 + rduration = ret.get("duration", 0) try: rdurations.append(float(rduration)) except ValueError: - rduration, _, _ = rduration.partition(' ms') + rduration, _, _ = rduration.partition(" ms") try: rdurations.append(float(rduration)) except ValueError: - log.error('Cannot parse a float from duration %s', ret.get('duration', 0)) + log.error( + "Cannot parse a float from duration %s", ret.get("duration", 0) + ) - tcolor = colors['GREEN'] - if ret.get('name') in ['state.orch', 'state.orchestrate', 'state.sls']: - nested = output(ret['changes']['return'], indent_level=indent_level+1) - ctext = re.sub('^', ' ' * 14 * indent_level, '\n'+nested, flags=re.MULTILINE) + tcolor = colors["GREEN"] + if ret.get("name") in ["state.orch", "state.orchestrate", "state.sls"]: + nested = output(ret["changes"]["return"], indent_level=indent_level + 1) + ctext = re.sub( + "^", " " * 14 * indent_level, "\n" + nested, flags=re.MULTILINE + ) schanged = True nchanges += 1 else: - schanged, ctext = _format_changes(ret['changes']) + schanged, ctext = _format_changes(ret["changes"]) nchanges += 1 if schanged else 0 # Skip this state if it was successful & diff output was requested - if __opts__.get('state_output_diff', False) and \ - ret['result'] and not schanged: + if ( + __opts__.get("state_output_diff", False) + and ret["result"] + and not schanged + ): continue # Skip this state if state_verbose is False, the result is True and # there were no changes made - if not __opts__.get('state_verbose', False) and \ - ret['result'] and not schanged: + if ( + not __opts__.get("state_verbose", False) + and ret["result"] + and not schanged + ): continue if schanged: - tcolor = colors['CYAN'] - if ret['result'] is False: - hcolor = colors['RED'] - tcolor = colors['RED'] - if ret['result'] is None: - hcolor = colors['LIGHT_YELLOW'] - tcolor = colors['LIGHT_YELLOW'] + tcolor = colors["CYAN"] + if ret["result"] is False: + hcolor = colors["RED"] + tcolor = colors["RED"] + if ret["result"] is None: + hcolor = colors["LIGHT_YELLOW"] + tcolor = colors["LIGHT_YELLOW"] - state_output = __opts__.get('state_output', 'full').lower() - comps = tname.split('_|-') + state_output = __opts__.get("state_output", "full").lower() + comps = tname.split("_|-") - if state_output.endswith('_id'): + if state_output.endswith("_id"): # Swap in the ID for the name. Refs #35137 comps[2] = comps[1] - if state_output.startswith('filter'): + if state_output.startswith("filter"): # By default, full data is shown for all types. However, return # data may be excluded by setting state_output_exclude to a # comma-separated list of True, False or None, or including the @@ -282,143 +299,140 @@ def _format_host(host, data, indent_level=1): # exclude=True, # The same functionality is also available for making return # data terse, instead of excluding it. - cliargs = __opts__.get('arg', []) + cliargs = __opts__.get("arg", []) clikwargs = {} for item in cliargs: - if isinstance(item, dict) and '__kwarg__' in item: + if isinstance(item, dict) and "__kwarg__" in item: clikwargs = item.copy() exclude = clikwargs.get( - 'exclude', __opts__.get('state_output_exclude', []) + "exclude", __opts__.get("state_output_exclude", []) ) if isinstance(exclude, six.string_types): - exclude = six.text_type(exclude).split(',') + exclude = six.text_type(exclude).split(",") - terse = clikwargs.get( - 'terse', __opts__.get('state_output_terse', []) - ) + terse = clikwargs.get("terse", __opts__.get("state_output_terse", [])) if isinstance(terse, six.string_types): - terse = six.text_type(terse).split(',') + terse = six.text_type(terse).split(",") - if six.text_type(ret['result']) in terse: + if six.text_type(ret["result"]) in terse: msg = _format_terse(tcolor, comps, ret, colors, tabular) hstrs.append(msg) continue - if six.text_type(ret['result']) in exclude: + if six.text_type(ret["result"]) in exclude: continue - elif any(( - state_output.startswith('terse'), - state_output.startswith('mixed') and ret['result'] is not False, # only non-error'd - state_output.startswith('changes') and ret['result'] and not schanged # non-error'd non-changed - )): + elif any( + ( + state_output.startswith("terse"), + state_output.startswith("mixed") + and ret["result"] is not False, # only non-error'd + state_output.startswith("changes") + and ret["result"] + and not schanged, # non-error'd non-changed + ) + ): # Print this chunk in a terse way and continue in the loop msg = _format_terse(tcolor, comps, ret, colors, tabular) hstrs.append(msg) continue state_lines = [ - '{tcolor}----------{colors[ENDC]}', - ' {tcolor} ID: {comps[1]}{colors[ENDC]}', - ' {tcolor}Function: {comps[0]}.{comps[3]}{colors[ENDC]}', - ' {tcolor} Result: {ret[result]!s}{colors[ENDC]}', - ' {tcolor} Comment: {comment}{colors[ENDC]}', + "{tcolor}----------{colors[ENDC]}", + " {tcolor} ID: {comps[1]}{colors[ENDC]}", + " {tcolor}Function: {comps[0]}.{comps[3]}{colors[ENDC]}", + " {tcolor} Result: {ret[result]!s}{colors[ENDC]}", + " {tcolor} Comment: {comment}{colors[ENDC]}", ] - if __opts__.get('state_output_profile', True) and 'start_time' in ret: - state_lines.extend([ - ' {tcolor} Started: {ret[start_time]!s}{colors[ENDC]}', - ' {tcolor}Duration: {ret[duration]!s}{colors[ENDC]}', - ]) + if __opts__.get("state_output_profile", True) and "start_time" in ret: + state_lines.extend( + [ + " {tcolor} Started: {ret[start_time]!s}{colors[ENDC]}", + " {tcolor}Duration: {ret[duration]!s}{colors[ENDC]}", + ] + ) # This isn't the prettiest way of doing this, but it's readable. if comps[1] != comps[2]: - state_lines.insert( - 3, ' {tcolor} Name: {comps[2]}{colors[ENDC]}') + state_lines.insert(3, " {tcolor} Name: {comps[2]}{colors[ENDC]}") # be sure that ret['comment'] is utf-8 friendly try: - if not isinstance(ret['comment'], six.text_type): - ret['comment'] = six.text_type(ret['comment']) + if not isinstance(ret["comment"], six.text_type): + ret["comment"] = six.text_type(ret["comment"]) except UnicodeDecodeError: # If we got here, we're on Python 2 and ret['comment'] somehow # contained a str type with unicode content. - ret['comment'] = salt.utils.stringutils.to_unicode(ret['comment']) + ret["comment"] = salt.utils.stringutils.to_unicode(ret["comment"]) try: - comment = salt.utils.data.decode(ret['comment']) - comment = comment.strip().replace( - '\n', - '\n' + ' ' * 14) + comment = salt.utils.data.decode(ret["comment"]) + comment = comment.strip().replace("\n", "\n" + " " * 14) except AttributeError: # Assume comment is a list try: - comment = ret['comment'].join(' ').replace( - '\n', - '\n' + ' ' * 13) + comment = ret["comment"].join(" ").replace("\n", "\n" + " " * 13) except AttributeError: # Comment isn't a list either, just convert to string - comment = six.text_type(ret['comment']) - comment = comment.strip().replace( - '\n', - '\n' + ' ' * 14) + comment = six.text_type(ret["comment"]) + comment = comment.strip().replace("\n", "\n" + " " * 14) # If there is a data attribute, append it to the comment - if 'data' in ret: - if isinstance(ret['data'], list): - for item in ret['data']: - comment = '{0} {1}'.format(comment, item) - elif isinstance(ret['data'], dict): - for key, value in ret['data'].items(): - comment = '{0}\n\t\t{1}: {2}'.format(comment, key, value) + if "data" in ret: + if isinstance(ret["data"], list): + for item in ret["data"]: + comment = "{0} {1}".format(comment, item) + elif isinstance(ret["data"], dict): + for key, value in ret["data"].items(): + comment = "{0}\n\t\t{1}: {2}".format(comment, key, value) else: - comment = '{0} {1}'.format(comment, ret['data']) - for detail in ['start_time', 'duration']: - ret.setdefault(detail, '') - if ret['duration'] != '': - ret['duration'] = '{0} ms'.format(ret['duration']) + comment = "{0} {1}".format(comment, ret["data"]) + for detail in ["start_time", "duration"]: + ret.setdefault(detail, "") + if ret["duration"] != "": + ret["duration"] = "{0} ms".format(ret["duration"]) svars = { - 'tcolor': tcolor, - 'comps': comps, - 'ret': ret, - 'comment': salt.utils.data.decode(comment), + "tcolor": tcolor, + "comps": comps, + "ret": ret, + "comment": salt.utils.data.decode(comment), # This nukes any trailing \n and indents the others. - 'colors': colors + "colors": colors, } hstrs.extend([sline.format(**svars) for sline in state_lines]) - changes = ' Changes: ' + ctext - hstrs.append(('{0}{1}{2[ENDC]}' - .format(tcolor, changes, colors))) + changes = " Changes: " + ctext + hstrs.append(("{0}{1}{2[ENDC]}".format(tcolor, changes, colors))) - if 'warnings' in ret: - rcounts.setdefault('warnings', 0) - rcounts['warnings'] += 1 + if "warnings" in ret: + rcounts.setdefault("warnings", 0) + rcounts["warnings"] += 1 wrapper = textwrap.TextWrapper( - width=80, - initial_indent=' ' * 14, - subsequent_indent=' ' * 14 + width=80, initial_indent=" " * 14, subsequent_indent=" " * 14 ) hstrs.append( - ' {colors[LIGHT_RED]} Warnings: {0}{colors[ENDC]}'.format( - wrapper.fill('\n'.join(ret['warnings'])).lstrip(), - colors=colors + " {colors[LIGHT_RED]} Warnings: {0}{colors[ENDC]}".format( + wrapper.fill("\n".join(ret["warnings"])).lstrip(), colors=colors ) ) # Append result counts to end of output - colorfmt = '{0}{1}{2[ENDC]}' - rlabel = {True: 'Succeeded', False: 'Failed', None: 'Not Run', 'warnings': 'Warnings'} - count_max_len = max([len(six.text_type(x)) for x in six.itervalues(rcounts)] or [0]) + colorfmt = "{0}{1}{2[ENDC]}" + rlabel = { + True: "Succeeded", + False: "Failed", + None: "Not Run", + "warnings": "Warnings", + } + count_max_len = max( + [len(six.text_type(x)) for x in six.itervalues(rcounts)] or [0] + ) label_max_len = max([len(x) for x in six.itervalues(rlabel)] or [0]) line_max_len = label_max_len + count_max_len + 2 # +2 for ': ' hstrs.append( colorfmt.format( - colors['CYAN'], - '\nSummary for {0}\n{1}'.format(host, '-' * line_max_len), - colors + colors["CYAN"], + "\nSummary for {0}\n{1}".format(host, "-" * line_max_len), + colors, ) ) def _counts(label, count): - return '{0}: {1:>{2}}'.format( - label, - count, - line_max_len - (len(label) + 2) - ) + return "{0}: {1:>{2}}".format(label, count, line_max_len - (len(label) + 2)) # Successful states changestats = [] @@ -426,109 +440,101 @@ def _format_host(host, data, indent_level=1): # test=True states changestats.append( colorfmt.format( - colors['LIGHT_YELLOW'], - 'unchanged={0}'.format(rcounts.get(None, 0)), - colors + colors["LIGHT_YELLOW"], + "unchanged={0}".format(rcounts.get(None, 0)), + colors, ) ) if nchanges > 0: changestats.append( - colorfmt.format( - colors['GREEN'], - 'changed={0}'.format(nchanges), - colors - ) + colorfmt.format(colors["GREEN"], "changed={0}".format(nchanges), colors) ) if changestats: - changestats = ' ({0})'.format(', '.join(changestats)) + changestats = " ({0})".format(", ".join(changestats)) else: - changestats = '' + changestats = "" hstrs.append( colorfmt.format( - colors['GREEN'], - _counts( - rlabel[True], - rcounts.get(True, 0) + rcounts.get(None, 0) - ), - colors - ) + changestats + colors["GREEN"], + _counts(rlabel[True], rcounts.get(True, 0) + rcounts.get(None, 0)), + colors, + ) + + changestats ) # Failed states num_failed = rcounts.get(False, 0) hstrs.append( colorfmt.format( - colors['RED'] if num_failed else colors['CYAN'], + colors["RED"] if num_failed else colors["CYAN"], _counts(rlabel[False], num_failed), - colors + colors, ) ) - num_warnings = rcounts.get('warnings', 0) + num_warnings = rcounts.get("warnings", 0) if num_warnings: hstrs.append( colorfmt.format( - colors['LIGHT_RED'], - _counts(rlabel['warnings'], num_warnings), - colors + colors["LIGHT_RED"], + _counts(rlabel["warnings"], num_warnings), + colors, ) ) - totals = '{0}\nTotal states run: {1:>{2}}'.format('-' * line_max_len, - sum(six.itervalues(rcounts)) - rcounts.get('warnings', 0), - line_max_len - 7) - hstrs.append(colorfmt.format(colors['CYAN'], totals, colors)) + totals = "{0}\nTotal states run: {1:>{2}}".format( + "-" * line_max_len, + sum(six.itervalues(rcounts)) - rcounts.get("warnings", 0), + line_max_len - 7, + ) + hstrs.append(colorfmt.format(colors["CYAN"], totals, colors)) - if __opts__.get('state_output_profile', True): + if __opts__.get("state_output_profile", True): sum_duration = sum(rdurations) - duration_unit = 'ms' + duration_unit = "ms" # convert to seconds if duration is 1000ms or more if sum_duration > 999: sum_duration /= 1000 - duration_unit = 's' - total_duration = 'Total run time: {0} {1}'.format( - '{0:.3f}'.format(sum_duration).rjust(line_max_len - 5), - duration_unit) - hstrs.append(colorfmt.format(colors['CYAN'], total_duration, colors)) + duration_unit = "s" + total_duration = "Total run time: {0} {1}".format( + "{0:.3f}".format(sum_duration).rjust(line_max_len - 5), duration_unit + ) + hstrs.append(colorfmt.format(colors["CYAN"], total_duration, colors)) if strip_colors: host = salt.output.strip_esc_sequence(host) - hstrs.insert(0, ('{0}{1}:{2[ENDC]}'.format(hcolor, host, colors))) - return '\n'.join(hstrs), nchanges > 0 + hstrs.insert(0, ("{0}{1}:{2[ENDC]}".format(hcolor, host, colors))) + return "\n".join(hstrs), nchanges > 0 def _nested_changes(changes): - ''' + """ Print the changes data using the nested outputter - ''' - ret = '\n' - ret += salt.output.out_format( - changes, - 'nested', - __opts__, - nested_indent=14) + """ + ret = "\n" + ret += salt.output.out_format(changes, "nested", __opts__, nested_indent=14) return ret def _format_changes(changes, orchestration=False): - ''' + """ Format the changes dict based on what the data is - ''' + """ if not changes: - return False, '' + return False, "" if orchestration: return True, _nested_changes(changes) if not isinstance(changes, dict): - return True, 'Invalid Changes data: {0}'.format(changes) + return True, "Invalid Changes data: {0}".format(changes) - ret = changes.get('ret') - if ret is not None and changes.get('out') == 'highstate': - ctext = '' + ret = changes.get("ret") + if ret is not None and changes.get("out") == "highstate": + ctext = "" changed = False for host, hostdata in six.iteritems(ret): s, c = _format_host(host, hostdata) - ctext += '\n' + '\n'.join((' ' * 14 + l) for l in s.splitlines()) + ctext += "\n" + "\n".join((" " * 14 + l) for l in s.splitlines()) changed = changed or c else: changed = True @@ -537,44 +543,40 @@ def _format_changes(changes, orchestration=False): def _format_terse(tcolor, comps, ret, colors, tabular): - ''' + """ Terse formatting of a message. - ''' - result = 'Clean' - if ret['changes']: - result = 'Changed' - if ret['result'] is False: - result = 'Failed' - elif ret['result'] is None: - result = 'Differs' + """ + result = "Clean" + if ret["changes"]: + result = "Changed" + if ret["result"] is False: + result = "Failed" + elif ret["result"] is None: + result = "Differs" if tabular is True: - fmt_string = '' - if 'warnings' in ret: - fmt_string += '{c[LIGHT_RED]}Warnings:\n{w}{c[ENDC]}\n'.format( - c=colors, w='\n'.join(ret['warnings']) + fmt_string = "" + if "warnings" in ret: + fmt_string += "{c[LIGHT_RED]}Warnings:\n{w}{c[ENDC]}\n".format( + c=colors, w="\n".join(ret["warnings"]) ) - fmt_string += '{0}' - if __opts__.get('state_output_profile', True) and 'start_time' in ret: - fmt_string += '{6[start_time]!s} [{6[duration]!s:>7} ms] ' - fmt_string += '{2:>10}.{3:<10} {4:7} Name: {1}{5}' + fmt_string += "{0}" + if __opts__.get("state_output_profile", True) and "start_time" in ret: + fmt_string += "{6[start_time]!s} [{6[duration]!s:>7} ms] " + fmt_string += "{2:>10}.{3:<10} {4:7} Name: {1}{5}" elif isinstance(tabular, six.string_types): fmt_string = tabular else: - fmt_string = '' - if 'warnings' in ret: - fmt_string += '{c[LIGHT_RED]}Warnings:\n{w}{c[ENDC]}'.format( - c=colors, w='\n'.join(ret['warnings']) + fmt_string = "" + if "warnings" in ret: + fmt_string += "{c[LIGHT_RED]}Warnings:\n{w}{c[ENDC]}".format( + c=colors, w="\n".join(ret["warnings"]) ) - fmt_string += ' {0} Name: {1} - Function: {2}.{3} - Result: {4}' - if __opts__.get('state_output_profile', True) and 'start_time' in ret: - fmt_string += ' Started: - {6[start_time]!s} Duration: {6[duration]!s} ms' - fmt_string += '{5}' + fmt_string += " {0} Name: {1} - Function: {2}.{3} - Result: {4}" + if __opts__.get("state_output_profile", True) and "start_time" in ret: + fmt_string += " Started: - {6[start_time]!s} Duration: {6[duration]!s} ms" + fmt_string += "{5}" - msg = fmt_string.format(tcolor, - comps[2], - comps[0], - comps[-1], - result, - colors['ENDC'], - ret) + msg = fmt_string.format( + tcolor, comps[2], comps[0], comps[-1], result, colors["ENDC"], ret + ) return msg diff --git a/salt/output/json_out.py b/salt/output/json_out.py index a68ac588280..52bee2967a2 100644 --- a/salt/output/json_out.py +++ b/salt/output/json_out.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Display return data in JSON format ================================== @@ -35,7 +35,7 @@ CLI Example: .. code-block:: bash salt '*' foo.bar --out=json -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -50,31 +50,31 @@ from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'json' +__virtualname__ = "json" def __virtual__(): - ''' + """ Rename to json - ''' + """ return __virtualname__ def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Print the output data in JSON - ''' + """ try: - if 'output_indent' not in __opts__: + if "output_indent" not in __opts__: return salt.utils.json.dumps(data, default=repr, indent=4) - indent = __opts__.get('output_indent') + indent = __opts__.get("output_indent") sort_keys = False if indent is None: indent = None - elif indent == 'pretty': + elif indent == "pretty": indent = 4 sort_keys = True @@ -82,16 +82,20 @@ def output(data, **kwargs): # pylint: disable=unused-argument if indent < 0: indent = None - return salt.utils.json.dumps(data, default=repr, indent=indent, sort_keys=sort_keys) + return salt.utils.json.dumps( + data, default=repr, indent=indent, sort_keys=sort_keys + ) except UnicodeDecodeError as exc: - log.error('Unable to serialize output to json') + log.error("Unable to serialize output to json") return salt.utils.json.dumps( - {'error': 'Unable to serialize output to json', - 'message': six.text_type(exc)} + { + "error": "Unable to serialize output to json", + "message": six.text_type(exc), + } ) except TypeError: - log.debug('An error occurred while outputting JSON', exc_info=True) + log.debug("An error occurred while outputting JSON", exc_info=True) # Return valid JSON for unserializable objects return salt.utils.json.dumps({}) diff --git a/salt/output/key.py b/salt/output/key.py index fb9b872be3f..b95f591f677 100644 --- a/salt/output/key.py +++ b/salt/output/key.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Display salt-key output ======================= The ``salt-key`` command makes use of this outputter to format its output. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -14,94 +14,88 @@ import salt.utils.data def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Read in the dict structure generated by the salt key API methods and print the structure. - ''' + """ color = salt.utils.color.get_colors( - __opts__.get('color'), - __opts__.get('color_theme')) - strip_colors = __opts__.get('strip_colors', True) + __opts__.get("color"), __opts__.get("color_theme") + ) + strip_colors = __opts__.get("strip_colors", True) ident = 0 - if __opts__.get('__multi_key'): + if __opts__.get("__multi_key"): ident = 4 - if __opts__['transport'] in ('zeromq', 'tcp'): - acc = 'minions' - pend = 'minions_pre' - den = 'minions_denied' - rej = 'minions_rejected' + if __opts__["transport"] in ("zeromq", "tcp"): + acc = "minions" + pend = "minions_pre" + den = "minions_denied" + rej = "minions_rejected" - cmap = {pend: color['RED'], - acc: color['GREEN'], - den: color['MAGENTA'], - rej: color['BLUE'], - 'local': color['MAGENTA']} + cmap = { + pend: color["RED"], + acc: color["GREEN"], + den: color["MAGENTA"], + rej: color["BLUE"], + "local": color["MAGENTA"], + } - trans = {pend: u'{0}{1}Unaccepted Keys:{2}'.format( - ' ' * ident, - color['LIGHT_RED'], - color['ENDC']), - acc: u'{0}{1}Accepted Keys:{2}'.format( - ' ' * ident, - color['LIGHT_GREEN'], - color['ENDC']), - den: u'{0}{1}Denied Keys:{2}'.format( - ' ' * ident, - color['LIGHT_MAGENTA'], - color['ENDC']), - rej: u'{0}{1}Rejected Keys:{2}'.format( - ' ' * ident, - color['LIGHT_BLUE'], - color['ENDC']), - 'local': u'{0}{1}Local Keys:{2}'.format( - ' ' * ident, - color['LIGHT_MAGENTA'], - color['ENDC'])} + trans = { + pend: "{0}{1}Unaccepted Keys:{2}".format( + " " * ident, color["LIGHT_RED"], color["ENDC"] + ), + acc: "{0}{1}Accepted Keys:{2}".format( + " " * ident, color["LIGHT_GREEN"], color["ENDC"] + ), + den: "{0}{1}Denied Keys:{2}".format( + " " * ident, color["LIGHT_MAGENTA"], color["ENDC"] + ), + rej: "{0}{1}Rejected Keys:{2}".format( + " " * ident, color["LIGHT_BLUE"], color["ENDC"] + ), + "local": "{0}{1}Local Keys:{2}".format( + " " * ident, color["LIGHT_MAGENTA"], color["ENDC"] + ), + } else: - acc = 'accepted' - pend = 'pending' - rej = 'rejected' + acc = "accepted" + pend = "pending" + rej = "rejected" - cmap = {pend: color['RED'], - acc: color['GREEN'], - rej: color['BLUE'], - 'local': color['MAGENTA']} + cmap = { + pend: color["RED"], + acc: color["GREEN"], + rej: color["BLUE"], + "local": color["MAGENTA"], + } - trans = {pend: u'{0}{1}Unaccepted Keys:{2}'.format( - ' ' * ident, - color['LIGHT_RED'], - color['ENDC']), - acc: u'{0}{1}Accepted Keys:{2}'.format( - ' ' * ident, - color['LIGHT_GREEN'], - color['ENDC']), - rej: u'{0}{1}Rejected Keys:{2}'.format( - ' ' * ident, - color['LIGHT_BLUE'], - color['ENDC']), - 'local': u'{0}{1}Local Keys:{2}'.format( - ' ' * ident, - color['LIGHT_MAGENTA'], - color['ENDC'])} + trans = { + pend: "{0}{1}Unaccepted Keys:{2}".format( + " " * ident, color["LIGHT_RED"], color["ENDC"] + ), + acc: "{0}{1}Accepted Keys:{2}".format( + " " * ident, color["LIGHT_GREEN"], color["ENDC"] + ), + rej: "{0}{1}Rejected Keys:{2}".format( + " " * ident, color["LIGHT_BLUE"], color["ENDC"] + ), + "local": "{0}{1}Local Keys:{2}".format( + " " * ident, color["LIGHT_MAGENTA"], color["ENDC"] + ), + } - ret = '' + ret = "" for status in sorted(data): - ret += u'{0}\n'.format(trans[status]) + ret += "{0}\n".format(trans[status]) for key in sorted(data[status]): key = salt.utils.data.decode(key) skey = salt.output.strip_esc_sequence(key) if strip_colors else key if isinstance(data[status], list): - ret += u'{0}{1}{2}{3}\n'.format( - ' ' * ident, - cmap[status], - skey, - color['ENDC']) + ret += "{0}{1}{2}{3}\n".format( + " " * ident, cmap[status], skey, color["ENDC"] + ) if isinstance(data[status], dict): - ret += u'{0}{1}{2}: {3}{4}\n'.format( - ' ' * ident, - cmap[status], - skey, - data[status][key], - color['ENDC']) + ret += "{0}{1}{2}: {3}{4}\n".format( + " " * ident, cmap[status], skey, data[status][key], color["ENDC"] + ) return ret diff --git a/salt/output/nested.py b/salt/output/nested.py index 8e3cb74ce5d..d72e6c8253b 100644 --- a/salt/output/nested.py +++ b/salt/output/nested.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Recursively display nested data =============================== @@ -22,8 +22,9 @@ Example output:: list: - Hello - World -''' +""" from __future__ import absolute_import, print_function, unicode_literals + # Import python libs from numbers import Number @@ -37,44 +38,35 @@ from salt.ext import six try: from collections.abc import Mapping except ImportError: + # pylint: disable=no-name-in-module from collections import Mapping + # pylint: enable=no-name-in-module + class NestDisplay(object): - ''' + """ Manage the nested display contents - ''' + """ + def __init__(self, retcode=0): self.__dict__.update( salt.utils.color.get_colors( - __opts__.get('color'), - __opts__.get('color_theme') + __opts__.get("color"), __opts__.get("color_theme") ) ) - self.strip_colors = __opts__.get('strip_colors', True) + self.strip_colors = __opts__.get("strip_colors", True) self.retcode = retcode - def ustring(self, - indent, - color, - msg, - prefix='', - suffix='', - endc=None): + def ustring(self, indent, color, msg, prefix="", suffix="", endc=None): if endc is None: endc = self.ENDC - indent *= ' ' - fmt = '{0}{1}{2}{3}{4}{5}' + indent *= " " + fmt = "{0}{1}{2}{3}{4}{5}" try: - return fmt.format( - indent, - color, - prefix, - msg, - endc, - suffix) + return fmt.format(indent, color, prefix, msg, endc, suffix) except UnicodeDecodeError: try: return fmt.format( @@ -83,21 +75,18 @@ class NestDisplay(object): prefix, salt.utils.stringutils.to_unicode(msg), endc, - suffix) + suffix, + ) except UnicodeDecodeError: # msg contains binary data that can't be decoded return str(fmt).format( # future lint: disable=blacklisted-function - indent, - color, - prefix, - msg, - endc, - suffix) + indent, color, prefix, msg, endc, suffix + ) def display(self, ret, indent, prefix, out): - ''' + """ Recursively iterate down through data structures to determine output - ''' + """ if isinstance(ret, bytes): try: ret = salt.utils.stringutils.to_unicode(ret) @@ -106,51 +95,29 @@ class NestDisplay(object): pass if ret is None or ret is True or ret is False: - out.append( - self.ustring( - indent, - self.LIGHT_YELLOW, - ret, - prefix=prefix - ) - ) + out.append(self.ustring(indent, self.LIGHT_YELLOW, ret, prefix=prefix)) # Number includes all python numbers types # (float, int, long, complex, ...) # use repr() to get the full precision also for older python versions # as until about python32 it was limited to 12 digits only by default elif isinstance(ret, Number): out.append( - self.ustring( - indent, - self.LIGHT_YELLOW, - repr(ret), - prefix=prefix - ) + self.ustring(indent, self.LIGHT_YELLOW, repr(ret), prefix=prefix) ) elif isinstance(ret, six.string_types): first_line = True for line in ret.splitlines(): - line_prefix = ' ' * len(prefix) if not first_line else prefix + line_prefix = " " * len(prefix) if not first_line else prefix if isinstance(line, bytes): out.append( self.ustring( - indent, - self.YELLOW, - 'Not string data', - prefix=line_prefix + indent, self.YELLOW, "Not string data", prefix=line_prefix ) ) break if self.strip_colors: line = salt.output.strip_esc_sequence(line) - out.append( - self.ustring( - indent, - self.GREEN, - line, - prefix=line_prefix - ) - ) + out.append(self.ustring(indent, self.GREEN, line, prefix=line_prefix)) first_line = False elif isinstance(ret, (list, tuple)): color = self.GREEN @@ -158,29 +125,17 @@ class NestDisplay(object): color = self.RED for ind in ret: if isinstance(ind, (list, tuple, Mapping)): - out.append( - self.ustring( - indent, - color, - '|_' - ) - ) - prefix = '' if isinstance(ind, Mapping) else '- ' + out.append(self.ustring(indent, color, "|_")) + prefix = "" if isinstance(ind, Mapping) else "- " self.display(ind, indent + 2, prefix, out) else: - self.display(ind, indent, '- ', out) + self.display(ind, indent, "- ", out) elif isinstance(ret, Mapping): if indent: color = self.CYAN if self.retcode != 0: color = self.RED - out.append( - self.ustring( - indent, - color, - '----------' - ) - ) + out.append(self.ustring(indent, color, "----------")) # respect key ordering of ordered dicts if isinstance(ret, salt.utils.odict.OrderedDict): @@ -192,33 +147,24 @@ class NestDisplay(object): color = self.RED for key in keys: val = ret[key] - out.append( - self.ustring( - indent, - color, - key, - suffix=':', - prefix=prefix - ) - ) - self.display(val, indent + 4, '', out) + out.append(self.ustring(indent, color, key, suffix=":", prefix=prefix)) + self.display(val, indent + 4, "", out) return out def output(ret, **kwargs): - ''' + """ Display ret data - ''' + """ # Prefer kwargs before opts - retcode = kwargs.get('_retcode', 0) - base_indent = kwargs.get('nested_indent', 0) \ - or __opts__.get('nested_indent', 0) + retcode = kwargs.get("_retcode", 0) + base_indent = kwargs.get("nested_indent", 0) or __opts__.get("nested_indent", 0) nest = NestDisplay(retcode=retcode) - lines = nest.display(ret, base_indent, '', []) + lines = nest.display(ret, base_indent, "", []) try: - return '\n'.join(lines) + return "\n".join(lines) except UnicodeDecodeError: # output contains binary data that can't be decoded - return str('\n').join( # future lint: disable=blacklisted-function + return str("\n").join( # future lint: disable=blacklisted-function [salt.utils.stringutils.to_str(x) for x in lines] ) diff --git a/salt/output/newline_values_only.py b/salt/output/newline_values_only.py index cbe5f89d38c..89eb3e8ecc0 100644 --- a/salt/output/newline_values_only.py +++ b/salt/output/newline_values_only.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Display values only, separated by newlines ========================================== @@ -75,7 +75,7 @@ Output 8 10 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -102,7 +102,7 @@ def _get_values(data): def _one_level_values(data): - return '\n'.join(_string_list(_get_values(data))) + return "\n".join(_string_list(_get_values(data))) def _string_list(a_list): @@ -110,7 +110,7 @@ def _string_list(a_list): def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Display modified ret data - ''' + """ return _one_level_values(data) diff --git a/salt/output/no_out_quiet.py b/salt/output/no_out_quiet.py index 8bd4b167ee3..ef6eb23fee3 100644 --- a/salt/output/no_out_quiet.py +++ b/salt/output/no_out_quiet.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Display no output ================= @@ -10,10 +10,10 @@ CLI Example: .. code-block:: bash salt '*' foo.bar --out=quiet -''' +""" # Define the module's virtual name -__virtualname__ = 'quiet' +__virtualname__ = "quiet" def __virtual__(): @@ -21,8 +21,8 @@ def __virtual__(): def output(ret, **kwargs): # pylint: disable=unused-argument - ''' + """ Don't display data. Used when you only are interested in the return. - ''' - return '' + """ + return "" diff --git a/salt/output/no_return.py b/salt/output/no_return.py index 3c3995bf562..893cc26e48f 100644 --- a/salt/output/no_return.py +++ b/salt/output/no_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Display output for minions that did not return ============================================== @@ -11,7 +11,7 @@ Example output:: virtucentos: Minion did not return -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -22,43 +22,38 @@ from salt.ext import six class NestDisplay(object): - ''' + """ Create generator for nested output - ''' + """ + def __init__(self): self.colors = salt.utils.color.get_colors( - __opts__.get('color'), - __opts__.get('color_theme')) + __opts__.get("color"), __opts__.get("color_theme") + ) def display(self, ret, indent, prefix, out): - ''' + """ Recursively iterate down through data structures to determine output - ''' + """ if isinstance(ret, six.string_types): - lines = ret.split('\n') + lines = ret.split("\n") for line in lines: - out += '{0}{1}{2}{3}{4}\n'.format( - self.colors['RED'], - ' ' * indent, - prefix, - line, - self.colors['ENDC']) + out += "{0}{1}{2}{3}{4}\n".format( + self.colors["RED"], " " * indent, prefix, line, self.colors["ENDC"] + ) elif isinstance(ret, dict): for key in sorted(ret): val = ret[key] - out += '{0}{1}{2}{3}{4}:\n'.format( - self.colors['CYAN'], - ' ' * indent, - prefix, - key, - self.colors['ENDC']) - out = self.display(val, indent + 4, '', out) + out += "{0}{1}{2}{3}{4}:\n".format( + self.colors["CYAN"], " " * indent, prefix, key, self.colors["ENDC"] + ) + out = self.display(val, indent + 4, "", out) return out def output(ret, **kwargs): # pylint: disable=unused-argument - ''' + """ Display ret data - ''' + """ nest = NestDisplay() - return nest.display(ret, 0, '', '') + return nest.display(ret, 0, "", "") diff --git a/salt/output/overstatestage.py b/salt/output/overstatestage.py index 8c4b11f0c4e..f6beb1b761a 100644 --- a/salt/output/overstatestage.py +++ b/salt/output/overstatestage.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Display clean output of an overstate stage ========================================== This outputter is used to display :ref:`Orchestrate Runner <orchestrate-runner>` stages, and should not be called directly. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -24,23 +24,18 @@ from salt.ext import six def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Format the data for printing stage information from the overstate system - ''' + """ colors = salt.utils.color.get_colors( - __opts__.get('color'), - __opts__.get('color_theme')) - ostr = '' + __opts__.get("color"), __opts__.get("color_theme") + ) + ostr = "" for comp in data: for name, stage in six.iteritems(comp): - ostr += '{0}{1}: {2}\n'.format( - colors['LIGHT_BLUE'], - name, - colors['ENDC']) + ostr += "{0}{1}: {2}\n".format(colors["LIGHT_BLUE"], name, colors["ENDC"]) for key in sorted(stage): - ostr += ' {0}{1}: {2}{3}\n'.format( - colors['LIGHT_BLUE'], - key, - stage[key], - colors['ENDC']) + ostr += " {0}{1}: {2}{3}\n".format( + colors["LIGHT_BLUE"], key, stage[key], colors["ENDC"] + ) return ostr diff --git a/salt/output/pony.py b/salt/output/pony.py index 85fb9ec6de5..efd24937c50 100644 --- a/salt/output/pony.py +++ b/salt/output/pony.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Display Pony output data structure ================================== @@ -45,29 +45,31 @@ CLI Example: .. code-block:: bash salt '*' foo.bar --out=pony -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import subprocess # Import Salt libs import salt.utils.data import salt.utils.path - -__virtualname__ = 'pony' +__virtualname__ = "pony" def __virtual__(): - if salt.utils.path.which('ponysay'): + if salt.utils.path.which("ponysay"): return __virtualname__ return False def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Mane function - ''' - high_out = __salt__['highstate'](data) - return subprocess.check_output(['ponysay', salt.utils.data.decode(high_out)]) # pylint: disable=E0598 + """ + high_out = __salt__["highstate"](data) + return subprocess.check_output( + ["ponysay", salt.utils.data.decode(high_out)] + ) # pylint: disable=E0598 diff --git a/salt/output/pprint_out.py b/salt/output/pprint_out.py index c2b4986ae1a..3c02da99d9e 100644 --- a/salt/output/pprint_out.py +++ b/salt/output/pprint_out.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Python pretty-print (pprint) ============================ @@ -19,7 +19,7 @@ Example output: {'saltmine': {'foo': {'bar': 'baz', 'dictionary': {'abc': 123, 'def': 456}, 'list': ['Hello', 'World']}}} -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -29,22 +29,22 @@ import pprint from salt.ext import six # Define the module's virtual name -__virtualname__ = 'pprint' +__virtualname__ = "pprint" def __virtual__(): - ''' + """ Change the name to pprint - ''' + """ return __virtualname__ def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Print out via pretty print - ''' + """ if isinstance(data, Exception): data = six.text_type(data) - if 'output_indent' in __opts__ and __opts__['output_indent'] >= 0: - return pprint.pformat(data, indent=__opts__['output_indent']) + if "output_indent" in __opts__ and __opts__["output_indent"] >= 0: + return pprint.pformat(data, indent=__opts__["output_indent"]) return pprint.pformat(data) diff --git a/salt/output/profile.py b/salt/output/profile.py index 3d981c5a299..2bf8a3483cc 100644 --- a/salt/output/profile.py +++ b/salt/output/profile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Display profiling data in a table format ======================================== @@ -29,11 +29,12 @@ To get the above appearance, use settings something like these:: out.table.delim: ' ' out.table.prefix: '' out.table.suffix: '' -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import salt.output.table_out as table_out -__virtualname__ = 'profile' +__virtualname__ = "profile" def __virtual__(): @@ -42,44 +43,43 @@ def __virtual__(): def _find_durations(data, name_max=60): ret = [] - ml = len('duration (ms)') + ml = len("duration (ms)") for host in data: for sid in data[host]: dat = data[host][sid] - ts = sid.split('_|-') + ts = sid.split("_|-") mod = ts[0] fun = ts[-1] - name = dat.get('name', dat.get('__id__')) - dur = float(data[host][sid].get('duration', -1)) + name = dat.get("name", dat.get("__id__")) + dur = float(data[host][sid].get("duration", -1)) if name is None: - name = '<>' + name = "<>" if len(name) > name_max: - name = name[0:name_max-3] + '...' + name = name[0 : name_max - 3] + "..." - l = len('{0:0.4f}'.format(dur)) + l = len("{0:0.4f}".format(dur)) if l > ml: ml = l - ret.append([dur, name, '{0}.{1}'.format(mod, fun)]) + ret.append([dur, name, "{0}.{1}".format(mod, fun)]) for row in ret: - row[0] = '{0:{w}.4f}'.format(row[0], w=ml) + row[0] = "{0:{w}.4f}".format(row[0], w=ml) return [x[1:] + x[0:1] for x in sorted(ret)] def output(data, **kwargs): - ''' + """ Display the profiling data in a table format. - ''' + """ rows = _find_durations(data) - kwargs['opts'] = __opts__ - kwargs['rows_key'] = 'rows' - kwargs['labels_key'] = 'labels' + kwargs["opts"] = __opts__ + kwargs["rows_key"] = "rows" + kwargs["labels_key"] = "labels" - to_show = {'labels': ['name', 'mod.fun', 'duration (ms)'], - 'rows': rows} + to_show = {"labels": ["name", "mod.fun", "duration (ms)"], "rows": rows} return table_out.output(to_show, **kwargs) diff --git a/salt/output/progress.py b/salt/output/progress.py index 4b1cdb7235e..f06dece4465 100644 --- a/salt/output/progress.py +++ b/salt/output/progress.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Display return data as a progress bar -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -9,6 +9,7 @@ from __future__ import absolute_import, print_function, unicode_literals # Import 3rd-party libs try: import progressbar + HAS_PROGRESSBAR = True except ImportError: HAS_PROGRESSBAR = False @@ -19,11 +20,11 @@ def __virtual__(): def output(ret, bar, **kwargs): # pylint: disable=unused-argument - ''' + """ Update the progress bar - ''' - if 'return_count' in ret: - val = ret['return_count'] + """ + if "return_count" in ret: + val = ret["return_count"] # Avoid to fail if targets are behind a syndic. In this case actual return count will be # higher than targeted by MoM itself. # TODO: implement a way to get the proper target minions count and remove this workaround. @@ -31,14 +32,23 @@ def output(ret, bar, **kwargs): # pylint: disable=unused-argument if val > bar.maxval: bar.maxval = val bar.update(val) - return '' + return "" def progress_iter(progress): - ''' + """ Initialize and return a progress bar iter - ''' - widgets = [progressbar.Percentage(), ' ', progressbar.Bar(), ' ', progressbar.Timer(), ' Returns: [', progressbar.Counter(), '/{0}]'.format(progress['minion_count'])] - bar = progressbar.ProgressBar(widgets=widgets, maxval=progress['minion_count']) + """ + widgets = [ + progressbar.Percentage(), + " ", + progressbar.Bar(), + " ", + progressbar.Timer(), + " Returns: [", + progressbar.Counter(), + "/{0}]".format(progress["minion_count"]), + ] + bar = progressbar.ProgressBar(widgets=widgets, maxval=progress["minion_count"]) bar.start() return bar diff --git a/salt/output/raw.py b/salt/output/raw.py index 57a4902c9e3..6aae14b2c70 100644 --- a/salt/output/raw.py +++ b/salt/output/raw.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Display raw output data structure ================================= @@ -23,7 +23,7 @@ Example output: salt '*' foo.bar --out=table {'myminion': {'foo': {'list': ['Hello', 'World'], 'bar': 'baz', 'dictionary': {'abc': 123, 'def': 456}}}} -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -36,9 +36,9 @@ from salt.ext import six def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Rather basic.... - ''' + """ if not isinstance(data, six.string_types): data = six.text_type(data) return salt.utils.stringutils.to_unicode(data) diff --git a/salt/output/table_out.py b/salt/output/table_out.py index 459f9d763a8..491b59c53d0 100644 --- a/salt/output/table_out.py +++ b/salt/output/table_out.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Display output in a table format ================================= @@ -41,7 +41,7 @@ CLI Example: .. code-block:: bash salt '*' foo.bar --out=table -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -58,7 +58,7 @@ import salt.utils.data from salt.ext import six from salt.ext.six.moves import map, zip # pylint: disable=redefined-builtin -__virtualname__ = 'table' +__virtualname__ = "table" def __virtual__(): @@ -66,33 +66,34 @@ def __virtual__(): class TableDisplay(object): - ''' + """ Manage the table display content. - ''' + """ _JUSTIFY_MAP = { - 'center': six.text_type.center, - 'right': six.text_type.rjust, - 'left': six.text_type.ljust + "center": six.text_type.center, + "right": six.text_type.rjust, + "left": six.text_type.ljust, } - def __init__(self, - has_header=True, # if header will be displayed - row_delimiter='-', # row delimiter char - delim=' | ', # column delimiter - justify='center', # text justify - separate_rows=True, # display the line separating two consecutive rows - prefix='| ', # character to display at the beginning of the row - suffix=' |', # character to display at the end of the row - width=50, # column max width - wrapfunc=None): # function wrapper + def __init__( + self, + has_header=True, # if header will be displayed + row_delimiter="-", # row delimiter char + delim=" | ", # column delimiter + justify="center", # text justify + separate_rows=True, # display the line separating two consecutive rows + prefix="| ", # character to display at the beginning of the row + suffix=" |", # character to display at the end of the row + width=50, # column max width + wrapfunc=None, + ): # function wrapper self.__dict__.update( salt.utils.color.get_colors( - __opts__.get('color'), - __opts__.get('color_theme') + __opts__.get("color"), __opts__.get("color_theme") ) ) - self.strip_colors = __opts__.get('strip_colors', True) + self.strip_colors = __opts__.get("strip_colors", True) self.has_header = has_header self.row_delimiter = row_delimiter @@ -103,121 +104,112 @@ class TableDisplay(object): self.suffix = suffix self.width = width - if not(wrapfunc and callable(wrapfunc)): + if not (wrapfunc and callable(wrapfunc)): self.wrapfunc = self.wrap_onspace else: self.wrapfunc = wrapfunc - def ustring(self, - indent, - color, - msg, - prefix='', - suffix='', - endc=None): - '''Build the unicode string to be displayed.''' + def ustring(self, indent, color, msg, prefix="", suffix="", endc=None): + """Build the unicode string to be displayed.""" if endc is None: endc = self.ENDC # pylint: disable=no-member - indent *= ' ' - fmt = u'{0}{1}{2}{3}{4}{5}' + indent *= " " + fmt = "{0}{1}{2}{3}{4}{5}" try: return fmt.format(indent, color, prefix, msg, endc, suffix) except UnicodeDecodeError: - return fmt.format(indent, color, prefix, salt.utils.data.decode(msg), endc, suffix) + return fmt.format( + indent, color, prefix, salt.utils.data.decode(msg), endc, suffix + ) def wrap_onspace(self, text): - ''' - When the text inside the column is longer then the width, will split by space and continue on the next line.''' + """ + When the text inside the column is longer then the width, will split by space and continue on the next line.""" def _truncate(line, word): - return '{line}{part}{word}'.format( - line=line, - part=' \n'[(len(line[line.rfind('\n')+1:]) + len(word.split('\n', 1)[0]) >= self.width)], - word=word + return "{line}{part}{word}".format( + line=line, + part=" \n"[ + ( + len(line[line.rfind("\n") + 1 :]) + len(word.split("\n", 1)[0]) + >= self.width ) + ], + word=word, + ) - return reduce(_truncate, text.split(' ')) + return reduce(_truncate, text.split(" ")) - def prepare_rows(self, - rows, - indent, - has_header): + def prepare_rows(self, rows, indent, has_header): - '''Prepare rows content to be displayed.''' + """Prepare rows content to be displayed.""" out = [] def row_wrapper(row): - new_rows = [ - self.wrapfunc(item).split('\n') - for item in row - ] + new_rows = [self.wrapfunc(item).split("\n") for item in row] rows = [] for item in map(lambda *args: args, *new_rows): if isinstance(item, (tuple, list)): - rows.append([substr or '' for substr in item]) + rows.append([substr or "" for substr in item]) else: rows.append([item]) return rows - logical_rows = [ - row_wrapper(row) - for row in rows - ] + logical_rows = [row_wrapper(row) for row in rows] columns = map(lambda *args: args, *reduce(operator.add, logical_rows)) max_widths = [ - max([len(six.text_type(item)) for item in column]) - for column in columns + max([len(six.text_type(item)) for item in column]) for column in columns ] - row_separator = self.row_delimiter * (len(self.prefix) + len(self.suffix) + sum(max_widths) + - len(self.delim) * (len(max_widths) - 1)) + row_separator = self.row_delimiter * ( + len(self.prefix) + + len(self.suffix) + + sum(max_widths) + + len(self.delim) * (len(max_widths) - 1) + ) justify = self._JUSTIFY_MAP[self.justify.lower()] if self.separate_rows: out.append( self.ustring( - indent, - self.LIGHT_GRAY, # pylint: disable=no-member - row_separator + indent, self.LIGHT_GRAY, row_separator # pylint: disable=no-member ) ) for physical_rows in logical_rows: for row in physical_rows: - line = self.prefix \ - + self.delim.join([ - justify(six.text_type(item), width) - for (item, width) in zip(row, max_widths) - ]) + self.suffix - out.append( - self.ustring( - indent, - self.WHITE, # pylint: disable=no-member - line + line = ( + self.prefix + + self.delim.join( + [ + justify(six.text_type(item), width) + for (item, width) in zip(row, max_widths) + ] ) + + self.suffix + ) + out.append( + self.ustring(indent, self.WHITE, line) # pylint: disable=no-member ) if self.separate_rows or has_header: out.append( self.ustring( indent, self.LIGHT_GRAY, # pylint: disable=no-member - row_separator + row_separator, ) ) has_header = False return out - def display_rows(self, - rows, - labels, - indent): + def display_rows(self, rows, labels, indent): - '''Prepares row content and displays.''' + """Prepares row content and displays.""" out = [] @@ -243,7 +235,10 @@ class TableDisplay(object): if first_row_type is dict: # and all the others temp_rows = [] if not labels: - labels = [six.text_type(label).replace('_', ' ').title() for label in sorted(rows[0])] + labels = [ + six.text_type(label).replace("_", " ").title() + for label in sorted(rows[0]) + ] for row in rows: temp_row = [] for key in sorted(row): @@ -251,21 +246,18 @@ class TableDisplay(object): temp_rows.append(temp_row) rows = temp_rows elif isinstance(rows[0], six.string_types): - rows = [[row] for row in rows] # encapsulate each row in a single-element list + rows = [ + [row] for row in rows + ] # encapsulate each row in a single-element list labels_and_rows = [labels] + rows if labels else rows has_header = self.has_header and labels return self.prepare_rows(labels_and_rows, indent + 4, has_header) - def display(self, - ret, - indent, - out, - rows_key=None, - labels_key=None): + def display(self, ret, indent, out, rows_key=None, labels_key=None): - '''Display table(s).''' + """Display table(s).""" rows = [] labels = None @@ -284,14 +276,14 @@ class TableDisplay(object): indent, self.DARK_GRAY, # pylint: disable=no-member key, - suffix=':' + suffix=":", ) ) out.append( self.ustring( indent, self.DARK_GRAY, # pylint: disable=no-member - '----------' + "----------", ) ) if isinstance(val, (list, tuple)): @@ -301,12 +293,20 @@ class TableDisplay(object): labels = ret.get(labels_key) # if any out.extend(self.display_rows(rows, labels, indent)) else: - self.display(val, indent + 4, out, rows_key=rows_key, labels_key=labels_key) + self.display( + val, + indent + 4, + out, + rows_key=rows_key, + labels_key=labels_key, + ) elif rows_key: # dig deeper for key in sorted(ret): val = ret[key] - self.display(val, indent, out, rows_key=rows_key, labels_key=labels_key) # same indent + self.display( + val, indent, out, rows_key=rows_key, labels_key=labels_key + ) # same indent elif isinstance(ret, (list, tuple)): if not rows_key: rows = ret @@ -316,7 +316,7 @@ class TableDisplay(object): def output(ret, **kwargs): - ''' + """ Display the output as table. Args: @@ -333,29 +333,35 @@ def output(ret, **kwargs): * rows_key: display the rows under a specific key. * labels_key: use the labels under a certain key. Otherwise will try to use the dictionary keys (if any). * title: display title when only one table is selected (using the ``rows_key`` argument). - ''' + """ # to facilitate re-use - if 'opts' in kwargs: + if "opts" in kwargs: global __opts__ # pylint: disable=W0601 - __opts__ = kwargs.pop('opts') + __opts__ = kwargs.pop("opts") # Prefer kwargs before opts - base_indent = kwargs.get('nested_indent', 0) \ - or __opts__.get('out.table.nested_indent', 0) - rows_key = kwargs.get('rows_key') \ - or __opts__.get('out.table.rows_key') - labels_key = kwargs.get('labels_key') \ - or __opts__.get('out.table.labels_key') - title = kwargs.get('title') \ - or __opts__.get('out.table.title') + base_indent = kwargs.get("nested_indent", 0) or __opts__.get( + "out.table.nested_indent", 0 + ) + rows_key = kwargs.get("rows_key") or __opts__.get("out.table.rows_key") + labels_key = kwargs.get("labels_key") or __opts__.get("out.table.labels_key") + title = kwargs.get("title") or __opts__.get("out.table.title") class_kvargs = {} - argks = ('has_header', 'row_delimiter', 'delim', 'justify', 'separate_rows', 'prefix', 'suffix', 'width') + argks = ( + "has_header", + "row_delimiter", + "delim", + "justify", + "separate_rows", + "prefix", + "suffix", + "width", + ) for argk in argks: - argv = kwargs.get(argk) \ - or __opts__.get('out.table.{key}'.format(key=argk)) + argv = kwargs.get(argk) or __opts__.get("out.table.{key}".format(key=argk)) if argv is not None: class_kvargs[argk] = argv @@ -368,12 +374,16 @@ def output(ret, **kwargs): base_indent, title, table.WHITE, # pylint: disable=no-member - suffix='\n' + suffix="\n", ) ) - return '\n'.join(table.display(salt.utils.data.decode(ret), - base_indent, - out, - rows_key=rows_key, - labels_key=labels_key)) + return "\n".join( + table.display( + salt.utils.data.decode(ret), + base_indent, + out, + rows_key=rows_key, + labels_key=labels_key, + ) + ) diff --git a/salt/output/txt.py b/salt/output/txt.py index 595a04e9f68..9ab2e742391 100644 --- a/salt/output/txt.py +++ b/salt/output/txt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Simple text outputter ===================== @@ -11,7 +11,7 @@ CLI Example: .. code-block:: bash salt '*' foo.bar --out=txt -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -19,24 +19,24 @@ import pprint def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Output the data in lines, very nice for running commands - ''' - ret = '' - if hasattr(data, 'keys'): + """ + ret = "" + if hasattr(data, "keys"): for key in data: value = data[key] # Don't blow up on non-strings try: for line in value.splitlines(): - ret += '{0}: {1}\n'.format(key, line) + ret += "{0}: {1}\n".format(key, line) except AttributeError: - ret += '{0}: {1}\n'.format(key, value) + ret += "{0}: {1}\n".format(key, value) else: try: - ret += data + '\n' + ret += data + "\n" except TypeError: # For non-dictionary, non-string data, just use print - ret += '{0}\n'.format(pprint.pformat(data)) + ret += "{0}\n".format(pprint.pformat(data)) return ret diff --git a/salt/output/virt_query.py b/salt/output/virt_query.py index 0345a28bce1..5bbd026c45f 100644 --- a/salt/output/virt_query.py +++ b/salt/output/virt_query.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" virt.query outputter ==================== Used to display the output from the :mod:`virt.query <salt.runners.virt.query>` runner. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -15,36 +15,39 @@ from salt.ext import six def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Display output for the salt-run virt.query function - ''' - out = '' - for id_ in data['data']: - out += '{0}\n'.format(id_) - for vm_ in data['data'][id_]['vm_info']: - out += ' {0}\n'.format(vm_) - vm_data = data[id_]['vm_info'][vm_] - if 'cpu' in vm_data: - out += ' CPU: {0}\n'.format(vm_data['cpu']) - if 'mem' in vm_data: - out += ' Memory: {0}\n'.format(vm_data['mem']) - if 'state' in vm_data: - out += ' State: {0}\n'.format(vm_data['state']) - if 'graphics' in vm_data: - if vm_data['graphics'].get('type', '') == 'vnc': - out += ' Graphics: vnc - {0}:{1}\n'.format( - id_, - vm_data['graphics']['port']) - if 'disks' in vm_data: - for disk, d_data in six.iteritems(vm_data['disks']): - out += ' Disk - {0}:\n'.format(disk) - out += ' Size: {0}\n'.format(d_data['disk size']) - out += ' File: {0}\n'.format(d_data['file']) - out += ' File Format: {0}\n'.format(d_data['file format']) - if 'nics' in vm_data: - for mac in vm_data['nics']: - out += ' Nic - {0}:\n'.format(mac) - out += ' Source: {0}\n'.format( - vm_data['nics'][mac]['source'][next(six.iterkeys(vm_data['nics'][mac]['source']))]) - out += ' Type: {0}\n'.format(vm_data['nics'][mac]['type']) + """ + out = "" + for id_ in data["data"]: + out += "{0}\n".format(id_) + for vm_ in data["data"][id_]["vm_info"]: + out += " {0}\n".format(vm_) + vm_data = data[id_]["vm_info"][vm_] + if "cpu" in vm_data: + out += " CPU: {0}\n".format(vm_data["cpu"]) + if "mem" in vm_data: + out += " Memory: {0}\n".format(vm_data["mem"]) + if "state" in vm_data: + out += " State: {0}\n".format(vm_data["state"]) + if "graphics" in vm_data: + if vm_data["graphics"].get("type", "") == "vnc": + out += " Graphics: vnc - {0}:{1}\n".format( + id_, vm_data["graphics"]["port"] + ) + if "disks" in vm_data: + for disk, d_data in six.iteritems(vm_data["disks"]): + out += " Disk - {0}:\n".format(disk) + out += " Size: {0}\n".format(d_data["disk size"]) + out += " File: {0}\n".format(d_data["file"]) + out += " File Format: {0}\n".format(d_data["file format"]) + if "nics" in vm_data: + for mac in vm_data["nics"]: + out += " Nic - {0}:\n".format(mac) + out += " Source: {0}\n".format( + vm_data["nics"][mac]["source"][ + next(six.iterkeys(vm_data["nics"][mac]["source"])) + ] + ) + out += " Type: {0}\n".format(vm_data["nics"][mac]["type"]) return out diff --git a/salt/output/yaml_out.py b/salt/output/yaml_out.py index d6e3ebf4ca1..06da2c88310 100644 --- a/salt/output/yaml_out.py +++ b/salt/output/yaml_out.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Display return data in YAML format ================================== @@ -26,7 +26,7 @@ CLI Example: list: - Hello - World -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import third party libs @@ -36,7 +36,7 @@ import logging import salt.utils.yaml # Define the module's virtual name -__virtualname__ = 'yaml' +__virtualname__ = "yaml" log = logging.getLogger(__name__) @@ -46,26 +46,26 @@ def __virtual__(): def output(data, **kwargs): # pylint: disable=unused-argument - ''' + """ Print out YAML using the block mode - ''' + """ params = {} - if 'output_indent' not in __opts__: + if "output_indent" not in __opts__: # default indentation params.update(default_flow_style=False) - elif __opts__['output_indent'] >= 0: + elif __opts__["output_indent"] >= 0: # custom indent - params.update(default_flow_style=False, - indent=__opts__['output_indent']) + params.update(default_flow_style=False, indent=__opts__["output_indent"]) else: # no indentation - params.update(default_flow_style=True, - indent=0) + params.update(default_flow_style=True, indent=0) try: return salt.utils.yaml.safe_dump(data, **params) except Exception as exc: # pylint: disable=broad-except import pprint + log.exception( - 'Exception %s encountered when trying to serialize %s', - exc, pprint.pformat(data) + "Exception %s encountered when trying to serialize %s", + exc, + pprint.pformat(data), ) diff --git a/salt/payload.py b/salt/payload.py index b51385fd63f..6628e02a88d 100644 --- a/salt/payload.py +++ b/salt/payload.py @@ -1,16 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Many aspects of the salt payload need to be managed, from the return of encrypted keys to general payload dynamics and packaging, these happen in here -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime +import gc + # import sys # Use if sys is commented out below import logging -import gc -import datetime # Import salt libs import salt.log @@ -18,11 +20,12 @@ import salt.transport.frame import salt.utils.immutabletypes as immutabletypes import salt.utils.msgpack import salt.utils.stringutils -from salt.exceptions import SaltReqTimeoutError, SaltDeserializationError -from salt.utils.data import CaseInsensitiveDict +from salt.exceptions import SaltDeserializationError, SaltReqTimeoutError # Import third party libs from salt.ext import six +from salt.utils.data import CaseInsensitiveDict + try: import zmq except ImportError: @@ -33,48 +36,49 @@ log = logging.getLogger(__name__) def package(payload): - ''' + """ This method for now just wraps msgpack.dumps, but it is here so that we can make the serialization a custom option in the future with ease. - ''' + """ return salt.utils.msgpack.dumps(payload) def unpackage(package_): - ''' + """ Unpackages a payload - ''' + """ return salt.utils.msgpack.loads(package_, use_list=True) def format_payload(enc, **kwargs): - ''' + """ Pass in the required arguments for a payload, the enc type and the cmd, then a list of keyword args to generate the body of the load dict. - ''' - payload = {'enc': enc} + """ + payload = {"enc": enc} load = {} for key in kwargs: load[key] = kwargs[key] - payload['load'] = load + payload["load"] = load return package(payload) class Serial(object): - ''' + """ Create a serialization object, this object manages all message serialization in Salt - ''' + """ + def __init__(self, opts): if isinstance(opts, dict): - self.serial = opts.get('serial', 'msgpack') + self.serial = opts.get("serial", "msgpack") elif isinstance(opts, six.string_types): self.serial = opts else: - self.serial = 'msgpack' + self.serial = "msgpack" def loads(self, msg, encoding=None, raw=False): - ''' + """ Run the correct loads serialization format :param encoding: Useful for Python 3 support. If the msgpack data @@ -89,17 +93,17 @@ class Serial(object): been lost in this case) to what the encoding is set as. In this case, it will fail if any of the contents cannot be converted. - ''' + """ try: + def ext_type_decoder(code, data): if code == 78: data = salt.utils.stringutils.to_unicode(data) - return datetime.datetime.strptime(data, '%Y%m%dT%H:%M:%S.%f') + return datetime.datetime.strptime(data, "%Y%m%dT%H:%M:%S.%f") return data gc.disable() # performance optimization for msgpack - loads_kwargs = {'use_list': True, - 'ext_hook': ext_type_decoder} + loads_kwargs = {"use_list": True, "ext_hook": ext_type_decoder} if salt.utils.msgpack.version >= (0, 4, 0): # msgpack only supports 'encoding' starting in 0.4.0. # Due to this, if we don't need it, don't pass it at all so @@ -107,17 +111,17 @@ class Serial(object): # of msgpack. if salt.utils.msgpack.version >= (0, 5, 2): if encoding is None: - loads_kwargs['raw'] = True + loads_kwargs["raw"] = True else: - loads_kwargs['raw'] = False + loads_kwargs["raw"] = False else: - loads_kwargs['encoding'] = encoding + loads_kwargs["encoding"] = encoding try: ret = salt.utils.msgpack.unpackb(msg, **loads_kwargs) except UnicodeDecodeError: # msg contains binary data - loads_kwargs.pop('raw', None) - loads_kwargs.pop('encoding', None) + loads_kwargs.pop("raw", None) + loads_kwargs.pop("encoding", None) ret = salt.utils.msgpack.loads(msg, **loads_kwargs) else: ret = salt.utils.msgpack.loads(msg, **loads_kwargs) @@ -125,17 +129,17 @@ class Serial(object): ret = salt.transport.frame.decode_embedded_strs(ret) except Exception as exc: # pylint: disable=broad-except log.critical( - 'Could not deserialize msgpack message. This often happens ' - 'when trying to read a file not in binary mode. ' - 'To see message payload, enable debug logging and retry. ' - 'Exception: %s', exc + "Could not deserialize msgpack message. This often happens " + "when trying to read a file not in binary mode. " + "To see message payload, enable debug logging and retry. " + "Exception: %s", + exc, ) - log.debug('Msgpack deserialization failure on message: %s', msg) + log.debug("Msgpack deserialization failure on message: %s", msg) gc.collect() raise six.raise_from( SaltDeserializationError( - 'Could not deserialize msgpack message.' - ' See log for more info.' + "Could not deserialize msgpack message." " See log for more info." ), exc, ) @@ -144,19 +148,19 @@ class Serial(object): return ret def load(self, fn_): - ''' + """ Run the correct serialization to load a file - ''' + """ data = fn_.read() fn_.close() if data: if six.PY3: - return self.loads(data, encoding='utf-8') + return self.loads(data, encoding="utf-8") else: return self.loads(data) def dumps(self, msg, use_bin_type=False): - ''' + """ Run the correct dumps serialization format :param use_bin_type: Useful for Python 3 support. Tells msgpack to @@ -164,7 +168,8 @@ class Serial(object): by encoding them differently. Since this changes the wire protocol, this option should not be used outside of IPC. - ''' + """ + def ext_type_encoder(obj): if isinstance(obj, six.integer_types): # msgpack can't handle the very long Python longs for jids @@ -174,8 +179,10 @@ class Serial(object): # msgpack doesn't support datetime.datetime and datetime.date datatypes. # So here we have converted these types to custom datatype # This is msgpack Extended types numbered 78 - return salt.utils.msgpack.ExtType(78, salt.utils.stringutils.to_bytes( - obj.strftime('%Y%m%dT%H:%M:%S.%f'))) + return salt.utils.msgpack.ExtType( + 78, + salt.utils.stringutils.to_bytes(obj.strftime("%Y%m%dT%H:%M:%S.%f")), + ) # The same for immutable types elif isinstance(obj, immutabletypes.ImmutableDict): return dict(obj) @@ -190,7 +197,9 @@ class Serial(object): return obj try: - return salt.utils.msgpack.packb(msg, default=ext_type_encoder, use_bin_type=use_bin_type) + return salt.utils.msgpack.packb( + msg, default=ext_type_encoder, use_bin_type=use_bin_type + ) except (OverflowError, salt.utils.msgpack.exceptions.PackValueError): # msgpack<=0.4.6 don't call ext encoder on very long integers raising the error instead. # Convert any very long longs to strings and call dumps again. @@ -198,7 +207,9 @@ class Serial(object): # Make sure we catch recursion here. objid = id(obj) if objid in context: - return '<Recursion on {} with id={}>'.format(type(obj).__name__, id(obj)) + return "<Recursion on {} with id={}>".format( + type(obj).__name__, id(obj) + ) context.add(objid) if isinstance(obj, dict): @@ -218,12 +229,14 @@ class Serial(object): return obj msg = verylong_encoder(msg, set()) - return salt.utils.msgpack.packb(msg, default=ext_type_encoder, use_bin_type=use_bin_type) + return salt.utils.msgpack.packb( + msg, default=ext_type_encoder, use_bin_type=use_bin_type + ) def dump(self, msg, fn_): - ''' + """ Serialize the correct data into the named file object - ''' + """ if six.PY2: fn_.write(self.dumps(msg)) else: @@ -235,10 +248,11 @@ class Serial(object): class SREQ(object): - ''' + """ Create a generic interface to wrap salt zeromq req calls. - ''' - def __init__(self, master, id_='', serial='msgpack', linger=0, opts=None): + """ + + def __init__(self, master, id_="", serial="msgpack", linger=0, opts=None): self.master = master self.id_ = id_ self.serial = Serial(serial) @@ -249,23 +263,21 @@ class SREQ(object): @property def socket(self): - ''' + """ Lazily create the socket. - ''' - if not hasattr(self, '_socket'): + """ + if not hasattr(self, "_socket"): # create a new one self._socket = self.context.socket(zmq.REQ) - if hasattr(zmq, 'RECONNECT_IVL_MAX'): - self._socket.setsockopt( - zmq.RECONNECT_IVL_MAX, 5000 - ) + if hasattr(zmq, "RECONNECT_IVL_MAX"): + self._socket.setsockopt(zmq.RECONNECT_IVL_MAX, 5000) self._set_tcp_keepalive() - if self.master.startswith('tcp://['): + if self.master.startswith("tcp://["): # Hint PF type if bracket enclosed IPv6 address - if hasattr(zmq, 'IPV6'): + if hasattr(zmq, "IPV6"): self._socket.setsockopt(zmq.IPV6, 1) - elif hasattr(zmq, 'IPV4ONLY'): + elif hasattr(zmq, "IPV4ONLY"): self._socket.setsockopt(zmq.IPV4ONLY, 0) self._socket.linger = self.linger if self.id_: @@ -274,46 +286,44 @@ class SREQ(object): return self._socket def _set_tcp_keepalive(self): - if hasattr(zmq, 'TCP_KEEPALIVE') and self.opts: - if 'tcp_keepalive' in self.opts: + if hasattr(zmq, "TCP_KEEPALIVE") and self.opts: + if "tcp_keepalive" in self.opts: + self._socket.setsockopt(zmq.TCP_KEEPALIVE, self.opts["tcp_keepalive"]) + if "tcp_keepalive_idle" in self.opts: self._socket.setsockopt( - zmq.TCP_KEEPALIVE, self.opts['tcp_keepalive'] + zmq.TCP_KEEPALIVE_IDLE, self.opts["tcp_keepalive_idle"] ) - if 'tcp_keepalive_idle' in self.opts: + if "tcp_keepalive_cnt" in self.opts: self._socket.setsockopt( - zmq.TCP_KEEPALIVE_IDLE, self.opts['tcp_keepalive_idle'] + zmq.TCP_KEEPALIVE_CNT, self.opts["tcp_keepalive_cnt"] ) - if 'tcp_keepalive_cnt' in self.opts: + if "tcp_keepalive_intvl" in self.opts: self._socket.setsockopt( - zmq.TCP_KEEPALIVE_CNT, self.opts['tcp_keepalive_cnt'] - ) - if 'tcp_keepalive_intvl' in self.opts: - self._socket.setsockopt( - zmq.TCP_KEEPALIVE_INTVL, self.opts['tcp_keepalive_intvl'] + zmq.TCP_KEEPALIVE_INTVL, self.opts["tcp_keepalive_intvl"] ) def clear_socket(self): - ''' + """ delete socket if you have it - ''' - if hasattr(self, '_socket'): + """ + if hasattr(self, "_socket"): if isinstance(self.poller.sockets, dict): sockets = list(self.poller.sockets.keys()) for socket in sockets: - log.trace('Unregistering socket: %s', socket) + log.trace("Unregistering socket: %s", socket) self.poller.unregister(socket) else: for socket in self.poller.sockets: - log.trace('Unregistering socket: %s', socket) + log.trace("Unregistering socket: %s", socket) self.poller.unregister(socket[0]) del self._socket def send(self, enc, load, tries=1, timeout=60): - ''' + """ Takes two arguments, the encryption type and the base payload - ''' - payload = {'enc': enc} - payload['load'] = load + """ + payload = {"enc": enc} + payload["load"] = load pkg = self.serial.dumps(payload) self.socket.send(pkg) self.poller.register(self.socket, zmq.POLLIN) @@ -325,23 +335,25 @@ class SREQ(object): break if tries > 1: log.info( - 'SaltReqTimeoutError: after %s seconds. (Try %s of %s)', - timeout, tried, tries + "SaltReqTimeoutError: after %s seconds. (Try %s of %s)", + timeout, + tried, + tries, ) if tried >= tries: self.clear_socket() raise SaltReqTimeoutError( - 'SaltReqTimeoutError: after {0} seconds, ran {1} ' - 'tries'.format(timeout * tried, tried) + "SaltReqTimeoutError: after {0} seconds, ran {1} " + "tries".format(timeout * tried, tried) ) return self.serial.loads(self.socket.recv()) def send_auto(self, payload, tries=1, timeout=60): - ''' + """ Detect the encryption type based on the payload - ''' - enc = payload.get('enc', 'clear') - load = payload.get('load', {}) + """ + enc = payload.get("enc", "clear") + load = payload.get("load", {}) return self.send(enc, load, tries, timeout) def destroy(self): @@ -367,4 +379,5 @@ class SREQ(object): # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 diff --git a/salt/pillar/__init__.py b/salt/pillar/__init__.py index 6b36cda708c..c411b66d539 100644 --- a/salt/pillar/__init__.py +++ b/salt/pillar/__init__.py @@ -1,23 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" Render the pillar data -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import collections import copy import fnmatch -import os -import collections +import inspect import logging -import salt.ext.tornado.gen +import os import sys import traceback -import inspect + +import salt.ext.tornado.gen +import salt.fileclient # Import salt libs import salt.loader -import salt.fileclient import salt.minion import salt.transport.client import salt.utils.args @@ -27,81 +29,121 @@ import salt.utils.data import salt.utils.dictupdate import salt.utils.url from salt.exceptions import SaltClientError + +# Import 3rd-party libs +from salt.ext import six from salt.template import compile_template -from salt.utils.odict import OrderedDict -from salt.version import __version__ + # Even though dictupdate is imported, invoking salt.utils.dictupdate.merge here # causes an UnboundLocalError. This should be investigated and fixed, but until # then, leave the import directly below this comment intact. from salt.utils.dictupdate import merge - -# Import 3rd-party libs -from salt.ext import six +from salt.utils.odict import OrderedDict +from salt.version import __version__ log = logging.getLogger(__name__) -def get_pillar(opts, grains, minion_id, saltenv=None, ext=None, funcs=None, - pillar_override=None, pillarenv=None, extra_minion_data=None): - ''' +def get_pillar( + opts, + grains, + minion_id, + saltenv=None, + ext=None, + funcs=None, + pillar_override=None, + pillarenv=None, + extra_minion_data=None, +): + """ Return the correct pillar driver based on the file_client option - ''' - file_client = opts['file_client'] - if opts.get('master_type') == 'disable' and file_client == 'remote': - file_client = 'local' - ptype = { - 'remote': RemotePillar, - 'local': Pillar - }.get(file_client, Pillar) + """ + file_client = opts["file_client"] + if opts.get("master_type") == "disable" and file_client == "remote": + file_client = "local" + ptype = {"remote": RemotePillar, "local": Pillar}.get(file_client, Pillar) # If local pillar and we're caching, run through the cache system first - log.debug('Determining pillar cache') - if opts['pillar_cache']: - log.info('Compiling pillar from cache') - log.debug('get_pillar using pillar cache with ext: %s', ext) - return PillarCache(opts, grains, minion_id, saltenv, ext=ext, functions=funcs, - pillar_override=pillar_override, pillarenv=pillarenv) - return ptype(opts, grains, minion_id, saltenv, ext, functions=funcs, - pillar_override=pillar_override, pillarenv=pillarenv, - extra_minion_data=extra_minion_data) + log.debug("Determining pillar cache") + if opts["pillar_cache"]: + log.info("Compiling pillar from cache") + log.debug("get_pillar using pillar cache with ext: %s", ext) + return PillarCache( + opts, + grains, + minion_id, + saltenv, + ext=ext, + functions=funcs, + pillar_override=pillar_override, + pillarenv=pillarenv, + ) + return ptype( + opts, + grains, + minion_id, + saltenv, + ext, + functions=funcs, + pillar_override=pillar_override, + pillarenv=pillarenv, + extra_minion_data=extra_minion_data, + ) # TODO: migrate everyone to this one! -def get_async_pillar(opts, grains, minion_id, saltenv=None, ext=None, funcs=None, - pillar_override=None, pillarenv=None, - extra_minion_data=None): - ''' +def get_async_pillar( + opts, + grains, + minion_id, + saltenv=None, + ext=None, + funcs=None, + pillar_override=None, + pillarenv=None, + extra_minion_data=None, +): + """ Return the correct pillar driver based on the file_client option - ''' - file_client = opts['file_client'] - if opts.get('master_type') == 'disable' and file_client == 'remote': - file_client = 'local' - ptype = { - 'remote': AsyncRemotePillar, - 'local': AsyncPillar, - }.get(file_client, AsyncPillar) - return ptype(opts, grains, minion_id, saltenv, ext, functions=funcs, - pillar_override=pillar_override, pillarenv=pillarenv, - extra_minion_data=extra_minion_data) + """ + file_client = opts["file_client"] + if opts.get("master_type") == "disable" and file_client == "remote": + file_client = "local" + ptype = {"remote": AsyncRemotePillar, "local": AsyncPillar}.get( + file_client, AsyncPillar + ) + return ptype( + opts, + grains, + minion_id, + saltenv, + ext, + functions=funcs, + pillar_override=pillar_override, + pillarenv=pillarenv, + extra_minion_data=extra_minion_data, + ) class RemotePillarMixin(object): - ''' + """ Common remote pillar functionality - ''' + """ + def get_ext_pillar_extra_minion_data(self, opts): - ''' + """ Returns the extra data from the minion's opts dict (the config file). This data will be passed to external pillar functions. - ''' + """ + def get_subconfig(opts_key): - ''' + """ Returns a dict containing the opts key subtree, while maintaining the opts structure - ''' + """ ret_dict = aux_dict = {} config_val = opts - subkeys = opts_key.split(':') + subkeys = opts_key.split(":") # Build an empty dict with the opts path for subkey in subkeys[:-1]: aux_dict[subkey] = {} @@ -116,75 +158,91 @@ class RemotePillarMixin(object): return ret_dict extra_data = {} - if 'pass_to_ext_pillars' in opts: - if not isinstance(opts['pass_to_ext_pillars'], list): - log.exception('\'pass_to_ext_pillars\' config is malformed.') - raise SaltClientError('\'pass_to_ext_pillars\' config is ' - 'malformed.') - for key in opts['pass_to_ext_pillars']: - salt.utils.dictupdate.update(extra_data, - get_subconfig(key), - recursive_update=True, - merge_lists=True) - log.trace('ext_pillar_extra_data = %s', extra_data) + if "pass_to_ext_pillars" in opts: + if not isinstance(opts["pass_to_ext_pillars"], list): + log.exception("'pass_to_ext_pillars' config is malformed.") + raise SaltClientError("'pass_to_ext_pillars' config is " "malformed.") + for key in opts["pass_to_ext_pillars"]: + salt.utils.dictupdate.update( + extra_data, + get_subconfig(key), + recursive_update=True, + merge_lists=True, + ) + log.trace("ext_pillar_extra_data = %s", extra_data) return extra_data class AsyncRemotePillar(RemotePillarMixin): - ''' + """ Get the pillar from the master - ''' - def __init__(self, opts, grains, minion_id, saltenv, ext=None, functions=None, - pillar_override=None, pillarenv=None, extra_minion_data=None): + """ + + def __init__( + self, + opts, + grains, + minion_id, + saltenv, + ext=None, + functions=None, + pillar_override=None, + pillarenv=None, + extra_minion_data=None, + ): self.opts = opts - self.opts['saltenv'] = saltenv + self.opts["saltenv"] = saltenv self.ext = ext self.grains = grains self.minion_id = minion_id self.channel = salt.transport.client.AsyncReqChannel.factory(opts) if pillarenv is not None: - self.opts['pillarenv'] = pillarenv + self.opts["pillarenv"] = pillarenv self.pillar_override = pillar_override or {} if not isinstance(self.pillar_override, dict): self.pillar_override = {} - log.error('Pillar data must be a dictionary') + log.error("Pillar data must be a dictionary") self.extra_minion_data = extra_minion_data or {} if not isinstance(self.extra_minion_data, dict): self.extra_minion_data = {} - log.error('Extra minion data must be a dictionary') - salt.utils.dictupdate.update(self.extra_minion_data, - self.get_ext_pillar_extra_minion_data(opts), - recursive_update=True, - merge_lists=True) + log.error("Extra minion data must be a dictionary") + salt.utils.dictupdate.update( + self.extra_minion_data, + self.get_ext_pillar_extra_minion_data(opts), + recursive_update=True, + merge_lists=True, + ) self._closing = False @salt.ext.tornado.gen.coroutine def compile_pillar(self): - ''' + """ Return a future which will contain the pillar data from the master - ''' - load = {'id': self.minion_id, - 'grains': self.grains, - 'saltenv': self.opts['saltenv'], - 'pillarenv': self.opts['pillarenv'], - 'pillar_override': self.pillar_override, - 'extra_minion_data': self.extra_minion_data, - 'ver': '2', - 'cmd': '_pillar'} + """ + load = { + "id": self.minion_id, + "grains": self.grains, + "saltenv": self.opts["saltenv"], + "pillarenv": self.opts["pillarenv"], + "pillar_override": self.pillar_override, + "extra_minion_data": self.extra_minion_data, + "ver": "2", + "cmd": "_pillar", + } if self.ext: - load['ext'] = self.ext + load["ext"] = self.ext try: ret_pillar = yield self.channel.crypted_transfer_decode_dictentry( - load, - dictkey='pillar', + load, dictkey="pillar", ) except Exception: # pylint: disable=broad-except - log.exception('Exception getting pillar:') - raise SaltClientError('Exception getting pillar.') + log.exception("Exception getting pillar:") + raise SaltClientError("Exception getting pillar.") if not isinstance(ret_pillar, dict): - msg = ('Got a bad pillar from master, type {0}, expecting dict: ' - '{1}').format(type(ret_pillar).__name__, ret_pillar) + msg = ( + "Got a bad pillar from master, type {0}, expecting dict: " "{1}" + ).format(type(ret_pillar).__name__, ret_pillar) log.error(msg) # raise an exception! Pillar isn't empty, we can't sync it! raise SaltClientError(msg) @@ -200,65 +258,82 @@ class AsyncRemotePillar(RemotePillarMixin): # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 class RemotePillar(RemotePillarMixin): - ''' + """ Get the pillar from the master - ''' - def __init__(self, opts, grains, minion_id, saltenv, ext=None, functions=None, - pillar_override=None, pillarenv=None, extra_minion_data=None): + """ + + def __init__( + self, + opts, + grains, + minion_id, + saltenv, + ext=None, + functions=None, + pillar_override=None, + pillarenv=None, + extra_minion_data=None, + ): self.opts = opts - self.opts['saltenv'] = saltenv + self.opts["saltenv"] = saltenv self.ext = ext self.grains = grains self.minion_id = minion_id self.channel = salt.transport.client.ReqChannel.factory(opts) if pillarenv is not None: - self.opts['pillarenv'] = pillarenv + self.opts["pillarenv"] = pillarenv self.pillar_override = pillar_override or {} if not isinstance(self.pillar_override, dict): self.pillar_override = {} - log.error('Pillar data must be a dictionary') + log.error("Pillar data must be a dictionary") self.extra_minion_data = extra_minion_data or {} if not isinstance(self.extra_minion_data, dict): self.extra_minion_data = {} - log.error('Extra minion data must be a dictionary') - salt.utils.dictupdate.update(self.extra_minion_data, - self.get_ext_pillar_extra_minion_data(opts), - recursive_update=True, - merge_lists=True) + log.error("Extra minion data must be a dictionary") + salt.utils.dictupdate.update( + self.extra_minion_data, + self.get_ext_pillar_extra_minion_data(opts), + recursive_update=True, + merge_lists=True, + ) self._closing = False def compile_pillar(self): - ''' + """ Return the pillar data from the master - ''' - load = {'id': self.minion_id, - 'grains': self.grains, - 'saltenv': self.opts['saltenv'], - 'pillarenv': self.opts['pillarenv'], - 'pillar_override': self.pillar_override, - 'extra_minion_data': self.extra_minion_data, - 'ver': '2', - 'cmd': '_pillar'} + """ + load = { + "id": self.minion_id, + "grains": self.grains, + "saltenv": self.opts["saltenv"], + "pillarenv": self.opts["pillarenv"], + "pillar_override": self.pillar_override, + "extra_minion_data": self.extra_minion_data, + "ver": "2", + "cmd": "_pillar", + } if self.ext: - load['ext'] = self.ext - ret_pillar = self.channel.crypted_transfer_decode_dictentry(load, - dictkey='pillar', - ) + load["ext"] = self.ext + ret_pillar = self.channel.crypted_transfer_decode_dictentry( + load, dictkey="pillar", + ) if not isinstance(ret_pillar, dict): log.error( - 'Got a bad pillar from master, type %s, expecting dict: %s', - type(ret_pillar).__name__, ret_pillar + "Got a bad pillar from master, type %s, expecting dict: %s", + type(ret_pillar).__name__, + ret_pillar, ) return {} return ret_pillar def destroy(self): - if hasattr(self, '_closing') and self._closing: + if hasattr(self, "_closing") and self._closing: return self._closing = True @@ -267,11 +342,12 @@ class RemotePillar(RemotePillarMixin): # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 class PillarCache(object): - ''' + """ Return a cached pillar if it exists, otherwise cache it. Pillar caches are structed in two diminensions: minion_id with a dict of @@ -283,10 +359,21 @@ class PillarCache(object): {'minion_1': {'base': {'pilar_key_1' 'pillar_val_1'} } - ''' + """ + # TODO ABC? - def __init__(self, opts, grains, minion_id, saltenv, ext=None, functions=None, - pillar_override=None, pillarenv=None, extra_minion_data=None): + def __init__( + self, + opts, + grains, + minion_id, + saltenv, + ext=None, + functions=None, + pillar_override=None, + pillarenv=None, + extra_minion_data=None, + ): # Yes, we need all of these because we need to route to the Pillar object # if we have no cache. This is another refactor target. @@ -300,214 +387,240 @@ class PillarCache(object): self.pillarenv = pillarenv if saltenv is None: - self.saltenv = 'base' + self.saltenv = "base" else: self.saltenv = saltenv # Determine caching backend self.cache = salt.utils.cache.CacheFactory.factory( - self.opts['pillar_cache_backend'], - self.opts['pillar_cache_ttl'], - minion_cache_path=self._minion_cache_path(minion_id)) + self.opts["pillar_cache_backend"], + self.opts["pillar_cache_ttl"], + minion_cache_path=self._minion_cache_path(minion_id), + ) def _minion_cache_path(self, minion_id): - ''' + """ Return the path to the cache file for the minion. Used only for disk-based backends - ''' - return os.path.join(self.opts['cachedir'], 'pillar_cache', minion_id) + """ + return os.path.join(self.opts["cachedir"], "pillar_cache", minion_id) def fetch_pillar(self): - ''' + """ In the event of a cache miss, we need to incur the overhead of caching a new pillar. - ''' - log.debug('Pillar cache getting external pillar with ext: %s', self.ext) - fresh_pillar = Pillar(self.opts, - self.grains, - self.minion_id, - self.saltenv, - ext=self.ext, - functions=self.functions, - pillar_override=self.pillar_override, - pillarenv=self.pillarenv) + """ + log.debug("Pillar cache getting external pillar with ext: %s", self.ext) + fresh_pillar = Pillar( + self.opts, + self.grains, + self.minion_id, + self.saltenv, + ext=self.ext, + functions=self.functions, + pillar_override=self.pillar_override, + pillarenv=self.pillarenv, + ) return fresh_pillar.compile_pillar() def compile_pillar(self, *args, **kwargs): # Will likely just be pillar_dirs - log.debug('Scanning pillar cache for information about minion %s and pillarenv %s', self.minion_id, self.pillarenv) - log.debug('Scanning cache: %s', self.cache._dict) + log.debug( + "Scanning pillar cache for information about minion %s and pillarenv %s", + self.minion_id, + self.pillarenv, + ) + log.debug("Scanning cache: %s", self.cache._dict) # Check the cache! if self.minion_id in self.cache: # Keyed by minion_id # TODO Compare grains, etc? if self.pillarenv in self.cache[self.minion_id]: # We have a cache hit! Send it back. - log.debug('Pillar cache hit for minion %s and pillarenv %s', self.minion_id, self.pillarenv) + log.debug( + "Pillar cache hit for minion %s and pillarenv %s", + self.minion_id, + self.pillarenv, + ) return self.cache[self.minion_id][self.pillarenv] else: # We found the minion but not the env. Store it. fresh_pillar = self.fetch_pillar() self.cache[self.minion_id][self.pillarenv] = fresh_pillar - log.debug('Pillar cache miss for pillarenv %s for minion %s', self.pillarenv, self.minion_id) + log.debug( + "Pillar cache miss for pillarenv %s for minion %s", + self.pillarenv, + self.minion_id, + ) return fresh_pillar else: # We haven't seen this minion yet in the cache. Store it. fresh_pillar = self.fetch_pillar() self.cache[self.minion_id] = {self.pillarenv: fresh_pillar} - log.debug('Pillar cache miss for minion %s', self.minion_id) - log.debug('Current pillar cache: %s', self.cache._dict) # FIXME hack! + log.debug("Pillar cache miss for minion %s", self.minion_id) + log.debug("Current pillar cache: %s", self.cache._dict) # FIXME hack! return fresh_pillar class Pillar(object): - ''' + """ Read over the pillar top files and render the pillar data - ''' - def __init__(self, opts, grains, minion_id, saltenv, ext=None, functions=None, - pillar_override=None, pillarenv=None, extra_minion_data=None): + """ + + def __init__( + self, + opts, + grains, + minion_id, + saltenv, + ext=None, + functions=None, + pillar_override=None, + pillarenv=None, + extra_minion_data=None, + ): self.minion_id = minion_id self.ext = ext if pillarenv is None: - if opts.get('pillarenv_from_saltenv', False): - opts['pillarenv'] = saltenv + if opts.get("pillarenv_from_saltenv", False): + opts["pillarenv"] = saltenv # use the local file client self.opts = self.__gen_opts(opts, grains, saltenv=saltenv, pillarenv=pillarenv) self.saltenv = saltenv self.client = salt.fileclient.get_file_client(self.opts, True) self.avail = self.__gather_avail() - if opts.get('file_client', '') == 'local': - opts['grains'] = grains + if opts.get("file_client", "") == "local": + opts["grains"] = grains # if we didn't pass in functions, lets load them if functions is None: utils = salt.loader.utils(opts) - if opts.get('file_client', '') == 'local': + if opts.get("file_client", "") == "local": self.functions = salt.loader.minion_mods(opts, utils=utils) else: self.functions = salt.loader.minion_mods(self.opts, utils=utils) else: self.functions = functions - self.opts['minion_id'] = minion_id + self.opts["minion_id"] = minion_id self.matchers = salt.loader.matchers(self.opts) self.rend = salt.loader.render(self.opts, self.functions) ext_pillar_opts = copy.deepcopy(self.opts) # Keep the incoming opts ID intact, ie, the master id - if 'id' in opts: - ext_pillar_opts['id'] = opts['id'] - self.merge_strategy = 'smart' - if opts.get('pillar_source_merging_strategy'): - self.merge_strategy = opts['pillar_source_merging_strategy'] + if "id" in opts: + ext_pillar_opts["id"] = opts["id"] + self.merge_strategy = "smart" + if opts.get("pillar_source_merging_strategy"): + self.merge_strategy = opts["pillar_source_merging_strategy"] self.ext_pillars = salt.loader.pillars(ext_pillar_opts, self.functions) self.ignored_pillars = {} self.pillar_override = pillar_override or {} if not isinstance(self.pillar_override, dict): self.pillar_override = {} - log.error('Pillar data must be a dictionary') + log.error("Pillar data must be a dictionary") self.extra_minion_data = extra_minion_data or {} if not isinstance(self.extra_minion_data, dict): self.extra_minion_data = {} - log.error('Extra minion data must be a dictionary') + log.error("Extra minion data must be a dictionary") self._closing = False def __valid_on_demand_ext_pillar(self, opts): - ''' + """ Check to see if the on demand external pillar is allowed - ''' + """ if not isinstance(self.ext, dict): - log.error( - 'On-demand pillar %s is not formatted as a dictionary', - self.ext - ) + log.error("On-demand pillar %s is not formatted as a dictionary", self.ext) return False - on_demand = opts.get('on_demand_ext_pillar', []) + on_demand = opts.get("on_demand_ext_pillar", []) try: invalid_on_demand = set([x for x in self.ext if x not in on_demand]) except TypeError: # Prevent traceback when on_demand_ext_pillar option is malformed log.error( - 'The \'on_demand_ext_pillar\' configuration option is ' - 'malformed, it should be a list of ext_pillar module names' + "The 'on_demand_ext_pillar' configuration option is " + "malformed, it should be a list of ext_pillar module names" ) return False if invalid_on_demand: log.error( - 'The following ext_pillar modules are not allowed for ' - 'on-demand pillar data: %s. Valid on-demand ext_pillar ' - 'modules are: %s. The valid modules can be adjusted by ' - 'setting the \'on_demand_ext_pillar\' config option.', - ', '.join(sorted(invalid_on_demand)), - ', '.join(on_demand), + "The following ext_pillar modules are not allowed for " + "on-demand pillar data: %s. Valid on-demand ext_pillar " + "modules are: %s. The valid modules can be adjusted by " + "setting the 'on_demand_ext_pillar' config option.", + ", ".join(sorted(invalid_on_demand)), + ", ".join(on_demand), ) return False return True def __gather_avail(self): - ''' + """ Gather the lists of available sls data from the master - ''' + """ avail = {} for saltenv in self._get_envs(): avail[saltenv] = self.client.list_states(saltenv) return avail def __gen_opts(self, opts_in, grains, saltenv=None, ext=None, pillarenv=None): - ''' + """ The options need to be altered to conform to the file client - ''' + """ opts = copy.deepcopy(opts_in) - opts['file_client'] = 'local' + opts["file_client"] = "local" if not grains: - opts['grains'] = {} + opts["grains"] = {} else: - opts['grains'] = grains + opts["grains"] = grains # Allow minion/CLI saltenv/pillarenv to take precedence over master - opts['saltenv'] = saltenv \ - if saltenv is not None \ - else opts.get('saltenv') - opts['pillarenv'] = pillarenv \ - if pillarenv is not None \ - else opts.get('pillarenv') - opts['id'] = self.minion_id - if opts['state_top'].startswith('salt://'): - opts['state_top'] = opts['state_top'] - elif opts['state_top'].startswith('/'): - opts['state_top'] = salt.utils.url.create(opts['state_top'][1:]) + opts["saltenv"] = saltenv if saltenv is not None else opts.get("saltenv") + opts["pillarenv"] = ( + pillarenv if pillarenv is not None else opts.get("pillarenv") + ) + opts["id"] = self.minion_id + if opts["state_top"].startswith("salt://"): + opts["state_top"] = opts["state_top"] + elif opts["state_top"].startswith("/"): + opts["state_top"] = salt.utils.url.create(opts["state_top"][1:]) else: - opts['state_top'] = salt.utils.url.create(opts['state_top']) + opts["state_top"] = salt.utils.url.create(opts["state_top"]) if self.ext and self.__valid_on_demand_ext_pillar(opts): - if 'ext_pillar' in opts: - opts['ext_pillar'].append(self.ext) + if "ext_pillar" in opts: + opts["ext_pillar"].append(self.ext) else: - opts['ext_pillar'] = [self.ext] - if '__env__' in opts['pillar_roots']: - env = opts.get('pillarenv') or opts.get('saltenv') or 'base' - if env not in opts['pillar_roots']: - log.debug("pillar environment '%s' maps to __env__ pillar_roots directory", env) - opts['pillar_roots'][env] = opts['pillar_roots'].pop('__env__') + opts["ext_pillar"] = [self.ext] + if "__env__" in opts["pillar_roots"]: + env = opts.get("pillarenv") or opts.get("saltenv") or "base" + if env not in opts["pillar_roots"]: + log.debug( + "pillar environment '%s' maps to __env__ pillar_roots directory", + env, + ) + opts["pillar_roots"][env] = opts["pillar_roots"].pop("__env__") else: - log.debug("pillar_roots __env__ ignored (environment '%s' found in pillar_roots)", - env) - opts['pillar_roots'].pop('__env__') + log.debug( + "pillar_roots __env__ ignored (environment '%s' found in pillar_roots)", + env, + ) + opts["pillar_roots"].pop("__env__") return opts def _get_envs(self): - ''' + """ Pull the file server environments out of the master options - ''' - envs = set(['base']) - if 'pillar_roots' in self.opts: - envs.update(list(self.opts['pillar_roots'])) + """ + envs = set(["base"]) + if "pillar_roots" in self.opts: + envs.update(list(self.opts["pillar_roots"])) return envs def get_tops(self): - ''' + """ Gather the top files - ''' + """ tops = collections.defaultdict(list) include = collections.defaultdict(list) done = collections.defaultdict(list) @@ -515,48 +628,51 @@ class Pillar(object): # Gather initial top files try: saltenvs = set() - if self.opts['pillarenv']: + if self.opts["pillarenv"]: # If the specified pillarenv is not present in the available # pillar environments, do not cache the pillar top file. - if self.opts['pillarenv'] not in self.opts['pillar_roots']: + if self.opts["pillarenv"] not in self.opts["pillar_roots"]: log.debug( - 'pillarenv \'%s\' not found in the configured pillar ' - 'environments (%s)', - self.opts['pillarenv'], ', '.join(self.opts['pillar_roots']) + "pillarenv '%s' not found in the configured pillar " + "environments (%s)", + self.opts["pillarenv"], + ", ".join(self.opts["pillar_roots"]), ) else: - saltenvs.add(self.opts['pillarenv']) + saltenvs.add(self.opts["pillarenv"]) else: saltenvs = self._get_envs() - if self.opts.get('pillar_source_merging_strategy', None) == "none": - saltenvs &= set([self.saltenv or 'base']) + if self.opts.get("pillar_source_merging_strategy", None) == "none": + saltenvs &= set([self.saltenv or "base"]) for saltenv in saltenvs: - top = self.client.cache_file(self.opts['state_top'], saltenv) + top = self.client.cache_file(self.opts["state_top"], saltenv) if top: - tops[saltenv].append(compile_template( - top, - self.rend, - self.opts['renderer'], - self.opts['renderer_blacklist'], - self.opts['renderer_whitelist'], - saltenv=saltenv, - _pillar_rend=True, - )) + tops[saltenv].append( + compile_template( + top, + self.rend, + self.opts["renderer"], + self.opts["renderer_blacklist"], + self.opts["renderer_whitelist"], + saltenv=saltenv, + _pillar_rend=True, + ) + ) except Exception as exc: # pylint: disable=broad-except errors.append( - ('Rendering Primary Top file failed, render error:\n{0}' - .format(exc))) - log.exception('Pillar rendering failed for minion %s', self.minion_id) + ("Rendering Primary Top file failed, render error:\n{0}".format(exc)) + ) + log.exception("Pillar rendering failed for minion %s", self.minion_id) # Search initial top files for includes for saltenv, ctops in six.iteritems(tops): for ctop in ctops: - if 'include' not in ctop: + if "include" not in ctop: continue - for sls in ctop['include']: + for sls in ctop["include"]: include[saltenv].append(sls) - ctop.pop('include') + ctop.pop("include") # Go through the includes and pull out the extra tops and add them while include: pops = [] @@ -569,23 +685,22 @@ class Pillar(object): continue try: tops[saltenv].append( - compile_template( - self.client.get_state( - sls, - saltenv - ).get('dest', False), - self.rend, - self.opts['renderer'], - self.opts['renderer_blacklist'], - self.opts['renderer_whitelist'], - saltenv=saltenv, - _pillar_rend=True, - ) - ) + compile_template( + self.client.get_state(sls, saltenv).get("dest", False), + self.rend, + self.opts["renderer"], + self.opts["renderer_blacklist"], + self.opts["renderer_whitelist"], + saltenv=saltenv, + _pillar_rend=True, + ) + ) except Exception as exc: # pylint: disable=broad-except errors.append( - ('Rendering Top file {0} failed, render error' - ':\n{1}').format(sls, exc)) + ( + "Rendering Top file {0} failed, render error" ":\n{1}" + ).format(sls, exc) + ) done[saltenv].append(sls) for saltenv in pops: if saltenv in include: @@ -594,15 +709,15 @@ class Pillar(object): return tops, errors def merge_tops(self, tops): - ''' + """ Cleanly merge the top files - ''' + """ top = collections.defaultdict(OrderedDict) orders = collections.defaultdict(OrderedDict) for ctops in six.itervalues(tops): for ctop in ctops: for saltenv, targets in six.iteritems(ctop): - if saltenv == 'include': + if saltenv == "include": continue for tgt in targets: matches = [] @@ -611,17 +726,17 @@ class Pillar(object): ignore_missing = False for comp in ctop[saltenv][tgt]: if isinstance(comp, dict): - if 'match' in comp: + if "match" in comp: matches.append(comp) - if 'order' in comp: - order = comp['order'] + if "order" in comp: + order = comp["order"] if not isinstance(order, int): try: order = int(order) except ValueError: order = 0 orders[saltenv][tgt] = order - if comp.get('ignore_missing', False): + if comp.get("ignore_missing", False): ignore_missing = True if isinstance(comp, six.string_types): states[comp] = True @@ -634,33 +749,32 @@ class Pillar(object): return self.sort_top_targets(top, orders) def sort_top_targets(self, top, orders): - ''' + """ Returns the sorted high data from the merged top files - ''' + """ sorted_top = collections.defaultdict(OrderedDict) # pylint: disable=cell-var-from-loop for saltenv, targets in six.iteritems(top): - sorted_targets = sorted(targets, - key=lambda target: orders[saltenv][target]) + sorted_targets = sorted(targets, key=lambda target: orders[saltenv][target]) for target in sorted_targets: sorted_top[saltenv][target] = targets[target] # pylint: enable=cell-var-from-loop return sorted_top def get_top(self): - ''' + """ Returns the high data derived from the top file - ''' + """ tops, errors = self.get_tops() try: merged_tops = self.merge_tops(tops) except TypeError as err: merged_tops = OrderedDict() - errors.append('Error encountered while rendering pillar top file.') + errors.append("Error encountered while rendering pillar top file.") return merged_tops, errors def top_matches(self, top, reload=False): - ''' + """ Search through the top high data for matches and return the states that this minion needs to execute. @@ -669,89 +783,97 @@ class Pillar(object): reload Reload the matcher loader - ''' + """ matches = {} if reload: self.matchers = salt.loader.matchers(self.opts) for saltenv, body in six.iteritems(top): - if self.opts['pillarenv']: - if saltenv != self.opts['pillarenv']: + if self.opts["pillarenv"]: + if saltenv != self.opts["pillarenv"]: continue for match, data in six.iteritems(body): - if self.matchers['confirm_top.confirm_top']( - match, - data, - self.opts.get('nodegroups', {}), - ): + if self.matchers["confirm_top.confirm_top"]( + match, data, self.opts.get("nodegroups", {}), + ): if saltenv not in matches: matches[saltenv] = env_matches = [] else: env_matches = matches[saltenv] for item in data: - if isinstance(item, six.string_types) and item not in env_matches: + if ( + isinstance(item, six.string_types) + and item not in env_matches + ): env_matches.append(item) return matches def render_pstate(self, sls, saltenv, mods, defaults=None): - ''' + """ Collect a single pillar sls file and render it - ''' + """ if defaults is None: defaults = {} - err = '' + err = "" errors = [] - fn_ = self.client.get_state(sls, saltenv).get('dest', False) + fn_ = self.client.get_state(sls, saltenv).get("dest", False) if not fn_: if sls in self.ignored_pillars.get(saltenv, []): - log.debug('Skipping ignored and missing SLS \'%s\' in ' - 'environment \'%s\'', sls, saltenv) + log.debug( + "Skipping ignored and missing SLS '%s' in " "environment '%s'", + sls, + saltenv, + ) return None, mods, errors - elif self.opts['pillar_roots'].get(saltenv): - msg = ('Specified SLS \'{0}\' in environment \'{1}\' is not' - ' available on the salt master').format(sls, saltenv) + elif self.opts["pillar_roots"].get(saltenv): + msg = ( + "Specified SLS '{0}' in environment '{1}' is not" + " available on the salt master" + ).format(sls, saltenv) log.error(msg) errors.append(msg) else: - msg = ('Specified SLS \'{0}\' in environment \'{1}\' was not ' - 'found. '.format(sls, saltenv)) - if self.opts.get('__git_pillar', False) is True: + msg = ( + "Specified SLS '{0}' in environment '{1}' was not " + "found. ".format(sls, saltenv) + ) + if self.opts.get("__git_pillar", False) is True: msg += ( - 'This is likely caused by a git_pillar top file ' - 'containing an environment other than the one for the ' - 'branch in which it resides. Each git_pillar ' - 'branch/tag must have its own top file.' + "This is likely caused by a git_pillar top file " + "containing an environment other than the one for the " + "branch in which it resides. Each git_pillar " + "branch/tag must have its own top file." ) else: msg += ( - 'This could be because SLS \'{0}\' is in an ' - 'environment other than \'{1}\', but \'{1}\' is ' - 'included in that environment\'s Pillar top file. It ' - 'could also be due to environment \'{1}\' not being ' - 'defined in \'pillar_roots\'.'.format(sls, saltenv) + "This could be because SLS '{0}' is in an " + "environment other than '{1}', but '{1}' is " + "included in that environment's Pillar top file. It " + "could also be due to environment '{1}' not being " + "defined in 'pillar_roots'.".format(sls, saltenv) ) log.debug(msg) # return state, mods, errors return None, mods, errors state = None try: - state = compile_template(fn_, - self.rend, - self.opts['renderer'], - self.opts['renderer_blacklist'], - self.opts['renderer_whitelist'], - saltenv, - sls, - _pillar_rend=True, - **defaults) - except Exception as exc: # pylint: disable=broad-except - msg = 'Rendering SLS \'{0}\' failed, render error:\n{1}'.format( - sls, exc + state = compile_template( + fn_, + self.rend, + self.opts["renderer"], + self.opts["renderer_blacklist"], + self.opts["renderer_whitelist"], + saltenv, + sls, + _pillar_rend=True, + **defaults ) + except Exception as exc: # pylint: disable=broad-except + msg = "Rendering SLS '{0}' failed, render error:\n{1}".format(sls, exc) log.critical(msg, exc_info=True) - if self.opts.get('pillar_safe_render_error', True): + if self.opts.get("pillar_safe_render_error", True): errors.append( - 'Rendering SLS \'{0}\' failed. Please see master log for ' - 'details.'.format(sls) + "Rendering SLS '{0}' failed. Please see master log for " + "details.".format(sls) ) else: errors.append(msg) @@ -759,64 +881,70 @@ class Pillar(object): nstate = None if state: if not isinstance(state, dict): - msg = 'SLS \'{0}\' does not render to a dictionary'.format(sls) + msg = "SLS '{0}' does not render to a dictionary".format(sls) log.error(msg) errors.append(msg) else: - if 'include' in state: - if not isinstance(state['include'], list): - msg = ('Include Declaration in SLS \'{0}\' is not ' - 'formed as a list'.format(sls)) + if "include" in state: + if not isinstance(state["include"], list): + msg = ( + "Include Declaration in SLS '{0}' is not " + "formed as a list".format(sls) + ) log.error(msg) errors.append(msg) else: # render included state(s) include_states = [] - for sub_sls in state.pop('include'): + for sub_sls in state.pop("include"): if isinstance(sub_sls, dict): sub_sls, v = next(six.iteritems(sub_sls)) - defaults = v.get('defaults', {}) - key = v.get('key', None) + defaults = v.get("defaults", {}) + key = v.get("key", None) else: key = None try: matched_pstates = fnmatch.filter( self.avail[saltenv], - sub_sls.lstrip('.').replace('/', '.'), + sub_sls.lstrip(".").replace("/", "."), ) except KeyError: errors.extend( - ['No matching pillar environment for environment ' - '\'{0}\' found'.format(saltenv)] + [ + "No matching pillar environment for environment " + "'{0}' found".format(saltenv) + ] ) matched_pstates = [sub_sls] for m_sub_sls in matched_pstates: if m_sub_sls not in mods: nstate, mods, err = self.render_pstate( - m_sub_sls, - saltenv, - mods, - defaults - ) + m_sub_sls, saltenv, mods, defaults + ) if nstate: if key: # If key is x:y, convert it to {x: {y: nstate}} - for key_fragment in reversed(key.split(":")): - nstate = { - key_fragment: nstate - } - if not self.opts.get('pillar_includes_override_sls', False): + for key_fragment in reversed( + key.split(":") + ): + nstate = {key_fragment: nstate} + if not self.opts.get( + "pillar_includes_override_sls", False + ): include_states.append(nstate) else: state = merge( state, nstate, self.merge_strategy, - self.opts.get('renderer', 'yaml'), - self.opts.get('pillar_merge_lists', False)) + self.opts.get("renderer", "yaml"), + self.opts.get( + "pillar_merge_lists", False + ), + ) if err: errors += err - if not self.opts.get('pillar_includes_override_sls', False): + if not self.opts.get("pillar_includes_override_sls", False): # merge included state(s) with the current state # merged last to ensure that its values are # authoritative. @@ -830,15 +958,16 @@ class Pillar(object): state, s, self.merge_strategy, - self.opts.get('renderer', 'yaml'), - self.opts.get('pillar_merge_lists', False)) + self.opts.get("renderer", "yaml"), + self.opts.get("pillar_merge_lists", False), + ) return state, mods, errors def render_pillar(self, matches, errors=None): - ''' + """ Extract the sls pillar files from the matches and render them into the pillar - ''' + """ pillar = copy.copy(self.pillar_override) if errors is None: errors = [] @@ -851,8 +980,10 @@ class Pillar(object): matched_pstates = fnmatch.filter(self.avail[saltenv], sls_match) except KeyError: errors.extend( - ['No matching pillar environment for environment ' - '\'{0}\' found'.format(saltenv)] + [ + "No matching pillar environment for environment " + "'{0}' found".format(saltenv) + ] ) if matched_pstates: pstatefiles.extend(matched_pstates) @@ -868,87 +999,92 @@ class Pillar(object): if pstate is not None: if not isinstance(pstate, dict): log.error( - 'The rendered pillar sls file, \'%s\' state did ' - 'not return the expected data format. This is ' - 'a sign of a malformed pillar sls file. Returned ' - 'errors: %s', + "The rendered pillar sls file, '%s' state did " + "not return the expected data format. This is " + "a sign of a malformed pillar sls file. Returned " + "errors: %s", sls, - ', '.join(["'{0}'".format(e) for e in errors]) + ", ".join(["'{0}'".format(e) for e in errors]), ) continue pillar = merge( pillar, pstate, self.merge_strategy, - self.opts.get('renderer', 'yaml'), - self.opts.get('pillar_merge_lists', False)) + self.opts.get("renderer", "yaml"), + self.opts.get("pillar_merge_lists", False), + ) return pillar, errors def _external_pillar_data(self, pillar, val, key): - ''' + """ Builds actual pillar data structure and updates the ``pillar`` variable - ''' + """ ext = None args = salt.utils.args.get_function_argspec(self.ext_pillars[key]).args if isinstance(val, dict): - if ('extra_minion_data' in args) and self.extra_minion_data: + if ("extra_minion_data" in args) and self.extra_minion_data: ext = self.ext_pillars[key]( - self.minion_id, pillar, - extra_minion_data=self.extra_minion_data, **val) + self.minion_id, + pillar, + extra_minion_data=self.extra_minion_data, + **val + ) else: ext = self.ext_pillars[key](self.minion_id, pillar, **val) elif isinstance(val, list): - if ('extra_minion_data' in args) and self.extra_minion_data: + if ("extra_minion_data" in args) and self.extra_minion_data: ext = self.ext_pillars[key]( - self.minion_id, pillar, *val, - extra_minion_data=self.extra_minion_data) + self.minion_id, + pillar, + *val, + extra_minion_data=self.extra_minion_data + ) else: - ext = self.ext_pillars[key](self.minion_id, - pillar, - *val) + ext = self.ext_pillars[key](self.minion_id, pillar, *val) else: - if ('extra_minion_data' in args) and self.extra_minion_data: + if ("extra_minion_data" in args) and self.extra_minion_data: ext = self.ext_pillars[key]( self.minion_id, pillar, val, - extra_minion_data=self.extra_minion_data) + extra_minion_data=self.extra_minion_data, + ) else: - ext = self.ext_pillars[key](self.minion_id, - pillar, - val) + ext = self.ext_pillars[key](self.minion_id, pillar, val) return ext def ext_pillar(self, pillar, errors=None): - ''' + """ Render the external pillar data - ''' + """ if errors is None: errors = [] try: # Make sure that on-demand git_pillar is fetched before we try to # compile the pillar data. git_pillar will fetch a remote when # the git ext_pillar() func is run, but only for masterless. - if self.ext and 'git' in self.ext \ - and self.opts.get('__role') != 'minion': + if self.ext and "git" in self.ext and self.opts.get("__role") != "minion": # Avoid circular import import salt.utils.gitfs import salt.pillar.git_pillar + git_pillar = salt.utils.gitfs.GitPillar( self.opts, - self.ext['git'], + self.ext["git"], per_remote_overrides=salt.pillar.git_pillar.PER_REMOTE_OVERRIDES, per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY, - global_only=salt.pillar.git_pillar.GLOBAL_ONLY) + global_only=salt.pillar.git_pillar.GLOBAL_ONLY, + ) git_pillar.fetch_remotes() except TypeError: # Handle malformed ext_pillar pass - if 'ext_pillar' not in self.opts: + if "ext_pillar" not in self.opts: return pillar, errors - if not isinstance(self.opts['ext_pillar'], list): + if not isinstance(self.opts["ext_pillar"], list): errors.append('The "ext_pillar" option is malformed') log.critical(errors[-1]) return pillar, errors @@ -959,65 +1095,63 @@ class Pillar(object): pillar, self.pillar_override, self.merge_strategy, - self.opts.get('renderer', 'yaml'), - self.opts.get('pillar_merge_lists', False)) + self.opts.get("renderer", "yaml"), + self.opts.get("pillar_merge_lists", False), + ) - for run in self.opts['ext_pillar']: + for run in self.opts["ext_pillar"]: if not isinstance(run, dict): errors.append('The "ext_pillar" option is malformed') log.critical(errors[-1]) return {}, errors - if next(six.iterkeys(run)) in self.opts.get('exclude_ext_pillar', []): + if next(six.iterkeys(run)) in self.opts.get("exclude_ext_pillar", []): continue for key, val in six.iteritems(run): if key not in self.ext_pillars: log.critical( - 'Specified ext_pillar interface %s is unavailable', - key + "Specified ext_pillar interface %s is unavailable", key ) continue try: - ext = self._external_pillar_data(pillar, - val, - key) + ext = self._external_pillar_data(pillar, val, key) except Exception as exc: # pylint: disable=broad-except errors.append( - 'Failed to load ext_pillar {0}: {1}'.format( - key, - exc.__str__(), - ) + "Failed to load ext_pillar {0}: {1}".format(key, exc.__str__(),) ) log.error( - 'Exception caught loading ext_pillar \'%s\':\n%s', - key, ''.join(traceback.format_tb(sys.exc_info()[2])) + "Exception caught loading ext_pillar '%s':\n%s", + key, + "".join(traceback.format_tb(sys.exc_info()[2])), ) if ext: pillar = merge( pillar, ext, self.merge_strategy, - self.opts.get('renderer', 'yaml'), - self.opts.get('pillar_merge_lists', False)) + self.opts.get("renderer", "yaml"), + self.opts.get("pillar_merge_lists", False), + ) ext = None return pillar, errors def compile_pillar(self, ext=True): - ''' + """ Render the pillar data and return - ''' + """ top, top_errors = self.get_top() if ext: - if self.opts.get('ext_pillar_first', False): - self.opts['pillar'], errors = self.ext_pillar(self.pillar_override) + if self.opts.get("ext_pillar_first", False): + self.opts["pillar"], errors = self.ext_pillar(self.pillar_override) self.rend = salt.loader.render(self.opts, self.functions) matches = self.top_matches(top, reload=True) pillar, errors = self.render_pillar(matches, errors=errors) pillar = merge( - self.opts['pillar'], + self.opts["pillar"], pillar, self.merge_strategy, - self.opts.get('renderer', 'yaml'), - self.opts.get('pillar_merge_lists', False)) + self.opts.get("renderer", "yaml"), + self.opts.get("pillar_merge_lists", False), + ) else: matches = self.top_matches(top) pillar, errors = self.render_pillar(matches) @@ -1026,58 +1160,62 @@ class Pillar(object): matches = self.top_matches(top) pillar, errors = self.render_pillar(matches) errors.extend(top_errors) - if self.opts.get('pillar_opts', False): + if self.opts.get("pillar_opts", False): mopts = dict(self.opts) - if 'grains' in mopts: - mopts.pop('grains') - mopts['saltversion'] = __version__ - pillar['master'] = mopts - if 'pillar' in self.opts and self.opts.get('ssh_merge_pillar', False): + if "grains" in mopts: + mopts.pop("grains") + mopts["saltversion"] = __version__ + pillar["master"] = mopts + if "pillar" in self.opts and self.opts.get("ssh_merge_pillar", False): pillar = merge( - self.opts['pillar'], + self.opts["pillar"], pillar, self.merge_strategy, - self.opts.get('renderer', 'yaml'), - self.opts.get('pillar_merge_lists', False)) + self.opts.get("renderer", "yaml"), + self.opts.get("pillar_merge_lists", False), + ) if errors: for error in errors: - log.critical('Pillar render error: %s', error) - pillar['_errors'] = errors + log.critical("Pillar render error: %s", error) + pillar["_errors"] = errors if self.pillar_override: pillar = merge( pillar, self.pillar_override, self.merge_strategy, - self.opts.get('renderer', 'yaml'), - self.opts.get('pillar_merge_lists', False)) + self.opts.get("renderer", "yaml"), + self.opts.get("pillar_merge_lists", False), + ) decrypt_errors = self.decrypt_pillar(pillar) if decrypt_errors: - pillar.setdefault('_errors', []).extend(decrypt_errors) + pillar.setdefault("_errors", []).extend(decrypt_errors) return pillar def decrypt_pillar(self, pillar): - ''' + """ Decrypt the specified pillar dictionary items, if configured to do so - ''' + """ errors = [] - if self.opts.get('decrypt_pillar'): - decrypt_pillar = self.opts['decrypt_pillar'] + if self.opts.get("decrypt_pillar"): + decrypt_pillar = self.opts["decrypt_pillar"] if not isinstance(decrypt_pillar, dict): - decrypt_pillar = \ - salt.utils.data.repack_dictlist(self.opts['decrypt_pillar']) + decrypt_pillar = salt.utils.data.repack_dictlist( + self.opts["decrypt_pillar"] + ) if not decrypt_pillar: - errors.append('decrypt_pillar config option is malformed') + errors.append("decrypt_pillar config option is malformed") for key, rend in six.iteritems(decrypt_pillar): ptr = salt.utils.data.traverse_dict( pillar, key, default=None, - delimiter=self.opts['decrypt_pillar_delimiter']) + delimiter=self.opts["decrypt_pillar_delimiter"], + ) if ptr is None: - log.debug('Pillar key %s not present', key) + log.debug("Pillar key %s not present", key) continue try: hash(ptr) @@ -1087,17 +1225,19 @@ class Pillar(object): try: ret = salt.utils.crypt.decrypt( ptr, - rend or self.opts['decrypt_pillar_default'], + rend or self.opts["decrypt_pillar_default"], renderers=self.rend, opts=self.opts, - valid_rend=self.opts['decrypt_pillar_renderers']) + valid_rend=self.opts["decrypt_pillar_renderers"], + ) if immutable: # Since the key pointed to an immutable type, we need # to replace it in the pillar dict. First we will find # the parent, and then we will replace the child key # with the return data from the renderer. parent, _, child = key.rpartition( - self.opts['decrypt_pillar_delimiter']) + self.opts["decrypt_pillar_delimiter"] + ) if not parent: # key is a top-level key, so the pointer to the # parent is the pillar dict itself. @@ -1107,21 +1247,20 @@ class Pillar(object): pillar, parent, default=None, - delimiter=self.opts['decrypt_pillar_delimiter']) + delimiter=self.opts["decrypt_pillar_delimiter"], + ) if ptr is not None: ptr[child] = ret except Exception as exc: # pylint: disable=broad-except - msg = 'Failed to decrypt pillar key \'{0}\': {1}'.format( - key, exc - ) + msg = "Failed to decrypt pillar key '{0}': {1}".format(key, exc) errors.append(msg) log.error(msg, exc_info=True) return errors def destroy(self): - ''' + """ This method exist in order to be API compatible with RemotePillar - ''' + """ if self._closing: return self._closing = True @@ -1129,6 +1268,7 @@ class Pillar(object): # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 diff --git a/salt/pillar/cmd_json.py b/salt/pillar/cmd_json.py index a045e54222f..ad8c4274c4a 100644 --- a/salt/pillar/cmd_json.py +++ b/salt/pillar/cmd_json.py @@ -1,31 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" Execute a command and read the output as JSON. The JSON data is then directly overlaid onto the minion's Pillar data. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Don't "fix" the above docstring to put it on two lines, as the sphinx -# autosummary pulls only the first line for its description. - # Import Python libs import logging # Import Salt libs import salt.utils.json +# Don't "fix" the above docstring to put it on two lines, as the sphinx +# autosummary pulls only the first line for its description. + + # Set up logging log = logging.getLogger(__name__) -def ext_pillar(minion_id, # pylint: disable=W0613 - pillar, # pylint: disable=W0613 - command): - ''' +def ext_pillar( + minion_id, pillar, command # pylint: disable=W0613 # pylint: disable=W0613 +): + """ Execute a command and read the output as JSON - ''' + """ try: - command = command.replace('%s', minion_id) - return salt.utils.json.loads(__salt__['cmd.run'](command)) + command = command.replace("%s", minion_id) + return salt.utils.json.loads(__salt__["cmd.run"](command)) except Exception: # pylint: disable=broad-except - log.critical('JSON data from %s failed to parse', command) + log.critical("JSON data from %s failed to parse", command) return {} diff --git a/salt/pillar/cmd_yaml.py b/salt/pillar/cmd_yaml.py index 2431ce1cae0..fa777fc1a96 100644 --- a/salt/pillar/cmd_yaml.py +++ b/salt/pillar/cmd_yaml.py @@ -1,35 +1,35 @@ # -*- coding: utf-8 -*- -''' +""" Execute a command and read the output as YAML. The YAML data is then directly overlaid onto the minion's Pillar data -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Don't "fix" the above docstring to put it on two lines, as the sphinx -# autosummary pulls only the first line for its description. - # Import Python libs import logging # Import Salt party libs import salt.utils.yaml +# Don't "fix" the above docstring to put it on two lines, as the sphinx +# autosummary pulls only the first line for its description. + + # Set up logging log = logging.getLogger(__name__) -def ext_pillar(minion_id, # pylint: disable=W0613 - pillar, # pylint: disable=W0613 - command): - ''' +def ext_pillar( + minion_id, pillar, command # pylint: disable=W0613 # pylint: disable=W0613 +): + """ Execute a command and read the output as YAML - ''' + """ try: - command = command.replace('%s', minion_id) - output = __salt__['cmd.run_stdout'](command, python_shell=True) + command = command.replace("%s", minion_id) + output = __salt__["cmd.run_stdout"](command, python_shell=True) return salt.utils.yaml.safe_load(output) except Exception: # pylint: disable=broad-except log.critical( - 'YAML data from \'%s\' failed to parse. Command output:\n%s', - command, output + "YAML data from '%s' failed to parse. Command output:\n%s", command, output ) return {} diff --git a/salt/pillar/cmd_yamlex.py b/salt/pillar/cmd_yamlex.py index d6772b41354..adafe29db47 100644 --- a/salt/pillar/cmd_yamlex.py +++ b/salt/pillar/cmd_yamlex.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Execute a command and read the output as YAMLEX. The YAMLEX data is then directly overlaid onto the minion's Pillar data -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -16,15 +17,15 @@ from salt.serializers.yamlex import deserialize log = logging.getLogger(__name__) -def ext_pillar(minion_id, # pylint: disable=W0613 - pillar, # pylint: disable=W0613 - command): - ''' +def ext_pillar( + minion_id, pillar, command # pylint: disable=W0613 # pylint: disable=W0613 +): + """ Execute a command and read the output as YAMLEX - ''' + """ try: - command = command.replace('%s', minion_id) - return deserialize(__salt__['cmd.run']('{0}'.format(command))) + command = command.replace("%s", minion_id) + return deserialize(__salt__["cmd.run"]("{0}".format(command))) except Exception: # pylint: disable=broad-except - log.critical('YAML data from %s failed to parse', command) + log.critical("YAML data from %s failed to parse", command) return {} diff --git a/salt/pillar/cobbler.py b/salt/pillar/cobbler.py index e418a4b3ba3..c28b981eab0 100644 --- a/salt/pillar/cobbler.py +++ b/salt/pillar/cobbler.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A module to pull data from Cobbler via its API into the Pillar dictionary @@ -23,34 +23,32 @@ modules. Module Documentation ==================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging + import salt.ext.six.moves.xmlrpc_client # pylint: disable=E0611 - -__opts__ = {'cobbler.url': 'http://localhost/cobbler_api', - 'cobbler.user': None, - 'cobbler.password': None - } +__opts__ = { + "cobbler.url": "http://localhost/cobbler_api", + "cobbler.user": None, + "cobbler.password": None, +} # Set up logging log = logging.getLogger(__name__) -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - key=None, - only=()): - ''' +def ext_pillar(minion_id, pillar, key=None, only=()): # pylint: disable=W0613 + """ Read pillar data from Cobbler via its API. - ''' - url = __opts__['cobbler.url'] - user = __opts__['cobbler.user'] - password = __opts__['cobbler.password'] + """ + url = __opts__["cobbler.url"] + user = __opts__["cobbler.user"] + password = __opts__["cobbler.password"] log.info("Querying cobbler at %r for information for %r", url, minion_id) try: @@ -59,9 +57,7 @@ def ext_pillar(minion_id, server.login(user, password) result = server.get_blended_data(None, minion_id) except Exception: # pylint: disable=broad-except - log.exception( - 'Could not connect to cobbler.' - ) + log.exception("Could not connect to cobbler.") return {} if only: diff --git a/salt/pillar/confidant.py b/salt/pillar/confidant.py index a0acc012272..7b7b23816eb 100644 --- a/salt/pillar/confidant.py +++ b/salt/pillar/confidant.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An external pillar module for getting credentials from confidant. Configuring the Confidant module @@ -36,18 +36,20 @@ ext_pillar: Module Documentation ==================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import copy + # Import python libs import logging -import copy # Import third party libs try: # pylint: disable=no-name-in-module import confidant.client import confidant.formatter + HAS_LIBS = True # pylint: enable=no-name-in-module except ImportError: @@ -56,13 +58,13 @@ except ImportError: # Set up logging log = logging.getLogger(__name__) -__virtualname__ = 'confidant' +__virtualname__ = "confidant" def __virtual__(): - ''' + """ Only return if requests and boto are installed. - ''' + """ if HAS_LIBS: return __virtualname__ else: @@ -70,36 +72,36 @@ def __virtual__(): def ext_pillar(minion_id, pillar, profile=None): - ''' + """ Read pillar data from Confidant via its API. - ''' + """ if profile is None: profile = {} # default to returning failure ret = { - 'credentials_result': False, - 'credentials': None, - 'credentials_metadata': None + "credentials_result": False, + "credentials": None, + "credentials_metadata": None, } profile_data = copy.deepcopy(profile) - if profile_data.get('disabled', False): - ret['result'] = True + if profile_data.get("disabled", False): + ret["result"] = True return ret - token_version = profile_data.get('token_version', 1) + token_version = profile_data.get("token_version", 1) try: - url = profile_data['url'] - auth_key = profile_data['auth_key'] - auth_context = profile_data['auth_context'] - role = auth_context['from'] + url = profile_data["url"] + auth_key = profile_data["auth_key"] + auth_context = profile_data["auth_context"] + role = auth_context["from"] except (KeyError, TypeError): - msg = ('profile has undefined url, auth_key or auth_context') + msg = "profile has undefined url, auth_key or auth_context" log.debug(msg) return ret - region = profile_data.get('region', 'us-east-1') - token_duration = profile_data.get('token_duration', 60) - retries = profile_data.get('retries', 5) - token_cache_file = profile_data.get('token_cache_file') - backoff = profile_data.get('backoff', 1) + region = profile_data.get("region", "us-east-1") + token_duration = profile_data.get("token_duration", 60) + retries = profile_data.get("retries", 5) + token_cache_file = profile_data.get("token_cache_file") + backoff = profile_data.get("backoff", 1) client = confidant.client.ConfidantClient( url, auth_key, @@ -109,17 +111,14 @@ def ext_pillar(minion_id, pillar, profile=None): token_cache_file=token_cache_file, region=region, retries=retries, - backoff=backoff + backoff=backoff, ) try: - data = client.get_service( - role, - decrypt_blind=True - ) + data = client.get_service(role, decrypt_blind=True) except confidant.client.TokenCreationError: return ret - if not data['result']: + if not data["result"]: return ret ret = confidant.formatter.combined_credential_pair_format(data) - ret['credentials_result'] = True + ret["credentials_result"] = True return ret diff --git a/salt/pillar/consul_pillar.py b/salt/pillar/consul_pillar.py index 6903ccde2d8..fd68ee4c0d2 100644 --- a/salt/pillar/consul_pillar.py +++ b/salt/pillar/consul_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use Consul K/V as a Pillar source with values parsed as YAML :depends: - python-consul @@ -22,9 +22,9 @@ All parameters are optional. The ``consul.token`` requires python-consul >= 0.4.7. -If you have a multi-datacenter Consul cluster you can map your ``pillarenv``s -to your data centers by providing a dictionary of mappings in ``consul.dc`` -field: +If you have a multi-datacenter Consul cluster you can map your ``pillarenv`` +entries to your data centers by providing a dictionary of mappings in +``consul.dc`` field: .. code-block:: yaml @@ -134,110 +134,111 @@ This behavior can be disabled by setting ``expand_keys`` to ``false``. ext_pillar: - consul: my_consul_config expand_keys=false -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging import re -from salt.exceptions import CommandExecutionError -from salt.utils.dictupdate import update as dict_merge import salt.utils.minions import salt.utils.yaml +from salt.exceptions import CommandExecutionError +from salt.utils.dictupdate import update as dict_merge # Import third party libs try: import consul - if not hasattr(consul, '__version__'): - consul.__version__ = '0.1' # Some packages has no version, and so this pillar crashes on access to it. + + if not hasattr(consul, "__version__"): + consul.__version__ = "0.1" # Some packages has no version, and so this pillar crashes on access to it. except ImportError: consul = None -__virtualname__ = 'consul' +__virtualname__ = "consul" # Set up logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only return if python-consul is installed - ''' + """ return __virtualname__ if consul is not None else False -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - conf): - ''' +def ext_pillar(minion_id, pillar, conf): # pylint: disable=W0613 + """ Check consul for all data - ''' + """ opts = {} temp = conf target_re = re.compile('target="(.*?)"') match = target_re.search(temp) if match: - opts['target'] = match.group(1) - temp = temp.replace(match.group(0), '') + opts["target"] = match.group(1) + temp = temp.replace(match.group(0), "") checker = salt.utils.minions.CkMinions(__opts__) - _res = checker.check_minions(opts['target'], 'compound') - minions = _res['minions'] - log.debug('Targeted minions: %r', minions) + _res = checker.check_minions(opts["target"], "compound") + minions = _res["minions"] + log.debug("Targeted minions: %r", minions) if minion_id not in minions: return {} - root_re = re.compile('(?<!_)root=(\S*)') # pylint: disable=W1401 + root_re = re.compile("(?<!_)root=(\S*)") # pylint: disable=W1401 match = root_re.search(temp) if match: - opts['root'] = match.group(1).rstrip('/') - temp = temp.replace(match.group(0), '') + opts["root"] = match.group(1).rstrip("/") + temp = temp.replace(match.group(0), "") else: - opts['root'] = "" + opts["root"] = "" - pillar_root_re = re.compile('pillar_root=(\S*)') # pylint: disable=W1401 + pillar_root_re = re.compile("pillar_root=(\S*)") # pylint: disable=W1401 match = pillar_root_re.search(temp) if match: - opts['pillar_root'] = match.group(1).rstrip('/') - temp = temp.replace(match.group(0), '') + opts["pillar_root"] = match.group(1).rstrip("/") + temp = temp.replace(match.group(0), "") else: - opts['pillar_root'] = "" + opts["pillar_root"] = "" - profile_re = re.compile('(?:profile=)?(\S+)') # pylint: disable=W1401 + profile_re = re.compile("(?:profile=)?(\S+)") # pylint: disable=W1401 match = profile_re.search(temp) if match: - opts['profile'] = match.group(1) - temp = temp.replace(match.group(0), '') + opts["profile"] = match.group(1) + temp = temp.replace(match.group(0), "") else: - opts['profile'] = None + opts["profile"] = None - expand_keys_re = re.compile('expand_keys=False', re.IGNORECASE) # pylint: disable=W1401 + expand_keys_re = re.compile( + "expand_keys=False", re.IGNORECASE + ) # pylint: disable=W1401 match = expand_keys_re.search(temp) if match: - opts['expand_keys'] = False - temp = temp.replace(match.group(0), '') + opts["expand_keys"] = False + temp = temp.replace(match.group(0), "") else: - opts['expand_keys'] = True + opts["expand_keys"] = True - client = get_conn(__opts__, opts['profile']) + client = get_conn(__opts__, opts["profile"]) - role = __salt__['grains.get']('role', None) - environment = __salt__['grains.get']('environment', None) + role = __salt__["grains.get"]("role", None) + environment = __salt__["grains.get"]("environment", None) # put the minion's ID in the path if necessary - opts['root'] %= { - 'minion_id': minion_id, - 'role': role, - 'environment': environment - } + opts["root"] %= {"minion_id": minion_id, "role": role, "environment": environment} try: - pillar_tree = fetch_tree(client, opts['root'], opts['expand_keys']) - if opts['pillar_root']: - log.debug('Merging consul path %s/ into pillar at %s/', opts['root'], opts['pillar_root']) + pillar_tree = fetch_tree(client, opts["root"], opts["expand_keys"]) + if opts["pillar_root"]: + log.debug( + "Merging consul path %s/ into pillar at %s/", + opts["root"], + opts["pillar_root"], + ) pillar = {} branch = pillar - keys = opts['pillar_root'].split('/') + keys = opts["pillar_root"].split("/") for i, k in enumerate(keys): if i == len(keys) - 1: @@ -248,52 +249,52 @@ def ext_pillar(minion_id, else: pillar = pillar_tree except KeyError: - log.error('No such key in consul profile %s: %s', opts['profile'], opts['root']) + log.error("No such key in consul profile %s: %s", opts["profile"], opts["root"]) pillar = {} return pillar def consul_fetch(client, path): - ''' + """ Query consul for all keys/values within base path - ''' + """ # Unless the root path is blank, it needs a trailing slash for # the kv get from Consul to work as expected - return client.kv.get('' if not path else path.rstrip('/') + '/', recurse=True) + return client.kv.get("" if not path else path.rstrip("/") + "/", recurse=True) def fetch_tree(client, path, expand_keys): - ''' + """ Grab data from consul, trim base path and remove any keys which are folders. Take the remaining data and send it to be formatted in such a way as to be used as pillar data. - ''' + """ _, items = consul_fetch(client, path) ret = {} - has_children = re.compile(r'/$') + has_children = re.compile(r"/$") - log.debug('Fetched items: %r', items) + log.debug("Fetched items: %r", items) if items is None: return ret for item in reversed(items): - key = re.sub(r'^' + re.escape(path) + '/?', '', item['Key']) - if key != '': - log.debug('path/key - %s: %s', path, key) - log.debug('has_children? %r', has_children.search(key)) + key = re.sub(r"^" + re.escape(path) + "/?", "", item["Key"]) + if key != "": + log.debug("path/key - %s: %s", path, key) + log.debug("has_children? %r", has_children.search(key)) if has_children.search(key) is None: - ret = pillar_format(ret, key.split('/'), item['Value'], expand_keys) - log.debug('Fetching subkeys for key: %r', item) + ret = pillar_format(ret, key.split("/"), item["Value"], expand_keys) + log.debug("Fetching subkeys for key: %r", item) return ret def pillar_format(ret, keys, value, expand_keys): - ''' + """ Perform data formatting to be used as pillar data and merge it with the current pillar data - ''' + """ # if value is empty in Consul then it's None here - skip it if value is None: return ret @@ -317,11 +318,11 @@ def pillar_format(ret, keys, value, expand_keys): def get_conn(opts, profile): - ''' + """ Return a client object for accessing consul - ''' - opts_pillar = opts.get('pillar', {}) - opts_master = opts_pillar.get('master', {}) + """ + opts_pillar = opts.get("pillar", {}) + opts_master = opts_pillar.get("master", {}) opts_merged = {} opts_merged.update(opts_master) @@ -335,28 +336,28 @@ def get_conn(opts, profile): params = {} for key in conf: - if key.startswith('consul.'): - params[key.split('.')[1]] = conf[key] + if key.startswith("consul."): + params[key.split(".")[1]] = conf[key] - if 'dc' in params: - pillarenv = opts_merged.get('pillarenv') or 'base' - params['dc'] = _resolve_datacenter(params['dc'], pillarenv) + if "dc" in params: + pillarenv = opts_merged.get("pillarenv") or "base" + params["dc"] = _resolve_datacenter(params["dc"], pillarenv) if consul: # Sanity check. ACL Tokens are supported on python-consul 0.4.7 onwards only. - if consul.__version__ < '0.4.7' and params.get('target'): - params.pop('target') + if consul.__version__ < "0.4.7" and params.get("target"): + params.pop("target") return consul.Consul(**params) else: raise CommandExecutionError( - '(unable to import consul, ' - 'module most likely not installed. Download python-consul ' - 'module and be sure to import consul)' + "(unable to import consul, " + "module most likely not installed. Download python-consul " + "module and be sure to import consul)" ) def _resolve_datacenter(dc, pillarenv): - ''' + """ If ``dc`` is a string - return it as is. If it's a dict then sort it in descending order by key length and try @@ -367,17 +368,17 @@ def _resolve_datacenter(dc, pillarenv): If none patterns matched return ``None`` which meanse us datacenter of conencted Consul agent. - ''' - log.debug('Resolving Consul datacenter based on: %s', dc) + """ + log.debug("Resolving Consul datacenter based on: %s", dc) try: mappings = dc.items() # is it a dict? except AttributeError: - log.debug('Using pre-defined DC: \'%s\'', dc) + log.debug("Using pre-defined DC: '%s'", dc) return dc - log.debug('Selecting DC based on pillarenv using %d pattern(s)', len(mappings)) - log.debug('Pillarenv set to \'%s\'', pillarenv) + log.debug("Selecting DC based on pillarenv using %d pattern(s)", len(mappings)) + log.debug("Pillarenv set to '%s'", pillarenv) # sort in reverse based on pattern length # but use alphabetic order within groups of patterns of same length @@ -386,12 +387,13 @@ def _resolve_datacenter(dc, pillarenv): for pattern, target in sorted_mappings: match = re.match(pattern, pillarenv) if match: - log.debug('Matched pattern: \'%s\'', pattern) + log.debug("Matched pattern: '%s'", pattern) result = target.format(**match.groupdict()) - log.debug('Resolved datacenter: \'%s\'', result) + log.debug("Resolved datacenter: '%s'", result) return result log.debug( - 'None of following patterns matched pillarenv=%s: %s', - pillarenv, ', '.join(repr(x) for x in mappings) + "None of following patterns matched pillarenv=%s: %s", + pillarenv, + ", ".join(repr(x) for x in mappings), ) diff --git a/salt/pillar/csvpillar.py b/salt/pillar/csvpillar.py index 320859a3744..3fbc15cbe9d 100644 --- a/salt/pillar/csvpillar.py +++ b/salt/pillar/csvpillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Store key/value pairs in a CSV file .. versionadded:: 2016.11.0 @@ -44,13 +44,14 @@ Will produce the following Pillar values for a minion named "jerry": 'role': 'web', 'env': 'prod', } -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import csv import salt.utils.files -__virtualname__ = 'csv' +__virtualname__ = "csv" def __virtual__(): @@ -58,16 +59,17 @@ def __virtual__(): def ext_pillar( - mid, - pillar, - path, - idkey='id', - namespace=None, - fieldnames=None, - restkey=None, - restval=None, - dialect='excel'): - ''' + mid, + pillar, + path, + idkey="id", + namespace=None, + fieldnames=None, + restkey=None, + restval=None, + dialect="excel", +): + """ Read a CSV into Pillar :param str path: Absolute path to a CSV file. @@ -75,10 +77,11 @@ def ext_pillar( :param str namespace: (Optional) A pillar key to namespace the values under. :param list fieldnames: (Optional) if the first row of the CSV is not column names they may be specified here instead. - ''' - with salt.utils.files.fopen(path, 'r') as f: - sheet = csv.DictReader(f, fieldnames, - restkey=restkey, restval=restval, dialect=dialect) + """ + with salt.utils.files.fopen(path, "r") as f: + sheet = csv.DictReader( + f, fieldnames, restkey=restkey, restval=restval, dialect=dialect + ) for row in sheet: if row[idkey] == mid: diff --git a/salt/pillar/digicert.py b/salt/pillar/digicert.py index d25926b4ea5..da8b0a3e255 100644 --- a/salt/pillar/digicert.py +++ b/salt/pillar/digicert.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Digicert Pillar Certificates This module will only return pillar data if the ``digicert`` runner module has @@ -12,34 +12,36 @@ section of your ``master`` configuration file: ext_pillar: - digicert: True -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging + import salt.cache import salt.syspaths as syspaths -__virtualname__ = 'digicert' +__virtualname__ = "digicert" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ No special requirements outside of Salt itself - ''' + """ return __virtualname__ def ext_pillar(minion_id, pillar, conf): - ''' + """ Return an existing set of certificates - ''' + """ cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) ret = {} - dns_names = cache.fetch('digicert/minions', minion_id) + dns_names = cache.fetch("digicert/minions", minion_id) for dns_name in dns_names: - data = cache.fetch('digicert/domains', dns_name) + data = cache.fetch("digicert/domains", dns_name) ret[dns_name] = data - del ret[dns_name]['csr'] - return {'digicert': ret} + del ret[dns_name]["csr"] + return {"digicert": ret} diff --git a/salt/pillar/django_orm.py b/salt/pillar/django_orm.py index 70b7af49107..e7b46be30a1 100644 --- a/salt/pillar/django_orm.py +++ b/salt/pillar/django_orm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Generate Pillar data from Django models through the Django ORM :maintainer: Micah Hausler <micah.hausler@gmail.com> @@ -88,7 +88,7 @@ work since the return from values() changes if a ManyToMany is present. Module Documentation ==================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging @@ -96,13 +96,14 @@ import os import sys import salt.exceptions -from salt.ext import six import salt.utils.stringutils +from salt.ext import six HAS_VIRTUALENV = False try: import virtualenv + HAS_VIRTUALENV = True except ImportError: pass @@ -111,23 +112,25 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Always load - ''' + """ return True -def ext_pillar(minion_id, # pylint: disable=W0613 - pillar, # pylint: disable=W0613 - pillar_name, - project_path, - settings_module, - django_app, - env=None, - env_file=None, - *args, # pylint: disable=W0613 - **kwargs): # pylint: disable=W0613 - ''' +def ext_pillar( + minion_id, # pylint: disable=W0613 + pillar, # pylint: disable=W0613 + pillar_name, + project_path, + settings_module, + django_app, + env=None, + env_file=None, + *args, # pylint: disable=W0613 + **kwargs +): # pylint: disable=W0613 + """ Connect to a Django database through the ORM and retrieve model fields :type pillar_name: str @@ -150,70 +153,76 @@ def ext_pillar(minion_id, # pylint: disable=W0613 :type env_file: str :param env_file: An optional bash file that sets up your environment. The file is run in a subprocess and the changed variables are then added - ''' + """ if not os.path.isdir(project_path): - log.error('Django project dir: \'%s\' not a directory!', project_path) + log.error("Django project dir: '%s' not a directory!", project_path) return {} if HAS_VIRTUALENV and env is not None and os.path.isdir(env): for path in virtualenv.path_locations(env): if not os.path.isdir(path): - log.error('Virtualenv %s not a directory!', path) + log.error("Virtualenv %s not a directory!", path) return {} # load the virtualenv first - sys.path.insert(0, - os.path.join( - virtualenv.path_locations(env)[1], - 'site-packages')) + sys.path.insert( + 0, os.path.join(virtualenv.path_locations(env)[1], "site-packages") + ) # load the django project sys.path.append(project_path) - os.environ['DJANGO_SETTINGS_MODULE'] = settings_module + os.environ["DJANGO_SETTINGS_MODULE"] = settings_module if env_file is not None: import subprocess base_env = {} - proc = subprocess.Popen(['bash', '-c', 'env'], stdout=subprocess.PIPE) + proc = subprocess.Popen(["bash", "-c", "env"], stdout=subprocess.PIPE) for line in proc.stdout: - (key, _, value) = salt.utils.stringutils.to_str(line).partition('=') + (key, _, value) = salt.utils.stringutils.to_str(line).partition("=") base_env[key] = value - command = ['bash', '-c', 'source {0} && env'.format(env_file)] + command = ["bash", "-c", "source {0} && env".format(env_file)] proc = subprocess.Popen(command, stdout=subprocess.PIPE) for line in proc.stdout: - (key, _, value) = salt.utils.stringutils.to_str(line).partition('=') + (key, _, value) = salt.utils.stringutils.to_str(line).partition("=") # only add a key if it is different or doesn't already exist if key not in base_env or base_env[key] != value: - os.environ[key] = value.rstrip('\n') - log.debug('Adding %s = %s to Django environment', key, value.rstrip('\n')) + os.environ[key] = value.rstrip("\n") + log.debug( + "Adding %s = %s to Django environment", key, value.rstrip("\n") + ) try: - from django.db.models.loading import get_model # pylint: disable=no-name-in-module + # pylint: disable=no-name-in-module + from django.db.models.loading import get_model + + # pylint: enable=no-name-in-module django_pillar = {} for proj_app, models in six.iteritems(django_app): - _, _, app = proj_app.rpartition('.') + _, _, app = proj_app.rpartition(".") django_pillar[app] = {} for model_name, model_meta in six.iteritems(models): model_orm = get_model(app, model_name) if model_orm is None: raise salt.exceptions.SaltException( - "Django model '{0}' not found in app '{1}'." - .format(app, model_name)) + "Django model '{0}' not found in app '{1}'.".format( + app, model_name + ) + ) pillar_for_model = django_pillar[app][model_orm.__name__] = {} - name_field = model_meta['name'] - fields = model_meta['fields'] + name_field = model_meta["name"] + fields = model_meta["fields"] - if 'filter' in model_meta: - qs = (model_orm.objects - .filter(**model_meta['filter']) - .values(*fields)) + if "filter" in model_meta: + qs = model_orm.objects.filter(**model_meta["filter"]).values( + *fields + ) else: qs = model_orm.objects.values(*fields) @@ -224,20 +233,24 @@ def ext_pillar(minion_id, # pylint: disable=W0613 if name_field not in model: raise salt.exceptions.SaltException( "Name '{0}' not found in returned fields.".format( - name_field)) + name_field + ) + ) if model[name_field] in pillar_for_model: raise salt.exceptions.SaltException( "Value for '{0}' is not unique: {0}".format( - model[name_field])) + model[name_field] + ) + ) pillar_for_model[model[name_field]] = model return {pillar_name: django_pillar} except ImportError as e: - log.error('Failed to import library: %s', e) + log.error("Failed to import library: %s", e) return {} except Exception as e: # pylint: disable=broad-except - log.error('Failed on Error: %s', e) - log.debug('django_orm traceback', exc_info=True) + log.error("Failed on Error: %s", e) + log.debug("django_orm traceback", exc_info=True) return {} diff --git a/salt/pillar/ec2_pillar.py b/salt/pillar/ec2_pillar.py index f129fb3290f..eb17490cd68 100644 --- a/salt/pillar/ec2_pillar.py +++ b/salt/pillar/ec2_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Retrieve EC2 instance data for minions for ec2_tags and ec2_tags_list The minion id must be the AWS instance-id or value in ``tag_match_key``. For @@ -52,12 +52,14 @@ This is a very simple pillar configuration that simply retrieves the instance data from AWS. Currently the only portion implemented are EC2 tags, which returns a list of key/value pairs for all of the EC2 tags assigned to the instance. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import re + import logging +import re + import salt.ext.six as six from salt.ext.six.moves import range @@ -69,6 +71,7 @@ try: import boto.ec2 import boto.utils import boto.exception + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -77,112 +80,158 @@ except ImportError: log = logging.getLogger(__name__) # DEBUG boto is far too verbose -logging.getLogger('boto').setLevel(logging.WARNING) +logging.getLogger("boto").setLevel(logging.WARNING) def __virtual__(): - ''' + """ Check for required version of boto and make this pillar available depending on outcome. - ''' + """ if not HAS_BOTO: return False boto_version = _StrictVersion(boto.__version__) - required_boto_version = _StrictVersion('2.8.0') + required_boto_version = _StrictVersion("2.8.0") if boto_version < required_boto_version: - log.error("%s: installed boto version %s < %s, can't retrieve instance data", - __name__, boto_version, required_boto_version) + log.error( + "%s: installed boto version %s < %s, can't retrieve instance data", + __name__, + boto_version, + required_boto_version, + ) return False return True def _get_instance_info(): - ''' + """ Helper function to return the instance ID and region of the master where this pillar is run. - ''' - identity = boto.utils.get_instance_identity()['document'] - return (identity['instanceId'], identity['region']) + """ + identity = boto.utils.get_instance_identity()["document"] + return (identity["instanceId"], identity["region"]) -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - use_grain=False, - minion_ids=None, - tag_match_key=None, - tag_match_value='asis', - tag_list_key=None, - tag_list_sep=';'): - ''' +def ext_pillar( + minion_id, + pillar, # pylint: disable=W0613 + use_grain=False, + minion_ids=None, + tag_match_key=None, + tag_match_value="asis", + tag_list_key=None, + tag_list_sep=";", +): + """ Execute a command and read the output as YAML - ''' - valid_tag_match_value = ['uqdn', 'asis'] + """ + valid_tag_match_value = ["uqdn", "asis"] # meta-data:instance-id - grain_instance_id = __grains__.get('meta-data', {}).get('instance-id', None) + grain_instance_id = __grains__.get("meta-data", {}).get("instance-id", None) if not grain_instance_id: # dynamic:instance-identity:document:instanceId - grain_instance_id = \ - __grains__.get('dynamic', {}).get('instance-identity', {}).get('document', {}).get('instance-id', None) - if grain_instance_id and re.search(r'^i-([0-9a-z]{17}|[0-9a-z]{8})$', grain_instance_id) is None: - log.error('External pillar %s, instance-id \'%s\' is not valid for ' - '\'%s\'', __name__, grain_instance_id, minion_id) + grain_instance_id = ( + __grains__.get("dynamic", {}) + .get("instance-identity", {}) + .get("document", {}) + .get("instance-id", None) + ) + if ( + grain_instance_id + and re.search(r"^i-([0-9a-z]{17}|[0-9a-z]{8})$", grain_instance_id) is None + ): + log.error( + "External pillar %s, instance-id '%s' is not valid for " "'%s'", + __name__, + grain_instance_id, + minion_id, + ) grain_instance_id = None # invalid instance id found, remove it from use. # Check AWS Tag restrictions .i.e. letters, spaces, and numbers and + - = . _ : / @ - if tag_match_key and re.match(r'[\w=.:/@-]+$', tag_match_key) is None: - log.error('External pillar %s, tag_match_key \'%s\' is not valid ', - __name__, tag_match_key if isinstance(tag_match_key, six.text_type) else 'non-string') + if tag_match_key and re.match(r"[\w=.:/@-]+$", tag_match_key) is None: + log.error( + "External pillar %s, tag_match_key '%s' is not valid ", + __name__, + tag_match_key if isinstance(tag_match_key, six.text_type) else "non-string", + ) return {} if tag_match_key and tag_match_value not in valid_tag_match_value: - log.error('External pillar %s, tag_value \'%s\' is not valid must be one ' - 'of %s', __name__, tag_match_value, ' '.join(valid_tag_match_value)) + log.error( + "External pillar %s, tag_value '%s' is not valid must be one " "of %s", + __name__, + tag_match_value, + " ".join(valid_tag_match_value), + ) return {} if not tag_match_key: - base_msg = ('External pillar %s, querying EC2 tags for minion id \'%s\' ' - 'against instance-id', __name__, minion_id) + base_msg = ( + "External pillar %s, querying EC2 tags for minion id '%s' " + "against instance-id", + __name__, + minion_id, + ) else: - base_msg = ('External pillar %s, querying EC2 tags for minion id \'%s\' ' - 'against instance-id or \'%s\' against \'%s\'', __name__, minion_id, tag_match_key, tag_match_value) + base_msg = ( + "External pillar %s, querying EC2 tags for minion id '%s' " + "against instance-id or '%s' against '%s'", + __name__, + minion_id, + tag_match_key, + tag_match_value, + ) log.debug(base_msg) find_filter = None find_id = None - if re.search(r'^i-([0-9a-z]{17}|[0-9a-z]{8})$', minion_id) is not None: + if re.search(r"^i-([0-9a-z]{17}|[0-9a-z]{8})$", minion_id) is not None: find_filter = None find_id = minion_id elif tag_match_key: - if tag_match_value == 'uqdn': - find_filter = {'tag:{0}'.format(tag_match_key): minion_id.split('.', 1)[0]} + if tag_match_value == "uqdn": + find_filter = {"tag:{0}".format(tag_match_key): minion_id.split(".", 1)[0]} else: - find_filter = {'tag:{0}'.format(tag_match_key): minion_id} + find_filter = {"tag:{0}".format(tag_match_key): minion_id} if grain_instance_id: # we have an untrusted grain_instance_id, use it to narrow the search # even more. Combination will be unique even if uqdn is set. - find_filter.update({'instance-id': grain_instance_id}) + find_filter.update({"instance-id": grain_instance_id}) # Add this if running state is not dependant on EC2Config # find_filter.update('instance-state-name': 'running') # no minion-id is instance-id and no suitable filter, try use_grain if enabled if not find_filter and not find_id and use_grain: if not grain_instance_id: - log.debug('Minion-id is not in AWS instance-id formation, and there ' - 'is no instance-id grain for minion %s', minion_id) + log.debug( + "Minion-id is not in AWS instance-id formation, and there " + "is no instance-id grain for minion %s", + minion_id, + ) return {} if minion_ids is not None and minion_id not in minion_ids: - log.debug('Minion-id is not in AWS instance ID format, and minion_ids ' - 'is set in the ec2_pillar configuration, but minion %s is ' - 'not in the list of allowed minions %s', minion_id, minion_ids) + log.debug( + "Minion-id is not in AWS instance ID format, and minion_ids " + "is set in the ec2_pillar configuration, but minion %s is " + "not in the list of allowed minions %s", + minion_id, + minion_ids, + ) return {} find_id = grain_instance_id if not (find_filter or find_id): - log.debug('External pillar %s, querying EC2 tags for minion id \'%s\' against ' - 'instance-id or \'%s\' against \'%s\' noughthing to match against', - __name__, minion_id, tag_match_key, tag_match_value) + log.debug( + "External pillar %s, querying EC2 tags for minion id '%s' against " + "instance-id or '%s' against '%s' noughthing to match against", + __name__, + minion_id, + tag_match_key, + tag_match_value, + ) return {} myself = boto.utils.get_instance_metadata(timeout=0.1, num_retries=1) @@ -196,48 +245,65 @@ def ext_pillar(minion_id, try: conn = boto.ec2.connect_to_region(region) except boto.exception.AWSConnectionError as exc: - log.error('%s: invalid AWS credentials, %s', __name__, exc) + log.error("%s: invalid AWS credentials, %s", __name__, exc) return {} if conn is None: - log.error('%s: Could not connect to region %s', __name__, region) + log.error("%s: Could not connect to region %s", __name__, region) return {} try: if find_id: - instance_data = conn.get_only_instances(instance_ids=[find_id], dry_run=False) + instance_data = conn.get_only_instances( + instance_ids=[find_id], dry_run=False + ) else: # filters and max_results can not be used togther. instance_data = conn.get_only_instances(filters=find_filter, dry_run=False) except boto.exception.EC2ResponseError as exc: - log.error('%s failed with \'%s\'', base_msg, exc) + log.error("%s failed with '%s'", base_msg, exc) return {} if not instance_data: - log.debug('%s no match using \'%s\'', base_msg, find_id if find_id else find_filter) + log.debug( + "%s no match using '%s'", base_msg, find_id if find_id else find_filter + ) return {} # Find a active instance, i.e. ignore terminated and stopped instances active_inst = [] for inst in range(0, len(instance_data)): - if instance_data[inst].state not in ['terminated', 'stopped']: + if instance_data[inst].state not in ["terminated", "stopped"]: active_inst.append(inst) valid_inst = len(active_inst) if not valid_inst: - log.debug('%s match found but not active \'%s\'', base_msg, find_id if find_id else find_filter) + log.debug( + "%s match found but not active '%s'", + base_msg, + find_id if find_id else find_filter, + ) return {} if valid_inst > 1: - log.error('%s multiple matches, ignored, using \'%s\'', base_msg, find_id if find_id else find_filter) + log.error( + "%s multiple matches, ignored, using '%s'", + base_msg, + find_id if find_id else find_filter, + ) return {} instance = instance_data[active_inst[0]] if instance.tags: ec2_tags = instance.tags ec2_tags_list = {} - log.debug('External pillar %s, for minion id \'%s\', tags: %s', __name__, minion_id, instance.tags) + log.debug( + "External pillar %s, for minion id '%s', tags: %s", + __name__, + minion_id, + instance.tags, + ) if tag_list_key and isinstance(tag_list_key, list): for item in tag_list_key: if item in ec2_tags: @@ -246,5 +312,5 @@ def ext_pillar(minion_id, else: ec2_tags_list[item] = [] # always return a result - return {'ec2_tags': ec2_tags, 'ec2_tags_list': ec2_tags_list} + return {"ec2_tags": ec2_tags, "ec2_tags_list": ec2_tags_list} return {} diff --git a/salt/pillar/etcd_pillar.py b/salt/pillar/etcd_pillar.py index 4004b9f23bf..489545b6796 100644 --- a/salt/pillar/etcd_pillar.py +++ b/salt/pillar/etcd_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use etcd data as a Pillar source .. versionadded:: 2014.7.0 @@ -57,7 +57,7 @@ key with all minions but override its value for a specific minion:: etcdctl set /salt-shared/mykey my_value etcdctl set /salt-private/special_minion_id/mykey my_other_value -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -66,29 +66,28 @@ import logging # Import third party libs try: import salt.utils.etcd_util + HAS_LIBS = True except ImportError: HAS_LIBS = False -__virtualname__ = 'etcd' +__virtualname__ = "etcd" # Set up logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only return if python-etcd is installed - ''' + """ return __virtualname__ if HAS_LIBS else False -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - conf): - ''' +def ext_pillar(minion_id, pillar, conf): # pylint: disable=W0613 + """ Check etcd for all data - ''' + """ comps = conf.split() profile = None @@ -96,19 +95,17 @@ def ext_pillar(minion_id, profile = comps[0] client = salt.utils.etcd_util.get_conn(__opts__, profile) - path = '/' - if len(comps) > 1 and comps[1].startswith('root='): - path = comps[1].replace('root=', '') + path = "/" + if len(comps) > 1 and comps[1].startswith("root="): + path = comps[1].replace("root=", "") # put the minion's ID in the path if necessary - path %= { - 'minion_id': minion_id - } + path %= {"minion_id": minion_id} try: pillar = salt.utils.etcd_util.tree(client, path) except KeyError: - log.error('No such key in etcd profile %s: %s', profile, path) + log.error("No such key in etcd profile %s: %s", profile, path) pillar = {} return pillar diff --git a/salt/pillar/extra_minion_data_in_pillar.py b/salt/pillar/extra_minion_data_in_pillar.py index bbedec68666..850ec2ee8b6 100644 --- a/salt/pillar/extra_minion_data_in_pillar.py +++ b/salt/pillar/extra_minion_data_in_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Add all extra minion data to the pillar. :codeauthor: Alexandru.Bleotu@morganstanley.ms.com @@ -11,7 +11,7 @@ The keyword ``<all>`` includes all keys. Complete example in etc/salt/master ===================================== -.. code-block:: yaml +.. code-block:: none ext_pillar: - extra_minion_data_in_pillar: @@ -27,17 +27,17 @@ Complete example in etc/salt/master - extra_minion_data_in_pillar: include: <all> -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import logging +import logging # Set up logging log = logging.getLogger(__name__) -__virtualname__ = 'extra_minion_data_in_pillar' +__virtualname__ = "extra_minion_data_in_pillar" def __virtual__(): @@ -45,9 +45,8 @@ def __virtual__(): def ext_pillar(minion_id, pillar, include, extra_minion_data=None): - def get_subtree(key, source_dict): - ''' + """ Returns a subtree corresponfing to the specified key. key @@ -55,10 +54,10 @@ def ext_pillar(minion_id, pillar, include, extra_minion_data=None): source_dict Source dictionary - ''' + """ ret_dict = aux_dict = {} subtree = source_dict - subkeys = key.split(':') + subkeys = key.split(":") # Build an empty intermediate subtree following the subkeys for subkey in subkeys[:-1]: # The result will be built in aux_dict @@ -75,14 +74,14 @@ def ext_pillar(minion_id, pillar, include, extra_minion_data=None): aux_dict[subkeys[-1]] = subtree[subkeys[-1]] return ret_dict - log.trace('minion_id = %s', minion_id) - log.trace('include = %s', include) - log.trace('extra_minion_data = %s', extra_minion_data) + log.trace("minion_id = %s", minion_id) + log.trace("include = %s", include) + log.trace("extra_minion_data = %s", extra_minion_data) data = {} if not extra_minion_data: return {} - if include in ['*', '<all>']: + if include in ["*", "<all>"]: return extra_minion_data data = {} for key in include: diff --git a/salt/pillar/file_tree.py b/salt/pillar/file_tree.py index 71bd004b27c..2ea7e38cd7b 100644 --- a/salt/pillar/file_tree.py +++ b/salt/pillar/file_tree.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The ``file_tree`` external pillar allows values from all files in a directory tree to be imported as Pillar data. @@ -141,7 +141,7 @@ will result in the following pillar tree for minion with ID ``test-host``: .. note:: The leaf data in the example shown is the contents of the pillar files. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -151,13 +151,13 @@ import os # Import salt libs import salt.loader +import salt.template import salt.utils.dictupdate import salt.utils.files import salt.utils.minions import salt.utils.path import salt.utils.stringio import salt.utils.stringutils -import salt.template from salt.ext import six # Set up logging @@ -165,20 +165,20 @@ log = logging.getLogger(__name__) def _on_walk_error(err): - ''' + """ Log salt.utils.path.os_walk() error. - ''' - log.error('%s: %s', err.filename, err.strerror) + """ + log.error("%s: %s", err.filename, err.strerror) def _check_newline(prefix, file_name, keep_newline): - ''' + """ Return a boolean stating whether or not a file's trailing newline should be removed. To figure this out, first check if keep_newline is a boolean and if so, return its opposite. Otherwise, iterate over keep_newline and check if any of the patterns match the file path. If a match is found, return False, otherwise return True. - ''' + """ if isinstance(keep_newline, bool): return not keep_newline full_path = os.path.join(prefix, file_name) @@ -192,23 +192,25 @@ def _check_newline(prefix, file_name, keep_newline): return True -def _construct_pillar(top_dir, - follow_dir_links, - keep_newline=False, - render_default=None, - renderer_blacklist=None, - renderer_whitelist=None, - template=False): - ''' +def _construct_pillar( + top_dir, + follow_dir_links, + keep_newline=False, + render_default=None, + renderer_blacklist=None, + renderer_whitelist=None, + template=False, +): + """ Construct pillar from file tree. - ''' + """ pillar = {} renderers = salt.loader.render(__opts__, __salt__) norm_top_dir = os.path.normpath(top_dir) for dir_path, dir_names, file_names in salt.utils.path.os_walk( - top_dir, topdown=True, onerror=_on_walk_error, - followlinks=follow_dir_links): + top_dir, topdown=True, onerror=_on_walk_error, followlinks=follow_dir_links + ): # Find current path in pillar tree pillar_node = pillar norm_dir_path = os.path.normpath(dir_path) @@ -230,33 +232,32 @@ def _construct_pillar(top_dir, for file_name in file_names: file_path = os.path.join(dir_path, file_name) if not os.path.isfile(file_path): - log.error('file_tree: %s: not a regular file', file_path) + log.error("file_tree: %s: not a regular file", file_path) continue - contents = b'' + contents = b"" try: - with salt.utils.files.fopen(file_path, 'rb') as fhr: - buf = fhr.read(__opts__['file_buffer_size']) + with salt.utils.files.fopen(file_path, "rb") as fhr: + buf = fhr.read(__opts__["file_buffer_size"]) while buf: contents += buf - buf = fhr.read(__opts__['file_buffer_size']) - if contents.endswith(b'\n') \ - and _check_newline(prefix, - file_name, - keep_newline): + buf = fhr.read(__opts__["file_buffer_size"]) + if contents.endswith(b"\n") and _check_newline( + prefix, file_name, keep_newline + ): contents = contents[:-1] except (IOError, OSError) as exc: - log.error('file_tree: Error reading %s: %s', - file_path, - exc.strerror) + log.error("file_tree: Error reading %s: %s", file_path, exc.strerror) else: data = contents if template is True: - data = salt.template.compile_template_str(template=salt.utils.stringutils.to_unicode(contents), - renderers=renderers, - default=render_default, - blacklist=renderer_blacklist, - whitelist=renderer_whitelist) + data = salt.template.compile_template_str( + template=salt.utils.stringutils.to_unicode(contents), + renderers=renderers, + default=render_default, + blacklist=renderer_blacklist, + whitelist=renderer_whitelist, + ) if salt.utils.stringio.is_readable(data): pillar_node[file_name] = data.getvalue() else: @@ -265,17 +266,19 @@ def _construct_pillar(top_dir, return pillar -def ext_pillar(minion_id, - pillar, - root_dir=None, - follow_dir_links=False, - debug=False, - keep_newline=False, - render_default=None, - renderer_blacklist=None, - renderer_whitelist=None, - template=False): - ''' +def ext_pillar( + minion_id, + pillar, + root_dir=None, + follow_dir_links=False, + debug=False, + keep_newline=False, + render_default=None, + renderer_blacklist=None, + renderer_whitelist=None, + template=False, +): + """ Compile pillar data from the given ``root_dir`` specific to Nodegroup names and Minion IDs. @@ -400,25 +403,28 @@ def ext_pillar(minion_id, :param template: Enable templating of pillar files. Defaults to ``False``. - ''' + """ # Not used del pillar if not root_dir: - log.error('file_tree: no root_dir specified') + log.error("file_tree: no root_dir specified") return {} if not os.path.isabs(root_dir): - pillarenv = __opts__['pillarenv'] + pillarenv = __opts__["pillarenv"] if pillarenv is None: - log.error('file_tree: root_dir is relative but pillarenv is not set') + log.error("file_tree: root_dir is relative but pillarenv is not set") return {} - log.debug('file_tree: pillarenv = %s', pillarenv) + log.debug("file_tree: pillarenv = %s", pillarenv) - env_roots = __opts__['pillar_roots'].get(pillarenv, None) + env_roots = __opts__["pillar_roots"].get(pillarenv, None) if env_roots is None: - log.error('file_tree: root_dir is relative but no pillar_roots are specified ' - ' for pillarenv %s', pillarenv) + log.error( + "file_tree: root_dir is relative but no pillar_roots are specified " + " for pillarenv %s", + pillarenv, + ) return {} env_dirs = [] @@ -433,108 +439,114 @@ def ext_pillar(minion_id, result_pillar = {} for root in dirs: - dir_pillar = _ext_pillar(minion_id, - root, - follow_dir_links, - debug, - keep_newline, - render_default, - renderer_blacklist, - renderer_whitelist, - template) - result_pillar = salt.utils.dictupdate.merge(result_pillar, - dir_pillar, - strategy='recurse') + dir_pillar = _ext_pillar( + minion_id, + root, + follow_dir_links, + debug, + keep_newline, + render_default, + renderer_blacklist, + renderer_whitelist, + template, + ) + result_pillar = salt.utils.dictupdate.merge( + result_pillar, dir_pillar, strategy="recurse" + ) return result_pillar -def _ext_pillar(minion_id, - root_dir, - follow_dir_links, - debug, - keep_newline, - render_default, - renderer_blacklist, - renderer_whitelist, - template): - ''' +def _ext_pillar( + minion_id, + root_dir, + follow_dir_links, + debug, + keep_newline, + render_default, + renderer_blacklist, + renderer_whitelist, + template, +): + """ Compile pillar data for a single root_dir for the specified minion ID - ''' - log.debug('file_tree: reading %s', root_dir) + """ + log.debug("file_tree: reading %s", root_dir) if not os.path.isdir(root_dir): log.error( - 'file_tree: root_dir %s does not exist or is not a directory', - root_dir + "file_tree: root_dir %s does not exist or is not a directory", root_dir ) return {} if not isinstance(keep_newline, (bool, list)): log.error( - 'file_tree: keep_newline must be either True/False or a list ' - 'of file globs. Skipping this ext_pillar for root_dir %s', - root_dir + "file_tree: keep_newline must be either True/False or a list " + "of file globs. Skipping this ext_pillar for root_dir %s", + root_dir, ) return {} ngroup_pillar = {} - nodegroups_dir = os.path.join(root_dir, 'nodegroups') - if os.path.exists(nodegroups_dir) and len(__opts__.get('nodegroups', ())) > 0: - master_ngroups = __opts__['nodegroups'] + nodegroups_dir = os.path.join(root_dir, "nodegroups") + if os.path.exists(nodegroups_dir) and len(__opts__.get("nodegroups", ())) > 0: + master_ngroups = __opts__["nodegroups"] ext_pillar_dirs = os.listdir(nodegroups_dir) if len(ext_pillar_dirs) > 0: for nodegroup in ext_pillar_dirs: - if (os.path.isdir(nodegroups_dir) and - nodegroup in master_ngroups): + if os.path.isdir(nodegroups_dir) and nodegroup in master_ngroups: ckminions = salt.utils.minions.CkMinions(__opts__) _res = ckminions.check_minions( - master_ngroups[nodegroup], - 'compound') - match = _res['minions'] + master_ngroups[nodegroup], "compound" + ) + match = _res["minions"] if minion_id in match: ngroup_dir = os.path.join( - nodegroups_dir, six.text_type(nodegroup)) - ngroup_pillar = salt.utils.dictupdate.merge(ngroup_pillar, - _construct_pillar(ngroup_dir, - follow_dir_links, - keep_newline, - render_default, - renderer_blacklist, - renderer_whitelist, - template), - strategy='recurse' + nodegroups_dir, six.text_type(nodegroup) + ) + ngroup_pillar = salt.utils.dictupdate.merge( + ngroup_pillar, + _construct_pillar( + ngroup_dir, + follow_dir_links, + keep_newline, + render_default, + renderer_blacklist, + renderer_whitelist, + template, + ), + strategy="recurse", ) else: if debug is True: log.debug( - 'file_tree: no nodegroups found in file tree directory %s, skipping...', - ext_pillar_dirs + "file_tree: no nodegroups found in file tree directory %s, skipping...", + ext_pillar_dirs, ) else: if debug is True: - log.debug('file_tree: no nodegroups found in master configuration') + log.debug("file_tree: no nodegroups found in master configuration") - host_dir = os.path.join(root_dir, 'hosts', minion_id) + host_dir = os.path.join(root_dir, "hosts", minion_id) if not os.path.exists(host_dir): if debug is True: log.debug( - 'file_tree: no pillar data for minion %s found in file tree directory %s', + "file_tree: no pillar data for minion %s found in file tree directory %s", minion_id, - host_dir + host_dir, ) return ngroup_pillar if not os.path.isdir(host_dir): - log.error('file_tree: %s exists, but is not a directory', host_dir) + log.error("file_tree: %s exists, but is not a directory", host_dir) return ngroup_pillar - host_pillar = _construct_pillar(host_dir, - follow_dir_links, - keep_newline, - render_default, - renderer_blacklist, - renderer_whitelist, - template) - return salt.utils.dictupdate.merge(ngroup_pillar, - host_pillar, - strategy='recurse') + host_pillar = _construct_pillar( + host_dir, + follow_dir_links, + keep_newline, + render_default, + renderer_blacklist, + renderer_whitelist, + template, + ) + return salt.utils.dictupdate.merge(ngroup_pillar, host_pillar, strategy="recurse") diff --git a/salt/pillar/foreman.py b/salt/pillar/foreman.py index 85847b7a340..8d4579f90b3 100644 --- a/salt/pillar/foreman.py +++ b/salt/pillar/foreman.py @@ -1,5 +1,5 @@ # -*- strcoding: utf-8 -*- -''' +""" A module to pull data from Foreman via its API into the Pillar dictionary @@ -37,102 +37,102 @@ Further information can be found on `GitHub <https://github.com/theforeman/forem Module Documentation ==================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging from salt.ext import six + try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False -__opts__ = {'foreman.url': 'http://foreman/api', - 'foreman.user': 'admin', - 'foreman.password': 'changeme', - 'foreman.api': 2, - 'foreman.verifyssl': True, - 'foreman.certfile': None, - 'foreman.keyfile': None, - 'foreman.cafile': None, - 'foreman.lookup_parameters': True, - } +__opts__ = { + "foreman.url": "http://foreman/api", + "foreman.user": "admin", + "foreman.password": "changeme", + "foreman.api": 2, + "foreman.verifyssl": True, + "foreman.certfile": None, + "foreman.keyfile": None, + "foreman.cafile": None, + "foreman.lookup_parameters": True, +} # Set up logging log = logging.getLogger(__name__) # Declare virtualname -__virtualname__ = 'foreman' +__virtualname__ = "foreman" def __virtual__(): - ''' + """ Only return if all the modules are available - ''' + """ if not HAS_REQUESTS: return False return __virtualname__ -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - key=None, - only=()): - ''' +def ext_pillar(minion_id, pillar, key=None, only=()): # pylint: disable=W0613 + """ Read pillar data from Foreman via its API. - ''' - url = __opts__['foreman.url'] - user = __opts__['foreman.user'] - password = __opts__['foreman.password'] - api = __opts__['foreman.api'] - verify = __opts__['foreman.verifyssl'] - certfile = __opts__['foreman.certfile'] - keyfile = __opts__['foreman.keyfile'] - cafile = __opts__['foreman.cafile'] - lookup_parameters = __opts__['foreman.lookup_parameters'] + """ + url = __opts__["foreman.url"] + user = __opts__["foreman.user"] + password = __opts__["foreman.password"] + api = __opts__["foreman.api"] + verify = __opts__["foreman.verifyssl"] + certfile = __opts__["foreman.certfile"] + keyfile = __opts__["foreman.keyfile"] + cafile = __opts__["foreman.cafile"] + lookup_parameters = __opts__["foreman.lookup_parameters"] log.info("Querying Foreman at %r for information for %r", url, minion_id) try: # Foreman API version 1 is currently not supported if api != 2: - log.error('Foreman API v2 is supported only, please specify' - 'version 2 in your Salt master config') + log.error( + "Foreman API v2 is supported only, please specify" + "version 2 in your Salt master config" + ) raise Exception - headers = {'accept': 'version=' + six.text_type(api) + ',application/json'} + headers = {"accept": "version=" + six.text_type(api) + ",application/json"} if verify and cafile is not None: verify = cafile resp = requests.get( - url + '/hosts/' + minion_id, - auth=(user, password), - headers=headers, - verify=verify, - cert=(certfile, keyfile) - ) + url + "/hosts/" + minion_id, + auth=(user, password), + headers=headers, + verify=verify, + cert=(certfile, keyfile), + ) result = resp.json() - log.debug('Raw response of the Foreman request is %r', result) + log.debug("Raw response of the Foreman request is %r", result) if lookup_parameters: parameters = dict() - for param in result['all_parameters']: - parameters.update({param['name']: param['value']}) + for param in result["all_parameters"]: + parameters.update({param["name"]: param["value"]}) - result['parameters'] = parameters + result["parameters"] = parameters if only: result = dict((k, result[k]) for k in only if k in result) except Exception: # pylint: disable=broad-except - log.exception( - 'Could not fetch host data via Foreman API:' - ) + log.exception("Could not fetch host data via Foreman API:") return {} if key: diff --git a/salt/pillar/git_pillar.py b/salt/pillar/git_pillar.py index 7ce941eb596..d196c62c4d6 100644 --- a/salt/pillar/git_pillar.py +++ b/salt/pillar/git_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use a git repository as a Pillar source --------------------------------------- @@ -365,40 +365,63 @@ remotes. The update is handled within the global loop, hence git_pillar_update_interval: 120 -''' +.. _git-pillar-fallback: + +fallback +~~~~~~~~ + +.. versionadded:: sodium + +Setting ``fallback`` per-remote or global configuration parameter will map non-existing environments to a default branch. Example: + +.. code-block:: yaml + + ext_pillar: + - git: + - __env__ https://mydomain.tld/top.git + - all_saltenvs: master + - __env__ https://mydomain.tld/pillar-nginx.git: + - mountpoint: web/server/ + - fallback: master + - __env__ https://mydomain.tld/pillar-appdata.git: + - mountpoint: web/server/ + - fallback: master + +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import copy import logging +import salt.utils.dictupdate + # Import salt libs import salt.utils.gitfs -import salt.utils.dictupdate import salt.utils.stringutils import salt.utils.versions from salt.exceptions import FileserverConfigError -from salt.pillar import Pillar # Import third party libs from salt.ext import six +from salt.pillar import Pillar -PER_REMOTE_OVERRIDES = ('env', 'root', 'ssl_verify', 'refspecs') -PER_REMOTE_ONLY = ('name', 'mountpoint', 'all_saltenvs') -GLOBAL_ONLY = ('base', 'branch') +PER_REMOTE_OVERRIDES = ("env", "root", "ssl_verify", "refspecs", "fallback") +PER_REMOTE_ONLY = ("name", "mountpoint", "all_saltenvs") +GLOBAL_ONLY = ("base", "branch") # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'git' +__virtualname__ = "git" def __virtual__(): - ''' + """ Only load if gitpython is available - ''' - git_ext_pillars = [x for x in __opts__['ext_pillar'] if 'git' in x] + """ + git_ext_pillars = [x for x in __opts__["ext_pillar"] if "git" in x] if not git_ext_pillars: # No git external pillars were configured return False @@ -414,92 +437,95 @@ def __virtual__(): def ext_pillar(minion_id, pillar, *repos): # pylint: disable=unused-argument - ''' + """ Checkout the ext_pillar sources and compile the resulting pillar SLS - ''' + """ opts = copy.deepcopy(__opts__) - opts['pillar_roots'] = {} - opts['__git_pillar'] = True + opts["pillar_roots"] = {} + opts["__git_pillar"] = True git_pillar = salt.utils.gitfs.GitPillar( opts, repos, per_remote_overrides=PER_REMOTE_OVERRIDES, per_remote_only=PER_REMOTE_ONLY, - global_only=GLOBAL_ONLY) - if __opts__.get('__role') == 'minion': + global_only=GLOBAL_ONLY, + ) + if __opts__.get("__role") == "minion": # If masterless, fetch the remotes. We'll need to remove this once # we make the minion daemon able to run standalone. git_pillar.fetch_remotes() git_pillar.checkout() ret = {} - merge_strategy = __opts__.get( - 'pillar_source_merging_strategy', - 'smart' - ) - merge_lists = __opts__.get( - 'pillar_merge_lists', - False - ) + merge_strategy = __opts__.get("pillar_source_merging_strategy", "smart") + merge_lists = __opts__.get("pillar_merge_lists", False) for pillar_dir, env in six.iteritems(git_pillar.pillar_dirs): # Map env if env == '__env__' before checking the env value - if env == '__env__': - env = opts.get('pillarenv') \ - or opts.get('saltenv') \ - or opts.get('git_pillar_base') - log.debug('__env__ maps to %s', env) + if env == "__env__": + env = ( + opts.get("pillarenv") + or opts.get("saltenv") + or opts.get("git_pillar_base") + ) + log.debug("__env__ maps to %s", env) # If pillarenv is set, only grab pillars with that match pillarenv - if opts['pillarenv'] and env != opts['pillarenv']: + if opts["pillarenv"] and env != opts["pillarenv"]: log.debug( - 'env \'%s\' for pillar dir \'%s\' does not match ' - 'pillarenv \'%s\', skipping', - env, pillar_dir, opts['pillarenv'] + "env '%s' for pillar dir '%s' does not match " + "pillarenv '%s', skipping", + env, + pillar_dir, + opts["pillarenv"], ) continue if pillar_dir in git_pillar.pillar_linked_dirs: log.debug( - 'git_pillar is skipping processing on %s as it is a ' - 'mounted repo', pillar_dir + "git_pillar is skipping processing on %s as it is a " "mounted repo", + pillar_dir, ) continue else: log.debug( - 'git_pillar is processing pillar SLS from %s for pillar ' - 'env \'%s\'', pillar_dir, env + "git_pillar is processing pillar SLS from %s for pillar " "env '%s'", + pillar_dir, + env, ) pillar_roots = [pillar_dir] - if __opts__['git_pillar_includes']: + if __opts__["git_pillar_includes"]: # Add the rest of the pillar_dirs in this environment to the # list, excluding the current pillar_dir being processed. This # is because it was already specified above as the first in the # list, so that its top file is sourced from the correct # location and not from another git_pillar remote. pillar_roots.extend( - [d for (d, e) in six.iteritems(git_pillar.pillar_dirs) - if env == e and d != pillar_dir] + [ + d + for (d, e) in six.iteritems(git_pillar.pillar_dirs) + if env == e and d != pillar_dir + ] ) - opts['pillar_roots'] = {env: pillar_roots} + opts["pillar_roots"] = {env: pillar_roots} local_pillar = Pillar(opts, __grains__, minion_id, env) ret = salt.utils.dictupdate.merge( ret, local_pillar.compile_pillar(ext=False), strategy=merge_strategy, - merge_lists=merge_lists + merge_lists=merge_lists, ) return ret -def _extract_key_val(kv, delimiter='='): - '''Extract key and value from key=val string. +def _extract_key_val(kv, delimiter="="): + """Extract key and value from key=val string. Example: >>> _extract_key_val('foo=bar') ('foo', 'bar') - ''' + """ pieces = kv.split(delimiter) key = pieces[0] val = delimiter.join(pieces[1:]) diff --git a/salt/pillar/gpg.py b/salt/pillar/gpg.py index 2a0b8ca0aeb..b5c8b068e30 100644 --- a/salt/pillar/gpg.py +++ b/salt/pillar/gpg.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Decrypt pillar data through the builtin GPG renderer In most cases, you'll want to make this the last external pillar used. For @@ -15,9 +15,10 @@ this: Set ``gpg_keydir`` in your config to adjust the homedir the renderer uses. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import salt.loader diff --git a/salt/pillar/hg_pillar.py b/salt/pillar/hg_pillar.py index e7badbdda79..030d28b4c0b 100644 --- a/salt/pillar/hg_pillar.py +++ b/salt/pillar/hg_pillar.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014 Floris Bruynooghe <flub@devork.be> +#  Copyright (C) 2014 Floris Bruynooghe <flub@devork.be> -r''' +r""" Use remote Mercurial repository as a Pillar source. .. versionadded:: 2015.8.0 @@ -16,10 +16,11 @@ This external Pillar source can be configured in the master config file as such: ext_pillar: - hg: ssh://hg@example.co/user/repo -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import copy import hashlib import logging @@ -31,13 +32,14 @@ import salt.utils.stringutils # Import Third Party Libs from salt.ext import six + try: import hglib except ImportError: hglib = None -__virtualname__ = 'hg' +__virtualname__ = "hg" log = logging.getLogger(__name__) @@ -46,62 +48,62 @@ __opts__ = {} def __virtual__(): - ''' + """ Only load if hglib is available. - ''' - ext_pillar_sources = [x for x in __opts__.get('ext_pillar', [])] - if not any(['hg' in x for x in ext_pillar_sources]): + """ + ext_pillar_sources = [x for x in __opts__.get("ext_pillar", [])] + if not any(["hg" in x for x in ext_pillar_sources]): return False if not hglib: - log.error('hglib not present') + log.error("hglib not present") return False return __virtualname__ def __init__(__opts__): - ''' + """ Initialise This is called every time a minion calls this external pillar. - ''' + """ -def ext_pillar(minion_id, pillar, repo, branch='default', root=None): - ''' +def ext_pillar(minion_id, pillar, repo, branch="default", root=None): + """ Extract pillar from an hg repository - ''' + """ with Repo(repo) as repo: repo.update(branch) - envname = 'base' if branch == 'default' else branch + envname = "base" if branch == "default" else branch if root: path = os.path.normpath(os.path.join(repo.working_dir, root)) else: path = repo.working_dir opts = copy.deepcopy(__opts__) - opts['pillar_roots'][envname] = [path] + opts["pillar_roots"][envname] = [path] pil = salt.pillar.Pillar(opts, __grains__, minion_id, envname) return pil.compile_pillar(ext=False) def update(repo_uri): - ''' + """ Execute an hg pull on all the repos - ''' + """ with Repo(repo_uri) as repo: repo.pull() class Repo(object): - ''' + """ Deal with remote hg (mercurial) repository for Pillar - ''' + """ def __init__(self, repo_uri): - ''' Initialize a hg repo (or open it if it already exists) ''' + """ Initialize a hg repo (or open it if it already exists) """ self.repo_uri = repo_uri - cachedir = os.path.join(__opts__['cachedir'], 'hg_pillar') - hash_type = getattr(hashlib, __opts__.get('hash_type', 'md5')) + cachedir = os.path.join(__opts__["cachedir"], "hg_pillar") + hash_type = getattr(hashlib, __opts__.get("hash_type", "md5")) if six.PY2: repo_hash = hash_type(repo_uri).hexdigest() else: @@ -114,22 +116,22 @@ class Repo(object): self.repo = hglib.open(self.working_dir) def pull(self): - log.debug('Updating hg repo from hg_pillar module (pull)') + log.debug("Updating hg repo from hg_pillar module (pull)") self.repo.pull() - def update(self, branch='default'): - ''' + def update(self, branch="default"): + """ Ensure we are using the latest revision in the hg repository - ''' - log.debug('Updating hg repo from hg_pillar module (pull)') + """ + log.debug("Updating hg repo from hg_pillar module (pull)") self.repo.pull() - log.debug('Updating hg repo from hg_pillar module (update)') + log.debug("Updating hg repo from hg_pillar module (update)") self.repo.update(branch, clean=True) def close(self): - ''' + """ Cleanup mercurial command server - ''' + """ self.repo.close() def __enter__(self): diff --git a/salt/pillar/hiera.py b/salt/pillar/hiera.py index eea64bfd082..b98c862d8d2 100644 --- a/salt/pillar/hiera.py +++ b/salt/pillar/hiera.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Use hiera data as a Pillar source -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -14,31 +15,30 @@ import salt.utils.yaml # Import 3rd-party libs from salt.ext import six - # Set up logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only return if hiera is installed - ''' - return 'hiera' if salt.utils.path.which('hiera') else False + """ + return "hiera" if salt.utils.path.which("hiera") else False -def ext_pillar(minion_id, # pylint: disable=W0613 - pillar, # pylint: disable=W0613 - conf): - ''' +def ext_pillar( + minion_id, pillar, conf # pylint: disable=W0613 # pylint: disable=W0613 +): + """ Execute hiera and return the data - ''' - cmd = 'hiera -c {0}'.format(conf) + """ + cmd = "hiera -c {0}".format(conf) for key, val in six.iteritems(__grains__): if isinstance(val, six.string_types): - cmd += ' {0}=\'{1}\''.format(key, val) + cmd += " {0}='{1}'".format(key, val) try: - data = salt.utils.yaml.safe_load(__salt__['cmd.run'](cmd)) + data = salt.utils.yaml.safe_load(__salt__["cmd.run"](cmd)) except Exception: # pylint: disable=broad-except - log.critical('Hiera YAML data failed to parse from conf %s', conf) + log.critical("Hiera YAML data failed to parse from conf %s", conf) return {} return data diff --git a/salt/pillar/http_json.py b/salt/pillar/http_json.py index 6943280218d..ba3ec4cc6fd 100644 --- a/salt/pillar/http_json.py +++ b/salt/pillar/http_json.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A module that adds data to the Pillar structure retrieved by an http request @@ -39,17 +39,18 @@ in <> brackets) in the url in order to populate pillar data based on the grain v Module Documentation ==================== -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import re from salt.ext import six # Import Salt libs -from salt.ext.six.moves.urllib.parse import quote as _quote # pylint: disable=no-name-in-module +from salt.ext.six.moves.urllib.parse import quote as _quote log = logging.getLogger(__name__) @@ -58,11 +59,8 @@ def __virtual__(): return True -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - url, - with_grains=False): - ''' +def ext_pillar(minion_id, pillar, url, with_grains=False): # pylint: disable=W0613 + """ Read pillar data from HTTP response. :param str url: Url to request. @@ -70,35 +68,35 @@ def ext_pillar(minion_id, :return: A dictionary of the pillar data to add. :rtype: dict - ''' + """ - url = url.replace('%s', _quote(minion_id)) + url = url.replace("%s", _quote(minion_id)) - grain_pattern = r'<(?P<grain_name>.*?)>' + grain_pattern = r"<(?P<grain_name>.*?)>" if with_grains: # Get the value of the grain and substitute each grain # name for the url-encoded version of its grain value. for match in re.finditer(grain_pattern, url): - grain_name = match.group('grain_name') - grain_value = __salt__['grains.get'](grain_name, None) + grain_name = match.group("grain_name") + grain_value = __salt__["grains.get"](grain_name, None) if not grain_value: log.error("Unable to get minion '%s' grain: %s", minion_id, grain_name) return {} grain_value = _quote(six.text_type(grain_value)) - url = re.sub('<{0}>'.format(grain_name), grain_value, url) + url = re.sub("<{0}>".format(grain_name), grain_value, url) - log.debug('Getting url: %s', url) - data = __salt__['http.query'](url=url, decode=True, decode_type='json') + log.debug("Getting url: %s", url) + data = __salt__["http.query"](url=url, decode=True, decode_type="json") - if 'dict' in data: - return data['dict'] + if "dict" in data: + return data["dict"] log.error("Error on minion '%s' http query: %s\nMore Info:\n", minion_id, url) for key in data: - log.error('%s: %s', key, data[key]) + log.error("%s: %s", key, data[key]) return {} diff --git a/salt/pillar/http_yaml.py b/salt/pillar/http_yaml.py index b47f20b2186..7b4565585e5 100644 --- a/salt/pillar/http_yaml.py +++ b/salt/pillar/http_yaml.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A module that adds data to the Pillar structure retrieved by an http request @@ -39,17 +39,18 @@ in <> brackets) in the url in order to populate pillar data based on the grain v Module Documentation ==================== -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import re from salt.ext import six # Import Salt libs -from salt.ext.six.moves.urllib.parse import quote as _quote # pylint: disable=no-name-in-module +from salt.ext.six.moves.urllib.parse import quote as _quote log = logging.getLogger(__name__) @@ -58,11 +59,8 @@ def __virtual__(): return True -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - url, - with_grains=False): - ''' +def ext_pillar(minion_id, pillar, url, with_grains=False): # pylint: disable=W0613 + """ Read pillar data from HTTP response. :param str url: Url to request. @@ -70,35 +68,35 @@ def ext_pillar(minion_id, :return: A dictionary of the pillar data to add. :rtype: dict - ''' + """ - url = url.replace('%s', _quote(minion_id)) + url = url.replace("%s", _quote(minion_id)) - grain_pattern = r'<(?P<grain_name>.*?)>' + grain_pattern = r"<(?P<grain_name>.*?)>" if with_grains: # Get the value of the grain and substitute each grain # name for the url-encoded version of its grain value. for match in re.finditer(grain_pattern, url): - grain_name = match.group('grain_name') - grain_value = __salt__['grains.get'](grain_name, None) + grain_name = match.group("grain_name") + grain_value = __salt__["grains.get"](grain_name, None) if not grain_value: log.error("Unable to get minion '%s' grain: %s", minion_id, grain_name) return {} grain_value = _quote(six.text_type(grain_value)) - url = re.sub('<{0}>'.format(grain_name), grain_value, url) + url = re.sub("<{0}>".format(grain_name), grain_value, url) - log.debug('Getting url: %s', url) - data = __salt__['http.query'](url=url, decode=True, decode_type='yaml') + log.debug("Getting url: %s", url) + data = __salt__["http.query"](url=url, decode=True, decode_type="yaml") - if 'dict' in data: - return data['dict'] + if "dict" in data: + return data["dict"] log.error("Error on minion '%s' http query: %s\nMore Info:\n", minion_id, url) for key in data: - log.error('%s: %s', key, data[key]) + log.error("%s: %s", key, data[key]) return {} diff --git a/salt/pillar/libvirt.py b/salt/pillar/libvirt.py index a6b2eaef474..d482f809951 100644 --- a/salt/pillar/libvirt.py +++ b/salt/pillar/libvirt.py @@ -1,15 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Load up the libvirt keys into Pillar for a given minion if said keys have been generated using the libvirt key runner :depends: certtool -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Don't "fix" the above docstring to put it on two lines, as the sphinx -# autosummary pulls only the first line for its description. - # Import python libs import os import subprocess @@ -19,128 +16,120 @@ import salt.utils.files import salt.utils.path import salt.utils.stringutils +# Don't "fix" the above docstring to put it on two lines, as the sphinx +# autosummary pulls only the first line for its description. + def __virtual__(): - return salt.utils.path.which('certtool') is not None + return salt.utils.path.which("certtool") is not None -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - command): # pylint: disable=W0613 - ''' +def ext_pillar( + minion_id, pillar, command # pylint: disable=W0613 +): # pylint: disable=W0613 + """ Read in the generated libvirt keys - ''' - key_dir = os.path.join( - __opts__['pki_dir'], - 'libvirt', - minion_id) - cacert = os.path.join(__opts__['pki_dir'], - 'libvirt', - 'cacert.pem') + """ + key_dir = os.path.join(__opts__["pki_dir"], "libvirt", minion_id) + cacert = os.path.join(__opts__["pki_dir"], "libvirt", "cacert.pem") if not os.path.isdir(key_dir): # No keys have been generated - gen_hyper_keys(minion_id, - pillar.get('ext_pillar_virt.country', 'US'), - pillar.get('ext_pillar_virt.st', 'Utah'), - pillar.get('ext_pillar_virt.locality', - 'Salt Lake City'), - pillar.get('ext_pillar_virt.organization', 'Salted'), - pillar.get('ext_pillar_virt.expiration_days', '365') - ) + gen_hyper_keys( + minion_id, + pillar.get("ext_pillar_virt.country", "US"), + pillar.get("ext_pillar_virt.st", "Utah"), + pillar.get("ext_pillar_virt.locality", "Salt Lake City"), + pillar.get("ext_pillar_virt.organization", "Salted"), + pillar.get("ext_pillar_virt.expiration_days", "365"), + ) ret = {} for key in os.listdir(key_dir): - if not key.endswith('.pem'): + if not key.endswith(".pem"): continue fn_ = os.path.join(key_dir, key) - with salt.utils.files.fopen(fn_, 'r') as fp_: - ret['libvirt.{0}'.format(key)] = \ - salt.utils.stringutils.to_unicode(fp_.read()) - with salt.utils.files.fopen(cacert, 'r') as fp_: - ret['libvirt.cacert.pem'] = \ - salt.utils.stringutils.to_unicode(fp_.read()) + with salt.utils.files.fopen(fn_, "r") as fp_: + ret["libvirt.{0}".format(key)] = salt.utils.stringutils.to_unicode( + fp_.read() + ) + with salt.utils.files.fopen(cacert, "r") as fp_: + ret["libvirt.cacert.pem"] = salt.utils.stringutils.to_unicode(fp_.read()) return ret -def gen_hyper_keys(minion_id, - country='US', - state='Utah', - locality='Salt Lake City', - organization='Salted', - expiration_days='365'): - ''' +def gen_hyper_keys( + minion_id, + country="US", + state="Utah", + locality="Salt Lake City", + organization="Salted", + expiration_days="365", +): + """ Generate the keys to be used by libvirt hypervisors, this routine gens the keys and applies them to the pillar for the hypervisor minions - ''' - key_dir = os.path.join( - __opts__['pki_dir'], - 'libvirt') + """ + key_dir = os.path.join(__opts__["pki_dir"], "libvirt") if not os.path.isdir(key_dir): os.makedirs(key_dir) - cakey = os.path.join(key_dir, 'cakey.pem') - cacert = os.path.join(key_dir, 'cacert.pem') - cainfo = os.path.join(key_dir, 'ca.info') + cakey = os.path.join(key_dir, "cakey.pem") + cacert = os.path.join(key_dir, "cacert.pem") + cainfo = os.path.join(key_dir, "ca.info") if not os.path.isfile(cainfo): - with salt.utils.files.fopen(cainfo, 'w+') as fp_: - fp_.write('cn = salted\nca\ncert_signing_key') + with salt.utils.files.fopen(cainfo, "w+") as fp_: + fp_.write("cn = salted\nca\ncert_signing_key") if not os.path.isfile(cakey): - subprocess.call( - 'certtool --generate-privkey > {0}'.format(cakey), - shell=True) + subprocess.call("certtool --generate-privkey > {0}".format(cakey), shell=True) if not os.path.isfile(cacert): - cmd = ('certtool --generate-self-signed --load-privkey {0} ' - '--template {1} --outfile {2}').format(cakey, cainfo, cacert) + cmd = ( + "certtool --generate-self-signed --load-privkey {0} " + "--template {1} --outfile {2}" + ).format(cakey, cainfo, cacert) subprocess.call(cmd, shell=True) sub_dir = os.path.join(key_dir, minion_id) if not os.path.isdir(sub_dir): os.makedirs(sub_dir) - priv = os.path.join(sub_dir, 'serverkey.pem') - cert = os.path.join(sub_dir, 'servercert.pem') - srvinfo = os.path.join(sub_dir, 'server.info') - cpriv = os.path.join(sub_dir, 'clientkey.pem') - ccert = os.path.join(sub_dir, 'clientcert.pem') - clientinfo = os.path.join(sub_dir, 'client.info') + priv = os.path.join(sub_dir, "serverkey.pem") + cert = os.path.join(sub_dir, "servercert.pem") + srvinfo = os.path.join(sub_dir, "server.info") + cpriv = os.path.join(sub_dir, "clientkey.pem") + ccert = os.path.join(sub_dir, "clientcert.pem") + clientinfo = os.path.join(sub_dir, "client.info") if not os.path.isfile(srvinfo): - with salt.utils.files.fopen(srvinfo, 'w+') as fp_: + with salt.utils.files.fopen(srvinfo, "w+") as fp_: infodat = salt.utils.stringutils.to_str( - 'organization = salted\ncn = {0}\ntls_www_server' - '\nencryption_key\nsigning_key' - '\ndigitalSignature\nexpiration_days = {1}'.format( - __grains__['fqdn'], expiration_days + "organization = salted\ncn = {0}\ntls_www_server" + "\nencryption_key\nsigning_key" + "\ndigitalSignature\nexpiration_days = {1}".format( + __grains__["fqdn"], expiration_days ) ) fp_.write(infodat) if not os.path.isfile(priv): - subprocess.call( - 'certtool --generate-privkey > {0}'.format(priv), - shell=True) + subprocess.call("certtool --generate-privkey > {0}".format(priv), shell=True) if not os.path.isfile(cert): - cmd = ('certtool --generate-certificate --load-privkey {0} ' - '--load-ca-certificate {1} --load-ca-privkey {2} ' - '--template {3} --outfile {4}' - ).format(priv, cacert, cakey, srvinfo, cert) + cmd = ( + "certtool --generate-certificate --load-privkey {0} " + "--load-ca-certificate {1} --load-ca-privkey {2} " + "--template {3} --outfile {4}" + ).format(priv, cacert, cakey, srvinfo, cert) subprocess.call(cmd, shell=True) if not os.path.isfile(clientinfo): - with salt.utils.files.fopen(clientinfo, 'w+') as fp_: + with salt.utils.files.fopen(clientinfo, "w+") as fp_: infodat = salt.utils.stringutils.to_str( - 'country = {0}\nstate = {1}\nlocality = {2}\n' - 'organization = {3}\ncn = {4}\n' - 'tls_www_client\nencryption_key\nsigning_key\n' - 'digitalSignature'.format( - country, - state, - locality, - organization, - __grains__['fqdn'] + "country = {0}\nstate = {1}\nlocality = {2}\n" + "organization = {3}\ncn = {4}\n" + "tls_www_client\nencryption_key\nsigning_key\n" + "digitalSignature".format( + country, state, locality, organization, __grains__["fqdn"] ) ) fp_.write(infodat) if not os.path.isfile(cpriv): - subprocess.call( - 'certtool --generate-privkey > {0}'.format(cpriv), - shell=True) + subprocess.call("certtool --generate-privkey > {0}".format(cpriv), shell=True) if not os.path.isfile(ccert): - cmd = ('certtool --generate-certificate --load-privkey {0} ' - '--load-ca-certificate {1} --load-ca-privkey {2} ' - '--template {3} --outfile {4}' - ).format(cpriv, cacert, cakey, clientinfo, ccert) + cmd = ( + "certtool --generate-certificate --load-privkey {0} " + "--load-ca-certificate {1} --load-ca-privkey {2} " + "--template {3} --outfile {4}" + ).format(cpriv, cacert, cakey, clientinfo, ccert) subprocess.call(cmd, shell=True) diff --git a/salt/pillar/makostack.py b/salt/pillar/makostack.py index 4a011ffe6a6..56a3684c44a 100644 --- a/salt/pillar/makostack.py +++ b/salt/pillar/makostack.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Simple and flexible YAML ext_pillar which can read pillar from within pillar. .. versionadded:: 2016.3.0 @@ -371,13 +371,14 @@ You can also select a custom merging strategy using a ``__`` object in a list: | - tom | - __: overwrite | - mat | | - root | - mat | | +----------------+-------------------------+-------------------------+ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import functools -import os import logging +import os # Import Salt libs import salt.utils.yaml @@ -388,21 +389,22 @@ from salt.ext import six try: from mako.lookup import TemplateLookup from mako import exceptions + HAS_MAKO = True except ImportError: HAS_MAKO = False log = logging.getLogger(__name__) -strategies = ('overwrite', 'merge-first', 'merge-last', 'remove') +strategies = ("overwrite", "merge-first", "merge-last", "remove") -__virtualname__ = 'makostack' +__virtualname__ = "makostack" # Only load in this module if the EC2 configurations are in place def __virtual__(): - ''' + """ Set up the libcloud functions and check for EC2 configurations - ''' + """ if HAS_MAKO is True: return __virtualname__ return False @@ -410,25 +412,28 @@ def __virtual__(): def ext_pillar(minion_id, pillar, *args, **kwargs): import salt.utils.data + stack = {} stack_config_files = list(args) traverse = { - 'pillar': functools.partial(salt.utils.data.traverse_dict_and_list, pillar), - 'grains': functools.partial(salt.utils.data.traverse_dict_and_list, __grains__), - 'opts': functools.partial(salt.utils.data.traverse_dict_and_list, __opts__), - } + "pillar": functools.partial(salt.utils.data.traverse_dict_and_list, pillar), + "grains": functools.partial(salt.utils.data.traverse_dict_and_list, __grains__), + "opts": functools.partial(salt.utils.data.traverse_dict_and_list, __opts__), + } for matcher, matchs in six.iteritems(kwargs): - t, matcher = matcher.split(':', 1) + t, matcher = matcher.split(":", 1) if t not in traverse: - raise Exception('Unknown traverse option "{0}", ' - 'should be one of {1}'.format(t, traverse.keys())) + raise Exception( + 'Unknown traverse option "{0}", ' + "should be one of {1}".format(t, traverse.keys()) + ) cfgs = matchs.get(traverse[t](matcher, None), []) if not isinstance(cfgs, list): cfgs = [cfgs] stack_config_files += cfgs for cfg in stack_config_files: - if ':' in cfg: - cfg, namespace = cfg.split(':', 1) + if ":" in cfg: + cfg, namespace = cfg.split(":", 1) else: namespace = None if not os.path.isfile(cfg): @@ -441,31 +446,38 @@ def ext_pillar(minion_id, pillar, *args, **kwargs): def _process_stack_cfg(cfg, stack, minion_id, pillar, namespace): basedir, filename = os.path.split(cfg) lookup = TemplateLookup(directories=[basedir]) - tops = lookup.get_template(filename).render(__opts__=__opts__, - __salt__=__salt__, - __grains__=__grains__, - minion_id=minion_id, - pillar=pillar, stack=stack) + tops = lookup.get_template(filename).render( + __opts__=__opts__, + __salt__=__salt__, + __grains__=__grains__, + minion_id=minion_id, + pillar=pillar, + stack=stack, + ) for path in _parse_top_cfg(tops): dirs = [basedir] - if path.startswith('/'): - dirs += ['/'] + if path.startswith("/"): + dirs += ["/"] lookup = TemplateLookup(directories=dirs) try: - p = lookup.get_template(path).render(__opts__=__opts__, - __salt__=__salt__, - __grains__=__grains__, - minion_id=minion_id, - pillar=pillar, stack=stack) + p = lookup.get_template(path).render( + __opts__=__opts__, + __salt__=__salt__, + __grains__=__grains__, + minion_id=minion_id, + pillar=pillar, + stack=stack, + ) obj = salt.utils.yaml.safe_load(p) if not isinstance(obj, dict): log.info( 'Ignoring Stack template "%s": Can\'t parse as a valid ' - 'yaml dictionary', path + "yaml dictionary", + path, ) continue if namespace: - for sub in namespace.split(':')[::-1]: + for sub in namespace.split(":")[::-1]: obj = {sub: obj} stack = _merge_dict(stack, obj) log.info('Stack template "%s" parsed', path) @@ -474,7 +486,7 @@ def _process_stack_cfg(cfg, stack, minion_id, pillar, namespace): continue except Exception as e: # pylint: disable=broad-except log.info('Ignoring Stack template "%s":', path) - log.info('%s', exceptions.text_error_template().render()) + log.info("%s", exceptions.text_error_template().render()) continue return stack @@ -482,29 +494,29 @@ def _process_stack_cfg(cfg, stack, minion_id, pillar, namespace): def _cleanup(obj): if obj: if isinstance(obj, dict): - obj.pop('__', None) + obj.pop("__", None) for k, v in six.iteritems(obj): obj[k] = _cleanup(v) - elif isinstance(obj, list) and isinstance(obj[0], dict) \ - and '__' in obj[0]: + elif isinstance(obj, list) and isinstance(obj[0], dict) and "__" in obj[0]: del obj[0] return obj def _merge_dict(stack, obj): - strategy = obj.pop('__', 'merge-last') + strategy = obj.pop("__", "merge-last") if strategy not in strategies: - raise Exception('Unknown strategy "{0}", should be one of {1}'.format( - strategy, strategies)) - if strategy == 'overwrite': + raise Exception( + 'Unknown strategy "{0}", should be one of {1}'.format(strategy, strategies) + ) + if strategy == "overwrite": return _cleanup(obj) else: for k, v in six.iteritems(obj): - if strategy == 'remove': + if strategy == "remove": stack.pop(k, None) continue if k in stack: - if strategy == 'merge-first': + if strategy == "merge-first": # merge-first is same as merge-last but the other way round # so let's switch stack[k] and v stack_k = stack[k] @@ -512,8 +524,7 @@ def _merge_dict(stack, obj): v = stack_k if type(stack[k]) != type(v): log.debug( - 'Force overwrite, types differ: \'%s\' != \'%s\'', - stack[k], v + "Force overwrite, types differ: '%s' != '%s'", stack[k], v ) stack[k] = _cleanup(v) elif isinstance(v, dict): @@ -528,27 +539,28 @@ def _merge_dict(stack, obj): def _merge_list(stack, obj): - strategy = 'merge-last' - if obj and isinstance(obj[0], dict) and '__' in obj[0]: - strategy = obj[0]['__'] + strategy = "merge-last" + if obj and isinstance(obj[0], dict) and "__" in obj[0]: + strategy = obj[0]["__"] del obj[0] if strategy not in strategies: - raise Exception('Unknown strategy "{0}", should be one of {1}'.format( - strategy, strategies)) - if strategy == 'overwrite': + raise Exception( + 'Unknown strategy "{0}", should be one of {1}'.format(strategy, strategies) + ) + if strategy == "overwrite": return obj - elif strategy == 'remove': + elif strategy == "remove": return [item for item in stack if item not in obj] - elif strategy == 'merge-first': + elif strategy == "merge-first": return obj + stack else: return stack + obj def _parse_top_cfg(content): - ''' + """ Allow top_cfg to be YAML - ''' + """ try: obj = salt.utils.yaml.safe_load(content) if isinstance(obj, list): diff --git a/salt/pillar/mongo.py b/salt/pillar/mongo.py index 9d07c773c4f..fde43bb8067 100644 --- a/salt/pillar/mongo.py +++ b/salt/pillar/mongo.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Read Pillar data from a mongodb collection :depends: pymongo (for salt-master) @@ -53,7 +53,7 @@ dict in your SLS templates. Module Documentation ==================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -62,38 +62,44 @@ import re # Import third party libs from salt.ext import six + try: import pymongo + HAS_PYMONGO = True except ImportError: HAS_PYMONGO = False -__opts__ = {'mongo.db': 'salt', - 'mongo.host': 'salt', - 'mongo.password': '', - 'mongo.port': 27017, - 'mongo.user': ''} +__opts__ = { + "mongo.db": "salt", + "mongo.host": "salt", + "mongo.password": "", + "mongo.port": 27017, + "mongo.user": "", +} def __virtual__(): if not HAS_PYMONGO: return False - return 'mongo' + return "mongo" # Set up logging log = logging.getLogger(__name__) -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - collection='pillar', - id_field='_id', - re_pattern=None, - re_replace='', - fields=None): - ''' +def ext_pillar( + minion_id, + pillar, # pylint: disable=W0613 + collection="pillar", + id_field="_id", + re_pattern=None, + re_replace="", + fields=None, +): + """ Connect to a mongo database and read per-node pillar information. Parameters: @@ -115,20 +121,20 @@ def ext_pillar(minion_id, entire document, the ``_id`` field will be converted to string. Be careful with other fields in the document as they must be string serializable. Defaults to ``None``. - ''' - host = __opts__['mongo.host'] - port = __opts__['mongo.port'] - log.info('connecting to %s:%s for mongo ext_pillar', host, port) + """ + host = __opts__["mongo.host"] + port = __opts__["mongo.port"] + log.info("connecting to %s:%s for mongo ext_pillar", host, port) conn = pymongo.MongoClient(host, port) - log.debug('using database \'%s\'', __opts__['mongo.db']) - mdb = conn[__opts__['mongo.db']] + log.debug("using database '%s'", __opts__["mongo.db"]) + mdb = conn[__opts__["mongo.db"]] - user = __opts__.get('mongo.user') - password = __opts__.get('mongo.password') + user = __opts__.get("mongo.user") + password = __opts__.get("mongo.password") if user and password: - log.debug('authenticating as \'%s\'', user) + log.debug("authenticating as '%s'", user) mdb.authenticate(user, password) # Do the regex string replacement on the minion id @@ -136,30 +142,25 @@ def ext_pillar(minion_id, minion_id = re.sub(re_pattern, re_replace, minion_id) log.info( - 'ext_pillar.mongo: looking up pillar def for {\'%s\': \'%s\'} ' - 'in mongo', id_field, minion_id + "ext_pillar.mongo: looking up pillar def for {'%s': '%s'} " "in mongo", + id_field, + minion_id, ) result = mdb[collection].find_one({id_field: minion_id}, projection=fields) if result: if fields: - log.debug( - 'ext_pillar.mongo: found document, returning fields \'%s\'', - fields - ) + log.debug("ext_pillar.mongo: found document, returning fields '%s'", fields) else: - log.debug('ext_pillar.mongo: found document, returning whole doc') - if '_id' in result: + log.debug("ext_pillar.mongo: found document, returning whole doc") + if "_id" in result: # Converting _id to a string # will avoid the most common serialization error cases, but DBRefs # and whatnot will still cause problems. - result['_id'] = six.text_type(result['_id']) + result["_id"] = six.text_type(result["_id"]) return result else: # If we can't find the minion the database it's not necessarily an # error. - log.debug( - 'ext_pillar.mongo: no document found in collection %s', - collection - ) + log.debug("ext_pillar.mongo: no document found in collection %s", collection) return {} diff --git a/salt/pillar/mysql.py b/salt/pillar/mysql.py index debe4d8b5bb..3667eccd3c9 100644 --- a/salt/pillar/mysql.py +++ b/salt/pillar/mysql.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Retrieve Pillar data by doing a MySQL query MariaDB provides Python support through the MySQL Python package. @@ -44,12 +44,13 @@ Complete example depth: 5 as_list: True with_lists: [1,3] -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs from contextlib import contextmanager -import logging # Import Salt libs from salt.pillar.sql_base import SqlBaseExtPillar @@ -67,6 +68,7 @@ except ImportError: try: # MySQLdb import failed, try to import PyMySQL import pymysql + pymysql.install_as_MySQLdb() import MySQLdb import MySQLdb.cursors @@ -76,35 +78,38 @@ except ImportError: def __virtual__(): - ''' + """ Confirm that a python mysql client is installed. - ''' - return bool(MySQLdb), 'No python mysql client installed.' if MySQLdb is None else '' + """ + return bool(MySQLdb), "No python mysql client installed." if MySQLdb is None else "" class MySQLExtPillar(SqlBaseExtPillar): - ''' + """ This class receives and processes the database rows from MySQL. - ''' + """ + @classmethod def _db_name(cls): - return 'MySQL' + return "MySQL" def _get_options(self): - ''' + """ Returns options used for the MySQL connection. - ''' - defaults = {'host': 'localhost', - 'user': 'salt', - 'pass': 'salt', - 'db': 'salt', - 'port': 3306, - 'ssl': {}} + """ + defaults = { + "host": "localhost", + "user": "salt", + "pass": "salt", + "db": "salt", + "port": 3306, + "ssl": {}, + } _options = {} - _opts = __opts__.get('mysql', {}) + _opts = __opts__.get("mysql", {}) for attr in defaults: if attr not in _opts: - log.debug('Using default for MySQL %s', attr) + log.debug("Using default for MySQL %s", attr) _options[attr] = defaults[attr] continue _options[attr] = _opts[attr] @@ -112,36 +117,36 @@ class MySQLExtPillar(SqlBaseExtPillar): @contextmanager def _get_cursor(self): - ''' + """ Yield a MySQL cursor - ''' + """ _options = self._get_options() - conn = MySQLdb.connect(host=_options['host'], - user=_options['user'], - passwd=_options['pass'], - db=_options['db'], port=_options['port'], - ssl=_options['ssl']) + conn = MySQLdb.connect( + host=_options["host"], + user=_options["user"], + passwd=_options["pass"], + db=_options["db"], + port=_options["port"], + ssl=_options["ssl"], + ) cursor = conn.cursor() try: yield cursor except MySQLdb.DatabaseError as err: - log.exception('Error in ext_pillar MySQL: %s', err.args) + log.exception("Error in ext_pillar MySQL: %s", err.args) finally: conn.close() def extract_queries(self, args, kwargs): # pylint: disable=useless-super-delegation - ''' + """ This function normalizes the config block into a set of queries we can use. The return is a list of consistently laid out dicts. - ''' + """ return super(MySQLExtPillar, self).extract_queries(args, kwargs) -def ext_pillar(minion_id, - pillar, - *args, - **kwargs): - ''' +def ext_pillar(minion_id, pillar, *args, **kwargs): + """ Execute queries against MySQL, merge and return as a dict - ''' + """ return MySQLExtPillar().fetch(minion_id, pillar, *args, **kwargs) diff --git a/salt/pillar/nacl.py b/salt/pillar/nacl.py index 37ae3cc8595..49f8d1529a4 100644 --- a/salt/pillar/nacl.py +++ b/salt/pillar/nacl.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Decrypt pillar data through the builtin NACL renderer In most cases, you'll want to make this the last external pillar used. For @@ -18,9 +18,10 @@ this: Set ``nacl.config`` in your config. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import salt diff --git a/salt/pillar/netbox.py b/salt/pillar/netbox.py index 361c1ec6ed3..0b87ef02cb4 100644 --- a/salt/pillar/netbox.py +++ b/salt/pillar/netbox.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A module that adds data to the Pillar structure from a NetBox API. .. versionadded:: 2019.2.0 @@ -46,9 +46,10 @@ site_details: ``True`` site_prefixes: ``True`` Whether should retrieve the prefixes of the site the device belongs to. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -59,114 +60,131 @@ log = logging.getLogger(__name__) def ext_pillar(minion_id, pillar, *args, **kwargs): - ''' + """ Query NetBox API for minion data - ''' - if minion_id == '*': - log.info('There\'s no data to collect from NetBox for the Master') + """ + if minion_id == "*": + log.info("There's no data to collect from NetBox for the Master") return {} # Pull settings from kwargs - api_url = kwargs['api_url'].rstrip('/') - api_token = kwargs.get('api_token') - site_details = kwargs.get('site_details', True) - site_prefixes = kwargs.get('site_prefixes', True) - proxy_username = kwargs.get('proxy_username', None) - proxy_return = kwargs.get('proxy_return', True) + api_url = kwargs["api_url"].rstrip("/") + api_token = kwargs.get("api_token") + site_details = kwargs.get("site_details", True) + site_prefixes = kwargs.get("site_prefixes", True) + proxy_username = kwargs.get("proxy_username", None) + proxy_return = kwargs.get("proxy_return", True) ret = {} # Fetch device from API headers = {} if api_token: - headers = { - 'Authorization': 'Token {}'.format(api_token) - } - device_url = '{api_url}/{app}/{endpoint}'.format(api_url=api_url, - app='dcim', - endpoint='devices') - device_results = salt.utils.http.query(device_url, - params={'name': minion_id}, - header_dict=headers, - decode=True) - # Check status code for API call - if 'error' in device_results: - log.error('API query failed for "%s", status code: %d', - minion_id, device_results['status']) - log.error(device_results['error']) + headers = {"Authorization": "Token {}".format(api_token)} + device_url = "{api_url}/{app}/{endpoint}".format( + api_url=api_url, app="dcim", endpoint="devices" + ) + device_results = salt.utils.http.query( + device_url, params={"name": minion_id}, header_dict=headers, decode=True + ) + # Check status code for API call + if "error" in device_results: + log.error( + 'API query failed for "%s", status code: %d', + minion_id, + device_results["status"], + ) + log.error(device_results["error"]) return ret # Assign results from API call to "netbox" key - devices = device_results['dict']['results'] + devices = device_results["dict"]["results"] if len(devices) == 1: - ret['netbox'] = devices[0] + ret["netbox"] = devices[0] elif len(devices) > 1: log.error('More than one device found for "%s"', minion_id) return ret else: log.error('Unable to pull NetBox data for "%s"', minion_id) return ret - site_id = ret['netbox']['site']['id'] - site_name = ret['netbox']['site']['name'] + site_id = ret["netbox"]["site"]["id"] + site_name = ret["netbox"]["site"]["name"] if site_details: - log.debug('Retrieving site details for "%s" - site %s (ID %d)', - minion_id, site_name, site_id) - site_url = '{api_url}/{app}/{endpoint}/{site_id}/'.format(api_url=api_url, - app='dcim', - endpoint='sites', - site_id=site_id) - site_details_ret = salt.utils.http.query(site_url, - header_dict=headers, - decode=True) - if 'error' in site_details_ret: - log.error('Unable to retrieve site details for %s (ID %d)', - site_name, site_id) - log.error('Status code: %d, error: %s', - site_details_ret['status'], - site_details_ret['error']) + log.debug( + 'Retrieving site details for "%s" - site %s (ID %d)', + minion_id, + site_name, + site_id, + ) + site_url = "{api_url}/{app}/{endpoint}/{site_id}/".format( + api_url=api_url, app="dcim", endpoint="sites", site_id=site_id + ) + site_details_ret = salt.utils.http.query( + site_url, header_dict=headers, decode=True + ) + if "error" in site_details_ret: + log.error( + "Unable to retrieve site details for %s (ID %d)", site_name, site_id + ) + log.error( + "Status code: %d, error: %s", + site_details_ret["status"], + site_details_ret["error"], + ) else: - ret['netbox']['site'] = site_details_ret['dict'] + ret["netbox"]["site"] = site_details_ret["dict"] if site_prefixes: - log.debug('Retrieving site prefixes for "%s" - site %s (ID %d)', - minion_id, site_name, site_id) - prefixes_url = '{api_url}/{app}/{endpoint}'.format(api_url=api_url, - app='ipam', - endpoint='prefixes') - site_prefixes_ret = salt.utils.http.query(prefixes_url, - params={'site_id': site_id}, - header_dict=headers, - decode=True) - if 'error' in site_prefixes_ret: - log.error('Unable to retrieve site prefixes for %s (ID %d)', - site_name, site_id) - log.error('Status code: %d, error: %s', - site_prefixes_ret['status'], - site_prefixes_ret['error']) + log.debug( + 'Retrieving site prefixes for "%s" - site %s (ID %d)', + minion_id, + site_name, + site_id, + ) + prefixes_url = "{api_url}/{app}/{endpoint}".format( + api_url=api_url, app="ipam", endpoint="prefixes" + ) + site_prefixes_ret = salt.utils.http.query( + prefixes_url, params={"site_id": site_id}, header_dict=headers, decode=True + ) + if "error" in site_prefixes_ret: + log.error( + "Unable to retrieve site prefixes for %s (ID %d)", site_name, site_id + ) + log.error( + "Status code: %d, error: %s", + site_prefixes_ret["status"], + site_prefixes_ret["error"], + ) else: - ret['netbox']['site']['prefixes'] = site_prefixes_ret['dict']['results'] + ret["netbox"]["site"]["prefixes"] = site_prefixes_ret["dict"]["results"] if proxy_return: # Attempt to add "proxy" key, based on platform API call try: # Fetch device from API - platform_results = salt.utils.http.query(ret['netbox']['platform']['url'], - header_dict=headers, - decode=True) + platform_results = salt.utils.http.query( + ret["netbox"]["platform"]["url"], header_dict=headers, decode=True + ) # Check status code for API call - if 'error' in platform_results: - log.info('API query failed for "%s": %s', - minion_id, platform_results['error']) + if "error" in platform_results: + log.info( + 'API query failed for "%s": %s', + minion_id, + platform_results["error"], + ) # Assign results from API call to "proxy" key if the platform has a # napalm_driver defined. - napalm_driver = platform_results['dict'].get('napalm_driver') + napalm_driver = platform_results["dict"].get("napalm_driver") if napalm_driver: - ret['proxy'] = { - 'host': str(ipaddress.IPv4Interface( - ret['netbox']['primary_ip4']['address']).ip), - 'driver': napalm_driver, - 'proxytype': 'napalm', + ret["proxy"] = { + "host": str( + ipaddress.IPv4Interface( + ret["netbox"]["primary_ip4"]["address"] + ).ip + ), + "driver": napalm_driver, + "proxytype": "napalm", } if proxy_username: - ret['proxy']['username'] = proxy_username + ret["proxy"]["username"] = proxy_username except Exception: # pylint: disable=broad-except - log.debug( - 'Could not create proxy config data for "%s"', minion_id) + log.debug('Could not create proxy config data for "%s"', minion_id) return ret diff --git a/salt/pillar/neutron.py b/salt/pillar/neutron.py index af037cda4ff..811d8f2121a 100644 --- a/salt/pillar/neutron.py +++ b/salt/pillar/neutron.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use Openstack Neutron data as a Pillar source. Will list all networks listed inside of Neutron, to all minions. @@ -41,15 +41,17 @@ name after the Keystone profile name: ext_pillar: - neutron: my_openstack_config neutron_networks -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs try: import salt.utils.openstack.neutron as suoneu + HAS_NEUTRON = True except NameError as exc: HAS_NEUTRON = False @@ -59,35 +61,33 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only return if python-neutronclient is installed - ''' + """ return HAS_NEUTRON def _auth(profile=None): - ''' + """ Set up neutron credentials - ''' - credentials = __salt__['config.option'](profile) + """ + credentials = __salt__["config.option"](profile) kwargs = { - 'username': credentials['keystone.user'], - 'password': credentials['keystone.password'], - 'tenant_name': credentials['keystone.tenant'], - 'auth_url': credentials['keystone.auth_url'], - 'region_name': credentials.get('keystone.region_name', None), - 'service_type': credentials['keystone.service_type'], + "username": credentials["keystone.user"], + "password": credentials["keystone.password"], + "tenant_name": credentials["keystone.tenant"], + "auth_url": credentials["keystone.auth_url"], + "region_name": credentials.get("keystone.region_name", None), + "service_type": credentials["keystone.service_type"], } return suoneu.SaltNeutron(**kwargs) -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - conf): - ''' +def ext_pillar(minion_id, pillar, conf): # pylint: disable=W0613 + """ Check neutron for all data - ''' + """ comps = conf.split() profile = None @@ -97,9 +97,9 @@ def ext_pillar(minion_id, conn = _auth(profile) ret = {} networks = conn.list_networks() - for network in networks['networks']: - ret[network['name']] = network + for network in networks["networks"]: + ret[network["name"]] = network if len(comps) < 2: - comps.append('networks') + comps.append("networks") return {comps[1]: ret} diff --git a/salt/pillar/nodegroups.py b/salt/pillar/nodegroups.py index e6808a90d15..4cb2fe451db 100644 --- a/salt/pillar/nodegroups.py +++ b/salt/pillar/nodegroups.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Nodegroups Pillar ================= @@ -32,40 +32,38 @@ Configuring Nodegroups Pillar - nodegroups: pillar_name: 'nodegroups' -''' +""" # Import futures from __future__ import absolute_import, print_function, unicode_literals -# Import Salt libs -from salt.utils.minions import CkMinions - # Import 3rd-party libs from salt.ext import six -__version__ = '0.0.2' +# Import Salt libs +from salt.utils.minions import CkMinions + +__version__ = "0.0.2" def ext_pillar(minion_id, pillar, pillar_name=None): - ''' + """ A salt external pillar which provides the list of nodegroups of which the minion is a member. :param minion_id: used for compound matching nodegroups :param pillar: provided by salt, but not used by nodegroups ext_pillar :param pillar_name: optional name to use for the pillar, defaults to 'nodegroups' :return: a dictionary which is included by the salt master in the pillars returned to the minion - ''' + """ - pillar_name = pillar_name or 'nodegroups' - all_nodegroups = __opts__['nodegroups'] + pillar_name = pillar_name or "nodegroups" + all_nodegroups = __opts__["nodegroups"] nodegroups_minion_is_in = [] ckminions = None for nodegroup_name in six.iterkeys(all_nodegroups): ckminions = ckminions or CkMinions(__opts__) - _res = ckminions.check_minions( - all_nodegroups[nodegroup_name], - 'compound') - match = _res['minions'] + _res = ckminions.check_minions(all_nodegroups[nodegroup_name], "compound") + match = _res["minions"] if minion_id in match: nodegroups_minion_is_in.append(nodegroup_name) diff --git a/salt/pillar/pepa.py b/salt/pillar/pepa.py index f7ee83489be..b90a3f34e53 100644 --- a/salt/pillar/pepa.py +++ b/salt/pillar/pepa.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -''' +""" Pepa ==== @@ -260,33 +260,36 @@ Links ===== For more examples and information see <https://github.com/mickep76/pepa>. -''' +""" # Import futures from __future__ import absolute_import, print_function, unicode_literals -__author__ = 'Michael Persson <michael.ake.persson@gmail.com>' -__copyright__ = 'Copyright (c) 2013 Michael Persson' -__license__ = 'Apache License, Version 2.0' -__version__ = '0.6.6' - # Import python libs import glob -import jinja2 import logging import os import re import sys +import jinja2 +import salt.utils.files +import salt.utils.yaml + # Import Salt libs from salt.ext import six from salt.ext.six.moves import input # pylint: disable=import-error,redefined-builtin -import salt.utils.files -import salt.utils.yaml + +__author__ = "Michael Persson <michael.ake.persson@gmail.com>" +__copyright__ = "Copyright (c) 2013 Michael Persson" +__license__ = "Apache License, Version 2.0" +__version__ = "0.6.6" + # Import 3rd-party libs try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False @@ -294,21 +297,30 @@ except ImportError: # Only used when called from a terminal log = None -if __name__ == '__main__': +if __name__ == "__main__": import argparse # pylint: disable=minimum-python-version parser = argparse.ArgumentParser() - parser.add_argument('hostname', help='Hostname') - parser.add_argument('-c', '--config', default='/etc/salt/master', help='Configuration file') - parser.add_argument('-d', '--debug', action='store_true', help='Print debug info') - parser.add_argument('-g', '--grains', help='Input Grains as YAML') - parser.add_argument('-p', '--pillar', help='Input Pillar as YAML') - parser.add_argument('-n', '--no-color', action='store_true', help='No color output') - parser.add_argument('-v', '--validate', action='store_true', help='Validate output') - parser.add_argument('-q', '--query-api', action='store_true', help='Query Saltstack REST API for Grains') - parser.add_argument('--url', default='https://salt:8000', help='URL for SaltStack REST API') - parser.add_argument('-u', '--username', help='Username for SaltStack REST API') - parser.add_argument('-P', '--password', help='Password for SaltStack REST API') + parser.add_argument("hostname", help="Hostname") + parser.add_argument( + "-c", "--config", default="/etc/salt/master", help="Configuration file" + ) + parser.add_argument("-d", "--debug", action="store_true", help="Print debug info") + parser.add_argument("-g", "--grains", help="Input Grains as YAML") + parser.add_argument("-p", "--pillar", help="Input Pillar as YAML") + parser.add_argument("-n", "--no-color", action="store_true", help="No color output") + parser.add_argument("-v", "--validate", action="store_true", help="Validate output") + parser.add_argument( + "-q", + "--query-api", + action="store_true", + help="Query Saltstack REST API for Grains", + ) + parser.add_argument( + "--url", default="https://salt:8000", help="URL for SaltStack REST API" + ) + parser.add_argument("-u", "--username", help="Username for SaltStack REST API") + parser.add_argument("-P", "--password", help="Password for SaltStack REST API") args = parser.parse_args() LOG_LEVEL = logging.WARNING @@ -319,7 +331,10 @@ if __name__ == '__main__': if not args.no_color: try: import colorlog # pylint: disable=import-error - formatter = colorlog.ColoredFormatter("[%(log_color)s%(levelname)-8s%(reset)s] %(log_color)s%(message)s%(reset)s") + + formatter = colorlog.ColoredFormatter( + "[%(log_color)s%(levelname)-8s%(reset)s] %(log_color)s%(message)s%(reset)s" + ) except ImportError: formatter = logging.Formatter("[%(levelname)-8s] %(message)s") else: @@ -329,7 +344,7 @@ if __name__ == '__main__': stream.setLevel(LOG_LEVEL) stream.setFormatter(formatter) - log = logging.getLogger('pythonConfig') + log = logging.getLogger("pythonConfig") log.setLevel(LOG_LEVEL) log.addHandler(stream) else: @@ -338,18 +353,16 @@ else: # Options __opts__ = { - 'pepa_roots': { - 'base': '/srv/salt' - }, - 'pepa_delimiter': '..', - 'pepa_validate': False + "pepa_roots": {"base": "/srv/salt"}, + "pepa_delimiter": "..", + "pepa_validate": False, } def __virtual__(): - ''' + """ Only return if all the modules are available - ''' + """ if not HAS_REQUESTS: return False @@ -357,13 +370,13 @@ def __virtual__(): def key_value_to_tree(data): - ''' + """ Convert key/value to tree - ''' + """ tree = {} for flatkey, value in six.iteritems(data): t = tree - keys = flatkey.split(__opts__['pepa_delimiter']) + keys = flatkey.split(__opts__["pepa_delimiter"]) for i, key in enumerate(keys, 1): if i == len(keys): t[key] = value @@ -373,26 +386,26 @@ def key_value_to_tree(data): def ext_pillar(minion_id, pillar, resource, sequence, subkey=False, subkey_only=False): - ''' + """ Evaluate Pepa templates - ''' - roots = __opts__['pepa_roots'] + """ + roots = __opts__["pepa_roots"] # Default input inp = {} - inp['default'] = 'default' - inp['hostname'] = minion_id + inp["default"] = "default" + inp["hostname"] = minion_id - if 'environment' in pillar: - inp['environment'] = pillar['environment'] - elif 'environment' in __grains__: - inp['environment'] = __grains__['environment'] + if "environment" in pillar: + inp["environment"] = pillar["environment"] + elif "environment" in __grains__: + inp["environment"] = __grains__["environment"] else: - inp['environment'] = 'base' + inp["environment"] = "base" # Load templates output = inp - output['pepa_templates'] = [] + output["pepa_templates"] = [] immutable = {} for categ, info in [next(six.iteritems(s)) for s in sequence]: @@ -401,16 +414,16 @@ def ext_pillar(minion_id, pillar, resource, sequence, subkey=False, subkey_only= continue alias = None - if isinstance(info, dict) and 'name' in info: - alias = info['name'] + if isinstance(info, dict) and "name" in info: + alias = info["name"] else: alias = categ templdir = None - if info and 'base_only' in info and info['base_only']: - templdir = os.path.join(roots['base'], resource, alias) + if info and "base_only" in info and info["base_only"]: + templdir = os.path.join(roots["base"], resource, alias) else: - templdir = os.path.join(roots[inp['environment']], resource, alias) + templdir = os.path.join(roots[inp["environment"]], resource, alias) entries = [] if isinstance(inp[categ], list): @@ -424,70 +437,83 @@ def ext_pillar(minion_id, pillar, resource, sequence, subkey=False, subkey_only= for entry in entries: results_jinja = None results = None - fn = os.path.join(templdir, re.sub(r'\W', '_', entry.lower()) + '.yaml') + fn = os.path.join(templdir, re.sub(r"\W", "_", entry.lower()) + ".yaml") if os.path.isfile(fn): log.info("Loading template: %s", fn) with salt.utils.files.fopen(fn) as fhr: template = jinja2.Template(fhr.read()) - output['pepa_templates'].append(fn) + output["pepa_templates"].append(fn) try: data = key_value_to_tree(output) - data['grains'] = __grains__.copy() - data['pillar'] = pillar.copy() + data["grains"] = __grains__.copy() + data["pillar"] = pillar.copy() results_jinja = template.render(data) results = salt.utils.yaml.safe_load(results_jinja) except jinja2.UndefinedError as err: - log.error('Failed to parse JINJA template: %s\n%s', fn, err) + log.error("Failed to parse JINJA template: %s\n%s", fn, err) except salt.utils.yaml.YAMLError as err: - log.error('Failed to parse YAML in template: %s\n%s', fn, err) + log.error("Failed to parse YAML in template: %s\n%s", fn, err) else: log.info("Template doesn't exist: %s", fn) continue if results is not None: for key in results: - skey = key.rsplit(__opts__['pepa_delimiter'], 1) + skey = key.rsplit(__opts__["pepa_delimiter"], 1) rkey = None operator = None - if len(skey) > 1 and key.rfind('()') > 0: - rkey = skey[0].rstrip(__opts__['pepa_delimiter']) + if len(skey) > 1 and key.rfind("()") > 0: + rkey = skey[0].rstrip(__opts__["pepa_delimiter"]) operator = skey[1] if key in immutable: - log.warning('Key %s is immutable, changes are not allowed', key) + log.warning("Key %s is immutable, changes are not allowed", key) elif rkey in immutable: - log.warning("Key %s is immutable, changes are not allowed", rkey) - elif operator == 'merge()' or operator == 'imerge()': - if operator == 'merge()': + log.warning( + "Key %s is immutable, changes are not allowed", rkey + ) + elif operator == "merge()" or operator == "imerge()": + if operator == "merge()": log.debug("Merge key %s: %s", rkey, results[key]) else: - log.debug("Set immutable and merge key %s: %s", rkey, results[key]) + log.debug( + "Set immutable and merge key %s: %s", rkey, results[key] + ) immutable[rkey] = True if rkey not in output: - log.error('Cant\'t merge key %s doesn\'t exist', rkey) + log.error("Cant't merge key %s doesn't exist", rkey) elif not isinstance(results[key], type(output[rkey])): - log.error('Can\'t merge different types for key %s', rkey) + log.error("Can't merge different types for key %s", rkey) elif isinstance(results[key], dict): output[rkey].update(results[key]) elif isinstance(results[key], list): output[rkey].extend(results[key]) else: - log.error('Unsupported type need to be list or dict for key %s', rkey) - elif operator == 'unset()' or operator == 'iunset()': - if operator == 'unset()': + log.error( + "Unsupported type need to be list or dict for key %s", + rkey, + ) + elif operator == "unset()" or operator == "iunset()": + if operator == "unset()": log.debug("Unset key %s", rkey) else: log.debug("Set immutable and unset key %s", rkey) immutable[rkey] = True if rkey in output: del output[rkey] - elif operator == 'immutable()': - log.debug("Set immutable and substitute key %s: %s", rkey, results[key]) + elif operator == "immutable()": + log.debug( + "Set immutable and substitute key %s: %s", + rkey, + results[key], + ) immutable[rkey] = True output[rkey] = results[key] elif operator is not None: - log.error('Unsupported operator %s, skipping key %s', operator, rkey) + log.error( + "Unsupported operator %s, skipping key %s", operator, rkey + ) else: log.debug("Substitute key %s: %s", key, results[key]) output[key] = results[key] @@ -501,49 +527,49 @@ def ext_pillar(minion_id, pillar, resource, sequence, subkey=False, subkey_only= pillar_data[resource] = tree.copy() else: pillar_data = tree - if __opts__['pepa_validate']: - pillar_data['pepa_keys'] = output.copy() + if __opts__["pepa_validate"]: + pillar_data["pepa_keys"] = output.copy() return pillar_data def validate(output, resource): - ''' + """ Validate Pepa templates - ''' + """ try: import cerberus # pylint: disable=import-error except ImportError: - log.critical('You need module cerberus in order to use validation') + log.critical("You need module cerberus in order to use validation") return - roots = __opts__['pepa_roots'] + roots = __opts__["pepa_roots"] - valdir = os.path.join(roots['base'], resource, 'validate') + valdir = os.path.join(roots["base"], resource, "validate") all_schemas = {} pepa_schemas = [] - for fn in glob.glob(valdir + '/*.yaml'): + for fn in glob.glob(valdir + "/*.yaml"): log.info("Loading schema: %s", fn) with salt.utils.files.fopen(fn) as fhr: template = jinja2.Template(fhr.read()) data = output - data['grains'] = __grains__.copy() - data['pillar'] = __pillar__.copy() + data["grains"] = __grains__.copy() + data["pillar"] = __pillar__.copy() schema = salt.utils.yaml.safe_load(template.render(data)) all_schemas.update(schema) pepa_schemas.append(fn) val = cerberus.Validator() - if not val.validate(output['pepa_keys'], all_schemas): + if not val.validate(output["pepa_keys"], all_schemas): for ekey, error in six.iteritems(val.errors): - log.warning('Validation failed for key %s: %s', ekey, error) + log.warning("Validation failed for key %s: %s", ekey, error) - output['pepa_schema_keys'] = all_schemas - output['pepa_schemas'] = pepa_schemas + output["pepa_schema_keys"] = all_schemas + output["pepa_schemas"] = pepa_schemas # Only used when called from a terminal -if __name__ == '__main__': +if __name__ == "__main__": # Load configuration file if not os.path.isfile(args.config): log.critical("Configuration file doesn't exist: %s", args.config) @@ -554,28 +580,28 @@ if __name__ == '__main__': __opts__.update(salt.utils.yaml.safe_load(fh_)) loc = 0 - for name in [next(iter(list(e.keys()))) for e in __opts__['ext_pillar']]: - if name == 'pepa': + for name in [next(iter(list(e.keys()))) for e in __opts__["ext_pillar"]]: + if name == "pepa": break loc += 1 # Get grains __grains__ = {} - if 'pepa_grains' in __opts__: - __grains__ = __opts__['pepa_grains'] + if "pepa_grains" in __opts__: + __grains__ = __opts__["pepa_grains"] if args.grains: __grains__.update(salt.utils.yaml.safe_load(args.grains)) # Get pillars __pillar__ = {} - if 'pepa_pillar' in __opts__: - __pillar__ = __opts__['pepa_pillar'] + if "pepa_pillar" in __opts__: + __pillar__ = __opts__["pepa_pillar"] if args.pillar: __pillar__.update(salt.utils.yaml.safe_load(args.pillar)) # Validate or not if args.validate: - __opts__['pepa_validate'] = True + __opts__["pepa_validate"] = True if args.query_api: import requests @@ -584,53 +610,58 @@ if __name__ == '__main__': username = args.username password = args.password if username is None: - username = input('Username: ') + username = input("Username: ") if password is None: password = getpass.getpass() - log.info('Authenticate REST API') - auth = {'username': username, 'password': password, 'eauth': 'pam'} - request = requests.post(args.url + '/login', auth) + log.info("Authenticate REST API") + auth = {"username": username, "password": password, "eauth": "pam"} + request = requests.post(args.url + "/login", auth) if not request.ok: - raise RuntimeError('Failed to authenticate to SaltStack REST API: {0}'.format(request.text)) + raise RuntimeError( + "Failed to authenticate to SaltStack REST API: {0}".format(request.text) + ) response = request.json() - token = response['return'][0]['token'] + token = response["return"][0]["token"] - log.info('Request Grains from REST API') - headers = {'X-Auth-Token': token, 'Accept': 'application/json'} - request = requests.get(args.url + '/minions/' + args.hostname, headers=headers) + log.info("Request Grains from REST API") + headers = {"X-Auth-Token": token, "Accept": "application/json"} + request = requests.get(args.url + "/minions/" + args.hostname, headers=headers) - result = request.json().get('return', [{}])[0] + result = request.json().get("return", [{}])[0] if args.hostname not in result: - raise RuntimeError('Failed to get Grains from SaltStack REST API') + raise RuntimeError("Failed to get Grains from SaltStack REST API") __grains__ = result[args.hostname] # Print results ex_subkey = False ex_subkey_only = False - if 'subkey' in __opts__['ext_pillar'][loc]['pepa']: - ex_subkey = __opts__['ext_pillar'][loc]['pepa']['subkey'] - if 'subkey_only' in __opts__['ext_pillar'][loc]['pepa']: - ex_subkey_only = __opts__['ext_pillar'][loc]['pepa']['subkey_only'] + if "subkey" in __opts__["ext_pillar"][loc]["pepa"]: + ex_subkey = __opts__["ext_pillar"][loc]["pepa"]["subkey"] + if "subkey_only" in __opts__["ext_pillar"][loc]["pepa"]: + ex_subkey_only = __opts__["ext_pillar"][loc]["pepa"]["subkey_only"] - result = ext_pillar(args.hostname, __pillar__, __opts__['ext_pillar'][loc]['pepa']['resource'], - __opts__['ext_pillar'][loc]['pepa']['sequence'], ex_subkey, ex_subkey_only) + result = ext_pillar( + args.hostname, + __pillar__, + __opts__["ext_pillar"][loc]["pepa"]["resource"], + __opts__["ext_pillar"][loc]["pepa"]["sequence"], + ex_subkey, + ex_subkey_only, + ) - if __opts__['pepa_validate']: - validate(result, __opts__['ext_pillar'][loc]['pepa']['resource']) + if __opts__["pepa_validate"]: + validate(result, __opts__["ext_pillar"][loc]["pepa"]["resource"]) try: orig_ignore = salt.utils.yaml.SafeOrderedDumper.ignore_aliases salt.utils.yaml.SafeOrderedDumper.ignore_aliases = lambda x, y: True def _print_result(result): - print(salt.utils.yaml.safe_dump( - result, - indent=4, - default_flow_style=False)) + print(salt.utils.yaml.safe_dump(result, indent=4, default_flow_style=False)) if not args.no_color: try: @@ -638,11 +669,15 @@ if __name__ == '__main__': import pygments import pygments.lexers import pygments.formatters + # pylint: disable=no-member - print(pygments.highlight( - salt.utils.yaml.safe_dump(result), - pygments.lexers.YamlLexer(), - pygments.formatters.TerminalFormatter())) + print( + pygments.highlight( + salt.utils.yaml.safe_dump(result), + pygments.lexers.YamlLexer(), + pygments.formatters.TerminalFormatter(), + ) + ) # pylint: enable=no-member, import-error except ImportError: _print_result(result) diff --git a/salt/pillar/pillar_ldap.py b/salt/pillar/pillar_ldap.py index e0dcac0f8f1..d85f91e9510 100644 --- a/salt/pillar/pillar_ldap.py +++ b/salt/pillar/pillar_ldap.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use LDAP data as a Pillar source This pillar module executes a series of LDAP searches. @@ -120,21 +120,24 @@ Result } ] } -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import logging +import os + +# Import third party libs +import jinja2 # Import salt libs import salt.utils.data from salt.exceptions import SaltInvocationError -# Import third party libs -import jinja2 try: import ldap # pylint: disable=W0611 + HAS_LDAP = True except ImportError: HAS_LDAP = False @@ -144,19 +147,19 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only return if ldap module is installed - ''' + """ if HAS_LDAP: - return 'pillar_ldap' + return "pillar_ldap" else: return False def _render_template(config_file): - ''' + """ Render config template, substituting grains where found. - ''' + """ dirname, filename = os.path.split(config_file) env = jinja2.Environment(loader=jinja2.FileSystemLoader(dirname)) template = env.get_template(filename) @@ -164,11 +167,11 @@ def _render_template(config_file): def _config(name, conf, default=None): - ''' + """ Return a value for 'name' from the config file options. If the 'name' is not in the config, the 'default' value is returned. This method converts unicode values to str type under python 2. - ''' + """ try: value = conf[name] except KeyError: @@ -177,7 +180,7 @@ def _config(name, conf, default=None): def _result_to_dict(data, result, conf, source): - ''' + """ Aggregates LDAP search result based on rules, returns a dictionary. Rules: @@ -202,23 +205,23 @@ def _result_to_dict(data, result, conf, source): { 'ntpserver': 'ntp.acme.local', 'foo': 'myfoo', 'vhost': ['www.acme.net', 'www.acme.local'] } - ''' - attrs = _config('attrs', conf) or [] - lists = _config('lists', conf) or [] - dict_key_attr = _config('dict_key_attr', conf) or 'dn' + """ + attrs = _config("attrs", conf) or [] + lists = _config("lists", conf) or [] + dict_key_attr = _config("dict_key_attr", conf) or "dn" # TODO: # deprecate the default 'mode: split' and make the more # straightforward 'mode: map' the new default - mode = _config('mode', conf) or 'split' - if mode == 'map': + mode = _config("mode", conf) or "split" + if mode == "map": data[source] = [] for record in result: ret = {} - if 'dn' in attrs or 'distinguishedName' in attrs: - log.debug('dn: %s', record[0]) - ret['dn'] = record[0] + if "dn" in attrs or "distinguishedName" in attrs: + log.debug("dn: %s", record[0]) + ret["dn"] = record[0] record = record[1] - log.debug('record: %s', record) + log.debug("record: %s", record) for key in record: if key in attrs: for item in record.get(key): @@ -226,40 +229,40 @@ def _result_to_dict(data, result, conf, source): if key in lists: ret[key] = record.get(key) data[source].append(ret) - elif mode == 'dict': + elif mode == "dict": data[source] = {} for record in result: ret = {} distinguished_name = record[0] - log.debug('dn: %s', distinguished_name) - if 'dn' in attrs or 'distinguishedName' in attrs: - ret['dn'] = distinguished_name + log.debug("dn: %s", distinguished_name) + if "dn" in attrs or "distinguishedName" in attrs: + ret["dn"] = distinguished_name record = record[1] - log.debug('record: %s', record) + log.debug("record: %s", record) for key in record: if key in attrs: for item in record.get(key): ret[key] = item if key in lists: ret[key] = record.get(key) - if dict_key_attr in ['dn', 'distinguishedName']: + if dict_key_attr in ["dn", "distinguishedName"]: dict_key = distinguished_name else: - dict_key = ','.join(sorted(record.get(dict_key_attr, []))) + dict_key = ",".join(sorted(record.get(dict_key_attr, []))) try: data[source][dict_key].append(ret) except KeyError: data[source][dict_key] = [ret] - elif mode == 'split': + elif mode == "split": for key in result[0][1]: if key in attrs: for item in result.get(key): - skey, sval = item.split('=', 1) + skey, sval = item.split("=", 1) data[skey] = sval elif key in lists: for item in result.get(key): - if '=' in item: - skey, sval = item.split('=', 1) + if "=" in item: + skey, sval = item.split("=", 1) if skey not in data: data[skey] = [sval] else: @@ -268,70 +271,72 @@ def _result_to_dict(data, result, conf, source): def _do_search(conf): - ''' + """ Builds connection and search arguments, performs the LDAP search and formats the results as a dictionary appropriate for pillar use. - ''' + """ # Build LDAP connection args connargs = {} - for name in ['server', 'port', 'tls', 'binddn', 'bindpw', 'anonymous']: + for name in ["server", "port", "tls", "binddn", "bindpw", "anonymous"]: connargs[name] = _config(name, conf) - if connargs['binddn'] and connargs['bindpw']: - connargs['anonymous'] = False + if connargs["binddn"] and connargs["bindpw"]: + connargs["anonymous"] = False # Build search args try: - _filter = conf['filter'] + _filter = conf["filter"] except KeyError: - raise SaltInvocationError('missing filter') - _dn = _config('dn', conf) - scope = _config('scope', conf) - _lists = _config('lists', conf) or [] - _attrs = _config('attrs', conf) or [] - _dict_key_attr = _config('dict_key_attr', conf, 'dn') + raise SaltInvocationError("missing filter") + _dn = _config("dn", conf) + scope = _config("scope", conf) + _lists = _config("lists", conf) or [] + _attrs = _config("attrs", conf) or [] + _dict_key_attr = _config("dict_key_attr", conf, "dn") attrs = _lists + _attrs + [_dict_key_attr] if not attrs: attrs = None # Perform the search try: - result = __salt__['ldap.search'](_filter, _dn, scope, attrs, - **connargs)['results'] + result = __salt__["ldap.search"](_filter, _dn, scope, attrs, **connargs)[ + "results" + ] except IndexError: # we got no results for this search - log.debug('LDAP search returned no results for filter %s', _filter) + log.debug("LDAP search returned no results for filter %s", _filter) result = {} except Exception: # pylint: disable=broad-except - log.critical( - 'Failed to retrieve pillar data from LDAP:\n', exc_info=True - ) + log.critical("Failed to retrieve pillar data from LDAP:\n", exc_info=True) return {} return result -def ext_pillar(minion_id, # pylint: disable=W0613 - pillar, # pylint: disable=W0613 - config_file): - ''' +def ext_pillar( + minion_id, pillar, config_file # pylint: disable=W0613 # pylint: disable=W0613 +): + """ Execute LDAP searches and return the aggregated data - ''' + """ config_template = None try: config_template = _render_template(config_file) except jinja2.exceptions.TemplateNotFound: - log.debug('pillar_ldap: missing configuration file %s', config_file) + log.debug("pillar_ldap: missing configuration file %s", config_file) except Exception: # pylint: disable=broad-except - log.debug('pillar_ldap: failed to render template for %s', - config_file, exc_info=True) + log.debug( + "pillar_ldap: failed to render template for %s", config_file, exc_info=True + ) if not config_template: # We don't have a config file return {} import salt.utils.yaml + try: opts = salt.utils.yaml.safe_load(config_template) or {} - opts['conf_file'] = config_file + opts["conf_file"] = config_file except Exception as err: # pylint: disable=broad-except import salt.log - msg = 'pillar_ldap: error parsing configuration file: {0} - {1}' + + msg = "pillar_ldap: error parsing configuration file: {0} - {1}" if salt.log.is_console_configured(): log.warning(msg.format(config_file, err)) else: @@ -340,24 +345,24 @@ def ext_pillar(minion_id, # pylint: disable=W0613 else: if not isinstance(opts, dict): log.warning( - 'pillar_ldap: %s is invalidly formatted, must be a YAML ' - 'dictionary. See the documentation for more information.', - config_file + "pillar_ldap: %s is invalidly formatted, must be a YAML " + "dictionary. See the documentation for more information.", + config_file, ) return {} - if 'search_order' not in opts: + if "search_order" not in opts: log.warning( - 'pillar_ldap: search_order missing from configuration. See the ' - 'documentation for more information.' + "pillar_ldap: search_order missing from configuration. See the " + "documentation for more information." ) return {} data = {} - for source in opts['search_order']: + for source in opts["search_order"]: config = opts[source] result = _do_search(config) - log.debug('source %s got result %s', source, result) + log.debug("source %s got result %s", source, result) if result: data = _result_to_dict(data, result, config, source) return data diff --git a/salt/pillar/postgres.py b/salt/pillar/postgres.py index 096c6a5c86f..9c87154f5de 100644 --- a/salt/pillar/postgres.py +++ b/salt/pillar/postgres.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Retrieve Pillar data by doing a postgres query .. versionadded:: 2017.7.0 @@ -27,12 +27,13 @@ Complete Example depth: 5 as_list: True with_lists: [1,3] -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs from contextlib import contextmanager -import logging # Import Salt libs from salt.pillar.sql_base import SqlBaseExtPillar @@ -43,6 +44,7 @@ log = logging.getLogger(__name__) # Import third party libs try: import psycopg2 + HAS_POSTGRES = True except ImportError: HAS_POSTGRES = False @@ -55,27 +57,30 @@ def __virtual__(): class POSTGRESExtPillar(SqlBaseExtPillar): - ''' + """ This class receives and processes the database rows from POSTGRES. - ''' + """ + @classmethod def _db_name(cls): - return 'POSTGRES' + return "POSTGRES" def _get_options(self): - ''' + """ Returns options used for the POSTGRES connection. - ''' - defaults = {'host': 'localhost', - 'user': 'salt', - 'pass': 'salt', - 'db': 'salt', - 'port': 5432} + """ + defaults = { + "host": "localhost", + "user": "salt", + "pass": "salt", + "db": "salt", + "port": 5432, + } _options = {} - _opts = __opts__.get('postgres', {}) + _opts = __opts__.get("postgres", {}) for attr in defaults: if attr not in _opts: - log.debug('Using default for POSTGRES %s', attr) + log.debug("Using default for POSTGRES %s", attr) _options[attr] = defaults[attr] continue _options[attr] = _opts[attr] @@ -83,37 +88,36 @@ class POSTGRESExtPillar(SqlBaseExtPillar): @contextmanager def _get_cursor(self): - ''' + """ Yield a POSTGRES cursor - ''' + """ _options = self._get_options() - conn = psycopg2.connect(host=_options['host'], - user=_options['user'], - password=_options['pass'], - dbname=_options['db'], - port=_options['port']) + conn = psycopg2.connect( + host=_options["host"], + user=_options["user"], + password=_options["pass"], + dbname=_options["db"], + port=_options["port"], + ) cursor = conn.cursor() try: yield cursor - log.debug('Connected to POSTGRES DB') + log.debug("Connected to POSTGRES DB") except psycopg2.DatabaseError as err: - log.exception('Error in ext_pillar POSTGRES: %s', err.args) + log.exception("Error in ext_pillar POSTGRES: %s", err.args) finally: conn.close() def extract_queries(self, args, kwargs): # pylint: disable=useless-super-delegation - ''' + """ This function normalizes the config block into a set of queries we can use. The return is a list of consistently laid out dicts. - ''' + """ return super(POSTGRESExtPillar, self).extract_queries(args, kwargs) -def ext_pillar(minion_id, - pillar, - *args, - **kwargs): - ''' +def ext_pillar(minion_id, pillar, *args, **kwargs): + """ Execute queries against POSTGRES, merge and return as a dict - ''' + """ return POSTGRESExtPillar().fetch(minion_id, pillar, *args, **kwargs) diff --git a/salt/pillar/puppet.py b/salt/pillar/puppet.py index 26d1b98ad82..e6449bd5d12 100644 --- a/salt/pillar/puppet.py +++ b/salt/pillar/puppet.py @@ -1,31 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" Execute an unmodified puppet_node_classifier and read the output as YAML. The YAML data is then directly overlaid onto the minion's Pillar data. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Don't "fix" the above docstring to put it on two lines, as the sphinx -# autosummary pulls only the first line for its description. - # Import Python libs import logging # Import Salt libs import salt.utils.yaml +# Don't "fix" the above docstring to put it on two lines, as the sphinx +# autosummary pulls only the first line for its description. + + # Set up logging log = logging.getLogger(__name__) -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - command): - ''' +def ext_pillar(minion_id, pillar, command): # pylint: disable=W0613 + """ Execute an unmodified puppet_node_classifier and read the output as YAML - ''' + """ try: - data = salt.utils.yaml.safe_load(__salt__['cmd.run']('{0} {1}'.format(command, minion_id))) - return data['parameters'] + data = salt.utils.yaml.safe_load( + __salt__["cmd.run"]("{0} {1}".format(command, minion_id)) + ) + return data["parameters"] except Exception: # pylint: disable=broad-except - log.critical('YAML data from %s failed to parse', command) + log.critical("YAML data from %s failed to parse", command) return {} diff --git a/salt/pillar/reclass_adapter.py b/salt/pillar/reclass_adapter.py index 1cb9284e008..9b9ff999bc2 100644 --- a/salt/pillar/reclass_adapter.py +++ b/salt/pillar/reclass_adapter.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use the "reclass" database as a Pillar source .. |reclass| replace:: **reclass** @@ -44,7 +44,7 @@ note of the differing data types for ``ext_pillar`` and ``master_tops``): If you want to run reclass from source, rather than installing it, you can either let the master know via the ``PYTHONPATH`` environment variable, or by setting the configuration option, like in the example above. -''' +""" # This file cannot be called reclass.py, because then the module import would @@ -56,30 +56,31 @@ from __future__ import absolute_import, print_function, unicode_literals # Import salt libs from salt.exceptions import SaltInvocationError -from salt.utils.reclass import ( - prepend_reclass_source_path, - filter_out_source_path_option, - set_inventory_base_uri_default -) # Import 3rd-party libs from salt.ext import six +from salt.utils.reclass import ( + filter_out_source_path_option, + prepend_reclass_source_path, + set_inventory_base_uri_default, +) # Define the module's virtual name -__virtualname__ = 'reclass' +__virtualname__ = "reclass" def __virtual__(retry=False): try: import reclass # pylint: disable=unused-import + return __virtualname__ except ImportError as e: if retry: return False - for pillar in __opts__.get('ext_pillar', []): - if 'reclass' not in pillar: + for pillar in __opts__.get("ext_pillar", []): + if "reclass" not in pillar: continue # each pillar entry is a single-key hash of name -> options @@ -91,15 +92,16 @@ def __virtual__(retry=False): def ext_pillar(minion_id, pillar, **kwargs): - ''' + """ Obtain the Pillar data from **reclass** for the given ``minion_id``. - ''' + """ # If reclass is installed, __virtual__ put it onto the search path, so we # don't need to protect against ImportError: # pylint: disable=3rd-party-module-not-gated,no-name-in-module from reclass.adapters.salt import ext_pillar as reclass_ext_pillar from reclass.errors import ReclassException + # pylint: enable=3rd-party-module-not-gated,no-name-in-module try: @@ -117,19 +119,19 @@ def ext_pillar(minion_id, pillar, **kwargs): return reclass_ext_pillar(minion_id, pillar, **kwargs) except TypeError as e: - if 'unexpected keyword argument' in six.text_type(e): + if "unexpected keyword argument" in six.text_type(e): arg = six.text_type(e).split()[-1] - raise SaltInvocationError('ext_pillar.reclass: unexpected option: ' - + arg) + raise SaltInvocationError("ext_pillar.reclass: unexpected option: " + arg) else: raise except KeyError as e: - if 'id' in six.text_type(e): - raise SaltInvocationError('ext_pillar.reclass: __opts__ does not ' - 'define minion ID') + if "id" in six.text_type(e): + raise SaltInvocationError( + "ext_pillar.reclass: __opts__ does not " "define minion ID" + ) else: raise except ReclassException as e: - raise SaltInvocationError('ext_pillar.reclass: {0}'.format(e)) + raise SaltInvocationError("ext_pillar.reclass: {0}".format(e)) diff --git a/salt/pillar/redismod.py b/salt/pillar/redismod.py index 0c6138801c8..9cf523a8515 100644 --- a/salt/pillar/redismod.py +++ b/salt/pillar/redismod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Read pillar data from a Redis backend ===================================== @@ -29,81 +29,75 @@ Configuring the Redis ext_pillar ext_pillar: - redis: {function: key_value} -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.json -__virtualname__ = 'redis' +__virtualname__ = "redis" def __virtual__(): - ''' + """ Only load if the redis module is in __salt__ - ''' - if 'redis.get_key' in __salt__: + """ + if "redis.get_key" in __salt__: return __virtualname__ return False def ext_pillar(minion_id, pillar, function, **kwargs): - ''' + """ Grabs external pillar data based on configured function - ''' - if function.startswith('_') or function not in globals(): + """ + if function.startswith("_") or function not in globals(): return {} # Call specified function to pull redis data return globals()[function](minion_id, pillar, **kwargs) -def key_value(minion_id, - pillar, # pylint: disable=W0613 - pillar_key='redis_pillar'): - ''' +def key_value(minion_id, pillar, pillar_key="redis_pillar"): # pylint: disable=W0613 + """ Looks for key in redis matching minion_id, returns a structure based on the data type of the redis key. String for string type, dict for hash type and lists for lists, sets and sorted sets. pillar_key Pillar key to return data into - ''' + """ # Identify key type and process as needed based on that type - key_type = __salt__['redis.key_type'](minion_id) - if key_type == 'string': - return {pillar_key: __salt__['redis.get_key'](minion_id)} - elif key_type == 'hash': - return {pillar_key: __salt__['redis.hgetall'](minion_id)} - elif key_type == 'list': - list_size = __salt__['redis.llen'](minion_id) + key_type = __salt__["redis.key_type"](minion_id) + if key_type == "string": + return {pillar_key: __salt__["redis.get_key"](minion_id)} + elif key_type == "hash": + return {pillar_key: __salt__["redis.hgetall"](minion_id)} + elif key_type == "list": + list_size = __salt__["redis.llen"](minion_id) if not list_size: return {} - return {pillar_key: __salt__['redis.lrange'](minion_id, 0, - list_size - 1)} - elif key_type == 'set': - return {pillar_key: __salt__['redis.smembers'](minion_id)} - elif key_type == 'zset': - set_size = __salt__['redis.zcard'](minion_id) + return {pillar_key: __salt__["redis.lrange"](minion_id, 0, list_size - 1)} + elif key_type == "set": + return {pillar_key: __salt__["redis.smembers"](minion_id)} + elif key_type == "zset": + set_size = __salt__["redis.zcard"](minion_id) if not set_size: return {} - return {pillar_key: __salt__['redis.zrange'](minion_id, 0, - set_size - 1)} + return {pillar_key: __salt__["redis.zrange"](minion_id, 0, set_size - 1)} # Return nothing for unhandled types return {} -def key_json(minion_id, - pillar, # pylint: disable=W0613 - pillar_key=None): - ''' +def key_json(minion_id, pillar, pillar_key=None): # pylint: disable=W0613 + """ Pulls a string from redis and deserializes it from json. Deserialized dictionary data loaded directly into top level if pillar_key is not set. pillar_key Pillar key to return data into - ''' - key_data = __salt__['redis.get_key'](minion_id) + """ + key_data = __salt__["redis.get_key"](minion_id) # Return nothing for non-existent keys if not key_data: return {} @@ -113,6 +107,6 @@ def key_json(minion_id, if isinstance(data, dict) and not pillar_key: return data elif not pillar_key: - return {'redis_pillar': data} + return {"redis_pillar": data} else: return {pillar_key: data} diff --git a/salt/pillar/rethinkdb_pillar.py b/salt/pillar/rethinkdb_pillar.py index 85d35af4ceb..8aa28fa938a 100644 --- a/salt/pillar/rethinkdb_pillar.py +++ b/salt/pillar/rethinkdb_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Provide external pillar data from RethinkDB .. versionadded:: 2018.3.0 @@ -41,7 +41,7 @@ In the example above the following happens. Module Documentation ==================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libraries @@ -50,18 +50,19 @@ import logging # Import 3rd party libraries try: import rethinkdb + HAS_RETHINKDB = True except ImportError: HAS_RETHINKDB = False -__virtualname__ = 'rethinkdb' +__virtualname__ = "rethinkdb" __opts__ = { - 'rethinkdb.host': 'salt', - 'rethinkdb.port': '28015', - 'rethinkdb.database': 'salt', - 'rethinkdb.username': None, - 'rethinkdb.password': None + "rethinkdb.host": "salt", + "rethinkdb.port": "28015", + "rethinkdb.database": "salt", + "rethinkdb.username": None, + "rethinkdb.password": None, } @@ -75,16 +76,14 @@ def __virtual__(): log = logging.getLogger(__name__) -def ext_pillar(minion_id, - pillar, - table='pillar', - id_field=None, - field=None, - pillar_key=None): - ''' +def ext_pillar( + minion_id, pillar, table="pillar", id_field=None, field=None, pillar_key=None +): + """ Collect minion external pillars from a RethinkDB database Arguments: + * `table`: The RethinkDB table containing external pillar information. Defaults to ``'pillar'`` * `id_field`: Field in document containing the minion id. @@ -94,47 +93,58 @@ def ext_pillar(minion_id, * `pillar_key`: The salt-master will nest found external pillars under this key before merging into the minion pillars. If blank, external pillars will be merged at top level - ''' - host = __opts__['rethinkdb.host'] - port = __opts__['rethinkdb.port'] - database = __opts__['rethinkdb.database'] - username = __opts__['rethinkdb.username'] - password = __opts__['rethinkdb.password'] + """ + host = __opts__["rethinkdb.host"] + port = __opts__["rethinkdb.port"] + database = __opts__["rethinkdb.database"] + username = __opts__["rethinkdb.username"] + password = __opts__["rethinkdb.password"] - log.debug('Connecting to %s:%s as user \'%s\' for RethinkDB ext_pillar', - host, port, username) + log.debug( + "Connecting to %s:%s as user '%s' for RethinkDB ext_pillar", + host, + port, + username, + ) # Connect to the database - conn = rethinkdb.connect(host=host, - port=port, - db=database, - user=username, - password=password) + conn = rethinkdb.connect( + host=host, port=port, db=database, user=username, password=password + ) data = None try: if id_field: - log.debug('ext_pillar.rethinkdb: looking up pillar. ' - 'table: %s, field: %s, minion: %s', - table, id_field, minion_id) + log.debug( + "ext_pillar.rethinkdb: looking up pillar. " + "table: %s, field: %s, minion: %s", + table, + id_field, + minion_id, + ) if field: - data = rethinkdb.table(table).filter( - {id_field: minion_id}).pluck(field).run(conn) + data = ( + rethinkdb.table(table) + .filter({id_field: minion_id}) + .pluck(field) + .run(conn) + ) else: - data = rethinkdb.table(table).filter( - {id_field: minion_id}).run(conn) + data = rethinkdb.table(table).filter({id_field: minion_id}).run(conn) else: - log.debug('ext_pillar.rethinkdb: looking up pillar. ' - 'table: %s, field: id, minion: %s', - table, minion_id) + log.debug( + "ext_pillar.rethinkdb: looking up pillar. " + "table: %s, field: id, minion: %s", + table, + minion_id, + ) if field: - data = rethinkdb.table(table).get(minion_id).pluck(field).run( - conn) + data = rethinkdb.table(table).get(minion_id).pluck(field).run(conn) else: data = rethinkdb.table(table).get(minion_id).run(conn) @@ -146,8 +156,10 @@ def ext_pillar(minion_id, # Return nothing if multiple documents are found for a minion if len(data.items) > 1: - log.error('ext_pillar.rethinkdb: ambiguous documents found for ' - 'minion %s', minion_id) + log.error( + "ext_pillar.rethinkdb: ambiguous documents found for " "minion %s", + minion_id, + ) return {} else: @@ -159,5 +171,5 @@ def ext_pillar(minion_id, else: # No document found in the database - log.debug('ext_pillar.rethinkdb: no document found') + log.debug("ext_pillar.rethinkdb: no document found") return {} diff --git a/salt/pillar/s3.py b/salt/pillar/s3.py index 43ee48d8cf9..0d1cc5c37e9 100644 --- a/salt/pillar/s3.py +++ b/salt/pillar/s3.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Copy pillar data from a bucket in Amazon S3 The S3 pillar can be configured in the master config file with the following @@ -86,35 +86,49 @@ If you wish to define your pillar data entirely within S3 it's recommended that you use the `prefix=` parameter and specify one entry in ext_pillar for each environment rather than specifying multiple_env. This is due to issue #22471 (https://github.com/saltstack/salt/issues/22471) -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os -import time import pickle +import time from copy import deepcopy +import salt.utils.files +import salt.utils.hashutils + # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six from salt.ext.six.moves import filter from salt.ext.six.moves.urllib.parse import quote as _quote -# pylint: enable=import-error,no-name-in-module,redefined-builtin # Import salt libs from salt.pillar import Pillar -import salt.utils.files -import salt.utils.hashutils + +# pylint: enable=import-error,no-name-in-module,redefined-builtin + # Set up logging log = logging.getLogger(__name__) class S3Credentials(object): - def __init__(self, key, keyid, bucket, service_url, verify_ssl=True, - kms_keyid=None, location=None, path_style=False, https_enable=True): + def __init__( + self, + key, + keyid, + bucket, + service_url, + verify_ssl=True, + kms_keyid=None, + location=None, + path_style=False, + https_enable=True, + ): self.key = key self.keyid = keyid self.kms_keyid = kms_keyid @@ -126,61 +140,75 @@ class S3Credentials(object): self.https_enable = https_enable -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - bucket, - key=None, - keyid=None, - verify_ssl=True, - location=None, - multiple_env=False, - environment='base', - prefix='', - service_url=None, - kms_keyid=None, - s3_cache_expire=30, # cache for 30 seconds - s3_sync_on_update=True, # sync cache on update rather than jit - path_style=False, - https_enable=True): +def ext_pillar( + minion_id, + pillar, # pylint: disable=W0613 + bucket, + key=None, + keyid=None, + verify_ssl=True, + location=None, + multiple_env=False, + environment="base", + prefix="", + service_url=None, + kms_keyid=None, + s3_cache_expire=30, # cache for 30 seconds + s3_sync_on_update=True, # sync cache on update rather than jit + path_style=False, + https_enable=True, +): - ''' + """ Execute a command and read the output as YAML - ''' + """ - s3_creds = S3Credentials(key, keyid, bucket, service_url, verify_ssl, - kms_keyid, location, path_style, https_enable) + s3_creds = S3Credentials( + key, + keyid, + bucket, + service_url, + verify_ssl, + kms_keyid, + location, + path_style, + https_enable, + ) # normpath is needed to remove appended '/' if root is empty string. - pillar_dir = os.path.normpath(os.path.join(_get_cache_dir(), environment, - bucket)) + pillar_dir = os.path.normpath(os.path.join(_get_cache_dir(), environment, bucket)) if prefix: pillar_dir = os.path.normpath(os.path.join(pillar_dir, prefix)) - if __opts__['pillar_roots'].get(environment, []) == [pillar_dir]: + if __opts__["pillar_roots"].get(environment, []) == [pillar_dir]: return {} - metadata = _init(s3_creds, bucket, multiple_env, environment, prefix, s3_cache_expire) + metadata = _init( + s3_creds, bucket, multiple_env, environment, prefix, s3_cache_expire + ) if s3_sync_on_update: # sync the buckets to the local cache - log.info('Syncing local pillar cache from S3...') + log.info("Syncing local pillar cache from S3...") for saltenv, env_meta in six.iteritems(metadata): for bucket, files in six.iteritems(_find_files(env_meta)): for file_path in files: - cached_file_path = _get_cached_file_name(bucket, saltenv, - file_path) - log.info('%s - %s : %s', bucket, saltenv, file_path) + cached_file_path = _get_cached_file_name(bucket, saltenv, file_path) + log.info("%s - %s : %s", bucket, saltenv, file_path) # load the file from S3 if not in the cache or too old - _get_file_from_s3(s3_creds, metadata, saltenv, bucket, - file_path, cached_file_path) + _get_file_from_s3( + s3_creds, metadata, saltenv, bucket, file_path, cached_file_path + ) - log.info('Sync local pillar cache from S3 completed.') + log.info("Sync local pillar cache from S3 completed.") opts = deepcopy(__opts__) - opts['pillar_roots'][environment] = [os.path.join(pillar_dir, environment)] if multiple_env else [pillar_dir] + opts["pillar_roots"][environment] = ( + [os.path.join(pillar_dir, environment)] if multiple_env else [pillar_dir] + ) # Avoid recursively re-adding this same pillar - opts['ext_pillar'] = [x for x in opts['ext_pillar'] if 's3' not in x] + opts["ext_pillar"] = [x for x in opts["ext_pillar"] if "s3" not in x] pil = Pillar(opts, __grains__, minion_id, environment) @@ -190,10 +218,10 @@ def ext_pillar(minion_id, def _init(creds, bucket, multiple_env, environment, prefix, s3_cache_expire): - ''' + """ Connect to S3 and download the metadata for each file in all buckets specified and cache the data to disk. - ''' + """ cache_file = _get_buckets_cache_filename(bucket, prefix) exp = time.time() - s3_cache_expire @@ -205,44 +233,45 @@ def _init(creds, bucket, multiple_env, environment, prefix, s3_cache_expire): # file does not exists then set mtime to 0 (aka epoch) cache_file_mtime = 0 - expired = (cache_file_mtime <= exp) + expired = cache_file_mtime <= exp log.debug( - 'S3 bucket cache file %s is %sexpired, mtime_diff=%ss, expiration=%ss', + "S3 bucket cache file %s is %sexpired, mtime_diff=%ss, expiration=%ss", cache_file, - '' if expired else 'not ', + "" if expired else "not ", cache_file_mtime - exp, - s3_cache_expire + s3_cache_expire, ) if expired: - pillars = _refresh_buckets_cache_file(creds, cache_file, multiple_env, - environment, prefix) + pillars = _refresh_buckets_cache_file( + creds, cache_file, multiple_env, environment, prefix + ) else: pillars = _read_buckets_cache_file(cache_file) - log.debug('S3 bucket retrieved pillars %s', pillars) + log.debug("S3 bucket retrieved pillars %s", pillars) return pillars def _get_cache_dir(): - ''' + """ Get pillar cache directory. Initialize it if it does not exist. - ''' + """ - cache_dir = os.path.join(__opts__['cachedir'], 'pillar_s3fs') + cache_dir = os.path.join(__opts__["cachedir"], "pillar_s3fs") if not os.path.isdir(cache_dir): - log.debug('Initializing S3 Pillar Cache') + log.debug("Initializing S3 Pillar Cache") os.makedirs(cache_dir) return cache_dir def _get_cached_file_name(bucket, saltenv, path): - ''' + """ Return the cached file name for a bucket path file - ''' + """ file_path = os.path.join(_get_cache_dir(), saltenv, bucket, path) @@ -254,33 +283,33 @@ def _get_cached_file_name(bucket, saltenv, path): def _get_buckets_cache_filename(bucket, prefix): - ''' + """ Return the filename of the cache for bucket contents. Create the path if it does not exist. - ''' + """ cache_dir = _get_cache_dir() if not os.path.exists(cache_dir): os.makedirs(cache_dir) - return os.path.join(cache_dir, '{0}-{1}-files.cache'.format(bucket, prefix)) + return os.path.join(cache_dir, "{0}-{1}-files.cache".format(bucket, prefix)) def _refresh_buckets_cache_file(creds, cache_file, multiple_env, environment, prefix): - ''' + """ Retrieve the content of all buckets and cache the metadata to the buckets cache file - ''' + """ # helper s3 query function def __get_s3_meta(continuation_token=None): # We want to use ListObjectsV2 so we get the NextContinuationToken - params = {'prefix': prefix, 'list-type': 2} + params = {"prefix": prefix, "list-type": 2} if continuation_token: - params['continuation-token'] = continuation_token + params["continuation-token"] = continuation_token - return __utils__['s3.query']( + return __utils__["s3.query"]( key=creds.key, keyid=creds.keyid, kms_keyid=creds.kms_keyid, @@ -291,31 +320,36 @@ def _refresh_buckets_cache_file(creds, cache_file, multiple_env, environment, pr return_bin=False, params=params, path_style=creds.path_style, - https_enable=creds.https_enable) + https_enable=creds.https_enable, + ) # grab only the files/dirs in the bucket def __get_pillar_files_from_s3_meta(s3_meta): - return [k for k in s3_meta if 'Key' in k] + return [k for k in s3_meta if "Key" in k] # pull out the environment dirs (e.g. the root dirs) def __get_pillar_environments(files): - environments = [(os.path.dirname(k['Key']).split('/', 1))[0] for k in files] + environments = [(os.path.dirname(k["Key"]).split("/", 1))[0] for k in files] return set(environments) def __get_continuation_token(s3_meta): - return next((item.get('NextContinuationToken') - for item in s3_meta - if item.get('NextContinuationToken')), - None) + return next( + ( + item.get("NextContinuationToken") + for item in s3_meta + if item.get("NextContinuationToken") + ), + None, + ) - log.debug('Refreshing S3 buckets pillar cache file') + log.debug("Refreshing S3 buckets pillar cache file") metadata = {} bucket = creds.bucket if not multiple_env: # Single environment per bucket - log.debug('Single environment per bucket mode') + log.debug("Single environment per bucket mode") bucket_files = {} s3_meta = __get_s3_meta() @@ -336,7 +370,7 @@ def _refresh_buckets_cache_file(creds, cache_file, multiple_env, environment, pr else: # Multiple environments per buckets - log.debug('Multiple environment per bucket mode') + log.debug("Multiple environment per bucket mode") s3_meta = __get_s3_meta() # s3 query returned data @@ -356,7 +390,7 @@ def _refresh_buckets_cache_file(creds, cache_file, multiple_env, environment, pr # pull out the files for the environment for saltenv in environments: # grab only files/dirs that match this saltenv. - env_files = [k for k in files if k['Key'].startswith(saltenv)] + env_files = [k for k in files if k["Key"].startswith(saltenv)] if saltenv not in metadata: metadata[saltenv] = {} @@ -370,31 +404,31 @@ def _refresh_buckets_cache_file(creds, cache_file, multiple_env, environment, pr if os.path.isfile(cache_file): os.remove(cache_file) - log.debug('Writing S3 buckets pillar cache file') + log.debug("Writing S3 buckets pillar cache file") - with salt.utils.files.fopen(cache_file, 'wb') as fp_: + with salt.utils.files.fopen(cache_file, "wb") as fp_: pickle.dump(metadata, fp_) return metadata def _read_buckets_cache_file(cache_file): - ''' + """ Return the contents of the buckets cache file - ''' + """ - log.debug('Reading buckets cache file') + log.debug("Reading buckets cache file") - with salt.utils.files.fopen(cache_file, 'rb') as fp_: + with salt.utils.files.fopen(cache_file, "rb") as fp_: data = pickle.load(fp_) return data def _find_files(metadata): - ''' + """ Looks for all the files in the S3 bucket cache metadata - ''' + """ ret = {} @@ -403,50 +437,54 @@ def _find_files(metadata): ret[bucket] = [] # grab the paths from the metadata - filePaths = [k['Key'] for k in data] + filePaths = [k["Key"] for k in data] # filter out the dirs - ret[bucket] += [k for k in filePaths if not k.endswith('/')] + ret[bucket] += [k for k in filePaths if not k.endswith("/")] return ret def _find_file_meta(metadata, bucket, saltenv, path): - ''' + """ Looks for a file's metadata in the S3 bucket cache file - ''' + """ env_meta = metadata[saltenv] if saltenv in metadata else {} bucket_meta = env_meta[bucket] if bucket in env_meta else {} - files_meta = list(list(filter((lambda k: 'Key' in k), bucket_meta))) + files_meta = list(list(filter((lambda k: "Key" in k), bucket_meta))) for item_meta in files_meta: - if 'Key' in item_meta and item_meta['Key'] == path: + if "Key" in item_meta and item_meta["Key"] == path: return item_meta -def _get_file_from_s3(creds, metadata, saltenv, bucket, path, - cached_file_path): - ''' +def _get_file_from_s3(creds, metadata, saltenv, bucket, path, cached_file_path): + """ Checks the local cache for the file, if it's old or missing go grab the file from S3 and update the cache - ''' + """ # check the local cache... if os.path.isfile(cached_file_path): file_meta = _find_file_meta(metadata, bucket, saltenv, path) - file_md5 = "".join(list(filter(str.isalnum, file_meta['ETag']))) \ - if file_meta else None + file_md5 = ( + "".join(list(filter(str.isalnum, file_meta["ETag"]))) if file_meta else None + ) - cached_md5 = salt.utils.hashutils.get_hash(cached_file_path, 'md5') + cached_md5 = salt.utils.hashutils.get_hash(cached_file_path, "md5") # hashes match we have a cache hit - log.debug('Cached file: path=%s, md5=%s, etag=%s', - cached_file_path, cached_md5, file_md5) + log.debug( + "Cached file: path=%s, md5=%s, etag=%s", + cached_file_path, + cached_md5, + file_md5, + ) if cached_md5 == file_md5: return # ... or get the file from S3 - __utils__['s3.query']( + __utils__["s3.query"]( key=creds.key, keyid=creds.keyid, kms_keyid=creds.kms_keyid, @@ -457,5 +495,5 @@ def _get_file_from_s3(creds, metadata, saltenv, bucket, path, verify_ssl=creds.verify_ssl, location=creds.location, path_style=creds.path_style, - https_enable=creds.https_enable + https_enable=creds.https_enable, ) diff --git a/salt/pillar/saltclass.py b/salt/pillar/saltclass.py index bd0be039493..614bec549de 100644 --- a/salt/pillar/saltclass.py +++ b/salt/pillar/saltclass.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" SaltClass Pillar Module ======================= @@ -10,27 +10,29 @@ SaltClass Pillar Module - path: /srv/saltclass For additional configuration instructions, see the :mod:`saltclass <salt.tops.saltclass>` module -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals -import salt.utils.saltclass as sc + import logging +import salt.utils.saltclass as sc + log = logging.getLogger(__name__) def __virtual__(): - ''' + """ This module has no external dependencies - ''' + """ return True def ext_pillar(minion_id, pillar, *args, **kwargs): - ''' + """ Compile pillar data - ''' + """ # Node definitions path will be retrieved from args (or set to default), # then added to 'salt_data' dict that is passed to the 'get_pillars' # function. The dictionary contains: @@ -45,21 +47,21 @@ def ext_pillar(minion_id, pillar, *args, **kwargs): # If path has not been set, make a default for i in args: - if 'path' not in i: - path = '/srv/saltclass' - args[i]['path'] = path - log.warning('path variable unset, using default: %s', path) + if "path" not in i: + path = "/srv/saltclass" + args[i]["path"] = path + log.warning("path variable unset, using default: %s", path) else: - path = i['path'] + path = i["path"] # Create a dict that will contain our salt dicts to pass it to reclass salt_data = { - '__opts__': __opts__, - '__salt__': __salt__, - '__grains__': __grains__, - '__pillar__': pillar, - 'minion_id': minion_id, - 'path': path + "__opts__": __opts__, + "__salt__": __salt__, + "__grains__": __grains__, + "__pillar__": pillar, + "minion_id": minion_id, + "path": path, } return sc.get_pillars(minion_id, salt_data) diff --git a/salt/pillar/sql_base.py b/salt/pillar/sql_base.py index eeb2269f775..976ca8c0d87 100644 --- a/salt/pillar/sql_base.py +++ b/salt/pillar/sql_base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Retrieve Pillar data by doing a SQL query This module is not meant to be used directly as an ext_pillar. @@ -167,22 +167,25 @@ More complete example for MySQL (to also show configuration) depth: 5 as_list: True with_lists: [1,3] -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import abc # Added in python2.6 so always available + +# Import python libs +import logging + +from salt.ext import six +from salt.ext.six.moves import range + +# Import Salt libs +from salt.utils.odict import OrderedDict + # Please don't strip redundant parentheses from this file. # I have added some for clarity. # tests/unit/pillar/mysql_test.py may help understand this code. -# Import python libs -import logging -import abc # Added in python2.6 so always available - -# Import Salt libs -from salt.utils.odict import OrderedDict -from salt.ext.six.moves import range -from salt.ext import six # Set up logging log = logging.getLogger(__name__) @@ -194,10 +197,10 @@ def __virtual__(): class SqlBaseExtPillar(six.with_metaclass(abc.ABCMeta, object)): - ''' + """ This class receives and processes the database rows in a database agnostic way. - ''' + """ result = None focus = None @@ -214,22 +217,22 @@ class SqlBaseExtPillar(six.with_metaclass(abc.ABCMeta, object)): @classmethod @abc.abstractmethod def _db_name(cls): - ''' + """ Return a friendly name for the database, e.g. 'MySQL' or 'SQLite'. Used in logging output. - ''' + """ @abc.abstractmethod def _get_cursor(self): - ''' + """ Yield a PEP 249 compliant Cursor as a context manager. - ''' + """ def extract_queries(self, args, kwargs): - ''' + """ This function normalizes the config block into a set of queries we can use. The return is a list of consistently laid out dicts. - ''' + """ # Please note the function signature is NOT an error. Neither args, nor # kwargs should have asterisks. We are passing in a list and dict, # rather than receiving variable args. Adding asterisks WILL BREAK the @@ -248,43 +251,48 @@ class SqlBaseExtPillar(six.with_metaclass(abc.ABCMeta, object)): qbuffer.extend([[k, kwargs[k]] for k in klist]) # Filter out values that don't have queries. - qbuffer = [x for x in qbuffer if ( + qbuffer = [ + x + for x in qbuffer + if ( (isinstance(x[1], six.string_types) and len(x[1])) - or - (isinstance(x[1], (list, tuple)) and (len(x[1]) > 0) and x[1][0]) - or - (isinstance(x[1], dict) and 'query' in x[1] and len(x[1]['query'])) - )] + or (isinstance(x[1], (list, tuple)) and (len(x[1]) > 0) and x[1][0]) + or (isinstance(x[1], dict) and "query" in x[1] and len(x[1]["query"])) + ) + ] # Next, turn the whole buffer into full dicts. for qb in qbuffer: - defaults = {'query': '', - 'depth': 0, - 'as_list': False, - 'with_lists': None, - 'ignore_null': False - } + defaults = { + "query": "", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + } if isinstance(qb[1], six.string_types): - defaults['query'] = qb[1] + defaults["query"] = qb[1] elif isinstance(qb[1], (list, tuple)): - defaults['query'] = qb[1][0] + defaults["query"] = qb[1][0] if len(qb[1]) > 1: - defaults['depth'] = qb[1][1] + defaults["depth"] = qb[1][1] # May set 'as_list' from qb[1][2]. else: defaults.update(qb[1]) - if defaults['with_lists'] and isinstance(defaults['with_lists'], six.string_types): - defaults['with_lists'] = [ - int(i) for i in defaults['with_lists'].split(',') + if defaults["with_lists"] and isinstance( + defaults["with_lists"], six.string_types + ): + defaults["with_lists"] = [ + int(i) for i in defaults["with_lists"].split(",") ] qb[1] = defaults return qbuffer def enter_root(self, root): - ''' + """ Set self.focus for kwarg queries - ''' + """ # There is no collision protection on root name isolation if root: self.result[root] = self.focus = {} @@ -292,10 +300,10 @@ class SqlBaseExtPillar(six.with_metaclass(abc.ABCMeta, object)): self.focus = self.result def process_fields(self, field_names, depth): - ''' + """ The primary purpose of this function is to store the sql field list and the depth to which we process. - ''' + """ # List of field names in correct order. self.field_names = field_names # number of fields. @@ -307,19 +315,19 @@ class SqlBaseExtPillar(six.with_metaclass(abc.ABCMeta, object)): self.depth = depth def process_results(self, rows): - ''' + """ This function takes a list of database results and iterates over, merging them into a dict form. - ''' + """ listify = OrderedDict() listify_dicts = OrderedDict() for ret in rows: # crd is the Current Return Data level, to make this non-recursive. crd = self.focus # Walk and create dicts above the final layer - for i in range(0, self.depth-1): + for i in range(0, self.depth - 1): # At the end we'll use listify to find values to make a list of - if i+1 in self.with_lists: + if i + 1 in self.with_lists: if id(crd) not in listify: listify[id(crd)] = [] listify_dicts[id(crd)] = crd @@ -354,39 +362,38 @@ class SqlBaseExtPillar(six.with_metaclass(abc.ABCMeta, object)): # If this test is true, the penultimate field is the key if self.depth == self.num_fields - 1: - nk = self.num_fields-2 # Aka, self.depth-1 + nk = self.num_fields - 2 # Aka, self.depth-1 # Should we and will we have a list at the end? - if ((self.as_list and (ret[nk] in crd)) or - (nk+1 in self.with_lists)): + if (self.as_list and (ret[nk] in crd)) or (nk + 1 in self.with_lists): if ret[nk] in crd: if not isinstance(crd[ret[nk]], list): crd[ret[nk]] = [crd[ret[nk]]] # if it's already a list, do nothing else: crd[ret[nk]] = [] - crd[ret[nk]].append(ret[self.num_fields-1]) + crd[ret[nk]].append(ret[self.num_fields - 1]) else: - if not self.ignore_null or ret[self.num_fields-1] is not None: - crd[ret[nk]] = ret[self.num_fields-1] + if not self.ignore_null or ret[self.num_fields - 1] is not None: + crd[ret[nk]] = ret[self.num_fields - 1] else: # Otherwise, the field name is the key but we have a spare. # The spare results because of {c: d} vs {c: {"d": d, "e": e }} # So, make that last dict - if ret[self.depth-1] not in crd: - crd[ret[self.depth-1]] = {} + if ret[self.depth - 1] not in crd: + crd[ret[self.depth - 1]] = {} # This bit doesn't escape listify if self.depth in self.with_lists: if id(crd) not in listify: listify[id(crd)] = [] listify_dicts[id(crd)] = crd - if ret[self.depth-1] not in listify[id(crd)]: - listify[id(crd)].append(ret[self.depth-1]) - crd = crd[ret[self.depth-1]] + if ret[self.depth - 1] not in listify[id(crd)]: + listify[id(crd)].append(ret[self.depth - 1]) + crd = crd[ret[self.depth - 1]] # Now for the remaining keys, we put them into the dict for i in range(self.depth, self.num_fields): nk = self.field_names[i] # Listify - if i+1 in self.with_lists: + if i + 1 in self.with_lists: if id(crd) not in listify: listify[id(crd)] = [] listify_dicts[id(crd)] = crd @@ -413,16 +420,12 @@ class SqlBaseExtPillar(six.with_metaclass(abc.ABCMeta, object)): elif isinstance(d[k], list): d[k] = [d[k]] - def fetch(self, - minion_id, - pillar, # pylint: disable=W0613 - *args, - **kwargs): - ''' + def fetch(self, minion_id, pillar, *args, **kwargs): # pylint: disable=W0613 + """ Execute queries, merge and return as a dict. - ''' + """ db_name = self._db_name() - log.info('Querying %s for information for %s', db_name, minion_id) + log.info("Querying %s for information for %s", db_name, minion_id) # # log.debug('ext_pillar %s args: %s', db_name, args) # log.debug('ext_pillar %s kwargs: %s', db_name, kwargs) @@ -432,20 +435,22 @@ class SqlBaseExtPillar(six.with_metaclass(abc.ABCMeta, object)): with self._get_cursor() as cursor: for root, details in qbuffer: # Run the query - cursor.execute(details['query'], (minion_id,)) + cursor.execute(details["query"], (minion_id,)) # Extract the field names the db has returned and process them - self.process_fields([row[0] for row in cursor.description], details['depth']) + self.process_fields( + [row[0] for row in cursor.description], details["depth"] + ) self.enter_root(root) - self.as_list = details['as_list'] - if details['with_lists']: - self.with_lists = details['with_lists'] + self.as_list = details["as_list"] + if details["with_lists"]: + self.with_lists = details["with_lists"] else: self.with_lists = [] - self.ignore_null = details['ignore_null'] + self.ignore_null = details["ignore_null"] self.process_results(cursor.fetchall()) - log.debug('ext_pillar %s: Return data: %s', db_name, self) + log.debug("ext_pillar %s: Return data: %s", db_name, self) return self.result diff --git a/salt/pillar/sqlcipher.py b/salt/pillar/sqlcipher.py index 3f28abb1819..902460032d5 100644 --- a/salt/pillar/sqlcipher.py +++ b/salt/pillar/sqlcipher.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Retrieve Pillar data by running a SQLCipher query .. versionadded:: 2016.3.0 @@ -56,12 +56,13 @@ Complete Example depth: 5 as_list: True with_lists: [1,3] -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs from contextlib import contextmanager -import logging # Import Salt libs from salt.pillar.sql_base import SqlBaseExtPillar @@ -72,6 +73,7 @@ log = logging.getLogger(__name__) # Import third party libs try: from pysqlcipher import dbapi2 as sqlcipher + HAS_SQLCIPHER = True except ImportError: HAS_SQLCIPHER = False @@ -84,26 +86,29 @@ def __virtual__(): class SQLCipherExtPillar(SqlBaseExtPillar): - ''' + """ This class receives and processes the database rows from SQLCipher. - ''' + """ + @classmethod def _db_name(cls): - return 'SQLCipher' + return "SQLCipher" def _get_options(self): - ''' + """ Returns options used for the SQLCipher connection. - ''' - defaults = {'database': '/var/lib/salt/pillar-sqlcipher.db', - 'pass': 'strong_pass_phrase', - 'timeout': 5.0} + """ + defaults = { + "database": "/var/lib/salt/pillar-sqlcipher.db", + "pass": "strong_pass_phrase", + "timeout": 5.0, + } _options = {} - _opts = __opts__.get('sqlcipher', {}) + _opts = __opts__.get("sqlcipher", {}) for attr in defaults: if attr not in _opts: - log.debug('Using default for SQLCipher pillar %s', attr) + log.debug("Using default for SQLCipher pillar %s", attr) _options[attr] = defaults[attr] continue _options[attr] = _opts[attr] @@ -111,27 +116,25 @@ class SQLCipherExtPillar(SqlBaseExtPillar): @contextmanager def _get_cursor(self): - ''' + """ Yield a SQLCipher cursor - ''' + """ _options = self._get_options() - conn = sqlcipher.connect(_options.get('database'), - timeout=float(_options.get('timeout'))) - conn.execute('pragma key="{0}"'.format(_options.get('pass'))) + conn = sqlcipher.connect( + _options.get("database"), timeout=float(_options.get("timeout")) + ) + conn.execute('pragma key="{0}"'.format(_options.get("pass"))) cursor = conn.cursor() try: yield cursor except sqlcipher.Error as err: - log.exception('Error in ext_pillar SQLCipher: %s', err.args) + log.exception("Error in ext_pillar SQLCipher: %s", err.args) finally: conn.close() -def ext_pillar(minion_id, - pillar, - *args, - **kwargs): - ''' +def ext_pillar(minion_id, pillar, *args, **kwargs): + """ Execute queries against SQLCipher, merge and return as a dict - ''' + """ return SQLCipherExtPillar().fetch(minion_id, pillar, *args, **kwargs) diff --git a/salt/pillar/sqlite3.py b/salt/pillar/sqlite3.py index 232e4e509d3..2a232f4ad92 100644 --- a/salt/pillar/sqlite3.py +++ b/salt/pillar/sqlite3.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Retrieve Pillar data by doing a SQLite3 query .. versionadded:: 2015.8.0 @@ -45,13 +45,14 @@ Complete Example depth: 5 as_list: True with_lists: [1,3] -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging +import sqlite3 + # Import python libs from contextlib import contextmanager -import logging -import sqlite3 # Import Salt libs from salt.pillar.sql_base import SqlBaseExtPillar @@ -65,26 +66,26 @@ def __virtual__(): class SQLite3ExtPillar(SqlBaseExtPillar): - ''' + """ This class receives and processes the database rows from SQLite3. - ''' + """ + @classmethod def _db_name(cls): - return 'SQLite3' + return "SQLite3" def _get_options(self): - ''' + """ Returns options used for the SQLite3 connection. - ''' - defaults = {'database': '/var/lib/salt/pillar.db', - 'timeout': 5.0} + """ + defaults = {"database": "/var/lib/salt/pillar.db", "timeout": 5.0} _options = {} _opts = {} - if 'sqlite3' in __opts__ and 'database' in __opts__['sqlite3']: - _opts = __opts__.get('sqlite3', {}) + if "sqlite3" in __opts__ and "database" in __opts__["sqlite3"]: + _opts = __opts__.get("sqlite3", {}) for attr in defaults: if attr not in _opts: - log.debug('Using default for SQLite3 pillar %s', attr) + log.debug("Using default for SQLite3 pillar %s", attr) _options[attr] = defaults[attr] continue _options[attr] = _opts[attr] @@ -92,26 +93,24 @@ class SQLite3ExtPillar(SqlBaseExtPillar): @contextmanager def _get_cursor(self): - ''' + """ Yield a SQLite3 cursor - ''' + """ _options = self._get_options() - conn = sqlite3.connect(_options.get('database'), - timeout=float(_options.get('timeout'))) + conn = sqlite3.connect( + _options.get("database"), timeout=float(_options.get("timeout")) + ) cursor = conn.cursor() try: yield cursor except sqlite3.Error as err: - log.exception('Error in ext_pillar SQLite3: %s', err.args) + log.exception("Error in ext_pillar SQLite3: %s", err.args) finally: conn.close() -def ext_pillar(minion_id, - pillar, - *args, - **kwargs): - ''' +def ext_pillar(minion_id, pillar, *args, **kwargs): + """ Execute queries against SQLite3, merge and return as a dict - ''' + """ return SQLite3ExtPillar().fetch(minion_id, pillar, *args, **kwargs) diff --git a/salt/pillar/stack.py b/salt/pillar/stack.py index e93cd9453e1..d3531db1bcf 100644 --- a/salt/pillar/stack.py +++ b/salt/pillar/stack.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Simple and flexible YAML ext_pillar which can read pillar from within pillar. .. versionadded:: 2016.3.0 @@ -372,49 +372,51 @@ You can also select a custom merging strategy using a ``__`` object in a list: | - tom | - __: overwrite | - mat | | - root | - mat | | +----------------+-------------------------+-------------------------+ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import functools import glob +import logging import os import posixpath -import logging -from jinja2 import FileSystemLoader, Environment - -# Import Salt libs -from salt.ext import six import salt.utils.data import salt.utils.jinja import salt.utils.yaml +from jinja2 import Environment, FileSystemLoader + +# Import Salt libs +from salt.ext import six log = logging.getLogger(__name__) -strategies = ('overwrite', 'merge-first', 'merge-last', 'remove') +strategies = ("overwrite", "merge-first", "merge-last", "remove") def ext_pillar(minion_id, pillar, *args, **kwargs): stack = {} stack_config_files = list(args) traverse = { - 'pillar': functools.partial(salt.utils.data.traverse_dict_and_list, pillar), - 'grains': functools.partial(salt.utils.data.traverse_dict_and_list, __grains__), - 'opts': functools.partial(salt.utils.data.traverse_dict_and_list, __opts__), - } + "pillar": functools.partial(salt.utils.data.traverse_dict_and_list, pillar), + "grains": functools.partial(salt.utils.data.traverse_dict_and_list, __grains__), + "opts": functools.partial(salt.utils.data.traverse_dict_and_list, __opts__), + } for matcher, matchs in six.iteritems(kwargs): - t, matcher = matcher.split(':', 1) + t, matcher = matcher.split(":", 1) if t not in traverse: - raise Exception('Unknown traverse option "{0}", ' - 'should be one of {1}'.format(t, traverse.keys())) + raise Exception( + 'Unknown traverse option "{0}", ' + "should be one of {1}".format(t, traverse.keys()) + ) cfgs = matchs.get(traverse[t](matcher, None), []) if not isinstance(cfgs, list): cfgs = [cfgs] stack_config_files += cfgs for cfg in stack_config_files: if not os.path.isfile(cfg): - log.info( - 'Ignoring pillar stack cfg "%s": file does not exist', cfg) + log.info('Ignoring pillar stack cfg "%s": file does not exist', cfg) continue stack = _process_stack_cfg(cfg, stack, minion_id, pillar) return stack @@ -425,39 +427,50 @@ def _to_unix_slashes(path): def _process_stack_cfg(cfg, stack, minion_id, pillar): - log.debug('Config: %s', cfg) + log.debug("Config: %s", cfg) basedir, filename = os.path.split(cfg) - jenv = Environment(loader=FileSystemLoader(basedir), extensions=['jinja2.ext.do', salt.utils.jinja.SerializerExtension]) - jenv.globals.update({ - "__opts__": __opts__, - "__salt__": __salt__, - "__grains__": __grains__, - "__stack__": { - 'traverse': salt.utils.data.traverse_dict_and_list, - 'cfg_path': cfg, + jenv = Environment( + loader=FileSystemLoader(basedir), + extensions=["jinja2.ext.do", salt.utils.jinja.SerializerExtension], + ) + jenv.globals.update( + { + "__opts__": __opts__, + "__salt__": __salt__, + "__grains__": __grains__, + "__stack__": { + "traverse": salt.utils.data.traverse_dict_and_list, + "cfg_path": cfg, }, - "minion_id": minion_id, - "pillar": pillar, - }) - for item in _parse_stack_cfg( - jenv.get_template(filename).render(stack=stack)): + "minion_id": minion_id, + "pillar": pillar, + } + ) + for item in _parse_stack_cfg(jenv.get_template(filename).render(stack=stack)): if not item.strip(): continue # silently ignore whitespace or empty lines paths = glob.glob(os.path.join(basedir, item)) if not paths: log.info( 'Ignoring pillar stack template "%s": can\'t find from root ' - 'dir "%s"', item, basedir + 'dir "%s"', + item, + basedir, ) continue for path in sorted(paths): - log.debug('YAML: basedir=%s, path=%s', basedir, path) + log.debug("YAML: basedir=%s, path=%s", basedir, path) # FileSystemLoader always expects unix-style paths unix_path = _to_unix_slashes(os.path.relpath(path, basedir)) - obj = salt.utils.yaml.safe_load(jenv.get_template(unix_path).render(stack=stack, ymlpath=path)) + obj = salt.utils.yaml.safe_load( + jenv.get_template(unix_path).render(stack=stack, ymlpath=path) + ) if not isinstance(obj, dict): - log.info('Ignoring pillar stack template "%s": Can\'t parse ' - 'as a valid yaml dictionary', path) + log.info( + 'Ignoring pillar stack template "%s": Can\'t parse ' + "as a valid yaml dictionary", + path, + ) continue stack = _merge_dict(stack, obj) return stack @@ -466,36 +479,38 @@ def _process_stack_cfg(cfg, stack, minion_id, pillar): def _cleanup(obj): if obj: if isinstance(obj, dict): - obj.pop('__', None) + obj.pop("__", None) for k, v in six.iteritems(obj): obj[k] = _cleanup(v) - elif isinstance(obj, list) and isinstance(obj[0], dict) \ - and '__' in obj[0]: + elif isinstance(obj, list) and isinstance(obj[0], dict) and "__" in obj[0]: del obj[0] return obj def _merge_dict(stack, obj): - strategy = obj.pop('__', 'merge-last') + strategy = obj.pop("__", "merge-last") if strategy not in strategies: - raise Exception('Unknown strategy "{0}", should be one of {1}'.format( - strategy, strategies)) - if strategy == 'overwrite': + raise Exception( + 'Unknown strategy "{0}", should be one of {1}'.format(strategy, strategies) + ) + if strategy == "overwrite": return _cleanup(obj) else: for k, v in six.iteritems(obj): - if strategy == 'remove': + if strategy == "remove": stack.pop(k, None) continue if k in stack: - if strategy == 'merge-first': + if strategy == "merge-first": # merge-first is same as merge-last but the other way round # so let's switch stack[k] and v stack_k = stack[k] stack[k] = _cleanup(v) v = stack_k if type(stack[k]) != type(v): - log.debug('Force overwrite, types differ: \'%s\' != \'%s\'', stack[k], v) + log.debug( + "Force overwrite, types differ: '%s' != '%s'", stack[k], v + ) stack[k] = _cleanup(v) elif isinstance(v, dict): stack[k] = _merge_dict(stack[k], v) @@ -509,27 +524,28 @@ def _merge_dict(stack, obj): def _merge_list(stack, obj): - strategy = 'merge-last' - if obj and isinstance(obj[0], dict) and '__' in obj[0]: - strategy = obj[0]['__'] + strategy = "merge-last" + if obj and isinstance(obj[0], dict) and "__" in obj[0]: + strategy = obj[0]["__"] del obj[0] if strategy not in strategies: - raise Exception('Unknown strategy "{0}", should be one of {1}'.format( - strategy, strategies)) - if strategy == 'overwrite': + raise Exception( + 'Unknown strategy "{0}", should be one of {1}'.format(strategy, strategies) + ) + if strategy == "overwrite": return obj - elif strategy == 'remove': + elif strategy == "remove": return [item for item in stack if item not in obj] - elif strategy == 'merge-first': + elif strategy == "merge-first": return obj + stack else: return stack + obj def _parse_stack_cfg(content): - ''' + """ Allow top level cfg to be YAML - ''' + """ try: obj = salt.utils.yaml.safe_load(content) if isinstance(obj, list): diff --git a/salt/pillar/svn_pillar.py b/salt/pillar/svn_pillar.py index 6b2455197cd..10b3fe888b4 100644 --- a/salt/pillar/svn_pillar.py +++ b/salt/pillar/svn_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Clone a remote SVN repository and use the filesystem as a Pillar source This external Pillar source can be configured in the master config file like @@ -45,56 +45,61 @@ section in it, like this: dev: '*': - bar -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import hashlib +import logging +import os + # Import python libs from copy import deepcopy -import logging -import os -import hashlib + +# Import salt libs +from salt.pillar import Pillar # Import third party libs HAS_SVN = False try: import pysvn + HAS_SVN = True CLIENT = pysvn.Client() except ImportError: pass -# Import salt libs -from salt.pillar import Pillar # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'svn' +__virtualname__ = "svn" def __virtual__(): - ext_pillar_sources = [x for x in __opts__.get('ext_pillar', [])] - if not any(['svn' in x for x in ext_pillar_sources]): + ext_pillar_sources = [x for x in __opts__.get("ext_pillar", [])] + if not any(["svn" in x for x in ext_pillar_sources]): return False if not HAS_SVN: - log.error('SVN-based ext_pillar is enabled in configuration but ' - 'could not be loaded, is pysvn installed?') + log.error( + "SVN-based ext_pillar is enabled in configuration but " + "could not be loaded, is pysvn installed?" + ) return False return __virtualname__ class SvnPillar(object): - ''' + """ Deal with the remote SVN repository for Pillar - ''' + """ def __init__(self, branch, repo_location, root, opts): - ''' + """ Try to initialize the SVN repo object - ''' + """ repo_hash = hashlib.md5(repo_location).hexdigest() - repo_dir = os.path.join(opts['cachedir'], 'pillar_svnfs', repo_hash) + repo_dir = os.path.join(opts["cachedir"], "pillar_svnfs", repo_hash) self.branch = branch self.root = root @@ -103,99 +108,100 @@ class SvnPillar(object): if not os.path.isdir(repo_dir): os.makedirs(repo_dir) - log.debug('Checking out fileserver for svn_pillar module') + log.debug("Checking out fileserver for svn_pillar module") try: CLIENT.checkout(repo_location, repo_dir) except pysvn.ClientError: log.error( - 'Failed to initialize svn_pillar %s %s', - repo_location, repo_dir + "Failed to initialize svn_pillar %s %s", repo_location, repo_dir ) def update(self): try: - log.debug('Updating fileserver for svn_pillar module') + log.debug("Updating fileserver for svn_pillar module") CLIENT.update(self.repo_dir) except pysvn.ClientError as exc: log.error( - 'Unable to fetch the latest changes from remote %s: %s', - self.repo_location, exc + "Unable to fetch the latest changes from remote %s: %s", + self.repo_location, + exc, ) def pillar_dir(self): - ''' + """ Returns the directory of the pillars (repo cache + branch + root) - ''' + """ repo_dir = self.repo_dir root = self.root branch = self.branch - if branch == 'trunk' or branch == 'base': - working_dir = os.path.join(repo_dir, 'trunk', root) + if branch == "trunk" or branch == "base": + working_dir = os.path.join(repo_dir, "trunk", root) if not os.path.isdir(working_dir): - log.error('Could not find %s/trunk/%s', self.repo_location, root) + log.error("Could not find %s/trunk/%s", self.repo_location, root) else: return os.path.normpath(working_dir) - working_dir = os.path.join(repo_dir, 'branches', branch, root) + working_dir = os.path.join(repo_dir, "branches", branch, root) if os.path.isdir(working_dir): return os.path.normpath(working_dir) - working_dir = os.path.join(working_dir, 'tags', branch, root) + working_dir = os.path.join(working_dir, "tags", branch, root) if os.path.isdir(working_dir): return os.path.normpath(working_dir) - log.error('Could not find %s/branches/%s/%s', self.repo_location, branch, root) + log.error("Could not find %s/branches/%s/%s", self.repo_location, branch, root) return repo_dir -def _extract_key_val(kv, delimiter='='): - '''Extract key and value from key=val string. +def _extract_key_val(kv, delimiter="="): + """Extract key and value from key=val string. Example: >>> _extract_key_val('foo=bar') ('foo', 'bar') - ''' + """ pieces = kv.split(delimiter) key = pieces[0] val = delimiter.join(pieces[1:]) return key, val -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - repo_string): - ''' +def ext_pillar(minion_id, pillar, repo_string): # pylint: disable=W0613 + """ Execute a command and read the output as YAML - ''' + """ # split the branch, repo name and optional extra (key=val) parameters. options = repo_string.strip().split() branch = options[0] repo_location = options[1] - root = '' + root = "" for extraopt in options[2:]: # Support multiple key=val attributes as custom parameters. - DELIM = '=' + DELIM = "=" if DELIM not in extraopt: - log.error('Incorrectly formatted extra parameter. ' - 'Missing \'%s\': %s', DELIM, extraopt) + log.error( + "Incorrectly formatted extra parameter. " "Missing '%s': %s", + DELIM, + extraopt, + ) key, val = _extract_key_val(extraopt, DELIM) - if key == 'root': + if key == "root": root = val else: - log.warning('Unrecognized extra parameter: %s', key) + log.warning("Unrecognized extra parameter: %s", key) svnpil = SvnPillar(branch, repo_location, root, __opts__) # environment is "different" from the branch - branch = (branch == 'trunk' and 'base' or branch) + branch = branch == "trunk" and "base" or branch pillar_dir = svnpil.pillar_dir() log.debug("[pillar_roots][%s] = %s", branch, pillar_dir) # Don't recurse forever-- the Pillar object will re-call the ext_pillar # function - if __opts__['pillar_roots'].get(branch, []) == [pillar_dir]: + if __opts__["pillar_roots"].get(branch, []) == [pillar_dir]: return {} svnpil.update() opts = deepcopy(__opts__) - opts['pillar_roots'][branch] = [pillar_dir] + opts["pillar_roots"][branch] = [pillar_dir] pil = Pillar(opts, __grains__, minion_id, branch) return pil.compile_pillar(ext=False) diff --git a/salt/pillar/varstack_pillar.py b/salt/pillar/varstack_pillar.py index 157cedf1d18..831820902a9 100644 --- a/salt/pillar/varstack_pillar.py +++ b/salt/pillar/varstack_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use `Varstack <https://github.com/conversis/varstack>`_ data as a Pillar source Configuring Varstack @@ -17,7 +17,7 @@ Varstack will then use /etc/varstack.yaml to determine which configuration data to return as pillar information. From there you can take a look at the `README <https://github.com/conversis/varstack/blob/master/README.md>`_ of varstack on how this file is evaluated. -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -27,21 +27,21 @@ except ImportError: varstack = None # Define the module's virtual name -__virtualname__ = 'varstack' +__virtualname__ = "varstack" def __virtual__(): return ( varstack and __virtualname__ or False, - 'The varstack module could not be loaded: varstack dependency is missing.' + "The varstack module could not be loaded: varstack dependency is missing.", ) -def ext_pillar(minion_id, # pylint: disable=W0613 - pillar, # pylint: disable=W0613 - conf): - ''' +def ext_pillar( + minion_id, pillar, conf # pylint: disable=W0613 # pylint: disable=W0613 +): + """ Parse varstack data and return the result - ''' + """ vs = varstack.Varstack(config_filename=conf) return vs.evaluate(__grains__) diff --git a/salt/pillar/vault.py b/salt/pillar/vault.py index 3bce53bb054..efefbd55588 100644 --- a/salt/pillar/vault.py +++ b/salt/pillar/vault.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Vault Pillar Module :maintainer: SaltStack @@ -119,36 +119,37 @@ minion-passwd minionbadpasswd1 minion-passwd: minionbadpasswd1 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging log = logging.getLogger(__name__) -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ This module has no external dependencies - ''' + """ return True -def ext_pillar(minion_id, # pylint: disable=W0613 - pillar, # pylint: disable=W0613 - conf, - nesting_key=None): - ''' +def ext_pillar( + minion_id, # pylint: disable=W0613 + pillar, # pylint: disable=W0613 + conf, + nesting_key=None, +): + """ Get pillar data from Vault for the configuration ``conf``. - ''' + """ comps = conf.split() - paths = [comp for comp in comps if comp.startswith('path=')] + paths = [comp for comp in comps if comp.startswith("path=")] if not paths: log.error('"%s" is not a valid Vault ext_pillar config', conf) return {} @@ -156,16 +157,20 @@ def ext_pillar(minion_id, # pylint: disable=W0613 vault_pillar = {} try: - path = paths[0].replace('path=', '') - path = path.format(**{'minion': minion_id}) - url = 'v1/{0}'.format(path) - response = __utils__['vault.make_request']('GET', url) + path = paths[0].replace("path=", "") + path = path.format(**{"minion": minion_id}) + version2 = __utils__["vault.is_v2"](path) + if version2["v2"]: + path = version2["data"] + + url = "v1/{0}".format(path) + response = __utils__["vault.make_request"]("GET", url) if response.status_code == 200: - vault_pillar = response.json().get('data', {}) + vault_pillar = response.json().get("data", {}) else: - log.info('Vault secret not found for: %s', path) + log.info("Vault secret not found for: %s", path) except KeyError: - log.error('No such path in Vault: %s', path) + log.error("No such path in Vault: %s", path) if nesting_key: vault_pillar = {nesting_key: vault_pillar} diff --git a/salt/pillar/venafi.py b/salt/pillar/venafi.py index 74cb89514c4..ec62525cd95 100644 --- a/salt/pillar/venafi.py +++ b/salt/pillar/venafi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Venafi Pillar Certificates This module will only return pillar data if the ``venafi`` runner module has @@ -12,32 +12,34 @@ section of your ``master`` configuration file: ext_pillar: - venafi: True -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging + import salt.cache import salt.syspaths as syspaths -__virtualname__ = 'venafi' +__virtualname__ = "venafi" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ No special requirements outside of Salt itself - ''' + """ return __virtualname__ def ext_pillar(minion_id, pillar, conf): - ''' + """ Return an existing set of certificates - ''' + """ cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) ret = {} - for dns_name in cache.list('venafi/domains'): - data = cache.fetch('venafi/domains', dns_name) - if data['minion_id'] == minion_id: + for dns_name in cache.list("venafi/domains"): + data = cache.fetch("venafi/domains", dns_name) + if data["minion_id"] == minion_id: ret[dns_name] = data - return {'venafi': ret} + return {"venafi": ret} diff --git a/salt/pillar/virtkey.py b/salt/pillar/virtkey.py index 9eb99e610f5..ca5ce4381e9 100644 --- a/salt/pillar/virtkey.py +++ b/salt/pillar/virtkey.py @@ -1,28 +1,28 @@ # -*- coding: utf-8 -*- -''' +""" Accept a key from a hypervisor if the virt runner has already submitted an authorization request -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Don't "fix" the above docstring to put it on two lines, as the sphinx -# autosummary pulls only the first line for its description. - # Import python libs import logging # Import salt libs import salt.utils.virt +# Don't "fix" the above docstring to put it on two lines, as the sphinx +# autosummary pulls only the first line for its description. + # Set up logging log = logging.getLogger(__name__) def ext_pillar(hyper_id, pillar, name, key): - ''' + """ Accept the key for the VM on the hyper, if authorized. - ''' + """ vk = salt.utils.virt.VirtKey(hyper_id, name, __opts__) ok = vk.accept(key) - pillar['virtkey'] = {name: ok} + pillar["virtkey"] = {name: ok} return {} diff --git a/salt/pillar/vmware_pillar.py b/salt/pillar/vmware_pillar.py index a4ed67ccdc1..d2935ca904a 100644 --- a/salt/pillar/vmware_pillar.py +++ b/salt/pillar/vmware_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Pillar data from vCenter or an ESXi host .. versionadded:: 2017.7.0 @@ -141,7 +141,7 @@ Optionally, the following keyword arguments can be passed to the ext_pillar for vCenter "Custom Attributes" (i.e. Annotations) will always be returned if it exists on the object as part of the pillar regardless of this setting. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -153,319 +153,269 @@ import salt.utils.vmware # Import 3rd-party libs from salt.ext import six + try: # pylint: disable=no-name-in-module from pyVmomi import vim from pyVim.connect import Disconnect + HAS_LIBS = True # pylint: enable=no-name-in-module except ImportError: HAS_LIBS = False -__virtualname__ = 'vmware' +__virtualname__ = "vmware" # Set up logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only return if python-etcd is installed - ''' + """ return __virtualname__ if HAS_LIBS else False -def ext_pillar(minion_id, - pillar, # pylint: disable=W0613 - **kwargs): - ''' +def ext_pillar(minion_id, pillar, **kwargs): # pylint: disable=W0613 + """ Check vmware/vcenter for all data - ''' + """ vmware_pillar = {} host = None username = None password = None property_types = [] - property_name = 'name' + property_name = "name" protocol = None port = None - pillar_key = 'vmware' + pillar_key = "vmware" replace_default_attributes = False type_specific_pillar_attributes = { - 'VirtualMachine': [ + "VirtualMachine": [ { - 'config': - [ - 'version', - 'guestId', - 'files', - 'tools', - 'flags', - 'memoryHotAddEnabled', - 'cpuHotAddEnabled', - 'cpuHotRemoveEnabled', - 'datastoreUrl', - 'swapPlacement', - 'bootOptions', - 'scheduledHardwareUpgradeInfo', - 'memoryAllocation', - 'cpuAllocation', - ] + "config": [ + "version", + "guestId", + "files", + "tools", + "flags", + "memoryHotAddEnabled", + "cpuHotAddEnabled", + "cpuHotRemoveEnabled", + "datastoreUrl", + "swapPlacement", + "bootOptions", + "scheduledHardwareUpgradeInfo", + "memoryAllocation", + "cpuAllocation", + ] }, { - 'summary': - [ - { - 'runtime': - [ - { - 'host': - [ - 'name', - {'parent': 'name'}, - ] - }, - 'bootTime', - ] - }, - { - 'guest': - [ - 'toolsStatus', - 'toolsVersionStatus', - 'toolsVersionStatus2', - 'toolsRunningStatus', - ] - }, - { - 'config': - [ - 'cpuReservation', - 'memoryReservation', - ] - }, - { - 'storage': - [ - 'committed', - 'uncommitted', - 'unshared', - ] - }, - {'dasVmProtection': ['dasProtected']}, - ] + "summary": [ + {"runtime": [{"host": ["name", {"parent": "name"}]}, "bootTime"]}, + { + "guest": [ + "toolsStatus", + "toolsVersionStatus", + "toolsVersionStatus2", + "toolsRunningStatus", + ] + }, + {"config": ["cpuReservation", "memoryReservation"]}, + {"storage": ["committed", "uncommitted", "unshared"]}, + {"dasVmProtection": ["dasProtected"]}, + ] }, { - 'storage': - [ - { - 'perDatastoreUsage': - [ - { - 'datastore': 'name' - }, - 'committed', - 'uncommitted', - 'unshared', - ] - } - ] + "storage": [ + { + "perDatastoreUsage": [ + {"datastore": "name"}, + "committed", + "uncommitted", + "unshared", + ] + } + ] }, ], - 'HostSystem': [ + "HostSystem": [ { - 'datastore': - [ - 'name', - 'overallStatus', - { - 'summary': - [ - 'url', - 'freeSpace', - 'maxFileSize', - 'maxVirtualDiskCapacity', - 'maxPhysicalRDMFileSize', - 'maxVirtualRDMFileSize', - { - 'vmfs': - [ - 'capacity', - 'blockSizeMb', - 'maxBlocks', - 'majorVersion', - 'version', - 'uuid', - { - 'extent': - [ - 'diskName', - 'partition', - ] - }, - 'vmfsUpgradeable', - 'ssd', - 'local', - ], - }, + "datastore": [ + "name", + "overallStatus", + { + "summary": [ + "url", + "freeSpace", + "maxFileSize", + "maxVirtualDiskCapacity", + "maxPhysicalRDMFileSize", + "maxVirtualRDMFileSize", + { + "vmfs": [ + "capacity", + "blockSizeMb", + "maxBlocks", + "majorVersion", + "version", + "uuid", + {"extent": ["diskName", "partition"]}, + "vmfsUpgradeable", + "ssd", + "local", ], - }, - {'vm': 'name'} - ] + }, + ], + }, + {"vm": "name"}, + ] }, { - 'vm': - [ - 'name', - 'overallStatus', - { - 'summary': - [ - {'runtime': 'powerState'}, - ] - }, - ] + "vm": [ + "name", + "overallStatus", + {"summary": [{"runtime": "powerState"}]}, + ] }, - ] + ], } pillar_attributes = [ - { - 'summary': - [ - 'overallStatus' - ] - }, - { - 'network': - [ - 'name', - {'config': {'distributedVirtualSwitch': 'name'}}, - ] - }, - { - 'datastore': - [ - 'name', - ] - }, - { - 'parent': - [ - 'name' - ] - }, + {"summary": ["overallStatus"]}, + {"network": ["name", {"config": {"distributedVirtualSwitch": "name"}}]}, + {"datastore": ["name"]}, + {"parent": ["name"]}, ] - if 'pillar_key' in kwargs: - pillar_key = kwargs['pillar_key'] + if "pillar_key" in kwargs: + pillar_key = kwargs["pillar_key"] vmware_pillar[pillar_key] = {} - if 'host' not in kwargs: - log.error('VMWare external pillar configured but host is not specified in ext_pillar configuration.') + if "host" not in kwargs: + log.error( + "VMWare external pillar configured but host is not specified in ext_pillar configuration." + ) return vmware_pillar else: - host = kwargs['host'] - log.debug('vmware_pillar -- host = %s', host) + host = kwargs["host"] + log.debug("vmware_pillar -- host = %s", host) - if 'username' not in kwargs: - log.error('VMWare external pillar requested but username is not specified in ext_pillar configuration.') + if "username" not in kwargs: + log.error( + "VMWare external pillar requested but username is not specified in ext_pillar configuration." + ) return vmware_pillar else: - username = kwargs['username'] - log.debug('vmware_pillar -- username = %s', username) + username = kwargs["username"] + log.debug("vmware_pillar -- username = %s", username) - if 'password' not in kwargs: - log.error('VMWare external pillar requested but password is not specified in ext_pillar configuration.') + if "password" not in kwargs: + log.error( + "VMWare external pillar requested but password is not specified in ext_pillar configuration." + ) return vmware_pillar else: - password = kwargs['password'] - log.debug('vmware_pillar -- password = %s', password) + password = kwargs["password"] + log.debug("vmware_pillar -- password = %s", password) - if 'replace_default_attributes' in kwargs: - replace_default_attributes = kwargs['replace_default_attributes'] + if "replace_default_attributes" in kwargs: + replace_default_attributes = kwargs["replace_default_attributes"] if replace_default_attributes: pillar_attributes = [] type_specific_pillar_attributes = {} - if 'property_types' in kwargs: - for prop_type in kwargs['property_types']: + if "property_types" in kwargs: + for prop_type in kwargs["property_types"]: if isinstance(prop_type, dict): property_types.append(getattr(vim, prop_type.keys()[0])) if isinstance(prop_type[prop_type.keys()[0]], list): - pillar_attributes = pillar_attributes + prop_type[prop_type.keys()[0]] + pillar_attributes = ( + pillar_attributes + prop_type[prop_type.keys()[0]] + ) else: - log.warning('A property_type dict was specified, but its value is not a list') + log.warning( + "A property_type dict was specified, but its value is not a list" + ) else: property_types.append(getattr(vim, prop_type)) else: property_types = [vim.VirtualMachine] - log.debug('vmware_pillar -- property_types = %s', property_types) + log.debug("vmware_pillar -- property_types = %s", property_types) - if 'property_name' in kwargs: - property_name = kwargs['property_name'] + if "property_name" in kwargs: + property_name = kwargs["property_name"] else: - property_name = 'name' - log.debug('vmware_pillar -- property_name = %s', property_name) + property_name = "name" + log.debug("vmware_pillar -- property_name = %s", property_name) - if 'protocol' in kwargs: - protocol = kwargs['protocol'] - log.debug('vmware_pillar -- protocol = %s', protocol) + if "protocol" in kwargs: + protocol = kwargs["protocol"] + log.debug("vmware_pillar -- protocol = %s", protocol) - if 'port' in kwargs: - port = kwargs['port'] - log.debug('vmware_pillar -- port = %s', port) + if "port" in kwargs: + port = kwargs["port"] + log.debug("vmware_pillar -- port = %s", port) virtualgrain = None osgrain = None - if 'virtual' in __grains__: - virtualgrain = __grains__['virtual'].lower() - if 'os' in __grains__: - osgrain = __grains__['os'].lower() + if "virtual" in __grains__: + virtualgrain = __grains__["virtual"].lower() + if "os" in __grains__: + osgrain = __grains__["os"].lower() - if virtualgrain == 'vmware' or osgrain == 'vmware esxi' or osgrain == 'esxi': + if virtualgrain == "vmware" or osgrain == "vmware esxi" or osgrain == "esxi": vmware_pillar[pillar_key] = {} try: - _conn = salt.utils.vmware.get_service_instance(host, - username, - password, - protocol, - port) + _conn = salt.utils.vmware.get_service_instance( + host, username, password, protocol, port + ) if _conn: data = None for prop_type in property_types: - data = salt.utils.vmware.get_mor_by_property(_conn, - prop_type, - minion_id, - property_name=property_name) + data = salt.utils.vmware.get_mor_by_property( + _conn, prop_type, minion_id, property_name=property_name + ) if data: - type_name = type(data).__name__.replace('vim.', '') - if hasattr(data, 'availableField'): - vmware_pillar[pillar_key]['annotations'] = {} + type_name = type(data).__name__.replace("vim.", "") + if hasattr(data, "availableField"): + vmware_pillar[pillar_key]["annotations"] = {} for availableField in data.availableField: for customValue in data.customValue: if availableField.key == customValue.key: - vmware_pillar[pillar_key]['annotations'][availableField.name] = customValue.value + vmware_pillar[pillar_key]["annotations"][ + availableField.name + ] = customValue.value type_specific_pillar_attribute = [] if type_name in type_specific_pillar_attributes: - type_specific_pillar_attribute = type_specific_pillar_attributes[type_name] - vmware_pillar[pillar_key] = dictupdate.update(vmware_pillar[pillar_key], - _crawl_attribute(data, - pillar_attributes + - type_specific_pillar_attribute)) + type_specific_pillar_attribute = type_specific_pillar_attributes[ + type_name + ] + vmware_pillar[pillar_key] = dictupdate.update( + vmware_pillar[pillar_key], + _crawl_attribute( + data, pillar_attributes + type_specific_pillar_attribute + ), + ) break # explicitly disconnect from vCenter when we are done, connections linger idle otherwise Disconnect(_conn) else: log.error( - 'Unable to obtain a connection with %s, please verify ' - 'your vmware ext_pillar configuration', host + "Unable to obtain a connection with %s, please verify " + "your vmware ext_pillar configuration", + host, ) 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.')) + 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." + ) + ) return vmware_pillar else: @@ -473,9 +423,9 @@ def ext_pillar(minion_id, def _recurse_config_to_dict(t_data): - ''' + """ helper function to recurse through a vim object and attempt to return all child objects - ''' + """ if not isinstance(t_data, type(None)): if isinstance(t_data, list): t_list = [] @@ -488,16 +438,16 @@ def _recurse_config_to_dict(t_data): t_dict[k] = _recurse_config_to_dict(v) return t_dict else: - if hasattr(t_data, '__dict__'): + if hasattr(t_data, "__dict__"): return _recurse_config_to_dict(t_data.__dict__) else: return _serializer(t_data) def _crawl_attribute(this_data, this_attr): - ''' + """ helper function to crawl an attribute specified for retrieval - ''' + """ if isinstance(this_data, list): t_list = [] for d in this_data: @@ -508,7 +458,9 @@ def _crawl_attribute(this_data, this_attr): t_dict = {} for k in this_attr: if hasattr(this_data, k): - t_dict[k] = _crawl_attribute(getattr(this_data, k, None), this_attr[k]) + t_dict[k] = _crawl_attribute( + getattr(this_data, k, None), this_attr[k] + ) return t_dict elif isinstance(this_attr, list): this_dict = {} @@ -516,14 +468,17 @@ def _crawl_attribute(this_data, this_attr): this_dict = dictupdate.update(this_dict, _crawl_attribute(this_data, l)) return this_dict else: - return {this_attr: _recurse_config_to_dict(getattr(this_data, this_attr, None))} + return { + this_attr: _recurse_config_to_dict(getattr(this_data, this_attr, None)) + } def _serializer(obj): - ''' + """ helper function to serialize some objects for prettier return - ''' + """ import datetime + if isinstance(obj, datetime.datetime): if obj.utcoffset() is not None: obj = obj - obj.utcoffset() diff --git a/salt/platform/win.py b/salt/platform/win.py index a6d20426dc2..d65e46b12bc 100644 --- a/salt/platform/win.py +++ b/salt/platform/win.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Windows specific utility functions, this module should be imported in a try, except block because it is only applicable on Windows platforms. @@ -8,34 +8,32 @@ Much of what is here was adapted from the following: https://stackoverflow.com/a/43233332 http://stackoverflow.com/questions/29566330 -''' +""" from __future__ import absolute_import, unicode_literals + import collections +import ctypes import logging import os -import psutil - -import ctypes from ctypes import wintypes -from salt.ext.six.moves import range -from salt.ext.six.moves import zip - -import win32con +import ntsecuritycon +import psutil import win32api +import win32con import win32process import win32security import win32service -import ntsecuritycon +from salt.ext.six.moves import range, zip # Set up logging log = logging.getLogger(__name__) -ntdll = ctypes.WinDLL('ntdll') -secur32 = ctypes.WinDLL('secur32') -kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) -advapi32 = ctypes.WinDLL('advapi32', use_last_error=True) -userenv = ctypes.WinDLL('userenv', use_last_error=True) +ntdll = ctypes.WinDLL("ntdll") +secur32 = ctypes.WinDLL("secur32") +kernel32 = ctypes.WinDLL("kernel32", use_last_error=True) +advapi32 = ctypes.WinDLL("advapi32", use_last_error=True) +userenv = ctypes.WinDLL("userenv", use_last_error=True) SYSTEM_SID = "S-1-5-18" LOCAL_SRV_SID = "S-1-5-19" @@ -44,35 +42,35 @@ NETWORK_SRV_SID = "S-1-5-19" LOGON_WITH_PROFILE = 0x00000001 WINSTA_ALL = ( - win32con.WINSTA_ACCESSCLIPBOARD | - win32con.WINSTA_ACCESSGLOBALATOMS | - win32con.WINSTA_CREATEDESKTOP | - win32con.WINSTA_ENUMDESKTOPS | - win32con.WINSTA_ENUMERATE | - win32con.WINSTA_EXITWINDOWS | - win32con.WINSTA_READATTRIBUTES | - win32con.WINSTA_READSCREEN | - win32con.WINSTA_WRITEATTRIBUTES | - win32con.DELETE | - win32con.READ_CONTROL | - win32con.WRITE_DAC | - win32con.WRITE_OWNER + win32con.WINSTA_ACCESSCLIPBOARD + | win32con.WINSTA_ACCESSGLOBALATOMS + | win32con.WINSTA_CREATEDESKTOP + | win32con.WINSTA_ENUMDESKTOPS + | win32con.WINSTA_ENUMERATE + | win32con.WINSTA_EXITWINDOWS + | win32con.WINSTA_READATTRIBUTES + | win32con.WINSTA_READSCREEN + | win32con.WINSTA_WRITEATTRIBUTES + | win32con.DELETE + | win32con.READ_CONTROL + | win32con.WRITE_DAC + | win32con.WRITE_OWNER ) DESKTOP_ALL = ( - win32con.DESKTOP_CREATEMENU | - win32con.DESKTOP_CREATEWINDOW | - win32con.DESKTOP_ENUMERATE | - win32con.DESKTOP_HOOKCONTROL | - win32con.DESKTOP_JOURNALPLAYBACK | - win32con.DESKTOP_JOURNALRECORD | - win32con.DESKTOP_READOBJECTS | - win32con.DESKTOP_SWITCHDESKTOP | - win32con.DESKTOP_WRITEOBJECTS | - win32con.DELETE | - win32con.READ_CONTROL | - win32con.WRITE_DAC | - win32con.WRITE_OWNER + win32con.DESKTOP_CREATEMENU + | win32con.DESKTOP_CREATEWINDOW + | win32con.DESKTOP_ENUMERATE + | win32con.DESKTOP_HOOKCONTROL + | win32con.DESKTOP_JOURNALPLAYBACK + | win32con.DESKTOP_JOURNALRECORD + | win32con.DESKTOP_READOBJECTS + | win32con.DESKTOP_SWITCHDESKTOP + | win32con.DESKTOP_WRITEOBJECTS + | win32con.DELETE + | win32con.READ_CONTROL + | win32con.WRITE_DAC + | win32con.WRITE_OWNER ) MAX_COMPUTER_NAME_LENGTH = 15 @@ -104,21 +102,16 @@ KERB_S4U_LOGON_FLAG_IDENTITY = 0x8 TOKEN_SOURCE_LENGTH = 8 -NEGOTIATE_PACKAGE_NAME = b'Negotiate' -MICROSOFT_KERBEROS_NAME = b'Kerberos' -MSV1_0_PACKAGE_NAME = b'MICROSOFT_AUTHENTICATION_PACKAGE_V1_0' +NEGOTIATE_PACKAGE_NAME = b"Negotiate" +MICROSOFT_KERBEROS_NAME = b"Kerberos" +MSV1_0_PACKAGE_NAME = b"MICROSOFT_AUTHENTICATION_PACKAGE_V1_0" DELETE = 0x00010000 READ_CONTROL = 0x00020000 WRITE_DAC = 0x00040000 WRITE_OWNER = 0x00080000 -STANDARD_RIGHTS_REQUIRED = ( - DELETE | - READ_CONTROL | - WRITE_DAC | - WRITE_OWNER -) +STANDARD_RIGHTS_REQUIRED = DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER TOKEN_ASSIGN_PRIMARY = 0x0001 TOKEN_DUPLICATE = 0x0002 @@ -131,16 +124,16 @@ TOKEN_ADJUST_DEFAULT = 0x0080 TOKEN_ADJUST_SESSIONID = 0x0100 TOKEN_ALL_ACCESS = ( - STANDARD_RIGHTS_REQUIRED | - TOKEN_ASSIGN_PRIMARY | - TOKEN_DUPLICATE | - TOKEN_IMPERSONATE | - TOKEN_QUERY | - TOKEN_QUERY_SOURCE | - TOKEN_ADJUST_PRIVILEGES | - TOKEN_ADJUST_GROUPS | - TOKEN_ADJUST_DEFAULT | - TOKEN_ADJUST_SESSIONID + STANDARD_RIGHTS_REQUIRED + | TOKEN_ASSIGN_PRIMARY + | TOKEN_DUPLICATE + | TOKEN_IMPERSONATE + | TOKEN_QUERY + | TOKEN_QUERY_SOURCE + | TOKEN_ADJUST_PRIVILEGES + | TOKEN_ADJUST_GROUPS + | TOKEN_ADJUST_DEFAULT + | TOKEN_ADJUST_SESSIONID ) DUPLICATE_CLOSE_SOURCE = 0x00000001 @@ -158,14 +151,13 @@ SecurityDelegation = 3 class NTSTATUS(wintypes.LONG): - def to_error(self): return ntdll.RtlNtStatusToDosError(self) def __repr__(self): name = self.__class__.__name__ status = wintypes.ULONG.from_buffer(self) - return '{}({})'.format(name, status.value) + return "{}({})".format(name, status.value) PNTSTATUS = ctypes.POINTER(NTSTATUS) @@ -174,17 +166,17 @@ PNTSTATUS = ctypes.POINTER(NTSTATUS) class BOOL(wintypes.BOOL): def __repr__(self): name = self.__class__.__name__ - return '{}({})'.format(name, bool(self)) + return "{}({})".format(name, bool(self)) class HANDLE(wintypes.HANDLE): - __slots__ = 'closed', + __slots__ = ("closed",) def __int__(self): return self.value or 0 def Detach(self): - if not getattr(self, 'closed', False): + if not getattr(self, "closed", False): self.closed = True value = int(self) self.value = None @@ -192,7 +184,7 @@ class HANDLE(wintypes.HANDLE): raise ValueError("already closed") def Close(self, CloseHandle=kernel32.CloseHandle): - if self and not getattr(self, 'closed', False): + if self and not getattr(self, "closed", False): CloseHandle(self.Detach()) __del__ = Close @@ -213,17 +205,17 @@ class LARGE_INTEGER(wintypes.LARGE_INTEGER): def __repr__(self): name = self.__class__.__name__ - return '{}({})'.format(name, self.value) + return "{}({})".format(name, self.value) def as_time(self): time100ns = self.value - self._unix_epoch if time100ns >= 0: return time100ns / 1e7 - raise ValueError('value predates the Unix epoch') + raise ValueError("value predates the Unix epoch") @classmethod def from_time(cls, t): - time100ns = int(t * 10**7) + time100ns = int(t * 10 ** 7) return cls(time100ns + cls._unix_epoch) @@ -235,9 +227,9 @@ PWCHAR = ctypes.POINTER(WCHAR) class STRING(ctypes.Structure): _fields_ = ( - ('Length', wintypes.USHORT), - ('MaximumLength', wintypes.USHORT), - ('Buffer', PCHAR), + ("Length", wintypes.USHORT), + ("MaximumLength", wintypes.USHORT), + ("Buffer", PCHAR), ) @@ -246,9 +238,9 @@ LPSTRING = ctypes.POINTER(STRING) class UNICODE_STRING(ctypes.Structure): _fields_ = ( - ('Length', wintypes.USHORT), - ('MaximumLength', wintypes.USHORT), - ('Buffer', PWCHAR), + ("Length", wintypes.USHORT), + ("MaximumLength", wintypes.USHORT), + ("Buffer", PWCHAR), ) @@ -257,8 +249,8 @@ LPUNICODE_STRING = ctypes.POINTER(UNICODE_STRING) class LUID(ctypes.Structure): _fields_ = ( - ('LowPart', wintypes.DWORD), - ('HighPart', wintypes.LONG), + ("LowPart", wintypes.DWORD), + ("HighPart", wintypes.LONG), ) def __new__(cls, value=0): @@ -269,7 +261,7 @@ class LUID(ctypes.Structure): def __repr__(self): name = self.__class__.__name__ - return '{}({})'.format(name, int(self)) + return "{}({})".format(name, int(self)) LPLUID = ctypes.POINTER(LUID) @@ -278,8 +270,8 @@ PSID = wintypes.LPVOID class SID_AND_ATTRIBUTES(ctypes.Structure): _fields_ = ( - ('Sid', PSID), - ('Attributes', wintypes.DWORD), + ("Sid", PSID), + ("Attributes", wintypes.DWORD), ) @@ -288,8 +280,8 @@ LPSID_AND_ATTRIBUTES = ctypes.POINTER(SID_AND_ATTRIBUTES) class TOKEN_GROUPS(ctypes.Structure): _fields_ = ( - ('GroupCount', wintypes.DWORD), - ('Groups', SID_AND_ATTRIBUTES * 1), + ("GroupCount", wintypes.DWORD), + ("Groups", SID_AND_ATTRIBUTES * 1), ) @@ -298,18 +290,20 @@ LPTOKEN_GROUPS = ctypes.POINTER(TOKEN_GROUPS) class TOKEN_SOURCE(ctypes.Structure): _fields_ = ( - ('SourceName', CHAR * TOKEN_SOURCE_LENGTH), - ('SourceIdentifier', LUID), + ("SourceName", CHAR * TOKEN_SOURCE_LENGTH), + ("SourceIdentifier", LUID), ) def __init__(self, SourceName=None, SourceIdentifier=None): super(TOKEN_SOURCE, self).__init__() if SourceName is not None: if not isinstance(SourceName, bytes): - SourceName = SourceName.encode('mbcs') + SourceName = SourceName.encode("mbcs") self.SourceName = SourceName if SourceIdentifier is None: - luid = self.SourceIdentifier # pylint: disable=access-member-before-definition + # pylint: disable=access-member-before-definition + luid = self.SourceIdentifier + # pylint: enable=access-member-before-definition ntdll.NtAllocateLocallyUniqueId(ctypes.byref(luid)) else: self.SourceIdentifier = SourceIdentifier @@ -323,12 +317,14 @@ SIZE_T = ctypes.c_size_t class QUOTA_LIMITS(ctypes.Structure): - _fields_ = (('PagedPoolLimit', SIZE_T), - ('NonPagedPoolLimit', SIZE_T), - ('MinimumWorkingSetSize', SIZE_T), - ('MaximumWorkingSetSize', SIZE_T), - ('PagefileLimit', SIZE_T), - ('TimeLimit', wintypes.LARGE_INTEGER)) + _fields_ = ( + ("PagedPoolLimit", SIZE_T), + ("NonPagedPoolLimit", SIZE_T), + ("MinimumWorkingSetSize", SIZE_T), + ("MaximumWorkingSetSize", SIZE_T), + ("PagefileLimit", SIZE_T), + ("TimeLimit", wintypes.LARGE_INTEGER), + ) LPQUOTA_LIMITS = ctypes.POINTER(QUOTA_LIMITS) @@ -347,7 +343,7 @@ class ContiguousUnicode(ctypes.Structure): def _get_unicode_string(self, name): wchar_size = ctypes.sizeof(WCHAR) - s = getattr(self, '_{}'.format(name)) + s = getattr(self, "_{}".format(name)) length = s.Length // wchar_size buf = s.Buffer if buf: @@ -357,7 +353,7 @@ class ContiguousUnicode(ctypes.Structure): def _set_unicode_buffer(self, values): cls = type(self) wchar_size = ctypes.sizeof(WCHAR) - bufsize = (len('\x00'.join(values)) + 1) * wchar_size + bufsize = (len("\x00".join(values)) + 1) * wchar_size ctypes.resize(self, ctypes.sizeof(cls) + bufsize) addr = ctypes.addressof(self) + ctypes.sizeof(cls) for value in values: @@ -369,9 +365,9 @@ class ContiguousUnicode(ctypes.Structure): values = [] for n in self._string_names_: if n == name: - values.append(value or '') + values.append(value or "") else: - values.append(getattr(self, n) or '') + values.append(getattr(self, n) or "") self._set_unicode_buffer(values) cls = type(self) @@ -379,11 +375,12 @@ class ContiguousUnicode(ctypes.Structure): addr = ctypes.addressof(self) + ctypes.sizeof(cls) for n, v in zip(self._string_names_, values): ptr = ctypes.cast(addr, PWCHAR) - ustr = getattr(self, '_{}'.format(n)) + ustr = getattr(self, "_{}".format(n)) length = ustr.Length = len(v) * wchar_size full_length = length + wchar_size - if ((n == name and value is None) or - (n != name and not (length or ustr.Buffer))): + if (n == name and value is None) or ( + n != name and not (length or ustr.Buffer) + ): ustr.Buffer = None ustr.MaximumLength = 0 else: @@ -410,7 +407,7 @@ class ContiguousUnicode(ctypes.Structure): ctypes.memmove(ctypes.byref(x), address, ctypes.sizeof(x)) delta = ctypes.addressof(x) - address for n in cls._string_names_: - ustr = getattr(x, '_{}'.format(n)) + ustr = getattr(x, "_{}".format(n)) addr = ctypes.c_void_p.from_buffer(ustr.Buffer) if addr: addr.value += delta @@ -426,12 +423,14 @@ class AuthInfo(ContiguousUnicode): class MSV1_0_INTERACTIVE_LOGON(AuthInfo): _message_type_ = MsV1_0InteractiveLogon - _string_names_ = 'LogonDomainName', 'UserName', 'Password' + _string_names_ = "LogonDomainName", "UserName", "Password" - _fields_ = (('MessageType', LOGON_SUBMIT_TYPE), - ('_LogonDomainName', UNICODE_STRING), - ('_UserName', UNICODE_STRING), - ('_Password', UNICODE_STRING)) + _fields_ = ( + ("MessageType", LOGON_SUBMIT_TYPE), + ("_LogonDomainName", UNICODE_STRING), + ("_UserName", UNICODE_STRING), + ("_Password", UNICODE_STRING), + ) def __init__(self, UserName=None, Password=None, LogonDomainName=None): super(MSV1_0_INTERACTIVE_LOGON, self).__init__() @@ -444,12 +443,14 @@ class MSV1_0_INTERACTIVE_LOGON(AuthInfo): class S4ULogon(AuthInfo): - _string_names_ = 'UserPrincipalName', 'DomainName' + _string_names_ = "UserPrincipalName", "DomainName" - _fields_ = (('MessageType', LOGON_SUBMIT_TYPE), - ('Flags', wintypes.ULONG), - ('_UserPrincipalName', UNICODE_STRING), - ('_DomainName', UNICODE_STRING)) + _fields_ = ( + ("MessageType", LOGON_SUBMIT_TYPE), + ("Flags", wintypes.ULONG), + ("_UserPrincipalName", UNICODE_STRING), + ("_DomainName", UNICODE_STRING), + ) def __init__(self, UserPrincipalName=None, DomainName=None, Flags=0): super(S4ULogon, self).__init__() @@ -481,24 +482,32 @@ class ProfileBuffer(ContiguousUnicode): class MSV1_0_INTERACTIVE_PROFILE(ProfileBuffer): _message_type_ = MsV1_0InteractiveLogon - _string_names_ = ('LogonScript', 'HomeDirectory', 'FullName', - 'ProfilePath', 'HomeDirectoryDrive', 'LogonServer') - _fields_ = (('MessageType', PROFILE_BUFFER_TYPE), - ('LogonCount', wintypes.USHORT), - ('BadPasswordCount', wintypes.USHORT), - ('LogonTime', LARGE_INTEGER), - ('LogoffTime', LARGE_INTEGER), - ('KickOffTime', LARGE_INTEGER), - ('PasswordLastSet', LARGE_INTEGER), - ('PasswordCanChange', LARGE_INTEGER), - ('PasswordMustChange', LARGE_INTEGER), - ('_LogonScript', UNICODE_STRING), - ('_HomeDirectory', UNICODE_STRING), - ('_FullName', UNICODE_STRING), - ('_ProfilePath', UNICODE_STRING), - ('_HomeDirectoryDrive', UNICODE_STRING), - ('_LogonServer', UNICODE_STRING), - ('UserFlags', wintypes.ULONG)) + _string_names_ = ( + "LogonScript", + "HomeDirectory", + "FullName", + "ProfilePath", + "HomeDirectoryDrive", + "LogonServer", + ) + _fields_ = ( + ("MessageType", PROFILE_BUFFER_TYPE), + ("LogonCount", wintypes.USHORT), + ("BadPasswordCount", wintypes.USHORT), + ("LogonTime", LARGE_INTEGER), + ("LogoffTime", LARGE_INTEGER), + ("KickOffTime", LARGE_INTEGER), + ("PasswordLastSet", LARGE_INTEGER), + ("PasswordCanChange", LARGE_INTEGER), + ("PasswordMustChange", LARGE_INTEGER), + ("_LogonScript", UNICODE_STRING), + ("_HomeDirectory", UNICODE_STRING), + ("_FullName", UNICODE_STRING), + ("_ProfilePath", UNICODE_STRING), + ("_HomeDirectoryDrive", UNICODE_STRING), + ("_LogonServer", UNICODE_STRING), + ("UserFlags", wintypes.ULONG), + ) def _check_status(result, func, args): @@ -522,9 +531,11 @@ STD_ERROR_HANDLE = wintypes.DWORD(-12).value class SECURITY_ATTRIBUTES(ctypes.Structure): - _fields_ = (('nLength', wintypes.DWORD), - ('lpSecurityDescriptor', wintypes.LPVOID), - ('bInheritHandle', wintypes.BOOL)) + _fields_ = ( + ("nLength", wintypes.DWORD), + ("lpSecurityDescriptor", wintypes.LPVOID), + ("bInheritHandle", wintypes.BOOL), + ) def __init__(self, **kwds): self.nLength = ctypes.sizeof(self) @@ -539,24 +550,27 @@ LPDWORD = ctypes.POINTER(ctypes.c_ulong) class STARTUPINFO(ctypes.Structure): """https://msdn.microsoft.com/en-us/library/ms686331""" - _fields_ = (('cb', wintypes.DWORD), - ('lpReserved', wintypes.LPWSTR), - ('lpDesktop', wintypes.LPWSTR), - ('lpTitle', wintypes.LPWSTR), - ('dwX', wintypes.DWORD), - ('dwY', wintypes.DWORD), - ('dwXSize', wintypes.DWORD), - ('dwYSize', wintypes.DWORD), - ('dwXCountChars', wintypes.DWORD), - ('dwYCountChars', wintypes.DWORD), - ('dwFillAttribute', wintypes.DWORD), - ('dwFlags', wintypes.DWORD), - ('wShowWindow', wintypes.WORD), - ('cbReserved2', wintypes.WORD), - ('lpReserved2', LPBYTE), - ('hStdInput', wintypes.HANDLE), - ('hStdOutput', wintypes.HANDLE), - ('hStdError', wintypes.HANDLE)) + + _fields_ = ( + ("cb", wintypes.DWORD), + ("lpReserved", wintypes.LPWSTR), + ("lpDesktop", wintypes.LPWSTR), + ("lpTitle", wintypes.LPWSTR), + ("dwX", wintypes.DWORD), + ("dwY", wintypes.DWORD), + ("dwXSize", wintypes.DWORD), + ("dwYSize", wintypes.DWORD), + ("dwXCountChars", wintypes.DWORD), + ("dwYCountChars", wintypes.DWORD), + ("dwFillAttribute", wintypes.DWORD), + ("dwFlags", wintypes.DWORD), + ("wShowWindow", wintypes.WORD), + ("cbReserved2", wintypes.WORD), + ("lpReserved2", LPBYTE), + ("hStdInput", wintypes.HANDLE), + ("hStdOutput", wintypes.HANDLE), + ("hStdError", wintypes.HANDLE), + ) def __init__(self, **kwds): self.cb = ctypes.sizeof(self) @@ -574,7 +588,7 @@ PPROC_THREAD_ATTRIBUTE_LIST = ctypes.POINTER(PROC_THREAD_ATTRIBUTE_LIST) class STARTUPINFOEX(STARTUPINFO): - _fields_ = (('lpAttributeList', PPROC_THREAD_ATTRIBUTE_LIST),) + _fields_ = (("lpAttributeList", PPROC_THREAD_ATTRIBUTE_LIST),) LPSTARTUPINFOEX = ctypes.POINTER(STARTUPINFOEX) @@ -582,10 +596,13 @@ LPSTARTUPINFOEX = ctypes.POINTER(STARTUPINFOEX) class PROCESS_INFORMATION(ctypes.Structure): """https://msdn.microsoft.com/en-us/library/ms684873""" - _fields_ = (('hProcess', wintypes.HANDLE), - ('hThread', wintypes.HANDLE), - ('dwProcessId', wintypes.DWORD), - ('dwThreadId', wintypes.DWORD)) + + _fields_ = ( + ("hProcess", wintypes.HANDLE), + ("hThread", wintypes.HANDLE), + ("dwProcessId", wintypes.DWORD), + ("dwThreadId", wintypes.DWORD), + ) LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION) @@ -633,31 +650,35 @@ def _win(func, restype, *argtypes): # https://msdn.microsoft.com/en-us/library/ms683231 -_win(kernel32.GetStdHandle, HANDLE_IHV, - wintypes.DWORD) # _In_ nStdHandle +_win(kernel32.GetStdHandle, HANDLE_IHV, wintypes.DWORD) # _In_ nStdHandle # https://msdn.microsoft.com/en-us/library/ms724211 -_win(kernel32.CloseHandle, wintypes.BOOL, - wintypes.HANDLE) # _In_ hObject +_win(kernel32.CloseHandle, wintypes.BOOL, wintypes.HANDLE) # _In_ hObject # https://msdn.microsoft.com/en-us/library/ms724935 -_win(kernel32.SetHandleInformation, wintypes.BOOL, +_win( + kernel32.SetHandleInformation, + wintypes.BOOL, wintypes.HANDLE, # _In_ hObject - wintypes.DWORD, # _In_ dwMask - wintypes.DWORD) # _In_ dwFlags + wintypes.DWORD, # _In_ dwMask + wintypes.DWORD, +) # _In_ dwFlags # https://msdn.microsoft.com/en-us/library/ms724251 -_win(kernel32.DuplicateHandle, wintypes.BOOL, +_win( + kernel32.DuplicateHandle, + wintypes.BOOL, wintypes.HANDLE, # _In_ hSourceProcessHandle, wintypes.HANDLE, # _In_ hSourceHandle, wintypes.HANDLE, # _In_ hTargetProcessHandle, - LPHANDLE, # _Out_ lpTargetHandle, - wintypes.DWORD, # _In_ dwDesiredAccess, - wintypes.BOOL, # _In_ bInheritHandle, - wintypes.DWORD) # _In_ dwOptions + LPHANDLE, # _Out_ lpTargetHandle, + wintypes.DWORD, # _In_ dwDesiredAccess, + wintypes.BOOL, # _In_ bInheritHandle, + wintypes.DWORD, +) # _In_ dwOptions # https://msdn.microsoft.com/en-us/library/ms683179 @@ -665,21 +686,27 @@ _win(kernel32.GetCurrentProcess, wintypes.HANDLE) # https://msdn.microsoft.com/en-us/library/ms683189 -_win(kernel32.GetExitCodeProcess, wintypes.BOOL, +_win( + kernel32.GetExitCodeProcess, + wintypes.BOOL, wintypes.HANDLE, # _In_ hProcess, - LPDWORD) # _Out_ lpExitCode + LPDWORD, +) # _Out_ lpExitCode # https://msdn.microsoft.com/en-us/library/aa365152 -_win(kernel32.CreatePipe, wintypes.BOOL, - PHANDLE, # _Out_ hReadPipe, - PHANDLE, # _Out_ hWritePipe, +_win( + kernel32.CreatePipe, + wintypes.BOOL, + PHANDLE, # _Out_ hReadPipe, + PHANDLE, # _Out_ hWritePipe, LPSECURITY_ATTRIBUTES, # _In_opt_ lpPipeAttributes, - wintypes.DWORD) # _In_ nSize + wintypes.DWORD, +) # _In_ nSize # https://msdn.microsoft.com/en-us/library/ms682431 -#_win(advapi32.CreateProcessWithTokenW, wintypes.BOOL, +# _win(advapi32.CreateProcessWithTokenW, wintypes.BOOL, # PHANDLE, # _In_ lpUsername # wintypes.DWORD, # _In_ dwLogonFlags # wintypes.LPCWSTR, # _In_opt_ lpApplicationName @@ -692,18 +719,21 @@ _win(kernel32.CreatePipe, wintypes.BOOL, # https://msdn.microsoft.com/en-us/library/ms682431 -_win(advapi32.CreateProcessWithLogonW, wintypes.BOOL, - wintypes.LPCWSTR, # _In_ lpUsername - wintypes.LPCWSTR, # _In_opt_ lpDomain - wintypes.LPCWSTR, # _In_ lpPassword - wintypes.DWORD, # _In_ dwLogonFlags - wintypes.LPCWSTR, # _In_opt_ lpApplicationName - wintypes.LPWSTR, # _Inout_opt_ lpCommandLine - wintypes.DWORD, # _In_ dwCreationFlags - wintypes.LPCWSTR, # _In_opt_ lpEnvironment - wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory - LPSTARTUPINFO, # _In_ lpStartupInfo - LPPROCESS_INFORMATION) # _Out_ lpProcessInformation +_win( + advapi32.CreateProcessWithLogonW, + wintypes.BOOL, + wintypes.LPCWSTR, # _In_ lpUsername + wintypes.LPCWSTR, # _In_opt_ lpDomain + wintypes.LPCWSTR, # _In_ lpPassword + wintypes.DWORD, # _In_ dwLogonFlags + wintypes.LPCWSTR, # _In_opt_ lpApplicationName + wintypes.LPWSTR, # _Inout_opt_ lpCommandLine + wintypes.DWORD, # _In_ dwCreationFlags + wintypes.LPCWSTR, # _In_opt_ lpEnvironment + wintypes.LPCWSTR, # _In_opt_ lpCurrentDirectory + LPSTARTUPINFO, # _In_ lpStartupInfo + LPPROCESS_INFORMATION, +) # _Out_ lpProcessInformation # https://msdn.microsoft.com/en-us/library/ms683179 @@ -711,104 +741,133 @@ _win(kernel32.GetCurrentProcess, wintypes.HANDLE) # https://msdn.microsoft.com/en-us/library/ms724251 -_win(kernel32.DuplicateHandle, BOOL, +_win( + kernel32.DuplicateHandle, + BOOL, wintypes.HANDLE, # _In_ hSourceProcessHandle wintypes.HANDLE, # _In_ hSourceHandle wintypes.HANDLE, # _In_ hTargetProcessHandle - LPHANDLE, # _Out_ lpTargetHandle - wintypes.DWORD, # _In_ dwDesiredAccess - wintypes.BOOL, # _In_ bInheritHandle - wintypes.DWORD) # _In_ dwOptions + LPHANDLE, # _Out_ lpTargetHandle + wintypes.DWORD, # _In_ dwDesiredAccess + wintypes.BOOL, # _In_ bInheritHandle + wintypes.DWORD, +) # _In_ dwOptions # https://msdn.microsoft.com/en-us/library/ms724295 -_win(kernel32.GetComputerNameW, BOOL, - wintypes.LPWSTR, # _Out_ lpBuffer - LPDWORD) # _Inout_ lpnSize +_win( + kernel32.GetComputerNameW, BOOL, wintypes.LPWSTR, LPDWORD # _Out_ lpBuffer +) # _Inout_ lpnSize # https://msdn.microsoft.com/en-us/library/aa379295 -_win(advapi32.OpenProcessToken, BOOL, +_win( + advapi32.OpenProcessToken, + BOOL, wintypes.HANDLE, # _In_ ProcessHandle - wintypes.DWORD, # _In_ DesiredAccess - LPHANDLE) # _Out_ TokenHandle + wintypes.DWORD, # _In_ DesiredAccess + LPHANDLE, +) # _Out_ TokenHandle # https://msdn.microsoft.com/en-us/library/aa446617 -_win(advapi32.DuplicateTokenEx, BOOL, - wintypes.HANDLE, # _In_ hExistingToken - wintypes.DWORD, # _In_ dwDesiredAccess - LPSECURITY_ATTRIBUTES, # _In_opt_ lpTokenAttributes +_win( + advapi32.DuplicateTokenEx, + BOOL, + wintypes.HANDLE, # _In_ hExistingToken + wintypes.DWORD, # _In_ dwDesiredAccess + LPSECURITY_ATTRIBUTES, # _In_opt_ lpTokenAttributes SECURITY_IMPERSONATION_LEVEL, # _In_ ImpersonationLevel - TOKEN_TYPE, # _In_ TokenType - LPHANDLE) # _Out_ phNewToken + TOKEN_TYPE, # _In_ TokenType + LPHANDLE, +) # _Out_ phNewToken # https://msdn.microsoft.com/en-us/library/ff566415 -_win(ntdll.NtAllocateLocallyUniqueId, NTSTATUS, - LPLUID) # _Out_ LUID +_win(ntdll.NtAllocateLocallyUniqueId, NTSTATUS, LPLUID) # _Out_ LUID # https://msdn.microsoft.com/en-us/library/aa378279 -_win(secur32.LsaFreeReturnBuffer, NTSTATUS, - wintypes.LPVOID,) # _In_ Buffer +_win( + secur32.LsaFreeReturnBuffer, NTSTATUS, wintypes.LPVOID, +) # _In_ Buffer # https://msdn.microsoft.com/en-us/library/aa378265 -_win(secur32.LsaConnectUntrusted, NTSTATUS, - LPHANDLE,) # _Out_ LsaHandle +_win( + secur32.LsaConnectUntrusted, NTSTATUS, LPHANDLE, +) # _Out_ LsaHandle -#https://msdn.microsoft.com/en-us/library/aa378318 -_win(secur32.LsaRegisterLogonProcess, NTSTATUS, - LPSTRING, # _In_ LogonProcessName - LPHANDLE, # _Out_ LsaHandle - LPLSA_OPERATIONAL_MODE) # _Out_ SecurityMode +# https://msdn.microsoft.com/en-us/library/aa378318 +_win( + secur32.LsaRegisterLogonProcess, + NTSTATUS, + LPSTRING, # _In_ LogonProcessName + LPHANDLE, # _Out_ LsaHandle + LPLSA_OPERATIONAL_MODE, +) # _Out_ SecurityMode # https://msdn.microsoft.com/en-us/library/aa378269 -_win(secur32.LsaDeregisterLogonProcess, NTSTATUS, - wintypes.HANDLE) # _In_ LsaHandle +_win(secur32.LsaDeregisterLogonProcess, NTSTATUS, wintypes.HANDLE) # _In_ LsaHandle # https://msdn.microsoft.com/en-us/library/aa378297 -_win(secur32.LsaLookupAuthenticationPackage, NTSTATUS, +_win( + secur32.LsaLookupAuthenticationPackage, + NTSTATUS, wintypes.HANDLE, # _In_ LsaHandle - LPSTRING, # _In_ PackageName - LPULONG) # _Out_ AuthenticationPackage + LPSTRING, # _In_ PackageName + LPULONG, +) # _Out_ AuthenticationPackage # https://msdn.microsoft.com/en-us/library/aa378292 -_win(secur32.LsaLogonUser, NTSTATUS, - wintypes.HANDLE, # _In_ LsaHandle - LPSTRING, # _In_ OriginName +_win( + secur32.LsaLogonUser, + NTSTATUS, + wintypes.HANDLE, # _In_ LsaHandle + LPSTRING, # _In_ OriginName SECURITY_LOGON_TYPE, # _In_ LogonType - wintypes.ULONG, # _In_ AuthenticationPackage - wintypes.LPVOID, # _In_ AuthenticationInformation - wintypes.ULONG, # _In_ AuthenticationInformationLength - LPTOKEN_GROUPS, # _In_opt_ LocalGroups - LPTOKEN_SOURCE, # _In_ SourceContext - LPLPVOID, # _Out_ ProfileBuffer - LPULONG, # _Out_ ProfileBufferLength - LPLUID, # _Out_ LogonId - LPHANDLE, # _Out_ Token - LPQUOTA_LIMITS, # _Out_ Quotas - PNTSTATUS) # _Out_ SubStatus + wintypes.ULONG, # _In_ AuthenticationPackage + wintypes.LPVOID, # _In_ AuthenticationInformation + wintypes.ULONG, # _In_ AuthenticationInformationLength + LPTOKEN_GROUPS, # _In_opt_ LocalGroups + LPTOKEN_SOURCE, # _In_ SourceContext + LPLPVOID, # _Out_ ProfileBuffer + LPULONG, # _Out_ ProfileBufferLength + LPLUID, # _Out_ LogonId + LPHANDLE, # _Out_ Token + LPQUOTA_LIMITS, # _Out_ Quotas + PNTSTATUS, +) # _Out_ SubStatus -def duplicate_token(source_token=None, access=TOKEN_ALL_ACCESS, - impersonation_level=SecurityImpersonation, - token_type=TokenPrimary, attributes=None): +def duplicate_token( + source_token=None, + access=TOKEN_ALL_ACCESS, + impersonation_level=SecurityImpersonation, + token_type=TokenPrimary, + attributes=None, +): close_source = False if source_token is None: close_source = True source_token = HANDLE() - advapi32.OpenProcessToken(kernel32.GetCurrentProcess(), - TOKEN_ALL_ACCESS, ctypes.byref(source_token)) + advapi32.OpenProcessToken( + kernel32.GetCurrentProcess(), TOKEN_ALL_ACCESS, ctypes.byref(source_token) + ) token = HANDLE() try: - advapi32.DuplicateTokenEx(source_token, access, attributes, - impersonation_level, token_type, ctypes.byref(token)) + advapi32.DuplicateTokenEx( + source_token, + access, + attributes, + impersonation_level, + token_type, + ctypes.byref(token), + ) finally: if close_source: source_token.Close() @@ -823,36 +882,45 @@ def lsa_connect_untrusted(): def lsa_register_logon_process(logon_process_name): if not isinstance(logon_process_name, bytes): - logon_process_name = logon_process_name.encode('mbcs') + logon_process_name = logon_process_name.encode("mbcs") logon_process_name = logon_process_name[:127] buf = ctypes.create_string_buffer(logon_process_name, 128) name = STRING(len(logon_process_name), len(buf), buf) handle = wintypes.HANDLE() mode = LSA_OPERATIONAL_MODE() - secur32.LsaRegisterLogonProcess(ctypes.byref(name), - ctypes.byref(handle), ctypes.byref(mode)) + secur32.LsaRegisterLogonProcess( + ctypes.byref(name), ctypes.byref(handle), ctypes.byref(mode) + ) return handle.value def lsa_lookup_authentication_package(lsa_handle, package_name): if not isinstance(package_name, bytes): - package_name = package_name.encode('mbcs') + package_name = package_name.encode("mbcs") package_name = package_name[:127] buf = ctypes.create_string_buffer(package_name) name = STRING(len(package_name), len(buf), buf) package = wintypes.ULONG() - secur32.LsaLookupAuthenticationPackage(lsa_handle, ctypes.byref(name), - ctypes.byref(package)) + secur32.LsaLookupAuthenticationPackage( + lsa_handle, ctypes.byref(name), ctypes.byref(package) + ) return package.value -LOGONINFO = collections.namedtuple('LOGONINFO', ('Token', 'LogonId', - 'Profile', 'Quotas')) +LOGONINFO = collections.namedtuple( + "LOGONINFO", ("Token", "LogonId", "Profile", "Quotas") +) -def lsa_logon_user(auth_info, local_groups=None, origin_name=py_origin_name, - source_context=None, auth_package=None, logon_type=None, - lsa_handle=None): +def lsa_logon_user( + auth_info, + local_groups=None, + origin_name=py_origin_name, + source_context=None, + auth_package=None, + logon_type=None, + lsa_handle=None, +): if local_groups is None: plocal_groups = LPTOKEN_GROUPS() else: @@ -860,7 +928,7 @@ def lsa_logon_user(auth_info, local_groups=None, origin_name=py_origin_name, if source_context is None: source_context = py_source_context if not isinstance(origin_name, bytes): - origin_name = origin_name.encode('mbcs') + origin_name = origin_name.encode("mbcs") buf = ctypes.create_string_buffer(origin_name) origin_name = STRING(len(origin_name), len(buf), buf) if auth_package is None: @@ -888,16 +956,24 @@ def lsa_logon_user(auth_info, local_groups=None, origin_name=py_origin_name, deregister = True try: if isinstance(auth_package, (str, bytes)): - auth_package = lsa_lookup_authentication_package(lsa_handle, - auth_package) + auth_package = lsa_lookup_authentication_package(lsa_handle, auth_package) try: - secur32.LsaLogonUser(lsa_handle, ctypes.byref(origin_name), - logon_type, auth_package, ctypes.byref(auth_info), - ctypes.sizeof(auth_info), plocal_groups, - ctypes.byref(source_context), ctypes.byref(profile_buffer), - ctypes.byref(profile_buffer_length), ctypes.byref(logonid), - ctypes.byref(htoken), ctypes.byref(quotas), - ctypes.byref(substatus)) + secur32.LsaLogonUser( + lsa_handle, + ctypes.byref(origin_name), + logon_type, + auth_package, + ctypes.byref(auth_info), + ctypes.sizeof(auth_info), + plocal_groups, + ctypes.byref(source_context), + ctypes.byref(profile_buffer), + ctypes.byref(profile_buffer_length), + ctypes.byref(logonid), + ctypes.byref(htoken), + ctypes.byref(quotas), + ctypes.byref(substatus), + ) except WindowsError: # pylint: disable=undefined-variable if substatus.value: raise ctypes.WinError(substatus.to_error()) @@ -908,7 +984,8 @@ def lsa_logon_user(auth_info, local_groups=None, origin_name=py_origin_name, buftype = PROFILE_BUFFER_TYPE.from_address(address).value if buftype == MsV1_0InteractiveLogon: profile = MSV1_0_INTERACTIVE_PROFILE.from_address_copy( - address, profile_buffer_length.value) + address, profile_buffer_length.value + ) secur32.LsaFreeReturnBuffer(address) finally: if deregister: @@ -916,43 +993,66 @@ def lsa_logon_user(auth_info, local_groups=None, origin_name=py_origin_name, return LOGONINFO(htoken, logonid, profile, quotas) -def logon_msv1(name, password, domain=None, local_groups=None, - origin_name=py_origin_name, source_context=None): - return lsa_logon_user(MSV1_0_INTERACTIVE_LOGON(name, password, domain), - local_groups, origin_name, source_context) +def logon_msv1( + name, + password, + domain=None, + local_groups=None, + origin_name=py_origin_name, + source_context=None, +): + return lsa_logon_user( + MSV1_0_INTERACTIVE_LOGON(name, password, domain), + local_groups, + origin_name, + source_context, + ) -def logon_msv1_s4u(name, local_groups=None, origin_name=py_origin_name, - source_context=None): +def logon_msv1_s4u( + name, local_groups=None, origin_name=py_origin_name, source_context=None +): domain = ctypes.create_unicode_buffer(MAX_COMPUTER_NAME_LENGTH + 1) length = wintypes.DWORD(len(domain)) kernel32.GetComputerNameW(domain, ctypes.byref(length)) - return lsa_logon_user(MSV1_0_S4U_LOGON(name, domain.value), - local_groups, origin_name, source_context) + return lsa_logon_user( + MSV1_0_S4U_LOGON(name, domain.value), local_groups, origin_name, source_context + ) -def logon_kerb_s4u(name, realm=None, local_groups=None, - origin_name=py_origin_name, - source_context=None, - logon_process_name=py_logon_process_name): +def logon_kerb_s4u( + name, + realm=None, + local_groups=None, + origin_name=py_origin_name, + source_context=None, + logon_process_name=py_logon_process_name, +): lsa_handle = lsa_register_logon_process(logon_process_name) try: - return lsa_logon_user(KERB_S4U_LOGON(name, realm), - local_groups, origin_name, source_context, - lsa_handle=lsa_handle) + return lsa_logon_user( + KERB_S4U_LOGON(name, realm), + local_groups, + origin_name, + source_context, + lsa_handle=lsa_handle, + ) finally: secur32.LsaDeregisterLogonProcess(lsa_handle) -def DuplicateHandle(hsrc=kernel32.GetCurrentProcess(), - srchandle=kernel32.GetCurrentProcess(), - htgt=kernel32.GetCurrentProcess(), - access=0, inherit=False, - options=win32con.DUPLICATE_SAME_ACCESS): +def DuplicateHandle( + hsrc=kernel32.GetCurrentProcess(), + srchandle=kernel32.GetCurrentProcess(), + htgt=kernel32.GetCurrentProcess(), + access=0, + inherit=False, + options=win32con.DUPLICATE_SAME_ACCESS, +): tgthandle = wintypes.HANDLE() - kernel32.DuplicateHandle(hsrc, srchandle, - htgt, ctypes.byref(tgthandle), - access, inherit, options) + kernel32.DuplicateHandle( + hsrc, srchandle, htgt, ctypes.byref(tgthandle), access, inherit, options + ) return tgthandle.value @@ -960,22 +1060,24 @@ def CreatePipe(inherit_read=False, inherit_write=False): read, write = wintypes.HANDLE(), wintypes.HANDLE() kernel32.CreatePipe(ctypes.byref(read), ctypes.byref(write), None, 0) if inherit_read: - kernel32.SetHandleInformation(read, win32con.HANDLE_FLAG_INHERIT, - win32con.HANDLE_FLAG_INHERIT) + kernel32.SetHandleInformation( + read, win32con.HANDLE_FLAG_INHERIT, win32con.HANDLE_FLAG_INHERIT + ) if inherit_write: - kernel32.SetHandleInformation(write, win32con.HANDLE_FLAG_INHERIT, - win32con.HANDLE_FLAG_INHERIT) + kernel32.SetHandleInformation( + write, win32con.HANDLE_FLAG_INHERIT, win32con.HANDLE_FLAG_INHERIT + ) return read.value, write.value def set_user_perm(obj, perm, sid): - ''' + """ Set an object permission for the given user sid - ''' + """ info = ( - win32security.OWNER_SECURITY_INFORMATION | - win32security.GROUP_SECURITY_INFORMATION | - win32security.DACL_SECURITY_INFORMATION + win32security.OWNER_SECURITY_INFORMATION + | win32security.GROUP_SECURITY_INFORMATION + | win32security.DACL_SECURITY_INFORMATION ) sd = win32security.GetUserObjectSecurity(obj, info) dacl = sd.GetSecurityDescriptorDacl() @@ -984,9 +1086,9 @@ def set_user_perm(obj, perm, sid): for idx in range(0, ace_cnt): (aceType, aceFlags), ace_mask, ace_sid = dacl.GetAce(idx) ace_exists = ( - aceType == ntsecuritycon.ACCESS_ALLOWED_ACE_TYPE and - ace_mask == perm and - ace_sid == sid + aceType == ntsecuritycon.ACCESS_ALLOWED_ACE_TYPE + and ace_mask == perm + and ace_sid == sid ) if ace_exists: # If the ace already exists, do nothing @@ -998,10 +1100,10 @@ def set_user_perm(obj, perm, sid): def grant_winsta_and_desktop(th): - ''' + """ Grant the token's user access to the current process's window station and desktop. - ''' + """ current_sid = win32security.GetTokenInformation(th, win32security.TokenUser)[0] # Add permissions for the sid to the current windows station and thread id. # This prevents windows error 0xC0000142. @@ -1012,21 +1114,23 @@ def grant_winsta_and_desktop(th): def environment_string(env): - senv = '' + senv = "" for k, v in env.items(): - senv += k + '=' + v + '\0' - senv += '\0' + senv += k + "=" + v + "\0" + senv += "\0" return ctypes.create_unicode_buffer(senv) -def CreateProcessWithTokenW(token, - logonflags=0, - applicationname=None, - commandline=None, - creationflags=0, - environment=None, - currentdirectory=None, - startupinfo=None): +def CreateProcessWithTokenW( + token, + logonflags=0, + applicationname=None, + commandline=None, + creationflags=0, + environment=None, + currentdirectory=None, + startupinfo=None, +): creationflags |= win32con.CREATE_UNICODE_ENVIRONMENT if commandline is not None: commandline = ctypes.create_unicode_buffer(commandline) @@ -1050,17 +1154,19 @@ def CreateProcessWithTokenW(token, ) if ret == 0: winerr = win32api.GetLastError() - exc = WindowsError(win32api.FormatMessage(winerr)) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + exc = WindowsError(win32api.FormatMessage(winerr)) + # pylint: enable=undefined-variable exc.winerror = winerr raise exc return process_info def enumerate_tokens(sid=None, session_id=None, privs=None): - ''' + """ Enumerate tokens from any existing processes that can be accessed. Optionally filter by sid. - ''' + """ for p in psutil.process_iter(): if p.pid == 0: continue @@ -1073,19 +1179,31 @@ def enumerate_tokens(sid=None, session_id=None, privs=None): raise exc try: access = ( - win32security.TOKEN_DUPLICATE | - win32security.TOKEN_QUERY | - win32security.TOKEN_IMPERSONATE | - win32security.TOKEN_ASSIGN_PRIMARY + win32security.TOKEN_DUPLICATE + | win32security.TOKEN_QUERY + | win32security.TOKEN_IMPERSONATE + | win32security.TOKEN_ASSIGN_PRIMARY ) th = win32security.OpenProcessToken(ph, access) except Exception as exc: # pylint: disable=broad-except - log.debug("OpenProcessToken failed pid=%d name=%s user%s", p.pid, p.name(), p.username()) + log.debug( + "OpenProcessToken failed pid=%d name=%s user%s", + p.pid, + p.name(), + p.username(), + ) continue try: - process_sid = win32security.GetTokenInformation(th, win32security.TokenUser)[0] + process_sid = win32security.GetTokenInformation( + th, win32security.TokenUser + )[0] except Exception as exc: # pylint: disable=broad-except - log.exception("GetTokenInformation pid=%d name=%s user%s", p.pid, p.name(), p.username()) + log.exception( + "GetTokenInformation pid=%d name=%s user%s", + p.pid, + p.name(), + p.username(), + ) continue proc_sid = win32security.ConvertSidToStringSid(process_sid) @@ -1093,15 +1211,22 @@ def enumerate_tokens(sid=None, session_id=None, privs=None): log.debug("Token for pid does not match user sid: %s", sid) continue - if session_id and win32security.GetTokenInformation(th, win32security.TokenSessionId) != session_id: + if ( + session_id + and win32security.GetTokenInformation(th, win32security.TokenSessionId) + != session_id + ): continue def has_priv(tok, priv): luid = win32security.LookupPrivilegeValue(None, priv) - for priv_luid, flags in win32security.GetTokenInformation(tok, win32security.TokenPrivileges): + for priv_luid, flags in win32security.GetTokenInformation( + tok, win32security.TokenPrivileges + ): if priv_luid == luid: return True return False + if privs: has_all = True for name in privs: @@ -1113,41 +1238,42 @@ def enumerate_tokens(sid=None, session_id=None, privs=None): def impersonate_sid(sid, session_id=None, privs=None): - ''' + """ Find an existing process token for the given sid and impersonate the token. - ''' + """ for tok in enumerate_tokens(sid, session_id, privs): tok = dup_token(tok) elevate_token(tok) if win32security.ImpersonateLoggedOnUser(tok) == 0: - raise WindowsError("Impersonation failure") # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + raise WindowsError("Impersonation failure") + # pylint: enable=undefined-variable return tok raise WindowsError("Impersonation failure") # pylint: disable=undefined-variable def dup_token(th): - ''' + """ duplicate the access token - ''' + """ # TODO: is `duplicate_token` the same? sec_attr = win32security.SECURITY_ATTRIBUTES() sec_attr.bInheritHandle = True return win32security.DuplicateTokenEx( - th, - win32security.SecurityImpersonation, - win32con.MAXIMUM_ALLOWED, - win32security.TokenPrimary, - sec_attr, + th, + win32security.SecurityImpersonation, + win32con.MAXIMUM_ALLOWED, + win32security.TokenPrimary, + sec_attr, ) def elevate_token(th): - ''' + """ Set all token privileges to enabled - ''' + """ # Get list of privileges this token contains - privileges = win32security.GetTokenInformation( - th, win32security.TokenPrivileges) + privileges = win32security.GetTokenInformation(th, win32security.TokenPrivileges) # Create a set of all privileges to be enabled enable_privs = set() @@ -1156,31 +1282,35 @@ def elevate_token(th): # Enable the privileges if win32security.AdjustTokenPrivileges(th, 0, enable_privs) == 0: - raise WindowsError(win32api.FormatMessage(win32api.GetLastError())) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + raise WindowsError(win32api.FormatMessage(win32api.GetLastError())) + # pylint: enable=undefined-variable def make_inheritable(token): - '''Create an inheritable handle''' + """Create an inheritable handle""" return win32api.DuplicateHandle( win32api.GetCurrentProcess(), token, win32api.GetCurrentProcess(), 0, 1, - win32con.DUPLICATE_SAME_ACCESS + win32con.DUPLICATE_SAME_ACCESS, ) -def CreateProcessWithLogonW(username=None, - domain=None, - password=None, - logonflags=0, - applicationname=None, - commandline=None, - creationflags=0, - environment=None, - currentdirectory=None, - startupinfo=None): +def CreateProcessWithLogonW( + username=None, + domain=None, + password=None, + logonflags=0, + applicationname=None, + commandline=None, + creationflags=0, + environment=None, + currentdirectory=None, + startupinfo=None, +): creationflags |= win32con.CREATE_UNICODE_ENVIRONMENT if commandline is not None: commandline = ctypes.create_unicode_buffer(commandline) diff --git a/salt/proxy/__init__.py b/salt/proxy/__init__.py index cc8086fc675..00523d6fcc7 100644 --- a/salt/proxy/__init__.py +++ b/salt/proxy/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" salt.proxy package -''' +""" diff --git a/salt/proxy/arista_pyeapi.py b/salt/proxy/arista_pyeapi.py index a1fbcba4896..0446d8c844e 100644 --- a/salt/proxy/arista_pyeapi.py +++ b/salt/proxy/arista_pyeapi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Arista pyeapi ============= @@ -64,34 +64,36 @@ Proxy Pillar Example host: router1.example.com username: example password: example -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python stdlib import logging +# Import salt modules +from salt.utils.args import clean_kwargs + # Import third party libs try: import pyeapi + HAS_PYEAPI = True except ImportError: HAS_PYEAPI = False -# Import salt modules -from salt.utils.args import clean_kwargs # ----------------------------------------------------------------------------- # proxy properties # ----------------------------------------------------------------------------- -__proxyenabled__ = ['pyeapi'] +__proxyenabled__ = ["pyeapi"] # proxy name # ----------------------------------------------------------------------------- # globals # ----------------------------------------------------------------------------- -__virtualname__ = 'pyeapi' +__virtualname__ = "pyeapi" log = logging.getLogger(__name__) pyeapi_device = {} @@ -101,59 +103,64 @@ pyeapi_device = {} def __virtual__(): - ''' + """ Proxy module available only if pyeapi is installed. - ''' + """ if not HAS_PYEAPI: - return False, 'The pyeapi proxy module requires the pyeapi library to be installed.' + return ( + False, + "The pyeapi proxy module requires the pyeapi library to be installed.", + ) return __virtualname__ + # ----------------------------------------------------------------------------- # proxy functions # ----------------------------------------------------------------------------- def init(opts): - ''' + """ Open the connection to the Arista switch over the eAPI. - ''' - proxy_dict = opts.get('proxy', {}) + """ + proxy_dict = opts.get("proxy", {}) conn_args = proxy_dict.copy() - conn_args.pop('proxytype', None) - opts['multiprocessing'] = conn_args.get('multiprocessing', True) + conn_args.pop("proxytype", None) + opts["multiprocessing"] = conn_args.get("multiprocessing", True) # This is not a SSH-based proxy, so it should be safe to enable # multiprocessing. try: conn = pyeapi.client.connect(**conn_args) - node = pyeapi.client.Node(conn, enablepwd=conn_args.get('enablepwd')) - pyeapi_device['connection'] = node - pyeapi_device['initialized'] = True - pyeapi_device['up'] = True + node = pyeapi.client.Node(conn, enablepwd=conn_args.get("enablepwd")) + pyeapi_device["connection"] = node + pyeapi_device["initialized"] = True + pyeapi_device["up"] = True except pyeapi.eapilib.ConnectionError as cerr: - log.error('Unable to connect to %s', conn_args['host'], exc_info=True) + log.error("Unable to connect to %s", conn_args["host"], exc_info=True) return False return True def ping(): - ''' + """ Connection open successfully? - ''' - return pyeapi_device.get('up', False) + """ + return pyeapi_device.get("up", False) def initialized(): - ''' + """ Connection finished initializing? - ''' - return pyeapi_device.get('initialized', False) + """ + return pyeapi_device.get("initialized", False) def shutdown(opts): - ''' + """ Closes connection with the device. - ''' - log.debug('Shutting down the pyeapi Proxy Minion %s', opts['id']) + """ + log.debug("Shutting down the pyeapi Proxy Minion %s", opts["id"]) + # ----------------------------------------------------------------------------- # callable functions @@ -161,15 +168,15 @@ def shutdown(opts): def conn(): - ''' + """ Return the connection object. - ''' - return pyeapi_device.get('connection') + """ + return pyeapi_device.get("connection") def call(method, *args, **kwargs): - ''' + """ Calls an arbitrary pyeapi method. - ''' + """ kwargs = clean_kwargs(**kwargs) - return getattr(pyeapi_device['connection'], method)(*args, **kwargs) + return getattr(pyeapi_device["connection"], method)(*args, **kwargs) diff --git a/salt/proxy/chronos.py b/salt/proxy/chronos.py index b85f7d8d004..cc60fc2c6a3 100644 --- a/salt/proxy/chronos.py +++ b/salt/proxy/chronos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Chronos ======== @@ -23,16 +23,16 @@ the chronos endpoint: base_url: http://my-chronos-master.mydomain.com:4400 .. versionadded:: 2015.8.2 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + import salt.utils.http - -__proxyenabled__ = ['chronos'] +__proxyenabled__ = ["chronos"] CONFIG = {} -CONFIG_BASE_URL = 'base_url' +CONFIG_BASE_URL = "base_url" log = logging.getLogger(__file__) @@ -41,43 +41,40 @@ def __virtual__(): def init(opts): - ''' + """ Perform any needed setup. - ''' - if CONFIG_BASE_URL in opts['proxy']: - CONFIG[CONFIG_BASE_URL] = opts['proxy'][CONFIG_BASE_URL] + """ + if CONFIG_BASE_URL in opts["proxy"]: + CONFIG[CONFIG_BASE_URL] = opts["proxy"][CONFIG_BASE_URL] else: - log.error('missing proxy property %s', CONFIG_BASE_URL) - log.debug('CONFIG: %s', CONFIG) + log.error("missing proxy property %s", CONFIG_BASE_URL) + log.debug("CONFIG: %s", CONFIG) def ping(): - ''' + """ Is the chronos api responding? - ''' + """ try: response = salt.utils.http.query( "{0}/scheduler/jobs".format(CONFIG[CONFIG_BASE_URL]), - decode_type='json', + decode_type="json", decode=True, ) log.debug( - 'chronos.info returned successfully: %s', - response, + "chronos.info returned successfully: %s", response, ) - if 'dict' in response: + if "dict" in response: return True except Exception as ex: # pylint: disable=broad-except log.error( - 'error pinging chronos with base_url %s: %s', - CONFIG[CONFIG_BASE_URL], - ex, + "error pinging chronos with base_url %s: %s", CONFIG[CONFIG_BASE_URL], ex, ) return False def shutdown(opts): - ''' + """ For this proxy shutdown is a no-op - ''' - log.debug('chronos proxy shutdown() called...') + """ + log.debug("chronos proxy shutdown() called...") diff --git a/salt/proxy/cimc.py b/salt/proxy/cimc.py index ef1f0412b4b..2e4adf13cf8 100644 --- a/salt/proxy/cimc.py +++ b/salt/proxy/cimc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Proxy Minion interface module for managing Cisco Integrated Management Controller devices ========================================================================================= @@ -64,7 +64,7 @@ password ^^^^^^^^ The password used to login to the cimc host. Required. -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -77,62 +77,64 @@ import salt.exceptions from salt._compat import ElementTree as ET # This must be present or the Salt loader won't load this module. -__proxyenabled__ = ['cimc'] +__proxyenabled__ = ["cimc"] # Variables are scoped to this module so we can have persistent data. -GRAINS_CACHE = {'vendor': 'Cisco'} +GRAINS_CACHE = {"vendor": "Cisco"} DETAILS = {} # Set up logging log = logging.getLogger(__file__) # Define the module's virtual name -__virtualname__ = 'cimc' +__virtualname__ = "cimc" def __virtual__(): - ''' + """ Only return if all the modules are available. - ''' + """ return __virtualname__ def init(opts): - ''' + """ This function gets called when the proxy starts up. - ''' - if 'host' not in opts['proxy']: - log.critical('No \'host\' key found in pillar for this proxy.') + """ + if "host" not in opts["proxy"]: + log.critical("No 'host' key found in pillar for this proxy.") return False - if 'username' not in opts['proxy']: - log.critical('No \'username\' key found in pillar for this proxy.') + if "username" not in opts["proxy"]: + log.critical("No 'username' key found in pillar for this proxy.") return False - if 'password' not in opts['proxy']: - log.critical('No \'passwords\' key found in pillar for this proxy.') + if "password" not in opts["proxy"]: + log.critical("No 'passwords' key found in pillar for this proxy.") return False - DETAILS['url'] = 'https://{0}/nuova'.format(opts['proxy']['host']) - DETAILS['headers'] = {'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': 62, - 'USER-Agent': 'lwp-request/2.06'} + DETAILS["url"] = "https://{0}/nuova".format(opts["proxy"]["host"]) + DETAILS["headers"] = { + "Content-Type": "application/x-www-form-urlencoded", + "Content-Length": 62, + "USER-Agent": "lwp-request/2.06", + } # Set configuration details - DETAILS['host'] = opts['proxy']['host'] - DETAILS['username'] = opts['proxy'].get('username') - DETAILS['password'] = opts['proxy'].get('password') + DETAILS["host"] = opts["proxy"]["host"] + DETAILS["username"] = opts["proxy"].get("username") + DETAILS["password"] = opts["proxy"].get("password") # Ensure connectivity to the device log.debug("Attempting to connect to cimc proxy host.") get_config_resolver_class("computeRackUnit") log.debug("Successfully connected to cimc proxy host.") - DETAILS['initialized'] = True + DETAILS["initialized"] = True def set_config_modify(dn=None, inconfig=None, hierarchical=False): - ''' + """ The configConfMo method configures the specified managed object in a single subtree (for example, DN). - ''' + """ ret = {} cookie = logon() @@ -141,17 +143,21 @@ def set_config_modify(dn=None, inconfig=None, hierarchical=False): if hierarchical is True: h = "true" - payload = '<configConfMo cookie="{0}" inHierarchical="{1}" dn="{2}">' \ - '<inConfig>{3}</inConfig></configConfMo>'.format(cookie, h, dn, inconfig) - r = __utils__['http.query'](DETAILS['url'], - data=payload, - method='POST', - decode_type='plain', - decode=True, - verify_ssl=False, - raise_error=True, - headers=DETAILS['headers']) - answer = re.findall(r'(<[\s\S.]*>)', r['text'])[0] + payload = ( + '<configConfMo cookie="{0}" inHierarchical="{1}" dn="{2}">' + "<inConfig>{3}</inConfig></configConfMo>".format(cookie, h, dn, inconfig) + ) + r = __utils__["http.query"]( + DETAILS["url"], + data=payload, + method="POST", + decode_type="plain", + decode=True, + verify_ssl=False, + raise_error=True, + headers=DETAILS["headers"], + ) + answer = re.findall(r"(<[\s\S.]*>)", r["text"])[0] items = ET.fromstring(answer) logout(cookie) for item in items: @@ -160,9 +166,9 @@ def set_config_modify(dn=None, inconfig=None, hierarchical=False): def get_config_resolver_class(cid=None, hierarchical=False): - ''' + """ The configResolveClass method returns requested managed object in a given class. - ''' + """ ret = {} cookie = logon() @@ -171,17 +177,21 @@ def get_config_resolver_class(cid=None, hierarchical=False): if hierarchical is True: h = "true" - payload = '<configResolveClass cookie="{0}" inHierarchical="{1}" classId="{2}"/>'.format(cookie, h, cid) - r = __utils__['http.query'](DETAILS['url'], - data=payload, - method='POST', - decode_type='plain', - decode=True, - verify_ssl=False, - raise_error=True, - headers=DETAILS['headers']) + payload = '<configResolveClass cookie="{0}" inHierarchical="{1}" classId="{2}"/>'.format( + cookie, h, cid + ) + r = __utils__["http.query"]( + DETAILS["url"], + data=payload, + method="POST", + decode_type="plain", + decode=True, + verify_ssl=False, + raise_error=True, + headers=DETAILS["headers"], + ) - answer = re.findall(r'(<[\s\S.]*>)', r['text'])[0] + answer = re.findall(r"(<[\s\S.]*>)", r["text"])[0] items = ET.fromstring(answer) logout(cookie) for item in items: @@ -190,50 +200,56 @@ def get_config_resolver_class(cid=None, hierarchical=False): def logon(): - ''' + """ Logs into the cimc device and returns the session cookie. - ''' + """ content = {} - payload = "<aaaLogin inName='{0}' inPassword='{1}'></aaaLogin>".format(DETAILS['username'], DETAILS['password']) - r = __utils__['http.query'](DETAILS['url'], - data=payload, - method='POST', - decode_type='plain', - decode=True, - verify_ssl=False, - raise_error=False, - headers=DETAILS['headers']) - answer = re.findall(r'(<[\s\S.]*>)', r['text'])[0] + payload = "<aaaLogin inName='{0}' inPassword='{1}'></aaaLogin>".format( + DETAILS["username"], DETAILS["password"] + ) + r = __utils__["http.query"]( + DETAILS["url"], + data=payload, + method="POST", + decode_type="plain", + decode=True, + verify_ssl=False, + raise_error=False, + headers=DETAILS["headers"], + ) + answer = re.findall(r"(<[\s\S.]*>)", r["text"])[0] items = ET.fromstring(answer) for item in items.attrib: content[item] = items.attrib[item] - if 'outCookie' not in content: + if "outCookie" not in content: raise salt.exceptions.CommandExecutionError("Unable to log into proxy device.") - return content['outCookie'] + return content["outCookie"] def logout(cookie=None): - ''' + """ Closes the session with the device. - ''' + """ payload = '<aaaLogout cookie="{0}" inCookie="{0}"></aaaLogout>'.format(cookie) - __utils__['http.query'](DETAILS['url'], - data=payload, - method='POST', - decode_type='plain', - decode=True, - verify_ssl=False, - raise_error=True, - headers=DETAILS['headers']) + __utils__["http.query"]( + DETAILS["url"], + data=payload, + method="POST", + decode_type="plain", + decode=True, + verify_ssl=False, + raise_error=True, + headers=DETAILS["headers"], + ) return def prepare_return(x): - ''' + """ Converts the etree to dict - ''' + """ ret = {} for a in list(x): if a.tag not in ret: @@ -245,40 +261,40 @@ def prepare_return(x): def initialized(): - ''' + """ Since grains are loaded in many different places and some of those places occur before the proxy can be initialized, return whether our init() function has been called - ''' - return DETAILS.get('initialized', False) + """ + return DETAILS.get("initialized", False) def grains(): - ''' + """ Get the grains from the proxied device - ''' - if not DETAILS.get('grains_cache', {}): - DETAILS['grains_cache'] = GRAINS_CACHE + """ + if not DETAILS.get("grains_cache", {}): + DETAILS["grains_cache"] = GRAINS_CACHE try: - compute_rack = get_config_resolver_class('computeRackUnit', False) - DETAILS['grains_cache'] = compute_rack['outConfigs']['computeRackUnit'] + compute_rack = get_config_resolver_class("computeRackUnit", False) + DETAILS["grains_cache"] = compute_rack["outConfigs"]["computeRackUnit"] except Exception as err: # pylint: disable=broad-except log.error(err) - return DETAILS['grains_cache'] + return DETAILS["grains_cache"] def grains_refresh(): - ''' + """ Refresh the grains from the proxied device - ''' - DETAILS['grains_cache'] = None + """ + DETAILS["grains_cache"] = None return grains() def ping(): - ''' + """ Returns true if the device is reachable, else false. - ''' + """ try: cookie = logon() logout(cookie) @@ -289,8 +305,8 @@ def ping(): def shutdown(): - ''' + """ Shutdown the connection to the proxy device. For this proxy, shutdown is a no-op. - ''' - log.debug('CIMC proxy shutdown() called.') + """ + log.debug("CIMC proxy shutdown() called.") diff --git a/salt/proxy/cisconso.py b/salt/proxy/cisconso.py index 4bc94b4c04b..c88ce0f4c5a 100644 --- a/salt/proxy/cisconso.py +++ b/salt/proxy/cisconso.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Proxy Minion interface module for managing (practically) any network device with Cisco Network Services Orchestrator (Cisco NSO). Cisco NSO uses a series of remote polling @@ -170,21 +170,23 @@ responding: .. code-block:: bash salt <id> test.ping -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs from salt.exceptions import SaltSystemExit # This must be present or the Salt loader won't load this module. -__proxyenabled__ = ['cisconso'] +__proxyenabled__ = ["cisconso"] try: from pynso.client import NSOClient from pynso.datastores import DatastoreType + HAS_PYNSO_LIBS = True except ImportError: HAS_PYNSO_LIBS = False @@ -198,7 +200,7 @@ DETAILS = {} log = logging.getLogger(__file__) # Define the module's virtual name -__virtualname__ = 'cisconso' +__virtualname__ = "cisconso" def __virtual__(): @@ -207,17 +209,17 @@ def __virtual__(): def init(opts): # Set configuration details - DETAILS['host'] = opts['proxy'].get('host') - DETAILS['username'] = opts['proxy'].get('username') - DETAILS['password'] = opts['proxy'].get('password') - DETAILS['use_ssl'] = bool(opts['proxy'].get('use_ssl')) - DETAILS['port'] = int(opts['proxy'].get('port')) + DETAILS["host"] = opts["proxy"].get("host") + DETAILS["username"] = opts["proxy"].get("username") + DETAILS["password"] = opts["proxy"].get("password") + DETAILS["use_ssl"] = bool(opts["proxy"].get("use_ssl")) + DETAILS["port"] = int(opts["proxy"].get("port")) def grains(): - ''' + """ Get the grains from the proxy device. - ''' + """ if not GRAINS_CACHE: return _grains() return GRAINS_CACHE @@ -225,15 +227,16 @@ def grains(): def _get_client(): return NSOClient( - host=DETAILS['host'], - username=DETAILS['username'], - password=DETAILS['password'], - port=DETAILS['port'], - ssl=DETAILS['use_ssl']) + host=DETAILS["host"], + username=DETAILS["username"], + password=DETAILS["password"], + port=DETAILS["port"], + ssl=DETAILS["use_ssl"], + ) def ping(): - ''' + """ Check to see if the host is responding. Returns False if the host didn't respond, True otherwise. @@ -242,7 +245,7 @@ def ping(): .. code-block:: bash salt cisco-nso test.ping - ''' + """ try: client = _get_client() client.info() @@ -254,15 +257,15 @@ def ping(): def shutdown(): - ''' + """ Shutdown the connection to the proxy device. For this proxy, shutdown is a no-op. - ''' - log.debug('Cisco NSO proxy shutdown() called...') + """ + log.debug("Cisco NSO proxy shutdown() called...") def get_data(datastore, path): - ''' + """ Get the configuration of the device tree at the given path :param datastore: The datastore, e.g. running, operational. @@ -279,13 +282,13 @@ def get_data(datastore, path): .. code-block:: bash salt cisco-nso cisconso.get_data devices - ''' + """ client = _get_client() return client.get_datastore_data(datastore, path) def set_data_value(datastore, path, data): - ''' + """ Get a data entry in a datastore :param datastore: The datastore, e.g. running, operational. @@ -301,20 +304,20 @@ def set_data_value(datastore, path, data): :rtype: ``bool`` :return: ``True`` if successful, otherwise error. - ''' + """ client = _get_client() return client.set_data_value(datastore, path, data) def get_rollbacks(): - ''' + """ Get a list of stored configuration rollbacks - ''' + """ return _get_client().get_rollbacks() def get_rollback(name): - ''' + """ Get the backup of stored a configuration rollback :param name: Typically an ID of the backup @@ -322,12 +325,12 @@ def get_rollback(name): :rtype: ``str`` :return: the contents of the rollback snapshot - ''' + """ return _get_client().get_rollback(name) def apply_rollback(datastore, name): - ''' + """ Apply a system rollback :param datastore: The datastore, e.g. running, operational. @@ -336,14 +339,14 @@ def apply_rollback(datastore, name): :param name: an ID of the rollback to restore :type name: ``str`` - ''' + """ return _get_client().apply_rollback(datastore, name) def _grains(): - ''' + """ Helper function to the grains from the proxied devices. - ''' + """ client = _get_client() # This is a collection of the configuration of all running devices under NSO ret = client.get_datastore(DatastoreType.RUNNING) diff --git a/salt/proxy/docker.py b/salt/proxy/docker.py index c70f8f52377..920f962ad0a 100644 --- a/salt/proxy/docker.py +++ b/salt/proxy/docker.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Docker Proxy Minion .. versionadded: 2019.2.0 @@ -31,42 +31,44 @@ container, and use grains to configure the proxy minion: name Name of the docker container -''' +""" from __future__ import absolute_import, unicode_literals -__proxyenabled__ = ['docker'] -__virtualname__ = 'docker' +__proxyenabled__ = ["docker"] +__virtualname__ = "docker" def __virtual__(): - if __opts__.get('proxy', {}).get('proxytype') != __virtualname__: - return False, 'Proxytype does not match: {0}'.format(__virtualname__) + if __opts__.get("proxy", {}).get("proxytype") != __virtualname__: + return False, "Proxytype does not match: {0}".format(__virtualname__) return True def module_executors(): - ''' + """ List of module executors to use for this Proxy Minion - ''' - return ['docker', ] + """ + return [ + "docker", + ] def init(opts): - ''' + """ Always initialize - ''' - __context__['initialized'] = True + """ + __context__["initialized"] = True def initialized(): - ''' + """ This should always be initialized - ''' - return __context__.get('initialized', False) + """ + return __context__.get("initialized", False) def shutdown(opts): - ''' + """ Nothing needs to be done to shutdown - ''' - __context__['initialized'] = False + """ + __context__["initialized"] = False diff --git a/salt/proxy/dummy.py b/salt/proxy/dummy.py index 486f52a4d4a..269ff148c23 100644 --- a/salt/proxy/dummy.py +++ b/salt/proxy/dummy.py @@ -1,28 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" This is the a dummy proxy-minion designed for testing the proxy minion subsystem. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os import pickle -import logging # Import Salt libs import salt.ext.six as six import salt.utils.files # This must be present or the Salt loader won't load this module -__proxyenabled__ = ['dummy'] +__proxyenabled__ = ["dummy"] # Variables are scoped to this module so we can have persistent data # across calls to fns in here. DETAILS = {} -DETAILS['services'] = {'apache': 'running', 'ntp': 'running', 'samba': 'stopped'} -DETAILS['packages'] = {'coreutils': '1.0', 'apache': '2.4', 'tinc': '1.4', 'redbull': '999.99'} +DETAILS["services"] = {"apache": "running", "ntp": "running", "samba": "stopped"} +DETAILS["packages"] = { + "coreutils": "1.0", + "apache": "2.4", + "tinc": "1.4", + "redbull": "999.99", +} FILENAME = salt.utils.files.mkstemp() # Want logging! log = logging.getLogger(__file__) @@ -31,30 +37,30 @@ log = logging.getLogger(__file__) # This does nothing, it's here just as an example and to provide a log # entry when the module is loaded. def __virtual__(): - ''' + """ Only return if all the modules are available - ''' - log.debug('dummy proxy __virtual__() called...') + """ + log.debug("dummy proxy __virtual__() called...") return True def _save_state(details): - with salt.utils.files.fopen(FILENAME, 'wb') as pck: + with salt.utils.files.fopen(FILENAME, "wb") as pck: pickle.dump(details, pck) def _load_state(): try: if six.PY3 is True: - mode = 'rb' + mode = "rb" else: - mode = 'r' + mode = "r" with salt.utils.files.fopen(FILENAME, mode) as pck: DETAILS = pickle.load(pck) except EOFError: DETAILS = {} - DETAILS['initialized'] = False + DETAILS["initialized"] = False _save_state(DETAILS) return DETAILS @@ -64,181 +70,188 @@ def _load_state(): # just put DETAILS['initialized'] = True here if nothing # else needs to be done. + def init(opts): - log.debug('dummy proxy init() called...') - DETAILS['initialized'] = True + log.debug("dummy proxy init() called...") + DETAILS["initialized"] = True _save_state(DETAILS) def initialized(): - ''' + """ Since grains are loaded in many different places and some of those places occur before the proxy can be initialized, return whether our init() function has been called - ''' + """ DETAILS = _load_state() - return DETAILS.get('initialized', False) + return DETAILS.get("initialized", False) def grains(): - ''' + """ Make up some grains - ''' + """ DETAILS = _load_state() - if 'grains_cache' not in DETAILS: - DETAILS['grains_cache'] = {'dummy_grain_1': 'one', 'dummy_grain_2': 'two', 'dummy_grain_3': 'three', } + if "grains_cache" not in DETAILS: + DETAILS["grains_cache"] = { + "dummy_grain_1": "one", + "dummy_grain_2": "two", + "dummy_grain_3": "three", + } _save_state(DETAILS) - return DETAILS['grains_cache'] + return DETAILS["grains_cache"] def grains_refresh(): - ''' + """ Refresh the grains - ''' + """ DETAILS = _load_state() - DETAILS['grains_cache'] = None + DETAILS["grains_cache"] = None _save_state(DETAILS) return grains() def fns(): - return {'details': 'This key is here because a function in ' - 'grains/rest_sample.py called fns() here in the proxymodule.'} + return { + "details": "This key is here because a function in " + "grains/rest_sample.py called fns() here in the proxymodule." + } def service_start(name): - ''' + """ Start a "service" on the dummy server - ''' + """ DETAILS = _load_state() - DETAILS['services'][name] = 'running' + DETAILS["services"][name] = "running" _save_state(DETAILS) - return 'running' + return "running" def service_stop(name): - ''' + """ Stop a "service" on the dummy server - ''' + """ DETAILS = _load_state() - DETAILS['services'][name] = 'stopped' + DETAILS["services"][name] = "stopped" _save_state(DETAILS) - return 'stopped' + return "stopped" def service_restart(name): - ''' + """ Restart a "service" on the REST server - ''' + """ return True def service_list(): - ''' + """ List "services" on the REST server - ''' + """ DETAILS = _load_state() - return list(DETAILS['services']) + return list(DETAILS["services"]) def service_status(name): - ''' + """ Check if a service is running on the REST server - ''' + """ DETAILS = _load_state() - if DETAILS['services'][name] == 'running': - return {'comment': 'running'} + if DETAILS["services"][name] == "running": + return {"comment": "running"} else: - return {'comment': 'stopped'} + return {"comment": "stopped"} def package_list(): - ''' + """ List "packages" installed on the REST server - ''' + """ DETAILS = _load_state() - return DETAILS['packages'] + return DETAILS["packages"] def package_install(name, **kwargs): - ''' + """ Install a "package" on the REST server - ''' + """ DETAILS = _load_state() - if kwargs.get('version', False): - version = kwargs['version'] + if kwargs.get("version", False): + version = kwargs["version"] else: - version = '1.0' - DETAILS['packages'][name] = version + version = "1.0" + DETAILS["packages"][name] = version _save_state(DETAILS) return {name: version} def upgrade(): - ''' + """ "Upgrade" packages - ''' + """ DETAILS = _load_state() pkgs = uptodate() - DETAILS['packages'] = pkgs + DETAILS["packages"] = pkgs _save_state(DETAILS) return pkgs def uptodate(): - ''' + """ Call the REST endpoint to see if the packages on the "server" are up to date. - ''' + """ DETAILS = _load_state() - for p in DETAILS['packages']: - version_float = float(DETAILS['packages'][p]) + for p in DETAILS["packages"]: + version_float = float(DETAILS["packages"][p]) version_float = version_float + 1.0 - DETAILS['packages'][p] = six.text_type(version_float) - return DETAILS['packages'] + DETAILS["packages"][p] = six.text_type(version_float) + return DETAILS["packages"] def package_remove(name): - ''' + """ Remove a "package" on the REST server - ''' + """ DETAILS = _load_state() - DETAILS['packages'].pop(name) + DETAILS["packages"].pop(name) _save_state(DETAILS) - return DETAILS['packages'] + return DETAILS["packages"] def package_status(name): - ''' + """ Check the installation status of a package on the REST server - ''' + """ DETAILS = _load_state() - if name in DETAILS['packages']: - return {name: DETAILS['packages'][name]} + if name in DETAILS["packages"]: + return {name: DETAILS["packages"][name]} def ping(): - ''' + """ Degenerate ping - ''' - log.debug('dummy proxy returning ping') + """ + log.debug("dummy proxy returning ping") return True def shutdown(opts): - ''' + """ For this proxy shutdown is a no-op - ''' - log.debug('dummy proxy shutdown() called...') + """ + log.debug("dummy proxy shutdown() called...") DETAILS = _load_state() - if 'filename' in DETAILS: - os.unlink(DETAILS['filename']) + if "filename" in DETAILS: + os.unlink(DETAILS["filename"]) def test_from_state(): - ''' + """ Test function so we have something to call from a state :return: - ''' - log.debug('test_from_state called') - return 'testvalue' + """ + log.debug("test_from_state called") + return "testvalue" diff --git a/salt/proxy/esxcluster.py b/salt/proxy/esxcluster.py index 19a8bc4cf59..2e8b2394fd0 100644 --- a/salt/proxy/esxcluster.py +++ b/salt/proxy/esxcluster.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Proxy Minion interface module for managing VMWare ESXi clusters. Dependencies @@ -67,7 +67,7 @@ The location of the VMware vCenter server (host of ip) where the datacenter should be managed. Required. mechanism -^^^^^^^^ +^^^^^^^^^ The mechanism used to connect to the vCenter server. Supported values are ``userpass`` and ``sspi``. Required. @@ -91,7 +91,7 @@ domain User domain. Required if mechanism is ``sspi``. principal -^^^^^^^^ +^^^^^^^^^ Kerberos principal. Rquired if mechanism is ``sspi``. protocol @@ -149,11 +149,12 @@ Associated states are documented in :mod:`salt.states.esxcluster </ref/states/all/salt.states.esxcluster>`. Look there to find an example structure for Pillar as well as an example ``.sls`` file for configuring an ESX cluster from scratch. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -163,11 +164,12 @@ from salt.config.schemas.esxcluster import EsxclusterProxySchema from salt.utils.dictupdate import merge # This must be present or the Salt loader won't load this module. -__proxyenabled__ = ['esxcluster'] +__proxyenabled__ = ["esxcluster"] # External libraries try: import jsonschema + HAS_JSONSCHEMA = True except ImportError: HAS_JSONSCHEMA = False @@ -181,84 +183,90 @@ DETAILS = {} # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'esxcluster' +__virtualname__ = "esxcluster" def __virtual__(): - ''' + """ Only load if the vsphere execution module is available. - ''' + """ if HAS_JSONSCHEMA: return __virtualname__ - return False, 'The esxcluster proxy module did not load.' + return False, "The esxcluster proxy module did not load." def init(opts): - ''' + """ This function gets called when the proxy starts up. For login the protocol and port are cached. - ''' - log.debug('Initting esxcluster proxy module in process %s', os.getpid()) - log.debug('Validating esxcluster proxy input') + """ + log.debug("Initting esxcluster proxy module in process %s", os.getpid()) + log.debug("Validating esxcluster proxy input") schema = EsxclusterProxySchema.serialize() - log.trace('schema = %s', schema) - proxy_conf = merge(opts.get('proxy', {}), __pillar__.get('proxy', {})) - log.trace('proxy_conf = %s', proxy_conf) + log.trace("schema = %s", schema) + proxy_conf = merge(opts.get("proxy", {}), __pillar__.get("proxy", {})) + log.trace("proxy_conf = %s", proxy_conf) try: jsonschema.validate(proxy_conf, schema) except jsonschema.exceptions.ValidationError as exc: raise salt.exceptions.InvalidConfigError(exc) # Save mandatory fields in cache - for key in ('vcenter', 'datacenter', 'cluster', 'mechanism'): + for key in ("vcenter", "datacenter", "cluster", "mechanism"): DETAILS[key] = proxy_conf[key] # Additional validation - if DETAILS['mechanism'] == 'userpass': - if 'username' not in proxy_conf: + if DETAILS["mechanism"] == "userpass": + if "username" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'userpass\', but no ' - '\'username\' key found in proxy config.') - if 'passwords' not in proxy_conf: + "Mechanism is set to 'userpass', but no " + "'username' key found in proxy config." + ) + if "passwords" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'userpass\', but no ' - '\'passwords\' key found in proxy config.') - for key in ('username', 'passwords'): + "Mechanism is set to 'userpass', but no " + "'passwords' key found in proxy config." + ) + for key in ("username", "passwords"): DETAILS[key] = proxy_conf[key] else: - if 'domain' not in proxy_conf: + if "domain" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'sspi\', but no ' - '\'domain\' key found in proxy config.') - if 'principal' not in proxy_conf: + "Mechanism is set to 'sspi', but no " + "'domain' key found in proxy config." + ) + if "principal" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'sspi\', but no ' - '\'principal\' key found in proxy config.') - for key in ('domain', 'principal'): + "Mechanism is set to 'sspi', but no " + "'principal' key found in proxy config." + ) + for key in ("domain", "principal"): DETAILS[key] = proxy_conf[key] # Save optional - DETAILS['protocol'] = proxy_conf.get('protocol') - DETAILS['port'] = proxy_conf.get('port') + DETAILS["protocol"] = proxy_conf.get("protocol") + DETAILS["port"] = proxy_conf.get("port") # Test connection - if DETAILS['mechanism'] == 'userpass': + if DETAILS["mechanism"] == "userpass": # Get the correct login details - log.debug('Retrieving credentials and testing vCenter connection for ' - 'mehchanism \'userpass\'') + log.debug( + "Retrieving credentials and testing vCenter connection for " + "mehchanism 'userpass'" + ) try: username, password = find_credentials() - DETAILS['password'] = password + DETAILS["password"] = password except salt.exceptions.SaltSystemExit as err: - log.critical('Error: %s', err) + log.critical("Error: %s", err) return False return True def ping(): - ''' + """ Returns True. CLI Example: @@ -266,44 +274,45 @@ def ping(): .. code-block:: bash salt esx-cluster test.ping - ''' + """ return True def shutdown(): - ''' + """ Shutdown the connection to the proxy device. For this proxy, shutdown is a no-op. - ''' - log.debug('esxcluster proxy shutdown() called...') + """ + log.debug("esxcluster proxy shutdown() called...") def find_credentials(): - ''' + """ Cycle through all the possible credentials and return the first one that works. - ''' + """ # if the username and password were already found don't fo though the # connection process again - if 'username' in DETAILS and 'password' in DETAILS: - return DETAILS['username'], DETAILS['password'] + if "username" in DETAILS and "password" in DETAILS: + return DETAILS["username"], DETAILS["password"] - passwords = DETAILS['passwords'] + passwords = DETAILS["passwords"] for password in passwords: - DETAILS['password'] = password - if not __salt__['vsphere.test_vcenter_connection'](): + DETAILS["password"] = password + if not __salt__["vsphere.test_vcenter_connection"](): # We are unable to authenticate continue # If we have data returned from above, we've successfully authenticated. - return DETAILS['username'], password + return DETAILS["username"], password # We've reached the end of the list without successfully authenticating. - raise salt.exceptions.VMwareConnectionError('Cannot complete login due to ' - 'incorrect credentials.') + raise salt.exceptions.VMwareConnectionError( + "Cannot complete login due to " "incorrect credentials." + ) def get_details(): - ''' + """ Function that returns the cached details - ''' + """ return DETAILS diff --git a/salt/proxy/esxdatacenter.py b/salt/proxy/esxdatacenter.py index b360ce60f5c..1082a7eb7d4 100644 --- a/salt/proxy/esxdatacenter.py +++ b/salt/proxy/esxdatacenter.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Proxy Minion interface module for managing VMWare ESXi clusters. Dependencies @@ -61,7 +61,7 @@ The location of the VMware vCenter server (host of ip) where the datacenter should be managed. Required. mechanism -^^^^^^^^ +^^^^^^^^^ The mechanism used to connect to the vCenter server. Supported values are ``userpass`` and ``sspi``. Required. @@ -85,7 +85,7 @@ domain User domain. Required if mechanism is ``sspi``. principal -^^^^^^^^ +^^^^^^^^^ Kerberos principal. Rquired if mechanism is ``sspi``. protocol @@ -143,10 +143,11 @@ Associated states are documented in :mod:`salt.states.esxdatacenter </ref/states/all/salt.states.esxdatacenter>`. Look there to find an example structure for Pillar as well as an example ``.sls`` file for configuring an ESX datacenter from scratch. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -156,11 +157,12 @@ from salt.config.schemas.esxdatacenter import EsxdatacenterProxySchema from salt.utils.dictupdate import merge # This must be present or the Salt loader won't load this module. -__proxyenabled__ = ['esxdatacenter'] +__proxyenabled__ = ["esxdatacenter"] # External libraries try: import jsonschema + HAS_JSONSCHEMA = True except ImportError: HAS_JSONSCHEMA = False @@ -173,83 +175,89 @@ DETAILS = {} # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'esxdatacenter' +__virtualname__ = "esxdatacenter" def __virtual__(): - ''' + """ Only load if the vsphere execution module is available. - ''' + """ if HAS_JSONSCHEMA: return __virtualname__ - return False, 'The esxdatacenter proxy module did not load.' + return False, "The esxdatacenter proxy module did not load." def init(opts): - ''' + """ This function gets called when the proxy starts up. All login details are cached. - ''' - log.debug('Initting esxdatacenter proxy module in process %s', os.getpid()) - log.trace('Validating esxdatacenter proxy input') + """ + log.debug("Initting esxdatacenter proxy module in process %s", os.getpid()) + log.trace("Validating esxdatacenter proxy input") schema = EsxdatacenterProxySchema.serialize() - log.trace('schema = %s', schema) - proxy_conf = merge(opts.get('proxy', {}), __pillar__.get('proxy', {})) - log.trace('proxy_conf = %s', proxy_conf) + log.trace("schema = %s", schema) + proxy_conf = merge(opts.get("proxy", {}), __pillar__.get("proxy", {})) + log.trace("proxy_conf = %s", proxy_conf) try: jsonschema.validate(proxy_conf, schema) except jsonschema.exceptions.ValidationError as exc: raise salt.exceptions.InvalidConfigError(exc) # Save mandatory fields in cache - for key in ('vcenter', 'datacenter', 'mechanism'): + for key in ("vcenter", "datacenter", "mechanism"): DETAILS[key] = proxy_conf[key] # Additional validation - if DETAILS['mechanism'] == 'userpass': - if 'username' not in proxy_conf: + if DETAILS["mechanism"] == "userpass": + if "username" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'userpass\', but no ' - '\'username\' key found in proxy config.') - if 'passwords' not in proxy_conf: + "Mechanism is set to 'userpass', but no " + "'username' key found in proxy config." + ) + if "passwords" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'userpass\', but no ' - '\'passwords\' key found in proxy config.') - for key in ('username', 'passwords'): + "Mechanism is set to 'userpass', but no " + "'passwords' key found in proxy config." + ) + for key in ("username", "passwords"): DETAILS[key] = proxy_conf[key] else: - if 'domain' not in proxy_conf: + if "domain" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'sspi\', but no ' - '\'domain\' key found in proxy config.') - if 'principal' not in proxy_conf: + "Mechanism is set to 'sspi', but no " + "'domain' key found in proxy config." + ) + if "principal" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'sspi\', but no ' - '\'principal\' key found in proxy config.') - for key in ('domain', 'principal'): + "Mechanism is set to 'sspi', but no " + "'principal' key found in proxy config." + ) + for key in ("domain", "principal"): DETAILS[key] = proxy_conf[key] # Save optional - DETAILS['protocol'] = proxy_conf.get('protocol') - DETAILS['port'] = proxy_conf.get('port') + DETAILS["protocol"] = proxy_conf.get("protocol") + DETAILS["port"] = proxy_conf.get("port") # Test connection - if DETAILS['mechanism'] == 'userpass': + if DETAILS["mechanism"] == "userpass": # Get the correct login details - log.debug('Retrieving credentials and testing vCenter connection for ' - 'mehchanism \'userpass\'') + log.debug( + "Retrieving credentials and testing vCenter connection for " + "mehchanism 'userpass'" + ) try: username, password = find_credentials() - DETAILS['password'] = password + DETAILS["password"] = password except salt.exceptions.SaltSystemExit as err: - log.critical('Error: %s', err) + log.critical("Error: %s", err) return False return True def ping(): - ''' + """ Returns True. CLI Example: @@ -257,44 +265,45 @@ def ping(): .. code-block:: bash salt dc_id test.ping - ''' + """ return True def shutdown(): - ''' + """ Shutdown the connection to the proxy device. For this proxy, shutdown is a no-op. - ''' - log.debug('esxdatacenter proxy shutdown() called...') + """ + log.debug("esxdatacenter proxy shutdown() called...") def find_credentials(): - ''' + """ Cycle through all the possible credentials and return the first one that works. - ''' + """ # if the username and password were already found don't fo though the # connection process again - if 'username' in DETAILS and 'password' in DETAILS: - return DETAILS['username'], DETAILS['password'] + if "username" in DETAILS and "password" in DETAILS: + return DETAILS["username"], DETAILS["password"] - passwords = DETAILS['passwords'] + passwords = DETAILS["passwords"] for password in passwords: - DETAILS['password'] = password - if not __salt__['vsphere.test_vcenter_connection'](): + DETAILS["password"] = password + if not __salt__["vsphere.test_vcenter_connection"](): # We are unable to authenticate continue # If we have data returned from above, we've successfully authenticated. - return DETAILS['username'], password + return DETAILS["username"], password # We've reached the end of the list without successfully authenticating. - raise salt.exceptions.VMwareConnectionError('Cannot complete login due to ' - 'incorrect credentials.') + raise salt.exceptions.VMwareConnectionError( + "Cannot complete login due to " "incorrect credentials." + ) def get_details(): - ''' + """ Function that returns the cached details - ''' + """ return DETAILS diff --git a/salt/proxy/esxi.py b/salt/proxy/esxi.py index bf2ecf43d6c..a9d6ac70401 100644 --- a/salt/proxy/esxi.py +++ b/salt/proxy/esxi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Proxy Minion interface module for managing VMware ESXi hosts. .. versionadded:: 2015.8.4 @@ -268,24 +268,27 @@ Associated states are thoroughly documented in to find an example structure for Pillar as well as an example ``.sls`` file for standing up an ESXi host from scratch. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os -# Import Salt Libs -from salt.exceptions import SaltSystemExit, InvalidConfigError from salt.config.schemas.esxi import EsxiProxySchema + +# Import Salt Libs +from salt.exceptions import InvalidConfigError, SaltSystemExit from salt.utils.dictupdate import merge # This must be present or the Salt loader won't load this module. -__proxyenabled__ = ['esxi'] +__proxyenabled__ = ["esxi"] # External libraries try: import jsonschema + HAS_JSONSCHEMA = True except ImportError: HAS_JSONSCHEMA = False @@ -298,145 +301,150 @@ DETAILS = {} # Set up logging log = logging.getLogger(__file__) # Define the module's virtual name -__virtualname__ = 'esxi' +__virtualname__ = "esxi" def __virtual__(): - ''' + """ Only load if the ESXi execution module is available. - ''' + """ if HAS_JSONSCHEMA: return __virtualname__ - return False, 'The ESXi Proxy Minion module did not load.' + return False, "The ESXi Proxy Minion module did not load." def init(opts): - ''' + """ This function gets called when the proxy starts up. For ESXi devices, the host, login credentials, and, if configured, the protocol and port are cached. - ''' - log.debug('Initting esxi proxy module in process %s', os.getpid()) - log.debug('Validating esxi proxy input') + """ + log.debug("Initting esxi proxy module in process %s", os.getpid()) + log.debug("Validating esxi proxy input") schema = EsxiProxySchema.serialize() - log.trace('esxi_proxy_schema = %s', schema) - proxy_conf = merge(opts.get('proxy', {}), __pillar__.get('proxy', {})) - log.trace('proxy_conf = %s', proxy_conf) + log.trace("esxi_proxy_schema = %s", schema) + proxy_conf = merge(opts.get("proxy", {}), __pillar__.get("proxy", {})) + log.trace("proxy_conf = %s", proxy_conf) try: jsonschema.validate(proxy_conf, schema) except jsonschema.exceptions.ValidationError as exc: raise InvalidConfigError(exc) - DETAILS['proxytype'] = proxy_conf['proxytype'] - if ('host' not in proxy_conf) and ('vcenter' not in proxy_conf): - log.critical('Neither \'host\' nor \'vcenter\' keys found in pillar ' - 'for this proxy.') + DETAILS["proxytype"] = proxy_conf["proxytype"] + if ("host" not in proxy_conf) and ("vcenter" not in proxy_conf): + log.critical( + "Neither 'host' nor 'vcenter' keys found in pillar " "for this proxy." + ) return False - if 'host' in proxy_conf: + if "host" in proxy_conf: # We have started the proxy by connecting directly to the host - if 'username' not in proxy_conf: - log.critical('No \'username\' key found in pillar for this proxy.') + if "username" not in proxy_conf: + log.critical("No 'username' key found in pillar for this proxy.") return False - if 'passwords' not in proxy_conf: - log.critical('No \'passwords\' key found in pillar for this proxy.') + if "passwords" not in proxy_conf: + log.critical("No 'passwords' key found in pillar for this proxy.") return False - host = proxy_conf['host'] + host = proxy_conf["host"] # Get the correct login details try: username, password = find_credentials(host) except SaltSystemExit as err: - log.critical('Error: %s', err) + log.critical("Error: %s", err) return False # Set configuration details - DETAILS['host'] = host - DETAILS['username'] = username - DETAILS['password'] = password - DETAILS['protocol'] = proxy_conf.get('protocol') - DETAILS['port'] = proxy_conf.get('port') + DETAILS["host"] = host + DETAILS["username"] = username + DETAILS["password"] = password + DETAILS["protocol"] = proxy_conf.get("protocol") + DETAILS["port"] = proxy_conf.get("port") return True - if 'vcenter' in proxy_conf: - vcenter = proxy_conf['vcenter'] - if not proxy_conf.get('esxi_host'): - log.critical('No \'esxi_host\' key found in pillar for this proxy.') - DETAILS['esxi_host'] = proxy_conf['esxi_host'] + if "vcenter" in proxy_conf: + vcenter = proxy_conf["vcenter"] + if not proxy_conf.get("esxi_host"): + log.critical("No 'esxi_host' key found in pillar for this proxy.") + DETAILS["esxi_host"] = proxy_conf["esxi_host"] # We have started the proxy by connecting via the vCenter - if 'mechanism' not in proxy_conf: - log.critical('No \'mechanism\' key found in pillar for this proxy.') + if "mechanism" not in proxy_conf: + log.critical("No 'mechanism' key found in pillar for this proxy.") return False - mechanism = proxy_conf['mechanism'] + mechanism = proxy_conf["mechanism"] # Save mandatory fields in cache - for key in ('vcenter', 'mechanism'): + for key in ("vcenter", "mechanism"): DETAILS[key] = proxy_conf[key] - if mechanism == 'userpass': - if 'username' not in proxy_conf: - log.critical('No \'username\' key found in pillar for this ' - 'proxy.') + if mechanism == "userpass": + if "username" not in proxy_conf: + log.critical("No 'username' key found in pillar for this " "proxy.") return False - if 'passwords' not in proxy_conf and \ - len(proxy_conf['passwords']) > 0: + if "passwords" not in proxy_conf and len(proxy_conf["passwords"]) > 0: - log.critical('Mechanism is set to \'userpass\' , but no ' - '\'passwords\' key found in pillar for this ' - 'proxy.') + log.critical( + "Mechanism is set to 'userpass' , but no " + "'passwords' key found in pillar for this " + "proxy." + ) return False - for key in ('username', 'passwords'): + for key in ("username", "passwords"): DETAILS[key] = proxy_conf[key] - elif mechanism == 'sspi': - if 'domain' not in proxy_conf: - log.critical('Mechanism is set to \'sspi\' , but no ' - '\'domain\' key found in pillar for this proxy.') + elif mechanism == "sspi": + if "domain" not in proxy_conf: + log.critical( + "Mechanism is set to 'sspi' , but no " + "'domain' key found in pillar for this proxy." + ) return False - if 'principal' not in proxy_conf: - log.critical('Mechanism is set to \'sspi\' , but no ' - '\'principal\' key found in pillar for this ' - 'proxy.') + if "principal" not in proxy_conf: + log.critical( + "Mechanism is set to 'sspi' , but no " + "'principal' key found in pillar for this " + "proxy." + ) return False - for key in ('domain', 'principal'): + for key in ("domain", "principal"): DETAILS[key] = proxy_conf[key] - if mechanism == 'userpass': + if mechanism == "userpass": # Get the correct login details - log.debug('Retrieving credentials and testing vCenter connection' - ' for mehchanism \'userpass\'') + log.debug( + "Retrieving credentials and testing vCenter connection" + " for mehchanism 'userpass'" + ) try: - username, password = find_credentials(DETAILS['vcenter']) - DETAILS['password'] = password + username, password = find_credentials(DETAILS["vcenter"]) + DETAILS["password"] = password except SaltSystemExit as err: - log.critical('Error: %s', err) + log.critical("Error: %s", err) return False # Save optional - DETAILS['protocol'] = proxy_conf.get('protocol', 'https') - DETAILS['port'] = proxy_conf.get('port', '443') - DETAILS['credstore'] = proxy_conf.get('credstore') + DETAILS["protocol"] = proxy_conf.get("protocol", "https") + DETAILS["port"] = proxy_conf.get("port", "443") + DETAILS["credstore"] = proxy_conf.get("credstore") def grains(): - ''' + """ Get the grains from the proxy device. - ''' + """ if not GRAINS_CACHE: - return _grains(DETAILS['host'], - DETAILS['protocol'], - DETAILS['port']) + return _grains(DETAILS["host"], DETAILS["protocol"], DETAILS["port"]) return GRAINS_CACHE def grains_refresh(): - ''' + """ Refresh the grains from the proxy device. - ''' + """ GRAINS_CACHE = {} return grains() def ping(): - ''' + """ Returns True if connection is to be done via a vCenter (no connection is attempted). Check to see if the host is responding when connecting directly via an ESXi host. @@ -446,17 +454,19 @@ def ping(): .. code-block:: bash salt esxi-host test.ping - ''' - if DETAILS.get('esxi_host'): + """ + if DETAILS.get("esxi_host"): return True else: # TODO Check connection if mechanism is SSPI - if DETAILS['mechanism'] == 'userpass': - find_credentials(DETAILS['host']) + if DETAILS["mechanism"] == "userpass": + find_credentials(DETAILS["host"]) try: - __salt__['vsphere.system_info'](host=DETAILS['host'], - username=DETAILS['username'], - password=DETAILS['password']) + __salt__["vsphere.system_info"]( + host=DETAILS["host"], + username=DETAILS["username"], + password=DETAILS["password"], + ) except SaltSystemExit as err: log.warning(err) return False @@ -464,15 +474,15 @@ def ping(): def shutdown(): - ''' + """ Shutdown the connection to the proxy device. For this proxy, shutdown is a no-op. - ''' - log.debug('ESXi proxy shutdown() called...') + """ + log.debug("ESXi proxy shutdown() called...") def ch_config(cmd, *args, **kwargs): - ''' + """ This function is called by the :mod:`salt.modules.esxi.cmd <salt.modules.esxi.cmd>` shim. It then calls whatever is passed in ``cmd`` inside the @@ -488,71 +498,71 @@ def ch_config(cmd, *args, **kwargs): kwargs Keyword arguments that need to be passed to that command. - ''' + """ # Strip the __pub_ keys...is there a better way to do this? for k in kwargs: - if k.startswith('__pub_'): + if k.startswith("__pub_"): kwargs.pop(k) - kwargs['host'] = DETAILS['host'] - kwargs['username'] = DETAILS['username'] - kwargs['password'] = DETAILS['password'] - kwargs['port'] = DETAILS['port'] - kwargs['protocol'] = DETAILS['protocol'] - kwargs['credstore'] = DETAILS['credstore'] + kwargs["host"] = DETAILS["host"] + kwargs["username"] = DETAILS["username"] + kwargs["password"] = DETAILS["password"] + kwargs["port"] = DETAILS["port"] + kwargs["protocol"] = DETAILS["protocol"] + kwargs["credstore"] = DETAILS["credstore"] - if 'vsphere.' + cmd not in __salt__: - return {'retcode': -1, 'message': 'vsphere.' + cmd + ' is not available.'} + if "vsphere." + cmd not in __salt__: + return {"retcode": -1, "message": "vsphere." + cmd + " is not available."} else: - return __salt__['vsphere.' + cmd](*args, **kwargs) + return __salt__["vsphere." + cmd](*args, **kwargs) def find_credentials(host): - ''' + """ Cycle through all the possible credentials and return the first one that works. - ''' - user_names = [__pillar__['proxy'].get('username', 'root')] - passwords = __pillar__['proxy']['passwords'] + """ + user_names = [__pillar__["proxy"].get("username", "root")] + passwords = __pillar__["proxy"]["passwords"] for user in user_names: for password in passwords: try: # Try to authenticate with the given user/password combination - ret = __salt__['vsphere.system_info'](host=host, - username=user, - password=password) + ret = __salt__["vsphere.system_info"]( + host=host, username=user, password=password + ) except SaltSystemExit: # If we can't authenticate, continue on to try the next password. continue # If we have data returned from above, we've successfully authenticated. if ret: - DETAILS['username'] = user - DETAILS['password'] = password + DETAILS["username"] = user + DETAILS["password"] = password return user, password # We've reached the end of the list without successfully authenticating. - raise SaltSystemExit('Cannot complete login due to an incorrect user name or password.') + raise SaltSystemExit( + "Cannot complete login due to an incorrect user name or password." + ) def _grains(host, protocol=None, port=None): - ''' + """ Helper function to the grains from the proxied device. - ''' - username, password = find_credentials(DETAILS['host']) - ret = __salt__['vsphere.system_info'](host=host, - username=username, - password=password, - protocol=protocol, - port=port) + """ + username, password = find_credentials(DETAILS["host"]) + ret = __salt__["vsphere.system_info"]( + host=host, username=username, password=password, protocol=protocol, port=port + ) GRAINS_CACHE.update(ret) return GRAINS_CACHE def is_connected_via_vcenter(): - return True if 'vcenter' in DETAILS else False + return True if "vcenter" in DETAILS else False def get_details(): - ''' + """ Return the proxy details - ''' + """ return DETAILS diff --git a/salt/proxy/esxvm.py b/salt/proxy/esxvm.py index cbc8ca5f6b6..f21aaa134f5 100644 --- a/salt/proxy/esxvm.py +++ b/salt/proxy/esxvm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Proxy Minion interface module for managing VMWare ESXi virtual machines. Dependencies @@ -85,7 +85,7 @@ domain User realm domain. Required if mechanism is ``sspi``. principal -^^^^^^^^ +^^^^^^^^^ Kerberos principal. Rquired if mechanism is ``sspi``. protocol @@ -143,10 +143,11 @@ Associated states are documented in :mod:`salt.states.esxvm </ref/states/all/salt.states.esxvm>`. Look there to find an example structure for Pillar as well as an example ``.sls`` file for configuring an ESX virtual machine from scratch. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -155,7 +156,7 @@ import salt.exceptions as excs from salt.utils.dictupdate import merge # This must be present or the Salt loader won't load this module. -__proxyenabled__ = ['esxvm'] +__proxyenabled__ = ["esxvm"] # Variables are scoped to this module so we can have persistent data @@ -167,75 +168,81 @@ DETAILS = {} # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'esxvm' +__virtualname__ = "esxvm" def __virtual__(): - ''' + """ Only load if the vsphere execution module is available. - ''' + """ return __virtualname__ def init(opts): - ''' + """ This function gets called when the proxy starts up. For login the protocol and port are cached. - ''' - log.debug('Initting esxvm proxy module in process %s', os.getpid()) - log.debug('Validating esxvm proxy input') - proxy_conf = merge(opts.get('proxy', {}), __pillar__.get('proxy', {})) - log.trace('proxy_conf = %s', proxy_conf) + """ + log.debug("Initting esxvm proxy module in process %s", os.getpid()) + log.debug("Validating esxvm proxy input") + proxy_conf = merge(opts.get("proxy", {}), __pillar__.get("proxy", {})) + log.trace("proxy_conf = %s", proxy_conf) # TODO json schema validation # Save mandatory fields in cache - for key in ('vcenter', 'datacenter', 'mechanism'): + for key in ("vcenter", "datacenter", "mechanism"): DETAILS[key] = proxy_conf[key] # Additional validation - if DETAILS['mechanism'] == 'userpass': - if 'username' not in proxy_conf: + if DETAILS["mechanism"] == "userpass": + if "username" not in proxy_conf: raise excs.InvalidProxyInputError( - 'Mechanism is set to \'userpass\' , but no ' - '\'username\' key found in pillar for this proxy.') - if 'passwords' not in proxy_conf: + "Mechanism is set to 'userpass' , but no " + "'username' key found in pillar for this proxy." + ) + if "passwords" not in proxy_conf: raise excs.InvalidProxyInputError( - 'Mechanism is set to \'userpass\' , but no ' - '\'passwords\' key found in pillar for this proxy.') - for key in ('username', 'passwords'): + "Mechanism is set to 'userpass' , but no " + "'passwords' key found in pillar for this proxy." + ) + for key in ("username", "passwords"): DETAILS[key] = proxy_conf[key] else: - if 'domain' not in proxy_conf: + if "domain" not in proxy_conf: raise excs.InvalidProxyInputError( - 'Mechanism is set to \'sspi\' , but no ' - '\'domain\' key found in pillar for this proxy.') - if 'principal' not in proxy_conf: + "Mechanism is set to 'sspi' , but no " + "'domain' key found in pillar for this proxy." + ) + if "principal" not in proxy_conf: raise excs.InvalidProxyInputError( - 'Mechanism is set to \'sspi\' , but no ' - '\'principal\' key found in pillar for this proxy.') - for key in ('domain', 'principal'): + "Mechanism is set to 'sspi' , but no " + "'principal' key found in pillar for this proxy." + ) + for key in ("domain", "principal"): DETAILS[key] = proxy_conf[key] # Save optional - DETAILS['protocol'] = proxy_conf.get('protocol') - DETAILS['port'] = proxy_conf.get('port') + DETAILS["protocol"] = proxy_conf.get("protocol") + DETAILS["port"] = proxy_conf.get("port") # Test connection - if DETAILS['mechanism'] == 'userpass': + if DETAILS["mechanism"] == "userpass": # Get the correct login details - log.debug('Retrieving credentials and testing vCenter connection for ' - 'mehchanism \'userpass\'') + log.debug( + "Retrieving credentials and testing vCenter connection for " + "mehchanism 'userpass'" + ) try: username, password = find_credentials() - DETAILS['password'] = password + DETAILS["password"] = password except excs.SaltSystemExit as err: - log.critical('Error: %s', err) + log.critical("Error: %s", err) return False return True def ping(): - ''' + """ Returns True. CLI Example: @@ -243,44 +250,45 @@ def ping(): .. code-block:: bash salt esx-vm test.ping - ''' + """ return True def shutdown(): - ''' + """ Shutdown the connection to the proxy device. For this proxy, shutdown is a no-op. - ''' - log.debug('ESX vm proxy shutdown() called...') + """ + log.debug("ESX vm proxy shutdown() called...") def find_credentials(): - ''' + """ Cycle through all the possible credentials and return the first one that works. - ''' + """ # if the username and password were already found don't go through the # connection process again - if 'username' in DETAILS and 'password' in DETAILS: - return DETAILS['username'], DETAILS['password'] + if "username" in DETAILS and "password" in DETAILS: + return DETAILS["username"], DETAILS["password"] - passwords = __pillar__['proxy']['passwords'] + passwords = __pillar__["proxy"]["passwords"] for password in passwords: - DETAILS['password'] = password - if not __salt__['vsphere.test_vcenter_connection'](): + DETAILS["password"] = password + if not __salt__["vsphere.test_vcenter_connection"](): # We are unable to authenticate continue # If we have data returned from above, we've successfully authenticated. - return DETAILS['username'], password + return DETAILS["username"], password # We've reached the end of the list without successfully authenticating. - raise excs.VMwareConnectionError('Cannot complete login due to ' - 'incorrect credentials.') + raise excs.VMwareConnectionError( + "Cannot complete login due to " "incorrect credentials." + ) def get_details(): - ''' + """ Function that returns the cached details - ''' + """ return DETAILS diff --git a/salt/proxy/fx2.py b/salt/proxy/fx2.py index f6d96f6d3f0..bbdab761407 100644 --- a/salt/proxy/fx2.py +++ b/salt/proxy/fx2.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*-, -''' +""" Dell FX2 chassis ================ @@ -170,16 +170,17 @@ Associated states are thoroughly documented in :mod:`salt.states.dellchassis <sa Look there to find an example structure for pillar as well as an example ``.sls`` file for standing up a Dell Chassis from scratch. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging + import salt.utils.http import salt.utils.path # This must be present or the Salt loader won't load this module -__proxyenabled__ = ['fx2'] +__proxyenabled__ = ["fx2"] # Variables are scoped to this module so we can have persistent data @@ -192,16 +193,16 @@ log = logging.getLogger(__file__) def __virtual__(): - ''' + """ Only return if all the modules are available - ''' - if not salt.utils.path.which('racadm'): + """ + if not salt.utils.path.which("racadm"): return False, 'fx2 proxy minion needs "racadm" to be installed.' return True def init(opts): - ''' + """ This function gets called when the proxy starts up. We check opts to see if a fallback user and password are supplied. If they are present, and the primary credentials don't work, then @@ -210,48 +211,48 @@ def init(opts): Whichever set of credentials works is placed in the persistent DETAILS dictionary and will be used for further communication with the chassis. - ''' - if 'host' not in opts['proxy']: + """ + if "host" not in opts["proxy"]: log.critical('No "host" key found in pillar for this proxy') return False - DETAILS['host'] = opts['proxy']['host'] + DETAILS["host"] = opts["proxy"]["host"] (username, password) = find_credentials() def admin_username(): - ''' + """ Return the admin_username in the DETAILS dictionary, or root if there is none present - ''' - return DETAILS.get('admin_username', 'root') + """ + return DETAILS.get("admin_username", "root") def admin_password(): - ''' + """ Return the admin_password in the DETAILS dictionary, or 'calvin' (the Dell default) if there is none present - ''' - if 'admin_password' not in DETAILS: - log.info('proxy.fx2: No admin_password in DETAILS, returning Dell default') - return 'calvin' + """ + if "admin_password" not in DETAILS: + log.info("proxy.fx2: No admin_password in DETAILS, returning Dell default") + return "calvin" - return DETAILS.get('admin_password', 'calvin') + return DETAILS.get("admin_password", "calvin") def host(): - return DETAILS['host'] + return DETAILS["host"] def _grains(host, user, password): - ''' + """ Get the grains from the proxied device - ''' - r = __salt__['dracr.system_info'](host=host, - admin_username=user, - admin_password=password) - if r.get('retcode', 0) == 0: + """ + r = __salt__["dracr.system_info"]( + host=host, admin_username=user, admin_password=password + ) + if r.get("retcode", 0) == 0: GRAINS_CACHE = r else: GRAINS_CACHE = {} @@ -259,62 +260,66 @@ def _grains(host, user, password): def grains(): - ''' + """ Get the grains from the proxied device - ''' + """ if not GRAINS_CACHE: - return _grains(DETAILS['host'], - DETAILS['admin_username'], - DETAILS['admin_password']) + return _grains( + DETAILS["host"], DETAILS["admin_username"], DETAILS["admin_password"] + ) return GRAINS_CACHE def grains_refresh(): - ''' + """ Refresh the grains from the proxied device - ''' + """ GRAINS_CACHE = {} return grains() def find_credentials(): - ''' + """ Cycle through all the possible credentials and return the first one that works - ''' - usernames = [__pillar__['proxy'].get('admin_username', 'root')] - if 'fallback_admin_username' in __pillar__.get('proxy'): - usernames.append(__pillar__['proxy'].get('fallback_admin_username')) + """ + usernames = [__pillar__["proxy"].get("admin_username", "root")] + if "fallback_admin_username" in __pillar__.get("proxy"): + usernames.append(__pillar__["proxy"].get("fallback_admin_username")) for user in usernames: - for pwd in __pillar__['proxy']['passwords']: - r = __salt__['dracr.get_chassis_name'](host=__pillar__['proxy']['host'], - admin_username=user, - admin_password=pwd) + for pwd in __pillar__["proxy"]["passwords"]: + r = __salt__["dracr.get_chassis_name"]( + host=__pillar__["proxy"]["host"], + admin_username=user, + admin_password=pwd, + ) # Retcode will be present if the chassis_name call failed try: - if r.get('retcode', None) is None: - DETAILS['admin_username'] = user - DETAILS['admin_password'] = pwd - __opts__['proxy']['admin_username'] = user - __opts__['proxy']['admin_password'] = pwd + if r.get("retcode", None) is None: + DETAILS["admin_username"] = user + DETAILS["admin_password"] = pwd + __opts__["proxy"]["admin_username"] = user + __opts__["proxy"]["admin_password"] = pwd return (user, pwd) except AttributeError: # Then the above was a string, and we can return the username # and password - DETAILS['admin_username'] = user - DETAILS['admin_password'] = pwd - __opts__['proxy']['admin_username'] = user - __opts__['proxy']['admin_password'] = pwd + DETAILS["admin_username"] = user + DETAILS["admin_password"] = pwd + __opts__["proxy"]["admin_username"] = user + __opts__["proxy"]["admin_password"] = pwd return (user, pwd) - log.debug('proxy fx2.find_credentials found no valid credentials, using Dell default') - return ('root', 'calvin') + log.debug( + "proxy fx2.find_credentials found no valid credentials, using Dell default" + ) + return ("root", "calvin") def chconfig(cmd, *args, **kwargs): - ''' + """ This function is called by the :mod:`salt.modules.chassis.cmd <salt.modules.chassis.cmd>` shim. It then calls whatever is passed in ``cmd`` inside the :mod:`salt.modules.dracr <salt.modules.dracr>` @@ -325,52 +330,54 @@ def chconfig(cmd, *args, **kwargs): :param kwargs: Keyword arguments that need to be passed to that command :return: Passthrough the return from the dracr module. - ''' + """ # Strip the __pub_ keys...is there a better way to do this? for k in list(kwargs): - if k.startswith('__pub_'): + if k.startswith("__pub_"): kwargs.pop(k) # Catch password reset - if 'dracr.'+cmd not in __salt__: - ret = {'retcode': -1, 'message': 'dracr.' + cmd + ' is not available'} + if "dracr." + cmd not in __salt__: + ret = {"retcode": -1, "message": "dracr." + cmd + " is not available"} else: - ret = __salt__['dracr.'+cmd](*args, **kwargs) + ret = __salt__["dracr." + cmd](*args, **kwargs) - if cmd == 'change_password': - if 'username' in kwargs: - __opts__['proxy']['admin_username'] = kwargs['username'] - DETAILS['admin_username'] = kwargs['username'] - if 'password' in kwargs: - __opts__['proxy']['admin_password'] = kwargs['password'] - DETAILS['admin_password'] = kwargs['password'] + if cmd == "change_password": + if "username" in kwargs: + __opts__["proxy"]["admin_username"] = kwargs["username"] + DETAILS["admin_username"] = kwargs["username"] + if "password" in kwargs: + __opts__["proxy"]["admin_password"] = kwargs["password"] + DETAILS["admin_password"] = kwargs["password"] return ret def ping(): - ''' + """ Is the chassis responding? :return: Returns False if the chassis didn't respond, True otherwise. - ''' - r = __salt__['dracr.system_info'](host=DETAILS['host'], - admin_username=DETAILS['admin_username'], - admin_password=DETAILS['admin_password']) - if r.get('retcode', 0) == 1: + """ + r = __salt__["dracr.system_info"]( + host=DETAILS["host"], + admin_username=DETAILS["admin_username"], + admin_password=DETAILS["admin_password"], + ) + if r.get("retcode", 0) == 1: return False else: return True try: - return r['dict'].get('ret', False) + return r["dict"].get("ret", False) except Exception: # pylint: disable=broad-except return False def shutdown(opts): - ''' + """ Shutdown the connection to the proxied device. For this proxy shutdown is a no-op. - ''' - log.debug('fx2 proxy shutdown() called...') + """ + log.debug("fx2 proxy shutdown() called...") diff --git a/salt/proxy/junos.py b/salt/proxy/junos.py index 956061b33a7..a68f03bfaea 100644 --- a/salt/proxy/junos.py +++ b/salt/proxy/junos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Interface with a Junos device via proxy-minion. To connect to a junos device \ via junos proxy, specify the host information in the pillar in '/srv/pillar/details.sls' @@ -34,7 +34,7 @@ Run the salt proxy via the following command: salt-proxy --proxyid=vmx -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging @@ -46,123 +46,140 @@ try: import jnpr.junos.utils import jnpr.junos.utils.config import jnpr.junos.utils.sw - from jnpr.junos.exception import RpcTimeoutError, ConnectClosedError,\ - RpcError, ConnectError, ProbeError, ConnectAuthError,\ - ConnectRefusedError, ConnectTimeoutError + from jnpr.junos.exception import ( + RpcTimeoutError, + ConnectClosedError, + RpcError, + ConnectError, + ProbeError, + ConnectAuthError, + ConnectRefusedError, + ConnectTimeoutError, + ) from ncclient.operations.errors import TimeoutExpiredError except ImportError: HAS_JUNOS = False -__proxyenabled__ = ['junos'] +__proxyenabled__ = ["junos"] thisproxy = {} log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'junos' +__virtualname__ = "junos" def __virtual__(): - ''' + """ Only return if all the modules are available - ''' + """ if not HAS_JUNOS: - return False, 'Missing dependency: The junos proxy minion requires the \'jnpr\' Python module.' + return ( + False, + "Missing dependency: The junos proxy minion requires the 'jnpr' Python module.", + ) return __virtualname__ def init(opts): - ''' + """ Open the connection to the Junos device, login, and bind to the Resource class - ''' - opts['multiprocessing'] = False - log.debug('Opening connection to junos') + """ + opts["multiprocessing"] = False + log.debug("Opening connection to junos") - args = {"host": opts['proxy']['host']} - optional_args = ['user', - 'username', - 'password', - 'passwd', - 'port', - 'gather_facts', - 'mode', - 'baud', - 'attempts', - 'auto_probe', - 'ssh_private_key_file', - 'ssh_config', - 'normalize' - ] + args = {"host": opts["proxy"]["host"]} + optional_args = [ + "user", + "username", + "password", + "passwd", + "port", + "gather_facts", + "mode", + "baud", + "attempts", + "auto_probe", + "ssh_private_key_file", + "ssh_config", + "normalize", + ] - if 'username' in opts['proxy'].keys(): - opts['proxy']['user'] = opts['proxy'].pop('username') - proxy_keys = opts['proxy'].keys() + if "username" in opts["proxy"].keys(): + opts["proxy"]["user"] = opts["proxy"].pop("username") + proxy_keys = opts["proxy"].keys() for arg in optional_args: if arg in proxy_keys: - args[arg] = opts['proxy'][arg] + args[arg] = opts["proxy"][arg] - thisproxy['conn'] = jnpr.junos.Device(**args) + thisproxy["conn"] = jnpr.junos.Device(**args) try: - thisproxy['conn'].open() - except (ProbeError, ConnectAuthError, ConnectRefusedError, ConnectTimeoutError, - ConnectError) as ex: + thisproxy["conn"].open() + except ( + ProbeError, + ConnectAuthError, + ConnectRefusedError, + ConnectTimeoutError, + ConnectError, + ) as ex: log.error("{} : not able to initiate connection to the device".format(str(ex))) - thisproxy['initialized'] = False + thisproxy["initialized"] = False return - if 'timeout' in proxy_keys: - timeout = int(opts['proxy']['timeout']) + if "timeout" in proxy_keys: + timeout = int(opts["proxy"]["timeout"]) try: - thisproxy['conn'].timeout = timeout + thisproxy["conn"].timeout = timeout except Exception as ex: # pylint: disable=broad-except - log.error('Not able to set timeout due to: %s', str(ex)) + log.error("Not able to set timeout due to: %s", str(ex)) else: - log.debug('RPC timeout set to %d seconds', timeout) + log.debug("RPC timeout set to %d seconds", timeout) try: - thisproxy['conn'].bind(cu=jnpr.junos.utils.config.Config) + thisproxy["conn"].bind(cu=jnpr.junos.utils.config.Config) except Exception as ex: # pylint: disable=broad-except - log.error('Bind failed with Config class due to: {}'.format(str(ex))) + log.error("Bind failed with Config class due to: {}".format(str(ex))) try: - thisproxy['conn'].bind(sw=jnpr.junos.utils.sw.SW) + thisproxy["conn"].bind(sw=jnpr.junos.utils.sw.SW) except Exception as ex: # pylint: disable=broad-except - log.error('Bind failed with SW class due to: {}'.format(str(ex))) - thisproxy['initialized'] = True + log.error("Bind failed with SW class due to: {}".format(str(ex))) + thisproxy["initialized"] = True def initialized(): - return thisproxy.get('initialized', False) + return thisproxy.get("initialized", False) def conn(): - return thisproxy['conn'] + return thisproxy["conn"] def alive(opts): - ''' + """ Validate and return the connection status with the remote device. .. versionadded:: 2018.3.0 - ''' + """ dev = conn() - thisproxy['conn'].connected = ping() + thisproxy["conn"].connected = ping() if not dev.connected: - __salt__['event.fire_master']({}, 'junos/proxy/{}/stop'.format( - opts['proxy']['host'])) + __salt__["event.fire_master"]( + {}, "junos/proxy/{}/stop".format(opts["proxy"]["host"]) + ) return dev.connected def ping(): - ''' + """ Ping? Pong! - ''' + """ dev = conn() # Check that the underlying netconf connection still exists. @@ -171,15 +188,14 @@ def ping(): # call rpc only if ncclient queue is empty. If not empty that means other # rpc call is going on. - if hasattr(dev._conn, '_session'): + if hasattr(dev._conn, "_session"): if dev._conn._session._transport.is_active(): # there is no on going rpc call. buffer tell can be 1 as it stores # remaining char after "]]>]]>" which can be a new line char - if dev._conn._session._buffer.tell() <= 1 and \ - dev._conn._session._q.empty(): + if dev._conn._session._buffer.tell() <= 1 and dev._conn._session._q.empty(): return _rpc_file_list(dev) else: - log.debug('skipped ping() call as proxy already getting data') + log.debug("skipped ping() call as proxy already getting data") return True else: # ssh connection is lost @@ -191,7 +207,7 @@ def ping(): def _rpc_file_list(dev): try: - dev.rpc.file_list(path='/dev/null', dev_timeout=5) + dev.rpc.file_list(path="/dev/null", dev_timeout=5) return True except (RpcTimeoutError, ConnectClosedError): try: @@ -205,34 +221,32 @@ def _rpc_file_list(dev): def proxytype(): - ''' + """ Returns the name of this proxy - ''' - return 'junos' + """ + return "junos" def get_serialized_facts(): - facts = dict(thisproxy['conn'].facts) - if 'version_info' in facts: - facts['version_info'] = \ - dict(facts['version_info']) + facts = dict(thisproxy["conn"].facts) + if "version_info" in facts: + facts["version_info"] = dict(facts["version_info"]) # For backward compatibility. 'junos_info' is present # only of in newer versions of facts. - if 'junos_info' in facts: - for re in facts['junos_info']: - facts['junos_info'][re]['object'] = \ - dict(facts['junos_info'][re]['object']) + if "junos_info" in facts: + for re in facts["junos_info"]: + facts["junos_info"][re]["object"] = dict(facts["junos_info"][re]["object"]) return facts def shutdown(opts): - ''' + """ This is called when the proxy-minion is exiting to make sure the connection to the device is closed cleanly. - ''' - log.debug('Proxy module %s shutting down!!', opts['id']) + """ + log.debug("Proxy module %s shutting down!!", opts["id"]) try: - thisproxy['conn'].close() + thisproxy["conn"].close() except Exception: # pylint: disable=broad-except pass diff --git a/salt/proxy/marathon.py b/salt/proxy/marathon.py index e1ad76a516e..b5d83a3a234 100644 --- a/salt/proxy/marathon.py +++ b/salt/proxy/marathon.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Marathon ======== @@ -23,16 +23,16 @@ the marathon endpoint: base_url: http://my-marathon-master.mydomain.com:8080 .. versionadded:: 2015.8.2 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + import salt.utils.http - -__proxyenabled__ = ['marathon'] +__proxyenabled__ = ["marathon"] CONFIG = {} -CONFIG_BASE_URL = 'base_url' +CONFIG_BASE_URL = "base_url" log = logging.getLogger(__file__) @@ -41,35 +41,34 @@ def __virtual__(): def init(opts): - ''' + """ Perform any needed setup. - ''' - if CONFIG_BASE_URL in opts['proxy']: - CONFIG[CONFIG_BASE_URL] = opts['proxy'][CONFIG_BASE_URL] + """ + if CONFIG_BASE_URL in opts["proxy"]: + CONFIG[CONFIG_BASE_URL] = opts["proxy"][CONFIG_BASE_URL] else: - log.error('missing proxy property %s', CONFIG_BASE_URL) - log.debug('CONFIG: %s', CONFIG) + log.error("missing proxy property %s", CONFIG_BASE_URL) + log.debug("CONFIG: %s", CONFIG) def ping(): - ''' + """ Is the marathon api responding? - ''' + """ try: response = salt.utils.http.query( "{0}/ping".format(CONFIG[CONFIG_BASE_URL]), - decode_type='plain', + decode_type="plain", decode=True, ) log.debug( - 'marathon.info returned successfully: %s', - response, + "marathon.info returned successfully: %s", response, ) - if 'text' in response and response['text'].strip() == 'pong': + if "text" in response and response["text"].strip() == "pong": return True except Exception as ex: # pylint: disable=broad-except log.error( - 'error calling marathon.info with base_url %s: %s', + "error calling marathon.info with base_url %s: %s", CONFIG[CONFIG_BASE_URL], ex, ) @@ -77,7 +76,7 @@ def ping(): def shutdown(opts): - ''' + """ For this proxy shutdown is a no-op - ''' - log.debug('marathon proxy shutdown() called...') + """ + log.debug("marathon proxy shutdown() called...") diff --git a/salt/proxy/napalm.py b/salt/proxy/napalm.py index 45a5284ff29..ef90c62d9af 100644 --- a/salt/proxy/napalm.py +++ b/salt/proxy/napalm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM: Network Automation and Programmability Abstraction Layer with Multivendor support ========================================================================================= @@ -156,23 +156,26 @@ Example using a user-specific library, extending NAPALM's capabilities, e.g. ``c For example, if the usual command is ``salt '*' net.arp``, you can use the following to connect using a different username instead: ``salt '*' net.arp username=my-alt-usr force_reconnect=True``. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python lib import logging -log = logging.getLogger(__file__) + +import salt.utils.napalm # Import Salt modules from salt.ext import six -import salt.utils.napalm + +log = logging.getLogger(__file__) + # ---------------------------------------------------------------------------------------------------------------------- # proxy properties # ---------------------------------------------------------------------------------------------------------------------- -__proxyenabled__ = ['napalm'] +__proxyenabled__ = ["napalm"] # proxy name # ---------------------------------------------------------------------------------------------------------------------- @@ -188,7 +191,8 @@ DETAILS = {} def __virtual__(): - return salt.utils.napalm.virtual(__opts__, 'napalm', __file__) + return salt.utils.napalm.virtual(__opts__, "napalm", __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported @@ -200,28 +204,29 @@ def __virtual__(): def init(opts): - ''' + """ Opens the connection with the network device. - ''' + """ NETWORK_DEVICE.update(salt.utils.napalm.get_device(opts)) - DETAILS['initialized'] = True + DETAILS["initialized"] = True return True def alive(opts): - ''' + """ Return the connection status with the remote device. .. versionadded:: 2017.7.0 - ''' + """ if salt.utils.napalm.not_always_alive(opts): return True # don't force reconnection for not-always alive proxies # or regular minion - is_alive_ret = call('is_alive', **{}) - if not is_alive_ret.get('result', False): + is_alive_ret = call("is_alive", **{}) + if not is_alive_ret.get("result", False): log.debug( - '[%s] Unable to execute `is_alive`: %s', - opts.get('id'), is_alive_ret.get('comment') + "[%s] Unable to execute `is_alive`: %s", + opts.get("id"), + is_alive_ret.get("comment"), ) # if `is_alive` is not implemented by the underneath driver, # will consider the connection to be still alive @@ -229,82 +234,81 @@ def alive(opts): # NOTE: revisit this if IOS is still not stable # and return False to force reconnection return True - flag = is_alive_ret.get('out', {}).get('is_alive', False) - log.debug('Is %s still alive? %s', opts.get('id'), 'Yes.' if flag else 'No.') + flag = is_alive_ret.get("out", {}).get("is_alive", False) + log.debug("Is %s still alive? %s", opts.get("id"), "Yes." if flag else "No.") return flag def ping(): - ''' + """ Connection open successfully? - ''' - return NETWORK_DEVICE.get('UP', False) + """ + return NETWORK_DEVICE.get("UP", False) def initialized(): - ''' + """ Connection finished initializing? - ''' - return DETAILS.get('initialized', False) + """ + return DETAILS.get("initialized", False) def get_device(): - ''' + """ Returns the network device object. - ''' + """ return NETWORK_DEVICE def get_grains(): - ''' + """ Retrieve facts from the network device. - ''' - return call('get_facts', **{}) + """ + return call("get_facts", **{}) def grains_refresh(): - ''' + """ Refresh the grains. - ''' - DETAILS['grains_cache'] = {} + """ + DETAILS["grains_cache"] = {} return get_grains() def fns(): - ''' + """ Method called by NAPALM grains module. - ''' - return { - 'details': 'Network device grains.' - } + """ + return {"details": "Network device grains."} def shutdown(opts): - ''' + """ Closes connection with the device. - ''' + """ try: - if not NETWORK_DEVICE.get('UP', False): - raise Exception('not connected!') - NETWORK_DEVICE.get('DRIVER').close() + if not NETWORK_DEVICE.get("UP", False): + raise Exception("not connected!") + NETWORK_DEVICE.get("DRIVER").close() except Exception as error: # pylint: disable=broad-except - port = NETWORK_DEVICE.get('OPTIONAL_ARGS', {}).get('port') + port = NETWORK_DEVICE.get("OPTIONAL_ARGS", {}).get("port") log.error( - 'Cannot close connection with %s%s! Please check error: %s', - NETWORK_DEVICE.get('HOSTNAME', '[unknown hostname]'), - ':{0}'.format(port) if port else '', - error + "Cannot close connection with %s%s! Please check error: %s", + NETWORK_DEVICE.get("HOSTNAME", "[unknown hostname]"), + ":{0}".format(port) if port else "", + error, ) return True + # ---------------------------------------------------------------------------------------------------------------------- # Callable functions # ---------------------------------------------------------------------------------------------------------------------- def call(method, *args, **kwargs): - ''' + """ Calls a specific method from the network driver instance. Please check the readthedocs_ page for the updated list of getters. @@ -335,7 +339,7 @@ def call(method, *args, **kwargs): 'show chassis fan' ] }) - ''' + """ kwargs_copy = {} kwargs_copy.update(kwargs) for karg, warg in six.iteritems(kwargs_copy): diff --git a/salt/proxy/netmiko_px.py b/salt/proxy/netmiko_px.py index 24ed33142b6..d58354adc5c 100644 --- a/salt/proxy/netmiko_px.py +++ b/salt/proxy/netmiko_px.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Netmiko ======= @@ -177,36 +177,38 @@ Proxy Pillar Example username: test use_keys: true secret: w3@k -''' +""" from __future__ import absolute_import # Import python stdlib import logging +# Import salt modules +from salt.utils.args import clean_kwargs + # Import third party libs try: from netmiko import ConnectHandler from netmiko.ssh_exception import NetMikoTimeoutException from netmiko.ssh_exception import NetMikoAuthenticationException + HAS_NETMIKO = True except ImportError: HAS_NETMIKO = False -# Import salt modules -from salt.utils.args import clean_kwargs # ----------------------------------------------------------------------------- # proxy properties # ----------------------------------------------------------------------------- -__proxyenabled__ = ['netmiko'] +__proxyenabled__ = ["netmiko"] # proxy name # ----------------------------------------------------------------------------- # globals # ----------------------------------------------------------------------------- -__virtualname__ = 'netmiko' +__virtualname__ = "netmiko" log = logging.getLogger(__name__) netmiko_device = {} @@ -216,75 +218,80 @@ netmiko_device = {} def __virtual__(): - ''' + """ Proxy module available only if Netmiko is installed. - ''' + """ if not HAS_NETMIKO: - return False, 'The netmiko proxy module requires netmiko library to be installed.' + return ( + False, + "The netmiko proxy module requires netmiko library to be installed.", + ) return __virtualname__ + # ----------------------------------------------------------------------------- # proxy functions # ----------------------------------------------------------------------------- def init(opts): - ''' + """ Open the connection to the network device managed through netmiko. - ''' - proxy_dict = opts.get('proxy', {}) - opts['multiprocessing'] = proxy_dict.get('multiprocessing', False) + """ + proxy_dict = opts.get("proxy", {}) + opts["multiprocessing"] = proxy_dict.get("multiprocessing", False) netmiko_connection_args = proxy_dict.copy() - netmiko_connection_args.pop('proxytype', None) - netmiko_device['always_alive'] = netmiko_connection_args.pop('always_alive', - opts.get('proxy_always_alive', True)) + netmiko_connection_args.pop("proxytype", None) + netmiko_device["always_alive"] = netmiko_connection_args.pop( + "always_alive", opts.get("proxy_always_alive", True) + ) try: connection = ConnectHandler(**netmiko_connection_args) - netmiko_device['connection'] = connection - netmiko_device['initialized'] = True - netmiko_device['args'] = netmiko_connection_args - netmiko_device['up'] = True - if not netmiko_device['always_alive']: - netmiko_device['connection'].disconnect() + netmiko_device["connection"] = connection + netmiko_device["initialized"] = True + netmiko_device["args"] = netmiko_connection_args + netmiko_device["up"] = True + if not netmiko_device["always_alive"]: + netmiko_device["connection"].disconnect() except NetMikoTimeoutException as t_err: - log.error('Unable to setup the netmiko connection', exc_info=True) + log.error("Unable to setup the netmiko connection", exc_info=True) except NetMikoAuthenticationException as au_err: - log.error('Unable to setup the netmiko connection', exc_info=True) + log.error("Unable to setup the netmiko connection", exc_info=True) return True def alive(opts): - ''' + """ Return the connection status with the network device. - ''' - log.debug('Checking if %s is still alive', opts.get('id', '')) - if not netmiko_device['always_alive']: + """ + log.debug("Checking if %s is still alive", opts.get("id", "")) + if not netmiko_device["always_alive"]: return True if ping() and initialized(): - return netmiko_device['connection'].remote_conn.transport.is_alive() + return netmiko_device["connection"].remote_conn.transport.is_alive() return False def ping(): - ''' + """ Connection open successfully? - ''' - return netmiko_device.get('up', False) + """ + return netmiko_device.get("up", False) def initialized(): - ''' + """ Connection finished initializing? - ''' - return netmiko_device.get('initialized', False) + """ + return netmiko_device.get("initialized", False) def shutdown(opts): - ''' + """ Closes connection with the device. - ''' - return call('disconnect') + """ + return call("disconnect") # ----------------------------------------------------------------------------- @@ -293,27 +300,27 @@ def shutdown(opts): def conn(): - ''' + """ Return the connection object. - ''' - return netmiko_device.get('connection') + """ + return netmiko_device.get("connection") def args(): - ''' + """ Return the Netmiko device args. - ''' - return netmiko_device['args'] + """ + return netmiko_device["args"] def call(method, *args, **kwargs): - ''' + """ Calls an arbitrary netmiko method. - ''' + """ kwargs = clean_kwargs(**kwargs) - if not netmiko_device['always_alive']: - connection = ConnectHandler(**netmiko_device['args']) + if not netmiko_device["always_alive"]: + connection = ConnectHandler(**netmiko_device["args"]) ret = getattr(connection, method)(*args, **kwargs) connection.disconnect() return ret - return getattr(netmiko_device['connection'], method)(*args, **kwargs) + return getattr(netmiko_device["connection"], method)(*args, **kwargs) diff --git a/salt/proxy/nxos.py b/salt/proxy/nxos.py index 0cccaeb770a..e6e3c25fd7c 100644 --- a/salt/proxy/nxos.py +++ b/salt/proxy/nxos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Proxy Minion for Cisco NX OS Switches .. versionadded: 2016.11.0 @@ -90,31 +90,32 @@ the :mod:`salt.modules.nxos<salt.modules.nxos>` execution module. only want one consistent connection used for everything, use `multiprocessing: False` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import multiprocessing import re # Import Salt libs from salt.utils.pycrypto import gen_hash, secure_password -from salt.utils.vt_helper import SSHConnection from salt.utils.vt import TerminalException +from salt.utils.vt_helper import SSHConnection log = logging.getLogger(__file__) -__proxyenabled__ = ['nxos'] -__virtualname__ = 'nxos' -DETAILS = {'grains_cache': {}} +__proxyenabled__ = ["nxos"] +__virtualname__ = "nxos" +DETAILS = {"grains_cache": {}} def __virtual__(): - ''' + """ Only return if all the modules are available - ''' - log.info('nxos proxy __virtual__() called...') + """ + log.info("nxos proxy __virtual__() called...") return __virtualname__ @@ -124,49 +125,50 @@ def _worker_name(): def init(opts=None): - ''' + """ Required. Can be used to initialize the server connection. - ''' + """ if opts is None: opts = __opts__ try: this_prompt = None - if 'prompt_regex' in opts['proxy']: - this_prompt = opts['proxy']['prompt_regex'] - elif 'prompt_name' in opts['proxy']: - this_prompt = '{0}.*#'.format(opts['proxy']['prompt_name']) + if "prompt_regex" in opts["proxy"]: + this_prompt = opts["proxy"]["prompt_regex"] + elif "prompt_name" in opts["proxy"]: + this_prompt = "{0}.*#".format(opts["proxy"]["prompt_name"]) else: - log.warning('nxos proxy configuration does not specify a prompt match.') - this_prompt = '.+#$' + log.warning("nxos proxy configuration does not specify a prompt match.") + this_prompt = ".+#$" DETAILS[_worker_name()] = SSHConnection( - host=opts['proxy']['host'], - username=opts['proxy']['username'], - password=opts['proxy']['password'], - key_accept=opts['proxy'].get('key_accept', False), - ssh_args=opts['proxy'].get('ssh_args', ''), - prompt=this_prompt) - out, err = DETAILS[_worker_name()].sendline('terminal length 0') + host=opts["proxy"]["host"], + username=opts["proxy"]["username"], + password=opts["proxy"]["password"], + key_accept=opts["proxy"].get("key_accept", False), + ssh_args=opts["proxy"].get("ssh_args", ""), + prompt=this_prompt, + ) + out, err = DETAILS[_worker_name()].sendline("terminal length 0") except TerminalException as e: log.error(e) return False - DETAILS['initialized'] = True + DETAILS["initialized"] = True def initialized(): - return DETAILS.get('initialized', False) + return DETAILS.get("initialized", False) def ping(): - ''' + """ Ping the device on the other end of the connection .. code-block:: bash salt '*' nxos.cmd ping - ''' + """ if _worker_name() not in DETAILS: init() try: @@ -177,85 +179,85 @@ def ping(): def shutdown(opts): - ''' + """ Disconnect - ''' + """ DETAILS[_worker_name()].close_connection() def sendline(command): - ''' + """ Run command through switch's cli .. code-block:: bash salt '*' nxos.cmd sendline 'show run | include "^username admin password"' - ''' + """ if ping() is False: init() out, err = DETAILS[_worker_name()].sendline(command) - _, out = out.split('\n', 1) - out, _, _ = out.rpartition('\n') + _, out = out.split("\n", 1) + out, _, _ = out.rpartition("\n") return out def grains(): - ''' + """ Get grains for proxy minion .. code-block:: bash salt '*' nxos.cmd grains - ''' - if not DETAILS['grains_cache']: + """ + if not DETAILS["grains_cache"]: ret = system_info() log.debug(ret) - DETAILS['grains_cache'].update(ret) - return {'nxos': DETAILS['grains_cache']} + DETAILS["grains_cache"].update(ret) + return {"nxos": DETAILS["grains_cache"]} def grains_refresh(): - ''' + """ Refresh the grains from the proxy device. .. code-block:: bash salt '*' nxos.cmd grains_refresh - ''' - DETAILS['grains_cache'] = {} + """ + DETAILS["grains_cache"] = {} return grains() def get_user(username): - ''' + """ Get username line from switch .. code-block:: bash salt '*' nxos.cmd get_user username=admin - ''' + """ return sendline('show run | include "^username {0} password 5 "'.format(username)) def get_roles(username): - ''' + """ Get roles that the username is assigned from switch .. code-block:: bash salt '*' nxos.cmd get_roles username=admin - ''' - info = sendline('show user-account {0}'.format(username)) - roles = re.search(r'^\s*roles:(.*)$', info, re.MULTILINE) + """ + info = sendline("show user-account {0}".format(username)) + roles = re.search(r"^\s*roles:(.*)$", info, re.MULTILINE) if roles: - roles = roles.group(1).strip().split(' ') + roles = roles.group(1).strip().split(" ") else: roles = [] return roles def check_password(username, password, encrypted=False): - ''' + """ Check if passed password is the one assigned to user .. code-block:: bash @@ -264,20 +266,26 @@ def check_password(username, password, encrypted=False): salt '*' nxos.cmd check_password username=admin \\ password='$5$2fWwO2vK$s7.Hr3YltMNHuhywQQ3nfOd.gAPHgs3SOBYYdGT3E.A' \\ encrypted=True - ''' - hash_algorithms = {'1': 'md5', - '2a': 'blowfish', - '5': 'sha256', - '6': 'sha512', } + """ + hash_algorithms = { + "1": "md5", + "2a": "blowfish", + "5": "sha256", + "6": "sha512", + } password_line = get_user(username) if not password_line: return None - if '!!' in password_line: + if "!!" in password_line: return False - cur_hash = re.search(r'(\$[0-6](?:\$[^$ ]+)+)', password_line).group(0) + cur_hash = re.search(r"(\$[0-6](?:\$[^$ ]+)+)", password_line).group(0) if encrypted is False: - hash_type, cur_salt, hashed_pass = re.search(r'^\$([0-6])\$([^$]+)\$(.*)$', cur_hash).groups() - new_hash = gen_hash(crypt_salt=cur_salt, password=password, algorithm=hash_algorithms[hash_type]) + hash_type, cur_salt, hashed_pass = re.search( + r"^\$([0-6])\$([^$]+)\$(.*)$", cur_hash + ).groups() + new_hash = gen_hash( + crypt_salt=cur_salt, password=password, algorithm=hash_algorithms[hash_type] + ) else: new_hash = password if new_hash == cur_hash: @@ -286,18 +294,20 @@ def check_password(username, password, encrypted=False): def check_role(username, role): - ''' + """ Check if user is assigned a specific role on switch .. code-block:: bash salt '*' nxos.cmd check_role username=admin role=network-admin - ''' + """ return role in get_roles(username) -def set_password(username, password, encrypted=False, role=None, crypt_salt=None, algorithm='sha256'): - ''' +def set_password( + username, password, encrypted=False, role=None, crypt_salt=None, algorithm="sha256" +): + """ Set users password on switch .. code-block:: bash @@ -306,100 +316,102 @@ def set_password(username, password, encrypted=False, role=None, crypt_salt=None salt '*' nxos.cmd set_password admin \\ password='$5$2fWwO2vK$s7.Hr3YltMNHuhywQQ3nfOd.gAPHgs3SOBYYdGT3E.A' \\ encrypted=True - ''' + """ password_line = get_user(username) if encrypted is False: if crypt_salt is None: # NXOS does not like non alphanumeric characters. Using the random module from pycrypto # can lead to having non alphanumeric characters in the salt for the hashed password. crypt_salt = secure_password(8, use_random=False) - hashed_pass = gen_hash(crypt_salt=crypt_salt, password=password, algorithm=algorithm) + hashed_pass = gen_hash( + crypt_salt=crypt_salt, password=password, algorithm=algorithm + ) else: hashed_pass = password - password_line = 'username {0} password 5 {1}'.format(username, hashed_pass) + password_line = "username {0} password 5 {1}".format(username, hashed_pass) if role is not None: - password_line += ' role {0}'.format(role) + password_line += " role {0}".format(role) try: - sendline('config terminal') + sendline("config terminal") ret = sendline(password_line) - sendline('end') - sendline('copy running-config startup-config') - return '\n'.join([password_line, ret]) + sendline("end") + sendline("copy running-config startup-config") + return "\n".join([password_line, ret]) except TerminalException as e: log.error(e) - return 'Failed to set password' + return "Failed to set password" def remove_user(username): - ''' + """ Remove user from switch .. code-block:: bash salt '*' nxos.cmd remove_user username=daniel - ''' + """ try: - sendline('config terminal') - user_line = 'no username {0}'.format(username) + sendline("config terminal") + user_line = "no username {0}".format(username) ret = sendline(user_line) - sendline('end') - sendline('copy running-config startup-config') - return '\n'.join([user_line, ret]) + sendline("end") + sendline("copy running-config startup-config") + return "\n".join([user_line, ret]) except TerminalException as e: log.error(e) - return 'Failed to set password' + return "Failed to set password" def set_role(username, role): - ''' + """ Assign role to username .. code-block:: bash salt '*' nxos.cmd set_role username=daniel role=vdc-admin - ''' + """ try: - sendline('config terminal') - role_line = 'username {0} role {1}'.format(username, role) + sendline("config terminal") + role_line = "username {0} role {1}".format(username, role) ret = sendline(role_line) - sendline('end') - sendline('copy running-config startup-config') - return '\n'.join([role_line, ret]) + sendline("end") + sendline("copy running-config startup-config") + return "\n".join([role_line, ret]) except TerminalException as e: log.error(e) - return 'Failed to set password' + return "Failed to set password" def unset_role(username, role): - ''' + """ Remove role from username .. code-block:: bash salt '*' nxos.cmd unset_role username=daniel role=vdc-admin - ''' + """ try: - sendline('config terminal') - role_line = 'no username {0} role {1}'.format(username, role) + sendline("config terminal") + role_line = "no username {0} role {1}".format(username, role) ret = sendline(role_line) - sendline('end') - sendline('copy running-config startup-config') - return '\n'.join([role_line, ret]) + sendline("end") + sendline("copy running-config startup-config") + return "\n".join([role_line, ret]) except TerminalException as e: log.error(e) - return 'Failed to set password' + return "Failed to set password" def show_run(): - ''' + """ Shortcut to run `show run` on switch .. code-block:: bash salt '*' nxos.cmd show_run - ''' + """ try: - ret = sendline('show run') + ret = sendline("show run") except TerminalException as e: log.error(e) return 'Failed to "show run"' @@ -407,15 +419,15 @@ def show_run(): def show_ver(): - ''' + """ Shortcut to run `show ver` on switch .. code-block:: bash salt '*' nxos.cmd show_ver - ''' + """ try: - ret = sendline('show ver') + ret = sendline("show ver") except TerminalException as e: log.error(e) return 'Failed to "show ver"' @@ -423,7 +435,7 @@ def show_ver(): def add_config(lines): - ''' + """ Add one or more config lines to the switch running config .. code-block:: bash @@ -432,16 +444,16 @@ def add_config(lines): .. note:: For more than one config added per command, lines should be a list. - ''' + """ if not isinstance(lines, list): lines = [lines] try: - sendline('config terminal') + sendline("config terminal") for line in lines: sendline(line) - sendline('end') - sendline('copy running-config startup-config') + sendline("end") + sendline("copy running-config startup-config") except TerminalException as e: log.error(e) return False @@ -449,7 +461,7 @@ def add_config(lines): def delete_config(lines): - ''' + """ Delete one or more config lines to the switch running config .. code-block:: bash @@ -458,16 +470,16 @@ def delete_config(lines): .. note:: For more than one config deleted per command, lines should be a list. - ''' + """ if not isinstance(lines, list): lines = [lines] try: - sendline('config terminal') + sendline("config terminal") for line in lines: - sendline(' '.join(['no', line])) + sendline(" ".join(["no", line])) - sendline('end') - sendline('copy running-config startup-config') + sendline("end") + sendline("copy running-config startup-config") except TerminalException as e: log.error(e) return False @@ -475,7 +487,7 @@ def delete_config(lines): def find(pattern): - ''' + """ Find all instances where the pattern is in the running command .. code-block:: bash @@ -485,13 +497,13 @@ def find(pattern): .. note:: This uses the `re.MULTILINE` regex format for python, and runs the regex against the whole show_run output. - ''' + """ matcher = re.compile(pattern, re.MULTILINE) return matcher.findall(show_run()) def replace(old_value, new_value, full_match=False): - ''' + """ Replace string or full line matches in switch's running config If full_match is set to True, then the whole line will need to be matched @@ -500,70 +512,70 @@ def replace(old_value, new_value, full_match=False): .. code-block:: bash salt '*' nxos.cmd replace 'TESTSTRINGHERE' 'NEWTESTSTRINGHERE' - ''' + """ if full_match is False: - matcher = re.compile('^.*{0}.*$'.format(re.escape(old_value)), re.MULTILINE) + matcher = re.compile("^.*{0}.*$".format(re.escape(old_value)), re.MULTILINE) repl = re.compile(re.escape(old_value)) else: matcher = re.compile(old_value, re.MULTILINE) repl = re.compile(old_value) - lines = {'old': [], 'new': []} + lines = {"old": [], "new": []} for line in matcher.finditer(show_run()): - lines['old'].append(line.group(0)) - lines['new'].append(repl.sub(new_value, line.group(0))) + lines["old"].append(line.group(0)) + lines["new"].append(repl.sub(new_value, line.group(0))) - delete_config(lines['old']) - add_config(lines['new']) + delete_config(lines["old"]) + add_config(lines["new"]) return lines def _parser(block): - return re.compile('^{block}\n(?:^[ \n].*$\n?)+'.format(block=block), re.MULTILINE) + return re.compile("^{block}\n(?:^[ \n].*$\n?)+".format(block=block), re.MULTILINE) def _parse_software(data): - ret = {'software': {}} - software = _parser('Software').search(data).group(0) - matcher = re.compile('^ ([^:]+): *([^\n]+)', re.MULTILINE) + ret = {"software": {}} + software = _parser("Software").search(data).group(0) + matcher = re.compile("^ ([^:]+): *([^\n]+)", re.MULTILINE) for line in matcher.finditer(software): key, val = line.groups() - ret['software'][key] = val - return ret['software'] + ret["software"][key] = val + return ret["software"] def _parse_hardware(data): - ret = {'hardware': {}} - hardware = _parser('Hardware').search(data).group(0) - matcher = re.compile('^ ([^:\n]+): *([^\n]+)', re.MULTILINE) + ret = {"hardware": {}} + hardware = _parser("Hardware").search(data).group(0) + matcher = re.compile("^ ([^:\n]+): *([^\n]+)", re.MULTILINE) for line in matcher.finditer(hardware): key, val = line.groups() - ret['hardware'][key] = val - return ret['hardware'] + ret["hardware"][key] = val + return ret["hardware"] def _parse_plugins(data): - ret = {'plugins': []} - plugins = _parser('plugin').search(data).group(0) - matcher = re.compile('^ (?:([^,]+), )+([^\n]+)', re.MULTILINE) + ret = {"plugins": []} + plugins = _parser("plugin").search(data).group(0) + matcher = re.compile("^ (?:([^,]+), )+([^\n]+)", re.MULTILINE) for line in matcher.finditer(plugins): - ret['plugins'].extend(line.groups()) - return ret['plugins'] + ret["plugins"].extend(line.groups()) + return ret["plugins"] def system_info(): - ''' + """ Return system information for grains of the NX OS proxy minion .. code-block:: bash salt '*' nxos.system_info - ''' + """ data = show_ver() info = { - 'software': _parse_software(data), - 'hardware': _parse_hardware(data), - 'plugins': _parse_plugins(data), + "software": _parse_software(data), + "hardware": _parse_hardware(data), + "plugins": _parse_plugins(data), } return info diff --git a/salt/proxy/nxos_api.py b/salt/proxy/nxos_api.py index 1d7eefb520e..da32e63391b 100644 --- a/salt/proxy/nxos_api.py +++ b/salt/proxy/nxos_api.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Proxy Minion to manage Cisco Nexus Switches (NX-OS) over the NX-API .. versionadded:: 2019.2.0 @@ -98,7 +98,7 @@ Proxy Pillar Example host: switch1.example.com username: example password: example -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python stdlib @@ -112,14 +112,14 @@ from salt.exceptions import SaltException # proxy properties # ----------------------------------------------------------------------------- -__proxyenabled__ = ['nxos_api'] +__proxyenabled__ = ["nxos_api"] # proxy name # ----------------------------------------------------------------------------- # globals # ----------------------------------------------------------------------------- -__virtualname__ = 'nxos_api' +__virtualname__ = "nxos_api" log = logging.getLogger(__name__) nxos_device = {} @@ -129,18 +129,19 @@ nxos_device = {} def __virtual__(): - ''' + """ This Proxy Module is widely available as there are no external dependencies. - ''' + """ return __virtualname__ + # ----------------------------------------------------------------------------- # proxy functions # ----------------------------------------------------------------------------- def init(opts): - ''' + """ Open the connection to the Nexsu switch over the NX-API. As the communication is HTTP based, there is no connection to maintain, @@ -148,44 +149,45 @@ def init(opts): bring up this Minion, we are executing a very simple command (``show clock``) which doesn't come with much overhead and it's sufficient to confirm we are indeed able to connect to the NX-API endpoint as configured. - ''' - proxy_dict = opts.get('proxy', {}) + """ + proxy_dict = opts.get("proxy", {}) conn_args = copy.deepcopy(proxy_dict) - conn_args.pop('proxytype', None) - opts['multiprocessing'] = conn_args.pop('multiprocessing', True) + conn_args.pop("proxytype", None) + opts["multiprocessing"] = conn_args.pop("multiprocessing", True) # This is not a SSH-based proxy, so it should be safe to enable # multiprocessing. try: - rpc_reply = __utils__['nxos_api.rpc']('show clock', **conn_args) + rpc_reply = __utils__["nxos_api.rpc"]("show clock", **conn_args) # Execute a very simple command to confirm we are able to connect properly - nxos_device['conn_args'] = conn_args - nxos_device['initialized'] = True - nxos_device['up'] = True + nxos_device["conn_args"] = conn_args + nxos_device["initialized"] = True + nxos_device["up"] = True except SaltException: - log.error('Unable to connect to %s', conn_args['host'], exc_info=True) + log.error("Unable to connect to %s", conn_args["host"], exc_info=True) raise return True def ping(): - ''' + """ Connection open successfully? - ''' - return nxos_device.get('up', False) + """ + return nxos_device.get("up", False) def initialized(): - ''' + """ Connection finished initializing? - ''' - return nxos_device.get('initialized', False) + """ + return nxos_device.get("initialized", False) def shutdown(opts): - ''' + """ Closes connection with the device. - ''' - log.debug('Shutting down the nxos_api Proxy Minion %s', opts['id']) + """ + log.debug("Shutting down the nxos_api Proxy Minion %s", opts["id"]) + # ----------------------------------------------------------------------------- # callable functions @@ -193,17 +195,17 @@ def shutdown(opts): def get_conn_args(): - ''' + """ Returns the connection arguments of the Proxy Minion. - ''' - conn_args = copy.deepcopy(nxos_device['conn_args']) + """ + conn_args = copy.deepcopy(nxos_device["conn_args"]) return conn_args -def rpc(commands, method='cli', **kwargs): - ''' +def rpc(commands, method="cli", **kwargs): + """ Executes an RPC request over the NX-API. - ''' - conn_args = nxos_device['conn_args'] + """ + conn_args = nxos_device["conn_args"] conn_args.update(kwargs) - return __utils__['nxos_api.rpc'](commands, method=method, **conn_args) + return __utils__["nxos_api.rpc"](commands, method=method, **conn_args) diff --git a/salt/proxy/panos.py b/salt/proxy/panos.py index 969661cc6dc..5c298b4f7d7 100644 --- a/salt/proxy/panos.py +++ b/salt/proxy/panos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Proxy Minion interface module for managing Palo Alto firewall devices ===================================================================== @@ -201,49 +201,50 @@ apikey ^^^^^^^^ The generated XML API key for the Panorama server. Required. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python Libs import logging -# Import Salt Libs -from salt._compat import ElementTree as ET import salt.exceptions import salt.utils.xmlutil as xml + +# Import Salt Libs +from salt._compat import ElementTree as ET from salt.ext import six # This must be present or the Salt loader won't load this module. -__proxyenabled__ = ['panos'] +__proxyenabled__ = ["panos"] # Variables are scoped to this module so we can have persistent data. -GRAINS_CACHE = {'vendor': 'Palo Alto'} +GRAINS_CACHE = {"vendor": "Palo Alto"} DETAILS = {} # Set up logging log = logging.getLogger(__file__) # Define the module's virtual name -__virtualname__ = 'panos' +__virtualname__ = "panos" def __virtual__(): - ''' + """ Only return if all the modules are available. - ''' + """ return __virtualname__ def _strip_dirty(xmltree): - ''' + """ Removes dirtyID tags from the candidate config result. Palo Alto devices will make the candidate configuration with a dirty ID after a change. This can cause unexpected results when parsing. - ''' - dirty = xmltree.attrib.pop('dirtyId', None) + """ + dirty = xmltree.attrib.pop("dirtyId", None) if dirty: - xmltree.attrib.pop('admin', None) - xmltree.attrib.pop('time', None) + xmltree.attrib.pop("admin", None) + xmltree.attrib.pop("time", None) for child in xmltree: child = _strip_dirty(child) @@ -252,155 +253,174 @@ def _strip_dirty(xmltree): def init(opts): - ''' + """ This function gets called when the proxy starts up. For panos devices, a determination is made on the connection type and the appropriate connection details that must be cached. - ''' - if 'host' not in opts['proxy']: - log.critical('No \'host\' key found in pillar for this proxy.') + """ + if "host" not in opts["proxy"]: + log.critical("No 'host' key found in pillar for this proxy.") return False - if 'apikey' not in opts['proxy']: + if "apikey" not in opts["proxy"]: # If we do not have an apikey, we must have both a username and password - if 'username' not in opts['proxy']: - log.critical('No \'username\' key found in pillar for this proxy.') + if "username" not in opts["proxy"]: + log.critical("No 'username' key found in pillar for this proxy.") return False - if 'password' not in opts['proxy']: - log.critical('No \'passwords\' key found in pillar for this proxy.') + if "password" not in opts["proxy"]: + log.critical("No 'passwords' key found in pillar for this proxy.") return False - DETAILS['url'] = 'https://{0}/api/'.format(opts['proxy']['host']) + DETAILS["url"] = "https://{0}/api/".format(opts["proxy"]["host"]) # Set configuration details - DETAILS['host'] = opts['proxy']['host'] - if 'serial' in opts['proxy']: - DETAILS['serial'] = opts['proxy'].get('serial') - if 'apikey' in opts['proxy']: + DETAILS["host"] = opts["proxy"]["host"] + if "serial" in opts["proxy"]: + DETAILS["serial"] = opts["proxy"].get("serial") + if "apikey" in opts["proxy"]: log.debug("Selected pan_key method for panos proxy module.") - DETAILS['method'] = 'pan_key' - DETAILS['apikey'] = opts['proxy'].get('apikey') + DETAILS["method"] = "pan_key" + DETAILS["apikey"] = opts["proxy"].get("apikey") else: log.debug("Selected pan_pass method for panos proxy module.") - DETAILS['method'] = 'pan_pass' - DETAILS['username'] = opts['proxy'].get('username') - DETAILS['password'] = opts['proxy'].get('password') + DETAILS["method"] = "pan_pass" + DETAILS["username"] = opts["proxy"].get("username") + DETAILS["password"] = opts["proxy"].get("password") else: - if 'apikey' in opts['proxy']: + if "apikey" in opts["proxy"]: log.debug("Selected dev_key method for panos proxy module.") - DETAILS['method'] = 'dev_key' - DETAILS['apikey'] = opts['proxy'].get('apikey') + DETAILS["method"] = "dev_key" + DETAILS["apikey"] = opts["proxy"].get("apikey") else: log.debug("Selected dev_pass method for panos proxy module.") - DETAILS['method'] = 'dev_pass' - DETAILS['username'] = opts['proxy'].get('username') - DETAILS['password'] = opts['proxy'].get('password') + DETAILS["method"] = "dev_pass" + DETAILS["username"] = opts["proxy"].get("username") + DETAILS["password"] = opts["proxy"].get("password") # Ensure connectivity to the device log.debug("Attempting to connect to panos proxy host.") - query = {'type': 'op', 'cmd': '<show><system><info></info></system></show>'} + query = {"type": "op", "cmd": "<show><system><info></info></system></show>"} call(query) log.debug("Successfully connected to panos proxy host.") - DETAILS['initialized'] = True + DETAILS["initialized"] = True def call(payload=None): - ''' + """ This function captures the query string and sends it to the Palo Alto device. - ''' + """ r = None try: - if DETAILS['method'] == 'dev_key': + if DETAILS["method"] == "dev_key": # Pass the api key without the target declaration - conditional_payload = {'key': DETAILS['apikey']} + conditional_payload = {"key": DETAILS["apikey"]} payload.update(conditional_payload) - r = __utils__['http.query'](DETAILS['url'], - data=payload, - method='POST', - decode_type='plain', - decode=True, - verify_ssl=False, - status=True, - raise_error=True) - elif DETAILS['method'] == 'dev_pass': + r = __utils__["http.query"]( + DETAILS["url"], + data=payload, + method="POST", + decode_type="plain", + decode=True, + verify_ssl=False, + status=True, + raise_error=True, + ) + elif DETAILS["method"] == "dev_pass": # Pass credentials without the target declaration - r = __utils__['http.query'](DETAILS['url'], - username=DETAILS['username'], - password=DETAILS['password'], - data=payload, - method='POST', - decode_type='plain', - decode=True, - verify_ssl=False, - status=True, - raise_error=True) - elif DETAILS['method'] == 'pan_key': + r = __utils__["http.query"]( + DETAILS["url"], + username=DETAILS["username"], + password=DETAILS["password"], + data=payload, + method="POST", + decode_type="plain", + decode=True, + verify_ssl=False, + status=True, + raise_error=True, + ) + elif DETAILS["method"] == "pan_key": # Pass the api key with the target declaration - conditional_payload = {'key': DETAILS['apikey'], - 'target': DETAILS['serial']} + conditional_payload = { + "key": DETAILS["apikey"], + "target": DETAILS["serial"], + } payload.update(conditional_payload) - r = __utils__['http.query'](DETAILS['url'], - data=payload, - method='POST', - decode_type='plain', - decode=True, - verify_ssl=False, - status=True, - raise_error=True) - elif DETAILS['method'] == 'pan_pass': + r = __utils__["http.query"]( + DETAILS["url"], + data=payload, + method="POST", + decode_type="plain", + decode=True, + verify_ssl=False, + status=True, + raise_error=True, + ) + elif DETAILS["method"] == "pan_pass": # Pass credentials with the target declaration - conditional_payload = {'target': DETAILS['serial']} + conditional_payload = {"target": DETAILS["serial"]} payload.update(conditional_payload) - r = __utils__['http.query'](DETAILS['url'], - username=DETAILS['username'], - password=DETAILS['password'], - data=payload, - method='POST', - decode_type='plain', - decode=True, - verify_ssl=False, - status=True, - raise_error=True) + r = __utils__["http.query"]( + DETAILS["url"], + username=DETAILS["username"], + password=DETAILS["password"], + data=payload, + method="POST", + decode_type="plain", + decode=True, + verify_ssl=False, + status=True, + raise_error=True, + ) except KeyError as err: - raise salt.exceptions.CommandExecutionError("Did not receive a valid response from host.") + raise salt.exceptions.CommandExecutionError( + "Did not receive a valid response from host." + ) if not r: - raise salt.exceptions.CommandExecutionError("Did not receive a valid response from host.") + raise salt.exceptions.CommandExecutionError( + "Did not receive a valid response from host." + ) - if six.text_type(r['status']) not in ['200', '201', '204']: - if six.text_type(r['status']) == '400': + if six.text_type(r["status"]) not in ["200", "201", "204"]: + if six.text_type(r["status"]) == "400": raise salt.exceptions.CommandExecutionError( - "The server cannot process the request due to a client error.") - elif six.text_type(r['status']) == '401': + "The server cannot process the request due to a client error." + ) + elif six.text_type(r["status"]) == "401": raise salt.exceptions.CommandExecutionError( "The server cannot process the request because it lacks valid authentication " - "credentials for the target resource.") - elif six.text_type(r['status']) == '403': + "credentials for the target resource." + ) + elif six.text_type(r["status"]) == "403": raise salt.exceptions.CommandExecutionError( - "The server refused to authorize the request.") - elif six.text_type(r['status']) == '404': + "The server refused to authorize the request." + ) + elif six.text_type(r["status"]) == "404": raise salt.exceptions.CommandExecutionError( - "The requested resource could not be found.") + "The requested resource could not be found." + ) else: raise salt.exceptions.CommandExecutionError( - "Did not receive a valid response from host.") + "Did not receive a valid response from host." + ) - xmldata = ET.fromstring(r['text']) + xmldata = ET.fromstring(r["text"]) # If we are pulling the candidate configuration, we need to strip the dirtyId - if payload['type'] == 'config' and payload['action'] == 'get': - xmldata = (_strip_dirty(xmldata)) + if payload["type"] == "config" and payload["action"] == "get": + xmldata = _strip_dirty(xmldata) return xml.to_dict(xmldata, True) -def is_required_version(required_version='0.0.0'): - ''' +def is_required_version(required_version="0.0.0"): + """ Because different versions of Palo Alto support different command sets, this function will return true if the current version of Palo Alto supports the required command. - ''' - if 'sw-version' in DETAILS['grains_cache']: - current_version = DETAILS['grains_cache']['sw-version'] + """ + if "sw-version" in DETAILS["grains_cache"]: + current_version = DETAILS["grains_cache"]["sw-version"] else: # If we do not have the current sw-version cached, we cannot check version requirements. return False @@ -431,43 +451,43 @@ def is_required_version(required_version='0.0.0'): def initialized(): - ''' + """ Since grains are loaded in many different places and some of those places occur before the proxy can be initialized, return whether our init() function has been called - ''' - return DETAILS.get('initialized', False) + """ + return DETAILS.get("initialized", False) def grains(): - ''' + """ Get the grains from the proxied device - ''' - if not DETAILS.get('grains_cache', {}): - DETAILS['grains_cache'] = GRAINS_CACHE + """ + if not DETAILS.get("grains_cache", {}): + DETAILS["grains_cache"] = GRAINS_CACHE try: - query = {'type': 'op', 'cmd': '<show><system><info></info></system></show>'} - DETAILS['grains_cache'] = call(query)['result']['system'] + query = {"type": "op", "cmd": "<show><system><info></info></system></show>"} + DETAILS["grains_cache"] = call(query)["result"]["system"] except Exception as err: # pylint: disable=broad-except pass - return DETAILS['grains_cache'] + return DETAILS["grains_cache"] def grains_refresh(): - ''' + """ Refresh the grains from the proxied device - ''' - DETAILS['grains_cache'] = None + """ + DETAILS["grains_cache"] = None return grains() def ping(): - ''' + """ Returns true if the device is reachable, else false. - ''' + """ try: - query = {'type': 'op', 'cmd': '<show><system><info></info></system></show>'} - if 'result' in call(query): + query = {"type": "op", "cmd": "<show><system><info></info></system></show>"} + if "result" in call(query): return True else: return False @@ -476,8 +496,8 @@ def ping(): def shutdown(): - ''' + """ Shutdown the connection to the proxy device. For this proxy, shutdown is a no-op. - ''' - log.debug('Panos proxy shutdown() called.') + """ + log.debug("Panos proxy shutdown() called.") diff --git a/salt/proxy/philips_hue.py b/salt/proxy/philips_hue.py index 48d27abffd7..0a2365b921a 100644 --- a/salt/proxy/philips_hue.py +++ b/salt/proxy/philips_hue.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -''' +""" Philips HUE lamps module for proxy. .. versionadded:: 2015.8.3 @@ -31,30 +31,31 @@ To configure the proxy minion: host: [hostname or ip] user: [username] -''' +""" # pylint: disable=import-error,no-name-in-module,redefined-builtin from __future__ import absolute_import, print_function, unicode_literals -import salt.ext.six.moves.http_client as http_client # Import python libs import logging import time + +import salt.ext.six.moves.http_client as http_client import salt.utils.json -from salt.exceptions import (CommandExecutionError, MinionError) +from salt.exceptions import CommandExecutionError, MinionError from salt.ext import six - -__proxyenabled__ = ['philips_hue'] +__proxyenabled__ = ["philips_hue"] CONFIG = {} log = logging.getLogger(__file__) class Const(object): - ''' + """ Constants for the lamp operations. - ''' + """ + LAMP_ON = {"on": True, "transitiontime": 0} LAMP_OFF = {"on": False, "transitiontime": 0} @@ -70,57 +71,61 @@ class Const(object): def __virtual__(): - ''' + """ Validate the module. - ''' + """ return True def init(cnf): - ''' + """ Initialize the module. - ''' - CONFIG['host'] = cnf.get('proxy', {}).get('host') - if not CONFIG['host']: - raise MinionError(message="Cannot find 'host' parameter in the proxy configuration") + """ + CONFIG["host"] = cnf.get("proxy", {}).get("host") + if not CONFIG["host"]: + raise MinionError( + message="Cannot find 'host' parameter in the proxy configuration" + ) - CONFIG['user'] = cnf.get('proxy', {}).get('user') - if not CONFIG['user']: - raise MinionError(message="Cannot find 'user' parameter in the proxy configuration") + CONFIG["user"] = cnf.get("proxy", {}).get("user") + if not CONFIG["user"]: + raise MinionError( + message="Cannot find 'user' parameter in the proxy configuration" + ) - CONFIG['uri'] = "/api/{0}".format(CONFIG['user']) + CONFIG["uri"] = "/api/{0}".format(CONFIG["user"]) def ping(*args, **kw): - ''' + """ Ping the lamps. - ''' + """ # Here blink them return True def shutdown(opts, *args, **kw): - ''' + """ Shuts down the service. - ''' + """ # This is no-op method, which is required but makes nothing at this point. return True -def _query(lamp_id, state, action='', method='GET'): - ''' +def _query(lamp_id, state, action="", method="GET"): + """ Query the URI :return: - ''' + """ # Because salt.utils.query is that dreadful... :( err = None - url = "{0}/lights{1}".format(CONFIG['uri'], - lamp_id and '/{0}'.format(lamp_id) or '') \ - + (action and "/{0}".format(action) or '') - conn = http_client.HTTPConnection(CONFIG['host']) - if method == 'PUT': + url = "{0}/lights{1}".format( + CONFIG["uri"], lamp_id and "/{0}".format(lamp_id) or "" + ) + (action and "/{0}".format(action) or "") + conn = http_client.HTTPConnection(CONFIG["host"]) + if method == "PUT": conn.request(method, url, salt.utils.json.dumps(state)) else: conn.request(method, url) @@ -138,53 +143,58 @@ def _query(lamp_id, state, action='', method='GET'): def _set(lamp_id, state, method="state"): - ''' + """ Set state to the device by ID. :param lamp_id: :param state: :return: - ''' + """ try: - res = _query(lamp_id, state, action=method, method='PUT') + res = _query(lamp_id, state, action=method, method="PUT") except Exception as err: # pylint: disable=broad-except raise CommandExecutionError(err) res = len(res) > 1 and res[-1] or res[0] - if res.get('success'): - res = {'result': True} - elif res.get('error'): - res = {'result': False, - 'description': res['error']['description'], - 'type': res['error']['type']} + if res.get("success"): + res = {"result": True} + elif res.get("error"): + res = { + "result": False, + "description": res["error"]["description"], + "type": res["error"]["type"], + } return res def _get_devices(params): - ''' + """ Parse device(s) ID(s) from the common params. :param params: :return: - ''' - if 'id' not in params: + """ + if "id" not in params: raise CommandExecutionError("Parameter ID is required.") - return type(params['id']) == int and [params['id']] \ - or [int(dev) for dev in params['id'].split(",")] + return ( + type(params["id"]) == int + and [params["id"]] + or [int(dev) for dev in params["id"].split(",")] + ) def _get_lights(): - ''' + """ Get all available lighting devices. - ''' + """ return _query(None, None) # Callers def call_lights(*args, **kwargs): - ''' + """ Get info about all available lamps. Options: @@ -198,10 +208,10 @@ def call_lights(*args, **kwargs): salt '*' hue.lights salt '*' hue.lights id=1 salt '*' hue.lights id=1,2,3 - ''' + """ res = dict() lights = _get_lights() - for dev_id in 'id' in kwargs and _get_devices(kwargs) or sorted(lights.keys()): + for dev_id in "id" in kwargs and _get_devices(kwargs) or sorted(lights.keys()): if lights.get(six.text_type(dev_id)): res[dev_id] = lights[six.text_type(dev_id)] @@ -209,7 +219,7 @@ def call_lights(*args, **kwargs): def call_switch(*args, **kwargs): - ''' + """ Switch lamp ON/OFF. If no particular state is passed, @@ -227,22 +237,26 @@ def call_switch(*args, **kwargs): salt '*' hue.switch salt '*' hue.switch id=1 salt '*' hue.switch id=1,2,3 on=True - ''' + """ out = dict() devices = _get_lights() - for dev_id in 'id' not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): - if 'on' in kwargs: - state = kwargs['on'] and Const.LAMP_ON or Const.LAMP_OFF + for dev_id in "id" not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): + if "on" in kwargs: + state = kwargs["on"] and Const.LAMP_ON or Const.LAMP_OFF else: # Invert the current state - state = devices[six.text_type(dev_id)]['state']['on'] and Const.LAMP_OFF or Const.LAMP_ON + state = ( + devices[six.text_type(dev_id)]["state"]["on"] + and Const.LAMP_OFF + or Const.LAMP_ON + ) out[dev_id] = _set(dev_id, state) return out def call_blink(*args, **kwargs): - ''' + """ Blink a lamp. If lamp is ON, then blink ON-OFF-ON, otherwise OFF-ON-OFF. Options: @@ -256,12 +270,12 @@ def call_blink(*args, **kwargs): salt '*' hue.blink id=1 salt '*' hue.blink id=1,2,3 - ''' + """ devices = _get_lights() - pause = kwargs.get('pause', 0) + pause = kwargs.get("pause", 0) res = dict() - for dev_id in 'id' not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): - state = devices[six.text_type(dev_id)]['state']['on'] + for dev_id in "id" not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): + state = devices[six.text_type(dev_id)]["state"]["on"] _set(dev_id, state and Const.LAMP_OFF or Const.LAMP_ON) if pause: time.sleep(pause) @@ -271,7 +285,7 @@ def call_blink(*args, **kwargs): def call_ping(*args, **kwargs): - ''' + """ Ping the lamps by issuing a short inversion blink to all available devices. CLI Example: @@ -279,17 +293,17 @@ def call_ping(*args, **kwargs): .. code-block:: bash salt '*' hue.ping - ''' + """ errors = dict() for dev_id, dev_status in call_blink().items(): - if not dev_status['result']: + if not dev_status["result"]: errors[dev_id] = False return errors or True def call_status(*args, **kwargs): - ''' + """ Return the status of the lamps. Options: @@ -303,21 +317,21 @@ def call_status(*args, **kwargs): salt '*' hue.status salt '*' hue.status id=1 salt '*' hue.status id=1,2,3 - ''' + """ res = dict() devices = _get_lights() - for dev_id in 'id' not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): + for dev_id in "id" not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): dev_id = six.text_type(dev_id) res[dev_id] = { - 'on': devices[dev_id]['state']['on'], - 'reachable': devices[dev_id]['state']['reachable'] + "on": devices[dev_id]["state"]["on"], + "reachable": devices[dev_id]["state"]["reachable"], } return res def call_rename(*args, **kwargs): - ''' + """ Rename a device. Options: @@ -330,19 +344,19 @@ def call_rename(*args, **kwargs): .. code-block:: bash salt '*' hue.rename id=1 title='WC for cats' - ''' + """ dev_id = _get_devices(kwargs) if len(dev_id) > 1: raise CommandExecutionError("Only one device can be renamed at a time") - if 'title' not in kwargs: + if "title" not in kwargs: raise CommandExecutionError("Title is missing") - return _set(dev_id[0], {"name": kwargs['title']}, method="") + return _set(dev_id[0], {"name": kwargs["title"]}, method="") def call_alert(*args, **kwargs): - ''' + """ Lamp alert Options: @@ -357,18 +371,20 @@ def call_alert(*args, **kwargs): salt '*' hue.alert salt '*' hue.alert id=1 salt '*' hue.alert id=1,2,3 on=false - ''' + """ res = dict() devices = _get_lights() - for dev_id in 'id' not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): - res[dev_id] = _set(dev_id, {"alert": kwargs.get("on", True) and "lselect" or "none"}) + for dev_id in "id" not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): + res[dev_id] = _set( + dev_id, {"alert": kwargs.get("on", True) and "lselect" or "none"} + ) return res def call_effect(*args, **kwargs): - ''' + """ Set an effect to the lamp. Options: @@ -383,18 +399,18 @@ def call_effect(*args, **kwargs): salt '*' hue.effect salt '*' hue.effect id=1 salt '*' hue.effect id=1,2,3 type=colorloop - ''' + """ res = dict() devices = _get_lights() - for dev_id in 'id' not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): + for dev_id in "id" not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): res[dev_id] = _set(dev_id, {"effect": kwargs.get("type", "none")}) return res def call_color(*args, **kwargs): - ''' + """ Set a color to the lamp. Options: @@ -417,19 +433,19 @@ def call_color(*args, **kwargs): salt '*' hue.color id=1 salt '*' hue.color id=1,2,3 oolor=red transition=30 salt '*' hue.color id=1 gamut=0.3,0.5 - ''' + """ res = dict() colormap = { - 'red': Const.COLOR_RED, - 'green': Const.COLOR_GREEN, - 'blue': Const.COLOR_BLUE, - 'orange': Const.COLOR_ORANGE, - 'pink': Const.COLOR_PINK, - 'white': Const.COLOR_WHITE, - 'yellow': Const.COLOR_YELLOW, - 'daylight': Const.COLOR_DAYLIGHT, - 'purple': Const.COLOR_PURPLE, + "red": Const.COLOR_RED, + "green": Const.COLOR_GREEN, + "blue": Const.COLOR_BLUE, + "orange": Const.COLOR_ORANGE, + "pink": Const.COLOR_PINK, + "white": Const.COLOR_WHITE, + "yellow": Const.COLOR_YELLOW, + "daylight": Const.COLOR_DAYLIGHT, + "purple": Const.COLOR_PURPLE, } devices = _get_lights() @@ -445,17 +461,17 @@ def call_color(*args, **kwargs): color = None if not color: - color = colormap.get(kwargs.get("color", 'white'), Const.COLOR_WHITE) + color = colormap.get(kwargs.get("color", "white"), Const.COLOR_WHITE) color.update({"transitiontime": max(min(kwargs.get("transition", 0), 200), 0)}) - for dev_id in 'id' not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): + for dev_id in "id" not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): res[dev_id] = _set(dev_id, color) return res def call_brightness(*args, **kwargs): - ''' + """ Set an effect to the lamp. Arguments: @@ -474,31 +490,31 @@ def call_brightness(*args, **kwargs): salt '*' hue.brightness value=100 salt '*' hue.brightness id=1 value=150 salt '*' hue.brightness id=1,2,3 value=255 - ''' + """ res = dict() - if 'value' not in kwargs: + if "value" not in kwargs: raise CommandExecutionError("Parameter 'value' is missing") try: - brightness = max(min(int(kwargs['value']), 244), 1) + brightness = max(min(int(kwargs["value"]), 244), 1) except Exception as err: # pylint: disable=broad-except raise CommandExecutionError("Parameter 'value' does not contains an integer") try: - transition = max(min(int(kwargs['transition']), 200), 0) + transition = max(min(int(kwargs["transition"]), 200), 0) except Exception as err: # pylint: disable=broad-except transition = 0 devices = _get_lights() - for dev_id in 'id' not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): + for dev_id in "id" not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): res[dev_id] = _set(dev_id, {"bri": brightness, "transitiontime": transition}) return res def call_temperature(*args, **kwargs): - ''' + """ Set the mired color temperature. More: http://en.wikipedia.org/wiki/Mired Arguments: @@ -516,18 +532,18 @@ def call_temperature(*args, **kwargs): salt '*' hue.temperature value=150 salt '*' hue.temperature value=150 id=1 salt '*' hue.temperature value=150 id=1,2,3 - ''' + """ res = dict() - if 'value' not in kwargs: + if "value" not in kwargs: raise CommandExecutionError("Parameter 'value' (150~500) is missing") try: - value = max(min(int(kwargs['value']), 500), 150) + value = max(min(int(kwargs["value"]), 500), 150) except Exception as err: # pylint: disable=broad-except raise CommandExecutionError("Parameter 'value' does not contains an integer") devices = _get_lights() - for dev_id in 'id' not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): + for dev_id in "id" not in kwargs and sorted(devices.keys()) or _get_devices(kwargs): res[dev_id] = _set(dev_id, {"ct": value}) return res diff --git a/salt/proxy/rest_sample.py b/salt/proxy/rest_sample.py index f4895e7116d..bed43119256 100644 --- a/salt/proxy/rest_sample.py +++ b/salt/proxy/rest_sample.py @@ -1,18 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" This is a simple proxy-minion designed to connect to and communicate with the bottle-based web service contained in https://github.com/saltstack/salt-contrib/tree/master/proxyminion_rest_example -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging + import salt.utils.http HAS_REST_EXAMPLE = True # This must be present or the Salt loader won't load this module -__proxyenabled__ = ['rest_sample'] +__proxyenabled__ = ["rest_sample"] # Variables are scoped to this module so we can have persistent data @@ -27,192 +28,217 @@ log = logging.getLogger(__file__) # This does nothing, it's here just as an example and to provide a log # entry when the module is loaded. def __virtual__(): - ''' + """ Only return if all the modules are available - ''' - log.debug('rest_sample proxy __virtual__() called...') + """ + log.debug("rest_sample proxy __virtual__() called...") return True + # Every proxy module needs an 'init', though you can # just put DETAILS['initialized'] = True here if nothing # else needs to be done. def init(opts): - log.debug('rest_sample proxy init() called...') - DETAILS['initialized'] = True + log.debug("rest_sample proxy init() called...") + DETAILS["initialized"] = True # Save the REST URL - DETAILS['url'] = opts['proxy']['url'] + DETAILS["url"] = opts["proxy"]["url"] # Make sure the REST URL ends with a '/' - if not DETAILS['url'].endswith('/'): - DETAILS['url'] += '/' + if not DETAILS["url"].endswith("/"): + DETAILS["url"] += "/" def initialized(): - ''' + """ Since grains are loaded in many different places and some of those places occur before the proxy can be initialized, return whether our init() function has been called - ''' - return DETAILS.get('initialized', False) + """ + return DETAILS.get("initialized", False) def alive(opts): - log.debug('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-') - log.debug('proxys alive() fn called') - log.debug('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-') + log.debug("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-") + log.debug("proxys alive() fn called") + log.debug("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-") return ping() def id(opts): - ''' + """ Return a unique ID for this proxy minion. This ID MUST NOT CHANGE. If it changes while the proxy is running the salt-master will get really confused and may stop talking to this minion - ''' - r = salt.utils.http.query(opts['proxy']['url']+'id', decode_type='json', decode=True) - return r['dict']['id'].encode('ascii', 'ignore') + """ + r = salt.utils.http.query( + opts["proxy"]["url"] + "id", decode_type="json", decode=True + ) + return r["dict"]["id"].encode("ascii", "ignore") def grains(): - ''' + """ Get the grains from the proxied device - ''' - if not DETAILS.get('grains_cache', {}): - r = salt.utils.http.query(DETAILS['url']+'info', decode_type='json', decode=True) - DETAILS['grains_cache'] = r['dict'] - return DETAILS['grains_cache'] + """ + if not DETAILS.get("grains_cache", {}): + r = salt.utils.http.query( + DETAILS["url"] + "info", decode_type="json", decode=True + ) + DETAILS["grains_cache"] = r["dict"] + return DETAILS["grains_cache"] def grains_refresh(): - ''' + """ Refresh the grains from the proxied device - ''' - DETAILS['grains_cache'] = None + """ + DETAILS["grains_cache"] = None return grains() def fns(): - return {'details': 'This key is here because a function in ' - 'grains/rest_sample.py called fns() here in the proxymodule.'} + return { + "details": "This key is here because a function in " + "grains/rest_sample.py called fns() here in the proxymodule." + } def service_start(name): - ''' + """ Start a "service" on the REST server - ''' - r = salt.utils.http.query(DETAILS['url']+'service/start/'+name, decode_type='json', decode=True) - return r['dict'] + """ + r = salt.utils.http.query( + DETAILS["url"] + "service/start/" + name, decode_type="json", decode=True + ) + return r["dict"] def service_stop(name): - ''' + """ Stop a "service" on the REST server - ''' - r = salt.utils.http.query(DETAILS['url']+'service/stop/'+name, decode_type='json', decode=True) - return r['dict'] + """ + r = salt.utils.http.query( + DETAILS["url"] + "service/stop/" + name, decode_type="json", decode=True + ) + return r["dict"] def service_restart(name): - ''' + """ Restart a "service" on the REST server - ''' - r = salt.utils.http.query(DETAILS['url']+'service/restart/'+name, decode_type='json', decode=True) - return r['dict'] + """ + r = salt.utils.http.query( + DETAILS["url"] + "service/restart/" + name, decode_type="json", decode=True + ) + return r["dict"] def service_list(): - ''' + """ List "services" on the REST server - ''' - r = salt.utils.http.query(DETAILS['url']+'service/list', decode_type='json', decode=True) - return r['dict'] + """ + r = salt.utils.http.query( + DETAILS["url"] + "service/list", decode_type="json", decode=True + ) + return r["dict"] def service_status(name): - ''' + """ Check if a service is running on the REST server - ''' - r = salt.utils.http.query(DETAILS['url']+'service/status/'+name, decode_type='json', decode=True) - return r['dict'] + """ + r = salt.utils.http.query( + DETAILS["url"] + "service/status/" + name, decode_type="json", decode=True + ) + return r["dict"] def package_list(): - ''' + """ List "packages" installed on the REST server - ''' - r = salt.utils.http.query(DETAILS['url']+'package/list', decode_type='json', decode=True) - return r['dict'] + """ + r = salt.utils.http.query( + DETAILS["url"] + "package/list", decode_type="json", decode=True + ) + return r["dict"] def package_install(name, **kwargs): - ''' + """ Install a "package" on the REST server - ''' - cmd = DETAILS['url']+'package/install/'+name - if kwargs.get('version', False): - cmd += '/'+kwargs['version'] + """ + cmd = DETAILS["url"] + "package/install/" + name + if kwargs.get("version", False): + cmd += "/" + kwargs["version"] else: - cmd += '/1.0' - r = salt.utils.http.query(cmd, decode_type='json', decode=True) - return r['dict'] + cmd += "/1.0" + r = salt.utils.http.query(cmd, decode_type="json", decode=True) + return r["dict"] def fix_outage(): - r = salt.utils.http.query(DETAILS['url']+'fix_outage') + r = salt.utils.http.query(DETAILS["url"] + "fix_outage") return r def uptodate(name): - ''' + """ Call the REST endpoint to see if the packages on the "server" are up to date. - ''' - r = salt.utils.http.query(DETAILS['url']+'package/remove/'+name, decode_type='json', decode=True) - return r['dict'] + """ + r = salt.utils.http.query( + DETAILS["url"] + "package/remove/" + name, decode_type="json", decode=True + ) + return r["dict"] def package_remove(name): - ''' + """ Remove a "package" on the REST server - ''' - r = salt.utils.http.query(DETAILS['url']+'package/remove/'+name, decode_type='json', decode=True) - return r['dict'] + """ + r = salt.utils.http.query( + DETAILS["url"] + "package/remove/" + name, decode_type="json", decode=True + ) + return r["dict"] def package_status(name): - ''' + """ Check the installation status of a package on the REST server - ''' - r = salt.utils.http.query(DETAILS['url']+'package/status/'+name, decode_type='json', decode=True) - return r['dict'] + """ + r = salt.utils.http.query( + DETAILS["url"] + "package/status/" + name, decode_type="json", decode=True + ) + return r["dict"] def ping(): - ''' + """ Is the REST server up? - ''' - r = salt.utils.http.query(DETAILS['url']+'ping', decode_type='json', decode=True) + """ + r = salt.utils.http.query(DETAILS["url"] + "ping", decode_type="json", decode=True) try: - return r['dict'].get('ret', False) + return r["dict"].get("ret", False) except Exception: # pylint: disable=broad-except return False def shutdown(opts): - ''' + """ For this proxy shutdown is a no-op - ''' - log.debug('rest_sample proxy shutdown() called...') + """ + log.debug("rest_sample proxy shutdown() called...") def test_from_state(): - ''' + """ Test function so we have something to call from a state :return: - ''' - log.debug('test_from_state called') - return 'testvalue' + """ + log.debug("test_from_state called") + return "testvalue" diff --git a/salt/proxy/ssh_sample.py b/salt/proxy/ssh_sample.py index 9fd70843402..f3ee8ddc9bc 100644 --- a/salt/proxy/ssh_sample.py +++ b/salt/proxy/ssh_sample.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" This is a simple proxy-minion designed to connect to and communicate with a server that exposes functionality via SSH. This can be used as an option when the device does not provide an api over HTTP and doesn't have the python stack to run a minion. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -12,11 +12,11 @@ import logging # Import Salt libs import salt.utils.json -from salt.utils.vt_helper import SSHConnection from salt.utils.vt import TerminalException +from salt.utils.vt_helper import SSHConnection # This must be present or the Salt loader won't load this module -__proxyenabled__ = ['ssh_sample'] +__proxyenabled__ = ["ssh_sample"] DETAILS = {} @@ -27,25 +27,27 @@ log = logging.getLogger(__file__) # This does nothing, it's here just as an example and to provide a log # entry when the module is loaded. def __virtual__(): - ''' + """ Only return if all the modules are available - ''' - log.info('ssh_sample proxy __virtual__() called...') + """ + log.info("ssh_sample proxy __virtual__() called...") return True def init(opts): - ''' + """ Required. Can be used to initialize the server connection. - ''' + """ try: - DETAILS['server'] = SSHConnection(host=__opts__['proxy']['host'], - username=__opts__['proxy']['username'], - password=__opts__['proxy']['password']) - out, err = DETAILS['server'].sendline('help') - DETAILS['initialized'] = True + DETAILS["server"] = SSHConnection( + host=__opts__["proxy"]["host"], + username=__opts__["proxy"]["username"], + password=__opts__["proxy"]["password"], + ) + out, err = DETAILS["server"].sendline("help") + DETAILS["initialized"] = True except TerminalException as e: log.error(e) @@ -53,51 +55,53 @@ def init(opts): def initialized(): - ''' + """ Since grains are loaded in many different places and some of those places occur before the proxy can be initialized, return whether our init() function has been called - ''' - return DETAILS.get('initialized', False) + """ + return DETAILS.get("initialized", False) def grains(): - ''' + """ Get the grains from the proxied device - ''' + """ - if not DETAILS.get('grains_cache', {}): - cmd = 'info' + if not DETAILS.get("grains_cache", {}): + cmd = "info" # Send the command to execute - out, err = DETAILS['server'].sendline(cmd) + out, err = DETAILS["server"].sendline(cmd) # "scrape" the output and return the right fields as a dict - DETAILS['grains_cache'] = parse(out) + DETAILS["grains_cache"] = parse(out) - return DETAILS['grains_cache'] + return DETAILS["grains_cache"] def grains_refresh(): - ''' + """ Refresh the grains from the proxied device - ''' - DETAILS['grains_cache'] = None + """ + DETAILS["grains_cache"] = None return grains() def fns(): - return {'details': 'This key is here because a function in ' - 'grains/ssh_sample.py called fns() here in the proxymodule.'} + return { + "details": "This key is here because a function in " + "grains/ssh_sample.py called fns() here in the proxymodule." + } def ping(): - ''' + """ Required. Ping the device on the other end of the connection - ''' + """ try: - out, err = DETAILS['server'].sendline('help') + out, err = DETAILS["server"].sendline("help") return True except TerminalException as e: log.error(e) @@ -105,131 +109,131 @@ def ping(): def shutdown(opts): - ''' + """ Disconnect - ''' - DETAILS['server'].close_connection() + """ + DETAILS["server"].close_connection() def parse(out): - ''' + """ Extract json from out. Parameter out: Type string. The data returned by the ssh command. - ''' + """ jsonret = [] in_json = False - for ln_ in out.split('\n'): - if '{' in ln_: + for ln_ in out.split("\n"): + if "{" in ln_: in_json = True if in_json: jsonret.append(ln_) - if '}' in ln_: + if "}" in ln_: in_json = False - return salt.utils.json.loads('\n'.join(jsonret)) + return salt.utils.json.loads("\n".join(jsonret)) def package_list(): - ''' + """ List "packages" by executing a command via ssh This function is called in response to the salt command ..code-block::bash salt target_minion pkg.list_pkgs - ''' + """ # Send the command to execute - out, err = DETAILS['server'].sendline('pkg_list\n') + out, err = DETAILS["server"].sendline("pkg_list\n") # "scrape" the output and return the right fields as a dict return parse(out) def package_install(name, **kwargs): - ''' + """ Install a "package" on the ssh server - ''' - cmd = 'pkg_install ' + name - if kwargs.get('version', False): - cmd += ' ' + kwargs['version'] + """ + cmd = "pkg_install " + name + if kwargs.get("version", False): + cmd += " " + kwargs["version"] # Send the command to execute - out, err = DETAILS['server'].sendline(cmd) + out, err = DETAILS["server"].sendline(cmd) # "scrape" the output and return the right fields as a dict return parse(out) def package_remove(name): - ''' + """ Remove a "package" on the ssh server - ''' - cmd = 'pkg_remove ' + name + """ + cmd = "pkg_remove " + name # Send the command to execute - out, err = DETAILS['server'].sendline(cmd) + out, err = DETAILS["server"].sendline(cmd) # "scrape" the output and return the right fields as a dict return parse(out) def service_list(): - ''' + """ Start a "service" on the ssh server .. versionadded:: 2015.8.2 - ''' - cmd = 'ps' + """ + cmd = "ps" # Send the command to execute - out, err = DETAILS['server'].sendline(cmd) + out, err = DETAILS["server"].sendline(cmd) # "scrape" the output and return the right fields as a dict return parse(out) def service_start(name): - ''' + """ Start a "service" on the ssh server .. versionadded:: 2015.8.2 - ''' - cmd = 'start ' + name + """ + cmd = "start " + name # Send the command to execute - out, err = DETAILS['server'].sendline(cmd) + out, err = DETAILS["server"].sendline(cmd) # "scrape" the output and return the right fields as a dict return parse(out) def service_stop(name): - ''' + """ Stop a "service" on the ssh server .. versionadded:: 2015.8.2 - ''' - cmd = 'stop ' + name + """ + cmd = "stop " + name # Send the command to execute - out, err = DETAILS['server'].sendline(cmd) + out, err = DETAILS["server"].sendline(cmd) # "scrape" the output and return the right fields as a dict return parse(out) def service_restart(name): - ''' + """ Restart a "service" on the ssh server .. versionadded:: 2015.8.2 - ''' - cmd = 'restart ' + name + """ + cmd = "restart " + name # Send the command to execute - out, err = DETAILS['server'].sendline(cmd) + out, err = DETAILS["server"].sendline(cmd) # "scrape" the output and return the right fields as a dict return parse(out) diff --git a/salt/proxy/vcenter.py b/salt/proxy/vcenter.py index d09d9416efe..fa1d090bd21 100644 --- a/salt/proxy/vcenter.py +++ b/salt/proxy/vcenter.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Proxy Minion interface module for managing VMWare vCenters. -:codeauthor: :email:`Rod McKenzie (roderick.mckenzie@morganstanley.com)` -:codeauthor: :email:`Alexandru Bleotu (alexandru.bleotu@morganstanley.com)` +:codeauthor: `Rod McKenzie (roderick.mckenzie@morganstanley.com)` +:codeauthor: `Alexandru Bleotu (alexandru.bleotu@morganstanley.com)` Dependencies ============ @@ -67,7 +67,7 @@ To use this proxy module, please use on of the following configurations: principal: <host kerberos principal> proxytype -^^^^^^^^^ +--------- The ``proxytype`` key and value pair is critical, as it tells Salt which interface to load from the ``proxy`` directory in Salt's install hierarchy, or from ``/srv/salt/_proxy`` on the Salt Master (if you have created your @@ -75,47 +75,47 @@ own proxy module, for example). To use this Proxy Module, set this to ``vcenter``. vcenter -^^^^^^^ +------- The location of the VMware vCenter server (host of ip). Required username -^^^^^^^^ +-------- The username used to login to the vcenter, such as ``root``. Required only for userpass. mechanism -^^^^^^^^ +--------- The mechanism used to connect to the vCenter server. Supported values are ``userpass`` and ``sspi``. Required. passwords -^^^^^^^^^ +--------- A list of passwords to be used to try and login to the vCenter server. At least one password in this list is required if mechanism is ``userpass`` The proxy integration will try the passwords listed in order. domain -^^^^^^ +------ User domain. Required if mechanism is ``sspi`` principal -^^^^^^^^ +--------- Kerberos principal. Rquired if mechanism is ``sspi`` protocol -^^^^^^^^ +-------- If the vCenter is not using the default protocol, set this value to an alternate protocol. Default is ``https``. port -^^^^ +---- If the ESXi host is not using the default port, set this value to an alternate port. Default is ``443``. Salt Proxy ----------- +========== After your pillar is in place, you can test the proxy. The proxy can run on any machine that has network connectivity to your Salt Master and to the @@ -173,17 +173,18 @@ It's important to understand how this particular proxy works. :mod:`Salt.modules.vsphere </ref/modules/all/salt.modules.vsphere>` is a standard Salt execution module. - If you pull up the docs for it you'll see -that almost every function in the module takes credentials and a targets either -a vcenter or a host. When credentials and a host aren't passed, Salt runs commands -through ``pyVmomi`` against the local machine. If you wanted, you could run -functions from this module on any host where an appropriate version of -``pyVmomi`` is installed, and that host would reach out over the network -and communicate with the ESXi host. -''' +If you pull up the docs for it you'll see that almost every function in the +module takes credentials and a targets either a vcenter or a host. When +credentials and a host aren't passed, Salt runs commands through ``pyVmomi`` +against the local machine. If you wanted, you could run functions from this +module on any host where an appropriate version of ``pyVmomi`` is installed, +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 @@ -193,11 +194,12 @@ from salt.config.schemas.vcenter import VCenterProxySchema from salt.utils.dictupdate import merge # This must be present or the Salt loader won't load this module. -__proxyenabled__ = ['vcenter'] +__proxyenabled__ = ["vcenter"] # External libraries try: import jsonschema + HAS_JSONSCHEMA = True except ImportError: HAS_JSONSCHEMA = False @@ -211,84 +213,90 @@ DETAILS = {} # Set up logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'vcenter' +__virtualname__ = "vcenter" def __virtual__(): - ''' + """ Only load if the vsphere execution module is available. - ''' + """ if HAS_JSONSCHEMA: return __virtualname__ - return False, 'The vcenter proxy module did not load.' + return False, "The vcenter proxy module did not load." def init(opts): - ''' + """ This function gets called when the proxy starts up. For login the protocol and port are cached. - ''' - log.info('Initting vcenter proxy module in process %s', os.getpid()) - log.trace('VCenter Proxy Validating vcenter proxy input') + """ + log.info("Initting vcenter proxy module in process %s", os.getpid()) + log.trace("VCenter Proxy Validating vcenter proxy input") schema = VCenterProxySchema.serialize() - log.trace('schema = %s', schema) - proxy_conf = merge(opts.get('proxy', {}), __pillar__.get('proxy', {})) - log.trace('proxy_conf = %s', proxy_conf) + log.trace("schema = %s", schema) + proxy_conf = merge(opts.get("proxy", {}), __pillar__.get("proxy", {})) + log.trace("proxy_conf = %s", proxy_conf) try: jsonschema.validate(proxy_conf, schema) except jsonschema.exceptions.ValidationError as exc: raise salt.exceptions.InvalidConfigError(exc) # Save mandatory fields in cache - for key in ('vcenter', 'mechanism'): + for key in ("vcenter", "mechanism"): DETAILS[key] = proxy_conf[key] # Additional validation - if DETAILS['mechanism'] == 'userpass': - if 'username' not in proxy_conf: + if DETAILS["mechanism"] == "userpass": + if "username" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'userpass\' , but no ' - '\'username\' key found in proxy config') - if 'passwords' not in proxy_conf: + "Mechanism is set to 'userpass' , but no " + "'username' key found in proxy config" + ) + if "passwords" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'userpass\' , but no ' - '\'passwords\' key found in proxy config') - for key in ('username', 'passwords'): + "Mechanism is set to 'userpass' , but no " + "'passwords' key found in proxy config" + ) + for key in ("username", "passwords"): DETAILS[key] = proxy_conf[key] else: - if 'domain' not in proxy_conf: + if "domain" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'sspi\' , but no ' - '\'domain\' key found in proxy config') - if 'principal' not in proxy_conf: + "Mechanism is set to 'sspi' , but no " + "'domain' key found in proxy config" + ) + if "principal" not in proxy_conf: raise salt.exceptions.InvalidConfigError( - 'Mechanism is set to \'sspi\' , but no ' - '\'principal\' key found in proxy config') - for key in ('domain', 'principal'): + "Mechanism is set to 'sspi' , but no " + "'principal' key found in proxy config" + ) + for key in ("domain", "principal"): DETAILS[key] = proxy_conf[key] # Save optional - DETAILS['protocol'] = proxy_conf.get('protocol') - DETAILS['port'] = proxy_conf.get('port') + DETAILS["protocol"] = proxy_conf.get("protocol") + DETAILS["port"] = proxy_conf.get("port") # Test connection - if DETAILS['mechanism'] == 'userpass': + if DETAILS["mechanism"] == "userpass": # Get the correct login details - log.info('Retrieving credentials and testing vCenter connection for ' - 'mehchanism \'userpass\'') + log.info( + "Retrieving credentials and testing vCenter connection for " + "mehchanism 'userpass'" + ) try: username, password = find_credentials() except salt.exceptions.SaltSystemExit as err: - log.critical('Error: %s', err) + log.critical("Error: %s", err) return False else: - DETAILS['password'] = password + DETAILS["password"] = password return True def ping(): - ''' + """ Returns True. CLI Example: @@ -296,44 +304,45 @@ def ping(): .. code-block:: bash salt vcenter test.ping - ''' + """ return True def shutdown(): - ''' + """ Shutdown the connection to the proxy device. For this proxy, shutdown is a no-op. - ''' - log.debug('VCenter proxy shutdown() called...') + """ + log.debug("VCenter proxy shutdown() called...") def find_credentials(): - ''' + """ Cycle through all the possible credentials and return the first one that works. - ''' + """ # if the username and password were already found don't fo though the # connection process again - if 'username' in DETAILS and 'password' in DETAILS: - return DETAILS['username'], DETAILS['password'] + if "username" in DETAILS and "password" in DETAILS: + return DETAILS["username"], DETAILS["password"] - passwords = __pillar__['proxy']['passwords'] + passwords = __pillar__["proxy"]["passwords"] for password in passwords: - DETAILS['password'] = password - if not __salt__['vsphere.test_vcenter_connection'](): + DETAILS["password"] = password + if not __salt__["vsphere.test_vcenter_connection"](): # We are unable to authenticate continue # If we have data returned from above, we've successfully authenticated. - return DETAILS['username'], password + return DETAILS["username"], password # We've reached the end of the list without successfully authenticating. - raise salt.exceptions.VMwareConnectionError('Cannot complete login due to ' - 'incorrect credentials.') + raise salt.exceptions.VMwareConnectionError( + "Cannot complete login due to " "incorrect credentials." + ) def get_details(): - ''' + """ Function that returns the cached details - ''' + """ return DETAILS diff --git a/salt/queues/__init__.py b/salt/queues/__init__.py index 76075735ae3..b463dc02fac 100644 --- a/salt/queues/__init__.py +++ b/salt/queues/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Queues Directory -''' +""" diff --git a/salt/queues/pgjsonb_queue.py b/salt/queues/pgjsonb_queue.py index fbb3e23b4b7..cb148fd7294 100644 --- a/salt/queues/pgjsonb_queue.py +++ b/salt/queues/pgjsonb_queue.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" .. versionadded:: 2016.3.0 This is a queue with postgres as the backend. It uses the jsonb store to @@ -37,29 +37,32 @@ Use the following Pg database schema: salt-run queue.insert test '{"name": "redis", "host": "172.16.0.8", "port": 6379}' backend=pgjsonb salt-run queue.process_queue test all backend=pgjsonb -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from contextlib import contextmanager + +import logging import sys +from contextlib import contextmanager # import salt libs import salt.utils.json -from salt.ext import six from salt.exceptions import SaltInvocationError, SaltMasterError +from salt.ext import six try: import psycopg2 + HAS_PG = True except ImportError: HAS_PG = False -import logging + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pgjsonb' +__virtualname__ = "pgjsonb" def __virtual__(): @@ -70,22 +73,28 @@ def __virtual__(): @contextmanager def _conn(commit=False): - ''' + """ Return an postgres cursor - ''' - defaults = {'host': 'localhost', - 'user': 'salt', - 'password': 'salt', - 'dbname': 'salt', - 'port': 5432} + """ + defaults = { + "host": "localhost", + "user": "salt", + "password": "salt", + "dbname": "salt", + "port": 5432, + } conn_kwargs = {} for key, value in defaults.items(): - conn_kwargs[key] = __opts__.get('queue.{0}.{1}'.format(__virtualname__, key), value) + conn_kwargs[key] = __opts__.get( + "queue.{0}.{1}".format(__virtualname__, key), value + ) try: conn = psycopg2.connect(**conn_kwargs) except psycopg2.OperationalError as exc: - raise SaltMasterError('pgjsonb returner could not connect to database: {exc}'.format(exc=exc)) + raise SaltMasterError( + "pgjsonb returner could not connect to database: {exc}".format(exc=exc) + ) cursor = conn.cursor() @@ -107,141 +116,153 @@ def _conn(commit=False): def _list_tables(cur): cmd = "select relname from pg_class where relkind='r' and relname !~ '^(pg_|sql_)';" - log.debug('SQL Query: %s', cmd) + log.debug("SQL Query: %s", cmd) cur.execute(cmd) result = cur.fetchall() return [x[0] for x in result] def _create_table(cur, queue): - cmd = 'CREATE TABLE {0}(id SERIAL PRIMARY KEY, '\ - 'data jsonb NOT NULL)'.format(queue) - log.debug('SQL Query: %s', cmd) + cmd = "CREATE TABLE {0}(id SERIAL PRIMARY KEY, " "data jsonb NOT NULL)".format( + queue + ) + log.debug("SQL Query: %s", cmd) cur.execute(cmd) return True def _list_items(queue): - ''' + """ Private function to list contents of a queue - ''' + """ with _conn() as cur: - cmd = 'SELECT data FROM {0}'.format(queue) - log.debug('SQL Query: %s', cmd) + cmd = "SELECT data FROM {0}".format(queue) + log.debug("SQL Query: %s", cmd) cur.execute(cmd) contents = cur.fetchall() return contents def list_queues(): - ''' + """ Return a list of Salt Queues on the Salt Master - ''' + """ with _conn() as cur: queues = _list_tables(cur) return queues def list_items(queue): - ''' + """ List contents of a queue - ''' + """ itemstuple = _list_items(queue) items = [item[0] for item in itemstuple] return items def list_length(queue): - ''' + """ Provide the number of items in a queue - ''' + """ items = _list_items(queue) return len(items) def _queue_exists(queue): - ''' + """ Does this queue exist :param queue: Name of the queue :type str :return: True if this queue exists and False otherwise :rtype bool - ''' + """ return queue in list_queues() def handle_queue_creation(queue): if not _queue_exists(queue): with _conn(commit=True) as cur: - log.debug('Queue %s does not exist. Creating', queue) + log.debug("Queue %s does not exist. Creating", queue) _create_table(cur, queue) else: - log.debug('Queue %s already exists.', queue) + log.debug("Queue %s already exists.", queue) def insert(queue, items): - ''' + """ Add an item or items to a queue - ''' + """ handle_queue_creation(queue) with _conn(commit=True) as cur: if isinstance(items, dict): items = salt.utils.json.dumps(items) - cmd = str('''INSERT INTO {0}(data) VALUES('{1}')''').format(queue, items) # future lint: disable=blacklisted-function - log.debug('SQL Query: %s', cmd) + cmd = str("""INSERT INTO {0}(data) VALUES('{1}')""").format( + queue, items + ) # future lint: disable=blacklisted-function + log.debug("SQL Query: %s", cmd) try: cur.execute(cmd) except psycopg2.IntegrityError as esc: - return ('Item already exists in this queue. ' - 'postgres error: {0}'.format(esc)) + return ( + "Item already exists in this queue. " + "postgres error: {0}".format(esc) + ) if isinstance(items, list): items = [(salt.utils.json.dumps(el),) for el in items] - cmd = str("INSERT INTO {0}(data) VALUES (%s)").format(queue) # future lint: disable=blacklisted-function - log.debug('SQL Query: %s', cmd) + cmd = str("INSERT INTO {0}(data) VALUES (%s)").format( + queue + ) # future lint: disable=blacklisted-function + log.debug("SQL Query: %s", cmd) try: cur.executemany(cmd, items) except psycopg2.IntegrityError as esc: - return ('One or more items already exists in this queue. ' - 'postgres error: {0}'.format(esc)) + return ( + "One or more items already exists in this queue. " + "postgres error: {0}".format(esc) + ) return True def delete(queue, items): - ''' + """ Delete an item or items from a queue - ''' + """ with _conn(commit=True) as cur: if isinstance(items, dict): - cmd = str("""DELETE FROM {0} WHERE data = '{1}'""").format( # future lint: disable=blacklisted-function - queue, - salt.utils.json.dumps(items)) - log.debug('SQL Query: %s', cmd) + cmd = str( + """DELETE FROM {0} WHERE data = '{1}'""" + ).format( # future lint: disable=blacklisted-function + queue, salt.utils.json.dumps(items) + ) + log.debug("SQL Query: %s", cmd) cur.execute(cmd) return True if isinstance(items, list): items = [(salt.utils.json.dumps(el),) for el in items] - cmd = 'DELETE FROM {0} WHERE data = %s'.format(queue) - log.debug('SQL Query: %s', cmd) + cmd = "DELETE FROM {0} WHERE data = %s".format(queue) + log.debug("SQL Query: %s", cmd) cur.executemany(cmd, items) return True def pop(queue, quantity=1, is_runner=False): - ''' + """ Pop one or more or all items from the queue return them. - ''' - cmd = 'SELECT id, data FROM {0}'.format(queue) - if quantity != 'all': + """ + cmd = "SELECT id, data FROM {0}".format(queue) + if quantity != "all": try: quantity = int(quantity) except ValueError as exc: - error_txt = ('Quantity must be an integer or "all".\n' - 'Error: "{0}".'.format(exc)) + error_txt = ( + 'Quantity must be an integer or "all".\n' 'Error: "{0}".'.format(exc) + ) raise SaltInvocationError(error_txt) - cmd = ''.join([cmd, ' LIMIT {0};'.format(quantity)]) - log.debug('SQL Query: %s', cmd) + cmd = "".join([cmd, " LIMIT {0};".format(quantity)]) + log.debug("SQL Query: %s", cmd) items = [] with _conn(commit=True) as cur: cur.execute(cmd) @@ -250,10 +271,9 @@ def pop(queue, quantity=1, is_runner=False): ids = [six.text_type(item[0]) for item in result] items = [item[1] for item in result] idlist = "','".join(ids) - del_cmd = '''DELETE FROM {0} WHERE id IN ('{1}');'''.format( - queue, idlist) + del_cmd = """DELETE FROM {0} WHERE id IN ('{1}');""".format(queue, idlist) - log.debug('SQL Query: %s', del_cmd) + log.debug("SQL Query: %s", del_cmd) cur.execute(del_cmd) return items diff --git a/salt/queues/sqlite_queue.py b/salt/queues/sqlite_queue.py index 3238ffd55ea..ae8a0c7e09a 100644 --- a/salt/queues/sqlite_queue.py +++ b/salt/queues/sqlite_queue.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" .. versionadded:: 2014.7.0 This is the default local master event queue built on sqlite. By default, an @@ -11,14 +11,16 @@ It's possible to store the sqlite3 database files by setting `sqlite_queue_dir` to another location:: sqlite_queue_dir: /home/myuser/salt/master/queues -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import glob import logging import os import re import sqlite3 + import salt.utils.json from salt.exceptions import SaltInvocationError @@ -28,7 +30,7 @@ from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'sqlite' +__virtualname__ = "sqlite" def __virtual__(): @@ -38,12 +40,12 @@ def __virtual__(): def _conn(queue): - ''' + """ Return an sqlite connection - ''' - queue_dir = __opts__['sqlite_queue_dir'] - db = os.path.join(queue_dir, '{0}.db'.format(queue)) - log.debug('Connecting to: %s', db) + """ + queue_dir = __opts__["sqlite_queue_dir"] + db = os.path.join(queue_dir, "{0}.db".format(queue)) + log.debug("Connecting to: %s", db) con = sqlite3.connect(db) tables = _list_tables(con) @@ -56,7 +58,7 @@ def _list_tables(con): with con: cur = con.cursor() cmd = 'SELECT name FROM sqlite_master WHERE type = "table"' - log.debug('SQL Query: %s', cmd) + log.debug("SQL Query: %s", cmd) cur.execute(cmd) result = cur.fetchall() return [x[0] for x in result] @@ -65,33 +67,34 @@ def _list_tables(con): def _create_table(con, queue): with con: cur = con.cursor() - cmd = 'CREATE TABLE {0}(id INTEGER PRIMARY KEY, '\ - 'name TEXT UNIQUE)'.format(queue) - log.debug('SQL Query: %s', cmd) + cmd = "CREATE TABLE {0}(id INTEGER PRIMARY KEY, " "name TEXT UNIQUE)".format( + queue + ) + log.debug("SQL Query: %s", cmd) cur.execute(cmd) return True def _list_items(queue): - ''' + """ Private function to list contents of a queue - ''' + """ con = _conn(queue) with con: cur = con.cursor() - cmd = 'SELECT name FROM {0}'.format(queue) - log.debug('SQL Query: %s', cmd) + cmd = "SELECT name FROM {0}".format(queue) + log.debug("SQL Query: %s", cmd) cur.execute(cmd) contents = cur.fetchall() return contents def _list_queues(): - ''' + """ Return a list of sqlite databases in the queue_dir - ''' - queue_dir = __opts__['sqlite_queue_dir'] - files = os.path.join(queue_dir, '*.db') + """ + queue_dir = __opts__["sqlite_queue_dir"] + files = os.path.join(queue_dir, "*.db") paths = glob.glob(files) queues = [os.path.splitext(os.path.basename(item))[0] for item in paths] @@ -99,35 +102,35 @@ def _list_queues(): def list_queues(): - ''' + """ Return a list of Salt Queues on the Salt Master - ''' + """ queues = _list_queues() return queues def list_items(queue): - ''' + """ List contents of a queue - ''' + """ itemstuple = _list_items(queue) items = [item[0] for item in itemstuple] return items def list_length(queue): - ''' + """ Provide the number of items in a queue - ''' + """ items = _list_items(queue) return len(items) def _quote_escape(item): - ''' + """ Make sure single quotes are escaped properly in sqlite3 fashion. e.g.: ' becomes '' - ''' + """ rex_sqlquote = re.compile("'", re.M) @@ -135,25 +138,26 @@ def _quote_escape(item): def insert(queue, items): - ''' + """ Add an item or items to a queue - ''' + """ con = _conn(queue) with con: cur = con.cursor() if isinstance(items, six.string_types): items = _quote_escape(items) - cmd = '''INSERT INTO {0}(name) VALUES('{1}')'''.format(queue, items) - log.debug('SQL Query: %s', cmd) + cmd = """INSERT INTO {0}(name) VALUES('{1}')""".format(queue, items) + log.debug("SQL Query: %s", cmd) try: cur.execute(cmd) except sqlite3.IntegrityError as esc: - return('Item already exists in this queue. ' - 'sqlite error: {0}'.format(esc)) + return "Item already exists in this queue. " "sqlite error: {0}".format( + esc + ) if isinstance(items, list): items = [_quote_escape(el) for el in items] cmd = "INSERT INTO {0}(name) VALUES(?)".format(queue) - log.debug('SQL Query: %s', cmd) + log.debug("SQL Query: %s", cmd) newitems = [] for item in items: newitems.append((item,)) @@ -161,38 +165,43 @@ def insert(queue, items): try: cur.executemany(cmd, newitems) except sqlite3.IntegrityError as esc: - return('One or more items already exists in this queue. ' - 'sqlite error: {0}'.format(esc)) + return ( + "One or more items already exists in this queue. " + "sqlite error: {0}".format(esc) + ) if isinstance(items, dict): items = salt.utils.json.dumps(items).replace('"', "'") items = _quote_escape(items) - cmd = str('''INSERT INTO {0}(name) VALUES('{1}')''').format(queue, items) # future lint: disable=blacklisted-function - log.debug('SQL Query: %s', cmd) + cmd = str("""INSERT INTO {0}(name) VALUES('{1}')""").format( + queue, items + ) # future lint: disable=blacklisted-function + log.debug("SQL Query: %s", cmd) try: cur.execute(cmd) except sqlite3.IntegrityError as esc: - return('Item already exists in this queue. ' - 'sqlite error: {0}'.format(esc)) + return "Item already exists in this queue. " "sqlite error: {0}".format( + esc + ) return True def delete(queue, items): - ''' + """ Delete an item or items from a queue - ''' + """ con = _conn(queue) with con: cur = con.cursor() if isinstance(items, six.string_types): items = _quote_escape(items) cmd = """DELETE FROM {0} WHERE name = '{1}'""".format(queue, items) - log.debug('SQL Query: %s', cmd) + log.debug("SQL Query: %s", cmd) cur.execute(cmd) return True if isinstance(items, list): items = [_quote_escape(el) for el in items] - cmd = 'DELETE FROM {0} WHERE name = ?'.format(queue) - log.debug('SQL Query: %s', cmd) + cmd = "DELETE FROM {0} WHERE name = ?".format(queue) + log.debug("SQL Query: %s", cmd) newitems = [] for item in items: newitems.append((item,)) @@ -201,27 +210,30 @@ def delete(queue, items): if isinstance(items, dict): items = salt.utils.json.dumps(items).replace('"', "'") items = _quote_escape(items) - cmd = ("""DELETE FROM {0} WHERE name = '{1}'""").format(queue, items) # future lint: disable=blacklisted-function - log.debug('SQL Query: %s', cmd) + cmd = ("""DELETE FROM {0} WHERE name = '{1}'""").format( + queue, items + ) # future lint: disable=blacklisted-function + log.debug("SQL Query: %s", cmd) cur.execute(cmd) return True return True def pop(queue, quantity=1, is_runner=False): - ''' + """ Pop one or more or all items from the queue return them. - ''' - cmd = 'SELECT name FROM {0}'.format(queue) - if quantity != 'all': + """ + cmd = "SELECT name FROM {0}".format(queue) + if quantity != "all": try: quantity = int(quantity) except ValueError as exc: - error_txt = ('Quantity must be an integer or "all".\n' - 'Error: "{0}".'.format(exc)) + error_txt = ( + 'Quantity must be an integer or "all".\n' 'Error: "{0}".'.format(exc) + ) raise SaltInvocationError(error_txt) - cmd = ''.join([cmd, ' LIMIT {0}'.format(quantity)]) - log.debug('SQL Query: %s', cmd) + cmd = "".join([cmd, " LIMIT {0}".format(quantity)]) + log.debug("SQL Query: %s", cmd) con = _conn(queue) items = [] with con: @@ -231,10 +243,11 @@ def pop(queue, quantity=1, is_runner=False): items = [item[0] for item in result] itemlist = '","'.join(items) _quote_escape(itemlist) - del_cmd = '''DELETE FROM {0} WHERE name IN ("{1}")'''.format( - queue, itemlist) + del_cmd = """DELETE FROM {0} WHERE name IN ("{1}")""".format( + queue, itemlist + ) - log.debug('SQL Query: %s', del_cmd) + log.debug("SQL Query: %s", del_cmd) cur.execute(del_cmd) con.commit() diff --git a/salt/renderers/__init__.py b/salt/renderers/__init__.py index 21a709fbed1..a6bb1dc3bbb 100644 --- a/salt/renderers/__init__.py +++ b/salt/renderers/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Renderers Directory -''' +""" diff --git a/salt/renderers/aws_kms.py b/salt/renderers/aws_kms.py index 19d0e9bc323..8620c4e8b92 100644 --- a/salt/renderers/aws_kms.py +++ b/salt/renderers/aws_kms.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -r''' -Renderer that will decrypt ciphers encrypted using `AWS KMS Envelope Encryption`_. - +r""" .. _`AWS KMS Envelope Encryption`: https://docs.aws.amazon.com/kms/latest/developerguide/workflow.html +Renderer that will decrypt ciphers encrypted using `AWS KMS Envelope Encryption`_. + Any key in the data to be rendered can be a urlsafe_b64encoded string, and this renderer will attempt to decrypt it before passing it off to Salt. This allows you to safely store secrets in source control, in such a way that only your Salt master can decrypt them and @@ -61,12 +61,13 @@ data like so: #!yaml|aws_kms a-secret: gAAAAABaj5uzShPI3PEz6nL5Vhk2eEHxGXSZj8g71B84CZsVjAAtDFY1mfjNRl-1Su9YVvkUzNjI4lHCJJfXqdcTvwczBYtKy0Pa7Ri02s10Wn1tF0tbRwk= -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import logging + import base64 +import logging # Import salt libs import salt.utils.stringio @@ -77,22 +78,24 @@ from salt.ext import six try: import botocore.exceptions import boto3 - logging.getLogger('boto3').setLevel(logging.CRITICAL) + + logging.getLogger("boto3").setLevel(logging.CRITICAL) except ImportError: pass try: import cryptography.fernet as fernet + HAS_FERNET = True except ImportError: HAS_FERNET = False def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ return HAS_FERNET and salt.utils.versions.check_boto_reqs() @@ -100,30 +103,30 @@ log = logging.getLogger(__name__) def _cfg(key, default=None): - ''' + """ Return the requested value from the aws_kms key in salt configuration. If it's not set, return the default. - ''' - root_cfg = __salt__.get('config.get', __opts__.get) - kms_cfg = root_cfg('aws_kms', {}) + """ + root_cfg = __salt__.get("config.get", __opts__.get) + kms_cfg = root_cfg("aws_kms", {}) return kms_cfg.get(key, default) def _cfg_data_key(): - ''' + """ Return the encrypted KMS data key from configuration. Raises SaltConfigurationError if not set. - ''' - data_key = _cfg('data_key', '') + """ + data_key = _cfg("data_key", "") if data_key: return data_key - raise salt.exceptions.SaltConfigurationError('aws_kms:data_key is not set') + raise salt.exceptions.SaltConfigurationError("aws_kms:data_key is not set") def _session(): - ''' + """ Return the boto3 session to use for the KMS client. If aws_kms:profile_name is set in the salt configuration, use that profile. @@ -131,130 +134,134 @@ def _session(): We use the boto3 profile system to avoid having to duplicate individual boto3 configuration settings in salt configuration. - ''' - profile_name = _cfg('profile_name') + """ + profile_name = _cfg("profile_name") if profile_name: log.info('Using the "%s" aws profile.', profile_name) else: - log.info('aws_kms:profile_name is not set in salt. Falling back on default profile.') + log.info( + "aws_kms:profile_name is not set in salt. Falling back on default profile." + ) try: return boto3.Session(profile_name=profile_name) except botocore.exceptions.ProfileNotFound as orig_exc: err_msg = 'Boto3 could not find the "{}" profile configured in Salt.'.format( - profile_name or 'default') + profile_name or "default" + ) config_error = salt.exceptions.SaltConfigurationError(err_msg) six.raise_from(config_error, orig_exc) except botocore.exceptions.NoRegionError as orig_exc: - err_msg = ('Boto3 was unable to determine the AWS ' - 'endpoint region using the {} profile.').format(profile_name or 'default') + err_msg = ( + "Boto3 was unable to determine the AWS " + "endpoint region using the {} profile." + ).format(profile_name or "default") config_error = salt.exceptions.SaltConfigurationError(err_msg) six.raise_from(config_error, orig_exc) def _kms(): - ''' + """ Return the boto3 client for the KMS API. - ''' + """ session = _session() - return session.client('kms') + return session.client("kms") def _api_decrypt(): - ''' + """ Return the response dictionary from the KMS decrypt API call. - ''' + """ kms = _kms() data_key = _cfg_data_key() try: return kms.decrypt(CiphertextBlob=data_key) except botocore.exceptions.ClientError as orig_exc: - error_code = orig_exc.response.get('Error', {}).get('Code', '') - if error_code != 'InvalidCiphertextException': + error_code = orig_exc.response.get("Error", {}).get("Code", "") + if error_code != "InvalidCiphertextException": raise - err_msg = 'aws_kms:data_key is not a valid KMS data key' + err_msg = "aws_kms:data_key is not a valid KMS data key" config_error = salt.exceptions.SaltConfigurationError(err_msg) six.raise_from(config_error, orig_exc) def _plaintext_data_key(): - ''' + """ Return the configured KMS data key decrypted and encoded in urlsafe base64. Cache the result to minimize API calls to AWS. - ''' - response = getattr(_plaintext_data_key, 'response', None) + """ + response = getattr(_plaintext_data_key, "response", None) cache_hit = response is not None if not cache_hit: response = _api_decrypt() - setattr(_plaintext_data_key, 'response', response) - key_id = response['KeyId'] - plaintext = response['Plaintext'] - if hasattr(plaintext, 'encode'): + setattr(_plaintext_data_key, "response", response) + key_id = response["KeyId"] + plaintext = response["Plaintext"] + if hasattr(plaintext, "encode"): plaintext = plaintext.encode(__salt_system_encoding__) - log.debug('Using key %s from %s', key_id, 'cache' if cache_hit else 'api call') + log.debug("Using key %s from %s", key_id, "cache" if cache_hit else "api call") return plaintext def _base64_plaintext_data_key(): - ''' + """ Return the configured KMS data key decrypted and encoded in urlsafe base64. - ''' + """ plaintext_data_key = _plaintext_data_key() return base64.urlsafe_b64encode(plaintext_data_key) def _decrypt_ciphertext(cipher, translate_newlines=False): - ''' + """ Given a blob of ciphertext as a bytestring, try to decrypt the cipher and return the decrypted string. If the cipher cannot be decrypted, log the error, and return the ciphertext back out. - ''' + """ if translate_newlines: - cipher = cipher.replace(r'\n', '\n') - if hasattr(cipher, 'encode'): + cipher = cipher.replace(r"\n", "\n") + if hasattr(cipher, "encode"): cipher = cipher.encode(__salt_system_encoding__) # Decryption data_key = _base64_plaintext_data_key() plain_text = fernet.Fernet(data_key).decrypt(cipher) - if hasattr(plain_text, 'decode'): + if hasattr(plain_text, "decode"): plain_text = plain_text.decode(__salt_system_encoding__) return six.text_type(plain_text) def _decrypt_object(obj, translate_newlines=False): - ''' + """ Recursively try to decrypt any object. Recur on objects that are not strings. Decrypt strings that are valid Fernet tokens. Return the rest unchanged. - ''' + """ if salt.utils.stringio.is_readable(obj): return _decrypt_object(obj.getvalue(), translate_newlines) if isinstance(obj, six.string_types): try: - return _decrypt_ciphertext(obj, - translate_newlines=translate_newlines) + return _decrypt_ciphertext(obj, translate_newlines=translate_newlines) except (fernet.InvalidToken, TypeError): return obj elif isinstance(obj, dict): for key, value in six.iteritems(obj): - obj[key] = _decrypt_object(value, - translate_newlines=translate_newlines) + obj[key] = _decrypt_object(value, translate_newlines=translate_newlines) return obj elif isinstance(obj, list): for key, value in enumerate(obj): - obj[key] = _decrypt_object(value, - translate_newlines=translate_newlines) + obj[key] = _decrypt_object(value, translate_newlines=translate_newlines) return obj else: return obj -def render(data, saltenv='base', sls='', argline='', **kwargs): # pylint: disable=unused-argument - ''' +def render( + data, saltenv="base", sls="", argline="", **kwargs +): # pylint: disable=unused-argument + """ Decrypt the data to be rendered that was encrypted using AWS KMS envelope encryption. - ''' - translate_newlines = kwargs.get('translate_newlines', False) + """ + translate_newlines = kwargs.get("translate_newlines", False) return _decrypt_object(data, translate_newlines=translate_newlines) diff --git a/salt/renderers/cheetah.py b/salt/renderers/cheetah.py index 8374d265c14..35c956b8230 100644 --- a/salt/renderers/cheetah.py +++ b/salt/renderers/cheetah.py @@ -1,35 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" Cheetah Renderer for Salt -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import 3rd party libs -try: - from Cheetah.Template import Template - HAS_LIBS = True -except ImportError: - HAS_LIBS = False - # Import salt libs from salt.ext import six +# Import 3rd party libs +try: + from Cheetah.Template import Template -def render(cheetah_data, saltenv='base', sls='', method='xml', **kws): - ''' + HAS_LIBS = True +except ImportError: + HAS_LIBS = False + + +def render(cheetah_data, saltenv="base", sls="", method="xml", **kws): + """ Render a Cheetah template. :rtype: A Python data structure - ''' + """ if not HAS_LIBS: return {} if not isinstance(cheetah_data, six.string_types): cheetah_data = cheetah_data.read() - if cheetah_data.startswith('#!'): - cheetah_data = cheetah_data[(cheetah_data.find('\n') + 1):] + if cheetah_data.startswith("#!"): + cheetah_data = cheetah_data[(cheetah_data.find("\n") + 1) :] if not cheetah_data.strip(): return {} diff --git a/salt/renderers/dson.py b/salt/renderers/dson.py index 6adf99acf14..01ec7890d00 100644 --- a/salt/renderers/dson.py +++ b/salt/renderers/dson.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" DSON Renderer for Salt This renderer is intended for demonstration purposes. Information on the DSON @@ -10,44 +10,45 @@ spec can be found `here`__. This renderer requires `Dogeon`__ (installable via pip) .. __: https://github.com/soasme/dogeon -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging +# Import salt libs +from salt.ext import six + try: import dson except ImportError: dson = None -# Import salt libs -from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): if dson is None: - return (False, 'The dogeon Python package is not installed') + return (False, "The dogeon Python package is not installed") return True -def render(dson_input, saltenv='base', sls='', **kwargs): - ''' +def render(dson_input, saltenv="base", sls="", **kwargs): + """ Accepts DSON data as a string or as a file object and runs it through the JSON parser. :rtype: A Python data structure - ''' + """ if not isinstance(dson_input, six.string_types): dson_input = dson_input.read() - log.debug('DSON input = %s', dson_input) + log.debug("DSON input = %s", dson_input) - if dson_input.startswith('#!'): - dson_input = dson_input[(dson_input.find('\n') + 1):] + if dson_input.startswith("#!"): + dson_input = dson_input[(dson_input.find("\n") + 1) :] if not dson_input.strip(): return {} return dson.loads(dson_input) diff --git a/salt/renderers/genshi.py b/salt/renderers/genshi.py index 469e8aefc11..76a241e0767 100644 --- a/salt/renderers/genshi.py +++ b/salt/renderers/genshi.py @@ -1,25 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" Genshi Renderer for Salt -''' +""" from __future__ import absolute_import, print_function, unicode_literals +# Import salt libs +from salt.ext import six + # Import 3rd party libs try: from genshi.template import MarkupTemplate from genshi.template import NewTextTemplate from genshi.template import OldTextTemplate + HAS_LIBS = True except ImportError: HAS_LIBS = False -# Import salt libs -from salt.ext import six - -def render(genshi_data, saltenv='base', sls='', method='xml', **kws): - ''' +def render(genshi_data, saltenv="base", sls="", method="xml", **kws): + """ Render a Genshi template. A method should be passed in as part of the kwargs. If no method is passed in, xml is assumed. Valid methods are: @@ -36,21 +37,21 @@ def render(genshi_data, saltenv='base', sls='', method='xml', **kws): is desired, it must be called explicitly :rtype: A Python data structure - ''' + """ if not HAS_LIBS: return {} if not isinstance(genshi_data, six.string_types): genshi_data = genshi_data.read() - if genshi_data.startswith('#!'): - genshi_data = genshi_data[(genshi_data.find('\n') + 1):] + if genshi_data.startswith("#!"): + genshi_data = genshi_data[(genshi_data.find("\n") + 1) :] if not genshi_data.strip(): return {} - if method == 'text' or method == 'newtext': + if method == "text" or method == "newtext": tmpl = NewTextTemplate(genshi_data) - elif method == 'oldtext': + elif method == "oldtext": tmpl = OldTextTemplate(genshi_data) else: tmpl = MarkupTemplate(genshi_data) diff --git a/salt/renderers/gpg.py b/salt/renderers/gpg.py index e861da81615..c6ab0e6e55a 100644 --- a/salt/renderers/gpg.py +++ b/salt/renderers/gpg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Renderer that will decrypt GPG ciphers Any key in the SLS file can be a GPG cipher, and this renderer will decrypt it @@ -37,6 +37,56 @@ your application. Be sure to back up the ``gpgkeys`` directory someplace safe! increasing the system entropy. On virtualised Linux systems, this can often be achieved by installing the ``rng-tools`` package. +Import keys to a master +************************ + +If the keys already exist and need to be imported to the salt master, run the +following to import them. + +.. code-block:: bash + + gpg --homedir /etc/salt/gpgkeys --import /path/to/private.key + gpg --import /path/to/pubkey.gpg + +If the salt master runs as normal user, become this user before importing the +keys. The default target dir will be ``~/.gnupg``. This can be overridden by +the ``--homedir`` option. The keys must be at least readable for the runuser. + +Adjust trust level of imported keys +*********************************** + +In some cases, importing existing keys may not be enough and the trust level of +the key needs to be adjusted. This can be done by editing the key. The ``key_id`` +and the actual trust level of the key can be seen by listing the already imported +keys. + +.. code-block:: bash + + gpg --homedir /etc/salt/gpgkeys --list-keys + gpg --list-secret-keys + +If the trust-level is not ``ultimate`` it needs to be changed by running + +.. code-block:: bash + + gpg --homedir /etc/salt/gpgkeys --edit-key <key_id> + +This will open an interactive shell for the management of the GPG encrypted key. Type +``trust`` to be able to set the trust level for the key and then select +``5 (I trust ultimately)``. Then quit the shell by typing ``save``. + +Enable usage of GPG keys on the master +************************************** + +Generating or importing the keys is not enough to activate the ability to decrypt +the pillars, especially if the keys are generated/imported in a non-standard dir. + +To enable the keys on the salt-master, the following needs to be added to the +masters configuration. + +.. code-block:: yaml + + gpg_keydir: <path/to/homedir> Export the Public Key --------------------- @@ -206,20 +256,22 @@ pillar data like so: .. code-block:: bash salt myminion state.sls secretstuff pillar_enc=gpg pillar="$ciphertext" -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import re -import logging -from subprocess import Popen, PIPE +from subprocess import PIPE, Popen + +import salt.syspaths # Import salt libs import salt.utils.path import salt.utils.stringio import salt.utils.stringutils -import salt.syspaths from salt.exceptions import SaltRenderError # Import 3rd-party libs @@ -229,67 +281,68 @@ log = logging.getLogger(__name__) GPG_CIPHERTEXT = re.compile( salt.utils.stringutils.to_bytes( - r'-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----' + r"-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----" ), re.DOTALL, ) def _get_gpg_exec(): - ''' + """ return the GPG executable or raise an error - ''' - gpg_exec = salt.utils.path.which('gpg') + """ + gpg_exec = salt.utils.path.which("gpg") if gpg_exec: return gpg_exec else: - raise SaltRenderError('GPG unavailable') + raise SaltRenderError("GPG unavailable") def _get_key_dir(): - ''' + """ return the location of the GPG key directory - ''' + """ gpg_keydir = None - if 'config.get' in __salt__: - gpg_keydir = __salt__['config.get']('gpg_keydir') + if "config.get" in __salt__: + gpg_keydir = __salt__["config.get"]("gpg_keydir") if not gpg_keydir: gpg_keydir = __opts__.get( - 'gpg_keydir', + "gpg_keydir", os.path.join( - __opts__.get( - 'config_dir', - os.path.dirname(__opts__['conf_file']), - ), - 'gpgkeys' - )) + __opts__.get("config_dir", os.path.dirname(__opts__["conf_file"]),), + "gpgkeys", + ), + ) return gpg_keydir def _decrypt_ciphertext(cipher): - ''' + """ Given a block of ciphertext as a string, and a gpg object, try to decrypt the cipher and return the decrypted string. If the cipher cannot be decrypted, log the error, and return the ciphertext back out. - ''' + """ try: - cipher = salt.utils.stringutils.to_unicode(cipher).replace(r'\n', '\n') + cipher = salt.utils.stringutils.to_unicode(cipher).replace(r"\n", "\n") except UnicodeDecodeError: # ciphertext is binary pass cipher = salt.utils.stringutils.to_bytes(cipher) - cmd = [_get_gpg_exec(), '--homedir', _get_key_dir(), '--status-fd', '2', - '--no-tty', '-d'] + cmd = [ + _get_gpg_exec(), + "--homedir", + _get_key_dir(), + "--status-fd", + "2", + "--no-tty", + "-d", + ] proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=False) decrypted_data, decrypt_error = proc.communicate(input=cipher) if not decrypted_data: - log.warning( - 'Could not decrypt cipher %r, received: %r', - cipher, - decrypt_error - ) + log.warning("Could not decrypt cipher %r, received: %r", cipher, decrypt_error) return cipher else: return decrypted_data @@ -299,7 +352,7 @@ def _decrypt_ciphertexts(cipher, translate_newlines=False, encoding=None): to_bytes = salt.utils.stringutils.to_bytes cipher = to_bytes(cipher) if translate_newlines: - cipher = cipher.replace(to_bytes(r'\n'), to_bytes('\n')) + cipher = cipher.replace(to_bytes(r"\n"), to_bytes("\n")) def replace(match): result = to_bytes(_decrypt_ciphertext(match.group())) @@ -309,7 +362,7 @@ def _decrypt_ciphertexts(cipher, translate_newlines=False, encoding=None): if num > 0: # Remove trailing newlines. Without if crypted value initially specified as a YAML multiline # it will conain unexpected trailing newline. - ret = ret.rstrip(b'\n') + ret = ret.rstrip(b"\n") else: ret = cipher @@ -322,37 +375,41 @@ def _decrypt_ciphertexts(cipher, translate_newlines=False, encoding=None): def _decrypt_object(obj, translate_newlines=False, encoding=None): - ''' + """ Recursively try to decrypt any object. If the object is a six.string_types (string or unicode), and it contains a valid GPG header, decrypt it, otherwise keep going until a string is found. - ''' + """ if salt.utils.stringio.is_readable(obj): return _decrypt_object(obj.getvalue(), translate_newlines) if isinstance(obj, six.string_types): - return _decrypt_ciphertexts(obj, translate_newlines=translate_newlines, encoding=encoding) + return _decrypt_ciphertexts( + obj, translate_newlines=translate_newlines, encoding=encoding + ) elif isinstance(obj, dict): for key, value in six.iteritems(obj): - obj[key] = _decrypt_object(value, - translate_newlines=translate_newlines) + obj[key] = _decrypt_object(value, translate_newlines=translate_newlines) return obj elif isinstance(obj, list): for key, value in enumerate(obj): - obj[key] = _decrypt_object(value, - translate_newlines=translate_newlines) + obj[key] = _decrypt_object(value, translate_newlines=translate_newlines) return obj else: return obj -def render(gpg_data, saltenv='base', sls='', argline='', **kwargs): - ''' +def render(gpg_data, saltenv="base", sls="", argline="", **kwargs): + """ Create a gpg object given a gpg_keydir, and then use it to try to decrypt the data to be rendered. - ''' + """ if not _get_gpg_exec(): - raise SaltRenderError('GPG unavailable') - log.debug('Reading GPG keys from: %s', _get_key_dir()) + raise SaltRenderError("GPG unavailable") + log.debug("Reading GPG keys from: %s", _get_key_dir()) - translate_newlines = kwargs.get('translate_newlines', False) - return _decrypt_object(gpg_data, translate_newlines=translate_newlines, encoding=kwargs.get('encoding', None)) + translate_newlines = kwargs.get("translate_newlines", False) + return _decrypt_object( + gpg_data, + translate_newlines=translate_newlines, + encoding=kwargs.get("encoding", None), + ) diff --git a/salt/renderers/hjson.py b/salt/renderers/hjson.py index f33d39eea06..feb618df7bc 100644 --- a/salt/renderers/hjson.py +++ b/salt/renderers/hjson.py @@ -1,37 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" hjson renderer for Salt See the hjson_ documentation for more information .. _hjson: http://laktak.github.io/hjson/ -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import 3rd party libs -try: - import hjson - HAS_LIBS = True -except ImportError: - HAS_LIBS = False - # Import salt libs from salt.ext import six +# Import 3rd party libs +try: + import hjson -def render(hjson_data, saltenv='base', sls='', **kws): - ''' + HAS_LIBS = True +except ImportError: + HAS_LIBS = False + + +def render(hjson_data, saltenv="base", sls="", **kws): + """ Accepts HJSON as a string or as a file object and runs it through the HJSON parser. :rtype: A Python data structure - ''' + """ if not isinstance(hjson_data, six.string_types): hjson_data = hjson_data.read() - if hjson_data.startswith('#!'): - hjson_data = hjson_data[(hjson_data.find('\n') + 1):] + if hjson_data.startswith("#!"): + hjson_data = hjson_data[(hjson_data.find("\n") + 1) :] if not hjson_data.strip(): return {} return hjson.loads(hjson_data) diff --git a/salt/renderers/jinja.py b/salt/renderers/jinja.py index bf285ff45b7..74e929abf18 100644 --- a/salt/renderers/jinja.py +++ b/salt/renderers/jinja.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" Jinja loading utils to enable a more powerful backend for jinja templates For Jinja usage information see :ref:`Understanding Jinja <understanding-jinja>`. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.templates + # Import salt libs from salt.exceptions import SaltRenderError -import salt.utils.templates # Import 3rd-party libs from salt.ext import six @@ -21,7 +23,7 @@ log = logging.getLogger(__name__) def _split_module_dicts(): - ''' + """ Create a copy of __salt__ dictionary with module.function and module[function] Takes advantage of Jinja's syntactic sugar lookup: @@ -29,12 +31,12 @@ def _split_module_dicts(): .. code-block:: {{ salt.cmd.run('uptime') }} - ''' + """ if not isinstance(__salt__, dict): return __salt__ mod_dict = dict(__salt__) for module_func_name, mod_fun in six.iteritems(mod_dict.copy()): - mod, fun = module_func_name.split('.', 1) + mod, fun = module_func_name.split(".", 1) if mod not in mod_dict: # create an empty object that we can add attributes to mod_dict[mod] = lambda: None @@ -42,36 +44,44 @@ def _split_module_dicts(): return mod_dict -def render(template_file, saltenv='base', sls='', argline='', - context=None, tmplpath=None, **kws): - ''' +def render( + template_file, + saltenv="base", + sls="", + argline="", + context=None, + tmplpath=None, + **kws +): + """ Render the template_file, passing the functions and grains into the Jinja rendering system. :rtype: string - ''' - from_str = argline == '-s' + """ + from_str = argline == "-s" if not from_str and argline: - raise SaltRenderError( - 'Unknown renderer option: {opt}'.format(opt=argline) - ) + raise SaltRenderError("Unknown renderer option: {opt}".format(opt=argline)) - tmp_data = salt.utils.templates.JINJA(template_file, - to_str=True, - salt=_split_module_dicts(), - grains=__grains__, - opts=__opts__, - pillar=__pillar__, - saltenv=saltenv, - sls=sls, - context=context, - tmplpath=tmplpath, - proxy=__proxy__, - **kws) - if not tmp_data.get('result', False): + tmp_data = salt.utils.templates.JINJA( + template_file, + to_str=True, + salt=_split_module_dicts(), + grains=__grains__, + opts=__opts__, + pillar=__pillar__, + saltenv=saltenv, + sls=sls, + context=context, + tmplpath=tmplpath, + proxy=__proxy__, + from_str=from_str, + **kws + ) + if not tmp_data.get("result", False): raise SaltRenderError( - tmp_data.get('data', 'Unknown render error in jinja renderer') + tmp_data.get("data", "Unknown render error in jinja renderer") ) - if isinstance(tmp_data['data'], bytes): - tmp_data['data'] = tmp_data['data'].decode(__salt_system_encoding__) - return StringIO(tmp_data['data']) + if isinstance(tmp_data["data"], bytes): + tmp_data["data"] = tmp_data["data"].decode(__salt_system_encoding__) + return StringIO(tmp_data["data"]) diff --git a/salt/renderers/json.py b/salt/renderers/json.py index b2c0042c4aa..d094de62d2d 100644 --- a/salt/renderers/json.py +++ b/salt/renderers/json.py @@ -1,30 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" JSON Renderer for Salt -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import salt.utils.json -json = salt.utils.json.import_json() # Import salt libs from salt.ext import six +json = salt.utils.json.import_json() -def render(json_data, saltenv='base', sls='', **kws): - ''' + +def render(json_data, saltenv="base", sls="", **kws): + """ Accepts JSON as a string or as a file object and runs it through the JSON parser. :rtype: A Python data structure - ''' + """ if not isinstance(json_data, six.string_types): json_data = json_data.read() - if json_data.startswith('#!'): - json_data = json_data[(json_data.find('\n') + 1):] + if json_data.startswith("#!"): + json_data = json_data[(json_data.find("\n") + 1) :] if not json_data.strip(): return {} return json.loads(json_data) diff --git a/salt/renderers/json5.py b/salt/renderers/json5.py index 7b9df094a92..5063cb5d253 100644 --- a/salt/renderers/json5.py +++ b/salt/renderers/json5.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" JSON5 Renderer for Salt .. versionadded:: 2016.3.0 @@ -10,45 +10,48 @@ information. This renderer requires the `json5 python bindings`__, installable via pip. .. __: https://pypi.python.org/pypi/json5 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -try: - import json5 as json - HAS_JSON5 = True -except ImportError: - HAS_JSON5 = False # Import salt libs from salt.ext import six +try: + import json5 as json + + HAS_JSON5 = True +except ImportError: + HAS_JSON5 = False + + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'json5' +__virtualname__ = "json5" def __virtual__(): if not HAS_JSON5: - return (False, 'json5 module not installed') + return (False, "json5 module not installed") return __virtualname__ -def render(json_data, saltenv='base', sls='', **kws): - ''' +def render(json_data, saltenv="base", sls="", **kws): + """ Accepts JSON as a string or as a file object and runs it through the JSON parser. :rtype: A Python data structure - ''' + """ if not isinstance(json_data, six.string_types): json_data = json_data.read() - if json_data.startswith('#!'): - json_data = json_data[(json_data.find('\n') + 1):] + if json_data.startswith("#!"): + json_data = json_data[(json_data.find("\n") + 1) :] if not json_data.strip(): return {} return json.loads(json_data) diff --git a/salt/renderers/mako.py b/salt/renderers/mako.py index 777d5417035..e5bcf8862b2 100644 --- a/salt/renderers/mako.py +++ b/salt/renderers/mako.py @@ -1,35 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" Mako Renderer for Salt -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import salt libs -from salt.ext import six import salt.utils.templates from salt.exceptions import SaltRenderError +# Import salt libs +from salt.ext import six -def render(template_file, saltenv='base', sls='', context=None, tmplpath=None, **kws): - ''' + +def render(template_file, saltenv="base", sls="", context=None, tmplpath=None, **kws): + """ Render the template_file, passing the functions and grains into the Mako rendering system. :rtype: string - ''' - tmp_data = salt.utils.templates.MAKO(template_file, to_str=True, - salt=__salt__, - grains=__grains__, - opts=__opts__, - pillar=__pillar__, - saltenv=saltenv, - sls=sls, - context=context, - tmplpath=tmplpath, - **kws) - if not tmp_data.get('result', False): - raise SaltRenderError(tmp_data.get('data', - 'Unknown render error in mako renderer')) - return six.moves.StringIO(tmp_data['data']) + """ + tmp_data = salt.utils.templates.MAKO( + template_file, + to_str=True, + salt=__salt__, + grains=__grains__, + opts=__opts__, + pillar=__pillar__, + saltenv=saltenv, + sls=sls, + context=context, + tmplpath=tmplpath, + **kws + ) + if not tmp_data.get("result", False): + raise SaltRenderError( + tmp_data.get("data", "Unknown render error in mako renderer") + ) + return six.moves.StringIO(tmp_data["data"]) diff --git a/salt/renderers/msgpack.py b/salt/renderers/msgpack.py index eceac4f53bb..a0f2973af81 100644 --- a/salt/renderers/msgpack.py +++ b/salt/renderers/msgpack.py @@ -6,8 +6,8 @@ import salt.utils.msgpack from salt.ext import six -def render(msgpack_data, saltenv='base', sls='', **kws): - ''' +def render(msgpack_data, saltenv="base", sls="", **kws): + """ Accepts a message pack string or a file object, renders said data back to a python dict. @@ -18,12 +18,12 @@ def render(msgpack_data, saltenv='base', sls='', **kws): fluid fileserver backends that rely on pure data sources. :rtype: A Python data structure - ''' + """ if not isinstance(msgpack_data, six.string_types): msgpack_data = msgpack_data.read() - if msgpack_data.startswith('#!'): - msgpack_data = msgpack_data[(msgpack_data.find('\n') + 1):] + if msgpack_data.startswith("#!"): + msgpack_data = msgpack_data[(msgpack_data.find("\n") + 1) :] if not msgpack_data.strip(): return {} return salt.utils.msgpack.loads(msgpack_data) diff --git a/salt/renderers/nacl.py b/salt/renderers/nacl.py index 5c05383666a..114594785ce 100644 --- a/salt/renderers/nacl.py +++ b/salt/renderers/nacl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Renderer that will decrypt NACL ciphers Any key in the SLS file can be an NACL cipher, and this renderer will decrypt it @@ -26,7 +26,7 @@ To set things up, first generate a keypair. On the master, run the following: Using encrypted pillar ---------------------- +---------------------- To encrypt secrets, copy the public key to your local machine and run: @@ -50,35 +50,36 @@ data like so: #!yaml|nacl a-secret: "NACL[MRN3cc+fmdxyQbz6WMF+jq1hKdU5X5BBI7OjK+atvHo1ll+w1gZ7XyWtZVfq9gK9rQaMfkDxmidJKwE0Mw==]" -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import re -import logging -# Import salt libs -import salt.utils.stringio -import salt.syspaths +import logging +import re # Import 3rd-party libs import salt.ext.six as six +import salt.syspaths + +# Import salt libs +import salt.utils.stringio log = logging.getLogger(__name__) -NACL_REGEX = r'^NACL\[(.*)\]$' +NACL_REGEX = r"^NACL\[(.*)\]$" def _decrypt_object(obj, **kwargs): - ''' + """ Recursively try to decrypt any object. If the object is a six.string_types (string or unicode), and it contains a valid NACLENC pretext, decrypt it, otherwise keep going until a string is found. - ''' + """ if salt.utils.stringio.is_readable(obj): return _decrypt_object(obj.getvalue(), **kwargs) if isinstance(obj, six.string_types): if re.search(NACL_REGEX, obj) is not None: - return __salt__['nacl.dec'](re.search(NACL_REGEX, obj).group(1), **kwargs) + return __salt__["nacl.dec"](re.search(NACL_REGEX, obj).group(1), **kwargs) else: return obj elif isinstance(obj, dict): @@ -93,9 +94,9 @@ def _decrypt_object(obj, **kwargs): return obj -def render(nacl_data, saltenv='base', sls='', argline='', **kwargs): - ''' +def render(nacl_data, saltenv="base", sls="", argline="", **kwargs): + """ Decrypt the data to be rendered using the given nacl key or the one given in config - ''' + """ return _decrypt_object(nacl_data, **kwargs) diff --git a/salt/renderers/pass.py b/salt/renderers/pass.py index d4ff61cbbaf..06723e53467 100644 --- a/salt/renderers/pass.py +++ b/salt/renderers/pass.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Pass Renderer for Salt ====================== @@ -46,14 +46,15 @@ Install pass binary pass: pkg.installed -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os from os.path import expanduser -from subprocess import Popen, PIPE +from subprocess import PIPE, Popen # Import salt libs import salt.utils.path @@ -66,39 +67,39 @@ log = logging.getLogger(__name__) def _get_pass_exec(): - ''' + """ Return the pass executable or raise an error - ''' - pass_exec = salt.utils.path.which('pass') + """ + pass_exec = salt.utils.path.which("pass") if pass_exec: return pass_exec else: - raise SaltRenderError('pass unavailable') + raise SaltRenderError("pass unavailable") def _fetch_secret(pass_path): - ''' + """ Fetch secret from pass based on pass_path. If there is any error, return back the original pass_path value - ''' + """ cmd = "pass show {0}".format(pass_path.strip()) - log.debug('Fetching secret: %s', cmd) + log.debug("Fetching secret: %s", cmd) - proc = Popen(cmd.split(' '), stdout=PIPE, stderr=PIPE) + proc = Popen(cmd.split(" "), stdout=PIPE, stderr=PIPE) pass_data, pass_error = proc.communicate() # The version of pass used during development sent output to # stdout instead of stderr even though its returncode was non zero. if proc.returncode or not pass_data: - log.warning('Could not fetch secret: %s %s', pass_data, pass_error) + log.warning("Could not fetch secret: %s %s", pass_data, pass_error) pass_data = pass_path return pass_data.strip() def _decrypt_object(obj): - ''' + """ Recursively try to find a pass path (string) that can be handed off to pass - ''' + """ if isinstance(obj, six.string_types): return _fetch_secret(obj) elif isinstance(obj, dict): @@ -110,13 +111,13 @@ def _decrypt_object(obj): return obj -def render(pass_info, saltenv='base', sls='', argline='', **kwargs): - ''' +def render(pass_info, saltenv="base", sls="", argline="", **kwargs): + """ Fetch secret from pass based on pass_path - ''' + """ _get_pass_exec() # Make sure environment variable HOME is set, since Pass looks for the # password-store under ~/.password-store. - os.environ['HOME'] = expanduser('~') + os.environ["HOME"] = expanduser("~") return _decrypt_object(pass_info) diff --git a/salt/renderers/py.py b/salt/renderers/py.py index f156a9bee3e..27a20257354 100644 --- a/salt/renderers/py.py +++ b/salt/renderers/py.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Pure python state renderer ========================== @@ -109,45 +109,48 @@ Full Example return config -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os -# Import salt libs -from salt.exceptions import SaltRenderError import salt.utils.templates +# Import salt libs +from salt.exceptions import SaltRenderError -def render(template, saltenv='base', sls='', tmplpath=None, **kws): - ''' + +def render(template, saltenv="base", sls="", tmplpath=None, **kws): + """ Render the python module's components :rtype: string - ''' + """ template = tmplpath if not os.path.isfile(template): - raise SaltRenderError('Template {0} is not a file!'.format(template)) + raise SaltRenderError("Template {0} is not a file!".format(template)) tmp_data = salt.utils.templates.py( - template, - True, - __salt__=__salt__, - salt=__salt__, - __grains__=__grains__, - grains=__grains__, - __opts__=__opts__, - opts=__opts__, - __pillar__=__pillar__, - pillar=__pillar__, - __env__=saltenv, - saltenv=saltenv, - __sls__=sls, - sls=sls, - **kws) - if not tmp_data.get('result', False): - raise SaltRenderError(tmp_data.get('data', - 'Unknown render error in py renderer')) + template, + True, + __salt__=__salt__, + salt=__salt__, + __grains__=__grains__, + grains=__grains__, + __opts__=__opts__, + opts=__opts__, + __pillar__=__pillar__, + pillar=__pillar__, + __env__=saltenv, + saltenv=saltenv, + __sls__=sls, + sls=sls, + **kws + ) + if not tmp_data.get("result", False): + raise SaltRenderError( + tmp_data.get("data", "Unknown render error in py renderer") + ) - return tmp_data['data'] + return tmp_data["data"] diff --git a/salt/renderers/pydsl.py b/salt/renderers/pydsl.py index 2242135bcdd..b6997dedb29 100644 --- a/salt/renderers/pydsl.py +++ b/salt/renderers/pydsl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A Python-based DSL :maintainer: Jack Kuan <kjkuan@gmail.com> @@ -333,20 +333,21 @@ For example: def main(): my_mod = sys.modules['salt.loaded.ext.module.my_mod'] -''' +""" from __future__ import absolute_import, print_function, unicode_literals import types + import salt.utils.pydsl as pydsl import salt.utils.stringutils +from salt.exceptions import SaltRenderError from salt.ext.six import exec_ from salt.utils.pydsl import PyDslError -from salt.exceptions import SaltRenderError -__all__ = ['render'] +__all__ = ["render"] -def render(template, saltenv='base', sls='', tmplpath=None, rendered_sls=None, **kws): +def render(template, saltenv="base", sls="", tmplpath=None, rendered_sls=None, **kws): sls = salt.utils.stringutils.to_str(sls) mod = types.ModuleType(sls) # Note: mod object is transient. Its existence only lasts as long as @@ -372,7 +373,8 @@ def render(template, saltenv='base', sls='', tmplpath=None, rendered_sls=None, * __env__=saltenv, __sls__=sls, __file__=tmplpath, - **kws) + **kws + ) dsl_sls.get_render_stack().append(dsl_sls) exec_(template.read(), mod.__dict__) @@ -388,4 +390,5 @@ def _wrap_sls(method): return getattr(sls, method.__name__)(*args, **kws) except PyDslError as exc: raise SaltRenderError(exc) + return _sls_method diff --git a/salt/renderers/pyobjects.py b/salt/renderers/pyobjects.py index 242bc9e343d..c2731c93187 100644 --- a/salt/renderers/pyobjects.py +++ b/salt/renderers/pyobjects.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Python renderer that includes a Pythonic Object based interface :maintainer: Evan Borgstrom <evan@borgstrom.ca> @@ -295,38 +295,39 @@ file ``samba/map.sls``, you could do the following. with Pkg.installed("samba", names=[Samba.server, Samba.client]): Service.running("samba", name=Samba.service) -''' +""" # TODO: Interface for working with reactor files # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import re +import salt.loader +import salt.utils.files + # Import Salt Libs from salt.ext import six -import salt.utils.files -import salt.loader from salt.fileclient import get_file_client -from salt.utils.pyobjects import Registry, StateFactory, SaltObject, Map -from salt.ext import six +from salt.utils.pyobjects import Map, Registry, SaltObject, StateFactory # our import regexes -FROM_RE = re.compile(r'^\s*from\s+(salt:\/\/.*)\s+import (.*)$') -IMPORT_RE = re.compile(r'^\s*import\s+(salt:\/\/.*)$') -FROM_AS_RE = re.compile(r'^(.*) as (.*)$') +FROM_RE = re.compile(r"^\s*from\s+(salt:\/\/.*)\s+import (.*)$") +IMPORT_RE = re.compile(r"^\s*import\s+(salt:\/\/.*)$") +FROM_AS_RE = re.compile(r"^(.*) as (.*)$") log = logging.getLogger(__name__) try: - __context__['pyobjects_loaded'] = True + __context__["pyobjects_loaded"] = True except NameError: __context__ = {} class PyobjectsModule(object): - '''This provides a wrapper for bare imports.''' + """This provides a wrapper for bare imports.""" def __init__(self, name, attrs): self.name = name @@ -337,85 +338,76 @@ class PyobjectsModule(object): def load_states(): - ''' + """ This loads our states into the salt __context__ - ''' + """ states = {} # the loader expects to find pillar & grain data - __opts__['grains'] = salt.loader.grains(__opts__) - __opts__['pillar'] = __pillar__ + __opts__["grains"] = salt.loader.grains(__opts__) + __opts__["pillar"] = __pillar__ lazy_utils = salt.loader.utils(__opts__) lazy_funcs = salt.loader.minion_mods(__opts__, utils=lazy_utils) lazy_serializers = salt.loader.serializers(__opts__) - lazy_states = salt.loader.states(__opts__, - lazy_funcs, - lazy_utils, - lazy_serializers) + lazy_states = salt.loader.states(__opts__, lazy_funcs, lazy_utils, lazy_serializers) # TODO: some way to lazily do this? This requires loading *all* state modules for key, func in six.iteritems(lazy_states): - if '.' not in key: + if "." not in key: continue - mod_name, func_name = key.split('.', 1) + mod_name, func_name = key.split(".", 1) if mod_name not in states: states[mod_name] = {} states[mod_name][func_name] = func - __context__['pyobjects_states'] = states + __context__["pyobjects_states"] = states -def render(template, saltenv='base', sls='', salt_data=True, **kwargs): - if 'pyobjects_states' not in __context__: +def render(template, saltenv="base", sls="", salt_data=True, **kwargs): + if "pyobjects_states" not in __context__: load_states() # these hold the scope that our sls file will be executed with _globals = {} # create our StateFactory objects - mod_globals = {'StateFactory': StateFactory} - for mod in __context__['pyobjects_states']: + mod_globals = {"StateFactory": StateFactory} + for mod in __context__["pyobjects_states"]: mod_locals = {} - mod_camel = ''.join([ - part.capitalize() - for part in mod.split('_') - ]) - valid_funcs = "','".join( - __context__['pyobjects_states'][mod] - ) + mod_camel = "".join([part.capitalize() for part in mod.split("_")]) + valid_funcs = "','".join(__context__["pyobjects_states"][mod]) mod_cmd = "{0} = StateFactory('{1!s}', valid_funcs=['{2}'])".format( - mod_camel, - mod, - valid_funcs + mod_camel, mod, valid_funcs ) six.exec_(mod_cmd, mod_globals, mod_locals) _globals[mod_camel] = mod_locals[mod_camel] # add our include and extend functions - _globals['include'] = Registry.include - _globals['extend'] = Registry.make_extend + _globals["include"] = Registry.include + _globals["extend"] = Registry.make_extend # add our map class Map.__salt__ = __salt__ - _globals['Map'] = Map + _globals["Map"] = Map # add some convenience methods to the global scope as well as the "dunder" # format of all of the salt objects try: - _globals.update({ - # salt, pillar & grains all provide shortcuts or object interfaces - 'salt': SaltObject(__salt__), - 'pillar': __salt__['pillar.get'], - 'grains': __salt__['grains.get'], - 'mine': __salt__['mine.get'], - 'config': __salt__['config.get'], - - # the "dunder" formats are still available for direct use - '__salt__': __salt__, - '__pillar__': __pillar__, - '__grains__': __grains__ - }) + _globals.update( + { + # salt, pillar & grains all provide shortcuts or object interfaces + "salt": SaltObject(__salt__), + "pillar": __salt__["pillar.get"], + "grains": __salt__["grains.get"], + "mine": __salt__["mine.get"], + "config": __salt__["config.get"], + # the "dunder" formats are still available for direct use + "__salt__": __salt__, + "__pillar__": __pillar__, + "__grains__": __grains__, + } + ) except NameError: pass @@ -440,7 +432,7 @@ def render(template, saltenv='base', sls='', salt_data=True, **kwargs): # Do not pass our globals to the modules we are including and keep the root _globals untouched template_globals = dict(_globals) for line in template.readlines(): - line = line.rstrip('\r\n') + line = line.rstrip("\r\n") matched = False for RE in (IMPORT_RE, FROM_RE): matches = RE.match(line) @@ -449,7 +441,7 @@ def render(template, saltenv='base', sls='', salt_data=True, **kwargs): import_file = matches.group(1).strip() try: - imports = matches.group(2).split(',') + imports = matches.group(2).split(",") except IndexError: # if we don't have a third group in the matches object it means # that we're importing everything @@ -458,7 +450,7 @@ def render(template, saltenv='base', sls='', salt_data=True, **kwargs): state_file = client.cache_file(import_file, saltenv) if not state_file: raise ImportError( - 'Could not find the file \'{0}\''.format(import_file) + "Could not find the file '{0}'".format(import_file) ) with salt.utils.files.fopen(state_file) as state_fh: @@ -470,7 +462,9 @@ def render(template, saltenv='base', sls='', salt_data=True, **kwargs): # under the name of the module -> i.e. foo.MapClass if imports is None: import_name = os.path.splitext(os.path.basename(state_file))[0] - template_globals[import_name] = PyobjectsModule(import_name, state_globals) + template_globals[import_name] = PyobjectsModule( + import_name, state_globals + ) else: for name in imports: name = alias = name.strip() @@ -482,10 +476,7 @@ def render(template, saltenv='base', sls='', salt_data=True, **kwargs): if name not in state_globals: raise ImportError( - '\'{0}\' was not found in \'{1}\''.format( - name, - import_file - ) + "'{0}' was not found in '{1}'".format(name, import_file) ) template_globals[alias] = state_globals[name] diff --git a/salt/renderers/stateconf.py b/salt/renderers/stateconf.py index cfce9e69261..61712e1b2ea 100644 --- a/salt/renderers/stateconf.py +++ b/salt/renderers/stateconf.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" A flexible renderer that takes a templating engine and a data format :maintainer: Jack Kuan <kjkuan@gmail.com> :maturity: new :platform: all -''' +""" # See http://docs.saltstack.org/en/latest/ref/renderers/all/salt.renderers.stateconf.html # for a guide to using this module. # @@ -28,11 +28,13 @@ A flexible renderer that takes a templating engine and a data format # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import copy +import getopt import logging import os import re -import getopt -import copy +from itertools import chain # Import salt libs import salt.utils.files @@ -43,22 +45,19 @@ from salt.exceptions import SaltRenderError from salt.ext import six from salt.ext.six.moves import StringIO # pylint: disable=import-error -__all__ = ['render'] +__all__ = ["render"] log = logging.getLogger(__name__) __opts__ = { - 'stateconf_end_marker': r'#\s*-+\s*end of state config\s*-+', + "stateconf_end_marker": r"#\s*-+\s*end of state config\s*-+", # e.g., something like "# --- end of state config --" works by default. - - 'stateconf_start_state': '.start', + "stateconf_start_state": ".start", # name of the state id for the generated start state. - - 'stateconf_goal_state': '.goal', + "stateconf_goal_state": ".goal", # name of the state id for the generated goal state. - - 'stateconf_state_func': 'stateconf.set' + "stateconf_state_func": "stateconf.set" # names the state and the state function to be recognized as a special # state from which to gather sls file context variables. It should be # specified in the 'state.func' notation, and both the state module and @@ -67,19 +66,19 @@ __opts__ = { # dict(name=name, result=True, changes={}, comment='') } -STATE_FUNC = STATE_NAME = '' +STATE_FUNC = STATE_NAME = "" def __init__(opts): global STATE_NAME, STATE_FUNC - STATE_FUNC = __opts__['stateconf_state_func'] - STATE_NAME = STATE_FUNC.split('.')[0] + STATE_FUNC = __opts__["stateconf_state_func"] + STATE_NAME = STATE_FUNC.split(".")[0] MOD_BASENAME = os.path.basename(__file__) INVALID_USAGE_ERROR = SaltRenderError( - 'Invalid use of {0} renderer!\n' - '''Usage: #!{0} [-GoSp] [<data_renderer> [options] . <template_renderer> [options]] + "Invalid use of {0} renderer!\n" + """Usage: #!{0} [-GoSp] [<data_renderer> [options] . <template_renderer> [options]] where an example <data_renderer> would be yaml and a <template_renderer> might be jinja. Each renderer can be passed its renderer specific options. @@ -99,25 +98,26 @@ Options(for this renderer): in the sls will have no effect, but other features of the renderer still apply. - '''.format(MOD_BASENAME) + """.format( + MOD_BASENAME + ) ) -def render(input, saltenv='base', sls='', argline='', **kws): +def render(input, saltenv="base", sls="", argline="", **kws): gen_start_state = False no_goal_state = False implicit_require = False def process_sls_data(data, context=None, extract=False): - sls_dir = os.path.dirname(sls.replace('.', os.path.sep)) if '.' in sls else sls - ctx = dict(sls_dir=sls_dir if sls_dir else '.') + sls_dir = os.path.dirname(sls.replace(".", os.path.sep)) if "." in sls else sls + ctx = dict(sls_dir=sls_dir if sls_dir else ".") if context: ctx.update(context) tmplout = render_template( - StringIO(data), saltenv, sls, context=ctx, - argline=rt_argline.strip(), **kws + StringIO(data), saltenv, sls, context=ctx, argline=rt_argline.strip(), **kws ) high = render_data(tmplout, saltenv, sls, argline=rd_argline.strip()) return process_high_data(high, extract) @@ -135,10 +135,10 @@ def render(input, saltenv='base', sls='', argline='', **kws): sid = has_names_decls(data) if sid: raise SaltRenderError( - '\'names\' declaration(found in state id: {0}) is ' - 'not supported with implicitly ordered states! You ' - 'should generate the states in a template for-loop ' - 'instead.'.format(sid) + "'names' declaration(found in state id: {0}) is " + "not supported with implicitly ordered states! You " + "should generate the states in a template for-loop " + "instead.".format(sid) ) add_implicit_requires(data) @@ -157,65 +157,66 @@ def render(input, saltenv='base', sls='', argline='', **kws): raise except Exception as err: # pylint: disable=broad-except log.exception( - 'Error found while pre-processing the salt file %s:\n%s', - sls, err + "Error found while pre-processing the salt file %s:\n%s", sls, err ) from salt.state import State + state = State(__opts__) errors = state.verify_high(high) if errors: - raise SaltRenderError('\n'.join(errors)) - raise SaltRenderError('sls preprocessing/rendering failed!') + raise SaltRenderError("\n".join(errors)) + raise SaltRenderError("sls preprocessing/rendering failed!") return data - # ---------------------- - renderers = kws['renderers'] - opts, args = getopt.getopt(argline.split(), 'Gosp') - argline = ' '.join(args) if args else 'yaml . jinja' - if ('-G', '') in opts: + # ---------------------- + renderers = kws["renderers"] + opts, args = getopt.getopt(argline.split(), "Gosp") + argline = " ".join(args) if args else "yaml . jinja" + + if ("-G", "") in opts: no_goal_state = True - if ('-o', '') in opts: + if ("-o", "") in opts: implicit_require = True - if ('-s', '') in opts: + if ("-s", "") in opts: gen_start_state = True - if ('-p', '') in opts: + if ("-p", "") in opts: data = process_high_data(input, extract=False) else: # Split on the first dot surrounded by spaces but not preceded by a # backslash. A backslash preceded dot will be replaced with just dot. args = [ - arg.strip().replace('\\.', '.') - for arg in re.split(r'\s+(?<!\\)\.\s+', argline, 1) + arg.strip().replace("\\.", ".") + for arg in re.split(r"\s+(?<!\\)\.\s+", argline, 1) ] try: - name, rd_argline = (args[0] + ' ').split(' ', 1) + name, rd_argline = (args[0] + " ").split(" ", 1) render_data = renderers[name] # e.g., the yaml renderer if implicit_require: - if name == 'yaml': - rd_argline = '-o ' + rd_argline + if name == "yaml": + rd_argline = "-o " + rd_argline else: raise SaltRenderError( - 'Implicit ordering is only supported if the yaml renderer ' - 'is used!' + "Implicit ordering is only supported if the yaml renderer " + "is used!" ) - name, rt_argline = (args[1] + ' ').split(' ', 1) + name, rt_argline = (args[1] + " ").split(" ", 1) render_template = renderers[name] # e.g., the mako renderer except KeyError as err: - raise SaltRenderError('Renderer: {0} is not available!'.format(err)) + raise SaltRenderError("Renderer: {0} is not available!".format(err)) except IndexError: raise INVALID_USAGE_ERROR if isinstance(input, six.string_types): - with salt.utils.files.fopen(input, 'r') as ifile: + with salt.utils.files.fopen(input, "r") as ifile: sls_templ = salt.utils.stringutils.to_unicode(ifile.read()) else: # assume file-like sls_templ = salt.utils.stringutils.to_unicode(input.read()) # first pass to extract the state configuration - match = re.search(__opts__['stateconf_end_marker'], sls_templ) + match = re.search(__opts__["stateconf_end_marker"], sls_templ) if match: - process_sls_data(sls_templ[:match.start()], extract=True) + process_sls_data(sls_templ[: match.start()], extract=True) # if some config has been extracted then remove the sls-name prefix # of the keys in the extracted stateconf.set context to make them easier @@ -223,10 +224,10 @@ def render(input, saltenv='base', sls='', argline='', **kws): if STATE_CONF: tmplctx = STATE_CONF.copy() if tmplctx: - prefix = sls + '::' + prefix = sls + "::" for k in six.iterkeys(tmplctx): # iterate over a copy of keys if k.startswith(prefix): - tmplctx[k[len(prefix):]] = tmplctx[k] + tmplctx[k[len(prefix) :]] = tmplctx[k] del tmplctx[k] else: tmplctx = {} @@ -236,20 +237,21 @@ def render(input, saltenv='base', sls='', argline='', **kws): if log.isEnabledFor(logging.DEBUG): import pprint # FIXME: pprint OrderedDict - log.debug('Rendered sls: %s', pprint.pformat(data)) + + log.debug("Rendered sls: %s", pprint.pformat(data)) return data def has_names_decls(data): for sid, _, _, args in statelist(data): - if sid == 'extend': + if sid == "extend": continue - for _ in nvlist(args, ['names']): + for _ in nvlist(args, ["names"]): return sid def rewrite_single_shorthand_state_decl(data): # pylint: disable=C0103 - ''' + """ Rewrite all state declarations that look like this:: state_id_decl: @@ -259,7 +261,7 @@ def rewrite_single_shorthand_state_decl(data): # pylint: disable=C0103 state_id_decl: state.func: [] - ''' + """ for sid, states in six.iteritems(data): if isinstance(states, six.string_types): data[sid] = {states: []} @@ -269,7 +271,7 @@ def rewrite_sls_includes_excludes(data, sls, saltenv): # if the path of the included/excluded sls starts with a leading dot(.) # then it's taken to be relative to the including/excluding sls. for sid in data: - if sid == 'include': + if sid == "include": includes = data[sid] for i, each in enumerate(includes): if isinstance(each, dict): @@ -277,38 +279,38 @@ def rewrite_sls_includes_excludes(data, sls, saltenv): else: slsenv = saltenv incl = each - if incl.startswith('.'): + if incl.startswith("."): includes[i] = {slsenv: _relative_to_abs_sls(incl, sls)} - elif sid == 'exclude': + elif sid == "exclude": for sdata in data[sid]: - if 'sls' in sdata and sdata['sls'].startswith('.'): - sdata['sls'] = _relative_to_abs_sls(sdata['sls'], sls) + if "sls" in sdata and sdata["sls"].startswith("."): + sdata["sls"] = _relative_to_abs_sls(sdata["sls"], sls) def _local_to_abs_sid(sid, sls): # id must starts with '.' - if '::' in sid: + if "::" in sid: return _relative_to_abs_sls(sid, sls) else: - abs_sls = _relative_to_abs_sls(sid, sls + '.') - return '::'.join(abs_sls.rsplit('.', 1)) + abs_sls = _relative_to_abs_sls(sid, sls + ".") + return "::".join(abs_sls.rsplit(".", 1)) def _relative_to_abs_sls(relative, sls): - ''' + """ Convert ``relative`` sls reference into absolute, relative to ``sls``. - ''' - levels, suffix = re.match(r'^(\.+)(.*)$', relative).groups() + """ + levels, suffix = re.match(r"^(\.+)(.*)$", relative).groups() level_count = len(levels) - p_comps = sls.split('.') + p_comps = sls.split(".") if level_count > len(p_comps): raise SaltRenderError( - 'Attempted relative include goes beyond top level package' + "Attempted relative include goes beyond top level package" ) - return '.'.join(p_comps[:-level_count] + [suffix]) + return ".".join(p_comps[:-level_count] + [suffix]) def nvlist(thelist, names=None): - ''' + """ Given a list of items:: - whatever @@ -321,7 +323,7 @@ def nvlist(thelist, names=None): items that are not name-value's(dictionaries) or those not in the list of matching names. The item in the returned tuple is the single-key dictionary. - ''' + """ # iterate over the list under the state dict. for nvitem in thelist: if isinstance(nvitem, dict): @@ -332,7 +334,7 @@ def nvlist(thelist, names=None): def nvlist2(thelist, names=None): - ''' + """ Like nvlist but applied one more time to each returned value. So, given a list, args, of arguments to a state like this:: @@ -345,26 +347,38 @@ def nvlist2(thelist, names=None): (dict_item, 'file', 'test.sh') where dict_item is the single-key dictionary of {'file': 'test.sh'}. - ''' + """ for _, _, value in nvlist(thelist, names): for each in nvlist(value): yield each -def statelist(states_dict, sid_excludes=frozenset(['include', 'exclude'])): +def statelist(states_dict, sid_excludes=frozenset(["include", "exclude"])): for sid, states in six.iteritems(states_dict): - if sid.startswith('__'): + if sid.startswith("__"): continue if sid in sid_excludes: continue for sname, args in six.iteritems(states): - if sname.startswith('__'): + if sname.startswith("__"): continue yield sid, states, sname, args -REQUISITES = ('require', 'require_in', 'watch', 'watch_in', 'use', 'use_in', 'listen', 'listen_in', 'onchanges', - 'onchanges_in', 'onfail', 'onfail_in') +REQUISITES = ( + "require", + "require_in", + "watch", + "watch_in", + "use", + "use_in", + "listen", + "listen_in", + "onchanges", + "onchanges_in", + "onfail", + "onfail_in", +) def rename_state_ids(data, sls, is_extend=False): @@ -374,28 +388,28 @@ def rename_state_ids(data, sls, is_extend=False): # update "local" references to the renamed states. - if 'extend' in data and not is_extend: - rename_state_ids(data['extend'], sls, True) + if "extend" in data and not is_extend: + rename_state_ids(data["extend"], sls, True) for sid, _, _, args in statelist(data): for req, sname, sid in nvlist2(args, REQUISITES): - if sid.startswith('.'): + if sid.startswith("."): req[sname] = _local_to_abs_sid(sid, sls) for sid in list(data): - if sid.startswith('.'): + if sid.startswith("."): newsid = _local_to_abs_sid(sid, sls) if newsid in data: raise SaltRenderError( - 'Can\'t rename state id({0}) into {1} because the later ' - 'already exists!'.format(sid, newsid) + "Can't rename state id({0}) into {1} because the later " + "already exists!".format(sid, newsid) ) # add a '- name: sid' to those states without '- name'. for sname, args in six.iteritems(data[sid]): if state_name(sname) == STATE_NAME: continue for arg in args: - if isinstance(arg, dict) and next(iter(arg)) == 'name': + if isinstance(arg, dict) and next(iter(arg)) == "name": break else: # then no '- name: ...' is defined in the state args @@ -405,21 +419,18 @@ def rename_state_ids(data, sls, is_extend=False): del data[sid] -REQUIRE = ('require', 'watch', 'listen', 'onchanges', 'onfail') -REQUIRE_IN = ('require_in', 'watch_in', 'listen_in', 'onchanges_in', 'onfail_in') +REQUIRE = ("require", "watch", "listen", "onchanges", "onfail") +REQUIRE_IN = ("require_in", "watch_in", "listen_in", "onchanges_in", "onfail_in") EXTENDED_REQUIRE = {} EXTENDED_REQUIRE_IN = {} -from itertools import chain - # To avoid cycles among states when each state requires the one before it: # explicit require/watch/listen/onchanges/onfail can only contain states before it # explicit require_in/watch_in/listen_in/onchanges_in/onfail_in can only contain states after it def add_implicit_requires(data): - def T(sid, state): # pylint: disable=C0103 - return '{0}:{1}'.format(sid, state_name(state)) + return "{0}:{1}".format(sid, state_name(state)) states_before = set() states_after = set() @@ -430,14 +441,16 @@ def add_implicit_requires(data): prev_state = (None, None) # (state_name, sid) for sid, states, sname, args in statelist(data): - if sid == 'extend': + if sid == "extend": for esid, _, _, eargs in statelist(states): for _, rstate, rsid in nvlist2(eargs, REQUIRE): - EXTENDED_REQUIRE.setdefault( - T(esid, rstate), []).append((None, rstate, rsid)) + EXTENDED_REQUIRE.setdefault(T(esid, rstate), []).append( + (None, rstate, rsid) + ) for _, rstate, rsid in nvlist2(eargs, REQUIRE_IN): - EXTENDED_REQUIRE_IN.setdefault( - T(esid, rstate), []).append((None, rstate, rsid)) + EXTENDED_REQUIRE_IN.setdefault(T(esid, rstate), []).append( + (None, rstate, rsid) + ) continue tag = T(sid, sname) @@ -449,8 +462,8 @@ def add_implicit_requires(data): for _, rstate, rsid in reqs: if T(rsid, rstate) in states_after: raise SaltRenderError( - 'State({0}) can\'t require/watch/listen/onchanges/onfail a state({1}) defined ' - 'after it!'.format(tag, T(rsid, rstate)) + "State({0}) can't require/watch/listen/onchanges/onfail a state({1}) defined " + "after it!".format(tag, T(rsid, rstate)) ) reqs = nvlist2(args, REQUIRE_IN) @@ -459,15 +472,15 @@ def add_implicit_requires(data): for _, rstate, rsid in reqs: if T(rsid, rstate) in states_before: raise SaltRenderError( - 'State({0}) can\'t require_in/watch_in/listen_in/onchanges_in/onfail_in a state({1}) ' - 'defined before it!'.format(tag, T(rsid, rstate)) + "State({0}) can't require_in/watch_in/listen_in/onchanges_in/onfail_in a state({1}) " + "defined before it!".format(tag, T(rsid, rstate)) ) # add a (- state: sid) item, at the beginning of the require of this # state if there's a state before this one. if prev_state[0] is not None: try: - next(nvlist(args, ['require']))[2].insert(0, dict([prev_state])) + next(nvlist(args, ["require"]))[2].insert(0, dict([prev_state])) except StopIteration: # i.e., there's no require args.append(dict(require=[dict([prev_state])])) @@ -476,11 +489,11 @@ def add_implicit_requires(data): def add_start_state(data, sls): - start_sid = __opts__['stateconf_start_state'] + start_sid = __opts__["stateconf_start_state"] if start_sid in data: raise SaltRenderError( - 'Can\'t generate start state({0})! The same state id already ' - 'exists!'.format(start_sid) + "Can't generate start state({0})! The same state id already " + "exists!".format(start_sid) ) if not data: return @@ -488,30 +501,29 @@ def add_start_state(data, sls): # the start state is either the first state whose id declaration has # no __sls__, or it's the first state whose id declaration has a # __sls__ == sls. - non_sids = ('include', 'exclude', 'extend') + non_sids = ("include", "exclude", "extend") for sid, states in six.iteritems(data): - if sid in non_sids or sid.startswith('__'): + if sid in non_sids or sid.startswith("__"): continue - if '__sls__' not in states or states['__sls__'] == sls: + if "__sls__" not in states or states["__sls__"] == sls: break else: - raise SaltRenderError('Can\'t determine the first state in the sls file!') + raise SaltRenderError("Can't determine the first state in the sls file!") reqin = {state_name(next(six.iterkeys(data[sid]))): sid} - data[start_sid] = {STATE_FUNC: [{'require_in': [reqin]}]} + data[start_sid] = {STATE_FUNC: [{"require_in": [reqin]}]} def add_goal_state(data): - goal_sid = __opts__['stateconf_goal_state'] + goal_sid = __opts__["stateconf_goal_state"] if goal_sid in data: raise SaltRenderError( - 'Can\'t generate goal state({0})! The same state id already ' - 'exists!'.format(goal_sid) + "Can't generate goal state({0})! The same state id already " + "exists!".format(goal_sid) ) else: reqlist = [] - for sid, states, state, _ in \ - statelist(data, ('include', 'exclude', 'extend')): - if '__sls__' in states: + for sid, states, state, _ in statelist(data, ("include", "exclude", "extend")): + if "__sls__" in states: # Then id declaration must have been included from a # rendered sls. Currently, this is only possible with # pydsl's high state output. @@ -521,11 +533,11 @@ def add_goal_state(data): def state_name(sname): - ''' + """ Return the name of the state regardless if sname is just the state name or a state.func name. - ''' - return sname.split('.', 1)[0] + """ + return sname.split(".", 1)[0] # Quick and dirty way to get attribute access for dictionary keys. @@ -544,13 +556,13 @@ class Bunch(dict): # STATE_CONF is: # { state_id => {name1: value1} } # -STATE_CONF = {} # stateconf.set -STATE_CONF_EXT = {} # stateconf.set under extend: ... +STATE_CONF = {} # stateconf.set +STATE_CONF_EXT = {} # stateconf.set under extend: ... def extract_state_confs(data, is_extend=False): for state_id, state_dict in six.iteritems(data): - if state_id == 'extend' and not is_extend: + if state_id == "extend" and not is_extend: extract_state_confs(state_dict, True) continue @@ -571,7 +583,7 @@ def extract_state_confs(data, is_extend=False): if not is_extend and state_id in STATE_CONF_EXT: extend = STATE_CONF_EXT[state_id] - for requisite in 'require', 'watch', 'listen', 'onchanges', 'onfail': + for requisite in "require", "watch", "listen", "onchanges", "onfail": if requisite in extend: extend[requisite] += to_dict[state_id].get(requisite, []) to_dict[state_id].update(STATE_CONF_EXT[state_id]) diff --git a/salt/renderers/wempy.py b/salt/renderers/wempy.py index e7625081316..d5b59e47459 100644 --- a/salt/renderers/wempy.py +++ b/salt/renderers/wempy.py @@ -3,33 +3,33 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.utils.templates +from salt.exceptions import SaltRenderError + # Import salt libs from salt.ext import six -from salt.exceptions import SaltRenderError -import salt.utils.templates -def render(template_file, - saltenv='base', - sls='', - argline='', - context=None, - **kws): - ''' +def render(template_file, saltenv="base", sls="", argline="", context=None, **kws): + """ Render the data passing the functions and grains into the rendering system :rtype: string - ''' - tmp_data = salt.utils.templates.WEMPY(template_file, to_str=True, - salt=__salt__, - grains=__grains__, - opts=__opts__, - pillar=__pillar__, - saltenv=saltenv, - sls=sls, - context=context, - **kws) - if not tmp_data.get('result', False): - raise SaltRenderError(tmp_data.get('data', - 'Unknown render error in the wempy renderer')) - return six.moves.StringIO(tmp_data['data']) + """ + tmp_data = salt.utils.templates.WEMPY( + template_file, + to_str=True, + salt=__salt__, + grains=__grains__, + opts=__opts__, + pillar=__pillar__, + saltenv=saltenv, + sls=sls, + context=context, + **kws + ) + if not tmp_data.get("result", False): + raise SaltRenderError( + tmp_data.get("data", "Unknown render error in the wempy renderer") + ) + return six.moves.StringIO(tmp_data["data"]) diff --git a/salt/renderers/yaml.py b/salt/renderers/yaml.py index ea3e7ceb1a1..fe1666a549c 100644 --- a/salt/renderers/yaml.py +++ b/salt/renderers/yaml.py @@ -1,60 +1,63 @@ # -*- coding: utf-8 -*- -''' +""" YAML Renderer for Salt For YAML usage information see :ref:`Understanding YAML <yaml>`. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging import warnings -from yaml.scanner import ScannerError -from yaml.parser import ParserError -from yaml.constructor import ConstructorError # Import salt libs import salt.utils.url import salt.utils.yamlloader as yamlloader_new import salt.utils.yamlloader_old as yamlloader_old -from salt.utils.odict import OrderedDict from salt.exceptions import SaltRenderError from salt.ext import six from salt.ext.six import string_types +from salt.utils.odict import OrderedDict +from yaml.constructor import ConstructorError +from yaml.parser import ParserError +from yaml.scanner import ScannerError log = logging.getLogger(__name__) _ERROR_MAP = { - ("found character '\\t' that cannot " - "start any token"): 'Illegal tab character' + ("found character '\\t' that cannot " "start any token"): "Illegal tab character" } def get_yaml_loader(argline): - ''' + """ Return the ordered dict yaml loader - ''' + """ + def yaml_loader(*args): - if __opts__.get('use_yamlloader_old'): + if __opts__.get("use_yamlloader_old"): yamlloader = yamlloader_old else: yamlloader = yamlloader_new return yamlloader.SaltYamlSafeLoader(*args, dictclass=OrderedDict) + return yaml_loader -def render(yaml_data, saltenv='base', sls='', argline='', **kws): - ''' +def render(yaml_data, saltenv="base", sls="", argline="", **kws): + """ Accepts YAML as a string or as a file object and runs it through the YAML parser. :rtype: A Python data structure - ''' - if __opts__.get('use_yamlloader_old'): - log.warning('Using the old YAML Loader for rendering, ' - 'consider disabling this and using the tojson' - ' filter.') + """ + if __opts__.get("use_yamlloader_old"): + log.warning( + "Using the old YAML Loader for rendering, " + "consider disabling this and using the tojson" + " filter." + ) yamlloader = yamlloader_old else: yamlloader = yamlloader_new @@ -72,25 +75,28 @@ def render(yaml_data, saltenv='base', sls='', argline='', **kws): if len(warn_list) > 0: for item in warn_list: log.warning( - '%s found in %s saltenv=%s', - item.message, salt.utils.url.create(sls), saltenv + "%s found in %s saltenv=%s", + item.message, + salt.utils.url.create(sls), + saltenv, ) if not data: data = {} - log.debug('Results of YAML rendering: \n%s', data) + log.debug("Results of YAML rendering: \n%s", data) def _validate_data(data): - ''' + """ PyYAML will for some reason allow improper YAML to be formed into an unhashable dict (that is, one with a dict as a key). This function will recursively go through and check the keys to make sure they're not dicts. - ''' + """ if isinstance(data, dict): for key, value in six.iteritems(data): if isinstance(key, dict): raise SaltRenderError( - 'Invalid YAML, possible double curly-brace') + "Invalid YAML, possible double curly-brace" + ) _validate_data(value) elif isinstance(data, list): for item in data: diff --git a/salt/renderers/yamlex.py b/salt/renderers/yamlex.py index 8e05eab910b..2184b16d4b8 100644 --- a/salt/renderers/yamlex.py +++ b/salt/renderers/yamlex.py @@ -12,22 +12,24 @@ from salt.serializers.yamlex import deserialize log = logging.getLogger(__name__) -def render(sls_data, saltenv='base', sls='', **kws): - ''' +def render(sls_data, saltenv="base", sls="", **kws): + """ Accepts YAML_EX as a string or as a file object and runs it through the YAML_EX parser. :rtype: A Python data structure - ''' + """ with warnings.catch_warnings(record=True) as warn_list: data = deserialize(sls_data) or {} for item in warn_list: log.warning( - '%s found in %s saltenv=%s', - item.message, salt.utils.url.create(sls), saltenv + "%s found in %s saltenv=%s", + item.message, + salt.utils.url.create(sls), + saltenv, ) - log.debug('Results of SLS rendering: \n%s', data) + log.debug("Results of SLS rendering: \n%s", data) return data diff --git a/salt/returners/__init__.py b/salt/returners/__init__.py index 73fabb82320..f79b3b3a99c 100644 --- a/salt/returners/__init__.py +++ b/salt/returners/__init__.py @@ -1,23 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" Returners Directory :func:`get_returner_options` is a general purpose function that returners may use to fetch their configuration options. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + from salt.ext import six log = logging.getLogger(__name__) -def get_returner_options(virtualname=None, - ret=None, - attrs=None, - **kwargs): - ''' +def get_returner_options(virtualname=None, ret=None, attrs=None, **kwargs): + """ Get the returner options from salt. :param str virtualname: The returner virtualname (as returned @@ -62,47 +60,34 @@ def get_returner_options(virtualname=None, For each key in profile_attr, a value is read in the are used to fetch a value pointed by 'virtualname.%key' in the dict found thanks to the param `profile_attr` - ''' + """ ret_config = _fetch_ret_config(ret) attrs = attrs or {} - profile_attr = kwargs.get('profile_attr', None) - profile_attrs = kwargs.get('profile_attrs', None) - defaults = kwargs.get('defaults', None) - __salt__ = kwargs.get('__salt__', {}) - __opts__ = kwargs.get('__opts__', {}) + profile_attr = kwargs.get("profile_attr", None) + profile_attrs = kwargs.get("profile_attrs", None) + defaults = kwargs.get("defaults", None) + __salt__ = kwargs.get("__salt__", {}) + __opts__ = kwargs.get("__opts__", {}) # select the config source - cfg = __salt__.get('config.option', __opts__) + cfg = __salt__.get("config.option", __opts__) # browse the config for relevant options, store them in a dict - _options = dict( - _options_browser( - cfg, - ret_config, - defaults, - virtualname, - attrs, - ) - ) + _options = dict(_options_browser(cfg, ret_config, defaults, virtualname, attrs,)) # override some values with relevant profile options _options.update( _fetch_profile_opts( - cfg, - virtualname, - __salt__, - _options, - profile_attr, - profile_attrs + cfg, virtualname, __salt__, _options, profile_attr, profile_attrs ) ) # override some values with relevant options from # keyword arguments passed via return_kwargs - if ret and 'ret_kwargs' in ret: - _options.update(ret['ret_kwargs']) + if ret and "ret_kwargs" in ret: + _options.update(ret["ret_kwargs"]) return _options @@ -115,9 +100,9 @@ def _fetch_ret_config(ret): """ if not ret: return None - if 'ret_config' not in ret: - return '' - return six.text_type(ret['ret_config']) + if "ret_config" not in ret: + return "" + return six.text_type(ret["ret_config"]) def _fetch_option(cfg, ret_config, virtualname, attr_name): @@ -131,9 +116,9 @@ def _fetch_option(cfg, ret_config, virtualname, attr_name): if isinstance(cfg, dict): c_cfg = cfg else: - c_cfg = cfg('{0}'.format(virtualname), {}) + c_cfg = cfg("{0}".format(virtualname), {}) - default_cfg_key = '{0}.{1}'.format(virtualname, attr_name) + default_cfg_key = "{0}.{1}".format(virtualname, attr_name) if not ret_config: # Using the default configuration key if isinstance(cfg, dict): @@ -145,20 +130,13 @@ def _fetch_option(cfg, ret_config, virtualname, attr_name): return c_cfg.get(attr_name, cfg(default_cfg_key)) # Using ret_config to override the default configuration key - ret_cfg = cfg('{0}.{1}'.format(ret_config, virtualname), {}) + ret_cfg = cfg("{0}.{1}".format(ret_config, virtualname), {}) - override_default_cfg_key = '{0}.{1}.{2}'.format( - ret_config, - virtualname, - attr_name, - ) + override_default_cfg_key = "{0}.{1}.{2}".format(ret_config, virtualname, attr_name,) override_cfg_default = cfg(override_default_cfg_key) # Look for the configuration item in the override location - ret_override_cfg = ret_cfg.get( - attr_name, - override_cfg_default - ) + ret_override_cfg = ret_cfg.get(attr_name, override_cfg_default) if ret_override_cfg: return ret_override_cfg @@ -185,7 +163,7 @@ def _options_browser(cfg, ret_config, defaults, virtualname, options): # Attribute not found, check for a default value if defaults: if option in defaults: - log.info('Using default for %s %s', virtualname, option) + log.info("Using default for %s %s", virtualname, option) yield option, defaults[option] continue @@ -194,12 +172,8 @@ def _options_browser(cfg, ret_config, defaults, virtualname, options): def _fetch_profile_opts( - cfg, virtualname, - __salt__, - _options, - profile_attr, - profile_attrs - ): + cfg, virtualname, __salt__, _options, profile_attr, profile_attrs +): """ Fetches profile specific options if applicable @@ -216,9 +190,9 @@ def _fetch_profile_opts( creds = {} profile = _options[profile_attr] if profile: - log.info('Using profile %s', profile) + log.info("Using profile %s", profile) - if 'config.option' in __salt__: + if "config.option" in __salt__: creds = cfg(profile) else: creds = cfg.get(profile) @@ -227,9 +201,6 @@ def _fetch_profile_opts( return {} return dict( - ( - pattr, - creds.get('{0}.{1}'.format(virtualname, profile_attrs[pattr])) - ) + (pattr, creds.get("{0}.{1}".format(virtualname, profile_attrs[pattr]))) for pattr in profile_attrs - ) + ) diff --git a/salt/returners/carbon_return.py b/salt/returners/carbon_return.py index 2d4d6cf8808..b5c360000a0 100644 --- a/salt/returners/carbon_return.py +++ b/salt/returners/carbon_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Take data from salt and "return" it into a carbon receiver Add the following configuration to the minion configuration file: @@ -79,10 +79,11 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return carbon --return_kwargs '{"skip_on_error": False}' -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import collections import logging import socket @@ -90,18 +91,19 @@ import struct import time from contextlib import contextmanager +import salt.returners + # Import salt libs import salt.utils.jid -import salt.returners # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import cPickle, map # pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext.six.moves import cPickle, map log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'carbon' +__virtualname__ = "carbon" def __virtual__(): @@ -109,80 +111,77 @@ def __virtual__(): def _get_options(ret): - ''' + """ Returns options used for the carbon returner. - ''' - attrs = {'host': 'host', - 'port': 'port', - 'skip': 'skip_on_error', - 'mode': 'mode'} + """ + attrs = {"host": "host", "port": "port", "skip": "skip_on_error", "mode": "mode"} - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options @contextmanager def _carbon(host, port): - ''' + """ Context manager to ensure the clean creation and destruction of a socket. host The IP or hostname of the carbon server port The port that carbon is listening on - ''' + """ carbon_sock = None try: - carbon_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, - socket.IPPROTO_TCP) + carbon_sock = socket.socket( + socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP + ) carbon_sock.connect((host, port)) except socket.error as err: - log.error('Error connecting to %s:%s, %s', host, port, err) + log.error("Error connecting to %s:%s, %s", host, port, err) raise else: - log.debug('Connected to carbon') + log.debug("Connected to carbon") yield carbon_sock finally: if carbon_sock is not None: # Shut down and close socket - log.debug('Destroying carbon socket') + log.debug("Destroying carbon socket") carbon_sock.shutdown(socket.SHUT_RDWR) carbon_sock.close() def _send_picklemetrics(metrics): - ''' + """ Format metrics for the carbon pickle protocol - ''' + """ - metrics = [(metric_name, (timestamp, value)) - for (metric_name, value, timestamp) in metrics] + metrics = [ + (metric_name, (timestamp, value)) for (metric_name, value, timestamp) in metrics + ] data = cPickle.dumps(metrics, -1) - payload = struct.pack(b'!L', len(data)) + data + payload = struct.pack(b"!L", len(data)) + data return payload def _send_textmetrics(metrics): - ''' + """ Format metrics for the carbon plaintext protocol - ''' + """ - data = [' '.join(map(six.text_type, metric)) for metric in metrics] + [''] + data = [" ".join(map(six.text_type, metric)) for metric in metrics] + [""] - return '\n'.join(data) + return "\n".join(data) def _walk(path, value, metrics, timestamp, skip): - ''' + """ Recursively include metrics from *value*. path @@ -198,25 +197,30 @@ def _walk(path, value, metrics, timestamp, skip): skip Whether or not to skip metrics when there's an error casting the value to a float. Defaults to `False`. - ''' + """ log.trace( - 'Carbon return walking path: %s, value: %s, metrics: %s, ' - 'timestamp: %s', path, value, metrics, timestamp + "Carbon return walking path: %s, value: %s, metrics: %s, " "timestamp: %s", + path, + value, + metrics, + timestamp, ) if isinstance(value, collections.Mapping): for key, val in six.iteritems(value): - _walk('{0}.{1}'.format(path, key), val, metrics, timestamp, skip) + _walk("{0}.{1}".format(path, key), val, metrics, timestamp, skip) elif isinstance(value, list): for item in value: - _walk('{0}.{1}'.format(path, item), item, metrics, timestamp, skip) + _walk("{0}.{1}".format(path, item), item, metrics, timestamp, skip) else: try: val = float(value) metrics.append((path, val, timestamp)) except (TypeError, ValueError): - msg = 'Error in carbon returner, when trying to convert metric: ' \ - '{0}, with val: {1}'.format(path, value) + msg = ( + "Error in carbon returner, when trying to convert metric: " + "{0}, with val: {1}".format(path, value) + ) if skip: log.debug(msg) else: @@ -225,21 +229,21 @@ def _walk(path, value, metrics, timestamp, skip): def _send(saltdata, metric_base, opts): - ''' + """ Send the data to carbon - ''' + """ - host = opts.get('host') - port = opts.get('port') - skip = opts.get('skip') - metric_base_pattern = opts.get('carbon.metric_base_pattern') - mode = opts.get('mode').lower() if 'mode' in opts else 'text' + host = opts.get("host") + port = opts.get("port") + skip = opts.get("skip") + metric_base_pattern = opts.get("carbon.metric_base_pattern") + mode = opts.get("mode").lower() if "mode" in opts else "text" - log.debug('Carbon minion configured with host: %s:%s', host, port) - log.debug('Using carbon protocol: %s', mode) + log.debug("Carbon minion configured with host: %s:%s", host, port) + log.debug("Using carbon protocol: %s", mode) if not (host and port): - log.error('Host or port not defined') + log.error("Host or port not defined") return # TODO: possible to use time return from salt job to be slightly more precise? @@ -247,63 +251,63 @@ def _send(saltdata, metric_base, opts): # {'fun': 'test.version', 'jid': '20130113193949451054', 'return': '0.11.0', 'id': 'salt'} timestamp = int(time.time()) - handler = _send_picklemetrics if mode == 'pickle' else _send_textmetrics + handler = _send_picklemetrics if mode == "pickle" else _send_textmetrics metrics = [] - log.trace('Carbon returning walking data: %s', saltdata) + log.trace("Carbon returning walking data: %s", saltdata) _walk(metric_base, saltdata, metrics, timestamp, skip) data = handler(metrics) - log.trace('Carbon inserting data: %s', data) + log.trace("Carbon inserting data: %s", data) with _carbon(host, port) as sock: total_sent_bytes = 0 while total_sent_bytes < len(data): sent_bytes = sock.send(data[total_sent_bytes:]) if sent_bytes == 0: - log.error('Bytes sent 0, Connection reset?') + log.error("Bytes sent 0, Connection reset?") return - log.debug('Sent %s bytes to carbon', sent_bytes) + log.debug("Sent %s bytes to carbon", sent_bytes) total_sent_bytes += sent_bytes def event_return(events): - ''' + """ Return event data to remote carbon server Provide a list of events to be stored in carbon - ''' + """ opts = _get_options({}) # Pass in empty ret, since this is a list of events - opts['skip'] = True + opts["skip"] = True for event in events: - log.trace('Carbon returner received event: %s', event) - metric_base = event['tag'] - saltdata = event['data'].get('data') + log.trace("Carbon returner received event: %s", event) + metric_base = event["tag"] + saltdata = event["data"].get("data") _send(saltdata, metric_base, opts) def returner(ret): - ''' + """ Return data to a remote carbon server using the text metric protocol Each metric will look like:: [module].[function].[minion_id].[metric path [...]].[metric name] - ''' + """ opts = _get_options(ret) - metric_base = ret['fun'] + metric_base = ret["fun"] # Strip the hostname from the carbon base if we are returning from virt # module since then we will get stable metric bases even if the VM is # migrate from host to host - if not metric_base.startswith('virt.'): - metric_base += '.' + ret['id'].replace('.', '_') + if not metric_base.startswith("virt."): + metric_base += "." + ret["id"].replace(".", "_") - saltdata = ret['return'] + saltdata = ret["return"] _send(saltdata, metric_base, opts) def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/cassandra_cql_return.py b/salt/returners/cassandra_cql_return.py index f9774faddd3..489025988f5 100644 --- a/salt/returners/cassandra_cql_return.py +++ b/salt/returners/cassandra_cql_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a cassandra server .. versionadded:: 2015.5.0 @@ -116,19 +116,20 @@ As always, your mileage may vary and your Cassandra cluster may have different needs. SaltStack has seen situations where these timeouts can resolve some stacktraces that appear to come from the Datastax Python driver. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -import uuid import time +import uuid + +import salt.exceptions # Import salt libs import salt.returners import salt.utils.jid import salt.utils.json -import salt.exceptions from salt.exceptions import CommandExecutionError from salt.ext import six @@ -155,6 +156,7 @@ try: from cassandra.connection import ConnectionException, ConnectionShutdown from cassandra.auth import PlainTextAuthProvider from cassandra.query import dict_factory + # pylint: enable=unused-import,no-name-in-module HAS_CASSANDRA_DRIVER = True except ImportError as e: @@ -170,73 +172,76 @@ log = logging.getLogger(__name__) # DataStax Python Driver for Apache Cassandra. Namespacing allows both the # modules/cassandra_cql and returners/cassandra_cql modules to use the # virtualname 'cassandra_cql'. -__virtualname__ = 'cassandra_cql' +__virtualname__ = "cassandra_cql" def __virtual__(): if not HAS_CASSANDRA_DRIVER: - return False, 'Could not import cassandra_cql returner; ' \ - 'cassandra-driver is not installed.' + return ( + False, + "Could not import cassandra_cql returner; " + "cassandra-driver is not installed.", + ) return True def returner(ret): - ''' + """ Return data to one of potentially many clustered cassandra nodes - ''' - query = '''INSERT INTO salt.salt_returns ( + """ + query = """INSERT INTO salt.salt_returns ( jid, minion_id, fun, alter_time, full_ret, return, success - ) VALUES (?, ?, ?, ?, ?, ?, ?)''' + ) VALUES (?, ?, ?, ?, ?, ?, ?)""" - statement_arguments = ['{0}'.format(ret['jid']), - '{0}'.format(ret['id']), - '{0}'.format(ret['fun']), - int(time.time() * 1000), - salt.utils.json.dumps(ret).replace("'", "''"), - salt.utils.json.dumps(ret['return']).replace("'", "''"), - ret.get('success', False)] + statement_arguments = [ + "{0}".format(ret["jid"]), + "{0}".format(ret["id"]), + "{0}".format(ret["fun"]), + int(time.time() * 1000), + salt.utils.json.dumps(ret).replace("'", "''"), + salt.utils.json.dumps(ret["return"]).replace("'", "''"), + ret.get("success", False), + ] # cassandra_cql.cql_query may raise a CommandExecutionError try: - __salt__['cassandra_cql.cql_query_with_prepare'](query, - 'returner_return', - tuple(statement_arguments), - asynchronous=True) + __salt__["cassandra_cql.cql_query_with_prepare"]( + query, "returner_return", tuple(statement_arguments), asynchronous=True + ) except CommandExecutionError: - log.critical('Could not insert into salt_returns with Cassandra returner.') + log.critical("Could not insert into salt_returns with Cassandra returner.") raise except Exception as e: # pylint: disable=broad-except - log.critical('Unexpected error while inserting into salt_returns: %s', e) + log.critical("Unexpected error while inserting into salt_returns: %s", e) raise # Store the last function called by the minion # The data in salt.minions will be used by get_fun and get_minions - query = '''INSERT INTO salt.minions ( + query = """INSERT INTO salt.minions ( minion_id, last_fun - ) VALUES (?, ?)''' + ) VALUES (?, ?)""" - statement_arguments = ['{0}'.format(ret['id']), '{0}'.format(ret['fun'])] + statement_arguments = ["{0}".format(ret["id"]), "{0}".format(ret["fun"])] # cassandra_cql.cql_query may raise a CommandExecutionError try: - __salt__['cassandra_cql.cql_query_with_prepare'](query, - 'returner_minion', - tuple(statement_arguments), - asynchronous=True) + __salt__["cassandra_cql.cql_query_with_prepare"]( + query, "returner_minion", tuple(statement_arguments), asynchronous=True + ) except CommandExecutionError: - log.critical('Could not store minion ID with Cassandra returner.') + log.critical("Could not store minion ID with Cassandra returner.") raise except Exception as e: # pylint: disable=broad-except log.critical( - 'Unexpected error while inserting minion ID into the minions ' - 'table: %s', e + "Unexpected error while inserting minion ID into the minions " "table: %s", + e, ) raise def event_return(events): - ''' + """ Return event to one of potentially many clustered cassandra nodes Requires that configuration be enabled via 'event_return' @@ -246,91 +251,91 @@ def event_return(events): highly inefficient nature of creating a monotonically increasing number across all nodes in a distributed database. Each event will be assigned a uuid by the connecting client. - ''' + """ for event in events: - tag = event.get('tag', '') - data = event.get('data', '') - query = '''INSERT INTO salt.salt_events ( + tag = event.get("tag", "") + data = event.get("data", "") + query = """INSERT INTO salt.salt_events ( id, alter_time, data, master_id, tag ) VALUES ( ?, ?, ?, ?, ?) - ''' - statement_arguments = [six.text_type(uuid.uuid1()), - int(time.time() * 1000), - salt.utils.json.dumps(data).replace("'", "''"), - __opts__['id'], - tag] + """ + statement_arguments = [ + six.text_type(uuid.uuid1()), + int(time.time() * 1000), + salt.utils.json.dumps(data).replace("'", "''"), + __opts__["id"], + tag, + ] # cassandra_cql.cql_query may raise a CommandExecutionError try: - __salt__['cassandra_cql.cql_query_with_prepare'](query, 'salt_events', - statement_arguments, - asynchronous=True) + __salt__["cassandra_cql.cql_query_with_prepare"]( + query, "salt_events", statement_arguments, asynchronous=True + ) except CommandExecutionError: - log.critical('Could not store events with Cassandra returner.') + log.critical("Could not store events with Cassandra returner.") raise except Exception as e: # pylint: disable=broad-except - log.critical( - 'Unexpected error while inserting into salt_events: %s', e) + log.critical("Unexpected error while inserting into salt_events: %s", e) raise def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid id - ''' + """ # Load is being stored as a text datatype. Single quotes are used in the # VALUES list. Therefore, all single quotes contained in the results from # salt.utils.json.dumps(load) must be escaped Cassandra style. - query = '''INSERT INTO salt.jids ( + query = """INSERT INTO salt.jids ( jid, load - ) VALUES (?, ?)''' + ) VALUES (?, ?)""" - statement_arguments = [ - jid, - salt.utils.json.dumps(load).replace("'", "''") - ] + statement_arguments = [jid, salt.utils.json.dumps(load).replace("'", "''")] # cassandra_cql.cql_query may raise a CommandExecutionError try: - __salt__['cassandra_cql.cql_query_with_prepare'](query, 'save_load', - statement_arguments, - asynchronous=True) + __salt__["cassandra_cql.cql_query_with_prepare"]( + query, "save_load", statement_arguments, asynchronous=True + ) except CommandExecutionError: - log.critical('Could not save load in jids table.') + log.critical("Could not save load in jids table.") raise except Exception as e: # pylint: disable=broad-except - log.critical('Unexpected error while inserting into jids: %s', e) + log.critical("Unexpected error while inserting into jids: %s", e) raise def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ # salt-run jobs.list_jobs FAILED def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' - query = '''SELECT load FROM salt.jids WHERE jid = ?;''' + """ + query = """SELECT load FROM salt.jids WHERE jid = ?;""" ret = {} # cassandra_cql.cql_query may raise a CommandExecutionError try: - data = __salt__['cassandra_cql.cql_query_with_prepare'](query, 'get_load', [jid]) + data = __salt__["cassandra_cql.cql_query_with_prepare"]( + query, "get_load", [jid] + ) if data: - load = data[0].get('load') + load = data[0].get("load") if load: ret = salt.utils.json.loads(load) except CommandExecutionError: - log.critical('Could not get load from jids table.') + log.critical("Could not get load from jids table.") raise except Exception as e: # pylint: disable=broad-except - log.critical('Unexpected error while getting load from jids: %s', e) + log.critical("Unexpected error while getting load from jids: %s", e) raise return ret @@ -338,28 +343,27 @@ def get_load(jid): # salt-call ret.get_jid cassandra_cql 20150327234537907315 PASSED def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' - query = '''SELECT minion_id, full_ret FROM salt.salt_returns WHERE jid = ?;''' + """ + query = """SELECT minion_id, full_ret FROM salt.salt_returns WHERE jid = ?;""" ret = {} # cassandra_cql.cql_query may raise a CommandExecutionError try: - data = __salt__['cassandra_cql.cql_query_with_prepare'](query, 'get_jid', [jid]) + data = __salt__["cassandra_cql.cql_query_with_prepare"](query, "get_jid", [jid]) if data: for row in data: - minion = row.get('minion_id') - full_ret = row.get('full_ret') + minion = row.get("minion_id") + full_ret = row.get("full_ret") if minion and full_ret: ret[minion] = salt.utils.json.loads(full_ret) except CommandExecutionError: - log.critical('Could not select job specific information.') + log.critical("Could not select job specific information.") raise except Exception as e: # pylint: disable=broad-except - log.critical( - 'Unexpected error while getting job specific information: %s', e) + log.critical("Unexpected error while getting job specific information: %s", e) raise return ret @@ -367,28 +371,27 @@ def get_jid(jid): # salt-call ret.get_fun cassandra_cql test.ping PASSED def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' - query = '''SELECT minion_id, last_fun FROM salt.minions where last_fun = ?;''' + """ + query = """SELECT minion_id, last_fun FROM salt.minions where last_fun = ?;""" ret = {} # cassandra_cql.cql_query may raise a CommandExecutionError try: - data = __salt__['cassandra_cql.cql_query'](query, 'get_fun', [fun]) + data = __salt__["cassandra_cql.cql_query"](query, "get_fun", [fun]) if data: for row in data: - minion = row.get('minion_id') - last_fun = row.get('last_fun') + minion = row.get("minion_id") + last_fun = row.get("last_fun") if minion and last_fun: ret[minion] = last_fun except CommandExecutionError: - log.critical('Could not get the list of minions.') + log.critical("Could not get the list of minions.") raise except Exception as e: # pylint: disable=broad-except - log.critical( - 'Unexpected error while getting list of minions: %s', e) + log.critical("Unexpected error while getting list of minions: %s", e) raise return ret @@ -396,30 +399,29 @@ def get_fun(fun): # salt-call ret.get_jids cassandra_cql PASSED def get_jids(): - ''' + """ Return a list of all job ids - ''' - query = '''SELECT jid, load FROM salt.jids;''' + """ + query = """SELECT jid, load FROM salt.jids;""" ret = {} # cassandra_cql.cql_query may raise a CommandExecutionError try: - data = __salt__['cassandra_cql.cql_query'](query) + data = __salt__["cassandra_cql.cql_query"](query) if data: for row in data: - jid = row.get('jid') - load = row.get('load') + jid = row.get("jid") + load = row.get("load") if jid and load: ret[jid] = salt.utils.jid.format_jid_instance( - jid, - salt.utils.json.loads(load)) + jid, salt.utils.json.loads(load) + ) except CommandExecutionError: - log.critical('Could not get a list of all job ids.') + log.critical("Could not get a list of all job ids.") raise except Exception as e: # pylint: disable=broad-except - log.critical( - 'Unexpected error while getting list of all job ids: %s', e) + log.critical("Unexpected error while getting list of all job ids: %s", e) raise return ret @@ -427,34 +429,33 @@ def get_jids(): # salt-call ret.get_minions cassandra_cql PASSED def get_minions(): - ''' + """ Return a list of minions - ''' - query = '''SELECT DISTINCT minion_id FROM salt.minions;''' + """ + query = """SELECT DISTINCT minion_id FROM salt.minions;""" ret = [] # cassandra_cql.cql_query may raise a CommandExecutionError try: - data = __salt__['cassandra_cql.cql_query'](query) + data = __salt__["cassandra_cql.cql_query"](query) if data: for row in data: - minion = row.get('minion_id') + minion = row.get("minion_id") if minion: ret.append(minion) except CommandExecutionError: - log.critical('Could not get the list of minions.') + log.critical("Could not get the list of minions.") raise except Exception as e: # pylint: disable=broad-except - log.critical( - 'Unexpected error while getting list of minions: %s', e) + log.critical("Unexpected error while getting list of minions: %s", e) raise return ret def prep_jid(nocache, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/cassandra_return.py b/salt/returners/cassandra_return.py index 07b4f02fb7c..f53817769cc 100644 --- a/salt/returners/cassandra_return.py +++ b/salt/returners/cassandra_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a Cassandra ColumnFamily Here's an example Keyspace / ColumnFamily setup that works with this @@ -17,10 +17,11 @@ Required python modules: pycassa To use the cassandra returner, append '--return cassandra' to the salt command. ex: salt '*' test.ping --return cassandra -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -28,56 +29,64 @@ import salt.utils.jid # Import third party libs from salt.ext import six + try: import pycassa # pylint: disable=import-error + HAS_PYCASSA = True except ImportError: HAS_PYCASSA = False log = logging.getLogger(__name__) -__opts__ = {'cassandra.servers': ['localhost:9160'], - 'cassandra.keyspace': 'salt', - 'cassandra.column_family': 'returns', - 'cassandra.consistency_level': 'ONE'} +__opts__ = { + "cassandra.servers": ["localhost:9160"], + "cassandra.keyspace": "salt", + "cassandra.column_family": "returns", + "cassandra.consistency_level": "ONE", +} # Define the module's virtual name -__virtualname__ = 'cassandra' +__virtualname__ = "cassandra" def __virtual__(): if not HAS_PYCASSA: - return False, 'Could not import cassandra returner; pycassa is not installed.' + return False, "Could not import cassandra returner; pycassa is not installed." return __virtualname__ def returner(ret): - ''' + """ Return data to a Cassandra ColumnFamily - ''' + """ - consistency_level = getattr(pycassa.ConsistencyLevel, - __opts__['cassandra.consistency_level']) + consistency_level = getattr( + pycassa.ConsistencyLevel, __opts__["cassandra.consistency_level"] + ) - pool = pycassa.ConnectionPool(__opts__['cassandra.keyspace'], - __opts__['cassandra.servers']) - ccf = pycassa.ColumnFamily(pool, __opts__['cassandra.column_family'], - write_consistency_level=consistency_level) + pool = pycassa.ConnectionPool( + __opts__["cassandra.keyspace"], __opts__["cassandra.servers"] + ) + ccf = pycassa.ColumnFamily( + pool, + __opts__["cassandra.column_family"], + write_consistency_level=consistency_level, + ) - columns = {'fun': ret['fun'], - 'id': ret['id']} - if isinstance(ret['return'], dict): - for key, value in six.iteritems(ret['return']): - columns['return.{0}'.format(key)] = six.text_type(value) + columns = {"fun": ret["fun"], "id": ret["id"]} + if isinstance(ret["return"], dict): + for key, value in six.iteritems(ret["return"]): + columns["return.{0}".format(key)] = six.text_type(value) else: - columns['return'] = six.text_type(ret['return']) + columns["return"] = six.text_type(ret["return"]) log.debug(columns) - ccf.insert(ret['jid'], columns) + ccf.insert(ret["jid"], columns) def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/couchbase_return.py b/salt/returners/couchbase_return.py index 2ab12b53c23..ece18829667 100644 --- a/salt/returners/couchbase_return.py +++ b/salt/returners/couchbase_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Simple returner for Couchbase. Optional configuration settings are listed below, along with sane defaults. @@ -47,15 +47,10 @@ JID/MINION_ID ============= return: return_data full_ret: full load of job return -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import logging -try: - import couchbase - HAS_DEPS = True -except ImportError: - HAS_DEPS = False +import logging # Import Salt libs import salt.utils.jid @@ -65,15 +60,23 @@ import salt.utils.minions # Import 3rd-party libs from salt.ext import six +try: + import couchbase + + HAS_DEPS = True +except ImportError: + HAS_DEPS = False + + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'couchbase' +__virtualname__ = "couchbase" # some globals COUCHBASE_CONN = None -DESIGN_NAME = 'couchbase_returner' +DESIGN_NAME = "couchbase_returner" VERIFIED_VIEWS = False _json = salt.utils.json.import_json() @@ -85,7 +88,7 @@ def _json_dumps(obj, **kwargs): def __virtual__(): if not HAS_DEPS: - return False, 'Could not import couchbase returner; couchbase is not installed.' + return False, "Could not import couchbase returner; couchbase is not installed." couchbase.set_json_converters(_json_dumps, salt.utils.json.loads) @@ -93,54 +96,64 @@ def __virtual__(): def _get_options(): - ''' + """ Get the couchbase options from salt. Apply defaults if required. - ''' - return {'host': __opts__.get('couchbase.host', 'salt'), - 'port': __opts__.get('couchbase.port', 8091), - 'bucket': __opts__.get('couchbase.bucket', 'salt'), - 'password': __opts__.get('couchbase.password', '')} + """ + return { + "host": __opts__.get("couchbase.host", "salt"), + "port": __opts__.get("couchbase.port", 8091), + "bucket": __opts__.get("couchbase.bucket", "salt"), + "password": __opts__.get("couchbase.password", ""), + } def _get_connection(): - ''' + """ Global function to access the couchbase connection (and make it if its closed) - ''' + """ global COUCHBASE_CONN if COUCHBASE_CONN is None: opts = _get_options() - if opts['password']: - COUCHBASE_CONN = couchbase.Couchbase.connect(host=opts['host'], - port=opts['port'], - bucket=opts['bucket'], - password=opts['password']) + if opts["password"]: + COUCHBASE_CONN = couchbase.Couchbase.connect( + host=opts["host"], + port=opts["port"], + bucket=opts["bucket"], + password=opts["password"], + ) else: - COUCHBASE_CONN = couchbase.Couchbase.connect(host=opts['host'], - port=opts['port'], - bucket=opts['bucket']) + COUCHBASE_CONN = couchbase.Couchbase.connect( + host=opts["host"], port=opts["port"], bucket=opts["bucket"] + ) return COUCHBASE_CONN def _verify_views(): - ''' + """ Verify that you have the views you need. This can be disabled by adding couchbase.skip_verify_views: True in config - ''' + """ global VERIFIED_VIEWS - if VERIFIED_VIEWS or __opts__.get('couchbase.skip_verify_views', False): + if VERIFIED_VIEWS or __opts__.get("couchbase.skip_verify_views", False): return cb_ = _get_connection() - ddoc = {'views': {'jids': {'map': "function (doc, meta) { if (meta.id.indexOf('/') === -1 && doc.load){ emit(meta.id, null) } }"}, - 'jid_returns': {'map': "function (doc, meta) { if (meta.id.indexOf('/') > -1){ key_parts = meta.id.split('/'); emit(key_parts[0], key_parts[1]); } }"} - } - } + ddoc = { + "views": { + "jids": { + "map": "function (doc, meta) { if (meta.id.indexOf('/') === -1 && doc.load){ emit(meta.id, null) } }" + }, + "jid_returns": { + "map": "function (doc, meta) { if (meta.id.indexOf('/') > -1){ key_parts = meta.id.split('/'); emit(key_parts[0], key_parts[1]); } }" + }, + } + } try: curr_ddoc = cb_.design_get(DESIGN_NAME, use_devmode=False).value - if curr_ddoc['views'] == ddoc['views']: + if curr_ddoc["views"] == ddoc["views"]: VERIFIED_VIEWS = True return except couchbase.exceptions.HTTPError: @@ -151,20 +164,20 @@ def _verify_views(): def _get_ttl(): - ''' + """ Return the TTL that we should store our objects with - ''' - return __opts__.get('couchbase.ttl', 24) * 60 * 60 # keep_jobs is in hours + """ + return __opts__.get("couchbase.ttl", 24) * 60 * 60 # keep_jobs is in hours -#TODO: add to returner docs-- this is a new one +# TODO: add to returner docs-- this is a new one def prep_jid(nocache=False, passed_jid=None): - ''' + """ Return a job id and prepare the job id directory This is the function responsible for making sure jids don't collide (unless its passed a jid) So do what you have to do to make sure that stays the case - ''' + """ if passed_jid is None: jid = salt.utils.jid.gen_jid(__opts__) else: @@ -173,10 +186,9 @@ def prep_jid(nocache=False, passed_jid=None): cb_ = _get_connection() try: - cb_.add(six.text_type(jid), - {'nocache': nocache}, - ttl=_get_ttl(), - ) + cb_.add( + six.text_type(jid), {"nocache": nocache}, ttl=_get_ttl(), + ) except couchbase.exceptions.KeyExistsError: # TODO: some sort of sleep or something? Spinning is generally bad practice if passed_jid is None: @@ -186,32 +198,31 @@ def prep_jid(nocache=False, passed_jid=None): def returner(load): - ''' + """ Return data to couchbase bucket - ''' + """ cb_ = _get_connection() - hn_key = '{0}/{1}'.format(load['jid'], load['id']) + hn_key = "{0}/{1}".format(load["jid"], load["id"]) try: - ret_doc = {'return': load['return'], - 'full_ret': salt.utils.json.dumps(load)} + ret_doc = {"return": load["return"], "full_ret": salt.utils.json.dumps(load)} - cb_.add(hn_key, - ret_doc, - ttl=_get_ttl(), - ) + cb_.add( + hn_key, ret_doc, ttl=_get_ttl(), + ) except couchbase.exceptions.KeyExistsError: log.error( - 'An extra return was detected from minion %s, please verify ' - 'the minion, this could be a replay attack', load['id'] + "An extra return was detected from minion %s, please verify " + "the minion, this could be a replay attack", + load["id"], ) return False def save_load(jid, clear_load, minion=None): - ''' + """ Save the load to the specified jid - ''' + """ cb_ = _get_connection() try: @@ -220,48 +231,45 @@ def save_load(jid, clear_load, minion=None): cb_.add(six.text_type(jid), {}, ttl=_get_ttl()) jid_doc = cb_.get(six.text_type(jid)) - jid_doc.value['load'] = clear_load + jid_doc.value["load"] = clear_load cb_.replace(six.text_type(jid), jid_doc.value, cas=jid_doc.cas, ttl=_get_ttl()) # if you have a tgt, save that for the UI etc - if 'tgt' in clear_load and clear_load['tgt'] != '': + if "tgt" in clear_load and clear_load["tgt"] != "": ckminions = salt.utils.minions.CkMinions(__opts__) # Retrieve the minions list _res = ckminions.check_minions( - clear_load['tgt'], - clear_load.get('tgt_type', 'glob') - ) - minions = _res['minions'] + clear_load["tgt"], clear_load.get("tgt_type", "glob") + ) + minions = _res["minions"] save_minions(jid, minions) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Save/update the minion list for a given jid. The syndic_id argument is included for API compatibility only. - ''' + """ cb_ = _get_connection() try: jid_doc = cb_.get(six.text_type(jid)) except couchbase.exceptions.NotFoundError: - log.warning('Could not write job cache file for jid: %s', jid) + log.warning("Could not write job cache file for jid: %s", jid) return False # save the minions to a cache so we can see in the UI - if 'minions' in jid_doc.value: - jid_doc.value['minions'] = sorted( - set(jid_doc.value['minions'] + minions) - ) + if "minions" in jid_doc.value: + jid_doc.value["minions"] = sorted(set(jid_doc.value["minions"] + minions)) else: - jid_doc.value['minions'] = minions + jid_doc.value["minions"] = minions cb_.replace(six.text_type(jid), jid_doc.value, cas=jid_doc.cas, ttl=_get_ttl()) def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' + """ cb_ = _get_connection() try: @@ -271,8 +279,8 @@ def get_load(jid): ret = {} try: - ret = jid_doc.value['load'] - ret['Minions'] = jid_doc.value['minions'] + ret = jid_doc.value["load"] + ret["Minions"] = jid_doc.value["minions"] except KeyError as e: log.error(e) @@ -280,59 +288,63 @@ def get_load(jid): def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' + """ cb_ = _get_connection() _verify_views() ret = {} - for result in cb_.query(DESIGN_NAME, 'jid_returns', key=six.text_type(jid), include_docs=True): + for result in cb_.query( + DESIGN_NAME, "jid_returns", key=six.text_type(jid), include_docs=True + ): ret[result.value] = result.doc.value return ret def get_jids(): - ''' + """ Return a list of all job ids - ''' + """ cb_ = _get_connection() _verify_views() ret = {} - for result in cb_.query(DESIGN_NAME, 'jids', include_docs=True): - ret[result.key] = _format_jid_instance(result.key, result.doc.value['load']) + for result in cb_.query(DESIGN_NAME, "jids", include_docs=True): + ret[result.key] = _format_jid_instance(result.key, result.doc.value["load"]) return ret def _format_job_instance(job): - ''' + """ Return a properly formatted job dict - ''' - ret = {'Function': job.get('fun', 'unknown-function'), - 'Arguments': list(job.get('arg', [])), - # unlikely but safeguard from invalid returns - 'Target': job.get('tgt', 'unknown-target'), - 'Target-type': job.get('tgt_type', 'list'), - 'User': job.get('user', 'root')} + """ + ret = { + "Function": job.get("fun", "unknown-function"), + "Arguments": list(job.get("arg", [])), + # unlikely but safeguard from invalid returns + "Target": job.get("tgt", "unknown-target"), + "Target-type": job.get("tgt_type", "list"), + "User": job.get("user", "root"), + } - if 'metadata' in job: - ret['Metadata'] = job.get('metadata', {}) + if "metadata" in job: + ret["Metadata"] = job.get("metadata", {}) else: - if 'kwargs' in job: - if 'metadata' in job['kwargs']: - ret['Metadata'] = job['kwargs'].get('metadata', {}) + if "kwargs" in job: + if "metadata" in job["kwargs"]: + ret["Metadata"] = job["kwargs"].get("metadata", {}) return ret def _format_jid_instance(jid, job): - ''' + """ Return a properly formatted jid dict - ''' + """ ret = _format_job_instance(job) - ret.update({'StartTime': salt.utils.jid.jid_to_time(jid)}) + ret.update({"StartTime": salt.utils.jid.jid_to_time(jid)}) return ret diff --git a/salt/returners/couchdb_return.py b/salt/returners/couchdb_return.py index 6d5014ad5a6..2f5efd03a41 100644 --- a/salt/returners/couchdb_return.py +++ b/salt/returners/couchdb_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Simple returner for CouchDB. Optional configuration settings are listed below, along with sane defaults: @@ -50,32 +50,34 @@ otherwise multi-minion targeting can lead to losing output: * the first returning minion is able to create a document in the database * other minions fail with ``{'error': 'HTTP Error 409: Conflict'}`` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time -# Import 3rd-party libs -# pylint: disable=no-name-in-module,import-error -from salt.ext.six.moves.urllib.error import HTTPError -from salt.ext.six.moves.urllib.request import ( - Request as _Request, - HTTPHandler as _HTTPHandler, - build_opener as _build_opener, -) -# pylint: enable=no-name-in-module,import-error +import salt.returners # Import Salt libs import salt.utils.jid import salt.utils.json -import salt.returners + +# Import 3rd-party libs +# pylint: disable=no-name-in-module,import-error +from salt.ext.six.moves.urllib.error import HTTPError +from salt.ext.six.moves.urllib.request import HTTPHandler as _HTTPHandler +from salt.ext.six.moves.urllib.request import Request as _Request +from salt.ext.six.moves.urllib.request import build_opener as _build_opener + +# pylint: enable=no-name-in-module,import-error + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'couchdb' +__virtualname__ = "couchdb" def __virtual__(): @@ -83,33 +85,30 @@ def __virtual__(): def _get_options(ret=None): - ''' + """ Get the couchdb options from salt. - ''' - attrs = {'url': 'url', - 'db': 'db'} + """ + attrs = {"url": "url", "db": "db"} - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) - if 'url' not in _options: + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) + if "url" not in _options: log.debug("Using default url.") - _options['url'] = "http://salt:5984/" + _options["url"] = "http://salt:5984/" - if 'db' not in _options: + if "db" not in _options: log.debug("Using default database.") - _options['db'] = "salt" + _options["db"] = "salt" return _options def _generate_doc(ret): - ''' + """ Create a object that will be saved into the database based on options. - ''' + """ # Create a copy of the object that we will return. retc = ret.copy() @@ -124,104 +123,105 @@ def _generate_doc(ret): def _request(method, url, content_type=None, _data=None): - ''' + """ Makes a HTTP request. Returns the JSON parse, or an obj with an error. - ''' + """ opener = _build_opener(_HTTPHandler) request = _Request(url, data=_data) if content_type: - request.add_header('Content-Type', content_type) + request.add_header("Content-Type", content_type) request.get_method = lambda: method try: handler = opener.open(request) except HTTPError as exc: - return {'error': '{0}'.format(exc)} + return {"error": "{0}".format(exc)} return salt.utils.json.loads(handler.read()) def returner(ret): - ''' + """ Take in the return and shove it into the couchdb database. - ''' + """ options = _get_options(ret) # Check to see if the database exists. - _response = _request("GET", options['url'] + "_all_dbs") - if options['db'] not in _response: + _response = _request("GET", options["url"] + "_all_dbs") + if options["db"] not in _response: # Make a PUT request to create the database. - _response = _request("PUT", options['url'] + options['db']) + _response = _request("PUT", options["url"] + options["db"]) # Confirm that the response back was simple 'ok': true. - if 'ok' not in _response or _response['ok'] is not True: - log.error('Unable to create database \'%s\'', options['db']) - log.error('Nothing logged! Lost data.') + if "ok" not in _response or _response["ok"] is not True: + log.error("Unable to create database '%s'", options["db"]) + log.error("Nothing logged! Lost data.") return - log.info('Created database \'%s\'', options['db']) + log.info("Created database '%s'", options["db"]) # Call _generate_doc to get a dict object of the document we're going to # shove into the database. doc = _generate_doc(ret) # Make the actual HTTP PUT request to create the doc. - _response = _request("PUT", - options['url'] + options['db'] + "/" + doc['_id'], - 'application/json', - salt.utils.json.dumps(doc)) + _response = _request( + "PUT", + options["url"] + options["db"] + "/" + doc["_id"], + "application/json", + salt.utils.json.dumps(doc), + ) # Sanity check regarding the response.. - if 'ok' not in _response or _response['ok'] is not True: - log.error('Unable to create document: \'%s\'', _response) - log.error('Nothing logged! Lost data.') + if "ok" not in _response or _response["ok"] is not True: + log.error("Unable to create document: '%s'", _response) + log.error("Nothing logged! Lost data.") def get_jid(jid): - ''' + """ Get the document with a given JID. - ''' + """ options = _get_options(ret=None) - _response = _request("GET", options['url'] + options['db'] + '/' + jid) - if 'error' in _response: - log.error('Unable to get JID \'%s\' : \'%s\'', jid, _response) + _response = _request("GET", options["url"] + options["db"] + "/" + jid) + if "error" in _response: + log.error("Unable to get JID '%s' : '%s'", jid, _response) return {} - return {_response['id']: _response} + return {_response["id"]: _response} def get_jids(): - ''' + """ List all the jobs that we have.. - ''' + """ options = _get_options(ret=None) - _response = _request("GET", options['url'] + options['db'] + "/_all_docs?include_docs=true") + _response = _request( + "GET", options["url"] + options["db"] + "/_all_docs?include_docs=true" + ) # Make sure the 'total_rows' is returned.. if not error out. - if 'total_rows' not in _response: - log.error( - 'Didn\'t get valid response from requesting all docs: %s', - _response - ) + if "total_rows" not in _response: + log.error("Didn't get valid response from requesting all docs: %s", _response) return {} # Return the rows. ret = {} - for row in _response['rows']: + for row in _response["rows"]: # Because this shows all the documents in the database, including the # design documents, verify the id is salt jid - jid = row['id'] + jid = row["id"] if not salt.utils.jid.is_jid(jid): continue - ret[jid] = salt.utils.jid.format_jid_instance(jid, row['doc']) + ret[jid] = salt.utils.jid.format_jid_instance(jid, row["doc"]) return ret def get_fun(fun): - ''' + """ Return a dict with key being minion and value being the job details of the last run of function 'fun'. - ''' + """ # Get the options.. options = _get_options(ret=None) @@ -235,36 +235,40 @@ def get_fun(fun): # Make a query of the by-minion-and-timestamp view and limit the count # to 1. - _response = _request("GET", - options['url'] + - options['db'] + - ('/_design/salt/_view/by-minion-fun-times' - 'tamp?descending=true&endkey=["{0}","{1}' - '",0]&startkey=["{0}","{1}",9999999999]&' - 'limit=1').format(minion, fun)) + _response = _request( + "GET", + options["url"] + + options["db"] + + ( + "/_design/salt/_view/by-minion-fun-times" + 'tamp?descending=true&endkey=["{0}","{1}' + '",0]&startkey=["{0}","{1}",9999999999]&' + "limit=1" + ).format(minion, fun), + ) # Skip the minion if we got an error.. - if 'error' in _response: + if "error" in _response: log.warning( - 'Got an error when querying for last command by a minion: %s', - _response['error'] + "Got an error when querying for last command by a minion: %s", + _response["error"], ) continue # Skip the minion if we didn't get any rows back. ( IE function that # they're looking for has a typo in it or some such ). - if len(_response['rows']) < 1: + if len(_response["rows"]) < 1: continue # Set the respnse .. - _ret[minion] = _response['rows'][0]['value'] + _ret[minion] = _response["rows"][0]["value"] return _ret def get_minions(): - ''' + """ Return a list of minion identifiers from a request of the view. - ''' + """ options = _get_options(ret=None) # Make sure the views are valid, which includes the minions.. @@ -272,38 +276,36 @@ def get_minions(): return [] # Make the request for the view.. - _response = _request("GET", - options['url'] + - options['db'] + - "/_design/salt/_view/minions?group=true") + _response = _request( + "GET", options["url"] + options["db"] + "/_design/salt/_view/minions?group=true" + ) # Verify that we got a response back. - if 'rows' not in _response: - log.error('Unable to get available minions: %s', _response) + if "rows" not in _response: + log.error("Unable to get available minions: %s", _response) return [] # Iterate over the rows to build up a list return it. _ret = [] - for row in _response['rows']: - _ret.append(row['key']) + for row in _response["rows"]: + _ret.append(row["key"]) return _ret def ensure_views(): - ''' + """ This function makes sure that all the views that should exist in the design document do exist. - ''' + """ # Get the options so we have the URL and DB.. options = _get_options(ret=None) # Make a request to check if the design document exists. - _response = _request("GET", - options['url'] + options['db'] + "/_design/salt") + _response = _request("GET", options["url"] + options["db"] + "/_design/salt") # If the document doesn't exist, or for some reason there are not views. - if 'error' in _response: + if "error" in _response: return set_salt_view() # Determine if any views are missing from the design doc stored on the @@ -311,7 +313,7 @@ def ensure_views(): # set_salt_view will set all the views, so we don't need to continue t # check. for view in get_valid_salt_views(): - if view not in _response['views']: + if view not in _response["views"]: return set_salt_view() # Valid views, return true. @@ -319,57 +321,57 @@ def ensure_views(): def get_valid_salt_views(): - ''' + """ Returns a dict object of views that should be part of the salt design document. - ''' + """ ret = {} - ret['minions'] = {} - ret['minions']['map'] = "function( doc ){ emit( doc.id, null ); }" - ret['minions']['reduce'] = \ - "function( keys,values,rereduce ){ return key[0]; }" + ret["minions"] = {} + ret["minions"]["map"] = "function( doc ){ emit( doc.id, null ); }" + ret["minions"]["reduce"] = "function( keys,values,rereduce ){ return key[0]; }" - ret['by-minion-fun-timestamp'] = {} - ret['by-minion-fun-timestamp']['map'] = \ - "function( doc ){ emit( [doc.id,doc.fun,doc.timestamp], doc ); }" + ret["by-minion-fun-timestamp"] = {} + ret["by-minion-fun-timestamp"][ + "map" + ] = "function( doc ){ emit( [doc.id,doc.fun,doc.timestamp], doc ); }" return ret def set_salt_view(): - ''' + """ Helper function that sets the salt design document. Uses get_valid_salt_views and some hardcoded values. - ''' + """ options = _get_options(ret=None) # Create the new object that we will shove in as the design doc. new_doc = {} - new_doc['views'] = get_valid_salt_views() - new_doc['language'] = "javascript" + new_doc["views"] = get_valid_salt_views() + new_doc["language"] = "javascript" # Make the request to update the design doc. - _response = _request("PUT", - options['url'] + options['db'] + "/_design/salt", - "application/json", salt.utils.json.dumps(new_doc)) - if 'error' in _response: - log.warning( - 'Unable to set the salt design document: %s', - _response['error'] - ) + _response = _request( + "PUT", + options["url"] + options["db"] + "/_design/salt", + "application/json", + salt.utils.json.dumps(new_doc), + ) + if "error" in _response: + log.warning("Unable to set the salt design document: %s", _response["error"]) return False return True def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ diff --git a/salt/returners/django_return.py b/salt/returners/django_return.py index ae4e623a708..374c6f7fcf8 100644 --- a/salt/returners/django_return.py +++ b/salt/returners/django_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A returner that will inform a Django system that returns are available using Django's signal system. @@ -25,9 +25,10 @@ An example Django module that registers a function called def returner_callback(sender, ret): print('I received {0} from {1}'.format(ret, sender)) -''' +""" # Import Python libraries from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libraries @@ -40,50 +41,55 @@ HAS_DJANGO = False try: from django import dispatch # pylint: disable=E0611 + HAS_DJANGO = True except ImportError: HAS_DJANGO = False # Define this module's virtual name -__virtualname__ = 'django' +__virtualname__ = "django" def __virtual__(): if not HAS_DJANGO: - return False, 'Could not import django returner; django is not installed.' + return False, "Could not import django returner; django is not installed." return True def returner(ret): - ''' + """ Signal a Django server that a return is available - ''' - signaled = dispatch.Signal(providing_args=['ret']).send(sender='returner', ret=ret) + """ + signaled = dispatch.Signal(providing_args=["ret"]).send(sender="returner", ret=ret) for signal in signaled: log.debug( - 'Django returner function \'returner\' signaled %s ' - 'which responded with %s', signal[0], signal[1] + "Django returner function 'returner' signaled %s " + "which responded with %s", + signal[0], + signal[1], ) def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid - ''' - signaled = dispatch.Signal( - providing_args=['jid', 'load']).send( - sender='save_load', jid=jid, load=load) + """ + signaled = dispatch.Signal(providing_args=["jid", "load"]).send( + sender="save_load", jid=jid, load=load + ) for signal in signaled: log.debug( - 'Django returner function \'save_load\' signaled %s ' - 'which responded with %s', signal[0], signal[1] + "Django returner function 'save_load' signaled %s " + "which responded with %s", + signal[0], + signal[1], ) def prep_jid(nocache=False, passed_jid=None): - ''' + """ Do any work necessary to prepare a JID, including sending a custom ID - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/elasticsearch_return.py b/salt/returners/elasticsearch_return.py index 76ea93cfe3b..585b52ac51e 100644 --- a/salt/returners/elasticsearch_return.py +++ b/salt/returners/elasticsearch_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to an elasticsearch server for indexing. :maintainer: Jurnell Cockhren <jurnell.cockhren@sophicware.com>, Arnold Bechtoldt <mail@arnoldbechtoldt.com> @@ -92,14 +92,15 @@ Minion configuration: functions_blacklist: - test.ping - saltutil.find_job -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import datetime -from datetime import tzinfo, timedelta -import uuid import logging +import uuid +from datetime import timedelta, tzinfo # Import Salt libs import salt.returners @@ -109,14 +110,14 @@ import salt.utils.json # Import 3rd-party libs from salt.ext import six -__virtualname__ = 'elasticsearch' +__virtualname__ = "elasticsearch" log = logging.getLogger(__name__) STATE_FUNCTIONS = { - 'state.apply': 'state_apply', - 'state.highstate': 'state_apply', - 'state.sls': 'state_apply', + "state.apply": "state_apply", + "state.highstate": "state_apply", + "state.sls": "state_apply", } @@ -125,40 +126,40 @@ def __virtual__(): def _get_options(ret=None): - ''' + """ Get the returner options from salt. - ''' + """ defaults = { - 'debug_returner_payload': False, - 'doc_type': 'default', - 'functions_blacklist': [], - 'index_date': False, - 'master_event_index': 'salt-master-event-cache', - 'master_event_doc_type': 'default', - 'master_job_cache_index': 'salt-master-job-cache', - 'master_job_cache_doc_type': 'default', - 'number_of_shards': 1, - 'number_of_replicas': 0, - 'states_order_output': False, - 'states_count': False, - 'states_single_index': False, + "debug_returner_payload": False, + "doc_type": "default", + "functions_blacklist": [], + "index_date": False, + "master_event_index": "salt-master-event-cache", + "master_event_doc_type": "default", + "master_job_cache_index": "salt-master-job-cache", + "master_job_cache_doc_type": "default", + "number_of_shards": 1, + "number_of_replicas": 0, + "states_order_output": False, + "states_count": False, + "states_single_index": False, } attrs = { - 'debug_returner_payload': 'debug_returner_payload', - 'doc_type': 'doc_type', - 'functions_blacklist': 'functions_blacklist', - 'index_date': 'index_date', - 'master_event_index': 'master_event_index', - 'master_event_doc_type': 'master_event_doc_type', - 'master_job_cache_index': 'master_job_cache_index', - 'master_job_cache_doc_type': 'master_job_cache_doc_type', - 'number_of_shards': 'number_of_shards', - 'number_of_replicas': 'number_of_replicas', - 'states_count': 'states_count', - 'states_order_output': 'states_order_output', - 'states_single_index': 'states_single_index', + "debug_returner_payload": "debug_returner_payload", + "doc_type": "doc_type", + "functions_blacklist": "functions_blacklist", + "index_date": "index_date", + "master_event_index": "master_event_index", + "master_event_doc_type": "master_event_doc_type", + "master_job_cache_index": "master_job_cache_index", + "master_job_cache_doc_type": "master_job_cache_doc_type", + "number_of_shards": "number_of_shards", + "number_of_replicas": "number_of_replicas", + "states_count": "states_count", + "states_order_output": "states_order_output", + "states_single_index": "states_single_index", } _options = salt.returners.get_returner_options( @@ -167,33 +168,33 @@ def _get_options(ret=None): attrs, __salt__=__salt__, __opts__=__opts__, - defaults=defaults) + defaults=defaults, + ) return _options def _ensure_index(index): - index_exists = __salt__['elasticsearch.index_exists'](index) + index_exists = __salt__["elasticsearch.index_exists"](index) if not index_exists: options = _get_options() index_definition = { - 'settings': { - 'number_of_shards': options['number_of_shards'], - 'number_of_replicas': options['number_of_replicas'], + "settings": { + "number_of_shards": options["number_of_shards"], + "number_of_replicas": options["number_of_replicas"], } } - __salt__['elasticsearch.index_create']('{0}-v1'.format(index), - index_definition) - __salt__['elasticsearch.alias_create']('{0}-v1'.format(index), index) + __salt__["elasticsearch.index_create"]("{0}-v1".format(index), index_definition) + __salt__["elasticsearch.alias_create"]("{0}-v1".format(index), index) def _convert_keys(data): if isinstance(data, dict): new_data = {} for k, sub_data in data.items(): - if '.' in k: - new_data['_orig_key'] = k - k = k.replace('.', '_') + if "." in k: + new_data["_orig_key"] = k + k = k.replace(".", "_") new_data[k] = _convert_keys(sub_data) elif isinstance(data, list): new_data = [] @@ -206,51 +207,53 @@ def _convert_keys(data): def returner(ret): - ''' + """ Process the return from Salt - ''' + """ - job_fun = ret['fun'] - job_fun_escaped = job_fun.replace('.', '_') - job_id = ret['jid'] - job_retcode = ret.get('retcode', 1) + job_fun = ret["fun"] + job_fun_escaped = job_fun.replace(".", "_") + job_id = ret["jid"] + job_retcode = ret.get("retcode", 1) job_success = True if not job_retcode else False options = _get_options(ret) - if job_fun in options['functions_blacklist']: + if job_fun in options["functions_blacklist"]: log.info( - 'Won\'t push new data to Elasticsearch, job with jid=%s and ' - 'function=%s which is in the user-defined list of ignored ' - 'functions', job_id, job_fun + "Won't push new data to Elasticsearch, job with jid=%s and " + "function=%s which is in the user-defined list of ignored " + "functions", + job_id, + job_fun, ) return - if ret.get('data', None) is None and ret.get('return') is None: + if ret.get("data", None) is None and ret.get("return") is None: log.info( - 'Won\'t push new data to Elasticsearch, job with jid=%s was ' - 'not successful', job_id + "Won't push new data to Elasticsearch, job with jid=%s was " + "not successful", + job_id, ) return # Build the index name - if options['states_single_index'] and job_fun in STATE_FUNCTIONS: - index = 'salt-{0}'.format(STATE_FUNCTIONS[job_fun]) + if options["states_single_index"] and job_fun in STATE_FUNCTIONS: + index = "salt-{0}".format(STATE_FUNCTIONS[job_fun]) else: - index = 'salt-{0}'.format(job_fun_escaped) + index = "salt-{0}".format(job_fun_escaped) - if options['index_date']: - index = '{0}-{1}'.format(index, - datetime.date.today().strftime('%Y.%m.%d')) + if options["index_date"]: + index = "{0}-{1}".format(index, datetime.date.today().strftime("%Y.%m.%d")) counts = {} # Do some special processing for state returns if job_fun in STATE_FUNCTIONS: # Init the state counts - if options['states_count']: + if options["states_count"]: counts = { - 'suceeded': 0, - 'failed': 0, + "suceeded": 0, + "failed": 0, } # Prepend each state execution key in ret['return'] with a zero-padded @@ -258,43 +261,41 @@ def returner(ret): # more easily. Change the index to be # index to be '<index>-ordered' so as not to clash with the unsorted # index data format - if options['states_order_output'] and isinstance(ret['return'], dict): - index = '{0}-ordered'.format(index) - max_chars = len(six.text_type(len(ret['return']))) + if options["states_order_output"] and isinstance(ret["return"], dict): + index = "{0}-ordered".format(index) + max_chars = len(six.text_type(len(ret["return"]))) - for uid, data in six.iteritems(ret['return']): + for uid, data in six.iteritems(ret["return"]): # Skip keys we've already prefixed - if uid.startswith(tuple('0123456789')): + if uid.startswith(tuple("0123456789")): continue # Store the function being called as it's a useful key to search - decoded_uid = uid.split('_|-') - ret['return'][uid]['_func'] = '{0}.{1}'.format( - decoded_uid[0], - decoded_uid[-1] + decoded_uid = uid.split("_|-") + ret["return"][uid]["_func"] = "{0}.{1}".format( + decoded_uid[0], decoded_uid[-1] ) # Prefix the key with the run order so it can be sorted - new_uid = '{0}_|-{1}'.format( - six.text_type(data['__run_num__']).zfill(max_chars), - uid, + new_uid = "{0}_|-{1}".format( + six.text_type(data["__run_num__"]).zfill(max_chars), uid, ) - ret['return'][new_uid] = ret['return'].pop(uid) + ret["return"][new_uid] = ret["return"].pop(uid) # Catch a state output that has failed and where the error message is # not in a dict as expected. This prevents elasticsearch from # complaining about a mapping error - elif not isinstance(ret['return'], dict): - ret['return'] = {'return': ret['return']} + elif not isinstance(ret["return"], dict): + ret["return"] = {"return": ret["return"]} # Need to count state successes and failures - if options['states_count']: - for state_data in ret['return'].values(): - if state_data['result'] is False: - counts['failed'] += 1 + if options["states_count"]: + for state_data in ret["return"].values(): + if state_data["result"] is False: + counts["failed"] += 1 else: - counts['suceeded'] += 1 + counts["suceeded"] += 1 # Ensure the index exists _ensure_index(index) @@ -305,106 +306,103 @@ def returner(ret): return timedelta(0) def tzname(self, dt): - return 'UTC' + return "UTC" def dst(self, dt): return timedelta(0) utc = UTC() data = { - '@timestamp': datetime.datetime.now(utc).isoformat(), - 'success': job_success, - 'retcode': job_retcode, - 'minion': ret['id'], - 'fun': job_fun, - 'jid': job_id, - 'counts': counts, - 'data': _convert_keys(ret['return']) + "@timestamp": datetime.datetime.now(utc).isoformat(), + "success": job_success, + "retcode": job_retcode, + "minion": ret["id"], + "fun": job_fun, + "jid": job_id, + "counts": counts, + "data": _convert_keys(ret["return"]), } - if options['debug_returner_payload']: - log.debug('elasicsearch payload: %s', data) + if options["debug_returner_payload"]: + log.debug("elasicsearch payload: %s", data) # Post the payload - ret = __salt__['elasticsearch.document_create'](index=index, - doc_type=options['doc_type'], - body=salt.utils.json.dumps(data)) + ret = __salt__["elasticsearch.document_create"]( + index=index, doc_type=options["doc_type"], body=salt.utils.json.dumps(data) + ) def event_return(events): - ''' + """ Return events to Elasticsearch Requires that the `event_return` configuration be set in master config. - ''' + """ options = _get_options() - index = options['master_event_index'] - doc_type = options['master_event_doc_type'] + index = options["master_event_index"] + doc_type = options["master_event_doc_type"] - if options['index_date']: - index = '{0}-{1}'.format(index, - datetime.date.today().strftime('%Y.%m.%d')) + if options["index_date"]: + index = "{0}-{1}".format(index, datetime.date.today().strftime("%Y.%m.%d")) _ensure_index(index) for event in events: - data = { - 'tag': event.get('tag', ''), - 'data': event.get('data', '') - } + data = {"tag": event.get("tag", ""), "data": event.get("data", "")} - ret = __salt__['elasticsearch.document_create'](index=index, - doc_type=doc_type, - id=uuid.uuid4(), - body=salt.utils.json.dumps(data)) + ret = __salt__["elasticsearch.document_create"]( + index=index, + doc_type=doc_type, + id=uuid.uuid4(), + body=salt.utils.json.dumps(data), + ) def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid id .. versionadded:: 2015.8.1 - ''' + """ options = _get_options() - index = options['master_job_cache_index'] - doc_type = options['master_job_cache_doc_type'] + index = options["master_job_cache_index"] + doc_type = options["master_job_cache_doc_type"] _ensure_index(index) data = { - 'jid': jid, - 'load': load, + "jid": jid, + "load": load, } - ret = __salt__['elasticsearch.document_create'](index=index, - doc_type=doc_type, - id=jid, - body=salt.utils.json.dumps(data)) + ret = __salt__["elasticsearch.document_create"]( + index=index, doc_type=doc_type, id=jid, body=salt.utils.json.dumps(data) + ) def get_load(jid): - ''' + """ Return the load data that marks a specified jid .. versionadded:: 2015.8.1 - ''' + """ options = _get_options() - index = options['master_job_cache_index'] - doc_type = options['master_job_cache_doc_type'] + index = options["master_job_cache_index"] + doc_type = options["master_job_cache_doc_type"] - data = __salt__['elasticsearch.document_get'](index=index, - id=jid, - doc_type=doc_type) + data = __salt__["elasticsearch.document_get"]( + index=index, id=jid, doc_type=doc_type + ) if data: return salt.utils.json.loads(data) return {} diff --git a/salt/returners/etcd_return.py b/salt/returners/etcd_return.py index 54139b1ffa6..9afcd335dcd 100644 --- a/salt/returners/etcd_return.py +++ b/salt/returners/etcd_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to an etcd server or cluster :depends: - python-etcd @@ -63,7 +63,7 @@ create the profiles as specified above. Then add: etcd.returner_read_profile: my_etcd_read etcd.returner_write_profile: my_etcd_write -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -72,8 +72,10 @@ import logging # Import salt libs import salt.utils.jid import salt.utils.json + try: import salt.utils.etcd_util + HAS_LIBS = True except ImportError: HAS_LIBS = False @@ -81,163 +83,159 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'etcd' +__virtualname__ = "etcd" def __virtual__(): - ''' + """ Only return if python-etcd is installed - ''' + """ if HAS_LIBS: return __virtualname__ - return False, 'Could not import etcd returner; python-etcd is not installed.' + return False, "Could not import etcd returner; python-etcd is not installed." def _get_conn(opts, profile=None): - ''' + """ Establish a connection to etcd - ''' + """ if profile is None: - profile = opts.get('etcd.returner') - path = opts.get('etcd.returner_root', '/salt/return') + profile = opts.get("etcd.returner") + path = opts.get("etcd.returner_root", "/salt/return") return salt.utils.etcd_util.get_conn(opts, profile), path def returner(ret): - ''' + """ Return data to an etcd server or cluster - ''' - write_profile = __opts__.get('etcd.returner_write_profile') + """ + write_profile = __opts__.get("etcd.returner_write_profile") if write_profile: - ttl = __opts__.get(write_profile, {}).get('etcd.ttl') + ttl = __opts__.get(write_profile, {}).get("etcd.ttl") else: - ttl = __opts__.get('etcd.ttl') + ttl = __opts__.get("etcd.ttl") client, path = _get_conn(__opts__, write_profile) # Make a note of this minion for the external job cache client.set( - '/'.join((path, 'minions', ret['id'])), - ret['jid'], - ttl=ttl, + "/".join((path, "minions", ret["id"])), ret["jid"], ttl=ttl, ) for field in ret: # Not using os.path.join because we're not dealing with file paths - dest = '/'.join(( - path, - 'jobs', - ret['jid'], - ret['id'], - field - )) + dest = "/".join((path, "jobs", ret["jid"], ret["id"], field)) client.set(dest, salt.utils.json.dumps(ret[field]), ttl=ttl) def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid - ''' - log.debug('sdstack_etcd returner <save_load> called jid: {0}'.format(jid)) - write_profile = __opts__.get('etcd.returner_write_profile') + """ + log.debug("sdstack_etcd returner <save_load> called jid: {0}".format(jid)) + write_profile = __opts__.get("etcd.returner_write_profile") client, path = _get_conn(__opts__, write_profile) if write_profile: - ttl = __opts__.get(write_profile, {}).get('etcd.ttl') + ttl = __opts__.get(write_profile, {}).get("etcd.ttl") else: - ttl = __opts__.get('etcd.ttl') + ttl = __opts__.get("etcd.ttl") client.set( - '/'.join((path, 'jobs', jid, '.load.p')), - salt.utils.json.dumps(load), - ttl=ttl, + "/".join((path, "jobs", jid, ".load.p")), salt.utils.json.dumps(load), ttl=ttl, ) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def clean_old_jobs(): - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' - log.debug('sdstack_etcd returner <get_load> called jid: {0}'.format(jid)) - read_profile = __opts__.get('etcd.returner_read_profile') + """ + log.debug("sdstack_etcd returner <get_load> called jid: {0}".format(jid)) + read_profile = __opts__.get("etcd.returner_read_profile") client, path = _get_conn(__opts__, read_profile) - return salt.utils.json.loads(client.get('/'.join((path, 'jobs', jid, '.load.p'))).value) + return salt.utils.json.loads( + client.get("/".join((path, "jobs", jid, ".load.p"))).value + ) def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' - log.debug('sdstack_etcd returner <get_jid> called jid: {0}'.format(jid)) + """ + log.debug("sdstack_etcd returner <get_jid> called jid: {0}".format(jid)) ret = {} client, path = _get_conn(__opts__) - items = client.get('/'.join((path, 'jobs', jid))) + items = client.get("/".join((path, "jobs", jid))) for item in items.children: - if str(item.key).endswith('.load.p'): + if str(item.key).endswith(".load.p"): continue - comps = str(item.key).split('/') - data = client.get('/'.join((path, 'jobs', jid, comps[-1], 'return'))).value - ret[comps[-1]] = {'return': salt.utils.json.loads(data)} + comps = str(item.key).split("/") + data = client.get("/".join((path, "jobs", jid, comps[-1], "return"))).value + ret[comps[-1]] = {"return": salt.utils.json.loads(data)} return ret def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' - log.debug('sdstack_etcd returner <get_fun> called fun: {0}'.format(fun)) + """ + log.debug("sdstack_etcd returner <get_fun> called fun: {0}".format(fun)) ret = {} client, path = _get_conn(__opts__) - items = client.get('/'.join((path, 'minions'))) + items = client.get("/".join((path, "minions"))) for item in items.children: - comps = str(item.key).split('/') - efun = salt.utils.json.loads(client.get('/'.join((path, 'jobs', str(item.value), comps[-1], 'fun'))).value) + comps = str(item.key).split("/") + efun = salt.utils.json.loads( + client.get( + "/".join((path, "jobs", str(item.value), comps[-1], "fun")) + ).value + ) if efun == fun: ret[comps[-1]] = str(efun) return ret def get_jids(): - ''' + """ Return a list of all job ids - ''' - log.debug('sdstack_etcd returner <get_jids> called') + """ + log.debug("sdstack_etcd returner <get_jids> called") ret = [] client, path = _get_conn(__opts__) - items = client.get('/'.join((path, 'jobs'))) + items = client.get("/".join((path, "jobs"))) for item in items.children: if item.dir is True: - jid = str(item.key).split('/')[-1] + jid = str(item.key).split("/")[-1] ret.append(jid) return ret def get_minions(): - ''' + """ Return a list of minions - ''' - log.debug('sdstack_etcd returner <get_minions> called') + """ + log.debug("sdstack_etcd returner <get_minions> called") ret = [] client, path = _get_conn(__opts__) - items = client.get('/'.join((path, 'minions'))) + items = client.get("/".join((path, "minions"))) for item in items.children: - comps = str(item.key).split('/') + comps = str(item.key).split("/") ret.append(comps[-1]) return ret def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/highstate_return.py b/salt/returners/highstate_return.py index c13f51655c7..aca796e3618 100644 --- a/salt/returners/highstate_return.py +++ b/salt/returners/highstate_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return the results of a highstate (or any other state function that returns data in a compatible format) via an HTML email or HTML file. @@ -76,62 +76,57 @@ values. Any other grain name could be used. As opposed to using values at the time of pillar generation, these will contain minion values at the time of execution. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import cgi import logging import smtplib -import cgi from email.mime.text import MIMEText -from salt.ext.six.moves import range -from salt.ext.six.moves import StringIO -from salt.ext import six - +import salt.returners import salt.utils.files import salt.utils.json import salt.utils.stringutils import salt.utils.yaml -import salt.returners +from salt.ext import six +from salt.ext.six.moves import StringIO, range log = logging.getLogger(__name__) -__virtualname__ = 'highstate' +__virtualname__ = "highstate" def __virtual__(): - ''' + """ Return our name - ''' + """ return __virtualname__ def _get_options(ret): - ''' + """ Return options - ''' + """ attrs = { - 'report_everything': 'report_everything', - 'report_changes': 'report_changes', - 'report_failures': 'report_failures', - 'failure_function': 'failure_function', - 'success_function': 'success_function', - 'report_format': 'report_format', - 'report_delivery': 'report_delivery', - 'file_output': 'file_output', - 'smtp_sender': 'smtp_sender', - 'smtp_recipients': 'smtp_recipients', - 'smtp_failure_subject': 'smtp_failure_subject', - 'smtp_success_subject': 'smtp_success_subject', - 'smtp_server': 'smtp_server' + "report_everything": "report_everything", + "report_changes": "report_changes", + "report_failures": "report_failures", + "failure_function": "failure_function", + "success_function": "success_function", + "report_format": "report_format", + "report_delivery": "report_delivery", + "file_output": "file_output", + "smtp_sender": "smtp_sender", + "smtp_recipients": "smtp_recipients", + "smtp_failure_subject": "smtp_failure_subject", + "smtp_success_subject": "smtp_success_subject", + "smtp_server": "smtp_server", } _options = salt.returners.get_returner_options( - __virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options @@ -142,140 +137,144 @@ def _get_options(ret): # sufficient for our needs. # _STYLES = { - '_table': 'border-collapse:collapse;width:100%;', - '_td': 'vertical-align:top;' - 'font-family:Helvetica,Arial,sans-serif;font-size:9pt;', - 'unchanged': 'color:blue;', - 'changed': 'color:green', - 'failed': 'color:red;', - 'first': 'border-top:0;border-left:1px solid #9e9e9e;', - 'first_first': 'border-top:0;border-left:0;', - 'notfirst_first': 'border-left:0;border-top:1px solid #9e9e9e;', - 'other': 'border-top:1px solid #9e9e9e;border-left:1px solid #9e9e9e;', - 'name': 'width:70pt;', - 'container': 'padding:0;' + "_table": "border-collapse:collapse;width:100%;", + "_td": "vertical-align:top;" + "font-family:Helvetica,Arial,sans-serif;font-size:9pt;", + "unchanged": "color:blue;", + "changed": "color:green", + "failed": "color:red;", + "first": "border-top:0;border-left:1px solid #9e9e9e;", + "first_first": "border-top:0;border-left:0;", + "notfirst_first": "border-left:0;border-top:1px solid #9e9e9e;", + "other": "border-top:1px solid #9e9e9e;border-left:1px solid #9e9e9e;", + "name": "width:70pt;", + "container": "padding:0;", } def _lookup_style(element, names): - ''' + """ Lookup style by either element name or the list of classes - ''' - return _STYLES.get('_'+element, '') + \ - ''.join([_STYLES.get(name, '') for name in names]) + """ + return _STYLES.get("_" + element, "") + "".join( + [_STYLES.get(name, "") for name in names] + ) -def _generate_html_table(data, out, level=0, extra_style=''): - ''' +def _generate_html_table(data, out, level=0, extra_style=""): + """ Generate a single table of data - ''' - print('<table style="{0}">'.format( - _lookup_style('table', ['table' + six.text_type(level)])), file=out) + """ + print( + '<table style="{0}">'.format( + _lookup_style("table", ["table" + six.text_type(level)]) + ), + file=out, + ) firstone = True - row_style = 'row' + six.text_type(level) - cell_style = 'cell' + six.text_type(level) + row_style = "row" + six.text_type(level) + cell_style = "cell" + six.text_type(level) for subdata in data: - first_style = 'first_first' if firstone else 'notfirst_first' - second_style = 'first' if firstone else 'other' + first_style = "first_first" if firstone else "notfirst_first" + second_style = "first" if firstone else "other" if isinstance(subdata, dict): - if '__style__' in subdata: - new_extra_style = subdata['__style__'] - del subdata['__style__'] + if "__style__" in subdata: + new_extra_style = subdata["__style__"] + del subdata["__style__"] else: new_extra_style = extra_style if len(subdata) == 1: name, value = next(six.iteritems(subdata)) - print('<tr style="{0}">'.format( - _lookup_style('tr', [row_style]) - ), file=out) - print('<td style="{0}">{1}</td>'.format( - _lookup_style( - 'td', - [cell_style, first_style, 'name', new_extra_style] - ), - name - ), file=out) - if isinstance(value, list): - print('<td style="{0}">'.format( - _lookup_style( - 'td', - [ - cell_style, - second_style, - 'container', - new_extra_style - ] - ) - ), file=out) - _generate_html_table( - value, - out, - level + 1, - new_extra_style - ) - print('</td>', file=out) - else: - print('<td style="{0}">{1}</td>'.format( - _lookup_style( - 'td', - [ - cell_style, - second_style, - 'value', - new_extra_style - ] - ), - cgi.escape(six.text_type(value)) - ), file=out) - print('</tr>', file=out) - elif isinstance(subdata, list): - print('<tr style="{0}">'.format( - _lookup_style('tr', [row_style]) - ), file=out) - print('<td style="{0}">'.format( - _lookup_style( - 'td', - [cell_style, first_style, 'container', extra_style] + print( + '<tr style="{0}">'.format(_lookup_style("tr", [row_style])), + file=out, ) - ), file=out) - _generate_html_table(subdata, out, level + 1, extra_style) - print('</td>', file=out) - print('</tr>', file=out) - else: - print('<tr style="{0}">'.format( - _lookup_style('tr', [row_style]) - ), file=out) - print('<td style="{0}">{1}</td>'.format( - _lookup_style( - 'td', - [cell_style, first_style, 'value', extra_style] + print( + '<td style="{0}">{1}</td>'.format( + _lookup_style( + "td", [cell_style, first_style, "name", new_extra_style] + ), + name, + ), + file=out, + ) + if isinstance(value, list): + print( + '<td style="{0}">'.format( + _lookup_style( + "td", + [ + cell_style, + second_style, + "container", + new_extra_style, + ], + ) + ), + file=out, + ) + _generate_html_table(value, out, level + 1, new_extra_style) + print("</td>", file=out) + else: + print( + '<td style="{0}">{1}</td>'.format( + _lookup_style( + "td", + [cell_style, second_style, "value", new_extra_style], + ), + cgi.escape(six.text_type(value)), + ), + file=out, + ) + print("</tr>", file=out) + elif isinstance(subdata, list): + print('<tr style="{0}">'.format(_lookup_style("tr", [row_style])), file=out) + print( + '<td style="{0}">'.format( + _lookup_style( + "td", [cell_style, first_style, "container", extra_style] + ) ), - cgi.escape(six.text_type(subdata)) - ), file=out) - print('</tr>', file=out) + file=out, + ) + _generate_html_table(subdata, out, level + 1, extra_style) + print("</td>", file=out) + print("</tr>", file=out) + else: + print('<tr style="{0}">'.format(_lookup_style("tr", [row_style])), file=out) + print( + '<td style="{0}">{1}</td>'.format( + _lookup_style( + "td", [cell_style, first_style, "value", extra_style] + ), + cgi.escape(six.text_type(subdata)), + ), + file=out, + ) + print("</tr>", file=out) firstone = False - print('</table>', file=out) + print("</table>", file=out) def _generate_html(data, out): - ''' + """ Generate report data as HTML - ''' - print('<html>', file=out) - print('<body>', file=out) + """ + print("<html>", file=out) + print("<body>", file=out) _generate_html_table(data, out, 0) - print('</body>', file=out) - print('</html>', file=out) + print("</body>", file=out) + print("</html>", file=out) def _dict_to_name_value(data): - ''' + """ Convert a dictionary to a list of dictionaries to facilitate ordering - ''' + """ if isinstance(data, dict): sorted_data = sorted(data.items(), key=lambda s: s[0]) result = [] @@ -290,51 +289,48 @@ def _dict_to_name_value(data): def _generate_states_report(sorted_data): - ''' + """ Generate states report - ''' + """ states = [] for state, data in sorted_data: - module, stateid, name, function = state.split('_|-') - module_function = '.'.join((module, function)) - result = data.get('result', '') + module, stateid, name, function = state.split("_|-") + module_function = ".".join((module, function)) + result = data.get("result", "") single = [ - {'function': module_function}, - {'name': name}, - {'result': result}, - {'duration': data.get('duration', 0.0)}, - {'comment': data.get('comment', '')} + {"function": module_function}, + {"name": name}, + {"result": result}, + {"duration": data.get("duration", 0.0)}, + {"comment": data.get("comment", "")}, ] if not result: - style = 'failed' + style = "failed" else: - changes = data.get('changes', {}) + changes = data.get("changes", {}) if changes and isinstance(changes, dict): - single.append({'changes': _dict_to_name_value(changes)}) - style = 'changed' + single.append({"changes": _dict_to_name_value(changes)}) + style = "changed" else: - style = 'unchanged' + style = "unchanged" - started = data.get('start_time', '') + started = data.get("start_time", "") if started: - single.append({'started': started}) + single.append({"started": started}) - states.append({stateid: single, '__style__': style}) + states.append({stateid: single, "__style__": style}) return states def _generate_report(ret, setup): - ''' + """ Generate report dictionary - ''' + """ - retdata = ret.get('return', {}) + retdata = ret.get("return", {}) - sorted_data = sorted( - retdata.items(), - key=lambda s: s[1].get('__run_num__', 0) - ) + sorted_data = sorted(retdata.items(), key=lambda s: s[1].get("__run_num__", 0)) total = 0 failed = 0 @@ -343,59 +339,62 @@ def _generate_report(ret, setup): # gather stats for _, data in sorted_data: - if not data.get('result', True): + if not data.get("result", True): failed += 1 total += 1 try: - duration += float(data.get('duration', 0.0)) + duration += float(data.get("duration", 0.0)) except ValueError: pass - if data.get('changes', {}): + if data.get("changes", {}): changed += 1 unchanged = total - failed - changed - log.debug('highstate total: %s', total) - log.debug('highstate failed: %s', failed) - log.debug('highstate unchanged: %s', unchanged) - log.debug('highstate changed: %s', changed) + log.debug("highstate total: %s", total) + log.debug("highstate failed: %s", failed) + log.debug("highstate unchanged: %s", unchanged) + log.debug("highstate changed: %s", changed) # generate report if required - if setup.get('report_everything', False) or \ - (setup.get('report_changes', True) and changed != 0) or \ - (setup.get('report_failures', True) and failed != 0): + if ( + setup.get("report_everything", False) + or (setup.get("report_changes", True) and changed != 0) + or (setup.get("report_failures", True) and failed != 0) + ): report = [ - {'stats': [ - {'total': total}, - {'failed': failed, '__style__': 'failed'}, - {'unchanged': unchanged, '__style__': 'unchanged'}, - {'changed': changed, '__style__': 'changed'}, - {'duration': duration} - ]}, - {'job': [ - {'function': ret.get('fun', '')}, - {'arguments': ret.get('fun_args', '')}, - {'jid': ret.get('jid', '')}, - {'success': ret.get('success', True)}, - {'retcode': ret.get('retcode', 0)} - ]}, - {'states': _generate_states_report(sorted_data)} + { + "stats": [ + {"total": total}, + {"failed": failed, "__style__": "failed"}, + {"unchanged": unchanged, "__style__": "unchanged"}, + {"changed": changed, "__style__": "changed"}, + {"duration": duration}, + ] + }, + { + "job": [ + {"function": ret.get("fun", "")}, + {"arguments": ret.get("fun_args", "")}, + {"jid": ret.get("jid", "")}, + {"success": ret.get("success", True)}, + {"retcode": ret.get("retcode", 0)}, + ] + }, + {"states": _generate_states_report(sorted_data)}, ] if failed: - function = setup.get('failure_function', None) + function = setup.get("failure_function", None) else: - function = setup.get('success_function', None) + function = setup.get("success_function", None) if function: func_result = __salt__[function]() - report.insert( - 0, - {'extra': [{function: _dict_to_name_value(func_result)}]} - ) + report.insert(0, {"extra": [{function: _dict_to_name_value(func_result)}]}) else: report = [] @@ -404,27 +403,27 @@ def _generate_report(ret, setup): def _sprinkle(config_str): - ''' + """ Sprinkle with grains of salt, that is convert 'test {id} test {host} ' types of strings - ''' - parts = [x for sub in config_str.split('{') for x in sub.split('}')] + """ + parts = [x for sub in config_str.split("{") for x in sub.split("}")] for i in range(1, len(parts), 2): - parts[i] = six.text_type(__grains__.get(parts[i], '')) - return ''.join(parts) + parts[i] = six.text_type(__grains__.get(parts[i], "")) + return "".join(parts) def _produce_output(report, failed, setup): - ''' + """ Produce output from the report dictionary generated by _generate_report - ''' - report_format = setup.get('report_format', 'yaml') + """ + report_format = setup.get("report_format", "yaml") - log.debug('highstate output format: %s', report_format) + log.debug("highstate output format: %s", report_format) - if report_format == 'json': + if report_format == "json": report_text = salt.utils.json.dumps(report) - elif report_format == 'yaml': + elif report_format == "yaml": string_file = StringIO() salt.utils.yaml.safe_dump(report, string_file, default_flow_style=False) string_file.seek(0) @@ -435,46 +434,46 @@ def _produce_output(report, failed, setup): string_file.seek(0) report_text = string_file.read() - report_delivery = setup.get('report_delivery', 'file') + report_delivery = setup.get("report_delivery", "file") - log.debug('highstate report_delivery: %s', report_delivery) + log.debug("highstate report_delivery: %s", report_delivery) - if report_delivery == 'file': - output_file = _sprinkle(setup.get('file_output', '/tmp/test.rpt')) - with salt.utils.files.fopen(output_file, 'w') as out: + if report_delivery == "file": + output_file = _sprinkle(setup.get("file_output", "/tmp/test.rpt")) + with salt.utils.files.fopen(output_file, "w") as out: out.write(salt.utils.stringutils.to_str(report_text)) else: msg = MIMEText(report_text, report_format) - sender = setup.get('smtp_sender', '') - recipients = setup.get('smtp_recipients', '') + sender = setup.get("smtp_sender", "") + recipients = setup.get("smtp_recipients", "") if failed: - subject = setup.get('smtp_failure_subject', 'Installation failure') + subject = setup.get("smtp_failure_subject", "Installation failure") else: - subject = setup.get('smtp_success_subject', 'Installation success') + subject = setup.get("smtp_success_subject", "Installation success") subject = _sprinkle(subject) - msg['Subject'] = subject - msg['From'] = sender - msg['To'] = recipients + msg["Subject"] = subject + msg["From"] = sender + msg["To"] = recipients - smtp = smtplib.SMTP(host=setup.get('smtp_server', '')) + smtp = smtplib.SMTP(host=setup.get("smtp_server", "")) smtp.sendmail( - sender, - [x.strip() for x in recipients.split(',')], msg.as_string()) + sender, [x.strip() for x in recipients.split(",")], msg.as_string() + ) smtp.quit() def returner(ret): - ''' + """ Check highstate return information and possibly fire off an email or save a file. - ''' + """ setup = _get_options(ret) - log.debug('highstate setup %s', setup) + log.debug("highstate setup %s", setup) report, failed = _generate_report(ret, setup) if report: @@ -482,7 +481,7 @@ def returner(ret): def __test_html(): - ''' + """ HTML generation test only used when called from the command line: python ./highstate.py Typical options for generating the report file: @@ -490,8 +489,8 @@ def __test_html(): report_format: yaml report_delivery: file file_output: '/srv/salt/_returners/test.rpt' - ''' - with salt.utils.files.fopen('test.rpt', 'r') as input_file: + """ + with salt.utils.files.fopen("test.rpt", "r") as input_file: data_text = salt.utils.stringutils.to_unicode(input_file.read()) data = salt.utils.yaml.safe_load(data_text) @@ -500,9 +499,9 @@ def __test_html(): string_file.seek(0) result = string_file.read() - with salt.utils.files.fopen('test.html', 'w') as output: + with salt.utils.files.fopen("test.html", "w") as output: output.write(salt.utils.stringutils.to_str(result)) -if __name__ == '__main__': +if __name__ == "__main__": __test_html() diff --git a/salt/returners/influxdb_return.py b/salt/returners/influxdb_return.py index b2918b82099..d010df42197 100644 --- a/salt/returners/influxdb_return.py +++ b/salt/returners/influxdb_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to an influxdb server. .. versionadded:: 2015.8.0 @@ -49,22 +49,24 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return influxdb --return_kwargs '{"db": "another-salt"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging + import requests +import salt.returners # Import Salt libs import salt.utils.jid -import salt.returners from salt.utils.decorators import memoize # Import third party libs try: import influxdb import influxdb.influxdb08 + HAS_INFLUXDB = True except ImportError: HAS_INFLUXDB = False @@ -75,31 +77,34 @@ influxDBVersionHeader = "X-Influxdb-Version" log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'influxdb' +__virtualname__ = "influxdb" def __virtual__(): if not HAS_INFLUXDB: - return False, 'Could not import influxdb returner; ' \ - 'influxdb python client is not installed.' + return ( + False, + "Could not import influxdb returner; " + "influxdb python client is not installed.", + ) return __virtualname__ def _get_options(ret=None): - ''' + """ Get the influxdb options from salt. - ''' - attrs = {'host': 'host', - 'port': 'port', - 'db': 'db', - 'user': 'user', - 'password': 'password'} + """ + attrs = { + "host": "host", + "port": "port", + "db": "db", + "user": "user", + "password": "password", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options @@ -108,64 +113,61 @@ def _get_version(host, port, user, password): version = None # check the InfluxDB version via the HTTP API try: - result = requests.get("http://{0}:{1}/ping".format(host, port), auth=(user, password)) + result = requests.get( + "http://{0}:{1}/ping".format(host, port), auth=(user, password) + ) if influxDBVersionHeader in result.headers: version = result.headers[influxDBVersionHeader] except Exception as ex: # pylint: disable=broad-except log.critical( - 'Failed to query InfluxDB version from HTTP API within InfluxDB ' - 'returner: %s', ex + "Failed to query InfluxDB version from HTTP API within InfluxDB " + "returner: %s", + ex, ) return version def _get_serv(ret=None): - ''' + """ Return an influxdb client object - ''' + """ _options = _get_options(ret) - host = _options.get('host') - port = _options.get('port') - database = _options.get('db') - user = _options.get('user') - password = _options.get('password') + host = _options.get("host") + port = _options.get("port") + database = _options.get("db") + user = _options.get("user") + password = _options.get("password") version = _get_version(host, port, user, password) if version and "v0.8" in version: - return influxdb.influxdb08.InfluxDBClient(host=host, - port=port, - username=user, - password=password, - database=database + return influxdb.influxdb08.InfluxDBClient( + host=host, port=port, username=user, password=password, database=database ) else: - return influxdb.InfluxDBClient(host=host, - port=port, - username=user, - password=password, - database=database + return influxdb.InfluxDBClient( + host=host, port=port, username=user, password=password, database=database ) def returner(ret): - ''' + """ Return data to a influxdb data store - ''' + """ serv = _get_serv(ret) # strip the 'return' key to avoid data duplication in the database - json_return = salt.utils.json.dumps(ret['return']) - del ret['return'] + json_return = salt.utils.json.dumps(ret["return"]) + del ret["return"] json_full_ret = salt.utils.json.dumps(ret) # create legacy request in case an InfluxDB 0.8.x version is used if "influxdb08" in serv.__module__: req = [ { - 'name': 'returns', - 'columns': ['fun', 'id', 'jid', 'return', 'full_ret'], - 'points': [ - [ret['fun'], ret['id'], ret['jid'], json_return, json_full_ret] + "name": "returns", + "columns": ["fun", "id", "jid", "return", "full_ret"], + "points": [ + [ret["fun"], ret["id"], ret["jid"], json_return, json_full_ret] ], } ] @@ -173,72 +175,59 @@ def returner(ret): else: req = [ { - 'measurement': 'returns', - 'tags': { - 'fun': ret['fun'], - 'id': ret['id'], - 'jid': ret['jid'] - }, - 'fields': { - 'return': json_return, - 'full_ret': json_full_ret - } + "measurement": "returns", + "tags": {"fun": ret["fun"], "id": ret["id"], "jid": ret["jid"]}, + "fields": {"return": json_return, "full_ret": json_full_ret}, } ] try: serv.write_points(req) except Exception as ex: # pylint: disable=broad-except - log.critical('Failed to store return with InfluxDB returner: %s', ex) + log.critical("Failed to store return with InfluxDB returner: %s", ex) def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid - ''' + """ serv = _get_serv(ret=None) # create legacy request in case an InfluxDB 0.8.x version is used if "influxdb08" in serv.__module__: req = [ { - 'name': 'jids', - 'columns': ['jid', 'load'], - 'points': [ - [jid, salt.utils.json.dumps(load)] - ], + "name": "jids", + "columns": ["jid", "load"], + "points": [[jid, salt.utils.json.dumps(load)]], } ] # create InfluxDB 0.9+ version request else: req = [ { - 'measurement': 'jids', - 'tags': { - 'jid': jid - }, - 'fields': { - 'load': salt.utils.json.dumps(load) - } + "measurement": "jids", + "tags": {"jid": jid}, + "fields": {"load": salt.utils.json.dumps(load)}, } ] try: serv.write_points(req) except Exception as ex: # pylint: disable=broad-except - log.critical('Failed to store load with InfluxDB returner: %s', ex) + log.critical("Failed to store load with InfluxDB returner: %s", ex) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' + """ serv = _get_serv(ret=None) sql = "select load from jids where jid = '{0}'".format(jid) @@ -251,9 +240,9 @@ def get_load(jid): def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' + """ serv = _get_serv(ret=None) sql = "select id, full_ret from returns where jid = '{0}'".format(jid) @@ -261,7 +250,7 @@ def get_jid(jid): data = serv.query(sql) ret = {} if data: - points = data[0]['points'] + points = data[0]["points"] for point in points: ret[point[3]] = salt.utils.json.loads(point[2]) @@ -269,21 +258,23 @@ def get_jid(jid): def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' + """ serv = _get_serv(ret=None) - sql = '''select first(id) as fid, first(full_ret) as fret + sql = """select first(id) as fid, first(full_ret) as fret from returns where fun = '{0}' group by fun, id - '''.format(fun) + """.format( + fun + ) data = serv.query(sql) ret = {} if data: - points = data[0]['points'] + points = data[0]["points"] for point in points: ret[point[1]] = salt.utils.json.loads(point[2]) @@ -291,9 +282,9 @@ def get_fun(fun): def get_jids(): - ''' + """ Return a list of all job ids - ''' + """ serv = _get_serv(ret=None) sql = "select distinct(jid) from jids group by load" @@ -304,29 +295,31 @@ def get_jids(): data = serv.query(sql) ret = {} if data: - for _, jid, load in data[0]['points']: - ret[jid] = salt.utils.jid.format_jid_instance(jid, salt.utils.json.loads(load)) + for _, jid, load in data[0]["points"]: + ret[jid] = salt.utils.jid.format_jid_instance( + jid, salt.utils.json.loads(load) + ) return ret def get_minions(): - ''' + """ Return a list of minions - ''' + """ serv = _get_serv(ret=None) sql = "select distinct(id) from returns" data = serv.query(sql) ret = [] if data: - for jid in data[0]['points']: + for jid in data[0]["points"]: ret.append(jid[1]) return ret def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/kafka_return.py b/salt/returners/kafka_return.py index 474b4958c8f..47ee7342d27 100644 --- a/salt/returners/kafka_return.py +++ b/salt/returners/kafka_return.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a Kafka topic :maintainer: Justin Desilets (justin.desilets@gmail.com) @@ -22,14 +22,17 @@ To use the kafka returner, append `--return kafka` to the Salt command, eg; salt '*' test.ping --return kafka -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging + import salt.utils.json # Import third-party libs try: from confluent_kafka import Producer + HAS_KAFKA = True except ImportError: HAS_KAFKA = False @@ -37,48 +40,56 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'kafka' +__virtualname__ = "kafka" def __virtual__(): if not HAS_KAFKA: - return False, 'Could not import kafka returner; confluent-kafka is not installed.' + return ( + False, + "Could not import kafka returner; confluent-kafka is not installed.", + ) return __virtualname__ def _get_conn(): - ''' + """ Return a kafka connection - ''' - if __salt__['config.option']('returner.kafka.bootstrap'): - bootstrap = ','.join(__salt__['config.option']('returner.kafka.bootstrap')) + """ + if __salt__["config.option"]("returner.kafka.bootstrap"): + bootstrap = ",".join(__salt__["config.option"]("returner.kafka.bootstrap")) else: - log.error('Unable to find kafka returner config option: bootstrap') + log.error("Unable to find kafka returner config option: bootstrap") return None return bootstrap def _delivery_report(err, msg): - ''' Called once for each message produced to indicate delivery result. - Triggered by poll() or flush(). ''' + """ Called once for each message produced to indicate delivery result. + Triggered by poll() or flush(). """ if err is not None: - log.error('Message delivery failed: %s', err) + log.error("Message delivery failed: %s", err) else: - log.debug('Message delivered to %s [%s]', msg.topic(), msg.partition()) + log.debug("Message delivered to %s [%s]", msg.topic(), msg.partition()) def returner(ret): - ''' + """ Return information to a Kafka server - ''' - if __salt__['config.option']('returner.kafka.topic'): - topic = __salt__['config.option']('returner.kafka.topic') + """ + if __salt__["config.option"]("returner.kafka.topic"): + topic = __salt__["config.option"]("returner.kafka.topic") conn = _get_conn() - producer = Producer({'bootstrap.servers': conn}) + producer = Producer({"bootstrap.servers": conn}) producer.poll(0) - producer.produce(topic, salt.utils.json.dumps(ret), str(ret).encode('utf-8'), callback=_delivery_report) + producer.produce( + topic, + salt.utils.json.dumps(ret), + str(ret).encode("utf-8"), + callback=_delivery_report, + ) producer.flush() else: - log.error('Unable to find kafka returner config option: topic') + log.error("Unable to find kafka returner config option: topic") diff --git a/salt/returners/librato_return.py b/salt/returners/librato_return.py index e911a027a23..1c19ad255af 100644 --- a/salt/returners/librato_return.py +++ b/salt/returners/librato_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Salt returner to return highstate stats to Librato To enable this returner the minion will need the Librato @@ -28,134 +28,135 @@ by adding more tags to the submission. pillar_data = __salt__['pillar.raw']() q.add(metric.name, value, tags={'Name': ret['id'],'Region': pillar_data['ec2_tags']['Name']}) -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.returners + # Import Salt libs import salt.utils.jid -import salt.returners # Import third party libs try: import librato + HAS_LIBRATO = True except ImportError: HAS_LIBRATO = False # Define the module's Virtual Name -__virtualname__ = 'librato' +__virtualname__ = "librato" log = logging.getLogger(__name__) def __virtual__(): if not HAS_LIBRATO: - return False, 'Could not import librato module; ' \ - 'librato python client is not installed.' + return ( + False, + "Could not import librato module; " + "librato python client is not installed.", + ) return __virtualname__ def _get_options(ret=None): - ''' + """ Get the Librato options from salt. - ''' - attrs = {'email': 'email', - 'api_token': 'api_token', - 'api_url': 'api_url' - } + """ + attrs = {"email": "email", "api_token": "api_token", "api_url": "api_url"} - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) - _options['api_url'] = _options.get('api_url', 'metrics-api.librato.com') + _options["api_url"] = _options.get("api_url", "metrics-api.librato.com") - log.debug('Retrieved Librato options: %s', _options) + log.debug("Retrieved Librato options: %s", _options) return _options def _get_librato(ret=None): - ''' + """ Return a Librato connection object. - ''' + """ _options = _get_options(ret) conn = librato.connect( - _options.get('email'), - _options.get('api_token'), + _options.get("email"), + _options.get("api_token"), sanitizer=librato.sanitize_metric_name, - hostname=_options.get('api_url')) + hostname=_options.get("api_url"), + ) log.info("Connected to librato.") return conn def _calculate_runtimes(states): - results = { - 'runtime': 0.00, - 'num_failed_states': 0, - 'num_passed_states': 0 - } + results = {"runtime": 0.00, "num_failed_states": 0, "num_passed_states": 0} for state, resultset in states.items(): - if isinstance(resultset, dict) and 'duration' in resultset: + if isinstance(resultset, dict) and "duration" in resultset: # Count the pass vs failures - if resultset['result']: - results['num_passed_states'] += 1 + if resultset["result"]: + results["num_passed_states"] += 1 else: - results['num_failed_states'] += 1 + results["num_failed_states"] += 1 # Count durations - results['runtime'] += resultset['duration'] + results["runtime"] += resultset["duration"] - log.debug('Parsed state metrics: %s', results) + log.debug("Parsed state metrics: %s", results) return results def returner(ret): - ''' + """ Parse the return data and return metrics to Librato. - ''' + """ librato_conn = _get_librato(ret) q = librato_conn.new_queue() - if ret['fun'] == 'state.highstate': - log.debug('Found returned Highstate data.') + if ret["fun"] == "state.highstate": + log.debug("Found returned Highstate data.") # Calculate the runtimes and number of failed states. - stats = _calculate_runtimes(ret['return']) - log.debug('Batching Metric retcode with %s', ret['retcode']) - q.add('saltstack.highstate.retcode', - ret['retcode'], tags={'Name': ret['id']}) + stats = _calculate_runtimes(ret["return"]) + log.debug("Batching Metric retcode with %s", ret["retcode"]) + q.add("saltstack.highstate.retcode", ret["retcode"], tags={"Name": ret["id"]}) + + log.debug("Batching Metric num_failed_jobs with %s", stats["num_failed_states"]) + q.add( + "saltstack.highstate.failed_states", + stats["num_failed_states"], + tags={"Name": ret["id"]}, + ) log.debug( - 'Batching Metric num_failed_jobs with %s', - stats['num_failed_states'] + "Batching Metric num_passed_states with %s", stats["num_passed_states"] ) - q.add('saltstack.highstate.failed_states', - stats['num_failed_states'], tags={'Name': ret['id']}) + q.add( + "saltstack.highstate.passed_states", + stats["num_passed_states"], + tags={"Name": ret["id"]}, + ) + + log.debug("Batching Metric runtime with %s", stats["runtime"]) + q.add("saltstack.highstate.runtime", stats["runtime"], tags={"Name": ret["id"]}) log.debug( - 'Batching Metric num_passed_states with %s', - stats['num_passed_states'] + "Batching Metric runtime with %s", + stats["num_failed_states"] + stats["num_passed_states"], ) - q.add('saltstack.highstate.passed_states', - stats['num_passed_states'], tags={'Name': ret['id']}) - - log.debug('Batching Metric runtime with %s', stats['runtime']) - q.add('saltstack.highstate.runtime', - stats['runtime'], tags={'Name': ret['id']}) - - log.debug( - 'Batching Metric runtime with %s', - stats['num_failed_states'] + stats['num_passed_states'] + q.add( + "saltstack.highstate.total_states", + stats["num_failed_states"] + stats["num_passed_states"], + tags={"Name": ret["id"]}, ) - q.add('saltstack.highstate.total_states', stats[ - 'num_failed_states'] + stats['num_passed_states'], tags={'Name': ret['id']}) - log.info('Sending metrics to Librato.') + log.info("Sending metrics to Librato.") q.submit() diff --git a/salt/returners/local.py b/salt/returners/local.py index 414bea9cbd5..ab902ec175d 100644 --- a/salt/returners/local.py +++ b/salt/returners/local.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The local returner is used to test the returner interface, it just prints the return data to the console to verify that it is being passed properly @@ -8,22 +8,22 @@ To use the local returner, append '--return local' to the salt command. ex: .. code-block:: bash salt '*' test.ping --return local -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals def returner(ret): - ''' + """ Print the return data to the terminal to verify functionality - ''' + """ print(ret) def event_return(event): - ''' + """ Print event return data to the terminal to verify functionality - ''' + """ print(event) diff --git a/salt/returners/local_cache.py b/salt/returners/local_cache.py index c58b6a37abc..24b5ed74615 100644 --- a/salt/returners/local_cache.py +++ b/salt/returners/local_cache.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Return data to local job cache -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import bisect + # Import python libs import errno import glob @@ -12,7 +14,8 @@ import logging import os import shutil import time -import bisect + +import salt.exceptions # Import salt libs import salt.payload @@ -22,7 +25,6 @@ import salt.utils.jid import salt.utils.minions import salt.utils.msgpack import salt.utils.stringutils -import salt.exceptions # Import 3rd-party libs from salt.ext import six @@ -31,32 +33,32 @@ from salt.ext.six.moves import range # pylint: disable=import-error,redefined-b log = logging.getLogger(__name__) # load is the published job -LOAD_P = '.load.p' +LOAD_P = ".load.p" # the list of minions that the job is targeted to (best effort match on the # master side) -MINIONS_P = '.minions.p' +MINIONS_P = ".minions.p" # format string for minion lists forwarded from syndic masters (the placeholder # will be replaced with the syndic master's id) -SYNDIC_MINIONS_P = '.minions.{0}.p' +SYNDIC_MINIONS_P = ".minions.{0}.p" # return is the "return" from the minion data -RETURN_P = 'return.p' +RETURN_P = "return.p" # out is the "out" from the minion data -OUT_P = 'out.p' +OUT_P = "out.p" # endtime is the end time for a job, not stored as msgpack -ENDTIME = 'endtime' +ENDTIME = "endtime" def _job_dir(): - ''' + """ Return root of the jobs cache directory - ''' - return os.path.join(__opts__['cachedir'], 'jobs') + """ + return os.path.join(__opts__["cachedir"], "jobs") def _walk_through(job_dir): - ''' + """ Walk though the jid dir and look for jobs - ''' + """ serial = salt.payload.Serial(__opts__) for top in os.listdir(job_dir): @@ -71,30 +73,33 @@ def _walk_through(job_dir): if not os.path.isfile(load_path): continue - with salt.utils.files.fopen(load_path, 'rb') as rfh: + with salt.utils.files.fopen(load_path, "rb") as rfh: try: job = serial.load(rfh) except Exception: # pylint: disable=broad-except - log.exception('Failed to deserialize %s', load_path) + log.exception("Failed to deserialize %s", load_path) continue if not job: - log.error('Deserialization of job succeded but there is no data in %s', load_path) + log.error( + "Deserialization of job succeded but there is no data in %s", + load_path, + ) continue - jid = job['jid'] + jid = job["jid"] yield jid, job, t_path, final -#TODO: add to returner docs-- this is a new one +# TODO: add to returner docs-- this is a new one def prep_jid(nocache=False, passed_jid=None, recurse_count=0): - ''' + """ Return a job id and prepare the job id directory. This is the function responsible for making sure jids don't collide (unless it is passed a jid). So do what you have to do to make sure that stays the case - ''' + """ if recurse_count >= 5: - err = 'prep_jid could not store a jid after {0} tries.'.format(recurse_count) + err = "prep_jid could not store a jid after {0} tries.".format(recurse_count) log.error(err) raise salt.exceptions.SaltCacheError(err) if passed_jid is None: # this can be a None or an empty string. @@ -102,7 +107,7 @@ def prep_jid(nocache=False, passed_jid=None, recurse_count=0): else: jid = passed_jid - jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__['hash_type']) + jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__["hash_type"]) # Make sure we create the jid dir, otherwise someone else is using it, # meaning we need a new jid. @@ -112,39 +117,39 @@ def prep_jid(nocache=False, passed_jid=None, recurse_count=0): except OSError: time.sleep(0.1) if passed_jid is None: - return prep_jid(nocache=nocache, recurse_count=recurse_count+1) + return prep_jid(nocache=nocache, recurse_count=recurse_count + 1) try: - with salt.utils.files.fopen(os.path.join(jid_dir, 'jid'), 'wb+') as fn_: + with salt.utils.files.fopen(os.path.join(jid_dir, "jid"), "wb+") as fn_: fn_.write(salt.utils.stringutils.to_bytes(jid)) if nocache: - with salt.utils.files.fopen(os.path.join(jid_dir, 'nocache'), 'wb+'): + with salt.utils.files.fopen(os.path.join(jid_dir, "nocache"), "wb+"): pass except IOError: - log.warning( - 'Could not write out jid file for job %s. Retrying.', jid) + log.warning("Could not write out jid file for job %s. Retrying.", jid) time.sleep(0.1) - return prep_jid(passed_jid=jid, nocache=nocache, - recurse_count=recurse_count+1) + return prep_jid( + passed_jid=jid, nocache=nocache, recurse_count=recurse_count + 1 + ) return jid def returner(load): - ''' + """ Return data to the local job cache - ''' + """ serial = salt.payload.Serial(__opts__) # if a minion is returning a standalone job, get a jobid - if load['jid'] == 'req': - load['jid'] = prep_jid(nocache=load.get('nocache', False)) + if load["jid"] == "req": + load["jid"] = prep_jid(nocache=load.get("nocache", False)) - jid_dir = salt.utils.jid.jid_dir(load['jid'], _job_dir(), __opts__['hash_type']) - if os.path.exists(os.path.join(jid_dir, 'nocache')): + jid_dir = salt.utils.jid.jid_dir(load["jid"], _job_dir(), __opts__["hash_type"]) + if os.path.exists(os.path.join(jid_dir, "nocache")): return - hn_dir = os.path.join(jid_dir, load['id']) + hn_dir = os.path.join(jid_dir, load["id"]) try: os.makedirs(hn_dir) @@ -152,53 +157,54 @@ def returner(load): if err.errno == errno.EEXIST: # Minion has already returned this jid and it should be dropped log.error( - 'An extra return was detected from minion %s, please verify ' - 'the minion, this could be a replay attack', load['id'] + "An extra return was detected from minion %s, please verify " + "the minion, this could be a replay attack", + load["id"], ) return False elif err.errno == errno.ENOENT: log.error( - 'An inconsistency occurred, a job was received with a job id ' - '(%s) that is not present in the local cache', load['jid'] + "An inconsistency occurred, a job was received with a job id " + "(%s) that is not present in the local cache", + load["jid"], ) return False raise serial.dump( - dict((key, load[key]) for key in ['return', 'retcode', 'success'] if key in load), + dict( + (key, load[key]) for key in ["return", "retcode", "success"] if key in load + ), # Use atomic open here to avoid the file being read before it's # completely written to. Refs #1935 - salt.utils.atomicfile.atomic_open( - os.path.join(hn_dir, RETURN_P), 'w+b' - ) + salt.utils.atomicfile.atomic_open(os.path.join(hn_dir, RETURN_P), "w+b"), ) - if 'out' in load: + if "out" in load: serial.dump( - load['out'], + load["out"], # Use atomic open here to avoid the file being read before # it's completely written to. Refs #1935 - salt.utils.atomicfile.atomic_open( - os.path.join(hn_dir, OUT_P), 'w+b' - ) + salt.utils.atomicfile.atomic_open(os.path.join(hn_dir, OUT_P), "w+b"), ) def save_load(jid, clear_load, minions=None, recurse_count=0): - ''' + """ Save the load to the specified jid minions argument is to provide a pre-computed list of matched minions for the job, for cases when this function can't compute that list itself (such as for salt-ssh) - ''' + """ if recurse_count >= 5: - err = ('save_load could not write job cache file after {0} retries.' - .format(recurse_count)) + err = "save_load could not write job cache file after {0} retries.".format( + recurse_count + ) log.error(err) raise salt.exceptions.SaltCacheError(err) - jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__['hash_type']) + jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__["hash_type"]) serial = salt.payload.Serial(__opts__) @@ -214,46 +220,44 @@ def save_load(jid, clear_load, minions=None, recurse_count=0): else: raise try: - with salt.utils.files.fopen(os.path.join(jid_dir, LOAD_P), 'w+b') as wfh: + with salt.utils.files.fopen(os.path.join(jid_dir, LOAD_P), "w+b") as wfh: serial.dump(clear_load, wfh) except IOError as exc: - log.warning( - 'Could not write job invocation cache file: %s', exc - ) + log.warning("Could not write job invocation cache file: %s", exc) time.sleep(0.1) - return save_load(jid=jid, clear_load=clear_load, - recurse_count=recurse_count+1) + return save_load( + jid=jid, clear_load=clear_load, recurse_count=recurse_count + 1 + ) # if you have a tgt, save that for the UI etc - if 'tgt' in clear_load and clear_load['tgt'] != '': + if "tgt" in clear_load and clear_load["tgt"] != "": if minions is None: ckminions = salt.utils.minions.CkMinions(__opts__) # Retrieve the minions list _res = ckminions.check_minions( - clear_load['tgt'], - clear_load.get('tgt_type', 'glob') - ) - minions = _res['minions'] + clear_load["tgt"], clear_load.get("tgt_type", "glob") + ) + minions = _res["minions"] # save the minions to a cache so we can see in the UI save_minions(jid, minions) def save_minions(jid, minions, syndic_id=None): - ''' + """ Save/update the serialized list of minions for a given job - ''' + """ # Ensure we have a list for Python 3 compatability minions = list(minions) log.debug( - 'Adding minions for job %s%s: %s', + "Adding minions for job %s%s: %s", jid, - ' from syndic master \'{0}\''.format(syndic_id) if syndic_id else '', - minions + " from syndic master '{0}'".format(syndic_id) if syndic_id else "", + minions, ) serial = salt.payload.Serial(__opts__) - jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__['hash_type']) + jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__["hash_type"]) try: if not os.path.exists(jid_dir): @@ -267,10 +271,7 @@ def save_minions(jid, minions, syndic_id=None): raise if syndic_id is not None: - minions_path = os.path.join( - jid_dir, - SYNDIC_MINIONS_P.format(syndic_id) - ) + minions_path = os.path.join(jid_dir, SYNDIC_MINIONS_P.format(syndic_id)) else: minions_path = os.path.join(jid_dir, MINIONS_P) @@ -280,20 +281,22 @@ def save_minions(jid, minions, syndic_id=None): os.makedirs(jid_dir) except OSError: pass - with salt.utils.files.fopen(minions_path, 'w+b') as wfh: + with salt.utils.files.fopen(minions_path, "w+b") as wfh: serial.dump(minions, wfh) except IOError as exc: log.error( - 'Failed to write minion list %s to job cache file %s: %s', - minions, minions_path, exc + "Failed to write minion list %s to job cache file %s: %s", + minions, + minions_path, + exc, ) def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' - jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__['hash_type']) + """ + jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__["hash_type"]) load_fn = os.path.join(jid_dir, LOAD_P) if not os.path.exists(jid_dir) or not os.path.exists(load_fn): return {} @@ -302,7 +305,7 @@ def get_load(jid): load_p = os.path.join(jid_dir, LOAD_P) num_tries = 5 for index in range(1, num_tries + 1): - with salt.utils.files.fopen(load_p, 'rb') as rfh: + with salt.utils.files.fopen(load_p, "rb") as rfh: try: ret = serial.load(rfh) break @@ -310,34 +313,32 @@ def get_load(jid): if index == num_tries: time.sleep(0.25) else: - log.critical('Failed to unpack %s', load_p) + log.critical("Failed to unpack %s", load_p) raise exc if ret is None: ret = {} minions_cache = [os.path.join(jid_dir, MINIONS_P)] - minions_cache.extend( - glob.glob(os.path.join(jid_dir, SYNDIC_MINIONS_P.format('*'))) - ) + minions_cache.extend(glob.glob(os.path.join(jid_dir, SYNDIC_MINIONS_P.format("*")))) all_minions = set() for minions_path in minions_cache: - log.debug('Reading minion list from %s', minions_path) + log.debug("Reading minion list from %s", minions_path) try: - with salt.utils.files.fopen(minions_path, 'rb') as rfh: + with salt.utils.files.fopen(minions_path, "rb") as rfh: all_minions.update(serial.load(rfh)) except IOError as exc: salt.utils.files.process_read_exception(exc, minions_path) if all_minions: - ret['Minions'] = sorted(all_minions) + ret["Minions"] = sorted(all_minions) return ret def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' - jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__['hash_type']) + """ + jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__["hash_type"]) serial = salt.payload.Serial(__opts__) ret = {} @@ -345,7 +346,7 @@ def get_jid(jid): if not os.path.isdir(jid_dir): return ret for fn_ in os.listdir(jid_dir): - if fn_.startswith('.'): + if fn_.startswith("."): continue if fn_ not in ret: retp = os.path.join(jid_dir, fn_, RETURN_P) @@ -354,50 +355,50 @@ def get_jid(jid): continue while fn_ not in ret: try: - with salt.utils.files.fopen(retp, 'rb') as rfh: + with salt.utils.files.fopen(retp, "rb") as rfh: ret_data = serial.load(rfh) - if not isinstance(ret_data, dict) or 'return' not in ret_data: + if not isinstance(ret_data, dict) or "return" not in ret_data: # Convert the old format in which return.p contains the only return data to # the new that is dict containing 'return' and optionally 'retcode' and # 'success'. - ret_data = {'return': ret_data} + ret_data = {"return": ret_data} ret[fn_] = ret_data if os.path.isfile(outp): - with salt.utils.files.fopen(outp, 'rb') as rfh: - ret[fn_]['out'] = serial.load(rfh) + with salt.utils.files.fopen(outp, "rb") as rfh: + ret[fn_]["out"] = serial.load(rfh) except Exception as exc: # pylint: disable=broad-except - if 'Permission denied:' in six.text_type(exc): + if "Permission denied:" in six.text_type(exc): raise return ret def get_jids(): - ''' + """ Return a dict mapping all job ids to job information - ''' + """ ret = {} for jid, job, _, _ in _walk_through(_job_dir()): ret[jid] = salt.utils.jid.format_jid_instance(jid, job) - if __opts__.get('job_cache_store_endtime'): + if __opts__.get("job_cache_store_endtime"): endtime = get_endtime(jid) if endtime: - ret[jid]['EndTime'] = endtime + ret[jid]["EndTime"] = endtime return ret def get_jids_filter(count, filter_find_job=True): - ''' + """ Return a list of all jobs information filtered by the given criteria. :param int count: show not more than the count of most recent jobs :param bool filter_find_jobs: filter out 'saltutil.find_job' jobs - ''' + """ keys = [] ret = [] for jid, job, _, _ in _walk_through(_job_dir()): job = salt.utils.jid.format_jid_instance_ext(jid, job) - if filter_find_job and job['Function'] == 'saltutil.find_job': + if filter_find_job and job["Function"] == "saltutil.find_job": continue i = bisect.bisect(keys, jid) if len(keys) == count and i == 0: @@ -411,10 +412,10 @@ def get_jids_filter(count, filter_find_job=True): def clean_old_jobs(): - ''' + """ Clean out the old jobs from the job cache - ''' - if __opts__['keep_jobs'] != 0: + """ + if __opts__["keep_jobs"] != 0: jid_root = _job_dir() if not os.path.exists(jid_root): @@ -437,7 +438,7 @@ def clean_old_jobs(): for final in t_path_dirs: f_path = os.path.join(t_path, final) - jid_file = os.path.join(f_path, 'jid') + jid_file = os.path.join(f_path, "jid") if not os.path.isfile(jid_file) and os.path.exists(f_path): # No jid file means corrupted cache entry, scrub it # by removing the entire f_path directory @@ -445,12 +446,14 @@ def clean_old_jobs(): elif os.path.isfile(jid_file): jid_ctime = os.stat(jid_file).st_ctime hours_difference = (time.time() - jid_ctime) / 3600.0 - if hours_difference > __opts__['keep_jobs'] and os.path.exists(t_path): + if hours_difference > __opts__["keep_jobs"] and os.path.exists( + t_path + ): # Remove the entire f_path from the original JID dir try: shutil.rmtree(f_path) except OSError as err: - log.error('Unable to remove %s: %s', f_path, err) + log.error("Unable to remove %s: %s", f_path, err) # Remove empty JID dirs from job cache, if they're old enough. # JID dirs may be empty either from a previous cache-clean with the bug @@ -462,54 +465,54 @@ def clean_old_jobs(): # t_path JID dirs were created, but not yet populated by a jid file. t_path_ctime = os.stat(t_path).st_ctime hours_difference = (time.time() - t_path_ctime) / 3600.0 - if hours_difference > __opts__['keep_jobs']: + if hours_difference > __opts__["keep_jobs"]: shutil.rmtree(t_path) def update_endtime(jid, time): - ''' + """ Update (or store) the end time for a given job Endtime is stored as a plain text string - ''' - jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__['hash_type']) + """ + jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__["hash_type"]) try: if not os.path.exists(jid_dir): os.makedirs(jid_dir) - with salt.utils.files.fopen(os.path.join(jid_dir, ENDTIME), 'w') as etfile: + with salt.utils.files.fopen(os.path.join(jid_dir, ENDTIME), "w") as etfile: etfile.write(salt.utils.stringutils.to_str(time)) except IOError as exc: - log.warning('Could not write job invocation cache file: %s', exc) + log.warning("Could not write job invocation cache file: %s", exc) def get_endtime(jid): - ''' + """ Retrieve the stored endtime for a given job Returns False if no endtime is present - ''' - jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__['hash_type']) + """ + jid_dir = salt.utils.jid.jid_dir(jid, _job_dir(), __opts__["hash_type"]) etpath = os.path.join(jid_dir, ENDTIME) if not os.path.exists(etpath): return False - with salt.utils.files.fopen(etpath, 'r') as etfile: - endtime = salt.utils.stringutils.to_unicode(etfile.read()).strip('\n') + with salt.utils.files.fopen(etpath, "r") as etfile: + endtime = salt.utils.stringutils.to_unicode(etfile.read()).strip("\n") return endtime def _reg_dir(): - ''' + """ Return the reg_dir for the given job id - ''' - return os.path.join(__opts__['cachedir'], 'thorium') + """ + return os.path.join(__opts__["cachedir"], "thorium") def save_reg(data): - ''' + """ Save the register to msgpack files - ''' + """ reg_dir = _reg_dir() - regfile = os.path.join(reg_dir, 'register') + regfile = os.path.join(reg_dir, "register") try: if not os.path.exists(reg_dir): os.makedirs(reg_dir) @@ -519,22 +522,22 @@ def save_reg(data): else: raise try: - with salt.utils.files.fopen(regfile, 'a') as fh_: + with salt.utils.files.fopen(regfile, "a") as fh_: salt.utils.msgpack.dump(data, fh_) except Exception: # pylint: disable=broad-except - log.error('Could not write to msgpack file %s', __opts__['outdir']) + log.error("Could not write to msgpack file %s", __opts__["outdir"]) raise def load_reg(): - ''' + """ Load the register from msgpack files - ''' + """ reg_dir = _reg_dir() - regfile = os.path.join(reg_dir, 'register') + regfile = os.path.join(reg_dir, "register") try: - with salt.utils.files.fopen(regfile, 'r') as fh_: + with salt.utils.files.fopen(regfile, "r") as fh_: return salt.utils.msgpack.load(fh_) except Exception: # pylint: disable=broad-except - log.error('Could not write to msgpack file %s', __opts__['outdir']) + log.error("Could not write to msgpack file %s", __opts__["outdir"]) raise diff --git a/salt/returners/mattermost_returner.py b/salt/returners/mattermost_returner.py index 09015b43911..f8c309fcbac 100644 --- a/salt/returners/mattermost_returner.py +++ b/salt/returners/mattermost_returner.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data via mattermost .. versionadded:: 2017.7.0 @@ -42,132 +42,121 @@ To override individual configuration items, append --return_kwargs '{'key:': 'va .. code-block:: bash salt '*' test.ping --return mattermost --return_kwargs '{'channel': '#random'}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging -# Import 3rd-party libs -from salt.ext import six # pylint: disable=import-error,no-name-in-module,redefined-builtin import salt.ext.six.moves.http_client -# pylint: enable=import-error,no-name-in-module,redefined-builtin # Import Salt Libs import salt.returners import salt.utils.json import salt.utils.mattermost +# Import 3rd-party libs +from salt.ext import six + +# pylint: enable=import-error,no-name-in-module,redefined-builtin + + log = logging.getLogger(__name__) -__virtualname__ = 'mattermost' +__virtualname__ = "mattermost" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ def _get_options(ret=None): - ''' + """ Get the mattermost options from salt. - ''' + """ - attrs = {'channel': 'channel', - 'username': 'username', - 'hook': 'hook', - 'api_url': 'api_url' - } + attrs = { + "channel": "channel", + "username": "username", + "hook": "hook", + "api_url": "api_url", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) - log.debug('Options: %s', _options) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) + log.debug("Options: %s", _options) return _options def returner(ret): - ''' + """ Send an mattermost message with the data - ''' + """ _options = _get_options(ret) - api_url = _options.get('api_url') - channel = _options.get('channel') - username = _options.get('username') - hook = _options.get('hook') + api_url = _options.get("api_url") + channel = _options.get("channel") + username = _options.get("username") + hook = _options.get("hook") if not hook: - log.error('mattermost.hook not defined in salt config') + log.error("mattermost.hook not defined in salt config") return - returns = ret.get('return') + returns = ret.get("return") - message = ('id: {0}\r\n' - 'function: {1}\r\n' - 'function args: {2}\r\n' - 'jid: {3}\r\n' - 'return: {4}\r\n').format( - ret.get('id'), - ret.get('fun'), - ret.get('fun_args'), - ret.get('jid'), - returns) + message = ( + "id: {0}\r\n" + "function: {1}\r\n" + "function args: {2}\r\n" + "jid: {3}\r\n" + "return: {4}\r\n" + ).format( + ret.get("id"), ret.get("fun"), ret.get("fun_args"), ret.get("jid"), returns + ) - mattermost = post_message(channel, - message, - username, - api_url, - hook) + mattermost = post_message(channel, message, username, api_url, hook) return mattermost def event_return(events): - ''' + """ Send the events to a mattermost room. :param events: List of events :return: Boolean if messages were sent successfully. - ''' + """ _options = _get_options() - api_url = _options.get('api_url') - channel = _options.get('channel') - username = _options.get('username') - hook = _options.get('hook') + api_url = _options.get("api_url") + channel = _options.get("channel") + username = _options.get("username") + hook = _options.get("hook") is_ok = True for event in events: - log.debug('Event: %s', event) - log.debug('Event data: %s', event['data']) - message = 'tag: {0}\r\n'.format(event['tag']) - for key, value in six.iteritems(event['data']): - message += '{0}: {1}\r\n'.format(key, value) - result = post_message(channel, - message, - username, - api_url, - hook) + log.debug("Event: %s", event) + log.debug("Event data: %s", event["data"]) + message = "tag: {0}\r\n".format(event["tag"]) + for key, value in six.iteritems(event["data"]): + message += "{0}: {1}\r\n".format(key, value) + result = post_message(channel, message, username, api_url, hook) if not result: is_ok = False return is_ok -def post_message(channel, - message, - username, - api_url, - hook): - ''' +def post_message(channel, message, username, api_url, hook): + """ Send a message to a mattermost room. :param channel: The room name. @@ -175,19 +164,20 @@ def post_message(channel, :param username: Specify who the message is from. :param hook: The mattermost hook, if not specified in the configuration. :return: Boolean if message was sent successfully. - ''' + """ parameters = dict() if channel: - parameters['channel'] = channel + parameters["channel"] = channel if username: - parameters['username'] = username - parameters['text'] = '```' + message + '```' # pre-formatted, fixed-width text - log.debug('Parameters: %s', parameters) + parameters["username"] = username + parameters["text"] = "```" + message + "```" # pre-formatted, fixed-width text + log.debug("Parameters: %s", parameters) result = salt.utils.mattermost.query( api_url=api_url, hook=hook, - data=str('payload={0}').format(salt.utils.json.dumps(parameters))) # future lint: disable=blacklisted-function + data=str("payload={0}").format(salt.utils.json.dumps(parameters)), + ) # future lint: disable=blacklisted-function - log.debug('result %s', result) + log.debug("result %s", result) return bool(result) diff --git a/salt/returners/memcache_return.py b/salt/returners/memcache_return.py index 27b44aa441b..159c3291142 100644 --- a/salt/returners/memcache_return.py +++ b/salt/returners/memcache_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a memcache server To enable this returner the minion will need the python client for memcache @@ -44,15 +44,15 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return memcache --return_kwargs '{"host": "hostname.domain.com"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging +import salt.returners import salt.utils.jid import salt.utils.json -import salt.returners from salt.ext import six log = logging.getLogger(__name__) @@ -60,54 +60,55 @@ log = logging.getLogger(__name__) # Import third party libs try: import memcache + HAS_MEMCACHE = True except ImportError: HAS_MEMCACHE = False # Define the module's virtual name -__virtualname__ = 'memcache' +__virtualname__ = "memcache" def __virtual__(): if not HAS_MEMCACHE: - return False, 'Could not import memcache returner; ' \ - 'memcache python client is not installed.' + return ( + False, + "Could not import memcache returner; " + "memcache python client is not installed.", + ) return __virtualname__ def _get_options(ret=None): - ''' + """ Get the memcache options from salt. - ''' - attrs = {'host': 'host', - 'port': 'port'} + """ + attrs = {"host": "host", "port": "port"} - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options def _get_serv(ret): - ''' + """ Return a memcache server object - ''' + """ _options = _get_options(ret) - host = _options.get('host') - port = _options.get('port') + host = _options.get("host") + port = _options.get("port") - log.debug('memcache server: %s:%s', host, port) + log.debug("memcache server: %s:%s", host, port) if not host or not port: - log.error('Host or port not defined in salt config') + log.error("Host or port not defined in salt config") return # Combine host and port to conform syntax of python memcache client memcacheoptions = (host, port) - return memcache.Client(['{0}:{1}'.format(*memcacheoptions)], debug=0) + return memcache.Client(["{0}:{1}".format(*memcacheoptions)], debug=0) # # TODO: make memcacheoptions cluster aware # Servers can be passed in two forms: # 1. Strings of the form C{"host:port"}, which implies a default weight of 1 @@ -118,62 +119,62 @@ def _get_serv(ret): def _get_list(serv, key): value = serv.get(key) if value: - return value.strip(',').split(',') + return value.strip(",").split(",") return [] def _append_list(serv, key, value): if value in _get_list(serv, key): return - r = serv.append(key, '{0},'.format(value)) + r = serv.append(key, "{0},".format(value)) if not r: - serv.add(key, '{0},'.format(value)) + serv.add(key, "{0},".format(value)) def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) def returner(ret): - ''' + """ Return data to a memcache data store - ''' + """ serv = _get_serv(ret) - minion = ret['id'] - jid = ret['jid'] - fun = ret['fun'] + minion = ret["id"] + jid = ret["jid"] + fun = ret["fun"] rets = salt.utils.json.dumps(ret) - serv.set('{0}:{1}'.format(jid, minion), rets) # cache for get_jid - serv.set('{0}:{1}'.format(fun, minion), rets) # cache for get_fun + serv.set("{0}:{1}".format(jid, minion), rets) # cache for get_jid + serv.set("{0}:{1}".format(fun, minion), rets) # cache for get_fun # The following operations are neither efficient nor atomic. # If there is a way to make them so, this should be updated. - _append_list(serv, 'minions', minion) - _append_list(serv, 'jids', jid) + _append_list(serv, "minions", minion) + _append_list(serv, "jids", jid) def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid - ''' + """ serv = _get_serv(ret=None) serv.set(jid, salt.utils.json.dumps(load)) - _append_list(serv, 'jids', jid) + _append_list(serv, "jids", jid) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' + """ serv = _get_serv(ret=None) data = serv.get(jid) if data: @@ -182,12 +183,12 @@ def get_load(jid): def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' + """ serv = _get_serv(ret=None) - minions = _get_list(serv, 'minions') - returns = serv.get_multi(minions, key_prefix='{0}:'.format(jid)) + minions = _get_list(serv, "minions") + returns = serv.get_multi(minions, key_prefix="{0}:".format(jid)) # returns = {minion: return, minion: return, ...} ret = {} for minion, data in six.iteritems(returns): @@ -196,12 +197,12 @@ def get_jid(jid): def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' + """ serv = _get_serv(ret=None) - minions = _get_list(serv, 'minions') - returns = serv.get_multi(minions, key_prefix='{0}:'.format(fun)) + minions = _get_list(serv, "minions") + returns = serv.get_multi(minions, key_prefix="{0}:".format(fun)) # returns = {minion: return, minion: return, ...} ret = {} for minion, data in six.iteritems(returns): @@ -210,11 +211,11 @@ def get_fun(fun): def get_jids(): - ''' + """ Return a list of all job ids - ''' + """ serv = _get_serv(ret=None) - jids = _get_list(serv, 'jids') + jids = _get_list(serv, "jids") loads = serv.get_multi(jids) # {jid: load, jid: load, ...} ret = {} for jid, load in six.iteritems(loads): @@ -223,8 +224,8 @@ def get_jids(): def get_minions(): - ''' + """ Return a list of minions - ''' + """ serv = _get_serv(ret=None) - return _get_list(serv, 'minions') + return _get_list(serv, "minions") diff --git a/salt/returners/mongo_future_return.py b/salt/returners/mongo_future_return.py index b9e13ac6a6e..05f61df6989 100644 --- a/salt/returners/mongo_future_return.py +++ b/salt/returners/mongo_future_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a mongodb server Required python modules: pymongo @@ -91,21 +91,23 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return mongo --return_kwargs '{"db": "another-salt"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging +import salt.returners + # Import Salt libs import salt.utils.jid -import salt.returners from salt.ext import six from salt.utils.versions import LooseVersion as _LooseVersion # Import third party libs try: import pymongo + PYMONGO_VERSION = _LooseVersion(pymongo.version) HAS_PYMONGO = True except ImportError: @@ -114,78 +116,80 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'mongo' +__virtualname__ = "mongo" def __virtual__(): if not HAS_PYMONGO: - return False, 'Could not import mongo returner; pymongo is not installed.' + return False, "Could not import mongo returner; pymongo is not installed." return __virtualname__ def _remove_dots(src): - ''' + """ Remove the dots from the given data structure - ''' + """ output = {} for key, val in six.iteritems(src): if isinstance(val, dict): val = _remove_dots(val) - output[key.replace('.', '-')] = val + output[key.replace(".", "-")] = val return output def _get_options(ret=None): - ''' + """ Get the mongo options from salt. - ''' - attrs = {'host': 'host', - 'port': 'port', - 'db': 'db', - 'user': 'user', - 'password': 'password', - 'indexes': 'indexes', - 'uri': 'uri'} + """ + attrs = { + "host": "host", + "port": "port", + "db": "db", + "user": "user", + "password": "password", + "indexes": "indexes", + "uri": "uri", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options def _get_conn(ret): - ''' + """ Return a mongodb connection object - ''' + """ _options = _get_options(ret) - host = _options.get('host') - port = _options.get('port') - uri = _options.get('uri') - db_ = _options.get('db') - user = _options.get('user') - password = _options.get('password') - indexes = _options.get('indexes', False) + host = _options.get("host") + port = _options.get("port") + uri = _options.get("uri") + db_ = _options.get("db") + user = _options.get("user") + password = _options.get("password") + indexes = _options.get("indexes", False) # at some point we should remove support for # pymongo versions < 2.3 until then there are # a bunch of these sections that need to be supported - if uri and PYMONGO_VERSION > _LooseVersion('2.3'): + if uri and PYMONGO_VERSION > _LooseVersion("2.3"): if uri and host: raise salt.exceptions.SaltConfigurationError( - "Mongo returner expects either uri or host configuration. Both were provided") + "Mongo returner expects either uri or host configuration. Both were provided" + ) pymongo.uri_parser.parse_uri(uri) conn = pymongo.MongoClient(uri) mdb = conn.get_database() else: - if PYMONGO_VERSION > _LooseVersion('2.3'): + if PYMONGO_VERSION > _LooseVersion("2.3"): conn = pymongo.MongoClient(host, port) else: if uri: raise salt.exceptions.SaltConfigurationError( - "pymongo <= 2.3 does not support uri format") + "pymongo <= 2.3 does not support uri format" + ) conn = pymongo.Connection(host, port) mdb = conn[db_] @@ -193,30 +197,30 @@ def _get_conn(ret): mdb.authenticate(user, password) if indexes: - if PYMONGO_VERSION > _LooseVersion('2.3'): - mdb.saltReturns.create_index('minion') - mdb.saltReturns.create_index('jid') - mdb.jobs.create_index('jid') - mdb.events.create_index('tag') + if PYMONGO_VERSION > _LooseVersion("2.3"): + mdb.saltReturns.create_index("minion") + mdb.saltReturns.create_index("jid") + mdb.jobs.create_index("jid") + mdb.events.create_index("tag") else: - mdb.saltReturns.ensure_index('minion') - mdb.saltReturns.ensure_index('jid') - mdb.jobs.ensure_index('jid') - mdb.events.ensure_index('tag') + mdb.saltReturns.ensure_index("minion") + mdb.saltReturns.ensure_index("jid") + mdb.jobs.ensure_index("jid") + mdb.events.ensure_index("tag") return conn, mdb def returner(ret): - ''' + """ Return data to a mongodb server - ''' + """ conn, mdb = _get_conn(ret) - if isinstance(ret['return'], dict): - back = _remove_dots(ret['return']) + if isinstance(ret["return"], dict): + back = _remove_dots(ret["return"]) else: - back = ret['return'] + back = ret["return"] if isinstance(ret, dict): full_ret = _remove_dots(ret) @@ -224,9 +228,15 @@ def returner(ret): full_ret = ret log.debug(back) - sdata = {'minion': ret['id'], 'jid': ret['jid'], 'return': back, 'fun': ret['fun'], 'full_ret': full_ret} - if 'out' in ret: - sdata['out'] = ret['out'] + sdata = { + "minion": ret["id"], + "jid": ret["jid"], + "return": back, + "fun": ret["fun"], + "full_ret": full_ret, + } + if "out" in ret: + sdata["out"] = ret["out"] # save returns in the saltReturns collection in the json format: # { 'minion': <minion_name>, 'jid': <job_id>, 'return': <return info with dots removed>, @@ -234,15 +244,15 @@ def returner(ret): # # again we run into the issue with deprecated code from previous versions - if PYMONGO_VERSION > _LooseVersion('2.3'): - #using .copy() to ensure that the original data is not changed, raising issue with pymongo team + if PYMONGO_VERSION > _LooseVersion("2.3"): + # using .copy() to ensure that the original data is not changed, raising issue with pymongo team mdb.saltReturns.insert_one(sdata.copy()) else: mdb.saltReturns.insert(sdata.copy()) def _safe_copy(dat): - ''' mongodb doesn't allow '.' in keys, but does allow unicode equivs. + """ mongodb doesn't allow '.' in keys, but does allow unicode equivs. Apparently the docs suggest using escaped unicode full-width encodings. *sigh* @@ -260,14 +270,19 @@ def _safe_copy(dat): Which means also escaping '%': % -> %25 - ''' + """ if isinstance(dat, dict): ret = {} for k in dat: - r = k.replace('%', '%25').replace('\\', '%5c').replace('$', '%24').replace('.', '%2e') + r = ( + k.replace("%", "%25") + .replace("\\", "%5c") + .replace("$", "%24") + .replace(".", "%2e") + ) if r != k: - log.debug('converting dict key from %s to %s for mongodb', k, r) + log.debug("converting dict key from %s to %s for mongodb", k, r) ret[r] = _safe_copy(dat[k]) return ret @@ -278,97 +293,97 @@ def _safe_copy(dat): def save_load(jid, load, minions=None): - ''' + """ Save the load for a given job id - ''' + """ conn, mdb = _get_conn(ret=None) to_save = _safe_copy(load) - if PYMONGO_VERSION > _LooseVersion('2.3'): - #using .copy() to ensure original data for load is unchanged + if PYMONGO_VERSION > _LooseVersion("2.3"): + # using .copy() to ensure original data for load is unchanged mdb.jobs.insert_one(to_save) else: mdb.jobs.insert(to_save) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load associated with a given job id - ''' + """ conn, mdb = _get_conn(ret=None) - return mdb.jobs.find_one({'jid': jid}, {'_id': 0}) + return mdb.jobs.find_one({"jid": jid}, {"_id": 0}) def get_jid(jid): - ''' + """ Return the return information associated with a jid - ''' + """ conn, mdb = _get_conn(ret=None) ret = {} - rdata = mdb.saltReturns.find({'jid': jid}, {'_id': 0}) + rdata = mdb.saltReturns.find({"jid": jid}, {"_id": 0}) if rdata: for data in rdata: - minion = data['minion'] + minion = data["minion"] # return data in the format {<minion>: { <unformatted full return data>}} - ret[minion] = data['full_ret'] + ret[minion] = data["full_ret"] return ret def get_fun(fun): - ''' + """ Return the most recent jobs that have executed the named function - ''' + """ conn, mdb = _get_conn(ret=None) ret = {} - rdata = mdb.saltReturns.find_one({'fun': fun}, {'_id': 0}) + rdata = mdb.saltReturns.find_one({"fun": fun}, {"_id": 0}) if rdata: ret = rdata return ret def get_minions(): - ''' + """ Return a list of minions - ''' + """ conn, mdb = _get_conn(ret=None) ret = [] - name = mdb.saltReturns.distinct('minion') + name = mdb.saltReturns.distinct("minion") ret.append(name) return ret def get_jids(): - ''' + """ Return a list of job ids - ''' + """ conn, mdb = _get_conn(ret=None) map = "function() { emit(this.jid, this); }" reduce = "function (key, values) { return values[0]; }" result = mdb.jobs.inline_map_reduce(map, reduce) ret = {} for r in result: - jid = r['_id'] - ret[jid] = salt.utils.jid.format_jid_instance(jid, r['value']) + jid = r["_id"] + ret[jid] = salt.utils.jid.format_jid_instance(jid, r["value"]) return ret def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) def event_return(events): - ''' + """ Return events to Mongodb server - ''' + """ conn, mdb = _get_conn(ret=None) if isinstance(events, list): @@ -377,7 +392,7 @@ def event_return(events): if isinstance(events, dict): log.debug(events) - if PYMONGO_VERSION > _LooseVersion('2.3'): + if PYMONGO_VERSION > _LooseVersion("2.3"): mdb.events.insert_one(events.copy()) else: mdb.events.insert(events.copy()) diff --git a/salt/returners/mongo_return.py b/salt/returners/mongo_return.py index c8b9e6ebc0d..3a9c3808012 100644 --- a/salt/returners/mongo_return.py +++ b/salt/returners/mongo_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a mongodb server Required python modules: pymongo @@ -59,21 +59,23 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return mongo --return_kwargs '{"db": "another-salt"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging +import salt.returners + # import Salt libs import salt.utils.jid -import salt.returners from salt.ext import six from salt.utils.versions import LooseVersion as _LooseVersion # Import third party libs try: import pymongo + PYMONGO_VERSION = _LooseVersion(pymongo.version) HAS_PYMONGO = True except ImportError: @@ -84,64 +86,64 @@ log = logging.getLogger(__name__) # Define the module's virtual name # currently only used iby _get_options -__virtualname__ = 'mongo' +__virtualname__ = "mongo" def __virtual__(): if not HAS_PYMONGO: - return False, 'Could not import mongo returner; pymongo is not installed.' - return 'mongo_return' + return False, "Could not import mongo returner; pymongo is not installed." + return "mongo_return" def _remove_dots(src): - ''' + """ Remove dots from the given data structure - ''' + """ output = {} for key, val in six.iteritems(src): if isinstance(val, dict): val = _remove_dots(val) - output[key.replace('.', '-')] = val + output[key.replace(".", "-")] = val return output def _get_options(ret): - ''' + """ Get the monogo_return options from salt. - ''' - attrs = {'host': 'host', - 'port': 'port', - 'db': 'db', - 'user': 'user', - 'password': 'password', - 'indexes': 'indexes'} + """ + attrs = { + "host": "host", + "port": "port", + "db": "db", + "user": "user", + "password": "password", + "indexes": "indexes", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options def _get_conn(ret): - ''' + """ Return a mongodb connection object - ''' + """ _options = _get_options(ret) - host = _options.get('host') - port = _options.get('port') - db_ = _options.get('db') - user = _options.get('user') - password = _options.get('password') - indexes = _options.get('indexes', False) + host = _options.get("host") + port = _options.get("port") + db_ = _options.get("db") + user = _options.get("user") + password = _options.get("password") + indexes = _options.get("indexes", False) # at some point we should remove support for # pymongo versions < 2.3 until then there are # a bunch of these sections that need to be supported - if PYMONGO_VERSION > _LooseVersion('2.3'): + if PYMONGO_VERSION > _LooseVersion("2.3"): conn = pymongo.MongoClient(host, port) else: conn = pymongo.Connection(host, port) @@ -151,31 +153,31 @@ def _get_conn(ret): mdb.authenticate(user, password) if indexes: - if PYMONGO_VERSION > _LooseVersion('2.3'): - mdb.saltReturns.create_index('minion') - mdb.saltReturns.create_index('jid') + if PYMONGO_VERSION > _LooseVersion("2.3"): + mdb.saltReturns.create_index("minion") + mdb.saltReturns.create_index("jid") - mdb.jobs.create_index('jid') + mdb.jobs.create_index("jid") else: - mdb.saltReturns.ensure_index('minion') - mdb.saltReturns.ensure_index('jid') + mdb.saltReturns.ensure_index("minion") + mdb.saltReturns.ensure_index("jid") - mdb.jobs.ensure_index('jid') + mdb.jobs.ensure_index("jid") return conn, mdb def returner(ret): - ''' + """ Return data to a mongodb server - ''' + """ conn, mdb = _get_conn(ret) - col = mdb[ret['id']] + col = mdb[ret["id"]] - if isinstance(ret['return'], dict): - back = _remove_dots(ret['return']) + if isinstance(ret["return"], dict): + back = _remove_dots(ret["return"]) else: - back = ret['return'] + back = ret["return"] if isinstance(ret, dict): full_ret = _remove_dots(ret) @@ -183,9 +185,15 @@ def returner(ret): full_ret = ret log.debug(back) - sdata = {'minion': ret['id'], 'jid': ret['jid'], 'return': back, 'fun': ret['fun'], 'full_ret': full_ret} - if 'out' in ret: - sdata['out'] = ret['out'] + sdata = { + "minion": ret["id"], + "jid": ret["jid"], + "return": back, + "fun": ret["fun"], + "full_ret": full_ret, + } + if "out" in ret: + sdata["out"] = ret["out"] # save returns in the saltReturns collection in the json format: # { 'minion': <minion_name>, 'jid': <job_id>, 'return': <return info with dots removed>, @@ -193,48 +201,48 @@ def returner(ret): # again we run into the issue with deprecated code from previous versions - if PYMONGO_VERSION > _LooseVersion('2.3'): - #using .copy() to ensure original data for load is unchanged + if PYMONGO_VERSION > _LooseVersion("2.3"): + # using .copy() to ensure original data for load is unchanged mdb.saltReturns.insert_one(sdata.copy()) else: mdb.saltReturns.insert(sdata.copy()) def get_jid(jid): - ''' + """ Return the return information associated with a jid - ''' + """ conn, mdb = _get_conn(ret=None) ret = {} - rdata = mdb.saltReturns.find({'jid': jid}, {'_id': 0}) + rdata = mdb.saltReturns.find({"jid": jid}, {"_id": 0}) if rdata: for data in rdata: - minion = data['minion'] + minion = data["minion"] # return data in the format {<minion>: { <unformatted full return data>}} - ret[minion] = data['full_ret'] + ret[minion] = data["full_ret"] return ret def get_fun(fun): - ''' + """ Return the most recent jobs that have executed the named function - ''' + """ conn, mdb = _get_conn(ret=None) ret = {} - rdata = mdb.saltReturns.find_one({'fun': fun}, {'_id': 0}) + rdata = mdb.saltReturns.find_one({"fun": fun}, {"_id": 0}) if rdata: ret = rdata return ret def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ diff --git a/salt/returners/multi_returner.py b/salt/returners/multi_returner.py index 23cf6dfed6f..29145b209d9 100644 --- a/salt/returners/multi_returner.py +++ b/salt/returners/multi_returner.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Read/Write multiple returners -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -13,16 +14,16 @@ import salt.minion log = logging.getLogger(__name__) -CONFIG_KEY = 'multi_returner' +CONFIG_KEY = "multi_returner" # cache of the master mininon for this returner MMINION = None def _mminion(): - ''' + """ Create a single mminion for this module to use, instead of reloading all the time - ''' + """ global MMINION if MMINION is None: @@ -32,7 +33,7 @@ def _mminion(): def prep_jid(nocache=False, passed_jid=None): - ''' + """ Call both with prep_jid on all returners in multi_returner TODO: finish this, what do do when you get different jids from 2 returners... @@ -40,79 +41,83 @@ def prep_jid(nocache=False, passed_jid=None): aren't unique, meaning that we have to make sure that no one else got the jid and if they did we spin to get a new one, which means "locking" the jid in 2 returners is non-trivial - ''' + """ jid = passed_jid for returner_ in __opts__[CONFIG_KEY]: if jid is None: - jid = _mminion().returners['{0}.prep_jid'.format(returner_)](nocache=nocache) + jid = _mminion().returners["{0}.prep_jid".format(returner_)]( + nocache=nocache + ) else: - r_jid = _mminion().returners['{0}.prep_jid'.format(returner_)](nocache=nocache) + r_jid = _mminion().returners["{0}.prep_jid".format(returner_)]( + nocache=nocache + ) if r_jid != jid: - log.debug('Uhh.... crud the jids do not match') + log.debug("Uhh.... crud the jids do not match") return jid def returner(load): - ''' + """ Write return to all returners in multi_returner - ''' + """ for returner_ in __opts__[CONFIG_KEY]: - _mminion().returners['{0}.returner'.format(returner_)](load) + _mminion().returners["{0}.returner".format(returner_)](load) def save_load(jid, clear_load, minions=None): - ''' + """ Write load to all returners in multi_returner - ''' + """ for returner_ in __opts__[CONFIG_KEY]: - _mminion().returners['{0}.save_load'.format(returner_)](jid, clear_load) + _mminion().returners["{0}.save_load".format(returner_)](jid, clear_load) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Merge the load data from all returners - ''' + """ ret = {} for returner_ in __opts__[CONFIG_KEY]: - ret.update(_mminion().returners['{0}.get_load'.format(returner_)](jid)) + ret.update(_mminion().returners["{0}.get_load".format(returner_)](jid)) return ret def get_jid(jid): - ''' + """ Merge the return data from all returners - ''' + """ ret = {} for returner_ in __opts__[CONFIG_KEY]: - ret.update(_mminion().returners['{0}.get_jid'.format(returner_)](jid)) + ret.update(_mminion().returners["{0}.get_jid".format(returner_)](jid)) return ret def get_jids(): - ''' + """ Return all job data from all returners - ''' + """ ret = {} for returner_ in __opts__[CONFIG_KEY]: - ret.update(_mminion().returners['{0}.get_jids'.format(returner_)]()) + ret.update(_mminion().returners["{0}.get_jids".format(returner_)]()) return ret def clean_old_jobs(): - ''' + """ Clean out the old jobs from all returners (if you have it) - ''' + """ for returner_ in __opts__[CONFIG_KEY]: - fstr = '{0}.clean_old_jobs'.format(returner_) + fstr = "{0}.clean_old_jobs".format(returner_) if fstr in _mminion().returners: _mminion().returners[fstr]() diff --git a/salt/returners/mysql.py b/salt/returners/mysql.py index 69599ec36aa..b7bb05164fb 100644 --- a/salt/returners/mysql.py +++ b/salt/returners/mysql.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a mysql server :maintainer: Dave Boucha <dave@saltstack.com>, Seth House <shouse@saltstack.com> @@ -137,25 +137,29 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return mysql --return_kwargs '{"db": "another-salt"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Let's not allow PyLint complain about string substitution -# pylint: disable=W1321,E1321 + +import logging +import sys # Import python libs from contextlib import contextmanager -import sys -import logging + +import salt.exceptions # Import salt libs import salt.returners import salt.utils.jid import salt.utils.json -import salt.exceptions # Import 3rd-party libs from salt.ext import six +# Let's not allow PyLint complain about string substitution +# pylint: disable=W1321,E1321 + + try: # Trying to import MySQLdb import MySQLdb @@ -166,6 +170,7 @@ except ImportError: try: # MySQLdb import failed, try to import PyMySQL import pymysql + pymysql.install_as_MySQLdb() import MySQLdb import MySQLdb.cursors @@ -177,50 +182,56 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'mysql' +__virtualname__ = "mysql" def __virtual__(): - ''' + """ Confirm that a python mysql client is installed. - ''' - return bool(MySQLdb), 'No python mysql client installed.' if MySQLdb is None else '' + """ + return bool(MySQLdb), "No python mysql client installed." if MySQLdb is None else "" def _get_options(ret=None): - ''' + """ Returns options used for the MySQL connection. - ''' - defaults = {'host': 'salt', - 'user': 'salt', - 'pass': 'salt', - 'db': 'salt', - 'port': 3306, - 'ssl_ca': None, - 'ssl_cert': None, - 'ssl_key': None} + """ + defaults = { + "host": "salt", + "user": "salt", + "pass": "salt", + "db": "salt", + "port": 3306, + "ssl_ca": None, + "ssl_cert": None, + "ssl_key": None, + } - attrs = {'host': 'host', - 'user': 'user', - 'pass': 'pass', - 'db': 'db', - 'port': 'port', - 'ssl_ca': 'ssl_ca', - 'ssl_cert': 'ssl_cert', - 'ssl_key': 'ssl_key'} + attrs = { + "host": "host", + "user": "user", + "pass": "pass", + "db": "db", + "port": "port", + "ssl_ca": "ssl_ca", + "ssl_cert": "ssl_cert", + "ssl_key": "ssl_key", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) + _options = salt.returners.get_returner_options( + __virtualname__, + ret, + attrs, + __salt__=__salt__, + __opts__=__opts__, + defaults=defaults, + ) # post processing for k, v in six.iteritems(_options): - if isinstance(v, six.string_types) and v.lower() == 'none': + if isinstance(v, six.string_types) and v.lower() == "none": # Ensure 'None' is rendered as None _options[k] = None - if k == 'port': + if k == "port": # Ensure port is an int _options[k] = int(v) @@ -229,46 +240,50 @@ def _get_options(ret=None): @contextmanager def _get_serv(ret=None, commit=False): - ''' + """ Return a mysql cursor - ''' + """ _options = _get_options(ret) connect = True - if __context__ and 'mysql_returner_conn' in __context__: + if __context__ and "mysql_returner_conn" in __context__: try: - log.debug('Trying to reuse MySQL connection pool') - conn = __context__['mysql_returner_conn'] + log.debug("Trying to reuse MySQL connection pool") + conn = __context__["mysql_returner_conn"] conn.ping() connect = False except OperationalError as exc: - log.debug('OperationalError on ping: %s', exc) + log.debug("OperationalError on ping: %s", exc) if connect: - log.debug('Generating new MySQL connection pool') + log.debug("Generating new MySQL connection pool") try: # An empty ssl_options dictionary passed to MySQLdb.connect will # effectively connect w/o SSL. ssl_options = {} - if _options.get('ssl_ca'): - ssl_options['ca'] = _options.get('ssl_ca') - if _options.get('ssl_cert'): - ssl_options['cert'] = _options.get('ssl_cert') - if _options.get('ssl_key'): - ssl_options['key'] = _options.get('ssl_key') - conn = MySQLdb.connect(host=_options.get('host'), - user=_options.get('user'), - passwd=_options.get('pass'), - db=_options.get('db'), - port=_options.get('port'), - ssl=ssl_options) + if _options.get("ssl_ca"): + ssl_options["ca"] = _options.get("ssl_ca") + if _options.get("ssl_cert"): + ssl_options["cert"] = _options.get("ssl_cert") + if _options.get("ssl_key"): + ssl_options["key"] = _options.get("ssl_key") + conn = MySQLdb.connect( + host=_options.get("host"), + user=_options.get("user"), + passwd=_options.get("pass"), + db=_options.get("db"), + port=_options.get("port"), + ssl=ssl_options, + ) try: - __context__['mysql_returner_conn'] = conn + __context__["mysql_returner_conn"] = conn except TypeError: pass except OperationalError as exc: - raise salt.exceptions.SaltMasterError('MySQL returner could not connect to database: {exc}'.format(exc=exc)) + raise salt.exceptions.SaltMasterError( + "MySQL returner could not connect to database: {exc}".format(exc=exc) + ) cursor = conn.cursor() @@ -287,53 +302,61 @@ def _get_serv(ret=None, commit=False): def returner(ret): - ''' + """ Return data to a mysql server - ''' + """ # if a minion is returning a standalone job, get a jobid - if ret['jid'] == 'req': - ret['jid'] = prep_jid(nocache=ret.get('nocache', False)) - save_load(ret['jid'], ret) + if ret["jid"] == "req": + ret["jid"] = prep_jid(nocache=ret.get("nocache", False)) + save_load(ret["jid"], ret) try: with _get_serv(ret, commit=True) as cur: - sql = '''INSERT INTO `salt_returns` + sql = """INSERT INTO `salt_returns` (`fun`, `jid`, `return`, `id`, `success`, `full_ret`) - VALUES (%s, %s, %s, %s, %s, %s)''' + VALUES (%s, %s, %s, %s, %s, %s)""" - cur.execute(sql, (ret['fun'], ret['jid'], - salt.utils.json.dumps(ret['return']), - ret['id'], - ret.get('success', False), - salt.utils.json.dumps(ret))) + cur.execute( + sql, + ( + ret["fun"], + ret["jid"], + salt.utils.json.dumps(ret["return"]), + ret["id"], + ret.get("success", False), + salt.utils.json.dumps(ret), + ), + ) except salt.exceptions.SaltMasterError as exc: log.critical(exc) - log.critical('Could not store return with MySQL returner. MySQL server unavailable.') + log.critical( + "Could not store return with MySQL returner. MySQL server unavailable." + ) def event_return(events): - ''' + """ Return event to mysql server Requires that configuration be enabled via 'event_return' option in master config. - ''' + """ with _get_serv(events, commit=True) as cur: for event in events: - tag = event.get('tag', '') - data = event.get('data', '') - sql = '''INSERT INTO `salt_events` (`tag`, `data`, `master_id`) - VALUES (%s, %s, %s)''' - cur.execute(sql, (tag, salt.utils.json.dumps(data), __opts__['id'])) + tag = event.get("tag", "") + data = event.get("data", "") + sql = """INSERT INTO `salt_events` (`tag`, `data`, `master_id`) + VALUES (%s, %s, %s)""" + cur.execute(sql, (tag, salt.utils.json.dumps(data), __opts__["id"])) def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid id - ''' + """ with _get_serv(commit=True) as cur: - sql = '''INSERT INTO `jids` (`jid`, `load`) VALUES (%s, %s)''' + sql = """INSERT INTO `jids` (`jid`, `load`) VALUES (%s, %s)""" try: cur.execute(sql, (jid, salt.utils.json.dumps(load))) @@ -345,18 +368,18 @@ def save_load(jid, load, minions=None): def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT `load` FROM `jids` WHERE `jid` = %s;''' + sql = """SELECT `load` FROM `jids` WHERE `jid` = %s;""" cur.execute(sql, (jid,)) data = cur.fetchone() if data: @@ -365,13 +388,13 @@ def get_load(jid): def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT id, full_ret FROM `salt_returns` - WHERE `jid` = %s''' + sql = """SELECT id, full_ret FROM `salt_returns` + WHERE `jid` = %s""" cur.execute(sql, (jid,)) data = cur.fetchall() @@ -383,18 +406,18 @@ def get_jid(jid): def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT s.id,s.jid, s.full_ret + sql = """SELECT s.id,s.jid, s.full_ret FROM `salt_returns` s JOIN ( SELECT MAX(`jid`) as jid from `salt_returns` GROUP BY fun, id) max ON s.jid = max.jid WHERE s.fun = %s - ''' + """ cur.execute(sql, (fun,)) data = cur.fetchall() @@ -407,58 +430,60 @@ def get_fun(fun): def get_jids(): - ''' + """ Return a list of all job ids - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT DISTINCT `jid`, `load` - FROM `jids`''' + sql = """SELECT DISTINCT `jid`, `load` + FROM `jids`""" cur.execute(sql) data = cur.fetchall() ret = {} for jid in data: ret[jid[0]] = salt.utils.jid.format_jid_instance( - jid[0], - salt.utils.json.loads(jid[1])) + jid[0], salt.utils.json.loads(jid[1]) + ) return ret def get_jids_filter(count, filter_find_job=True): - ''' + """ Return a list of all job ids :param int count: show not more than the count of most recent jobs :param bool filter_find_jobs: filter out 'saltutil.find_job' jobs - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT * FROM ( + sql = """SELECT * FROM ( SELECT DISTINCT `jid` ,`load` FROM `jids` {0} ORDER BY `jid` DESC limit {1} ) `tmp` - ORDER BY `jid`;''' - where = '''WHERE `load` NOT LIKE '%"fun": "saltutil.find_job"%' ''' + ORDER BY `jid`;""" + where = """WHERE `load` NOT LIKE '%"fun": "saltutil.find_job"%' """ - cur.execute(sql.format(where if filter_find_job else '', count)) + cur.execute(sql.format(where if filter_find_job else "", count)) data = cur.fetchall() ret = [] for jid in data: - ret.append(salt.utils.jid.format_jid_instance_ext( - jid[0], - salt.utils.json.loads(jid[1]))) + ret.append( + salt.utils.jid.format_jid_instance_ext( + jid[0], salt.utils.json.loads(jid[1]) + ) + ) return ret def get_minions(): - ''' + """ Return a list of minions - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT DISTINCT id - FROM `salt_returns`''' + sql = """SELECT DISTINCT id + FROM `salt_returns`""" cur.execute(sql) data = cur.fetchall() @@ -469,43 +494,49 @@ def get_minions(): def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) def _purge_jobs(timestamp): - ''' + """ Purge records from the returner tables. :param job_age_in_seconds: Purge jobs older than this :return: - ''' + """ with _get_serv() as cur: try: - sql = 'delete from `jids` where jid in (select distinct jid from salt_returns where alter_time < %s)' + sql = "delete from `jids` where jid in (select distinct jid from salt_returns where alter_time < %s)" cur.execute(sql, (timestamp,)) - cur.execute('COMMIT') + cur.execute("COMMIT") except MySQLdb.Error as e: - log.error('mysql returner archiver was unable to delete contents of table \'jids\'') + log.error( + "mysql returner archiver was unable to delete contents of table 'jids'" + ) log.error(six.text_type(e)) raise salt.exceptions.SaltRunnerError(six.text_type(e)) try: - sql = 'delete from `salt_returns` where alter_time < %s' + sql = "delete from `salt_returns` where alter_time < %s" cur.execute(sql, (timestamp,)) - cur.execute('COMMIT') + cur.execute("COMMIT") except MySQLdb.Error as e: - log.error('mysql returner archiver was unable to delete contents of table \'salt_returns\'') + log.error( + "mysql returner archiver was unable to delete contents of table 'salt_returns'" + ) log.error(six.text_type(e)) raise salt.exceptions.SaltRunnerError(six.text_type(e)) try: - sql = 'delete from `salt_events` where alter_time < %s' + sql = "delete from `salt_events` where alter_time < %s" cur.execute(sql, (timestamp,)) - cur.execute('COMMIT') + cur.execute("COMMIT") except MySQLdb.Error as e: - log.error('mysql returner archiver was unable to delete contents of table \'salt_events\'') + log.error( + "mysql returner archiver was unable to delete contents of table 'salt_events'" + ) log.error(six.text_type(e)) raise salt.exceptions.SaltRunnerError(six.text_type(e)) @@ -513,35 +544,41 @@ def _purge_jobs(timestamp): def _archive_jobs(timestamp): - ''' + """ Copy rows to a set of backup tables, then purge rows. :param timestamp: Archive rows older than this timestamp :return: - ''' - source_tables = ['jids', - 'salt_returns', - 'salt_events'] + """ + source_tables = ["jids", "salt_returns", "salt_events"] with _get_serv() as cur: target_tables = {} for table_name in source_tables: try: - tmp_table_name = table_name + '_archive' - sql = 'create table if not exists {0} like {1}'.format(tmp_table_name, table_name) + tmp_table_name = table_name + "_archive" + sql = "create table if not exists {0} like {1}".format( + tmp_table_name, table_name + ) cur.execute(sql) - cur.execute('COMMIT') + cur.execute("COMMIT") target_tables[table_name] = tmp_table_name except MySQLdb.Error as e: - log.error('mysql returner archiver was unable to create the archive tables.') + log.error( + "mysql returner archiver was unable to create the archive tables." + ) log.error(six.text_type(e)) raise salt.exceptions.SaltRunnerError(six.text_type(e)) try: - sql = 'insert into `{0}` select * from `{1}` where jid in (select distinct jid from salt_returns where alter_time < %s)'.format(target_tables['jids'], 'jids') + sql = "insert into `{0}` select * from `{1}` where jid in (select distinct jid from salt_returns where alter_time < %s)".format( + target_tables["jids"], "jids" + ) cur.execute(sql, (timestamp,)) - cur.execute('COMMIT') + cur.execute("COMMIT") except MySQLdb.Error as e: - log.error('mysql returner archiver was unable to copy contents of table \'jids\'') + log.error( + "mysql returner archiver was unable to copy contents of table 'jids'" + ) log.error(six.text_type(e)) raise salt.exceptions.SaltRunnerError(six.text_type(e)) except Exception as e: # pylint: disable=broad-except @@ -549,20 +586,28 @@ def _archive_jobs(timestamp): raise try: - sql = 'insert into `{0}` select * from `{1}` where alter_time < %s'.format(target_tables['salt_returns'], 'salt_returns') + sql = "insert into `{0}` select * from `{1}` where alter_time < %s".format( + target_tables["salt_returns"], "salt_returns" + ) cur.execute(sql, (timestamp,)) - cur.execute('COMMIT') + cur.execute("COMMIT") except MySQLdb.Error as e: - log.error('mysql returner archiver was unable to copy contents of table \'salt_returns\'') + log.error( + "mysql returner archiver was unable to copy contents of table 'salt_returns'" + ) log.error(six.text_type(e)) raise salt.exceptions.SaltRunnerError(six.text_type(e)) try: - sql = 'insert into `{0}` select * from `{1}` where alter_time < %s'.format(target_tables['salt_events'], 'salt_events') + sql = "insert into `{0}` select * from `{1}` where alter_time < %s".format( + target_tables["salt_events"], "salt_events" + ) cur.execute(sql, (timestamp,)) - cur.execute('COMMIT') + cur.execute("COMMIT") except MySQLdb.Error as e: - log.error('mysql returner archiver was unable to copy contents of table \'salt_events\'') + log.error( + "mysql returner archiver was unable to copy contents of table 'salt_events'" + ) log.error(six.text_type(e)) raise salt.exceptions.SaltRunnerError(six.text_type(e)) @@ -570,24 +615,28 @@ def _archive_jobs(timestamp): def clean_old_jobs(): - ''' + """ Called in the master's event loop every loop_interval. Archives and/or deletes the events and job details from the database. :return: - ''' - if __opts__.get('keep_jobs', False) and int(__opts__.get('keep_jobs', 0)) > 0: + """ + if __opts__.get("keep_jobs", False) and int(__opts__.get("keep_jobs", 0)) > 0: try: with _get_serv() as cur: - sql = 'select date_sub(now(), interval {0} hour) as stamp;'.format(__opts__['keep_jobs']) + sql = "select date_sub(now(), interval {0} hour) as stamp;".format( + __opts__["keep_jobs"] + ) cur.execute(sql) rows = cur.fetchall() stamp = rows[0][0] - if __opts__.get('archive_jobs', False): + if __opts__.get("archive_jobs", False): _archive_jobs(stamp) else: _purge_jobs(stamp) except MySQLdb.Error as e: - log.error('Mysql returner was unable to get timestamp for purge/archive of jobs') + log.error( + "Mysql returner was unable to get timestamp for purge/archive of jobs" + ) log.error(six.text_type(e)) raise salt.exceptions.SaltRunnerError(six.text_type(e)) diff --git a/salt/returners/nagios_nrdp_return.py b/salt/returners/nagios_nrdp_return.py index 8b9b104de76..bd608734fb5 100644 --- a/salt/returners/nagios_nrdp_return.py +++ b/salt/returners/nagios_nrdp_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data to Nagios The following fields can be set in the minion conf file:: @@ -47,89 +47,98 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return nagios --return_kwargs '{"service": "service-name"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import cgi import logging +import salt.ext.six.moves.http_client import salt.returners + # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six -import salt.ext.six.moves.http_client + # pylint: enable=import-error,no-name-in-module,redefined-builtin log = logging.getLogger(__name__) -__virtualname__ = 'nagios_nrdp' +__virtualname__ = "nagios_nrdp" def __virtual__(): - ''' + """ Return virtualname - ''' - return 'nagios.list_plugins' in __salt__ + """ + return "nagios.list_plugins" in __salt__ def _get_options(ret=None): - ''' + """ Get the requests options from salt. - ''' - attrs = {'url': 'url', - 'token': 'token', - 'service': 'service', - 'checktype': 'checktype', - } + """ + attrs = { + "url": "url", + "token": "token", + "service": "service", + "checktype": "checktype", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) - log.debug('attrs %s', attrs) - if 'checktype' not in _options or _options['checktype'] == '': + log.debug("attrs %s", attrs) + if "checktype" not in _options or _options["checktype"] == "": # default to passive check type - _options['checktype'] = '1' + _options["checktype"] = "1" - if _options['checktype'] == 'active': - _options['checktype'] = '0' + if _options["checktype"] == "active": + _options["checktype"] = "0" - if _options['checktype'] == 'passive': - _options['checktype'] = '1' + if _options["checktype"] == "passive": + _options["checktype"] = "1" # checktype should be a string - _options['checktype'] = six.text_type(_options['checktype']) + _options["checktype"] = six.text_type(_options["checktype"]) return _options def _prepare_xml(options=None, state=None): - ''' + """ Get the requests options from salt. - ''' + """ if state: - _state = '0' + _state = "0" else: - _state = '2' + _state = "2" xml = "<?xml version='1.0'?>\n<checkresults>\n" # No service defined then we set the status of the hostname - if 'service' in options and options['service'] != '': - xml += "<checkresult type='service' checktype='" + six.text_type(options['checktype'])+"'>" - xml += "<hostname>"+cgi.escape(options['hostname'], True)+"</hostname>" - xml += "<servicename>"+cgi.escape(options['service'], True)+"</servicename>" + if "service" in options and options["service"] != "": + xml += ( + "<checkresult type='service' checktype='" + + six.text_type(options["checktype"]) + + "'>" + ) + xml += "<hostname>" + cgi.escape(options["hostname"], True) + "</hostname>" + xml += "<servicename>" + cgi.escape(options["service"], True) + "</servicename>" else: - xml += "<checkresult type='host' checktype='" + six.text_type(options['checktype'])+"'>" - xml += "<hostname>"+cgi.escape(options['hostname'], True)+"</hostname>" + xml += ( + "<checkresult type='host' checktype='" + + six.text_type(options["checktype"]) + + "'>" + ) + xml += "<hostname>" + cgi.escape(options["hostname"], True) + "</hostname>" - xml += "<state>"+_state+"</state>" + xml += "<state>" + _state + "</state>" - if 'output' in options: - xml += "<output>"+cgi.escape(options['output'], True)+"</output>" + if "output" in options: + xml += "<output>" + cgi.escape(options["output"], True) + "</output>" xml += "</checkresult>" @@ -139,69 +148,66 @@ def _prepare_xml(options=None, state=None): def _getText(nodelist): - ''' + """ Simple function to return value from XML - ''' + """ rc = [] for node in nodelist: if node.nodeType == node.TEXT_NODE: rc.append(node.data) - return ''.join(rc) + return "".join(rc) def _post_data(options=None, xml=None): - ''' + """ Post data to Nagios NRDP - ''' - params = {'token': options['token'].strip(), 'cmd': 'submitcheck', 'XMLDATA': xml} + """ + params = {"token": options["token"].strip(), "cmd": "submitcheck", "XMLDATA": xml} res = salt.utils.http.query( - url=options['url'], - method='POST', + url=options["url"], + method="POST", params=params, - data='', + data="", decode=True, status=True, header_dict={}, opts=__opts__, ) - if res.get('status', None) == salt.ext.six.moves.http_client.OK: - if res.get('dict', None) and isinstance(res['dict'], list): - _content = res['dict'][0] - if _content.get('status', None): + if res.get("status", None) == salt.ext.six.moves.http_client.OK: + if res.get("dict", None) and isinstance(res["dict"], list): + _content = res["dict"][0] + if _content.get("status", None): return True else: return False else: - log.error('No content returned from Nagios NRDP.') + log.error("No content returned from Nagios NRDP.") return False else: - log.error( - 'Error returned from Nagios NRDP. Status code: %s.', - res.status_code - ) + log.error("Error returned from Nagios NRDP. Status code: %s.", res.status_code) return False def returner(ret): - ''' + """ Send a message to Nagios with the data - ''' + """ _options = _get_options(ret) - log.debug('_options %s', _options) - _options['hostname'] = ret.get('id') + log.debug("_options %s", _options) + _options["hostname"] = ret.get("id") - if 'url' not in _options or _options['url'] == '': - log.error('nagios_nrdp.url not defined in salt config') + if "url" not in _options or _options["url"] == "": + log.error("nagios_nrdp.url not defined in salt config") return - if 'token' not in _options or _options['token'] == '': - log.error('nagios_nrdp.token not defined in salt config') + if "token" not in _options or _options["token"] == "": + log.error("nagios_nrdp.token not defined in salt config") return - xml = _prepare_xml(options=_options, state=ret['return']) + xml = _prepare_xml(options=_options, state=ret["return"]) res = _post_data(options=_options, xml=xml) return res diff --git a/salt/returners/odbc.py b/salt/returners/odbc.py index e7be6df2d95..072f574bf68 100644 --- a/salt/returners/odbc.py +++ b/salt/returners/odbc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to an ODBC compliant server. This driver was developed with Microsoft SQL Server in mind, but theoretically could be used to return data to any compliant ODBC database @@ -122,120 +122,120 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return odbc --return_kwargs '{"dsn": "dsn-name"}' -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.returners + # Import Salt libs import salt.utils.jid import salt.utils.json -import salt.returners # FIXME We'll need to handle this differently for Windows. # Import third party libs try: import pyodbc - #import psycopg2.extras + + # import psycopg2.extras HAS_ODBC = True except ImportError: HAS_ODBC = False # Define the module's virtual name -__virtualname__ = 'odbc' +__virtualname__ = "odbc" def __virtual__(): if not HAS_ODBC: - return False, 'Could not import odbc returner; pyodbc is not installed.' + return False, "Could not import odbc returner; pyodbc is not installed." return True def _get_options(ret=None): - ''' + """ Get the odbc options from salt. - ''' - attrs = {'dsn': 'dsn', - 'user': 'user', - 'passwd': 'passwd'} + """ + attrs = {"dsn": "dsn", "user": "user", "passwd": "passwd"} - _options = salt.returners.get_returner_options('returner.{0}'.format(__virtualname__), - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + "returner.{0}".format(__virtualname__), + ret, + attrs, + __salt__=__salt__, + __opts__=__opts__, + ) return _options def _get_conn(ret=None): - ''' + """ Return a MSSQL connection. - ''' + """ _options = _get_options(ret) - dsn = _options.get('dsn') - user = _options.get('user') - passwd = _options.get('passwd') + dsn = _options.get("dsn") + user = _options.get("user") + passwd = _options.get("passwd") - return pyodbc.connect('DSN={0};UID={1};PWD={2}'.format( - dsn, - user, - passwd)) + return pyodbc.connect("DSN={0};UID={1};PWD={2}".format(dsn, user, passwd)) def _close_conn(conn): - ''' + """ Close the MySQL connection - ''' + """ conn.commit() conn.close() def returner(ret): - ''' + """ Return data to an odbc server - ''' + """ conn = _get_conn(ret) cur = conn.cursor() - sql = '''INSERT INTO salt_returns + sql = """INSERT INTO salt_returns (fun, jid, retval, id, success, full_ret) - VALUES (?, ?, ?, ?, ?, ?)''' + VALUES (?, ?, ?, ?, ?, ?)""" cur.execute( - sql, ( - ret['fun'], - ret['jid'], - salt.utils.json.dumps(ret['return']), - ret['id'], - ret['success'], - salt.utils.json.dumps(ret) - ) + sql, + ( + ret["fun"], + ret["jid"], + salt.utils.json.dumps(ret["return"]), + ret["id"], + ret["success"], + salt.utils.json.dumps(ret), + ), ) _close_conn(conn) def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid id - ''' + """ conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''INSERT INTO jids (jid, load) VALUES (?, ?)''' + sql = """INSERT INTO jids (jid, load) VALUES (?, ?)""" cur.execute(sql, (jid, salt.utils.json.dumps(load))) _close_conn(conn) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' + """ conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT load FROM jids WHERE jid = ?;''' + sql = """SELECT load FROM jids WHERE jid = ?;""" cur.execute(sql, (jid,)) data = cur.fetchone() @@ -246,12 +246,12 @@ def get_load(jid): def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' + """ conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT id, full_ret FROM salt_returns WHERE jid = ?''' + sql = """SELECT id, full_ret FROM salt_returns WHERE jid = ?""" cur.execute(sql, (jid,)) data = cur.fetchall() @@ -264,17 +264,17 @@ def get_jid(jid): def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' + """ conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT s.id,s.jid, s.full_ret + sql = """SELECT s.id,s.jid, s.full_ret FROM salt_returns s JOIN ( SELECT MAX(jid) AS jid FROM salt_returns GROUP BY fun, id) max ON s.jid = max.jid WHERE s.fun = ? - ''' + """ cur.execute(sql, (fun,)) data = cur.fetchall() @@ -288,12 +288,12 @@ def get_fun(fun): def get_jids(): - ''' + """ Return a list of all job ids - ''' + """ conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT distinct jid, load FROM jids''' + sql = """SELECT distinct jid, load FROM jids""" cur.execute(sql) data = cur.fetchall() @@ -305,12 +305,12 @@ def get_jids(): def get_minions(): - ''' + """ Return a list of minions - ''' + """ conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT DISTINCT id FROM salt_returns''' + sql = """SELECT DISTINCT id FROM salt_returns""" cur.execute(sql) data = cur.fetchall() @@ -322,7 +322,7 @@ def get_minions(): def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/pgjsonb.py b/salt/returners/pgjsonb.py index e7e272ac9c9..a56fcfb13b7 100644 --- a/salt/returners/pgjsonb.py +++ b/salt/returners/pgjsonb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a PostgreSQL server with json data stored in Pg's jsonb data type :maintainer: Dave Boucha <dave@saltstack.com>, Seth House <shouse@saltstack.com>, C. R. Oldham <cr@saltstack.com> @@ -159,27 +159,32 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return pgjsonb --return_kwargs '{"db": "another-salt"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Let's not allow PyLint complain about string substitution -# pylint: disable=W1321,E1321 + +import logging +import sys +import time # Import python libs from contextlib import contextmanager -import sys -import time -import logging + +import salt.exceptions # Import salt libs import salt.returners import salt.utils.jid -import salt.exceptions from salt.ext import six +# Let's not allow PyLint complain about string substitution +# pylint: disable=W1321,E1321 + + # Import third party libs try: import psycopg2 import psycopg2.extras + HAS_PG = True except ImportError: HAS_PG = False @@ -187,85 +192,93 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pgjsonb' +__virtualname__ = "pgjsonb" -PG_SAVE_LOAD_SQL = '''INSERT INTO jids (jid, load) VALUES (%(jid)s, %(load)s)''' +PG_SAVE_LOAD_SQL = """INSERT INTO jids (jid, load) VALUES (%(jid)s, %(load)s)""" def __virtual__(): if not HAS_PG: - return False, 'Could not import pgjsonb returner; python-psycopg2 is not installed.' + return ( + False, + "Could not import pgjsonb returner; python-psycopg2 is not installed.", + ) return True def _get_options(ret=None): - ''' + """ Returns options used for the MySQL connection. - ''' + """ defaults = { - 'host': 'localhost', - 'user': 'salt', - 'pass': 'salt', - 'db': 'salt', - 'port': 5432 + "host": "localhost", + "user": "salt", + "pass": "salt", + "db": "salt", + "port": 5432, } attrs = { - 'host': 'host', - 'user': 'user', - 'pass': 'pass', - 'db': 'db', - 'port': 'port', - 'sslmode': 'sslmode', - 'sslcert': 'sslcert', - 'sslkey': 'sslkey', - 'sslrootcert': 'sslrootcert', - 'sslcrl': 'sslcrl', + "host": "host", + "user": "user", + "pass": "pass", + "db": "db", + "port": "port", + "sslmode": "sslmode", + "sslcert": "sslcert", + "sslkey": "sslkey", + "sslrootcert": "sslrootcert", + "sslcrl": "sslcrl", } - _options = salt.returners.get_returner_options('returner.{0}'.format(__virtualname__), - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) + _options = salt.returners.get_returner_options( + "returner.{0}".format(__virtualname__), + ret, + attrs, + __salt__=__salt__, + __opts__=__opts__, + defaults=defaults, + ) # Ensure port is an int - if 'port' in _options: - _options['port'] = int(_options['port']) + if "port" in _options: + _options["port"] = int(_options["port"]) return _options @contextmanager def _get_serv(ret=None, commit=False): - ''' + """ Return a Pg cursor - ''' + """ _options = _get_options(ret) try: # An empty ssl_options dictionary passed to MySQLdb.connect will # effectively connect w/o SSL. ssl_options = { - k: v for k, v in six.iteritems(_options) - if k in ['sslmode', 'sslcert', 'sslkey', 'sslrootcert', 'sslcrl'] + k: v + for k, v in six.iteritems(_options) + if k in ["sslmode", "sslcert", "sslkey", "sslrootcert", "sslcrl"] } conn = psycopg2.connect( - host=_options.get('host'), - port=_options.get('port'), - dbname=_options.get('db'), - user=_options.get('user'), - password=_options.get('pass'), + host=_options.get("host"), + port=_options.get("port"), + dbname=_options.get("db"), + user=_options.get("user"), + password=_options.get("pass"), **ssl_options ) except psycopg2.OperationalError as exc: - raise salt.exceptions.SaltMasterError('pgjsonb returner could not connect to database: {exc}'.format(exc=exc)) + raise salt.exceptions.SaltMasterError( + "pgjsonb returner could not connect to database: {exc}".format(exc=exc) + ) if conn.server_version is not None and conn.server_version >= 90500: global PG_SAVE_LOAD_SQL - PG_SAVE_LOAD_SQL = '''INSERT INTO jids + PG_SAVE_LOAD_SQL = """INSERT INTO jids (jid, load) VALUES (%(jid)s, %(load)s) ON CONFLICT (jid) DO UPDATE - SET load=%(load)s''' + SET load=%(load)s""" cursor = conn.cursor() @@ -286,50 +299,60 @@ def _get_serv(ret=None, commit=False): def returner(ret): - ''' + """ Return data to a Pg server - ''' + """ try: with _get_serv(ret, commit=True) as cur: - sql = '''INSERT INTO salt_returns + sql = """INSERT INTO salt_returns (fun, jid, return, id, success, full_ret, alter_time) - VALUES (%s, %s, %s, %s, %s, %s, to_timestamp(%s))''' + VALUES (%s, %s, %s, %s, %s, %s, to_timestamp(%s))""" - cur.execute(sql, (ret['fun'], ret['jid'], - psycopg2.extras.Json(ret['return']), - ret['id'], - ret.get('success', False), - psycopg2.extras.Json(ret), - time.time())) + cur.execute( + sql, + ( + ret["fun"], + ret["jid"], + psycopg2.extras.Json(ret["return"]), + ret["id"], + ret.get("success", False), + psycopg2.extras.Json(ret), + time.time(), + ), + ) except salt.exceptions.SaltMasterError: - log.critical('Could not store return with pgjsonb returner. PostgreSQL server unavailable.') + log.critical( + "Could not store return with pgjsonb returner. PostgreSQL server unavailable." + ) def event_return(events): - ''' + """ Return event to Pg server Requires that configuration be enabled via 'event_return' option in master config. - ''' + """ with _get_serv(events, commit=True) as cur: for event in events: - tag = event.get('tag', '') - data = event.get('data', '') - sql = '''INSERT INTO salt_events (tag, data, master_id, alter_time) - VALUES (%s, %s, %s, to_timestamp(%s))''' - cur.execute(sql, (tag, psycopg2.extras.Json(data), - __opts__['id'], time.time())) + tag = event.get("tag", "") + data = event.get("data", "") + sql = """INSERT INTO salt_events (tag, data, master_id, alter_time) + VALUES (%s, %s, %s, to_timestamp(%s))""" + cur.execute( + sql, (tag, psycopg2.extras.Json(data), __opts__["id"], time.time()) + ) def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid id - ''' + """ with _get_serv(commit=True) as cur: try: - cur.execute(PG_SAVE_LOAD_SQL, - {'jid': jid, 'load': psycopg2.extras.Json(load)}) + cur.execute( + PG_SAVE_LOAD_SQL, {"jid": jid, "load": psycopg2.extras.Json(load)} + ) except psycopg2.IntegrityError: # https://github.com/saltstack/salt/issues/22171 # Without this try/except we get tons of duplicate entry errors @@ -338,18 +361,18 @@ def save_load(jid, load, minions=None): def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT load FROM jids WHERE jid = %s;''' + sql = """SELECT load FROM jids WHERE jid = %s;""" cur.execute(sql, (jid,)) data = cur.fetchone() if data: @@ -358,13 +381,13 @@ def get_load(jid): def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT id, full_ret FROM salt_returns - WHERE jid = %s''' + sql = """SELECT id, full_ret FROM salt_returns + WHERE jid = %s""" cur.execute(sql, (jid,)) data = cur.fetchall() @@ -376,18 +399,18 @@ def get_jid(jid): def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT s.id,s.jid, s.full_ret + sql = """SELECT s.id,s.jid, s.full_ret FROM salt_returns s JOIN ( SELECT MAX(`jid`) as jid from salt_returns GROUP BY fun, id) max ON s.jid = max.jid WHERE s.fun = %s - ''' + """ cur.execute(sql, (fun,)) data = cur.fetchall() @@ -400,13 +423,13 @@ def get_fun(fun): def get_jids(): - ''' + """ Return a list of all job ids - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT jid, load - FROM jids''' + sql = """SELECT jid, load + FROM jids""" cur.execute(sql) data = cur.fetchall() @@ -417,13 +440,13 @@ def get_jids(): def get_minions(): - ''' + """ Return a list of minions - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT DISTINCT id - FROM salt_returns''' + sql = """SELECT DISTINCT id + FROM salt_returns""" cur.execute(sql) data = cur.fetchall() @@ -434,23 +457,23 @@ def get_minions(): def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) def _purge_jobs(timestamp): - ''' + """ Purge records from the returner tables. :param job_age_in_seconds: Purge jobs older than this :return: - ''' + """ with _get_serv() as cursor: try: - sql = 'delete from jids where jid in (select distinct jid from salt_returns where alter_time < %s)' + sql = "delete from jids where jid in (select distinct jid from salt_returns where alter_time < %s)" cursor.execute(sql, (timestamp,)) - cursor.execute('COMMIT') + cursor.execute("COMMIT") except psycopg2.DatabaseError as err: error = err.args sys.stderr.write(six.text_type(error)) @@ -458,9 +481,9 @@ def _purge_jobs(timestamp): raise err try: - sql = 'delete from salt_returns where alter_time < %s' + sql = "delete from salt_returns where alter_time < %s" cursor.execute(sql, (timestamp,)) - cursor.execute('COMMIT') + cursor.execute("COMMIT") except psycopg2.DatabaseError as err: error = err.args sys.stderr.write(six.text_type(error)) @@ -468,9 +491,9 @@ def _purge_jobs(timestamp): raise err try: - sql = 'delete from salt_events where alter_time < %s' + sql = "delete from salt_events where alter_time < %s" cursor.execute(sql, (timestamp,)) - cursor.execute('COMMIT') + cursor.execute("COMMIT") except psycopg2.DatabaseError as err: error = err.args sys.stderr.write(six.text_type(error)) @@ -481,23 +504,23 @@ def _purge_jobs(timestamp): def _archive_jobs(timestamp): - ''' + """ Copy rows to a set of backup tables, then purge rows. :param timestamp: Archive rows older than this timestamp :return: - ''' - source_tables = ['jids', - 'salt_returns', - 'salt_events'] + """ + source_tables = ["jids", "salt_returns", "salt_events"] with _get_serv() as cursor: target_tables = {} for table_name in source_tables: try: - tmp_table_name = table_name + '_archive' - sql = 'create table IF NOT exists {0} (LIKE {1})'.format(tmp_table_name, table_name) + tmp_table_name = table_name + "_archive" + sql = "create table IF NOT exists {0} (LIKE {1})".format( + tmp_table_name, table_name + ) cursor.execute(sql) - cursor.execute('COMMIT') + cursor.execute("COMMIT") target_tables[table_name] = tmp_table_name except psycopg2.DatabaseError as err: error = err.args @@ -506,9 +529,11 @@ def _archive_jobs(timestamp): raise err try: - sql = 'insert into {0} select * from {1} where jid in (select distinct jid from salt_returns where alter_time < %s)'.format(target_tables['jids'], 'jids') + sql = "insert into {0} select * from {1} where jid in (select distinct jid from salt_returns where alter_time < %s)".format( + target_tables["jids"], "jids" + ) cursor.execute(sql, (timestamp,)) - cursor.execute('COMMIT') + cursor.execute("COMMIT") except psycopg2.DatabaseError as err: error = err.args sys.stderr.write(six.text_type(error)) @@ -519,9 +544,11 @@ def _archive_jobs(timestamp): raise try: - sql = 'insert into {0} select * from {1} where alter_time < %s'.format(target_tables['salt_returns'], 'salt_returns') + sql = "insert into {0} select * from {1} where alter_time < %s".format( + target_tables["salt_returns"], "salt_returns" + ) cursor.execute(sql, (timestamp,)) - cursor.execute('COMMIT') + cursor.execute("COMMIT") except psycopg2.DatabaseError as err: error = err.args sys.stderr.write(six.text_type(error)) @@ -529,9 +556,11 @@ def _archive_jobs(timestamp): raise err try: - sql = 'insert into {0} select * from {1} where alter_time < %s'.format(target_tables['salt_events'], 'salt_events') + sql = "insert into {0} select * from {1} where alter_time < %s".format( + target_tables["salt_events"], "salt_events" + ) cursor.execute(sql, (timestamp,)) - cursor.execute('COMMIT') + cursor.execute("COMMIT") except psycopg2.DatabaseError as err: error = err.args sys.stderr.write(six.text_type(error)) @@ -542,20 +571,22 @@ def _archive_jobs(timestamp): def clean_old_jobs(): - ''' + """ Called in the master's event loop every loop_interval. Archives and/or deletes the events and job details from the database. :return: - ''' - if __opts__.get('keep_jobs', False) and int(__opts__.get('keep_jobs', 0)) > 0: + """ + if __opts__.get("keep_jobs", False) and int(__opts__.get("keep_jobs", 0)) > 0: try: with _get_serv() as cur: - sql = "select (NOW() - interval '{0}' hour) as stamp;".format(__opts__['keep_jobs']) + sql = "select (NOW() - interval '{0}' hour) as stamp;".format( + __opts__["keep_jobs"] + ) cur.execute(sql) rows = cur.fetchall() stamp = rows[0][0] - if __opts__.get('archive_jobs', False): + if __opts__.get("archive_jobs", False): _archive_jobs(stamp) else: _purge_jobs(stamp) diff --git a/salt/returners/postgres.py b/salt/returners/postgres.py index 3de287716d8..5c3cffb685b 100644 --- a/salt/returners/postgres.py +++ b/salt/returners/postgres.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a postgresql server .. note:: @@ -125,82 +125,96 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return postgres --return_kwargs '{"db": "another-salt"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import sys -import logging from contextlib import contextmanager +import salt.exceptions +import salt.returners + # Import Salt libs import salt.utils.jid import salt.utils.json -import salt.returners -import salt.exceptions # Import third party libs from salt.ext import six + try: import psycopg2 + HAS_POSTGRES = True except ImportError: HAS_POSTGRES = False -__virtualname__ = 'postgres' +__virtualname__ = "postgres" log = logging.getLogger(__name__) def __virtual__(): if not HAS_POSTGRES: - return False, 'Could not import postgres returner; psycopg2 is not installed.' + return False, "Could not import postgres returner; psycopg2 is not installed." return __virtualname__ def _get_options(ret=None): - ''' + """ Get the postgres options from salt. - ''' - defaults = {'host': 'localhost', - 'user': 'salt', - 'passwd': 'salt', - 'db': 'salt', - 'port': 5432} + """ + defaults = { + "host": "localhost", + "user": "salt", + "passwd": "salt", + "db": "salt", + "port": 5432, + } - attrs = {'host': 'host', - 'user': 'user', - 'passwd': 'passwd', - 'db': 'db', - 'port': 'port'} + attrs = { + "host": "host", + "user": "user", + "passwd": "passwd", + "db": "db", + "port": "port", + } - _options = salt.returners.get_returner_options('returner.{0}'.format(__virtualname__), - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) + _options = salt.returners.get_returner_options( + "returner.{0}".format(__virtualname__), + ret, + attrs, + __salt__=__salt__, + __opts__=__opts__, + defaults=defaults, + ) # Ensure port is an int - if 'port' in _options: - _options['port'] = int(_options['port']) + if "port" in _options: + _options["port"] = int(_options["port"]) return _options @contextmanager def _get_serv(ret=None, commit=False): - ''' + """ Return a Pg cursor - ''' + """ _options = _get_options(ret) try: - conn = psycopg2.connect(host=_options.get('host'), - user=_options.get('user'), - password=_options.get('passwd'), - database=_options.get('db'), - port=_options.get('port')) + conn = psycopg2.connect( + host=_options.get("host"), + user=_options.get("user"), + password=_options.get("passwd"), + database=_options.get("db"), + port=_options.get("port"), + ) except psycopg2.OperationalError as exc: - raise salt.exceptions.SaltMasterError('postgres returner could not connect to database: {exc}'.format(exc=exc)) + raise salt.exceptions.SaltMasterError( + "postgres returner could not connect to database: {exc}".format(exc=exc) + ) cursor = conn.cursor() @@ -221,57 +235,59 @@ def _get_serv(ret=None, commit=False): def returner(ret): - ''' + """ Return data to a postgres server - ''' + """ try: with _get_serv(ret, commit=True) as cur: - sql = '''INSERT INTO salt_returns + sql = """INSERT INTO salt_returns (fun, jid, return, id, success, full_ret) - VALUES (%s, %s, %s, %s, %s, %s)''' + VALUES (%s, %s, %s, %s, %s, %s)""" cur.execute( - sql, ( - ret['fun'], - ret['jid'], - salt.utils.json.dumps(ret['return']), - ret['id'], - ret.get('success', False), - salt.utils.json.dumps(ret))) + sql, + ( + ret["fun"], + ret["jid"], + salt.utils.json.dumps(ret["return"]), + ret["id"], + ret.get("success", False), + salt.utils.json.dumps(ret), + ), + ) except salt.exceptions.SaltMasterError: - log.critical('Could not store return with postgres returner. PostgreSQL server unavailable.') + log.critical( + "Could not store return with postgres returner. PostgreSQL server unavailable." + ) def event_return(events): - ''' + """ Return event to Pg server Requires that configuration be enabled via 'event_return' option in master config. - ''' + """ with _get_serv(events, commit=True) as cur: for event in events: - tag = event.get('tag', '') - data = event.get('data', '') - sql = '''INSERT INTO salt_events (tag, data, master_id) - VALUES (%s, %s, %s)''' - cur.execute(sql, (tag, - salt.utils.json.dumps(data), - __opts__['id'])) + tag = event.get("tag", "") + data = event.get("data", "") + sql = """INSERT INTO salt_events (tag, data, master_id) + VALUES (%s, %s, %s)""" + cur.execute(sql, (tag, salt.utils.json.dumps(data), __opts__["id"])) def save_load(jid, load, minions=None): # pylint: disable=unused-argument - ''' + """ Save the load to the specified jid id - ''' + """ with _get_serv(commit=True) as cur: - sql = '''INSERT INTO jids + sql = """INSERT INTO jids (jid, load) - VALUES (%s, %s)''' + VALUES (%s, %s)""" try: - cur.execute(sql, (jid, - salt.utils.json.dumps(load))) + cur.execute(sql, (jid, salt.utils.json.dumps(load))) except psycopg2.IntegrityError: # https://github.com/saltstack/salt/issues/22171 # Without this try/except we get tons of duplicate entry errors @@ -280,17 +296,17 @@ def save_load(jid, load, minions=None): # pylint: disable=unused-argument def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT load FROM jids WHERE jid = %s;''' + sql = """SELECT load FROM jids WHERE jid = %s;""" cur.execute(sql, (jid,)) data = cur.fetchone() if data: @@ -299,13 +315,13 @@ def get_load(jid): def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT id, full_ret FROM salt_returns - WHERE jid = %s''' + sql = """SELECT id, full_ret FROM salt_returns + WHERE jid = %s""" cur.execute(sql, (jid,)) data = cur.fetchall() @@ -317,18 +333,18 @@ def get_jid(jid): def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT s.id,s.jid, s.full_ret + sql = """SELECT s.id,s.jid, s.full_ret FROM salt_returns s JOIN ( SELECT MAX(`jid`) as jid from salt_returns GROUP BY fun, id) max ON s.jid = max.jid WHERE s.fun = %s - ''' + """ cur.execute(sql, (fun,)) data = cur.fetchall() @@ -341,31 +357,32 @@ def get_fun(fun): def get_jids(): - ''' + """ Return a list of all job ids - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT jid, load - FROM jids''' + sql = """SELECT jid, load + FROM jids""" cur.execute(sql) data = cur.fetchall() ret = {} for jid, load in data: - ret[jid] = salt.utils.jid.format_jid_instance(jid, - salt.utils.json.loads(load)) + ret[jid] = salt.utils.jid.format_jid_instance( + jid, salt.utils.json.loads(load) + ) return ret def get_minions(): - ''' + """ Return a list of minions - ''' + """ with _get_serv(ret=None, commit=True) as cur: - sql = '''SELECT DISTINCT id - FROM salt_returns''' + sql = """SELECT DISTINCT id + FROM salt_returns""" cur.execute(sql) data = cur.fetchall() @@ -376,7 +393,7 @@ def get_minions(): def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/postgres_local_cache.py b/salt/returners/postgres_local_cache.py index 4fc3c0ff1b6..24b631888c8 100644 --- a/salt/returners/postgres_local_cache.py +++ b/salt/returners/postgres_local_cache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use a postgresql server for the master job cache. This helps the job cache to cope with scale. @@ -105,10 +105,11 @@ and then: EOF Required python modules: psycopg2 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import re import sys @@ -121,75 +122,79 @@ from salt.ext import six # Import third party libs try: import psycopg2 + HAS_POSTGRES = True except ImportError: HAS_POSTGRES = False log = logging.getLogger(__name__) -__virtualname__ = 'postgres_local_cache' +__virtualname__ = "postgres_local_cache" def __virtual__(): if not HAS_POSTGRES: - return (False, 'Could not import psycopg2; postges_local_cache disabled') + return (False, "Could not import psycopg2; postges_local_cache disabled") return __virtualname__ def _get_conn(): - ''' + """ Return a postgres connection. - ''' + """ try: conn = psycopg2.connect( - host=__opts__['master_job_cache.postgres.host'], - user=__opts__['master_job_cache.postgres.user'], - password=__opts__['master_job_cache.postgres.passwd'], - database=__opts__['master_job_cache.postgres.db'], - port=__opts__['master_job_cache.postgres.port']) + host=__opts__["master_job_cache.postgres.host"], + user=__opts__["master_job_cache.postgres.user"], + password=__opts__["master_job_cache.postgres.passwd"], + database=__opts__["master_job_cache.postgres.db"], + port=__opts__["master_job_cache.postgres.port"], + ) except psycopg2.OperationalError: - log.error('Could not connect to SQL server: %s', sys.exc_info()[0]) + log.error("Could not connect to SQL server: %s", sys.exc_info()[0]) return None return conn def _close_conn(conn): - ''' + """ Close the postgres connection. - ''' + """ conn.commit() conn.close() def _format_job_instance(job): - ''' + """ Format the job instance correctly - ''' - ret = {'Function': job.get('fun', 'unknown-function'), - 'Arguments': salt.utils.json.loads(job.get('arg', '[]')), - # unlikely but safeguard from invalid returns - 'Target': job.get('tgt', 'unknown-target'), - 'Target-type': job.get('tgt_type', 'list'), - 'User': job.get('user', 'root')} + """ + ret = { + "Function": job.get("fun", "unknown-function"), + "Arguments": salt.utils.json.loads(job.get("arg", "[]")), + # unlikely but safeguard from invalid returns + "Target": job.get("tgt", "unknown-target"), + "Target-type": job.get("tgt_type", "list"), + "User": job.get("user", "root"), + } # TODO: Add Metadata support when it is merged from develop return ret def _format_jid_instance(jid, job): - ''' + """ Format the jid correctly - ''' + """ ret = _format_job_instance(job) - ret.update({'StartTime': salt.utils.jid.jid_to_time(jid)}) + ret.update({"StartTime": salt.utils.jid.jid_to_time(jid)}) return ret def _gen_jid(cur): - ''' + """ Generate an unique job id - ''' + """ jid = salt.utils.jid.gen_jid(__opts__) - sql = '''SELECT jid FROM jids WHERE jid = %s''' + sql = """SELECT jid FROM jids WHERE jid = %s""" cur.execute(sql, (jid,)) data = cur.fetchall() if not data: @@ -198,12 +203,12 @@ def _gen_jid(cur): def prep_jid(nocache=False, passed_jid=None): - ''' + """ Return a job id and prepare the job id directory This is the function responsible for making sure jids don't collide (unless its passed a jid). So do what you have to do to make sure that stays the case - ''' + """ conn = _get_conn() if conn is None: return None @@ -222,70 +227,76 @@ def prep_jid(nocache=False, passed_jid=None): def returner(load): - ''' + """ Return data to a postgres server - ''' + """ conn = _get_conn() if conn is None: return None cur = conn.cursor() - sql = '''INSERT INTO salt_returns + sql = """INSERT INTO salt_returns (fun, jid, return, id, success) - VALUES (%s, %s, %s, %s, %s)''' - job_ret = {'return': six.text_type(six.text_type(load['return']), 'utf-8', 'replace')} - if 'retcode' in load: - job_ret['retcode'] = load['retcode'] - if 'success' in load: - job_ret['success'] = load['success'] + VALUES (%s, %s, %s, %s, %s)""" + job_ret = { + "return": six.text_type(six.text_type(load["return"]), "utf-8", "replace") + } + if "retcode" in load: + job_ret["retcode"] = load["retcode"] + if "success" in load: + job_ret["success"] = load["success"] cur.execute( - sql, ( - load['fun'], - load['jid'], + sql, + ( + load["fun"], + load["jid"], salt.utils.json.dumps(job_ret), - load['id'], - load.get('success'), - ) + load["id"], + load.get("success"), + ), ) _close_conn(conn) def event_return(events): - ''' + """ Return event to a postgres server Require that configuration be enabled via 'event_return' option in master config. - ''' + """ conn = _get_conn() if conn is None: return None cur = conn.cursor() for event in events: - tag = event.get('tag', '') - data = event.get('data', '') - sql = '''INSERT INTO salt_events + tag = event.get("tag", "") + data = event.get("data", "") + sql = """INSERT INTO salt_events (tag, data, master_id) - VALUES (%s, %s, %s)''' - cur.execute(sql, (tag, salt.utils.json.dumps(data), __opts__['id'])) + VALUES (%s, %s, %s)""" + cur.execute(sql, (tag, salt.utils.json.dumps(data), __opts__["id"])) _close_conn(conn) def save_load(jid, clear_load, minions=None): - ''' + """ Save the load to the specified jid id - ''' + """ jid = _escape_jid(jid) conn = _get_conn() if conn is None: return None cur = conn.cursor() - sql = '''INSERT INTO jids ''' \ - '''(jid, started, tgt_type, cmd, tgt, kwargs, ret, username, arg,''' \ - ''' fun) ''' \ - '''VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)''' + sql = ( + """INSERT INTO jids """ + """(jid, started, tgt_type, cmd, tgt, kwargs, ret, username, arg,""" + """ fun) """ + """VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""" + ) cur.execute( - sql, ( + sql, + ( jid, salt.utils.jid.jid_to_time(jid), six.text_type(clear_load.get("tgt_type")), @@ -296,31 +307,31 @@ def save_load(jid, clear_load, minions=None): six.text_type(clear_load.get("user")), six.text_type(salt.utils.json.dumps(clear_load.get("arg"))), six.text_type(clear_load.get("fun")), - ) + ), ) # TODO: Add Metadata support when it is merged from develop _close_conn(conn) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def _escape_jid(jid): - ''' + """ Do proper formatting of the jid - ''' + """ jid = six.text_type(jid) jid = re.sub(r"'*", "", jid) return jid def _build_dict(data): - ''' + """ Rebuild dict - ''' + """ result = {} # TODO: Add Metadata support when it is merged from develop result["jid"] = data[0] @@ -336,16 +347,18 @@ def _build_dict(data): def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' + """ jid = _escape_jid(jid) conn = _get_conn() if conn is None: return None cur = conn.cursor() - sql = '''SELECT jid, tgt_type, cmd, tgt, kwargs, ret, username, arg,''' \ - ''' fun FROM jids WHERE jid = %s''' + sql = ( + """SELECT jid, tgt_type, cmd, tgt, kwargs, ret, username, arg,""" + """ fun FROM jids WHERE jid = %s""" + ) cur.execute(sql, (jid,)) data = cur.fetchone() if data: @@ -355,15 +368,15 @@ def get_load(jid): def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' + """ jid = _escape_jid(jid) conn = _get_conn() if conn is None: return None cur = conn.cursor() - sql = '''SELECT id, return FROM salt_returns WHERE jid = %s''' + sql = """SELECT id, return FROM salt_returns WHERE jid = %s""" cur.execute(sql, (jid,)) data = cur.fetchall() @@ -371,36 +384,41 @@ def get_jid(jid): if data: for minion, full_ret in data: ret_data = salt.utils.json.loads(full_ret) - if not isinstance(ret_data, dict) or 'return' not in ret_data: + if not isinstance(ret_data, dict) or "return" not in ret_data: # Convert the old format in which the return contains the only return data to the # new that is dict containing 'return' and optionally 'retcode' and 'success'. - ret_data = {'return': ret_data} + ret_data = {"return": ret_data} ret[minion] = ret_data _close_conn(conn) return ret def get_jids(): - ''' + """ Return a list of all job ids For master job cache this also formats the output and returns a string - ''' + """ conn = _get_conn() cur = conn.cursor() - sql = '''SELECT ''' \ - '''jid, tgt_type, cmd, tgt, kwargs, ret, username, arg, fun ''' \ - '''FROM jids''' - if __opts__['keep_jobs'] != 0: - sql = sql + " WHERE started > NOW() - INTERVAL '" \ - + six.text_type(__opts__['keep_jobs']) + "' HOUR" + sql = ( + """SELECT """ + """jid, tgt_type, cmd, tgt, kwargs, ret, username, arg, fun """ + """FROM jids""" + ) + if __opts__["keep_jobs"] != 0: + sql = ( + sql + + " WHERE started > NOW() - INTERVAL '" + + six.text_type(__opts__["keep_jobs"]) + + "' HOUR" + ) cur.execute(sql) ret = {} data = cur.fetchone() while data: data_dict = _build_dict(data) - ret[data_dict["jid"]] = \ - _format_jid_instance(data_dict["jid"], data_dict) + ret[data_dict["jid"]] = _format_jid_instance(data_dict["jid"], data_dict) data = cur.fetchone() cur.close() conn.close() @@ -408,7 +426,7 @@ def get_jids(): def clean_old_jobs(): - ''' + """ Clean out the old jobs from the job cache - ''' + """ return diff --git a/salt/returners/pushover_returner.py b/salt/returners/pushover_returner.py index e36d79b0585..21a2dd23d0d 100644 --- a/salt/returners/pushover_returner.py +++ b/salt/returners/pushover_returner.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data via pushover (http://www.pushover.net) .. versionadded:: 2016.3.0 @@ -74,91 +74,100 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return pushover --return_kwargs '{"title": "Salt is awesome!"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import Python libs import pprint -import logging - -# Import 3rd-party libs -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -# pylint: enable=import-error,no-name-in-module,redefined-builtin # Import Salt Libs import salt.returners import salt.utils.pushover from salt.exceptions import SaltInvocationError +# Import 3rd-party libs +# pylint: disable=import-error,no-name-in-module,redefined-builtin +from salt.ext.six.moves.urllib.parse import urlencode as _urlencode + +# pylint: enable=import-error,no-name-in-module,redefined-builtin + + log = logging.getLogger(__name__) -__virtualname__ = 'pushover' +__virtualname__ = "pushover" def _get_options(ret=None): - ''' + """ Get the pushover options from salt. - ''' + """ - defaults = {'priority': '0'} + defaults = {"priority": "0"} - attrs = {'pushover_profile': 'profile', - 'user': 'user', - 'device': 'device', - 'token': 'token', - 'priority': 'priority', - 'title': 'title', - 'api_version': 'api_version', - 'expire': 'expire', - 'retry': 'retry', - 'sound': 'sound', - } + attrs = { + "pushover_profile": "profile", + "user": "user", + "device": "device", + "token": "token", + "priority": "priority", + "title": "title", + "api_version": "api_version", + "expire": "expire", + "retry": "retry", + "sound": "sound", + } - profile_attr = 'pushover_profile' + profile_attr = "pushover_profile" - profile_attrs = {'user': 'user', - 'device': 'device', - 'token': 'token', - 'priority': 'priority', - 'title': 'title', - 'api_version': 'api_version', - 'expire': 'expire', - 'retry': 'retry', - 'sound': 'sound', - } + profile_attrs = { + "user": "user", + "device": "device", + "token": "token", + "priority": "priority", + "title": "title", + "api_version": "api_version", + "expire": "expire", + "retry": "retry", + "sound": "sound", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - profile_attr=profile_attr, - profile_attrs=profile_attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) + _options = salt.returners.get_returner_options( + __virtualname__, + ret, + attrs, + profile_attr=profile_attr, + profile_attrs=profile_attrs, + __salt__=__salt__, + __opts__=__opts__, + defaults=defaults, + ) return _options def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ -def _post_message(user, - device, - message, - title, - priority, - expire, - retry, - sound, - api_version=1, - token=None): - ''' +def _post_message( + user, + device, + message, + title, + priority, + expire, + retry, + sound, + api_version=1, + token=None, +): + """ Send a message to a Pushover user or group. :param user: The user or group to send to, must be key of user or group not email address. :param message: The message to send to the PushOver user or group. @@ -168,84 +177,93 @@ def _post_message(user, :param notify: Whether to notify the room, default: False. :param token: The PushOver token, if not specified in the configuration. :return: Boolean if message was sent successfully. - ''' + """ user_validate = salt.utils.pushover.validate_user(user, device, token) - if not user_validate['result']: + if not user_validate["result"]: return user_validate parameters = dict() - parameters['user'] = user - parameters['device'] = device - parameters['token'] = token - parameters['title'] = title - parameters['priority'] = priority - parameters['expire'] = expire - parameters['retry'] = retry - parameters['message'] = message + parameters["user"] = user + parameters["device"] = device + parameters["token"] = token + parameters["title"] = title + parameters["priority"] = priority + parameters["expire"] = expire + parameters["retry"] = retry + parameters["message"] = message if sound: sound_validate = salt.utils.pushover.validate_sound(sound, token) - if sound_validate['res']: - parameters['sound'] = sound + if sound_validate["res"]: + parameters["sound"] = sound - result = salt.utils.pushover.query(function='message', - method='POST', - header_dict={'Content-Type': 'application/x-www-form-urlencoded'}, - data=_urlencode(parameters), - opts=__opts__) + result = salt.utils.pushover.query( + function="message", + method="POST", + header_dict={"Content-Type": "application/x-www-form-urlencoded"}, + data=_urlencode(parameters), + opts=__opts__, + ) return result def returner(ret): - ''' + """ Send an PushOver message with the data - ''' + """ _options = _get_options(ret) - user = _options.get('user') - device = _options.get('device') - token = _options.get('token') - title = _options.get('title') - priority = _options.get('priority') - expire = _options.get('expire') - retry = _options.get('retry') - sound = _options.get('sound') + user = _options.get("user") + device = _options.get("device") + token = _options.get("token") + title = _options.get("title") + priority = _options.get("priority") + expire = _options.get("expire") + retry = _options.get("retry") + sound = _options.get("sound") if not token: - raise SaltInvocationError('Pushover token is unavailable.') + raise SaltInvocationError("Pushover token is unavailable.") if not user: - raise SaltInvocationError('Pushover user key is unavailable.') + raise SaltInvocationError("Pushover user key is unavailable.") if priority and priority == 2: if not expire and not retry: - raise SaltInvocationError('Priority 2 requires pushover.expire and pushover.retry options.') + raise SaltInvocationError( + "Priority 2 requires pushover.expire and pushover.retry options." + ) - message = ('id: {0}\r\n' - 'function: {1}\r\n' - 'function args: {2}\r\n' - 'jid: {3}\r\n' - 'return: {4}\r\n').format( - ret.get('id'), - ret.get('fun'), - ret.get('fun_args'), - ret.get('jid'), - pprint.pformat(ret.get('return'))) + message = ( + "id: {0}\r\n" + "function: {1}\r\n" + "function args: {2}\r\n" + "jid: {3}\r\n" + "return: {4}\r\n" + ).format( + ret.get("id"), + ret.get("fun"), + ret.get("fun_args"), + ret.get("jid"), + pprint.pformat(ret.get("return")), + ) - result = _post_message(user=user, - device=device, - message=message, - title=title, - priority=priority, - expire=expire, - retry=retry, - sound=sound, - token=token) + result = _post_message( + user=user, + device=device, + message=message, + title=title, + priority=priority, + expire=expire, + retry=retry, + sound=sound, + token=token, + ) - log.debug('pushover result %s', result) - if not result['res']: - log.info('Error: %s', result['message']) + log.debug("pushover result %s", result) + if not result["res"]: + log.info("Error: %s", result["message"]) return diff --git a/salt/returners/rawfile_json.py b/salt/returners/rawfile_json.py index 366f8ba5e3b..990befe1ded 100644 --- a/salt/returners/rawfile_json.py +++ b/salt/returners/rawfile_json.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Take data from salt and "return" it into a raw file containing the json, with one line per event. @@ -15,10 +15,11 @@ Common use is to log all events on the master. This can generate a lot of noise, so you may wish to configure batch processing and/or configure the :conf_master:`event_return_whitelist` or :conf_master:`event_return_blacklist` to restrict the events that are written. -''' +""" # Import python libs -from __future__ import absolute_import, print_function, with_statement, unicode_literals +from __future__ import absolute_import, print_function, unicode_literals, with_statement + import logging import salt.returners @@ -28,7 +29,7 @@ import salt.utils.json log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'rawfile_json' +__virtualname__ = "rawfile_json" def __virtual__(): @@ -36,49 +37,51 @@ def __virtual__(): def _get_options(ret): - ''' + """ Returns options used for the rawfile_json returner. - ''' - defaults = {'filename': '/var/log/salt/events'} - attrs = {'filename': 'filename'} - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) + """ + defaults = {"filename": "/var/log/salt/events"} + attrs = {"filename": "filename"} + _options = salt.returners.get_returner_options( + __virtualname__, + ret, + attrs, + __salt__=__salt__, + __opts__=__opts__, + defaults=defaults, + ) return _options def returner(ret): - ''' + """ Write the return data to a file on the minion. - ''' + """ opts = _get_options(ret) try: - with salt.utils.files.flopen(opts['filename'], 'a') as logfile: + with salt.utils.files.flopen(opts["filename"], "a") as logfile: salt.utils.json.dump(ret, logfile) - logfile.write(str('\n')) # future lint: disable=blacklisted-function + logfile.write(str("\n")) # future lint: disable=blacklisted-function except Exception: # pylint: disable=broad-except - log.error('Could not write to rawdata_json file %s', opts['filename']) + log.error("Could not write to rawdata_json file %s", opts["filename"]) raise def event_return(events): - ''' + """ Write event data (return data and non-return data) to file on the master. - ''' + """ if len(events) == 0: # events is an empty list. # Don't open the logfile in vain. return opts = _get_options({}) # Pass in empty ret, since this is a list of events try: - with salt.utils.files.flopen(opts['filename'], 'a') as logfile: + with salt.utils.files.flopen(opts["filename"], "a") as logfile: for event in events: salt.utils.json.dump(event, logfile) - logfile.write(str('\n')) # future lint: disable=blacklisted-function + logfile.write(str("\n")) # future lint: disable=blacklisted-function except Exception: # pylint: disable=broad-except - log.error('Could not write to rawdata_json file %s', opts['filename']) + log.error("Could not write to rawdata_json file %s", opts["filename"]) raise diff --git a/salt/returners/redis_return.py b/salt/returners/redis_return.py index 09ddd52784f..59311033567 100644 --- a/salt/returners/redis_return.py +++ b/salt/returners/redis_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to a redis server To enable this returner the minion will need the python client for redis @@ -92,9 +92,10 @@ cluster.skip_full_coverage_check: ``False`` Most cloud hosted redis clusters will require this to be set to ``True`` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -105,8 +106,10 @@ import salt.utils.platform # Import 3rd-party libs from salt.ext import six + try: import redis + HAS_REDIS = True except ImportError: HAS_REDIS = False @@ -114,7 +117,11 @@ except ImportError: log = logging.getLogger(__name__) try: + # pylint: disable=no-name-in-module from rediscluster import StrictRedisCluster + + # pylint: enable=no-name-in-module + HAS_REDIS_CLUSTER = True except ImportError: HAS_REDIS_CLUSTER = False @@ -122,199 +129,206 @@ except ImportError: REDIS_POOL = None # Define the module's virtual name -__virtualname__ = 'redis' +__virtualname__ = "redis" def __virtual__(): - ''' + """ The redis library must be installed for this module to work. The redis redis cluster library must be installed if cluster_mode is True - ''' + """ if not HAS_REDIS: - return False, 'Could not import redis returner; ' \ - 'redis python client is not installed.' - if not HAS_REDIS_CLUSTER and _get_options().get('cluster_mode', False): + return ( + False, + "Could not import redis returner; redis python client is not installed.", + ) + if not HAS_REDIS_CLUSTER and _get_options().get("cluster_mode", False): return (False, "Please install the redis-py-cluster package.") return __virtualname__ def _get_options(ret=None): - ''' + """ Get the redis options from salt. - ''' - attrs = {'host': 'host', - 'port': 'port', - 'unix_socket_path': 'unix_socket_path', - 'db': 'db', - 'cluster_mode': 'cluster_mode', - 'startup_nodes': 'cluster.startup_nodes', - 'skip_full_coverage_check': 'cluster.skip_full_coverage_check', - } + """ + attrs = { + "host": "host", + "port": "port", + "unix_socket_path": "unix_socket_path", + "db": "db", + "cluster_mode": "cluster_mode", + "startup_nodes": "cluster.startup_nodes", + "skip_full_coverage_check": "cluster.skip_full_coverage_check", + } if salt.utils.platform.is_proxy(): return { - 'host': __opts__.get('redis.host', 'salt'), - 'port': __opts__.get('redis.port', 6379), - 'unix_socket_path': __opts__.get('redis.unix_socket_path', None), - 'db': __opts__.get('redis.db', '0'), - 'cluster_mode': __opts__.get('redis.cluster_mode', False), - 'startup_nodes': __opts__.get('redis.cluster.startup_nodes', {}), - 'skip_full_coverage_check': __opts__.get('redis.cluster.skip_full_coverage_check', False) + "host": __opts__.get("redis.host", "salt"), + "port": __opts__.get("redis.port", 6379), + "unix_socket_path": __opts__.get("redis.unix_socket_path", None), + "db": __opts__.get("redis.db", "0"), + "cluster_mode": __opts__.get("redis.cluster_mode", False), + "startup_nodes": __opts__.get("redis.cluster.startup_nodes", {}), + "skip_full_coverage_check": __opts__.get( + "redis.cluster.skip_full_coverage_check", False + ), } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options def _get_serv(ret=None): - ''' + """ Return a redis server object - ''' + """ _options = _get_options(ret) global REDIS_POOL if REDIS_POOL: return REDIS_POOL - elif _options.get('cluster_mode'): - REDIS_POOL = StrictRedisCluster(startup_nodes=_options.get('startup_nodes'), - skip_full_coverage_check=_options.get('skip_full_coverage_check'), - decode_responses=True) + elif _options.get("cluster_mode"): + REDIS_POOL = StrictRedisCluster( + startup_nodes=_options.get("startup_nodes"), + skip_full_coverage_check=_options.get("skip_full_coverage_check"), + decode_responses=True, + ) else: - REDIS_POOL = redis.StrictRedis(host=_options.get('host'), - port=_options.get('port'), - unix_socket_path=_options.get('unix_socket_path', None), - db=_options.get('db'), - decode_responses=True) + REDIS_POOL = redis.StrictRedis( + host=_options.get("host"), + port=_options.get("port"), + unix_socket_path=_options.get("unix_socket_path", None), + db=_options.get("db"), + decode_responses=True, + ) return REDIS_POOL def _get_ttl(): - return __opts__.get('keep_jobs', 24) * 3600 + return __opts__.get("keep_jobs", 24) * 3600 def returner(ret): - ''' + """ Return data to a redis data store - ''' + """ serv = _get_serv(ret) pipeline = serv.pipeline(transaction=False) - minion, jid = ret['id'], ret['jid'] - pipeline.hset('ret:{0}'.format(jid), minion, salt.utils.json.dumps(ret)) - pipeline.expire('ret:{0}'.format(jid), _get_ttl()) - pipeline.set('{0}:{1}'.format(minion, ret['fun']), jid) - pipeline.sadd('minions', minion) + minion, jid = ret["id"], ret["jid"] + pipeline.hset("ret:{0}".format(jid), minion, salt.utils.json.dumps(ret)) + pipeline.expire("ret:{0}".format(jid), _get_ttl()) + pipeline.set("{0}:{1}".format(minion, ret["fun"]), jid) + pipeline.sadd("minions", minion) pipeline.execute() def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid - ''' + """ serv = _get_serv(ret=None) - serv.setex('load:{0}'.format(jid), _get_ttl(), salt.utils.json.dumps(load)) + serv.setex("load:{0}".format(jid), _get_ttl(), salt.utils.json.dumps(load)) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load data that marks a specified jid - ''' + """ serv = _get_serv(ret=None) - data = serv.get('load:{0}'.format(jid)) + data = serv.get("load:{0}".format(jid)) if data: return salt.utils.json.loads(data) return {} def get_jid(jid): - ''' + """ Return the information returned when the specified job id was executed - ''' + """ serv = _get_serv(ret=None) ret = {} - for minion, data in six.iteritems(serv.hgetall('ret:{0}'.format(jid))): + for minion, data in six.iteritems(serv.hgetall("ret:{0}".format(jid))): if data: ret[minion] = salt.utils.json.loads(data) return ret def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' + """ serv = _get_serv(ret=None) ret = {} - for minion in serv.smembers('minions'): - ind_str = '{0}:{1}'.format(minion, fun) + for minion in serv.smembers("minions"): + ind_str = "{0}:{1}".format(minion, fun) try: jid = serv.get(ind_str) except Exception: # pylint: disable=broad-except continue if not jid: continue - data = serv.get('{0}:{1}'.format(minion, jid)) + data = serv.get("{0}:{1}".format(minion, jid)) if data: ret[minion] = salt.utils.json.loads(data) return ret def get_jids(): - ''' + """ Return a dict mapping all job ids to job information - ''' + """ serv = _get_serv(ret=None) ret = {} - for s in serv.mget(serv.keys('load:*')): + for s in serv.mget(serv.keys("load:*")): if s is None: continue load = salt.utils.json.loads(s) - jid = load['jid'] + jid = load["jid"] ret[jid] = salt.utils.jid.format_jid_instance(jid, load) return ret def get_minions(): - ''' + """ Return a list of minions - ''' + """ serv = _get_serv(ret=None) - return list(serv.smembers('minions')) + return list(serv.smembers("minions")) def clean_old_jobs(): - ''' + """ Clean out minions's return data for old jobs. Normally, hset 'ret:<jid>' are saved with a TTL, and will eventually get cleaned by redis.But for jobs with some very late minion return, the corresponding hset's TTL will be refreshed to a too late timestamp, we'll do manually cleaning here. - ''' + """ serv = _get_serv(ret=None) - ret_jids = serv.keys('ret:*') - living_jids = set(serv.keys('load:*')) + ret_jids = serv.keys("ret:*") + living_jids = set(serv.keys("load:*")) to_remove = [] for ret_key in ret_jids: - load_key = ret_key.replace('ret:', 'load:', 1) + load_key = ret_key.replace("ret:", "load:", 1) if load_key not in living_jids: to_remove.append(ret_key) if len(to_remove) != 0: serv.delete(*to_remove) - log.debug('clean old jobs: %s', to_remove) + log.debug("clean old jobs: %s", to_remove) def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/sentry_return.py b/salt/returners/sentry_return.py index 791f5e4b41b..759db264fa9 100644 --- a/salt/returners/sentry_return.py +++ b/salt/returners/sentry_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Salt returner that reports execution results back to sentry. The returner will inspect the payload to identify errors and flag them as such. @@ -40,7 +40,7 @@ The tags list (optional) specifies grains items that will be used as sentry tags, allowing tagging of events in the sentry ui. To report only errors to sentry, set report_errors_only: true. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -61,127 +61,135 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'sentry' +__virtualname__ = "sentry" def __virtual__(): if not has_raven: - return False, 'Could not import sentry returner; ' \ - 'raven python client is not installed.' + return ( + False, + "Could not import sentry returner; " + "raven python client is not installed.", + ) return __virtualname__ def returner(ret): - ''' + """ Log outcome to sentry. The returner tries to identify errors and report them as such. All other messages will be reported at info level. Failed states will be appended as separate list for convenience. - ''' + """ try: _connect_sentry(_get_message(ret), ret) except Exception as err: # pylint: disable=broad-except - log.error('Can\'t run connect_sentry: %s', err, exc_info=True) + log.error("Can't run connect_sentry: %s", err, exc_info=True) def _ret_is_not_error(result): - if result.get('return') and isinstance(result['return'], dict): - result_dict = result['return'] - is_staterun = all('-' in key for key in result_dict.keys()) + if result.get("return") and isinstance(result["return"], dict): + result_dict = result["return"] + is_staterun = all("-" in key for key in result_dict.keys()) if is_staterun: failed_states = {} for state_id, state_result in six.iteritems(result_dict): - if not state_result['result']: + if not state_result["result"]: failed_states[state_id] = state_result if failed_states: - result['failed_states'] = failed_states + result["failed_states"] = failed_states return False return True - if result.get('success'): + if result.get("success"): return True return False def _get_message(ret): - if not ret.get('fun_args'): - return 'salt func: {}'.format(ret['fun']) - arg_string = ' '.join([arg for arg in ret['fun_args'] if isinstance(arg, six.string_types)]) - kwarg_string = '' - if isinstance(ret['fun_args'], list) and len(ret['fun_args']) > 0: - kwargs = ret['fun_args'][-1] + if not ret.get("fun_args"): + return "salt func: {}".format(ret["fun"]) + arg_string = " ".join( + [arg for arg in ret["fun_args"] if isinstance(arg, six.string_types)] + ) + kwarg_string = "" + if isinstance(ret["fun_args"], list) and len(ret["fun_args"]) > 0: + kwargs = ret["fun_args"][-1] if isinstance(kwargs, dict): - kwarg_string = ' '.join(sorted(['{}={}'.format(k, v) for k, v in kwargs.items() if not k.startswith('_')])) - return 'salt func: {fun} {argstr} {kwargstr}'.format(fun=ret['fun'], argstr=arg_string, kwargstr=kwarg_string).strip() + kwarg_string = " ".join( + sorted( + [ + "{}={}".format(k, v) + for k, v in kwargs.items() + if not k.startswith("_") + ] + ) + ) + return "salt func: {fun} {argstr} {kwargstr}".format( + fun=ret["fun"], argstr=arg_string, kwargstr=kwarg_string + ).strip() def _connect_sentry(message, result): - ''' + """ Connect to the Sentry server - ''' - pillar_data = __salt__['pillar.raw']() - grains = __salt__['grains.items']() - raven_config = pillar_data['raven'] - hide_pillar = raven_config.get('hide_pillar') + """ + pillar_data = __salt__["pillar.raw"]() + grains = __salt__["grains.items"]() + raven_config = pillar_data["raven"] + hide_pillar = raven_config.get("hide_pillar") sentry_data = { - 'result': result, - 'pillar': 'HIDDEN' if hide_pillar else pillar_data, - 'grains': grains - } - data = { - 'platform': 'python', - 'culprit': message, - 'level': 'error' + "result": result, + "pillar": "HIDDEN" if hide_pillar else pillar_data, + "grains": grains, } + data = {"platform": "python", "culprit": message, "level": "error"} tags = {} - if 'tags' in raven_config: - for tag in raven_config['tags']: + if "tags" in raven_config: + for tag in raven_config["tags"]: tags[tag] = grains[tag] if _ret_is_not_error(result): - data['level'] = 'info' + data["level"] = "info" - if raven_config.get('report_errors_only') and data['level'] != 'error': + if raven_config.get("report_errors_only") and data["level"] != "error": return - if raven_config.get('dsn'): - client = Client(raven_config.get('dsn'), transport=HTTPTransport) + if raven_config.get("dsn"): + client = Client(raven_config.get("dsn"), transport=HTTPTransport) else: try: servers = [] - for server in raven_config['servers']: - servers.append(server + '/api/store/') + for server in raven_config["servers"]: + servers.append(server + "/api/store/") client = Client( servers=servers, - public_key=raven_config['public_key'], - secret_key=raven_config['secret_key'], - project=raven_config['project'], - transport=HTTPTransport + public_key=raven_config["public_key"], + secret_key=raven_config["secret_key"], + project=raven_config["project"], + transport=HTTPTransport, ) except KeyError as missing_key: - log.error( - 'Sentry returner needs key \'%s\' in pillar', - missing_key - ) + log.error("Sentry returner needs key '%s' in pillar", missing_key) return try: msgid = client.capture( - 'raven.events.Message', + "raven.events.Message", message=message, data=data, extra=sentry_data, - tags=tags + tags=tags, ) - log.info('Message id %s written to sentry', msgid) + log.info("Message id %s written to sentry", msgid) except Exception as exc: # pylint: disable=broad-except - log.error('Can\'t send message to sentry: %s', exc, exc_info=True) + log.error("Can't send message to sentry: %s", exc, exc_info=True) def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/slack_returner.py b/salt/returners/slack_returner.py index b35eb9979d4..254f9aa5915 100644 --- a/salt/returners/slack_returner.py +++ b/salt/returners/slack_returner.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data via slack .. versionadded:: 2015.5.0 @@ -76,78 +76,81 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return slack --return_kwargs '{"channel": "#random"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import Python libs import pprint -import logging # pylint: disable=import-error,no-name-in-module,redefined-builtin import salt.ext.six.moves.http_client -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -# pylint: enable=import-error,no-name-in-module,redefined-builtin # Import Salt Libs import salt.returners import salt.utils.slack import salt.utils.yaml +from salt.ext.six.moves.urllib.parse import urlencode as _urlencode + +# pylint: enable=import-error,no-name-in-module,redefined-builtin + log = logging.getLogger(__name__) -__virtualname__ = 'slack' +__virtualname__ = "slack" def _get_options(ret=None): - ''' + """ Get the slack options from salt. - ''' + """ - defaults = {'channel': '#general'} + defaults = {"channel": "#general"} - attrs = {'slack_profile': 'profile', - 'channel': 'channel', - 'username': 'username', - 'as_user': 'as_user', - 'api_key': 'api_key', - 'changes': 'changes', - 'only_show_failed': 'only_show_failed', - 'yaml_format': 'yaml_format', - } + attrs = { + "slack_profile": "profile", + "channel": "channel", + "username": "username", + "as_user": "as_user", + "api_key": "api_key", + "changes": "changes", + "only_show_failed": "only_show_failed", + "yaml_format": "yaml_format", + } - profile_attr = 'slack_profile' + profile_attr = "slack_profile" - profile_attrs = {'from_jid': 'from_jid', - 'api_key': 'api_key', - 'api_version': 'api_key' - } + profile_attrs = { + "from_jid": "from_jid", + "api_key": "api_key", + "api_version": "api_key", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - profile_attr=profile_attr, - profile_attrs=profile_attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) + _options = salt.returners.get_returner_options( + __virtualname__, + ret, + attrs, + profile_attr=profile_attr, + profile_attrs=profile_attrs, + __salt__=__salt__, + __opts__=__opts__, + defaults=defaults, + ) return _options def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ -def _post_message(channel, - message, - username, - as_user, - api_key=None): - ''' +def _post_message(channel, message, username, as_user, api_key=None): + """ Send a message to a Slack room. :param channel: The room name. :param message: The message to send to the Slack room. @@ -156,22 +159,24 @@ def _post_message(channel, :param api_key: The Slack api key, if not specified in the configuration. :param api_version: The Slack api version, if not specified in the configuration. :return: Boolean if message was sent successfully. - ''' + """ parameters = dict() - parameters['channel'] = channel - parameters['username'] = username - parameters['as_user'] = as_user - parameters['text'] = '```' + message + '```' # pre-formatted, fixed-width text + parameters["channel"] = channel + parameters["username"] = username + parameters["as_user"] = as_user + parameters["text"] = "```" + message + "```" # pre-formatted, fixed-width text # Slack wants the body on POST to be urlencoded. - result = salt.utils.slack.query(function='message', - api_key=api_key, - method='POST', - header_dict={'Content-Type': 'application/x-www-form-urlencoded'}, - data=_urlencode(parameters)) + result = salt.utils.slack.query( + function="message", + api_key=api_key, + method="POST", + header_dict={"Content-Type": "application/x-www-form-urlencoded"}, + data=_urlencode(parameters), + ) - log.debug('Slack message post result: %s', result) + log.debug("Slack message post result: %s", result) if result: return True else: @@ -179,66 +184,71 @@ def _post_message(channel, def returner(ret): - ''' + """ Send an slack message with the data - ''' + """ _options = _get_options(ret) - channel = _options.get('channel') - username = _options.get('username') - as_user = _options.get('as_user') - api_key = _options.get('api_key') - changes = _options.get('changes') - only_show_failed = _options.get('only_show_failed') - yaml_format = _options.get('yaml_format') + channel = _options.get("channel") + username = _options.get("username") + as_user = _options.get("as_user") + api_key = _options.get("api_key") + changes = _options.get("changes") + only_show_failed = _options.get("only_show_failed") + yaml_format = _options.get("yaml_format") if not channel: - log.error('slack.channel not defined in salt config') + log.error("slack.channel not defined in salt config") return if not username: - log.error('slack.username not defined in salt config') + log.error("slack.username not defined in salt config") return if not as_user: - log.error('slack.as_user not defined in salt config') + log.error("slack.as_user not defined in salt config") return if not api_key: - log.error('slack.api_key not defined in salt config') + log.error("slack.api_key not defined in salt config") return if only_show_failed and changes: - log.error('cannot define both slack.changes and slack.only_show_failed in salt config') + log.error( + "cannot define both slack.changes and slack.only_show_failed in salt config" + ) return - returns = ret.get('return') + returns = ret.get("return") if changes is True: - returns = {(key, value) for key, value in returns.items() if value['result'] is not True or value['changes']} + returns = { + (key, value) + for key, value in returns.items() + if value["result"] is not True or value["changes"] + } if only_show_failed is True: - returns = {(key, value) for key, value in returns.items() if value['result'] is not True} + returns = { + (key, value) + for key, value in returns.items() + if value["result"] is not True + } if yaml_format is True: returns = salt.utils.yaml.safe_dump(returns) else: returns = pprint.pformat(returns) - message = ('id: {0}\r\n' - 'function: {1}\r\n' - 'function args: {2}\r\n' - 'jid: {3}\r\n' - 'return: {4}\r\n').format( - ret.get('id'), - ret.get('fun'), - ret.get('fun_args'), - ret.get('jid'), - returns) + message = ( + "id: {0}\r\n" + "function: {1}\r\n" + "function args: {2}\r\n" + "jid: {3}\r\n" + "return: {4}\r\n" + ).format( + ret.get("id"), ret.get("fun"), ret.get("fun_args"), ret.get("jid"), returns + ) - slack = _post_message(channel, - message, - username, - as_user, - api_key) + slack = _post_message(channel, message, username, as_user, api_key) return slack diff --git a/salt/returners/slack_webhook_return.py b/salt/returners/slack_webhook_return.py index aad1cdf656a..98dc7b521fc 100644 --- a/salt/returners/slack_webhook_return.py +++ b/salt/returners/slack_webhook_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data via Slack using Incoming Webhooks :codeauthor: `Carlos D. Álvaro <github@cdalvaro.io>` @@ -53,152 +53,164 @@ To use the alternative configuration, append '--return_config alternative' to th salt '*' test.ping --return slack_webhook --return_config alternative -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import json + # Import Python libs import logging -import json # pylint: disable=import-error,no-name-in-module,redefined-builtin import salt.ext.six.moves.http_client -from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -from salt.ext import six -from salt.ext.six.moves import map -from salt.ext.six.moves import range -# pylint: enable=import-error,no-name-in-module,redefined-builtin # Import Salt Libs import salt.returners import salt.utils.http import salt.utils.yaml +from salt.ext import six +from salt.ext.six.moves import map, range +from salt.ext.six.moves.urllib.parse import urlencode as _urlencode + +# pylint: enable=import-error,no-name-in-module,redefined-builtin + log = logging.getLogger(__name__) -__virtualname__ = 'slack_webhook' +__virtualname__ = "slack_webhook" def _get_options(ret=None): - ''' + """ Get the slack_webhook options from salt. :param ret: Salt return dictionary :return: A dictionary with options - ''' + """ defaults = { - 'success_title': '{id} | Succeeded', - 'failure_title': '{id} | Failed', - 'author_icon': '', - 'show_tasks': False + "success_title": "{id} | Succeeded", + "failure_title": "{id} | Failed", + "author_icon": "", + "show_tasks": False, } attrs = { - 'webhook': 'webhook', - 'success_title': 'success_title', - 'failure_title': 'failure_title', - 'author_icon': 'author_icon', - 'show_tasks': 'show_tasks' + "webhook": "webhook", + "success_title": "success_title", + "failure_title": "failure_title", + "author_icon": "author_icon", + "show_tasks": "show_tasks", } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) + _options = salt.returners.get_returner_options( + __virtualname__, + ret, + attrs, + __salt__=__salt__, + __opts__=__opts__, + defaults=defaults, + ) return _options def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ def _sprinkle(config_str): - ''' + """ Sprinkle with grains of salt, that is convert 'test {id} test {host} ' types of strings :param config_str: The string to be sprinkled :return: The string sprinkled - ''' - parts = [x for sub in config_str.split('{') for x in sub.split('}')] + """ + parts = [x for sub in config_str.split("{") for x in sub.split("}")] for i in range(1, len(parts), 2): - parts[i] = six.text_type(__grains__.get(parts[i], '')) - return ''.join(parts) + parts[i] = six.text_type(__grains__.get(parts[i], "")) + return "".join(parts) def _format_task(task): - ''' + """ Return a dictionary with the task ready for slack fileds :param task: The name of the task :return: A dictionary ready to be inserted in Slack fields array - ''' - return {'value': task, 'short': False} + """ + return {"value": task, "short": False} def _generate_payload(author_icon, title, report): - ''' + """ Prepare the payload for Slack :param author_icon: The url for the thumbnail to be displayed :param title: The title of the message :param report: A dictionary with the report of the Salt function :return: The payload ready for Slack - ''' + """ title = _sprinkle(title) unchanged = { - 'color': 'good', - 'title': 'Unchanged: {unchanged}'.format(unchanged=report['unchanged'].get('counter', None)) + "color": "good", + "title": "Unchanged: {unchanged}".format( + unchanged=report["unchanged"].get("counter", None) + ), } changed = { - 'color': 'warning', - 'title': 'Changed: {changed}'.format(changed=report['changed'].get('counter', None)) + "color": "warning", + "title": "Changed: {changed}".format( + changed=report["changed"].get("counter", None) + ), } - if report['changed'].get('tasks'): - changed['fields'] = list( - map(_format_task, report['changed'].get('tasks'))) + if report["changed"].get("tasks"): + changed["fields"] = list(map(_format_task, report["changed"].get("tasks"))) failed = { - 'color': 'danger', - 'title': 'Failed: {failed}'.format(failed=report['failed'].get('counter', None)) + "color": "danger", + "title": "Failed: {failed}".format( + failed=report["failed"].get("counter", None) + ), } - if report['failed'].get('tasks'): - failed['fields'] = list( - map(_format_task, report['failed'].get('tasks'))) + if report["failed"].get("tasks"): + failed["fields"] = list(map(_format_task, report["failed"].get("tasks"))) - text = 'Function: {function}\n'.format(function=report.get('function')) - if report.get('arguments'): - text += 'Function Args: {arguments}\n'.format( - arguments=str(list(map(str, report.get('arguments'))))) + text = "Function: {function}\n".format(function=report.get("function")) + if report.get("arguments"): + text += "Function Args: {arguments}\n".format( + arguments=str(list(map(str, report.get("arguments")))) + ) - text += 'JID: {jid}\n'.format(jid=report.get('jid')) - text += 'Total: {total}\n'.format(total=report.get('total')) - text += 'Duration: {duration:.2f} secs'.format( - duration=float(report.get('duration'))) + text += "JID: {jid}\n".format(jid=report.get("jid")) + text += "Total: {total}\n".format(total=report.get("total")) + text += "Duration: {duration:.2f} secs".format( + duration=float(report.get("duration")) + ) payload = { - 'attachments': [ + "attachments": [ { - 'fallback': title, - 'color': "#272727", - 'author_name': _sprinkle('{id}'), - 'author_link': _sprinkle('{localhost}'), - 'author_icon': author_icon, - 'title': 'Success: {success}'.format(success=str(report.get('success'))), - 'text': text + "fallback": title, + "color": "#272727", + "author_name": _sprinkle("{id}"), + "author_link": _sprinkle("{localhost}"), + "author_icon": author_icon, + "title": "Success: {success}".format( + success=str(report.get("success")) + ), + "text": text, }, unchanged, changed, - failed + failed, ] } @@ -206,19 +218,16 @@ def _generate_payload(author_icon, title, report): def _generate_report(ret, show_tasks): - ''' + """ Generate a report of the Salt function :param ret: The Salt return :param show_tasks: Flag to show the name of the changed and failed states :return: The report - ''' + """ - returns = ret.get('return') + returns = ret.get("return") - sorted_data = sorted( - returns.items(), - key=lambda s: s[1].get('__run_num__', 0) - ) + sorted_data = sorted(returns.items(), key=lambda s: s[1].get("__run_num__", 0)) total = 0 failed = 0 @@ -231,107 +240,98 @@ def _generate_report(ret, show_tasks): # gather stats for state, data in sorted_data: # state: module, stateid, name, function - _, stateid, _, _ = state.split('_|-') - task = '{filename}.sls | {taskname}'.format( - filename=str(data.get('__sls__')), taskname=stateid) + _, stateid, _, _ = state.split("_|-") + task = "{filename}.sls | {taskname}".format( + filename=str(data.get("__sls__")), taskname=stateid + ) - if not data.get('result', True): + if not data.get("result", True): failed += 1 failed_tasks.append(task) - if data.get('changes', {}): + if data.get("changes", {}): changed += 1 changed_tasks.append(task) total += 1 try: - duration += float(data.get('duration', 0.0)) + duration += float(data.get("duration", 0.0)) except ValueError: pass unchanged = total - failed - changed - log.debug('%s total: %s', __virtualname__, total) - log.debug('%s failed: %s', __virtualname__, failed) - log.debug('%s unchanged: %s', __virtualname__, unchanged) - log.debug('%s changed: %s', __virtualname__, changed) + log.debug("%s total: %s", __virtualname__, total) + log.debug("%s failed: %s", __virtualname__, failed) + log.debug("%s unchanged: %s", __virtualname__, unchanged) + log.debug("%s changed: %s", __virtualname__, changed) report = { - 'id': ret.get('id'), - 'success': True if failed == 0 else False, - 'total': total, - 'function': ret.get('fun'), - 'arguments': ret.get('fun_args', []), - 'jid': ret.get('jid'), - 'duration': duration / 1000, - 'unchanged': { - 'counter': unchanged - }, - 'changed': { - 'counter': changed, - 'tasks': changed_tasks if show_tasks else [] - }, - 'failed': { - 'counter': failed, - 'tasks': failed_tasks if show_tasks else [] - } + "id": ret.get("id"), + "success": True if failed == 0 else False, + "total": total, + "function": ret.get("fun"), + "arguments": ret.get("fun_args", []), + "jid": ret.get("jid"), + "duration": duration / 1000, + "unchanged": {"counter": unchanged}, + "changed": {"counter": changed, "tasks": changed_tasks if show_tasks else []}, + "failed": {"counter": failed, "tasks": failed_tasks if show_tasks else []}, } return report def _post_message(webhook, author_icon, title, report): - ''' + """ Send a message to a Slack room through a webhook :param webhook: The url of the incoming webhook :param author_icon: The thumbnail image to be displayed on the right side of the message :param title: The title of the message :param report: The report of the function state :return: Boolean if message was sent successfully - ''' + """ payload = _generate_payload(author_icon, title, report) - data = _urlencode({ - 'payload': json.dumps(payload, ensure_ascii=False) - }) + data = _urlencode({"payload": json.dumps(payload, ensure_ascii=False)}) - webhook_url = 'https://hooks.slack.com/services/{webhook}'.format(webhook=webhook) - query_result = salt.utils.http.query(webhook_url, 'POST', data=data) + webhook_url = "https://hooks.slack.com/services/{webhook}".format(webhook=webhook) + query_result = salt.utils.http.query(webhook_url, "POST", data=data) - if query_result['body'] == 'ok' or query_result['status'] <= 201: + if query_result["body"] == "ok" or query_result["status"] <= 201: return True else: - log.error('Slack incoming webhook message post result: %s', query_result) + log.error("Slack incoming webhook message post result: %s", query_result) return { - 'res': False, - 'message': query_result.get('body', query_result['status']) + "res": False, + "message": query_result.get("body", query_result["status"]), } def returner(ret): - ''' + """ Send a slack message with the data through a webhook :param ret: The Salt return :return: The result of the post - ''' + """ _options = _get_options(ret) - webhook = _options.get('webhook', None) - show_tasks = _options.get('show_tasks') - author_icon = _options.get('author_icon') + webhook = _options.get("webhook", None) + show_tasks = _options.get("show_tasks") + author_icon = _options.get("author_icon") - if not webhook or webhook is '': - log.error('%s.webhook not defined in salt config', __virtualname__) + if not webhook or webhook is "": + log.error("%s.webhook not defined in salt config", __virtualname__) return report = _generate_report(ret, show_tasks) - if report.get('success'): - title = _options.get('success_title') + if report.get("success"): + title = _options.get("success_title") else: - title = _options.get('failure_title') + title = _options.get("failure_title") slack = _post_message(webhook, author_icon, title, report) diff --git a/salt/returners/sms_return.py b/salt/returners/sms_return.py index 466693400d1..55ddf0638e4 100644 --- a/salt/returners/sms_return.py +++ b/salt/returners/sms_return.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Return data by SMS. .. versionadded:: 2015.5.0 @@ -27,8 +27,9 @@ To use the sms returner, append '--return sms' to the salt command. salt '*' test.ping --return sms -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging import salt.returners @@ -38,68 +39,64 @@ log = logging.getLogger(__name__) try: from twilio.rest import TwilioRestClient from twilio.rest.exceptions import TwilioRestException + HAS_TWILIO = True except ImportError: HAS_TWILIO = False -__virtualname__ = 'sms' +__virtualname__ = "sms" def __virtual__(): if HAS_TWILIO: return __virtualname__ - return False, 'Could not import sms returner; twilio is not installed.' + return False, "Could not import sms returner; twilio is not installed." def _get_options(ret=None): - ''' + """ Get the Twilio options from salt. - ''' - attrs = {'sid': 'sid', - 'token': 'token', - 'to': 'to', - 'from': 'from'} + """ + attrs = {"sid": "sid", "token": "token", "to": "to", "from": "from"} - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options def returner(ret): - ''' + """ Return a response in an SMS message - ''' + """ _options = _get_options(ret) - sid = _options.get('sid', None) - token = _options.get('token', None) - sender = _options.get('from', None) - receiver = _options.get('to', None) + sid = _options.get("sid", None) + token = _options.get("token", None) + sender = _options.get("from", None) + receiver = _options.get("to", None) if sid is None or token is None: - log.error('Twilio sid/authentication token missing') + log.error("Twilio sid/authentication token missing") return None if sender is None or receiver is None: - log.error('Twilio to/from fields are missing') + log.error("Twilio to/from fields are missing") return None client = TwilioRestClient(sid, token) try: message = client.messages.create( - body='Minion: {0}\nCmd: {1}\nSuccess: {2}\n\nJid: {3}'.format( - ret['id'], ret['fun'], ret['success'], ret['jid'] - ), to=receiver, from_=sender) - except TwilioRestException as e: - log.error( - 'Twilio [https://www.twilio.com/docs/errors/%s]', - e.code + body="Minion: {0}\nCmd: {1}\nSuccess: {2}\n\nJid: {3}".format( + ret["id"], ret["fun"], ret["success"], ret["jid"] + ), + to=receiver, + from_=sender, ) + except TwilioRestException as e: + log.error("Twilio [https://www.twilio.com/docs/errors/%s]", e.code) return False return True diff --git a/salt/returners/smtp_return.py b/salt/returners/smtp_return.py index ddd717ffbef..2574572a4a0 100644 --- a/salt/returners/smtp_return.py +++ b/salt/returners/smtp_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data via email The following fields can be set in the minion conf file. Fields are optional @@ -103,24 +103,27 @@ Also you need to create additional file ``/srv/salt/templates/email.j2`` with em result: {{result}} This configuration enables Salt Master to send an email when accepting or rejecting minions keys. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import logging +import os import smtplib from email.utils import formatdate +import salt.loader +import salt.returners +import salt.utils.jid + # Import Salt libs from salt.ext import six -import salt.utils.jid -import salt.returners -import salt.loader from salt.template import compile_template try: import gnupg + HAS_GNUPG = True except ImportError: HAS_GNUPG = False @@ -128,7 +131,7 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'smtp' +__virtualname__ = "smtp" def __virtual__(): @@ -136,144 +139,142 @@ def __virtual__(): def _get_options(ret=None): - ''' + """ Get the SMTP options from salt. - ''' - attrs = {'from': 'from', - 'to': 'to', - 'host': 'host', - 'port': 'port', - 'username': 'username', - 'password': 'password', - 'subject': 'subject', - 'gpgowner': 'gpgowner', - 'fields': 'fields', - 'tls': 'tls', - 'renderer': 'renderer', - 'template': 'template'} + """ + attrs = { + "from": "from", + "to": "to", + "host": "host", + "port": "port", + "username": "username", + "password": "password", + "subject": "subject", + "gpgowner": "gpgowner", + "fields": "fields", + "tls": "tls", + "renderer": "renderer", + "template": "template", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options def returner(ret): - ''' + """ Send an email with the data - ''' + """ _options = _get_options(ret) - from_addr = _options.get('from') - to_addrs = _options.get('to').split(',') - host = _options.get('host') - port = _options.get('port') - user = _options.get('username') - passwd = _options.get('password') - subject = _options.get('subject') or 'Email from Salt' - gpgowner = _options.get('gpgowner') - fields = _options.get('fields').split(',') if 'fields' in _options else [] - smtp_tls = _options.get('tls') + from_addr = _options.get("from") + to_addrs = _options.get("to").split(",") + host = _options.get("host") + port = _options.get("port") + user = _options.get("username") + passwd = _options.get("password") + subject = _options.get("subject") or "Email from Salt" + gpgowner = _options.get("gpgowner") + fields = _options.get("fields").split(",") if "fields" in _options else [] + smtp_tls = _options.get("tls") - renderer = _options.get('renderer') or 'jinja' + renderer = _options.get("renderer") or "jinja" rend = salt.loader.render(__opts__, {}) - blacklist = __opts__.get('renderer_blacklist') - whitelist = __opts__.get('renderer_whitelist') + blacklist = __opts__.get("renderer_blacklist") + whitelist = __opts__.get("renderer_whitelist") if not port: port = 25 - log.debug('SMTP port has been set to %s', port) + log.debug("SMTP port has been set to %s", port) for field in fields: if field in ret: - subject += ' {0}'.format(ret[field]) - subject = compile_template(':string:', - rend, - renderer, - blacklist, - whitelist, - input_data=subject, - **ret) + subject += " {0}".format(ret[field]) + subject = compile_template( + ":string:", rend, renderer, blacklist, whitelist, input_data=subject, **ret + ) if isinstance(subject, six.moves.StringIO): subject = subject.read() log.debug("smtp_return: Subject is '%s'", subject) - template = _options.get('template') + template = _options.get("template") if template: - content = compile_template(template, rend, renderer, blacklist, whitelist, **ret) + content = compile_template( + template, rend, renderer, blacklist, whitelist, **ret + ) else: - template = ('id: {{id}}\r\n' - 'function: {{fun}}\r\n' - 'function args: {{fun_args}}\r\n' - 'jid: {{jid}}\r\n' - 'return: {{return}}\r\n') - content = compile_template(':string:', - rend, - renderer, - blacklist, - whitelist, - input_data=template, - **ret) + template = ( + "id: {{id}}\r\n" + "function: {{fun}}\r\n" + "function args: {{fun_args}}\r\n" + "jid: {{jid}}\r\n" + "return: {{return}}\r\n" + ) + content = compile_template( + ":string:", rend, renderer, blacklist, whitelist, input_data=template, **ret + ) if gpgowner: if HAS_GNUPG: - gpg = gnupg.GPG(gnupghome=os.path.expanduser('~{0}/.gnupg'.format(gpgowner)), - options=['--trust-model always']) + gpg = gnupg.GPG( + gnupghome=os.path.expanduser("~{0}/.gnupg".format(gpgowner)), + options=["--trust-model always"], + ) encrypted_data = gpg.encrypt(content, to_addrs) if encrypted_data.ok: - log.debug('smtp_return: Encryption successful') + log.debug("smtp_return: Encryption successful") content = six.text_type(encrypted_data) else: - log.error('smtp_return: Encryption failed, only an error message will be sent') - content = 'Encryption failed, the return data was not sent.\r\n\r\n{0}\r\n{1}'.format( - encrypted_data.status, encrypted_data.stderr) + log.error( + "smtp_return: Encryption failed, only an error message will be sent" + ) + content = "Encryption failed, the return data was not sent.\r\n\r\n{0}\r\n{1}".format( + encrypted_data.status, encrypted_data.stderr + ) else: - log.error("gnupg python module is required in order to user gpgowner in smtp returner ; ignoring gpgowner configuration for now") + log.error( + "gnupg python module is required in order to user gpgowner in smtp returner ; ignoring gpgowner configuration for now" + ) if isinstance(content, six.moves.StringIO): content = content.read() - message = ('From: {0}\r\n' - 'To: {1}\r\n' - 'Date: {2}\r\n' - 'Subject: {3}\r\n' - '\r\n' - '{4}').format(from_addr, - ', '.join(to_addrs), - formatdate(localtime=True), - subject, - content) + message = ( + "From: {0}\r\n" "To: {1}\r\n" "Date: {2}\r\n" "Subject: {3}\r\n" "\r\n" "{4}" + ).format( + from_addr, ", ".join(to_addrs), formatdate(localtime=True), subject, content + ) - log.debug('smtp_return: Connecting to the server...') + log.debug("smtp_return: Connecting to the server...") server = smtplib.SMTP(host, int(port)) if smtp_tls is True: server.starttls() - log.debug('smtp_return: TLS enabled') + log.debug("smtp_return: TLS enabled") if user and passwd: server.login(user, passwd) - log.debug('smtp_return: Authenticated') + log.debug("smtp_return: Authenticated") # enable logging SMTP session after the login credentials were passed server.set_debuglevel(1) server.sendmail(from_addr, to_addrs, message) - log.debug('smtp_return: Message sent.') + log.debug("smtp_return: Message sent.") server.quit() def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) def event_return(events): - ''' + """ Return event data via SMTP - ''' + """ for event in events: - ret = event.get('data', False) + ret = event.get("data", False) if ret: returner(ret) diff --git a/salt/returners/splunk.py b/salt/returners/splunk.py index ad03096b9b5..509eab3cf77 100644 --- a/salt/returners/splunk.py +++ b/salt/returners/splunk.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Send json response data to Splunk via the HTTP Event Collector Requires the following config values to be specified in config or pillar: @@ -16,14 +16,16 @@ 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 requests import socket import time +import requests + # Import salt libs import salt.utils.json @@ -40,65 +42,76 @@ __virtualname__ = "splunk" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ def returner(ret): - ''' + """ Send a message to Splunk via the HTTP Event Collector - ''' + """ return _send_splunk(ret) def _get_options(): try: - token = __salt__['config.get']('splunk_http_forwarder:token') - indexer = __salt__['config.get']('splunk_http_forwarder:indexer') - sourcetype = __salt__['config.get']('splunk_http_forwarder:sourcetype') - index = __salt__['config.get']('splunk_http_forwarder:index') + token = __salt__["config.get"]("splunk_http_forwarder:token") + indexer = __salt__["config.get"]("splunk_http_forwarder:indexer") + sourcetype = __salt__["config.get"]("splunk_http_forwarder:sourcetype") + index = __salt__["config.get"]("splunk_http_forwarder:index") except Exception: # pylint: disable=broad-except log.error("Splunk HTTP Forwarder parameters not present in config.") return None - splunk_opts = {"token": token, "indexer": indexer, "sourcetype": sourcetype, "index": index} + splunk_opts = { + "token": token, + "indexer": indexer, + "sourcetype": sourcetype, + "index": index, + } return splunk_opts def _send_splunk(event, index_override=None, sourcetype_override=None): - ''' + """ Send the results to Splunk. Requires the Splunk HTTP Event Collector running on port 8088. This is available on Splunk Enterprise version 6.3 or higher. - ''' + """ # Get Splunk Options opts = _get_options() - log.info(str('Options: %s'), # future lint: disable=blacklisted-function - salt.utils.json.dumps(opts)) - http_event_collector_key = opts['token'] - http_event_collector_host = opts['indexer'] + log.info( + str("Options: %s"), # future lint: disable=blacklisted-function + salt.utils.json.dumps(opts), + ) + http_event_collector_key = opts["token"] + http_event_collector_host = opts["indexer"] # Set up the collector - splunk_event = http_event_collector(http_event_collector_key, http_event_collector_host) + splunk_event = http_event_collector( + http_event_collector_key, http_event_collector_host + ) # init the payload payload = {} # Set up the event metadata if index_override is None: - payload.update({"index": opts['index']}) + payload.update({"index": opts["index"]}) else: payload.update({"index": index_override}) if sourcetype_override is None: - payload.update({"sourcetype": opts['sourcetype']}) + payload.update({"sourcetype": opts["sourcetype"]}) else: payload.update({"index": sourcetype_override}) # Add the event payload.update({"event": event}) - log.info(str('Payload: %s'), # future lint: disable=blacklisted-function - salt.utils.json.dumps(payload)) + log.info( + str("Payload: %s"), # future lint: disable=blacklisted-function + salt.utils.json.dumps(payload), + ) # Fire it off splunk_event.sendEvent(payload) return True @@ -106,21 +119,23 @@ 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): - def __init__(self, - token, - http_event_server, - host="", - http_event_port='8088', - http_event_server_ssl=True, - max_bytes=_max_content_bytes): +class http_event_collector(object): + def __init__( + self, + token, + http_event_server, + host="", + http_event_port="8088", + http_event_server_ssl=True, + max_bytes=_max_content_bytes, + ): self.token = token self.batchEvents = [] self.maxByteLength = max_bytes self.currentByteLength = 0 - # Set host to specified value or default to localhostname if no value provided + # Set host to specified value or default to localhostname if no value provided if host: self.host = host else: @@ -131,10 +146,10 @@ class http_event_collector(object): # Defaults to port 8088 if port not passed if http_event_server_ssl: - buildURI = ['https://'] + buildURI = ["https://"] else: - buildURI = ['http://'] - for i in [http_event_server, ':', http_event_port, '/services/collector/event']: + buildURI = ["http://"] + for i in [http_event_server, ":", http_event_port, "/services/collector/event"]: buildURI.append(i) self.server_uri = "".join(buildURI) @@ -145,14 +160,14 @@ class http_event_collector(object): def sendEvent(self, payload, eventtime=""): # Method to immediately send an event to the http event collector - headers = {'Authorization': 'Splunk ' + self.token} + headers = {"Authorization": "Splunk " + self.token} # 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())) # Fill in local hostname if not manually populated - if 'host' not in payload: + if "host" not in payload: payload.update({"host": self.host}) # Update time value on payload if need to use system time @@ -160,10 +175,12 @@ class http_event_collector(object): data.update(payload) # send event to http event collector - r = requests.post(self.server_uri, - data=salt.utils.json.dumps(data), - headers=headers, - verify=http_event_collector_SSL_verify) + r = requests.post( + self.server_uri, + data=salt.utils.json.dumps(data), + headers=headers, + verify=http_event_collector_SSL_verify, + ) # Print debug info if flag set if http_event_collector_debug: @@ -174,7 +191,7 @@ class http_event_collector(object): # Method to store the event in a batch to flush later # Fill in local hostname if not manually populated - if 'host' not in payload: + if "host" not in payload: payload.update({"host": self.host}) serialized_payload = salt.utils.json.dumps(payload) @@ -184,7 +201,7 @@ class http_event_collector(object): self.flushBatch() # Print debug info if flag set if http_event_collector_debug: - log.debug('auto flushing') + log.debug("auto flushing") else: self.currentByteLength = self.currentByteLength + payloadLength @@ -202,7 +219,12 @@ class http_event_collector(object): # Method to flush the batch list of events if len(self.batchEvents) > 0: - headers = {'Authorization': 'Splunk '+self.token} - r = requests.post(self.server_uri, data=" ".join(self.batchEvents), headers=headers, verify=http_event_collector_SSL_verify) + headers = {"Authorization": "Splunk " + self.token} + r = requests.post( + self.server_uri, + data=" ".join(self.batchEvents), + headers=headers, + verify=http_event_collector_SSL_verify, + ) self.batchEvents = [] self.currentByteLength = 0 diff --git a/salt/returners/sqlite3_return.py b/salt/returners/sqlite3_return.py index f5b40470742..dc9cec27851 100644 --- a/salt/returners/sqlite3_return.py +++ b/salt/returners/sqlite3_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Insert minion return data into a sqlite3 database :maintainer: Mickey Malone <mickey.malone@gmail.com> @@ -79,17 +79,19 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return sqlite3 --return_kwargs '{"db": "/var/lib/salt/another-salt.db"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import datetime + # Import python libs import logging -import datetime + +import salt.returners # Import Salt libs import salt.utils.jid import salt.utils.json -import salt.returners # Import 3rd-party libs from salt.ext import six @@ -97,6 +99,7 @@ from salt.ext import six # Better safe than sorry here. Even though sqlite3 is included in python try: import sqlite3 + HAS_SQLITE3 = True except ImportError: HAS_SQLITE3 = False @@ -104,111 +107,107 @@ except ImportError: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'sqlite3' +__virtualname__ = "sqlite3" def __virtual__(): if not HAS_SQLITE3: - return False, 'Could not import sqlite3 returner; sqlite3 is not installed.' + return False, "Could not import sqlite3 returner; sqlite3 is not installed." return __virtualname__ def _get_options(ret=None): - ''' + """ Get the SQLite3 options from salt. - ''' - attrs = {'database': 'database', - 'timeout': 'timeout'} + """ + attrs = {"database": "database", "timeout": "timeout"} - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) return _options def _get_conn(ret=None): - ''' + """ Return a sqlite3 database connection - ''' + """ # Possible todo: support detect_types, isolation_level, check_same_thread, # factory, cached_statements. Do we really need to though? _options = _get_options(ret) - database = _options.get('database') - timeout = _options.get('timeout') + database = _options.get("database") + timeout = _options.get("timeout") if not database: - raise Exception( - 'sqlite3 config option "sqlite3.database" is missing') + raise Exception('sqlite3 config option "sqlite3.database" is missing') if not timeout: - raise Exception( - 'sqlite3 config option "sqlite3.timeout" is missing') - log.debug('Connecting the sqlite3 database: %s timeout: %s', database, timeout) + raise Exception('sqlite3 config option "sqlite3.timeout" is missing') + log.debug("Connecting the sqlite3 database: %s timeout: %s", database, timeout) conn = sqlite3.connect(database, timeout=float(timeout)) return conn def _close_conn(conn): - ''' + """ Close the sqlite3 database connection - ''' - log.debug('Closing the sqlite3 database connection') + """ + log.debug("Closing the sqlite3 database connection") conn.commit() conn.close() def returner(ret): - ''' + """ Insert minion return data into the sqlite3 database - ''' - log.debug('sqlite3 returner <returner> called with data: %s', ret) + """ + log.debug("sqlite3 returner <returner> called with data: %s", ret) conn = _get_conn(ret) cur = conn.cursor() - sql = '''INSERT INTO salt_returns + sql = """INSERT INTO salt_returns (fun, jid, id, fun_args, date, full_ret, success) - VALUES (:fun, :jid, :id, :fun_args, :date, :full_ret, :success)''' - cur.execute(sql, - {'fun': ret['fun'], - 'jid': ret['jid'], - 'id': ret['id'], - 'fun_args': six.text_type(ret['fun_args']) if ret.get('fun_args') else None, - 'date': six.text_type(datetime.datetime.now()), - 'full_ret': salt.utils.json.dumps(ret['return']), - 'success': ret.get('success', '')}) + VALUES (:fun, :jid, :id, :fun_args, :date, :full_ret, :success)""" + cur.execute( + sql, + { + "fun": ret["fun"], + "jid": ret["jid"], + "id": ret["id"], + "fun_args": six.text_type(ret["fun_args"]) if ret.get("fun_args") else None, + "date": six.text_type(datetime.datetime.now()), + "full_ret": salt.utils.json.dumps(ret["return"]), + "success": ret.get("success", ""), + }, + ) _close_conn(conn) def save_load(jid, load, minions=None): - ''' + """ Save the load to the specified jid - ''' - log.debug('sqlite3 returner <save_load> called jid: %s load: %s', jid, load) + """ + log.debug("sqlite3 returner <save_load> called jid: %s load: %s", jid, load) conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''INSERT INTO jids (jid, load) VALUES (:jid, :load)''' - cur.execute(sql, - {'jid': jid, - 'load': salt.utils.json.dumps(load)}) + sql = """INSERT INTO jids (jid, load) VALUES (:jid, :load)""" + cur.execute(sql, {"jid": jid, "load": salt.utils.json.dumps(load)}) _close_conn(conn) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument - ''' + """ Included for API consistency - ''' + """ def get_load(jid): - ''' + """ Return the load from a specified jid - ''' - log.debug('sqlite3 returner <get_load> called jid: %s', jid) + """ + log.debug("sqlite3 returner <get_load> called jid: %s", jid) conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT load FROM jids WHERE jid = :jid''' - cur.execute(sql, - {'jid': jid}) + sql = """SELECT load FROM jids WHERE jid = :jid""" + cur.execute(sql, {"jid": jid}) data = cur.fetchone() if data: return salt.utils.json.loads(data[0].encode()) @@ -217,40 +216,38 @@ def get_load(jid): def get_jid(jid): - ''' + """ Return the information returned from a specified jid - ''' - log.debug('sqlite3 returner <get_jid> called jid: %s', jid) + """ + log.debug("sqlite3 returner <get_jid> called jid: %s", jid) conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT id, full_ret FROM salt_returns WHERE jid = :jid''' - cur.execute(sql, - {'jid': jid}) + sql = """SELECT id, full_ret FROM salt_returns WHERE jid = :jid""" + cur.execute(sql, {"jid": jid}) data = cur.fetchone() - log.debug('query result: %s', data) + log.debug("query result: %s", data) ret = {} if data and len(data) > 1: - ret = {six.text_type(data[0]): {'return': salt.utils.json.loads(data[1])}} - log.debug('ret: %s', ret) + ret = {six.text_type(data[0]): {"return": salt.utils.json.loads(data[1])}} + log.debug("ret: %s", ret) _close_conn(conn) return ret def get_fun(fun): - ''' + """ Return a dict of the last function called for all minions - ''' - log.debug('sqlite3 returner <get_fun> called fun: %s', fun) + """ + log.debug("sqlite3 returner <get_fun> called fun: %s", fun) conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT s.id, s.full_ret, s.jid + sql = """SELECT s.id, s.full_ret, s.jid FROM salt_returns s JOIN ( SELECT MAX(jid) AS jid FROM salt_returns GROUP BY fun, id) max ON s.jid = max.jid WHERE s.fun = :fun - ''' - cur.execute(sql, - {'fun': fun}) + """ + cur.execute(sql, {"fun": fun}) data = cur.fetchall() ret = {} if data: @@ -265,13 +262,13 @@ def get_fun(fun): def get_jids(): - ''' + """ Return a list of all job ids - ''' - log.debug('sqlite3 returner <get_jids> called') + """ + log.debug("sqlite3 returner <get_jids> called") conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT jid, load FROM jids''' + sql = """SELECT jid, load FROM jids""" cur.execute(sql) data = cur.fetchall() ret = {} @@ -282,13 +279,13 @@ def get_jids(): def get_minions(): - ''' + """ Return a list of minions - ''' - log.debug('sqlite3 returner <get_minions> called') + """ + log.debug("sqlite3 returner <get_minions> called") conn = _get_conn(ret=None) cur = conn.cursor() - sql = '''SELECT DISTINCT id FROM salt_returns''' + sql = """SELECT DISTINCT id FROM salt_returns""" cur.execute(sql) data = cur.fetchall() ret = [] @@ -299,7 +296,7 @@ def get_minions(): def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/syslog_return.py b/salt/returners/syslog_return.py index 08014259b82..d994ee4166e 100644 --- a/salt/returners/syslog_return.py +++ b/salt/returners/syslog_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return data to the host operating system's syslog facility To use the syslog returner, append '--return syslog' to the @@ -85,82 +85,84 @@ To override individual configuration items, append the $MaxMessageSize config parameter. Please consult your syslog implmentation's documentation to determine how to adjust this limit. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import python libs -try: - import syslog - HAS_SYSLOG = True -except ImportError: - HAS_SYSLOG = False +import salt.returners # Import Salt libs import salt.utils.jid import salt.utils.json -import salt.returners from salt.ext import six +# Import python libs +try: + import syslog + + HAS_SYSLOG = True +except ImportError: + HAS_SYSLOG = False + + log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'syslog' +__virtualname__ = "syslog" def _get_options(ret=None): - ''' + """ Get the returner options from salt. - ''' + """ - defaults = {'level': 'LOG_INFO', - 'facility': 'LOG_USER', - 'options': [] - } + defaults = {"level": "LOG_INFO", "facility": "LOG_USER", "options": []} - attrs = {'level': 'level', - 'facility': 'facility', - 'tag': 'tag', - 'options': 'options' - } + attrs = { + "level": "level", + "facility": "facility", + "tag": "tag", + "options": "options", + } - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__, - defaults=defaults) + _options = salt.returners.get_returner_options( + __virtualname__, + ret, + attrs, + __salt__=__salt__, + __opts__=__opts__, + defaults=defaults, + ) return _options def _verify_options(options): - ''' + """ Verify options and log warnings Returns True if all options can be verified, otherwise False - ''' + """ # sanity check all vals used for bitwise operations later - bitwise_args = [('level', options['level']), - ('facility', options['facility']) - ] - bitwise_args.extend([('option', x) for x in options['options']]) + bitwise_args = [("level", options["level"]), ("facility", options["facility"])] + bitwise_args.extend([("option", x) for x in options["options"]]) for opt_name, opt in bitwise_args: if not hasattr(syslog, opt): - log.error('syslog has no attribute %s', opt) + log.error("syslog has no attribute %s", opt) return False if not isinstance(getattr(syslog, opt), int): - log.error('%s is not a valid syslog %s', opt, opt_name) + log.error("%s is not a valid syslog %s", opt, opt_name) return False # Sanity check tag - if 'tag' in options: - if not isinstance(options['tag'], six.string_types): - log.error('tag must be a string') + if "tag" in options: + if not isinstance(options["tag"], six.string_types): + log.error("tag must be a string") return False - if len(options['tag']) > 32: - log.error('tag size is limited to 32 characters') + if len(options["tag"]) > 32: + log.error("tag size is limited to 32 characters") return False return True @@ -168,14 +170,14 @@ def _verify_options(options): def __virtual__(): if not HAS_SYSLOG: - return False, 'Could not import syslog returner; syslog is not installed.' + return False, "Could not import syslog returner; syslog is not installed." return __virtualname__ def returner(ret): - ''' + """ Return data to the local syslog - ''' + """ _options = _get_options(ret) @@ -183,17 +185,19 @@ def returner(ret): return # Get values from syslog module - level = getattr(syslog, _options['level']) - facility = getattr(syslog, _options['facility']) + level = getattr(syslog, _options["level"]) + facility = getattr(syslog, _options["facility"]) # parse for syslog options logoption = 0 - for opt in _options['options']: + for opt in _options["options"]: logoption = logoption | getattr(syslog, opt) # Open syslog correctly based on options and tag - if 'tag' in _options: - syslog.openlog(ident=salt.utils.stringutils.to_str(_options['tag']), logoption=logoption) + if "tag" in _options: + syslog.openlog( + ident=salt.utils.stringutils.to_str(_options["tag"]), logoption=logoption + ) else: syslog.openlog(logoption=logoption) @@ -204,9 +208,8 @@ def returner(ret): syslog.closelog() -def prep_jid(nocache=False, - passed_jid=None): # pylint: disable=unused-argument - ''' +def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/telegram_return.py b/salt/returners/telegram_return.py index 42f266ae392..d6055b775f2 100644 --- a/salt/returners/telegram_return.py +++ b/salt/returners/telegram_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data via Telegram. The following fields can be set in the minion conf file:: @@ -21,7 +21,7 @@ To use the Telegram return, append '--return telegram' to the salt command. salt '*' test.ping --return telegram -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -32,68 +32,62 @@ import salt.returners log = logging.getLogger(__name__) -__virtualname__ = 'telegram' +__virtualname__ = "telegram" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ def _get_options(ret=None): - ''' + """ Get the Telegram options from salt. :param ret: The data to be sent. :return: Dictionary containing the data and options needed to send them to telegram. - ''' - attrs = {'chat_id': 'chat_id', - 'token': 'token'} + """ + attrs = {"chat_id": "chat_id", "token": "token"} - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - __salt__=__salt__, - __opts__=__opts__) - log.debug('Options: %s', _options) + _options = salt.returners.get_returner_options( + __virtualname__, ret, attrs, __salt__=__salt__, __opts__=__opts__ + ) + log.debug("Options: %s", _options) return _options def returner(ret): - ''' + """ Send a Telegram message with the data. :param ret: The data to be sent. :return: Boolean if message was sent successfully. - ''' + """ _options = _get_options(ret) - chat_id = _options.get('chat_id') - token = _options.get('token') + chat_id = _options.get("chat_id") + token = _options.get("token") if not chat_id: - log.error('telegram.chat_id not defined in salt config') + log.error("telegram.chat_id not defined in salt config") if not token: - log.error('telegram.token not defined in salt config') + log.error("telegram.token not defined in salt config") - returns = ret.get('return') + returns = ret.get("return") - message = ('id: {0}\r\n' - 'function: {1}\r\n' - 'function args: {2}\r\n' - 'jid: {3}\r\n' - 'return: {4}\r\n').format( - ret.get('id'), - ret.get('fun'), - ret.get('fun_args'), - ret.get('jid'), - returns) + message = ( + "id: {0}\r\n" + "function: {1}\r\n" + "function args: {2}\r\n" + "jid: {3}\r\n" + "return: {4}\r\n" + ).format( + ret.get("id"), ret.get("fun"), ret.get("fun_args"), ret.get("jid"), returns + ) - return __salt__['telegram.post_message'](message, - chat_id=chat_id, - token=token) + return __salt__["telegram.post_message"](message, chat_id=chat_id, token=token) diff --git a/salt/returners/xmpp_return.py b/salt/returners/xmpp_return.py index 2cd066e7b34..672819de08f 100644 --- a/salt/returners/xmpp_return.py +++ b/salt/returners/xmpp_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data via xmpp :depends: sleekxmpp >= 1.3.1 @@ -66,7 +66,7 @@ To override individual configuration items, append --return_kwargs '{"key:": "va salt '*' test.ping --return xmpp --return_kwargs '{"recipient": "someone-else@xmpp.example.com"}' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -80,60 +80,69 @@ from salt.utils.versions import LooseVersion as _LooseVersion HAS_LIBS = False try: from sleekxmpp import ClientXMPP as _ClientXMPP # pylint: disable=import-error + HAS_LIBS = True except ImportError: + class _ClientXMPP(object): - ''' + """ Fake class in order not to raise errors - ''' + """ + log = logging.getLogger(__name__) -__virtualname__ = 'xmpp' +__virtualname__ = "xmpp" def _get_options(ret=None): - ''' + """ Get the xmpp options from salt. - ''' - attrs = {'xmpp_profile': 'profile', - 'from_jid': 'jid', - 'password': 'password', - 'recipient_jid': 'recipient'} + """ + attrs = { + "xmpp_profile": "profile", + "from_jid": "jid", + "password": "password", + "recipient_jid": "recipient", + } - profile_attr = 'xmpp_profile' + profile_attr = "xmpp_profile" - profile_attrs = {'from_jid': 'jid', - 'password': 'password'} + profile_attrs = {"from_jid": "jid", "password": "password"} - _options = salt.returners.get_returner_options(__virtualname__, - ret, - attrs, - profile_attr=profile_attr, - profile_attrs=profile_attrs, - __salt__=__salt__, - __opts__=__opts__) + _options = salt.returners.get_returner_options( + __virtualname__, + ret, + attrs, + profile_attr=profile_attr, + profile_attrs=profile_attrs, + __salt__=__salt__, + __opts__=__opts__, + ) return _options def __virtual__(): - ''' + """ Only load this module if right version of sleekxmpp is installed on this minion. - ''' - min_version = '1.3.1' + """ + min_version = "1.3.1" if HAS_LIBS: import sleekxmpp # pylint: disable=3rd-party-module-not-gated + # Certain XMPP functionaility we're using doesn't work with versions under 1.3.1 sleekxmpp_version = _LooseVersion(sleekxmpp.__version__) valid_version = _LooseVersion(min_version) if sleekxmpp_version >= valid_version: return __virtualname__ - return False, 'Could not import xmpp returner; sleekxmpp python client is not ' \ - 'installed or is older than version \'{0}\'.'.format(min_version) + return ( + False, + "Could not import xmpp returner; sleekxmpp python client is not " + "installed or is older than version '{0}'.".format(min_version), + ) class SendMsgBot(_ClientXMPP): - def __init__(self, jid, password, recipient, msg): # PyLint wrongly reports an error when calling super, hence the above # disable call @@ -142,55 +151,56 @@ class SendMsgBot(_ClientXMPP): self.recipient = recipient self.msg = msg - self.add_event_handler('session_start', self.start) + self.add_event_handler("session_start", self.start) def start(self, event): self.send_presence() - self.send_message(mto=self.recipient, - mbody=self.msg, - mtype='chat') + self.send_message(mto=self.recipient, mbody=self.msg, mtype="chat") self.disconnect(wait=True) def returner(ret): - ''' + """ Send an xmpp message with the data - ''' + """ _options = _get_options(ret) - from_jid = _options.get('from_jid') - password = _options.get('password') - recipient_jid = _options.get('recipient_jid') + from_jid = _options.get("from_jid") + password = _options.get("password") + recipient_jid = _options.get("recipient_jid") if not from_jid: - log.error('xmpp.jid not defined in salt config') + log.error("xmpp.jid not defined in salt config") return if not password: - log.error('xmpp.password not defined in salt config') + log.error("xmpp.password not defined in salt config") return if not recipient_jid: - log.error('xmpp.recipient not defined in salt config') + log.error("xmpp.recipient not defined in salt config") return - message = ('id: {0}\r\n' - 'function: {1}\r\n' - 'function args: {2}\r\n' - 'jid: {3}\r\n' - 'return: {4}\r\n').format( - ret.get('id'), - ret.get('fun'), - ret.get('fun_args'), - ret.get('jid'), - pprint.pformat(ret.get('return'))) + message = ( + "id: {0}\r\n" + "function: {1}\r\n" + "function args: {2}\r\n" + "jid: {3}\r\n" + "return: {4}\r\n" + ).format( + ret.get("id"), + ret.get("fun"), + ret.get("fun_args"), + ret.get("jid"), + pprint.pformat(ret.get("return")), + ) xmpp = SendMsgBot(from_jid, password, recipient_jid, message) - xmpp.register_plugin('xep_0030') # Service Discovery - xmpp.register_plugin('xep_0199') # XMPP Ping + xmpp.register_plugin("xep_0030") # Service Discovery + xmpp.register_plugin("xep_0199") # XMPP Ping if xmpp.connect(): xmpp.process(block=True) diff --git a/salt/returners/zabbix_return.py b/salt/returners/zabbix_return.py index 2f4f1dc80f3..0936b53f546 100644 --- a/salt/returners/zabbix_return.py +++ b/salt/returners/zabbix_return.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data to Zabbix The following Type: "Zabbix trapper" with "Type of information" Text items are required: @@ -15,32 +15,36 @@ To use the Zabbix returner, append '--return zabbix' to the salt command. ex: .. code-block:: bash salt '*' test.ping --return zabbix -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt libs from salt.ext import six - # Define the module's virtual name -__virtualname__ = 'zabbix' +__virtualname__ = "zabbix" def __virtual__(): if zbx(): return True - return False, 'Zabbix returner: No zabbix_sender and zabbix_agend.conf found.' + return False, "Zabbix returner: No zabbix_sender and zabbix_agend.conf found." def zbx(): - if os.path.exists('/usr/local/zabbix/bin/zabbix_sender') and os.path.exists('/usr/local/zabbix/etc/zabbix_agentd.conf'): - zabbix_sender = '/usr/local/zabbix/bin/zabbix_sender' - zabbix_config = '/usr/local/zabbix/etc/zabbix_agentd.conf' + if os.path.exists("/usr/local/zabbix/bin/zabbix_sender") and os.path.exists( + "/usr/local/zabbix/etc/zabbix_agentd.conf" + ): + zabbix_sender = "/usr/local/zabbix/bin/zabbix_sender" + zabbix_config = "/usr/local/zabbix/etc/zabbix_agentd.conf" return {"sender": zabbix_sender, "config": zabbix_config} - elif os.path.exists('/usr/bin/zabbix_sender') and os.path.exists('/etc/zabbix/zabbix_agentd.conf'): + elif os.path.exists("/usr/bin/zabbix_sender") and os.path.exists( + "/etc/zabbix/zabbix_agentd.conf" + ): zabbix_sender = "/usr/bin/zabbix_sender" zabbix_config = "/etc/zabbix/zabbix_agentd.conf" return {"sender": zabbix_sender, "config": zabbix_config} @@ -49,29 +53,48 @@ def zbx(): def zabbix_send(key, output): - cmd = zbx()['sender'] + " -c " + zbx()['config'] + " -k " + key + " -o \"" + output +"\"" - __salt__['cmd.shell'](cmd) + cmd = ( + zbx()["sender"] + + " -c " + + zbx()["config"] + + " -k " + + key + + ' -o "' + + output + + '"' + ) + __salt__["cmd.shell"](cmd) def save_load(jid, load, minions=None): - ''' + """ Included for API consistency - ''' + """ def returner(ret): changes = False errors = False - job_minion_id = ret['id'] + job_minion_id = ret["id"] - if type(ret['return']) is dict: - for state, item in six.iteritems(ret['return']): - if 'comment' in item and 'name' in item and item['result'] is False: + if type(ret["return"]) is dict: + for state, item in six.iteritems(ret["return"]): + if "comment" in item and "name" in item and item["result"] is False: errors = True - zabbix_send("salt.trap.high", 'SALT:\nname: {0}\ncomment: {1}'.format(item['name'], item['comment'])) - elif 'comment' in item and 'name' in item and item['changes']: + zabbix_send( + "salt.trap.high", + "SALT:\nname: {0}\ncomment: {1}".format( + item["name"], item["comment"] + ), + ) + elif "comment" in item and "name" in item and item["changes"]: changes = True - zabbix_send("salt.trap.warning", 'SALT:\nname: {0}\ncomment: {1}'.format(item['name'], item['comment'])) + zabbix_send( + "salt.trap.warning", + "SALT:\nname: {0}\ncomment: {1}".format( + item["name"], item["comment"] + ), + ) if not changes and not errors: - zabbix_send("salt.trap.info", 'SALT {0} OK'.format(job_minion_id)) + zabbix_send("salt.trap.info", "SALT {0} OK".format(job_minion_id)) diff --git a/salt/roster/__init__.py b/salt/roster/__init__.py index 40dfe189eb7..58914641823 100644 --- a/salt/roster/__init__.py +++ b/salt/roster/__init__.py @@ -1,54 +1,53 @@ # -*- coding: utf-8 -*- -''' +""" Generate roster data, this data is used by non-minion devices which need to be hit from the master rather than acting as an independent entity. This covers hitting minions without zeromq in place via an ssh agent, and connecting to systems that cannot or should not host a minion agent. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging +import os + # Import salt libs import salt.loader import salt.syspaths - -import os -import logging from salt.ext import six log = logging.getLogger(__name__) def get_roster_file(options): - ''' + """ Find respective roster file. :param options: :return: - ''' + """ template = None # The __disable_custom_roster is always True if Salt SSH Client comes # from Salt API. In that case no way to define own 'roster_file', instead # this file needs to be chosen from already validated rosters # (see /etc/salt/master config). - if options.get('__disable_custom_roster') and options.get('roster_file'): - roster = options.get('roster_file').strip('/') - for roster_location in options.get('rosters'): + if options.get("__disable_custom_roster") and options.get("roster_file"): + roster = options.get("roster_file").strip("/") + for roster_location in options.get("rosters"): r_file = os.path.join(roster_location, roster) if os.path.isfile(r_file): template = r_file break - del options['roster_file'] + del options["roster_file"] if not template: - if options.get('roster_file'): - template = options.get('roster_file') - elif 'config_dir' in options.get('__master_opts__', {}): - template = os.path.join(options['__master_opts__']['config_dir'], - 'roster') - elif 'config_dir' in options: - template = os.path.join(options['config_dir'], 'roster') + if options.get("roster_file"): + template = options.get("roster_file") + elif "config_dir" in options.get("__master_opts__", {}): + template = os.path.join(options["__master_opts__"]["config_dir"], "roster") + elif "config_dir" in options: + template = os.path.join(options["config_dir"], "roster") else: - template = os.path.join(salt.syspaths.CONFIG_DIR, 'roster') + template = os.path.join(salt.syspaths.CONFIG_DIR, "roster") if not os.path.isfile(template): raise IOError('Roster file "{0}" not found'.format(template)) @@ -60,53 +59,54 @@ def get_roster_file(options): class Roster(object): - ''' + """ Used to manage a roster of minions allowing the master to become outwardly minion aware - ''' - def __init__(self, opts, backends='flat'): + """ + + def __init__(self, opts, backends="flat"): self.opts = opts if isinstance(backends, list): self.backends = backends elif isinstance(backends, six.string_types): - self.backends = backends.split(',') + self.backends = backends.split(",") else: self.backends = backends if not backends: - self.backends = ['flat'] + self.backends = ["flat"] utils = salt.loader.utils(self.opts) runner = salt.loader.runner(self.opts, utils=utils) self.rosters = salt.loader.roster(self.opts, runner=runner, utils=utils) def _gen_back(self): - ''' + """ Return a list of loaded roster backends - ''' + """ back = set() if self.backends: for backend in self.backends: - fun = '{0}.targets'.format(backend) + fun = "{0}.targets".format(backend) if fun in self.rosters: back.add(backend) return back return sorted(back) def targets(self, tgt, tgt_type): - ''' + """ Return a dict of {'id': {'ipv4': <ipaddr>}} data sets to be used as targets given the passed tgt and tgt_type - ''' + """ targets = {} for back in self._gen_back(): - f_str = '{0}.targets'.format(back) + f_str = "{0}.targets".format(back) if f_str not in self.rosters: continue try: targets.update(self.rosters[f_str](tgt, tgt_type)) except salt.exceptions.SaltRenderError as exc: - log.error('Unable to render roster file: %s', exc) + log.error("Unable to render roster file: %s", exc) except IOError as exc: log.error("Can't access roster for backend %s: %s", back, exc) - log.debug('Matched minions: %s', targets) + log.debug("Matched minions: %s", targets) return targets diff --git a/salt/roster/ansible.py b/salt/roster/ansible.py index f4a2a23e0b1..11e41ebc24b 100644 --- a/salt/roster/ansible.py +++ b/salt/roster/ansible.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Read in an Ansible inventory file or script Flat inventory files should be in the regular ansible inventory format. @@ -86,9 +86,10 @@ This is the format that an inventory script needs to output to work with ansible True Any of the [groups] or direct hostnames will return. The 'all' is special, and returns everything. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import fnmatch @@ -97,51 +98,60 @@ import salt.utils.path from salt.roster import get_roster_file CONVERSION = { - 'ansible_ssh_host': 'host', - 'ansible_ssh_port': 'port', - 'ansible_ssh_user': 'user', - 'ansible_ssh_pass': 'passwd', - 'ansible_sudo_pass': 'sudo', - 'ansible_ssh_private_key_file': 'priv' + "ansible_ssh_host": "host", + "ansible_ssh_port": "port", + "ansible_ssh_user": "user", + "ansible_ssh_pass": "passwd", + "ansible_sudo_pass": "sudo", + "ansible_ssh_private_key_file": "priv", } -__virtualname__ = 'ansible' +__virtualname__ = "ansible" def __virtual__(): - return salt.utils.path.which('ansible-inventory') and __virtualname__, 'Install `ansible` to use inventory' + return ( + salt.utils.path.which("ansible-inventory") and __virtualname__, + "Install `ansible` to use inventory", + ) -def targets(tgt, tgt_type='glob', **kwargs): - ''' +def targets(tgt, tgt_type="glob", **kwargs): + """ Return the targets from the ansible inventory_file Default: /etc/salt/roster - ''' - inventory = __runner__['salt.cmd']('cmd.run', 'ansible-inventory -i {0} --list'.format(get_roster_file(__opts__))) - __context__['inventory'] = __utils__['json.loads'](__utils__['stringutils.to_str'](inventory)) + """ + inventory = __runner__["salt.cmd"]( + "cmd.run", "ansible-inventory -i {0} --list".format(get_roster_file(__opts__)) + ) + __context__["inventory"] = __utils__["json.loads"]( + __utils__["stringutils.to_str"](inventory) + ) - if tgt_type == 'glob': - hosts = [host for host in _get_hosts_from_group('all') if fnmatch.fnmatch(host, tgt)] - elif tgt_type == 'nodegroup': + if tgt_type == "glob": + hosts = [ + host for host in _get_hosts_from_group("all") if fnmatch.fnmatch(host, tgt) + ] + elif tgt_type == "nodegroup": hosts = _get_hosts_from_group(tgt) return {host: _get_hostvars(host) for host in hosts} def _get_hosts_from_group(group): - inventory = __context__['inventory'] - hosts = [host for host in inventory[group].get('hosts', [])] - for child in inventory[group].get('children', []): + inventory = __context__["inventory"] + hosts = [host for host in inventory[group].get("hosts", [])] + for child in inventory[group].get("children", []): hosts.extend(_get_hosts_from_group(child)) return hosts def _get_hostvars(host): - hostvars = __context__['inventory']['_meta'].get('hostvars', {}).get(host, {}) - ret = copy.deepcopy(__opts__.get('roster_defaults', {})) + hostvars = __context__["inventory"]["_meta"].get("hostvars", {}).get(host, {}) + ret = copy.deepcopy(__opts__.get("roster_defaults", {})) for value in CONVERSION: if value in hostvars: ret[CONVERSION[value]] = hostvars.pop(value) - ret['minion_opts'] = hostvars - if 'host' not in ret: - ret['host'] = host + ret["minion_opts"] = hostvars + if "host" not in ret: + ret["host"] = host return ret diff --git a/salt/roster/cache.py b/salt/roster/cache.py index f1c00c94e16..324cdc39c1d 100644 --- a/salt/roster/cache.py +++ b/salt/roster/cache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The ``cache`` roster provides a flexible interface to the Salt Masters' minion cache to access regular minions over ``salt-ssh``. @@ -92,34 +92,36 @@ This should be especially useful for the other roster keys: - salt:ssh:private_key - ssh:auth:private_key -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import copy import logging import re -import copy + +import salt.cache # Import Salt libs import salt.utils.data import salt.utils.minions -import salt.cache from salt._compat import ipaddress from salt.ext import six log = logging.getLogger(__name__) -def targets(tgt, tgt_type='glob', **kwargs): # pylint: disable=W0613 - ''' +def targets(tgt, tgt_type="glob", **kwargs): # pylint: disable=W0613 + """ Return the targets from the Salt Masters' minion cache. All targets and matchers are supported. The resulting roster can be configured using ``roster_order`` and ``roster_default``. - ''' + """ minions = salt.utils.minions.CkMinions(__opts__) _res = minions.check_minions(tgt, tgt_type) - minions = _res['minions'] + minions = _res["minions"] ret = {} if not minions: @@ -128,9 +130,10 @@ def targets(tgt, tgt_type='glob', **kwargs): # pylint: disable=W0613 cache = salt.cache.Cache(__opts__) - roster_order = __opts__.get('roster_order', { - 'host': ('ipv6-private', 'ipv6-global', 'ipv4-private', 'ipv4-public') - }) + roster_order = __opts__.get( + "roster_order", + {"host": ("ipv6-private", "ipv6-global", "ipv4-private", "ipv4-public")}, + ) ret = {} for minion_id in minions: @@ -139,7 +142,7 @@ def targets(tgt, tgt_type='glob', **kwargs): # pylint: disable=W0613 except LookupError: continue - minion_res = copy.deepcopy(__opts__.get('roster_defaults', {})) + minion_res = copy.deepcopy(__opts__.get("roster_defaults", {})) for param, order in roster_order.items(): if not isinstance(order, (list, tuple)): order = [order] @@ -149,37 +152,39 @@ def targets(tgt, tgt_type='glob', **kwargs): # pylint: disable=W0613 minion_res[param] = kres break - if 'host' in minion_res: + if "host" in minion_res: ret[minion_id] = minion_res else: - log.warning('Could not determine host information for minion %s', minion_id) + log.warning("Could not determine host information for minion %s", minion_id) - log.debug('Roster lookup result: %s', ret) + log.debug("Roster lookup result: %s", ret) return ret def _load_minion(minion_id, cache): - data_minion, grains, pillar = salt.utils.minions.get_minion_data(minion_id, __opts__) + data_minion, grains, pillar = salt.utils.minions.get_minion_data( + minion_id, __opts__ + ) if minion_id != data_minion: - log.error('Asked for minion %s, got %s', minion_id, data_minion) + log.error("Asked for minion %s, got %s", minion_id, data_minion) raise LookupError if not grains: - log.warning('No grain data for minion id %s', minion_id) + log.warning("No grain data for minion id %s", minion_id) grains = {} if not pillar: - log.warning('No pillar data for minion id %s', minion_id) + log.warning("No pillar data for minion id %s", minion_id) pillar = {} addrs = { - 4: sorted([ipaddress.IPv4Address(addr) for addr in grains.get('ipv4', [])]), - 6: sorted([ipaddress.IPv6Address(addr) for addr in grains.get('ipv6', [])]) + 4: sorted([ipaddress.IPv4Address(addr) for addr in grains.get("ipv4", [])]), + 6: sorted([ipaddress.IPv6Address(addr) for addr in grains.get("ipv6", [])]), } - mine = cache.fetch('minions/{0}'.format(minion_id), 'mine') + mine = cache.fetch("minions/{0}".format(minion_id), "mine") return grains, pillar, addrs, mine @@ -201,32 +206,28 @@ def _data_lookup(ref, lookup): def _minion_lookup(minion_id, key, minion): grains, pillar, addrs, mine = minion - if key == 'id': + if key == "id": # Just paste in the minion ID return minion_id elif isinstance(key, dict): # Lookup the key in the dict for data_id, lookup in key.items(): - ref = { - 'pillar': pillar, - 'grain': grains, - 'mine': mine, - }[data_id] + ref = {"pillar": pillar, "grain": grains, "mine": mine}[data_id] for k in _data_lookup(ref, lookup): if k: return k return None - elif key.startswith('sdb://'): + elif key.startswith("sdb://"): # It's a Salt SDB url - return salt['sdb.get'](key) - elif re.match(r'^[0-9a-fA-F:./]+$', key): + return salt["sdb.get"](key) + elif re.match(r"^[0-9a-fA-F:./]+$", key): # It smells like a CIDR block try: net = ipaddress.ip_network(key, strict=True) except ValueError: - log.error('%s is an invalid CIDR network', net) + log.error("%s is an invalid CIDR network", net) return None for addr in addrs[net.version]: @@ -235,14 +236,18 @@ def _minion_lookup(minion_id, key, minion): else: # Take the addresses from the grains and filter them filters = { - 'global': lambda addr: addr.is_global if addr.version == 6 else not addr.is_private, - 'public': lambda addr: not addr.is_private, - 'private': lambda addr: addr.is_private and not addr.is_loopback and not addr.is_link_local, - 'local': lambda addr: addr.is_loopback, + "global": lambda addr: addr.is_global + if addr.version == 6 + else not addr.is_private, + "public": lambda addr: not addr.is_private, + "private": lambda addr: addr.is_private + and not addr.is_loopback + and not addr.is_link_local, + "local": lambda addr: addr.is_loopback, } ip_vers = [4, 6] - if key.startswith('ipv'): + if key.startswith("ipv"): ip_vers = [int(key[3])] key = key[5:] @@ -252,4 +257,6 @@ def _minion_lookup(minion_id, key, minion): if filters[key](addr): return six.text_type(addr) except KeyError: - raise KeyError('Invalid filter {0} specified in roster_order'.format(key)) + raise KeyError( + "Invalid filter {0} specified in roster_order".format(key) + ) diff --git a/salt/roster/cloud.py b/salt/roster/cloud.py index ab7a49b3b4f..9b92433632d 100644 --- a/salt/roster/cloud.py +++ b/salt/roster/cloud.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use the cloud cache on the master to derive IPv4 addresses based on minion ID. This roster requires that the minion in question was created using at least the @@ -16,41 +16,46 @@ usually located at /etc/salt/cloud. For example, add the following: ssh_username: my_user sudo: True -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import copy +import os + +import salt.config # Import Salt libs import salt.loader import salt.utils.cloud import salt.utils.validate.net -import salt.config from salt.ext.six import string_types -def targets(tgt, tgt_type='glob', **kwargs): # pylint: disable=W0613 - ''' +def targets(tgt, tgt_type="glob", **kwargs): # pylint: disable=W0613 + """ Return the targets from the flat yaml file, checks opts for location but defaults to /etc/salt/roster - ''' + """ ret = {} cloud_opts = salt.config.cloud_config( - os.path.join(os.path.dirname(__opts__['conf_file']), 'cloud') + os.path.join(os.path.dirname(__opts__["conf_file"]), "cloud") ) - minions = __runner__['cache.cloud'](tgt) + minions = __runner__["cache.cloud"](tgt) for minion_id, full_info in minions.items(): - profile, provider = full_info.get('profile', None), full_info.get('provider', None) + profile, provider = ( + full_info.get("profile", None), + full_info.get("provider", None), + ) vm_ = { - 'driver': provider, - 'profile': profile, + "driver": provider, + "profile": profile, } - public_ips = full_info.get('public_ips', []) - private_ips = full_info.get('private_ips', []) + public_ips = full_info.get("public_ips", []) + private_ips = full_info.get("private_ips", []) ip_list = [] for item in (public_ips, private_ips): if isinstance(item, list): @@ -58,56 +63,54 @@ def targets(tgt, tgt_type='glob', **kwargs): # pylint: disable=W0613 elif isinstance(item, string_types): ip_list.append(item) - roster_order = __opts__.get('roster_order', ( - 'public', 'private', 'local' - )) + roster_order = __opts__.get("roster_order", ("public", "private", "local")) preferred_ip = extract_ipv4(roster_order, ip_list) - ret[minion_id] = copy.deepcopy(__opts__.get('roster_defaults', {})) - ret[minion_id].update({'host': preferred_ip}) + ret[minion_id] = copy.deepcopy(__opts__.get("roster_defaults", {})) + ret[minion_id].update({"host": preferred_ip}) ssh_username = salt.utils.cloud.ssh_usernames(vm_, cloud_opts) if isinstance(ssh_username, string_types): - ret[minion_id]['user'] = ssh_username + ret[minion_id]["user"] = ssh_username elif isinstance(ssh_username, list): - if ssh_username[0] != 'root': - ret[minion_id]['user'] = ssh_username[0] + if ssh_username[0] != "root": + ret[minion_id]["user"] = ssh_username[0] password = salt.config.get_cloud_config_value( - 'ssh_password', vm_, cloud_opts, search_global=False, default=None + "ssh_password", vm_, cloud_opts, search_global=False, default=None ) if password: - ret[minion_id]['password'] = password + ret[minion_id]["password"] = password key_filename = salt.config.get_cloud_config_value( - 'private_key', vm_, cloud_opts, search_global=False, default=None + "private_key", vm_, cloud_opts, search_global=False, default=None ) if key_filename: - ret[minion_id]['priv'] = key_filename + ret[minion_id]["priv"] = key_filename sudo = salt.config.get_cloud_config_value( - 'sudo', vm_, cloud_opts, search_global=False, default=None + "sudo", vm_, cloud_opts, search_global=False, default=None ) if sudo: - ret[minion_id]['sudo'] = sudo + ret[minion_id]["sudo"] = sudo return ret def extract_ipv4(roster_order, ipv4): - ''' + """ Extract the preferred IP address from the ipv4 grain - ''' + """ for ip_type in roster_order: for ip_ in ipv4: - if ':' in ip_: + if ":" in ip_: continue if not salt.utils.validate.net.ipv4_addr(ip_): continue - if ip_type == 'local' and ip_.startswith('127.'): + if ip_type == "local" and ip_.startswith("127."): return ip_ - elif ip_type == 'private' and not salt.utils.cloud.is_public_ip(ip_): + elif ip_type == "private" and not salt.utils.cloud.is_public_ip(ip_): return ip_ - elif ip_type == 'public' and salt.utils.cloud.is_public_ip(ip_): + elif ip_type == "public" and salt.utils.cloud.is_public_ip(ip_): return ip_ return None diff --git a/salt/roster/clustershell.py b/salt/roster/clustershell.py index 77d5b118a8c..c319ca2b4ce 100644 --- a/salt/roster/clustershell.py +++ b/salt/roster/clustershell.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This roster resolves hostname in a pdsh/clustershell style. :depends: clustershell, https://github.com/cea-hpc/clustershell @@ -10,12 +10,14 @@ When you want to use host globs for target matching, use ``--roster clustershell salt-ssh --roster clustershell 'server_[1-10,21-30],test_server[5,7,9]' test.ping -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import socket + import copy +import socket + from salt.ext import six from salt.ext.six.moves import map # pylint: disable=import-error,redefined-builtin @@ -23,37 +25,37 @@ REQ_ERROR = None try: from ClusterShell.NodeSet import NodeSet except (ImportError, OSError) as e: - REQ_ERROR = 'ClusterShell import error, perhaps missing python ClusterShell package' + REQ_ERROR = "ClusterShell import error, perhaps missing python ClusterShell package" def __virtual__(): return (REQ_ERROR is None, REQ_ERROR) -def targets(tgt, tgt_type='glob', **kwargs): - ''' +def targets(tgt, tgt_type="glob", **kwargs): + """ Return the targets - ''' + """ ret = {} - ports = __opts__['ssh_scan_ports'] + ports = __opts__["ssh_scan_ports"] if not isinstance(ports, list): # Comma-separate list of integers - ports = list(map(int, six.text_type(ports).split(','))) + ports = list(map(int, six.text_type(ports).split(","))) hosts = list(NodeSet(tgt)) host_addrs = dict([(h, socket.gethostbyname(h)) for h in hosts]) for host, addr in host_addrs.items(): addr = six.text_type(addr) - ret[host] = copy.deepcopy(__opts__.get('roster_defaults', {})) + ret[host] = copy.deepcopy(__opts__.get("roster_defaults", {})) for port in ports: try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(float(__opts__['ssh_scan_timeout'])) + sock.settimeout(float(__opts__["ssh_scan_timeout"])) sock.connect((addr, port)) sock.shutdown(socket.SHUT_RDWR) sock.close() - ret[host].update({'host': addr, 'port': port}) + ret[host].update({"host": addr, "port": port}) except socket.error: pass return ret diff --git a/salt/roster/flat.py b/salt/roster/flat.py index 8a7fa46d99e..85cc49d9dda 100644 --- a/salt/roster/flat.py +++ b/salt/roster/flat.py @@ -1,36 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" Read in the roster from a flat file using the renderer system -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + +import salt.config + # Import Salt libs import salt.loader -import salt.config from salt.ext import six -from salt.template import compile_template from salt.roster import get_roster_file +from salt.template import compile_template -import logging log = logging.getLogger(__name__) -def targets(tgt, tgt_type='glob', **kwargs): - ''' +def targets(tgt, tgt_type="glob", **kwargs): + """ Return the targets from the flat yaml file, checks opts for location but defaults to /etc/salt/roster - ''' + """ template = get_roster_file(__opts__) rend = salt.loader.render(__opts__, {}) - raw = compile_template(template, - rend, - __opts__['renderer'], - __opts__['renderer_blacklist'], - __opts__['renderer_whitelist'], - mask_value='passw*', - **kwargs) + raw = compile_template( + template, + rend, + __opts__["renderer"], + __opts__["renderer_blacklist"], + __opts__["renderer_whitelist"], + mask_value="passw*", + **kwargs + ) conditioned_raw = {} for minion in raw: conditioned_raw[six.text_type(minion)] = salt.config.apply_sdb(raw[minion]) - return __utils__['roster_matcher.targets'](conditioned_raw, tgt, tgt_type, 'ipv4') + return __utils__["roster_matcher.targets"](conditioned_raw, tgt, tgt_type, "ipv4") diff --git a/salt/roster/range.py b/salt/roster/range.py index 6099e543ef5..af432a76485 100644 --- a/salt/roster/range.py +++ b/salt/roster/range.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This roster resolves targets from a range server. :depends: seco.range, https://github.com/ytoolshed/range @@ -10,21 +10,23 @@ When you want to use a range query for target matching, use ``--roster range``. salt-ssh --roster range '%%%example.range.cluster' test.ping -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import fnmatch -import copy +import copy +import fnmatch import logging + log = logging.getLogger(__name__) # Try to import range from https://github.com/ytoolshed/range HAS_RANGE = False try: import seco.range + HAS_RANGE = True except ImportError: - log.error('Unable to load range library') + log.error("Unable to load range library") # pylint: enable=import-error @@ -32,36 +34,36 @@ def __virtual__(): return HAS_RANGE -def targets(tgt, tgt_type='range', **kwargs): - ''' +def targets(tgt, tgt_type="range", **kwargs): + """ Return the targets from a range query - ''' + """ - r = seco.range.Range(__opts__['range_server']) - log.debug('Range connection to \'%s\' established', __opts__['range_server']) + r = seco.range.Range(__opts__["range_server"]) + log.debug("Range connection to '%s' established", __opts__["range_server"]) hosts = [] try: - log.debug('Querying range for \'%s\'', tgt) + log.debug("Querying range for '%s'", tgt) hosts = r.expand(tgt) except seco.range.RangeException as err: - log.error('Range server exception: %s', err) + log.error("Range server exception: %s", err) return {} - log.debug('Range responded with: \'%s\'', hosts) + log.debug("Range responded with: '%s'", hosts) # Currently we only support giving a raw range entry, no target filtering supported other than what range returns :S tgt_func = { - 'range': target_range, - 'glob': target_range, + "range": target_range, + "glob": target_range, # 'glob': target_glob, } - log.debug('Filtering using tgt_type: \'%s\'', tgt_type) + log.debug("Filtering using tgt_type: '%s'", tgt_type) try: targeted_hosts = tgt_func[tgt_type](tgt, hosts) except KeyError: raise NotImplementedError - log.debug('Targeting data for salt-ssh: \'%s\'', targeted_hosts) + log.debug("Targeting data for salt-ssh: '%s'", targeted_hosts) return targeted_hosts @@ -69,10 +71,10 @@ def targets(tgt, tgt_type='range', **kwargs): def target_range(tgt, hosts): ret = {} for host in hosts: - ret[host] = copy.deepcopy(__opts__.get('roster_defaults', {})) - ret[host].update({'host': host}) - if __opts__.get('ssh_user'): - ret[host].update({'user': __opts__['ssh_user']}) + ret[host] = copy.deepcopy(__opts__.get("roster_defaults", {})) + ret[host].update({"host": host}) + if __opts__.get("ssh_user"): + ret[host].update({"user": __opts__["ssh_user"]}) return ret @@ -80,8 +82,8 @@ def target_glob(tgt, hosts): ret = {} for host in hosts: if fnmatch.fnmatch(tgt, host): - ret[host] = copy.deepcopy(__opts__.get('roster_defaults', {})) - ret[host].update({'host': host}) - if __opts__.get('ssh_user'): - ret[host].update({'user': __opts__['ssh_user']}) + ret[host] = copy.deepcopy(__opts__.get("roster_defaults", {})) + ret[host].update({"host": host}) + if __opts__.get("ssh_user"): + ret[host].update({"user": __opts__["ssh_user"]}) return ret diff --git a/salt/roster/scan.py b/salt/roster/scan.py index 610e3e3ee70..728d9d9f719 100644 --- a/salt/roster/scan.py +++ b/salt/roster/scan.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Scan a netmask or ipaddr for open ssh ports -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import socket -import logging + import copy +import logging +import socket # Import salt libs import salt.utils.network @@ -20,34 +21,35 @@ from salt.ext.six.moves import map # pylint: disable=import-error,redefined-bui log = logging.getLogger(__name__) -def targets(tgt, tgt_type='glob', **kwargs): - ''' +def targets(tgt, tgt_type="glob", **kwargs): + """ Return the targets from the flat yaml file, checks opts for location but defaults to /etc/salt/roster - ''' + """ rmatcher = RosterMatcher(tgt, tgt_type) return rmatcher.targets() class RosterMatcher(object): - ''' + """ Matcher for the roster data structure - ''' + """ + def __init__(self, tgt, tgt_type): self.tgt = tgt self.tgt_type = tgt_type def targets(self): - ''' + """ Return ip addrs based on netmask, sitting in the "glob" spot because it is the default - ''' + """ addrs = () ret = {} - ports = __opts__['ssh_scan_ports'] + ports = __opts__["ssh_scan_ports"] if not isinstance(ports, list): # Comma-separate list of integers - ports = list(map(int, six.text_type(ports).split(','))) + ports = list(map(int, six.text_type(ports).split(","))) try: addrs = [ipaddress.ip_address(self.tgt)] except ValueError: @@ -57,17 +59,17 @@ class RosterMatcher(object): pass for addr in addrs: addr = six.text_type(addr) - ret[addr] = copy.deepcopy(__opts__.get('roster_defaults', {})) - log.trace('Scanning host: %s', addr) + ret[addr] = copy.deepcopy(__opts__.get("roster_defaults", {})) + log.trace("Scanning host: %s", addr) for port in ports: - log.trace('Scanning port: %s', port) + log.trace("Scanning port: %s", port) try: sock = salt.utils.network.get_socket(addr, socket.SOCK_STREAM) - sock.settimeout(float(__opts__['ssh_scan_timeout'])) + sock.settimeout(float(__opts__["ssh_scan_timeout"])) sock.connect((addr, port)) sock.shutdown(socket.SHUT_RDWR) sock.close() - ret[addr].update({'host': addr, 'port': port}) + ret[addr].update({"host": addr, "port": port}) except socket.error: pass return ret diff --git a/salt/roster/sshconfig.py b/salt/roster/sshconfig.py index 107e07fe959..963fab886c7 100644 --- a/salt/roster/sshconfig.py +++ b/salt/roster/sshconfig.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" Parses roster entries out of Host directives from SSH config .. code-block:: bash salt-ssh --roster sshconfig '*' -r "echo hi" -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import collections +import fnmatch +import logging + # Import python libs import os -import collections -import fnmatch import re # Import Salt libs @@ -19,44 +21,43 @@ import salt.utils.files import salt.utils.stringutils from salt.ext import six -import logging log = logging.getLogger(__name__) -_SSHConfRegex = collections.namedtuple('_SSHConfRegex', ['target_field', 'pattern']) +_SSHConfRegex = collections.namedtuple("_SSHConfRegex", ["target_field", "pattern"]) _ROSTER_FIELDS = ( - _SSHConfRegex(target_field='user', pattern=r'\s+User (.*)'), - _SSHConfRegex(target_field='port', pattern=r'\s+Port (.*)'), - _SSHConfRegex(target_field='priv', pattern=r'\s+IdentityFile (.*)'), + _SSHConfRegex(target_field="user", pattern=r"\s+User (.*)"), + _SSHConfRegex(target_field="port", pattern=r"\s+Port (.*)"), + _SSHConfRegex(target_field="priv", pattern=r"\s+IdentityFile (.*)"), ) def _get_ssh_config_file(opts): - ''' + """ :return: Path to the .ssh/config file - usually <home>/.ssh/config - ''' - ssh_config_file = opts.get('ssh_config_file') + """ + ssh_config_file = opts.get("ssh_config_file") if not os.path.isfile(ssh_config_file): - raise IOError('Cannot find SSH config file') + raise IOError("Cannot find SSH config file") if not os.access(ssh_config_file, os.R_OK): - raise IOError('Cannot access SSH config file: {}'.format(ssh_config_file)) + raise IOError("Cannot access SSH config file: {}".format(ssh_config_file)) return ssh_config_file def parse_ssh_config(lines): - ''' + """ Parses lines from the SSH config to create roster targets. :param lines: Individual lines from the ssh config file :return: Dictionary of targets in similar style to the flat roster - ''' + """ # transform the list of individual lines into a list of sublists where each # sublist represents a single Host definition hosts = [] for line in lines: line = salt.utils.stringutils.to_unicode(line) - if not line or line.startswith('#'): + if not line or line.startswith("#"): continue - elif line.startswith('Host '): + elif line.startswith("Host "): hosts.append([]) hosts[-1].append(line) @@ -77,7 +78,7 @@ def parse_ssh_config(lines): wildcard_targets = [] non_wildcard_targets = [] for target in targets.keys(): - if '*' in target or '?' in target: + if "*" in target or "?" in target: wildcard_targets.append(target) else: non_wildcard_targets.append(target) @@ -90,17 +91,17 @@ def parse_ssh_config(lines): # finally, update the 'host' to refer to its declaration in the SSH config # so that its connection parameters can be utilized for target in targets: - targets[target]['host'] = target + targets[target]["host"] = target return targets -def targets(tgt, tgt_type='glob', **kwargs): - ''' +def targets(tgt, tgt_type="glob", **kwargs): + """ Return the targets from the flat yaml file, checks opts for location but defaults to /etc/salt/roster - ''' + """ ssh_config_file = _get_ssh_config_file(__opts__) - with salt.utils.files.fopen(ssh_config_file, 'r') as fp: + with salt.utils.files.fopen(ssh_config_file, "r") as fp: all_minions = parse_ssh_config([line.rstrip() for line in fp]) rmatcher = RosterMatcher(all_minions, tgt, tgt_type) matched = rmatcher.targets() @@ -108,27 +109,28 @@ def targets(tgt, tgt_type='glob', **kwargs): class RosterMatcher(object): - ''' + """ Matcher for the roster data structure - ''' + """ + def __init__(self, raw, tgt, tgt_type): self.tgt = tgt self.tgt_type = tgt_type self.raw = raw def targets(self): - ''' + """ Execute the correct tgt_type routine and return - ''' + """ try: - return getattr(self, 'ret_{0}_minions'.format(self.tgt_type))() + return getattr(self, "ret_{0}_minions".format(self.tgt_type))() except AttributeError: return {} def ret_glob_minions(self): - ''' + """ Return minions that match via glob - ''' + """ minions = {} for minion in self.raw: if fnmatch.fnmatch(minion, self.tgt): @@ -138,11 +140,11 @@ class RosterMatcher(object): return minions def get_data(self, minion): - ''' + """ Return the configured ip - ''' + """ if isinstance(self.raw[minion], six.string_types): - return {'host': self.raw[minion]} + return {"host": self.raw[minion]} if isinstance(self.raw[minion], dict): return self.raw[minion] return False diff --git a/salt/roster/terraform.py b/salt/roster/terraform.py index 48849026607..4dfdf4d9e98 100644 --- a/salt/roster/terraform.py +++ b/salt/roster/terraform.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Dynamic roster from terraform current state =========================================== @@ -53,9 +53,10 @@ definition. Please refer to the `Terraform Salt`_ provider for more detailed examples. .. _Terraform Salt: https://github.com/dmacvicar/terraform-provider-salt -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging import os.path @@ -65,30 +66,33 @@ import salt.utils.json log = logging.getLogger(__name__) -TF_OUTPUT_PREFIX = 'salt.roster.' -TF_ROSTER_ATTRS = {'host': 's', - 'user': 's', - 'passwd': 's', - 'port': 'i', - 'sudo': 'b', - 'sudo_user': 's', - 'tty': 'b', 'priv': 's', - 'timeout': 'i', - 'minion_opts': 'm', - 'thin_dir': 's', - 'cmd_umask': 'i'} -MINION_ID = 'salt_id' +TF_OUTPUT_PREFIX = "salt.roster." +TF_ROSTER_ATTRS = { + "host": "s", + "user": "s", + "passwd": "s", + "port": "i", + "sudo": "b", + "sudo_user": "s", + "tty": "b", + "priv": "s", + "timeout": "i", + "minion_opts": "m", + "thin_dir": "s", + "cmd_umask": "i", +} +MINION_ID = "salt_id" def _handle_salt_host_resource(resource): - ''' + """ Handles salt_host resources. See https://github.com/dmacvicar/terraform-provider-salt Returns roster attributes for the resource or None - ''' + """ ret = {} - attrs = resource.get('primary', {}).get('attributes', {}) + attrs = resource.get("primary", {}).get("attributes", {}) ret[MINION_ID] = attrs.get(MINION_ID) valid_attrs = set(attrs.keys()).intersection(TF_ROSTER_ATTRS.keys()) for attr in valid_attrs: @@ -97,58 +101,56 @@ def _handle_salt_host_resource(resource): def _add_ssh_key(ret): - ''' + """ Setups the salt-ssh minion to be accessed with salt-ssh default key - ''' + """ priv = None - if __opts__.get('ssh_use_home_key') and os.path.isfile(os.path.expanduser('~/.ssh/id_rsa')): - priv = os.path.expanduser('~/.ssh/id_rsa') + if __opts__.get("ssh_use_home_key") and os.path.isfile( + os.path.expanduser("~/.ssh/id_rsa") + ): + priv = os.path.expanduser("~/.ssh/id_rsa") else: priv = __opts__.get( - 'ssh_priv', - os.path.abspath(os.path.join( - __opts__['pki_dir'], - 'ssh', - 'salt-ssh.rsa' - )) + "ssh_priv", + os.path.abspath(os.path.join(__opts__["pki_dir"], "ssh", "salt-ssh.rsa")), ) if priv and os.path.isfile(priv): - ret['priv'] = priv + ret["priv"] = priv def _cast_output_to_type(value, typ): - '''cast the value depending on the terraform type''' - if typ == 'b': + """cast the value depending on the terraform type""" + if typ == "b": return bool(value) - if typ == 'i': + if typ == "i": return int(value) return value -def _parse_state_file(state_file_path='terraform.tfstate'): - ''' +def _parse_state_file(state_file_path="terraform.tfstate"): + """ Parses the terraform state file passing different resource types to the right handler - ''' + """ ret = {} - with salt.utils.files.fopen(state_file_path, 'r') as fh_: + with salt.utils.files.fopen(state_file_path, "r") as fh_: tfstate = salt.utils.json.load(fh_) - modules = tfstate.get('modules') + modules = tfstate.get("modules") if not modules: - log.error('Malformed tfstate file. No modules found') + log.error("Malformed tfstate file. No modules found") return ret for module in modules: - resources = module.get('resources', []) + resources = module.get("resources", []) for resource_name, resource in salt.ext.six.iteritems(resources): roster_entry = None - if resource['type'] == 'salt_host': + if resource["type"] == "salt_host": roster_entry = _handle_salt_host_resource(resource) if not roster_entry: continue - minion_id = roster_entry.get(MINION_ID, resource.get('id')) + minion_id = roster_entry.get(MINION_ID, resource.get("id")) if not minion_id: continue @@ -159,24 +161,24 @@ def _parse_state_file(state_file_path='terraform.tfstate'): return ret -def targets(tgt, tgt_type='glob', **kwargs): # pylint: disable=W0613 - ''' +def targets(tgt, tgt_type="glob", **kwargs): # pylint: disable=W0613 + """ Returns the roster from the terraform state file, checks opts for location, but defaults to terraform.tfstate - ''' - roster_file = os.path.abspath('terraform.tfstate') - if __opts__.get('roster_file'): - roster_file = os.path.abspath(__opts__['roster_file']) + """ + roster_file = os.path.abspath("terraform.tfstate") + if __opts__.get("roster_file"): + roster_file = os.path.abspath(__opts__["roster_file"]) if not os.path.isfile(roster_file): log.error("Can't find terraform state file '%s'", roster_file) return {} - log.debug('terraform roster: using %s state file', roster_file) + log.debug("terraform roster: using %s state file", roster_file) - if not roster_file.endswith('.tfstate'): + if not roster_file.endswith(".tfstate"): log.error("Terraform roster can only be used with terraform state files") return {} raw = _parse_state_file(roster_file) - log.debug('%s hosts in terraform state file', len(raw)) - return __utils__['roster_matcher.targets'](raw, tgt, tgt_type, 'ipv4') + log.debug("%s hosts in terraform state file", len(raw)) + return __utils__["roster_matcher.targets"](raw, tgt, tgt_type, "ipv4") diff --git a/salt/runner.py b/salt/runner.py index 0212c1a3ecd..e15aa6e8e8f 100644 --- a/salt/runner.py +++ b/salt/runner.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Execute salt convenience routines -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import logging +import os # Import salt libs import salt.exceptions @@ -24,7 +25,7 @@ log = logging.getLogger(__name__) class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object): - ''' + """ The interface used by the :command:`salt-run` CLI tool on the Salt Master It executes :ref:`runner modules <all-salt.runners>` which run on the Salt @@ -37,9 +38,10 @@ class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object): Salt's :conf_master:`external_auth` can be used to authenticate calls. The eauth user must be authorized to execute runner modules: (``@runner``). Only the :py:meth:`master_call` below supports eauth. - ''' - client = 'runner' - tag_prefix = 'run' + """ + + client = "runner" + tag_prefix = "run" def __init__(self, opts, context=None): self.opts = opts @@ -49,23 +51,23 @@ class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object): @property def functions(self): - if not hasattr(self, '_functions'): - if not hasattr(self, 'utils'): + if not hasattr(self, "_functions"): + if not hasattr(self, "utils"): self.utils = salt.loader.utils(self.opts) # Must be self.functions for mixin to work correctly :-/ try: self._functions = salt.loader.runner( - self.opts, utils=self.utils, context=self.context) + self.opts, utils=self.utils, context=self.context + ) except AttributeError: # Just in case self.utils is still not present (perhaps due to # problems with the loader), load the runner funcs without them - self._functions = salt.loader.runner( - self.opts, context=self.context) + self._functions = salt.loader.runner(self.opts, context=self.context) return self._functions def _reformat_low(self, low): - ''' + """ Format the low data for RunnerClient()'s master_call() function This also normalizes the following low data formats to a single, common @@ -74,20 +76,31 @@ class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object): Old-style low: ``{'fun': 'jobs.lookup_jid', 'jid': '1234'}`` New-style: ``{'fun': 'jobs.lookup_jid', 'kwarg': {'jid': '1234'}}`` CLI-style: ``{'fun': 'jobs.lookup_jid', 'arg': ['jid="1234"']}`` - ''' - fun = low.pop('fun') + """ + fun = low.pop("fun") verify_fun(self.functions, fun) - eauth_creds = dict([(i, low.pop(i)) for i in [ - 'username', 'password', 'eauth', 'token', 'client', 'user', 'key', - ] if i in low]) + eauth_creds = dict( + [ + (i, low.pop(i)) + for i in [ + "username", + "password", + "eauth", + "token", + "client", + "user", + "key", + ] + if i in low + ] + ) # Run name=value args through parse_input. We don't need to run kwargs # through because there is no way to send name=value strings in the low # dict other than by including an `arg` array. - _arg, _kwarg = salt.utils.args.parse_input( - low.pop('arg', []), condition=False) - _kwarg.update(low.pop('kwarg', {})) + _arg, _kwarg = salt.utils.args.parse_input(low.pop("arg", []), condition=False) + _kwarg.update(low.pop("kwarg", {})) # If anything hasn't been pop()'ed out of low by this point it must be # an old-style kwarg. @@ -100,15 +113,13 @@ class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object): munged.extend(_arg) munged.append(dict(__kwarg__=True, **_kwarg)) arg, kwarg = salt.minion.load_args_and_kwargs( - self.functions[fun], - munged, - ignore_invalid=True) + self.functions[fun], munged, ignore_invalid=True + ) - return dict(fun=fun, kwarg={'kwarg': kwarg, 'arg': arg}, - **eauth_creds) + return dict(fun=fun, kwarg={"kwarg": kwarg, "arg": arg}, **eauth_creds) def cmd_async(self, low): - ''' + """ Execute a runner function asynchronously; eauth is respected This function requires that :conf_master:`external_auth` is configured @@ -122,13 +133,13 @@ class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object): 'password': 'saltdev', 'eauth': 'pam', }) - ''' + """ reformatted_low = self._reformat_low(low) return mixins.AsyncClientMixin.cmd_async(self, reformatted_low) def cmd_sync(self, low, timeout=None, full_return=False): - ''' + """ Execute a runner function synchronously; eauth is respected This function requires that :conf_master:`external_auth` is configured @@ -142,157 +153,174 @@ class RunnerClient(mixins.SyncClientMixin, mixins.AsyncClientMixin, object): 'password': 'saltdev', 'eauth': 'pam', }) - ''' + """ reformatted_low = self._reformat_low(low) - return mixins.SyncClientMixin.cmd_sync(self, reformatted_low, timeout, full_return) + return mixins.SyncClientMixin.cmd_sync( + self, reformatted_low, timeout, full_return + ) - def cmd(self, fun, arg=None, pub_data=None, kwarg=None, print_event=True, full_return=False): # pylint: disable=useless-super-delegation - ''' + def cmd( + self, + fun, + arg=None, + pub_data=None, + kwarg=None, + print_event=True, + full_return=False, + ): # pylint: disable=useless-super-delegation + """ Execute a function - ''' - return super(RunnerClient, self).cmd(fun, - arg, - pub_data, - kwarg, - print_event, - full_return) + """ + return super(RunnerClient, self).cmd( + fun, arg, pub_data, kwarg, print_event, full_return + ) class Runner(RunnerClient): - ''' + """ Execute the salt runner interface - ''' + """ + def __init__(self, opts, context=None): super(Runner, self).__init__(opts, context=context) self.returners = salt.loader.returners(opts, self.functions, context=context) self.outputters = salt.loader.outputters(opts) def print_docs(self): - ''' + """ Print out the documentation! - ''' - arg = self.opts.get('fun', None) + """ + arg = self.opts.get("fun", None) docs = super(Runner, self).get_docs(arg) for fun in sorted(docs): - display_output('{0}:'.format(fun), 'text', self.opts) + display_output("{0}:".format(fun), "text", self.opts) print(docs[fun]) # TODO: move to mixin whenever we want a salt-wheel cli def run(self): - ''' + """ Execute the runner sequence - ''' + """ import salt.minion + ret = {} - if self.opts.get('doc', False): + if self.opts.get("doc", False): self.print_docs() else: - low = {'fun': self.opts['fun']} + low = {"fun": self.opts["fun"]} try: # Allocate a jid async_pub = self._gen_async_pub() - self.jid = async_pub['jid'] + self.jid = async_pub["jid"] fun_args = salt.utils.args.parse_input( - self.opts['arg'], - no_parse=self.opts.get('no_parse', [])) + self.opts["arg"], no_parse=self.opts.get("no_parse", []) + ) - verify_fun(self.functions, low['fun']) + verify_fun(self.functions, low["fun"]) args, kwargs = salt.minion.load_args_and_kwargs( - self.functions[low['fun']], - fun_args) - low['arg'] = args - low['kwarg'] = kwargs + self.functions[low["fun"]], fun_args + ) + low["arg"] = args + low["kwarg"] = kwargs - if self.opts.get('eauth'): - if 'token' in self.opts: + if self.opts.get("eauth"): + if "token" in self.opts: try: - with salt.utils.files.fopen(os.path.join(self.opts['cachedir'], '.root_key'), 'r') as fp_: - low['key'] = salt.utils.stringutils.to_unicode(fp_.readline()) + with salt.utils.files.fopen( + os.path.join(self.opts["cachedir"], ".root_key"), "r" + ) as fp_: + low["key"] = salt.utils.stringutils.to_unicode( + fp_.readline() + ) except IOError: - low['token'] = self.opts['token'] + low["token"] = self.opts["token"] # If using eauth and a token hasn't already been loaded into # low, prompt the user to enter auth credentials - if 'token' not in low and 'key' not in low and self.opts['eauth']: + if "token" not in low and "key" not in low and self.opts["eauth"]: # This is expensive. Don't do it unless we need to. import salt.auth + resolver = salt.auth.Resolver(self.opts) - res = resolver.cli(self.opts['eauth']) - if self.opts['mktoken'] and res: - tok = resolver.token_cli( - self.opts['eauth'], - res - ) + res = resolver.cli(self.opts["eauth"]) + if self.opts["mktoken"] and res: + tok = resolver.token_cli(self.opts["eauth"], res) if tok: - low['token'] = tok.get('token', '') + low["token"] = tok.get("token", "") if not res: - log.error('Authentication failed') + log.error("Authentication failed") return ret low.update(res) - low['eauth'] = self.opts['eauth'] + low["eauth"] = self.opts["eauth"] else: user = salt.utils.user.get_specific_user() - if low['fun'] in ['state.orchestrate', 'state.orch', 'state.sls']: - low['kwarg']['orchestration_jid'] = async_pub['jid'] + if low["fun"] in ["state.orchestrate", "state.orch", "state.sls"]: + low["kwarg"]["orchestration_jid"] = async_pub["jid"] # Run the runner! - if self.opts.get('async', False): - if self.opts.get('eauth'): + if self.opts.get("async", False): + if self.opts.get("eauth"): async_pub = self.cmd_async(low) else: - async_pub = self.asynchronous(self.opts['fun'], - low, - user=user, - pub=async_pub) + async_pub = self.asynchronous( + self.opts["fun"], low, user=user, pub=async_pub + ) # by default: info will be not enougth to be printed out ! log.warning( - 'Running in asynchronous mode. Results of this execution may ' - 'be collected by attaching to the master event bus or ' - 'by examing the master job cache, if configured. ' - 'This execution is running under tag %s', async_pub['tag'] + "Running in asynchronous mode. Results of this execution may " + "be collected by attaching to the master event bus or " + "by examing the master job cache, if configured. " + "This execution is running under tag %s", + async_pub["tag"], ) - return async_pub['jid'] # return the jid + return async_pub["jid"] # return the jid # otherwise run it in the main process - if self.opts.get('eauth'): + if self.opts.get("eauth"): ret = self.cmd_sync(low) - if isinstance(ret, dict) and set(ret) == {'data', 'outputter'}: - outputter = ret['outputter'] - ret = ret['data'] + if isinstance(ret, dict) and set(ret) == {"data", "outputter"}: + outputter = ret["outputter"] + ret = ret["data"] else: outputter = None display_output(ret, outputter, self.opts) else: - ret = self._proc_function(self.opts['fun'], - low, - user, - async_pub['tag'], - async_pub['jid'], - daemonize=False) + ret = self._proc_function( + self.opts["fun"], + low, + user, + async_pub["tag"], + async_pub["jid"], + daemonize=False, + ) except salt.exceptions.SaltException as exc: - with salt.utils.event.get_event('master', opts=self.opts) as evt: - evt.fire_event({'success': False, - 'return': '{0}'.format(exc), - 'retcode': 254, - 'fun': self.opts['fun'], - 'fun_args': fun_args, - 'jid': self.jid}, - tag='salt/run/{0}/ret'.format(self.jid)) + with salt.utils.event.get_event("master", opts=self.opts) as evt: + evt.fire_event( + { + "success": False, + "return": "{0}".format(exc), + "retcode": 254, + "fun": self.opts["fun"], + "fun_args": fun_args, + "jid": self.jid, + }, + tag="salt/run/{0}/ret".format(self.jid), + ) # Attempt to grab documentation - if 'fun' in low: - ret = self.get_docs('{0}*'.format(low['fun'])) + if "fun" in low: + ret = self.get_docs("{0}*".format(low["fun"])) else: ret = None # If we didn't get docs returned then # return the `not availble` message. if not ret: - ret = '{0}'.format(exc) - if not self.opts.get('quiet', False): - display_output(ret, 'nested', self.opts) + ret = "{0}".format(exc) + if not self.opts.get("quiet", False): + display_output(ret, "nested", self.opts) else: - log.debug('Runner return: %s', ret) + log.debug("Runner return: %s", ret) return ret diff --git a/salt/runners/__init__.py b/salt/runners/__init__.py index bcb72bc2f7d..75987ad753b 100644 --- a/salt/runners/__init__.py +++ b/salt/runners/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Runners Directory -''' +""" diff --git a/salt/runners/asam.py b/salt/runners/asam.py index 6df9a9d1638..89953edb18c 100644 --- a/salt/runners/asam.py +++ b/salt/runners/asam.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Novell ASAM Runner ================== @@ -27,7 +27,7 @@ master configuration at ``/etc/salt/master`` or ``/etc/salt/master.d/asam.conf`` Optionally, ``protocol`` and ``port`` can be specified if the Fan-Out Driver server is not using the defaults. Default is ``protocol: https`` and ``port: 3451``. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -40,6 +40,7 @@ HAS_LIBS = False try: import requests from salt.ext.six.moves.html_parser import HTMLParser # pylint: disable=E0611 + HAS_LIBS = True class ASAMHTMLParser(HTMLParser): # fix issue #30477 @@ -55,6 +56,7 @@ try: return self.data.append(attr[1]) + except ImportError: pass @@ -62,10 +64,10 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Check for ASAM Fan-Out driver configuration in master config file or directory and load runner only if it is specified - ''' + """ if not HAS_LIBS: return False @@ -74,47 +76,57 @@ def __virtual__(): return True -def _get_asam_configuration(driver_url=''): - ''' +def _get_asam_configuration(driver_url=""): + """ Return the configuration read from the master configuration file or directory - ''' - asam_config = __opts__['asam'] if 'asam' in __opts__ else None + """ + asam_config = __opts__["asam"] if "asam" in __opts__ else None if asam_config: try: for asam_server, service_config in six.iteritems(asam_config): - username = service_config.get('username', None) - password = service_config.get('password', None) - protocol = service_config.get('protocol', 'https') - port = service_config.get('port', 3451) + username = service_config.get("username", None) + password = service_config.get("password", None) + protocol = service_config.get("protocol", "https") + port = service_config.get("port", 3451) if not username or not password: log.error( - 'Username or Password has not been specified in the ' - 'master configuration for %s', asam_server + "Username or Password has not been specified in the " + "master configuration for %s", + asam_server, ) return False ret = { - 'platform_edit_url': "{0}://{1}:{2}/config/PlatformEdit.html".format(protocol, asam_server, port), - 'platform_config_url': "{0}://{1}:{2}/config/PlatformConfig.html".format(protocol, asam_server, port), - 'platformset_edit_url': "{0}://{1}:{2}/config/PlatformSetEdit.html".format(protocol, asam_server, port), - 'platformset_config_url': "{0}://{1}:{2}/config/PlatformSetConfig.html".format(protocol, asam_server, port), - 'username': username, - 'password': password + "platform_edit_url": "{0}://{1}:{2}/config/PlatformEdit.html".format( + protocol, asam_server, port + ), + "platform_config_url": "{0}://{1}:{2}/config/PlatformConfig.html".format( + protocol, asam_server, port + ), + "platformset_edit_url": "{0}://{1}:{2}/config/PlatformSetEdit.html".format( + protocol, asam_server, port + ), + "platformset_config_url": "{0}://{1}:{2}/config/PlatformSetConfig.html".format( + protocol, asam_server, port + ), + "username": username, + "password": password, } if (not driver_url) or (driver_url == asam_server): return ret except Exception as exc: # pylint: disable=broad-except - log.error('Exception encountered: %s', exc) + log.error("Exception encountered: %s", exc) return False if driver_url: log.error( - 'Configuration for %s has not been specified in the master ' - 'configuration', driver_url + "Configuration for %s has not been specified in the master " + "configuration", + driver_url, ) return False @@ -126,7 +138,7 @@ def _make_post_request(url, data, auth, verify=True): if r.status_code != requests.codes.ok: r.raise_for_status() else: - return r.text.split('\n') + return r.text.split("\n") def _parse_html_content(html_content): @@ -142,11 +154,11 @@ def _parse_html_content(html_content): def _get_platformset_name(data, platform_name): for item in data: - if platform_name in item and item.startswith('PlatformEdit.html?'): - parameter_list = item.split('&') + if platform_name in item and item.startswith("PlatformEdit.html?"): + parameter_list = item.split("&") for parameter in parameter_list: if parameter.startswith("platformSetName"): - return parameter.split('=')[1] + return parameter.split("=")[1] return None @@ -154,11 +166,11 @@ def _get_platformset_name(data, platform_name): def _get_platforms(data): platform_list = [] for item in data: - if item.startswith('PlatformEdit.html?'): - parameter_list = item.split('PlatformEdit.html?', 1)[1].split('&') + if item.startswith("PlatformEdit.html?"): + parameter_list = item.split("PlatformEdit.html?", 1)[1].split("&") for parameter in parameter_list: if parameter.startswith("platformName"): - platform_list.append(parameter.split('=')[1]) + platform_list.append(parameter.split("=")[1]) return platform_list @@ -166,17 +178,19 @@ def _get_platforms(data): def _get_platform_sets(data): platform_set_list = [] for item in data: - if item.startswith('PlatformSetEdit.html?'): - parameter_list = item.split('PlatformSetEdit.html?', 1)[1].split('&') + if item.startswith("PlatformSetEdit.html?"): + parameter_list = item.split("PlatformSetEdit.html?", 1)[1].split("&") for parameter in parameter_list: if parameter.startswith("platformSetName"): - platform_set_list.append(parameter.split('=')[1].replace('%20', ' ')) + platform_set_list.append( + parameter.split("=")[1].replace("%20", " ") + ) return platform_set_list def remove_platform(name, server_url): - ''' + """ To remove specified ASAM platform from the Novell Fan-Out Driver CLI Example: @@ -184,27 +198,24 @@ def remove_platform(name, server_url): .. code-block:: bash salt-run asam.remove_platform my-test-vm prov1.domain.com - ''' + """ config = _get_asam_configuration(server_url) if not config: return False - url = config['platform_config_url'] + url = config["platform_config_url"] data = { - 'manual': 'false', + "manual": "false", } - auth = ( - config['username'], - config['password'] - ) + auth = (config["username"], config["password"]) try: html_content = _make_post_request(url, data, auth, verify=False) except Exception as exc: # pylint: disable=broad-except err_msg = "Failed to look up existing platforms on {0}".format(server_url) - log.error('%s:\n%s', err_msg, exc) + log.error("%s:\n%s", err_msg, exc) return {name: err_msg} parser = _parse_html_content(html_content) @@ -212,15 +223,15 @@ def remove_platform(name, server_url): if platformset_name: log.debug(platformset_name) - data['platformName'] = name - data['platformSetName'] = six.text_type(platformset_name) - data['postType'] = 'platformRemove' - data['Submit'] = 'Yes' + data["platformName"] = name + data["platformSetName"] = six.text_type(platformset_name) + data["postType"] = "platformRemove" + data["Submit"] = "Yes" try: html_content = _make_post_request(url, data, auth, verify=False) except Exception as exc: # pylint: disable=broad-except err_msg = "Failed to delete platform from {1}".format(server_url) - log.error('%s:\n%s', err_msg, exc) + log.error("%s:\n%s", err_msg, exc) return {name: err_msg} parser = _parse_html_content(html_content) @@ -230,11 +241,13 @@ def remove_platform(name, server_url): else: return {name: "Successfully deleted platform from {0}".format(server_url)} else: - return {name: "Specified platform name does not exist on {0}".format(server_url)} + return { + name: "Specified platform name does not exist on {0}".format(server_url) + } def list_platforms(server_url): - ''' + """ To list all ASAM platforms present on the Novell Fan-Out Driver CLI Example: @@ -242,27 +255,24 @@ def list_platforms(server_url): .. code-block:: bash salt-run asam.list_platforms prov1.domain.com - ''' + """ config = _get_asam_configuration(server_url) if not config: return False - url = config['platform_config_url'] + url = config["platform_config_url"] data = { - 'manual': 'false', + "manual": "false", } - auth = ( - config['username'], - config['password'] - ) + auth = (config["username"], config["password"]) try: html_content = _make_post_request(url, data, auth, verify=False) except Exception as exc: # pylint: disable=broad-except err_msg = "Failed to look up existing platforms" - log.error('%s:\n%s', err_msg, exc) + log.error("%s:\n%s", err_msg, exc) return {server_url: err_msg} parser = _parse_html_content(html_content) @@ -275,7 +285,7 @@ def list_platforms(server_url): def list_platform_sets(server_url): - ''' + """ To list all ASAM platform sets present on the Novell Fan-Out Driver CLI Example: @@ -283,27 +293,24 @@ def list_platform_sets(server_url): .. code-block:: bash salt-run asam.list_platform_sets prov1.domain.com - ''' + """ config = _get_asam_configuration(server_url) if not config: return False - url = config['platformset_config_url'] + url = config["platformset_config_url"] data = { - 'manual': 'false', + "manual": "false", } - auth = ( - config['username'], - config['password'] - ) + auth = (config["username"], config["password"]) try: html_content = _make_post_request(url, data, auth, verify=False) 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) + log.error("%s:\n%s", err_msg, exc) return {server_url: err_msg} parser = _parse_html_content(html_content) @@ -316,7 +323,7 @@ def list_platform_sets(server_url): def add_platform(name, platform_set, server_url): - ''' + """ To add an ASAM platform using the specified ASAM platform set on the Novell Fan-Out Driver @@ -325,7 +332,7 @@ def add_platform(name, platform_set, server_url): .. code-block:: bash salt-run asam.add_platform my-test-vm test-platform-set prov1.domain.com - ''' + """ config = _get_asam_configuration(server_url) if not config: return False @@ -338,27 +345,24 @@ def add_platform(name, platform_set, server_url): if platform_set not in platform_sets[server_url]: return {name: "Specified platform set does not exist on {0}".format(server_url)} - url = config['platform_edit_url'] + url = config["platform_edit_url"] data = { - 'platformName': name, - 'platformSetName': platform_set, - 'manual': 'false', - 'previousURL': '/config/platformAdd.html', - 'postType': 'PlatformAdd', - 'Submit': 'Apply' + "platformName": name, + "platformSetName": platform_set, + "manual": "false", + "previousURL": "/config/platformAdd.html", + "postType": "PlatformAdd", + "Submit": "Apply", } - auth = ( - config['username'], - config['password'] - ) + auth = (config["username"], config["password"]) try: html_content = _make_post_request(url, data, auth, verify=False) except Exception as exc: # pylint: disable=broad-except err_msg = "Failed to add platform on {0}".format(server_url) - log.error('%s:\n%s', err_msg, exc) + log.error("%s:\n%s", err_msg, exc) return {name: err_msg} platforms = list_platforms(server_url) diff --git a/salt/runners/auth.py b/salt/runners/auth.py index 205d308c1a1..901dc47c97b 100644 --- a/salt/runners/auth.py +++ b/salt/runners/auth.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Authentication runner for creating, deleting, and managing eauth tokens. .. versionadded:: 2016.11.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt libs @@ -17,7 +18,7 @@ import salt.netapi def mk_token(**load): - r''' + r""" Create an eauth token using provided credentials Non-root users may specify an expiration date -- if allowed via the @@ -39,19 +40,18 @@ def mk_token(**load): # Calculate the number of seconds using expr. salt-run auth.mk_token username=saltdev password=saltdev eauth=auto \ token_expire=$(expr \( 365 \* 24 \* 60 \* 60 \) \* 3) - ''' + """ # This will hang if the master daemon is not running. netapi = salt.netapi.NetapiClient(__opts__) if not netapi._is_master_running(): - raise salt.exceptions.SaltDaemonNotRunning( - 'Salt Master must be running.') + raise salt.exceptions.SaltDaemonNotRunning("Salt Master must be running.") auth = salt.auth.Resolver(__opts__) return auth.mk_token(load) def del_token(token): - ''' + """ Delete an eauth token by name CLI Example: @@ -59,8 +59,8 @@ def del_token(token): .. code-block:: shell salt-run auth.del_token 6556760736e4077daa601baec2b67c24 - ''' - token_path = os.path.join(__opts__['token_dir'], token) + """ + token_path = os.path.join(__opts__["token_dir"], token) if os.path.exists(token_path): return os.remove(token_path) is None return False diff --git a/salt/runners/bgp.py b/salt/runners/bgp.py index 12bddf6663d..b00670812a4 100644 --- a/salt/runners/bgp.py +++ b/salt/runners/bgp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" BGP Finder ========== @@ -96,57 +96,52 @@ Configuration - holdtime - flap_count outputter: yaml -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import third party libs -try: - from netaddr import IPNetwork - from netaddr import IPAddress - # pylint: disable=unused-import - from napalm_base import helpers as napalm_helpers - # pylint: enable=unused-import - HAS_NAPALM_BASE = True -except ImportError: - HAS_NAPALM_BASE = False - # Import salt lib import salt.output from salt.ext import six from salt.ext.six.moves import map +# Import third party libs +try: + from netaddr import IPNetwork + from netaddr import IPAddress + + # pylint: disable=unused-import + from napalm_base import helpers as napalm_helpers + + # pylint: enable=unused-import + HAS_NAPALM_BASE = True +except ImportError: + HAS_NAPALM_BASE = False + + # ----------------------------------------------------------------------------- # module properties # ----------------------------------------------------------------------------- -__virtualname__ = 'bgp' +__virtualname__ = "bgp" # ----------------------------------------------------------------------------- # global variables # ----------------------------------------------------------------------------- -_DEFAULT_TARGET = '*' -_DEFAULT_EXPR_FORM = 'glob' +_DEFAULT_TARGET = "*" +_DEFAULT_EXPR_FORM = "glob" _DEFAULT_DISPLAY = True -_DEFAULT_OUTPUTTER = 'table' -_DEFAULT_INCLUDED_FIELDS = [ - 'device', - 'as_number', - 'neighbor_address' -] -_DEFAULT_RETURN_FIELDS = [ - 'connection_stats', - 'import_policy', - 'export_policy' -] +_DEFAULT_OUTPUTTER = "table" +_DEFAULT_INCLUDED_FIELDS = ["device", "as_number", "neighbor_address"] +_DEFAULT_RETURN_FIELDS = ["connection_stats", "import_policy", "export_policy"] _DEFAULT_LABELS_MAPPING = { - 'device': 'Device', - 'as_number': 'AS Number', - 'neighbor_address': 'Neighbor IP', - 'connection_stats': 'State|#Active/Received/Accepted/Damped', - 'import_policy': 'Policy IN', - 'export_policy': 'Policy OUT', - 'vrf': 'VRF' + "device": "Device", + "as_number": "AS Number", + "neighbor_address": "Neighbor IP", + "connection_stats": "State|#Active/Received/Accepted/Damped", + "import_policy": "Policy IN", + "export_policy": "Policy OUT", + "vrf": "VRF", } # ----------------------------------------------------------------------------- @@ -157,7 +152,8 @@ _DEFAULT_LABELS_MAPPING = { def __virtual__(): if HAS_NAPALM_BASE: return __virtualname__ - return (False, 'The napalm-base module could not be imported') + return (False, "The napalm-base module could not be imported") + # ----------------------------------------------------------------------------- # helper functions -- will not be exported @@ -165,72 +161,70 @@ def __virtual__(): def _get_bgp_runner_opts(): - ''' + """ Return the bgp runner options. - ''' - runner_opts = __opts__.get('runners', {}).get('bgp', {}) + """ + runner_opts = __opts__.get("runners", {}).get("bgp", {}) return { - 'tgt': runner_opts.get('tgt', _DEFAULT_TARGET), - 'tgt_type': runner_opts.get('tgt_type', _DEFAULT_EXPR_FORM), - 'display': runner_opts.get('display', _DEFAULT_DISPLAY), - 'return_fields': _DEFAULT_INCLUDED_FIELDS + runner_opts.get('return_fields', _DEFAULT_RETURN_FIELDS), - 'outputter': runner_opts.get('outputter', _DEFAULT_OUTPUTTER), + "tgt": runner_opts.get("tgt", _DEFAULT_TARGET), + "tgt_type": runner_opts.get("tgt_type", _DEFAULT_EXPR_FORM), + "display": runner_opts.get("display", _DEFAULT_DISPLAY), + "return_fields": _DEFAULT_INCLUDED_FIELDS + + runner_opts.get("return_fields", _DEFAULT_RETURN_FIELDS), + "outputter": runner_opts.get("outputter", _DEFAULT_OUTPUTTER), } def _get_mine(opts=None): - ''' + """ Helper to return the mine data from the minions, as configured on the runner opts. - ''' + """ if not opts: # not a massive improvement, but better than recomputing the runner opts dict opts = _get_bgp_runner_opts() - return __salt__['mine.get'](opts['tgt'], - 'bgp.neighbors', - tgt_type=opts['tgt_type']) + return __salt__["mine.get"](opts["tgt"], "bgp.neighbors", tgt_type=opts["tgt_type"]) def _compare_match(dict1, dict2): - ''' + """ Compare two dictionaries and return a boolean value if their values match. - ''' + """ for karg, warg in six.iteritems(dict1): if karg in dict2 and dict2[karg] != warg: return False return True -def _display_runner(rows, - labels, - title, - display=_DEFAULT_DISPLAY, - outputter=_DEFAULT_OUTPUTTER): - ''' +def _display_runner( + rows, labels, title, display=_DEFAULT_DISPLAY, outputter=_DEFAULT_OUTPUTTER +): + """ Display or return the rows. - ''' + """ if display: - if outputter == 'table': - ret = salt.output.out_format({'rows': rows, 'labels': labels}, - 'table', - __opts__, - title=title, - rows_key='rows', - labels_key='labels') + if outputter == "table": + ret = salt.output.out_format( + {"rows": rows, "labels": labels}, + "table", + __opts__, + title=title, + rows_key="rows", + labels_key="labels", + ) else: - ret = salt.output.out_format(rows, - outputter, - __opts__) + ret = salt.output.out_format(rows, outputter, __opts__) print(ret) else: return rows + # ----------------------------------------------------------------------------- # callable functions # ----------------------------------------------------------------------------- def neighbors(*asns, **kwargs): - ''' + """ Search for BGP neighbors details in the mines of the ``bgp.neighbors`` function. Arguments: @@ -294,64 +288,85 @@ def neighbors(*asns, **kwargs): ________________________________________________________________________________________________________________________________________________________________ | edge01.tbg01 | 13335 | 192.168.172.17 | Established 0/1/1/0 | import-policy | export-policy | ________________________________________________________________________________________________________________________________________________________________ - ''' + """ opts = _get_bgp_runner_opts() - title = kwargs.pop('title', None) - display = kwargs.pop('display', opts['display']) - outputter = kwargs.pop('outputter', opts['outputter']) + title = kwargs.pop("title", None) + display = kwargs.pop("display", opts["display"]) + outputter = kwargs.pop("outputter", opts["outputter"]) # cleaning up the kwargs # __pub args not used in this runner (yet) kwargs_copy = {} kwargs_copy.update(kwargs) for karg, _ in six.iteritems(kwargs_copy): - if karg.startswith('__pub'): + if karg.startswith("__pub"): kwargs.pop(karg) if not asns and not kwargs: if display: - print('Please specify at least an AS Number or an output filter') + print("Please specify at least an AS Number or an output filter") return [] - device = kwargs.pop('device', None) - neighbor_ip = kwargs.pop('ip', None) - ipnet = kwargs.pop('network', None) + device = kwargs.pop("device", None) + neighbor_ip = kwargs.pop("ip", None) + ipnet = kwargs.pop("network", None) ipnet_obj = IPNetwork(ipnet) if ipnet else None # any other key passed on the CLI can be used as a filter rows = [] # building the labels labels = {} - for field in opts['return_fields']: + for field in opts["return_fields"]: if field in _DEFAULT_LABELS_MAPPING: labels[field] = _DEFAULT_LABELS_MAPPING[field] else: # transform from 'previous_connection_state' to 'Previous Connection State' - labels[field] = ' '.join(map(lambda word: word.title(), field.split('_'))) - display_fields = list(set(opts['return_fields']) - set(_DEFAULT_INCLUDED_FIELDS)) + labels[field] = " ".join(map(lambda word: word.title(), field.split("_"))) + display_fields = list(set(opts["return_fields"]) - set(_DEFAULT_INCLUDED_FIELDS)) get_bgp_neighbors_all = _get_mine(opts=opts) if not title: title_parts = [] if asns: - title_parts.append('BGP Neighbors for {asns}'.format( - asns=', '.join([six.text_type(asn) for asn in asns]) - )) + title_parts.append( + "BGP Neighbors for {asns}".format( + asns=", ".join([six.text_type(asn) for asn in asns]) + ) + ) if neighbor_ip: - title_parts.append('Selecting neighbors having the remote IP address: {ipaddr}'.format(ipaddr=neighbor_ip)) + title_parts.append( + "Selecting neighbors having the remote IP address: {ipaddr}".format( + ipaddr=neighbor_ip + ) + ) if ipnet: - title_parts.append('Selecting neighbors within the IP network: {ipnet}'.format(ipnet=ipnet)) + title_parts.append( + "Selecting neighbors within the IP network: {ipnet}".format(ipnet=ipnet) + ) if kwargs: - title_parts.append('Searching for BGP neighbors having the attributes: {attrmap}'.format( - attrmap=', '.join(map(lambda key: '{key}={value}'.format(key=key, value=kwargs[key]), kwargs)) - )) - title = '\n'.join(title_parts) - for minion, get_bgp_neighbors_minion in six.iteritems(get_bgp_neighbors_all): # pylint: disable=too-many-nested-blocks - if not get_bgp_neighbors_minion.get('result'): + title_parts.append( + "Searching for BGP neighbors having the attributes: {attrmap}".format( + attrmap=", ".join( + map( + lambda key: "{key}={value}".format( + key=key, value=kwargs[key] + ), + kwargs, + ) + ) + ) + ) + title = "\n".join(title_parts) + for minion, get_bgp_neighbors_minion in six.iteritems( + get_bgp_neighbors_all + ): # pylint: disable=too-many-nested-blocks + if not get_bgp_neighbors_minion.get("result"): continue # ignore empty or failed mines if device and minion != device: # when requested to display only the neighbors on a certain device continue - get_bgp_neighbors_minion_out = get_bgp_neighbors_minion.get('out', {}) - for vrf, vrf_bgp_neighbors in six.iteritems(get_bgp_neighbors_minion_out): # pylint: disable=unused-variable + get_bgp_neighbors_minion_out = get_bgp_neighbors_minion.get("out", {}) + for vrf, vrf_bgp_neighbors in six.iteritems( + get_bgp_neighbors_minion_out + ): # pylint: disable=unused-variable for asn, get_bgp_neighbors_minion_asn in six.iteritems(vrf_bgp_neighbors): if asns and asn not in asns: # if filtering by AS number(s), @@ -363,45 +378,52 @@ def neighbors(*asns, **kwargs): # requested filtering by neighbors stats # but this one does not correspond continue - if neighbor_ip and neighbor_ip != neighbor.get('remote_address'): + if neighbor_ip and neighbor_ip != neighbor.get("remote_address"): # requested filtering by neighbors IP addr continue - if ipnet_obj and neighbor.get('remote_address'): - neighbor_ip_obj = IPAddress(neighbor.get('remote_address')) + if ipnet_obj and neighbor.get("remote_address"): + neighbor_ip_obj = IPAddress(neighbor.get("remote_address")) if neighbor_ip_obj not in ipnet_obj: # Neighbor not in this network continue row = { - 'device': minion, - 'neighbor_address': neighbor.get('remote_address'), - 'as_number': asn + "device": minion, + "neighbor_address": neighbor.get("remote_address"), + "as_number": asn, } - if 'vrf' in display_fields: - row['vrf'] = vrf - if 'connection_stats' in display_fields: - connection_stats = '{state} {active}/{received}/{accepted}/{damped}'.format( - state=neighbor.get('connection_state', -1), - active=neighbor.get('active_prefix_count', -1), - received=neighbor.get('received_prefix_count', -1), - accepted=neighbor.get('accepted_prefix_count', -1), - damped=neighbor.get('suppressed_prefix_count', -1), + if "vrf" in display_fields: + row["vrf"] = vrf + if "connection_stats" in display_fields: + connection_stats = "{state} {active}/{received}/{accepted}/{damped}".format( + state=neighbor.get("connection_state", -1), + active=neighbor.get("active_prefix_count", -1), + received=neighbor.get("received_prefix_count", -1), + accepted=neighbor.get("accepted_prefix_count", -1), + damped=neighbor.get("suppressed_prefix_count", -1), + ) + row["connection_stats"] = connection_stats + if ( + "interface_description" in display_fields + or "interface_name" in display_fields + ): + net_find = __salt__["net.interfaces"]( + device=minion, + ipnet=neighbor.get("remote_address"), + display=False, ) - row['connection_stats'] = connection_stats - if 'interface_description' in display_fields or 'interface_name' in display_fields: - net_find = __salt__['net.interfaces'](device=minion, - ipnet=neighbor.get('remote_address'), - display=False) if net_find: - if 'interface_description' in display_fields: - row['interface_description'] = net_find[0]['interface_description'] - if 'interface_name' in display_fields: - row['interface_name'] = net_find[0]['interface'] + if "interface_description" in display_fields: + row["interface_description"] = net_find[0][ + "interface_description" + ] + if "interface_name" in display_fields: + row["interface_name"] = net_find[0]["interface"] else: # if unable to find anything, leave blank - if 'interface_description' in display_fields: - row['interface_description'] = '' - if 'interface_name' in display_fields: - row['interface_name'] = '' + if "interface_description" in display_fields: + row["interface_description"] = "" + if "interface_name" in display_fields: + row["interface_name"] = "" for field in display_fields: if field in neighbor: row[field] = neighbor[field] diff --git a/salt/runners/cache.py b/salt/runners/cache.py index fff2580ed71..2cd9e8f5c7e 100644 --- a/salt/runners/cache.py +++ b/salt/runners/cache.py @@ -1,37 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Return cached data from minions -''' +""" from __future__ import absolute_import, print_function, unicode_literals + # Import python libs import fnmatch import logging import os +import salt.cache + # Import salt libs import salt.config -from salt.ext import six +import salt.fileserver.gitfs import salt.log +import salt.payload +import salt.pillar.git_pillar +import salt.runners.winrepo import salt.utils.args import salt.utils.gitfs import salt.utils.master -import salt.payload -import salt.cache -import salt.fileserver.gitfs -import salt.pillar.git_pillar -import salt.runners.winrepo from salt.exceptions import SaltInvocationError +from salt.ext import six from salt.fileserver import clear_lock as _clear_lock log = logging.getLogger(__name__) __func_alias__ = { - 'list_': 'list', + "list_": "list", } -def grains(tgt=None, tgt_type='glob', **kwargs): - ''' +def grains(tgt=None, tgt_type="glob", **kwargs): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -55,27 +57,26 @@ def grains(tgt=None, tgt_type='glob', **kwargs): .. code-block:: bash salt-run cache.grains '*' - ''' + """ if tgt is None: # Change ``tgt=None`` to ``tgt`` (mandatory kwarg) in Salt Sodium. # This behavior was changed in PR #45588 to fix Issue #45489. salt.utils.versions.warn_until( - 'Sodium', - 'Detected missing \'tgt\' option. Cached grains will not be returned ' - 'without a specified \'tgt\'. This option will be required starting in ' - 'Salt Sodium and this warning will be removed.' + "Sodium", + "Detected missing 'tgt' option. Cached grains will not be returned " + "without a specified 'tgt'. This option will be required starting in " + "Salt Sodium and this warning will be removed.", ) - pillar_util = salt.utils.master.MasterPillarUtil(tgt, tgt_type, - use_cached_grains=True, - grains_fallback=False, - opts=__opts__) + pillar_util = salt.utils.master.MasterPillarUtil( + tgt, tgt_type, use_cached_grains=True, grains_fallback=False, opts=__opts__ + ) cached_grains = pillar_util.get_minion_grains() return cached_grains -def pillar(tgt=None, tgt_type='glob', **kwargs): - ''' +def pillar(tgt=None, tgt_type="glob", **kwargs): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -88,19 +89,22 @@ def pillar(tgt=None, tgt_type='glob', **kwargs): .. code-block:: bash salt-run cache.pillar - ''' - pillar_util = salt.utils.master.MasterPillarUtil(tgt, tgt_type, - use_cached_grains=True, - grains_fallback=False, - use_cached_pillar=True, - pillar_fallback=False, - opts=__opts__) + """ + pillar_util = salt.utils.master.MasterPillarUtil( + tgt, + tgt_type, + use_cached_grains=True, + grains_fallback=False, + use_cached_pillar=True, + pillar_fallback=False, + opts=__opts__, + ) cached_pillar = pillar_util.get_minion_pillar() return cached_pillar -def mine(tgt=None, tgt_type='glob', **kwargs): - ''' +def mine(tgt=None, tgt_type="glob", **kwargs): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -112,42 +116,52 @@ def mine(tgt=None, tgt_type='glob', **kwargs): .. code-block:: bash salt-run cache.mine - ''' - pillar_util = salt.utils.master.MasterPillarUtil(tgt, tgt_type, - use_cached_grains=False, - grains_fallback=False, - use_cached_pillar=False, - pillar_fallback=False, - opts=__opts__) + """ + pillar_util = salt.utils.master.MasterPillarUtil( + tgt, + tgt_type, + use_cached_grains=False, + grains_fallback=False, + use_cached_pillar=False, + pillar_fallback=False, + opts=__opts__, + ) cached_mine = pillar_util.get_cached_mine_data() return cached_mine -def _clear_cache(tgt=None, - tgt_type='glob', - clear_pillar_flag=False, - clear_grains_flag=False, - clear_mine_flag=False, - clear_mine_func_flag=None): - ''' +def _clear_cache( + tgt=None, + tgt_type="glob", + clear_pillar_flag=False, + clear_grains_flag=False, + clear_mine_flag=False, + clear_mine_func_flag=None, +): + """ Clear the cached data/files for the targeted minions. - ''' + """ if tgt is None: return False - pillar_util = salt.utils.master.MasterPillarUtil(tgt, tgt_type, - use_cached_grains=True, - grains_fallback=False, - use_cached_pillar=True, - pillar_fallback=False, - opts=__opts__) - return pillar_util.clear_cached_minion_data(clear_pillar=clear_pillar_flag, - clear_grains=clear_grains_flag, - clear_mine=clear_mine_flag, - clear_mine_func=clear_mine_func_flag) + pillar_util = salt.utils.master.MasterPillarUtil( + tgt, + tgt_type, + use_cached_grains=True, + grains_fallback=False, + use_cached_pillar=True, + pillar_fallback=False, + opts=__opts__, + ) + return pillar_util.clear_cached_minion_data( + clear_pillar=clear_pillar_flag, + clear_grains=clear_grains_flag, + clear_mine=clear_mine_flag, + clear_mine_func=clear_mine_func_flag, + ) -def clear_pillar(tgt=None, tgt_type='glob'): - ''' +def clear_pillar(tgt=None, tgt_type="glob"): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -159,12 +173,12 @@ def clear_pillar(tgt=None, tgt_type='glob'): .. code-block:: bash salt-run cache.clear_pillar - ''' + """ return _clear_cache(tgt, tgt_type, clear_pillar_flag=True) -def clear_grains(tgt=None, tgt_type='glob'): - ''' +def clear_grains(tgt=None, tgt_type="glob"): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -176,12 +190,12 @@ def clear_grains(tgt=None, tgt_type='glob'): .. code-block:: bash salt-run cache.clear_grains - ''' + """ return _clear_cache(tgt, tgt_type, clear_grains_flag=True) -def clear_mine(tgt=None, tgt_type='glob'): - ''' +def clear_mine(tgt=None, tgt_type="glob"): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -193,14 +207,12 @@ def clear_mine(tgt=None, tgt_type='glob'): .. code-block:: bash salt-run cache.clear_mine - ''' + """ return _clear_cache(tgt, tgt_type, clear_mine_flag=True) -def clear_mine_func(tgt=None, - tgt_type='glob', - clear_mine_func_flag=None): - ''' +def clear_mine_func(tgt=None, tgt_type="glob", clear_mine_func_flag=None): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -212,12 +224,12 @@ def clear_mine_func(tgt=None, .. code-block:: bash salt-run cache.clear_mine_func tgt='*' clear_mine_func_flag='network.interfaces' - ''' + """ return _clear_cache(tgt, tgt_type, clear_mine_func_flag=clear_mine_func_flag) -def clear_all(tgt=None, tgt_type='glob'): - ''' +def clear_all(tgt=None, tgt_type="glob"): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -229,16 +241,18 @@ def clear_all(tgt=None, tgt_type='glob'): .. code-block:: bash salt-run cache.clear_all - ''' - return _clear_cache(tgt, - tgt_type, - clear_pillar_flag=True, - clear_grains_flag=True, - clear_mine_flag=True) + """ + return _clear_cache( + tgt, + tgt_type, + clear_pillar_flag=True, + clear_grains_flag=True, + clear_mine_flag=True, + ) def clear_git_lock(role, remote=None, **kwargs): - ''' + """ .. versionadded:: 2015.8.2 Remove the update locks for Salt components (gitfs, git_pillar, winrepo) @@ -278,44 +292,46 @@ def clear_git_lock(role, remote=None, **kwargs): salt-run cache.clear_git_lock git_pillar type=update salt-run cache.clear_git_lock git_pillar type=update,checkout salt-run cache.clear_git_lock git_pillar type='["update", "mountpoint"]' - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) type_ = salt.utils.args.split_input( - kwargs.pop('type', ['update', 'checkout', 'mountpoint'])) + kwargs.pop("type", ["update", "checkout", "mountpoint"]) + ) if kwargs: salt.utils.args.invalid_kwargs(kwargs) - if role == 'gitfs': + if role == "gitfs": git_objects = [ salt.utils.gitfs.GitFS( __opts__, - __opts__['gitfs_remotes'], + __opts__["gitfs_remotes"], per_remote_overrides=salt.fileserver.gitfs.PER_REMOTE_OVERRIDES, - per_remote_only=salt.fileserver.gitfs.PER_REMOTE_ONLY + per_remote_only=salt.fileserver.gitfs.PER_REMOTE_ONLY, ) ] - elif role == 'git_pillar': + elif role == "git_pillar": git_objects = [] - for ext_pillar in __opts__['ext_pillar']: + for ext_pillar in __opts__["ext_pillar"]: key = next(iter(ext_pillar)) - if key == 'git': - if not isinstance(ext_pillar['git'], list): + if key == "git": + if not isinstance(ext_pillar["git"], list): continue obj = salt.utils.gitfs.GitPillar( __opts__, - ext_pillar['git'], + ext_pillar["git"], per_remote_overrides=salt.pillar.git_pillar.PER_REMOTE_OVERRIDES, per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY, - global_only=salt.pillar.git_pillar.GLOBAL_ONLY) + global_only=salt.pillar.git_pillar.GLOBAL_ONLY, + ) git_objects.append(obj) - elif role == 'winrepo': - winrepo_dir = __opts__['winrepo_dir'] - winrepo_remotes = __opts__['winrepo_remotes'] + elif role == "winrepo": + winrepo_dir = __opts__["winrepo_dir"] + winrepo_remotes = __opts__["winrepo_remotes"] git_objects = [] for remotes, base_dir in ( (winrepo_remotes, winrepo_dir), - (__opts__['winrepo_remotes_ng'], __opts__['winrepo_dir_ng']) + (__opts__["winrepo_remotes_ng"], __opts__["winrepo_dir_ng"]), ): obj = salt.utils.gitfs.WinRepo( __opts__, @@ -323,29 +339,29 @@ def clear_git_lock(role, remote=None, **kwargs): per_remote_overrides=salt.runners.winrepo.PER_REMOTE_OVERRIDES, per_remote_only=salt.runners.winrepo.PER_REMOTE_ONLY, global_only=salt.runners.winrepo.GLOBAL_ONLY, - cache_root=base_dir) + cache_root=base_dir, + ) git_objects.append(obj) else: - raise SaltInvocationError('Invalid role \'{0}\''.format(role)) + raise SaltInvocationError("Invalid role '{0}'".format(role)) ret = {} for obj in git_objects: for lock_type in type_: - cleared, errors = _clear_lock(obj.clear_lock, - role, - remote=remote, - lock_type=lock_type) + cleared, errors = _clear_lock( + obj.clear_lock, role, remote=remote, lock_type=lock_type + ) if cleared: - ret.setdefault('cleared', []).extend(cleared) + ret.setdefault("cleared", []).extend(cleared) if errors: - ret.setdefault('errors', []).extend(errors) + ret.setdefault("errors", []).extend(errors) if not ret: - return 'No locks were removed' + return "No locks were removed" return ret def cloud(tgt, provider=None): - ''' + """ Return cloud cache data for target. .. note:: Only works with glob matching @@ -362,17 +378,17 @@ def cloud(tgt, provider=None): salt-run cache.cloud 'salt*' salt-run cache.cloud glance.example.org provider=openstack - ''' + """ if not isinstance(tgt, six.string_types): return {} opts = salt.config.cloud_config( - os.path.join(os.path.dirname(__opts__['conf_file']), 'cloud') + os.path.join(os.path.dirname(__opts__["conf_file"]), "cloud") ) - if not opts.get('update_cachedir'): + if not opts.get("update_cachedir"): return {} - cloud_cache = __utils__['cloud.list_cache_nodes_full'](opts=opts, provider=provider) + cloud_cache = __utils__["cloud.list_cache_nodes_full"](opts=opts, provider=provider) if cloud_cache is None: return {} @@ -382,12 +398,12 @@ def cloud(tgt, provider=None): for name, data in six.iteritems(servers): if fnmatch.fnmatch(name, tgt): ret[name] = data - ret[name]['provider'] = provider + ret[name]["provider"] = provider return ret def store(bank, key, data, cachedir=None): - ''' + """ Lists entries stored in the specified bank. CLI Example: @@ -395,9 +411,9 @@ def store(bank, key, data, cachedir=None): .. code-block:: bash salt-run cache.store mycache mykey 'The time has come the walrus said' - ''' + """ if cachedir is None: - cachedir = __opts__['cachedir'] + cachedir = __opts__["cachedir"] try: cache = salt.cache.Cache(__opts__, cachedir=cachedir) @@ -407,7 +423,7 @@ def store(bank, key, data, cachedir=None): def list_(bank, cachedir=None): - ''' + """ Lists entries stored in the specified bank. CLI Example: @@ -415,9 +431,9 @@ def list_(bank, cachedir=None): .. code-block:: bash salt-run cache.list cloud/active/ec2/myec2 cachedir=/var/cache/salt/ - ''' + """ if cachedir is None: - cachedir = __opts__['cachedir'] + cachedir = __opts__["cachedir"] try: cache = salt.cache.Cache(__opts__, cachedir=cachedir) @@ -427,7 +443,7 @@ def list_(bank, cachedir=None): def fetch(bank, key, cachedir=None): - ''' + """ Fetch data from a salt.cache bank. CLI Example: @@ -435,9 +451,9 @@ def fetch(bank, key, cachedir=None): .. code-block:: bash salt-run cache.fetch cloud/active/ec2/myec2 myminion cachedir=/var/cache/salt/ - ''' + """ if cachedir is None: - cachedir = __opts__['cachedir'] + cachedir = __opts__["cachedir"] try: cache = salt.cache.Cache(__opts__, cachedir=cachedir) @@ -447,7 +463,7 @@ def fetch(bank, key, cachedir=None): def flush(bank, key=None, cachedir=None): - ''' + """ Remove the key from the cache bank with all the key content. If no key is specified remove the entire bank with all keys and sub-banks inside. @@ -457,9 +473,9 @@ def flush(bank, key=None, cachedir=None): salt-run cache.flush cloud/active/ec2/myec2 cachedir=/var/cache/salt/ salt-run cache.flush cloud/active/ec2/myec2 myminion cachedir=/var/cache/salt/ - ''' + """ if cachedir is None: - cachedir = __opts__['cachedir'] + cachedir = __opts__["cachedir"] try: cache = salt.cache.Cache(__opts__, cachedir=cachedir) diff --git a/salt/runners/cloud.py b/salt/runners/cloud.py index 0ed49011e85..4e21b217677 100644 --- a/salt/runners/cloud.py +++ b/salt/runners/cloud.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" The Salt Cloud Runner ===================== This runner wraps the functionality of salt cloud making salt cloud routines available to all internal apis via the runner system -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -22,71 +22,71 @@ log = logging.getLogger(__name__) def _get_client(): - ''' + """ Return cloud client - ''' + """ client = salt.cloud.CloudClient( - os.path.join(os.path.dirname(__opts__['conf_file']), 'cloud') - ) + os.path.join(os.path.dirname(__opts__["conf_file"]), "cloud") + ) return client -def list_sizes(provider='all'): - ''' +def list_sizes(provider="all"): + """ List cloud provider sizes for the given providers - ''' + """ client = _get_client() sizes = client.list_sizes(provider) return sizes -def list_images(provider='all'): - ''' +def list_images(provider="all"): + """ List cloud provider images for the given providers - ''' + """ client = _get_client() images = client.list_images(provider) return images -def list_locations(provider='all'): - ''' +def list_locations(provider="all"): + """ List cloud provider sizes for the given providers - ''' + """ client = _get_client() locations = client.list_locations(provider) return locations -def query(query_type='list_nodes'): - ''' +def query(query_type="list_nodes"): + """ List cloud provider data for all providers - ''' + """ client = _get_client() info = client.query(query_type) return info -def full_query(query_type='list_nodes_full'): - ''' +def full_query(query_type="list_nodes_full"): + """ List all available cloud provider data - ''' + """ client = _get_client() info = client.full_query(query_type) return info -def select_query(query_type='list_nodes_select'): - ''' +def select_query(query_type="list_nodes_select"): + """ List selected nodes - ''' + """ client = _get_client() info = client.select_query(query_type) return info def profile(prof=None, instances=None, opts=None, **kwargs): - ''' + """ Create a cloud vm with the given profile and instances, instances can be a list or comma-delimited string @@ -95,18 +95,18 @@ def profile(prof=None, instances=None, opts=None, **kwargs): .. code-block:: bash salt-run cloud.profile prof=my-ec2 instances=node1,node2,node3 - ''' - if prof is None and 'profile' in kwargs: - prof = kwargs['profile'] + """ + if prof is None and "profile" in kwargs: + prof = kwargs["profile"] if prof is None: - return {'Error': 'A profile (or prof) must be defined'} + return {"Error": "A profile (or prof) must be defined"} - if instances is None and 'names' in kwargs: - instances = kwargs['names'] + if instances is None and "names" in kwargs: + instances = kwargs["names"] if instances is None: - return {'Error': 'One or more instances (comma-delimited) must be set'} + return {"Error": "One or more instances (comma-delimited) must be set"} client = _get_client() if isinstance(opts, dict): @@ -116,9 +116,9 @@ def profile(prof=None, instances=None, opts=None, **kwargs): def map_run(path=None, opts=None, **kwargs): - ''' + """ Execute a salt cloud map file - ''' + """ client = _get_client() if isinstance(opts, dict): client.opts.update(opts) @@ -127,9 +127,9 @@ def map_run(path=None, opts=None, **kwargs): def destroy(instances, opts=None): - ''' + """ Destroy the named vm(s) - ''' + """ client = _get_client() if isinstance(opts, dict): client.opts.update(opts) @@ -137,14 +137,16 @@ def destroy(instances, opts=None): return info -def action(func=None, - cloudmap=None, - instances=None, - provider=None, - instance=None, - opts=None, - **kwargs): - ''' +def action( + func=None, + cloudmap=None, + instances=None, + provider=None, + instance=None, + opts=None, + **kwargs +): + """ Execute a single action on the given map/provider/instance CLI Example: @@ -152,7 +154,7 @@ def action(func=None, .. code-block:: bash salt-run cloud.action start my-salt-vm - ''' + """ info = {} client = _get_client() if isinstance(opts, dict): @@ -164,7 +166,7 @@ def action(func=None, instances, provider, instance, - salt.utils.args.clean_kwargs(**kwargs) + salt.utils.args.clean_kwargs(**kwargs), ) except SaltCloudConfigError as err: log.error(err) @@ -172,7 +174,7 @@ def action(func=None, def create(provider, instances, opts=None, **kwargs): - ''' + """ Create an instance using Salt Cloud CLI Example: @@ -182,7 +184,7 @@ def create(provider, instances, opts=None, **kwargs): salt-run cloud.create my-ec2-config myinstance \\ image=ami-1624987f size='t1.micro' ssh_username=ec2-user \\ securitygroup=default delvol_on_destroy=True - ''' + """ client = _get_client() if isinstance(opts, dict): client.opts.update(opts) diff --git a/salt/runners/config.py b/salt/runners/config.py index 2c3d8c667bd..f1994aba536 100644 --- a/salt/runners/config.py +++ b/salt/runners/config.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" This runner is designed to mirror the execution module config.py, but for master settings -''' +""" from __future__ import absolute_import, print_function, unicode_literals import salt.utils.data import salt.utils.sdb -def get(key, default='', delimiter=':'): - ''' +def get(key, default="", delimiter=":"): + """ Retrieve master config options, with optional nesting via the delimiter argument. @@ -32,9 +32,11 @@ def get(key, default='', delimiter=':'): salt-run config.get gitfs_remotes salt-run config.get file_roots:base salt-run config.get file_roots,base delimiter=',' - ''' - ret = salt.utils.data.traverse_dict_and_list(__opts__, key, default='_|-', delimiter=delimiter) - if ret == '_|-': + """ + ret = salt.utils.data.traverse_dict_and_list( + __opts__, key, default="_|-", delimiter=delimiter + ) + if ret == "_|-": return default else: return salt.utils.sdb.sdb_get(ret, __opts__) diff --git a/salt/runners/ddns.py b/salt/runners/ddns.py index cf3c86e801a..97cc0045918 100644 --- a/salt/runners/ddns.py +++ b/salt/runners/ddns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Dynamic DNS Runner ================== @@ -9,12 +9,17 @@ Runner to interact with DNS server and create/delete/update DNS records :codeauthor: Nitin Madhok <nmadhok@clemson.edu> -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging + +# Import salt libs +import salt.utils.files +import salt.utils.json # Import third party libs HAS_LIBS = False @@ -22,22 +27,20 @@ try: import dns.query import dns.update import dns.tsigkeyring + HAS_LIBS = True except ImportError: HAS_LIBS = False -# Import salt libs -import salt.utils.files -import salt.utils.json log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Check if required libs (python-dns) is installed and load runner only if they are present - ''' + """ if not HAS_LIBS: return False @@ -53,9 +56,20 @@ def _get_keyring(keyfile): return keyring -def create(zone, name, ttl, rdtype, data, keyname, keyfile, nameserver, - timeout, port=53, keyalgorithm='hmac-md5'): - ''' +def create( + zone, + name, + ttl, + rdtype, + data, + keyname, + keyfile, + nameserver, + timeout, + port=53, + keyalgorithm="hmac-md5", +): + """ Create a DNS record. The nameserver must be an IP address and the master running this runner must have create privileges on that server. @@ -64,10 +78,10 @@ def create(zone, name, ttl, rdtype, data, keyname, keyfile, nameserver, .. code-block:: bash salt-run ddns.create domain.com my-test-vm 3600 A 10.20.30.40 my-tsig-key /etc/salt/tsig.keyring 10.0.0.1 5 - ''' + """ if zone in name: - name = name.replace(zone, '').rstrip('.') - fqdn = '{0}.{1}'.format(name, zone) + name = name.replace(zone, "").rstrip(".") + fqdn = "{0}.{1}".format(name, zone) request = dns.message.make_query(fqdn, rdtype) answer = dns.query.udp(request, nameserver, timeout, port) @@ -76,24 +90,41 @@ def create(zone, name, ttl, rdtype, data, keyname, keyfile, nameserver, for rrset in answer.answer: if rdata in rrset.items: - return {fqdn: 'Record of type \'{0}\' already exists with ttl of {1}'.format(rdtype, rrset.ttl)} + return { + fqdn: "Record of type '{0}' already exists with ttl of {1}".format( + rdtype, rrset.ttl + ) + } keyring = _get_keyring(keyfile) - dns_update = dns.update.Update(zone, keyring=keyring, keyname=keyname, - keyalgorithm=keyalgorithm) + dns_update = dns.update.Update( + zone, keyring=keyring, keyname=keyname, keyalgorithm=keyalgorithm + ) dns_update.add(name, ttl, rdata) answer = dns.query.udp(dns_update, nameserver, timeout, port) if answer.rcode() > 0: - return {fqdn: 'Failed to create record of type \'{0}\''.format(rdtype)} + return {fqdn: "Failed to create record of type '{0}'".format(rdtype)} - return {fqdn: 'Created record of type \'{0}\': {1} -> {2}'.format(rdtype, fqdn, data)} + return {fqdn: "Created record of type '{0}': {1} -> {2}".format(rdtype, fqdn, data)} -def update(zone, name, ttl, rdtype, data, keyname, keyfile, nameserver, - timeout, replace=False, port=53, keyalgorithm='hmac-md5'): - ''' +def update( + zone, + name, + ttl, + rdtype, + data, + keyname, + keyfile, + nameserver, + timeout, + replace=False, + port=53, + keyalgorithm="hmac-md5", +): + """ Replace, or update a DNS record. The nameserver must be an IP address and the master running this runner must have update privileges on that server. @@ -107,14 +138,14 @@ def update(zone, name, ttl, rdtype, data, keyname, keyfile, nameserver, .. code-block:: bash salt-run ddns.update domain.com my-test-vm 3600 A 10.20.30.40 my-tsig-key /etc/salt/tsig.keyring 10.0.0.1 5 - ''' + """ if zone in name: - name = name.replace(zone, '').rstrip('.') - fqdn = '{0}.{1}'.format(name, zone) + name = name.replace(zone, "").rstrip(".") + fqdn = "{0}.{1}".format(name, zone) request = dns.message.make_query(fqdn, rdtype) answer = dns.query.udp(request, nameserver, timeout, port) if not answer.answer: - return {fqdn: 'No matching DNS record(s) found'} + return {fqdn: "No matching DNS record(s) found"} rdata_value = dns.rdatatype.from_text(rdtype) rdata = dns.rdata.from_text(dns.rdataclass.IN, rdata_value, data) @@ -123,28 +154,42 @@ def update(zone, name, ttl, rdtype, data, keyname, keyfile, nameserver, if rdata in rrset.items: rr = rrset.items if ttl == rrset.ttl: - if replace and (len(answer.answer) > 1 - or len(rrset.items) > 1): + if replace and (len(answer.answer) > 1 or len(rrset.items) > 1): break - return {fqdn: 'Record of type \'{0}\' already present with ttl of {1}'.format(rdtype, ttl)} + return { + fqdn: "Record of type '{0}' already present with ttl of {1}".format( + rdtype, ttl + ) + } break keyring = _get_keyring(keyfile) - dns_update = dns.update.Update(zone, keyring=keyring, keyname=keyname, - keyalgorithm=keyalgorithm) + dns_update = dns.update.Update( + zone, keyring=keyring, keyname=keyname, keyalgorithm=keyalgorithm + ) dns_update.replace(name, ttl, rdata) answer = dns.query.udp(dns_update, nameserver, timeout, port) if answer.rcode() > 0: - return {fqdn: 'Failed to update record of type \'{0}\''.format(rdtype)} + return {fqdn: "Failed to update record of type '{0}'".format(rdtype)} - return {fqdn: 'Updated record of type \'{0}\''.format(rdtype)} + return {fqdn: "Updated record of type '{0}'".format(rdtype)} -def delete(zone, name, keyname, keyfile, nameserver, timeout, rdtype=None, - data=None, port=53, keyalgorithm='hmac-md5'): - ''' +def delete( + zone, + name, + keyname, + keyfile, + nameserver, + timeout, + rdtype=None, + data=None, + port=53, + keyalgorithm="hmac-md5", +): + """ Delete a DNS record. CLI Example: @@ -152,20 +197,21 @@ def delete(zone, name, keyname, keyfile, nameserver, timeout, rdtype=None, .. code-block:: bash salt-run ddns.delete domain.com my-test-vm my-tsig-key /etc/salt/tsig.keyring 10.0.0.1 5 A - ''' + """ if zone in name: - name = name.replace(zone, '').rstrip('.') - fqdn = '{0}.{1}'.format(name, zone) - request = dns.message.make_query(fqdn, (rdtype or 'ANY')) + name = name.replace(zone, "").rstrip(".") + fqdn = "{0}.{1}".format(name, zone) + request = dns.message.make_query(fqdn, (rdtype or "ANY")) answer = dns.query.udp(request, nameserver, timeout, port) if not answer.answer: - return {fqdn: 'No matching DNS record(s) found'} + return {fqdn: "No matching DNS record(s) found"} keyring = _get_keyring(keyfile) - dns_update = dns.update.Update(zone, keyring=keyring, keyname=keyname, - keyalgorithm=keyalgorithm) + dns_update = dns.update.Update( + zone, keyring=keyring, keyname=keyname, keyalgorithm=keyalgorithm + ) if rdtype: rdata_value = dns.rdatatype.from_text(rdtype) @@ -179,14 +225,24 @@ def delete(zone, name, keyname, keyfile, nameserver, timeout, rdtype=None, answer = dns.query.udp(dns_update, nameserver, timeout, port) if answer.rcode() > 0: - return {fqdn: 'Failed to delete DNS record(s)'} + return {fqdn: "Failed to delete DNS record(s)"} - return {fqdn: 'Deleted DNS record(s)'} + return {fqdn: "Deleted DNS record(s)"} -def add_host(zone, name, ttl, ip, keyname, keyfile, nameserver, timeout, - port=53, keyalgorithm='hmac-md5'): - ''' +def add_host( + zone, + name, + ttl, + ip, + keyname, + keyfile, + nameserver, + timeout, + port=53, + keyalgorithm="hmac-md5", +): + """ Create both A and PTR (reverse) records for a host. CLI Example: @@ -194,17 +250,28 @@ def add_host(zone, name, ttl, ip, keyname, keyfile, nameserver, timeout, .. code-block:: bash salt-run ddns.add_host domain.com my-test-vm 3600 10.20.30.40 my-tsig-key /etc/salt/tsig.keyring 10.0.0.1 5 - ''' + """ res = [] if zone in name: - name = name.replace(zone, '').rstrip('.') - fqdn = '{0}.{1}'.format(name, zone) + name = name.replace(zone, "").rstrip(".") + fqdn = "{0}.{1}".format(name, zone) - ret = create(zone, name, ttl, 'A', ip, keyname, keyfile, nameserver, - timeout, port, keyalgorithm) + ret = create( + zone, + name, + ttl, + "A", + ip, + keyname, + keyfile, + nameserver, + timeout, + port, + keyalgorithm, + ) res.append(ret[fqdn]) - parts = ip.split('.')[::-1] + parts = ip.split(".")[::-1] i = len(parts) popped = [] @@ -214,11 +281,22 @@ def add_host(zone, name, ttl, ip, keyname, keyfile, nameserver, timeout, i -= 1 popped.append(p) - zone = '{0}.{1}'.format('.'.join(parts), 'in-addr.arpa.') - name = '.'.join(popped) - rev_fqdn = '{0}.{1}'.format(name, zone) - ret = create(zone, name, ttl, 'PTR', "{0}.".format(fqdn), keyname, - keyfile, nameserver, timeout, port, keyalgorithm) + zone = "{0}.{1}".format(".".join(parts), "in-addr.arpa.") + name = ".".join(popped) + rev_fqdn = "{0}.{1}".format(name, zone) + ret = create( + zone, + name, + ttl, + "PTR", + "{0}.".format(fqdn), + keyname, + keyfile, + nameserver, + timeout, + port, + keyalgorithm, + ) if "Created" in ret[rev_fqdn]: res.append(ret[rev_fqdn]) @@ -229,9 +307,10 @@ def add_host(zone, name, ttl, ip, keyname, keyfile, nameserver, timeout, return {fqdn: res} -def delete_host(zone, name, keyname, keyfile, nameserver, timeout, port=53, - keyalgorithm='hmac-md5'): - ''' +def delete_host( + zone, name, keyname, keyfile, nameserver, timeout, port=53, keyalgorithm="hmac-md5" +): + """ Delete both forward (A) and reverse (PTR) records for a host only if the forward (A) record exists. @@ -240,12 +319,12 @@ def delete_host(zone, name, keyname, keyfile, nameserver, timeout, port=53, .. code-block:: bash salt-run ddns.delete_host domain.com my-test-vm my-tsig-key /etc/salt/tsig.keyring 10.0.0.1 5 - ''' + """ res = [] if zone in name: - name = name.replace(zone, '').rstrip('.') - fqdn = '{0}.{1}'.format(name, zone) - request = dns.message.make_query(fqdn, 'A') + name = name.replace(zone, "").rstrip(".") + fqdn = "{0}.{1}".format(name, zone) + request = dns.message.make_query(fqdn, "A") answer = dns.query.udp(request, nameserver, timeout, port) try: @@ -253,12 +332,20 @@ def delete_host(zone, name, keyname, keyfile, nameserver, timeout, port=53, except IndexError: ips = [] - ret = delete(zone, name, keyname, keyfile, nameserver, timeout, port=port, - keyalgorithm=keyalgorithm) - res.append("{0} of type \'A\'".format(ret[fqdn])) + ret = delete( + zone, + name, + keyname, + keyfile, + nameserver, + timeout, + port=port, + keyalgorithm=keyalgorithm, + ) + res.append("{0} of type 'A'".format(ret[fqdn])) for ip in ips: - parts = ip.split('.')[::-1] + parts = ip.split(".")[::-1] i = len(parts) popped = [] @@ -267,14 +354,24 @@ def delete_host(zone, name, keyname, keyfile, nameserver, timeout, port=53, p = parts.pop(0) i -= 1 popped.append(p) - zone = '{0}.{1}'.format('.'.join(parts), 'in-addr.arpa.') - name = '.'.join(popped) - rev_fqdn = '{0}.{1}'.format(name, zone) - ret = delete(zone, name, keyname, keyfile, nameserver, timeout, - 'PTR', "{0}.".format(fqdn), port, keyalgorithm) + zone = "{0}.{1}".format(".".join(parts), "in-addr.arpa.") + name = ".".join(popped) + rev_fqdn = "{0}.{1}".format(name, zone) + ret = delete( + zone, + name, + keyname, + keyfile, + nameserver, + timeout, + "PTR", + "{0}.".format(fqdn), + port, + keyalgorithm, + ) if "Deleted" in ret[rev_fqdn]: - res.append("{0} of type \'PTR\'".format(ret[rev_fqdn])) + res.append("{0} of type 'PTR'".format(ret[rev_fqdn])) return {fqdn: res} res.append(ret[rev_fqdn]) diff --git a/salt/runners/digicertapi.py b/salt/runners/digicertapi.py index cb95159584c..d275571f2b4 100644 --- a/salt/runners/digicertapi.py +++ b/salt/runners/digicertapi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for Digicert. Heavily based on the Venafi runner by Joseph Hall (jphall@saltstack.com). Before using this module you need to register an account with Digicert's CertCentral. @@ -34,24 +34,28 @@ You can also include default values of the following variables to help with crea This API currently only supports RSA key types. Support for other key types will be added if interest warrants. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import os -import logging -import tempfile -import subprocess + import collections +import logging +import os import re -import salt.syspaths as syspaths +import subprocess +import tempfile + import salt.cache +import salt.syspaths as syspaths import salt.utils.files import salt.utils.http import salt.utils.json +from salt.exceptions import CommandExecutionError, SaltRunnerError from salt.ext import six from salt.ext.six.moves import range -from salt.exceptions import (CommandExecutionError, SaltRunnerError) + try: from M2Crypto import RSA + HAS_M2 = True except ImportError: HAS_M2 = False @@ -60,46 +64,46 @@ except ImportError: except ImportError: from Crypto.PublicKey import RSA -__virtualname__ = 'digicert' +__virtualname__ = "digicert" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load the module if digicert has configuration in place - ''' - if __opts__.get('digicert', {}).get('api_key'): + """ + if __opts__.get("digicert", {}).get("api_key"): return __virtualname__ return False def _base_url(): - ''' + """ Return the base_url - ''' - return __opts__.get('digicert', {}).get( - 'base_url', 'https://www.digicert.com/services/v2/' + """ + return __opts__.get("digicert", {}).get( + "base_url", "https://www.digicert.com/services/v2/" ) def _api_key(): - ''' + """ Return the API key - ''' - return __opts__.get('digicert', {}).get('api_key', '') + """ + return __opts__.get("digicert", {}).get("api_key", "") def _paginate(url, topkey, *args, **kwargs): - ''' + """ Wrapper to assist with paginated responses from Digicert's REST API. - ''' + """ ret = salt.utils.http.query(url, **kwargs) - if 'errors' in ret['dict']: - return ret['dict'] + if "errors" in ret["dict"]: + return ret["dict"] - lim = int(ret['dict']['page']['limit']) - total = int(ret['dict']['page']['total']) + lim = int(ret["dict"]["page"]["limit"]) + total = int(ret["dict"]["page"]["total"]) if total == 0: return {} @@ -108,20 +112,20 @@ def _paginate(url, topkey, *args, **kwargs): # If the count returned is less than the page size, just return the dict if numpages == 1: - return ret['dict'][topkey] + return ret["dict"][topkey] - aggregate_ret = ret['dict'][topkey] + aggregate_ret = ret["dict"][topkey] url = args[0] for p in range(2, numpages): - param_url = url + '?offset={0}'.format(lim * (p - 1)) + param_url = url + "?offset={0}".format(lim * (p - 1)) next_ret = salt.utils.http.query(param_url, kwargs) - aggregate_ret[topkey].extend(next_ret['dict'][topkey]) + aggregate_ret[topkey].extend(next_ret["dict"][topkey]) return aggregate_ret def list_domains(container_id=None): - ''' + """ List domains that CertCentral knows about. You can filter by container_id (also known as "Division") by passing a container_id. @@ -130,29 +134,27 @@ def list_domains(container_id=None): .. code-block:: bash salt-run digicert.list_domains - ''' + """ if container_id: - url = '{0}/domain?{1}'.format(_base_url(), container_id) + url = "{0}/domain?{1}".format(_base_url(), container_id) else: - url = '{0}/domain'.format(_base_url()) + url = "{0}/domain".format(_base_url()) - orgs = _paginate(url, - "domains", - method='GET', - decode=True, - decode_type='json', - header_dict={ - 'X-DC-DEVKEY': _api_key(), - 'Content-Type': 'application/json', - } + orgs = _paginate( + url, + "domains", + method="GET", + decode=True, + decode_type="json", + header_dict={"X-DC-DEVKEY": _api_key(), "Content-Type": "application/json"}, ) - ret = {'domains': orgs} + ret = {"domains": orgs} return ret def list_requests(status=None): - ''' + """ List certificate requests made to CertCentral. You can filter by status: ``pending``, ``approved``, ``rejected`` @@ -161,30 +163,28 @@ def list_requests(status=None): .. code-block:: bash salt-run digicert.list_requests pending - ''' + """ if status: - url = '{0}/request?status={1}'.format(_base_url(), status) + url = "{0}/request?status={1}".format(_base_url(), status) else: - url = '{0}/request'.format(_base_url()) + url = "{0}/request".format(_base_url()) - reqs = _paginate(url, - "requests", - method='GET', - decode=True, - decode_type='json', - raise_error=False, - header_dict={ - 'X-DC-DEVKEY': _api_key(), - 'Content-Type': 'application/json', - } + reqs = _paginate( + url, + "requests", + method="GET", + decode=True, + decode_type="json", + raise_error=False, + header_dict={"X-DC-DEVKEY": _api_key(), "Content-Type": "application/json"}, ) - ret = {'requests': reqs} + ret = {"requests": reqs} return ret def list_orders(status=None): - ''' + """ List certificate orders made to CertCentral. CLI Example: @@ -192,27 +192,31 @@ def list_orders(status=None): .. code-block:: bash salt-run digicert.list_orders - ''' - url = '{0}/order/certificate'.format(_base_url()) + """ + url = "{0}/order/certificate".format(_base_url()) - reqs = _paginate(url, - "orders", - method='GET', - decode=True, - decode_type='json', - raise_error=False, - header_dict={ - 'X-DC-DEVKEY': _api_key(), - 'Content-Type': 'application/json', - } + reqs = _paginate( + url, + "orders", + method="GET", + decode=True, + decode_type="json", + raise_error=False, + header_dict={"X-DC-DEVKEY": _api_key(), "Content-Type": "application/json"}, ) - ret = {'orders': reqs} + ret = {"orders": reqs} return ret -def get_certificate(order_id=None, certificate_id=None, minion_id=None, cert_format='pem_all', filename=None): - ''' +def get_certificate( + order_id=None, + certificate_id=None, + minion_id=None, + cert_format="pem_all", + filename=None, +): + """ Retrieve a certificate by order_id or certificate_id and write it to stdout or a filename. A list of permissible cert_formats is here: @@ -235,72 +239,71 @@ def get_certificate(order_id=None, certificate_id=None, minion_id=None, cert_for command you will want to leave off the ``filename`` argument and make sure to include ``--no-color`` so there will be no terminal ANSI escape sequences. - ''' + """ if order_id: order_cert = salt.utils.http.query( - '{0}/order/certificate/{1}'.format(_base_url(), - order_id), - method='GET', + "{0}/order/certificate/{1}".format(_base_url(), order_id), + method="GET", raise_error=False, decode=True, - decode_type='json', + decode_type="json", header_dict={ - 'X-DC-DEVKEY': _api_key(), - 'Content-Type': 'application/json', - } + "X-DC-DEVKEY": _api_key(), + "Content-Type": "application/json", + }, ) - if order_cert['dict'].get('status') != 'issued': - return {'certificate': order_cert['dict']} + if order_cert["dict"].get("status") != "issued": + return {"certificate": order_cert["dict"]} - if order_cert['dict'].get('errors', False): - return {'certificate': order_cert['dict']} + if order_cert["dict"].get("errors", False): + return {"certificate": order_cert["dict"]} - certificate_id = order_cert['dict'].get('certificate').get('id', None) - common_name = order_cert['dict'].get('certificate').get('common_name') + certificate_id = order_cert["dict"].get("certificate").get("id", None) + common_name = order_cert["dict"].get("certificate").get("common_name") if not certificate_id: - return {'certificate': - {'errors': - {'code': 'unknown', - 'message': 'Unknown error, no certificate ID passed on command line or in body returned from API'}}} + return { + "certificate": { + "errors": { + "code": "unknown", + "message": "Unknown error, no certificate ID passed on command line or in body returned from API", + } + } + } if filename: ret_cert = salt.utils.http.query( - '{0}/certificate/{1}/download/format/{2}'.format(_base_url(), - certificate_id, - cert_format), - method='GET', + "{0}/certificate/{1}/download/format/{2}".format( + _base_url(), certificate_id, cert_format + ), + method="GET", decode=False, text=False, headers=True, text_out=filename, raise_error=False, - header_dict={ - 'X-DC-DEVKEY': _api_key(), - } - ) + header_dict={"X-DC-DEVKEY": _api_key()}, + ) else: ret_cert = salt.utils.http.query( - '{0}/certificate/{1}/download/format/{2}'.format(_base_url(), - certificate_id, - cert_format), - method='GET', + "{0}/certificate/{1}/download/format/{2}".format( + _base_url(), certificate_id, cert_format + ), + method="GET", text=False, decode=False, raise_error=False, - header_dict={ - 'X-DC-DEVKEY': _api_key(), - } - ) - if 'errors' in ret_cert: - return {'certificate': ret_cert} + header_dict={"X-DC-DEVKEY": _api_key()}, + ) + if "errors" in ret_cert: + return {"certificate": ret_cert} - if 'body' not in ret_cert: - ret = {'certificate': ret_cert} + if "body" not in ret_cert: + ret = {"certificate": ret_cert} cert = ret_cert if isinstance(ret_cert, dict): - ret = ret_cert['body'] + ret = ret_cert["body"] cert = ret else: ret = ret_cert @@ -313,11 +316,20 @@ def get_certificate(order_id=None, certificate_id=None, minion_id=None, cert_for os.write(fd, cert) os.close(fd) - cmd = ['openssl', 'x509', '-noout', '-subject', '-nameopt', 'multiline', '-in', filename] + cmd = [ + "openssl", + "x509", + "-noout", + "-subject", + "-nameopt", + "multiline", + "-in", + filename, + ] out = subprocess.check_output(cmd) common_name = None for l in out.splitlines(): - common_name_match = re.search(' *commonName *= *(.*)', l) + common_name_match = re.search(" *commonName *= *(.*)", l) if common_name_match: common_name = common_name_match.group(1) break @@ -325,25 +337,30 @@ def get_certificate(order_id=None, certificate_id=None, minion_id=None, cert_for os.unlink(tmpfilename) if common_name: - bank = 'digicert/domains' + bank = "digicert/domains" cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) try: data = cache.fetch(bank, common_name) except TypeError: - data = {'certificate': cert} + data = {"certificate": cert} cache.store(bank, common_name, data) - if 'headers' in ret_cert: - return {'certificate': {'filename': filename, - 'original_filename': ret_cert['headers'].get('Content-Disposition', 'Not provided'), - 'Content-Type': ret_cert['headers'].get('Content-Type', 'Not provided') - }} + if "headers" in ret_cert: + return { + "certificate": { + "filename": filename, + "original_filename": ret_cert["headers"].get( + "Content-Disposition", "Not provided" + ), + "Content-Type": ret_cert["headers"].get("Content-Type", "Not provided"), + } + } - return {'certificate': cert} + return {"certificate": cert} def list_organizations(container_id=None, include_validation=True): - ''' + """ List organizations that CertCentral knows about. You can filter by container_id (also known as "Division") by passing a container_id. This function returns validation information by default; pass @@ -354,29 +371,39 @@ def list_organizations(container_id=None, include_validation=True): .. code-block:: bash salt-run digicert.list_organizations - ''' + """ - orgs = _paginate('{0}/organization'.format(_base_url()), - "organizations", - method='GET', - decode=True, - decode_type='json', - header_dict={ - 'X-DC-DEVKEY': _api_key(), - 'Content-Type': 'application/json', - } + orgs = _paginate( + "{0}/organization".format(_base_url()), + "organizations", + method="GET", + decode=True, + decode_type="json", + header_dict={"X-DC-DEVKEY": _api_key(), "Content-Type": "application/json"}, ) - ret = {'organizations': orgs} + ret = {"organizations": orgs} return ret -def order_certificate(minion_id, common_name, organization_id, validity_years, - cert_key_passphrase=None, signature_hash=None, key_len=2048, - dns_names=None, organization_units=None, server_platform=None, - custom_expiration_date=None, comments=None, disable_renewal_notifications=False, - product_type_hint=None, renewal_of_order_id=None): - ''' +def order_certificate( + minion_id, + common_name, + organization_id, + validity_years, + cert_key_passphrase=None, + signature_hash=None, + key_len=2048, + dns_names=None, + organization_units=None, + server_platform=None, + custom_expiration_date=None, + comments=None, + disable_renewal_notifications=False, + product_type_hint=None, + renewal_of_order_id=None, +): + """ Order a certificate. Requires that an Organization has been created inside Digicert's CertCentral. See here for API documentation: @@ -394,98 +421,111 @@ def order_certificate(minion_id, common_name, organization_id, validity_years, This runner can also be used to renew a certificate by passing `renewal_of_order_id`. Previous order details can be retrieved with digicertapi.list_orders. - ''' + """ if dns_names and isinstance(dns_names, six.string_types): dns_names = [dns_names] if dns_names and not isinstance(dns_names, collections.Sequence): - raise SaltRunnerError('order_certificate needs a single dns_name, or an array of dns_names.') - certificate = {'common_name': common_name} - certificate['dns_names'] = dns_names + raise SaltRunnerError( + "order_certificate needs a single dns_name, or an array of dns_names." + ) + certificate = {"common_name": common_name} + certificate["dns_names"] = dns_names if signature_hash: - certificate['signature_hash'] = signature_hash + certificate["signature_hash"] = signature_hash else: - certificate['signature_hash'] = __opts__.get('digicert', {}).get('shatype', 'sha256') + certificate["signature_hash"] = __opts__.get("digicert", {}).get( + "shatype", "sha256" + ) body = {} if organization_units and isinstance(organization_units, six.string_types): organization_units = [organization_units] if organization_units and not isinstance(organization_units, collections.Sequence): - raise SaltRunnerError('Organization_units is not a valid data type.') + raise SaltRunnerError("Organization_units is not a valid data type.") if organization_units: - certificate['organization_units'] = organization_units + certificate["organization_units"] = organization_units if organization_units: # Currently the Digicert API requires organization units to be an array # but only pays attention to the first one. - csr = gen_csr(minion_id, common_name, organization_id, - ou_name=organization_units[0], - shatype=certificate['signature_hash'], key_len=key_len, - password=cert_key_passphrase) + csr = gen_csr( + minion_id, + common_name, + organization_id, + ou_name=organization_units[0], + shatype=certificate["signature_hash"], + key_len=key_len, + password=cert_key_passphrase, + ) else: - csr = gen_csr(minion_id, common_name, organization_id, - shatype=certificate['signature_hash'], key_len=key_len, - password=cert_key_passphrase) + csr = gen_csr( + minion_id, + common_name, + organization_id, + shatype=certificate["signature_hash"], + key_len=key_len, + password=cert_key_passphrase, + ) - certificate['csr'] = csr + certificate["csr"] = csr if server_platform: - certificate['server_platform']['id'] = server_platform + certificate["server_platform"]["id"] = server_platform - body['organization'] = {'id': organization_id} + body["organization"] = {"id": organization_id} if custom_expiration_date: - body['custom_expiration_date'] = custom_expiration_date + body["custom_expiration_date"] = custom_expiration_date if validity_years: - body['validity_years'] = validity_years + body["validity_years"] = validity_years if comments: - body['comments'] = comments + body["comments"] = comments - body['disable_renewal_notifications'] = disable_renewal_notifications + body["disable_renewal_notifications"] = disable_renewal_notifications if product_type_hint: - body['product'] = {'type_hint': product_type_hint} + body["product"] = {"type_hint": product_type_hint} if renewal_of_order_id: - body['renewal_of_order_id'] = renewal_of_order_id + body["renewal_of_order_id"] = renewal_of_order_id - body['certificate'] = certificate + body["certificate"] = certificate encoded_body = salt.utils.json.dumps(body) qdata = salt.utils.http.query( - '{0}/order/certificate/ssl'.format(_base_url()), - method='POST', + "{0}/order/certificate/ssl".format(_base_url()), + method="POST", data=encoded_body, decode=True, - decode_type='json', - header_dict={ - 'X-DC-DEVKEY': _api_key(), - 'Content-Type': 'application/json', - }, - raise_error=False + decode_type="json", + header_dict={"X-DC-DEVKEY": _api_key(), "Content-Type": "application/json"}, + raise_error=False, ) - if 'errors' not in qdata['dict']: - bank = 'digicert/domains' + if "errors" not in qdata["dict"]: + bank = "digicert/domains" cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) data = cache.fetch(bank, common_name) if data is None: data = {} - data.update({ - 'minion_id': minion_id, - 'order_id': qdata['dict']['requests'][0]['id'], - 'csr': csr, - }) + data.update( + { + "minion_id": minion_id, + "order_id": qdata["dict"]["requests"][0]["id"], + "csr": csr, + } + ) cache.store(bank, common_name, data) _id_map(minion_id, common_name) - return {'order': qdata['dict']} + return {"order": qdata["dict"]} def gen_key(minion_id, dns_name=None, password=None, key_len=2048): - ''' + """ Generate and return a private_key. If a ``dns_name`` is passed in, the private_key will be cached under that name. @@ -494,32 +534,33 @@ def gen_key(minion_id, dns_name=None, password=None, key_len=2048): .. code-block:: bash salt-run digicert.gen_key <minion_id> [dns_name] [password] - ''' - keygen_type = 'RSA' + """ + keygen_type = "RSA" if keygen_type == "RSA": if HAS_M2: gen = RSA.gen_key(key_len, 65537) - private_key = gen.as_pem(cipher='des_ede3_cbc', callback=lambda x: six.b(password)) + private_key = gen.as_pem( + cipher="des_ede3_cbc", callback=lambda x: six.b(password) + ) else: gen = RSA.generate(bits=key_len) - private_key = gen.exportKey('PEM', password) + private_key = gen.exportKey("PEM", password) if dns_name is not None: - bank = 'digicert/domains' + bank = "digicert/domains" cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) try: data = cache.fetch(bank, dns_name) - data['private_key'] = private_key - data['minion_id'] = minion_id + data["private_key"] = private_key + data["minion_id"] = minion_id except TypeError: - data = {'private_key': private_key, - 'minion_id': minion_id} + data = {"private_key": private_key, "minion_id": minion_id} cache.store(bank, dns_name, data) return private_key def get_org_details(organization_id): - ''' + """ Return the details for an organization CLI Example: @@ -529,103 +570,107 @@ def get_org_details(organization_id): salt-run digicert.get_org_details 34 Returns a dictionary with the org details, or with 'error' and 'status' keys. - ''' + """ qdata = salt.utils.http.query( - '{0}/organization/{1}'.format(_base_url(), organization_id), - method='GET', + "{0}/organization/{1}".format(_base_url(), organization_id), + method="GET", decode=True, - decode_type='json', - header_dict={ - 'X-DC-DEVKEY': _api_key(), - 'Content-Type': 'application/json', - }, + decode_type="json", + header_dict={"X-DC-DEVKEY": _api_key(), "Content-Type": "application/json"}, ) return qdata def gen_csr( - minion_id, - dns_name, - organization_id, - ou_name=None, - key_len=2048, - shatype='sha256', - password=None): - ''' + minion_id, + dns_name, + organization_id, + ou_name=None, + key_len=2048, + shatype="sha256", + password=None, +): + """ CLI Example: .. code-block:: bash salt-run digicert.gen_csr <minion_id> <dns_name> - ''' + """ org_details = get_org_details(organization_id) - if 'error' in org_details: - raise SaltRunnerError('Problem getting organization details for organization_id={0} ({1})'.format(organization_id, org_details['error'])) - if org_details['dict'].get('status', 'active') == 'inactive': - raise SaltRunnerError('Organization with organization_id={0} is marked inactive'.format(organization_id)) + if "error" in org_details: + raise SaltRunnerError( + "Problem getting organization details for organization_id={0} ({1})".format( + organization_id, org_details["error"] + ) + ) + if org_details["dict"].get("status", "active") == "inactive": + raise SaltRunnerError( + "Organization with organization_id={0} is marked inactive".format( + organization_id + ) + ) tmpdir = tempfile.mkdtemp() os.chmod(tmpdir, 0o700) - bank = 'digicert/domains' + bank = "digicert/domains" cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) data = cache.fetch(bank, dns_name) if data is None: data = {} - if 'private_key' not in data: - data['private_key'] = gen_key(minion_id, dns_name, password, key_len=key_len) + if "private_key" not in data: + data["private_key"] = gen_key(minion_id, dns_name, password, key_len=key_len) - tmppriv = '{0}/priv'.format(tmpdir) - tmpcsr = '{0}/csr'.format(tmpdir) - with salt.utils.files.fopen(tmppriv, 'w') as if_: - if_.write(salt.utils.stringutils.to_str(data['private_key'])) + tmppriv = "{0}/priv".format(tmpdir) + tmpcsr = "{0}/csr".format(tmpdir) + with salt.utils.files.fopen(tmppriv, "w") as if_: + if_.write(salt.utils.stringutils.to_str(data["private_key"])) - subject = '/C={0}/ST={1}/L={2}/O={3}'.format( - org_details['dict']['country'], - org_details['dict']['state'], - org_details['dict']['city'], - org_details['dict']['display_name']) + subject = "/C={0}/ST={1}/L={2}/O={3}".format( + org_details["dict"]["country"], + org_details["dict"]["state"], + org_details["dict"]["city"], + org_details["dict"]["display_name"], + ) if ou_name: - subject = subject + '/OU={0}'.format(ou_name) + subject = subject + "/OU={0}".format(ou_name) - subject = subject + '/CN={0}'.format(dns_name) + subject = subject + "/CN={0}".format(dns_name) cmd = "openssl req -new -{0} -key {1} -out {2} -subj '{3}'".format( - shatype, - tmppriv, - tmpcsr, - subject + shatype, tmppriv, tmpcsr, subject ) - output = __salt__['salt.cmd']('cmd.run', cmd) + output = __salt__["salt.cmd"]("cmd.run", cmd) - if 'problems making Certificate Request' in output: + if "problems making Certificate Request" in output: raise CommandExecutionError( - 'There was a problem generating the CSR. Please ensure that you ' - 'have a valid Organization established inside CertCentral' + "There was a problem generating the CSR. Please ensure that you " + "have a valid Organization established inside CertCentral" ) - with salt.utils.files.fopen(tmpcsr, 'r') as of_: + with salt.utils.files.fopen(tmpcsr, "r") as of_: csr = salt.utils.stringutils.to_unicode(of_.read()) - data['minion_id'] = minion_id - data['csr'] = csr + data["minion_id"] = minion_id + data["csr"] = csr cache.store(bank, dns_name, data) return csr # Request and renew are the same, so far as this module is concerned -#renew = request +# renew = request def _id_map(minion_id, dns_name): - ''' + """ Maintain a relationship between a minion and a dns name - ''' - bank = 'digicert/minions' + """ + bank = "digicert/minions" cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) dns_names = cache.fetch(bank, minion_id) if not isinstance(dns_names, list): @@ -636,7 +681,7 @@ def _id_map(minion_id, dns_name): def show_organization(domain): - ''' + """ Show organization information, especially the company id CLI Example: @@ -644,26 +689,22 @@ def show_organization(domain): .. code-block:: bash salt-run digicert.show_company example.com - ''' + """ data = salt.utils.http.query( - '{0}/companies/domain/{1}'.format(_base_url(), domain), + "{0}/companies/domain/{1}".format(_base_url(), domain), status=True, decode=True, - decode_type='json', - header_dict={ - 'tppl-api-key': _api_key(), - }, + decode_type="json", + header_dict={"tppl-api-key": _api_key()}, ) - status = data['status'] - if six.text_type(status).startswith('4') or six.text_type(status).startswith('5'): - raise CommandExecutionError( - 'There was an API error: {0}'.format(data['error']) - ) - return data.get('dict', {}) + status = data["status"] + if six.text_type(status).startswith("4") or six.text_type(status).startswith("5"): + raise CommandExecutionError("There was an API error: {0}".format(data["error"])) + return data.get("dict", {}) def show_csrs(): - ''' + """ Show certificate requests for this API key CLI Example: @@ -671,26 +712,22 @@ def show_csrs(): .. code-block:: bash salt-run digicert.show_csrs - ''' + """ data = salt.utils.http.query( - '{0}/certificaterequests'.format(_base_url()), + "{0}/certificaterequests".format(_base_url()), status=True, decode=True, - decode_type='json', - header_dict={ - 'tppl-api-key': _api_key(), - }, + decode_type="json", + header_dict={"tppl-api-key": _api_key()}, ) - status = data['status'] - if six.text_type(status).startswith('4') or six.text_type(status).startswith('5'): - raise CommandExecutionError( - 'There was an API error: {0}'.format(data['error']) - ) - return data.get('dict', {}) + status = data["status"] + if six.text_type(status).startswith("4") or six.text_type(status).startswith("5"): + raise CommandExecutionError("There was an API error: {0}".format(data["error"])) + return data.get("dict", {}) def show_rsa(minion_id, dns_name): - ''' + """ Show a private RSA key CLI Example: @@ -698,17 +735,15 @@ def show_rsa(minion_id, dns_name): .. code-block:: bash salt-run digicert.show_rsa myminion domain.example.com - ''' + """ cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) - bank = 'digicert/domains' - data = cache.fetch( - bank, dns_name - ) - return data['private_key'] + bank = "digicert/domains" + data = cache.fetch(bank, dns_name) + return data["private_key"] def list_domain_cache(): - ''' + """ List domains that have been cached CLI Example: @@ -716,13 +751,13 @@ def list_domain_cache(): .. code-block:: bash salt-run digicert.list_domain_cache - ''' + """ cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) - return cache.list('digicert/domains') + return cache.list("digicert/domains") def del_cached_domain(domains): - ''' + """ Delete cached domains from the master CLI Example: @@ -730,21 +765,21 @@ def del_cached_domain(domains): .. code-block:: bash salt-run digicert.del_cached_domain domain1.example.com,domain2.example.com - ''' + """ cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) if isinstance(domains, six.string_types): - domains = domains.split(',') + domains = domains.split(",") if not isinstance(domains, list): raise CommandExecutionError( - 'You must pass in either a string containing one or more domains ' - 'separated by commas, or a list of single domain strings' + "You must pass in either a string containing one or more domains " + "separated by commas, or a list of single domain strings" ) success = [] failed = [] for domain in domains: try: - cache.flush('digicert/domains', domain) + cache.flush("digicert/domains", domain) success.append(domain) except CommandExecutionError: failed.append(domain) - return {'Succeeded': success, 'Failed': failed} + return {"Succeeded": success, "Failed": failed} diff --git a/salt/runners/doc.py b/salt/runners/doc.py index af42c99eedb..23c8607d051 100644 --- a/salt/runners/doc.py +++ b/salt/runners/doc.py @@ -1,31 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" A runner module to collect and display the inline documentation from the various module types -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import itertools # Import salt libs import salt.client import salt.runner import salt.wheel +from salt.exceptions import SaltClientError # Import 3rd-party libs from salt.ext import six -from salt.exceptions import SaltClientError def __virtual__(): - ''' + """ Always load - ''' + """ return True def runner(): - ''' + """ Return all inline documentation for runner modules CLI Example: @@ -33,14 +34,14 @@ def runner(): .. code-block:: bash salt-run doc.runner - ''' + """ client = salt.runner.RunnerClient(__opts__) ret = client.get_docs() return ret def wheel(): - ''' + """ Return all inline documentation for wheel modules CLI Example: @@ -48,14 +49,14 @@ def wheel(): .. code-block:: bash salt-run doc.wheel - ''' + """ client = salt.wheel.Wheel(__opts__) ret = client.get_docs() return ret def execution(): - ''' + """ Collect all the sys.doc output from each minion and return the aggregate CLI Example: @@ -63,19 +64,19 @@ def execution(): .. code-block:: bash salt-run doc.execution - ''' - client = salt.client.get_local_client(__opts__['conf_file']) + """ + client = salt.client.get_local_client(__opts__["conf_file"]) docs = {} try: - for ret in client.cmd_iter('*', 'sys.doc', timeout=__opts__['timeout']): + for ret in client.cmd_iter("*", "sys.doc", timeout=__opts__["timeout"]): for v in six.itervalues(ret): docs.update(v) except SaltClientError as exc: print(exc) return [] - i = itertools.chain.from_iterable([six.iteritems(docs['ret'])]) + i = itertools.chain.from_iterable([six.iteritems(docs["ret"])]) ret = dict(list(i)) return ret diff --git a/salt/runners/drac.py b/salt/runners/drac.py index 3480a23dd3b..45e3cb56582 100644 --- a/salt/runners/drac.py +++ b/salt/runners/drac.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Dell DRAC from the Master The login credentials need to be configured in the Salt master @@ -11,15 +11,17 @@ configuration file. username: admin password: secret -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import 3rd-party libs try: import paramiko + HAS_PARAMIKO = True except ImportError: HAS_PARAMIKO = False @@ -31,28 +33,33 @@ def __virtual__(): if HAS_PARAMIKO: return True - return False, 'The drac runner module cannot be loaded: paramiko package is not installed.' + return ( + False, + "The drac runner module cannot be loaded: paramiko package is not installed.", + ) def __connect(hostname, timeout=20, username=None, password=None): - ''' + """ Connect to the DRAC - ''' - drac_cred = __opts__.get('drac') - err_msg = 'No drac login credentials found. Please add the \'username\' and \'password\' ' \ - 'fields beneath a \'drac\' key in the master configuration file. Or you can ' \ - 'pass in a username and password as kwargs at the CLI.' + """ + drac_cred = __opts__.get("drac") + err_msg = ( + "No drac login credentials found. Please add the 'username' and 'password' " + "fields beneath a 'drac' key in the master configuration file. Or you can " + "pass in a username and password as kwargs at the CLI." + ) if not username: if drac_cred is None: log.error(err_msg) return False - username = drac_cred.get('username', None) + username = drac_cred.get("username", None) if not password: if drac_cred is None: log.error(err_msg) return False - password = drac_cred.get('password', None) + password = drac_cred.get("password", None) client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) @@ -60,35 +67,37 @@ def __connect(hostname, timeout=20, username=None, password=None): try: client.connect(hostname, username=username, password=password, timeout=timeout) except Exception as e: # pylint: disable=broad-except - log.error('Unable to connect to %s: %s', hostname, e) + log.error("Unable to connect to %s: %s", hostname, e) return False return client def __version(client): - ''' + """ Grab DRAC version - ''' - versions = {9: 'CMC', - 8: 'iDRAC6', - 10: 'iDRAC6', - 11: 'iDRAC6', - 16: 'iDRAC7', - 17: 'iDRAC7'} + """ + versions = { + 9: "CMC", + 8: "iDRAC6", + 10: "iDRAC6", + 11: "iDRAC6", + 16: "iDRAC7", + 17: "iDRAC7", + } if isinstance(client, paramiko.SSHClient): - (stdin, stdout, stderr) = client.exec_command('racadm getconfig -g idRacInfo') + (stdin, stdout, stderr) = client.exec_command("racadm getconfig -g idRacInfo") for i in stdout.readlines(): - if i[2:].startswith('idRacType'): - return versions.get(int(i[2:].split('=')[1]), None) + if i[2:].startswith("idRacType"): + return versions.get(int(i[2:].split("=")[1]), None) return None def pxe(hostname, timeout=20, username=None, password=None): - ''' + """ Connect to the Dell DRAC and have the boot order set to PXE and power cycle the system to PXE boot @@ -97,32 +106,32 @@ def pxe(hostname, timeout=20, username=None, password=None): .. code-block:: bash salt-run drac.pxe example.com - ''' + """ _cmds = [ - 'racadm config -g cfgServerInfo -o cfgServerFirstBootDevice pxe', - 'racadm config -g cfgServerInfo -o cfgServerBootOnce 1', - 'racadm serveraction powercycle', + "racadm config -g cfgServerInfo -o cfgServerFirstBootDevice pxe", + "racadm config -g cfgServerInfo -o cfgServerBootOnce 1", + "racadm serveraction powercycle", ] client = __connect(hostname, timeout, username, password) if isinstance(client, paramiko.SSHClient): for i, cmd in enumerate(_cmds, 1): - log.info('Executing command %s', i) + log.info("Executing command %s", i) (stdin, stdout, stderr) = client.exec_command(cmd) - if 'successful' in stdout.readline(): - log.info('Executing command: %s', cmd) + if "successful" in stdout.readline(): + log.info("Executing command: %s", cmd) else: - log.error('Unable to execute: %s', cmd) + log.error("Unable to execute: %s", cmd) return False return True def reboot(hostname, timeout=20, username=None, password=None): - ''' + """ Reboot a server using the Dell DRAC CLI Example: @@ -130,26 +139,26 @@ def reboot(hostname, timeout=20, username=None, password=None): .. code-block:: bash salt-run drac.reboot example.com - ''' + """ client = __connect(hostname, timeout, username, password) if isinstance(client, paramiko.SSHClient): - (stdin, stdout, stderr) = client.exec_command('racadm serveraction powercycle') + (stdin, stdout, stderr) = client.exec_command("racadm serveraction powercycle") - if 'successful' in stdout.readline(): - log.info('powercycle successful') + if "successful" in stdout.readline(): + log.info("powercycle successful") else: - log.error('powercycle racadm command failed') + log.error("powercycle racadm command failed") return False else: - log.error('client was not of type paramiko.SSHClient') + log.error("client was not of type paramiko.SSHClient") return False return True def poweroff(hostname, timeout=20, username=None, password=None): - ''' + """ Power server off CLI Example: @@ -157,26 +166,26 @@ def poweroff(hostname, timeout=20, username=None, password=None): .. code-block:: bash salt-run drac.poweroff example.com - ''' + """ client = __connect(hostname, timeout, username, password) if isinstance(client, paramiko.SSHClient): - (stdin, stdout, stderr) = client.exec_command('racadm serveraction powerdown') + (stdin, stdout, stderr) = client.exec_command("racadm serveraction powerdown") - if 'successful' in stdout.readline(): - log.info('powerdown successful') + if "successful" in stdout.readline(): + log.info("powerdown successful") else: - log.error('powerdown racadm command failed') + log.error("powerdown racadm command failed") return False else: - log.error('client was not of type paramiko.SSHClient') + log.error("client was not of type paramiko.SSHClient") return False return True def poweron(hostname, timeout=20, username=None, password=None): - ''' + """ Power server on CLI Example: @@ -184,26 +193,26 @@ def poweron(hostname, timeout=20, username=None, password=None): .. code-block:: bash salt-run drac.poweron example.com - ''' + """ client = __connect(hostname, timeout, username, password) if isinstance(client, paramiko.SSHClient): - (stdin, stdout, stderr) = client.exec_command('racadm serveraction powerup') + (stdin, stdout, stderr) = client.exec_command("racadm serveraction powerup") - if 'successful' in stdout.readline(): - log.info('powerup successful') + if "successful" in stdout.readline(): + log.info("powerup successful") else: - log.error('powerup racadm command failed') + log.error("powerup racadm command failed") return False else: - log.error('client was not of type paramiko.SSHClient') + log.error("client was not of type paramiko.SSHClient") return False return True def version(hostname, timeout=20, username=None, password=None): - ''' + """ Display the version of DRAC CLI Example: @@ -211,5 +220,5 @@ def version(hostname, timeout=20, username=None, password=None): .. code-block:: bash salt-run drac.version example.com - ''' + """ return __version(__connect(hostname, timeout, username, password)) diff --git a/salt/runners/error.py b/salt/runners/error.py index cfb93466c5e..39ec047dc1c 100644 --- a/salt/runners/error.py +++ b/salt/runners/error.py @@ -1,19 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Error generator to enable integration testing of salt runner error handling -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import python libs - - # Import salt libs import salt.utils.error +# Import python libs -def error(name=None, message=''): - ''' + +def error(name=None, message=""): + """ If name is None Then return empty dict Otherwise raise an exception with __name__ from name, message from message @@ -24,7 +23,7 @@ def error(name=None, message=''): salt-run error salt-run error.error name="Exception" message="This is an error." - ''' + """ ret = {} if name is not None: salt.utils.error.raise_error(name=name, message=message) diff --git a/salt/runners/event.py b/salt/runners/event.py index e76bf2b7d6a..1eacd0b4527 100644 --- a/salt/runners/event.py +++ b/salt/runners/event.py @@ -1,21 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" Module for sending events using the runner system. .. versionadded:: 2016.11.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging import salt.utils.event - log = logging.getLogger(__name__) def send(tag, data=None): - ''' + """ Send an event with the given tag and data. This is useful for sending events directly to the master from the shell @@ -73,8 +72,9 @@ def send(tag, data=None): salt-run state.orchestrate orch.command pillar='{"exit_code": 0}' salt-run state.orchestrate orch.command pillar='{"exit_code": 1}' - ''' + """ data = data or {} - event = salt.utils.event.get_master_event(__opts__, __opts__['sock_dir'], - listen=False) + event = salt.utils.event.get_master_event( + __opts__, __opts__["sock_dir"], listen=False + ) return event.fire_event(data, tag) diff --git a/salt/runners/f5.py b/salt/runners/f5.py index 206d442c44b..2d9ad4d9f67 100644 --- a/salt/runners/f5.py +++ b/salt/runners/f5.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Runner to provide F5 Load Balancer functionality :depends: - pycontrol Python module @@ -16,14 +16,16 @@ Runner to provide F5 Load Balancer functionality bigip2.example.com: username: admin password: secret -''' +""" from __future__ import absolute_import, print_function, unicode_literals + # Import salt libs from salt.exceptions import CommandExecutionError # Import third party libs try: import pycontrol.pycontrol as f5 + HAS_PYCONTROL = True except ImportError: HAS_PYCONTROL = False @@ -43,138 +45,127 @@ class F5Mgmt(object): self._connect() def _connect(self): - ''' + """ Connect to F5 - ''' + """ try: - self.bigIP = f5.BIGIP(hostname=self.lb, - username=self.username, - password=self.password, - fromurl=True, - wsdls=['LocalLB.VirtualServer', - 'LocalLB.Pool']) - except Exception: # pylint: disable=broad-except - raise Exception( - 'Unable to connect to {0}'.format(self.lb) + self.bigIP = f5.BIGIP( + hostname=self.lb, + username=self.username, + password=self.password, + fromurl=True, + wsdls=["LocalLB.VirtualServer", "LocalLB.Pool"], ) + except Exception: # pylint: disable=broad-except + raise Exception("Unable to connect to {0}".format(self.lb)) return True def create_vs(self, name, ip, port, protocol, profile, pool_name): - ''' + """ Create a virtual server - ''' + """ vs = self.bigIP.LocalLB.VirtualServer - vs_def = vs.typefactory.create('Common.VirtualServerDefinition') + vs_def = vs.typefactory.create("Common.VirtualServerDefinition") vs_def.name = name vs_def.address = ip vs_def.port = port - common_protocols = vs.typefactory.create('Common.ProtocolType') + common_protocols = vs.typefactory.create("Common.ProtocolType") - p = [i[0] for i in common_protocols if i[0].split('_')[1] == protocol.upper()] + p = [i[0] for i in common_protocols if i[0].split("_")[1] == protocol.upper()] if p: vs_def.protocol = p else: - raise CommandExecutionError('Unknown protocol') + raise CommandExecutionError("Unknown protocol") - vs_def_seq = vs.typefactory.create('Common.VirtualServerSequence') + vs_def_seq = vs.typefactory.create("Common.VirtualServerSequence") vs_def_seq.item = [vs_def] - vs_type = vs.typefactory.create( - 'LocalLB.VirtualServer.VirtualServerType' - ) + vs_type = vs.typefactory.create("LocalLB.VirtualServer.VirtualServerType") vs_resource = vs.typefactory.create( - 'LocalLB.VirtualServer.VirtualServerResource' + "LocalLB.VirtualServer.VirtualServerResource" ) vs_resource.type = vs_type.RESOURCE_TYPE_POOL vs_resource.default_pool_name = pool_name resource_seq = vs.typefactory.create( - 'LocalLB.VirtualServer.VirtualServerResourceSequence' + "LocalLB.VirtualServer.VirtualServerResourceSequence" ) resource_seq.item = [vs_resource] - vs_context = vs.typefactory.create('LocalLB.ProfileContextType') - vs_profile = vs.typefactory.create( - 'LocalLB.VirtualServer.VirtualServerProfile' - ) + vs_context = vs.typefactory.create("LocalLB.ProfileContextType") + vs_profile = vs.typefactory.create("LocalLB.VirtualServer.VirtualServerProfile") vs_profile.profile_context = vs_context.PROFILE_CONTEXT_TYPE_ALL vs_profile.profile_name = protocol vs_profile_http = vs.typefactory.create( - 'LocalLB.VirtualServer.VirtualServerProfile' + "LocalLB.VirtualServer.VirtualServerProfile" ) vs_profile_http.profile_name = profile vs_profile_conn = vs.typefactory.create( - 'LocalLB.VirtualServer.VirtualServerProfile' + "LocalLB.VirtualServer.VirtualServerProfile" ) - vs_profile_conn.profile_name = 'oneconnect' + vs_profile_conn.profile_name = "oneconnect" vs_profile_seq = vs.typefactory.create( - 'LocalLB.VirtualServer.VirtualServerProfileSequence' + "LocalLB.VirtualServer.VirtualServerProfileSequence" ) vs_profile_seq.item = [vs_profile, vs_profile_http, vs_profile_conn] try: - vs.create(definitions=vs_def_seq, - wildmasks=['255.255.255.255'], - resources=resource_seq, - profiles=[vs_profile_seq]) + vs.create( + definitions=vs_def_seq, + wildmasks=["255.255.255.255"], + resources=resource_seq, + profiles=[vs_profile_seq], + ) except Exception as e: # pylint: disable=broad-except raise Exception( - 'Unable to create `{0}` virtual server\n\n{1}'.format(name, e) + "Unable to create `{0}` virtual server\n\n{1}".format(name, e) ) return True - def create_pool(self, name, method='ROUND_ROBIN'): - ''' + def create_pool(self, name, method="ROUND_ROBIN"): + """ Create a pool on the F5 load balancer - ''' - lbmethods = self.bigIP.LocalLB.Pool.typefactory.create( - 'LocalLB.LBMethod' - ) + """ + lbmethods = self.bigIP.LocalLB.Pool.typefactory.create("LocalLB.LBMethod") - supported_method = [i[0] for i in lbmethods if ( - i[0].split('_', 2)[-1] == method.upper() - )] + supported_method = [ + i[0] for i in lbmethods if (i[0].split("_", 2)[-1] == method.upper()) + ] if supported_method and not self.check_pool(name): try: - self.bigIP.LocalLB.Pool.create(pool_names=[name], - lb_methods=[supported_method], - members=[[]]) - except Exception as e: # pylint: disable=broad-except - raise Exception( - 'Unable to create `{0}` pool\n\n{1}'.format(name, e) + self.bigIP.LocalLB.Pool.create( + pool_names=[name], lb_methods=[supported_method], members=[[]] ) + except Exception as e: # pylint: disable=broad-except + raise Exception("Unable to create `{0}` pool\n\n{1}".format(name, e)) else: - raise Exception('Unsupported method') + raise Exception("Unsupported method") return True def add_pool_member(self, name, port, pool_name): - ''' + """ Add a node to a pool - ''' + """ if not self.check_pool(pool_name): - raise CommandExecutionError( - '{0} pool does not exists'.format(pool_name) - ) + raise CommandExecutionError("{0} pool does not exists".format(pool_name)) members_seq = self.bigIP.LocalLB.Pool.typefactory.create( - 'Common.IPPortDefinitionSequence' + "Common.IPPortDefinitionSequence" ) members_seq.items = [] - member = self.bigIP.LocalLB.Pool.typefactory.create( - 'Common.IPPortDefinition' - ) + member = self.bigIP.LocalLB.Pool.typefactory.create("Common.IPPortDefinition") member.address = name member.port = port @@ -182,40 +173,39 @@ class F5Mgmt(object): members_seq.items.append(member) try: - self.bigIP.LocalLB.Pool.add_member(pool_names=[pool_name], - members=[members_seq]) + self.bigIP.LocalLB.Pool.add_member( + pool_names=[pool_name], members=[members_seq] + ) except Exception as e: # pylint: disable=broad-except raise Exception( - 'Unable to add `{0}` to `{1}`\n\n{2}'.format(name, - pool_name, - e) + "Unable to add `{0}` to `{1}`\n\n{2}".format(name, pool_name, e) ) return True def check_pool(self, name): - ''' + """ Check to see if a pool exists - ''' + """ pools = self.bigIP.LocalLB.Pool for pool in pools.get_list(): - if pool.split('/')[-1] == name: + if pool.split("/")[-1] == name: return True return False def check_virtualserver(self, name): - ''' + """ Check to see if a virtual server exists - ''' + """ vs = self.bigIP.LocalLB.VirtualServer for v in vs.get_list(): - if v.split('/')[-1] == name: + if v.split("/")[-1] == name: return True return False def check_member_pool(self, member, pool_name): - ''' + """ Check a pool member exists in a specific pool - ''' + """ members = self.bigIP.LocalLB.Pool.get_member(pool_names=[pool_name])[0] for mem in members: if member == mem.address: @@ -223,17 +213,15 @@ class F5Mgmt(object): return False def lbmethods(self): - ''' + """ List all the load balancer methods - ''' - methods = self.bigIP.LocalLB.Pool.typefactory.create( - 'LocalLB.LBMethod' - ) - return [method[0].split('_', 2)[-1] for method in methods] + """ + methods = self.bigIP.LocalLB.Pool.typefactory.create("LocalLB.LBMethod") + return [method[0].split("_", 2)[-1] for method in methods] def create_vs(lb, name, ip, port, protocol, profile, pool_name): - ''' + """ Create a virtual server CLI Examples: @@ -241,19 +229,19 @@ def create_vs(lb, name, ip, port, protocol, profile, pool_name): .. code-block:: bash salt-run f5.create_vs lbalancer vs_name 10.0.0.1 80 tcp http poolname - ''' - if __opts__['load_balancers'].get(lb, None): - (username, password) = list(__opts__['load_balancers'][lb].values()) + """ + if __opts__["load_balancers"].get(lb, None): + (username, password) = list(__opts__["load_balancers"][lb].values()) else: - raise Exception('Unable to find `{0}` load balancer'.format(lb)) + raise Exception("Unable to find `{0}` load balancer".format(lb)) F5 = F5Mgmt(lb, username, password) F5.create_vs(name, ip, port, protocol, profile, pool_name) return True -def create_pool(lb, name, method='ROUND_ROBIN'): - ''' +def create_pool(lb, name, method="ROUND_ROBIN"): + """ Create a pool on the F5 load balancer CLI Examples: @@ -262,18 +250,18 @@ def create_pool(lb, name, method='ROUND_ROBIN'): salt-run f5.create_pool load_balancer pool_name loadbalance_method salt-run f5.create_pool load_balancer my_pool ROUND_ROBIN - ''' - if __opts__['load_balancers'].get(lb, None): - (username, password) = list(__opts__['load_balancers'][lb].values()) + """ + if __opts__["load_balancers"].get(lb, None): + (username, password) = list(__opts__["load_balancers"][lb].values()) else: - raise Exception('Unable to find `{0}` load balancer'.format(lb)) + raise Exception("Unable to find `{0}` load balancer".format(lb)) F5 = F5Mgmt(lb, username, password) F5.create_pool(name, method) return True def add_pool_member(lb, name, port, pool_name): - ''' + """ Add a node to a pool CLI Examples: @@ -281,18 +269,18 @@ def add_pool_member(lb, name, port, pool_name): .. code-block:: bash salt-run f5.add_pool_member load_balancer 10.0.0.1 80 my_pool - ''' - if __opts__['load_balancers'].get(lb, None): - (username, password) = list(__opts__['load_balancers'][lb].values()) + """ + if __opts__["load_balancers"].get(lb, None): + (username, password) = list(__opts__["load_balancers"][lb].values()) else: - raise Exception('Unable to find `{0}` load balancer'.format(lb)) + raise Exception("Unable to find `{0}` load balancer".format(lb)) F5 = F5Mgmt(lb, username, password) F5.add_pool_member(name, port, pool_name) return True def check_pool(lb, name): - ''' + """ Check to see if a pool exists CLI Examples: @@ -300,17 +288,17 @@ def check_pool(lb, name): .. code-block:: bash salt-run f5.check_pool load_balancer pool_name - ''' - if __opts__['load_balancers'].get(lb, None): - (username, password) = list(__opts__['load_balancers'][lb].values()) + """ + if __opts__["load_balancers"].get(lb, None): + (username, password) = list(__opts__["load_balancers"][lb].values()) else: - raise Exception('Unable to find `{0}` load balancer'.format(lb)) + raise Exception("Unable to find `{0}` load balancer".format(lb)) F5 = F5Mgmt(lb, username, password) return F5.check_pool(name) def check_virtualserver(lb, name): - ''' + """ Check to see if a virtual server exists CLI Examples: @@ -318,17 +306,17 @@ def check_virtualserver(lb, name): .. code-block:: bash salt-run f5.check_virtualserver load_balancer virtual_server - ''' - if __opts__['load_balancers'].get(lb, None): - (username, password) = list(__opts__['load_balancers'][lb].values()) + """ + if __opts__["load_balancers"].get(lb, None): + (username, password) = list(__opts__["load_balancers"][lb].values()) else: - raise Exception('Unable to find `{0}` load balancer'.format(lb)) + raise Exception("Unable to find `{0}` load balancer".format(lb)) F5 = F5Mgmt(lb, username, password) return F5.check_virtualserver(name) def check_member_pool(lb, member, pool_name): - ''' + """ Check a pool member exists in a specific pool CLI Examples: @@ -336,10 +324,10 @@ def check_member_pool(lb, member, pool_name): .. code-block:: bash salt-run f5.check_member_pool load_balancer 10.0.0.1 my_pool - ''' - if __opts__['load_balancers'].get(lb, None): - (username, password) = list(__opts__['load_balancers'][lb].values()) + """ + if __opts__["load_balancers"].get(lb, None): + (username, password) = list(__opts__["load_balancers"][lb].values()) else: - raise Exception('Unable to find `{0}` load balancer'.format(lb)) + raise Exception("Unable to find `{0}` load balancer".format(lb)) F5 = F5Mgmt(lb, username, password) return F5.check_member_pool(member, pool_name) diff --git a/salt/runners/fileserver.py b/salt/runners/fileserver.py index 27391ba0c28..64d09a8533d 100644 --- a/salt/runners/fileserver.py +++ b/salt/runners/fileserver.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Directly manage the Salt fileserver plugins -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs @@ -9,7 +9,7 @@ import salt.fileserver def envs(backend=None, sources=False): - ''' + """ Return the available fileserver environments. If no backend is provided, then the environments for all configured backends will be returned. @@ -34,13 +34,13 @@ def envs(backend=None, sources=False): salt-run fileserver.envs salt-run fileserver.envs backend=roots,git salt-run fileserver.envs git - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) return sorted(fileserver.envs(back=backend, sources=sources)) def clear_file_list_cache(saltenv=None, backend=None): - ''' + """ .. versionadded:: 2016.11.0 The Salt fileserver caches the files/directories/symlinks for each @@ -141,14 +141,14 @@ def clear_file_list_cache(saltenv=None, backend=None): salt-run fileserver.clear_file_list_cache saltenv=base backend=roots # Clear all file list caches from the 'roots' fileserver backend salt-run fileserver.clear_file_list_cache backend=roots - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) - load = {'saltenv': saltenv, 'fsbackend': backend} + load = {"saltenv": saltenv, "fsbackend": backend} return fileserver.clear_file_list_cache(load=load) -def file_list(saltenv='base', backend=None): - ''' +def file_list(saltenv="base", backend=None): + """ Return a list of files from the salt fileserver saltenv : base @@ -187,14 +187,14 @@ def file_list(saltenv='base', backend=None): salt-run fileserver.file_list saltenv=dev backend=git salt-run fileserver.file_list base hg,roots salt-run fileserver.file_list -git - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) - load = {'saltenv': saltenv, 'fsbackend': backend} + load = {"saltenv": saltenv, "fsbackend": backend} return fileserver.file_list(load=load) -def symlink_list(saltenv='base', backend=None): - ''' +def symlink_list(saltenv="base", backend=None): + """ Return a list of symlinked files and dirs saltenv : base @@ -233,14 +233,14 @@ def symlink_list(saltenv='base', backend=None): salt-run fileserver.symlink_list saltenv=dev backend=git salt-run fileserver.symlink_list base hg,roots salt-run fileserver.symlink_list -git - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) - load = {'saltenv': saltenv, 'fsbackend': backend} + load = {"saltenv": saltenv, "fsbackend": backend} return fileserver.symlink_list(load=load) -def dir_list(saltenv='base', backend=None): - ''' +def dir_list(saltenv="base", backend=None): + """ Return a list of directories in the given environment saltenv : base @@ -279,14 +279,14 @@ def dir_list(saltenv='base', backend=None): salt-run fileserver.dir_list saltenv=dev backend=git salt-run fileserver.dir_list base hg,roots salt-run fileserver.dir_list -git - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) - load = {'saltenv': saltenv, 'fsbackend': backend} + load = {"saltenv": saltenv, "fsbackend": backend} return fileserver.dir_list(load=load) -def empty_dir_list(saltenv='base', backend=None): - ''' +def empty_dir_list(saltenv="base", backend=None): + """ .. versionadded:: 2015.5.0 Return a list of empty directories in the given environment @@ -316,14 +316,14 @@ def empty_dir_list(saltenv='base', backend=None): salt-run fileserver.empty_dir_list salt-run fileserver.empty_dir_list saltenv=prod salt-run fileserver.empty_dir_list backend=roots - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) - load = {'saltenv': saltenv, 'fsbackend': backend} + load = {"saltenv": saltenv, "fsbackend": backend} return fileserver.file_list_emptydirs(load=load) def update(backend=None): - ''' + """ Update the fileserver cache. If no backend is provided, then the cache for all configured backends will be updated. @@ -347,14 +347,14 @@ def update(backend=None): salt-run fileserver.update salt-run fileserver.update backend=roots,git - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) fileserver.update(back=backend) return True def clear_cache(backend=None): - ''' + """ .. versionadded:: 2015.5.0 Clear the fileserver cache from VCS fileserver backends (:mod:`git @@ -378,21 +378,21 @@ def clear_cache(backend=None): salt-run fileserver.clear_cache backend=git,hg salt-run fileserver.clear_cache hg salt-run fileserver.clear_cache -roots - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) cleared, errors = fileserver.clear_cache(back=backend) ret = {} if cleared: - ret['cleared'] = cleared + ret["cleared"] = cleared if errors: - ret['errors'] = errors + ret["errors"] = errors if not ret: - return 'No cache was cleared' + return "No cache was cleared" return ret def clear_lock(backend=None, remote=None): - ''' + """ .. versionadded:: 2015.5.0 Clear the fileserver update lock from VCS fileserver backends (:mod:`git @@ -419,21 +419,21 @@ def clear_lock(backend=None, remote=None): salt-run fileserver.clear_lock backend=git,hg salt-run fileserver.clear_lock backend=git remote=github salt-run fileserver.clear_lock remote=bitbucket - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) cleared, errors = fileserver.clear_lock(back=backend, remote=remote) ret = {} if cleared: - ret['cleared'] = cleared + ret["cleared"] = cleared if errors: - ret['errors'] = errors + ret["errors"] = errors if not ret: - return 'No locks were removed' + return "No locks were removed" return ret def lock(backend=None, remote=None): - ''' + """ .. versionadded:: 2015.5.0 Set a fileserver update lock for VCS fileserver backends (:mod:`git @@ -461,14 +461,14 @@ def lock(backend=None, remote=None): salt-run fileserver.lock backend=git,hg salt-run fileserver.lock backend=git remote='*github.com*' salt-run fileserver.lock remote=bitbucket - ''' + """ fileserver = salt.fileserver.Fileserver(__opts__) locked, errors = fileserver.lock(back=backend, remote=remote) ret = {} if locked: - ret['locked'] = locked + ret["locked"] = locked if errors: - ret['errors'] = errors + ret["errors"] = errors if not ret: - return 'No locks were set' + return "No locks were set" return ret diff --git a/salt/runners/git_pillar.py b/salt/runners/git_pillar.py index 87bc7af1e43..068995e4d74 100644 --- a/salt/runners/git_pillar.py +++ b/salt/runners/git_pillar.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Runner module to directly manage the git external pillar -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -16,7 +16,7 @@ log = logging.getLogger(__name__) def update(branch=None, repo=None): - ''' + """ .. versionadded:: 2014.1.0 .. versionchanged:: 2015.8.4 @@ -59,11 +59,11 @@ def update(branch=None, repo=None): salt-run git_pillar.update # Run with debug logging salt-run git_pillar.update -l debug - ''' + """ ret = {} - for ext_pillar in __opts__.get('ext_pillar', []): + for ext_pillar in __opts__.get("ext_pillar", []): pillar_type = next(iter(ext_pillar)) - if pillar_type != 'git': + if pillar_type != "git": continue pillar_conf = ext_pillar[pillar_type] pillar = salt.utils.gitfs.GitPillar( @@ -71,7 +71,8 @@ def update(branch=None, repo=None): pillar_conf, per_remote_overrides=salt.pillar.git_pillar.PER_REMOTE_OVERRIDES, per_remote_only=salt.pillar.git_pillar.PER_REMOTE_ONLY, - global_only=salt.pillar.git_pillar.GLOBAL_ONLY) + global_only=salt.pillar.git_pillar.GLOBAL_ONLY, + ) for remote in pillar.remotes: # Skip this remote if it doesn't match the search criteria if branch is not None: @@ -84,9 +85,10 @@ def update(branch=None, repo=None): result = remote.fetch() except Exception as exc: # pylint: disable=broad-except log.error( - 'Exception \'%s\' caught while fetching git_pillar ' - 'remote \'%s\'', exc, remote.id, - exc_info_on_loglevel=logging.DEBUG + "Exception '%s' caught while fetching git_pillar " "remote '%s'", + exc, + remote.id, + exc_info_on_loglevel=logging.DEBUG, ) result = False finally: @@ -96,9 +98,9 @@ def update(branch=None, repo=None): if not ret: if branch is not None or repo is not None: raise SaltRunnerError( - 'Specified git branch/repo not found in ext_pillar config' + "Specified git branch/repo not found in ext_pillar config" ) else: - raise SaltRunnerError('No git_pillar remotes are configured') + raise SaltRunnerError("No git_pillar remotes are configured") return ret diff --git a/salt/runners/http.py b/salt/runners/http.py index c0f29980961..910c1b6df8d 100644 --- a/salt/runners/http.py +++ b/salt/runners/http.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Module for making various web calls. Primarily designed for webhooks and the like, but also useful for basic http testing. .. versionadded:: 2015.5.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + # Import Python libs import logging @@ -16,7 +17,7 @@ log = logging.getLogger(__name__) def query(url, output=True, **kwargs): - ''' + """ Query a resource, and decode the return data Passes through all the parameters described in the @@ -31,22 +32,22 @@ def query(url, output=True, **kwargs): params='key1=val1&key2=val2' salt-run http.query http://somelink.com/ method=POST \ data='<xml>somecontent</xml>' - ''' + """ if output is not True: - log.warning('Output option has been deprecated. Please use --quiet.') - if 'node' not in kwargs: - kwargs['node'] = 'master' + log.warning("Output option has been deprecated. Please use --quiet.") + if "node" not in kwargs: + kwargs["node"] = "master" opts = __opts__.copy() - if 'opts' in kwargs: - opts.update(kwargs['opts']) - del kwargs['opts'] + if "opts" in kwargs: + opts.update(kwargs["opts"]) + del kwargs["opts"] ret = salt.utils.http.query(url=url, opts=opts, **kwargs) return ret def update_ca_bundle(target=None, source=None, merge_files=None): - ''' + """ Update the local CA bundle file from a URL .. versionadded:: 2015.5.0 @@ -77,7 +78,5 @@ def update_ca_bundle(target=None, source=None, merge_files=None): .. code-block:: bash salt-run http.update_ca_bundle merge_files=/path/to/mycert.pem - ''' - return salt.utils.http.update_ca_bundle( - target, source, __opts__, merge_files - ) + """ + return salt.utils.http.update_ca_bundle(target, source, __opts__, merge_files) diff --git a/salt/runners/jobs.py b/salt/runners/jobs.py index 59e15f7bfad..8fb46606406 100644 --- a/salt/runners/jobs.py +++ b/salt/runners/jobs.py @@ -1,29 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" A convenience system to manage jobs, both active and already run -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import fnmatch import logging import os # Import salt libs import salt.client +import salt.minion import salt.payload +import salt.returners import salt.utils.args import salt.utils.files import salt.utils.jid -import salt.minion -import salt.returners +from salt.exceptions import SaltClientError # Import 3rd-party libs from salt.ext import six -from salt.exceptions import SaltClientError try: import dateutil.parser as dateutil_parser + DATEUTIL_SUPPORT = True except ImportError: DATEUTIL_SUPPORT = False @@ -32,7 +34,7 @@ log = logging.getLogger(__name__) def active(display_progress=False): - ''' + """ Return a report on all actively running jobs from a job id centric perspective @@ -41,49 +43,58 @@ def active(display_progress=False): .. code-block:: bash salt-run jobs.active - ''' + """ ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) try: - active_ = client.cmd('*', 'saltutil.running', timeout=__opts__['timeout']) + active_ = client.cmd("*", "saltutil.running", timeout=__opts__["timeout"]) except SaltClientError as client_error: print(client_error) return ret if display_progress: - __jid_event__.fire_event({ - 'message': 'Attempting to contact minions: {0}'.format(list(active_.keys())) - }, 'progress') + __jid_event__.fire_event( + { + "message": "Attempting to contact minions: {0}".format( + list(active_.keys()) + ) + }, + "progress", + ) for minion, data in six.iteritems(active_): if display_progress: - __jid_event__.fire_event({'message': 'Received reply from minion {0}'.format(minion)}, 'progress') + __jid_event__.fire_event( + {"message": "Received reply from minion {0}".format(minion)}, "progress" + ) if not isinstance(data, list): continue for job in data: - if not job['jid'] in ret: - ret[job['jid']] = _format_jid_instance(job['jid'], job) - ret[job['jid']].update({'Running': [{minion: job.get('pid', None)}], 'Returned': []}) + if not job["jid"] in ret: + ret[job["jid"]] = _format_jid_instance(job["jid"], job) + ret[job["jid"]].update( + {"Running": [{minion: job.get("pid", None)}], "Returned": []} + ) else: - ret[job['jid']]['Running'].append({minion: job['pid']}) + ret[job["jid"]]["Running"].append({minion: job["pid"]}) mminion = salt.minion.MasterMinion(__opts__) for jid in ret: - returner = _get_returner((__opts__['ext_job_cache'], __opts__['master_job_cache'])) - data = mminion.returners['{0}.get_jid'.format(returner)](jid) + returner = _get_returner( + (__opts__["ext_job_cache"], __opts__["master_job_cache"]) + ) + data = mminion.returners["{0}.get_jid".format(returner)](jid) if data: for minion in data: - if minion not in ret[jid]['Returned']: - ret[jid]['Returned'].append(minion) + if minion not in ret[jid]["Returned"]: + ret[jid]["Returned"].append(minion) return ret -def lookup_jid(jid, - ext_source=None, - returned=True, - missing=False, - display_progress=False): - ''' +def lookup_jid( + jid, ext_source=None, returned=True, missing=False, display_progress=False +): + """ Return the printout from a previously executed job jid @@ -112,59 +123,52 @@ def lookup_jid(jid, salt-run jobs.lookup_jid 20130916125524463507 salt-run jobs.lookup_jid 20130916125524463507 --out=highstate - ''' + """ ret = {} mminion = salt.minion.MasterMinion(__opts__) - returner = _get_returner(( - __opts__['ext_job_cache'], - ext_source, - __opts__['master_job_cache'] - )) + returner = _get_returner( + (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) + ) try: - data = list_job( - jid, - ext_source=ext_source, - display_progress=display_progress - ) + data = list_job(jid, ext_source=ext_source, display_progress=display_progress) except TypeError: - return ('Requested returner could not be loaded. ' - 'No JIDs could be retrieved.') + return "Requested returner could not be loaded. " "No JIDs could be retrieved." - targeted_minions = data.get('Minions', []) - returns = data.get('Result', {}) + targeted_minions = data.get("Minions", []) + returns = data.get("Result", {}) if returns: for minion in returns: if display_progress: - __jid_event__.fire_event({'message': minion}, 'progress') - if u'return' in returns[minion]: + __jid_event__.fire_event({"message": minion}, "progress") + if "return" in returns[minion]: if returned: - ret[minion] = returns[minion].get(u'return') + ret[minion] = returns[minion].get("return") else: if returned: - ret[minion] = returns[minion].get('return') + ret[minion] = returns[minion].get("return") if missing: for minion_id in (x for x in targeted_minions if x not in returns): - ret[minion_id] = 'Minion did not return' + ret[minion_id] = "Minion did not return" # We need to check to see if the 'out' key is present and use it to specify # the correct outputter, so we get highstate output for highstate runs. try: # Check if the return data has an 'out' key. We'll use that as the # outputter in the absence of one being passed on the CLI. - outputter = returns[next(iter(returns))].get('out') + outputter = returns[next(iter(returns))].get("out") except (StopIteration, AttributeError): outputter = None if outputter: - return {'outputter': outputter, 'data': ret} + return {"outputter": outputter, "data": ret} else: return ret def list_job(jid, ext_source=None, display_progress=False): - ''' + """ List a specific job given by its jid ext_source @@ -181,43 +185,41 @@ def list_job(jid, ext_source=None, display_progress=False): salt-run jobs.list_job 20130916125524463507 salt-run jobs.list_job 20130916125524463507 --out=pprint - ''' - ret = {'jid': jid} + """ + ret = {"jid": jid} mminion = salt.minion.MasterMinion(__opts__) - returner = _get_returner(( - __opts__['ext_job_cache'], - ext_source, - __opts__['master_job_cache'] - )) + returner = _get_returner( + (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) + ) if display_progress: __jid_event__.fire_event( - {'message': 'Querying returner: {0}'.format(returner)}, - 'progress' + {"message": "Querying returner: {0}".format(returner)}, "progress" ) - job = mminion.returners['{0}.get_load'.format(returner)](jid) + job = mminion.returners["{0}.get_load".format(returner)](jid) ret.update(_format_jid_instance(jid, job)) - ret['Result'] = mminion.returners['{0}.get_jid'.format(returner)](jid) + ret["Result"] = mminion.returners["{0}.get_jid".format(returner)](jid) - fstr = '{0}.get_endtime'.format(__opts__['master_job_cache']) - if (__opts__.get('job_cache_store_endtime') - and fstr in mminion.returners): + fstr = "{0}.get_endtime".format(__opts__["master_job_cache"]) + if __opts__.get("job_cache_store_endtime") and fstr in mminion.returners: endtime = mminion.returners[fstr](jid) if endtime: - ret['EndTime'] = endtime + ret["EndTime"] = endtime return ret -def list_jobs(ext_source=None, - outputter=None, - search_metadata=None, - search_function=None, - search_target=None, - start_time=None, - end_time=None, - display_progress=False): - ''' +def list_jobs( + ext_source=None, + outputter=None, + search_metadata=None, + search_function=None, + search_target=None, + start_time=None, + end_time=None, + display_progress=False, +): + """ List all detectable jobs and associated functions ext_source @@ -292,39 +294,38 @@ def list_jobs(ext_source=None, salt-run jobs.list_jobs search_function='test.*' search_target='localhost' search_metadata='{"bar": "foo"}' salt-run jobs.list_jobs start_time='2015, Mar 16 19:00' end_time='2015, Mar 18 22:00' - ''' - returner = _get_returner(( - __opts__['ext_job_cache'], - ext_source, - __opts__['master_job_cache'] - )) + """ + returner = _get_returner( + (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) + ) if display_progress: __jid_event__.fire_event( - {'message': 'Querying returner {0} for jobs.'.format(returner)}, - 'progress' + {"message": "Querying returner {0} for jobs.".format(returner)}, "progress" ) mminion = salt.minion.MasterMinion(__opts__) - ret = mminion.returners['{0}.get_jids'.format(returner)]() + ret = mminion.returners["{0}.get_jids".format(returner)]() mret = {} for item in ret: _match = True if search_metadata: _match = False - if 'Metadata' in ret[item]: + if "Metadata" in ret[item]: if isinstance(search_metadata, dict): for key in search_metadata: - if key in ret[item]['Metadata']: - if ret[item]['Metadata'][key] == search_metadata[key]: + if key in ret[item]["Metadata"]: + if ret[item]["Metadata"][key] == search_metadata[key]: _match = True else: - log.info('The search_metadata parameter must be specified' - ' as a dictionary. Ignoring.') + log.info( + "The search_metadata parameter must be specified" + " as a dictionary. Ignoring." + ) if search_target and _match: _match = False - if 'Target' in ret[item]: - targets = ret[item]['Target'] + if "Target" in ret[item]: + targets = ret[item]["Target"] if isinstance(targets, six.string_types): targets = [targets] for target in targets: @@ -334,52 +335,49 @@ def list_jobs(ext_source=None, if search_function and _match: _match = False - if 'Function' in ret[item]: + if "Function" in ret[item]: for key in salt.utils.args.split_input(search_function): - if fnmatch.fnmatch(ret[item]['Function'], key): + if fnmatch.fnmatch(ret[item]["Function"], key): _match = True if start_time and _match: _match = False if DATEUTIL_SUPPORT: parsed_start_time = dateutil_parser.parse(start_time) - _start_time = dateutil_parser.parse(ret[item]['StartTime']) + _start_time = dateutil_parser.parse(ret[item]["StartTime"]) if _start_time >= parsed_start_time: _match = True else: log.error( - '\'dateutil\' library not available, skipping start_time ' - 'comparison.' + "'dateutil' library not available, skipping start_time " + "comparison." ) if end_time and _match: _match = False if DATEUTIL_SUPPORT: parsed_end_time = dateutil_parser.parse(end_time) - _start_time = dateutil_parser.parse(ret[item]['StartTime']) + _start_time = dateutil_parser.parse(ret[item]["StartTime"]) if _start_time <= parsed_end_time: _match = True else: log.error( - '\'dateutil\' library not available, skipping end_time ' - 'comparison.' + "'dateutil' library not available, skipping end_time " "comparison." ) if _match: mret[item] = ret[item] if outputter: - return {'outputter': outputter, 'data': mret} + return {"outputter": outputter, "data": mret} else: return mret -def list_jobs_filter(count, - filter_find_job=True, - ext_source=None, - outputter=None, - display_progress=False): - ''' +def list_jobs_filter( + count, filter_find_job=True, ext_source=None, outputter=None, display_progress=False +): + """ List all detectable jobs and associated functions ext_source @@ -392,34 +390,31 @@ def list_jobs_filter(count, salt-run jobs.list_jobs_filter 50 salt-run jobs.list_jobs_filter 100 filter_find_job=False - ''' - returner = _get_returner(( - __opts__['ext_job_cache'], - ext_source, - __opts__['master_job_cache'] - )) + """ + returner = _get_returner( + (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) + ) if display_progress: __jid_event__.fire_event( - {'message': 'Querying returner {0} for jobs.'.format(returner)}, - 'progress' + {"message": "Querying returner {0} for jobs.".format(returner)}, "progress" ) mminion = salt.minion.MasterMinion(__opts__) - fun = '{0}.get_jids_filter'.format(returner) + fun = "{0}.get_jids_filter".format(returner) if fun not in mminion.returners: raise NotImplementedError( - '\'{0}\' returner function not implemented yet.'.format(fun) + "'{0}' returner function not implemented yet.".format(fun) ) ret = mminion.returners[fun](count, filter_find_job) if outputter: - return {'outputter': outputter, 'data': ret} + return {"outputter": outputter, "data": ret} else: return ret def print_job(jid, ext_source=None): - ''' + """ Print a specific job's detail given by its jid, including the return data. CLI Example: @@ -427,39 +422,36 @@ def print_job(jid, ext_source=None): .. code-block:: bash salt-run jobs.print_job 20130916125524463507 - ''' + """ ret = {} - returner = _get_returner(( - __opts__['ext_job_cache'], - ext_source, - __opts__['master_job_cache'] - )) + returner = _get_returner( + (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) + ) mminion = salt.minion.MasterMinion(__opts__) try: - job = mminion.returners['{0}.get_load'.format(returner)](jid) + job = mminion.returners["{0}.get_load".format(returner)](jid) ret[jid] = _format_jid_instance(jid, job) except TypeError: - ret[jid]['Result'] = ( - 'Requested returner {0} is not available. Jobs cannot be ' - 'retrieved. Check master log for details.'.format(returner) + ret[jid]["Result"] = ( + "Requested returner {0} is not available. Jobs cannot be " + "retrieved. Check master log for details.".format(returner) ) return ret - ret[jid]['Result'] = mminion.returners['{0}.get_jid'.format(returner)](jid) + ret[jid]["Result"] = mminion.returners["{0}.get_jid".format(returner)](jid) - fstr = '{0}.get_endtime'.format(__opts__['master_job_cache']) - if (__opts__.get('job_cache_store_endtime') - and fstr in mminion.returners): + fstr = "{0}.get_endtime".format(__opts__["master_job_cache"]) + if __opts__.get("job_cache_store_endtime") and fstr in mminion.returners: endtime = mminion.returners[fstr](jid) if endtime: - ret[jid]['EndTime'] = endtime + ret[jid]["EndTime"] = endtime return ret def exit_success(jid, ext_source=None): - ''' + """ Check if a job has been executed and exit successfully jid @@ -472,36 +464,35 @@ def exit_success(jid, ext_source=None): .. code-block:: bash salt-run jobs.exit_success 20160520145827701627 - ''' + """ ret = dict() - data = list_job( - jid, - ext_source=ext_source - ) + data = list_job(jid, ext_source=ext_source) - minions = data.get('Minions', []) - result = data.get('Result', {}) + minions = data.get("Minions", []) + result = data.get("Result", {}) for minion in minions: - if minion in result and 'return' in result[minion]: - ret[minion] = True if result[minion]['return'] else False + if minion in result and "return" in result[minion]: + ret[minion] = True if result[minion]["return"] else False else: ret[minion] = False for minion in result: - if 'return' in result[minion] and result[minion]['return']: + if "return" in result[minion] and result[minion]["return"]: ret[minion] = True return ret -def last_run(ext_source=None, - outputter=None, - metadata=None, - function=None, - target=None, - display_progress=False): - ''' +def last_run( + ext_source=None, + outputter=None, + metadata=None, + function=None, + target=None, + display_progress=False, +): + """ .. versionadded:: 2015.8.0 List all detectable jobs and associated functions @@ -514,19 +505,21 @@ def last_run(ext_source=None, salt-run jobs.last_run target=nodename salt-run jobs.last_run function='cmd.run' salt-run jobs.last_run metadata="{'foo': 'bar'}" - ''' + """ if metadata: if not isinstance(metadata, dict): - log.info('The metadata parameter must be specified as a dictionary') + log.info("The metadata parameter must be specified as a dictionary") return False - _all_jobs = list_jobs(ext_source=ext_source, - outputter=outputter, - search_metadata=metadata, - search_function=function, - search_target=target, - display_progress=display_progress) + _all_jobs = list_jobs( + ext_source=ext_source, + outputter=outputter, + search_metadata=metadata, + search_function=function, + search_target=target, + display_progress=display_progress, + ) if _all_jobs: last_job = sorted(_all_jobs)[-1] return print_job(last_job, ext_source) @@ -535,73 +528,74 @@ def last_run(ext_source=None, def _get_returner(returner_types): - ''' + """ Helper to iterate over returner_types and pick the first one - ''' + """ for returner in returner_types: if returner and returner is not None: return returner def _format_job_instance(job): - ''' + """ Helper to format a job instance - ''' + """ if not job: - ret = {'Error': 'Cannot contact returner or no job with this jid'} + ret = {"Error": "Cannot contact returner or no job with this jid"} return ret - ret = {'Function': job.get('fun', 'unknown-function'), - 'Arguments': list(job.get('arg', [])), - # unlikely but safeguard from invalid returns - 'Target': job.get('tgt', 'unknown-target'), - 'Target-type': job.get('tgt_type', 'list'), - 'User': job.get('user', 'root')} + ret = { + "Function": job.get("fun", "unknown-function"), + "Arguments": list(job.get("arg", [])), + # unlikely but safeguard from invalid returns + "Target": job.get("tgt", "unknown-target"), + "Target-type": job.get("tgt_type", "list"), + "User": job.get("user", "root"), + } - if 'metadata' in job: - ret['Metadata'] = job.get('metadata', {}) + if "metadata" in job: + ret["Metadata"] = job.get("metadata", {}) else: - if 'kwargs' in job: - if 'metadata' in job['kwargs']: - ret['Metadata'] = job['kwargs'].get('metadata', {}) + if "kwargs" in job: + if "metadata" in job["kwargs"]: + ret["Metadata"] = job["kwargs"].get("metadata", {}) - if 'Minions' in job: - ret['Minions'] = job['Minions'] + if "Minions" in job: + ret["Minions"] = job["Minions"] return ret def _format_jid_instance(jid, job): - ''' + """ Helper to format jid instance - ''' + """ ret = _format_job_instance(job) - ret.update({'StartTime': salt.utils.jid.jid_to_time(jid)}) + ret.update({"StartTime": salt.utils.jid.jid_to_time(jid)}) return ret def _walk_through(job_dir, display_progress=False): - ''' + """ Walk through the job dir and return jobs - ''' + """ serial = salt.payload.Serial(__opts__) for top in os.listdir(job_dir): t_path = os.path.join(job_dir, top) for final in os.listdir(t_path): - load_path = os.path.join(t_path, final, '.load.p') - with salt.utils.files.fopen(load_path, 'rb') as rfh: + load_path = os.path.join(t_path, final, ".load.p") + with salt.utils.files.fopen(load_path, "rb") as rfh: job = serial.load(rfh) if not os.path.isfile(load_path): continue - with salt.utils.files.fopen(load_path, 'rb') as rfh: + with salt.utils.files.fopen(load_path, "rb") as rfh: job = serial.load(rfh) - jid = job['jid'] + jid = job["jid"] if display_progress: __jid_event__.fire_event( - {'message': 'Found JID {0}'.format(jid)}, - 'progress' + {"message": "Found JID {0}".format(jid)}, "progress" ) yield jid, job, t_path, final diff --git a/salt/runners/launchd.py b/salt/runners/launchd.py index 52496bc2953..e2812edc2e8 100644 --- a/salt/runners/launchd.py +++ b/salt/runners/launchd.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Manage launchd plist files -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -10,7 +10,7 @@ import sys def write_launchd_plist(program): - ''' + """ Write a launchd plist for managing salt-master or salt-minion CLI Example: @@ -18,8 +18,8 @@ def write_launchd_plist(program): .. code-block:: bash salt-run launchd.write_launchd_plist salt-master - ''' - plist_sample_text = ''' + """ + plist_sample_text = """ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> @@ -46,18 +46,16 @@ def write_launchd_plist(program): </dict> </dict> </plist> - '''.strip() + """.strip() - supported_programs = ['salt-master', 'salt-minion'] + supported_programs = ["salt-master", "salt-minion"] if program not in supported_programs: - sys.stderr.write( - 'Supported programs: \'{0}\'\n'.format(supported_programs) - ) + sys.stderr.write("Supported programs: '{0}'\n".format(supported_programs)) sys.exit(-1) return plist_sample_text.format( program=program, python=sys.executable, - script=os.path.join(os.path.dirname(sys.executable), program) + script=os.path.join(os.path.dirname(sys.executable), program), ) diff --git a/salt/runners/lxc.py b/salt/runners/lxc.py index 0e2a1d482a9..bbd88a9743f 100644 --- a/salt/runners/lxc.py +++ b/salt/runners/lxc.py @@ -1,40 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Control Linux Containers via Salt :depends: lxc execution module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time -import os + import copy import logging +import os +import time # Import Salt libs import salt.client +import salt.key import salt.utils.args import salt.utils.cloud import salt.utils.files import salt.utils.stringutils import salt.utils.virt -import salt.key -from salt.utils.odict import OrderedDict as _OrderedDict # Import 3rd-party lib from salt.ext import six +from salt.utils.odict import OrderedDict as _OrderedDict log = logging.getLogger(__name__) # Don't shadow built-in's. -__func_alias__ = { - 'list_': 'list' -} +__func_alias__ = {"list_": "list"} def _do(name, fun, path=None): - ''' + """ Invoke a function in the lxc module with no args path @@ -42,27 +41,24 @@ def _do(name, fun, path=None): default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 - ''' + """ host = find_guest(name, quiet=True, path=path) if not host: return False - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) cmd_ret = client.cmd_iter( - host, - 'lxc.{0}'.format(fun), - [name], - kwarg={'path': path}, - timeout=60) + host, "lxc.{0}".format(fun), [name], kwarg={"path": path}, timeout=60 + ) data = next(cmd_ret) - data = data.get(host, {}).get('ret', None) + data = data.get(host, {}).get("ret", None) if data: data = {host: data} return data def _do_names(names, fun, path=None): - ''' + """ Invoke a function in the lxc module with no args path @@ -70,32 +66,35 @@ def _do_names(names, fun, path=None): default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 - ''' + """ ret = {} hosts = find_guests(names, path=path) if not hosts: return False - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) for host, sub_names in six.iteritems(hosts): cmds = [] for name in sub_names: - cmds.append(client.cmd_iter( + cmds.append( + client.cmd_iter( host, - 'lxc.{0}'.format(fun), + "lxc.{0}".format(fun), [name], - kwarg={'path': path}, - timeout=60)) + kwarg={"path": path}, + timeout=60, + ) + ) for cmd in cmds: data = next(cmd) - data = data.get(host, {}).get('ret', None) + data = data.get(host, {}).get("ret", None) if data: ret.update({host: data}) return ret def find_guest(name, quiet=False, path=None): - ''' + """ Returns the host for a container. path @@ -108,25 +107,25 @@ def find_guest(name, quiet=False, path=None): .. code-block:: bash salt-run lxc.find_guest name - ''' + """ if quiet: - log.warning("'quiet' argument is being deprecated." - ' Please migrate to --quiet') + log.warning( + "'quiet' argument is being deprecated." " Please migrate to --quiet" + ) for data in _list_iter(path=path): host, l = next(six.iteritems(data)) - for x in 'running', 'frozen', 'stopped': + for x in "running", "frozen", "stopped": if name in l[x]: if not quiet: __jid_event__.fire_event( - {'data': host, - 'outputter': 'lxc_find_host'}, - 'progress') + {"data": host, "outputter": "lxc_find_host"}, "progress" + ) return host return None def find_guests(names, path=None): - ''' + """ Return a dict of hosts and named guests path @@ -135,9 +134,9 @@ def find_guests(names, path=None): .. versionadded:: 2015.8.0 - ''' + """ ret = {} - names = names.split(',') + names = names.split(",") for data in _list_iter(path=path): host, stat = next(six.iteritems(data)) for state in stat: @@ -151,7 +150,7 @@ def find_guests(names, path=None): def init(names, host=None, saltcloud_mode=False, quiet=False, **kwargs): - ''' + """ Initialize a new container @@ -233,78 +232,81 @@ def init(names, host=None, saltcloud_mode=False, quiet=False, **kwargs): config Optional config parameters. By default, the id is set to the name of the container. - ''' - path = kwargs.get('path', None) + """ + path = kwargs.get("path", None) if quiet: - log.warning("'quiet' argument is being deprecated." - ' Please migrate to --quiet') - ret = {'comment': '', 'result': True} + log.warning( + "'quiet' argument is being deprecated." " Please migrate to --quiet" + ) + ret = {"comment": "", "result": True} if host is None: # TODO: Support selection of host based on available memory/cpu/etc. - ret['comment'] = 'A host must be provided' - ret['result'] = False + ret["comment"] = "A host must be provided" + ret["result"] = False return ret if isinstance(names, six.string_types): - names = names.split(',') + names = names.split(",") if not isinstance(names, list): - ret['comment'] = 'Container names are not formed as a list' - ret['result'] = False + ret["comment"] = "Container names are not formed as a list" + ret["result"] = False return ret # check that the host is alive - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) alive = False try: - if client.cmd(host, 'test.ping', timeout=20).get(host, None): + if client.cmd(host, "test.ping", timeout=20).get(host, None): alive = True except (TypeError, KeyError): pass if not alive: - ret['comment'] = 'Host {0} is not reachable'.format(host) - ret['result'] = False + ret["comment"] = "Host {0} is not reachable".format(host) + ret["result"] = False return ret - log.info('Searching for LXC Hosts') - data = __salt__['lxc.list'](host, quiet=True, path=path) + log.info("Searching for LXC Hosts") + data = __salt__["lxc.list"](host, quiet=True, path=path) for host, containers in six.iteritems(data): for name in names: if name in sum(six.itervalues(containers), []): log.info( - 'Container \'%s\' already exists on host \'%s\', init ' - 'can be a NO-OP', name, host + "Container '%s' already exists on host '%s', init " + "can be a NO-OP", + name, + host, ) if host not in data: - ret['comment'] = 'Host \'{0}\' was not found'.format(host) - ret['result'] = False + ret["comment"] = "Host '{0}' was not found".format(host) + ret["result"] = False return ret kw = salt.utils.args.clean_kwargs(**kwargs) - pub_key = kw.get('pub_key', None) - priv_key = kw.get('priv_key', None) + pub_key = kw.get("pub_key", None) + priv_key = kw.get("priv_key", None) explicit_auth = pub_key and priv_key - approve_key = kw.get('approve_key', True) + approve_key = kw.get("approve_key", True) seeds = {} - seed_arg = kwargs.get('seed', True) + seed_arg = kwargs.get("seed", True) if approve_key and not explicit_auth: skey = salt.key.Key(__opts__) - all_minions = skey.all_keys().get('minions', []) + all_minions = skey.all_keys().get("minions", []) for name in names: seed = seed_arg if name in all_minions: try: - if client.cmd(name, 'test.ping', timeout=20).get(name, None): + if client.cmd(name, "test.ping", timeout=20).get(name, None): seed = False except (TypeError, KeyError): pass seeds[name] = seed kv = salt.utils.virt.VirtKey(host, name, __opts__) if kv.authorize(): - log.info('Container key will be preauthorized') + log.info("Container key will be preauthorized") else: - ret['comment'] = 'Container key preauthorization failed' - ret['result'] = False + ret["comment"] = "Container key preauthorization failed" + ret["result"] = False return ret - log.info('Creating container(s) \'%s\' on host \'%s\'', names, host) + log.info("Creating container(s) '%s' on host '%s'", names, host) cmds = [] for name in names: @@ -312,23 +314,26 @@ def init(names, host=None, saltcloud_mode=False, quiet=False, **kwargs): kw = salt.utils.args.clean_kwargs(**kwargs) if saltcloud_mode: kw = copy.deepcopy(kw) - kw['name'] = name + kw["name"] = name saved_kwargs = kw kw = client.cmd( - host, 'lxc.cloud_init_interface', args + [kw], - tgt_type='list', timeout=600).get(host, {}) + host, + "lxc.cloud_init_interface", + args + [kw], + tgt_type="list", + timeout=600, + ).get(host, {}) kw.update(saved_kwargs) - name = kw.pop('name', name) + name = kw.pop("name", name) # be sure not to seed an already seeded host - kw['seed'] = seeds.get(name, seed_arg) - if not kw['seed']: - kw.pop('seed_cmd', '') + kw["seed"] = seeds.get(name, seed_arg) + if not kw["seed"]: + kw.pop("seed_cmd", "") cmds.append( - (host, - name, - client.cmd_iter(host, 'lxc.init', args, kwarg=kw, timeout=600))) - done = ret.setdefault('done', []) - errors = ret.setdefault('errors', _OrderedDict()) + (host, name, client.cmd_iter(host, "lxc.init", args, kwarg=kw, timeout=600)) + ) + done = ret.setdefault("done", []) + errors = ret.setdefault("errors", _OrderedDict()) for ix, acmd in enumerate(cmds): hst, container_name, cmd = acmd @@ -339,28 +344,29 @@ def init(names, host=None, saltcloud_mode=False, quiet=False, **kwargs): error = None if isinstance(sub_ret, dict) and host in sub_ret: j_ret = sub_ret[hst] - container = j_ret.get('ret', {}) + container = j_ret.get("ret", {}) if container and isinstance(container, dict): - if not container.get('result', False): + if not container.get("result", False): error = container else: - error = 'Invalid return for {0}: {1} {2}'.format( - container_name, container, sub_ret) + error = "Invalid return for {0}: {1} {2}".format( + container_name, container, sub_ret + ) else: error = sub_ret if not error: - error = 'unknown error (no return)' + error = "unknown error (no return)" if error: - ret['result'] = False + ret["result"] = False serrs.append(error) else: - container['container_name'] = name + container["container_name"] = name containers.append(container) done.append(container) # marking ping status as True only and only if we have at # least provisioned one container - ret['ping_status'] = bool(len(done)) + ret["ping_status"] = bool(len(done)) # for all provisioned containers, last job is to verify # - the key status @@ -368,45 +374,46 @@ def init(names, host=None, saltcloud_mode=False, quiet=False, **kwargs): for container in done: # explicitly check and update # the minion key/pair stored on the master - container_name = container['container_name'] - key = os.path.join(__opts__['pki_dir'], 'minions', container_name) + container_name = container["container_name"] + key = os.path.join(__opts__["pki_dir"], "minions", container_name) if explicit_auth: - fcontent = '' + fcontent = "" if os.path.exists(key): with salt.utils.files.fopen(key) as fic: fcontent = salt.utils.stringutils.to_unicode(fic.read()).strip() pub_key = salt.utils.stringutils.to_unicode(pub_key) if pub_key.strip() != fcontent: - with salt.utils.files.fopen(key, 'w') as fic: + with salt.utils.files.fopen(key, "w") as fic: fic.write(salt.utils.stringutils.to_str(pub_key)) fic.flush() - mid = j_ret.get('mid', None) + mid = j_ret.get("mid", None) if not mid: continue def testping(**kw): - mid_ = kw['mid'] - ping = client.cmd(mid_, 'test.ping', timeout=20) + mid_ = kw["mid"] + ping = client.cmd(mid_, "test.ping", timeout=20) time.sleep(1) if ping: - return 'OK' - raise Exception('Unresponsive {0}'.format(mid_)) + return "OK" + raise Exception("Unresponsive {0}".format(mid_)) + ping = salt.utils.cloud.wait_for_fun(testping, timeout=21, mid=mid) - if ping != 'OK': - ret['ping_status'] = False - ret['result'] = False + if ping != "OK": + ret["ping_status"] = False + ret["result"] = False # if no lxc detected as touched (either inited or verified) # we result to False if not done: - ret['result'] = False + ret["result"] = False if not quiet: - __jid_event__.fire_event({'message': ret}, 'progress') + __jid_event__.fire_event({"message": ret}, "progress") return ret def cloud_init(names, host=None, quiet=False, **kwargs): - ''' + """ Wrapper for using lxc.init in saltcloud compatibility mode names @@ -424,15 +431,16 @@ def cloud_init(names, host=None, quiet=False, **kwargs): saltcloud_mode init the container with the saltcloud opts format instead - ''' + """ if quiet: log.warning("'quiet' argument is being deprecated. Please migrate to --quiet") - return __salt__['lxc.init'](names=names, host=host, - saltcloud_mode=True, quiet=quiet, **kwargs) + return __salt__["lxc.init"]( + names=names, host=host, saltcloud_mode=True, quiet=quiet, **kwargs + ) def _list_iter(host=None, path=None): - ''' + """ Return a generator iterating over hosts path @@ -440,12 +448,10 @@ def _list_iter(host=None, path=None): default: /var/lib/lxc (system default) .. versionadded:: 2015.8.0 - ''' - tgt = host or '*' - client = salt.client.get_local_client(__opts__['conf_file']) - for container_info in client.cmd_iter( - tgt, 'lxc.list', kwarg={'path': path} - ): + """ + tgt = host or "*" + client = salt.client.get_local_client(__opts__["conf_file"]) + for container_info in client.cmd_iter(tgt, "lxc.list", kwarg={"path": path}): if not container_info: continue if not isinstance(container_info, dict): @@ -456,16 +462,16 @@ def _list_iter(host=None, path=None): continue if not isinstance(container_info[id_], dict): continue - if 'ret' not in container_info[id_]: + if "ret" not in container_info[id_]: continue - if not isinstance(container_info[id_]['ret'], dict): + if not isinstance(container_info[id_]["ret"], dict): continue - chunk[id_] = container_info[id_]['ret'] + chunk[id_] = container_info[id_]["ret"] yield chunk def list_(host=None, quiet=False, path=None): - ''' + """ List defined containers (running, stopped, and frozen) for the named (or all) host(s). @@ -478,19 +484,20 @@ def list_(host=None, quiet=False, path=None): .. code-block:: bash salt-run lxc.list [host=minion_id] - ''' + """ it = _list_iter(host, path=path) ret = {} for chunk in it: ret.update(chunk) if not quiet: __jid_event__.fire_event( - {'data': chunk, 'outputter': 'lxc_list'}, 'progress') + {"data": chunk, "outputter": "lxc_list"}, "progress" + ) return ret def purge(name, delete_key=True, quiet=False, path=None): - ''' + """ Purge the named container and delete its minion key if present. WARNING: Destroys all data associated with the container. @@ -503,8 +510,8 @@ def purge(name, delete_key=True, quiet=False, path=None): .. code-block:: bash salt-run lxc.purge name - ''' - data = _do_names(name, 'destroy', path=path) + """ + data = _do_names(name, "destroy", path=path) if data is False: return data @@ -516,13 +523,12 @@ def purge(name, delete_key=True, quiet=False, path=None): return if not quiet: - __jid_event__.fire_event( - {'data': data, 'outputter': 'lxc_purge'}, 'progress') + __jid_event__.fire_event({"data": data, "outputter": "lxc_purge"}, "progress") return data def start(name, quiet=False, path=None): - ''' + """ Start the named container. path @@ -534,16 +540,15 @@ def start(name, quiet=False, path=None): .. code-block:: bash salt-run lxc.start name - ''' - data = _do_names(name, 'start', path=path) + """ + data = _do_names(name, "start", path=path) if data and not quiet: - __jid_event__.fire_event( - {'data': data, 'outputter': 'lxc_start'}, 'progress') + __jid_event__.fire_event({"data": data, "outputter": "lxc_start"}, "progress") return data def stop(name, quiet=False, path=None): - ''' + """ Stop the named container. path @@ -555,16 +560,17 @@ def stop(name, quiet=False, path=None): .. code-block:: bash salt-run lxc.stop name - ''' - data = _do_names(name, 'stop', path=path) + """ + data = _do_names(name, "stop", path=path) if data and not quiet: __jid_event__.fire_event( - {'data': data, 'outputter': 'lxc_force_off'}, 'progress') + {"data": data, "outputter": "lxc_force_off"}, "progress" + ) return data def freeze(name, quiet=False, path=None): - ''' + """ Freeze the named container path @@ -576,16 +582,15 @@ def freeze(name, quiet=False, path=None): .. code-block:: bash salt-run lxc.freeze name - ''' - data = _do_names(name, 'freeze') + """ + data = _do_names(name, "freeze") if data and not quiet: - __jid_event__.fire_event( - {'data': data, 'outputter': 'lxc_pause'}, 'progress') + __jid_event__.fire_event({"data": data, "outputter": "lxc_pause"}, "progress") return data def unfreeze(name, quiet=False, path=None): - ''' + """ Unfreeze the named container path @@ -597,16 +602,15 @@ def unfreeze(name, quiet=False, path=None): .. code-block:: bash salt-run lxc.unfreeze name - ''' - data = _do_names(name, 'unfreeze', path=path) + """ + data = _do_names(name, "unfreeze", path=path) if data and not quiet: - __jid_event__.fire_event( - {'data': data, 'outputter': 'lxc_resume'}, 'progress') + __jid_event__.fire_event({"data": data, "outputter": "lxc_resume"}, "progress") return data def info(name, quiet=False, path=None): - ''' + """ Returns information about a container. path @@ -618,9 +622,8 @@ def info(name, quiet=False, path=None): .. code-block:: bash salt-run lxc.info name - ''' - data = _do_names(name, 'info', path=path) + """ + data = _do_names(name, "info", path=path) if data and not quiet: - __jid_event__.fire_event( - {'data': data, 'outputter': 'lxc_info'}, 'progress') + __jid_event__.fire_event({"data": data, "outputter": "lxc_info"}, "progress") return data diff --git a/salt/runners/manage.py b/salt/runners/manage.py index 2f7599b7777..086770209ff 100644 --- a/salt/runners/manage.py +++ b/salt/runners/manage.py @@ -1,24 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" General management functions for salt, tools like seeing what hosts are up and what hosts are down -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + +import logging import operator +import os import re import subprocess import tempfile import time -import logging import uuid -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves.urllib.request import urlopen as _urlopen # pylint: disable=no-name-in-module,import-error - # Import salt libs import salt.client import salt.client.ssh @@ -28,48 +25,58 @@ import salt.utils.files import salt.utils.minions import salt.utils.path import salt.utils.versions -import salt.wheel import salt.version +import salt.wheel from salt.exceptions import SaltClientError, SaltSystemExit -FINGERPRINT_REGEX = re.compile(r'^([a-f0-9]{2}:){15}([a-f0-9]{2})$') + +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves.urllib.request import urlopen as _urlopen + +FINGERPRINT_REGEX = re.compile(r"^([a-f0-9]{2}:){15}([a-f0-9]{2})$") log = logging.getLogger(__name__) def _ping(tgt, tgt_type, timeout, gather_job_timeout): - client = salt.client.get_local_client(__opts__['conf_file']) - pub_data = client.run_job(tgt, 'test.ping', (), tgt_type, '', timeout, '', listen=True) + client = salt.client.get_local_client(__opts__["conf_file"]) + pub_data = client.run_job( + tgt, "test.ping", (), tgt_type, "", timeout, "", listen=True + ) if not pub_data: return pub_data log.debug( - 'manage runner will ping the following minion(s): %s', - ', '.join(sorted(pub_data['minions'])) + "manage runner will ping the following minion(s): %s", + ", ".join(sorted(pub_data["minions"])), ) returned = set() for fn_ret in client.get_cli_event_returns( - pub_data['jid'], - pub_data['minions'], - client._get_timeout(timeout), - tgt, - tgt_type, - gather_job_timeout=gather_job_timeout): + pub_data["jid"], + pub_data["minions"], + client._get_timeout(timeout), + tgt, + tgt_type, + gather_job_timeout=gather_job_timeout, + ): if fn_ret: for mid, _ in six.iteritems(fn_ret): - log.debug('minion \'%s\' returned from ping', mid) + log.debug("minion '%s' returned from ping", mid) returned.add(mid) - not_returned = sorted(set(pub_data['minions']) - returned) + not_returned = sorted(set(pub_data["minions"]) - returned) returned = sorted(returned) return returned, not_returned -def status(output=True, tgt='*', tgt_type='glob', timeout=None, gather_job_timeout=None): - ''' +def status( + output=True, tgt="*", tgt_type="glob", timeout=None, gather_job_timeout=None +): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -83,21 +90,21 @@ def status(output=True, tgt='*', tgt_type='glob', timeout=None, gather_job_timeo salt-run manage.status salt-run manage.status tgt="webservers" tgt_type="nodegroup" salt-run manage.status timeout=5 gather_job_timeout=10 - ''' + """ ret = {} if not timeout: - timeout = __opts__['timeout'] + timeout = __opts__["timeout"] if not gather_job_timeout: - gather_job_timeout = __opts__['gather_job_timeout'] + gather_job_timeout = __opts__["gather_job_timeout"] res = _ping(tgt, tgt_type, timeout, gather_job_timeout) - ret['up'], ret['down'] = ([], []) if not res else res + ret["up"], ret["down"] = ([], []) if not res else res return ret def key_regen(): - ''' + """ This routine is used to regenerate all keys in an environment. This is invasive! ALL KEYS IN THE SALT ENVIRONMENT WILL BE REGENERATED!! @@ -120,36 +127,40 @@ def key_regen(): .. code-block:: bash salt-run manage.key_regen - ''' - client = salt.client.get_local_client(__opts__['conf_file']) + """ + client = salt.client.get_local_client(__opts__["conf_file"]) try: - client.cmd('*', 'saltutil.regen_keys') + client.cmd("*", "saltutil.regen_keys") except SaltClientError as client_error: print(client_error) return False - for root, _, files in salt.utils.path.os_walk(__opts__['pki_dir']): + for root, _, files in salt.utils.path.os_walk(__opts__["pki_dir"]): for fn_ in files: path = os.path.join(root, fn_) try: os.remove(path) except os.error: pass - msg = ('The minion and master keys have been deleted. Restart the Salt\n' - 'Master within the next 60 seconds!!!\n\n' - 'Wait for the minions to reconnect. Once the minions reconnect\n' - 'the new keys will appear in pending and will need to be re-\n' - 'accepted by running:\n' - ' salt-key -A\n\n' - 'Be advised that minions not currently connected to the master\n' - 'will not be able to reconnect and may require manual\n' - 'regeneration via a local call to\n' - ' salt-call saltutil.regen_keys') + msg = ( + "The minion and master keys have been deleted. Restart the Salt\n" + "Master within the next 60 seconds!!!\n\n" + "Wait for the minions to reconnect. Once the minions reconnect\n" + "the new keys will appear in pending and will need to be re-\n" + "accepted by running:\n" + " salt-key -A\n\n" + "Be advised that minions not currently connected to the master\n" + "will not be able to reconnect and may require manual\n" + "regeneration via a local call to\n" + " salt-call saltutil.regen_keys" + ) return msg -def down(removekeys=False, tgt='*', tgt_type='glob', timeout=None, gather_job_timeout=None): - ''' +def down( + removekeys=False, tgt="*", tgt_type="glob", timeout=None, gather_job_timeout=None +): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -165,22 +176,25 @@ def down(removekeys=False, tgt='*', tgt_type='glob', timeout=None, gather_job_ti salt-run manage.down removekeys=True salt-run manage.down tgt="webservers" tgt_type="nodegroup" - ''' - ret = status(output=False, - tgt=tgt, - tgt_type=tgt_type, - timeout=timeout, - gather_job_timeout=gather_job_timeout - ).get('down', []) + """ + ret = status( + output=False, + tgt=tgt, + tgt_type=tgt_type, + timeout=timeout, + gather_job_timeout=gather_job_timeout, + ).get("down", []) for minion in ret: if removekeys: wheel = salt.wheel.Wheel(__opts__) - wheel.call_func('key.delete', match=minion) + wheel.call_func("key.delete", match=minion) return ret -def up(tgt='*', tgt_type='glob', timeout=None, gather_job_timeout=None): # pylint: disable=C0103 - ''' +def up( + tgt="*", tgt_type="glob", timeout=None, gather_job_timeout=None +): # pylint: disable=C0103 + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -194,31 +208,31 @@ def up(tgt='*', tgt_type='glob', timeout=None, gather_job_timeout=None): # pyli salt-run manage.up salt-run manage.up tgt="webservers" tgt_type="nodegroup" salt-run manage.up timeout=5 gather_job_timeout=10 - ''' + """ ret = status( output=False, tgt=tgt, tgt_type=tgt_type, timeout=timeout, - gather_job_timeout=gather_job_timeout - ).get('up', []) + gather_job_timeout=gather_job_timeout, + ).get("up", []) return ret def _show_ip_migration(show_ip, show_ipv4): if show_ipv4 is not None: salt.utils.versions.warn_until( - 'Sodium', - 'The \'show_ipv4\' argument has been renamed to \'show_ip\' as' - 'it now also includes IPv6 addresses for IPv6-connected' - 'minions.' + "Sodium", + "The 'show_ipv4' argument has been renamed to 'show_ip' as" + "it now also includes IPv6 addresses for IPv6-connected" + "minions.", ) return show_ipv4 return show_ip def list_state(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -238,7 +252,7 @@ def list_state(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.list_state - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) # Always return 'present' for 0MQ for now @@ -252,7 +266,7 @@ def list_state(subset=None, show_ip=False, show_ipv4=None): def list_not_state(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -272,7 +286,7 @@ def list_not_state(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.list_not_state - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) connected = list_state(subset=None, show_ip=show_ip) @@ -288,7 +302,7 @@ def list_not_state(subset=None, show_ip=False, show_ipv4=None): def present(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now includes IPv6 addresses for IPv6-connected minions. @@ -307,13 +321,13 @@ def present(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.present - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_state(subset=subset, show_ip=show_ip) def not_present(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.5.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -333,13 +347,13 @@ def not_present(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.not_present - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_not_state(subset=subset, show_ip=show_ip) def joined(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -359,13 +373,13 @@ def joined(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.joined - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_state(subset=subset, show_ip=show_ip) def not_joined(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -385,13 +399,13 @@ def not_joined(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.not_joined - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_not_state(subset=subset, show_ip=show_ip) def allowed(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -411,13 +425,13 @@ def allowed(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.allowed - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_state(subset=subset, show_ip=show_ip) def not_allowed(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -437,13 +451,13 @@ def not_allowed(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.not_allowed - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_not_state(subset=subset, show_ip=show_ip) def alived(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -463,13 +477,13 @@ def alived(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.alived - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_state(subset=subset, show_ip=show_ip) def not_alived(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -489,13 +503,13 @@ def not_alived(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.not_alived - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_not_state(subset=subset, show_ip=show_ip) def reaped(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -515,13 +529,13 @@ def reaped(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.reaped - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_state(subset=subset, show_ip=show_ip) def not_reaped(subset=None, show_ip=False, show_ipv4=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2019.2.0 The 'show_ipv4' argument has been renamed to 'show_ip' as it now @@ -541,13 +555,13 @@ def not_reaped(subset=None, show_ip=False, show_ipv4=None): .. code-block:: bash salt-run manage.not_reaped - ''' + """ show_ip = _show_ip_migration(show_ip, show_ipv4) return list_not_state(subset=subset, show_ip=show_ip) -def safe_accept(target, tgt_type='glob'): - ''' +def safe_accept(target, tgt_type="glob"): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -560,11 +574,11 @@ def safe_accept(target, tgt_type='glob'): salt-run manage.safe_accept my_minion salt-run manage.safe_accept minion1,minion2 tgt_type=list - ''' + """ salt_key = salt.key.Key(__opts__) ssh_client = salt.client.ssh.client.SSHClient() - ret = ssh_client.cmd(target, 'key.finger', tgt_type=tgt_type) + ret = ssh_client.cmd(target, "key.finger", tgt_type=tgt_type) failures = {} for minion, finger in six.iteritems(ret): @@ -572,18 +586,18 @@ def safe_accept(target, tgt_type='glob'): failures[minion] = finger else: fingerprints = salt_key.finger(minion) - accepted = fingerprints.get('minions', {}) - pending = fingerprints.get('minions_pre', {}) + accepted = fingerprints.get("minions", {}) + pending = fingerprints.get("minions_pre", {}) if minion in accepted: del ret[minion] continue elif minion not in pending: - failures[minion] = ("Minion key {0} not found by salt-key" - .format(minion)) + failures[minion] = "Minion key {0} not found by salt-key".format(minion) elif pending[minion] != finger: - failures[minion] = ("Minion key {0} does not match the key in " - "salt-key: {1}" - .format(finger, pending[minion])) + failures[minion] = ( + "Minion key {0} does not match the key in " + "salt-key: {1}".format(finger, pending[minion]) + ) else: subprocess.call(["salt-key", "-qya", minion]) @@ -591,19 +605,21 @@ def safe_accept(target, tgt_type='glob'): del ret[minion] if failures: - print('safe_accept failed on the following minions:') + print("safe_accept failed on the following minions:") for minion, message in six.iteritems(failures): print(minion) - print('-' * len(minion)) + print("-" * len(minion)) print(message) - print('') + print("") - __jid_event__.fire_event({'message': 'Accepted {0:d} keys'.format(len(ret))}, 'progress') + __jid_event__.fire_event( + {"message": "Accepted {0:d} keys".format(len(ret))}, "progress" + ) return ret, failures def versions(): - ''' + """ Check the version of active minions CLI Example: @@ -611,21 +627,21 @@ def versions(): .. code-block:: bash salt-run manage.versions - ''' + """ ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) try: - minions = client.cmd('*', 'test.version', timeout=__opts__['timeout']) + minions = client.cmd("*", "test.version", timeout=__opts__["timeout"]) except SaltClientError as client_error: print(client_error) return ret labels = { - -2: 'Minion offline', - -1: 'Minion requires update', - 0: 'Up to date', - 1: 'Minion newer than master', - 2: 'Master', + -2: "Minion offline", + -1: "Minion requires update", + 0: "Up to date", + 1: "Minion newer than master", + 2: "Master", } version_status = {} @@ -659,17 +675,19 @@ def versions(): return ret -def bootstrap(version='develop', - script=None, - hosts='', - script_args='', - roster='flat', - ssh_user=None, - ssh_password=None, - ssh_priv_key=None, - tmp_dir='/tmp/.bootstrap', - http_backend='tornado'): - ''' +def bootstrap( + version="develop", + script=None, + hosts="", + script_args="", + roster="flat", + ssh_user=None, + ssh_password=None, + ssh_priv_key=None, + tmp_dir="/tmp/.bootstrap", + http_backend="tornado", +): + """ Bootstrap minions with salt-bootstrap version : develop @@ -740,53 +758,60 @@ def bootstrap(version='develop', salt-run manage.bootstrap hosts='host1,host2' version='v0.17' \ script='https://bootstrap.saltstack.com/develop' - ''' + """ if script is None: - script = 'https://bootstrap.saltstack.com' + script = "https://bootstrap.saltstack.com" client_opts = __opts__.copy() if roster is not None: - client_opts['roster'] = roster + client_opts["roster"] = roster if ssh_user is not None: - client_opts['ssh_user'] = ssh_user + client_opts["ssh_user"] = ssh_user if ssh_password is not None: - client_opts['ssh_passwd'] = ssh_password + client_opts["ssh_passwd"] = ssh_password if ssh_priv_key is not None: - client_opts['ssh_priv'] = ssh_priv_key + client_opts["ssh_priv"] = ssh_priv_key - for host in hosts.split(','): - client_opts['tgt'] = host - client_opts['selected_target_option'] = 'glob' - tmp_dir = '{0}-{1}/'.format(tmp_dir.rstrip('/'), uuid.uuid4()) - deploy_command = os.path.join(tmp_dir, 'deploy.sh') + for host in hosts.split(","): + client_opts["tgt"] = host + client_opts["selected_target_option"] = "glob" + tmp_dir = "{0}-{1}/".format(tmp_dir.rstrip("/"), uuid.uuid4()) + deploy_command = os.path.join(tmp_dir, "deploy.sh") try: - client_opts['argv'] = ['file.makedirs', tmp_dir, 'mode=0700'] + client_opts["argv"] = ["file.makedirs", tmp_dir, "mode=0700"] salt.client.ssh.SSH(client_opts).run() - client_opts['argv'] = [ - 'http.query', + client_opts["argv"] = [ + "http.query", script, - 'backend={0}'.format(http_backend), - 'text_out={0}'.format(deploy_command) + "backend={0}".format(http_backend), + "text_out={0}".format(deploy_command), ] salt.client.ssh.SSH(client_opts).run() - client_opts['argv'] = [ - 'cmd.run', - ' '.join(['sh', deploy_command, script_args]), - 'python_shell=False' + client_opts["argv"] = [ + "cmd.run", + " ".join(["sh", deploy_command, script_args]), + "python_shell=False", ] salt.client.ssh.SSH(client_opts).run() - client_opts['argv'] = ['file.remove', tmp_dir] + client_opts["argv"] = ["file.remove", tmp_dir] salt.client.ssh.SSH(client_opts).run() except SaltSystemExit as exc: log.error(six.text_type(exc)) -def bootstrap_psexec(hosts='', master=None, version=None, arch='win32', - installer_url=None, username=None, password=None): - ''' +def bootstrap_psexec( + hosts="", + master=None, + version=None, + arch="win32", + installer_url=None, + username=None, + password=None, +): + """ Bootstrap Windows minions via PsExec. hosts @@ -819,15 +844,22 @@ def bootstrap_psexec(hosts='', master=None, version=None, arch='win32', salt-run manage.bootstrap_psexec hosts='host1,host2' salt-run manage.bootstrap_psexec hosts='host1,host2' version='0.17' username='DOMAIN\\Administrator' salt-run manage.bootstrap_psexec hosts='host1,host2' installer_url='http://exampledomain/salt-installer.exe' - ''' + """ if not installer_url: - base_url = 'https://repo.saltstack.com/windows/' + base_url = "https://repo.saltstack.com/windows/" source = _urlopen(base_url).read() - salty_rx = re.compile('>(Salt-Minion-(.+?)-(.+)-Setup.exe)</a></td><td align="right">(.*?)\\s*<') - source_list = sorted([[path, ver, plat, time.strptime(date, "%d-%b-%Y %H:%M")] - for path, ver, plat, date in salty_rx.findall(source)], - key=operator.itemgetter(3), reverse=True) + salty_rx = re.compile( + '>(Salt-Minion-(.+?)-(.+)-Setup.exe)</a></td><td align="right">(.*?)\\s*<' + ) + source_list = sorted( + [ + [path, ver, plat, time.strptime(date, "%d-%b-%Y %H:%M")] + for path, ver, plat, date in salty_rx.findall(source) + ], + key=operator.itemgetter(3), + reverse=True, + ) if version: source_list = [s for s in source_list if s[1] == version] if arch: @@ -848,7 +880,7 @@ def bootstrap_psexec(hosts='', master=None, version=None, arch='win32', # The following script was borrowed from an informative article about # downloading exploit payloads for malware. Nope, no irony here. # http://www.greyhathacker.net/?p=500 - vb_script = '''strFileURL = "{0}" + vb_script = """strFileURL = "{0}" strHDLocation = "{1}" Set objXMLHTTP = CreateObject("MSXML2.XMLHTTP") objXMLHTTP.open "GET", strFileURL, false @@ -865,27 +897,29 @@ Set objADOStream = Nothing End if Set objXMLHTTP = Nothing Set objShell = CreateObject("WScript.Shell") -objShell.Exec("{1}{2}")''' +objShell.Exec("{1}{2}")""" - vb_saltexec = 'saltinstall.exe' - vb_saltexec_args = ' /S /minion-name=%COMPUTERNAME%' + vb_saltexec = "saltinstall.exe" + vb_saltexec_args = " /S /minion-name=%COMPUTERNAME%" if master: - vb_saltexec_args += ' /master={0}'.format(master) + vb_saltexec_args += " /master={0}".format(master) # One further thing we need to do; the Windows Salt minion is pretty # self-contained, except for the Microsoft Visual C++ 2008 runtime. # It's tiny, so the bootstrap will attempt a silent install. - vb_vcrunexec = 'vcredist.exe' - if arch == 'AMD64': + vb_vcrunexec = "vcredist.exe" + if arch == "AMD64": vb_vcrun = vb_script.format( - 'http://download.microsoft.com/download/d/2/4/d242c3fb-da5a-4542-ad66-f9661d0a8d19/vcredist_x64.exe', - vb_vcrunexec, - ' /q') + "http://download.microsoft.com/download/d/2/4/d242c3fb-da5a-4542-ad66-f9661d0a8d19/vcredist_x64.exe", + vb_vcrunexec, + " /q", + ) else: vb_vcrun = vb_script.format( - 'http://download.microsoft.com/download/d/d/9/dd9a82d0-52ef-40db-8dab-795376989c03/vcredist_x86.exe', - vb_vcrunexec, - ' /q') + "http://download.microsoft.com/download/d/d/9/dd9a82d0-52ef-40db-8dab-795376989c03/vcredist_x86.exe", + vb_vcrunexec, + " /q", + ) vb_salt = vb_script.format(installer_url, vb_saltexec, vb_saltexec_args) @@ -897,26 +931,37 @@ objShell.Exec("{1}{2}")''' # This is to accommodate for reinstalling Salt over an old or broken build, # e.g. if the master address is changed, the salt-minion process will fail # to authenticate and quit; which means infinite restarts under Windows. - batch = 'cd /d %TEMP%\nnet stop salt-minion\ndel c:\\salt\\conf\\pki\\minion\\minion_master.pub\n' + batch = "cd /d %TEMP%\nnet stop salt-minion\ndel c:\\salt\\conf\\pki\\minion\\minion_master.pub\n" # Speaking of command-line hostile, cscript only supports reading a script # from a file. Glue it together line by line. for x, y in ((vb_vcrunexec, vb_vcrun), (vb_saltexec, vb_salt)): - vb_lines = y.split('\n') - batch += '\ndel ' + x + '\n@echo ' + vb_lines[0] + ' >' + \ - x + '.vbs\n@echo ' + \ - (' >>' + x + '.vbs\n@echo ').join(vb_lines[1:]) + \ - ' >>' + x + '.vbs\ncscript.exe /NoLogo ' + x + '.vbs' + vb_lines = y.split("\n") + batch += ( + "\ndel " + + x + + "\n@echo " + + vb_lines[0] + + " >" + + x + + ".vbs\n@echo " + + (" >>" + x + ".vbs\n@echo ").join(vb_lines[1:]) + + " >>" + + x + + ".vbs\ncscript.exe /NoLogo " + + x + + ".vbs" + ) - batch_path = tempfile.mkstemp(suffix='.bat')[1] - with salt.utils.files.fopen(batch_path, 'wb') as batch_file: + batch_path = tempfile.mkstemp(suffix=".bat")[1] + with salt.utils.files.fopen(batch_path, "wb") as batch_file: batch_file.write(batch) for host in hosts.split(","): - argv = ['psexec', '\\\\' + host] + argv = ["psexec", "\\\\" + host] if username: - argv += ['-u', username] + argv += ["-u", username] if password: - argv += ['-p', password] - argv += ['-h', '-c', batch_path] + argv += ["-p", password] + argv += ["-h", "-c", batch_path] subprocess.call(argv) diff --git a/salt/runners/mattermost.py b/salt/runners/mattermost.py index 9fb92e72d10..5fcd1762979 100644 --- a/salt/runners/mattermost.py +++ b/salt/runners/mattermost.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Module for sending messages to Mattermost .. versionadded:: 2017.7.0 @@ -13,91 +13,93 @@ Module for sending messages to Mattermost mattermost: hook: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 api_url: https://example.com -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.json -# Import 3rd-party libs -from salt.ext import six - # Import Salt libs # pylint: disable=import-error,no-name-in-module,redefined-builtin import salt.utils.mattermost + # pylint: enable=import-error,no-name-in-module from salt.exceptions import SaltInvocationError +# Import 3rd-party libs +from salt.ext import six + log = logging.getLogger(__name__) -__virtualname__ = 'mattermost' +__virtualname__ = "mattermost" def __virtual__(): - ''' + """ Return virtual name of the module. :return: The virtual name of the module. - ''' + """ return __virtualname__ def _get_hook(): - ''' + """ Retrieves and return the Mattermost's configured hook :return: String: the hook string - ''' - hook = __salt__['config.get']('mattermost.hook') or \ - __salt__['config.get']('mattermost:hook') + """ + hook = __salt__["config.get"]("mattermost.hook") or __salt__["config.get"]( + "mattermost:hook" + ) if not hook: - raise SaltInvocationError('No Mattermost Hook found') + raise SaltInvocationError("No Mattermost Hook found") return hook def _get_api_url(): - ''' + """ Retrieves and return the Mattermost's configured api url :return: String: the api url string - ''' - api_url = __salt__['config.get']('mattermost.api_url') or \ - __salt__['config.get']('mattermost:api_url') + """ + api_url = __salt__["config.get"]("mattermost.api_url") or __salt__["config.get"]( + "mattermost:api_url" + ) if not api_url: - raise SaltInvocationError('No Mattermost API URL found') + raise SaltInvocationError("No Mattermost API URL found") return api_url def _get_channel(): - ''' + """ Retrieves the Mattermost's configured channel :return: String: the channel string - ''' - channel = __salt__['config.get']('mattermost.channel') or \ - __salt__['config.get']('mattermost:channel') + """ + channel = __salt__["config.get"]("mattermost.channel") or __salt__["config.get"]( + "mattermost:channel" + ) return channel def _get_username(): - ''' + """ Retrieves the Mattermost's configured username :return: String: the username string - ''' - username = __salt__['config.get']('mattermost.username') or \ - __salt__['config.get']('mattermost:username') + """ + username = __salt__["config.get"]("mattermost.username") or __salt__["config.get"]( + "mattermost:username" + ) return username -def post_message(message, - channel=None, - username=None, - api_url=None, - hook=None): - ''' +def post_message(message, channel=None, username=None, api_url=None, hook=None): + """ Send a message to a Mattermost channel. :param channel: The channel name, either will work. :param username: The username of the poster. @@ -111,7 +113,7 @@ def post_message(message, .. code-block:: bash salt-run mattermost.post_message message='Build is done' - ''' + """ if not api_url: api_url = _get_api_url() @@ -125,20 +127,19 @@ def post_message(message, channel = _get_channel() if not message: - log.error('message is a required option.') + log.error("message is a required option.") parameters = dict() if channel: - parameters['channel'] = channel + parameters["channel"] = channel if username: - parameters['username'] = username - parameters['text'] = '```' + message + '```' # pre-formatted, fixed-width text - log.debug('Parameters: %s', parameters) + parameters["username"] = username + parameters["text"] = "```" + message + "```" # pre-formatted, fixed-width text + log.debug("Parameters: %s", parameters) data = salt.utils.json.dumps(parameters) result = salt.utils.mattermost.query( - api_url=api_url, - hook=hook, - data=str('payload={0}').format(data)) # future lint: blacklisted-function + api_url=api_url, hook=hook, data=str("payload={0}").format(data) + ) # future lint: blacklisted-function if result: return True @@ -146,12 +147,8 @@ def post_message(message, return result -def post_event(event, - channel=None, - username=None, - api_url=None, - hook=None): - ''' +def post_event(event, channel=None, username=None, api_url=None, hook=None): + """ Send an event to a Mattermost channel. :param channel: The channel name, either will work. :param username: The username of the poster. @@ -159,7 +156,7 @@ def post_event(event, :param api_url: The Mattermost api url, if not specified in the configuration. :param hook: The Mattermost hook, if not specified in the configuration. :return: Boolean if message was sent successfully. - ''' + """ if not api_url: api_url = _get_api_url() @@ -173,16 +170,14 @@ def post_event(event, channel = _get_channel() if not event: - log.error('message is a required option.') + log.error("message is a required option.") - log.debug('Event: %s', event) - log.debug('Event data: %s', event['data']) - message = 'tag: {0}\r\n'.format(event['tag']) - for key, value in six.iteritems(event['data']): - message += '{0}: {1}\r\n'.format(key, value) - result = post_message(message, - channel=channel, - username=username, - api_url=api_url, - hook=hook) + log.debug("Event: %s", event) + log.debug("Event data: %s", event["data"]) + message = "tag: {0}\r\n".format(event["tag"]) + for key, value in six.iteritems(event["data"]): + message += "{0}: {1}\r\n".format(key, value) + result = post_message( + message, channel=channel, username=username, api_url=api_url, hook=hook + ) return bool(result) diff --git a/salt/runners/mine.py b/salt/runners/mine.py index e613616da75..8dbc41e13ac 100644 --- a/salt/runners/mine.py +++ b/salt/runners/mine.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" A runner to access data from the salt mine -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python Libs @@ -13,8 +13,8 @@ import salt.daemons.masterapi log = logging.getLevelName(__name__) -def get(tgt, fun, tgt_type='glob'): - ''' +def get(tgt, fun, tgt_type="glob"): + """ Gathers the data from the specified minions' mine, pass in the target, function to look up and the target type @@ -23,23 +23,20 @@ def get(tgt, fun, tgt_type='glob'): .. code-block:: bash salt-run mine.get '*' network.interfaces - ''' + """ masterapi = salt.daemons.masterapi.RemoteFuncs(__opts__) load = { - 'id': __opts__['id'], - 'fun': fun, - 'tgt': tgt, - 'tgt_type': tgt_type, + "id": __opts__["id"], + "fun": fun, + "tgt": tgt, + "tgt_type": tgt_type, } ret = masterapi._mine_get(load) return ret -def update(tgt, - tgt_type='glob', - clear=False, - mine_functions=None): - ''' +def update(tgt, tgt_type="glob", clear=False, mine_functions=None): + """ .. versionadded:: 2017.7.0 Update the mine data on a certain group of minions. @@ -67,10 +64,12 @@ def update(tgt, salt-run mine.update '*' salt-run mine.update 'juniper-edges' tgt_type='nodegroup' - ''' - ret = __salt__['salt.execute'](tgt, - 'mine.update', - tgt_type=tgt_type, - clear=clear, - mine_functions=mine_functions) + """ + ret = __salt__["salt.execute"]( + tgt, + "mine.update", + tgt_type=tgt_type, + clear=clear, + mine_functions=mine_functions, + ) return ret diff --git a/salt/runners/nacl.py b/salt/runners/nacl.py index 5af875c995b..ebd2151878d 100644 --- a/salt/runners/nacl.py +++ b/salt/runners/nacl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This module helps include encrypted passwords in pillars, grains and salt state files. :depends: libnacl, https://github.com/saltstack/libnacl @@ -110,7 +110,7 @@ Larger files like certificates can be encrypted with: salt-run nacl.enc_file /tmp/cert.crt out=/tmp/cert.nacl -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -118,7 +118,7 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.nacl -__virtualname__ = 'nacl' +__virtualname__ = "nacl" def __virtual__(): @@ -126,7 +126,7 @@ def __virtual__(): def keygen(sk_file=None, pk_file=None, **kwargs): - ''' + """ Use libnacl to generate a keypair. If no `sk_file` is defined return a keypair. @@ -144,23 +144,23 @@ def keygen(sk_file=None, pk_file=None, **kwargs): salt-run nacl.keygen sk_file=/etc/salt/pki/master/nacl salt-run nacl.keygen sk_file=/etc/salt/pki/master/nacl pk_file=/etc/salt/pki/master/nacl.pub salt-run nacl.keygen - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.keygen(sk_file, pk_file, **kwargs) def enc(data, **kwargs): - ''' + """ Alias to `{box_type}_encrypt` box_type: secretbox, sealedbox(default) - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.enc(data, **kwargs) def enc_file(name, out=None, **kwargs): - ''' + """ This is a helper function to encrypt a file and return its contents. You can provide an optional output file using `out` @@ -174,23 +174,23 @@ def enc_file(name, out=None, **kwargs): salt-run nacl.enc_file name=/tmp/id_rsa salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \ sk_file=/etc/salt/pki/master/nacl.pub - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.enc_file(name, out, **kwargs) def dec(data, **kwargs): - ''' + """ Alias to `{box_type}_decrypt` box_type: secretbox, sealedbox(default) - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.dec(data, **kwargs) def dec_file(name, out=None, **kwargs): - ''' + """ This is a helper function to decrypt a file and return its contents. You can provide an optional output file using `out` @@ -204,13 +204,13 @@ def dec_file(name, out=None, **kwargs): salt-run nacl.dec_file name=/tmp/id_rsa.nacl salt-run nacl.dec_file name=/tmp/id_rsa.nacl box_type=secretbox \ sk_file=/etc/salt/pki/master/nacl.pub - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.dec_file(name, out, **kwargs) def sealedbox_encrypt(data, **kwargs): - ''' + """ Encrypt data using a public key generated from `nacl.keygen`. The encryptd data can be decrypted using `nacl.sealedbox_decrypt` only with the secret key. @@ -219,13 +219,13 @@ def sealedbox_encrypt(data, **kwargs): .. code-block:: bash salt-run nacl.sealedbox_encrypt datatoenc - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.sealedbox_encrypt(data, **kwargs) def sealedbox_decrypt(data, **kwargs): - ''' + """ Decrypt data using a secret key that was encrypted using a public key with `nacl.sealedbox_encrypt`. CLI Examples: @@ -235,13 +235,13 @@ def sealedbox_decrypt(data, **kwargs): salt-run nacl.sealedbox_decrypt pEXHQM6cuaF7A= salt-run nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl salt-run nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo=' - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.sealedbox_decrypt(data, **kwargs) def secretbox_encrypt(data, **kwargs): - ''' + """ Encrypt data using a secret key generated from `nacl.keygen`. The same secret key can be used to decrypt the data using `nacl.secretbox_decrypt`. @@ -252,13 +252,13 @@ def secretbox_encrypt(data, **kwargs): salt-run nacl.secretbox_encrypt datatoenc salt-run nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl salt-run nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo=' - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.secretbox_encrypt(data, **kwargs) def secretbox_decrypt(data, **kwargs): - ''' + """ Decrypt data that was encrypted using `nacl.secretbox_encrypt` using the secret key that was generated from `nacl.keygen`. @@ -269,6 +269,6 @@ def secretbox_decrypt(data, **kwargs): salt-run nacl.secretbox_decrypt pEXHQM6cuaF7A= salt-run nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl salt-run nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo=' - ''' - kwargs['opts'] = __opts__ + """ + kwargs["opts"] = __opts__ return salt.utils.nacl.secretbox_decrypt(data, **kwargs) diff --git a/salt/runners/net.py b/salt/runners/net.py index 342d55e2e8c..d900d5f2b9d 100644 --- a/salt/runners/net.py +++ b/salt/runners/net.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NET Finder ========== @@ -67,7 +67,7 @@ Configuration - jsrv - fxp0 outputter: yaml -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt lib @@ -81,6 +81,7 @@ try: from netaddr import IPNetwork # netaddr is already required by napalm-base from netaddr.core import AddrFormatError from napalm_base import helpers as napalm_helpers + HAS_NAPALM_BASE = True except ImportError: HAS_NAPALM_BASE = False @@ -89,12 +90,12 @@ except ImportError: # module properties # ----------------------------------------------------------------------------- -_DEFAULT_TARGET = '*' -_DEFAULT_EXPR_FORM = 'glob' +_DEFAULT_TARGET = "*" +_DEFAULT_EXPR_FORM = "glob" _DEFAULT_IGNORE_INTF = [] # 'lo0', 'em1', 'em0', 'jsrv', 'fxp0' _DEFAULT_DISPLAY = True -_DEFAULT_OUTPUTTER = 'table' +_DEFAULT_OUTPUTTER = "table" # ----------------------------------------------------------------------------- @@ -109,69 +110,71 @@ _CACHE = {} # ----------------------------------------------------------------------------- # Define the module's virtual name -__virtualname__ = 'net' +__virtualname__ = "net" def __virtual__(): if HAS_NAPALM_BASE: return __virtualname__ - return (False, 'The napalm-base module could not be imported') + return (False, "The napalm-base module could not be imported") def _get_net_runner_opts(): - ''' + """ Return the net.find runner options. - ''' - runner_opts = __opts__.get('runners', {}).get('net.find', {}) + """ + runner_opts = __opts__.get("runners", {}).get("net.find", {}) return { - 'target': runner_opts.get('target', _DEFAULT_TARGET), - 'expr_form': runner_opts.get('expr_form', _DEFAULT_EXPR_FORM), - 'ignore_interfaces': runner_opts.get('ignore_interfaces', _DEFAULT_IGNORE_INTF), - 'display': runner_opts.get('display', _DEFAULT_DISPLAY), - 'outputter': runner_opts.get('outputter', _DEFAULT_OUTPUTTER), + "target": runner_opts.get("target", _DEFAULT_TARGET), + "expr_form": runner_opts.get("expr_form", _DEFAULT_EXPR_FORM), + "ignore_interfaces": runner_opts.get("ignore_interfaces", _DEFAULT_IGNORE_INTF), + "display": runner_opts.get("display", _DEFAULT_DISPLAY), + "outputter": runner_opts.get("outputter", _DEFAULT_OUTPUTTER), } def _get_mine(fun): - ''' + """ Return the mine function from all the targeted minions. Just a small helper to avoid redundant pieces of code. - ''' + """ if fun in _CACHE and _CACHE[fun]: return _CACHE[fun] net_runner_opts = _get_net_runner_opts() - _CACHE[fun] = __salt__['mine.get'](net_runner_opts.get('target'), - fun, - tgt_type=net_runner_opts.get('expr_form')) + _CACHE[fun] = __salt__["mine.get"]( + net_runner_opts.get("target"), fun, tgt_type=net_runner_opts.get("expr_form") + ) return _CACHE[fun] def _display_runner(rows, labels, title, display=_DEFAULT_DISPLAY): - ''' + """ Display or return the rows. - ''' + """ if display: net_runner_opts = _get_net_runner_opts() - if net_runner_opts.get('outputter') == 'table': - ret = salt.output.out_format({'rows': rows, 'labels': labels}, - 'table', - __opts__, - title=title, - rows_key='rows', - labels_key='labels') + if net_runner_opts.get("outputter") == "table": + ret = salt.output.out_format( + {"rows": rows, "labels": labels}, + "table", + __opts__, + title=title, + rows_key="rows", + labels_key="labels", + ) else: - ret = salt.output.out_format(rows, - net_runner_opts.get('outputter'), - __opts__) + ret = salt.output.out_format( + rows, net_runner_opts.get("outputter"), __opts__ + ) print(ret) else: return rows def _get_network_obj(addr): - ''' + """ Try to convert a string into a valid IP Network object. - ''' + """ ip_netw = None try: ip_netw = IPNetwork(addr) @@ -181,58 +184,72 @@ def _get_network_obj(addr): def _find_interfaces_ip(mac): - ''' + """ Helper to search the interfaces IPs using the MAC address. - ''' + """ try: mac = napalm_helpers.convert(napalm_helpers.mac, mac) except AddrFormatError: - return ('', '', []) + return ("", "", []) - all_interfaces = _get_mine('net.interfaces') - all_ipaddrs = _get_mine('net.ipaddrs') + all_interfaces = _get_mine("net.interfaces") + all_ipaddrs = _get_mine("net.ipaddrs") for device, device_interfaces in six.iteritems(all_interfaces): - if not device_interfaces.get('result', False): + if not device_interfaces.get("result", False): continue - for interface, interface_details in six.iteritems(device_interfaces.get('out', {})): + for interface, interface_details in six.iteritems( + device_interfaces.get("out", {}) + ): try: - interface_mac = napalm_helpers.convert(napalm_helpers.mac, interface_details.get('mac_address')) + interface_mac = napalm_helpers.convert( + napalm_helpers.mac, interface_details.get("mac_address") + ) except AddrFormatError: continue if mac != interface_mac: continue - interface_ipaddrs = all_ipaddrs.get(device, {}).get('out', {}).get(interface, {}) - ip_addresses = interface_ipaddrs.get('ipv4', {}) - ip_addresses.update(interface_ipaddrs.get('ipv6', {})) - interface_ips = ['{0}/{1}'.format(ip_addr, - addr_details.get('prefix_length', '32')) - for ip_addr, addr_details in six.iteritems(ip_addresses)] + interface_ipaddrs = ( + all_ipaddrs.get(device, {}).get("out", {}).get(interface, {}) + ) + ip_addresses = interface_ipaddrs.get("ipv4", {}) + ip_addresses.update(interface_ipaddrs.get("ipv6", {})) + interface_ips = [ + "{0}/{1}".format(ip_addr, addr_details.get("prefix_length", "32")) + for ip_addr, addr_details in six.iteritems(ip_addresses) + ] return device, interface, interface_ips - return ('', '', []) + return ("", "", []) def _find_interfaces_mac(ip): # pylint: disable=invalid-name - ''' + """ Helper to get the interfaces hardware address using the IP Address. - ''' - all_interfaces = _get_mine('net.interfaces') - all_ipaddrs = _get_mine('net.ipaddrs') + """ + all_interfaces = _get_mine("net.interfaces") + all_ipaddrs = _get_mine("net.ipaddrs") for device, device_ipaddrs in six.iteritems(all_ipaddrs): - if not device_ipaddrs.get('result', False): + if not device_ipaddrs.get("result", False): continue - for interface, interface_ipaddrs in six.iteritems(device_ipaddrs.get('out', {})): - ip_addresses = interface_ipaddrs.get('ipv4', {}).keys() - ip_addresses.extend(interface_ipaddrs.get('ipv6', {}).keys()) + for interface, interface_ipaddrs in six.iteritems( + device_ipaddrs.get("out", {}) + ): + ip_addresses = interface_ipaddrs.get("ipv4", {}).keys() + ip_addresses.extend(interface_ipaddrs.get("ipv6", {}).keys()) for ipaddr in ip_addresses: if ip != ipaddr: continue - interface_mac = all_interfaces.get(device, {}).get('out', {}).get(interface, {}).get('mac_address', '') + interface_mac = ( + all_interfaces.get(device, {}) + .get("out", {}) + .get(interface, {}) + .get("mac_address", "") + ) return device, interface, interface_mac - return ('', '', '') + return ("", "", "") # ----------------------------------------------------------------------------- @@ -240,14 +257,16 @@ def _find_interfaces_mac(ip): # pylint: disable=invalid-name # ----------------------------------------------------------------------------- -def interfaces(device=None, - interface=None, - title=None, - pattern=None, - ipnet=None, - best=True, - display=_DEFAULT_DISPLAY): - ''' +def interfaces( + device=None, + interface=None, + title=None, + pattern=None, + ipnet=None, + best=True, + display=_DEFAULT_DISPLAY, +): + """ Search for interfaces details in the following mine functions: - net.interfaces @@ -299,13 +318,13 @@ def interfaces(device=None, _________________________________________________________________________________________________________________ | edge01.oua01 | vt-0/0/10 | | True | True | 1000 | | | _________________________________________________________________________________________________________________ - ''' + """ def _ipnet_belongs(net): - ''' + """ Helper to tell if a IP address or network belong to a certain network. - ''' - if net == '0.0.0.0/0': + """ + if net == "0.0.0.0/0": return False net_obj = _get_network_obj(net) if not net_obj: @@ -313,36 +332,38 @@ def interfaces(device=None, return ipnet in net_obj or net_obj in ipnet labels = { - 'device': 'Device', - 'interface': 'Interface', - 'interface_description': 'Interface Description', - 'is_up': 'UP', - 'is_enabled': 'Enabled', - 'speed': 'Speed [Mbps]', - 'mac': 'MAC Address', - 'ips': 'IP Addresses' + "device": "Device", + "interface": "Interface", + "interface_description": "Interface Description", + "is_up": "UP", + "is_enabled": "Enabled", + "speed": "Speed [Mbps]", + "mac": "MAC Address", + "ips": "IP Addresses", } rows = [] net_runner_opts = _get_net_runner_opts() if pattern: - title = 'Pattern "{0}" found in the description of the following interfaces'.format(pattern) + title = 'Pattern "{0}" found in the description of the following interfaces'.format( + pattern + ) if not title: - title = 'Details' + title = "Details" if interface: - title += ' for interface {0}'.format(interface) + title += " for interface {0}".format(interface) else: - title += ' for all interfaces' + title += " for all interfaces" if device: - title += ' on device {0}'.format(device) + title += " on device {0}".format(device) if ipnet: - title += ' that include network {net}'.format(net=six.text_type(ipnet)) + title += " that include network {net}".format(net=six.text_type(ipnet)) if best: - title += ' - only best match returned' + title += " - only best match returned" - all_interfaces = _get_mine('net.interfaces') - all_ipaddrs = _get_mine('net.ipaddrs') + all_interfaces = _get_mine("net.interfaces") + all_ipaddrs = _get_mine("net.ipaddrs") if device: all_interfaces = {device: all_interfaces.get(device, {})} @@ -352,52 +373,64 @@ def interfaces(device=None, best_row = {} best_net_match = None - for device, net_interfaces_out in six.iteritems(all_interfaces): # pylint: disable=too-many-nested-blocks + for device, net_interfaces_out in six.iteritems( + all_interfaces + ): # pylint: disable=too-many-nested-blocks if not net_interfaces_out: continue - if not net_interfaces_out.get('result', False): + if not net_interfaces_out.get("result", False): continue - selected_device_interfaces = net_interfaces_out.get('out', {}) + selected_device_interfaces = net_interfaces_out.get("out", {}) if interface: - selected_device_interfaces = {interface: selected_device_interfaces.get(interface, {})} - for interface_name, interface_details in six.iteritems(selected_device_interfaces): + selected_device_interfaces = { + interface: selected_device_interfaces.get(interface, {}) + } + for interface_name, interface_details in six.iteritems( + selected_device_interfaces + ): if not interface_details: continue - if ipnet and interface_name in net_runner_opts.get('ignore_interfaces'): + if ipnet and interface_name in net_runner_opts.get("ignore_interfaces"): continue - interface_description = (interface_details.get('description', '') or '') + interface_description = interface_details.get("description", "") or "" if pattern: if pattern.lower() not in interface_description.lower(): continue - if not all_ipaddrs.get(device, {}).get('result', False): + if not all_ipaddrs.get(device, {}).get("result", False): continue ips = [] device_entry = { - 'device': device, - 'interface': interface_name, - 'interface_description': interface_description, - 'is_up': (interface_details.get('is_up', '') or ''), - 'is_enabled': (interface_details.get('is_enabled', '') or ''), - 'speed': (interface_details.get('speed', '') or ''), - 'mac': napalm_helpers.convert(napalm_helpers.mac, (interface_details.get('mac_address', '') or '')), - 'ips': [] + "device": device, + "interface": interface_name, + "interface_description": interface_description, + "is_up": (interface_details.get("is_up", "") or ""), + "is_enabled": (interface_details.get("is_enabled", "") or ""), + "speed": (interface_details.get("speed", "") or ""), + "mac": napalm_helpers.convert( + napalm_helpers.mac, (interface_details.get("mac_address", "") or "") + ), + "ips": [], } intf_entry_found = False - for intrf, interface_ips in six.iteritems(all_ipaddrs.get(device, {}).get('out', {})): - if intrf.split('.')[0] == interface_name: - ip_addresses = interface_ips.get('ipv4', {}) # all IPv4 addresses - ip_addresses.update(interface_ips.get('ipv6', {})) # and all IPv6 addresses + for intrf, interface_ips in six.iteritems( + all_ipaddrs.get(device, {}).get("out", {}) + ): + if intrf.split(".")[0] == interface_name: + ip_addresses = interface_ips.get("ipv4", {}) # all IPv4 addresses + ip_addresses.update( + interface_ips.get("ipv6", {}) + ) # and all IPv6 addresses ips = [ - '{0}/{1}'.format( - ip_addr, - addr_details.get('prefix_length', '32') - ) for ip_addr, addr_details in six.iteritems(ip_addresses) + "{0}/{1}".format( + ip_addr, addr_details.get("prefix_length", "32") + ) + for ip_addr, addr_details in six.iteritems(ip_addresses) ] interf_entry = {} interf_entry.update(device_entry) - interf_entry['ips'] = ips + interf_entry["ips"] = ips if display: - interf_entry['ips'] = '\n'.join(interf_entry['ips']) + interf_entry["ips"] = "\n".join(interf_entry["ips"]) if ipnet: inet_ips = [ six.text_type(ip) for ip in ips if _ipnet_belongs(ip) @@ -422,7 +455,7 @@ def interfaces(device=None, interf_entry = {} interf_entry.update(device_entry) if display: - interf_entry['ips'] = '' + interf_entry["ips"] = "" rows.append(interf_entry) if ipnet and best and best_row: @@ -431,12 +464,10 @@ def interfaces(device=None, return _display_runner(rows, labels, title, display=display) -def findarp(device=None, - interface=None, - mac=None, - ip=None, - display=_DEFAULT_DISPLAY): # pylint: disable=invalid-name - ''' +def findarp( + device=None, interface=None, mac=None, ip=None, display=_DEFAULT_DISPLAY +): # pylint: disable=invalid-name + """ Search for entries in the ARP tables using the following mine functions: - net.arp @@ -474,27 +505,27 @@ def findarp(device=None, ________________________________________________________________________________ | edge01.bjm01 | irb.171 [ae0.171] | 8C:60:0F:78:EC:41 | 172.172.17.19 | 956.0 | ________________________________________________________________________________ - ''' + """ labels = { - 'device': 'Device', - 'interface': 'Interface', - 'mac': 'MAC', - 'ip': 'IP', - 'age': 'Age' + "device": "Device", + "interface": "Interface", + "mac": "MAC", + "ip": "IP", + "age": "Age", } rows = [] - all_arp = _get_mine('net.arp') + all_arp = _get_mine("net.arp") title = "ARP Entries" if device: - title += ' on device {device}'.format(device=device) + title += " on device {device}".format(device=device) if interface: - title += ' on interface {interf}'.format(interf=interface) + title += " on interface {interf}".format(interf=interface) if ip: - title += ' for IP {ip}'.format(ip=ip) + title += " for IP {ip}".format(ip=ip) if mac: - title += ' for MAC {mac}'.format(mac=mac) + title += " for MAC {mac}".format(mac=mac) if device: all_arp = {device: all_arp.get(device)} @@ -502,27 +533,42 @@ def findarp(device=None, for device, device_arp in six.iteritems(all_arp): if not device_arp: continue - if not device_arp.get('result', False): + if not device_arp.get("result", False): continue - arp_table = device_arp.get('out', []) + arp_table = device_arp.get("out", []) for arp_entry in arp_table: - if ((mac and arp_entry.get('mac', '').lower() == mac.lower()) or # pylint: disable=too-many-boolean-expressions - (interface and interface in arp_entry.get('interface', '')) or - (ip and napalm_helpers.convert(napalm_helpers.ip, arp_entry.get('ip', '')) == - napalm_helpers.convert(napalm_helpers.ip, ip))): - rows.append({ - 'device': device, - 'interface': arp_entry.get('interface'), - 'mac': napalm_helpers.convert(napalm_helpers.mac, arp_entry.get('mac')), - 'ip': napalm_helpers.convert(napalm_helpers.ip, arp_entry.get('ip')), - 'age': arp_entry.get('age') - }) + if ( + (mac and arp_entry.get("mac", "").lower() == mac.lower()) + or ( # pylint: disable=too-many-boolean-expressions + interface and interface in arp_entry.get("interface", "") + ) + or ( + ip + and napalm_helpers.convert( + napalm_helpers.ip, arp_entry.get("ip", "") + ) + == napalm_helpers.convert(napalm_helpers.ip, ip) + ) + ): + rows.append( + { + "device": device, + "interface": arp_entry.get("interface"), + "mac": napalm_helpers.convert( + napalm_helpers.mac, arp_entry.get("mac") + ), + "ip": napalm_helpers.convert( + napalm_helpers.ip, arp_entry.get("ip") + ), + "age": arp_entry.get("age"), + } + ) return _display_runner(rows, labels, title, display=display) def findmac(device=None, mac=None, interface=None, vlan=None, display=_DEFAULT_DISPLAY): - ''' + """ Search in the MAC Address tables, using the following mine functions: - net.mac @@ -560,28 +606,28 @@ def findmac(device=None, mac=None, interface=None, vlan=None, display=_DEFAULT_D _____________________________________________________________________________________________ | edge01.bjm01 | ae0.171 | 8C:60:0F:78:EC:41 | 171 | False | True | 0 | 0.0 | _____________________________________________________________________________________________ - ''' + """ labels = { - 'device': 'Device', - 'interface': 'Interface', - 'mac': 'MAC', - 'vlan': 'VLAN', - 'static': 'Static', - 'active': 'Active', - 'moves': 'Moves', - 'last_move': 'Last Move' + "device": "Device", + "interface": "Interface", + "mac": "MAC", + "vlan": "VLAN", + "static": "Static", + "active": "Active", + "moves": "Moves", + "last_move": "Last Move", } rows = [] - all_mac = _get_mine('net.mac') + all_mac = _get_mine("net.mac") title = "MAC Address(es)" if device: - title += ' on device {device}'.format(device=device) + title += " on device {device}".format(device=device) if interface: - title += ' on interface {interf}'.format(interf=interface) + title += " on interface {interf}".format(interf=interface) if vlan: - title += ' on VLAN {vlan}'.format(vlan=vlan) + title += " on VLAN {vlan}".format(vlan=vlan) if device: all_mac = {device: all_mac.get(device)} @@ -589,36 +635,51 @@ def findmac(device=None, mac=None, interface=None, vlan=None, display=_DEFAULT_D for device, device_mac in six.iteritems(all_mac): if not device_mac: continue - if not device_mac.get('result', False): + if not device_mac.get("result", False): continue - mac_table = device_mac.get('out', []) + mac_table = device_mac.get("out", []) for mac_entry in mac_table: - if ((mac and # pylint: disable=too-many-boolean-expressions - napalm_helpers.convert(napalm_helpers.mac, mac_entry.get('mac', '')) == - napalm_helpers.convert(napalm_helpers.mac, mac)) or - (interface and interface in mac_entry.get('interface', '')) or - (vlan and six.text_type(mac_entry.get('vlan', '')) == six.text_type(vlan))): - rows.append({ - 'device': device, - 'interface': mac_entry.get('interface'), - 'mac': napalm_helpers.convert(napalm_helpers.mac, mac_entry.get('mac')), - 'vlan': mac_entry.get('vlan'), - 'static': mac_entry.get('static'), - 'active': mac_entry.get('active'), - 'moves': mac_entry.get('moves'), - 'last_move': mac_entry.get('last_move') - }) + if ( + ( + mac + and napalm_helpers.convert( # pylint: disable=too-many-boolean-expressions + napalm_helpers.mac, mac_entry.get("mac", "") + ) + == napalm_helpers.convert(napalm_helpers.mac, mac) + ) + or (interface and interface in mac_entry.get("interface", "")) + or ( + vlan + and six.text_type(mac_entry.get("vlan", "")) == six.text_type(vlan) + ) + ): + rows.append( + { + "device": device, + "interface": mac_entry.get("interface"), + "mac": napalm_helpers.convert( + napalm_helpers.mac, mac_entry.get("mac") + ), + "vlan": mac_entry.get("vlan"), + "static": mac_entry.get("static"), + "active": mac_entry.get("active"), + "moves": mac_entry.get("moves"), + "last_move": mac_entry.get("last_move"), + } + ) return _display_runner(rows, labels, title, display=display) -def lldp(device=None, - interface=None, - title=None, - pattern=None, - chassis=None, - display=_DEFAULT_DISPLAY): - ''' +def lldp( + device=None, + interface=None, + title=None, + pattern=None, + chassis=None, + display=_DEFAULT_DISPLAY, +): + """ Search in the LLDP neighbors, using the following mine functions: - net.lldp @@ -680,33 +741,35 @@ def lldp(device=None, | | | | | | | | (c) 2002-2012 by Cisco Systems, Inc. Compiled | | | | | | | | | 2/17/2016 22:00:00 | _________________________________________________________________________________________________________________________________________________________________________________________ - ''' - all_lldp = _get_mine('net.lldp') + """ + all_lldp = _get_mine("net.lldp") labels = { - 'device': 'Device', - 'interface': 'Interface', - 'parent_interface': 'Parent Interface', - 'remote_chassis_id': 'Remote Chassis ID', - 'remote_port_id': 'Remote Port ID', - 'remote_port_desc': 'Remote Port Description', - 'remote_system_name': 'Remote System Name', - 'remote_system_desc': 'Remote System Description' + "device": "Device", + "interface": "Interface", + "parent_interface": "Parent Interface", + "remote_chassis_id": "Remote Chassis ID", + "remote_port_id": "Remote Port ID", + "remote_port_desc": "Remote Port Description", + "remote_system_name": "Remote System Name", + "remote_system_desc": "Remote System Description", } rows = [] if pattern: - title = 'Pattern "{0}" found in one of the following LLDP details'.format(pattern) + title = 'Pattern "{0}" found in one of the following LLDP details'.format( + pattern + ) if not title: - title = 'LLDP Neighbors' + title = "LLDP Neighbors" if interface: - title += ' for interface {0}'.format(interface) + title += " for interface {0}".format(interface) else: - title += ' for all interfaces' + title += " for all interfaces" if device: - title += ' on device {0}'.format(device) + title += " on device {0}".format(device) if chassis: - title += ' having Chassis ID {0}'.format(chassis) + title += " having Chassis ID {0}".format(chassis) if device: all_lldp = {device: all_lldp.get(device)} @@ -714,46 +777,57 @@ def lldp(device=None, for device, device_lldp in six.iteritems(all_lldp): if not device_lldp: continue - if not device_lldp.get('result', False): + if not device_lldp.get("result", False): continue - lldp_interfaces = device_lldp.get('out', {}) + lldp_interfaces = device_lldp.get("out", {}) if interface: lldp_interfaces = {interface: lldp_interfaces.get(interface, [])} for intrf, interface_lldp in six.iteritems(lldp_interfaces): if not interface_lldp: continue for lldp_row in interface_lldp: - rsn = (lldp_row.get('remote_system_name', '') or '') - rpi = (lldp_row.get('remote_port_id', '') or '') - rsd = (lldp_row.get('remote_system_description', '') or '') - rpd = (lldp_row.get('remote_port_description', '') or '') - rci = (lldp_row.get('remote_chassis_id', '') or '') + rsn = lldp_row.get("remote_system_name", "") or "" + rpi = lldp_row.get("remote_port_id", "") or "" + rsd = lldp_row.get("remote_system_description", "") or "" + rpd = lldp_row.get("remote_port_description", "") or "" + rci = lldp_row.get("remote_chassis_id", "") or "" if pattern: ptl = pattern.lower() - if not((ptl in rsn.lower()) or (ptl in rsd.lower()) or - (ptl in rpd.lower()) or (ptl in rci.lower())): + if not ( + (ptl in rsn.lower()) + or (ptl in rsd.lower()) + or (ptl in rpd.lower()) + or (ptl in rci.lower()) + ): # nothing matched, let's move on continue if chassis: - if (napalm_helpers.convert(napalm_helpers.mac, rci) != - napalm_helpers.convert(napalm_helpers.mac, chassis)): + if napalm_helpers.convert( + napalm_helpers.mac, rci + ) != napalm_helpers.convert(napalm_helpers.mac, chassis): continue - rows.append({ - 'device': device, - 'interface': intrf, - 'parent_interface': (lldp_row.get('parent_interface', '') or ''), - 'remote_chassis_id': napalm_helpers.convert(napalm_helpers.mac, rci), - 'remote_port_id': rpi, - 'remote_port_descr': rpd, - 'remote_system_name': rsn, - 'remote_system_descr': rsd - }) + rows.append( + { + "device": device, + "interface": intrf, + "parent_interface": ( + lldp_row.get("parent_interface", "") or "" + ), + "remote_chassis_id": napalm_helpers.convert( + napalm_helpers.mac, rci + ), + "remote_port_id": rpi, + "remote_port_descr": rpd, + "remote_system_name": rsn, + "remote_system_descr": rsd, + } + ) return _display_runner(rows, labels, title, display=display) def find(addr, best=True, display=_DEFAULT_DISPLAY): - ''' + """ Search in all possible entities (Interfaces, MAC tables, ARP tables, LLDP neighbors), using the following mine functions: @@ -798,40 +872,40 @@ def find(addr, best=True, display=_DEFAULT_DISPLAY): _____________________________________________________________________________ | edge01.flw01 | irb.349 [ae0.349] | 2C:60:0C:2A:4C:0A | 10.10.10.7 | 832.0 | _____________________________________________________________________________ - ''' + """ if not addr: if display: - print('Please type a valid MAC/IP Address / Device / Interface / VLAN') + print("Please type a valid MAC/IP Address / Device / Interface / VLAN") return {} - device = '' - interface = '' - mac = '' - ip = '' # pylint: disable=invalid-name + device = "" + interface = "" + mac = "" + ip = "" # pylint: disable=invalid-name ipnet = None results = { - 'int_net': [], - 'int_descr': [], - 'int_name': [], - 'int_ip': [], - 'int_mac': [], - 'int_device': [], - 'lldp_descr': [], - 'lldp_int': [], - 'lldp_device': [], - 'lldp_mac': [], - 'lldp_device_int': [], - 'mac_device': [], - 'mac_int': [], - 'arp_device': [], - 'arp_int': [], - 'arp_mac': [], - 'arp_ip': [] + "int_net": [], + "int_descr": [], + "int_name": [], + "int_ip": [], + "int_mac": [], + "int_device": [], + "lldp_descr": [], + "lldp_int": [], + "lldp_device": [], + "lldp_mac": [], + "lldp_device_int": [], + "mac_device": [], + "mac_int": [], + "arp_device": [], + "arp_int": [], + "arp_mac": [], + "arp_ip": [], } if isinstance(addr, int): - results['mac'] = findmac(vlan=addr, display=display) + results["mac"] = findmac(vlan=addr, display=display) if not display: return results else: @@ -846,69 +920,71 @@ def find(addr, best=True, display=_DEFAULT_DISPLAY): mac = False if not mac: try: - ip = napalm_helpers.convert(napalm_helpers.ip, addr) # pylint: disable=invalid-name + ip = napalm_helpers.convert( + napalm_helpers.ip, addr + ) # pylint: disable=invalid-name except ValueError: pass ipnet = _get_network_obj(addr) if ipnet: - results['int_net'] = interfaces(ipnet=ipnet, best=best, display=display) + results["int_net"] = interfaces(ipnet=ipnet, best=best, display=display) if not (ipnet or ip): # search in all possible places # display all interfaces details - results['int_descr'] = interfaces(pattern=addr, display=display) - results['int_name'] = interfaces(interface=addr, display=display) - results['int_device'] = interfaces(device=addr, display=display) + results["int_descr"] = interfaces(pattern=addr, display=display) + results["int_name"] = interfaces(interface=addr, display=display) + results["int_device"] = interfaces(device=addr, display=display) # search in LLDP details - results['lldp_descr'] = lldp(pattern=addr, display=display) - results['lldp_int'] = lldp(interface=addr, display=display) - results['lldp_device'] = lldp(device=addr, display=display) + results["lldp_descr"] = lldp(pattern=addr, display=display) + results["lldp_int"] = lldp(interface=addr, display=display) + results["lldp_device"] = lldp(device=addr, display=display) # search in MAC Address tables - results['mac_device'] = findmac(device=addr, display=display) - results['mac_int'] = findmac(interface=addr, display=display) + results["mac_device"] = findmac(device=addr, display=display) + results["mac_int"] = findmac(interface=addr, display=display) # search in ARP tables - results['arp_device'] = findarp(device=addr, display=display) - results['arp_int'] = findarp(interface=addr, display=display) + results["arp_device"] = findarp(device=addr, display=display) + results["arp_int"] = findarp(interface=addr, display=display) if not display: return results if mac: - results['int_descr'] = findmac(mac=mac, display=display) - results['arp_mac'] = findarp(mac=mac, display=display) - results['lldp_mac'] = lldp(chassis=mac, display=display) + results["int_descr"] = findmac(mac=mac, display=display) + results["arp_mac"] = findarp(mac=mac, display=display) + results["lldp_mac"] = lldp(chassis=mac, display=display) if ip: - results['arp_ip'] = findarp(ip=ip, display=display) + results["arp_ip"] = findarp(ip=ip, display=display) # let's search in Interfaces if mac: device, interface, ips = _find_interfaces_ip(mac) - ip = ', '.join(ips) # pylint: disable=invalid-name + ip = ", ".join(ips) # pylint: disable=invalid-name if device and interface: - title = 'Interface {interface} on {device} has the physical address ({mac})'.format( - interface=interface, - device=device, - mac=mac + title = "Interface {interface} on {device} has the physical address ({mac})".format( + interface=interface, device=device, mac=mac + ) + results["int_mac"] = interfaces( + device=device, interface=interface, title=title, display=display ) - results['int_mac'] = interfaces(device=device, interface=interface, title=title, display=display) elif ip: device, interface, mac = _find_interfaces_mac(ip) if device and interface: - title = 'IP Address {ip} is set for interface {interface}, on {device}'.format( - interface=interface, - device=device, - ip=ip + title = "IP Address {ip} is set for interface {interface}, on {device}".format( + interface=interface, device=device, ip=ip + ) + results["int_ip"] = interfaces( + device=device, interface=interface, title=title, display=display ) - results['int_ip'] = interfaces(device=device, interface=interface, title=title, display=display) if device and interface: - results['lldp_device_int'] = lldp(device, interface, display=display) + results["lldp_device_int"] = lldp(device, interface, display=display) if not display: return results def multi_find(*patterns, **kwargs): - ''' + """ Execute multiple search tasks. This function is based on the `find` function. Depending on the search items, some information might overlap. @@ -956,12 +1032,14 @@ def multi_find(*patterns, **kwargs): ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | edge01.oua04 | xe-0/1/2 | ae1 | DE:AD:BE:EF:DE:AD | Ethernet1/49 | | Cisco NX-OS(tm) n6000, Software (n6000-uk9) | edge07.oua04.dummy.net | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ''' + """ out = {} for pattern in set(patterns): - search_result = find(pattern, - best=kwargs.get('best', True), - display=kwargs.get('display', _DEFAULT_DISPLAY)) + search_result = find( + pattern, + best=kwargs.get("best", True), + display=kwargs.get("display", _DEFAULT_DISPLAY), + ) out[pattern] = search_result - if not kwargs.get('display', _DEFAULT_DISPLAY): + if not kwargs.get("display", _DEFAULT_DISPLAY): return out diff --git a/salt/runners/network.py b/salt/runners/network.py index 56420d8b59a..45746170f67 100644 --- a/salt/runners/network.py +++ b/salt/runners/network.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Network tools to run from the Master -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import socket @@ -16,8 +17,8 @@ import salt.utils.stringutils log = logging.getLogger(__name__) -def wollist(maclist, bcast='255.255.255.255', destport=9): - ''' +def wollist(maclist, bcast="255.255.255.255", destport=9): + """ Send a "Magic Packet" to wake up a list of Minions. This list must contain one MAC hardware address per line @@ -28,23 +29,25 @@ def wollist(maclist, bcast='255.255.255.255', destport=9): salt-run network.wollist '/path/to/maclist' salt-run network.wollist '/path/to/maclist' 255.255.255.255 7 salt-run network.wollist '/path/to/maclist' 255.255.255.255 7 - ''' + """ ret = [] try: - with salt.utils.files.fopen(maclist, 'r') as ifile: + with salt.utils.files.fopen(maclist, "r") as ifile: for mac in ifile: mac = salt.utils.stringutils.to_unicode(mac).strip() wol(mac, bcast, destport) - print('Waking up {0}'.format(mac)) + print("Waking up {0}".format(mac)) ret.append(mac) except Exception as err: # pylint: disable=broad-except - __jid_event__.fire_event({'error': 'Failed to open the MAC file. Error: {0}'.format(err)}, 'progress') + __jid_event__.fire_event( + {"error": "Failed to open the MAC file. Error: {0}".format(err)}, "progress" + ) return [] return ret -def wol(mac, bcast='255.255.255.255', destport=9): - ''' +def wol(mac, bcast="255.255.255.255", destport=9): + """ Send a "Magic Packet" to wake up a Minion CLI Example: @@ -54,16 +57,16 @@ def wol(mac, bcast='255.255.255.255', destport=9): salt-run network.wol 08-00-27-13-69-77 salt-run network.wol 080027136977 255.255.255.255 7 salt-run network.wol 08:00:27:13:69:77 255.255.255.255 7 - ''' + """ dest = salt.utils.network.mac_str_to_bytes(mac) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - sock.sendto(b'\xff' * 6 + dest * 16, (bcast, int(destport))) + sock.sendto(b"\xff" * 6 + dest * 16, (bcast, int(destport))) return True -def wolmatch(tgt, tgt_type='glob', bcast='255.255.255.255', destport=9): - ''' +def wolmatch(tgt, tgt_type="glob", bcast="255.255.255.255", destport=9): + """ Send a "Magic Packet" to wake up Minions that are matched in the grains cache CLI Example: @@ -72,15 +75,15 @@ def wolmatch(tgt, tgt_type='glob', bcast='255.255.255.255', destport=9): salt-run network.wolmatch minion_id salt-run network.wolmatch 192.168.0.0/16 tgt_type='ipcidr' bcast=255.255.255.255 destport=7 - ''' + """ ret = [] - minions = __salt__['cache.grains'](tgt, tgt_type) + minions = __salt__["cache.grains"](tgt, tgt_type) for minion in minions: - for iface, mac in minion['hwaddr_interfaces'].items(): - if iface == 'lo': + for iface, mac in minion["hwaddr_interfaces"].items(): + if iface == "lo": continue mac = mac.strip() wol(mac, bcast, destport) - log.info('Waking up %s', mac) + log.info("Waking up %s", mac) ret.append(mac) return ret diff --git a/salt/runners/pagerduty.py b/salt/runners/pagerduty.py index a8d4e08eaa2..5148ae8d2f8 100644 --- a/salt/runners/pagerduty.py +++ b/salt/runners/pagerduty.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Runner Module for Firing Events via PagerDuty .. versionadded:: 2014.1.0 @@ -14,7 +14,7 @@ Runner Module for Firing Events via PagerDuty my-pagerduty-account: pagerduty.api_key: F3Rbyjbve43rfFWf2214 pagerduty.subdomain: mysubdomain -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -26,127 +26,116 @@ from salt.ext import six def __virtual__(): - ''' + """ No dependencies outside of what Salt itself requires - ''' + """ return True def list_services(profile=None, api_key=None): - ''' + """ List services belonging to this account CLI Example: salt-run pagerduty.list_services my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'services', - 'name', - __salt__['config.option'](profile), - api_key, - opts=__opts__ + "services", "name", __salt__["config.option"](profile), api_key, opts=__opts__ ) def list_incidents(profile=None, api_key=None): - ''' + """ List incidents belonging to this account CLI Example: salt-run pagerduty.list_incidents my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'incidents', - 'id', - __salt__['config.option'](profile), - api_key, - opts=__opts__ + "incidents", "id", __salt__["config.option"](profile), api_key, opts=__opts__ ) def list_users(profile=None, api_key=None): - ''' + """ List users belonging to this account CLI Example: salt-run pagerduty.list_users my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'users', - 'id', - __salt__['config.option'](profile), - api_key, - opts=__opts__ + "users", "id", __salt__["config.option"](profile), api_key, opts=__opts__ ) def list_schedules(profile=None, api_key=None): - ''' + """ List schedules belonging to this account CLI Example: salt-run pagerduty.list_schedules my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'schedules', - 'id', - __salt__['config.option'](profile), - api_key, - opts=__opts__ + "schedules", "id", __salt__["config.option"](profile), api_key, opts=__opts__ ) def list_windows(profile=None, api_key=None): - ''' + """ List maintenance windows belonging to this account CLI Example: salt-run pagerduty.list_windows my-pagerduty-account salt-run pagerduty.list_maintenance_windows my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'maintenance_windows', - 'id', - __salt__['config.option'](profile), + "maintenance_windows", + "id", + __salt__["config.option"](profile), api_key, - opts=__opts__ + opts=__opts__, ) # The long version, added for consistency -list_maintenance_windows = salt.utils.functools.alias_function(list_windows, 'list_maintenance_windows') +list_maintenance_windows = salt.utils.functools.alias_function( + list_windows, "list_maintenance_windows" +) def list_policies(profile=None, api_key=None): - ''' + """ List escalation policies belonging to this account CLI Example: salt-run pagerduty.list_policies my-pagerduty-account salt-run pagerduty.list_escalation_policies my-pagerduty-account - ''' + """ return salt.utils.pagerduty.list_items( - 'escalation_policies', - 'id', - __salt__['config.option'](profile), + "escalation_policies", + "id", + __salt__["config.option"](profile), api_key, - opts=__opts__ + opts=__opts__, ) # The long version, added for consistency -list_escalation_policies = salt.utils.functools.alias_function(list_policies, 'list_escalation_policies') +list_escalation_policies = salt.utils.functools.alias_function( + list_policies, "list_escalation_policies" +) -def create_event(service_key=None, description=None, details=None, - incident_key=None, profile=None): - ''' +def create_event( + service_key=None, description=None, details=None, incident_key=None, profile=None +): + """ Create an event in PagerDuty. Designed for use in states. CLI Example: @@ -170,26 +159,28 @@ def create_event(service_key=None, description=None, details=None, profile This refers to the configuration profile to use to connect to the PagerDuty service. - ''' - trigger_url = 'https://events.pagerduty.com/generic/2010-04-15/create_event.json' + """ + trigger_url = "https://events.pagerduty.com/generic/2010-04-15/create_event.json" if isinstance(details, six.string_types): details = salt.utils.yaml.safe_load(details) if isinstance(details, six.string_types): - details = {'details': details} + details = {"details": details} - ret = salt.utils.json.loads(salt.utils.pagerduty.query( - method='POST', - profile_dict=__salt__['config.option'](profile), - api_key=service_key, - data={ - 'service_key': service_key, - 'incident_key': incident_key, - 'event_type': 'trigger', - 'description': description, - 'details': details, - }, - url=trigger_url, - opts=__opts__ - )) + ret = salt.utils.json.loads( + salt.utils.pagerduty.query( + method="POST", + profile_dict=__salt__["config.option"](profile), + api_key=service_key, + data={ + "service_key": service_key, + "incident_key": incident_key, + "event_type": "trigger", + "description": description, + "details": details, + }, + url=trigger_url, + opts=__opts__, + ) + ) return ret diff --git a/salt/runners/pillar.py b/salt/runners/pillar.py index 7331fcb41b8..acbcc8d97a8 100644 --- a/salt/runners/pillar.py +++ b/salt/runners/pillar.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Functions to interact with the pillar compiler on the master -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -9,8 +9,8 @@ import salt.pillar import salt.utils.minions -def show_top(minion=None, saltenv='base'): - ''' +def show_top(minion=None, saltenv="base"): + """ Returns the compiled top data for pillar for a specific minion. If no minion is specified, we use the first minion we find. @@ -19,25 +19,21 @@ def show_top(minion=None, saltenv='base'): .. code-block:: bash salt-run pillar.show_top - ''' + """ id_, grains, _ = salt.utils.minions.get_minion_data(minion, __opts__) - pillar = salt.pillar.Pillar( - __opts__, - grains, - id_, - saltenv) + pillar = salt.pillar.Pillar(__opts__, grains, id_, saltenv) top, errors = pillar.get_top() if errors: - __jid_event__.fire_event({'data': errors, 'outputter': 'nested'}, 'progress') + __jid_event__.fire_event({"data": errors, "outputter": "nested"}, "progress") return errors return top -def show_pillar(minion='*', **kwargs): - ''' +def show_pillar(minion="*", **kwargs): + """ Returns the compiled pillar either of a specific minion or just the global available pillars. This function assumes that no minion has the id ``*``. @@ -81,28 +77,23 @@ def show_pillar(minion='*', **kwargs): runner = salt.runner.RunnerClient(opts) pillar = runner.cmd('pillar.show_pillar', []) print(pillar) - ''' + """ pillarenv = None - saltenv = 'base' + saltenv = "base" id_, grains, _ = salt.utils.minions.get_minion_data(minion, __opts__) if grains is None: - grains = {'fqdn': minion} + grains = {"fqdn": minion} for key in kwargs: - if key == 'saltenv': + if key == "saltenv": saltenv = kwargs[key] - elif key == 'pillarenv': + elif key == "pillarenv": # pillarenv overridden on CLI pillarenv = kwargs[key] else: grains[key] = kwargs[key] - pillar = salt.pillar.Pillar( - __opts__, - grains, - id_, - saltenv, - pillarenv=pillarenv) + pillar = salt.pillar.Pillar(__opts__, grains, id_, saltenv, pillarenv=pillarenv) compiled_pillar = pillar.compile_pillar() return compiled_pillar diff --git a/salt/runners/pkg.py b/salt/runners/pkg.py index fc3d1359b9b..03caf1fc3d8 100644 --- a/salt/runners/pkg.py +++ b/salt/runners/pkg.py @@ -1,35 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" Package helper functions using ``salt.modules.pkg`` .. versionadded:: 2015.8.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.minion + # Import salt libs import salt.output -import salt.minion # Import 3rd-party libs from salt.ext import six def _get_returner(returner_types): - ''' + """ Helper to iterate over retuerner_types and pick the first one - ''' + """ for returner in returner_types: if returner: return returner -def list_upgrades(jid, - style='group', - outputter='nested', - ext_source=None): - ''' +def list_upgrades(jid, style="group", outputter="nested", ext_source=None): + """ Show list of available pkg upgrades using a specified format style CLI Example: @@ -37,28 +35,26 @@ def list_upgrades(jid, .. code-block:: bash salt-run pkg.list_upgrades jid=20141120114114417719 style=group - ''' + """ mminion = salt.minion.MasterMinion(__opts__) - returner = _get_returner(( - __opts__['ext_job_cache'], - ext_source, - __opts__['master_job_cache'] - )) + returner = _get_returner( + (__opts__["ext_job_cache"], ext_source, __opts__["master_job_cache"]) + ) - data = mminion.returners['{0}.get_jid'.format(returner)](jid) + data = mminion.returners["{0}.get_jid".format(returner)](jid) pkgs = {} - if style == 'group': + if style == "group": for minion in data: - results = data[minion]['return'] + results = data[minion]["return"] for pkg, pkgver in six.iteritems(results): if pkg not in six.iterkeys(pkgs): - pkgs[pkg] = {pkgver: {'hosts': []}} + pkgs[pkg] = {pkgver: {"hosts": []}} if pkgver not in six.iterkeys(pkgs[pkg]): - pkgs[pkg].update({pkgver: {'hosts': []}}) + pkgs[pkg].update({pkgver: {"hosts": []}}) - pkgs[pkg][pkgver]['hosts'].append(minion) + pkgs[pkg][pkgver]["hosts"].append(minion) if outputter: salt.output.display_output(pkgs, outputter, opts=__opts__) diff --git a/salt/runners/queue.py b/salt/runners/queue.py index aad005d99dd..6322dab5683 100644 --- a/salt/runners/queue.py +++ b/salt/runners/queue.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" General management and processing of queues. ============================================ @@ -60,20 +60,20 @@ master and it will run the specified amount of commands per time period. The above configuration will pop 2 runner jobs off the runner queue, and then run them. And it will do this every minute, unless there are any jobs that are still running from the last time the process_runner task was executed. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.loader +from salt.exceptions import SaltInvocationError from salt.ext import six from salt.utils.event import get_event, tagify -from salt.exceptions import SaltInvocationError -def insert(queue, items, backend='sqlite'): - ''' +def insert(queue, items, backend="sqlite"): + """ Add an item or items to a queue CLI Example: @@ -84,17 +84,17 @@ def insert(queue, items, backend='sqlite'): salt-run queue.insert myqueue "['item1', 'item2', 'item3']" salt-run queue.insert myqueue myitem backend=sqlite salt-run queue.insert myqueue "['item1', 'item2', 'item3']" backend=sqlite - ''' + """ queue_funcs = salt.loader.queues(__opts__) - cmd = '{0}.insert'.format(backend) + cmd = "{0}.insert".format(backend) if cmd not in queue_funcs: raise SaltInvocationError('Function "{0}" is not available'.format(cmd)) ret = queue_funcs[cmd](items=items, queue=queue) return ret -def delete(queue, items, backend='sqlite'): - ''' +def delete(queue, items, backend="sqlite"): + """ Delete an item or items from a queue CLI Example: @@ -104,17 +104,17 @@ def delete(queue, items, backend='sqlite'): salt-run queue.delete myqueue myitem salt-run queue.delete myqueue myitem backend=sqlite salt-run queue.delete myqueue "['item1', 'item2', 'item3']" - ''' + """ queue_funcs = salt.loader.queues(__opts__) - cmd = '{0}.delete'.format(backend) + cmd = "{0}.delete".format(backend) if cmd not in queue_funcs: raise SaltInvocationError('Function "{0}" is not available'.format(cmd)) ret = queue_funcs[cmd](items=items, queue=queue) return ret -def list_queues(backend='sqlite'): - ''' +def list_queues(backend="sqlite"): + """ Return a list of Salt Queues on the backend CLI Example: @@ -123,17 +123,17 @@ def list_queues(backend='sqlite'): salt-run queue.list_queues salt-run queue.list_queues backend=sqlite - ''' + """ queue_funcs = salt.loader.queues(__opts__) - cmd = '{0}.list_queues'.format(backend) + cmd = "{0}.list_queues".format(backend) if cmd not in queue_funcs: raise SaltInvocationError('Function "{0}" is not available'.format(cmd)) ret = queue_funcs[cmd]() return ret -def list_length(queue, backend='sqlite'): - ''' +def list_length(queue, backend="sqlite"): + """ Provide the number of items in a queue CLI Example: @@ -142,17 +142,17 @@ def list_length(queue, backend='sqlite'): salt-run queue.list_length myqueue salt-run queue.list_length myqueue backend=sqlite - ''' + """ queue_funcs = salt.loader.queues(__opts__) - cmd = '{0}.list_length'.format(backend) + cmd = "{0}.list_length".format(backend) if cmd not in queue_funcs: raise SaltInvocationError('Function "{0}" is not available'.format(cmd)) ret = queue_funcs[cmd](queue=queue) return ret -def list_items(queue, backend='sqlite'): - ''' +def list_items(queue, backend="sqlite"): + """ List contents of a queue CLI Example: @@ -161,17 +161,17 @@ def list_items(queue, backend='sqlite'): salt-run queue.list_items myqueue salt-run queue.list_items myqueue backend=sqlite - ''' + """ queue_funcs = salt.loader.queues(__opts__) - cmd = '{0}.list_items'.format(backend) + cmd = "{0}.list_items".format(backend) if cmd not in queue_funcs: raise SaltInvocationError('Function "{0}" is not available'.format(cmd)) ret = queue_funcs[cmd](queue=queue) return ret -def pop(queue, quantity=1, backend='sqlite', is_runner=False): - ''' +def pop(queue, quantity=1, backend="sqlite", is_runner=False): + """ Pop one or more or all items from a queue CLI Example: @@ -183,17 +183,17 @@ def pop(queue, quantity=1, backend='sqlite', is_runner=False): salt-run queue.pop myqueue all salt-run queue.pop myqueue 6 backend=sqlite salt-run queue.pop myqueue all backend=sqlite - ''' + """ queue_funcs = salt.loader.queues(__opts__) - cmd = '{0}.pop'.format(backend) + cmd = "{0}.pop".format(backend) if cmd not in queue_funcs: raise SaltInvocationError('Function "{0}" is not available'.format(cmd)) ret = queue_funcs[cmd](quantity=quantity, queue=queue, is_runner=is_runner) return ret -def process_queue(queue, quantity=1, backend='sqlite', is_runner=False): - ''' +def process_queue(queue, quantity=1, backend="sqlite", is_runner=False): + """ Pop items off a queue and create an event on the Salt event bus to be processed by a Reactor. @@ -204,43 +204,46 @@ def process_queue(queue, quantity=1, backend='sqlite', is_runner=False): salt-run queue.process_queue myqueue salt-run queue.process_queue myqueue 6 salt-run queue.process_queue myqueue all backend=sqlite - ''' + """ # get ready to send an event with get_event( - 'master', - __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=False) as event_bus: + "master", + __opts__["sock_dir"], + __opts__["transport"], + opts=__opts__, + listen=False, + ) as event_bus: try: - items = pop(queue=queue, quantity=quantity, backend=backend, is_runner=is_runner) + items = pop( + queue=queue, quantity=quantity, backend=backend, is_runner=is_runner + ) except SaltInvocationError as exc: - error_txt = '{0}'.format(exc) - __jid_event__.fire_event({'errors': error_txt}, 'progress') + error_txt = "{0}".format(exc) + __jid_event__.fire_event({"errors": error_txt}, "progress") return False - data = {'items': items, - 'backend': backend, - 'queue': queue, - } - event_bus.fire_event(data, tagify([queue, 'process'], prefix='queue')) + data = { + "items": items, + "backend": backend, + "queue": queue, + } + event_bus.fire_event(data, tagify([queue, "process"], prefix="queue")) return data def __get_queue_opts(queue=None, backend=None): - ''' + """ Get consistent opts for the queued runners - ''' + """ if queue is None: - queue = __opts__.get('runner_queue', {}).get('queue') + queue = __opts__.get("runner_queue", {}).get("queue") if backend is None: - backend = __opts__.get('runner_queue', {}).get('backend', 'pgjsonb') - return {'backend': backend, - 'queue': queue} + backend = __opts__.get("runner_queue", {}).get("backend", "pgjsonb") + return {"backend": backend, "queue": queue} def insert_runner(fun, args=None, kwargs=None, queue=None, backend=None): - ''' + """ Insert a reference to a runner into the queue so that it can be run later. fun @@ -265,20 +268,20 @@ def insert_runner(fun, args=None, kwargs=None, queue=None, backend=None): salt-run queue.insert_runner test.stdout_print salt-run queue.insert_runner event.send test_insert_runner kwargs='{"data": {"foo": "bar"}}' - ''' + """ if args is None: args = [] elif isinstance(args, six.string_types): - args = args.split(',') + args = args.split(",") if kwargs is None: kwargs = {} queue_kwargs = __get_queue_opts(queue=queue, backend=backend) - data = {'fun': fun, 'args': args, 'kwargs': kwargs} + data = {"fun": fun, "args": args, "kwargs": kwargs} return insert(items=data, **queue_kwargs) def process_runner(quantity=1, queue=None, backend=None): - ''' + """ Process queued runners quantity @@ -297,8 +300,8 @@ def process_runner(quantity=1, queue=None, backend=None): salt-run queue.process_runner salt-run queue.process_runner 5 - ''' + """ queue_kwargs = __get_queue_opts(queue=queue, backend=backend) data = process_queue(quantity=quantity, is_runner=True, **queue_kwargs) - for job in data['items']: - __salt__[job['fun']](*job['args'], **job['kwargs']) + for job in data["items"]: + __salt__[job["fun"]](*job["args"], **job["kwargs"]) diff --git a/salt/runners/reactor.py b/salt/runners/reactor.py index a78271c2426..c720c74b6f3 100644 --- a/salt/runners/reactor.py +++ b/salt/runners/reactor.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A convenience system to manage reactors Beginning in the 2017.7 release, the reactor runner requires that the reactor @@ -12,27 +12,29 @@ engine configuration for the Salt master. engines: - reactor -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import salt libs -import salt.utils.reactor import salt.syspaths import salt.utils.event import salt.utils.process + +# Import salt libs +import salt.utils.reactor from salt.ext.six import string_types log = logging.getLogger(__name__) __func_alias__ = { - 'list_': 'list', + "list_": "list", } -def list_(saltenv='base', test=None): - ''' +def list_(saltenv="base", test=None): + """ List currently configured reactors CLI Example: @@ -40,23 +42,24 @@ def list_(saltenv='base', test=None): .. code-block:: bash salt-run reactor.list - ''' + """ sevent = salt.utils.event.get_event( - 'master', - __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=True) + "master", + __opts__["sock_dir"], + __opts__["transport"], + opts=__opts__, + listen=True, + ) - __jid_event__.fire_event({}, 'salt/reactors/manage/list') + __jid_event__.fire_event({}, "salt/reactors/manage/list") - results = sevent.get_event(wait=30, tag='salt/reactors/manage/list-results') - reactors = results['reactors'] + results = sevent.get_event(wait=30, tag="salt/reactors/manage/list-results") + reactors = results["reactors"] return reactors -def add(event, reactors, saltenv='base', test=None): - ''' +def add(event, reactors, saltenv="base", test=None): + """ Add a new reactor CLI Example: @@ -64,27 +67,28 @@ def add(event, reactors, saltenv='base', test=None): .. code-block:: bash salt-run reactor.add 'salt/cloud/*/destroyed' reactors='/srv/reactor/destroy/*.sls' - ''' + """ if isinstance(reactors, string_types): reactors = [reactors] sevent = salt.utils.event.get_event( - 'master', - __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=True) + "master", + __opts__["sock_dir"], + __opts__["transport"], + opts=__opts__, + listen=True, + ) - __jid_event__.fire_event({'event': event, - 'reactors': reactors}, - 'salt/reactors/manage/add') + __jid_event__.fire_event( + {"event": event, "reactors": reactors}, "salt/reactors/manage/add" + ) - res = sevent.get_event(wait=30, tag='salt/reactors/manage/add-complete') - return res['result'] + res = sevent.get_event(wait=30, tag="salt/reactors/manage/add-complete") + return res["result"] -def delete(event, saltenv='base', test=None): - ''' +def delete(event, saltenv="base", test=None): + """ Delete a reactor CLI Example: @@ -92,15 +96,16 @@ def delete(event, saltenv='base', test=None): .. code-block:: bash salt-run reactor.delete 'salt/cloud/*/destroyed' - ''' + """ sevent = salt.utils.event.get_event( - 'master', - __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=True) + "master", + __opts__["sock_dir"], + __opts__["transport"], + opts=__opts__, + listen=True, + ) - __jid_event__.fire_event({'event': event}, 'salt/reactors/manage/delete') + __jid_event__.fire_event({"event": event}, "salt/reactors/manage/delete") - res = sevent.get_event(wait=30, tag='salt/reactors/manage/delete-complete') - return res['result'] + res = sevent.get_event(wait=30, tag="salt/reactors/manage/delete-complete") + return res["result"] diff --git a/salt/runners/salt.py b/salt/runners/salt.py index 9ba670c3014..e8551adc282 100644 --- a/salt/runners/salt.py +++ b/salt/runners/salt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This runner makes Salt's execution modules available on the salt master. @@ -28,9 +28,10 @@ Execution modules are also available to salt runners: __salt__['salt.cmd'](fun=fun, args=args, kwargs=kwargs) -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging @@ -45,7 +46,7 @@ log = logging.getLogger(__name__) # pylint: disable=invalid-name def cmd(fun, *args, **kwargs): - ''' + """ .. versionchanged:: 2018.3.0 Added ``with_pillar`` argument @@ -78,45 +79,49 @@ def cmd(fun, *args, **kwargs): # call functions with arguments and keyword arguments salt-run salt.cmd test.arg 1 2 3 a=1 salt-run salt.cmd mymod.myfunc with_pillar=True - ''' - log.debug('Called salt.cmd runner with minion function %s', fun) + """ + log.debug("Called salt.cmd runner with minion function %s", fun) kwargs = salt.utils.args.clean_kwargs(**kwargs) - with_pillar = kwargs.pop('with_pillar', False) + with_pillar = kwargs.pop("with_pillar", False) opts = copy.deepcopy(__opts__) - opts['grains'] = salt.loader.grains(opts) + opts["grains"] = salt.loader.grains(opts) if with_pillar: - opts['pillar'] = salt.pillar.get_pillar( + opts["pillar"] = salt.pillar.get_pillar( opts, - opts['grains'], - opts['id'], - saltenv=opts['saltenv'], - pillarenv=opts.get('pillarenv')).compile_pillar() + opts["grains"], + opts["id"], + saltenv=opts["saltenv"], + pillarenv=opts.get("pillarenv"), + ).compile_pillar() else: - opts['pillar'] = {} + opts["pillar"] = {} functions = salt.loader.minion_mods( - opts, - utils=salt.loader.utils(opts), - context=__context__) + opts, utils=salt.loader.utils(opts), context=__context__ + ) - return functions[fun](*args, **kwargs) \ - if fun in functions \ - else '\'{0}\' is not available.'.format(fun) + return ( + functions[fun](*args, **kwargs) + if fun in functions + else "'{0}' is not available.".format(fun) + ) -def execute(tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - ret='', - jid='', - kwarg=None, - **kwargs): - ''' +def execute( + tgt, + fun, + arg=(), + timeout=None, + tgt_type="glob", + ret="", + jid="", + kwarg=None, + **kwargs +): + """ .. versionadded:: 2017.7.0 Execute ``fun`` on all minions matched by ``tgt`` and ``tgt_type``. @@ -146,20 +151,22 @@ def execute(tgt, tgt_type: nodegroup days: 1 returner: redis - ''' - client = salt.client.get_local_client(__opts__['conf_file']) + """ + client = salt.client.get_local_client(__opts__["conf_file"]) try: - ret = client.cmd(tgt, - fun, - arg=arg, - timeout=timeout or __opts__['timeout'], - tgt_type=tgt_type, # no warn_until, as this is introduced only in 2017.7.0 - ret=ret, - jid=jid, - kwarg=kwarg, - **kwargs) + ret = client.cmd( + tgt, + fun, + arg=arg, + timeout=timeout or __opts__["timeout"], + tgt_type=tgt_type, # no warn_until, as this is introduced only in 2017.7.0 + ret=ret, + jid=jid, + kwarg=kwarg, + **kwargs + ) except SaltClientError as client_error: - log.error('Error while executing %s on %s (%s)', fun, tgt, tgt_type) + log.error("Error while executing %s on %s (%s)", fun, tgt, tgt_type) log.error(client_error) return {} return ret diff --git a/salt/runners/saltutil.py b/salt/runners/saltutil.py index a2673988bb1..ae8c8188f0a 100644 --- a/salt/runners/saltutil.py +++ b/salt/runners/saltutil.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" The Saltutil runner is used to sync custom types to the Master. See the :mod:`saltutil module <salt.modules.saltutil>` for documentation on managing updates to minions. .. versionadded:: 2016.3.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -17,8 +17,8 @@ import salt.utils.extmods log = logging.getLogger(__name__) -def sync_all(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_all(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync all custom types saltenv : base @@ -37,39 +37,124 @@ def sync_all(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): salt-run saltutil.sync_all salt-run saltutil.sync_all extmod_whitelist={'runners': ['custom_runner'], 'grains': []} - ''' - log.debug('Syncing all') + """ + log.debug("Syncing all") ret = {} - ret['clouds'] = sync_clouds(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['modules'] = sync_modules(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['states'] = sync_states(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['grains'] = sync_grains(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['renderers'] = sync_renderers(saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist) - ret['returners'] = sync_returners(saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist) - ret['output'] = sync_output(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['proxymodules'] = sync_proxymodules(saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist) - ret['runners'] = sync_runners(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['wheel'] = sync_wheel(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['engines'] = sync_engines(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['thorium'] = sync_thorium(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['queues'] = sync_queues(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['pillar'] = sync_pillar(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['utils'] = sync_utils(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['sdb'] = sync_sdb(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['cache'] = sync_cache(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['fileserver'] = sync_fileserver(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['tops'] = sync_tops(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['tokens'] = sync_eauth_tokens(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['serializers'] = sync_serializers(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) - ret['executors'] = sync_executors(saltenv=saltenv, extmod_whitelist=extmod_whitelist, extmod_blacklist=extmod_blacklist) + ret["clouds"] = sync_clouds( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["modules"] = sync_modules( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["states"] = sync_states( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["grains"] = sync_grains( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["renderers"] = sync_renderers( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["returners"] = sync_returners( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["output"] = sync_output( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["proxymodules"] = sync_proxymodules( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["runners"] = sync_runners( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["wheel"] = sync_wheel( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["engines"] = sync_engines( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["thorium"] = sync_thorium( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["queues"] = sync_queues( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["pillar"] = sync_pillar( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["utils"] = sync_utils( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["sdb"] = sync_sdb( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["cache"] = sync_cache( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["fileserver"] = sync_fileserver( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["tops"] = sync_tops( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["tokens"] = sync_eauth_tokens( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["serializers"] = sync_serializers( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) + ret["executors"] = sync_executors( + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + ) return ret -def sync_modules(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_modules(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync execution modules from ``salt://_modules`` to the master saltenv : base @@ -87,13 +172,18 @@ def sync_modules(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_modules - ''' - return salt.utils.extmods.sync(__opts__, 'modules', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "modules", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_states(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_states(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync state modules from ``salt://_states`` to the master saltenv : base @@ -111,13 +201,18 @@ def sync_states(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_states - ''' - return salt.utils.extmods.sync(__opts__, 'states', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "states", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_grains(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_grains(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync grains modules from ``salt://_grains`` to the master saltenv : base @@ -135,13 +230,18 @@ def sync_grains(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_grains - ''' - return salt.utils.extmods.sync(__opts__, 'grains', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "grains", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_renderers(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_renderers(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync renderer modules from from ``salt://_renderers`` to the master saltenv : base @@ -159,13 +259,18 @@ def sync_renderers(saltenv='base', extmod_whitelist=None, extmod_blacklist=None) .. code-block:: bash salt-run saltutil.sync_renderers - ''' - return salt.utils.extmods.sync(__opts__, 'renderers', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "renderers", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_returners(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_returners(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync returner modules from ``salt://_returners`` to the master saltenv : base @@ -183,13 +288,18 @@ def sync_returners(saltenv='base', extmod_whitelist=None, extmod_blacklist=None) .. code-block:: bash salt-run saltutil.sync_returners - ''' - return salt.utils.extmods.sync(__opts__, 'returners', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "returners", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_output(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_output(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync output modules from ``salt://_output`` to the master saltenv : base @@ -207,13 +317,18 @@ def sync_output(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_output - ''' - return salt.utils.extmods.sync(__opts__, 'output', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "output", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_proxymodules(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_proxymodules(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync proxy modules from ``salt://_proxy`` to the master saltenv : base @@ -231,13 +346,18 @@ def sync_proxymodules(saltenv='base', extmod_whitelist=None, extmod_blacklist=No .. code-block:: bash salt-run saltutil.sync_proxymodules - ''' - return salt.utils.extmods.sync(__opts__, 'proxy', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "proxy", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_runners(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_runners(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync runners from ``salt://_runners`` to the master saltenv : base @@ -255,13 +375,18 @@ def sync_runners(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_runners - ''' - return salt.utils.extmods.sync(__opts__, 'runners', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "runners", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_wheel(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_wheel(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync wheel modules from ``salt://_wheel`` to the master saltenv : base @@ -279,13 +404,18 @@ def sync_wheel(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_wheel - ''' - return salt.utils.extmods.sync(__opts__, 'wheel', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "wheel", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_engines(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_engines(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync engines from ``salt://_engines`` to the master saltenv : base @@ -303,13 +433,18 @@ def sync_engines(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_engines - ''' - return salt.utils.extmods.sync(__opts__, 'engines', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "engines", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_thorium(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_thorium(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2018.3.0 Sync Thorium from ``salt://_thorium`` to the master @@ -329,13 +464,18 @@ def sync_thorium(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_thorium - ''' - return salt.utils.extmods.sync(__opts__, 'thorium', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "thorium", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_queues(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_queues(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync queue modules from ``salt://_queues`` to the master saltenv : base @@ -353,13 +493,18 @@ def sync_queues(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_queues - ''' - return salt.utils.extmods.sync(__opts__, 'queues', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "queues", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_pillar(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_pillar(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ Sync pillar modules from ``salt://_pillar`` to the master saltenv : base @@ -377,13 +522,18 @@ def sync_pillar(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_pillar - ''' - return salt.utils.extmods.sync(__opts__, 'pillar', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "pillar", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_utils(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_utils(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2016.11.0 Sync utils modules from ``salt://_utils`` to the master @@ -403,13 +553,18 @@ def sync_utils(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_utils - ''' - return salt.utils.extmods.sync(__opts__, 'utils', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "utils", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_sdb(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_sdb(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2017.7.0 Sync sdb modules from ``salt://_sdb`` to the master @@ -429,13 +584,18 @@ def sync_sdb(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_sdb - ''' - return salt.utils.extmods.sync(__opts__, 'sdb', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "sdb", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_tops(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_tops(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2016.3.7,2016.11.4,2017.7.0 Sync master_tops modules from ``salt://_tops`` to the master @@ -449,13 +609,18 @@ def sync_tops(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_tops - ''' - return salt.utils.extmods.sync(__opts__, 'tops', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "tops", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_cache(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_cache(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2017.7.0 Sync cache modules from ``salt://_cache`` to the master @@ -475,13 +640,18 @@ def sync_cache(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_cache - ''' - return salt.utils.extmods.sync(__opts__, 'cache', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "cache", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_fileserver(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_fileserver(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2018.3.0 Sync fileserver modules from ``salt://_fileserver`` to the master @@ -501,13 +671,18 @@ def sync_fileserver(saltenv='base', extmod_whitelist=None, extmod_blacklist=None .. code-block:: bash salt-run saltutil.sync_fileserver - ''' - return salt.utils.extmods.sync(__opts__, 'fileserver', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "fileserver", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_clouds(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_clouds(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2017.7.0 Sync cloud modules from ``salt://_clouds`` to the master @@ -527,13 +702,18 @@ def sync_clouds(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_clouds - ''' - return salt.utils.extmods.sync(__opts__, 'clouds', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "clouds", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_roster(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_roster(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2017.7.0 Sync roster modules from ``salt://_roster`` to the master @@ -553,13 +733,18 @@ def sync_roster(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): .. code-block:: bash salt-run saltutil.sync_roster - ''' - return salt.utils.extmods.sync(__opts__, 'roster', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "roster", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_eauth_tokens(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_eauth_tokens(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2018.3.0 Sync eauth token modules from ``salt://_tokens`` to the master @@ -579,13 +764,18 @@ def sync_eauth_tokens(saltenv='base', extmod_whitelist=None, extmod_blacklist=No .. code-block:: bash salt-run saltutil.sync_eauth_tokens - ''' - return salt.utils.extmods.sync(__opts__, 'tokens', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "tokens", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_serializers(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_serializers(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 2019.2.0 Sync serializer modules from ``salt://_serializers`` to the master @@ -605,13 +795,18 @@ def sync_serializers(saltenv='base', extmod_whitelist=None, extmod_blacklist=Non .. code-block:: bash salt-run saltutil.sync_utils - ''' - return salt.utils.extmods.sync(__opts__, 'serializers', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "serializers", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] -def sync_executors(saltenv='base', extmod_whitelist=None, extmod_blacklist=None): - ''' +def sync_executors(saltenv="base", extmod_whitelist=None, extmod_blacklist=None): + """ .. versionadded:: 3000 Sync executor modules from ``salt://_executors`` to the master @@ -631,6 +826,11 @@ def sync_executors(saltenv='base', extmod_whitelist=None, extmod_blacklist=None) .. code-block:: bash salt-run saltutil.sync_executors - ''' - return salt.utils.extmods.sync(__opts__, 'executors', saltenv=saltenv, extmod_whitelist=extmod_whitelist, - extmod_blacklist=extmod_blacklist)[0] + """ + return salt.utils.extmods.sync( + __opts__, + "executors", + saltenv=saltenv, + extmod_whitelist=extmod_whitelist, + extmod_blacklist=extmod_blacklist, + )[0] diff --git a/salt/runners/sdb.py b/salt/runners/sdb.py index 3a2a63e6949..06d94172e40 100644 --- a/salt/runners/sdb.py +++ b/salt/runners/sdb.py @@ -1,20 +1,19 @@ # coding: utf-8 -''' +""" Runner for setting and querying data via the sdb API on the master -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.utils.sdb - __func_alias__ = { - 'set_': 'set', + "set_": "set", } def get(uri): - ''' + """ Get a value from a db, using a uri in the form of sdb://<profile>/<key>. If the uri provided does not start with sdb://, then it will be returned as-is. @@ -23,12 +22,12 @@ def get(uri): .. code-block:: bash salt-run sdb.get sdb://mymemcached/foo - ''' + """ return salt.utils.sdb.sdb_get(uri, __opts__, __utils__) def set_(uri, value): - ''' + """ Set a value in a db, using a uri in the form of ``sdb://<profile>/<key>``. If the uri provided does not start with ``sdb://`` or the value is not successfully set, return ``False``. @@ -38,12 +37,12 @@ def set_(uri, value): .. code-block:: bash salt-run sdb.set sdb://mymemcached/foo bar - ''' + """ return salt.utils.sdb.sdb_set(uri, value, __opts__, __utils__) def delete(uri): - ''' + """ Delete a value from a db, using a uri in the form of ``sdb://<profile>/<key>``. If the uri provided does not start with ``sdb://`` or the value is not successfully deleted, return ``False``. @@ -53,14 +52,14 @@ def delete(uri): .. code-block:: bash salt-run sdb.delete sdb://mymemcached/foo - ''' + """ return salt.utils.sdb.sdb_delete(uri, __opts__, __utils__) -def get_or_set_hash(uri, - length=8, - chars='abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'): - ''' +def get_or_set_hash( + uri, length=8, chars="abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)" +): + """ Perform a one-time generation of a hash and write it to sdb. If that value has already been set return the value instead. @@ -79,5 +78,5 @@ def get_or_set_hash(uri, as directives by the YAML parser, such as strings beginning with ``%``. To avoid issues when using the output of this function in an SLS file containing YAML+Jinja, surround the call with single quotes. - ''' + """ return salt.utils.sdb.sdb_get_or_set_hash(uri, __opts__, length, chars, __utils__) diff --git a/salt/runners/smartos_vmadm.py b/salt/runners/smartos_vmadm.py index e7091d9f16d..fd66677dc3f 100644 --- a/salt/runners/smartos_vmadm.py +++ b/salt/runners/smartos_vmadm.py @@ -1,31 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Runner for SmartOS minions control vmadm -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.client from salt.exceptions import SaltClientError -from salt.utils.odict import OrderedDict # Import 3rd party libs from salt.ext import six +from salt.utils.odict import OrderedDict # Function aliases -__func_alias__ = { - 'list_vms': 'list' -} +__func_alias__ = {"list_vms": "list"} # Define the module's virtual name -__virtualname__ = 'vmadm' +__virtualname__ = "vmadm" def __virtual__(): - ''' + """ Provides vmadm runner - ''' + """ # NOTE: always load vmadm runner # we could check using test.ping + a grain # match, but doing this on master startup is @@ -33,56 +31,61 @@ def __virtual__(): return __virtualname__ -def _action(action='get', search=None, one=True, force=False): - ''' +def _action(action="get", search=None, one=True, force=False): + """ Multi action helper for start, stop, get, ... - ''' + """ vms = {} matched_vms = [] - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) ## lookup vms try: vmadm_args = {} - vmadm_args['order'] = 'uuid,alias,hostname,state' - if '=' in search: - vmadm_args['search'] = search - for cn in client.cmd_iter('G@virtual:physical and G@os:smartos', - 'vmadm.list', kwarg=vmadm_args, - tgt_type='compound'): + vmadm_args["order"] = "uuid,alias,hostname,state" + if "=" in search: + vmadm_args["search"] = search + for cn in client.cmd_iter( + "G@virtual:physical and G@os:smartos", + "vmadm.list", + kwarg=vmadm_args, + tgt_type="compound", + ): if not cn: continue node = next(six.iterkeys(cn)) - if not isinstance(cn[node], dict) or \ - 'ret' not in cn[node] or \ - not isinstance(cn[node]['ret'], dict): + if ( + not isinstance(cn[node], dict) + or "ret" not in cn[node] + or not isinstance(cn[node]["ret"], dict) + ): continue - for vm in cn[node]['ret']: - vmcfg = cn[node]['ret'][vm] - vmcfg['node'] = node + for vm in cn[node]["ret"]: + vmcfg = cn[node]["ret"][vm] + vmcfg["node"] = node vms[vm] = vmcfg except SaltClientError as client_error: pass ## check if we have vms if len(vms) == 0: - return {'Error': 'No vms found.'} + return {"Error": "No vms found."} ## simple search - if '=' not in search: + if "=" not in search: loop_pass = 0 while loop_pass < 3: ## each pass will try a different field if loop_pass == 0: - field = 'uuid' + field = "uuid" elif loop_pass == 1: - field = 'hostname' + field = "hostname" else: - field = 'alias' + field = "alias" ## loop vms and try to match for vm in vms: - if field == 'uuid' and vm == search: + if field == "uuid" and vm == search: matched_vms.append(vm) break # exit for on uuid match (max = 1) elif field in vms[vm] and vms[vm][field] == search: @@ -99,42 +102,41 @@ def _action(action='get', search=None, one=True, force=False): ## check if we have vms if len(matched_vms) == 0: - return {'Error': 'No vms matched.'} + return {"Error": "No vms matched."} ## multiple allowed? if one and len(matched_vms) > 1: return { - 'Error': 'Matched {0} vms, only one allowed!'.format(len(matched_vms)), - 'Matches': matched_vms + "Error": "Matched {0} vms, only one allowed!".format(len(matched_vms)), + "Matches": matched_vms, } ## perform action ret = {} - if action in ['start', 'stop', 'reboot', 'get']: + if action in ["start", "stop", "reboot", "get"]: for vm in matched_vms: - vmadm_args = { - 'key': 'uuid', - 'vm': vm - } + vmadm_args = {"key": "uuid", "vm": vm} try: - for vmadm_res in client.cmd_iter(vms[vm]['node'], 'vmadm.{0}'.format(action), kwarg=vmadm_args): + for vmadm_res in client.cmd_iter( + vms[vm]["node"], "vmadm.{0}".format(action), kwarg=vmadm_args + ): if not vmadm_res: continue - if vms[vm]['node'] in vmadm_res: - ret[vm] = vmadm_res[vms[vm]['node']]['ret'] + if vms[vm]["node"] in vmadm_res: + ret[vm] = vmadm_res[vms[vm]["node"]]["ret"] except SaltClientError as client_error: ret[vm] = False - elif action in ['is_running']: + elif action in ["is_running"]: ret = True for vm in matched_vms: - if vms[vm]['state'] != 'running': + if vms[vm]["state"] != "running": ret = False break return ret def nodes(verbose=False): - ''' + """ List all compute nodes verbose : boolean @@ -147,36 +149,47 @@ def nodes(verbose=False): salt-run vmadm.nodes salt-run vmadm.nodes verbose=True - ''' + """ ret = {} if verbose else [] - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) ## get list of nodes try: - for cn in client.cmd_iter('G@virtual:physical and G@os:smartos', - 'grains.items', tgt_type='compound'): + for cn in client.cmd_iter( + "G@virtual:physical and G@os:smartos", "grains.items", tgt_type="compound" + ): if not cn: continue node = next(six.iterkeys(cn)) - if not isinstance(cn[node], dict) or \ - 'ret' not in cn[node] or \ - not isinstance(cn[node]['ret'], dict): + if ( + not isinstance(cn[node], dict) + or "ret" not in cn[node] + or not isinstance(cn[node]["ret"], dict) + ): continue if verbose: ret[node] = {} - ret[node]['version'] = {} - ret[node]['version']['platform'] = cn[node]['ret']['osrelease'] - if 'computenode_sdc_version' in cn[node]['ret']: - ret[node]['version']['sdc'] = cn[node]['ret']['computenode_sdc_version'] - ret[node]['vms'] = {} - if 'computenode_vm_capable' in cn[node]['ret'] and \ - cn[node]['ret']['computenode_vm_capable'] and \ - 'computenode_vm_hw_virt' in cn[node]['ret']: - ret[node]['vms']['hw_cap'] = cn[node]['ret']['computenode_vm_hw_virt'] + ret[node]["version"] = {} + ret[node]["version"]["platform"] = cn[node]["ret"]["osrelease"] + if "computenode_sdc_version" in cn[node]["ret"]: + ret[node]["version"]["sdc"] = cn[node]["ret"][ + "computenode_sdc_version" + ] + ret[node]["vms"] = {} + if ( + "computenode_vm_capable" in cn[node]["ret"] + and cn[node]["ret"]["computenode_vm_capable"] + and "computenode_vm_hw_virt" in cn[node]["ret"] + ): + ret[node]["vms"]["hw_cap"] = cn[node]["ret"][ + "computenode_vm_hw_virt" + ] else: - ret[node]['vms']['hw_cap'] = False - if 'computenode_vms_running' in cn[node]['ret']: - ret[node]['vms']['running'] = cn[node]['ret']['computenode_vms_running'] + ret[node]["vms"]["hw_cap"] = False + if "computenode_vms_running" in cn[node]["ret"]: + ret[node]["vms"]["running"] = cn[node]["ret"][ + "computenode_vms_running" + ] else: ret.append(node) except SaltClientError as client_error: @@ -188,7 +201,7 @@ def nodes(verbose=False): def list_vms(search=None, verbose=False): - ''' + """ List all vms search : string @@ -203,39 +216,48 @@ def list_vms(search=None, verbose=False): salt-run vmadm.list salt-run vmadm.list search='type=KVM' salt-run vmadm.list verbose=True - ''' + """ ret = OrderedDict() if verbose else [] - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) try: vmadm_args = {} - vmadm_args['order'] = 'uuid,alias,hostname,state,type,cpu_cap,vcpus,ram' + vmadm_args["order"] = "uuid,alias,hostname,state,type,cpu_cap,vcpus,ram" if search: - vmadm_args['search'] = search - for cn in client.cmd_iter('G@virtual:physical and G@os:smartos', - 'vmadm.list', kwarg=vmadm_args, - tgt_type='compound'): + vmadm_args["search"] = search + for cn in client.cmd_iter( + "G@virtual:physical and G@os:smartos", + "vmadm.list", + kwarg=vmadm_args, + tgt_type="compound", + ): if not cn: continue node = next(six.iterkeys(cn)) - if not isinstance(cn[node], dict) or \ - 'ret' not in cn[node] or \ - not isinstance(cn[node]['ret'], dict): + if ( + not isinstance(cn[node], dict) + or "ret" not in cn[node] + or not isinstance(cn[node]["ret"], dict) + ): continue - for vm in cn[node]['ret']: - vmcfg = cn[node]['ret'][vm] + for vm in cn[node]["ret"]: + vmcfg = cn[node]["ret"][vm] if verbose: ret[vm] = OrderedDict() - ret[vm]['hostname'] = vmcfg['hostname'] - ret[vm]['alias'] = vmcfg['alias'] - ret[vm]['computenode'] = node - ret[vm]['state'] = vmcfg['state'] - ret[vm]['resources'] = OrderedDict() - ret[vm]['resources']['memory'] = vmcfg['ram'] - if vmcfg['type'] == 'KVM': - ret[vm]['resources']['cpu'] = "{0:.2f}".format(int(vmcfg['vcpus'])) + ret[vm]["hostname"] = vmcfg["hostname"] + ret[vm]["alias"] = vmcfg["alias"] + ret[vm]["computenode"] = node + ret[vm]["state"] = vmcfg["state"] + ret[vm]["resources"] = OrderedDict() + ret[vm]["resources"]["memory"] = vmcfg["ram"] + if vmcfg["type"] == "KVM": + ret[vm]["resources"]["cpu"] = "{0:.2f}".format( + int(vmcfg["vcpus"]) + ) else: - if vmcfg['cpu_cap'] != '': - ret[vm]['resources']['cpu'] = "{0:.2f}".format(int(vmcfg['cpu_cap'])/100) + if vmcfg["cpu_cap"] != "": + ret[vm]["resources"]["cpu"] = "{0:.2f}".format( + int(vmcfg["cpu_cap"]) / 100 + ) else: ret.append(vm) except SaltClientError as client_error: @@ -248,7 +270,7 @@ def list_vms(search=None, verbose=False): def start(search, one=True): - ''' + """ Start one or more vms search : string @@ -267,12 +289,12 @@ def start(search, one=True): salt-run vmadm.start 91244bba-1146-e4ec-c07e-e825e0223aa9 salt-run vmadm.start search='alias=jiska' salt-run vmadm.start search='type=KVM' one=False - ''' - return _action('start', search, one) + """ + return _action("start", search, one) def stop(search, one=True): - ''' + """ Stop one or more vms search : string @@ -291,12 +313,12 @@ def stop(search, one=True): salt-run vmadm.stop 91244bba-1146-e4ec-c07e-e825e0223aa9 salt-run vmadm.stop search='alias=jody' salt-run vmadm.stop search='type=KVM' one=False - ''' - return _action('stop', search, one) + """ + return _action("stop", search, one) def reboot(search, one=True, force=False): - ''' + """ Reboot one or more vms search : string @@ -317,12 +339,12 @@ def reboot(search, one=True, force=False): salt-run vmadm.reboot 91244bba-1146-e4ec-c07e-e825e0223aa9 salt-run vmadm.reboot search='alias=marije' salt-run vmadm.reboot search='type=KVM' one=False - ''' - return _action('reboot', search, one, force) + """ + return _action("reboot", search, one, force) def get(search, one=True): - ''' + """ Return information for vms search : string @@ -340,12 +362,12 @@ def get(search, one=True): salt-run vmadm.get 91244bba-1146-e4ec-c07e-e825e0223aa9 salt-run vmadm.get search='alias=saskia' - ''' - return _action('get', search, one) + """ + return _action("get", search, one) def is_running(search): - ''' + """ Return true if vm is running search : string @@ -364,8 +386,8 @@ def is_running(search): salt-run vmadm.is_running 91244bba-1146-e4ec-c07e-e825e0223aa9 salt-run vmadm.is_running search='alias=julia' - ''' - return _action('is_running', search, False) + """ + return _action("is_running", search, False) # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/runners/spacewalk.py b/salt/runners/spacewalk.py index 07ca9bd7110..4fa8562b3d4 100644 --- a/salt/runners/spacewalk.py +++ b/salt/runners/spacewalk.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Spacewalk Runner ================ @@ -28,7 +28,7 @@ master configuration at ``/etc/salt/master`` or ``/etc/salt/master.d/spacewalk.c Optionally, ``protocol`` can be specified if the spacewalk server is not using the defaults. Default is ``protocol: https``. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -44,52 +44,54 @@ _sessions = {} def __virtual__(): - ''' + """ Check for spacewalk configuration in master config file or directory and load runner only if it is specified - ''' + """ if not _get_spacewalk_configuration(): - return False, 'No spacewalk configuration found' + return False, "No spacewalk configuration found" return True -def _get_spacewalk_configuration(spacewalk_url=''): - ''' +def _get_spacewalk_configuration(spacewalk_url=""): + """ Return the configuration read from the master configuration file or directory - ''' - spacewalk_config = __opts__['spacewalk'] if 'spacewalk' in __opts__ else None + """ + spacewalk_config = __opts__["spacewalk"] if "spacewalk" in __opts__ else None if spacewalk_config: try: for spacewalk_server, service_config in six.iteritems(spacewalk_config): - username = service_config.get('username', None) - password = service_config.get('password', None) - protocol = service_config.get('protocol', 'https') + username = service_config.get("username", None) + password = service_config.get("password", None) + protocol = service_config.get("protocol", "https") if not username or not password: log.error( - 'Username or Password has not been specified in the master ' - 'configuration for %s', spacewalk_server + "Username or Password has not been specified in the master " + "configuration for %s", + spacewalk_server, ) return False ret = { - 'api_url': '{0}://{1}/rpc/api'.format(protocol, spacewalk_server), - 'username': username, - 'password': password + "api_url": "{0}://{1}/rpc/api".format(protocol, spacewalk_server), + "username": username, + "password": password, } if (not spacewalk_url) or (spacewalk_url == spacewalk_server): return ret except Exception as exc: # pylint: disable=broad-except - log.error('Exception encountered: %s', exc) + log.error("Exception encountered: %s", exc) return False if spacewalk_url: log.error( - 'Configuration for %s has not been specified in the master ' - 'configuration', spacewalk_url + "Configuration for %s has not been specified in the master " + "configuration", + spacewalk_url, ) return False @@ -97,46 +99,50 @@ def _get_spacewalk_configuration(spacewalk_url=''): def _get_client_and_key(url, user, password, verbose=0): - ''' + """ Return the client object and session key for the client - ''' + """ session = {} - session['client'] = six.moves.xmlrpc_client.Server(url, verbose=verbose, use_datetime=True) - session['key'] = session['client'].auth.login(user, password) + session["client"] = six.moves.xmlrpc_client.Server( + url, verbose=verbose, use_datetime=True + ) + session["key"] = session["client"].auth.login(user, password) return session def _disconnect_session(session): - ''' + """ Disconnect API connection - ''' - session['client'].auth.logout(session['key']) + """ + session["client"].auth.logout(session["key"]) def _get_session(server): - ''' + """ Get session and key - ''' + """ if server in _sessions: return _sessions[server] config = _get_spacewalk_configuration(server) if not config: - raise Exception('No config for \'{0}\' found on master'.format(server)) + raise Exception("No config for '{0}' found on master".format(server)) - session = _get_client_and_key(config['api_url'], config['username'], config['password']) + session = _get_client_and_key( + config["api_url"], config["username"], config["password"] + ) atexit.register(_disconnect_session, session) - client = session['client'] - key = session['key'] + client = session["client"] + key = session["key"] _sessions[server] = (client, key) return client, key def api(server, command, *args, **kwargs): - ''' + """ Call the Spacewalk xmlrpc api. CLI Example: @@ -158,33 +164,35 @@ def api(server, command, *args, **kwargs): - arguments: - MyGroup - Description - ''' - if 'arguments' in kwargs: - arguments = kwargs['arguments'] + """ + if "arguments" in kwargs: + arguments = kwargs["arguments"] else: arguments = args - call = '{0} {1}'.format(command, arguments) + call = "{0} {1}".format(command, arguments) try: client, key = _get_session(server) except Exception as exc: # pylint: disable=broad-except - err_msg = 'Exception raised when connecting to spacewalk server ({0}): {1}'.format(server, exc) + err_msg = "Exception raised when connecting to spacewalk server ({0}): {1}".format( + server, exc + ) log.error(err_msg) return {call: err_msg} - namespace, method = command.split('.') + namespace, method = command.split(".") endpoint = getattr(getattr(client, namespace), method) try: output = endpoint(key, *arguments) except Exception as e: # pylint: disable=broad-except - output = 'API call failed: {0}'.format(e) + output = "API call failed: {0}".format(e) return {call: output} def addGroupsToKey(server, activation_key, groups): - ''' + """ Add server groups to a activation key CLI Example: @@ -192,38 +200,42 @@ def addGroupsToKey(server, activation_key, groups): .. code-block:: bash salt-run spacewalk.addGroupsToKey spacewalk01.domain.com 1-my-key '[group1, group2]' - ''' + """ try: client, key = _get_session(server) except Exception as exc: # pylint: disable=broad-except - err_msg = 'Exception raised when connecting to spacewalk server ({0}): {1}'.format(server, exc) + err_msg = "Exception raised when connecting to spacewalk server ({0}): {1}".format( + server, exc + ) log.error(err_msg) - return {'Error': err_msg} + return {"Error": err_msg} all_groups = client.systemgroup.listAllGroups(key) groupIds = [] for group in all_groups: - if group['name'] in groups: - groupIds.append(group['id']) + if group["name"] in groups: + groupIds.append(group["id"]) if client.activationkey.addServerGroups(key, activation_key, groupIds) == 1: return {activation_key: groups} else: - return {activation_key: 'Failed to add groups to activation key'} + return {activation_key: "Failed to add groups to activation key"} def deleteAllGroups(server): - ''' + """ Delete all server groups from Spacewalk - ''' + """ try: client, key = _get_session(server) except Exception as exc: # pylint: disable=broad-except - err_msg = 'Exception raised when connecting to spacewalk server ({0}): {1}'.format(server, exc) + err_msg = "Exception raised when connecting to spacewalk server ({0}): {1}".format( + server, exc + ) log.error(err_msg) - return {'Error': err_msg} + return {"Error": err_msg} groups = client.systemgroup.listAllGroups(key) @@ -231,19 +243,19 @@ def deleteAllGroups(server): failed_groups = [] for group in groups: - if client.systemgroup.delete(key, group['name']) == 1: - deleted_groups.append(group['name']) + if client.systemgroup.delete(key, group["name"]) == 1: + deleted_groups.append(group["name"]) else: - failed_groups.append(group['name']) + failed_groups.append(group["name"]) - ret = {'deleted': deleted_groups} + ret = {"deleted": deleted_groups} if failed_groups: - ret['failed'] = failed_groups + ret["failed"] = failed_groups return ret def deleteAllSystems(server): - ''' + """ Delete all systems from Spacewalk CLI Example: @@ -251,31 +263,33 @@ def deleteAllSystems(server): .. code-block:: bash salt-run spacewalk.deleteAllSystems spacewalk01.domain.com - ''' + """ try: client, key = _get_session(server) except Exception as exc: # pylint: disable=broad-except - err_msg = 'Exception raised when connecting to spacewalk server ({0}): {1}'.format(server, exc) + err_msg = "Exception raised when connecting to spacewalk server ({0}): {1}".format( + server, exc + ) log.error(err_msg) - return {'Error': err_msg} + return {"Error": err_msg} systems = client.system.listSystems(key) ids = [] names = [] for system in systems: - ids.append(system['id']) - names.append(system['name']) + ids.append(system["id"]) + names.append(system["name"]) if client.system.deleteSystems(key, ids) == 1: - return {'deleted': names} + return {"deleted": names} else: - return {'Error': 'Failed to delete all systems'} + return {"Error": "Failed to delete all systems"} def deleteAllActivationKeys(server): - ''' + """ Delete all activation keys from Spacewalk CLI Example: @@ -283,14 +297,16 @@ def deleteAllActivationKeys(server): .. code-block:: bash salt-run spacewalk.deleteAllActivationKeys spacewalk01.domain.com - ''' + """ try: client, key = _get_session(server) except Exception as exc: # pylint: disable=broad-except - err_msg = 'Exception raised when connecting to spacewalk server ({0}): {1}'.format(server, exc) + err_msg = "Exception raised when connecting to spacewalk server ({0}): {1}".format( + server, exc + ) log.error(err_msg) - return {'Error': err_msg} + return {"Error": err_msg} activation_keys = client.activationkey.listActivationKeys(key) @@ -298,19 +314,19 @@ def deleteAllActivationKeys(server): failed_keys = [] for aKey in activation_keys: - if client.activationkey.delete(key, aKey['key']) == 1: - deleted_keys.append(aKey['key']) + if client.activationkey.delete(key, aKey["key"]) == 1: + deleted_keys.append(aKey["key"]) else: - failed_keys.append(aKey['key']) + failed_keys.append(aKey["key"]) - ret = {'deleted': deleted_keys} + ret = {"deleted": deleted_keys} if failed_keys: - ret['failed'] = failed_keys + ret["failed"] = failed_keys return ret def unregister(name, server_url): - ''' + """ Unregister specified server from Spacewalk CLI Example: @@ -318,12 +334,14 @@ def unregister(name, server_url): .. code-block:: bash salt-run spacewalk.unregister my-test-vm spacewalk01.domain.com - ''' + """ try: client, key = _get_session(server_url) except Exception as exc: # pylint: disable=broad-except - err_msg = 'Exception raised when connecting to spacewalk server ({0}): {1}'.format(server_url, exc) + err_msg = "Exception raised when connecting to spacewalk server ({0}): {1}".format( + server_url, exc + ) log.error(err_msg) return {name: err_msg} @@ -331,10 +349,12 @@ def unregister(name, server_url): if systems_list: for system in systems_list: - out = client.system.deleteSystem(key, system['id']) + out = client.system.deleteSystem(key, system["id"]) if out == 1: - return {name: 'Successfully unregistered from {0}'.format(server_url)} + return {name: "Successfully unregistered from {0}".format(server_url)} else: - return {name: 'Failed to unregister from {0}'.format(server_url)} + return {name: "Failed to unregister from {0}".format(server_url)} else: - return {name: 'System does not exist in spacewalk server ({0})'.format(server_url)} + return { + name: "System does not exist in spacewalk server ({0})".format(server_url) + } diff --git a/salt/runners/ssh.py b/salt/runners/ssh.py index 482f6c67690..0a6ab3f2a03 100644 --- a/salt/runners/ssh.py +++ b/salt/runners/ssh.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" A Runner module interface on top of the salt-ssh Python API. This allows for programmatic use from salt-api, the Reactor, Orchestrate, etc. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -12,13 +12,8 @@ from __future__ import absolute_import, print_function, unicode_literals import salt.client.ssh.client -def cmd(tgt, - fun, - arg=(), - timeout=None, - tgt_type='glob', - kwarg=None): - ''' +def cmd(tgt, fun, arg=(), timeout=None, tgt_type="glob", kwarg=None): + """ .. versionadded:: 2015.5.0 .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier @@ -29,12 +24,6 @@ def cmd(tgt, A wrapper around the :py:meth:`SSHClient.cmd <salt.client.ssh.client.SSHClient.cmd>` method. - ''' + """ client = salt.client.ssh.client.SSHClient(mopts=__opts__) - return client.cmd( - tgt, - fun, - arg, - timeout, - tgt_type, - kwarg) + return client.cmd(tgt, fun, arg, timeout, tgt_type, kwarg) diff --git a/salt/runners/state.py b/salt/runners/state.py index cc3eb50f1ba..2ff740cb78d 100644 --- a/salt/runners/state.py +++ b/salt/runners/state.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Execute orchestration functions -''' +""" # Import pytohn libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -17,50 +18,52 @@ LOGGER = logging.getLogger(__name__) def pause(jid, state_id=None, duration=None): - ''' + """ Set up a state id pause, this instructs a running state to pause at a given state id. This needs to pass in the jid of the running state and can optionally pass in a duration in seconds. - ''' + """ minion = salt.minion.MasterMinion(__opts__) - minion.functions['state.pause'](jid, state_id, duration) + minion.functions["state.pause"](jid, state_id, duration) -set_pause = salt.utils.functools.alias_function(pause, 'set_pause') +set_pause = salt.utils.functools.alias_function(pause, "set_pause") def resume(jid, state_id=None): - ''' + """ Remove a pause from a jid, allowing it to continue - ''' + """ minion = salt.minion.MasterMinion(__opts__) - minion.functions['state.resume'](jid, state_id) + minion.functions["state.resume"](jid, state_id) -rm_pause = salt.utils.functools.alias_function(resume, 'rm_pause') +rm_pause = salt.utils.functools.alias_function(resume, "rm_pause") def soft_kill(jid, state_id=None): - ''' + """ Set up a state run to die before executing the given state id, this instructs a running state to safely exit at a given state id. This needs to pass in the jid of the running state. If a state_id is not passed then the jid referenced will be safely exited at the beginning of the next state run. - ''' + """ minion = salt.minion.MasterMinion(__opts__) - minion.functions['state.soft_kill'](jid, state_id) + minion.functions["state.soft_kill"](jid, state_id) -def orchestrate(mods, - saltenv='base', - test=None, - exclude=None, - pillar=None, - pillarenv=None, - pillar_enc=None, - orchestration_jid=None): - ''' +def orchestrate( + mods, + saltenv="base", + test=None, + exclude=None, + pillar=None, + pillarenv=None, + pillar_enc=None, + orchestration_jid=None, +): + """ .. versionadded:: 0.17.0 Execute a state run from the master, used as a powerful orchestration @@ -101,47 +104,46 @@ def orchestrate(mods, salt-run state.orchestrate webserver pillar_enc=gpg pillar="$(cat somefile.json)" - ''' + """ if pillar is not None and not isinstance(pillar, dict): - raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary' - ) - __opts__['file_client'] = 'local' + raise SaltInvocationError("Pillar data must be formatted as a dictionary") + __opts__["file_client"] = "local" minion = salt.minion.MasterMinion(__opts__) - if pillarenv is None and 'pillarenv' in __opts__: - pillarenv = __opts__['pillarenv'] - if saltenv is None and 'saltenv' in __opts__: - saltenv = __opts__['saltenv'] + if pillarenv is None and "pillarenv" in __opts__: + pillarenv = __opts__["pillarenv"] + if saltenv is None and "saltenv" in __opts__: + saltenv = __opts__["saltenv"] if orchestration_jid is None: orchestration_jid = salt.utils.jid.gen_jid(__opts__) - running = minion.functions['state.sls']( - mods, - test, - exclude, - pillar=pillar, - saltenv=saltenv, - pillarenv=pillarenv, - pillar_enc=pillar_enc, - __pub_jid=orchestration_jid, - orchestration_jid=orchestration_jid) - ret = {'data': {minion.opts['id']: running}, 'outputter': 'highstate'} - res = __utils__['state.check_result'](ret['data']) + running = minion.functions["state.sls"]( + mods, + test, + exclude, + pillar=pillar, + saltenv=saltenv, + pillarenv=pillarenv, + pillar_enc=pillar_enc, + __pub_jid=orchestration_jid, + orchestration_jid=orchestration_jid, + ) + ret = {"data": {minion.opts["id"]: running}, "outputter": "highstate"} + res = __utils__["state.check_result"](ret["data"]) if res: - ret['retcode'] = 0 + ret["retcode"] = 0 else: - ret['retcode'] = 1 + ret["retcode"] = 1 return ret # Aliases for orchestrate runner -orch = salt.utils.functools.alias_function(orchestrate, 'orch') -sls = salt.utils.functools.alias_function(orchestrate, 'sls') +orch = salt.utils.functools.alias_function(orchestrate, "orch") +sls = salt.utils.functools.alias_function(orchestrate, "sls") def orchestrate_single(fun, name, test=None, queue=False, pillar=None, **kwargs): - ''' + """ Execute a single state orchestration routine .. versionadded:: 2015.5.0 @@ -151,27 +153,21 @@ def orchestrate_single(fun, name, test=None, queue=False, pillar=None, **kwargs) .. code-block:: bash salt-run state.orchestrate_single fun=salt.wheel name=key.list_all - ''' + """ if pillar is not None and not isinstance(pillar, dict): - raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary' - ) - __opts__['file_client'] = 'local' + raise SaltInvocationError("Pillar data must be formatted as a dictionary") + __opts__["file_client"] = "local" minion = salt.minion.MasterMinion(__opts__) - running = minion.functions['state.single']( - fun, - name, - test=None, - queue=False, - pillar=pillar, - **kwargs) - ret = {minion.opts['id']: running} - __jid_event__.fire_event({'data': ret, 'outputter': 'highstate'}, 'progress') + running = minion.functions["state.single"]( + fun, name, test=None, queue=False, pillar=pillar, **kwargs + ) + ret = {minion.opts["id"]: running} + __jid_event__.fire_event({"data": ret, "outputter": "highstate"}, "progress") return ret def orchestrate_high(data, test=None, queue=False, pillar=None, **kwargs): - ''' + """ Execute a single state orchestration routine .. versionadded:: 2015.5.0 @@ -188,32 +184,29 @@ def orchestrate_high(data, test=None, queue=False, pillar=None, **kwargs): require: [{salt: stage_one}], }]}, }' - ''' + """ if pillar is not None and not isinstance(pillar, dict): - raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary' - ) - __opts__['file_client'] = 'local' + raise SaltInvocationError("Pillar data must be formatted as a dictionary") + __opts__["file_client"] = "local" minion = salt.minion.MasterMinion(__opts__) - running = minion.functions['state.high']( - data, - test=None, - queue=False, - pillar=pillar, - **kwargs) - ret = {minion.opts['id']: running} - __jid_event__.fire_event({'data': ret, 'outputter': 'highstate'}, 'progress') + running = minion.functions["state.high"]( + data, test=None, queue=False, pillar=pillar, **kwargs + ) + ret = {minion.opts["id"]: running} + __jid_event__.fire_event({"data": ret, "outputter": "highstate"}, "progress") return ret -def orchestrate_show_sls(mods, - saltenv='base', - test=None, - queue=False, - pillar=None, - pillarenv=None, - pillar_enc=None): - ''' +def orchestrate_show_sls( + mods, + saltenv="base", + test=None, + queue=False, + pillar=None, + pillarenv=None, + pillar_enc=None, +): + """ Display the state data from a specific sls, or list of sls files, after being render using the master minion. @@ -225,36 +218,35 @@ def orchestrate_show_sls(mods, .. code-block:: bash salt-run state.orch_show_sls my-orch-formula.my-orch-state 'pillar={ nodegroup: ng1 }' - ''' + """ if pillar is not None and not isinstance(pillar, dict): - raise SaltInvocationError( - 'Pillar data must be formatted as a dictionary') + raise SaltInvocationError("Pillar data must be formatted as a dictionary") - __opts__['file_client'] = 'local' + __opts__["file_client"] = "local" minion = salt.minion.MasterMinion(__opts__) - running = minion.functions['state.show_sls']( + running = minion.functions["state.show_sls"]( mods, test, queue, pillar=pillar, pillarenv=pillarenv, pillar_enc=pillar_enc, - saltenv=saltenv) + saltenv=saltenv, + ) - ret = {minion.opts['id']: running} + ret = {minion.opts["id"]: running} return ret -orch_show_sls = salt.utils.functools.alias_function(orchestrate_show_sls, 'orch_show_sls') +orch_show_sls = salt.utils.functools.alias_function( + orchestrate_show_sls, "orch_show_sls" +) -def event(tagmatch='*', - count=-1, - quiet=False, - sock_dir=None, - pretty=False, - node='master'): - r''' +def event( + tagmatch="*", count=-1, quiet=False, sock_dir=None, pretty=False, node="master" +): + r""" Watch Salt's event bus and block until the given tag is matched .. versionadded:: 2014.7.0 @@ -301,13 +293,14 @@ def event(tagmatch='*', See :blob:`tests/eventlisten.sh` for an example of usage within a shell script. - ''' - statemod = salt.loader.raw_mod(__opts__, 'state', None) + """ + statemod = salt.loader.raw_mod(__opts__, "state", None) - return statemod['state.event']( - tagmatch=tagmatch, - count=count, - quiet=quiet, - sock_dir=sock_dir, - pretty=pretty, - node=node) + return statemod["state.event"]( + tagmatch=tagmatch, + count=count, + quiet=quiet, + sock_dir=sock_dir, + pretty=pretty, + node=node, + ) diff --git a/salt/runners/survey.py b/salt/runners/survey.py index 9b2354a4290..719429ad5b0 100644 --- a/salt/runners/survey.py +++ b/salt/runners/survey.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A general map/reduce style salt runner for aggregating results returned by several different minions. @@ -10,7 +10,7 @@ matching results. Useful for playing the game: *"some of these things are not like the others..."* when identifying discrepancies in a large infrastructure managed by salt. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -25,7 +25,7 @@ from salt.ext.six.moves import range def hash(*args, **kwargs): - ''' + """ Return the MATCHING minion pools from the aggregated and sorted results of a salt command @@ -50,13 +50,13 @@ def hash(*args, **kwargs): .. code-block:: bash salt-run survey.hash "*" file.get_hash /etc/salt/minion survey_sort=up - ''' + """ return _get_pool_results(*args, **kwargs) def diff(*args, **kwargs): - ''' + """ Return the DIFFERENCE of the result sets returned by each matching minion pool @@ -85,7 +85,7 @@ def diff(*args, **kwargs): .. code-block:: bash salt-run survey.diff survey_sort=up "*" cp.get_file_str file:///etc/hosts - ''' + """ # TODO: The salt execution module "cp.get_file_str file:///..." is a # non-obvious way to display the differences between files using # survey.diff . A more obvious method needs to be found or developed. @@ -96,36 +96,36 @@ def diff(*args, **kwargs): is_first_time = True for k in bulk_ret: - print('minion pool :\n' - '------------') - print(k['pool']) - print('pool size :\n' - '----------') - print(' ' + six.text_type(len(k['pool']))) + print("minion pool :\n" "------------") + print(k["pool"]) + print("pool size :\n" "----------") + print(" " + six.text_type(len(k["pool"]))) if is_first_time: is_first_time = False - print('pool result :\n' - '------------') - print(' ' + bulk_ret[0]['result']) + print("pool result :\n" "------------") + print(" " + bulk_ret[0]["result"]) print() continue - outs = ('differences from "{0}" results :').format( - bulk_ret[0]['pool'][0]) + outs = ('differences from "{0}" results :').format(bulk_ret[0]["pool"][0]) print(outs) - print('-' * (len(outs) - 1)) - from_result = bulk_ret[0]['result'].splitlines() + print("-" * (len(outs) - 1)) + from_result = bulk_ret[0]["result"].splitlines() for i in range(0, len(from_result)): - from_result[i] += '\n' - to_result = k['result'].splitlines() + from_result[i] += "\n" + to_result = k["result"].splitlines() for i in range(0, len(to_result)): - to_result[i] += '\n' - outs = '' - outs += ''.join(difflib.unified_diff(from_result, - to_result, - fromfile=bulk_ret[0]['pool'][0], - tofile=k['pool'][0], - n=0)) + to_result[i] += "\n" + outs = "" + outs += "".join( + difflib.unified_diff( + from_result, + to_result, + fromfile=bulk_ret[0]["pool"][0], + tofile=k["pool"][0], + n=0, + ) + ) print(outs) print() @@ -133,7 +133,7 @@ def diff(*args, **kwargs): def _get_pool_results(*args, **kwargs): - ''' + """ A helper function which returns a dictionary of minion pools along with their matching result sets. Useful for developing other "survey style" functions. @@ -141,7 +141,7 @@ def _get_pool_results(*args, **kwargs): specifying sort order. Because the kwargs namespace of the "salt" and "survey" command are shared, the name "survey_sort" was chosen to help avoid option conflicts. - ''' + """ # TODO: the option "survey.sort=" would be preferred for namespace # separation but the kwargs parser for the salt-run command seems to # improperly pass the options containing a "." in them for later modules to @@ -153,34 +153,45 @@ def _get_pool_results(*args, **kwargs): cmd = args[1] ret = {} - sort = kwargs.pop('survey_sort', 'down') - direction = sort != 'up' + sort = kwargs.pop("survey_sort", "down") + direction = sort != "up" - tgt_type = kwargs.pop('tgt_type', 'compound') - if tgt_type not in ['compound', 'pcre']: - tgt_type = 'compound' + tgt_type = kwargs.pop("tgt_type", "compound") + if tgt_type not in ["compound", "pcre"]: + tgt_type = "compound" - kwargs_passthru = dict((k, kwargs[k]) for k in six.iterkeys(kwargs) if not k.startswith('_')) + kwargs_passthru = dict( + (k, kwargs[k]) for k in six.iterkeys(kwargs) if not k.startswith("_") + ) - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) try: - minions = client.cmd(tgt, cmd, args[2:], timeout=__opts__['timeout'], tgt_type=tgt_type, kwarg=kwargs_passthru) + minions = client.cmd( + tgt, + cmd, + args[2:], + timeout=__opts__["timeout"], + tgt_type=tgt_type, + kwarg=kwargs_passthru, + ) except SaltClientError as client_error: print(client_error) return ret # hash minion return values as a string for minion in sorted(minions): - digest = hashlib.sha256(six.text_type(minions[minion]).encode(__salt_system_encoding__)).hexdigest() + digest = hashlib.sha256( + six.text_type(minions[minion]).encode(__salt_system_encoding__) + ).hexdigest() if digest not in ret: ret[digest] = {} - ret[digest]['pool'] = [] - ret[digest]['result'] = six.text_type(minions[minion]) + ret[digest]["pool"] = [] + ret[digest]["result"] = six.text_type(minions[minion]) - ret[digest]['pool'].append(minion) + ret[digest]["pool"].append(minion) sorted_ret = [] - for k in sorted(ret, key=lambda k: len(ret[k]['pool']), reverse=direction): + for k in sorted(ret, key=lambda k: len(ret[k]["pool"]), reverse=direction): # return aggregated results, sorted by size of the hash pool sorted_ret.append(ret[k]) diff --git a/salt/runners/test.py b/salt/runners/test.py index 8f3b05cdff0..b405c63df12 100644 --- a/salt/runners/test.py +++ b/salt/runners/test.py @@ -1,83 +1,100 @@ # -*- coding: utf-8 -*- -''' +""" This runner is used only for test purposes and servers no production purpose -''' +""" from __future__ import absolute_import, print_function, unicode_literals + # Import python libs import time + from salt.ext import six from salt.ext.six.moves import range def arg(*args, **kwargs): - ''' + """ Output the given args and kwargs Kwargs will be filtered for 'private' keynames. - ''' - kwargs = dict((k, v) for k, v in six.iteritems(kwargs) - if not k.startswith('__')) + """ + kwargs = dict((k, v) for k, v in six.iteritems(kwargs) if not k.startswith("__")) ret = { - 'args': args, - 'kwargs': kwargs, + "args": args, + "kwargs": kwargs, } return ret def raw_arg(*args, **kwargs): - ''' + """ Output the given args and kwargs - ''' + """ ret = { - 'args': args, - 'kwargs': kwargs, + "args": args, + "kwargs": kwargs, } return ret -def metasyntactic(locality='us'): - ''' +def metasyntactic(locality="us"): + """ Return common metasyntactic variables for the given locality - ''' + """ lookup = { - 'us': ['foo', 'bar', 'baz', 'qux', 'quux', 'quuz', 'corge', 'grault', - 'garply', 'waldo', 'fred', 'plugh', 'xyzzy', 'thud'], - 'uk': ['wibble', 'wobble', 'wubble', 'flob'], + "us": [ + "foo", + "bar", + "baz", + "qux", + "quux", + "quuz", + "corge", + "grault", + "garply", + "waldo", + "fred", + "plugh", + "xyzzy", + "thud", + ], + "uk": ["wibble", "wobble", "wubble", "flob"], } return lookup.get(locality, None) def stdout_print(): - ''' + """ Print 'foo' and return 'bar' - ''' - print('foo') - return 'bar' + """ + print("foo") + return "bar" def sleep(s_time=10): - ''' + """ Sleep t seconds, then return True - ''' + """ print(s_time) time.sleep(s_time) return True def stream(): - ''' + """ Return True - ''' + """ ret = True for i in range(1, 100): - __jid_event__.fire_event({'message': 'Runner is {0}% done'.format(i)}, 'progress') + __jid_event__.fire_event( + {"message": "Runner is {0}% done".format(i)}, "progress" + ) time.sleep(0.1) return ret def get_opts(): - ''' + """ .. versionadded:: 2018.3.0 Return the configuration options of the master. @@ -87,5 +104,5 @@ def get_opts(): .. code-block:: bash salt-run test.get_opts - ''' + """ return __opts__ diff --git a/salt/runners/thin.py b/salt/runners/thin.py index 12b3954a718..1de863039fd 100644 --- a/salt/runners/thin.py +++ b/salt/runners/thin.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" The thin runner is used to manage the salt thin systems. Salt Thin is a transport-less version of Salt that can be used to run routines in a standalone way. This runner has tools which generate the standalone salt system for easy consumption. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -14,10 +14,16 @@ from __future__ import absolute_import, print_function, unicode_literals import salt.utils.thin -def generate(extra_mods='', overwrite=False, so_mods='', - python2_bin='python2', python3_bin='python3', absonly=True, - compress='gzip'): - ''' +def generate( + extra_mods="", + overwrite=False, + so_mods="", + python2_bin="python2", + python3_bin="python3", + absonly=True, + compress="gzip", +): + """ Generate the salt-thin tarball and print the location of the tarball Optional additional mods to include (e.g. mako) can be supplied as a comma delimited string. Permits forcing an overwrite of the output file as well. @@ -30,24 +36,31 @@ def generate(extra_mods='', overwrite=False, so_mods='', salt-run thin.generate mako salt-run thin.generate mako,wempy 1 salt-run thin.generate overwrite=1 - ''' - conf_mods = __opts__.get('thin_extra_mods') + """ + conf_mods = __opts__.get("thin_extra_mods") if conf_mods: - extra_mods = ','.join([conf_mods, extra_mods]) + extra_mods = ",".join([conf_mods, extra_mods]) - return salt.utils.thin.gen_thin(__opts__['cachedir'], - extra_mods, - overwrite, - so_mods, - python2_bin, - python3_bin, - absonly, - compress) + return salt.utils.thin.gen_thin( + __opts__["cachedir"], + extra_mods, + overwrite, + so_mods, + python2_bin, + python3_bin, + absonly, + compress, + ) -def generate_min(extra_mods='', overwrite=False, so_mods='', - python2_bin='python2', python3_bin='python3'): - ''' +def generate_min( + extra_mods="", + overwrite=False, + so_mods="", + python2_bin="python2", + python3_bin="python3", +): + """ Generate the salt-thin tarball and print the location of the tarball Optional additional mods to include (e.g. mako) can be supplied as a comma delimited string. Permits forcing an overwrite of the output file as well. @@ -57,14 +70,11 @@ def generate_min(extra_mods='', overwrite=False, so_mods='', .. code-block:: bash salt-run thin.generate_min - ''' - conf_mods = __opts__.get('min_extra_mods') + """ + conf_mods = __opts__.get("min_extra_mods") if conf_mods: - extra_mods = ','.join([conf_mods, extra_mods]) + extra_mods = ",".join([conf_mods, extra_mods]) - return salt.utils.thin.gen_min(__opts__['cachedir'], - extra_mods, - overwrite, - so_mods, - python2_bin, - python3_bin) + return salt.utils.thin.gen_min( + __opts__["cachedir"], extra_mods, overwrite, so_mods, python2_bin, python3_bin + ) diff --git a/salt/runners/vault.py b/salt/runners/vault.py index fc89079a0e7..add90a736e2 100644 --- a/salt/runners/vault.py +++ b/salt/runners/vault.py @@ -1,18 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" :maintainer: SaltStack :maturity: new :platform: all Runner functions supporting the Vault modules. Configuration instructions are documented in the execution module docs. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import json import logging import string + import requests # Import Salt libs @@ -26,7 +28,7 @@ log = logging.getLogger(__name__) def generate_token(minion_id, signature, impersonated_by_master=False): - ''' + """ Generate a Vault token for minion minion_id minion_id @@ -39,65 +41,66 @@ def generate_token(minion_id, signature, impersonated_by_master=False): impersonated_by_master If the master needs to create a token on behalf of the minion, this is True. This happens when the master generates minion pillars. - ''' + """ log.debug( - 'Token generation request for %s (impersonated by master: %s)', - minion_id, impersonated_by_master + "Token generation request for %s (impersonated by master: %s)", + minion_id, + impersonated_by_master, ) _validate_signature(minion_id, signature, impersonated_by_master) try: - config = __opts__['vault'] - verify = config.get('verify', None) + config = __opts__["vault"] + verify = config.get("verify", None) - if config['auth']['method'] == 'approle': + if config["auth"]["method"] == "approle": if _selftoken_expired(): - log.debug('Vault token expired. Recreating one') + log.debug("Vault token expired. Recreating one") # Requesting a short ttl token - url = '{0}/v1/auth/approle/login'.format(config['url']) + url = "{0}/v1/auth/approle/login".format(config["url"]) - payload = {'role_id': config['auth']['role_id']} - if 'secret_id' in config['auth']: - payload['secret_id'] = config['auth']['secret_id'] + payload = {"role_id": config["auth"]["role_id"]} + if "secret_id" in config["auth"]: + payload["secret_id"] = config["auth"]["secret_id"] response = requests.post(url, json=payload, verify=verify) if response.status_code != 200: - return {'error': response.reason} - config['auth']['token'] = response.json()['auth']['client_token'] + return {"error": response.reason} + config["auth"]["token"] = response.json()["auth"]["client_token"] url = _get_token_create_url(config) - headers = {'X-Vault-Token': config['auth']['token']} + headers = {"X-Vault-Token": config["auth"]["token"]} audit_data = { - 'saltstack-jid': globals().get('__jid__', '<no jid set>'), - 'saltstack-minion': minion_id, - 'saltstack-user': globals().get('__user__', '<no user set>') + "saltstack-jid": globals().get("__jid__", "<no jid set>"), + "saltstack-minion": minion_id, + "saltstack-user": globals().get("__user__", "<no user set>"), } payload = { - 'policies': _get_policies(minion_id, config), - 'num_uses': 1, - 'meta': audit_data - } + "policies": _get_policies(minion_id, config), + "num_uses": 1, + "meta": audit_data, + } - if payload['policies'] == []: - return {'error': 'No policies matched minion'} + if payload["policies"] == []: + return {"error": "No policies matched minion"} - log.trace('Sending token creation request to Vault') + log.trace("Sending token creation request to Vault") response = requests.post(url, headers=headers, json=payload, verify=verify) if response.status_code != 200: - return {'error': response.reason} + return {"error": response.reason} - auth_data = response.json()['auth'] + auth_data = response.json()["auth"] return { - 'token': auth_data['client_token'], - 'url': config['url'], - 'verify': verify, + "token": auth_data["client_token"], + "url": config["url"], + "verify": verify, } except Exception as e: # pylint: disable=broad-except - return {'error': six.text_type(e)} + return {"error": six.text_type(e)} def unseal(): - ''' + """ Unseal Vault server This function uses the 'keys' from the 'vault' configuration to unseal vault server @@ -118,16 +121,18 @@ def unseal(): .. code-block:: bash salt-run vault.unseal - ''' - for key in __opts__['vault']['keys']: - ret = __utils__['vault.make_request']('PUT', 'v1/sys/unseal', data=json.dumps({'key': key})).json() - if ret['sealed'] is False: + """ + for key in __opts__["vault"]["keys"]: + ret = __utils__["vault.make_request"]( + "PUT", "v1/sys/unseal", data=json.dumps({"key": key}) + ).json() + if ret["sealed"] is False: return True return False def show_policies(minion_id): - ''' + """ Show the Vault policies that are applied to tokens for the given minion minion_id @@ -138,59 +143,57 @@ def show_policies(minion_id): .. code-block:: bash salt-run vault.show_policies myminion - ''' - config = __opts__['vault'] + """ + config = __opts__["vault"] return _get_policies(minion_id, config) def _validate_signature(minion_id, signature, impersonated_by_master): - ''' + """ Validate that either minion with id minion_id, or the master, signed the request - ''' - pki_dir = __opts__['pki_dir'] + """ + pki_dir = __opts__["pki_dir"] if impersonated_by_master: - public_key = '{0}/master.pub'.format(pki_dir) + public_key = "{0}/master.pub".format(pki_dir) else: - public_key = '{0}/minions/{1}'.format(pki_dir, minion_id) + public_key = "{0}/minions/{1}".format(pki_dir, minion_id) - log.trace('Validating signature for %s', minion_id) + log.trace("Validating signature for %s", minion_id) signature = base64.b64decode(signature) if not salt.crypt.verify_signature(public_key, minion_id, signature): raise salt.exceptions.AuthenticationError( - 'Could not validate token request from {0}'.format(minion_id) - ) - log.trace('Signature ok') + "Could not validate token request from {0}".format(minion_id) + ) + log.trace("Signature ok") def _get_policies(minion_id, config): - ''' + """ Get the policies that should be applied to a token for minion_id - ''' + """ _, grains, _ = salt.utils.minions.get_minion_data(minion_id, __opts__) policy_patterns = config.get( - 'policies', - ['saltstack/minion/{minion}', 'saltstack/minions'] - ) - mappings = {'minion': minion_id, 'grains': grains or {}} + "policies", ["saltstack/minion/{minion}", "saltstack/minions"] + ) + mappings = {"minion": minion_id, "grains": grains or {}} policies = [] for pattern in policy_patterns: try: for expanded_pattern in _expand_pattern_lists(pattern, **mappings): policies.append( - expanded_pattern.format(**mappings) - .lower() # Vault requirement - ) + expanded_pattern.format(**mappings).lower() # Vault requirement + ) except KeyError: - log.warning('Could not resolve policy pattern %s', pattern) + log.warning("Could not resolve policy pattern %s", pattern) - log.debug('%s policies: %s', minion_id, policies) + log.debug("%s policies: %s", minion_id, policies) return policies def _expand_pattern_lists(pattern, **mappings): - ''' + """ Expands the pattern for any list-valued mappings, such that for any list of length N in the mappings present in the pattern, N copies of the pattern are returned, each with an element of the list substituted. @@ -214,23 +217,23 @@ def _expand_pattern_lists(pattern, **mappings): ``[by-role/web, by-role/database]``. Note that this method does not expand any non-list patterns. - ''' + """ expanded_patterns = [] f = string.Formatter() - ''' + """ This function uses a string.Formatter to get all the formatting tokens from the pattern, then recursively replaces tokens whose expanded value is a list. For a list with N items, it will create N new pattern strings and then continue with the next token. In practice this is expected to not be very expensive, since patterns will typically involve a handful of lists at most. - ''' # pylint: disable=W0105 + """ # pylint: disable=W0105 for (_, field_name, _, _) in f.parse(pattern): if field_name is None: continue (value, _) = f.get_field(field_name, None, mappings) if isinstance(value, list): - token = '{{{0}}}'.format(field_name) + token = "{{{0}}}".format(field_name) expanded = [pattern.replace(token, six.text_type(elem)) for elem in value] for expanded_item in expanded: result = _expand_pattern_lists(expanded_item, **mappings) @@ -240,30 +243,30 @@ def _expand_pattern_lists(pattern, **mappings): def _selftoken_expired(): - ''' + """ Validate the current token exists and is still valid - ''' + """ try: - verify = __opts__['vault'].get('verify', None) - url = '{0}/v1/auth/token/lookup-self'.format(__opts__['vault']['url']) - if 'token' not in __opts__['vault']['auth']: + verify = __opts__["vault"].get("verify", None) + url = "{0}/v1/auth/token/lookup-self".format(__opts__["vault"]["url"]) + if "token" not in __opts__["vault"]["auth"]: return True - headers = {'X-Vault-Token': __opts__['vault']['auth']['token']} + headers = {"X-Vault-Token": __opts__["vault"]["auth"]["token"]} response = requests.get(url, headers=headers, verify=verify) if response.status_code != 200: return True return False except Exception as e: # pylint: disable=broad-except raise salt.exceptions.CommandExecutionError( - 'Error while looking up self token : {0}'.format(six.text_type(e)) - ) + "Error while looking up self token : {0}".format(six.text_type(e)) + ) def _get_token_create_url(config): - ''' + """ Create Vault url for token creation - ''' - role_name = config.get('role_name', None) - auth_path = '/v1/auth/token/create' - base_url = config['url'] - return '/'.join(x.strip('/') for x in (base_url, auth_path, role_name) if x) + """ + role_name = config.get("role_name", None) + auth_path = "/v1/auth/token/create" + base_url = config["url"] + return "/".join(x.strip("/") for x in (base_url, auth_path, role_name) if x) diff --git a/salt/runners/venafiapi.py b/salt/runners/venafiapi.py index bc584165372..43ac2e91cfe 100644 --- a/salt/runners/venafiapi.py +++ b/salt/runners/venafiapi.py @@ -25,12 +25,13 @@ Support for Venafi """ from __future__ import absolute_import, print_function, unicode_literals -import logging -import time +import logging # Import Salt libs import sys +import time + import salt.cache import salt.syspaths as syspaths import salt.utils.files @@ -40,36 +41,45 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six + try: import vcert from vcert.common import CertificateRequest + HAS_VCERT = True except ImportError: HAS_VCERT = False -CACHE_BANK_NAME = 'venafi/domains' -__virtualname__ = 'venafi' +CACHE_BANK_NAME = "venafi/domains" +__virtualname__ = "venafi" log = logging.getLogger(__name__) def _init_connection(): log.info("Initializing Venafi Trust Platform or Venafi Cloud connection") - api_key = __opts__.get('venafi', {}).get('api_key', '') - base_url = __opts__.get('venafi', {}).get('base_url', '') + api_key = __opts__.get("venafi", {}).get("api_key", "") + base_url = __opts__.get("venafi", {}).get("base_url", "") log.info("Using base_url: %s", base_url) - tpp_user = __opts__.get('venafi', {}).get('tpp_user', '') - tpp_password = __opts__.get('venafi', {}).get('tpp_password', '') - trust_bundle = __opts__.get('venafi', {}).get('trust_bundle', '') - fake = __opts__.get('venafi', {}).get('fake', '') + tpp_user = __opts__.get("venafi", {}).get("tpp_user", "") + tpp_password = __opts__.get("venafi", {}).get("tpp_password", "") + trust_bundle = __opts__.get("venafi", {}).get("trust_bundle", "") + fake = __opts__.get("venafi", {}).get("fake", "") log.info("Finished config processing") if fake: return vcert.Connection(fake=True) elif trust_bundle: log.info("Will use trust bundle from file %s", trust_bundle) - return vcert.Connection(url=base_url, token=api_key, user=tpp_user, password=tpp_password, - http_request_kwargs={"verify": trust_bundle}) + return vcert.Connection( + url=base_url, + token=api_key, + user=tpp_user, + password=tpp_password, + http_request_kwargs={"verify": trust_bundle}, + ) else: - return vcert.Connection(url=base_url, token=api_key, user=tpp_user, password=tpp_password) + return vcert.Connection( + url=base_url, token=api_key, user=tpp_user, password=tpp_password + ) def __virtual__(): @@ -110,13 +120,20 @@ def request( sys.exit(1) if key_password is not None: - if key_password.startswith('sdb://'): - key_password = __salt__['sdb.get'](key_password) + if key_password.startswith("sdb://"): + key_password = __salt__["sdb.get"](key_password) conn = _init_connection() if csr_path is None: - request = CertificateRequest(common_name=dns_name, country=country, province=state, locality=loc, - organization=org, organizational_unit=org_unit, key_password=key_password) + request = CertificateRequest( + common_name=dns_name, + country=country, + province=state, + locality=loc, + organization=org, + organizational_unit=org_unit, + key_password=key_password, + ) zone_config = conn.read_zone_conf(zone) log.info("Updating request from zone %s", zone_config) request.update_from_zone_config(zone_config) @@ -129,7 +146,7 @@ def request( request = CertificateRequest(csr=csr, common_name=dns_name) except Exception as e: raise Exception( - 'Unable to open file {file}: {excp}'.format(file=csr_path, excp=e) + "Unable to open file {file}: {excp}".format(file=csr_path, excp=e) ) conn.request_cert(request, zone) @@ -151,17 +168,17 @@ def request( private_key = pkey_file.read() except Exception as e: raise Exception( - 'Unable to open file {file}: {excp}'.format(file=pkey_path, excp=e) + "Unable to open file {file}: {excp}".format(file=pkey_path, excp=e) ) else: private_key = None cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) data = { - 'minion_id': minion_id, - 'cert': cert.cert, - 'chain': cert.chain, - 'pkey': private_key + "minion_id": minion_id, + "cert": cert.cert, + "chain": cert.chain, + "pkey": private_key, } cache.store(CACHE_BANK_NAME, dns_name, data) return cert.cert, private_key @@ -198,7 +215,7 @@ def show_cert(dns_name): cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) domain_data = cache.fetch(CACHE_BANK_NAME, dns_name) or {} - cert = domain_data.get('cert') + cert = domain_data.get("cert") return cert @@ -213,7 +230,7 @@ def list_domain_cache(): salt-run venafi.list_domain_cache """ cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) - return cache.list('venafi/domains') + return cache.list("venafi/domains") def del_cached_domain(domains): @@ -228,11 +245,11 @@ def del_cached_domain(domains): """ cache = salt.cache.Cache(__opts__, syspaths.CACHE_DIR) if isinstance(domains, six.string_types): - domains = domains.split(',') + domains = domains.split(",") if not isinstance(domains, list): raise CommandExecutionError( - 'You must pass in either a string containing one or more domains ' - 'separated by commas, or a list of single domain strings' + "You must pass in either a string containing one or more domains " + "separated by commas, or a list of single domain strings" ) success = [] failed = [] @@ -242,4 +259,4 @@ def del_cached_domain(domains): success.append(domain) except CommandExecutionError: failed.append(domain) - return {'Succeeded': success, 'Failed': failed} + return {"Succeeded": success, "Failed": failed} diff --git a/salt/runners/virt.py b/salt/runners/virt.py index 2bdc5d06a20..307d8928113 100644 --- a/salt/runners/virt.py +++ b/salt/runners/virt.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Control virtual machines via Salt -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os.path + import logging +import os.path # Import Salt libs import salt.client @@ -22,54 +23,57 @@ from salt.ext import six log = logging.getLogger(__name__) -def _determine_host(data, omit=''): - ''' +def _determine_host(data, omit=""): + """ Determine what the most resource free host is based on the given data - ''' + """ # This is just checking for the host with the most free ram, this needs # to be much more complicated. - host = '' + host = "" bestmem = 0 for hv_, comps in six.iteritems(data): if hv_ == omit: continue if not isinstance(comps, dict): continue - if comps.get('freemem', 0) > bestmem: - bestmem = comps['freemem'] + if comps.get("freemem", 0) > bestmem: + bestmem = comps["freemem"] host = hv_ return host def _find_vm(name, data, quiet=False): - ''' + """ Scan the query data for the named VM - ''' + """ for hv_ in data: # Check if data is a dict, and not '"virt.full_info" is not available.' if not isinstance(data[hv_], dict): continue - if name in data[hv_].get('vm_info', {}): - ret = {hv_: {name: data[hv_]['vm_info'][name]}} + if name in data[hv_].get("vm_info", {}): + ret = {hv_: {name: data[hv_]["vm_info"][name]}} if not quiet: - __jid_event__.fire_event({'data': ret, 'outputter': 'nested'}, 'progress') + __jid_event__.fire_event( + {"data": ret, "outputter": "nested"}, "progress" + ) return ret return {} def query(host=None, quiet=False): - ''' + """ Query the virtual machines. When called without options all hosts are detected and a full query is returned. A single host can be passed in to specify an individual host to query. - ''' + """ if quiet: log.warning("'quiet' is deprecated. Please migrate to --quiet") ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) try: - for info in client.cmd_iter('virtual:physical', - 'virt.full_info', tgt_type='grain'): + for info in client.cmd_iter( + "virtual:physical", "virt.full_info", tgt_type="grain" + ): if not info: continue if not isinstance(info, dict): @@ -81,32 +85,33 @@ def query(host=None, quiet=False): continue if not isinstance(info[id_], dict): continue - if 'ret' not in info[id_]: + if "ret" not in info[id_]: continue - if not isinstance(info[id_]['ret'], dict): + if not isinstance(info[id_]["ret"], dict): continue - chunk[id_] = info[id_]['ret'] + chunk[id_] = info[id_]["ret"] ret.update(chunk) if not quiet: - __jid_event__.fire_event({'data': chunk, 'outputter': 'virt_query'}, 'progress') + __jid_event__.fire_event( + {"data": chunk, "outputter": "virt_query"}, "progress" + ) except SaltClientError as client_error: print(client_error) return ret def list(host=None, quiet=False, hyper=None): # pylint: disable=redefined-builtin - ''' + """ List the virtual machines on each host, this is a simplified query, showing only the virtual machine names belonging to each host. A single host can be passed in to specify an individual host to list. - ''' + """ if quiet: log.warning("'quiet' is deprecated. Please migrate to --quiet") ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) - for info in client.cmd_iter('virtual:physical', - 'virt.vm_info', tgt_type='grain'): + client = salt.client.get_local_client(__opts__["conf_file"]) + for info in client.cmd_iter("virtual:physical", "virt.vm_info", tgt_type="grain"): if not info: continue if not isinstance(info, dict): @@ -118,64 +123,65 @@ def list(host=None, quiet=False, hyper=None): # pylint: disable=redefined-built continue if not isinstance(info[id_], dict): continue - if 'ret' not in info[id_]: + if "ret" not in info[id_]: continue - if not isinstance(info[id_]['ret'], dict): + if not isinstance(info[id_]["ret"], dict): continue data = {} - for key, val in six.iteritems(info[id_]['ret']): - if val['state'] in data: - data[val['state']].append(key) + for key, val in six.iteritems(info[id_]["ret"]): + if val["state"] in data: + data[val["state"]].append(key) else: - data[val['state']] = [key] + data[val["state"]] = [key] chunk[id_] = data ret.update(chunk) if not quiet: - __jid_event__.fire_event({'data': chunk, 'outputter': 'nested'}, 'progress') + __jid_event__.fire_event({"data": chunk, "outputter": "nested"}, "progress") return ret def next_host(): - ''' + """ Return the host to use for the next autodeployed VM. This queries the available host and executes some math the determine the most "available" next host. - ''' + """ host = _determine_host(query(quiet=True)) print(host) return host def host_info(host=None): - ''' + """ Return information about the host connected to this master - ''' + """ data = query(host, quiet=True) for id_ in data: - if 'vm_info' in data[id_]: - data[id_].pop('vm_info') - __jid_event__.fire_event({'data': data, 'outputter': 'nested'}, 'progress') + if "vm_info" in data[id_]: + data[id_].pop("vm_info") + __jid_event__.fire_event({"data": data, "outputter": "nested"}, "progress") return data def init( - name, - cpu, - mem, - image, - hypervisor='kvm', - host=None, - seed=True, - nic='default', - install=True, - start=True, - disk='default', - saltenv='base', - enable_vnc=False, - seed_cmd='seed.apply', - enable_qcow=False): - ''' + name, + cpu, + mem, + image, + hypervisor="kvm", + host=None, + seed=True, + nic="default", + install=True, + start=True, + disk="default", + saltenv="base", + enable_vnc=False, + seed_cmd="seed.apply", + enable_qcow=False, +): + """ This routine is used to create a new virtual machine. This routines takes a number of options to determine what the newly created virtual machine will look like. @@ -230,295 +236,300 @@ def init( enable_qcow Clone disk image as a copy-on-write qcow2 image, using downloaded `image` as backing file. - ''' - __jid_event__.fire_event({'message': 'Searching for hosts'}, 'progress') + """ + __jid_event__.fire_event({"message": "Searching for hosts"}, "progress") data = query(host, quiet=True) # Check if the name is already deployed for node in data: - if 'vm_info' in data[node]: - if name in data[node]['vm_info']: + if "vm_info" in data[node]: + if name in data[node]["vm_info"]: __jid_event__.fire_event( - {'message': 'Virtual machine {0} is already deployed'.format(name)}, - 'progress' + {"message": "Virtual machine {0} is already deployed".format(name)}, + "progress", ) - return 'fail' + return "fail" if host is None: host = _determine_host(data) if host not in data or not host: __jid_event__.fire_event( - {'message': 'Host {0} was not found'.format(host)}, - 'progress' + {"message": "Host {0} was not found".format(host)}, "progress" ) - return 'fail' + return "fail" pub_key = None priv_key = None if seed: - __jid_event__.fire_event({'message': 'Minion will be preseeded'}, 'progress') + __jid_event__.fire_event({"message": "Minion will be preseeded"}, "progress") priv_key, pub_key = salt.utils.cloud.gen_keys() - accepted_key = os.path.join(__opts__['pki_dir'], 'minions', name) - with salt.utils.files.fopen(accepted_key, 'w') as fp_: + accepted_key = os.path.join(__opts__["pki_dir"], "minions", name) + with salt.utils.files.fopen(accepted_key, "w") as fp_: fp_.write(salt.utils.stringutils.to_str(pub_key)) - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) __jid_event__.fire_event( - {'message': 'Creating VM {0} on host {1}'.format(name, host)}, - 'progress' + {"message": "Creating VM {0} on host {1}".format(name, host)}, "progress" ) try: cmd_ret = client.cmd_iter( host, - 'virt.init', - [ - name, - cpu, - mem - ], + "virt.init", + [name, cpu, mem], timeout=600, kwarg={ - 'image': image, - 'nic': nic, - 'hypervisor': hypervisor, - 'start': start, - 'disk': disk, - 'saltenv': saltenv, - 'seed': seed, - 'install': install, - 'pub_key': pub_key, - 'priv_key': priv_key, - 'seed_cmd': seed_cmd, - 'enable_vnc': enable_vnc, - 'enable_qcow': enable_qcow, - }) + "image": image, + "nic": nic, + "hypervisor": hypervisor, + "start": start, + "disk": disk, + "saltenv": saltenv, + "seed": seed, + "install": install, + "pub_key": pub_key, + "priv_key": priv_key, + "seed_cmd": seed_cmd, + "enable_vnc": enable_vnc, + "enable_qcow": enable_qcow, + }, + ) except SaltClientError as client_error: # Fall through to ret error handling below print(client_error) ret = next(cmd_ret) if not ret: - __jid_event__.fire_event({'message': 'VM {0} was not initialized.'.format(name)}, 'progress') - return 'fail' + __jid_event__.fire_event( + {"message": "VM {0} was not initialized.".format(name)}, "progress" + ) + return "fail" for minion_id in ret: - if ret[minion_id]['ret'] is False: - print('VM {0} initialization failed. Returned error: {1}'.format(name, ret[minion_id]['ret'])) - return 'fail' + if ret[minion_id]["ret"] is False: + print( + "VM {0} initialization failed. Returned error: {1}".format( + name, ret[minion_id]["ret"] + ) + ) + return "fail" - __jid_event__.fire_event({'message': 'VM {0} initialized on host {1}'.format(name, host)}, 'progress') - return 'good' + __jid_event__.fire_event( + {"message": "VM {0} initialized on host {1}".format(name, host)}, "progress" + ) + return "good" def vm_info(name, quiet=False): - ''' + """ Return the information on the named VM - ''' + """ data = query(quiet=True) return _find_vm(name, data, quiet) def reset(name): - ''' + """ Force power down and restart an existing VM - ''' + """ ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) data = vm_info(name, quiet=True) if not data: - __jid_event__.fire_event({'message': 'Failed to find VM {0} to reset'.format(name)}, 'progress') - return 'fail' + __jid_event__.fire_event( + {"message": "Failed to find VM {0} to reset".format(name)}, "progress" + ) + return "fail" host = next(six.iterkeys(data)) try: - cmd_ret = client.cmd_iter( - host, - 'virt.reset', - [name], - timeout=600) + cmd_ret = client.cmd_iter(host, "virt.reset", [name], timeout=600) for comp in cmd_ret: ret.update(comp) - __jid_event__.fire_event({'message': 'Reset VM {0}'.format(name)}, 'progress') + __jid_event__.fire_event({"message": "Reset VM {0}".format(name)}, "progress") except SaltClientError as client_error: print(client_error) return ret def start(name): - ''' + """ Start a named virtual machine - ''' + """ ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) data = vm_info(name, quiet=True) if not data: - __jid_event__.fire_event({'message': 'Failed to find VM {0} to start'.format(name)}, 'progress') - return 'fail' + __jid_event__.fire_event( + {"message": "Failed to find VM {0} to start".format(name)}, "progress" + ) + return "fail" host = next(six.iterkeys(data)) - if data[host][name]['state'] == 'running': - print('VM {0} is already running'.format(name)) - return 'bad state' + if data[host][name]["state"] == "running": + print("VM {0} is already running".format(name)) + return "bad state" try: - cmd_ret = client.cmd_iter( - host, - 'virt.start', - [name], - timeout=600) + cmd_ret = client.cmd_iter(host, "virt.start", [name], timeout=600) except SaltClientError as client_error: - return 'Virtual machine {0} not started: {1}'. format(name, client_error) + return "Virtual machine {0} not started: {1}".format(name, client_error) for comp in cmd_ret: ret.update(comp) - __jid_event__.fire_event({'message': 'Started VM {0}'.format(name)}, 'progress') - return 'good' + __jid_event__.fire_event({"message": "Started VM {0}".format(name)}, "progress") + return "good" def force_off(name): - ''' + """ Force power down the named virtual machine - ''' + """ ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) data = vm_info(name, quiet=True) if not data: - print('Failed to find VM {0} to destroy'.format(name)) - return 'fail' + print("Failed to find VM {0} to destroy".format(name)) + return "fail" host = next(six.iterkeys(data)) - if data[host][name]['state'] == 'shutdown': - print('VM {0} is already shutdown'.format(name)) - return'bad state' + if data[host][name]["state"] == "shutdown": + print("VM {0} is already shutdown".format(name)) + return "bad state" try: - cmd_ret = client.cmd_iter( - host, - 'virt.stop', - [name], - timeout=600) + cmd_ret = client.cmd_iter(host, "virt.stop", [name], timeout=600) except SaltClientError as client_error: - return 'Virtual machine {0} could not be forced off: {1}'.format(name, client_error) + return "Virtual machine {0} could not be forced off: {1}".format( + name, client_error + ) for comp in cmd_ret: ret.update(comp) - __jid_event__.fire_event({'message': 'Powered off VM {0}'.format(name)}, 'progress') - return 'good' + __jid_event__.fire_event({"message": "Powered off VM {0}".format(name)}, "progress") + return "good" def purge(name, delete_key=True): - ''' + """ Destroy the named VM - ''' + """ ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) data = vm_info(name, quiet=True) if not data: - __jid_event__.fire_event({'error': 'Failed to find VM {0} to purge'.format(name)}, 'progress') - return 'fail' + __jid_event__.fire_event( + {"error": "Failed to find VM {0} to purge".format(name)}, "progress" + ) + return "fail" host = next(six.iterkeys(data)) try: - cmd_ret = client.cmd_iter( - host, - 'virt.purge', - [name, True], - timeout=600) + cmd_ret = client.cmd_iter(host, "virt.purge", [name, True], timeout=600) except SaltClientError as client_error: - return 'Virtual machine {0} could not be purged: {1}'.format(name, client_error) + return "Virtual machine {0} could not be purged: {1}".format(name, client_error) for comp in cmd_ret: ret.update(comp) if delete_key: - log.debug('Deleting key %s', name) + log.debug("Deleting key %s", name) skey = salt.key.Key(__opts__) skey.delete_key(name) - __jid_event__.fire_event({'message': 'Purged VM {0}'.format(name)}, 'progress') - return 'good' + __jid_event__.fire_event({"message": "Purged VM {0}".format(name)}, "progress") + return "good" def pause(name): - ''' + """ Pause the named VM - ''' + """ ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) data = vm_info(name, quiet=True) if not data: - __jid_event__.fire_event({'error': 'Failed to find VM {0} to pause'.format(name)}, 'progress') - return 'fail' + __jid_event__.fire_event( + {"error": "Failed to find VM {0} to pause".format(name)}, "progress" + ) + return "fail" host = next(six.iterkeys(data)) - if data[host][name]['state'] == 'paused': - __jid_event__.fire_event({'error': 'VM {0} is already paused'.format(name)}, 'progress') - return 'bad state' + if data[host][name]["state"] == "paused": + __jid_event__.fire_event( + {"error": "VM {0} is already paused".format(name)}, "progress" + ) + return "bad state" try: - cmd_ret = client.cmd_iter( - host, - 'virt.pause', - [name], - timeout=600) + cmd_ret = client.cmd_iter(host, "virt.pause", [name], timeout=600) except SaltClientError as client_error: - return 'Virtual machine {0} could not be pasued: {1}'.format(name, client_error) + return "Virtual machine {0} could not be pasued: {1}".format(name, client_error) for comp in cmd_ret: ret.update(comp) - __jid_event__.fire_event({'message': 'Paused VM {0}'.format(name)}, 'progress') - return 'good' + __jid_event__.fire_event({"message": "Paused VM {0}".format(name)}, "progress") + return "good" def resume(name): - ''' + """ Resume a paused VM - ''' + """ ret = {} - client = salt.client.get_local_client(__opts__['conf_file']) + client = salt.client.get_local_client(__opts__["conf_file"]) data = vm_info(name, quiet=True) if not data: - __jid_event__.fire_event({'error': 'Failed to find VM {0} to pause'.format(name)}, 'progress') - return 'not found' + __jid_event__.fire_event( + {"error": "Failed to find VM {0} to pause".format(name)}, "progress" + ) + return "not found" host = next(six.iterkeys(data)) - if data[host][name]['state'] != 'paused': - __jid_event__.fire_event({'error': 'VM {0} is not paused'.format(name)}, 'progress') - return 'bad state' + if data[host][name]["state"] != "paused": + __jid_event__.fire_event( + {"error": "VM {0} is not paused".format(name)}, "progress" + ) + return "bad state" try: - cmd_ret = client.cmd_iter( - host, - 'virt.resume', - [name], - timeout=600) + cmd_ret = client.cmd_iter(host, "virt.resume", [name], timeout=600) except SaltClientError as client_error: - return 'Virtual machine {0} could not be resumed: {1}'.format(name, client_error) + return "Virtual machine {0} could not be resumed: {1}".format( + name, client_error + ) for comp in cmd_ret: ret.update(comp) - __jid_event__.fire_event({'message': 'Resumed VM {0}'.format(name)}, 'progress') - return 'good' + __jid_event__.fire_event({"message": "Resumed VM {0}".format(name)}, "progress") + return "good" -def migrate(name, target=''): - ''' +def migrate(name, target=""): + """ Migrate a VM from one host to another. This routine will just start the migration and display information on how to look up the progress. - ''' - client = salt.client.get_local_client(__opts__['conf_file']) + """ + client = salt.client.get_local_client(__opts__["conf_file"]) data = query(quiet=True) origin_data = _find_vm(name, data, quiet=True) try: origin_host = list(origin_data.keys())[0] except IndexError: - __jid_event__.fire_event({'error': 'Named VM {0} was not found to migrate'.format(name)}, 'progress') - return '' - disks = origin_data[origin_host][name]['disks'] + __jid_event__.fire_event( + {"error": "Named VM {0} was not found to migrate".format(name)}, "progress" + ) + return "" + disks = origin_data[origin_host][name]["disks"] if not origin_data: - __jid_event__.fire_event({'error': 'Named VM {0} was not found to migrate'.format(name)}, 'progress') - return '' + __jid_event__.fire_event( + {"error": "Named VM {0} was not found to migrate".format(name)}, "progress" + ) + return "" if not target: target = _determine_host(data, origin_host) if target not in data: - __jid_event__.fire_event({'error': 'Target host {0} not found'.format(origin_data)}, 'progress') - return '' + __jid_event__.fire_event( + {"error": "Target host {0} not found".format(origin_data)}, "progress" + ) + return "" try: - client.cmd(target, 'virt.seed_non_shared_migrate', [disks, True]) - jid = client.cmd_async(origin_host, - 'virt.migrate_non_shared', - [name, target]) + client.cmd(target, "virt.seed_non_shared_migrate", [disks, True]) + jid = client.cmd_async(origin_host, "virt.migrate_non_shared", [name, target]) except SaltClientError as client_error: - return 'Virtual machine {0} could not be migrated: {1}'.format(name, client_error) + return "Virtual machine {0} could not be migrated: {1}".format( + name, client_error + ) - msg = ('The migration of virtual machine {0} to host {1} has begun, ' - 'and can be tracked via jid {2}. The ``salt-run virt.query`` ' - 'runner can also be used, the target VM will be shown as paused ' - 'until the migration is complete.').format(name, target, jid) - __jid_event__.fire_event({'message': msg}, 'progress') + msg = ( + "The migration of virtual machine {0} to host {1} has begun, " + "and can be tracked via jid {2}. The ``salt-run virt.query`` " + "runner can also be used, the target VM will be shown as paused " + "until the migration is complete." + ).format(name, target, jid) + __jid_event__.fire_event({"message": msg}, "progress") diff --git a/salt/runners/vistara.py b/salt/runners/vistara.py index 32774949727..4d5f03cc312 100644 --- a/salt/runners/vistara.py +++ b/salt/runners/vistara.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Vistara Runner Runner to interact with the Vistara (http://www.vistarait.com/) REST API @@ -19,7 +19,7 @@ For example ``/etc/salt/master.d/_vistara.conf``: client_secret: ThisI5AreallyLongsecretKeyIwonderwhyTheyMakethemSoBigTheseDays00 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -27,6 +27,7 @@ import logging # Import Salt libs import salt.output + # See https://docs.saltstack.com/en/latest/topics/tutorials/http.html import salt.utils.http @@ -34,38 +35,48 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Check to see if master config has the necessary config - ''' - vistara_config = __opts__['vistara'] if 'vistara' in __opts__ else None + """ + vistara_config = __opts__["vistara"] if "vistara" in __opts__ else None if vistara_config: - client_id = vistara_config.get('client_id', None) - client_key = vistara_config.get('client_key', None) - client_secret = vistara_config.get('client_secret', None) + client_id = vistara_config.get("client_id", None) + client_key = vistara_config.get("client_key", None) + client_secret = vistara_config.get("client_secret", None) if not client_id or not client_key or not client_secret: - return False, ("vistara client_id or client_key or client_secret " - "has not been specified in the Salt master config.") + return ( + False, + ( + "vistara client_id or client_key or client_secret " + "has not been specified in the Salt master config." + ), + ) return True - return False, ("vistara config has not been specificed in the Salt master " - "config. See documentation for this runner.") + return ( + False, + ( + "vistara config has not been specificed in the Salt master " + "config. See documentation for this runner." + ), + ) def _get_vistara_configuration(): - ''' + """ Return the Vistara configuration read from the master config - ''' + """ return { - 'client_id': __opts__['vistara']['client_id'], - 'client_key': __opts__['vistara']['client_key'], - 'client_secret': __opts__['vistara']['client_secret'] + "client_id": __opts__["vistara"]["client_id"], + "client_key": __opts__["vistara"]["client_key"], + "client_secret": __opts__["vistara"]["client_secret"], } def delete_device(name, safety_on=True): - ''' + """ Deletes a device from Vistara based on DNS name or partial name. By default, delete_device will only perform the delete if a single host is returned. Set safety_on=False to delete all matches (up to default API search page size) @@ -78,20 +89,22 @@ def delete_device(name, safety_on=True): salt-run vistara.delete_device 'hostname-101' salt-run vistara.delete_device 'hostname-1' safety_on=False - ''' + """ config = _get_vistara_configuration() if not config: return False - access_token = _get_oath2_access_token(config['client_key'], config['client_secret']) + access_token = _get_oath2_access_token( + config["client_key"], config["client_secret"] + ) if not access_token: - return 'Vistara access token not available' + return "Vistara access token not available" - query_string = 'dnsName:{0}'.format(name) + query_string = "dnsName:{0}".format(name) - devices = _search_devices(query_string, config['client_id'], access_token) + devices = _search_devices(query_string, config["client_id"], access_token) if not devices: return "No devices found" @@ -99,14 +112,16 @@ def delete_device(name, safety_on=True): device_count = len(devices) if safety_on and device_count != 1: - return "Expected to delete 1 device and found {0}. "\ + return ( + "Expected to delete 1 device and found {0}. " "Set safety_on=False to override.".format(device_count) + ) delete_responses = [] for device in devices: - device_id = device['id'] + device_id = device["id"] log.debug(device_id) - delete_response = _delete_resource(device_id, config['client_id'], access_token) + delete_response = _delete_resource(device_id, config["client_id"], access_token) if not delete_response: return False delete_responses.append(delete_response) @@ -116,73 +131,66 @@ def delete_device(name, safety_on=True): def _search_devices(query_string, client_id, access_token): - authstring = 'Bearer {0}'.format(access_token) + authstring = "Bearer {0}".format(access_token) headers = { - 'Authorization': authstring, - 'Content-Type': 'application/json', - 'Accept': 'application/json' + "Authorization": authstring, + "Content-Type": "application/json", + "Accept": "application/json", } - params = { - 'queryString': query_string - } + params = {"queryString": query_string} - method = 'GET' - url = 'https://api.vistara.io/api/v2/tenants/{0}/devices/search'.format(client_id) + method = "GET" + url = "https://api.vistara.io/api/v2/tenants/{0}/devices/search".format(client_id) resp = salt.utils.http.query( - url=url, - method=method, - header_dict=headers, - params=params, - opts=__opts__ + url=url, method=method, header_dict=headers, params=params, opts=__opts__ ) - respbody = resp.get('body', None) + respbody = resp.get("body", None) if not respbody: return False - respbodydict = salt.utils.json.loads(resp['body']) - deviceresults = respbodydict['results'] + respbodydict = salt.utils.json.loads(resp["body"]) + deviceresults = respbodydict["results"] return deviceresults def _delete_resource(device_id, client_id, access_token): - authstring = 'Bearer {0}'.format(access_token) + authstring = "Bearer {0}".format(access_token) headers = { - 'Authorization': authstring, - 'Content-Type': 'application/json', - 'Accept': 'application/json' + "Authorization": authstring, + "Content-Type": "application/json", + "Accept": "application/json", } - method = 'DELETE' - url = 'https://api.vistara.io/api/v2/tenants/{0}/rtype/DEVICE/resource/{1}'.format(client_id, device_id) - - resp = salt.utils.http.query( - url=url, - method=method, - header_dict=headers, - opts=__opts__ + method = "DELETE" + url = "https://api.vistara.io/api/v2/tenants/{0}/rtype/DEVICE/resource/{1}".format( + client_id, device_id ) - respbody = resp.get('body', None) + resp = salt.utils.http.query( + url=url, method=method, header_dict=headers, opts=__opts__ + ) + + respbody = resp.get("body", None) if not respbody: return False - respbodydict = salt.utils.json.loads(resp['body']) + respbodydict = salt.utils.json.loads(resp["body"]) return respbodydict def _get_oath2_access_token(client_key, client_secret): - ''' + """ Query the vistara API and get an access_token - ''' + """ if not client_key and not client_secret: log.error( "client_key and client_secret have not been specified " @@ -190,31 +198,27 @@ def _get_oath2_access_token(client_key, client_secret): ) return False - method = 'POST' - url = 'https://api.vistara.io/auth/oauth/token' + method = "POST" + url = "https://api.vistara.io/auth/oauth/token" headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Accept': 'application/json' + "Content-Type": "application/x-www-form-urlencoded", + "Accept": "application/json", } params = { - 'grant_type': 'client_credentials', - 'client_id': client_key, - 'client_secret': client_secret + "grant_type": "client_credentials", + "client_id": client_key, + "client_secret": client_secret, } resp = salt.utils.http.query( - url=url, - method=method, - header_dict=headers, - params=params, - opts=__opts__ + url=url, method=method, header_dict=headers, params=params, opts=__opts__ ) - respbody = resp.get('body', None) + respbody = resp.get("body", None) if not respbody: return False - access_token = salt.utils.json.loads(respbody)['access_token'] + access_token = salt.utils.json.loads(respbody)["access_token"] return access_token diff --git a/salt/runners/winrepo.py b/salt/runners/winrepo.py index 1d4cc0ab8be..bce33db2f21 100644 --- a/salt/runners/winrepo.py +++ b/salt/runners/winrepo.py @@ -1,44 +1,46 @@ # -*- coding: utf-8 -*- -''' +""" Runner to manage Windows software repo -''' +""" # WARNING: Any modules imported here must also be added to # salt/modules/win_repo.py # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os -# Import third party libs -from salt.ext import six - -# Import salt libs -from salt.exceptions import CommandExecutionError, SaltRenderError +import salt.loader +import salt.minion +import salt.template import salt.utils.files import salt.utils.gitfs import salt.utils.msgpack import salt.utils.path -import logging -import salt.minion -import salt.loader -import salt.template + +# Import salt libs +from salt.exceptions import CommandExecutionError, SaltRenderError + +# Import third party libs +from salt.ext import six log = logging.getLogger(__name__) # Global parameters which can be overridden on a per-remote basis -PER_REMOTE_OVERRIDES = ('ssl_verify', 'refspecs') +PER_REMOTE_OVERRIDES = ("ssl_verify", "refspecs") # Fall back to default per-remote-only. This isn't technically needed since # salt.utils.gitfs.GitBase.__init__ will default to # salt.utils.gitfs.PER_REMOTE_ONLY for this value, so this is mainly for # runners and other modules that import salt.runners.winrepo. PER_REMOTE_ONLY = salt.utils.gitfs.PER_REMOTE_ONLY -GLOBAL_ONLY = ('branch',) +GLOBAL_ONLY = ("branch",) def genrepo(opts=None, fire_event=True): - ''' + """ Generate winrepo_cachefile based on sls files in the winrepo_dir opts @@ -53,12 +55,12 @@ def genrepo(opts=None, fire_event=True): .. code-block:: bash salt-run winrepo.genrepo - ''' + """ if opts is None: opts = __opts__ - winrepo_dir = opts['winrepo_dir'] - winrepo_cachefile = opts['winrepo_cachefile'] + winrepo_dir = opts["winrepo_dir"] + winrepo_cachefile = opts["winrepo_cachefile"] ret = {} if not os.path.exists(winrepo_dir): @@ -66,67 +68,63 @@ def genrepo(opts=None, fire_event=True): renderers = salt.loader.render(opts, __salt__) for root, _, files in salt.utils.path.os_walk(winrepo_dir): for name in files: - if name.endswith('.sls'): + if name.endswith(".sls"): try: config = salt.template.compile_template( - os.path.join(root, name), - renderers, - opts['renderer'], - opts['renderer_blacklist'], - opts['renderer_whitelist'] - ) - except SaltRenderError as exc: - log.debug( - 'Failed to render %s.', - os.path.join(root, name) + os.path.join(root, name), + renderers, + opts["renderer"], + opts["renderer_blacklist"], + opts["renderer_whitelist"], ) - log.debug('Error: %s.', exc) + except SaltRenderError as exc: + log.debug("Failed to render %s.", os.path.join(root, name)) + log.debug("Error: %s.", exc) continue if config: revmap = {} for pkgname, versions in six.iteritems(config): - log.debug( - 'Compiling winrepo data for package \'%s\'', - pkgname - ) + log.debug("Compiling winrepo data for package '%s'", pkgname) for version, repodata in six.iteritems(versions): log.debug( - 'Compiling winrepo data for %s version %s', - pkgname, version + "Compiling winrepo data for %s version %s", + pkgname, + version, ) if not isinstance(version, six.string_types): - config[pkgname][six.text_type(version)] = \ - config[pkgname].pop(version) + config[pkgname][six.text_type(version)] = config[ + pkgname + ].pop(version) if not isinstance(repodata, dict): - msg = 'Failed to compile {0}.'.format( + msg = "Failed to compile {0}.".format( os.path.join(root, name) ) log.debug(msg) if fire_event: try: __jid_event__.fire_event( - {'error': msg}, - 'progress' + {"error": msg}, "progress" ) except NameError: log.error( - 'Attempted to fire the an event ' - 'with the following error, but ' - 'event firing is not supported: %s', - msg + "Attempted to fire the an event " + "with the following error, but " + "event firing is not supported: %s", + msg, ) continue - revmap[repodata['full_name']] = pkgname - ret.setdefault('repo', {}).update(config) - ret.setdefault('name_map', {}).update(revmap) + revmap[repodata["full_name"]] = pkgname + ret.setdefault("repo", {}).update(config) + ret.setdefault("name_map", {}).update(revmap) with salt.utils.files.fopen( - os.path.join(winrepo_dir, winrepo_cachefile), 'w+b') as repo: + os.path.join(winrepo_dir, winrepo_cachefile), "w+b" + ) as repo: repo.write(salt.utils.msgpack.dumps(ret)) return ret def update_git_repos(opts=None, clean=False, masterless=False): - ''' + """ Checkout git repos containing Windows Software Package Definitions opts @@ -150,63 +148,70 @@ def update_git_repos(opts=None, clean=False, masterless=False): salt-run winrepo.update_git_repos salt-run winrepo.update_git_repos clean=True - ''' + """ if opts is None: opts = __opts__ - winrepo_dir = opts['winrepo_dir'] - winrepo_remotes = opts['winrepo_remotes'] + winrepo_dir = opts["winrepo_dir"] + winrepo_remotes = opts["winrepo_remotes"] - winrepo_cfg = [(winrepo_remotes, winrepo_dir), - (opts['winrepo_remotes_ng'], opts['winrepo_dir_ng'])] + winrepo_cfg = [ + (winrepo_remotes, winrepo_dir), + (opts["winrepo_remotes_ng"], opts["winrepo_dir_ng"]), + ] ret = {} for remotes, base_dir in winrepo_cfg: - if not any((salt.utils.gitfs.GITPYTHON_VERSION, - salt.utils.gitfs.PYGIT2_VERSION)): + if not any( + (salt.utils.gitfs.GITPYTHON_VERSION, salt.utils.gitfs.PYGIT2_VERSION) + ): # Use legacy code winrepo_result = {} for remote_info in remotes: - if '/' in remote_info: - targetname = remote_info.split('/')[-1] + if "/" in remote_info: + targetname = remote_info.split("/")[-1] else: targetname = remote_info - rev = 'HEAD' + rev = "HEAD" # If a revision is specified, use it. try: rev, remote_url = remote_info.strip().split() except ValueError: remote_url = remote_info - gittarget = os.path.join(base_dir, targetname).replace('.', '_') + gittarget = os.path.join(base_dir, targetname).replace(".", "_") if masterless: - result = __salt__['state.single']('git.latest', - name=remote_url, - rev=rev, - branch='winrepo', - target=gittarget, - force_checkout=True, - force_reset=True) + result = __salt__["state.single"]( + "git.latest", + name=remote_url, + rev=rev, + branch="winrepo", + target=gittarget, + force_checkout=True, + force_reset=True, + ) if isinstance(result, list): # Errors were detected raise CommandExecutionError( - 'Failed up update winrepo remotes: {0}'.format( - '\n'.join(result) + "Failed up update winrepo remotes: {0}".format( + "\n".join(result) ) ) - if 'name' not in result: + if "name" not in result: # Highstate output dict, the results are actually nested # one level down. key = next(iter(result)) result = result[key] else: mminion = salt.minion.MasterMinion(opts) - result = mminion.states['git.latest'](remote_url, - rev=rev, - branch='winrepo', - target=gittarget, - force_checkout=True, - force_reset=True) - winrepo_result[result['name']] = result['result'] + result = mminion.states["git.latest"]( + remote_url, + rev=rev, + branch="winrepo", + target=gittarget, + force_checkout=True, + force_reset=True, + ) + winrepo_result[result["name"]] = result["result"] ret.update(winrepo_result) else: # New winrepo code utilizing salt.utils.gitfs @@ -217,7 +222,8 @@ def update_git_repos(opts=None, clean=False, masterless=False): per_remote_overrides=PER_REMOTE_OVERRIDES, per_remote_only=PER_REMOTE_ONLY, global_only=GLOBAL_ONLY, - cache_root=base_dir) + cache_root=base_dir, + ) winrepo.fetch_remotes() # Since we're not running update(), we need to manually call # clear_old_remotes() to remove directories from remotes that @@ -226,7 +232,7 @@ def update_git_repos(opts=None, clean=False, masterless=False): winrepo.clear_old_remotes() winrepo.checkout() except Exception as exc: # pylint: disable=broad-except - msg = 'Failed to update winrepo_remotes: {0}'.format(exc) + msg = "Failed to update winrepo_remotes: {0}".format(exc) log.error(msg, exc_info_on_loglevel=logging.DEBUG) return msg ret.update(winrepo.winrepo_dirs) diff --git a/salt/scripts.py b/salt/scripts.py index 5e623a578e8..7560a1cedc3 100644 --- a/salt/scripts.py +++ b/salt/scripts.py @@ -1,32 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" This module contains the function calls to execute command line scripts -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys -import time -import signal + +import functools import logging -import functools -import threading -import traceback +import os import signal -import functools +import sys +import threading +import time +import traceback from random import randint -# Import salt libs -from salt.exceptions import SaltSystemExit, SaltClientError, SaltReqTimeoutError import salt.defaults.exitcodes # pylint: disable=unused-import import salt.ext.six as six +# Import salt libs +from salt.exceptions import SaltClientError, SaltReqTimeoutError, SaltSystemExit + log = logging.getLogger(__name__) -def _handle_interrupt(exc, original_exc, hardfail=False, trace=''): - ''' +def _handle_interrupt(exc, original_exc, hardfail=False, trace=""): + """ if hardfailing: If we got the original stacktrace, log it If all cases, raise the original exception @@ -34,7 +34,7 @@ def _handle_interrupt(exc, original_exc, hardfail=False, trace=''): stack. else just let salt exit gracefully - ''' + """ if hardfail: if trace: log.error(trace) @@ -49,23 +49,23 @@ def _handle_signals(client, signum, sigframe): # Ref: https://bugs.python.org/issue23003 trace = traceback.format_exc() except AttributeError: - trace = '' + trace = "" try: hardcrash = client.options.hard_crash except (AttributeError, KeyError): hardcrash = False if signum == signal.SIGINT: - exit_msg = '\nExiting gracefully on Ctrl-c' + exit_msg = "\nExiting gracefully on Ctrl-c" try: - jid = client.local_client.pub_data['jid'] + jid = client.local_client.pub_data["jid"] exit_msg += ( - '\n' - 'This job\'s jid is: {0}\n' - 'The minions may not have all finished running and any remaining ' - 'minions will return upon completion. To look up the return data ' - 'for this job later, run the following command:\n\n' - 'salt-run jobs.lookup_jid {0}'.format(jid) + "\n" + "This job's jid is: {0}\n" + "The minions may not have all finished running and any remaining " + "minions will return upon completion. To look up the return data " + "for this job later, run the following command:\n\n" + "salt-run jobs.lookup_jid {0}".format(jid) ) except (AttributeError, KeyError): pass @@ -74,8 +74,10 @@ def _handle_signals(client, signum, sigframe): _handle_interrupt( SystemExit(exit_msg), - Exception('\nExiting with hard crash on Ctrl-c'), - hardcrash, trace=trace) + Exception("\nExiting with hard crash on Ctrl-c"), + hardcrash, + trace=trace, + ) def _install_signal_handlers(client): @@ -90,42 +92,45 @@ def _install_signal_handlers(client): def salt_master(): - ''' + """ Start the salt master. - ''' + """ import salt.cli.daemons # Fix for setuptools generated scripts, so that it will # work with multiprocessing fork emulation. # (see multiprocessing.forking.get_preparation_data()) - if __name__ != '__main__': - sys.modules['__main__'] = sys.modules[__name__] + if __name__ != "__main__": + sys.modules["__main__"] = sys.modules[__name__] -# REMOVEME after Python 2.7 support is dropped (also the six import) + # REMOVEME after Python 2.7 support is dropped (also the six import) if six.PY2: from salt.utils.versions import warn_until + # Message borrowed from pip's deprecation warning - warn_until('Sodium', - 'Python 2.7 will reach the end of its life on January 1st,' - ' 2020. Please upgrade your Python as Python 2.7 won\'t be' - ' maintained after that date. Salt will drop support for' - ' Python 2.7 in the Sodium release or later.') -# END REMOVEME + warn_until( + "Sodium", + "Python 2.7 will reach the end of its life on January 1st," + " 2020. Please upgrade your Python as Python 2.7 won't be" + " maintained after that date. Salt will drop support for" + " Python 2.7 in the Sodium release or later.", + ) + # END REMOVEME master = salt.cli.daemons.Master() master.start() def minion_process(): - ''' + """ Start a minion process - ''' + """ import salt.utils.platform import salt.utils.process import salt.cli.daemons # salt_minion spawns this function in a new process - salt.utils.process.appendproctitle('KeepAlive') + salt.utils.process.appendproctitle("KeepAlive") def handle_hup(manager, sig, frame): manager.minion.reload() @@ -133,12 +138,12 @@ def minion_process(): lock = threading.RLock() def suicide_when_without_parent(parent_pid): - ''' + """ Have the minion suicide if the parent process is gone NOTE: small race issue where the parent PID could be replace with another process with same PID! - ''' + """ while lock.acquire(blocking=False): lock.release() time.sleep(5) @@ -149,28 +154,30 @@ def minion_process(): except OSError as exc: # forcibly exit, regular sys.exit raises an exception-- which # isn't sufficient in a thread - log.error('Minion process encountered exception: %s', exc) + log.error("Minion process encountered exception: %s", exc) os._exit(salt.defaults.exitcodes.EX_GENERIC) try: if not salt.utils.platform.is_windows(): - thread = threading.Thread(target=suicide_when_without_parent, args=(os.getppid(),)) + thread = threading.Thread( + target=suicide_when_without_parent, args=(os.getppid(),) + ) thread.start() minion = salt.cli.daemons.Minion() - signal.signal(signal.SIGHUP, - functools.partial(handle_hup, - minion)) + signal.signal(signal.SIGHUP, functools.partial(handle_hup, minion)) minion.start() except (SaltClientError, SaltReqTimeoutError, SaltSystemExit) as exc: lock.acquire(blocking=True) - log.warning('Fatal functionality error caught by minion handler:\n', exc_info=True) - log.warning('** Restarting minion **') + log.warning( + "Fatal functionality error caught by minion handler:\n", exc_info=True + ) + log.warning("** Restarting minion **") delay = 60 - if minion is not None and hasattr(minion, 'config'): - delay = minion.config.get('random_reauth_delay', 60) + if minion is not None and hasattr(minion, "config"): + delay = minion.config.get("random_reauth_delay", 60) delay = randint(1, delay) - log.info('waiting random_reauth_delay %ss', delay) + log.info("waiting random_reauth_delay %ss", delay) time.sleep(delay) sys.exit(salt.defaults.exitcodes.SALT_KEEPALIVE) finally: @@ -178,14 +185,15 @@ def minion_process(): def salt_minion(): - ''' + """ Start the salt minion in a subprocess. Auto restart minion on error. - ''' + """ import signal import salt.utils.platform import salt.utils.process + salt.utils.process.notify_systemd() import salt.cli.daemons @@ -194,38 +202,43 @@ def salt_minion(): # Fix for setuptools generated scripts, so that it will # work with multiprocessing fork emulation. # (see multiprocessing.forking.get_preparation_data()) - if __name__ != '__main__': - sys.modules['__main__'] = sys.modules[__name__] + if __name__ != "__main__": + sys.modules["__main__"] = sys.modules[__name__] - if '' in sys.path: - sys.path.remove('') + if "" in sys.path: + sys.path.remove("") if salt.utils.platform.is_windows(): minion = salt.cli.daemons.Minion() minion.start() return -# REMOVEME after Python 2.7 support is dropped (also the six import) + # REMOVEME after Python 2.7 support is dropped (also the six import) elif six.PY2: from salt.utils.versions import warn_until - # Message borrowed from pip's deprecation warning - warn_until('Sodium', - 'Python 2.7 will reach the end of its life on January 1st,' - ' 2020. Please upgrade your Python as Python 2.7 won\'t be' - ' maintained after that date. Salt will drop support for' - ' Python 2.7 in the Sodium release or later.') -# END REMOVEME - if '--disable-keepalive' in sys.argv: - sys.argv.remove('--disable-keepalive') + # Message borrowed from pip's deprecation warning + warn_until( + "Sodium", + "Python 2.7 will reach the end of its life on January 1st," + " 2020. Please upgrade your Python as Python 2.7 won't be" + " maintained after that date. Salt will drop support for" + " Python 2.7 in the Sodium release or later.", + ) + # END REMOVEME + + if "--disable-keepalive" in sys.argv: + sys.argv.remove("--disable-keepalive") minion = salt.cli.daemons.Minion() minion.start() return - def escalate_signal_to_process(pid, signum, sigframe): # pylint: disable=unused-argument - ''' + def escalate_signal_to_process( + pid, signum, sigframe + ): # pylint: disable=unused-argument + """ Escalate the signal received to the multiprocessing process that is actually running the minion - ''' + """ # escalate signal os.kill(pid, signum) @@ -236,15 +249,18 @@ def salt_minion(): try: process = multiprocessing.Process(target=minion_process) process.start() - signal.signal(signal.SIGTERM, - functools.partial(escalate_signal_to_process, - process.pid)) - signal.signal(signal.SIGINT, - functools.partial(escalate_signal_to_process, - process.pid)) - signal.signal(signal.SIGHUP, - functools.partial(escalate_signal_to_process, - process.pid)) + signal.signal( + signal.SIGTERM, + functools.partial(escalate_signal_to_process, process.pid), + ) + signal.signal( + signal.SIGINT, + functools.partial(escalate_signal_to_process, process.pid), + ) + signal.signal( + signal.SIGHUP, + functools.partial(escalate_signal_to_process, process.pid), + ) except Exception: # pylint: disable=broad-except # if multiprocessing does not work minion = salt.cli.daemons.Minion() @@ -273,22 +289,23 @@ def salt_minion(): def proxy_minion_process(queue): - ''' + """ Start a proxy minion process - ''' + """ import salt.cli.daemons import salt.utils.platform + # salt_minion spawns this function in a new process lock = threading.RLock() def suicide_when_without_parent(parent_pid): - ''' + """ Have the minion suicide if the parent process is gone NOTE: there is a small race issue where the parent PID could be replace with another process with the same PID! - ''' + """ while lock.acquire(blocking=False): lock.release() time.sleep(5) @@ -302,7 +319,9 @@ def proxy_minion_process(queue): try: if not salt.utils.platform.is_windows(): - thread = threading.Thread(target=suicide_when_without_parent, args=(os.getppid(),)) + thread = threading.Thread( + target=suicide_when_without_parent, args=(os.getppid(),) + ) thread.start() restart = False @@ -310,8 +329,10 @@ def proxy_minion_process(queue): status = salt.defaults.exitcodes.EX_OK proxyminion = salt.cli.daemons.ProxyMinion() proxyminion.start() - except (Exception, SaltClientError, SaltReqTimeoutError, SaltSystemExit) as exc: # pylint: disable=broad-except - log.error('Proxy Minion failed to start: ', exc_info=True) + # pylint: disable=broad-except + except (Exception, SaltClientError, SaltReqTimeoutError, SaltSystemExit,) as exc: + # pylint: enable=broad-except + log.error("Proxy Minion failed to start: ", exc_info=True) restart = True # status is superfluous since the process will be restarted status = salt.defaults.exitcodes.SALT_KEEPALIVE @@ -322,13 +343,13 @@ def proxy_minion_process(queue): lock.acquire(blocking=True) if restart is True: - log.warning('** Restarting proxy minion **') + log.warning("** Restarting proxy minion **") delay = 60 if proxyminion is not None: - if hasattr(proxyminion, 'config'): - delay = proxyminion.config.get('random_reauth_delay', 60) + if hasattr(proxyminion, "config"): + delay = proxyminion.config.get("random_reauth_delay", 60) random_delay = randint(1, delay) - log.info('Sleeping random_reauth_delay of %s seconds', random_delay) + log.info("Sleeping random_reauth_delay of %s seconds", random_delay) # preform delay after minion resources have been cleaned queue.put(random_delay) else: @@ -337,22 +358,23 @@ def proxy_minion_process(queue): def salt_proxy(): - ''' + """ Start a proxy minion. - ''' + """ import salt.cli.daemons import salt.utils.platform import multiprocessing - if '' in sys.path: - sys.path.remove('') + + if "" in sys.path: + sys.path.remove("") if salt.utils.platform.is_windows(): proxyminion = salt.cli.daemons.ProxyMinion() proxyminion.start() return - if '--disable-keepalive' in sys.argv: - sys.argv.remove('--disable-keepalive') + if "--disable-keepalive" in sys.argv: + sys.argv.remove("--disable-keepalive") proxyminion = salt.cli.daemons.ProxyMinion() proxyminion.start() return @@ -393,13 +415,15 @@ def salt_proxy(): def salt_syndic(): - ''' + """ Start the salt syndic. - ''' + """ import salt.utils.process + salt.utils.process.notify_systemd() import salt.cli.daemons + pid = os.getpid() try: syndic = salt.cli.daemons.Syndic() @@ -409,10 +433,11 @@ def salt_syndic(): def salt_key(): - ''' + """ Manage the authentication keys with salt-key. - ''' + """ import salt.cli.key + try: client = salt.cli.key.SaltKey() _install_signal_handlers(client) @@ -422,48 +447,52 @@ def salt_key(): def salt_cp(): - ''' + """ Publish commands to the salt system from the command line on the master. - ''' + """ import salt.cli.cp + client = salt.cli.cp.SaltCPCli() _install_signal_handlers(client) client.run() def salt_call(): - ''' + """ Directly call a salt command in the modules, does not require a running salt minion to run. - ''' + """ import salt.cli.call - if '' in sys.path: - sys.path.remove('') + + if "" in sys.path: + sys.path.remove("") client = salt.cli.call.SaltCall() _install_signal_handlers(client) client.run() def salt_run(): - ''' + """ Execute a salt convenience routine. - ''' + """ import salt.cli.run - if '' in sys.path: - sys.path.remove('') + + if "" in sys.path: + sys.path.remove("") client = salt.cli.run.SaltRun() _install_signal_handlers(client) client.run() def salt_ssh(): - ''' + """ Execute the salt-ssh system - ''' + """ import salt.cli.ssh - if '' in sys.path: - sys.path.remove('') + + if "" in sys.path: + sys.path.remove("") try: client = salt.cli.ssh.SaltSSH() _install_signal_handlers(client) @@ -474,16 +503,13 @@ def salt_ssh(): hardcrash = client.options.hard_crash except (AttributeError, KeyError): hardcrash = False - _handle_interrupt( - SystemExit(err), - err, - hardcrash, trace=trace) + _handle_interrupt(SystemExit(err), err, hardcrash, trace=trace) def salt_cloud(): - ''' + """ The main function for salt-cloud - ''' + """ # Define 'salt' global so we may use it after ImportError. Otherwise, # UnboundLocalError will be raised. global salt # pylint: disable=W0602 @@ -494,11 +520,11 @@ def salt_cloud(): import salt.cloud.cli except ImportError as e: # No salt cloud on Windows - log.error('Error importing salt cloud: %s', e) - print('salt-cloud is not available in this system') + log.error("Error importing salt cloud: %s", e) + print("salt-cloud is not available in this system") sys.exit(salt.defaults.exitcodes.EX_UNAVAILABLE) - if '' in sys.path: - sys.path.remove('') + if "" in sys.path: + sys.path.remove("") client = salt.cloud.cli.SaltCloud() _install_signal_handlers(client) @@ -506,76 +532,83 @@ def salt_cloud(): def salt_api(): - ''' + """ The main function for salt-api - ''' + """ import salt.utils.process + salt.utils.process.notify_systemd() import salt.cli.api + sapi = salt.cli.api.SaltAPI() # pylint: disable=E1120 sapi.start() def salt_main(): - ''' + """ Publish commands to the salt system from the command line on the master. - ''' + """ import salt.cli.salt - if '' in sys.path: - sys.path.remove('') + + if "" in sys.path: + sys.path.remove("") client = salt.cli.salt.SaltCMD() _install_signal_handlers(client) client.run() def salt_spm(): - ''' + """ The main function for spm, the Salt Package Manager .. versionadded:: 2015.8.0 - ''' + """ import salt.cli.spm + spm = salt.cli.spm.SPM() # pylint: disable=E1120 spm.run() def salt_extend(extension, name, description, salt_dir, merge): - ''' + """ Quickstart for developing on the saltstack installation .. versionadded:: 2016.11.0 - ''' + """ import salt.utils.extend - salt.utils.extend.run(extension=extension, - name=name, - description=description, - salt_dir=salt_dir, - merge=merge) + + salt.utils.extend.run( + extension=extension, + name=name, + description=description, + salt_dir=salt_dir, + merge=merge, + ) def salt_unity(): - ''' + """ Change the args and redirect to another salt script - ''' + """ avail = [] for fun in dir(sys.modules[__name__]): - if fun.startswith('salt'): + if fun.startswith("salt"): avail.append(fun[5:]) if len(sys.argv) < 2: - msg = 'Must pass in a salt command, available commands are:' + msg = "Must pass in a salt command, available commands are:" for cmd in avail: - msg += '\n{0}'.format(cmd) + msg += "\n{0}".format(cmd) print(msg) sys.exit(1) cmd = sys.argv[1] if cmd not in avail: # Fall back to the salt command - sys.argv[0] = 'salt' + sys.argv[0] = "salt" s_fun = salt_main else: - sys.argv[0] = 'salt-{0}'.format(cmd) + sys.argv[0] = "salt-{0}".format(cmd) sys.argv.pop(1) - s_fun = getattr(sys.modules[__name__], 'salt_{0}'.format(cmd)) + s_fun = getattr(sys.modules[__name__], "salt_{0}".format(cmd)) s_fun() diff --git a/salt/sdb/__init__.py b/salt/sdb/__init__.py index df17b622e3b..44e9b8986bf 100644 --- a/salt/sdb/__init__.py +++ b/salt/sdb/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" SDB Module Directory -''' +""" diff --git a/salt/sdb/cache.py b/salt/sdb/cache.py index 5664b46bdd3..ea4eb20aae3 100644 --- a/salt/sdb/cache.py +++ b/salt/sdb/cache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" cache Module :maintainer: SaltStack @@ -43,67 +43,66 @@ it must be specified in the URI: .. code-block:: yaml master_ip: sdb://mastercloudcache/public_ips?bank=cloud/active/ec2/my-ec2-conf/saltmaster -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import salt.cache -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} -__virtualname__ = 'cache' +__virtualname__ = "cache" def __virtual__(): - ''' + """ Only load the module if keyring is installed - ''' + """ return __virtualname__ def set_(key, value, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Set a key/value pair in the cache service - ''' + """ key, profile = _parse_key(key, profile) cache = salt.cache.Cache(__opts__) - cache.store(profile['bank'], key, value) + cache.store(profile["bank"], key, value) return get(key, service, profile) def get(key, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Get a value from the cache service - ''' + """ key, profile = _parse_key(key, profile) cache = salt.cache.Cache(__opts__) - return cache.fetch(profile['bank'], key=key) + return cache.fetch(profile["bank"], key=key) def delete(key, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Get a value from the cache service - ''' + """ key, profile = _parse_key(key, profile) cache = salt.cache.Cache(__opts__) try: - cache.flush(profile['bank'], key=key) + cache.flush(profile["bank"], key=key) return True except Exception: # pylint: disable=broad-except return False def _parse_key(key, profile): - ''' + """ Parse out a key and update the opts with any override data - ''' - comps = key.split('?') + """ + comps = key.split("?") if len(comps) > 1: - for item in comps[1].split('&'): - newkey, newval = item.split('=') + for item in comps[1].split("&"): + newkey, newval = item.split("=") profile[newkey] = newval - if 'cachedir' in profile: - __opts__['cachedir'] = profile['cachedir'] + if "cachedir" in profile: + __opts__["cachedir"] = profile["cachedir"] return comps[0], profile diff --git a/salt/sdb/confidant.py b/salt/sdb/confidant.py index 0020d973e04..40ab5f157b0 100644 --- a/salt/sdb/confidant.py +++ b/salt/sdb/confidant.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" An SDB module for getting credentials from confidant. Configuring the Confidant module @@ -34,7 +34,7 @@ The module can be configured via sdb in the minion config: Module Documentation ==================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -46,6 +46,7 @@ try: # pylint: disable=no-name-in-module import confidant.client import confidant.formatter + HAS_LIBS = True # pylint: enable=no-name-in-module except ImportError: @@ -54,13 +55,13 @@ except ImportError: # Set up logging log = logging.getLogger(__name__) -__virtualname__ = 'confidant' +__virtualname__ = "confidant" def __virtual__(): - ''' + """ Only return if requests and boto are installed. - ''' + """ if HAS_LIBS: return __virtualname__ else: @@ -68,7 +69,7 @@ def __virtual__(): def get(key, profile=None): - ''' + """ Read pillar data from Confidant via its API. CLI Example: @@ -82,28 +83,28 @@ def get(key, profile=None): call succeeded or failed to fetch credentials from confidant (or from local cache). If result is false, the data in credentials or credentials_metadata can't be trusted. - ''' + """ # default to returning failure - ret = {'result': False, 'credentials': None, 'credentials_metadata': None} + ret = {"result": False, "credentials": None, "credentials_metadata": None} profile_data = copy.deepcopy(profile) - if profile_data.get('disabled', False): - ret['result'] = True + if profile_data.get("disabled", False): + ret["result"] = True return ret.get(key) - token_version = profile_data.get('token_version', 1) + token_version = profile_data.get("token_version", 1) try: - url = profile_data['url'] - auth_key = profile_data['auth_key'] - auth_context = profile_data['auth_context'] - role = auth_context['from'] + url = profile_data["url"] + auth_key = profile_data["auth_key"] + auth_context = profile_data["auth_context"] + role = auth_context["from"] except (KeyError, TypeError): - msg = ('profile has undefined url, auth_key or auth_context') + msg = "profile has undefined url, auth_key or auth_context" log.debug(msg) return ret.get(key) - region = profile_data.get('region', 'us-east-1') - token_duration = profile_data.get('token_duration', 60) - retries = profile_data.get('retries', 5) - token_cache_file = profile_data.get('token_cache_file') - backoff = profile_data.get('backoff', 1) + region = profile_data.get("region", "us-east-1") + token_duration = profile_data.get("token_duration", 60) + retries = profile_data.get("retries", 5) + token_cache_file = profile_data.get("token_cache_file") + backoff = profile_data.get("backoff", 1) client = confidant.client.ConfidantClient( url, auth_key, @@ -113,17 +114,14 @@ def get(key, profile=None): token_cache_file=token_cache_file, region=region, retries=retries, - backoff=backoff + backoff=backoff, ) try: - data = client.get_service( - role, - decrypt_blind=True - ) + data = client.get_service(role, decrypt_blind=True) except confidant.client.TokenCreationError: return ret.get(key) - if not data['result']: + if not data["result"]: return ret.get(key) ret = confidant.formatter.combined_credential_pair_format(data) - ret['result'] = True + ret["result"] = True return ret.get(key) diff --git a/salt/sdb/consul.py b/salt/sdb/consul.py index 567e299a377..acf71c2d6a1 100644 --- a/salt/sdb/consul.py +++ b/salt/sdb/consul.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Consul sdb Module :maintainer: SaltStack @@ -26,21 +26,20 @@ requires very little. For example: The ``driver`` refers to the Consul module, all other options are optional. For option details see: https://python-consul.readthedocs.io/en/latest/#consul -''' +""" from __future__ import absolute_import, print_function, unicode_literals from salt.exceptions import CommandExecutionError try: import consul + HAS_CONSUL = True except ImportError: HAS_CONSUL = False -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def set_(key, value, profile=None): @@ -60,15 +59,15 @@ def get(key, profile=None): _, result = conn.kv.get(key) - return result['Value'] if result else None + return result["Value"] if result else None def get_conn(profile): - ''' + """ Return a client object for accessing consul - ''' + """ params = {} - for key in ('host', 'port', 'token', 'scheme', 'consistency', 'dc', 'verify'): + for key in ("host", "port", "token", "scheme", "consistency", "dc", "verify"): if key in profile: params[key] = profile[key] @@ -76,6 +75,6 @@ def get_conn(profile): return consul.Consul(**params) else: raise CommandExecutionError( - '(unable to import consul, ' - 'module most likely not installed. PLease install python-consul)' + "(unable to import consul, " + "module most likely not installed. PLease install python-consul)" ) diff --git a/salt/sdb/couchdb.py b/salt/sdb/couchdb.py index 8ed158f8f91..40465a3b9e3 100644 --- a/salt/sdb/couchdb.py +++ b/salt/sdb/couchdb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" CouchDB sdb Module :maintainer: SaltStack @@ -34,80 +34,83 @@ to do the map-reduce logic necessary to calculate the ID you wish to fetch. Additional contributions to build true map-reduce functionality into this module would be welcome. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libraries import logging from uuid import uuid4 -try: - import couchdb - HAS_COUCH = True -except ImportError: - HAS_COUCH = False # Import Salt libraries from salt.utils.decorators import memoize +try: + import couchdb + + HAS_COUCH = True +except ImportError: + HAS_COUCH = False + + log = logging.getLogger(__name__) # 'set' is a reserved word -__func_alias__ = {'set_': 'set'} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Require the python2-couchdb libraries - ''' + """ return HAS_COUCH @memoize def _construct_uri(profile): - ''' + """ Examine configuration and return a uri for the couchdb server in the following format: .. code-block:: bash http://localhost:5984/ - ''' - return 'http://{host}:{port}'.format(**profile) + """ + return "http://{host}:{port}".format(**profile) def _get_conn(profile): - ''' + """ Get a connection to CouchDB - ''' - DEFAULT_BASE_URL = _construct_uri(profile) or 'http://localhost:5984' + """ + DEFAULT_BASE_URL = _construct_uri(profile) or "http://localhost:5984" server = couchdb.Server() - if profile['database'] not in server: - server.create(profile['database']) + if profile["database"] not in server: + server.create(profile["database"]) return server def set_(key, value, profile=None): - ''' + """ Set a key/value pair in couchdb - ''' + """ db = _get_db(profile) - return db.save({'_id': uuid4().hex, key: value}) + return db.save({"_id": uuid4().hex, key: value}) def get(key, profile=None): - ''' + """ Get a value from couchdb by id - ''' + """ db = _get_db(profile) return db.get(key) def _get_db(profile): - ''' + """ Wraps _get_conn() to return a db - ''' + """ server = _get_conn(profile) db = _get_db(profile) return db diff --git a/salt/sdb/env.py b/salt/sdb/env.py index 99a1ac75cb7..d0a76bd6d6b 100644 --- a/salt/sdb/env.py +++ b/salt/sdb/env.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Environment sdb Module :maintainer: SaltStack @@ -56,35 +56,33 @@ in the environment: compute_name: sdb://osenv/compute ..snip -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import os -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Always load - ''' + """ return True def set_(key, value, profile=None): - ''' + """ Set a key/value pair - ''' + """ # pylint: disable=unused-argument return os.environ.setdefault(key, value) def get(key, profile=None): - ''' + """ Get a value - ''' + """ # pylint: disable=unused-argument return os.environ.get(key) diff --git a/salt/sdb/etcd_db.py b/salt/sdb/etcd_db.py index 4a07905668b..fe2f8f6ce75 100644 --- a/salt/sdb/etcd_db.py +++ b/salt/sdb/etcd_db.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" etcd Database Module :maintainer: SaltStack @@ -30,58 +30,58 @@ is hosting the etcd database and ``etcd.port`` refers to the port on that host. password: sdb://myetcd/mypassword -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging try: import salt.utils.etcd_util + HAS_LIBS = True except ImportError: HAS_LIBS = False log = logging.getLogger(__name__) -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} -__virtualname__ = 'etcd' +__virtualname__ = "etcd" def __virtual__(): - ''' + """ Only load the module if keyring is installed - ''' + """ if HAS_LIBS: return __virtualname__ return False def set_(key, value, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Set a key/value pair in the etcd service - ''' + """ client = _get_conn(profile) client.set(key, value) return get(key, service, profile) def get(key, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Get a value from the etcd service - ''' + """ client = _get_conn(profile) result = client.get(key) return result.value def delete(key, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Get a value from the etcd service - ''' + """ client = _get_conn(profile) try: client.delete(key) @@ -91,7 +91,7 @@ def delete(key, service=None, profile=None): # pylint: disable=W0613 def _get_conn(profile): - ''' + """ Get a connection - ''' + """ return salt.utils.etcd_util.get_conn(profile) diff --git a/salt/sdb/keyring_db.py b/salt/sdb/keyring_db.py index af654e63372..dca0c693d43 100644 --- a/salt/sdb/keyring_db.py +++ b/salt/sdb/keyring_db.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Keyring Database Module :maintainer: SaltStack @@ -43,7 +43,7 @@ examples and documentation, see keyring: https://pypi.python.org/pypi/keyring .. versionadded:: 2014.1.4 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # import python libs @@ -51,49 +51,48 @@ import logging try: import keyring + HAS_LIBS = True except ImportError: HAS_LIBS = False log = logging.getLogger(__name__) -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} -__virtualname__ = 'keyring' +__virtualname__ = "keyring" def __virtual__(): - ''' + """ Only load the module if keyring is installed - ''' + """ if HAS_LIBS: return __virtualname__ return False def set_(key, value, service=None, profile=None): - ''' + """ Set a key/value pair in a keyring service - ''' + """ service = _get_service(service, profile) keyring.set_password(service, key, value) def get(key, service=None, profile=None): - ''' + """ Get a value from a keyring service - ''' + """ service = _get_service(service, profile) return keyring.get_password(service, key) def _get_service(service, profile): - ''' + """ Get a service name - ''' - if isinstance(profile, dict) and 'service' in profile: - return profile['service'] + """ + if isinstance(profile, dict) and "service" in profile: + return profile["service"] return service diff --git a/salt/sdb/memcached.py b/salt/sdb/memcached.py index 48058771a95..bb8128ba2de 100644 --- a/salt/sdb/memcached.py +++ b/salt/sdb/memcached.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Memcached sdb Module :maintainer: SaltStack @@ -29,7 +29,7 @@ and ``mymemcached`` refers to the name that will appear in the URI: password: sdb://mymemcached/mykey -''' +""" from __future__ import absolute_import, print_function, unicode_literals # import python libs @@ -38,39 +38,36 @@ import logging # import Salt libs import salt.utils.memcached - -DEFAULT_HOST = '127.0.0.1' +DEFAULT_HOST = "127.0.0.1" DEFAULT_PORT = 11211 DEFAULT_EXPIRATION = 0 log = logging.getLogger(__name__) -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Only load the module if memcached is installed - ''' + """ if salt.utils.memcached.HAS_LIBS: return True return False def set_(key, value, profile=None): - ''' + """ Set a key/value pair in memcached - ''' + """ conn = salt.utils.memcached.get_conn(profile) - time = profile.get('expire', DEFAULT_EXPIRATION) + time = profile.get("expire", DEFAULT_EXPIRATION) return salt.utils.memcached.set_(conn, key, value, time=time) def get(key, profile=None): - ''' + """ Get a value from memcached - ''' + """ conn = salt.utils.memcached.get_conn(profile) return salt.utils.memcached.get(conn, key) diff --git a/salt/sdb/redis_sdb.py b/salt/sdb/redis_sdb.py index fe45faf9b5e..9f8388b6deb 100644 --- a/salt/sdb/redis_sdb.py +++ b/salt/sdb/redis_sdb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Redis SDB module ================ @@ -23,62 +23,61 @@ requires very little. For example: The ``driver`` refers to the Redis module, all other options are optional. For option details see: https://redis-py.readthedocs.io/en/latest/. -''' +""" from __future__ import absolute_import, print_function, unicode_literals try: import redis + HAS_REDIS = True except ImportError: HAS_REDIS = False -__func_alias__ = { - 'set_': 'set' -} -__virtualname__ = 'redis' +__func_alias__ = {"set_": "set"} +__virtualname__ = "redis" def __virtual__(): - ''' + """ Module virtual name. - ''' + """ if not HAS_REDIS: - return (False, 'Please install python-redis to use this SDB module.') + return (False, "Please install python-redis to use this SDB module.") return __virtualname__ def set_(key, value, profile=None): - ''' + """ Set a value into the Redis SDB. - ''' + """ if not profile: return False redis_kwargs = profile.copy() - redis_kwargs.pop('driver') + redis_kwargs.pop("driver") redis_conn = redis.StrictRedis(**redis_kwargs) return redis_conn.set(key, value) def get(key, profile=None): - ''' + """ Get a value from the Redis SDB. - ''' + """ if not profile: return False redis_kwargs = profile.copy() - redis_kwargs.pop('driver') + redis_kwargs.pop("driver") redis_conn = redis.StrictRedis(**redis_kwargs) return redis_conn.get(key) def delete(key, profile=None): - ''' + """ Delete a key from the Redis SDB. - ''' + """ if not profile: return False redis_kwargs = profile.copy() - redis_kwargs.pop('driver') + redis_kwargs.pop("driver") redis_conn = redis.StrictRedis(**redis_kwargs) return redis_conn.delete(key) diff --git a/salt/sdb/rest.py b/salt/sdb/rest.py index 7d741599641..2acc83f7255 100644 --- a/salt/sdb/rest.py +++ b/salt/sdb/rest.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Generic REST API SDB Module :maintainer: SaltStack @@ -63,10 +63,11 @@ For instance: url: https://api.example.com/users/ params: user: myuser -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import salt.loader @@ -75,59 +76,53 @@ from salt.template import compile_template log = logging.getLogger(__name__) -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def set_(key, value, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Set a key/value pair in the REST interface - ''' + """ return query(key, value, service, profile) def get(key, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Get a value from the REST interface - ''' + """ return query(key, None, service, profile) def query(key, value=None, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Get a value from the REST interface - ''' - comps = key.split('?') + """ + comps = key.split("?") key = comps[0] key_vars = {} - for pair in comps[1].split('&'): - pair_key, pair_val = pair.split('=') + for pair in comps[1].split("&"): + pair_key, pair_val = pair.split("=") key_vars[pair_key] = pair_val - renderer = __opts__.get('renderer', 'jinja|yaml') + renderer = __opts__.get("renderer", "jinja|yaml") rend = salt.loader.render(__opts__, {}) - blacklist = __opts__.get('renderer_blacklist') - whitelist = __opts__.get('renderer_whitelist') + blacklist = __opts__.get("renderer_blacklist") + whitelist = __opts__.get("renderer_whitelist") url = compile_template( - ':string:', + ":string:", rend, renderer, blacklist, whitelist, - input_data=profile[key]['url'], + input_data=profile[key]["url"], **key_vars ) extras = {} for item in profile[key]: - if item not in ('backend', 'url'): + if item not in ("backend", "url"): extras[item] = profile[key][item] - result = http.query( - url, - decode=True, - **extras - ) + result = http.query(url, decode=True, **extras) - return result['dict'] + return result["dict"] diff --git a/salt/sdb/sqlite3.py b/salt/sdb/sqlite3.py index 52357c8b738..cf2095ce752 100644 --- a/salt/sdb/sqlite3.py +++ b/salt/sdb/sqlite3.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" SQLite sdb Module :maintainer: SaltStack @@ -41,75 +41,79 @@ create the table(s) and get and set values. - "CREATE INDEX myidx ON advanced (a)" get_query: "SELECT d FROM advanced WHERE a=:key" set_query: "INSERT OR REPLACE INTO advanced (a, d) VALUES (:key, :value)" -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import codecs + # Import python libs import logging -import codecs -try: - import sqlite3 - HAS_SQLITE3 = True -except ImportError: - HAS_SQLITE3 = False # Import salt libs import salt.utils.msgpack from salt.ext import six +try: + import sqlite3 -DEFAULT_TABLE = 'sdb' + HAS_SQLITE3 = True +except ImportError: + HAS_SQLITE3 = False + + +DEFAULT_TABLE = "sdb" log = logging.getLogger(__name__) -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Only load if sqlite3 is available. - ''' + """ if not HAS_SQLITE3: return False return True -def _quote(s, errors='strict'): - encodable = s.encode('utf-8', errors).decode('utf-8') +def _quote(s, errors="strict"): + encodable = s.encode("utf-8", errors).decode("utf-8") - nul_index = encodable.find('\x00') + nul_index = encodable.find("\x00") if nul_index >= 0: - error = UnicodeEncodeError('NUL-terminated utf-8', encodable, - nul_index, nul_index + 1, 'NUL not allowed') + error = UnicodeEncodeError( + "NUL-terminated utf-8", + encodable, + nul_index, + nul_index + 1, + "NUL not allowed", + ) error_handler = codecs.lookup_error(errors) replacement, _ = error_handler(error) - encodable = encodable.replace('\x00', replacement) + encodable = encodable.replace("\x00", replacement) return '"' + encodable.replace('"', '""') + '"' def _connect(profile): - db = profile['database'] + db = profile["database"] table = None conn = sqlite3.connect(db) cur = conn.cursor() - stmts = profile.get('create_statements') - table = profile.get('table', DEFAULT_TABLE) - idx = _quote(table + '_idx') + stmts = profile.get("create_statements") + table = profile.get("table", DEFAULT_TABLE) + idx = _quote(table + "_idx") table = _quote(table) try: if stmts: for sql in stmts: cur.execute(sql) - elif profile.get('create_table', True): - cur.execute(('CREATE TABLE {0} (key text, ' - 'value blob)').format(table)) - cur.execute(('CREATE UNIQUE INDEX {0} ON {1} ' - '(key)').format(idx, table)) + elif profile.get("create_table", True): + cur.execute(("CREATE TABLE {0} (key text, " "value blob)").format(table)) + cur.execute(("CREATE UNIQUE INDEX {0} ON {1} " "(key)").format(idx, table)) except sqlite3.OperationalError: pass @@ -117,33 +121,38 @@ def _connect(profile): def set_(key, value, profile=None): - ''' + """ Set a key/value pair in sqlite3 - ''' + """ if not profile: return False conn, cur, table = _connect(profile) if six.PY2: - value = buffer(salt.utils.msgpack.packb(value)) # pylint: disable=undefined-variable + # pylint: disable=undefined-variable + value = buffer(salt.utils.msgpack.packb(value)) + # pylint: enable=undefined-variable else: value = memoryview(salt.utils.msgpack.packb(value)) - q = profile.get('set_query', ('INSERT OR REPLACE INTO {0} VALUES ' - '(:key, :value)').format(table)) - conn.execute(q, {'key': key, 'value': value}) + q = profile.get( + "set_query", + ("INSERT OR REPLACE INTO {0} VALUES " "(:key, :value)").format(table), + ) + conn.execute(q, {"key": key, "value": value}) conn.commit() return True def get(key, profile=None): - ''' + """ Get a value from sqlite3 - ''' + """ if not profile: return None _, cur, table = _connect(profile) - q = profile.get('get_query', ('SELECT value FROM {0} WHERE ' - 'key=:key'.format(table))) - res = cur.execute(q, {'key': key}) + q = profile.get( + "get_query", ("SELECT value FROM {0} WHERE " "key=:key".format(table)) + ) + res = cur.execute(q, {"key": key}) res = res.fetchone() if not res: return None diff --git a/salt/sdb/tism.py b/salt/sdb/tism.py index 5690b47b4c5..89ebd597f7d 100644 --- a/salt/sdb/tism.py +++ b/salt/sdb/tism.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" tISM - the Immutable Secrets Manager SDB Module :maintainer: tISM @@ -26,17 +26,19 @@ configuration. driver: tism url: https://my.tismd:8080/decrypt token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6MSwiZXhwIjoxNTg1MTExNDYwLCJqdGkiOiI3NnA5cWNiMWdtdmw4Iiwia2V5cyI6WyJBTEwiXX0.RtAhG6Uorf5xnSf4Ya_GwJnoHkCsql4r1_hiOeDSLzo -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.http as http + # Import Salt libs import salt.utils.json -import salt.utils.http as http -from salt.ext import six from salt.exceptions import SaltConfigurationError +from salt.ext import six log = logging.getLogger(__name__) @@ -44,35 +46,35 @@ __virtualname__ = "tism" def __virtual__(): - ''' + """ This module has no other system dependencies - ''' + """ return __virtualname__ def get(key, service=None, profile=None): # pylint: disable=W0613 - ''' + """ Get a decrypted secret from the tISMd API - ''' + """ - if not profile.get('url') or not profile.get('token'): - raise SaltConfigurationError("url and/or token missing from the tism sdb profile") + if not profile.get("url") or not profile.get("token"): + raise SaltConfigurationError( + "url and/or token missing from the tism sdb profile" + ) - request = {"token": profile['token'], "encsecret": key} + request = {"token": profile["token"], "encsecret": key} result = http.query( - profile['url'], - method='POST', - data=salt.utils.json.dumps(request), + profile["url"], method="POST", data=salt.utils.json.dumps(request), ) - decrypted = result.get('body') + decrypted = result.get("body") if not decrypted: log.warning( - 'tism.get sdb decryption request failed with error %s', - result.get('error', 'unknown') + "tism.get sdb decryption request failed with error %s", + result.get("error", "unknown"), ) - return 'ERROR' + six.text_type(result.get('status', 'unknown')) + return "ERROR" + six.text_type(result.get("status", "unknown")) return decrypted diff --git a/salt/sdb/vault.py b/salt/sdb/vault.py index 99b5b27e103..4cb8add381d 100644 --- a/salt/sdb/vault.py +++ b/salt/sdb/vault.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Vault SDB Module :maintainer: SaltStack @@ -38,63 +38,76 @@ The above URI is analogous to running the following vault command: .. code-block:: bash $ vault read -field=mypassword secret/passwords -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging + import salt.exceptions log = logging.getLogger(__name__) -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def set_(key, value, profile=None): - ''' + """ Set a key/value pair in the vault service - ''' - if '?' in key: - path, key = key.split('?') + """ + if "?" in key: + path, key = key.split("?") else: - path, key = key.rsplit('/', 1) + path, key = key.rsplit("/", 1) + data = {key: value} + + version2 = __utils__["vault.is_v2"](path) + if version2["v2"]: + path = version2["data"] + data = {"data": data} try: - url = 'v1/{0}'.format(path) - data = {key: value} - response = __utils__['vault.make_request']( - 'POST', - url, - profile, - json=data) + url = "v1/{0}".format(path) + response = __utils__["vault.make_request"]("POST", url, json=data) if response.status_code != 204: response.raise_for_status() return True except Exception as e: # pylint: disable=broad-except - log.error('Failed to write secret! %s: %s', type(e).__name__, e) + log.error("Failed to write secret! %s: %s", type(e).__name__, e) raise salt.exceptions.CommandExecutionError(e) def get(key, profile=None): - ''' + """ Get a value from the vault service - ''' - if '?' in key: - path, key = key.split('?') + """ + if "?" in key: + path, key = key.split("?") else: - path, key = key.rsplit('/', 1) + path, key = key.rsplit("/", 1) + + version2 = __utils__["vault.is_v2"](path) + if version2["v2"]: + path = version2["data"] try: - url = 'v1/{0}'.format(path) - response = __utils__['vault.make_request']('GET', url, profile) + url = "v1/{0}".format(path) + response = __utils__["vault.make_request"]("GET", url, profile) + if response.status_code == 404: + return None if response.status_code != 200: response.raise_for_status() - data = response.json()['data'] + data = response.json()["data"] - return data[key] + if version2["v2"]: + if key in data["data"]: + return data["data"][key] + else: + if key in data: + return data[key] + return None except Exception as e: # pylint: disable=broad-except - log.error('Failed to read secret! %s: %s', type(e).__name__, e) + log.error("Failed to read secret! %s: %s", type(e).__name__, e) raise salt.exceptions.CommandExecutionError(e) diff --git a/salt/sdb/yaml.py b/salt/sdb/yaml.py index 3d4660dfe9a..814d8a44e89 100644 --- a/salt/sdb/yaml.py +++ b/salt/sdb/yaml.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Pull sdb values from a YAML file :maintainer: SaltStack @@ -35,26 +35,28 @@ Optional configuration: merge_list: false gpg: true -Setting the ``gpg`` option to ``true`` (default is ``false``) will decrypt embedded -GPG-encrypted data using the :py:mod:`GPG renderer <salt.renderers.gpg>`. -''' +.. versionadded:: 2018.3.0 + +Setting the ``gpg`` option to ``true`` (default is ``false``) will decrypt +embedded GPG-encrypted data using the :py:mod:`GPG renderer +<salt.renderers.gpg>`. +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import salt.exceptions import salt.loader -import salt.utils.data -import salt.utils.files -import salt.utils.dictupdate import salt.renderers.gpg +import salt.utils.data +import salt.utils.dictupdate +import salt.utils.files log = logging.getLogger(__name__) -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): @@ -62,39 +64,40 @@ def __virtual__(): def set_(*args, **kwargs): # pylint: disable=W0613 - ''' + """ Setting a value is not supported; edit the YAML files directly - ''' + """ raise salt.exceptions.NotImplemented() def get(key, profile=None): # pylint: disable=W0613 - ''' + """ Get a value from the dictionary - ''' + """ data = _get_values(profile) # Decrypt SDB data if specified in the profile - if profile and profile.get('gpg', False): + if profile and profile.get("gpg", False): return salt.utils.data.traverse_dict_and_list(_decrypt(data), key, None) return salt.utils.data.traverse_dict_and_list(data, key, None) def _get_values(profile=None): - ''' + """ Retrieve all the referenced files, deserialize, then merge them together - ''' + """ profile = profile or {} serializers = salt.loader.serializers(__opts__) ret = {} - for fname in profile.get('files', []): + for fname in profile.get("files", []): try: with salt.utils.files.flopen(fname) as yamlfile: contents = serializers.yaml.deserialize(yamlfile) ret = salt.utils.dictupdate.merge( - ret, contents, **profile.get('merge', {})) + ret, contents, **profile.get("merge", {}) + ) except IOError: log.error("File '%s' not found ", fname) except TypeError as exc: @@ -103,7 +106,7 @@ def _get_values(profile=None): def _decrypt(data): - ''' + """ Pass the dictionary through the GPG renderer to decrypt encrypted values. - ''' - return salt.loader.render(__opts__, __salt__)['gpg'](data) + """ + return salt.loader.render(__opts__, __salt__)["gpg"](data) diff --git a/salt/serializers/__init__.py b/salt/serializers/__init__.py index c3f89bd6e3f..ea76a7013ce 100644 --- a/salt/serializers/__init__.py +++ b/salt/serializers/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" salt.serializers ~~~~~~~~~~~~~~~~~~~~~~ @@ -13,9 +13,10 @@ :available: flag that tells if the serializer is available (all dependencies are met etc.) -''' +""" from __future__ import absolute_import, print_function, unicode_literals + from salt.exceptions import SaltException, SaltRenderError diff --git a/salt/serializers/configparser.py b/salt/serializers/configparser.py index 1889d950266..f391f36333b 100644 --- a/salt/serializers/configparser.py +++ b/salt/serializers/configparser.py @@ -1,33 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" salt.serializers.configparser ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2016.3.0 Implements a configparser serializer. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.ext.six.moves.configparser as configparser # pylint: disable=E0611 + # Import Salt Libs from salt.ext import six -import salt.ext.six.moves.configparser as configparser # pylint: disable=E0611 from salt.serializers import DeserializationError, SerializationError -__all__ = ['deserialize', 'serialize', 'available'] +__all__ = ["deserialize", "serialize", "available"] available = True def deserialize(stream_or_string, **options): - ''' + """ Deserialize any string or stream like object into a Python data structure. :param stream_or_string: stream or string to deserialize. :param options: options given to lower configparser module. - ''' + """ if six.PY3: cp = configparser.ConfigParser(**options) @@ -58,17 +59,21 @@ def deserialize(stream_or_string, **options): def serialize(obj, **options): - ''' + """ Serialize Python data to a configparser formatted string or file. :param obj: the data structure to serialize :param options: options given to lower configparser module. - ''' + """ try: if not isinstance(obj, dict): - raise TypeError("configparser can only serialize dictionaries, not {0}".format(type(obj))) - fp = options.pop('fp', None) + raise TypeError( + "configparser can only serialize dictionaries, not {0}".format( + type(obj) + ) + ) + fp = options.pop("fp", None) if six.PY3: cp = configparser.ConfigParser(**options) else: @@ -93,9 +98,9 @@ def _is_defaultsect(section_name): def _read_dict(cp, dictionary): - ''' + """ Cribbed from python3's ConfigParser.read_dict function. - ''' + """ for section, keys in dictionary.items(): section = six.text_type(section) diff --git a/salt/serializers/json.py b/salt/serializers/json.py index 22f831aabfd..45e791fa4f6 100644 --- a/salt/serializers/json.py +++ b/salt/serializers/json.py @@ -1,47 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" salt.serializers.json ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implements JSON serializer. It's just a wrapper around json (or simplejson if available). -''' +""" from __future__ import absolute_import, print_function, unicode_literals +# Import Salt libs +import salt.utils.json + +# Import 3rd-party libs +from salt.ext import six +from salt.serializers import DeserializationError, SerializationError + try: import simplejson as _json except ImportError: import json as _json # pylint: disable=blacklisted-import -# Import Salt libs -import salt.utils.json -from salt.serializers import DeserializationError, SerializationError -# Import 3rd-party libs -from salt.ext import six - -__all__ = ['deserialize', 'serialize', 'available'] +__all__ = ["deserialize", "serialize", "available"] available = True def deserialize(stream_or_string, **options): - ''' + """ Deserialize any string or stream like object into a Python data structure. :param stream_or_string: stream or string to deserialize. :param options: options given to lower json/simplejson module. - ''' + """ try: if not isinstance(stream_or_string, (bytes, six.string_types)): - return salt.utils.json.load( - stream_or_string, _json_module=_json, **options) + return salt.utils.json.load(stream_or_string, _json_module=_json, **options) if isinstance(stream_or_string, bytes): - stream_or_string = stream_or_string.decode('utf-8') + stream_or_string = stream_or_string.decode("utf-8") return salt.utils.json.loads(stream_or_string, _json_module=_json) except Exception as error: # pylint: disable=broad-except @@ -49,15 +49,15 @@ def deserialize(stream_or_string, **options): def serialize(obj, **options): - ''' + """ Serialize Python data to JSON. :param obj: the data structure to serialize :param options: options given to lower json/simplejson module. - ''' + """ try: - if 'fp' in options: + if "fp" in options: return salt.utils.json.dump(obj, _json_module=_json, **options) else: return salt.utils.json.dumps(obj, _json_module=_json, **options) diff --git a/salt/serializers/msgpack.py b/salt/serializers/msgpack.py index 6082686cbae..321f6200be7 100644 --- a/salt/serializers/msgpack.py +++ b/salt/serializers/msgpack.py @@ -1,30 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" salt.serializers.msgpack ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implements MsgPack serializer. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging # Import Salt Libs import salt.utils.msgpack -from salt.serializers import DeserializationError, SerializationError # Import 3rd-party libs from salt.ext import six +from salt.serializers import DeserializationError, SerializationError log = logging.getLogger(__name__) available = salt.utils.msgpack.HAS_MSGPACK if not available: + def _fail(): - raise RuntimeError('msgpack is not available') + raise RuntimeError("msgpack is not available") def _serialize(obj, **options): _fail() @@ -32,6 +34,7 @@ if not available: def _deserialize(stream_or_string, **options): _fail() + elif salt.utils.msgpack.version >= (0, 2, 0): def _serialize(obj, **options): @@ -42,22 +45,23 @@ elif salt.utils.msgpack.version >= (0, 2, 0): def _deserialize(stream_or_string, **options): try: - options.setdefault('use_list', True) - options.setdefault('encoding', 'utf-8') + options.setdefault("use_list", True) + options.setdefault("encoding", "utf-8") return salt.utils.msgpack.loads(stream_or_string, **options) except Exception as error: # pylint: disable=broad-except raise DeserializationError(error) + else: # msgpack.version < 0.2.0 def _encoder(obj): - ''' + """ Since OrderedDict is identified as a dictionary, we can't make use of msgpack custom types, we will need to convert by hand. This means iterating through all elements of dictionaries, lists and tuples. - ''' + """ if isinstance(obj, dict): data = [(key, _encoder(value)) for key, value in six.iteritems(obj)] return dict(data) @@ -76,26 +80,27 @@ else: # msgpack.version < 0.2.0 raise SerializationError(error) def _deserialize(stream_or_string, **options): - options.setdefault('use_list', True) + options.setdefault("use_list", True) try: obj = salt.utils.msgpack.loads(stream_or_string) return _decoder(obj) except Exception as error: # pylint: disable=broad-except raise DeserializationError(error) + serialize = _serialize deserialize = _deserialize -serialize.__doc__ = ''' +serialize.__doc__ = """ Serialize Python data to MsgPack. :param obj: the data structure to serialize :param options: options given to lower msgpack module. -''' +""" -deserialize.__doc__ = ''' +deserialize.__doc__ = """ Deserialize any string of stream like object into a Python data structure. :param stream_or_string: stream or string to deserialize. :param options: options given to lower msgpack module. -''' +""" diff --git a/salt/serializers/python.py b/salt/serializers/python.py index 7272413d898..d83d82e40cc 100644 --- a/salt/serializers/python.py +++ b/salt/serializers/python.py @@ -1,43 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" salt.serializers.python ~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2016.3.0 Implements a Python serializer (via pprint.format) -''' +""" from __future__ import absolute_import, unicode_literals + import pprint +import salt.utils.json + try: import simplejson as _json except ImportError: import json as _json # pylint: disable=blacklisted-import -import salt.utils.json -__all__ = ['serialize', 'available'] +__all__ = ["serialize", "available"] available = True def serialize(obj, **options): - ''' + """ Serialize Python data to a Python string representation (via pprint.format) :param obj: the data structure to serialize :param options: options given to pprint.format - ''' + """ - #round-trip this through JSON to avoid OrderedDict types + # round-trip this through JSON to avoid OrderedDict types # there's probably a more performant way to do this... # TODO remove json round-trip when all dataset will use # serializers return pprint.pformat( salt.utils.json.loads( - salt.utils.json.dumps(obj, _json_module=_json), - _json_module=_json + salt.utils.json.dumps(obj, _json_module=_json), _json_module=_json ), **options ) diff --git a/salt/serializers/toml.py b/salt/serializers/toml.py index 725a5e0c56c..a9976ba7e2a 100644 --- a/salt/serializers/toml.py +++ b/salt/serializers/toml.py @@ -1,45 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" salt.serializers.toml ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implements TOML serializer. It's just a wrapper around pytoml module. -''' +""" from __future__ import absolute_import -# Import pytoml -try: - import pytoml as toml - available = True -except ImportError: - available = False - -# Import Salt libs -from salt.serializers import DeserializationError, SerializationError - # Import 3rd-party libs from salt.ext import six -__all__ = ['deserialize', 'serialize', 'available'] +# Import Salt libs +from salt.serializers import DeserializationError, SerializationError + +# Import pytoml +try: + import pytoml as toml + + available = True +except ImportError: + available = False + + +__all__ = ["deserialize", "serialize", "available"] def deserialize(stream_or_string, **options): - ''' + """ Deserialize from TOML into Python data structure. :param stream_or_string: toml stream or string to deserialize. :param options: options given to lower pytoml module. - ''' + """ try: if not isinstance(stream_or_string, (bytes, six.string_types)): return toml.load(stream_or_string, **options) if isinstance(stream_or_string, bytes): - stream_or_string = stream_or_string.decode('utf-8') + stream_or_string = stream_or_string.decode("utf-8") return toml.loads(stream_or_string) except Exception as error: # pylint: disable=broad-except @@ -47,16 +49,16 @@ def deserialize(stream_or_string, **options): def serialize(obj, **options): - ''' + """ Serialize Python data to TOML. :param obj: the data structure to serialize. :param options: options given to lower pytoml module. - ''' + """ try: - if 'file_out' in options: - return toml.dump(obj, options['file_out'], **options) + if "file_out" in options: + return toml.dump(obj, options["file_out"], **options) else: return toml.dumps(obj, **options) except Exception as error: # pylint: disable=broad-except diff --git a/salt/serializers/yaml.py b/salt/serializers/yaml.py index de6f153f751..1061d1180a4 100644 --- a/salt/serializers/yaml.py +++ b/salt/serializers/yaml.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" salt.serializers.yaml ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -7,87 +7,84 @@ Underneath, it is based on pyyaml and use the safe dumper and loader. It also use C bindings if they are available. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import datetime import logging import yaml +from salt.ext import six +from salt.serializers import DeserializationError, SerializationError +from salt.utils.odict import OrderedDict from yaml.constructor import ConstructorError from yaml.scanner import ScannerError -from salt.serializers import DeserializationError, SerializationError -from salt.ext import six -from salt.utils.odict import OrderedDict - -__all__ = ['deserialize', 'serialize', 'available'] +__all__ = ["deserialize", "serialize", "available"] log = logging.getLogger(__name__) available = True # prefer C bindings over python when available -BaseLoader = getattr(yaml, 'CSafeLoader', yaml.SafeLoader) -BaseDumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper) +BaseLoader = getattr(yaml, "CSafeLoader", yaml.SafeLoader) +BaseDumper = getattr(yaml, "CSafeDumper", yaml.SafeDumper) ERROR_MAP = { - ("found character '\\t' " - "that cannot start any token"): 'Illegal tab character' + ("found character '\\t' " "that cannot start any token"): "Illegal tab character" } def deserialize(stream_or_string, **options): - ''' + """ Deserialize any string of stream like object into a Python data structure. :param stream_or_string: stream or string to deserialize. :param options: options given to lower yaml module. - ''' + """ - options.setdefault('Loader', Loader) + options.setdefault("Loader", Loader) try: return yaml.load(stream_or_string, **options) except ScannerError as error: - log.exception('Error encountered while deserializing') - err_type = ERROR_MAP.get(error.problem, 'Unknown yaml render error') + log.exception("Error encountered while deserializing") + err_type = ERROR_MAP.get(error.problem, "Unknown yaml render error") line_num = error.problem_mark.line + 1 - raise DeserializationError(err_type, - line_num, - error.problem_mark.buffer) + raise DeserializationError(err_type, line_num, error.problem_mark.buffer) except ConstructorError as error: - log.exception('Error encountered while deserializing') + log.exception("Error encountered while deserializing") raise DeserializationError(error) except Exception as error: # pylint: disable=broad-except - log.exception('Error encountered while deserializing') + log.exception("Error encountered while deserializing") raise DeserializationError(error) def serialize(obj, **options): - ''' + """ Serialize Python data to YAML. :param obj: the data structure to serialize :param options: options given to lower yaml module. - ''' + """ - options.setdefault('Dumper', Dumper) - options.setdefault('default_flow_style', None) + options.setdefault("Dumper", Dumper) + options.setdefault("default_flow_style", None) try: response = yaml.dump(obj, **options) - if response.endswith('\n...\n'): + if response.endswith("\n...\n"): return response[:-5] - if response.endswith('\n'): + if response.endswith("\n"): return response[:-1] return response except Exception as error: # pylint: disable=broad-except - log.exception('Error encountered while serializing') + log.exception("Error encountered while serializing") raise SerializationError(error) class EncryptedString(str): - yaml_tag = '!encrypted' + yaml_tag = "!encrypted" @staticmethod def yaml_constructor(loader, tag, node): @@ -99,26 +96,28 @@ class EncryptedString(str): class Loader(BaseLoader): # pylint: disable=W0232 - '''Overwrites Loader as not for pollute legacy Loader''' + """Overwrites Loader as not for pollute legacy Loader""" Loader.add_multi_constructor(EncryptedString.yaml_tag, EncryptedString.yaml_constructor) -Loader.add_multi_constructor('tag:yaml.org,2002:null', Loader.construct_yaml_null) -Loader.add_multi_constructor('tag:yaml.org,2002:bool', Loader.construct_yaml_bool) -Loader.add_multi_constructor('tag:yaml.org,2002:int', Loader.construct_yaml_int) -Loader.add_multi_constructor('tag:yaml.org,2002:float', Loader.construct_yaml_float) -Loader.add_multi_constructor('tag:yaml.org,2002:binary', Loader.construct_yaml_binary) -Loader.add_multi_constructor('tag:yaml.org,2002:timestamp', Loader.construct_yaml_timestamp) -Loader.add_multi_constructor('tag:yaml.org,2002:omap', Loader.construct_yaml_omap) -Loader.add_multi_constructor('tag:yaml.org,2002:pairs', Loader.construct_yaml_pairs) -Loader.add_multi_constructor('tag:yaml.org,2002:set', Loader.construct_yaml_set) -Loader.add_multi_constructor('tag:yaml.org,2002:str', Loader.construct_yaml_str) -Loader.add_multi_constructor('tag:yaml.org,2002:seq', Loader.construct_yaml_seq) -Loader.add_multi_constructor('tag:yaml.org,2002:map', Loader.construct_yaml_map) +Loader.add_multi_constructor("tag:yaml.org,2002:null", Loader.construct_yaml_null) +Loader.add_multi_constructor("tag:yaml.org,2002:bool", Loader.construct_yaml_bool) +Loader.add_multi_constructor("tag:yaml.org,2002:int", Loader.construct_yaml_int) +Loader.add_multi_constructor("tag:yaml.org,2002:float", Loader.construct_yaml_float) +Loader.add_multi_constructor("tag:yaml.org,2002:binary", Loader.construct_yaml_binary) +Loader.add_multi_constructor( + "tag:yaml.org,2002:timestamp", Loader.construct_yaml_timestamp +) +Loader.add_multi_constructor("tag:yaml.org,2002:omap", Loader.construct_yaml_omap) +Loader.add_multi_constructor("tag:yaml.org,2002:pairs", Loader.construct_yaml_pairs) +Loader.add_multi_constructor("tag:yaml.org,2002:set", Loader.construct_yaml_set) +Loader.add_multi_constructor("tag:yaml.org,2002:str", Loader.construct_yaml_str) +Loader.add_multi_constructor("tag:yaml.org,2002:seq", Loader.construct_yaml_seq) +Loader.add_multi_constructor("tag:yaml.org,2002:map", Loader.construct_yaml_map) class Dumper(BaseDumper): # pylint: disable=W0232 - '''Overwrites Dumper as not for pollute legacy Dumper''' + """Overwrites Dumper as not for pollute legacy Dumper""" Dumper.add_multi_representer(EncryptedString, EncryptedString.yaml_dumper) diff --git a/salt/serializers/yamlex.py b/salt/serializers/yamlex.py index 47a667a68d1..bf9b2782481 100644 --- a/salt/serializers/yamlex.py +++ b/salt/serializers/yamlex.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" salt.serializers.yamlex ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -98,37 +98,37 @@ placeholder: {!aggregate foo: {baz: inga}} Document is defacto an aggregate mapping. -''' +""" # pylint: disable=invalid-name,no-member,missing-docstring,no-self-use # pylint: disable=too-few-public-methods,too-many-public-methods # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import datetime import logging +# Import 3rd-party libs +import yaml +from salt.ext import six # Import Salt Libs from salt.serializers import DeserializationError, SerializationError -from salt.utils.aggregation import aggregate, Map, Sequence +from salt.utils.aggregation import Map, Sequence, aggregate from salt.utils.odict import OrderedDict - -# Import 3rd-party libs -import yaml -from yaml.nodes import MappingNode from yaml.constructor import ConstructorError +from yaml.nodes import MappingNode from yaml.scanner import ScannerError -from salt.ext import six -__all__ = ['deserialize', 'serialize', 'available'] +__all__ = ["deserialize", "serialize", "available"] log = logging.getLogger(__name__) available = True # prefer C bindings over python when available -BaseLoader = getattr(yaml, 'CSafeLoader', yaml.SafeLoader) +BaseLoader = getattr(yaml, "CSafeLoader", yaml.SafeLoader) if six.PY3: # CSafeDumper causes repr errors in python3, so use the pure Python one try: @@ -139,89 +139,87 @@ if six.PY3: # Here just in case, but yaml.dumper.SafeDumper should always exist BaseDumper = yaml.SafeDumper else: - BaseDumper = getattr(yaml, 'CSafeDumper', yaml.SafeDumper) + BaseDumper = getattr(yaml, "CSafeDumper", yaml.SafeDumper) ERROR_MAP = { - ("found character '\\t' " - "that cannot start any token"): 'Illegal tab character' + ("found character '\\t' " "that cannot start any token"): "Illegal tab character" } def deserialize(stream_or_string, **options): - ''' + """ Deserialize any string of stream like object into a Python data structure. :param stream_or_string: stream or string to deserialize. :param options: options given to lower yaml module. - ''' + """ - options.setdefault('Loader', Loader) + options.setdefault("Loader", Loader) try: return yaml.load(stream_or_string, **options) except ScannerError as error: - log.exception('Error encountered while deserializing') - err_type = ERROR_MAP.get(error.problem, 'Unknown yaml render error') + log.exception("Error encountered while deserializing") + err_type = ERROR_MAP.get(error.problem, "Unknown yaml render error") line_num = error.problem_mark.line + 1 - raise DeserializationError(err_type, - line_num, - error.problem_mark.buffer) + raise DeserializationError(err_type, line_num, error.problem_mark.buffer) except ConstructorError as error: - log.exception('Error encountered while deserializing') + log.exception("Error encountered while deserializing") raise DeserializationError(error) except Exception as error: # pylint: disable=broad-except - log.exception('Error encountered while deserializing') + log.exception("Error encountered while deserializing") raise DeserializationError(error) def serialize(obj, **options): - ''' + """ Serialize Python data to YAML. :param obj: the data structure to serialize :param options: options given to lower yaml module. - ''' + """ - options.setdefault('Dumper', Dumper) - options.setdefault('default_flow_style', None) + options.setdefault("Dumper", Dumper) + options.setdefault("default_flow_style", None) try: response = yaml.dump(obj, **options) - if response.endswith('\n...\n'): + if response.endswith("\n...\n"): return response[:-5] - if response.endswith('\n'): + if response.endswith("\n"): return response[:-1] return response except Exception as error: # pylint: disable=broad-except - log.exception('Error encountered while serializing') + log.exception("Error encountered while serializing") raise SerializationError(error) class Loader(BaseLoader): # pylint: disable=W0232 - ''' + """ Create a custom YAML loader that uses the custom constructor. This allows for the YAML loading defaults to be manipulated based on needs within salt to make things like sls file more intuitive. - ''' + """ - DEFAULT_SCALAR_TAG = 'tag:yaml.org,2002:str' - DEFAULT_SEQUENCE_TAG = 'tag:yaml.org,2002:seq' - DEFAULT_MAPPING_TAG = 'tag:yaml.org,2002:omap' + DEFAULT_SCALAR_TAG = "tag:yaml.org,2002:str" + DEFAULT_SEQUENCE_TAG = "tag:yaml.org,2002:seq" + DEFAULT_MAPPING_TAG = "tag:yaml.org,2002:omap" def compose_document(self): node = BaseLoader.compose_document(self) - node.tag = '!aggregate' + node.tag = "!aggregate" return node def construct_yaml_omap(self, node): - ''' + """ Build the SLSMap - ''' + """ sls_map = SLSMap() if not isinstance(node, MappingNode): raise ConstructorError( None, None, - 'expected a mapping node, but found {0}'.format(node.id), - node.start_mark) + "expected a mapping node, but found {0}".format(node.id), + node.start_mark, + ) self.flatten_mapping(node) @@ -229,12 +227,12 @@ class Loader(BaseLoader): # pylint: disable=W0232 # !reset instruction applies on document only. # It tells to reset previous decoded value for this present key. - reset = key_node.tag == '!reset' + reset = key_node.tag == "!reset" # even if !aggregate tag apply only to values and not keys # it's a reason to act as a such nazi. - if key_node.tag == '!aggregate': - log.warning('!aggregate applies on values only, not on keys') + if key_node.tag == "!aggregate": + log.warning("!aggregate applies on values only, not on keys") value_node.tag = key_node.tag key_node.tag = self.resolve_sls_tag(key_node)[0] @@ -242,8 +240,9 @@ class Loader(BaseLoader): # pylint: disable=W0232 try: hash(key) except TypeError: - err = ('While constructing a mapping {0} found unacceptable ' - 'key {1}').format(node.start_mark, key_node.start_mark) + err = ( + "While constructing a mapping {0} found unacceptable " "key {1}" + ).format(node.start_mark, key_node.start_mark) raise ConstructorError(err) value = self.construct_object(value_node, deep=False) if key in sls_map and not reset: @@ -252,37 +251,36 @@ class Loader(BaseLoader): # pylint: disable=W0232 return sls_map def construct_sls_str(self, node): - ''' + """ Build the SLSString. - ''' + """ # Ensure obj is str, not py2 unicode or py3 bytes obj = self.construct_scalar(node) if six.PY2: - obj = obj.encode('utf-8') + obj = obj.encode("utf-8") return SLSString(obj) def construct_sls_int(self, node): - ''' + """ Verify integers and pass them in correctly is they are declared as octal - ''' - if node.value == '0': + """ + if node.value == "0": pass - elif node.value.startswith('0') \ - and not node.value.startswith(('0b', '0x')): - node.value = node.value.lstrip('0') + elif node.value.startswith("0") and not node.value.startswith(("0b", "0x")): + node.value = node.value.lstrip("0") # If value was all zeros, node.value would have been reduced to # an empty string. Change it to '0'. - if node.value == '': - node.value = '0' + if node.value == "": + node.value = "0" return int(node.value) def construct_sls_aggregate(self, node): try: tag, deep = self.resolve_sls_tag(node) except Exception: # pylint: disable=broad-except - raise ConstructorError('unable to build reset') + raise ConstructorError("unable to build reset") node = copy.copy(node) node.tag = tag @@ -299,7 +297,7 @@ class Loader(BaseLoader): # pylint: disable=W0232 try: tag, deep = self.resolve_sls_tag(node) except Exception: # pylint: disable=broad-except - raise ConstructorError('unable to build reset') + raise ConstructorError("unable to build reset") node = copy.copy(node) node.tag = tag @@ -318,28 +316,36 @@ class Loader(BaseLoader): # pylint: disable=W0232 tag = self.DEFAULT_MAPPING_TAG deep = True else: - raise ConstructorError('unable to resolve tag') + raise ConstructorError("unable to resolve tag") return tag, deep -Loader.add_constructor('!aggregate', Loader.construct_sls_aggregate) # custom type -Loader.add_constructor('!reset', Loader.construct_sls_reset) # custom type -Loader.add_constructor('tag:yaml.org,2002:omap', Loader.construct_yaml_omap) # our overwrite -Loader.add_constructor('tag:yaml.org,2002:str', Loader.construct_sls_str) # our overwrite -Loader.add_constructor('tag:yaml.org,2002:int', Loader.construct_sls_int) # our overwrite -Loader.add_multi_constructor('tag:yaml.org,2002:null', Loader.construct_yaml_null) -Loader.add_multi_constructor('tag:yaml.org,2002:bool', Loader.construct_yaml_bool) -Loader.add_multi_constructor('tag:yaml.org,2002:float', Loader.construct_yaml_float) -Loader.add_multi_constructor('tag:yaml.org,2002:binary', Loader.construct_yaml_binary) -Loader.add_multi_constructor('tag:yaml.org,2002:timestamp', Loader.construct_yaml_timestamp) -Loader.add_multi_constructor('tag:yaml.org,2002:pairs', Loader.construct_yaml_pairs) -Loader.add_multi_constructor('tag:yaml.org,2002:set', Loader.construct_yaml_set) -Loader.add_multi_constructor('tag:yaml.org,2002:seq', Loader.construct_yaml_seq) -Loader.add_multi_constructor('tag:yaml.org,2002:map', Loader.construct_yaml_map) +Loader.add_constructor("!aggregate", Loader.construct_sls_aggregate) # custom type +Loader.add_constructor("!reset", Loader.construct_sls_reset) # custom type +Loader.add_constructor( + "tag:yaml.org,2002:omap", Loader.construct_yaml_omap +) # our overwrite +Loader.add_constructor( + "tag:yaml.org,2002:str", Loader.construct_sls_str +) # our overwrite +Loader.add_constructor( + "tag:yaml.org,2002:int", Loader.construct_sls_int +) # our overwrite +Loader.add_multi_constructor("tag:yaml.org,2002:null", Loader.construct_yaml_null) +Loader.add_multi_constructor("tag:yaml.org,2002:bool", Loader.construct_yaml_bool) +Loader.add_multi_constructor("tag:yaml.org,2002:float", Loader.construct_yaml_float) +Loader.add_multi_constructor("tag:yaml.org,2002:binary", Loader.construct_yaml_binary) +Loader.add_multi_constructor( + "tag:yaml.org,2002:timestamp", Loader.construct_yaml_timestamp +) +Loader.add_multi_constructor("tag:yaml.org,2002:pairs", Loader.construct_yaml_pairs) +Loader.add_multi_constructor("tag:yaml.org,2002:set", Loader.construct_yaml_set) +Loader.add_multi_constructor("tag:yaml.org,2002:seq", Loader.construct_yaml_seq) +Loader.add_multi_constructor("tag:yaml.org,2002:map", Loader.construct_yaml_map) class SLSMap(OrderedDict): - ''' + """ Ensures that dict str() and repr() are YAML friendly. .. code-block:: python @@ -352,7 +358,7 @@ class SLSMap(OrderedDict): >>> print sls_map.__str__() {a: b, c: null} - ''' + """ def __str__(self): return serialize(self, default_flow_style=True) @@ -362,7 +368,7 @@ class SLSMap(OrderedDict): class SLSString(str): - ''' + """ Ensures that str str() and repr() are YAML friendly. .. code-block:: python @@ -375,7 +381,7 @@ class SLSString(str): >>> print sls_scalar "foo" - ''' + """ def __str__(self): return serialize(self, default_style='"') @@ -393,18 +399,21 @@ class AggregatedSequence(Sequence): class Dumper(BaseDumper): # pylint: disable=W0232 - ''' + """ sls dumper. - ''' + """ + def represent_odict(self, data): - return self.represent_mapping('tag:yaml.org,2002:map', list(data.items())) + return self.represent_mapping("tag:yaml.org,2002:map", list(data.items())) Dumper.add_multi_representer(type(None), Dumper.represent_none) if six.PY2: Dumper.add_multi_representer(six.binary_type, Dumper.represent_str) Dumper.add_multi_representer(six.text_type, Dumper.represent_unicode) - Dumper.add_multi_representer(long, Dumper.represent_long) # pylint: disable=incompatible-py3-code,undefined-variable + # pylint: disable=incompatible-py3-code,undefined-variable + Dumper.add_multi_representer(long, Dumper.represent_long) + # pylint: enable=incompatible-py3-code,undefined-variable else: Dumper.add_multi_representer(six.binary_type, Dumper.represent_binary) Dumper.add_multi_representer(six.text_type, Dumper.represent_str) @@ -413,7 +422,9 @@ Dumper.add_multi_representer(int, Dumper.represent_int) Dumper.add_multi_representer(float, Dumper.represent_float) Dumper.add_multi_representer(list, Dumper.represent_list) Dumper.add_multi_representer(tuple, Dumper.represent_list) -Dumper.add_multi_representer(dict, Dumper.represent_odict) # make every dict like obj to be represented as a map +Dumper.add_multi_representer( + dict, Dumper.represent_odict +) # make every dict like obj to be represented as a map Dumper.add_multi_representer(set, Dumper.represent_set) Dumper.add_multi_representer(datetime.date, Dumper.represent_date) Dumper.add_multi_representer(datetime.datetime, Dumper.represent_datetime) @@ -421,9 +432,9 @@ Dumper.add_multi_representer(None, Dumper.represent_undefined) def merge_recursive(obj_a, obj_b, level=False): - ''' + """ Merge obj_b into obj_a. - ''' - return aggregate(obj_a, obj_b, level, - map_class=AggregatedMap, - sequence_class=AggregatedSequence) + """ + return aggregate( + obj_a, obj_b, level, map_class=AggregatedMap, sequence_class=AggregatedSequence + ) diff --git a/salt/spm/__init__.py b/salt/spm/__init__.py index f3ad06b9d0a..ab22dc3b91f 100644 --- a/salt/spm/__init__.py +++ b/salt/spm/__init__.py @@ -1,46 +1,49 @@ # -*- coding: utf-8 -*- -''' +""" This module provides the point of entry to SPM, the Salt Package Manager .. versionadded:: 2015.8.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import tarfile -import shutil + import hashlib import logging +import os +import shutil import sys -try: - import pwd - import grp -except ImportError: - pass +import tarfile + +import salt.cache # Import Salt libs import salt.client import salt.config import salt.loader -import salt.cache import salt.syspaths as syspaths -from salt.ext import six -from salt.ext.six import string_types -from salt.ext.six.moves import input -from salt.ext.six.moves import filter -from salt.template import compile_template import salt.utils.files import salt.utils.http as http import salt.utils.path import salt.utils.platform import salt.utils.win_functions import salt.utils.yaml +from salt.ext import six +from salt.ext.six import string_types +from salt.ext.six.moves import filter, input +from salt.template import compile_template + +try: + import pwd + import grp +except ImportError: + pass + # Get logging started log = logging.getLogger(__name__) -FILE_TYPES = ('c', 'd', 'g', 'l', 'r', 's', 'm') +FILE_TYPES = ("c", "d", "g", "l", "r", "s", "m") # c: config file # d: documentation file # g: ghost file (i.e. the file contents are not included in the package payload) @@ -51,48 +54,47 @@ FILE_TYPES = ('c', 'd', 'g', 'l', 'r', 's', 'm') class SPMException(Exception): - ''' + """ Base class for SPMClient exceptions - ''' + """ class SPMInvocationError(SPMException): - ''' + """ Wrong number of arguments or other usage error - ''' + """ class SPMPackageError(SPMException): - ''' + """ Problem with package file or package installation - ''' + """ class SPMDatabaseError(SPMException): - ''' + """ SPM database not found, etc - ''' + """ class SPMOperationCanceled(SPMException): - ''' + """ SPM install or uninstall was canceled - ''' + """ class SPMClient(object): - ''' + """ Provide an SPM Client - ''' + """ + def __init__(self, ui, opts=None): # pylint: disable=W0231 self.ui = ui if not opts: - opts = salt.config.spm_config( - os.path.join(syspaths.CONFIG_DIR, 'spm') - ) + opts = salt.config.spm_config(os.path.join(syspaths.CONFIG_DIR, "spm")) self.opts = opts - self.db_prov = self.opts.get('spm_db_provider', 'sqlite3') - self.files_prov = self.opts.get('spm_files_provider', 'local') + self.db_prov = self.opts.get("spm_db_provider", "sqlite3") + self.files_prov = self.opts.get("spm_files_provider", "local") self._prep_pkgdb() self._prep_pkgfiles() self.db_conn = None @@ -107,44 +109,44 @@ class SPMClient(object): def _init(self): if not self.db_conn: - self.db_conn = self._pkgdb_fun('init') + self.db_conn = self._pkgdb_fun("init") if not self.files_conn: - self.files_conn = self._pkgfiles_fun('init') + self.files_conn = self._pkgfiles_fun("init") def _close(self): if self.db_conn: self.db_conn.close() def run(self, args): - ''' + """ Run the SPM command - ''' + """ command = args[0] try: - if command == 'install': + if command == "install": self._install(args) - elif command == 'local': + elif command == "local": self._local(args) - elif command == 'repo': + elif command == "repo": self._repo(args) - elif command == 'remove': + elif command == "remove": self._remove(args) - elif command == 'build': + elif command == "build": self._build(args) - elif command == 'update_repo': + elif command == "update_repo": self._download_repo_metadata(args) - elif command == 'create_repo': + elif command == "create_repo": self._create_repo(args) - elif command == 'files': + elif command == "files": self._list_files(args) - elif command == 'info': + elif command == "info": self._info(args) - elif command == 'list': + elif command == "list": self._list(args) - elif command == 'close': + elif command == "close": self._close() else: - raise SPMInvocationError('Invalid command \'{0}\''.format(command)) + raise SPMInvocationError("Invalid command '{0}'".format(command)) except SPMException as exc: self.ui.error(six.text_type(exc)) @@ -152,102 +154,104 @@ class SPMClient(object): try: return getattr(getattr(self.pkgdb, self.db_prov), func)(*args, **kwargs) except AttributeError: - return self.pkgdb['{0}.{1}'.format(self.db_prov, func)](*args, **kwargs) + return self.pkgdb["{0}.{1}".format(self.db_prov, func)](*args, **kwargs) def _pkgfiles_fun(self, func, *args, **kwargs): try: - return getattr(getattr(self.pkgfiles, self.files_prov), func)(*args, **kwargs) + return getattr(getattr(self.pkgfiles, self.files_prov), func)( + *args, **kwargs + ) except AttributeError: - return self.pkgfiles['{0}.{1}'.format(self.files_prov, func)](*args, **kwargs) + return self.pkgfiles["{0}.{1}".format(self.files_prov, func)]( + *args, **kwargs + ) def _list(self, args): - ''' + """ Process local commands - ''' + """ args.pop(0) command = args[0] - if command == 'packages': + if command == "packages": self._list_packages(args) - elif command == 'files': + elif command == "files": self._list_files(args) - elif command == 'repos': + elif command == "repos": self._repo_list(args) else: - raise SPMInvocationError('Invalid list command \'{0}\''.format(command)) + raise SPMInvocationError("Invalid list command '{0}'".format(command)) def _local(self, args): - ''' + """ Process local commands - ''' + """ args.pop(0) command = args[0] - if command == 'install': + if command == "install": self._local_install(args) - elif command == 'files': + elif command == "files": self._local_list_files(args) - elif command == 'info': + elif command == "info": self._local_info(args) else: - raise SPMInvocationError('Invalid local command \'{0}\''.format(command)) + raise SPMInvocationError("Invalid local command '{0}'".format(command)) def _repo(self, args): - ''' + """ Process repo commands - ''' + """ args.pop(0) command = args[0] - if command == 'list': + if command == "list": self._repo_list(args) - elif command == 'packages': + elif command == "packages": self._repo_packages(args) - elif command == 'search': + elif command == "search": self._repo_packages(args, search=True) - elif command == 'update': + elif command == "update": self._download_repo_metadata(args) - elif command == 'create': + elif command == "create": self._create_repo(args) else: - raise SPMInvocationError('Invalid repo command \'{0}\''.format(command)) + raise SPMInvocationError("Invalid repo command '{0}'".format(command)) def _repo_packages(self, args, search=False): - ''' + """ List packages for one or more configured repos - ''' + """ packages = [] repo_metadata = self._get_repo_metadata() for repo in repo_metadata: - for pkg in repo_metadata[repo]['packages']: + for pkg in repo_metadata[repo]["packages"]: if args[1] in pkg: - version = repo_metadata[repo]['packages'][pkg]['info']['version'] - release = repo_metadata[repo]['packages'][pkg]['info']['release'] + version = repo_metadata[repo]["packages"][pkg]["info"]["version"] + release = repo_metadata[repo]["packages"][pkg]["info"]["release"] packages.append((pkg, version, release, repo)) for pkg in sorted(packages): - self.ui.status( - '{0}\t{1}-{2}\t{3}'.format(pkg[0], pkg[1], pkg[2], pkg[3]) - ) + self.ui.status("{0}\t{1}-{2}\t{3}".format(pkg[0], pkg[1], pkg[2], pkg[3])) return packages def _repo_list(self, args): - ''' + """ List configured repos This can be called either as a ``repo`` command or a ``list`` command - ''' + """ repo_metadata = self._get_repo_metadata() for repo in repo_metadata: self.ui.status(repo) def _install(self, args): - ''' + """ Install a package from a repo - ''' + """ if len(args) < 2: - raise SPMInvocationError('A package must be specified') + raise SPMInvocationError("A package must be specified") caller_opts = self.opts.copy() - caller_opts['file_client'] = 'local' + caller_opts["file_client"] = "local" self.caller = salt.client.Caller(mopts=caller_opts) - self.client = salt.client.get_local_client(self.opts['conf_file']) + self.client = salt.client.get_local_client(self.opts["conf_file"]) cache = salt.cache.Cache(self.opts) packages = args[1:] @@ -256,28 +260,28 @@ class SPMClient(object): recommended = [] to_install = [] for pkg in packages: - if pkg.endswith('.spm'): - if self._pkgfiles_fun('path_exists', pkg): - comps = pkg.split('-') - comps = os.path.split('-'.join(comps[:-2])) + if pkg.endswith(".spm"): + if self._pkgfiles_fun("path_exists", pkg): + comps = pkg.split("-") + comps = os.path.split("-".join(comps[:-2])) pkg_name = comps[-1] - formula_tar = tarfile.open(pkg, 'r:bz2') - formula_ref = formula_tar.extractfile('{0}/FORMULA'.format(pkg_name)) + formula_tar = tarfile.open(pkg, "r:bz2") + formula_ref = formula_tar.extractfile( + "{0}/FORMULA".format(pkg_name) + ) formula_def = salt.utils.yaml.safe_load(formula_ref) file_map[pkg_name] = pkg to_, op_, re_ = self._check_all_deps( - pkg_name=pkg_name, - pkg_file=pkg, - formula_def=formula_def + pkg_name=pkg_name, pkg_file=pkg, formula_def=formula_def ) to_install.extend(to_) optional.extend(op_) recommended.extend(re_) formula_tar.close() else: - raise SPMInvocationError('Package file {0} not found'.format(pkg)) + raise SPMInvocationError("Package file {0} not found".format(pkg)) else: to_, op_, re_ = self._check_all_deps(pkg_name=pkg) to_install.extend(to_) @@ -286,18 +290,22 @@ class SPMClient(object): optional = set(filter(len, optional)) if optional: - self.ui.status('The following dependencies are optional:\n\t{0}\n'.format( - '\n\t'.join(optional) - )) + self.ui.status( + "The following dependencies are optional:\n\t{0}\n".format( + "\n\t".join(optional) + ) + ) recommended = set(filter(len, recommended)) if recommended: - self.ui.status('The following dependencies are recommended:\n\t{0}\n'.format( - '\n\t'.join(recommended) - )) + self.ui.status( + "The following dependencies are recommended:\n\t{0}\n".format( + "\n\t".join(recommended) + ) + ) to_install = set(filter(len, to_install)) - msg = 'Installing packages:\n\t{0}\n'.format('\n\t'.join(to_install)) - if not self.opts['assume_yes']: + msg = "Installing packages:\n\t{0}\n".format("\n\t".join(to_install)) + if not self.opts["assume_yes"]: self.ui.confirm(msg) repo_metadata = self._get_repo_metadata() @@ -309,198 +317,207 @@ class SPMClient(object): else: for repo in repo_metadata: repo_info = repo_metadata[repo] - if package in repo_info['packages']: + if package in repo_info["packages"]: dl_package = False - repo_ver = repo_info['packages'][package]['info']['version'] - repo_rel = repo_info['packages'][package]['info']['release'] - repo_url = repo_info['info']['url'] + repo_ver = repo_info["packages"][package]["info"]["version"] + repo_rel = repo_info["packages"][package]["info"]["release"] + repo_url = repo_info["info"]["url"] if package in dl_list: # Check package version, replace if newer version - if repo_ver == dl_list[package]['version']: + if repo_ver == dl_list[package]["version"]: # Version is the same, check release - if repo_rel > dl_list[package]['release']: + if repo_rel > dl_list[package]["release"]: dl_package = True - elif repo_rel == dl_list[package]['release']: + elif repo_rel == dl_list[package]["release"]: # Version and release are the same, give # preference to local (file://) repos - if dl_list[package]['source'].startswith('file://'): - if not repo_url.startswith('file://'): + if dl_list[package]["source"].startswith("file://"): + if not repo_url.startswith("file://"): dl_package = True - elif repo_ver > dl_list[package]['version']: + elif repo_ver > dl_list[package]["version"]: dl_package = True else: dl_package = True if dl_package is True: # Put together download directory - cache_path = os.path.join( - self.opts['spm_cache_dir'], - repo - ) + cache_path = os.path.join(self.opts["spm_cache_dir"], repo) # Put together download paths - dl_url = '{0}/{1}'.format( - repo_info['info']['url'], - repo_info['packages'][package]['filename'] + dl_url = "{0}/{1}".format( + repo_info["info"]["url"], + repo_info["packages"][package]["filename"], ) out_file = os.path.join( - cache_path, - repo_info['packages'][package]['filename'] + cache_path, repo_info["packages"][package]["filename"] ) dl_list[package] = { - 'version': repo_ver, - 'release': repo_rel, - 'source': dl_url, - 'dest_dir': cache_path, - 'dest_file': out_file, + "version": repo_ver, + "release": repo_rel, + "source": dl_url, + "dest_dir": cache_path, + "dest_file": out_file, } for package in dl_list: - dl_url = dl_list[package]['source'] - cache_path = dl_list[package]['dest_dir'] - out_file = dl_list[package]['dest_file'] + dl_url = dl_list[package]["source"] + cache_path = dl_list[package]["dest_dir"] + out_file = dl_list[package]["dest_file"] # Make sure download directory exists if not os.path.exists(cache_path): os.makedirs(cache_path) # Download the package - if dl_url.startswith('file://'): - dl_url = dl_url.replace('file://', '') + if dl_url.startswith("file://"): + dl_url = dl_url.replace("file://", "") shutil.copyfile(dl_url, out_file) else: - with salt.utils.files.fopen(out_file, 'w') as outf: - outf.write(self._query_http(dl_url, repo_info['info'])) + with salt.utils.files.fopen(out_file, "w") as outf: + outf.write(self._query_http(dl_url, repo_info["info"])) # First we download everything, then we install for package in dl_list: - out_file = dl_list[package]['dest_file'] + out_file = dl_list[package]["dest_file"] # Kick off the install self._install_indv_pkg(package, out_file) return def _local_install(self, args, pkg_name=None): - ''' + """ Install a package from a file - ''' + """ if len(args) < 2: - raise SPMInvocationError('A package file must be specified') + raise SPMInvocationError("A package file must be specified") self._install(args) def _check_all_deps(self, pkg_name=None, pkg_file=None, formula_def=None): - ''' + """ Starting with one package, check all packages for dependencies - ''' + """ if pkg_file and not os.path.exists(pkg_file): - raise SPMInvocationError('Package file {0} not found'.format(pkg_file)) + raise SPMInvocationError("Package file {0} not found".format(pkg_file)) self.repo_metadata = self._get_repo_metadata() if not formula_def: for repo in self.repo_metadata: - if not isinstance(self.repo_metadata[repo]['packages'], dict): + if not isinstance(self.repo_metadata[repo]["packages"], dict): continue - if pkg_name in self.repo_metadata[repo]['packages']: - formula_def = self.repo_metadata[repo]['packages'][pkg_name]['info'] + if pkg_name in self.repo_metadata[repo]["packages"]: + formula_def = self.repo_metadata[repo]["packages"][pkg_name]["info"] if not formula_def: - raise SPMInvocationError('Unable to read formula for {0}'.format(pkg_name)) + raise SPMInvocationError("Unable to read formula for {0}".format(pkg_name)) # Check to see if the package is already installed - pkg_info = self._pkgdb_fun('info', pkg_name, self.db_conn) + pkg_info = self._pkgdb_fun("info", pkg_name, self.db_conn) pkgs_to_install = [] - if pkg_info is None or self.opts['force']: + if pkg_info is None or self.opts["force"]: pkgs_to_install.append(pkg_name) - elif pkg_info is not None and not self.opts['force']: + elif pkg_info is not None and not self.opts["force"]: raise SPMPackageError( - 'Package {0} already installed, not installing again'.format(formula_def['name']) + "Package {0} already installed, not installing again".format( + formula_def["name"] + ) ) optional_install = [] recommended_install = [] - if 'dependencies' in formula_def or 'optional' in formula_def or 'recommended' in formula_def: + if ( + "dependencies" in formula_def + or "optional" in formula_def + or "recommended" in formula_def + ): self.avail_pkgs = {} for repo in self.repo_metadata: - if not isinstance(self.repo_metadata[repo]['packages'], dict): + if not isinstance(self.repo_metadata[repo]["packages"], dict): continue - for pkg in self.repo_metadata[repo]['packages']: + for pkg in self.repo_metadata[repo]["packages"]: self.avail_pkgs[pkg] = repo needs, unavail, optional, recommended = self._resolve_deps(formula_def) if len(unavail) > 0: raise SPMPackageError( - 'Cannot install {0}, the following dependencies are needed:\n\n{1}'.format( - formula_def['name'], '\n'.join(unavail)) + "Cannot install {0}, the following dependencies are needed:\n\n{1}".format( + formula_def["name"], "\n".join(unavail) + ) ) if optional: optional_install.extend(optional) for dep_pkg in optional: - pkg_info = self._pkgdb_fun('info', formula_def['name']) + pkg_info = self._pkgdb_fun("info", formula_def["name"]) msg = dep_pkg if isinstance(pkg_info, dict): - msg = '{0} [Installed]'.format(dep_pkg) + msg = "{0} [Installed]".format(dep_pkg) optional_install.append(msg) if recommended: recommended_install.extend(recommended) for dep_pkg in recommended: - pkg_info = self._pkgdb_fun('info', formula_def['name']) + pkg_info = self._pkgdb_fun("info", formula_def["name"]) msg = dep_pkg if isinstance(pkg_info, dict): - msg = '{0} [Installed]'.format(dep_pkg) + msg = "{0} [Installed]".format(dep_pkg) recommended_install.append(msg) if needs: pkgs_to_install.extend(needs) for dep_pkg in needs: - pkg_info = self._pkgdb_fun('info', formula_def['name']) + pkg_info = self._pkgdb_fun("info", formula_def["name"]) msg = dep_pkg if isinstance(pkg_info, dict): - msg = '{0} [Installed]'.format(dep_pkg) + msg = "{0} [Installed]".format(dep_pkg) return pkgs_to_install, optional_install, recommended_install def _install_indv_pkg(self, pkg_name, pkg_file): - ''' + """ Install one individual package - ''' - self.ui.status('... installing {0}'.format(pkg_name)) - formula_tar = tarfile.open(pkg_file, 'r:bz2') - formula_ref = formula_tar.extractfile('{0}/FORMULA'.format(pkg_name)) + """ + self.ui.status("... installing {0}".format(pkg_name)) + formula_tar = tarfile.open(pkg_file, "r:bz2") + formula_ref = formula_tar.extractfile("{0}/FORMULA".format(pkg_name)) formula_def = salt.utils.yaml.safe_load(formula_ref) - for field in ('version', 'release', 'summary', 'description'): + for field in ("version", "release", "summary", "description"): if field not in formula_def: - raise SPMPackageError('Invalid package: the {0} was not found'.format(field)) + raise SPMPackageError( + "Invalid package: the {0} was not found".format(field) + ) pkg_files = formula_tar.getmembers() # First pass: check for files that already exist - existing_files = self._pkgfiles_fun('check_existing', pkg_name, pkg_files, formula_def) + existing_files = self._pkgfiles_fun( + "check_existing", pkg_name, pkg_files, formula_def + ) - if existing_files and not self.opts['force']: - raise SPMPackageError('Not installing {0} due to existing files:\n\n{1}'.format( - pkg_name, '\n'.join(existing_files)) + if existing_files and not self.opts["force"]: + raise SPMPackageError( + "Not installing {0} due to existing files:\n\n{1}".format( + pkg_name, "\n".join(existing_files) + ) ) # We've decided to install - self._pkgdb_fun('register_pkg', pkg_name, formula_def, self.db_conn) + self._pkgdb_fun("register_pkg", pkg_name, formula_def, self.db_conn) # Run the pre_local_state script, if present - if 'pre_local_state' in formula_def: - high_data = self._render(formula_def['pre_local_state'], formula_def) - ret = self.caller.cmd('state.high', data=high_data) - if 'pre_tgt_state' in formula_def: - log.debug('Executing pre_tgt_state script') - high_data = self._render(formula_def['pre_tgt_state']['data'], formula_def) - tgt = formula_def['pre_tgt_state']['tgt'] + if "pre_local_state" in formula_def: + high_data = self._render(formula_def["pre_local_state"], formula_def) + ret = self.caller.cmd("state.high", data=high_data) + if "pre_tgt_state" in formula_def: + log.debug("Executing pre_tgt_state script") + high_data = self._render(formula_def["pre_tgt_state"]["data"], formula_def) + tgt = formula_def["pre_tgt_state"]["tgt"] ret = self.client.run_job( - tgt=formula_def['pre_tgt_state']['tgt'], - fun='state.high', - tgt_type=formula_def['pre_tgt_state'].get('tgt_type', 'glob'), - timout=self.opts['timeout'], + tgt=formula_def["pre_tgt_state"]["tgt"], + fun="state.high", + tgt_type=formula_def["pre_tgt_state"].get("tgt_type", "glob"), + timout=self.opts["timeout"], data=high_data, ) @@ -509,11 +526,11 @@ class SPMClient(object): if salt.utils.platform.is_windows(): uname = gname = salt.utils.win_functions.get_current_user() uname_sid = salt.utils.win_functions.get_sid_from_name(uname) - uid = self.opts.get('spm_uid', uname_sid) - gid = self.opts.get('spm_gid', uname_sid) + uid = self.opts.get("spm_uid", uname_sid) + gid = self.opts.get("spm_gid", uname_sid) else: - uid = self.opts.get('spm_uid', os.getuid()) - gid = self.opts.get('spm_gid', os.getgid()) + uid = self.opts.get("spm_uid", os.getuid()) + gid = self.opts.get("spm_gid", os.getgid()) uname = pwd.getpwuid(uid)[0] gname = grp.getgrgid(gid)[0] @@ -524,66 +541,70 @@ class SPMClient(object): member.uname = uname member.gname = gname - out_path = self._pkgfiles_fun('install_file', - pkg_name, - formula_tar, - member, - formula_def, - self.files_conn) + out_path = self._pkgfiles_fun( + "install_file", + pkg_name, + formula_tar, + member, + formula_def, + self.files_conn, + ) if out_path is not False: if member.isdir(): - digest = '' + digest = "" else: - self._verbose('Installing file {0} to {1}'.format(member.name, out_path), log.trace) + self._verbose( + "Installing file {0} to {1}".format(member.name, out_path), + log.trace, + ) file_hash = hashlib.sha1() - digest = self._pkgfiles_fun('hash_file', - os.path.join(out_path, member.name), - file_hash, - self.files_conn) - self._pkgdb_fun('register_file', - pkg_name, - member, - out_path, - digest, - self.db_conn) + digest = self._pkgfiles_fun( + "hash_file", + os.path.join(out_path, member.name), + file_hash, + self.files_conn, + ) + self._pkgdb_fun( + "register_file", pkg_name, member, out_path, digest, self.db_conn + ) # Run the post_local_state script, if present - if 'post_local_state' in formula_def: - log.debug('Executing post_local_state script') - high_data = self._render(formula_def['post_local_state'], formula_def) - self.caller.cmd('state.high', data=high_data) - if 'post_tgt_state' in formula_def: - log.debug('Executing post_tgt_state script') - high_data = self._render(formula_def['post_tgt_state']['data'], formula_def) - tgt = formula_def['post_tgt_state']['tgt'] + if "post_local_state" in formula_def: + log.debug("Executing post_local_state script") + high_data = self._render(formula_def["post_local_state"], formula_def) + self.caller.cmd("state.high", data=high_data) + if "post_tgt_state" in formula_def: + log.debug("Executing post_tgt_state script") + high_data = self._render(formula_def["post_tgt_state"]["data"], formula_def) + tgt = formula_def["post_tgt_state"]["tgt"] ret = self.client.run_job( - tgt=formula_def['post_tgt_state']['tgt'], - fun='state.high', - tgt_type=formula_def['post_tgt_state'].get('tgt_type', 'glob'), - timout=self.opts['timeout'], + tgt=formula_def["post_tgt_state"]["tgt"], + fun="state.high", + tgt_type=formula_def["post_tgt_state"].get("tgt_type", "glob"), + timout=self.opts["timeout"], data=high_data, ) formula_tar.close() def _resolve_deps(self, formula_def): - ''' + """ Return a list of packages which need to be installed, to resolve all dependencies - ''' - pkg_info = self.pkgdb['{0}.info'.format(self.db_prov)](formula_def['name']) + """ + pkg_info = self.pkgdb["{0}.info".format(self.db_prov)](formula_def["name"]) if not isinstance(pkg_info, dict): pkg_info = {} can_has = {} cant_has = [] - if 'dependencies' in formula_def and formula_def['dependencies'] is None: - formula_def['dependencies'] = '' - for dep in formula_def.get('dependencies', '').split(','): + if "dependencies" in formula_def and formula_def["dependencies"] is None: + formula_def["dependencies"] = "" + for dep in formula_def.get("dependencies", "").split(","): dep = dep.strip() if not dep: continue - if self.pkgdb['{0}.info'.format(self.db_prov)](dep): + if self.pkgdb["{0}.info".format(self.db_prov)](dep): continue if dep in self.avail_pkgs: @@ -591,8 +612,8 @@ class SPMClient(object): else: cant_has.append(dep) - optional = formula_def.get('optional', '').split(',') - recommended = formula_def.get('recommended', '').split(',') + optional = formula_def.get("optional", "").split(",") + recommended = formula_def.get("recommended", "").split(",") inspected = [] to_inspect = can_has.copy() @@ -606,8 +627,8 @@ class SPMClient(object): inspected.append(dep) repo_contents = self.repo_metadata.get(can_has[dep], {}) - repo_packages = repo_contents.get('packages', {}) - dep_formula = repo_packages.get(dep, {}).get('info', {}) + repo_packages = repo_contents.get("packages", {}) + dep_formula = repo_packages.get(dep, {}).get("info", {}) also_can, also_cant, opt_dep, rec_dep = self._resolve_deps(dep_formula) can_has.update(also_can) @@ -618,50 +639,56 @@ class SPMClient(object): return can_has, cant_has, optional, recommended def _traverse_repos(self, callback, repo_name=None): - ''' + """ Traverse through all repo files and apply the functionality provided in the callback to them - ''' + """ repo_files = [] - if os.path.exists(self.opts['spm_repos_config']): - repo_files.append(self.opts['spm_repos_config']) + if os.path.exists(self.opts["spm_repos_config"]): + repo_files.append(self.opts["spm_repos_config"]) - for (dirpath, dirnames, filenames) in salt.utils.path.os_walk('{0}.d'.format(self.opts['spm_repos_config'])): + for (dirpath, dirnames, filenames) in salt.utils.path.os_walk( + "{0}.d".format(self.opts["spm_repos_config"]) + ): for repo_file in filenames: - if not repo_file.endswith('.repo'): + if not repo_file.endswith(".repo"): continue repo_files.append(repo_file) for repo_file in repo_files: - repo_path = '{0}.d/{1}'.format(self.opts['spm_repos_config'], repo_file) + repo_path = "{0}.d/{1}".format(self.opts["spm_repos_config"], repo_file) with salt.utils.files.fopen(repo_path) as rph: repo_data = salt.utils.yaml.safe_load(rph) for repo in repo_data: - if repo_data[repo].get('enabled', True) is False: + if repo_data[repo].get("enabled", True) is False: continue if repo_name is not None and repo != repo_name: continue callback(repo, repo_data[repo]) def _query_http(self, dl_path, repo_info): - ''' + """ Download files via http - ''' + """ query = None response = None try: - if 'username' in repo_info: + if "username" in repo_info: try: - if 'password' in repo_info: + if "password" in repo_info: query = http.query( - dl_path, text=True, - username=repo_info['username'], - password=repo_info['password'] + dl_path, + text=True, + username=repo_info["username"], + password=repo_info["password"], ) else: - raise SPMException('Auth defined, but password is not set for username: \'{0}\'' - .format(repo_info['username'])) + raise SPMException( + "Auth defined, but password is not set for username: '{0}'".format( + repo_info["username"] + ) + ) except SPMException as exc: self.ui.error(six.text_type(exc)) else: @@ -671,66 +698,66 @@ class SPMClient(object): try: if query: - if 'SPM-METADATA' in dl_path: - response = salt.utils.yaml.safe_load(query.get('text', '{}')) + if "SPM-METADATA" in dl_path: + response = salt.utils.yaml.safe_load(query.get("text", "{}")) else: - response = query.get('text') + response = query.get("text") else: - raise SPMException('Response is empty, please check for Errors above.') + raise SPMException("Response is empty, please check for Errors above.") except SPMException as exc: self.ui.error(six.text_type(exc)) return response def _download_repo_metadata(self, args): - ''' + """ Connect to all repos and download metadata - ''' - cache = salt.cache.Cache(self.opts, self.opts['spm_cache_dir']) + """ + cache = salt.cache.Cache(self.opts, self.opts["spm_cache_dir"]) def _update_metadata(repo, repo_info): - dl_path = '{0}/SPM-METADATA'.format(repo_info['url']) - if dl_path.startswith('file://'): - dl_path = dl_path.replace('file://', '') - with salt.utils.files.fopen(dl_path, 'r') as rpm: + dl_path = "{0}/SPM-METADATA".format(repo_info["url"]) + if dl_path.startswith("file://"): + dl_path = dl_path.replace("file://", "") + with salt.utils.files.fopen(dl_path, "r") as rpm: metadata = salt.utils.yaml.safe_load(rpm) else: metadata = self._query_http(dl_path, repo_info) - cache.store('.', repo, metadata) + cache.store(".", repo, metadata) repo_name = args[1] if len(args) > 1 else None self._traverse_repos(_update_metadata, repo_name) def _get_repo_metadata(self): - ''' + """ Return cached repo metadata - ''' - cache = salt.cache.Cache(self.opts, self.opts['spm_cache_dir']) + """ + cache = salt.cache.Cache(self.opts, self.opts["spm_cache_dir"]) metadata = {} def _read_metadata(repo, repo_info): - if cache.updated('.', repo) is None: - log.warning('Updating repo metadata') + if cache.updated(".", repo) is None: + log.warning("Updating repo metadata") self._download_repo_metadata({}) metadata[repo] = { - 'info': repo_info, - 'packages': cache.fetch('.', repo), + "info": repo_info, + "packages": cache.fetch(".", repo), } self._traverse_repos(_read_metadata) return metadata def _create_repo(self, args): - ''' + """ Scan a directory and create an SPM-METADATA file which describes all of the SPM files in that directory. - ''' + """ if len(args) < 2: - raise SPMInvocationError('A path to a directory must be specified') + raise SPMInvocationError("A path to a directory must be specified") - if args[1] == '.': + if args[1] == ".": repo_path = os.getcwdu() else: repo_path = args[1] @@ -739,370 +766,396 @@ class SPMClient(object): repo_metadata = {} for (dirpath, dirnames, filenames) in salt.utils.path.os_walk(repo_path): for spm_file in filenames: - if not spm_file.endswith('.spm'): + if not spm_file.endswith(".spm"): continue - spm_path = '{0}/{1}'.format(repo_path, spm_file) + spm_path = "{0}/{1}".format(repo_path, spm_file) if not tarfile.is_tarfile(spm_path): continue - comps = spm_file.split('-') - spm_name = '-'.join(comps[:-2]) - spm_fh = tarfile.open(spm_path, 'r:bz2') - formula_handle = spm_fh.extractfile('{0}/FORMULA'.format(spm_name)) + comps = spm_file.split("-") + spm_name = "-".join(comps[:-2]) + spm_fh = tarfile.open(spm_path, "r:bz2") + formula_handle = spm_fh.extractfile("{0}/FORMULA".format(spm_name)) formula_conf = salt.utils.yaml.safe_load(formula_handle.read()) use_formula = True if spm_name in repo_metadata: # This package is already in the repo; use the latest - cur_info = repo_metadata[spm_name]['info'] + cur_info = repo_metadata[spm_name]["info"] new_info = formula_conf - if int(new_info['version']) == int(cur_info['version']): + if int(new_info["version"]) == int(cur_info["version"]): # Version is the same, check release - if int(new_info['release']) < int(cur_info['release']): + if int(new_info["release"]) < int(cur_info["release"]): # This is an old release; don't use it use_formula = False - elif int(new_info['version']) < int(cur_info['version']): + elif int(new_info["version"]) < int(cur_info["version"]): # This is an old version; don't use it use_formula = False if use_formula is True: # Ignore/archive/delete the old version log.debug( - '%s %s-%s had been added, but %s-%s will replace it', - spm_name, cur_info['version'], cur_info['release'], - new_info['version'], new_info['release'] + "%s %s-%s had been added, but %s-%s will replace it", + spm_name, + cur_info["version"], + cur_info["release"], + new_info["version"], + new_info["release"], ) - old_files.append(repo_metadata[spm_name]['filename']) + old_files.append(repo_metadata[spm_name]["filename"]) else: # Ignore/archive/delete the new version log.debug( - '%s %s-%s has been found, but is older than %s-%s', - spm_name, new_info['version'], new_info['release'], - cur_info['version'], cur_info['release'] + "%s %s-%s has been found, but is older than %s-%s", + spm_name, + new_info["version"], + new_info["release"], + cur_info["version"], + cur_info["release"], ) old_files.append(spm_file) if use_formula is True: log.debug( - 'adding %s-%s-%s to the repo', - formula_conf['name'], formula_conf['version'], - formula_conf['release'] + "adding %s-%s-%s to the repo", + formula_conf["name"], + formula_conf["version"], + formula_conf["release"], ) repo_metadata[spm_name] = { - 'info': formula_conf.copy(), + "info": formula_conf.copy(), } - repo_metadata[spm_name]['filename'] = spm_file + repo_metadata[spm_name]["filename"] = spm_file - metadata_filename = '{0}/SPM-METADATA'.format(repo_path) - with salt.utils.files.fopen(metadata_filename, 'w') as mfh: + metadata_filename = "{0}/SPM-METADATA".format(repo_path) + with salt.utils.files.fopen(metadata_filename, "w") as mfh: salt.utils.yaml.safe_dump( - repo_metadata, - mfh, - indent=4, - canonical=False, - default_flow_style=False, + repo_metadata, mfh, indent=4, canonical=False, default_flow_style=False, ) - log.debug('Wrote %s', metadata_filename) + log.debug("Wrote %s", metadata_filename) for file_ in old_files: - if self.opts['spm_repo_dups'] == 'ignore': + if self.opts["spm_repo_dups"] == "ignore": # ignore old packages, but still only add the latest - log.debug('%s will be left in the directory', file_) - elif self.opts['spm_repo_dups'] == 'archive': + log.debug("%s will be left in the directory", file_) + elif self.opts["spm_repo_dups"] == "archive": # spm_repo_archive_path is where old packages are moved - if not os.path.exists('./archive'): + if not os.path.exists("./archive"): try: - os.makedirs('./archive') - log.debug('%s has been archived', file_) + os.makedirs("./archive") + log.debug("%s has been archived", file_) except IOError: - log.error('Unable to create archive directory') + log.error("Unable to create archive directory") try: - shutil.move(file_, './archive') + shutil.move(file_, "./archive") except (IOError, OSError): - log.error('Unable to archive %s', file_) - elif self.opts['spm_repo_dups'] == 'delete': + log.error("Unable to archive %s", file_) + elif self.opts["spm_repo_dups"] == "delete": # delete old packages from the repo try: os.remove(file_) - log.debug('%s has been deleted', file_) + log.debug("%s has been deleted", file_) except IOError: - log.error('Unable to delete %s', file_) + log.error("Unable to delete %s", file_) except OSError: # pylint: disable=duplicate-except # The file has already been deleted pass def _remove(self, args): - ''' + """ Remove a package - ''' + """ if len(args) < 2: - raise SPMInvocationError('A package must be specified') + raise SPMInvocationError("A package must be specified") packages = args[1:] - msg = 'Removing packages:\n\t{0}'.format('\n\t'.join(packages)) + msg = "Removing packages:\n\t{0}".format("\n\t".join(packages)) - if not self.opts['assume_yes']: + if not self.opts["assume_yes"]: self.ui.confirm(msg) for package in packages: - self.ui.status('... removing {0}'.format(package)) + self.ui.status("... removing {0}".format(package)) - if not self._pkgdb_fun('db_exists', self.opts['spm_db']): - raise SPMDatabaseError('No database at {0}, cannot remove {1}'.format(self.opts['spm_db'], package)) + if not self._pkgdb_fun("db_exists", self.opts["spm_db"]): + raise SPMDatabaseError( + "No database at {0}, cannot remove {1}".format( + self.opts["spm_db"], package + ) + ) # Look at local repo index - pkg_info = self._pkgdb_fun('info', package, self.db_conn) + pkg_info = self._pkgdb_fun("info", package, self.db_conn) if pkg_info is None: - raise SPMInvocationError('Package {0} not installed'.format(package)) + raise SPMInvocationError("Package {0} not installed".format(package)) # Find files that have not changed and remove them - files = self._pkgdb_fun('list_files', package, self.db_conn) + files = self._pkgdb_fun("list_files", package, self.db_conn) dirs = [] for filerow in files: - if self._pkgfiles_fun('path_isdir', filerow[0]): + if self._pkgfiles_fun("path_isdir", filerow[0]): dirs.append(filerow[0]) continue file_hash = hashlib.sha1() - digest = self._pkgfiles_fun('hash_file', filerow[0], file_hash, self.files_conn) + digest = self._pkgfiles_fun( + "hash_file", filerow[0], file_hash, self.files_conn + ) if filerow[1] == digest: - self._verbose('Removing file {0}'.format(filerow[0]), log.trace) - self._pkgfiles_fun('remove_file', filerow[0], self.files_conn) + self._verbose("Removing file {0}".format(filerow[0]), log.trace) + self._pkgfiles_fun("remove_file", filerow[0], self.files_conn) else: - self._verbose('Not removing file {0}'.format(filerow[0]), log.trace) - self._pkgdb_fun('unregister_file', filerow[0], package, self.db_conn) + self._verbose("Not removing file {0}".format(filerow[0]), log.trace) + self._pkgdb_fun("unregister_file", filerow[0], package, self.db_conn) # Clean up directories for dir_ in sorted(dirs, reverse=True): - self._pkgdb_fun('unregister_file', dir_, package, self.db_conn) + self._pkgdb_fun("unregister_file", dir_, package, self.db_conn) try: - self._verbose('Removing directory {0}'.format(dir_), log.trace) + self._verbose("Removing directory {0}".format(dir_), log.trace) os.rmdir(dir_) except OSError: # Leave directories in place that still have files in them - self._verbose('Cannot remove directory {0}, probably not empty'.format(dir_), log.trace) + self._verbose( + "Cannot remove directory {0}, probably not empty".format(dir_), + log.trace, + ) - self._pkgdb_fun('unregister_pkg', package, self.db_conn) + self._pkgdb_fun("unregister_pkg", package, self.db_conn) def _verbose(self, msg, level=log.debug): - ''' + """ Display verbose information - ''' - if self.opts.get('verbose', False) is True: + """ + if self.opts.get("verbose", False) is True: self.ui.status(msg) level(msg) def _local_info(self, args): - ''' + """ List info for a package file - ''' + """ if len(args) < 2: - raise SPMInvocationError('A package filename must be specified') + raise SPMInvocationError("A package filename must be specified") pkg_file = args[1] if not os.path.exists(pkg_file): - raise SPMInvocationError('Package file {0} not found'.format(pkg_file)) + raise SPMInvocationError("Package file {0} not found".format(pkg_file)) - comps = pkg_file.split('-') - comps = '-'.join(comps[:-2]).split('/') + comps = pkg_file.split("-") + comps = "-".join(comps[:-2]).split("/") name = comps[-1] - formula_tar = tarfile.open(pkg_file, 'r:bz2') - formula_ref = formula_tar.extractfile('{0}/FORMULA'.format(name)) + formula_tar = tarfile.open(pkg_file, "r:bz2") + formula_ref = formula_tar.extractfile("{0}/FORMULA".format(name)) formula_def = salt.utils.yaml.safe_load(formula_ref) self.ui.status(self._get_info(formula_def)) formula_tar.close() def _info(self, args): - ''' + """ List info for a package - ''' + """ if len(args) < 2: - raise SPMInvocationError('A package must be specified') + raise SPMInvocationError("A package must be specified") package = args[1] - pkg_info = self._pkgdb_fun('info', package, self.db_conn) + pkg_info = self._pkgdb_fun("info", package, self.db_conn) if pkg_info is None: - raise SPMPackageError('package {0} not installed'.format(package)) + raise SPMPackageError("package {0} not installed".format(package)) self.ui.status(self._get_info(pkg_info)) def _get_info(self, formula_def): - ''' + """ Get package info - ''' + """ fields = ( - 'name', - 'os', - 'os_family', - 'release', - 'version', - 'dependencies', - 'os_dependencies', - 'os_family_dependencies', - 'summary', - 'description', + "name", + "os", + "os_family", + "release", + "version", + "dependencies", + "os_dependencies", + "os_family_dependencies", + "summary", + "description", ) for item in fields: if item not in formula_def: - formula_def[item] = 'None' + formula_def[item] = "None" - if 'installed' not in formula_def: - formula_def['installed'] = 'Not installed' + if "installed" not in formula_def: + formula_def["installed"] = "Not installed" - return ('Name: {name}\n' - 'Version: {version}\n' - 'Release: {release}\n' - 'Install Date: {installed}\n' - 'Supported OSes: {os}\n' - 'Supported OS families: {os_family}\n' - 'Dependencies: {dependencies}\n' - 'OS Dependencies: {os_dependencies}\n' - 'OS Family Dependencies: {os_family_dependencies}\n' - 'Summary: {summary}\n' - 'Description:\n' - '{description}').format(**formula_def) + return ( + "Name: {name}\n" + "Version: {version}\n" + "Release: {release}\n" + "Install Date: {installed}\n" + "Supported OSes: {os}\n" + "Supported OS families: {os_family}\n" + "Dependencies: {dependencies}\n" + "OS Dependencies: {os_dependencies}\n" + "OS Family Dependencies: {os_family_dependencies}\n" + "Summary: {summary}\n" + "Description:\n" + "{description}" + ).format(**formula_def) def _local_list_files(self, args): - ''' + """ List files for a package file - ''' + """ if len(args) < 2: - raise SPMInvocationError('A package filename must be specified') + raise SPMInvocationError("A package filename must be specified") pkg_file = args[1] if not os.path.exists(pkg_file): - raise SPMPackageError('Package file {0} not found'.format(pkg_file)) - formula_tar = tarfile.open(pkg_file, 'r:bz2') + raise SPMPackageError("Package file {0} not found".format(pkg_file)) + formula_tar = tarfile.open(pkg_file, "r:bz2") pkg_files = formula_tar.getmembers() for member in pkg_files: self.ui.status(member.name) def _list_packages(self, args): - ''' + """ List files for an installed package - ''' - packages = self._pkgdb_fun('list_packages', self.db_conn) + """ + packages = self._pkgdb_fun("list_packages", self.db_conn) for package in packages: - if self.opts['verbose']: - status_msg = ','.join(package) + if self.opts["verbose"]: + status_msg = ",".join(package) else: status_msg = package[0] self.ui.status(status_msg) def _list_files(self, args): - ''' + """ List files for an installed package - ''' + """ if len(args) < 2: - raise SPMInvocationError('A package name must be specified') + raise SPMInvocationError("A package name must be specified") package = args[-1] - files = self._pkgdb_fun('list_files', package, self.db_conn) + files = self._pkgdb_fun("list_files", package, self.db_conn) if files is None: - raise SPMPackageError('package {0} not installed'.format(package)) + raise SPMPackageError("package {0} not installed".format(package)) else: for file_ in files: - if self.opts['verbose']: - status_msg = ','.join(file_) + if self.opts["verbose"]: + status_msg = ",".join(file_) else: status_msg = file_[0] self.ui.status(status_msg) def _build(self, args): - ''' + """ Build a package - ''' + """ if len(args) < 2: - raise SPMInvocationError('A path to a formula must be specified') + raise SPMInvocationError("A path to a formula must be specified") - self.abspath = args[1].rstrip('/') - comps = self.abspath.split('/') + self.abspath = args[1].rstrip("/") + comps = self.abspath.split("/") self.relpath = comps[-1] - formula_path = '{0}/FORMULA'.format(self.abspath) + formula_path = "{0}/FORMULA".format(self.abspath) if not os.path.exists(formula_path): - raise SPMPackageError('Formula file {0} not found'.format(formula_path)) + raise SPMPackageError("Formula file {0} not found".format(formula_path)) with salt.utils.files.fopen(formula_path) as fp_: formula_conf = salt.utils.yaml.safe_load(fp_) - for field in ('name', 'version', 'release', 'summary', 'description'): + for field in ("name", "version", "release", "summary", "description"): if field not in formula_conf: - raise SPMPackageError('Invalid package: a {0} must be defined'.format(field)) + raise SPMPackageError( + "Invalid package: a {0} must be defined".format(field) + ) - out_path = '{0}/{1}-{2}-{3}.spm'.format( - self.opts['spm_build_dir'], - formula_conf['name'], - formula_conf['version'], - formula_conf['release'], + out_path = "{0}/{1}-{2}-{3}.spm".format( + self.opts["spm_build_dir"], + formula_conf["name"], + formula_conf["version"], + formula_conf["release"], ) - if not os.path.exists(self.opts['spm_build_dir']): - os.mkdir(self.opts['spm_build_dir']) + if not os.path.exists(self.opts["spm_build_dir"]): + os.mkdir(self.opts["spm_build_dir"]) self.formula_conf = formula_conf - formula_tar = tarfile.open(out_path, 'w:bz2') + formula_tar = tarfile.open(out_path, "w:bz2") - if 'files' in formula_conf: + if "files" in formula_conf: # This allows files to be added to the SPM file in a specific order. # It also allows for files to be tagged as a certain type, as with # RPM files. This tag is ignored here, but is used when installing # the SPM file. - if isinstance(formula_conf['files'], list): - formula_dir = tarfile.TarInfo(formula_conf['name']) + if isinstance(formula_conf["files"], list): + formula_dir = tarfile.TarInfo(formula_conf["name"]) formula_dir.type = tarfile.DIRTYPE formula_tar.addfile(formula_dir) - for file_ in formula_conf['files']: + for file_ in formula_conf["files"]: for ftype in FILE_TYPES: - if file_.startswith('{0}|'.format(ftype)): - file_ = file_.lstrip('{0}|'.format(ftype)) + if file_.startswith("{0}|".format(ftype)): + file_ = file_.lstrip("{0}|".format(ftype)) formula_tar.add( os.path.join(os.getcwd(), file_), - os.path.join(formula_conf['name'], file_), + os.path.join(formula_conf["name"], file_), ) else: # If no files are specified, then the whole directory will be added. try: - formula_tar.add(formula_path, formula_conf['name'], filter=self._exclude) - formula_tar.add(self.abspath, formula_conf['name'], filter=self._exclude) + formula_tar.add( + formula_path, formula_conf["name"], filter=self._exclude + ) + formula_tar.add( + self.abspath, formula_conf["name"], filter=self._exclude + ) except TypeError: - formula_tar.add(formula_path, formula_conf['name'], exclude=self._exclude) - formula_tar.add(self.abspath, formula_conf['name'], exclude=self._exclude) + formula_tar.add( + formula_path, formula_conf["name"], exclude=self._exclude + ) + formula_tar.add( + self.abspath, formula_conf["name"], exclude=self._exclude + ) formula_tar.close() - self.ui.status('Built package {0}'.format(out_path)) + self.ui.status("Built package {0}".format(out_path)) def _exclude(self, member): - ''' + """ Exclude based on opts - ''' + """ if isinstance(member, string_types): return None - for item in self.opts['spm_build_exclude']: - if member.name.startswith('{0}/{1}'.format(self.formula_conf['name'], item)): + for item in self.opts["spm_build_exclude"]: + if member.name.startswith( + "{0}/{1}".format(self.formula_conf["name"], item) + ): return None - elif member.name.startswith('{0}/{1}'.format(self.abspath, item)): + elif member.name.startswith("{0}/{1}".format(self.abspath, item)): return None return member def _render(self, data, formula_def): - ''' + """ Render a [pre|post]_local_state or [pre|post]_tgt_state script - ''' + """ # FORMULA can contain a renderer option - renderer = formula_def.get('renderer', self.opts.get('renderer', 'jinja|yaml')) + renderer = formula_def.get("renderer", self.opts.get("renderer", "jinja|yaml")) rend = salt.loader.render(self.opts, {}) - blacklist = self.opts.get('renderer_blacklist') - whitelist = self.opts.get('renderer_whitelist') + blacklist = self.opts.get("renderer_blacklist") + whitelist = self.opts.get("renderer_whitelist") template_vars = formula_def.copy() - template_vars['opts'] = self.opts.copy() + template_vars["opts"] = self.opts.copy() return compile_template( - ':string:', + ":string:", rend, renderer, blacklist, @@ -1113,34 +1166,36 @@ class SPMClient(object): class SPMUserInterface(object): - ''' + """ Handle user interaction with an SPMClient object - ''' + """ + def status(self, msg): - ''' + """ Report an SPMClient status message - ''' + """ raise NotImplementedError() def error(self, msg): - ''' + """ Report an SPM error message - ''' + """ raise NotImplementedError() def confirm(self, action): - ''' + """ Get confirmation from the user before performing an SPMClient action. Return if the action is confirmed, or raise SPMOperationCanceled(<msg>) if canceled. - ''' + """ raise NotImplementedError() class SPMCmdlineInterface(SPMUserInterface): - ''' + """ Command-line interface to SPMClient - ''' + """ + def status(self, msg): print(msg) @@ -1149,6 +1204,6 @@ class SPMCmdlineInterface(SPMUserInterface): def confirm(self, action): print(action) - res = input('Proceed? [N/y] ') - if not res.lower().startswith('y'): - raise SPMOperationCanceled('canceled') + res = input("Proceed? [N/y] ") + if not res.lower().startswith("y"): + raise SPMOperationCanceled("canceled") diff --git a/salt/spm/pkgdb/sqlite3.py b/salt/spm/pkgdb/sqlite3.py index de7b15c397f..a84a4cdd58c 100644 --- a/salt/spm/pkgdb/sqlite3.py +++ b/salt/spm/pkgdb/sqlite3.py @@ -1,16 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" This module allows SPM to use sqlite3 as the backend for SPM's package database. .. versionadded:: 2015.8.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import datetime import logging import os import sqlite3 from sqlite3 import OperationalError + from salt.ext.six.moves import zip # Get logging started @@ -18,23 +20,24 @@ log = logging.getLogger(__name__) def init(): - ''' + """ Get an sqlite3 connection, and initialize the package database if necessary - ''' - if not os.path.exists(__opts__['spm_cache_dir']): - log.debug('Creating SPM cache directory at %s', __opts__['spm_db']) - os.makedirs(__opts__['spm_cache_dir']) + """ + if not os.path.exists(__opts__["spm_cache_dir"]): + log.debug("Creating SPM cache directory at %s", __opts__["spm_db"]) + os.makedirs(__opts__["spm_cache_dir"]) - if not os.path.exists(__opts__['spm_db']): - log.debug('Creating new package database at %s', __opts__['spm_db']) + if not os.path.exists(__opts__["spm_db"]): + log.debug("Creating new package database at %s", __opts__["spm_db"]) sqlite3.enable_callback_tracebacks(True) - conn = sqlite3.connect(__opts__['spm_db'], isolation_level=None) + conn = sqlite3.connect(__opts__["spm_db"], isolation_level=None) try: - conn.execute('SELECT count(*) FROM packages') + conn.execute("SELECT count(*) FROM packages") except OperationalError: - conn.execute('''CREATE TABLE packages ( + conn.execute( + """CREATE TABLE packages ( package text, version text, release text, @@ -46,12 +49,14 @@ def init(): os_family_dependencies text, summary text, description text - )''') + )""" + ) try: - conn.execute('SELECT count(*) FROM files') + conn.execute("SELECT count(*) FROM files") except OperationalError: - conn.execute('''CREATE TABLE files ( + conn.execute( + """CREATE TABLE files ( package text, path text, size real, @@ -64,36 +69,36 @@ def init(): uname text, gname text, mtime text - )''') + )""" + ) return conn def info(package, conn=None): - ''' + """ List info for a package - ''' + """ close = False if conn is None: close = True conn = init() fields = ( - 'package', - 'version', - 'release', - 'installed', - 'os', - 'os_family', - 'dependencies', - 'os_dependencies', - 'os_family_dependencies', - 'summary', - 'description', + "package", + "version", + "release", + "installed", + "os", + "os_family", + "dependencies", + "os_dependencies", + "os_family_dependencies", + "summary", + "description", ) data = conn.execute( - 'SELECT {0} FROM packages WHERE package=?'.format(','.join(fields)), - (package, ) + "SELECT {0} FROM packages WHERE package=?".format(",".join(fields)), (package,) ) row = data.fetchone() if close: @@ -102,22 +107,22 @@ def info(package, conn=None): return None formula_def = dict(list(zip(fields, row))) - formula_def['name'] = formula_def['package'] + formula_def["name"] = formula_def["package"] return formula_def def list_packages(conn=None): - ''' + """ List files for an installed package - ''' + """ close = False if conn is None: close = True conn = init() ret = [] - data = conn.execute('SELECT package FROM packages') + data = conn.execute("SELECT package FROM packages") for pkg in data.fetchall(): ret.append(pkg) @@ -127,22 +132,22 @@ def list_packages(conn=None): def list_files(package, conn=None): - ''' + """ List files for an installed package - ''' + """ close = False if conn is None: close = True conn = init() - data = conn.execute('SELECT package FROM packages WHERE package=?', (package, )) + data = conn.execute("SELECT package FROM packages WHERE package=?", (package,)) if not data.fetchone(): if close: conn.close() return None ret = [] - data = conn.execute('SELECT path, sum FROM files WHERE package=?', (package, )) + data = conn.execute("SELECT path, sum FROM files WHERE package=?", (package,)) for file_ in data.fetchall(): ret.append(file_) if close: @@ -152,84 +157,90 @@ def list_files(package, conn=None): def register_pkg(name, formula_def, conn=None): - ''' + """ Register a package in the package database - ''' + """ close = False if conn is None: close = True conn = init() - conn.execute('INSERT INTO packages VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', ( - name, - formula_def['version'], - formula_def['release'], - datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT'), - formula_def.get('os', None), - formula_def.get('os_family', None), - formula_def.get('dependencies', None), - formula_def.get('os_dependencies', None), - formula_def.get('os_family_dependencies', None), - formula_def['summary'], - formula_def['description'], - )) + conn.execute( + "INSERT INTO packages VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + ( + name, + formula_def["version"], + formula_def["release"], + datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT"), + formula_def.get("os", None), + formula_def.get("os_family", None), + formula_def.get("dependencies", None), + formula_def.get("os_dependencies", None), + formula_def.get("os_family_dependencies", None), + formula_def["summary"], + formula_def["description"], + ), + ) if close: conn.close() -def register_file(name, member, path, digest='', conn=None): - ''' +def register_file(name, member, path, digest="", conn=None): + """ Register a file in the package database - ''' + """ close = False if conn is None: close = True conn = init() - conn.execute('INSERT INTO files VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', ( - name, - '{0}/{1}'.format(path, member.path), - member.size, - member.mode, - digest, - member.devmajor, - member.devminor, - member.linkname, - member.linkpath, - member.uname, - member.gname, - member.mtime - )) + conn.execute( + "INSERT INTO files VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + ( + name, + "{0}/{1}".format(path, member.path), + member.size, + member.mode, + digest, + member.devmajor, + member.devminor, + member.linkname, + member.linkpath, + member.uname, + member.gname, + member.mtime, + ), + ) if close: conn.close() def unregister_pkg(name, conn=None): - ''' + """ Unregister a package from the package database - ''' + """ if conn is None: conn = init() - conn.execute('DELETE FROM packages WHERE package=?', (name, )) + conn.execute("DELETE FROM packages WHERE package=?", (name,)) def unregister_file(path, pkg=None, conn=None): # pylint: disable=W0612 - ''' + """ Unregister a file from the package database - ''' + """ close = False if conn is None: close = True conn = init() - conn.execute('DELETE FROM files WHERE path=?', (path, )) + conn.execute("DELETE FROM files WHERE path=?", (path,)) if close: conn.close() def db_exists(db_): - ''' + """ Check to see whether the file already exists - ''' + """ return os.path.exists(db_) diff --git a/salt/spm/pkgfiles/local.py b/salt/spm/pkgfiles/local.py index 23c53ec718d..7ab089d52a9 100644 --- a/salt/spm/pkgfiles/local.py +++ b/salt/spm/pkgfiles/local.py @@ -1,15 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" This module allows SPM to use the local filesystem to install files for SPM. .. versionadded:: 2015.8.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import os.path -import logging # Import Salt libs import salt.syspaths @@ -21,7 +22,7 @@ from salt.ext import six # Get logging started log = logging.getLogger(__name__) -FILE_TYPES = ('c', 'd', 'g', 'l', 'r', 's', 'm') +FILE_TYPES = ("c", "d", "g", "l", "r", "s", "m") # c: config file # d: documentation file # g: ghost file (i.e. the file contents are not included in the package payload) @@ -32,175 +33,175 @@ FILE_TYPES = ('c', 'd', 'g', 'l', 'r', 's', 'm') def init(**kwargs): - ''' + """ Initialize the directories for the files - ''' - formula_path = __opts__['formula_path'] - pillar_path = __opts__['pillar_path'] - reactor_path = __opts__['reactor_path'] + """ + formula_path = __opts__["formula_path"] + pillar_path = __opts__["pillar_path"] + reactor_path = __opts__["reactor_path"] for dir_ in (formula_path, pillar_path, reactor_path): if not os.path.exists(dir_): os.makedirs(dir_) return { - 'formula_path': formula_path, - 'pillar_path': pillar_path, - 'reactor_path': reactor_path, + "formula_path": formula_path, + "pillar_path": pillar_path, + "reactor_path": reactor_path, } def check_existing(package, pkg_files, formula_def, conn=None): - ''' + """ Check the filesystem for existing files - ''' + """ if conn is None: conn = init() - node_type = six.text_type(__opts__.get('spm_node_type')) + node_type = six.text_type(__opts__.get("spm_node_type")) existing_files = [] for member in pkg_files: if member.isdir(): continue - tld = formula_def.get('top_level_dir', package) - new_name = member.name.replace('{0}/'.format(package), '') + tld = formula_def.get("top_level_dir", package) + new_name = member.name.replace("{0}/".format(package), "") if not new_name.startswith(tld): continue - if member.name.startswith('{0}/_'.format(package)): - if node_type in ('master', 'minion'): + if member.name.startswith("{0}/_".format(package)): + if node_type in ("master", "minion"): # Module files are distributed via extmods directory out_file = os.path.join( salt.syspaths.CACHE_DIR, node_type, - 'extmods', - new_name.replace('_', ''), + "extmods", + new_name.replace("_", ""), ) else: # Module files are distributed via _modules, _states, etc - out_file = os.path.join(conn['formula_path'], new_name) - elif member.name == '{0}/pillar.example'.format(package): + out_file = os.path.join(conn["formula_path"], new_name) + elif member.name == "{0}/pillar.example".format(package): # Pillars are automatically put in the pillar_path - new_name = '{0}.sls.orig'.format(package) - out_file = os.path.join(conn['pillar_path'], new_name) - elif package.endswith('-conf'): + new_name = "{0}.sls.orig".format(package) + out_file = os.path.join(conn["pillar_path"], new_name) + elif package.endswith("-conf"): # Configuration files go into /etc/salt/ out_file = os.path.join(salt.syspaths.CONFIG_DIR, new_name) - elif package.endswith('-reactor'): + elif package.endswith("-reactor"): # Reactor files go into /srv/reactor/ - out_file = os.path.join(conn['reactor_path'], member.name) + out_file = os.path.join(conn["reactor_path"], member.name) else: - out_file = os.path.join(conn['formula_path'], member.name) + out_file = os.path.join(conn["formula_path"], member.name) if os.path.exists(out_file): existing_files.append(out_file) - if not __opts__['force']: - log.error('%s already exists, not installing', out_file) + if not __opts__["force"]: + log.error("%s already exists, not installing", out_file) return existing_files def install_file(package, formula_tar, member, formula_def, conn=None): - ''' + """ Install a single file to the file system - ''' + """ if member.name == package: return False if conn is None: conn = init() - node_type = six.text_type(__opts__.get('spm_node_type')) + node_type = six.text_type(__opts__.get("spm_node_type")) - out_path = conn['formula_path'] + out_path = conn["formula_path"] - tld = formula_def.get('top_level_dir', package) - new_name = member.name.replace('{0}/'.format(package), '', 1) - if not new_name.startswith(tld) and not new_name.startswith('_') and not \ - new_name.startswith('pillar.example') and not new_name.startswith('README'): - log.debug('%s not in top level directory, not installing', new_name) + tld = formula_def.get("top_level_dir", package) + new_name = member.name.replace("{0}/".format(package), "", 1) + if ( + not new_name.startswith(tld) + and not new_name.startswith("_") + and not new_name.startswith("pillar.example") + and not new_name.startswith("README") + ): + log.debug("%s not in top level directory, not installing", new_name) return False - for line in formula_def.get('files', []): - tag = '' + for line in formula_def.get("files", []): + tag = "" for ftype in FILE_TYPES: - if line.startswith('{0}|'.format(ftype)): - tag = line.split('|', 1)[0] - line = line.split('|', 1)[1] + if line.startswith("{0}|".format(ftype)): + tag = line.split("|", 1)[0] + line = line.split("|", 1)[1] if tag and new_name == line: - if tag in ('c', 'd', 'g', 'l', 'r'): - out_path = __opts__['spm_share_dir'] - elif tag in ('s', 'm'): + if tag in ("c", "d", "g", "l", "r"): + out_path = __opts__["spm_share_dir"] + elif tag in ("s", "m"): pass - if member.name.startswith('{0}/_'.format(package)): - if node_type in ('master', 'minion'): + if member.name.startswith("{0}/_".format(package)): + if node_type in ("master", "minion"): # Module files are distributed via extmods directory - member.name = new_name.name.replace('{0}/_'.format(package), '') - out_path = os.path.join( - salt.syspaths.CACHE_DIR, - node_type, - 'extmods', - ) + member.name = new_name.name.replace("{0}/_".format(package), "") + out_path = os.path.join(salt.syspaths.CACHE_DIR, node_type, "extmods",) else: # Module files are distributed via _modules, _states, etc - member.name = new_name.name.replace('{0}/'.format(package), '') - elif member.name == '{0}/pillar.example'.format(package): + member.name = new_name.name.replace("{0}/".format(package), "") + elif member.name == "{0}/pillar.example".format(package): # Pillars are automatically put in the pillar_path - member.name = '{0}.sls.orig'.format(package) - out_path = conn['pillar_path'] - elif package.endswith('-conf'): + member.name = "{0}.sls.orig".format(package) + out_path = conn["pillar_path"] + elif package.endswith("-conf"): # Configuration files go into /etc/salt/ - member.name = member.name.replace('{0}/'.format(package), '') + member.name = member.name.replace("{0}/".format(package), "") out_path = salt.syspaths.CONFIG_DIR - elif package.endswith('-reactor'): + elif package.endswith("-reactor"): # Reactor files go into /srv/reactor/ - out_path = __opts__['reactor_path'] + out_path = __opts__["reactor_path"] # This ensures that double directories (i.e., apache/apache/) don't # get created - comps = member.path.split('/') + comps = member.path.split("/") if len(comps) > 1 and comps[0] == comps[1]: - member.path = '/'.join(comps[1:]) + member.path = "/".join(comps[1:]) - log.debug('Installing package file %s to %s', member.name, out_path) + log.debug("Installing package file %s to %s", member.name, out_path) formula_tar.extract(member, out_path) return out_path def remove_file(path, conn=None): - ''' + """ Remove a single file from the file system - ''' + """ if conn is None: conn = init() - log.debug('Removing package file %s', path) + log.debug("Removing package file %s", path) os.remove(path) def hash_file(path, hashobj, conn=None): - ''' + """ Get the hexdigest hash value of a file - ''' + """ if os.path.isdir(path): - return '' + return "" - with salt.utils.files.fopen(path, 'r') as f: + with salt.utils.files.fopen(path, "r") as f: hashobj.update(salt.utils.stringutils.to_bytes(f.read())) return hashobj.hexdigest() def path_exists(path): - ''' + """ Check to see whether the file already exists - ''' + """ return os.path.exists(path) def path_isdir(path): - ''' + """ Check to see whether the file already exists - ''' + """ return os.path.isdir(path) diff --git a/salt/state.py b/salt/state.py index 1db1c35c524..627bf0db68f 100644 --- a/salt/state.py +++ b/salt/state.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The State Compiler is used to execute states in Salt. A state is unlike an execution module in that instead of just executing a command, it ensures that a certain state is present on the system. @@ -10,27 +10,31 @@ The data sent to the state calls is as follows: 'name': '<the name argument passed to all states>' 'argn': '<arbitrary argument, can have many of these>' } -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys + import copy -import site +import datetime import fnmatch import logging -import datetime -import traceback -import re -import time +import os import random +import re +import site +import sys +import time +import traceback + +import salt.fileclient # Import salt libs import salt.loader import salt.minion import salt.pillar -import salt.fileclient +import salt.syspaths as syspaths +import salt.transport.client import salt.utils.args import salt.utils.crypt import salt.utils.data @@ -44,22 +48,20 @@ import salt.utils.msgpack import salt.utils.platform import salt.utils.process import salt.utils.url -import salt.syspaths as syspaths -import salt.transport.client -from salt.serializers.msgpack import serialize as msgpack_serialize, deserialize as msgpack_deserialize -from salt.template import compile_template, compile_template_str -from salt.exceptions import ( - SaltRenderError, - SaltReqTimeoutError -) -from salt.utils.odict import OrderedDict, DefaultOrderedDict + # Explicit late import to avoid circular import. DO NOT MOVE THIS. import salt.utils.yamlloader as yamlloader +from salt.exceptions import SaltRenderError, SaltReqTimeoutError # Import third party libs # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six from salt.ext.six.moves import map, range, reload_module +from salt.serializers.msgpack import deserialize as msgpack_deserialize +from salt.serializers.msgpack import serialize as msgpack_serialize +from salt.template import compile_template, compile_template_str +from salt.utils.odict import DefaultOrderedDict, OrderedDict + # pylint: enable=import-error,no-name-in-module,redefined-builtin log = logging.getLogger(__name__) @@ -67,66 +69,67 @@ log = logging.getLogger(__name__) # These are keywords passed to state module functions which are to be used # by salt in this state module and not on the actual state module function -STATE_REQUISITE_KEYWORDS = frozenset([ - 'onchanges', - 'onchanges_any', - 'onfail', - 'onfail_any', - 'onfail_stop', - 'prereq', - 'prerequired', - 'watch', - 'watch_any', - 'require', - 'require_any', - 'listen', - ]) -STATE_REQUISITE_IN_KEYWORDS = frozenset([ - 'onchanges_in', - 'onfail_in', - 'prereq_in', - 'watch_in', - 'require_in', - 'listen_in', - ]) -STATE_RUNTIME_KEYWORDS = frozenset([ - 'fun', - 'state', - 'check_cmd', - 'failhard', - 'onlyif', - 'unless', - 'retry', - 'order', - 'parallel', - 'prereq', - 'prereq_in', - 'prerequired', - 'reload_modules', - 'reload_grains', - 'reload_pillar', - 'runas', - 'runas_password', - 'fire_event', - 'saltenv', - 'use', - 'use_in', - '__env__', - '__sls__', - '__id__', - '__orchestration_jid__', - '__pub_user', - '__pub_arg', - '__pub_jid', - '__pub_fun', - '__pub_tgt', - '__pub_ret', - '__pub_pid', - '__pub_tgt_type', - '__prereq__', - ]) +STATE_REQUISITE_KEYWORDS = frozenset( + [ + "onchanges", + "onchanges_any", + "onfail", + "onfail_any", + "onfail_stop", + "prereq", + "prerequired", + "watch", + "watch_any", + "require", + "require_any", + "listen", + ] +) +STATE_REQUISITE_IN_KEYWORDS = frozenset( + ["onchanges_in", "onfail_in", "prereq_in", "watch_in", "require_in", "listen_in"] +) +STATE_RUNTIME_KEYWORDS = frozenset( + [ + "fun", + "state", + "check_cmd", + "failhard", + "onlyif", + "unless", + "retry", + "order", + "parallel", + "prereq", + "prereq_in", + "prerequired", + "reload_modules", + "reload_grains", + "reload_pillar", + "runas", + "runas_password", + "fire_event", + "saltenv", + "use", + "use_in", + "__env__", + "__sls__", + "__id__", + "__orchestration_jid__", + "__pub_user", + "__pub_arg", + "__pub_jid", + "__pub_fun", + "__pub_tgt", + "__pub_ret", + "__pub_pid", + "__pub_tgt_type", + "__prereq__", + ] +) -STATE_INTERNAL_KEYWORDS = STATE_REQUISITE_KEYWORDS.union(STATE_REQUISITE_IN_KEYWORDS).union(STATE_RUNTIME_KEYWORDS) +STATE_INTERNAL_KEYWORDS = STATE_REQUISITE_KEYWORDS.union( + STATE_REQUISITE_IN_KEYWORDS +).union(STATE_RUNTIME_KEYWORDS) def _odict_hashable(self): @@ -137,50 +140,50 @@ OrderedDict.__hash__ = _odict_hashable def split_low_tag(tag): - ''' + """ Take a low tag and split it back into the low dict that it came from - ''' - state, id_, name, fun = tag.split('_|-') + """ + state, id_, name, fun = tag.split("_|-") - return {'state': state, - '__id__': id_, - 'name': name, - 'fun': fun} + return {"state": state, "__id__": id_, "name": name, "fun": fun} def _gen_tag(low): - ''' + """ Generate the running dict tag string from the low data structure - ''' - return '{0[state]}_|-{0[__id__]}_|-{0[name]}_|-{0[fun]}'.format(low) + """ + return "{0[state]}_|-{0[__id__]}_|-{0[name]}_|-{0[fun]}".format(low) def _clean_tag(tag): - ''' + """ Make tag name safe for filenames - ''' + """ return salt.utils.files.safe_filename_leaf(tag) def _l_tag(name, id_): - low = {'name': 'listen_{0}'.format(name), - '__id__': 'listen_{0}'.format(id_), - 'state': 'Listen_Error', - 'fun': 'Listen_Error'} + low = { + "name": "listen_{0}".format(name), + "__id__": "listen_{0}".format(id_), + "state": "Listen_Error", + "fun": "Listen_Error", + } return _gen_tag(low) def _calculate_fake_duration(): - ''' + """ Generate a NULL duration for when states do not run but we want the results to be consistent. - ''' + """ utc_start_time = datetime.datetime.utcnow() - local_start_time = utc_start_time - \ - (datetime.datetime.utcnow() - datetime.datetime.now()) + local_start_time = utc_start_time - ( + datetime.datetime.utcnow() - datetime.datetime.now() + ) utc_finish_time = datetime.datetime.utcnow() start_time = local_start_time.time().isoformat() - delta = (utc_finish_time - utc_start_time) + delta = utc_finish_time - utc_start_time # duration in milliseconds.microseconds duration = (delta.seconds * 1000000 + delta.microseconds) / 1000.0 @@ -188,11 +191,11 @@ def _calculate_fake_duration(): def get_accumulator_dir(cachedir): - ''' + """ Return the directory that accumulator data is stored in, creating it if it doesn't exist. - ''' - fn_ = os.path.join(cachedir, 'accumulator') + """ + fn_ = os.path.join(cachedir, "accumulator") if not os.path.isdir(fn_): # accumulator_dir is not present, create it os.makedirs(fn_) @@ -200,19 +203,19 @@ def get_accumulator_dir(cachedir): def trim_req(req): - ''' + """ Trim any function off of a requisite - ''' + """ reqfirst = next(iter(req)) - if '.' in reqfirst: - return {reqfirst.split('.')[0]: req[reqfirst]} + if "." in reqfirst: + return {reqfirst.split(".")[0]: req[reqfirst]} return req def state_args(id_, state, high): - ''' + """ Return a set of the arguments passed to the named state - ''' + """ args = set() if id_ not in high: return args @@ -228,18 +231,18 @@ def state_args(id_, state, high): def find_name(name, state, high): - ''' + """ Scan high data for the id referencing the given name and return a list of (IDs, state) tuples that match Note: if `state` is sls, then we are looking for all IDs that match the given SLS - ''' + """ ext_id = [] if name in high: ext_id.append((name, state)) # if we are requiring an entire SLS, then we need to add ourselves to everything in that SLS - elif state == 'sls': + elif state == "sls": for nid, item in six.iteritems(high): - if item['__sls__'] == name: + if item["__sls__"] == name: ext_id.append((nid, next(iter(item)))) # otherwise we are requiring a single state, lets find it else: @@ -258,65 +261,65 @@ def find_name(name, state, high): def find_sls_ids(sls, high): - ''' + """ Scan for all ids in the given sls and return them in a dict; {name: state} - ''' + """ ret = [] for nid, item in six.iteritems(high): try: - sls_tgt = item['__sls__'] + sls_tgt = item["__sls__"] except TypeError: - if nid != '__exclude__': + if nid != "__exclude__": log.error( - 'Invalid non-dict item \'%s\' in high data. Value: %r', - nid, item + "Invalid non-dict item '%s' in high data. Value: %r", nid, item ) continue else: if sls_tgt == sls: for st_ in item: - if st_.startswith('__'): + if st_.startswith("__"): continue ret.append((nid, st_)) return ret def format_log(ret): - ''' + """ Format the state into a log message - ''' - msg = '' + """ + msg = "" if isinstance(ret, dict): # Looks like the ret may be a valid state return - if 'changes' in ret: + if "changes" in ret: # Yep, looks like a valid state return - chg = ret['changes'] + chg = ret["changes"] if not chg: - if ret['comment']: - msg = ret['comment'] + if ret["comment"]: + msg = ret["comment"] else: - msg = 'No changes made for {0[name]}'.format(ret) + msg = "No changes made for {0[name]}".format(ret) elif isinstance(chg, dict): - if 'diff' in chg: - if isinstance(chg['diff'], six.string_types): - msg = 'File changed:\n{0}'.format(chg['diff']) + if "diff" in chg: + if isinstance(chg["diff"], six.string_types): + msg = "File changed:\n{0}".format(chg["diff"]) if all([isinstance(x, dict) for x in six.itervalues(chg)]): - if all([('old' in x and 'new' in x) - for x in six.itervalues(chg)]): - msg = 'Made the following changes:\n' + if all([("old" in x and "new" in x) for x in six.itervalues(chg)]): + msg = "Made the following changes:\n" for pkg in chg: - old = chg[pkg]['old'] + old = chg[pkg]["old"] if not old and old not in (False, None): - old = 'absent' - new = chg[pkg]['new'] + old = "absent" + new = chg[pkg]["new"] if not new and new not in (False, None): - new = 'absent' + new = "absent" # This must be able to handle unicode as some package names contain # non-ascii characters like "Français" or "Español". See Issue #33605. - msg += '\'{0}\' changed from \'{1}\' to \'{2}\'\n'.format(pkg, old, new) + msg += "'{0}' changed from '{1}' to '{2}'\n".format( + pkg, old, new + ) if not msg: - msg = six.text_type(ret['changes']) - if ret['result'] is True or ret['result'] is None: + msg = six.text_type(ret["changes"]) + if ret["result"] is True or ret["result"] is None: log.info(msg) else: log.error(msg) @@ -326,9 +329,9 @@ def format_log(ret): def master_compile(master_opts, minion_opts, grains, id_, saltenv): - ''' + """ Compile the master side low state data, and build the hidden state file - ''' + """ st_ = MasterHighState(master_opts, minion_opts, grains, id_, saltenv) return st_.compile_highstate() @@ -342,63 +345,68 @@ def ishashable(obj): def mock_ret(cdata): - ''' + """ Returns a mocked return dict with information about the run, without executing the state function - ''' + """ # As this is expanded it should be sent into the execution module # layer or it should be turned into a standalone loader system - if cdata['args']: - name = cdata['args'][0] + if cdata["args"]: + name = cdata["args"][0] else: - name = cdata['kwargs']['name'] - return {'name': name, - 'comment': 'Not called, mocked', - 'changes': {}, - 'result': True} + name = cdata["kwargs"]["name"] + return { + "name": name, + "comment": "Not called, mocked", + "changes": {}, + "result": True, + } class StateError(Exception): - ''' + """ Custom exception class. - ''' + """ class Compiler(object): - ''' + """ Class used to compile and manage the High Data structure - ''' + """ + def __init__(self, opts, renderers): self.opts = opts self.rend = renderers def render_template(self, template, **kwargs): - ''' + """ Enforce the states in a template - ''' - high = compile_template(template, - self.rend, - self.opts['renderer'], - self.opts['renderer_blacklist'], - self.opts['renderer_whitelist'], - **kwargs) + """ + high = compile_template( + template, + self.rend, + self.opts["renderer"], + self.opts["renderer_blacklist"], + self.opts["renderer_whitelist"], + **kwargs + ) if not high: return high return self.pad_funcs(high) def pad_funcs(self, high): - ''' + """ Turns dot delimited function refs into function strings - ''' + """ for name in high: if not isinstance(high[name], dict): if isinstance(high[name], six.string_types): # Is this is a short state? It needs to be padded! - if '.' in high[name]: - comps = high[name].split('.') + if "." in high[name]: + comps = high[name].split(".") if len(comps) >= 2: # Merge the comps - comps[1] = '.'.join(comps[1:len(comps)]) + comps[1] = ".".join(comps[1 : len(comps)]) high[name] = { # '__sls__': template, # '__env__': None, @@ -408,15 +416,15 @@ class Compiler(object): continue skeys = set() for key in sorted(high[name]): - if key.startswith('_'): + if key.startswith("_"): continue if not isinstance(high[name][key], list): continue - if '.' in key: - comps = key.split('.') + if "." in key: + comps = key.split(".") if len(comps) >= 2: # Merge the comps - comps[1] = '.'.join(comps[1:len(comps)]) + comps[1] = ".".join(comps[1 : len(comps)]) # Salt doesn't support state files such as: # # /etc/redis/redis.conf: @@ -436,54 +444,53 @@ class Compiler(object): return high def verify_high(self, high): - ''' + """ Verify that the high data is viable and follows the data structure - ''' + """ errors = [] if not isinstance(high, dict): - errors.append('High data is not a dictionary and is invalid') + errors.append("High data is not a dictionary and is invalid") reqs = OrderedDict() for name, body in six.iteritems(high): - if name.startswith('__'): + if name.startswith("__"): continue if not isinstance(name, six.string_types): errors.append( - 'ID \'{0}\' in SLS \'{1}\' is not formed as a string, but ' - 'is a {2}'.format( - name, - body['__sls__'], - type(name).__name__ - ) + "ID '{0}' in SLS '{1}' is not formed as a string, but " + "is a {2}".format(name, body["__sls__"], type(name).__name__) ) if not isinstance(body, dict): - err = ('The type {0} in {1} is not formatted as a dictionary' - .format(name, body)) + err = "The type {0} in {1} is not formatted as a dictionary".format( + name, body + ) errors.append(err) continue for state in body: - if state.startswith('__'): + if state.startswith("__"): continue if not isinstance(body[state], list): errors.append( - 'State \'{0}\' in SLS \'{1}\' is not formed as a list' - .format(name, body['__sls__']) + "State '{0}' in SLS '{1}' is not formed as a list".format( + name, body["__sls__"] + ) ) else: fun = 0 - if '.' in state: + if "." in state: fun += 1 for arg in body[state]: if isinstance(arg, six.string_types): fun += 1 - if ' ' in arg.strip(): - errors.append(('The function "{0}" in state ' - '"{1}" in SLS "{2}" has ' - 'whitespace, a function with whitespace is ' - 'not supported, perhaps this is an argument ' - 'that is missing a ":"').format( - arg, - name, - body['__sls__'])) + if " " in arg.strip(): + errors.append( + ( + 'The function "{0}" in state ' + '"{1}" in SLS "{2}" has ' + "whitespace, a function with whitespace is " + "not supported, perhaps this is an argument " + 'that is missing a ":"' + ).format(arg, name, body["__sls__"]) + ) elif isinstance(arg, dict): # The arg is a dict, if the arg is require or # watch, it must be a list. @@ -491,52 +498,57 @@ class Compiler(object): # Add the requires to the reqs dict and check them # all for recursive requisites. argfirst = next(iter(arg)) - if argfirst in ('require', 'watch', 'prereq', 'onchanges'): + if argfirst in ("require", "watch", "prereq", "onchanges"): if not isinstance(arg[argfirst], list): - errors.append(('The {0}' - ' statement in state \'{1}\' in SLS \'{2}\' ' - 'needs to be formed as a list').format( - argfirst, - name, - body['__sls__'] - )) + errors.append( + ( + "The {0}" + " statement in state '{1}' in SLS '{2}' " + "needs to be formed as a list" + ).format(argfirst, name, body["__sls__"]) + ) # It is a list, verify that the members of the # list are all single key dicts. else: - reqs[name] = {'state': state} + reqs[name] = {"state": state} for req in arg[argfirst]: if isinstance(req, six.string_types): - req = {'id': req} + req = {"id": req} if not isinstance(req, dict): - err = ('Requisite declaration {0}' - ' in SLS {1} is not formed as a' - ' single key dictionary').format( - req, - body['__sls__']) + err = ( + "Requisite declaration {0}" + " in SLS {1} is not formed as a" + " single key dictionary" + ).format(req, body["__sls__"]) errors.append(err) continue req_key = next(iter(req)) req_val = req[req_key] - if '.' in req_key: - errors.append(( - 'Invalid requisite type \'{0}\' ' - 'in state \'{1}\', in SLS ' - '\'{2}\'. Requisite types must ' - 'not contain dots, did you ' - 'mean \'{3}\'?'.format( - req_key, - name, - body['__sls__'], - req_key[:req_key.find('.')] + if "." in req_key: + errors.append( + ( + "Invalid requisite type '{0}' " + "in state '{1}', in SLS " + "'{2}'. Requisite types must " + "not contain dots, did you " + "mean '{3}'?".format( + req_key, + name, + body["__sls__"], + req_key[: req_key.find(".")], + ) ) - )) + ) if not ishashable(req_val): - errors.append(( - 'Illegal requisite "{0}", ' - 'is SLS {1}\n' + errors.append( + ( + 'Illegal requisite "{0}", ' + "is SLS {1}\n" ).format( six.text_type(req_val), - body['__sls__'])) + body["__sls__"], + ) + ) continue # Check for global recursive requisites @@ -547,98 +559,111 @@ class Compiler(object): if req_val in reqs: if name in reqs[req_val]: if reqs[req_val][name] == state: - if reqs[req_val]['state'] == reqs[name][req_val]: - err = ('A recursive ' - 'requisite was found, SLS ' - '"{0}" ID "{1}" ID "{2}"' + if ( + reqs[req_val]["state"] + == reqs[name][req_val] + ): + err = ( + "A recursive " + "requisite was found, SLS " + '"{0}" ID "{1}" ID "{2}"' ).format( - body['__sls__'], - name, - req_val - ) + body["__sls__"], + name, + req_val, + ) errors.append(err) # Make sure that there is only one key in the # dict if len(list(arg)) != 1: - errors.append(('Multiple dictionaries ' - 'defined in argument of state \'{0}\' in SLS' - ' \'{1}\'').format( - name, - body['__sls__'])) + errors.append( + ( + "Multiple dictionaries " + "defined in argument of state '{0}' in SLS" + " '{1}'" + ).format(name, body["__sls__"]) + ) if not fun: - if state == 'require' or state == 'watch': + if state == "require" or state == "watch": continue - errors.append(('No function declared in state \'{0}\' in' - ' SLS \'{1}\'').format(state, body['__sls__'])) + errors.append( + ( + "No function declared in state '{0}' in" " SLS '{1}'" + ).format(state, body["__sls__"]) + ) elif fun > 1: errors.append( - 'Too many functions declared in state \'{0}\' in ' - 'SLS \'{1}\''.format(state, body['__sls__']) + "Too many functions declared in state '{0}' in " + "SLS '{1}'".format(state, body["__sls__"]) ) return errors def order_chunks(self, chunks): - ''' + """ Sort the chunk list verifying that the chunks follow the order specified in the order options. - ''' + """ cap = 1 for chunk in chunks: - if 'order' in chunk: - if not isinstance(chunk['order'], int): + if "order" in chunk: + if not isinstance(chunk["order"], int): continue - chunk_order = chunk['order'] + chunk_order = chunk["order"] if chunk_order > cap - 1 and chunk_order > 0: cap = chunk_order + 100 for chunk in chunks: - if 'order' not in chunk: - chunk['order'] = cap + if "order" not in chunk: + chunk["order"] = cap continue - if not isinstance(chunk['order'], (int, float)): - if chunk['order'] == 'last': - chunk['order'] = cap + 1000000 - elif chunk['order'] == 'first': - chunk['order'] = 0 + if not isinstance(chunk["order"], (int, float)): + if chunk["order"] == "last": + chunk["order"] = cap + 1000000 + elif chunk["order"] == "first": + chunk["order"] = 0 else: - chunk['order'] = cap - if 'name_order' in chunk: - chunk['order'] = chunk['order'] + chunk.pop('name_order') / 10000.0 - if chunk['order'] < 0: - chunk['order'] = cap + 1000000 + chunk['order'] - chunk['name'] = salt.utils.data.decode(chunk['name']) - chunks.sort(key=lambda chunk: (chunk['order'], '{0[state]}{0[name]}{0[fun]}'.format(chunk))) + chunk["order"] = cap + if "name_order" in chunk: + chunk["order"] = chunk["order"] + chunk.pop("name_order") / 10000.0 + if chunk["order"] < 0: + chunk["order"] = cap + 1000000 + chunk["order"] + chunk["name"] = salt.utils.data.decode(chunk["name"]) + chunks.sort( + key=lambda chunk: ( + chunk["order"], + "{0[state]}{0[name]}{0[fun]}".format(chunk), + ) + ) return chunks def compile_high_data(self, high): - ''' + """ "Compile" the high data as it is retrieved from the CLI or YAML into the individual state executor structures - ''' + """ chunks = [] for name, body in six.iteritems(high): - if name.startswith('__'): + if name.startswith("__"): continue for state, run in six.iteritems(body): funcs = set() names = [] - if state.startswith('__'): + if state.startswith("__"): continue - chunk = {'state': state, - 'name': name} - if '__sls__' in body: - chunk['__sls__'] = body['__sls__'] - if '__env__' in body: - chunk['__env__'] = body['__env__'] - chunk['__id__'] = name + chunk = {"state": state, "name": name} + if "__sls__" in body: + chunk["__sls__"] = body["__sls__"] + if "__env__" in body: + chunk["__env__"] = body["__env__"] + chunk["__id__"] = name for arg in run: if isinstance(arg, six.string_types): funcs.add(arg) continue if isinstance(arg, dict): for key, val in six.iteritems(arg): - if key == 'names': + if key == "names": for _name in val: if _name not in names: names.append(_name) @@ -651,33 +676,33 @@ class Compiler(object): live = copy.deepcopy(chunk) if isinstance(entry, dict): low_name = next(six.iterkeys(entry)) - live['name'] = low_name + live["name"] = low_name list(map(live.update, entry[low_name])) else: - live['name'] = entry - live['name_order'] = name_order + live["name"] = entry + live["name_order"] = name_order name_order = name_order + 1 for fun in funcs: - live['fun'] = fun + live["fun"] = fun chunks.append(live) else: live = copy.deepcopy(chunk) for fun in funcs: - live['fun'] = fun + live["fun"] = fun chunks.append(live) chunks = self.order_chunks(chunks) return chunks def apply_exclude(self, high): - ''' + """ Read in the __exclude__ list and remove all excluded objects from the high data - ''' - if '__exclude__' not in high: + """ + if "__exclude__" not in high: return high ex_sls = set() ex_id = set() - exclude = high.pop('__exclude__') + exclude = high.pop("__exclude__") for exc in exclude: if isinstance(exc, six.string_types): # The exclude statement is a string, assume it is an sls @@ -687,17 +712,17 @@ class Compiler(object): if len(exc) != 1: continue key = next(six.iterkeys(exc)) - if key == 'sls': - ex_sls.add(exc['sls']) - elif key == 'id': - ex_id.add(exc['id']) + if key == "sls": + ex_sls.add(exc["sls"]) + elif key == "id": + ex_id.add(exc["id"]) # Now the excludes have been simplified, use them if ex_sls: # There are sls excludes, find the associtaed ids for name, body in six.iteritems(high): - if name.startswith('__'): + if name.startswith("__"): continue - if body.get('__sls__', '') in ex_sls: + if body.get("__sls__", "") in ex_sls: ex_id.add(name) for id_ in ex_id: if id_ in high: @@ -706,23 +731,25 @@ class Compiler(object): class State(object): - ''' + """ Class used to execute salt states - ''' + """ + def __init__( - self, - opts, - pillar_override=None, - jid=None, - pillar_enc=None, - proxy=None, - context=None, - mocked=False, - loader='states', - initial_pillar=None): + self, + opts, + pillar_override=None, + jid=None, + pillar_enc=None, + proxy=None, + context=None, + mocked=False, + loader="states", + initial_pillar=None, + ): self.states_loader = loader - if 'grains' not in opts: - opts['grains'] = salt.loader.grains(opts) + if "grains" not in opts: + opts["grains"] = salt.loader.grains(opts) self.opts = opts self.proxy = proxy self._pillar_override = pillar_override @@ -732,21 +759,22 @@ class State(object): except AttributeError: pillar_enc = six.text_type(pillar_enc).lower() self._pillar_enc = pillar_enc - log.debug('Gathering pillar data for state run') + log.debug("Gathering pillar data for state run") if initial_pillar and not self._pillar_override: - self.opts['pillar'] = initial_pillar + self.opts["pillar"] = initial_pillar else: # Compile pillar data - self.opts['pillar'] = self._gather_pillar() + self.opts["pillar"] = self._gather_pillar() # Reapply overrides on top of compiled pillar if self._pillar_override: - self.opts['pillar'] = salt.utils.dictupdate.merge( - self.opts['pillar'], + self.opts["pillar"] = salt.utils.dictupdate.merge( + self.opts["pillar"], self._pillar_override, - self.opts.get('pillar_source_merging_strategy', 'smart'), - self.opts.get('renderer', 'yaml'), - self.opts.get('pillar_merge_lists', False)) - log.debug('Finished gathering pillar data for state run') + self.opts.get("pillar_source_merging_strategy", "smart"), + self.opts.get("renderer", "yaml"), + self.opts.get("pillar_merge_lists", False), + ) + log.debug("Finished gathering pillar data for state run") self.state_con = context or {} self.load_modules() self.active = set() @@ -759,9 +787,9 @@ class State(object): self.mocked = mocked def _gather_pillar(self): - ''' + """ Whenever a state run starts, gather the pillar data fresh - ''' + """ if self._pillar_override: if self._pillar_enc: try: @@ -769,11 +797,12 @@ class State(object): self._pillar_override, self._pillar_enc, translate_newlines=True, - renderers=getattr(self, 'rend', None), + renderers=getattr(self, "rend", None), opts=self.opts, - valid_rend=self.opts['decrypt_pillar_renderers']) + valid_rend=self.opts["decrypt_pillar_renderers"], + ) except Exception as exc: # pylint: disable=broad-except - log.error('Failed to decrypt pillar override: %s', exc) + log.error("Failed to decrypt pillar override: %s", exc) if isinstance(self._pillar_override, six.string_types): # This can happen if an entire pillar dictionary was passed as @@ -782,366 +811,415 @@ class State(object): # Use the YAML loader to convert that to a Python dictionary. try: self._pillar_override = yamlloader.load( - self._pillar_override, - Loader=yamlloader.SaltYamlSafeLoader) + self._pillar_override, Loader=yamlloader.SaltYamlSafeLoader + ) except Exception as exc: # pylint: disable=broad-except - log.error('Failed to load CLI pillar override') + log.error("Failed to load CLI pillar override") log.exception(exc) if not isinstance(self._pillar_override, dict): - log.error('Pillar override was not passed as a dictionary') + log.error("Pillar override was not passed as a dictionary") self._pillar_override = None pillar = salt.pillar.get_pillar( - self.opts, - self.opts['grains'], - self.opts['id'], - self.opts['saltenv'], - pillar_override=self._pillar_override, - pillarenv=self.opts.get('pillarenv')) + self.opts, + self.opts["grains"], + self.opts["id"], + self.opts["saltenv"], + pillar_override=self._pillar_override, + pillarenv=self.opts.get("pillarenv"), + ) return pillar.compile_pillar() def _mod_init(self, low): - ''' + """ Check the module initialization function, if this is the first run of a state package that has a mod_init function, then execute the mod_init function in the state module. - ''' + """ # ensure that the module is loaded try: - self.states['{0}.{1}'.format(low['state'], low['fun'])] # pylint: disable=W0106 + self.states[ + "{0}.{1}".format(low["state"], low["fun"]) + ] # pylint: disable=W0106 except KeyError: return - minit = '{0}.mod_init'.format(low['state']) - if low['state'] not in self.mod_init: + minit = "{0}.mod_init".format(low["state"]) + if low["state"] not in self.mod_init: if minit in self.states._dict: mret = self.states[minit](low) if not mret: return - self.mod_init.add(low['state']) + self.mod_init.add(low["state"]) def _mod_aggregate(self, low, running, chunks): - ''' + """ Execute the aggregation systems to runtime modify the low chunk - ''' - agg_opt = self.functions['config.option']('state_aggregate') - if 'aggregate' in low: - agg_opt = low['aggregate'] + """ + agg_opt = self.functions["config.option"]("state_aggregate") + if "aggregate" in low: + agg_opt = low["aggregate"] if agg_opt is True: - agg_opt = [low['state']] + agg_opt = [low["state"]] elif not isinstance(agg_opt, list): return low - if low['state'] in agg_opt and not low.get('__agg__'): - agg_fun = '{0}.mod_aggregate'.format(low['state']) + if low["state"] in agg_opt and not low.get("__agg__"): + agg_fun = "{0}.mod_aggregate".format(low["state"]) if agg_fun in self.states: try: low = self.states[agg_fun](low, chunks, running) - low['__agg__'] = True + low["__agg__"] = True except TypeError: - log.error('Failed to execute aggregate for state %s', low['state']) + log.error("Failed to execute aggregate for state %s", low["state"]) return low def _run_check(self, low_data): - ''' + """ Check that unless doesn't return 0, and that onlyif returns a 0. - ''' - ret = {'result': False, 'comment': []} + """ + ret = {"result": False, "comment": []} cmd_opts = {} - if 'shell' in self.opts['grains']: - cmd_opts['shell'] = self.opts['grains'].get('shell') + if "shell" in self.opts["grains"]: + cmd_opts["shell"] = self.opts["grains"].get("shell") - if 'onlyif' in low_data: + if "onlyif" in low_data: _ret = self._run_check_onlyif(low_data, cmd_opts) - ret['result'] = _ret['result'] - ret['comment'].append(_ret['comment']) - if 'skip_watch' in _ret: - ret['skip_watch'] = _ret['skip_watch'] + ret["result"] = _ret["result"] + ret["comment"].append(_ret["comment"]) + if "skip_watch" in _ret: + ret["skip_watch"] = _ret["skip_watch"] - if 'unless' in low_data: + if "unless" in low_data: _ret = self._run_check_unless(low_data, cmd_opts) # If either result is True, the returned result should be True - ret['result'] = _ret['result'] or ret['result'] - ret['comment'].append(_ret['comment']) - if 'skip_watch' in _ret: + ret["result"] = _ret["result"] or ret["result"] + ret["comment"].append(_ret["comment"]) + if "skip_watch" in _ret: # If either result is True, the returned result should be True - ret['skip_watch'] = _ret['skip_watch'] or ret['skip_watch'] + ret["skip_watch"] = _ret["skip_watch"] or ret["skip_watch"] return ret def _run_check_function(self, entry): """Format slot args and run unless/onlyif function.""" - fun = entry.pop('fun') - args = entry.pop('args') if 'args' in entry else [] - cdata = { - 'args': args, - 'kwargs': entry - } + fun = entry.pop("fun") + args = entry.pop("args") if "args" in entry else [] + cdata = {"args": args, "kwargs": entry} self.format_slots(cdata) - return self.functions[fun](*cdata['args'], **cdata['kwargs']) + return self.functions[fun](*cdata["args"], **cdata["kwargs"]) def _run_check_onlyif(self, low_data, cmd_opts): - ''' + """ Check that unless doesn't return 0, and that onlyif returns a 0. - ''' - ret = {'result': False} + """ + ret = {"result": False} - if not isinstance(low_data['onlyif'], list): - low_data_onlyif = [low_data['onlyif']] + if not isinstance(low_data["onlyif"], list): + low_data_onlyif = [low_data["onlyif"]] else: - low_data_onlyif = low_data['onlyif'] + low_data_onlyif = low_data["onlyif"] def _check_cmd(cmd): - if cmd != 0 and ret['result'] is False: - ret.update({'comment': 'onlyif condition is false', - 'skip_watch': True, - 'result': True}) + if cmd != 0 and ret["result"] is False: + ret.update( + { + "comment": "onlyif condition is false", + "skip_watch": True, + "result": True, + } + ) elif cmd == 0: - ret.update({'comment': 'onlyif condition is true', 'result': False}) + ret.update({"comment": "onlyif condition is true", "result": False}) for entry in low_data_onlyif: if isinstance(entry, six.string_types): - cmd = self.functions['cmd.retcode']( - entry, ignore_retcode=True, python_shell=True, **cmd_opts) - log.debug('Last command return code: %s', cmd) + cmd = self.functions["cmd.retcode"]( + entry, ignore_retcode=True, python_shell=True, **cmd_opts + ) + log.debug("Last command return code: %s", cmd) _check_cmd(cmd) elif isinstance(entry, dict): - if 'fun' not in entry: - ret['comment'] = 'no `fun` argument in onlyif: {0}'.format(entry) - log.warning(ret['comment']) + if "fun" not in entry: + ret["comment"] = "no `fun` argument in onlyif: {0}".format(entry) + log.warning(ret["comment"]) return ret result = self._run_check_function(entry) - if self.state_con.get('retcode', 0): - _check_cmd(self.state_con['retcode']) + if self.state_con.get("retcode", 0): + _check_cmd(self.state_con["retcode"]) elif not result: - ret.update({'comment': 'onlyif condition is false', - 'skip_watch': True, - 'result': True}) + ret.update( + { + "comment": "onlyif condition is false", + "skip_watch": True, + "result": True, + } + ) else: - ret.update({'comment': 'onlyif condition is true', - 'result': False}) + ret.update({"comment": "onlyif condition is true", "result": False}) else: - ret.update({'comment': 'onlyif execution failed, bad type passed', 'result': False}) + ret.update( + { + "comment": "onlyif execution failed, bad type passed", + "result": False, + } + ) return ret def _run_check_unless(self, low_data, cmd_opts): - ''' + """ Check that unless doesn't return 0, and that onlyif returns a 0. - ''' - ret = {'result': False} + """ + ret = {"result": False} - if not isinstance(low_data['unless'], list): - low_data_unless = [low_data['unless']] + if not isinstance(low_data["unless"], list): + low_data_unless = [low_data["unless"]] else: - low_data_unless = low_data['unless'] + low_data_unless = low_data["unless"] def _check_cmd(cmd): - if cmd == 0 and ret['result'] is False: - ret.update({'comment': 'unless condition is true', - 'skip_watch': True, - 'result': True}) + if cmd == 0 and ret["result"] is False: + ret.update( + { + "comment": "unless condition is true", + "skip_watch": True, + "result": True, + } + ) elif cmd != 0: - ret.update({'comment': 'unless condition is false', 'result': False}) + ret.update({"comment": "unless condition is false", "result": False}) for entry in low_data_unless: if isinstance(entry, six.string_types): - cmd = self.functions['cmd.retcode'](entry, ignore_retcode=True, python_shell=True, **cmd_opts) - log.debug('Last command return code: %s', cmd) + cmd = self.functions["cmd.retcode"]( + entry, ignore_retcode=True, python_shell=True, **cmd_opts + ) + log.debug("Last command return code: %s", cmd) _check_cmd(cmd) elif isinstance(entry, dict): - if 'fun' not in entry: - ret['comment'] = 'no `fun` argument in unless: {0}'.format(entry) - log.warning(ret['comment']) + if "fun" not in entry: + ret["comment"] = "no `fun` argument in unless: {0}".format(entry) + log.warning(ret["comment"]) return ret result = self._run_check_function(entry) - if self.state_con.get('retcode', 0): - _check_cmd(self.state_con['retcode']) + if self.state_con.get("retcode", 0): + _check_cmd(self.state_con["retcode"]) elif result: - ret.update({'comment': 'unless condition is true', - 'skip_watch': True, - 'result': True}) + ret.update( + { + "comment": "unless condition is true", + "skip_watch": True, + "result": True, + } + ) else: - ret.update({'comment': 'unless condition is false', - 'result': False}) + ret.update( + {"comment": "unless condition is false", "result": False} + ) else: - ret.update({'comment': 'unless condition is false, bad type passed', 'result': False}) + ret.update( + { + "comment": "unless condition is false, bad type passed", + "result": False, + } + ) # No reason to stop, return ret return ret def _run_check_cmd(self, low_data): - ''' + """ Alter the way a successful state run is determined - ''' - ret = {'result': False} + """ + ret = {"result": False} cmd_opts = {} - if 'shell' in self.opts['grains']: - cmd_opts['shell'] = self.opts['grains'].get('shell') - for entry in low_data['check_cmd']: - cmd = self.functions['cmd.retcode']( - entry, ignore_retcode=True, python_shell=True, **cmd_opts) - log.debug('Last command return code: %s', cmd) - if cmd == 0 and ret['result'] is False: - ret.update({'comment': 'check_cmd determined the state succeeded', 'result': True}) + if "shell" in self.opts["grains"]: + cmd_opts["shell"] = self.opts["grains"].get("shell") + for entry in low_data["check_cmd"]: + cmd = self.functions["cmd.retcode"]( + entry, ignore_retcode=True, python_shell=True, **cmd_opts + ) + log.debug("Last command return code: %s", cmd) + if cmd == 0 and ret["result"] is False: + ret.update( + { + "comment": "check_cmd determined the state succeeded", + "result": True, + } + ) elif cmd != 0: - ret.update({'comment': 'check_cmd determined the state failed', 'result': False}) + ret.update( + { + "comment": "check_cmd determined the state failed", + "result": False, + } + ) return ret return ret def reset_run_num(self): - ''' + """ Rest the run_num value to 0 - ''' + """ self.__run_num = 0 def _load_states(self): - ''' + """ Read the state loader value and loadup the correct states subsystem - ''' - if self.states_loader == 'thorium': - self.states = salt.loader.thorium(self.opts, self.functions, {}) # TODO: Add runners, proxy? + """ + if self.states_loader == "thorium": + self.states = salt.loader.thorium( + self.opts, self.functions, {} + ) # TODO: Add runners, proxy? else: - self.states = salt.loader.states(self.opts, self.functions, self.utils, - self.serializers, context=self.state_con, proxy=self.proxy) + self.states = salt.loader.states( + self.opts, + self.functions, + self.utils, + self.serializers, + context=self.state_con, + proxy=self.proxy, + ) def load_modules(self, data=None, proxy=None): - ''' + """ Load the modules into the state - ''' - log.info('Loading fresh modules for state activity') + """ + log.info("Loading fresh modules for state activity") self.utils = salt.loader.utils(self.opts) - self.functions = salt.loader.minion_mods(self.opts, self.state_con, - utils=self.utils, - proxy=self.proxy) + self.functions = salt.loader.minion_mods( + self.opts, self.state_con, utils=self.utils, proxy=self.proxy + ) if isinstance(data, dict): - if data.get('provider', False): - if isinstance(data['provider'], six.string_types): - providers = [{data['state']: data['provider']}] - elif isinstance(data['provider'], list): - providers = data['provider'] + if data.get("provider", False): + if isinstance(data["provider"], six.string_types): + providers = [{data["state"]: data["provider"]}] + elif isinstance(data["provider"], list): + providers = data["provider"] else: providers = {} for provider in providers: for mod in provider: - funcs = salt.loader.raw_mod(self.opts, - provider[mod], - self.functions) + funcs = salt.loader.raw_mod( + self.opts, provider[mod], self.functions + ) if funcs: for func in funcs: - f_key = '{0}{1}'.format( - mod, - func[func.rindex('.'):] - ) + f_key = "{0}{1}".format(mod, func[func.rindex(".") :]) self.functions[f_key] = funcs[func] self.serializers = salt.loader.serializers(self.opts) self._load_states() - self.rend = salt.loader.render(self.opts, self.functions, - states=self.states, proxy=self.proxy, context=self.state_con) + self.rend = salt.loader.render( + self.opts, + self.functions, + states=self.states, + proxy=self.proxy, + context=self.state_con, + ) def module_refresh(self): - ''' + """ Refresh all the modules - ''' - log.debug('Refreshing modules...') - if self.opts['grains'].get('os') != 'MacOS': + """ + log.debug("Refreshing modules...") + if self.opts["grains"].get("os") != "MacOS": # In case a package has been installed into the current python # process 'site-packages', the 'site' module needs to be reloaded in # order for the newly installed package to be importable. try: reload_module(site) except RuntimeError: - log.error('Error encountered during module reload. Modules were not reloaded.') + log.error( + "Error encountered during module reload. Modules were not reloaded." + ) except TypeError: - log.error('Error encountered during module reload. Modules were not reloaded.') + log.error( + "Error encountered during module reload. Modules were not reloaded." + ) self.load_modules() - if not self.opts.get('local', False) and self.opts.get('multiprocessing', True): - self.functions['saltutil.refresh_modules']() + if not self.opts.get("local", False) and self.opts.get("multiprocessing", True): + self.functions["saltutil.refresh_modules"]() def check_refresh(self, data, ret): - ''' + """ Check to see if the modules for this state instance need to be updated, only update if the state is a file or a package and if it changed something. If the file function is managed check to see if the file is a possible module type, e.g. a python, pyx, or .so. Always refresh if the function is recurse, since that can lay down anything. - ''' + """ _reload_modules = False - if data.get('reload_grains', False): - log.debug('Refreshing grains...') - self.opts['grains'] = salt.loader.grains(self.opts) + if data.get("reload_grains", False): + log.debug("Refreshing grains...") + self.opts["grains"] = salt.loader.grains(self.opts) _reload_modules = True - if data.get('reload_pillar', False): - log.debug('Refreshing pillar...') - self.opts['pillar'] = self._gather_pillar() + if data.get("reload_pillar", False): + log.debug("Refreshing pillar...") + self.opts["pillar"] = self._gather_pillar() _reload_modules = True - if not ret['changes']: - if data.get('force_reload_modules', False): + if not ret["changes"]: + if data.get("force_reload_modules", False): self.module_refresh() return - if data.get('reload_modules', False) or _reload_modules: + if data.get("reload_modules", False) or _reload_modules: # User explicitly requests a reload self.module_refresh() return - if data['state'] == 'file': - if data['fun'] == 'managed': - if data['name'].endswith( - ('.py', '.pyx', '.pyo', '.pyc', '.so')): + if data["state"] == "file": + if data["fun"] == "managed": + if data["name"].endswith((".py", ".pyx", ".pyo", ".pyc", ".so")): self.module_refresh() - elif data['fun'] == 'recurse': + elif data["fun"] == "recurse": self.module_refresh() - elif data['fun'] == 'symlink': - if 'bin' in data['name']: + elif data["fun"] == "symlink": + if "bin" in data["name"]: self.module_refresh() - elif data['state'] in ('pkg', 'ports'): + elif data["state"] in ("pkg", "ports"): self.module_refresh() def verify_data(self, data): - ''' + """ Verify the data, return an error statement if something is wrong - ''' + """ errors = [] - if 'state' not in data: + if "state" not in data: errors.append('Missing "state" data') - if 'fun' not in data: + if "fun" not in data: errors.append('Missing "fun" data') - if 'name' not in data: + if "name" not in data: errors.append('Missing "name" data') - if data['name'] and not isinstance(data['name'], six.string_types): + if data["name"] and not isinstance(data["name"], six.string_types): errors.append( - 'ID \'{0}\' {1}is not formed as a string, but is a {2}'.format( - data['name'], - 'in SLS \'{0}\' '.format(data['__sls__']) - if '__sls__' in data else '', - type(data['name']).__name__ + "ID '{0}' {1}is not formed as a string, but is a {2}".format( + data["name"], + "in SLS '{0}' ".format(data["__sls__"]) + if "__sls__" in data + else "", + type(data["name"]).__name__, ) ) if errors: return errors - full = data['state'] + '.' + data['fun'] + full = data["state"] + "." + data["fun"] if full not in self.states: - if '__sls__' in data: + if "__sls__" in data: errors.append( - 'State \'{0}\' was not found in SLS \'{1}\''.format( - full, - data['__sls__'] - ) + "State '{0}' was not found in SLS '{1}'".format( + full, data["__sls__"] ) + ) reason = self.states.missing_fun_string(full) if reason: - errors.append('Reason: {0}'.format(reason)) + errors.append("Reason: {0}".format(reason)) else: - errors.append( - 'Specified state \'{0}\' was not found'.format( - full - ) - ) + errors.append("Specified state '{0}' was not found".format(full)) else: # First verify that the parameters are met aspec = salt.utils.args.get_function_argspec(self.states[full]) @@ -1154,101 +1232,105 @@ class State(object): for ind in range(arglen - deflen): if aspec.args[ind] not in data: errors.append( - 'Missing parameter {0} for state {1}'.format( - aspec.args[ind], - full + "Missing parameter {0} for state {1}".format( + aspec.args[ind], full ) ) # If this chunk has a recursive require, then it will cause a # recursive loop when executing, check for it - reqdec = '' - if 'require' in data: - reqdec = 'require' - if 'watch' in data: + reqdec = "" + if "require" in data: + reqdec = "require" + if "watch" in data: # Check to see if the service has a mod_watch function, if it does # not, then just require # to just require extend the require statement with the contents # of watch so that the mod_watch function is not called and the # requisite capability is still used - if '{0}.mod_watch'.format(data['state']) not in self.states: - if 'require' in data: - data['require'].extend(data.pop('watch')) + if "{0}.mod_watch".format(data["state"]) not in self.states: + if "require" in data: + data["require"].extend(data.pop("watch")) else: - data['require'] = data.pop('watch') - reqdec = 'require' + data["require"] = data.pop("watch") + reqdec = "require" else: - reqdec = 'watch' + reqdec = "watch" if reqdec: for req in data[reqdec]: reqfirst = next(iter(req)) - if data['state'] == reqfirst: - if (fnmatch.fnmatch(data['name'], req[reqfirst]) - or fnmatch.fnmatch(data['__id__'], req[reqfirst])): - err = ('Recursive require detected in SLS {0} for' - ' require {1} in ID {2}').format( - data['__sls__'], - req, - data['__id__']) + if data["state"] == reqfirst: + if fnmatch.fnmatch(data["name"], req[reqfirst]) or fnmatch.fnmatch( + data["__id__"], req[reqfirst] + ): + err = ( + "Recursive require detected in SLS {0} for" + " require {1} in ID {2}" + ).format(data["__sls__"], req, data["__id__"]) errors.append(err) return errors def verify_high(self, high): - ''' + """ Verify that the high data is viable and follows the data structure - ''' + """ errors = [] if not isinstance(high, dict): - errors.append('High data is not a dictionary and is invalid') + errors.append("High data is not a dictionary and is invalid") reqs = OrderedDict() for name, body in six.iteritems(high): try: - if name.startswith('__'): + if name.startswith("__"): continue except AttributeError: pass if not isinstance(name, six.string_types): errors.append( - 'ID \'{0}\' in SLS \'{1}\' is not formed as a string, but ' - 'is a {2}. It may need to be quoted.'.format( - name, body['__sls__'], type(name).__name__) + "ID '{0}' in SLS '{1}' is not formed as a string, but " + "is a {2}. It may need to be quoted.".format( + name, body["__sls__"], type(name).__name__ + ) ) if not isinstance(body, dict): - err = ('The type {0} in {1} is not formatted as a dictionary' - .format(name, body)) + err = "The type {0} in {1} is not formatted as a dictionary".format( + name, body + ) errors.append(err) continue for state in body: - if state.startswith('__'): + if state.startswith("__"): continue if body[state] is None: errors.append( - 'ID \'{0}\' in SLS \'{1}\' contains a short declaration ' - '({2}) with a trailing colon. When not passing any ' - 'arguments to a state, the colon must be omitted.' - .format(name, body['__sls__'], state) + "ID '{0}' in SLS '{1}' contains a short declaration " + "({2}) with a trailing colon. When not passing any " + "arguments to a state, the colon must be omitted.".format( + name, body["__sls__"], state + ) ) continue if not isinstance(body[state], list): errors.append( - 'State \'{0}\' in SLS \'{1}\' is not formed as a list' - .format(name, body['__sls__']) + "State '{0}' in SLS '{1}' is not formed as a list".format( + name, body["__sls__"] + ) ) else: fun = 0 - if '.' in state: + if "." in state: fun += 1 for arg in body[state]: if isinstance(arg, six.string_types): fun += 1 - if ' ' in arg.strip(): - errors.append(('The function "{0}" in state ' - '"{1}" in SLS "{2}" has ' - 'whitespace, a function with whitespace is ' - 'not supported, perhaps this is an argument ' - 'that is missing a ":"').format( - arg, - name, - body['__sls__'])) + if " " in arg.strip(): + errors.append( + ( + 'The function "{0}" in state ' + '"{1}" in SLS "{2}" has ' + "whitespace, a function with whitespace is " + "not supported, perhaps this is an argument " + 'that is missing a ":"' + ).format(arg, name, body["__sls__"]) + ) elif isinstance(arg, dict): # The arg is a dict, if the arg is require or # watch, it must be a list. @@ -1256,22 +1338,19 @@ class State(object): # Add the requires to the reqs dict and check them # all for recursive requisites. argfirst = next(iter(arg)) - if argfirst == 'names': + if argfirst == "names": if not isinstance(arg[argfirst], list): errors.append( - 'The \'names\' argument in state ' - '\'{0}\' in SLS \'{1}\' needs to be ' - 'formed as a list' - .format(name, body['__sls__']) + "The 'names' argument in state " + "'{0}' in SLS '{1}' needs to be " + "formed as a list".format(name, body["__sls__"]) ) - if argfirst in ('require', 'watch', 'prereq', 'onchanges'): + if argfirst in ("require", "watch", "prereq", "onchanges"): if not isinstance(arg[argfirst], list): errors.append( - 'The {0} statement in state \'{1}\' in ' - 'SLS \'{2}\' needs to be formed as a ' - 'list'.format(argfirst, - name, - body['__sls__']) + "The {0} statement in state '{1}' in " + "SLS '{2}' needs to be formed as a " + "list".format(argfirst, name, body["__sls__"]) ) # It is a list, verify that the members of the # list are all single key dicts. @@ -1279,35 +1358,37 @@ class State(object): reqs[name] = OrderedDict(state=state) for req in arg[argfirst]: if isinstance(req, six.string_types): - req = {'id': req} + req = {"id": req} if not isinstance(req, dict): - err = ('Requisite declaration {0}' - ' in SLS {1} is not formed as a' - ' single key dictionary').format( - req, - body['__sls__']) + err = ( + "Requisite declaration {0}" + " in SLS {1} is not formed as a" + " single key dictionary" + ).format(req, body["__sls__"]) errors.append(err) continue req_key = next(iter(req)) req_val = req[req_key] - if '.' in req_key: + if "." in req_key: errors.append( - 'Invalid requisite type \'{0}\' ' - 'in state \'{1}\', in SLS ' - '\'{2}\'. Requisite types must ' - 'not contain dots, did you ' - 'mean \'{3}\'?'.format( + "Invalid requisite type '{0}' " + "in state '{1}', in SLS " + "'{2}'. Requisite types must " + "not contain dots, did you " + "mean '{3}'?".format( req_key, name, - body['__sls__'], - req_key[:req_key.find('.')] + body["__sls__"], + req_key[: req_key.find(".")], ) ) if not ishashable(req_val): - errors.append(( - 'Illegal requisite "{0}", ' - 'please check your syntax.\n' - ).format(req_val)) + errors.append( + ( + 'Illegal requisite "{0}", ' + "please check your syntax.\n" + ).format(req_val) + ) continue # Check for global recursive requisites @@ -1318,118 +1399,129 @@ class State(object): if req_val in reqs: if name in reqs[req_val]: if reqs[req_val][name] == state: - if reqs[req_val]['state'] == reqs[name][req_val]: - err = ('A recursive ' - 'requisite was found, SLS ' - '"{0}" ID "{1}" ID "{2}"' + if ( + reqs[req_val]["state"] + == reqs[name][req_val] + ): + err = ( + "A recursive " + "requisite was found, SLS " + '"{0}" ID "{1}" ID "{2}"' ).format( - body['__sls__'], - name, - req_val - ) + body["__sls__"], + name, + req_val, + ) errors.append(err) # Make sure that there is only one key in the # dict if len(list(arg)) != 1: errors.append( - 'Multiple dictionaries defined in ' - 'argument of state \'{0}\' in SLS \'{1}\'' - .format(name, body['__sls__']) + "Multiple dictionaries defined in " + "argument of state '{0}' in SLS '{1}'".format( + name, body["__sls__"] + ) ) if not fun: - if state == 'require' or state == 'watch': + if state == "require" or state == "watch": continue errors.append( - 'No function declared in state \'{0}\' in SLS \'{1}\'' - .format(state, body['__sls__']) + "No function declared in state '{0}' in SLS '{1}'".format( + state, body["__sls__"] + ) ) elif fun > 1: errors.append( - 'Too many functions declared in state \'{0}\' in ' - 'SLS \'{1}\''.format(state, body['__sls__']) + "Too many functions declared in state '{0}' in " + "SLS '{1}'".format(state, body["__sls__"]) ) return errors def verify_chunks(self, chunks): - ''' + """ Verify the chunks in a list of low data structures - ''' + """ err = [] for chunk in chunks: err.extend(self.verify_data(chunk)) return err def order_chunks(self, chunks): - ''' + """ Sort the chunk list verifying that the chunks follow the order specified in the order options. - ''' + """ cap = 1 for chunk in chunks: - if 'order' in chunk: - if not isinstance(chunk['order'], int): + if "order" in chunk: + if not isinstance(chunk["order"], int): continue - chunk_order = chunk['order'] + chunk_order = chunk["order"] if chunk_order > cap - 1 and chunk_order > 0: cap = chunk_order + 100 for chunk in chunks: - if 'order' not in chunk: - chunk['order'] = cap + if "order" not in chunk: + chunk["order"] = cap continue - if not isinstance(chunk['order'], (int, float)): - if chunk['order'] == 'last': - chunk['order'] = cap + 1000000 - elif chunk['order'] == 'first': - chunk['order'] = 0 + if not isinstance(chunk["order"], (int, float)): + if chunk["order"] == "last": + chunk["order"] = cap + 1000000 + elif chunk["order"] == "first": + chunk["order"] = 0 else: - chunk['order'] = cap - if 'name_order' in chunk: - chunk['order'] = chunk['order'] + chunk.pop('name_order') / 10000.0 - if chunk['order'] < 0: - chunk['order'] = cap + 1000000 + chunk['order'] - chunks.sort(key=lambda chunk: (chunk['order'], '{0[state]}{0[name]}{0[fun]}'.format(chunk))) + chunk["order"] = cap + if "name_order" in chunk: + chunk["order"] = chunk["order"] + chunk.pop("name_order") / 10000.0 + if chunk["order"] < 0: + chunk["order"] = cap + 1000000 + chunk["order"] + chunks.sort( + key=lambda chunk: ( + chunk["order"], + "{0[state]}{0[name]}{0[fun]}".format(chunk), + ) + ) return chunks def compile_high_data(self, high, orchestration_jid=None): - ''' + """ "Compile" the high data as it is retrieved from the CLI or YAML into the individual state executor structures - ''' + """ chunks = [] for name, body in six.iteritems(high): - if name.startswith('__'): + if name.startswith("__"): continue for state, run in six.iteritems(body): funcs = set() names = [] - if state.startswith('__'): + if state.startswith("__"): continue - chunk = {'state': state, - 'name': name} + chunk = {"state": state, "name": name} if orchestration_jid is not None: - chunk['__orchestration_jid__'] = orchestration_jid - if '__sls__' in body: - chunk['__sls__'] = body['__sls__'] - if '__env__' in body: - chunk['__env__'] = body['__env__'] - chunk['__id__'] = name + chunk["__orchestration_jid__"] = orchestration_jid + if "__sls__" in body: + chunk["__sls__"] = body["__sls__"] + if "__env__" in body: + chunk["__env__"] = body["__env__"] + chunk["__id__"] = name for arg in run: if isinstance(arg, six.string_types): funcs.add(arg) continue if isinstance(arg, dict): for key, val in six.iteritems(arg): - if key == 'names': + if key == "names": for _name in val: if _name not in names: names.append(_name) - elif key == 'state': + elif key == "state": # Don't pass down a state override continue - elif (key == 'name' and - not isinstance(val, six.string_types)): + elif key == "name" and not isinstance( + val, six.string_types + ): # Invalid name, fall back to ID chunk[key] = name else: @@ -1440,57 +1532,56 @@ class State(object): live = copy.deepcopy(chunk) if isinstance(entry, dict): low_name = next(six.iterkeys(entry)) - live['name'] = low_name + live["name"] = low_name list(map(live.update, entry[low_name])) else: - live['name'] = entry - live['name_order'] = name_order + live["name"] = entry + live["name_order"] = name_order name_order += 1 for fun in funcs: - live['fun'] = fun + live["fun"] = fun chunks.append(live) else: live = copy.deepcopy(chunk) for fun in funcs: - live['fun'] = fun + live["fun"] = fun chunks.append(live) chunks = self.order_chunks(chunks) return chunks def reconcile_extend(self, high): - ''' + """ Pull the extend data and add it to the respective high data - ''' + """ errors = [] - if '__extend__' not in high: + if "__extend__" not in high: return high, errors - ext = high.pop('__extend__') + ext = high.pop("__extend__") for ext_chunk in ext: for name, body in six.iteritems(ext_chunk): if name not in high: - state_type = next( - x for x in body if not x.startswith('__') - ) + state_type = next(x for x in body if not x.startswith("__")) # Check for a matching 'name' override in high data ids = find_name(name, state_type, high) if len(ids) != 1: errors.append( - 'Cannot extend ID \'{0}\' in \'{1}:{2}\'. It is not ' - 'part of the high state.\n' - 'This is likely due to a missing include statement ' - 'or an incorrectly typed ID.\nEnsure that a ' - 'state with an ID of \'{0}\' is available\nin ' - 'environment \'{1}\' and to SLS \'{2}\''.format( + "Cannot extend ID '{0}' in '{1}:{2}'. It is not " + "part of the high state.\n" + "This is likely due to a missing include statement " + "or an incorrectly typed ID.\nEnsure that a " + "state with an ID of '{0}' is available\nin " + "environment '{1}' and to SLS '{2}'".format( name, - body.get('__env__', 'base'), - body.get('__sls__', 'base')) + body.get("__env__", "base"), + body.get("__sls__", "base"), ) + ) continue else: name = ids[0][0] for state, run in six.iteritems(body): - if state.startswith('__'): + if state.startswith("__"): continue if state not in high[name]: high[name][state] = run @@ -1499,26 +1590,34 @@ class State(object): for arg in run: update = False for hind in range(len(high[name][state])): - if isinstance(arg, six.string_types) and isinstance(high[name][state][hind], six.string_types): + if isinstance(arg, six.string_types) and isinstance( + high[name][state][hind], six.string_types + ): # replacing the function, replace the index high[name][state].pop(hind) high[name][state].insert(hind, arg) update = True continue - if isinstance(arg, dict) and isinstance(high[name][state][hind], dict): + if isinstance(arg, dict) and isinstance( + high[name][state][hind], dict + ): # It is an option, make sure the options match argfirst = next(iter(arg)) if argfirst == next(iter(high[name][state][hind])): # If argfirst is a requisite then we must merge # our requisite with that of the target state if argfirst in STATE_REQUISITE_KEYWORDS: - high[name][state][hind][argfirst].extend(arg[argfirst]) + high[name][state][hind][argfirst].extend( + arg[argfirst] + ) # otherwise, its not a requisite and we are just extending (replacing) else: high[name][state][hind] = arg update = True - if (argfirst == 'name' and - next(iter(high[name][state][hind])) == 'names'): + if ( + argfirst == "name" + and next(iter(high[name][state][hind])) == "names" + ): # If names are overwritten by name use the name high[name][state][hind] = arg if not update: @@ -1526,15 +1625,15 @@ class State(object): return high, errors def apply_exclude(self, high): - ''' + """ Read in the __exclude__ list and remove all excluded objects from the high data - ''' - if '__exclude__' not in high: + """ + if "__exclude__" not in high: return high ex_sls = set() ex_id = set() - exclude = high.pop('__exclude__') + exclude = high.pop("__exclude__") for exc in exclude: if isinstance(exc, six.string_types): # The exclude statement is a string, assume it is an sls @@ -1544,17 +1643,17 @@ class State(object): if len(exc) != 1: continue key = next(six.iterkeys(exc)) - if key == 'sls': - ex_sls.add(exc['sls']) - elif key == 'id': - ex_id.add(exc['id']) + if key == "sls": + ex_sls.add(exc["sls"]) + elif key == "id": + ex_id.add(exc["id"]) # Now the excludes have been simplified, use them if ex_sls: # There are sls excludes, find the associated ids for name, body in six.iteritems(high): - if name.startswith('__'): + if name.startswith("__"): continue - sls = body.get('__sls__', '') + sls = body.get("__sls__", "") if not sls: continue for ex_ in ex_sls: @@ -1566,18 +1665,29 @@ class State(object): return high def requisite_in(self, high): - ''' + """ Extend the data reference with requisite_in arguments - ''' - req_in = {'require_in', 'watch_in', 'onfail_in', 'onchanges_in', 'use', 'use_in', 'prereq', 'prereq_in'} - req_in_all = req_in.union({'require', 'watch', 'onfail', 'onfail_stop', 'onchanges'}) + """ + req_in = { + "require_in", + "watch_in", + "onfail_in", + "onchanges_in", + "use", + "use_in", + "prereq", + "prereq_in", + } + req_in_all = req_in.union( + {"require", "watch", "onfail", "onfail_stop", "onchanges"} + ) extend = {} errors = [] for id_, body in six.iteritems(high): if not isinstance(body, dict): continue for state, run in six.iteritems(body): - if state.startswith('__'): + if state.startswith("__"): continue for arg in run: if isinstance(arg, dict): @@ -1591,7 +1701,7 @@ class State(object): key = next(iter(arg)) if key not in req_in: continue - rkey = key.split('_')[0] + rkey = key.split("_")[0] items = arg[key] if isinstance(items, dict): # Formatted as a single req_in @@ -1601,38 +1711,34 @@ class State(object): found = False if name not in extend: extend[name] = OrderedDict() - if '.' in _state: + if "." in _state: errors.append( - 'Invalid requisite in {0}: {1} for ' - '{2}, in SLS \'{3}\'. Requisites must ' - 'not contain dots, did you mean \'{4}\'?' - .format( + "Invalid requisite in {0}: {1} for " + "{2}, in SLS '{3}'. Requisites must " + "not contain dots, did you mean '{4}'?".format( rkey, _state, name, - body['__sls__'], - _state[:_state.find('.')] + body["__sls__"], + _state[: _state.find(".")], ) ) - _state = _state.split('.')[0] + _state = _state.split(".")[0] if _state not in extend[name]: extend[name][_state] = [] - extend[name]['__env__'] = body['__env__'] - extend[name]['__sls__'] = body['__sls__'] + extend[name]["__env__"] = body["__env__"] + extend[name]["__sls__"] = body["__sls__"] for ind in range(len(extend[name][_state])): - if next(iter( - extend[name][_state][ind])) == rkey: + if next(iter(extend[name][_state][ind])) == rkey: # Extending again extend[name][_state][ind][rkey].append( - {state: id_} - ) + {state: id_} + ) found = True if found: continue # The rkey is not present yet, create it - extend[name][_state].append( - {rkey: [{state: id_}]} - ) + extend[name][_state].append({rkey: [{state: id_}]}) if isinstance(items, list): # Formed as a list of requisite additions @@ -1641,19 +1747,26 @@ class State(object): if not isinstance(ind, dict): # Malformed req_in if ind in high: - _ind_high = [x for x - in high[ind] - if not x.startswith('__')] + _ind_high = [ + x + for x in high[ind] + if not x.startswith("__") + ] ind = {_ind_high[0]: ind} else: found = False for _id in iter(high): - for state in [state for state - in iter(high[_id]) - if not state.startswith('__')]: + for state in [ + state + for state in iter(high[_id]) + if not state.startswith("__") + ]: for j in iter(high[_id][state]): - if isinstance(j, dict) and 'name' in j: - if j['name'] == ind: + if ( + isinstance(j, dict) + and "name" in j + ): + if j["name"] == ind: ind = {state: _id} found = True if not found: @@ -1662,37 +1775,36 @@ class State(object): continue pstate = next(iter(ind)) pname = ind[pstate] - if pstate == 'sls': + if pstate == "sls": # Expand hinges here hinges = find_sls_ids(pname, high) else: hinges.append((pname, pstate)) - if '.' in pstate: + if "." in pstate: errors.append( - 'Invalid requisite in {0}: {1} for ' - '{2}, in SLS \'{3}\'. Requisites must ' - 'not contain dots, did you mean \'{4}\'?' - .format( + "Invalid requisite in {0}: {1} for " + "{2}, in SLS '{3}'. Requisites must " + "not contain dots, did you mean '{4}'?".format( rkey, pstate, pname, - body['__sls__'], - pstate[:pstate.find('.')] + body["__sls__"], + pstate[: pstate.find(".")], ) ) pstate = pstate.split(".")[0] for tup in hinges: name, _state = tup - if key == 'prereq_in': + if key == "prereq_in": # Add prerequired to origin if id_ not in extend: extend[id_] = OrderedDict() if state not in extend[id_]: extend[id_][state] = [] extend[id_][state].append( - {'prerequired': [{_state: name}]} - ) - if key == 'prereq': + {"prerequired": [{_state: name}]} + ) + if key == "prereq": # Add prerequired to prereqs ext_ids = find_name(name, _state, high) for ext_id, _req_state in ext_ids: @@ -1701,10 +1813,10 @@ class State(object): if _req_state not in extend[ext_id]: extend[ext_id][_req_state] = [] extend[ext_id][_req_state].append( - {'prerequired': [{state: id_}]} - ) + {"prerequired": [{state: id_}]} + ) continue - if key == 'use_in': + if key == "use_in": # Add the running states args to the # use_in states ext_ids = find_name(name, _state, high) @@ -1725,13 +1837,13 @@ class State(object): if next(iter(arg)) in ignore_args: continue # Don't use name or names - if next(six.iterkeys(arg)) == 'name': + if next(six.iterkeys(arg)) == "name": continue - if next(six.iterkeys(arg)) == 'names': + if next(six.iterkeys(arg)) == "names": continue extend[ext_id][_req_state].append(arg) continue - if key == 'use': + if key == "use": # Add the use state's args to the # running state ext_ids = find_name(name, _state, high) @@ -1752,9 +1864,9 @@ class State(object): if next(iter(arg)) in ignore_args: continue # Don't use name or names - if next(six.iterkeys(arg)) == 'name': + if next(six.iterkeys(arg)) == "name": continue - if next(six.iterkeys(arg)) == 'names': + if next(six.iterkeys(arg)) == "names": continue extend[id_][state].append(arg) continue @@ -1763,62 +1875,63 @@ class State(object): extend[name] = OrderedDict() if _state not in extend[name]: extend[name][_state] = [] - extend[name]['__env__'] = body['__env__'] - extend[name]['__sls__'] = body['__sls__'] + extend[name]["__env__"] = body["__env__"] + extend[name]["__sls__"] = body["__sls__"] for ind in range(len(extend[name][_state])): - if next(iter( - extend[name][_state][ind])) == rkey: + if ( + next(iter(extend[name][_state][ind])) + == rkey + ): # Extending again extend[name][_state][ind][rkey].append( - {state: id_} - ) + {state: id_} + ) found = True if found: continue # The rkey is not present yet, create it - extend[name][_state].append( - {rkey: [{state: id_}]} - ) - high['__extend__'] = [] + extend[name][_state].append({rkey: [{state: id_}]}) + high["__extend__"] = [] for key, val in six.iteritems(extend): - high['__extend__'].append({key: val}) + high["__extend__"].append({key: val}) req_in_high, req_in_errors = self.reconcile_extend(high) errors.extend(req_in_errors) return req_in_high, errors def _call_parallel_target(self, name, cdata, low): - ''' + """ The target function to call that will create the parallel thread/process - ''' + """ # we need to re-record start/end duration here because it is impossible to # correctly calculate further down the chain utc_start_time = datetime.datetime.utcnow() + self.format_slots(cdata) tag = _gen_tag(low) try: - ret = self.states[cdata['full']](*cdata['args'], - **cdata['kwargs']) + ret = self.states[cdata["full"]](*cdata["args"], **cdata["kwargs"]) except Exception as exc: # pylint: disable=broad-except - log.debug('An exception occurred in this state: %s', exc, - exc_info_on_loglevel=logging.DEBUG) + log.debug( + "An exception occurred in this state: %s", + exc, + exc_info_on_loglevel=logging.DEBUG, + ) trb = traceback.format_exc() ret = { - 'result': False, - 'name': name, - 'changes': {}, - 'comment': 'An exception occurred in this state: {0}'.format(trb) + "result": False, + "name": name, + "changes": {}, + "comment": "An exception occurred in this state: {0}".format(trb), } utc_finish_time = datetime.datetime.utcnow() - delta = (utc_finish_time - utc_start_time) + delta = utc_finish_time - utc_start_time # duration in milliseconds.microseconds duration = (delta.seconds * 1000000 + delta.microseconds) / 1000.0 - ret['duration'] = duration + ret["duration"] = duration - troot = os.path.join(self.opts['cachedir'], self.jid) - tfile = os.path.join( - troot, - salt.utils.hashutils.sha1_digest(tag)) + troot = os.path.join(self.opts["cachedir"], self.jid) + tfile = os.path.join(troot, salt.utils.hashutils.sha1_digest(tag)) if not os.path.isdir(troot): try: os.makedirs(troot) @@ -1826,110 +1939,117 @@ class State(object): # Looks like the directory was created between the check # and the attempt, we are safe to pass pass - with salt.utils.files.fopen(tfile, 'wb+') as fp_: + with salt.utils.files.fopen(tfile, "wb+") as fp_: fp_.write(msgpack_serialize(ret)) def call_parallel(self, cdata, low): - ''' + """ Call the state defined in the given cdata in parallel - ''' + """ # There are a number of possibilities to not have the cdata # populated with what we might have expected, so just be smart # enough to not raise another KeyError as the name is easily # guessable and fallback in all cases to present the real # exception to the user - name = (cdata.get('args') or [None])[0] or cdata['kwargs'].get('name') + name = (cdata.get("args") or [None])[0] or cdata["kwargs"].get("name") if not name: - name = low.get('name', low.get('__id__')) + name = low.get("name", low.get("__id__")) proc = salt.utils.process.Process( - target=self._call_parallel_target, - args=(name, cdata, low)) + target=self._call_parallel_target, args=(name, cdata, low) + ) proc.start() - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': 'Started in a separate process', - 'proc': proc} + ret = { + "name": name, + "result": None, + "changes": {}, + "comment": "Started in a separate process", + "proc": proc, + } return ret - @salt.utils.decorators.state.OutputUnifier('content_check', 'unify') + @salt.utils.decorators.state.OutputUnifier("content_check", "unify") def call(self, low, chunks=None, running=None, retries=1): - ''' + """ Call a state directly with the low data structure, verify data before processing. - ''' + """ utc_start_time = datetime.datetime.utcnow() - local_start_time = utc_start_time - (datetime.datetime.utcnow() - datetime.datetime.now()) - log.info('Running state [%s] at time %s', - low['name'].strip() if isinstance(low['name'], six.string_types) - else low['name'], - local_start_time.time().isoformat() + local_start_time = utc_start_time - ( + datetime.datetime.utcnow() - datetime.datetime.now() + ) + log.info( + "Running state [%s] at time %s", + low["name"].strip() + if isinstance(low["name"], six.string_types) + else low["name"], + local_start_time.time().isoformat(), ) errors = self.verify_data(low) if errors: ret = { - 'result': False, - 'name': low['name'], - 'changes': {}, - 'comment': '', - } + "result": False, + "name": low["name"], + "changes": {}, + "comment": "", + } for err in errors: - ret['comment'] += '{0}\n'.format(err) - ret['__run_num__'] = self.__run_num + ret["comment"] += "{0}\n".format(err) + ret["__run_num__"] = self.__run_num self.__run_num += 1 format_log(ret) self.check_refresh(low, ret) return ret else: - ret = {'result': False, 'name': low['name'], 'changes': {}} + ret = {"result": False, "name": low["name"], "changes": {}} - self.state_con['runas'] = low.get('runas', None) + self.state_con["runas"] = low.get("runas", None) - if low['state'] == 'cmd' and 'password' in low: - self.state_con['runas_password'] = low['password'] + if low["state"] == "cmd" and "password" in low: + self.state_con["runas_password"] = low["password"] else: - self.state_con['runas_password'] = low.get('runas_password', None) + self.state_con["runas_password"] = low.get("runas_password", None) - if not low.get('__prereq__'): + if not low.get("__prereq__"): log.info( - 'Executing state %s.%s for [%s]', - low['state'], - low['fun'], - low['name'].strip() if isinstance(low['name'], six.string_types) - else low['name'] + "Executing state %s.%s for [%s]", + low["state"], + low["fun"], + low["name"].strip() + if isinstance(low["name"], six.string_types) + else low["name"], ) - if 'provider' in low: + if "provider" in low: self.load_modules(low) - state_func_name = '{0[state]}.{0[fun]}'.format(low) + state_func_name = "{0[state]}.{0[fun]}".format(low) cdata = salt.utils.args.format_call( self.states[state_func_name], low, - initial_ret={'full': state_func_name}, - expected_extra_kws=STATE_INTERNAL_KEYWORDS + initial_ret={"full": state_func_name}, + expected_extra_kws=STATE_INTERNAL_KEYWORDS, ) inject_globals = { # Pass a copy of the running dictionary, the low state chunks and # the current state dictionaries. # We pass deep copies here because we don't want any misbehaving # state module to change these at runtime. - '__low__': immutabletypes.freeze(low), - '__running__': immutabletypes.freeze(running) if running else {}, - '__instance_id__': self.instance_id, - '__lowstate__': immutabletypes.freeze(chunks) if chunks else {} + "__low__": immutabletypes.freeze(low), + "__running__": immutabletypes.freeze(running) if running else {}, + "__instance_id__": self.instance_id, + "__lowstate__": immutabletypes.freeze(chunks) if chunks else {}, } - if '__env__' in low: - inject_globals['__env__'] = six.text_type(low['__env__']) + if "__env__" in low: + inject_globals["__env__"] = six.text_type(low["__env__"]) if self.inject_globals: inject_globals.update(self.inject_globals) - if low.get('__prereq__'): - test = sys.modules[self.states[cdata['full']].__module__].__opts__['test'] - sys.modules[self.states[cdata['full']].__module__].__opts__['test'] = True + if low.get("__prereq__"): + test = sys.modules[self.states[cdata["full"]].__module__].__opts__["test"] + sys.modules[self.states[cdata["full"]].__module__].__opts__["test"] = True try: # Let's get a reference to the salt environment to use within this # state call. @@ -1939,91 +2059,104 @@ class State(object): # that's not found in cdata, we look for what we're being passed in # the original data, namely, the special dunder __env__. If that's # not found we default to 'base' - if ('unless' in low and '{0[state]}.mod_run_check'.format(low) not in self.states) or \ - ('onlyif' in low and '{0[state]}.mod_run_check'.format(low) not in self.states): + if ( + "unless" in low + and "{0[state]}.mod_run_check".format(low) not in self.states + ) or ( + "onlyif" in low + and "{0[state]}.mod_run_check".format(low) not in self.states + ): ret.update(self._run_check(low)) - if not self.opts.get('lock_saltenv', False): + if not self.opts.get("lock_saltenv", False): # NOTE: Overriding the saltenv when lock_saltenv is blocked in # salt/modules/state.py, before we ever get here, but this # additional check keeps use of the State class outside of the # salt/modules/state.py from getting around this setting. - if 'saltenv' in low: - inject_globals['__env__'] = six.text_type(low['saltenv']) - elif isinstance(cdata['kwargs'].get('env', None), six.string_types): + if "saltenv" in low: + inject_globals["__env__"] = six.text_type(low["saltenv"]) + elif isinstance(cdata["kwargs"].get("env", None), six.string_types): # User is using a deprecated env setting which was parsed by # format_call. # We check for a string type since module functions which # allow setting the OS environ also make use of the "env" # keyword argument, which is not a string - inject_globals['__env__'] = six.text_type(cdata['kwargs']['env']) + inject_globals["__env__"] = six.text_type(cdata["kwargs"]["env"]) - if '__env__' not in inject_globals: + if "__env__" not in inject_globals: # Let's use the default environment - inject_globals['__env__'] = 'base' + inject_globals["__env__"] = "base" - if '__orchestration_jid__' in low: - inject_globals['__orchestration_jid__'] = \ - low['__orchestration_jid__'] + if "__orchestration_jid__" in low: + inject_globals["__orchestration_jid__"] = low["__orchestration_jid__"] - if 'result' not in ret or ret['result'] is False: + if "result" not in ret or ret["result"] is False: self.states.inject_globals = inject_globals if self.mocked: ret = mock_ret(cdata) else: # Execute the state function - if not low.get('__prereq__') and low.get('parallel'): + if not low.get("__prereq__") and low.get("parallel"): # run the state call in parallel, but only if not in a prereq ret = self.call_parallel(cdata, low) else: self.format_slots(cdata) - ret = self.states[cdata['full']](*cdata['args'], - **cdata['kwargs']) + ret = self.states[cdata["full"]]( + *cdata["args"], **cdata["kwargs"] + ) self.states.inject_globals = {} - if 'check_cmd' in low and '{0[state]}.mod_run_check_cmd'.format(low) not in self.states: + if ( + "check_cmd" in low + and "{0[state]}.mod_run_check_cmd".format(low) not in self.states + ): ret.update(self._run_check_cmd(low)) except Exception as exc: # pylint: disable=broad-except - log.debug('An exception occurred in this state: %s', exc, - exc_info_on_loglevel=logging.DEBUG) + log.debug( + "An exception occurred in this state: %s", + exc, + exc_info_on_loglevel=logging.DEBUG, + ) trb = traceback.format_exc() # There are a number of possibilities to not have the cdata # populated with what we might have expected, so just be smart # enough to not raise another KeyError as the name is easily # guessable and fallback in all cases to present the real # exception to the user - name = (cdata.get('args') or [None])[0] or cdata['kwargs'].get('name') + name = (cdata.get("args") or [None])[0] or cdata["kwargs"].get("name") if not name: - name = low.get('name', low.get('__id__')) + name = low.get("name", low.get("__id__")) ret = { - 'result': False, - 'name': name, - 'changes': {}, - 'comment': 'An exception occurred in this state: {0}'.format(trb) + "result": False, + "name": name, + "changes": {}, + "comment": "An exception occurred in this state: {0}".format(trb), } finally: - if low.get('__prereq__'): - sys.modules[self.states[cdata['full']].__module__].__opts__['test'] = test + if low.get("__prereq__"): + sys.modules[self.states[cdata["full"]].__module__].__opts__[ + "test" + ] = test - self.state_con.pop('runas', None) - self.state_con.pop('runas_password', None) + self.state_con.pop("runas", None) + self.state_con.pop("runas_password", None) if not isinstance(ret, dict): return ret # If format_call got any warnings, let's show them to the user - if 'warnings' in cdata: - ret.setdefault('warnings', []).extend(cdata['warnings']) + if "warnings" in cdata: + ret.setdefault("warnings", []).extend(cdata["warnings"]) - if 'provider' in low: + if "provider" in low: self.load_modules() - if low.get('__prereq__'): - low['__prereq__'] = False + if low.get("__prereq__"): + low["__prereq__"] = False return ret - ret['__sls__'] = low.get('__sls__') - ret['__run_num__'] = self.__run_num + ret["__sls__"] = low.get("__sls__") + ret["__run_num__"] = self.__run_num self.__run_num += 1 format_log(ret) self.check_refresh(low, ret) @@ -2031,128 +2164,144 @@ class State(object): timezone_delta = datetime.datetime.utcnow() - datetime.datetime.now() local_finish_time = utc_finish_time - timezone_delta local_start_time = utc_start_time - timezone_delta - ret['start_time'] = local_start_time.time().isoformat() - delta = (utc_finish_time - utc_start_time) + ret["start_time"] = local_start_time.time().isoformat() + delta = utc_finish_time - utc_start_time # duration in milliseconds.microseconds duration = (delta.seconds * 1000000 + delta.microseconds) / 1000.0 - ret['duration'] = duration - ret['__id__'] = low['__id__'] + ret["duration"] = duration + ret["__id__"] = low["__id__"] log.info( - 'Completed state [%s] at time %s (duration_in_ms=%s)', - low['name'].strip() if isinstance(low['name'], six.string_types) - else low['name'], + "Completed state [%s] at time %s (duration_in_ms=%s)", + low["name"].strip() + if isinstance(low["name"], six.string_types) + else low["name"], local_finish_time.time().isoformat(), - duration + duration, ) - if 'retry' in low: - low['retry'] = self.verify_retry_data(low['retry']) - if not sys.modules[self.states[cdata['full']].__module__].__opts__['test']: - if low['retry']['until'] != ret['result']: - if low['retry']['attempts'] > retries: - interval = low['retry']['interval'] - if low['retry']['splay'] != 0: - interval = interval + random.randint(0, low['retry']['splay']) + if "retry" in low: + low["retry"] = self.verify_retry_data(low["retry"]) + if not sys.modules[self.states[cdata["full"]].__module__].__opts__["test"]: + if low["retry"]["until"] != ret["result"]: + if low["retry"]["attempts"] > retries: + interval = low["retry"]["interval"] + if low["retry"]["splay"] != 0: + interval = interval + random.randint( + 0, low["retry"]["splay"] + ) log.info( - 'State result does not match retry until value, ' - 'state will be re-run in %s seconds', interval + "State result does not match retry until value, " + "state will be re-run in %s seconds", + interval, ) - self.functions['test.sleep'](interval) - retry_ret = self.call(low, chunks, running, retries=retries+1) + self.functions["test.sleep"](interval) + retry_ret = self.call(low, chunks, running, retries=retries + 1) orig_ret = ret ret = retry_ret - ret['comment'] = '\n'.join( - [( - 'Attempt {0}: Returned a result of "{1}", ' - 'with the following comment: "{2}"'.format( - retries, - orig_ret['result'], - orig_ret['comment']) - ), - '' if not ret['comment'] else ret['comment']]) - ret['duration'] = ret['duration'] + orig_ret['duration'] + (interval * 1000) + ret["comment"] = "\n".join( + [ + ( + 'Attempt {0}: Returned a result of "{1}", ' + 'with the following comment: "{2}"'.format( + retries, orig_ret["result"], orig_ret["comment"] + ) + ), + "" if not ret["comment"] else ret["comment"], + ] + ) + ret["duration"] = ( + ret["duration"] + orig_ret["duration"] + (interval * 1000) + ) if retries == 1: - ret['start_time'] = orig_ret['start_time'] + ret["start_time"] = orig_ret["start_time"] else: - ret['comment'] = ' '.join( - ['' if not ret['comment'] else ret['comment'], - ('The state would be retried every {1} seconds ' - '(with a splay of up to {3} seconds) ' - 'a maximum of {0} times or until a result of {2} ' - 'is returned').format(low['retry']['attempts'], - low['retry']['interval'], - low['retry']['until'], - low['retry']['splay'])]) + ret["comment"] = " ".join( + [ + "" if not ret["comment"] else six.text_type(ret["comment"]), + ( + "The state would be retried every {1} seconds " + "(with a splay of up to {3} seconds) " + "a maximum of {0} times or until a result of {2} " + "is returned" + ).format( + low["retry"]["attempts"], + low["retry"]["interval"], + low["retry"]["until"], + low["retry"]["splay"], + ), + ] + ) return ret def __eval_slot(self, slot): - log.debug('Evaluating slot: %s', slot) - fmt = slot.split(':', 2) + log.debug("Evaluating slot: %s", slot) + fmt = slot.split(":", 2) if len(fmt) != 3: - log.warning('Malformed slot: %s', slot) + log.warning("Malformed slot: %s", slot) return slot - if fmt[1] != 'salt': - log.warning('Malformed slot: %s', slot) - log.warning('Only execution modules are currently supported in slots. This means slot ' - 'should start with "__slot__:salt:"') + if fmt[1] != "salt": + log.warning("Malformed slot: %s", slot) + log.warning( + "Only execution modules are currently supported in slots. This means slot " + 'should start with "__slot__:salt:"' + ) return slot fun, args, kwargs = salt.utils.args.parse_function(fmt[2]) if not fun or fun not in self.functions: - log.warning('Malformed slot: %s', slot) - log.warning('Execution module should be specified in a function call format: ' - 'test.arg(\'arg\', kw=\'kwarg\')') + log.warning("Malformed slot: %s", slot) + log.warning( + "Execution module should be specified in a function call format: " + "test.arg('arg', kw='kwarg')" + ) return slot - log.debug('Calling slot: %s(%s, %s)', fun, args, kwargs) + log.debug("Calling slot: %s(%s, %s)", fun, args, kwargs) slot_return = self.functions[fun](*args, **kwargs) # Given input __slot__:salt:test.arg(somekey="value").not.exist ~ /appended # slot_text should be __slot...).not.exist # append_data should be ~ /appended - slot_text = fmt[2].split('~')[0] - append_data = fmt[2].split('~', 1)[1:] - log.debug('slot_text: %s', slot_text) - log.debug('append_data: %s', append_data) + slot_text = fmt[2].split("~")[0] + append_data = fmt[2].split("~", 1)[1:] + log.debug("slot_text: %s", slot_text) + log.debug("append_data: %s", append_data) # Support parsing slot dict response # return_get should result in a kwargs.nested.dict path by getting # everything after first closing paren: ) return_get = None try: - return_get = slot_text[slot_text.rindex(')')+1:] + return_get = slot_text[slot_text.rindex(")") + 1 :] except ValueError: pass if return_get: - #remove first period - return_get = return_get.split('.', 1)[1].strip() - log.debug('Searching slot result %s for %s', slot_return, return_get) - slot_return = salt.utils.data.traverse_dict_and_list(slot_return, - return_get, - default=None, - delimiter='.' - ) + # remove first period + return_get = return_get.split(".", 1)[1].strip() + log.debug("Searching slot result %s for %s", slot_return, return_get) + slot_return = salt.utils.data.traverse_dict_and_list( + slot_return, return_get, default=None, delimiter="." + ) if append_data: if isinstance(slot_return, six.string_types): # Append text to slot string result - append_data = ' '.join(append_data).strip() - log.debug('appending to slot result: %s', append_data) + append_data = " ".join(append_data).strip() + log.debug("appending to slot result: %s", append_data) slot_return += append_data else: - log.error('Ignoring slot append, slot result is not a string') + log.error("Ignoring slot append, slot result is not a string") return slot_return def format_slots(self, cdata): - ''' + """ Read in the arguments from the low level slot syntax to make a last minute runtime call to gather relevant data for the specific routine Will parse strings, first level of dictionary values, and strings and first level dict values inside of lists - ''' + """ # __slot__:salt.cmd.run(foo, bar, baz=qux) - SLOT_TEXT = '__slot__:' - ctx = (('args', enumerate(cdata['args'])), - ('kwargs', cdata['kwargs'].items())) + SLOT_TEXT = "__slot__:" + ctx = (("args", enumerate(cdata["args"])), ("kwargs", cdata["kwargs"].items())) for atype, avalues in ctx: for ind, arg in avalues: arg = salt.utils.data.decode(arg, keep=True) @@ -2174,18 +2323,24 @@ class State(object): for key, value in listvalue.items(): try: if value.startswith(SLOT_TEXT): - log.trace("Slot processsing nested dict value %s", value) - cdata[atype][ind][idx][key] = self.__eval_slot(value) + log.trace( + "Slot processsing nested dict value %s", + value, + ) + cdata[atype][ind][idx][key] = self.__eval_slot( + value + ) except AttributeError: # Not a string/slot continue if isinstance(listvalue, six.text_type): # Search strings in a list for __slot__: if listvalue.startswith(SLOT_TEXT): - log.trace("Slot processsing nested string %s", listvalue) + log.trace( + "Slot processsing nested string %s", listvalue + ) cdata[atype][ind][idx] = self.__eval_slot(listvalue) - elif isinstance(arg, six.text_type) \ - and arg.startswith(SLOT_TEXT): + elif isinstance(arg, six.text_type) and arg.startswith(SLOT_TEXT): # Search strings for __slot__: log.trace("Slot processsing %s", arg) cdata[atype][ind] = self.__eval_slot(arg) @@ -2194,20 +2349,20 @@ class State(object): continue def verify_retry_data(self, retry_data): - ''' + """ verifies the specified retry data - ''' + """ retry_defaults = { - 'until': True, - 'attempts': 2, - 'splay': 0, - 'interval': 30, + "until": True, + "attempts": 2, + "splay": 0, + "interval": 30, } expected_data = { - 'until': bool, - 'attempts': int, - 'interval': int, - 'splay': int, + "until": bool, + "attempts": int, + "interval": int, + "splay": int, } validated_retry_data = {} if isinstance(retry_data, dict): @@ -2217,56 +2372,62 @@ class State(object): validated_retry_data[expected_key] = retry_data[expected_key] else: log.warning( - 'An invalid value was passed for the retry %s, ' - 'using default value \'%s\'', - expected_key, retry_defaults[expected_key] + "An invalid value was passed for the retry %s, " + "using default value '%s'", + expected_key, + retry_defaults[expected_key], ) - validated_retry_data[expected_key] = retry_defaults[expected_key] + validated_retry_data[expected_key] = retry_defaults[ + expected_key + ] else: validated_retry_data[expected_key] = retry_defaults[expected_key] else: - log.warning(('State is set to retry, but a valid dict for retry ' - 'configuration was not found. Using retry defaults')) + log.warning( + ( + "State is set to retry, but a valid dict for retry " + "configuration was not found. Using retry defaults" + ) + ) validated_retry_data = retry_defaults return validated_retry_data def call_chunks(self, chunks): - ''' + """ Iterate over a list of chunks and call them, checking for requires. - ''' + """ # Check for any disabled states disabled = {} - if 'state_runs_disabled' in self.opts['grains']: + if "state_runs_disabled" in self.opts["grains"]: for low in chunks[:]: - state_ = '{0}.{1}'.format(low['state'], low['fun']) - for pat in self.opts['grains']['state_runs_disabled']: + state_ = "{0}.{1}".format(low["state"], low["fun"]) + for pat in self.opts["grains"]["state_runs_disabled"]: if fnmatch.fnmatch(state_, pat): comment = ( - 'The state function "{0}" is currently disabled by "{1}", ' - 'to re-enable, run state.enable {1}.' - ).format( - state_, - pat, - ) + 'The state function "{0}" is currently disabled by "{1}", ' + "to re-enable, run state.enable {1}." + ).format(state_, pat,) _tag = _gen_tag(low) - disabled[_tag] = {'changes': {}, - 'result': False, - 'comment': comment, - '__run_num__': self.__run_num, - '__sls__': low['__sls__']} + disabled[_tag] = { + "changes": {}, + "result": False, + "comment": comment, + "__run_num__": self.__run_num, + "__sls__": low["__sls__"], + } self.__run_num += 1 chunks.remove(low) break running = {} for low in chunks: - if '__FAILHARD__' in running: - running.pop('__FAILHARD__') + if "__FAILHARD__" in running: + running.pop("__FAILHARD__") return running tag = _gen_tag(low) if tag not in running: # Check if this low chunk is paused action = self.check_pause(low) - if action == 'kill': + if action == "kill": break running = self.call_chunk(low, running, chunks) if self.check_failhard(low, running): @@ -2280,32 +2441,32 @@ class State(object): return ret def check_failhard(self, low, running): - ''' + """ Check if the low data chunk should send a failhard signal - ''' + """ tag = _gen_tag(low) - if self.opts.get('test', False): + if self.opts.get("test", False): return False - if low.get('failhard', self.opts['failhard']) and tag in running: - if running[tag]['result'] is None: + if low.get("failhard", self.opts["failhard"]) and tag in running: + if running[tag]["result"] is None: return False - return not running[tag]['result'] + return not running[tag]["result"] return False def check_pause(self, low): - ''' + """ Check to see if this low chunk has been paused - ''' + """ if not self.jid: # Can't pause on salt-ssh since we can't track continuous state return - pause_path = os.path.join(self.opts['cachedir'], 'state_pause', self.jid) + pause_path = os.path.join(self.opts["cachedir"], "state_pause", self.jid) start = time.time() if os.path.isfile(pause_path): try: while True: tries = 0 - with salt.utils.files.fopen(pause_path, 'rb') as fp_: + with salt.utils.files.fopen(pause_path, "rb") as fp_: try: pdat = msgpack_deserialize(fp_.read()) except salt.utils.msgpack.exceptions.UnpackValueError: @@ -2316,118 +2477,126 @@ class State(object): tries += 1 time.sleep(1) continue - id_ = low['__id__'] - key = '' + id_ = low["__id__"] + key = "" if id_ in pdat: key = id_ - elif '__all__' in pdat: - key = '__all__' + elif "__all__" in pdat: + key = "__all__" if key: - if 'duration' in pdat[key]: + if "duration" in pdat[key]: now = time.time() - if now - start > pdat[key]['duration']: - return 'run' - if 'kill' in pdat[key]: - return 'kill' + if now - start > pdat[key]["duration"]: + return "run" + if "kill" in pdat[key]: + return "kill" else: - return 'run' + return "run" time.sleep(1) except Exception as exc: # pylint: disable=broad-except - log.error('Failed to read in pause data for file located at: %s', pause_path) - return 'run' - return 'run' + log.error( + "Failed to read in pause data for file located at: %s", pause_path + ) + return "run" + return "run" def reconcile_procs(self, running): - ''' + """ Check the running dict for processes and resolve them - ''' + """ retset = set() for tag in running: - proc = running[tag].get('proc') + proc = running[tag].get("proc") if proc: if not proc.is_alive(): ret_cache = os.path.join( - self.opts['cachedir'], + self.opts["cachedir"], self.jid, - salt.utils.hashutils.sha1_digest(tag)) + salt.utils.hashutils.sha1_digest(tag), + ) if not os.path.isfile(ret_cache): - ret = {'result': False, - 'comment': 'Parallel process failed to return', - 'name': running[tag]['name'], - 'changes': {}} + ret = { + "result": False, + "comment": "Parallel process failed to return", + "name": running[tag]["name"], + "changes": {}, + } try: - with salt.utils.files.fopen(ret_cache, 'rb') as fp_: + with salt.utils.files.fopen(ret_cache, "rb") as fp_: ret = msgpack_deserialize(fp_.read()) except (OSError, IOError): - ret = {'result': False, - 'comment': 'Parallel cache failure', - 'name': running[tag]['name'], - 'changes': {}} + ret = { + "result": False, + "comment": "Parallel cache failure", + "name": running[tag]["name"], + "changes": {}, + } running[tag].update(ret) - running[tag].pop('proc') + running[tag].pop("proc") else: retset.add(False) return False not in retset def check_requisite(self, low, running, chunks, pre=False): - ''' + """ Look into the running data to check the status of all requisite states - ''' + """ present = False # If mod_watch is not available make it a require - if 'watch' in low: - if '{0}.mod_watch'.format(low['state']) not in self.states: - if 'require' in low: - low['require'].extend(low.pop('watch')) + if "watch" in low: + if "{0}.mod_watch".format(low["state"]) not in self.states: + if "require" in low: + low["require"].extend(low.pop("watch")) else: - low['require'] = low.pop('watch') + low["require"] = low.pop("watch") else: present = True - if 'watch_any' in low: - if '{0}.mod_watch'.format(low['state']) not in self.states: - if 'require_any' in low: - low['require_any'].extend(low.pop('watch_any')) + if "watch_any" in low: + if "{0}.mod_watch".format(low["state"]) not in self.states: + if "require_any" in low: + low["require_any"].extend(low.pop("watch_any")) else: - low['require_any'] = low.pop('watch_any') + low["require_any"] = low.pop("watch_any") else: present = True - if 'require' in low: + if "require" in low: present = True - if 'require_any' in low: + if "require_any" in low: present = True - if 'prerequired' in low: + if "prerequired" in low: present = True - if 'prereq' in low: + if "prereq" in low: present = True - if 'onfail' in low: + if "onfail" in low: present = True - if 'onfail_any' in low: + if "onfail_any" in low: present = True - if 'onchanges' in low: + if "onchanges" in low: present = True - if 'onchanges_any' in low: + if "onchanges_any" in low: present = True if not present: - return 'met', () + return "met", () self.reconcile_procs(running) reqs = { - 'require': [], - 'require_any': [], - 'watch': [], - 'watch_any': [], - 'prereq': [], - 'onfail': [], - 'onfail_any': [], - 'onchanges': [], - 'onchanges_any': []} + "require": [], + "require_any": [], + "watch": [], + "watch_any": [], + "prereq": [], + "onfail": [], + "onfail_any": [], + "onchanges": [], + "onchanges_any": [], + } if pre: - reqs['prerequired'] = [] + reqs["prerequired"] = [] for r_state in reqs: if r_state in low and low[r_state] is not None: for req in low[r_state]: if isinstance(req, six.string_types): - req = {'id': req} + req = {"id": req} req = trim_req(req) found = False for chunk in chunks: @@ -2435,38 +2604,43 @@ class State(object): req_val = req[req_key] if req_val is None: continue - if req_key == 'sls': + if req_key == "sls": # Allow requisite tracking of entire sls files - if fnmatch.fnmatch(chunk['__sls__'], req_val): + if fnmatch.fnmatch(chunk["__sls__"], req_val): found = True reqs[r_state].append(chunk) continue try: if isinstance(req_val, six.string_types): - if (fnmatch.fnmatch(chunk['name'], req_val) or - fnmatch.fnmatch(chunk['__id__'], req_val)): - if req_key == 'id' or chunk['state'] == req_key: + if fnmatch.fnmatch( + chunk["name"], req_val + ) or fnmatch.fnmatch(chunk["__id__"], req_val): + if req_key == "id" or chunk["state"] == req_key: found = True reqs[r_state].append(chunk) else: raise KeyError except KeyError as exc: raise SaltRenderError( - 'Could not locate requisite of [{0}] present in state with name [{1}]'.format( - req_key, chunk['name'])) + "Could not locate requisite of [{0}] present in state with name [{1}]".format( + req_key, chunk["name"] + ) + ) except TypeError: # On Python 2, the above req_val, being an OrderedDict, will raise a KeyError, # however on Python 3 it will raise a TypeError # This was found when running tests.unit.test_state.StateCompilerTestCase.test_render_error_on_invalid_requisite raise SaltRenderError( - 'Could not locate requisite of [{0}] present in state with name [{1}]'.format( - req_key, chunk['name'])) + "Could not locate requisite of [{0}] present in state with name [{1}]".format( + req_key, chunk["name"] + ) + ) if not found: - return 'unmet', () + return "unmet", () fun_stats = set() for r_state, chunks in six.iteritems(reqs): req_stats = set() - if r_state.startswith('prereq') and not r_state.startswith('prerequired'): + if r_state.startswith("prereq") and not r_state.startswith("prerequired"): run_dict = self.pre else: run_dict = running @@ -2479,70 +2653,70 @@ class State(object): for chunk in chunks: tag = _gen_tag(chunk) if tag not in run_dict: - req_stats.add('unmet') + req_stats.add("unmet") continue - if r_state.startswith('onfail'): - if run_dict[tag]['result'] is True: - req_stats.add('onfail') # At least one state is OK + if r_state.startswith("onfail"): + if run_dict[tag]["result"] is True: + req_stats.add("onfail") # At least one state is OK continue else: - if run_dict[tag]['result'] is False: - req_stats.add('fail') + if run_dict[tag]["result"] is False: + req_stats.add("fail") continue - if r_state.startswith('onchanges'): - if not run_dict[tag]['changes']: - req_stats.add('onchanges') + if r_state.startswith("onchanges"): + if not run_dict[tag]["changes"]: + req_stats.add("onchanges") else: - req_stats.add('onchangesmet') + req_stats.add("onchangesmet") continue - if r_state.startswith('watch') and run_dict[tag]['changes']: - req_stats.add('change') + if r_state.startswith("watch") and run_dict[tag]["changes"]: + req_stats.add("change") continue - if r_state.startswith('prereq') and run_dict[tag]['result'] is None: - if not r_state.startswith('prerequired'): - req_stats.add('premet') - if r_state.startswith('prereq') and not run_dict[tag]['result'] is None: - if not r_state.startswith('prerequired'): - req_stats.add('pre') + if r_state.startswith("prereq") and run_dict[tag]["result"] is None: + if not r_state.startswith("prerequired"): + req_stats.add("premet") + if r_state.startswith("prereq") and not run_dict[tag]["result"] is None: + if not r_state.startswith("prerequired"): + req_stats.add("pre") else: - if run_dict[tag].get('__state_ran__', True): - req_stats.add('met') - if r_state.endswith('_any'): - if 'met' in req_stats or 'change' in req_stats: - if 'fail' in req_stats: - req_stats.remove('fail') - if 'onchangesmet' in req_stats: - if 'onchanges' in req_stats: - req_stats.remove('onchanges') - if 'fail' in req_stats: - req_stats.remove('fail') - if 'onfail' in req_stats: - if 'fail' in req_stats: - req_stats.remove('onfail') + if run_dict[tag].get("__state_ran__", True): + req_stats.add("met") + if r_state.endswith("_any"): + if "met" in req_stats or "change" in req_stats: + if "fail" in req_stats: + req_stats.remove("fail") + if "onchangesmet" in req_stats: + if "onchanges" in req_stats: + req_stats.remove("onchanges") + if "fail" in req_stats: + req_stats.remove("fail") + if "onfail" in req_stats: + if "fail" in req_stats: + req_stats.remove("onfail") fun_stats.update(req_stats) - if 'unmet' in fun_stats: - status = 'unmet' - elif 'fail' in fun_stats: - status = 'fail' - elif 'pre' in fun_stats: - if 'premet' in fun_stats: - status = 'met' + if "unmet" in fun_stats: + status = "unmet" + elif "fail" in fun_stats: + status = "fail" + elif "pre" in fun_stats: + if "premet" in fun_stats: + status = "met" else: - status = 'pre' - elif 'onfail' in fun_stats and 'met' not in fun_stats: - status = 'onfail' # all onfail states are OK - elif 'onchanges' in fun_stats and 'onchangesmet' not in fun_stats: - status = 'onchanges' - elif 'change' in fun_stats: - status = 'change' + status = "pre" + elif "onfail" in fun_stats and "met" not in fun_stats: + status = "onfail" # all onfail states are OK + elif "onchanges" in fun_stats and "onchangesmet" not in fun_stats: + status = "onchanges" + elif "change" in fun_stats: + status = "change" else: - status = 'met' + status = "met" return status, reqs def event(self, chunk_ret, length, fire_event=False): - ''' + """ Fire an event on the master bus If `fire_event` is set to True an event will be sent with the @@ -2555,56 +2729,71 @@ class State(object): If the `state_events` is set to True in the config, then after the chunk is evaluated an event will be set up to the master with the results. - ''' - if not self.opts.get('local') and (self.opts.get('state_events', True) or fire_event): - if not self.opts.get('master_uri'): + """ + if not self.opts.get("local") and ( + self.opts.get("state_events", True) or fire_event + ): + if not self.opts.get("master_uri"): ev_func = lambda ret, tag, preload=None: salt.utils.event.get_master_event( - self.opts, self.opts['sock_dir'], listen=False).fire_event(ret, tag) + self.opts, self.opts["sock_dir"], listen=False + ).fire_event( + ret, tag + ) else: - ev_func = self.functions['event.fire_master'] + ev_func = self.functions["event.fire_master"] - ret = {'ret': chunk_ret} + ret = {"ret": chunk_ret} if fire_event is True: tag = salt.utils.event.tagify( - [self.jid, self.opts['id'], six.text_type(chunk_ret['name'])], 'state_result' - ) + [self.jid, self.opts["id"], six.text_type(chunk_ret["name"])], + "state_result", + ) elif isinstance(fire_event, six.string_types): tag = salt.utils.event.tagify( - [self.jid, self.opts['id'], six.text_type(fire_event)], 'state_result' - ) + [self.jid, self.opts["id"], six.text_type(fire_event)], + "state_result", + ) else: tag = salt.utils.event.tagify( - [self.jid, 'prog', self.opts['id'], six.text_type(chunk_ret['__run_num__'])], 'job' - ) - ret['len'] = length - preload = {'jid': self.jid} + [ + self.jid, + "prog", + self.opts["id"], + six.text_type(chunk_ret["__run_num__"]), + ], + "job", + ) + ret["len"] = length + preload = {"jid": self.jid} ev_func(ret, tag, preload=preload) def call_chunk(self, low, running, chunks): - ''' + """ Check if a chunk has any requires, execute the requires and then the chunk - ''' + """ low = self._mod_aggregate(low, running, chunks) self._mod_init(low) tag = _gen_tag(low) - if not low.get('prerequired'): + if not low.get("prerequired"): self.active.add(tag) - requisites = ['require', - 'require_any', - 'watch', - 'watch_any', - 'prereq', - 'onfail', - 'onfail_any', - 'onchanges', - 'onchanges_any'] - if not low.get('__prereq__'): - requisites.append('prerequired') + requisites = [ + "require", + "require_any", + "watch", + "watch_any", + "prereq", + "onfail", + "onfail_any", + "onchanges", + "onchanges_any", + ] + if not low.get("__prereq__"): + requisites.append("prerequired") status, reqs = self.check_requisite(low, running, chunks, pre=True) else: status, reqs = self.check_requisite(low, running, chunks) - if status == 'unmet': + if status == "unmet": lost = {} reqs = [] for requisite in requisites: @@ -2613,7 +2802,7 @@ class State(object): continue for req in low[requisite]: if isinstance(req, six.string_types): - req = {'id': req} + req = {"id": req} req = trim_req(req) found = False req_key = next(iter(req)) @@ -2621,53 +2810,59 @@ class State(object): for chunk in chunks: if req_val is None: continue - if req_key == 'sls': + if req_key == "sls": # Allow requisite tracking of entire sls files - if fnmatch.fnmatch(chunk['__sls__'], req_val): - if requisite == 'prereq': - chunk['__prereq__'] = True + if fnmatch.fnmatch(chunk["__sls__"], req_val): + if requisite == "prereq": + chunk["__prereq__"] = True reqs.append(chunk) found = True continue - if (fnmatch.fnmatch(chunk['name'], req_val) or - fnmatch.fnmatch(chunk['__id__'], req_val)): - if req_key == 'id' or chunk['state'] == req_key: - if requisite == 'prereq': - chunk['__prereq__'] = True - elif requisite == 'prerequired': - chunk['__prerequired__'] = True + if fnmatch.fnmatch(chunk["name"], req_val) or fnmatch.fnmatch( + chunk["__id__"], req_val + ): + if req_key == "id" or chunk["state"] == req_key: + if requisite == "prereq": + chunk["__prereq__"] = True + elif requisite == "prerequired": + chunk["__prerequired__"] = True reqs.append(chunk) found = True if not found: lost[requisite].append(req) - if lost['require'] or lost['watch'] or lost['prereq'] \ - or lost['onfail'] or lost['onchanges'] \ - or lost.get('prerequired'): - comment = 'The following requisites were not found:\n' + if ( + lost["require"] + or lost["watch"] + or lost["prereq"] + or lost["onfail"] + or lost["onchanges"] + or lost.get("prerequired") + ): + comment = "The following requisites were not found:\n" for requisite, lreqs in six.iteritems(lost): if not lreqs: continue - comment += \ - '{0}{1}:\n'.format(' ' * 19, requisite) + comment += "{0}{1}:\n".format(" " * 19, requisite) for lreq in lreqs: req_key = next(iter(lreq)) req_val = lreq[req_key] - comment += \ - '{0}{1}: {2}\n'.format(' ' * 23, req_key, req_val) - if low.get('__prereq__'): + comment += "{0}{1}: {2}\n".format(" " * 23, req_key, req_val) + if low.get("__prereq__"): run_dict = self.pre else: run_dict = running start_time, duration = _calculate_fake_duration() - run_dict[tag] = {'changes': {}, - 'result': False, - 'duration': duration, - 'start_time': start_time, - 'comment': comment, - '__run_num__': self.__run_num, - '__sls__': low['__sls__']} + run_dict[tag] = { + "changes": {}, + "result": False, + "duration": duration, + "start_time": start_time, + "comment": comment, + "__run_num__": self.__run_num, + "__sls__": low["__sls__"], + } self.__run_num += 1 - self.event(run_dict[tag], len(chunks), fire_event=low.get('fire_event')) + self.event(run_dict[tag], len(chunks), fire_event=low.get("fire_event")) return running for chunk in reqs: # Check to see if the chunk has been run, only run it if @@ -2675,52 +2870,55 @@ class State(object): ctag = _gen_tag(chunk) if ctag not in running: if ctag in self.active: - if chunk.get('__prerequired__'): + if chunk.get("__prerequired__"): # Prereq recusive, run this chunk with prereq on if tag not in self.pre: - low['__prereq__'] = True + low["__prereq__"] = True self.pre[ctag] = self.call(low, chunks, running) return running else: return running elif ctag not in running: - log.error('Recursive requisite found') + log.error("Recursive requisite found") running[tag] = { - 'changes': {}, - 'result': False, - 'comment': 'Recursive requisite found', - '__run_num__': self.__run_num, - '__sls__': low['__sls__']} + "changes": {}, + "result": False, + "comment": "Recursive requisite found", + "__run_num__": self.__run_num, + "__sls__": low["__sls__"], + } self.__run_num += 1 - self.event(running[tag], len(chunks), fire_event=low.get('fire_event')) + self.event( + running[tag], len(chunks), fire_event=low.get("fire_event") + ) return running running = self.call_chunk(chunk, running, chunks) if self.check_failhard(chunk, running): - running['__FAILHARD__'] = True + running["__FAILHARD__"] = True return running - if low.get('__prereq__'): + if low.get("__prereq__"): status, reqs = self.check_requisite(low, running, chunks) self.pre[tag] = self.call(low, chunks, running) - if not self.pre[tag]['changes'] and status == 'change': - self.pre[tag]['changes'] = {'watch': 'watch'} - self.pre[tag]['result'] = None + if not self.pre[tag]["changes"] and status == "change": + self.pre[tag]["changes"] = {"watch": "watch"} + self.pre[tag]["result"] = None else: running = self.call_chunk(low, running, chunks) if self.check_failhard(chunk, running): - running['__FAILHARD__'] = True + running["__FAILHARD__"] = True return running - elif status == 'met': - if low.get('__prereq__'): + elif status == "met": + if low.get("__prereq__"): self.pre[tag] = self.call(low, chunks, running) else: running[tag] = self.call(low, chunks, running) - elif status == 'fail': + elif status == "fail": # if the requisite that failed was due to a prereq on this low state # show the normal error if tag in self.pre: running[tag] = self.pre[tag] - running[tag]['__run_num__'] = self.__run_num - running[tag]['__sls__'] = low['__sls__'] + running[tag]["__run_num__"] = self.__run_num + running[tag]["__sls__"] = low["__sls__"] # otherwise the failure was due to a requisite down the chain else: # determine what the requisite failures where, and return @@ -2736,93 +2934,104 @@ class State(object): if req_ret is None: continue # If the result was False (not None) it was a failure - if req_ret['result'] is False: + if req_ret["result"] is False: # use SLS.ID for the key-- so its easier to find - key = '{sls}.{_id}'.format(sls=req_low['__sls__'], - _id=req_low['__id__']) + key = "{sls}.{_id}".format( + sls=req_low["__sls__"], _id=req_low["__id__"] + ) failed_requisites.add(key) - _cmt = 'One or more requisite failed: {0}'.format( - ', '.join(six.text_type(i) for i in failed_requisites) + _cmt = "One or more requisite failed: {0}".format( + ", ".join(six.text_type(i) for i in failed_requisites) ) start_time, duration = _calculate_fake_duration() running[tag] = { - 'changes': {}, - 'result': False, - 'duration': duration, - 'start_time': start_time, - 'comment': _cmt, - '__run_num__': self.__run_num, - '__sls__': low['__sls__'] + "changes": {}, + "result": False, + "duration": duration, + "start_time": start_time, + "comment": _cmt, + "__run_num__": self.__run_num, + "__sls__": low["__sls__"], } self.pre[tag] = running[tag] self.__run_num += 1 - elif status == 'change' and not low.get('__prereq__'): + elif status == "change" and not low.get("__prereq__"): ret = self.call(low, chunks, running) - if not ret['changes'] and not ret.get('skip_watch', False): + if not ret["changes"] and not ret.get("skip_watch", False): low = low.copy() - low['sfun'] = low['fun'] - low['fun'] = 'mod_watch' - low['__reqs__'] = reqs + low["sfun"] = low["fun"] + low["fun"] = "mod_watch" + low["__reqs__"] = reqs ret = self.call(low, chunks, running) running[tag] = ret - elif status == 'pre': + elif status == "pre": start_time, duration = _calculate_fake_duration() - pre_ret = {'changes': {}, - 'result': True, - 'duration': duration, - 'start_time': start_time, - 'comment': 'No changes detected', - '__run_num__': self.__run_num, - '__sls__': low['__sls__']} + pre_ret = { + "changes": {}, + "result": True, + "duration": duration, + "start_time": start_time, + "comment": "No changes detected", + "__run_num__": self.__run_num, + "__sls__": low["__sls__"], + } running[tag] = pre_ret self.pre[tag] = pre_ret self.__run_num += 1 - elif status == 'onfail': + elif status == "onfail": start_time, duration = _calculate_fake_duration() - running[tag] = {'changes': {}, - 'result': True, - 'duration': duration, - 'start_time': start_time, - 'comment': 'State was not run because onfail req did not change', - '__state_ran__': False, - '__run_num__': self.__run_num, - '__sls__': low['__sls__']} + running[tag] = { + "changes": {}, + "result": True, + "duration": duration, + "start_time": start_time, + "comment": "State was not run because onfail req did not change", + "__state_ran__": False, + "__run_num__": self.__run_num, + "__sls__": low["__sls__"], + } self.__run_num += 1 - elif status == 'onchanges': + elif status == "onchanges": start_time, duration = _calculate_fake_duration() - running[tag] = {'changes': {}, - 'result': True, - 'duration': duration, - 'start_time': start_time, - 'comment': 'State was not run because none of the onchanges reqs changed', - '__state_ran__': False, - '__run_num__': self.__run_num, - '__sls__': low['__sls__']} + running[tag] = { + "changes": {}, + "result": True, + "duration": duration, + "start_time": start_time, + "comment": "State was not run because none of the onchanges reqs changed", + "__state_ran__": False, + "__run_num__": self.__run_num, + "__sls__": low["__sls__"], + } self.__run_num += 1 else: - if low.get('__prereq__'): + if low.get("__prereq__"): self.pre[tag] = self.call(low, chunks, running) else: running[tag] = self.call(low, chunks, running) if tag in running: - self.event(running[tag], len(chunks), fire_event=low.get('fire_event')) + self.event(running[tag], len(chunks), fire_event=low.get("fire_event")) return running def call_listen(self, chunks, running): - ''' + """ Find all of the listen routines and call the associated mod_watch runs - ''' + """ listeners = [] crefs = {} for chunk in chunks: - crefs[(chunk['state'], chunk['__id__'], chunk['name'])] = chunk - if 'listen' in chunk: - listeners.append({(chunk['state'], chunk['__id__'], chunk['name']): chunk['listen']}) - if 'listen_in' in chunk: - for l_in in chunk['listen_in']: + crefs[(chunk["state"], chunk["__id__"], chunk["name"])] = chunk + if "listen" in chunk: + listeners.append( + {(chunk["state"], chunk["__id__"], chunk["name"]): chunk["listen"]} + ) + if "listen_in" in chunk: + for l_in in chunk["listen_in"]: for key, val in six.iteritems(l_in): - listeners.append({(key, val, 'lookup'): [{chunk['state']: chunk['__id__']}]}) + listeners.append( + {(key, val, "lookup"): [{chunk["state"]: chunk["__id__"]}]} + ) mod_watchers = [] errors = {} for l_dict in listeners: @@ -2831,45 +3040,66 @@ class State(object): if not isinstance(listen_to, dict): found = False for chunk in chunks: - if chunk['__id__'] == listen_to or \ - chunk['name'] == listen_to: - listen_to = {chunk['state']: chunk['__id__']} + if ( + chunk["__id__"] == listen_to + or chunk["name"] == listen_to + ): + listen_to = {chunk["state"]: chunk["__id__"]} found = True if not found: continue for lkey, lval in six.iteritems(listen_to): if not any(lkey == cref[0] and lval in cref for cref in crefs): - rerror = {_l_tag(lkey, lval): - { - 'comment': 'Referenced state {0}: {1} does not exist'.format(lkey, lval), - 'name': 'listen_{0}:{1}'.format(lkey, lval), - 'result': False, - 'changes': {} - }} + rerror = { + _l_tag(lkey, lval): { + "comment": "Referenced state {0}: {1} does not exist".format( + lkey, lval + ), + "name": "listen_{0}:{1}".format(lkey, lval), + "result": False, + "changes": {}, + } + } errors.update(rerror) continue to_tags = [ - _gen_tag(data) for cref, data in six.iteritems(crefs) if lkey == cref[0] and lval in cref + _gen_tag(data) + for cref, data in six.iteritems(crefs) + if lkey == cref[0] and lval in cref ] for to_tag in to_tags: if to_tag not in running: continue - if running[to_tag]['changes']: - if not any(key[0] == cref[0] and key[1] in cref for cref in crefs): - rerror = {_l_tag(key[0], key[1]): - {'comment': 'Referenced state {0}: {1} does not exist'.format(key[0], key[1]), - 'name': 'listen_{0}:{1}'.format(key[0], key[1]), - 'result': False, - 'changes': {}}} + if running[to_tag]["changes"]: + if not any( + key[0] == cref[0] and key[1] in cref + for cref in crefs + ): + rerror = { + _l_tag(key[0], key[1]): { + "comment": "Referenced state {0}: {1} does not exist".format( + key[0], key[1] + ), + "name": "listen_{0}:{1}".format( + key[0], key[1] + ), + "result": False, + "changes": {}, + } + } errors.update(rerror) continue - new_chunks = [data for cref, data in six.iteritems(crefs) if key[0] == cref[0] and key[1] in cref] + new_chunks = [ + data + for cref, data in six.iteritems(crefs) + if key[0] == cref[0] and key[1] in cref + ] for chunk in new_chunks: low = chunk.copy() - low['sfun'] = chunk['fun'] - low['fun'] = 'mod_watch' - low['__id__'] = 'listener_{0}'.format(low['__id__']) + low["sfun"] = chunk["fun"] + low["fun"] = "mod_watch" + low["__id__"] = "listener_{0}".format(low["__id__"]) for req in STATE_REQUISITE_KEYWORDS: if req in low: low.pop(req) @@ -2877,15 +3107,15 @@ class State(object): ret = self.call_chunks(mod_watchers) running.update(ret) for err in errors: - errors[err]['__run_num__'] = self.__run_num + errors[err]["__run_num__"] = self.__run_num self.__run_num += 1 running.update(errors) return running def call_high(self, high, orchestration_jid=None): - ''' + """ Process a high data call and ensure the defined states. - ''' + """ errors = [] # If there is extension data reconcile it high, ext_errors = self.reconcile_extend(high) @@ -2911,17 +3141,17 @@ class State(object): def _cleanup_accumulator_data(): accum_data_path = os.path.join( - get_accumulator_dir(self.opts['cachedir']), - self.instance_id + get_accumulator_dir(self.opts["cachedir"]), self.instance_id ) try: os.remove(accum_data_path) - log.debug('Deleted accumulator data file %s', accum_data_path) + log.debug("Deleted accumulator data file %s", accum_data_path) except OSError: - log.debug('File %s does not exist, no need to cleanup', accum_data_path) + log.debug("File %s does not exist, no need to cleanup", accum_data_path) + _cleanup_accumulator_data() if self.jid is not None: - pause_path = os.path.join(self.opts['cachedir'], 'state_pause', self.jid) + pause_path = os.path.join(self.opts["cachedir"], "state_pause", self.jid) if os.path.isfile(pause_path): try: os.remove(pause_path) @@ -2938,16 +3168,16 @@ class State(object): if not isinstance(high, dict): errors.append( - 'Template {0} does not render to a dictionary'.format(template) + "Template {0} does not render to a dictionary".format(template) ) return high, errors - invalid_items = ('include', 'exclude', 'extends') + invalid_items = ("include", "exclude", "extends") for item in invalid_items: if item in high: errors.append( - 'The \'{0}\' declaration found on \'{1}\' is invalid when ' - 'rendering single templates'.format(item, template) + "The '{0}' declaration found on '{1}' is invalid when " + "rendering single templates".format(item, template) ) return high, errors @@ -2955,8 +3185,8 @@ class State(object): if not isinstance(high[name], dict): if isinstance(high[name], six.string_types): # Is this is a short state, it needs to be padded - if '.' in high[name]: - comps = high[name].split('.') + if "." in high[name]: + comps = high[name].split(".") high[name] = { # '__sls__': template, # '__env__': None, @@ -2965,27 +3195,27 @@ class State(object): continue errors.append( - 'ID {0} in template {1} is not a dictionary'.format( + "ID {0} in template {1} is not a dictionary".format( name, template ) ) continue skeys = set() for key in sorted(high[name]): - if key.startswith('_'): + if key.startswith("_"): continue if high[name][key] is None: errors.append( - 'ID \'{0}\' in template {1} contains a short ' - 'declaration ({2}) with a trailing colon. When not ' - 'passing any arguments to a state, the colon must be ' - 'omitted.'.format(name, template, key) + "ID '{0}' in template {1} contains a short " + "declaration ({2}) with a trailing colon. When not " + "passing any arguments to a state, the colon must be " + "omitted.".format(name, template, key) ) continue if not isinstance(high[name][key], list): continue - if '.' in key: - comps = key.split('.') + if "." in key: + comps = key.split(".") # Salt doesn't support state files such as: # # /etc/redis/redis.conf: @@ -2997,9 +3227,8 @@ class State(object): # - regex: ^requirepass if comps[0] in skeys: errors.append( - 'ID \'{0}\' in template \'{1}\' contains multiple ' - 'state declarations of the same type' - .format(name, template) + "ID '{0}' in template '{1}' contains multiple " + "state declarations of the same type".format(name, template) ) continue high[name][comps[0]] = high[name].pop(key) @@ -3011,14 +3240,16 @@ class State(object): return high, errors def call_template(self, template): - ''' + """ Enforce the states in a template - ''' - high = compile_template(template, - self.rend, - self.opts['renderer'], - self.opts['renderer_blacklist'], - self.opts['renderer_whitelist']) + """ + high = compile_template( + template, + self.rend, + self.opts["renderer"], + self.opts["renderer_blacklist"], + self.opts["renderer_whitelist"], + ) if not high: return high high, errors = self.render_template(high, template) @@ -3027,30 +3258,33 @@ class State(object): return self.call_high(high) def call_template_str(self, template): - ''' + """ Enforce the states in a template, pass the template as a string - ''' - high = compile_template_str(template, - self.rend, - self.opts['renderer'], - self.opts['renderer_blacklist'], - self.opts['renderer_whitelist']) + """ + high = compile_template_str( + template, + self.rend, + self.opts["renderer"], + self.opts["renderer_blacklist"], + self.opts["renderer_whitelist"], + ) if not high: return high - high, errors = self.render_template(high, '<template-str>') + high, errors = self.render_template(high, "<template-str>") if errors: return errors return self.call_high(high) class BaseHighState(object): - ''' + """ The BaseHighState is an abstract base class that is the foundation of running a highstate, extend it and add a self.state object of type State. When extending this class, please note that ``self.client`` and ``self.matcher`` should be instantiated and handled. - ''' + """ + def __init__(self, opts): self.opts = self.__gen_opts(opts) self.iorder = 10000 @@ -3059,73 +3293,77 @@ class BaseHighState(object): self.building_highstate = OrderedDict() def __gather_avail(self): - ''' + """ Gather the lists of available sls data from the master - ''' + """ avail = {} for saltenv in self._get_envs(): avail[saltenv] = self.client.list_states(saltenv) return avail def __gen_opts(self, opts): - ''' + """ The options used by the High State object are derived from options on the minion and the master, or just the minion if the high state call is entirely local. - ''' + """ # If the state is intended to be applied locally, then the local opts # should have all of the needed data, otherwise overwrite the local # data items with data from the master - if 'local_state' in opts: - if opts['local_state']: + if "local_state" in opts: + if opts["local_state"]: return opts mopts = self.client.master_opts() if not isinstance(mopts, dict): # An error happened on the master - opts['renderer'] = 'jinja|yaml' - opts['failhard'] = False - opts['state_top'] = salt.utils.url.create('top.sls') - opts['nodegroups'] = {} - opts['file_roots'] = {'base': [syspaths.BASE_FILE_ROOTS_DIR]} + opts["renderer"] = "jinja|yaml" + opts["failhard"] = False + opts["state_top"] = salt.utils.url.create("top.sls") + opts["nodegroups"] = {} + opts["file_roots"] = {"base": [syspaths.BASE_FILE_ROOTS_DIR]} else: - opts['renderer'] = mopts['renderer'] - opts['failhard'] = mopts.get('failhard', False) - if mopts['state_top'].startswith('salt://'): - opts['state_top'] = mopts['state_top'] - elif mopts['state_top'].startswith('/'): - opts['state_top'] = salt.utils.url.create(mopts['state_top'][1:]) + opts["renderer"] = mopts["renderer"] + opts["failhard"] = mopts.get("failhard", False) + if mopts["state_top"].startswith("salt://"): + opts["state_top"] = mopts["state_top"] + elif mopts["state_top"].startswith("/"): + opts["state_top"] = salt.utils.url.create(mopts["state_top"][1:]) else: - opts['state_top'] = salt.utils.url.create(mopts['state_top']) - opts['state_top_saltenv'] = mopts.get('state_top_saltenv', None) - opts['nodegroups'] = mopts.get('nodegroups', {}) - opts['state_auto_order'] = mopts.get( - 'state_auto_order', - opts['state_auto_order']) - opts['file_roots'] = mopts['file_roots'] - opts['top_file_merging_strategy'] = mopts.get('top_file_merging_strategy', - opts.get('top_file_merging_strategy')) - opts['env_order'] = mopts.get('env_order', opts.get('env_order', [])) - opts['default_top'] = mopts.get('default_top', opts.get('default_top')) - opts['state_events'] = mopts.get('state_events') - opts['state_aggregate'] = mopts.get('state_aggregate', opts.get('state_aggregate', False)) - opts['jinja_env'] = mopts.get('jinja_env', {}) - opts['jinja_sls_env'] = mopts.get('jinja_sls_env', {}) - opts['jinja_lstrip_blocks'] = mopts.get('jinja_lstrip_blocks', False) - opts['jinja_trim_blocks'] = mopts.get('jinja_trim_blocks', False) + opts["state_top"] = salt.utils.url.create(mopts["state_top"]) + opts["state_top_saltenv"] = mopts.get("state_top_saltenv", None) + opts["nodegroups"] = mopts.get("nodegroups", {}) + opts["state_auto_order"] = mopts.get( + "state_auto_order", opts["state_auto_order"] + ) + opts["file_roots"] = mopts["file_roots"] + opts["top_file_merging_strategy"] = mopts.get( + "top_file_merging_strategy", opts.get("top_file_merging_strategy") + ) + opts["env_order"] = mopts.get("env_order", opts.get("env_order", [])) + opts["default_top"] = mopts.get("default_top", opts.get("default_top")) + opts["state_events"] = mopts.get("state_events") + opts["state_aggregate"] = mopts.get( + "state_aggregate", opts.get("state_aggregate", False) + ) + opts["jinja_env"] = mopts.get("jinja_env", {}) + opts["jinja_sls_env"] = mopts.get("jinja_sls_env", {}) + opts["jinja_lstrip_blocks"] = mopts.get("jinja_lstrip_blocks", False) + opts["jinja_trim_blocks"] = mopts.get("jinja_trim_blocks", False) return opts def _get_envs(self): - ''' + """ Pull the file server environments out of the master options - ''' - envs = ['base'] - if 'file_roots' in self.opts: - envs.extend([x for x in list(self.opts['file_roots']) - if x not in envs]) - env_order = self.opts.get('env_order', []) + """ + envs = ["base"] + if "file_roots" in self.opts: + envs.extend([x for x in list(self.opts["file_roots"]) if x not in envs]) + env_order = self.opts.get("env_order", []) # Remove duplicates while preserving the order members = set() - env_order = [env for env in env_order if not (env in members or members.add(env))] + env_order = [ + env for env in env_order if not (env in members or members.add(env)) + ] client_envs = self.client.envs() if env_order and client_envs: return [env for env in env_order if env in client_envs] @@ -3137,97 +3375,100 @@ class BaseHighState(object): return envs def get_tops(self): - ''' + """ Gather the top files - ''' + """ tops = DefaultOrderedDict(list) include = DefaultOrderedDict(list) done = DefaultOrderedDict(list) found = 0 # did we find any contents in the top files? # Gather initial top files - merging_strategy = self.opts['top_file_merging_strategy'] - if merging_strategy == 'same' and not self.opts['saltenv']: - if not self.opts['default_top']: + merging_strategy = self.opts["top_file_merging_strategy"] + if merging_strategy == "same" and not self.opts["saltenv"]: + if not self.opts["default_top"]: raise SaltRenderError( - 'top_file_merging_strategy set to \'same\', but no ' - 'default_top configuration option was set' + "top_file_merging_strategy set to 'same', but no " + "default_top configuration option was set" ) - if self.opts['saltenv']: + if self.opts["saltenv"]: contents = self.client.cache_file( - self.opts['state_top'], - self.opts['saltenv'] + self.opts["state_top"], self.opts["saltenv"] ) if contents: found = 1 - tops[self.opts['saltenv']] = [ + tops[self.opts["saltenv"]] = [ compile_template( contents, self.state.rend, - self.state.opts['renderer'], - self.state.opts['renderer_blacklist'], - self.state.opts['renderer_whitelist'], - saltenv=self.opts['saltenv'] + self.state.opts["renderer"], + self.state.opts["renderer_blacklist"], + self.state.opts["renderer_whitelist"], + saltenv=self.opts["saltenv"], ) ] else: - tops[self.opts['saltenv']] = [{}] + tops[self.opts["saltenv"]] = [{}] else: found = 0 - state_top_saltenv = self.opts.get('state_top_saltenv', False) - if state_top_saltenv \ - and not isinstance(state_top_saltenv, six.string_types): + state_top_saltenv = self.opts.get("state_top_saltenv", False) + if state_top_saltenv and not isinstance( + state_top_saltenv, six.string_types + ): state_top_saltenv = six.text_type(state_top_saltenv) - for saltenv in [state_top_saltenv] if state_top_saltenv \ - else self._get_envs(): - contents = self.client.cache_file( - self.opts['state_top'], - saltenv - ) + for saltenv in ( + [state_top_saltenv] if state_top_saltenv else self._get_envs() + ): + contents = self.client.cache_file(self.opts["state_top"], saltenv) if contents: found = found + 1 tops[saltenv].append( compile_template( contents, self.state.rend, - self.state.opts['renderer'], - self.state.opts['renderer_blacklist'], - self.state.opts['renderer_whitelist'], - saltenv=saltenv + self.state.opts["renderer"], + self.state.opts["renderer_blacklist"], + self.state.opts["renderer_whitelist"], + saltenv=saltenv, ) ) else: tops[saltenv].append({}) - log.debug('No contents loaded for saltenv \'%s\'', saltenv) + log.debug("No contents loaded for saltenv '%s'", saltenv) - if found > 1 and merging_strategy == 'merge' and not self.opts.get('env_order', None): + if ( + found > 1 + and merging_strategy == "merge" + and not self.opts.get("env_order", None) + ): log.warning( - 'top_file_merging_strategy is set to \'%s\' and ' - 'multiple top files were found. Merging order is not ' - 'deterministic, it may be desirable to either set ' - 'top_file_merging_strategy to \'same\' or use the ' - '\'env_order\' configuration parameter to specify the ' - 'merging order.', merging_strategy + "top_file_merging_strategy is set to '%s' and " + "multiple top files were found. Merging order is not " + "deterministic, it may be desirable to either set " + "top_file_merging_strategy to 'same' or use the " + "'env_order' configuration parameter to specify the " + "merging order.", + merging_strategy, ) if found == 0: log.debug( - 'No contents found in top file. If this is not expected, ' - 'verify that the \'file_roots\' specified in \'etc/master\' ' - 'are accessible. The \'file_roots\' configuration is: %s', - repr(self.state.opts['file_roots']) + "No contents found in top file. If this is not expected, " + "verify that the 'file_roots' specified in 'etc/master' " + "are accessible. The 'file_roots' configuration is: %s", + repr(self.state.opts["file_roots"]), ) # Search initial top files for includes for saltenv, ctops in six.iteritems(tops): for ctop in ctops: - if 'include' not in ctop: + if "include" not in ctop: continue - for sls in ctop['include']: + for sls in ctop["include"]: include[saltenv].append(sls) - ctop.pop('include') + ctop.pop("include") # Go through the includes and pull out the extra tops and add them while include: pops = [] @@ -3241,15 +3482,12 @@ class BaseHighState(object): continue tops[saltenv].append( compile_template( - self.client.get_state( - sls, - saltenv - ).get('dest', False), + self.client.get_state(sls, saltenv).get("dest", False), self.state.rend, - self.state.opts['renderer'], - self.state.opts['renderer_blacklist'], - self.state.opts['renderer_whitelist'], - saltenv + self.state.opts["renderer"], + self.state.opts["renderer_blacklist"], + self.state.opts["renderer_whitelist"], + saltenv, ) ) done[saltenv].append(sls) @@ -3259,94 +3497,103 @@ class BaseHighState(object): return tops def merge_tops(self, tops): - ''' + """ Cleanly merge the top files - ''' - merging_strategy = self.opts['top_file_merging_strategy'] + """ + merging_strategy = self.opts["top_file_merging_strategy"] try: - merge_attr = '_merge_tops_{0}'.format(merging_strategy) + merge_attr = "_merge_tops_{0}".format(merging_strategy) merge_func = getattr(self, merge_attr) - if not hasattr(merge_func, '__call__'): - msg = '\'{0}\' is not callable'.format(merge_attr) + if not hasattr(merge_func, "__call__"): + msg = "'{0}' is not callable".format(merge_attr) log.error(msg) raise TypeError(msg) except (AttributeError, TypeError): log.warning( - 'Invalid top_file_merging_strategy \'%s\', falling back to ' - '\'merge\'', merging_strategy + "Invalid top_file_merging_strategy '%s', falling back to " "'merge'", + merging_strategy, ) merge_func = self._merge_tops_merge return merge_func(tops) def _merge_tops_merge(self, tops): - ''' + """ The default merging strategy. The base env is authoritative, so it is checked first, followed by the remaining environments. In top files from environments other than "base", only the section matching the environment from the top file will be considered, and it too will be ignored if that environment was defined in the "base" top file. - ''' + """ top = DefaultOrderedDict(OrderedDict) # Check base env first as it is authoritative - base_tops = tops.pop('base', DefaultOrderedDict(OrderedDict)) + base_tops = tops.pop("base", DefaultOrderedDict(OrderedDict)) for ctop in base_tops: for saltenv, targets in six.iteritems(ctop): - if saltenv == 'include': + if saltenv == "include": continue try: for tgt in targets: top[saltenv][tgt] = ctop[saltenv][tgt] except TypeError: - raise SaltRenderError('Unable to render top file. No targets found.') + raise SaltRenderError( + "Unable to render top file. No targets found." + ) for cenv, ctops in six.iteritems(tops): for ctop in ctops: for saltenv, targets in six.iteritems(ctop): - if saltenv == 'include': + if saltenv == "include": continue elif saltenv != cenv: log.debug( - 'Section for saltenv \'%s\' in the \'%s\' ' - 'saltenv\'s top file will be ignored, as the ' - 'top_file_merging_strategy is set to \'merge\' ' - 'and the saltenvs do not match', - saltenv, cenv + "Section for saltenv '%s' in the '%s' " + "saltenv's top file will be ignored, as the " + "top_file_merging_strategy is set to 'merge' " + "and the saltenvs do not match", + saltenv, + cenv, ) continue elif saltenv in top: log.debug( - 'Section for saltenv \'%s\' in the \'%s\' ' - 'saltenv\'s top file will be ignored, as this ' - 'saltenv was already defined in the \'base\' top ' - 'file', saltenv, cenv + "Section for saltenv '%s' in the '%s' " + "saltenv's top file will be ignored, as this " + "saltenv was already defined in the 'base' top " + "file", + saltenv, + cenv, ) continue try: for tgt in targets: top[saltenv][tgt] = ctop[saltenv][tgt] except TypeError: - raise SaltRenderError('Unable to render top file. No targets found.') + raise SaltRenderError( + "Unable to render top file. No targets found." + ) return top def _merge_tops_same(self, tops): - ''' + """ For each saltenv, only consider the top file from that saltenv. All sections matching a given saltenv, which appear in a different saltenv's top file, will be ignored. - ''' + """ top = DefaultOrderedDict(OrderedDict) for cenv, ctops in six.iteritems(tops): if all([x == {} for x in ctops]): # No top file found in this env, check the default_top - default_top = self.opts['default_top'] + default_top = self.opts["default_top"] fallback_tops = tops.get(default_top, []) if all([x == {} for x in fallback_tops]): # Nothing in the fallback top file log.error( - 'The \'%s\' saltenv has no top file, and the fallback ' - 'saltenv specified by default_top (%s) also has no ' - 'top file', cenv, default_top + "The '%s' saltenv has no top file, and the fallback " + "saltenv specified by default_top (%s) also has no " + "top file", + cenv, + default_top, ) continue @@ -3355,17 +3602,21 @@ class BaseHighState(object): if saltenv != cenv: continue log.debug( - 'The \'%s\' saltenv has no top file, using the ' - 'default_top saltenv (%s)', cenv, default_top + "The '%s' saltenv has no top file, using the " + "default_top saltenv (%s)", + cenv, + default_top, ) for tgt in targets: top[saltenv][tgt] = ctop[saltenv][tgt] break else: log.error( - 'The \'%s\' saltenv has no top file, and no ' - 'matches were found in the top file for the ' - 'default_top saltenv (%s)', cenv, default_top + "The '%s' saltenv has no top file, and no " + "matches were found in the top file for the " + "default_top saltenv (%s)", + cenv, + default_top, ) continue @@ -3373,15 +3624,16 @@ class BaseHighState(object): else: for ctop in ctops: for saltenv, targets in six.iteritems(ctop): - if saltenv == 'include': + if saltenv == "include": continue elif saltenv != cenv: log.debug( - 'Section for saltenv \'%s\' in the \'%s\' ' - 'saltenv\'s top file will be ignored, as the ' - 'top_file_merging_strategy is set to \'same\' ' - 'and the saltenvs do not match', - saltenv, cenv + "Section for saltenv '%s' in the '%s' " + "saltenv's top file will be ignored, as the " + "top_file_merging_strategy is set to 'same' " + "and the saltenvs do not match", + saltenv, + cenv, ) continue @@ -3389,13 +3641,16 @@ class BaseHighState(object): for tgt in targets: top[saltenv][tgt] = ctop[saltenv][tgt] except TypeError: - raise SaltRenderError('Unable to render top file. No targets found.') + raise SaltRenderError( + "Unable to render top file. No targets found." + ) return top def _merge_tops_merge_all(self, tops): - ''' + """ Merge the top files into a single dictionary - ''' + """ + def _read_tgt(tgt): match_type = None states = [] @@ -3410,7 +3665,7 @@ class BaseHighState(object): for ctops in six.itervalues(tops): for ctop in ctops: for saltenv, targets in six.iteritems(ctop): - if saltenv == 'include': + if saltenv == "include": continue try: for tgt in targets: @@ -3427,37 +3682,40 @@ class BaseHighState(object): merged.extend([x for x in m_states2 if x not in merged]) top[saltenv][tgt] = merged except TypeError: - raise SaltRenderError('Unable to render top file. No targets found.') + raise SaltRenderError( + "Unable to render top file. No targets found." + ) return top def verify_tops(self, tops): - ''' + """ Verify the contents of the top file data - ''' + """ errors = [] if not isinstance(tops, dict): - errors.append('Top data was not formed as a dict') + errors.append("Top data was not formed as a dict") # No further checks will work, bail out return errors for saltenv, matches in six.iteritems(tops): - if saltenv == 'include': + if saltenv == "include": continue if not isinstance(saltenv, six.string_types): errors.append( - 'Environment {0} in top file is not formed as a ' - 'string'.format(saltenv) + "Environment {0} in top file is not formed as a " + "string".format(saltenv) ) - if saltenv == '': - errors.append('Empty saltenv statement in top file') + if saltenv == "": + errors.append("Empty saltenv statement in top file") if not isinstance(matches, dict): errors.append( - 'The top file matches for saltenv {0} are not ' - 'formatted as a dict'.format(saltenv) + "The top file matches for saltenv {0} are not " + "formatted as a dict".format(saltenv) ) for slsmods in six.itervalues(matches): if not isinstance(slsmods, list): - errors.append('Malformed topfile (state declarations not ' - 'formed as a list)') + errors.append( + "Malformed topfile (state declarations not " "formed as a list)" + ) continue for slsmod in slsmods: if isinstance(slsmod, dict): @@ -3465,61 +3723,55 @@ class BaseHighState(object): for val in six.itervalues(slsmod): if not val: errors.append( - 'Improperly formatted top file matcher ' - 'in saltenv {0}: {1} file'.format( - slsmod, - val - ) + "Improperly formatted top file matcher " + "in saltenv {0}: {1} file".format(slsmod, val) ) elif isinstance(slsmod, six.string_types): # This is a sls module if not slsmod: errors.append( - 'Environment {0} contains an empty sls ' - 'index'.format(saltenv) + "Environment {0} contains an empty sls " + "index".format(saltenv) ) return errors def get_top(self): - ''' + """ Returns the high data derived from the top file - ''' + """ try: tops = self.get_tops() except SaltRenderError as err: - log.error('Unable to render top file: %s', err.error) + log.error("Unable to render top file: %s", err.error) return {} return self.merge_tops(tops) def top_matches(self, top): - ''' + """ Search through the top high data for matches and return the states that this minion needs to execute. Returns: {'saltenv': ['state1', 'state2', ...]} - ''' + """ matches = DefaultOrderedDict(OrderedDict) # pylint: disable=cell-var-from-loop for saltenv, body in six.iteritems(top): - if self.opts['saltenv']: - if saltenv != self.opts['saltenv']: + if self.opts["saltenv"]: + if saltenv != self.opts["saltenv"]: continue for match, data in six.iteritems(body): + def _filter_matches(_match, _data, _opts): if isinstance(_data, six.string_types): _data = [_data] - if self.matchers['confirm_top.confirm_top']( - _match, - _data, - _opts - ): + if self.matchers["confirm_top.confirm_top"](_match, _data, _opts): if saltenv not in matches: matches[saltenv] = [] for item in _data: - if 'subfilter' in item: - _tmpdata = item.pop('subfilter') + if "subfilter" in item: + _tmpdata = item.pop("subfilter") for match, data in six.iteritems(_tmpdata): _filter_matches(match, data, _opts) if isinstance(item, six.string_types): @@ -3531,11 +3783,12 @@ class BaseHighState(object): if env_key not in matches: matches[env_key] = [] matches[env_key].append(inc_sls) - _filter_matches(match, data, self.opts['nodegroups']) + + _filter_matches(match, data, self.opts["nodegroups"]) ext_matches = self._master_tops() for saltenv in ext_matches: top_file_matches = matches.get(saltenv, []) - if self.opts.get('master_tops_first'): + if self.opts.get("master_tops_first"): first = ext_matches[saltenv] second = top_file_matches else: @@ -3547,94 +3800,90 @@ class BaseHighState(object): return matches def _master_tops(self): - ''' + """ Get results from the master_tops system. Override this function if the execution of the master_tops needs customization. - ''' + """ return self.client.master_tops() def load_dynamic(self, matches): - ''' + """ If autoload_dynamic_modules is True then automatically load the dynamic modules - ''' - if not self.opts['autoload_dynamic_modules']: + """ + if not self.opts["autoload_dynamic_modules"]: return - syncd = self.state.functions['saltutil.sync_all'](list(matches), - refresh=False) - if syncd['grains']: - self.opts['grains'] = salt.loader.grains(self.opts) - self.state.opts['pillar'] = self.state._gather_pillar() + syncd = self.state.functions["saltutil.sync_all"](list(matches), refresh=False) + if syncd["grains"]: + self.opts["grains"] = salt.loader.grains(self.opts) + self.state.opts["pillar"] = self.state._gather_pillar() self.state.module_refresh() def render_state(self, sls, saltenv, mods, matches, local=False): - ''' + """ Render a state file and retrieve all of the include states - ''' + """ errors = [] if not local: state_data = self.client.get_state(sls, saltenv) - fn_ = state_data.get('dest', False) + fn_ = state_data.get("dest", False) else: fn_ = sls if not os.path.isfile(fn_): errors.append( - 'Specified SLS {0} on local filesystem cannot ' - 'be found.'.format(sls) + "Specified SLS {0} on local filesystem cannot " + "be found.".format(sls) ) state = None if not fn_: errors.append( - 'Specified SLS {0} in saltenv {1} is not ' - 'available on the salt master or through a configured ' - 'fileserver'.format(sls, saltenv) + "Specified SLS {0} in saltenv {1} is not " + "available on the salt master or through a configured " + "fileserver".format(sls, saltenv) ) else: try: - state = compile_template(fn_, - self.state.rend, - self.state.opts['renderer'], - self.state.opts['renderer_blacklist'], - self.state.opts['renderer_whitelist'], - saltenv, - sls, - rendered_sls=mods - ) - except SaltRenderError as exc: - msg = 'Rendering SLS \'{0}:{1}\' failed: {2}'.format( - saltenv, sls, exc + state = compile_template( + fn_, + self.state.rend, + self.state.opts["renderer"], + self.state.opts["renderer_blacklist"], + self.state.opts["renderer_whitelist"], + saltenv, + sls, + rendered_sls=mods, ) + except SaltRenderError as exc: + msg = "Rendering SLS '{0}:{1}' failed: {2}".format(saltenv, sls, exc) log.critical(msg) errors.append(msg) except Exception as exc: # pylint: disable=broad-except - msg = 'Rendering SLS {0} failed, render error: {1}'.format( - sls, exc - ) + msg = "Rendering SLS {0} failed, render error: {1}".format(sls, exc) log.critical( msg, # Show the traceback if the debug logging level is enabled - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) - errors.append('{0}\n{1}'.format(msg, traceback.format_exc())) + errors.append("{0}\n{1}".format(msg, traceback.format_exc())) try: - mods.add('{0}:{1}'.format(saltenv, sls)) + mods.add("{0}:{1}".format(saltenv, sls)) except AttributeError: pass if state: if not isinstance(state, dict): - errors.append( - 'SLS {0} does not render to a dictionary'.format(sls) - ) + errors.append("SLS {0} does not render to a dictionary".format(sls)) else: include = [] - if 'include' in state: - if not isinstance(state['include'], list): - err = ('Include Declaration in SLS {0} is not formed ' - 'as a list'.format(sls)) + if "include" in state: + if not isinstance(state["include"], list): + err = ( + "Include Declaration in SLS {0} is not formed " + "as a list".format(sls) + ) errors.append(err) else: - include = state.pop('include') + include = state.pop("include") self._handle_extend(state, sls, saltenv, errors) self._handle_exclude(state, sls, saltenv, errors) @@ -3645,7 +3894,7 @@ class BaseHighState(object): # 'sls.to.include' <- same as {<saltenv>: 'sls.to.include'} # {<env_key>: 'sls.to.include'} # {'_xenv': 'sls.to.resolve'} - xenv_key = '_xenv' + xenv_key = "_xenv" if isinstance(inc_sls, dict): env_key, inc_sls = inc_sls.popitem() @@ -3653,50 +3902,60 @@ class BaseHighState(object): env_key = saltenv if env_key not in self.avail: - msg = ('Nonexistent saltenv \'{0}\' found in include ' - 'of \'{1}\' within SLS \'{2}:{3}\'' - .format(env_key, inc_sls, saltenv, sls)) + msg = ( + "Nonexistent saltenv '{0}' found in include " + "of '{1}' within SLS '{2}:{3}'".format( + env_key, inc_sls, saltenv, sls + ) + ) log.error(msg) errors.append(msg) continue - if inc_sls.startswith('.'): - match = re.match(r'^(\.+)(.*)$', inc_sls) + if inc_sls.startswith("."): + match = re.match(r"^(\.+)(.*)$", inc_sls) if match: levels, include = match.groups() else: - msg = ('Badly formatted include {0} found in include ' - 'in SLS \'{2}:{3}\'' - .format(inc_sls, saltenv, sls)) + msg = ( + "Badly formatted include {0} found in include " + "in SLS '{2}:{3}'".format(inc_sls, saltenv, sls) + ) log.error(msg) errors.append(msg) continue level_count = len(levels) - p_comps = sls.split('.') - if state_data.get('source', '').endswith('/init.sls'): - p_comps.append('init') + p_comps = sls.split(".") + if state_data.get("source", "").endswith("/init.sls"): + p_comps.append("init") if level_count > len(p_comps): - msg = ('Attempted relative include of \'{0}\' ' - 'within SLS \'{1}:{2}\' ' - 'goes beyond top level package ' - .format(inc_sls, saltenv, sls)) + msg = ( + "Attempted relative include of '{0}' " + "within SLS '{1}:{2}' " + "goes beyond top level package ".format( + inc_sls, saltenv, sls + ) + ) log.error(msg) errors.append(msg) continue - inc_sls = '.'.join(p_comps[:-level_count] + [include]) + inc_sls = ".".join(p_comps[:-level_count] + [include]) if env_key != xenv_key: if matches is None: matches = [] # Resolve inc_sls in the specified environment - if env_key in matches or fnmatch.filter(self.avail[env_key], inc_sls): + if env_key in matches or fnmatch.filter( + self.avail[env_key], inc_sls + ): resolved_envs = [env_key] else: resolved_envs = [] else: # Resolve inc_sls in the subset of environment matches resolved_envs = [ - aenv for aenv in matches + aenv + for aenv in matches if fnmatch.filter(self.avail[aenv], inc_sls) ] @@ -3708,20 +3967,18 @@ class BaseHighState(object): # there were no matches, then leave inc_sls as the # target so that the next recursion of render_state # will recognize the error. - sls_targets = fnmatch.filter( - self.avail[saltenv], + sls_targets = fnmatch.filter(self.avail[saltenv], inc_sls) or [ inc_sls - ) or [inc_sls] + ] for sls_target in sls_targets: - r_env = resolved_envs[0] if len(resolved_envs) == 1 else saltenv - mod_tgt = '{0}:{1}'.format(r_env, sls_target) + r_env = ( + resolved_envs[0] if len(resolved_envs) == 1 else saltenv + ) + mod_tgt = "{0}:{1}".format(r_env, sls_target) if mod_tgt not in mods: nstate, err = self.render_state( - sls_target, - r_env, - mods, - matches + sls_target, r_env, mods, matches ) if nstate: self.merge_included_states(state, nstate, errors) @@ -3729,34 +3986,36 @@ class BaseHighState(object): if err: errors.extend(err) else: - msg = '' + msg = "" if not resolved_envs: - msg = ('Unknown include: Specified SLS {0}: {1} is not available on the salt ' - 'master in saltenv(s): {2} ' - ).format(env_key, - inc_sls, - ', '.join(matches) if env_key == xenv_key else env_key) + msg = ( + "Unknown include: Specified SLS {0}: {1} is not available on the salt " + "master in saltenv(s): {2} " + ).format( + env_key, + inc_sls, + ", ".join(matches) if env_key == xenv_key else env_key, + ) elif len(resolved_envs) > 1: - msg = ('Ambiguous include: Specified SLS {0}: {1} is available on the salt master ' - 'in multiple available saltenvs: {2}' - ).format(env_key, - inc_sls, - ', '.join(resolved_envs)) + msg = ( + "Ambiguous include: Specified SLS {0}: {1} is available on the salt master " + "in multiple available saltenvs: {2}" + ).format(env_key, inc_sls, ", ".join(resolved_envs)) log.critical(msg) errors.append(msg) try: self._handle_iorder(state) except TypeError: - log.critical('Could not render SLS %s. Syntax error detected.', sls) + log.critical("Could not render SLS %s. Syntax error detected.", sls) else: state = {} return state, errors def _handle_iorder(self, state): - ''' + """ Take a state and apply the iorder system - ''' - if self.opts['state_auto_order']: + """ + if self.opts["state_auto_order"]: for name in state: for s_dec in state[name]: if not isinstance(s_dec, six.string_types): @@ -3771,55 +4030,53 @@ class BaseHighState(object): continue found = False - if s_dec.startswith('_'): + if s_dec.startswith("_"): continue for arg in state[name][s_dec]: if isinstance(arg, dict): if len(arg) > 0: - if next(six.iterkeys(arg)) == 'order': + if next(six.iterkeys(arg)) == "order": found = True if not found: if not isinstance(state[name][s_dec], list): # quite certainly a syntax error, managed elsewhere continue - state[name][s_dec].append( - {'order': self.iorder} - ) + state[name][s_dec].append({"order": self.iorder}) self.iorder += 1 return state def _handle_state_decls(self, state, sls, saltenv, errors): - ''' + """ Add sls and saltenv components to the state - ''' + """ for name in state: if not isinstance(state[name], dict): - if name == '__extend__': + if name == "__extend__": continue - if name == '__exclude__': + if name == "__exclude__": continue if isinstance(state[name], six.string_types): # Is this is a short state, it needs to be padded - if '.' in state[name]: - comps = state[name].split('.') - state[name] = {'__sls__': sls, - '__env__': saltenv, - comps[0]: [comps[1]]} + if "." in state[name]: + comps = state[name].split(".") + state[name] = { + "__sls__": sls, + "__env__": saltenv, + comps[0]: [comps[1]], + } continue - errors.append( - 'ID {0} in SLS {1} is not a dictionary'.format(name, sls) - ) + errors.append("ID {0} in SLS {1} is not a dictionary".format(name, sls)) continue skeys = set() for key in list(state[name]): - if key.startswith('_'): + if key.startswith("_"): continue if not isinstance(state[name][key], list): continue - if '.' in key: - comps = key.split('.') + if "." in key: + comps = key.split(".") # Salt doesn't support state files such as: # # /etc/redis/redis.conf: @@ -3832,8 +4089,8 @@ class BaseHighState(object): # - regex: ^requirepass if comps[0] in skeys: errors.append( - 'ID \'{0}\' in SLS \'{1}\' contains multiple state ' - 'declarations of the same type'.format(name, sls) + "ID '{0}' in SLS '{1}' contains multiple state " + "declarations of the same type".format(name, sls) ) continue state[name][comps[0]] = state[name].pop(key) @@ -3841,61 +4098,67 @@ class BaseHighState(object): skeys.add(comps[0]) continue skeys.add(key) - if '__sls__' not in state[name]: - state[name]['__sls__'] = sls - if '__env__' not in state[name]: - state[name]['__env__'] = saltenv + if "__sls__" not in state[name]: + state[name]["__sls__"] = sls + if "__env__" not in state[name]: + state[name]["__env__"] = saltenv def _handle_extend(self, state, sls, saltenv, errors): - ''' + """ Take the extend dec out of state and apply to the highstate global dec - ''' - if 'extend' in state: - ext = state.pop('extend') + """ + if "extend" in state: + ext = state.pop("extend") if not isinstance(ext, dict): - errors.append(('Extension value in SLS \'{0}\' is not a ' - 'dictionary').format(sls)) + errors.append( + ("Extension value in SLS '{0}' is not a " "dictionary").format(sls) + ) return for name in ext: if not isinstance(ext[name], dict): - errors.append(('Extension name \'{0}\' in SLS \'{1}\' is ' - 'not a dictionary' - .format(name, sls))) + errors.append( + ( + "Extension name '{0}' in SLS '{1}' is " + "not a dictionary".format(name, sls) + ) + ) continue - if '__sls__' not in ext[name]: - ext[name]['__sls__'] = sls - if '__env__' not in ext[name]: - ext[name]['__env__'] = saltenv + if "__sls__" not in ext[name]: + ext[name]["__sls__"] = sls + if "__env__" not in ext[name]: + ext[name]["__env__"] = saltenv for key in list(ext[name]): - if key.startswith('_'): + if key.startswith("_"): continue if not isinstance(ext[name][key], list): continue - if '.' in key: - comps = key.split('.') + if "." in key: + comps = key.split(".") ext[name][comps[0]] = ext[name].pop(key) ext[name][comps[0]].append(comps[1]) - state.setdefault('__extend__', []).append(ext) + state.setdefault("__extend__", []).append(ext) def _handle_exclude(self, state, sls, saltenv, errors): - ''' + """ Take the exclude dec out of the state and apply it to the highstate global dec - ''' - if 'exclude' in state: - exc = state.pop('exclude') + """ + if "exclude" in state: + exc = state.pop("exclude") if not isinstance(exc, list): - err = ('Exclude Declaration in SLS {0} is not formed ' - 'as a list'.format(sls)) + err = ( + "Exclude Declaration in SLS {0} is not formed " + "as a list".format(sls) + ) errors.append(err) - state.setdefault('__exclude__', []).extend(exc) + state.setdefault("__exclude__", []).extend(exc) def render_highstate(self, matches): - ''' + """ Gather the state files and render them into a single unified salt high data structure. - ''' + """ highstate = self.building_highstate all_errors = [] mods = set() @@ -3904,12 +4167,12 @@ class BaseHighState(object): for sls_match in states: if saltenv in self.avail: statefiles = fnmatch.filter(self.avail[saltenv], sls_match) - elif '__env__' in self.avail: - statefiles = fnmatch.filter(self.avail['__env__'], sls_match) + elif "__env__" in self.avail: + statefiles = fnmatch.filter(self.avail["__env__"], sls_match) else: all_errors.append( - 'No matching salt environment for environment ' - '\'{0}\' found'.format(saltenv) + "No matching salt environment for environment " + "'{0}' found".format(saltenv) ) # if we did not found any sls in the fileserver listing, this # may be because the sls was generated or added later, we can @@ -3919,86 +4182,84 @@ class BaseHighState(object): statefiles = [sls_match] for sls in statefiles: - r_env = '{0}:{1}'.format(saltenv, sls) + r_env = "{0}:{1}".format(saltenv, sls) if r_env in mods: continue - state, errors = self.render_state( - sls, saltenv, mods, matches) + state, errors = self.render_state(sls, saltenv, mods, matches) if state: self.merge_included_states(highstate, state, errors) for i, error in enumerate(errors[:]): - if 'is not available' in error: + if "is not available" in error: # match SLS foobar in environment - this_sls = 'SLS {0} in saltenv'.format( - sls_match) + this_sls = "SLS {0} in saltenv".format(sls_match) if this_sls in error: errors[i] = ( - 'No matching sls found for \'{0}\' ' - 'in env \'{1}\''.format(sls_match, saltenv)) + "No matching sls found for '{0}' " + "in env '{1}'".format(sls_match, saltenv) + ) all_errors.extend(errors) self.clean_duplicate_extends(highstate) return highstate, all_errors def clean_duplicate_extends(self, highstate): - if '__extend__' in highstate: + if "__extend__" in highstate: highext = [] - for items in (six.iteritems(ext) for ext in highstate['__extend__']): + for items in (six.iteritems(ext) for ext in highstate["__extend__"]): for item in items: if item not in highext: highext.append(item) - highstate['__extend__'] = [{t[0]: t[1]} for t in highext] + highstate["__extend__"] = [{t[0]: t[1]} for t in highext] def merge_included_states(self, highstate, state, errors): # The extend members can not be treated as globally unique: - if '__extend__' in state: - highstate.setdefault('__extend__', - []).extend(state.pop('__extend__')) - if '__exclude__' in state: - highstate.setdefault('__exclude__', - []).extend(state.pop('__exclude__')) + if "__extend__" in state: + highstate.setdefault("__extend__", []).extend(state.pop("__extend__")) + if "__exclude__" in state: + highstate.setdefault("__exclude__", []).extend(state.pop("__exclude__")) for id_ in state: if id_ in highstate: if highstate[id_] != state[id_]: - errors.append(( - 'Detected conflicting IDs, SLS' - ' IDs need to be globally unique.\n The' - ' conflicting ID is \'{0}\' and is found in SLS' - ' \'{1}:{2}\' and SLS \'{3}:{4}\'').format( - id_, - highstate[id_]['__env__'], - highstate[id_]['__sls__'], - state[id_]['__env__'], - state[id_]['__sls__']) + errors.append( + ( + "Detected conflicting IDs, SLS" + " IDs need to be globally unique.\n The" + " conflicting ID is '{0}' and is found in SLS" + " '{1}:{2}' and SLS '{3}:{4}'" + ).format( + id_, + highstate[id_]["__env__"], + highstate[id_]["__sls__"], + state[id_]["__env__"], + state[id_]["__sls__"], + ) ) try: highstate.update(state) except ValueError: - errors.append( - 'Error when rendering state with contents: {0}'.format(state) - ) + errors.append("Error when rendering state with contents: {0}".format(state)) def _check_pillar(self, force=False): - ''' + """ Check the pillar for errors, refuse to run the state if there are errors in the pillar and return the pillar errors - ''' + """ if force: return True - if '_errors' in self.state.opts['pillar']: + if "_errors" in self.state.opts["pillar"]: return False return True def matches_whitelist(self, matches, whitelist): - ''' + """ Reads over the matches and returns a matches dict with just the ones that are in the whitelist - ''' + """ if not whitelist: return matches ret_matches = {} if not isinstance(whitelist, list): - whitelist = whitelist.split(',') + whitelist = whitelist.split(",") for env in matches: for sls in matches[env]: if sls in whitelist: @@ -4006,28 +4267,34 @@ class BaseHighState(object): ret_matches[env].append(sls) return ret_matches - def call_highstate(self, exclude=None, cache=None, cache_name='highstate', - force=False, whitelist=None, orchestration_jid=None): - ''' + def call_highstate( + self, + exclude=None, + cache=None, + cache_name="highstate", + force=False, + whitelist=None, + orchestration_jid=None, + ): + """ Run the sequence to execute the salt highstate for this minion - ''' + """ # Check that top file exists - tag_name = 'no_|-states_|-states_|-None' - ret = {tag_name: { - 'result': False, - 'comment': 'No states found for this minion', - 'name': 'No States', - 'changes': {}, - '__run_num__': 0, - }} - cfn = os.path.join( - self.opts['cachedir'], - '{0}.cache.p'.format(cache_name) - ) + tag_name = "no_|-states_|-states_|-None" + ret = { + tag_name: { + "result": False, + "comment": "No states found for this minion", + "name": "No States", + "changes": {}, + "__run_num__": 0, + } + } + cfn = os.path.join(self.opts["cachedir"], "{0}.cache.p".format(cache_name)) if cache: if os.path.isfile(cfn): - with salt.utils.files.fopen(cfn, 'rb') as fp_: + with salt.utils.files.fopen(cfn, "rb") as fp_: high = self.serial.load(fp_) return self.state.call_high(high, orchestration_jid) # File exists so continue @@ -4035,8 +4302,8 @@ class BaseHighState(object): try: top = self.get_top() except SaltRenderError as err: - ret[tag_name]['comment'] = 'Unable to render top file: ' - ret[tag_name]['comment'] += six.text_type(err.error) + ret[tag_name]["comment"] = "Unable to render top file: " + ret[tag_name]["comment"] += six.text_type(err.error) return ret except Exception: # pylint: disable=broad-except trb = traceback.format_exc() @@ -4045,24 +4312,26 @@ class BaseHighState(object): err += self.verify_tops(top) matches = self.top_matches(top) if not matches: - msg = ('No Top file or master_tops data matches found. Please see ' - 'master log for details.') - ret[tag_name]['comment'] = msg + msg = ( + "No Top file or master_tops data matches found. Please see " + "master log for details." + ) + ret[tag_name]["comment"] = msg return ret matches = self.matches_whitelist(matches, whitelist) self.load_dynamic(matches) if not self._check_pillar(force): - err += ['Pillar failed to render with the following messages:'] - err += self.state.opts['pillar']['_errors'] + err += ["Pillar failed to render with the following messages:"] + err += self.state.opts["pillar"]["_errors"] else: high, errors = self.render_highstate(matches) if exclude: if isinstance(exclude, six.string_types): - exclude = exclude.split(',') - if '__exclude__' in high: - high['__exclude__'].extend(exclude) + exclude = exclude.split(",") + if "__exclude__" in high: + high["__exclude__"].extend(exclude) else: - high['__exclude__'] = exclude + high["__exclude__"] = exclude err += errors if err: return err @@ -4072,11 +4341,12 @@ class BaseHighState(object): try: if salt.utils.platform.is_windows(): # Make sure cache file isn't read-only - self.state.functions['cmd.run']( - ['attrib', '-R', cfn], + self.state.functions["cmd.run"]( + ["attrib", "-R", cfn], python_shell=False, - output_loglevel='quiet') - with salt.utils.files.fopen(cfn, 'w+b') as fp_: + output_loglevel="quiet", + ) + with salt.utils.files.fopen(cfn, "w+b") as fp_: try: self.serial.dump(high, fp_) except TypeError: @@ -4088,9 +4358,9 @@ class BaseHighState(object): return self.state.call_high(high, orchestration_jid) def compile_highstate(self): - ''' + """ Return just the highstate or the errors - ''' + """ err = [] top = self.get_top() err += self.verify_tops(top) @@ -4104,10 +4374,10 @@ class BaseHighState(object): return high def compile_low_chunks(self): - ''' + """ Compile the highstate but don't run it, return the low chunks to see exactly what the highstate will execute - ''' + """ top = self.get_top() matches = self.top_matches(top) high, errors = self.render_highstate(matches) @@ -4131,9 +4401,9 @@ class BaseHighState(object): return chunks def compile_state_usage(self): - ''' + """ Return all used and unused states for the minion based on the top match data - ''' + """ err = [] top = self.get_top() err += self.verify_tops(top) @@ -4146,23 +4416,23 @@ class BaseHighState(object): for saltenv, states in self.avail.items(): env_usage = { - 'used': [], - 'unused': [], - 'count_all': 0, - 'count_used': 0, - 'count_unused': 0 + "used": [], + "unused": [], + "count_all": 0, + "count_used": 0, + "count_unused": 0, } env_matches = matches.get(saltenv) for state in states: - env_usage['count_all'] += 1 + env_usage["count_all"] += 1 if state in env_matches: - env_usage['count_used'] += 1 - env_usage['used'].append(state) + env_usage["count_used"] += 1 + env_usage["used"].append(state) else: - env_usage['count_unused'] += 1 - env_usage['unused'].append(state) + env_usage["count_unused"] += 1 + env_usage["unused"].append(state) state_usage[saltenv] = env_usage @@ -4170,37 +4440,41 @@ class BaseHighState(object): class HighState(BaseHighState): - ''' + """ Generate and execute the salt "High State". The High State is the compound state derived from a group of template files stored on the salt master or in the local cache. - ''' + """ + # a stack of active HighState objects during a state.highstate run stack = [] def __init__( - self, - opts, - pillar_override=None, - jid=None, - pillar_enc=None, - proxy=None, - context=None, - mocked=False, - loader='states', - initial_pillar=None): + self, + opts, + pillar_override=None, + jid=None, + pillar_enc=None, + proxy=None, + context=None, + mocked=False, + loader="states", + initial_pillar=None, + ): self.opts = opts self.client = salt.fileclient.get_file_client(self.opts) BaseHighState.__init__(self, opts) - self.state = State(self.opts, - pillar_override, - jid, - pillar_enc, - proxy=proxy, - context=context, - mocked=mocked, - loader=loader, - initial_pillar=initial_pillar) + self.state = State( + self.opts, + pillar_override, + jid, + pillar_enc, + proxy=proxy, + context=context, + mocked=mocked, + loader=loader, + initial_pillar=initial_pillar, + ) self.matchers = salt.loader.matchers(self.opts) self.proxy = proxy @@ -4235,69 +4509,70 @@ class HighState(BaseHighState): class MasterState(State): - ''' + """ Create a State object for master side compiling - ''' + """ + def __init__(self, opts, minion): State.__init__(self, opts) def load_modules(self, data=None, proxy=None): - ''' + """ Load the modules into the state - ''' - log.info('Loading fresh modules for state activity') + """ + log.info("Loading fresh modules for state activity") # Load a modified client interface that looks like the interface used # from the minion, but uses remote execution # - self.functions = salt.client.FunctionWrapper( - self.opts, - self.opts['id'] - ) + self.functions = salt.client.FunctionWrapper(self.opts, self.opts["id"]) # Load the states, but they should not be used in this class apart # from inspection self.utils = salt.loader.utils(self.opts) self.serializers = salt.loader.serializers(self.opts) - self.states = salt.loader.states(self.opts, self.functions, self.utils, self.serializers) - self.rend = salt.loader.render(self.opts, self.functions, states=self.states, context=self.state_con) + self.states = salt.loader.states( + self.opts, self.functions, self.utils, self.serializers + ) + self.rend = salt.loader.render( + self.opts, self.functions, states=self.states, context=self.state_con + ) class MasterHighState(HighState): - ''' + """ Execute highstate compilation from the master - ''' - def __init__(self, master_opts, minion_opts, grains, id_, - saltenv=None): + """ + + def __init__(self, master_opts, minion_opts, grains, id_, saltenv=None): # Force the fileclient to be local opts = copy.deepcopy(minion_opts) - opts['file_client'] = 'local' - opts['file_roots'] = master_opts['master_roots'] - opts['renderer'] = master_opts['renderer'] - opts['state_top'] = master_opts['state_top'] - opts['id'] = id_ - opts['grains'] = grains + opts["file_client"] = "local" + opts["file_roots"] = master_opts["master_roots"] + opts["renderer"] = master_opts["renderer"] + opts["state_top"] = master_opts["state_top"] + opts["id"] = id_ + opts["grains"] = grains HighState.__init__(self, opts) class RemoteHighState(object): - ''' + """ Manage gathering the data from the master - ''' + """ + # XXX: This class doesn't seem to be used anywhere def __init__(self, opts, grains): self.opts = opts self.grains = grains self.serial = salt.payload.Serial(self.opts) # self.auth = salt.crypt.SAuth(opts) - self.channel = salt.transport.client.ReqChannel.factory(self.opts['master_uri']) + self.channel = salt.transport.client.ReqChannel.factory(self.opts["master_uri"]) self._closing = False def compile_master(self): - ''' + """ Return the state data from the master - ''' - load = {'grains': self.grains, - 'opts': self.opts, - 'cmd': '_master_state'} + """ + load = {"grains": self.grains, "opts": self.opts, "cmd": "_master_state"} try: return self.channel.send(load, tries=3, timeout=72000) except SaltReqTimeoutError: @@ -4313,4 +4588,5 @@ class RemoteHighState(object): # pylint: disable=W1701 def __del__(self): self.destroy() + # pylint: enable=W1701 diff --git a/salt/states/__init__.py b/salt/states/__init__.py index fc89223250c..4892e870e17 100644 --- a/salt/states/__init__.py +++ b/salt/states/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" States Directory -''' +""" diff --git a/salt/states/acme.py b/salt/states/acme.py index b4c0101bd82..f6facefd3aa 100644 --- a/salt/states/acme.py +++ b/salt/states/acme.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" ACME / Let's Encrypt certificate management state ================================================= @@ -24,9 +24,10 @@ See also the module documentation - onchanges_in: - cmd: reload-gitlab -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -36,32 +37,36 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only work when the ACME module agrees - ''' - return 'acme.cert' in __salt__ + """ + if "acme.cert" in __salt__: + return True + return (False, "acme module could not be loaded") -def cert(name, - aliases=None, - email=None, - webroot=None, - test_cert=False, - renew=None, - keysize=None, - server=None, - owner='root', - group='root', - mode='0640', - certname=None, - preferred_challenges=None, - tls_sni_01_port=None, - tls_sni_01_address=None, - http_01_port=None, - http_01_address=None, - dns_plugin=None, - dns_plugin_credentials=None): - ''' +def cert( + name, + aliases=None, + email=None, + webroot=None, + test_cert=False, + renew=None, + keysize=None, + server=None, + owner="root", + group="root", + mode="0640", + certname=None, + preferred_challenges=None, + tls_sni_01_port=None, + tls_sni_01_address=None, + http_01_port=None, + http_01_address=None, + dns_plugin=None, + dns_plugin_credentials=None, +): + """ Obtain/renew a certificate from an ACME CA, probably Let's Encrypt. :param name: Common Name of the certificate (DNS name of certificate) @@ -90,30 +95,32 @@ def cert(name, :param https_01_address: The address the server listens to during http-01 challenge. :param dns_plugin: Name of a DNS plugin to use (currently only 'cloudflare') :param dns_plugin_credentials: Path to the credentials file if required by the specified DNS plugin - ''' - ret = {'name': name, 'result': 'changeme', 'comment': [], 'changes': {}} + """ + ret = {"name": name, "result": "changeme", "comment": [], "changes": {}} action = None current_certificate = {} new_certificate = {} - if not __salt__['acme.has'](name): - action = 'obtain' - elif __salt__['acme.needs_renewal'](name, renew): - action = 'renew' - current_certificate = __salt__['acme.info'](name) + if not __salt__["acme.has"](name): + action = "obtain" + elif __salt__["acme.needs_renewal"](name, renew): + action = "renew" + current_certificate = __salt__["acme.info"](name) else: - ret['result'] = True - ret['comment'].append('Certificate {} exists and does not need renewal.' - ''.format(name)) + ret["result"] = True + ret["comment"].append( + "Certificate {} exists and does not need renewal." "".format(name) + ) if action: - if __opts__['test']: - ret['result'] = None - ret['comment'].append('Certificate {} would have been {}ed.' - ''.format(name, action)) - ret['changes'] = {'old': 'current certificate', 'new': 'new certificate'} + if __opts__["test"]: + ret["result"] = None + ret["comment"].append( + "Certificate {} would have been {}ed." "".format(name, action) + ) + ret["changes"] = {"old": "current certificate", "new": "new certificate"} else: - res = __salt__['acme.cert']( + res = __salt__["acme.cert"]( name, aliases=aliases, email=email, @@ -134,9 +141,11 @@ def cert(name, dns_plugin=dns_plugin, dns_plugin_credentials=dns_plugin_credentials, ) - ret['result'] = res['result'] - ret['comment'].append(res['comment']) - if ret['result']: - new_certificate = __salt__['acme.info'](name) - ret['changes'] = salt.utils.dictdiffer.deep_diff(current_certificate, new_certificate) + ret["result"] = res["result"] + ret["comment"].append(res["comment"]) + if ret["result"]: + new_certificate = __salt__["acme.info"](name) + ret["changes"] = salt.utils.dictdiffer.deep_diff( + current_certificate, new_certificate + ) return ret diff --git a/salt/states/alias.py b/salt/states/alias.py index e2ed5cd773b..bc624ba1acd 100644 --- a/salt/states/alias.py +++ b/salt/states/alias.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configuration of email aliases The mail aliases file can be managed to contain definitions for specific email @@ -25,12 +25,12 @@ file from the default location, set the following in your minion config: aliases.file: /my/alias/file -''' +""" from __future__ import absolute_import, print_function, unicode_literals def present(name, target): - ''' + """ Ensures that the named alias is present with the given target or list of targets. If the alias exists but the target differs from the previous entry, the target(s) will be overwritten. If the alias does not exist, the @@ -41,57 +41,49 @@ def present(name, target): target The forwarding address - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - if __salt__['aliases.has_target'](name, target): - ret['result'] = True - ret['comment'] = 'Alias {0} already present'.format(name) + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + if __salt__["aliases.has_target"](name, target): + ret["result"] = True + ret["comment"] = "Alias {0} already present".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Alias {0} -> {1} is set to be added'.format( - name, target - ) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Alias {0} -> {1} is set to be added".format(name, target) return ret - if __salt__['aliases.set_target'](name, target): - ret['changes'] = {'alias': name} - ret['result'] = True - ret['comment'] = 'Set email alias {0} -> {1}'.format(name, target) + if __salt__["aliases.set_target"](name, target): + ret["changes"] = {"alias": name} + ret["result"] = True + ret["comment"] = "Set email alias {0} -> {1}".format(name, target) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to set alias {0} -> {1}'.format(name, target) + ret["result"] = False + ret["comment"] = "Failed to set alias {0} -> {1}".format(name, target) return ret def absent(name): - ''' + """ Ensure that the named alias is absent name The alias to remove - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - if not __salt__['aliases.get_target'](name): - ret['result'] = True - ret['comment'] = 'Alias {0} already absent'.format(name) + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + if not __salt__["aliases.get_target"](name): + ret["result"] = True + ret["comment"] = "Alias {0} already absent".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Alias {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Alias {0} is set to be removed".format(name) return ret - if __salt__['aliases.rm_alias'](name): - ret['changes'] = {'alias': name} - ret['result'] = True - ret['comment'] = 'Removed alias {0}'.format(name) + if __salt__["aliases.rm_alias"](name): + ret["changes"] = {"alias": name} + ret["result"] = True + ret["comment"] = "Removed alias {0}".format(name) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to remove alias {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to remove alias {0}".format(name) return ret diff --git a/salt/states/alternatives.py b/salt/states/alternatives.py index ae34b41439e..e2fbaba1e29 100644 --- a/salt/states/alternatives.py +++ b/salt/states/alternatives.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configuration of the alternatives system Control the alternatives system @@ -25,24 +25,24 @@ Control the alternatives system - name: hadoop-0.20-conf - path: {{ my_hadoop_conf }} -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Define a function alias in order not to shadow built-in's -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Only load if alternatives execution module is available. - ''' - return True if 'alternatives.auto' in __salt__ else False + """ + if "alternatives.auto" in __salt__: + return True + return (False, "alernatives module could not be loaded") def install(name, link, path, priority): - ''' + """ Install new alternative for defined <name> name @@ -61,50 +61,56 @@ def install(name, link, path, priority): priority is an integer; options with higher numbers have higher priority in automatic mode. - ''' - ret = {'name': name, - 'link': link, - 'path': path, - 'priority': priority, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = { + "name": name, + "link": link, + "path": path, + "priority": priority, + "result": True, + "changes": {}, + "comment": "", + } - if __salt__['alternatives.check_exists'](name, path): - ret['comment'] = 'Alternative {0} for {1} is already registered'.format(path, name) + if __salt__["alternatives.check_exists"](name, path): + ret["comment"] = "Alternative {0} for {1} is already registered".format( + path, name + ) else: - if __opts__['test']: - ret['comment'] = ( - 'Alternative will be set for {0} to {1} with priority {2}' + if __opts__["test"]: + ret["comment"] = ( + "Alternative will be set for {0} to {1} with priority {2}" ).format(name, path, priority) - ret['result'] = None + ret["result"] = None return ret - out = __salt__['alternatives.install'](name, link, path, priority) - if __salt__['alternatives.check_exists'](name, path): - if __salt__['alternatives.check_installed'](name, path): - ret['comment'] = ( - 'Alternative for {0} set to path {1} with priority {2}' + out = __salt__["alternatives.install"](name, link, path, priority) + if __salt__["alternatives.check_exists"](name, path): + if __salt__["alternatives.check_installed"](name, path): + ret["comment"] = ( + "Alternative for {0} set to path {1} with priority {2}" ).format(name, path, priority) else: - ret['comment'] = ( - 'Alternative {0} for {1} registered with priority {2} and not set to default' + ret["comment"] = ( + "Alternative {0} for {1} registered with priority {2} and not set to default" ).format(path, name, priority) - ret['changes'] = {'name': name, - 'link': link, - 'path': path, - 'priority': priority} + ret["changes"] = { + "name": name, + "link": link, + "path": path, + "priority": priority, + } else: - ret['result'] = False - ret['comment'] = ( - 'Alternative for {0} not installed: {1}' - ).format(name, out) + ret["result"] = False + ret["comment"] = ("Alternative for {0} not installed: {1}").format( + name, out + ) return ret def remove(name, path): - ''' + """ Removes installed alternative for defined <name> and <path> or fallback to default alternative, if some defined before. @@ -115,52 +121,45 @@ def remove(name, path): path is the location of one of the alternative target files. (e.g. /usr/bin/less) - ''' - ret = {'name': name, - 'path': path, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "path": path, "result": True, "changes": {}, "comment": ""} - isinstalled = __salt__['alternatives.check_exists'](name, path) + isinstalled = __salt__["alternatives.check_exists"](name, path) if isinstalled: - if __opts__['test']: - ret['comment'] = ('Alternative for {0} will be removed' - .format(name)) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Alternative for {0} will be removed".format(name) + ret["result"] = None return ret - __salt__['alternatives.remove'](name, path) - current = __salt__['alternatives.show_current'](name) + __salt__["alternatives.remove"](name, path) + current = __salt__["alternatives.show_current"](name) if current: - ret['result'] = True - ret['comment'] = ( - 'Alternative for {0} removed. Falling back to path {1}' + ret["result"] = True + ret["comment"] = ( + "Alternative for {0} removed. Falling back to path {1}" ).format(name, current) - ret['changes'] = {'path': current} + ret["changes"] = {"path": current} return ret - ret['comment'] = 'Alternative for {0} removed'.format(name) - ret['changes'] = {} + ret["comment"] = "Alternative for {0} removed".format(name) + ret["changes"] = {} return ret - current = __salt__['alternatives.show_current'](name) + current = __salt__["alternatives.show_current"](name) if current: - ret['result'] = True - ret['comment'] = ( - 'Alternative for {0} is set to it\'s default path {1}' - ).format(name, current) + ret["result"] = True + ret["comment"] = ("Alternative for {0} is set to it's default path {1}").format( + name, current + ) return ret - ret['result'] = False - ret['comment'] = ( - 'Alternative for {0} doesn\'t exist' - ).format(name) + ret["result"] = False + ret["comment"] = ("Alternative for {0} doesn't exist").format(name) return ret def auto(name): - ''' + """ .. versionadded:: 0.17.0 Instruct alternatives to use the highest priority @@ -170,28 +169,25 @@ def auto(name): is the master name for this link group (e.g. pager) - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - display = __salt__['alternatives.display'](name) + display = __salt__["alternatives.display"](name) line = display.splitlines()[0] - if line.endswith(' auto mode'): - ret['comment'] = '{0} already in auto mode'.format(name) + if line.endswith(" auto mode"): + ret["comment"] = "{0} already in auto mode".format(name) return ret - if __opts__['test']: - ret['comment'] = '{0} will be put in auto mode'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} will be put in auto mode".format(name) + ret["result"] = None return ret - ret['changes']['result'] = __salt__['alternatives.auto'](name) + ret["changes"]["result"] = __salt__["alternatives.auto"](name) return ret def set_(name, path): - ''' + """ .. versionadded:: 0.17.0 Sets alternative for <name> to <path>, if <path> is defined @@ -210,19 +206,15 @@ def set_(name, path): foo: alternatives.set: - path: /usr/bin/foo-2.0 - ''' - ret = {'name': name, - 'path': path, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "path": path, "result": True, "changes": {}, "comment": ""} - current = __salt__['alternatives.show_current'](name) + current = __salt__["alternatives.show_current"](name) if current == path: - ret['comment'] = 'Alternative for {0} already set to {1}'.format(name, path) + ret["comment"] = "Alternative for {0} already set to {1}".format(name, path) return ret - display = __salt__['alternatives.display'](name) + display = __salt__["alternatives.display"](name) isinstalled = False for line in display.splitlines(): if line.startswith(path): @@ -230,28 +222,26 @@ def set_(name, path): break if isinstalled: - if __opts__['test']: - ret['comment'] = ( - 'Alternative for {0} will be set to path {1}' - ).format(name, path) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = ("Alternative for {0} will be set to path {1}").format( + name, path + ) + ret["result"] = None return ret - __salt__['alternatives.set'](name, path) - current = __salt__['alternatives.show_current'](name) + __salt__["alternatives.set"](name, path) + current = __salt__["alternatives.show_current"](name) if current == path: - ret['comment'] = ( - 'Alternative for {0} set to path {1}' - ).format(name, current) - ret['changes'] = {'path': current} + ret["comment"] = ("Alternative for {0} set to path {1}").format( + name, current + ) + ret["changes"] = {"path": current} else: - ret['comment'] = 'Alternative for {0} not updated'.format(name) + ret["comment"] = "Alternative for {0} not updated".format(name) return ret else: - ret['result'] = False - ret['comment'] = ( - 'Alternative {0} for {1} doesn\'t exist' - ).format(path, name) + ret["result"] = False + ret["comment"] = ("Alternative {0} for {1} doesn't exist").format(path, name) return ret diff --git a/salt/states/ansiblegate.py b/salt/states/ansiblegate.py index b42dc02938c..6acfca705d8 100644 --- a/salt/states/ansiblegate.py +++ b/salt/states/ansiblegate.py @@ -15,7 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -r''' +r""" Execution of Ansible modules from within states =============================================== @@ -32,34 +32,37 @@ state: - name: emacs - state: installed -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging import os import sys +import salt.ext.six as six + # Import salt modules import salt.fileclient -import salt.ext.six as six -from salt.utils.decorators import depends import salt.utils.decorators.path +from salt.utils.decorators import depends log = logging.getLogger(__name__) -__virtualname__ = 'ansible' +__virtualname__ = "ansible" -@depends('ansible') +@depends("ansible") class AnsibleState(object): - ''' + """ Ansible state caller. - ''' + """ + def get_args(self, argset): - ''' + """ Get args and kwargs from the argset. :param argset: :return: - ''' + """ args = [] kwargs = {} for element in argset or []: @@ -70,69 +73,74 @@ class AnsibleState(object): return args, kwargs def __call__(self, **kwargs): - ''' + """ Call Ansible module. :return: - ''' + """ ret = { - 'name': kwargs.pop('name'), - 'changes': {}, - 'comment': '', - 'result': True, + "name": kwargs.pop("name"), + "changes": {}, + "comment": "", + "result": True, } for mod_name, mod_params in kwargs.items(): args, kwargs = self.get_args(mod_params) try: - ans_mod_out = __salt__['ansible.{0}'.format(mod_name)](**{'__pub_arg': [args, kwargs]}) + ans_mod_out = __salt__["ansible.{0}".format(mod_name)]( + **{"__pub_arg": [args, kwargs]} + ) except Exception as err: # pylint: disable=broad-except ans_mod_out = 'Module "{0}" failed. Error message: ({1}) {2}'.format( - mod_name, err.__class__.__name__, err) - ret['result'] = False - ret['changes'][mod_name] = ans_mod_out + mod_name, err.__class__.__name__, err + ) + ret["result"] = False + ret["changes"][mod_name] = ans_mod_out return ret def __virtual__(): - ''' + """ Disable, if Ansible is not available around on the Minion. - ''' - setattr(sys.modules[__name__], 'call', lambda **kwargs: AnsibleState()(**kwargs)) # pylint: disable=W0108 + """ + # pylint: disable=unnecessary-lambda + setattr(sys.modules[__name__], "call", lambda **kwargs: AnsibleState()(**kwargs)) + # pylint: enable=unnecessary-lambda return __virtualname__ def _client(): - ''' + """ Get a fileclient - ''' + """ return salt.fileclient.get_file_client(__opts__) def _changes(plays): - ''' + """ Find changes in ansible return data - ''' + """ changes = {} - for play in plays['plays']: + for play in plays["plays"]: task_changes = {} - for task in play['tasks']: + for task in play["tasks"]: host_changes = {} - for host, data in six.iteritems(task['hosts']): - if data['changed'] is True: - host_changes[host] = data.get('diff', data.get('changes', {})) + for host, data in six.iteritems(task["hosts"]): + if data["changed"] is True: + host_changes[host] = data.get("diff", data.get("changes", {})) if host_changes: - task_changes[task['task']['name']] = host_changes + task_changes[task["task"]["name"]] = host_changes if task_changes: - changes[play['play']['name']] = task_changes + changes[play["play"]["name"]] = task_changes return changes -@salt.utils.decorators.path.which('ansible-playbook') +@salt.utils.decorators.path.which("ansible-playbook") def playbooks(name, rundir=None, git_repo=None, git_kwargs=None, ansible_kwargs=None): - ''' + """ Run Ansible Playbooks :param name: path to playbook. This can be relative to rundir or the git repo @@ -155,42 +163,42 @@ def playbooks(name, rundir=None, git_repo=None, git_kwargs=None, ansible_kwargs= - git_repo: git://github.com/gituser/playbook.git - git_kwargs: rev: master - ''' + """ ret = { - 'result': False, - 'changes': {}, - 'comment': 'Running playbook {0}'.format(name), - 'name': name, + "result": False, + "changes": {}, + "comment": "Running playbook {0}".format(name), + "name": name, } if git_repo: if not isinstance(rundir, six.text_type) or not os.path.isdir(rundir): - rundir = _client()._extrn_path(git_repo, 'base') - log.trace('rundir set to %s', rundir) + rundir = _client()._extrn_path(git_repo, "base") + log.trace("rundir set to %s", rundir) if not isinstance(git_kwargs, dict): - log.debug('Setting git_kwargs to empty dict: %s', git_kwargs) + log.debug("Setting git_kwargs to empty dict: %s", git_kwargs) git_kwargs = {} - __states__['git.latest']( - name=git_repo, - target=rundir, - **git_kwargs - ) + __states__["git.latest"](name=git_repo, target=rundir, **git_kwargs) if not isinstance(ansible_kwargs, dict): - log.debug('Setting ansible_kwargs to empty dict: %s', ansible_kwargs) + log.debug("Setting ansible_kwargs to empty dict: %s", ansible_kwargs) ansible_kwargs = {} - checks = __salt__['ansible.playbooks'](name, rundir=rundir, check=True, diff=True, **ansible_kwargs) - if all(not check['changed'] for check in six.itervalues(checks['stats'])): - ret['comment'] = 'No changes to be made from playbook {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Changes will be made from playbook {0}'.format(name) - ret['result'] = None - ret['changes'] = _changes(checks) + checks = __salt__["ansible.playbooks"]( + name, rundir=rundir, check=True, diff=True, **ansible_kwargs + ) + if all(not check["changed"] for check in six.itervalues(checks["stats"])): + ret["comment"] = "No changes to be made from playbook {0}".format(name) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Changes will be made from playbook {0}".format(name) + ret["result"] = None + ret["changes"] = _changes(checks) else: - results = __salt__['ansible.playbooks'](name, rundir=rundir, diff=True, **ansible_kwargs) - ret['comment'] = 'Changes were made by playbook {0}'.format(name) - ret['changes'] = _changes(results) - ret['result'] = all( - not check['failures'] and not check['unreachable'] - for check in six.itervalues(checks['stats']) + results = __salt__["ansible.playbooks"]( + name, rundir=rundir, diff=True, **ansible_kwargs + ) + ret["comment"] = "Changes were made by playbook {0}".format(name) + ret["changes"] = _changes(results) + ret["result"] = all( + not check["failures"] and not check["unreachable"] + for check in six.itervalues(checks["stats"]) ) return ret diff --git a/salt/states/apache.py b/salt/states/apache.py index 54f8f61aad0..f79294743a0 100644 --- a/salt/states/apache.py +++ b/salt/states/apache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Apache state .. versionadded:: 2014.7.0 @@ -83,9 +83,9 @@ it still needs keyword ``this`` with empty string (or "\b" if nicer output is re - Else: this: "\b" do: another thing -''' +""" -from __future__ import absolute_import, with_statement, print_function, unicode_literals +from __future__ import absolute_import, print_function, unicode_literals, with_statement # Import python libs import os @@ -96,45 +96,38 @@ import salt.utils.stringutils def __virtual__(): - return 'apache.config' in __salt__ + if "apache.config" in __salt__: + return True + return (False, "apache module could not be loaded") def configfile(name, config): - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - configs = __salt__['apache.config'](name, config, edit=False) - current_configs = '' + configs = __salt__["apache.config"](name, config, edit=False) + current_configs = "" if os.path.exists(name): with salt.utils.files.fopen(name) as config_file: current_configs = salt.utils.stringutils.to_unicode(config_file.read()) if configs.strip() == current_configs.strip(): - ret['result'] = True - ret['comment'] = 'Configuration is up to date.' + ret["result"] = True + ret["comment"] = "Configuration is up to date." return ret - elif __opts__['test']: - ret['comment'] = 'Configuration will update.' - ret['changes'] = { - 'old': current_configs, - 'new': configs - } - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "Configuration will update." + ret["changes"] = {"old": current_configs, "new": configs} + ret["result"] = None return ret try: - with salt.utils.files.fopen(name, 'w') as config_file: + with salt.utils.files.fopen(name, "w") as config_file: print(salt.utils.stringutils.to_str(configs), file=config_file) - ret['changes'] = { - 'old': current_configs, - 'new': configs - } - ret['result'] = True - ret['comment'] = 'Successfully created configuration.' + ret["changes"] = {"old": current_configs, "new": configs} + ret["result"] = True + ret["comment"] = "Successfully created configuration." except Exception as exc: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = 'Failed to create apache configuration.' + ret["result"] = False + ret["comment"] = "Failed to create apache configuration." return ret diff --git a/salt/states/apache_conf.py b/salt/states/apache_conf.py index b29159006f5..00f81960f7d 100644 --- a/salt/states/apache_conf.py +++ b/salt/states/apache_conf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Apache Confs .. versionadded:: 2016.3.0 @@ -15,84 +15,86 @@ Enable and disable apache confs. Disable security conf: apache_conf.disabled: - name: security -''' +""" from __future__ import absolute_import, print_function, unicode_literals -from salt.ext import six # Import salt libs import salt.utils.path +from salt.ext import six def __virtual__(): - ''' + """ Only load if a2enconf is available. - ''' - return 'apache_conf' if 'apache.a2enconf' in __salt__ and salt.utils.path.which('a2enconf') else False + """ + if "apache.a2enconf" in __salt__ and salt.utils.path.which("a2enconf"): + return "apache_conf" + return (False, "apache module could not be loaded") def enabled(name): - ''' + """ Ensure an Apache conf is enabled. name Name of the Apache conf - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_enabled = __salt__['apache.check_conf_enabled'](name) + is_enabled = __salt__["apache.check_conf_enabled"](name) if not is_enabled: - if __opts__['test']: - msg = 'Apache conf {0} is set to be enabled.'.format(name) - ret['comment'] = msg - ret['changes']['old'] = None - ret['changes']['new'] = name - ret['result'] = None + if __opts__["test"]: + msg = "Apache conf {0} is set to be enabled.".format(name) + ret["comment"] = msg + ret["changes"]["old"] = None + ret["changes"]["new"] = name + ret["result"] = None return ret - status = __salt__['apache.a2enconf'](name)['Status'] - if isinstance(status, six.string_types) and 'enabled' in status: - ret['result'] = True - ret['changes']['old'] = None - ret['changes']['new'] = name + status = __salt__["apache.a2enconf"](name)["Status"] + if isinstance(status, six.string_types) and "enabled" in status: + ret["result"] = True + ret["changes"]["old"] = None + ret["changes"]["new"] = name else: - ret['result'] = False - ret['comment'] = 'Failed to enable {0} Apache conf'.format(name) + ret["result"] = False + ret["comment"] = "Failed to enable {0} Apache conf".format(name) if isinstance(status, six.string_types): - ret['comment'] = ret['comment'] + ' ({0})'.format(status) + ret["comment"] = ret["comment"] + " ({0})".format(status) return ret else: - ret['comment'] = '{0} already enabled.'.format(name) + ret["comment"] = "{0} already enabled.".format(name) return ret def disabled(name): - ''' + """ Ensure an Apache conf is disabled. name Name of the Apache conf - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_enabled = __salt__['apache.check_conf_enabled'](name) + is_enabled = __salt__["apache.check_conf_enabled"](name) if is_enabled: - if __opts__['test']: - msg = 'Apache conf {0} is set to be disabled.'.format(name) - ret['comment'] = msg - ret['changes']['old'] = name - ret['changes']['new'] = None - ret['result'] = None + if __opts__["test"]: + msg = "Apache conf {0} is set to be disabled.".format(name) + ret["comment"] = msg + ret["changes"]["old"] = name + ret["changes"]["new"] = None + ret["result"] = None return ret - status = __salt__['apache.a2disconf'](name)['Status'] - if isinstance(status, six.string_types) and 'disabled' in status: - ret['result'] = True - ret['changes']['old'] = name - ret['changes']['new'] = None + status = __salt__["apache.a2disconf"](name)["Status"] + if isinstance(status, six.string_types) and "disabled" in status: + ret["result"] = True + ret["changes"]["old"] = name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to disable {0} Apache conf'.format(name) + ret["result"] = False + ret["comment"] = "Failed to disable {0} Apache conf".format(name) if isinstance(status, six.string_types): - ret['comment'] = ret['comment'] + ' ({0})'.format(status) + ret["comment"] = ret["comment"] + " ({0})".format(status) return ret else: - ret['comment'] = '{0} already disabled.'.format(name) + ret["comment"] = "{0} already disabled.".format(name) return ret diff --git a/salt/states/apache_module.py b/salt/states/apache_module.py index d162fe89579..5d5c2e466a2 100644 --- a/salt/states/apache_module.py +++ b/salt/states/apache_module.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Apache Modules .. versionadded:: 2014.7.0 @@ -15,7 +15,7 @@ Enable and disable apache modules. Disable cgi module: apache_module.disabled: - name: cgi -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -25,79 +25,81 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load if a2enmod is available. - ''' - return 'apache_module' if 'apache.a2enmod' in __salt__ else False + """ + if "apache.a2enmod" in __salt__: + return "apache_module" + return (False, "apache module could not be loaded") def enabled(name): - ''' + """ Ensure an Apache module is enabled. .. versionadded:: 2016.3.0 name Name of the Apache module - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_enabled = __salt__['apache.check_mod_enabled'](name) + is_enabled = __salt__["apache.check_mod_enabled"](name) if not is_enabled: - if __opts__['test']: - msg = 'Apache module {0} is set to be enabled.'.format(name) - ret['comment'] = msg - ret['changes']['old'] = None - ret['changes']['new'] = name - ret['result'] = None + if __opts__["test"]: + msg = "Apache module {0} is set to be enabled.".format(name) + ret["comment"] = msg + ret["changes"]["old"] = None + ret["changes"]["new"] = name + ret["result"] = None return ret - status = __salt__['apache.a2enmod'](name)['Status'] - if isinstance(status, six.string_types) and 'enabled' in status: - ret['result'] = True - ret['changes']['old'] = None - ret['changes']['new'] = name + status = __salt__["apache.a2enmod"](name)["Status"] + if isinstance(status, six.string_types) and "enabled" in status: + ret["result"] = True + ret["changes"]["old"] = None + ret["changes"]["new"] = name else: - ret['result'] = False - ret['comment'] = 'Failed to enable {0} Apache module'.format(name) + ret["result"] = False + ret["comment"] = "Failed to enable {0} Apache module".format(name) if isinstance(status, six.string_types): - ret['comment'] = ret['comment'] + ' ({0})'.format(status) + ret["comment"] = ret["comment"] + " ({0})".format(status) return ret else: - ret['comment'] = '{0} already enabled.'.format(name) + ret["comment"] = "{0} already enabled.".format(name) return ret def disabled(name): - ''' + """ Ensure an Apache module is disabled. .. versionadded:: 2016.3.0 name Name of the Apache module - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_enabled = __salt__['apache.check_mod_enabled'](name) + is_enabled = __salt__["apache.check_mod_enabled"](name) if is_enabled: - if __opts__['test']: - msg = 'Apache module {0} is set to be disabled.'.format(name) - ret['comment'] = msg - ret['changes']['old'] = name - ret['changes']['new'] = None - ret['result'] = None + if __opts__["test"]: + msg = "Apache module {0} is set to be disabled.".format(name) + ret["comment"] = msg + ret["changes"]["old"] = name + ret["changes"]["new"] = None + ret["result"] = None return ret - status = __salt__['apache.a2dismod'](name)['Status'] - if isinstance(status, six.string_types) and 'disabled' in status: - ret['result'] = True - ret['changes']['old'] = name - ret['changes']['new'] = None + status = __salt__["apache.a2dismod"](name)["Status"] + if isinstance(status, six.string_types) and "disabled" in status: + ret["result"] = True + ret["changes"]["old"] = name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to disable {0} Apache module'.format(name) + ret["result"] = False + ret["comment"] = "Failed to disable {0} Apache module".format(name) if isinstance(status, six.string_types): - ret['comment'] = ret['comment'] + ' ({0})'.format(status) + ret["comment"] = ret["comment"] + " ({0})".format(status) return ret else: - ret['comment'] = '{0} already disabled.'.format(name) + ret["comment"] = "{0} already disabled.".format(name) return ret diff --git a/salt/states/apache_site.py b/salt/states/apache_site.py index 8a179c33d37..a37668128c8 100644 --- a/salt/states/apache_site.py +++ b/salt/states/apache_site.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Apache Sites .. versionadded:: 2016.3.0 @@ -15,7 +15,7 @@ Enable and disable apache sites. Disable default site: apache_site.disabled: - name: default -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -23,75 +23,77 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load if a2ensite is available. - ''' - return 'apache_site' if 'apache.a2ensite' in __salt__ else False + """ + if "apache.a2ensite" in __salt__: + return "apache_site" + return (False, "apache module could not be loaded") def enabled(name): - ''' + """ Ensure an Apache site is enabled. name Name of the Apache site - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_enabled = __salt__['apache.check_site_enabled'](name) + is_enabled = __salt__["apache.check_site_enabled"](name) if not is_enabled: - if __opts__['test']: - msg = 'Apache site {0} is set to be enabled.'.format(name) - ret['comment'] = msg - ret['changes']['old'] = None - ret['changes']['new'] = name - ret['result'] = None + if __opts__["test"]: + msg = "Apache site {0} is set to be enabled.".format(name) + ret["comment"] = msg + ret["changes"]["old"] = None + ret["changes"]["new"] = name + ret["result"] = None return ret - status = __salt__['apache.a2ensite'](name)['Status'] - if isinstance(status, six.string_types) and 'enabled' in status: - ret['result'] = True - ret['changes']['old'] = None - ret['changes']['new'] = name + status = __salt__["apache.a2ensite"](name)["Status"] + if isinstance(status, six.string_types) and "enabled" in status: + ret["result"] = True + ret["changes"]["old"] = None + ret["changes"]["new"] = name else: - ret['result'] = False - ret['comment'] = 'Failed to enable {0} Apache site'.format(name) + ret["result"] = False + ret["comment"] = "Failed to enable {0} Apache site".format(name) if isinstance(status, six.string_types): - ret['comment'] = ret['comment'] + ' ({0})'.format(status) + ret["comment"] = ret["comment"] + " ({0})".format(status) return ret else: - ret['comment'] = '{0} already enabled.'.format(name) + ret["comment"] = "{0} already enabled.".format(name) return ret def disabled(name): - ''' + """ Ensure an Apache site is disabled. name Name of the Apache site - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_enabled = __salt__['apache.check_site_enabled'](name) + is_enabled = __salt__["apache.check_site_enabled"](name) if is_enabled: - if __opts__['test']: - msg = 'Apache site {0} is set to be disabled.'.format(name) - ret['comment'] = msg - ret['changes']['old'] = name - ret['changes']['new'] = None - ret['result'] = None + if __opts__["test"]: + msg = "Apache site {0} is set to be disabled.".format(name) + ret["comment"] = msg + ret["changes"]["old"] = name + ret["changes"]["new"] = None + ret["result"] = None return ret - status = __salt__['apache.a2dissite'](name)['Status'] - if isinstance(status, six.string_types) and 'disabled' in status: - ret['result'] = True - ret['changes']['old'] = name - ret['changes']['new'] = None + status = __salt__["apache.a2dissite"](name)["Status"] + if isinstance(status, six.string_types) and "disabled" in status: + ret["result"] = True + ret["changes"]["old"] = name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to disable {0} Apache site'.format(name) + ret["result"] = False + ret["comment"] = "Failed to disable {0} Apache site".format(name) if isinstance(status, six.string_types): - ret['comment'] = ret['comment'] + ' ({0})'.format(status) + ret["comment"] = ret["comment"] + " ({0})".format(status) return ret else: - ret['comment'] = '{0} already disabled.'.format(name) + ret["comment"] = "{0} already disabled.".format(name) return ret diff --git a/salt/states/aptpkg.py b/salt/states/aptpkg.py index 5b867dc81aa..e8cbbaa3619 100644 --- a/salt/states/aptpkg.py +++ b/salt/states/aptpkg.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Package management operations specific to APT- and DEB-based systems ==================================================================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -15,44 +15,42 @@ log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'apt' +__virtualname__ = "apt" def __virtual__(): - ''' + """ Only work on apt-based platforms with pkg.get_selections - ''' - return (__virtualname__ - if __salt__.get('pkg.get_selections', False) - else False) + """ + if "pkg.get_selections" in __salt__: + return True + return (False, "apt module could not be loaded") def held(name): - ''' + """ Set package in 'hold' state, meaning it will not be upgraded. name The name of the package, e.g., 'tmux' - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} - state = __salt__['pkg.get_selections']( - pattern=name, - ) + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + state = __salt__["pkg.get_selections"](pattern=name,) if not state: - ret.update(comment='Package {0} does not have a state'.format(name)) - elif not salt.utils.data.is_true(state.get('hold', False)): - if not __opts__['test']: - result = __salt__['pkg.set_selections']( - selection={'hold': [name]} + ret.update(comment="Package {0} does not have a state".format(name)) + elif not salt.utils.data.is_true(state.get("hold", False)): + if not __opts__["test"]: + result = __salt__["pkg.set_selections"](selection={"hold": [name]}) + ret.update( + changes=result[name], + result=True, + comment="Package {0} is now being held".format(name), ) - ret.update(changes=result[name], - result=True, - comment='Package {0} is now being held'.format(name)) else: - ret.update(result=None, - comment='Package {0} is set to be held'.format(name)) + ret.update( + result=None, comment="Package {0} is set to be held".format(name) + ) else: - ret.update(result=True, - comment='Package {0} is already held'.format(name)) + ret.update(result=True, comment="Package {0} is already held".format(name)) return ret diff --git a/salt/states/archive.py b/salt/states/archive.py index 9e0bc5811e6..d02845f956d 100644 --- a/salt/states/archive.py +++ b/salt/states/archive.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Extract an archive .. versionadded:: 2014.1.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import logging import os @@ -17,11 +18,6 @@ import string import tarfile from contextlib import closing -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import shlex_quote as _cmd_quote -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=no-name-in-module - # Import Salt libs import salt.utils.args import salt.utils.files @@ -31,15 +27,20 @@ import salt.utils.platform import salt.utils.url from salt.exceptions import CommandExecutionError, CommandNotFoundError +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import shlex_quote as _cmd_quote +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse + log = logging.getLogger(__name__) def _path_is_abs(path): - ''' + """ Return a bool telling whether or ``path`` is absolute. If ``path`` is None, return ``True``. This function is designed to validate variables which optionally contain a file path. - ''' + """ if path is None: return True try: @@ -50,45 +51,44 @@ def _path_is_abs(path): def _add_explanation(ret, source_hash_trigger, contents_missing): - ''' + """ Common code to add additional explanation to the state's comment field, both when test=True and not - ''' + """ if source_hash_trigger: - ret['comment'] += ', due to source_hash update' + ret["comment"] += ", due to source_hash update" elif contents_missing: - ret['comment'] += ', due to absence of one or more files/dirs' + ret["comment"] += ", due to absence of one or more files/dirs" def _gen_checksum(path): - return {'hsum': salt.utils.hashutils.get_hash(path, form=__opts__['hash_type']), - 'hash_type': __opts__['hash_type']} + return { + "hsum": salt.utils.hashutils.get_hash(path, form=__opts__["hash_type"]), + "hash_type": __opts__["hash_type"], + } def _checksum_file_path(path): try: - relpath = '.'.join((os.path.relpath(path, __opts__['cachedir']), 'hash')) - if re.match(r'..[/\\]', relpath): + relpath = ".".join((os.path.relpath(path, __opts__["cachedir"]), "hash")) + if re.match(r"..[/\\]", relpath): # path is a local file relpath = salt.utils.path.join( - 'local', - os.path.splitdrive(path)[-1].lstrip('/\\'), + "local", os.path.splitdrive(path)[-1].lstrip("/\\"), ) except ValueError as exc: # The path is on a different drive (Windows) - if six.text_type(exc).startswith('path is on'): + if six.text_type(exc).startswith("path is on"): drive, path = os.path.splitdrive(path) relpath = salt.utils.path.join( - 'local', - drive.rstrip(':'), - path.lstrip('/\\'), + "local", drive.rstrip(":"), path.lstrip("/\\"), ) - elif str(exc).startswith('Cannot mix UNC'): - relpath = salt.utils.path.join('unc', path) + elif str(exc).startswith("Cannot mix UNC"): + relpath = salt.utils.path.join("unc", path) else: raise - ret = salt.utils.path.join(__opts__['cachedir'], 'archive_hash', relpath) - log.debug('Using checksum file %s for cached archive file %s', ret, path) + ret = salt.utils.path.join(__opts__["cachedir"], "archive_hash", relpath) + log.debug("Using checksum file %s for cached archive file %s", ret, path) return ret @@ -98,46 +98,48 @@ def _update_checksum(path): if not os.path.isdir(checksum_dir): os.makedirs(checksum_dir) source_sum = _gen_checksum(path) - hash_type = source_sum.get('hash_type') - hsum = source_sum.get('hsum') + hash_type = source_sum.get("hash_type") + hsum = source_sum.get("hsum") if hash_type and hsum: lines = [] try: try: - with salt.utils.files.fopen(checksum_file, 'r') as fp_: + with salt.utils.files.fopen(checksum_file, "r") as fp_: for line in fp_: try: - lines.append(line.rstrip('\n').split(':', 1)) + lines.append(line.rstrip("\n").split(":", 1)) except ValueError: continue except (IOError, OSError) as exc: if exc.errno != errno.ENOENT: raise - with salt.utils.files.fopen(checksum_file, 'w') as fp_: + with salt.utils.files.fopen(checksum_file, "w") as fp_: for line in lines: if line[0] == hash_type: line[1] = hsum - fp_.write('{0}:{1}\n'.format(*line)) + fp_.write("{0}:{1}\n".format(*line)) if hash_type not in [x[0] for x in lines]: - fp_.write('{0}:{1}\n'.format(hash_type, hsum)) + fp_.write("{0}:{1}\n".format(hash_type, hsum)) except (IOError, OSError) as exc: log.warning( - 'Failed to update checksum for %s: %s', - path, exc.__str__(), exc_info=True + "Failed to update checksum for %s: %s", + path, + exc.__str__(), + exc_info=True, ) def _read_cached_checksum(path, form=None): if form is None: - form = __opts__['hash_type'] + form = __opts__["hash_type"] checksum_file = _checksum_file_path(path) try: - with salt.utils.files.fopen(checksum_file, 'r') as fp_: + with salt.utils.files.fopen(checksum_file, "r") as fp_: for line in fp_: # Should only be one line in this file but just in case it # isn't, read only a single line to avoid overuse of memory. - hash_type, hsum = line.rstrip('\n').split(':', 1) + hash_type, hsum = line.rstrip("\n").split(":", 1) if hash_type == form: break else: @@ -145,57 +147,57 @@ def _read_cached_checksum(path, form=None): except (IOError, OSError, ValueError): return None else: - return {'hash_type': hash_type, 'hsum': hsum} + return {"hash_type": hash_type, "hsum": hsum} def _compare_checksum(cached, source_sum): cached_sum = _read_cached_checksum( - cached, - form=source_sum.get('hash_type', __opts__['hash_type']) + cached, form=source_sum.get("hash_type", __opts__["hash_type"]) ) return source_sum == cached_sum def _is_bsdtar(): - return 'bsdtar' in __salt__['cmd.run'](['tar', '--version'], - python_shell=False) + return "bsdtar" in __salt__["cmd.run"](["tar", "--version"], python_shell=False) def _cleanup_destdir(name): - ''' + """ Attempt to remove the specified directory - ''' + """ try: os.rmdir(name) except OSError: pass -def extracted(name, - source, - source_hash=None, - source_hash_name=None, - source_hash_update=False, - skip_files_list_verify=False, - skip_verify=False, - password=None, - options=None, - list_options=None, - force=False, - overwrite=False, - clean=False, - clean_parent=False, - user=None, - group=None, - if_missing=None, - trim_output=False, - use_cmd_unzip=None, - extract_perms=True, - enforce_toplevel=True, - enforce_ownership_on=None, - archive_format=None, - **kwargs): - ''' +def extracted( + name, + source, + source_hash=None, + source_hash_name=None, + source_hash_update=False, + skip_files_list_verify=False, + skip_verify=False, + password=None, + options=None, + list_options=None, + force=False, + overwrite=False, + clean=False, + clean_parent=False, + user=None, + group=None, + if_missing=None, + trim_output=False, + use_cmd_unzip=None, + extract_perms=True, + enforce_toplevel=True, + enforce_ownership_on=None, + archive_format=None, + **kwargs +): + """ .. versionadded:: 2014.1.0 .. versionchanged:: 2016.11.0 This state has been rewritten. Some arguments are new to this release @@ -692,39 +694,41 @@ def extracted(name, - source: https://github.com/downloads/Graylog2/graylog2-server/graylog2-server-0.9.6p1.tar.lzma - source_hash: md5=499ae16dcae71eeb7c3a30c75ea7a1a6 - source_hash_update: True - ''' - ret = {'name': name, 'result': False, 'changes': {}, 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} # Remove pub kwargs as they're irrelevant here. kwargs = salt.utils.args.clean_kwargs(**kwargs) if skip_files_list_verify and skip_verify: - ret['comment'] = ('Only one of "skip_files_list_verify" and ' - '"skip_verify" can be set to True') + ret["comment"] = ( + 'Only one of "skip_files_list_verify" and ' + '"skip_verify" can be set to True' + ) return ret - if 'keep_source' in kwargs and 'keep' in kwargs: - ret.setdefault('warnings', []).append( - 'Both \'keep_source\' and \'keep\' were used. Since these both ' - 'do the same thing, \'keep\' was ignored.' + if "keep_source" in kwargs and "keep" in kwargs: + ret.setdefault("warnings", []).append( + "Both 'keep_source' and 'keep' were used. Since these both " + "do the same thing, 'keep' was ignored." ) - keep_source = bool(kwargs.pop('keep_source')) - kwargs.pop('keep') - elif 'keep_source' in kwargs: - keep_source = bool(kwargs.pop('keep_source')) - elif 'keep' in kwargs: - keep_source = bool(kwargs.pop('keep')) + keep_source = bool(kwargs.pop("keep_source")) + kwargs.pop("keep") + elif "keep_source" in kwargs: + keep_source = bool(kwargs.pop("keep_source")) + elif "keep" in kwargs: + keep_source = bool(kwargs.pop("keep")) else: # Neither was passed, default is True keep_source = True if not _path_is_abs(name): - ret['comment'] = '{0} is not an absolute path'.format(name) + ret["comment"] = "{0} is not an absolute path".format(name) return ret else: if not name: # Empty name, like None, '' etc. - ret['comment'] = 'Name of the directory path needs to be specified' + ret["comment"] = "Name of the directory path needs to be specified" return ret # os.path.isfile() returns False when there is a trailing slash, hence # our need for first stripping the slash and then adding it back later. @@ -737,24 +741,24 @@ def extracted(name, # False name = name.rstrip(os.sep) if os.path.isfile(name): - ret['comment'] = '{0} exists and is not a directory'.format(name) + ret["comment"] = "{0} exists and is not a directory".format(name) return ret # Add back the slash so that file.makedirs properly creates the # destdir if it needs to be created. file.makedirs expects a trailing # slash in the directory path. name += os.sep if not _path_is_abs(if_missing): - ret['comment'] = 'Value for \'if_missing\' is not an absolute path' + ret["comment"] = "Value for 'if_missing' is not an absolute path" return ret if not _path_is_abs(enforce_ownership_on): - ret['comment'] = ('Value for \'enforce_ownership_on\' is not an ' - 'absolute path') + ret["comment"] = "Value for 'enforce_ownership_on' is not an " "absolute path" return ret else: if enforce_ownership_on is not None: try: - not_rel = os.path.relpath(enforce_ownership_on, - name).startswith('..' + os.sep) + not_rel = os.path.relpath(enforce_ownership_on, name).startswith( + ".." + os.sep + ) except Exception: # pylint: disable=broad-except # A ValueError is raised on Windows when the paths passed to # os.path.relpath are not on the same drive letter. Using a @@ -762,35 +766,35 @@ def extracted(name, # from making this state blow up with a traceback. not_rel = True if not_rel: - ret['comment'] = ( - 'Value for \'enforce_ownership_on\' must be within {0}' - .format(name) - ) + ret[ + "comment" + ] = "Value for 'enforce_ownership_on' must be within {0}".format(name) return ret if if_missing is not None and os.path.exists(if_missing): - ret['result'] = True - ret['comment'] = 'Path {0} exists'.format(if_missing) + ret["result"] = True + ret["comment"] = "Path {0} exists".format(if_missing) return ret if user or group: if salt.utils.platform.is_windows(): - ret['comment'] = \ - 'User/group ownership cannot be enforced on Windows minions' + ret[ + "comment" + ] = "User/group ownership cannot be enforced on Windows minions" return ret if user: - uid = __salt__['file.user_to_uid'](user) - if uid == '': - ret['comment'] = 'User {0} does not exist'.format(user) + uid = __salt__["file.user_to_uid"](user) + if uid == "": + ret["comment"] = "User {0} does not exist".format(user) return ret else: uid = -1 if group: - gid = __salt__['file.group_to_gid'](group) - if gid == '': - ret['comment'] = 'Group {0} does not exist'.format(group) + gid = __salt__["file.group_to_gid"](group) + if gid == "": + ret["comment"] = "Group {0} does not exist".format(group) return ret else: gid = -1 @@ -801,37 +805,34 @@ def extracted(name, uid = gid = -1 if source_hash_update and not source_hash: - ret.setdefault('warnings', []).append( - 'The \'source_hash_update\' argument is ignored when ' - '\'source_hash\' is not also specified.' + ret.setdefault("warnings", []).append( + "The 'source_hash_update' argument is ignored when " + "'source_hash' is not also specified." ) try: - source_match = __salt__['file.source_list'](source, - source_hash, - __env__)[0] + source_match = __salt__["file.source_list"](source, source_hash, __env__)[0] except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret if not source_match: - ret['result'] = False - ret['comment'] = 'Invalid source "{0}"'.format(source) + ret["result"] = False + ret["comment"] = 'Invalid source "{0}"'.format(source) return ret urlparsed_source = _urlparse(source_match) urlparsed_scheme = urlparsed_source.scheme urlparsed_path = os.path.join( - urlparsed_source.netloc, - urlparsed_source.path).rstrip(os.sep) + urlparsed_source.netloc, urlparsed_source.path + ).rstrip(os.sep) # urlparsed_scheme will be the drive letter if this is a Windows file path # This checks for a drive letter as the scheme and changes it to file - if urlparsed_scheme and \ - urlparsed_scheme.lower() in string.ascii_lowercase: - urlparsed_path = ':'.join([urlparsed_scheme, urlparsed_path]) - urlparsed_scheme = 'file' + if urlparsed_scheme and urlparsed_scheme.lower() in string.ascii_lowercase: + urlparsed_path = ":".join([urlparsed_scheme, urlparsed_path]) + urlparsed_scheme = "file" source_hash_basename = urlparsed_path or urlparsed_source.netloc @@ -840,18 +841,19 @@ def extracted(name, # Get rid of "file://" from start of source_match source_match = os.path.realpath(os.path.expanduser(urlparsed_path)) if not os.path.isfile(source_match): - ret['comment'] = 'Source file \'{0}\' does not exist'.format( - salt.utils.url.redact_http_basic_auth(source_match)) + ret["comment"] = "Source file '{0}' does not exist".format( + salt.utils.url.redact_http_basic_auth(source_match) + ) return ret - valid_archive_formats = ('tar', 'rar', 'zip') + valid_archive_formats = ("tar", "rar", "zip") if not archive_format: archive_format = salt.utils.files.guess_archive_type(source_hash_basename) if archive_format is None: - ret['comment'] = ( - 'Could not guess archive_format from the value of the ' - '\'source\' argument. Please set this archive_format to one ' - 'of the following: {0}'.format(', '.join(valid_archive_formats)) + ret["comment"] = ( + "Could not guess archive_format from the value of the " + "'source' argument. Please set this archive_format to one " + "of the following: {0}".format(", ".join(valid_archive_formats)) ) return ret try: @@ -859,12 +861,11 @@ def extracted(name, except AttributeError: pass if archive_format not in valid_archive_formats: - ret['comment'] = ( - 'Invalid archive_format \'{0}\'. Either set it to a supported ' - 'value ({1}) or remove this argument and the archive format will ' - 'be guesseed based on file extension.'.format( - archive_format, - ', '.join(valid_archive_formats), + ret["comment"] = ( + "Invalid archive_format '{0}'. Either set it to a supported " + "value ({1}) or remove this argument and the archive format will " + "be guessed based on file extension.".format( + archive_format, ", ".join(valid_archive_formats), ) ) return ret @@ -873,74 +874,76 @@ def extracted(name, options = six.text_type(options) strip_components = None - if options and archive_format == 'tar': + if options and archive_format == "tar": try: strip_components = int( re.search( - r'''--strip(?:-components)?(?:\s+|=)["']?(\d+)["']?''', - options + r"""--strip(?:-components)?(?:\s+|=)["']?(\d+)["']?""", options ).group(1) ) except (AttributeError, ValueError): pass - if archive_format == 'zip': + if archive_format == "zip": if options: if use_cmd_unzip is None: log.info( - 'Presence of CLI options in archive.extracted state for ' - '\'%s\' implies that use_cmd_unzip is set to True.', name + "Presence of CLI options in archive.extracted state for " + "'%s' implies that use_cmd_unzip is set to True.", + name, ) use_cmd_unzip = True elif not use_cmd_unzip: # use_cmd_unzip explicitly disabled - ret['comment'] = ( - '\'use_cmd_unzip\' cannot be set to False if CLI options ' - 'are being specified (via the \'options\' argument). ' - 'Either remove \'use_cmd_unzip\', or set it to True.' + ret["comment"] = ( + "'use_cmd_unzip' cannot be set to False if CLI options " + "are being specified (via the 'options' argument). " + "Either remove 'use_cmd_unzip', or set it to True." ) return ret if use_cmd_unzip: - if 'archive.cmd_unzip' not in __salt__: - ret['comment'] = ( - 'archive.cmd_unzip function not available, unzip might ' - 'not be installed on minion' + if "archive.cmd_unzip" not in __salt__: + ret["comment"] = ( + "archive.cmd_unzip function not available, unzip might " + "not be installed on minion" ) return ret if password: if use_cmd_unzip is None: log.info( - 'Presence of a password in archive.extracted state for ' - '\'%s\' implies that use_cmd_unzip is set to False.', name + "Presence of a password in archive.extracted state for " + "'%s' implies that use_cmd_unzip is set to False.", + name, ) use_cmd_unzip = False elif use_cmd_unzip: - ret.setdefault('warnings', []).append( - 'Using a password in combination with setting ' - '\'use_cmd_unzip\' to True is considered insecure. It is ' - 'recommended to remove the \'use_cmd_unzip\' argument (or ' - 'set it to False) and allow Salt to extract the archive ' - 'using Python\'s built-in ZIP file support.' + ret.setdefault("warnings", []).append( + "Using a password in combination with setting " + "'use_cmd_unzip' to True is considered insecure. It is " + "recommended to remove the 'use_cmd_unzip' argument (or " + "set it to False) and allow Salt to extract the archive " + "using Python's built-in ZIP file support." ) else: if password: - ret['comment'] = \ - 'The \'password\' argument is only supported for zip archives' + ret[ + "comment" + ] = "The 'password' argument is only supported for zip archives" return ret - if archive_format == 'rar': - if 'archive.unrar' not in __salt__: - ret['comment'] = ( - 'archive.unrar function not available, rar/unrar might ' - 'not be installed on minion' + if archive_format == "rar": + if "archive.unrar" not in __salt__: + ret["comment"] = ( + "archive.unrar function not available, rar/unrar might " + "not be installed on minion" ) return ret - supports_options = ('tar', 'zip') + supports_options = ("tar", "zip") if options and archive_format not in supports_options: - ret['comment'] = ( - 'The \'options\' argument is only compatible with the following ' - 'archive formats: {0}'.format(', '.join(supports_options)) + ret["comment"] = ( + "The 'options' argument is only compatible with the following " + "archive formats: {0}".format(", ".join(supports_options)) ) return ret @@ -953,21 +956,21 @@ def extracted(name, # string-ified integer. trim_output = int(trim_output) except TypeError: - ret['comment'] = ( - 'Invalid value for trim_output, must be True/False or an ' - 'integer' + ret["comment"] = ( + "Invalid value for trim_output, must be True/False or an " "integer" ) return ret if source_hash: try: - source_sum = __salt__['file.get_source_sum']( + source_sum = __salt__["file.get_source_sum"]( source=source_match, source_hash=source_hash, source_hash_name=source_hash_name, - saltenv=__env__) + saltenv=__env__, + ) except CommandExecutionError as exc: - ret['comment'] = exc.strerror + ret["comment"] = exc.strerror return ret else: source_sum = {} @@ -976,75 +979,79 @@ def extracted(name, if source_is_local: cached = source_match else: - cached = __salt__['cp.is_cached'](source_match, saltenv=__env__) + cached = __salt__["cp.is_cached"](source_match, saltenv=__env__) if cached: existing_cached_source_sum = _read_cached_checksum(cached) - log.debug('Existing source sum is: "%s". Expected source sum is "%s"', - existing_cached_source_sum, source_sum) + log.debug( + 'Existing source sum is: "%s". Expected source sum is "%s"', + existing_cached_source_sum, + source_sum, + ) if source_sum and existing_cached_source_sum: - if existing_cached_source_sum['hsum'] == source_sum['hsum']: - ret['result'] = None if __opts__['test'] else True - ret['comment'] = ( - 'Archive {0} existing source sum is the same as the ' - 'expected one and skip_files_list_verify argument was set ' - 'to True. Extraction is not needed'.format( + if existing_cached_source_sum["hsum"] == source_sum["hsum"]: + ret["result"] = None if __opts__["test"] else True + ret["comment"] = ( + "Archive {0} existing source sum is the same as the " + "expected one and skip_files_list_verify argument was set " + "to True. Extraction is not needed".format( salt.utils.url.redact_http_basic_auth(source_match) ) ) return ret else: - log.debug('There is no cached source %s available on minion', - source_match) + log.debug("There is no cached source %s available on minion", source_match) if source_is_local: cached = source_match else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ( - 'Archive {0} would be cached (if necessary) and checked to ' - 'discover if extraction is needed'.format( + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Archive {0} would be cached (if necessary) and checked to " + "discover if extraction is needed".format( salt.utils.url.redact_http_basic_auth(source_match) ) ) return ret - if 'file.cached' not in __states__: + if "file.cached" not in __states__: # Shouldn't happen unless there is a traceback keeping # salt/states/file.py from being processed through the loader. If # that is the case, we have much more important problems as _all_ # file states would be unavailable. - ret['comment'] = ( - 'Unable to cache {0}, file.cached state not available'.format( - salt.utils.url.redact_http_basic_auth(source_match) - ) + ret[ + "comment" + ] = "Unable to cache {0}, file.cached state not available".format( + salt.utils.url.redact_http_basic_auth(source_match) ) return ret try: - result = __states__['file.cached'](source_match, - source_hash=source_hash, - source_hash_name=source_hash_name, - skip_verify=skip_verify, - saltenv=__env__) + result = __states__["file.cached"]( + source_match, + source_hash=source_hash, + source_hash_name=source_hash_name, + skip_verify=skip_verify, + saltenv=__env__, + ) except Exception as exc: # pylint: disable=broad-except - msg = 'Failed to cache {0}: {1}'.format( - salt.utils.url.redact_http_basic_auth(source_match), - exc.__str__()) + msg = "Failed to cache {0}: {1}".format( + salt.utils.url.redact_http_basic_auth(source_match), exc.__str__() + ) log.exception(msg) - ret['comment'] = msg + ret["comment"] = msg return ret else: - log.debug('file.cached: %s', result) + log.debug("file.cached: %s", result) - if result['result']: + if result["result"]: # Get the path of the file in the minion cache - cached = __salt__['cp.is_cached'](source_match, saltenv=__env__) + cached = __salt__["cp.is_cached"](source_match, saltenv=__env__) else: log.debug( - 'failed to download %s', - salt.utils.url.redact_http_basic_auth(source_match) + "failed to download %s", + salt.utils.url.redact_http_basic_auth(source_match), ) return result @@ -1054,94 +1061,96 @@ def extracted(name, # Create local hash sum file if we're going to track sum update _update_checksum(cached) - if archive_format == 'zip' and not password: - log.debug('Checking %s to see if it is password-protected', - source_match) + if archive_format == "zip" and not password: + log.debug("Checking %s to see if it is password-protected", source_match) # Either use_cmd_unzip was explicitly set to True, or was # implicitly enabled by setting the "options" argument. try: - encrypted_zip = __salt__['archive.is_encrypted']( - cached, - clean=False, - saltenv=__env__) + encrypted_zip = __salt__["archive.is_encrypted"]( + cached, clean=False, saltenv=__env__ + ) except CommandExecutionError: # This would happen if archive_format=zip and the source archive is # not actually a zip file. pass else: if encrypted_zip: - ret['comment'] = ( - 'Archive {0} is password-protected, but no password was ' - 'specified. Please set the \'password\' argument.'.format( + ret["comment"] = ( + "Archive {0} is password-protected, but no password was " + "specified. Please set the 'password' argument.".format( salt.utils.url.redact_http_basic_auth(source_match) ) ) return ret try: - contents = __salt__['archive.list'](cached, - archive_format=archive_format, - options=list_options, - strip_components=strip_components, - clean=False, - verbose=True) + contents = __salt__["archive.list"]( + cached, + archive_format=archive_format, + options=list_options, + strip_components=strip_components, + clean=False, + verbose=True, + ) except CommandExecutionError as exc: contents = None errors = [] if not if_missing: - errors.append('\'if_missing\' must be set') + errors.append("'if_missing' must be set") if not enforce_ownership_on and (user or group): errors.append( - 'Ownership cannot be managed without setting ' - '\'enforce_ownership_on\'.' + "Ownership cannot be managed without setting " "'enforce_ownership_on'." ) msg = exc.strerror if errors: - msg += '\n\n' - if archive_format == 'tar': + msg += "\n\n" + if archive_format == "tar": msg += ( - 'If the source archive is a tar archive compressed using ' - 'a compression type not natively supported by the tar ' - 'command, then setting the \'list_options\' argument may ' - 'allow the contents to be listed. Otherwise, if Salt is ' - 'unable to determine the files/directories in the ' - 'archive, the following workaround(s) would need to be ' - 'used for this state to proceed' + "If the source archive is a tar archive compressed using " + "a compression type not natively supported by the tar " + "command, then setting the 'list_options' argument may " + "allow the contents to be listed. Otherwise, if Salt is " + "unable to determine the files/directories in the " + "archive, the following workaround(s) would need to be " + "used for this state to proceed" ) else: msg += ( - 'The following workarounds must be used for this state to ' - 'proceed' + "The following workarounds must be used for this state to " + "proceed" ) - msg += ( - ' (assuming the source file is a valid {0} archive):\n' - .format(archive_format) + msg += " (assuming the source file is a valid {0} archive):\n".format( + archive_format ) for error in errors: - msg += '\n- {0}'.format(error) - ret['comment'] = msg + msg += "\n- {0}".format(error) + ret["comment"] = msg return ret - if enforce_toplevel and contents is not None \ - and (len(contents['top_level_dirs']) > 1 - or len(contents['top_level_files']) > 0): - ret['comment'] = ('Archive does not have a single top-level directory. ' - 'To allow this archive to be extracted, set ' - '\'enforce_toplevel\' to False. To avoid a ' - '\'{0}-bomb\' it may also be advisable to set a ' - 'top-level directory by adding it to the \'name\' ' - 'value (for example, setting \'name\' to {1} ' - 'instead of {2}).'.format( - archive_format, - os.path.join(name, 'some_dir'), - name, - )) + if ( + enforce_toplevel + and contents is not None + and ( + len(contents["top_level_dirs"]) > 1 or len(contents["top_level_files"]) > 0 + ) + ): + ret["comment"] = ( + "Archive does not have a single top-level directory. " + "To allow this archive to be extracted, set " + "'enforce_toplevel' to False. To avoid a " + "'{0}-bomb' it may also be advisable to set a " + "top-level directory by adding it to the 'name' " + "value (for example, setting 'name' to {1} " + "instead of {2}).".format( + archive_format, os.path.join(name, "some_dir"), name, + ) + ) return ret if clean and clean_parent: - ret['comment'] = "Only one of 'clean' and 'clean_parent' can be set to True" - ret['result'] = False + ret["comment"] = "Only one of 'clean' and 'clean_parent' can be set to True" + ret["result"] = False return ret extraction_needed = overwrite @@ -1166,18 +1175,21 @@ def extracted(name, if exc.errno == errno.ENOENT: extraction_needed = True else: - ret['comment'] = ( - 'Failed to check for existence of if_missing path ' - '({0}): {1}'.format(if_missing, exc.__str__()) + ret["comment"] = ( + "Failed to check for existence of if_missing path " + "({0}): {1}".format(if_missing, exc.__str__()) ) return ret else: incorrect_type = [] - for path_list, func in \ - ((contents['dirs'], stat.S_ISDIR), - (contents['files'], lambda x: not stat.S_ISLNK(x) - and not stat.S_ISDIR(x)), - (contents['links'], stat.S_ISLNK)): + for path_list, func in ( + (contents["dirs"], stat.S_ISDIR), + ( + contents["files"], + lambda x: not stat.S_ISLNK(x) and not stat.S_ISDIR(x), + ), + (contents["links"], stat.S_ISLNK), + ): for path in path_list: full_path = salt.utils.path.join(name, path) try: @@ -1194,45 +1206,43 @@ def extracted(name, # that dir will raise an ENOTDIR OSError. So we # expect these and will only abort here if the # error code is something else. - ret['comment'] = exc.__str__() + ret["comment"] = exc.__str__() return ret if incorrect_type: - incorrect_paths = '\n\n' + '\n'.join( - ['- {0}'.format(x) for x in incorrect_type] + incorrect_paths = "\n\n" + "\n".join( + ["- {0}".format(x) for x in incorrect_type] ) - ret['comment'] = ( - 'The below paths (relative to {0}) exist, but are the ' - 'incorrect type (file instead of directory, symlink ' - 'instead of file, etc.).'.format(name) + ret["comment"] = ( + "The below paths (relative to {0}) exist, but are the " + "incorrect type (file instead of directory, symlink " + "instead of file, etc.).".format(name) ) - if __opts__['test'] and clean and contents is not None: - ret['result'] = None - ret['comment'] += ( - ' Since the \'clean\' option is enabled, the ' - 'destination paths would be cleared and the ' - 'archive would be extracted.{0}'.format( - incorrect_paths - ) + if __opts__["test"] and clean and contents is not None: + ret["result"] = None + ret["comment"] += ( + " Since the 'clean' option is enabled, the " + "destination paths would be cleared and the " + "archive would be extracted.{0}".format(incorrect_paths) ) return ret - if __opts__['test'] and clean_parent and contents is not None: - ret['result'] = None - ret['comment'] += ( - ' Since the \'clean_parent\' option is enabled, the ' - 'destination parent directory would be removed first ' - 'and then re-created and the archive would be ' - 'extracted' + if __opts__["test"] and clean_parent and contents is not None: + ret["result"] = None + ret["comment"] += ( + " Since the 'clean_parent' option is enabled, the " + "destination parent directory would be removed first " + "and then re-created and the archive would be " + "extracted" ) return ret # Skip notices of incorrect types if we're cleaning if not (clean and contents is not None): if not force: - ret['comment'] += ( - ' To proceed with extraction, set \'force\' to ' - 'True. Note that this will remove these paths ' - 'before extracting.{0}'.format(incorrect_paths) + ret["comment"] += ( + " To proceed with extraction, set 'force' to " + "True. Note that this will remove these paths " + "before extracting.{0}".format(incorrect_paths) ) return ret else: @@ -1241,28 +1251,31 @@ def extracted(name, full_path = os.path.join(name, path) try: salt.utils.files.rm_rf(full_path.rstrip(os.sep)) - ret['changes'].setdefault( - 'removed', []).append(full_path) + ret["changes"].setdefault("removed", []).append( + full_path + ) extraction_needed = True except OSError as exc: if exc.errno != errno.ENOENT: errors.append(exc.__str__()) if errors: msg = ( - 'One or more paths existed by were the incorrect ' - 'type (i.e. file instead of directory or ' - 'vice-versa), but could not be removed. The ' - 'following errors were observed:\n' + "One or more paths existed by were the incorrect " + "type (i.e. file instead of directory or " + "vice-versa), but could not be removed. The " + "following errors were observed:\n" ) for error in errors: - msg += '\n- {0}'.format(error) - ret['comment'] = msg + msg += "\n- {0}".format(error) + ret["comment"] = msg return ret - if not extraction_needed \ - and source_hash_update \ - and existing_cached_source_sum is not None \ - and not _compare_checksum(cached, existing_cached_source_sum): + if ( + not extraction_needed + and source_hash_update + and existing_cached_source_sum is not None + and not _compare_checksum(cached, existing_cached_source_sum) + ): extraction_needed = True source_hash_trigger = True else: @@ -1272,224 +1285,236 @@ def extracted(name, if extraction_needed: if source_is_local and source_hash and not skip_verify: - ret['result'] = __salt__['file.check_hash'](source_match, source_sum['hsum']) - if not ret['result']: - ret['comment'] = \ - '{0} does not match the desired source_hash {1}'.format( - salt.utils.url.redact_http_basic_auth(source_match), - source_sum['hsum'] - ) + ret["result"] = __salt__["file.check_hash"]( + source_match, source_sum["hsum"] + ) + if not ret["result"]: + ret[ + "comment" + ] = "{0} does not match the desired source_hash {1}".format( + salt.utils.url.redact_http_basic_auth(source_match), + source_sum["hsum"], + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = \ - 'Archive {0} would be extracted to {1}'.format( - salt.utils.url.redact_http_basic_auth(source_match), - name - ) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Archive {0} would be extracted to {1}".format( + salt.utils.url.redact_http_basic_auth(source_match), name + ) if clean and contents is not None: - ret['comment'] += ', after cleaning destination path(s)' + ret["comment"] += ", after cleaning destination path(s)" _add_explanation(ret, source_hash_trigger, contents_missing) return ret if clean_parent and contents is not None: errors = [] - log.debug('Removing directory %s due to clean_parent set to True', name) + log.debug("Removing directory %s due to clean_parent set to True", name) try: salt.utils.files.rm_rf(name.rstrip(os.sep)) - ret['changes'].setdefault( - 'removed', "Directory {} was removed prior to the extraction".format(name)) + ret["changes"].setdefault( + "removed", + "Directory {} was removed prior to the extraction".format(name), + ) except OSError as exc: if exc.errno != errno.ENOENT: errors.append(six.text_type(exc)) if errors: msg = ( - 'Unable to remove the directory {}. The following ' - 'errors were observed:\n'.format(name) + "Unable to remove the directory {}. The following " + "errors were observed:\n".format(name) ) for error in errors: - msg += '\n- {0}'.format(error) - ret['comment'] = msg + msg += "\n- {0}".format(error) + ret["comment"] = msg return ret if clean and contents is not None: errors = [] - log.debug('Cleaning archive paths from within %s', name) - for path in contents['top_level_dirs'] + contents['top_level_files']: + log.debug("Cleaning archive paths from within %s", name) + for path in contents["top_level_dirs"] + contents["top_level_files"]: full_path = os.path.join(name, path) try: - log.debug('Removing %s', full_path) + log.debug("Removing %s", full_path) salt.utils.files.rm_rf(full_path.rstrip(os.sep)) - ret['changes'].setdefault( - 'removed', []).append(full_path) + ret["changes"].setdefault("removed", []).append(full_path) except OSError as exc: if exc.errno != errno.ENOENT: errors.append(exc.__str__()) if errors: msg = ( - 'One or more paths could not be cleaned. The following ' - 'errors were observed:\n' + "One or more paths could not be cleaned. The following " + "errors were observed:\n" ) for error in errors: - msg += '\n- {0}'.format(error) - ret['comment'] = msg + msg += "\n- {0}".format(error) + ret["comment"] = msg return ret if not os.path.isdir(name): - __states__['file.directory'](name, user=user, makedirs=True) + __states__["file.directory"](name, user=user, makedirs=True) created_destdir = True - log.debug('Extracting %s to %s', cached, name) + log.debug("Extracting %s to %s", cached, name) try: - if archive_format == 'zip': + if archive_format == "zip": if use_cmd_unzip: try: - files = __salt__['archive.cmd_unzip']( + files = __salt__["archive.cmd_unzip"]( cached, name, options=options, trim_output=trim_output, password=password, - **kwargs) + **kwargs + ) except (CommandExecutionError, CommandNotFoundError) as exc: - ret['comment'] = exc.strerror + ret["comment"] = exc.strerror return ret else: - files = __salt__['archive.unzip'](cached, - name, - options=options, - trim_output=trim_output, - password=password, - extract_perms=extract_perms, - **kwargs) - elif archive_format == 'rar': + files = __salt__["archive.unzip"]( + cached, + name, + options=options, + trim_output=trim_output, + password=password, + extract_perms=extract_perms, + **kwargs + ) + elif archive_format == "rar": try: - files = __salt__['archive.unrar'](cached, - name, - trim_output=trim_output, - **kwargs) + files = __salt__["archive.unrar"]( + cached, name, trim_output=trim_output, **kwargs + ) except (CommandExecutionError, CommandNotFoundError) as exc: - ret['comment'] = exc.strerror + ret["comment"] = exc.strerror return ret else: if options is None: try: - with closing(tarfile.open(cached, 'r')) as tar: + with closing(tarfile.open(cached, "r")) as tar: tar.extractall(salt.utils.stringutils.to_str(name)) files = tar.getnames() if trim_output: files = files[:trim_output] except tarfile.ReadError: - if salt.utils.path.which('xz'): - if __salt__['cmd.retcode']( - ['xz', '-t', cached], + if salt.utils.path.which("xz"): + if ( + __salt__["cmd.retcode"]( + ["xz", "-t", cached], python_shell=False, - ignore_retcode=True) == 0: + ignore_retcode=True, + ) + == 0 + ): # XZ-compressed data log.debug( - 'Tar file is XZ-compressed, attempting ' - 'decompression and extraction using XZ Utils ' - 'and the tar command' + "Tar file is XZ-compressed, attempting " + "decompression and extraction using XZ Utils " + "and the tar command" ) # Must use python_shell=True here because not # all tar implementations support the -J flag # for decompressing XZ-compressed data. We need # to dump the decompressed data to stdout and # pipe it to tar for extraction. - cmd = 'xz --decompress --stdout {0} | tar xvf -' - results = __salt__['cmd.run_all']( + cmd = "xz --decompress --stdout {0} | tar xvf -" + results = __salt__["cmd.run_all"]( cmd.format(_cmd_quote(cached)), cwd=name, - python_shell=True) - if results['retcode'] != 0: + python_shell=True, + ) + if results["retcode"] != 0: if created_destdir: _cleanup_destdir(name) - ret['result'] = False - ret['changes'] = results + ret["result"] = False + ret["changes"] = results return ret if _is_bsdtar(): - files = results['stderr'] + files = results["stderr"] else: - files = results['stdout'] + files = results["stdout"] else: # Failed to open tar archive and it is not # XZ-compressed, gracefully fail the state if created_destdir: _cleanup_destdir(name) - ret['result'] = False - ret['comment'] = ( - 'Failed to read from tar archive using ' - 'Python\'s native tar file support. If ' - 'archive is compressed using something ' - 'other than gzip or bzip2, the ' - '\'options\' argument may be required to ' - 'pass the correct options to the tar ' - 'command in order to extract the archive.' + ret["result"] = False + ret["comment"] = ( + "Failed to read from tar archive using " + "Python's native tar file support. If " + "archive is compressed using something " + "other than gzip or bzip2, the " + "'options' argument may be required to " + "pass the correct options to the tar " + "command in order to extract the archive." ) return ret else: if created_destdir: _cleanup_destdir(name) - ret['result'] = False - ret['comment'] = ( - 'Failed to read from tar archive. If it is ' - 'XZ-compressed, install xz-utils to attempt ' - 'extraction.' + ret["result"] = False + ret["comment"] = ( + "Failed to read from tar archive. If it is " + "XZ-compressed, install xz-utils to attempt " + "extraction." ) return ret else: - if not salt.utils.path.which('tar'): - ret['comment'] = ( - 'tar command not available, it might not be ' - 'installed on minion' + if not salt.utils.path.which("tar"): + ret["comment"] = ( + "tar command not available, it might not be " + "installed on minion" ) return ret # Ignore verbose file list options as we are already using # "v" below in tar_shortopts - tar_opts = [x for x in shlex.split(options) - if x not in ('v', '-v', '--verbose')] + tar_opts = [ + x + for x in shlex.split(options) + if x not in ("v", "-v", "--verbose") + ] - tar_cmd = ['tar'] - tar_shortopts = 'xv' + tar_cmd = ["tar"] + tar_shortopts = "xv" tar_longopts = [] for position, opt in enumerate(tar_opts): - if opt.startswith('-'): + if opt.startswith("-"): tar_longopts.append(opt) else: if position > 0: tar_longopts.append(opt) else: append_opt = opt - append_opt = append_opt.replace('x', '') - append_opt = append_opt.replace('f', '') + append_opt = append_opt.replace("x", "") + append_opt = append_opt.replace("f", "") tar_shortopts = tar_shortopts + append_opt - if __grains__['os'].lower() == 'openbsd': - tar_shortopts = '-' + tar_shortopts + if __grains__["os"].lower() == "openbsd": + tar_shortopts = "-" + tar_shortopts tar_cmd.append(tar_shortopts) tar_cmd.extend(tar_longopts) - tar_cmd.extend(['-f', cached]) + tar_cmd.extend(["-f", cached]) - results = __salt__['cmd.run_all'](tar_cmd, - cwd=name, - python_shell=False) - if results['retcode'] != 0: - ret['result'] = False - ret['changes'] = results + results = __salt__["cmd.run_all"]( + tar_cmd, cwd=name, python_shell=False + ) + if results["retcode"] != 0: + ret["result"] = False + ret["changes"] = results return ret if _is_bsdtar(): - files = results['stderr'].splitlines() + files = results["stderr"].splitlines() else: - files = results['stdout'].splitlines() + files = results["stdout"].splitlines() if not files: - files = 'no tar output so far' + files = "no tar output so far" except CommandExecutionError as exc: - ret['comment'] = exc.strerror + ret["comment"] = exc.strerror return ret # Recursively set user and group ownership of files @@ -1507,47 +1532,47 @@ def extracted(name, enforce_links = [] else: if contents is not None: - enforce_dirs = contents['top_level_dirs'] - enforce_files = contents['top_level_files'] - enforce_links = contents['top_level_links'] + enforce_dirs = contents["top_level_dirs"] + enforce_files = contents["top_level_files"] + enforce_links = contents["top_level_links"] recurse = [] if user: - recurse.append('user') + recurse.append("user") if group: - recurse.append('group') - recurse_str = ', '.join(recurse) + recurse.append("group") + recurse_str = ", ".join(recurse) - owner_changes = dict([ - (x, y) for x, y in (('user', user), ('group', group)) if y - ]) + owner_changes = dict( + [(x, y) for x, y in (("user", user), ("group", group)) if y] + ) for dirname in enforce_dirs: full_path = os.path.join(name, dirname) if not os.path.isdir(full_path): - if not __opts__['test']: + if not __opts__["test"]: enforce_missing.append(full_path) else: log.debug( - 'Enforcing %s ownership on %s using a file.directory state%s', + "Enforcing %s ownership on %s using a file.directory state%s", recurse_str, dirname, - ' (dry-run only)' if __opts__['test'] else '' + " (dry-run only)" if __opts__["test"] else "", ) - dir_result = __states__['file.directory'](full_path, - user=user, - group=group, - recurse=recurse) - log.debug('file.directory: %s', dir_result) + dir_result = __states__["file.directory"]( + full_path, user=user, group=group, recurse=recurse + ) + log.debug("file.directory: %s", dir_result) - if dir_result.get('changes'): - ret['changes']['updated ownership'] = True + if dir_result.get("changes"): + ret["changes"]["updated ownership"] = True try: - if not dir_result['result']: + if not dir_result["result"]: enforce_failed.append(full_path) except (KeyError, TypeError): log.warning( - 'Bad state return %s for file.directory state on %s', - dir_result, dirname + "Bad state return %s for file.directory state on %s", + dir_result, + dirname, ) for filename in enforce_files + enforce_links: @@ -1559,7 +1584,7 @@ def extracted(name, # things up a bit. file_stat = os.lstat(full_path) except OSError as exc: - if not __opts__['test']: + if not __opts__["test"]: if exc.errno == errno.ENOENT: enforce_missing.append(full_path) enforce_failed.append(full_path) @@ -1570,46 +1595,45 @@ def extracted(name, # can selectively compare the uid/gid from the values in # file_stat, _only if_ the "desired" uid/gid is something other # than -1. - if (uid != -1 and uid != file_stat.st_uid) \ - or (gid != -1 and gid != file_stat.st_gid): - if __opts__['test']: - ret['changes']['updated ownership'] = True + if (uid != -1 and uid != file_stat.st_uid) or ( + gid != -1 and gid != file_stat.st_gid + ): + if __opts__["test"]: + ret["changes"]["updated ownership"] = True else: try: os.lchown(full_path, uid, gid) - ret['changes']['updated ownership'] = True + ret["changes"]["updated ownership"] = True except OSError: enforce_failed.append(filename) if extraction_needed: if len(files) > 0: if created_destdir: - ret['changes']['directories_created'] = [name] - ret['changes']['extracted_files'] = files - ret['comment'] = '{0} extracted to {1}'.format( - salt.utils.url.redact_http_basic_auth(source_match), - name, + ret["changes"]["directories_created"] = [name] + ret["changes"]["extracted_files"] = files + ret["comment"] = "{0} extracted to {1}".format( + salt.utils.url.redact_http_basic_auth(source_match), name, ) _add_explanation(ret, source_hash_trigger, contents_missing) - ret['result'] = True + ret["result"] = True else: - ret['result'] = False - ret['comment'] = 'No files were extracted from {0}'.format( + ret["result"] = False + ret["comment"] = "No files were extracted from {0}".format( salt.utils.url.redact_http_basic_auth(source_match) ) else: - ret['result'] = True + ret["result"] = True if if_missing_path_exists: - ret['comment'] = '{0} exists'.format(if_missing) + ret["comment"] = "{0} exists".format(if_missing) else: - ret['comment'] = 'All files in archive are already present' - if __opts__['test']: - if ret['changes'].get('updated ownership'): - ret['result'] = None - ret['comment'] += ( - '. Ownership would be updated on one or more ' - 'files/directories.' + ret["comment"] = "All files in archive are already present" + if __opts__["test"]: + if ret["changes"].get("updated ownership"): + ret["result"] = None + ret["comment"] += ( + ". Ownership would be updated on one or more " "files/directories." ) if enforce_missing: @@ -1621,32 +1645,32 @@ def extracted(name, # semaphore to conditionally extract, we don't want to make this a # case where the state fails, so we only fail the state if # is_missing is not used. - ret['result'] = False - ret['comment'] += ( - '\n\nWhile trying to enforce user/group ownership, the following ' - 'paths were missing:\n' + ret["result"] = False + ret["comment"] += ( + "\n\nWhile trying to enforce user/group ownership, the following " + "paths were missing:\n" ) for item in enforce_missing: - ret['comment'] += '\n- {0}'.format(item) + ret["comment"] += "\n- {0}".format(item) if enforce_failed: - ret['result'] = False - ret['comment'] += ( - '\n\nWhile trying to enforce user/group ownership, Salt was ' - 'unable to change ownership on the following paths:\n' + ret["result"] = False + ret["comment"] += ( + "\n\nWhile trying to enforce user/group ownership, Salt was " + "unable to change ownership on the following paths:\n" ) for item in enforce_failed: - ret['comment'] += '\n- {0}'.format(item) + ret["comment"] += "\n- {0}".format(item) if not source_is_local: if keep_source: - log.debug('Keeping cached source file %s', cached) + log.debug("Keeping cached source file %s", cached) else: - log.debug('Cleaning cached source file %s', cached) - result = __states__['file.not_cached'](source_match, saltenv=__env__) - if not result['result']: + log.debug("Cleaning cached source file %s", cached) + result = __states__["file.not_cached"](source_match, saltenv=__env__) + if not result["result"]: # Don't let failure to delete cached file cause the state # itself to fail, just drop it in the warnings. - ret.setdefault('warnings', []).append(result['comment']) + ret.setdefault("warnings", []).append(result["comment"]) return ret diff --git a/salt/states/artifactory.py b/salt/states/artifactory.py index 4c9e432244e..d9e7c2e97e3 100644 --- a/salt/states/artifactory.py +++ b/salt/states/artifactory.py @@ -1,19 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" This state downloads artifacts from artifactory. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging + from salt.ext import six log = logging.getLogger(__name__) -def downloaded(name, artifact, target_dir='/tmp', target_file=None, use_literal_group_id=False): - ''' +def downloaded( + name, artifact, target_dir="/tmp", target_file=None, use_literal_group_id=False +): + """ Ensures that the artifact from artifactory exists at given location. If it doesn't exist, then it will be downloaded. If it already exists then the checksum of existing file is checked against checksum in artifactory. If it is different then the step will fail. @@ -77,85 +81,98 @@ def downloaded(name, artifact, target_dir='/tmp', target_file=None, use_literal_ version: '1.0' - target_dir: /opt/jboss7/modules/com/company/lib - ''' - log.debug(" ======================== STATE: artifactory.downloaded (name: %s) ", name) - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + log.debug( + " ======================== STATE: artifactory.downloaded (name: %s) ", name + ) + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - if 'test' in __opts__ and __opts__['test'] is True: + if "test" in __opts__ and __opts__["test"] is True: fetch_result = {} - fetch_result['status'] = True - fetch_result['comment'] = 'Artifact would be downloaded from URL: {0}'.format(artifact['artifactory_url']) - fetch_result['changes'] = {} + fetch_result["status"] = True + fetch_result["comment"] = "Artifact would be downloaded from URL: {0}".format( + artifact["artifactory_url"] + ) + fetch_result["changes"] = {} else: try: - fetch_result = __fetch_from_artifactory(artifact, target_dir, target_file, use_literal_group_id) + fetch_result = __fetch_from_artifactory( + artifact, target_dir, target_file, use_literal_group_id + ) except Exception as exc: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(exc) + ret["result"] = False + ret["comment"] = six.text_type(exc) return ret - log.debug('fetch_result = %s', fetch_result) + log.debug("fetch_result = %s", fetch_result) - ret['result'] = fetch_result['status'] - ret['comment'] = fetch_result['comment'] - ret['changes'] = fetch_result['changes'] - log.debug('ret = %s', ret) + ret["result"] = fetch_result["status"] + ret["comment"] = fetch_result["comment"] + ret["changes"] = fetch_result["changes"] + log.debug("ret = %s", ret) return ret def __fetch_from_artifactory(artifact, target_dir, target_file, use_literal_group_id): - if ('latest_snapshot' in artifact and artifact['latest_snapshot']) or artifact['version'] == 'latest_snapshot': - fetch_result = __salt__['artifactory.get_latest_snapshot'](artifactory_url=artifact['artifactory_url'], - repository=artifact['repository'], - group_id=artifact['group_id'], - artifact_id=artifact['artifact_id'], - packaging=artifact['packaging'] if 'packaging' in artifact else 'jar', - classifier=artifact['classifier'] if 'classifier' in artifact else None, - target_dir=target_dir, - target_file=target_file, - username=artifact['username'] if 'username' in artifact else None, - password=artifact['password'] if 'password' in artifact else None, - use_literal_group_id=use_literal_group_id) - elif artifact['version'].endswith('SNAPSHOT'): - fetch_result = __salt__['artifactory.get_snapshot'](artifactory_url=artifact['artifactory_url'], - repository=artifact['repository'], - group_id=artifact['group_id'], - artifact_id=artifact['artifact_id'], - packaging=artifact['packaging'] if 'packaging' in artifact else 'jar', - classifier=artifact['classifier'] if 'classifier' in artifact else None, - version=artifact['version'], - target_dir=target_dir, - target_file=target_file, - username=artifact['username'] if 'username' in artifact else None, - password=artifact['password'] if 'password' in artifact else None, - use_literal_group_id=use_literal_group_id) - elif artifact['version'] == 'latest': - fetch_result = __salt__['artifactory.get_latest_release'](artifactory_url=artifact['artifactory_url'], - repository=artifact['repository'], - group_id=artifact['group_id'], - artifact_id=artifact['artifact_id'], - packaging=artifact['packaging'] if 'packaging' in artifact else 'jar', - classifier=artifact['classifier'] if 'classifier' in artifact else None, - target_dir=target_dir, - target_file=target_file, - username=artifact['username'] if 'username' in artifact else None, - password=artifact['password'] if 'password' in artifact else None, - use_literal_group_id=use_literal_group_id) + if ("latest_snapshot" in artifact and artifact["latest_snapshot"]) or artifact[ + "version" + ] == "latest_snapshot": + fetch_result = __salt__["artifactory.get_latest_snapshot"]( + artifactory_url=artifact["artifactory_url"], + repository=artifact["repository"], + group_id=artifact["group_id"], + artifact_id=artifact["artifact_id"], + packaging=artifact["packaging"] if "packaging" in artifact else "jar", + classifier=artifact["classifier"] if "classifier" in artifact else None, + target_dir=target_dir, + target_file=target_file, + username=artifact["username"] if "username" in artifact else None, + password=artifact["password"] if "password" in artifact else None, + use_literal_group_id=use_literal_group_id, + ) + elif artifact["version"].endswith("SNAPSHOT"): + fetch_result = __salt__["artifactory.get_snapshot"]( + artifactory_url=artifact["artifactory_url"], + repository=artifact["repository"], + group_id=artifact["group_id"], + artifact_id=artifact["artifact_id"], + packaging=artifact["packaging"] if "packaging" in artifact else "jar", + classifier=artifact["classifier"] if "classifier" in artifact else None, + version=artifact["version"], + target_dir=target_dir, + target_file=target_file, + username=artifact["username"] if "username" in artifact else None, + password=artifact["password"] if "password" in artifact else None, + use_literal_group_id=use_literal_group_id, + ) + elif artifact["version"] == "latest": + fetch_result = __salt__["artifactory.get_latest_release"]( + artifactory_url=artifact["artifactory_url"], + repository=artifact["repository"], + group_id=artifact["group_id"], + artifact_id=artifact["artifact_id"], + packaging=artifact["packaging"] if "packaging" in artifact else "jar", + classifier=artifact["classifier"] if "classifier" in artifact else None, + target_dir=target_dir, + target_file=target_file, + username=artifact["username"] if "username" in artifact else None, + password=artifact["password"] if "password" in artifact else None, + use_literal_group_id=use_literal_group_id, + ) else: - fetch_result = __salt__['artifactory.get_release'](artifactory_url=artifact['artifactory_url'], - repository=artifact['repository'], - group_id=artifact['group_id'], - artifact_id=artifact['artifact_id'], - packaging=artifact['packaging'] if 'packaging' in artifact else 'jar', - classifier=artifact['classifier'] if 'classifier' in artifact else None, - version=artifact['version'], - target_dir=target_dir, - target_file=target_file, - username=artifact['username'] if 'username' in artifact else None, - password=artifact['password'] if 'password' in artifact else None, - use_literal_group_id=use_literal_group_id) + fetch_result = __salt__["artifactory.get_release"]( + artifactory_url=artifact["artifactory_url"], + repository=artifact["repository"], + group_id=artifact["group_id"], + artifact_id=artifact["artifact_id"], + packaging=artifact["packaging"] if "packaging" in artifact else "jar", + classifier=artifact["classifier"] if "classifier" in artifact else None, + version=artifact["version"], + target_dir=target_dir, + target_file=target_file, + username=artifact["username"] if "username" in artifact else None, + password=artifact["password"] if "password" in artifact else None, + use_literal_group_id=use_literal_group_id, + ) return fetch_result diff --git a/salt/states/at.py b/salt/states/at.py index 67a4df352b4..dae9ce37633 100644 --- a/salt/states/at.py +++ b/salt/states/at.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Configuration disposable regularly scheduled tasks for at. ========================================================== The at state can be add disposable regularly scheduled tasks for your system. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -17,14 +17,16 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Most everything has the ability to support at(1) - ''' - return 'at.at' in __salt__ + """ + if "at.at" in __salt__: + return True + return (False, "at module could not be loaded") def present(name, timespec, tag=None, user=None, job=None, unique_tag=False): - ''' + """ .. versionchanged:: 2017.7.0 Add a job to queue. @@ -55,78 +57,56 @@ def present(name, timespec, tag=None, user=None, job=None, unique_tag=False): - tag: love - user: jam - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # if job is missing, use name if not job: job = name # quick return on test=True - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'job {0} added and will run on {1}'.format( - job, - timespec, - ) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "job {0} added and will run on {1}".format(job, timespec,) return ret # quick return if unique_tag and job exists if unique_tag: if not tag: - ret['result'] = False - ret['comment'] = 'no tag provided and unique_tag is set to True' + ret["result"] = False + ret["comment"] = "no tag provided and unique_tag is set to True" return ret - elif len(__salt__['at.jobcheck'](tag=tag)['jobs']) > 0: - ret['comment'] = 'atleast one job with tag {tag} exists.'.format( - tag=tag - ) + elif len(__salt__["at.jobcheck"](tag=tag)["jobs"]) > 0: + ret["comment"] = "atleast one job with tag {tag} exists.".format(tag=tag) return ret # create job if user: - luser = __salt__['user.info'](user) + luser = __salt__["user.info"](user) if not luser: - ret['result'] = False - ret['comment'] = 'user {0} does not exists'.format(user) + ret["result"] = False + ret["comment"] = "user {0} does not exists".format(user) return ret - ret['comment'] = 'job {0} added and will run as {1} on {2}'.format( - job, - user, - timespec, - ) - res = __salt__['at.at']( - timespec, - job, - tag=tag, - runas=user, + ret["comment"] = "job {0} added and will run as {1} on {2}".format( + job, user, timespec, ) + res = __salt__["at.at"](timespec, job, tag=tag, runas=user,) else: - ret['comment'] = 'job {0} added and will run on {1}'.format( - job, - timespec, - ) - res = __salt__['at.at']( - timespec, - job, - tag=tag, - ) + ret["comment"] = "job {0} added and will run on {1}".format(job, timespec,) + res = __salt__["at.at"](timespec, job, tag=tag,) # set ret['changes'] - if 'jobs' in res and len(res['jobs']) > 0: - ret['changes'] = res['jobs'][0] - if 'error' in res: - ret['result'] = False - ret['comment'] = res['error'] + if "jobs" in res and len(res["jobs"]) > 0: + ret["changes"] = res["jobs"][0] + if "error" in res: + ret["result"] = False + ret["comment"] = res["error"] return ret def absent(name, jobid=None, **kwargs): - ''' + """ .. versionchanged:: 2017.7.0 Remove a job from queue @@ -180,50 +160,47 @@ def absent(name, jobid=None, **kwargs): .. note: all other filters are ignored and only job with id 4 is removed - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # limit was never support - if 'limit' in kwargs: - ret['comment'] = 'limit parameter not supported {0}'.format(name) - ret['result'] = False + if "limit" in kwargs: + ret["comment"] = "limit parameter not supported {0}".format(name) + ret["result"] = False return ret # quick return on test=True - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'removed ? job(s)' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "removed ? job(s)" return ret # remove specific job if jobid: - jobs = __salt__['at.atq'](jobid) - if 'jobs' in jobs and len(jobs['jobs']) == 0: - ret['result'] = True - ret['comment'] = 'job with id {jobid} not present'.format( - jobid=jobid - ) + jobs = __salt__["at.atq"](jobid) + if "jobs" in jobs and len(jobs["jobs"]) == 0: + ret["result"] = True + ret["comment"] = "job with id {jobid} not present".format(jobid=jobid) return ret - elif 'jobs' in jobs and len(jobs['jobs']) == 1: - if 'job' in jobs['jobs'][0] and jobs['jobs'][0]['job']: - res = __salt__['at.atrm'](jobid) - ret['result'] = jobid in res['jobs']['removed'] - if ret['result']: - ret['comment'] = 'job with id {jobid} was removed'.format( + elif "jobs" in jobs and len(jobs["jobs"]) == 1: + if "job" in jobs["jobs"][0] and jobs["jobs"][0]["job"]: + res = __salt__["at.atrm"](jobid) + ret["result"] = jobid in res["jobs"]["removed"] + if ret["result"]: + ret["comment"] = "job with id {jobid} was removed".format( jobid=jobid ) else: - ret['comment'] = 'failed to remove job with id {jobid}'.format( + ret["comment"] = "failed to remove job with id {jobid}".format( jobid=jobid ) - ret['changes']['removed'] = res['jobs']['removed'] + ret["changes"]["removed"] = res["jobs"]["removed"] return ret else: - ret['result'] = False - ret['comment'] = 'more than one job was return for job with id {jobid}'.format( + ret["result"] = False + ret[ + "comment" + ] = "more than one job was return for job with id {jobid}".format( jobid=jobid ) return ret @@ -231,22 +208,24 @@ def absent(name, jobid=None, **kwargs): # remove jobs based on filter if kwargs: # we pass kwargs to at.jobcheck - opts = list(list(map(str, [j['job'] for j in __salt__['at.jobcheck'](**kwargs)['jobs']]))) - res = __salt__['at.atrm'](*opts) + opts = list( + list( + map(str, [j["job"] for j in __salt__["at.jobcheck"](**kwargs)["jobs"]]) + ) + ) + res = __salt__["at.atrm"](*opts) else: # arguments to filter with, removing everything! - res = __salt__['at.atrm']('all') + res = __salt__["at.atrm"]("all") - if len(res['jobs']['removed']) > 0: - ret['changes']['removed'] = res['jobs']['removed'] - ret['comment'] = 'removed {count} job(s)'.format( - count=len(res['jobs']['removed']) - ) + if len(res["jobs"]["removed"]) > 0: + ret["changes"]["removed"] = res["jobs"]["removed"] + ret["comment"] = "removed {count} job(s)".format(count=len(res["jobs"]["removed"])) return ret def watch(name, timespec, tag=None, user=None, job=None, unique_tag=False): - ''' + """ .. versionadded:: 2017.7.0 Add an at job if trigger by watch @@ -279,17 +258,12 @@ def watch(name, timespec, tag=None, user=None, job=None, unique_tag=False): - watch: - file: /etc/salt/minion - ''' - return { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + """ + return {"name": name, "changes": {}, "result": True, "comment": ""} def mod_watch(name, **kwargs): - ''' + """ The at watcher, called to invoke the watch command. .. note:: @@ -301,16 +275,13 @@ def mod_watch(name, **kwargs): name The name of the atjob - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if kwargs['sfun'] == 'watch': - for p in ['sfun', '__reqs__']: + if kwargs["sfun"] == "watch": + for p in ["sfun", "__reqs__"]: del kwargs[p] - kwargs['name'] = name + kwargs["name"] = name ret = present(**kwargs) return ret diff --git a/salt/states/augeas.py b/salt/states/augeas.py index ce45d65ee89..8ae61b04573 100644 --- a/salt/states/augeas.py +++ b/salt/states/augeas.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configuration management using Augeas .. versionadded:: 0.17.0 @@ -26,36 +26,38 @@ Augeas_ can be used to manage configuration files. For affected Debian/Ubuntu hosts, installing ``libpython2.7`` has been known to resolve the issue. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import difflib +import logging +import os.path + # Import python libs import re -import os.path -import logging -import difflib # Import Salt libs import salt.utils.args import salt.utils.files import salt.utils.stringutils from salt.ext import six - from salt.modules.augeas_cfg import METHOD_MAP log = logging.getLogger(__name__) def __virtual__(): - return 'augeas' if 'augeas.execute' in __salt__ else False + if "augeas.execute" in __salt__: + return "augeas" + return (False, "augeas module could not be loaded") def _workout_filename(filename): - ''' + """ Recursively workout the file name from an augeas change - ''' - if os.path.isfile(filename) or filename == '/': - if filename == '/': + """ + if os.path.isfile(filename) or filename == "/": + if filename == "/": filename = None return filename else: @@ -63,44 +65,49 @@ def _workout_filename(filename): def _check_filepath(changes): - ''' + """ Ensure all changes are fully qualified and affect only one file. This ensures that the diff output works and a state change is not incorrectly reported. - ''' + """ filename = None for change_ in changes: try: - cmd, arg = change_.split(' ', 1) + cmd, arg = change_.split(" ", 1) if cmd not in METHOD_MAP: - error = 'Command {0} is not supported (yet)'.format(cmd) + error = "Command {0} is not supported (yet)".format(cmd) raise ValueError(error) method = METHOD_MAP[cmd] parts = salt.utils.args.shlex_split(arg) - if method in ['set', 'setm', 'move', 'remove']: + if method in ["set", "setm", "move", "remove"]: filename_ = parts[0] else: _, _, filename_ = parts - if not filename_.startswith('/files'): - error = 'Changes should be prefixed with ' \ - '/files if no context is provided,' \ - ' change: {0}'.format(change_) + if not filename_.startswith("/files"): + error = ( + "Changes should be prefixed with " + "/files if no context is provided," + " change: {0}".format(change_) + ) raise ValueError(error) - filename_ = re.sub('^/files|/$', '', filename_) + filename_ = re.sub("^/files|/$", "", filename_) if filename is not None: if filename != filename_: - error = 'Changes should be made to one ' \ - 'file at a time, detected changes ' \ - 'to {0} and {1}'.format(filename, filename_) + error = ( + "Changes should be made to one " + "file at a time, detected changes " + "to {0} and {1}".format(filename, filename_) + ) raise ValueError(error) filename = filename_ except (ValueError, IndexError) as err: log.error(err) - if 'error' not in locals(): - error = 'Invalid formatted command, ' \ - 'see debug log for details: {0}' \ - .format(change_) + if "error" not in locals(): + error = ( + "Invalid formatted command, " + "see debug log for details: {0}".format(change_) + ) else: error = six.text_type(err) raise ValueError(error) @@ -110,9 +117,8 @@ def _check_filepath(changes): return filename -def change(name, context=None, changes=None, lens=None, - load_path=None, **kwargs): - ''' +def change(name, context=None, changes=None, lens=None, load_path=None, **kwargs): + """ .. versionadded:: 2014.7.0 This state replaces :py:func:`~salt.states.augeas.setvalue`. @@ -256,68 +262,65 @@ def change(name, context=None, changes=None, lens=None, http://augeas.net/docs/references/lenses/files/services-aug.html#Services.record - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not changes or not isinstance(changes, list): - ret['comment'] = '\'changes\' must be specified as a list' + ret["comment"] = "'changes' must be specified as a list" return ret if load_path is not None: if not isinstance(load_path, list): - ret['comment'] = '\'load_path\' must be specified as a list' + ret["comment"] = "'load_path' must be specified as a list" return ret else: - load_path = ':'.join(load_path) + load_path = ":".join(load_path) filename = None if context is None: try: filename = _check_filepath(changes) except ValueError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret else: - filename = re.sub('^/files|/$', '', context) + filename = re.sub("^/files|/$", "", context) - if __opts__['test']: - ret['result'] = True - ret['comment'] = 'Executing commands' + if __opts__["test"]: + ret["result"] = True + ret["comment"] = "Executing commands" if context: - ret['comment'] += ' in file "{0}":\n'.format(context) - ret['comment'] += "\n".join(changes) + ret["comment"] += ' in file "{0}":\n'.format(context) + ret["comment"] += "\n".join(changes) return ret old_file = [] if filename is not None and os.path.isfile(filename): - with salt.utils.files.fopen(filename, 'r') as file_: - old_file = [salt.utils.stringutils.to_unicode(x) - for x in file_.readlines()] + with salt.utils.files.fopen(filename, "r") as file_: + old_file = [salt.utils.stringutils.to_unicode(x) for x in file_.readlines()] - result = __salt__['augeas.execute']( - context=context, lens=lens, - commands=changes, load_path=load_path) - ret['result'] = result['retval'] + result = __salt__["augeas.execute"]( + context=context, lens=lens, commands=changes, load_path=load_path + ) + ret["result"] = result["retval"] - if ret['result'] is False: - ret['comment'] = 'Error: {0}'.format(result['error']) + if ret["result"] is False: + ret["comment"] = "Error: {0}".format(result["error"]) return ret if filename is not None and os.path.isfile(filename): - with salt.utils.files.fopen(filename, 'r') as file_: - new_file = [salt.utils.stringutils.to_unicode(x) - for x in file_.readlines()] - diff = ''.join( - difflib.unified_diff(old_file, new_file, n=0)) + with salt.utils.files.fopen(filename, "r") as file_: + new_file = [salt.utils.stringutils.to_unicode(x) for x in file_.readlines()] + diff = "".join(difflib.unified_diff(old_file, new_file, n=0)) if diff: - ret['comment'] = 'Changes have been saved' - ret['changes'] = {'diff': diff} + ret["comment"] = "Changes have been saved" + ret["changes"] = {"diff": diff} else: - ret['comment'] = 'No changes made' + ret["comment"] = "No changes made" else: - ret['comment'] = 'Changes have been saved' - ret['changes'] = {'updates': changes} + ret["comment"] = "Changes have been saved" + ret["changes"] = {"updates": changes} return ret diff --git a/salt/states/aws_sqs.py b/salt/states/aws_sqs.py index 36a34646af8..e769cb8559c 100644 --- a/salt/states/aws_sqs.py +++ b/salt/states/aws_sqs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage SQS Queues Create and destroy SQS queues. Be aware that this interacts with Amazon's @@ -14,25 +14,21 @@ information. myqueue: aws_sqs.exists: - region: eu-west-1 -''' +""" from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if aws is available. - ''' - if __salt__['cmd.has_exec']('aws'): - return 'aws_sqs' - return False + """ + if __salt__["cmd.has_exec"]("aws"): + return "aws_sqs" + return (False, "aws command not found") -def exists( - name, - region, - user=None, - opts=False): - ''' +def exists(name, region, user=None, opts=False): + """ Ensure the SQS queue exists. name @@ -46,36 +42,31 @@ def exists( opts Include additional arguments and options to the aws command line - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - does_exist = __salt__['aws_sqs.queue_exists'](name, region, opts, user) + does_exist = __salt__["aws_sqs.queue_exists"](name, region, opts, user) if not does_exist: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'AWS SQS queue {0} is set to be created'.format( - name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "AWS SQS queue {0} is set to be created".format(name) return ret - created = __salt__['aws_sqs.create_queue'](name, region, opts, user) - if created['retcode'] == 0: - ret['changes']['new'] = created['stdout'] + created = __salt__["aws_sqs.create_queue"](name, region, opts, user) + if created["retcode"] == 0: + ret["changes"]["new"] = created["stdout"] else: - ret['result'] = False - ret['comment'] = created['stderr'] + ret["result"] = False + ret["comment"] = created["stderr"] else: - ret['comment'] = '{0} exists in {1}'.format(name, region) + ret["comment"] = "{0} exists in {1}".format(name, region) return ret -def absent( - name, - region, - user=None, - opts=False): - ''' +def absent(name, region, user=None, opts=False): + """ Remove the named SQS queue if it exists. name @@ -89,24 +80,23 @@ def absent( opts Include additional arguments and options to the aws command line - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - does_exist = __salt__['aws_sqs.queue_exists'](name, region, opts, user) + does_exist = __salt__["aws_sqs.queue_exists"](name, region, opts, user) if does_exist: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'AWS SQS queue {0} is set to be removed'.format( - name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "AWS SQS queue {0} is set to be removed".format(name) return ret - removed = __salt__['aws_sqs.delete_queue'](name, region, opts, user) - if removed['retcode'] == 0: - ret['changes']['removed'] = removed['stdout'] + removed = __salt__["aws_sqs.delete_queue"](name, region, opts, user) + if removed["retcode"] == 0: + ret["changes"]["removed"] = removed["stdout"] else: - ret['result'] = False - ret['comment'] = removed['stderr'] + ret["result"] = False + ret["comment"] = removed["stderr"] else: - ret['comment'] = '{0} does not exist in {1}'.format(name, region) + ret["comment"] = "{0} does not exist in {1}".format(name, region) return ret diff --git a/salt/states/azurearm_compute.py b/salt/states/azurearm_compute.py index add83ad1bee..4036728c176 100644 --- a/salt/states/azurearm_compute.py +++ b/salt/states/azurearm_compute.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure (ARM) Compute State Module .. versionadded:: 2019.2.0 @@ -83,28 +83,39 @@ Azure (ARM) Compute State Module - resource_group: my_rg - connection_auth: {{ profile }} -''' +""" # Python libs from __future__ import absolute_import + import logging -__virtualname__ = 'azurearm_compute' +__virtualname__ = "azurearm_compute" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only make this state available if the azurearm_compute module is available. - ''' - return __virtualname__ if 'azurearm_compute.availability_set_create_or_update' in __salt__ else False + """ + if "azurearm_compute.availability_set_create_or_update" in __salt__: + return __virtualname__ + return (False, "azurearm module could not be loaded") -def availability_set_present(name, resource_group, tags=None, platform_update_domain_count=None, - platform_fault_domain_count=None, virtual_machines=None, sku=None, connection_auth=None, - **kwargs): - ''' +def availability_set_present( + name, + resource_group, + tags=None, + platform_update_domain_count=None, + platform_fault_domain_count=None, + virtual_machines=None, + sku=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure an availability set exists. @@ -154,96 +165,96 @@ def availability_set_present(name, resource_group, tags=None, platform_update_do - require: - azurearm_resource: Ensure resource group exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret if sku: - sku = {'name': sku.capitalize()} + sku = {"name": sku.capitalize()} - aset = __salt__['azurearm_compute.availability_set_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + aset = __salt__["azurearm_compute.availability_set_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' not in aset: - tag_changes = __utils__['dictdiffer.deep_diff'](aset.get('tags', {}), tags or {}) + if "error" not in aset: + tag_changes = __utils__["dictdiffer.deep_diff"]( + aset.get("tags", {}), tags or {} + ) if tag_changes: - ret['changes']['tags'] = tag_changes + ret["changes"]["tags"] = tag_changes - if platform_update_domain_count and (int(platform_update_domain_count) != aset.get('platform_update_domain_count')): - ret['changes']['platform_update_domain_count'] = { - 'old': aset.get('platform_update_domain_count'), - 'new': platform_update_domain_count + if platform_update_domain_count and ( + int(platform_update_domain_count) + != aset.get("platform_update_domain_count") + ): + ret["changes"]["platform_update_domain_count"] = { + "old": aset.get("platform_update_domain_count"), + "new": platform_update_domain_count, } - if platform_fault_domain_count and (int(platform_fault_domain_count) != aset.get('platform_fault_domain_count')): - ret['changes']['platform_fault_domain_count'] = { - 'old': aset.get('platform_fault_domain_count'), - 'new': platform_fault_domain_count + if platform_fault_domain_count and ( + int(platform_fault_domain_count) != aset.get("platform_fault_domain_count") + ): + ret["changes"]["platform_fault_domain_count"] = { + "old": aset.get("platform_fault_domain_count"), + "new": platform_fault_domain_count, } - if sku and (sku['name'] != aset.get('sku', {}).get('name')): - ret['changes']['sku'] = { - 'old': aset.get('sku'), - 'new': sku - } + if sku and (sku["name"] != aset.get("sku", {}).get("name")): + ret["changes"]["sku"] = {"old": aset.get("sku"), "new": sku} if virtual_machines: if not isinstance(virtual_machines, list): - ret['comment'] = 'Virtual machines must be supplied as a list!' + ret["comment"] = "Virtual machines must be supplied as a list!" return ret - aset_vms = aset.get('virtual_machines', []) - remote_vms = sorted([vm['id'].split('/')[-1].lower() for vm in aset_vms if 'id' in aset_vms]) + aset_vms = aset.get("virtual_machines", []) + remote_vms = sorted( + [vm["id"].split("/")[-1].lower() for vm in aset_vms if "id" in aset_vms] + ) local_vms = sorted([vm.lower() for vm in virtual_machines or []]) if local_vms != remote_vms: - ret['changes']['virtual_machines'] = { - 'old': aset_vms, - 'new': virtual_machines + ret["changes"]["virtual_machines"] = { + "old": aset_vms, + "new": virtual_machines, } - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Availability set {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Availability set {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Availability set {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Availability set {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'virtual_machines': virtual_machines, - 'platform_update_domain_count': platform_update_domain_count, - 'platform_fault_domain_count': platform_fault_domain_count, - 'sku': sku, - 'tags': tags - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "virtual_machines": virtual_machines, + "platform_update_domain_count": platform_update_domain_count, + "platform_fault_domain_count": platform_fault_domain_count, + "sku": sku, + "tags": tags, + }, } - if __opts__['test']: - ret['comment'] = 'Availability set {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Availability set {0} would be created.".format(name) + ret["result"] = None return ret aset_kwargs = kwargs.copy() aset_kwargs.update(connection_auth) - aset = __salt__['azurearm_compute.availability_set_create_or_update']( + aset = __salt__["azurearm_compute.availability_set_create_or_update"]( name=name, resource_group=resource_group, virtual_machines=virtual_machines, @@ -254,17 +265,19 @@ def availability_set_present(name, resource_group, tags=None, platform_update_do **aset_kwargs ) - if 'error' not in aset: - ret['result'] = True - ret['comment'] = 'Availability set {0} has been created.'.format(name) + if "error" not in aset: + ret["result"] = True + ret["comment"] = "Availability set {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create availability set {0}! ({1})'.format(name, aset.get('error')) + ret["comment"] = "Failed to create availability set {0}! ({1})".format( + name, aset.get("error") + ) return ret def availability_set_absent(name, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure an availability set does not exist in a resource group. @@ -278,49 +291,42 @@ def availability_set_absent(name, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - aset = __salt__['azurearm_compute.availability_set_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + aset = __salt__["azurearm_compute.availability_set_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in aset: - ret['result'] = True - ret['comment'] = 'Availability set {0} was not found.'.format(name) + if "error" in aset: + ret["result"] = True + ret["comment"] = "Availability set {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Availability set {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': aset, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Availability set {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": aset, + "new": {}, } return ret - deleted = __salt__['azurearm_compute.availability_set_delete'](name, resource_group, **connection_auth) + deleted = __salt__["azurearm_compute.availability_set_delete"]( + name, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Availability set {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': aset, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Availability set {0} has been deleted.".format(name) + ret["changes"] = {"old": aset, "new": {}} return ret - ret['comment'] = 'Failed to delete availability set {0}!'.format(name) + ret["comment"] = "Failed to delete availability set {0}!".format(name) return ret diff --git a/salt/states/azurearm_dns.py b/salt/states/azurearm_dns.py index fa29cf15321..1d1c39c850d 100644 --- a/salt/states/azurearm_dns.py +++ b/salt/states/azurearm_dns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure (ARM) DNS State Module .. versionadded:: 3000 @@ -18,32 +18,39 @@ Azure (ARM) DNS State Module * `azure-mgmt-web <https://pypi.python.org/pypi/azure-mgmt-web>`_ >= 0.32.0 * `azure-storage <https://pypi.python.org/pypi/azure-storage>`_ >= 0.34.3 * `msrestazure <https://pypi.python.org/pypi/msrestazure>`_ >= 0.4.21 + :platform: linux -:configuration: This module requires Azure Resource Manager credentials to be passed as a dictionary of -keyword arguments to the ``connection_auth`` parameter in order to work properly. Since the authentication -parameters are sensitive, it's recommended to pass them to the states via pillar. +:configuration: + This module requires Azure Resource Manager credentials to be passed as a dictionary of + keyword arguments to the ``connection_auth`` parameter in order to work properly. Since the authentication + parameters are sensitive, it's recommended to pass them to the states via pillar. - Required provider parameters: +Required provider parameters: if using username and password: - * ``subscription_id`` - * ``username`` - * ``password`` + + * ``subscription_id`` + * ``username`` + * ``password`` if using a service principal: - * ``subscription_id`` - * ``tenant`` - * ``client_id`` - * ``secret`` - Optional provider parameters: + * ``subscription_id`` + * ``tenant`` + * ``client_id`` + * ``secret`` + +Optional provider parameters: **cloud_environment**: Used to point the cloud driver to different API endpoints, such as Azure GovCloud. Possible values: - * ``AZURE_PUBLIC_CLOUD`` (default) - * ``AZURE_CHINA_CLOUD`` - * ``AZURE_US_GOV_CLOUD`` - * ``AZURE_GERMAN_CLOUD`` + + Possible values: + + * ``AZURE_PUBLIC_CLOUD`` (default) + * ``AZURE_CHINA_CLOUD`` + * ``AZURE_US_GOV_CLOUD`` + * ``AZURE_GERMAN_CLOUD`` Example Pillar for Azure Resource Manager authentication: @@ -63,7 +70,7 @@ parameters are sensitive, it's recommended to pass them to the states via pillar Example states using Azure Resource Manager authentication: - .. code-block:: yaml + .. code-block:: none {% set profile = salt['pillar.get']('azurearm:mysubscription') %} Ensure DNS zone exists: @@ -103,35 +110,49 @@ parameters are sensitive, it's recommended to pass them to the states via pillar - resource_group: my_rg - connection_auth: {{ profile }} -''' +""" # Python libs from __future__ import absolute_import + import logging # Salt libs import salt.ext.six as six + try: from salt.ext.six.moves import range as six_range except ImportError: six_range = range -__virtualname__ = 'azurearm_dns' +__virtualname__ = "azurearm_dns" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only make this state available if the azurearm_dns module is available. - ''' - return __virtualname__ if 'azurearm_dns.zones_list_by_resource_group' in __salt__ else False + """ + if "azurearm_dns.zones_list_by_resource_group" in __salt__: + return __virtualname__ + return (False, "azurearm_dns module could not be loaded") -def zone_present(name, resource_group, etag=None, if_match=None, if_none_match=None, - registration_virtual_networks=None, resolution_virtual_networks=None, - tags=None, zone_type='Public', connection_auth=None, **kwargs): - ''' +def zone_present( + name, + resource_group, + etag=None, + if_match=None, + if_none_match=None, + registration_virtual_networks=None, + resolution_virtual_networks=None, + tags=None, + zone_type="Public", + connection_auth=None, + **kwargs +): + """ .. versionadded:: 3000 Ensure a DNS zone exists. @@ -189,93 +210,110 @@ def zone_present(name, resource_group, etag=None, if_match=None, if_none_match=N contact_name: Elmer Fudd Gantry - connection_auth: {{ profile }} - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - zone = __salt__['azurearm_dns.zone_get'](name, resource_group, azurearm_log_level='info', **connection_auth) + zone = __salt__["azurearm_dns.zone_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth + ) - if 'error' not in zone: - tag_changes = __utils__['dictdiffer.deep_diff'](zone.get('tags', {}), tags or {}) + if "error" not in zone: + tag_changes = __utils__["dictdiffer.deep_diff"]( + zone.get("tags", {}), tags or {} + ) if tag_changes: - ret['changes']['tags'] = tag_changes + ret["changes"]["tags"] = tag_changes # The zone_type parameter is only accessible in azure-mgmt-dns >=2.0.0rc1 - if zone.get('zone_type'): - if zone.get('zone_type').lower() != zone_type.lower(): - ret['changes']['zone_type'] = { - 'old': zone['zone_type'], - 'new': zone_type + if zone.get("zone_type"): + if zone.get("zone_type").lower() != zone_type.lower(): + ret["changes"]["zone_type"] = { + "old": zone["zone_type"], + "new": zone_type, } - if zone_type.lower() == 'private': + if zone_type.lower() == "private": # The registration_virtual_networks parameter is only accessible in azure-mgmt-dns >=2.0.0rc1 - if registration_virtual_networks and not isinstance(registration_virtual_networks, list): - ret['comment'] = 'registration_virtual_networks must be supplied as a list of VNET ID paths!' + if registration_virtual_networks and not isinstance( + registration_virtual_networks, list + ): + ret[ + "comment" + ] = "registration_virtual_networks must be supplied as a list of VNET ID paths!" return ret - reg_vnets = zone.get('registration_virtual_networks', []) - remote_reg_vnets = sorted([vnet['id'].lower() for vnet in reg_vnets if 'id' in vnet]) - local_reg_vnets = sorted([vnet.lower() for vnet in registration_virtual_networks or []]) + reg_vnets = zone.get("registration_virtual_networks", []) + remote_reg_vnets = sorted( + [vnet["id"].lower() for vnet in reg_vnets if "id" in vnet] + ) + local_reg_vnets = sorted( + [vnet.lower() for vnet in registration_virtual_networks or []] + ) if local_reg_vnets != remote_reg_vnets: - ret['changes']['registration_virtual_networks'] = { - 'old': remote_reg_vnets, - 'new': local_reg_vnets + ret["changes"]["registration_virtual_networks"] = { + "old": remote_reg_vnets, + "new": local_reg_vnets, } # The resolution_virtual_networks parameter is only accessible in azure-mgmt-dns >=2.0.0rc1 - if resolution_virtual_networks and not isinstance(resolution_virtual_networks, list): - ret['comment'] = 'resolution_virtual_networks must be supplied as a list of VNET ID paths!' + if resolution_virtual_networks and not isinstance( + resolution_virtual_networks, list + ): + ret[ + "comment" + ] = "resolution_virtual_networks must be supplied as a list of VNET ID paths!" return ret - res_vnets = zone.get('resolution_virtual_networks', []) - remote_res_vnets = sorted([vnet['id'].lower() for vnet in res_vnets if 'id' in vnet]) - local_res_vnets = sorted([vnet.lower() for vnet in resolution_virtual_networks or []]) + res_vnets = zone.get("resolution_virtual_networks", []) + remote_res_vnets = sorted( + [vnet["id"].lower() for vnet in res_vnets if "id" in vnet] + ) + local_res_vnets = sorted( + [vnet.lower() for vnet in resolution_virtual_networks or []] + ) if local_res_vnets != remote_res_vnets: - ret['changes']['resolution_virtual_networks'] = { - 'old': remote_res_vnets, - 'new': local_res_vnets + ret["changes"]["resolution_virtual_networks"] = { + "old": remote_res_vnets, + "new": local_res_vnets, } - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'DNS zone {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "DNS zone {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'DNS zone {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "DNS zone {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'resource_group': resource_group, - 'etag': etag, - 'registration_virtual_networks': registration_virtual_networks, - 'resolution_virtual_networks': resolution_virtual_networks, - 'tags': tags, - 'zone_type': zone_type, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "resource_group": resource_group, + "etag": etag, + "registration_virtual_networks": registration_virtual_networks, + "resolution_virtual_networks": resolution_virtual_networks, + "tags": tags, + "zone_type": zone_type, + }, } - if __opts__['test']: - ret['comment'] = 'DNS zone {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "DNS zone {0} would be created.".format(name) + ret["result"] = None return ret zone_kwargs = kwargs.copy() zone_kwargs.update(connection_auth) - zone = __salt__['azurearm_dns.zone_create_or_update']( + zone = __salt__["azurearm_dns.zone_create_or_update"]( name=name, resource_group=resource_group, etag=etag, @@ -288,17 +326,19 @@ def zone_present(name, resource_group, etag=None, if_match=None, if_none_match=N **zone_kwargs ) - if 'error' not in zone: - ret['result'] = True - ret['comment'] = 'DNS zone {0} has been created.'.format(name) + if "error" not in zone: + ret["result"] = True + ret["comment"] = "DNS zone {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create DNS zone {0}! ({1})'.format(name, zone.get('error')) + ret["comment"] = "Failed to create DNS zone {0}! ({1})".format( + name, zone.get("error") + ) return ret def zone_absent(name, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 3000 Ensure a DNS zone does not exist in the resource group. @@ -312,59 +352,71 @@ def zone_absent(name, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - zone = __salt__['azurearm_dns.zone_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + zone = __salt__["azurearm_dns.zone_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in zone: - ret['result'] = True - ret['comment'] = 'DNS zone {0} was not found.'.format(name) + if "error" in zone: + ret["result"] = True + ret["comment"] = "DNS zone {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'DNS zone {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': zone, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "DNS zone {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": zone, + "new": {}, } return ret - deleted = __salt__['azurearm_dns.zone_delete'](name, resource_group, **connection_auth) + deleted = __salt__["azurearm_dns.zone_delete"]( + name, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'DNS zone {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': zone, - 'new': {} - } + ret["result"] = True + ret["comment"] = "DNS zone {0} has been deleted.".format(name) + ret["changes"] = {"old": zone, "new": {}} return ret - ret['comment'] = 'Failed to delete DNS zone {0}!'.format(name) + ret["comment"] = "Failed to delete DNS zone {0}!".format(name) return ret -def record_set_present(name, zone_name, resource_group, record_type, if_match=None, if_none_match=None, - etag=None, metadata=None, ttl=None, arecords=None, aaaa_records=None, mx_records=None, - ns_records=None, ptr_records=None, srv_records=None, txt_records=None, cname_record=None, - soa_record=None, caa_records=None, connection_auth=None, **kwargs): - ''' +def record_set_present( + name, + zone_name, + resource_group, + record_type, + if_match=None, + if_none_match=None, + etag=None, + metadata=None, + ttl=None, + arecords=None, + aaaa_records=None, + mx_records=None, + ns_records=None, + ptr_records=None, + srv_records=None, + txt_records=None, + cname_record=None, + soa_record=None, + caa_records=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 3000 Ensure a record set exists in a DNS zone. @@ -392,7 +444,7 @@ def record_set_present(name, zone_name, resource_group, record_type, if_match=No will be ignored. :param etag: - The etag of the record set. `Etags <https://docs.microsoft.com/en-us/azure/dns/dns-zones-records#etags>`_ are + The etag of the record set. `Etags <https://docs.microsoft.com/en-us/azure/dns/dns-zones-records#etags>`__ are used to handle concurrent changes to the same resource safely. :param metadata: @@ -403,52 +455,52 @@ def record_set_present(name, zone_name, resource_group, record_type, if_match=No :param arecords: The list of A records in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.arecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.arecord?view=azure-python>`__ to create a list of dictionaries representing the record objects. :param aaaa_records: The list of AAAA records in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.aaaarecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.aaaarecord?view=azure-python>`__ to create a list of dictionaries representing the record objects. :param mx_records: The list of MX records in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.mxrecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.mxrecord?view=azure-python>`__ to create a list of dictionaries representing the record objects. :param ns_records: The list of NS records in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.nsrecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.nsrecord?view=azure-python>`__ to create a list of dictionaries representing the record objects. :param ptr_records: The list of PTR records in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.ptrrecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.ptrrecord?view=azure-python>`__ to create a list of dictionaries representing the record objects. :param srv_records: The list of SRV records in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.srvrecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.srvrecord?view=azure-python>`__ to create a list of dictionaries representing the record objects. :param txt_records: The list of TXT records in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.txtrecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.txtrecord?view=azure-python>`__ to create a list of dictionaries representing the record objects. :param cname_record: The CNAME record in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.cnamerecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.cnamerecord?view=azure-python>`__ to create a dictionary representing the record object. :param soa_record: The SOA record in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.soarecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.soarecord?view=azure-python>`__ to create a dictionary representing the record object. :param caa_records: The list of CAA records in the record set. View the - `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.caarecord?view=azure-python>`_ + `Azure SDK documentation <https://docs.microsoft.com/en-us/python/api/azure.mgmt.dns.models.caarecord?view=azure-python>`__ to create a list of dictionaries representing the record objects. :param connection_auth: @@ -473,69 +525,78 @@ def record_set_present(name, zone_name, resource_group, record_type, if_match=No contact_name: Elmer Fudd Gantry - connection_auth: {{ profile }} - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} record_vars = [ - 'arecords', - 'aaaa_records', - 'mx_records', - 'ns_records', - 'ptr_records', - 'srv_records', - 'txt_records', - 'cname_record', - 'soa_record', - 'caa_records' + "arecords", + "aaaa_records", + "mx_records", + "ns_records", + "ptr_records", + "srv_records", + "txt_records", + "cname_record", + "soa_record", + "caa_records", ] if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - rec_set = __salt__['azurearm_dns.record_set_get']( + rec_set = __salt__["azurearm_dns.record_set_get"]( name, zone_name, resource_group, record_type, - azurearm_log_level='info', + azurearm_log_level="info", **connection_auth ) - if 'error' not in rec_set: - metadata_changes = __utils__['dictdiffer.deep_diff'](rec_set.get('metadata', {}), metadata or {}) + if "error" not in rec_set: + metadata_changes = __utils__["dictdiffer.deep_diff"]( + rec_set.get("metadata", {}), metadata or {} + ) if metadata_changes: - ret['changes']['metadata'] = metadata_changes + ret["changes"]["metadata"] = metadata_changes for record_str in record_vars: # pylint: disable=eval-used record = eval(record_str) if record: if not ttl: - ret['comment'] = 'TTL is required when specifying record information!' + ret[ + "comment" + ] = "TTL is required when specifying record information!" return ret if not rec_set.get(record_str): - ret['changes'] = {'new': {record_str: record}} + ret["changes"] = {"new": {record_str: record}} continue - if record_str[-1] != 's': + if record_str[-1] != "s": if not isinstance(record, dict): - ret['comment'] = '{0} record information must be specified as a dictionary!'.format(record_str) - return ret - for k, v in record.items(): - if v != rec_set[record_str].get(k): - ret['changes'] = {'new': {record_str: record}} - elif record_str[-1] == 's': - if not isinstance(record, list): - ret['comment'] = '{0} record information must be specified as a list of dictionaries!'.format( + ret[ + "comment" + ] = "{0} record information must be specified as a dictionary!".format( record_str ) return ret - local, remote = [sorted(config) for config in (record, rec_set[record_str])] + for k, v in record.items(): + if v != rec_set[record_str].get(k): + ret["changes"] = {"new": {record_str: record}} + elif record_str[-1] == "s": + if not isinstance(record, list): + ret[ + "comment" + ] = "{0} record information must be specified as a list of dictionaries!".format( + record_str + ) + return ret + local, remote = [ + sorted(config) for config in (record, rec_set[record_str]) + ] for idx in six_range(0, len(local)): for key in local[idx]: local_val = local[idx][key] @@ -545,46 +606,46 @@ def record_set_present(name, zone_name, resource_group, record_type, if_match=No if isinstance(remote_val, six.string_types): remote_val = remote_val.lower() if local_val != remote_val: - ret['changes'] = {'new': {record_str: record}} + ret["changes"] = {"new": {record_str: record}} - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Record set {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Record set {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Record set {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Record set {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'zone_name': zone_name, - 'resource_group': resource_group, - 'record_type': record_type, - 'etag': etag, - 'metadata': metadata, - 'ttl': ttl, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "zone_name": zone_name, + "resource_group": resource_group, + "record_type": record_type, + "etag": etag, + "metadata": metadata, + "ttl": ttl, + }, } for record in record_vars: # pylint: disable=eval-used if eval(record): # pylint: disable=eval-used - ret['changes']['new'][record] = eval(record) + ret["changes"]["new"][record] = eval(record) - if __opts__['test']: - ret['comment'] = 'Record set {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Record set {0} would be created.".format(name) + ret["result"] = None return ret rec_set_kwargs = kwargs.copy() rec_set_kwargs.update(connection_auth) - rec_set = __salt__['azurearm_dns.record_set_create_or_update']( + rec_set = __salt__["azurearm_dns.record_set_create_or_update"]( name=name, zone_name=zone_name, resource_group=resource_group, @@ -607,17 +668,19 @@ def record_set_present(name, zone_name, resource_group, record_type, if_match=No **rec_set_kwargs ) - if 'error' not in rec_set: - ret['result'] = True - ret['comment'] = 'Record set {0} has been created.'.format(name) + if "error" not in rec_set: + ret["result"] = True + ret["comment"] = "Record set {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create record set {0}! ({1})'.format(name, rec_set.get('error')) + ret["comment"] = "Failed to create record set {0}! ({1})".format( + name, rec_set.get("error") + ) return ret def record_set_absent(name, zone_name, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 3000 Ensure a record set does not exist in the DNS zone. @@ -634,50 +697,44 @@ def record_set_absent(name, zone_name, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - rec_set = __salt__['azurearm_dns.record_set_get']( - name, - zone_name, - resource_group, - azurearm_log_level='info', - **connection_auth + rec_set = __salt__["azurearm_dns.record_set_get"]( + name, zone_name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in rec_set: - ret['result'] = True - ret['comment'] = 'Record set {0} was not found in zone {1}.'.format(name, zone_name) + if "error" in rec_set: + ret["result"] = True + ret["comment"] = "Record set {0} was not found in zone {1}.".format( + name, zone_name + ) return ret - elif __opts__['test']: - ret['comment'] = 'Record set {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': rec_set, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Record set {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": rec_set, + "new": {}, } return ret - deleted = __salt__['azurearm_dns.record_set_delete'](name, zone_name, resource_group, **connection_auth) + deleted = __salt__["azurearm_dns.record_set_delete"]( + name, zone_name, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Record set {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': rec_set, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Record set {0} has been deleted.".format(name) + ret["changes"] = {"old": rec_set, "new": {}} return ret - ret['comment'] = 'Failed to delete record set {0}!'.format(name) + ret["comment"] = "Failed to delete record set {0}!".format(name) return ret diff --git a/salt/states/azurearm_network.py b/salt/states/azurearm_network.py index 11ce5c46a1c..19352fcb35e 100644 --- a/salt/states/azurearm_network.py +++ b/salt/states/azurearm_network.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure (ARM) Network State Module .. versionadded:: 2019.2.0 @@ -85,10 +85,11 @@ Azure (ARM) Network State Module - resource_group: my_rg - connection_auth: {{ profile }} -''' +""" # Python libs from __future__ import absolute_import + import logging # Salt libs @@ -97,21 +98,30 @@ try: except ImportError: six_range = range -__virtualname__ = 'azurearm_network' +__virtualname__ = "azurearm_network" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only make this state available if the azurearm_network module is available. - ''' - return __virtualname__ if 'azurearm_network.check_ip_address_availability' in __salt__ else False + """ + if "azurearm_network.check_ip_address_availability" in __salt__: + return __virtualname__ + return (False, "azurearm_network module could not be loaded") -def virtual_network_present(name, address_prefixes, resource_group, dns_servers=None, - tags=None, connection_auth=None, **kwargs): - ''' +def virtual_network_present( + name, + address_prefixes, + resource_group, + dns_servers=None, + tags=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a virtual network exists. @@ -154,93 +164,95 @@ def virtual_network_present(name, address_prefixes, resource_group, dns_servers= - require: - azurearm_resource: Ensure resource group exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - vnet = __salt__['azurearm_network.virtual_network_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + vnet = __salt__["azurearm_network.virtual_network_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' not in vnet: - tag_changes = __utils__['dictdiffer.deep_diff'](vnet.get('tags', {}), tags or {}) + if "error" not in vnet: + tag_changes = __utils__["dictdiffer.deep_diff"]( + vnet.get("tags", {}), tags or {} + ) if tag_changes: - ret['changes']['tags'] = tag_changes + ret["changes"]["tags"] = tag_changes dns_changes = set(dns_servers or []).symmetric_difference( - set(vnet.get('dhcp_options', {}).get('dns_servers', []))) + set(vnet.get("dhcp_options", {}).get("dns_servers", [])) + ) if dns_changes: - ret['changes']['dns_servers'] = { - 'old': vnet.get('dhcp_options', {}).get('dns_servers', []), - 'new': dns_servers, + ret["changes"]["dns_servers"] = { + "old": vnet.get("dhcp_options", {}).get("dns_servers", []), + "new": dns_servers, } addr_changes = set(address_prefixes or []).symmetric_difference( - set(vnet.get('address_space', {}).get('address_prefixes', []))) + set(vnet.get("address_space", {}).get("address_prefixes", [])) + ) if addr_changes: - ret['changes']['address_space'] = { - 'address_prefixes': { - 'old': vnet.get('address_space', {}).get('address_prefixes', []), - 'new': address_prefixes, + ret["changes"]["address_space"] = { + "address_prefixes": { + "old": vnet.get("address_space", {}).get("address_prefixes", []), + "new": address_prefixes, } } - if kwargs.get('enable_ddos_protection', False) != vnet.get('enable_ddos_protection'): - ret['changes']['enable_ddos_protection'] = { - 'old': vnet.get('enable_ddos_protection'), - 'new': kwargs.get('enable_ddos_protection') + if kwargs.get("enable_ddos_protection", False) != vnet.get( + "enable_ddos_protection" + ): + ret["changes"]["enable_ddos_protection"] = { + "old": vnet.get("enable_ddos_protection"), + "new": kwargs.get("enable_ddos_protection"), } - if kwargs.get('enable_vm_protection', False) != vnet.get('enable_vm_protection'): - ret['changes']['enable_vm_protection'] = { - 'old': vnet.get('enable_vm_protection'), - 'new': kwargs.get('enable_vm_protection') + if kwargs.get("enable_vm_protection", False) != vnet.get( + "enable_vm_protection" + ): + ret["changes"]["enable_vm_protection"] = { + "old": vnet.get("enable_vm_protection"), + "new": kwargs.get("enable_vm_protection"), } - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Virtual network {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Virtual network {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Virtual network {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Virtual network {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'resource_group': resource_group, - 'address_space': {'address_prefixes': address_prefixes}, - 'dhcp_options': {'dns_servers': dns_servers}, - 'enable_ddos_protection': kwargs.get('enable_ddos_protection', False), - 'enable_vm_protection': kwargs.get('enable_vm_protection', False), - 'tags': tags, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "resource_group": resource_group, + "address_space": {"address_prefixes": address_prefixes}, + "dhcp_options": {"dns_servers": dns_servers}, + "enable_ddos_protection": kwargs.get("enable_ddos_protection", False), + "enable_vm_protection": kwargs.get("enable_vm_protection", False), + "tags": tags, + }, } - if __opts__['test']: - ret['comment'] = 'Virtual network {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Virtual network {0} would be created.".format(name) + ret["result"] = None return ret vnet_kwargs = kwargs.copy() vnet_kwargs.update(connection_auth) - vnet = __salt__['azurearm_network.virtual_network_create_or_update']( + vnet = __salt__["azurearm_network.virtual_network_create_or_update"]( name=name, resource_group=resource_group, address_prefixes=address_prefixes, @@ -249,17 +261,19 @@ def virtual_network_present(name, address_prefixes, resource_group, dns_servers= **vnet_kwargs ) - if 'error' not in vnet: - ret['result'] = True - ret['comment'] = 'Virtual network {0} has been created.'.format(name) + if "error" not in vnet: + ret["result"] = True + ret["comment"] = "Virtual network {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create virtual network {0}! ({1})'.format(name, vnet.get('error')) + ret["comment"] = "Failed to create virtual network {0}! ({1})".format( + name, vnet.get("error") + ) return ret def virtual_network_absent(name, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a virtual network does not exist in the resource group. @@ -273,57 +287,58 @@ def virtual_network_absent(name, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - vnet = __salt__['azurearm_network.virtual_network_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + vnet = __salt__["azurearm_network.virtual_network_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in vnet: - ret['result'] = True - ret['comment'] = 'Virtual network {0} was not found.'.format(name) + if "error" in vnet: + ret["result"] = True + ret["comment"] = "Virtual network {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Virtual network {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': vnet, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Virtual network {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": vnet, + "new": {}, } return ret - deleted = __salt__['azurearm_network.virtual_network_delete'](name, resource_group, **connection_auth) + deleted = __salt__["azurearm_network.virtual_network_delete"]( + name, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Virtual network {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': vnet, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Virtual network {0} has been deleted.".format(name) + ret["changes"] = {"old": vnet, "new": {}} return ret - ret['comment'] = 'Failed to delete virtual network {0}!'.format(name) + ret["comment"] = "Failed to delete virtual network {0}!".format(name) return ret -def subnet_present(name, address_prefix, virtual_network, resource_group, - security_group=None, route_table=None, connection_auth=None, **kwargs): - ''' +def subnet_present( + name, + address_prefix, + virtual_network, + resource_group, + security_group=None, + route_table=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a subnet exists. @@ -368,83 +383,77 @@ def subnet_present(name, address_prefix, virtual_network, resource_group, - azurearm_network: Ensure network security group exists - azurearm_network: Ensure route table exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - snet = __salt__['azurearm_network.subnet_get']( + snet = __salt__["azurearm_network.subnet_get"]( name, virtual_network, resource_group, - azurearm_log_level='info', + azurearm_log_level="info", **connection_auth ) - if 'error' not in snet: - if address_prefix != snet.get('address_prefix'): - ret['changes']['address_prefix'] = { - 'old': snet.get('address_prefix'), - 'new': address_prefix + if "error" not in snet: + if address_prefix != snet.get("address_prefix"): + ret["changes"]["address_prefix"] = { + "old": snet.get("address_prefix"), + "new": address_prefix, } nsg_name = None - if snet.get('network_security_group'): - nsg_name = snet['network_security_group']['id'].split('/')[-1] + if snet.get("network_security_group"): + nsg_name = snet["network_security_group"]["id"].split("/")[-1] if security_group and (security_group != nsg_name): - ret['changes']['network_security_group'] = { - 'old': nsg_name, - 'new': security_group + ret["changes"]["network_security_group"] = { + "old": nsg_name, + "new": security_group, } rttbl_name = None - if snet.get('route_table'): - rttbl_name = snet['route_table']['id'].split('/')[-1] + if snet.get("route_table"): + rttbl_name = snet["route_table"]["id"].split("/")[-1] if route_table and (route_table != rttbl_name): - ret['changes']['route_table'] = { - 'old': rttbl_name, - 'new': route_table - } + ret["changes"]["route_table"] = {"old": rttbl_name, "new": route_table} - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Subnet {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Subnet {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Subnet {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Subnet {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'address_prefix': address_prefix, - 'network_security_group': security_group, - 'route_table': route_table - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "address_prefix": address_prefix, + "network_security_group": security_group, + "route_table": route_table, + }, } - if __opts__['test']: - ret['comment'] = 'Subnet {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Subnet {0} would be created.".format(name) + ret["result"] = None return ret snet_kwargs = kwargs.copy() snet_kwargs.update(connection_auth) - snet = __salt__['azurearm_network.subnet_create_or_update']( + snet = __salt__["azurearm_network.subnet_create_or_update"]( name=name, virtual_network=virtual_network, resource_group=resource_group, @@ -454,17 +463,19 @@ def subnet_present(name, address_prefix, virtual_network, resource_group, **snet_kwargs ) - if 'error' not in snet: - ret['result'] = True - ret['comment'] = 'Subnet {0} has been created.'.format(name) + if "error" not in snet: + ret["result"] = True + ret["comment"] = "Subnet {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create subnet {0}! ({1})'.format(name, snet.get('error')) + ret["comment"] = "Failed to create subnet {0}! ({1})".format( + name, snet.get("error") + ) return ret def subnet_absent(name, virtual_network, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a virtual network does not exist in the virtual network. @@ -481,58 +492,55 @@ def subnet_absent(name, virtual_network, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - snet = __salt__['azurearm_network.subnet_get']( + snet = __salt__["azurearm_network.subnet_get"]( name, virtual_network, resource_group, - azurearm_log_level='info', + azurearm_log_level="info", **connection_auth ) - if 'error' in snet: - ret['result'] = True - ret['comment'] = 'Subnet {0} was not found.'.format(name) + if "error" in snet: + ret["result"] = True + ret["comment"] = "Subnet {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Subnet {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': snet, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Subnet {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": snet, + "new": {}, } return ret - deleted = __salt__['azurearm_network.subnet_delete'](name, virtual_network, resource_group, **connection_auth) + deleted = __salt__["azurearm_network.subnet_delete"]( + name, virtual_network, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Subnet {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': snet, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Subnet {0} has been deleted.".format(name) + ret["changes"] = {"old": snet, "new": {}} return ret - ret['comment'] = 'Failed to delete subnet {0}!'.format(name) + ret["comment"] = "Failed to delete subnet {0}!".format(name) return ret -def network_security_group_present(name, resource_group, tags=None, security_rules=None, connection_auth=None, - **kwargs): - ''' +def network_security_group_present( + name, resource_group, tags=None, security_rules=None, connection_auth=None, **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a network security group exists. @@ -591,70 +599,68 @@ def network_security_group_present(name, resource_group, tags=None, security_rul - require: - azurearm_resource: Ensure resource group exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - nsg = __salt__['azurearm_network.network_security_group_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + nsg = __salt__["azurearm_network.network_security_group_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' not in nsg: - tag_changes = __utils__['dictdiffer.deep_diff'](nsg.get('tags', {}), tags or {}) + if "error" not in nsg: + tag_changes = __utils__["dictdiffer.deep_diff"](nsg.get("tags", {}), tags or {}) if tag_changes: - ret['changes']['tags'] = tag_changes + ret["changes"]["tags"] = tag_changes if security_rules: - comp_ret = __utils__['azurearm.compare_list_of_dicts'](nsg.get('security_rules', []), security_rules) + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + nsg.get("security_rules", []), security_rules + ) - if comp_ret.get('comment'): - ret['comment'] = '"security_rules" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"security_rules" {0}'.format(comp_ret["comment"]) return ret - if comp_ret.get('changes'): - ret['changes']['security_rules'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["security_rules"] = comp_ret["changes"] - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Network security group {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Network security group {0} is already present.".format( + name + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Network security group {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Network security group {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'resource_group': resource_group, - 'tags': tags, - 'security_rules': security_rules, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "resource_group": resource_group, + "tags": tags, + "security_rules": security_rules, + }, } - if __opts__['test']: - ret['comment'] = 'Network security group {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Network security group {0} would be created.".format(name) + ret["result"] = None return ret nsg_kwargs = kwargs.copy() nsg_kwargs.update(connection_auth) - nsg = __salt__['azurearm_network.network_security_group_create_or_update']( + nsg = __salt__["azurearm_network.network_security_group_create_or_update"]( name=name, resource_group=resource_group, tags=tags, @@ -662,17 +668,19 @@ def network_security_group_present(name, resource_group, tags=None, security_rul **nsg_kwargs ) - if 'error' not in nsg: - ret['result'] = True - ret['comment'] = 'Network security group {0} has been created.'.format(name) + if "error" not in nsg: + ret["result"] = True + ret["comment"] = "Network security group {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create network security group {0}! ({1})'.format(name, nsg.get('error')) + ret["comment"] = "Failed to create network security group {0}! ({1})".format( + name, nsg.get("error") + ) return ret def network_security_group_absent(name, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a network security group does not exist in the resource group. @@ -686,60 +694,68 @@ def network_security_group_absent(name, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - nsg = __salt__['azurearm_network.network_security_group_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + nsg = __salt__["azurearm_network.network_security_group_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in nsg: - ret['result'] = True - ret['comment'] = 'Network security group {0} was not found.'.format(name) + if "error" in nsg: + ret["result"] = True + ret["comment"] = "Network security group {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Network security group {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': nsg, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Network security group {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": nsg, + "new": {}, } return ret - deleted = __salt__['azurearm_network.network_security_group_delete'](name, resource_group, **connection_auth) + deleted = __salt__["azurearm_network.network_security_group_delete"]( + name, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Network security group {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': nsg, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Network security group {0} has been deleted.".format(name) + ret["changes"] = {"old": nsg, "new": {}} return ret - ret['comment'] = 'Failed to delete network security group {0}!'.format(name) + ret["comment"] = "Failed to delete network security group {0}!".format(name) return ret -def security_rule_present(name, access, direction, priority, protocol, security_group, resource_group, - destination_address_prefix=None, destination_port_range=None, source_address_prefix=None, - source_port_range=None, description=None, destination_address_prefixes=None, - destination_port_ranges=None, source_address_prefixes=None, source_port_ranges=None, - connection_auth=None, **kwargs): - ''' +def security_rule_present( + name, + access, + direction, + priority, + protocol, + security_group, + resource_group, + destination_address_prefix=None, + destination_port_range=None, + source_address_prefix=None, + source_port_range=None, + description=None, + destination_address_prefixes=None, + destination_port_ranges=None, + source_address_prefixes=None, + source_port_ranges=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a security rule exists. @@ -829,201 +845,211 @@ def security_rule_present(name, access, direction, priority, protocol, security_ - require: - azurearm_network: Ensure network security group exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret exclusive_params = [ - ('source_port_ranges', 'source_port_range'), - ('source_address_prefixes', 'source_address_prefix'), - ('destination_port_ranges', 'destination_port_range'), - ('destination_address_prefixes', 'destination_address_prefix'), + ("source_port_ranges", "source_port_range"), + ("source_address_prefixes", "source_address_prefix"), + ("destination_port_ranges", "destination_port_range"), + ("destination_address_prefixes", "destination_address_prefix"), ] for params in exclusive_params: # pylint: disable=eval-used if not eval(params[0]) and not eval(params[1]): - ret['comment'] = 'Either the {0} or {1} parameter must be provided!'.format(params[0], params[1]) + ret["comment"] = "Either the {0} or {1} parameter must be provided!".format( + params[0], params[1] + ) return ret # pylint: disable=eval-used if eval(params[0]): # pylint: disable=eval-used if not isinstance(eval(params[0]), list): - ret['comment'] = 'The {0} parameter must be a list!'.format(params[0]) + ret["comment"] = "The {0} parameter must be a list!".format(params[0]) return ret # pylint: disable=exec-used - exec('{0} = None'.format(params[1])) + exec("{0} = None".format(params[1])) - rule = __salt__['azurearm_network.security_rule_get']( + rule = __salt__["azurearm_network.security_rule_get"]( name, security_group, resource_group, - azurearm_log_level='info', + azurearm_log_level="info", **connection_auth ) - if 'error' not in rule: + if "error" not in rule: # access changes - if access.capitalize() != rule.get('access'): - ret['changes']['access'] = { - 'old': rule.get('access'), - 'new': access - } + if access.capitalize() != rule.get("access"): + ret["changes"]["access"] = {"old": rule.get("access"), "new": access} # description changes - if description != rule.get('description'): - ret['changes']['description'] = { - 'old': rule.get('description'), - 'new': description + if description != rule.get("description"): + ret["changes"]["description"] = { + "old": rule.get("description"), + "new": description, } # direction changes - if direction.capitalize() != rule.get('direction'): - ret['changes']['direction'] = { - 'old': rule.get('direction'), - 'new': direction + if direction.capitalize() != rule.get("direction"): + ret["changes"]["direction"] = { + "old": rule.get("direction"), + "new": direction, } # priority changes - if int(priority) != rule.get('priority'): - ret['changes']['priority'] = { - 'old': rule.get('priority'), - 'new': priority - } + if int(priority) != rule.get("priority"): + ret["changes"]["priority"] = {"old": rule.get("priority"), "new": priority} # protocol changes - if protocol.lower() != rule.get('protocol', '').lower(): - ret['changes']['protocol'] = { - 'old': rule.get('protocol'), - 'new': protocol - } + if protocol.lower() != rule.get("protocol", "").lower(): + ret["changes"]["protocol"] = {"old": rule.get("protocol"), "new": protocol} # destination_port_range changes - if destination_port_range != rule.get('destination_port_range'): - ret['changes']['destination_port_range'] = { - 'old': rule.get('destination_port_range'), - 'new': destination_port_range + if destination_port_range != rule.get("destination_port_range"): + ret["changes"]["destination_port_range"] = { + "old": rule.get("destination_port_range"), + "new": destination_port_range, } # source_port_range changes - if source_port_range != rule.get('source_port_range'): - ret['changes']['source_port_range'] = { - 'old': rule.get('source_port_range'), - 'new': source_port_range + if source_port_range != rule.get("source_port_range"): + ret["changes"]["source_port_range"] = { + "old": rule.get("source_port_range"), + "new": source_port_range, } # destination_port_ranges changes - if sorted(destination_port_ranges or []) != sorted(rule.get('destination_port_ranges', [])): - ret['changes']['destination_port_ranges'] = { - 'old': rule.get('destination_port_ranges'), - 'new': destination_port_ranges + if sorted(destination_port_ranges or []) != sorted( + rule.get("destination_port_ranges", []) + ): + ret["changes"]["destination_port_ranges"] = { + "old": rule.get("destination_port_ranges"), + "new": destination_port_ranges, } # source_port_ranges changes - if sorted(source_port_ranges or []) != sorted(rule.get('source_port_ranges', [])): - ret['changes']['source_port_ranges'] = { - 'old': rule.get('source_port_ranges'), - 'new': source_port_ranges + if sorted(source_port_ranges or []) != sorted( + rule.get("source_port_ranges", []) + ): + ret["changes"]["source_port_ranges"] = { + "old": rule.get("source_port_ranges"), + "new": source_port_ranges, } # destination_address_prefix changes - if (destination_address_prefix or '').lower() != rule.get('destination_address_prefix', '').lower(): - ret['changes']['destination_address_prefix'] = { - 'old': rule.get('destination_address_prefix'), - 'new': destination_address_prefix + if (destination_address_prefix or "").lower() != rule.get( + "destination_address_prefix", "" + ).lower(): + ret["changes"]["destination_address_prefix"] = { + "old": rule.get("destination_address_prefix"), + "new": destination_address_prefix, } # source_address_prefix changes - if (source_address_prefix or '').lower() != rule.get('source_address_prefix', '').lower(): - ret['changes']['source_address_prefix'] = { - 'old': rule.get('source_address_prefix'), - 'new': source_address_prefix + if (source_address_prefix or "").lower() != rule.get( + "source_address_prefix", "" + ).lower(): + ret["changes"]["source_address_prefix"] = { + "old": rule.get("source_address_prefix"), + "new": source_address_prefix, } # destination_address_prefixes changes - if sorted(destination_address_prefixes or []) != sorted(rule.get('destination_address_prefixes', [])): - if len(destination_address_prefixes or []) != len(rule.get('destination_address_prefixes', [])): - ret['changes']['destination_address_prefixes'] = { - 'old': rule.get('destination_address_prefixes'), - 'new': destination_address_prefixes + if sorted(destination_address_prefixes or []) != sorted( + rule.get("destination_address_prefixes", []) + ): + if len(destination_address_prefixes or []) != len( + rule.get("destination_address_prefixes", []) + ): + ret["changes"]["destination_address_prefixes"] = { + "old": rule.get("destination_address_prefixes"), + "new": destination_address_prefixes, } else: - local_dst_addrs, remote_dst_addrs = (sorted(destination_address_prefixes), - sorted(rule.get('destination_address_prefixes'))) + local_dst_addrs, remote_dst_addrs = ( + sorted(destination_address_prefixes), + sorted(rule.get("destination_address_prefixes")), + ) for idx in six_range(0, len(local_dst_addrs)): if local_dst_addrs[idx].lower() != remote_dst_addrs[idx].lower(): - ret['changes']['destination_address_prefixes'] = { - 'old': rule.get('destination_address_prefixes'), - 'new': destination_address_prefixes + ret["changes"]["destination_address_prefixes"] = { + "old": rule.get("destination_address_prefixes"), + "new": destination_address_prefixes, } break # source_address_prefixes changes - if sorted(source_address_prefixes or []) != sorted(rule.get('source_address_prefixes', [])): - if len(source_address_prefixes or []) != len(rule.get('source_address_prefixes', [])): - ret['changes']['source_address_prefixes'] = { - 'old': rule.get('source_address_prefixes'), - 'new': source_address_prefixes + if sorted(source_address_prefixes or []) != sorted( + rule.get("source_address_prefixes", []) + ): + if len(source_address_prefixes or []) != len( + rule.get("source_address_prefixes", []) + ): + ret["changes"]["source_address_prefixes"] = { + "old": rule.get("source_address_prefixes"), + "new": source_address_prefixes, } else: - local_src_addrs, remote_src_addrs = (sorted(source_address_prefixes), - sorted(rule.get('source_address_prefixes'))) + local_src_addrs, remote_src_addrs = ( + sorted(source_address_prefixes), + sorted(rule.get("source_address_prefixes")), + ) for idx in six_range(0, len(local_src_addrs)): if local_src_addrs[idx].lower() != remote_src_addrs[idx].lower(): - ret['changes']['source_address_prefixes'] = { - 'old': rule.get('source_address_prefixes'), - 'new': source_address_prefixes + ret["changes"]["source_address_prefixes"] = { + "old": rule.get("source_address_prefixes"), + "new": source_address_prefixes, } break - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Security rule {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Security rule {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Security rule {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Security rule {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'access': access, - 'description': description, - 'direction': direction, - 'priority': priority, - 'protocol': protocol, - 'destination_address_prefix': destination_address_prefix, - 'destination_address_prefixes': destination_address_prefixes, - 'destination_port_range': destination_port_range, - 'destination_port_ranges': destination_port_ranges, - 'source_address_prefix': source_address_prefix, - 'source_address_prefixes': source_address_prefixes, - 'source_port_range': source_port_range, - 'source_port_ranges': source_port_ranges, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "access": access, + "description": description, + "direction": direction, + "priority": priority, + "protocol": protocol, + "destination_address_prefix": destination_address_prefix, + "destination_address_prefixes": destination_address_prefixes, + "destination_port_range": destination_port_range, + "destination_port_ranges": destination_port_ranges, + "source_address_prefix": source_address_prefix, + "source_address_prefixes": source_address_prefixes, + "source_port_range": source_port_range, + "source_port_ranges": source_port_ranges, + }, } - if __opts__['test']: - ret['comment'] = 'Security rule {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Security rule {0} would be created.".format(name) + ret["result"] = None return ret rule_kwargs = kwargs.copy() rule_kwargs.update(connection_auth) - rule = __salt__['azurearm_network.security_rule_create_or_update']( + rule = __salt__["azurearm_network.security_rule_create_or_update"]( name=name, access=access, description=description, @@ -1043,17 +1069,19 @@ def security_rule_present(name, access, direction, priority, protocol, security_ **rule_kwargs ) - if 'error' not in rule: - ret['result'] = True - ret['comment'] = 'Security rule {0} has been created.'.format(name) + if "error" not in rule: + ret["result"] = True + ret["comment"] = "Security rule {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create security rule {0}! ({1})'.format(name, rule.get('error')) + ret["comment"] = "Failed to create security rule {0}! ({1})".format( + name, rule.get("error") + ) return ret def security_rule_absent(name, security_group, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a security rule does not exist in the network security group. @@ -1070,59 +1098,67 @@ def security_rule_absent(name, security_group, resource_group, connection_auth=N :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - rule = __salt__['azurearm_network.security_rule_get']( + rule = __salt__["azurearm_network.security_rule_get"]( name, security_group, resource_group, - azurearm_log_level='info', + azurearm_log_level="info", **connection_auth ) - if 'error' in rule: - ret['result'] = True - ret['comment'] = 'Security rule {0} was not found.'.format(name) + if "error" in rule: + ret["result"] = True + ret["comment"] = "Security rule {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Security rule {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': rule, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Security rule {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": rule, + "new": {}, } return ret - deleted = __salt__['azurearm_network.security_rule_delete'](name, security_group, resource_group, **connection_auth) + deleted = __salt__["azurearm_network.security_rule_delete"]( + name, security_group, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Security rule {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': rule, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Security rule {0} has been deleted.".format(name) + ret["changes"] = {"old": rule, "new": {}} return ret - ret['comment'] = 'Failed to delete security rule {0}!'.format(name) + ret["comment"] = "Failed to delete security rule {0}!".format(name) return ret -def load_balancer_present(name, resource_group, sku=None, frontend_ip_configurations=None, backend_address_pools=None, - load_balancing_rules=None, probes=None, inbound_nat_rules=None, inbound_nat_pools=None, - outbound_nat_rules=None, tags=None, connection_auth=None, **kwargs): - ''' +def load_balancer_present( + name, + resource_group, + sku=None, + frontend_ip_configurations=None, + backend_address_pools=None, + load_balancing_rules=None, + probes=None, + inbound_nat_rules=None, + inbound_nat_pools=None, + outbound_nat_rules=None, + tags=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a load balancer exists. @@ -1285,176 +1321,181 @@ def load_balancer_present(name, resource_group, sku=None, frontend_ip_configurat - azurearm_resource: Ensure resource group exists - azurearm_network: Ensure public IP exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret if sku: - sku = {'name': sku.capitalize()} + sku = {"name": sku.capitalize()} - load_bal = __salt__['azurearm_network.load_balancer_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + load_bal = __salt__["azurearm_network.load_balancer_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' not in load_bal: + if "error" not in load_bal: # tag changes - tag_changes = __utils__['dictdiffer.deep_diff'](load_bal.get('tags', {}), tags or {}) + tag_changes = __utils__["dictdiffer.deep_diff"]( + load_bal.get("tags", {}), tags or {} + ) if tag_changes: - ret['changes']['tags'] = tag_changes + ret["changes"]["tags"] = tag_changes # sku changes if sku: - sku_changes = __utils__['dictdiffer.deep_diff'](load_bal.get('sku', {}), sku) + sku_changes = __utils__["dictdiffer.deep_diff"]( + load_bal.get("sku", {}), sku + ) if sku_changes: - ret['changes']['sku'] = sku_changes + ret["changes"]["sku"] = sku_changes # frontend_ip_configurations changes if frontend_ip_configurations: - comp_ret = __utils__['azurearm.compare_list_of_dicts']( - load_bal.get('frontend_ip_configurations', []), + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + load_bal.get("frontend_ip_configurations", []), frontend_ip_configurations, - ['public_ip_address', 'subnet'] + ["public_ip_address", "subnet"], ) - if comp_ret.get('comment'): - ret['comment'] = '"frontend_ip_configurations" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"frontend_ip_configurations" {0}'.format( + comp_ret["comment"] + ) return ret - if comp_ret.get('changes'): - ret['changes']['frontend_ip_configurations'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["frontend_ip_configurations"] = comp_ret["changes"] # backend_address_pools changes if backend_address_pools: - comp_ret = __utils__['azurearm.compare_list_of_dicts']( - load_bal.get('backend_address_pools', []), - backend_address_pools + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + load_bal.get("backend_address_pools", []), backend_address_pools ) - if comp_ret.get('comment'): - ret['comment'] = '"backend_address_pools" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"backend_address_pools" {0}'.format( + comp_ret["comment"] + ) return ret - if comp_ret.get('changes'): - ret['changes']['backend_address_pools'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["backend_address_pools"] = comp_ret["changes"] # probes changes if probes: - comp_ret = __utils__['azurearm.compare_list_of_dicts'](load_bal.get('probes', []), probes) + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + load_bal.get("probes", []), probes + ) - if comp_ret.get('comment'): - ret['comment'] = '"probes" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"probes" {0}'.format(comp_ret["comment"]) return ret - if comp_ret.get('changes'): - ret['changes']['probes'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["probes"] = comp_ret["changes"] # load_balancing_rules changes if load_balancing_rules: - comp_ret = __utils__['azurearm.compare_list_of_dicts']( - load_bal.get('load_balancing_rules', []), + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + load_bal.get("load_balancing_rules", []), load_balancing_rules, - ['frontend_ip_configuration', 'backend_address_pool', 'probe'] + ["frontend_ip_configuration", "backend_address_pool", "probe"], ) - if comp_ret.get('comment'): - ret['comment'] = '"load_balancing_rules" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"load_balancing_rules" {0}'.format( + comp_ret["comment"] + ) return ret - if comp_ret.get('changes'): - ret['changes']['load_balancing_rules'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["load_balancing_rules"] = comp_ret["changes"] # inbound_nat_rules changes if inbound_nat_rules: - comp_ret = __utils__['azurearm.compare_list_of_dicts']( - load_bal.get('inbound_nat_rules', []), + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + load_bal.get("inbound_nat_rules", []), inbound_nat_rules, - ['frontend_ip_configuration'] + ["frontend_ip_configuration"], ) - if comp_ret.get('comment'): - ret['comment'] = '"inbound_nat_rules" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"inbound_nat_rules" {0}'.format(comp_ret["comment"]) return ret - if comp_ret.get('changes'): - ret['changes']['inbound_nat_rules'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["inbound_nat_rules"] = comp_ret["changes"] # inbound_nat_pools changes if inbound_nat_pools: - comp_ret = __utils__['azurearm.compare_list_of_dicts']( - load_bal.get('inbound_nat_pools', []), + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + load_bal.get("inbound_nat_pools", []), inbound_nat_pools, - ['frontend_ip_configuration'] + ["frontend_ip_configuration"], ) - if comp_ret.get('comment'): - ret['comment'] = '"inbound_nat_pools" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"inbound_nat_pools" {0}'.format(comp_ret["comment"]) return ret - if comp_ret.get('changes'): - ret['changes']['inbound_nat_pools'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["inbound_nat_pools"] = comp_ret["changes"] # outbound_nat_rules changes if outbound_nat_rules: - comp_ret = __utils__['azurearm.compare_list_of_dicts']( - load_bal.get('outbound_nat_rules', []), + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + load_bal.get("outbound_nat_rules", []), outbound_nat_rules, - ['frontend_ip_configuration'] + ["frontend_ip_configuration"], ) - if comp_ret.get('comment'): - ret['comment'] = '"outbound_nat_rules" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"outbound_nat_rules" {0}'.format(comp_ret["comment"]) return ret - if comp_ret.get('changes'): - ret['changes']['outbound_nat_rules'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["outbound_nat_rules"] = comp_ret["changes"] - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Load balancer {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Load balancer {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Load balancer {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Load balancer {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'sku': sku, - 'tags': tags, - 'frontend_ip_configurations': frontend_ip_configurations, - 'backend_address_pools': backend_address_pools, - 'load_balancing_rules': load_balancing_rules, - 'probes': probes, - 'inbound_nat_rules': inbound_nat_rules, - 'inbound_nat_pools': inbound_nat_pools, - 'outbound_nat_rules': outbound_nat_rules, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "sku": sku, + "tags": tags, + "frontend_ip_configurations": frontend_ip_configurations, + "backend_address_pools": backend_address_pools, + "load_balancing_rules": load_balancing_rules, + "probes": probes, + "inbound_nat_rules": inbound_nat_rules, + "inbound_nat_pools": inbound_nat_pools, + "outbound_nat_rules": outbound_nat_rules, + }, } - if __opts__['test']: - ret['comment'] = 'Load balancer {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Load balancer {0} would be created.".format(name) + ret["result"] = None return ret lb_kwargs = kwargs.copy() lb_kwargs.update(connection_auth) - load_bal = __salt__['azurearm_network.load_balancer_create_or_update']( + load_bal = __salt__["azurearm_network.load_balancer_create_or_update"]( name=name, resource_group=resource_group, sku=sku, @@ -1469,17 +1510,19 @@ def load_balancer_present(name, resource_group, sku=None, frontend_ip_configurat **lb_kwargs ) - if 'error' not in load_bal: - ret['result'] = True - ret['comment'] = 'Load balancer {0} has been created.'.format(name) + if "error" not in load_bal: + ret["result"] = True + ret["comment"] = "Load balancer {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create load balancer {0}! ({1})'.format(name, load_bal.get('error')) + ret["comment"] = "Failed to create load balancer {0}! ({1})".format( + name, load_bal.get("error") + ) return ret def load_balancer_absent(name, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a load balancer does not exist in the resource group. @@ -1493,58 +1536,60 @@ def load_balancer_absent(name, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - load_bal = __salt__['azurearm_network.load_balancer_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + load_bal = __salt__["azurearm_network.load_balancer_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in load_bal: - ret['result'] = True - ret['comment'] = 'Load balancer {0} was not found.'.format(name) + if "error" in load_bal: + ret["result"] = True + ret["comment"] = "Load balancer {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Load balancer {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': load_bal, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Load balancer {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": load_bal, + "new": {}, } return ret - deleted = __salt__['azurearm_network.load_balancer_delete'](name, resource_group, **connection_auth) + deleted = __salt__["azurearm_network.load_balancer_delete"]( + name, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Load balancer {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': load_bal, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Load balancer {0} has been deleted.".format(name) + ret["changes"] = {"old": load_bal, "new": {}} return ret - ret['comment'] = 'Failed to delete load balancer {0}!'.format(name) + ret["comment"] = "Failed to delete load balancer {0}!".format(name) return ret -def public_ip_address_present(name, resource_group, tags=None, sku=None, public_ip_allocation_method=None, - public_ip_address_version=None, dns_settings=None, idle_timeout_in_minutes=None, - connection_auth=None, **kwargs): - ''' +def public_ip_address_present( + name, + resource_group, + tags=None, + sku=None, + public_ip_allocation_method=None, + public_ip_address_version=None, + dns_settings=None, + idle_timeout_in_minutes=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a public IP address exists. @@ -1603,110 +1648,113 @@ def public_ip_address_present(name, resource_group, tags=None, sku=None, public_ - require: - azurearm_resource: Ensure resource group exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret if sku: - sku = {'name': sku.capitalize()} + sku = {"name": sku.capitalize()} - pub_ip = __salt__['azurearm_network.public_ip_address_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + pub_ip = __salt__["azurearm_network.public_ip_address_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' not in pub_ip: + if "error" not in pub_ip: # tag changes - tag_changes = __utils__['dictdiffer.deep_diff'](pub_ip.get('tags', {}), tags or {}) + tag_changes = __utils__["dictdiffer.deep_diff"]( + pub_ip.get("tags", {}), tags or {} + ) if tag_changes: - ret['changes']['tags'] = tag_changes + ret["changes"]["tags"] = tag_changes # dns_settings changes if dns_settings: if not isinstance(dns_settings, dict): - ret['comment'] = 'DNS settings must be provided as a dictionary!' + ret["comment"] = "DNS settings must be provided as a dictionary!" return ret for key in dns_settings: - if dns_settings[key] != pub_ip.get('dns_settings', {}).get(key): - ret['changes']['dns_settings'] = { - 'old': pub_ip.get('dns_settings'), - 'new': dns_settings + if dns_settings[key] != pub_ip.get("dns_settings", {}).get(key): + ret["changes"]["dns_settings"] = { + "old": pub_ip.get("dns_settings"), + "new": dns_settings, } break # sku changes if sku: - sku_changes = __utils__['dictdiffer.deep_diff'](pub_ip.get('sku', {}), sku) + sku_changes = __utils__["dictdiffer.deep_diff"](pub_ip.get("sku", {}), sku) if sku_changes: - ret['changes']['sku'] = sku_changes + ret["changes"]["sku"] = sku_changes # public_ip_allocation_method changes if public_ip_allocation_method: - if public_ip_allocation_method.capitalize() != pub_ip.get('public_ip_allocation_method'): - ret['changes']['public_ip_allocation_method'] = { - 'old': pub_ip.get('public_ip_allocation_method'), - 'new': public_ip_allocation_method + if public_ip_allocation_method.capitalize() != pub_ip.get( + "public_ip_allocation_method" + ): + ret["changes"]["public_ip_allocation_method"] = { + "old": pub_ip.get("public_ip_allocation_method"), + "new": public_ip_allocation_method, } # public_ip_address_version changes if public_ip_address_version: - if public_ip_address_version.lower() != pub_ip.get('public_ip_address_version', '').lower(): - ret['changes']['public_ip_address_version'] = { - 'old': pub_ip.get('public_ip_address_version'), - 'new': public_ip_address_version + if ( + public_ip_address_version.lower() + != pub_ip.get("public_ip_address_version", "").lower() + ): + ret["changes"]["public_ip_address_version"] = { + "old": pub_ip.get("public_ip_address_version"), + "new": public_ip_address_version, } # idle_timeout_in_minutes changes - if idle_timeout_in_minutes and (int(idle_timeout_in_minutes) != pub_ip.get('idle_timeout_in_minutes')): - ret['changes']['idle_timeout_in_minutes'] = { - 'old': pub_ip.get('idle_timeout_in_minutes'), - 'new': idle_timeout_in_minutes + if idle_timeout_in_minutes and ( + int(idle_timeout_in_minutes) != pub_ip.get("idle_timeout_in_minutes") + ): + ret["changes"]["idle_timeout_in_minutes"] = { + "old": pub_ip.get("idle_timeout_in_minutes"), + "new": idle_timeout_in_minutes, } - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Public IP address {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Public IP address {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Public IP address {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Public IP address {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'tags': tags, - 'dns_settings': dns_settings, - 'sku': sku, - 'public_ip_allocation_method': public_ip_allocation_method, - 'public_ip_address_version': public_ip_address_version, - 'idle_timeout_in_minutes': idle_timeout_in_minutes, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "tags": tags, + "dns_settings": dns_settings, + "sku": sku, + "public_ip_allocation_method": public_ip_allocation_method, + "public_ip_address_version": public_ip_address_version, + "idle_timeout_in_minutes": idle_timeout_in_minutes, + }, } - if __opts__['test']: - ret['comment'] = 'Public IP address {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Public IP address {0} would be created.".format(name) + ret["result"] = None return ret pub_ip_kwargs = kwargs.copy() pub_ip_kwargs.update(connection_auth) - pub_ip = __salt__['azurearm_network.public_ip_address_create_or_update']( + pub_ip = __salt__["azurearm_network.public_ip_address_create_or_update"]( name=name, resource_group=resource_group, sku=sku, @@ -1718,17 +1766,19 @@ def public_ip_address_present(name, resource_group, tags=None, sku=None, public_ **pub_ip_kwargs ) - if 'error' not in pub_ip: - ret['result'] = True - ret['comment'] = 'Public IP address {0} has been created.'.format(name) + if "error" not in pub_ip: + ret["result"] = True + ret["comment"] = "Public IP address {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create public IP address {0}! ({1})'.format(name, pub_ip.get('error')) + ret["comment"] = "Failed to create public IP address {0}! ({1})".format( + name, pub_ip.get("error") + ) return ret def public_ip_address_absent(name, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a public IP address does not exist in the resource group. @@ -1742,59 +1792,65 @@ def public_ip_address_absent(name, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - pub_ip = __salt__['azurearm_network.public_ip_address_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + pub_ip = __salt__["azurearm_network.public_ip_address_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in pub_ip: - ret['result'] = True - ret['comment'] = 'Public IP address {0} was not found.'.format(name) + if "error" in pub_ip: + ret["result"] = True + ret["comment"] = "Public IP address {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Public IP address {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': pub_ip, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Public IP address {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": pub_ip, + "new": {}, } return ret - deleted = __salt__['azurearm_network.public_ip_address_delete'](name, resource_group, **connection_auth) + deleted = __salt__["azurearm_network.public_ip_address_delete"]( + name, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Public IP address {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': pub_ip, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Public IP address {0} has been deleted.".format(name) + ret["changes"] = {"old": pub_ip, "new": {}} return ret - ret['comment'] = 'Failed to delete public IP address {0}!'.format(name) + ret["comment"] = "Failed to delete public IP address {0}!".format(name) return ret -def network_interface_present(name, ip_configurations, subnet, virtual_network, resource_group, tags=None, - virtual_machine=None, network_security_group=None, dns_settings=None, mac_address=None, - primary=None, enable_accelerated_networking=None, enable_ip_forwarding=None, - connection_auth=None, **kwargs): - ''' +def network_interface_present( + name, + ip_configurations, + subnet, + virtual_network, + resource_group, + tags=None, + virtual_machine=None, + network_security_group=None, + dns_settings=None, + mac_address=None, + primary=None, + enable_accelerated_networking=None, + enable_ip_forwarding=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a network interface exists. @@ -1880,148 +1936,146 @@ def network_interface_present(name, ip_configurations, subnet, virtual_network, - azurearm_network: Ensure network security group exists - azurearm_network: Ensure another public IP exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - iface = __salt__['azurearm_network.network_interface_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + iface = __salt__["azurearm_network.network_interface_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' not in iface: + if "error" not in iface: # tag changes - tag_changes = __utils__['dictdiffer.deep_diff'](iface.get('tags', {}), tags or {}) + tag_changes = __utils__["dictdiffer.deep_diff"]( + iface.get("tags", {}), tags or {} + ) if tag_changes: - ret['changes']['tags'] = tag_changes + ret["changes"]["tags"] = tag_changes # mac_address changes - if mac_address and (mac_address != iface.get('mac_address')): - ret['changes']['mac_address'] = { - 'old': iface.get('mac_address'), - 'new': mac_address + if mac_address and (mac_address != iface.get("mac_address")): + ret["changes"]["mac_address"] = { + "old": iface.get("mac_address"), + "new": mac_address, } # primary changes if primary is not None: - if primary != iface.get('primary', True): - ret['changes']['primary'] = { - 'old': iface.get('primary'), - 'new': primary + if primary != iface.get("primary", True): + ret["changes"]["primary"] = { + "old": iface.get("primary"), + "new": primary, } # enable_accelerated_networking changes if enable_accelerated_networking is not None: - if enable_accelerated_networking != iface.get('enable_accelerated_networking'): - ret['changes']['enable_accelerated_networking'] = { - 'old': iface.get('enable_accelerated_networking'), - 'new': enable_accelerated_networking + if enable_accelerated_networking != iface.get( + "enable_accelerated_networking" + ): + ret["changes"]["enable_accelerated_networking"] = { + "old": iface.get("enable_accelerated_networking"), + "new": enable_accelerated_networking, } # enable_ip_forwarding changes if enable_ip_forwarding is not None: - if enable_ip_forwarding != iface.get('enable_ip_forwarding'): - ret['changes']['enable_ip_forwarding'] = { - 'old': iface.get('enable_ip_forwarding'), - 'new': enable_ip_forwarding + if enable_ip_forwarding != iface.get("enable_ip_forwarding"): + ret["changes"]["enable_ip_forwarding"] = { + "old": iface.get("enable_ip_forwarding"), + "new": enable_ip_forwarding, } # network_security_group changes nsg_name = None - if iface.get('network_security_group'): - nsg_name = iface['network_security_group']['id'].split('/')[-1] + if iface.get("network_security_group"): + nsg_name = iface["network_security_group"]["id"].split("/")[-1] if network_security_group and (network_security_group != nsg_name): - ret['changes']['network_security_group'] = { - 'old': nsg_name, - 'new': network_security_group + ret["changes"]["network_security_group"] = { + "old": nsg_name, + "new": network_security_group, } # virtual_machine changes vm_name = None - if iface.get('virtual_machine'): - vm_name = iface['virtual_machine']['id'].split('/')[-1] + if iface.get("virtual_machine"): + vm_name = iface["virtual_machine"]["id"].split("/")[-1] if virtual_machine and (virtual_machine != vm_name): - ret['changes']['virtual_machine'] = { - 'old': vm_name, - 'new': virtual_machine - } + ret["changes"]["virtual_machine"] = {"old": vm_name, "new": virtual_machine} # dns_settings changes if dns_settings: if not isinstance(dns_settings, dict): - ret['comment'] = 'DNS settings must be provided as a dictionary!' + ret["comment"] = "DNS settings must be provided as a dictionary!" return ret for key in dns_settings: - if dns_settings[key].lower() != iface.get('dns_settings', {}).get(key, '').lower(): - ret['changes']['dns_settings'] = { - 'old': iface.get('dns_settings'), - 'new': dns_settings + if ( + dns_settings[key].lower() + != iface.get("dns_settings", {}).get(key, "").lower() + ): + ret["changes"]["dns_settings"] = { + "old": iface.get("dns_settings"), + "new": dns_settings, } break # ip_configurations changes - comp_ret = __utils__['azurearm.compare_list_of_dicts']( - iface.get('ip_configurations', []), + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + iface.get("ip_configurations", []), ip_configurations, - ['public_ip_address', 'subnet'] + ["public_ip_address", "subnet"], ) - if comp_ret.get('comment'): - ret['comment'] = '"ip_configurations" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"ip_configurations" {0}'.format(comp_ret["comment"]) return ret - if comp_ret.get('changes'): - ret['changes']['ip_configurations'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["ip_configurations"] = comp_ret["changes"] - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Network interface {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Network interface {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Network interface {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Network interface {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'ip_configurations': ip_configurations, - 'dns_settings': dns_settings, - 'network_security_group': network_security_group, - 'virtual_machine': virtual_machine, - 'enable_accelerated_networking': enable_accelerated_networking, - 'enable_ip_forwarding': enable_ip_forwarding, - 'mac_address': mac_address, - 'primary': primary, - 'tags': tags, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "ip_configurations": ip_configurations, + "dns_settings": dns_settings, + "network_security_group": network_security_group, + "virtual_machine": virtual_machine, + "enable_accelerated_networking": enable_accelerated_networking, + "enable_ip_forwarding": enable_ip_forwarding, + "mac_address": mac_address, + "primary": primary, + "tags": tags, + }, } - if __opts__['test']: - ret['comment'] = 'Network interface {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Network interface {0} would be created.".format(name) + ret["result"] = None return ret iface_kwargs = kwargs.copy() iface_kwargs.update(connection_auth) - iface = __salt__['azurearm_network.network_interface_create_or_update']( + iface = __salt__["azurearm_network.network_interface_create_or_update"]( name=name, subnet=subnet, virtual_network=virtual_network, @@ -2038,17 +2092,19 @@ def network_interface_present(name, ip_configurations, subnet, virtual_network, **iface_kwargs ) - if 'error' not in iface: - ret['result'] = True - ret['comment'] = 'Network interface {0} has been created.'.format(name) + if "error" not in iface: + ret["result"] = True + ret["comment"] = "Network interface {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create network interface {0}! ({1})'.format(name, iface.get('error')) + ret["comment"] = "Failed to create network interface {0}! ({1})".format( + name, iface.get("error") + ) return ret def network_interface_absent(name, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a network interface does not exist in the resource group. @@ -2062,57 +2118,57 @@ def network_interface_absent(name, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - iface = __salt__['azurearm_network.network_interface_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + iface = __salt__["azurearm_network.network_interface_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in iface: - ret['result'] = True - ret['comment'] = 'Network interface {0} was not found.'.format(name) + if "error" in iface: + ret["result"] = True + ret["comment"] = "Network interface {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Network interface {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': iface, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Network interface {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": iface, + "new": {}, } return ret - deleted = __salt__['azurearm_network.network_interface_delete'](name, resource_group, **connection_auth) + deleted = __salt__["azurearm_network.network_interface_delete"]( + name, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Network interface {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': iface, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Network interface {0} has been deleted.".format(name) + ret["changes"] = {"old": iface, "new": {}} return ret - ret['comment'] = 'Failed to delete network interface {0}!)'.format(name) + ret["comment"] = "Failed to delete network interface {0}!)".format(name) return ret -def route_table_present(name, resource_group, tags=None, routes=None, disable_bgp_route_propagation=None, - connection_auth=None, **kwargs): - ''' +def route_table_present( + name, + resource_group, + tags=None, + routes=None, + disable_bgp_route_propagation=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a route table exists. @@ -2160,80 +2216,80 @@ def route_table_present(name, resource_group, tags=None, routes=None, disable_bg - require: - azurearm_resource: Ensure resource group exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - rt_tbl = __salt__['azurearm_network.route_table_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + rt_tbl = __salt__["azurearm_network.route_table_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' not in rt_tbl: + if "error" not in rt_tbl: # tag changes - tag_changes = __utils__['dictdiffer.deep_diff'](rt_tbl.get('tags', {}), tags or {}) + tag_changes = __utils__["dictdiffer.deep_diff"]( + rt_tbl.get("tags", {}), tags or {} + ) if tag_changes: - ret['changes']['tags'] = tag_changes + ret["changes"]["tags"] = tag_changes # disable_bgp_route_propagation changes # pylint: disable=line-too-long - if disable_bgp_route_propagation and (disable_bgp_route_propagation != rt_tbl.get('disable_bgp_route_propagation')): - ret['changes']['disable_bgp_route_propagation'] = { - 'old': rt_tbl.get('disable_bgp_route_propagation'), - 'new': disable_bgp_route_propagation + if disable_bgp_route_propagation and ( + disable_bgp_route_propagation != rt_tbl.get("disable_bgp_route_propagation") + ): + ret["changes"]["disable_bgp_route_propagation"] = { + "old": rt_tbl.get("disable_bgp_route_propagation"), + "new": disable_bgp_route_propagation, } # routes changes if routes: - comp_ret = __utils__['azurearm.compare_list_of_dicts'](rt_tbl.get('routes', []), routes) + comp_ret = __utils__["azurearm.compare_list_of_dicts"]( + rt_tbl.get("routes", []), routes + ) - if comp_ret.get('comment'): - ret['comment'] = '"routes" {0}'.format(comp_ret['comment']) + if comp_ret.get("comment"): + ret["comment"] = '"routes" {0}'.format(comp_ret["comment"]) return ret - if comp_ret.get('changes'): - ret['changes']['routes'] = comp_ret['changes'] + if comp_ret.get("changes"): + ret["changes"]["routes"] = comp_ret["changes"] - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Route table {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Route table {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Route table {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Route table {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'tags': tags, - 'routes': routes, - 'disable_bgp_route_propagation': disable_bgp_route_propagation, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "tags": tags, + "routes": routes, + "disable_bgp_route_propagation": disable_bgp_route_propagation, + }, } - if __opts__['test']: - ret['comment'] = 'Route table {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route table {0} would be created.".format(name) + ret["result"] = None return ret rt_tbl_kwargs = kwargs.copy() rt_tbl_kwargs.update(connection_auth) - rt_tbl = __salt__['azurearm_network.route_table_create_or_update']( + rt_tbl = __salt__["azurearm_network.route_table_create_or_update"]( name=name, resource_group=resource_group, disable_bgp_route_propagation=disable_bgp_route_propagation, @@ -2242,17 +2298,19 @@ def route_table_present(name, resource_group, tags=None, routes=None, disable_bg **rt_tbl_kwargs ) - if 'error' not in rt_tbl: - ret['result'] = True - ret['comment'] = 'Route table {0} has been created.'.format(name) + if "error" not in rt_tbl: + ret["result"] = True + ret["comment"] = "Route table {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create route table {0}! ({1})'.format(name, rt_tbl.get('error')) + ret["comment"] = "Failed to create route table {0}! ({1})".format( + name, rt_tbl.get("error") + ) return ret def route_table_absent(name, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a route table does not exist in the resource group. @@ -2266,57 +2324,58 @@ def route_table_absent(name, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - rt_tbl = __salt__['azurearm_network.route_table_get']( - name, - resource_group, - azurearm_log_level='info', - **connection_auth + rt_tbl = __salt__["azurearm_network.route_table_get"]( + name, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in rt_tbl: - ret['result'] = True - ret['comment'] = 'Route table {0} was not found.'.format(name) + if "error" in rt_tbl: + ret["result"] = True + ret["comment"] = "Route table {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Route table {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': rt_tbl, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Route table {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": rt_tbl, + "new": {}, } return ret - deleted = __salt__['azurearm_network.route_table_delete'](name, resource_group, **connection_auth) + deleted = __salt__["azurearm_network.route_table_delete"]( + name, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Route table {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': rt_tbl, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Route table {0} has been deleted.".format(name) + ret["changes"] = {"old": rt_tbl, "new": {}} return ret - ret['comment'] = 'Failed to delete route table {0}!'.format(name) + ret["comment"] = "Failed to delete route table {0}!".format(name) return ret -def route_present(name, address_prefix, next_hop_type, route_table, resource_group, next_hop_ip_address=None, - connection_auth=None, **kwargs): - ''' +def route_present( + name, + address_prefix, + next_hop_type, + route_table, + resource_group, + next_hop_ip_address=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a route exists within a route table. @@ -2360,75 +2419,70 @@ def route_present(name, address_prefix, next_hop_type, route_table, resource_gro - require: - azurearm_network: Ensure route table exists - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - route = __salt__['azurearm_network.route_get']( - name, - route_table, - resource_group, - azurearm_log_level='info', - **connection_auth + route = __salt__["azurearm_network.route_get"]( + name, route_table, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' not in route: - if address_prefix != route.get('address_prefix'): - ret['changes']['address_prefix'] = { - 'old': route.get('address_prefix'), - 'new': address_prefix + if "error" not in route: + if address_prefix != route.get("address_prefix"): + ret["changes"]["address_prefix"] = { + "old": route.get("address_prefix"), + "new": address_prefix, } - if next_hop_type.lower() != route.get('next_hop_type', '').lower(): - ret['changes']['next_hop_type'] = { - 'old': route.get('next_hop_type'), - 'new': next_hop_type + if next_hop_type.lower() != route.get("next_hop_type", "").lower(): + ret["changes"]["next_hop_type"] = { + "old": route.get("next_hop_type"), + "new": next_hop_type, } - if next_hop_type.lower() == 'virtualappliance' and next_hop_ip_address != route.get('next_hop_ip_address'): - ret['changes']['next_hop_ip_address'] = { - 'old': route.get('next_hop_ip_address'), - 'new': next_hop_ip_address + if next_hop_type.lower() == "virtualappliance" and next_hop_ip_address != route.get( + "next_hop_ip_address" + ): + ret["changes"]["next_hop_ip_address"] = { + "old": route.get("next_hop_ip_address"), + "new": next_hop_ip_address, } - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Route {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Route {0} is already present.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Route {0} would be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Route {0} would be updated.".format(name) return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'address_prefix': address_prefix, - 'next_hop_type': next_hop_type, - 'next_hop_ip_address': next_hop_ip_address - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "address_prefix": address_prefix, + "next_hop_type": next_hop_type, + "next_hop_ip_address": next_hop_ip_address, + }, } - if __opts__['test']: - ret['comment'] = 'Route {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route {0} would be created.".format(name) + ret["result"] = None return ret route_kwargs = kwargs.copy() route_kwargs.update(connection_auth) - route = __salt__['azurearm_network.route_create_or_update']( + route = __salt__["azurearm_network.route_create_or_update"]( name=name, route_table=route_table, resource_group=resource_group, @@ -2438,17 +2492,19 @@ def route_present(name, address_prefix, next_hop_type, route_table, resource_gro **route_kwargs ) - if 'error' not in route: - ret['result'] = True - ret['comment'] = 'Route {0} has been created.'.format(name) + if "error" not in route: + ret["result"] = True + ret["comment"] = "Route {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create route {0}! ({1})'.format(name, route.get('error')) + ret["comment"] = "Failed to create route {0}! ({1})".format( + name, route.get("error") + ) return ret def route_absent(name, route_table, resource_group, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a route table does not exist in the resource group. @@ -2465,50 +2521,42 @@ def route_absent(name, route_table, resource_group, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - route = __salt__['azurearm_network.route_get']( - name, - route_table, - resource_group, - azurearm_log_level='info', - **connection_auth + route = __salt__["azurearm_network.route_get"]( + name, route_table, resource_group, azurearm_log_level="info", **connection_auth ) - if 'error' in route: - ret['result'] = True - ret['comment'] = 'Route {0} was not found.'.format(name) + if "error" in route: + ret["result"] = True + ret["comment"] = "Route {0} was not found.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Route {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': route, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Route {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": route, + "new": {}, } return ret - deleted = __salt__['azurearm_network.route_delete'](name, route_table, resource_group, **connection_auth) + deleted = __salt__["azurearm_network.route_delete"]( + name, route_table, resource_group, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Route {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': route, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Route {0} has been deleted.".format(name) + ret["changes"] = {"old": route, "new": {}} return ret - ret['comment'] = 'Failed to delete route {0}!'.format(name) + ret["comment"] = "Failed to delete route {0}!".format(name) return ret diff --git a/salt/states/azurearm_resource.py b/salt/states/azurearm_resource.py index 530a543dd84..db908b2d672 100644 --- a/salt/states/azurearm_resource.py +++ b/salt/states/azurearm_resource.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure (ARM) Resource State Module .. versionadded:: 2019.2.0 @@ -79,30 +79,35 @@ Azure (ARM) Resource State Module - name: other_rg - connection_auth: {{ profile }} -''' +""" # Import Python libs from __future__ import absolute_import + import json import logging # Import Salt libs import salt.utils.files -__virtualname__ = 'azurearm_resource' +__virtualname__ = "azurearm_resource" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only make this state available if the azurearm_resource module is available. - ''' - return __virtualname__ if 'azurearm_resource.resource_group_check_existence' in __salt__ else False + """ + if "azurearm_resource.resource_group_check_existence" in __salt__: + return __virtualname__ + return (False, "azurearm_resource module could not be loaded") -def resource_group_present(name, location, managed_by=None, tags=None, connection_auth=None, **kwargs): - ''' +def resource_group_present( + name, location, managed_by=None, tags=None, connection_auth=None, **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a resource group exists. @@ -137,81 +142,78 @@ def resource_group_present(name, location, managed_by=None, tags=None, connectio contact_name: Elmer Fudd Gantry - connection_auth: {{ profile }} - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret group = {} - present = __salt__['azurearm_resource.resource_group_check_existence'](name, **connection_auth) + present = __salt__["azurearm_resource.resource_group_check_existence"]( + name, **connection_auth + ) if present: - group = __salt__['azurearm_resource.resource_group_get'](name, **connection_auth) - ret['changes'] = __utils__['dictdiffer.deep_diff'](group.get('tags', {}), tags or {}) + group = __salt__["azurearm_resource.resource_group_get"]( + name, **connection_auth + ) + ret["changes"] = __utils__["dictdiffer.deep_diff"]( + group.get("tags", {}), tags or {} + ) - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Resource group {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Resource group {0} is already present.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Resource group {0} tags would be updated.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': group.get('tags', {}), - 'new': tags - } + if __opts__["test"]: + ret["comment"] = "Resource group {0} tags would be updated.".format(name) + ret["result"] = None + ret["changes"] = {"old": group.get("tags", {}), "new": tags} return ret - elif __opts__['test']: - ret['comment'] = 'Resource group {0} would be created.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'location': location, - 'managed_by': managed_by, - 'tags': tags, - } + elif __opts__["test"]: + ret["comment"] = "Resource group {0} would be created.".format(name) + ret["result"] = None + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "location": location, + "managed_by": managed_by, + "tags": tags, + }, } return ret group_kwargs = kwargs.copy() group_kwargs.update(connection_auth) - group = __salt__['azurearm_resource.resource_group_create_or_update']( - name, - location, - managed_by=managed_by, - tags=tags, - **group_kwargs + group = __salt__["azurearm_resource.resource_group_create_or_update"]( + name, location, managed_by=managed_by, tags=tags, **group_kwargs + ) + present = __salt__["azurearm_resource.resource_group_check_existence"]( + name, **connection_auth ) - present = __salt__['azurearm_resource.resource_group_check_existence'](name, **connection_auth) if present: - ret['result'] = True - ret['comment'] = 'Resource group {0} has been created.'.format(name) - ret['changes'] = { - 'old': {}, - 'new': group - } + ret["result"] = True + ret["comment"] = "Resource group {0} has been created.".format(name) + ret["changes"] = {"old": {}, "new": group} return ret - ret['comment'] = 'Failed to create resource group {0}! ({1})'.format(name, group.get('error')) + ret["comment"] = "Failed to create resource group {0}! ({1})".format( + name, group.get("error") + ) return ret def resource_group_absent(name, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a resource group does not exist in the current subscription. @@ -222,64 +224,80 @@ def resource_group_absent(name, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret group = {} - present = __salt__['azurearm_resource.resource_group_check_existence'](name, **connection_auth) + present = __salt__["azurearm_resource.resource_group_check_existence"]( + name, **connection_auth + ) if not present: - ret['result'] = True - ret['comment'] = 'Resource group {0} is already absent.'.format(name) + ret["result"] = True + ret["comment"] = "Resource group {0} is already absent.".format(name) return ret - elif __opts__['test']: - group = __salt__['azurearm_resource.resource_group_get'](name, **connection_auth) + elif __opts__["test"]: + group = __salt__["azurearm_resource.resource_group_get"]( + name, **connection_auth + ) - ret['comment'] = 'Resource group {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': group, - 'new': {}, + ret["comment"] = "Resource group {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": group, + "new": {}, } return ret - group = __salt__['azurearm_resource.resource_group_get'](name, **connection_auth) - deleted = __salt__['azurearm_resource.resource_group_delete'](name, **connection_auth) + group = __salt__["azurearm_resource.resource_group_get"](name, **connection_auth) + deleted = __salt__["azurearm_resource.resource_group_delete"]( + name, **connection_auth + ) if deleted: present = False else: - present = __salt__['azurearm_resource.resource_group_check_existence'](name, **connection_auth) + present = __salt__["azurearm_resource.resource_group_check_existence"]( + name, **connection_auth + ) if not present: - ret['result'] = True - ret['comment'] = 'Resource group {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': group, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Resource group {0} has been deleted.".format(name) + ret["changes"] = {"old": group, "new": {}} return ret - ret['comment'] = 'Failed to delete resource group {0}!'.format(name) + ret["comment"] = "Failed to delete resource group {0}!".format(name) return ret -def policy_definition_present(name, policy_rule=None, policy_type=None, mode=None, display_name=None, description=None, - metadata=None, parameters=None, policy_rule_json=None, policy_rule_file=None, - template='jinja', source_hash=None, source_hash_name=None, skip_verify=False, - connection_auth=None, **kwargs): - ''' +def policy_definition_present( + name, + policy_rule=None, + policy_type=None, + mode=None, + display_name=None, + description=None, + metadata=None, + parameters=None, + policy_rule_json=None, + policy_rule_file=None, + template="jinja", + source_hash=None, + source_hash_name=None, + skip_verify=False, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a security policy definition exists. @@ -365,29 +383,36 @@ def policy_definition_present(name, policy_rule=None, policy_type=None, mode=Non effect: deny - connection_auth: {{ profile }} - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret if not policy_rule and not policy_rule_json and not policy_rule_file: - ret['comment'] = 'One of "policy_rule", "policy_rule_json", or "policy_rule_file" is required!' + ret[ + "comment" + ] = 'One of "policy_rule", "policy_rule_json", or "policy_rule_file" is required!' return ret - if sum(x is not None for x in [policy_rule, policy_rule_json, policy_rule_file]) > 1: - ret['comment'] = 'Only one of "policy_rule", "policy_rule_json", or "policy_rule_file" is allowed!' + if ( + sum(x is not None for x in [policy_rule, policy_rule_json, policy_rule_file]) + > 1 + ): + ret[ + "comment" + ] = 'Only one of "policy_rule", "policy_rule_json", or "policy_rule_file" is allowed!' return ret - if ((policy_rule_json or policy_rule_file) and - (policy_type or mode or display_name or description or metadata or parameters)): - ret['comment'] = 'Policy definitions cannot be passed when "policy_rule_json" or "policy_rule_file" is defined!' + if (policy_rule_json or policy_rule_file) and ( + policy_type or mode or display_name or description or metadata or parameters + ): + ret[ + "comment" + ] = 'Policy definitions cannot be passed when "policy_rule_json" or "policy_rule_file" is defined!' return ret temp_rule = {} @@ -395,12 +420,12 @@ def policy_definition_present(name, policy_rule=None, policy_type=None, mode=Non try: temp_rule = json.loads(policy_rule_json) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = 'Unable to load policy rule json! ({0})'.format(exc) + ret["comment"] = "Unable to load policy rule json! ({0})".format(exc) return ret elif policy_rule_file: try: # pylint: disable=unused-variable - sfn, source_sum, comment_ = __salt__['file.get_managed']( + sfn, source_sum, comment_ = __salt__["file.get_managed"]( None, template, policy_rule_file, @@ -416,18 +441,24 @@ def policy_definition_present(name, policy_rule=None, policy_type=None, mode=Non **kwargs ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = 'Unable to locate policy rule file "{0}"! ({1})'.format(policy_rule_file, exc) + ret["comment"] = 'Unable to locate policy rule file "{0}"! ({1})'.format( + policy_rule_file, exc + ) return ret if not sfn: - ret['comment'] = 'Unable to locate policy rule file "{0}"!)'.format(policy_rule_file) + ret["comment"] = 'Unable to locate policy rule file "{0}"!)'.format( + policy_rule_file + ) return ret try: - with salt.utils.files.fopen(sfn, 'r') as prf: + with salt.utils.files.fopen(sfn, "r") as prf: temp_rule = json.load(prf) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = 'Unable to load policy rule file "{0}"! ({1})'.format(policy_rule_file, exc) + ret["comment"] = 'Unable to load policy rule file "{0}"! ({1})'.format( + policy_rule_file, exc + ) return ret if sfn: @@ -435,83 +466,88 @@ def policy_definition_present(name, policy_rule=None, policy_type=None, mode=Non policy_name = name if policy_rule_json or policy_rule_file: - if temp_rule.get('name'): - policy_name = temp_rule.get('name') - policy_rule = temp_rule.get('properties', {}).get('policyRule') - policy_type = temp_rule.get('properties', {}).get('policyType') - mode = temp_rule.get('properties', {}).get('mode') - display_name = temp_rule.get('properties', {}).get('displayName') - description = temp_rule.get('properties', {}).get('description') - metadata = temp_rule.get('properties', {}).get('metadata') - parameters = temp_rule.get('properties', {}).get('parameters') + if temp_rule.get("name"): + policy_name = temp_rule.get("name") + policy_rule = temp_rule.get("properties", {}).get("policyRule") + policy_type = temp_rule.get("properties", {}).get("policyType") + mode = temp_rule.get("properties", {}).get("mode") + display_name = temp_rule.get("properties", {}).get("displayName") + description = temp_rule.get("properties", {}).get("description") + metadata = temp_rule.get("properties", {}).get("metadata") + parameters = temp_rule.get("properties", {}).get("parameters") - policy = __salt__['azurearm_resource.policy_definition_get'](name, azurearm_log_level='info', **connection_auth) + policy = __salt__["azurearm_resource.policy_definition_get"]( + name, azurearm_log_level="info", **connection_auth + ) - if 'error' not in policy: - if policy_type and policy_type.lower() != policy.get('policy_type', '').lower(): - ret['changes']['policy_type'] = { - 'old': policy.get('policy_type'), - 'new': policy_type + if "error" not in policy: + if policy_type and policy_type.lower() != policy.get("policy_type", "").lower(): + ret["changes"]["policy_type"] = { + "old": policy.get("policy_type"), + "new": policy_type, } - if (mode or '').lower() != policy.get('mode', '').lower(): - ret['changes']['mode'] = { - 'old': policy.get('mode'), - 'new': mode + if (mode or "").lower() != policy.get("mode", "").lower(): + ret["changes"]["mode"] = {"old": policy.get("mode"), "new": mode} + + if (display_name or "").lower() != policy.get("display_name", "").lower(): + ret["changes"]["display_name"] = { + "old": policy.get("display_name"), + "new": display_name, } - if (display_name or '').lower() != policy.get('display_name', '').lower(): - ret['changes']['display_name'] = { - 'old': policy.get('display_name'), - 'new': display_name + if (description or "").lower() != policy.get("description", "").lower(): + ret["changes"]["description"] = { + "old": policy.get("description"), + "new": description, } - if (description or '').lower() != policy.get('description', '').lower(): - ret['changes']['description'] = { - 'old': policy.get('description'), - 'new': description - } - - rule_changes = __utils__['dictdiffer.deep_diff'](policy.get('policy_rule', {}), policy_rule or {}) + rule_changes = __utils__["dictdiffer.deep_diff"]( + policy.get("policy_rule", {}), policy_rule or {} + ) if rule_changes: - ret['changes']['policy_rule'] = rule_changes + ret["changes"]["policy_rule"] = rule_changes - meta_changes = __utils__['dictdiffer.deep_diff'](policy.get('metadata', {}), metadata or {}) + meta_changes = __utils__["dictdiffer.deep_diff"]( + policy.get("metadata", {}), metadata or {} + ) if meta_changes: - ret['changes']['metadata'] = meta_changes + ret["changes"]["metadata"] = meta_changes - param_changes = __utils__['dictdiffer.deep_diff'](policy.get('parameters', {}), parameters or {}) + param_changes = __utils__["dictdiffer.deep_diff"]( + policy.get("parameters", {}), parameters or {} + ) if param_changes: - ret['changes']['parameters'] = param_changes + ret["changes"]["parameters"] = param_changes - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Policy definition {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Policy definition {0} is already present.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Policy definition {0} would be updated.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Policy definition {0} would be updated.".format(name) + ret["result"] = None return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': policy_name, - 'policy_type': policy_type, - 'mode': mode, - 'display_name': display_name, - 'description': description, - 'metadata': metadata, - 'parameters': parameters, - 'policy_rule': policy_rule, - } + ret["changes"] = { + "old": {}, + "new": { + "name": policy_name, + "policy_type": policy_type, + "mode": mode, + "display_name": display_name, + "description": description, + "metadata": metadata, + "parameters": parameters, + "policy_rule": policy_rule, + }, } - if __opts__['test']: - ret['comment'] = 'Policy definition {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Policy definition {0} would be created.".format(name) + ret["result"] = None return ret # Convert OrderedDict to dict @@ -523,7 +559,7 @@ def policy_definition_present(name, policy_rule=None, policy_type=None, mode=Non policy_kwargs = kwargs.copy() policy_kwargs.update(connection_auth) - policy = __salt__['azurearm_resource.policy_definition_create_or_update']( + policy = __salt__["azurearm_resource.policy_definition_create_or_update"]( name=policy_name, policy_rule=policy_rule, policy_type=policy_type, @@ -535,17 +571,19 @@ def policy_definition_present(name, policy_rule=None, policy_type=None, mode=Non **policy_kwargs ) - if 'error' not in policy: - ret['result'] = True - ret['comment'] = 'Policy definition {0} has been created.'.format(name) + if "error" not in policy: + ret["result"] = True + ret["comment"] = "Policy definition {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create policy definition {0}! ({1})'.format(name, policy.get('error')) + ret["comment"] = "Failed to create policy definition {0}! ({1})".format( + name, policy.get("error") + ) return ret def policy_definition_absent(name, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a policy definition does not exist in the current subscription. @@ -556,52 +594,59 @@ def policy_definition_absent(name, connection_auth=None): :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - policy = __salt__['azurearm_resource.policy_definition_get'](name, azurearm_log_level='info', **connection_auth) + policy = __salt__["azurearm_resource.policy_definition_get"]( + name, azurearm_log_level="info", **connection_auth + ) - if 'error' in policy: - ret['result'] = True - ret['comment'] = 'Policy definition {0} is already absent.'.format(name) + if "error" in policy: + ret["result"] = True + ret["comment"] = "Policy definition {0} is already absent.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Policy definition {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': policy, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Policy definition {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": policy, + "new": {}, } return ret - deleted = __salt__['azurearm_resource.policy_definition_delete'](name, **connection_auth) + deleted = __salt__["azurearm_resource.policy_definition_delete"]( + name, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Policy definition {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': policy, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Policy definition {0} has been deleted.".format(name) + ret["changes"] = {"old": policy, "new": {}} return ret - ret['comment'] = 'Failed to delete policy definition {0}!'.format(name) + ret["comment"] = "Failed to delete policy definition {0}!".format(name) return ret -def policy_assignment_present(name, scope, definition_name, display_name=None, description=None, assignment_type=None, - parameters=None, connection_auth=None, **kwargs): - ''' +def policy_assignment_present( + name, + scope, + definition_name, + display_name=None, + description=None, + assignment_type=None, + parameters=None, + connection_auth=None, + **kwargs +): + """ .. versionadded:: 2019.2.0 Ensure a security policy assignment exists. @@ -644,88 +689,78 @@ def policy_assignment_present(name, scope, definition_name, display_name=None, d - description: Test assignment for testing assignments. - connection_auth: {{ profile }} - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - policy = __salt__['azurearm_resource.policy_assignment_get']( - name, - scope, - azurearm_log_level='info', - **connection_auth + policy = __salt__["azurearm_resource.policy_assignment_get"]( + name, scope, azurearm_log_level="info", **connection_auth ) - if 'error' not in policy: - if assignment_type and assignment_type.lower() != policy.get('type', '').lower(): - ret['changes']['type'] = { - 'old': policy.get('type'), - 'new': assignment_type - } + if "error" not in policy: + if ( + assignment_type + and assignment_type.lower() != policy.get("type", "").lower() + ): + ret["changes"]["type"] = {"old": policy.get("type"), "new": assignment_type} - if scope.lower() != policy['scope'].lower(): - ret['changes']['scope'] = { - 'old': policy['scope'], - 'new': scope - } + if scope.lower() != policy["scope"].lower(): + ret["changes"]["scope"] = {"old": policy["scope"], "new": scope} - pa_name = policy['policy_definition_id'].split('/')[-1] + pa_name = policy["policy_definition_id"].split("/")[-1] if definition_name.lower() != pa_name.lower(): - ret['changes']['definition_name'] = { - 'old': pa_name, - 'new': definition_name + ret["changes"]["definition_name"] = {"old": pa_name, "new": definition_name} + + if (display_name or "").lower() != policy.get("display_name", "").lower(): + ret["changes"]["display_name"] = { + "old": policy.get("display_name"), + "new": display_name, } - if (display_name or '').lower() != policy.get('display_name', '').lower(): - ret['changes']['display_name'] = { - 'old': policy.get('display_name'), - 'new': display_name + if (description or "").lower() != policy.get("description", "").lower(): + ret["changes"]["description"] = { + "old": policy.get("description"), + "new": description, } - if (description or '').lower() != policy.get('description', '').lower(): - ret['changes']['description'] = { - 'old': policy.get('description'), - 'new': description - } - - param_changes = __utils__['dictdiffer.deep_diff'](policy.get('parameters', {}), parameters or {}) + param_changes = __utils__["dictdiffer.deep_diff"]( + policy.get("parameters", {}), parameters or {} + ) if param_changes: - ret['changes']['parameters'] = param_changes + ret["changes"]["parameters"] = param_changes - if not ret['changes']: - ret['result'] = True - ret['comment'] = 'Policy assignment {0} is already present.'.format(name) + if not ret["changes"]: + ret["result"] = True + ret["comment"] = "Policy assignment {0} is already present.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Policy assignment {0} would be updated.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Policy assignment {0} would be updated.".format(name) + ret["result"] = None return ret else: - ret['changes'] = { - 'old': {}, - 'new': { - 'name': name, - 'scope': scope, - 'definition_name': definition_name, - 'type': assignment_type, - 'display_name': display_name, - 'description': description, - 'parameters': parameters, - } + ret["changes"] = { + "old": {}, + "new": { + "name": name, + "scope": scope, + "definition_name": definition_name, + "type": assignment_type, + "display_name": display_name, + "description": description, + "parameters": parameters, + }, } - if __opts__['test']: - ret['comment'] = 'Policy assignment {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Policy assignment {0} would be created.".format(name) + ret["result"] = None return ret if isinstance(parameters, dict): @@ -733,7 +768,7 @@ def policy_assignment_present(name, scope, definition_name, display_name=None, d policy_kwargs = kwargs.copy() policy_kwargs.update(connection_auth) - policy = __salt__['azurearm_resource.policy_assignment_create']( + policy = __salt__["azurearm_resource.policy_assignment_create"]( name=name, scope=scope, definition_name=definition_name, @@ -744,17 +779,19 @@ def policy_assignment_present(name, scope, definition_name, display_name=None, d **policy_kwargs ) - if 'error' not in policy: - ret['result'] = True - ret['comment'] = 'Policy assignment {0} has been created.'.format(name) + if "error" not in policy: + ret["result"] = True + ret["comment"] = "Policy assignment {0} has been created.".format(name) return ret - ret['comment'] = 'Failed to create policy assignment {0}! ({1})'.format(name, policy.get('error')) + ret["comment"] = "Failed to create policy assignment {0}! ({1})".format( + name, policy.get("error") + ) return ret def policy_assignment_absent(name, scope, connection_auth=None): - ''' + """ .. versionadded:: 2019.2.0 Ensure a policy assignment does not exist in the provided scope. @@ -768,49 +805,42 @@ def policy_assignment_absent(name, scope, connection_auth=None): connection_auth A dict with subscription and authentication parameters to be used in connecting to the Azure Resource Manager API. - ''' - ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not isinstance(connection_auth, dict): - ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + ret[ + "comment" + ] = "Connection information must be specified via connection_auth dictionary!" return ret - policy = __salt__['azurearm_resource.policy_assignment_get']( - name, - scope, - azurearm_log_level='info', - **connection_auth + policy = __salt__["azurearm_resource.policy_assignment_get"]( + name, scope, azurearm_log_level="info", **connection_auth ) - if 'error' in policy: - ret['result'] = True - ret['comment'] = 'Policy assignment {0} is already absent.'.format(name) + if "error" in policy: + ret["result"] = True + ret["comment"] = "Policy assignment {0} is already absent.".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Policy assignment {0} would be deleted.'.format(name) - ret['result'] = None - ret['changes'] = { - 'old': policy, - 'new': {}, + elif __opts__["test"]: + ret["comment"] = "Policy assignment {0} would be deleted.".format(name) + ret["result"] = None + ret["changes"] = { + "old": policy, + "new": {}, } return ret - deleted = __salt__['azurearm_resource.policy_assignment_delete'](name, scope, **connection_auth) + deleted = __salt__["azurearm_resource.policy_assignment_delete"]( + name, scope, **connection_auth + ) if deleted: - ret['result'] = True - ret['comment'] = 'Policy assignment {0} has been deleted.'.format(name) - ret['changes'] = { - 'old': policy, - 'new': {} - } + ret["result"] = True + ret["comment"] = "Policy assignment {0} has been deleted.".format(name) + ret["changes"] = {"old": policy, "new": {}} return ret - ret['comment'] = 'Failed to delete policy assignment {0}!'.format(name) + ret["comment"] = "Failed to delete policy assignment {0}!".format(name) return ret diff --git a/salt/states/beacon.py b/salt/states/beacon.py index 58f6b324dc6..5e7c1fffac7 100644 --- a/salt/states/beacon.py +++ b/salt/states/beacon.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of the Salt beacons ============================== @@ -67,20 +67,19 @@ Management of the Salt beacons - interval: 10 - beacon_module: inotify - disable_during_state_run: True -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import Salt libs from salt.ext import six -import logging log = logging.getLogger(__name__) -def present(name, - save=False, - **kwargs): - ''' +def present(name, save=False, **kwargs): + """ Ensure beacon is configured with the included beacon data. name @@ -88,68 +87,63 @@ def present(name, save True/False, if True the beacons.conf file be updated too. Default is False. - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - current_beacons = __salt__['beacons.list'](return_yaml=False, **kwargs) + current_beacons = __salt__["beacons.list"](return_yaml=False, **kwargs) beacon_data = [{k: v} for k, v in six.iteritems(kwargs)] if name in current_beacons: if beacon_data == current_beacons[name]: - ret['comment'].append('Job {0} in correct state'.format(name)) + ret["comment"].append("Job {0} in correct state".format(name)) else: - if __opts__.get('test'): - kwargs['test'] = True - result = __salt__['beacons.modify'](name, beacon_data, **kwargs) - ret['comment'].append(result['comment']) - ret['changes'] = result['changes'] + if __opts__.get("test"): + kwargs["test"] = True + result = __salt__["beacons.modify"](name, beacon_data, **kwargs) + ret["comment"].append(result["comment"]) + ret["changes"] = result["changes"] else: - result = __salt__['beacons.modify'](name, beacon_data, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["beacons.modify"](name, beacon_data, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - if 'changes' in result: - ret['comment'].append('Modifying {0} in beacons'.format(name)) - ret['changes'] = result['changes'] + if "changes" in result: + ret["comment"].append("Modifying {0} in beacons".format(name)) + ret["changes"] = result["changes"] else: - ret['comment'].append(result['comment']) + ret["comment"].append(result["comment"]) else: - if __opts__.get('test'): - kwargs['test'] = True - result = __salt__['beacons.add'](name, beacon_data, **kwargs) - ret['comment'].append(result['comment']) + if __opts__.get("test"): + kwargs["test"] = True + result = __salt__["beacons.add"](name, beacon_data, **kwargs) + ret["comment"].append(result["comment"]) else: - result = __salt__['beacons.add'](name, beacon_data, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["beacons.add"](name, beacon_data, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - ret['comment'].append('Adding {0} to beacons'.format(name)) + ret["comment"].append("Adding {0} to beacons".format(name)) if save: - if __opts__.get('test'): - ret['comment'].append('Beacon {0} would be saved'.format(name)) + if __opts__.get("test"): + ret["comment"].append("Beacon {0} would be saved".format(name)) else: - result = __salt__['beacons.save']() - ret['comment'].append('Beacon {0} saved'.format(name)) + result = __salt__["beacons.save"]() + ret["comment"].append("Beacon {0} saved".format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret -def absent(name, - save=False, - **kwargs): - ''' +def absent(name, save=False, **kwargs): + """ Ensure beacon is absent. name @@ -157,115 +151,106 @@ def absent(name, save True/False, if True the beacons.conf file be updated too. Default is False. - ''' + """ ### NOTE: The keyword arguments in **kwargs are ignored in this state, but ### cannot be removed from the function definition, otherwise the use ### of unsupported arguments will result in a traceback. - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - current_beacons = __salt__['beacons.list'](return_yaml=False, **kwargs) + current_beacons = __salt__["beacons.list"](return_yaml=False, **kwargs) if name in current_beacons: - if __opts__.get('test'): - kwargs['test'] = True - result = __salt__['beacons.delete'](name, **kwargs) - ret['comment'].append(result['comment']) + if __opts__.get("test"): + kwargs["test"] = True + result = __salt__["beacons.delete"](name, **kwargs) + ret["comment"].append(result["comment"]) else: - result = __salt__['beacons.delete'](name, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["beacons.delete"](name, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - ret['comment'].append('Removed {0} from beacons'.format(name)) + ret["comment"].append("Removed {0} from beacons".format(name)) else: - ret['comment'].append('{0} not configured in beacons'.format(name)) + ret["comment"].append("{0} not configured in beacons".format(name)) if save: - if __opts__.get('test'): - ret['comment'].append('Beacon {0} would be saved'.format(name)) + if __opts__.get("test"): + ret["comment"].append("Beacon {0} would be saved".format(name)) else: - result = __salt__['beacons.save']() - ret['comment'].append('Beacon {0} saved'.format(name)) + result = __salt__["beacons.save"]() + ret["comment"].append("Beacon {0} saved".format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret def enabled(name, **kwargs): - ''' + """ Enable a beacon. name The name of the beacon to enable. - ''' + """ ### NOTE: The keyword arguments in **kwargs are ignored in this state, but ### cannot be removed from the function definition, otherwise the use ### of unsupported arguments will result in a traceback. - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - current_beacons = __salt__['beacons.list'](return_yaml=False, **kwargs) + current_beacons = __salt__["beacons.list"](return_yaml=False, **kwargs) if name in current_beacons: - if __opts__.get('test'): - kwargs['test'] = True - result = __salt__['beacons.enable_beacon'](name, **kwargs) - ret['comment'].append(result['comment']) + if __opts__.get("test"): + kwargs["test"] = True + result = __salt__["beacons.enable_beacon"](name, **kwargs) + ret["comment"].append(result["comment"]) else: - result = __salt__['beacons.enable_beacon'](name, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["beacons.enable_beacon"](name, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - ret['comment'].append('Enabled {0} from beacons'.format(name)) + ret["comment"].append("Enabled {0} from beacons".format(name)) else: - ret['comment'].append('{0} not a configured beacon'.format(name)) + ret["comment"].append("{0} not a configured beacon".format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret def disabled(name, **kwargs): - ''' + """ Disable a beacon. name The name of the beacon to disable. - ''' + """ ### NOTE: The keyword arguments in **kwargs are ignored in this state, but ### cannot be removed from the function definition, otherwise the use ### of unsupported arguments will result in a traceback. - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - current_beacons = __salt__['beacons.list'](return_yaml=False, **kwargs) + current_beacons = __salt__["beacons.list"](return_yaml=False, **kwargs) if name in current_beacons: - if __opts__.get('test'): - kwargs['test'] = True - result = __salt__['beacons.disable_beacon'](name, **kwargs) - ret['comment'].append(result['comment']) + if __opts__.get("test"): + kwargs["test"] = True + result = __salt__["beacons.disable_beacon"](name, **kwargs) + ret["comment"].append(result["comment"]) else: - result = __salt__['beacons.disable_beacon'](name, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["beacons.disable_beacon"](name, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - ret['comment'].append('Disabled beacon {0}.'.format(name)) + ret["comment"].append("Disabled beacon {0}.".format(name)) else: - ret['comment'].append('Job {0} is not configured.'.format(name)) + ret["comment"].append("Job {0} is not configured.".format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret diff --git a/salt/states/bigip.py b/salt/states/bigip.py index add2b1c16a3..4efc96dbd06 100644 --- a/salt/states/bigip.py +++ b/salt/states/bigip.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" A state module designed to enforce load-balancing configurations for F5 Big-IP entities. :maturity: develop :platform: f5_bigip_11.6 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -15,47 +15,51 @@ import salt.utils.json from salt.ext import six -#set up virtual function +# set up virtual function def __virtual__(): - ''' + """ Only load if the bigip exec module is available in __salt__ - ''' - return 'bigip' if 'bigip.list_transaction' in __salt__ else False + """ + if "bigip.list_transaction" in __salt__: + return "bigip" + return (False, "bigip module could not be loaded") def _load_result(response, ret): - ''' + """ format the results of listing functions - ''' + """ - #were we able to connect? - if response['code'] is None: - ret['comment'] = response['content'] - #forbidden? - elif response['code'] == 401: - ret['comment'] = '401 Forbidden: Authentication required!' - #Not found? - elif response['code'] == 404: - ret['comment'] = response['content']['message'] - #200? - elif response['code'] == 200: - ret['result'] = True - ret['comment'] = 'Listing Current Configuration Only. ' \ - 'Not action or changes occurred during the execution of this state.' - ret['changes'] = response['content'] - #something bad + # were we able to connect? + if response["code"] is None: + ret["comment"] = response["content"] + # forbidden? + elif response["code"] == 401: + ret["comment"] = "401 Forbidden: Authentication required!" + # Not found? + elif response["code"] == 404: + ret["comment"] = response["content"]["message"] + # 200? + elif response["code"] == 200: + ret["result"] = True + ret["comment"] = ( + "Listing Current Configuration Only. " + "Not action or changes occurred during the execution of this state." + ) + ret["changes"] = response["content"] + # something bad else: - ret['comment'] = response['content']['message'] + ret["comment"] = response["content"]["message"] return ret def _strip_key(dictionary, keyword): - ''' + """ look for a certain key within a dictionary and nullify ti's contents, check within nested dictionaries and lists as well. Certain attributes such as "generation" will change even when there were no changes made to the entity. - ''' + """ for key, value in six.iteritems(dictionary): if key == keyword: @@ -71,70 +75,96 @@ def _strip_key(dictionary, keyword): def _check_for_changes(entity_type, ret, existing, modified): - ''' + """ take an existing entity and a modified entity and check for changes. - ''' + """ - ret['result'] = True + ret["result"] = True - #were there any changes? generation always changes, remove it. + # were there any changes? generation always changes, remove it. if isinstance(existing, dict) and isinstance(modified, dict): - if 'generation' in modified['content'].keys(): - del modified['content']['generation'] + if "generation" in modified["content"].keys(): + del modified["content"]["generation"] - if 'generation' in existing['content'].keys(): - del existing['content']['generation'] + if "generation" in existing["content"].keys(): + del existing["content"]["generation"] - if modified['content'] == existing['content']: - ret['comment'] = '{entity_type} is currently enforced to the desired state. No changes made.'.format(entity_type=entity_type) + if modified["content"] == existing["content"]: + ret[ + "comment" + ] = "{entity_type} is currently enforced to the desired state. No changes made.".format( + entity_type=entity_type + ) else: - ret['comment'] = '{entity_type} was enforced to the desired state. Note: Only parameters specified ' \ - 'were enforced. See changes for details.'.format(entity_type=entity_type) - ret['changes']['old'] = existing['content'] - ret['changes']['new'] = modified['content'] + ret["comment"] = ( + "{entity_type} was enforced to the desired state. Note: Only parameters specified " + "were enforced. See changes for details.".format( + entity_type=entity_type + ) + ) + ret["changes"]["old"] = existing["content"] + ret["changes"]["new"] = modified["content"] else: if modified == existing: - ret['comment'] = '{entity_type} is currently enforced to the desired state. No changes made.'.format(entity_type=entity_type) + ret[ + "comment" + ] = "{entity_type} is currently enforced to the desired state. No changes made.".format( + entity_type=entity_type + ) else: - ret['comment'] = '{entity_type} was enforced to the desired state. Note: Only parameters specified ' \ - 'were enforced. See changes for details.'.format(entity_type=entity_type) - ret['changes']['old'] = existing - ret['changes']['new'] = modified + ret["comment"] = ( + "{entity_type} was enforced to the desired state. Note: Only parameters specified " + "were enforced. See changes for details.".format( + entity_type=entity_type + ) + ) + ret["changes"]["old"] = existing + ret["changes"]["new"] = modified return ret def _test_output(ret, action, params): - ''' + """ For testing just output what the state will attempt to do without actually doing it. - ''' + """ - if action == 'list': - ret['comment'] += 'The list action will just list an entity and will make no changes.\n' - elif action == 'create' or action == 'add': - ret['comment'] += 'The create action will attempt to create an entity if it does not already exist.\n' - elif action == 'delete': - ret['comment'] += 'The delete action will attempt to delete an existing entity if it exists.\n' - elif action == 'manage': - ret['comment'] += 'The manage action will create a new entity if it does not exist. If it does exist, it will be enforced' \ - 'to the desired state.\n' - elif action == 'modify': - ret['comment'] += 'The modify action will attempt to modify an existing entity only if it exists.\n' + if action == "list": + ret[ + "comment" + ] += "The list action will just list an entity and will make no changes.\n" + elif action == "create" or action == "add": + ret[ + "comment" + ] += "The create action will attempt to create an entity if it does not already exist.\n" + elif action == "delete": + ret[ + "comment" + ] += "The delete action will attempt to delete an existing entity if it exists.\n" + elif action == "manage": + ret["comment"] += ( + "The manage action will create a new entity if it does not exist. If it does exist, it will be enforced" + "to the desired state.\n" + ) + elif action == "modify": + ret[ + "comment" + ] += "The modify action will attempt to modify an existing entity only if it exists.\n" - ret['comment'] += 'An iControl REST Request will be made using the parameters:\n' - ret['comment'] += salt.utils.json.dumps(params, indent=4) + ret["comment"] += "An iControl REST Request will be made using the parameters:\n" + ret["comment"] += salt.utils.json.dumps(params, indent=4) - ret['changes'] = {} + ret["changes"] = {} # Return ``None`` when running with ``test=true``. - ret['result'] = None + ret["result"] = None return ret def list_node(hostname, username, password, name): - ''' + """ A function to connect to a bigip device and list a specific node. hostname @@ -145,25 +175,28 @@ def list_node(hostname, username, password, name): The iControl REST password name The name of the node to list. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'list', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name - } + if __opts__["test"]: + return _test_output( + ret, + "list", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + }, ) - response = __salt__['bigip.list_node'](hostname, username, password, name) + response = __salt__["bigip.list_node"](hostname, username, password, name) return _load_result(response, ret) def create_node(hostname, username, password, name, address): - ''' + """ Create a new node if it does not already exist. hostname @@ -176,37 +209,42 @@ def create_node(hostname, username, password, name, address): The name of the node to create address The address of the node - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'create', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'address': address - } + if __opts__["test"]: + return _test_output( + ret, + "create", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "address": address, + }, ) - #is this node currently configured? - existing = __salt__['bigip.list_node'](hostname, username, password, name) + # is this node currently configured? + existing = __salt__["bigip.list_node"](hostname, username, password, name) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - ret['result'] = True - ret['comment'] = 'A node by this name currently exists. No change made.' + ret["result"] = True + ret["comment"] = "A node by this name currently exists. No change made." # if it doesn't exist - elif existing['code'] == 404: - response = __salt__['bigip.create_node'](hostname, username, password, name, address) + elif existing["code"] == 404: + response = __salt__["bigip.create_node"]( + hostname, username, password, name, address + ) - ret['result'] = True - ret['changes']['old'] = {} - ret['changes']['new'] = response['content'] - ret['comment'] = 'Node was successfully created.' + ret["result"] = True + ret["changes"]["old"] = {} + ret["changes"]["new"] = response["content"] + ret["comment"] = "Node was successfully created." # else something else was returned else: @@ -215,17 +253,23 @@ def create_node(hostname, username, password, name, address): return ret -def manage_node(hostname, username, password, name, address, - connection_limit=None, - description=None, - dynamic_ratio=None, - logging=None, - monitor=None, - rate_limit=None, - ratio=None, - session=None, - node_state=None): - ''' +def manage_node( + hostname, + username, + password, + name, + address, + connection_limit=None, + description=None, + dynamic_ratio=None, + logging=None, + monitor=None, + rate_limit=None, + ratio=None, + session=None, + node_state=None, +): + """ Manages a node of a given bigip device. If the node does not exist it will be created, otherwise, only the properties which are different than the existing will be updated. @@ -257,107 +301,126 @@ def manage_node(hostname, username, password, name, address, [user-enabled | user-disabled] node_state (state) [user-down | user-up ] - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'manage', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'address': address, - 'connection_limit': connection_limit, - 'description': description, - 'dynamic_ratio': dynamic_ratio, - 'logging': logging, - 'monitor': monitor, - 'rate_limit': rate_limit, - 'ratio': ratio, - 'session': session, - 'state:': node_state - } + if __opts__["test"]: + return _test_output( + ret, + "manage", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "address": address, + "connection_limit": connection_limit, + "description": description, + "dynamic_ratio": dynamic_ratio, + "logging": logging, + "monitor": monitor, + "rate_limit": rate_limit, + "ratio": ratio, + "session": session, + "state:": node_state, + }, ) - #is this node currently configured? - existing = __salt__['bigip.list_node'](hostname, username, password, name) + # is this node currently configured? + existing = __salt__["bigip.list_node"](hostname, username, password, name) # if it exists by name - if existing['code'] == 200: + if existing["code"] == 200: # ensure the address is the same, we don't want to modify a different node than what # we think we are managing - if existing['content']['address'] != address: - ret['result'] = False - ret['comment'] = 'A node with this name exists but the address does not match.' + if existing["content"]["address"] != address: + ret["result"] = False + ret[ + "comment" + ] = "A node with this name exists but the address does not match." - modified = __salt__['bigip.modify_node'](hostname=hostname, - username=username, - password=password, - name=name, - connection_limit=connection_limit, - description=description, - dynamic_ratio=dynamic_ratio, - logging=logging, - monitor=monitor, - rate_limit=rate_limit, - ratio=ratio, - session=session, - state=node_state) + modified = __salt__["bigip.modify_node"]( + hostname=hostname, + username=username, + password=password, + name=name, + connection_limit=connection_limit, + description=description, + dynamic_ratio=dynamic_ratio, + logging=logging, + monitor=monitor, + rate_limit=rate_limit, + ratio=ratio, + session=session, + state=node_state, + ) - #was the modification successful? - if modified['code'] == 200: - ret = _check_for_changes('Node', ret, existing, modified) + # was the modification successful? + if modified["code"] == 200: + ret = _check_for_changes("Node", ret, existing, modified) else: ret = _load_result(modified, ret) # not found, attempt to create it - elif existing['code'] == 404: + elif existing["code"] == 404: - new = __salt__['bigip.create_node'](hostname, username, password, name, address) + new = __salt__["bigip.create_node"](hostname, username, password, name, address) # were we able to create it? - if new['code'] == 200: + if new["code"] == 200: # try modification - modified = __salt__['bigip.modify_node'](hostname=hostname, - username=username, - password=password, - name=name, - connection_limit=connection_limit, - description=description, - dynamic_ratio=dynamic_ratio, - logging=logging, - monitor=monitor, - rate_limit=rate_limit, - ratio=ratio, - session=session, - state=node_state) - #was the modification successful? - if modified['code'] == 200: + modified = __salt__["bigip.modify_node"]( + hostname=hostname, + username=username, + password=password, + name=name, + connection_limit=connection_limit, + description=description, + dynamic_ratio=dynamic_ratio, + logging=logging, + monitor=monitor, + rate_limit=rate_limit, + ratio=ratio, + session=session, + state=node_state, + ) + # was the modification successful? + if modified["code"] == 200: - ret['result'] = True - ret['comment'] = 'Node was created and enforced to the desired state. Note: Only parameters specified ' \ - 'were enforced. See changes for details.' - ret['changes']['old'] = {} - ret['changes']['new'] = modified['content'] + ret["result"] = True + ret["comment"] = ( + "Node was created and enforced to the desired state. Note: Only parameters specified " + "were enforced. See changes for details." + ) + ret["changes"]["old"] = {} + ret["changes"]["new"] = modified["content"] # roll it back else: - deleted = __salt__['bigip.delete_node'](hostname, username, password, name) + deleted = __salt__["bigip.delete_node"]( + hostname, username, password, name + ) # did we get rid of it? - if deleted['code'] == 200: - ret['comment'] = 'Node was successfully created but an error occurred during modification. ' \ - 'The creation of the node has been rolled back. Message is as follows:\n' \ - '{message}'.format(message=modified['content']['message']) + if deleted["code"] == 200: + ret["comment"] = ( + "Node was successfully created but an error occurred during modification. " + "The creation of the node has been rolled back. Message is as follows:\n" + "{message}".format(message=modified["content"]["message"]) + ) # something bad happened else: - ret['comment'] = 'Node was successfully created but an error occurred during modification. ' \ - 'The creation of the node was not able to be rolled back. Message is as follows:' \ - '\n {message}\n{message_two}'.format(message=modified['content']['message'], - message_two=deleted['content']['message']) + ret["comment"] = ( + "Node was successfully created but an error occurred during modification. " + "The creation of the node was not able to be rolled back. Message is as follows:" + "\n {message}\n{message_two}".format( + message=modified["content"]["message"], + message_two=deleted["content"]["message"], + ) + ) # unable to create it else: @@ -369,17 +432,22 @@ def manage_node(hostname, username, password, name, address, return ret -def modify_node(hostname, username, password, name, - connection_limit=None, - description=None, - dynamic_ratio=None, - logging=None, - monitor=None, - rate_limit=None, - ratio=None, - session=None, - node_state=None): - ''' +def modify_node( + hostname, + username, + password, + name, + connection_limit=None, + description=None, + dynamic_ratio=None, + logging=None, + monitor=None, + rate_limit=None, + ratio=None, + session=None, + node_state=None, +): + """ Modify an existing node. Only a node which already exists will be modified and only the parameters specified will be enforced. @@ -409,57 +477,62 @@ def modify_node(hostname, username, password, name, [user-enabled | user-disabled] node_state (state) [user-down | user-up ] - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'modify', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'connection_limit': connection_limit, - 'description': description, - 'dynamic_ratio': dynamic_ratio, - 'logging': logging, - 'monitor': monitor, - 'rate_limit': rate_limit, - 'ratio': ratio, - 'session': session, - 'state:': node_state - } + if __opts__["test"]: + return _test_output( + ret, + "modify", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "connection_limit": connection_limit, + "description": description, + "dynamic_ratio": dynamic_ratio, + "logging": logging, + "monitor": monitor, + "rate_limit": rate_limit, + "ratio": ratio, + "session": session, + "state:": node_state, + }, ) - #is this node currently configured? - existing = __salt__['bigip.list_node'](hostname, username, password, name) + # is this node currently configured? + existing = __salt__["bigip.list_node"](hostname, username, password, name) # if it exists by name - if existing['code'] == 200: + if existing["code"] == 200: - modified = __salt__['bigip.modify_node'](hostname=hostname, - username=username, - password=password, - name=name, - connection_limit=connection_limit, - description=description, - dynamic_ratio=dynamic_ratio, - logging=logging, - monitor=monitor, - rate_limit=rate_limit, - ratio=ratio, - session=session, - state=node_state) + modified = __salt__["bigip.modify_node"]( + hostname=hostname, + username=username, + password=password, + name=name, + connection_limit=connection_limit, + description=description, + dynamic_ratio=dynamic_ratio, + logging=logging, + monitor=monitor, + rate_limit=rate_limit, + ratio=ratio, + session=session, + state=node_state, + ) - #was the modification successful? - if modified['code'] == 200: - ret = _check_for_changes('Node', ret, existing, modified) + # was the modification successful? + if modified["code"] == 200: + ret = _check_for_changes("Node", ret, existing, modified) else: ret = _load_result(modified, ret) # not found, attempt to create it - elif existing['code'] == 404: - ret['comment'] = 'A node with this name was not found.' + elif existing["code"] == 404: + ret["comment"] = "A node with this name was not found." # an error occurred else: ret = _load_result(existing, ret) @@ -468,7 +541,7 @@ def modify_node(hostname, username, password, name, def delete_node(hostname, username, password, name): - ''' + """ Delete an existing node. hostname @@ -479,42 +552,45 @@ def delete_node(hostname, username, password, name): The iControl REST password name The name of the node which will be deleted. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'delete', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - } + if __opts__["test"]: + return _test_output( + ret, + "delete", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + }, ) - #is this node currently configured? - existing = __salt__['bigip.list_node'](hostname, username, password, name) + # is this node currently configured? + existing = __salt__["bigip.list_node"](hostname, username, password, name) # if it exists by name - if existing['code'] == 200: + if existing["code"] == 200: - deleted = __salt__['bigip.delete_node'](hostname, username, password, name) + deleted = __salt__["bigip.delete_node"](hostname, username, password, name) # did we get rid of it? - if deleted['code'] == 200: - ret['result'] = True - ret['comment'] = 'Node was successfully deleted.' - ret['changes']['old'] = existing['content'] - ret['changes']['new'] = {} + if deleted["code"] == 200: + ret["result"] = True + ret["comment"] = "Node was successfully deleted." + ret["changes"]["old"] = existing["content"] + ret["changes"]["new"] = {} # something bad happened else: ret = _load_result(existing, ret) # not found - elif existing['code'] == 404: - ret['result'] = True - ret['comment'] = 'This node already does not exist. No changes made.' - ret['changes']['old'] = {} - ret['changes']['new'] = {} + elif existing["code"] == 404: + ret["result"] = True + ret["comment"] = "This node already does not exist. No changes made." + ret["changes"]["old"] = {} + ret["changes"]["new"] = {} else: ret = _load_result(existing, ret) @@ -523,7 +599,7 @@ def delete_node(hostname, username, password, name): def list_pool(hostname, username, password, name): - ''' + """ A function to connect to a bigip device and list a specific pool. hostname @@ -534,47 +610,56 @@ def list_pool(hostname, username, password, name): The iControl REST password name The name of the pool to list. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'list', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - } + if __opts__["test"]: + return _test_output( + ret, + "list", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + }, ) - response = __salt__['bigip.list_pool'](hostname, username, password, name) + response = __salt__["bigip.list_pool"](hostname, username, password, name) return _load_result(response, ret) -def create_pool(hostname, username, password, name, members=None, - allow_nat=None, - allow_snat=None, - description=None, - gateway_failsafe_device=None, - ignore_persisted_weight=None, - ip_tos_to_client=None, - ip_tos_to_server=None, - link_qos_to_client=None, - link_qos_to_server=None, - load_balancing_mode=None, - min_active_members=None, - min_up_members=None, - min_up_members_action=None, - min_up_members_checking=None, - monitor=None, - profiles=None, - queue_depth_limit=None, - queue_on_connection_limit=None, - queue_time_limit=None, - reselect_tries=None, - service_down_action=None, - slow_ramp_time=None): - ''' +def create_pool( + hostname, + username, + password, + name, + members=None, + allow_nat=None, + allow_snat=None, + description=None, + gateway_failsafe_device=None, + ignore_persisted_weight=None, + ip_tos_to_client=None, + ip_tos_to_server=None, + link_qos_to_client=None, + link_qos_to_server=None, + load_balancing_mode=None, + min_active_members=None, + min_up_members=None, + min_up_members_action=None, + min_up_members_checking=None, + monitor=None, + profiles=None, + queue_depth_limit=None, + queue_on_connection_limit=None, + queue_time_limit=None, + reselect_tries=None, + service_down_action=None, + slow_ramp_time=None, +): + """ Create a new node if it does not already exist. hostname @@ -642,85 +727,90 @@ def create_pool(hostname, username, password, name, members=None, [drop | none | reselect | reset] slow_ramp_time [integer] - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'create', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'members': members, - 'allow_nat': allow_nat, - 'allow_snat': allow_snat, - 'description': description, - 'gateway_failsafe_device': gateway_failsafe_device, - 'ignore_persisted_weight': ignore_persisted_weight, - 'ip_tos_client:': ip_tos_to_client, - 'ip_tos_server': ip_tos_to_server, - 'link_qos_to_client': link_qos_to_client, - 'link_qos_to_server': link_qos_to_server, - 'load_balancing_mode': load_balancing_mode, - 'min_active_members': min_active_members, - 'min_up_members': min_up_members, - 'min_up_members_checking': min_up_members_checking, - 'monitor': monitor, - 'profiles': profiles, - 'queue_depth_limit': queue_depth_limit, - 'queue_on_connection_limit': queue_on_connection_limit, - 'queue_time_limit': queue_time_limit, - 'reselect_tries': reselect_tries, - 'service_down_action': service_down_action, - 'slow_ramp_time': slow_ramp_time - } + if __opts__["test"]: + return _test_output( + ret, + "create", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "members": members, + "allow_nat": allow_nat, + "allow_snat": allow_snat, + "description": description, + "gateway_failsafe_device": gateway_failsafe_device, + "ignore_persisted_weight": ignore_persisted_weight, + "ip_tos_client:": ip_tos_to_client, + "ip_tos_server": ip_tos_to_server, + "link_qos_to_client": link_qos_to_client, + "link_qos_to_server": link_qos_to_server, + "load_balancing_mode": load_balancing_mode, + "min_active_members": min_active_members, + "min_up_members": min_up_members, + "min_up_members_checking": min_up_members_checking, + "monitor": monitor, + "profiles": profiles, + "queue_depth_limit": queue_depth_limit, + "queue_on_connection_limit": queue_on_connection_limit, + "queue_time_limit": queue_time_limit, + "reselect_tries": reselect_tries, + "service_down_action": service_down_action, + "slow_ramp_time": slow_ramp_time, + }, ) - #is this pool currently configured? - existing = __salt__['bigip.list_pool'](hostname, username, password, name) + # is this pool currently configured? + existing = __salt__["bigip.list_pool"](hostname, username, password, name) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - ret['result'] = True - ret['comment'] = 'A pool by this name currently exists. No change made.' + ret["result"] = True + ret["comment"] = "A pool by this name currently exists. No change made." # if it doesn't exist - elif existing['code'] == 404: + elif existing["code"] == 404: - response = __salt__['bigip.create_pool'](hostname=hostname, - username=username, - password=password, - name=name, - members=members, - allow_nat=allow_nat, - allow_snat=allow_snat, - description=description, - gateway_failsafe_device=gateway_failsafe_device, - ignore_persisted_weight=ignore_persisted_weight, - ip_tos_to_client=ip_tos_to_client, - ip_tos_to_server=ip_tos_to_server, - link_qos_to_client=link_qos_to_client, - link_qos_to_server=link_qos_to_server, - load_balancing_mode=load_balancing_mode, - min_active_members=min_active_members, - min_up_members=min_up_members, - min_up_members_action=min_up_members_action, - min_up_members_checking=min_up_members_checking, - monitor=monitor, - profiles=profiles, - queue_depth_limit=queue_depth_limit, - queue_on_connection_limit=queue_on_connection_limit, - queue_time_limit=queue_time_limit, - reselect_tries=reselect_tries, - service_down_action=service_down_action, - slow_ramp_time=slow_ramp_time) - if response['code'] == 200: - ret['result'] = True - ret['changes']['old'] = {} - ret['changes']['new'] = response['content'] - ret['comment'] = 'Pool was successfully created.' + response = __salt__["bigip.create_pool"]( + hostname=hostname, + username=username, + password=password, + name=name, + members=members, + allow_nat=allow_nat, + allow_snat=allow_snat, + description=description, + gateway_failsafe_device=gateway_failsafe_device, + ignore_persisted_weight=ignore_persisted_weight, + ip_tos_to_client=ip_tos_to_client, + ip_tos_to_server=ip_tos_to_server, + link_qos_to_client=link_qos_to_client, + link_qos_to_server=link_qos_to_server, + load_balancing_mode=load_balancing_mode, + min_active_members=min_active_members, + min_up_members=min_up_members, + min_up_members_action=min_up_members_action, + min_up_members_checking=min_up_members_checking, + monitor=monitor, + profiles=profiles, + queue_depth_limit=queue_depth_limit, + queue_on_connection_limit=queue_on_connection_limit, + queue_time_limit=queue_time_limit, + reselect_tries=reselect_tries, + service_down_action=service_down_action, + slow_ramp_time=slow_ramp_time, + ) + if response["code"] == 200: + ret["result"] = True + ret["changes"]["old"] = {} + ret["changes"]["new"] = response["content"] + ret["comment"] = "Pool was successfully created." else: ret = _load_result(existing, ret) @@ -731,30 +821,35 @@ def create_pool(hostname, username, password, name, members=None, return ret -def manage_pool(hostname, username, password, name, - allow_nat=None, - allow_snat=None, - description=None, - gateway_failsafe_device=None, - ignore_persisted_weight=None, - ip_tos_to_client=None, - ip_tos_to_server=None, - link_qos_to_client=None, - link_qos_to_server=None, - load_balancing_mode=None, - min_active_members=None, - min_up_members=None, - min_up_members_action=None, - min_up_members_checking=None, - monitor=None, - profiles=None, - queue_depth_limit=None, - queue_on_connection_limit=None, - queue_time_limit=None, - reselect_tries=None, - service_down_action=None, - slow_ramp_time=None): - ''' +def manage_pool( + hostname, + username, + password, + name, + allow_nat=None, + allow_snat=None, + description=None, + gateway_failsafe_device=None, + ignore_persisted_weight=None, + ip_tos_to_client=None, + ip_tos_to_server=None, + link_qos_to_client=None, + link_qos_to_server=None, + load_balancing_mode=None, + min_active_members=None, + min_up_members=None, + min_up_members_action=None, + min_up_members_checking=None, + monitor=None, + profiles=None, + queue_depth_limit=None, + queue_on_connection_limit=None, + queue_time_limit=None, + reselect_tries=None, + service_down_action=None, + slow_ramp_time=None, +): + """ Create a new pool if it does not already exist. Pool members are managed separately. Only the parameters specified are enforced. @@ -822,125 +917,134 @@ def manage_pool(hostname, username, password, name, slow_ramp_time [integer] - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'manage', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'allow_nat': allow_nat, - 'allow_snat': allow_snat, - 'description': description, - 'gateway_failsafe_device': gateway_failsafe_device, - 'ignore_persisted_weight': ignore_persisted_weight, - 'ip_tos_client:': ip_tos_to_client, - 'ip_tos_server': ip_tos_to_server, - 'link_qos_to_client': link_qos_to_client, - 'link_qos_to_server': link_qos_to_server, - 'load_balancing_mode': load_balancing_mode, - 'min_active_members': min_active_members, - 'min_up_members': min_up_members, - 'min_up_members_checking': min_up_members_checking, - 'monitor': monitor, - 'profiles': profiles, - 'queue_depth_limit': queue_depth_limit, - 'queue_on_connection_limit': queue_on_connection_limit, - 'queue_time_limit': queue_time_limit, - 'reselect_tries': reselect_tries, - 'service_down_action': service_down_action, - 'slow_ramp_time': slow_ramp_time - } + if __opts__["test"]: + return _test_output( + ret, + "manage", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "allow_nat": allow_nat, + "allow_snat": allow_snat, + "description": description, + "gateway_failsafe_device": gateway_failsafe_device, + "ignore_persisted_weight": ignore_persisted_weight, + "ip_tos_client:": ip_tos_to_client, + "ip_tos_server": ip_tos_to_server, + "link_qos_to_client": link_qos_to_client, + "link_qos_to_server": link_qos_to_server, + "load_balancing_mode": load_balancing_mode, + "min_active_members": min_active_members, + "min_up_members": min_up_members, + "min_up_members_checking": min_up_members_checking, + "monitor": monitor, + "profiles": profiles, + "queue_depth_limit": queue_depth_limit, + "queue_on_connection_limit": queue_on_connection_limit, + "queue_time_limit": queue_time_limit, + "reselect_tries": reselect_tries, + "service_down_action": service_down_action, + "slow_ramp_time": slow_ramp_time, + }, ) - #is this pool currently configured? - existing = __salt__['bigip.list_pool'](hostname, username, password, name) + # is this pool currently configured? + existing = __salt__["bigip.list_pool"](hostname, username, password, name) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - modified = __salt__['bigip.modify_pool'](hostname=hostname, - username=username, - password=password, - name=name, - allow_nat=allow_nat, - allow_snat=allow_snat, - description=description, - gateway_failsafe_device=gateway_failsafe_device, - ignore_persisted_weight=ignore_persisted_weight, - ip_tos_to_client=ip_tos_to_client, - ip_tos_to_server=ip_tos_to_server, - link_qos_to_client=link_qos_to_client, - link_qos_to_server=link_qos_to_server, - load_balancing_mode=load_balancing_mode, - min_active_members=min_active_members, - min_up_members=min_up_members, - min_up_members_action=min_up_members_action, - min_up_members_checking=min_up_members_checking, - monitor=monitor, - profiles=profiles, - queue_depth_limit=queue_depth_limit, - queue_on_connection_limit=queue_on_connection_limit, - queue_time_limit=queue_time_limit, - reselect_tries=reselect_tries, - service_down_action=service_down_action, - slow_ramp_time=slow_ramp_time) + modified = __salt__["bigip.modify_pool"]( + hostname=hostname, + username=username, + password=password, + name=name, + allow_nat=allow_nat, + allow_snat=allow_snat, + description=description, + gateway_failsafe_device=gateway_failsafe_device, + ignore_persisted_weight=ignore_persisted_weight, + ip_tos_to_client=ip_tos_to_client, + ip_tos_to_server=ip_tos_to_server, + link_qos_to_client=link_qos_to_client, + link_qos_to_server=link_qos_to_server, + load_balancing_mode=load_balancing_mode, + min_active_members=min_active_members, + min_up_members=min_up_members, + min_up_members_action=min_up_members_action, + min_up_members_checking=min_up_members_checking, + monitor=monitor, + profiles=profiles, + queue_depth_limit=queue_depth_limit, + queue_on_connection_limit=queue_on_connection_limit, + queue_time_limit=queue_time_limit, + reselect_tries=reselect_tries, + service_down_action=service_down_action, + slow_ramp_time=slow_ramp_time, + ) - #was the modification successful? - if modified['code'] == 200: + # was the modification successful? + if modified["code"] == 200: - #remove member listings and self-links - del existing['content']['membersReference'] - del modified['content']['membersReference'] - del existing['content']['selfLink'] - del modified['content']['selfLink'] + # remove member listings and self-links + del existing["content"]["membersReference"] + del modified["content"]["membersReference"] + del existing["content"]["selfLink"] + del modified["content"]["selfLink"] - ret = _check_for_changes('Pool', ret, existing, modified) + ret = _check_for_changes("Pool", ret, existing, modified) else: ret = _load_result(modified, ret) # if it doesn't exist - elif existing['code'] == 404: + elif existing["code"] == 404: - new = __salt__['bigip.create_pool'](hostname=hostname, - username=username, - password=password, - name=name, - allow_nat=allow_nat, - allow_snat=allow_snat, - description=description, - gateway_failsafe_device=gateway_failsafe_device, - ignore_persisted_weight=ignore_persisted_weight, - ip_tos_to_client=ip_tos_to_client, - ip_tos_to_server=ip_tos_to_server, - link_qos_to_client=link_qos_to_client, - link_qos_to_server=link_qos_to_server, - load_balancing_mode=load_balancing_mode, - min_active_members=min_active_members, - min_up_members=min_up_members, - min_up_members_action=min_up_members_action, - min_up_members_checking=min_up_members_checking, - monitor=monitor, - profiles=profiles, - queue_depth_limit=queue_depth_limit, - queue_on_connection_limit=queue_on_connection_limit, - queue_time_limit=queue_time_limit, - reselect_tries=reselect_tries, - service_down_action=service_down_action, - slow_ramp_time=slow_ramp_time) + new = __salt__["bigip.create_pool"]( + hostname=hostname, + username=username, + password=password, + name=name, + allow_nat=allow_nat, + allow_snat=allow_snat, + description=description, + gateway_failsafe_device=gateway_failsafe_device, + ignore_persisted_weight=ignore_persisted_weight, + ip_tos_to_client=ip_tos_to_client, + ip_tos_to_server=ip_tos_to_server, + link_qos_to_client=link_qos_to_client, + link_qos_to_server=link_qos_to_server, + load_balancing_mode=load_balancing_mode, + min_active_members=min_active_members, + min_up_members=min_up_members, + min_up_members_action=min_up_members_action, + min_up_members_checking=min_up_members_checking, + monitor=monitor, + profiles=profiles, + queue_depth_limit=queue_depth_limit, + queue_on_connection_limit=queue_on_connection_limit, + queue_time_limit=queue_time_limit, + reselect_tries=reselect_tries, + service_down_action=service_down_action, + slow_ramp_time=slow_ramp_time, + ) # were we able to create it? - if new['code'] == 200: - ret['result'] = True - ret['comment'] = 'Pool was created and enforced to the desired state. Note: Only parameters specified ' \ - 'were enforced. See changes for details.' - ret['changes']['old'] = {} - ret['changes']['new'] = new['content'] + if new["code"] == 200: + ret["result"] = True + ret["comment"] = ( + "Pool was created and enforced to the desired state. Note: Only parameters specified " + "were enforced. See changes for details." + ) + ret["changes"]["old"] = {} + ret["changes"]["new"] = new["content"] - # unable to create it + # unable to create it else: ret = _load_result(new, ret) @@ -951,30 +1055,35 @@ def manage_pool(hostname, username, password, name, return ret -def modify_pool(hostname, username, password, name, - allow_nat=None, - allow_snat=None, - description=None, - gateway_failsafe_device=None, - ignore_persisted_weight=None, - ip_tos_to_client=None, - ip_tos_to_server=None, - link_qos_to_client=None, - link_qos_to_server=None, - load_balancing_mode=None, - min_active_members=None, - min_up_members=None, - min_up_members_action=None, - min_up_members_checking=None, - monitor=None, - profiles=None, - queue_depth_limit=None, - queue_on_connection_limit=None, - queue_time_limit=None, - reselect_tries=None, - service_down_action=None, - slow_ramp_time=None): - ''' +def modify_pool( + hostname, + username, + password, + name, + allow_nat=None, + allow_snat=None, + description=None, + gateway_failsafe_device=None, + ignore_persisted_weight=None, + ip_tos_to_client=None, + ip_tos_to_server=None, + link_qos_to_client=None, + link_qos_to_server=None, + load_balancing_mode=None, + min_active_members=None, + min_up_members=None, + min_up_members_action=None, + min_up_members_checking=None, + monitor=None, + profiles=None, + queue_depth_limit=None, + queue_on_connection_limit=None, + queue_time_limit=None, + reselect_tries=None, + service_down_action=None, + slow_ramp_time=None, +): + """ Modify an existing pool. Pool members are managed separately. Only the parameters specified are enforced. @@ -1042,89 +1151,94 @@ def modify_pool(hostname, username, password, name, slow_ramp_time [integer] - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'modify', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'allow_nat': allow_nat, - 'allow_snat': allow_snat, - 'description': description, - 'gateway_failsafe_device': gateway_failsafe_device, - 'ignore_persisted_weight': ignore_persisted_weight, - 'ip_tos_client:': ip_tos_to_client, - 'ip_tos_server': ip_tos_to_server, - 'link_qos_to_client': link_qos_to_client, - 'link_qos_to_server': link_qos_to_server, - 'load_balancing_mode': load_balancing_mode, - 'min_active_members': min_active_members, - 'min_up_members': min_up_members, - 'min_up_members_checking': min_up_members_checking, - 'monitor': monitor, - 'profiles': profiles, - 'queue_depth_limit': queue_depth_limit, - 'queue_on_connection_limit': queue_on_connection_limit, - 'queue_time_limit': queue_time_limit, - 'reselect_tries': reselect_tries, - 'service_down_action': service_down_action, - 'slow_ramp_time': slow_ramp_time - } + if __opts__["test"]: + return _test_output( + ret, + "modify", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "allow_nat": allow_nat, + "allow_snat": allow_snat, + "description": description, + "gateway_failsafe_device": gateway_failsafe_device, + "ignore_persisted_weight": ignore_persisted_weight, + "ip_tos_client:": ip_tos_to_client, + "ip_tos_server": ip_tos_to_server, + "link_qos_to_client": link_qos_to_client, + "link_qos_to_server": link_qos_to_server, + "load_balancing_mode": load_balancing_mode, + "min_active_members": min_active_members, + "min_up_members": min_up_members, + "min_up_members_checking": min_up_members_checking, + "monitor": monitor, + "profiles": profiles, + "queue_depth_limit": queue_depth_limit, + "queue_on_connection_limit": queue_on_connection_limit, + "queue_time_limit": queue_time_limit, + "reselect_tries": reselect_tries, + "service_down_action": service_down_action, + "slow_ramp_time": slow_ramp_time, + }, ) - #is this pool currently configured? - existing = __salt__['bigip.list_pool'](hostname, username, password, name) + # is this pool currently configured? + existing = __salt__["bigip.list_pool"](hostname, username, password, name) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - modified = __salt__['bigip.modify_pool'](hostname=hostname, - username=username, - password=password, - name=name, - allow_nat=allow_nat, - allow_snat=allow_snat, - description=description, - gateway_failsafe_device=gateway_failsafe_device, - ignore_persisted_weight=ignore_persisted_weight, - ip_tos_to_client=ip_tos_to_client, - ip_tos_to_server=ip_tos_to_server, - link_qos_to_client=link_qos_to_client, - link_qos_to_server=link_qos_to_server, - load_balancing_mode=load_balancing_mode, - min_active_members=min_active_members, - min_up_members=min_up_members, - min_up_members_action=min_up_members_action, - min_up_members_checking=min_up_members_checking, - monitor=monitor, - profiles=profiles, - queue_depth_limit=queue_depth_limit, - queue_on_connection_limit=queue_on_connection_limit, - queue_time_limit=queue_time_limit, - reselect_tries=reselect_tries, - service_down_action=service_down_action, - slow_ramp_time=slow_ramp_time) + modified = __salt__["bigip.modify_pool"]( + hostname=hostname, + username=username, + password=password, + name=name, + allow_nat=allow_nat, + allow_snat=allow_snat, + description=description, + gateway_failsafe_device=gateway_failsafe_device, + ignore_persisted_weight=ignore_persisted_weight, + ip_tos_to_client=ip_tos_to_client, + ip_tos_to_server=ip_tos_to_server, + link_qos_to_client=link_qos_to_client, + link_qos_to_server=link_qos_to_server, + load_balancing_mode=load_balancing_mode, + min_active_members=min_active_members, + min_up_members=min_up_members, + min_up_members_action=min_up_members_action, + min_up_members_checking=min_up_members_checking, + monitor=monitor, + profiles=profiles, + queue_depth_limit=queue_depth_limit, + queue_on_connection_limit=queue_on_connection_limit, + queue_time_limit=queue_time_limit, + reselect_tries=reselect_tries, + service_down_action=service_down_action, + slow_ramp_time=slow_ramp_time, + ) - #was the modification successful? - if modified['code'] == 200: + # was the modification successful? + if modified["code"] == 200: - #remove member listings and self-links - del existing['content']['membersReference'] - del modified['content']['membersReference'] - del existing['content']['selfLink'] - del modified['content']['selfLink'] + # remove member listings and self-links + del existing["content"]["membersReference"] + del modified["content"]["membersReference"] + del existing["content"]["selfLink"] + del modified["content"]["selfLink"] - ret = _check_for_changes('Pool', ret, existing, modified) + ret = _check_for_changes("Pool", ret, existing, modified) else: ret = _load_result(modified, ret) # if it doesn't exist - elif existing['code'] == 404: - ret['comment'] = 'A pool with this name was not found.' + elif existing["code"] == 404: + ret["comment"] = "A pool with this name was not found." # else something else was returned else: ret = _load_result(existing, ret) @@ -1133,7 +1247,7 @@ def modify_pool(hostname, username, password, name, def delete_pool(hostname, username, password, name): - ''' + """ Delete an existing pool. hostname @@ -1144,42 +1258,45 @@ def delete_pool(hostname, username, password, name): The iControl REST password name The name of the pool which will be deleted - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'delete', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - } + if __opts__["test"]: + return _test_output( + ret, + "delete", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + }, ) - #is this pool currently configured? - existing = __salt__['bigip.list_pool'](hostname, username, password, name) + # is this pool currently configured? + existing = __salt__["bigip.list_pool"](hostname, username, password, name) # if it exists by name - if existing['code'] == 200: + if existing["code"] == 200: - deleted = __salt__['bigip.delete_pool'](hostname, username, password, name) + deleted = __salt__["bigip.delete_pool"](hostname, username, password, name) # did we get rid of it? - if deleted['code'] == 200: - ret['result'] = True - ret['comment'] = 'Pool was successfully deleted.' - ret['changes']['old'] = existing['content'] - ret['changes']['new'] = {} + if deleted["code"] == 200: + ret["result"] = True + ret["comment"] = "Pool was successfully deleted." + ret["changes"]["old"] = existing["content"] + ret["changes"]["new"] = {} # something bad happened else: ret = _load_result(deleted, ret) # not found - elif existing['code'] == 404: - ret['result'] = True - ret['comment'] = 'This pool already does not exist. No changes made.' - ret['changes']['old'] = {} - ret['changes']['new'] = {} + elif existing["code"] == 404: + ret["result"] = True + ret["comment"] = "This pool already does not exist. No changes made." + ret["changes"]["old"] = {} + ret["changes"]["new"] = {} else: ret = _load_result(existing, ret) @@ -1188,7 +1305,7 @@ def delete_pool(hostname, username, password, name): def manage_pool_members(hostname, username, password, name, members): - ''' + """ Manage the members of an existing pool. This function replaces all current pool members. Only the parameters specified are enforced. @@ -1203,62 +1320,73 @@ def manage_pool_members(hostname, username, password, name, members): members list of pool members to manage. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'manage', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'members': members - } + if __opts__["test"]: + return _test_output( + ret, + "manage", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "members": members, + }, ) - #is this pool currently configured? - existing = __salt__['bigip.list_pool'](hostname, username, password, name) + # is this pool currently configured? + existing = __salt__["bigip.list_pool"](hostname, username, password, name) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - #what are the current members? - current_members = existing['content']['membersReference']['items'] + # what are the current members? + current_members = existing["content"]["membersReference"]["items"] - modified = __salt__['bigip.replace_pool_members'](hostname, username, password, name, members) + modified = __salt__["bigip.replace_pool_members"]( + hostname, username, password, name, members + ) - #was the modification successful? - if modified['code'] == 200: + # was the modification successful? + if modified["code"] == 200: - #re-list the pool with new membership - new_listing = __salt__['bigip.list_pool'](hostname, username, password, name) + # re-list the pool with new membership + new_listing = __salt__["bigip.list_pool"]( + hostname, username, password, name + ) - #just in case something happened... - if new_listing['code'] != 200: + # just in case something happened... + if new_listing["code"] != 200: ret = _load_result(new_listing, ret) - ret['comment'] = 'modification of the pool was successful but an error occurred upon retrieving new' \ - ' listing.' + ret["comment"] = ( + "modification of the pool was successful but an error occurred upon retrieving new" + " listing." + ) return ret - new_members = new_listing['content']['membersReference']['items'] + new_members = new_listing["content"]["membersReference"]["items"] - #remove generation keys and create new lists indexed by integers + # remove generation keys and create new lists indexed by integers for current_member in current_members: - del current_member['generation'] + del current_member["generation"] for new_member in new_members: - del new_member['generation'] + del new_member["generation"] - #anything changed? - ret = _check_for_changes('Pool Membership', ret, current_members, new_members) + # anything changed? + ret = _check_for_changes( + "Pool Membership", ret, current_members, new_members + ) else: ret = _load_result(modified, ret) - #pool does not exists - elif existing['code'] == 404: - ret['comment'] = 'A pool with this name was not found.' + # pool does not exists + elif existing["code"] == 404: + ret["comment"] = "A pool with this name was not found." else: ret = _load_result(existing, ret) @@ -1267,7 +1395,7 @@ def manage_pool_members(hostname, username, password, name, members): def add_pool_member(hostname, username, password, name, member): - ''' + """ A function to connect to a bigip device and add a new member to an existing pool. hostname @@ -1280,95 +1408,116 @@ def add_pool_member(hostname, username, password, name, member): The name of the pool to modify member The member to add to the pool - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'add', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'members': member - } + if __opts__["test"]: + return _test_output( + ret, + "add", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "members": member, + }, ) - #is this pool member currently configured? - existing_pool = __salt__['bigip.list_pool'](hostname, username, password, name) + # is this pool member currently configured? + existing_pool = __salt__["bigip.list_pool"](hostname, username, password, name) - if existing_pool['code'] == 200: + if existing_pool["code"] == 200: # for some reason iControl REST doesn't support listing a single pool member. # the response from GET for listing a member will return 200 even if it doesn't exists. # because of this we have to do some rather "unnecessary" searching within a pool. - #what are the current members? - current_members = existing_pool['content']['membersReference']['items'] + # what are the current members? + current_members = existing_pool["content"]["membersReference"]["items"] - #loop through them + # loop through them exists = False for current_member in current_members: - if current_member['name'] == member['name']: + if current_member["name"] == member["name"]: exists = True break if exists: - ret['result'] = True - ret['comment'] = 'Member: {name} already exists within this pool. No changes made.'.format(name=member['name']) - ret['changes']['old'] = {} - ret['changes']['new'] = {} + ret["result"] = True + ret[ + "comment" + ] = "Member: {name} already exists within this pool. No changes made.".format( + name=member["name"] + ) + ret["changes"]["old"] = {} + ret["changes"]["new"] = {} else: - new_member = __salt__['bigip.add_pool_member'](hostname, username, password, name, member) + new_member = __salt__["bigip.add_pool_member"]( + hostname, username, password, name, member + ) - if new_member['code'] == 200: - ret['result'] = True - ret['comment'] = 'Member: {name} has been successfully added to the pool.'.format(name=member['name']) - ret['changes']['old'] = {} + if new_member["code"] == 200: + ret["result"] = True + ret[ + "comment" + ] = "Member: {name} has been successfully added to the pool.".format( + name=member["name"] + ) + ret["changes"]["old"] = {} - #look up the member again... - pool_listing = __salt__['bigip.list_pool'](hostname, username, password, name) + # look up the member again... + pool_listing = __salt__["bigip.list_pool"]( + hostname, username, password, name + ) - if pool_listing['code'] != 200: + if pool_listing["code"] != 200: ret = _load_result(new_member, ret) return ret - members = pool_listing['content']['membersReference']['items'] - #loop through them + members = pool_listing["content"]["membersReference"]["items"] + # loop through them for current_member in members: - if current_member['name'] == member['name']: + if current_member["name"] == member["name"]: added_member = current_member break - ret['changes']['new'] = added_member + ret["changes"]["new"] = added_member # member wasn't added else: ret = _load_result(new_member, ret) - #pool does not exists - elif existing_pool['code'] == 404: - ret['comment'] = 'A pool with this name was not found.' + # pool does not exists + elif existing_pool["code"] == 404: + ret["comment"] = "A pool with this name was not found." else: ret = _load_result(existing_pool, ret) return ret -def modify_pool_member(hostname, username, password, name, member, - connection_limit=None, - description=None, - dynamic_ratio=None, - inherit_profile=None, - logging=None, - monitor=None, - priority_group=None, - profiles=None, - rate_limit=None, - ratio=None, - session=None, - member_state=None): - ''' +def modify_pool_member( + hostname, + username, + password, + name, + member, + connection_limit=None, + description=None, + dynamic_ratio=None, + inherit_profile=None, + logging=None, + monitor=None, + priority_group=None, + profiles=None, + rate_limit=None, + ratio=None, + session=None, + member_state=None, +): + """ A function to connect to a bigip device and modify a member of an existing pool. hostname @@ -1406,88 +1555,99 @@ def modify_pool_member(hostname, username, password, name, member, member_state (state) [ user-up | user-down ] - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'modify', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'members': member - } + if __opts__["test"]: + return _test_output( + ret, + "modify", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "members": member, + }, ) - #is this pool member currently configured? - existing_pool = __salt__['bigip.list_pool'](hostname, username, password, name) + # is this pool member currently configured? + existing_pool = __salt__["bigip.list_pool"](hostname, username, password, name) - if existing_pool['code'] == 200: + if existing_pool["code"] == 200: # for some reason iControl REST doesn't support listing a single pool member. # the response from GET for listing a member will return 200 even if it doesn't exists. # because of this we have to do some rather "unnecessary" searching within a pool. - #what are the current members? - current_members = existing_pool['content']['membersReference']['items'] + # what are the current members? + current_members = existing_pool["content"]["membersReference"]["items"] - #loop through them + # loop through them exists = False for current_member in current_members: - if current_member['name'] == member: + if current_member["name"] == member: exists = True existing_member = current_member break if exists: - #modify the pool member - modified = __salt__['bigip.modify_pool_member'](hostname=hostname, - username=username, - password=password, - name=name, - member=member, - connection_limit=connection_limit, - description=description, - dynamic_ratio=dynamic_ratio, - inherit_profile=inherit_profile, - logging=logging, - monitor=monitor, - priority_group=priority_group, - profiles=profiles, - rate_limit=rate_limit, - ratio=ratio, - session=session, - state=member_state) + # modify the pool member + modified = __salt__["bigip.modify_pool_member"]( + hostname=hostname, + username=username, + password=password, + name=name, + member=member, + connection_limit=connection_limit, + description=description, + dynamic_ratio=dynamic_ratio, + inherit_profile=inherit_profile, + logging=logging, + monitor=monitor, + priority_group=priority_group, + profiles=profiles, + rate_limit=rate_limit, + ratio=ratio, + session=session, + state=member_state, + ) - #re-list the pool - new_pool = __salt__['bigip.list_pool'](hostname, username, password, name) + # re-list the pool + new_pool = __salt__["bigip.list_pool"](hostname, username, password, name) - if modified['code'] == 200 and modified['code'] == 200: + if modified["code"] == 200 and modified["code"] == 200: - #what are the new members? - new_members = new_pool['content']['membersReference']['items'] + # what are the new members? + new_members = new_pool["content"]["membersReference"]["items"] - #loop through them + # loop through them for new_member in new_members: - if new_member['name'] == member: + if new_member["name"] == member: modified_member = new_member break - #check for changes - old = {'content': existing_member} - new = {'content': modified_member} - ret = _check_for_changes('Pool Member: {member}'.format(member=member), ret, old, new) + # check for changes + old = {"content": existing_member} + new = {"content": modified_member} + ret = _check_for_changes( + "Pool Member: {member}".format(member=member), ret, old, new + ) else: ret = _load_result(modified, ret) else: - ret['comment'] = 'Member: {name} does not exists within this pool. No changes made.'.format(name=member['name']) + ret[ + "comment" + ] = "Member: {name} does not exists within this pool. No changes made.".format( + name=member["name"] + ) - #pool does not exists - elif existing_pool['code'] == 404: - ret['comment'] = 'A pool with this name was not found.' + # pool does not exists + elif existing_pool["code"] == 404: + ret["comment"] = "A pool with this name was not found." else: ret = _load_result(existing_pool, ret) @@ -1495,7 +1655,7 @@ def modify_pool_member(hostname, username, password, name, member, def delete_pool_member(hostname, username, password, name, member): - ''' + """ Delete an existing pool member. hostname @@ -1508,52 +1668,61 @@ def delete_pool_member(hostname, username, password, name, member): The name of the pool to be modified member The name of the member to delete from the pool - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'delete', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'members': member - } + if __opts__["test"]: + return _test_output( + ret, + "delete", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "members": member, + }, ) - #is this pool currently configured? - existing = __salt__['bigip.list_pool'](hostname, username, password, name) + # is this pool currently configured? + existing = __salt__["bigip.list_pool"](hostname, username, password, name) # if it exists by name - if existing['code'] == 200: + if existing["code"] == 200: - #what are the current members? - current_members = existing['content']['membersReference']['items'] + # what are the current members? + current_members = existing["content"]["membersReference"]["items"] - #loop through them + # loop through them exists = False for current_member in current_members: - if current_member['name'] == member: + if current_member["name"] == member: exists = True existing_member = current_member break if exists: - deleted = __salt__['bigip.delete_pool_member'](hostname, username, password, name, member) + deleted = __salt__["bigip.delete_pool_member"]( + hostname, username, password, name, member + ) # did we get rid of it? - if deleted['code'] == 200: - ret['result'] = True - ret['comment'] = 'Pool Member: {member} was successfully deleted.'.format(member=member) - ret['changes']['old'] = existing_member - ret['changes']['new'] = {} + if deleted["code"] == 200: + ret["result"] = True + ret[ + "comment" + ] = "Pool Member: {member} was successfully deleted.".format( + member=member + ) + ret["changes"]["old"] = existing_member + ret["changes"]["new"] = {} # something bad happened else: - ret['result'] = True - ret['comment'] = 'This pool member already does not exist. No changes made.' - ret['changes']['old'] = {} - ret['changes']['new'] = {} + ret["result"] = True + ret["comment"] = "This pool member already does not exist. No changes made." + ret["changes"]["old"] = {} + ret["changes"]["new"] = {} else: ret = _load_result(existing, ret) @@ -1562,7 +1731,7 @@ def delete_pool_member(hostname, username, password, name, member): def list_virtual(hostname, username, password, name): - ''' + """ A function to list a specific virtual. hostname @@ -1573,63 +1742,72 @@ def list_virtual(hostname, username, password, name): The iControl REST password name The name of the virtual to list - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'list', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name - } + if __opts__["test"]: + return _test_output( + ret, + "list", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + }, ) - response = __salt__['bigip.list_virtual'](hostname, username, password, name) + response = __salt__["bigip.list_virtual"](hostname, username, password, name) return _load_result(response, ret) -def create_virtual(hostname, username, password, name, destination, - pool=None, - address_status=None, - auto_lasthop=None, - bwc_policy=None, - cmp_enabled=None, - connection_limit=None, - dhcp_relay=None, - description=None, - fallback_persistence=None, - flow_eviction_policy=None, - gtm_score=None, - ip_forward=None, - ip_protocol=None, - internal=None, - twelve_forward=None, - last_hop_pool=None, - mask=None, - mirror=None, - nat64=None, - persist=None, - profiles=None, - policies=None, - rate_class=None, - rate_limit=None, - rate_limit_mode=None, - rate_limit_dst=None, - rate_limit_src=None, - rules=None, - related_rules=None, - reject=None, - source=None, - source_address_translation=None, - source_port=None, - virtual_state=None, - traffic_classes=None, - translate_address=None, - translate_port=None, - vlans=None): - ''' +def create_virtual( + hostname, + username, + password, + name, + destination, + pool=None, + address_status=None, + auto_lasthop=None, + bwc_policy=None, + cmp_enabled=None, + connection_limit=None, + dhcp_relay=None, + description=None, + fallback_persistence=None, + flow_eviction_policy=None, + gtm_score=None, + ip_forward=None, + ip_protocol=None, + internal=None, + twelve_forward=None, + last_hop_pool=None, + mask=None, + mirror=None, + nat64=None, + persist=None, + profiles=None, + policies=None, + rate_class=None, + rate_limit=None, + rate_limit_mode=None, + rate_limit_dst=None, + rate_limit_src=None, + rules=None, + related_rules=None, + reject=None, + source=None, + source_address_translation=None, + source_port=None, + virtual_state=None, + traffic_classes=None, + translate_address=None, + translate_port=None, + vlans=None, +): + """ A function to connect to a bigip device and create a virtual server if it does not already exists. hostname @@ -1727,117 +1905,122 @@ def create_virtual(hostname, username, password, name, destination, [ list] enabled [ true | false ] - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'create', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'destination': destination, - 'pool': pool, - 'address_status': address_status, - 'auto_lasthop': auto_lasthop, - 'bwc_policy': bwc_policy, - 'cmp_enabled': cmp_enabled, - 'connection_limit': connection_limit, - 'dhcp_relay': dhcp_relay, - 'description': description, - 'fallback_persistence': fallback_persistence, - 'flow_eviction_policy': flow_eviction_policy, - 'gtm_score': gtm_score, - 'ip_forward': ip_forward, - 'ip_protocol': ip_protocol, - 'internal': internal, - 'twelve_forward': twelve_forward, - 'last_hop_pool': last_hop_pool, - 'mask': mask, - 'mirror': mirror, - 'nat64': nat64, - 'persist': persist, - 'profiles': profiles, - 'policies': policies, - 'rate_class': rate_class, - 'rate_limit': rate_limit, - 'rate_limit_mode': rate_limit_mode, - 'rate_limit_dst': rate_limit_dst, - 'rate_limit_src': rate_limit_src, - 'rules': rules, - 'related_rules': related_rules, - 'reject': reject, - 'source': source, - 'source_address_translation': source_address_translation, - 'source_port': source_port, - 'virtual_state': virtual_state, - 'traffic_classes': traffic_classes, - 'translate_address': translate_address, - 'translate_port': translate_port, - 'vlans': vlans - } + if __opts__["test"]: + return _test_output( + ret, + "create", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "destination": destination, + "pool": pool, + "address_status": address_status, + "auto_lasthop": auto_lasthop, + "bwc_policy": bwc_policy, + "cmp_enabled": cmp_enabled, + "connection_limit": connection_limit, + "dhcp_relay": dhcp_relay, + "description": description, + "fallback_persistence": fallback_persistence, + "flow_eviction_policy": flow_eviction_policy, + "gtm_score": gtm_score, + "ip_forward": ip_forward, + "ip_protocol": ip_protocol, + "internal": internal, + "twelve_forward": twelve_forward, + "last_hop_pool": last_hop_pool, + "mask": mask, + "mirror": mirror, + "nat64": nat64, + "persist": persist, + "profiles": profiles, + "policies": policies, + "rate_class": rate_class, + "rate_limit": rate_limit, + "rate_limit_mode": rate_limit_mode, + "rate_limit_dst": rate_limit_dst, + "rate_limit_src": rate_limit_src, + "rules": rules, + "related_rules": related_rules, + "reject": reject, + "source": source, + "source_address_translation": source_address_translation, + "source_port": source_port, + "virtual_state": virtual_state, + "traffic_classes": traffic_classes, + "translate_address": translate_address, + "translate_port": translate_port, + "vlans": vlans, + }, ) - existing = __salt__['bigip.list_virtual'](hostname, username, password, name) + existing = __salt__["bigip.list_virtual"](hostname, username, password, name) # does this virtual exist? - if existing['code'] == 200: + if existing["code"] == 200: - ret['result'] = True - ret['comment'] = 'A virtual by this name currently exists. No change made.' + ret["result"] = True + ret["comment"] = "A virtual by this name currently exists. No change made." - elif existing['code'] == 404: + elif existing["code"] == 404: - #create it - virtual = __salt__['bigip.create_virtual'](hostname=hostname, - username=username, - password=password, - name=name, - destination=destination, - description=description, - pool=pool, - address_status=address_status, - auto_lasthop=auto_lasthop, - bwc_policy=bwc_policy, - cmp_enabled=cmp_enabled, - connection_limit=connection_limit, - dhcp_relay=dhcp_relay, - fallback_persistence=fallback_persistence, - flow_eviction_policy=flow_eviction_policy, - gtm_score=gtm_score, - ip_forward=ip_forward, - ip_protocol=ip_protocol, - internal=internal, - twelve_forward=twelve_forward, - last_hop_pool=last_hop_pool, - mask=mask, - mirror=mirror, - nat64=nat64, - persist=persist, - profiles=profiles, - policies=policies, - rate_class=rate_class, - rate_limit=rate_limit, - rate_limit_mode=rate_limit_mode, - rate_limit_dst=rate_limit_dst, - rate_limit_src=rate_limit_src, - rules=rules, - related_rules=related_rules, - reject=reject, - source=source, - source_address_translation=source_address_translation, - source_port=source_port, - state=virtual_state, - traffic_classes=traffic_classes, - translate_address=translate_address, - translate_port=translate_port, - vlans=vlans) - if virtual['code'] == 200: - ret['result'] = True - ret['changes']['old'] = {} - ret['changes']['new'] = virtual['content'] - ret['comment'] = 'Virtual was successfully created.' + # create it + virtual = __salt__["bigip.create_virtual"]( + hostname=hostname, + username=username, + password=password, + name=name, + destination=destination, + description=description, + pool=pool, + address_status=address_status, + auto_lasthop=auto_lasthop, + bwc_policy=bwc_policy, + cmp_enabled=cmp_enabled, + connection_limit=connection_limit, + dhcp_relay=dhcp_relay, + fallback_persistence=fallback_persistence, + flow_eviction_policy=flow_eviction_policy, + gtm_score=gtm_score, + ip_forward=ip_forward, + ip_protocol=ip_protocol, + internal=internal, + twelve_forward=twelve_forward, + last_hop_pool=last_hop_pool, + mask=mask, + mirror=mirror, + nat64=nat64, + persist=persist, + profiles=profiles, + policies=policies, + rate_class=rate_class, + rate_limit=rate_limit, + rate_limit_mode=rate_limit_mode, + rate_limit_dst=rate_limit_dst, + rate_limit_src=rate_limit_src, + rules=rules, + related_rules=related_rules, + reject=reject, + source=source, + source_address_translation=source_address_translation, + source_port=source_port, + state=virtual_state, + traffic_classes=traffic_classes, + translate_address=translate_address, + translate_port=translate_port, + vlans=vlans, + ) + if virtual["code"] == 200: + ret["result"] = True + ret["changes"]["old"] = {} + ret["changes"]["new"] = virtual["content"] + ret["comment"] = "Virtual was successfully created." else: ret = _load_result(existing, ret) @@ -1848,46 +2031,52 @@ def create_virtual(hostname, username, password, name, destination, return ret -def manage_virtual(hostname, username, password, name, destination, - pool=None, - address_status=None, - auto_lasthop=None, - bwc_policy=None, - cmp_enabled=None, - connection_limit=None, - dhcp_relay=None, - description=None, - fallback_persistence=None, - flow_eviction_policy=None, - gtm_score=None, - ip_forward=None, - ip_protocol=None, - internal=None, - twelve_forward=None, - last_hop_pool=None, - mask=None, - mirror=None, - nat64=None, - persist=None, - profiles=None, - policies=None, - rate_class=None, - rate_limit=None, - rate_limit_mode=None, - rate_limit_dst=None, - rate_limit_src=None, - rules=None, - related_rules=None, - reject=None, - source=None, - source_address_translation=None, - source_port=None, - virtual_state=None, - traffic_classes=None, - translate_address=None, - translate_port=None, - vlans=None): - ''' +def manage_virtual( + hostname, + username, + password, + name, + destination, + pool=None, + address_status=None, + auto_lasthop=None, + bwc_policy=None, + cmp_enabled=None, + connection_limit=None, + dhcp_relay=None, + description=None, + fallback_persistence=None, + flow_eviction_policy=None, + gtm_score=None, + ip_forward=None, + ip_protocol=None, + internal=None, + twelve_forward=None, + last_hop_pool=None, + mask=None, + mirror=None, + nat64=None, + persist=None, + profiles=None, + policies=None, + rate_class=None, + rate_limit=None, + rate_limit_mode=None, + rate_limit_dst=None, + rate_limit_src=None, + rules=None, + related_rules=None, + reject=None, + source=None, + source_address_translation=None, + source_port=None, + virtual_state=None, + traffic_classes=None, + translate_address=None, + translate_port=None, + vlans=None, +): + """ Manage a virtual server. If a virtual does not exists it will be created, otherwise only the parameters specified will be enforced. @@ -1986,179 +2175,190 @@ def manage_virtual(hostname, username, password, name, destination, [ list] enabled [ true | false ] - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'manage', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'destination': destination, - 'pool': pool, - 'address_status': address_status, - 'auto_lasthop': auto_lasthop, - 'bwc_policy': bwc_policy, - 'cmp_enabled': cmp_enabled, - 'connection_limit': connection_limit, - 'dhcp_relay': dhcp_relay, - 'description': description, - 'fallback_persistence': fallback_persistence, - 'flow_eviction_policy': flow_eviction_policy, - 'gtm_score': gtm_score, - 'ip_forward': ip_forward, - 'ip_protocol': ip_protocol, - 'internal': internal, - 'twelve_forward': twelve_forward, - 'last_hop_pool': last_hop_pool, - 'mask': mask, - 'mirror': mirror, - 'nat64': nat64, - 'persist': persist, - 'profiles': profiles, - 'policies': policies, - 'rate_class': rate_class, - 'rate_limit': rate_limit, - 'rate_limit_mode': rate_limit_mode, - 'rate_limit_dst': rate_limit_dst, - 'rate_limit_src': rate_limit_src, - 'rules': rules, - 'related_rules': related_rules, - 'reject': reject, - 'source': source, - 'source_address_translation': source_address_translation, - 'source_port': source_port, - 'virtual_state': virtual_state, - 'traffic_classes': traffic_classes, - 'translate_address': translate_address, - 'translate_port': translate_port, - 'vlans': vlans - } + if __opts__["test"]: + return _test_output( + ret, + "manage", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "destination": destination, + "pool": pool, + "address_status": address_status, + "auto_lasthop": auto_lasthop, + "bwc_policy": bwc_policy, + "cmp_enabled": cmp_enabled, + "connection_limit": connection_limit, + "dhcp_relay": dhcp_relay, + "description": description, + "fallback_persistence": fallback_persistence, + "flow_eviction_policy": flow_eviction_policy, + "gtm_score": gtm_score, + "ip_forward": ip_forward, + "ip_protocol": ip_protocol, + "internal": internal, + "twelve_forward": twelve_forward, + "last_hop_pool": last_hop_pool, + "mask": mask, + "mirror": mirror, + "nat64": nat64, + "persist": persist, + "profiles": profiles, + "policies": policies, + "rate_class": rate_class, + "rate_limit": rate_limit, + "rate_limit_mode": rate_limit_mode, + "rate_limit_dst": rate_limit_dst, + "rate_limit_src": rate_limit_src, + "rules": rules, + "related_rules": related_rules, + "reject": reject, + "source": source, + "source_address_translation": source_address_translation, + "source_port": source_port, + "virtual_state": virtual_state, + "traffic_classes": traffic_classes, + "translate_address": translate_address, + "translate_port": translate_port, + "vlans": vlans, + }, ) - existing = __salt__['bigip.list_virtual'](hostname, username, password, name) + existing = __salt__["bigip.list_virtual"](hostname, username, password, name) # does this virtual exist? - if existing['code'] == 200: + if existing["code"] == 200: # modify - modified = __salt__['bigip.modify_virtual'](hostname=hostname, - username=username, - password=password, - name=name, - destination=destination, - description=description, - pool=pool, - address_status=address_status, - auto_lasthop=auto_lasthop, - bwc_policy=bwc_policy, - cmp_enabled=cmp_enabled, - connection_limit=connection_limit, - dhcp_relay=dhcp_relay, - fallback_persistence=fallback_persistence, - flow_eviction_policy=flow_eviction_policy, - gtm_score=gtm_score, - ip_forward=ip_forward, - ip_protocol=ip_protocol, - internal=internal, - twelve_forward=twelve_forward, - last_hop_pool=last_hop_pool, - mask=mask, - mirror=mirror, - nat64=nat64, - persist=persist, - profiles=profiles, - policies=policies, - rate_class=rate_class, - rate_limit=rate_limit, - rate_limit_mode=rate_limit_mode, - rate_limit_dst=rate_limit_dst, - rate_limit_src=rate_limit_src, - rules=rules, - related_rules=related_rules, - reject=reject, - source=source, - source_address_translation=source_address_translation, - source_port=source_port, - state=virtual_state, - traffic_classes=traffic_classes, - translate_address=translate_address, - translate_port=translate_port, - vlans=vlans) + modified = __salt__["bigip.modify_virtual"]( + hostname=hostname, + username=username, + password=password, + name=name, + destination=destination, + description=description, + pool=pool, + address_status=address_status, + auto_lasthop=auto_lasthop, + bwc_policy=bwc_policy, + cmp_enabled=cmp_enabled, + connection_limit=connection_limit, + dhcp_relay=dhcp_relay, + fallback_persistence=fallback_persistence, + flow_eviction_policy=flow_eviction_policy, + gtm_score=gtm_score, + ip_forward=ip_forward, + ip_protocol=ip_protocol, + internal=internal, + twelve_forward=twelve_forward, + last_hop_pool=last_hop_pool, + mask=mask, + mirror=mirror, + nat64=nat64, + persist=persist, + profiles=profiles, + policies=policies, + rate_class=rate_class, + rate_limit=rate_limit, + rate_limit_mode=rate_limit_mode, + rate_limit_dst=rate_limit_dst, + rate_limit_src=rate_limit_src, + rules=rules, + related_rules=related_rules, + reject=reject, + source=source, + source_address_translation=source_address_translation, + source_port=source_port, + state=virtual_state, + traffic_classes=traffic_classes, + translate_address=translate_address, + translate_port=translate_port, + vlans=vlans, + ) - #was the modification successful? - if modified['code'] == 200: + # was the modification successful? + if modified["code"] == 200: - #relist it to compare - relisting = __salt__['bigip.list_virtual'](hostname, username, password, name) + # relist it to compare + relisting = __salt__["bigip.list_virtual"]( + hostname, username, password, name + ) - if relisting['code'] == 200: + if relisting["code"] == 200: - relisting = _strip_key(relisting, 'generation') - existing = _strip_key(existing, 'generation') + relisting = _strip_key(relisting, "generation") + existing = _strip_key(existing, "generation") - ret = _check_for_changes('Virtual', ret, existing, relisting) + ret = _check_for_changes("Virtual", ret, existing, relisting) else: ret = _load_result(relisting, ret) else: ret = _load_result(modified, ret) - elif existing['code'] == 404: + elif existing["code"] == 404: - #create it - virtual = __salt__['bigip.create_virtual'](hostname=hostname, - username=username, - password=password, - name=name, - destination=destination, - description=description, - pool=pool, - address_status=address_status, - auto_lasthop=auto_lasthop, - bwc_policy=bwc_policy, - cmp_enabled=cmp_enabled, - connection_limit=connection_limit, - dhcp_relay=dhcp_relay, - fallback_persistence=fallback_persistence, - flow_eviction_policy=flow_eviction_policy, - gtm_score=gtm_score, - ip_forward=ip_forward, - ip_protocol=ip_protocol, - internal=internal, - twelve_forward=twelve_forward, - last_hop_pool=last_hop_pool, - mask=mask, - mirror=mirror, - nat64=nat64, - persist=persist, - profiles=profiles, - policies=policies, - rate_class=rate_class, - rate_limit=rate_limit, - rate_limit_mode=rate_limit_mode, - rate_limit_dst=rate_limit_dst, - rate_limit_src=rate_limit_src, - rules=rules, - related_rules=related_rules, - reject=reject, - source=source, - source_address_translation=source_address_translation, - source_port=source_port, - state=virtual_state, - traffic_classes=traffic_classes, - translate_address=translate_address, - translate_port=translate_port, - vlans=vlans) + # create it + virtual = __salt__["bigip.create_virtual"]( + hostname=hostname, + username=username, + password=password, + name=name, + destination=destination, + description=description, + pool=pool, + address_status=address_status, + auto_lasthop=auto_lasthop, + bwc_policy=bwc_policy, + cmp_enabled=cmp_enabled, + connection_limit=connection_limit, + dhcp_relay=dhcp_relay, + fallback_persistence=fallback_persistence, + flow_eviction_policy=flow_eviction_policy, + gtm_score=gtm_score, + ip_forward=ip_forward, + ip_protocol=ip_protocol, + internal=internal, + twelve_forward=twelve_forward, + last_hop_pool=last_hop_pool, + mask=mask, + mirror=mirror, + nat64=nat64, + persist=persist, + profiles=profiles, + policies=policies, + rate_class=rate_class, + rate_limit=rate_limit, + rate_limit_mode=rate_limit_mode, + rate_limit_dst=rate_limit_dst, + rate_limit_src=rate_limit_src, + rules=rules, + related_rules=related_rules, + reject=reject, + source=source, + source_address_translation=source_address_translation, + source_port=source_port, + state=virtual_state, + traffic_classes=traffic_classes, + translate_address=translate_address, + translate_port=translate_port, + vlans=vlans, + ) - #were we able to create it? - if virtual['code'] == 200: - ret['result'] = True - ret['changes']['old'] = {} - ret['changes']['new'] = virtual['content'] - ret['comment'] = 'Virtual was successfully created and enforced to the desired state.' + # were we able to create it? + if virtual["code"] == 200: + ret["result"] = True + ret["changes"]["old"] = {} + ret["changes"]["new"] = virtual["content"] + ret[ + "comment" + ] = "Virtual was successfully created and enforced to the desired state." else: ret = _load_result(virtual, ret) @@ -2169,46 +2369,52 @@ def manage_virtual(hostname, username, password, name, destination, return ret -def modify_virtual(hostname, username, password, name, destination, - pool=None, - address_status=None, - auto_lasthop=None, - bwc_policy=None, - cmp_enabled=None, - connection_limit=None, - dhcp_relay=None, - description=None, - fallback_persistence=None, - flow_eviction_policy=None, - gtm_score=None, - ip_forward=None, - ip_protocol=None, - internal=None, - twelve_forward=None, - last_hop_pool=None, - mask=None, - mirror=None, - nat64=None, - persist=None, - profiles=None, - policies=None, - rate_class=None, - rate_limit=None, - rate_limit_mode=None, - rate_limit_dst=None, - rate_limit_src=None, - rules=None, - related_rules=None, - reject=None, - source=None, - source_address_translation=None, - source_port=None, - virtual_state=None, - traffic_classes=None, - translate_address=None, - translate_port=None, - vlans=None): - ''' +def modify_virtual( + hostname, + username, + password, + name, + destination, + pool=None, + address_status=None, + auto_lasthop=None, + bwc_policy=None, + cmp_enabled=None, + connection_limit=None, + dhcp_relay=None, + description=None, + fallback_persistence=None, + flow_eviction_policy=None, + gtm_score=None, + ip_forward=None, + ip_protocol=None, + internal=None, + twelve_forward=None, + last_hop_pool=None, + mask=None, + mirror=None, + nat64=None, + persist=None, + profiles=None, + policies=None, + rate_class=None, + rate_limit=None, + rate_limit_mode=None, + rate_limit_dst=None, + rate_limit_src=None, + rules=None, + related_rules=None, + reject=None, + source=None, + source_address_translation=None, + source_port=None, + virtual_state=None, + traffic_classes=None, + translate_address=None, + translate_port=None, + vlans=None, +): + """ Modify an virtual server. modify an existing virtual. Only parameters specified will be enforced. hostname @@ -2307,128 +2513,135 @@ def modify_virtual(hostname, username, password, name, destination, enabled [ true | false ] - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'modify', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name, - 'destination': destination, - 'pool': pool, - 'address_status': address_status, - 'auto_lasthop': auto_lasthop, - 'bwc_policy': bwc_policy, - 'cmp_enabled': cmp_enabled, - 'connection_limit': connection_limit, - 'dhcp_relay': dhcp_relay, - 'description': description, - 'fallback_persistence': fallback_persistence, - 'flow_eviction_policy': flow_eviction_policy, - 'gtm_score': gtm_score, - 'ip_forward': ip_forward, - 'ip_protocol': ip_protocol, - 'internal': internal, - 'twelve_forward': twelve_forward, - 'last_hop_pool': last_hop_pool, - 'mask': mask, - 'mirror': mirror, - 'nat64': nat64, - 'persist': persist, - 'profiles': profiles, - 'policies': policies, - 'rate_class': rate_class, - 'rate_limit': rate_limit, - 'rate_limit_mode': rate_limit_mode, - 'rate_limit_dst': rate_limit_dst, - 'rate_limit_src': rate_limit_src, - 'rules': rules, - 'related_rules': related_rules, - 'reject': reject, - 'source': source, - 'source_address_translation': source_address_translation, - 'source_port': source_port, - 'virtual_state': virtual_state, - 'traffic_classes': traffic_classes, - 'translate_address': translate_address, - 'translate_port': translate_port, - 'vlans': vlans - } + if __opts__["test"]: + return _test_output( + ret, + "modify", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + "destination": destination, + "pool": pool, + "address_status": address_status, + "auto_lasthop": auto_lasthop, + "bwc_policy": bwc_policy, + "cmp_enabled": cmp_enabled, + "connection_limit": connection_limit, + "dhcp_relay": dhcp_relay, + "description": description, + "fallback_persistence": fallback_persistence, + "flow_eviction_policy": flow_eviction_policy, + "gtm_score": gtm_score, + "ip_forward": ip_forward, + "ip_protocol": ip_protocol, + "internal": internal, + "twelve_forward": twelve_forward, + "last_hop_pool": last_hop_pool, + "mask": mask, + "mirror": mirror, + "nat64": nat64, + "persist": persist, + "profiles": profiles, + "policies": policies, + "rate_class": rate_class, + "rate_limit": rate_limit, + "rate_limit_mode": rate_limit_mode, + "rate_limit_dst": rate_limit_dst, + "rate_limit_src": rate_limit_src, + "rules": rules, + "related_rules": related_rules, + "reject": reject, + "source": source, + "source_address_translation": source_address_translation, + "source_port": source_port, + "virtual_state": virtual_state, + "traffic_classes": traffic_classes, + "translate_address": translate_address, + "translate_port": translate_port, + "vlans": vlans, + }, ) - existing = __salt__['bigip.list_virtual'](hostname, username, password, name) + existing = __salt__["bigip.list_virtual"](hostname, username, password, name) # does this virtual exist? - if existing['code'] == 200: + if existing["code"] == 200: # modify - modified = __salt__['bigip.modify_virtual'](hostname=hostname, - username=username, - password=password, - name=name, - destination=destination, - description=description, - pool=pool, - address_status=address_status, - auto_lasthop=auto_lasthop, - bwc_policy=bwc_policy, - cmp_enabled=cmp_enabled, - connection_limit=connection_limit, - dhcp_relay=dhcp_relay, - fallback_persistence=fallback_persistence, - flow_eviction_policy=flow_eviction_policy, - gtm_score=gtm_score, - ip_forward=ip_forward, - ip_protocol=ip_protocol, - internal=internal, - twelve_forward=twelve_forward, - last_hop_pool=last_hop_pool, - mask=mask, - mirror=mirror, - nat64=nat64, - persist=persist, - profiles=profiles, - policies=policies, - rate_class=rate_class, - rate_limit=rate_limit, - rate_limit_mode=rate_limit_mode, - rate_limit_dst=rate_limit_dst, - rate_limit_src=rate_limit_src, - rules=rules, - related_rules=related_rules, - reject=reject, - source=source, - source_address_translation=source_address_translation, - source_port=source_port, - state=virtual_state, - traffic_classes=traffic_classes, - translate_address=translate_address, - translate_port=translate_port, - vlans=vlans) + modified = __salt__["bigip.modify_virtual"]( + hostname=hostname, + username=username, + password=password, + name=name, + destination=destination, + description=description, + pool=pool, + address_status=address_status, + auto_lasthop=auto_lasthop, + bwc_policy=bwc_policy, + cmp_enabled=cmp_enabled, + connection_limit=connection_limit, + dhcp_relay=dhcp_relay, + fallback_persistence=fallback_persistence, + flow_eviction_policy=flow_eviction_policy, + gtm_score=gtm_score, + ip_forward=ip_forward, + ip_protocol=ip_protocol, + internal=internal, + twelve_forward=twelve_forward, + last_hop_pool=last_hop_pool, + mask=mask, + mirror=mirror, + nat64=nat64, + persist=persist, + profiles=profiles, + policies=policies, + rate_class=rate_class, + rate_limit=rate_limit, + rate_limit_mode=rate_limit_mode, + rate_limit_dst=rate_limit_dst, + rate_limit_src=rate_limit_src, + rules=rules, + related_rules=related_rules, + reject=reject, + source=source, + source_address_translation=source_address_translation, + source_port=source_port, + state=virtual_state, + traffic_classes=traffic_classes, + translate_address=translate_address, + translate_port=translate_port, + vlans=vlans, + ) - #was the modification successful? - if modified['code'] == 200: + # was the modification successful? + if modified["code"] == 200: - #relist it to compare - relisting = __salt__['bigip.list_virtual'](hostname, username, password, name) + # relist it to compare + relisting = __salt__["bigip.list_virtual"]( + hostname, username, password, name + ) - if relisting['code'] == 200: + if relisting["code"] == 200: - relisting = _strip_key(relisting, 'generation') - existing = _strip_key(existing, 'generation') + relisting = _strip_key(relisting, "generation") + existing = _strip_key(existing, "generation") - ret = _check_for_changes('Virtual', ret, existing, relisting) + ret = _check_for_changes("Virtual", ret, existing, relisting) else: ret = _load_result(relisting, ret) else: ret = _load_result(modified, ret) - elif existing['code'] == 404: - ret['comment'] = 'A Virtual with this name was not found.' + elif existing["code"] == 404: + ret["comment"] = "A Virtual with this name was not found." # else something else was returned else: ret = _load_result(existing, ret) @@ -2437,7 +2650,7 @@ def modify_virtual(hostname, username, password, name, destination, def delete_virtual(hostname, username, password, name): - ''' + """ Delete an existing virtual. hostname @@ -2448,41 +2661,44 @@ def delete_virtual(hostname, username, password, name): The iControl REST password name The name of the virtual which will be deleted - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'delete', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'name': name - } + if __opts__["test"]: + return _test_output( + ret, + "delete", + params={ + "hostname": hostname, + "username": username, + "password": password, + "name": name, + }, ) - #is this virtual currently configured? - existing = __salt__['bigip.list_virtual'](hostname, username, password, name) + # is this virtual currently configured? + existing = __salt__["bigip.list_virtual"](hostname, username, password, name) # if it exists by name - if existing['code'] == 200: + if existing["code"] == 200: - deleted = __salt__['bigip.delete_virtual'](hostname, username, password, name) + deleted = __salt__["bigip.delete_virtual"](hostname, username, password, name) # did we get rid of it? - if deleted['code'] == 200: - ret['result'] = True - ret['comment'] = 'Virtual was successfully deleted.' - ret['changes']['old'] = existing['content'] - ret['changes']['new'] = {} + if deleted["code"] == 200: + ret["result"] = True + ret["comment"] = "Virtual was successfully deleted." + ret["changes"]["old"] = existing["content"] + ret["changes"]["new"] = {} # something bad happened else: ret = _load_result(deleted, ret) # not found - elif existing['code'] == 404: - ret['result'] = True - ret['comment'] = 'This virtual already does not exist. No changes made.' - ret['changes']['old'] = {} - ret['changes']['new'] = {} + elif existing["code"] == 404: + ret["result"] = True + ret["comment"] = "This virtual already does not exist. No changes made." + ret["changes"]["old"] = {} + ret["changes"]["new"] = {} else: ret = _load_result(existing, ret) @@ -2490,7 +2706,7 @@ def delete_virtual(hostname, username, password, name): def list_monitor(hostname, username, password, monitor_type, name): - ''' + """ A function to list an existing monitor. hostname @@ -2503,26 +2719,31 @@ def list_monitor(hostname, username, password, monitor_type, name): The type of monitor to list name The name of the monitor to list - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'list', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'monitor_type': monitor_type, - 'name': name - } + if __opts__["test"]: + return _test_output( + ret, + "list", + params={ + "hostname": hostname, + "username": username, + "password": password, + "monitor_type": monitor_type, + "name": name, + }, ) - response = __salt__['bigip.list_monitor'](hostname, username, password, monitor_type, name) + response = __salt__["bigip.list_monitor"]( + hostname, username, password, monitor_type, name + ) return _load_result(response, ret) def create_monitor(hostname, username, password, monitor_type, name, **kwargs): - ''' + """ A function to connect to a bigip device and create a monitor. hostname @@ -2540,43 +2761,47 @@ def create_monitor(hostname, username, password, monitor_type, name, **kwargs): Consult F5 BIGIP user guide for specific options for each monitor type. Typically, tmsh arg names are used. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: + if __opts__["test"]: params = { - 'hostname': hostname, - 'username': username, - 'password': password, - 'monitor_type': monitor_type, - 'name': name + "hostname": hostname, + "username": username, + "password": password, + "monitor_type": monitor_type, + "name": name, } for key, value in six.iteritems(kwargs): params[key] = value - return _test_output(ret, 'create', params) + return _test_output(ret, "create", params) - #is this monitor currently configured? - existing = __salt__['bigip.list_monitor'](hostname, username, password, monitor_type, name) + # is this monitor currently configured? + existing = __salt__["bigip.list_monitor"]( + hostname, username, password, monitor_type, name + ) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - ret['result'] = True - ret['comment'] = 'A monitor by this name currently exists. No change made.' + ret["result"] = True + ret["comment"] = "A monitor by this name currently exists. No change made." # if it doesn't exist - elif existing['code'] == 404: + elif existing["code"] == 404: - response = __salt__['bigip.create_monitor'](hostname, username, password, monitor_type, name, **kwargs) - if response['code'] == 200: - ret['result'] = True - ret['changes']['old'] = {} - ret['changes']['new'] = response['content'] - ret['comment'] = 'Monitor was successfully created.' + response = __salt__["bigip.create_monitor"]( + hostname, username, password, monitor_type, name, **kwargs + ) + if response["code"] == 200: + ret["result"] = True + ret["changes"]["old"] = {} + ret["changes"]["new"] = response["content"] + ret["comment"] = "Monitor was successfully created." else: ret = _load_result(response, ret) @@ -2588,7 +2813,7 @@ def create_monitor(hostname, username, password, monitor_type, name, **kwargs): def manage_monitor(hostname, username, password, monitor_type, name, **kwargs): - ''' + """ Create a new monitor if a monitor of this type and name does not already exists. If it does exists, only the parameters specified will be enforced. @@ -2607,51 +2832,57 @@ def manage_monitor(hostname, username, password, monitor_type, name, **kwargs): Consult F5 BIGIP user guide for specific options for each monitor type. Typically, tmsh arg names are used. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: + if __opts__["test"]: params = { - 'hostname': hostname, - 'username': username, - 'password': password, - 'monitor_type': monitor_type, - 'name': name + "hostname": hostname, + "username": username, + "password": password, + "monitor_type": monitor_type, + "name": name, } for key, value in six.iteritems(kwargs): params[key] = value - return _test_output(ret, 'manage', params) + return _test_output(ret, "manage", params) - #is this monitor currently configured? - existing = __salt__['bigip.list_monitor'](hostname, username, password, monitor_type, name) + # is this monitor currently configured? + existing = __salt__["bigip.list_monitor"]( + hostname, username, password, monitor_type, name + ) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - #modify the monitor - modified = __salt__['bigip.modify_monitor'](hostname, username, password, monitor_type, name, **kwargs) + # modify the monitor + modified = __salt__["bigip.modify_monitor"]( + hostname, username, password, monitor_type, name, **kwargs + ) - #was the modification successful? - if modified['code'] == 200: - del existing['content']['selfLink'] - del modified['content']['selfLink'] - ret = _check_for_changes('Monitor', ret, existing, modified) + # was the modification successful? + if modified["code"] == 200: + del existing["content"]["selfLink"] + del modified["content"]["selfLink"] + ret = _check_for_changes("Monitor", ret, existing, modified) else: ret = _load_result(modified, ret) # if it doesn't exist - elif existing['code'] == 404: + elif existing["code"] == 404: - response = __salt__['bigip.create_monitor'](hostname, username, password, monitor_type, name, **kwargs) - if response['code'] == 200: - ret['result'] = True - ret['changes']['old'] = {} - ret['changes']['new'] = response['content'] - ret['comment'] = 'Monitor was successfully created.' + response = __salt__["bigip.create_monitor"]( + hostname, username, password, monitor_type, name, **kwargs + ) + if response["code"] == 200: + ret["result"] = True + ret["changes"]["old"] = {} + ret["changes"]["new"] = response["content"] + ret["comment"] = "Monitor was successfully created." else: ret = _load_result(response, ret) @@ -2663,7 +2894,7 @@ def manage_monitor(hostname, username, password, monitor_type, name, **kwargs): def modify_monitor(hostname, username, password, monitor_type, name, **kwargs): - ''' + """ Modify an existing monitor. If it does exists, only the parameters specified will be enforced. @@ -2682,45 +2913,49 @@ def modify_monitor(hostname, username, password, monitor_type, name, **kwargs): Consult F5 BIGIP user guide for specific options for each monitor type. Typically, tmsh arg names are used. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: + if __opts__["test"]: params = { - 'hostname': hostname, - 'username': username, - 'password': password, - 'monitor_type': monitor_type, - 'name': name + "hostname": hostname, + "username": username, + "password": password, + "monitor_type": monitor_type, + "name": name, } for key, value in six.iteritems(kwargs): params[key] = value - return _test_output(ret, 'modify', params) + return _test_output(ret, "modify", params) - #is this monitor currently configured? - existing = __salt__['bigip.list_monitor'](hostname, username, password, monitor_type, name) + # is this monitor currently configured? + existing = __salt__["bigip.list_monitor"]( + hostname, username, password, monitor_type, name + ) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - #modify the monitor - modified = __salt__['bigip.modify_monitor'](hostname, username, password, monitor_type, name, **kwargs) + # modify the monitor + modified = __salt__["bigip.modify_monitor"]( + hostname, username, password, monitor_type, name, **kwargs + ) - #was the modification successful? - if modified['code'] == 200: - del existing['content']['selfLink'] - del modified['content']['selfLink'] - ret = _check_for_changes('Monitor', ret, existing, modified) + # was the modification successful? + if modified["code"] == 200: + del existing["content"]["selfLink"] + del modified["content"]["selfLink"] + ret = _check_for_changes("Monitor", ret, existing, modified) else: ret = _load_result(modified, ret) # if it doesn't exist - elif existing['code'] == 404: - ret['comment'] = 'A Monitor with this name was not found.' + elif existing["code"] == 404: + ret["comment"] = "A Monitor with this name was not found." # else something else was returned else: ret = _load_result(existing, ret) @@ -2729,7 +2964,7 @@ def modify_monitor(hostname, username, password, monitor_type, name, **kwargs): def delete_monitor(hostname, username, password, monitor_type, name): - ''' + """ Modify an existing monitor. If it does exists, only the parameters specified will be enforced. @@ -2748,41 +2983,49 @@ def delete_monitor(hostname, username, password, monitor_type, name): Consult F5 BIGIP user guide for specific options for each monitor type. Typically, tmsh arg names are used. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'delete', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'monitor_type': monitor_type, - 'name': name - }) + if __opts__["test"]: + return _test_output( + ret, + "delete", + params={ + "hostname": hostname, + "username": username, + "password": password, + "monitor_type": monitor_type, + "name": name, + }, + ) - #is this profile currently configured? - existing = __salt__['bigip.list_monitor'](hostname, username, password, monitor_type, name) + # is this profile currently configured? + existing = __salt__["bigip.list_monitor"]( + hostname, username, password, monitor_type, name + ) # if it exists by name - if existing['code'] == 200: + if existing["code"] == 200: - deleted = __salt__['bigip.delete_monitor'](hostname, username, password, monitor_type, name) + deleted = __salt__["bigip.delete_monitor"]( + hostname, username, password, monitor_type, name + ) # did we get rid of it? - if deleted['code'] == 200: - ret['result'] = True - ret['comment'] = 'Monitor was successfully deleted.' - ret['changes']['old'] = existing['content'] - ret['changes']['new'] = {} + if deleted["code"] == 200: + ret["result"] = True + ret["comment"] = "Monitor was successfully deleted." + ret["changes"]["old"] = existing["content"] + ret["changes"]["new"] = {} # something bad happened else: ret = _load_result(deleted, ret) # not found - elif existing['code'] == 404: - ret['result'] = True - ret['comment'] = 'This Monitor already does not exist. No changes made.' - ret['changes']['old'] = {} - ret['changes']['new'] = {} + elif existing["code"] == 404: + ret["result"] = True + ret["comment"] = "This Monitor already does not exist. No changes made." + ret["changes"]["old"] = {} + ret["changes"]["new"] = {} else: ret = _load_result(existing, ret) @@ -2790,7 +3033,7 @@ def delete_monitor(hostname, username, password, monitor_type, name): def list_profile(hostname, username, password, profile_type, name): - ''' + """ A function to list an existing profile. hostname @@ -2803,25 +3046,31 @@ def list_profile(hostname, username, password, profile_type, name): The type of profile to list name The name of the profile to list - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'list', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'profile_type': profile_type, - 'name': name - }) + if __opts__["test"]: + return _test_output( + ret, + "list", + params={ + "hostname": hostname, + "username": username, + "password": password, + "profile_type": profile_type, + "name": name, + }, + ) - response = __salt__['bigip.list_profile'](hostname, username, password, profile_type, name) + response = __salt__["bigip.list_profile"]( + hostname, username, password, profile_type, name + ) return _load_result(response, ret) def create_profile(hostname, username, password, profile_type, name, **kwargs): - r''' + r""" A function to connect to a bigip device and create a profile. hostname @@ -2843,38 +3092,46 @@ def create_profile(hostname, username, password, profile_type, name, **kwargs): Special Characters ``|``, ``,`` and ``:`` must be escaped using ``\`` when used within strings. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'create', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'profile_type': profile_type, - 'name': name - }) + if __opts__["test"]: + return _test_output( + ret, + "create", + params={ + "hostname": hostname, + "username": username, + "password": password, + "profile_type": profile_type, + "name": name, + }, + ) - #is this profile currently configured? - existing = __salt__['bigip.list_profile'](hostname, username, password, profile_type, name) + # is this profile currently configured? + existing = __salt__["bigip.list_profile"]( + hostname, username, password, profile_type, name + ) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - ret['result'] = True - ret['comment'] = 'A profile by this name currently exists. No change made.' + ret["result"] = True + ret["comment"] = "A profile by this name currently exists. No change made." # if it doesn't exist - elif existing['code'] == 404: + elif existing["code"] == 404: - response = __salt__['bigip.create_profile'](hostname, username, password, profile_type, name, **kwargs) + response = __salt__["bigip.create_profile"]( + hostname, username, password, profile_type, name, **kwargs + ) - if response['code'] == 200: - ret['result'] = True - ret['changes']['old'] = {} - ret['changes']['new'] = response['content'] - ret['comment'] = 'Profile was successfully created.' + if response["code"] == 200: + ret["result"] = True + ret["changes"]["old"] = {} + ret["changes"]["new"] = response["content"] + ret["comment"] = "Profile was successfully created." else: ret = _load_result(response, ret) @@ -2886,7 +3143,7 @@ def create_profile(hostname, username, password, profile_type, name, **kwargs): def manage_profile(hostname, username, password, profile_type, name, **kwargs): - ''' + """ Create a new profile if a monitor of this type and name does not already exists. If it does exists, only the parameters specified will be enforced. @@ -2905,51 +3162,57 @@ def manage_profile(hostname, username, password, profile_type, name, **kwargs): Consult F5 BIGIP user guide for specific options for each profile type. Typically, tmsh arg names are used. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: + if __opts__["test"]: params = { - 'hostname': hostname, - 'username': username, - 'password': password, - 'profile_type': profile_type, - 'name': name + "hostname": hostname, + "username": username, + "password": password, + "profile_type": profile_type, + "name": name, } for key, value in six.iteritems(kwargs): params[key] = value - return _test_output(ret, 'manage', params) + return _test_output(ret, "manage", params) - #is this profile currently configured? - existing = __salt__['bigip.list_profile'](hostname, username, password, profile_type, name) + # is this profile currently configured? + existing = __salt__["bigip.list_profile"]( + hostname, username, password, profile_type, name + ) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - #modify the profile - modified = __salt__['bigip.modify_profile'](hostname, username, password, profile_type, name, **kwargs) + # modify the profile + modified = __salt__["bigip.modify_profile"]( + hostname, username, password, profile_type, name, **kwargs + ) - #was the modification successful? - if modified['code'] == 200: - del existing['content']['selfLink'] - del modified['content']['selfLink'] - ret = _check_for_changes('Profile', ret, existing, modified) + # was the modification successful? + if modified["code"] == 200: + del existing["content"]["selfLink"] + del modified["content"]["selfLink"] + ret = _check_for_changes("Profile", ret, existing, modified) else: ret = _load_result(modified, ret) # if it doesn't exist - elif existing['code'] == 404: + elif existing["code"] == 404: - response = __salt__['bigip.create_profile'](hostname, username, password, profile_type, name, **kwargs) - if response['code'] == 200: - ret['result'] = True - ret['changes']['old'] = {} - ret['changes']['new'] = response['content'] - ret['comment'] = 'Profile was successfully created.' + response = __salt__["bigip.create_profile"]( + hostname, username, password, profile_type, name, **kwargs + ) + if response["code"] == 200: + ret["result"] = True + ret["changes"]["old"] = {} + ret["changes"]["new"] = response["content"] + ret["comment"] = "Profile was successfully created." else: ret = _load_result(existing, ret) @@ -2961,7 +3224,7 @@ def manage_profile(hostname, username, password, profile_type, name, **kwargs): def modify_profile(hostname, username, password, profile_type, name, **kwargs): - ''' + """ Modify an existing profile. If it does exists, only the parameters specified will be enforced. @@ -2980,45 +3243,49 @@ def modify_profile(hostname, username, password, profile_type, name, **kwargs): Consult F5 BIGIP user guide for specific options for each monitor type. Typically, tmsh arg names are used. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: + if __opts__["test"]: params = { - 'hostname': hostname, - 'username': username, - 'password': password, - 'profile_type': profile_type, - 'name': name + "hostname": hostname, + "username": username, + "password": password, + "profile_type": profile_type, + "name": name, } for key, value in six.iteritems(kwargs): params[key] = value - return _test_output(ret, 'modify', params) + return _test_output(ret, "modify", params) - #is this profile currently configured? - existing = __salt__['bigip.list_profile'](hostname, username, password, profile_type, name) + # is this profile currently configured? + existing = __salt__["bigip.list_profile"]( + hostname, username, password, profile_type, name + ) # if it exists - if existing['code'] == 200: + if existing["code"] == 200: - #modify the profile - modified = __salt__['bigip.modify_profile'](hostname, username, password, profile_type, name, **kwargs) + # modify the profile + modified = __salt__["bigip.modify_profile"]( + hostname, username, password, profile_type, name, **kwargs + ) - #was the modification successful? - if modified['code'] == 200: - del existing['content']['selfLink'] - del modified['content']['selfLink'] - ret = _check_for_changes('Profile', ret, existing, modified) + # was the modification successful? + if modified["code"] == 200: + del existing["content"]["selfLink"] + del modified["content"]["selfLink"] + ret = _check_for_changes("Profile", ret, existing, modified) else: ret = _load_result(modified, ret) # if it doesn't exist - elif existing['code'] == 404: - ret['comment'] = 'A Profile with this name was not found.' + elif existing["code"] == 404: + ret["comment"] = "A Profile with this name was not found." # else something else was returned else: ret = _load_result(existing, ret) @@ -3027,7 +3294,7 @@ def modify_profile(hostname, username, password, profile_type, name, **kwargs): def delete_profile(hostname, username, password, profile_type, name): - ''' + """ Modify an existing profile. If it does exists, only the parameters specified will be enforced. @@ -3046,42 +3313,50 @@ def delete_profile(hostname, username, password, profile_type, name): Consult F5 BIGIP user guide for specific options for each profile type. Typically, tmsh arg names are used. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - return _test_output(ret, 'delete', params={ - 'hostname': hostname, - 'username': username, - 'password': password, - 'profile_type': profile_type, - 'name': name - }) + if __opts__["test"]: + return _test_output( + ret, + "delete", + params={ + "hostname": hostname, + "username": username, + "password": password, + "profile_type": profile_type, + "name": name, + }, + ) - #is this profile currently configured? - existing = __salt__['bigip.list_profile'](hostname, username, password, profile_type, name) + # is this profile currently configured? + existing = __salt__["bigip.list_profile"]( + hostname, username, password, profile_type, name + ) # if it exists by name - if existing['code'] == 200: + if existing["code"] == 200: - deleted = __salt__['bigip.delete_profile'](hostname, username, password, profile_type, name) + deleted = __salt__["bigip.delete_profile"]( + hostname, username, password, profile_type, name + ) # did we get rid of it? - if deleted['code'] == 200: - ret['result'] = True - ret['comment'] = 'Profile was successfully deleted.' - ret['changes']['old'] = existing['content'] - ret['changes']['new'] = {} + if deleted["code"] == 200: + ret["result"] = True + ret["comment"] = "Profile was successfully deleted." + ret["changes"]["old"] = existing["content"] + ret["changes"]["new"] = {} # something bad happened else: ret = _load_result(deleted, ret) # not found - elif existing['code'] == 404: - ret['result'] = True - ret['comment'] = 'This Profile already does not exist. No changes made.' - ret['changes']['old'] = {} - ret['changes']['new'] = {} + elif existing["code"] == 404: + ret["result"] = True + ret["comment"] = "This Profile already does not exist. No changes made." + ret["changes"]["old"] = {} + ret["changes"]["new"] = {} else: ret = _load_result(existing, ret) diff --git a/salt/states/blockdev.py b/salt/states/blockdev.py index d911c75cb81..3c52584c68d 100644 --- a/salt/states/blockdev.py +++ b/salt/states/blockdev.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Block Devices A state module to manage blockdevices @@ -19,37 +19,43 @@ A state module to manage blockdevices .. versionadded:: 2014.7.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os import os.path import time -import logging # Import salt libs import salt.utils.path from salt.ext.six.moves import range -__virtualname__ = 'blockdev' +__virtualname__ = "blockdev" # Init logger log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if the disk execution module is available - ''' - if 'disk.tune' in __salt__: + """ + if "disk.tune" in __salt__: return __virtualname__ - return (False, ('Cannot load the {0} state module: ' - 'disk execution module not found'.format(__virtualname__))) + return ( + False, + ( + "Cannot load the {0} state module: " + "disk execution module not found".format(__virtualname__) + ), + ) def tuned(name, **kwargs): - ''' + """ Manage options of block device name @@ -67,58 +73,61 @@ def tuned(name, **kwargs): - read-write Set Read-Write - ''' + """ - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + ret = {"changes": {}, "comment": "", "name": name, "result": True} - kwarg_map = {'read-ahead': 'getra', - 'filesystem-read-ahead': 'getfra', - 'read-only': 'getro', - 'read-write': 'getro'} + kwarg_map = { + "read-ahead": "getra", + "filesystem-read-ahead": "getfra", + "read-only": "getro", + "read-write": "getro", + } - if not __salt__['file.is_blkdev']: - ret['comment'] = ('Changes to {0} cannot be applied. ' - 'Not a block device. ').format(name) - elif __opts__['test']: - ret['comment'] = 'Changes to {0} will be applied '.format(name) - ret['result'] = None + if not __salt__["file.is_blkdev"]: + ret["comment"] = ( + "Changes to {0} cannot be applied. " "Not a block device. " + ).format(name) + elif __opts__["test"]: + ret["comment"] = "Changes to {0} will be applied ".format(name) + ret["result"] = None return ret else: - current = __salt__['disk.dump'](name) - changes = __salt__['disk.tune'](name, **kwargs) + current = __salt__["disk.dump"](name) + changes = __salt__["disk.tune"](name, **kwargs) changeset = {} for key in kwargs: if key in kwarg_map: switch = kwarg_map[key] if current[switch] != changes[switch]: if isinstance(kwargs[key], bool): - old = (current[switch] == '1') - new = (changes[switch] == '1') + old = current[switch] == "1" + new = changes[switch] == "1" else: old = current[switch] new = changes[switch] - if key == 'read-write': + if key == "read-write": old = not old new = not new - changeset[key] = 'Changed from {0} to {1}'.format(old, new) + changeset[key] = "Changed from {0} to {1}".format(old, new) if changes: if changeset: - ret['comment'] = ('Block device {0} ' - 'successfully modified ').format(name) - ret['changes'] = changeset + ret["comment"] = ("Block device {0} " "successfully modified ").format( + name + ) + ret["changes"] = changeset else: - ret['comment'] = 'Block device {0} already in correct state'.format(name) + ret["comment"] = "Block device {0} already in correct state".format( + name + ) else: - ret['comment'] = 'Failed to modify block device {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to modify block device {0}".format(name) + ret["result"] = False return ret -def formatted(name, fs_type='ext4', force=False, **kwargs): - ''' +def formatted(name, fs_type="ext4", force=False, **kwargs): + """ Manage filesystems of partitions. name @@ -135,31 +144,33 @@ def formatted(name, fs_type='ext4', force=False, **kwargs): This option is dangerous, use it with caution. .. versionadded:: 2016.11.0 - ''' - ret = {'changes': {}, - 'comment': '{0} already formatted with {1}'.format(name, fs_type), - 'name': name, - 'result': False} + """ + ret = { + "changes": {}, + "comment": "{0} already formatted with {1}".format(name, fs_type), + "name": name, + "result": False, + } if not os.path.exists(name): - ret['comment'] = '{0} does not exist'.format(name) + ret["comment"] = "{0} does not exist".format(name) return ret current_fs = _checkblk(name) if current_fs == fs_type: - ret['result'] = True + ret["result"] = True return ret - elif not salt.utils.path.which('mkfs.{0}'.format(fs_type)): - ret['comment'] = 'Invalid fs_type: {0}'.format(fs_type) - ret['result'] = False + elif not salt.utils.path.which("mkfs.{0}".format(fs_type)): + ret["comment"] = "Invalid fs_type: {0}".format(fs_type) + ret["result"] = False return ret - elif __opts__['test']: - ret['comment'] = 'Changes to {0} will be applied '.format(name) - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "Changes to {0} will be applied ".format(name) + ret["result"] = None return ret - __salt__['disk.format'](name, fs_type, force=force, **kwargs) + __salt__["disk.format"](name, fs_type, force=force, **kwargs) # Repeat fstype check up to 10 times with 3s sleeping between each # to avoid detection failing although mkfs has succeeded @@ -167,33 +178,34 @@ def formatted(name, fs_type='ext4', force=False, **kwargs): # This retry maybe superfluous - switching to blkid for i in range(10): - log.info('Check blk fstype attempt %d of 10', i + 1) + log.info("Check blk fstype attempt %d of 10", i + 1) current_fs = _checkblk(name) if current_fs == fs_type: - ret['comment'] = ('{0} has been formatted ' - 'with {1}').format(name, fs_type) - ret['changes'] = {'new': fs_type, 'old': current_fs} - ret['result'] = True + ret["comment"] = ("{0} has been formatted " "with {1}").format( + name, fs_type + ) + ret["changes"] = {"new": fs_type, "old": current_fs} + ret["result"] = True return ret - if current_fs == '': - log.info('Waiting 3s before next check') + if current_fs == "": + log.info("Waiting 3s before next check") time.sleep(3) else: break - ret['comment'] = 'Failed to format {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to format {0}".format(name) + ret["result"] = False return ret def _checkblk(name): - ''' + """ Check if the blk exists and return its fstype if ok - ''' + """ - blk = __salt__['cmd.run']( - ['blkid', '-o', 'value', '-s', 'TYPE', name], - ignore_retcode=True) - return '' if not blk else blk + blk = __salt__["cmd.run"]( + ["blkid", "-o", "value", "-s", "TYPE", name], ignore_retcode=True + ) + return "" if not blk else blk diff --git a/salt/states/boto3_elasticache.py b/salt/states/boto3_elasticache.py index 564df0be3d3..0eea912ed2f 100644 --- a/salt/states/boto3_elasticache.py +++ b/salt/states/boto3_elasticache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Elasticache with boto3 ============================= @@ -86,24 +86,23 @@ passed in as a dict, or as a string to pull from pillars or minion config: - profile: keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if boto is available. - ''' - if 'boto3_elasticache.cache_cluster_exists' in __salt__: - return 'boto3_elasticache' - else: - return False + """ + if "boto3_elasticache.cache_cluster_exists" in __salt__: + return "boto3_elasticache" + return (False, "boto3_elasticcache module could not be loaded") def _diff_cache_cluster(current, desired): - ''' + """ If you need to enhance what modify_cache_cluster() considers when deciding what is to be (or can be) updated, add it to 'modifiable' below. It's a dict mapping the param as used in modify_cache_cluster() to that in describe_cache_clusters(). Any data fiddlery that @@ -114,35 +113,45 @@ def _diff_cache_cluster(current, desired): settings which might have previously been changed from their 'default' values will not be changed back simply by leaving them out of 'desired'. This is both intentional, and much, much easier to code :) - ''' + """ ### The data formats are annoyingly (and as far as I can can tell, unnecessarily) ### different - we have to munge to a common format to compare... - if current.get('SecurityGroups') is not None: - current['SecurityGroupIds'] = [s['SecurityGroupId'] for s in current['SecurityGroups']] - if current.get('CacheSecurityGroups') is not None: - current['CacheSecurityGroupNames'] = [c['CacheSecurityGroupName'] for c in current['CacheSecurityGroups']] - if current.get('NotificationConfiguration') is not None: - current['NotificationTopicArn'] = current['NotificationConfiguration']['TopicArn'] - current['NotificationTopicStatus'] = current['NotificationConfiguration']['TopicStatus'] - if current.get('CacheParameterGroup') is not None: - current['CacheParameterGroupName'] = current['CacheParameterGroup']['CacheParameterGroupName'] + if current.get("SecurityGroups") is not None: + current["SecurityGroupIds"] = [ + s["SecurityGroupId"] for s in current["SecurityGroups"] + ] + if current.get("CacheSecurityGroups") is not None: + current["CacheSecurityGroupNames"] = [ + c["CacheSecurityGroupName"] for c in current["CacheSecurityGroups"] + ] + if current.get("NotificationConfiguration") is not None: + current["NotificationTopicArn"] = current["NotificationConfiguration"][ + "TopicArn" + ] + current["NotificationTopicStatus"] = current["NotificationConfiguration"][ + "TopicStatus" + ] + if current.get("CacheParameterGroup") is not None: + current["CacheParameterGroupName"] = current["CacheParameterGroup"][ + "CacheParameterGroupName" + ] modifiable = { - 'AutoMinorVersionUpgrade': 'AutoMinorVersionUpgrade', - 'AZMode': 'AZMode', - 'CacheNodeType': 'CacheNodeType', - 'CacheNodeIdsToRemove': None, - 'CacheParameterGroupName': 'CacheParameterGroupName', - 'CacheSecurityGroupNames': 'CacheSecurityGroupNames', - 'EngineVersion': 'EngineVersion', - 'NewAvailabilityZones': None, - 'NotificationTopicArn': 'NotificationTopicArn', - 'NotificationTopicStatus': 'NotificationTopicStatus', - 'NumCacheNodes': 'NumCacheNodes', - 'PreferredMaintenanceWindow': 'PreferredMaintenanceWindow', - 'SecurityGroupIds': 'SecurityGroupIds', - 'SnapshotRetentionLimit': 'SnapshotRetentionLimit', - 'SnapshotWindow': 'SnapshotWindow' + "AutoMinorVersionUpgrade": "AutoMinorVersionUpgrade", + "AZMode": "AZMode", + "CacheNodeType": "CacheNodeType", + "CacheNodeIdsToRemove": None, + "CacheParameterGroupName": "CacheParameterGroupName", + "CacheSecurityGroupNames": "CacheSecurityGroupNames", + "EngineVersion": "EngineVersion", + "NewAvailabilityZones": None, + "NotificationTopicArn": "NotificationTopicArn", + "NotificationTopicStatus": "NotificationTopicStatus", + "NumCacheNodes": "NumCacheNodes", + "PreferredMaintenanceWindow": "PreferredMaintenanceWindow", + "SecurityGroupIds": "SecurityGroupIds", + "SnapshotRetentionLimit": "SnapshotRetentionLimit", + "SnapshotWindow": "SnapshotWindow", } need_update = {} @@ -160,9 +169,17 @@ def _diff_cache_cluster(current, desired): return need_update -def cache_cluster_present(name, wait=900, security_groups=None, region=None, key=None, - keyid=None, profile=None, **args): - ''' +def cache_cluster_present( + name, + wait=900, + security_groups=None, + region=None, + key=None, + keyid=None, + profile=None, + **args +): + """ Ensure a given cache cluster exists. name @@ -409,20 +426,20 @@ def cache_cluster_present(name, wait=900, security_groups=None, region=None, key profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - current = __salt__['boto3_elasticache.' - 'describe_cache_clusters'](name, region=region, key=key, - keyid=keyid, profile=profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + current = __salt__["boto3_elasticache." "describe_cache_clusters"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if current: check_update = True else: check_update = False only_on_modify = [ - 'CacheNodeIdsToRemove', - 'NewAvailabilityZones', - 'NotificationTopicStatus' + "CacheNodeIdsToRemove", + "NewAvailabilityZones", + "NotificationTopicStatus", ] create_args = {} for k, v in args.items(): @@ -430,62 +447,74 @@ def cache_cluster_present(name, wait=900, security_groups=None, region=None, key check_update = True else: create_args[k] = v - if __opts__['test']: - ret['comment'] = 'Cache cluster {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Cache cluster {0} would be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto3_elasticache.' - 'create_cache_cluster'](name, wait=wait, security_groups=security_groups, - region=region, key=key, keyid=keyid, - profile=profile, **create_args) + created = __salt__["boto3_elasticache." "create_cache_cluster"]( + name, + wait=wait, + security_groups=security_groups, + region=region, + key=key, + keyid=keyid, + profile=profile, + **create_args + ) if created: - new = __salt__['boto3_elasticache.' - 'describe_cache_clusters'](name, region=region, key=key, - keyid=keyid, profile=profile) - ret['comment'] = 'Cache cluster {0} was created.'.format(name) - ret['changes']['old'] = None - ret['changes']['new'] = new[0] + new = __salt__["boto3_elasticache." "describe_cache_clusters"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["comment"] = "Cache cluster {0} was created.".format(name) + ret["changes"]["old"] = None + ret["changes"]["new"] = new[0] else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} cache cluster.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} cache cluster.".format(name) if check_update: # Refresh this in case we're updating from 'only_on_modify' above... - updated = __salt__['boto3_elasticache.' - 'describe_cache_clusters'](name, region=region, key=key, - keyid=keyid, profile=profile) - need_update = _diff_cache_cluster(updated['CacheClusters'][0], args) + updated = __salt__["boto3_elasticache." "describe_cache_clusters"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + need_update = _diff_cache_cluster(updated["CacheClusters"][0], args) if need_update: - if __opts__['test']: - ret['comment'] = 'Cache cluster {0} would be modified.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Cache cluster {0} would be modified.".format(name) + ret["result"] = None return ret - modified = __salt__['boto3_elasticache.' - 'modify_cache_cluster'](name, wait=wait, - security_groups=security_groups, - region=region, key=key, keyid=keyid, - profile=profile, **need_update) + modified = __salt__["boto3_elasticache." "modify_cache_cluster"]( + name, + wait=wait, + security_groups=security_groups, + region=region, + key=key, + keyid=keyid, + profile=profile, + **need_update + ) if modified: - new = __salt__['boto3_elasticache.' - 'describe_cache_clusters'](name, region=region, key=key, - keyid=keyid, profile=profile) - if ret['comment']: # 'create' just ran... - ret['comment'] += ' ... and then immediately modified.' + new = __salt__["boto3_elasticache." "describe_cache_clusters"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + if ret["comment"]: # 'create' just ran... + ret["comment"] += " ... and then immediately modified." else: - ret['comment'] = 'Cache cluster {0} was modified.'.format(name) - ret['changes']['old'] = current - ret['changes']['new'] = new[0] + ret["comment"] = "Cache cluster {0} was modified.".format(name) + ret["changes"]["old"] = current + ret["changes"]["new"] = new[0] else: - ret['result'] = False - ret['comment'] = 'Failed to modify cache cluster {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to modify cache cluster {0}.".format(name) else: - ret['comment'] = 'Cache cluster {0} is in the desired state.'.format(name) + ret["comment"] = "Cache cluster {0} is in the desired state.".format(name) return ret -def cache_cluster_absent(name, wait=600, region=None, key=None, keyid=None, - profile=None, **args): - ''' +def cache_cluster_absent( + name, wait=600, region=None, key=None, keyid=None, profile=None, **args +): + """ Ensure a given cache cluster is deleted. name @@ -519,33 +548,39 @@ def cache_cluster_absent(name, wait=600, region=None, key=None, keyid=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - exists = __salt__['boto3_elasticache.' - 'cache_cluster_exists'](name, region=region, key=key, - keyid=keyid, profile=profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + exists = __salt__["boto3_elasticache." "cache_cluster_exists"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if exists: - if __opts__['test']: - ret['comment'] = 'Cache cluster {0} would be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Cache cluster {0} would be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto3_elasticache.' - 'delete_cache_cluster'](name, wait=wait, region=region, key=key, - keyid=keyid, profile=profile, **args) + deleted = __salt__["boto3_elasticache." "delete_cache_cluster"]( + name, + wait=wait, + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) if deleted: - ret['changes']['old'] = name - ret['changes']['new'] = None + ret["changes"]["old"] = name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} cache cluster.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} cache cluster.".format(name) else: - ret['comment'] = 'Cache cluster {0} already absent.'.format(name) + ret["comment"] = "Cache cluster {0} already absent.".format(name) return ret def _diff_replication_group(current, desired): - ''' + """ If you need to enhance what modify_replication_group() considers when deciding what is to be (or can be) updated, add it to 'modifiable' below. It's a dict mapping the param as used in modify_replication_group() to that in describe_replication_groups(). Any data fiddlery @@ -556,30 +591,31 @@ def _diff_replication_group(current, desired): settings which might have previously been changed from their 'default' values will not be changed back simply by leaving them out of 'desired'. This is both intentional, and much, much easier to code :) - ''' - if current.get('AutomaticFailover') is not None: - current['AutomaticFailoverEnabled'] = True if current['AutomaticFailover'] in ('enabled', - 'enabling') else False + """ + if current.get("AutomaticFailover") is not None: + current["AutomaticFailoverEnabled"] = ( + True if current["AutomaticFailover"] in ("enabled", "enabling") else False + ) modifiable = { # Amazingly, the AWS API provides NO WAY to query the current state of most repl group # settings! All we can do is send a modify op with the desired value, just in case it's # different. And THEN, we can't determine if it's been changed! Stupid? YOU BET! - 'AutomaticFailoverEnabled': 'AutomaticFailoverEnabled', - 'AutoMinorVersionUpgrade': None, - 'CacheNodeType': None, - 'CacheParameterGroupName': None, - 'CacheSecurityGroupNames': None, - 'EngineVersion': None, - 'NotificationTopicArn': None, - 'NotificationTopicStatus': None, - 'PreferredMaintenanceWindow': None, - 'PrimaryClusterId': None, - 'ReplicationGroupDescription': 'Description', - 'SecurityGroupIds': None, - 'SnapshotRetentionLimit': 'SnapshotRetentionLimit', - 'SnapshottingClusterId': 'SnapshottingClusterId', - 'SnapshotWindow': 'SnapshotWindow' + "AutomaticFailoverEnabled": "AutomaticFailoverEnabled", + "AutoMinorVersionUpgrade": None, + "CacheNodeType": None, + "CacheParameterGroupName": None, + "CacheSecurityGroupNames": None, + "EngineVersion": None, + "NotificationTopicArn": None, + "NotificationTopicStatus": None, + "PreferredMaintenanceWindow": None, + "PrimaryClusterId": None, + "ReplicationGroupDescription": "Description", + "SecurityGroupIds": None, + "SnapshotRetentionLimit": "SnapshotRetentionLimit", + "SnapshottingClusterId": "SnapshottingClusterId", + "SnapshotWindow": "SnapshotWindow", } need_update = {} @@ -597,9 +633,17 @@ def _diff_replication_group(current, desired): return need_update -def replication_group_present(name, wait=900, security_groups=None, region=None, key=None, - keyid=None, profile=None, **args): - ''' +def replication_group_present( + name, + wait=900, + security_groups=None, + region=None, + key=None, + keyid=None, + profile=None, + **args +): + """ Ensure a replication group exists and is in the given state. name @@ -838,83 +882,93 @@ def replication_group_present(name, wait=900, security_groups=None, region=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - current = __salt__['boto3_elasticache.' - 'describe_replication_groups'](name, region=region, key=key, - keyid=keyid, profile=profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + current = __salt__["boto3_elasticache." "describe_replication_groups"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if current: check_update = True else: check_update = False - only_on_modify = [ - 'SnapshottingClusterId', - 'NotificationTopicStatus' - ] + only_on_modify = ["SnapshottingClusterId", "NotificationTopicStatus"] create_args = {} for k, v in args.items(): if k in only_on_modify: check_update = True else: create_args[k] = v - if __opts__['test']: - ret['comment'] = 'Replication group {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Replication group {0} would be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto3_elasticache.' - 'create_replication_group'](name, wait=wait, - security_groups=security_groups, - region=region, key=key, keyid=keyid, - profile=profile, **create_args) + created = __salt__["boto3_elasticache." "create_replication_group"]( + name, + wait=wait, + security_groups=security_groups, + region=region, + key=key, + keyid=keyid, + profile=profile, + **create_args + ) if created: - new = __salt__['boto3_elasticache.' - 'describe_replication_groups'](name, region=region, key=key, - keyid=keyid, profile=profile) - ret['comment'] = 'Replication group {0} was created.'.format(name) - ret['changes']['old'] = None - ret['changes']['new'] = new[0] + new = __salt__["boto3_elasticache." "describe_replication_groups"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["comment"] = "Replication group {0} was created.".format(name) + ret["changes"]["old"] = None + ret["changes"]["new"] = new[0] else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} replication group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} replication group.".format(name) if check_update: # Refresh this in case we're updating from 'only_on_modify' above... - updated = __salt__['boto3_elasticache.' - 'describe_replication_groups'](name, region=region, key=key, - keyid=keyid, profile=profile)[0] + updated = __salt__["boto3_elasticache." "describe_replication_groups"]( + name, region=region, key=key, keyid=keyid, profile=profile + )[0] need_update = _diff_replication_group(updated, args) if need_update: - if __opts__['test']: - ret['comment'] = 'Replication group {0} would be modified.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Replication group {0} would be modified.".format(name) + ret["result"] = None return ret - modified = __salt__['boto3_elasticache.' - 'modify_replication_group'](name, wait=wait, - security_groups=security_groups, - region=region, key=key, keyid=keyid, - profile=profile, **need_update) + modified = __salt__["boto3_elasticache." "modify_replication_group"]( + name, + wait=wait, + security_groups=security_groups, + region=region, + key=key, + keyid=keyid, + profile=profile, + **need_update + ) if modified: - new = __salt__['boto3_elasticache.' - 'describe_replication_groups'](name, region=region, key=key, - keyid=keyid, profile=profile) - if ret['comment']: # 'create' just ran... - ret['comment'] += ' ... and then immediately modified.' + new = __salt__["boto3_elasticache." "describe_replication_groups"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + if ret["comment"]: # 'create' just ran... + ret["comment"] += " ... and then immediately modified." else: - ret['comment'] = 'Replication group {0} was modified.'.format(name) - ret['changes']['old'] = current[0] if current else None - ret['changes']['new'] = new[0] + ret["comment"] = "Replication group {0} was modified.".format(name) + ret["changes"]["old"] = current[0] if current else None + ret["changes"]["new"] = new[0] else: - ret['result'] = False - ret['comment'] = 'Failed to modify replication group {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to modify replication group {0}.".format(name) else: - ret['comment'] = 'Replication group {0} is in the desired state.'.format(name) + ret["comment"] = "Replication group {0} is in the desired state.".format( + name + ) return ret -def replication_group_absent(name, wait=600, region=None, key=None, keyid=None, - profile=None, **args): - ''' +def replication_group_absent( + name, wait=600, region=None, key=None, keyid=None, profile=None, **args +): + """ Ensure a given replication group is deleted. name @@ -952,33 +1006,39 @@ def replication_group_absent(name, wait=600, region=None, key=None, keyid=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - exists = __salt__['boto3_elasticache.' - 'replication_group_exists'](name, region=region, key=key, - keyid=keyid, profile=profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + exists = __salt__["boto3_elasticache." "replication_group_exists"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if exists: - if __opts__['test']: - ret['comment'] = 'Replication group {0} would be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Replication group {0} would be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto3_elasticache.' - 'delete_replication_group'](name, wait=wait, region=region, key=key, - keyid=keyid, profile=profile, **args) + deleted = __salt__["boto3_elasticache." "delete_replication_group"]( + name, + wait=wait, + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) if deleted: - ret['changes']['old'] = name - ret['changes']['new'] = None + ret["changes"]["old"] = name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} replication group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} replication group.".format(name) else: - ret['comment'] = 'Replication group {0} already absent.'.format(name) + ret["comment"] = "Replication group {0} already absent.".format(name) return ret def _diff_cache_subnet_group(current, desired): - ''' + """ If you need to enhance what modify_cache_subnet_group() considers when deciding what is to be (or can be) updated, add it to 'modifiable' below. It's a dict mapping the param as used in modify_cache_subnet_group() to that in describe_cache_subnet_group(). Any data fiddlery that @@ -989,10 +1049,10 @@ def _diff_cache_subnet_group(current, desired): settings which might have previously been changed from their 'default' values will not be changed back simply by leaving them out of 'desired'. This is both intentional, and much, much easier to code :) - ''' + """ modifiable = { - 'CacheSubnetGroupDescription': 'CacheSubnetGroupDescription', - 'SubnetIds': 'SubnetIds' + "CacheSubnetGroupDescription": "CacheSubnetGroupDescription", + "SubnetIds": "SubnetIds", } need_update = {} @@ -1010,9 +1070,10 @@ def _diff_cache_subnet_group(current, desired): return need_update -def cache_subnet_group_present(name, subnets=None, region=None, key=None, keyid=None, profile=None, - **args): - ''' +def cache_subnet_group_present( + name, subnets=None, region=None, key=None, keyid=None, profile=None, **args +): + """ Ensure cache subnet group exists. name @@ -1045,63 +1106,79 @@ def cache_subnet_group_present(name, subnets=None, region=None, key=None, keyid= profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - current = __salt__['boto3_elasticache.' - 'describe_cache_subnet_groups'](name, region=region, key=key, - keyid=keyid, profile=profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + current = __salt__["boto3_elasticache." "describe_cache_subnet_groups"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if current: check_update = True else: check_update = False - if __opts__['test']: - ret['comment'] = 'Cache subnet group {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Cache subnet group {0} would be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto3_elasticache.' - 'create_cache_subnet_group'](name, subnets=subnets, - region=region, key=key, keyid=keyid, - profile=profile, **args) + created = __salt__["boto3_elasticache." "create_cache_subnet_group"]( + name, + subnets=subnets, + region=region, + key=key, + keyid=keyid, + profile=profile, + **args + ) if created: - new = __salt__['boto3_elasticache.' - 'describe_cache_subnet_groups'](name, region=region, key=key, - keyid=keyid, profile=profile) - ret['comment'] = 'Cache subnet group {0} was created.'.format(name) - ret['changes']['old'] = None - ret['changes']['new'] = new[0] + new = __salt__["boto3_elasticache." "describe_cache_subnet_groups"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["comment"] = "Cache subnet group {0} was created.".format(name) + ret["changes"]["old"] = None + ret["changes"]["new"] = new[0] else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} cache subnet group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} cache subnet group.".format(name) if check_update: need_update = _diff_cache_subnet_group(current, args) if need_update: - if __opts__['test']: - ret['comment'] = 'Cache subnet group {0} would be modified.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Cache subnet group {0} would be modified.".format( + name + ) + ret["result"] = None return ret - modified = __salt__['boto3_elasticache.' - 'modify_cache_subnet_group'](name, subnets=subnets, - region=region, key=key, keyid=keyid, - profile=profile, **need_update) + modified = __salt__["boto3_elasticache." "modify_cache_subnet_group"]( + name, + subnets=subnets, + region=region, + key=key, + keyid=keyid, + profile=profile, + **need_update + ) if modified: - new = __salt__['boto3_elasticache.' - 'describe_cache_subnet_groups'](name, region=region, key=key, - keyid=keyid, profile=profile) - ret['comment'] = 'Cache subnet group {0} was modified.'.format(name) - ret['changes']['old'] = current['CacheSubetGroups'][0] - ret['changes']['new'] = new[0] + new = __salt__["boto3_elasticache." "describe_cache_subnet_groups"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["comment"] = "Cache subnet group {0} was modified.".format(name) + ret["changes"]["old"] = current["CacheSubetGroups"][0] + ret["changes"]["new"] = new[0] else: - ret['result'] = False - ret['comment'] = 'Failed to modify cache subnet group {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to modify cache subnet group {0}.".format(name) else: - ret['comment'] = 'Cache subnet group {0} is in the desired state.'.format(name) + ret["comment"] = "Cache subnet group {0} is in the desired state.".format( + name + ) return ret -def cache_subnet_group_absent(name, region=None, key=None, keyid=None, profile=None, **args): - ''' +def cache_subnet_group_absent( + name, region=None, key=None, keyid=None, profile=None, **args +): + """ Ensure a given cache subnet group is deleted. name @@ -1123,26 +1200,26 @@ def cache_subnet_group_absent(name, region=None, key=None, keyid=None, profile=N profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - args = dict([(k, v) for k, v in args.items() if not k.startswith('_')]) - exists = __salt__['boto3_elasticache.' - 'cache_subnet_group_exists'](name, region=region, key=key, - keyid=keyid, profile=profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + args = dict([(k, v) for k, v in args.items() if not k.startswith("_")]) + exists = __salt__["boto3_elasticache." "cache_subnet_group_exists"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if exists: - if __opts__['test']: - ret['comment'] = 'Cache subnet group {0} would be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Cache subnet group {0} would be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto3_elasticache.' - 'delete_cache_subnet_group'](name, region=region, key=key, - keyid=keyid, profile=profile, **args) + deleted = __salt__["boto3_elasticache." "delete_cache_subnet_group"]( + name, region=region, key=key, keyid=keyid, profile=profile, **args + ) if deleted: - ret['changes']['old'] = name - ret['changes']['new'] = None + ret["changes"]["old"] = name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} cache_subnet group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} cache_subnet group.".format(name) else: - ret['comment'] = 'Cache subnet group {0} already absent.'.format(name) + ret["comment"] = "Cache subnet group {0} already absent.".format(name) return ret diff --git a/salt/states/boto3_elasticsearch.py b/salt/states/boto3_elasticsearch.py index 20c1a5e088d..b8e7ab0d0eb 100644 --- a/salt/states/boto3_elasticsearch.py +++ b/salt/states/boto3_elasticsearch.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Elasticsearch Service ============================ @@ -42,10 +42,11 @@ Manage Elasticsearch Service :codeauthor: Herbert Buurman <herbert.buurman@ogd.nl> :depends: boto3 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -55,63 +56,72 @@ from salt.utils.versions import LooseVersion # Import 3rd-party libs log = logging.getLogger(__name__) -__virtualname__ = 'boto3_elasticsearch' +__virtualname__ = "boto3_elasticsearch" def __virtual__(): - ''' + """ Only load if boto3 and the required module functions are available. - ''' + """ requirements = { - 'salt': [ - 'boto3_elasticsearch.describe_elasticsearch_domain', - 'boto3_elasticsearch.create_elasticsearch_domain', - 'boto3_elasticsearch.update_elasticsearch_domain_config', - 'boto3_elasticsearch.exists', - 'boto3_elasticsearch.get_upgrade_status', - 'boto3_elasticsearch.wait_for_upgrade', - 'boto3_elasticsearch.check_upgrade_eligibility', - 'boto3_elasticsearch.upgrade_elasticsearch_domain', + "salt": [ + "boto3_elasticsearch.describe_elasticsearch_domain", + "boto3_elasticsearch.create_elasticsearch_domain", + "boto3_elasticsearch.update_elasticsearch_domain_config", + "boto3_elasticsearch.exists", + "boto3_elasticsearch.get_upgrade_status", + "boto3_elasticsearch.wait_for_upgrade", + "boto3_elasticsearch.check_upgrade_eligibility", + "boto3_elasticsearch.upgrade_elasticsearch_domain", ], } - for req in requirements['salt']: + for req in requirements["salt"]: if req not in __salt__: - return (False, 'A required function was not found in __salt__: {}'.format(req)) + return ( + False, + "A required function was not found in __salt__: {}".format(req), + ) return __virtualname__ def _check_return_value(ret): - ''' + """ Helper function to check if the 'result' key of the return value has been properly set. This is to detect unexpected code-paths that would otherwise return a 'success'-y value but not actually be succesful. :param dict ret: The returned value of a state function. - ''' - if ret['result'] == 'oops': - ret['result'] = False - ret['comment'].append('An internal error has occurred: The result value was ' - 'not properly changed.') + """ + if ret["result"] == "oops": + ret["result"] = False + ret["comment"].append( + "An internal error has occurred: The result value was " + "not properly changed." + ) return ret def present( - name, - elasticsearch_version=None, - elasticsearch_cluster_config=None, - ebs_options=None, - access_policies=None, - snapshot_options=None, - vpc_options=None, - cognito_options=None, - encryption_at_rest_options=None, - node_to_node_encryption_options=None, - advanced_options=None, - log_publishing_options=None, - blocking=True, - tags=None, - region=None, keyid=None, key=None, profile=None): - ''' + name, + elasticsearch_version=None, + elasticsearch_cluster_config=None, + ebs_options=None, + access_policies=None, + snapshot_options=None, + vpc_options=None, + cognito_options=None, + encryption_at_rest_options=None, + node_to_node_encryption_options=None, + advanced_options=None, + log_publishing_options=None, + blocking=True, + tags=None, + region=None, + keyid=None, + key=None, + profile=None, +): + """ Ensure an Elasticsearch Domain exists. :param str name: The name of the Elasticsearch domain that you are creating. @@ -242,151 +252,185 @@ def present( - tags: foo: bar baz: qux - ''' - ret = {'name': name, 'result': 'oops', 'comment': [], 'changes': {}} + """ + ret = {"name": name, "result": "oops", "comment": [], "changes": {}} action = None current_domain = None - target_conf = salt.utils.data.filter_falsey({ - 'DomainName': name, - 'ElasticsearchClusterConfig': elasticsearch_cluster_config, - 'EBSOptions': ebs_options, - 'AccessPolicies': (salt.utils.json.dumps(access_policies) - if isinstance(access_policies, dict) - else access_policies), - 'SnapshotOptions': snapshot_options, - 'VPCOptions': vpc_options, - 'CognitoOptions': cognito_options, - 'AdvancedOptions': advanced_options, - 'LogPublishingOptions': log_publishing_options, - }, recurse_depth=3) - res = __salt__['boto3_elasticsearch.describe_elasticsearch_domain']( - name, - region=region, keyid=keyid, key=key, profile=profile) - if not res['result']: - ret['result'] = False - if 'ResourceNotFoundException' in res['error']: - action = 'create' - config_diff = {'old': None, 'new': target_conf} + target_conf = salt.utils.data.filter_falsey( + { + "DomainName": name, + "ElasticsearchClusterConfig": elasticsearch_cluster_config, + "EBSOptions": ebs_options, + "AccessPolicies": ( + salt.utils.json.dumps(access_policies) + if isinstance(access_policies, dict) + else access_policies + ), + "SnapshotOptions": snapshot_options, + "VPCOptions": vpc_options, + "CognitoOptions": cognito_options, + "AdvancedOptions": advanced_options, + "LogPublishingOptions": log_publishing_options, + }, + recurse_depth=3, + ) + res = __salt__["boto3_elasticsearch.describe_elasticsearch_domain"]( + name, region=region, keyid=keyid, key=key, profile=profile + ) + if not res["result"]: + ret["result"] = False + if "ResourceNotFoundException" in res["error"]: + action = "create" + config_diff = {"old": None, "new": target_conf} else: - ret['comment'].append(res['error']) + ret["comment"].append(res["error"]) else: - current_domain = salt.utils.data.filter_falsey(res['response'], recurse_depth=3) - current_domain_version = current_domain['ElasticsearchVersion'] + current_domain = salt.utils.data.filter_falsey(res["response"], recurse_depth=3) + current_domain_version = current_domain["ElasticsearchVersion"] # Remove some values from current_domain that cannot be updated - for item in ['DomainId', 'UpgradeProcessing', 'Created', 'Deleted', 'Processing', - 'Endpoints', 'ARN', 'EncryptionAtRestOptions', 'NodeToNodeEncryptionOptions', - 'ElasticsearchVersion', 'ServiceSoftwareOptions']: + for item in [ + "DomainId", + "UpgradeProcessing", + "Created", + "Deleted", + "Processing", + "Endpoints", + "ARN", + "EncryptionAtRestOptions", + "NodeToNodeEncryptionOptions", + "ElasticsearchVersion", + "ServiceSoftwareOptions", + ]: if item in current_domain: del current_domain[item] # Further remove values from VPCOptions (if present) that are read-only - for item in ['VPCId', 'AvailabilityZones']: - if item in current_domain.get('VPCOptions', {}): - del current_domain['VPCOptions'][item] + for item in ["VPCId", "AvailabilityZones"]: + if item in current_domain.get("VPCOptions", {}): + del current_domain["VPCOptions"][item] # Some special cases - if 'CognitoOptions' in current_domain: - if 'CognitoOptions' not in target_conf and not current_domain['CognitoOptions']['Enabled']: - del current_domain['CognitoOptions'] - if 'AdvancedOptions' not in target_conf and \ - 'rest.action.multi.allow_explicit_index' in current_domain['AdvancedOptions']: - del current_domain['AdvancedOptions']['rest.action.multi.allow_explicit_index'] - if not current_domain['AdvancedOptions']: - del current_domain['AdvancedOptions'] + if "CognitoOptions" in current_domain: + if ( + "CognitoOptions" not in target_conf + and not current_domain["CognitoOptions"]["Enabled"] + ): + del current_domain["CognitoOptions"] + if ( + "AdvancedOptions" not in target_conf + and "rest.action.multi.allow_explicit_index" + in current_domain["AdvancedOptions"] + ): + del current_domain["AdvancedOptions"][ + "rest.action.multi.allow_explicit_index" + ] + if not current_domain["AdvancedOptions"]: + del current_domain["AdvancedOptions"] # Compare current configuration with provided configuration config_diff = salt.utils.data.recursive_diff(current_domain, target_conf) if config_diff: - action = 'update' + action = "update" # Compare ElasticsearchVersion separately, as the update procedure differs. if elasticsearch_version and current_domain_version != elasticsearch_version: - action = 'upgrade' + action = "upgrade" - if action in ['create', 'update']: - if __opts__['test']: - ret['result'] = None - ret['comment'].append('The Elasticsearch Domain "{}" would have been {}d.' - ''.format(name, action)) - ret['changes'] = config_diff + if action in ["create", "update"]: + if __opts__["test"]: + ret["result"] = None + ret["comment"].append( + 'The Elasticsearch Domain "{}" would have been {}d.' + "".format(name, action) + ) + ret["changes"] = config_diff else: - boto_kwargs = salt.utils.data.filter_falsey({ - 'elasticsearch_version': elasticsearch_version, - 'elasticsearch_cluster_config': elasticsearch_cluster_config, - 'ebs_options': ebs_options, - 'vpc_options': vpc_options, - 'access_policies': access_policies, - 'snapshot_options': snapshot_options, - 'cognito_options': cognito_options, - 'encryption_at_rest_options': encryption_at_rest_options, - 'node_to_node_encryption_options': node_to_node_encryption_options, - 'advanced_options': advanced_options, - 'log_publishing_options': log_publishing_options, - 'blocking': blocking, - 'region': region, 'keyid': keyid, 'key': key, 'profile': profile, - }) - if action == 'update': + boto_kwargs = salt.utils.data.filter_falsey( + { + "elasticsearch_version": elasticsearch_version, + "elasticsearch_cluster_config": elasticsearch_cluster_config, + "ebs_options": ebs_options, + "vpc_options": vpc_options, + "access_policies": access_policies, + "snapshot_options": snapshot_options, + "cognito_options": cognito_options, + "encryption_at_rest_options": encryption_at_rest_options, + "node_to_node_encryption_options": node_to_node_encryption_options, + "advanced_options": advanced_options, + "log_publishing_options": log_publishing_options, + "blocking": blocking, + "region": region, + "keyid": keyid, + "key": key, + "profile": profile, + } + ) + if action == "update": # Drop certain kwargs that do not apply to updates. - for item in ['elasticsearch_version', 'encryption_at_rest_options', - 'node_to_node_encryption_options']: + for item in [ + "elasticsearch_version", + "encryption_at_rest_options", + "node_to_node_encryption_options", + ]: if item in boto_kwargs: del boto_kwargs[item] - res = __salt__['boto3_elasticsearch.{}_elasticsearch_domain{}' - ''.format(action, '_config' if action == 'update' else '')]( - name, - **boto_kwargs) - if 'error' in res: - ret['result'] = False - ret['comment'].append(res['error']) + res = __salt__[ + "boto3_elasticsearch.{}_elasticsearch_domain{}" + "".format(action, "_config" if action == "update" else "") + ](name, **boto_kwargs) + if "error" in res: + ret["result"] = False + ret["comment"].append(res["error"]) else: - ret['result'] = True - ret['comment'].append('Elasticsearch Domain "{}" has been {}d.'.format(name, action)) - ret['changes'] = config_diff - elif action == 'upgrade': + ret["result"] = True + ret["comment"].append( + 'Elasticsearch Domain "{}" has been {}d.'.format(name, action) + ) + ret["changes"] = config_diff + elif action == "upgrade": res = upgraded( name, elasticsearch_version, - region=region, keyid=keyid, key=key, profile=profile) - ret['result'] = res['result'] - ret['comment'].extend(res['comment']) - if res['changes']: + region=region, + keyid=keyid, + key=key, + profile=profile, + ) + ret["result"] = res["result"] + ret["comment"].extend(res["comment"]) + if res["changes"]: salt.utils.dictupdate.set_dict_key_value( - ret, - 'changes:old:version', - res['changes']['old']) + ret, "changes:old:version", res["changes"]["old"] + ) salt.utils.dictupdate.set_dict_key_value( - ret, - 'changes:new:version', - res['changes']['new']) + ret, "changes:new:version", res["changes"]["new"] + ) if tags is not None: res = tagged( name, tags=tags, replace=True, - region=region, keyid=keyid, key=key, profile=profile) - ret['result'] = res['result'] - ret['comment'].extend(res['comment']) - if 'old' in res['changes']: + region=region, + keyid=keyid, + key=key, + profile=profile, + ) + ret["result"] = res["result"] + ret["comment"].extend(res["comment"]) + if "old" in res["changes"]: salt.utils.dictupdate.update_dict_key_value( - ret, - 'changes:old:tags', - res['changes']['old'] + ret, "changes:old:tags", res["changes"]["old"] ) - if 'new' in res['changes']: + if "new" in res["changes"]: salt.utils.dictupdate.update_dict_key_value( - ret, - 'changes:new:tags', - res['changes']['new'] + ret, "changes:new:tags", res["changes"]["new"] ) ret = _check_return_value(ret) return ret -def absent( - name, - blocking=True, - region=None, keyid=None, key=None, profile=None): - ''' +def absent(name, blocking=True, region=None, keyid=None, key=None, profile=None): + """ Ensure the Elasticsearch Domain specified does not exist. :param str name: The name of the Elasticsearch domain to be made absent. @@ -403,49 +447,62 @@ def absent( boto3_elasticsearch.absent: - name: my_domain - region: eu-west-1 - ''' - ret = {'name': name, 'result': 'oops', 'comment': [], 'changes': {}} + """ + ret = {"name": name, "result": "oops", "comment": [], "changes": {}} - res = __salt__['boto3_elasticsearch.exists']( - name, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append(res['error']) - elif res['result']: - if __opts__['test']: - ret['result'] = None - ret['comment'].append('Elasticsearch domain "{}" would have been removed.' - ''.format(name)) - ret['changes'] = {'old': name, 'new': None} + res = __salt__["boto3_elasticsearch.exists"]( + name, region=region, keyid=keyid, key=key, profile=profile + ) + if "error" in res: + ret["result"] = False + ret["comment"].append(res["error"]) + elif res["result"]: + if __opts__["test"]: + ret["result"] = None + ret["comment"].append( + 'Elasticsearch domain "{}" would have been removed.' "".format(name) + ) + ret["changes"] = {"old": name, "new": None} else: - res = __salt__['boto3_elasticsearch.delete_elasticsearch_domain']( + res = __salt__["boto3_elasticsearch.delete_elasticsearch_domain"]( domain_name=name, blocking=blocking, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append('Error deleting Elasticsearch domain "{}": {}' - ''.format(name, res['error'])) + region=region, + keyid=keyid, + key=key, + profile=profile, + ) + if "error" in res: + ret["result"] = False + ret["comment"].append( + 'Error deleting Elasticsearch domain "{}": {}' + "".format(name, res["error"]) + ) else: - ret['result'] = True - ret['comment'].append('Elasticsearch domain "{}" has been deleted.' - ''.format(name)) - ret['changes'] = {'old': name, 'new': None} + ret["result"] = True + ret["comment"].append( + 'Elasticsearch domain "{}" has been deleted.' "".format(name) + ) + ret["changes"] = {"old": name, "new": None} else: - ret['result'] = True - ret['comment'].append('Elasticsearch domain "{}" is already absent.' - ''.format(name)) + ret["result"] = True + ret["comment"].append( + 'Elasticsearch domain "{}" is already absent.' "".format(name) + ) ret = _check_return_value(ret) return ret def upgraded( - name, - elasticsearch_version, - blocking=True, - region=None, keyid=None, key=None, profile=None): - ''' + name, + elasticsearch_version, + blocking=True, + region=None, + keyid=None, + key=None, + profile=None, +): + """ Ensures the Elasticsearch domain specified runs on the specified version of elasticsearch. Only upgrades are possible as downgrades require a manual snapshot and an S3 bucket to store them in. @@ -467,118 +524,152 @@ def upgraded( - name: my_domain - elasticsearch_version: '7.2' - region: eu-west-1 - ''' - ret = {'name': name, 'result': 'oops', 'comment': [], 'changes': {}} + """ + ret = {"name": name, "result": "oops", "comment": [], "changes": {}} current_domain = None - res = __salt__['boto3_elasticsearch.describe_elasticsearch_domain']( - name, - region=region, keyid=keyid, key=key, profile=profile) - if not res['result']: - ret['result'] = False - if 'ResourceNotFoundException' in res['error']: - ret['comment'].append('The Elasticsearch domain "{}" does not exist.' - ''.format(name)) + res = __salt__["boto3_elasticsearch.describe_elasticsearch_domain"]( + name, region=region, keyid=keyid, key=key, profile=profile + ) + if not res["result"]: + ret["result"] = False + if "ResourceNotFoundException" in res["error"]: + ret["comment"].append( + 'The Elasticsearch domain "{}" does not exist.' "".format(name) + ) else: - ret['comment'].append(res['error']) + ret["comment"].append(res["error"]) else: - current_domain = res['response'] - current_version = current_domain['ElasticsearchVersion'] + current_domain = res["response"] + current_version = current_domain["ElasticsearchVersion"] if elasticsearch_version and current_version == elasticsearch_version: - ret['result'] = True - ret['comment'].append('The Elasticsearch domain "{}" is already ' - 'at the desired version {}' - ''.format(name, elasticsearch_version)) + ret["result"] = True + ret["comment"].append( + 'The Elasticsearch domain "{}" is already ' + "at the desired version {}" + "".format(name, elasticsearch_version) + ) elif LooseVersion(elasticsearch_version) < LooseVersion(current_version): - ret['result'] = False - ret['comment'].append('Elasticsearch domain "{}" cannot be downgraded ' - 'to version "{}".' - ''.format(name, elasticsearch_version)) - if isinstance(ret['result'], bool): + ret["result"] = False + ret["comment"].append( + 'Elasticsearch domain "{}" cannot be downgraded ' + 'to version "{}".' + "".format(name, elasticsearch_version) + ) + if isinstance(ret["result"], bool): return ret - log.debug('%s :upgraded: Check upgrade in progress', __name__) + log.debug("%s :upgraded: Check upgrade in progress", __name__) # Check if an upgrade is already in progress - res = __salt__['boto3_elasticsearch.get_upgrade_status']( - name, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append('Error determining current upgrade status ' - 'of domain "{}": {}'.format(name, res['error'])) + res = __salt__["boto3_elasticsearch.get_upgrade_status"]( + name, region=region, keyid=keyid, key=key, profile=profile + ) + if "error" in res: + ret["result"] = False + ret["comment"].append( + "Error determining current upgrade status " + 'of domain "{}": {}'.format(name, res["error"]) + ) return ret - if res['response'].get('StepStatus') == 'IN_PROGRESS': + if res["response"].get("StepStatus") == "IN_PROGRESS": if blocking: # An upgrade is already in progress, wait for it to complete - res2 = __salt__['boto3_elasticsearch.wait_for_upgrade']( - name, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res2: - ret['result'] = False - ret['comment'].append('Error waiting for upgrade of domain ' - '"{}" to complete: {}' - ''.format(name, res2['error'])) - elif res2['response'].get('UpgradeName', '').endswith(elasticsearch_version): - ret['result'] = True - ret['comment'].append('Elasticsearch Domain "{}" is ' - 'already at version "{}".' - ''.format(name, elasticsearch_version)) + res2 = __salt__["boto3_elasticsearch.wait_for_upgrade"]( + name, region=region, keyid=keyid, key=key, profile=profile + ) + if "error" in res2: + ret["result"] = False + ret["comment"].append( + "Error waiting for upgrade of domain " + '"{}" to complete: {}' + "".format(name, res2["error"]) + ) + elif ( + res2["response"].get("UpgradeName", "").endswith(elasticsearch_version) + ): + ret["result"] = True + ret["comment"].append( + 'Elasticsearch Domain "{}" is ' + 'already at version "{}".' + "".format(name, elasticsearch_version) + ) else: # We are not going to wait for it to complete, so bail. - ret['result'] = True - ret['comment'].append('An upgrade of Elasticsearch domain "{}" ' - 'is already underway: {}' - ''.format(name, res['response'].get('UpgradeName'))) - if isinstance(ret['result'], bool): + ret["result"] = True + ret["comment"].append( + 'An upgrade of Elasticsearch domain "{}" ' + "is already underway: {}" + "".format(name, res["response"].get("UpgradeName")) + ) + if isinstance(ret["result"], bool): return ret - log.debug('%s :upgraded: Check upgrade eligibility', __name__) + log.debug("%s :upgraded: Check upgrade eligibility", __name__) # Check if the domain is eligible for an upgrade - res = __salt__['boto3_elasticsearch.check_upgrade_eligibility']( + res = __salt__["boto3_elasticsearch.check_upgrade_eligibility"]( name, elasticsearch_version, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append('Error checking upgrade eligibility for ' - 'domain "{}": {}'.format(name, res['error'])) - elif not res['response']: - ret['result'] = False - ret['comment'].append('The Elasticsearch Domain "{}" is not eligible to ' - 'be upgraded to version {}.' - ''.format(name, elasticsearch_version)) + region=region, + keyid=keyid, + key=key, + profile=profile, + ) + if "error" in res: + ret["result"] = False + ret["comment"].append( + "Error checking upgrade eligibility for " + 'domain "{}": {}'.format(name, res["error"]) + ) + elif not res["response"]: + ret["result"] = False + ret["comment"].append( + 'The Elasticsearch Domain "{}" is not eligible to ' + "be upgraded to version {}." + "".format(name, elasticsearch_version) + ) else: - log.debug('%s :upgraded: Start the upgrade', __name__) + log.debug("%s :upgraded: Start the upgrade", __name__) # Start the upgrade - if __opts__['test']: - ret['result'] = None - ret['comment'].append('The Elasticsearch version for domain "{}" would have been upgraded.') - ret['changes'] = {'old': current_domain['ElasticsearchVersion'], - 'new': elasticsearch_version} + if __opts__["test"]: + ret["result"] = None + ret["comment"].append( + 'The Elasticsearch version for domain "{}" would have been upgraded.' + ) + ret["changes"] = { + "old": current_domain["ElasticsearchVersion"], + "new": elasticsearch_version, + } else: - res = __salt__['boto3_elasticsearch.upgrade_elasticsearch_domain']( + res = __salt__["boto3_elasticsearch.upgrade_elasticsearch_domain"]( name, elasticsearch_version, blocking=blocking, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append('Error upgrading Elasticsearch domain "{}": {}' - ''.format(name, res['error'])) + region=region, + keyid=keyid, + key=key, + profile=profile, + ) + if "error" in res: + ret["result"] = False + ret["comment"].append( + 'Error upgrading Elasticsearch domain "{}": {}' + "".format(name, res["error"]) + ) else: - ret['result'] = True - ret['comment'].append('The Elasticsearch domain "{}" has been ' - 'upgraded to version {}.' - ''.format(name, elasticsearch_version)) - ret['changes'] = {'old': current_domain['ElasticsearchVersion'], - 'new': elasticsearch_version} + ret["result"] = True + ret["comment"].append( + 'The Elasticsearch domain "{}" has been ' + "upgraded to version {}." + "".format(name, elasticsearch_version) + ) + ret["changes"] = { + "old": current_domain["ElasticsearchVersion"], + "new": elasticsearch_version, + } ret = _check_return_value(ret) return ret -def latest( - name, - minor_only=True, - region=None, keyid=None, key=None, profile=None): - ''' +def latest(name, minor_only=True, region=None, keyid=None, key=None, profile=None): + """ Ensures the Elasticsearch domain specifies runs on the latest compatible version of elasticsearch, upgrading it if it is not. @@ -602,78 +693,98 @@ def latest( - name: my_domain - minor_only: True - region: eu-west-1 - ''' - ret = {'name': name, 'result': 'oops', 'comment': [], 'changes': {}} + """ + ret = {"name": name, "result": "oops", "comment": [], "changes": {}} # Get current version - res = __salt__['boto3_elasticsearch.describe_elasticsearch_domain']( - domain_name=name, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append('Error getting information of Elasticsearch domain "{}": {}' - ''.format(name, res['error'])) + res = __salt__["boto3_elasticsearch.describe_elasticsearch_domain"]( + domain_name=name, region=region, keyid=keyid, key=key, profile=profile + ) + if "error" in res: + ret["result"] = False + ret["comment"].append( + 'Error getting information of Elasticsearch domain "{}": {}' + "".format(name, res["error"]) + ) else: - current_version = res['response']['ElasticsearchVersion'] + current_version = res["response"]["ElasticsearchVersion"] # Get latest compatible version latest_version = None - res = __salt__['boto3_elasticsearch.get_compatible_elasticsearch_versions']( - domain_name=name, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append('Error getting compatible Elasticsearch versions ' - 'for Elasticsearch domain "{}": {}' - ''.format(name, res['error'])) - if isinstance(ret['result'], bool): + res = __salt__["boto3_elasticsearch.get_compatible_elasticsearch_versions"]( + domain_name=name, region=region, keyid=keyid, key=key, profile=profile + ) + if "error" in res: + ret["result"] = False + ret["comment"].append( + "Error getting compatible Elasticsearch versions " + 'for Elasticsearch domain "{}": {}' + "".format(name, res["error"]) + ) + if isinstance(ret["result"], bool): return ret try: - latest_version = res['response'][0]['TargetVersions'].pop(-1) + latest_version = res["response"][0]["TargetVersions"].pop(-1) except IndexError: pass if not current_version: - ret['result'] = True - ret['comment'].append('The Elasticsearch domain "{}" can not be upgraded.' - ''.format(name)) + ret["result"] = True + ret["comment"].append( + 'The Elasticsearch domain "{}" can not be upgraded.' "".format(name) + ) elif not latest_version: - ret['result'] = True - ret['comment'].append('The Elasticsearch domain "{}" is already at ' - 'the lastest version "{}".' - ''.format(name, current_version)) + ret["result"] = True + ret["comment"].append( + 'The Elasticsearch domain "{}" is already at ' + 'the lastest version "{}".' + "".format(name, current_version) + ) else: - a_current_version = current_version.split('.') - a_latest_version = latest_version.split('.') + a_current_version = current_version.split(".") + a_latest_version = latest_version.split(".") if not (minor_only and a_current_version[0] != a_latest_version[0]): - if __opts__['test']: - ret['result'] = None - ret['comment'].append('Elasticsearch domain "{}" would have been updated ' - 'to version "{}".'.format(name, latest_version)) - ret['changes'] = {'old': current_version, 'new': latest_version} + if __opts__["test"]: + ret["result"] = None + ret["comment"].append( + 'Elasticsearch domain "{}" would have been updated ' + 'to version "{}".'.format(name, latest_version) + ) + ret["changes"] = {"old": current_version, "new": latest_version} else: ret = upgraded( name, latest_version, - region=region, keyid=keyid, key=key, profile=profile) + region=region, + keyid=keyid, + key=key, + profile=profile, + ) else: - ret['result'] = True - ret['comment'].append('Elasticsearch domain "{}" is already at its ' - 'latest minor version {}.' - ''.format(name, current_version)) + ret["result"] = True + ret["comment"].append( + 'Elasticsearch domain "{}" is already at its ' + "latest minor version {}." + "".format(name, current_version) + ) ret = _check_return_value(ret) - if ret['result'] and ret['changes'] and not minor_only: + if ret["result"] and ret["changes"] and not minor_only: # Try and see if we can upgrade again - res = latest(name, minor_only=minor_only, region=region, keyid=keyid, key=key, profile=profile) - if res['result'] and res['changes']: - ret['changes']['new'] = res['changes']['new'] - ret['comment'].extend(res['comment']) + res = latest( + name, + minor_only=minor_only, + region=region, + keyid=keyid, + key=key, + profile=profile, + ) + if res["result"] and res["changes"]: + ret["changes"]["new"] = res["changes"]["new"] + ret["comment"].extend(res["comment"]) return ret def tagged( - name, - tags=None, - replace=False, - region=None, keyid=None, key=None, profile=None): - ''' + name, tags=None, replace=False, region=None, keyid=None, key=None, profile=None +): + """ Ensures the Elasticsearch domain has the tags provided. Adds tags to the domain unless ``replace`` is set to ``True``, in which case all existing tags will be replaced with the tags provided in ``tags``. @@ -686,69 +797,89 @@ def tagged( .. versionadded:: Natrium - ''' - ret = {'name': name, 'result': 'oops', 'comment': [], 'changes': {}} + """ + ret = {"name": name, "result": "oops", "comment": [], "changes": {}} current_tags = {} # Check if the domain exists - res = __salt__['boto3_elasticsearch.exists']( - name, - region=region, keyid=keyid, key=key, profile=profile) - if res['result']: - res = __salt__['boto3_elasticsearch.list_tags']( - name, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append('Error fetching tags of Elasticsearch domain ' - '"{}": {}'.format(name, res['error'])) + res = __salt__["boto3_elasticsearch.exists"]( + name, region=region, keyid=keyid, key=key, profile=profile + ) + if res["result"]: + res = __salt__["boto3_elasticsearch.list_tags"]( + name, region=region, keyid=keyid, key=key, profile=profile + ) + if "error" in res: + ret["result"] = False + ret["comment"].append( + "Error fetching tags of Elasticsearch domain " + '"{}": {}'.format(name, res["error"]) + ) else: - current_tags = res['response'] or {} + current_tags = res["response"] or {} else: - ret['result'] = False - ret['comment'].append('Elasticsearch domain "{}" does not exist.' - ''.format(name)) - if isinstance(ret['result'], bool): + ret["result"] = False + ret["comment"].append( + 'Elasticsearch domain "{}" does not exist.' "".format(name) + ) + if isinstance(ret["result"], bool): return ret diff_tags = salt.utils.dictdiffer.deep_diff(current_tags, tags) if not diff_tags: - ret['result'] = True - ret['comment'].append('Elasticsearch domain "{}" already has the specified ' - 'tags.'.format(name)) + ret["result"] = True + ret["comment"].append( + 'Elasticsearch domain "{}" already has the specified ' "tags.".format(name) + ) else: if replace: - ret['changes'] = diff_tags + ret["changes"] = diff_tags else: - ret['changes'] = {'old': current_tags, 'new': current_tags.update(tags)} - if __opts__['test']: - ret['result'] = None - ret['comment'].append('Tags on Elasticsearch domain "{}" would have ' - 'been {}ed.'.format(name, 'replac' if replace else 'add')) + ret["changes"] = {"old": current_tags, "new": current_tags.update(tags)} + if __opts__["test"]: + ret["result"] = None + ret["comment"].append( + 'Tags on Elasticsearch domain "{}" would have ' + "been {}ed.".format(name, "replac" if replace else "add") + ) else: if replace: - res = __salt__['boto3_elasticsearch.remove_tags']( + res = __salt__["boto3_elasticsearch.remove_tags"]( tag_keys=current_tags.keys(), domain_name=name, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append('Error removing current tags from Elasticsearch ' - 'domain "{}": {}'.format(name, res['error'])) - ret['changes'] = {} - if isinstance(ret['result'], bool): + region=region, + keyid=keyid, + key=key, + profile=profile, + ) + if "error" in res: + ret["result"] = False + ret["comment"].append( + "Error removing current tags from Elasticsearch " + 'domain "{}": {}'.format(name, res["error"]) + ) + ret["changes"] = {} + if isinstance(ret["result"], bool): return ret - res = __salt__['boto3_elasticsearch.add_tags']( + res = __salt__["boto3_elasticsearch.add_tags"]( domain_name=name, tags=tags, - region=region, keyid=keyid, key=key, profile=profile) - if 'error' in res: - ret['result'] = False - ret['comment'].append('Error tagging Elasticsearch domain ' - '"{}": {}'.format(name, res['error'])) - ret['changes'] = {} + region=region, + keyid=keyid, + key=key, + profile=profile, + ) + if "error" in res: + ret["result"] = False + ret["comment"].append( + "Error tagging Elasticsearch domain " + '"{}": {}'.format(name, res["error"]) + ) + ret["changes"] = {} else: - ret['result'] = True - ret['comment'].append('Tags on Elasticsearch domain "{}" have been ' - '{}ed.'.format(name, 'replac' if replace else 'add')) + ret["result"] = True + ret["comment"].append( + 'Tags on Elasticsearch domain "{}" have been ' + "{}ed.".format(name, "replac" if replace else "add") + ) ret = _check_return_value(ret) return ret diff --git a/salt/states/boto3_route53.py b/salt/states/boto3_route53.py index 661b3503854..0b986b5b497 100644 --- a/salt/states/boto3_route53.py +++ b/salt/states/boto3_route53.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Route53 records with Boto 3 .. versionadded:: 2017.7.0 @@ -59,35 +59,48 @@ passed in as a dict, or as a string to pull from pillars or minion config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # keep lint from choking -#pylint: disable=W0106 -#pylint: disable=E1320 +# pylint: disable=W0106 +# pylint: disable=E1320 # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import uuid -# Import Salt Libs -from salt.exceptions import SaltInvocationError import salt.utils.data import salt.utils.dictupdate +# Import Salt Libs +from salt.exceptions import SaltInvocationError + log = logging.getLogger(__name__) # pylint: disable=W1699 def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto3_route53' if 'boto3_route53.find_hosted_zone' in __salt__ else False + """ + if "boto3_route53.find_hosted_zone" in __salt__: + return "boto3_route53" + return (False, "boto3_route53 module could not be loaded") -def hosted_zone_present(name, Name=None, PrivateZone=False, - CallerReference=None, Comment=None, VPCs=None, - region=None, key=None, keyid=None, profile=None): - ''' +def hosted_zone_present( + name, + Name=None, + PrivateZone=False, + CallerReference=None, + Comment=None, + VPCs=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure a hosted zone exists with the given attributes. name @@ -131,61 +144,86 @@ def hosted_zone_present(name, Name=None, PrivateZone=False, not provided, an effort will be made to determine it from VPCId or VPCName, if possible. This will fail if a given VPCName exists in multiple regions visible to the bound account, in which case you'll need to provide an explicit value for VPCRegion. - ''' + """ Name = Name if Name else name - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not PrivateZone and VPCs: - raise SaltInvocationError("Parameter 'VPCs' is invalid when creating a public zone.") + raise SaltInvocationError( + "Parameter 'VPCs' is invalid when creating a public zone." + ) if PrivateZone and not VPCs: - raise SaltInvocationError("Parameter 'VPCs' is required when creating a private zone.") + raise SaltInvocationError( + "Parameter 'VPCs' is required when creating a private zone." + ) if VPCs: if not isinstance(VPCs, list): raise SaltInvocationError("Parameter 'VPCs' must be a list of dicts.") for v in VPCs: - if not isinstance(v, dict) or not salt.utils.data.exactly_one((v.get('VPCId'), v.get('VPCName'))): - raise SaltInvocationError("Parameter 'VPCs' must be a list of dicts, each composed " - "of either a 'VPCId' or a 'VPCName', and optionally a " - "'VPCRegion', to help distinguish between multitple matches.") + if not isinstance(v, dict) or not salt.utils.data.exactly_one( + (v.get("VPCId"), v.get("VPCName")) + ): + raise SaltInvocationError( + "Parameter 'VPCs' must be a list of dicts, each composed " + "of either a 'VPCId' or a 'VPCName', and optionally a " + "'VPCRegion', to help distinguish between multitple matches." + ) # Massage VPCs into something AWS will accept... fixed_vpcs = [] if PrivateZone: for v in VPCs: - VPCId = v.get('VPCId') - VPCName = v.get('VPCName') - VPCRegion = v.get('VPCRegion') - VPCs = __salt__['boto_vpc.describe_vpcs'](vpc_id=VPCId, name=VPCName, region=region, - key=key, keyid=keyid, profile=profile).get('vpcs', []) + VPCId = v.get("VPCId") + VPCName = v.get("VPCName") + VPCRegion = v.get("VPCRegion") + VPCs = __salt__["boto_vpc.describe_vpcs"]( + vpc_id=VPCId, + name=VPCName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("vpcs", []) if VPCRegion and VPCs: - VPCs = [v for v in VPCs if v['region'] == VPCRegion] + VPCs = [v for v in VPCs if v["region"] == VPCRegion] if not VPCs: - ret['comment'] = ('A VPC matching given criteria (vpc: {0} / vpc_region: {1}) not ' - 'found.'.format(VPCName or VPCId, VPCRegion)) - log.error(ret['comment']) - ret['result'] = False + ret["comment"] = ( + "A VPC matching given criteria (vpc: {0} / vpc_region: {1}) not " + "found.".format(VPCName or VPCId, VPCRegion) + ) + log.error(ret["comment"]) + ret["result"] = False return ret if len(VPCs) > 1: - ret['comment'] = ('Multiple VPCs matching given criteria (vpc: {0} / vpc_region: ' - '{1}) found: {2}.'.format(VPCName or VPCId, VPCRegion, - ', '.join([v['id'] for v in VPCs]))) - log.error(ret['comment']) - ret['result'] = False + ret["comment"] = ( + "Multiple VPCs matching given criteria (vpc: {0} / vpc_region: " + "{1}) found: {2}.".format( + VPCName or VPCId, VPCRegion, ", ".join([v["id"] for v in VPCs]) + ) + ) + log.error(ret["comment"]) + ret["result"] = False return ret vpc = VPCs[0] if VPCName: - VPCId = vpc['id'] + VPCId = vpc["id"] if not VPCRegion: - VPCRegion = vpc['region'] - fixed_vpcs += [{'VPCId': VPCId, 'VPCRegion': VPCRegion}] + VPCRegion = vpc["region"] + fixed_vpcs += [{"VPCId": VPCId, "VPCRegion": VPCRegion}] create = False update_comment = False add_vpcs = [] del_vpcs = [] - args = {'Name': Name, 'PrivateZone': PrivateZone, - 'region': region, 'key': key, 'keyid': keyid, 'profile': profile} - zone = __salt__['boto3_route53.find_hosted_zone'](**args) + args = { + "Name": Name, + "PrivateZone": PrivateZone, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } + zone = __salt__["boto3_route53.find_hosted_zone"](**args) if not zone: create = True # Grrrr - can only pass one VPC when initially creating a private zone... @@ -193,114 +231,164 @@ def hosted_zone_present(name, Name=None, PrivateZone=False, if len(fixed_vpcs) > 1: add_vpcs = fixed_vpcs[1:] fixed_vpcs = fixed_vpcs[:1] - CallerReference = CallerReference if CallerReference else str(uuid.uuid4()) # future lint: disable=blacklisted-function + CallerReference = ( + CallerReference if CallerReference else str(uuid.uuid4()) + ) # future lint: disable=blacklisted-function else: # Currently the only modifiable traits about a zone are associated VPCs and the comment. zone = zone[0] if PrivateZone: - for z in zone.get('VPCs'): + for z in zone.get("VPCs"): if z not in fixed_vpcs: del_vpcs += [z] for z in fixed_vpcs: - if z not in zone.get('VPCs'): + if z not in zone.get("VPCs"): add_vpcs += [z] - if zone['HostedZone']['Config'].get('Comment') != Comment: + if zone["HostedZone"]["Config"].get("Comment") != Comment: update_comment = True if not (create or add_vpcs or del_vpcs or update_comment): - ret['comment'] = 'Hostd Zone {0} already in desired state'.format(Name) + ret["comment"] = "Hostd Zone {0} already in desired state".format(Name) return ret if create: - if __opts__['test']: - ret['comment'] = 'Route 53 {} hosted zone {} would be created.'.format('private' if - PrivateZone else 'public', Name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route 53 {} hosted zone {} would be created.".format( + "private" if PrivateZone else "public", Name + ) + ret["result"] = None return ret - vpc_id = fixed_vpcs[0].get('VPCId') if fixed_vpcs else None - vpc_region = fixed_vpcs[0].get('VPCRegion') if fixed_vpcs else None - newzone = __salt__['boto3_route53.create_hosted_zone'](Name=Name, - CallerReference=CallerReference, Comment=Comment, - PrivateZone=PrivateZone, VPCId=vpc_id, VPCRegion=vpc_region, - region=region, key=key, keyid=keyid, profile=profile) + vpc_id = fixed_vpcs[0].get("VPCId") if fixed_vpcs else None + vpc_region = fixed_vpcs[0].get("VPCRegion") if fixed_vpcs else None + newzone = __salt__["boto3_route53.create_hosted_zone"]( + Name=Name, + CallerReference=CallerReference, + Comment=Comment, + PrivateZone=PrivateZone, + VPCId=vpc_id, + VPCRegion=vpc_region, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if newzone: newzone = newzone[0] - ret['comment'] = 'Route 53 {} hosted zone {} successfully created'.format('private' if - PrivateZone else 'public', Name) - log.info(ret['comment']) - ret['changes']['new'] = newzone + ret["comment"] = "Route 53 {} hosted zone {} successfully created".format( + "private" if PrivateZone else "public", Name + ) + log.info(ret["comment"]) + ret["changes"]["new"] = newzone else: - ret['comment'] = 'Creation of Route 53 {} hosted zone {} failed'.format('private' if - PrivateZone else 'public', Name) - log.error(ret['comment']) - ret['result'] = False + ret["comment"] = "Creation of Route 53 {} hosted zone {} failed".format( + "private" if PrivateZone else "public", Name + ) + log.error(ret["comment"]) + ret["result"] = False return ret if update_comment: - if __opts__['test']: - ret['comment'] = 'Route 53 {} hosted zone {} comment would be updated.'.format('private' - if PrivateZone else 'public', Name) - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "Route 53 {} hosted zone {} comment would be updated.".format( + "private" if PrivateZone else "public", Name + ) + ret["result"] = None return ret - r = __salt__['boto3_route53.update_hosted_zone_comment'](Name=Name, - Comment=Comment, PrivateZone=PrivateZone, region=region, key=key, keyid=keyid, - profile=profile) + r = __salt__["boto3_route53.update_hosted_zone_comment"]( + Name=Name, + Comment=Comment, + PrivateZone=PrivateZone, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if r: r = r[0] - msg = 'Route 53 {} hosted zone {} comment successfully updated'.format('private' if - PrivateZone else 'public', Name) + msg = "Route 53 {} hosted zone {} comment successfully updated".format( + "private" if PrivateZone else "public", Name + ) log.info(msg) - ret['comment'] = ' '.join([ret['comment'], msg]) - ret['changes']['old'] = zone - ret['changes']['new'] = salt.utils.dictupdate.update(ret['changes'].get('new', {}), r) + ret["comment"] = " ".join([ret["comment"], msg]) + ret["changes"]["old"] = zone + ret["changes"]["new"] = salt.utils.dictupdate.update( + ret["changes"].get("new", {}), r + ) else: - ret['comment'] = 'Update of Route 53 {} hosted zone {} comment failed'.format('private' - if PrivateZone else 'public', Name) - log.error(ret['comment']) - ret['result'] = False + ret[ + "comment" + ] = "Update of Route 53 {} hosted zone {} comment failed".format( + "private" if PrivateZone else "public", Name + ) + log.error(ret["comment"]) + ret["result"] = False return ret if add_vpcs or del_vpcs: - if __opts__['test']: - ret['comment'] = 'Route 53 {} hosted zone {} associated VPCs would be updated.'.format( - 'private' if PrivateZone else 'public', Name) - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "Route 53 {} hosted zone {} associated VPCs would be updated.".format( + "private" if PrivateZone else "public", Name + ) + ret["result"] = None return ret all_added = True all_deled = True - for vpc in add_vpcs: # Add any new first to avoid the "can't delete last VPC" errors. - r = __salt__['boto3_route53.associate_vpc_with_hosted_zone'](Name=Name, - VPCId=vpc['VPCId'], VPCRegion=vpc['VPCRegion'], region=region, key=key, - keyid=keyid, profile=profile) + for ( + vpc + ) in add_vpcs: # Add any new first to avoid the "can't delete last VPC" errors. + r = __salt__["boto3_route53.associate_vpc_with_hosted_zone"]( + Name=Name, + VPCId=vpc["VPCId"], + VPCRegion=vpc["VPCRegion"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not r: all_added = False for vpc in del_vpcs: - r = __salt__['boto3_route53.disassociate_vpc_from_hosted_zone'](Name=Name, - VPCId=vpc['VPCId'], VPCRegion=vpc['VPCRegion'], region=region, key=key, - keyid=keyid, profile=profile) + r = __salt__["boto3_route53.disassociate_vpc_from_hosted_zone"]( + Name=Name, + VPCId=vpc["VPCId"], + VPCRegion=vpc["VPCRegion"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not r: all_deled = False - ret['changes']['old'] = zone - ret['changes']['new'] = __salt__['boto3_route53.find_hosted_zone'](**args) + ret["changes"]["old"] = zone + ret["changes"]["new"] = __salt__["boto3_route53.find_hosted_zone"](**args) if all_added and all_deled: - msg = 'Route 53 {} hosted zone {} associated VPCs successfully updated'.format('private' - if PrivateZone else 'public', Name) + msg = "Route 53 {} hosted zone {} associated VPCs successfully updated".format( + "private" if PrivateZone else "public", Name + ) log.info(msg) - ret['comment'] = ' '.join([ret['comment'], msg]) + ret["comment"] = " ".join([ret["comment"], msg]) else: - ret['comment'] = 'Update of Route 53 {} hosted zone {} associated VPCs failed'.format( - 'private' if PrivateZone else 'public', Name) - log.error(ret['comment']) - ret['result'] = False + ret[ + "comment" + ] = "Update of Route 53 {} hosted zone {} associated VPCs failed".format( + "private" if PrivateZone else "public", Name + ) + log.error(ret["comment"]) + ret["result"] = False return ret return ret -def hosted_zone_absent(name, Name=None, PrivateZone=False, - region=None, key=None, keyid=None, profile=None): - ''' +def hosted_zone_absent( + name, Name=None, PrivateZone=False, region=None, key=None, keyid=None, profile=None +): + """ Ensure the Route53 Hostes Zone described is absent name @@ -313,49 +401,77 @@ def hosted_zone_absent(name, Name=None, PrivateZone=False, PrivateZone Set True if deleting a private hosted zone. - ''' + """ Name = Name if Name else name - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - args = {'Name': Name, 'PrivateZone': PrivateZone, 'region': region, 'key': key, - 'keyid': keyid, 'profile': profile} - zone = __salt__['boto3_route53.find_hosted_zone'](**args) + args = { + "Name": Name, + "PrivateZone": PrivateZone, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } + zone = __salt__["boto3_route53.find_hosted_zone"](**args) if not zone: - ret['comment'] = 'Route 53 {} hosted zone {} already absent'.format('private' if - PrivateZone else 'public', Name) - log.info(ret['comment']) + ret["comment"] = "Route 53 {} hosted zone {} already absent".format( + "private" if PrivateZone else "public", Name + ) + log.info(ret["comment"]) return ret - if __opts__['test']: - ret['comment'] = 'Route 53 {} hosted zone {} would be deleted'.format('private' if - PrivateZone else 'public', Name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route 53 {} hosted zone {} would be deleted".format( + "private" if PrivateZone else "public", Name + ) + ret["result"] = None return ret zone = zone[0] - Id = zone['HostedZone']['Id'] - if __salt__['boto3_route53.delete_hosted_zone'](Id=Id, region=region, key=key, - keyid=keyid, profile=profile): - ret['comment'] = 'Route 53 {} hosted zone {} deleted'.format('private' if PrivateZone else - 'public', Name) - log.info(ret['comment']) - ret['changes']['old'] = zone - ret['changes']['new'] = None + Id = zone["HostedZone"]["Id"] + if __salt__["boto3_route53.delete_hosted_zone"]( + Id=Id, region=region, key=key, keyid=keyid, profile=profile + ): + ret["comment"] = "Route 53 {} hosted zone {} deleted".format( + "private" if PrivateZone else "public", Name + ) + log.info(ret["comment"]) + ret["changes"]["old"] = zone + ret["changes"]["new"] = None else: - ret['comment'] = 'Failed to delete Route 53 {} hosted zone {}'.format('private' if - PrivateZone else 'public', Name) - log.info(ret['comment']) - ret['result'] = False + ret["comment"] = "Failed to delete Route 53 {} hosted zone {}".format( + "private" if PrivateZone else "public", Name + ) + log.info(ret["comment"]) + ret["result"] = False return ret return ret -def rr_present(name, HostedZoneId=None, DomainName=None, PrivateZone=False, Name=None, Type=None, - SetIdentifier=None, Weight=None, Region=None, GeoLocation=None, Failover=None, - TTL=None, ResourceRecords=None, AliasTarget=None, HealthCheckId=None, - TrafficPolicyInstanceId=None, - region=None, key=None, keyid=None, profile=None): - ''' +def rr_present( + name, + HostedZoneId=None, + DomainName=None, + PrivateZone=False, + Name=None, + Type=None, + SetIdentifier=None, + Weight=None, + Region=None, + GeoLocation=None, + Failover=None, + TTL=None, + ResourceRecords=None, + AliasTarget=None, + HealthCheckId=None, + TrafficPolicyInstanceId=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the Route53 record is present. name @@ -527,106 +643,163 @@ def rr_present(name, HostedZoneId=None, DomainName=None, PrivateZone=False, Name profile Dict, or pillar key pointing to a dict, containing AWS region/key/keyid. - ''' + """ Name = Name if Name else name if Type is None: - raise SaltInvocationError("'Type' is a required parameter when adding or updating" - "resource records.") - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + raise SaltInvocationError( + "'Type' is a required parameter when adding or updating" "resource records." + ) + ret = {"name": name, "result": True, "comment": "", "changes": {}} - args = {'Id': HostedZoneId, 'Name': DomainName, 'PrivateZone': PrivateZone, - 'region': region, 'key': key, 'keyid': keyid, 'profile': profile} - zone = __salt__['boto3_route53.find_hosted_zone'](**args) + args = { + "Id": HostedZoneId, + "Name": DomainName, + "PrivateZone": PrivateZone, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } + zone = __salt__["boto3_route53.find_hosted_zone"](**args) if not zone: - ret['comment'] = 'Route 53 {} hosted zone {} not found'.format('private' if PrivateZone - else 'public', DomainName) - log.info(ret['comment']) + ret["comment"] = "Route 53 {} hosted zone {} not found".format( + "private" if PrivateZone else "public", DomainName + ) + log.info(ret["comment"]) return ret zone = zone[0] - HostedZoneId = zone['HostedZone']['Id'] + HostedZoneId = zone["HostedZone"]["Id"] # Convert any magic RR values to something AWS will understand, and otherwise clean them up. fixed_rrs = [] if ResourceRecords: for rr in ResourceRecords: - if rr.startswith('magic:'): - fields = rr.split(':') - if fields[1] == 'ec2_instance_tag': + if rr.startswith("magic:"): + fields = rr.split(":") + if fields[1] == "ec2_instance_tag": if len(fields) != 5: - log.warning("Invalid magic RR value seen: '%s'. Passing as-is.", rr) + log.warning( + "Invalid magic RR value seen: '%s'. Passing as-is.", rr + ) fixed_rrs += [rr] continue tag_name = fields[2] tag_value = fields[3] instance_attr = fields[4] - good_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped') - r = __salt__['boto_ec2.find_instances']( - tags={tag_name: tag_value}, return_objs=True, in_states=good_states, - region=region, key=key, keyid=keyid, profile=profile) + good_states = ( + "pending", + "rebooting", + "running", + "stopping", + "stopped", + ) + r = __salt__["boto_ec2.find_instances"]( + tags={tag_name: tag_value}, + return_objs=True, + in_states=good_states, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if len(r) < 1: - ret['comment'] = 'No EC2 instance with tag {} == {} found'.format(tag_name, - tag_value) - log.error(ret['comment']) - ret['result'] = False + ret[ + "comment" + ] = "No EC2 instance with tag {} == {} found".format( + tag_name, tag_value + ) + log.error(ret["comment"]) + ret["result"] = False return ret if len(r) > 1: - ret['comment'] = 'Multiple EC2 instances with tag {} == {} found'.format( - tag_name, tag_value) - log.error(ret['comment']) - ret['result'] = False + ret[ + "comment" + ] = "Multiple EC2 instances with tag {} == {} found".format( + tag_name, tag_value + ) + log.error(ret["comment"]) + ret["result"] = False return ret instance = r[0] res = getattr(instance, instance_attr, None) if res: - log.debug('Found %s %s for instance %s', instance_attr, res, instance.id) - fixed_rrs += [__salt__['boto3_route53.aws_encode'](res)] + log.debug( + "Found %s %s for instance %s", + instance_attr, + res, + instance.id, + ) + fixed_rrs += [__salt__["boto3_route53.aws_encode"](res)] else: - ret['comment'] = 'Attribute {} not found on instance {}'.format(instance_attr, - instance.id) - log.error(ret['comment']) - ret['result'] = False + ret["comment"] = "Attribute {} not found on instance {}".format( + instance_attr, instance.id + ) + log.error(ret["comment"]) + ret["result"] = False return ret else: - ret['comment'] = ('Unknown RR magic value seen: {}. Please extend the ' - 'boto3_route53 state module to add support for your preferred ' - 'incantation.'.format(fields[1])) - log.error(ret['comment']) - ret['result'] = False + ret["comment"] = ( + "Unknown RR magic value seen: {}. Please extend the " + "boto3_route53 state module to add support for your preferred " + "incantation.".format(fields[1]) + ) + log.error(ret["comment"]) + ret["result"] = False return ret else: # for TXT records the entry must be encapsulated in quotes as required by the API # this appears to be incredibly difficult with the jinja templating engine # so inject the quotations here to make a viable ChangeBatch - if Type == 'TXT': + if Type == "TXT": rr = '"{}"'.format(rr) fixed_rrs += [rr] - ResourceRecords = [{'Value': rr} for rr in sorted(fixed_rrs)] + ResourceRecords = [{"Value": rr} for rr in sorted(fixed_rrs)] - recordsets = __salt__['boto3_route53.get_resource_records'](HostedZoneId=HostedZoneId, - StartRecordName=Name, StartRecordType=Type, region=region, key=key, keyid=keyid, - profile=profile) + recordsets = __salt__["boto3_route53.get_resource_records"]( + HostedZoneId=HostedZoneId, + StartRecordName=Name, + StartRecordType=Type, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if SetIdentifier and recordsets: - log.debug('Filter recordsets %s by SetIdentifier %s.', recordsets, SetIdentifier) - recordsets = [r for r in recordsets if r.get('SetIdentifier') == SetIdentifier] - log.debug('Resulted in recordsets %s.', recordsets) + log.debug( + "Filter recordsets %s by SetIdentifier %s.", recordsets, SetIdentifier + ) + recordsets = [r for r in recordsets if r.get("SetIdentifier") == SetIdentifier] + log.debug("Resulted in recordsets %s.", recordsets) create = False update = False - updatable = ['SetIdentifier', 'Weight', 'Region', 'GeoLocation', 'Failover', 'TTL', - 'AliasTarget', 'HealthCheckId', 'TrafficPolicyInstanceId'] + updatable = [ + "SetIdentifier", + "Weight", + "Region", + "GeoLocation", + "Failover", + "TTL", + "AliasTarget", + "HealthCheckId", + "TrafficPolicyInstanceId", + ] if not recordsets: create = True - if __opts__['test']: - ret['comment'] = 'Route 53 resource record {} with type {} would be added.'.format( - Name, Type) - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "Route 53 resource record {} with type {} would be added.".format( + Name, Type + ) + ret["result"] = None return ret elif len(recordsets) > 1: - ret['comment'] = 'Given criteria matched more than one ResourceRecordSet.' - log.error(ret['comment']) - ret['result'] = False + ret["comment"] = "Given criteria matched more than one ResourceRecordSet." + log.error(ret["comment"]) + ret["result"] = False return ret else: rrset = recordsets[0] @@ -634,68 +807,90 @@ def rr_present(name, HostedZoneId=None, DomainName=None, PrivateZone=False, Name if locals().get(u) != rrset.get(u): update = True break - if rrset.get('ResourceRecords') is not None: - if ResourceRecords != sorted(rrset.get('ResourceRecords'), key=lambda x: x['Value']): + if rrset.get("ResourceRecords") is not None: + if ResourceRecords != sorted( + rrset.get("ResourceRecords"), key=lambda x: x["Value"] + ): update = True - elif (AliasTarget is not None) and (rrset.get('AliasTarget') is not None): - if sorted(AliasTarget) != sorted(rrset.get('AliasTarget')): + elif (AliasTarget is not None) and (rrset.get("AliasTarget") is not None): + if sorted(AliasTarget) != sorted(rrset.get("AliasTarget")): update = True if not create and not update: - ret['comment'] = ('Route 53 resource record {} with type {} is already in the desired state.' - ''.format(Name, Type)) - log.info(ret['comment']) + ret["comment"] = ( + "Route 53 resource record {} with type {} is already in the desired state." + "".format(Name, Type) + ) + log.info(ret["comment"]) return ret else: - if __opts__['test']: - ret['comment'] = 'Route 53 resource record {} with type {} would be updated.'.format( - Name, Type) - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "Route 53 resource record {} with type {} would be updated.".format( + Name, Type + ) + ret["result"] = None return ret - ResourceRecordSet = { - 'Name': Name, - 'Type': Type - } + ResourceRecordSet = {"Name": Name, "Type": Type} if ResourceRecords: - ResourceRecordSet['ResourceRecords'] = ResourceRecords + ResourceRecordSet["ResourceRecords"] = ResourceRecords for u in updatable: if locals().get(u) or (locals().get(u) == 0): ResourceRecordSet.update({u: locals().get(u)}) else: - log.debug('Not updating ResourceRecordSet with local value: %s', locals().get(u)) + log.debug( + "Not updating ResourceRecordSet with local value: %s", + locals().get(u), + ) ChangeBatch = { - 'Changes': [ - { - 'Action': 'UPSERT', - 'ResourceRecordSet': ResourceRecordSet, - } - ] + "Changes": [{"Action": "UPSERT", "ResourceRecordSet": ResourceRecordSet}] } - if __salt__['boto3_route53.change_resource_record_sets'](HostedZoneId=HostedZoneId, - ChangeBatch=ChangeBatch, region=region, key=key, keyid=keyid, profile=profile): - ret['comment'] = 'Route 53 resource record {} with type {} {}.'.format(Name, - Type, 'created' if create else 'updated') - log.info(ret['comment']) + if __salt__["boto3_route53.change_resource_record_sets"]( + HostedZoneId=HostedZoneId, + ChangeBatch=ChangeBatch, + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + ret["comment"] = "Route 53 resource record {} with type {} {}.".format( + Name, Type, "created" if create else "updated" + ) + log.info(ret["comment"]) if create: - ret['changes']['old'] = None + ret["changes"]["old"] = None else: - ret['changes']['old'] = rrset - ret['changes']['new'] = ResourceRecordSet + ret["changes"]["old"] = rrset + ret["changes"]["new"] = ResourceRecordSet else: - ret['comment'] = 'Failed to {} Route 53 resource record {} with type {}.'.format( - 'create' if create else 'update', Name, Type) - log.error(ret['comment']) - ret['result'] = False + ret[ + "comment" + ] = "Failed to {} Route 53 resource record {} with type {}.".format( + "create" if create else "update", Name, Type + ) + log.error(ret["comment"]) + ret["result"] = False return ret -def rr_absent(name, HostedZoneId=None, DomainName=None, PrivateZone=False, - Name=None, Type=None, SetIdentifier=None, - region=None, key=None, keyid=None, profile=None): - ''' +def rr_absent( + name, + HostedZoneId=None, + DomainName=None, + PrivateZone=False, + Name=None, + Type=None, + SetIdentifier=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the Route53 record is deleted. name @@ -735,66 +930,96 @@ def rr_absent(name, HostedZoneId=None, DomainName=None, PrivateZone=False, profile Dict, or pillar key pointing to a dict, containing AWS region/key/keyid. - ''' + """ Name = Name if Name else name if Type is None: - raise SaltInvocationError("'Type' is a required parameter when deleting resource records.") - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + raise SaltInvocationError( + "'Type' is a required parameter when deleting resource records." + ) + ret = {"name": name, "result": True, "comment": "", "changes": {}} - args = {'Id': HostedZoneId, 'Name': DomainName, 'PrivateZone': PrivateZone, - 'region': region, 'key': key, 'keyid': keyid, 'profile': profile} - zone = __salt__['boto3_route53.find_hosted_zone'](**args) + args = { + "Id": HostedZoneId, + "Name": DomainName, + "PrivateZone": PrivateZone, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } + zone = __salt__["boto3_route53.find_hosted_zone"](**args) if not zone: - ret['comment'] = 'Route 53 {} hosted zone {} not found'.format('private' if PrivateZone - else 'public', DomainName) - log.info(ret['comment']) + ret["comment"] = "Route 53 {} hosted zone {} not found".format( + "private" if PrivateZone else "public", DomainName + ) + log.info(ret["comment"]) return ret zone = zone[0] - HostedZoneId = zone['HostedZone']['Id'] + HostedZoneId = zone["HostedZone"]["Id"] - recordsets = __salt__['boto3_route53.get_resource_records'](HostedZoneId=HostedZoneId, - StartRecordName=Name, StartRecordType=Type, region=region, key=key, keyid=keyid, - profile=profile) + recordsets = __salt__["boto3_route53.get_resource_records"]( + HostedZoneId=HostedZoneId, + StartRecordName=Name, + StartRecordType=Type, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if SetIdentifier and recordsets: - log.debug('Filter recordsets %s by SetIdentifier %s.', recordsets, SetIdentifier) - recordsets = [r for r in recordsets if r.get('SetIdentifier') == SetIdentifier] - log.debug('Resulted in recordsets %s.', recordsets) + log.debug( + "Filter recordsets %s by SetIdentifier %s.", recordsets, SetIdentifier + ) + recordsets = [r for r in recordsets if r.get("SetIdentifier") == SetIdentifier] + log.debug("Resulted in recordsets %s.", recordsets) if not recordsets: - ret['comment'] = 'Route 53 resource record {} with type {} already absent.'.format( - Name, Type) + ret[ + "comment" + ] = "Route 53 resource record {} with type {} already absent.".format( + Name, Type + ) return ret elif len(recordsets) > 1: - ret['comment'] = 'Given criteria matched more than one ResourceRecordSet.' - log.error(ret['comment']) - ret['result'] = False + ret["comment"] = "Given criteria matched more than one ResourceRecordSet." + log.error(ret["comment"]) + ret["result"] = False return ret ResourceRecordSet = recordsets[0] - if __opts__['test']: - ret['comment'] = 'Route 53 resource record {} with type {} would be deleted.'.format( - Name, Type) - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "Route 53 resource record {} with type {} would be deleted.".format( + Name, Type + ) + ret["result"] = None return ret ChangeBatch = { - 'Changes': [ - { - 'Action': 'DELETE', - 'ResourceRecordSet': ResourceRecordSet, - } - ] + "Changes": [{"Action": "DELETE", "ResourceRecordSet": ResourceRecordSet}] } - if __salt__['boto3_route53.change_resource_record_sets'](HostedZoneId=HostedZoneId, - ChangeBatch=ChangeBatch, region=region, key=key, keyid=keyid, profile=profile): - ret['comment'] = 'Route 53 resource record {} with type {} deleted.'.format(Name, Type) - log.info(ret['comment']) - ret['changes']['old'] = ResourceRecordSet - ret['changes']['new'] = None + if __salt__["boto3_route53.change_resource_record_sets"]( + HostedZoneId=HostedZoneId, + ChangeBatch=ChangeBatch, + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + ret["comment"] = "Route 53 resource record {} with type {} deleted.".format( + Name, Type + ) + log.info(ret["comment"]) + ret["changes"]["old"] = ResourceRecordSet + ret["changes"]["new"] = None else: - ret['comment'] = 'Failed to delete Route 53 resource record {} with type {}.'.format(Name, - Type) - log.error(ret['comment']) - ret['result'] = False + ret[ + "comment" + ] = "Failed to delete Route 53 resource record {} with type {}.".format( + Name, Type + ) + log.error(ret["comment"]) + ret["result"] = False return ret diff --git a/salt/states/boto3_sns.py b/salt/states/boto3_sns.py index b70785943d8..375d666d6ca 100644 --- a/salt/states/boto3_sns.py +++ b/salt/states/boto3_sns.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Manage SNS Topics - +================= Create and destroy SNS topics. Be aware that this interacts with Amazon's services, and so may incur charges. @@ -40,25 +40,33 @@ passed in as a dict, or as a string to pull from pillars or minion config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs - # Using a profile from pillars +Using a profile from pillars +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: yaml + mytopic: boto3_sns.topic_present: - region: us-east-1 - profile: mysnsprofile - # Passing in a profile +Passing in a profile +^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: yaml + mytopic: boto3_sns.topic_present: - region: us-east-1 - profile: keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import re -import logging import copy +import logging +import re import salt.utils.json from salt.ext import six @@ -67,15 +75,24 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto3_sns' if 'boto3_sns.topic_exists' in __salt__ else False + """ + if "boto3_sns.topic_exists" in __salt__: + return "boto3_sns" + return (False, "boto3_sns module could not be loaded") -def topic_present(name, subscriptions=None, attributes=None, - region=None, key=None, keyid=None, profile=None): - ''' +def topic_present( + name, + subscriptions=None, + attributes=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the SNS topic exists. name @@ -97,6 +114,7 @@ def topic_present(name, subscriptions=None, attributes=None, attributes Dictionary of attributes to set on the SNS topic Valid attribute keys are: + - Policy: the JSON serialization of the topic's access control policy - DisplayName: the human-readable name used in the "From" field for notifications to email and email-json endpoints @@ -114,37 +132,41 @@ def topic_present(name, subscriptions=None, attributes=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} something_changed = False - current = __salt__['boto3_sns.describe_topic'](name, region, key, keyid, profile) + current = __salt__["boto3_sns.describe_topic"](name, region, key, keyid, profile) if current: - ret['comment'] = 'AWS SNS topic {0} present.'.format(name) - TopicArn = current['TopicArn'] + ret["comment"] = "AWS SNS topic {0} present.".format(name) + TopicArn = current["TopicArn"] else: - if __opts__['test']: - ret['comment'] = 'AWS SNS topic {0} would be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "AWS SNS topic {0} would be created.".format(name) + ret["result"] = None return ret else: - TopicArn = __salt__['boto3_sns.create_topic'](name, region=region, key=key, - keyid=keyid, profile=profile) + TopicArn = __salt__["boto3_sns.create_topic"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if TopicArn: - ret['comment'] = 'AWS SNS topic {0} created with ARN {1}.'.format(name, TopicArn) + ret["comment"] = "AWS SNS topic {0} created with ARN {1}.".format( + name, TopicArn + ) something_changed = True else: - ret['comment'] = 'Failed to create AWS SNS topic {0}'.format(name) - log.error(ret['comment']) - ret['result'] = False + ret["comment"] = "Failed to create AWS SNS topic {0}".format(name) + log.error(ret["comment"]) + ret["result"] = False return ret ### Update any explicitly defined attributes want_attrs = attributes if attributes else {} # Freshen these in case we just created it above - current_attrs = __salt__['boto3_sns.get_topic_attributes'](TopicArn, region=region, key=key, - keyid=keyid, profile=profile) - for attr in ['DisplayName', 'Policy', 'DeliveryPolicy']: + current_attrs = __salt__["boto3_sns.get_topic_attributes"]( + TopicArn, region=region, key=key, keyid=keyid, profile=profile + ) + for attr in ["DisplayName", "Policy", "DeliveryPolicy"]: curr_val = current_attrs.get(attr) want_val = want_attrs.get(attr) # Some get default values if not set, so it's not safe to enforce absense if they're @@ -154,88 +176,124 @@ def topic_present(name, subscriptions=None, attributes=None, continue if _json_objs_equal(want_val, curr_val): continue - if __opts__['test']: - ret['comment'] += ' Attribute {0} would be updated on topic {1}.'.format(attr, TopicArn) - ret['result'] = None + if __opts__["test"]: + ret["comment"] += " Attribute {0} would be updated on topic {1}.".format( + attr, TopicArn + ) + ret["result"] = None continue - want_val = want_val if isinstance(want_val, six.string_types) else salt.utils.json.dumps(want_val) - if __salt__['boto3_sns.set_topic_attributes'](TopicArn, attr, want_val, region=region, - key=key, keyid=keyid, profile=profile): - ret['comment'] += ' Attribute {0} set to {1} on topic {2}.'.format(attr, want_val, - TopicArn) + want_val = ( + want_val + if isinstance(want_val, six.string_types) + else salt.utils.json.dumps(want_val) + ) + if __salt__["boto3_sns.set_topic_attributes"]( + TopicArn, + attr, + want_val, + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + ret["comment"] += " Attribute {0} set to {1} on topic {2}.".format( + attr, want_val, TopicArn + ) something_changed = True else: - ret['comment'] += ' Failed to update {0} on topic {1}.'.format(attr, TopicArn) - ret['result'] = False + ret["comment"] += " Failed to update {0} on topic {1}.".format( + attr, TopicArn + ) + ret["result"] = False return ret ### Add / remove subscriptions want_subs = subscriptions if subscriptions else [] obfuscated_subs = [] - current_subs = current.get('Subscriptions', []) - current_slim = [{'Protocol': s['Protocol'], 'Endpoint': s['Endpoint']} for s in current_subs] + current_subs = current.get("Subscriptions", []) + current_slim = [ + {"Protocol": s["Protocol"], "Endpoint": s["Endpoint"]} for s in current_subs + ] subscribe = [] unsubscribe = [] for sub in want_subs: # If the subscription contains inline digest auth, AWS will obfuscate the password with # '****'. Thus we need to do the same with ours to permit 1-to-1 comparison. # Example: https://user:****@my.endpoiint.com/foo/bar - endpoint = sub['Endpoint'] - matches = re.search(r'https://(?P<user>\w+):(?P<pass>\w+)@', endpoint) + endpoint = sub["Endpoint"] + matches = re.search(r"https://(?P<user>\w+):(?P<pass>\w+)@", endpoint) if matches is not None: - sub['Endpoint'] = endpoint.replace(':' + matches.groupdict()['pass'], ':****') + sub["Endpoint"] = endpoint.replace( + ":" + matches.groupdict()["pass"], ":****" + ) obfuscated_subs += [copy.deepcopy(sub)] # Now set it back... if sub not in current_slim: - sub['Endpoint'] = endpoint + sub["Endpoint"] = endpoint subscribe += [sub] for sub in current_subs: - minimal = {'Protocol': sub['Protocol'], 'Endpoint': sub['Endpoint']} + minimal = {"Protocol": sub["Protocol"], "Endpoint": sub["Endpoint"]} if minimal not in obfuscated_subs: - unsubscribe += [sub['SubscriptionArn']] + unsubscribe += [sub["SubscriptionArn"]] for sub in subscribe: - prot = sub['Protocol'] - endp = sub['Endpoint'] - if __opts__['test']: - msg = ' Subscription {0}:{1} would be set on topic {2}.'.format(prot, endp, TopicArn) - ret['comment'] += msg - ret['result'] = None + prot = sub["Protocol"] + endp = sub["Endpoint"] + if __opts__["test"]: + msg = " Subscription {0}:{1} would be set on topic {2}.".format( + prot, endp, TopicArn + ) + ret["comment"] += msg + ret["result"] = None continue - subbed = __salt__['boto3_sns.subscribe'](TopicArn, prot, endp, region=region, key=key, - keyid=keyid, profile=profile) + subbed = __salt__["boto3_sns.subscribe"]( + TopicArn, prot, endp, region=region, key=key, keyid=keyid, profile=profile + ) if subbed: - msg = ' Subscription {0}:{1} set on topic {2}.'.format(prot, endp, TopicArn) - ret['comment'] += msg + msg = " Subscription {0}:{1} set on topic {2}.".format(prot, endp, TopicArn) + ret["comment"] += msg something_changed = True else: - msg = ' Failed to set subscription {0}:{1} on topic {2}.'.format(prot, endp, TopicArn) - ret['comment'] += msg - ret['result'] = False + msg = " Failed to set subscription {0}:{1} on topic {2}.".format( + prot, endp, TopicArn + ) + ret["comment"] += msg + ret["result"] = False return ret for sub in unsubscribe: - if __opts__['test']: - msg = ' Subscription {0} would be removed from topic {1}.'.format(sub, TopicArn) - ret['comment'] += msg - ret['result'] = None + if __opts__["test"]: + msg = " Subscription {0} would be removed from topic {1}.".format( + sub, TopicArn + ) + ret["comment"] += msg + ret["result"] = None continue - unsubbed = __salt__['boto3_sns.unsubscribe'](sub, region=region, key=key, keyid=keyid, - profile=profile) + unsubbed = __salt__["boto3_sns.unsubscribe"]( + sub, region=region, key=key, keyid=keyid, profile=profile + ) if unsubbed: - ret['comment'] += ' Subscription {0} removed from topic {1}.'.format(sub, TopicArn) + ret["comment"] += " Subscription {0} removed from topic {1}.".format( + sub, TopicArn + ) something_changed = True else: - msg = ' Failed to remove subscription {0} from topic {1}.'.format(sub, TopicArn) - ret['comment'] += msg - ret['result'] = False + msg = " Failed to remove subscription {0} from topic {1}.".format( + sub, TopicArn + ) + ret["comment"] += msg + ret["result"] = False return ret if something_changed: - ret['changes']['old'] = current - ret['changes']['new'] = __salt__['boto3_sns.describe_topic'](name, region, key, keyid, profile) + ret["changes"]["old"] = current + ret["changes"]["new"] = __salt__["boto3_sns.describe_topic"]( + name, region, key, keyid, profile + ) return ret -def topic_absent(name, unsubscribe=False, region=None, key=None, keyid=None, profile=None): - ''' +def topic_absent( + name, unsubscribe=False, region=None, key=None, keyid=None, profile=None +): + """ Ensure the named sns topic is deleted. name @@ -257,65 +315,82 @@ def topic_absent(name, unsubscribe=False, region=None, key=None, keyid=None, pro profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} something_changed = False - current = __salt__['boto3_sns.describe_topic'](name, region, key, keyid, profile) + current = __salt__["boto3_sns.describe_topic"](name, region, key, keyid, profile) if not current: - ret['comment'] = 'AWS SNS topic {0} absent.'.format(name) + ret["comment"] = "AWS SNS topic {0} absent.".format(name) else: - TopicArn = current['TopicArn'] - if __opts__['test']: - ret['comment'] = 'AWS SNS topic {0} would be removed.'.format(TopicArn) + TopicArn = current["TopicArn"] + if __opts__["test"]: + ret["comment"] = "AWS SNS topic {0} would be removed.".format(TopicArn) if unsubscribe: - ret['comment'] += ' {0} subscription(s) would be removed.'.format( - len(current['Subscriptions'])) - ret['result'] = None + ret["comment"] += " {0} subscription(s) would be removed.".format( + len(current["Subscriptions"]) + ) + ret["result"] = None return ret if unsubscribe: - for sub in current['Subscriptions']: - if sub['SubscriptionArn'] == 'PendingConfirmation': + for sub in current["Subscriptions"]: + if sub["SubscriptionArn"] == "PendingConfirmation": # The API won't let you delete subscriptions in pending status... log.warning( - 'Ignoring PendingConfirmation subscription %s %s on ' - 'topic %s', sub['Protocol'], sub['Endpoint'], sub['TopicArn'] + "Ignoring PendingConfirmation subscription %s %s on " + "topic %s", + sub["Protocol"], + sub["Endpoint"], + sub["TopicArn"], ) continue - if __salt__['boto3_sns.unsubscribe'](sub['SubscriptionArn'], region=region, key=key, - keyid=keyid, profile=profile): - log.debug('Deleted subscription %s for SNS topic %s', sub, TopicArn) + if __salt__["boto3_sns.unsubscribe"]( + sub["SubscriptionArn"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + log.debug("Deleted subscription %s for SNS topic %s", sub, TopicArn) something_changed = True else: - ret['comment'] = 'Failed to delete subscription {0} for SNS topic {1}'.format( - sub, TopicArn) - ret['result'] = False + ret[ + "comment" + ] = "Failed to delete subscription {0} for SNS topic {1}".format( + sub, TopicArn + ) + ret["result"] = False return ret - if not __salt__['boto3_sns.delete_topic'](TopicArn, region=region, key=key, keyid=keyid, - profile=profile): - ret['comment'] = 'Failed to delete SNS topic {0}'.format(TopicArn) - log.error(ret['comment']) - ret['result'] = False + if not __salt__["boto3_sns.delete_topic"]( + TopicArn, region=region, key=key, keyid=keyid, profile=profile + ): + ret["comment"] = "Failed to delete SNS topic {0}".format(TopicArn) + log.error(ret["comment"]) + ret["result"] = False else: - ret['comment'] = 'AWS SNS topic {0} deleted.'.format(TopicArn) + ret["comment"] = "AWS SNS topic {0} deleted.".format(TopicArn) if unsubscribe: - ret['comment'] += ' '.join(['Subscription {0} deleted'.format(s) - for s in current['Subscriptions']]) + ret["comment"] += " ".join( + [ + "Subscription {0} deleted".format(s) + for s in current["Subscriptions"] + ] + ) something_changed = True if something_changed: - ret['changes']['old'] = current - ret['changes']['new'] = __salt__['boto3_sns.describe_topic'](name, region, key, keyid, profile) + ret["changes"]["old"] = current + ret["changes"]["new"] = __salt__["boto3_sns.describe_topic"]( + name, region, key, keyid, profile + ) return ret def _json_objs_equal(left, right): - left = __utils__['boto3.ordered']( - salt.utils.json.loads(left) - if isinstance(left, six.string_types) - else left) - right = __utils__['boto3.ordered']( - salt.utils.json.loads(right) - if isinstance(right, six.string_types) - else right) + left = __utils__["boto3.ordered"]( + salt.utils.json.loads(left) if isinstance(left, six.string_types) else left + ) + right = __utils__["boto3.ordered"]( + salt.utils.json.loads(right) if isinstance(right, six.string_types) else right + ) return left == right diff --git a/salt/states/boto_apigateway.py b/salt/states/boto_apigateway.py index dcec5d03adc..15fec0b29b1 100644 --- a/salt/states/boto_apigateway.py +++ b/salt/states/boto_apigateway.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Apigateway Rest APIs =========================== @@ -49,10 +49,11 @@ config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import hashlib import logging import os @@ -70,18 +71,33 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_apigateway' if 'boto_apigateway.describe_apis' in __salt__ else False + """ + if "boto_apigateway.describe_apis" in __salt__: + return "boto_apigateway" + return (False, "boto_apigateway module could not be loaded") -def present(name, api_name, swagger_file, stage_name, api_key_required, - lambda_integration_role, lambda_region=None, stage_variables=None, - region=None, key=None, keyid=None, profile=None, - lambda_funcname_format='{stage}_{api}_{resource}_{method}', - authorization_type='NONE', error_response_template=None, response_template=None): - ''' +def present( + name, + api_name, + swagger_file, + stage_name, + api_key_required, + lambda_integration_role, + lambda_region=None, + stage_variables=None, + region=None, + key=None, + keyid=None, + profile=None, + lambda_funcname_format="{stage}_{api}_{resource}_{method}", + authorization_type="NONE", + error_response_template=None, + response_template=None, +): + """ Ensure the spcified api_name with the corresponding swaggerfile is deployed to the given stage_name in AWS ApiGateway. @@ -251,99 +267,106 @@ def present(name, api_name, swagger_file, stage_name, api_key_required, function as is. .. versionadded:: 2017.7.0 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} try: - common_args = dict([('region', region), - ('key', key), - ('keyid', keyid), - ('profile', profile)]) + common_args = dict( + [("region", region), ("key", key), ("keyid", keyid), ("profile", profile)] + ) # try to open the swagger file and basic validation - swagger = _Swagger(api_name, stage_name, - lambda_funcname_format, - swagger_file, - error_response_template, response_template, - common_args) + swagger = _Swagger( + api_name, + stage_name, + lambda_funcname_format, + swagger_file, + error_response_template, + response_template, + common_args, + ) # retrieve stage variables stage_vars = _get_stage_variables(stage_variables) # verify if api and stage already exists ret = swagger.verify_api(ret) - if ret.get('publish'): + if ret.get("publish"): # there is a deployment label with signature matching the given api_name, # swagger file name, swagger file md5 sum, and swagger file info object # just reassociate the stage_name to the given deployment label. - if __opts__['test']: - ret['comment'] = ('[stage: {0}] will be reassociated to an already available ' - 'deployment that matched the given [api_name: {1}] ' - 'and [swagger_file: {2}].\n' - 'Stage variables will be set ' - 'to {3}.'.format(stage_name, api_name, swagger_file, stage_vars)) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = ( + "[stage: {0}] will be reassociated to an already available " + "deployment that matched the given [api_name: {1}] " + "and [swagger_file: {2}].\n" + "Stage variables will be set " + "to {3}.".format(stage_name, api_name, swagger_file, stage_vars) + ) + ret["result"] = None return ret return swagger.publish_api(ret, stage_vars) - if ret.get('current'): + if ret.get("current"): # already at desired state for the stage, swagger_file, and api_name - if __opts__['test']: - ret['comment'] = ('[stage: {0}] is already at desired state with an associated ' - 'deployment matching the given [api_name: {1}] ' - 'and [swagger_file: {2}].\n' - 'Stage variables will be set ' - 'to {3}.'.format(stage_name, api_name, swagger_file, stage_vars)) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = ( + "[stage: {0}] is already at desired state with an associated " + "deployment matching the given [api_name: {1}] " + "and [swagger_file: {2}].\n" + "Stage variables will be set " + "to {3}.".format(stage_name, api_name, swagger_file, stage_vars) + ) + ret["result"] = None return swagger.overwrite_stage_variables(ret, stage_vars) # there doesn't exist any previous deployments for the given swagger_file, we need # to redeploy the content of the swagger file to the api, models, and resources object # and finally create a new deployment and tie the stage_name to this new deployment - if __opts__['test']: - ret['comment'] = ('There is no deployment matching the given [api_name: {0}] ' - 'and [swagger_file: {1}]. A new deployment will be ' - 'created and the [stage_name: {2}] will then be associated ' - 'to the newly created deployment.\n' - 'Stage variables will be set ' - 'to {3}.'.format(api_name, swagger_file, stage_name, stage_vars)) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = ( + "There is no deployment matching the given [api_name: {0}] " + "and [swagger_file: {1}]. A new deployment will be " + "created and the [stage_name: {2}] will then be associated " + "to the newly created deployment.\n" + "Stage variables will be set " + "to {3}.".format(api_name, swagger_file, stage_name, stage_vars) + ) + ret["result"] = None return ret ret = swagger.deploy_api(ret) - if ret.get('abort'): + if ret.get("abort"): return ret ret = swagger.deploy_models(ret) - if ret.get('abort'): + if ret.get("abort"): return ret - ret = swagger.deploy_resources(ret, - api_key_required=api_key_required, - lambda_integration_role=lambda_integration_role, - lambda_region=lambda_region, - authorization_type=authorization_type) - if ret.get('abort'): + ret = swagger.deploy_resources( + ret, + api_key_required=api_key_required, + lambda_integration_role=lambda_integration_role, + lambda_region=lambda_region, + authorization_type=authorization_type, + ) + if ret.get("abort"): return ret ret = swagger.publish_api(ret, stage_vars) except (ValueError, IOError) as e: - ret['result'] = False - ret['comment'] = '{0}'.format(e.args) + ret["result"] = False + ret["comment"] = "{0}".format(e.args) return ret def _get_stage_variables(stage_variables): - ''' + """ Helper function to retrieve stage variables from pillars/options, if the input is a string - ''' + """ ret = dict() if stage_variables is None: return ret @@ -351,7 +374,7 @@ def _get_stage_variables(stage_variables): if isinstance(stage_variables, six.string_types): if stage_variables in __opts__: ret = __opts__[stage_variables] - master_opts = __pillar__.get('master', {}) + master_opts = __pillar__.get("master", {}) if stage_variables in master_opts: ret = master_opts[stage_variables] if stage_variables in __pillar__: @@ -365,8 +388,17 @@ def _get_stage_variables(stage_variables): return ret -def absent(name, api_name, stage_name, nuke_api=False, region=None, key=None, keyid=None, profile=None): - ''' +def absent( + name, + api_name, + stage_name, + nuke_api=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the stage_name associated with the given api_name deployed by boto_apigateway's present state is removed. If the currently associated deployment to the given stage_name has no other stages associated with it, the deployment will also be removed. @@ -398,61 +430,58 @@ def absent(name, api_name, stage_name, nuke_api=False, region=None, key=None, ke profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} try: - common_args = dict([('region', region), - ('key', key), - ('keyid', keyid), - ('profile', profile)]) + common_args = dict( + [("region", region), ("key", key), ("keyid", keyid), ("profile", profile)] + ) - swagger = _Swagger(api_name, stage_name, '', None, None, None, common_args) + swagger = _Swagger(api_name, stage_name, "", None, None, None, common_args) if not swagger.restApiId: - ret['comment'] = '[Rest API: {0}] does not exist.'.format(api_name) + ret["comment"] = "[Rest API: {0}] does not exist.".format(api_name) return ret - if __opts__['test']: + if __opts__["test"]: if nuke_api: - ret['comment'] = ('[stage: {0}] will be deleted, if there are no other ' - 'active stages, the [api: {1} will also be ' - 'deleted.'.format(stage_name, api_name)) + ret["comment"] = ( + "[stage: {0}] will be deleted, if there are no other " + "active stages, the [api: {1} will also be " + "deleted.".format(stage_name, api_name) + ) else: - ret['comment'] = ('[stage: {0}] will be deleted.'.format(stage_name)) - ret['result'] = None + ret["comment"] = "[stage: {0}] will be deleted.".format(stage_name) + ret["result"] = None return ret ret = swagger.delete_stage(ret) - if ret.get('abort'): + if ret.get("abort"): return ret if nuke_api and swagger.no_more_deployments_remain(): ret = swagger.delete_api(ret) except (ValueError, IOError) as e: - ret['result'] = False - ret['comment'] = '{0}'.format(e.args) + ret["result"] = False + ret["comment"] = "{0}".format(e.args) return ret # Helper Swagger Class for swagger version 2.0 API specification def _gen_md5_filehash(fname, *args): - ''' + """ helper function to generate a md5 hash of the swagger definition file any extra argument passed to the function is converted to a string and participates in the hash calculation - ''' + """ _hash = hashlib.md5() - with salt.utils.files.fopen(fname, 'rb') as f: - for chunk in iter(lambda: f.read(4096), b''): + with salt.utils.files.fopen(fname, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): _hash.update(chunk) for extra_arg in args: @@ -461,40 +490,55 @@ def _gen_md5_filehash(fname, *args): def _dict_to_json_pretty(d, sort_keys=True): - ''' + """ helper function to generate pretty printed json output - ''' - return salt.utils.json.dumps(d, indent=4, separators=(',', ': '), sort_keys=sort_keys) + """ + return salt.utils.json.dumps( + d, indent=4, separators=(",", ": "), sort_keys=sort_keys + ) # Heuristic on whether or not the property name loosely matches given set of 'interesting' factors # If you are interested in IDs for example, 'id', 'blah_id', 'blahId' would all match def _name_matches(name, matches): - ''' + """ Helper function to see if given name has any of the patterns in given matches - ''' + """ for m in matches: if name.endswith(m): return True - if name.lower().endswith('_' + m.lower()): + if name.lower().endswith("_" + m.lower()): return True if name.lower() == m.lower(): return True return False -def _object_reducer(o, names=('id', 'name', 'path', 'httpMethod', - 'statusCode', 'Created', 'Deleted', - 'Updated', 'Flushed', 'Associated', 'Disassociated')): - ''' +def _object_reducer( + o, + names=( + "id", + "name", + "path", + "httpMethod", + "statusCode", + "Created", + "Deleted", + "Updated", + "Flushed", + "Associated", + "Disassociated", + ), +): + """ Helper function to reduce the amount of information that will be kept in the change log for API GW related return values - ''' + """ result = {} if isinstance(o, dict): for k, v in six.iteritems(o): if isinstance(v, dict): - reduced = v if k == 'variables' else _object_reducer(v, names) + reduced = v if k == "variables" else _object_reducer(v, names) if reduced or _name_matches(k, names): result[k] = reduced elif isinstance(v, list): @@ -512,231 +556,299 @@ def _object_reducer(o, names=('id', 'name', 'path', 'httpMethod', def _log_changes(ret, changekey, changevalue): - ''' + """ For logging create/update/delete operations to AWS ApiGateway - ''' - cl = ret['changes'].get('new', []) + """ + cl = ret["changes"].get("new", []) cl.append({changekey: _object_reducer(changevalue)}) - ret['changes']['new'] = cl + ret["changes"]["new"] = cl return ret def _log_error_and_abort(ret, obj): - ''' + """ helper function to update errors in the return structure - ''' - ret['result'] = False - ret['abort'] = True - if 'error' in obj: - ret['comment'] = '{0}'.format(obj.get('error')) + """ + ret["result"] = False + ret["abort"] = True + if "error" in obj: + ret["comment"] = "{0}".format(obj.get("error")) return ret class _Swagger(object): - ''' + """ this is a helper class that holds the swagger definition file and the associated logic related to how to interpret the file and apply it to AWS Api Gateway. The main interface to the outside world is in deploy_api, deploy_models, and deploy_resources methods. - ''' + """ - SWAGGER_OBJ_V2_FIELDS = ('swagger', 'info', 'host', 'basePath', 'schemes', 'consumes', 'produces', - 'paths', 'definitions', 'parameters', 'responses', 'securityDefinitions', - 'security', 'tags', 'externalDocs') + SWAGGER_OBJ_V2_FIELDS = ( + "swagger", + "info", + "host", + "basePath", + "schemes", + "consumes", + "produces", + "paths", + "definitions", + "parameters", + "responses", + "securityDefinitions", + "security", + "tags", + "externalDocs", + ) # SWAGGER OBJECT V2 Fields that are required by boto apigateway states. - SWAGGER_OBJ_V2_FIELDS_REQUIRED = ('swagger', 'info', 'basePath', 'schemes', 'paths', 'definitions') + SWAGGER_OBJ_V2_FIELDS_REQUIRED = ( + "swagger", + "info", + "basePath", + "schemes", + "paths", + "definitions", + ) # SWAGGER OPERATION NAMES - SWAGGER_OPERATION_NAMES = ('get', 'put', 'post', 'delete', 'options', 'head', 'patch') - SWAGGER_VERSIONS_SUPPORTED = ('2.0',) + SWAGGER_OPERATION_NAMES = ( + "get", + "put", + "post", + "delete", + "options", + "head", + "patch", + ) + SWAGGER_VERSIONS_SUPPORTED = ("2.0",) # VENDOR SPECIFIC FIELD PATTERNS - VENDOR_EXT_PATTERN = re.compile('^x-') + VENDOR_EXT_PATTERN = re.compile("^x-") # JSON_SCHEMA_REF - JSON_SCHEMA_DRAFT_4 = 'http://json-schema.org/draft-04/schema#' + JSON_SCHEMA_DRAFT_4 = "http://json-schema.org/draft-04/schema#" # AWS integration templates for normal and options methods - REQUEST_TEMPLATE = {'application/json': '#set($inputRoot = $input.path(\'$\'))\n' - '{\n' - '"header_params" : {\n' - '#set ($map = $input.params().header)\n' - '#foreach( $param in $map.entrySet() )\n' - '"$param.key" : "$param.value" #if( $foreach.hasNext ), #end\n' - '#end\n' - '},\n' - '"query_params" : {\n' - '#set ($map = $input.params().querystring)\n' - '#foreach( $param in $map.entrySet() )\n' - '"$param.key" : "$param.value" #if( $foreach.hasNext ), #end\n' - '#end\n' - '},\n' - '"path_params" : {\n' - '#set ($map = $input.params().path)\n' - '#foreach( $param in $map.entrySet() )\n' - '"$param.key" : "$param.value" #if( $foreach.hasNext ), #end\n' - '#end\n' - '},\n' - '"apigw_context" : {\n' - '"apiId": "$context.apiId",\n' - '"httpMethod": "$context.httpMethod",\n' - '"requestId": "$context.requestId",\n' - '"resourceId": "$context.resourceId",\n' - '"resourcePath": "$context.resourcePath",\n' - '"stage": "$context.stage",\n' - '"identity": {\n' - ' "user":"$context.identity.user",\n' - ' "userArn":"$context.identity.userArn",\n' - ' "userAgent":"$context.identity.userAgent",\n' - ' "sourceIp":"$context.identity.sourceIp",\n' - ' "cognitoIdentityId":"$context.identity.cognitoIdentityId",\n' - ' "cognitoIdentityPoolId":"$context.identity.cognitoIdentityPoolId",\n' - ' "cognitoAuthenticationType":"$context.identity.cognitoAuthenticationType",\n' - ' "cognitoAuthenticationProvider":["$util.escapeJavaScript($context.identity.cognitoAuthenticationProvider)"],\n' - ' "caller":"$context.identity.caller",\n' - ' "apiKey":"$context.identity.apiKey",\n' - ' "accountId":"$context.identity.accountId"\n' - '}\n' - '},\n' - '"body_params" : $input.json(\'$\'),\n' - '"stage_variables": {\n' - '#foreach($variable in $stageVariables.keySet())\n' - '"$variable": "$util.escapeJavaScript($stageVariables.get($variable))"\n' - '#if($foreach.hasNext), #end\n' - '#end\n' - '}\n' - '}'} - REQUEST_OPTION_TEMPLATE = {'application/json': '{"statusCode": 200}'} + REQUEST_TEMPLATE = { + "application/json": "#set($inputRoot = $input.path('$'))\n" + "{\n" + '"header_params" : {\n' + "#set ($map = $input.params().header)\n" + "#foreach( $param in $map.entrySet() )\n" + '"$param.key" : "$param.value" #if( $foreach.hasNext ), #end\n' + "#end\n" + "},\n" + '"query_params" : {\n' + "#set ($map = $input.params().querystring)\n" + "#foreach( $param in $map.entrySet() )\n" + '"$param.key" : "$param.value" #if( $foreach.hasNext ), #end\n' + "#end\n" + "},\n" + '"path_params" : {\n' + "#set ($map = $input.params().path)\n" + "#foreach( $param in $map.entrySet() )\n" + '"$param.key" : "$param.value" #if( $foreach.hasNext ), #end\n' + "#end\n" + "},\n" + '"apigw_context" : {\n' + '"apiId": "$context.apiId",\n' + '"httpMethod": "$context.httpMethod",\n' + '"requestId": "$context.requestId",\n' + '"resourceId": "$context.resourceId",\n' + '"resourcePath": "$context.resourcePath",\n' + '"stage": "$context.stage",\n' + '"identity": {\n' + ' "user":"$context.identity.user",\n' + ' "userArn":"$context.identity.userArn",\n' + ' "userAgent":"$context.identity.userAgent",\n' + ' "sourceIp":"$context.identity.sourceIp",\n' + ' "cognitoIdentityId":"$context.identity.cognitoIdentityId",\n' + ' "cognitoIdentityPoolId":"$context.identity.cognitoIdentityPoolId",\n' + ' "cognitoAuthenticationType":"$context.identity.cognitoAuthenticationType",\n' + ' "cognitoAuthenticationProvider":["$util.escapeJavaScript($context.identity.cognitoAuthenticationProvider)"],\n' + ' "caller":"$context.identity.caller",\n' + ' "apiKey":"$context.identity.apiKey",\n' + ' "accountId":"$context.identity.accountId"\n' + "}\n" + "},\n" + "\"body_params\" : $input.json('$'),\n" + '"stage_variables": {\n' + "#foreach($variable in $stageVariables.keySet())\n" + '"$variable": "$util.escapeJavaScript($stageVariables.get($variable))"\n' + "#if($foreach.hasNext), #end\n" + "#end\n" + "}\n" + "}" + } + REQUEST_OPTION_TEMPLATE = {"application/json": '{"statusCode": 200}'} # AWS integration response template mapping to convert stackTrace part or the error # to a uniform format containing strings only. Swagger does not seem to allow defining # an array of non-uniform types, to it is not possible to create error model to match # exactly what comes out of lambda functions in case of error. - RESPONSE_TEMPLATE = {'application/json': '#set($inputRoot = $input.path(\'$\'))\n' - '{\n' - ' "errorMessage" : "$inputRoot.errorMessage",\n' - ' "errorType" : "$inputRoot.errorType",\n' - ' "stackTrace" : [\n' - '#foreach($stackTrace in $inputRoot.stackTrace)\n' - ' [\n' - '#foreach($elem in $stackTrace)\n' - ' "$elem"\n' - '#if($foreach.hasNext),#end\n' - '#end\n' - ' ]\n' - '#if($foreach.hasNext),#end\n' - '#end\n' - ' ]\n' - '}'} + RESPONSE_TEMPLATE = { + "application/json": "#set($inputRoot = $input.path('$'))\n" + "{\n" + ' "errorMessage" : "$inputRoot.errorMessage",\n' + ' "errorType" : "$inputRoot.errorType",\n' + ' "stackTrace" : [\n' + "#foreach($stackTrace in $inputRoot.stackTrace)\n" + " [\n" + "#foreach($elem in $stackTrace)\n" + ' "$elem"\n' + "#if($foreach.hasNext),#end\n" + "#end\n" + " ]\n" + "#if($foreach.hasNext),#end\n" + "#end\n" + " ]\n" + "}" + } RESPONSE_OPTION_TEMPLATE = {} # This string should not be modified, every API created by this state will carry the description # below. - AWS_API_DESCRIPTION = _dict_to_json_pretty({"provisioned_by": "Salt boto_apigateway.present State", - "context": "See deployment or stage description"}) + AWS_API_DESCRIPTION = _dict_to_json_pretty( + { + "provisioned_by": "Salt boto_apigateway.present State", + "context": "See deployment or stage description", + } + ) class SwaggerParameter(object): - ''' + """ This is a helper class for the Swagger Parameter Object - ''' - LOCATIONS = ('body', 'query', 'header', 'path') + """ + + LOCATIONS = ("body", "query", "header", "path") def __init__(self, paramdict): self._paramdict = paramdict @property def location(self): - ''' + """ returns location in the swagger parameter object - ''' - _location = self._paramdict.get('in') + """ + _location = self._paramdict.get("in") if _location in _Swagger.SwaggerParameter.LOCATIONS: return _location - raise ValueError('Unsupported parameter location: {0} in Parameter Object'.format(_location)) + raise ValueError( + "Unsupported parameter location: {0} in Parameter Object".format( + _location + ) + ) @property def name(self): - ''' + """ returns parameter name in the swagger parameter object - ''' - _name = self._paramdict.get('name') + """ + _name = self._paramdict.get("name") if _name: - if self.location == 'header': - return 'method.request.header.{0}'.format(_name) - elif self.location == 'query': - return 'method.request.querystring.{0}'.format(_name) - elif self.location == 'path': - return 'method.request.path.{0}'.format(_name) + if self.location == "header": + return "method.request.header.{0}".format(_name) + elif self.location == "query": + return "method.request.querystring.{0}".format(_name) + elif self.location == "path": + return "method.request.path.{0}".format(_name) return None - raise ValueError('Parameter must have a name: {0}'.format(_dict_to_json_pretty(self._paramdict))) + raise ValueError( + "Parameter must have a name: {0}".format( + _dict_to_json_pretty(self._paramdict) + ) + ) @property def schema(self): - ''' + """ returns the name of the schema given the reference in the swagger parameter object - ''' - if self.location == 'body': - _schema = self._paramdict.get('schema') + """ + if self.location == "body": + _schema = self._paramdict.get("schema") if _schema: - if '$ref' in _schema: - schema_name = _schema.get('$ref').split('/')[-1] + if "$ref" in _schema: + schema_name = _schema.get("$ref").split("/")[-1] return schema_name - raise ValueError(('Body parameter must have a JSON reference ' - 'to the schema definition due to Amazon API restrictions: {0}'.format(self.name))) - raise ValueError('Body parameter must have a schema: {0}'.format(self.name)) + raise ValueError( + ( + "Body parameter must have a JSON reference " + "to the schema definition due to Amazon API restrictions: {0}".format( + self.name + ) + ) + ) + raise ValueError( + "Body parameter must have a schema: {0}".format(self.name) + ) return None class SwaggerMethodResponse(object): - ''' + """ Helper class for Swagger Method Response Object - ''' + """ def __init__(self, r): self._r = r @property def schema(self): - ''' + """ returns the name of the schema given the reference in the swagger method response object - ''' - _schema = self._r.get('schema') + """ + _schema = self._r.get("schema") if _schema: - if '$ref' in _schema: - return _schema.get('$ref').split('/')[-1] - raise ValueError(('Method response must have a JSON reference ' - 'to the schema definition: {0}'.format(_schema))) + if "$ref" in _schema: + return _schema.get("$ref").split("/")[-1] + raise ValueError( + ( + "Method response must have a JSON reference " + "to the schema definition: {0}".format(_schema) + ) + ) return None @property def headers(self): - ''' + """ returns the headers dictionary in the method response object - ''' - _headers = self._r.get('headers', {}) + """ + _headers = self._r.get("headers", {}) return _headers - def __init__(self, api_name, stage_name, lambda_funcname_format, - swagger_file_path, error_response_template, response_template, common_aws_args): + def __init__( + self, + api_name, + stage_name, + lambda_funcname_format, + swagger_file_path, + error_response_template, + response_template, + common_aws_args, + ): self._api_name = api_name self._stage_name = stage_name self._lambda_funcname_format = lambda_funcname_format self._common_aws_args = common_aws_args - self._restApiId = '' - self._deploymentId = '' + self._restApiId = "" + self._deploymentId = "" self._error_response_template = error_response_template self._response_template = response_template if swagger_file_path is not None: if os.path.exists(swagger_file_path) and os.path.isfile(swagger_file_path): self._swagger_file = swagger_file_path - self._md5_filehash = _gen_md5_filehash(self._swagger_file, - error_response_template, - response_template) - with salt.utils.files.fopen(self._swagger_file, 'rb') as sf: + self._md5_filehash = _gen_md5_filehash( + self._swagger_file, error_response_template, response_template + ) + with salt.utils.files.fopen(self._swagger_file, "rb") as sf: self._cfg = salt.utils.yaml.safe_load(sf) - self._swagger_version = '' + self._swagger_version = "" else: - raise IOError('Invalid swagger file path, {0}'.format(swagger_file_path)) + raise IOError( + "Invalid swagger file path, {0}".format(swagger_file_path) + ) self._validate_swagger_file() @@ -745,160 +857,188 @@ class _Swagger(object): self._resolve_api_id() def _is_http_error_rescode(self, code): - ''' + """ Helper function to determine if the passed code is in the 400~599 range of http error codes - ''' - return bool(re.match(r'^\s*[45]\d\d\s*$', code)) + """ + return bool(re.match(r"^\s*[45]\d\d\s*$", code)) def _validate_error_response_model(self, paths, mods): - ''' + """ Helper function to help validate the convention established in the swagger file on how to handle response code mapping/integration - ''' + """ for path, ops in paths: for opname, opobj in six.iteritems(ops): if opname not in _Swagger.SWAGGER_OPERATION_NAMES: continue - if 'responses' not in opobj: - raise ValueError('missing mandatory responses field in path item object') - for rescode, resobj in six.iteritems(opobj.get('responses')): - if not self._is_http_error_rescode(str(rescode)): # future lint: disable=blacklisted-function + if "responses" not in opobj: + raise ValueError( + "missing mandatory responses field in path item object" + ) + for rescode, resobj in six.iteritems(opobj.get("responses")): + if not self._is_http_error_rescode( + str(rescode) + ): # future lint: disable=blacklisted-function continue # only check for response code from 400-599 - if 'schema' not in resobj: - raise ValueError('missing schema field in path {0}, ' - 'op {1}, response {2}'.format(path, opname, rescode)) + if "schema" not in resobj: + raise ValueError( + "missing schema field in path {0}, " + "op {1}, response {2}".format(path, opname, rescode) + ) - schemaobj = resobj.get('schema') - if '$ref' not in schemaobj: - raise ValueError('missing $ref field under schema in ' - 'path {0}, op {1}, response {2}'.format(path, opname, rescode)) - schemaobjref = schemaobj.get('$ref', '/') - modelname = schemaobjref.split('/')[-1] + schemaobj = resobj.get("schema") + if "$ref" not in schemaobj: + raise ValueError( + "missing $ref field under schema in " + "path {0}, op {1}, response {2}".format( + path, opname, rescode + ) + ) + schemaobjref = schemaobj.get("$ref", "/") + modelname = schemaobjref.split("/")[-1] if modelname not in mods: - raise ValueError('model schema {0} reference not found ' - 'under /definitions'.format(schemaobjref)) + raise ValueError( + "model schema {0} reference not found " + "under /definitions".format(schemaobjref) + ) model = mods.get(modelname) - if model.get('type') != 'object': - raise ValueError('model schema {0} must be type object'.format(modelname)) - if 'properties' not in model: - raise ValueError('model schema {0} must have properties fields'.format(modelname)) + if model.get("type") != "object": + raise ValueError( + "model schema {0} must be type object".format(modelname) + ) + if "properties" not in model: + raise ValueError( + "model schema {0} must have properties fields".format( + modelname + ) + ) - modelprops = model.get('properties') - if 'errorMessage' not in modelprops: - raise ValueError('model schema {0} must have errorMessage as a property to ' - 'match AWS convention. If pattern is not set, .+ will ' - 'be used'.format(modelname)) + modelprops = model.get("properties") + if "errorMessage" not in modelprops: + raise ValueError( + "model schema {0} must have errorMessage as a property to " + "match AWS convention. If pattern is not set, .+ will " + "be used".format(modelname) + ) def _validate_lambda_funcname_format(self): - ''' + """ Checks if the lambda function name format contains only known elements :return: True on success, ValueError raised on error - ''' + """ try: if self._lambda_funcname_format: - known_kwargs = dict(stage='', - api='', - resource='', - method='') + known_kwargs = dict(stage="", api="", resource="", method="") self._lambda_funcname_format.format(**known_kwargs) return True except Exception: # pylint: disable=broad-except - raise ValueError('Invalid lambda_funcname_format {0}. Please review ' - 'documentation for known substitutable keys'.format(self._lambda_funcname_format)) + raise ValueError( + "Invalid lambda_funcname_format {0}. Please review " + "documentation for known substitutable keys".format( + self._lambda_funcname_format + ) + ) def _validate_swagger_file(self): - ''' + """ High level check/validation of the input swagger file based on https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md This is not a full schema compliance check, but rather make sure that the input file (YAML or JSON) can be read into a dictionary, and we check for the content of the Swagger Object for version and info. - ''' + """ # check for any invalid fields for Swagger Object V2 for field in self._cfg: - if (field not in _Swagger.SWAGGER_OBJ_V2_FIELDS and - not _Swagger.VENDOR_EXT_PATTERN.match(field)): - raise ValueError('Invalid Swagger Object Field: {0}'.format(field)) + if ( + field not in _Swagger.SWAGGER_OBJ_V2_FIELDS + and not _Swagger.VENDOR_EXT_PATTERN.match(field) + ): + raise ValueError("Invalid Swagger Object Field: {0}".format(field)) # check for Required Swagger fields by Saltstack boto apigateway state for field in _Swagger.SWAGGER_OBJ_V2_FIELDS_REQUIRED: if field not in self._cfg: - raise ValueError('Missing Swagger Object Field: {0}'.format(field)) + raise ValueError("Missing Swagger Object Field: {0}".format(field)) # check for Swagger Version - self._swagger_version = self._cfg.get('swagger') + self._swagger_version = self._cfg.get("swagger") if self._swagger_version not in _Swagger.SWAGGER_VERSIONS_SUPPORTED: - raise ValueError('Unsupported Swagger version: {0},' - 'Supported versions are {1}'.format(self._swagger_version, - _Swagger.SWAGGER_VERSIONS_SUPPORTED)) + raise ValueError( + "Unsupported Swagger version: {0}," + "Supported versions are {1}".format( + self._swagger_version, _Swagger.SWAGGER_VERSIONS_SUPPORTED + ) + ) log.info(type(self._models)) self._validate_error_response_model(self.paths, self._models()) @property def md5_filehash(self): - ''' + """ returns md5 hash for the swagger file - ''' + """ return self._md5_filehash @property def info(self): - ''' + """ returns the swagger info object as a dictionary - ''' - info = self._cfg.get('info') + """ + info = self._cfg.get("info") if not info: - raise ValueError('Info Object has no values') + raise ValueError("Info Object has no values") return info @property def info_json(self): - ''' + """ returns the swagger info object as a pretty printed json string. - ''' + """ return _dict_to_json_pretty(self.info) @property def rest_api_name(self): - ''' + """ returns the name of the api - ''' + """ return self._api_name @property def rest_api_version(self): - ''' + """ returns the version field in the swagger info object - ''' - version = self.info.get('version') + """ + version = self.info.get("version") if not version: - raise ValueError('Missing version value in Info Object') + raise ValueError("Missing version value in Info Object") return version def _models(self): - ''' + """ returns an iterator for the models specified in the swagger file - ''' - models = self._cfg.get('definitions') + """ + models = self._cfg.get("definitions") if not models: - raise ValueError('Definitions Object has no values, You need to define them in your swagger file') + raise ValueError( + "Definitions Object has no values, You need to define them in your swagger file" + ) return models def models(self): - ''' + """ generator to return the tuple of model and its schema to create on aws. - ''' + """ model_dict = self._build_all_dependencies() while True: model = self._get_model_without_dependencies(model_dict) @@ -908,85 +1048,92 @@ class _Swagger(object): @property def paths(self): - ''' + """ returns an iterator for the relative resource paths specified in the swagger file - ''' - paths = self._cfg.get('paths') + """ + paths = self._cfg.get("paths") if not paths: - raise ValueError('Paths Object has no values, You need to define them in your swagger file') + raise ValueError( + "Paths Object has no values, You need to define them in your swagger file" + ) for path in paths: - if not path.startswith('/'): - raise ValueError('Path object {0} should start with /. Please fix it'.format(path)) + if not path.startswith("/"): + raise ValueError( + "Path object {0} should start with /. Please fix it".format(path) + ) return six.iteritems(paths) @property def basePath(self): - ''' + """ returns the base path field as defined in the swagger file - ''' - basePath = self._cfg.get('basePath', '') + """ + basePath = self._cfg.get("basePath", "") return basePath @property def restApiId(self): - ''' + """ returns the rest api id as returned by AWS on creation of the rest api - ''' + """ return self._restApiId @restApiId.setter def restApiId(self, restApiId): - ''' + """ allows the assignment of the rest api id on creation of the rest api - ''' + """ self._restApiId = restApiId @property def deployment_label_json(self): - ''' + """ this property returns the unique description in pretty printed json for a particular api deployment - ''' + """ return _dict_to_json_pretty(self.deployment_label) @property def deployment_label(self): - ''' + """ this property returns the deployment label dictionary (mainly used by stage description) - ''' + """ label = dict() - label['swagger_info_object'] = self.info - label['api_name'] = self.rest_api_name - label['swagger_file'] = os.path.basename(self._swagger_file) - label['swagger_file_md5sum'] = self.md5_filehash + label["swagger_info_object"] = self.info + label["api_name"] = self.rest_api_name + label["swagger_file"] = os.path.basename(self._swagger_file) + label["swagger_file_md5sum"] = self.md5_filehash return label # methods to interact with boto_apigateway execution modules def _one_or_more_stages_remain(self, deploymentId): - ''' + """ Helper function to find whether there are other stages still associated with a deployment - ''' - stages = __salt__['boto_apigateway.describe_api_stages'](restApiId=self.restApiId, - deploymentId=deploymentId, - **self._common_aws_args).get('stages') + """ + stages = __salt__["boto_apigateway.describe_api_stages"]( + restApiId=self.restApiId, deploymentId=deploymentId, **self._common_aws_args + ).get("stages") return bool(stages) def no_more_deployments_remain(self): - ''' + """ Helper function to find whether there are deployments left with stages associated - ''' + """ no_more_deployments = True - deployments = __salt__['boto_apigateway.describe_api_deployments'](restApiId=self.restApiId, - **self._common_aws_args).get('deployments') + deployments = __salt__["boto_apigateway.describe_api_deployments"]( + restApiId=self.restApiId, **self._common_aws_args + ).get("deployments") if deployments: for deployment in deployments: - deploymentId = deployment.get('id') - stages = __salt__['boto_apigateway.describe_api_stages'](restApiId=self.restApiId, - deploymentId=deploymentId, - **self._common_aws_args).get('stages') + deploymentId = deployment.get("id") + stages = __salt__["boto_apigateway.describe_api_stages"]( + restApiId=self.restApiId, + deploymentId=deploymentId, + **self._common_aws_args + ).get("stages") if stages: no_more_deployments = False break @@ -994,141 +1141,168 @@ class _Swagger(object): return no_more_deployments def _get_current_deployment_id(self): - ''' + """ Helper method to find the deployment id that the stage name is currently assocaited with. - ''' - deploymentId = '' - stage = __salt__['boto_apigateway.describe_api_stage'](restApiId=self.restApiId, - stageName=self._stage_name, - **self._common_aws_args).get('stage') + """ + deploymentId = "" + stage = __salt__["boto_apigateway.describe_api_stage"]( + restApiId=self.restApiId, + stageName=self._stage_name, + **self._common_aws_args + ).get("stage") if stage: - deploymentId = stage.get('deploymentId') + deploymentId = stage.get("deploymentId") return deploymentId def _get_current_deployment_label(self): - ''' + """ Helper method to find the deployment label that the stage_name is currently associated with. - ''' + """ deploymentId = self._get_current_deployment_id() - deployment = __salt__['boto_apigateway.describe_api_deployment'](restApiId=self.restApiId, - deploymentId=deploymentId, - **self._common_aws_args).get('deployment') + deployment = __salt__["boto_apigateway.describe_api_deployment"]( + restApiId=self.restApiId, deploymentId=deploymentId, **self._common_aws_args + ).get("deployment") if deployment: - return deployment.get('description') + return deployment.get("description") return None def _get_desired_deployment_id(self): - ''' + """ Helper method to return the deployment id matching the desired deployment label for this Swagger object based on the given api_name, swagger_file - ''' - deployments = __salt__['boto_apigateway.describe_api_deployments'](restApiId=self.restApiId, - **self._common_aws_args).get('deployments') + """ + deployments = __salt__["boto_apigateway.describe_api_deployments"]( + restApiId=self.restApiId, **self._common_aws_args + ).get("deployments") if deployments: for deployment in deployments: - if deployment.get('description') == self.deployment_label_json: - return deployment.get('id') - return '' + if deployment.get("description") == self.deployment_label_json: + return deployment.get("id") + return "" def overwrite_stage_variables(self, ret, stage_variables): - ''' + """ overwrite the given stage_name's stage variables with the given stage_variables - ''' - res = __salt__['boto_apigateway.overwrite_api_stage_variables'](restApiId=self.restApiId, - stageName=self._stage_name, - variables=stage_variables, - **self._common_aws_args) + """ + res = __salt__["boto_apigateway.overwrite_api_stage_variables"]( + restApiId=self.restApiId, + stageName=self._stage_name, + variables=stage_variables, + **self._common_aws_args + ) - if not res.get('overwrite'): - ret['result'] = False - ret['abort'] = True - ret['comment'] = res.get('error') + if not res.get("overwrite"): + ret["result"] = False + ret["abort"] = True + ret["comment"] = res.get("error") else: - ret = _log_changes(ret, - 'overwrite_stage_variables', - res.get('stage')) + ret = _log_changes(ret, "overwrite_stage_variables", res.get("stage")) return ret def _set_current_deployment(self, stage_desc_json, stage_variables): - ''' + """ Helper method to associate the stage_name to the given deploymentId and make this current - ''' - stage = __salt__['boto_apigateway.describe_api_stage'](restApiId=self.restApiId, - stageName=self._stage_name, - **self._common_aws_args).get('stage') + """ + stage = __salt__["boto_apigateway.describe_api_stage"]( + restApiId=self.restApiId, + stageName=self._stage_name, + **self._common_aws_args + ).get("stage") if not stage: - stage = __salt__['boto_apigateway.create_api_stage'](restApiId=self.restApiId, - stageName=self._stage_name, - deploymentId=self._deploymentId, - description=stage_desc_json, - variables=stage_variables, - **self._common_aws_args) - if not stage.get('stage'): - return {'set': False, 'error': stage.get('error')} + stage = __salt__["boto_apigateway.create_api_stage"]( + restApiId=self.restApiId, + stageName=self._stage_name, + deploymentId=self._deploymentId, + description=stage_desc_json, + variables=stage_variables, + **self._common_aws_args + ) + if not stage.get("stage"): + return {"set": False, "error": stage.get("error")} else: # overwrite the stage variables - overwrite = __salt__['boto_apigateway.overwrite_api_stage_variables'](restApiId=self.restApiId, - stageName=self._stage_name, - variables=stage_variables, - **self._common_aws_args) - if not overwrite.get('stage'): - return {'set': False, 'error': overwrite.get('error')} + overwrite = __salt__["boto_apigateway.overwrite_api_stage_variables"]( + restApiId=self.restApiId, + stageName=self._stage_name, + variables=stage_variables, + **self._common_aws_args + ) + if not overwrite.get("stage"): + return {"set": False, "error": overwrite.get("error")} - return __salt__['boto_apigateway.activate_api_deployment'](restApiId=self.restApiId, - stageName=self._stage_name, - deploymentId=self._deploymentId, - **self._common_aws_args) + return __salt__["boto_apigateway.activate_api_deployment"]( + restApiId=self.restApiId, + stageName=self._stage_name, + deploymentId=self._deploymentId, + **self._common_aws_args + ) def _resolve_api_id(self): - ''' + """ returns an Api Id that matches the given api_name and the hardcoded _Swagger.AWS_API_DESCRIPTION as the api description - ''' - apis = __salt__['boto_apigateway.describe_apis'](name=self.rest_api_name, - description=_Swagger.AWS_API_DESCRIPTION, - **self._common_aws_args).get('restapi') + """ + apis = __salt__["boto_apigateway.describe_apis"]( + name=self.rest_api_name, + description=_Swagger.AWS_API_DESCRIPTION, + **self._common_aws_args + ).get("restapi") if apis: if len(apis) == 1: - self.restApiId = apis[0].get('id') + self.restApiId = apis[0].get("id") else: - raise ValueError('Multiple APIs matching given name {0} and ' - 'description {1}'.format(self.rest_api_name, self.info_json)) + raise ValueError( + "Multiple APIs matching given name {0} and " + "description {1}".format(self.rest_api_name, self.info_json) + ) def delete_stage(self, ret): - ''' + """ Method to delete the given stage_name. If the current deployment tied to the given stage_name has no other stages associated with it, the deployment will be removed as well - ''' + """ deploymentId = self._get_current_deployment_id() if deploymentId: - result = __salt__['boto_apigateway.delete_api_stage'](restApiId=self.restApiId, - stageName=self._stage_name, - **self._common_aws_args) - if not result.get('deleted'): - ret['abort'] = True - ret['result'] = False - ret['comment'] = 'delete_stage delete_api_stage, {0}'.format(result.get('error')) + result = __salt__["boto_apigateway.delete_api_stage"]( + restApiId=self.restApiId, + stageName=self._stage_name, + **self._common_aws_args + ) + if not result.get("deleted"): + ret["abort"] = True + ret["result"] = False + ret["comment"] = "delete_stage delete_api_stage, {0}".format( + result.get("error") + ) else: # check if it is safe to delete the deployment as well. if not self._one_or_more_stages_remain(deploymentId): - result = __salt__['boto_apigateway.delete_api_deployment'](restApiId=self.restApiId, - deploymentId=deploymentId, - **self._common_aws_args) - if not result.get('deleted'): - ret['abort'] = True - ret['result'] = False - ret['comment'] = 'delete_stage delete_api_deployment, {0}'.format(result.get('error')) + result = __salt__["boto_apigateway.delete_api_deployment"]( + restApiId=self.restApiId, + deploymentId=deploymentId, + **self._common_aws_args + ) + if not result.get("deleted"): + ret["abort"] = True + ret["result"] = False + ret[ + "comment" + ] = "delete_stage delete_api_deployment, {0}".format( + result.get("error") + ) else: - ret['comment'] = 'stage {0} has been deleted.\n'.format(self._stage_name) + ret["comment"] = "stage {0} has been deleted.\n".format( + self._stage_name + ) else: # no matching stage_name/deployment found - ret['comment'] = 'stage {0} does not exist'.format(self._stage_name) + ret["comment"] = "stage {0} does not exist".format(self._stage_name) return ret def verify_api(self, ret): - ''' + """ this method helps determine if the given stage_name is already on a deployment label matching the input api_name, swagger_file. @@ -1136,161 +1310,196 @@ class _Swagger(object): If not and there is previous deployment labels in AWS matching the given input api_name and swagger file, indicate to the caller that we only need to reassociate stage_name to the previously existing deployment label. - ''' + """ if self.restApiId: deployed_label_json = self._get_current_deployment_label() if deployed_label_json == self.deployment_label_json: - ret['comment'] = ('Already at desired state, the stage {0} is already at the desired ' - 'deployment label:\n{1}'.format(self._stage_name, deployed_label_json)) - ret['current'] = True + ret["comment"] = ( + "Already at desired state, the stage {0} is already at the desired " + "deployment label:\n{1}".format( + self._stage_name, deployed_label_json + ) + ) + ret["current"] = True return ret else: self._deploymentId = self._get_desired_deployment_id() if self._deploymentId: - ret['publish'] = True + ret["publish"] = True return ret def publish_api(self, ret, stage_variables): - ''' + """ this method tie the given stage_name to a deployment matching the given swagger_file - ''' + """ stage_desc = dict() - stage_desc['current_deployment_label'] = self.deployment_label + stage_desc["current_deployment_label"] = self.deployment_label stage_desc_json = _dict_to_json_pretty(stage_desc) if self._deploymentId: # just do a reassociate of stage_name to an already existing deployment res = self._set_current_deployment(stage_desc_json, stage_variables) - if not res.get('set'): - ret['abort'] = True - ret['result'] = False - ret['comment'] = res.get('error') + if not res.get("set"): + ret["abort"] = True + ret["result"] = False + ret["comment"] = res.get("error") else: - ret = _log_changes(ret, - 'publish_api (reassociate deployment, set stage_variables)', - res.get('response')) + ret = _log_changes( + ret, + "publish_api (reassociate deployment, set stage_variables)", + res.get("response"), + ) else: # no deployment existed for the given swagger_file for this Swagger object - res = __salt__['boto_apigateway.create_api_deployment'](restApiId=self.restApiId, - stageName=self._stage_name, - stageDescription=stage_desc_json, - description=self.deployment_label_json, - variables=stage_variables, - **self._common_aws_args) - if not res.get('created'): - ret['abort'] = True - ret['result'] = False - ret['comment'] = res.get('error') + res = __salt__["boto_apigateway.create_api_deployment"]( + restApiId=self.restApiId, + stageName=self._stage_name, + stageDescription=stage_desc_json, + description=self.deployment_label_json, + variables=stage_variables, + **self._common_aws_args + ) + if not res.get("created"): + ret["abort"] = True + ret["result"] = False + ret["comment"] = res.get("error") else: - ret = _log_changes(ret, 'publish_api (new deployment)', res.get('deployment')) + ret = _log_changes( + ret, "publish_api (new deployment)", res.get("deployment") + ) return ret def _cleanup_api(self): - ''' + """ Helper method to clean up resources and models if we detected a change in the swagger file for a stage - ''' - resources = __salt__['boto_apigateway.describe_api_resources'](restApiId=self.restApiId, - **self._common_aws_args) - if resources.get('resources'): - res = resources.get('resources')[1:] + """ + resources = __salt__["boto_apigateway.describe_api_resources"]( + restApiId=self.restApiId, **self._common_aws_args + ) + if resources.get("resources"): + res = resources.get("resources")[1:] res.reverse() for resource in res: - delres = __salt__['boto_apigateway.delete_api_resources'](restApiId=self.restApiId, - path=resource.get('path'), - **self._common_aws_args) - if not delres.get('deleted'): + delres = __salt__["boto_apigateway.delete_api_resources"]( + restApiId=self.restApiId, + path=resource.get("path"), + **self._common_aws_args + ) + if not delres.get("deleted"): return delres - models = __salt__['boto_apigateway.describe_api_models'](restApiId=self.restApiId, **self._common_aws_args) - if models.get('models'): - for model in models.get('models'): - delres = __salt__['boto_apigateway.delete_api_model'](restApiId=self.restApiId, - modelName=model.get('name'), - **self._common_aws_args) - if not delres.get('deleted'): + models = __salt__["boto_apigateway.describe_api_models"]( + restApiId=self.restApiId, **self._common_aws_args + ) + if models.get("models"): + for model in models.get("models"): + delres = __salt__["boto_apigateway.delete_api_model"]( + restApiId=self.restApiId, + modelName=model.get("name"), + **self._common_aws_args + ) + if not delres.get("deleted"): return delres - return {'deleted': True} + return {"deleted": True} def deploy_api(self, ret): - ''' + """ this method create the top level rest api in AWS apigateway - ''' + """ if self.restApiId: res = self._cleanup_api() - if not res.get('deleted'): - ret['comment'] = 'Failed to cleanup restAreId {0}'.format(self.restApiId) - ret['abort'] = True - ret['result'] = False + if not res.get("deleted"): + ret["comment"] = "Failed to cleanup restAreId {0}".format( + self.restApiId + ) + ret["abort"] = True + ret["result"] = False return ret return ret - response = __salt__['boto_apigateway.create_api'](name=self.rest_api_name, - description=_Swagger.AWS_API_DESCRIPTION, - **self._common_aws_args) + response = __salt__["boto_apigateway.create_api"]( + name=self.rest_api_name, + description=_Swagger.AWS_API_DESCRIPTION, + **self._common_aws_args + ) - if not response.get('created'): - ret['result'] = False - ret['abort'] = True - if 'error' in response: - ret['comment'] = 'Failed to create rest api: {0}.'.format(response['error']['message']) + if not response.get("created"): + ret["result"] = False + ret["abort"] = True + if "error" in response: + ret["comment"] = "Failed to create rest api: {0}.".format( + response["error"]["message"] + ) return ret - self.restApiId = response.get('restapi', {}).get('id') + self.restApiId = response.get("restapi", {}).get("id") - return _log_changes(ret, 'deploy_api', response.get('restapi')) + return _log_changes(ret, "deploy_api", response.get("restapi")) def delete_api(self, ret): - ''' + """ Method to delete a Rest Api named defined in the swagger file's Info Object's title value. ret a dictionary for returning status to Saltstack - ''' + """ - exists_response = __salt__['boto_apigateway.api_exists'](name=self.rest_api_name, - description=_Swagger.AWS_API_DESCRIPTION, - **self._common_aws_args) - if exists_response.get('exists'): - if __opts__['test']: - ret['comment'] = 'Rest API named {0} is set to be deleted.'.format(self.rest_api_name) - ret['result'] = None - ret['abort'] = True + exists_response = __salt__["boto_apigateway.api_exists"]( + name=self.rest_api_name, + description=_Swagger.AWS_API_DESCRIPTION, + **self._common_aws_args + ) + if exists_response.get("exists"): + if __opts__["test"]: + ret["comment"] = "Rest API named {0} is set to be deleted.".format( + self.rest_api_name + ) + ret["result"] = None + ret["abort"] = True return ret - delete_api_response = __salt__['boto_apigateway.delete_api'](name=self.rest_api_name, - description=_Swagger.AWS_API_DESCRIPTION, - **self._common_aws_args) - if not delete_api_response.get('deleted'): - ret['result'] = False - ret['abort'] = True - if 'error' in delete_api_response: - ret['comment'] = 'Failed to delete rest api: {0}.'.format(delete_api_response['error']['message']) + delete_api_response = __salt__["boto_apigateway.delete_api"]( + name=self.rest_api_name, + description=_Swagger.AWS_API_DESCRIPTION, + **self._common_aws_args + ) + if not delete_api_response.get("deleted"): + ret["result"] = False + ret["abort"] = True + if "error" in delete_api_response: + ret["comment"] = "Failed to delete rest api: {0}.".format( + delete_api_response["error"]["message"] + ) return ret - ret = _log_changes(ret, 'delete_api', delete_api_response) + ret = _log_changes(ret, "delete_api", delete_api_response) else: - ret['comment'] = ('api already absent for swagger file: ' - '{0}, desc: {1}'.format(self.rest_api_name, self.info_json)) + ret["comment"] = ( + "api already absent for swagger file: " + "{0}, desc: {1}".format(self.rest_api_name, self.info_json) + ) return ret def _aws_model_ref_from_swagger_ref(self, r): - ''' + """ Helper function to reference models created on aws apigw - ''' - model_name = r.split('/')[-1] - return 'https://apigateway.amazonaws.com/restapis/{0}/models/{1}'.format(self.restApiId, model_name) + """ + model_name = r.split("/")[-1] + return "https://apigateway.amazonaws.com/restapis/{0}/models/{1}".format( + self.restApiId, model_name + ) def _update_schema_to_aws_notation(self, schema): - ''' + """ Helper function to map model schema to aws notation - ''' + """ result = {} for k, v in schema.items(): - if k == '$ref': + if k == "$ref": v = self._aws_model_ref_from_swagger_ref(v) if isinstance(v, dict): v = self._update_schema_to_aws_notation(v) @@ -1298,17 +1507,19 @@ class _Swagger(object): return result def _build_dependent_model_list(self, obj_schema): - ''' + """ Helper function to build the list of models the given object schema is referencing. - ''' + """ dep_models_list = [] if obj_schema: - obj_schema['type'] = obj_schema.get('type', 'object') - if obj_schema['type'] == 'array': - dep_models_list.extend(self._build_dependent_model_list(obj_schema.get('items', {}))) + obj_schema["type"] = obj_schema.get("type", "object") + if obj_schema["type"] == "array": + dep_models_list.extend( + self._build_dependent_model_list(obj_schema.get("items", {})) + ) else: - ref = obj_schema.get('$ref') + ref = obj_schema.get("$ref") if ref: ref_obj_model = ref.split("/")[-1] ref_obj_schema = self._models().get(ref_obj_model) @@ -1316,16 +1527,18 @@ class _Swagger(object): dep_models_list.extend([ref_obj_model]) else: # need to walk each property object - properties = obj_schema.get('properties') + properties = obj_schema.get("properties") if properties: for _, prop_obj_schema in six.iteritems(properties): - dep_models_list.extend(self._build_dependent_model_list(prop_obj_schema)) + dep_models_list.extend( + self._build_dependent_model_list(prop_obj_schema) + ) return list(set(dep_models_list)) def _build_all_dependencies(self): - ''' + """ Helper function to build a map of model to their list of model reference dependencies - ''' + """ ret = {} for model, schema in six.iteritems(self._models()): dep_list = self._build_dependent_model_list(schema) @@ -1333,9 +1546,9 @@ class _Swagger(object): return ret def _get_model_without_dependencies(self, models_dict): - ''' + """ Helper function to find the next model that should be created - ''' + """ next_model = None if not models_dict: return next_model @@ -1346,8 +1559,10 @@ class _Swagger(object): break if next_model is None: - raise ValueError('incomplete model definitions, models in dependency ' - 'list not defined: {0}'.format(models_dict)) + raise ValueError( + "incomplete model definitions, models in dependency " + "list not defined: {0}".format(models_dict) + ) # remove the model from other depednencies before returning models_dict.pop(next_model) @@ -1358,125 +1573,160 @@ class _Swagger(object): return next_model def deploy_models(self, ret): - ''' + """ Method to deploy swagger file's definition objects and associated schema to AWS Apigateway as Models ret a dictionary for returning status to Saltstack - ''' + """ for model, schema in self.models(): # add in a few attributes into the model schema that AWS expects # _schema = schema.copy() _schema = self._update_schema_to_aws_notation(schema) - _schema.update({'$schema': _Swagger.JSON_SCHEMA_DRAFT_4, - 'title': '{0} Schema'.format(model)}) + _schema.update( + { + "$schema": _Swagger.JSON_SCHEMA_DRAFT_4, + "title": "{0} Schema".format(model), + } + ) # check to see if model already exists, aws has 2 default models [Empty, Error] # which may need upate with data from swagger file - model_exists_response = __salt__['boto_apigateway.api_model_exists'](restApiId=self.restApiId, - modelName=model, - **self._common_aws_args) + model_exists_response = __salt__["boto_apigateway.api_model_exists"]( + restApiId=self.restApiId, modelName=model, **self._common_aws_args + ) - if model_exists_response.get('exists'): - update_model_schema_response = ( - __salt__['boto_apigateway.update_api_model_schema'](restApiId=self.restApiId, - modelName=model, - schema=_dict_to_json_pretty(_schema), - **self._common_aws_args)) - if not update_model_schema_response.get('updated'): - ret['result'] = False - ret['abort'] = True - if 'error' in update_model_schema_response: - ret['comment'] = ('Failed to update existing model {0} with schema {1}, ' - 'error: {2}'.format(model, _dict_to_json_pretty(schema), - update_model_schema_response['error']['message'])) + if model_exists_response.get("exists"): + update_model_schema_response = __salt__[ + "boto_apigateway.update_api_model_schema" + ]( + restApiId=self.restApiId, + modelName=model, + schema=_dict_to_json_pretty(_schema), + **self._common_aws_args + ) + if not update_model_schema_response.get("updated"): + ret["result"] = False + ret["abort"] = True + if "error" in update_model_schema_response: + ret["comment"] = ( + "Failed to update existing model {0} with schema {1}, " + "error: {2}".format( + model, + _dict_to_json_pretty(schema), + update_model_schema_response["error"]["message"], + ) + ) return ret - ret = _log_changes(ret, 'deploy_models', update_model_schema_response) + ret = _log_changes(ret, "deploy_models", update_model_schema_response) else: - create_model_response = ( - __salt__['boto_apigateway.create_api_model'](restApiId=self.restApiId, modelName=model, - modelDescription=model, - schema=_dict_to_json_pretty(_schema), - contentType='application/json', - **self._common_aws_args)) + create_model_response = __salt__["boto_apigateway.create_api_model"]( + restApiId=self.restApiId, + modelName=model, + modelDescription=model, + schema=_dict_to_json_pretty(_schema), + contentType="application/json", + **self._common_aws_args + ) - if not create_model_response.get('created'): - ret['result'] = False - ret['abort'] = True - if 'error' in create_model_response: - ret['comment'] = ('Failed to create model {0}, schema {1}, ' - 'error: {2}'.format(model, _dict_to_json_pretty(schema), - create_model_response['error']['message'])) + if not create_model_response.get("created"): + ret["result"] = False + ret["abort"] = True + if "error" in create_model_response: + ret["comment"] = ( + "Failed to create model {0}, schema {1}, " + "error: {2}".format( + model, + _dict_to_json_pretty(schema), + create_model_response["error"]["message"], + ) + ) return ret - ret = _log_changes(ret, 'deploy_models', create_model_response) + ret = _log_changes(ret, "deploy_models", create_model_response) return ret def _lambda_name(self, resourcePath, httpMethod): - ''' + """ Helper method to construct lambda name based on the rule specified in doc string of boto_apigateway.api_present function - ''' - lambda_name = self._lambda_funcname_format.format(stage=self._stage_name, - api=self.rest_api_name, - resource=resourcePath, - method=httpMethod) + """ + lambda_name = self._lambda_funcname_format.format( + stage=self._stage_name, + api=self.rest_api_name, + resource=resourcePath, + method=httpMethod, + ) lambda_name = lambda_name.strip() - lambda_name = re.sub(r'{|}', '', lambda_name) - lambda_name = re.sub(r'\s+|/', '_', lambda_name).lower() - return re.sub(r'_+', '_', lambda_name) + lambda_name = re.sub(r"{|}", "", lambda_name) + lambda_name = re.sub(r"\s+|/", "_", lambda_name).lower() + return re.sub(r"_+", "_", lambda_name) def _lambda_uri(self, lambda_name, lambda_region): - ''' + """ Helper Method to construct the lambda uri for use in method integration - ''' - profile = self._common_aws_args.get('profile') - region = self._common_aws_args.get('region') + """ + profile = self._common_aws_args.get("profile") + region = self._common_aws_args.get("region") - lambda_region = __utils__['boto3.get_region']('lambda', lambda_region, profile) - apigw_region = __utils__['boto3.get_region']('apigateway', region, profile) + lambda_region = __utils__["boto3.get_region"]("lambda", lambda_region, profile) + apigw_region = __utils__["boto3.get_region"]("apigateway", region, profile) - lambda_desc = __salt__['boto_lambda.describe_function'](lambda_name, **self._common_aws_args) + lambda_desc = __salt__["boto_lambda.describe_function"]( + lambda_name, **self._common_aws_args + ) if lambda_region != apigw_region: - if not lambda_desc.get('function'): + if not lambda_desc.get("function"): # try look up in the same region as the apigateway as well if previous lookup failed - lambda_desc = __salt__['boto_lambda.describe_function'](lambda_name, **self._common_aws_args) + lambda_desc = __salt__["boto_lambda.describe_function"]( + lambda_name, **self._common_aws_args + ) - if not lambda_desc.get('function'): - raise ValueError('Could not find lambda function {0} in ' - 'regions [{1}, {2}].'.format(lambda_name, lambda_region, apigw_region)) + if not lambda_desc.get("function"): + raise ValueError( + "Could not find lambda function {0} in " + "regions [{1}, {2}].".format(lambda_name, lambda_region, apigw_region) + ) - lambda_arn = lambda_desc.get('function').get('FunctionArn') - lambda_uri = ('arn:aws:apigateway:{0}:lambda:path/2015-03-31' - '/functions/{1}/invocations'.format(apigw_region, lambda_arn)) + lambda_arn = lambda_desc.get("function").get("FunctionArn") + lambda_uri = ( + "arn:aws:apigateway:{0}:lambda:path/2015-03-31" + "/functions/{1}/invocations".format(apigw_region, lambda_arn) + ) return lambda_uri def _parse_method_data(self, method_name, method_data): - ''' + """ Helper function to construct the method request params, models, request_templates and integration_type values needed to configure method request integration/mappings. - ''' + """ method_params = {} method_models = {} - if 'parameters' in method_data: - for param in method_data['parameters']: + if "parameters" in method_data: + for param in method_data["parameters"]: p = _Swagger.SwaggerParameter(param) if p.name: method_params[p.name] = True if p.schema: - method_models['application/json'] = p.schema + method_models["application/json"] = p.schema - request_templates = _Swagger.REQUEST_OPTION_TEMPLATE if method_name == 'options' else _Swagger.REQUEST_TEMPLATE - integration_type = "MOCK" if method_name == 'options' else "AWS" + request_templates = ( + _Swagger.REQUEST_OPTION_TEMPLATE + if method_name == "options" + else _Swagger.REQUEST_TEMPLATE + ) + integration_type = "MOCK" if method_name == "options" else "AWS" - return {'params': method_params, - 'models': method_models, - 'request_templates': request_templates, - 'integration_type': integration_type} + return { + "params": method_params, + "models": method_models, + "request_templates": request_templates, + "integration_type": integration_type, + } def _find_patterns(self, o): result = [] @@ -1485,59 +1735,81 @@ class _Swagger(object): if isinstance(v, dict): result.extend(self._find_patterns(v)) else: - if k == 'pattern': + if k == "pattern": result.append(v) return result def _get_pattern_for_schema(self, schema_name, httpStatus): - ''' + """ returns the pattern specified in a response schema - ''' - defaultPattern = '.+' if self._is_http_error_rescode(httpStatus) else '.*' + """ + defaultPattern = ".+" if self._is_http_error_rescode(httpStatus) else ".*" model = self._models().get(schema_name) patterns = self._find_patterns(model) return patterns[0] if patterns else defaultPattern def _get_response_template(self, method_name, http_status): - if method_name == 'options' or not self._is_http_error_rescode(http_status): - response_templates = {'application/json': self._response_template} \ - if self._response_template else self.RESPONSE_OPTION_TEMPLATE + if method_name == "options" or not self._is_http_error_rescode(http_status): + response_templates = ( + {"application/json": self._response_template} + if self._response_template + else self.RESPONSE_OPTION_TEMPLATE + ) else: - response_templates = {'application/json': self._error_response_template} \ - if self._error_response_template else self.RESPONSE_TEMPLATE + response_templates = ( + {"application/json": self._error_response_template} + if self._error_response_template + else self.RESPONSE_TEMPLATE + ) return response_templates def _parse_method_response(self, method_name, method_response, httpStatus): - ''' + """ Helper function to construct the method response params, models, and integration_params values needed to configure method response integration/mappings. - ''' + """ method_response_models = {} - method_response_pattern = '.*' + method_response_pattern = ".*" if method_response.schema: - method_response_models['application/json'] = method_response.schema - method_response_pattern = self._get_pattern_for_schema(method_response.schema, httpStatus) + method_response_models["application/json"] = method_response.schema + method_response_pattern = self._get_pattern_for_schema( + method_response.schema, httpStatus + ) method_response_params = {} method_integration_response_params = {} for header in method_response.headers: - response_header = 'method.response.header.{0}'.format(header) + response_header = "method.response.header.{0}".format(header) method_response_params[response_header] = False header_data = method_response.headers.get(header) method_integration_response_params[response_header] = ( - "'{0}'".format(header_data.get('default')) if 'default' in header_data else "'*'") + "'{0}'".format(header_data.get("default")) + if "default" in header_data + else "'*'" + ) response_templates = self._get_response_template(method_name, httpStatus) - return {'params': method_response_params, - 'models': method_response_models, - 'integration_params': method_integration_response_params, - 'pattern': method_response_pattern, - 'response_templates': response_templates} + return { + "params": method_response_params, + "models": method_response_models, + "integration_params": method_integration_response_params, + "pattern": method_response_pattern, + "response_templates": response_templates, + } - def _deploy_method(self, ret, resource_path, method_name, method_data, api_key_required, - lambda_integration_role, lambda_region, authorization_type): - ''' + def _deploy_method( + self, + ret, + resource_path, + method_name, + method_data, + api_key_required, + lambda_integration_role, + lambda_region, + authorization_type, + ): + """ Method to create a method for the given resource path, along with its associated request and response integrations. @@ -1567,90 +1839,111 @@ class _Swagger(object): authorization_type 'NONE' or 'AWS_IAM' - ''' + """ method = self._parse_method_data(method_name.lower(), method_data) # for options method to enable CORS, api_key_required will be set to False always. # authorization_type will be set to 'NONE' always. - if method_name.lower() == 'options': + if method_name.lower() == "options": api_key_required = False - authorization_type = 'NONE' + authorization_type = "NONE" - m = __salt__['boto_apigateway.create_api_method'](restApiId=self.restApiId, - resourcePath=resource_path, - httpMethod=method_name.upper(), - authorizationType=authorization_type, - apiKeyRequired=api_key_required, - requestParameters=method.get('params'), - requestModels=method.get('models'), - **self._common_aws_args) - if not m.get('created'): + m = __salt__["boto_apigateway.create_api_method"]( + restApiId=self.restApiId, + resourcePath=resource_path, + httpMethod=method_name.upper(), + authorizationType=authorization_type, + apiKeyRequired=api_key_required, + requestParameters=method.get("params"), + requestModels=method.get("models"), + **self._common_aws_args + ) + if not m.get("created"): ret = _log_error_and_abort(ret, m) return ret - ret = _log_changes(ret, '_deploy_method.create_api_method', m) + ret = _log_changes(ret, "_deploy_method.create_api_method", m) lambda_uri = "" - if method_name.lower() != 'options': - lambda_uri = self._lambda_uri(self._lambda_name(resource_path, method_name), - lambda_region=lambda_region) + if method_name.lower() != "options": + lambda_uri = self._lambda_uri( + self._lambda_name(resource_path, method_name), + lambda_region=lambda_region, + ) # NOTE: integration method is set to POST always, as otherwise AWS makes wrong assumptions # about the intent of the call. HTTP method will be passed to lambda as part of the API gateway context - integration = ( - __salt__['boto_apigateway.create_api_integration'](restApiId=self.restApiId, - resourcePath=resource_path, - httpMethod=method_name.upper(), - integrationType=method.get('integration_type'), - integrationHttpMethod='POST', - uri=lambda_uri, - credentials=lambda_integration_role, - requestTemplates=method.get('request_templates'), - **self._common_aws_args)) - if not integration.get('created'): + integration = __salt__["boto_apigateway.create_api_integration"]( + restApiId=self.restApiId, + resourcePath=resource_path, + httpMethod=method_name.upper(), + integrationType=method.get("integration_type"), + integrationHttpMethod="POST", + uri=lambda_uri, + credentials=lambda_integration_role, + requestTemplates=method.get("request_templates"), + **self._common_aws_args + ) + if not integration.get("created"): ret = _log_error_and_abort(ret, integration) return ret - ret = _log_changes(ret, '_deploy_method.create_api_integration', integration) + ret = _log_changes(ret, "_deploy_method.create_api_integration", integration) - if 'responses' in method_data: - for response, response_data in six.iteritems(method_data['responses']): + if "responses" in method_data: + for response, response_data in six.iteritems(method_data["responses"]): httpStatus = str(response) # future lint: disable=blacklisted-function - method_response = self._parse_method_response(method_name.lower(), - _Swagger.SwaggerMethodResponse(response_data), httpStatus) + method_response = self._parse_method_response( + method_name.lower(), + _Swagger.SwaggerMethodResponse(response_data), + httpStatus, + ) - mr = __salt__['boto_apigateway.create_api_method_response']( + mr = __salt__["boto_apigateway.create_api_method_response"]( restApiId=self.restApiId, resourcePath=resource_path, httpMethod=method_name.upper(), statusCode=httpStatus, - responseParameters=method_response.get('params'), - responseModels=method_response.get('models'), - **self._common_aws_args) - if not mr.get('created'): + responseParameters=method_response.get("params"), + responseModels=method_response.get("models"), + **self._common_aws_args + ) + if not mr.get("created"): ret = _log_error_and_abort(ret, mr) return ret - ret = _log_changes(ret, '_deploy_method.create_api_method_response', mr) + ret = _log_changes(ret, "_deploy_method.create_api_method_response", mr) - mir = __salt__['boto_apigateway.create_api_integration_response']( + mir = __salt__["boto_apigateway.create_api_integration_response"]( restApiId=self.restApiId, resourcePath=resource_path, httpMethod=method_name.upper(), statusCode=httpStatus, - selectionPattern=method_response.get('pattern'), - responseParameters=method_response.get('integration_params'), - responseTemplates=method_response.get('response_templates'), - **self._common_aws_args) - if not mir.get('created'): + selectionPattern=method_response.get("pattern"), + responseParameters=method_response.get("integration_params"), + responseTemplates=method_response.get("response_templates"), + **self._common_aws_args + ) + if not mir.get("created"): ret = _log_error_and_abort(ret, mir) return ret - ret = _log_changes(ret, '_deploy_method.create_api_integration_response', mir) + ret = _log_changes( + ret, "_deploy_method.create_api_integration_response", mir + ) else: - raise ValueError('No responses specified for {0} {1}'.format(resource_path, method_name)) + raise ValueError( + "No responses specified for {0} {1}".format(resource_path, method_name) + ) return ret - def deploy_resources(self, ret, api_key_required, lambda_integration_role, lambda_region, authorization_type): - ''' + def deploy_resources( + self, + ret, + api_key_required, + lambda_integration_role, + lambda_region, + authorization_type, + ): + """ Method to deploy resources defined in the swagger file. ret @@ -1668,26 +1961,43 @@ class _Swagger(object): authorization_type 'NONE' or 'AWS_IAM' - ''' + """ for path, pathData in self.paths: - resource = __salt__['boto_apigateway.create_api_resources'](restApiId=self.restApiId, - path=path, - **self._common_aws_args) - if not resource.get('created'): + resource = __salt__["boto_apigateway.create_api_resources"]( + restApiId=self.restApiId, path=path, **self._common_aws_args + ) + if not resource.get("created"): ret = _log_error_and_abort(ret, resource) return ret - ret = _log_changes(ret, 'deploy_resources', resource) + ret = _log_changes(ret, "deploy_resources", resource) for method, method_data in six.iteritems(pathData): if method in _Swagger.SWAGGER_OPERATION_NAMES: - ret = self._deploy_method(ret, path, method, method_data, api_key_required, - lambda_integration_role, lambda_region, authorization_type) + ret = self._deploy_method( + ret, + path, + method, + method_data, + api_key_required, + lambda_integration_role, + lambda_region, + authorization_type, + ) return ret -def usage_plan_present(name, plan_name, description=None, throttle=None, quota=None, region=None, key=None, keyid=None, - profile=None): - ''' +def usage_plan_present( + name, + plan_name, + description=None, + throttle=None, + quota=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the spcifieda usage plan with the corresponding metrics is deployed .. versionadded:: 2017.7.0 @@ -1735,99 +2045,114 @@ def usage_plan_present(name, plan_name, description=None, throttle=None, quota=N period: DAY - profile: my_profile - ''' + """ func_params = locals() - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} try: - common_args = dict([('region', region), - ('key', key), - ('keyid', keyid), - ('profile', profile)]) + common_args = dict( + [("region", region), ("key", key), ("keyid", keyid), ("profile", profile)] + ) - existing = __salt__['boto_apigateway.describe_usage_plans'](name=plan_name, **common_args) - if 'error' in existing: - ret['result'] = False - ret['comment'] = 'Failed to describe existing usage plans' + existing = __salt__["boto_apigateway.describe_usage_plans"]( + name=plan_name, **common_args + ) + if "error" in existing: + ret["result"] = False + ret["comment"] = "Failed to describe existing usage plans" return ret - if not existing['plans']: + if not existing["plans"]: # plan does not exist, we need to create it - if __opts__['test']: - ret['comment'] = 'a new usage plan {0} would be created'.format(plan_name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "a new usage plan {0} would be created".format( + plan_name + ) + ret["result"] = None return ret - result = __salt__['boto_apigateway.create_usage_plan'](name=plan_name, - description=description, - throttle=throttle, - quota=quota, - **common_args) - if 'error' in result: - ret['result'] = False - ret['comment'] = 'Failed to create a usage plan {0}, {1}'.format(plan_name, result['error']) + result = __salt__["boto_apigateway.create_usage_plan"]( + name=plan_name, + description=description, + throttle=throttle, + quota=quota, + **common_args + ) + if "error" in result: + ret["result"] = False + ret["comment"] = "Failed to create a usage plan {0}, {1}".format( + plan_name, result["error"] + ) return ret - ret['changes']['old'] = {'plan': None} - ret['comment'] = 'A new usage plan {0} has been created'.format(plan_name) + ret["changes"]["old"] = {"plan": None} + ret["comment"] = "A new usage plan {0} has been created".format(plan_name) else: # need an existing plan modified to match given value - plan = existing['plans'][0] + plan = existing["plans"][0] needs_updating = False - modifiable_params = (('throttle', ('rateLimit', 'burstLimit')), ('quota', ('limit', 'offset', 'period'))) + modifiable_params = ( + ("throttle", ("rateLimit", "burstLimit")), + ("quota", ("limit", "offset", "period")), + ) for p, fields in modifiable_params: for f in fields: - actual_param = {} if func_params.get(p) is None else func_params.get(p) + actual_param = ( + {} if func_params.get(p) is None else func_params.get(p) + ) if plan.get(p, {}).get(f, None) != actual_param.get(f, None): needs_updating = True break if not needs_updating: - ret['comment'] = 'usage plan {0} is already in a correct state'.format(plan_name) - ret['result'] = True + ret["comment"] = "usage plan {0} is already in a correct state".format( + plan_name + ) + ret["result"] = True return ret - if __opts__['test']: - ret['comment'] = 'a new usage plan {0} would be updated'.format(plan_name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "a new usage plan {0} would be updated".format( + plan_name + ) + ret["result"] = None return ret - result = __salt__['boto_apigateway.update_usage_plan'](plan['id'], - throttle=throttle, - quota=quota, - **common_args) - if 'error' in result: - ret['result'] = False - ret['comment'] = 'Failed to update a usage plan {0}, {1}'.format(plan_name, result['error']) + result = __salt__["boto_apigateway.update_usage_plan"]( + plan["id"], throttle=throttle, quota=quota, **common_args + ) + if "error" in result: + ret["result"] = False + ret["comment"] = "Failed to update a usage plan {0}, {1}".format( + plan_name, result["error"] + ) return ret - ret['changes']['old'] = {'plan': plan} - ret['comment'] = 'usage plan {0} has been updated'.format(plan_name) + ret["changes"]["old"] = {"plan": plan} + ret["comment"] = "usage plan {0} has been updated".format(plan_name) - newstate = __salt__['boto_apigateway.describe_usage_plans'](name=plan_name, **common_args) - if 'error' in existing: - ret['result'] = False - ret['comment'] = 'Failed to describe existing usage plans after updates' + newstate = __salt__["boto_apigateway.describe_usage_plans"]( + name=plan_name, **common_args + ) + if "error" in existing: + ret["result"] = False + ret["comment"] = "Failed to describe existing usage plans after updates" return ret - ret['changes']['new'] = {'plan': newstate['plans'][0]} + ret["changes"]["new"] = {"plan": newstate["plans"][0]} except (ValueError, IOError) as e: - ret['result'] = False - ret['comment'] = '{0}'.format(e.args) + ret["result"] = False + ret["comment"] = "{0}".format(e.args) return ret def usage_plan_absent(name, plan_name, region=None, key=None, keyid=None, profile=None): - ''' + """ Ensures usage plan identified by name is no longer present .. versionadded:: 2017.7.0 @@ -1845,55 +2170,58 @@ def usage_plan_absent(name, plan_name, region=None, key=None, keyid=None, profil - plan_name: my_usage_plan - profile: my_profile - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} try: - common_args = dict([('region', region), - ('key', key), - ('keyid', keyid), - ('profile', profile)]) + common_args = dict( + [("region", region), ("key", key), ("keyid", keyid), ("profile", profile)] + ) - existing = __salt__['boto_apigateway.describe_usage_plans'](name=plan_name, **common_args) - if 'error' in existing: - ret['result'] = False - ret['comment'] = 'Failed to describe existing usage plans' + existing = __salt__["boto_apigateway.describe_usage_plans"]( + name=plan_name, **common_args + ) + if "error" in existing: + ret["result"] = False + ret["comment"] = "Failed to describe existing usage plans" return ret - if not existing['plans']: - ret['comment'] = 'Usage plan {0} does not exist already'.format(plan_name) + if not existing["plans"]: + ret["comment"] = "Usage plan {0} does not exist already".format(plan_name) return ret - if __opts__['test']: - ret['comment'] = 'Usage plan {0} exists and would be deleted'.format(plan_name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Usage plan {0} exists and would be deleted".format( + plan_name + ) + ret["result"] = None return ret - plan_id = existing['plans'][0]['id'] - result = __salt__['boto_apigateway.delete_usage_plan'](plan_id, **common_args) + plan_id = existing["plans"][0]["id"] + result = __salt__["boto_apigateway.delete_usage_plan"](plan_id, **common_args) - if 'error' in result: - ret['result'] = False - ret['comment'] = 'Failed to delete usage plan {0}, {1}'.format(plan_name, result) + if "error" in result: + ret["result"] = False + ret["comment"] = "Failed to delete usage plan {0}, {1}".format( + plan_name, result + ) return ret - ret['comment'] = 'Usage plan {0} has been deleted'.format(plan_name) - ret['changes']['old'] = {'plan': existing['plans'][0]} - ret['changes']['new'] = {'plan': None} + ret["comment"] = "Usage plan {0} has been deleted".format(plan_name) + ret["changes"]["old"] = {"plan": existing["plans"][0]} + ret["changes"]["new"] = {"plan": None} except (ValueError, IOError) as e: - ret['result'] = False - ret['comment'] = '{0}'.format(e.args) + ret["result"] = False + ret["comment"] = "{0}".format(e.args) return ret -def usage_plan_association_present(name, plan_name, api_stages, region=None, key=None, keyid=None, profile=None): - ''' +def usage_plan_association_present( + name, plan_name, api_stages, region=None, key=None, keyid=None, profile=None +): + """ Ensures usage plan identified by name is added to provided api_stages .. versionadded:: 2017.7.0 @@ -1925,37 +2253,36 @@ def usage_plan_association_present(name, plan_name, api_stages, region=None, key stage: my_stage - profile: my_profile - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} try: - common_args = dict([('region', region), - ('key', key), - ('keyid', keyid), - ('profile', profile)]) + common_args = dict( + [("region", region), ("key", key), ("keyid", keyid), ("profile", profile)] + ) - existing = __salt__['boto_apigateway.describe_usage_plans'](name=plan_name, **common_args) - if 'error' in existing: - ret['result'] = False - ret['comment'] = 'Failed to describe existing usage plans' + existing = __salt__["boto_apigateway.describe_usage_plans"]( + name=plan_name, **common_args + ) + if "error" in existing: + ret["result"] = False + ret["comment"] = "Failed to describe existing usage plans" return ret - if not existing['plans']: - ret['comment'] = 'Usage plan {0} does not exist'.format(plan_name) - ret['result'] = False + if not existing["plans"]: + ret["comment"] = "Usage plan {0} does not exist".format(plan_name) + ret["result"] = False return ret - if len(existing['plans']) != 1: - ret['comment'] = 'There are multiple usage plans with the same name - it is not supported' - ret['result'] = False + if len(existing["plans"]) != 1: + ret[ + "comment" + ] = "There are multiple usage plans with the same name - it is not supported" + ret["result"] = False return ret - plan = existing['plans'][0] - plan_id = plan['id'] - plan_stages = plan.get('apiStages', []) + plan = existing["plans"][0] + plan_id = plan["id"] + plan_stages = plan.get("apiStages", []) stages_to_add = [] for api in api_stages: @@ -1963,30 +2290,36 @@ def usage_plan_association_present(name, plan_name, api_stages, region=None, key stages_to_add.append(api) if not stages_to_add: - ret['comment'] = 'Usage plan is already asssociated to all api stages' + ret["comment"] = "Usage plan is already asssociated to all api stages" return ret - result = __salt__['boto_apigateway.attach_usage_plan_to_apis'](plan_id, stages_to_add, **common_args) - if 'error' in result: - ret['comment'] = 'Failed to associate a usage plan {0} to the apis {1}, {2}'.format(plan_name, - stages_to_add, - result['error']) - ret['result'] = False + result = __salt__["boto_apigateway.attach_usage_plan_to_apis"]( + plan_id, stages_to_add, **common_args + ) + if "error" in result: + ret[ + "comment" + ] = "Failed to associate a usage plan {0} to the apis {1}, {2}".format( + plan_name, stages_to_add, result["error"] + ) + ret["result"] = False return ret - ret['comment'] = 'successfully associated usage plan to apis' - ret['changes']['old'] = plan_stages - ret['changes']['new'] = result.get('result', {}).get('apiStages', []) + ret["comment"] = "successfully associated usage plan to apis" + ret["changes"]["old"] = plan_stages + ret["changes"]["new"] = result.get("result", {}).get("apiStages", []) except (ValueError, IOError) as e: - ret['result'] = False - ret['comment'] = '{0}'.format(e.args) + ret["result"] = False + ret["comment"] = "{0}".format(e.args) return ret -def usage_plan_association_absent(name, plan_name, api_stages, region=None, key=None, keyid=None, profile=None): - ''' +def usage_plan_association_absent( + name, plan_name, api_stages, region=None, key=None, keyid=None, profile=None +): + """ Ensures usage plan identified by name is removed from provided api_stages If a plan is associated to stages not listed in api_stages parameter, those associations remain intact. @@ -2020,40 +2353,41 @@ def usage_plan_association_absent(name, plan_name, api_stages, region=None, key= stage: my_stage - profile: my_profile - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} try: - common_args = dict([('region', region), - ('key', key), - ('keyid', keyid), - ('profile', profile)]) + common_args = dict( + [("region", region), ("key", key), ("keyid", keyid), ("profile", profile)] + ) - existing = __salt__['boto_apigateway.describe_usage_plans'](name=plan_name, **common_args) - if 'error' in existing: - ret['result'] = False - ret['comment'] = 'Failed to describe existing usage plans' + existing = __salt__["boto_apigateway.describe_usage_plans"]( + name=plan_name, **common_args + ) + if "error" in existing: + ret["result"] = False + ret["comment"] = "Failed to describe existing usage plans" return ret - if not existing['plans']: - ret['comment'] = 'Usage plan {0} does not exist'.format(plan_name) - ret['result'] = False + if not existing["plans"]: + ret["comment"] = "Usage plan {0} does not exist".format(plan_name) + ret["result"] = False return ret - if len(existing['plans']) != 1: - ret['comment'] = 'There are multiple usage plans with the same name - it is not supported' - ret['result'] = False + if len(existing["plans"]) != 1: + ret[ + "comment" + ] = "There are multiple usage plans with the same name - it is not supported" + ret["result"] = False return ret - plan = existing['plans'][0] - plan_id = plan['id'] - plan_stages = plan.get('apiStages', []) + plan = existing["plans"][0] + plan_id = plan["id"] + plan_stages = plan.get("apiStages", []) if not plan_stages: - ret['comment'] = 'Usage plan {0} has no associated stages already'.format(plan_name) + ret["comment"] = "Usage plan {0} has no associated stages already".format( + plan_name + ) return ret stages_to_remove = [] @@ -2062,23 +2396,27 @@ def usage_plan_association_absent(name, plan_name, api_stages, region=None, key= stages_to_remove.append(api) if not stages_to_remove: - ret['comment'] = 'Usage plan is already not asssociated to any api stages' + ret["comment"] = "Usage plan is already not asssociated to any api stages" return ret - result = __salt__['boto_apigateway.detach_usage_plan_from_apis'](plan_id, stages_to_remove, **common_args) - if 'error' in result: - ret['comment'] = 'Failed to disassociate a usage plan {0} from the apis {1}, {2}'.format(plan_name, - stages_to_remove, - result['error']) - ret['result'] = False + result = __salt__["boto_apigateway.detach_usage_plan_from_apis"]( + plan_id, stages_to_remove, **common_args + ) + if "error" in result: + ret[ + "comment" + ] = "Failed to disassociate a usage plan {0} from the apis {1}, {2}".format( + plan_name, stages_to_remove, result["error"] + ) + ret["result"] = False return ret - ret['comment'] = 'successfully disassociated usage plan from apis' - ret['changes']['old'] = plan_stages - ret['changes']['new'] = result.get('result', {}).get('apiStages', []) + ret["comment"] = "successfully disassociated usage plan from apis" + ret["changes"]["old"] = plan_stages + ret["changes"]["new"] = result.get("result", {}).get("apiStages", []) except (ValueError, IOError) as e: - ret['result'] = False - ret['comment'] = '{0}'.format(e.args) + ret["result"] = False + ret["comment"] = "{0}".format(e.args) return ret diff --git a/salt/states/boto_asg.py b/salt/states/boto_asg.py index 3530f80bbe7..140fcc75522 100644 --- a/salt/states/boto_asg.py +++ b/salt/states/boto_asg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Autoscale Groups ======================= @@ -190,64 +190,68 @@ Overriding the alarm values on the resource: CPU: attributes: threshold: 50.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import copy import hashlib import logging -import copy # Import Salt libs import salt.utils.dictupdate as dictupdate import salt.utils.stringutils -from salt.ext import six from salt.exceptions import SaltInvocationError +from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_asg' if 'boto_asg.exists' in __salt__ else False + """ + if "boto_asg.exists" in __salt__: + return "boto_asg" + return (False, "boto_asg module could not be loaded") def present( - name, - launch_config_name, - availability_zones, - min_size, - max_size, - launch_config=None, - desired_capacity=None, - load_balancers=None, - default_cooldown=None, - health_check_type=None, - health_check_period=None, - placement_group=None, - vpc_zone_identifier=None, - subnet_names=None, - tags=None, - termination_policies=None, - termination_policies_from_pillar='boto_asg_termination_policies', - suspended_processes=None, - scaling_policies=None, - scaling_policies_from_pillar='boto_asg_scaling_policies', - scheduled_actions=None, - scheduled_actions_from_pillar='boto_asg_scheduled_actions', - alarms=None, - alarms_from_pillar='boto_asg_alarms', - region=None, - key=None, - keyid=None, - profile=None, - notification_arn=None, - notification_arn_from_pillar='boto_asg_notification_arn', - notification_types=None, - notification_types_from_pillar='boto_asg_notification_types'): - ''' + name, + launch_config_name, + availability_zones, + min_size, + max_size, + launch_config=None, + desired_capacity=None, + load_balancers=None, + default_cooldown=None, + health_check_type=None, + health_check_period=None, + placement_group=None, + vpc_zone_identifier=None, + subnet_names=None, + tags=None, + termination_policies=None, + termination_policies_from_pillar="boto_asg_termination_policies", + suspended_processes=None, + scaling_policies=None, + scaling_policies_from_pillar="boto_asg_scaling_policies", + scheduled_actions=None, + scheduled_actions_from_pillar="boto_asg_scheduled_actions", + alarms=None, + alarms_from_pillar="boto_asg_alarms", + region=None, + key=None, + keyid=None, + profile=None, + notification_arn=None, + notification_arn_from_pillar="boto_asg_notification_arn", + notification_types=None, + notification_types_from_pillar="boto_asg_notification_types", +): + """ Ensure the autoscale group exists. name @@ -434,189 +438,208 @@ def present( name of the pillar dict that contains ``notifcation_types`` settings. ``notification_types`` defined for this specific state will override those from the pillar. - ''' + """ if vpc_zone_identifier and subnet_names: - raise SaltInvocationError('vpc_zone_identifier and subnet_names are ' - 'mutually exclusive options.') - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + raise SaltInvocationError( + "vpc_zone_identifier and subnet_names are " "mutually exclusive options." + ) + ret = {"name": name, "result": True, "comment": "", "changes": {}} if subnet_names: vpc_zone_identifier = [] for i in subnet_names: - r = __salt__['boto_vpc.get_resource_id']('subnet', name=i, region=region, - key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['comment'] = 'Error looking up subnet ids: {0}'.format(r['error']) - ret['result'] = False + r = __salt__["boto_vpc.get_resource_id"]( + "subnet", name=i, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["comment"] = "Error looking up subnet ids: {0}".format(r["error"]) + ret["result"] = False return ret - if 'id' not in r: - ret['comment'] = 'Subnet {0} does not exist.'.format(i) - ret['result'] = False + if "id" not in r: + ret["comment"] = "Subnet {0} does not exist.".format(i) + ret["result"] = False return ret - vpc_zone_identifier.append(r['id']) + vpc_zone_identifier.append(r["id"]) if vpc_zone_identifier: - vpc_id = __salt__['boto_vpc.get_subnet_association']( - vpc_zone_identifier, - region, - key, - keyid, - profile + vpc_id = __salt__["boto_vpc.get_subnet_association"]( + vpc_zone_identifier, region, key, keyid, profile ) - vpc_id = vpc_id.get('vpc_id') - log.debug('Auto Scaling Group %s is associated with VPC ID %s', - name, vpc_id) + vpc_id = vpc_id.get("vpc_id") + log.debug("Auto Scaling Group %s is associated with VPC ID %s", name, vpc_id) else: vpc_id = None - log.debug('Auto Scaling Group %s has no VPC Association', name) + log.debug("Auto Scaling Group %s has no VPC Association", name) # if launch_config is defined, manage the launch config first. # hash the launch_config dict to create a unique name suffix and then # ensure it is present if launch_config: - launch_config_bytes = salt.utils.stringutils.to_bytes(str(launch_config)) # future lint: disable=blacklisted-function - launch_config_name = launch_config_name + '-' + hashlib.md5(launch_config_bytes).hexdigest() + launch_config_bytes = salt.utils.stringutils.to_bytes( + str(launch_config) + ) # future lint: disable=blacklisted-function + launch_config_name = ( + launch_config_name + "-" + hashlib.md5(launch_config_bytes).hexdigest() + ) args = { - 'name': launch_config_name, - 'region': region, - 'key': key, - 'keyid': keyid, - 'profile': profile + "name": launch_config_name, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, } for index, item in enumerate(launch_config): - if 'image_name' in item: - image_name = item['image_name'] - iargs = {'ami_name': image_name, 'region': region, 'key': key, - 'keyid': keyid, 'profile': profile} - image_ids = __salt__['boto_ec2.find_images'](**iargs) + if "image_name" in item: + image_name = item["image_name"] + iargs = { + "ami_name": image_name, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } + image_ids = __salt__["boto_ec2.find_images"](**iargs) if image_ids: # find_images() returns False on failure - launch_config[index]['image_id'] = image_ids[0] + launch_config[index]["image_id"] = image_ids[0] else: - log.warning("Couldn't find AMI named `%s`, passing literally.", image_name) - launch_config[index]['image_id'] = image_name - del launch_config[index]['image_name'] + log.warning( + "Couldn't find AMI named `%s`, passing literally.", image_name + ) + launch_config[index]["image_id"] = image_name + del launch_config[index]["image_name"] break if vpc_id: - log.debug('Auto Scaling Group {0} is a associated with a vpc') + log.debug("Auto Scaling Group {0} is a associated with a vpc") # locate the security groups attribute of a launch config sg_index = None for index, item in enumerate(launch_config): - if 'security_groups' in item: + if "security_groups" in item: sg_index = index break # if security groups exist within launch_config then convert # to group ids if sg_index is not None: - log.debug('security group associations found in launch config') - _group_ids = __salt__['boto_secgroup.convert_to_group_ids']( - launch_config[sg_index]['security_groups'], vpc_id=vpc_id, - region=region, key=key, keyid=keyid, profile=profile + log.debug("security group associations found in launch config") + _group_ids = __salt__["boto_secgroup.convert_to_group_ids"]( + launch_config[sg_index]["security_groups"], + vpc_id=vpc_id, + region=region, + key=key, + keyid=keyid, + profile=profile, ) - launch_config[sg_index]['security_groups'] = _group_ids + launch_config[sg_index]["security_groups"] = _group_ids for d in launch_config: args.update(d) - if not __opts__['test']: - lc_ret = __states__['boto_lc.present'](**args) - if lc_ret['result'] is True and lc_ret['changes']: - if 'launch_config' not in ret['changes']: - ret['changes']['launch_config'] = {} - ret['changes']['launch_config'] = lc_ret['changes'] + if not __opts__["test"]: + lc_ret = __states__["boto_lc.present"](**args) + if lc_ret["result"] is True and lc_ret["changes"]: + if "launch_config" not in ret["changes"]: + ret["changes"]["launch_config"] = {} + ret["changes"]["launch_config"] = lc_ret["changes"] - asg = __salt__['boto_asg.get_config'](name, region, key, keyid, profile) + asg = __salt__["boto_asg.get_config"](name, region, key, keyid, profile) termination_policies = _determine_termination_policies( - termination_policies, - termination_policies_from_pillar + termination_policies, termination_policies_from_pillar ) scaling_policies = _determine_scaling_policies( - scaling_policies, - scaling_policies_from_pillar + scaling_policies, scaling_policies_from_pillar ) scheduled_actions = _determine_scheduled_actions( - scheduled_actions, - scheduled_actions_from_pillar + scheduled_actions, scheduled_actions_from_pillar ) if asg is None: - ret['result'] = False - ret['comment'] = 'Failed to check autoscale group existence.' + ret["result"] = False + ret["comment"] = "Failed to check autoscale group existence." elif not asg: - if __opts__['test']: - msg = 'Autoscale group set to be created.' - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Autoscale group set to be created." + ret["comment"] = msg + ret["result"] = None return ret notification_arn, notification_types = _determine_notification_info( notification_arn, notification_arn_from_pillar, notification_types, - notification_types_from_pillar + notification_types_from_pillar, + ) + created = __salt__["boto_asg.create"]( + name, + launch_config_name, + availability_zones, + min_size, + max_size, + desired_capacity, + load_balancers, + default_cooldown, + health_check_type, + health_check_period, + placement_group, + vpc_zone_identifier, + tags, + termination_policies, + suspended_processes, + scaling_policies, + scheduled_actions, + region, + notification_arn, + notification_types, + key, + keyid, + profile, ) - created = __salt__['boto_asg.create'](name, launch_config_name, - availability_zones, min_size, - max_size, desired_capacity, - load_balancers, default_cooldown, - health_check_type, - health_check_period, - placement_group, - vpc_zone_identifier, tags, - termination_policies, - suspended_processes, - scaling_policies, scheduled_actions, - region, notification_arn, - notification_types, - key, keyid, profile) if created: - ret['changes']['old'] = None - asg = __salt__['boto_asg.get_config'](name, region, key, keyid, - profile) - ret['changes']['new'] = asg + ret["changes"]["old"] = None + asg = __salt__["boto_asg.get_config"](name, region, key, keyid, profile) + ret["changes"]["new"] = asg else: - ret['result'] = False - ret['comment'] = 'Failed to create autoscale group' + ret["result"] = False + ret["comment"] = "Failed to create autoscale group" else: need_update = False # If any of these attributes can't be modified after creation # time, we should remove them from the dict. if scaling_policies: for policy in scaling_policies: - if 'min_adjustment_step' not in policy: - policy['min_adjustment_step'] = None + if "min_adjustment_step" not in policy: + policy["min_adjustment_step"] = None if scheduled_actions: for s_name, action in six.iteritems(scheduled_actions): - if 'end_time' not in action: - action['end_time'] = None + if "end_time" not in action: + action["end_time"] = None config = { - 'launch_config_name': launch_config_name, - 'availability_zones': availability_zones, - 'min_size': min_size, - 'max_size': max_size, - 'desired_capacity': desired_capacity, - 'default_cooldown': default_cooldown, - 'health_check_type': health_check_type, - 'health_check_period': health_check_period, - 'vpc_zone_identifier': vpc_zone_identifier, - 'tags': tags, - 'termination_policies': termination_policies, - 'suspended_processes': suspended_processes, - 'scaling_policies': scaling_policies, - 'scheduled_actions': scheduled_actions + "launch_config_name": launch_config_name, + "availability_zones": availability_zones, + "min_size": min_size, + "max_size": max_size, + "desired_capacity": desired_capacity, + "default_cooldown": default_cooldown, + "health_check_type": health_check_type, + "health_check_period": health_check_period, + "vpc_zone_identifier": vpc_zone_identifier, + "tags": tags, + "termination_policies": termination_policies, + "suspended_processes": suspended_processes, + "scaling_policies": scaling_policies, + "scheduled_actions": scheduled_actions, } - #ensure that we reset termination_policies to default if none are specified + # ensure that we reset termination_policies to default if none are specified if not termination_policies: - config['termination_policies'] = ['Default'] + config["termination_policies"] = ["Default"] if suspended_processes is None: - config['suspended_processes'] = [] + config["suspended_processes"] = [] # ensure that we delete scaling_policies if none are specified if scaling_policies is None: - config['scaling_policies'] = [] + config["scaling_policies"] = [] # ensure that we delete scheduled_actions if none are specified if scheduled_actions is None: - config['scheduled_actions'] = {} + config["scheduled_actions"] = {} # allow defaults on start_time for s_name, action in six.iteritems(scheduled_actions): - if 'start_time' not in action: - asg_action = asg['scheduled_actions'].get(s_name, {}) - if 'start_time' in asg_action: - del asg_action['start_time'] + if "start_time" not in action: + asg_action = asg["scheduled_actions"].get(s_name, {}) + if "start_time" in asg_action: + del asg_action["start_time"] proposed = {} # note: do not loop using "key, value" - this can modify the value of # the aws access key @@ -626,29 +649,29 @@ def present( # always be returned from AWS. if value is None: continue - value = __utils__['boto3.ordered'](value) + value = __utils__["boto3.ordered"](value) if asg_property in asg: - _value = __utils__['boto3.ordered'](asg[asg_property]) + _value = __utils__["boto3.ordered"](asg[asg_property]) if not value == _value: - log.debug('%s asg_property differs from %s', value, _value) - proposed.setdefault('old', {}).update({asg_property: _value}) - proposed.setdefault('new', {}).update({asg_property: value}) + log.debug("%s asg_property differs from %s", value, _value) + proposed.setdefault("old", {}).update({asg_property: _value}) + proposed.setdefault("new", {}).update({asg_property: value}) need_update = True if need_update: - if __opts__['test']: - msg = 'Autoscale group set to be updated.' - ret['comment'] = msg - ret['result'] = None - ret['changes'] = proposed + if __opts__["test"]: + msg = "Autoscale group set to be updated." + ret["comment"] = msg + ret["result"] = None + ret["changes"] = proposed return ret # add in alarms notification_arn, notification_types = _determine_notification_info( notification_arn, notification_arn_from_pillar, notification_types, - notification_types_from_pillar + notification_types_from_pillar, ) - updated, msg = __salt__['boto_asg.update']( + updated, msg = __salt__["boto_asg.update"]( name, launch_config_name, availability_zones, @@ -671,50 +694,59 @@ def present( notification_types=notification_types, key=key, keyid=keyid, - profile=profile + profile=profile, ) - if asg['launch_config_name'] != launch_config_name: + if asg["launch_config_name"] != launch_config_name: # delete the old launch_config_name - deleted = __salt__['boto_asg.delete_launch_configuration']( - asg['launch_config_name'], + deleted = __salt__["boto_asg.delete_launch_configuration"]( + asg["launch_config_name"], region=region, key=key, keyid=keyid, - profile=profile + profile=profile, ) if deleted: - if 'launch_config' not in ret['changes']: - ret['changes']['launch_config'] = {} - ret['changes']['launch_config']['deleted'] = asg['launch_config_name'] + if "launch_config" not in ret["changes"]: + ret["changes"]["launch_config"] = {} + ret["changes"]["launch_config"]["deleted"] = asg[ + "launch_config_name" + ] if updated: - ret['changes']['old'] = asg - asg = __salt__['boto_asg.get_config'](name, region, key, keyid, - profile) - ret['changes']['new'] = asg - ret['comment'] = 'Updated autoscale group.' + ret["changes"]["old"] = asg + asg = __salt__["boto_asg.get_config"](name, region, key, keyid, profile) + ret["changes"]["new"] = asg + ret["comment"] = "Updated autoscale group." else: - ret['result'] = False - ret['comment'] = msg + ret["result"] = False + ret["comment"] = msg else: - ret['comment'] = 'Autoscale group present.' + ret["comment"] = "Autoscale group present." # add in alarms _ret = _alarms_present( - name, min_size == max_size, alarms, alarms_from_pillar, region, key, - keyid, profile + name, + min_size == max_size, + alarms, + alarms_from_pillar, + region, + key, + keyid, + profile, ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret -def _determine_termination_policies(termination_policies, termination_policies_from_pillar): - ''' +def _determine_termination_policies( + termination_policies, termination_policies_from_pillar +): + """ helper method for present. ensure that termination_policies are set - ''' + """ pillar_termination_policies = copy.deepcopy( - __salt__['config.option'](termination_policies_from_pillar, []) + __salt__["config.option"](termination_policies_from_pillar, []) ) if not termination_policies and len(pillar_termination_policies) > 0: termination_policies = pillar_termination_policies @@ -722,11 +754,11 @@ def _determine_termination_policies(termination_policies, termination_policies_f def _determine_scaling_policies(scaling_policies, scaling_policies_from_pillar): - ''' + """ helper method for present. ensure that scaling_policies are set - ''' + """ pillar_scaling_policies = copy.deepcopy( - __salt__['config.option'](scaling_policies_from_pillar, {}) + __salt__["config.option"](scaling_policies_from_pillar, {}) ) if not scaling_policies and len(pillar_scaling_policies) > 0: scaling_policies = pillar_scaling_policies @@ -734,100 +766,106 @@ def _determine_scaling_policies(scaling_policies, scaling_policies_from_pillar): def _determine_scheduled_actions(scheduled_actions, scheduled_actions_from_pillar): - ''' + """ helper method for present, ensure scheduled actions are setup - ''' - tmp = copy.deepcopy( - __salt__['config.option'](scheduled_actions_from_pillar, {}) - ) + """ + tmp = copy.deepcopy(__salt__["config.option"](scheduled_actions_from_pillar, {})) # merge with data from state if scheduled_actions: tmp = dictupdate.update(tmp, scheduled_actions) return tmp -def _determine_notification_info(notification_arn, - notification_arn_from_pillar, - notification_types, - notification_types_from_pillar): - ''' +def _determine_notification_info( + notification_arn, + notification_arn_from_pillar, + notification_types, + notification_types_from_pillar, +): + """ helper method for present. ensure that notification_configs are set - ''' + """ pillar_arn_list = copy.deepcopy( - __salt__['config.option'](notification_arn_from_pillar, {}) + __salt__["config.option"](notification_arn_from_pillar, {}) ) pillar_arn = None if len(pillar_arn_list) > 0: pillar_arn = pillar_arn_list[0] pillar_notification_types = copy.deepcopy( - __salt__['config.option'](notification_types_from_pillar, {}) + __salt__["config.option"](notification_types_from_pillar, {}) ) arn = notification_arn if notification_arn else pillar_arn types = notification_types if notification_types else pillar_notification_types return (arn, types) -def _alarms_present(name, min_size_equals_max_size, alarms, alarms_from_pillar, region, key, keyid, profile): - ''' +def _alarms_present( + name, + min_size_equals_max_size, + alarms, + alarms_from_pillar, + region, + key, + keyid, + profile, +): + """ helper method for present. ensure that cloudwatch_alarms are set - ''' + """ # load data from alarms_from_pillar - tmp = copy.deepcopy(__salt__['config.option'](alarms_from_pillar, {})) + tmp = copy.deepcopy(__salt__["config.option"](alarms_from_pillar, {})) # merge with data from alarms if alarms: tmp = dictupdate.update(tmp, alarms) # set alarms, using boto_cloudwatch_alarm.present - merged_return_value = {'name': name, 'result': True, 'comment': '', 'changes': {}} + merged_return_value = {"name": name, "result": True, "comment": "", "changes": {}} for _, info in six.iteritems(tmp): # add asg to name and description - info['name'] = name + ' ' + info['name'] - info['attributes']['description'] = name + ' ' + info['attributes']['description'] + info["name"] = name + " " + info["name"] + info["attributes"]["description"] = ( + name + " " + info["attributes"]["description"] + ) # add dimension attribute - if 'dimensions' not in info['attributes']: - info['attributes']['dimensions'] = {'AutoScalingGroupName': [name]} + if "dimensions" not in info["attributes"]: + info["attributes"]["dimensions"] = {"AutoScalingGroupName": [name]} scaling_policy_actions_only = True # replace ":self:" with our name - for action_type in ['alarm_actions', 'insufficient_data_actions', 'ok_actions']: - if action_type in info['attributes']: + for action_type in ["alarm_actions", "insufficient_data_actions", "ok_actions"]: + if action_type in info["attributes"]: new_actions = [] - for action in info['attributes'][action_type]: - if 'scaling_policy' not in action: + for action in info["attributes"][action_type]: + if "scaling_policy" not in action: scaling_policy_actions_only = False - if ':self:' in action: - action = action.replace(':self:', ':{0}:'.format(name)) + if ":self:" in action: + action = action.replace(":self:", ":{0}:".format(name)) new_actions.append(action) - info['attributes'][action_type] = new_actions + info["attributes"][action_type] = new_actions # skip alarms that only have actions for scaling policy, if min_size == max_size for this ASG if scaling_policy_actions_only and min_size_equals_max_size: continue # set alarm kwargs = { - 'name': info['name'], - 'attributes': info['attributes'], - 'region': region, - 'key': key, - 'keyid': keyid, - 'profile': profile, + "name": info["name"], + "attributes": info["attributes"], + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, } - results = __states__['boto_cloudwatch_alarm.present'](**kwargs) - if not results['result']: - merged_return_value['result'] = False - if results.get('changes', {}) != {}: - merged_return_value['changes'][info['name']] = results['changes'] - if 'comment' in results: - merged_return_value['comment'] += results['comment'] + results = __states__["boto_cloudwatch_alarm.present"](**kwargs) + if not results["result"]: + merged_return_value["result"] = False + if results.get("changes", {}) != {}: + merged_return_value["changes"][info["name"]] = results["changes"] + if "comment" in results: + merged_return_value["comment"] += results["comment"] return merged_return_value def absent( - name, - force=False, - region=None, - key=None, - keyid=None, - profile=None, - remove_lc=False): - ''' + name, force=False, region=None, key=None, keyid=None, profile=None, remove_lc=False +): + """ Ensure the named autoscale group is deleted. name @@ -851,42 +889,45 @@ def absent( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - asg = __salt__['boto_asg.get_config'](name, region, key, keyid, profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + asg = __salt__["boto_asg.get_config"](name, region, key, keyid, profile) if asg is None: - ret['result'] = False - ret['comment'] = 'Failed to check autoscale group existence.' + ret["result"] = False + ret["comment"] = "Failed to check autoscale group existence." elif asg: - if __opts__['test']: - ret['comment'] = 'Autoscale group set to be deleted.' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Autoscale group set to be deleted." + ret["result"] = None if remove_lc: - msg = 'Launch configuration {0} is set to be deleted.'.format(asg['launch_config_name']) - ret['comment'] = ' '.join([ret['comment'], msg]) + msg = "Launch configuration {0} is set to be deleted.".format( + asg["launch_config_name"] + ) + ret["comment"] = " ".join([ret["comment"], msg]) return ret - deleted = __salt__['boto_asg.delete'](name, force, region, key, keyid, - profile) + deleted = __salt__["boto_asg.delete"](name, force, region, key, keyid, profile) if deleted: if remove_lc: - lc_deleted = __salt__['boto_asg.delete_launch_configuration'](asg['launch_config_name'], - region, - key, - keyid, - profile) + lc_deleted = __salt__["boto_asg.delete_launch_configuration"]( + asg["launch_config_name"], region, key, keyid, profile + ) if lc_deleted: - if 'launch_config' not in ret['changes']: - ret['changes']['launch_config'] = {} - ret['changes']['launch_config']['deleted'] = asg['launch_config_name'] + if "launch_config" not in ret["changes"]: + ret["changes"]["launch_config"] = {} + ret["changes"]["launch_config"]["deleted"] = asg[ + "launch_config_name" + ] else: - ret['result'] = False - ret['comment'] = ' '.join([ret['comment'], 'Failed to delete launch configuration.']) - ret['changes']['old'] = asg - ret['changes']['new'] = None - ret['comment'] = 'Deleted autoscale group.' + ret["result"] = False + ret["comment"] = " ".join( + [ret["comment"], "Failed to delete launch configuration."] + ) + ret["changes"]["old"] = asg + ret["changes"]["new"] = None + ret["comment"] = "Deleted autoscale group." else: - ret['result'] = False - ret['comment'] = 'Failed to delete autoscale group.' + ret["result"] = False + ret["comment"] = "Failed to delete autoscale group." else: - ret['comment'] = 'Autoscale group does not exist.' + ret["comment"] = "Autoscale group does not exist." return ret diff --git a/salt/states/boto_cfn.py b/salt/states/boto_cfn.py index afd781292a7..e7343aa657a 100644 --- a/salt/states/boto_cfn.py +++ b/salt/states/boto_cfn.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection module for Amazon Cloud Formation .. versionadded:: 2015.8.0 @@ -35,10 +35,11 @@ Connection module for Amazon Cloud Formation stack-absent: boto_cfn.absent: - name: mystack -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -49,6 +50,7 @@ from salt.ext import six # Import 3rd-party libs try: from salt._compat import ElementTree as ET + HAS_ELEMENT_TREE = True except ImportError: HAS_ELEMENT_TREE = False @@ -56,27 +58,54 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'boto_cfn' +__virtualname__ = "boto_cfn" def __virtual__(): - ''' + """ Only load if elementtree xml library and boto are available. - ''' + """ if not HAS_ELEMENT_TREE: - return (False, 'Cannot load {0} state: ElementTree library unavailable'.format(__virtualname__)) + return ( + False, + "Cannot load {0} state: ElementTree library unavailable".format( + __virtualname__ + ), + ) - if 'boto_cfn.exists' in __salt__: + if "boto_cfn.exists" in __salt__: return True else: - return (False, 'Cannot load {0} state: boto_cfn module unavailable'.format(__virtualname__)) + return ( + False, + "Cannot load {0} state: boto_cfn module unavailable".format( + __virtualname__ + ), + ) -def present(name, template_body=None, template_url=None, parameters=None, notification_arns=None, disable_rollback=None, - timeout_in_minutes=None, capabilities=None, tags=None, on_failure=None, stack_policy_body=None, - stack_policy_url=None, use_previous_template=None, stack_policy_during_update_body=None, - stack_policy_during_update_url=None, region=None, key=None, keyid=None, profile=None): - ''' +def present( + name, + template_body=None, + template_url=None, + parameters=None, + notification_arns=None, + disable_rollback=None, + timeout_in_minutes=None, + capabilities=None, + tags=None, + on_failure=None, + stack_policy_body=None, + stack_policy_url=None, + use_previous_template=None, + stack_policy_during_update_body=None, + stack_policy_during_update_url=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure cloud formation stack is present. name (string) - Name of the stack. @@ -137,74 +166,112 @@ def present(name, template_body=None, template_url=None, parameters=None, notifi .. _`SNS_console`: https://console.aws.amazon.com/sns - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} template_body = _get_template(template_body, name) stack_policy_body = _get_template(stack_policy_body, name) - stack_policy_during_update_body = _get_template(stack_policy_during_update_body, name) + stack_policy_during_update_body = _get_template( + stack_policy_during_update_body, name + ) for i in [template_body, stack_policy_body, stack_policy_during_update_body]: if isinstance(i, dict): return i _valid = _validate(template_body, template_url, region, key, keyid, profile) - log.debug('Validate is : %s.', _valid) + log.debug("Validate is : %s.", _valid) if _valid is not True: code, message = _valid - ret['result'] = False - ret['comment'] = 'Template could not be validated.\n{0} \n{1}'.format(code, message) + ret["result"] = False + ret["comment"] = "Template could not be validated.\n{0} \n{1}".format( + code, message + ) return ret - log.debug('Template %s is valid.', name) - if __salt__['boto_cfn.exists'](name, region, key, keyid, profile): - template = __salt__['boto_cfn.get_template'](name, region, key, keyid, profile) - template = template['GetTemplateResponse']['GetTemplateResult']['TemplateBody'].encode('ascii', 'ignore') + log.debug("Template %s is valid.", name) + if __salt__["boto_cfn.exists"](name, region, key, keyid, profile): + template = __salt__["boto_cfn.get_template"](name, region, key, keyid, profile) + template = template["GetTemplateResponse"]["GetTemplateResult"][ + "TemplateBody" + ].encode("ascii", "ignore") template = salt.utils.json.loads(template) _template_body = salt.utils.json.loads(template_body) compare = salt.utils.compat.cmp(template, _template_body) if compare != 0: - log.debug('Templates are not the same. Compare value is %s', compare) + log.debug("Templates are not the same. Compare value is %s", compare) # At this point we should be able to run update safely since we already validated the template - if __opts__['test']: - ret['comment'] = 'Stack {0} is set to be updated.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Stack {0} is set to be updated.".format(name) + ret["result"] = None return ret - updated = __salt__['boto_cfn.update_stack'](name, template_body, template_url, parameters, - notification_arns, disable_rollback, timeout_in_minutes, - capabilities, tags, use_previous_template, - stack_policy_during_update_body, - stack_policy_during_update_url, stack_policy_body, - stack_policy_url, - region, key, keyid, profile) + updated = __salt__["boto_cfn.update_stack"]( + name, + template_body, + template_url, + parameters, + notification_arns, + disable_rollback, + timeout_in_minutes, + capabilities, + tags, + use_previous_template, + stack_policy_during_update_body, + stack_policy_during_update_url, + stack_policy_body, + stack_policy_url, + region, + key, + keyid, + profile, + ) if isinstance(updated, six.string_types): code, message = _get_error(updated) - log.debug('Update error is %s and message is %s', code, message) - ret['result'] = False - ret['comment'] = 'Stack {0} could not be updated.\n{1} \n{2}.'.format(name, code, message) + log.debug("Update error is %s and message is %s", code, message) + ret["result"] = False + ret["comment"] = "Stack {0} could not be updated.\n{1} \n{2}.".format( + name, code, message + ) return ret - ret['comment'] = 'Cloud formation template {0} has been updated.'.format(name) - ret['changes']['new'] = updated + ret["comment"] = "Cloud formation template {0} has been updated.".format( + name + ) + ret["changes"]["new"] = updated return ret - ret['comment'] = 'Stack {0} exists.'.format(name) - ret['changes'] = {} + ret["comment"] = "Stack {0} exists.".format(name) + ret["changes"] = {} return ret - if __opts__['test']: - ret['comment'] = 'Stack {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Stack {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_cfn.create'](name, template_body, template_url, parameters, notification_arns, - disable_rollback, timeout_in_minutes, capabilities, tags, on_failure, - stack_policy_body, stack_policy_url, region, key, keyid, profile) + created = __salt__["boto_cfn.create"]( + name, + template_body, + template_url, + parameters, + notification_arns, + disable_rollback, + timeout_in_minutes, + capabilities, + tags, + on_failure, + stack_policy_body, + stack_policy_url, + region, + key, + keyid, + profile, + ) if created: - ret['comment'] = 'Stack {0} was created.'.format(name) - ret['changes']['new'] = created + ret["comment"] = "Stack {0} was created.".format(name) + ret["changes"]["new"] = created return ret - ret['result'] = False + ret["result"] = False return ret def absent(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Ensure cloud formation stack is absent. name (string) – The name of the stack to delete. @@ -217,57 +284,68 @@ def absent(name, region=None, key=None, keyid=None, profile=None): profile (dict) - A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - if not __salt__['boto_cfn.exists'](name, region, key, keyid, profile): - ret['comment'] = 'Stack {0} does not exist.'.format(name) - ret['changes'] = {} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + if not __salt__["boto_cfn.exists"](name, region, key, keyid, profile): + ret["comment"] = "Stack {0} does not exist.".format(name) + ret["changes"] = {} return ret - if __opts__['test']: - ret['comment'] = 'Stack {0} is set to be deleted.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Stack {0} is set to be deleted.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_cfn.delete'](name, region, key, keyid, profile) + deleted = __salt__["boto_cfn.delete"](name, region, key, keyid, profile) if isinstance(deleted, six.string_types): code, message = _get_error(deleted) - ret['comment'] = 'Stack {0} could not be deleted.\n{1}\n{2}'.format(name, code, message) - ret['result'] = False - ret['changes'] = {} + ret["comment"] = "Stack {0} could not be deleted.\n{1}\n{2}".format( + name, code, message + ) + ret["result"] = False + ret["changes"] = {} return ret if deleted: - ret['comment'] = 'Stack {0} was deleted.'.format(name) - ret['changes']['deleted'] = name + ret["comment"] = "Stack {0} was deleted.".format(name) + ret["changes"]["deleted"] = name return ret def _get_template(template, name): # Checks if template is a file in salt defined by salt://. - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - if template is not None and 'salt://' in template: + ret = {"name": name, "result": True, "comment": "", "changes": {}} + if template is not None and "salt://" in template: try: - return __salt__['cp.get_file_str'](template) + return __salt__["cp.get_file_str"](template) except IOError as e: log.debug(e) - ret['comment'] = 'File {0} not found.'.format(template) - ret['result'] = False + ret["comment"] = "File {0} not found.".format(template) + ret["result"] = False return ret return template -def _validate(template_body=None, template_url=None, region=None, key=None, keyid=None, profile=None): +def _validate( + template_body=None, + template_url=None, + region=None, + key=None, + keyid=None, + profile=None, +): # Validates template. returns true if template syntax is correct. - validate = __salt__['boto_cfn.validate_template'](template_body, template_url, region, key, keyid, profile) - log.debug('Validate result is %s.', validate) + validate = __salt__["boto_cfn.validate_template"]( + template_body, template_url, region, key, keyid, profile + ) + log.debug("Validate result is %s.", validate) if isinstance(validate, six.string_types): code, message = _get_error(validate) - log.debug('Validate error is %s and message is %s.', code, message) + log.debug("Validate error is %s and message is %s.", code, message) return code, message return True def _get_error(error): # Converts boto exception to string that can be used to output error. - error = '\n'.join(error.split('\n')[1:]) + error = "\n".join(error.split("\n")[1:]) error = ET.fromstring(error) code = error[0][1].text message = error[0][2].text diff --git a/salt/states/boto_cloudfront.py b/salt/states/boto_cloudfront.py index d29d3df2359..c046b65d2d6 100644 --- a/salt/states/boto_cloudfront.py +++ b/salt/states/boto_cloudfront.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage CloudFront distributions .. versionadded:: 2018.3.0 @@ -42,10 +42,11 @@ either passed in as a dict, or a string to pull from pillars or minion config: region: us-east-1 :depends: boto3 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import difflib import logging @@ -53,25 +54,19 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - if 'boto_cloudfront.get_distribution' not in __salt__: - msg = 'The boto_cloudfront state module could not be loaded: {}.' - return (False, msg.format('boto_cloudfront exec module unavailable.')) - return 'boto_cloudfront' + """ + if "boto_cloudfront.get_distribution" not in __salt__: + msg = "The boto_cloudfront state module could not be loaded: {}." + return (False, msg.format("boto_cloudfront exec module unavailable.")) + return "boto_cloudfront" def present( - name, - config, - tags, - region=None, - key=None, - keyid=None, - profile=None, + name, config, tags, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Ensure the CloudFront distribution is present. name (string) @@ -108,122 +103,100 @@ def present( Enabled: True - tags: testing_key: testing_value - ''' + """ ret = { - 'name': name, - 'comment': '', - 'changes': {}, + "name": name, + "comment": "", + "changes": {}, } - res = __salt__['boto_cloudfront.get_distribution']( - name, - region=region, - key=key, - keyid=keyid, - profile=profile, + res = __salt__["boto_cloudfront.get_distribution"]( + name, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in res: - ret['result'] = False - ret['comment'] = 'Error checking distribution {0}: {1}'.format( - name, - res['error'], + if "error" in res: + ret["result"] = False + ret["comment"] = "Error checking distribution {0}: {1}".format( + name, res["error"], ) return ret - old = res['result'] + old = res["result"] if old is None: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Distribution {0} set for creation.'.format(name) - ret['changes'] = {'old': None, 'new': name} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Distribution {0} set for creation.".format(name) + ret["changes"] = {"old": None, "new": name} return ret - res = __salt__['boto_cloudfront.create_distribution']( - name, - config, - tags, - region=region, - key=key, - keyid=keyid, - profile=profile, + res = __salt__["boto_cloudfront.create_distribution"]( + name, config, tags, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in res: - ret['result'] = False - ret['comment'] = 'Error creating distribution {0}: {1}'.format( - name, - res['error'], + if "error" in res: + ret["result"] = False + ret["comment"] = "Error creating distribution {0}: {1}".format( + name, res["error"], ) return ret - ret['result'] = True - ret['comment'] = 'Created distribution {0}.'.format(name) - ret['changes'] = {'old': None, 'new': name} + ret["result"] = True + ret["comment"] = "Created distribution {0}.".format(name) + ret["changes"] = {"old": None, "new": name} return ret else: full_config_old = { - 'config': old['distribution']['DistributionConfig'], - 'tags': old['tags'], - } + "config": old["distribution"]["DistributionConfig"], + "tags": old["tags"], + } full_config_new = { - 'config': config, - 'tags': tags, - } - diffed_config = __utils__['dictdiffer.deep_diff']( - full_config_old, - full_config_new, + "config": config, + "tags": tags, + } + diffed_config = __utils__["dictdiffer.deep_diff"]( + full_config_old, full_config_new, ) def _yaml_safe_dump(attrs): - ''' + """ Safely dump YAML using a readable flow style - ''' - dumper_name = 'IndentedSafeOrderedDumper' - dumper = __utils__['yaml.get_dumper'](dumper_name) - return __utils__['yaml.dump']( - attrs, - default_flow_style=False, - Dumper=dumper) - - changes_diff = ''.join(difflib.unified_diff( - _yaml_safe_dump(full_config_old).splitlines(True), - _yaml_safe_dump(full_config_new).splitlines(True), - )) - - any_changes = bool('old' in diffed_config or 'new' in diffed_config) - if not any_changes: - ret['result'] = True - ret['comment'] = 'Distribution {0} has correct config.'.format( - name, + """ + dumper_name = "IndentedSafeOrderedDumper" + dumper = __utils__["yaml.get_dumper"](dumper_name) + return __utils__["yaml.dump"]( + attrs, default_flow_style=False, Dumper=dumper ) - return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = '\n'.join([ - 'Distribution {0} set for new config:'.format(name), - changes_diff, - ]) - ret['changes'] = {'diff': changes_diff} - return ret - - res = __salt__['boto_cloudfront.update_distribution']( - name, - config, - tags, - region=region, - key=key, - keyid=keyid, - profile=profile, + changes_diff = "".join( + difflib.unified_diff( + _yaml_safe_dump(full_config_old).splitlines(True), + _yaml_safe_dump(full_config_new).splitlines(True), + ) ) - if 'error' in res: - ret['result'] = False - ret['comment'] = 'Error updating distribution {0}: {1}'.format( - name, - res['error'], + + any_changes = bool("old" in diffed_config or "new" in diffed_config) + if not any_changes: + ret["result"] = True + ret["comment"] = "Distribution {0} has correct config.".format(name,) + return ret + + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "\n".join( + ["Distribution {0} set for new config:".format(name), changes_diff] + ) + ret["changes"] = {"diff": changes_diff} + return ret + + res = __salt__["boto_cloudfront.update_distribution"]( + name, config, tags, region=region, key=key, keyid=keyid, profile=profile, + ) + if "error" in res: + ret["result"] = False + ret["comment"] = "Error updating distribution {0}: {1}".format( + name, res["error"], ) return ret - ret['result'] = True - ret['comment'] = 'Updated distribution {0}.'.format(name) - ret['changes'] = {'diff': changes_diff} + ret["result"] = True + ret["comment"] = "Updated distribution {0}.".format(name) + ret["changes"] = {"diff": changes_diff} return ret diff --git a/salt/states/boto_cloudtrail.py b/salt/states/boto_cloudtrail.py index ced828c2160..ef0ebd04015 100644 --- a/salt/states/boto_cloudtrail.py +++ b/salt/states/boto_cloudtrail.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage CloudTrail Objects ========================= @@ -50,41 +50,52 @@ config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import os.path +import salt.utils.data + # Import Salt Libs from salt.ext import six -import salt.utils.data log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_cloudtrail' if 'boto_cloudtrail.exists' in __salt__ else False + """ + if "boto_cloudtrail.exists" in __salt__: + return "boto_cloudtrail" + return (False, "boto_cloudtrail module could not be loaded") -def present(name, Name, - S3BucketName, S3KeyPrefix=None, - SnsTopicName=None, - IncludeGlobalServiceEvents=True, - IsMultiRegionTrail=None, - EnableLogFileValidation=False, - CloudWatchLogsLogGroupArn=None, - CloudWatchLogsRoleArn=None, - KmsKeyId=None, - LoggingEnabled=True, - Tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def present( + name, + Name, + S3BucketName, + S3KeyPrefix=None, + SnsTopicName=None, + IncludeGlobalServiceEvents=True, + IsMultiRegionTrail=None, + EnableLogFileValidation=False, + CloudWatchLogsLogGroupArn=None, + CloudWatchLogsRoleArn=None, + KmsKeyId=None, + LoggingEnabled=True, + Tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure trail exists. name @@ -146,178 +157,221 @@ def present(name, Name, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': Name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": Name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_cloudtrail.exists'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_cloudtrail.exists"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create trail: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create trail: {0}.".format(r["error"]["message"]) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'CloudTrail {0} is set to be created.'.format(Name) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "CloudTrail {0} is set to be created.".format(Name) + ret["result"] = None return ret - r = __salt__['boto_cloudtrail.create'](Name=Name, - S3BucketName=S3BucketName, - S3KeyPrefix=S3KeyPrefix, - SnsTopicName=SnsTopicName, - IncludeGlobalServiceEvents=IncludeGlobalServiceEvents, - IsMultiRegionTrail=IsMultiRegionTrail, - EnableLogFileValidation=EnableLogFileValidation, - CloudWatchLogsLogGroupArn=CloudWatchLogsLogGroupArn, - CloudWatchLogsRoleArn=CloudWatchLogsRoleArn, - KmsKeyId=KmsKeyId, - region=region, key=key, keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create trail: {0}.'.format(r['error']['message']) + r = __salt__["boto_cloudtrail.create"]( + Name=Name, + S3BucketName=S3BucketName, + S3KeyPrefix=S3KeyPrefix, + SnsTopicName=SnsTopicName, + IncludeGlobalServiceEvents=IncludeGlobalServiceEvents, + IsMultiRegionTrail=IsMultiRegionTrail, + EnableLogFileValidation=EnableLogFileValidation, + CloudWatchLogsLogGroupArn=CloudWatchLogsLogGroupArn, + CloudWatchLogsRoleArn=CloudWatchLogsRoleArn, + KmsKeyId=KmsKeyId, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create trail: {0}.".format( + r["error"]["message"] + ) return ret - _describe = __salt__['boto_cloudtrail.describe'](Name, - region=region, key=key, keyid=keyid, profile=profile) - ret['changes']['old'] = {'trail': None} - ret['changes']['new'] = _describe - ret['comment'] = 'CloudTrail {0} created.'.format(Name) + _describe = __salt__["boto_cloudtrail.describe"]( + Name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["old"] = {"trail": None} + ret["changes"]["new"] = _describe + ret["comment"] = "CloudTrail {0} created.".format(Name) if LoggingEnabled: - r = __salt__['boto_cloudtrail.start_logging'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create trail: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_cloudtrail.start_logging"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create trail: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret - ret['changes']['new']['trail']['LoggingEnabled'] = True + ret["changes"]["new"]["trail"]["LoggingEnabled"] = True else: - ret['changes']['new']['trail']['LoggingEnabled'] = False + ret["changes"]["new"]["trail"]["LoggingEnabled"] = False if bool(Tags): - r = __salt__['boto_cloudtrail.add_tags'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile, **Tags) - if not r.get('tagged'): - ret['result'] = False - ret['comment'] = 'Failed to create trail: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_cloudtrail.add_tags"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile, **Tags + ) + if not r.get("tagged"): + ret["result"] = False + ret["comment"] = "Failed to create trail: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret - ret['changes']['new']['trail']['Tags'] = Tags + ret["changes"]["new"]["trail"]["Tags"] = Tags return ret - ret['comment'] = os.linesep.join([ret['comment'], 'CloudTrail {0} is present.'.format(Name)]) - ret['changes'] = {} + ret["comment"] = os.linesep.join( + [ret["comment"], "CloudTrail {0} is present.".format(Name)] + ) + ret["changes"] = {} # trail exists, ensure config matches - _describe = __salt__['boto_cloudtrail.describe'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in _describe: - ret['result'] = False - ret['comment'] = 'Failed to update trail: {0}.'.format(_describe['error']['message']) - ret['changes'] = {} + _describe = __salt__["boto_cloudtrail.describe"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in _describe: + ret["result"] = False + ret["comment"] = "Failed to update trail: {0}.".format( + _describe["error"]["message"] + ) + ret["changes"] = {} return ret - _describe = _describe.get('trail') + _describe = _describe.get("trail") - r = __salt__['boto_cloudtrail.status'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile) - _describe['LoggingEnabled'] = r.get('trail', {}).get('IsLogging', False) + r = __salt__["boto_cloudtrail.status"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile + ) + _describe["LoggingEnabled"] = r.get("trail", {}).get("IsLogging", False) need_update = False - bucket_vars = {'S3BucketName': 'S3BucketName', - 'S3KeyPrefix': 'S3KeyPrefix', - 'SnsTopicName': 'SnsTopicName', - 'IncludeGlobalServiceEvents': 'IncludeGlobalServiceEvents', - 'IsMultiRegionTrail': 'IsMultiRegionTrail', - 'EnableLogFileValidation': 'LogFileValidationEnabled', - 'CloudWatchLogsLogGroupArn': 'CloudWatchLogsLogGroupArn', - 'CloudWatchLogsRoleArn': 'CloudWatchLogsRoleArn', - 'KmsKeyId': 'KmsKeyId', - 'LoggingEnabled': 'LoggingEnabled'} + bucket_vars = { + "S3BucketName": "S3BucketName", + "S3KeyPrefix": "S3KeyPrefix", + "SnsTopicName": "SnsTopicName", + "IncludeGlobalServiceEvents": "IncludeGlobalServiceEvents", + "IsMultiRegionTrail": "IsMultiRegionTrail", + "EnableLogFileValidation": "LogFileValidationEnabled", + "CloudWatchLogsLogGroupArn": "CloudWatchLogsLogGroupArn", + "CloudWatchLogsRoleArn": "CloudWatchLogsRoleArn", + "KmsKeyId": "KmsKeyId", + "LoggingEnabled": "LoggingEnabled", + } for invar, outvar in six.iteritems(bucket_vars): if _describe[outvar] != locals()[invar]: need_update = True - ret['changes'].setdefault('new', {})[invar] = locals()[invar] - ret['changes'].setdefault('old', {})[invar] = _describe[outvar] + ret["changes"].setdefault("new", {})[invar] = locals()[invar] + ret["changes"].setdefault("old", {})[invar] = _describe[outvar] - r = __salt__['boto_cloudtrail.list_tags'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile) - _describe['Tags'] = r.get('tags', {}) - tagchange = salt.utils.data.compare_dicts(_describe['Tags'], Tags) + r = __salt__["boto_cloudtrail.list_tags"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile + ) + _describe["Tags"] = r.get("tags", {}) + tagchange = salt.utils.data.compare_dicts(_describe["Tags"], Tags) if bool(tagchange): need_update = True - ret['changes'].setdefault('new', {})['Tags'] = Tags - ret['changes'].setdefault('old', {})['Tags'] = _describe['Tags'] + ret["changes"].setdefault("new", {})["Tags"] = Tags + ret["changes"].setdefault("old", {})["Tags"] = _describe["Tags"] if need_update: - if __opts__['test']: - msg = 'CloudTrail {0} set to be modified.'.format(Name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "CloudTrail {0} set to be modified.".format(Name) + ret["comment"] = msg + ret["result"] = None return ret - ret['comment'] = os.linesep.join([ret['comment'], 'CloudTrail to be modified']) - r = __salt__['boto_cloudtrail.update'](Name=Name, - S3BucketName=S3BucketName, - S3KeyPrefix=S3KeyPrefix, - SnsTopicName=SnsTopicName, - IncludeGlobalServiceEvents=IncludeGlobalServiceEvents, - IsMultiRegionTrail=IsMultiRegionTrail, - EnableLogFileValidation=EnableLogFileValidation, - CloudWatchLogsLogGroupArn=CloudWatchLogsLogGroupArn, - CloudWatchLogsRoleArn=CloudWatchLogsRoleArn, - KmsKeyId=KmsKeyId, - region=region, key=key, keyid=keyid, profile=profile) - if not r.get('updated'): - ret['result'] = False - ret['comment'] = 'Failed to update trail: {0}.'.format(r['error']['message']) - ret['changes'] = {} + ret["comment"] = os.linesep.join([ret["comment"], "CloudTrail to be modified"]) + r = __salt__["boto_cloudtrail.update"]( + Name=Name, + S3BucketName=S3BucketName, + S3KeyPrefix=S3KeyPrefix, + SnsTopicName=SnsTopicName, + IncludeGlobalServiceEvents=IncludeGlobalServiceEvents, + IsMultiRegionTrail=IsMultiRegionTrail, + EnableLogFileValidation=EnableLogFileValidation, + CloudWatchLogsLogGroupArn=CloudWatchLogsLogGroupArn, + CloudWatchLogsRoleArn=CloudWatchLogsRoleArn, + KmsKeyId=KmsKeyId, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to update trail: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret if LoggingEnabled: - r = __salt__['boto_cloudtrail.start_logging'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile) - if not r.get('started'): - ret['result'] = False - ret['comment'] = 'Failed to update trail: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_cloudtrail.start_logging"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile + ) + if not r.get("started"): + ret["result"] = False + ret["comment"] = "Failed to update trail: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret else: - r = __salt__['boto_cloudtrail.stop_logging'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile) - if not r.get('stopped'): - ret['result'] = False - ret['comment'] = 'Failed to update trail: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_cloudtrail.stop_logging"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile + ) + if not r.get("stopped"): + ret["result"] = False + ret["comment"] = "Failed to update trail: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret if bool(tagchange): adds = {} removes = {} for k, diff in six.iteritems(tagchange): - if diff.get('new', '') != '': + if diff.get("new", "") != "": # there's an update for this key adds[k] = Tags[k] - elif diff.get('old', '') != '': - removes[k] = _describe['Tags'][k] + elif diff.get("old", "") != "": + removes[k] = _describe["Tags"][k] if bool(adds): - r = __salt__['boto_cloudtrail.add_tags'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile, **adds) + r = __salt__["boto_cloudtrail.add_tags"]( + Name=Name, + region=region, + key=key, + keyid=keyid, + profile=profile, + **adds + ) if bool(removes): - r = __salt__['boto_cloudtrail.remove_tags'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile, - **removes) + r = __salt__["boto_cloudtrail.remove_tags"]( + Name=Name, + region=region, + key=key, + keyid=keyid, + profile=profile, + **removes + ) return ret -def absent(name, Name, - region=None, key=None, keyid=None, profile=None): - ''' +def absent(name, Name, region=None, key=None, keyid=None, profile=None): + """ Ensure trail with passed properties is absent. name @@ -338,37 +392,34 @@ def absent(name, Name, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': Name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": Name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_cloudtrail.exists'](Name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete trail: {0}.'.format(r['error']['message']) + r = __salt__["boto_cloudtrail.exists"]( + Name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete trail: {0}.".format(r["error"]["message"]) return ret - if r and not r['exists']: - ret['comment'] = 'CloudTrail {0} does not exist.'.format(Name) + if r and not r["exists"]: + ret["comment"] = "CloudTrail {0} does not exist.".format(Name) return ret - if __opts__['test']: - ret['comment'] = 'CloudTrail {0} is set to be removed.'.format(Name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "CloudTrail {0} is set to be removed.".format(Name) + ret["result"] = None return ret - r = __salt__['boto_cloudtrail.delete'](Name, - region=region, key=key, - keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete trail: {0}.'.format(r['error']['message']) + r = __salt__["boto_cloudtrail.delete"]( + Name, region=region, key=key, keyid=keyid, profile=profile + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete trail: {0}.".format(r["error"]["message"]) return ret - ret['changes']['old'] = {'trail': Name} - ret['changes']['new'] = {'trail': None} - ret['comment'] = 'CloudTrail {0} deleted.'.format(Name) + ret["changes"]["old"] = {"trail": Name} + ret["changes"]["new"] = {"trail": None} + ret["comment"] = "CloudTrail {0} deleted.".format(Name) return ret diff --git a/salt/states/boto_cloudwatch_alarm.py b/salt/states/boto_cloudwatch_alarm.py index 5c008324bf6..5cabd7713d2 100644 --- a/salt/states/boto_cloudwatch_alarm.py +++ b/salt/states/boto_cloudwatch_alarm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Cloudwatch alarms .. versionadded:: 2014.7.0 @@ -51,7 +51,7 @@ as a passed in dict, or as a string to pull from pillars or minion config: - the-sqs-queue-name alarm_actions: - arn:aws:sns:us-east-1:1111111:myalerting-action -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -64,20 +64,16 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_cloudwatch_alarm' if 'boto_cloudwatch.get_alarm' in __salt__ else False + """ + if "boto_cloudwatch.get_alarm" in __salt__: + return "boto_cloudwatch_alarm" + return (False, "boto_cloudwatch module could not be loaded") -def present( - name, - attributes, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def present(name, attributes, region=None, key=None, keyid=None, profile=None): + """ Ensure the cloudwatch alarm exists. name @@ -98,16 +94,16 @@ def present( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - alarm_details = __salt__['boto_cloudwatch.get_alarm']( + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + alarm_details = __salt__["boto_cloudwatch.get_alarm"]( name, region, key, keyid, profile ) # Convert to arn's for k in ["alarm_actions", "insufficient_data_actions", "ok_actions"]: if k in attributes: - attributes[k] = __salt__['boto_cloudwatch.convert_to_arn']( + attributes[k] = __salt__["boto_cloudwatch.convert_to_arn"]( attributes[k], region, key, keyid, profile ) @@ -139,51 +135,46 @@ def present( "region": region, "key": key, "keyid": keyid, - "profile": profile + "profile": profile, } create_or_update_alarm_args.update(attributes) - if alarm_details: # alarm is present. update, or do nothing + if alarm_details: # alarm is present. update, or do nothing # check to see if attributes matches is_present. If so, do nothing. if len(difference) == 0: - ret['comment'] = "alarm {0} present and matching".format(name) + ret["comment"] = "alarm {0} present and matching".format(name) return ret - if __opts__['test']: - msg = 'alarm {0} is to be created/updated.'.format(name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "alarm {0} is to be created/updated.".format(name) + ret["comment"] = msg + ret["result"] = None return ret - result = __salt__['boto_cloudwatch.create_or_update_alarm']( + result = __salt__["boto_cloudwatch.create_or_update_alarm"]( **create_or_update_alarm_args ) if result: - ret['changes']['diff'] = difference + ret["changes"]["diff"] = difference else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} alarm'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} alarm".format(name) else: # alarm is absent. create it. - if __opts__['test']: - msg = 'alarm {0} is to be created/updated.'.format(name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "alarm {0} is to be created/updated.".format(name) + ret["comment"] = msg + ret["result"] = None return ret - result = __salt__['boto_cloudwatch.create_or_update_alarm']( + result = __salt__["boto_cloudwatch.create_or_update_alarm"]( **create_or_update_alarm_args ) if result: - ret['changes']['new'] = attributes + ret["changes"]["new"] = attributes else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} alarm'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} alarm".format(name) return ret -def absent( - name, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def absent(name, region=None, key=None, keyid=None, profile=None): + """ Ensure the named cloudwatch alarm is deleted. name @@ -201,26 +192,28 @@ def absent( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_present = __salt__['boto_cloudwatch.get_alarm'](name, region, key, - keyid, profile) + is_present = __salt__["boto_cloudwatch.get_alarm"]( + name, region, key, keyid, profile + ) if is_present: - if __opts__['test']: - ret['comment'] = 'alarm {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "alarm {0} is set to be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_cloudwatch.delete_alarm'](name, region, key, - keyid, profile) + deleted = __salt__["boto_cloudwatch.delete_alarm"]( + name, region, key, keyid, profile + ) if deleted: - ret['changes']['old'] = name - ret['changes']['new'] = None + ret["changes"]["old"] = name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} alarm.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} alarm.".format(name) else: - ret['comment'] = '{0} does not exist in {1}.'.format(name, region) + ret["comment"] = "{0} does not exist in {1}.".format(name, region) return ret diff --git a/salt/states/boto_cloudwatch_event.py b/salt/states/boto_cloudwatch_event.py index c576ae9db81..82b7c1a8f1a 100644 --- a/salt/states/boto_cloudwatch_event.py +++ b/salt/states/boto_cloudwatch_event.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage CloudTrail Objects ========================= @@ -50,10 +50,11 @@ config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -67,21 +68,29 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_cloudwatch_event' if 'boto_cloudwatch_event.exists' in __salt__ else False + """ + if "boto_cloudwatch_event.exists" in __salt__: + return "boto_cloudwatch_event" + return (False, "boto_cloudwatch_event module could not be loaded") -def present(name, Name=None, - ScheduleExpression=None, - EventPattern=None, - Description=None, - RoleArn=None, - State=None, - Targets=None, - region=None, key=None, keyid=None, profile=None): - ''' +def present( + name, + Name=None, + ScheduleExpression=None, + EventPattern=None, + Description=None, + RoleArn=None, + State=None, + Targets=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure trail exists. name @@ -123,12 +132,8 @@ def present(name, Name=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': Name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": Name, "result": True, "comment": "", "changes": {}} Name = Name if Name else name @@ -137,139 +142,194 @@ def present(name, Name=None, if Targets is None: Targets = [] - r = __salt__['boto_cloudwatch_event.exists'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_cloudwatch_event.exists"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create event rule: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create event rule: {0}.".format( + r["error"]["message"] + ) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'CloudWatch event rule {0} is set to be created.'.format(Name) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "CloudWatch event rule {0} is set to be created.".format( + Name + ) + ret["result"] = None return ret - r = __salt__['boto_cloudwatch_event.create_or_update'](Name=Name, - ScheduleExpression=ScheduleExpression, - EventPattern=EventPattern, - Description=Description, - RoleArn=RoleArn, - State=State, - region=region, key=key, keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create event rule: {0}.'.format(r['error']['message']) + r = __salt__["boto_cloudwatch_event.create_or_update"]( + Name=Name, + ScheduleExpression=ScheduleExpression, + EventPattern=EventPattern, + Description=Description, + RoleArn=RoleArn, + State=State, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create event rule: {0}.".format( + r["error"]["message"] + ) return ret - _describe = __salt__['boto_cloudwatch_event.describe'](Name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in _describe: - ret['result'] = False - ret['comment'] = 'Failed to create event rule: {0}.'.format(_describe['error']['message']) - ret['changes'] = {} + _describe = __salt__["boto_cloudwatch_event.describe"]( + Name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in _describe: + ret["result"] = False + ret["comment"] = "Failed to create event rule: {0}.".format( + _describe["error"]["message"] + ) + ret["changes"] = {} return ret - ret['changes']['old'] = {'rule': None} - ret['changes']['new'] = _describe - ret['comment'] = 'CloudTrail {0} created.'.format(Name) + ret["changes"]["old"] = {"rule": None} + ret["changes"]["new"] = _describe + ret["comment"] = "CloudTrail {0} created.".format(Name) if bool(Targets): - r = __salt__['boto_cloudwatch_event.put_targets'](Rule=Name, - Targets=Targets, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create event rule: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_cloudwatch_event.put_targets"]( + Rule=Name, + Targets=Targets, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create event rule: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret - ret['changes']['new']['rule']['Targets'] = Targets + ret["changes"]["new"]["rule"]["Targets"] = Targets return ret - ret['comment'] = os.linesep.join([ret['comment'], 'CloudWatch event rule {0} is present.'.format(Name)]) - ret['changes'] = {} + ret["comment"] = os.linesep.join( + [ret["comment"], "CloudWatch event rule {0} is present.".format(Name)] + ) + ret["changes"] = {} # trail exists, ensure config matches - _describe = __salt__['boto_cloudwatch_event.describe'](Name=Name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in _describe: - ret['result'] = False - ret['comment'] = 'Failed to update event rule: {0}.'.format(_describe['error']['message']) - ret['changes'] = {} + _describe = __salt__["boto_cloudwatch_event.describe"]( + Name=Name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in _describe: + ret["result"] = False + ret["comment"] = "Failed to update event rule: {0}.".format( + _describe["error"]["message"] + ) + ret["changes"] = {} return ret - _describe = _describe.get('rule') + _describe = _describe.get("rule") - r = __salt__['boto_cloudwatch_event.list_targets'](Rule=Name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to update event rule: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_cloudwatch_event.list_targets"]( + Rule=Name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to update event rule: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret - _describe['Targets'] = r.get('targets', []) + _describe["Targets"] = r.get("targets", []) need_update = False - rule_vars = {'ScheduleExpression': 'ScheduleExpression', - 'EventPattern': 'EventPattern', - 'Description': 'Description', - 'RoleArn': 'RoleArn', - 'State': 'State', - 'Targets': 'Targets'} + rule_vars = { + "ScheduleExpression": "ScheduleExpression", + "EventPattern": "EventPattern", + "Description": "Description", + "RoleArn": "RoleArn", + "State": "State", + "Targets": "Targets", + } for invar, outvar in six.iteritems(rule_vars): if _describe[outvar] != locals()[invar]: need_update = True - ret['changes'].setdefault('new', {})[invar] = locals()[invar] - ret['changes'].setdefault('old', {})[invar] = _describe[outvar] + ret["changes"].setdefault("new", {})[invar] = locals()[invar] + ret["changes"].setdefault("old", {})[invar] = _describe[outvar] if need_update: - if __opts__['test']: - msg = 'CloudWatch event rule {0} set to be modified.'.format(Name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "CloudWatch event rule {0} set to be modified.".format(Name) + ret["comment"] = msg + ret["result"] = None return ret - ret['comment'] = os.linesep.join([ret['comment'], 'CloudWatch event rule to be modified']) - r = __salt__['boto_cloudwatch_event.create_or_update'](Name=Name, - ScheduleExpression=ScheduleExpression, - EventPattern=EventPattern, - Description=Description, - RoleArn=RoleArn, - State=State, - region=region, key=key, keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to update event rule: {0}.'.format(r['error']['message']) - ret['changes'] = {} + ret["comment"] = os.linesep.join( + [ret["comment"], "CloudWatch event rule to be modified"] + ) + r = __salt__["boto_cloudwatch_event.create_or_update"]( + Name=Name, + ScheduleExpression=ScheduleExpression, + EventPattern=EventPattern, + Description=Description, + RoleArn=RoleArn, + State=State, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to update event rule: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret - if _describe['Targets'] != Targets: - removes = [i.get('Id') for i in _describe['Targets']] + if _describe["Targets"] != Targets: + removes = [i.get("Id") for i in _describe["Targets"]] log.error(Targets) if bool(Targets): for target in Targets: - tid = target.get('Id', None) + tid = target.get("Id", None) if tid is not None and tid in removes: ix = removes.index(tid) removes.pop(ix) - r = __salt__['boto_cloudwatch_event.put_targets'](Rule=Name, - Targets=Targets, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to update event rule: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_cloudwatch_event.put_targets"]( + Rule=Name, + Targets=Targets, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to update event rule: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret if bool(removes): - r = __salt__['boto_cloudwatch_event.remove_targets'](Rule=Name, - Ids=removes, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to update event rule: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_cloudwatch_event.remove_targets"]( + Rule=Name, + Ids=removes, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to update event rule: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret return ret def absent(name, Name=None, region=None, key=None, keyid=None, profile=None): - ''' + """ Ensure CloudWatch event rule with passed properties is absent. name @@ -291,62 +351,67 @@ def absent(name, Name=None, region=None, key=None, keyid=None, profile=None): profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': Name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": Name, "result": True, "comment": "", "changes": {}} Name = Name if Name else name - r = __salt__['boto_cloudwatch_event.exists'](Name, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete event rule: {0}.'.format(r['error']['message']) + r = __salt__["boto_cloudwatch_event.exists"]( + Name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete event rule: {0}.".format( + r["error"]["message"] + ) return ret - if r and not r['exists']: - ret['comment'] = 'CloudWatch event rule {0} does not exist.'.format(Name) + if r and not r["exists"]: + ret["comment"] = "CloudWatch event rule {0} does not exist.".format(Name) return ret - if __opts__['test']: - ret['comment'] = 'CloudWatch event rule {0} is set to be removed.'.format(Name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "CloudWatch event rule {0} is set to be removed.".format(Name) + ret["result"] = None return ret # must remove all targets first - r = __salt__['boto_cloudwatch_event.list_targets'](Rule=Name, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('targets'): - ret['result'] = False - ret['comment'] = 'Failed to delete event rule: {0}.'.format(r['error']['message']) + r = __salt__["boto_cloudwatch_event.list_targets"]( + Rule=Name, region=region, key=key, keyid=keyid, profile=profile + ) + if not r.get("targets"): + ret["result"] = False + ret["comment"] = "Failed to delete event rule: {0}.".format( + r["error"]["message"] + ) return ret - ids = [t.get('Id') for t in r['targets']] + ids = [t.get("Id") for t in r["targets"]] if bool(ids): - r = __salt__['boto_cloudwatch_event.remove_targets'](Rule=Name, Ids=ids, - region=region, key=key, - keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete event rule: {0}.'.format(r['error']['message']) + r = __salt__["boto_cloudwatch_event.remove_targets"]( + Rule=Name, Ids=ids, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete event rule: {0}.".format( + r["error"]["message"] + ) return ret - if r.get('failures'): - ret['result'] = False - ret['comment'] = 'Failed to delete event rule: {0}.'.format(r['failures']) + if r.get("failures"): + ret["result"] = False + ret["comment"] = "Failed to delete event rule: {0}.".format(r["failures"]) return ret - r = __salt__['boto_cloudwatch_event.delete'](Name, - region=region, key=key, - keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete event rule: {0}.'.format(r['error']['message']) + r = __salt__["boto_cloudwatch_event.delete"]( + Name, region=region, key=key, keyid=keyid, profile=profile + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete event rule: {0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'rule': Name} - ret['changes']['new'] = {'rule': None} - ret['comment'] = 'CloudWatch event rule {0} deleted.'.format(Name) + ret["changes"]["old"] = {"rule": Name} + ret["changes"]["new"] = {"rule": None} + ret["comment"] = "CloudWatch event rule {0} deleted.".format(Name) return ret diff --git a/salt/states/boto_cognitoidentity.py b/salt/states/boto_cognitoidentity.py index d0d13e46862..cf338e3c7f4 100644 --- a/salt/states/boto_cognitoidentity.py +++ b/salt/states/boto_cognitoidentity.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage CognitoIdentity Functions ================================ @@ -44,11 +44,13 @@ config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging + from salt.ext.six import string_types # Import Salt Libs @@ -57,18 +59,20 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_cognitoidentity' if 'boto_cognitoidentity.describe_identity_pools' in __salt__ else False + """ + if "boto_cognitoidentity.describe_identity_pools" in __salt__: + return "boto_cognitoidentity" + return (False, "boto_cognitoidentity module could not be loaded") def _get_object(objname, objtype): - ''' + """ Helper function to retrieve objtype from pillars if objname is string_types, used for SupportedLoginProviders and OpenIdConnectProviderARNs. - ''' + """ ret = None if objname is None: return ret @@ -76,7 +80,7 @@ def _get_object(objname, objtype): if isinstance(objname, string_types): if objname in __opts__: ret = __opts__[objname] - master_opts = __pillar__.get('master', {}) + master_opts = __pillar__.get("master", {}) if objname in master_opts: ret = master_opts[objname] if objname in __pillar__: @@ -90,57 +94,72 @@ def _get_object(objname, objtype): return ret -def _role_present(ret, IdentityPoolId, AuthenticatedRole, UnauthenticatedRole, conn_params): - ''' +def _role_present( + ret, IdentityPoolId, AuthenticatedRole, UnauthenticatedRole, conn_params +): + """ Helper function to set the Roles to the identity pool - ''' - r = __salt__['boto_cognitoidentity.get_identity_pool_roles'](IdentityPoolName='', - IdentityPoolId=IdentityPoolId, - **conn_params) - if r.get('error'): - ret['result'] = False - failure_comment = ('Failed to get existing identity pool roles: ' - '{0}'.format(r['error'].get('message', r['error']))) - ret['comment'] = '{0}\n{1}'.format(ret['comment'], failure_comment) + """ + r = __salt__["boto_cognitoidentity.get_identity_pool_roles"]( + IdentityPoolName="", IdentityPoolId=IdentityPoolId, **conn_params + ) + if r.get("error"): + ret["result"] = False + failure_comment = "Failed to get existing identity pool roles: " "{0}".format( + r["error"].get("message", r["error"]) + ) + ret["comment"] = "{0}\n{1}".format(ret["comment"], failure_comment) return - existing_identity_pool_role = r.get('identity_pool_roles')[0].get('Roles', {}) - r = __salt__['boto_cognitoidentity.set_identity_pool_roles'](IdentityPoolId=IdentityPoolId, - AuthenticatedRole=AuthenticatedRole, - UnauthenticatedRole=UnauthenticatedRole, - **conn_params) - if not r.get('set'): - ret['result'] = False - failure_comment = ('Failed to set roles: ' - '{0}'.format(r['error'].get('message', r['error']))) - ret['comment'] = '{0}\n{1}'.format(ret['comment'], failure_comment) + existing_identity_pool_role = r.get("identity_pool_roles")[0].get("Roles", {}) + r = __salt__["boto_cognitoidentity.set_identity_pool_roles"]( + IdentityPoolId=IdentityPoolId, + AuthenticatedRole=AuthenticatedRole, + UnauthenticatedRole=UnauthenticatedRole, + **conn_params + ) + if not r.get("set"): + ret["result"] = False + failure_comment = "Failed to set roles: " "{0}".format( + r["error"].get("message", r["error"]) + ) + ret["comment"] = "{0}\n{1}".format(ret["comment"], failure_comment) return - updated_identity_pool_role = r.get('roles') + updated_identity_pool_role = r.get("roles") if existing_identity_pool_role != updated_identity_pool_role: - if not ret['changes']: - ret['changes']['old'] = dict() - ret['changes']['new'] = dict() - ret['changes']['old']['Roles'] = existing_identity_pool_role - ret['changes']['new']['Roles'] = r.get('roles') - ret['comment'] = ('{0}\n{1}'.format(ret['comment'], 'identity pool roles updated.')) + if not ret["changes"]: + ret["changes"]["old"] = dict() + ret["changes"]["new"] = dict() + ret["changes"]["old"]["Roles"] = existing_identity_pool_role + ret["changes"]["new"]["Roles"] = r.get("roles") + ret["comment"] = "{0}\n{1}".format( + ret["comment"], "identity pool roles updated." + ) else: - ret['comment'] = ('{0}\n{1}'.format(ret['comment'], 'identity pool roles is already current.')) + ret["comment"] = "{0}\n{1}".format( + ret["comment"], "identity pool roles is already current." + ) return -def pool_present(name, - IdentityPoolName, - AuthenticatedRole, - AllowUnauthenticatedIdentities=False, - UnauthenticatedRole=None, - SupportedLoginProviders=None, - DeveloperProviderName=None, - OpenIdConnectProviderARNs=None, - region=None, key=None, keyid=None, profile=None): - ''' +def pool_present( + name, + IdentityPoolName, + AuthenticatedRole, + AllowUnauthenticatedIdentities=False, + UnauthenticatedRole=None, + SupportedLoginProviders=None, + DeveloperProviderName=None, + OpenIdConnectProviderARNs=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure Cognito Identity Pool exists. name @@ -186,102 +205,131 @@ def pool_present(name, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': IdentityPoolName, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": IdentityPoolName, "result": True, "comment": "", "changes": {}} conn_params = dict(region=region, key=key, keyid=keyid, profile=profile) - r = __salt__['boto_cognitoidentity.describe_identity_pools'](IdentityPoolName=IdentityPoolName, - **conn_params) + r = __salt__["boto_cognitoidentity.describe_identity_pools"]( + IdentityPoolName=IdentityPoolName, **conn_params + ) - if r.get('error'): - ret['result'] = False - ret['comment'] = 'Failed to describe identity pools {0}'.format(r['error']['message']) + if r.get("error"): + ret["result"] = False + ret["comment"] = "Failed to describe identity pools {0}".format( + r["error"]["message"] + ) return ret - identity_pools = r.get('identity_pools') + identity_pools = r.get("identity_pools") if identity_pools and len(identity_pools) > 1: - ret['result'] = False - ret['comment'] = ('More than one identity pool for the given name matched ' - 'Cannot execute pool_present function.\n' - 'Matched Identity Pools:\n{0}'.format(identity_pools)) + ret["result"] = False + ret["comment"] = ( + "More than one identity pool for the given name matched " + "Cannot execute pool_present function.\n" + "Matched Identity Pools:\n{0}".format(identity_pools) + ) return ret existing_identity_pool = None if identity_pools is None else identity_pools[0] - IdentityPoolId = None if existing_identity_pool is None else existing_identity_pool.get('IdentityPoolId') + IdentityPoolId = ( + None + if existing_identity_pool is None + else existing_identity_pool.get("IdentityPoolId") + ) - if __opts__['test']: + if __opts__["test"]: if identity_pools is None: - ret['comment'] = ('A new identity pool named {0} will be ' - 'created.'.format(IdentityPoolName)) + ret["comment"] = "A new identity pool named {0} will be " "created.".format( + IdentityPoolName + ) else: - ret['comment'] = ('An existing identity pool named {0} with id ' - '{1}will be updated.'.format(IdentityPoolName, - IdentityPoolId)) - ret['result'] = None + ret["comment"] = ( + "An existing identity pool named {0} with id " + "{1}will be updated.".format(IdentityPoolName, IdentityPoolId) + ) + ret["result"] = None return ret SupportedLoginProviders = _get_object(SupportedLoginProviders, dict) OpenIdConnectProviderARNs = _get_object(OpenIdConnectProviderARNs, list) - request_params = dict(IdentityPoolName=IdentityPoolName, - AllowUnauthenticatedIdentities=AllowUnauthenticatedIdentities, - SupportedLoginProviders=SupportedLoginProviders, - DeveloperProviderName=DeveloperProviderName, - OpenIdConnectProviderARNs=OpenIdConnectProviderARNs) + request_params = dict( + IdentityPoolName=IdentityPoolName, + AllowUnauthenticatedIdentities=AllowUnauthenticatedIdentities, + SupportedLoginProviders=SupportedLoginProviders, + DeveloperProviderName=DeveloperProviderName, + OpenIdConnectProviderARNs=OpenIdConnectProviderARNs, + ) request_params.update(conn_params) updated_identity_pool = None if IdentityPoolId is None: - r = __salt__['boto_cognitoidentity.create_identity_pool'](**request_params) + r = __salt__["boto_cognitoidentity.create_identity_pool"](**request_params) - if r.get('created'): - updated_identity_pool = r.get('identity_pool') - IdentityPoolId = updated_identity_pool.get('IdentityPoolId') - ret['comment'] = ('A new identity pool with name {0}, id {1} ' - 'is created.'.format(IdentityPoolName, IdentityPoolId)) + if r.get("created"): + updated_identity_pool = r.get("identity_pool") + IdentityPoolId = updated_identity_pool.get("IdentityPoolId") + ret["comment"] = ( + "A new identity pool with name {0}, id {1} " + "is created.".format(IdentityPoolName, IdentityPoolId) + ) else: - ret['result'] = False - ret['comment'] = ('Failed to add a new identity pool: ' - '{0}'.format(r['error'].get('message', r['error']))) + ret["result"] = False + ret["comment"] = "Failed to add a new identity pool: " "{0}".format( + r["error"].get("message", r["error"]) + ) return ret - else: # Update an existing pool - request_params['IdentityPoolId'] = IdentityPoolId + else: # Update an existing pool + request_params["IdentityPoolId"] = IdentityPoolId # we will never change the IdentityPoolName from the state module - request_params.pop('IdentityPoolName', None) - r = __salt__['boto_cognitoidentity.update_identity_pool'](**request_params) + request_params.pop("IdentityPoolName", None) + r = __salt__["boto_cognitoidentity.update_identity_pool"](**request_params) - if r.get('updated'): - updated_identity_pool = r.get('identity_pool') - ret['comment'] = ('Existing identity pool with name {0}, id {1} ' - 'is updated.'.format(IdentityPoolName, IdentityPoolId)) + if r.get("updated"): + updated_identity_pool = r.get("identity_pool") + ret["comment"] = ( + "Existing identity pool with name {0}, id {1} " + "is updated.".format(IdentityPoolName, IdentityPoolId) + ) else: - ret['result'] = False - ret['comment'] = ('Failed to update an existing identity pool {0} {1}: ' - '{2}'.format(IdentityPoolName, IdentityPoolId, - r['error'].get('message', r['error']))) + ret["result"] = False + ret["comment"] = ( + "Failed to update an existing identity pool {0} {1}: " + "{2}".format( + IdentityPoolName, + IdentityPoolId, + r["error"].get("message", r["error"]), + ) + ) return ret if existing_identity_pool != updated_identity_pool: - ret['changes']['old'] = dict() - ret['changes']['new'] = dict() - change_key = 'Identity Pool Name {0}'.format(IdentityPoolName) - ret['changes']['old'][change_key] = existing_identity_pool - ret['changes']['new'][change_key] = updated_identity_pool + ret["changes"]["old"] = dict() + ret["changes"]["new"] = dict() + change_key = "Identity Pool Name {0}".format(IdentityPoolName) + ret["changes"]["old"][change_key] = existing_identity_pool + ret["changes"]["new"][change_key] = updated_identity_pool else: - ret['comment'] = 'Identity Pool state is current, no changes.' + ret["comment"] = "Identity Pool state is current, no changes." # Now update the Auth/Unauth Roles - _role_present(ret, IdentityPoolId, AuthenticatedRole, UnauthenticatedRole, conn_params) + _role_present( + ret, IdentityPoolId, AuthenticatedRole, UnauthenticatedRole, conn_params + ) return ret -def pool_absent(name, IdentityPoolName, RemoveAllMatched=False, - region=None, key=None, keyid=None, profile=None): - ''' +def pool_absent( + name, + IdentityPoolName, + RemoveAllMatched=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure cognito identity pool with passed properties is absent. name @@ -311,68 +359,78 @@ def pool_absent(name, IdentityPoolName, RemoveAllMatched=False, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': IdentityPoolName, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": IdentityPoolName, "result": True, "comment": "", "changes": {}} conn_params = dict(region=region, key=key, keyid=keyid, profile=profile) - r = __salt__['boto_cognitoidentity.describe_identity_pools'](IdentityPoolName=IdentityPoolName, - **conn_params) + r = __salt__["boto_cognitoidentity.describe_identity_pools"]( + IdentityPoolName=IdentityPoolName, **conn_params + ) - if r.get('error'): - ret['result'] = False - ret['comment'] = 'Failed to describe identity pools {0}'.format(r['error']['message']) + if r.get("error"): + ret["result"] = False + ret["comment"] = "Failed to describe identity pools {0}".format( + r["error"]["message"] + ) return ret - identity_pools = r.get('identity_pools') + identity_pools = r.get("identity_pools") if identity_pools is None: - ret['result'] = True - ret['comment'] = 'No matching identity pool for the given name {0}'.format(IdentityPoolName) + ret["result"] = True + ret["comment"] = "No matching identity pool for the given name {0}".format( + IdentityPoolName + ) return ret if not RemoveAllMatched and len(identity_pools) > 1: - ret['result'] = False - ret['comment'] = ('More than one identity pool for the given name matched ' - 'and RemoveAllMatched flag is False.\n' - 'Matched Identity Pools:\n{0}'.format(identity_pools)) + ret["result"] = False + ret["comment"] = ( + "More than one identity pool for the given name matched " + "and RemoveAllMatched flag is False.\n" + "Matched Identity Pools:\n{0}".format(identity_pools) + ) return ret - if __opts__['test']: - ret['comment'] = ('The following matched identity pools will be ' - 'deleted.\n{0}'.format(identity_pools)) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = ( + "The following matched identity pools will be " + "deleted.\n{0}".format(identity_pools) + ) + ret["result"] = None return ret for identity_pool in identity_pools: - IdentityPoolId = identity_pool.get('IdentityPoolId') - r = __salt__['boto_cognitoidentity.delete_identity_pools'](IdentityPoolName='', - IdentityPoolId=IdentityPoolId, - **conn_params) - if r.get('error'): - ret['result'] = False - failure_comment = ('Failed to delete identity pool {0}: ' - '{1}'.format(IdentityPoolId, r['error'].get('message', r['error']))) - ret['comment'] = '{0}\n{1}'.format(ret['comment'], failure_comment) + IdentityPoolId = identity_pool.get("IdentityPoolId") + r = __salt__["boto_cognitoidentity.delete_identity_pools"]( + IdentityPoolName="", IdentityPoolId=IdentityPoolId, **conn_params + ) + if r.get("error"): + ret["result"] = False + failure_comment = "Failed to delete identity pool {0}: " "{1}".format( + IdentityPoolId, r["error"].get("message", r["error"]) + ) + ret["comment"] = "{0}\n{1}".format(ret["comment"], failure_comment) return ret - if r.get('deleted'): - if not ret['changes']: - ret['changes']['old'] = dict() - ret['changes']['new'] = dict() - change_key = 'Identity Pool Id {0}'.format(IdentityPoolId) - ret['changes']['old'][change_key] = IdentityPoolName - ret['changes']['new'][change_key] = None - ret['comment'] = '{0}\n{1}'.format(ret['comment'], '{0} deleted'.format(change_key)) + if r.get("deleted"): + if not ret["changes"]: + ret["changes"]["old"] = dict() + ret["changes"]["new"] = dict() + change_key = "Identity Pool Id {0}".format(IdentityPoolId) + ret["changes"]["old"][change_key] = IdentityPoolName + ret["changes"]["new"][change_key] = None + ret["comment"] = "{0}\n{1}".format( + ret["comment"], "{0} deleted".format(change_key) + ) else: - ret['result'] = False - failure_comment = 'Identity Pool Id {0} not deleted, returned count 0'.format(IdentityPoolId) - ret['comment'] = '{0}\n{1}'.format(ret['comment'], failure_comment) + ret["result"] = False + failure_comment = "Identity Pool Id {0} not deleted, returned count 0".format( + IdentityPoolId + ) + ret["comment"] = "{0}\n{1}".format(ret["comment"], failure_comment) return ret return ret diff --git a/salt/states/boto_datapipeline.py b/salt/states/boto_datapipeline.py index a6ee599fcd2..5273c205404 100644 --- a/salt/states/boto_datapipeline.py +++ b/salt/states/boto_datapipeline.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Data Pipelines .. versionadded:: 2016.3.0 @@ -47,10 +47,11 @@ config: startAt: FIRST_ACTIVATION_DATE_TIME - parameter_values: myDDBTableName: my-dynamo-table -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import datetime import difflib @@ -63,21 +64,28 @@ from salt.ext.six.moves import zip def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_datapipeline' if 'boto_datapipeline.create_pipeline' in __salt__ else False + """ + if "boto_datapipeline.create_pipeline" in __salt__: + return "boto_datapipeline" + return (False, "boto_datapipeline module could not be loaded") -def present(name, pipeline_objects=None, - pipeline_objects_from_pillars='boto_datapipeline_pipeline_objects', - parameter_objects=None, - parameter_objects_from_pillars='boto_datapipeline_parameter_objects', - parameter_values=None, - parameter_values_from_pillars='boto_datapipeline_parameter_values', - region=None, - key=None, keyid=None, profile=None): - ''' +def present( + name, + pipeline_objects=None, + pipeline_objects_from_pillars="boto_datapipeline_pipeline_objects", + parameter_objects=None, + parameter_objects_from_pillars="boto_datapipeline_parameter_objects", + parameter_values=None, + parameter_values_from_pillars="boto_datapipeline_parameter_values", + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the data pipeline exists with matching definition. name @@ -113,8 +121,8 @@ def present(name, pipeline_objects=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} pipeline_objects = pipeline_objects or {} parameter_objects = parameter_objects or {} @@ -131,145 +139,150 @@ def present(name, pipeline_objects=None, profile=profile, ) if present: - ret['comment'] = 'AWS data pipeline {0} present'.format(name) + ret["comment"] = "AWS data pipeline {0} present".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Data pipeline {0} is set to be created or updated'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Data pipeline {0} is set to be created or updated".format( + name + ) + ret["result"] = None return ret - result_create_pipeline = __salt__['boto_datapipeline.create_pipeline']( - name, - name, - region=region, - key=key, - keyid=keyid, - profile=profile, + result_create_pipeline = __salt__["boto_datapipeline.create_pipeline"]( + name, name, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in result_create_pipeline: - ret['result'] = False - ret['comment'] = 'Failed to create data pipeline {0}: {1}'.format( - name, result_create_pipeline['error']) + if "error" in result_create_pipeline: + ret["result"] = False + ret["comment"] = "Failed to create data pipeline {0}: {1}".format( + name, result_create_pipeline["error"] + ) return ret - pipeline_id = result_create_pipeline['result'] + pipeline_id = result_create_pipeline["result"] - result_pipeline_definition = __salt__['boto_datapipeline.put_pipeline_definition']( + result_pipeline_definition = __salt__["boto_datapipeline.put_pipeline_definition"]( pipeline_id, _pipeline_objects(pipeline_objects_from_pillars, pipeline_objects), - parameter_objects=_parameter_objects(parameter_objects_from_pillars, parameter_objects), - parameter_values=_parameter_values(parameter_values_from_pillars, parameter_values), + parameter_objects=_parameter_objects( + parameter_objects_from_pillars, parameter_objects + ), + parameter_values=_parameter_values( + parameter_values_from_pillars, parameter_values + ), region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in result_pipeline_definition: + if "error" in result_pipeline_definition: if _immutable_fields_error(result_pipeline_definition): # If update not possible, delete and retry - result_delete_pipeline = __salt__['boto_datapipeline.delete_pipeline']( - pipeline_id, - region=region, - key=key, - keyid=keyid, - profile=profile, + result_delete_pipeline = __salt__["boto_datapipeline.delete_pipeline"]( + pipeline_id, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in result_delete_pipeline: - ret['result'] = False - ret['comment'] = 'Failed to delete data pipeline {0}: {1}'.format( - pipeline_id, result_delete_pipeline['error']) + if "error" in result_delete_pipeline: + ret["result"] = False + ret["comment"] = "Failed to delete data pipeline {0}: {1}".format( + pipeline_id, result_delete_pipeline["error"] + ) return ret - result_create_pipeline = __salt__['boto_datapipeline.create_pipeline']( - name, - name, - region=region, - key=key, - keyid=keyid, - profile=profile, + result_create_pipeline = __salt__["boto_datapipeline.create_pipeline"]( + name, name, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in result_create_pipeline: - ret['result'] = False - ret['comment'] = 'Failed to create data pipeline {0}: {1}'.format( - name, result_create_pipeline['error']) + if "error" in result_create_pipeline: + ret["result"] = False + ret["comment"] = "Failed to create data pipeline {0}: {1}".format( + name, result_create_pipeline["error"] + ) return ret - pipeline_id = result_create_pipeline['result'] + pipeline_id = result_create_pipeline["result"] - result_pipeline_definition = __salt__['boto_datapipeline.put_pipeline_definition']( + result_pipeline_definition = __salt__[ + "boto_datapipeline.put_pipeline_definition" + ]( pipeline_id, _pipeline_objects(pipeline_objects_from_pillars, pipeline_objects), - parameter_objects=_parameter_objects(parameter_objects_from_pillars, parameter_objects), - parameter_values=_parameter_values(parameter_values_from_pillars, parameter_values), + parameter_objects=_parameter_objects( + parameter_objects_from_pillars, parameter_objects + ), + parameter_values=_parameter_values( + parameter_values_from_pillars, parameter_values + ), region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in result_pipeline_definition: + if "error" in result_pipeline_definition: # Still erroring after possible retry - ret['result'] = False - ret['comment'] = 'Failed to create data pipeline {0}: {1}'.format( - name, result_pipeline_definition['error']) + ret["result"] = False + ret["comment"] = "Failed to create data pipeline {0}: {1}".format( + name, result_pipeline_definition["error"] + ) return ret - result_activate_pipeline = __salt__['boto_datapipeline.activate_pipeline']( - pipeline_id, - region=region, - key=key, - keyid=keyid, - profile=profile, + result_activate_pipeline = __salt__["boto_datapipeline.activate_pipeline"]( + pipeline_id, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in result_activate_pipeline: - ret['result'] = False - ret['comment'] = 'Failed to create data pipeline {0}: {1}'.format( - name, result_pipeline_definition['error']) + if "error" in result_activate_pipeline: + ret["result"] = False + ret["comment"] = "Failed to create data pipeline {0}: {1}".format( + name, result_pipeline_definition["error"] + ) return ret - pipeline_definition_result = __salt__['boto_datapipeline.get_pipeline_definition']( + pipeline_definition_result = __salt__["boto_datapipeline.get_pipeline_definition"]( pipeline_id, - version='active', + version="active", region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in pipeline_definition_result: + if "error" in pipeline_definition_result: new_pipeline_definition = {} else: - new_pipeline_definition = _standardize(pipeline_definition_result['result']) + new_pipeline_definition = _standardize(pipeline_definition_result["result"]) if not old_pipeline_definition: - ret['changes']['new'] = 'Pipeline created.' - ret['comment'] = 'Data pipeline {0} created'.format(name) + ret["changes"]["new"] = "Pipeline created." + ret["comment"] = "Data pipeline {0} created".format(name) else: - ret['changes']['diff'] = _diff(old_pipeline_definition, new_pipeline_definition) - ret['comment'] = 'Data pipeline {0} updated'.format(name) + ret["changes"]["diff"] = _diff(old_pipeline_definition, new_pipeline_definition) + ret["comment"] = "Data pipeline {0} updated".format(name) return ret def _immutable_fields_error(result_pipeline_definition): - '''Return true if update pipeline failed due to immutable fields + """Return true if update pipeline failed due to immutable fields Some fields cannot be changed after a pipeline has been activated. http://docs.aws.amazon.com/datapipeline/latest/DeveloperGuide/dp-manage-pipeline-modify-console.html#dp-edit-pipeline-limits - ''' - for e in result_pipeline_definition['error']: + """ + for e in result_pipeline_definition["error"]: for e2 in e["errors"]: if "can not be changed" in e2: return True return False -def _pipeline_present_with_definition(name, expected_pipeline_objects, - expected_parameter_objects, - expected_parameter_values, region, key, - keyid, profile): - ''' +def _pipeline_present_with_definition( + name, + expected_pipeline_objects, + expected_parameter_objects, + expected_parameter_values, + region, + key, + keyid, + profile, +): + """ Return true if the pipeline exists and the definition matches. name @@ -296,38 +309,38 @@ def _pipeline_present_with_definition(name, expected_pipeline_objects, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - result_pipeline_id = __salt__['boto_datapipeline.pipeline_id_from_name']( - name, - region=region, - key=key, - keyid=keyid, - profile=profile, + """ + result_pipeline_id = __salt__["boto_datapipeline.pipeline_id_from_name"]( + name, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in result_pipeline_id: + if "error" in result_pipeline_id: return False, {} - pipeline_id = result_pipeline_id['result'] - pipeline_definition_result = __salt__['boto_datapipeline.get_pipeline_definition']( + pipeline_id = result_pipeline_id["result"] + pipeline_definition_result = __salt__["boto_datapipeline.get_pipeline_definition"]( pipeline_id, - version='active', + version="active", region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in pipeline_definition_result: + if "error" in pipeline_definition_result: return False, {} - pipeline_definition = _standardize(pipeline_definition_result['result']) + pipeline_definition = _standardize(pipeline_definition_result["result"]) - pipeline_objects = pipeline_definition.get('pipelineObjects') - parameter_objects = pipeline_definition.get('parameterObjects') - parameter_values = pipeline_definition.get('parameterValues') + pipeline_objects = pipeline_definition.get("pipelineObjects") + parameter_objects = pipeline_definition.get("parameterObjects") + parameter_values = pipeline_definition.get("parameterValues") - present = (_recursive_compare(_cleaned(pipeline_objects), _cleaned(expected_pipeline_objects)) and - _recursive_compare(parameter_objects, expected_parameter_objects) and - _recursive_compare(parameter_values, expected_parameter_values)) + present = ( + _recursive_compare( + _cleaned(pipeline_objects), _cleaned(expected_pipeline_objects) + ) + and _recursive_compare(parameter_objects, expected_parameter_objects) + and _recursive_compare(parameter_values, expected_parameter_values) + ) return present, pipeline_definition @@ -340,20 +353,21 @@ def _cleaned(_pipeline_objects): """ pipeline_objects = copy.deepcopy(_pipeline_objects) for pipeline_object in pipeline_objects: - if pipeline_object['id'] == 'DefaultSchedule': - for field_object in pipeline_object['fields']: - if field_object['key'] == 'startDateTime': - start_date_time_string = field_object['stringValue'] - start_date_time = datetime.datetime.strptime(start_date_time_string, - "%Y-%m-%dT%H:%M:%S") - field_object['stringValue'] = start_date_time.strftime("%H:%M:%S") + if pipeline_object["id"] == "DefaultSchedule": + for field_object in pipeline_object["fields"]: + if field_object["key"] == "startDateTime": + start_date_time_string = field_object["stringValue"] + start_date_time = datetime.datetime.strptime( + start_date_time_string, "%Y-%m-%dT%H:%M:%S" + ) + field_object["stringValue"] = start_date_time.strftime("%H:%M:%S") return pipeline_objects def _recursive_compare(v1, v2): - ''' + """ Return v1 == v2. Compares list, dict, recursively. - ''' + """ if isinstance(v1, list): if v2 is None: v2 = [] @@ -381,39 +395,42 @@ def _recursive_compare(v1, v2): def _id_or_key(list_item): - ''' + """ Return the value at key 'id' or 'key'. - ''' + """ if isinstance(list_item, dict): - if 'id' in list_item: - return list_item['id'] - if 'key' in list_item: - return list_item['key'] + if "id" in list_item: + return list_item["id"] + if "key" in list_item: + return list_item["key"] return list_item def _diff(old_pipeline_definition, new_pipeline_definition): - ''' + """ Return string diff of pipeline definitions. - ''' - old_pipeline_definition.pop('ResponseMetadata', None) - new_pipeline_definition.pop('ResponseMetadata', None) + """ + old_pipeline_definition.pop("ResponseMetadata", None) + new_pipeline_definition.pop("ResponseMetadata", None) - diff = salt.utils.data.decode(difflib.unified_diff( - salt.utils.json.dumps(old_pipeline_definition, indent=4).splitlines(True), - salt.utils.json.dumps(new_pipeline_definition, indent=4).splitlines(True), - )) - return ''.join(diff) # future lint: disable=blacklisted-function + diff = salt.utils.data.decode( + difflib.unified_diff( + salt.utils.json.dumps(old_pipeline_definition, indent=4).splitlines(True), + salt.utils.json.dumps(new_pipeline_definition, indent=4).splitlines(True), + ) + ) + return "".join(diff) # future lint: disable=blacklisted-function def _standardize(structure): - ''' + """ Return standardized format for lists/dictionaries. Lists of dictionaries are sorted by the value of the dictionary at its primary key ('id' or 'key'). OrderedDict's are converted to basic dictionaries. - ''' + """ + def mutating_helper(structure): if isinstance(structure, list): structure.sort(key=_id_or_key) @@ -431,7 +448,7 @@ def _standardize(structure): def _pipeline_objects(pipeline_objects_from_pillars, pipeline_object_overrides): - ''' + """ Return a list of pipeline objects that compose the pipeline pipeline_objects_from_pillars @@ -439,17 +456,17 @@ def _pipeline_objects(pipeline_objects_from_pillars, pipeline_object_overrides): pipeline_object_overrides Pipeline objects to use. Will override objects read from pillars. - ''' - from_pillars = copy.deepcopy(__salt__['pillar.get'](pipeline_objects_from_pillars)) + """ + from_pillars = copy.deepcopy(__salt__["pillar.get"](pipeline_objects_from_pillars)) from_pillars.update(pipeline_object_overrides) pipeline_objects = _standardize(_dict_to_list_ids(from_pillars)) for pipeline_object in pipeline_objects: - pipeline_object['fields'] = _properties_from_dict(pipeline_object['fields']) + pipeline_object["fields"] = _properties_from_dict(pipeline_object["fields"]) return pipeline_objects def _parameter_objects(parameter_objects_from_pillars, parameter_object_overrides): - ''' + """ Return a list of parameter objects that configure the pipeline parameter_objects_from_pillars @@ -457,17 +474,19 @@ def _parameter_objects(parameter_objects_from_pillars, parameter_object_override parameter_object_overrides Parameter objects to use. Will override objects read from pillars. - ''' - from_pillars = copy.deepcopy(__salt__['pillar.get'](parameter_objects_from_pillars)) + """ + from_pillars = copy.deepcopy(__salt__["pillar.get"](parameter_objects_from_pillars)) from_pillars.update(parameter_object_overrides) parameter_objects = _standardize(_dict_to_list_ids(from_pillars)) for parameter_object in parameter_objects: - parameter_object['attributes'] = _properties_from_dict(parameter_object['attributes']) + parameter_object["attributes"] = _properties_from_dict( + parameter_object["attributes"] + ) return parameter_objects def _parameter_values(parameter_values_from_pillars, parameter_value_overrides): - ''' + """ Return a dictionary of parameter values that configure the pipeline parameter_values_from_pillars @@ -475,29 +494,29 @@ def _parameter_values(parameter_values_from_pillars, parameter_value_overrides): parameter_value_overrides Parameter values to use. Will override values read from pillars. - ''' - from_pillars = copy.deepcopy(__salt__['pillar.get'](parameter_values_from_pillars)) + """ + from_pillars = copy.deepcopy(__salt__["pillar.get"](parameter_values_from_pillars)) from_pillars.update(parameter_value_overrides) parameter_values = _standardize(from_pillars) - return _properties_from_dict(parameter_values, key_name='id') + return _properties_from_dict(parameter_values, key_name="id") def _dict_to_list_ids(objects): - ''' + """ Convert a dictionary to a list of dictionaries, where each element has a key value pair {'id': key}. This makes it easy to override pillar values while still satisfying the boto api. - ''' + """ list_with_ids = [] for key, value in six.iteritems(objects): - element = {'id': key} + element = {"id": key} element.update(value) list_with_ids.append(element) return list_with_ids -def _properties_from_dict(d, key_name='key'): - ''' +def _properties_from_dict(d, key_name="key"): + """ Transforms dictionary into pipeline object properties. The output format conforms to boto's specification. @@ -521,24 +540,18 @@ def _properties_from_dict(d, key_name='key'): 'refValue': '2', }, ] - ''' + """ fields = [] for key, value in six.iteritems(d): if isinstance(value, dict): - fields.append({ - key_name: key, - 'refValue': value['ref'], - }) + fields.append({key_name: key, "refValue": value["ref"]}) else: - fields.append({ - key_name: key, - 'stringValue': value, - }) + fields.append({key_name: key, "stringValue": value}) return fields def absent(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Ensure a pipeline with the service_name does not exist name @@ -556,33 +569,25 @@ def absent(name, region=None, key=None, keyid=None, profile=None): profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - result_pipeline_id = __salt__['boto_datapipeline.pipeline_id_from_name']( - name, - region=region, - key=key, - keyid=keyid, - profile=profile, + result_pipeline_id = __salt__["boto_datapipeline.pipeline_id_from_name"]( + name, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' not in result_pipeline_id: - pipeline_id = result_pipeline_id['result'] - if __opts__['test']: - ret['comment'] = 'Data pipeline {0} set to be deleted.'.format(name) - ret['result'] = None + if "error" not in result_pipeline_id: + pipeline_id = result_pipeline_id["result"] + if __opts__["test"]: + ret["comment"] = "Data pipeline {0} set to be deleted.".format(name) + ret["result"] = None return ret else: - __salt__['boto_datapipeline.delete_pipeline']( - pipeline_id, - region=region, - key=key, - keyid=keyid, - profile=profile, + __salt__["boto_datapipeline.delete_pipeline"]( + pipeline_id, region=region, key=key, keyid=keyid, profile=profile, ) - ret['changes']['old'] = {'pipeline_id': pipeline_id} - ret['changes']['new'] = None + ret["changes"]["old"] = {"pipeline_id": pipeline_id} + ret["changes"]["new"] = None else: - ret['comment'] = 'AWS data pipeline {0} absent.'.format(name) + ret["comment"] = "AWS data pipeline {0} absent.".format(name) return ret diff --git a/salt/states/boto_dynamodb.py b/salt/states/boto_dynamodb.py index 6553b14cf0e..c973b62ef5b 100644 --- a/salt/states/boto_dynamodb.py +++ b/salt/states/boto_dynamodb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage DynamoDB Tables ====================== @@ -153,57 +153,62 @@ Setting the alarms in a pillar: attributes: threshold_percent: 0.90 period: 900 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import copy import datetime +import logging import math import sys -import logging -import copy + +import salt.utils.dictupdate as dictupdate # Import salt libs from salt.ext import six -import salt.utils.dictupdate as dictupdate logging.basicConfig( level=logging.INFO, - format='%(asctime)s %(name)s %(levelname)s %(message)s', - stream=sys.stdout + format="%(asctime)s %(name)s %(levelname)s %(message)s", + stream=sys.stdout, ) log = logging.getLogger() class GsiNotUpdatableError(Exception): - '''Raised when a global secondary index cannot be updated.''' + """Raised when a global secondary index cannot be updated.""" def __virtual__(): - ''' + """ Only load if boto_dynamodb is available. - ''' - ret = 'boto_dynamodb' if 'boto_dynamodb.exists' in __salt__ else False - return ret + """ + if "boto_dynamodb.exists" in __salt__: + return "boto_dynamodb" + return (False, "boto_dynamodb module could not be loaded") -def present(name=None, - table_name=None, - region=None, - key=None, - keyid=None, - profile=None, - read_capacity_units=None, - write_capacity_units=None, - alarms=None, - alarms_from_pillar="boto_dynamodb_alarms", - hash_key=None, - hash_key_data_type=None, - range_key=None, - range_key_data_type=None, - local_indexes=None, - global_indexes=None, - backup_configs_from_pillars='boto_dynamodb_backup_configs'): - ''' +def present( + name=None, + table_name=None, + region=None, + key=None, + keyid=None, + profile=None, + read_capacity_units=None, + write_capacity_units=None, + alarms=None, + alarms_from_pillar="boto_dynamodb_alarms", + hash_key=None, + hash_key_data_type=None, + range_key=None, + range_key_data_type=None, + local_indexes=None, + global_indexes=None, + backup_configs_from_pillars="boto_dynamodb_backup_configs", +): + """ Ensure the DynamoDB table exists. Table throughput can be updated after table creation. @@ -266,12 +271,14 @@ def present(name=None, backup_configs_from_pillars Pillars to use to configure DataPipeline backups - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if table_name: - ret['warnings'] = ['boto_dynamodb.present: `table_name` is deprecated.' - ' Please use `name` instead.'] - ret['name'] = table_name + ret["warnings"] = [ + "boto_dynamodb.present: `table_name` is deprecated." + " Please use `name` instead." + ] + ret["name"] = table_name name = table_name comments = [] @@ -279,20 +286,14 @@ def present(name=None, changes_new = {} # Ensure DynamoDB table exists - table_exists = __salt__['boto_dynamodb.exists']( - name, - region, - key, - keyid, - profile - ) + table_exists = __salt__["boto_dynamodb.exists"](name, region, key, keyid, profile) if not table_exists: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'DynamoDB table {0} would be created.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "DynamoDB table {0} would be created.".format(name) return ret else: - is_created = __salt__['boto_dynamodb.create_table']( + is_created = __salt__["boto_dynamodb.create_table"]( name, region, key, @@ -305,50 +306,48 @@ def present(name=None, range_key, range_key_data_type, local_indexes, - global_indexes + global_indexes, ) if not is_created: - ret['result'] = False - ret['comment'] = 'Failed to create table {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create table {0}".format(name) _add_changes(ret, changes_old, changes_new) return ret - comments.append('DynamoDB table {0} was successfully created'.format(name)) - changes_new['table'] = name - changes_new['read_capacity_units'] = read_capacity_units - changes_new['write_capacity_units'] = write_capacity_units - changes_new['hash_key'] = hash_key - changes_new['hash_key_data_type'] = hash_key_data_type - changes_new['range_key'] = range_key - changes_new['range_key_data_type'] = range_key_data_type - changes_new['local_indexes'] = local_indexes - changes_new['global_indexes'] = global_indexes + comments.append("DynamoDB table {0} was successfully created".format(name)) + changes_new["table"] = name + changes_new["read_capacity_units"] = read_capacity_units + changes_new["write_capacity_units"] = write_capacity_units + changes_new["hash_key"] = hash_key + changes_new["hash_key_data_type"] = hash_key_data_type + changes_new["range_key"] = range_key + changes_new["range_key_data_type"] = range_key_data_type + changes_new["local_indexes"] = local_indexes + changes_new["global_indexes"] = global_indexes else: - comments.append('DynamoDB table {0} exists'.format(name)) + comments.append("DynamoDB table {0} exists".format(name)) # Ensure DynamoDB table provisioned throughput matches - description = __salt__['boto_dynamodb.describe']( - name, - region, - key, - keyid, - profile + description = __salt__["boto_dynamodb.describe"](name, region, key, keyid, profile) + provisioned_throughput = description.get("Table", {}).get( + "ProvisionedThroughput", {} + ) + current_write_capacity_units = provisioned_throughput.get("WriteCapacityUnits") + current_read_capacity_units = provisioned_throughput.get("ReadCapacityUnits") + throughput_matches = ( + current_write_capacity_units == write_capacity_units + and current_read_capacity_units == read_capacity_units ) - provisioned_throughput = description.get('Table', {}).get('ProvisionedThroughput', {}) - current_write_capacity_units = provisioned_throughput.get('WriteCapacityUnits') - current_read_capacity_units = provisioned_throughput.get('ReadCapacityUnits') - throughput_matches = (current_write_capacity_units == write_capacity_units and - current_read_capacity_units == read_capacity_units) if not throughput_matches: - if __opts__['test']: - ret['result'] = None - comments.append('DynamoDB table {0} is set to be updated.'.format(name)) + if __opts__["test"]: + ret["result"] = None + comments.append("DynamoDB table {0} is set to be updated.".format(name)) else: - is_updated = __salt__['boto_dynamodb.update']( + is_updated = __salt__["boto_dynamodb.update"]( name, throughput={ - 'read': read_capacity_units, - 'write': write_capacity_units, + "read": read_capacity_units, + "write": write_capacity_units, }, region=region, key=key, @@ -356,88 +355,117 @@ def present(name=None, profile=profile, ) if not is_updated: - ret['result'] = False - ret['comment'] = 'Failed to update table {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to update table {0}".format(name) _add_changes(ret, changes_old, changes_new) return ret - comments.append('DynamoDB table {0} was successfully updated'.format(name)) - changes_old['read_capacity_units'] = current_read_capacity_units, - changes_old['write_capacity_units'] = current_write_capacity_units, - changes_new['read_capacity_units'] = read_capacity_units, - changes_new['write_capacity_units'] = write_capacity_units, + comments.append("DynamoDB table {0} was successfully updated".format(name)) + changes_old["read_capacity_units"] = (current_read_capacity_units,) + changes_old["write_capacity_units"] = (current_write_capacity_units,) + changes_new["read_capacity_units"] = (read_capacity_units,) + changes_new["write_capacity_units"] = (write_capacity_units,) else: - comments.append('DynamoDB table {0} throughput matches'.format(name)) + comments.append("DynamoDB table {0} throughput matches".format(name)) - provisioned_indexes = description.get('Table', {}).get('GlobalSecondaryIndexes', []) + provisioned_indexes = description.get("Table", {}).get("GlobalSecondaryIndexes", []) - _ret = _global_indexes_present(provisioned_indexes, global_indexes, changes_old, - changes_new, comments, name, region, key, keyid, - profile) - if not _ret['result']: - comments.append(_ret['comment']) - ret['result'] = _ret['result'] - if ret['result'] is False: - ret['comment'] = ',\n'.join(comments) + _ret = _global_indexes_present( + provisioned_indexes, + global_indexes, + changes_old, + changes_new, + comments, + name, + region, + key, + keyid, + profile, + ) + if not _ret["result"]: + comments.append(_ret["comment"]) + ret["result"] = _ret["result"] + if ret["result"] is False: + ret["comment"] = ",\n".join(comments) _add_changes(ret, changes_old, changes_new) return ret - _ret = _alarms_present(name, alarms, alarms_from_pillar, - write_capacity_units, read_capacity_units, - region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - comments.append(_ret['comment']) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: - ret['comment'] = ',\n'.join(comments) + _ret = _alarms_present( + name, + alarms, + alarms_from_pillar, + write_capacity_units, + read_capacity_units, + region, + key, + keyid, + profile, + ) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + comments.append(_ret["comment"]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: + ret["comment"] = ",\n".join(comments) _add_changes(ret, changes_old, changes_new) return ret # Ensure backup datapipeline is present datapipeline_configs = copy.deepcopy( - __salt__['pillar.get'](backup_configs_from_pillars, []) + __salt__["pillar.get"](backup_configs_from_pillars, []) ) for config in datapipeline_configs: datapipeline_ret = _ensure_backup_datapipeline_present( name=name, - schedule_name=config['name'], - period=config['period'], - utc_hour=config['utc_hour'], - s3_base_location=config['s3_base_location'], + schedule_name=config["name"], + period=config["period"], + utc_hour=config["utc_hour"], + s3_base_location=config["s3_base_location"], ) # Add comments and changes if successful changes were made (True for live mode, # None for test mode). - if datapipeline_ret['result'] in [True, None]: - ret['result'] = datapipeline_ret['result'] - comments.append(datapipeline_ret['comment']) - if datapipeline_ret.get('changes'): - ret['changes']['backup_datapipeline_{0}'.format(config['name'])] = \ - datapipeline_ret.get('changes'), + if datapipeline_ret["result"] in [True, None]: + ret["result"] = datapipeline_ret["result"] + comments.append(datapipeline_ret["comment"]) + if datapipeline_ret.get("changes"): + ret["changes"]["backup_datapipeline_{0}".format(config["name"])] = ( + datapipeline_ret.get("changes"), + ) else: - ret['comment'] = ',\n'.join([ret['comment'], datapipeline_ret['comment']]) + ret["comment"] = ",\n".join([ret["comment"], datapipeline_ret["comment"]]) _add_changes(ret, changes_old, changes_new) return ret - ret['comment'] = ',\n'.join(comments) + ret["comment"] = ",\n".join(comments) _add_changes(ret, changes_old, changes_new) return ret def _add_changes(ret, changes_old, changes_new): if changes_old: - ret['changes']['old'] = changes_old + ret["changes"]["old"] = changes_old if changes_new: - ret['changes']['new'] = changes_new + ret["changes"]["new"] = changes_new -def _global_indexes_present(provisioned_indexes, global_indexes, changes_old, - changes_new, comments, name, region, key, keyid, - profile): - '''Handles global secondary index for the table present state.''' - ret = {'result': True} +def _global_indexes_present( + provisioned_indexes, + global_indexes, + changes_old, + changes_new, + comments, + name, + region, + key, + keyid, + profile, +): + """Handles global secondary index for the table present state.""" + ret = {"result": True} if provisioned_indexes: - provisioned_gsi_config = dict((index['IndexName'], index) for index in provisioned_indexes) + provisioned_gsi_config = dict( + (index["IndexName"], index) for index in provisioned_indexes + ) else: provisioned_gsi_config = {} provisioned_index_names = set(provisioned_gsi_config.keys()) @@ -452,50 +480,79 @@ def _global_indexes_present(provisioned_indexes, global_indexes, changes_old, index_name = None for entry in index_config: # Key by the name field in the index config. - if entry.keys() == ['name']: + if entry.keys() == ["name"]: index_name = entry.values()[0] if not index_name: - ret['result'] = False - ret['comment'] = 'Index name not found for table {0}'.format(name) + ret["result"] = False + ret["comment"] = "Index name not found for table {0}".format(name) return ret gsi_config[index_name] = index - existing_index_names, new_index_names, index_names_to_be_deleted = _partition_index_names( - provisioned_index_names, set(gsi_config.keys())) + ( + existing_index_names, + new_index_names, + index_names_to_be_deleted, + ) = _partition_index_names(provisioned_index_names, set(gsi_config.keys())) if index_names_to_be_deleted: - ret['result'] = False - ret['comment'] = ('Deletion of GSIs ({0}) is not supported! Please do this ' - 'manually in the AWS console.'.format(', '.join(index_names_to_be_deleted))) + ret["result"] = False + ret["comment"] = ( + "Deletion of GSIs ({0}) is not supported! Please do this " + "manually in the AWS console.".format(", ".join(index_names_to_be_deleted)) + ) return ret elif len(new_index_names) > 1: - ret['result'] = False - ret['comment'] = ('Creation of multiple GSIs ({0}) is not supported due to API ' - 'limitations. Please create them one at a time.'.format(new_index_names)) + ret["result"] = False + ret["comment"] = ( + "Creation of multiple GSIs ({0}) is not supported due to API " + "limitations. Please create them one at a time.".format(new_index_names) + ) return ret if new_index_names: # Given the length check above, new_index_names should have a single element here. index_name = next(iter(new_index_names)) - _add_global_secondary_index(ret, name, index_name, changes_old, changes_new, comments, - gsi_config, region, key, keyid, profile) - if not ret['result']: + _add_global_secondary_index( + ret, + name, + index_name, + changes_old, + changes_new, + comments, + gsi_config, + region, + key, + keyid, + profile, + ) + if not ret["result"]: return ret if existing_index_names: - _update_global_secondary_indexes(ret, changes_old, changes_new, comments, - existing_index_names, provisioned_gsi_config, gsi_config, - name, region, key, keyid, profile) - if not ret['result']: + _update_global_secondary_indexes( + ret, + changes_old, + changes_new, + comments, + existing_index_names, + provisioned_gsi_config, + gsi_config, + name, + region, + key, + keyid, + profile, + ) + if not ret["result"]: return ret - if 'global_indexes' not in changes_old and 'global_indexes' not in changes_new: - comments.append('All global secondary indexes match') + if "global_indexes" not in changes_old and "global_indexes" not in changes_new: + comments.append("All global secondary indexes match") return ret def _partition_index_names(provisioned_index_names, index_names): - '''Returns 3 disjoint sets of indexes: existing, to be created, and to be deleted.''' + """Returns 3 disjoint sets of indexes: existing, to be created, and to be deleted.""" existing_index_names = set() new_index_names = set() for name in index_names: @@ -507,19 +564,32 @@ def _partition_index_names(provisioned_index_names, index_names): return existing_index_names, new_index_names, index_names_to_be_deleted -def _add_global_secondary_index(ret, name, index_name, changes_old, changes_new, comments, - gsi_config, region, key, keyid, profile): - '''Updates ret iff there was a failure or in test mode.''' - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Dynamo table {0} will have a GSI added: {1}'.format( - name, index_name) +def _add_global_secondary_index( + ret, + name, + index_name, + changes_old, + changes_new, + comments, + gsi_config, + region, + key, + keyid, + profile, +): + """Updates ret iff there was a failure or in test mode.""" + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Dynamo table {0} will have a GSI added: {1}".format( + name, index_name + ) return - changes_new.setdefault('global_indexes', {}) - success = __salt__['boto_dynamodb.create_global_secondary_index']( + changes_new.setdefault("global_indexes", {}) + success = __salt__["boto_dynamodb.create_global_secondary_index"]( name, - __salt__['boto_dynamodb.extract_index']( - gsi_config[index_name], global_index=True), + __salt__["boto_dynamodb.extract_index"]( + gsi_config[index_name], global_index=True + ), region=region, key=key, keyid=keyid, @@ -527,51 +597,64 @@ def _add_global_secondary_index(ret, name, index_name, changes_old, changes_new, ) if success: - comments.append('Created GSI {0}'.format(index_name)) - changes_new['global_indexes'][index_name] = gsi_config[index_name] + comments.append("Created GSI {0}".format(index_name)) + changes_new["global_indexes"][index_name] = gsi_config[index_name] else: - ret['result'] = False - ret['comment'] = 'Failed to create GSI {0}'.format(index_name) + ret["result"] = False + ret["comment"] = "Failed to create GSI {0}".format(index_name) -def _update_global_secondary_indexes(ret, changes_old, changes_new, comments, existing_index_names, - provisioned_gsi_config, gsi_config, name, region, key, - keyid, profile): - '''Updates ret iff there was a failure or in test mode.''' +def _update_global_secondary_indexes( + ret, + changes_old, + changes_new, + comments, + existing_index_names, + provisioned_gsi_config, + gsi_config, + name, + region, + key, + keyid, + profile, +): + """Updates ret iff there was a failure or in test mode.""" try: provisioned_throughputs, index_updates = _determine_gsi_updates( - existing_index_names, provisioned_gsi_config, gsi_config) + existing_index_names, provisioned_gsi_config, gsi_config + ) except GsiNotUpdatableError as e: - ret['result'] = False - ret['comment'] = six.text_type(e) + ret["result"] = False + ret["comment"] = six.text_type(e) return if index_updates: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Dynamo table {0} will have GSIs updated: {1}'.format( - name, ', '.join(index_updates.keys())) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Dynamo table {0} will have GSIs updated: {1}".format( + name, ", ".join(index_updates.keys()) + ) return - changes_old.setdefault('global_indexes', {}) - changes_new.setdefault('global_indexes', {}) - success = __salt__['boto_dynamodb.update_global_secondary_index']( - name, - index_updates, - region=region, - key=key, - keyid=keyid, - profile=profile, + changes_old.setdefault("global_indexes", {}) + changes_new.setdefault("global_indexes", {}) + success = __salt__["boto_dynamodb.update_global_secondary_index"]( + name, index_updates, region=region, key=key, keyid=keyid, profile=profile, ) if success: comments.append( - 'Updated GSIs with new throughputs {0}'.format(index_updates)) + "Updated GSIs with new throughputs {0}".format(index_updates) + ) for index_name in index_updates: - changes_old['global_indexes'][index_name] = provisioned_throughputs[index_name] - changes_new['global_indexes'][index_name] = index_updates[index_name] + changes_old["global_indexes"][index_name] = provisioned_throughputs[ + index_name + ] + changes_new["global_indexes"][index_name] = index_updates[index_name] else: - ret['result'] = False - ret['comment'] = 'Failed to update GSI throughputs {0}'.format(index_updates) + ret["result"] = False + ret["comment"] = "Failed to update GSI throughputs {0}".format( + index_updates + ) def _determine_gsi_updates(existing_index_names, provisioned_gsi_config, gsi_config): @@ -580,69 +663,96 @@ def _determine_gsi_updates(existing_index_names, provisioned_gsi_config, gsi_con index_updates = {} for index_name in existing_index_names: current_config = provisioned_gsi_config[index_name] - new_config = __salt__['boto_dynamodb.extract_index']( - gsi_config[index_name], global_index=True).schema() + new_config = __salt__["boto_dynamodb.extract_index"]( + gsi_config[index_name], global_index=True + ).schema() # The provisioned config will have more fields than the new config, so only consider # fields in new_config. for key in new_config: - if key in current_config and key != 'ProvisionedThroughput': + if key in current_config and key != "ProvisionedThroughput": new_value = new_config[key] current_value = current_config[key] # This is a special case since the Projection value can contain a list (not # correctly comparable with == as order doesn't matter). - if key == 'Projection': - if new_value['ProjectionType'] != current_value['ProjectionType']: - raise GsiNotUpdatableError('GSI projection types do not match') - elif set(new_value.get('NonKeyAttributes', [])) != set(current_value.get('NonKeyAttributes', [])): - raise GsiNotUpdatableError('NonKeyAttributes do not match for GSI projection') + if key == "Projection": + if new_value["ProjectionType"] != current_value["ProjectionType"]: + raise GsiNotUpdatableError("GSI projection types do not match") + elif set(new_value.get("NonKeyAttributes", [])) != set( + current_value.get("NonKeyAttributes", []) + ): + raise GsiNotUpdatableError( + "NonKeyAttributes do not match for GSI projection" + ) elif new_value != current_value: raise GsiNotUpdatableError( - 'GSI property {0} cannot be updated for index {1}'.format(key, index_name)) + "GSI property {0} cannot be updated for index {1}".format( + key, index_name + ) + ) - current_throughput = current_config.get('ProvisionedThroughput') - current_read = current_throughput.get('ReadCapacityUnits') - current_write = current_throughput.get('WriteCapacityUnits') - provisioned_throughputs[index_name] = {'read': current_read, 'write': current_write} - new_throughput = new_config.get('ProvisionedThroughput') - new_read = new_throughput.get('ReadCapacityUnits') - new_write = new_throughput.get('WriteCapacityUnits') + current_throughput = current_config.get("ProvisionedThroughput") + current_read = current_throughput.get("ReadCapacityUnits") + current_write = current_throughput.get("WriteCapacityUnits") + provisioned_throughputs[index_name] = { + "read": current_read, + "write": current_write, + } + new_throughput = new_config.get("ProvisionedThroughput") + new_read = new_throughput.get("ReadCapacityUnits") + new_write = new_throughput.get("WriteCapacityUnits") if current_read != new_read or current_write != new_write: - index_updates[index_name] = {'read': new_read, 'write': new_write} + index_updates[index_name] = {"read": new_read, "write": new_write} return provisioned_throughputs, index_updates -def _alarms_present(name, alarms, alarms_from_pillar, - write_capacity_units, read_capacity_units, - region, key, keyid, profile): - '''helper method for present. ensure that cloudwatch_alarms are set''' +def _alarms_present( + name, + alarms, + alarms_from_pillar, + write_capacity_units, + read_capacity_units, + region, + key, + keyid, + profile, +): + """helper method for present. ensure that cloudwatch_alarms are set""" # load data from alarms_from_pillar - tmp = copy.deepcopy( - __salt__['config.option'](alarms_from_pillar, {}) - ) + tmp = copy.deepcopy(__salt__["config.option"](alarms_from_pillar, {})) # merge with data from alarms if alarms: tmp = dictupdate.update(tmp, alarms) # set alarms, using boto_cloudwatch_alarm.present - merged_return_value = {'name': name, 'result': True, 'comment': '', 'changes': {}} + merged_return_value = {"name": name, "result": True, "comment": "", "changes": {}} for _, info in six.iteritems(tmp): # add dynamodb table to name and description info["name"] = name + " " + info["name"] - info["attributes"]["description"] = name + " " + info["attributes"]["description"] + info["attributes"]["description"] = ( + name + " " + info["attributes"]["description"] + ) # add dimension attribute info["attributes"]["dimensions"] = {"TableName": [name]} - if info["attributes"]["metric"] == "ConsumedWriteCapacityUnits" \ - and "threshold" not in info["attributes"]: - info["attributes"]["threshold"] = math.ceil(write_capacity_units * info["attributes"]["threshold_percent"]) + if ( + info["attributes"]["metric"] == "ConsumedWriteCapacityUnits" + and "threshold" not in info["attributes"] + ): + info["attributes"]["threshold"] = math.ceil( + write_capacity_units * info["attributes"]["threshold_percent"] + ) del info["attributes"]["threshold_percent"] # the write_capacity_units is given in unit / second. So we need # to multiply by the period to get the proper threshold. # http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/MonitoringDynamoDB.html info["attributes"]["threshold"] *= info["attributes"]["period"] - if info["attributes"]["metric"] == "ConsumedReadCapacityUnits" \ - and "threshold" not in info["attributes"]: - info["attributes"]["threshold"] = math.ceil(read_capacity_units * info["attributes"]["threshold_percent"]) + if ( + info["attributes"]["metric"] == "ConsumedReadCapacityUnits" + and "threshold" not in info["attributes"] + ): + info["attributes"]["threshold"] = math.ceil( + read_capacity_units * info["attributes"]["threshold_percent"] + ) del info["attributes"]["threshold_percent"] # the read_capacity_units is given in unit / second. So we need # to multiply by the period to get the proper threshold. @@ -657,7 +767,7 @@ def _alarms_present(name, alarms, alarms_from_pillar, "keyid": keyid, "profile": profile, } - results = __states__['boto_cloudwatch_alarm.present'](**kwargs) + results = __states__["boto_cloudwatch_alarm.present"](**kwargs) if not results["result"]: merged_return_value["result"] = results["result"] if results.get("changes", {}) != {}: @@ -667,38 +777,41 @@ def _alarms_present(name, alarms, alarms_from_pillar, return merged_return_value -def _ensure_backup_datapipeline_present(name, schedule_name, period, - utc_hour, s3_base_location): +def _ensure_backup_datapipeline_present( + name, schedule_name, period, utc_hour, s3_base_location +): kwargs = { - 'name': '{0}-{1}-backup'.format(name, schedule_name), - 'pipeline_objects': { - 'DefaultSchedule': { - 'name': schedule_name, - 'fields': { - 'period': period, - 'type': 'Schedule', - 'startDateTime': _next_datetime_with_utc_hour(name, utc_hour).isoformat(), - } + "name": "{0}-{1}-backup".format(name, schedule_name), + "pipeline_objects": { + "DefaultSchedule": { + "name": schedule_name, + "fields": { + "period": period, + "type": "Schedule", + "startDateTime": _next_datetime_with_utc_hour( + name, utc_hour + ).isoformat(), + }, }, }, - 'parameter_values': { - 'myDDBTableName': name, - 'myOutputS3Loc': '{0}/{1}/'.format(s3_base_location, name), - } + "parameter_values": { + "myDDBTableName": name, + "myOutputS3Loc": "{0}/{1}/".format(s3_base_location, name), + }, } - return __states__['boto_datapipeline.present'](**kwargs) + return __states__["boto_datapipeline.present"](**kwargs) def _get_deterministic_value_for_table_name(table_name, max_value): - ''' + """ For a given table_name, returns hash of the table_name limited by max_value. - ''' + """ return hash(table_name) % max_value def _next_datetime_with_utc_hour(table_name, utc_hour): - ''' + """ Datapipeline API is throttling us, as all the pipelines are started at the same time. We would like to uniformly distribute the startTime over a 60 minute window. @@ -706,7 +819,7 @@ def _next_datetime_with_utc_hour(table_name, utc_hour): hour == utc_hour minute = A value between 0-59 (depending on table name) second = A value between 0-59 (depending on table name) - ''' + """ today = datetime.date.today() # The minute and second values generated are deterministic, as we do not want @@ -717,7 +830,7 @@ def _next_datetime_with_utc_hour(table_name, utc_hour): day=today.day, hour=utc_hour, minute=_get_deterministic_value_for_table_name(table_name, 60), - second=_get_deterministic_value_for_table_name(table_name, 60) + second=_get_deterministic_value_for_table_name(table_name, 60), ) if start_date_time < datetime.datetime.utcnow(): @@ -727,12 +840,8 @@ def _next_datetime_with_utc_hour(table_name, utc_hour): return start_date_time -def absent(name, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def absent(name, region=None, key=None, keyid=None, profile=None): + """ Ensure the DynamoDB table does not exist. name @@ -750,30 +859,24 @@ def absent(name, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - exists = __salt__['boto_dynamodb.exists']( - name, - region, - key, - keyid, - profile - ) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + exists = __salt__["boto_dynamodb.exists"](name, region, key, keyid, profile) if not exists: - ret['comment'] = 'DynamoDB table {0} does not exist'.format(name) + ret["comment"] = "DynamoDB table {0} does not exist".format(name) return ret - if __opts__['test']: - ret['comment'] = 'DynamoDB table {0} is set to be deleted'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "DynamoDB table {0} is set to be deleted".format(name) + ret["result"] = None return ret - is_deleted = __salt__['boto_dynamodb.delete'](name, region, key, keyid, profile) + is_deleted = __salt__["boto_dynamodb.delete"](name, region, key, keyid, profile) if is_deleted: - ret['comment'] = 'Deleted DynamoDB table {0}'.format(name) - ret['changes'].setdefault('old', 'Table {0} exists'.format(name)) - ret['changes'].setdefault('new', 'Table {0} deleted'.format(name)) + ret["comment"] = "Deleted DynamoDB table {0}".format(name) + ret["changes"].setdefault("old", "Table {0} exists".format(name)) + ret["changes"].setdefault("new", "Table {0} deleted".format(name)) else: - ret['comment'] = 'Failed to delete DynamoDB table {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to delete DynamoDB table {0}".format(name) + ret["result"] = False return ret diff --git a/salt/states/boto_ec2.py b/salt/states/boto_ec2.py index 9d7e6c13407..68e51369d2e 100644 --- a/salt/states/boto_ec2.py +++ b/salt/states/boto_ec2.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage EC2 .. versionadded:: 2015.8.0 @@ -49,137 +49,136 @@ The below code deletes a key pair: - region: eu-west-1 - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging -from time import time, sleep +from time import sleep, time + +import salt.utils.data +import salt.utils.dictupdate as dictupdate +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import salt libs from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,no-name-in-module,redefined-builtin -import salt.utils.data -import salt.utils.dictupdate as dictupdate -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.ext.six.moves import range log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - if 'boto_ec2.get_key' in __salt__: - return 'boto_ec2' - else: - return False + """ + if "boto_ec2.get_key" in __salt__: + return "boto_ec2" + return (False, "boto_ec2 module could not be loaded") -def key_present(name, save_private=None, upload_public=None, region=None, - key=None, keyid=None, profile=None): - ''' +def key_present( + name, + save_private=None, + upload_public=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure key pair is present. - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } - exists = __salt__['boto_ec2.get_key'](name, region, key, keyid, profile) - log.debug('exists is %s', exists) - if upload_public is not None and 'salt://' in upload_public: + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + exists = __salt__["boto_ec2.get_key"](name, region, key, keyid, profile) + log.debug("exists is %s", exists) + if upload_public is not None and "salt://" in upload_public: try: - upload_public = __salt__['cp.get_file_str'](upload_public) + upload_public = __salt__["cp.get_file_str"](upload_public) except IOError as e: log.debug(e) - ret['comment'] = 'File {0} not found.'.format(upload_public) - ret['result'] = False + ret["comment"] = "File {0} not found.".format(upload_public) + ret["result"] = False return ret if not exists: - if __opts__['test']: - ret['comment'] = 'The key {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The key {0} is set to be created.".format(name) + ret["result"] = None return ret if save_private and not upload_public: - created = __salt__['boto_ec2.create_key']( + created = __salt__["boto_ec2.create_key"]( name, save_private, region, key, keyid, profile ) if created: - ret['result'] = True - ret['comment'] = 'The key {0} is created.'.format(name) - ret['changes']['new'] = created + ret["result"] = True + ret["comment"] = "The key {0} is created.".format(name) + ret["changes"]["new"] = created else: - ret['result'] = False - ret['comment'] = 'Could not create key {0} '.format(name) + ret["result"] = False + ret["comment"] = "Could not create key {0} ".format(name) elif not save_private and upload_public: - imported = __salt__['boto_ec2.import_key'](name, upload_public, - region, key, keyid, - profile) + imported = __salt__["boto_ec2.import_key"]( + name, upload_public, region, key, keyid, profile + ) if imported: - ret['result'] = True - ret['comment'] = 'The key {0} is created.'.format(name) - ret['changes']['old'] = None - ret['changes']['new'] = imported + ret["result"] = True + ret["comment"] = "The key {0} is created.".format(name) + ret["changes"]["old"] = None + ret["changes"]["new"] = imported else: - ret['result'] = False - ret['comment'] = 'Could not create key {0} '.format(name) + ret["result"] = False + ret["comment"] = "Could not create key {0} ".format(name) else: - ret['result'] = False - ret['comment'] = 'You can either upload or download a private key ' + ret["result"] = False + ret["comment"] = "You can either upload or download a private key " else: - ret['result'] = True - ret['comment'] = 'The key name {0} already exists'.format(name) + ret["result"] = True + ret["comment"] = "The key name {0} already exists".format(name) return ret def key_absent(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Deletes a key pair - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } - exists = __salt__['boto_ec2.get_key'](name, region, key, keyid, profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + exists = __salt__["boto_ec2.get_key"](name, region, key, keyid, profile) if exists: - if __opts__['test']: - ret['comment'] = 'The key {0} is set to be deleted.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The key {0} is set to be deleted.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_ec2.delete_key'](name, region, - key, keyid, - profile) - log.debug('exists is %s', deleted) + deleted = __salt__["boto_ec2.delete_key"](name, region, key, keyid, profile) + log.debug("exists is %s", deleted) if deleted: - ret['result'] = True - ret['comment'] = 'The key {0} is deleted.'.format(name) - ret['changes']['old'] = name + ret["result"] = True + ret["comment"] = "The key {0} is deleted.".format(name) + ret["changes"]["old"] = name else: - ret['result'] = False - ret['comment'] = 'Could not delete key {0} '.format(name) + ret["result"] = False + ret["comment"] = "Could not delete key {0} ".format(name) else: - ret['result'] = True - ret['comment'] = 'The key name {0} does not exist'.format(name) + ret["result"] = True + ret["comment"] = "The key name {0} does not exist".format(name) return ret def eni_present( - name, - subnet_id=None, - subnet_name=None, - private_ip_address=None, - description=None, - groups=None, - source_dest_check=True, - allocate_eip=None, - arecords=None, - region=None, - key=None, - keyid=None, - profile=None): - ''' + name, + subnet_id=None, + subnet_name=None, + private_ip_address=None, + description=None, + groups=None, + source_dest_check=True, + allocate_eip=None, + arecords=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the EC2 ENI exists. .. versionadded:: 2016.3.0 @@ -236,231 +235,261 @@ def eni_present( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ if not salt.utils.data.exactly_one((subnet_id, subnet_name)): - raise SaltInvocationError('One (but not both) of subnet_id or ' - 'subnet_name must be provided.') + raise SaltInvocationError( + "One (but not both) of subnet_id or " "subnet_name must be provided." + ) if not groups: - raise SaltInvocationError('groups is a required argument.') + raise SaltInvocationError("groups is a required argument.") if not isinstance(groups, list): - raise SaltInvocationError('groups must be a list.') + raise SaltInvocationError("groups must be a list.") if not isinstance(source_dest_check, bool): - raise SaltInvocationError('source_dest_check must be a bool.') - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - r = __salt__['boto_ec2.get_network_interface']( + raise SaltInvocationError("source_dest_check must be a bool.") + ret = {"name": name, "result": True, "comment": "", "changes": {}} + r = __salt__["boto_ec2.get_network_interface"]( name=name, region=region, key=key, keyid=keyid, profile=profile ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Error when attempting to find eni: {0}.'.format( - r['error']['message'] + if "error" in r: + ret["result"] = False + ret["comment"] = "Error when attempting to find eni: {0}.".format( + r["error"]["message"] ) return ret - if not r['result']: - if __opts__['test']: - ret['comment'] = 'ENI is set to be created.' + if not r["result"]: + if __opts__["test"]: + ret["comment"] = "ENI is set to be created." if allocate_eip: - ret['comment'] = ' '.join([ret['comment'], 'An EIP is set to be allocated/assocaited to the ENI.']) + ret["comment"] = " ".join( + [ + ret["comment"], + "An EIP is set to be allocated/assocaited to the ENI.", + ] + ) if arecords: - ret['comment'] = ' '.join([ret['comment'], 'A records are set to be created.']) - ret['result'] = None + ret["comment"] = " ".join( + [ret["comment"], "A records are set to be created."] + ) + ret["result"] = None return ret - result_create = __salt__['boto_ec2.create_network_interface']( - name, subnet_id=subnet_id, subnet_name=subnet_name, private_ip_address=private_ip_address, - description=description, groups=groups, region=region, key=key, - keyid=keyid, profile=profile + result_create = __salt__["boto_ec2.create_network_interface"]( + name, + subnet_id=subnet_id, + subnet_name=subnet_name, + private_ip_address=private_ip_address, + description=description, + groups=groups, + region=region, + key=key, + keyid=keyid, + profile=profile, ) - if 'error' in result_create: - ret['result'] = False - ret['comment'] = 'Failed to create ENI: {0}'.format( - result_create['error']['message'] + if "error" in result_create: + ret["result"] = False + ret["comment"] = "Failed to create ENI: {0}".format( + result_create["error"]["message"] ) return ret - r['result'] = result_create['result'] - ret['comment'] = 'Created ENI {0}'.format(name) - ret['changes']['id'] = r['result']['id'] + r["result"] = result_create["result"] + ret["comment"] = "Created ENI {0}".format(name) + ret["changes"]["id"] = r["result"]["id"] else: _ret = _eni_attribute( - r['result'], 'description', description, region, key, keyid, - profile + r["result"], "description", description, region, key, keyid, profile ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = _ret['comment'] - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = _ret["comment"] + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - _ret = _eni_groups( - r['result'], groups, region, key, keyid, profile - ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + _ret = _eni_groups(r["result"], groups, region, key, keyid, profile) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret # Actions that need to occur whether creating or updating _ret = _eni_attribute( - r['result'], 'source_dest_check', source_dest_check, region, key, - keyid, profile + r["result"], "source_dest_check", source_dest_check, region, key, keyid, profile ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret if allocate_eip: - if 'allocationId' not in r['result']: - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'An EIP is set to be allocated and assocaited to the ENI.']) + if "allocationId" not in r["result"]: + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "An EIP is set to be allocated and assocaited to the ENI.", + ] + ) else: - domain = 'vpc' if allocate_eip == 'vpc' else None - eip_alloc = __salt__['boto_ec2.allocate_eip_address'](domain=domain, - region=region, - key=key, - keyid=keyid, - profile=profile) + domain = "vpc" if allocate_eip == "vpc" else None + eip_alloc = __salt__["boto_ec2.allocate_eip_address"]( + domain=domain, region=region, key=key, keyid=keyid, profile=profile + ) if eip_alloc: - _ret = __salt__['boto_ec2.associate_eip_address'](instance_id=None, - instance_name=None, - public_ip=None, - allocation_id=eip_alloc['allocation_id'], - network_interface_id=r['result']['id'], - private_ip_address=None, - allow_reassociation=False, - region=region, - key=key, - keyid=keyid, - profile=profile) + _ret = __salt__["boto_ec2.associate_eip_address"]( + instance_id=None, + instance_name=None, + public_ip=None, + allocation_id=eip_alloc["allocation_id"], + network_interface_id=r["result"]["id"], + private_ip_address=None, + allow_reassociation=False, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not _ret: - _ret = __salt__['boto_ec2.release_eip_address'](public_ip=None, - allocation_id=eip_alloc['allocation_id'], - region=region, - key=key, - keyid=keyid, - profile=profile) - ret['result'] = False - msg = 'Failed to assocaite the allocated EIP address with the ENI. The EIP {0}'.format('was successfully released.' if _ret else 'was NOT RELEASED.') - ret['comment'] = ' '.join([ret['comment'], msg]) + _ret = __salt__["boto_ec2.release_eip_address"]( + public_ip=None, + allocation_id=eip_alloc["allocation_id"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["result"] = False + msg = "Failed to assocaite the allocated EIP address with the ENI. The EIP {0}".format( + "was successfully released." + if _ret + else "was NOT RELEASED." + ) + ret["comment"] = " ".join([ret["comment"], msg]) return ret else: - ret['result'] = False - ret['comment'] = ' '.join([ret['comment'], 'Failed to allocate an EIP address']) + ret["result"] = False + ret["comment"] = " ".join( + [ret["comment"], "Failed to allocate an EIP address"] + ) return ret else: - ret['comment'] = ' '.join([ret['comment'], 'An EIP is already allocated/assocaited to the ENI']) + ret["comment"] = " ".join( + [ret["comment"], "An EIP is already allocated/assocaited to the ENI"] + ) if arecords: for arecord in arecords: - if 'name' not in arecord: + if "name" not in arecord: msg = 'The arecord must contain a "name" property.' raise SaltInvocationError(msg) - log.debug('processing arecord %s', arecord) + log.debug("processing arecord %s", arecord) _ret = None - dns_provider = 'boto_route53' - arecord['record_type'] = 'A' + dns_provider = "boto_route53" + arecord["record_type"] = "A" public_ip_arecord = False - if 'public' in arecord: - public_ip_arecord = arecord.pop('public') + if "public" in arecord: + public_ip_arecord = arecord.pop("public") if public_ip_arecord: - if 'publicIp' in r['result']: - arecord['value'] = r['result']['publicIp'] - elif 'public_ip' in eip_alloc: - arecord['value'] = eip_alloc['public_ip'] + if "publicIp" in r["result"]: + arecord["value"] = r["result"]["publicIp"] + elif "public_ip" in eip_alloc: + arecord["value"] = eip_alloc["public_ip"] else: - msg = 'Unable to add an A record for the public IP address, a public IP address does not seem to be allocated to this ENI.' + msg = "Unable to add an A record for the public IP address, a public IP address does not seem to be allocated to this ENI." raise CommandExecutionError(msg) else: - arecord['value'] = r['result']['private_ip_address'] - if 'provider' in arecord: - dns_provider = arecord.pop('provider') - if dns_provider == 'boto_route53': - if 'profile' not in arecord: - arecord['profile'] = profile - if 'key' not in arecord: - arecord['key'] = key - if 'keyid' not in arecord: - arecord['keyid'] = keyid - if 'region' not in arecord: - arecord['region'] = region - _ret = __states__['.'.join([dns_provider, 'present'])](**arecord) - log.debug('ret from dns_provider.present = %s', _ret) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + arecord["value"] = r["result"]["private_ip_address"] + if "provider" in arecord: + dns_provider = arecord.pop("provider") + if dns_provider == "boto_route53": + if "profile" not in arecord: + arecord["profile"] = profile + if "key" not in arecord: + arecord["key"] = key + if "keyid" not in arecord: + arecord["keyid"] = keyid + if "region" not in arecord: + arecord["region"] = region + _ret = __states__[".".join([dns_provider, "present"])](**arecord) + log.debug("ret from dns_provider.present = %s", _ret) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret return ret def _eni_attribute(metadata, attr, value, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} + ret = {"result": True, "comment": "", "changes": {}} if metadata[attr] == value: return ret - if __opts__['test']: - ret['comment'] = 'ENI set to have {0} updated.'.format(attr) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ENI set to have {0} updated.".format(attr) + ret["result"] = None return ret - result_update = __salt__['boto_ec2.modify_network_interface_attribute']( - network_interface_id=metadata['id'], attr=attr, - value=value, region=region, key=key, keyid=keyid, profile=profile + result_update = __salt__["boto_ec2.modify_network_interface_attribute"]( + network_interface_id=metadata["id"], + attr=attr, + value=value, + region=region, + key=key, + keyid=keyid, + profile=profile, ) - if 'error' in result_update: - msg = 'Failed to update ENI {0}: {1}.' - ret['result'] = False - ret['comment'] = msg.format(attr, result_update['error']['message']) + if "error" in result_update: + msg = "Failed to update ENI {0}: {1}." + ret["result"] = False + ret["comment"] = msg.format(attr, result_update["error"]["message"]) else: - ret['comment'] = 'Updated ENI {0}.'.format(attr) - ret['changes'][attr] = { - 'old': metadata[attr], - 'new': value - } + ret["comment"] = "Updated ENI {0}.".format(attr) + ret["changes"][attr] = {"old": metadata[attr], "new": value} return ret def _eni_groups(metadata, groups, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - group_ids = [g['id'] for g in metadata['groups']] + ret = {"result": True, "comment": "", "changes": {}} + group_ids = [g["id"] for g in metadata["groups"]] group_ids.sort() - _groups = __salt__['boto_secgroup.convert_to_group_ids']( - groups, vpc_id=metadata['vpc_id'], region=region, key=key, keyid=keyid, - profile=profile + _groups = __salt__["boto_secgroup.convert_to_group_ids"]( + groups, + vpc_id=metadata["vpc_id"], + region=region, + key=key, + keyid=keyid, + profile=profile, ) if not _groups: - ret['comment'] = 'Could not find secgroup ids for provided groups.' - ret['result'] = False + ret["comment"] = "Could not find secgroup ids for provided groups." + ret["result"] = False _groups.sort() if group_ids == _groups: return ret - if __opts__['test']: - ret['comment'] = 'ENI set to have groups updated.' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ENI set to have groups updated." + ret["result"] = None return ret - result_update = __salt__['boto_ec2.modify_network_interface_attribute']( - network_interface_id=metadata['id'], attr='groups', - value=_groups, region=region, key=key, keyid=keyid, profile=profile + result_update = __salt__["boto_ec2.modify_network_interface_attribute"]( + network_interface_id=metadata["id"], + attr="groups", + value=_groups, + region=region, + key=key, + keyid=keyid, + profile=profile, ) - if 'error' in result_update: - msg = 'Failed to update ENI groups: {1}.' - ret['result'] = False - ret['comment'] = msg.format(result_update['error']['message']) + if "error" in result_update: + msg = "Failed to update ENI groups: {1}." + ret["result"] = False + ret["comment"] = msg.format(result_update["error"]["message"]) else: - ret['comment'] = 'Updated ENI groups.' - ret['changes']['groups'] = { - 'old': group_ids, - 'new': _groups - } + ret["comment"] = "Updated ENI groups." + ret["changes"]["groups"] = {"old": group_ids, "new": _groups} return ret def eni_absent( - name, - release_eip=False, - region=None, - key=None, - keyid=None, - profile=None): - ''' + name, release_eip=False, region=None, key=None, keyid=None, profile=None +): + """ Ensure the EC2 ENI is absent. .. versionadded:: 2016.3.0 @@ -483,129 +512,178 @@ def eni_absent( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - r = __salt__['boto_ec2.get_network_interface']( + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + r = __salt__["boto_ec2.get_network_interface"]( name=name, region=region, key=key, keyid=keyid, profile=profile ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Error when attempting to find eni: {0}.'.format( - r['error']['message'] + if "error" in r: + ret["result"] = False + ret["comment"] = "Error when attempting to find eni: {0}.".format( + r["error"]["message"] ) return ret - if not r['result']: - if __opts__['test']: - ret['comment'] = 'ENI is set to be deleted.' - ret['result'] = None + if not r["result"]: + if __opts__["test"]: + ret["comment"] = "ENI is set to be deleted." + ret["result"] = None return ret else: - if __opts__['test']: - ret['comment'] = 'ENI is set to be deleted.' - if release_eip and 'allocationId' in r['result']: - ret['comment'] = ' '.join([ret['comment'], 'Allocated/associated EIP is set to be released']) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ENI is set to be deleted." + if release_eip and "allocationId" in r["result"]: + ret["comment"] = " ".join( + [ret["comment"], "Allocated/associated EIP is set to be released"] + ) + ret["result"] = None return ret - if 'id' in r['result']['attachment']: - result_detach = __salt__['boto_ec2.detach_network_interface']( - name=name, force=True, region=region, key=key, - keyid=keyid, profile=profile + if "id" in r["result"]["attachment"]: + result_detach = __salt__["boto_ec2.detach_network_interface"]( + name=name, + force=True, + region=region, + key=key, + keyid=keyid, + profile=profile, ) - if 'error' in result_detach: - ret['result'] = False - ret['comment'] = 'Failed to detach ENI: {0}'.format( - result_detach['error']['message'] + if "error" in result_detach: + ret["result"] = False + ret["comment"] = "Failed to detach ENI: {0}".format( + result_detach["error"]["message"] ) return ret # TODO: Ensure the detach occurs before continuing - result_delete = __salt__['boto_ec2.delete_network_interface']( - name=name, region=region, key=key, - keyid=keyid, profile=profile + result_delete = __salt__["boto_ec2.delete_network_interface"]( + name=name, region=region, key=key, keyid=keyid, profile=profile ) - if 'error' in result_delete: - ret['result'] = False - ret['comment'] = 'Failed to delete ENI: {0}'.format( - result_delete['error']['message'] + if "error" in result_delete: + ret["result"] = False + ret["comment"] = "Failed to delete ENI: {0}".format( + result_delete["error"]["message"] ) return ret - ret['comment'] = 'Deleted ENI {0}'.format(name) - ret['changes']['id'] = None - if release_eip and 'allocationId' in r['result']: - _ret = __salt__['boto_ec2.release_eip_address'](public_ip=None, - allocation_id=r['result']['allocationId'], - region=region, - key=key, - keyid=keyid, - profile=profile) + ret["comment"] = "Deleted ENI {0}".format(name) + ret["changes"]["id"] = None + if release_eip and "allocationId" in r["result"]: + _ret = __salt__["boto_ec2.release_eip_address"]( + public_ip=None, + allocation_id=r["result"]["allocationId"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not _ret: - ret['comment'] = ' '.join([ret['comment'], 'Failed to release EIP allocated to the ENI.']) - ret['result'] = False + ret["comment"] = " ".join( + [ret["comment"], "Failed to release EIP allocated to the ENI."] + ) + ret["result"] = False return ret else: - ret['comment'] = ' '.join([ret['comment'], 'EIP released.']) - ret['changes']['eip released'] = True + ret["comment"] = " ".join([ret["comment"], "EIP released."]) + ret["changes"]["eip released"] = True return ret -def snapshot_created(name, ami_name, instance_name, wait_until_available=True, wait_timeout_seconds=300, **kwargs): - ''' +def snapshot_created( + name, + ami_name, + instance_name, + wait_until_available=True, + wait_timeout_seconds=300, + **kwargs +): + """ Create a snapshot from the given instance .. versionadded:: 2016.3.0 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - if not __salt__['boto_ec2.create_image'](ami_name=ami_name, instance_name=instance_name, **kwargs): - ret['comment'] = 'Failed to create new AMI {ami_name}'.format(ami_name=ami_name) - ret['result'] = False + if not __salt__["boto_ec2.create_image"]( + ami_name=ami_name, instance_name=instance_name, **kwargs + ): + ret["comment"] = "Failed to create new AMI {ami_name}".format(ami_name=ami_name) + ret["result"] = False return ret - ret['comment'] = 'Created new AMI {ami_name}'.format(ami_name=ami_name) - ret['changes']['new'] = {ami_name: ami_name} + ret["comment"] = "Created new AMI {ami_name}".format(ami_name=ami_name) + ret["changes"]["new"] = {ami_name: ami_name} if not wait_until_available: return ret starttime = time() while True: - images = __salt__['boto_ec2.find_images'](ami_name=ami_name, return_objs=True, **kwargs) - if images and images[0].state == 'available': + images = __salt__["boto_ec2.find_images"]( + ami_name=ami_name, return_objs=True, **kwargs + ) + if images and images[0].state == "available": break if time() - starttime > wait_timeout_seconds: if images: - ret['comment'] = 'AMI still in state {state} after timeout'.format(state=images[0].state) + ret["comment"] = "AMI still in state {state} after timeout".format( + state=images[0].state + ) else: - ret['comment'] = 'AMI with name {ami_name} not found after timeout.'.format(ami_name=ami_name) - ret['result'] = False + ret[ + "comment" + ] = "AMI with name {ami_name} not found after timeout.".format( + ami_name=ami_name + ) + ret["result"] = False return ret sleep(5) return ret -def instance_present(name, instance_name=None, instance_id=None, image_id=None, - image_name=None, tags=None, key_name=None, - security_groups=None, user_data=None, instance_type=None, - placement=None, kernel_id=None, ramdisk_id=None, - vpc_id=None, vpc_name=None, monitoring_enabled=None, - subnet_id=None, subnet_name=None, private_ip_address=None, - block_device_map=None, disable_api_termination=None, - instance_initiated_shutdown_behavior=None, - placement_group=None, client_token=None, - security_group_ids=None, security_group_names=None, - additional_info=None, tenancy=None, - instance_profile_arn=None, instance_profile_name=None, - ebs_optimized=None, network_interfaces=None, - network_interface_name=None, - network_interface_id=None, - attributes=None, target_state=None, public_ip=None, - allocation_id=None, allocate_eip=False, region=None, - key=None, keyid=None, profile=None): +def instance_present( + name, + instance_name=None, + instance_id=None, + image_id=None, + image_name=None, + tags=None, + key_name=None, + security_groups=None, + user_data=None, + instance_type=None, + placement=None, + kernel_id=None, + ramdisk_id=None, + vpc_id=None, + vpc_name=None, + monitoring_enabled=None, + subnet_id=None, + subnet_name=None, + private_ip_address=None, + block_device_map=None, + disable_api_termination=None, + instance_initiated_shutdown_behavior=None, + placement_group=None, + client_token=None, + security_group_ids=None, + security_group_names=None, + additional_info=None, + tenancy=None, + instance_profile_arn=None, + instance_profile_name=None, + ebs_optimized=None, + network_interfaces=None, + network_interface_name=None, + network_interface_id=None, + attributes=None, + target_state=None, + public_ip=None, + allocation_id=None, + allocate_eip=False, + region=None, + key=None, + keyid=None, + profile=None, +): ### TODO - implement 'target_state={running, stopped}' - ''' + """ Ensure an EC2 instance is running with the given attributes and state. name @@ -761,103 +839,140 @@ def instance_present(name, instance_name=None, instance_id=None, image_id=None, that contains a dict with region, key and keyid. .. versionadded:: 2016.3.0 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} _create = False - running_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped') + running_states = ("pending", "rebooting", "running", "stopping", "stopped") changed_attrs = {} if not salt.utils.data.exactly_one((image_id, image_name)): - raise SaltInvocationError('Exactly one of image_id OR ' - 'image_name must be provided.') - if (public_ip or allocation_id or allocate_eip) and not salt.utils.data.exactly_one((public_ip, allocation_id, allocate_eip)): - raise SaltInvocationError('At most one of public_ip, allocation_id OR ' - 'allocate_eip may be provided.') + raise SaltInvocationError( + "Exactly one of image_id OR " "image_name must be provided." + ) + if (public_ip or allocation_id or allocate_eip) and not salt.utils.data.exactly_one( + (public_ip, allocation_id, allocate_eip) + ): + raise SaltInvocationError( + "At most one of public_ip, allocation_id OR " + "allocate_eip may be provided." + ) if instance_id: - exists = __salt__['boto_ec2.exists'](instance_id=instance_id, region=region, key=key, - keyid=keyid, profile=profile, in_states=running_states) + exists = __salt__["boto_ec2.exists"]( + instance_id=instance_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + in_states=running_states, + ) if not exists: _create = True else: - instances = __salt__['boto_ec2.find_instances'](name=instance_name if instance_name else name, - region=region, key=key, keyid=keyid, profile=profile, - in_states=running_states) + instances = __salt__["boto_ec2.find_instances"]( + name=instance_name if instance_name else name, + region=region, + key=key, + keyid=keyid, + profile=profile, + in_states=running_states, + ) if not instances: _create = True elif len(instances) > 1: - log.debug('Multiple instances matching criteria found - cannot determine a singular instance-id') + log.debug( + "Multiple instances matching criteria found - cannot determine a singular instance-id" + ) instance_id = None # No way to know, we'll just have to bail later.... else: instance_id = instances[0] if _create: - if __opts__['test']: - ret['comment'] = 'The instance {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The instance {0} is set to be created.".format(name) + ret["result"] = None return ret if image_name: - args = {'ami_name': image_name, 'region': region, 'key': key, - 'keyid': keyid, 'profile': profile} - image_ids = __salt__['boto_ec2.find_images'](**args) + args = { + "ami_name": image_name, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } + image_ids = __salt__["boto_ec2.find_images"](**args) if image_ids: image_id = image_ids[0] else: image_id = image_name - r = __salt__['boto_ec2.run'](image_id, instance_name if instance_name else name, - tags=tags, key_name=key_name, - security_groups=security_groups, user_data=user_data, - instance_type=instance_type, placement=placement, - kernel_id=kernel_id, ramdisk_id=ramdisk_id, vpc_id=vpc_id, - vpc_name=vpc_name, monitoring_enabled=monitoring_enabled, - subnet_id=subnet_id, subnet_name=subnet_name, - private_ip_address=private_ip_address, - block_device_map=block_device_map, - disable_api_termination=disable_api_termination, - instance_initiated_shutdown_behavior=instance_initiated_shutdown_behavior, - placement_group=placement_group, client_token=client_token, - security_group_ids=security_group_ids, - security_group_names=security_group_names, - additional_info=additional_info, tenancy=tenancy, - instance_profile_arn=instance_profile_arn, - instance_profile_name=instance_profile_name, - ebs_optimized=ebs_optimized, network_interfaces=network_interfaces, - network_interface_name=network_interface_name, - network_interface_id=network_interface_id, - region=region, key=key, keyid=keyid, profile=profile) - if not r or 'instance_id' not in r: - ret['result'] = False - ret['comment'] = 'Failed to create instance {0}.'.format(instance_name if instance_name else name) + r = __salt__["boto_ec2.run"]( + image_id, + instance_name if instance_name else name, + tags=tags, + key_name=key_name, + security_groups=security_groups, + user_data=user_data, + instance_type=instance_type, + placement=placement, + kernel_id=kernel_id, + ramdisk_id=ramdisk_id, + vpc_id=vpc_id, + vpc_name=vpc_name, + monitoring_enabled=monitoring_enabled, + subnet_id=subnet_id, + subnet_name=subnet_name, + private_ip_address=private_ip_address, + block_device_map=block_device_map, + disable_api_termination=disable_api_termination, + instance_initiated_shutdown_behavior=instance_initiated_shutdown_behavior, + placement_group=placement_group, + client_token=client_token, + security_group_ids=security_group_ids, + security_group_names=security_group_names, + additional_info=additional_info, + tenancy=tenancy, + instance_profile_arn=instance_profile_arn, + instance_profile_name=instance_profile_name, + ebs_optimized=ebs_optimized, + network_interfaces=network_interfaces, + network_interface_name=network_interface_name, + network_interface_id=network_interface_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r or "instance_id" not in r: + ret["result"] = False + ret["comment"] = "Failed to create instance {0}.".format( + instance_name if instance_name else name + ) return ret - instance_id = r['instance_id'] - ret['changes'] = {'old': {}, 'new': {}} - ret['changes']['old']['instance_id'] = None - ret['changes']['new']['instance_id'] = instance_id + instance_id = r["instance_id"] + ret["changes"] = {"old": {}, "new": {}} + ret["changes"]["old"]["instance_id"] = None + ret["changes"]["new"]["instance_id"] = instance_id # To avoid issues we only allocate new EIPs at instance creation. # This might miss situations where an instance is initially created # created without and one is added later, but the alternative is the # risk of EIPs allocated at every state run. if allocate_eip: - if __opts__['test']: - ret['comment'] = 'New EIP would be allocated.' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "New EIP would be allocated." + ret["result"] = None return ret - domain = 'vpc' if vpc_id or vpc_name else None - r = __salt__['boto_ec2.allocate_eip_address']( - domain=domain, region=region, key=key, keyid=keyid, - profile=profile) + domain = "vpc" if vpc_id or vpc_name else None + r = __salt__["boto_ec2.allocate_eip_address"]( + domain=domain, region=region, key=key, keyid=keyid, profile=profile + ) if not r: - ret['result'] = False - ret['comment'] = 'Failed to allocate new EIP.' + ret["result"] = False + ret["comment"] = "Failed to allocate new EIP." return ret - allocation_id = r['allocation_id'] - log.info("New EIP with address %s allocated.", r['public_ip']) + allocation_id = r["allocation_id"] + log.info("New EIP with address %s allocated.", r["public_ip"]) else: log.info("EIP not requested.") @@ -866,132 +981,210 @@ def instance_present(name, instance_name=None, instance_id=None, image_id=None, tries = 10 secs = 3 for t in range(tries): - r = __salt__['boto_ec2.get_eip_address_info']( - addresses=public_ip, allocation_ids=allocation_id, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_ec2.get_eip_address_info"]( + addresses=public_ip, + allocation_ids=allocation_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if r: break else: log.info( - 'Waiting up to %s secs for new EIP %s to become available', - tries * secs, public_ip or allocation_id + "Waiting up to %s secs for new EIP %s to become available", + tries * secs, + public_ip or allocation_id, ) time.sleep(secs) if not r: - ret['result'] = False - ret['comment'] = 'Failed to lookup EIP {0}.'.format(public_ip or allocation_id) + ret["result"] = False + ret["comment"] = "Failed to lookup EIP {0}.".format( + public_ip or allocation_id + ) return ret - ip = r[0]['public_ip'] - if r[0].get('instance_id'): - if r[0]['instance_id'] != instance_id: - ret['result'] = False - ret['comment'] = ('EIP {0} is already associated with instance ' - '{1}.'.format(public_ip if public_ip else - allocation_id, r[0]['instance_id'])) + ip = r[0]["public_ip"] + if r[0].get("instance_id"): + if r[0]["instance_id"] != instance_id: + ret["result"] = False + ret["comment"] = ( + "EIP {0} is already associated with instance " + "{1}.".format( + public_ip if public_ip else allocation_id, r[0]["instance_id"] + ) + ) return ret else: - if __opts__['test']: - ret['comment'] = 'Instance {0} to be updated.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Instance {0} to be updated.".format(name) + ret["result"] = None return ret - r = __salt__['boto_ec2.associate_eip_address']( - instance_id=instance_id, public_ip=public_ip, - allocation_id=allocation_id, region=region, key=key, - keyid=keyid, profile=profile) + r = __salt__["boto_ec2.associate_eip_address"]( + instance_id=instance_id, + public_ip=public_ip, + allocation_id=allocation_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if r: - if 'new' not in ret['changes']: - ret['changes']['new'] = {} - ret['changes']['new']['public_ip'] = ip + if "new" not in ret["changes"]: + ret["changes"]["new"] = {} + ret["changes"]["new"]["public_ip"] = ip else: - ret['result'] = False - ret['comment'] = 'Failed to attach EIP to instance {0}.'.format( - instance_name if instance_name else name) + ret["result"] = False + ret["comment"] = "Failed to attach EIP to instance {0}.".format( + instance_name if instance_name else name + ) return ret if attributes: for k, v in six.iteritems(attributes): - curr = __salt__['boto_ec2.get_attribute'](k, instance_id=instance_id, region=region, key=key, - keyid=keyid, profile=profile) + curr = __salt__["boto_ec2.get_attribute"]( + k, + instance_id=instance_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) curr = {} if not isinstance(curr, dict) else curr if curr.get(k) == v: continue else: - if __opts__['test']: - changed_attrs[k] = 'The instance attribute {0} is set to be changed from \'{1}\' to \'{2}\'.'.format( - k, curr.get(k), v) + if __opts__["test"]: + changed_attrs[ + k + ] = "The instance attribute {0} is set to be changed from '{1}' to '{2}'.".format( + k, curr.get(k), v + ) continue try: - r = __salt__['boto_ec2.set_attribute'](attribute=k, attribute_value=v, - instance_id=instance_id, region=region, - key=key, keyid=keyid, profile=profile) + r = __salt__["boto_ec2.set_attribute"]( + attribute=k, + attribute_value=v, + instance_id=instance_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) except SaltInvocationError as e: - ret['result'] = False - ret['comment'] = 'Failed to set attribute {0} to {1} on instance {2}.'.format(k, v, instance_name) + ret["result"] = False + ret[ + "comment" + ] = "Failed to set attribute {0} to {1} on instance {2}.".format( + k, v, instance_name + ) return ret - ret['changes'] = ret['changes'] if ret['changes'] else {'old': {}, 'new': {}} - ret['changes']['old'][k] = curr.get(k) - ret['changes']['new'][k] = v + ret["changes"] = ( + ret["changes"] if ret["changes"] else {"old": {}, "new": {}} + ) + ret["changes"]["old"][k] = curr.get(k) + ret["changes"]["new"][k] = v - if __opts__['test']: + if __opts__["test"]: if changed_attrs: - ret['changes']['new'] = changed_attrs - ret['result'] = None + ret["changes"]["new"] = changed_attrs + ret["result"] = None else: - ret['comment'] = 'Instance {0} is in the correct state'.format(instance_name if instance_name else name) - ret['result'] = True + ret["comment"] = "Instance {0} is in the correct state".format( + instance_name if instance_name else name + ) + ret["result"] = True if tags and instance_id is not None: tags = dict(tags) - curr_tags = dict(__salt__['boto_ec2.get_all_tags'](filters={'resource-id': instance_id}, - region=region, key=key, keyid=keyid, profile=profile).get(instance_id, {})) + curr_tags = dict( + __salt__["boto_ec2.get_all_tags"]( + filters={"resource-id": instance_id}, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get(instance_id, {}) + ) current = set(curr_tags.keys()) desired = set(tags.keys()) - remove = list(current - desired) # Boto explicitly requires a list here and can't cope with a set... + remove = list( + current - desired + ) # Boto explicitly requires a list here and can't cope with a set... add = dict([(t, tags[t]) for t in desired - current]) replace = dict([(t, tags[t]) for t in tags if tags.get(t) != curr_tags.get(t)]) # Tag keys are unique despite the bizarre semantics uses which make it LOOK like they could be duplicative. add.update(replace) if add or remove: - if __opts__['test']: - ret['changes']['old'] = ret['changes']['old'] if 'old' in ret['changes'] else {} - ret['changes']['new'] = ret['changes']['new'] if 'new' in ret['changes'] else {} - ret['changes']['old']['tags'] = curr_tags - ret['changes']['new']['tags'] = tags - ret['comment'] += ' Tags would be updated on instance {0}.'.format(instance_name if - instance_name else name) + if __opts__["test"]: + ret["changes"]["old"] = ( + ret["changes"]["old"] if "old" in ret["changes"] else {} + ) + ret["changes"]["new"] = ( + ret["changes"]["new"] if "new" in ret["changes"] else {} + ) + ret["changes"]["old"]["tags"] = curr_tags + ret["changes"]["new"]["tags"] = tags + ret["comment"] += " Tags would be updated on instance {0}.".format( + instance_name if instance_name else name + ) else: if remove: - if not __salt__['boto_ec2.delete_tags'](resource_ids=instance_id, tags=remove, - region=region, key=key, keyid=keyid, - profile=profile): - msg = "Error while deleting tags on instance {0}".format(instance_name if - instance_name else name) + if not __salt__["boto_ec2.delete_tags"]( + resource_ids=instance_id, + tags=remove, + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + msg = "Error while deleting tags on instance {0}".format( + instance_name if instance_name else name + ) log.error(msg) - ret['comment'] += ' ' + msg - ret['result'] = False + ret["comment"] += " " + msg + ret["result"] = False return ret if add: - if not __salt__['boto_ec2.create_tags'](resource_ids=instance_id, tags=add, - region=region, key=key, keyid=keyid, - profile=profile): - msg = "Error while creating tags on instance {0}".format(instance_name if - instance_name else name) + if not __salt__["boto_ec2.create_tags"]( + resource_ids=instance_id, + tags=add, + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + msg = "Error while creating tags on instance {0}".format( + instance_name if instance_name else name + ) log.error(msg) - ret['comment'] += ' ' + msg - ret['result'] = False + ret["comment"] += " " + msg + ret["result"] = False return ret - ret['changes']['old'] = ret['changes']['old'] if 'old' in ret['changes'] else {} - ret['changes']['new'] = ret['changes']['new'] if 'new' in ret['changes'] else {} - ret['changes']['old']['tags'] = curr_tags - ret['changes']['new']['tags'] = tags + ret["changes"]["old"] = ( + ret["changes"]["old"] if "old" in ret["changes"] else {} + ) + ret["changes"]["new"] = ( + ret["changes"]["new"] if "new" in ret["changes"] else {} + ) + ret["changes"]["old"]["tags"] = curr_tags + ret["changes"]["new"]["tags"] = tags return ret -def instance_absent(name, instance_name=None, instance_id=None, - release_eip=False, region=None, key=None, keyid=None, - profile=None, filters=None): - ''' +def instance_absent( + name, + instance_name=None, + instance_id=None, + release_eip=False, + region=None, + key=None, + keyid=None, + profile=None, + filters=None, +): + """ Ensure an EC2 instance does not exist (is stopped and removed). .. versionchanged:: 2016.11.0 @@ -1023,101 +1216,143 @@ def instance_absent(name, instance_name=None, instance_id=None, - filters: vpc-id: vpc-abcdef12 - ''' + """ ### TODO - Implement 'force' option?? Would automagically turn off ### 'disableApiTermination', as needed, before trying to delete. - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } - running_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped') + ret = {"name": name, "result": True, "comment": "", "changes": {}} + running_states = ("pending", "rebooting", "running", "stopping", "stopped") if not instance_id: try: - instance_id = __salt__['boto_ec2.get_id'](name=instance_name if instance_name else name, - region=region, key=key, keyid=keyid, - profile=profile, in_states=running_states, - filters=filters) + instance_id = __salt__["boto_ec2.get_id"]( + name=instance_name if instance_name else name, + region=region, + key=key, + keyid=keyid, + profile=profile, + in_states=running_states, + filters=filters, + ) except CommandExecutionError as e: - ret['result'] = None - ret['comment'] = ("Couldn't determine current status of instance " - "{0}.".format(instance_name or name)) + ret["result"] = None + ret["comment"] = ( + "Couldn't determine current status of instance " + "{0}.".format(instance_name or name) + ) return ret - instances = __salt__['boto_ec2.find_instances'](instance_id=instance_id, region=region, - key=key, keyid=keyid, profile=profile, - return_objs=True, filters=filters) + instances = __salt__["boto_ec2.find_instances"]( + instance_id=instance_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + return_objs=True, + filters=filters, + ) if not instances: - ret['result'] = True - ret['comment'] = 'Instance {0} is already gone.'.format(instance_id) + ret["result"] = True + ret["comment"] = "Instance {0} is already gone.".format(instance_id) return ret instance = instances[0] ### Honor 'disableApiTermination' - if you want to override it, first use set_attribute() to turn it off - no_can_do = __salt__['boto_ec2.get_attribute']('disableApiTermination', instance_id=instance_id, - region=region, key=key, keyid=keyid, profile=profile) - if no_can_do.get('disableApiTermination') is True: - ret['result'] = False - ret['comment'] = 'Termination of instance {0} via the API is disabled.'.format(instance_id) + no_can_do = __salt__["boto_ec2.get_attribute"]( + "disableApiTermination", + instance_id=instance_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if no_can_do.get("disableApiTermination") is True: + ret["result"] = False + ret["comment"] = "Termination of instance {0} via the API is disabled.".format( + instance_id + ) return ret - if __opts__['test']: - ret['comment'] = 'The instance {0} is set to be deleted.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The instance {0} is set to be deleted.".format(name) + ret["result"] = None return ret - r = __salt__['boto_ec2.terminate'](instance_id=instance_id, name=instance_name, region=region, - key=key, keyid=keyid, profile=profile) + r = __salt__["boto_ec2.terminate"]( + instance_id=instance_id, + name=instance_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not r: - ret['result'] = False - ret['comment'] = 'Failed to terminate instance {0}.'.format(instance_id) + ret["result"] = False + ret["comment"] = "Failed to terminate instance {0}.".format(instance_id) return ret - ret['changes']['old'] = {'instance_id': instance_id} - ret['changes']['new'] = None + ret["changes"]["old"] = {"instance_id": instance_id} + ret["changes"]["new"] = None if release_eip: - ip = getattr(instance, 'ip_address', None) + ip = getattr(instance, "ip_address", None) if ip: - base_args = {'region': region, 'key': key, 'keyid': keyid, 'profile': profile} + base_args = { + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } public_ip = None alloc_id = None assoc_id = None - if getattr(instance, 'vpc_id', None): - r = __salt__['boto_ec2.get_eip_address_info'](addresses=ip, **base_args) - if r and 'allocation_id' in r[0]: - alloc_id = r[0]['allocation_id'] - assoc_id = r[0].get('association_id') + if getattr(instance, "vpc_id", None): + r = __salt__["boto_ec2.get_eip_address_info"](addresses=ip, **base_args) + if r and "allocation_id" in r[0]: + alloc_id = r[0]["allocation_id"] + assoc_id = r[0].get("association_id") else: # I /believe/ this situation is impossible but let's hedge our bets... - ret['result'] = False - ret['comment'] = "Can't determine AllocationId for address {0}.".format(ip) + ret["result"] = False + ret[ + "comment" + ] = "Can't determine AllocationId for address {0}.".format(ip) return ret else: public_ip = instance.ip_address if assoc_id: # Race here - sometimes the terminate above will already have dropped this - if not __salt__['boto_ec2.disassociate_eip_address'](association_id=assoc_id, - **base_args): + if not __salt__["boto_ec2.disassociate_eip_address"]( + association_id=assoc_id, **base_args + ): log.warning("Failed to disassociate EIP %s.", ip) - if __salt__['boto_ec2.release_eip_address'](allocation_id=alloc_id, public_ip=public_ip, - **base_args): - log.info("Released EIP address %s", public_ip or r[0]['public_ip']) - ret['changes']['old']['public_ip'] = public_ip or r[0]['public_ip'] + if __salt__["boto_ec2.release_eip_address"]( + allocation_id=alloc_id, public_ip=public_ip, **base_args + ): + log.info("Released EIP address %s", public_ip or r[0]["public_ip"]) + ret["changes"]["old"]["public_ip"] = public_ip or r[0]["public_ip"] else: - ret['result'] = False - ret['comment'] = "Failed to release EIP {0}.".format(ip) + ret["result"] = False + ret["comment"] = "Failed to release EIP {0}.".format(ip) return ret return ret -def volume_absent(name, volume_name=None, volume_id=None, instance_name=None, - instance_id=None, device=None, region=None, key=None, keyid=None, profile=None): - ''' +def volume_absent( + name, + volume_name=None, + volume_id=None, + instance_name=None, + instance_id=None, + device=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the EC2 volume is detached and absent. .. versionadded:: 2016.11.0 @@ -1157,70 +1392,82 @@ def volume_absent(name, volume_name=None, volume_id=None, instance_name=None, A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} filters = {} - running_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped') + running_states = ("pending", "rebooting", "running", "stopping", "stopped") - if not salt.utils.data.exactly_one((volume_name, volume_id, instance_name, instance_id)): - raise SaltInvocationError("Exactly one of 'volume_name', 'volume_id', " - "'instance_name', or 'instance_id' must be provided.") + if not salt.utils.data.exactly_one( + (volume_name, volume_id, instance_name, instance_id) + ): + raise SaltInvocationError( + "Exactly one of 'volume_name', 'volume_id', " + "'instance_name', or 'instance_id' must be provided." + ) if (instance_name or instance_id) and not device: - raise SaltInvocationError("Parameter 'device' is required when either " - "'instance_name' or 'instance_id' is specified.") + raise SaltInvocationError( + "Parameter 'device' is required when either " + "'instance_name' or 'instance_id' is specified." + ) if volume_id: - filters.update({'volume-id': volume_id}) + filters.update({"volume-id": volume_id}) if volume_name: - filters.update({'tag:Name': volume_name}) + filters.update({"tag:Name": volume_name}) if instance_name: - instance_id = __salt__['boto_ec2.get_id']( - name=instance_name, region=region, key=key, keyid=keyid, - profile=profile, in_states=running_states) + instance_id = __salt__["boto_ec2.get_id"]( + name=instance_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + in_states=running_states, + ) if not instance_id: - ret['comment'] = ('Instance with Name {0} not found. Assuming ' - 'associated volumes gone.'.format(instance_name)) + ret["comment"] = ( + "Instance with Name {0} not found. Assuming " + "associated volumes gone.".format(instance_name) + ) return ret if instance_id: - filters.update({'attachment.instance-id': instance_id}) + filters.update({"attachment.instance-id": instance_id}) if device: - filters.update({'attachment.device': device}) + filters.update({"attachment.device": device}) - args = {'region': region, 'key': key, 'keyid': keyid, 'profile': profile} + args = {"region": region, "key": key, "keyid": keyid, "profile": profile} - vols = __salt__['boto_ec2.get_all_volumes'](filters=filters, **args) + vols = __salt__["boto_ec2.get_all_volumes"](filters=filters, **args) if len(vols) < 1: - ret['comment'] = 'Volume matching criteria not found, assuming already absent' + ret["comment"] = "Volume matching criteria not found, assuming already absent" return ret if len(vols) > 1: - msg = "More than one volume matched criteria, can't continue in state {0}".format(name) + msg = "More than one volume matched criteria, can't continue in state {0}".format( + name + ) log.error(msg) - ret['comment'] = msg - ret['result'] = False + ret["comment"] = msg + ret["result"] = False return ret vol = vols[0] - log.info('Matched Volume ID %s', vol) + log.info("Matched Volume ID %s", vol) - if __opts__['test']: - ret['comment'] = 'The volume {0} is set to be deleted.'.format(vol) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The volume {0} is set to be deleted.".format(vol) + ret["result"] = None return ret - if __salt__['boto_ec2.delete_volume'](volume_id=vol, force=True, **args): - ret['comment'] = 'Volume {0} deleted.'.format(vol) - ret['changes'] = {'old': {'volume_id': vol}, 'new': {'volume_id': None}} + if __salt__["boto_ec2.delete_volume"](volume_id=vol, force=True, **args): + ret["comment"] = "Volume {0} deleted.".format(vol) + ret["changes"] = {"old": {"volume_id": vol}, "new": {"volume_id": None}} else: - ret['comment'] = 'Error deleting volume {0}.'.format(vol) - ret['result'] = False + ret["comment"] = "Error deleting volume {0}.".format(vol) + ret["result"] = False return ret -def volumes_tagged(name, tag_maps, authoritative=False, region=None, key=None, - keyid=None, profile=None): - ''' +def volumes_tagged( + name, tag_maps, authoritative=False, region=None, key=None, keyid=None, profile=None +): + """ Ensure EC2 volume(s) matching the given filters have the defined tags. .. versionadded:: 2016.11.0 @@ -1276,44 +1523,60 @@ def volumes_tagged(name, tag_maps, authoritative=False, region=None, key=None, A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } - args = {'tag_maps': tag_maps, 'authoritative': authoritative, - 'region': region, 'key': key, 'keyid': keyid, 'profile': profile} + ret = {"name": name, "result": True, "comment": "", "changes": {}} + args = { + "tag_maps": tag_maps, + "authoritative": authoritative, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, + } - if __opts__['test']: - args['dry_run'] = True - r = __salt__['boto_ec2.set_volumes_tags'](**args) - if r['success']: - if r.get('changes'): - ret['comment'] = 'Tags would be updated.' - ret['changes'] = r['changes'] - ret['result'] = None + if __opts__["test"]: + args["dry_run"] = True + r = __salt__["boto_ec2.set_volumes_tags"](**args) + if r["success"]: + if r.get("changes"): + ret["comment"] = "Tags would be updated." + ret["changes"] = r["changes"] + ret["result"] = None else: - ret['comment'] = 'Error validating requested volume tags.' - ret['result'] = False + ret["comment"] = "Error validating requested volume tags." + ret["result"] = False return ret - r = __salt__['boto_ec2.set_volumes_tags'](**args) - if r['success']: - if r.get('changes'): - ret['comment'] = 'Tags applied.' - ret['changes'] = r['changes'] + r = __salt__["boto_ec2.set_volumes_tags"](**args) + if r["success"]: + if r.get("changes"): + ret["comment"] = "Tags applied." + ret["changes"] = r["changes"] else: - ret['comment'] = 'Error updating requested volume tags.' - ret['result'] = False + ret["comment"] = "Error updating requested volume tags." + ret["result"] = False return ret -def volume_present(name, volume_name=None, volume_id=None, instance_name=None, - instance_id=None, device=None, size=None, snapshot_id=None, - volume_type=None, iops=None, encrypted=False, kms_key_id=None, - region=None, key=None, keyid=None, profile=None): - ''' +def volume_present( + name, + volume_name=None, + volume_id=None, + instance_name=None, + instance_id=None, + device=None, + size=None, + snapshot_id=None, + volume_type=None, + iops=None, + encrypted=False, + kms_key_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the EC2 volume is present and attached. .. @@ -1381,138 +1644,182 @@ def volume_present(name, volume_name=None, volume_id=None, instance_name=None, A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} old_dict = {} new_dict = {} - running_states = ('running', 'stopped') + running_states = ("running", "stopped") if not salt.utils.data.exactly_one((volume_name, volume_id)): - raise SaltInvocationError("Exactly one of 'volume_name', 'volume_id', " - " must be provided.") + raise SaltInvocationError( + "Exactly one of 'volume_name', 'volume_id', " " must be provided." + ) if not salt.utils.data.exactly_one((instance_name, instance_id)): - raise SaltInvocationError("Exactly one of 'instance_name', or 'instance_id'" - " must be provided.") + raise SaltInvocationError( + "Exactly one of 'instance_name', or 'instance_id'" " must be provided." + ) if device is None: raise SaltInvocationError("Parameter 'device' is required.") - args = {'region': region, 'key': key, 'keyid': keyid, 'profile': profile} + args = {"region": region, "key": key, "keyid": keyid, "profile": profile} if instance_name: - instance_id = __salt__['boto_ec2.get_id']( - name=instance_name, in_states=running_states, **args) + instance_id = __salt__["boto_ec2.get_id"]( + name=instance_name, in_states=running_states, **args + ) if not instance_id: - raise SaltInvocationError('Instance with Name {0} not found.'.format(instance_name)) + raise SaltInvocationError( + "Instance with Name {0} not found.".format(instance_name) + ) - instances = __salt__['boto_ec2.find_instances'](instance_id=instance_id, return_objs=True, **args) + instances = __salt__["boto_ec2.find_instances"]( + instance_id=instance_id, return_objs=True, **args + ) instance = instances[0] if volume_name: filters = {} - filters.update({'tag:Name': volume_name}) - vols = __salt__['boto_ec2.get_all_volumes'](filters=filters, **args) + filters.update({"tag:Name": volume_name}) + vols = __salt__["boto_ec2.get_all_volumes"](filters=filters, **args) if len(vols) > 1: - msg = "More than one volume matched volume name {0}, can't continue in state {1}".format(volume_name, - name) + msg = "More than one volume matched volume name {0}, can't continue in state {1}".format( + volume_name, name + ) raise SaltInvocationError(msg) if len(vols) < 1: - if __opts__['test']: - ret['comment'] = ('The volume with name {0} is set to be created and attached' - ' on {1}({2}).'.format(volume_name, instance_id, device)) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = ( + "The volume with name {0} is set to be created and attached" + " on {1}({2}).".format(volume_name, instance_id, device) + ) + ret["result"] = None return ret - _rt = __salt__['boto_ec2.create_volume'](zone_name=instance.placement, - size=size, - snapshot_id=snapshot_id, - volume_type=volume_type, - iops=iops, - encrypted=encrypted, - kms_key_id=kms_key_id, - wait_for_creation=True, **args) - if 'result' in _rt: - volume_id = _rt['result'] + _rt = __salt__["boto_ec2.create_volume"]( + zone_name=instance.placement, + size=size, + snapshot_id=snapshot_id, + volume_type=volume_type, + iops=iops, + encrypted=encrypted, + kms_key_id=kms_key_id, + wait_for_creation=True, + **args + ) + if "result" in _rt: + volume_id = _rt["result"] else: - raise SaltInvocationError('Error creating volume with name {0}.'.format(volume_name)) - _rt = __salt__['boto_ec2.set_volumes_tags'](tag_maps=[{ - 'filters': {'volume_ids': [volume_id]}, - 'tags': {'Name': volume_name} - }], **args) - if _rt['success'] is False: - raise SaltInvocationError('Error updating requested volume ' - '{0} with name {1}. {2}'.format(volume_id, - volume_name, - _rt['comment'])) - old_dict['volume_id'] = None - new_dict['volume_id'] = volume_id + raise SaltInvocationError( + "Error creating volume with name {0}.".format(volume_name) + ) + _rt = __salt__["boto_ec2.set_volumes_tags"]( + tag_maps=[ + { + "filters": {"volume_ids": [volume_id]}, + "tags": {"Name": volume_name}, + } + ], + **args + ) + if _rt["success"] is False: + raise SaltInvocationError( + "Error updating requested volume " + "{0} with name {1}. {2}".format( + volume_id, volume_name, _rt["comment"] + ) + ) + old_dict["volume_id"] = None + new_dict["volume_id"] = volume_id else: volume_id = vols[0] - vols = __salt__['boto_ec2.get_all_volumes'](volume_ids=[volume_id], return_objs=True, **args) + vols = __salt__["boto_ec2.get_all_volumes"]( + volume_ids=[volume_id], return_objs=True, **args + ) if len(vols) < 1: - raise SaltInvocationError('Volume {0} do not exist'.format(volume_id)) + raise SaltInvocationError("Volume {0} do not exist".format(volume_id)) vol = vols[0] if vol.zone != instance.placement: - raise SaltInvocationError(('Volume {0} in {1} cannot attach to instance' - ' {2} in {3}.').format(volume_id, - vol.zone, - instance_id, - instance.placement)) + raise SaltInvocationError( + ("Volume {0} in {1} cannot attach to instance" " {2} in {3}.").format( + volume_id, vol.zone, instance_id, instance.placement + ) + ) attach_data = vol.attach_data if attach_data is not None and attach_data.instance_id is not None: if instance_id == attach_data.instance_id and device == attach_data.device: - ret['comment'] = 'The volume {0} is attached on {1}({2}).'.format(volume_id, - instance_id, device) + ret["comment"] = "The volume {0} is attached on {1}({2}).".format( + volume_id, instance_id, device + ) return ret else: - if __opts__['test']: - ret['comment'] = ('The volume {0} is set to be detached' - ' from {1}({2} and attached on {3}({4}).').format(attach_data.instance_id, - attach_data.devic, - volume_id, - instance_id, - device) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = ( + "The volume {0} is set to be detached" + " from {1}({2} and attached on {3}({4})." + ).format( + attach_data.instance_id, + attach_data.devic, + volume_id, + instance_id, + device, + ) + ret["result"] = None return ret - if __salt__['boto_ec2.detach_volume'](volume_id=volume_id, wait_for_detachement=True, **args): - ret['comment'] = 'Volume {0} is detached from {1}({2}).'.format(volume_id, - attach_data.instance_id, - attach_data.device) - old_dict['instance_id'] = attach_data.instance_id - old_dict['device'] = attach_data.device + if __salt__["boto_ec2.detach_volume"]( + volume_id=volume_id, wait_for_detachement=True, **args + ): + ret["comment"] = "Volume {0} is detached from {1}({2}).".format( + volume_id, attach_data.instance_id, attach_data.device + ) + old_dict["instance_id"] = attach_data.instance_id + old_dict["device"] = attach_data.device else: - raise SaltInvocationError(('The volume {0} is already attached on instance {1}({2}).' - ' Failed to detach').format(volume_id, - attach_data.instance_id, - attach_data.device)) + raise SaltInvocationError( + ( + "The volume {0} is already attached on instance {1}({2})." + " Failed to detach" + ).format(volume_id, attach_data.instance_id, attach_data.device) + ) else: - old_dict['instance_id'] = instance_id - old_dict['device'] = None - if __opts__['test']: - ret['comment'] = 'The volume {0} is set to be attached on {1}({2}).'.format(volume_id, - instance_id, - device) - ret['result'] = None + old_dict["instance_id"] = instance_id + old_dict["device"] = None + if __opts__["test"]: + ret["comment"] = "The volume {0} is set to be attached on {1}({2}).".format( + volume_id, instance_id, device + ) + ret["result"] = None return ret - if __salt__['boto_ec2.attach_volume'](volume_id=volume_id, instance_id=instance_id, - device=device, **args): - ret['comment'] = ' '.join([ - ret['comment'], - 'Volume {0} is attached on {1}({2}).'.format(volume_id, instance_id, device)]) - new_dict['instance_id'] = instance_id - new_dict['device'] = device - ret['changes'] = {'old': old_dict, 'new': new_dict} + if __salt__["boto_ec2.attach_volume"]( + volume_id=volume_id, instance_id=instance_id, device=device, **args + ): + ret["comment"] = " ".join( + [ + ret["comment"], + "Volume {0} is attached on {1}({2}).".format( + volume_id, instance_id, device + ), + ] + ) + new_dict["instance_id"] = instance_id + new_dict["device"] = device + ret["changes"] = {"old": old_dict, "new": new_dict} else: - ret['comment'] = 'Error attaching volume {0} to instance {1}({2}).'.format(volume_id, - instance_id, - device) - ret['result'] = False + ret["comment"] = "Error attaching volume {0} to instance {1}({2}).".format( + volume_id, instance_id, device + ) + ret["result"] = False return ret -def private_ips_present(name, network_interface_name=None, network_interface_id=None, - private_ip_addresses=None, allow_reassignment=False, region=None, key=None, - keyid=None, profile=None): - ''' +def private_ips_present( + name, + network_interface_name=None, + network_interface_id=None, + private_ip_addresses=None, + allow_reassignment=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure an ENI has secondary private ip addresses associated with it name @@ -1534,110 +1841,129 @@ def private_ips_present(name, network_interface_name=None, network_interface_id= profile (variable) - A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ if not salt.utils.data.exactly_one((network_interface_name, network_interface_id)): - raise SaltInvocationError("Exactly one of 'network_interface_name', " - "'network_interface_id' must be provided") + raise SaltInvocationError( + "Exactly one of 'network_interface_name', " + "'network_interface_id' must be provided" + ) if not private_ip_addresses: - raise SaltInvocationError("You must provide the private_ip_addresses to associate with the " - "ENI") + raise SaltInvocationError( + "You must provide the private_ip_addresses to associate with the " "ENI" + ) ret = { - 'name': name, - 'result': True, - 'comment': '', - 'changes': {'old': [], 'new': []} + "name": name, + "result": True, + "comment": "", + "changes": {"old": [], "new": []}, } get_eni_args = { - 'name': network_interface_name, - 'network_interface_id': network_interface_id, - 'region': region, - 'key': key, - 'keyid': keyid, - 'profile': profile + "name": network_interface_name, + "network_interface_id": network_interface_id, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, } - eni = __salt__['boto_ec2.get_network_interface'](**get_eni_args) + eni = __salt__["boto_ec2.get_network_interface"](**get_eni_args) # Check if there are any new secondary private ips to add to the eni - if eni and eni.get('result', {}).get('private_ip_addresses'): - for eni_pip in eni['result']['private_ip_addresses']: - ret['changes']['old'].append(eni_pip['private_ip_address']) + if eni and eni.get("result", {}).get("private_ip_addresses"): + for eni_pip in eni["result"]["private_ip_addresses"]: + ret["changes"]["old"].append(eni_pip["private_ip_address"]) ips_to_add = [] for private_ip in private_ip_addresses: - if private_ip not in ret['changes']['old']: + if private_ip not in ret["changes"]["old"]: ips_to_add.append(private_ip) if ips_to_add: - if not __opts__['test']: + if not __opts__["test"]: # Assign secondary private ips to ENI assign_ips_args = { - 'network_interface_id': network_interface_id, - 'private_ip_addresses': ips_to_add, - 'allow_reassignment': allow_reassignment, - 'region': region, - 'key': key, - 'keyid': keyid, - 'profile': profile + "network_interface_id": network_interface_id, + "private_ip_addresses": ips_to_add, + "allow_reassignment": allow_reassignment, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, } - __salt__['boto_ec2.assign_private_ip_addresses'](**assign_ips_args) + __salt__["boto_ec2.assign_private_ip_addresses"](**assign_ips_args) # Verify secondary private ips were properly assigned to ENI - eni = __salt__['boto_ec2.get_network_interface'](**get_eni_args) - if eni and eni.get('result', {}).get('private_ip_addresses', None): - for eni_pip in eni['result']['private_ip_addresses']: - ret['changes']['new'].append(eni_pip['private_ip_address']) + eni = __salt__["boto_ec2.get_network_interface"](**get_eni_args) + if eni and eni.get("result", {}).get("private_ip_addresses", None): + for eni_pip in eni["result"]["private_ip_addresses"]: + ret["changes"]["new"].append(eni_pip["private_ip_address"]) ips_not_added = [] for private_ip in private_ip_addresses: - if private_ip not in ret['changes']['new']: + if private_ip not in ret["changes"]["new"]: ips_not_added.append(private_ip) # Display results if ips_not_added: - ret['result'] = False - ret['comment'] = ('ips on eni: {0}\n' - 'attempted to add: {1}\n' - 'could not add the following ips: {2}\n'.format( - '\n\t- ' + '\n\t- '.join(ret['changes']['new']), - '\n\t- ' + '\n\t- '.join(ips_to_add), - '\n\t- ' + '\n\t- '.join(ips_not_added))) + ret["result"] = False + ret["comment"] = ( + "ips on eni: {0}\n" + "attempted to add: {1}\n" + "could not add the following ips: {2}\n".format( + "\n\t- " + "\n\t- ".join(ret["changes"]["new"]), + "\n\t- " + "\n\t- ".join(ips_to_add), + "\n\t- " + "\n\t- ".join(ips_not_added), + ) + ) else: - ret['comment'] = "added ips: {0}".format( - '\n\t- ' + '\n\t- '.join(ips_to_add)) + ret["comment"] = "added ips: {0}".format( + "\n\t- " + "\n\t- ".join(ips_to_add) + ) # Verify there were changes - if ret['changes']['old'] == ret['changes']['new']: - ret['changes'] = {} + if ret["changes"]["old"] == ret["changes"]["new"]: + ret["changes"] = {} else: # Testing mode, show that there were ips to add - ret['comment'] = ('ips on eni: {0}\n' - 'ips that would be added: {1}\n'.format( - '\n\t- ' + '\n\t- '.join(ret['changes']['old']), - '\n\t- ' + '\n\t- '.join(ips_to_add))) - ret['changes'] = {} - ret['result'] = None + ret["comment"] = ( + "ips on eni: {0}\n" + "ips that would be added: {1}\n".format( + "\n\t- " + "\n\t- ".join(ret["changes"]["old"]), + "\n\t- " + "\n\t- ".join(ips_to_add), + ) + ) + ret["changes"] = {} + ret["result"] = None else: - ret['comment'] = 'ips on eni: {0}'.format( - '\n\t- ' + '\n\t- '.join(ret['changes']['old'])) + ret["comment"] = "ips on eni: {0}".format( + "\n\t- " + "\n\t- ".join(ret["changes"]["old"]) + ) # there were no changes since we did not attempt to remove ips - ret['changes'] = {} + ret["changes"] = {} return ret -def private_ips_absent(name, network_interface_name=None, network_interface_id=None, - private_ip_addresses=None, region=None, key=None, keyid=None, profile=None): +def private_ips_absent( + name, + network_interface_name=None, + network_interface_id=None, + private_ip_addresses=None, + region=None, + key=None, + keyid=None, + profile=None, +): - ''' + """ Ensure an ENI does not have secondary private ip addresses associated with it name @@ -1655,113 +1981,128 @@ def private_ips_absent(name, network_interface_name=None, network_interface_id=N profile (variable) - A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ if not salt.utils.data.exactly_one((network_interface_name, network_interface_id)): - raise SaltInvocationError("Exactly one of 'network_interface_name', " - "'network_interface_id' must be provided") + raise SaltInvocationError( + "Exactly one of 'network_interface_name', " + "'network_interface_id' must be provided" + ) if not private_ip_addresses: - raise SaltInvocationError("You must provide the private_ip_addresses to unassociate with " - "the ENI") + raise SaltInvocationError( + "You must provide the private_ip_addresses to unassociate with " "the ENI" + ) if not isinstance(private_ip_addresses, list): private_ip_addresses = [private_ip_addresses] ret = { - 'name': name, - 'result': True, - 'comment': '', - 'changes': {'new': [], 'old': []} + "name": name, + "result": True, + "comment": "", + "changes": {"new": [], "old": []}, } get_eni_args = { - 'name': network_interface_name, - 'network_interface_id': network_interface_id, - 'region': region, - 'key': key, - 'keyid': keyid, - 'profile': profile + "name": network_interface_name, + "network_interface_id": network_interface_id, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, } - eni = __salt__['boto_ec2.get_network_interface'](**get_eni_args) + eni = __salt__["boto_ec2.get_network_interface"](**get_eni_args) # Check if there are any old private ips to remove from the eni primary_private_ip = None - if eni and eni.get('result', {}).get('private_ip_addresses'): - for eni_pip in eni['result']['private_ip_addresses']: - ret['changes']['old'].append(eni_pip['private_ip_address']) - if eni_pip['primary']: - primary_private_ip = eni_pip['private_ip_address'] + if eni and eni.get("result", {}).get("private_ip_addresses"): + for eni_pip in eni["result"]["private_ip_addresses"]: + ret["changes"]["old"].append(eni_pip["private_ip_address"]) + if eni_pip["primary"]: + primary_private_ip = eni_pip["private_ip_address"] ips_to_remove = [] for private_ip in private_ip_addresses: - if private_ip in ret['changes']['old']: + if private_ip in ret["changes"]["old"]: ips_to_remove.append(private_ip) if private_ip == primary_private_ip: - ret['result'] = False - ret['comment'] = ('You cannot unassign the primary private ip address ({0}) on an ' - 'eni\n' - 'ips on eni: {1}\n' - 'attempted to remove: {2}\n'.format( - primary_private_ip, - '\n\t- ' + '\n\t- '.join(ret['changes']['old']), - '\n\t- ' + '\n\t- '.join(private_ip_addresses))) - ret['changes'] = {} + ret["result"] = False + ret["comment"] = ( + "You cannot unassign the primary private ip address ({0}) on an " + "eni\n" + "ips on eni: {1}\n" + "attempted to remove: {2}\n".format( + primary_private_ip, + "\n\t- " + "\n\t- ".join(ret["changes"]["old"]), + "\n\t- " + "\n\t- ".join(private_ip_addresses), + ) + ) + ret["changes"] = {} return ret if ips_to_remove: - if not __opts__['test']: + if not __opts__["test"]: # Unassign secondary private ips to ENI assign_ips_args = { - 'network_interface_id': network_interface_id, - 'private_ip_addresses': ips_to_remove, - 'region': region, - 'key': key, - 'keyid': keyid, - 'profile': profile + "network_interface_id": network_interface_id, + "private_ip_addresses": ips_to_remove, + "region": region, + "key": key, + "keyid": keyid, + "profile": profile, } - __salt__['boto_ec2.unassign_private_ip_addresses'](**assign_ips_args) + __salt__["boto_ec2.unassign_private_ip_addresses"](**assign_ips_args) # Verify secondary private ips were properly unassigned from ENI - eni = __salt__['boto_ec2.get_network_interface'](**get_eni_args) - if eni and eni.get('result', {}).get('private_ip_addresses', None): - for eni_pip in eni['result']['private_ip_addresses']: - ret['changes']['new'].append(eni_pip['private_ip_address']) + eni = __salt__["boto_ec2.get_network_interface"](**get_eni_args) + if eni and eni.get("result", {}).get("private_ip_addresses", None): + for eni_pip in eni["result"]["private_ip_addresses"]: + ret["changes"]["new"].append(eni_pip["private_ip_address"]) ips_not_removed = [] for private_ip in private_ip_addresses: - if private_ip in ret['changes']['new']: + if private_ip in ret["changes"]["new"]: ips_not_removed.append(private_ip) if ips_not_removed: - ret['result'] = False - ret['comment'] = ('ips on eni: {0}\n' - 'attempted to remove: {1}\n' - 'could not remove the following ips: {2}\n'.format( - '\n\t- ' + '\n\t- '.join(ret['changes']['new']), - '\n\t- ' + '\n\t- '.join(ips_to_remove), - '\n\t- ' + '\n\t- '.join(ips_not_removed))) + ret["result"] = False + ret["comment"] = ( + "ips on eni: {0}\n" + "attempted to remove: {1}\n" + "could not remove the following ips: {2}\n".format( + "\n\t- " + "\n\t- ".join(ret["changes"]["new"]), + "\n\t- " + "\n\t- ".join(ips_to_remove), + "\n\t- " + "\n\t- ".join(ips_not_removed), + ) + ) else: - ret['comment'] = "removed ips: {0}".format('\n\t- ' + '\n\t- '.join(ips_to_remove)) + ret["comment"] = "removed ips: {0}".format( + "\n\t- " + "\n\t- ".join(ips_to_remove) + ) # Verify there were changes - if ret['changes']['old'] == ret['changes']['new']: - ret['changes'] = {} + if ret["changes"]["old"] == ret["changes"]["new"]: + ret["changes"] = {} else: # Testing mode, show that there were ips to remove - ret['comment'] = ('ips on eni: {0}\n' - 'ips that would be removed: {1}\n'.format( - '\n\t- ' + '\n\t- '.join(ret['changes']['old']), - '\n\t- ' + '\n\t- '.join(ips_to_remove))) - ret['changes'] = {} - ret['result'] = None + ret["comment"] = ( + "ips on eni: {0}\n" + "ips that would be removed: {1}\n".format( + "\n\t- " + "\n\t- ".join(ret["changes"]["old"]), + "\n\t- " + "\n\t- ".join(ips_to_remove), + ) + ) + ret["changes"] = {} + ret["result"] = None else: - ret['comment'] = 'ips on network interface: {0}'.format( - '\n\t- ' + '\n\t- '.join(ret['changes']['old'])) + ret["comment"] = "ips on network interface: {0}".format( + "\n\t- " + "\n\t- ".join(ret["changes"]["old"]) + ) # there were no changes since we did not attempt to remove ips - ret['changes'] = {} + ret["changes"] = {} return ret diff --git a/salt/states/boto_elasticache.py b/salt/states/boto_elasticache.py index 2e81f4c7221..4d0cafd67f1 100644 --- a/salt/states/boto_elasticache.py +++ b/salt/states/boto_elasticache.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Elasticache ================== @@ -74,23 +74,23 @@ passed in as a dict, or as a string to pull from pillars or minion config: - profile: keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - if 'boto_elasticache.exists' in __salt__: - return 'boto_elasticache' - else: - return False + """ + if "boto_elasticache.exists" in __salt__: + return "boto_elasticache" + return (False, "boto_elasticache module could not be loaded") def cache_cluster_present(*args, **kwargs): @@ -98,27 +98,28 @@ def cache_cluster_present(*args, **kwargs): def present( - name, - engine=None, - cache_node_type=None, - num_cache_nodes=None, - preferred_availability_zone=None, - port=None, - cache_parameter_group_name=None, - cache_security_group_names=None, - replication_group_id=None, - auto_minor_version_upgrade=True, - security_group_ids=None, - cache_subnet_group_name=None, - engine_version=None, - notification_topic_arn=None, - preferred_maintenance_window=None, - wait=None, - region=None, - key=None, - keyid=None, - profile=None): - ''' + name, + engine=None, + cache_node_type=None, + num_cache_nodes=None, + preferred_availability_zone=None, + port=None, + cache_parameter_group_name=None, + cache_security_group_names=None, + replication_group_id=None, + auto_minor_version_upgrade=True, + security_group_ids=None, + cache_subnet_group_name=None, + engine_version=None, + notification_topic_arn=None, + preferred_maintenance_window=None, + wait=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the cache cluster exists. name @@ -202,41 +203,42 @@ def present( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if cache_security_group_names and cache_subnet_group_name: - _subnet_group = __salt__['boto_elasticache.get_cache_subnet_group']( + _subnet_group = __salt__["boto_elasticache.get_cache_subnet_group"]( cache_subnet_group_name, region, key, keyid, profile ) - vpc_id = _subnet_group['vpc_id'] + vpc_id = _subnet_group["vpc_id"] if not security_group_ids: security_group_ids = [] - _security_group_ids = __salt__['boto_secgroup.convert_to_group_ids']( + _security_group_ids = __salt__["boto_secgroup.convert_to_group_ids"]( groups=cache_security_group_names, vpc_id=vpc_id, region=region, key=key, keyid=keyid, - profile=profile + profile=profile, ) security_group_ids.extend(_security_group_ids) cache_security_group_names = None - config = __salt__['boto_elasticache.get_config'](name, region, key, keyid, - profile) + config = __salt__["boto_elasticache.get_config"](name, region, key, keyid, profile) if config is None: - msg = 'Failed to retrieve cache cluster info from AWS.' - ret['comment'] = msg - ret['result'] = None + msg = "Failed to retrieve cache cluster info from AWS." + ret["comment"] = msg + ret["result"] = None return ret elif not config: - if __opts__['test']: - msg = 'Cache cluster {0} is set to be created.'.format(name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Cache cluster {0} is set to be created.".format(name) + ret["comment"] = msg + ret["result"] = None return ret - created = __salt__['boto_elasticache.create']( - name=name, num_cache_nodes=num_cache_nodes, - cache_node_type=cache_node_type, engine=engine, + created = __salt__["boto_elasticache.create"]( + name=name, + num_cache_nodes=num_cache_nodes, + cache_node_type=cache_node_type, + engine=engine, replication_group_id=replication_group_id, engine_version=engine_version, cache_parameter_group_name=cache_parameter_group_name, @@ -245,28 +247,43 @@ def present( security_group_ids=security_group_ids, preferred_availability_zone=preferred_availability_zone, preferred_maintenance_window=preferred_maintenance_window, - port=port, notification_topic_arn=notification_topic_arn, + port=port, + notification_topic_arn=notification_topic_arn, auto_minor_version_upgrade=auto_minor_version_upgrade, - wait=wait, region=region, key=key, keyid=keyid, profile=profile) + wait=wait, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if created: - ret['changes']['old'] = None - config = __salt__['boto_elasticache.get_config'](name, region, key, - keyid, profile) - ret['changes']['new'] = config + ret["changes"]["old"] = None + config = __salt__["boto_elasticache.get_config"]( + name, region, key, keyid, profile + ) + ret["changes"]["new"] = config else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} cache cluster.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} cache cluster.".format(name) return ret # TODO: support modification of existing elasticache clusters else: - ret['comment'] = 'Cache cluster {0} is present.'.format(name) + ret["comment"] = "Cache cluster {0} is present.".format(name) return ret -def subnet_group_present(name, subnet_ids=None, subnet_names=None, - description=None, tags=None, region=None, - key=None, keyid=None, profile=None): - ''' +def subnet_group_present( + name, + subnet_ids=None, + subnet_names=None, + description=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure ElastiCache subnet group exists. .. versionadded:: 2015.8.0 @@ -298,34 +315,37 @@ def subnet_group_present(name, subnet_ids=None, subnet_names=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - exists = __salt__['boto_elasticache.subnet_group_exists'](name=name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile) + exists = __salt__["boto_elasticache.subnet_group_exists"]( + name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile + ) if not exists: - if __opts__['test']: - ret['comment'] = 'Subnet group {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Subnet group {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_elasticache.create_subnet_group'](name=name, subnet_ids=subnet_ids, - subnet_names=subnet_names, - description=description, tags=tags, - region=region, key=key, keyid=keyid, - profile=profile) + created = __salt__["boto_elasticache.create_subnet_group"]( + name=name, + subnet_ids=subnet_ids, + subnet_names=subnet_names, + description=description, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not created: - ret['result'] = False - ret['comment'] = 'Failed to create {0} subnet group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} subnet group.".format(name) return ret - ret['changes']['old'] = None - ret['changes']['new'] = name - ret['comment'] = 'Subnet group {0} created.'.format(name) + ret["changes"]["old"] = None + ret["changes"]["new"] = name + ret["comment"] = "Subnet group {0} created.".format(name) return ret - ret['comment'] = 'Subnet group present.' + ret["comment"] = "Subnet group present." return ret @@ -334,7 +354,7 @@ def cache_cluster_absent(*args, **kwargs): def absent(name, wait=True, region=None, key=None, keyid=None, profile=None): - ''' + """ Ensure the named elasticache cluster is deleted. name @@ -356,26 +376,27 @@ def absent(name, wait=True, region=None, key=None, keyid=None, profile=None): profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_present = __salt__['boto_elasticache.exists'](name, region, key, keyid, profile) + is_present = __salt__["boto_elasticache.exists"](name, region, key, keyid, profile) if is_present: - if __opts__['test']: - ret['comment'] = 'Cache cluster {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Cache cluster {0} is set to be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_elasticache.delete'](name, wait, region, key, - keyid, profile) + deleted = __salt__["boto_elasticache.delete"]( + name, wait, region, key, keyid, profile + ) if deleted: - ret['changes']['old'] = name - ret['changes']['new'] = None + ret["changes"]["old"] = name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} cache cluster.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} cache cluster.".format(name) else: - ret['comment'] = '{0} does not exist in {1}.'.format(name, region) + ret["comment"] = "{0} does not exist in {1}.".format(name, region) return ret @@ -383,9 +404,17 @@ def replication_group_present(*args, **kwargs): return creategroup(*args, **kwargs) -def creategroup(name, primary_cluster_id, replication_group_description, wait=None, - region=None, key=None, keyid=None, profile=None): - ''' +def creategroup( + name, + primary_cluster_id, + replication_group_description, + wait=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the a replication group is create. name @@ -412,88 +441,107 @@ def creategroup(name, primary_cluster_id, replication_group_description, wait=No profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} - is_present = __salt__['boto_elasticache.group_exists'](name, region, key, keyid, - profile) + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} + is_present = __salt__["boto_elasticache.group_exists"]( + name, region, key, keyid, profile + ) if not is_present: - if __opts__['test']: - ret['comment'] = 'Replication {0} is set to be created.'.format( - name) - ret['result'] = None - created = __salt__['boto_elasticache.create_replication_group'](name, primary_cluster_id, - replication_group_description, - wait, region, key, keyid, profile) + if __opts__["test"]: + ret["comment"] = "Replication {0} is set to be created.".format(name) + ret["result"] = None + created = __salt__["boto_elasticache.create_replication_group"]( + name, + primary_cluster_id, + replication_group_description, + wait, + region, + key, + keyid, + profile, + ) if created: - config = __salt__['boto_elasticache.describe_replication_group'](name, region, key, keyid, profile) - ret['changes']['old'] = None - ret['changes']['new'] = config - ret['result'] = True + config = __salt__["boto_elasticache.describe_replication_group"]( + name, region, key, keyid, profile + ) + ret["changes"]["old"] = None + ret["changes"]["new"] = config + ret["result"] = True else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} replication group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} replication group.".format(name) else: - ret['comment'] = '{0} replication group exists .'.format(name) - ret['result'] = True + ret["comment"] = "{0} replication group exists .".format(name) + ret["result"] = True return ret -def subnet_group_absent(name, tags=None, region=None, key=None, keyid=None, profile=None): - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } +def subnet_group_absent( + name, tags=None, region=None, key=None, keyid=None, profile=None +): + ret = {"name": name, "result": True, "comment": "", "changes": {}} - exists = __salt__['boto_elasticache.subnet_group_exists'](name=name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile) + exists = __salt__["boto_elasticache.subnet_group_exists"]( + name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile + ) if not exists: - ret['result'] = True - ret['comment'] = '{0} ElastiCache subnet group does not exist.'.format(name) + ret["result"] = True + ret["comment"] = "{0} ElastiCache subnet group does not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'ElastiCache subnet group {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ElastiCache subnet group {0} is set to be removed.".format( + name + ) + ret["result"] = None return ret - deleted = __salt__['boto_elasticache.delete_subnet_group'](name, region, key, keyid, profile) + deleted = __salt__["boto_elasticache.delete_subnet_group"]( + name, region, key, keyid, profile + ) if not deleted: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} ElastiCache subnet group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} ElastiCache subnet group.".format(name) return ret - ret['changes']['old'] = name - ret['changes']['new'] = None - ret['comment'] = 'ElastiCache subnet group {0} deleted.'.format(name) + ret["changes"]["old"] = name + ret["changes"]["new"] = None + ret["comment"] = "ElastiCache subnet group {0} deleted.".format(name) return ret -def replication_group_absent(name, tags=None, region=None, key=None, keyid=None, profile=None): - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } +def replication_group_absent( + name, tags=None, region=None, key=None, keyid=None, profile=None +): + ret = {"name": name, "result": True, "comment": "", "changes": {}} - exists = __salt__['boto_elasticache.group_exists'](name=name, region=region, key=key, - keyid=keyid, profile=profile) + exists = __salt__["boto_elasticache.group_exists"]( + name=name, region=region, key=key, keyid=keyid, profile=profile + ) if not exists: - ret['result'] = True - ret['comment'] = '{0} ElastiCache replication group does not exist.'.format(name) - log.info(ret['comment']) + ret["result"] = True + ret["comment"] = "{0} ElastiCache replication group does not exist.".format( + name + ) + log.info(ret["comment"]) return ret - if __opts__['test']: - ret['comment'] = 'ElastiCache replication group {0} is set to be removed.'.format(name) - ret['result'] = True + if __opts__["test"]: + ret[ + "comment" + ] = "ElastiCache replication group {0} is set to be removed.".format(name) + ret["result"] = True return ret - deleted = __salt__['boto_elasticache.delete_replication_group'](name, region, key, keyid, profile) + deleted = __salt__["boto_elasticache.delete_replication_group"]( + name, region, key, keyid, profile + ) if not deleted: - ret['result'] = False - log.error(ret['comment']) - ret['comment'] = 'Failed to delete {0} ElastiCache replication group.'.format(name) + ret["result"] = False + log.error(ret["comment"]) + ret["comment"] = "Failed to delete {0} ElastiCache replication group.".format( + name + ) return ret - ret['changes']['old'] = name - ret['changes']['new'] = None - ret['comment'] = 'ElastiCache replication group {0} deleted.'.format(name) - log.info(ret['comment']) + ret["changes"]["old"] = name + ret["changes"]["new"] = None + ret["comment"] = "ElastiCache replication group {0} deleted.".format(name) + log.info(ret["comment"]) return ret diff --git a/salt/states/boto_elasticsearch_domain.py b/salt/states/boto_elasticsearch_domain.py index 3dab795e4ed..9b4a68fc0d2 100644 --- a/salt/states/boto_elasticsearch_domain.py +++ b/salt/states/boto_elasticsearch_domain.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Elasticsearch Domains ============================ @@ -76,10 +76,11 @@ config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -93,26 +94,34 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_elasticsearch_domain' if 'boto_elasticsearch_domain.exists' in __salt__ else False + """ + if "boto_elasticsearch_domain.exists" in __salt__: + return "boto_elasticsearch_domain" + return (False, "boto_elasticsearch_domain module could not be loaded") def _compare_json(current, desired): - return __utils__['boto3.json_objs_equal'](current, desired) + return __utils__["boto3.json_objs_equal"](current, desired) -def present(name, DomainName, - ElasticsearchClusterConfig=None, - EBSOptions=None, - AccessPolicies=None, - SnapshotOptions=None, - AdvancedOptions=None, - Tags=None, - region=None, key=None, keyid=None, profile=None, - ElasticsearchVersion="1.5"): - ''' +def present( + name, + DomainName, + ElasticsearchClusterConfig=None, + EBSOptions=None, + AccessPolicies=None, + SnapshotOptions=None, + AdvancedOptions=None, + Tags=None, + region=None, + key=None, + keyid=None, + profile=None, + ElasticsearchVersion="1.5", +): + """ Ensure domain exists. name @@ -195,139 +204,150 @@ def present(name, DomainName, ElasticsearchVersion String of format X.Y to specify version for the Elasticsearch domain eg. "1.5" or "2.3". - ''' - ret = {'name': DomainName, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": DomainName, "result": True, "comment": "", "changes": {}} if ElasticsearchClusterConfig is None: ElasticsearchClusterConfig = { - 'DedicatedMasterEnabled': False, - 'InstanceCount': 1, - 'InstanceType': 'm3.medium.elasticsearch', - 'ZoneAwarenessEnabled': False + "DedicatedMasterEnabled": False, + "InstanceCount": 1, + "InstanceType": "m3.medium.elasticsearch", + "ZoneAwarenessEnabled": False, } if EBSOptions is None: EBSOptions = { - 'EBSEnabled': False, + "EBSEnabled": False, } if SnapshotOptions is None: - SnapshotOptions = { - 'AutomatedSnapshotStartHour': 0 - } + SnapshotOptions = {"AutomatedSnapshotStartHour": 0} if AdvancedOptions is None: - AdvancedOptions = { - 'rest.action.multi.allow_explicit_index': 'true' - } + AdvancedOptions = {"rest.action.multi.allow_explicit_index": "true"} if Tags is None: Tags = {} if AccessPolicies is not None and isinstance(AccessPolicies, six.string_types): try: AccessPolicies = salt.utils.json.loads(AccessPolicies) except ValueError as e: - ret['result'] = False - ret['comment'] = 'Failed to create domain: {0}.'.format(e.message) + ret["result"] = False + ret["comment"] = "Failed to create domain: {0}.".format(e.message) return ret - r = __salt__['boto_elasticsearch_domain.exists'](DomainName=DomainName, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_elasticsearch_domain.exists"]( + DomainName=DomainName, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create domain: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create domain: {0}.".format(r["error"]["message"]) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'Domain {0} is set to be created.'.format(DomainName) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "Domain {0} is set to be created.".format(DomainName) + ret["result"] = None return ret - r = __salt__['boto_elasticsearch_domain.create'](DomainName=DomainName, - ElasticsearchClusterConfig=ElasticsearchClusterConfig, - EBSOptions=EBSOptions, - AccessPolicies=AccessPolicies, - SnapshotOptions=SnapshotOptions, - AdvancedOptions=AdvancedOptions, - ElasticsearchVersion=str(ElasticsearchVersion), # future lint: disable=blacklisted-function - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create domain: {0}.'.format(r['error']['message']) + r = __salt__["boto_elasticsearch_domain.create"]( + DomainName=DomainName, + ElasticsearchClusterConfig=ElasticsearchClusterConfig, + EBSOptions=EBSOptions, + AccessPolicies=AccessPolicies, + SnapshotOptions=SnapshotOptions, + AdvancedOptions=AdvancedOptions, + ElasticsearchVersion=str( + ElasticsearchVersion + ), # future lint: disable=blacklisted-function + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create domain: {0}.".format( + r["error"]["message"] + ) return ret - _describe = __salt__['boto_elasticsearch_domain.describe'](DomainName, - region=region, key=key, keyid=keyid, profile=profile) - ret['changes']['old'] = {'domain': None} - ret['changes']['new'] = _describe - ret['comment'] = 'Domain {0} created.'.format(DomainName) + _describe = __salt__["boto_elasticsearch_domain.describe"]( + DomainName, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["old"] = {"domain": None} + ret["changes"]["new"] = _describe + ret["comment"] = "Domain {0} created.".format(DomainName) return ret - ret['comment'] = os.linesep.join([ret['comment'], 'Domain {0} is present.'.format(DomainName)]) - ret['changes'] = {} + ret["comment"] = os.linesep.join( + [ret["comment"], "Domain {0} is present.".format(DomainName)] + ) + ret["changes"] = {} # domain exists, ensure config matches - _status = __salt__['boto_elasticsearch_domain.status'](DomainName=DomainName, - region=region, key=key, keyid=keyid, - profile=profile)['domain'] - if _status.get('ElasticsearchVersion') != str(ElasticsearchVersion): # future lint: disable=blacklisted-function - ret['result'] = False - ret['comment'] = ( - 'Failed to update domain: version cannot be modified ' - 'from {0} to {1}.'.format( - _status.get('ElasticsearchVersion'), - str(ElasticsearchVersion) # future lint: disable=blacklisted-function + _status = __salt__["boto_elasticsearch_domain.status"]( + DomainName=DomainName, region=region, key=key, keyid=keyid, profile=profile + )["domain"] + if _status.get("ElasticsearchVersion") != str( + ElasticsearchVersion + ): # future lint: disable=blacklisted-function + ret["result"] = False + ret["comment"] = ( + "Failed to update domain: version cannot be modified " + "from {0} to {1}.".format( + _status.get("ElasticsearchVersion"), + str(ElasticsearchVersion), # future lint: disable=blacklisted-function ) ) return ret - _describe = __salt__['boto_elasticsearch_domain.describe'](DomainName=DomainName, - region=region, key=key, keyid=keyid, - profile=profile)['domain'] - _describe['AccessPolicies'] = salt.utils.json.loads(_describe['AccessPolicies']) + _describe = __salt__["boto_elasticsearch_domain.describe"]( + DomainName=DomainName, region=region, key=key, keyid=keyid, profile=profile + )["domain"] + _describe["AccessPolicies"] = salt.utils.json.loads(_describe["AccessPolicies"]) # When EBSEnabled is false, describe returns extra values that can't be set - if not _describe.get('EBSOptions', {}).get('EBSEnabled'): - opts = _describe.get('EBSOptions', {}) - opts.pop('VolumeSize', None) - opts.pop('VolumeType', None) + if not _describe.get("EBSOptions", {}).get("EBSEnabled"): + opts = _describe.get("EBSOptions", {}) + opts.pop("VolumeSize", None) + opts.pop("VolumeType", None) comm_args = {} need_update = False - es_opts = {'ElasticsearchClusterConfig': ElasticsearchClusterConfig, - 'EBSOptions': EBSOptions, - 'AccessPolicies': AccessPolicies, - 'SnapshotOptions': SnapshotOptions, - 'AdvancedOptions': AdvancedOptions} + es_opts = { + "ElasticsearchClusterConfig": ElasticsearchClusterConfig, + "EBSOptions": EBSOptions, + "AccessPolicies": AccessPolicies, + "SnapshotOptions": SnapshotOptions, + "AdvancedOptions": AdvancedOptions, + } for k, v in six.iteritems(es_opts): if not _compare_json(v, _describe[k]): need_update = True comm_args[k] = v - ret['changes'].setdefault('new', {})[k] = v - ret['changes'].setdefault('old', {})[k] = _describe[k] + ret["changes"].setdefault("new", {})[k] = v + ret["changes"].setdefault("old", {})[k] = _describe[k] if need_update: - if __opts__['test']: - msg = 'Domain {0} set to be modified.'.format(DomainName) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Domain {0} set to be modified.".format(DomainName) + ret["comment"] = msg + ret["result"] = None return ret - ret['comment'] = os.linesep.join([ret['comment'], 'Domain to be modified']) + ret["comment"] = os.linesep.join([ret["comment"], "Domain to be modified"]) - r = __salt__['boto_elasticsearch_domain.update'](DomainName=DomainName, - region=region, key=key, - keyid=keyid, profile=profile, - **comm_args) - if not r.get('updated'): - ret['result'] = False - ret['comment'] = 'Failed to update domain: {0}.'.format(r['error']) - ret['changes'] = {} + r = __salt__["boto_elasticsearch_domain.update"]( + DomainName=DomainName, + region=region, + key=key, + keyid=keyid, + profile=profile, + **comm_args + ) + if not r.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to update domain: {0}.".format(r["error"]) + ret["changes"] = {} return ret return ret -def absent(name, DomainName, - region=None, key=None, keyid=None, profile=None): - ''' +def absent(name, DomainName, region=None, key=None, keyid=None, profile=None): + """ Ensure domain with passed properties is absent. name @@ -348,38 +368,35 @@ def absent(name, DomainName, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': DomainName, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": DomainName, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_elasticsearch_domain.exists'](DomainName, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete domain: {0}.'.format(r['error']['message']) + r = __salt__["boto_elasticsearch_domain.exists"]( + DomainName, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete domain: {0}.".format(r["error"]["message"]) return ret - if r and not r['exists']: - ret['comment'] = 'Domain {0} does not exist.'.format(DomainName) + if r and not r["exists"]: + ret["comment"] = "Domain {0} does not exist.".format(DomainName) return ret - if __opts__['test']: - ret['comment'] = 'Domain {0} is set to be removed.'.format(DomainName) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Domain {0} is set to be removed.".format(DomainName) + ret["result"] = None return ret - r = __salt__['boto_elasticsearch_domain.delete'](DomainName, - region=region, key=key, - keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete domain: {0}.'.format(r['error']['message']) + r = __salt__["boto_elasticsearch_domain.delete"]( + DomainName, region=region, key=key, keyid=keyid, profile=profile + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete domain: {0}.".format(r["error"]["message"]) return ret - ret['changes']['old'] = {'domain': DomainName} - ret['changes']['new'] = {'domain': None} - ret['comment'] = 'Domain {0} deleted.'.format(DomainName) + ret["changes"]["old"] = {"domain": DomainName} + ret["changes"]["new"] = {"domain": None} + ret["comment"] = "Domain {0} deleted.".format(DomainName) return ret diff --git a/salt/states/boto_elb.py b/salt/states/boto_elb.py index fa09c5d496e..ff06e3d086a 100644 --- a/salt/states/boto_elb.py +++ b/salt/states/boto_elb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage ELBs .. versionadded:: 2014.7.0 @@ -233,15 +233,16 @@ Tags can also be set: - tags: MyTag: 'My Tag Value' OtherTag: 'My Other Value' -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import logging # Import Salt Libs import hashlib +import logging import re + import salt.utils.data import salt.utils.dictupdate import salt.utils.stringutils @@ -252,21 +253,41 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_elb' if 'boto_elb.exists' in __salt__ else False + """ + if "boto_elb.exists" in __salt__: + return "boto_elb" + return (False, "boto_elb module could not be loaded") -def present(name, listeners, availability_zones=None, subnets=None, - subnet_names=None, security_groups=None, scheme='internet-facing', - health_check=None, attributes=None, - attributes_from_pillar="boto_elb_attributes", cnames=None, - alarms=None, alarms_from_pillar="boto_elb_alarms", policies=None, - policies_from_pillar="boto_elb_policies", backends=None, - region=None, key=None, keyid=None, profile=None, wait_for_sync=True, - tags=None, instance_ids=None, instance_names=None): - ''' +def present( + name, + listeners, + availability_zones=None, + subnets=None, + subnet_names=None, + security_groups=None, + scheme="internet-facing", + health_check=None, + attributes=None, + attributes_from_pillar="boto_elb_attributes", + cnames=None, + alarms=None, + alarms_from_pillar="boto_elb_alarms", + policies=None, + policies_from_pillar="boto_elb_policies", + backends=None, + region=None, + key=None, + keyid=None, + profile=None, + wait_for_sync=True, + tags=None, + instance_ids=None, + instance_names=None, +): + """ Ensure the ELB exists. name @@ -373,121 +394,183 @@ def present(name, listeners, availability_zones=None, subnets=None, instance_names list of instance names. The state will ensure that these, and ONLY these, instances are registered with the ELB. This is additive with instance_ids. - ''' + """ # load data from attributes_from_pillar and merge with attributes - tmp = __salt__['config.option'](attributes_from_pillar, {}) + tmp = __salt__["config.option"](attributes_from_pillar, {}) attributes = salt.utils.dictupdate.update(tmp, attributes) if attributes else tmp - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not isinstance(security_groups, (six.string_types, list, type(None))): - msg = ("The 'security_group' parameter must be either a list or a " - "comma-separated string.") + msg = ( + "The 'security_group' parameter must be either a list or a " + "comma-separated string." + ) log.error(msg) - ret.update({'comment': msg, 'result': False}) + ret.update({"comment": msg, "result": False}) return ret if isinstance(security_groups, six.string_types): - security_groups = security_groups.split(',') + security_groups = security_groups.split(",") - _ret = _elb_present(name, availability_zones, listeners, subnets, subnet_names, - security_groups, scheme, region, key, keyid, profile) - ret.update({'changes': _ret['changes'], - 'comment': ' '.join([ret['comment'], _ret['comment']])}) - ret['result'] = ret['result'] if _ret['result'] else _ret['result'] - if ret['result'] is False: + _ret = _elb_present( + name, + availability_zones, + listeners, + subnets, + subnet_names, + security_groups, + scheme, + region, + key, + keyid, + profile, + ) + ret.update( + { + "changes": _ret["changes"], + "comment": " ".join([ret["comment"], _ret["comment"]]), + } + ) + ret["result"] = ret["result"] if _ret["result"] else _ret["result"] + if ret["result"] is False: return ret - exists = __salt__['boto_elb.exists'](name, region, key, keyid, profile) - if not exists and __opts__['test']: + exists = __salt__["boto_elb.exists"](name, region, key, keyid, profile) + if not exists and __opts__["test"]: return ret if attributes: _ret = _attributes_present(name, attributes, region, key, keyid, profile) - ret.update({'changes': salt.utils.dictupdate.update(ret['changes'], _ret['changes']), - 'comment': ' '.join([ret['comment'], _ret['comment']])}) - ret['result'] = ret['result'] if _ret['result'] else _ret['result'] - if ret['result'] is False: + ret.update( + { + "changes": salt.utils.dictupdate.update( + ret["changes"], _ret["changes"] + ), + "comment": " ".join([ret["comment"], _ret["comment"]]), + } + ) + ret["result"] = ret["result"] if _ret["result"] else _ret["result"] + if ret["result"] is False: return ret - _ret = _health_check_present(name, health_check, region, key, keyid, - profile) - ret.update({'changes': salt.utils.dictupdate.update(ret['changes'], _ret['changes']), - 'comment': ' '.join([ret['comment'], _ret['comment']])}) - ret['result'] = ret['result'] if _ret['result'] else _ret['result'] - if ret['result'] is False: + _ret = _health_check_present(name, health_check, region, key, keyid, profile) + ret.update( + { + "changes": salt.utils.dictupdate.update(ret["changes"], _ret["changes"]), + "comment": " ".join([ret["comment"], _ret["comment"]]), + } + ) + ret["result"] = ret["result"] if _ret["result"] else _ret["result"] + if ret["result"] is False: return ret if cnames: - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, - profile) + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) if lb: for cname in cnames: _ret = None - dns_provider = 'boto_route53' - cname.update({'record_type': 'CNAME', 'value': lb['dns_name']}) - if 'provider' in cname: - dns_provider = cname.pop('provider') - if dns_provider == 'boto_route53': - for p in ('profile', 'key', 'keyid', 'region', 'wait_for_sync'): + dns_provider = "boto_route53" + cname.update({"record_type": "CNAME", "value": lb["dns_name"]}) + if "provider" in cname: + dns_provider = cname.pop("provider") + if dns_provider == "boto_route53": + for p in ("profile", "key", "keyid", "region", "wait_for_sync"): cname[p] = locals().get(p) if p not in cname else cname[p] - _ret = __states__['boto_route53.present'](**cname) - ret.update({'changes': salt.utils.dictupdate.update(ret['changes'], _ret['changes']), - 'comment': ' '.join([ret['comment'], _ret['comment']])}) - ret['result'] = ret['result'] if _ret['result'] else _ret['result'] - if ret['result'] is False: + _ret = __states__["boto_route53.present"](**cname) + ret.update( + { + "changes": salt.utils.dictupdate.update( + ret["changes"], _ret["changes"] + ), + "comment": " ".join([ret["comment"], _ret["comment"]]), + } + ) + ret["result"] = ret["result"] if _ret["result"] else _ret["result"] + if ret["result"] is False: return ret - _ret = _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profile) - ret.update({'changes': salt.utils.dictupdate.update(ret['changes'], _ret['changes']), - 'comment': ' '.join([ret['comment'], _ret['comment']])}) - ret['result'] = ret['result'] if _ret['result'] else _ret['result'] - if ret['result'] is False: + _ret = _alarms_present( + name, alarms, alarms_from_pillar, region, key, keyid, profile + ) + ret.update( + { + "changes": salt.utils.dictupdate.update(ret["changes"], _ret["changes"]), + "comment": " ".join([ret["comment"], _ret["comment"]]), + } + ) + ret["result"] = ret["result"] if _ret["result"] else _ret["result"] + if ret["result"] is False: return ret - _ret = _policies_present(name, policies, policies_from_pillar, listeners, - backends, region, key, keyid, profile) - ret.update({'changes': salt.utils.dictupdate.update(ret['changes'], _ret['changes']), - 'comment': ' '.join([ret['comment'], _ret['comment']])}) - ret['result'] = ret['result'] if _ret['result'] else _ret['result'] - if ret['result'] is False: + _ret = _policies_present( + name, + policies, + policies_from_pillar, + listeners, + backends, + region, + key, + keyid, + profile, + ) + ret.update( + { + "changes": salt.utils.dictupdate.update(ret["changes"], _ret["changes"]), + "comment": " ".join([ret["comment"], _ret["comment"]]), + } + ) + ret["result"] = ret["result"] if _ret["result"] else _ret["result"] + if ret["result"] is False: return ret _ret = _tags_present(name, tags, region, key, keyid, profile) - ret.update({'changes': salt.utils.dictupdate.update(ret['changes'], _ret['changes']), - 'comment': ' '.join([ret['comment'], _ret['comment']])}) - ret['result'] = ret['result'] if _ret['result'] else _ret['result'] - if ret['result'] is False: + ret.update( + { + "changes": salt.utils.dictupdate.update(ret["changes"], _ret["changes"]), + "comment": " ".join([ret["comment"], _ret["comment"]]), + } + ) + ret["result"] = ret["result"] if _ret["result"] else _ret["result"] + if ret["result"] is False: return ret if not instance_ids: instance_ids = [] if instance_names: # AWS borks on adding instances in "non-running" states, so filter 'em out. - running_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped') + running_states = ("pending", "rebooting", "running", "stopping", "stopped") for n in instance_names: - instance_ids += __salt__['boto_ec2.find_instances']( - name=n, region=region, key=key, keyid=keyid, profile=profile, - in_states=running_states) + instance_ids += __salt__["boto_ec2.find_instances"]( + name=n, + region=region, + key=key, + keyid=keyid, + profile=profile, + in_states=running_states, + ) # Backwards compat: Only touch attached instances if requested (e.g. if some are defined). if instance_ids: - if __opts__['test']: - if __salt__['boto_elb.set_instances']( - name, instance_ids, True, region, key, keyid, profile): - ret['comment'] += ' ELB {0} instances would be updated.'.format(name) - ret['result'] = None + if __opts__["test"]: + if __salt__["boto_elb.set_instances"]( + name, instance_ids, True, region, key, keyid, profile + ): + ret["comment"] += " ELB {0} instances would be updated.".format(name) + ret["result"] = None else: - success = __salt__['boto_elb.set_instances']( - name, instance_ids, False, region, key, keyid, profile) + success = __salt__["boto_elb.set_instances"]( + name, instance_ids, False, region, key, keyid, profile + ) if not success: - ret['comment'] += "Failed to set requested instances." - ret['result'] = False + ret["comment"] += "Failed to set requested instances." + ret["result"] = False return ret -def register_instances(name, instances, region=None, key=None, keyid=None, - profile=None): - ''' +def register_instances( + name, instances, region=None, key=None, keyid=None, profile=None +): + """ Add EC2 instance(s) to an Elastic Load Balancer. Removing an instance from the ``instances`` list does not remove it from the ELB. @@ -510,188 +593,240 @@ def register_instances(name, instances, region=None, key=None, keyid=None, - instances: - instance-id1 - instance-id2 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - lb = __salt__['boto_elb.exists'](name, region, key, keyid, profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + lb = __salt__["boto_elb.exists"](name, region, key, keyid, profile) if not lb: - msg = 'Could not find lb {0}'.format(name) + msg = "Could not find lb {0}".format(name) log.error(msg) - ret.update({'comment': msg, 'result': False}) + ret.update({"comment": msg, "result": False}) return ret - health = __salt__['boto_elb.get_instance_health']( - name, region, key, keyid, profile) - nodes = [value['instance_id'] for value in health - if value['description'] != 'Instance deregistration currently in progress.'] + health = __salt__["boto_elb.get_instance_health"](name, region, key, keyid, profile) + nodes = [ + value["instance_id"] + for value in health + if value["description"] != "Instance deregistration currently in progress." + ] new = [value for value in instances if value not in nodes] if not new: - msg = 'Instance/s {0} already exist.'.format(six.text_type(instances).strip('[]')) + msg = "Instance/s {0} already exist.".format( + six.text_type(instances).strip("[]") + ) log.debug(msg) - ret.update({'comment': msg}) + ret.update({"comment": msg}) return ret - if __opts__['test']: - ret['comment'] = 'ELB {0} is set to register : {1}.'.format(name, new) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ELB {0} is set to register : {1}.".format(name, new) + ret["result"] = None return ret - state = __salt__['boto_elb.register_instances']( - name, instances, region, key, keyid, profile) + state = __salt__["boto_elb.register_instances"]( + name, instances, region, key, keyid, profile + ) if state: - msg = 'Load Balancer {0} has been changed'.format(name) + msg = "Load Balancer {0} has been changed".format(name) log.info(msg) new = set().union(nodes, instances) - ret.update({'comment': msg, 'changes': {'old': '\n'.join(nodes), - 'new': '\n'.join(list(new))}}) + ret.update( + { + "comment": msg, + "changes": {"old": "\n".join(nodes), "new": "\n".join(list(new))}, + } + ) else: - msg = 'Load balancer {0} failed to add instances'.format(name) + msg = "Load balancer {0} failed to add instances".format(name) log.error(msg) - ret.update({'comment': msg, 'result': False}) + ret.update({"comment": msg, "result": False}) return ret -DEFAULT_PILLAR_LISTENER_POLICY_KEY = 'boto_elb_listener_policies' +DEFAULT_PILLAR_LISTENER_POLICY_KEY = "boto_elb_listener_policies" -def _elb_present(name, availability_zones, listeners, subnets, subnet_names, - security_groups, scheme, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} +def _elb_present( + name, + availability_zones, + listeners, + subnets, + subnet_names, + security_groups, + scheme, + region, + key, + keyid, + profile, +): + ret = {"result": True, "comment": "", "changes": {}} if not salt.utils.data.exactly_one((availability_zones, subnets, subnet_names)): - raise SaltInvocationError('Exactly one of availability_zones, subnets, ' - 'subnet_names must be provided as arguments.') + raise SaltInvocationError( + "Exactly one of availability_zones, subnets, " + "subnet_names must be provided as arguments." + ) if not listeners: listeners = [] for listener in listeners: if len(listener) < 3: - raise SaltInvocationError('Listeners must have at minimum port,' - ' instance_port and protocol values in' - ' the provided list.') - if 'elb_port' not in listener: - raise SaltInvocationError('elb_port is a required value for' - ' listeners.') - if 'instance_port' not in listener: - raise SaltInvocationError('instance_port is a required value for' - ' listeners.') - if 'elb_protocol' not in listener: - raise SaltInvocationError('elb_protocol is a required value for' - ' listeners.') - listener['elb_protocol'] = listener['elb_protocol'].upper() - if listener['elb_protocol'] == 'HTTPS' and 'certificate' not in listener: - raise SaltInvocationError('certificate is a required value for' - ' listeners if HTTPS is set for' - ' elb_protocol.') + raise SaltInvocationError( + "Listeners must have at minimum port," + " instance_port and protocol values in" + " the provided list." + ) + if "elb_port" not in listener: + raise SaltInvocationError("elb_port is a required value for" " listeners.") + if "instance_port" not in listener: + raise SaltInvocationError( + "instance_port is a required value for" " listeners." + ) + if "elb_protocol" not in listener: + raise SaltInvocationError( + "elb_protocol is a required value for" " listeners." + ) + listener["elb_protocol"] = listener["elb_protocol"].upper() + if listener["elb_protocol"] == "HTTPS" and "certificate" not in listener: + raise SaltInvocationError( + "certificate is a required value for" + " listeners if HTTPS is set for" + " elb_protocol." + ) # best attempt at principle of least surprise here: # only use the default pillar in cases where we don't explicitly # define policies OR policies_from_pillar on a listener - policies = listener.setdefault('policies', []) - policies_pillar = listener.get('policies_from_pillar', None) + policies = listener.setdefault("policies", []) + policies_pillar = listener.get("policies_from_pillar", None) if not policies and policies_pillar is None: policies_pillar = DEFAULT_PILLAR_LISTENER_POLICY_KEY if policies_pillar: - policies += __salt__['pillar.get'](policies_pillar, {}).get(listener['elb_protocol'], []) + policies += __salt__["pillar.get"](policies_pillar, {}).get( + listener["elb_protocol"], [] + ) # Look up subnet ids from names if provided if subnet_names: subnets = [] for i in subnet_names: - r = __salt__['boto_vpc.get_resource_id']('subnet', name=i, region=region, - key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['comment'] = 'Error looking up subnet ids: {0}'.format(r['error']) - ret['result'] = False + r = __salt__["boto_vpc.get_resource_id"]( + "subnet", name=i, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["comment"] = "Error looking up subnet ids: {0}".format(r["error"]) + ret["result"] = False return ret - if 'id' not in r: - ret['comment'] = 'Subnet {0} does not exist.'.format(i) - ret['result'] = False + if "id" not in r: + ret["comment"] = "Subnet {0} does not exist.".format(i) + ret["result"] = False return ret - subnets.append(r['id']) + subnets.append(r["id"]) _security_groups = None if subnets: - vpc_id = __salt__['boto_vpc.get_subnet_association'](subnets, region, key, keyid, profile) - vpc_id = vpc_id.get('vpc_id') + vpc_id = __salt__["boto_vpc.get_subnet_association"]( + subnets, region, key, keyid, profile + ) + vpc_id = vpc_id.get("vpc_id") if not vpc_id: - ret['comment'] = 'Subnets {0} do not map to a valid vpc id.'.format(subnets) - ret['result'] = False + ret["comment"] = "Subnets {0} do not map to a valid vpc id.".format(subnets) + ret["result"] = False return ret - _security_groups = __salt__['boto_secgroup.convert_to_group_ids']( - security_groups, vpc_id=vpc_id, region=region, key=key, - keyid=keyid, profile=profile + _security_groups = __salt__["boto_secgroup.convert_to_group_ids"]( + security_groups, + vpc_id=vpc_id, + region=region, + key=key, + keyid=keyid, + profile=profile, ) if not _security_groups: - ret['comment'] = 'Security groups {0} do not map to valid security group ids.'.format(security_groups) - ret['result'] = False + ret[ + "comment" + ] = "Security groups {0} do not map to valid security group ids.".format( + security_groups + ) + ret["result"] = False return ret - exists = __salt__['boto_elb.exists'](name, region, key, keyid, profile) + exists = __salt__["boto_elb.exists"](name, region, key, keyid, profile) if not exists: - if __opts__['test']: - ret['comment'] = 'ELB {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ELB {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_elb.create'](name=name, - availability_zones=availability_zones, - listeners=listeners, subnets=subnets, - security_groups=_security_groups, - scheme=scheme, region=region, key=key, - keyid=keyid, profile=profile) + created = __salt__["boto_elb.create"]( + name=name, + availability_zones=availability_zones, + listeners=listeners, + subnets=subnets, + security_groups=_security_groups, + scheme=scheme, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if created: - ret['changes']['old'] = {'elb': None} - ret['changes']['new'] = {'elb': name} - ret['comment'] = 'ELB {0} created.'.format(name) + ret["changes"]["old"] = {"elb": None} + ret["changes"]["new"] = {"elb": name} + ret["comment"] = "ELB {0} created.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} ELB.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} ELB.".format(name) else: - ret['comment'] = 'ELB {0} present.'.format(name) - _ret = _security_groups_present(name, _security_groups, region, key, keyid, profile) - ret['changes'] = salt.utils.dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["comment"] = "ELB {0} present.".format(name) + _ret = _security_groups_present( + name, _security_groups, region, key, keyid, profile + ) + ret["changes"] = salt.utils.dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret _ret = _listeners_present(name, listeners, region, key, keyid, profile) - ret['changes'] = salt.utils.dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = salt.utils.dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret if availability_zones: _ret = _zones_present(name, availability_zones, region, key, keyid, profile) - ret['changes'] = salt.utils.dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = salt.utils.dictupdate.update( + ret["changes"], _ret["changes"] + ) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret elif subnets: _ret = _subnets_present(name, subnets, region, key, keyid, profile) - ret['changes'] = salt.utils.dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + ret["changes"] = salt.utils.dictupdate.update( + ret["changes"], _ret["changes"] + ) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret def _listeners_present(name, listeners, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile) + ret = {"result": True, "comment": "", "changes": {}} + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) if not lb: - ret['comment'] = '{0} ELB configuration could not be retrieved.'.format(name) - ret['result'] = False + ret["comment"] = "{0} ELB configuration could not be retrieved.".format(name) + ret["result"] = False return ret if not listeners: listeners = [] expected_listeners_by_tuple = {} for l in listeners: - l_key = __salt__['boto_elb.listener_dict_to_tuple'](l) + l_key = __salt__["boto_elb.listener_dict_to_tuple"](l) expected_listeners_by_tuple[l_key] = l actual_listeners_by_tuple = {} - for l in lb['listeners']: - l_key = __salt__['boto_elb.listener_dict_to_tuple'](l) + for l in lb["listeners"]: + l_key = __salt__["boto_elb.listener_dict_to_tuple"](l) actual_listeners_by_tuple[l_key] = l to_delete = [] @@ -704,190 +839,199 @@ def _listeners_present(name, listeners, region, key, keyid, profile): if t not in expected_listeners_by_tuple: to_delete.append(l) - if __opts__['test']: + if __opts__["test"]: msg = [] if to_create or to_delete: - msg.append('ELB {0} set to have listeners modified:'.format(name)) + msg.append("ELB {0} set to have listeners modified:".format(name)) for listener in to_create: - msg.append('Listener {0} added.'.format( - __salt__['boto_elb.listener_dict_to_tuple'](listener))) + msg.append( + "Listener {0} added.".format( + __salt__["boto_elb.listener_dict_to_tuple"](listener) + ) + ) for listener in to_delete: - msg.append('Listener {0} deleted.'.format( - __salt__['boto_elb.listener_dict_to_tuple'](listener))) - ret['result'] = None + msg.append( + "Listener {0} deleted.".format( + __salt__["boto_elb.listener_dict_to_tuple"](listener) + ) + ) + ret["result"] = None else: - msg.append('Listeners already set on ELB {0}.'.format(name)) - ret['comment'] = ' '.join(msg) + msg.append("Listeners already set on ELB {0}.".format(name)) + ret["comment"] = " ".join(msg) return ret if to_delete: - ports = [l['elb_port'] for l in to_delete] - deleted = __salt__['boto_elb.delete_listeners'](name, ports, - region, key, keyid, - profile) + ports = [l["elb_port"] for l in to_delete] + deleted = __salt__["boto_elb.delete_listeners"]( + name, ports, region, key, keyid, profile + ) if deleted: - ret['comment'] = 'Deleted listeners on {0} ELB.'.format(name) + ret["comment"] = "Deleted listeners on {0} ELB.".format(name) else: - ret['comment'] = 'Failed to delete listeners on {0} ELB.'.format(name) - ret['result'] = False + ret["comment"] = "Failed to delete listeners on {0} ELB.".format(name) + ret["result"] = False if to_create: - created = __salt__['boto_elb.create_listeners'](name, to_create, - region, key, keyid, - profile) + created = __salt__["boto_elb.create_listeners"]( + name, to_create, region, key, keyid, profile + ) if created: - msg = 'Created listeners on {0} ELB.' - ret['comment'] = ' '.join([ret['comment'], msg.format(name)]) + msg = "Created listeners on {0} ELB." + ret["comment"] = " ".join([ret["comment"], msg.format(name)]) else: - msg = 'Failed to create listeners on {0} ELB.' - ret['comment'] = ' '.join([ret['comment'], msg.format(name)]) - ret['result'] = False + msg = "Failed to create listeners on {0} ELB." + ret["comment"] = " ".join([ret["comment"], msg.format(name)]) + ret["result"] = False if to_create or to_delete: - ret['changes']['listeners'] = {} - ret['changes']['listeners']['old'] = lb['listeners'] - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, - profile) - ret['changes']['listeners']['new'] = lb['listeners'] + ret["changes"]["listeners"] = {} + ret["changes"]["listeners"]["old"] = lb["listeners"] + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) + ret["changes"]["listeners"]["new"] = lb["listeners"] else: - ret['comment'] = 'Listeners already set on ELB {0}.'.format(name) + ret["comment"] = "Listeners already set on ELB {0}.".format(name) return ret def _security_groups_present(name, security_groups, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile) + ret = {"result": True, "comment": "", "changes": {}} + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) if not lb: - ret['comment'] = '{0} ELB configuration could not be retrieved.'.format(name) - ret['result'] = False + ret["comment"] = "{0} ELB configuration could not be retrieved.".format(name) + ret["result"] = False return ret if not security_groups: security_groups = [] change_needed = False - if set(security_groups) != set(lb['security_groups']): + if set(security_groups) != set(lb["security_groups"]): change_needed = True if change_needed: - if __opts__['test']: - ret['comment'] = 'ELB {0} set to have security groups modified.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ELB {0} set to have security groups modified.".format( + name + ) + ret["result"] = None return ret - changed = __salt__['boto_elb.apply_security_groups']( + changed = __salt__["boto_elb.apply_security_groups"]( name, security_groups, region, key, keyid, profile ) if changed: - ret['comment'] = 'Modified security_groups on {0} ELB.'.format(name) + ret["comment"] = "Modified security_groups on {0} ELB.".format(name) else: - ret['comment'] = 'Failed to modify security_groups on {0} ELB.'.format(name) - ret['result'] = False - ret['changes']['old'] = {'security_groups': lb['security_groups']} - ret['changes']['new'] = {'security_groups': security_groups} + ret["comment"] = "Failed to modify security_groups on {0} ELB.".format(name) + ret["result"] = False + ret["changes"]["old"] = {"security_groups": lb["security_groups"]} + ret["changes"]["new"] = {"security_groups": security_groups} else: - ret['comment'] = 'security_groups already set on ELB {0}.'.format(name) + ret["comment"] = "security_groups already set on ELB {0}.".format(name) return ret def _attributes_present(name, attributes, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - _attributes = __salt__['boto_elb.get_attributes'](name, region, key, keyid, - profile) + ret = {"result": True, "comment": "", "changes": {}} + _attributes = __salt__["boto_elb.get_attributes"](name, region, key, keyid, profile) if not _attributes: - ret['result'] = False - ret['comment'] = 'Failed to retrieve attributes for ELB {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to retrieve attributes for ELB {0}.".format(name) return ret attrs_to_set = [] - if 'cross_zone_load_balancing' in attributes: - czlb = attributes['cross_zone_load_balancing'] - _czlb = _attributes['cross_zone_load_balancing'] - if czlb['enabled'] != _czlb['enabled']: - attrs_to_set.append('cross_zone_load_balancing') - if 'connection_draining' in attributes: - cd = attributes['connection_draining'] - _cd = _attributes['connection_draining'] - if (cd['enabled'] != _cd['enabled'] - or cd.get('timeout', 300) != _cd.get('timeout')): - attrs_to_set.append('connection_draining') - if 'connecting_settings' in attributes: - cs = attributes['connecting_settings'] - _cs = _attributes['connecting_settings'] - if cs['idle_timeout'] != _cs['idle_timeout']: - attrs_to_set.append('connecting_settings') - if 'access_log' in attributes: - for attr, val in six.iteritems(attributes['access_log']): - if six.text_type(_attributes['access_log'][attr]) != six.text_type(val): - attrs_to_set.append('access_log') - if 's3_bucket_prefix' in attributes['access_log']: - sbp = attributes['access_log']['s3_bucket_prefix'] - if sbp.startswith('/') or sbp.endswith('/'): - raise SaltInvocationError('s3_bucket_prefix can not start or' - ' end with /.') + if "cross_zone_load_balancing" in attributes: + czlb = attributes["cross_zone_load_balancing"] + _czlb = _attributes["cross_zone_load_balancing"] + if czlb["enabled"] != _czlb["enabled"]: + attrs_to_set.append("cross_zone_load_balancing") + if "connection_draining" in attributes: + cd = attributes["connection_draining"] + _cd = _attributes["connection_draining"] + if cd["enabled"] != _cd["enabled"] or cd.get("timeout", 300) != _cd.get( + "timeout" + ): + attrs_to_set.append("connection_draining") + if "connecting_settings" in attributes: + cs = attributes["connecting_settings"] + _cs = _attributes["connecting_settings"] + if cs["idle_timeout"] != _cs["idle_timeout"]: + attrs_to_set.append("connecting_settings") + if "access_log" in attributes: + for attr, val in six.iteritems(attributes["access_log"]): + if six.text_type(_attributes["access_log"][attr]) != six.text_type(val): + attrs_to_set.append("access_log") + if "s3_bucket_prefix" in attributes["access_log"]: + sbp = attributes["access_log"]["s3_bucket_prefix"] + if sbp.startswith("/") or sbp.endswith("/"): + raise SaltInvocationError( + "s3_bucket_prefix can not start or" " end with /." + ) if attrs_to_set: - if __opts__['test']: - ret['comment'] = 'ELB {0} set to have attributes set.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ELB {0} set to have attributes set.".format(name) + ret["result"] = None return ret - was_set = __salt__['boto_elb.set_attributes'](name, attributes, - region, key, keyid, - profile) + was_set = __salt__["boto_elb.set_attributes"]( + name, attributes, region, key, keyid, profile + ) if was_set: - ret['changes']['old'] = {'attributes': _attributes} - ret['changes']['new'] = {'attributes': attributes} - ret['comment'] = 'Set attributes on ELB {0}.'.format(name) + ret["changes"]["old"] = {"attributes": _attributes} + ret["changes"]["new"] = {"attributes": attributes} + ret["comment"] = "Set attributes on ELB {0}.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to set attributes on ELB {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to set attributes on ELB {0}.".format(name) else: - ret['comment'] = 'Attributes already set on ELB {0}.'.format(name) + ret["comment"] = "Attributes already set on ELB {0}.".format(name) return ret def _health_check_present(name, health_check, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} + ret = {"result": True, "comment": "", "changes": {}} if not health_check: health_check = {} - _health_check = __salt__['boto_elb.get_health_check'](name, region, key, - keyid, profile) + _health_check = __salt__["boto_elb.get_health_check"]( + name, region, key, keyid, profile + ) if not _health_check: - ret['result'] = False - ret['comment'] = 'Failed to retrieve health_check for ELB {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to retrieve health_check for ELB {0}.".format(name) return ret need_to_set = False for attr, val in six.iteritems(health_check): if six.text_type(_health_check[attr]) != six.text_type(val): need_to_set = True if need_to_set: - if __opts__['test']: - ret['comment'] = 'ELB {0} set to have health check set.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ELB {0} set to have health check set.".format(name) + ret["result"] = None return ret - was_set = __salt__['boto_elb.set_health_check'](name, health_check, - region, key, keyid, - profile) + was_set = __salt__["boto_elb.set_health_check"]( + name, health_check, region, key, keyid, profile + ) if was_set: - ret['changes']['old'] = {'health_check': _health_check} - _health_check = __salt__['boto_elb.get_health_check'](name, region, - key, keyid, - profile) - ret['changes']['new'] = {'health_check': _health_check} - ret['comment'] = 'Set health check on ELB {0}.'.format(name) + ret["changes"]["old"] = {"health_check": _health_check} + _health_check = __salt__["boto_elb.get_health_check"]( + name, region, key, keyid, profile + ) + ret["changes"]["new"] = {"health_check": _health_check} + ret["comment"] = "Set health check on ELB {0}.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to set health check on ELB {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to set health check on ELB {0}.".format(name) else: - ret['comment'] = 'Health check already set on ELB {0}.'.format(name) + ret["comment"] = "Health check already set on ELB {0}.".format(name) return ret def _zones_present(name, availability_zones, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile) + ret = {"result": True, "comment": "", "changes": {}} + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) if not lb: - ret['result'] = False - ret['comment'] = 'Failed to retrieve ELB {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to retrieve ELB {0}.".format(name) return ret to_enable = [] to_disable = [] - _zones = lb['availability_zones'] + _zones = lb["availability_zones"] for zone in availability_zones: if zone not in _zones: to_enable.append(zone) @@ -895,51 +1039,52 @@ def _zones_present(name, availability_zones, region, key, keyid, profile): if zone not in availability_zones: to_disable.append(zone) if to_enable or to_disable: - if __opts__['test']: - ret['comment'] = 'ELB {0} to have availability zones set.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ELB {0} to have availability zones set.".format(name) + ret["result"] = None return ret if to_enable: - enabled = __salt__['boto_elb.enable_availability_zones']( - name, to_enable, region, key, keyid, profile) + enabled = __salt__["boto_elb.enable_availability_zones"]( + name, to_enable, region, key, keyid, profile + ) if enabled: - ret['comment'] = 'Enabled availability zones on {0} ELB.'.format(name) + ret["comment"] = "Enabled availability zones on {0} ELB.".format(name) else: - ret['comment'] = 'Failed to enable availability zones on {0} ELB.'.format(name) - ret['result'] = False + ret[ + "comment" + ] = "Failed to enable availability zones on {0} ELB.".format(name) + ret["result"] = False if to_disable: - disabled = __salt__['boto_elb.disable_availability_zones']( - name, to_disable, region, key, keyid, profile) + disabled = __salt__["boto_elb.disable_availability_zones"]( + name, to_disable, region, key, keyid, profile + ) if disabled: - msg = 'Disabled availability zones on {0} ELB.' - ret['comment'] = ' '.join([ret['comment'], msg.format(name)]) + msg = "Disabled availability zones on {0} ELB." + ret["comment"] = " ".join([ret["comment"], msg.format(name)]) else: - msg = 'Failed to disable availability zones on {0} ELB.' - ret['comment'] = ' '.join([ret['comment'], msg.format(name)]) - ret['result'] = False - ret['changes']['old'] = {'availability_zones': - lb['availability_zones']} - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, - profile) - ret['changes']['new'] = {'availability_zones': - lb['availability_zones']} + msg = "Failed to disable availability zones on {0} ELB." + ret["comment"] = " ".join([ret["comment"], msg.format(name)]) + ret["result"] = False + ret["changes"]["old"] = {"availability_zones": lb["availability_zones"]} + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) + ret["changes"]["new"] = {"availability_zones": lb["availability_zones"]} else: - ret['comment'] = 'Availability zones already set on ELB {0}.'.format(name) + ret["comment"] = "Availability zones already set on ELB {0}.".format(name) return ret def _subnets_present(name, subnets, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} + ret = {"result": True, "comment": "", "changes": {}} if not subnets: subnets = [] - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile) + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) if not lb: - ret['result'] = False - ret['comment'] = 'Failed to retrieve ELB {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to retrieve ELB {0}.".format(name) return ret to_enable = [] to_disable = [] - _subnets = lb['subnets'] + _subnets = lb["subnets"] for subnet in subnets: if subnet not in _subnets: to_enable.append(subnet) @@ -947,52 +1092,54 @@ def _subnets_present(name, subnets, region, key, keyid, profile): if subnet not in subnets: to_disable.append(subnet) if to_enable or to_disable: - if __opts__['test']: - ret['comment'] = 'ELB {0} to have subnets set.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ELB {0} to have subnets set.".format(name) + ret["result"] = None return ret if to_enable: - attached = __salt__['boto_elb.attach_subnets'](name, to_enable, - region, key, keyid, - profile) + attached = __salt__["boto_elb.attach_subnets"]( + name, to_enable, region, key, keyid, profile + ) if attached: - ret['comment'] = 'Attached subnets on {0} ELB.'.format(name) + ret["comment"] = "Attached subnets on {0} ELB.".format(name) else: - ret['comment'] = 'Failed to attach subnets on {0} ELB.'.format(name) - ret['result'] = False + ret["comment"] = "Failed to attach subnets on {0} ELB.".format(name) + ret["result"] = False if to_disable: - detached = __salt__['boto_elb.detach_subnets'](name, to_disable, - region, key, keyid, - profile) + detached = __salt__["boto_elb.detach_subnets"]( + name, to_disable, region, key, keyid, profile + ) if detached: - ret['comment'] = ' '.join([ - ret['comment'], - 'Detached subnets on {0} ELB.'.format(name) - ]) + ret["comment"] = " ".join( + [ret["comment"], "Detached subnets on {0} ELB.".format(name)] + ) else: - ret['comment'] = ' '.join([ - ret['comment'], - 'Failed to detach subnets on {0} ELB.'.format(name) - ]) - ret['result'] = False - ret['changes']['old'] = {'subnets': lb['subnets']} - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, - profile) - ret['changes']['new'] = {'subnets': lb['subnets']} + ret["comment"] = " ".join( + [ + ret["comment"], + "Failed to detach subnets on {0} ELB.".format(name), + ] + ) + ret["result"] = False + ret["changes"]["old"] = {"subnets": lb["subnets"]} + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) + ret["changes"]["new"] = {"subnets": lb["subnets"]} else: - ret['comment'] = 'Subnets already set on ELB {0}.'.format(name) + ret["comment"] = "Subnets already set on ELB {0}.".format(name) return ret def _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profile): - '''helper method for present. ensure that cloudwatch_alarms are set''' - current = __salt__['config.option'](alarms_from_pillar, {}) + """helper method for present. ensure that cloudwatch_alarms are set""" + current = __salt__["config.option"](alarms_from_pillar, {}) if alarms: current = salt.utils.dictupdate.update(current, alarms) - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} for _, info in six.iteritems(current): info["name"] = name + " " + info["name"] - info["attributes"]["description"] = name + " " + info["attributes"]["description"] + info["attributes"]["description"] = ( + name + " " + info["attributes"]["description"] + ) info["attributes"]["dimensions"] = {"LoadBalancerName": [name]} kwargs = { "name": info["name"], @@ -1003,8 +1150,8 @@ def _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profil "profile": profile, } # No test=False cluase needed since the state handles that itself... - results = __states__['boto_cloudwatch_alarm.present'](**kwargs) - if not results.get('result'): + results = __states__["boto_cloudwatch_alarm.present"](**kwargs) + if not results.get("result"): ret["result"] = results["result"] if results.get("changes", {}) != {}: ret["changes"][info["name"]] = results["changes"] @@ -1013,12 +1160,21 @@ def _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profil return ret -def _policies_present(name, policies, policies_from_pillar, listeners, backends, - region, key, keyid, profile): - '''helper method for present. ensure that ELB policies are set''' +def _policies_present( + name, + policies, + policies_from_pillar, + listeners, + backends, + region, + key, + keyid, + profile, +): + """helper method for present. ensure that ELB policies are set""" if policies is None: policies = [] - pillar_policies = __salt__['config.option'](policies_from_pillar, []) + pillar_policies = __salt__["config.option"](policies_from_pillar, []) policies = policies + pillar_policies if backends is None: backends = [] @@ -1026,42 +1182,49 @@ def _policies_present(name, policies, policies_from_pillar, listeners, backends, # check for policy name uniqueness and correct type policy_names = set() for p in policies: - if 'policy_name' not in p: - raise SaltInvocationError('policy_name is a required value for ' - 'policies.') - if 'policy_type' not in p: - raise SaltInvocationError('policy_type is a required value for ' - 'policies.') - if 'policy' not in p: - raise SaltInvocationError('policy is a required value for ' - 'listeners.') + if "policy_name" not in p: + raise SaltInvocationError( + "policy_name is a required value for " "policies." + ) + if "policy_type" not in p: + raise SaltInvocationError( + "policy_type is a required value for " "policies." + ) + if "policy" not in p: + raise SaltInvocationError("policy is a required value for " "listeners.") # check for unique policy names - if p['policy_name'] in policy_names: - raise SaltInvocationError('Policy names must be unique: policy {0}' - ' is declared twice.'.format(p['policy_name'])) - policy_names.add(p['policy_name']) + if p["policy_name"] in policy_names: + raise SaltInvocationError( + "Policy names must be unique: policy {0}" + " is declared twice.".format(p["policy_name"]) + ) + policy_names.add(p["policy_name"]) # check that listeners refer to valid policy names for l in listeners: - for p in l.get('policies', []): + for p in l.get("policies", []): if p not in policy_names: - raise SaltInvocationError('Listener {0} on ELB {1} refers to ' - 'undefined policy {2}.'.format(l['elb_port'], name, p)) + raise SaltInvocationError( + "Listener {0} on ELB {1} refers to " + "undefined policy {2}.".format(l["elb_port"], name, p) + ) # check that backends refer to valid policy names for b in backends: - for p in b.get('policies', []): + for p in b.get("policies", []): if p not in policy_names: - raise SaltInvocationError('Backend {0} on ELB {1} refers to ' - 'undefined policy ' - '{2}.'.format(b['instance_port'], name, p)) + raise SaltInvocationError( + "Backend {0} on ELB {1} refers to " + "undefined policy " + "{2}.".format(b["instance_port"], name, p) + ) - ret = {'result': True, 'comment': '', 'changes': {}} + ret = {"result": True, "comment": "", "changes": {}} - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile) + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) if not lb: - ret['comment'] = '{0} ELB configuration could not be retrieved.'.format(name) - ret['result'] = False + ret["comment"] = "{0} ELB configuration could not be retrieved.".format(name) + ret["result"] = False return ret # Policies have two names: @@ -1074,10 +1237,10 @@ def _policies_present(name, policies, policies_from_pillar, listeners, backends, for p in policies: cname = _policy_cname(p) policies_by_cname[cname] = p - cnames_by_name[p['policy_name']] = cname + cnames_by_name[p["policy_name"]] = cname expected_policy_names = policies_by_cname.keys() - actual_policy_names = lb['policies'] + actual_policy_names = lb["policies"] # This is sadly a huge hack to get around the fact that AWS assigns a # default SSLNegotiationPolicyType policy (with the naming scheme @@ -1090,29 +1253,31 @@ def _policies_present(name, policies, policies_from_pillar, listeners, backends, expected_policies_by_listener = {} for l in listeners: - expected_policies_by_listener[l['elb_port']] = set( - [cnames_by_name[p] for p in l.get('policies', [])]) + expected_policies_by_listener[l["elb_port"]] = set( + [cnames_by_name[p] for p in l.get("policies", [])] + ) actual_policies_by_listener = {} - for l in lb['listeners']: - listener_policies = set(l.get('policies', [])) - actual_policies_by_listener[l['elb_port']] = listener_policies + for l in lb["listeners"]: + listener_policies = set(l.get("policies", [])) + actual_policies_by_listener[l["elb_port"]] = listener_policies # Determine if any actual listener policies look like default policies, # so we can exclude them from deletion below (see note about this hack # above). for p in listener_policies: - if re.match(r'^ELBSecurityPolicy-\d{4}-\d{2}$', p): + if re.match(r"^ELBSecurityPolicy-\d{4}-\d{2}$", p): default_aws_policies.add(p) expected_policies_by_backend = {} for b in backends: - expected_policies_by_backend[b['instance_port']] = set( - [cnames_by_name[p] for p in b.get('policies', [])]) + expected_policies_by_backend[b["instance_port"]] = set( + [cnames_by_name[p] for p in b.get("policies", [])] + ) actual_policies_by_backend = {} - for b in lb['backends']: - backend_policies = set(b.get('policies', [])) - actual_policies_by_backend[b['instance_port']] = backend_policies + for b in lb["backends"]: + backend_policies = set(b.get("policies", [])) + actual_policies_by_backend[b["instance_port"]] = backend_policies to_delete = [] to_create = [] @@ -1141,224 +1306,250 @@ def _policies_present(name, policies, policies_from_pillar, listeners, backends, if policies != expected_policies_by_backend.get(port, set()): backends_to_update.add(port) - if __opts__['test']: + if __opts__["test"]: msg = [] if to_create or to_delete: - msg.append('ELB {0} set to have policies modified:'.format(name)) + msg.append("ELB {0} set to have policies modified:".format(name)) for policy in to_create: - msg.append('Policy {0} added.'.format(policy)) + msg.append("Policy {0} added.".format(policy)) for policy in to_delete: - msg.append('Policy {0} deleted.'.format(policy)) - ret['result'] = None + msg.append("Policy {0} deleted.".format(policy)) + ret["result"] = None else: - msg.append('Policies already set on ELB {0}.'.format(name)) + msg.append("Policies already set on ELB {0}.".format(name)) for listener in listeners_to_update: - msg.append('Listener {0} policies updated.'.format(listener)) + msg.append("Listener {0} policies updated.".format(listener)) for backend in backends_to_update: - msg.append('Backend {0} policies updated.'.format(backend)) - ret['comment'] = ' '.join(msg) + msg.append("Backend {0} policies updated.".format(backend)) + ret["comment"] = " ".join(msg) return ret if to_create: for policy_name in to_create: - created = __salt__['boto_elb.create_policy']( + created = __salt__["boto_elb.create_policy"]( name=name, policy_name=policy_name, - policy_type=policies_by_cname[policy_name]['policy_type'], - policy=policies_by_cname[policy_name]['policy'], + policy_type=policies_by_cname[policy_name]["policy_type"], + policy=policies_by_cname[policy_name]["policy"], region=region, key=key, keyid=keyid, - profile=profile) + profile=profile, + ) if created: - ret['changes'].setdefault(policy_name, {})['new'] = policy_name - comment = "Policy {0} was created on ELB {1}".format( - policy_name, name) - ret['comment'] = ' '.join([ret['comment'], comment]) - ret['result'] = True + ret["changes"].setdefault(policy_name, {})["new"] = policy_name + comment = "Policy {0} was created on ELB {1}".format(policy_name, name) + ret["comment"] = " ".join([ret["comment"], comment]) + ret["result"] = True else: - ret['result'] = False + ret["result"] = False return ret for port in listeners_to_update: - policy_set = __salt__['boto_elb.set_listener_policy']( - name=name, - port=port, - policies=list(expected_policies_by_listener.get(port, [])), - region=region, - key=key, - keyid=keyid, - profile=profile) + policy_set = __salt__["boto_elb.set_listener_policy"]( + name=name, + port=port, + policies=list(expected_policies_by_listener.get(port, [])), + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if policy_set: - policy_key = 'listener_{0}_policy'.format(port) - ret['changes'][policy_key] = { - 'old': list(actual_policies_by_listener.get(port, [])), - 'new': list(expected_policies_by_listener.get(port, [])), - } + policy_key = "listener_{0}_policy".format(port) + ret["changes"][policy_key] = { + "old": list(actual_policies_by_listener.get(port, [])), + "new": list(expected_policies_by_listener.get(port, [])), + } comment = "Policy {0} was created on ELB {1} listener {2}".format( - expected_policies_by_listener[port], name, port) - ret['comment'] = ' '.join([ret['comment'], comment]) - ret['result'] = True + expected_policies_by_listener[port], name, port + ) + ret["comment"] = " ".join([ret["comment"], comment]) + ret["result"] = True else: - ret['result'] = False + ret["result"] = False return ret for port in backends_to_update: - policy_set = __salt__['boto_elb.set_backend_policy']( - name=name, - port=port, - policies=list(expected_policies_by_backend.get(port, [])), - region=region, - key=key, - keyid=keyid, - profile=profile) + policy_set = __salt__["boto_elb.set_backend_policy"]( + name=name, + port=port, + policies=list(expected_policies_by_backend.get(port, [])), + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if policy_set: - policy_key = 'backend_{0}_policy'.format(port) - ret['changes'][policy_key] = { - 'old': list(actual_policies_by_backend.get(port, [])), - 'new': list(expected_policies_by_backend.get(port, [])), - } + policy_key = "backend_{0}_policy".format(port) + ret["changes"][policy_key] = { + "old": list(actual_policies_by_backend.get(port, [])), + "new": list(expected_policies_by_backend.get(port, [])), + } comment = "Policy {0} was created on ELB {1} backend {2}".format( - expected_policies_by_backend[port], name, port) - ret['comment'] = ' '.join([ret['comment'], comment]) - ret['result'] = True + expected_policies_by_backend[port], name, port + ) + ret["comment"] = " ".join([ret["comment"], comment]) + ret["result"] = True else: - ret['result'] = False + ret["result"] = False return ret if to_delete: for policy_name in to_delete: - deleted = __salt__['boto_elb.delete_policy']( + deleted = __salt__["boto_elb.delete_policy"]( name=name, policy_name=policy_name, region=region, key=key, keyid=keyid, - profile=profile) + profile=profile, + ) if deleted: - ret['changes'].setdefault(policy_name, {})['old'] = policy_name + ret["changes"].setdefault(policy_name, {})["old"] = policy_name comment = "Policy {0} was deleted from ELB {1}".format( - policy_name, name) - ret['comment'] = ' '.join([ret['comment'], comment]) - ret['result'] = True + policy_name, name + ) + ret["comment"] = " ".join([ret["comment"], comment]) + ret["result"] = True else: - ret['result'] = False + ret["result"] = False return ret return ret def _policy_cname(policy_dict): - policy_name = policy_dict['policy_name'] - policy_type = policy_dict['policy_type'] - policy = policy_dict['policy'] - canonical_policy_repr = six.text_type(sorted(list(six.iteritems(policy)), key=lambda x: six.text_type(x[0]))) + policy_name = policy_dict["policy_name"] + policy_type = policy_dict["policy_type"] + policy = policy_dict["policy"] + canonical_policy_repr = six.text_type( + sorted(list(six.iteritems(policy)), key=lambda x: six.text_type(x[0])) + ) policy_hash = hashlib.md5( - salt.utils.stringutils.to_bytes(str(canonical_policy_repr))).hexdigest() # future lint: disable=blacklisted-function - if policy_type.endswith('Type'): + salt.utils.stringutils.to_bytes(str(canonical_policy_repr)) + ).hexdigest() # future lint: disable=blacklisted-function + if policy_type.endswith("Type"): policy_type = policy_type[:-4] return "{0}-{1}-{2}".format(policy_type, policy_name, policy_hash) def absent(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Ensure an ELB does not exist name name of the ELB - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - exists = __salt__['boto_elb.exists'](name, region, key, keyid, profile) + exists = __salt__["boto_elb.exists"](name, region, key, keyid, profile) if exists: - if __opts__['test']: - ret['comment'] = 'ELB {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "ELB {0} is set to be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_elb.delete'](name, region, key, keyid, - profile) + deleted = __salt__["boto_elb.delete"](name, region, key, keyid, profile) if deleted: - ret['changes']['old'] = {'elb': name} - ret['changes']['new'] = {'elb': None} - ret['comment'] = 'ELB {0} deleted.'.format(name) + ret["changes"]["old"] = {"elb": name} + ret["changes"]["new"] = {"elb": None} + ret["comment"] = "ELB {0} deleted.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} ELB.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} ELB.".format(name) else: - ret['comment'] = '{0} ELB does not exist.'.format(name) + ret["comment"] = "{0} ELB does not exist.".format(name) return ret def _tags_present(name, tags, region, key, keyid, profile): - ''' + """ helper function to validate tags on elb - ''' - ret = {'result': True, 'comment': '', 'changes': {}} + """ + ret = {"result": True, "comment": "", "changes": {}} if tags: - lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile) + lb = __salt__["boto_elb.get_elb_config"](name, region, key, keyid, profile) tags_to_add = tags tags_to_update = {} tags_to_remove = [] - if lb.get('tags'): - for _tag in lb['tags']: + if lb.get("tags"): + for _tag in lb["tags"]: if _tag not in tags.keys(): if _tag not in tags_to_remove: tags_to_remove.append(_tag) else: - if tags[_tag] != lb['tags'][_tag]: + if tags[_tag] != lb["tags"][_tag]: tags_to_update[_tag] = tags[_tag] tags_to_add.pop(_tag) if tags_to_remove: - if __opts__['test']: - msg = 'The following tag{0} set to be removed: {1}.'.format( - ('s are' if len(tags_to_remove) > 1 else ' is'), ', '.join(tags_to_remove)) - ret['comment'] = ' '.join([ret['comment'], msg]) - ret['result'] = None + if __opts__["test"]: + msg = "The following tag{0} set to be removed: {1}.".format( + ("s are" if len(tags_to_remove) > 1 else " is"), + ", ".join(tags_to_remove), + ) + ret["comment"] = " ".join([ret["comment"], msg]) + ret["result"] = None else: - _ret = __salt__['boto_elb.delete_tags']( - name, tags_to_remove, region, key, keyid, profile) + _ret = __salt__["boto_elb.delete_tags"]( + name, tags_to_remove, region, key, keyid, profile + ) if not _ret: - ret['result'] = False - msg = 'Error attempting to delete tag {0}.'.format(tags_to_remove) - ret['comment'] = ' '.join([ret['comment'], msg]) + ret["result"] = False + msg = "Error attempting to delete tag {0}.".format(tags_to_remove) + ret["comment"] = " ".join([ret["comment"], msg]) return ret - if 'old' not in ret['changes']: - ret['changes'] = salt.utils.dictupdate.update(ret['changes'], {'old': {'tags': {}}}) + if "old" not in ret["changes"]: + ret["changes"] = salt.utils.dictupdate.update( + ret["changes"], {"old": {"tags": {}}} + ) for _tag in tags_to_remove: - ret['changes']['old']['tags'][_tag] = lb['tags'][_tag] + ret["changes"]["old"]["tags"][_tag] = lb["tags"][_tag] if tags_to_add or tags_to_update: - if __opts__['test']: + if __opts__["test"]: if tags_to_add: - msg = 'The following tag{0} set to be added: {1}.'.format( - ('s are' if len(tags_to_add.keys()) > 1 else ' is'), - ', '.join(tags_to_add.keys())) - ret['comment'] = ' '. join([ret['comment'], msg]) - ret['result'] = None + msg = "The following tag{0} set to be added: {1}.".format( + ("s are" if len(tags_to_add.keys()) > 1 else " is"), + ", ".join(tags_to_add.keys()), + ) + ret["comment"] = " ".join([ret["comment"], msg]) + ret["result"] = None if tags_to_update: - msg = 'The following tag {0} set to be updated: {1}.'.format( - ('values are' if len(tags_to_update.keys()) > 1 else 'value is'), - ', '.join(tags_to_update.keys())) - ret['comment'] = ' '.join([ret['comment'], msg]) + msg = "The following tag {0} set to be updated: {1}.".format( + ( + "values are" + if len(tags_to_update.keys()) > 1 + else "value is" + ), + ", ".join(tags_to_update.keys()), + ) + ret["comment"] = " ".join([ret["comment"], msg]) else: - all_tag_changes = salt.utils.dictupdate.update(tags_to_add, tags_to_update) - _ret = __salt__['boto_elb.set_tags']( - name, all_tag_changes, region, key, keyid, profile) + all_tag_changes = salt.utils.dictupdate.update( + tags_to_add, tags_to_update + ) + _ret = __salt__["boto_elb.set_tags"]( + name, all_tag_changes, region, key, keyid, profile + ) if not _ret: - ret['result'] = False - msg = 'Error attempting to set tags.' - ret['comment'] = ' '.join([ret['comment'], msg]) + ret["result"] = False + msg = "Error attempting to set tags." + ret["comment"] = " ".join([ret["comment"], msg]) return ret - if 'old' not in ret['changes']: - ret['changes'] = salt.utils.dictupdate.update(ret['changes'], {'old': {'tags': {}}}) - if 'new' not in ret['changes']: - ret['changes'] = salt.utils.dictupdate.update(ret['changes'], {'new': {'tags': {}}}) + if "old" not in ret["changes"]: + ret["changes"] = salt.utils.dictupdate.update( + ret["changes"], {"old": {"tags": {}}} + ) + if "new" not in ret["changes"]: + ret["changes"] = salt.utils.dictupdate.update( + ret["changes"], {"new": {"tags": {}}} + ) for tag in all_tag_changes: - ret['changes']['new']['tags'][tag] = tags[tag] - if 'tags' in lb: - if lb['tags']: - if tag in lb['tags']: - ret['changes']['old']['tags'][tag] = lb['tags'][tag] + ret["changes"]["new"]["tags"][tag] = tags[tag] + if "tags" in lb: + if lb["tags"]: + if tag in lb["tags"]: + ret["changes"]["old"]["tags"][tag] = lb["tags"][tag] if not tags_to_update and not tags_to_remove and not tags_to_add: - msg = 'Tags are already set.' - ret['comment'] = ' '.join([ret['comment'], msg]) + msg = "Tags are already set." + ret["comment"] = " ".join([ret["comment"], msg]) return ret diff --git a/salt/states/boto_elbv2.py b/salt/states/boto_elbv2.py index c5d9ae5cd32..3d5faaec696 100644 --- a/salt/states/boto_elbv2.py +++ b/salt/states/boto_elbv2.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage AWS Application Load Balancer .. versionadded:: 2017.7.0 @@ -32,13 +32,14 @@ passed in as a dict, or as a string to pull from pillars or minion config: keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs region: us-east-1 -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import copy + # Import Python Libs import logging -import copy # Import 3rd-party libs from salt.ext import six @@ -47,22 +48,34 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - if 'boto_elbv2.target_group_exists' in __salt__: - return 'boto_elbv2' + """ + if "boto_elbv2.target_group_exists" in __salt__: + return "boto_elbv2" return (False, "The boto_elbv2 module cannot be loaded: boto3 library not found") -def create_target_group(name, protocol, port, vpc_id, - region=None, key=None, keyid=None, profile=None, - health_check_protocol='HTTP', health_check_port='traffic-port', - health_check_path='/', health_check_interval_seconds=30, - health_check_timeout_seconds=5, healthy_threshold_count=5, - unhealthy_threshold_count=2, **kwargs): +def create_target_group( + name, + protocol, + port, + vpc_id, + region=None, + key=None, + keyid=None, + profile=None, + health_check_protocol="HTTP", + health_check_port="traffic-port", + health_check_path="/", + health_check_interval_seconds=30, + health_check_timeout_seconds=5, + healthy_threshold_count=5, + unhealthy_threshold_count=2, + **kwargs +): - ''' + """ .. versionadded:: 2017.11.0 Create target group if not present. @@ -111,47 +124,49 @@ def create_target_group(name, protocol, port, vpc_id, - protocol: https - port: 443 - vpc_id: myVPC - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile): - ret['result'] = True - ret['comment'] = 'Target Group {0} already exists'.format(name) + if __salt__["boto_elbv2.target_group_exists"](name, region, key, keyid, profile): + ret["result"] = True + ret["comment"] = "Target Group {0} already exists".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Target Group {0} will be created'.format(name) + if __opts__["test"]: + ret["comment"] = "Target Group {0} will be created".format(name) return ret - state = __salt__['boto_elbv2.create_target_group'](name, - protocol, - port, - vpc_id, - region=region, - key=key, - keyid=keyid, - profile=profile, - health_check_protocol=health_check_protocol, - health_check_port=health_check_port, - health_check_path=health_check_path, - health_check_interval_seconds=health_check_interval_seconds, - health_check_timeout_seconds=health_check_timeout_seconds, - healthy_threshold_count=healthy_threshold_count, - unhealthy_threshold_count=unhealthy_threshold_count, - **kwargs) + state = __salt__["boto_elbv2.create_target_group"]( + name, + protocol, + port, + vpc_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + health_check_protocol=health_check_protocol, + health_check_port=health_check_port, + health_check_path=health_check_path, + health_check_interval_seconds=health_check_interval_seconds, + health_check_timeout_seconds=health_check_timeout_seconds, + healthy_threshold_count=healthy_threshold_count, + unhealthy_threshold_count=unhealthy_threshold_count, + **kwargs + ) if state: - ret['changes']['target_group'] = name - ret['result'] = True - ret['comment'] = 'Target Group {0} created'.format(name) + ret["changes"]["target_group"] = name + ret["result"] = True + ret["comment"] = "Target Group {0} created".format(name) else: - ret['result'] = False - ret['comment'] = 'Target Group {0} creation failed'.format(name) + ret["result"] = False + ret["comment"] = "Target Group {0} creation failed".format(name) return ret def delete_target_group(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Delete target group. name @@ -170,37 +185,38 @@ def delete_target_group(name, region=None, key=None, keyid=None, profile=None): - protocol: https - port: 443 - vpc_id: myVPC - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if not __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile): - ret['result'] = True - ret['comment'] = 'Target Group {0} does not exists'.format(name) + if not __salt__["boto_elbv2.target_group_exists"]( + name, region, key, keyid, profile + ): + ret["result"] = True + ret["comment"] = "Target Group {0} does not exists".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Target Group {0} will be deleted'.format(name) + if __opts__["test"]: + ret["comment"] = "Target Group {0} will be deleted".format(name) return ret - state = __salt__['boto_elbv2.delete_target_group'](name, - region=region, - key=key, - keyid=keyid, - profile=profile) + state = __salt__["boto_elbv2.delete_target_group"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if state: - ret['result'] = True - ret['changes']['target_group'] = name - ret['comment'] = 'Target Group {0} deleted'.format(name) + ret["result"] = True + ret["changes"]["target_group"] = name + ret["comment"] = "Target Group {0} deleted".format(name) else: - ret['result'] = False - ret['comment'] = 'Target Group {0} deletion failed'.format(name) + ret["result"] = False + ret["comment"] = "Target Group {0} deletion failed".format(name) return ret -def targets_registered(name, targets, region=None, key=None, keyid=None, - profile=None, **kwargs): - ''' +def targets_registered( + name, targets, region=None, key=None, keyid=None, profile=None, **kwargs +): + """ .. versionadded:: 2017.7.0 Add targets to an Application Load Balancer target group. This state will not remove targets. @@ -220,15 +236,13 @@ def targets_registered(name, targets, region=None, key=None, keyid=None, - targets: - instance-id1 - instance-id2 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile): - health = __salt__['boto_elbv2.describe_target_health'](name, - region=region, - key=key, - keyid=keyid, - profile=profile) + if __salt__["boto_elbv2.target_group_exists"](name, region, key, keyid, profile): + health = __salt__["boto_elbv2.describe_target_health"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) failure = False changes = False newhealth_mock = copy.copy(health) @@ -238,50 +252,57 @@ def targets_registered(name, targets, region=None, key=None, keyid=None, for target in targets: if target in health and health.get(target) != "draining": - ret['comment'] = ret['comment'] + 'Target/s {0} already registered and is {1}.\n'.format(target, health[target]) - ret['result'] = True + ret["comment"] = ret[ + "comment" + ] + "Target/s {0} already registered and is {1}.\n".format( + target, health[target] + ) + ret["result"] = True else: - if __opts__['test']: + if __opts__["test"]: changes = True newhealth_mock.update({target: "initial"}) else: - state = __salt__['boto_elbv2.register_targets'](name, - targets, - region=region, - key=key, - keyid=keyid, - profile=profile) + state = __salt__["boto_elbv2.register_targets"]( + name, + targets, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if state: changes = True - ret['result'] = True + ret["result"] = True else: - ret['comment'] = 'Target Group {0} failed to add targets'.format(name) + ret[ + "comment" + ] = "Target Group {0} failed to add targets".format(name) failure = True if failure: - ret['result'] = False + ret["result"] = False if changes: - ret['changes']['old'] = health - if __opts__['test']: - ret['comment'] = 'Target Group {0} would be changed'.format(name) - ret['result'] = None - ret['changes']['new'] = newhealth_mock + ret["changes"]["old"] = health + if __opts__["test"]: + ret["comment"] = "Target Group {0} would be changed".format(name) + ret["result"] = None + ret["changes"]["new"] = newhealth_mock else: - ret['comment'] = 'Target Group {0} has been changed'.format(name) - newhealth = __salt__['boto_elbv2.describe_target_health'](name, - region=region, - key=key, - keyid=keyid, - profile=profile) - ret['changes']['new'] = newhealth + ret["comment"] = "Target Group {0} has been changed".format(name) + newhealth = __salt__["boto_elbv2.describe_target_health"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["new"] = newhealth return ret else: - ret['comment'] = 'Could not find target group {0}'.format(name) + ret["comment"] = "Could not find target group {0}".format(name) return ret -def targets_deregistered(name, targets, region=None, key=None, keyid=None, - profile=None, **kwargs): - ''' +def targets_deregistered( + name, targets, region=None, key=None, keyid=None, profile=None, **kwargs +): + """ Remove targets to an Application Load Balancer target group. name @@ -300,14 +321,12 @@ def targets_deregistered(name, targets, region=None, key=None, keyid=None, - targets: - instance-id1 - instance-id2 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} - if __salt__['boto_elbv2.target_group_exists'](name, region, key, keyid, profile): - health = __salt__['boto_elbv2.describe_target_health'](name, - region=region, - key=key, - keyid=keyid, - profile=profile) + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} + if __salt__["boto_elbv2.target_group_exists"](name, region, key, keyid, profile): + health = __salt__["boto_elbv2.describe_target_health"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) failure = False changes = False newhealth_mock = copy.copy(health) @@ -315,42 +334,46 @@ def targets_deregistered(name, targets, region=None, key=None, keyid=None, targets = [targets] for target in targets: if target not in health or health.get(target) == "draining": - ret['comment'] = ret['comment'] + 'Target/s {0} already deregistered\n'.format(target) - ret['result'] = True + ret["comment"] = ret[ + "comment" + ] + "Target/s {0} already deregistered\n".format(target) + ret["result"] = True else: - if __opts__['test']: + if __opts__["test"]: changes = True newhealth_mock.update({target: "draining"}) else: - state = __salt__['boto_elbv2.deregister_targets'](name, - targets, - region=region, - key=key, - keyid=keyid, - profile=profile) + state = __salt__["boto_elbv2.deregister_targets"]( + name, + targets, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if state: changes = True - ret['result'] = True + ret["result"] = True else: - ret['comment'] = 'Target Group {0} failed to remove targets'.format(name) + ret[ + "comment" + ] = "Target Group {0} failed to remove targets".format(name) failure = True if failure: - ret['result'] = False + ret["result"] = False if changes: - ret['changes']['old'] = health - if __opts__['test']: - ret['comment'] = 'Target Group {0} would be changed'.format(name) - ret['result'] = None - ret['changes']['new'] = newhealth_mock + ret["changes"]["old"] = health + if __opts__["test"]: + ret["comment"] = "Target Group {0} would be changed".format(name) + ret["result"] = None + ret["changes"]["new"] = newhealth_mock else: - ret['comment'] = 'Target Group {0} has been changed'.format(name) - newhealth = __salt__['boto_elbv2.describe_target_health'](name, - region=region, - key=key, - keyid=keyid, - profile=profile) - ret['changes']['new'] = newhealth + ret["comment"] = "Target Group {0} has been changed".format(name) + newhealth = __salt__["boto_elbv2.describe_target_health"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["new"] = newhealth return ret else: - ret['comment'] = 'Could not find target group {0}'.format(name) + ret["comment"] = "Could not find target group {0}".format(name) return ret diff --git a/salt/states/boto_iam.py b/salt/states/boto_iam.py index 81c6762ea5c..e05b1f0dbdc 100644 --- a/salt/states/boto_iam.py +++ b/salt/states/boto_iam.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage IAM objects ================== @@ -129,36 +129,39 @@ passed in as a dict, or as a string to pull from pillars or minion config: - saml_metadata_document: salt://base/files/provider.xml - keyid: 'AKIAJHTMIQ2ASDFLASDF' - key: 'safsdfsal;fdkjsafkljsASSADFalkfj' -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os # Import Salt Libs import salt.utils.data +import salt.utils.dictupdate as dictupdate import salt.utils.files import salt.utils.json -import salt.utils.stringutils import salt.utils.odict as odict -import salt.utils.dictupdate as dictupdate +import salt.utils.stringutils from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin # Import 3rd party libs try: from salt._compat import ElementTree as ET + HAS_ELEMENT_TREE = True except ImportError: HAS_ELEMENT_TREE = False log = logging.getLogger(__name__) -__virtualname__ = 'boto_iam' +__virtualname__ = "boto_iam" if six.PY2: + def _byteify(thing): # Note that we intentionally don't treat odicts here - they won't # compare equal in many circumstances where AWS treats them the same... @@ -167,30 +170,51 @@ if six.PY2: elif isinstance(thing, list): return [_byteify(m) for m in thing] elif isinstance(thing, six.text_type): # pylint: disable=W1699 - return thing.encode('utf-8') + return thing.encode("utf-8") else: return thing + else: # six.PY3 + def _byteify(text): return text def __virtual__(): - ''' + """ Only load if elementtree xml library and boto are available. - ''' + """ if not HAS_ELEMENT_TREE: - return (False, 'Cannot load {0} state: ElementTree library unavailable'.format(__virtualname__)) + return ( + False, + "Cannot load {0} state: ElementTree library unavailable".format( + __virtualname__ + ), + ) - if 'boto_iam.get_user' in __salt__: + if "boto_iam.get_user" in __salt__: return True else: - return (False, 'Cannot load {0} state: boto_iam module unavailable'.format(__virtualname__)) + return ( + False, + "Cannot load {0} state: boto_iam module unavailable".format( + __virtualname__ + ), + ) -def user_absent(name, delete_keys=True, delete_mfa_devices=True, delete_profile=True, region=None, key=None, keyid=None, profile=None): - ''' +def user_absent( + name, + delete_keys=True, + delete_mfa_devices=True, + delete_profile=True, + region=None, + key=None, + keyid=None, + profile=None, +): + """ .. versionadded:: 2015.8.0 @@ -224,95 +248,185 @@ def user_absent(name, delete_keys=True, delete_mfa_devices=True, delete_profile= profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - if not __salt__['boto_iam.get_user'](name, region, key, keyid, profile): - ret['result'] = True - ret['comment'] = 'IAM User {0} does not exist.'.format(name) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + if not __salt__["boto_iam.get_user"](name, region, key, keyid, profile): + ret["result"] = True + ret["comment"] = "IAM User {0} does not exist.".format(name) return ret # delete the user's access keys if delete_keys: - keys = __salt__['boto_iam.get_all_access_keys'](user_name=name, region=region, key=key, - keyid=keyid, profile=profile) - log.debug('Keys for user %s are %s.', name, keys) + keys = __salt__["boto_iam.get_all_access_keys"]( + user_name=name, region=region, key=key, keyid=keyid, profile=profile + ) + log.debug("Keys for user %s are %s.", name, keys) if isinstance(keys, dict): - keys = keys['list_access_keys_response']['list_access_keys_result']['access_key_metadata'] + keys = keys["list_access_keys_response"]["list_access_keys_result"][ + "access_key_metadata" + ] for k in keys: - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'Key {0} is set to be deleted.'.format(k['access_key_id'])]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "Key {0} is set to be deleted.".format(k["access_key_id"]), + ] + ) + ret["result"] = None else: - if _delete_key(ret, k['access_key_id'], name, region, key, keyid, profile): - ret['comment'] = ' '.join([ret['comment'], 'Key {0} has been deleted.'.format(k['access_key_id'])]) - ret['changes'][k['access_key_id']] = 'deleted' + if _delete_key( + ret, k["access_key_id"], name, region, key, keyid, profile + ): + ret["comment"] = " ".join( + [ + ret["comment"], + "Key {0} has been deleted.".format(k["access_key_id"]), + ] + ) + ret["changes"][k["access_key_id"]] = "deleted" # delete the user's MFA tokens if delete_mfa_devices: - devices = __salt__['boto_iam.get_all_mfa_devices'](user_name=name, region=region, key=key, keyid=keyid, profile=profile) + devices = __salt__["boto_iam.get_all_mfa_devices"]( + user_name=name, region=region, key=key, keyid=keyid, profile=profile + ) if devices: for d in devices: - serial = d['serial_number'] - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} MFA device {1} is set to be deactivated.'.format(name, serial)]) - ret['result'] = None + serial = d["serial_number"] + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "IAM user {0} MFA device {1} is set to be deactivated.".format( + name, serial + ), + ] + ) + ret["result"] = None else: - mfa_deactivated = __salt__['boto_iam.deactivate_mfa_device'](user_name=name, serial=serial, region=region, key=key, keyid=keyid, profile=profile) + mfa_deactivated = __salt__["boto_iam.deactivate_mfa_device"]( + user_name=name, + serial=serial, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if mfa_deactivated: - ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} MFA device {1} is deactivated.'.format(name, serial)]) - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'Virtual MFA device {0} is set to be deleted.'.format(serial)]) - ret['result'] = None + ret["comment"] = " ".join( + [ + ret["comment"], + "IAM user {0} MFA device {1} is deactivated.".format( + name, serial + ), + ] + ) + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "Virtual MFA device {0} is set to be deleted.".format( + serial + ), + ] + ) + ret["result"] = None else: - mfa_deleted = __salt__['boto_iam.delete_virtual_mfa_device'](serial=serial, region=region, key=key, keyid=keyid, profile=profile) + mfa_deleted = __salt__["boto_iam.delete_virtual_mfa_device"]( + serial=serial, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if mfa_deleted: - ret['comment'] = ' '.join([ret['comment'], 'Virtual MFA device {0} is deleted.'.format(serial)]) + ret["comment"] = " ".join( + [ + ret["comment"], + "Virtual MFA device {0} is deleted.".format(serial), + ] + ) # delete the user's login profile if delete_profile: - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} login profile is set to be deleted.'.format(name)]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "IAM user {0} login profile is set to be deleted.".format(name), + ] + ) + ret["result"] = None else: - profile_deleted = __salt__['boto_iam.delete_login_profile'](name, region, key, keyid, profile) + profile_deleted = __salt__["boto_iam.delete_login_profile"]( + name, region, key, keyid, profile + ) if profile_deleted: - ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} login profile is deleted.'.format(name)]) - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} managed policies are set to be detached.'.format(name)]) - ret['result'] = None + ret["comment"] = " ".join( + [ + ret["comment"], + "IAM user {0} login profile is deleted.".format(name), + ] + ) + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "IAM user {0} managed policies are set to be detached.".format(name), + ] + ) + ret["result"] = None else: _ret = _user_policies_detached(name, region, key, keyid, profile) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} inline policies are set to be deleted.'.format(name)]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "IAM user {0} inline policies are set to be deleted.".format(name), + ] + ) + ret["result"] = None else: _ret = _user_policies_deleted(name, region, key, keyid, profile) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret # finally, actually delete the user - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} is set to be deleted.'.format(name)]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = " ".join( + [ret["comment"], "IAM user {0} is set to be deleted.".format(name)] + ) + ret["result"] = None return ret - deleted = __salt__['boto_iam.delete_user'](name, region, key, keyid, profile) + deleted = __salt__["boto_iam.delete_user"](name, region, key, keyid, profile) if deleted is True: - ret['comment'] = ' '.join([ret['comment'], 'IAM user {0} is deleted.'.format(name)]) - ret['result'] = True - ret['changes']['deleted'] = name + ret["comment"] = " ".join( + [ret["comment"], "IAM user {0} is deleted.".format(name)] + ) + ret["result"] = True + ret["changes"]["deleted"] = name return ret - ret['comment'] = 'IAM user {0} could not be deleted.\n {1}'.format(name, deleted) - ret['result'] = False + ret["comment"] = "IAM user {0} could not be deleted.\n {1}".format(name, deleted) + ret["result"] = False return ret -def keys_present(name, number, save_dir, region=None, key=None, keyid=None, profile=None, - save_format="{2}\n{0}\n{3}\n{1}\n"): - ''' +def keys_present( + name, + number, + save_dir, + region=None, + key=None, + keyid=None, + profile=None, + save_format="{2}\n{0}\n{3}\n{1}\n", +): + """ .. versionadded:: 2015.8.0 Ensure the IAM access keys are present. @@ -345,77 +459,92 @@ def keys_present(name, number, save_dir, region=None, key=None, keyid=None, prof "{2}\\n{0}\\n{3}\\n{1}\\n", where {0} and {1} are placeholders for new key_id and key respectively, whereas {2} and {3} are "key_id-{number}" and 'key-{number}' strings kept for compatibility. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - if not __salt__['boto_iam.get_user'](name, region, key, keyid, profile): - ret['result'] = False - ret['comment'] = 'IAM User {0} does not exist.'.format(name) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + if not __salt__["boto_iam.get_user"](name, region, key, keyid, profile): + ret["result"] = False + ret["comment"] = "IAM User {0} does not exist.".format(name) return ret if not isinstance(number, int): - ret['comment'] = 'The number of keys must be an integer.' - ret['result'] = False + ret["comment"] = "The number of keys must be an integer." + ret["result"] = False return ret if not os.path.isdir(save_dir): - ret['comment'] = 'The directory {0} does not exist.'.format(save_dir) - ret['result'] = False + ret["comment"] = "The directory {0} does not exist.".format(save_dir) + ret["result"] = False return ret - keys = __salt__['boto_iam.get_all_access_keys'](user_name=name, region=region, key=key, - keyid=keyid, profile=profile) + keys = __salt__["boto_iam.get_all_access_keys"]( + user_name=name, region=region, key=key, keyid=keyid, profile=profile + ) if isinstance(keys, six.string_types): - log.debug('keys are : false %s', keys) + log.debug("keys are : false %s", keys) error, message = _get_error(keys) - ret['comment'] = 'Could not get keys.\n{0}\n{1}'.format(error, message) - ret['result'] = False + ret["comment"] = "Could not get keys.\n{0}\n{1}".format(error, message) + ret["result"] = False return ret - keys = keys['list_access_keys_response']['list_access_keys_result']['access_key_metadata'] - log.debug('Keys are : %s.', keys) + keys = keys["list_access_keys_response"]["list_access_keys_result"][ + "access_key_metadata" + ] + log.debug("Keys are : %s.", keys) if len(keys) >= number: - ret['comment'] = 'The number of keys exist for user {0}'.format(name) - ret['result'] = True + ret["comment"] = "The number of keys exist for user {0}".format(name) + ret["result"] = True return ret - if __opts__['test']: - ret['comment'] = 'Access key is set to be created for {0}.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Access key is set to be created for {0}.".format(name) + ret["result"] = None return ret new_keys = {} - for i in range(number-len(keys)): - created = __salt__['boto_iam.create_access_key'](name, region, key, keyid, profile) + for i in range(number - len(keys)): + created = __salt__["boto_iam.create_access_key"]( + name, region, key, keyid, profile + ) if isinstance(created, six.string_types): error, message = _get_error(created) - ret['comment'] = 'Could not create keys.\n{0}\n{1}'.format(error, message) - ret['result'] = False + ret["comment"] = "Could not create keys.\n{0}\n{1}".format(error, message) + ret["result"] = False return ret - log.debug('Created is : %s', created) - response = 'create_access_key_response' - result = 'create_access_key_result' + log.debug("Created is : %s", created) + response = "create_access_key_response" + result = "create_access_key_result" new_keys[six.text_type(i)] = {} - new_keys[six.text_type(i)]['key_id'] = created[response][result]['access_key']['access_key_id'] - new_keys[six.text_type(i)]['secret_key'] = created[response][result]['access_key']['secret_access_key'] + new_keys[six.text_type(i)]["key_id"] = created[response][result]["access_key"][ + "access_key_id" + ] + new_keys[six.text_type(i)]["secret_key"] = created[response][result][ + "access_key" + ]["secret_access_key"] try: - with salt.utils.files.fopen('{0}/{1}'.format(save_dir, name), 'a') as _wrf: + with salt.utils.files.fopen("{0}/{1}".format(save_dir, name), "a") as _wrf: for key_num, key in new_keys.items(): - key_id = key['key_id'] - secret_key = key['secret_key'] - _wrf.write(salt.utils.stringutils.to_str( - save_format.format( - key_id, - secret_key, - 'key_id-{0}'.format(key_num), - 'key-{0}'.format(key_num) + key_id = key["key_id"] + secret_key = key["secret_key"] + _wrf.write( + salt.utils.stringutils.to_str( + save_format.format( + key_id, + secret_key, + "key_id-{0}".format(key_num), + "key-{0}".format(key_num), + ) ) - )) - ret['comment'] = 'Keys have been written to file {0}/{1}.'.format(save_dir, name) - ret['result'] = True - ret['changes'] = new_keys + ) + ret["comment"] = "Keys have been written to file {0}/{1}.".format( + save_dir, name + ) + ret["result"] = True + ret["changes"] = new_keys return ret except IOError: - ret['comment'] = 'Could not write to file {0}/{1}.'.format(save_dir, name) - ret['result'] = False + ret["comment"] = "Could not write to file {0}/{1}.".format(save_dir, name) + ret["result"] = False return ret -def keys_absent(access_keys, user_name, region=None, key=None, keyid=None, profile=None): - ''' +def keys_absent( + access_keys, user_name, region=None, key=None, keyid=None, profile=None +): + """ .. versionadded:: 2015.8.0 @@ -439,48 +568,74 @@ def keys_absent(access_keys, user_name, region=None, key=None, keyid=None, profi profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': access_keys, 'result': True, 'comment': '', 'changes': {}} - if not __salt__['boto_iam.get_user'](user_name, region, key, keyid, profile): - ret['result'] = False - ret['comment'] = 'IAM User {0} does not exist.'.format(user_name) + """ + ret = {"name": access_keys, "result": True, "comment": "", "changes": {}} + if not __salt__["boto_iam.get_user"](user_name, region, key, keyid, profile): + ret["result"] = False + ret["comment"] = "IAM User {0} does not exist.".format(user_name) return ret for k in access_keys: ret = _delete_key(ret, k, user_name, region, key, keyid, profile) return ret -def _delete_key(ret, access_key_id, user_name, region=None, key=None, keyid=None, profile=None): - keys = __salt__['boto_iam.get_all_access_keys'](user_name=user_name, region=region, key=key, - keyid=keyid, profile=profile) - log.debug('Keys for user %s are : %s.', keys, user_name) +def _delete_key( + ret, access_key_id, user_name, region=None, key=None, keyid=None, profile=None +): + keys = __salt__["boto_iam.get_all_access_keys"]( + user_name=user_name, region=region, key=key, keyid=keyid, profile=profile + ) + log.debug("Keys for user %s are : %s.", keys, user_name) if isinstance(keys, six.string_types): - log.debug('Keys %s are a string. Something went wrong.', keys) - ret['comment'] = ' '.join([ret['comment'], 'Key {0} could not be deleted.'.format(access_key_id)]) + log.debug("Keys %s are a string. Something went wrong.", keys) + ret["comment"] = " ".join( + [ret["comment"], "Key {0} could not be deleted.".format(access_key_id)] + ) return ret - keys = keys['list_access_keys_response']['list_access_keys_result']['access_key_metadata'] + keys = keys["list_access_keys_response"]["list_access_keys_result"][ + "access_key_metadata" + ] for k in keys: - log.debug('Key is: %s and is compared with: %s', k['access_key_id'], access_key_id) - if six.text_type(k['access_key_id']) == six.text_type(access_key_id): - if __opts__['test']: - ret['comment'] = 'Access key {0} is set to be deleted.'.format(access_key_id) - ret['result'] = None + log.debug( + "Key is: %s and is compared with: %s", k["access_key_id"], access_key_id + ) + if six.text_type(k["access_key_id"]) == six.text_type(access_key_id): + if __opts__["test"]: + ret["comment"] = "Access key {0} is set to be deleted.".format( + access_key_id + ) + ret["result"] = None return ret - deleted = __salt__['boto_iam.delete_access_key'](access_key_id, user_name, region, key, - keyid, profile) + deleted = __salt__["boto_iam.delete_access_key"]( + access_key_id, user_name, region, key, keyid, profile + ) if deleted: - ret['comment'] = ' '.join([ret['comment'], 'Key {0} has been deleted.'.format(access_key_id)]) - ret['changes'][access_key_id] = 'deleted' + ret["comment"] = " ".join( + [ret["comment"], "Key {0} has been deleted.".format(access_key_id)] + ) + ret["changes"][access_key_id] = "deleted" return ret - ret['comment'] = ' '.join([ret['comment'], 'Key {0} could not be deleted.'.format(access_key_id)]) + ret["comment"] = " ".join( + [ret["comment"], "Key {0} could not be deleted.".format(access_key_id)] + ) return ret - ret['comment'] = ' '.join([ret['comment'], 'Key {0} does not exist.'.format(k)]) + ret["comment"] = " ".join([ret["comment"], "Key {0} does not exist.".format(k)]) return ret -def user_present(name, policies=None, policies_from_pillars=None, managed_policies=None, password=None, path=None, - region=None, key=None, keyid=None, profile=None): - ''' +def user_present( + name, + policies=None, + policies_from_pillars=None, + managed_policies=None, + password=None, + path=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ .. versionadded:: 2015.8.0 @@ -525,8 +680,8 @@ def user_present(name, policies=None, policies_from_pillars=None, managed_polici profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not policies: policies = {} if not policies_from_pillars: @@ -535,54 +690,66 @@ def user_present(name, policies=None, policies_from_pillars=None, managed_polici managed_policies = [] _policies = {} for policy in policies_from_pillars: - _policy = __salt__['pillar.get'](policy) + _policy = __salt__["pillar.get"](policy) _policies.update(_policy) _policies.update(policies) - exists = __salt__['boto_iam.get_user'](name, region, key, keyid, profile) + exists = __salt__["boto_iam.get_user"](name, region, key, keyid, profile) if not exists: - if __opts__['test']: - ret['comment'] = 'IAM user {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "IAM user {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_iam.create_user'](name, path, region, key, keyid, profile) + created = __salt__["boto_iam.create_user"]( + name, path, region, key, keyid, profile + ) if created: - ret['changes']['user'] = created - ret['comment'] = ' '.join([ret['comment'], 'User {0} has been created.'.format(name)]) + ret["changes"]["user"] = created + ret["comment"] = " ".join( + [ret["comment"], "User {0} has been created.".format(name)] + ) if password: ret = _case_password(ret, name, password, region, key, keyid, profile) _ret = _user_policies_present(name, _policies, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) else: - ret['comment'] = ' '.join([ret['comment'], 'User {0} is present.'.format(name)]) + ret["comment"] = " ".join([ret["comment"], "User {0} is present.".format(name)]) if password: ret = _case_password(ret, name, password, region, key, keyid, profile) _ret = _user_policies_present(name, _policies, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) _ret = _user_policies_attached(name, managed_policies, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret return ret -def _user_policies_present(name, policies=None, region=None, key=None, keyid=None, profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} +def _user_policies_present( + name, policies=None, region=None, key=None, keyid=None, profile=None +): + ret = {"result": True, "comment": "", "changes": {}} policies_to_create = {} policies_to_delete = [] for policy_name, policy in six.iteritems(policies): if isinstance(policy, six.string_types): - dict_policy = _byteify(salt.utils.json.loads(policy, object_pairs_hook=odict.OrderedDict)) + dict_policy = _byteify( + salt.utils.json.loads(policy, object_pairs_hook=odict.OrderedDict) + ) else: dict_policy = _byteify(policy) - _policy = _byteify(__salt__['boto_iam.get_user_policy'](name, policy_name, region, key, keyid, profile)) + _policy = _byteify( + __salt__["boto_iam.get_user_policy"]( + name, policy_name, region, key, keyid, profile + ) + ) if _policy != dict_policy: log.debug("Policy mismatch:\n%s\n%s", _policy, dict_policy) policies_to_create[policy_name] = policy - _list = __salt__['boto_iam.get_all_user_policies']( + _list = __salt__["boto_iam.get_all_user_policies"]( user_name=name, region=region, key=key, keyid=keyid, profile=profile ) for policy_name in _list: @@ -591,222 +758,252 @@ def _user_policies_present(name, policies=None, region=None, key=None, keyid=Non if policies_to_create or policies_to_delete: _to_modify = list(policies_to_delete) _to_modify.extend(policies_to_create) - if __opts__['test']: - ret['comment'] = '{0} policies to be modified on user {1}.'.format(', '.join(_to_modify), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be modified on user {1}.".format( + ", ".join(_to_modify), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'policies': _list} + ret["changes"]["old"] = {"policies": _list} for policy_name, policy in six.iteritems(policies_to_create): - policy_set = __salt__['boto_iam.put_user_policy']( + policy_set = __salt__["boto_iam.put_user_policy"]( name, policy_name, policy, region, key, keyid, profile ) if not policy_set: - _list = __salt__['boto_iam.get_all_user_policies']( + _list = __salt__["boto_iam.get_all_user_policies"]( user_name=name, region=region, key=key, keyid=keyid, profile=profile ) - ret['changes']['new'] = {'policies': _list} - ret['result'] = False - ret['comment'] = 'Failed to add policy {0} for user {1}'.format(policy_name, name) + ret["changes"]["new"] = {"policies": _list} + ret["result"] = False + ret["comment"] = "Failed to add policy {0} for user {1}".format( + policy_name, name + ) return ret for policy_name in policies_to_delete: - policy_unset = __salt__['boto_iam.delete_user_policy']( + policy_unset = __salt__["boto_iam.delete_user_policy"]( name, policy_name, region, key, keyid, profile ) if not policy_unset: - _list = __salt__['boto_iam.get_all_user_policies']( + _list = __salt__["boto_iam.get_all_user_policies"]( user_name=name, region=region, key=key, keyid=keyid, profile=profile ) - ret['changes']['new'] = {'policies': _list} - ret['result'] = False - ret['comment'] = 'Failed to add policy {0} to user {1}'.format(policy_name, name) + ret["changes"]["new"] = {"policies": _list} + ret["result"] = False + ret["comment"] = "Failed to add policy {0} to user {1}".format( + policy_name, name + ) return ret - _list = __salt__['boto_iam.get_all_user_policies']( + _list = __salt__["boto_iam.get_all_user_policies"]( user_name=name, region=region, key=key, keyid=keyid, profile=profile ) - ret['changes']['new'] = {'policies': _list} - ret['comment'] = '{0} policies modified on user {1}.'.format(', '.join(_list), name) + ret["changes"]["new"] = {"policies": _list} + ret["comment"] = "{0} policies modified on user {1}.".format( + ", ".join(_list), name + ) return ret def _user_policies_attached( - name, - managed_policies=None, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} + name, managed_policies=None, region=None, key=None, keyid=None, profile=None +): + ret = {"result": True, "comment": "", "changes": {}} policies_to_attach = [] policies_to_detach = [] for policy in managed_policies or []: - entities = __salt__['boto_iam.list_entities_for_policy'](policy, - entity_filter='User', - region=region, key=key, keyid=keyid, - profile=profile) + entities = __salt__["boto_iam.list_entities_for_policy"]( + policy, + entity_filter="User", + region=region, + key=key, + keyid=keyid, + profile=profile, + ) found = False - for userdict in entities.get('policy_users', []): - if name == userdict.get('user_name'): + for userdict in entities.get("policy_users", []): + if name == userdict.get("user_name"): found = True break if not found: policies_to_attach.append(policy) - _list = __salt__['boto_iam.list_attached_user_policies'](name, region=region, key=key, keyid=keyid, - profile=profile) - oldpolicies = [x.get('policy_arn') for x in _list] + _list = __salt__["boto_iam.list_attached_user_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + oldpolicies = [x.get("policy_arn") for x in _list] for policy_data in _list: - if policy_data.get('policy_name') not in managed_policies \ - and policy_data.get('policy_arn') not in managed_policies: - policies_to_detach.append(policy_data.get('policy_arn')) + if ( + policy_data.get("policy_name") not in managed_policies + and policy_data.get("policy_arn") not in managed_policies + ): + policies_to_detach.append(policy_data.get("policy_arn")) if policies_to_attach or policies_to_detach: _to_modify = list(policies_to_detach) _to_modify.extend(policies_to_attach) - if __opts__['test']: - ret['comment'] = '{0} policies to be modified on user {1}.'.format(', '.join(_to_modify), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be modified on user {1}.".format( + ", ".join(_to_modify), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'managed_policies': oldpolicies} + ret["changes"]["old"] = {"managed_policies": oldpolicies} for policy_name in policies_to_attach: - policy_set = __salt__['boto_iam.attach_user_policy'](policy_name, - name, - region=region, key=key, - keyid=keyid, - profile=profile) + policy_set = __salt__["boto_iam.attach_user_policy"]( + policy_name, name, region=region, key=key, keyid=keyid, profile=profile + ) if not policy_set: - _list = __salt__['boto_iam.list_attached_user_policies'](name, region=region, - key=key, - keyid=keyid, - profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to add policy {0} to user {1}'.format(policy_name, name) + _list = __salt__["boto_iam.list_attached_user_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to add policy {0} to user {1}".format( + policy_name, name + ) return ret for policy_name in policies_to_detach: - policy_unset = __salt__['boto_iam.detach_user_policy'](policy_name, - name, - region=region, key=key, - keyid=keyid, - profile=profile) + policy_unset = __salt__["boto_iam.detach_user_policy"]( + policy_name, name, region=region, key=key, keyid=keyid, profile=profile + ) if not policy_unset: - _list = __salt__['boto_iam.list_attached_user_policies'](name, region=region, - key=key, - keyid=keyid, - profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to remove policy {0} from user {1}'.format(policy_name, name) + _list = __salt__["boto_iam.list_attached_user_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to remove policy {0} from user {1}".format( + policy_name, name + ) return ret - _list = __salt__['boto_iam.list_attached_user_policies'](name, region=region, key=key, - keyid=keyid, - profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] + _list = __salt__["boto_iam.list_attached_user_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] log.debug(newpolicies) - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['comment'] = '{0} policies modified on user {1}.'.format(', '.join(newpolicies), name) + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["comment"] = "{0} policies modified on user {1}.".format( + ", ".join(newpolicies), name + ) return ret -def _user_policies_detached( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - _list = __salt__['boto_iam.list_attached_user_policies'](user_name=name, - region=region, key=key, keyid=keyid, profile=profile) - oldpolicies = [x.get('policy_arn') for x in _list] +def _user_policies_detached(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} + _list = __salt__["boto_iam.list_attached_user_policies"]( + user_name=name, region=region, key=key, keyid=keyid, profile=profile + ) + oldpolicies = [x.get("policy_arn") for x in _list] if not _list: - ret['comment'] = 'No attached policies in user {0}.'.format(name) + ret["comment"] = "No attached policies in user {0}.".format(name) return ret - if __opts__['test']: - ret['comment'] = '{0} policies to be detached from user {1}.'.format(', '.join(oldpolicies), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be detached from user {1}.".format( + ", ".join(oldpolicies), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'managed_policies': oldpolicies} + ret["changes"]["old"] = {"managed_policies": oldpolicies} for policy_arn in oldpolicies: - policy_unset = __salt__['boto_iam.detach_user_policy'](policy_arn, - name, - region=region, key=key, - keyid=keyid, - profile=profile) + policy_unset = __salt__["boto_iam.detach_user_policy"]( + policy_arn, name, region=region, key=key, keyid=keyid, profile=profile + ) if not policy_unset: - _list = __salt__['boto_iam.list_attached_user_policies'](name, region=region, - key=key, keyid=keyid, - profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to detach {0} from user {1}'.format(policy_arn, name) + _list = __salt__["boto_iam.list_attached_user_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to detach {0} from user {1}".format( + policy_arn, name + ) return ret - _list = __salt__['boto_iam.list_attached_user_policies'](name, region=region, key=key, - keyid=keyid, profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['comment'] = '{0} policies detached from user {1}.'.format(', '.join(oldpolicies), name) + _list = __salt__["boto_iam.list_attached_user_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["comment"] = "{0} policies detached from user {1}.".format( + ", ".join(oldpolicies), name + ) return ret -def _user_policies_deleted( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - oldpolicies = __salt__['boto_iam.get_all_user_policies'](user_name=name, - region=region, key=key, keyid=keyid, profile=profile) +def _user_policies_deleted(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} + oldpolicies = __salt__["boto_iam.get_all_user_policies"]( + user_name=name, region=region, key=key, keyid=keyid, profile=profile + ) if not oldpolicies: - ret['comment'] = 'No inline policies in user {0}.'.format(name) + ret["comment"] = "No inline policies in user {0}.".format(name) return ret - if __opts__['test']: - ret['comment'] = '{0} policies to be deleted from user {1}.'.format(', '.join(oldpolicies), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be deleted from user {1}.".format( + ", ".join(oldpolicies), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'inline_policies': oldpolicies} + ret["changes"]["old"] = {"inline_policies": oldpolicies} for policy_name in oldpolicies: - policy_deleted = __salt__['boto_iam.delete_user_policy'](name, - policy_name, - region=region, key=key, - keyid=keyid, - profile=profile) + policy_deleted = __salt__["boto_iam.delete_user_policy"]( + name, policy_name, region=region, key=key, keyid=keyid, profile=profile + ) if not policy_deleted: - newpolicies = __salt__['boto_iam.get_all_user_policies'](name, region=region, - key=key, keyid=keyid, - profile=profile) - ret['changes']['new'] = {'inline_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to detach {0} from user {1}'.format(policy_name, name) + newpolicies = __salt__["boto_iam.get_all_user_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["new"] = {"inline_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to detach {0} from user {1}".format( + policy_name, name + ) return ret - newpolicies = __salt__['boto_iam.get_all_user_policies'](name, region=region, key=key, - keyid=keyid, profile=profile) - ret['changes']['new'] = {'inline_policies': newpolicies} - ret['comment'] = '{0} policies deleted from user {1}.'.format(', '.join(oldpolicies), name) + newpolicies = __salt__["boto_iam.get_all_user_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["new"] = {"inline_policies": newpolicies} + ret["comment"] = "{0} policies deleted from user {1}.".format( + ", ".join(oldpolicies), name + ) return ret -def _case_password(ret, name, password, region=None, key=None, keyid=None, profile=None): - if __opts__['test']: - ret['comment'] = 'Login policy for {0} is set to be changed.'.format(name) - ret['result'] = None +def _case_password( + ret, name, password, region=None, key=None, keyid=None, profile=None +): + if __opts__["test"]: + ret["comment"] = "Login policy for {0} is set to be changed.".format(name) + ret["result"] = None return ret - login = __salt__['boto_iam.create_login_profile'](name, password, region, key, keyid, profile) - log.debug('Login is : %s.', login) + login = __salt__["boto_iam.create_login_profile"]( + name, password, region, key, keyid, profile + ) + log.debug("Login is : %s.", login) if login: - if 'Conflict' in login: - ret['comment'] = ' '.join([ret['comment'], 'Login profile for user {0} exists.'.format(name)]) + if "Conflict" in login: + ret["comment"] = " ".join( + [ret["comment"], "Login profile for user {0} exists.".format(name)] + ) else: - ret['comment'] = ' '.join([ret['comment'], 'Password has been added to User {0}.'.format(name)]) - ret['changes']['password'] = 'REDACTED' + ret["comment"] = " ".join( + [ret["comment"], "Password has been added to User {0}.".format(name)] + ) + ret["changes"]["password"] = "REDACTED" else: - ret['result'] = False - ret['comment'] = ' '.join([ret['comment'], 'Password for user {0} could not be set.\nPlease check your password policy.'.format(name)]) + ret["result"] = False + ret["comment"] = " ".join( + [ + ret["comment"], + "Password for user {0} could not be set.\nPlease check your password policy.".format( + name + ), + ] + ) return ret def group_absent(name, region=None, key=None, keyid=None, profile=None): - ''' + """ .. versionadded:: 2015.8.0 @@ -827,58 +1024,88 @@ def group_absent(name, region=None, key=None, keyid=None, profile=None): profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - if not __salt__['boto_iam.get_group'](name, region, key, keyid, profile): - ret['result'] = True - ret['comment'] = 'IAM Group {0} does not exist.'.format(name) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + if not __salt__["boto_iam.get_group"](name, region, key, keyid, profile): + ret["result"] = True + ret["comment"] = "IAM Group {0} does not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'IAM group {0} managed policies are set to be detached.'.format(name)]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "IAM group {0} managed policies are set to be detached.".format(name), + ] + ) + ret["result"] = None else: _ret = _group_policies_detached(name, region, key, keyid, profile) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'IAM group {0} inline policies are set to be deleted.'.format(name)]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "IAM group {0} inline policies are set to be deleted.".format(name), + ] + ) + ret["result"] = None else: _ret = _group_policies_deleted(name, region, key, keyid, profile) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - ret['comment'] = ' '.join([ret['comment'], 'IAM group {0} users are set to be removed.'.format(name)]) - existing_users = __salt__['boto_iam.get_group_members'](group_name=name, region=region, key=key, keyid=keyid, profile=profile) + ret["comment"] = " ".join( + [ret["comment"], "IAM group {0} users are set to be removed.".format(name)] + ) + existing_users = __salt__["boto_iam.get_group_members"]( + group_name=name, region=region, key=key, keyid=keyid, profile=profile + ) _ret = _case_group(ret, [], name, existing_users, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret # finally, actually delete the group - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'IAM group {0} is set to be deleted.'.format(name)]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = " ".join( + [ret["comment"], "IAM group {0} is set to be deleted.".format(name)] + ) + ret["result"] = None return ret - deleted = __salt__['boto_iam.delete_group'](name, region, key, keyid, profile) + deleted = __salt__["boto_iam.delete_group"](name, region, key, keyid, profile) if deleted is True: - ret['comment'] = ' '.join([ret['comment'], 'IAM group {0} is deleted.'.format(name)]) - ret['result'] = True - ret['changes']['deleted'] = name + ret["comment"] = " ".join( + [ret["comment"], "IAM group {0} is deleted.".format(name)] + ) + ret["result"] = True + ret["changes"]["deleted"] = name return ret - ret['comment'] = 'IAM group {0} could not be deleted.\n {1}'.format(name, deleted) - ret['result'] = False + ret["comment"] = "IAM group {0} could not be deleted.\n {1}".format(name, deleted) + ret["result"] = False return ret -def group_present(name, policies=None, policies_from_pillars=None, managed_policies=None, users=None, path='/', region=None, key=None, keyid=None, profile=None, delete_policies=True): - ''' +def group_present( + name, + policies=None, + policies_from_pillars=None, + managed_policies=None, + users=None, + path="/", + region=None, + key=None, + keyid=None, + profile=None, + delete_policies=True, +): + """ .. versionadded:: 2015.8.0 @@ -926,8 +1153,8 @@ def group_present(name, policies=None, policies_from_pillars=None, managed_polic Default value is ``True``. If ``False`` is specified, existing policies will not be deleted or detached allowing manual modifications on the IAM group to be persistent. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not policies: policies = {} if not policies_from_pillars: @@ -936,42 +1163,59 @@ def group_present(name, policies=None, policies_from_pillars=None, managed_polic managed_policies = [] _policies = {} for policy in policies_from_pillars: - _policy = __salt__['pillar.get'](policy) + _policy = __salt__["pillar.get"](policy) _policies.update(_policy) _policies.update(policies) - exists = __salt__['boto_iam.get_group'](group_name=name, region=region, key=key, keyid=keyid, profile=profile) + exists = __salt__["boto_iam.get_group"]( + group_name=name, region=region, key=key, keyid=keyid, profile=profile + ) if not exists: - if __opts__['test']: - ret['comment'] = 'IAM group {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "IAM group {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_iam.create_group'](group_name=name, path=path, region=region, key=key, keyid=keyid, profile=profile) + created = __salt__["boto_iam.create_group"]( + group_name=name, + path=path, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not created: - ret['comment'] = 'Failed to create IAM group {0}.'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create IAM group {0}.".format(name) + ret["result"] = False return ret - ret['changes']['group'] = created - ret['comment'] = ' '.join([ret['comment'], 'Group {0} has been created.'.format(name)]) + ret["changes"]["group"] = created + ret["comment"] = " ".join( + [ret["comment"], "Group {0} has been created.".format(name)] + ) else: - ret['comment'] = ' '.join([ret['comment'], 'Group {0} is present.'.format(name)]) + ret["comment"] = " ".join( + [ret["comment"], "Group {0} is present.".format(name)] + ) # Group exists, ensure group policies and users are set. _ret = _group_policies_present( name, _policies, region, key, keyid, profile, delete_policies ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret - _ret = _group_policies_attached(name, managed_policies, region, key, keyid, profile, delete_policies) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + _ret = _group_policies_attached( + name, managed_policies, region, key, keyid, profile, delete_policies + ) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret if users is not None: - log.debug('Users are : %s.', users) - existing_users = __salt__['boto_iam.get_group_members'](group_name=name, region=region, key=key, keyid=keyid, profile=profile) + log.debug("Users are : %s.", users) + existing_users = __salt__["boto_iam.get_group_members"]( + group_name=name, region=region, key=key, keyid=keyid, profile=profile + ) ret = _case_group(ret, users, name, existing_users, region, key, keyid, profile) return ret @@ -979,57 +1223,102 @@ def group_present(name, policies=None, policies_from_pillars=None, managed_polic def _case_group(ret, users, group_name, existing_users, region, key, keyid, profile): _users = [] for user in existing_users: - _users.append(user['user_name']) - log.debug('upstream users are %s', _users) + _users.append(user["user_name"]) + log.debug("upstream users are %s", _users) for user in users: - log.debug('users are %s', user) + log.debug("users are %s", user) if user in _users: - log.debug('user exists') - ret['comment'] = ' '.join([ret['comment'], 'User {0} is already a member of group {1}.'.format(user, group_name)]) + log.debug("user exists") + ret["comment"] = " ".join( + [ + ret["comment"], + "User {0} is already a member of group {1}.".format( + user, group_name + ), + ] + ) continue else: - log.debug('user is set to be added %s', user) - if __opts__['test']: - ret['comment'] = 'User {0} is set to be added to group {1}.'.format(user, group_name) - ret['result'] = None + log.debug("user is set to be added %s", user) + if __opts__["test"]: + ret["comment"] = "User {0} is set to be added to group {1}.".format( + user, group_name + ) + ret["result"] = None else: - __salt__['boto_iam.add_user_to_group'](user, group_name, region, key, keyid, profile) - ret['comment'] = ' '.join([ret['comment'], 'User {0} has been added to group {1}.'.format(user, group_name)]) - ret['changes'][user] = group_name + __salt__["boto_iam.add_user_to_group"]( + user, group_name, region, key, keyid, profile + ) + ret["comment"] = " ".join( + [ + ret["comment"], + "User {0} has been added to group {1}.".format( + user, group_name + ), + ] + ) + ret["changes"][user] = group_name for user in _users: if user not in users: - if __opts__['test']: - ret['comment'] = ' '.join([ret['comment'], 'User {0} is set to be removed from group {1}.'.format(user, group_name)]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = " ".join( + [ + ret["comment"], + "User {0} is set to be removed from group {1}.".format( + user, group_name + ), + ] + ) + ret["result"] = None else: - __salt__['boto_iam.remove_user_from_group'](group_name=group_name, user_name=user, region=region, - key=key, keyid=keyid, profile=profile) - ret['comment'] = ' '.join([ret['comment'], 'User {0} has been removed from group {1}.'.format(user, group_name)]) - ret['changes'][user] = 'Removed from group {0}.'.format(group_name) + __salt__["boto_iam.remove_user_from_group"]( + group_name=group_name, + user_name=user, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["comment"] = " ".join( + [ + ret["comment"], + "User {0} has been removed from group {1}.".format( + user, group_name + ), + ] + ) + ret["changes"][user] = "Removed from group {0}.".format(group_name) return ret def _group_policies_present( - name, - policies=None, - region=None, - key=None, - keyid=None, - profile=None, - delete_policies=True): - ret = {'result': True, 'comment': '', 'changes': {}} + name, + policies=None, + region=None, + key=None, + keyid=None, + profile=None, + delete_policies=True, +): + ret = {"result": True, "comment": "", "changes": {}} policies_to_create = {} policies_to_delete = [] for policy_name, policy in six.iteritems(policies): if isinstance(policy, six.string_types): - dict_policy = _byteify(salt.utils.json.loads(policy, object_pairs_hook=odict.OrderedDict)) + dict_policy = _byteify( + salt.utils.json.loads(policy, object_pairs_hook=odict.OrderedDict) + ) else: dict_policy = _byteify(policy) - _policy = _byteify(__salt__['boto_iam.get_group_policy'](name, policy_name, region, key, keyid, profile)) + _policy = _byteify( + __salt__["boto_iam.get_group_policy"]( + name, policy_name, region, key, keyid, profile + ) + ) if _policy != dict_policy: log.debug("Policy mismatch:\n%s\n%s", _policy, dict_policy) policies_to_create[policy_name] = policy - _list = __salt__['boto_iam.get_all_group_policies']( + _list = __salt__["boto_iam.get_all_group_policies"]( name, region, key, keyid, profile ) for policy_name in _list: @@ -1038,207 +1327,240 @@ def _group_policies_present( if policies_to_create or policies_to_delete: _to_modify = list(policies_to_delete) _to_modify.extend(policies_to_create) - if __opts__['test']: - ret['comment'] = '{0} policies to be modified on group {1}.'.format(', '.join(_to_modify), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be modified on group {1}.".format( + ", ".join(_to_modify), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'policies': _list} + ret["changes"]["old"] = {"policies": _list} for policy_name, policy in six.iteritems(policies_to_create): - policy_set = __salt__['boto_iam.put_group_policy']( + policy_set = __salt__["boto_iam.put_group_policy"]( name, policy_name, policy, region, key, keyid, profile ) if not policy_set: - _list = __salt__['boto_iam.get_all_group_policies']( + _list = __salt__["boto_iam.get_all_group_policies"]( name, region, key, keyid, profile ) - ret['changes']['new'] = {'policies': _list} - ret['result'] = False - ret['comment'] = 'Failed to add policy {0} to group {1}'.format(policy_name, name) + ret["changes"]["new"] = {"policies": _list} + ret["result"] = False + ret["comment"] = "Failed to add policy {0} to group {1}".format( + policy_name, name + ) return ret for policy_name in policies_to_delete: - policy_unset = __salt__['boto_iam.delete_group_policy']( + policy_unset = __salt__["boto_iam.delete_group_policy"]( name, policy_name, region, key, keyid, profile ) if not policy_unset: - _list = __salt__['boto_iam.get_all_group_policies']( + _list = __salt__["boto_iam.get_all_group_policies"]( name, region, key, keyid, profile ) - ret['changes']['new'] = {'policies': _list} - ret['result'] = False - ret['comment'] = 'Failed to add policy {0} to group {1}'.format(policy_name, name) + ret["changes"]["new"] = {"policies": _list} + ret["result"] = False + ret["comment"] = "Failed to add policy {0} to group {1}".format( + policy_name, name + ) return ret - _list = __salt__['boto_iam.get_all_group_policies']( + _list = __salt__["boto_iam.get_all_group_policies"]( name, region, key, keyid, profile ) - ret['changes']['new'] = {'policies': _list} - ret['comment'] = '{0} policies modified on group {1}.'.format(', '.join(_list), name) + ret["changes"]["new"] = {"policies": _list} + ret["comment"] = "{0} policies modified on group {1}.".format( + ", ".join(_list), name + ) return ret def _group_policies_attached( - name, - managed_policies=None, - region=None, - key=None, - keyid=None, - profile=None, - detach_policies=True): - ret = {'result': True, 'comment': '', 'changes': {}} + name, + managed_policies=None, + region=None, + key=None, + keyid=None, + profile=None, + detach_policies=True, +): + ret = {"result": True, "comment": "", "changes": {}} policies_to_attach = [] policies_to_detach = [] for policy in managed_policies or []: - entities = __salt__['boto_iam.list_entities_for_policy'](policy, - entity_filter='Group', - region=region, key=key, keyid=keyid, - profile=profile) + entities = __salt__["boto_iam.list_entities_for_policy"]( + policy, + entity_filter="Group", + region=region, + key=key, + keyid=keyid, + profile=profile, + ) found = False - for groupdict in entities.get('policy_groups', []): - if name == groupdict.get('group_name'): + for groupdict in entities.get("policy_groups", []): + if name == groupdict.get("group_name"): found = True break if not found: policies_to_attach.append(policy) - _list = __salt__['boto_iam.list_attached_group_policies'](name, region=region, key=key, keyid=keyid, - profile=profile) - oldpolicies = [x.get('policy_arn') for x in _list] + _list = __salt__["boto_iam.list_attached_group_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + oldpolicies = [x.get("policy_arn") for x in _list] for policy_data in _list: - if detach_policies \ - and policy_data.get('policy_name') not in managed_policies \ - and policy_data.get('policy_arn') not in managed_policies: - policies_to_detach.append(policy_data.get('policy_arn')) + if ( + detach_policies + and policy_data.get("policy_name") not in managed_policies + and policy_data.get("policy_arn") not in managed_policies + ): + policies_to_detach.append(policy_data.get("policy_arn")) if policies_to_attach or policies_to_detach: _to_modify = list(policies_to_detach) _to_modify.extend(policies_to_attach) - if __opts__['test']: - ret['comment'] = '{0} policies to be modified on group {1}.'.format(', '.join(_to_modify), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be modified on group {1}.".format( + ", ".join(_to_modify), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'managed_policies': oldpolicies} + ret["changes"]["old"] = {"managed_policies": oldpolicies} for policy_name in policies_to_attach: - policy_set = __salt__['boto_iam.attach_group_policy'](policy_name, - name, - region=region, key=key, - keyid=keyid, - profile=profile) + policy_set = __salt__["boto_iam.attach_group_policy"]( + policy_name, name, region=region, key=key, keyid=keyid, profile=profile + ) if not policy_set: - _list = __salt__['boto_iam.list_attached_group_policies'](name, region=region, - key=key, keyid=keyid, - profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to add policy {0} to group {1}'.format(policy_name, name) + _list = __salt__["boto_iam.list_attached_group_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to add policy {0} to group {1}".format( + policy_name, name + ) return ret for policy_name in policies_to_detach: - policy_unset = __salt__['boto_iam.detach_group_policy'](policy_name, - name, - region=region, key=key, - keyid=keyid, - profile=profile) + policy_unset = __salt__["boto_iam.detach_group_policy"]( + policy_name, name, region=region, key=key, keyid=keyid, profile=profile + ) if not policy_unset: - _list = __salt__['boto_iam.list_attached_group_policies'](name, region=region, - key=key, keyid=keyid, - profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to remove policy {0} from group {1}'.format(policy_name, name) + _list = __salt__["boto_iam.list_attached_group_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to remove policy {0} from group {1}".format( + policy_name, name + ) return ret - _list = __salt__['boto_iam.list_attached_group_policies'](name, region=region, key=key, - keyid=keyid, profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] + _list = __salt__["boto_iam.list_attached_group_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] log.debug(newpolicies) - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['comment'] = '{0} policies modified on group {1}.'.format(', '.join(newpolicies), name) + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["comment"] = "{0} policies modified on group {1}.".format( + ", ".join(newpolicies), name + ) return ret -def _group_policies_detached( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - _list = __salt__['boto_iam.list_attached_group_policies'](group_name=name, - region=region, key=key, keyid=keyid, profile=profile) - oldpolicies = [x.get('policy_arn') for x in _list] +def _group_policies_detached(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} + _list = __salt__["boto_iam.list_attached_group_policies"]( + group_name=name, region=region, key=key, keyid=keyid, profile=profile + ) + oldpolicies = [x.get("policy_arn") for x in _list] if not _list: - ret['comment'] = 'No attached policies in group {0}.'.format(name) + ret["comment"] = "No attached policies in group {0}.".format(name) return ret - if __opts__['test']: - ret['comment'] = '{0} policies to be detached from group {1}.'.format(', '.join(oldpolicies), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be detached from group {1}.".format( + ", ".join(oldpolicies), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'managed_policies': oldpolicies} + ret["changes"]["old"] = {"managed_policies": oldpolicies} for policy_arn in oldpolicies: - policy_unset = __salt__['boto_iam.detach_group_policy'](policy_arn, - name, - region=region, key=key, - keyid=keyid, - profile=profile) + policy_unset = __salt__["boto_iam.detach_group_policy"]( + policy_arn, name, region=region, key=key, keyid=keyid, profile=profile + ) if not policy_unset: - _list = __salt__['boto_iam.list_attached_group_policies'](name, region=region, - key=key, keyid=keyid, - profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to detach {0} from group {1}'.format(policy_arn, name) + _list = __salt__["boto_iam.list_attached_group_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to detach {0} from group {1}".format( + policy_arn, name + ) return ret - _list = __salt__['boto_iam.list_attached_group_policies'](name, region=region, key=key, - keyid=keyid, profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['comment'] = '{0} policies detached from group {1}.'.format(', '.join(newpolicies), name) + _list = __salt__["boto_iam.list_attached_group_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["comment"] = "{0} policies detached from group {1}.".format( + ", ".join(newpolicies), name + ) return ret -def _group_policies_deleted( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - oldpolicies = __salt__['boto_iam.get_all_group_policies'](group_name=name, - region=region, key=key, keyid=keyid, profile=profile) +def _group_policies_deleted(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} + oldpolicies = __salt__["boto_iam.get_all_group_policies"]( + group_name=name, region=region, key=key, keyid=keyid, profile=profile + ) if not oldpolicies: - ret['comment'] = 'No inline policies in group {0}.'.format(name) + ret["comment"] = "No inline policies in group {0}.".format(name) return ret - if __opts__['test']: - ret['comment'] = '{0} policies to be deleted from group {1}.'.format(', '.join(oldpolicies), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be deleted from group {1}.".format( + ", ".join(oldpolicies), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'inline_policies': oldpolicies} + ret["changes"]["old"] = {"inline_policies": oldpolicies} for policy_name in oldpolicies: - policy_deleted = __salt__['boto_iam.delete_group_policy'](name, - policy_name, - region=region, key=key, - keyid=keyid, - profile=profile) + policy_deleted = __salt__["boto_iam.delete_group_policy"]( + name, policy_name, region=region, key=key, keyid=keyid, profile=profile + ) if not policy_deleted: - newpolicies = __salt__['boto_iam.get_all_group_policies'](name, region=region, - key=key, keyid=keyid, - profile=profile) - ret['changes']['new'] = {'inline_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to detach {0} from group {1}'.format(policy_name, name) + newpolicies = __salt__["boto_iam.get_all_group_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["new"] = {"inline_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to detach {0} from group {1}".format( + policy_name, name + ) return ret - newpolicies = __salt__['boto_iam.get_all_group_policies'](name, region=region, key=key, - keyid=keyid, profile=profile) - ret['changes']['new'] = {'inline_policies': newpolicies} - ret['comment'] = '{0} policies deleted from group {1}.'.format(', '.join(oldpolicies), name) + newpolicies = __salt__["boto_iam.get_all_group_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["new"] = {"inline_policies": newpolicies} + ret["comment"] = "{0} policies deleted from group {1}.".format( + ", ".join(oldpolicies), name + ) return ret -def account_policy(name=None, allow_users_to_change_password=None, - hard_expiry=None, max_password_age=None, - minimum_password_length=None, password_reuse_prevention=None, - require_lowercase_characters=None, require_numbers=None, - require_symbols=None, require_uppercase_characters=None, - region=None, key=None, keyid=None, profile=None): - ''' +def account_policy( + name=None, + allow_users_to_change_password=None, + hard_expiry=None, + max_password_age=None, + minimum_password_length=None, + password_reuse_prevention=None, + require_lowercase_characters=None, + require_numbers=None, + require_symbols=None, + require_uppercase_characters=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Change account policy. .. versionadded:: 2015.8.0 @@ -1291,46 +1613,59 @@ def account_policy(name=None, allow_users_to_change_password=None, profile (dict) A dict with region, key and keyid, or a pillar key (string) - ''' + """ config = locals() - ret = {'name': 'Account Policy', 'result': True, 'comment': '', 'changes': {}} - info = __salt__['boto_iam.get_account_policy'](region, key, keyid, profile) + ret = {"name": "Account Policy", "result": True, "comment": "", "changes": {}} + info = __salt__["boto_iam.get_account_policy"](region, key, keyid, profile) if not info: - ret['comment'] = 'Account policy is not Enabled.' - ret['result'] = False + ret["comment"] = "Account policy is not Enabled." + ret["result"] = False return ret for key, value in config.items(): - if key in ('region', 'key', 'keyid', 'profile', 'name'): + if key in ("region", "key", "keyid", "profile", "name"): continue - if value is not None and six.text_type(info[key]) != six.text_type(value).lower(): - ret['comment'] = ' '.join([ret['comment'], 'Policy value {0} has been set to {1}.'.format(value, info[key])]) - ret['changes'][key] = six.text_type(value).lower() - if not ret['changes']: - ret['comment'] = 'Account policy is not changed.' + if ( + value is not None + and six.text_type(info[key]) != six.text_type(value).lower() + ): + ret["comment"] = " ".join( + [ + ret["comment"], + "Policy value {0} has been set to {1}.".format(value, info[key]), + ] + ) + ret["changes"][key] = six.text_type(value).lower() + if not ret["changes"]: + ret["comment"] = "Account policy is not changed." return ret - if __opts__['test']: - ret['comment'] = 'Account policy is set to be changed.' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Account policy is set to be changed." + ret["result"] = None return ret - if __salt__['boto_iam.update_account_password_policy'](allow_users_to_change_password, - hard_expiry, - max_password_age, - minimum_password_length, - password_reuse_prevention, - require_lowercase_characters, - require_numbers, - require_symbols, - require_uppercase_characters, - region, key, keyid, profile): + if __salt__["boto_iam.update_account_password_policy"]( + allow_users_to_change_password, + hard_expiry, + max_password_age, + minimum_password_length, + password_reuse_prevention, + require_lowercase_characters, + require_numbers, + require_symbols, + require_uppercase_characters, + region, + key, + keyid, + profile, + ): return ret - ret['comment'] = 'Account policy is not changed.' - ret['changes'] = {} - ret['result'] = False + ret["comment"] = "Account policy is not changed." + ret["changes"] = {} + ret["result"] = False return ret def server_cert_absent(name, region=None, key=None, keyid=None, profile=None): - ''' + """ Deletes a server certificate. .. versionadded:: 2015.8.0 @@ -1349,29 +1684,40 @@ def server_cert_absent(name, region=None, key=None, keyid=None, profile=None): profile (string) The profile that contains a dict of region, key, keyid - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - exists = __salt__['boto_iam.get_server_certificate'](name, region, key, keyid, profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + exists = __salt__["boto_iam.get_server_certificate"]( + name, region, key, keyid, profile + ) if not exists: - ret['comment'] = 'Certificate {0} does not exist.'.format(name) + ret["comment"] = "Certificate {0} does not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Server certificate {0} is set to be deleted.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Server certificate {0} is set to be deleted.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_iam.delete_server_cert'](name, region, key, keyid, profile) + deleted = __salt__["boto_iam.delete_server_cert"](name, region, key, keyid, profile) if not deleted: - ret['result'] = False - ret['comment'] = 'Certificate {0} failed to be deleted.'.format(name) + ret["result"] = False + ret["comment"] = "Certificate {0} failed to be deleted.".format(name) return ret - ret['comment'] = 'Certificate {0} was deleted.'.format(name) - ret['changes'] = deleted + ret["comment"] = "Certificate {0} was deleted.".format(name) + ret["changes"] = deleted return ret -def server_cert_present(name, public_key, private_key, cert_chain=None, path=None, - region=None, key=None, keyid=None, profile=None): - ''' +def server_cert_present( + name, + public_key, + private_key, + cert_chain=None, + path=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Crete server certificate. .. versionadded:: 2015.8.0 @@ -1403,55 +1749,66 @@ def server_cert_present(name, public_key, private_key, cert_chain=None, path=Non profile (string) The profile that contains a dict of region, key, keyid - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - exists = __salt__['boto_iam.get_server_certificate'](name, region, key, keyid, profile) - log.debug('Variables are : %s.', locals()) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + exists = __salt__["boto_iam.get_server_certificate"]( + name, region, key, keyid, profile + ) + log.debug("Variables are : %s.", locals()) if exists: - ret['comment'] = 'Certificate {0} exists.'.format(name) + ret["comment"] = "Certificate {0} exists.".format(name) return ret - if 'salt://' in public_key: + if "salt://" in public_key: try: - public_key = __salt__['cp.get_file_str'](public_key) + public_key = __salt__["cp.get_file_str"](public_key) except IOError as e: log.debug(e) - ret['comment'] = 'File {0} not found.'.format(public_key) - ret['result'] = False + ret["comment"] = "File {0} not found.".format(public_key) + ret["result"] = False return ret - if 'salt://' in private_key: + if "salt://" in private_key: try: - private_key = __salt__['cp.get_file_str'](private_key) + private_key = __salt__["cp.get_file_str"](private_key) except IOError as e: log.debug(e) - ret['comment'] = 'File {0} not found.'.format(private_key) - ret['result'] = False + ret["comment"] = "File {0} not found.".format(private_key) + ret["result"] = False return ret - if cert_chain is not None and 'salt://' in cert_chain: + if cert_chain is not None and "salt://" in cert_chain: try: - cert_chain = __salt__['cp.get_file_str'](cert_chain) + cert_chain = __salt__["cp.get_file_str"](cert_chain) except IOError as e: log.debug(e) - ret['comment'] = 'File {0} not found.'.format(cert_chain) - ret['result'] = False + ret["comment"] = "File {0} not found.".format(cert_chain) + ret["result"] = False return ret - if __opts__['test']: - ret['comment'] = 'Server certificate {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Server certificate {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_iam.upload_server_cert'](name, public_key, private_key, cert_chain, - path, region, key, keyid, profile) + created = __salt__["boto_iam.upload_server_cert"]( + name, public_key, private_key, cert_chain, path, region, key, keyid, profile + ) if created is not False: - ret['comment'] = 'Certificate {0} was created.'.format(name) - ret['changes'] = created + ret["comment"] = "Certificate {0} was created.".format(name) + ret["changes"] = created return ret - ret['result'] = False - ret['comment'] = 'Certificate {0} failed to be created.'.format(name) + ret["result"] = False + ret["comment"] = "Certificate {0} failed to be created.".format(name) return ret -def policy_present(name, policy_document, path=None, description=None, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_present( + name, + policy_document, + path=None, + description=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ .. versionadded:: 2015.8.0 @@ -1481,32 +1838,39 @@ def policy_present(name, policy_document, path=None, description=None, profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - policy = __salt__['boto_iam.get_policy'](name, region, key, keyid, profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + policy = __salt__["boto_iam.get_policy"](name, region, key, keyid, profile) if not policy: - if __opts__['test']: - ret['comment'] = 'IAM policy {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "IAM policy {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_iam.create_policy'](name, policy_document, path, description, region, key, keyid, profile) + created = __salt__["boto_iam.create_policy"]( + name, policy_document, path, description, region, key, keyid, profile + ) if created: - ret['changes']['policy'] = created - ret['comment'] = ' '.join([ret['comment'], 'Policy {0} has been created.'.format(name)]) + ret["changes"]["policy"] = created + ret["comment"] = " ".join( + [ret["comment"], "Policy {0} has been created.".format(name)] + ) else: - ret['result'] = False - ret['comment'] = 'Failed to update policy.' - ret['changes'] = {} + ret["result"] = False + ret["comment"] = "Failed to update policy." + ret["changes"] = {} return ret else: - policy = policy.get('policy', {}) - ret['comment'] = ' '.join([ret['comment'], 'Policy {0} is present.'.format(name)]) - _describe = __salt__['boto_iam.get_policy_version'](name, policy.get('default_version_id'), - region, key, keyid, profile).get('policy_version', {}) - if isinstance(_describe['document'], six.string_types): - describeDict = salt.utils.json.loads(_describe['document']) + policy = policy.get("policy", {}) + ret["comment"] = " ".join( + [ret["comment"], "Policy {0} is present.".format(name)] + ) + _describe = __salt__["boto_iam.get_policy_version"]( + name, policy.get("default_version_id"), region, key, keyid, profile + ).get("policy_version", {}) + if isinstance(_describe["document"], six.string_types): + describeDict = salt.utils.json.loads(_describe["document"]) else: - describeDict = _describe['document'] + describeDict = _describe["document"] if isinstance(policy_document, six.string_types): policy_document = salt.utils.json.loads(policy_document) @@ -1514,38 +1878,47 @@ def policy_present(name, policy_document, path=None, description=None, r = salt.utils.data.compare_dicts(describeDict, policy_document) if bool(r): - if __opts__['test']: - ret['comment'] = 'Policy {0} set to be modified.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Policy {0} set to be modified.".format(name) + ret["result"] = None return ret - ret['comment'] = ' '.join([ret['comment'], 'Policy to be modified']) + ret["comment"] = " ".join([ret["comment"], "Policy to be modified"]) policy_document = salt.utils.json.dumps(policy_document) - r = __salt__['boto_iam.create_policy_version'](policy_name=name, - policy_document=policy_document, - set_as_default=True, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to update policy: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_iam.create_policy_version"]( + policy_name=name, + policy_document=policy_document, + set_as_default=True, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to update policy: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret - __salt__['boto_iam.delete_policy_version'](policy_name=name, - version_id=policy['default_version_id'], - region=region, key=key, - keyid=keyid, profile=profile) + __salt__["boto_iam.delete_policy_version"]( + policy_name=name, + version_id=policy["default_version_id"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) - ret['changes'].setdefault('new', {})['document'] = policy_document - ret['changes'].setdefault('old', {})['document'] = _describe['document'] + ret["changes"].setdefault("new", {})["document"] = policy_document + ret["changes"].setdefault("old", {})["document"] = _describe["document"] return ret -def policy_absent(name, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_absent(name, region=None, key=None, keyid=None, profile=None): + """ .. versionadded:: 2015.8.0 @@ -1566,50 +1939,57 @@ def policy_absent(name, profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_iam.policy_exists'](name, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_iam.policy_exists"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if not r: - ret['comment'] = 'Policy {0} does not exist.'.format(name) + ret["comment"] = "Policy {0} does not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Policy {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Policy {0} is set to be removed.".format(name) + ret["result"] = None return ret # delete non-default versions - versions = __salt__['boto_iam.list_policy_versions'](name, - region=region, key=key, - keyid=keyid, profile=profile) + versions = __salt__["boto_iam.list_policy_versions"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if versions: for version in versions: - if version.get('is_default_version', False) in ('true', True): + if version.get("is_default_version", False) in ("true", True): continue - r = __salt__['boto_iam.delete_policy_version'](name, - version_id=version.get('version_id'), - region=region, key=key, - keyid=keyid, profile=profile) + r = __salt__["boto_iam.delete_policy_version"]( + name, + version_id=version.get("version_id"), + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not r: - ret['result'] = False - ret['comment'] = 'Failed to delete policy {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete policy {0}.".format(name) return ret - r = __salt__['boto_iam.delete_policy'](name, - region=region, key=key, - keyid=keyid, profile=profile) + r = __salt__["boto_iam.delete_policy"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if not r: - ret['result'] = False - ret['comment'] = 'Failed to delete policy {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete policy {0}.".format(name) return ret - ret['changes']['old'] = {'policy': name} - ret['changes']['new'] = {'policy': None} - ret['comment'] = 'Policy {0} deleted.'.format(name) + ret["changes"]["old"] = {"policy": name} + ret["changes"]["new"] = {"policy": None} + ret["comment"] = "Policy {0} deleted.".format(name) return ret -def saml_provider_present(name, saml_metadata_document, region=None, key=None, keyid=None, profile=None): - ''' +def saml_provider_present( + name, saml_metadata_document, region=None, key=None, keyid=None, profile=None +): + """ .. versionadded:: 2016.11.0 Ensure the SAML provider with the specified name is present. @@ -1632,41 +2012,48 @@ def saml_provider_present(name, saml_metadata_document, region=None, key=None, k profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - if 'salt://' in saml_metadata_document: + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + if "salt://" in saml_metadata_document: try: - saml_metadata_document = __salt__['cp.get_file_str'](saml_metadata_document) + saml_metadata_document = __salt__["cp.get_file_str"](saml_metadata_document) ET.fromstring(saml_metadata_document) except IOError as e: log.debug(e) - ret['comment'] = 'SAML document file {0} not found or could not be loaded'.format(name) - ret['result'] = False + ret[ + "comment" + ] = "SAML document file {0} not found or could not be loaded".format(name) + ret["result"] = False return ret - for provider in __salt__['boto_iam.list_saml_providers'](region=region, - key=key, keyid=keyid, - profile=profile): + for provider in __salt__["boto_iam.list_saml_providers"]( + region=region, key=key, keyid=keyid, profile=profile + ): if provider == name: - ret['comment'] = 'SAML provider {0} is present.'.format(name) + ret["comment"] = "SAML provider {0} is present.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'SAML provider {0} is set to be create.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "SAML provider {0} is set to be create.".format(name) + ret["result"] = None return ret - created = __salt__['boto_iam.create_saml_provider'](name, saml_metadata_document, - region=region, key=key, keyid=keyid, - profile=profile) + created = __salt__["boto_iam.create_saml_provider"]( + name, + saml_metadata_document, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if created: - ret['comment'] = 'SAML provider {0} was created.'.format(name) - ret['changes']['new'] = name + ret["comment"] = "SAML provider {0} was created.".format(name) + ret["changes"]["new"] = name return ret - ret['result'] = False - ret['comment'] = 'SAML provider {0} failed to be created.'.format(name) + ret["result"] = False + ret["comment"] = "SAML provider {0} failed to be created.".format(name) return ret def saml_provider_absent(name, region=None, key=None, keyid=None, profile=None): - ''' + """ .. versionadded:: 2016.11.0 Ensure the SAML provider with the specified name is absent. @@ -1689,33 +2076,33 @@ def saml_provider_absent(name, region=None, key=None, keyid=None, profile=None): profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - provider = __salt__['boto_iam.list_saml_providers'](region=region, - key=key, keyid=keyid, - profile=profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + provider = __salt__["boto_iam.list_saml_providers"]( + region=region, key=key, keyid=keyid, profile=profile + ) if len(provider) == 0: - ret['comment'] = 'SAML provider {0} is absent.'.format(name) + ret["comment"] = "SAML provider {0} is absent.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'SAML provider {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "SAML provider {0} is set to be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_iam.delete_saml_provider'](name, region=region, - key=key, keyid=keyid, - profile=profile) + deleted = __salt__["boto_iam.delete_saml_provider"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if deleted is not False: - ret['comment'] = 'SAML provider {0} was deleted.'.format(name) - ret['changes']['old'] = name + ret["comment"] = "SAML provider {0} was deleted.".format(name) + ret["changes"]["old"] = name return ret - ret['result'] = False - ret['comment'] = 'SAML provider {0} failed to be deleted.'.format(name) + ret["result"] = False + ret["comment"] = "SAML provider {0} failed to be deleted.".format(name) return ret def _get_error(error): # Converts boto exception to string that can be used to output error. - error = '\n'.join(error.split('\n')[1:]) + error = "\n".join(error.split("\n")[1:]) error = ET.fromstring(error) code = error[0][1].text message = error[0][2].text diff --git a/salt/states/boto_iam_role.py b/salt/states/boto_iam_role.py index c8eec3bbd49..5377b7a34f9 100644 --- a/salt/states/boto_iam_role.py +++ b/salt/states/boto_iam_role.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage IAM roles ================ @@ -85,39 +85,44 @@ on the IAM role to be persistent. This functionality was added in 2015.8.0. If ``region`` is missing from the ``profile`` data set, ``us-east-1`` will be used as the default region. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging -import salt.utils.dictupdate as dictupdate -from salt.utils.odict import OrderedDict + import salt.utils.dictdiffer +import salt.utils.dictupdate as dictupdate from salt.ext import six +from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_iam_role' if 'boto_iam.role_exists' in __salt__ else False + """ + if "boto_iam.role_exists" in __salt__: + return "boto_iam_role" + return (False, "boto_iam module could not be loaded") def present( - name, - policy_document=None, - policy_document_from_pillars=None, - path=None, - policies=None, - policies_from_pillars=None, - managed_policies=None, - create_instance_profile=True, - region=None, - key=None, - keyid=None, - profile=None, - delete_policies=True): - ''' + name, + policy_document=None, + policy_document_from_pillars=None, + path=None, + policies=None, + policies_from_pillars=None, + managed_policies=None, + create_instance_profile=True, + region=None, + key=None, + keyid=None, + profile=None, + delete_policies=True, +): + """ Ensure the IAM role exists. name @@ -176,22 +181,21 @@ def present( allowing manual modifications on the IAM role to be persistent. .. versionadded:: 2015.8.0 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # Build up _policy_document _policy_document = {} if policy_document_from_pillars: - from_pillars = __salt__['pillar.get'](policy_document_from_pillars) + from_pillars = __salt__["pillar.get"](policy_document_from_pillars) if from_pillars: - _policy_document['Version'] = from_pillars['Version'] - _policy_document.setdefault('Statement', []) - _policy_document['Statement'].extend(from_pillars['Statement']) + _policy_document["Version"] = from_pillars["Version"] + _policy_document.setdefault("Statement", []) + _policy_document["Statement"].extend(from_pillars["Statement"]) if policy_document: - _policy_document['Version'] = policy_document['Version'] - _policy_document.setdefault('Statement', []) - _policy_document['Statement'].extend(policy_document['Statement']) - _ret = _role_present(name, _policy_document, path, region, key, keyid, - profile) + _policy_document["Version"] = policy_document["Version"] + _policy_document.setdefault("Statement", []) + _policy_document["Statement"].extend(policy_document["Statement"]) + _ret = _role_present(name, _policy_document, path, region, key, keyid, profile) # Build up _policies if not policies: @@ -202,163 +206,162 @@ def present( managed_policies = [] _policies = {} for policy in policies_from_pillars: - _policy = __salt__['pillar.get'](policy) + _policy = __salt__["pillar.get"](policy) _policies.update(_policy) _policies.update(policies) - ret['changes'] = _ret['changes'] - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = _ret["changes"] + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret if create_instance_profile: _ret = _instance_profile_present(name, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret _ret = _instance_profile_associated(name, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - _ret = _policies_present(name, _policies, region, key, keyid, profile, - delete_policies) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + _ret = _policies_present( + name, _policies, region, key, keyid, profile, delete_policies + ) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] _ret = _policies_attached(name, managed_policies, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret def _role_present( - name, - policy_document=None, - path=None, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - role = __salt__['boto_iam.describe_role'](name, region, key, keyid, - profile) + name, + policy_document=None, + path=None, + region=None, + key=None, + keyid=None, + profile=None, +): + ret = {"result": True, "comment": "", "changes": {}} + role = __salt__["boto_iam.describe_role"](name, region, key, keyid, profile) if not role: - if __opts__['test']: - ret['comment'] = 'IAM role {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "IAM role {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_iam.create_role'](name, policy_document, - path, region, key, - keyid, profile) + created = __salt__["boto_iam.create_role"]( + name, policy_document, path, region, key, keyid, profile + ) if created: - ret['changes']['old'] = {'role': None} - ret['changes']['new'] = {'role': name} - ret['comment'] = 'IAM role {0} created.'.format(name) + ret["changes"]["old"] = {"role": None} + ret["changes"]["new"] = {"role": name} + ret["comment"] = "IAM role {0} created.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} IAM role.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} IAM role.".format(name) else: - ret['comment'] = '{0} role present.'.format(name) + ret["comment"] = "{0} role present.".format(name) if not policy_document: - _policy_document = __salt__['boto_iam.build_policy']( - region, key, keyid, profile) + _policy_document = __salt__["boto_iam.build_policy"]( + region, key, keyid, profile + ) else: _policy_document = policy_document if salt.utils.dictdiffer.deep_diff( - _sort_policy(role['assume_role_policy_document']), - _sort_policy(_policy_document)): - if __opts__['test']: - msg = 'Assume role policy document to be updated.' - ret['comment'] = '{0} {1}'.format(ret['comment'], msg) - ret['result'] = None + _sort_policy(role["assume_role_policy_document"]), + _sort_policy(_policy_document), + ): + if __opts__["test"]: + msg = "Assume role policy document to be updated." + ret["comment"] = "{0} {1}".format(ret["comment"], msg) + ret["result"] = None return ret - updated = __salt__['boto_iam.update_assume_role_policy']( + updated = __salt__["boto_iam.update_assume_role_policy"]( name, _policy_document, region, key, keyid, profile ) if updated: - msg = 'Assume role policy document updated.' - ret['comment'] = '{0} {1}'.format(ret['comment'], msg) - ret['changes']['old'] = {'policy_document': role['assume_role_policy_document']} - ret['changes']['new'] = {'policy_document': _policy_document} + msg = "Assume role policy document updated." + ret["comment"] = "{0} {1}".format(ret["comment"], msg) + ret["changes"]["old"] = { + "policy_document": role["assume_role_policy_document"] + } + ret["changes"]["new"] = {"policy_document": _policy_document} else: - ret['result'] = False - msg = 'Failed to update assume role policy.' - ret['comment'] = '{0} {1}'.format(ret['comment'], msg) + ret["result"] = False + msg = "Failed to update assume role policy." + ret["comment"] = "{0} {1}".format(ret["comment"], msg) return ret -def _instance_profile_present( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - exists = __salt__['boto_iam.instance_profile_exists'](name, region, key, - keyid, profile) +def _instance_profile_present(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} + exists = __salt__["boto_iam.instance_profile_exists"]( + name, region, key, keyid, profile + ) if not exists: - if __opts__['test']: - ret['comment'] = 'Instance profile {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Instance profile {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_iam.create_instance_profile'](name, region, - key, keyid, - profile) + created = __salt__["boto_iam.create_instance_profile"]( + name, region, key, keyid, profile + ) if created: - ret['changes']['old'] = {'instance_profile': None} - ret['changes']['new'] = {'instance_profile': name} - ret['comment'] = 'Instance profile {0} created.'.format(name) + ret["changes"]["old"] = {"instance_profile": None} + ret["changes"]["new"] = {"instance_profile": name} + ret["comment"] = "Instance profile {0} created.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} instance profile.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} instance profile.".format(name) return ret -def _instance_profile_associated( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - is_associated = __salt__['boto_iam.profile_associated'](name, name, region, - key, keyid, - profile) +def _instance_profile_associated(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} + is_associated = __salt__["boto_iam.profile_associated"]( + name, name, region, key, keyid, profile + ) if not is_associated: - if __opts__['test']: - ret['comment'] = 'Instance profile {0} is set to be associated.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Instance profile {0} is set to be associated.".format( + name + ) + ret["result"] = None return ret - associated = __salt__['boto_iam.associate_profile_to_role'](name, name, - region, - key, keyid, - profile) + associated = __salt__["boto_iam.associate_profile_to_role"]( + name, name, region, key, keyid, profile + ) if associated: - ret['changes']['old'] = {'profile_associated': None} - ret['changes']['new'] = {'profile_associated': True} - ret['comment'] = 'Instance profile {0} associated.'.format(name) + ret["changes"]["old"] = {"profile_associated": None} + ret["changes"]["new"] = {"profile_associated": True} + ret["comment"] = "Instance profile {0} associated.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to associate {0} instance profile with {0} role.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "Failed to associate {0} instance profile with {0} role.".format(name) return ret def _sort_policy(doc): - ''' + """ List-type sub-items in policies don't happen to be order-sensitive, but compare operations will render them unequal, leading to non-idempotent state runs. We'll sort any list-type subitems before comparison to reduce the likelihood of false negatives. - ''' + """ if isinstance(doc, list): return sorted([_sort_policy(i) for i in doc]) elif isinstance(doc, (dict, OrderedDict)): @@ -367,154 +370,171 @@ def _sort_policy(doc): def _policies_present( - name, - policies=None, - region=None, - key=None, - keyid=None, - profile=None, - delete_policies=True): - ret = {'result': True, 'comment': '', 'changes': {}} + name, + policies=None, + region=None, + key=None, + keyid=None, + profile=None, + delete_policies=True, +): + ret = {"result": True, "comment": "", "changes": {}} policies_to_create = {} policies_to_delete = [] for policy_name, policy in six.iteritems(policies): - _policy = __salt__['boto_iam.get_role_policy'](name, policy_name, - region, key, keyid, - profile) + _policy = __salt__["boto_iam.get_role_policy"]( + name, policy_name, region, key, keyid, profile + ) if _policy != policy: policies_to_create[policy_name] = policy - _list = __salt__['boto_iam.list_role_policies'](name, region, key, keyid, - profile) + _list = __salt__["boto_iam.list_role_policies"](name, region, key, keyid, profile) for policy_name in _list: if delete_policies and policy_name not in policies: policies_to_delete.append(policy_name) if policies_to_create or policies_to_delete: _to_modify = list(policies_to_delete) _to_modify.extend(policies_to_create) - if __opts__['test']: - ret['comment'] = '{0} policies to be modified on role {1}.'.format(', '.join(_to_modify), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be modified on role {1}.".format( + ", ".join(_to_modify), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'policies': _list} + ret["changes"]["old"] = {"policies": _list} for policy_name, policy in six.iteritems(policies_to_create): - policy_set = __salt__['boto_iam.create_role_policy'](name, - policy_name, - policy, - region, key, - keyid, - profile) + policy_set = __salt__["boto_iam.create_role_policy"]( + name, policy_name, policy, region, key, keyid, profile + ) if not policy_set: - _list = __salt__['boto_iam.list_role_policies'](name, region, - key, keyid, - profile) - ret['changes']['new'] = {'policies': _list} - ret['result'] = False - ret['comment'] = 'Failed to add policy {0} to role {1}'.format(policy_name, name) + _list = __salt__["boto_iam.list_role_policies"]( + name, region, key, keyid, profile + ) + ret["changes"]["new"] = {"policies": _list} + ret["result"] = False + ret["comment"] = "Failed to add policy {0} to role {1}".format( + policy_name, name + ) return ret for policy_name in policies_to_delete: - policy_unset = __salt__['boto_iam.delete_role_policy'](name, - policy_name, - region, key, - keyid, - profile) + policy_unset = __salt__["boto_iam.delete_role_policy"]( + name, policy_name, region, key, keyid, profile + ) if not policy_unset: - _list = __salt__['boto_iam.list_role_policies'](name, region, - key, keyid, - profile) - ret['changes']['new'] = {'policies': _list} - ret['result'] = False - ret['comment'] = 'Failed to remove policy {0} from role {1}'.format(policy_name, name) + _list = __salt__["boto_iam.list_role_policies"]( + name, region, key, keyid, profile + ) + ret["changes"]["new"] = {"policies": _list} + ret["result"] = False + ret["comment"] = "Failed to remove policy {0} from role {1}".format( + policy_name, name + ) return ret - _list = __salt__['boto_iam.list_role_policies'](name, region, key, - keyid, profile) - ret['changes']['new'] = {'policies': _list} - ret['comment'] = '{0} policies modified on role {1}.'.format(', '.join(_list), name) + _list = __salt__["boto_iam.list_role_policies"]( + name, region, key, keyid, profile + ) + ret["changes"]["new"] = {"policies": _list} + ret["comment"] = "{0} policies modified on role {1}.".format( + ", ".join(_list), name + ) return ret def _policies_attached( - name, - managed_policies=None, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} + name, managed_policies=None, region=None, key=None, keyid=None, profile=None +): + ret = {"result": True, "comment": "", "changes": {}} policies_to_attach = [] policies_to_detach = [] for policy in managed_policies or []: - entities = __salt__['boto_iam.list_entities_for_policy']( - policy, - entity_filter='Role', - region=region, key=key, keyid=keyid, profile=profile) + entities = __salt__["boto_iam.list_entities_for_policy"]( + policy, + entity_filter="Role", + region=region, + key=key, + keyid=keyid, + profile=profile, + ) found = False - for roledict in entities.get('policy_roles', []): - if name == roledict.get('role_name'): + for roledict in entities.get("policy_roles", []): + if name == roledict.get("role_name"): found = True break if not found: policies_to_attach.append(policy) - _list = __salt__['boto_iam.list_attached_role_policies']( - name, region=region, key=key, keyid=keyid, profile=profile) - oldpolicies = [x.get('policy_arn') for x in _list] + _list = __salt__["boto_iam.list_attached_role_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + oldpolicies = [x.get("policy_arn") for x in _list] for policy_data in _list: - if policy_data.get('policy_name') not in managed_policies \ - and policy_data.get('policy_arn') not in managed_policies: - policies_to_detach.append(policy_data.get('policy_arn')) + if ( + policy_data.get("policy_name") not in managed_policies + and policy_data.get("policy_arn") not in managed_policies + ): + policies_to_detach.append(policy_data.get("policy_arn")) if policies_to_attach or policies_to_detach: _to_modify = list(policies_to_detach) _to_modify.extend(policies_to_attach) - if __opts__['test']: - ret['comment'] = '{0} policies to be modified on role {1}.'.format(', '.join(_to_modify), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be modified on role {1}.".format( + ", ".join(_to_modify), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'managed_policies': oldpolicies} + ret["changes"]["old"] = {"managed_policies": oldpolicies} for policy_name in policies_to_attach: - policy_set = __salt__['boto_iam.attach_role_policy'](policy_name, - role_name=name, - region=region, - key=key, - keyid=keyid, - profile=profile) + policy_set = __salt__["boto_iam.attach_role_policy"]( + policy_name, + role_name=name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not policy_set: - _list = __salt__['boto_iam.list_attached_role_policies']( - name, region=region, key=key, keyid=keyid, profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to add policy {0} to role {1}'.format(policy_name, name) + _list = __salt__["boto_iam.list_attached_role_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to add policy {0} to role {1}".format( + policy_name, name + ) return ret for policy_name in policies_to_detach: - policy_unset = __salt__['boto_iam.detach_role_policy'](policy_name, - role_name=name, - region=region, - key=key, - keyid=keyid, - profile=profile) + policy_unset = __salt__["boto_iam.detach_role_policy"]( + policy_name, + role_name=name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not policy_unset: - _list = __salt__['boto_iam.list_attached_role_policies']( - name, region=region, key=key, keyid=keyid, profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to remove policy {0} from role {1}'.format(policy_name, name) + _list = __salt__["boto_iam.list_attached_role_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to remove policy {0} from role {1}".format( + policy_name, name + ) return ret - _list = __salt__['boto_iam.list_attached_role_policies']( - name, region=region, key=key, keyid=keyid, profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] + _list = __salt__["boto_iam.list_attached_role_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] log.debug(newpolicies) - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['comment'] = '{0} policies modified on role {1}.'.format(', '.join(newpolicies), name) + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["comment"] = "{0} policies modified on role {1}.".format( + ", ".join(newpolicies), name + ) return ret -def absent( - name, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def absent(name, region=None, key=None, keyid=None, profile=None): + """ Ensure the IAM role is deleted. name @@ -532,204 +552,195 @@ def absent( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} _ret = _policies_absent(name, region, key, keyid, profile) - ret['changes'] = _ret['changes'] - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = _ret["changes"] + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret _ret = _policies_detached(name, region, key, keyid, profile) - ret['changes'] = _ret['changes'] - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = _ret["changes"] + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret _ret = _instance_profile_disassociated(name, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret _ret = _instance_profile_absent(name, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret _ret = _role_absent(name, region, key, keyid, profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret -def _role_absent( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} +def _role_absent(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} - exists = __salt__['boto_iam.role_exists'](name, region, key, keyid, - profile) + exists = __salt__["boto_iam.role_exists"](name, region, key, keyid, profile) if exists: - if __opts__['test']: - ret['comment'] = 'IAM role {0} is set to be removed.'.format( - name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "IAM role {0} is set to be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_iam.delete_role'](name, region, key, keyid, - profile) + deleted = __salt__["boto_iam.delete_role"](name, region, key, keyid, profile) if deleted: - ret['changes']['old'] = {'role': name} - ret['changes']['new'] = {'role': None} - ret['comment'] = 'IAM role {0} removed.'.format(name) + ret["changes"]["old"] = {"role": name} + ret["changes"]["new"] = {"role": None} + ret["comment"] = "IAM role {0} removed.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} iam role.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} iam role.".format(name) else: - ret['comment'] = '{0} role does not exist.'.format(name) + ret["comment"] = "{0} role does not exist.".format(name) return ret -def _instance_profile_absent( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} +def _instance_profile_absent(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} - exists = __salt__['boto_iam.instance_profile_exists'](name, region, key, - keyid, profile) + exists = __salt__["boto_iam.instance_profile_exists"]( + name, region, key, keyid, profile + ) if exists: - if __opts__['test']: - ret['comment'] = 'Instance profile {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Instance profile {0} is set to be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_iam.delete_instance_profile'](name, region, - key, keyid, - profile) + deleted = __salt__["boto_iam.delete_instance_profile"]( + name, region, key, keyid, profile + ) if deleted: - ret['changes']['old'] = {'instance_profile': name} - ret['changes']['new'] = {'instance_profile': None} - ret['comment'] = 'Instance profile {0} removed.'.format(name) + ret["changes"]["old"] = {"instance_profile": name} + ret["changes"]["new"] = {"instance_profile": None} + ret["comment"] = "Instance profile {0} removed.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} instance profile.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} instance profile.".format(name) else: - ret['comment'] = '{0} instance profile does not exist.'.format(name) + ret["comment"] = "{0} instance profile does not exist.".format(name) return ret -def _policies_absent( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - _list = __salt__['boto_iam.list_role_policies'](name, region, key, keyid, - profile) +def _policies_absent(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} + _list = __salt__["boto_iam.list_role_policies"](name, region, key, keyid, profile) if not _list: - ret['comment'] = 'No policies in role {0}.'.format(name) + ret["comment"] = "No policies in role {0}.".format(name) return ret - if __opts__['test']: - ret['comment'] = '{0} policies to be removed from role {1}.'.format(', '.join(_list), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be removed from role {1}.".format( + ", ".join(_list), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'policies': _list} + ret["changes"]["old"] = {"policies": _list} for policy_name in _list: - policy_unset = __salt__['boto_iam.delete_role_policy'](name, - policy_name, - region, key, - keyid, - profile) + policy_unset = __salt__["boto_iam.delete_role_policy"]( + name, policy_name, region, key, keyid, profile + ) if not policy_unset: - _list = __salt__['boto_iam.list_role_policies'](name, region, - key, keyid, - profile) - ret['changes']['new'] = {'policies': _list} - ret['result'] = False - ret['comment'] = 'Failed to add policy {0} to role {1}'.format(policy_name, name) + _list = __salt__["boto_iam.list_role_policies"]( + name, region, key, keyid, profile + ) + ret["changes"]["new"] = {"policies": _list} + ret["result"] = False + ret["comment"] = "Failed to add policy {0} to role {1}".format( + policy_name, name + ) return ret - _list = __salt__['boto_iam.list_role_policies'](name, region, key, - keyid, profile) - ret['changes']['new'] = {'policies': _list} - ret['comment'] = '{0} policies removed from role {1}.'.format(', '.join(_list), name) + _list = __salt__["boto_iam.list_role_policies"](name, region, key, keyid, profile) + ret["changes"]["new"] = {"policies": _list} + ret["comment"] = "{0} policies removed from role {1}.".format( + ", ".join(_list), name + ) return ret -def _policies_detached( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - _list = __salt__['boto_iam.list_attached_role_policies']( - role_name=name, region=region, key=key, keyid=keyid, profile=profile) - oldpolicies = [x.get('policy_arn') for x in _list] +def _policies_detached(name, region=None, key=None, keyid=None, profile=None): + ret = {"result": True, "comment": "", "changes": {}} + _list = __salt__["boto_iam.list_attached_role_policies"]( + role_name=name, region=region, key=key, keyid=keyid, profile=profile + ) + oldpolicies = [x.get("policy_arn") for x in _list] if not _list: - ret['comment'] = 'No attached policies in role {0}.'.format(name) + ret["comment"] = "No attached policies in role {0}.".format(name) return ret - if __opts__['test']: - ret['comment'] = '{0} policies to be detached from role {1}.'.format(', '.join(oldpolicies), name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} policies to be detached from role {1}.".format( + ", ".join(oldpolicies), name + ) + ret["result"] = None return ret - ret['changes']['old'] = {'managed_policies': oldpolicies} + ret["changes"]["old"] = {"managed_policies": oldpolicies} for policy_arn in oldpolicies: - policy_unset = __salt__['boto_iam.detach_role_policy'](policy_arn, - name, - region=region, - key=key, - keyid=keyid, - profile=profile) + policy_unset = __salt__["boto_iam.detach_role_policy"]( + policy_arn, name, region=region, key=key, keyid=keyid, profile=profile + ) if not policy_unset: - _list = __salt__['boto_iam.list_attached_role_policies']( - name, region=region, key=key, keyid=keyid, profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['result'] = False - ret['comment'] = 'Failed to detach {0} from role {1}'.format(policy_arn, name) + _list = __salt__["boto_iam.list_attached_role_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["result"] = False + ret["comment"] = "Failed to detach {0} from role {1}".format( + policy_arn, name + ) return ret - _list = __salt__['boto_iam.list_attached_role_policies']( - name, region=region, key=key, keyid=keyid, profile=profile) - newpolicies = [x.get('policy_arn') for x in _list] - ret['changes']['new'] = {'managed_policies': newpolicies} - ret['comment'] = '{0} policies detached from role {1}.'.format(', '.join(newpolicies), name) + _list = __salt__["boto_iam.list_attached_role_policies"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + newpolicies = [x.get("policy_arn") for x in _list] + ret["changes"]["new"] = {"managed_policies": newpolicies} + ret["comment"] = "{0} policies detached from role {1}.".format( + ", ".join(newpolicies), name + ) return ret def _instance_profile_disassociated( - name, - region=None, - key=None, - keyid=None, - profile=None): - ret = {'result': True, 'comment': '', 'changes': {}} - is_associated = __salt__['boto_iam.profile_associated'](name, name, region, - key, keyid, - profile) + name, region=None, key=None, keyid=None, profile=None +): + ret = {"result": True, "comment": "", "changes": {}} + is_associated = __salt__["boto_iam.profile_associated"]( + name, name, region, key, keyid, profile + ) if is_associated: - if __opts__['test']: - ret['comment'] = 'Instance profile {0} is set to be disassociated.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Instance profile {0} is set to be disassociated.".format( + name + ) + ret["result"] = None return ret - associated = __salt__['boto_iam.disassociate_profile_from_role'](name, name, region, key, keyid, profile) + associated = __salt__["boto_iam.disassociate_profile_from_role"]( + name, name, region, key, keyid, profile + ) if associated: - ret['changes']['old'] = {'profile_associated': True} - ret['changes']['new'] = {'profile_associated': False} - ret['comment'] = 'Instance profile {0} disassociated.'.format(name) + ret["changes"]["old"] = {"profile_associated": True} + ret["changes"]["new"] = {"profile_associated": False} + ret["comment"] = "Instance profile {0} disassociated.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to disassociate {0} instance profile from {0} role.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "Failed to disassociate {0} instance profile from {0} role.".format( + name + ) return ret diff --git a/salt/states/boto_iot.py b/salt/states/boto_iot.py index 737d841341a..0d038c31756 100644 --- a/salt/states/boto_iot.py +++ b/salt/states/boto_iot.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage IoT Objects ================== @@ -69,9 +69,10 @@ config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import datetime import logging import os @@ -88,16 +89,25 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_iot' if 'boto_iot.policy_exists' in __salt__ else False + """ + if "boto_iot.policy_exists" in __salt__: + return "boto_iot" + return (False, "boto_iot module could not be loaded") -def thing_type_present(name, thingTypeName, thingTypeDescription, +def thing_type_present( + name, + thingTypeName, + thingTypeDescription, searchableAttributesList, - region=None, key=None, keyid=None, profile=None): - ''' + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure thing type exists. .. versionadded:: 2016.11.0 @@ -128,59 +138,70 @@ def thing_type_present(name, thingTypeName, thingTypeDescription, A dict with region, key, keyid, or a pillar key (string) that contains a dict with region, key, and keyid - ''' - ret = { - 'name': thingTypeName, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": thingTypeName, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_iot.thing_type_exists']( - thingTypeName=thingTypeName, - region=region, key=key, keyid=keyid, profile=profile - ) - - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create thing type: {0}.'.format(r['error']['message']) - return ret - - if r.get('exists'): - ret['result'] = True - ret['comment'] = 'Thing type with given name {0} already exists'.format(thingTypeName) - return ret - - if __opts__['test']: - ret['comment'] = 'Thing type {0} is set to be created.'.format(thingTypeName) - ret['result'] = None - return ret - - r = __salt__['boto_iot.create_thing_type']( - thingTypeName=thingTypeName, - thingTypeDescription=thingTypeDescription, - searchableAttributesList=searchableAttributesList, - region=region, key=key, keyid=keyid, profile=profile - ) - - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create thing type: {0}.'.format(r['error']['message']) - return ret - - _describe = __salt__['boto_iot.describe_thing_type']( + r = __salt__["boto_iot.thing_type_exists"]( thingTypeName=thingTypeName, - region=region, key=key, keyid=keyid, profile=profile + region=region, + key=key, + keyid=keyid, + profile=profile, ) - ret['changes']['old'] = {'thing_type': None} - ret['changes']['new'] = _describe - ret['comment'] = 'Thing Type {0} created.'.format(thingTypeName) + + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create thing type: {0}.".format( + r["error"]["message"] + ) + return ret + + if r.get("exists"): + ret["result"] = True + ret["comment"] = "Thing type with given name {0} already exists".format( + thingTypeName + ) + return ret + + if __opts__["test"]: + ret["comment"] = "Thing type {0} is set to be created.".format(thingTypeName) + ret["result"] = None + return ret + + r = __salt__["boto_iot.create_thing_type"]( + thingTypeName=thingTypeName, + thingTypeDescription=thingTypeDescription, + searchableAttributesList=searchableAttributesList, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create thing type: {0}.".format( + r["error"]["message"] + ) + return ret + + _describe = __salt__["boto_iot.describe_thing_type"]( + thingTypeName=thingTypeName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"]["old"] = {"thing_type": None} + ret["changes"]["new"] = _describe + ret["comment"] = "Thing Type {0} created.".format(thingTypeName) return ret -def thing_type_absent(name, thingTypeName, - region=None, key=None, keyid=None, profile=None): - ''' +def thing_type_absent( + name, thingTypeName, region=None, key=None, keyid=None, profile=None +): + """ Ensure thing type with passed properties is absent. .. versionadded:: 2016.11.0 @@ -204,38 +225,41 @@ def thing_type_absent(name, thingTypeName, A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': thingTypeName, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": thingTypeName, "result": True, "comment": "", "changes": {}} - _describe = __salt__['boto_iot.describe_thing_type']( + _describe = __salt__["boto_iot.describe_thing_type"]( thingTypeName=thingTypeName, - region=region, key=key, keyid=keyid, profile=profile + region=region, + key=key, + keyid=keyid, + profile=profile, ) - if 'error' in _describe: - ret['result'] = False - ret['comment'] = 'Failed to delete thing type: {0}.'.format(_describe['error']['message']) + if "error" in _describe: + ret["result"] = False + ret["comment"] = "Failed to delete thing type: {0}.".format( + _describe["error"]["message"] + ) return ret - if _describe and not _describe['thing_type']: - ret['comment'] = 'Thing Type {0} does not exist.'.format(thingTypeName) + if _describe and not _describe["thing_type"]: + ret["comment"] = "Thing Type {0} does not exist.".format(thingTypeName) return ret - _existing_thing_type = _describe['thing_type'] - _thing_type_metadata = _existing_thing_type.get('thingTypeMetadata') - _deprecated = _thing_type_metadata.get('deprecated', False) + _existing_thing_type = _describe["thing_type"] + _thing_type_metadata = _existing_thing_type.get("thingTypeMetadata") + _deprecated = _thing_type_metadata.get("deprecated", False) - if __opts__['test']: + if __opts__["test"]: if _deprecated: - _change_desc = 'removed' + _change_desc = "removed" else: - _change_desc = 'deprecated and removed' - ret['comment'] = 'Thing Type {0} is set to be {1}.'.format(thingTypeName, _change_desc) - ret['result'] = None + _change_desc = "deprecated and removed" + ret["comment"] = "Thing Type {0} is set to be {1}.".format( + thingTypeName, _change_desc + ) + ret["result"] = None return ret # initialize a delete_wait_timer to be 5 minutes @@ -244,27 +268,31 @@ def thing_type_absent(name, thingTypeName, _delete_wait_timer = 300 if _deprecated is False: - _deprecate = __salt__['boto_iot.deprecate_thing_type']( + _deprecate = __salt__["boto_iot.deprecate_thing_type"]( thingTypeName=thingTypeName, undoDeprecate=False, - region=region, key=key, keyid=keyid, profile=profile + region=region, + key=key, + keyid=keyid, + profile=profile, ) - if 'error' in _deprecate: - ret['result'] = False - ret['comment'] = 'Failed to deprecate thing type: {0}.'.format(_deprecate['error']['message']) + if "error" in _deprecate: + ret["result"] = False + ret["comment"] = "Failed to deprecate thing type: {0}.".format( + _deprecate["error"]["message"] + ) return ret else: # grab the deprecation date string from _thing_type_metadata - _deprecation_date_str = _thing_type_metadata.get('deprecationDate') + _deprecation_date_str = _thing_type_metadata.get("deprecationDate") if _deprecation_date_str: # see if we can wait less than 5 minutes - _tz_index = _deprecation_date_str.find('+') + _tz_index = _deprecation_date_str.find("+") if _tz_index != -1: _deprecation_date_str = _deprecation_date_str[:_tz_index] _deprecation_date = datetime.datetime.strptime( - _deprecation_date_str, - "%Y-%m-%d %H:%M:%S.%f" + _deprecation_date_str, "%Y-%m-%d %H:%M:%S.%f" ) _elapsed_time_delta = datetime.datetime.utcnow() - _deprecation_date @@ -276,29 +304,36 @@ def thing_type_absent(name, thingTypeName, # wait required 5 minutes since deprecation time if _delete_wait_timer: log.warning( - 'wait for %s seconds per AWS (5 minutes after deprecation time) ' - 'before we can delete iot thing type', _delete_wait_timer + "wait for %s seconds per AWS (5 minutes after deprecation time) " + "before we can delete iot thing type", + _delete_wait_timer, ) time.sleep(_delete_wait_timer) # delete thing type - r = __salt__['boto_iot.delete_thing_type']( - thingTypeName=thingTypeName, - region=region, key=key, keyid=keyid, profile=profile + r = __salt__["boto_iot.delete_thing_type"]( + thingTypeName=thingTypeName, + region=region, + key=key, + keyid=keyid, + profile=profile, ) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete thing type: {0}.'.format(r['error']['message']) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete thing type: {0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = _describe - ret['changes']['new'] = {'thing_type': None} - ret['comment'] = 'Thing Type {0} deleted.'.format(thingTypeName) + ret["changes"]["old"] = _describe + ret["changes"]["new"] = {"thing_type": None} + ret["comment"] = "Thing Type {0} deleted.".format(thingTypeName) return ret -def policy_present(name, policyName, policyDocument, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_present( + name, policyName, policyDocument, region=None, key=None, keyid=None, profile=None +): + """ Ensure policy exists. name @@ -324,90 +359,108 @@ def policy_present(name, policyName, policyDocument, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': policyName, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": policyName, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_iot.policy_exists'](policyName=policyName, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_iot.policy_exists"]( + policyName=policyName, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create policy: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create policy: {0}.".format(r["error"]["message"]) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'Policy {0} is set to be created.'.format(policyName) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "Policy {0} is set to be created.".format(policyName) + ret["result"] = None return ret - r = __salt__['boto_iot.create_policy'](policyName=policyName, - policyDocument=policyDocument, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create policy: {0}.'.format(r['error']['message']) + r = __salt__["boto_iot.create_policy"]( + policyName=policyName, + policyDocument=policyDocument, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create policy: {0}.".format( + r["error"]["message"] + ) return ret - _describe = __salt__['boto_iot.describe_policy'](policyName, - region=region, key=key, keyid=keyid, profile=profile) - ret['changes']['old'] = {'policy': None} - ret['changes']['new'] = _describe - ret['comment'] = 'Policy {0} created.'.format(policyName) + _describe = __salt__["boto_iot.describe_policy"]( + policyName, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["old"] = {"policy": None} + ret["changes"]["new"] = _describe + ret["comment"] = "Policy {0} created.".format(policyName) return ret - ret['comment'] = os.linesep.join([ret['comment'], 'Policy {0} is present.'.format(policyName)]) - ret['changes'] = {} + ret["comment"] = os.linesep.join( + [ret["comment"], "Policy {0} is present.".format(policyName)] + ) + ret["changes"] = {} # policy exists, ensure config matches - _describe = __salt__['boto_iot.describe_policy'](policyName=policyName, - region=region, key=key, keyid=keyid, profile=profile)['policy'] + _describe = __salt__["boto_iot.describe_policy"]( + policyName=policyName, region=region, key=key, keyid=keyid, profile=profile + )["policy"] - if isinstance(_describe['policyDocument'], six.string_types): - describeDict = salt.utils.json.loads(_describe['policyDocument']) + if isinstance(_describe["policyDocument"], six.string_types): + describeDict = salt.utils.json.loads(_describe["policyDocument"]) else: - describeDict = _describe['policyDocument'] + describeDict = _describe["policyDocument"] if isinstance(policyDocument, six.string_types): policyDocument = salt.utils.json.loads(policyDocument) r = salt.utils.data.compare_dicts(describeDict, policyDocument) if bool(r): - if __opts__['test']: - msg = 'Policy {0} set to be modified.'.format(policyName) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Policy {0} set to be modified.".format(policyName) + ret["comment"] = msg + ret["result"] = None return ret - ret['comment'] = os.linesep.join([ret['comment'], 'Policy to be modified']) + ret["comment"] = os.linesep.join([ret["comment"], "Policy to be modified"]) policyDocument = salt.utils.json.dumps(policyDocument) - r = __salt__['boto_iot.create_policy_version'](policyName=policyName, - policyDocument=policyDocument, - setAsDefault=True, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to update policy: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_iot.create_policy_version"]( + policyName=policyName, + policyDocument=policyDocument, + setAsDefault=True, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to update policy: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret - __salt__['boto_iot.delete_policy_version'](policyName=policyName, - policyVersionId=_describe['defaultVersionId'], - region=region, key=key, - keyid=keyid, profile=profile) + __salt__["boto_iot.delete_policy_version"]( + policyName=policyName, + policyVersionId=_describe["defaultVersionId"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) - ret['changes'].setdefault('new', {})['policyDocument'] = policyDocument - ret['changes'].setdefault('old', {})['policyDocument'] = _describe['policyDocument'] + ret["changes"].setdefault("new", {})["policyDocument"] = policyDocument + ret["changes"].setdefault("old", {})["policyDocument"] = _describe[ + "policyDocument" + ] return ret -def policy_absent(name, policyName, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_absent(name, policyName, region=None, key=None, keyid=None, profile=None): + """ Ensure policy with passed properties is absent. name @@ -428,44 +481,47 @@ def policy_absent(name, policyName, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': policyName, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": policyName, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_iot.policy_exists'](policyName, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete policy: {0}.'.format(r['error']['message']) + r = __salt__["boto_iot.policy_exists"]( + policyName, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete policy: {0}.".format(r["error"]["message"]) return ret - if r and not r['exists']: - ret['comment'] = 'Policy {0} does not exist.'.format(policyName) + if r and not r["exists"]: + ret["comment"] = "Policy {0} does not exist.".format(policyName) return ret - if __opts__['test']: - ret['comment'] = 'Policy {0} is set to be removed.'.format(policyName) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Policy {0} is set to be removed.".format(policyName) + ret["result"] = None return ret # delete non-default versions - versions = __salt__['boto_iot.list_policy_versions'](policyName, - region=region, key=key, - keyid=keyid, profile=profile) + versions = __salt__["boto_iot.list_policy_versions"]( + policyName, region=region, key=key, keyid=keyid, profile=profile + ) if versions: - for version in versions.get('policyVersions', []): - if version.get('isDefaultVersion', False): + for version in versions.get("policyVersions", []): + if version.get("isDefaultVersion", False): continue - r = __salt__['boto_iot.delete_policy_version'](policyName, - policyVersionId=version.get('versionId'), - region=region, key=key, - keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete policy: {0}.'.format(r['error']['message']) + r = __salt__["boto_iot.delete_policy_version"]( + policyName, + policyVersionId=version.get("versionId"), + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete policy: {0}.".format( + r["error"]["message"] + ) return ret # For the delete to succeed, the policy must be detached from any # principals. However, no API is provided to list the principals to which it @@ -474,22 +530,23 @@ def policy_absent(name, policyName, # detached. # delete policy - r = __salt__['boto_iot.delete_policy'](policyName, - region=region, key=key, - keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete policy: {0}.'.format(r['error']['message']) + r = __salt__["boto_iot.delete_policy"]( + policyName, region=region, key=key, keyid=keyid, profile=profile + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete policy: {0}.".format(r["error"]["message"]) return ret - ret['changes']['old'] = {'policy': policyName} - ret['changes']['new'] = {'policy': None} - ret['comment'] = 'Policy {0} deleted.'.format(policyName) + ret["changes"]["old"] = {"policy": policyName} + ret["changes"]["new"] = {"policy": None} + ret["comment"] = "Policy {0} deleted.".format(policyName) return ret -def policy_attached(name, policyName, principal, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_attached( + name, policyName, principal, region=None, key=None, keyid=None, profile=None +): + """ Ensure policy is attached to the given principal. name @@ -513,53 +570,61 @@ def policy_attached(name, policyName, principal, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': policyName, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": policyName, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_iot.list_principal_policies'](principal=principal, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_iot.list_principal_policies"]( + principal=principal, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to attach policy: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to attach policy: {0}.".format(r["error"]["message"]) return ret attached = False - for policy in r.get('policies', []): - if policy.get('policyName') == policyName: + for policy in r.get("policies", []): + if policy.get("policyName") == policyName: attached = True break if not attached: - if __opts__['test']: - ret['comment'] = 'Policy {0} is set to be attached to {1}.'.format(policyName, principal) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Policy {0} is set to be attached to {1}.".format( + policyName, principal + ) + ret["result"] = None return ret - r = __salt__['boto_iot.attach_principal_policy'](policyName=policyName, - principal=principal, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('attached'): - ret['result'] = False - ret['comment'] = 'Failed to attach policy: {0}.'.format(r['error']['message']) + r = __salt__["boto_iot.attach_principal_policy"]( + policyName=policyName, + principal=principal, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("attached"): + ret["result"] = False + ret["comment"] = "Failed to attach policy: {0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'attached': False} - ret['changes']['new'] = {'attached': True} - ret['comment'] = 'Policy {0} attached to {1}.'.format(policyName, principal) + ret["changes"]["old"] = {"attached": False} + ret["changes"]["new"] = {"attached": True} + ret["comment"] = "Policy {0} attached to {1}.".format(policyName, principal) return ret - ret['comment'] = os.linesep.join([ret['comment'], 'Policy {0} is attached.'.format(policyName)]) - ret['changes'] = {} + ret["comment"] = os.linesep.join( + [ret["comment"], "Policy {0} is attached.".format(policyName)] + ) + ret["changes"] = {} return ret -def policy_detached(name, policyName, principal, - region=None, key=None, keyid=None, profile=None): - ''' +def policy_detached( + name, policyName, principal, region=None, key=None, keyid=None, profile=None +): + """ Ensure policy is attached to the given principal. name @@ -583,55 +648,71 @@ def policy_detached(name, policyName, principal, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': policyName, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": policyName, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_iot.list_principal_policies'](principal=principal, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_iot.list_principal_policies"]( + principal=principal, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to detached policy: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to detached policy: {0}.".format(r["error"]["message"]) return ret attached = False - for policy in r.get('policies', []): - if policy.get('policyName') == policyName: + for policy in r.get("policies", []): + if policy.get("policyName") == policyName: attached = True break if attached: - if __opts__['test']: - ret['comment'] = 'Policy {0} is set to be detached from {1}.'.format(policyName, principal) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Policy {0} is set to be detached from {1}.".format( + policyName, principal + ) + ret["result"] = None return ret - r = __salt__['boto_iot.detach_principal_policy'](policyName=policyName, - principal=principal, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('detached'): - ret['result'] = False - ret['comment'] = 'Failed to detach policy: {0}.'.format(r['error']['message']) + r = __salt__["boto_iot.detach_principal_policy"]( + policyName=policyName, + principal=principal, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("detached"): + ret["result"] = False + ret["comment"] = "Failed to detach policy: {0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'attached': True} - ret['changes']['new'] = {'attached': False} - ret['comment'] = 'Policy {0} detached from {1}.'.format(policyName, principal) + ret["changes"]["old"] = {"attached": True} + ret["changes"]["new"] = {"attached": False} + ret["comment"] = "Policy {0} detached from {1}.".format(policyName, principal) return ret - ret['comment'] = os.linesep.join([ret['comment'], 'Policy {0} is detached.'.format(policyName)]) - ret['changes'] = {} + ret["comment"] = os.linesep.join( + [ret["comment"], "Policy {0} is detached.".format(policyName)] + ) + ret["changes"] = {} return ret -def topic_rule_present(name, ruleName, sql, actions, description='', - ruleDisabled=False, - region=None, key=None, keyid=None, profile=None): - ''' +def topic_rule_present( + name, + ruleName, + sql, + actions, + description="", + ruleDisabled=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure topic rule exists. name @@ -664,91 +745,99 @@ def topic_rule_present(name, ruleName, sql, actions, description='', profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': ruleName, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": ruleName, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_iot.topic_rule_exists'](ruleName=ruleName, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_iot.topic_rule_exists"]( + ruleName=ruleName, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create rule: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create rule: {0}.".format(r["error"]["message"]) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'Rule {0} is set to be created.'.format(ruleName) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "Rule {0} is set to be created.".format(ruleName) + ret["result"] = None return ret - r = __salt__['boto_iot.create_topic_rule'](ruleName=ruleName, - sql=sql, - actions=actions, - description=description, - ruleDisabled=ruleDisabled, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create rule: {0}.'.format(r['error']['message']) + r = __salt__["boto_iot.create_topic_rule"]( + ruleName=ruleName, + sql=sql, + actions=actions, + description=description, + ruleDisabled=ruleDisabled, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create rule: {0}.".format(r["error"]["message"]) return ret - _describe = __salt__['boto_iot.describe_topic_rule'](ruleName, - region=region, key=key, keyid=keyid, profile=profile) - ret['changes']['old'] = {'rule': None} - ret['changes']['new'] = _describe - ret['comment'] = 'Rule {0} created.'.format(ruleName) + _describe = __salt__["boto_iot.describe_topic_rule"]( + ruleName, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["old"] = {"rule": None} + ret["changes"]["new"] = _describe + ret["comment"] = "Rule {0} created.".format(ruleName) return ret - ret['comment'] = os.linesep.join([ret['comment'], 'Rule {0} is present.'.format(ruleName)]) - ret['changes'] = {} + ret["comment"] = os.linesep.join( + [ret["comment"], "Rule {0} is present.".format(ruleName)] + ) + ret["changes"] = {} # policy exists, ensure config matches - _describe = __salt__['boto_iot.describe_topic_rule'](ruleName=ruleName, - region=region, key=key, keyid=keyid, profile=profile)['rule'] + _describe = __salt__["boto_iot.describe_topic_rule"]( + ruleName=ruleName, region=region, key=key, keyid=keyid, profile=profile + )["rule"] if isinstance(actions, six.string_types): actions = salt.utils.json.loads(actions) need_update = False # cmp() function is deprecated in Python 3: use the following as a substitute for 'r'. - r = (_describe['actions'] > actions) - (_describe['actions'] < actions) + r = (_describe["actions"] > actions) - (_describe["actions"] < actions) if bool(r): need_update = True - ret['changes'].setdefault('new', {})['actions'] = actions - ret['changes'].setdefault('old', {})['actions'] = _describe['actions'] + ret["changes"].setdefault("new", {})["actions"] = actions + ret["changes"].setdefault("old", {})["actions"] = _describe["actions"] - for var in ('sql', 'description', 'ruleDisabled'): + for var in ("sql", "description", "ruleDisabled"): if _describe[var] != locals()[var]: need_update = True - ret['changes'].setdefault('new', {})[var] = locals()[var] - ret['changes'].setdefault('old', {})[var] = _describe[var] + ret["changes"].setdefault("new", {})[var] = locals()[var] + ret["changes"].setdefault("old", {})[var] = _describe[var] if need_update: - if __opts__['test']: - msg = 'Rule {0} set to be modified.'.format(ruleName) - ret['changes'] = {} - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Rule {0} set to be modified.".format(ruleName) + ret["changes"] = {} + ret["comment"] = msg + ret["result"] = None return ret - ret['comment'] = os.linesep.join([ret['comment'], 'Rule to be modified']) - r = __salt__['boto_iot.replace_topic_rule'](ruleName=ruleName, - sql=sql, - actions=actions, - description=description, - ruleDisabled=ruleDisabled, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('replaced'): - ret['result'] = False - ret['comment'] = 'Failed to update rule: {0}.'.format(r['error']['message']) - ret['changes'] = {} + ret["comment"] = os.linesep.join([ret["comment"], "Rule to be modified"]) + r = __salt__["boto_iot.replace_topic_rule"]( + ruleName=ruleName, + sql=sql, + actions=actions, + description=description, + ruleDisabled=ruleDisabled, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("replaced"): + ret["result"] = False + ret["comment"] = "Failed to update rule: {0}.".format(r["error"]["message"]) + ret["changes"] = {} return ret -def topic_rule_absent(name, ruleName, - region=None, key=None, keyid=None, profile=None): - ''' +def topic_rule_absent(name, ruleName, region=None, key=None, keyid=None, profile=None): + """ Ensure topic rule with passed properties is absent. name @@ -769,37 +858,34 @@ def topic_rule_absent(name, ruleName, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': ruleName, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": ruleName, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_iot.topic_rule_exists'](ruleName, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete rule: {0}.'.format(r['error']['message']) + r = __salt__["boto_iot.topic_rule_exists"]( + ruleName, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete rule: {0}.".format(r["error"]["message"]) return ret - if r and not r['exists']: - ret['comment'] = 'Rule {0} does not exist.'.format(ruleName) + if r and not r["exists"]: + ret["comment"] = "Rule {0} does not exist.".format(ruleName) return ret - if __opts__['test']: - ret['comment'] = 'Rule {0} is set to be removed.'.format(ruleName) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Rule {0} is set to be removed.".format(ruleName) + ret["result"] = None return ret - r = __salt__['boto_iot.delete_topic_rule'](ruleName, - region=region, key=key, - keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete rule: {0}.'.format(r['error']['message']) + r = __salt__["boto_iot.delete_topic_rule"]( + ruleName, region=region, key=key, keyid=keyid, profile=profile + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete rule: {0}.".format(r["error"]["message"]) return ret - ret['changes']['old'] = {'rule': ruleName} - ret['changes']['new'] = {'rule': None} - ret['comment'] = 'Rule {0} deleted.'.format(ruleName) + ret["changes"]["old"] = {"rule": ruleName} + ret["changes"]["new"] = {"rule": None} + ret["comment"] = "Rule {0} deleted.".format(ruleName) return ret diff --git a/salt/states/boto_kinesis.py b/salt/states/boto_kinesis.py index 87a5952bb89..453c0b82ac7 100644 --- a/salt/states/boto_kinesis.py +++ b/salt/states/boto_kinesis.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Kinesis Streams ====================== @@ -54,37 +54,44 @@ pillars or minion config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs - region: us-east-1 -''' -# Keep pylint from chocking on ret +""" + # pylint: disable=undefined-variable # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +# Keep pylint from chocking on ret import logging log = logging.getLogger(__name__) -__virtualname__ = 'boto_kinesis' +__virtualname__ = "boto_kinesis" def __virtual__(): - ''' + """ Only load if boto_kinesis is available. - ''' - if 'boto_kinesis.exists' in __salt__: + """ + if "boto_kinesis.exists" in __salt__: return __virtualname__ - return False, 'The boto_kinesis module could not be loaded: boto libraries not found.' + return ( + False, + "The boto_kinesis module could not be loaded: boto libraries not found.", + ) -def present(name, - retention_hours=None, - enhanced_monitoring=None, - num_shards=None, - do_reshard=True, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def present( + name, + retention_hours=None, + enhanced_monitoring=None, + num_shards=None, + do_reshard=True, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the kinesis stream is properly configured and scaled. name (string) @@ -123,145 +130,141 @@ def present(name, profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} comments = [] changes_old = {} changes_new = {} # Ensure stream exists - exists = __salt__['boto_kinesis.exists']( - name, - region, - key, - keyid, - profile - ) - if exists['result'] is False: - if __opts__['test']: - ret['result'] = None - comments.append('Kinesis stream {0} would be created'.format(name)) + exists = __salt__["boto_kinesis.exists"](name, region, key, keyid, profile) + if exists["result"] is False: + if __opts__["test"]: + ret["result"] = None + comments.append("Kinesis stream {0} would be created".format(name)) _add_changes(ret, changes_old, changes_new, comments) return ret else: - is_created = __salt__['boto_kinesis.create_stream']( - name, - num_shards, - region, - key, - keyid, - profile + is_created = __salt__["boto_kinesis.create_stream"]( + name, num_shards, region, key, keyid, profile ) - if 'error' in is_created: - ret['result'] = False - comments.append('Failed to create stream {0}: {1}'.format(name, is_created['error'])) + if "error" in is_created: + ret["result"] = False + comments.append( + "Failed to create stream {0}: {1}".format(name, is_created["error"]) + ) _add_changes(ret, changes_old, changes_new, comments) return ret - comments.append('Kinesis stream {0} successfully created'.format(name)) - changes_new['name'] = name - changes_new['num_shards'] = num_shards + comments.append("Kinesis stream {0} successfully created".format(name)) + changes_new["name"] = name + changes_new["num_shards"] = num_shards else: - comments.append('Kinesis stream {0} already exists'.format(name)) + comments.append("Kinesis stream {0} already exists".format(name)) - stream_response = __salt__['boto_kinesis.get_stream_when_active']( - name, - region, - key, - keyid, - profile + stream_response = __salt__["boto_kinesis.get_stream_when_active"]( + name, region, key, keyid, profile ) - if 'error' in stream_response: - ret['result'] = False - comments.append('Kinesis stream {0}: error getting description: {1}' - .format(name, stream_response['error'])) + if "error" in stream_response: + ret["result"] = False + comments.append( + "Kinesis stream {0}: error getting description: {1}".format( + name, stream_response["error"] + ) + ) _add_changes(ret, changes_old, changes_new, comments) return ret - stream_details = stream_response['result']["StreamDescription"] + stream_details = stream_response["result"]["StreamDescription"] # Configure retention hours if retention_hours is not None: old_retention_hours = stream_details["RetentionPeriodHours"] - retention_matches = (old_retention_hours == retention_hours) + retention_matches = old_retention_hours == retention_hours if not retention_matches: - if __opts__['test']: - ret['result'] = None - comments.append('Kinesis stream {0}: retention hours would be updated to {1}' - .format(name, retention_hours)) + if __opts__["test"]: + ret["result"] = None + comments.append( + "Kinesis stream {0}: retention hours would be updated to {1}".format( + name, retention_hours + ) + ) else: if old_retention_hours > retention_hours: - retention_updated = __salt__['boto_kinesis.decrease_stream_retention_period']( - name, - retention_hours, - region, - key, - keyid, - profile - ) + retention_updated = __salt__[ + "boto_kinesis.decrease_stream_retention_period" + ](name, retention_hours, region, key, keyid, profile) else: - retention_updated = __salt__['boto_kinesis.increase_stream_retention_period']( - name, - retention_hours, - region, - key, - keyid, - profile - ) + retention_updated = __salt__[ + "boto_kinesis.increase_stream_retention_period" + ](name, retention_hours, region, key, keyid, profile) - if 'error' in retention_updated: - ret['result'] = False - comments.append('Kinesis stream {0}: failed to update retention hours: {1}' - .format(name, retention_updated['error'])) + if "error" in retention_updated: + ret["result"] = False + comments.append( + "Kinesis stream {0}: failed to update retention hours: {1}".format( + name, retention_updated["error"] + ) + ) _add_changes(ret, changes_old, changes_new, comments) return ret - comments.append('Kinesis stream {0}: retention hours was successfully updated'.format(name)) - changes_old['retention_hours'] = old_retention_hours - changes_new['retention_hours'] = retention_hours + comments.append( + "Kinesis stream {0}: retention hours was successfully updated".format( + name + ) + ) + changes_old["retention_hours"] = old_retention_hours + changes_new["retention_hours"] = retention_hours # wait until active again, otherwise it will log a lot of ResourceInUseExceptions # note that this isn't required below; reshard() will itself handle waiting - stream_response = __salt__['boto_kinesis.get_stream_when_active']( - name, - region, - key, - keyid, - profile + stream_response = __salt__["boto_kinesis.get_stream_when_active"]( + name, region, key, keyid, profile ) - if 'error' in stream_response: - ret['result'] = False - comments.append('Kinesis stream {0}: error getting description: {1}' - .format(name, stream_response['error'])) + if "error" in stream_response: + ret["result"] = False + comments.append( + "Kinesis stream {0}: error getting description: {1}".format( + name, stream_response["error"] + ) + ) _add_changes(ret, changes_old, changes_new, comments) return ret - stream_details = stream_response['result']["StreamDescription"] + stream_details = stream_response["result"]["StreamDescription"] else: - comments.append('Kinesis stream {0}: retention hours did not require change, already set at {1}' - .format(name, old_retention_hours)) + comments.append( + "Kinesis stream {0}: retention hours did not require change, already set at {1}".format( + name, old_retention_hours + ) + ) else: - comments.append('Kinesis stream {0}: did not configure retention hours'.format(name)) + comments.append( + "Kinesis stream {0}: did not configure retention hours".format(name) + ) # Configure enhanced monitoring if enhanced_monitoring is not None: - if enhanced_monitoring is True or enhanced_monitoring == ['ALL']: + if enhanced_monitoring is True or enhanced_monitoring == ["ALL"]: # for ease of comparison; describe_stream will always return the full list of metrics, never 'ALL' enhanced_monitoring = [ - "IncomingBytes", - "OutgoingRecords", - "IteratorAgeMilliseconds", - "IncomingRecords", - "ReadProvisionedThroughputExceeded", - "WriteProvisionedThroughputExceeded", - "OutgoingBytes" - ] + "IncomingBytes", + "OutgoingRecords", + "IteratorAgeMilliseconds", + "IncomingRecords", + "ReadProvisionedThroughputExceeded", + "WriteProvisionedThroughputExceeded", + "OutgoingBytes", + ] elif enhanced_monitoring is False or enhanced_monitoring == "None": enhanced_monitoring = [] - old_enhanced_monitoring = stream_details.get("EnhancedMonitoring")[0]["ShardLevelMetrics"] + old_enhanced_monitoring = stream_details.get("EnhancedMonitoring")[0][ + "ShardLevelMetrics" + ] new_monitoring_set = set(enhanced_monitoring) old_monitoring_set = set(old_enhanced_monitoring) @@ -271,125 +274,155 @@ def present(name, disable_metrics = list(old_monitoring_set.difference(matching_metrics)) if len(enable_metrics) != 0: - if __opts__['test']: - ret['result'] = None - comments.append('Kinesis stream {0}: would enable enhanced monitoring for {1}' - .format(name, enable_metrics)) + if __opts__["test"]: + ret["result"] = None + comments.append( + "Kinesis stream {0}: would enable enhanced monitoring for {1}".format( + name, enable_metrics + ) + ) else: - metrics_enabled = __salt__['boto_kinesis.enable_enhanced_monitoring']( - name, - enable_metrics, - region, - key, - keyid, - profile + metrics_enabled = __salt__["boto_kinesis.enable_enhanced_monitoring"]( + name, enable_metrics, region, key, keyid, profile ) - if 'error' in metrics_enabled: - ret['result'] = False - comments.append('Kinesis stream {0}: failed to enable enhanced monitoring: {1}' - .format(name, metrics_enabled['error'])) + if "error" in metrics_enabled: + ret["result"] = False + comments.append( + "Kinesis stream {0}: failed to enable enhanced monitoring: {1}".format( + name, metrics_enabled["error"] + ) + ) _add_changes(ret, changes_old, changes_new, comments) return ret - comments.append('Kinesis stream {0}: enhanced monitoring was enabled for shard-level metrics {1}' - .format(name, enable_metrics)) + comments.append( + "Kinesis stream {0}: enhanced monitoring was enabled for shard-level metrics {1}".format( + name, enable_metrics + ) + ) if len(disable_metrics) != 0: - if __opts__['test']: - ret['result'] = None - comments.append('Kinesis stream {0}: would disable enhanced monitoring for {1}' - .format(name, disable_metrics)) + if __opts__["test"]: + ret["result"] = None + comments.append( + "Kinesis stream {0}: would disable enhanced monitoring for {1}".format( + name, disable_metrics + ) + ) else: - metrics_disabled = __salt__['boto_kinesis.disable_enhanced_monitoring']( - name, - disable_metrics, - region, - key, - keyid, - profile + metrics_disabled = __salt__["boto_kinesis.disable_enhanced_monitoring"]( + name, disable_metrics, region, key, keyid, profile ) - if 'error' in metrics_disabled: - ret['result'] = False - comments.append('Kinesis stream {0}: failed to disable enhanced monitoring: {1}' - .format(name, metrics_disabled['error'])) + if "error" in metrics_disabled: + ret["result"] = False + comments.append( + "Kinesis stream {0}: failed to disable enhanced monitoring: {1}".format( + name, metrics_disabled["error"] + ) + ) _add_changes(ret, changes_old, changes_new, comments) return ret - comments.append('Kinesis stream {0}: enhanced monitoring was disabled for shard-level metrics {1}' - .format(name, disable_metrics)) + comments.append( + "Kinesis stream {0}: enhanced monitoring was disabled for shard-level metrics {1}".format( + name, disable_metrics + ) + ) if len(disable_metrics) == 0 and len(enable_metrics) == 0: - comments.append('Kinesis stream {0}: enhanced monitoring did not require change, already set at {1}' - .format(name, (old_enhanced_monitoring if len(old_enhanced_monitoring) > 0 else "None"))) - elif not __opts__['test']: - changes_old['enhanced_monitoring'] = (old_enhanced_monitoring if len(old_enhanced_monitoring) > 0 - else "None") - changes_new['enhanced_monitoring'] = (enhanced_monitoring if len(enhanced_monitoring) > 0 - else "None") + comments.append( + "Kinesis stream {0}: enhanced monitoring did not require change, already set at {1}".format( + name, + ( + old_enhanced_monitoring + if len(old_enhanced_monitoring) > 0 + else "None" + ), + ) + ) + elif not __opts__["test"]: + changes_old["enhanced_monitoring"] = ( + old_enhanced_monitoring if len(old_enhanced_monitoring) > 0 else "None" + ) + changes_new["enhanced_monitoring"] = ( + enhanced_monitoring if len(enhanced_monitoring) > 0 else "None" + ) else: - comments.append('Kinesis stream {0}: did not configure enhanced monitoring'.format(name)) + comments.append( + "Kinesis stream {0}: did not configure enhanced monitoring".format(name) + ) # Reshard stream if necessary - min_hash_key, max_hash_key, full_stream_details = __salt__['boto_kinesis.get_info_for_reshard']( - stream_details - ) + min_hash_key, max_hash_key, full_stream_details = __salt__[ + "boto_kinesis.get_info_for_reshard" + ](stream_details) old_num_shards = len(full_stream_details["OpenShards"]) if num_shards is not None and do_reshard: - num_shards_matches = (old_num_shards == num_shards) + num_shards_matches = old_num_shards == num_shards if not num_shards_matches: - if __opts__['test']: - ret['result'] = None - comments.append('Kinesis stream {0}: would be resharded from {1} to {2} shards' - .format(name, old_num_shards, num_shards)) + if __opts__["test"]: + ret["result"] = None + comments.append( + "Kinesis stream {0}: would be resharded from {1} to {2} shards".format( + name, old_num_shards, num_shards + ) + ) else: log.info( - 'Resharding stream from %s to %s shards, this could take ' - 'a while', old_num_shards, num_shards + "Resharding stream from %s to %s shards, this could take " + "a while", + old_num_shards, + num_shards, ) # reshard returns True when a split/merge action is taken, # or False when no more actions are required continue_reshard = True while continue_reshard: - reshard_response = __salt__['boto_kinesis.reshard']( - name, - num_shards, - do_reshard, - region, - key, - keyid, - profile) + reshard_response = __salt__["boto_kinesis.reshard"]( + name, num_shards, do_reshard, region, key, keyid, profile + ) - if 'error' in reshard_response: - ret['result'] = False - comments.append('Encountered error while resharding {0}: {1}' - .format(name, reshard_response['error'])) + if "error" in reshard_response: + ret["result"] = False + comments.append( + "Encountered error while resharding {0}: {1}".format( + name, reshard_response["error"] + ) + ) _add_changes(ret, changes_old, changes_new, comments) return ret - continue_reshard = reshard_response['result'] + continue_reshard = reshard_response["result"] - comments.append('Kinesis stream {0}: successfully resharded to {1} shards'.format(name, num_shards)) - changes_old['num_shards'] = old_num_shards - changes_new['num_shards'] = num_shards + comments.append( + "Kinesis stream {0}: successfully resharded to {1} shards".format( + name, num_shards + ) + ) + changes_old["num_shards"] = old_num_shards + changes_new["num_shards"] = num_shards else: - comments.append('Kinesis stream {0}: did not require resharding, remains at {1} shards' - .format(name, old_num_shards)) + comments.append( + "Kinesis stream {0}: did not require resharding, remains at {1} shards".format( + name, old_num_shards + ) + ) else: - comments.append('Kinesis stream {0}: did not reshard, remains at {1} shards'.format(name, old_num_shards)) + comments.append( + "Kinesis stream {0}: did not reshard, remains at {1} shards".format( + name, old_num_shards + ) + ) _add_changes(ret, changes_old, changes_new, comments) return ret -def absent(name, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def absent(name, region=None, key=None, keyid=None, profile=None): + """ Delete the kinesis stream, if it exists. name (string) @@ -407,46 +440,38 @@ def absent(name, profile (dict) A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - exists = __salt__['boto_kinesis.exists']( - name, - region, - key, - keyid, - profile - ) - if exists['result'] is False: - ret['comment'] = 'Kinesis stream {0} does not exist'.format(name) + exists = __salt__["boto_kinesis.exists"](name, region, key, keyid, profile) + if exists["result"] is False: + ret["comment"] = "Kinesis stream {0} does not exist".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Kinesis stream {0} would be deleted'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Kinesis stream {0} would be deleted".format(name) + ret["result"] = None return ret - is_deleted = __salt__['boto_kinesis.delete_stream']( - name, - region, - key, - keyid, - profile + is_deleted = __salt__["boto_kinesis.delete_stream"]( + name, region, key, keyid, profile ) - if 'error' in is_deleted: - ret['comment'] = 'Failed to delete stream {0}: {1}'.format(name, is_deleted['error']) - ret['result'] = False + if "error" in is_deleted: + ret["comment"] = "Failed to delete stream {0}: {1}".format( + name, is_deleted["error"] + ) + ret["result"] = False else: - ret['comment'] = 'Deleted stream {0}'.format(name) - ret['changes'].setdefault('old', 'Stream {0} exists'.format(name)) - ret['changes'].setdefault('new', 'Stream {0} deleted'.format(name)) + ret["comment"] = "Deleted stream {0}".format(name) + ret["changes"].setdefault("old", "Stream {0} exists".format(name)) + ret["changes"].setdefault("new", "Stream {0} deleted".format(name)) return ret def _add_changes(ret, changes_old, changes_new, comments): - ret['comment'] = ',\n'.join(comments) + ret["comment"] = ",\n".join(comments) if changes_old: - ret['changes']['old'] = changes_old + ret["changes"]["old"] = changes_old if changes_new: - ret['changes']['new'] = changes_new + ret["changes"]["new"] = changes_new diff --git a/salt/states/boto_kms.py b/salt/states/boto_kms.py index 62ead7e86a5..b5e328cdc52 100644 --- a/salt/states/boto_kms.py +++ b/salt/states/boto_kms.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage KMS keys, key policies and grants. .. versionadded:: 2015.8.0 @@ -55,33 +55,37 @@ config: - profile: keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import salt.utils.dictupdate as dictupdate from salt.exceptions import SaltInvocationError def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_kms' if 'boto_kms.describe_key' in __salt__ else False + """ + if "boto_kms.describe_key" in __salt__: + return "boto_kms" + return (False, "boto_kms module could not be loaded") def key_present( - name, - policy, - description=None, - key_usage=None, - grants=None, - manage_grants=False, - key_rotation=False, - enabled=True, - region=None, - key=None, - keyid=None, - profile=None): - ''' + name, + policy, + description=None, + key_usage=None, + grants=None, + manage_grants=False, + key_rotation=False, + enabled=True, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the KMS key exists. KMS keys can not be deleted, so this function must be used to ensure the key is enabled or disabled. @@ -123,35 +127,21 @@ def key_present( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ if not policy: - raise SaltInvocationError('policy is a required argument.') + raise SaltInvocationError("policy is a required argument.") if grants and not isinstance(grants, list): - raise SaltInvocationError('manage_grants must be a list.') + raise SaltInvocationError("manage_grants must be a list.") if not isinstance(manage_grants, bool): - raise SaltInvocationError('manage_grants must be true or false.') + raise SaltInvocationError("manage_grants must be true or false.") if not isinstance(key_rotation, bool): - raise SaltInvocationError('key_rotation must be true or false.') + raise SaltInvocationError("key_rotation must be true or false.") if not isinstance(enabled, bool): - raise SaltInvocationError('enabled must be true or false.') + raise SaltInvocationError("enabled must be true or false.") # TODO: support grant from pillars. # TODO: support key policy from pillars. - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} _ret = _key_present( - name, policy, description, key_usage, key_rotation, enabled, region, - key, keyid, profile - ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: - return ret - # TODO: add grants_present function - return ret - - -def _key_present( name, policy, description, @@ -161,205 +151,222 @@ def _key_present( region, key, keyid, - profile): - ret = {'result': True, 'comment': '', 'changes': {}} - alias = 'alias/{0}'.format(name) - r = __salt__['boto_kms.key_exists'](alias, region, key, keyid, profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Error when attempting to find key: {0}.'.format( - r['error']['message'] + profile, + ) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: + return ret + # TODO: add grants_present function + return ret + + +def _key_present( + name, + policy, + description, + key_usage, + key_rotation, + enabled, + region, + key, + keyid, + profile, +): + ret = {"result": True, "comment": "", "changes": {}} + alias = "alias/{0}".format(name) + r = __salt__["boto_kms.key_exists"](alias, region, key, keyid, profile) + if "error" in r: + ret["result"] = False + ret["comment"] = "Error when attempting to find key: {0}.".format( + r["error"]["message"] ) return ret - if not r['result']: - if __opts__['test']: - ret['comment'] = 'Key is set to be created.' - ret['result'] = None + if not r["result"]: + if __opts__["test"]: + ret["comment"] = "Key is set to be created." + ret["result"] = None return ret - rc = __salt__['boto_kms.create_key']( + rc = __salt__["boto_kms.create_key"]( policy, description, key_usage, region, key, keyid, profile ) - if 'error' in rc: - ret['result'] = False - ret['comment'] = 'Failed to create key: {0}'.format( - rc['error']['message'] - ) + if "error" in rc: + ret["result"] = False + ret["comment"] = "Failed to create key: {0}".format(rc["error"]["message"]) return ret - key_metadata = rc['key_metadata'] - kms_key_id = key_metadata['KeyId'] - rn = __salt__['boto_kms.create_alias']( + key_metadata = rc["key_metadata"] + kms_key_id = key_metadata["KeyId"] + rn = __salt__["boto_kms.create_alias"]( alias, kms_key_id, region, key, keyid, profile ) - if 'error' in rn: + if "error" in rn: # We can't recover from this. KMS only exposes enable/disable # and disable is not necessarily a great action here. AWS sucks # for not including alias in the create_key call. - ret['result'] = False - ret['comment'] = ( - 'Failed to create key alias for key_id {0}. This resource ' - 'will be left dangling. Please clean manually. ' - 'Error: {1}'.format(kms_key_id, rn['error']['message']) + ret["result"] = False + ret["comment"] = ( + "Failed to create key alias for key_id {0}. This resource " + "will be left dangling. Please clean manually. " + "Error: {1}".format(kms_key_id, rn["error"]["message"]) ) return ret - ret['changes']['old'] = {'key': None} - ret['changes']['new'] = {'key': name} - ret['comment'] = 'Key {0} created.'.format(name) + ret["changes"]["old"] = {"key": None} + ret["changes"]["new"] = {"key": name} + ret["comment"] = "Key {0} created.".format(name) else: - rd = __salt__['boto_kms.describe_key']( - alias, region, key, keyid, profile - ) - if 'error' in rd: - ret['result'] = False - ret['comment'] = 'Failed to update key: {0}.'.format( - rd['error']['message'] - ) + rd = __salt__["boto_kms.describe_key"](alias, region, key, keyid, profile) + if "error" in rd: + ret["result"] = False + ret["comment"] = "Failed to update key: {0}.".format(rd["error"]["message"]) return ret - key_metadata = rd['key_metadata'] - _ret = _key_description( - key_metadata, description, region, key, keyid, profile - ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + key_metadata = rd["key_metadata"] + _ret = _key_description(key_metadata, description, region, key, keyid, profile) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - _ret = _key_policy( - key_metadata, policy, region, key, keyid, profile - ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + _ret = _key_policy(key_metadata, policy, region, key, keyid, profile) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret # Actions that need to occur whether creating or updating - _ret = _key_enabled( - key_metadata, enabled, region, key, keyid, profile - ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + _ret = _key_enabled(key_metadata, enabled, region, key, keyid, profile) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - _ret = _key_rotation( - key_metadata, key_rotation, region, key, keyid, profile - ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + _ret = _key_rotation(key_metadata, key_rotation, region, key, keyid, profile) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret def _key_enabled(key_metadata, enabled, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - kms_key_id = key_metadata['KeyId'] - if key_metadata['Enabled'] == enabled: + ret = {"result": True, "comment": "", "changes": {}} + kms_key_id = key_metadata["KeyId"] + if key_metadata["Enabled"] == enabled: return ret - if __opts__['test']: - ret['comment'] = 'Key set to have enabled status updated.' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Key set to have enabled status updated." + ret["result"] = None return ret if enabled: - re = __salt__['boto_kms.enable_key']( - kms_key_id, region, key, keyid, profile - ) - event = 'Enabled' + re = __salt__["boto_kms.enable_key"](kms_key_id, region, key, keyid, profile) + event = "Enabled" else: - re = __salt__['boto_kms.disable_key']( - kms_key_id, region, key, keyid, profile + re = __salt__["boto_kms.disable_key"](kms_key_id, region, key, keyid, profile) + event = "Disabled" + if "error" in re: + ret["result"] = False + ret["comment"] = "Failed to update key enabled status: {0}.".format( + re["error"]["message"] ) - event = 'Disabled' - if 'error' in re: - ret['result'] = False - ret['comment'] = 'Failed to update key enabled status: {0}.'.format(re['error']['message']) else: - ret['comment'] = '{0} key.'.format(event) + ret["comment"] = "{0} key.".format(event) return ret def _key_description(key_metadata, description, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - if key_metadata['Description'] == description: + ret = {"result": True, "comment": "", "changes": {}} + if key_metadata["Description"] == description: return ret - if __opts__['test']: - ret['comment'] = 'Key set to have description updated.' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Key set to have description updated." + ret["result"] = None return ret - rdu = __salt__['boto_kms.update_key_description']( - key_metadata['KeyId'], description, region, key, keyid, profile + rdu = __salt__["boto_kms.update_key_description"]( + key_metadata["KeyId"], description, region, key, keyid, profile ) - if 'error' in rdu: - ret['result'] = False - ret['comment'] = 'Failed to update key description: {0}.'.format(rdu['error']['message']) + if "error" in rdu: + ret["result"] = False + ret["comment"] = "Failed to update key description: {0}.".format( + rdu["error"]["message"] + ) else: - ret['comment'] = 'Updated key description.' + ret["comment"] = "Updated key description." return ret def _key_rotation(key_metadata, key_rotation, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - kms_key_id = key_metadata['KeyId'] - rke = __salt__['boto_kms.get_key_rotation_status']( + ret = {"result": True, "comment": "", "changes": {}} + kms_key_id = key_metadata["KeyId"] + rke = __salt__["boto_kms.get_key_rotation_status"]( kms_key_id, region, key, keyid, profile ) - if rke['result'] == key_rotation: + if rke["result"] == key_rotation: return ret - if __opts__['test']: - ret['comment'] = 'Key set to have key rotation policy updated.' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Key set to have key rotation policy updated." + ret["result"] = None return ret - if not key_metadata['Enabled']: - ret['comment'] = 'Key is disabled, not changing key rotation policy.' - ret['result'] = None + if not key_metadata["Enabled"]: + ret["comment"] = "Key is disabled, not changing key rotation policy." + ret["result"] = None return ret if key_rotation: - rk = __salt__['boto_kms.enable_key_rotation']( + rk = __salt__["boto_kms.enable_key_rotation"]( kms_key_id, region, key, keyid, profile ) else: - rk = __salt__['boto_kms.enable_key_rotation']( + rk = __salt__["boto_kms.enable_key_rotation"]( kms_key_id, region, key, keyid, profile ) - if 'error' in rk: + if "error" in rk: # Just checking for the key being disabled isn't enough, since key # disabling is very eventually consistent, so we have a long race # condition to handle. We check the error message to see if the failure # was due to a key being disabled. - if 'is disabled' in rk['error']['message']: - msg = 'Key is disabled, not changing key rotation policy.' - ret['result'] = None - ret['comment'] = msg + if "is disabled" in rk["error"]["message"]: + msg = "Key is disabled, not changing key rotation policy." + ret["result"] = None + ret["comment"] = msg return ret - ret['result'] = False - ret['comment'] = 'Failed to set key rotation: {0}.'.format(rk['error']['message']) + ret["result"] = False + ret["comment"] = "Failed to set key rotation: {0}.".format( + rk["error"]["message"] + ) else: - ret['changes'] = {'old': {'key_rotation': not key_rotation}, - 'new': {'key_rotation': key_rotation}} - ret['comment'] = 'Set key rotation policy to {0}.'.format(key_rotation) + ret["changes"] = { + "old": {"key_rotation": not key_rotation}, + "new": {"key_rotation": key_rotation}, + } + ret["comment"] = "Set key rotation policy to {0}.".format(key_rotation) return ret def _key_policy(key_metadata, policy, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - kms_key_id = key_metadata['KeyId'] - rkp = __salt__['boto_kms.get_key_policy']( - kms_key_id, 'default', region, key, keyid, profile + ret = {"result": True, "comment": "", "changes": {}} + kms_key_id = key_metadata["KeyId"] + rkp = __salt__["boto_kms.get_key_policy"]( + kms_key_id, "default", region, key, keyid, profile ) - if rkp['key_policy'] == policy: + if rkp["key_policy"] == policy: return ret - if __opts__['test']: - ret['comment'] = '{0} Key set to have key policy updated.'.format(ret['comment']) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} Key set to have key policy updated.".format( + ret["comment"] + ) + ret["result"] = None return ret - rpkp = __salt__['boto_kms.put_key_policy']( - kms_key_id, 'default', policy, region, key, keyid, profile + rpkp = __salt__["boto_kms.put_key_policy"]( + kms_key_id, "default", policy, region, key, keyid, profile ) - if 'error' in rpkp: - ret['result'] = False - ret['comment'] = '{0} Failed to update key policy: {1}'.format(ret['comment'], rpkp['error']['message']) + if "error" in rpkp: + ret["result"] = False + ret["comment"] = "{0} Failed to update key policy: {1}".format( + ret["comment"], rpkp["error"]["message"] + ) else: - ret['comment'] = 'Updated key policy.' + ret["comment"] = "Updated key policy." return ret diff --git a/salt/states/boto_lambda.py b/salt/states/boto_lambda.py index e10d3b1483e..a416359af31 100644 --- a/salt/states/boto_lambda.py +++ b/salt/states/boto_lambda.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Lambda Functions ======================= @@ -58,39 +58,59 @@ config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + +import hashlib import logging import os -import hashlib -# Import Salt Libs -from salt.ext import six import salt.utils.data import salt.utils.dictupdate as dictupdate import salt.utils.files import salt.utils.json from salt.exceptions import SaltInvocationError +# Import Salt Libs +from salt.ext import six + log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_lambda' if 'boto_lambda.function_exists' in __salt__ else False + """ + if "boto_lambda.function_exists" in __salt__: + return "boto_lambda" + return (False, "boto_lambda module could not be loaded") -def function_present(name, FunctionName, Runtime, Role, Handler, ZipFile=None, - S3Bucket=None, S3Key=None, S3ObjectVersion=None, - Description='', Timeout=3, MemorySize=128, - Permissions=None, RoleRetries=5, region=None, key=None, - keyid=None, profile=None, VpcConfig=None, - Environment=None): - ''' +def function_present( + name, + FunctionName, + Runtime, + Role, + Handler, + ZipFile=None, + S3Bucket=None, + S3Key=None, + S3ObjectVersion=None, + Description="", + Timeout=3, + MemorySize=128, + Permissions=None, + RoleRetries=5, + region=None, + key=None, + keyid=None, + profile=None, + VpcConfig=None, + Environment=None, +): + """ Ensure function exists. name @@ -201,124 +221,166 @@ def function_present(name, FunctionName, Runtime, Role, Handler, ZipFile=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': FunctionName, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": FunctionName, "result": True, "comment": "", "changes": {}} if Permissions is not None: if isinstance(Permissions, six.string_types): Permissions = salt.utils.json.loads(Permissions) - required_keys = set(('Action', 'Principal')) - optional_keys = set(('SourceArn', 'SourceAccount', 'Qualifier')) + required_keys = set(("Action", "Principal")) + optional_keys = set(("SourceArn", "SourceAccount", "Qualifier")) for sid, permission in six.iteritems(Permissions): keyset = set(permission.keys()) if not keyset.issuperset(required_keys): - raise SaltInvocationError('{0} are required for each permission ' - 'specification'.format(', '.join(required_keys))) + raise SaltInvocationError( + "{0} are required for each permission " + "specification".format(", ".join(required_keys)) + ) keyset = keyset - required_keys keyset = keyset - optional_keys if bool(keyset): raise SaltInvocationError( - 'Invalid permission value {0}'.format(', '.join(keyset))) + "Invalid permission value {0}".format(", ".join(keyset)) + ) - r = __salt__['boto_lambda.function_exists']( - FunctionName=FunctionName, region=region, - key=key, keyid=keyid, profile=profile) + r = __salt__["boto_lambda.function_exists"]( + FunctionName=FunctionName, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = ('Failed to create function: ' - '{0}.'.format(r['error']['message'])) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create function: " "{0}.".format( + r["error"]["message"] + ) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'Function {0} is set to be created.'.format( - FunctionName) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "Function {0} is set to be created.".format(FunctionName) + ret["result"] = None return ret - r = __salt__['boto_lambda.create_function']( - FunctionName=FunctionName, Runtime=Runtime, Role=Role, - Handler=Handler, ZipFile=ZipFile, S3Bucket=S3Bucket, S3Key=S3Key, - S3ObjectVersion=S3ObjectVersion, Description=Description, - Timeout=Timeout, MemorySize=MemorySize, VpcConfig=VpcConfig, - Environment=Environment, WaitForRole=True, RoleRetries=RoleRetries, - region=region, key=key, keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = ('Failed to create function: ' - '{0}.'.format(r['error']['message'])) + r = __salt__["boto_lambda.create_function"]( + FunctionName=FunctionName, + Runtime=Runtime, + Role=Role, + Handler=Handler, + ZipFile=ZipFile, + S3Bucket=S3Bucket, + S3Key=S3Key, + S3ObjectVersion=S3ObjectVersion, + Description=Description, + Timeout=Timeout, + MemorySize=MemorySize, + VpcConfig=VpcConfig, + Environment=Environment, + WaitForRole=True, + RoleRetries=RoleRetries, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create function: " "{0}.".format( + r["error"]["message"] + ) return ret if Permissions: for sid, permission in six.iteritems(Permissions): - r = __salt__['boto_lambda.add_permission']( - FunctionName=FunctionName, StatementId=sid, - region=region, key=key, keyid=keyid, profile=profile, - **permission) - if not r.get('updated'): - ret['result'] = False - ret['comment'] = ('Failed to create function: ' - '{0}.'.format(r['error']['message'])) + r = __salt__["boto_lambda.add_permission"]( + FunctionName=FunctionName, + StatementId=sid, + region=region, + key=key, + keyid=keyid, + profile=profile, + **permission + ) + if not r.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to create function: " "{0}.".format( + r["error"]["message"] + ) - _describe = __salt__['boto_lambda.describe_function']( - FunctionName, region=region, key=key, keyid=keyid, profile=profile) - _describe['function']['Permissions'] = ( - __salt__['boto_lambda.get_permissions']( - FunctionName, region=region, key=key, keyid=keyid, - profile=profile)['permissions']) - ret['changes']['old'] = {'function': None} - ret['changes']['new'] = _describe - ret['comment'] = 'Function {0} created.'.format(FunctionName) + _describe = __salt__["boto_lambda.describe_function"]( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + ) + _describe["function"]["Permissions"] = __salt__["boto_lambda.get_permissions"]( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + )["permissions"] + ret["changes"]["old"] = {"function": None} + ret["changes"]["new"] = _describe + ret["comment"] = "Function {0} created.".format(FunctionName) return ret - ret['comment'] = os.linesep.join( - [ret['comment'], 'Function {0} is present.'.format(FunctionName)]) - ret['changes'] = {} + ret["comment"] = os.linesep.join( + [ret["comment"], "Function {0} is present.".format(FunctionName)] + ) + ret["changes"] = {} # function exists, ensure config matches - _ret = _function_config_present(FunctionName, Role, Handler, Description, - Timeout, MemorySize, VpcConfig, - Environment, region, key, keyid, - profile, RoleRetries) - if not _ret.get('result'): - ret['result'] = _ret.get('result', False) - ret['comment'] = _ret['comment'] - ret['changes'] = {} + _ret = _function_config_present( + FunctionName, + Role, + Handler, + Description, + Timeout, + MemorySize, + VpcConfig, + Environment, + region, + key, + keyid, + profile, + RoleRetries, + ) + if not _ret.get("result"): + ret["result"] = _ret.get("result", False) + ret["comment"] = _ret["comment"] + ret["changes"] = {} return ret - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - _ret = _function_code_present(FunctionName, ZipFile, S3Bucket, S3Key, S3ObjectVersion, - region, key, keyid, profile) - if not _ret.get('result'): - ret['result'] = _ret.get('result', False) - ret['comment'] = _ret['comment'] - ret['changes'] = {} + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + _ret = _function_code_present( + FunctionName, + ZipFile, + S3Bucket, + S3Key, + S3ObjectVersion, + region, + key, + keyid, + profile, + ) + if not _ret.get("result"): + ret["result"] = _ret.get("result", False) + ret["comment"] = _ret["comment"] + ret["changes"] = {} return ret - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - _ret = _function_permissions_present(FunctionName, Permissions, - region, key, keyid, profile) - if not _ret.get('result'): - ret['result'] = _ret.get('result', False) - ret['comment'] = _ret['comment'] - ret['changes'] = {} + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + _ret = _function_permissions_present( + FunctionName, Permissions, region, key, keyid, profile + ) + if not _ret.get("result"): + ret["result"] = _ret.get("result", False) + ret["comment"] = _ret["comment"] + ret["changes"] = {} return ret - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) return ret def _get_role_arn(name, region=None, key=None, keyid=None, profile=None): - if name.startswith('arn:aws:iam:'): + if name.startswith("arn:aws:iam:"): return name - account_id = __salt__['boto_iam.get_account_id']( + account_id = __salt__["boto_iam.get_account_id"]( region=region, key=key, keyid=keyid, profile=profile ) - return 'arn:aws:iam::{0}:role/{1}'.format(account_id, name) + return "arn:aws:iam::{0}:role/{1}".format(account_id, name) def _resolve_vpcconfig(conf, region=None, key=None, keyid=None, profile=None): @@ -328,92 +390,131 @@ def _resolve_vpcconfig(conf, region=None, key=None, keyid=None, profile=None): # if the conf is None, we should explicitly set the VpcConfig to # {'SubnetIds': [], 'SecurityGroupIds': []} to take the lambda out of # the VPC it was in - return {'SubnetIds': [], 'SecurityGroupIds': []} + return {"SubnetIds": [], "SecurityGroupIds": []} if not isinstance(conf, dict): - raise SaltInvocationError('VpcConfig must be a dict.') - sns = [__salt__['boto_vpc.get_resource_id']('subnet', s, region=region, key=key, - keyid=keyid, profile=profile).get('id') for s in conf.pop('SubnetNames', [])] - sgs = [__salt__['boto_secgroup.get_group_id'](s, region=region, key=key, keyid=keyid, - profile=profile) for s in conf.pop('SecurityGroupNames', [])] - conf.setdefault('SubnetIds', []).extend(sns) - conf.setdefault('SecurityGroupIds', []).extend(sgs) + raise SaltInvocationError("VpcConfig must be a dict.") + sns = [ + __salt__["boto_vpc.get_resource_id"]( + "subnet", s, region=region, key=key, keyid=keyid, profile=profile + ).get("id") + for s in conf.pop("SubnetNames", []) + ] + sgs = [ + __salt__["boto_secgroup.get_group_id"]( + s, region=region, key=key, keyid=keyid, profile=profile + ) + for s in conf.pop("SecurityGroupNames", []) + ] + conf.setdefault("SubnetIds", []).extend(sns) + conf.setdefault("SecurityGroupIds", []).extend(sgs) return conf -def _function_config_present(FunctionName, Role, Handler, Description, Timeout, - MemorySize, VpcConfig, Environment, region, - key, keyid, profile, RoleRetries): - ret = {'result': True, 'comment': '', 'changes': {}} - func = __salt__['boto_lambda.describe_function']( - FunctionName, region=region, - key=key, keyid=keyid, profile=profile)['function'] - role_arn = _get_role_arn(Role, region, key, keyid, profile) # pylint: disable=possibly-unused-variable +def _function_config_present( + FunctionName, + Role, + Handler, + Description, + Timeout, + MemorySize, + VpcConfig, + Environment, + region, + key, + keyid, + profile, + RoleRetries, +): + ret = {"result": True, "comment": "", "changes": {}} + func = __salt__["boto_lambda.describe_function"]( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + )["function"] + # pylint: disable=possibly-unused-variable + role_arn = _get_role_arn(Role, region, key, keyid, profile) + # pylint: enable=possibly-unused-variable need_update = False - options = {'Role': 'role_arn', - 'Handler': 'Handler', - 'Description': 'Description', - 'Timeout': 'Timeout', - 'MemorySize': 'MemorySize'} + options = { + "Role": "role_arn", + "Handler": "Handler", + "Description": "Description", + "Timeout": "Timeout", + "MemorySize": "MemorySize", + } for val, var in six.iteritems(options): if func[val] != locals()[var]: need_update = True - ret['changes'].setdefault('new', {})[var] = locals()[var] - ret['changes'].setdefault('old', {})[var] = func[val] + ret["changes"].setdefault("new", {})[var] = locals()[var] + ret["changes"].setdefault("old", {})[var] = func[val] # VpcConfig returns the extra value 'VpcId' so do a special compare - oldval = func.get('VpcConfig') + oldval = func.get("VpcConfig") if oldval is not None: - oldval.pop('VpcId', None) + oldval.pop("VpcId", None) fixed_VpcConfig = _resolve_vpcconfig(VpcConfig, region, key, keyid, profile) - if __utils__['boto3.ordered'](oldval) != __utils__['boto3.ordered'](fixed_VpcConfig): + if __utils__["boto3.ordered"](oldval) != __utils__["boto3.ordered"]( + fixed_VpcConfig + ): need_update = True - ret['changes'].setdefault('new', {})['VpcConfig'] = fixed_VpcConfig - ret['changes'].setdefault( - 'old', {})['VpcConfig'] = func.get('VpcConfig') + ret["changes"].setdefault("new", {})["VpcConfig"] = fixed_VpcConfig + ret["changes"].setdefault("old", {})["VpcConfig"] = func.get("VpcConfig") if Environment is not None: - if func.get('Environment') != Environment: + if func.get("Environment") != Environment: need_update = True - ret['changes'].setdefault('new', {})['Environment'] = Environment - ret['changes'].setdefault('old', {})['Environment'] = func.get( - 'Environment') + ret["changes"].setdefault("new", {})["Environment"] = Environment + ret["changes"].setdefault("old", {})["Environment"] = func.get( + "Environment" + ) if need_update: - ret['comment'] = os.linesep.join( - [ret['comment'], 'Function config to be modified']) - if __opts__['test']: - ret['comment'] = 'Function {0} set to be modified.'.format(FunctionName) - ret['result'] = None + ret["comment"] = os.linesep.join( + [ret["comment"], "Function config to be modified"] + ) + if __opts__["test"]: + ret["comment"] = "Function {0} set to be modified.".format(FunctionName) + ret["result"] = None return ret - _r = __salt__['boto_lambda.update_function_config']( - FunctionName=FunctionName, Role=Role, Handler=Handler, - Description=Description, Timeout=Timeout, MemorySize=MemorySize, - VpcConfig=fixed_VpcConfig, Environment=Environment, region=region, - key=key, keyid=keyid, profile=profile, WaitForRole=True, - RoleRetries=RoleRetries) - if not _r.get('updated'): - ret['result'] = False - ret['comment'] = ('Failed to update function: ' - '{0}.'.format(_r['error']['message'])) - ret['changes'] = {} + _r = __salt__["boto_lambda.update_function_config"]( + FunctionName=FunctionName, + Role=Role, + Handler=Handler, + Description=Description, + Timeout=Timeout, + MemorySize=MemorySize, + VpcConfig=fixed_VpcConfig, + Environment=Environment, + region=region, + key=key, + keyid=keyid, + profile=profile, + WaitForRole=True, + RoleRetries=RoleRetries, + ) + if not _r.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to update function: " "{0}.".format( + _r["error"]["message"] + ) + ret["changes"] = {} return ret -def _function_code_present(FunctionName, ZipFile, S3Bucket, S3Key, - S3ObjectVersion, region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - func = __salt__['boto_lambda.describe_function']( - FunctionName, region=region, - key=key, keyid=keyid, profile=profile)['function'] +def _function_code_present( + FunctionName, ZipFile, S3Bucket, S3Key, S3ObjectVersion, region, key, keyid, profile +): + ret = {"result": True, "comment": "", "changes": {}} + func = __salt__["boto_lambda.describe_function"]( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + )["function"] update = False if ZipFile: size = os.path.getsize(ZipFile) - if size == func['CodeSize']: + if size == func["CodeSize"]: sha = hashlib.sha256() - with salt.utils.files.fopen(ZipFile, 'rb') as f: + with salt.utils.files.fopen(ZipFile, "rb") as f: sha.update(f.read()) - hashed = sha.digest().encode('base64').strip() - if hashed != func['CodeSha256']: + hashed = sha.digest().encode("base64").strip() + if hashed != func["CodeSha256"]: update = True else: update = True @@ -423,87 +524,117 @@ def _function_code_present(FunctionName, ZipFile, S3Bucket, S3Key, # idempotent update = True if update: - if __opts__['test']: - ret['comment'] = 'Function {0} set to be modified.'.format(FunctionName) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Function {0} set to be modified.".format(FunctionName) + ret["result"] = None return ret - ret['changes']['old'] = { - 'CodeSha256': func['CodeSha256'], - 'CodeSize': func['CodeSize'], + ret["changes"]["old"] = { + "CodeSha256": func["CodeSha256"], + "CodeSize": func["CodeSize"], } - func = __salt__['boto_lambda.update_function_code']( - FunctionName, ZipFile, S3Bucket, - S3Key, S3ObjectVersion, - region=region, key=key, keyid=keyid, profile=profile) - if not func.get('updated'): - ret['result'] = False - ret['comment'] = ('Failed to update function: ' - '{0}.'.format(func['error']['message'])) - ret['changes'] = {} + func = __salt__["boto_lambda.update_function_code"]( + FunctionName, + ZipFile, + S3Bucket, + S3Key, + S3ObjectVersion, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not func.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to update function: " "{0}.".format( + func["error"]["message"] + ) + ret["changes"] = {} return ret - func = func['function'] - if func['CodeSha256'] != ret['changes']['old']['CodeSha256'] or \ - func['CodeSize'] != ret['changes']['old']['CodeSize']: - ret['comment'] = os.linesep.join( - [ret['comment'], 'Function code to be modified']) - ret['changes']['new'] = { - 'CodeSha256': func['CodeSha256'], - 'CodeSize': func['CodeSize'], + func = func["function"] + if ( + func["CodeSha256"] != ret["changes"]["old"]["CodeSha256"] + or func["CodeSize"] != ret["changes"]["old"]["CodeSize"] + ): + ret["comment"] = os.linesep.join( + [ret["comment"], "Function code to be modified"] + ) + ret["changes"]["new"] = { + "CodeSha256": func["CodeSha256"], + "CodeSize": func["CodeSize"], } else: - del ret['changes']['old'] + del ret["changes"]["old"] return ret -def _function_permissions_present(FunctionName, Permissions, - region, key, keyid, profile): - ret = {'result': True, 'comment': '', 'changes': {}} - curr_permissions = __salt__['boto_lambda.get_permissions']( - FunctionName, region=region, - key=key, keyid=keyid, profile=profile).get('permissions') +def _function_permissions_present( + FunctionName, Permissions, region, key, keyid, profile +): + ret = {"result": True, "comment": "", "changes": {}} + curr_permissions = __salt__["boto_lambda.get_permissions"]( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + ).get("permissions") if curr_permissions is None: curr_permissions = {} need_update = False diffs = salt.utils.data.compare_dicts(curr_permissions, Permissions or {}) if bool(diffs): - ret['comment'] = os.linesep.join( - [ret['comment'], 'Function permissions to be modified']) - if __opts__['test']: - ret['comment'] = 'Function {0} set to be modified.'.format(FunctionName) - ret['result'] = None + ret["comment"] = os.linesep.join( + [ret["comment"], "Function permissions to be modified"] + ) + if __opts__["test"]: + ret["comment"] = "Function {0} set to be modified.".format(FunctionName) + ret["result"] = None return ret for sid, diff in six.iteritems(diffs): - if diff.get('old', '') != '': + if diff.get("old", "") != "": # There's a permssion that needs to be removed - _r = __salt__['boto_lambda.remove_permission']( - FunctionName=FunctionName, StatementId=sid, - region=region, key=key, keyid=keyid, profile=profile) - ret['changes'].setdefault( - 'new', {}).setdefault('Permissions', {})[sid] = {} - ret['changes'].setdefault( - 'old', {}).setdefault('Permissions', {})[sid] = diff['old'] - if diff.get('new', '') != '': + _r = __salt__["boto_lambda.remove_permission"]( + FunctionName=FunctionName, + StatementId=sid, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"].setdefault("new", {}).setdefault("Permissions", {})[ + sid + ] = {} + ret["changes"].setdefault("old", {}).setdefault("Permissions", {})[ + sid + ] = diff["old"] + if diff.get("new", "") != "": # New permission information needs to be added - _r = __salt__['boto_lambda.add_permission']( - FunctionName=FunctionName, StatementId=sid, - region=region, key=key, keyid=keyid, profile=profile, - **diff['new']) - ret['changes'].setdefault( - 'new', {}).setdefault('Permissions', {})[sid] = diff['new'] - oldperms = ret['changes'].setdefault( - 'old', {}).setdefault('Permissions', {}) + _r = __salt__["boto_lambda.add_permission"]( + FunctionName=FunctionName, + StatementId=sid, + region=region, + key=key, + keyid=keyid, + profile=profile, + **diff["new"] + ) + ret["changes"].setdefault("new", {}).setdefault("Permissions", {})[ + sid + ] = diff["new"] + oldperms = ( + ret["changes"].setdefault("old", {}).setdefault("Permissions", {}) + ) if sid not in oldperms: oldperms[sid] = {} - if not _r.get('updated'): - ret['result'] = False - ret['comment'] = ('Failed to update function: ' - '{0}.'.format(_r['error']['message'])) - ret['changes'] = {} + if not _r.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to update function: " "{0}.".format( + _r["error"]["message"] + ) + ret["changes"] = {} return ret -def function_absent(name, FunctionName, region=None, key=None, keyid=None, profile=None): - ''' +def function_absent( + name, FunctionName, region=None, key=None, keyid=None, profile=None +): + """ Ensure function with passed properties is absent. name @@ -524,48 +655,53 @@ def function_absent(name, FunctionName, region=None, key=None, keyid=None, profi profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': FunctionName, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": FunctionName, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_lambda.function_exists']( - FunctionName, region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete function: {0}.'.format(r['error'][ - 'message']) + r = __salt__["boto_lambda.function_exists"]( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete function: {0}.".format(r["error"]["message"]) return ret - if r and not r['exists']: - ret['comment'] = 'Function {0} does not exist.'.format(FunctionName) + if r and not r["exists"]: + ret["comment"] = "Function {0} does not exist.".format(FunctionName) return ret - if __opts__['test']: - ret['comment'] = 'Function {0} is set to be removed.'.format( - FunctionName) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Function {0} is set to be removed.".format(FunctionName) + ret["result"] = None return ret - r = __salt__['boto_lambda.delete_function'](FunctionName, - region=region, key=key, - keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = ('Failed to delete function: ' - '{0}.'.format(r['error']['message'])) + r = __salt__["boto_lambda.delete_function"]( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete function: " "{0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'function': FunctionName} - ret['changes']['new'] = {'function': None} - ret['comment'] = 'Function {0} deleted.'.format(FunctionName) + ret["changes"]["old"] = {"function": FunctionName} + ret["changes"]["new"] = {"function": None} + ret["comment"] = "Function {0} deleted.".format(FunctionName) return ret -def alias_present(name, FunctionName, Name, FunctionVersion, Description='', - region=None, key=None, keyid=None, profile=None): - ''' +def alias_present( + name, + FunctionName, + Name, + FunctionVersion, + Description="", + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure alias exists. name @@ -596,81 +732,99 @@ def alias_present(name, FunctionName, Name, FunctionVersion, Description='', profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': Name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": Name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_lambda.alias_exists']( - FunctionName=FunctionName, Name=Name, region=region, - key=key, keyid=keyid, profile=profile) + r = __salt__["boto_lambda.alias_exists"]( + FunctionName=FunctionName, + Name=Name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = ('Failed to create alias: ' - '{0}.'.format(r['error']['message'])) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create alias: " "{0}.".format(r["error"]["message"]) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'Alias {0} is set to be created.'.format(Name) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "Alias {0} is set to be created.".format(Name) + ret["result"] = None return ret - r = __salt__['boto_lambda.create_alias'](FunctionName, Name, - FunctionVersion, Description, - region, key, keyid, profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = ('Failed to create alias: ' - '{0}.'.format(r['error']['message'])) + r = __salt__["boto_lambda.create_alias"]( + FunctionName, + Name, + FunctionVersion, + Description, + region, + key, + keyid, + profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create alias: " "{0}.".format( + r["error"]["message"] + ) return ret - _describe = __salt__['boto_lambda.describe_alias']( - FunctionName, Name, region=region, key=key, - keyid=keyid, profile=profile) - ret['changes']['old'] = {'alias': None} - ret['changes']['new'] = _describe - ret['comment'] = 'Alias {0} created.'.format(Name) + _describe = __salt__["boto_lambda.describe_alias"]( + FunctionName, Name, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["old"] = {"alias": None} + ret["changes"]["new"] = _describe + ret["comment"] = "Alias {0} created.".format(Name) return ret - ret['comment'] = os.linesep.join( - [ret['comment'], 'Alias {0} is present.'.format(Name)]) - ret['changes'] = {} - _describe = __salt__['boto_lambda.describe_alias']( - FunctionName, Name, region=region, key=key, keyid=keyid, - profile=profile)['alias'] + ret["comment"] = os.linesep.join( + [ret["comment"], "Alias {0} is present.".format(Name)] + ) + ret["changes"] = {} + _describe = __salt__["boto_lambda.describe_alias"]( + FunctionName, Name, region=region, key=key, keyid=keyid, profile=profile + )["alias"] need_update = False - options = {'FunctionVersion': 'FunctionVersion', - 'Description': 'Description'} + options = {"FunctionVersion": "FunctionVersion", "Description": "Description"} for val, var in six.iteritems(options): if _describe[val] != locals()[var]: need_update = True - ret['changes'].setdefault('new', {})[var] = locals()[var] - ret['changes'].setdefault('old', {})[var] = _describe[val] + ret["changes"].setdefault("new", {})[var] = locals()[var] + ret["changes"].setdefault("old", {})[var] = _describe[val] if need_update: - ret['comment'] = os.linesep.join( - [ret['comment'], 'Alias config to be modified']) - if __opts__['test']: - ret['comment'] = 'Alias {0} set to be modified.'.format(Name) - ret['result'] = None + ret["comment"] = os.linesep.join( + [ret["comment"], "Alias config to be modified"] + ) + if __opts__["test"]: + ret["comment"] = "Alias {0} set to be modified.".format(Name) + ret["result"] = None return ret - _r = __salt__['boto_lambda.update_alias']( - FunctionName=FunctionName, Name=Name, - FunctionVersion=FunctionVersion, Description=Description, - region=region, key=key, keyid=keyid, profile=profile) - if not _r.get('updated'): - ret['result'] = False - ret['comment'] = ('Failed to update alias: ' - '{0}.'.format(_r['error']['message'])) - ret['changes'] = {} + _r = __salt__["boto_lambda.update_alias"]( + FunctionName=FunctionName, + Name=Name, + FunctionVersion=FunctionVersion, + Description=Description, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not _r.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to update alias: " "{0}.".format( + _r["error"]["message"] + ) + ret["changes"] = {} return ret -def alias_absent(name, FunctionName, Name, region=None, key=None, keyid=None, profile=None): - ''' +def alias_absent( + name, FunctionName, Name, region=None, key=None, keyid=None, profile=None +): + """ Ensure alias with passed properties is absent. name @@ -694,64 +848,66 @@ def alias_absent(name, FunctionName, Name, region=None, key=None, keyid=None, pr profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': Name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": Name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_lambda.alias_exists']( - FunctionName, Name, region=region, key=key, keyid=keyid, - profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = ('Failed to delete alias: ' - '{0}.'.format(r['error']['message'])) + r = __salt__["boto_lambda.alias_exists"]( + FunctionName, Name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete alias: " "{0}.".format(r["error"]["message"]) return ret - if r and not r['exists']: - ret['comment'] = 'Alias {0} does not exist.'.format(Name) + if r and not r["exists"]: + ret["comment"] = "Alias {0} does not exist.".format(Name) return ret - if __opts__['test']: - ret['comment'] = 'Alias {0} is set to be removed.'.format(Name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Alias {0} is set to be removed.".format(Name) + ret["result"] = None return ret - r = __salt__['boto_lambda.delete_alias']( - FunctionName, Name, region=region, key=key, keyid=keyid, - profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = ('Failed to delete alias: ' - '{0}.'.format(r['error']['message'])) + r = __salt__["boto_lambda.delete_alias"]( + FunctionName, Name, region=region, key=key, keyid=keyid, profile=profile + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete alias: " "{0}.".format(r["error"]["message"]) return ret - ret['changes']['old'] = {'alias': Name} - ret['changes']['new'] = {'alias': None} - ret['comment'] = 'Alias {0} deleted.'.format(Name) + ret["changes"]["old"] = {"alias": Name} + ret["changes"]["new"] = {"alias": None} + ret["comment"] = "Alias {0} deleted.".format(Name) return ret def _get_function_arn(name, region=None, key=None, keyid=None, profile=None): - if name.startswith('arn:aws:lambda:'): + if name.startswith("arn:aws:lambda:"): return name - account_id = __salt__['boto_iam.get_account_id']( + account_id = __salt__["boto_iam.get_account_id"]( region=region, key=key, keyid=keyid, profile=profile ) - if profile and 'region' in profile: - region = profile['region'] + if profile and "region" in profile: + region = profile["region"] if region is None: - region = 'us-east-1' - return 'arn:aws:lambda:{0}:{1}:function:{2}'.format(region, account_id, name) + region = "us-east-1" + return "arn:aws:lambda:{0}:{1}:function:{2}".format(region, account_id, name) -def event_source_mapping_present(name, EventSourceArn, FunctionName, - StartingPosition, Enabled=True, BatchSize=100, - region=None, key=None, keyid=None, - profile=None): - ''' +def event_source_mapping_present( + name, + EventSourceArn, + FunctionName, + StartingPosition, + Enabled=True, + BatchSize=100, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure event source mapping exists. name @@ -799,101 +955,127 @@ def event_source_mapping_present(name, EventSourceArn, FunctionName, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': None, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": None, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_lambda.event_source_mapping_exists']( - EventSourceArn=EventSourceArn, FunctionName=FunctionName, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_lambda.event_source_mapping_exists"]( + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = ('Failed to create event source mapping: ' - '{0}.'.format(r['error']['message'])) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create event source mapping: " "{0}.".format( + r["error"]["message"] + ) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = ('Event source mapping {0} is set ' - 'to be created.'.format(FunctionName)) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "Event source mapping {0} is set " "to be created.".format( + FunctionName + ) + ret["result"] = None return ret - r = __salt__['boto_lambda.create_event_source_mapping']( - EventSourceArn=EventSourceArn, FunctionName=FunctionName, - StartingPosition=StartingPosition, Enabled=Enabled, - BatchSize=BatchSize, region=region, key=key, keyid=keyid, - profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = ('Failed to create event source mapping: ' - '{0}.'.format(r['error']['message'])) + r = __salt__["boto_lambda.create_event_source_mapping"]( + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + StartingPosition=StartingPosition, + Enabled=Enabled, + BatchSize=BatchSize, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create event source mapping: " "{0}.".format( + r["error"]["message"] + ) return ret - _describe = __salt__['boto_lambda.describe_event_source_mapping']( - EventSourceArn=EventSourceArn, FunctionName=FunctionName, - region=region, key=key, keyid=keyid, profile=profile) - ret['name'] = _describe['event_source_mapping']['UUID'] - ret['changes']['old'] = {'event_source_mapping': None} - ret['changes']['new'] = _describe - ret['comment'] = ('Event source mapping {0} ' - 'created.'.format(ret['name'])) + _describe = __salt__["boto_lambda.describe_event_source_mapping"]( + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["name"] = _describe["event_source_mapping"]["UUID"] + ret["changes"]["old"] = {"event_source_mapping": None} + ret["changes"]["new"] = _describe + ret["comment"] = "Event source mapping {0} " "created.".format(ret["name"]) return ret - ret['comment'] = os.linesep.join( - [ret['comment'], 'Event source mapping is present.']) - ret['changes'] = {} - _describe = __salt__['boto_lambda.describe_event_source_mapping']( - EventSourceArn=EventSourceArn, FunctionName=FunctionName, - region=region, key=key, keyid=keyid, - profile=profile)['event_source_mapping'] + ret["comment"] = os.linesep.join( + [ret["comment"], "Event source mapping is present."] + ) + ret["changes"] = {} + _describe = __salt__["boto_lambda.describe_event_source_mapping"]( + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + region=region, + key=key, + keyid=keyid, + profile=profile, + )["event_source_mapping"] need_update = False - options = {'BatchSize': 'BatchSize'} + options = {"BatchSize": "BatchSize"} for val, var in six.iteritems(options): if _describe[val] != locals()[var]: need_update = True - ret['changes'].setdefault('new', {})[var] = locals()[var] - ret['changes'].setdefault('old', {})[var] = _describe[val] + ret["changes"].setdefault("new", {})[var] = locals()[var] + ret["changes"].setdefault("old", {})[var] = _describe[val] # verify FunctionName against FunctionArn - function_arn = _get_function_arn(FunctionName, region=region, - key=key, keyid=keyid, profile=profile) - if _describe['FunctionArn'] != function_arn: + function_arn = _get_function_arn( + FunctionName, region=region, key=key, keyid=keyid, profile=profile + ) + if _describe["FunctionArn"] != function_arn: need_update = True - ret['changes'].setdefault('new', {})['FunctionArn'] = function_arn - ret['changes'].setdefault('old', {})['FunctionArn'] = _describe[ - 'FunctionArn'] + ret["changes"].setdefault("new", {})["FunctionArn"] = function_arn + ret["changes"].setdefault("old", {})["FunctionArn"] = _describe["FunctionArn"] # TODO check for 'Enabled', since it doesn't directly map to a specific # state if need_update: - ret['comment'] = os.linesep.join( - [ret['comment'], 'Event source mapping to be modified']) - if __opts__['test']: - ret['comment'] = ( - 'Event source mapping {0} set to be modified.'.format( - _describe['UUID'] - ) + ret["comment"] = os.linesep.join( + [ret["comment"], "Event source mapping to be modified"] + ) + if __opts__["test"]: + ret["comment"] = "Event source mapping {0} set to be modified.".format( + _describe["UUID"] ) - ret['result'] = None + ret["result"] = None return ret - _r = __salt__['boto_lambda.update_event_source_mapping']( - UUID=_describe['UUID'], FunctionName=FunctionName, - Enabled=Enabled, BatchSize=BatchSize, - region=region, key=key, keyid=keyid, profile=profile) - if not _r.get('updated'): - ret['result'] = False - ret['comment'] = ('Failed to update mapping: ' - '{0}.'.format(_r['error']['message'])) - ret['changes'] = {} + _r = __salt__["boto_lambda.update_event_source_mapping"]( + UUID=_describe["UUID"], + FunctionName=FunctionName, + Enabled=Enabled, + BatchSize=BatchSize, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not _r.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to update mapping: " "{0}.".format( + _r["error"]["message"] + ) + ret["changes"] = {} return ret -def event_source_mapping_absent(name, EventSourceArn, FunctionName, - region=None, key=None, keyid=None, profile=None): - ''' +def event_source_mapping_absent( + name, EventSourceArn, FunctionName, region=None, key=None, keyid=None, profile=None +): + """ Ensure event source mapping with passed properties is absent. name @@ -917,41 +1099,49 @@ def event_source_mapping_absent(name, EventSourceArn, FunctionName, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': None, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": None, "result": True, "comment": "", "changes": {}} - desc = __salt__['boto_lambda.describe_event_source_mapping']( - EventSourceArn=EventSourceArn, FunctionName=FunctionName, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in desc: - ret['result'] = False - ret['comment'] = ('Failed to delete event source mapping: ' - '{0}.'.format(desc['error']['message'])) + desc = __salt__["boto_lambda.describe_event_source_mapping"]( + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in desc: + ret["result"] = False + ret["comment"] = "Failed to delete event source mapping: " "{0}.".format( + desc["error"]["message"] + ) return ret - if not desc.get('event_source_mapping'): - ret['comment'] = 'Event source mapping does not exist.' + if not desc.get("event_source_mapping"): + ret["comment"] = "Event source mapping does not exist." return ret - ret['name'] = desc['event_source_mapping']['UUID'] - if __opts__['test']: - ret['comment'] = 'Event source mapping is set to be removed.' - ret['result'] = None + ret["name"] = desc["event_source_mapping"]["UUID"] + if __opts__["test"]: + ret["comment"] = "Event source mapping is set to be removed." + ret["result"] = None return ret - r = __salt__['boto_lambda.delete_event_source_mapping']( - EventSourceArn=EventSourceArn, FunctionName=FunctionName, - region=region, key=key, keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete event source mapping: {0}.'.format(r['error'][ - 'message']) + r = __salt__["boto_lambda.delete_event_source_mapping"]( + EventSourceArn=EventSourceArn, + FunctionName=FunctionName, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete event source mapping: {0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = desc - ret['changes']['new'] = {'event_source_mapping': None} - ret['comment'] = 'Event source mapping deleted.' + ret["changes"]["old"] = desc + ret["changes"]["new"] = {"event_source_mapping": None} + ret["comment"] = "Event source mapping deleted." return ret diff --git a/salt/states/boto_lc.py b/salt/states/boto_lc.py index 8f43303b262..8e99dd564ab 100644 --- a/salt/states/boto_lc.py +++ b/salt/states/boto_lc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Launch Configurations .. versionadded:: 2014.7.0 @@ -97,42 +97,46 @@ and autoscale groups are completely dependent on each other. keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs region: us-east-1 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + from salt.exceptions import SaltInvocationError def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_lc' if 'boto_asg.exists' in __salt__ else False + """ + if "boto_asg.exists" in __salt__: + return "boto_lc" + return (False, "boto_asg module could not be loaded") def present( - name, - image_id, - key_name=None, - vpc_id=None, - vpc_name=None, - security_groups=None, - user_data=None, - cloud_init=None, - instance_type='m1.small', - kernel_id=None, - ramdisk_id=None, - block_device_mappings=None, - delete_on_termination=None, - instance_monitoring=False, - spot_price=None, - instance_profile_name=None, - ebs_optimized=False, - associate_public_ip_address=None, - region=None, - key=None, - keyid=None, - profile=None): - ''' + name, + image_id, + key_name=None, + vpc_id=None, + vpc_name=None, + security_groups=None, + user_data=None, + cloud_init=None, + instance_type="m1.small", + kernel_id=None, + ramdisk_id=None, + block_device_mappings=None, + delete_on_termination=None, + instance_monitoring=False, + spot_price=None, + instance_profile_name=None, + ebs_optimized=False, + associate_public_ip_address=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure the launch configuration exists. name @@ -240,27 +244,26 @@ def present( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ if user_data and cloud_init: - raise SaltInvocationError('user_data and cloud_init are mutually' - ' exclusive options.') - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - exists = __salt__['boto_asg.launch_configuration_exists'](name, - region=region, - key=key, - keyid=keyid, - profile=profile) + raise SaltInvocationError( + "user_data and cloud_init are mutually" " exclusive options." + ) + ret = {"name": name, "result": True, "comment": "", "changes": {}} + exists = __salt__["boto_asg.launch_configuration_exists"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if not exists: - if __opts__['test']: - msg = 'Launch configuration set to be created.' - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Launch configuration set to be created." + ret["comment"] = msg + ret["result"] = None return ret if cloud_init: - user_data = __salt__['boto_asg.get_cloud_init_mime'](cloud_init) + user_data = __salt__["boto_asg.get_cloud_init_mime"](cloud_init) # TODO: Ensure image_id, key_name, security_groups and instance_profile # exist, or throw an invocation error. - created = __salt__['boto_asg.create_launch_configuration']( + created = __salt__["boto_asg.create_launch_configuration"]( name, image_id, key_name=key_name, @@ -281,25 +284,21 @@ def present( region=region, key=key, keyid=keyid, - profile=profile) + profile=profile, + ) if created: - ret['changes']['old'] = None - ret['changes']['new'] = name + ret["changes"]["old"] = None + ret["changes"]["new"] = name else: - ret['result'] = False - ret['comment'] = 'Failed to create launch configuration.' + ret["result"] = False + ret["comment"] = "Failed to create launch configuration." else: - ret['comment'] = 'Launch configuration present.' + ret["comment"] = "Launch configuration present." return ret -def absent( - name, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def absent(name, region=None, key=None, keyid=None, profile=None): + """ Ensure the named launch configuration is deleted. name @@ -317,31 +316,26 @@ def absent( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - exists = __salt__['boto_asg.launch_configuration_exists'](name, - region=region, - key=key, - keyid=keyid, - profile=profile) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + exists = __salt__["boto_asg.launch_configuration_exists"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if exists: - if __opts__['test']: - ret['comment'] = 'Launch configuration set to be deleted.' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Launch configuration set to be deleted." + ret["result"] = None return ret - deleted = __salt__['boto_asg.delete_launch_configuration']( - name, - region=region, - key=key, - keyid=keyid, - profile=profile) + deleted = __salt__["boto_asg.delete_launch_configuration"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if deleted: - ret['changes']['old'] = name - ret['changes']['new'] = None - ret['comment'] = 'Deleted launch configuration.' + ret["changes"]["old"] = name + ret["changes"]["new"] = None + ret["comment"] = "Deleted launch configuration." else: - ret['result'] = False - ret['comment'] = 'Failed to delete launch configuration.' + ret["result"] = False + ret["comment"] = "Failed to delete launch configuration." else: - ret['comment'] = 'Launch configuration does not exist.' + ret["comment"] = "Launch configuration does not exist." return ret diff --git a/salt/states/boto_rds.py b/salt/states/boto_rds.py index 7a8974994e1..d3336dd1789 100644 --- a/salt/states/boto_rds.py +++ b/salt/states/boto_rds.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage RDSs =========== @@ -67,74 +67,78 @@ config: :depends: boto3 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os +import salt.utils.data +from salt.exceptions import SaltInvocationError + # Import Salt Libs from salt.ext import six -from salt.exceptions import SaltInvocationError -import salt.utils.data log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - if 'boto_rds.exists' in __salt__: - return 'boto_rds' - return(False, __salt__.missing_fun_string('boto_rds.exists')) + """ + if "boto_rds.exists" in __salt__: + return "boto_rds" + return (False, __salt__.missing_fun_string("boto_rds.exists")) -def present(name, - allocated_storage, - db_instance_class, - engine, - master_username, - master_user_password, - db_name=None, - storage_type=None, - db_security_groups=None, - vpc_security_group_ids=None, - vpc_security_groups=None, - availability_zone=None, - db_subnet_group_name=None, - preferred_maintenance_window=None, - db_parameter_group_name=None, - db_cluster_identifier=None, - tde_credential_arn=None, - tde_credential_password=None, - storage_encrypted=None, - kms_keyid=None, - backup_retention_period=None, - preferred_backup_window=None, - port=None, - multi_az=None, - engine_version=None, - auto_minor_version_upgrade=None, - license_model=None, - iops=None, - option_group_name=None, - character_set_name=None, - publicly_accessible=None, - wait_status=None, - tags=None, - copy_tags_to_snapshot=None, - region=None, - domain=None, - key=None, - keyid=None, - monitoring_interval=None, - monitoring_role_arn=None, - domain_iam_role_name=None, - promotion_tier=None, - profile=None): - ''' +def present( + name, + allocated_storage, + db_instance_class, + engine, + master_username, + master_user_password, + db_name=None, + storage_type=None, + db_security_groups=None, + vpc_security_group_ids=None, + vpc_security_groups=None, + availability_zone=None, + db_subnet_group_name=None, + preferred_maintenance_window=None, + db_parameter_group_name=None, + db_cluster_identifier=None, + tde_credential_arn=None, + tde_credential_password=None, + storage_encrypted=None, + kms_keyid=None, + backup_retention_period=None, + preferred_backup_window=None, + port=None, + multi_az=None, + engine_version=None, + auto_minor_version_upgrade=None, + license_model=None, + iops=None, + option_group_name=None, + character_set_name=None, + publicly_accessible=None, + wait_status=None, + tags=None, + copy_tags_to_snapshot=None, + region=None, + domain=None, + key=None, + keyid=None, + monitoring_interval=None, + monitoring_role_arn=None, + domain_iam_role_name=None, + promotion_tier=None, + profile=None, +): + """ Ensure RDS instance exists. name @@ -301,73 +305,105 @@ def present(name, contains a dict with region, key and keyid. .. _create_db_instance: https://boto3.readthedocs.io/en/latest/reference/services/rds.html#RDS.Client.create_db_instance - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_rds.exists'](name, tags, region, key, keyid, profile) + r = __salt__["boto_rds.exists"](name, tags, region, key, keyid, profile) - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'RDS instance {0} would be created.'.format(name) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "RDS instance {0} would be created.".format(name) + ret["result"] = None return ret - r = __salt__['boto_rds.create'](name, allocated_storage, - db_instance_class, engine, - master_username, - master_user_password, - db_name, db_security_groups, - vpc_security_group_ids, - vpc_security_groups, - availability_zone, - db_subnet_group_name, - preferred_maintenance_window, - db_parameter_group_name, - backup_retention_period, - preferred_backup_window, - port, multi_az, engine_version, - auto_minor_version_upgrade, - license_model, iops, - option_group_name, - character_set_name, - publicly_accessible, wait_status, - tags, db_cluster_identifier, - storage_type, tde_credential_arn, - tde_credential_password, - storage_encrypted, kms_keyid, - domain, copy_tags_to_snapshot, - monitoring_interval, - monitoring_role_arn, - domain_iam_role_name, region, - promotion_tier, key, keyid, profile) + r = __salt__["boto_rds.create"]( + name, + allocated_storage, + db_instance_class, + engine, + master_username, + master_user_password, + db_name, + db_security_groups, + vpc_security_group_ids, + vpc_security_groups, + availability_zone, + db_subnet_group_name, + preferred_maintenance_window, + db_parameter_group_name, + backup_retention_period, + preferred_backup_window, + port, + multi_az, + engine_version, + auto_minor_version_upgrade, + license_model, + iops, + option_group_name, + character_set_name, + publicly_accessible, + wait_status, + tags, + db_cluster_identifier, + storage_type, + tde_credential_arn, + tde_credential_password, + storage_encrypted, + kms_keyid, + domain, + copy_tags_to_snapshot, + monitoring_interval, + monitoring_role_arn, + domain_iam_role_name, + region, + promotion_tier, + key, + keyid, + profile, + ) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create RDS instance {0}.'.format(r['error']['message']) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create RDS instance {0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'instance': None} - ret['changes']['new'] = { - 'instance': __salt__['boto_rds.describe_db_instances']( - name=name, jmespath='DBInstances[0]', region=region, key=key, - keyid=keyid, profile=profile)} - ret['comment'] = 'RDS instance {0} created.'.format(name) + ret["changes"]["old"] = {"instance": None} + ret["changes"]["new"] = { + "instance": __salt__["boto_rds.describe_db_instances"]( + name=name, + jmespath="DBInstances[0]", + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + } + ret["comment"] = "RDS instance {0} created.".format(name) else: - ret['comment'] = 'RDS instance {0} exists.'.format(name) + ret["comment"] = "RDS instance {0} exists.".format(name) return ret -def replica_present(name, source, db_instance_class=None, - availability_zone=None, port=None, - auto_minor_version_upgrade=None, iops=None, - option_group_name=None, publicly_accessible=None, - tags=None, region=None, key=None, keyid=None, - profile=None, db_parameter_group_name=None): - ''' +def replica_present( + name, + source, + db_instance_class=None, + availability_zone=None, + port=None, + auto_minor_version_upgrade=None, + iops=None, + option_group_name=None, + publicly_accessible=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + db_parameter_group_name=None, +): + """ Ensure RDS replica exists. .. code-block:: yaml @@ -376,62 +412,93 @@ def replica_present(name, source, db_instance_class=None, boto_rds.create_replica: - name: myreplica - source: mydb - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } - replica_exists = __salt__['boto_rds.exists'](name, tags, region, key, - keyid, profile) - if not replica_exists.get('exists'): - if __opts__['test']: - ret['comment'] = 'RDS read replica {0} is set to be created '.format(name) - ret['result'] = None + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + replica_exists = __salt__["boto_rds.exists"]( + name, tags, region, key, keyid, profile + ) + if not replica_exists.get("exists"): + if __opts__["test"]: + ret["comment"] = "RDS read replica {0} is set to be created ".format(name) + ret["result"] = None return ret - created = __salt__['boto_rds.create_read_replica'](name, source, - db_instance_class, - availability_zone, port, - auto_minor_version_upgrade, - iops, option_group_name, - publicly_accessible, - tags, region, key, - keyid, profile) + created = __salt__["boto_rds.create_read_replica"]( + name, + source, + db_instance_class, + availability_zone, + port, + auto_minor_version_upgrade, + iops, + option_group_name, + publicly_accessible, + tags, + region, + key, + keyid, + profile, + ) if created: - ret['comment'] = 'RDS replica {0} created.'.format(name) - ret['changes']['old'] = {'instance': None} - ret['changes']['new'] = { - 'instance': __salt__['boto_rds.describe_db_instances']( - name=name, jmespath='DBInstances[0]', region=region, - key=key, keyid=keyid, profile=profile)} + ret["comment"] = "RDS replica {0} created.".format(name) + ret["changes"]["old"] = {"instance": None} + ret["changes"]["new"] = { + "instance": __salt__["boto_rds.describe_db_instances"]( + name=name, + jmespath="DBInstances[0]", + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + } else: - ret['result'] = False - ret['comment'] = 'Failed to create RDS replica {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create RDS replica {0}.".format(name) else: - jmespath = 'DBInstances[0].DBParameterGroups[0].DBParameterGroupName' - pmg_name = __salt__['boto_rds.describe_db_instances'](name=name, - jmespath=jmespath, region=region, key=key, keyid=keyid, - profile=profile) + jmespath = "DBInstances[0].DBParameterGroups[0].DBParameterGroupName" + pmg_name = __salt__["boto_rds.describe_db_instances"]( + name=name, + jmespath=jmespath, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) pmg_name = pmg_name[0] if pmg_name else None if pmg_name != db_parameter_group_name: - modified = __salt__['boto_rds.modify_db_instance']( - name=name, db_parameter_group_name=db_parameter_group_name, - region=region, key=key, keyid=keyid, profile=profile) + modified = __salt__["boto_rds.modify_db_instance"]( + name=name, + db_parameter_group_name=db_parameter_group_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not modified: - ret['result'] = False - ret['comment'] = ('Failed to update parameter group of {0} RDS ' - 'instance.'.format(name)) - ret['changes']['old'] = pmg_name - ret['changes']['new'] = db_parameter_group_name - ret['result'] = True - ret['comment'] = 'RDS replica {0} exists.'.format(name) + ret["result"] = False + ret["comment"] = ( + "Failed to update parameter group of {0} RDS " + "instance.".format(name) + ) + ret["changes"]["old"] = pmg_name + ret["changes"]["new"] = db_parameter_group_name + ret["result"] = True + ret["comment"] = "RDS replica {0} exists.".format(name) return ret -def subnet_group_present(name, description, subnet_ids=None, subnet_names=None, - tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def subnet_group_present( + name, + description, + subnet_ids=None, + subnet_names=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure DB subnet group exists. name @@ -463,70 +530,81 @@ def subnet_group_present(name, description, subnet_ids=None, subnet_names=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ if not salt.utils.data.exactly_one((subnet_ids, subnet_names)): - raise SaltInvocationError('One (but not both) of subnet_ids or ' - 'subnet_names must be provided.') + raise SaltInvocationError( + "One (but not both) of subnet_ids or " "subnet_names must be provided." + ) - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not subnet_ids: subnet_ids = [] if subnet_names: for i in subnet_names: - r = __salt__['boto_vpc.get_resource_id']('subnet', name=i, - region=region, - key=key, keyid=keyid, - profile=profile) + r = __salt__["boto_vpc.get_resource_id"]( + "subnet", name=i, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['comment'] = 'Error looking up subnet ids: {0}'.format( - r['error']['message']) - ret['result'] = False + if "error" in r: + ret["comment"] = "Error looking up subnet ids: {0}".format( + r["error"]["message"] + ) + ret["result"] = False return ret - if r['id'] is None: - ret['comment'] = 'Subnet {0} does not exist.'.format(i) - ret['result'] = False + if r["id"] is None: + ret["comment"] = "Subnet {0} does not exist.".format(i) + ret["result"] = False return ret - subnet_ids.append(r['id']) + subnet_ids.append(r["id"]) - exists = __salt__['boto_rds.subnet_group_exists'](name=name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile) - if not exists.get('exists'): - if __opts__['test']: - ret['comment'] = 'Subnet group {0} is set to be created.'.format(name) - ret['result'] = None + exists = __salt__["boto_rds.subnet_group_exists"]( + name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile + ) + if not exists.get("exists"): + if __opts__["test"]: + ret["comment"] = "Subnet group {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_rds.create_subnet_group'](name=name, - description=description, - subnet_ids=subnet_ids, - tags=tags, region=region, - key=key, keyid=keyid, - profile=profile) + created = __salt__["boto_rds.create_subnet_group"]( + name=name, + description=description, + subnet_ids=subnet_ids, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not created: - ret['result'] = False - ret['comment'] = 'Failed to create {0} subnet group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} subnet group.".format(name) return ret - ret['changes']['old'] = None - ret['changes']['new'] = name - ret['comment'] = 'Subnet {0} created.'.format(name) + ret["changes"]["old"] = None + ret["changes"]["new"] = name + ret["comment"] = "Subnet {0} created.".format(name) return ret else: - ret['comment'] = 'Subnet {0} present.'.format(name) + ret["comment"] = "Subnet {0} present.".format(name) return ret -def absent(name, skip_final_snapshot=None, final_db_snapshot_identifier=None, - tags=None, wait_for_deletion=True, timeout=180, - region=None, key=None, keyid=None, profile=None): - ''' +def absent( + name, + skip_final_snapshot=None, + final_db_snapshot_identifier=None, + tags=None, + wait_for_deletion=True, + timeout=180, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure RDS instance is absent. name @@ -563,70 +641,86 @@ def absent(name, skip_final_snapshot=None, final_db_snapshot_identifier=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - current = __salt__['boto_rds.describe_db_instances']( - name=name, region=region, key=key, keyid=keyid, profile=profile) + current = __salt__["boto_rds.describe_db_instances"]( + name=name, region=region, key=key, keyid=keyid, profile=profile + ) if not current: - ret['result'] = True - ret['comment'] = '{0} RDS already absent.'.format(name) + ret["result"] = True + ret["comment"] = "{0} RDS already absent.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'RDS {0} would be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "RDS {0} would be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_rds.delete'](name, skip_final_snapshot, - final_db_snapshot_identifier, - region, key, keyid, profile, tags, - wait_for_deletion, timeout) + deleted = __salt__["boto_rds.delete"]( + name, + skip_final_snapshot, + final_db_snapshot_identifier, + region, + key, + keyid, + profile, + tags, + wait_for_deletion, + timeout, + ) if not deleted: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} RDS.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} RDS.".format(name) return ret - ret['changes']['old'] = {'instance': current[0]} - ret['changes']['new'] = {'instance': None} - ret['comment'] = 'RDS {0} deleted.'.format(name) + ret["changes"]["old"] = {"instance": current[0]} + ret["changes"]["new"] = {"instance": None} + ret["comment"] = "RDS {0} deleted.".format(name) return ret -def subnet_group_absent(name, tags=None, region=None, key=None, keyid=None, profile=None): - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } +def subnet_group_absent( + name, tags=None, region=None, key=None, keyid=None, profile=None +): + ret = {"name": name, "result": True, "comment": "", "changes": {}} - exists = __salt__['boto_rds.subnet_group_exists'](name=name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile) + exists = __salt__["boto_rds.subnet_group_exists"]( + name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile + ) if not exists: - ret['result'] = True - ret['comment'] = '{0} RDS subnet group does not exist.'.format(name) + ret["result"] = True + ret["comment"] = "{0} RDS subnet group does not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'RDS subnet group {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "RDS subnet group {0} is set to be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_rds.delete_subnet_group'](name, region, key, keyid, profile) + deleted = __salt__["boto_rds.delete_subnet_group"]( + name, region, key, keyid, profile + ) if not deleted: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} RDS subnet group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} RDS subnet group.".format(name) return ret - ret['changes']['old'] = name - ret['changes']['new'] = None - ret['comment'] = 'RDS subnet group {0} deleted.'.format(name) + ret["changes"]["old"] = name + ret["changes"]["new"] = None + ret["comment"] = "RDS subnet group {0} deleted.".format(name) return ret -def parameter_present(name, db_parameter_group_family, description, parameters=None, - apply_method="pending-reboot", tags=None, region=None, key=None, keyid=None, profile=None): - ''' +def parameter_present( + name, + db_parameter_group_family, + description, + parameters=None, + apply_method="pending-reboot", + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure DB parameter group exists and update parameters. name @@ -668,67 +762,112 @@ def parameter_present(name, db_parameter_group_family, description, parameters=N profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } - res = __salt__['boto_rds.parameter_group_exists'](name=name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile) - if not res.get('exists'): - if __opts__['test']: - ret['comment'] = 'Parameter group {0} is set to be created.'.format(name) - ret['result'] = None + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + res = __salt__["boto_rds.parameter_group_exists"]( + name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile + ) + if not res.get("exists"): + if __opts__["test"]: + ret["comment"] = "Parameter group {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_rds.create_parameter_group'](name=name, db_parameter_group_family=db_parameter_group_family, - description=description, tags=tags, region=region, - key=key, keyid=keyid, profile=profile) + created = __salt__["boto_rds.create_parameter_group"]( + name=name, + db_parameter_group_family=db_parameter_group_family, + description=description, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not created: - ret['result'] = False - ret['comment'] = 'Failed to create {0} parameter group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} parameter group.".format(name) return ret - ret['changes']['New Parameter Group'] = name - ret['comment'] = 'Parameter group {0} created.'.format(name) + ret["changes"]["New Parameter Group"] = name + ret["comment"] = "Parameter group {0} created.".format(name) else: - ret['comment'] = 'Parameter group {0} present.'.format(name) + ret["comment"] = "Parameter group {0} present.".format(name) if parameters is not None: params = {} changed = {} for items in parameters: for k, value in items.items(): if type(value) is bool: - params[k] = 'on' if value else 'off' + params[k] = "on" if value else "off" else: params[k] = six.text_type(value) - log.debug('Parameters from user are : %s.', params) - options = __salt__['boto_rds.describe_parameters'](name=name, region=region, key=key, keyid=keyid, profile=profile) - if not options.get('result'): - ret['result'] = False - ret['comment'] = os.linesep.join([ret['comment'], 'Faled to get parameters for group {0}.'.format(name)]) + log.debug("Parameters from user are : %s.", params) + options = __salt__["boto_rds.describe_parameters"]( + name=name, region=region, key=key, keyid=keyid, profile=profile + ) + if not options.get("result"): + ret["result"] = False + ret["comment"] = os.linesep.join( + [ret["comment"], "Faled to get parameters for group {0}.".format(name)] + ) return ret - for parameter in options['parameters'].values(): - if parameter['ParameterName'] in params and params.get(parameter['ParameterName']) != six.text_type(parameter['ParameterValue']): + for parameter in options["parameters"].values(): + if parameter["ParameterName"] in params and params.get( + parameter["ParameterName"] + ) != six.text_type(parameter["ParameterValue"]): log.debug( - 'Values that are being compared for %s are %s:%s.', - parameter['ParameterName'], - params.get(parameter['ParameterName']), - parameter['ParameterValue'] + "Values that are being compared for %s are %s:%s.", + parameter["ParameterName"], + params.get(parameter["ParameterName"]), + parameter["ParameterValue"], + ) + changed[parameter["ParameterName"]] = params.get( + parameter["ParameterName"] ) - changed[parameter['ParameterName']] = params.get(parameter['ParameterName']) if len(changed) > 0: - if __opts__['test']: - ret['comment'] = os.linesep.join([ret['comment'], 'Parameters {0} for group {1} are set to be changed.'.format(changed, name)]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = os.linesep.join( + [ + ret["comment"], + "Parameters {0} for group {1} are set to be changed.".format( + changed, name + ), + ] + ) + ret["result"] = None return ret - update = __salt__['boto_rds.update_parameter_group'](name, parameters=changed, apply_method=apply_method, tags=tags, region=region, - key=key, keyid=keyid, profile=profile) - if 'error' in update: - ret['result'] = False - ret['comment'] = os.linesep.join([ret['comment'], 'Failed to change parameters {0} for group {1}:'.format(changed, name), update['error']['message']]) + update = __salt__["boto_rds.update_parameter_group"]( + name, + parameters=changed, + apply_method=apply_method, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in update: + ret["result"] = False + ret["comment"] = os.linesep.join( + [ + ret["comment"], + "Failed to change parameters {0} for group {1}:".format( + changed, name + ), + update["error"]["message"], + ] + ) return ret - ret['changes']['Parameters'] = changed - ret['comment'] = os.linesep.join([ret['comment'], 'Parameters {0} for group {1} are changed.'.format(changed, name)]) + ret["changes"]["Parameters"] = changed + ret["comment"] = os.linesep.join( + [ + ret["comment"], + "Parameters {0} for group {1} are changed.".format(changed, name), + ] + ) else: - ret['comment'] = os.linesep.join([ret['comment'], 'Parameters {0} for group {1} are present.'.format(params, name)]) + ret["comment"] = os.linesep.join( + [ + ret["comment"], + "Parameters {0} for group {1} are present.".format(params, name), + ] + ) return ret diff --git a/salt/states/boto_route53.py b/salt/states/boto_route53.py index 11a04284c02..4e81832d323 100644 --- a/salt/states/boto_route53.py +++ b/salt/states/boto_route53.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Route53 records .. versionadded:: 2014.7.0 @@ -69,36 +69,52 @@ passed in as a dict, or as a string to pull from pillars or minion config: - profile: keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging import uuid # Import Salt Libs import salt.utils.data import salt.utils.json -from salt.ext import six from salt.exceptions import SaltInvocationError +from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_route53' if 'boto_route53.get_record' in __salt__ else False + """ + if "boto_route53.get_record" in __salt__: + return "boto_route53" + return (False, "boto_route53 module could not be loaded") def rr_present(*args, **kwargs): return present(*args, **kwargs) -def present(name, value, zone, record_type, ttl=None, identifier=None, region=None, key=None, - keyid=None, profile=None, wait_for_sync=True, split_dns=False, private_zone=False): - ''' +def present( + name, + value, + zone, + record_type, + ttl=None, + identifier=None, + region=None, + key=None, + keyid=None, + profile=None, + wait_for_sync=True, + split_dns=False, + private_zone=False, +): + """ Ensure the Route53 record is present. name @@ -142,73 +158,101 @@ def present(name, value, zone, record_type, ttl=None, identifier=None, region=No private_zone If using split_dns, specify if this is the private zone. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # If a list is passed in for value, change it to a comma-separated string # So it will work with subsequent boto module calls and string functions if isinstance(value, list): - value = ','.join(value) - elif value.startswith('private:') or value.startswith('public:'): - name_tag = value.split(':', 1)[1] - in_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped') - r = __salt__['boto_ec2.find_instances'](name=name_tag, - return_objs=True, - in_states=in_states, - profile=profile) + value = ",".join(value) + elif value.startswith("private:") or value.startswith("public:"): + name_tag = value.split(":", 1)[1] + in_states = ("pending", "rebooting", "running", "stopping", "stopped") + r = __salt__["boto_ec2.find_instances"]( + name=name_tag, return_objs=True, in_states=in_states, profile=profile + ) if len(r) < 1: - ret['comment'] = 'Error: instance with Name tag {0} not found'.format(name_tag) - ret['result'] = False + ret["comment"] = "Error: instance with Name tag {0} not found".format( + name_tag + ) + ret["result"] = False return ret if len(r) > 1: - ret['comment'] = 'Error: Name tag {0} matched more than one instance'.format(name_tag) - ret['result'] = False + ret[ + "comment" + ] = "Error: Name tag {0} matched more than one instance".format(name_tag) + ret["result"] = False return ret instance = r[0] - private_ip = getattr(instance, 'private_ip_address', None) - public_ip = getattr(instance, 'ip_address', None) - if value.startswith('private:'): + private_ip = getattr(instance, "private_ip_address", None) + public_ip = getattr(instance, "ip_address", None) + if value.startswith("private:"): value = private_ip - log.info('Found private IP %s for instance %s', private_ip, name_tag) + log.info("Found private IP %s for instance %s", private_ip, name_tag) else: if public_ip is None: - ret['comment'] = 'Error: No Public IP assigned to instance with Name {0}'.format(name_tag) - ret['result'] = False + ret[ + "comment" + ] = "Error: No Public IP assigned to instance with Name {0}".format( + name_tag + ) + ret["result"] = False return ret value = public_ip - log.info('Found public IP %s for instance %s', public_ip, name_tag) + log.info("Found public IP %s for instance %s", public_ip, name_tag) try: - record = __salt__['boto_route53.get_record'](name, zone, record_type, - False, region, key, keyid, - profile, split_dns, - private_zone, identifier) + record = __salt__["boto_route53.get_record"]( + name, + zone, + record_type, + False, + region, + key, + keyid, + profile, + split_dns, + private_zone, + identifier, + ) except SaltInvocationError as err: - ret['comment'] = 'Error: {0}'.format(err) - ret['result'] = False + ret["comment"] = "Error: {0}".format(err) + ret["result"] = False return ret if isinstance(record, dict) and not record: - if __opts__['test']: - ret['comment'] = 'Route53 record {0} set to be added.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route53 record {0} set to be added.".format(name) + ret["result"] = None return ret - added = __salt__['boto_route53.add_record'](name, value, zone, - record_type, identifier, - ttl, region, key, keyid, - profile, wait_for_sync, - split_dns, private_zone) + added = __salt__["boto_route53.add_record"]( + name, + value, + zone, + record_type, + identifier, + ttl, + region, + key, + keyid, + profile, + wait_for_sync, + split_dns, + private_zone, + ) if added: - ret['changes']['old'] = None - ret['changes']['new'] = {'name': name, - 'value': value, - 'record_type': record_type, - 'ttl': ttl, - 'identifier': identifier} - ret['comment'] = 'Added {0} Route53 record.'.format(name) + ret["changes"]["old"] = None + ret["changes"]["new"] = { + "name": name, + "value": value, + "record_type": record_type, + "ttl": ttl, + "identifier": identifier, + } + ret["comment"] = "Added {0} Route53 record.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to add {0} Route53 record.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to add {0} Route53 record.".format(name) return ret elif record: need_to_update = False @@ -217,42 +261,51 @@ def present(name, value, zone, record_type, ttl=None, identifier=None, region=No # to split and check with the period stripped from the input and what's # in route53. # TODO: figure out if this will cause us problems with some records. - _values = [x.rstrip('.') for x in value.split(',')] - _r_values = [x.rstrip('.') for x in record['value'].split(',')] + _values = [x.rstrip(".") for x in value.split(",")] + _r_values = [x.rstrip(".") for x in record["value"].split(",")] _values.sort() _r_values.sort() if _values != _r_values: need_to_update = True - if identifier and identifier != record['identifier']: + if identifier and identifier != record["identifier"]: need_to_update = True - if ttl and six.text_type(ttl) != six.text_type(record['ttl']): + if ttl and six.text_type(ttl) != six.text_type(record["ttl"]): need_to_update = True if need_to_update: - if __opts__['test']: - ret['comment'] = 'Route53 record {0} set to be updated.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route53 record {0} set to be updated.".format(name) + ret["result"] = None return ret - updated = __salt__['boto_route53.update_record'](name, value, zone, - record_type, - identifier, ttl, - region, key, - keyid, profile, - wait_for_sync, - split_dns, - private_zone) + updated = __salt__["boto_route53.update_record"]( + name, + value, + zone, + record_type, + identifier, + ttl, + region, + key, + keyid, + profile, + wait_for_sync, + split_dns, + private_zone, + ) if updated: - ret['changes']['old'] = record - ret['changes']['new'] = {'name': name, - 'value': value, - 'record_type': record_type, - 'ttl': ttl, - 'identifier': identifier} - ret['comment'] = 'Updated {0} Route53 record.'.format(name) + ret["changes"]["old"] = record + ret["changes"]["new"] = { + "name": name, + "value": value, + "record_type": record_type, + "ttl": ttl, + "identifier": identifier, + } + ret["comment"] = "Updated {0} Route53 record.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to update {0} Route53 record.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to update {0} Route53 record.".format(name) else: - ret['comment'] = '{0} exists.'.format(name) + ret["comment"] = "{0} exists.".format(name) return ret @@ -261,18 +314,19 @@ def rr_absent(*args, **kwargs): def absent( - name, - zone, - record_type, - identifier=None, - region=None, - key=None, - keyid=None, - profile=None, - wait_for_sync=True, - split_dns=False, - private_zone=False): - ''' + name, + zone, + record_type, + identifier=None, + region=None, + key=None, + keyid=None, + profile=None, + wait_for_sync=True, + split_dns=False, + private_zone=False, +): + """ Ensure the Route53 record is deleted. name @@ -309,42 +363,68 @@ def absent( private_zone If using split_dns, specify if this is the private zone. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - record = __salt__['boto_route53.get_record'](name, zone, record_type, - False, region, key, keyid, - profile, split_dns, - private_zone, identifier) + record = __salt__["boto_route53.get_record"]( + name, + zone, + record_type, + False, + region, + key, + keyid, + profile, + split_dns, + private_zone, + identifier, + ) if record: - if __opts__['test']: - ret['comment'] = 'Route53 record {0} set to be deleted.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route53 record {0} set to be deleted.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_route53.delete_record'](name, zone, - record_type, - identifier, False, - region, key, keyid, - profile, - wait_for_sync, - split_dns, - private_zone) + deleted = __salt__["boto_route53.delete_record"]( + name, + zone, + record_type, + identifier, + False, + region, + key, + keyid, + profile, + wait_for_sync, + split_dns, + private_zone, + ) if deleted: - ret['changes']['old'] = record - ret['changes']['new'] = None - ret['comment'] = 'Deleted {0} Route53 record.'.format(name) + ret["changes"]["old"] = record + ret["changes"]["new"] = None + ret["comment"] = "Deleted {0} Route53 record.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} Route53 record.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} Route53 record.".format(name) else: - ret['comment'] = '{0} does not exist.'.format(name) + ret["comment"] = "{0} does not exist.".format(name) return ret -def hosted_zone_present(name, domain_name=None, private_zone=False, caller_ref=None, comment='', - vpc_id=None, vpc_name=None, vpc_region=None, region=None, key=None, - keyid=None, profile=None): - ''' +def hosted_zone_present( + name, + domain_name=None, + private_zone=False, + caller_ref=None, + comment="", + vpc_id=None, + vpc_name=None, + vpc_region=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure a hosted zone exists with the given attributes. Note that most things cannot be modified once a zone is created - it must be deleted and re-spun to update these attributes: @@ -395,103 +475,124 @@ def hosted_zone_present(name, domain_name=None, private_zone=False, caller_ref=N provided, an effort will be made to determine it from vpc_id or vpc_name, where possible. If this fails, you'll need to provide an explicit value for this option. Ignored when creating a non-private zone. - ''' + """ domain_name = domain_name if domain_name else name - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} # First translaste vpc_name into a vpc_id if possible if private_zone: if not salt.utils.data.exactly_one((vpc_name, vpc_id)): - raise SaltInvocationError('Either vpc_name or vpc_id is required when creating a ' - 'private zone.') - vpcs = __salt__['boto_vpc.describe_vpcs']( - vpc_id=vpc_id, name=vpc_name, region=region, key=key, - keyid=keyid, profile=profile).get('vpcs', []) + raise SaltInvocationError( + "Either vpc_name or vpc_id is required when creating a " "private zone." + ) + vpcs = __salt__["boto_vpc.describe_vpcs"]( + vpc_id=vpc_id, + name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ).get("vpcs", []) if vpc_region and vpcs: - vpcs = [v for v in vpcs if v['region'] == vpc_region] + vpcs = [v for v in vpcs if v["region"] == vpc_region] if not vpcs: - msg = 'Private zone requested but a VPC matching given criteria not found.' + msg = "Private zone requested but a VPC matching given criteria not found." log.error(msg) - ret['comment'] = msg - ret['result'] = False + ret["comment"] = msg + ret["result"] = False return ret if len(vpcs) > 1: log.error( - 'Private zone requested but multiple VPCs matching given ' - 'criteria found: %s', [v['id'] for v in vpcs] + "Private zone requested but multiple VPCs matching given " + "criteria found: %s", + [v["id"] for v in vpcs], ) return None vpc = vpcs[0] if vpc_name: - vpc_id = vpc['id'] + vpc_id = vpc["id"] if not vpc_region: - vpc_region = vpc['region'] + vpc_region = vpc["region"] # Next, see if it (or they) exist at all, anywhere? - deets = __salt__['boto_route53.describe_hosted_zones']( - domain_name=domain_name, region=region, key=key, keyid=keyid, - profile=profile) + deets = __salt__["boto_route53.describe_hosted_zones"]( + domain_name=domain_name, region=region, key=key, keyid=keyid, profile=profile + ) create = False if not deets: create = True else: # Something exists - now does it match our criteria? - if (salt.utils.json.loads(deets['HostedZone']['Config']['PrivateZone']) != - private_zone): + if ( + salt.utils.json.loads(deets["HostedZone"]["Config"]["PrivateZone"]) + != private_zone + ): create = True else: if private_zone: - for v, d in deets.get('VPCs', {}).items(): - if (d['VPCId'] == vpc_id - and d['VPCRegion'] == vpc_region): + for v, d in deets.get("VPCs", {}).items(): + if d["VPCId"] == vpc_id and d["VPCRegion"] == vpc_region: create = False break else: create = True if not create: - ret['comment'] = 'Hostd Zone {0} already in desired state'.format( - domain_name) + ret["comment"] = "Hostd Zone {0} already in desired state".format( + domain_name + ) else: # Until we get modifies in place with boto3, the best option is to # attempt creation and let route53 tell us if we're stepping on # toes. We can't just fail, because some scenarios (think split # horizon DNS) require zones with identical names but different # settings... - log.info('A Hosted Zone with name %s already exists, but with ' - 'different settings. Will attempt to create the one ' - 'requested on the assumption this is what is desired. ' - 'This may fail...', domain_name) + log.info( + "A Hosted Zone with name %s already exists, but with " + "different settings. Will attempt to create the one " + "requested on the assumption this is what is desired. " + "This may fail...", + domain_name, + ) if create: if caller_ref is None: caller_ref = six.text_type(uuid.uuid4()) - if __opts__['test']: - ret['comment'] = 'Route53 Hosted Zone {0} set to be added.'.format( - domain_name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route53 Hosted Zone {0} set to be added.".format( + domain_name + ) + ret["result"] = None return ret - res = __salt__['boto_route53.create_hosted_zone'](domain_name=domain_name, - caller_ref=caller_ref, comment=comment, private_zone=private_zone, - vpc_id=vpc_id, vpc_region=vpc_region, region=region, key=key, - keyid=keyid, profile=profile) + res = __salt__["boto_route53.create_hosted_zone"]( + domain_name=domain_name, + caller_ref=caller_ref, + comment=comment, + private_zone=private_zone, + vpc_id=vpc_id, + vpc_region=vpc_region, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if res: - msg = 'Hosted Zone {0} successfully created'.format(domain_name) + msg = "Hosted Zone {0} successfully created".format(domain_name) log.info(msg) - ret['comment'] = msg - ret['changes']['old'] = None - ret['changes']['new'] = res + ret["comment"] = msg + ret["changes"]["old"] = None + ret["changes"]["new"] = res else: - ret['comment'] = 'Creating Hosted Zone {0} failed'.format( - domain_name) - ret['result'] = False + ret["comment"] = "Creating Hosted Zone {0} failed".format(domain_name) + ret["result"] = False return ret -def hosted_zone_absent(name, domain_name=None, region=None, key=None, - keyid=None, profile=None): - ''' +def hosted_zone_absent( + name, domain_name=None, region=None, key=None, keyid=None, profile=None +): + """ Ensure the Route53 Hostes Zone described is absent name @@ -501,32 +602,33 @@ def hosted_zone_absent(name, domain_name=None, region=None, key=None, The FQDN (including final period) of the zone you wish absent. If not provided, the value of name will be used. - ''' + """ domain_name = domain_name if domain_name else name - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - deets = __salt__['boto_route53.describe_hosted_zones']( - domain_name=domain_name, region=region, key=key, keyid=keyid, - profile=profile) + deets = __salt__["boto_route53.describe_hosted_zones"]( + domain_name=domain_name, region=region, key=key, keyid=keyid, profile=profile + ) if not deets: - ret['comment'] = 'Hosted Zone {0} already absent'.format(domain_name) - log.info(ret['comment']) + ret["comment"] = "Hosted Zone {0} already absent".format(domain_name) + log.info(ret["comment"]) return ret - if __opts__['test']: - ret['comment'] = 'Route53 Hosted Zone {0} set to be deleted.'.format( - domain_name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route53 Hosted Zone {0} set to be deleted.".format( + domain_name + ) + ret["result"] = None return ret # Not entirely comfortable with this - no safety checks around pub/priv, VPCs # or anything else. But this is all the module function exposes, so hmph. # Inclined to put it on the "wait 'til we port to boto3" pile in any case :) - if __salt__['boto_route53.delete_zone']( - zone=domain_name, region=region, key=key, keyid=keyid, - profile=profile): - ret['comment'] = 'Route53 Hosted Zone {0} deleted'.format(domain_name) - log.info(ret['comment']) - ret['changes']['old'] = deets - ret['changes']['new'] = None + if __salt__["boto_route53.delete_zone"]( + zone=domain_name, region=region, key=key, keyid=keyid, profile=profile + ): + ret["comment"] = "Route53 Hosted Zone {0} deleted".format(domain_name) + log.info(ret["comment"]) + ret["changes"]["old"] = deets + ret["changes"]["new"] = None return ret diff --git a/salt/states/boto_s3.py b/salt/states/boto_s3.py index 49e77510cf6..bb9b0bf3212 100644 --- a/salt/states/boto_s3.py +++ b/salt/states/boto_s3.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Manage S3 Resources -================= +=================== .. versionadded:: 2018.3.0 @@ -47,10 +47,11 @@ config: - profile: my-profile :depends: boto3 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import copy import difflib import logging @@ -63,48 +64,49 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - if 'boto_s3.get_object_metadata' not in __salt__: - return False - return 'boto_s3' + """ + if "boto_s3.get_object_metadata" not in __salt__: + return (False, "boto_s3 module could not be loaded") + return "boto_s3" # Keys for `extra_args` that we support. # Currently, this excludes the `ACL` and `Grant*` keys. # Most keys are stored and returned by AWS: -STORED_EXTRA_ARGS = frozenset([ - 'CacheControl', - 'ContentDisposition', - 'ContentEncoding', - 'ContentLanguage', - 'ContentType', - 'Expires', - 'Metadata', - 'ServerSideEncryption', - 'SSECustomerAlgorithm', - 'SSECustomerKeyMD5', - 'SSEKMSKeyId', - 'StorageClass', - 'WebsiteRedirectLocation', -]) +STORED_EXTRA_ARGS = frozenset( + [ + "CacheControl", + "ContentDisposition", + "ContentEncoding", + "ContentLanguage", + "ContentType", + "Expires", + "Metadata", + "ServerSideEncryption", + "SSECustomerAlgorithm", + "SSECustomerKeyMD5", + "SSEKMSKeyId", + "StorageClass", + "WebsiteRedirectLocation", + ] +) # However, some keys are only specified on upload, # but won't be stored/returned by AWS as metadata: -UPLOAD_ONLY_EXTRA_ARGS = frozenset([ - # AWS doesn't store customer provided keys, - # can use SSECustomerKeyMD5 to check for correct key - 'SSECustomerKey', - 'RequestPayer', -]) +UPLOAD_ONLY_EXTRA_ARGS = frozenset( + [ + # AWS doesn't store customer provided keys, + # can use SSECustomerKeyMD5 to check for correct key + "SSECustomerKey", + "RequestPayer", + ] +) # Some extra args must also be passed along to retrive metadata, # namely SSE-C (customer-provided encryption) and RequestPayer args. -GET_METADATA_EXTRA_ARGS = frozenset([ - 'SSECustomerAlgorithm', - 'SSECustomerKey', - 'SSECustomerKeyMD5', - 'RequestPayer', -]) +GET_METADATA_EXTRA_ARGS = frozenset( + ["SSECustomerAlgorithm", "SSECustomerKey", "SSECustomerKeyMD5", "RequestPayer"] +) def object_present( @@ -112,13 +114,13 @@ def object_present( source=None, hash_type=None, extra_args=None, - extra_args_from_pillar='boto_s3_object_extra_args', + extra_args_from_pillar="boto_s3_object_extra_args", region=None, key=None, keyid=None, profile=None, ): - ''' + """ Ensure object exists in S3. name @@ -162,73 +164,69 @@ def object_present( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ ret = { - 'name': name, - 'comment': '', - 'changes': {}, + "name": name, + "comment": "", + "changes": {}, } if extra_args is None: extra_args = {} combined_extra_args = copy.deepcopy( - __salt__['config.option'](extra_args_from_pillar, {}) + __salt__["config.option"](extra_args_from_pillar, {}) ) - __utils__['dictupdate.update'](combined_extra_args, extra_args) + __utils__["dictupdate.update"](combined_extra_args, extra_args) if combined_extra_args: supported_args = STORED_EXTRA_ARGS | UPLOAD_ONLY_EXTRA_ARGS combined_extra_args_keys = frozenset(six.iterkeys(combined_extra_args)) extra_keys = combined_extra_args_keys - supported_args if extra_keys: - msg = 'extra_args keys {0} are not supported'.format(extra_keys) - return {'error': msg} + msg = "extra_args keys {0} are not supported".format(extra_keys) + return {"error": msg} # Get the hash of the local file if not hash_type: - hash_type = __opts__['hash_type'] + hash_type = __opts__["hash_type"] try: digest = salt.utils.hashutils.get_hash(source, form=hash_type) except IOError as e: - ret['result'] = False - ret['comment'] = "Could not read local file {0}: {1}".format( - source, - e, - ) + ret["result"] = False + ret["comment"] = "Could not read local file {0}: {1}".format(source, e,) return ret except ValueError as e: # Invalid hash type exception from get_hash - ret['result'] = False - ret['comment'] = 'Could not hash local file {0}: {1}'.format( - source, - e, - ) + ret["result"] = False + ret["comment"] = "Could not hash local file {0}: {1}".format(source, e,) return ret - HASH_METADATA_KEY = 'salt_managed_content_hash' - combined_extra_args.setdefault('Metadata', {}) - if HASH_METADATA_KEY in combined_extra_args['Metadata']: + HASH_METADATA_KEY = "salt_managed_content_hash" + combined_extra_args.setdefault("Metadata", {}) + if HASH_METADATA_KEY in combined_extra_args["Metadata"]: # Be lenient, silently allow hash metadata key if digest value matches - if combined_extra_args['Metadata'][HASH_METADATA_KEY] != digest: - ret['result'] = False - ret['comment'] = ( - 'Salt uses the {0} metadata key internally,' - 'do not pass it to the boto_s3.object_present state.' + if combined_extra_args["Metadata"][HASH_METADATA_KEY] != digest: + ret["result"] = False + ret["comment"] = ( + "Salt uses the {0} metadata key internally," + "do not pass it to the boto_s3.object_present state." ).format(HASH_METADATA_KEY) return ret - combined_extra_args['Metadata'][HASH_METADATA_KEY] = digest + combined_extra_args["Metadata"][HASH_METADATA_KEY] = digest # Remove upload-only keys from full set of extra_args # to create desired dict for comparisons desired_metadata = dict( - (k, v) for k, v in six.iteritems(combined_extra_args) + (k, v) + for k, v in six.iteritems(combined_extra_args) if k not in UPLOAD_ONLY_EXTRA_ARGS ) # Some args (SSE-C, RequestPayer) must also be passed to get_metadata metadata_extra_args = dict( - (k, v) for k, v in six.iteritems(combined_extra_args) + (k, v) + for k, v in six.iteritems(combined_extra_args) if k in GET_METADATA_EXTRA_ARGS ) - r = __salt__['boto_s3.get_object_metadata']( + r = __salt__["boto_s3.get_object_metadata"]( name, extra_args=metadata_extra_args, region=region, @@ -236,56 +234,54 @@ def object_present( keyid=keyid, profile=profile, ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to check if S3 object exists: {0}.'.format( - r['error'], - ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to check if S3 object exists: {0}.".format(r["error"],) return ret - if r['result']: + if r["result"]: # Check if content and metadata match # A hash of the content is injected into the metadata, # so we can combine both checks into one # Only check metadata keys specified by the user, # ignore other fields that have been set s3_metadata = dict( - (k, r['result'][k]) for k in STORED_EXTRA_ARGS - if k in desired_metadata and k in r['result'] + (k, r["result"][k]) + for k in STORED_EXTRA_ARGS + if k in desired_metadata and k in r["result"] ) if s3_metadata == desired_metadata: - ret['result'] = True - ret['comment'] = 'S3 object {0} is present.'.format(name) + ret["result"] = True + ret["comment"] = "S3 object {0} is present.".format(name) return ret - action = 'update' + action = "update" else: s3_metadata = None - action = 'create' + action = "create" def _yaml_safe_dump(attrs): - ''' + """ Safely dump YAML using a readable flow style - ''' - dumper_name = 'IndentedSafeOrderedDumper' - dumper = __utils__['yaml.get_dumper'](dumper_name) - return __utils__['yaml.dump']( - attrs, - default_flow_style=False, - Dumper=dumper) + """ + dumper_name = "IndentedSafeOrderedDumper" + dumper = __utils__["yaml.get_dumper"](dumper_name) + return __utils__["yaml.dump"](attrs, default_flow_style=False, Dumper=dumper) - changes_diff = ''.join(difflib.unified_diff( - _yaml_safe_dump(s3_metadata).splitlines(True), - _yaml_safe_dump(desired_metadata).splitlines(True), - )) + changes_diff = "".join( + difflib.unified_diff( + _yaml_safe_dump(s3_metadata).splitlines(True), + _yaml_safe_dump(desired_metadata).splitlines(True), + ) + ) - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'S3 object {0} set to be {1}d.'.format(name, action) - ret['comment'] += '\nChanges:\n{0}'.format(changes_diff) - ret['changes'] = {'diff': changes_diff} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "S3 object {0} set to be {1}d.".format(name, action) + ret["comment"] += "\nChanges:\n{0}".format(changes_diff) + ret["changes"] = {"diff": changes_diff} return ret - r = __salt__['boto_s3.upload_file']( + r = __salt__["boto_s3.upload_file"]( source, name, extra_args=combined_extra_args, @@ -295,16 +291,13 @@ def object_present( profile=profile, ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to {0} S3 object: {1}.'.format( - action, - r['error'], - ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to {0} S3 object: {1}.".format(action, r["error"],) return ret - ret['result'] = True - ret['comment'] = 'S3 object {0} {1}d.'.format(name, action) - ret['comment'] += '\nChanges:\n{0}'.format(changes_diff) - ret['changes'] = {'diff': changes_diff} + ret["result"] = True + ret["comment"] = "S3 object {0} {1}d.".format(name, action) + ret["comment"] += "\nChanges:\n{0}".format(changes_diff) + ret["changes"] = {"diff": changes_diff} return ret diff --git a/salt/states/boto_s3_bucket.py b/salt/states/boto_s3_bucket.py index d6e57b72128..0e3b6038f0c 100644 --- a/salt/states/boto_s3_bucket.py +++ b/salt/states/boto_s3_bucket.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage S3 Buckets ================= @@ -136,10 +136,11 @@ config: - keyid: GKTADJGHEIQSXMKKRBJ08H - key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging @@ -153,121 +154,127 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_s3_bucket' if 'boto_s3_bucket.exists' in __salt__ else False + """ + if "boto_s3_bucket.exists" in __salt__: + return "boto_s3_bucket" + return (False, "boto_s3_bucket module could not be loaded") def _normalize_user(user_dict): ret = copy.deepcopy(user_dict) # 'Type' is required as input to the AWS API, but not returned as output. So # we ignore it everywhere. - if 'Type' in ret: - del ret['Type'] + if "Type" in ret: + del ret["Type"] return ret def _get_canonical_id(region, key, keyid, profile): - ret = __salt__['boto_s3_bucket.list']( + ret = __salt__["boto_s3_bucket.list"]( region=region, key=key, keyid=keyid, profile=profile - ).get('Owner') + ).get("Owner") return _normalize_user(ret) def _prep_acl_for_compare(ACL): - ''' + """ Prepares the ACL returned from the AWS API for comparison with a given one. - ''' + """ ret = copy.deepcopy(ACL) - ret['Owner'] = _normalize_user(ret['Owner']) - for item in ret.get('Grants', ()): - item['Grantee'] = _normalize_user(item.get('Grantee')) + ret["Owner"] = _normalize_user(ret["Owner"]) + for item in ret.get("Grants", ()): + item["Grantee"] = _normalize_user(item.get("Grantee")) return ret def _acl_to_grant(ACL, owner_canonical_id): - if 'AccessControlPolicy' in ACL: - ret = copy.deepcopy(ACL['AccessControlPolicy']) - ret['Owner'] = _normalize_user(ret['Owner']) - for item in ACL.get('Grants', ()): - item['Grantee'] = _normalize_user(item.get('Grantee')) + if "AccessControlPolicy" in ACL: + ret = copy.deepcopy(ACL["AccessControlPolicy"]) + ret["Owner"] = _normalize_user(ret["Owner"]) + for item in ACL.get("Grants", ()): + item["Grantee"] = _normalize_user(item.get("Grantee")) # If AccessControlPolicy is set, other options are not allowed return ret owner_canonical_grant = copy.deepcopy(owner_canonical_id) - owner_canonical_grant.update({'Type': 'CanonicalUser'}) - ret = { - 'Grants': [], - 'Owner': owner_canonical_id - } - if 'ACL' in ACL: + owner_canonical_grant.update({"Type": "CanonicalUser"}) + ret = {"Grants": [], "Owner": owner_canonical_id} + if "ACL" in ACL: # This is syntactic sugar; expand it out - acl = ACL['ACL'] - if acl in ('public-read', 'public-read-write'): - ret['Grants'].append({ - 'Grantee': { - 'Type': 'Group', - 'URI': 'http://acs.amazonaws.com/groups/global/AllUsers' - }, - 'Permission': 'READ' - }) - if acl == 'public-read-write': - ret['Grants'].append({ - 'Grantee': { - 'Type': 'Group', - 'URI': 'http://acs.amazonaws.com/groups/global/AllUsers' - }, - 'Permission': 'WRITE' - }) - if acl == 'aws-exec-read': - ret['Grants'].append({ - 'Grantee': { - 'Type': 'CanonicalUser', - 'DisplayName': 'za-team', - 'ID': '6aa5a366c34c1cbe25dc49211496e913e0351eb0e8c37aa3477e40942ec6b97c' - }, - 'Permission': 'READ' - }) - if acl == 'authenticated-read': - ret['Grants'].append({ - 'Grantee': { - 'Type': 'Group', - 'URI': 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers' - }, - 'Permission': 'READ' - }) - if acl == 'log-delivery-write': - for permission in ('WRITE', 'READ_ACP'): - ret['Grants'].append({ - 'Grantee': { - 'Type': 'Group', - 'URI': 'http://acs.amazonaws.com/groups/s3/LogDelivery' + acl = ACL["ACL"] + if acl in ("public-read", "public-read-write"): + ret["Grants"].append( + { + "Grantee": { + "Type": "Group", + "URI": "http://acs.amazonaws.com/groups/global/AllUsers", }, - 'Permission': permission - }) + "Permission": "READ", + } + ) + if acl == "public-read-write": + ret["Grants"].append( + { + "Grantee": { + "Type": "Group", + "URI": "http://acs.amazonaws.com/groups/global/AllUsers", + }, + "Permission": "WRITE", + } + ) + if acl == "aws-exec-read": + ret["Grants"].append( + { + "Grantee": { + "Type": "CanonicalUser", + "DisplayName": "za-team", + "ID": "6aa5a366c34c1cbe25dc49211496e913e0351eb0e8c37aa3477e40942ec6b97c", + }, + "Permission": "READ", + } + ) + if acl == "authenticated-read": + ret["Grants"].append( + { + "Grantee": { + "Type": "Group", + "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers", + }, + "Permission": "READ", + } + ) + if acl == "log-delivery-write": + for permission in ("WRITE", "READ_ACP"): + ret["Grants"].append( + { + "Grantee": { + "Type": "Group", + "URI": "http://acs.amazonaws.com/groups/s3/LogDelivery", + }, + "Permission": permission, + } + ) for key, permission in ( - ('GrantFullControl', 'FULL_CONTROL'), - ('GrantRead', 'READ'), - ('GrantReadACP', 'READ_ACP'), - ('GrantWrite', 'WRITE'), - ('GrantWriteACP', 'WRITE_ACP'), + ("GrantFullControl", "FULL_CONTROL"), + ("GrantRead", "READ"), + ("GrantReadACP", "READ_ACP"), + ("GrantWrite", "WRITE"), + ("GrantWriteACP", "WRITE_ACP"), ): if key in ACL: - for item in ACL[key].split(','): - kind, val = item.split('=') - if kind == 'uri': - grantee = { - 'Type': 'Group', - 'URI': val - } - elif kind == 'id': + for item in ACL[key].split(","): + kind, val = item.split("=") + if kind == "uri": + grantee = {"Type": "Group", "URI": val} + elif kind == "id": grantee = { # No API provides this info, so the result will never # match, and we will always update. Result is still # idempotent # 'DisplayName': ???, - 'Type': 'CanonicalUser', - 'ID': val + "Type": "CanonicalUser", + "ID": val, } else: grantee = { @@ -277,46 +284,42 @@ def _acl_to_grant(ACL, owner_canonical_id): # 'DisplayName': ???, # 'ID': ??? } - ret['Grants'].append({ - 'Grantee': grantee, - 'Permission': permission - }) + ret["Grants"].append({"Grantee": grantee, "Permission": permission}) # Boto only seems to list the default Grants when no other Grants are defined - if not ret['Grants']: - ret['Grants'] = [{ - 'Grantee': owner_canonical_grant, - 'Permission': 'FULL_CONTROL' - }] + if not ret["Grants"]: + ret["Grants"] = [ + {"Grantee": owner_canonical_grant, "Permission": "FULL_CONTROL"} + ] return ret def _get_role_arn(name, region=None, key=None, keyid=None, profile=None): - if name.startswith('arn:aws:iam:'): + if name.startswith("arn:aws:iam:"): return name - account_id = __salt__['boto_iam.get_account_id']( + account_id = __salt__["boto_iam.get_account_id"]( region=region, key=key, keyid=keyid, profile=profile ) - if profile and 'region' in profile: - region = profile['region'] + if profile and "region" in profile: + region = profile["region"] if region is None: - region = 'us-east-1' - return 'arn:aws:iam::{0}:role/{1}'.format(account_id, name) + region = "us-east-1" + return "arn:aws:iam::{0}:role/{1}".format(account_id, name) def _compare_json(current, desired, region, key, keyid, profile): - return __utils__['boto3.json_objs_equal'](current, desired) + return __utils__["boto3.json_objs_equal"](current, desired) def _compare_acl(current, desired, region, key, keyid, profile): - ''' + """ ACLs can be specified using macro-style names that get expanded to something more complex. There's no predictable way to reverse it. So expand all syntactic sugar in our input, and compare against that rather than the input itself. - ''' + """ ocid = _get_canonical_id(region, key, keyid, profile) - return __utils__['boto3.json_objs_equal'](current, _acl_to_grant(desired, ocid)) + return __utils__["boto3.json_objs_equal"](current, _acl_to_grant(desired, ocid)) def _compare_policy(current, desired, region, key, keyid, profile): @@ -324,31 +327,38 @@ def _compare_policy(current, desired, region, key, keyid, profile): def _compare_replication(current, desired, region, key, keyid, profile): - ''' + """ Replication accepts a non-ARN role name, but always returns an ARN - ''' - if desired is not None and desired.get('Role'): + """ + if desired is not None and desired.get("Role"): desired = copy.deepcopy(desired) - desired['Role'] = _get_role_arn(desired['Role'], - region=region, key=key, keyid=keyid, profile=profile) - return __utils__['boto3.json_objs_equal'](current, desired) + desired["Role"] = _get_role_arn( + desired["Role"], region=region, key=key, keyid=keyid, profile=profile + ) + return __utils__["boto3.json_objs_equal"](current, desired) -def present(name, Bucket, - LocationConstraint=None, - ACL=None, - CORSRules=None, - LifecycleConfiguration=None, - Logging=None, - NotificationConfiguration=None, - Policy=None, - Replication=None, - RequestPayment=None, - Tagging=None, - Versioning=None, - Website=None, - region=None, key=None, keyid=None, profile=None): - ''' +def present( + name, + Bucket, + LocationConstraint=None, + ACL=None, + CORSRules=None, + LifecycleConfiguration=None, + Logging=None, + NotificationConfiguration=None, + Policy=None, + Replication=None, + RequestPayment=None, + Tagging=None, + Versioning=None, + Website=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure bucket exists. name @@ -410,129 +420,202 @@ def present(name, Bucket, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': Bucket, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": Bucket, "result": True, "comment": "", "changes": {}} if ACL is None: - ACL = {'ACL': 'private'} + ACL = {"ACL": "private"} if NotificationConfiguration is None: NotificationConfiguration = {} if RequestPayment is None: - RequestPayment = {'Payer': 'BucketOwner'} + RequestPayment = {"Payer": "BucketOwner"} if Policy: if isinstance(Policy, six.string_types): Policy = salt.utils.json.loads(Policy) - Policy = __utils__['boto3.ordered'](Policy) + Policy = __utils__["boto3.ordered"](Policy) - r = __salt__['boto_s3_bucket.exists'](Bucket=Bucket, - region=region, key=key, keyid=keyid, profile=profile) + r = __salt__["boto_s3_bucket.exists"]( + Bucket=Bucket, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create bucket: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create bucket: {0}.".format(r["error"]["message"]) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'S3 bucket {0} is set to be created.'.format(Bucket) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "S3 bucket {0} is set to be created.".format(Bucket) + ret["result"] = None return ret - r = __salt__['boto_s3_bucket.create'](Bucket=Bucket, - LocationConstraint=LocationConstraint, - region=region, key=key, keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create bucket: {0}.'.format(r['error']['message']) + r = __salt__["boto_s3_bucket.create"]( + Bucket=Bucket, + LocationConstraint=LocationConstraint, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create bucket: {0}.".format( + r["error"]["message"] + ) return ret for setter, testval, funcargs in ( - ('put_acl', ACL, ACL), - ('put_cors', CORSRules, {"CORSRules": CORSRules}), - ('put_lifecycle_configuration', LifecycleConfiguration, {"Rules": LifecycleConfiguration}), - ('put_logging', Logging, Logging), - ('put_notification_configuration', NotificationConfiguration, NotificationConfiguration), - ('put_policy', Policy, {"Policy": Policy}), - # versioning must be set before replication - ('put_versioning', Versioning, Versioning), - ('put_replication', Replication, Replication), - ('put_request_payment', RequestPayment, RequestPayment), - ('put_tagging', Tagging, Tagging), - ('put_website', Website, Website), + ("put_acl", ACL, ACL), + ("put_cors", CORSRules, {"CORSRules": CORSRules}), + ( + "put_lifecycle_configuration", + LifecycleConfiguration, + {"Rules": LifecycleConfiguration}, + ), + ("put_logging", Logging, Logging), + ( + "put_notification_configuration", + NotificationConfiguration, + NotificationConfiguration, + ), + ("put_policy", Policy, {"Policy": Policy}), + # versioning must be set before replication + ("put_versioning", Versioning, Versioning), + ("put_replication", Replication, Replication), + ("put_request_payment", RequestPayment, RequestPayment), + ("put_tagging", Tagging, Tagging), + ("put_website", Website, Website), ): if testval is not None: - r = __salt__['boto_s3_bucket.{0}'.format(setter)](Bucket=Bucket, - region=region, key=key, keyid=keyid, profile=profile, - **funcargs) - if not r.get('updated'): - ret['result'] = False - ret['comment'] = 'Failed to create bucket: {0}.'.format(r['error']['message']) + r = __salt__["boto_s3_bucket.{0}".format(setter)]( + Bucket=Bucket, + region=region, + key=key, + keyid=keyid, + profile=profile, + **funcargs + ) + if not r.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to create bucket: {0}.".format( + r["error"]["message"] + ) return ret - _describe = __salt__['boto_s3_bucket.describe'](Bucket, - region=region, key=key, keyid=keyid, profile=profile) - ret['changes']['old'] = {'bucket': None} - ret['changes']['new'] = _describe - ret['comment'] = 'S3 bucket {0} created.'.format(Bucket) + _describe = __salt__["boto_s3_bucket.describe"]( + Bucket, region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["old"] = {"bucket": None} + ret["changes"]["new"] = _describe + ret["comment"] = "S3 bucket {0} created.".format(Bucket) return ret # bucket exists, ensure config matches - ret['comment'] = ' '.join([ret['comment'], 'S3 bucket {0} is present.'.format(Bucket)]) - ret['changes'] = {} - _describe = __salt__['boto_s3_bucket.describe'](Bucket=Bucket, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in _describe: - ret['result'] = False - ret['comment'] = 'Failed to update bucket: {0}.'.format(_describe['error']['message']) - ret['changes'] = {} + ret["comment"] = " ".join( + [ret["comment"], "S3 bucket {0} is present.".format(Bucket)] + ) + ret["changes"] = {} + _describe = __salt__["boto_s3_bucket.describe"]( + Bucket=Bucket, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in _describe: + ret["result"] = False + ret["comment"] = "Failed to update bucket: {0}.".format( + _describe["error"]["message"] + ) + ret["changes"] = {} return ret - _describe = _describe['bucket'] + _describe = _describe["bucket"] # Once versioning has been enabled, it can't completely go away, it can # only be suspended - if not bool(Versioning) and bool(_describe.get('Versioning')): - Versioning = {'Status': 'Suspended'} + if not bool(Versioning) and bool(_describe.get("Versioning")): + Versioning = {"Status": "Suspended"} config_items = [ - ('ACL', 'put_acl', - _describe.get('ACL'), _compare_acl, ACL, - None), - ('CORS', 'put_cors', - _describe.get('CORS'), _compare_json, {"CORSRules": CORSRules} if CORSRules else None, - 'delete_cors'), - ('LifecycleConfiguration', 'put_lifecycle_configuration', - _describe.get('LifecycleConfiguration'), _compare_json, {"Rules": LifecycleConfiguration} if LifecycleConfiguration else None, - 'delete_lifecycle_configuration'), - ('Logging', 'put_logging', - _describe.get('Logging', {}).get('LoggingEnabled'), _compare_json, Logging, - None), - ('NotificationConfiguration', 'put_notification_configuration', - _describe.get('NotificationConfiguration'), _compare_json, NotificationConfiguration, - None), - ('Policy', 'put_policy', - _describe.get('Policy'), _compare_policy, {"Policy": Policy} if Policy else None, - 'delete_policy'), - ('RequestPayment', 'put_request_payment', - _describe.get('RequestPayment'), _compare_json, RequestPayment, - None), - ('Tagging', 'put_tagging', - _describe.get('Tagging'), _compare_json, Tagging, - 'delete_tagging'), - ('Website', 'put_website', - _describe.get('Website'), _compare_json, Website, - 'delete_website'), + ("ACL", "put_acl", _describe.get("ACL"), _compare_acl, ACL, None), + ( + "CORS", + "put_cors", + _describe.get("CORS"), + _compare_json, + {"CORSRules": CORSRules} if CORSRules else None, + "delete_cors", + ), + ( + "LifecycleConfiguration", + "put_lifecycle_configuration", + _describe.get("LifecycleConfiguration"), + _compare_json, + {"Rules": LifecycleConfiguration} if LifecycleConfiguration else None, + "delete_lifecycle_configuration", + ), + ( + "Logging", + "put_logging", + _describe.get("Logging", {}).get("LoggingEnabled"), + _compare_json, + Logging, + None, + ), + ( + "NotificationConfiguration", + "put_notification_configuration", + _describe.get("NotificationConfiguration"), + _compare_json, + NotificationConfiguration, + None, + ), + ( + "Policy", + "put_policy", + _describe.get("Policy"), + _compare_policy, + {"Policy": Policy} if Policy else None, + "delete_policy", + ), + ( + "RequestPayment", + "put_request_payment", + _describe.get("RequestPayment"), + _compare_json, + RequestPayment, + None, + ), + ( + "Tagging", + "put_tagging", + _describe.get("Tagging"), + _compare_json, + Tagging, + "delete_tagging", + ), + ( + "Website", + "put_website", + _describe.get("Website"), + _compare_json, + Website, + "delete_website", + ), ] - versioning_item = ('Versioning', 'put_versioning', - _describe.get('Versioning'), _compare_json, Versioning or {}, - None) + versioning_item = ( + "Versioning", + "put_versioning", + _describe.get("Versioning"), + _compare_json, + Versioning or {}, + None, + ) # Substitute full ARN into desired state for comparison - replication_item = ('Replication', 'put_replication', - _describe.get('Replication', {}).get('ReplicationConfiguration'), _compare_replication, Replication, - 'delete_replication') + replication_item = ( + "Replication", + "put_replication", + _describe.get("Replication", {}).get("ReplicationConfiguration"), + _compare_replication, + Replication, + "delete_replication", + ) # versioning must be turned on before replication can be on, thus replication # must be turned off before versioning can be off @@ -547,65 +630,81 @@ def present(name, Bucket, update = False for varname, setter, current, comparator, desired, deleter in config_items: - if varname == 'Policy': + if varname == "Policy": if current is not None: - temp = current.get('Policy') + temp = current.get("Policy") # Policy description is always returned as a JSON string. # Convert it to JSON now for ease of comparisons later. if isinstance(temp, six.string_types): - current = __utils__['boto3.ordered']( - {'Policy': salt.utils.json.loads(temp)} + current = __utils__["boto3.ordered"]( + {"Policy": salt.utils.json.loads(temp)} ) if not comparator(current, desired, region, key, keyid, profile): update = True - if varname == 'ACL': - ret['changes'].setdefault('new', {})[varname] = _acl_to_grant( - desired, _get_canonical_id(region, key, keyid, profile)) + if varname == "ACL": + ret["changes"].setdefault("new", {})[varname] = _acl_to_grant( + desired, _get_canonical_id(region, key, keyid, profile) + ) else: - ret['changes'].setdefault('new', {})[varname] = desired - ret['changes'].setdefault('old', {})[varname] = current + ret["changes"].setdefault("new", {})[varname] = desired + ret["changes"].setdefault("old", {})[varname] = current - if not __opts__['test']: + if not __opts__["test"]: if deleter and desired is None: # Setting can be deleted, so use that to unset it - r = __salt__['boto_s3_bucket.{0}'.format(deleter)](Bucket=Bucket, - region=region, key=key, keyid=keyid, profile=profile) - if not r.get('deleted'): - ret['result'] = False - ret['comment'] = 'Failed to update bucket: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_s3_bucket.{0}".format(deleter)]( + Bucket=Bucket, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("deleted"): + ret["result"] = False + ret["comment"] = "Failed to update bucket: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret else: - r = __salt__['boto_s3_bucket.{0}'.format(setter)](Bucket=Bucket, - region=region, key=key, keyid=keyid, profile=profile, - **(desired or {})) - if not r.get('updated'): - ret['result'] = False - ret['comment'] = 'Failed to update bucket: {0}.'.format(r['error']['message']) - ret['changes'] = {} + r = __salt__["boto_s3_bucket.{0}".format(setter)]( + Bucket=Bucket, + region=region, + key=key, + keyid=keyid, + profile=profile, + **(desired or {}) + ) + if not r.get("updated"): + ret["result"] = False + ret["comment"] = "Failed to update bucket: {0}.".format( + r["error"]["message"] + ) + ret["changes"] = {} return ret - if update and __opts__['test']: - msg = 'S3 bucket {0} set to be modified.'.format(Bucket) - ret['comment'] = msg - ret['result'] = None + if update and __opts__["test"]: + msg = "S3 bucket {0} set to be modified.".format(Bucket) + ret["comment"] = msg + ret["result"] = None return ret # Since location can't be changed, try that last so at least the rest of # the things are correct by the time we fail here. Fail so the user will # notice something mismatches their desired state. - if _describe.get('Location', {}).get('LocationConstraint') != LocationConstraint: - msg = 'Bucket {0} location does not match desired configuration, but cannot be changed'.format(LocationConstraint) + if _describe.get("Location", {}).get("LocationConstraint") != LocationConstraint: + msg = "Bucket {0} location does not match desired configuration, but cannot be changed".format( + LocationConstraint + ) log.warning(msg) - ret['result'] = False - ret['comment'] = 'Failed to update bucket: {0}.'.format(msg) + ret["result"] = False + ret["comment"] = "Failed to update bucket: {0}.".format(msg) return ret return ret -def absent(name, Bucket, Force=False, - region=None, key=None, keyid=None, profile=None): - ''' +def absent(name, Bucket, Force=False, region=None, key=None, keyid=None, profile=None): + """ Ensure bucket with passed properties is absent. name @@ -629,36 +728,34 @@ def absent(name, Bucket, Force=False, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': Bucket, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": Bucket, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_s3_bucket.exists'](Bucket, region=region, key=key, - keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete bucket: {0}.'.format(r['error']['message']) + r = __salt__["boto_s3_bucket.exists"]( + Bucket, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete bucket: {0}.".format(r["error"]["message"]) return ret - if r and not r['exists']: - ret['comment'] = 'S3 bucket {0} does not exist.'.format(Bucket) + if r and not r["exists"]: + ret["comment"] = "S3 bucket {0} does not exist.".format(Bucket) return ret - if __opts__['test']: - ret['comment'] = 'S3 bucket {0} is set to be removed.'.format(Bucket) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "S3 bucket {0} is set to be removed.".format(Bucket) + ret["result"] = None return ret - r = __salt__['boto_s3_bucket.delete'](Bucket, Force=Force, region=region, - key=key, keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete bucket: {0}.'.format(r['error']['message']) + r = __salt__["boto_s3_bucket.delete"]( + Bucket, Force=Force, region=region, key=key, keyid=keyid, profile=profile + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete bucket: {0}.".format(r["error"]["message"]) return ret - ret['changes']['old'] = {'bucket': Bucket} - ret['changes']['new'] = {'bucket': None} - ret['comment'] = 'S3 bucket {0} deleted.'.format(Bucket) + ret["changes"]["old"] = {"bucket": Bucket} + ret["changes"]["new"] = {"bucket": None} + ret["comment"] = "S3 bucket {0} deleted.".format(Bucket) return ret diff --git a/salt/states/boto_secgroup.py b/salt/states/boto_secgroup.py index 8fd6aee8a8a..5f61e394ee3 100644 --- a/salt/states/boto_secgroup.py +++ b/salt/states/boto_secgroup.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Security Groups ====================== @@ -102,7 +102,7 @@ passed in as a dict, or as a string to pull from pillars or minion config: If ``region`` is missing from the ``profile`` data set, ``us-east-1`` will be used as the default region. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -118,27 +118,30 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_secgroup' if 'boto_secgroup.exists' in __salt__ else False + """ + if "boto_secgroup.exists" in __salt__: + return "boto_secgroup" + return (False, "boto_secgroup module could not be loaded") def present( - name, - description, - vpc_id=None, - vpc_name=None, - rules=None, - rules_egress=None, - delete_ingress_rules=True, - delete_egress_rules=True, - region=None, - key=None, - keyid=None, - profile=None, - tags=None): - ''' + name, + description, + vpc_id=None, + vpc_name=None, + rules=None, + rules_egress=None, + delete_ingress_rules=True, + delete_egress_rules=True, + region=None, + key=None, + keyid=None, + profile=None, + tags=None, +): + """ Ensure the security group exists with the specified rules. name @@ -194,109 +197,159 @@ def present( List of key:value pairs of tags to set on the security group .. versionadded:: 2016.3.0 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - _ret = _security_group_present(name, description, vpc_id=vpc_id, - vpc_name=vpc_name, region=region, - key=key, keyid=keyid, profile=profile) - ret['changes'] = _ret['changes'] - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + _ret = _security_group_present( + name, + description, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"] = _ret["changes"] + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - elif ret['result'] is None: + elif ret["result"] is None: return ret if rules is not None: - _ret = _rules_present(name, rules, delete_ingress_rules, vpc_id=vpc_id, - vpc_name=vpc_name, region=region, key=key, - keyid=keyid, profile=profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + _ret = _rules_present( + name, + rules, + delete_ingress_rules, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] if rules_egress is not None: - _ret = _rules_egress_present(name, rules_egress, delete_egress_rules, - vpc_id=vpc_id, vpc_name=vpc_name, - region=region, key=key, keyid=keyid, - profile=profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + _ret = _rules_egress_present( + name, + rules_egress, + delete_egress_rules, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] _ret = _tags_present( - name=name, tags=tags, vpc_id=vpc_id, vpc_name=vpc_name, - region=region, key=key, keyid=keyid, profile=profile + name=name, + tags=tags, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, ) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] return ret -def _security_group_present(name, description, vpc_id=None, vpc_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def _security_group_present( + name, + description, + vpc_id=None, + vpc_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ given a group name or a group name and vpc id (or vpc name): 1. determine if the group exists 2. if the group does not exist, creates the group 3. return the group's configuration and any changes made - ''' - ret = {'result': True, 'comment': '', 'changes': {}} - exists = __salt__['boto_secgroup.exists'](name, region, key, keyid, - profile, vpc_id, vpc_name) + """ + ret = {"result": True, "comment": "", "changes": {}} + exists = __salt__["boto_secgroup.exists"]( + name, region, key, keyid, profile, vpc_id, vpc_name + ) if not exists: - if __opts__['test']: - ret['comment'] = 'Security group {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Security group {0} is set to be created.".format(name) + ret["result"] = None return ret - created = __salt__['boto_secgroup.create'](name=name, description=description, - vpc_id=vpc_id, vpc_name=vpc_name, - region=region, key=key, keyid=keyid, - profile=profile) + created = __salt__["boto_secgroup.create"]( + name=name, + description=description, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if created: - ret['changes']['old'] = {'secgroup': None} - sg = __salt__['boto_secgroup.get_config'](name=name, group_id=None, region=region, key=key, - keyid=keyid, profile=profile, vpc_id=vpc_id, - vpc_name=vpc_name) - ret['changes']['new'] = {'secgroup': sg} - ret['comment'] = 'Security group {0} created.'.format(name) + ret["changes"]["old"] = {"secgroup": None} + sg = __salt__["boto_secgroup.get_config"]( + name=name, + group_id=None, + region=region, + key=key, + keyid=keyid, + profile=profile, + vpc_id=vpc_id, + vpc_name=vpc_name, + ) + ret["changes"]["new"] = {"secgroup": sg} + ret["comment"] = "Security group {0} created.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} security group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} security group.".format(name) else: - ret['comment'] = 'Security group {0} present.'.format(name) + ret["comment"] = "Security group {0} present.".format(name) return ret def _split_rules(rules): - ''' + """ Split rules with lists into individual rules. We accept some attributes as lists or strings. The data we get back from the execution module lists rules as individual rules. We need to split the provided rules into individual rules to compare them. - ''' + """ split = [] for rule in rules: - cidr_ip = rule.get('cidr_ip') - group_name = rule.get('source_group_name') - group_id = rule.get('source_group_group_id') + cidr_ip = rule.get("cidr_ip") + group_name = rule.get("source_group_name") + group_id = rule.get("source_group_group_id") if cidr_ip and not isinstance(cidr_ip, six.string_types): for ip in cidr_ip: _rule = rule.copy() - _rule['cidr_ip'] = ip + _rule["cidr_ip"] = ip split.append(_rule) elif group_name and not isinstance(group_name, six.string_types): for name in group_name: _rule = rule.copy() - _rule['source_group_name'] = name + _rule["source_group_name"] = name split.append(_rule) elif group_id and not isinstance(group_id, six.string_types): for _id in group_id: _rule = rule.copy() - _rule['source_group_group_id'] = _id + _rule["source_group_group_id"] = _id split.append(_rule) else: split.append(rule) @@ -304,45 +357,47 @@ def _split_rules(rules): def _check_rule(rule, _rule): - ''' + """ Check to see if two rules are the same. Needed to compare rules fetched from boto, since they may not completely match rules defined in sls files but may be functionally equivalent. - ''' + """ # We need to alter what Boto returns if no ports are specified # so that we can compare rules fairly. # # Boto returns None for from_port and to_port where we're required # to pass in "-1" instead. - if _rule.get('from_port') is None: - _rule['from_port'] = -1 - if _rule.get('to_port') is None: - _rule['to_port'] = -1 + if _rule.get("from_port") is None: + _rule["from_port"] = -1 + if _rule.get("to_port") is None: + _rule["to_port"] = -1 - if (rule['ip_protocol'] == _rule['ip_protocol'] and - six.text_type(rule['from_port']) == six.text_type(_rule['from_port']) and - six.text_type(rule['to_port']) == six.text_type(_rule['to_port'])): - _cidr_ip = _rule.get('cidr_ip') - if _cidr_ip and _cidr_ip == rule.get('cidr_ip'): + if ( + rule["ip_protocol"] == _rule["ip_protocol"] + and six.text_type(rule["from_port"]) == six.text_type(_rule["from_port"]) + and six.text_type(rule["to_port"]) == six.text_type(_rule["to_port"]) + ): + _cidr_ip = _rule.get("cidr_ip") + if _cidr_ip and _cidr_ip == rule.get("cidr_ip"): return True - _owner_id = _rule.get('source_group_owner_id') - if _owner_id and _owner_id == rule.get('source_group_owner_id'): + _owner_id = _rule.get("source_group_owner_id") + if _owner_id and _owner_id == rule.get("source_group_owner_id"): return True - _group_id = _rule.get('source_group_group_id') - if _group_id and _group_id == rule.get('source_group_group_id'): + _group_id = _rule.get("source_group_group_id") + if _group_id and _group_id == rule.get("source_group_group_id"): return True - _group_name = _rule.get('source_group_name') - if _group_name and _group_id == rule.get('source_group_name'): + _group_name = _rule.get("source_group_name") + if _group_name and _group_id == rule.get("source_group_name"): return True return False def _get_rule_changes(rules, _rules): - ''' + """ given a list of desired rules (rules) and existing rules (_rules) return a list of rules to delete (to_delete) and to create (to_create) - ''' + """ to_delete = [] to_create = [] # for each rule in state file @@ -350,34 +405,58 @@ def _get_rule_changes(rules, _rules): # 2. determine if rule exists in existing security group rules for rule in rules: try: - ip_protocol = six.text_type(rule.get('ip_protocol')) + ip_protocol = six.text_type(rule.get("ip_protocol")) except KeyError: - raise SaltInvocationError('ip_protocol, to_port, and from_port are' - ' required arguments for security group' - ' rules.') - supported_protocols = ['tcp', '6', 6, 'udp', '17', 17, 'icmp', '1', 1, - 'all', '-1', -1] - if ip_protocol not in supported_protocols and (not - '{0}'.format(ip_protocol).isdigit() or int(ip_protocol) > 255): raise SaltInvocationError( - 'Invalid ip_protocol {0} specified in security group rule.'.format(ip_protocol)) + "ip_protocol, to_port, and from_port are" + " required arguments for security group" + " rules." + ) + supported_protocols = [ + "tcp", + "6", + 6, + "udp", + "17", + 17, + "icmp", + "1", + 1, + "all", + "-1", + -1, + ] + if ip_protocol not in supported_protocols and ( + not "{0}".format(ip_protocol).isdigit() or int(ip_protocol) > 255 + ): + raise SaltInvocationError( + "Invalid ip_protocol {0} specified in security group rule.".format( + ip_protocol + ) + ) # For the 'all' case, we need to change the protocol name to '-1'. - if ip_protocol == 'all': - rule['ip_protocol'] = '-1' - cidr_ip = rule.get('cidr_ip', None) - group_name = rule.get('source_group_name', None) - group_id = rule.get('source_group_group_id', None) + if ip_protocol == "all": + rule["ip_protocol"] = "-1" + cidr_ip = rule.get("cidr_ip", None) + group_name = rule.get("source_group_name", None) + group_id = rule.get("source_group_group_id", None) if cidr_ip and (group_id or group_name): - raise SaltInvocationError('cidr_ip and source groups can not both' - ' be specified in security group rules.') + raise SaltInvocationError( + "cidr_ip and source groups can not both" + " be specified in security group rules." + ) if group_id and group_name: - raise SaltInvocationError('Either source_group_group_id or' - ' source_group_name can be specified in' - ' security group rules, but not both.') + raise SaltInvocationError( + "Either source_group_group_id or" + " source_group_name can be specified in" + " security group rules, but not both." + ) if not (cidr_ip or group_id or group_name): - raise SaltInvocationError('cidr_ip, source_group_group_id, or' - ' source_group_name must be provided for' - ' security group rules.') + raise SaltInvocationError( + "cidr_ip, source_group_group_id, or" + " source_group_name must be provided for" + " security group rules." + ) rule_found = False # for each rule in existing security group ruleset determine if # new rule exists @@ -398,217 +477,322 @@ def _get_rule_changes(rules, _rules): if not rule_found: # Can only supply name or id, not both. Since we're deleting # entries, it doesn't matter which we pick. - _rule.pop('source_group_name', None) + _rule.pop("source_group_name", None) to_delete.append(_rule) - log.debug('Rules to be deleted: %s', to_delete) - log.debug('Rules to be created: %s', to_create) + log.debug("Rules to be deleted: %s", to_delete) + log.debug("Rules to be created: %s", to_create) return (to_delete, to_create) -def _rules_present(name, rules, delete_ingress_rules=True, vpc_id=None, - vpc_name=None, region=None, key=None, keyid=None, profile=None): - ''' +def _rules_present( + name, + rules, + delete_ingress_rules=True, + vpc_id=None, + vpc_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ given a group name or group name and vpc_id (or vpc name): 1. get lists of desired rule changes (using _get_rule_changes) 2. authorize/create rules missing rules 3. if delete_ingress_rules is True, delete/revoke non-requested rules 4. return 'old' and 'new' group rules - ''' - ret = {'result': True, 'comment': '', 'changes': {}} - sg = __salt__['boto_secgroup.get_config'](name=name, group_id=None, region=region, key=key, - keyid=keyid, profile=profile, vpc_id=vpc_id, - vpc_name=vpc_name) + """ + ret = {"result": True, "comment": "", "changes": {}} + sg = __salt__["boto_secgroup.get_config"]( + name=name, + group_id=None, + region=region, + key=key, + keyid=keyid, + profile=profile, + vpc_id=vpc_id, + vpc_name=vpc_name, + ) if not sg: - ret['comment'] = '{0} security group configuration could not be retrieved.'.format(name) - ret['result'] = False + ret[ + "comment" + ] = "{0} security group configuration could not be retrieved.".format(name) + ret["result"] = False return ret rules = _split_rules(rules) if vpc_id or vpc_name: for rule in rules: - _source_group_name = rule.get('source_group_name', None) + _source_group_name = rule.get("source_group_name", None) if _source_group_name: _group_vpc_name = vpc_name _group_vpc_id = vpc_id - _source_group_name_vpc = rule.get('source_group_name_vpc', None) + _source_group_name_vpc = rule.get("source_group_name_vpc", None) if _source_group_name_vpc: _group_vpc_name = _source_group_name_vpc _group_vpc_id = None - _group_id = __salt__['boto_secgroup.get_group_id']( - name=_source_group_name, vpc_id=_group_vpc_id, vpc_name=_group_vpc_name, - region=region, key=key, keyid=keyid, profile=profile + _group_id = __salt__["boto_secgroup.get_group_id"]( + name=_source_group_name, + vpc_id=_group_vpc_id, + vpc_name=_group_vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, ) if not _group_id: raise SaltInvocationError( - 'source_group_name {0} does not map to a valid ' - 'source group id.'.format(_source_group_name) + "source_group_name {0} does not map to a valid " + "source group id.".format(_source_group_name) ) - rule['source_group_name'] = None + rule["source_group_name"] = None if _source_group_name_vpc: - rule.pop('source_group_name_vpc') - rule['source_group_group_id'] = _group_id + rule.pop("source_group_name_vpc") + rule["source_group_group_id"] = _group_id # rules = rules that exist in salt state # sg['rules'] = that exist in present group - to_delete, to_create = _get_rule_changes(rules, sg['rules']) + to_delete, to_create = _get_rule_changes(rules, sg["rules"]) to_delete = to_delete if delete_ingress_rules else [] if to_create or to_delete: - if __opts__['test']: + if __opts__["test"]: msg = """Security group {0} set to have rules modified. To be created: {1} - To be deleted: {2}""".format(name, pprint.pformat(to_create), - pprint.pformat(to_delete)) - ret['comment'] = msg - ret['result'] = None + To be deleted: {2}""".format( + name, pprint.pformat(to_create), pprint.pformat(to_delete) + ) + ret["comment"] = msg + ret["result"] = None return ret if to_delete: deleted = True for rule in to_delete: - _deleted = __salt__['boto_secgroup.revoke']( - name, vpc_id=vpc_id, vpc_name=vpc_name, region=region, - key=key, keyid=keyid, profile=profile, **rule) + _deleted = __salt__["boto_secgroup.revoke"]( + name, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + **rule + ) if not _deleted: deleted = False if deleted: - ret['comment'] = 'Removed rules on {0} security group.'.format(name) + ret["comment"] = "Removed rules on {0} security group.".format(name) else: - ret['comment'] = 'Failed to remove rules on {0} security group.'.format(name) - ret['result'] = False + ret["comment"] = "Failed to remove rules on {0} security group.".format( + name + ) + ret["result"] = False if to_create: created = True for rule in to_create: - _created = __salt__['boto_secgroup.authorize']( - name, vpc_id=vpc_id, vpc_name=vpc_name, region=region, - key=key, keyid=keyid, profile=profile, **rule) + _created = __salt__["boto_secgroup.authorize"]( + name, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + **rule + ) if not _created: created = False if created: - ret['comment'] = ' '.join([ - ret['comment'], - 'Created rules on {0} security group.'.format(name) - ]) + ret["comment"] = " ".join( + [ + ret["comment"], + "Created rules on {0} security group.".format(name), + ] + ) else: - ret['comment'] = ' '.join([ - ret['comment'], - 'Failed to create rules on {0} security group.'.format(name) - ]) - ret['result'] = False - ret['changes']['old'] = {'rules': sg['rules']} - sg = __salt__['boto_secgroup.get_config'](name=name, group_id=None, region=region, key=key, - keyid=keyid, profile=profile, vpc_id=vpc_id, - vpc_name=vpc_name) - ret['changes']['new'] = {'rules': sg['rules']} + ret["comment"] = " ".join( + [ + ret["comment"], + "Failed to create rules on {0} security group.".format(name), + ] + ) + ret["result"] = False + ret["changes"]["old"] = {"rules": sg["rules"]} + sg = __salt__["boto_secgroup.get_config"]( + name=name, + group_id=None, + region=region, + key=key, + keyid=keyid, + profile=profile, + vpc_id=vpc_id, + vpc_name=vpc_name, + ) + ret["changes"]["new"] = {"rules": sg["rules"]} return ret -def _rules_egress_present(name, rules_egress, delete_egress_rules=True, vpc_id=None, - vpc_name=None, region=None, key=None, keyid=None, profile=None): - ''' +def _rules_egress_present( + name, + rules_egress, + delete_egress_rules=True, + vpc_id=None, + vpc_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ given a group name or group name and vpc_id (or vpc name): 1. get lists of desired rule changes (using _get_rule_changes) 2. authorize/create missing rules 3. if delete_egress_rules is True, delete/revoke non-requested rules 4. return 'old' and 'new' group rules - ''' - ret = {'result': True, 'comment': '', 'changes': {}} - sg = __salt__['boto_secgroup.get_config'](name=name, group_id=None, region=region, key=key, - keyid=keyid, profile=profile, vpc_id=vpc_id, - vpc_name=vpc_name) + """ + ret = {"result": True, "comment": "", "changes": {}} + sg = __salt__["boto_secgroup.get_config"]( + name=name, + group_id=None, + region=region, + key=key, + keyid=keyid, + profile=profile, + vpc_id=vpc_id, + vpc_name=vpc_name, + ) if not sg: - ret['comment'] = '{0} security group configuration could not be retrieved.'.format(name) - ret['result'] = False + ret[ + "comment" + ] = "{0} security group configuration could not be retrieved.".format(name) + ret["result"] = False return ret rules_egress = _split_rules(rules_egress) if vpc_id or vpc_name: for rule in rules_egress: - _source_group_name = rule.get('source_group_name', None) + _source_group_name = rule.get("source_group_name", None) if _source_group_name: _group_vpc_name = vpc_name _group_vpc_id = vpc_id - _source_group_name_vpc = rule.get('source_group_name_vpc', None) + _source_group_name_vpc = rule.get("source_group_name_vpc", None) if _source_group_name_vpc: _group_vpc_name = _source_group_name_vpc _group_vpc_id = None - _group_id = __salt__['boto_secgroup.get_group_id']( - name=_source_group_name, vpc_id=_group_vpc_id, vpc_name=_group_vpc_name, - region=region, key=key, keyid=keyid, profile=profile + _group_id = __salt__["boto_secgroup.get_group_id"]( + name=_source_group_name, + vpc_id=_group_vpc_id, + vpc_name=_group_vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, ) if not _group_id: raise SaltInvocationError( - 'source_group_name {0} does not map to a valid ' - 'source group id.'.format(_source_group_name) + "source_group_name {0} does not map to a valid " + "source group id.".format(_source_group_name) ) - rule['source_group_name'] = None + rule["source_group_name"] = None if _source_group_name_vpc: - rule.pop('source_group_name_vpc') - rule['source_group_group_id'] = _group_id + rule.pop("source_group_name_vpc") + rule["source_group_group_id"] = _group_id # rules_egress = rules that exist in salt state # sg['rules_egress'] = that exist in present group - to_delete, to_create = _get_rule_changes(rules_egress, sg['rules_egress']) + to_delete, to_create = _get_rule_changes(rules_egress, sg["rules_egress"]) to_delete = to_delete if delete_egress_rules else [] if to_create or to_delete: - if __opts__['test']: + if __opts__["test"]: msg = """Security group {0} set to have rules modified. To be created: {1} - To be deleted: {2}""".format(name, pprint.pformat(to_create), - pprint.pformat(to_delete)) - ret['comment'] = msg - ret['result'] = None + To be deleted: {2}""".format( + name, pprint.pformat(to_create), pprint.pformat(to_delete) + ) + ret["comment"] = msg + ret["result"] = None return ret if to_delete: deleted = True for rule in to_delete: - _deleted = __salt__['boto_secgroup.revoke']( - name, vpc_id=vpc_id, vpc_name=vpc_name, region=region, - key=key, keyid=keyid, profile=profile, egress=True, **rule) + _deleted = __salt__["boto_secgroup.revoke"]( + name, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + egress=True, + **rule + ) if not _deleted: deleted = False if deleted: - ret['comment'] = ' '.join([ - ret['comment'], - 'Removed egress rule on {0} security group.'.format(name) - ]) + ret["comment"] = " ".join( + [ + ret["comment"], + "Removed egress rule on {0} security group.".format(name), + ] + ) else: - ret['comment'] = ' '.join([ - ret['comment'], - 'Failed to remove egress rule on {0} security group.'.format(name) - ]) - ret['result'] = False + ret["comment"] = " ".join( + [ + ret["comment"], + "Failed to remove egress rule on {0} security group.".format( + name + ), + ] + ) + ret["result"] = False if to_create: created = True for rule in to_create: - _created = __salt__['boto_secgroup.authorize']( - name, vpc_id=vpc_id, vpc_name=vpc_name, region=region, - key=key, keyid=keyid, profile=profile, egress=True, **rule) + _created = __salt__["boto_secgroup.authorize"]( + name, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + egress=True, + **rule + ) if not _created: created = False if created: - ret['comment'] = ' '.join([ - ret['comment'], - 'Created egress rules on {0} security group.'.format(name) - ]) + ret["comment"] = " ".join( + [ + ret["comment"], + "Created egress rules on {0} security group.".format(name), + ] + ) else: - ret['comment'] = ' '.join([ - ret['comment'], - 'Failed to create egress rules on {0} security group.'.format(name) - ]) - ret['result'] = False + ret["comment"] = " ".join( + [ + ret["comment"], + "Failed to create egress rules on {0} security group.".format( + name + ), + ] + ) + ret["result"] = False - ret['changes']['old'] = {'rules_egress': sg['rules_egress']} - sg = __salt__['boto_secgroup.get_config'](name=name, group_id=None, region=region, key=key, - keyid=keyid, profile=profile, vpc_id=vpc_id, - vpc_name=vpc_name) - ret['changes']['new'] = {'rules_egress': sg['rules_egress']} + ret["changes"]["old"] = {"rules_egress": sg["rules_egress"]} + sg = __salt__["boto_secgroup.get_config"]( + name=name, + group_id=None, + region=region, + key=key, + keyid=keyid, + profile=profile, + vpc_id=vpc_id, + vpc_name=vpc_name, + ) + ret["changes"]["new"] = {"rules_egress": sg["rules_egress"]} return ret def absent( - name, - vpc_id=None, - vpc_name=None, - region=None, - key=None, - keyid=None, - profile=None): - ''' + name, vpc_id=None, vpc_name=None, region=None, key=None, keyid=None, profile=None +): + """ Ensure a security group with the specified name does not exist. name @@ -636,126 +820,179 @@ def absent( that contains a dict with region, key and keyid. .. versionadded:: 2016.3.0 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - sg = __salt__['boto_secgroup.get_config'](name=name, group_id=None, region=region, key=key, - keyid=keyid, profile=profile, vpc_id=vpc_id, - vpc_name=vpc_name) + sg = __salt__["boto_secgroup.get_config"]( + name=name, + group_id=None, + region=region, + key=key, + keyid=keyid, + profile=profile, + vpc_id=vpc_id, + vpc_name=vpc_name, + ) if sg: - if __opts__['test']: - ret['comment'] = 'Security group {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Security group {0} is set to be removed.".format(name) + ret["result"] = None return ret - deleted = __salt__['boto_secgroup.delete'](name=name, group_id=None, region=region, key=key, - keyid=keyid, profile=profile, vpc_id=vpc_id, - vpc_name=vpc_name) + deleted = __salt__["boto_secgroup.delete"]( + name=name, + group_id=None, + region=region, + key=key, + keyid=keyid, + profile=profile, + vpc_id=vpc_id, + vpc_name=vpc_name, + ) if deleted: - ret['changes']['old'] = {'secgroup': sg} - ret['changes']['new'] = {'secgroup': None} - ret['comment'] = 'Security group {0} deleted.'.format(name) + ret["changes"]["old"] = {"secgroup": sg} + ret["changes"]["new"] = {"secgroup": None} + ret["comment"] = "Security group {0} deleted.".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} security group.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} security group.".format(name) else: - ret['comment'] = '{0} security group does not exist.'.format(name) + ret["comment"] = "{0} security group does not exist.".format(name) return ret -def _tags_present(name, tags, vpc_id=None, vpc_name=None, region=None, - key=None, keyid=None, profile=None): - ''' +def _tags_present( + name, + tags, + vpc_id=None, + vpc_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ helper function to validate tags are correct - ''' - ret = {'result': True, 'comment': '', 'changes': {}} + """ + ret = {"result": True, "comment": "", "changes": {}} if tags: - sg = __salt__['boto_secgroup.get_config'](name=name, group_id=None, region=region, key=key, - keyid=keyid, profile=profile, vpc_id=vpc_id, - vpc_name=vpc_name) + sg = __salt__["boto_secgroup.get_config"]( + name=name, + group_id=None, + region=region, + key=key, + keyid=keyid, + profile=profile, + vpc_id=vpc_id, + vpc_name=vpc_name, + ) if not sg: - ret['comment'] = '{0} security group configuration could not be retrieved.'.format(name) - ret['result'] = False + ret[ + "comment" + ] = "{0} security group configuration could not be retrieved.".format(name) + ret["result"] = False return ret tags_to_add = tags tags_to_update = {} tags_to_remove = [] - if sg.get('tags'): - for existing_tag in sg['tags']: + if sg.get("tags"): + for existing_tag in sg["tags"]: if existing_tag not in tags: if existing_tag not in tags_to_remove: tags_to_remove.append(existing_tag) else: - if tags[existing_tag] != sg['tags'][existing_tag]: + if tags[existing_tag] != sg["tags"][existing_tag]: tags_to_update[existing_tag] = tags[existing_tag] tags_to_add.pop(existing_tag) if tags_to_remove: - if __opts__['test']: - msg = 'The following tag{0} set to be removed: {1}.'.format( - ('s are' if len(tags_to_remove) > 1 else ' is'), ', '.join(tags_to_remove)) - ret['comment'] = ' '.join([ret['comment'], msg]) - ret['result'] = None + if __opts__["test"]: + msg = "The following tag{0} set to be removed: {1}.".format( + ("s are" if len(tags_to_remove) > 1 else " is"), + ", ".join(tags_to_remove), + ) + ret["comment"] = " ".join([ret["comment"], msg]) + ret["result"] = None else: - temp_ret = __salt__['boto_secgroup.delete_tags'](tags_to_remove, - name=name, - group_id=None, - vpc_name=vpc_name, - vpc_id=vpc_id, - region=region, - key=key, - keyid=keyid, - profile=profile) + temp_ret = __salt__["boto_secgroup.delete_tags"]( + tags_to_remove, + name=name, + group_id=None, + vpc_name=vpc_name, + vpc_id=vpc_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not temp_ret: - ret['result'] = False - ret['comment'] = ' '.join([ - ret['comment'], - 'Error attempting to delete tags {0}.'.format(tags_to_remove) - ]) + ret["result"] = False + ret["comment"] = " ".join( + [ + ret["comment"], + "Error attempting to delete tags {0}.".format( + tags_to_remove + ), + ] + ) return ret - if 'old' not in ret['changes']: - ret['changes'] = dictupdate.update(ret['changes'], {'old': {'tags': {}}}) + if "old" not in ret["changes"]: + ret["changes"] = dictupdate.update( + ret["changes"], {"old": {"tags": {}}} + ) for rem_tag in tags_to_remove: - ret['changes']['old']['tags'][rem_tag] = sg['tags'][rem_tag] + ret["changes"]["old"]["tags"][rem_tag] = sg["tags"][rem_tag] if tags_to_add or tags_to_update: - if __opts__['test']: + if __opts__["test"]: if tags_to_add: - msg = 'The following tag{0} set to be added: {1}.'.format( - ('s are' if len(tags_to_add.keys()) > 1 else ' is'), - ', '.join(tags_to_add.keys())) - ret['comment'] = ' '.join([ret['comment'], msg]) - ret['result'] = None + msg = "The following tag{0} set to be added: {1}.".format( + ("s are" if len(tags_to_add.keys()) > 1 else " is"), + ", ".join(tags_to_add.keys()), + ) + ret["comment"] = " ".join([ret["comment"], msg]) + ret["result"] = None if tags_to_update: - msg = 'The following tag {0} set to be updated: {1}.'.format( - ('values are' if len(tags_to_update.keys()) > 1 else 'value is'), - ', '.join(tags_to_update.keys())) - ret['comment'] = ' '.join([ret['comment'], msg]) - ret['result'] = None + msg = "The following tag {0} set to be updated: {1}.".format( + ( + "values are" + if len(tags_to_update.keys()) > 1 + else "value is" + ), + ", ".join(tags_to_update.keys()), + ) + ret["comment"] = " ".join([ret["comment"], msg]) + ret["result"] = None else: all_tag_changes = dictupdate.update(tags_to_add, tags_to_update) - temp_ret = __salt__['boto_secgroup.set_tags'](all_tag_changes, - name=name, - group_id=None, - vpc_name=vpc_name, - vpc_id=vpc_id, - region=region, - key=key, - keyid=keyid, - profile=profile) + temp_ret = __salt__["boto_secgroup.set_tags"]( + all_tag_changes, + name=name, + group_id=None, + vpc_name=vpc_name, + vpc_id=vpc_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not temp_ret: - ret['result'] = False - msg = 'Error attempting to set tags.' - ret['comment'] = ' '.join([ret['comment'], msg]) + ret["result"] = False + msg = "Error attempting to set tags." + ret["comment"] = " ".join([ret["comment"], msg]) return ret - if 'old' not in ret['changes']: - ret['changes'] = dictupdate.update(ret['changes'], {'old': {'tags': {}}}) - if 'new' not in ret['changes']: - ret['changes'] = dictupdate.update(ret['changes'], {'new': {'tags': {}}}) + if "old" not in ret["changes"]: + ret["changes"] = dictupdate.update( + ret["changes"], {"old": {"tags": {}}} + ) + if "new" not in ret["changes"]: + ret["changes"] = dictupdate.update( + ret["changes"], {"new": {"tags": {}}} + ) for tag in all_tag_changes: - ret['changes']['new']['tags'][tag] = tags[tag] - if 'tags' in sg: - if sg['tags']: - if tag in sg['tags']: - ret['changes']['old']['tags'][tag] = sg['tags'][tag] + ret["changes"]["new"]["tags"][tag] = tags[tag] + if "tags" in sg: + if sg["tags"]: + if tag in sg["tags"]: + ret["changes"]["old"]["tags"][tag] = sg["tags"][tag] if not tags_to_update and not tags_to_remove and not tags_to_add: - ret['comment'] = ' '.join([ret['comment'], 'Tags are already set.']) + ret["comment"] = " ".join([ret["comment"], "Tags are already set."]) return ret diff --git a/salt/states/boto_sns.py b/salt/states/boto_sns.py index 154e766fc3c..79c16cc399c 100644 --- a/salt/states/boto_sns.py +++ b/salt/states/boto_sns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage SNS Topics @@ -53,7 +53,7 @@ passed in as a dict, or as a string to pull from pillars or minion config: - profile: keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Standard Libs @@ -61,20 +61,16 @@ import re def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_sns' if 'boto_sns.exists' in __salt__ else False + """ + if "boto_sns.exists" in __salt__: + return "boto_sns" + return (False, "boto_sns module could not be loaded") -def present( - name, - subscriptions=None, - region=None, - key=None, - keyid=None, - profile=None): - ''' +def present(name, subscriptions=None, region=None, key=None, keyid=None, profile=None): + """ Ensure the SNS topic exists. name @@ -104,117 +100,108 @@ def present( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - is_present = __salt__['boto_sns.exists']( + is_present = __salt__["boto_sns.exists"]( name, region=region, key=key, keyid=keyid, profile=profile ) if is_present: - ret['result'] = True - ret['comment'] = 'AWS SNS topic {0} present.'.format(name) + ret["result"] = True + ret["comment"] = "AWS SNS topic {0} present.".format(name) else: - if __opts__['test']: - msg = 'AWS SNS topic {0} is set to be created.'.format(name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "AWS SNS topic {0} is set to be created.".format(name) + ret["comment"] = msg + ret["result"] = None return ret - created = __salt__['boto_sns.create']( + created = __salt__["boto_sns.create"]( name, region=region, key=key, keyid=keyid, profile=profile ) if created: - msg = 'AWS SNS topic {0} created.'.format(name) - ret['comment'] = msg - ret['changes']['old'] = None - ret['changes']['new'] = {'topic': name, 'subscriptions': []} - ret['result'] = True + msg = "AWS SNS topic {0} created.".format(name) + ret["comment"] = msg + ret["changes"]["old"] = None + ret["changes"]["new"] = {"topic": name, "subscriptions": []} + ret["result"] = True else: - ret['comment'] = 'Failed to create {0} AWS SNS topic'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create {0} AWS SNS topic".format(name) + ret["result"] = False return ret if not subscriptions: return ret # Get current subscriptions - _subscriptions = __salt__['boto_sns.get_all_subscriptions_by_topic']( + _subscriptions = __salt__["boto_sns.get_all_subscriptions_by_topic"]( name, region=region, key=key, keyid=keyid, profile=profile ) # Convert subscriptions into a data strucure we can compare against _subscriptions = [ - {'protocol': s['Protocol'], 'endpoint': s['Endpoint']} - for s in _subscriptions + {"protocol": s["Protocol"], "endpoint": s["Endpoint"]} for s in _subscriptions ] for subscription in subscriptions: # If the subscription contains inline digest auth, AWS will *** the # password. So we need to do the same with ours if the regex matches # Example: https://user:****@my.endpoiint.com/foo/bar - _endpoint = subscription['endpoint'] - matches = re.search( - r'https://(?P<user>\w+):(?P<pass>\w+)@', - _endpoint) + _endpoint = subscription["endpoint"] + matches = re.search(r"https://(?P<user>\w+):(?P<pass>\w+)@", _endpoint) # We are using https and have auth creds - the password will be starred out, # so star out our password so we can still match it if matches is not None: - subscription['endpoint'] = _endpoint.replace( - matches.groupdict()['pass'], - '****') + subscription["endpoint"] = _endpoint.replace( + matches.groupdict()["pass"], "****" + ) if subscription not in _subscriptions: # Ensure the endpoint is set back to its original value, # incase we starred out a password - subscription['endpoint'] = _endpoint + subscription["endpoint"] = _endpoint - if __opts__['test']: - msg = ' AWS SNS subscription {0}:{1} to be set on topic {2}.'\ - .format( - subscription['protocol'], - subscription['endpoint'], - name) - ret['comment'] += msg - ret['result'] = None + if __opts__["test"]: + msg = " AWS SNS subscription {0}:{1} to be set on topic {2}.".format( + subscription["protocol"], subscription["endpoint"], name + ) + ret["comment"] += msg + ret["result"] = None continue - created = __salt__['boto_sns.subscribe']( - name, subscription['protocol'], subscription['endpoint'], - region=region, key=key, keyid=keyid, profile=profile) + created = __salt__["boto_sns.subscribe"]( + name, + subscription["protocol"], + subscription["endpoint"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if created: - msg = ' AWS SNS subscription {0}:{1} set on topic {2}.'\ - .format(subscription['protocol'], - subscription['endpoint'], - name) - ret['comment'] += msg - ret['changes'].setdefault('old', None) - ret['changes']\ - .setdefault('new', {})\ - .setdefault('subscriptions', [])\ - .append(subscription) - ret['result'] = True + msg = " AWS SNS subscription {0}:{1} set on topic {2}.".format( + subscription["protocol"], subscription["endpoint"], name + ) + ret["comment"] += msg + ret["changes"].setdefault("old", None) + ret["changes"].setdefault("new", {}).setdefault( + "subscriptions", [] + ).append(subscription) + ret["result"] = True else: - ret['result'] = False + ret["result"] = False return ret else: - msg = ' AWS SNS subscription {0}:{1} already set on topic {2}.'\ - .format( - subscription['protocol'], - subscription['endpoint'], - name) - ret['comment'] += msg + msg = " AWS SNS subscription {0}:{1} already set on topic {2}.".format( + subscription["protocol"], subscription["endpoint"], name + ) + ret["comment"] += msg return ret -def absent( - name, - region=None, - key=None, - keyid=None, - profile=None, - unsubscribe=False): - ''' +def absent(name, region=None, key=None, keyid=None, profile=None, unsubscribe=False): + """ Ensure the named sns topic is deleted. name @@ -238,50 +225,61 @@ def absent( deleting the SNS topic .. versionadded:: 2016.11.0 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_present = __salt__['boto_sns.exists']( + is_present = __salt__["boto_sns.exists"]( name, region=region, key=key, keyid=keyid, profile=profile ) if is_present: - subscriptions = __salt__['boto_sns.get_all_subscriptions_by_topic']( - name, region=region, key=key, keyid=keyid, profile=profile - ) if unsubscribe else [] + subscriptions = ( + __salt__["boto_sns.get_all_subscriptions_by_topic"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) + if unsubscribe + else [] + ) failed_unsubscribe_subscriptions = [] - if __opts__.get('test'): - ret['comment'] = ( - 'AWS SNS topic {0} is set to be removed. ' - '{1} subscription(s) will be removed.'.format(name, len(subscriptions)) + if __opts__.get("test"): + ret["comment"] = ( + "AWS SNS topic {0} is set to be removed. " + "{1} subscription(s) will be removed.".format(name, len(subscriptions)) ) - ret['result'] = None + ret["result"] = None return ret for subscription in subscriptions: - unsubscribed = __salt__['boto_sns.unsubscribe']( - name, subscription['SubscriptionArn'], region=region, - key=key, keyid=keyid, profile=profile + unsubscribed = __salt__["boto_sns.unsubscribe"]( + name, + subscription["SubscriptionArn"], + region=region, + key=key, + keyid=keyid, + profile=profile, ) if unsubscribed is False: failed_unsubscribe_subscriptions.append(subscription) - deleted = __salt__['boto_sns.delete']( - name, region=region, key=key, keyid=keyid, profile=profile) + deleted = __salt__["boto_sns.delete"]( + name, region=region, key=key, keyid=keyid, profile=profile + ) if deleted: - ret['comment'] = 'AWS SNS topic {0} deleted.'.format(name) - ret['changes']['new'] = None + ret["comment"] = "AWS SNS topic {0} deleted.".format(name) + ret["changes"]["new"] = None if unsubscribe is False: - ret['changes']['old'] = {'topic': name} + ret["changes"]["old"] = {"topic": name} else: - ret['changes']['old'] = {'topic': name, 'subscriptions': subscriptions} + ret["changes"]["old"] = {"topic": name, "subscriptions": subscriptions} if failed_unsubscribe_subscriptions: - ret['changes']['new'] = {'subscriptions': failed_unsubscribe_subscriptions} + ret["changes"]["new"] = { + "subscriptions": failed_unsubscribe_subscriptions + } else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} AWS SNS topic.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} AWS SNS topic.".format(name) else: - ret['comment'] = 'AWS SNS topic {0} does not exist.'.format(name) + ret["comment"] = "AWS SNS topic {0} does not exist.".format(name) return ret diff --git a/salt/states/boto_sqs.py b/salt/states/boto_sqs.py index 964c6e863ec..b198a5f4f80 100644 --- a/salt/states/boto_sqs.py +++ b/salt/states/boto_sqs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage SQS Queues .. versionadded:: 2014.7.0 @@ -56,7 +56,7 @@ passed in as a dict, or as a string to pull from pillars or minion config: - profile: keyid: GKTADJGHEIQSXMKKRBJ08H key: askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -73,21 +73,18 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - return 'boto_sqs' if 'boto_sqs.exists' in __salt__ else False + """ + if "boto_sqs.exists" in __salt__: + return "boto_sqs" + return (False, "boto_sqs module could not be loaded") def present( - name, - attributes=None, - region=None, - key=None, - keyid=None, - profile=None, + name, attributes=None, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Ensure the SQS queue exists. name @@ -108,38 +105,32 @@ def present( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ ret = { - 'name': name, - 'result': True, - 'comment': [], - 'changes': {}, + "name": name, + "result": True, + "comment": [], + "changes": {}, } - r = __salt__['boto_sqs.exists']( - name, - region=region, - key=key, - keyid=keyid, - profile=profile, + r = __salt__["boto_sqs.exists"]( + name, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in r: - ret['result'] = False - ret['comment'].append(r['error']) + if "error" in r: + ret["result"] = False + ret["comment"].append(r["error"]) return ret - if r['result']: - ret['comment'].append('SQS queue {0} present.'.format(name)) + if r["result"]: + ret["comment"].append("SQS queue {0} present.".format(name)) else: - if __opts__['test']: - ret['result'] = None - ret['comment'].append( - 'SQS queue {0} is set to be created.'.format(name), - ) - ret['changes'] = {'old': None, 'new': name} + if __opts__["test"]: + ret["result"] = None + ret["comment"].append("SQS queue {0} is set to be created.".format(name),) + ret["changes"] = {"old": None, "new": name} return ret - r = __salt__['boto_sqs.create']( + r = __salt__["boto_sqs.create"]( name, attributes=attributes, region=region, @@ -147,117 +138,96 @@ def present( keyid=keyid, profile=profile, ) - if 'error' in r: - ret['result'] = False - ret['comment'].append( - 'Failed to create SQS queue {0}: {1}'.format(name, r['error']), + if "error" in r: + ret["result"] = False + ret["comment"].append( + "Failed to create SQS queue {0}: {1}".format(name, r["error"]), ) return ret - ret['comment'].append('SQS queue {0} created.'.format(name)) - ret['changes']['old'] = None - ret['changes']['new'] = name + ret["comment"].append("SQS queue {0} created.".format(name)) + ret["changes"]["old"] = None + ret["changes"]["new"] = name # Return immediately, as the create call also set all attributes return ret if not attributes: return ret - r = __salt__['boto_sqs.get_attributes']( - name, - region=region, - key=key, - keyid=keyid, - profile=profile, + r = __salt__["boto_sqs.get_attributes"]( + name, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in r: - ret['result'] = False - ret['comment'].append( - 'Failed to get queue attributes: {0}'.format(r['error']), - ) + if "error" in r: + ret["result"] = False + ret["comment"].append("Failed to get queue attributes: {0}".format(r["error"]),) return ret - current_attributes = r['result'] + current_attributes = r["result"] attrs_to_set = {} for attr, val in six.iteritems(attributes): _val = current_attributes.get(attr, None) - if attr == 'Policy': + if attr == "Policy": # Normalize by brute force if isinstance(_val, six.string_types): _val = salt.utils.json.loads(_val) if isinstance(val, six.string_types): val = salt.utils.json.loads(val) if _val != val: - log.debug('Policies differ:\n%s\n%s', _val, val) + log.debug("Policies differ:\n%s\n%s", _val, val) attrs_to_set[attr] = salt.utils.json.dumps(val, sort_keys=True) elif six.text_type(_val) != six.text_type(val): - log.debug('Attributes differ:\n%s\n%s', _val, val) + log.debug("Attributes differ:\n%s\n%s", _val, val) attrs_to_set[attr] = val - attr_names = ', '.join(attrs_to_set) + attr_names = ", ".join(attrs_to_set) if not attrs_to_set: - ret['comment'].append('Queue attributes already set correctly.') + ret["comment"].append("Queue attributes already set correctly.") return ret final_attributes = current_attributes.copy() final_attributes.update(attrs_to_set) def _yaml_safe_dump(attrs): - ''' + """ Safely dump YAML using a readable flow style - ''' - dumper = __utils__['yaml.get_dumper']('IndentedSafeOrderedDumper') - return __utils__['yaml.dump']( - attrs, - default_flow_style=False, - Dumper=dumper) + """ + dumper = __utils__["yaml.get_dumper"]("IndentedSafeOrderedDumper") + return __utils__["yaml.dump"](attrs, default_flow_style=False, Dumper=dumper) - attributes_diff = ''.join(difflib.unified_diff( - _yaml_safe_dump(current_attributes).splitlines(True), - _yaml_safe_dump(final_attributes).splitlines(True), - )) + attributes_diff = "".join( + difflib.unified_diff( + _yaml_safe_dump(current_attributes).splitlines(True), + _yaml_safe_dump(final_attributes).splitlines(True), + ) + ) - if __opts__['test']: - ret['result'] = None - ret['comment'].append( - 'Attribute(s) {0} set to be updated:\n{1}'.format( - attr_names, - attributes_diff, + if __opts__["test"]: + ret["result"] = None + ret["comment"].append( + "Attribute(s) {0} set to be updated:\n{1}".format( + attr_names, attributes_diff, ) ) - ret['changes'] = {'attributes': {'diff': attributes_diff}} + ret["changes"] = {"attributes": {"diff": attributes_diff}} return ret - r = __salt__['boto_sqs.set_attributes']( - name, - attrs_to_set, - region=region, - key=key, - keyid=keyid, - profile=profile, + r = __salt__["boto_sqs.set_attributes"]( + name, attrs_to_set, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in r: - ret['result'] = False - ret['comment'].append( - 'Failed to set queue attributes: {0}'.format(r['error']), - ) + if "error" in r: + ret["result"] = False + ret["comment"].append("Failed to set queue attributes: {0}".format(r["error"]),) return ret - ret['comment'].append( - 'Updated SQS queue attribute(s) {0}.'.format(attr_names), - ) - ret['changes']['attributes'] = {'diff': attributes_diff} + ret["comment"].append("Updated SQS queue attribute(s) {0}.".format(attr_names),) + ret["changes"]["attributes"] = {"diff": attributes_diff} return ret def absent( - name, - region=None, - key=None, - keyid=None, - profile=None, + name, region=None, key=None, keyid=None, profile=None, ): - ''' + """ Ensure the named sqs queue is deleted. name @@ -275,47 +245,36 @@ def absent( profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_sqs.exists']( - name, - region=region, - key=key, - keyid=keyid, - profile=profile, + r = __salt__["boto_sqs.exists"]( + name, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in r: - ret['result'] = False - ret['comment'] = six.text_type(r['error']) + if "error" in r: + ret["result"] = False + ret["comment"] = six.text_type(r["error"]) return ret - if not r['result']: - ret['comment'] = 'SQS queue {0} does not exist in {1}.'.format( - name, - region, - ) + if not r["result"]: + ret["comment"] = "SQS queue {0} does not exist in {1}.".format(name, region,) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'SQS queue {0} is set to be removed.'.format(name) - ret['changes'] = {'old': name, 'new': None} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "SQS queue {0} is set to be removed.".format(name) + ret["changes"] = {"old": name, "new": None} return ret - r = __salt__['boto_sqs.delete']( - name, - region=region, - key=key, - keyid=keyid, - profile=profile, + r = __salt__["boto_sqs.delete"]( + name, region=region, key=key, keyid=keyid, profile=profile, ) - if 'error' in r: - ret['result'] = False - ret['comment'] = six.text_type(r['error']) + if "error" in r: + ret["result"] = False + ret["comment"] = six.text_type(r["error"]) return ret - ret['comment'] = 'SQS queue {0} was deleted.'.format(name) - ret['changes']['old'] = name - ret['changes']['new'] = None + ret["comment"] = "SQS queue {0} was deleted.".format(name) + ret["changes"]["old"] = name + ret["changes"]["new"] = None return ret diff --git a/salt/states/boto_vpc.py b/salt/states/boto_vpc.py index dc0338344eb..5dd240a6d64 100644 --- a/salt/states/boto_vpc.py +++ b/salt/states/boto_vpc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage VPCs ================= @@ -140,39 +140,52 @@ Delete also accepts a VPC peering connection id. boto_vpc.delete_vpc_peering_connection: - conn_id: pcx-1873c371 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.dictupdate as dictupdate + # Import Salt Libs from salt.ext import six -import salt.utils.dictupdate as dictupdate -__virtualname__ = 'boto_vpc' +__virtualname__ = "boto_vpc" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if boto is available. - ''' - boto_version = '2.8.0' - boto3_version = '1.2.6' - if 'boto_vpc.exists' in __salt__: + """ + boto_version = "2.8.0" + boto3_version = "1.2.6" + if "boto_vpc.exists" in __salt__: return __virtualname__ else: - return False, 'The following libraries are required to run the boto_vpc state module: ' \ - 'boto >= {0} and boto3 >= {1}.'.format(boto_version, - boto3_version) + return ( + False, + "The following libraries are required to run the boto_vpc state module: " + "boto >= {0} and boto3 >= {1}.".format(boto_version, boto3_version), + ) -def present(name, cidr_block, instance_tenancy=None, dns_support=None, - dns_hostnames=None, tags=None, region=None, key=None, keyid=None, - profile=None): - ''' +def present( + name, + cidr_block, + instance_tenancy=None, + dns_support=None, + dns_hostnames=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure VPC exists. name @@ -207,46 +220,52 @@ def present(name, cidr_block, instance_tenancy=None, dns_support=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.exists'](name=name, tags=tags, region=region, - key=key, keyid=keyid, profile=profile) + r = __salt__["boto_vpc.exists"]( + name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create VPC: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create VPC: {0}.".format(r["error"]["message"]) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'VPC {0} is set to be created.'.format(name) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "VPC {0} is set to be created.".format(name) + ret["result"] = None return ret - r = __salt__['boto_vpc.create'](cidr_block, instance_tenancy=instance_tenancy, vpc_name=name, - enable_dns_support=dns_support, enable_dns_hostnames=dns_hostnames, - tags=tags, region=region, key=key, keyid=keyid, - profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Error in creating VPC: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.create"]( + cidr_block, + instance_tenancy=instance_tenancy, + vpc_name=name, + enable_dns_support=dns_support, + enable_dns_hostnames=dns_hostnames, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Error in creating VPC: {0}.".format(r["error"]["message"]) return ret - _describe = __salt__['boto_vpc.describe'](vpc_id=r['id'], region=region, key=key, - keyid=keyid, profile=profile) - ret['changes']['old'] = {'vpc': None} - ret['changes']['new'] = _describe - ret['comment'] = 'VPC {0} created.'.format(name) + _describe = __salt__["boto_vpc.describe"]( + vpc_id=r["id"], region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["old"] = {"vpc": None} + ret["changes"]["new"] = _describe + ret["comment"] = "VPC {0} created.".format(name) return ret - ret['comment'] = 'VPC present.' + ret["comment"] = "VPC present." return ret def absent(name, tags=None, region=None, key=None, keyid=None, profile=None): - ''' + """ Ensure VPC with passed properties is absent. name @@ -267,48 +286,57 @@ def absent(name, tags=None, region=None, key=None, keyid=None, profile=None): profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.get_id'](name=name, tags=tags, region=region, - key=key, keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete VPC: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.get_id"]( + name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete VPC: {0}.".format(r["error"]["message"]) return ret - _id = r.get('id') + _id = r.get("id") if not _id: - ret['comment'] = '{0} VPC does not exist.'.format(name) + ret["comment"] = "{0} VPC does not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'VPC {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "VPC {0} is set to be removed.".format(name) + ret["result"] = None return ret - r = __salt__['boto_vpc.delete'](vpc_name=name, tags=tags, - region=region, key=key, - keyid=keyid, profile=profile) - if not r['deleted']: - ret['result'] = False - ret['comment'] = 'Failed to delete VPC: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.delete"]( + vpc_name=name, tags=tags, region=region, key=key, keyid=keyid, profile=profile + ) + if not r["deleted"]: + ret["result"] = False + ret["comment"] = "Failed to delete VPC: {0}.".format(r["error"]["message"]) return ret - ret['changes']['old'] = {'vpc': _id} - ret['changes']['new'] = {'vpc': None} - ret['comment'] = 'VPC {0} deleted.'.format(name) + ret["changes"]["old"] = {"vpc": _id} + ret["changes"]["new"] = {"vpc": None} + ret["comment"] = "VPC {0} deleted.".format(name) return ret -def dhcp_options_present(name, dhcp_options_id=None, vpc_name=None, vpc_id=None, - domain_name=None, domain_name_servers=None, ntp_servers=None, - netbios_name_servers=None, netbios_node_type=None, - tags=None, region=None, key=None, keyid=None, profile=None): - ''' +def dhcp_options_present( + name, + dhcp_options_id=None, + vpc_name=None, + vpc_id=None, + domain_name=None, + domain_name_servers=None, + ntp_servers=None, + netbios_name_servers=None, + netbios_node_type=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure a set of DHCP options with the given settings exist. Note that the current implementation only SETS values during option set creation. It is unable to update option sets in place, and thus merely @@ -372,64 +400,77 @@ def dhcp_options_present(name, dhcp_options_id=None, vpc_name=None, vpc_id=None, contains a dict with region, key and keyid. .. versionadded:: 2016.3.0 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } - _new = {'domain_name': domain_name, - 'domain_name_servers': domain_name_servers, - 'ntp_servers': ntp_servers, - 'netbios_name_servers': netbios_name_servers, - 'netbios_node_type': netbios_node_type - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + _new = { + "domain_name": domain_name, + "domain_name_servers": domain_name_servers, + "ntp_servers": ntp_servers, + "netbios_name_servers": netbios_name_servers, + "netbios_node_type": netbios_node_type, + } # boto provides no "update_dhcp_options()" functionality, and you can't delete it if # it's attached, and you can't detach it if it's the only one, so just check if it's # there or not, and make no effort to validate its actual settings... :( ### TODO - add support for multiple sets of DHCP options, and then for "swapping out" ### sets by creating new, mapping, then deleting the old. - r = __salt__['boto_vpc.dhcp_options_exists'](dhcp_options_id=dhcp_options_id, - dhcp_options_name=name, - region=region, key=key, keyid=keyid, - profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to validate DHCP options: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.dhcp_options_exists"]( + dhcp_options_id=dhcp_options_id, + dhcp_options_name=name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to validate DHCP options: {0}.".format( + r["error"]["message"] + ) return ret - if r.get('exists'): - ret['comment'] = 'DHCP options already present.' + if r.get("exists"): + ret["comment"] = "DHCP options already present." return ret else: - if __opts__['test']: - ret['comment'] = 'DHCP options {0} are set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "DHCP options {0} are set to be created.".format(name) + ret["result"] = None return ret - r = __salt__['boto_vpc.create_dhcp_options'](domain_name=domain_name, - domain_name_servers=domain_name_servers, - ntp_servers=ntp_servers, - netbios_name_servers=netbios_name_servers, - netbios_node_type=netbios_node_type, - dhcp_options_name=name, tags=tags, - vpc_id=vpc_id, vpc_name=vpc_name, - region=region, key=key, keyid=keyid, - profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create DHCP options: {0}'.format(r['error']['message']) + r = __salt__["boto_vpc.create_dhcp_options"]( + domain_name=domain_name, + domain_name_servers=domain_name_servers, + ntp_servers=ntp_servers, + netbios_name_servers=netbios_name_servers, + netbios_node_type=netbios_node_type, + dhcp_options_name=name, + tags=tags, + vpc_id=vpc_id, + vpc_name=vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create DHCP options: {0}".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'dhcp_options': None} - ret['changes']['new'] = {'dhcp_options': _new} - ret['comment'] = 'DHCP options {0} created.'.format(name) + ret["changes"]["old"] = {"dhcp_options": None} + ret["changes"]["new"] = {"dhcp_options": _new} + ret["comment"] = "DHCP options {0} created.".format(name) return ret -def dhcp_options_absent(name=None, dhcp_options_id=None, region=None, key=None, keyid=None, profile=None): - ''' +def dhcp_options_absent( + name=None, dhcp_options_id=None, region=None, key=None, keyid=None, profile=None +): + """ Ensure a set of DHCP options with the given settings exist. name @@ -458,52 +499,63 @@ def dhcp_options_absent(name=None, dhcp_options_id=None, region=None, key=None, contains a dict with region, key and keyid. .. versionadded:: 2016.3.0 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.get_resource_id']('dhcp_options', name=name, - region=region, key=key, - keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete DHCP options: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.get_resource_id"]( + "dhcp_options", name=name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete DHCP options: {0}.".format( + r["error"]["message"] + ) return ret - _id = r.get('id') + _id = r.get("id") if not _id: - ret['comment'] = 'DHCP options {0} do not exist.'.format(name) + ret["comment"] = "DHCP options {0} do not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'DHCP options {0} are set to be deleted.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "DHCP options {0} are set to be deleted.".format(name) + ret["result"] = None return ret - r = __salt__['boto_vpc.delete_dhcp_options'](dhcp_options_id=r['id'], region=region, - key=key, keyid=keyid, profile=profile) - if not r.get('deleted'): - ret['result'] = False - ret['comment'] = 'Failed to delete DHCP options: {0}'.format(r['error']['message']) + r = __salt__["boto_vpc.delete_dhcp_options"]( + dhcp_options_id=r["id"], region=region, key=key, keyid=keyid, profile=profile + ) + if not r.get("deleted"): + ret["result"] = False + ret["comment"] = "Failed to delete DHCP options: {0}".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'dhcp_options': _id} - ret['changes']['new'] = {'dhcp_options': None} - ret['comment'] = 'DHCP options {0} deleted.'.format(name) + ret["changes"]["old"] = {"dhcp_options": _id} + ret["changes"]["new"] = {"dhcp_options": None} + ret["comment"] = "DHCP options {0} deleted.".format(name) return ret -def subnet_present(name, cidr_block, vpc_name=None, vpc_id=None, - availability_zone=None, tags=None, - region=None, key=None, - keyid=None, profile=None, - route_table_id=None, route_table_name=None, auto_assign_public_ipv4=False): +def subnet_present( + name, + cidr_block, + vpc_name=None, + vpc_id=None, + availability_zone=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, + route_table_id=None, + route_table_name=None, + auto_assign_public_ipv4=False, +): - ''' + """ Ensure a subnet exists. name @@ -551,21 +603,22 @@ def subnet_present(name, cidr_block, vpc_name=None, vpc_id=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.subnet_exists'](subnet_name=name, tags=tags, - region=region, key=key, - keyid=keyid, profile=profile) + r = __salt__["boto_vpc.subnet_exists"]( + subnet_name=name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create subnet: {0}.'.format(r['error']['message']) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create subnet: {0}.".format(r["error"]["message"]) return ret route_table_desc = None @@ -576,103 +629,156 @@ def subnet_present(name, cidr_block, vpc_name=None, vpc_id=None, route_table_found = False if route_table_id: rtid = route_table_id - rt = __salt__['boto_vpc.route_table_exists'](route_table_id=route_table_id, - region=region, key=key, keyid=keyid, - profile=profile) + rt = __salt__["boto_vpc.route_table_exists"]( + route_table_id=route_table_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) elif route_table_name: rtid = route_table_name - rt = __salt__['boto_vpc.route_table_exists'](route_table_name=route_table_name, - region=region, key=key, keyid=keyid, - profile=profile) + rt = __salt__["boto_vpc.route_table_exists"]( + route_table_name=route_table_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if rt: - if 'exists' in rt: - if rt['exists']: + if "exists" in rt: + if rt["exists"]: if route_table_id: route_table_found = True - route_table_desc = __salt__['boto_vpc.describe_route_table'](route_table_id=route_table_id, - region=region, key=key, keyid=keyid, - profile=profile) + route_table_desc = __salt__["boto_vpc.describe_route_table"]( + route_table_id=route_table_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) elif route_table_name: route_table_found = True - route_table_desc = __salt__['boto_vpc.describe_route_table'](route_table_name=route_table_name, - region=region, key=key, keyid=keyid, - profile=profile) + route_table_desc = __salt__["boto_vpc.describe_route_table"]( + route_table_name=route_table_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not route_table_found: - ret['result'] = False - ret['comment'] = 'The specified route table {0} could not be found.'.format(rtid) + ret["result"] = False + ret["comment"] = "The specified route table {0} could not be found.".format( + rtid + ) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'Subnet {0} is set to be created.'.format(name) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "Subnet {0} is set to be created.".format(name) + ret["result"] = None return ret - r = __salt__['boto_vpc.create_subnet'](subnet_name=name, - cidr_block=cidr_block, - availability_zone=availability_zone, - auto_assign_public_ipv4=auto_assign_public_ipv4, - vpc_name=vpc_name, vpc_id=vpc_id, - tags=tags, region=region, - key=key, keyid=keyid, - profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create subnet: {0}'.format(r['error']['message']) + r = __salt__["boto_vpc.create_subnet"]( + subnet_name=name, + cidr_block=cidr_block, + availability_zone=availability_zone, + auto_assign_public_ipv4=auto_assign_public_ipv4, + vpc_name=vpc_name, + vpc_id=vpc_id, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create subnet: {0}".format( + r["error"]["message"] + ) return ret - _describe = __salt__['boto_vpc.describe_subnet'](subnet_id=r['id'], region=region, key=key, - keyid=keyid, profile=profile) - ret['changes']['old'] = {'subnet': None} - ret['changes']['new'] = _describe - ret['comment'] = 'Subnet {0} created.'.format(name) + _describe = __salt__["boto_vpc.describe_subnet"]( + subnet_id=r["id"], region=region, key=key, keyid=keyid, profile=profile + ) + ret["changes"]["old"] = {"subnet": None} + ret["changes"]["new"] = _describe + ret["comment"] = "Subnet {0} created.".format(name) else: - ret['comment'] = 'Subnet present.' + ret["comment"] = "Subnet present." if route_table_desc: if not _describe: - _describe = __salt__['boto_vpc.describe_subnet'](subnet_name=name, region=region, - key=key, keyid=keyid, profile=profile) - if not _verify_subnet_association(route_table_desc, _describe['subnet']['id']): - if __opts__['test']: - msg = 'Subnet is set to be associated with route table {0}'.format(rtid) - ret['comment'] = ' '.join([ret['comment'], msg]) - ret['result'] = None + _describe = __salt__["boto_vpc.describe_subnet"]( + subnet_name=name, region=region, key=key, keyid=keyid, profile=profile + ) + if not _verify_subnet_association(route_table_desc, _describe["subnet"]["id"]): + if __opts__["test"]: + msg = "Subnet is set to be associated with route table {0}".format(rtid) + ret["comment"] = " ".join([ret["comment"], msg]) + ret["result"] = None return ret - if 'explicit_route_table_association_id' in _describe['subnet']: - log.debug('Need to disassociate from existing route table') - drt_ret = __salt__['boto_vpc.disassociate_route_table'](_describe['subnet']['explicit_route_table_association_id'], - region=region, key=key, keyid=keyid, profile=profile) - if not drt_ret['disassociated']: - msg = 'Unable to disassociate subnet {0} with its current route table.'.format(name) - ret['comment'] = ' '.join([ret['comment'], msg]) - ret['result'] = False + if "explicit_route_table_association_id" in _describe["subnet"]: + log.debug("Need to disassociate from existing route table") + drt_ret = __salt__["boto_vpc.disassociate_route_table"]( + _describe["subnet"]["explicit_route_table_association_id"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not drt_ret["disassociated"]: + msg = "Unable to disassociate subnet {0} with its current route table.".format( + name + ) + ret["comment"] = " ".join([ret["comment"], msg]) + ret["result"] = False return ret - if 'old' not in ret['changes']: - ret['changes']['old'] = _describe - art_ret = __salt__['boto_vpc.associate_route_table'](route_table_id=route_table_desc['id'], - subnet_name=name, region=region, - key=key, keyid=keyid, profile=profile) - if 'error' in art_ret: - msg = 'Failed to associate subnet {0} with route table {1}: {2}.'.format(name, rtid, - art_ret['error']['message']) - ret['comment'] = ' '.join([ret['comment'], msg]) - ret['result'] = False + if "old" not in ret["changes"]: + ret["changes"]["old"] = _describe + art_ret = __salt__["boto_vpc.associate_route_table"]( + route_table_id=route_table_desc["id"], + subnet_name=name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in art_ret: + msg = "Failed to associate subnet {0} with route table {1}: {2}.".format( + name, rtid, art_ret["error"]["message"] + ) + ret["comment"] = " ".join([ret["comment"], msg]) + ret["result"] = False return ret else: - msg = 'Subnet successfully associated with route table {0}.'.format(rtid) - ret['comment'] = ' '.join([ret['comment'], msg]) - if 'new' not in ret['changes']: - ret['changes']['new'] = __salt__['boto_vpc.describe_subnet'](subnet_name=name, region=region, - key=key, keyid=keyid, profile=profile) + msg = "Subnet successfully associated with route table {0}.".format( + rtid + ) + ret["comment"] = " ".join([ret["comment"], msg]) + if "new" not in ret["changes"]: + ret["changes"]["new"] = __salt__["boto_vpc.describe_subnet"]( + subnet_name=name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) else: - ret['changes']['new']['subnet']['explicit_route_table_association_id'] = art_ret['association_id'] + ret["changes"]["new"]["subnet"][ + "explicit_route_table_association_id" + ] = art_ret["association_id"] else: - ret['comment'] = ' '.join([ret['comment'], - 'Subnet is already associated with route table {0}'.format(rtid)]) + ret["comment"] = " ".join( + [ + ret["comment"], + "Subnet is already associated with route table {0}".format(rtid), + ] + ) return ret def _verify_subnet_association(route_table_desc, subnet_id): - ''' + """ Helper function verify a subnet's route table association route_table_desc @@ -682,17 +788,19 @@ def _verify_subnet_association(route_table_desc, subnet_id): the subnet id to verify .. versionadded:: 2016.11.0 - ''' + """ if route_table_desc: - if 'associations' in route_table_desc: - for association in route_table_desc['associations']: - if association['subnet_id'] == subnet_id: + if "associations" in route_table_desc: + for association in route_table_desc["associations"]: + if association["subnet_id"] == subnet_id: return True return False -def subnet_absent(name=None, subnet_id=None, region=None, key=None, keyid=None, profile=None): - ''' +def subnet_absent( + name=None, subnet_id=None, region=None, key=None, keyid=None, profile=None +): + """ Ensure subnet with passed properties is absent. name @@ -710,51 +818,54 @@ def subnet_absent(name=None, subnet_id=None, region=None, key=None, keyid=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.get_resource_id']('subnet', name=name, - region=region, key=key, - keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete subnet: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.get_resource_id"]( + "subnet", name=name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete subnet: {0}.".format(r["error"]["message"]) return ret - _id = r.get('id') + _id = r.get("id") if not _id: - ret['comment'] = '{0} subnet does not exist.'.format(name) + ret["comment"] = "{0} subnet does not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Subnet {0} ({1}) is set to be removed.'.format(name, r['id']) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Subnet {0} ({1}) is set to be removed.".format(name, r["id"]) + ret["result"] = None return ret - r = __salt__['boto_vpc.delete_subnet'](subnet_name=name, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('deleted'): - ret['result'] = False - ret['comment'] = 'Failed to delete subnet: {0}'.format(r['error']['message']) + r = __salt__["boto_vpc.delete_subnet"]( + subnet_name=name, region=region, key=key, keyid=keyid, profile=profile + ) + if not r.get("deleted"): + ret["result"] = False + ret["comment"] = "Failed to delete subnet: {0}".format(r["error"]["message"]) return ret - ret['changes']['old'] = {'subnet': _id} - ret['changes']['new'] = {'subnet': None} - ret['comment'] = 'Subnet {0} deleted.'.format(name) + ret["changes"]["old"] = {"subnet": _id} + ret["changes"]["new"] = {"subnet": None} + ret["comment"] = "Subnet {0} deleted.".format(name) return ret -def internet_gateway_present(name, vpc_name=None, vpc_id=None, - tags=None, region=None, key=None, - keyid=None, profile=None): - ''' +def internet_gateway_present( + name, + vpc_name=None, + vpc_id=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure an internet gateway exists. name @@ -782,48 +893,59 @@ def internet_gateway_present(name, vpc_name=None, vpc_id=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.resource_exists']('internet_gateway', name=name, - region=region, key=key, - keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create internet gateway: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.resource_exists"]( + "internet_gateway", + name=name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create internet gateway: {0}.".format( + r["error"]["message"] + ) return ret - if not r.get('exists'): - if __opts__['test']: - ret['comment'] = 'Internet gateway {0} is set to be created.'.format(name) - ret['result'] = None + if not r.get("exists"): + if __opts__["test"]: + ret["comment"] = "Internet gateway {0} is set to be created.".format(name) + ret["result"] = None return ret - r = __salt__['boto_vpc.create_internet_gateway'](internet_gateway_name=name, - vpc_name=vpc_name, vpc_id=vpc_id, - tags=tags, region=region, - key=key, keyid=keyid, - profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create internet gateway: {0}'.format(r['error']['message']) + r = __salt__["boto_vpc.create_internet_gateway"]( + internet_gateway_name=name, + vpc_name=vpc_name, + vpc_id=vpc_id, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create internet gateway: {0}".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'internet_gateway': None} - ret['changes']['new'] = {'internet_gateway': r['id']} - ret['comment'] = 'Internet gateway {0} created.'.format(name) + ret["changes"]["old"] = {"internet_gateway": None} + ret["changes"]["new"] = {"internet_gateway": r["id"]} + ret["comment"] = "Internet gateway {0} created.".format(name) return ret - ret['comment'] = 'Internet gateway {0} present.'.format(name) + ret["comment"] = "Internet gateway {0} present.".format(name) return ret -def internet_gateway_absent(name, detach=False, region=None, - key=None, keyid=None, profile=None): - ''' +def internet_gateway_absent( + name, detach=False, region=None, key=None, keyid=None, profile=None +): + """ Ensure the named internet gateway is absent. name @@ -844,49 +966,68 @@ def internet_gateway_absent(name, detach=False, region=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.get_resource_id']('internet_gateway', name=name, - region=region, key=key, - keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete internet gateway: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.get_resource_id"]( + "internet_gateway", + name=name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete internet gateway: {0}.".format( + r["error"]["message"] + ) return ret - igw_id = r['id'] + igw_id = r["id"] if not igw_id: - ret['comment'] = 'Internet gateway {0} does not exist.'.format(name) + ret["comment"] = "Internet gateway {0} does not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Internet gateway {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Internet gateway {0} is set to be removed.".format(name) + ret["result"] = None return ret - r = __salt__['boto_vpc.delete_internet_gateway'](internet_gateway_name=name, - detach=detach, region=region, - key=key, keyid=keyid, - profile=profile) - if not r.get('deleted'): - ret['result'] = False - ret['comment'] = 'Failed to delete internet gateway: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.delete_internet_gateway"]( + internet_gateway_name=name, + detach=detach, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("deleted"): + ret["result"] = False + ret["comment"] = "Failed to delete internet gateway: {0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'internet_gateway': igw_id} - ret['changes']['new'] = {'internet_gateway': None} - ret['comment'] = 'Internet gateway {0} deleted.'.format(name) + ret["changes"]["old"] = {"internet_gateway": igw_id} + ret["changes"]["new"] = {"internet_gateway": None} + ret["comment"] = "Internet gateway {0} deleted.".format(name) return ret -def route_table_present(name, vpc_name=None, vpc_id=None, routes=None, - subnet_ids=None, subnet_names=None, tags=None, - region=None, key=None, keyid=None, profile=None): - ''' +def route_table_present( + name, + vpc_name=None, + vpc_id=None, + routes=None, + subnet_ids=None, + subnet_names=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Ensure route table with routes exists and is associated to a VPC. This function requires boto3 to be installed if nat gatewyas are specified. @@ -945,217 +1086,329 @@ def route_table_present(name, vpc_name=None, vpc_id=None, routes=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - _ret = _route_table_present(name=name, vpc_name=vpc_name, vpc_id=vpc_id, - tags=tags, region=region, key=key, - keyid=keyid, profile=profile) - ret['changes'] = _ret['changes'] - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + _ret = _route_table_present( + name=name, + vpc_name=vpc_name, + vpc_id=vpc_id, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"] = _ret["changes"] + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - if ret['result'] is None and __opts__['test']: + if ret["result"] is None and __opts__["test"]: return ret - _ret = _routes_present(route_table_name=name, routes=routes, tags=tags, - region=region, key=key, keyid=keyid, profile=profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + _ret = _routes_present( + route_table_name=name, + routes=routes, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret - _ret = _subnets_present(route_table_name=name, subnet_ids=subnet_ids, - subnet_names=subnet_names, tags=tags, region=region, - key=key, keyid=keyid, profile=profile) - ret['changes'] = dictupdate.update(ret['changes'], _ret['changes']) - ret['comment'] = ' '.join([ret['comment'], _ret['comment']]) - if not _ret['result']: - ret['result'] = _ret['result'] - if ret['result'] is False: + _ret = _subnets_present( + route_table_name=name, + subnet_ids=subnet_ids, + subnet_names=subnet_names, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"] = dictupdate.update(ret["changes"], _ret["changes"]) + ret["comment"] = " ".join([ret["comment"], _ret["comment"]]) + if not _ret["result"]: + ret["result"] = _ret["result"] + if ret["result"] is False: return ret return ret -def _route_table_present(name, vpc_name=None, vpc_id=None, tags=None, region=None, - key=None, keyid=None, profile=None): - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } +def _route_table_present( + name, + vpc_name=None, + vpc_id=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.get_resource_id'](resource='route_table', name=name, - region=region, key=key, keyid=keyid, - profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to create route table: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.get_resource_id"]( + resource="route_table", + name=name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to create route table: {0}.".format( + r["error"]["message"] + ) return ret - _id = r.get('id') + _id = r.get("id") if not _id: - if __opts__['test']: - msg = 'Route table {0} is set to be created.'.format(name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Route table {0} is set to be created.".format(name) + ret["comment"] = msg + ret["result"] = None return ret - r = __salt__['boto_vpc.create_route_table'](route_table_name=name, - vpc_name=vpc_name, - vpc_id=vpc_id, tags=tags, - region=region, key=key, - keyid=keyid, profile=profile) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create route table: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.create_route_table"]( + route_table_name=name, + vpc_name=vpc_name, + vpc_id=vpc_id, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create route table: {0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'route_table': None} - ret['changes']['new'] = {'route_table': r['id']} - ret['comment'] = 'Route table {0} created.'.format(name) + ret["changes"]["old"] = {"route_table": None} + ret["changes"]["new"] = {"route_table": r["id"]} + ret["comment"] = "Route table {0} created.".format(name) return ret - ret['comment'] = 'Route table {0} ({1}) present.'.format(name, _id) + ret["comment"] = "Route table {0} ({1}) present.".format(name, _id) return ret -def _routes_present(route_table_name, routes, tags=None, region=None, key=None, keyid=None, profile=None): - ret = {'name': route_table_name, - 'result': True, - 'comment': '', - 'changes': {} - } +def _routes_present( + route_table_name, routes, tags=None, region=None, key=None, keyid=None, profile=None +): + ret = {"name": route_table_name, "result": True, "comment": "", "changes": {}} - route_table = __salt__['boto_vpc.describe_route_tables'](route_table_name=route_table_name, tags=tags, - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in route_table: - msg = 'Could not retrieve configuration for route table {0}: {1}`.'.format(route_table_name, - route_table['error']['message']) - ret['comment'] = msg - ret['result'] = False + route_table = __salt__["boto_vpc.describe_route_tables"]( + route_table_name=route_table_name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in route_table: + msg = "Could not retrieve configuration for route table {0}: {1}`.".format( + route_table_name, route_table["error"]["message"] + ) + ret["comment"] = msg + ret["result"] = False return ret route_table = route_table[0] _routes = [] if routes: - route_keys = set(('gateway_id', 'instance_id', 'destination_cidr_block', 'interface_id', 'vpc_peering_connection_id', 'nat_gateway_id')) + route_keys = set( + ( + "gateway_id", + "instance_id", + "destination_cidr_block", + "interface_id", + "vpc_peering_connection_id", + "nat_gateway_id", + ) + ) for i in routes: - #_r = {k:i[k] for k in i if k in route_keys} + # _r = {k:i[k] for k in i if k in route_keys} _r = {} for k, v in six.iteritems(i): if k in route_keys: _r[k] = i[k] - if i.get('internet_gateway_name'): - r = __salt__['boto_vpc.get_resource_id']('internet_gateway', name=i['internet_gateway_name'], - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - msg = 'Error looking up id for internet gateway {0}: {1}'.format(i.get('internet_gateway_name'), - r['error']['message']) - ret['comment'] = msg - ret['result'] = False + if i.get("internet_gateway_name"): + r = __salt__["boto_vpc.get_resource_id"]( + "internet_gateway", + name=i["internet_gateway_name"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + msg = "Error looking up id for internet gateway {0}: {1}".format( + i.get("internet_gateway_name"), r["error"]["message"] + ) + ret["comment"] = msg + ret["result"] = False return ret - if r['id'] is None: - msg = 'Internet gateway {0} does not exist.'.format(i) - ret['comment'] = msg - ret['result'] = False + if r["id"] is None: + msg = "Internet gateway {0} does not exist.".format(i) + ret["comment"] = msg + ret["result"] = False return ret - _r['gateway_id'] = r['id'] - if i.get('vpc_peering_connection_name'): - r = __salt__['boto_vpc.get_resource_id']('vpc_peering_connection', name=i['vpc_peering_connection_name'], - region=region, key=key, keyid=keyid, profile=profile) - if 'error' in r: - msg = 'Error looking up id for VPC peering connection {0}: {1}'.format(i.get('vpc_peering_connection_name'), - r['error']['message']) - ret['comment'] = msg - ret['result'] = False + _r["gateway_id"] = r["id"] + if i.get("vpc_peering_connection_name"): + r = __salt__["boto_vpc.get_resource_id"]( + "vpc_peering_connection", + name=i["vpc_peering_connection_name"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + msg = "Error looking up id for VPC peering connection {0}: {1}".format( + i.get("vpc_peering_connection_name"), r["error"]["message"] + ) + ret["comment"] = msg + ret["result"] = False return ret - if r['id'] is None: - msg = 'VPC peering connection {0} does not exist.'.format(i) - ret['comment'] = msg - ret['result'] = False + if r["id"] is None: + msg = "VPC peering connection {0} does not exist.".format(i) + ret["comment"] = msg + ret["result"] = False return ret - _r['vpc_peering_connection_id'] = r['id'] - if i.get('instance_name'): - running_states = ('pending', 'rebooting', 'running', 'stopping', 'stopped') - r = __salt__['boto_ec2.get_id'](name=i['instance_name'], region=region, - key=key, keyid=keyid, profile=profile, - in_states=running_states) + _r["vpc_peering_connection_id"] = r["id"] + if i.get("instance_name"): + running_states = ( + "pending", + "rebooting", + "running", + "stopping", + "stopped", + ) + r = __salt__["boto_ec2.get_id"]( + name=i["instance_name"], + region=region, + key=key, + keyid=keyid, + profile=profile, + in_states=running_states, + ) if r is None: - msg = 'Instance {0} does not exist.'.format(i['instance_name']) - ret['comment'] = msg - ret['result'] = False + msg = "Instance {0} does not exist.".format(i["instance_name"]) + ret["comment"] = msg + ret["result"] = False return ret - _r['instance_id'] = r - if i.get('nat_gateway_subnet_name'): - r = __salt__['boto_vpc.describe_nat_gateways'](subnet_name=i['nat_gateway_subnet_name'], - region=region, key=key, keyid=keyid, profile=profile) + _r["instance_id"] = r + if i.get("nat_gateway_subnet_name"): + r = __salt__["boto_vpc.describe_nat_gateways"]( + subnet_name=i["nat_gateway_subnet_name"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not r: - msg = 'Nat gateway does not exist.' - ret['comment'] = msg - ret['result'] = False + msg = "Nat gateway does not exist." + ret["comment"] = msg + ret["result"] = False return ret - _r['nat_gateway_id'] = r[0]['NatGatewayId'] + _r["nat_gateway_id"] = r[0]["NatGatewayId"] _routes.append(_r) to_delete = [] to_create = [] for route in _routes: - if route not in route_table['routes']: + if route not in route_table["routes"]: to_create.append(dict(route)) - for route in route_table['routes']: + for route in route_table["routes"]: if route not in _routes: - if route.get('gateway_id') != 'local': + if route.get("gateway_id") != "local": to_delete.append(route) if to_create or to_delete: - if __opts__['test']: - msg = 'Route table {0} set to have routes modified.'.format(route_table_name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Route table {0} set to have routes modified.".format( + route_table_name + ) + ret["comment"] = msg + ret["result"] = None return ret if to_delete: for r in to_delete: - res = __salt__['boto_vpc.delete_route'](route_table_id=route_table['id'], - destination_cidr_block=r['destination_cidr_block'], - region=region, key=key, keyid=keyid, - profile=profile) - if not res['deleted']: - msg = 'Failed to delete route {0} from route table {1}: {2}.'.format(r['destination_cidr_block'], - route_table_name, res['error']['message']) - ret['comment'] = msg - ret['result'] = False + res = __salt__["boto_vpc.delete_route"]( + route_table_id=route_table["id"], + destination_cidr_block=r["destination_cidr_block"], + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if not res["deleted"]: + msg = "Failed to delete route {0} from route table {1}: {2}.".format( + r["destination_cidr_block"], + route_table_name, + res["error"]["message"], + ) + ret["comment"] = msg + ret["result"] = False return ret - ret['comment'] = 'Deleted route {0} from route table {1}.'.format(r['destination_cidr_block'], route_table_name) + ret["comment"] = "Deleted route {0} from route table {1}.".format( + r["destination_cidr_block"], route_table_name + ) if to_create: for r in to_create: - res = __salt__['boto_vpc.create_route'](route_table_id=route_table['id'], region=region, key=key, - keyid=keyid, profile=profile, **r) - if not res['created']: - msg = 'Failed to create route {0} in route table {1}: {2}.'.format(r['destination_cidr_block'], route_table_name, - res['error']['message']) - ret['comment'] = msg - ret['result'] = False + res = __salt__["boto_vpc.create_route"]( + route_table_id=route_table["id"], + region=region, + key=key, + keyid=keyid, + profile=profile, + **r + ) + if not res["created"]: + msg = "Failed to create route {0} in route table {1}: {2}.".format( + r["destination_cidr_block"], + route_table_name, + res["error"]["message"], + ) + ret["comment"] = msg + ret["result"] = False return ret - ret['comment'] = 'Created route {0} in route table {1}.'.format(r['destination_cidr_block'], route_table_name) - ret['changes']['old'] = {'routes': route_table['routes']} - route = __salt__['boto_vpc.describe_route_tables'](route_table_name=route_table_name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile) - ret['changes']['new'] = {'routes': route[0]['routes']} + ret["comment"] = "Created route {0} in route table {1}.".format( + r["destination_cidr_block"], route_table_name + ) + ret["changes"]["old"] = {"routes": route_table["routes"]} + route = __salt__["boto_vpc.describe_route_tables"]( + route_table_name=route_table_name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"]["new"] = {"routes": route[0]["routes"]} return ret -def _subnets_present(route_table_name, subnet_ids=None, subnet_names=None, tags=None, region=None, key=None, keyid=None, profile=None): - ret = {'name': route_table_name, - 'result': True, - 'comment': '', - 'changes': {} - } +def _subnets_present( + route_table_name, + subnet_ids=None, + subnet_names=None, + tags=None, + region=None, + key=None, + keyid=None, + profile=None, +): + ret = {"name": route_table_name, "result": True, "comment": "", "changes": {}} if not subnet_ids: subnet_ids = [] @@ -1163,78 +1416,106 @@ def _subnets_present(route_table_name, subnet_ids=None, subnet_names=None, tags= # Look up subnet ids if subnet_names: for i in subnet_names: - r = __salt__['boto_vpc.get_resource_id']('subnet', name=i, region=region, - key=key, keyid=keyid, profile=profile) + r = __salt__["boto_vpc.get_resource_id"]( + "subnet", name=i, region=region, key=key, keyid=keyid, profile=profile + ) - if 'error' in r: - msg = 'Error looking up subnet ids: {0}'.format(r['error']['message']) - ret['comment'] = msg - ret['result'] = False + if "error" in r: + msg = "Error looking up subnet ids: {0}".format(r["error"]["message"]) + ret["comment"] = msg + ret["result"] = False return ret - if r['id'] is None: - msg = 'Subnet {0} does not exist.'.format(i) - ret['comment'] = msg - ret['result'] = False + if r["id"] is None: + msg = "Subnet {0} does not exist.".format(i) + ret["comment"] = msg + ret["result"] = False return ret - subnet_ids.append(r['id']) + subnet_ids.append(r["id"]) # Describe routing table - route_table = __salt__['boto_vpc.describe_route_table'](route_table_name=route_table_name, tags=tags, region=region, - key=key, keyid=keyid, profile=profile) + route_table = __salt__["boto_vpc.describe_route_table"]( + route_table_name=route_table_name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not route_table: - msg = 'Could not retrieve configuration for route table {0}.'.format(route_table_name) - ret['comment'] = msg - ret['result'] = False + msg = "Could not retrieve configuration for route table {0}.".format( + route_table_name + ) + ret["comment"] = msg + ret["result"] = False return ret - assoc_ids = [x['subnet_id'] for x in route_table['associations']] + assoc_ids = [x["subnet_id"] for x in route_table["associations"]] to_create = [x for x in subnet_ids if x not in assoc_ids] to_delete = [] - for x in route_table['associations']: + for x in route_table["associations"]: # Don't remove the main route table association - if x['subnet_id'] not in subnet_ids and x['subnet_id'] is not None: - to_delete.append(x['id']) + if x["subnet_id"] not in subnet_ids and x["subnet_id"] is not None: + to_delete.append(x["id"]) if to_create or to_delete: - if __opts__['test']: - msg = 'Subnet associations for route table {0} set to be modified.'.format(route_table_name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Subnet associations for route table {0} set to be modified.".format( + route_table_name + ) + ret["comment"] = msg + ret["result"] = None return ret if to_delete: for r_asc in to_delete: - r = __salt__['boto_vpc.disassociate_route_table'](r_asc, region, key, keyid, profile) - if 'error' in r: - msg = 'Failed to dissociate {0} from route table {1}: {2}.'.format(r_asc, route_table_name, - r['error']['message']) - ret['comment'] = msg - ret['result'] = False + r = __salt__["boto_vpc.disassociate_route_table"]( + r_asc, region, key, keyid, profile + ) + if "error" in r: + msg = "Failed to dissociate {0} from route table {1}: {2}.".format( + r_asc, route_table_name, r["error"]["message"] + ) + ret["comment"] = msg + ret["result"] = False return ret - ret['comment'] = 'Dissociated subnet {0} from route table {1}.'.format(r_asc, route_table_name) + ret["comment"] = "Dissociated subnet {0} from route table {1}.".format( + r_asc, route_table_name + ) if to_create: for sn in to_create: - r = __salt__['boto_vpc.associate_route_table'](route_table_id=route_table['id'], - subnet_id=sn, - region=region, key=key, - keyid=keyid, profile=profile) - if 'error' in r: - msg = 'Failed to associate subnet {0} with route table {1}: {2}.'.format(sn, route_table_name, - r['error']['message']) - ret['comment'] = msg - ret['result'] = False + r = __salt__["boto_vpc.associate_route_table"]( + route_table_id=route_table["id"], + subnet_id=sn, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + if "error" in r: + msg = "Failed to associate subnet {0} with route table {1}: {2}.".format( + sn, route_table_name, r["error"]["message"] + ) + ret["comment"] = msg + ret["result"] = False return ret - ret['comment'] = 'Associated subnet {0} with route table {1}.'.format(sn, route_table_name) - ret['changes']['old'] = {'subnets_associations': route_table['associations']} - new_sub = __salt__['boto_vpc.describe_route_table'](route_table_name=route_table_name, tags=tags, region=region, key=key, - keyid=keyid, profile=profile) - ret['changes']['new'] = {'subnets_associations': new_sub['associations']} + ret["comment"] = "Associated subnet {0} with route table {1}.".format( + sn, route_table_name + ) + ret["changes"]["old"] = {"subnets_associations": route_table["associations"]} + new_sub = __salt__["boto_vpc.describe_route_table"]( + route_table_name=route_table_name, + tags=tags, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + ret["changes"]["new"] = {"subnets_associations": new_sub["associations"]} return ret -def route_table_absent(name, region=None, - key=None, keyid=None, profile=None): - ''' +def route_table_absent(name, region=None, key=None, keyid=None, profile=None): + """ Ensure the named route table is absent. name @@ -1252,50 +1533,55 @@ def route_table_absent(name, region=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.get_resource_id']('route_table', name=name, - region=region, key=key, - keyid=keyid, profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = r['error']['message'] + r = __salt__["boto_vpc.get_resource_id"]( + "route_table", name=name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = r["error"]["message"] return ret - rtbl_id = r['id'] + rtbl_id = r["id"] if not rtbl_id: - ret['comment'] = 'Route table {0} does not exist.'.format(name) + ret["comment"] = "Route table {0} does not exist.".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Route table {0} is set to be removed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Route table {0} is set to be removed.".format(name) + ret["result"] = None return ret - r = __salt__['boto_vpc.delete_route_table'](route_table_name=name, - region=region, - key=key, keyid=keyid, - profile=profile) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete route table: {0}'.format(r['error']['message']) + r = __salt__["boto_vpc.delete_route_table"]( + route_table_name=name, region=region, key=key, keyid=keyid, profile=profile + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete route table: {0}".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'route_table': rtbl_id} - ret['changes']['new'] = {'route_table': None} - ret['comment'] = 'Route table {0} deleted.'.format(name) + ret["changes"]["old"] = {"route_table": rtbl_id} + ret["changes"]["new"] = {"route_table": None} + ret["comment"] = "Route table {0} deleted.".format(name) return ret -def nat_gateway_present(name, subnet_name=None, subnet_id=None, - region=None, key=None, keyid=None, profile=None, allocation_id=None): - ''' +def nat_gateway_present( + name, + subnet_name=None, + subnet_id=None, + region=None, + key=None, + keyid=None, + profile=None, + allocation_id=None, +): + """ Ensure a nat gateway exists within the specified subnet This function requires boto3. @@ -1335,49 +1621,62 @@ def nat_gateway_present(name, subnet_name=None, subnet_id=None, profile A dict with region, key and keyid, or a pillar key (string) that contains a dict with region, key and keyid. - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.describe_nat_gateways'](subnet_name=subnet_name, - subnet_id=subnet_id, - region=region, key=key, keyid=keyid, - profile=profile) + r = __salt__["boto_vpc.describe_nat_gateways"]( + subnet_name=subnet_name, + subnet_id=subnet_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not r: - if __opts__['test']: - msg = 'Nat gateway is set to be created.' - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Nat gateway is set to be created." + ret["comment"] = msg + ret["result"] = None return ret - r = __salt__['boto_vpc.create_nat_gateway'](subnet_name=subnet_name, - subnet_id=subnet_id, - region=region, key=key, - keyid=keyid, profile=profile, - allocation_id=allocation_id) - if not r.get('created'): - ret['result'] = False - ret['comment'] = 'Failed to create nat gateway: {0}.'.format(r['error']['message']) + r = __salt__["boto_vpc.create_nat_gateway"]( + subnet_name=subnet_name, + subnet_id=subnet_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + allocation_id=allocation_id, + ) + if not r.get("created"): + ret["result"] = False + ret["comment"] = "Failed to create nat gateway: {0}.".format( + r["error"]["message"] + ) return ret - ret['changes']['old'] = {'nat_gateway': None} - ret['changes']['new'] = {'nat_gateway': r['id']} - ret['comment'] = 'Nat gateway created.' + ret["changes"]["old"] = {"nat_gateway": None} + ret["changes"]["new"] = {"nat_gateway": r["id"]} + ret["comment"] = "Nat gateway created." return ret inst = r[0] - _id = inst.get('NatGatewayId') - ret['comment'] = 'Nat gateway {0} present.'.format(_id) + _id = inst.get("NatGatewayId") + ret["comment"] = "Nat gateway {0} present.".format(_id) return ret -def nat_gateway_absent(name=None, subnet_name=None, subnet_id=None, - region=None, key=None, keyid=None, profile=None, - wait_for_delete_retries=0): - ''' +def nat_gateway_absent( + name=None, + subnet_name=None, + subnet_id=None, + region=None, + key=None, + keyid=None, + profile=None, + wait_for_delete_retries=0, +): + """ Ensure the nat gateway in the named subnet is absent. This function requires boto3. @@ -1414,50 +1713,64 @@ def nat_gateway_absent(name=None, subnet_name=None, subnet_id=None, the NAT gateway is in deleted or failed state before proceeding. Default is set to 0 for backward compatibility. - ''' + """ - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } + ret = {"name": name, "result": True, "comment": "", "changes": {}} - r = __salt__['boto_vpc.describe_nat_gateways'](subnet_name=subnet_name, - subnet_id=subnet_id, - region=region, key=key, keyid=keyid, - profile=profile) + r = __salt__["boto_vpc.describe_nat_gateways"]( + subnet_name=subnet_name, + subnet_id=subnet_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) if not r: - ret['comment'] = 'Nat gateway does not exist.' + ret["comment"] = "Nat gateway does not exist." return ret - if __opts__['test']: - ret['comment'] = 'Nat gateway is set to be removed.' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Nat gateway is set to be removed." + ret["result"] = None return ret for gw in r: - rtbl_id = gw.get('NatGatewayId') - r = __salt__['boto_vpc.delete_nat_gateway'](nat_gateway_id=rtbl_id, - release_eips=True, - region=region, - key=key, keyid=keyid, - profile=profile, - wait_for_delete=True, - wait_for_delete_retries=wait_for_delete_retries) - if 'error' in r: - ret['result'] = False - ret['comment'] = 'Failed to delete nat gateway: {0}'.format(r['error']['message']) + rtbl_id = gw.get("NatGatewayId") + r = __salt__["boto_vpc.delete_nat_gateway"]( + nat_gateway_id=rtbl_id, + release_eips=True, + region=region, + key=key, + keyid=keyid, + profile=profile, + wait_for_delete=True, + wait_for_delete_retries=wait_for_delete_retries, + ) + if "error" in r: + ret["result"] = False + ret["comment"] = "Failed to delete nat gateway: {0}".format( + r["error"]["message"] + ) return ret - ret['comment'] = ', '.join((ret['comment'], 'Nat gateway {0} deleted.'.format(rtbl_id))) - ret['changes']['old'] = {'nat_gateway': rtbl_id} - ret['changes']['new'] = {'nat_gateway': None} + ret["comment"] = ", ".join( + (ret["comment"], "Nat gateway {0} deleted.".format(rtbl_id)) + ) + ret["changes"]["old"] = {"nat_gateway": rtbl_id} + ret["changes"]["new"] = {"nat_gateway": None} return ret # pylint: disable=too-many-arguments -def accept_vpc_peering_connection(name=None, conn_id=None, conn_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def accept_vpc_peering_connection( + name=None, + conn_id=None, + conn_name=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Accept a VPC pending requested peering connection between two VPCs. name @@ -1496,50 +1809,72 @@ def accept_vpc_peering_connection(name=None, conn_id=None, conn_name=None, - conn_id: pbx-1873d472 - region: us-west-2 - ''' - log.debug('Called state to accept VPC peering connection') - pending = __salt__['boto_vpc.is_peering_connection_pending']( - conn_id=conn_id, conn_name=conn_name, region=region, key=key, - keyid=keyid, profile=profile) + """ + log.debug("Called state to accept VPC peering connection") + pending = __salt__["boto_vpc.is_peering_connection_pending"]( + conn_id=conn_id, + conn_name=conn_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) ret = { - 'name': name, - 'result': True, - 'changes': {}, - 'comment': 'Boto VPC peering state' + "name": name, + "result": True, + "changes": {}, + "comment": "Boto VPC peering state", } if not pending: - ret['result'] = True - ret['changes'].update({'old': - 'No pending VPC peering connection found. Nothing to be done.'}) + ret["result"] = True + ret["changes"].update( + {"old": "No pending VPC peering connection found. Nothing to be done."} + ) return ret - if __opts__['test']: - ret['changes'].update({'old': - 'Pending VPC peering connection found and can be accepted'}) + if __opts__["test"]: + ret["changes"].update( + {"old": "Pending VPC peering connection found and can be accepted"} + ) return ret - fun = 'boto_vpc.accept_vpc_peering_connection' - log.debug('Calling `%s()` to accept this VPC peering connection', fun) - result = __salt__[fun](conn_id=conn_id, name=conn_name, region=region, key=key, - keyid=keyid, profile=profile) + fun = "boto_vpc.accept_vpc_peering_connection" + log.debug("Calling `%s()` to accept this VPC peering connection", fun) + result = __salt__[fun]( + conn_id=conn_id, + name=conn_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) - if 'error' in result: - ret['comment'] = "Failed to accept VPC peering: {0}".format(result['error']) - ret['result'] = False + if "error" in result: + ret["comment"] = "Failed to accept VPC peering: {0}".format(result["error"]) + ret["result"] = False return ret - ret['changes'].update({'old': '', 'new': result['msg']}) + ret["changes"].update({"old": "", "new": result["msg"]}) return ret # pylint: disable=too-many-arguments -def request_vpc_peering_connection(name, requester_vpc_id=None, requester_vpc_name=None, - peer_vpc_id=None, peer_vpc_name=None, conn_name=None, - peer_owner_id=None, region=None, key=None, keyid=None, - profile=None): - ''' +def request_vpc_peering_connection( + name, + requester_vpc_id=None, + requester_vpc_name=None, + peer_vpc_id=None, + peer_vpc_name=None, + conn_name=None, + peer_owner_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ name Name of the state @@ -1586,38 +1921,33 @@ def request_vpc_peering_connection(name, requester_vpc_id=None, requester_vpc_na - peer_vpc_id: vpc-ae83f9ca - conn_name: salt_peering_connection - ''' - log.debug('Called state to request VPC peering connection') + """ + log.debug("Called state to request VPC peering connection") ret = { - 'name': name, - 'result': True, - 'changes': {}, - 'comment': 'Boto VPC peering state' + "name": name, + "result": True, + "changes": {}, + "comment": "Boto VPC peering state", } if conn_name: - vpc_ids = __salt__['boto_vpc.describe_vpc_peering_connection']( - conn_name, - region=region, - key=key, - keyid=keyid, - profile=profile - ).get('VPC-Peerings', []) + vpc_ids = __salt__["boto_vpc.describe_vpc_peering_connection"]( + conn_name, region=region, key=key, keyid=keyid, profile=profile + ).get("VPC-Peerings", []) else: vpc_ids = [] if vpc_ids: - ret['comment'] = ('VPC peering connection already exists, ' - 'nothing to be done.') + ret["comment"] = "VPC peering connection already exists, " "nothing to be done." return ret - if __opts__['test']: + if __opts__["test"]: if not vpc_ids: - ret['comment'] = 'VPC peering connection will be created' + ret["comment"] = "VPC peering connection will be created" return ret - log.debug('Called module to create VPC peering connection') + log.debug("Called module to create VPC peering connection") - result = __salt__['boto_vpc.request_vpc_peering_connection']( + result = __salt__["boto_vpc.request_vpc_peering_connection"]( requester_vpc_id, requester_vpc_name, peer_vpc_id, @@ -1627,26 +1957,32 @@ def request_vpc_peering_connection(name, requester_vpc_id=None, requester_vpc_na region=region, key=key, keyid=keyid, - profile=profile + profile=profile, ) - if 'error' in result: - ret['comment'] = "Failed to request VPC peering: {0}".format(result['error']) - ret['result'] = False + if "error" in result: + ret["comment"] = "Failed to request VPC peering: {0}".format(result["error"]) + ret["result"] = False return ret - ret['changes'].update({ - 'old': '', - 'new': result['msg'] - }) + ret["changes"].update({"old": "", "new": result["msg"]}) return ret -def vpc_peering_connection_present(name, requester_vpc_id=None, requester_vpc_name=None, - peer_vpc_id=None, peer_vpc_name=None, conn_name=None, - peer_owner_id=None, region=None, key=None, keyid=None, - profile=None): - ''' +def vpc_peering_connection_present( + name, + requester_vpc_id=None, + requester_vpc_name=None, + peer_vpc_id=None, + peer_vpc_name=None, + conn_name=None, + peer_owner_id=None, + region=None, + key=None, + keyid=None, + profile=None, +): + """ name Name of the state @@ -1699,43 +2035,64 @@ def vpc_peering_connection_present(name, requester_vpc_id=None, requester_vpc_na - peer_owner_id: 012345654321 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {} - } - if __salt__['boto_vpc.is_peering_connection_pending'](conn_name=conn_name, region=region, - key=key, keyid=keyid, profile=profile): - if __salt__['boto_vpc.peering_connection_pending_from_vpc'](conn_name=conn_name, - vpc_id=requester_vpc_id, - vpc_name=requester_vpc_name, - region=region, key=key, - keyid=keyid, profile=profile): - ret['comment'] = ('VPC peering {0} already requested - pending ' - 'acceptance by {1}'.format(conn_name, peer_owner_id - or peer_vpc_name or peer_vpc_id)) - log.info(ret['comment']) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + if __salt__["boto_vpc.is_peering_connection_pending"]( + conn_name=conn_name, region=region, key=key, keyid=keyid, profile=profile + ): + if __salt__["boto_vpc.peering_connection_pending_from_vpc"]( + conn_name=conn_name, + vpc_id=requester_vpc_id, + vpc_name=requester_vpc_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ): + ret["comment"] = ( + "VPC peering {0} already requested - pending " + "acceptance by {1}".format( + conn_name, peer_owner_id or peer_vpc_name or peer_vpc_id + ) + ) + log.info(ret["comment"]) return ret - return accept_vpc_peering_connection(name=name, conn_name=conn_name, - region=region, key=key, keyid=keyid, - profile=profile) - return request_vpc_peering_connection(name=name, requester_vpc_id=requester_vpc_id, - requester_vpc_name=requester_vpc_name, - peer_vpc_id=peer_vpc_id, peer_vpc_name=peer_vpc_name, - conn_name=conn_name, peer_owner_id=peer_owner_id, - region=region, key=key, keyid=keyid, profile=profile) + return accept_vpc_peering_connection( + name=name, + conn_name=conn_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) + return request_vpc_peering_connection( + name=name, + requester_vpc_id=requester_vpc_id, + requester_vpc_name=requester_vpc_name, + peer_vpc_id=peer_vpc_id, + peer_vpc_name=peer_vpc_name, + conn_name=conn_name, + peer_owner_id=peer_owner_id, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) -def vpc_peering_connection_absent(name, conn_id=None, conn_name=None, - region=None, key=None, keyid=None, profile=None): - return delete_vpc_peering_connection(name, conn_id, conn_name, region, key, keyid, profile) +def vpc_peering_connection_absent( + name, conn_id=None, conn_name=None, region=None, key=None, keyid=None, profile=None +): + return delete_vpc_peering_connection( + name, conn_id, conn_name, region, key, keyid, profile + ) # pylint: disable=too-many-arguments -def delete_vpc_peering_connection(name, conn_id=None, conn_name=None, - region=None, key=None, keyid=None, profile=None): - ''' +def delete_vpc_peering_connection( + name, conn_id=None, conn_name=None, region=None, key=None, keyid=None, profile=None +): + """ name Name of the state @@ -1779,42 +2136,45 @@ def delete_vpc_peering_connection(name, conn_id=None, conn_name=None, boto_vpc.delete_vpc_peering_connection: - conn_name: salt_vpc_peering - ''' - log.debug('Called state to delete VPC peering connection') + """ + log.debug("Called state to delete VPC peering connection") ret = { - 'name': name, - 'result': True, - 'changes': {}, - 'comment': 'Boto VPC peering state' + "name": name, + "result": True, + "changes": {}, + "comment": "Boto VPC peering state", } if conn_name: - vpc_ids = __salt__['boto_vpc.describe_vpc_peering_connection']( - conn_name, region=region, key=key, keyid=keyid, profile=profile).get('VPC-Peerings', []) + vpc_ids = __salt__["boto_vpc.describe_vpc_peering_connection"]( + conn_name, region=region, key=key, keyid=keyid, profile=profile + ).get("VPC-Peerings", []) else: vpc_ids = [conn_id] if not vpc_ids: - ret['comment'] = 'No VPC connection found, nothing to be done.' + ret["comment"] = "No VPC connection found, nothing to be done." return ret - if __opts__['test']: + if __opts__["test"]: if vpc_ids: - ret['comment'] = 'VPC peering connection would be deleted' + ret["comment"] = "VPC peering connection would be deleted" return ret - log.debug('Called module to delete VPC peering connection') + log.debug("Called module to delete VPC peering connection") - result = __salt__['boto_vpc.delete_vpc_peering_connection']( - conn_id=conn_id, conn_name=conn_name, region=region, key=key, - keyid=keyid, profile=profile) + result = __salt__["boto_vpc.delete_vpc_peering_connection"]( + conn_id=conn_id, + conn_name=conn_name, + region=region, + key=key, + keyid=keyid, + profile=profile, + ) - if 'error' in result: - ret['comment'] = "Failed to delete VPC peering: {0}".format(result['error']) - ret['result'] = False + if "error" in result: + ret["comment"] = "Failed to delete VPC peering: {0}".format(result["error"]) + ret["result"] = False return ret - ret['changes'].update({ - 'old': '', - 'new': result['msg'] - }) + ret["changes"].update({"old": "", "new": result["msg"]}) return ret diff --git a/salt/states/bower.py b/salt/states/bower.py index f9754e2b0c3..b1f6b4cc05d 100644 --- a/salt/states/bower.py +++ b/salt/states/bower.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of Bower Packages ============================== @@ -28,7 +28,7 @@ Example: - dir: /path/to/project - require: - npm: bower -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -40,18 +40,16 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load if the bower module is available in __salt__ - ''' - return 'bower' if 'bower.list' in __salt__ else False + """ + if "bower.list" in __salt__: + return "bower" + return (False, "bower module could not be loaded") -def installed(name, - dir, - pkgs=None, - user=None, - env=None): - ''' +def installed(name, dir, pkgs=None, user=None, env=None): + """ Verify that the given package is installed and is at the correct version (if specified). @@ -84,8 +82,8 @@ def installed(name, format is the same as the :py:func:`cmd.run <salt.states.cmd.run>`. state function. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} if pkgs is not None: pkg_list = pkgs @@ -93,19 +91,18 @@ def installed(name, pkg_list = [name] try: - installed_pkgs = __salt__['bower.list'](dir=dir, runas=user, env=env) + installed_pkgs = __salt__["bower.list"](dir=dir, runas=user, env=env) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error looking up \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error looking up '{0}': {1}".format(name, err) return ret else: - installed_pkgs = dict((p, info) for p, info in - six.iteritems(installed_pkgs)) + installed_pkgs = dict((p, info) for p, info in six.iteritems(installed_pkgs)) pkgs_satisfied = [] pkgs_to_install = [] for pkg in pkg_list: - pkg_name, _, pkg_ver = pkg.partition('#') + pkg_name, _, pkg_ver = pkg.partition("#") pkg_name = pkg_name.strip() if pkg_name not in installed_pkgs: @@ -114,10 +111,8 @@ def installed(name, if pkg_name in installed_pkgs: installed_pkg = installed_pkgs[pkg_name] - installed_pkg_ver = installed_pkg.get('pkgMeta').get('version') - installed_name_ver = '{0}#{1}'.format( - pkg_name, - installed_pkg_ver) + installed_pkg_ver = installed_pkg.get("pkgMeta").get("version") + installed_name_ver = "{0}#{1}".format(pkg_name, installed_pkg_ver) # If given an explicit version check the installed version matches. if pkg_ver: @@ -131,67 +126,73 @@ def installed(name, pkgs_satisfied.append(installed_name_ver) continue - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None comment_msg = [] if pkgs_to_install: comment_msg.append( - 'Bower package(s) \'{0}\' are set to be installed'.format( - ', '.join(pkgs_to_install))) + "Bower package(s) '{0}' are set to be installed".format( + ", ".join(pkgs_to_install) + ) + ) - ret['changes'] = {'old': [], 'new': pkgs_to_install} + ret["changes"] = {"old": [], "new": pkgs_to_install} if pkgs_satisfied: comment_msg.append( - 'Package(s) \'{0}\' satisfied by {1}'.format( - ', '.join(pkg_list), ', '.join(pkgs_satisfied))) + "Package(s) '{0}' satisfied by {1}".format( + ", ".join(pkg_list), ", ".join(pkgs_satisfied) + ) + ) - ret['comment'] = '. '.join(comment_msg) + ret["comment"] = ". ".join(comment_msg) return ret if not pkgs_to_install: - ret['result'] = True - ret['comment'] = ('Package(s) \'{0}\' satisfied by {1}'.format( - ', '.join(pkg_list), ', '.join(pkgs_satisfied))) + ret["result"] = True + ret["comment"] = "Package(s) '{0}' satisfied by {1}".format( + ", ".join(pkg_list), ", ".join(pkgs_satisfied) + ) return ret try: cmd_args = { - 'pkg': None, - 'dir': dir, - 'pkgs': None, - 'runas': user, - 'env': env, + "pkg": None, + "dir": dir, + "pkgs": None, + "runas": user, + "env": env, } if pkgs is not None: - cmd_args['pkgs'] = pkgs + cmd_args["pkgs"] = pkgs else: - cmd_args['pkg'] = pkg_name + cmd_args["pkg"] = pkg_name - call = __salt__['bower.install'](**cmd_args) + call = __salt__["bower.install"](**cmd_args) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error installing \'{0}\': {1}'.format( - ', '.join(pkg_list), err) + ret["result"] = False + ret["comment"] = "Error installing '{0}': {1}".format(", ".join(pkg_list), err) return ret if call: - ret['result'] = True - ret['changes'] = {'old': [], 'new': pkgs_to_install} - ret['comment'] = 'Package(s) \'{0}\' successfully installed'.format( - ', '.join(pkgs_to_install)) + ret["result"] = True + ret["changes"] = {"old": [], "new": pkgs_to_install} + ret["comment"] = "Package(s) '{0}' successfully installed".format( + ", ".join(pkgs_to_install) + ) else: - ret['result'] = False - ret['comment'] = 'Could not install package(s) \'{0}\''.format( - ', '.join(pkg_list)) + ret["result"] = False + ret["comment"] = "Could not install package(s) '{0}'".format( + ", ".join(pkg_list) + ) return ret def removed(name, dir, user=None): - ''' + """ Verify that the given package is not installed. dir @@ -200,44 +201,43 @@ def removed(name, dir, user=None): user The user to run Bower with - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} try: - installed_pkgs = __salt__['bower.list'](dir=dir, runas=user) + installed_pkgs = __salt__["bower.list"](dir=dir, runas=user) except (CommandExecutionError, CommandNotFoundError) as err: - ret['result'] = False - ret['comment'] = 'Error removing \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error removing '{0}': {1}".format(name, err) return ret if name not in installed_pkgs: - ret['result'] = True - ret['comment'] = 'Package \'{0}\' is not installed'.format(name) + ret["result"] = True + ret["comment"] = "Package '{0}' is not installed".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Package \'{0}\' is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Package '{0}' is set to be removed".format(name) return ret try: - if __salt__['bower.uninstall'](pkg=name, dir=dir, runas=user): - ret['result'] = True - ret['changes'] = {name: 'Removed'} - ret['comment'] = 'Package \'{0}\' was successfully removed'.format( - name) + if __salt__["bower.uninstall"](pkg=name, dir=dir, runas=user): + ret["result"] = True + ret["changes"] = {name: "Removed"} + ret["comment"] = "Package '{0}' was successfully removed".format(name) else: - ret['result'] = False - ret['comment'] = 'Error removing \'{0}\''.format(name) + ret["result"] = False + ret["comment"] = "Error removing '{0}'".format(name) except (CommandExecutionError, CommandNotFoundError) as err: - ret['result'] = False - ret['comment'] = 'Error removing \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error removing '{0}': {1}".format(name, err) return ret def bootstrap(name, user=None): - ''' + """ Bootstraps a frontend distribution. Will execute 'bower install' on the specified directory. @@ -245,36 +245,35 @@ def bootstrap(name, user=None): user The user to run Bower with - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Directory \'{0}\' is set to be bootstrapped'.format( - name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Directory '{0}' is set to be bootstrapped".format(name) return ret try: - call = __salt__['bower.install'](pkg=None, dir=name, runas=user) + call = __salt__["bower.install"](pkg=None, dir=name, runas=user) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error bootstrapping \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error bootstrapping '{0}': {1}".format(name, err) return ret if not call: - ret['result'] = True - ret['comment'] = 'Directory is already bootstrapped' + ret["result"] = True + ret["comment"] = "Directory is already bootstrapped" return ret - ret['result'] = True - ret['changes'] = {name: 'Bootstrapped'} - ret['comment'] = 'Directory was successfully bootstrapped' + ret["result"] = True + ret["changes"] = {name: "Bootstrapped"} + ret["comment"] = "Directory was successfully bootstrapped" return ret def pruned(name, user=None, env=None): - ''' + """ .. versionadded:: 2017.7.0 Cleans up local bower_components directory. @@ -284,27 +283,26 @@ def pruned(name, user=None, env=None): user The user to run Bower with - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Directory \'{0}\' is set to be pruned'.format( - name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Directory '{0}' is set to be pruned".format(name) return ret try: - call = __salt__['bower.prune'](dir=name, runas=user, env=env) + call = __salt__["bower.prune"](dir=name, runas=user, env=env) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error pruning \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error pruning '{0}': {1}".format(name, err) return ret - ret['result'] = True + ret["result"] = True if call: - ret['comment'] = 'Directory \'{0}\' was successfully pruned'.format(name) - ret['changes'] = {'old': [], 'new': call} + ret["comment"] = "Directory '{0}' was successfully pruned".format(name) + ret["changes"] = {"old": [], "new": call} else: - ret['comment'] = 'No packages were pruned from directory \'{0}\''.format(name) + ret["comment"] = "No packages were pruned from directory '{0}'".format(name) return ret diff --git a/salt/states/cabal.py b/salt/states/cabal.py index 63a81ed4890..a694ff0978a 100644 --- a/salt/states/cabal.py +++ b/salt/states/cabal.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of Cabal Packages ============================== @@ -21,24 +21,27 @@ pkg.installed state for the package which provides cabal - require: - pkg: cabal-install -''' +""" from __future__ import absolute_import, print_function, unicode_literals -from salt.exceptions import CommandExecutionError, CommandNotFoundError import salt.utils.path +from salt.exceptions import CommandExecutionError, CommandNotFoundError def __virtual__(): - ''' + """ Only work when cabal-install is installed. - ''' - return (salt.utils.path.which('cabal') is not None) and \ - (salt.utils.path.which('ghc-pkg') is not None) + """ + if (salt.utils.path.which("cabal") is not None) and ( + salt.utils.path.which("ghc-pkg") is not None + ): + return True + return (False, "cabal or ghc-pkg commands not found") def _parse_pkg_string(pkg): - ''' + """ Parse pkg string and return a tuple of package name, separator, and package version. @@ -50,17 +53,13 @@ def _parse_pkg_string(pkg): For the sake of simplicity only the first form is supported, support for other forms can be added later. - ''' - pkg_name, separator, pkg_ver = pkg.partition('-') + """ + pkg_name, separator, pkg_ver = pkg.partition("-") return (pkg_name.strip(), separator, pkg_ver.strip()) -def installed(name, - pkgs=None, - user=None, - install_global=False, - env=None): - ''' +def installed(name, pkgs=None, user=None, install_global=False, env=None): + """ Verify that the given package is installed and is at the correct version (if specified). @@ -80,14 +79,14 @@ def installed(name, A list of environment variables to be set prior to execution. The format is the same as the :py:func:`cmd.run <salt.states.cmd.run>`. state function. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} try: - call = __salt__['cabal.update'](user=user, env=env) + call = __salt__["cabal.update"](user=user, env=env) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Could not run cabal update {0}'.format(err) + ret["result"] = False + ret["comment"] = "Could not run cabal update {0}".format(err) return ret if pkgs is not None: @@ -96,11 +95,10 @@ def installed(name, pkg_list = [name] try: - installed_pkgs = __salt__['cabal.list']( - user=user, installed=True, env=env) + installed_pkgs = __salt__["cabal.list"](user=user, installed=True, env=env) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error looking up \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error looking up '{0}': {1}".format(name, err) return ret pkgs_satisfied = [] @@ -120,87 +118,89 @@ def installed(name, else: pkgs_satisfied.append(pkg) - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None comment_msg = [] if pkgs_to_install: comment_msg.append( - 'Packages(s) \'{0}\' are set to be installed'.format( - ', '.join(pkgs_to_install))) + "Packages(s) '{0}' are set to be installed".format( + ", ".join(pkgs_to_install) + ) + ) if pkgs_satisfied: comment_msg.append( - 'Packages(s) \'{0}\' satisfied by {1}'.format( - ', '.join(pkg_list), ', '.join(pkgs_satisfied))) + "Packages(s) '{0}' satisfied by {1}".format( + ", ".join(pkg_list), ", ".join(pkgs_satisfied) + ) + ) - ret['comment'] = '. '.join(comment_msg) + ret["comment"] = ". ".join(comment_msg) return ret if not pkgs_to_install: - ret['result'] = True - ret['comment'] = ('Packages(s) \'{0}\' satisfied by {1}'.format( - ', '.join(pkg_list), ', '.join(pkgs_satisfied))) + ret["result"] = True + ret["comment"] = "Packages(s) '{0}' satisfied by {1}".format( + ", ".join(pkg_list), ", ".join(pkgs_satisfied) + ) return ret try: - call = __salt__['cabal.install'](pkgs=pkg_list, - user=user, - install_global=install_global, - env=env) + call = __salt__["cabal.install"]( + pkgs=pkg_list, user=user, install_global=install_global, env=env + ) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error installing \'{0}\': {1}'.format( - ', '.join(pkg_list), err) + ret["result"] = False + ret["comment"] = "Error installing '{0}': {1}".format(", ".join(pkg_list), err) return ret if call and isinstance(call, dict): - ret['result'] = True - ret['changes'] = {'old': [], 'new': pkgs_to_install} - ret['comment'] = 'Packages(s) \'{0}\' successfully installed'.format( - ', '.join(pkgs_to_install)) + ret["result"] = True + ret["changes"] = {"old": [], "new": pkgs_to_install} + ret["comment"] = "Packages(s) '{0}' successfully installed".format( + ", ".join(pkgs_to_install) + ) else: - ret['result'] = False - ret['comment'] = 'Could not install packages(s) \'{0}\''.format( - ', '.join(pkg_list)) + ret["result"] = False + ret["comment"] = "Could not install packages(s) '{0}'".format( + ", ".join(pkg_list) + ) return ret -def removed(name, - user=None, - env=None): - ''' +def removed(name, user=None, env=None): + """ Verify that given package is not installed. - ''' + """ - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} try: - installed_pkgs = __salt__['cabal.list']( - user=user, installed=True, env=env) + installed_pkgs = __salt__["cabal.list"](user=user, installed=True, env=env) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error looking up \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error looking up '{0}': {1}".format(name, err) if name not in installed_pkgs: - ret['result'] = True - ret['comment'] = 'Package \'{0}\' is not installed'.format(name) + ret["result"] = True + ret["comment"] = "Package '{0}' is not installed".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Package \'{0}\' is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Package '{0}' is set to be removed".format(name) return ret - if __salt__['cabal.uninstall'](pkg=name, user=user, env=env): - ret['result'] = True - ret['changes'][name] = 'Removed' - ret['comment'] = 'Package \'{0}\' was successfully removed'.format(name) + if __salt__["cabal.uninstall"](pkg=name, user=user, env=env): + ret["result"] = True + ret["changes"][name] = "Removed" + ret["comment"] = "Package '{0}' was successfully removed".format(name) else: - ret['result'] = False - ret['comment'] = 'Error removing package \'{0}\''.format(name) + ret["result"] = False + ret["comment"] = "Error removing package '{0}'".format(name) return ret diff --git a/salt/states/ceph.py b/salt/states/ceph.py index 217a981af9f..a895d50fa0f 100644 --- a/salt/states/ceph.py +++ b/salt/states/ceph.py @@ -1,58 +1,58 @@ # -*- coding: utf-8 -*- -''' +""" Manage ceph with salt. .. versionadded:: 2016.11.0 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs import salt.utils.json from salt.exceptions import CommandExecutionError, CommandNotFoundError - log = logging.getLogger(__name__) def _unchanged(name, msg): - ''' + """ Utility function: Return structure unchanged - ''' - return {'name': name, 'result': True, 'comment': msg, 'changes': {}} + """ + return {"name": name, "result": True, "comment": msg, "changes": {}} def _test(name, msg): - ''' + """ Utility function: Return structure test - ''' - return {'name': name, 'result': None, 'comment': msg, 'changes': {}} + """ + return {"name": name, "result": None, "comment": msg, "changes": {}} def _error(name, msg): - ''' + """ Utility function: Return structure error - ''' - return {'name': name, 'result': False, 'comment': msg, 'changes': {}} + """ + return {"name": name, "result": False, "comment": msg, "changes": {}} def _changed(name, msg, **changes): - ''' + """ Utility function: Return structure changed - ''' - return {'name': name, 'result': True, 'comment': msg, 'changes': changes} + """ + return {"name": name, "result": True, "comment": msg, "changes": changes} def _ordereddict2dict(input_ordered_dict): - ''' + """ Convert ordered dictionary to a dictionary - ''' + """ return salt.utils.json.loads(salt.utils.json.dumps(input_ordered_dict)) def quorum(name, **kwargs): - ''' + """ Quorum state This state checks the mon daemons are in quorum. It does not alter the @@ -67,15 +67,15 @@ def quorum(name, **kwargs): sesceph.quorum: - require: - sesceph: mon_running - ''' + """ parameters = _ordereddict2dict(kwargs) if parameters is None: return _error(name, "Invalid parameters:%s") - if __opts__['test']: + if __opts__["test"]: return _test(name, "cluster quorum") try: - cluster_quorum = __salt__['ceph.cluster_quorum'](**parameters) + cluster_quorum = __salt__["ceph.cluster_quorum"](**parameters) except (CommandExecutionError, CommandNotFoundError) as err: return _error(name, err.strerror) if cluster_quorum: diff --git a/salt/states/chef.py b/salt/states/chef.py index b8f3c10a6b7..100db45eb76 100644 --- a/salt/states/chef.py +++ b/salt/states/chef.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Execute Chef client runs ===================================================================== @@ -18,7 +18,7 @@ Run chef-client or chef-solo my-solo-run: chef.solo: - environment: dev -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -26,14 +26,16 @@ import re def __virtual__(): - ''' + """ Only load if Chef execution module is available. - ''' - return True if 'chef.client' in __salt__ else False + """ + if "chef.client" in __salt__: + return True + return (False, "chef module could not be loaded") def client(name, **kwargs): - ''' + """ name Unique identifier for the state. Does not affect the Chef run. @@ -89,12 +91,12 @@ def client(name, **kwargs): validation_key Set the validation key file location, used for registering new clients - ''' - return _run(name, 'chef.client', kwargs) + """ + return _run(name, "chef.client", kwargs) def solo(name, **kwargs): - ''' + """ name Unique identifier for the state. Does not affect the Chef run. @@ -133,29 +135,26 @@ def solo(name, **kwargs): user User to set privilege to - ''' - return _run(name, 'chef.solo', kwargs) + """ + return _run(name, "chef.solo", kwargs) def _run(name, mod, kwargs): - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - result = __salt__[mod](whyrun=__opts__['test'], **kwargs) - if result['retcode'] == 0: + result = __salt__[mod](whyrun=__opts__["test"], **kwargs) + if result["retcode"] == 0: - if _has_changes(result['stdout']): + if _has_changes(result["stdout"]): # Populate the 'changes' dict if anything changed - ret['changes']['summary'] = _summary(result['stdout']) - ret['result'] = True if not __opts__['test'] else None + ret["changes"]["summary"] = _summary(result["stdout"]) + ret["result"] = True if not __opts__["test"] else None else: - ret['result'] = True + ret["result"] = True else: - ret['result'] = False + ret["result"] = False - ret['comment'] = '\n'.join([result['stdout'], result['stderr']]) + ret["comment"] = "\n".join([result["stdout"], result["stderr"]]) return ret @@ -164,9 +163,5 @@ def _summary(stdout): def _has_changes(stdout): - regex = re.search( - r'Chef Client finished, (\d+)', - _summary(stdout), - re.IGNORECASE - ) + regex = re.search(r"Chef Client finished, (\d+)", _summary(stdout), re.IGNORECASE) return int(regex.group(1)) > 0 diff --git a/salt/states/chocolatey.py b/salt/states/chocolatey.py index 3ca84e82fe5..aeeb1ccc53d 100644 --- a/salt/states/chocolatey.py +++ b/salt/states/chocolatey.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Manage Chocolatey package installs .. versionadded:: 2016.3.0 @@ -9,7 +9,7 @@ Manage Chocolatey package installs current versions, find available versions, etc. This is normally a slow operation and may be optimized by specifying a local, smaller chocolatey repo. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -21,16 +21,27 @@ from salt.exceptions import SaltInvocationError def __virtual__(): - ''' + """ Load only if chocolatey is loaded - ''' - return 'chocolatey' if 'chocolatey.install' in __salt__ else False + """ + if "chocolatey.install" in __salt__: + return "chocolatey" + return (False, "chocolatey module could not be loaded") -def installed(name, version=None, source=None, force=False, pre_versions=False, - install_args=None, override_args=False, force_x86=False, - package_args=None, allow_multiple=False): - ''' +def installed( + name, + version=None, + source=None, + force=False, + pre_versions=False, + install_args=None, + override_args=False, + force_x86=False, + package_args=None, + allow_multiple=False, +): + """ Installs a package if not already installed Args: @@ -84,118 +95,123 @@ def installed(name, version=None, source=None, force=False, pre_versions=False, - version: '12.04' - source: 'mychocolatey/source' - force: True - ''' + """ if force and allow_multiple: raise SaltInvocationError( - 'Cannot use \'force\' in conjunction with \'allow_multiple\'') + "Cannot use 'force' in conjunction with 'allow_multiple'" + ) - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} # Get list of currently installed packages - pre_install = __salt__['chocolatey.list'](local_only=True) + pre_install = __salt__["chocolatey.list"](local_only=True) # Determine action # Package not installed if name.lower() not in [package.lower() for package in pre_install.keys()]: if version: - ret['changes'] = {name: 'Version {0} will be installed'.format(version)} + ret["changes"] = {name: "Version {0} will be installed".format(version)} else: - ret['changes'] = {name: 'Latest version will be installed'} + ret["changes"] = {name: "Latest version will be installed"} # Package installed else: - version_info = __salt__['chocolatey.version'](name=name, - check_remote=True, - source=source) + version_info = __salt__["chocolatey.version"]( + name=name, check_remote=True, source=source + ) full_name = name for pkg in version_info: if name.lower() == pkg.lower(): full_name = pkg - installed_version = version_info[full_name]['installed'][0] + installed_version = version_info[full_name]["installed"][0] if version: if salt.utils.versions.compare( - ver1=installed_version, oper="==", ver2=version): + ver1=installed_version, oper="==", ver2=version + ): if force: - ret['changes'] = { - name: 'Version {0} will be reinstalled'.format(version)} - ret['comment'] = 'Reinstall {0} {1}'.format(full_name, version) + ret["changes"] = { + name: "Version {0} will be reinstalled".format(version) + } + ret["comment"] = "Reinstall {0} {1}".format(full_name, version) else: - ret['comment'] = '{0} {1} is already installed'.format(name, version) - if __opts__['test']: - ret['result'] = None + ret["comment"] = "{0} {1} is already installed".format( + name, version + ) + if __opts__["test"]: + ret["result"] = None return ret else: if allow_multiple: - ret['changes'] = { - name: 'Version {0} will be installed side by side with ' - 'Version {1} if supported'.format(version, installed_version) + ret["changes"] = { + name: "Version {0} will be installed side by side with " + "Version {1} if supported".format(version, installed_version) } - ret['comment'] = ( - 'Install {0} {1} side-by-side with {0} {2}'.format( - full_name, version, installed_version - ) + ret["comment"] = "Install {0} {1} side-by-side with {0} {2}".format( + full_name, version, installed_version ) else: - ret['changes'] = { - name: 'Version {0} will be installed over Version {1}'.format(version, installed_version) + ret["changes"] = { + name: "Version {0} will be installed over Version {1}".format( + version, installed_version + ) } - ret['comment'] = 'Install {0} {1} over {0} {2}'.format( + ret["comment"] = "Install {0} {1} over {0} {2}".format( full_name, version, installed_version ) force = True else: version = installed_version if force: - ret['changes'] = { - name: 'Version {0} will be reinstalled'.format(version)} - ret['comment'] = 'Reinstall {0} {1}'.format(full_name, version) + ret["changes"] = { + name: "Version {0} will be reinstalled".format(version) + } + ret["comment"] = "Reinstall {0} {1}".format(full_name, version) else: - ret['comment'] = '{0} {1} is already installed'.format(name, version) - if __opts__['test']: - ret['result'] = None + ret["comment"] = "{0} {1} is already installed".format(name, version) + if __opts__["test"]: + ret["result"] = None return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The installation was tested' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The installation was tested" return ret # Install the package - result = __salt__['chocolatey.install'](name=name, - version=version, - source=source, - force=force, - pre_versions=pre_versions, - install_args=install_args, - override_args=override_args, - force_x86=force_x86, - package_args=package_args, - allow_multiple=allow_multiple) + result = __salt__["chocolatey.install"]( + name=name, + version=version, + source=source, + force=force, + pre_versions=pre_versions, + install_args=install_args, + override_args=override_args, + force_x86=force_x86, + package_args=package_args, + allow_multiple=allow_multiple, + ) - if 'Running chocolatey failed' not in result: - ret['result'] = True + if "Running chocolatey failed" not in result: + ret["result"] = True else: - ret['result'] = False + ret["result"] = False - if not ret['result']: - ret['comment'] = 'Failed to install the package {0}'.format(name) + if not ret["result"]: + ret["comment"] = "Failed to install the package {0}".format(name) # Get list of installed packages after 'chocolatey.install' - post_install = __salt__['chocolatey.list'](local_only=True) + post_install = __salt__["chocolatey.list"](local_only=True) - ret['changes'] = salt.utils.data.compare_dicts(pre_install, post_install) + ret["changes"] = salt.utils.data.compare_dicts(pre_install, post_install) return ret def uninstalled(name, version=None, uninstall_args=None, override_args=False): - ''' + """ Uninstalls a package name @@ -222,67 +238,65 @@ def uninstalled(name, version=None, uninstall_args=None, override_args=False): - name: mypackage - version: '21.5' - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} # Get list of currently installed packages - pre_uninstall = __salt__['chocolatey.list'](local_only=True) + pre_uninstall = __salt__["chocolatey.list"](local_only=True) # Determine if package is installed if name.lower() in [package.lower() for package in pre_uninstall.keys()]: try: - ret['changes'] = { - name: '{0} version {1} will be removed'.format( + ret["changes"] = { + name: "{0} version {1} will be removed".format( name, pre_uninstall[name][0] ) } except KeyError: - ret['changes'] = {name: '{0} will be removed'.format(name)} + ret["changes"] = {name: "{0} will be removed".format(name)} else: - ret['comment'] = 'The package {0} is not installed'.format(name) + ret["comment"] = "The package {0} is not installed".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The uninstall was tested' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The uninstall was tested" return ret # Uninstall the package - result = __salt__['chocolatey.uninstall'](name, - version, - uninstall_args, - override_args) + result = __salt__["chocolatey.uninstall"]( + name, version, uninstall_args, override_args + ) - if 'Running chocolatey failed' not in result: - ret['result'] = True + if "Running chocolatey failed" not in result: + ret["result"] = True else: - ret['result'] = False + ret["result"] = False - if not ret['result']: - ret['comment'] = 'Failed to uninstall the package {0}'.format(name) + if not ret["result"]: + ret["comment"] = "Failed to uninstall the package {0}".format(name) # Get list of installed packages after 'chocolatey.uninstall' - post_uninstall = __salt__['chocolatey.list'](local_only=True) + post_uninstall = __salt__["chocolatey.list"](local_only=True) - ret['changes'] = salt.utils.data.compare_dicts(pre_uninstall, post_uninstall) + ret["changes"] = salt.utils.data.compare_dicts(pre_uninstall, post_uninstall) return ret -def upgraded(name, - version=None, - source=None, - force=False, - pre_versions=False, - install_args=None, - override_args=False, - force_x86=False, - package_args=None): - ''' +def upgraded( + name, + version=None, + source=None, + force=False, + pre_versions=False, + install_args=None, + override_args=False, + force_x86=False, + package_args=None, +): + """ Upgrades a package. Will install the package if not installed. .. versionadded:: 2018.3.0 @@ -332,28 +346,25 @@ def upgraded(name, - name: packagename - version: '12.04' - source: 'mychocolatey/source' - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} # Get list of currently installed packages - pre_install = __salt__['chocolatey.list'](local_only=True) + pre_install = __salt__["chocolatey.list"](local_only=True) # Determine if there are changes # Package not installed if name.lower() not in [package.lower() for package in pre_install.keys()]: if version: - ret['changes'][name] = 'Version {0} will be installed'.format(version) - ret['comment'] = 'Install version {0}'.format(version) + ret["changes"][name] = "Version {0} will be installed".format(version) + ret["comment"] = "Install version {0}".format(version) else: - ret['changes'][name] = 'Latest version will be installed' - ret['comment'] = 'Install latest version' + ret["changes"][name] = "Latest version will be installed" + ret["comment"] = "Install latest version" # Package installed else: - version_info = __salt__['chocolatey.version'](name, check_remote=True) + version_info = __salt__["chocolatey.version"](name, check_remote=True) # Get the actual full name out of version_info full_name = name @@ -361,81 +372,85 @@ def upgraded(name, if name.lower() == pkg.lower(): full_name = pkg - installed_version = version_info[full_name]['installed'][0] + installed_version = version_info[full_name]["installed"][0] # If version is not passed, use available... if available is available if not version: - if 'available' in version_info[full_name]: - version = version_info[full_name]['available'][0] + if "available" in version_info[full_name]: + version = version_info[full_name]["available"][0] if version: # If installed version and new version are the same if salt.utils.versions.compare( - ver1=installed_version, - oper="==", - ver2=version): + ver1=installed_version, oper="==", ver2=version + ): if force: - ret['changes'][name] = 'Version {0} will be reinstalled'.format(version) - ret['comment'] = 'Reinstall {0} {1}'.format(full_name, version) + ret["changes"][name] = "Version {0} will be reinstalled".format( + version + ) + ret["comment"] = "Reinstall {0} {1}".format(full_name, version) else: - ret['comment'] = '{0} {1} is already installed'.format( + ret["comment"] = "{0} {1} is already installed".format( name, installed_version ) else: # If installed version is older than new version if salt.utils.versions.compare( - ver1=installed_version, oper="<", ver2=version): - ret['changes'][name] = 'Version {0} will be upgraded to Version {1}'.format( + ver1=installed_version, oper="<", ver2=version + ): + ret["changes"][ + name + ] = "Version {0} will be upgraded to Version {1}".format( installed_version, version ) - ret['comment'] = 'Upgrade {0} {1} to {2}'.format( + ret["comment"] = "Upgrade {0} {1} to {2}".format( full_name, installed_version, version ) # If installed version is newer than new version else: - ret['comment'] = ( - '{0} {1} (newer) is already installed'.format( - name, installed_version - ) + ret["comment"] = "{0} {1} (newer) is already installed".format( + name, installed_version ) # Catch all for a condition where version is not passed and there is no # available version else: - ret['comment'] = 'No version found to install' + ret["comment"] = "No version found to install" # Return if there are no changes to be made - if not ret['changes']: + if not ret["changes"]: return ret # Return if running in test mode - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # Install the package - result = __salt__['chocolatey.upgrade'](name=name, - version=version, - source=source, - force=force, - pre_versions=pre_versions, - install_args=install_args, - override_args=override_args, - force_x86=force_x86, - package_args=package_args) + result = __salt__["chocolatey.upgrade"]( + name=name, + version=version, + source=source, + force=force, + pre_versions=pre_versions, + install_args=install_args, + override_args=override_args, + force_x86=force_x86, + package_args=package_args, + ) - if 'Running chocolatey failed' not in result: - ret['comment'] = 'Package {0} upgraded successfully'.format(name) - ret['result'] = True + if "Running chocolatey failed" not in result: + ret["comment"] = "Package {0} upgraded successfully".format(name) + ret["result"] = True else: - ret['comment'] = 'Failed to upgrade the package {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to upgrade the package {0}".format(name) + ret["result"] = False # Get list of installed packages after 'chocolatey.install' - post_install = __salt__['chocolatey.list'](local_only=True) + post_install = __salt__["chocolatey.list"](local_only=True) # Prior to this, ret['changes'] would have contained expected changes, # replace them with the actual changes now that we have completed the # installation. - ret['changes'] = salt.utils.data.compare_dicts(pre_install, post_install) + ret["changes"] = salt.utils.data.compare_dicts(pre_install, post_install) return ret diff --git a/salt/states/chronos_job.py b/salt/states/chronos_job.py index 92243cd411e..7e6c2eeb375 100644 --- a/salt/states/chronos_job.py +++ b/salt/states/chronos_job.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configure Chronos jobs via a salt proxy. .. code-block:: yaml @@ -12,132 +12,133 @@ Configure Chronos jobs via a salt proxy. owner: "me@example.com" .. versionadded:: 2015.8.2 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import copy import logging -from salt.ext import six import salt.utils.configcomparer +from salt.ext import six -__proxyenabled__ = ['chronos'] +__proxyenabled__ = ["chronos"] log = logging.getLogger(__file__) def config(name, config): - ''' + """ Ensure that the chronos job with the given name is present and is configured to match the given config values. :param name: The job name :param config: The configuration to apply (dict) :return: A standard Salt changes dictionary - ''' + """ # setup return structure ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': '', + "name": name, + "changes": {}, + "result": False, + "comment": "", } # get existing config if job is present existing_config = None - if __salt__['chronos.has_job'](name): - existing_config = __salt__['chronos.job'](name)['job'] + if __salt__["chronos.has_job"](name): + existing_config = __salt__["chronos.job"](name)["job"] # compare existing config with defined config if existing_config: update_config = copy.deepcopy(existing_config) salt.utils.configcomparer.compare_and_update_config( - config, - update_config, - ret['changes'], + config, update_config, ret["changes"], ) else: # the job is not configured--we need to create it from scratch - ret['changes']['job'] = { - 'new': config, - 'old': None, + ret["changes"]["job"] = { + "new": config, + "old": None, } update_config = config - if ret['changes']: + if ret["changes"]: # if the only change is in schedule, check to see if patterns are equivalent - if 'schedule' in ret['changes'] and len(ret['changes']) == 1: - if 'new' in ret['changes']['schedule'] and 'old' in ret['changes']['schedule']: - new = ret['changes']['schedule']['new'] - log.debug('new schedule: %s', new) - old = ret['changes']['schedule']['old'] - log.debug('old schedule: %s', old) + if "schedule" in ret["changes"] and len(ret["changes"]) == 1: + if ( + "new" in ret["changes"]["schedule"] + and "old" in ret["changes"]["schedule"] + ): + new = ret["changes"]["schedule"]["new"] + log.debug("new schedule: %s", new) + old = ret["changes"]["schedule"]["old"] + log.debug("old schedule: %s", old) if new and old: - _new = new.split('/') - log.debug('_new schedule: %s', _new) - _old = old.split('/') - log.debug('_old schedule: %s', _old) + _new = new.split("/") + log.debug("_new schedule: %s", _new) + _old = old.split("/") + log.debug("_old schedule: %s", _old) if len(_new) == 3 and len(_old) == 3: - log.debug('_new[0] == _old[0]: %s', - six.text_type(_new[0]) == six.text_type(_old[0])) - log.debug('_new[2] == _old[2]: %s', - six.text_type(_new[2]) == six.text_type(_old[2])) - if six.text_type(_new[0]) == six.text_type(_old[0]) and \ - six.text_type(_new[2]) == six.text_type(_old[2]): - log.debug('schedules match--no need for changes') - ret['changes'] = {} + log.debug( + "_new[0] == _old[0]: %s", + six.text_type(_new[0]) == six.text_type(_old[0]), + ) + log.debug( + "_new[2] == _old[2]: %s", + six.text_type(_new[2]) == six.text_type(_old[2]), + ) + if six.text_type(_new[0]) == six.text_type( + _old[0] + ) and six.text_type(_new[2]) == six.text_type(_old[2]): + log.debug("schedules match--no need for changes") + ret["changes"] = {} # update the config if we registered any changes - log.debug('schedules match--no need for changes') - if ret['changes']: + log.debug("schedules match--no need for changes") + if ret["changes"]: # if test report there will be an update - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Chronos job {0} is set to be updated'.format( - name - ) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Chronos job {0} is set to be updated".format(name) return ret - update_result = __salt__['chronos.update_job'](name, update_config) - if 'exception' in update_result: - ret['result'] = False - ret['comment'] = 'Failed to update job config for {0}: {1}'.format( - name, - update_result['exception'], + update_result = __salt__["chronos.update_job"](name, update_config) + if "exception" in update_result: + ret["result"] = False + ret["comment"] = "Failed to update job config for {0}: {1}".format( + name, update_result["exception"], ) return ret else: - ret['result'] = True - ret['comment'] = 'Updated job config for {0}'.format(name) + ret["result"] = True + ret["comment"] = "Updated job config for {0}".format(name) return ret - ret['result'] = True - ret['comment'] = 'Chronos job {0} configured correctly'.format(name) + ret["result"] = True + ret["comment"] = "Chronos job {0} configured correctly".format(name) return ret def absent(name): - ''' + """ Ensure that the chronos job with the given name is not present. :param name: The app name :return: A standard Salt changes dictionary - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - if not __salt__['chronos.has_job'](name): - ret['result'] = True - ret['comment'] = 'Job {0} already absent'.format(name) + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + if not __salt__["chronos.has_job"](name): + ret["result"] = True + ret["comment"] = "Job {0} already absent".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Job {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Job {0} is set to be removed".format(name) return ret - if __salt__['chronos.rm_job'](name): - ret['changes'] = {'job': name} - ret['result'] = True - ret['comment'] = 'Removed job {0}'.format(name) + if __salt__["chronos.rm_job"](name): + ret["changes"] = {"job": name} + ret["result"] = True + ret["comment"] = "Removed job {0}".format(name) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to remove job {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to remove job {0}".format(name) return ret diff --git a/salt/states/cimc.py b/salt/states/cimc.py index 72e8dbaefaa..982522a1c36 100644 --- a/salt/states/cimc.py +++ b/salt/states/cimc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A state module to manage Cisco UCS chassis devices. :codeauthor: ``Spencer Ervin <spencer_ervin@hotmail.com>`` @@ -16,35 +16,33 @@ relies on the CIMC proxy module to interface with the device. .. seealso:: :py:mod:`CIMC Proxy Module <salt.proxy.cimc>` -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging log = logging.getLogger(__name__) def __virtual__(): - return 'cimc.get_system_info' in __salt__ + if "cimc.get_system_info" in __salt__: + return True + return (False, "cimc module could not be loaded") def _default_ret(name): - ''' + """ Set the default response values. - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} return ret def hostname(name, hostname=None): - ''' + """ Ensures that the hostname is set to the specified value. .. versionadded:: 2019.2.0 @@ -61,11 +59,11 @@ def hostname(name, hostname=None): cimc.hostname: - hostname: foobar - ''' + """ ret = _default_ret(name) - current_name = __salt__['cimc.get_hostname']() + current_name = __salt__["cimc.get_hostname"]() req_change = False @@ -76,32 +74,32 @@ def hostname(name, hostname=None): if req_change: - update = __salt__['cimc.set_hostname'](hostname) + update = __salt__["cimc.set_hostname"](hostname) if not update: - ret['result'] = False - ret['comment'] = "Error setting hostname." + ret["result"] = False + ret["comment"] = "Error setting hostname." return ret - ret['changes']['before'] = current_name - ret['changes']['after'] = hostname - ret['comment'] = "Hostname modified." + ret["changes"]["before"] = current_name + ret["changes"]["after"] = hostname + ret["comment"] = "Hostname modified." else: - ret['comment'] = "Hostname already configured. No changes required." + ret["comment"] = "Hostname already configured. No changes required." except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = "Error occurred setting hostname." + ret["result"] = False + ret["comment"] = "Error occurred setting hostname." log.error(err) return ret - ret['result'] = True + ret["result"] = True return ret def logging_levels(name, remote=None, local=None): - ''' + """ Ensures that the logging levels are set on the device. The logging levels must match the following options: emergency, alert, critical, error, warning, notice, informational, debug. @@ -123,50 +121,50 @@ def logging_levels(name, remote=None, local=None): - remote: informational - local: notice - ''' + """ ret = _default_ret(name) - syslog_conf = __salt__['cimc.get_syslog_settings']() + syslog_conf = __salt__["cimc.get_syslog_settings"]() req_change = False try: - syslog_dict = syslog_conf['outConfigs']['commSyslog'][0] + syslog_dict = syslog_conf["outConfigs"]["commSyslog"][0] - if remote and syslog_dict['remoteSeverity'] != remote: + if remote and syslog_dict["remoteSeverity"] != remote: req_change = True - elif local and syslog_dict['localSeverity'] != local: + elif local and syslog_dict["localSeverity"] != local: req_change = True if req_change: - update = __salt__['cimc.set_logging_levels'](remote, local) + update = __salt__["cimc.set_logging_levels"](remote, local) - if update['outConfig']['commSyslog'][0]['status'] != 'modified': - ret['result'] = False - ret['comment'] = "Error setting logging levels." + if update["outConfig"]["commSyslog"][0]["status"] != "modified": + ret["result"] = False + ret["comment"] = "Error setting logging levels." return ret - ret['changes']['before'] = syslog_conf - ret['changes']['after'] = __salt__['cimc.get_syslog_settings']() - ret['comment'] = "Logging level settings modified." + ret["changes"]["before"] = syslog_conf + ret["changes"]["after"] = __salt__["cimc.get_syslog_settings"]() + ret["comment"] = "Logging level settings modified." else: - ret['comment'] = "Logging level already configured. No changes required." + ret["comment"] = "Logging level already configured. No changes required." except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = "Error occurred setting logging level settings." + ret["result"] = False + ret["comment"] = "Error occurred setting logging level settings." log.error(err) return ret - ret['result'] = True + ret["result"] = True return ret def ntp(name, servers): - ''' + """ Ensures that the NTP servers are configured. Servers are provided as an individual string or list format. Only four NTP servers will be reviewed. Any entries past four will be ignored. @@ -188,10 +186,10 @@ def ntp(name, servers): cimc.ntp: - servers: foo.bar.com - ''' + """ ret = _default_ret(name) - ntp_servers = ['', '', '', ''] + ntp_servers = ["", "", "", ""] # Parse our server arguments if isinstance(servers, list): @@ -202,53 +200,54 @@ def ntp(name, servers): else: ntp_servers[0] = servers - conf = __salt__['cimc.get_ntp']() + conf = __salt__["cimc.get_ntp"]() # Check if our NTP configuration is already set req_change = False try: - if conf['outConfigs']['commNtpProvider'][0]['ntpEnable'] != 'yes' \ - or ntp_servers[0] != conf['outConfigs']['commNtpProvider'][0]['ntpServer1'] \ - or ntp_servers[1] != conf['outConfigs']['commNtpProvider'][0]['ntpServer2'] \ - or ntp_servers[2] != conf['outConfigs']['commNtpProvider'][0]['ntpServer3'] \ - or ntp_servers[3] != conf['outConfigs']['commNtpProvider'][0]['ntpServer4']: + if ( + conf["outConfigs"]["commNtpProvider"][0]["ntpEnable"] != "yes" + or ntp_servers[0] != conf["outConfigs"]["commNtpProvider"][0]["ntpServer1"] + or ntp_servers[1] != conf["outConfigs"]["commNtpProvider"][0]["ntpServer2"] + or ntp_servers[2] != conf["outConfigs"]["commNtpProvider"][0]["ntpServer3"] + or ntp_servers[3] != conf["outConfigs"]["commNtpProvider"][0]["ntpServer4"] + ): req_change = True except KeyError as err: - ret['result'] = False - ret['comment'] = "Unable to confirm current NTP settings." + ret["result"] = False + ret["comment"] = "Unable to confirm current NTP settings." log.error(err) return ret if req_change: try: - update = __salt__['cimc.set_ntp_server'](ntp_servers[0], - ntp_servers[1], - ntp_servers[2], - ntp_servers[3]) - if update['outConfig']['commNtpProvider'][0]['status'] != 'modified': - ret['result'] = False - ret['comment'] = "Error setting NTP configuration." + update = __salt__["cimc.set_ntp_server"]( + ntp_servers[0], ntp_servers[1], ntp_servers[2], ntp_servers[3] + ) + if update["outConfig"]["commNtpProvider"][0]["status"] != "modified": + ret["result"] = False + ret["comment"] = "Error setting NTP configuration." return ret except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = "Error setting NTP configuration." + ret["result"] = False + ret["comment"] = "Error setting NTP configuration." log.error(err) return ret - ret['changes']['before'] = conf - ret['changes']['after'] = __salt__['cimc.get_ntp']() - ret['comment'] = "NTP settings modified." + ret["changes"]["before"] = conf + ret["changes"]["after"] = __salt__["cimc.get_ntp"]() + ret["comment"] = "NTP settings modified." else: - ret['comment'] = "NTP already configured. No changes required." + ret["comment"] = "NTP already configured. No changes required." - ret['result'] = True + ret["result"] = True return ret def power_configuration(name, policy=None, delayType=None, delayValue=None): - ''' + """ Ensures that the power configuration is configured on the system. This is only available on some C-Series servers. @@ -296,60 +295,63 @@ def power_configuration(name, policy=None, delayType=None, delayValue=None): - policy: stay-off - ''' + """ ret = _default_ret(name) - power_conf = __salt__['cimc.get_power_configuration']() + power_conf = __salt__["cimc.get_power_configuration"]() req_change = False try: - power_dict = power_conf['outConfigs']['biosVfResumeOnACPowerLoss'][0] + power_dict = power_conf["outConfigs"]["biosVfResumeOnACPowerLoss"][0] - if policy and power_dict['vpResumeOnACPowerLoss'] != policy: + if policy and power_dict["vpResumeOnACPowerLoss"] != policy: req_change = True elif policy == "reset": - if power_dict['delayType'] != delayType: + if power_dict["delayType"] != delayType: req_change = True - elif power_dict['delayType'] == "fixed": - if str(power_dict['delay']) != str(delayValue): + elif power_dict["delayType"] == "fixed": + if str(power_dict["delay"]) != str(delayValue): req_change = True else: - ret['result'] = False - ret['comment'] = "The power policy must be specified." + ret["result"] = False + ret["comment"] = "The power policy must be specified." return ret if req_change: - update = __salt__['cimc.set_power_configuration'](policy, - delayType, - delayValue) + update = __salt__["cimc.set_power_configuration"]( + policy, delayType, delayValue + ) - if update['outConfig']['biosVfResumeOnACPowerLoss'][0]['status'] != 'modified': - ret['result'] = False - ret['comment'] = "Error setting power configuration." + if ( + update["outConfig"]["biosVfResumeOnACPowerLoss"][0]["status"] + != "modified" + ): + ret["result"] = False + ret["comment"] = "Error setting power configuration." return ret - ret['changes']['before'] = power_conf - ret['changes']['after'] = __salt__['cimc.get_power_configuration']() - ret['comment'] = "Power settings modified." + ret["changes"]["before"] = power_conf + ret["changes"]["after"] = __salt__["cimc.get_power_configuration"]() + ret["comment"] = "Power settings modified." else: - ret['comment'] = "Power settings already configured. No changes required." + ret["comment"] = "Power settings already configured. No changes required." except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = "Error occurred setting power settings." + ret["result"] = False + ret["comment"] = "Error occurred setting power settings." log.error(err) return ret - ret['result'] = True + ret["result"] = True return ret def syslog(name, primary=None, secondary=None): - ''' + """ Ensures that the syslog servers are set to the specified values. A value of None will be ignored. name: The name of the module function to execute. @@ -367,75 +369,75 @@ def syslog(name, primary=None, secondary=None): - primary: 10.10.10.10 - secondary: foo.bar.com - ''' + """ ret = _default_ret(name) - conf = __salt__['cimc.get_syslog']() + conf = __salt__["cimc.get_syslog"]() req_change = False if primary: prim_change = True - if 'outConfigs' in conf and 'commSyslogClient' in conf['outConfigs']: - for entry in conf['outConfigs']['commSyslogClient']: - if entry['name'] != 'primary': + if "outConfigs" in conf and "commSyslogClient" in conf["outConfigs"]: + for entry in conf["outConfigs"]["commSyslogClient"]: + if entry["name"] != "primary": continue - if entry['adminState'] == 'enabled' and entry['hostname'] == primary: + if entry["adminState"] == "enabled" and entry["hostname"] == primary: prim_change = False if prim_change: try: - update = __salt__['cimc.set_syslog_server'](primary, "primary") - if update['outConfig']['commSyslogClient'][0]['status'] == 'modified': + update = __salt__["cimc.set_syslog_server"](primary, "primary") + if update["outConfig"]["commSyslogClient"][0]["status"] == "modified": req_change = True else: - ret['result'] = False - ret['comment'] = "Error setting primary SYSLOG server." + ret["result"] = False + ret["comment"] = "Error setting primary SYSLOG server." return ret except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = "Error setting primary SYSLOG server." + ret["result"] = False + ret["comment"] = "Error setting primary SYSLOG server." log.error(err) return ret if secondary: sec_change = True - if 'outConfig' in conf and 'commSyslogClient' in conf['outConfig']: - for entry in conf['outConfig']['commSyslogClient']: - if entry['name'] != 'secondary': + if "outConfig" in conf and "commSyslogClient" in conf["outConfig"]: + for entry in conf["outConfig"]["commSyslogClient"]: + if entry["name"] != "secondary": continue - if entry['adminState'] == 'enabled' and entry['hostname'] == secondary: + if entry["adminState"] == "enabled" and entry["hostname"] == secondary: sec_change = False if sec_change: try: - update = __salt__['cimc.set_syslog_server'](secondary, "secondary") - if update['outConfig']['commSyslogClient'][0]['status'] == 'modified': + update = __salt__["cimc.set_syslog_server"](secondary, "secondary") + if update["outConfig"]["commSyslogClient"][0]["status"] == "modified": req_change = True else: - ret['result'] = False - ret['comment'] = "Error setting secondary SYSLOG server." + ret["result"] = False + ret["comment"] = "Error setting secondary SYSLOG server." return ret except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = "Error setting secondary SYSLOG server." + ret["result"] = False + ret["comment"] = "Error setting secondary SYSLOG server." log.error(err) return ret if req_change: - ret['changes']['before'] = conf - ret['changes']['after'] = __salt__['cimc.get_syslog']() - ret['comment'] = "SYSLOG settings modified." + ret["changes"]["before"] = conf + ret["changes"]["after"] = __salt__["cimc.get_syslog"]() + ret["comment"] = "SYSLOG settings modified." else: - ret['comment'] = "SYSLOG already configured. No changes required." + ret["comment"] = "SYSLOG already configured. No changes required." - ret['result'] = True + ret["result"] = True return ret -def user(name, id='', user='', priv='', password='', status='active'): - ''' +def user(name, id="", user="", priv="", password="", status="active"): + """ Ensures that a user is configured on the device. Due to being unable to verify the user password. This is a forced operation. @@ -465,39 +467,41 @@ def user(name, id='', user='', priv='', password='', status='active'): - password: mypassword - status: active - ''' + """ ret = _default_ret(name) - user_conf = __salt__['cimc.get_users']() + user_conf = __salt__["cimc.get_users"]() try: - for entry in user_conf['outConfigs']['aaaUser']: - if entry['id'] == str(id): + for entry in user_conf["outConfigs"]["aaaUser"]: + if entry["id"] == str(id): conf = entry if not conf: - ret['result'] = False - ret['comment'] = "Unable to find requested user id on device. Please verify id is valid." + ret["result"] = False + ret[ + "comment" + ] = "Unable to find requested user id on device. Please verify id is valid." return ret - updates = __salt__['cimc.set_user'](str(id), user, password, priv, status) + updates = __salt__["cimc.set_user"](str(id), user, password, priv, status) - if 'outConfig' in updates: - ret['changes']['before'] = conf - ret['changes']['after'] = updates['outConfig']['aaaUser'] - ret['comment'] = "User settings modified." + if "outConfig" in updates: + ret["changes"]["before"] = conf + ret["changes"]["after"] = updates["outConfig"]["aaaUser"] + ret["comment"] = "User settings modified." else: - ret['result'] = False - ret['comment'] = "Error setting user configuration." + ret["result"] = False + ret["comment"] = "Error setting user configuration." return ret except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = "Error setting user configuration." + ret["result"] = False + ret["comment"] = "Error setting user configuration." log.error(err) return ret - ret['result'] = True + ret["result"] = True return ret diff --git a/salt/states/cisconso.py b/salt/states/cisconso.py index 85257e75ed6..be86688d5a9 100644 --- a/salt/states/cisconso.py +++ b/salt/states/cisconso.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" State module for Cisco NSO Proxy minions .. versionadded: 2016.11.0 For documentation on setting up the cisconso proxy minion look in the documentation for :mod:`salt.proxy.cisconso <salt.proxy.cisconso>`. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -16,11 +16,13 @@ import salt.utils.compat def __virtual__(): - return 'cisconso.set_data_value' in __salt__ + if "cisconso.set_data_value" in __salt__: + return True + return (False, "cisconso module could not be loaded") def value_present(name, datastore, path, config): - ''' + """ Ensure a specific value exists at a given path :param name: The name for this rule @@ -51,49 +53,50 @@ def value_present(name, datastore, path, config): method: pap "list-name": foobar - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - existing = __salt__['cisconso.get_data'](datastore, path) + existing = __salt__["cisconso.get_data"](datastore, path) if salt.utils.compat.cmp(existing, config): - ret['result'] = True - ret['comment'] = 'Config is already set' + ret["result"] = True + ret["comment"] = "Config is already set" - elif __opts__['test'] is True: - ret['result'] = None - ret['comment'] = 'Config will be added' + elif __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "Config will be added" diff = _DictDiffer(existing, config) - ret['changes']['new'] = diff.added() - ret['changes']['removed'] = diff.removed() - ret['changes']['changed'] = diff.changed() + ret["changes"]["new"] = diff.added() + ret["changes"]["removed"] = diff.removed() + ret["changes"]["changed"] = diff.changed() else: - __salt__['cisconso.set_data_value'](datastore, path, config) - ret['result'] = True - ret['comment'] = 'Successfully added config' + __salt__["cisconso.set_data_value"](datastore, path, config) + ret["result"] = True + ret["comment"] = "Successfully added config" diff = _DictDiffer(existing, config) - ret['changes']['new'] = diff.added() - ret['changes']['removed'] = diff.removed() - ret['changes']['changed'] = diff.changed() + ret["changes"]["new"] = diff.added() + ret["changes"]["removed"] = diff.removed() + ret["changes"]["changed"] = diff.changed() return ret class _DictDiffer(object): - ''' + """ Calculate the difference between two dictionaries as: (1) items added (2) items removed (3) keys same in both but changed values (4) keys same in both and unchanged values - ''' + """ + def __init__(self, current_dict, past_dict): self.current_dict, self.past_dict = current_dict, past_dict - self.set_current, self.set_past = set(current_dict.keys()), set(past_dict.keys()) + self.set_current, self.set_past = ( + set(current_dict.keys()), + set(past_dict.keys()), + ) self.intersect = self.set_current.intersection(self.set_past) def added(self): @@ -103,7 +106,11 @@ class _DictDiffer(object): return self.set_past - self.intersect def changed(self): - return set(o for o in self.intersect if self.past_dict[o] != self.current_dict[o]) + return set( + o for o in self.intersect if self.past_dict[o] != self.current_dict[o] + ) def unchanged(self): - return set(o for o in self.intersect if self.past_dict[o] == self.current_dict[o]) + return set( + o for o in self.intersect if self.past_dict[o] == self.current_dict[o] + ) diff --git a/salt/states/cloud.py b/salt/states/cloud.py index dd9f982255c..6d8493759a9 100644 --- a/salt/states/cloud.py +++ b/salt/states/cloud.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Using states instead of maps to deploy clouds ============================================= @@ -12,60 +12,57 @@ Use this minion to spin up a cloud instance: my-ec2-instance: cloud.profile: my-ec2-config -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import pprint -# Import 3rd-party libs -from salt.ext import six +import pprint # Import Salt Libs import salt.utils.cloud as suc +# Import 3rd-party libs +from salt.ext import six + def __virtual__(): - ''' + """ Only load if the cloud module is available in __salt__ - ''' - return 'cloud.profile' in __salt__ + """ + if "cloud.profile" in __salt__: + return True + return (False, "cloud module could not be loaded") def _check_name(name): - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} - if suc.check_name(name, 'a-zA-Z0-9._-'): - ret['comment'] = 'Invalid characters in name.' - ret['result'] = False + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + if suc.check_name(name, "a-zA-Z0-9._-"): + ret["comment"] = "Invalid characters in name." + ret["result"] = False return ret else: - ret['result'] = True + ret["result"] = True return ret -def _valid(name, comment='', changes=None): +def _valid(name, comment="", changes=None): if not changes: changes = {} - return {'name': name, - 'result': True, - 'changes': changes, - 'comment': comment} + return {"name": name, "result": True, "changes": changes, "comment": comment} def _get_instance(names): # for some reason loader overwrites __opts__['test'] with default # value of False, thus store and then load it again after action - test = __opts__.get('test', False) - instance = __salt__['cloud.action'](fun='show_instance', names=names) - __opts__['test'] = test + test = __opts__.get("test", False) + instance = __salt__["cloud.action"](fun="show_instance", names=names) + __opts__["test"] = test return instance def present(name, cloud_provider, onlyif=None, unless=None, opts=None, **kwargs): - ''' + """ Spin up a single instance on a cloud provider, using salt-cloud. This state does not take a profile argument; rather, it takes the arguments that would normally be configured as part of the state. @@ -90,68 +87,60 @@ def present(name, cloud_provider, onlyif=None, unless=None, opts=None, **kwargs) opts Any extra opts that need to be used - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - retcode = __salt__['cmd.retcode'] + retcode = __salt__["cmd.retcode"] if onlyif is not None: if not isinstance(onlyif, six.string_types): if not onlyif: - return _valid(name, comment='onlyif condition is false') + return _valid(name, comment="onlyif condition is false") elif isinstance(onlyif, six.string_types): if retcode(onlyif, python_shell=True) != 0: - return _valid(name, comment='onlyif condition is false') + return _valid(name, comment="onlyif condition is false") if unless is not None: if not isinstance(unless, six.string_types): if unless: - return _valid(name, comment='unless condition is true') + return _valid(name, comment="unless condition is true") elif isinstance(unless, six.string_types): if retcode(unless, python_shell=True) == 0: - return _valid(name, comment='unless condition is true') + return _valid(name, comment="unless condition is true") # provider=None not cloud_provider because # need to ensure ALL providers don't have the instance - if __salt__['cloud.has_instance'](name=name, provider=None): - ret['result'] = True - ret['comment'] = 'Already present instance {0}'.format(name) + if __salt__["cloud.has_instance"](name=name, provider=None): + ret["result"] = True + ret["comment"] = "Already present instance {0}".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Instance {0} needs to be created'.format(name) + if __opts__["test"]: + ret["comment"] = "Instance {0} needs to be created".format(name) return ret - info = __salt__['cloud.create'](cloud_provider, name, opts=opts, **kwargs) - if info and 'Error' not in info: - ret['changes'] = info - ret['result'] = True - ret['comment'] = ('Created instance {0} using provider {1} ' - 'and the following options: {2}').format( - name, - cloud_provider, - pprint.pformat(kwargs) - ) - elif info and 'Error' in info: - ret['result'] = False - ret['comment'] = ('Failed to create instance {0}' - 'using profile {1}: {2}').format( - name, - profile, - info['Error'], - ) + info = __salt__["cloud.create"](cloud_provider, name, opts=opts, **kwargs) + if info and "Error" not in info: + ret["changes"] = info + ret["result"] = True + ret["comment"] = ( + "Created instance {0} using provider {1} " "and the following options: {2}" + ).format(name, cloud_provider, pprint.pformat(kwargs)) + elif info and "Error" in info: + ret["result"] = False + ret["comment"] = ( + "Failed to create instance {0}" "using profile {1}: {2}" + ).format(name, profile, info["Error"],) else: - ret['result'] = False - ret['comment'] = ('Failed to create instance {0}' - ' using profile {1},' - ' please check your configuration').format(name, - profile) + ret["result"] = False + ret["comment"] = ( + "Failed to create instance {0}" + " using profile {1}," + " please check your configuration" + ).format(name, profile) return ret def absent(name, onlyif=None, unless=None): - ''' + """ Ensure that no instances with the specified names exist. CAUTION: This is a destructive state, which will search all @@ -167,56 +156,52 @@ def absent(name, onlyif=None, unless=None): unless Do not run the state at least unless succeed - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} - retcode = __salt__['cmd.retcode'] + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + retcode = __salt__["cmd.retcode"] if onlyif is not None: if not isinstance(onlyif, six.string_types): if not onlyif: - return _valid(name, comment='onlyif condition is false') + return _valid(name, comment="onlyif condition is false") elif isinstance(onlyif, six.string_types): if retcode(onlyif, python_shell=True) != 0: - return _valid(name, comment='onlyif condition is false') + return _valid(name, comment="onlyif condition is false") if unless is not None: if not isinstance(unless, six.string_types): if unless: - return _valid(name, comment='unless condition is true') + return _valid(name, comment="unless condition is true") elif isinstance(unless, six.string_types): if retcode(unless, python_shell=True) == 0: - return _valid(name, comment='unless condition is true') + return _valid(name, comment="unless condition is true") - if not __salt__['cloud.has_instance'](name=name, provider=None): - ret['result'] = True - ret['comment'] = 'Already absent instance {0}'.format(name) + if not __salt__["cloud.has_instance"](name=name, provider=None): + ret["result"] = True + ret["comment"] = "Already absent instance {0}".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Instance {0} needs to be destroyed'.format(name) + if __opts__["test"]: + ret["comment"] = "Instance {0} needs to be destroyed".format(name) return ret - info = __salt__['cloud.destroy'](name) - if info and 'Error' not in info: - ret['changes'] = info - ret['result'] = True - ret['comment'] = 'Destroyed instance {0}'.format(name) - elif 'Error' in info: - ret['result'] = False - ret['comment'] = ('Failed to destroy instance {0}: {1}').format( - name, - info['Error'], + info = __salt__["cloud.destroy"](name) + if info and "Error" not in info: + ret["changes"] = info + ret["result"] = True + ret["comment"] = "Destroyed instance {0}".format(name) + elif "Error" in info: + ret["result"] = False + ret["comment"] = ("Failed to destroy instance {0}: {1}").format( + name, info["Error"], ) else: - ret['result'] = False - ret['comment'] = 'Failed to destroy instance {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to destroy instance {0}".format(name) return ret def profile(name, profile, onlyif=None, unless=None, opts=None, **kwargs): - ''' + """ Create a single instance on a cloud provider, using a salt-cloud profile. Note that while profiles used this function do take any configuration @@ -242,257 +227,225 @@ def profile(name, profile, onlyif=None, unless=None, opts=None, **kwargs): opts Any extra opts that need to be used - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} - retcode = __salt__['cmd.retcode'] + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + retcode = __salt__["cmd.retcode"] if onlyif is not None: if not isinstance(onlyif, six.string_types): if not onlyif: - return _valid(name, comment='onlyif condition is false') + return _valid(name, comment="onlyif condition is false") elif isinstance(onlyif, six.string_types): if retcode(onlyif, python_shell=True) != 0: - return _valid(name, comment='onlyif condition is false') + return _valid(name, comment="onlyif condition is false") if unless is not None: if not isinstance(unless, six.string_types): if unless: - return _valid(name, comment='unless condition is true') + return _valid(name, comment="unless condition is true") elif isinstance(unless, six.string_types): if retcode(unless, python_shell=True) == 0: - return _valid(name, comment='unless condition is true') + return _valid(name, comment="unless condition is true") instance = _get_instance([name]) - if instance and not any('Not Actioned' in key for key in instance): - ret['result'] = True - ret['comment'] = 'Already present instance {0}'.format(name) + if instance and not any("Not Actioned" in key for key in instance): + ret["result"] = True + ret["comment"] = "Already present instance {0}".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Instance {0} needs to be created'.format(name) + if __opts__["test"]: + ret["comment"] = "Instance {0} needs to be created".format(name) return ret - info = __salt__['cloud.profile'](profile, name, vm_overrides=kwargs, opts=opts) + info = __salt__["cloud.profile"](profile, name, vm_overrides=kwargs, opts=opts) # get either {Error: ''} or {namestring: {Error: ''}} # which is what we can get from providers returns - main_error = info.get('Error', '') - name_error = '' + main_error = info.get("Error", "") + name_error = "" if isinstance(info, dict): subinfo = info.get(name, {}) if isinstance(subinfo, dict): - name_error = subinfo.get('Error', None) + name_error = subinfo.get("Error", None) error = main_error or name_error if info and not error: node_info = info.get(name) - ret['result'] = True - default_msg = 'Created instance {0} using profile {1}'.format( - name, profile,) + ret["result"] = True + default_msg = "Created instance {0} using profile {1}".format(name, profile,) # some providers support changes - if 'changes' in node_info: - ret['changes'] = node_info['changes'] - ret['comment'] = node_info.get('comment', default_msg) + if "changes" in node_info: + ret["changes"] = node_info["changes"] + ret["comment"] = node_info.get("comment", default_msg) else: - ret['changes'] = info - ret['comment'] = default_msg + ret["changes"] = info + ret["comment"] = default_msg elif error: - ret['result'] = False - ret['comment'] = ('Failed to create instance {0}' - ' using profile {1}: {2}').format( - name, - profile, - '{0}\n{1}\n'.format(main_error, name_error).strip(), - ) + ret["result"] = False + ret["comment"] = ( + "Failed to create instance {0}" " using profile {1}: {2}" + ).format(name, profile, "{0}\n{1}\n".format(main_error, name_error).strip(),) else: - ret['result'] = False - ret['comment'] = ('Failed to create instance {0}' - 'using profile {1}').format( - name, - profile, + ret["result"] = False + ret["comment"] = ("Failed to create instance {0}" "using profile {1}").format( + name, profile, ) return ret def volume_present(name, provider=None, **kwargs): - ''' + """ Check that a block volume exists. - ''' + """ ret = _check_name(name) - if not ret['result']: + if not ret["result"]: return ret - volumes = __salt__['cloud.volume_list'](provider=provider) + volumes = __salt__["cloud.volume_list"](provider=provider) if name in volumes: - ret['comment'] = 'Volume exists: {0}'.format(name) - ret['result'] = True + ret["comment"] = "Volume exists: {0}".format(name) + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Volume {0} will be created.'.format(name) - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "Volume {0} will be created.".format(name) + ret["result"] = None return ret - response = __salt__['cloud.volume_create']( - names=name, - provider=provider, - **kwargs - ) + response = __salt__["cloud.volume_create"](names=name, provider=provider, **kwargs) if response: - ret['result'] = True - ret['comment'] = 'Volume {0} was created'.format(name) - ret['changes'] = {'old': None, 'new': response} + ret["result"] = True + ret["comment"] = "Volume {0} was created".format(name) + ret["changes"] = {"old": None, "new": response} else: - ret['result'] = False - ret['comment'] = 'Volume {0} failed to create.'.format(name) + ret["result"] = False + ret["comment"] = "Volume {0} failed to create.".format(name) return ret def volume_absent(name, provider=None, **kwargs): - ''' + """ Check that a block volume exists. - ''' + """ ret = _check_name(name) - if not ret['result']: + if not ret["result"]: return ret - volumes = __salt__['cloud.volume_list'](provider=provider) + volumes = __salt__["cloud.volume_list"](provider=provider) if name not in volumes: - ret['comment'] = 'Volume is absent.' - ret['result'] = True + ret["comment"] = "Volume is absent." + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Volume {0} will be deleted.'.format(name) - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "Volume {0} will be deleted.".format(name) + ret["result"] = None return ret - response = __salt__['cloud.volume_delete']( - names=name, - provider=provider, - **kwargs - ) + response = __salt__["cloud.volume_delete"](names=name, provider=provider, **kwargs) if response: - ret['result'] = True - ret['comment'] = 'Volume {0} was deleted'.format(name) - ret['changes'] = {'old': volumes[name], 'new': response} + ret["result"] = True + ret["comment"] = "Volume {0} was deleted".format(name) + ret["changes"] = {"old": volumes[name], "new": response} else: - ret['result'] = False - ret['comment'] = 'Volume {0} failed to delete.'.format(name) + ret["result"] = False + ret["comment"] = "Volume {0} failed to delete.".format(name) return ret def volume_attached(name, server_name, provider=None, **kwargs): - ''' + """ Check if a block volume is attached. - ''' + """ ret = _check_name(name) - if not ret['result']: + if not ret["result"]: return ret ret = _check_name(server_name) - if not ret['result']: + if not ret["result"]: return ret - volumes = __salt__['cloud.volume_list'](provider=provider) - instance = __salt__['cloud.action']( - fun='show_instance', - names=server_name - ) + volumes = __salt__["cloud.volume_list"](provider=provider) + instance = __salt__["cloud.action"](fun="show_instance", names=server_name) - if name in volumes and volumes[name]['attachments']: + if name in volumes and volumes[name]["attachments"]: volume = volumes[name] - ret['comment'] = ( - 'Volume {name} is already attached: {attachments}'.format( - **volumes[name] - ) + ret["comment"] = "Volume {name} is already attached: {attachments}".format( + **volumes[name] ) - ret['result'] = True + ret["result"] = True return ret elif name not in volumes: - ret['comment'] = 'Volume {0} does not exist'.format(name) - ret['result'] = False + ret["comment"] = "Volume {0} does not exist".format(name) + ret["result"] = False return ret elif not instance: - ret['comment'] = 'Server {0} does not exist'.format(server_name) - ret['result'] = False + ret["comment"] = "Server {0} does not exist".format(server_name) + ret["result"] = False return ret - elif __opts__['test']: - ret['comment'] = 'Volume {0} will be will be attached.'.format( - name - ) - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "Volume {0} will be will be attached.".format(name) + ret["result"] = None return ret - response = __salt__['cloud.volume_attach']( - provider=provider, - names=name, - server_name=server_name, - **kwargs + response = __salt__["cloud.volume_attach"]( + provider=provider, names=name, server_name=server_name, **kwargs ) if response: - ret['result'] = True - ret['comment'] = 'Volume {0} was created'.format(name) - ret['changes'] = {'old': volumes[name], 'new': response} + ret["result"] = True + ret["comment"] = "Volume {0} was created".format(name) + ret["changes"] = {"old": volumes[name], "new": response} else: - ret['result'] = False - ret['comment'] = 'Volume {0} failed to attach.'.format(name) + ret["result"] = False + ret["comment"] = "Volume {0} failed to attach.".format(name) return ret def volume_detached(name, server_name=None, provider=None, **kwargs): - ''' + """ Check if a block volume is attached. Returns True if server or Volume do not exist. - ''' + """ ret = _check_name(name) - if not ret['result']: + if not ret["result"]: return ret if server_name is not None: ret = _check_name(server_name) - if not ret['result']: + if not ret["result"]: return ret - volumes = __salt__['cloud.volume_list'](provider=provider) + volumes = __salt__["cloud.volume_list"](provider=provider) if server_name: - instance = __salt__['cloud.action'](fun='show_instance', names=[name]) + instance = __salt__["cloud.action"](fun="show_instance", names=[name]) else: instance = None - if name in volumes and not volumes[name]['attachments']: + if name in volumes and not volumes[name]["attachments"]: volume = volumes[name] - ret['comment'] = ( - 'Volume {name} is not currently attached to anything.' + ret["comment"] = ( + "Volume {name} is not currently attached to anything." ).format(**volumes[name]) - ret['result'] = True + ret["result"] = True return ret elif name not in volumes: - ret['comment'] = 'Volume {0} does not exist'.format(name) - ret['result'] = True + ret["comment"] = "Volume {0} does not exist".format(name) + ret["result"] = True return ret elif not instance and server_name is not None: - ret['comment'] = 'Server {0} does not exist'.format(server_name) - ret['result'] = True + ret["comment"] = "Server {0} does not exist".format(server_name) + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Volume {0} will be will be detached.'.format( - name - ) - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "Volume {0} will be will be detached.".format(name) + ret["result"] = None return ret - response = __salt__['cloud.volume_detach']( - provider=provider, - names=name, - server_name=server_name, - **kwargs + response = __salt__["cloud.volume_detach"]( + provider=provider, names=name, server_name=server_name, **kwargs ) if response: - ret['result'] = True - ret['comment'] = 'Volume {0} was created'.format(name) - ret['changes'] = {'old': volumes[name], 'new': response} + ret["result"] = True + ret["comment"] = "Volume {0} was created".format(name) + ret["changes"] = {"old": volumes[name], "new": response} else: - ret['result'] = False - ret['comment'] = 'Volume {0} failed to detach.'.format(name) + ret["result"] = False + ret["comment"] = "Volume {0} failed to detach.".format(name) return ret diff --git a/salt/states/cmd.py b/salt/states/cmd.py index d611e9d3905..e4e54e1578e 100644 --- a/salt/states/cmd.py +++ b/salt/states/cmd.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Execution of arbitrary commands =============================== @@ -229,14 +229,14 @@ To use it, one may pass it like this. Example: cmd.run: - env: {{ salt['pillar.get']('example:key', {}) }} -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os import copy import logging +import os # Import salt libs import salt.utils.args @@ -249,17 +249,17 @@ log = logging.getLogger(__name__) def _reinterpreted_state(state): - ''' + """ Re-interpret the state returned by salt.state.run using our protocol. - ''' - ret = state['changes'] - state['changes'] = {} - state['comment'] = '' + """ + ret = state["changes"] + state["changes"] = {} + state["comment"] = "" - out = ret.get('stdout') + out = ret.get("stdout") if not out: - if ret.get('stderr'): - state['comment'] = ret['stderr'] + if ret.get("stderr"): + state["comment"] = ret["stderr"] return state is_json = False @@ -267,33 +267,32 @@ def _reinterpreted_state(state): data = salt.utils.json.loads(out) if not isinstance(data, dict): return _failout( - state, - 'script JSON output must be a JSON object (e.g., {})!' + state, "script JSON output must be a JSON object (e.g., {})!" ) is_json = True except ValueError: - idx = out.rstrip().rfind('\n') + idx = out.rstrip().rfind("\n") if idx != -1: - out = out[idx + 1:] + out = out[idx + 1 :] data = {} try: for item in salt.utils.args.shlex_split(out): - key, val = item.split('=') + key, val = item.split("=") data[key] = val except ValueError: state = _failout( state, - 'Failed parsing script output! ' - 'Stdout must be JSON or a line of name=value pairs.' + "Failed parsing script output! " + "Stdout must be JSON or a line of name=value pairs.", ) - state['changes'].update(ret) + state["changes"].update(ret) return state - changed = _is_true(data.get('changed', 'no')) + changed = _is_true(data.get("changed", "no")) - if 'comment' in data: - state['comment'] = data['comment'] - del data['comment'] + if "comment" in data: + state["comment"] = data["comment"] + del data["comment"] if changed: for key in ret: @@ -301,120 +300,140 @@ def _reinterpreted_state(state): # if stdout is the state output in JSON, don't show it. # otherwise it contains the one line name=value pairs, strip it. - data['stdout'] = '' if is_json else data.get('stdout', '')[:idx] - state['changes'] = data + data["stdout"] = "" if is_json else data.get("stdout", "")[:idx] + state["changes"] = data - #FIXME: if it's not changed but there's stdout and/or stderr then those + # FIXME: if it's not changed but there's stdout and/or stderr then those # won't be shown as the function output. (though, they will be shown # inside INFO logs). return state def _failout(state, msg): - state['comment'] = msg - state['result'] = False + state["comment"] = msg + state["result"] = False return state def _is_true(val): - if val and six.text_type(val).lower() in ('true', 'yes', '1'): + if val and six.text_type(val).lower() in ("true", "yes", "1"): return True - elif six.text_type(val).lower() in ('false', 'no', '0'): + elif six.text_type(val).lower() in ("false", "no", "0"): return False - raise ValueError('Failed parsing boolean value: {0}'.format(val)) + raise ValueError("Failed parsing boolean value: {0}".format(val)) def mod_run_check(cmd_kwargs, onlyif, unless, creates): - ''' + """ Execute the onlyif and unless logic. Return a result dict if: * onlyif failed (onlyif != 0) * unless succeeded (unless == 0) else return True - ''' + """ # never use VT for onlyif/unless executions because this will lead # to quote problems cmd_kwargs = copy.deepcopy(cmd_kwargs) - cmd_kwargs['use_vt'] = False - cmd_kwargs['bg'] = False + cmd_kwargs["use_vt"] = False + cmd_kwargs["bg"] = False if onlyif is not None: if isinstance(onlyif, six.string_types): - cmd = __salt__['cmd.retcode'](onlyif, ignore_retcode=True, python_shell=True, **cmd_kwargs) - log.debug('Last command return code: {0}'.format(cmd)) + cmd = __salt__["cmd.retcode"]( + onlyif, ignore_retcode=True, python_shell=True, **cmd_kwargs + ) + log.debug("Last command return code: {0}".format(cmd)) if cmd != 0: - return {'comment': 'onlyif condition is false', - 'skip_watch': True, - 'result': True} + return { + "comment": "onlyif condition is false", + "skip_watch": True, + "result": True, + } elif isinstance(onlyif, list): for entry in onlyif: - cmd = __salt__['cmd.retcode'](entry, ignore_retcode=True, python_shell=True, **cmd_kwargs) - log.debug('Last command \'{0}\' return code: {1}'.format(entry, cmd)) + cmd = __salt__["cmd.retcode"]( + entry, ignore_retcode=True, python_shell=True, **cmd_kwargs + ) + log.debug("Last command '{0}' return code: {1}".format(entry, cmd)) if cmd != 0: - return {'comment': 'onlyif condition is false: {0}'.format(entry), - 'skip_watch': True, - 'result': True} + return { + "comment": "onlyif condition is false: {0}".format(entry), + "skip_watch": True, + "result": True, + } elif not isinstance(onlyif, six.string_types): if not onlyif: - log.debug('Command not run: onlyif did not evaluate to string_type') - return {'comment': 'onlyif condition is false', - 'skip_watch': True, - 'result': True} + log.debug("Command not run: onlyif did not evaluate to string_type") + return { + "comment": "onlyif condition is false", + "skip_watch": True, + "result": True, + } if unless is not None: if isinstance(unless, six.string_types): - cmd = __salt__['cmd.retcode'](unless, ignore_retcode=True, python_shell=True, **cmd_kwargs) - log.debug('Last command return code: {0}'.format(cmd)) + cmd = __salt__["cmd.retcode"]( + unless, ignore_retcode=True, python_shell=True, **cmd_kwargs + ) + log.debug("Last command return code: {0}".format(cmd)) if cmd == 0: - return {'comment': 'unless condition is true', - 'skip_watch': True, - 'result': True} + return { + "comment": "unless condition is true", + "skip_watch": True, + "result": True, + } elif isinstance(unless, list): cmd = [] for entry in unless: - cmd.append(__salt__['cmd.retcode'](entry, ignore_retcode=True, python_shell=True, **cmd_kwargs)) - log.debug('Last command return code: {0}'.format(cmd)) + cmd.append( + __salt__["cmd.retcode"]( + entry, ignore_retcode=True, python_shell=True, **cmd_kwargs + ) + ) + log.debug("Last command return code: {0}".format(cmd)) if all([c == 0 for c in cmd]): - return {'comment': 'unless condition is true', - 'skip_watch': True, - 'result': True} + return { + "comment": "unless condition is true", + "skip_watch": True, + "result": True, + } elif not isinstance(unless, six.string_types): if unless: - log.debug('Command not run: unless did not evaluate to string_type') - return {'comment': 'unless condition is true', - 'skip_watch': True, - 'result': True} + log.debug("Command not run: unless did not evaluate to string_type") + return { + "comment": "unless condition is true", + "skip_watch": True, + "result": True, + } if isinstance(creates, six.string_types) and os.path.exists(creates): - return {'comment': '{0} exists'.format(creates), - 'result': True} - elif isinstance(creates, list) and all([ - os.path.exists(path) for path in creates - ]): - return {'comment': 'All files in creates exist', - 'result': True} + return {"comment": "{0} exists".format(creates), "result": True} + elif isinstance(creates, list) and all([os.path.exists(path) for path in creates]): + return {"comment": "All files in creates exist", "result": True} # No reason to stop, return True return True -def wait(name, - onlyif=None, - unless=None, - creates=None, - cwd=None, - root=None, - runas=None, - shell=None, - env=(), - stateful=False, - umask=None, - output_loglevel='debug', - hide_output=False, - use_vt=False, - success_retcodes=None, - **kwargs): - ''' +def wait( + name, + onlyif=None, + unless=None, + creates=None, + cwd=None, + root=None, + runas=None, + shell=None, + env=(), + stateful=False, + umask=None, + output_loglevel="debug", + hide_output=False, + use_vt=False, + success_retcodes=None, + **kwargs +): + """ Run the given command only if the watch statement calls it. .. note:: @@ -530,34 +549,33 @@ def wait(name, the return code will be overridden with zero. .. versionadded:: 2019.2.0 - ''' + """ # Ignoring our arguments is intentional. - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + return {"name": name, "changes": {}, "result": True, "comment": ""} # Alias "cmd.watch" to "cmd.wait", as this is a common misconfiguration -watch = salt.utils.functools.alias_function(wait, 'watch') +watch = salt.utils.functools.alias_function(wait, "watch") -def wait_script(name, - source=None, - template=None, - onlyif=None, - unless=None, - cwd=None, - runas=None, - shell=None, - env=None, - stateful=False, - umask=None, - use_vt=False, - output_loglevel='debug', - hide_output=False, - **kwargs): - ''' +def wait_script( + name, + source=None, + template=None, + onlyif=None, + unless=None, + cwd=None, + runas=None, + shell=None, + env=None, + stateful=False, + umask=None, + use_vt=False, + output_loglevel="debug", + hide_output=False, + **kwargs +): + """ Download a script from a remote source and execute it only if a watch statement calls it. @@ -669,34 +687,33 @@ def wait_script(name, the return code will be overridden with zero. .. versionadded:: 2019.2.0 - ''' + """ # Ignoring our arguments is intentional. - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + return {"name": name, "changes": {}, "result": True, "comment": ""} -def run(name, - onlyif=None, - unless=None, - creates=None, - cwd=None, - root=None, - runas=None, - shell=None, - env=None, - prepend_path=None, - stateful=False, - umask=None, - output_loglevel='debug', - hide_output=False, - timeout=None, - ignore_timeout=False, - use_vt=False, - success_retcodes=None, - **kwargs): - ''' +def run( + name, + onlyif=None, + unless=None, + creates=None, + cwd=None, + root=None, + runas=None, + shell=None, + env=None, + prepend_path=None, + stateful=False, + umask=None, + output_loglevel="debug", + hide_output=False, + timeout=None, + ignore_timeout=False, + use_vt=False, + success_retcodes=None, + **kwargs +): + """ Run a command if certain circumstances are met. Use ``cmd.wait`` if you want to use the ``watch`` requisite. @@ -850,111 +867,114 @@ def run(name, - file: /usr/local/sbin/get-pip.py - reload_modules: True - ''' + """ ### NOTE: The keyword arguments in **kwargs are passed directly to the ### ``cmd.run_all`` function and cannot be removed from the function ### definition, otherwise the use of unsupported arguments in a ### ``cmd.run`` state will result in a traceback. - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} test_name = None if not isinstance(stateful, list): stateful = stateful is True - elif isinstance(stateful, list) and 'test_name' in stateful[0]: - test_name = stateful[0]['test_name'] - if __opts__['test'] and test_name: + elif isinstance(stateful, list) and "test_name" in stateful[0]: + test_name = stateful[0]["test_name"] + if __opts__["test"] and test_name: name = test_name # Need the check for None here, if env is not provided then it falls back # to None and it is assumed that the environment is not being overridden. if env is not None and not isinstance(env, (list, dict)): - ret['comment'] = ('Invalidly-formatted \'env\' parameter. See ' - 'documentation.') + ret["comment"] = "Invalidly-formatted 'env' parameter. See " "documentation." return ret cmd_kwargs = copy.deepcopy(kwargs) - cmd_kwargs.update({'cwd': cwd, - 'root': root, - 'runas': runas, - 'use_vt': use_vt, - 'shell': shell or __grains__['shell'], - 'env': env, - 'prepend_path': prepend_path, - 'umask': umask, - 'output_loglevel': output_loglevel, - 'hide_output': hide_output, - 'success_retcodes': success_retcodes}) + cmd_kwargs.update( + { + "cwd": cwd, + "root": root, + "runas": runas, + "use_vt": use_vt, + "shell": shell or __grains__["shell"], + "env": env, + "prepend_path": prepend_path, + "umask": umask, + "output_loglevel": output_loglevel, + "hide_output": hide_output, + "success_retcodes": success_retcodes, + } + ) cret = mod_run_check(cmd_kwargs, onlyif, unless, creates) if isinstance(cret, dict): ret.update(cret) return ret - if __opts__['test'] and not test_name: - ret['result'] = None - ret['comment'] = 'Command "{0}" would have been executed'.format(name) + if __opts__["test"] and not test_name: + ret["result"] = None + ret["comment"] = 'Command "{0}" would have been executed'.format(name) return _reinterpreted_state(ret) if stateful else ret if cwd and not os.path.isdir(cwd): - ret['comment'] = ( - 'Desired working directory "{0}" ' - 'is not available' - ).format(cwd) + ret["comment"] = ('Desired working directory "{0}" ' "is not available").format( + cwd + ) return ret # Wow, we passed the test, run this sucker! try: - run_cmd = 'cmd.run_all' if not root else 'cmd.run_chroot' + run_cmd = "cmd.run_all" if not root else "cmd.run_chroot" cmd_all = __salt__[run_cmd]( cmd=name, timeout=timeout, python_shell=True, **cmd_kwargs ) except Exception as err: # pylint: disable=broad-except - ret['comment'] = six.text_type(err) + ret["comment"] = six.text_type(err) return ret - ret['changes'] = cmd_all - ret['result'] = not bool(cmd_all['retcode']) - ret['comment'] = 'Command "{0}" run'.format(name) + ret["changes"] = cmd_all + ret["result"] = not bool(cmd_all["retcode"]) + ret["comment"] = 'Command "{0}" run'.format(name) # Ignore timeout errors if asked (for nohups) and treat cmd as a success if ignore_timeout: - trigger = 'Timed out after' - if ret['changes'].get('retcode') == 1 and trigger in ret['changes'].get('stdout'): - ret['changes']['retcode'] = 0 - ret['result'] = True + trigger = "Timed out after" + if ret["changes"].get("retcode") == 1 and trigger in ret["changes"].get( + "stdout" + ): + ret["changes"]["retcode"] = 0 + ret["result"] = True if stateful: ret = _reinterpreted_state(ret) - if __opts__['test'] and cmd_all['retcode'] == 0 and ret['changes']: - ret['result'] = None + if __opts__["test"] and cmd_all["retcode"] == 0 and ret["changes"]: + ret["result"] = None return ret -def script(name, - source=None, - template=None, - onlyif=None, - unless=None, - creates=None, - cwd=None, - runas=None, - shell=None, - env=None, - stateful=False, - umask=None, - timeout=None, - use_vt=False, - output_loglevel='debug', - hide_output=False, - defaults=None, - context=None, - success_retcodes=None, - **kwargs): - ''' +def script( + name, + source=None, + template=None, + onlyif=None, + unless=None, + creates=None, + cwd=None, + runas=None, + shell=None, + env=None, + stateful=False, + umask=None, + timeout=None, + use_vt=False, + output_loglevel="debug", + hide_output=False, + defaults=None, + context=None, + success_retcodes=None, + **kwargs +): + """ Download a script and execute it with specified arguments. source @@ -1095,34 +1115,32 @@ def script(name, .. versionadded:: 2019.2.0 - ''' + """ test_name = None if not isinstance(stateful, list): stateful = stateful is True - elif isinstance(stateful, list) and 'test_name' in stateful[0]: - test_name = stateful[0]['test_name'] - if __opts__['test'] and test_name: + elif isinstance(stateful, list) and "test_name" in stateful[0]: + test_name = stateful[0]["test_name"] + if __opts__["test"] and test_name: name = test_name - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Need the check for None here, if env is not provided then it falls back # to None and it is assumed that the environment is not being overridden. if env is not None and not isinstance(env, (list, dict)): - ret['comment'] = ('Invalidly-formatted \'env\' parameter. See ' - 'documentation.') + ret["comment"] = "Invalidly-formatted 'env' parameter. See " "documentation." return ret if context and not isinstance(context, dict): - ret['comment'] = ('Invalidly-formatted \'context\' parameter. Must ' - 'be formed as a dict.') + ret["comment"] = ( + "Invalidly-formatted 'context' parameter. Must " "be formed as a dict." + ) return ret if defaults and not isinstance(defaults, dict): - ret['comment'] = ('Invalidly-formatted \'defaults\' parameter. Must ' - 'be formed as a dict.') + ret["comment"] = ( + "Invalidly-formatted 'defaults' parameter. Must " "be formed as a dict." + ) return ret tmpctx = defaults if defaults else {} @@ -1130,26 +1148,30 @@ def script(name, tmpctx.update(context) cmd_kwargs = copy.deepcopy(kwargs) - cmd_kwargs.update({'runas': runas, - 'shell': shell or __grains__['shell'], - 'env': env, - 'onlyif': onlyif, - 'unless': unless, - 'cwd': cwd, - 'template': template, - 'umask': umask, - 'timeout': timeout, - 'output_loglevel': output_loglevel, - 'hide_output': hide_output, - 'use_vt': use_vt, - 'context': tmpctx, - 'saltenv': __env__, - 'success_retcodes': success_retcodes}) + cmd_kwargs.update( + { + "runas": runas, + "shell": shell or __grains__["shell"], + "env": env, + "onlyif": onlyif, + "unless": unless, + "cwd": cwd, + "template": template, + "umask": umask, + "timeout": timeout, + "output_loglevel": output_loglevel, + "hide_output": hide_output, + "use_vt": use_vt, + "context": tmpctx, + "saltenv": __env__, + "success_retcodes": success_retcodes, + } + ) run_check_cmd_kwargs = { - 'cwd': cwd, - 'runas': runas, - 'shell': shell or __grains__['shell'] + "cwd": cwd, + "runas": runas, + "shell": shell or __grains__["shell"], } # Change the source to be the name arg if it is not specified @@ -1157,65 +1179,64 @@ def script(name, source = name # If script args present split from name and define args - if not cmd_kwargs.get('args', None) and len(name.split()) > 1: - cmd_kwargs.update({'args': name.split(' ', 1)[1]}) + if not cmd_kwargs.get("args", None) and len(name.split()) > 1: + cmd_kwargs.update({"args": name.split(" ", 1)[1]}) - cret = mod_run_check( - run_check_cmd_kwargs, onlyif, unless, creates - ) + cret = mod_run_check(run_check_cmd_kwargs, onlyif, unless, creates) if isinstance(cret, dict): ret.update(cret) return ret - if __opts__['test'] and not test_name: - ret['result'] = None - ret['comment'] = 'Command \'{0}\' would have been ' \ - 'executed'.format(name) + if __opts__["test"] and not test_name: + ret["result"] = None + ret["comment"] = "Command '{0}' would have been " "executed".format(name) return _reinterpreted_state(ret) if stateful else ret if cwd and not os.path.isdir(cwd): - ret['comment'] = ( - 'Desired working directory "{0}" ' - 'is not available' - ).format(cwd) + ret["comment"] = ('Desired working directory "{0}" ' "is not available").format( + cwd + ) return ret # Wow, we passed the test, run this sucker! try: - cmd_all = __salt__['cmd.script'](source, python_shell=True, **cmd_kwargs) + cmd_all = __salt__["cmd.script"](source, python_shell=True, **cmd_kwargs) except (CommandExecutionError, SaltRenderError, IOError) as err: - ret['comment'] = six.text_type(err) + ret["comment"] = six.text_type(err) return ret - ret['changes'] = cmd_all - if kwargs.get('retcode', False): - ret['result'] = not bool(cmd_all) + ret["changes"] = cmd_all + if kwargs.get("retcode", False): + ret["result"] = not bool(cmd_all) else: - ret['result'] = not bool(cmd_all['retcode']) - if ret.get('changes', {}).get('cache_error'): - ret['comment'] = 'Unable to cache script {0} from saltenv ' \ - '\'{1}\''.format(source, __env__) + ret["result"] = not bool(cmd_all["retcode"]) + if ret.get("changes", {}).get("cache_error"): + ret["comment"] = "Unable to cache script {0} from saltenv " "'{1}'".format( + source, __env__ + ) else: - ret['comment'] = 'Command \'{0}\' run'.format(name) + ret["comment"] = "Command '{0}' run".format(name) if stateful: ret = _reinterpreted_state(ret) - if __opts__['test'] and cmd_all['retcode'] == 0 and ret['changes']: - ret['result'] = None + if __opts__["test"] and cmd_all["retcode"] == 0 and ret["changes"]: + ret["result"] = None return ret -def call(name, - func, - args=(), - kws=None, - onlyif=None, - unless=None, - creates=None, - output_loglevel='debug', - hide_output=False, - use_vt=False, - **kwargs): - ''' +def call( + name, + func, + args=(), + kws=None, + onlyif=None, + unless=None, + creates=None, + output_loglevel="debug", + hide_output=False, + use_vt=False, + **kwargs +): + """ Invoke a pre-defined Python function with arguments specified in the state declaration. This function is mainly used by the :mod:`salt.renderers.pydsl` renderer. @@ -1246,20 +1267,19 @@ def call(name, 'result': True if result is None else bool(result), 'comment': result if isinstance(result, six.string_types) else '' } - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - cmd_kwargs = {'cwd': kwargs.get('cwd'), - 'runas': kwargs.get('user'), - 'shell': kwargs.get('shell') or __grains__['shell'], - 'env': kwargs.get('env'), - 'use_vt': use_vt, - 'output_loglevel': output_loglevel, - 'hide_output': hide_output, - 'umask': kwargs.get('umask')} + cmd_kwargs = { + "cwd": kwargs.get("cwd"), + "runas": kwargs.get("user"), + "shell": kwargs.get("shell") or __grains__["shell"], + "env": kwargs.get("env"), + "use_vt": use_vt, + "output_loglevel": output_loglevel, + "hide_output": hide_output, + "umask": kwargs.get("umask"), + } cret = mod_run_check(cmd_kwargs, onlyif, unless, creates) if isinstance(cret, dict): @@ -1274,34 +1294,33 @@ def call(name, return ret else: # result must be JSON serializable else we get an error - ret['changes'] = {'retval': result} - ret['result'] = True if result is None else bool(result) + ret["changes"] = {"retval": result} + ret["result"] = True if result is None else bool(result) if isinstance(result, six.string_types): - ret['comment'] = result + ret["comment"] = result return ret -def wait_call(name, - func, - args=(), - kws=None, - onlyif=None, - unless=None, - creates=None, - stateful=False, - use_vt=False, - output_loglevel='debug', - hide_output=False, - **kwargs): +def wait_call( + name, + func, + args=(), + kws=None, + onlyif=None, + unless=None, + creates=None, + stateful=False, + use_vt=False, + output_loglevel="debug", + hide_output=False, + **kwargs +): # Ignoring our arguments is intentional. - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + return {"name": name, "changes": {}, "result": True, "comment": ""} def mod_watch(name, **kwargs): - ''' + """ Execute a cmd function based on a watch call .. note:: @@ -1309,33 +1328,37 @@ def mod_watch(name, **kwargs): :ref:`requisite <requisites>`. It should not be called directly. Parameters for this function should be set by the state being triggered. - ''' - if kwargs['sfun'] in ('wait', 'run', 'watch'): - if kwargs.get('stateful'): - kwargs.pop('stateful') + """ + if kwargs["sfun"] in ("wait", "run", "watch"): + if kwargs.get("stateful"): + kwargs.pop("stateful") return _reinterpreted_state(run(name, **kwargs)) return run(name, **kwargs) - elif kwargs['sfun'] == 'wait_script' or kwargs['sfun'] == 'script': - if kwargs.get('stateful'): - kwargs.pop('stateful') + elif kwargs["sfun"] == "wait_script" or kwargs["sfun"] == "script": + if kwargs.get("stateful"): + kwargs.pop("stateful") return _reinterpreted_state(script(name, **kwargs)) return script(name, **kwargs) - elif kwargs['sfun'] == 'wait_call' or kwargs['sfun'] == 'call': - if kwargs.get('func'): - func = kwargs.pop('func') + elif kwargs["sfun"] == "wait_call" or kwargs["sfun"] == "call": + if kwargs.get("func"): + func = kwargs.pop("func") return call(name, func, **kwargs) else: - return {'name': name, - 'changes': {}, - 'comment': ( - 'cmd.{0[sfun]} needs a named parameter func' - ).format(kwargs), - 'result': False} + return { + "name": name, + "changes": {}, + "comment": ("cmd.{0[sfun]} needs a named parameter func").format( + kwargs + ), + "result": False, + } - return {'name': name, - 'changes': {}, - 'comment': 'cmd.{0[sfun]} does not work with the watch requisite, ' - 'please use cmd.wait or cmd.wait_script'.format(kwargs), - 'result': False} + return { + "name": name, + "changes": {}, + "comment": "cmd.{0[sfun]} does not work with the watch requisite, " + "please use cmd.wait or cmd.wait_script".format(kwargs), + "result": False, + } diff --git a/salt/states/composer.py b/salt/states/composer.py index 9496b16fe1b..96607dead4f 100644 --- a/salt/states/composer.py +++ b/salt/states/composer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of Composer Packages ================================= @@ -36,7 +36,7 @@ the location of composer in the state. - composer: /path/to/composer.phar - php: /usr/local/bin/php - no_dev: true -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -44,27 +44,31 @@ from salt.exceptions import SaltException def __virtual__(): - ''' + """ Only load if the composer module is available in __salt__ - ''' - return 'composer.install' in __salt__ + """ + if "composer.install" in __salt__: + return True + return (False, "composer module could not be loaded") -def installed(name, - composer=None, - php=None, - user=None, - prefer_source=None, - prefer_dist=None, - no_scripts=None, - no_plugins=None, - optimize=None, - no_dev=None, - quiet=False, - composer_home='/root', - always_check=True, - env=None): - ''' +def installed( + name, + composer=None, + php=None, + user=None, + prefer_source=None, + prefer_dist=None, + no_scripts=None, + no_plugins=None, + optimize=None, + no_dev=None, + quiet=False, + composer_home="/root", + always_check=True, + env=None, +): + """ Verify that the correct versions of composer dependencies are present. name @@ -115,37 +119,39 @@ def installed(name, env A list of environment variables to be set prior to execution. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - did_install = __salt__['composer.did_composer_install'](name) + did_install = __salt__["composer.did_composer_install"](name) # Check if composer.lock exists, if so we already ran `composer install` # and we don't need to do it again if always_check is False and did_install: - ret['result'] = True - ret['comment'] = 'Composer already installed this directory' + ret["result"] = True + ret["comment"] = "Composer already installed this directory" return ret # The state of the system does need to be changed. Check if we're running # in ``test=true`` mode. - if __opts__['test'] is True: + if __opts__["test"] is True: if did_install is True: install_status = "" else: install_status = "not " - ret['comment'] = 'The state of "{0}" will be changed.'.format(name) - ret['changes'] = { - 'old': 'composer install has {0}been run in {1}'.format(install_status, name), - 'new': 'composer install will be run in {0}'.format(name) + ret["comment"] = 'The state of "{0}" will be changed.'.format(name) + ret["changes"] = { + "old": "composer install has {0}been run in {1}".format( + install_status, name + ), + "new": "composer install will be run in {0}".format(name), } - ret['result'] = None + ret["result"] = None return ret try: - call = __salt__['composer.install']( + call = __salt__["composer.install"]( name, composer=composer, php=php, @@ -158,44 +164,45 @@ def installed(name, no_dev=no_dev, quiet=quiet, composer_home=composer_home, - env=env + env=env, ) except (SaltException) as err: - ret['result'] = False - ret['comment'] = 'Error executing composer in \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error executing composer in '{0}': {1}".format(name, err) return ret # If composer retcode != 0 then an exception was thrown and we dealt with it. # Any other case is success, regardless of what composer decides to output. - ret['result'] = True + ret["result"] = True if quiet is True: - ret['comment'] = 'Composer install completed successfully, output silenced by quiet flag' + ret[ + "comment" + ] = "Composer install completed successfully, output silenced by quiet flag" else: - ret['comment'] = 'Composer install completed successfully' - ret['changes'] = { - 'stderr': call['stderr'], - 'stdout': call['stdout'] - } + ret["comment"] = "Composer install completed successfully" + ret["changes"] = {"stderr": call["stderr"], "stdout": call["stdout"]} return ret -def update(name, - composer=None, - php=None, - user=None, - prefer_source=None, - prefer_dist=None, - no_scripts=None, - no_plugins=None, - optimize=None, - no_dev=None, - quiet=False, - composer_home='/root', - env=None): - ''' +def update( + name, + composer=None, + php=None, + user=None, + prefer_source=None, + prefer_dist=None, + no_scripts=None, + no_plugins=None, + optimize=None, + no_dev=None, + quiet=False, + composer_home="/root", + env=None, +): + """ Composer update the directory to ensure we have the latest versions of all project dependencies. @@ -242,11 +249,11 @@ def update(name, env A list of environment variables to be set prior to execution. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} # Check if composer.lock exists, if so we already ran `composer install` - is_installed = __salt__['composer.did_composer_install'](name) + is_installed = __salt__["composer.did_composer_install"](name) if is_installed: old_status = "composer install has not yet been run in {0}".format(name) else: @@ -254,17 +261,17 @@ def update(name, # The state of the system does need to be changed. Check if we're running # in ``test=true`` mode. - if __opts__['test'] is True: - ret['comment'] = 'The state of "{0}" will be changed.'.format(name) - ret['changes'] = { - 'old': old_status, - 'new': 'composer install/update will be run in {0}'.format(name) + if __opts__["test"] is True: + ret["comment"] = 'The state of "{0}" will be changed.'.format(name) + ret["changes"] = { + "old": old_status, + "new": "composer install/update will be run in {0}".format(name), } - ret['result'] = None + ret["result"] = None return ret try: - call = __salt__['composer.update']( + call = __salt__["composer.update"]( name, composer=composer, php=php, @@ -277,25 +284,24 @@ def update(name, no_dev=no_dev, quiet=quiet, composer_home=composer_home, - env=env + env=env, ) except (SaltException) as err: - ret['result'] = False - ret['comment'] = 'Error executing composer in \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error executing composer in '{0}': {1}".format(name, err) return ret # If composer retcode != 0 then an exception was thrown and we dealt with it. # Any other case is success, regardless of what composer decides to output. - ret['result'] = True + ret["result"] = True if quiet is True: - ret['comment'] = 'Composer update completed successfully, output silenced by quiet flag' + ret[ + "comment" + ] = "Composer update completed successfully, output silenced by quiet flag" else: - ret['comment'] = 'Composer update completed successfully' - ret['changes'] = { - 'stderr': call['stderr'], - 'stdout': call['stdout'] - } + ret["comment"] = "Composer update completed successfully" + ret["changes"] = {"stderr": call["stderr"], "stdout": call["stdout"]} return ret diff --git a/salt/states/cron.py b/salt/states/cron.py index 2cdf4d1d036..8851c3de410 100644 --- a/salt/states/cron.py +++ b/salt/states/cron.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of cron, the Unix command scheduler ============================================== @@ -136,42 +136,40 @@ The script will be executed every reboot if cron daemon support this option. This counter part definition will ensure than a job with a special keyword is not set. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os # Import salt libs import salt.utils.files -from salt.modules.cron import ( - _needs_change, - _cron_matched -) from salt.ext import six +from salt.modules.cron import _cron_matched, _needs_change def __virtual__(): - if 'cron.list_tab' in __salt__: + if "cron.list_tab" in __salt__: return True - else: - return (False, 'cron module could not be loaded') + return (False, "cron module could not be loaded") -def _check_cron(user, - cmd, - minute=None, - hour=None, - daymonth=None, - month=None, - dayweek=None, - comment=None, - commented=None, - identifier=None, - special=None): - ''' +def _check_cron( + user, + cmd, + minute=None, + hour=None, + daymonth=None, + month=None, + dayweek=None, + comment=None, + commented=None, + identifier=None, + special=None, +): + """ Return the changes - ''' + """ if minute is not None: minute = six.text_type(minute).lower() if hour is not None: @@ -188,84 +186,100 @@ def _check_cron(user, commented = commented is True if cmd is not None: cmd = six.text_type(cmd) - lst = __salt__['cron.list_tab'](user) + lst = __salt__["cron.list_tab"](user) if special is None: - for cron in lst['crons']: + for cron in lst["crons"]: if _cron_matched(cron, cmd, identifier): - if any([_needs_change(x, y) for x, y in - ((cron['minute'], minute), (cron['hour'], hour), - (cron['daymonth'], daymonth), (cron['month'], month), - (cron['dayweek'], dayweek), (cron['identifier'], identifier), - (cron['cmd'], cmd), (cron['comment'], comment), - (cron['commented'], commented))]): - return 'update' - return 'present' + if any( + [ + _needs_change(x, y) + for x, y in ( + (cron["minute"], minute), + (cron["hour"], hour), + (cron["daymonth"], daymonth), + (cron["month"], month), + (cron["dayweek"], dayweek), + (cron["identifier"], identifier), + (cron["cmd"], cmd), + (cron["comment"], comment), + (cron["commented"], commented), + ) + ] + ): + return "update" + return "present" else: - for cron in lst['special']: + for cron in lst["special"]: if _cron_matched(cron, cmd, identifier): - if any([_needs_change(x, y) for x, y in - ((cron['spec'], special), - (cron['identifier'], identifier), - (cron['cmd'], cmd), - (cron['comment'], comment), - (cron['commented'], commented))]): - return 'update' - return 'present' - return 'absent' + if any( + [ + _needs_change(x, y) + for x, y in ( + (cron["spec"], special), + (cron["identifier"], identifier), + (cron["cmd"], cmd), + (cron["comment"], comment), + (cron["commented"], commented), + ) + ] + ): + return "update" + return "present" + return "absent" -def _check_cron_env(user, - name, - value=None): - ''' +def _check_cron_env(user, name, value=None): + """ Return the environment changes - ''' + """ if value is None: value = "" # Matching value set in salt.modules.cron._render_tab - lst = __salt__['cron.list_tab'](user) - for env in lst['env']: - if name == env['name']: - if value != env['value']: - return 'update' - return 'present' - return 'absent' + lst = __salt__["cron.list_tab"](user) + for env in lst["env"]: + if name == env["name"]: + if value != env["value"]: + return "update" + return "present" + return "absent" def _get_cron_info(): - ''' + """ Returns the proper group owner and path to the cron directory - ''' - owner = 'root' - if __grains__['os'] == 'FreeBSD': - group = 'wheel' - crontab_dir = '/var/cron/tabs' - elif __grains__['os'] == 'OpenBSD': - group = 'crontab' - crontab_dir = '/var/cron/tabs' - elif __grains__['os_family'] == 'Solaris': - group = 'root' - crontab_dir = '/var/spool/cron/crontabs' - elif __grains__['os'] == 'MacOS': - group = 'wheel' - crontab_dir = '/usr/lib/cron/tabs' + """ + owner = "root" + if __grains__["os"] == "FreeBSD": + group = "wheel" + crontab_dir = "/var/cron/tabs" + elif __grains__["os"] == "OpenBSD": + group = "crontab" + crontab_dir = "/var/cron/tabs" + elif __grains__["os_family"] == "Solaris": + group = "root" + crontab_dir = "/var/spool/cron/crontabs" + elif __grains__["os"] == "MacOS": + group = "wheel" + crontab_dir = "/usr/lib/cron/tabs" else: - group = 'root' - crontab_dir = '/var/spool/cron' + group = "root" + crontab_dir = "/var/spool/cron" return owner, group, crontab_dir -def present(name, - user='root', - minute='*', - hour='*', - daymonth='*', - month='*', - dayweek='*', - comment=None, - commented=False, - identifier=False, - special=None): - ''' +def present( + name, + user="root", + minute="*", + hour="*", + daymonth="*", + month="*", + dayweek="*", + comment=None, + commented=False, + identifier=False, + special=None, +): + """ Verifies that the specified cron job is present for the specified user. It is recommended to use `identifier`. Otherwise the cron job is installed twice if you change the name. @@ -316,79 +330,79 @@ def present(name, Quotes must be used, otherwise PyYAML will strip the '@' sign. .. versionadded:: 2016.3.0 - ''' + """ name = name.strip() if identifier is False: identifier = name - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} - if __opts__['test']: - status = _check_cron(user, - cmd=name, - minute=minute, - hour=hour, - daymonth=daymonth, - month=month, - dayweek=dayweek, - comment=comment, - commented=commented, - identifier=identifier, - special=special) - ret['result'] = None - if status == 'absent': - ret['comment'] = 'Cron {0} is set to be added'.format(name) - elif status == 'present': - ret['result'] = True - ret['comment'] = 'Cron {0} already present'.format(name) - elif status == 'update': - ret['comment'] = 'Cron {0} is set to be updated'.format(name) + ret = {"changes": {}, "comment": "", "name": name, "result": True} + if __opts__["test"]: + status = _check_cron( + user, + cmd=name, + minute=minute, + hour=hour, + daymonth=daymonth, + month=month, + dayweek=dayweek, + comment=comment, + commented=commented, + identifier=identifier, + special=special, + ) + ret["result"] = None + if status == "absent": + ret["comment"] = "Cron {0} is set to be added".format(name) + elif status == "present": + ret["result"] = True + ret["comment"] = "Cron {0} already present".format(name) + elif status == "update": + ret["comment"] = "Cron {0} is set to be updated".format(name) return ret if special is None: - data = __salt__['cron.set_job'](user=user, - minute=minute, - hour=hour, - daymonth=daymonth, - month=month, - dayweek=dayweek, - cmd=name, - comment=comment, - commented=commented, - identifier=identifier) + data = __salt__["cron.set_job"]( + user=user, + minute=minute, + hour=hour, + daymonth=daymonth, + month=month, + dayweek=dayweek, + cmd=name, + comment=comment, + commented=commented, + identifier=identifier, + ) else: - data = __salt__['cron.set_special'](user=user, - special=special, - cmd=name, - comment=comment, - commented=commented, - identifier=identifier) - if data == 'present': - ret['comment'] = 'Cron {0} already present'.format(name) + data = __salt__["cron.set_special"]( + user=user, + special=special, + cmd=name, + comment=comment, + commented=commented, + identifier=identifier, + ) + if data == "present": + ret["comment"] = "Cron {0} already present".format(name) return ret - if data == 'new': - ret['comment'] = 'Cron {0} added to {1}\'s crontab'.format(name, user) - ret['changes'] = {user: name} + if data == "new": + ret["comment"] = "Cron {0} added to {1}'s crontab".format(name, user) + ret["changes"] = {user: name} return ret - if data == 'updated': - ret['comment'] = 'Cron {0} updated'.format(name) - ret['changes'] = {user: name} + if data == "updated": + ret["comment"] = "Cron {0} updated".format(name) + ret["changes"] = {user: name} return ret - ret['comment'] = ('Cron {0} for user {1} failed to commit with error \n{2}' - .format(name, user, data)) - ret['result'] = False + ret["comment"] = "Cron {0} for user {1} failed to commit with error \n{2}".format( + name, user, data + ) + ret["result"] = False return ret -def absent(name, - user='root', - identifier=False, - special=None, - **kwargs): - ''' +def absent(name, user="root", identifier=False, special=None, **kwargs): + """ Verifies that the specified cron job is absent for the specified user; only the name is matched when removing a cron job. @@ -406,7 +420,7 @@ def absent(name, special The special keyword used in the job (eg. @reboot, @hourly...). Quotes must be used, otherwise PyYAML will strip the '@' sign. - ''' + """ # NOTE: The keyword arguments in **kwargs are ignored in this state, but # cannot be removed from the function definition, otherwise the use # of unsupported arguments will result in a traceback. @@ -414,51 +428,52 @@ def absent(name, name = name.strip() if identifier is False: identifier = name - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - if __opts__['test']: + if __opts__["test"]: status = _check_cron(user, name, identifier=identifier) - ret['result'] = None - if status == 'absent': - ret['result'] = True - ret['comment'] = 'Cron {0} is absent'.format(name) - elif status == 'present' or status == 'update': - ret['comment'] = 'Cron {0} is set to be removed'.format(name) + ret["result"] = None + if status == "absent": + ret["result"] = True + ret["comment"] = "Cron {0} is absent".format(name) + elif status == "present" or status == "update": + ret["comment"] = "Cron {0} is set to be removed".format(name) return ret if special is None: - data = __salt__['cron.rm_job'](user, name, identifier=identifier) + data = __salt__["cron.rm_job"](user, name, identifier=identifier) else: - data = __salt__['cron.rm_special'](user, name, special=special, identifier=identifier) + data = __salt__["cron.rm_special"]( + user, name, special=special, identifier=identifier + ) - if data == 'absent': - ret['comment'] = "Cron {0} already absent".format(name) + if data == "absent": + ret["comment"] = "Cron {0} already absent".format(name) return ret - if data == 'removed': - ret['comment'] = ("Cron {0} removed from {1}'s crontab" - .format(name, user)) - ret['changes'] = {user: name} + if data == "removed": + ret["comment"] = "Cron {0} removed from {1}'s crontab".format(name, user) + ret["changes"] = {user: name} return ret - ret['comment'] = ("Cron {0} for user {1} failed to commit with error {2}" - .format(name, user, data)) - ret['result'] = False + ret["comment"] = "Cron {0} for user {1} failed to commit with error {2}".format( + name, user, data + ) + ret["result"] = False return ret -def file(name, - source_hash='', - source_hash_name=None, - user='root', - template=None, - context=None, - replace=True, - defaults=None, - backup='', - **kwargs): - ''' +def file( + name, + source_hash="", + source_hash_name=None, + user="root", + template=None, + context=None, + replace=True, + defaults=None, + backup="", + **kwargs +): + """ Provides file.managed-like functionality (templating, etc.) for a pre-made crontab file, to be assigned to a given user. @@ -538,68 +553,67 @@ def file(name, backup Overrides the default backup mode for the user's crontab. - ''' + """ # Initial set up - mode = '0600' + mode = "0600" try: - group = __salt__['user.info'](user)['groups'][0] + group = __salt__["user.info"](user)["groups"][0] except Exception: # pylint: disable=broad-except - ret = {'changes': {}, - 'comment': "Could not identify group for user {0}".format(user), - 'name': name, - 'result': False} + ret = { + "changes": {}, + "comment": "Could not identify group for user {0}".format(user), + "name": name, + "result": False, + } return ret cron_path = salt.utils.files.mkstemp() - with salt.utils.files.fopen(cron_path, 'w+') as fp_: - raw_cron = __salt__['cron.raw_cron'](user) - if not raw_cron.endswith('\n'): + with salt.utils.files.fopen(cron_path, "w+") as fp_: + raw_cron = __salt__["cron.raw_cron"](user) + if not raw_cron.endswith("\n"): raw_cron = "{0}\n".format(raw_cron) fp_.write(salt.utils.stringutils.to_str(raw_cron)) - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + ret = {"changes": {}, "comment": "", "name": name, "result": True} # Avoid variable naming confusion in below module calls, since ID # declaration for this state will be a source URI. source = name if not replace and os.stat(cron_path).st_size > 0: - ret['comment'] = 'User {0} already has a crontab. No changes ' \ - 'made'.format(user) + ret["comment"] = "User {0} already has a crontab. No changes " "made".format( + user + ) os.unlink(cron_path) return ret - if __opts__['test']: - fcm = __salt__['file.check_managed'](name=cron_path, - source=source, - source_hash=source_hash, - source_hash_name=source_hash_name, - user=user, - group=group, - mode=mode, - attrs=[], # no special attrs for cron - template=template, - context=context, - defaults=defaults, - saltenv=__env__, - **kwargs - ) - ret['result'], ret['comment'] = fcm + if __opts__["test"]: + fcm = __salt__["file.check_managed"]( + name=cron_path, + source=source, + source_hash=source_hash, + source_hash_name=source_hash_name, + user=user, + group=group, + mode=mode, + attrs=[], # no special attrs for cron + template=template, + context=context, + defaults=defaults, + saltenv=__env__, + **kwargs + ) + ret["result"], ret["comment"] = fcm os.unlink(cron_path) return ret # If the source is a list then find which file exists - source, source_hash = __salt__['file.source_list'](source, - source_hash, - __env__) + source, source_hash = __salt__["file.source_list"](source, source_hash, __env__) # Gather the source file from the server try: - sfn, source_sum, comment = __salt__['file.get_managed']( + sfn, source_sum, comment = __salt__["file.get_managed"]( name=cron_path, template=template, source=source, @@ -612,23 +626,23 @@ def file(name, saltenv=__env__, context=context, defaults=defaults, - skip_verify=False, # skip_verify + skip_verify=False, # skip_verify **kwargs ) except Exception as exc: # pylint: disable=broad-except - ret['result'] = False - ret['changes'] = {} - ret['comment'] = 'Unable to manage file: {0}'.format(exc) + ret["result"] = False + ret["changes"] = {} + ret["comment"] = "Unable to manage file: {0}".format(exc) return ret if comment: - ret['comment'] = comment - ret['result'] = False + ret["comment"] = comment + ret["result"] = False os.unlink(cron_path) return ret try: - ret = __salt__['file.manage_file']( + ret = __salt__["file.manage_file"]( name=cron_path, sfn=sfn, ret=ret, @@ -639,40 +653,39 @@ def file(name, mode=mode, attrs=[], saltenv=__env__, - backup=backup + backup=backup, ) except Exception as exc: # pylint: disable=broad-except - ret['result'] = False - ret['changes'] = {} - ret['comment'] = 'Unable to manage file: {0}'.format(exc) + ret["result"] = False + ret["changes"] = {} + ret["comment"] = "Unable to manage file: {0}".format(exc) return ret cron_ret = None - if "diff" in ret['changes']: - cron_ret = __salt__['cron.write_cron_file_verbose'](user, cron_path) + if "diff" in ret["changes"]: + cron_ret = __salt__["cron.write_cron_file_verbose"](user, cron_path) # Check cmd return code and show success or failure - if cron_ret['retcode'] == 0: - ret['comment'] = 'Crontab for user {0} was updated'.format(user) - ret['result'] = True - ret['changes'] = ret['changes'] + if cron_ret["retcode"] == 0: + ret["comment"] = "Crontab for user {0} was updated".format(user) + ret["result"] = True + ret["changes"] = ret["changes"] else: - ret['comment'] = 'Unable to update user {0} crontab {1}.' \ - ' Error: {2}'.format(user, cron_path, cron_ret['stderr']) - ret['result'] = False - ret['changes'] = {} - elif ret['result']: - ret['comment'] = 'Crontab for user {0} is in the correct ' \ - 'state'.format(user) - ret['changes'] = {} + ret["comment"] = ( + "Unable to update user {0} crontab {1}." + " Error: {2}".format(user, cron_path, cron_ret["stderr"]) + ) + ret["result"] = False + ret["changes"] = {} + elif ret["result"]: + ret["comment"] = "Crontab for user {0} is in the correct " "state".format(user) + ret["changes"] = {} os.unlink(cron_path) return ret -def env_present(name, - value=None, - user='root'): - ''' +def env_present(name, value=None, user="root"): + """ Verifies that the specified environment variable is present in the crontab for the specified user. @@ -685,46 +698,45 @@ def env_present(name, value The value to set for the given environment variable - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} - if __opts__['test']: + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} + if __opts__["test"]: status = _check_cron_env(user, name, value=value) - ret['result'] = None - if status == 'absent': - ret['comment'] = 'Cron env {0} is set to be added'.format(name) - elif status == 'present': - ret['result'] = True - ret['comment'] = 'Cron env {0} already present'.format(name) - elif status == 'update': - ret['comment'] = 'Cron env {0} is set to be updated'.format(name) + ret["result"] = None + if status == "absent": + ret["comment"] = "Cron env {0} is set to be added".format(name) + elif status == "present": + ret["result"] = True + ret["comment"] = "Cron env {0} already present".format(name) + elif status == "update": + ret["comment"] = "Cron env {0} is set to be updated".format(name) return ret - data = __salt__['cron.set_env'](user, name, value=value) - if data == 'present': - ret['comment'] = 'Cron env {0} already present'.format(name) + data = __salt__["cron.set_env"](user, name, value=value) + if data == "present": + ret["comment"] = "Cron env {0} already present".format(name) return ret - if data == 'new': - ret['comment'] = 'Cron env {0} added to {1}\'s crontab'.format(name, user) - ret['changes'] = {user: name} + if data == "new": + ret["comment"] = "Cron env {0} added to {1}'s crontab".format(name, user) + ret["changes"] = {user: name} return ret - if data == 'updated': - ret['comment'] = 'Cron env {0} updated'.format(name) - ret['changes'] = {user: name} + if data == "updated": + ret["comment"] = "Cron env {0} updated".format(name) + ret["changes"] = {user: name} return ret - ret['comment'] = ('Cron env {0} for user {1} failed to commit with error \n{2}' - .format(name, user, data)) - ret['result'] = False + ret[ + "comment" + ] = "Cron env {0} for user {1} failed to commit with error \n{2}".format( + name, user, data + ) + ret["result"] = False return ret -def env_absent(name, - user='root'): - ''' +def env_absent(name, user="root"): + """ Verifies that the specified environment variable is absent from the crontab for the specified user @@ -734,34 +746,31 @@ def env_absent(name, user The name of the user whose crontab needs to be modified, defaults to the root user - ''' + """ name = name.strip() - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - if __opts__['test']: + if __opts__["test"]: status = _check_cron_env(user, name) - ret['result'] = None - if status == 'absent': - ret['result'] = True - ret['comment'] = 'Cron env {0} is absent'.format(name) - elif status == 'present' or status == 'update': - ret['comment'] = 'Cron env {0} is set to be removed'.format(name) + ret["result"] = None + if status == "absent": + ret["result"] = True + ret["comment"] = "Cron env {0} is absent".format(name) + elif status == "present" or status == "update": + ret["comment"] = "Cron env {0} is set to be removed".format(name) return ret - data = __salt__['cron.rm_env'](user, name) - if data == 'absent': - ret['comment'] = "Cron env {0} already absent".format(name) + data = __salt__["cron.rm_env"](user, name) + if data == "absent": + ret["comment"] = "Cron env {0} already absent".format(name) return ret - if data == 'removed': - ret['comment'] = ("Cron env {0} removed from {1}'s crontab" - .format(name, user)) - ret['changes'] = {user: name} + if data == "removed": + ret["comment"] = "Cron env {0} removed from {1}'s crontab".format(name, user) + ret["changes"] = {user: name} return ret - ret['comment'] = ("Cron env {0} for user {1} failed to commit with error {2}" - .format(name, user, data)) - ret['result'] = False + ret["comment"] = "Cron env {0} for user {1} failed to commit with error {2}".format( + name, user, data + ) + ret["result"] = False return ret diff --git a/salt/states/cryptdev.py b/salt/states/cryptdev.py index 92dc08ac248..a4961f3082c 100644 --- a/salt/states/cryptdev.py +++ b/salt/states/cryptdev.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Opening of Encrypted Devices -======================= +============================ Ensure that an encrypted device is mapped with the `mapped` function: @@ -27,22 +27,25 @@ Ensure that an encrypted device is mapped with the `mapped` function: - config: /etc/alternate-crypttab .. versionadded:: 2018.3.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + log = logging.getLogger(__name__) -def mapped(name, - device, - keyfile=None, - opts=None, - config='/etc/crypttab', - persist=True, - immediate=False, - match_on='name'): - ''' +def mapped( + name, + device, + keyfile=None, + opts=None, + config="/etc/crypttab", + persist=True, + immediate=False, + match_on="name", +): + """ Verify that a device is mapped name @@ -78,67 +81,71 @@ def mapped(name, Default is ``name``, meaning that the line is matched only by the name parameter. If the desired configuration requires two devices mapped to the same name, supply a list of parameters to match on. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # If neither option is set, we've been asked to do nothing. if not immediate and not persist: - ret['result'] = False - ret['comment'] = 'Either persist or immediate must be set, otherwise this state does nothing' + ret["result"] = False + ret[ + "comment" + ] = "Either persist or immediate must be set, otherwise this state does nothing" return ret - if immediate and (keyfile is None or keyfile == 'none' or keyfile == '-'): - ret['result'] = False - ret['changes']['cryptsetup'] = 'Device cannot be mapped immediately without a keyfile' + if immediate and (keyfile is None or keyfile == "none" or keyfile == "-"): + ret["result"] = False + ret["changes"][ + "cryptsetup" + ] = "Device cannot be mapped immediately without a keyfile" elif immediate: # Get the active crypt mounts. If ours is listed already, no action is necessary. - active = __salt__['cryptdev.active']() + active = __salt__["cryptdev.active"]() if name not in active.keys(): # Open the map using cryptsetup. This does not pass any options. if opts: - log.warning('Ignore cryptdev configuration when mapping immediately') + log.warning("Ignore cryptdev configuration when mapping immediately") - if __opts__['test']: - ret['result'] = None - ret['commment'] = 'Device would be mapped immediately' + if __opts__["test"]: + ret["result"] = None + ret["commment"] = "Device would be mapped immediately" else: - cryptsetup_result = __salt__['cryptdev.open'](name, device, keyfile) + cryptsetup_result = __salt__["cryptdev.open"](name, device, keyfile) if cryptsetup_result: - ret['changes']['cryptsetup'] = 'Device mapped using cryptsetup' + ret["changes"]["cryptsetup"] = "Device mapped using cryptsetup" else: - ret['changes']['cryptsetup'] = 'Device failed to map using cryptsetup' - ret['result'] = False + ret["changes"][ + "cryptsetup" + ] = "Device failed to map using cryptsetup" + ret["result"] = False - if persist and not __opts__['test']: - crypttab_result = __salt__['cryptdev.set_crypttab'](name, - device, - password=keyfile, - options=opts, - config=config, - match_on=match_on) + if persist and not __opts__["test"]: + crypttab_result = __salt__["cryptdev.set_crypttab"]( + name, + device, + password=keyfile, + options=opts, + config=config, + match_on=match_on, + ) if crypttab_result: - if crypttab_result == 'new': - ret['changes']['crypttab'] = 'Entry added in {0}'.format(config) + if crypttab_result == "new": + ret["changes"]["crypttab"] = "Entry added in {0}".format(config) - if crypttab_result == 'change': - ret['changes']['crypttab'] = 'Existing entry in {0} changed'.format(config) + if crypttab_result == "change": + ret["changes"]["crypttab"] = "Existing entry in {0} changed".format( + config + ) else: - ret['changes']['crypttab'] = 'Unable to set entry in {0}'.format(config) - ret['result'] = False + ret["changes"]["crypttab"] = "Unable to set entry in {0}".format(config) + ret["result"] = False return ret -def unmapped(name, - config='/etc/crypttab', - persist=True, - immediate=False): - ''' +def unmapped(name, config="/etc/crypttab", persist=True, immediate=False): + """ Ensure that a device is unmapped name @@ -153,36 +160,35 @@ def unmapped(name, immediate Set if the device should be unmapped immediately. Default is ``False``. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if immediate: # Get the active crypt mounts. If ours is not listed already, no action is necessary. - active = __salt__['cryptdev.active']() + active = __salt__["cryptdev.active"]() if name in active.keys(): # Close the map using cryptsetup. - if __opts__['test']: - ret['result'] = None - ret['commment'] = 'Device would be unmapped immediately' + if __opts__["test"]: + ret["result"] = None + ret["commment"] = "Device would be unmapped immediately" else: - cryptsetup_result = __salt__['cryptdev.close'](name) + cryptsetup_result = __salt__["cryptdev.close"](name) if cryptsetup_result: - ret['changes']['cryptsetup'] = 'Device unmapped using cryptsetup' + ret["changes"]["cryptsetup"] = "Device unmapped using cryptsetup" else: - ret['changes']['cryptsetup'] = 'Device failed to unmap using cryptsetup' - ret['result'] = False + ret["changes"][ + "cryptsetup" + ] = "Device failed to unmap using cryptsetup" + ret["result"] = False - if persist and not __opts__['test']: - crypttab_result = __salt__['cryptdev.rm_crypttab'](name, config=config) + if persist and not __opts__["test"]: + crypttab_result = __salt__["cryptdev.rm_crypttab"](name, config=config) if crypttab_result: - if crypttab_result == 'change': - ret['changes']['crypttab'] = 'Entry removed from {0}'.format(config) + if crypttab_result == "change": + ret["changes"]["crypttab"] = "Entry removed from {0}".format(config) else: - ret['changes']['crypttab'] = 'Unable to remove entry in {0}'.format(config) - ret['result'] = False + ret["changes"]["crypttab"] = "Unable to remove entry in {0}".format(config) + ret["result"] = False return ret diff --git a/salt/states/csf.py b/salt/states/csf.py index 9398c014337..7babe88a666 100644 --- a/salt/states/csf.py +++ b/salt/states/csf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" CSF Ip tables management ======================== @@ -13,9 +13,10 @@ CSF Ip tables management csf.rule_present: ip: 1.2.3.4 method: allow -''' # pylint: disable=W0105 +""" # pylint: disable=W0105 # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs @@ -25,20 +26,24 @@ log = logging.getLogger(__name__) def __virtual__(): - return 'csf' + if "csf.exists" in __salt__: + return "csf" + return (False, "csf module could not be loaded") -def rule_present(name, - method, - port=None, - proto='tcp', - direction='in', - port_origin='d', - ip_origin='s', - ttl=None, - comment='', - reload=False): - ''' +def rule_present( + name, + method, + port=None, + proto="tcp", + direction="in", + port_origin="d", + ip_origin="s", + ttl=None, + comment="", + reload=False, +): + """ Ensure iptable rule exists. name @@ -82,62 +87,70 @@ def rule_present(name, Reload the csf service after applying this rule. Default false. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Rule already exists.'} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Rule already exists.", + } ip = name # Check if rule is already present - exists = __salt__['csf.exists'](method=method, - ip=ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin, - ttl=ttl, - comment=comment) + exists = __salt__["csf.exists"]( + method=method, + ip=ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + ttl=ttl, + comment=comment, + ) if exists: return ret else: if ttl: - method = 'temp{0}'.format(method) - func = __salt__['csf.{0}'.format(method)] - rule = func(ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin, - ttl=ttl, - comment=comment) + method = "temp{0}".format(method) + func = __salt__["csf.{0}".format(method)] + rule = func( + ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + ttl=ttl, + comment=comment, + ) if rule: - comment = 'Rule has been added.' + comment = "Rule has been added." if reload: - if __salt__['csf.reload'](): - comment += ' Csf reloaded.' + if __salt__["csf.reload"](): + comment += " Csf reloaded." else: - comment += ' Unable to reload csf.' - ret['result'] = False - ret['comment'] = comment - ret['changes']['Rule'] = 'Created' + comment += " Unable to reload csf." + ret["result"] = False + ret["comment"] = comment + ret["changes"]["Rule"] = "Created" return ret -def rule_absent(name, - method, - port=None, - proto='tcp', - direction='in', - port_origin='d', - ip_origin='s', - ttl=None, - reload=False): - ''' +def rule_absent( + name, + method, + port=None, + proto="tcp", + direction="in", + port_origin="d", + ip_origin="s", + ttl=None, + reload=False, +): + """ Ensure iptable is not present. name @@ -176,49 +189,50 @@ def rule_absent(name, reload Reload the csf service after applying this rule. Default false. - ''' + """ ip = name - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Rule not present.'} + ret = {"name": name, "changes": {}, "result": True, "comment": "Rule not present."} - exists = __salt__['csf.exists'](method, - ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin, - ttl=ttl) + exists = __salt__["csf.exists"]( + method, + ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + ttl=ttl, + ) if not exists: return ret else: - rule = __salt__['csf.remove_rule'](method=method, - ip=ip, - port=port, - proto=proto, - direction=direction, - port_origin=port_origin, - ip_origin=ip_origin, - comment='', - ttl=ttl) + rule = __salt__["csf.remove_rule"]( + method=method, + ip=ip, + port=port, + proto=proto, + direction=direction, + port_origin=port_origin, + ip_origin=ip_origin, + comment="", + ttl=ttl, + ) if rule: - comment = 'Rule has been removed.' + comment = "Rule has been removed." if reload: - if __salt__['csf.reload'](): - comment += ' Csf reloaded.' + if __salt__["csf.reload"](): + comment += " Csf reloaded." else: - comment += 'Csf unable to be reloaded.' - ret['comment'] = comment - ret['changes']['Rule'] = 'Removed' + comment += "Csf unable to be reloaded." + ret["comment"] = comment + ret["changes"]["Rule"] = "Removed" return ret -def ports_open(name, ports, proto='tcp', direction='in'): - ''' +def ports_open(name, ports, proto="tcp", direction="in"): + """ Ensure ports are open for a protocol, in a direction. e.g. - proto='tcp', direction='in' would set the values for TCP_IN in the csf.conf file. @@ -234,39 +248,41 @@ def ports_open(name, ports, proto='tcp', direction='in'): Choose 'in', 'out', or both to indicate the port should be opened for inbound traffic, outbound traffic, or both. - ''' + """ ports = list(six.moves.map(six.text_type, ports)) diff = False - ret = {'name': ','.join(ports), - 'changes': {}, - 'result': True, - 'comment': 'Ports open.'} + ret = { + "name": ",".join(ports), + "changes": {}, + "result": True, + "comment": "Ports open.", + } - current_ports = __salt__['csf.get_ports'](proto=proto, direction=direction) + current_ports = __salt__["csf.get_ports"](proto=proto, direction=direction) direction = direction.upper() - directions = __salt__['csf.build_directions'](direction) + directions = __salt__["csf.build_directions"](direction) for direction in directions: - log.trace('current_ports[direction]: %s', current_ports[direction]) - log.trace('ports: %s', ports) + log.trace("current_ports[direction]: %s", current_ports[direction]) + log.trace("ports: %s", ports) if current_ports[direction] != ports: diff = True if diff: - result = __salt__['csf.allow_ports'](ports, proto=proto, direction=direction) - ret['changes']['Ports'] = 'Changed' - ret['comment'] = result + result = __salt__["csf.allow_ports"](ports, proto=proto, direction=direction) + ret["changes"]["Ports"] = "Changed" + ret["comment"] = result return ret def nics_skip(name, nics, ipv6): - ''' + """ Alias for :mod:`csf.nics_skipped <salt.states.csf.nics_skipped>` - ''' + """ return nics_skipped(name, nics=nics, ipv6=ipv6) def nics_skipped(name, nics, ipv6=False): - ''' + """ name Meaningless arg, but required for state. @@ -276,79 +292,85 @@ def nics_skipped(name, nics, ipv6=False): ipv6 Boolean. Set to true if you want to skip the ipv6 interface. Default false (ipv4). - ''' - ret = {'name': ','.join(nics), - 'changes': {}, - 'result': True, - 'comment': 'NICs skipped.'} + """ + ret = { + "name": ",".join(nics), + "changes": {}, + "result": True, + "comment": "NICs skipped.", + } - current_skipped_nics = __salt__['csf.get_skipped_nics'](ipv6=ipv6) + current_skipped_nics = __salt__["csf.get_skipped_nics"](ipv6=ipv6) if nics == current_skipped_nics: return ret - result = __salt__['csf.skip_nics'](nics, ipv6=ipv6) - ret['changes']['Skipped NICs'] = 'Changed' + result = __salt__["csf.skip_nics"](nics, ipv6=ipv6) + ret["changes"]["Skipped NICs"] = "Changed" return ret def testing_on(name, reload=False): - ''' + """ Ensure testing mode is enabled in csf. reload Reload CSF after changing the testing status. Default false. - ''' + """ - ret = {'name': 'testing mode', - 'changes': {}, - 'result': True, - 'comment': 'Testing mode already ON.'} + ret = { + "name": "testing mode", + "changes": {}, + "result": True, + "comment": "Testing mode already ON.", + } result = {} - testing = __salt__['csf.get_testing_status']() + testing = __salt__["csf.get_testing_status"]() if int(testing) == 1: return ret - enable = __salt__['csf.enable_testing_mode']() + enable = __salt__["csf.enable_testing_mode"]() if enable: - comment = 'Csf testing mode enabled' + comment = "Csf testing mode enabled" if reload: - if __salt__['csf.reload'](): - comment += ' and csf reloaded.' - ret['changes']['Testing Mode'] = 'on' - ret['comment'] = result + if __salt__["csf.reload"](): + comment += " and csf reloaded." + ret["changes"]["Testing Mode"] = "on" + ret["comment"] = result return ret def testing_off(name, reload=False): - ''' + """ Ensure testing mode is enabled in csf. reload Reload CSF after changing the testing status. Default false. - ''' + """ - ret = {'name': 'testing mode', - 'changes': {}, - 'result': True, - 'comment': 'Testing mode already OFF.'} + ret = { + "name": "testing mode", + "changes": {}, + "result": True, + "comment": "Testing mode already OFF.", + } result = {} - testing = __salt__['csf.get_testing_status']() + testing = __salt__["csf.get_testing_status"]() if int(testing) == 0: return ret - disable = __salt__['csf.disable_testing_mode']() + disable = __salt__["csf.disable_testing_mode"]() if disable: - comment = 'Csf testing mode disabled' + comment = "Csf testing mode disabled" if reload: - if __salt__['csf.reload'](): - comment += ' and csf reloaded.' - ret['changes']['Testing Mode'] = 'off' - ret['comment'] = comment + if __salt__["csf.reload"](): + comment += " and csf reloaded." + ret["changes"]["Testing Mode"] = "off" + ret["comment"] = comment return ret def option_present(name, value, reload=False): - ''' + """ Ensure the state of a particular option/setting in csf. name @@ -359,31 +381,34 @@ def option_present(name, value, reload=False): reload Boolean. If set to true, csf will be reloaded after. - ''' - ret = {'name': 'testing mode', - 'changes': {}, - 'result': True, - 'comment': 'Option already present.'} + """ + ret = { + "name": "testing mode", + "changes": {}, + "result": True, + "comment": "Option already present.", + } option = name - current_option = __salt__['csf.get_option'](option) + current_option = __salt__["csf.get_option"](option) if current_option: - l = __salt__['csf.split_option'](current_option) + l = __salt__["csf.split_option"](current_option) option_value = l[1] if '"{0}"'.format(value) == option_value: return ret else: - result = __salt__['csf.set_option'](option, value) - ret['comment'] = 'Option modified.' - ret['changes']['Option'] = 'Changed' + result = __salt__["csf.set_option"](option, value) + ret["comment"] = "Option modified." + ret["changes"]["Option"] = "Changed" else: - result = __salt__['file.append']('/etc/csf/csf.conf', - args='{0} = "{1}"'.format(option, value)) - ret['comment'] = 'Option not present. Appended to csf.conf' - ret['changes']['Option'] = 'Changed.' + result = __salt__["file.append"]( + "/etc/csf/csf.conf", args='{0} = "{1}"'.format(option, value) + ) + ret["comment"] = "Option not present. Appended to csf.conf" + ret["changes"]["Option"] = "Changed." if reload: - if __salt__['csf.reload'](): - ret['comment'] += '. Csf reloaded.' + if __salt__["csf.reload"](): + ret["comment"] += ". Csf reloaded." else: - ret['comment'] += '. Csf failed to reload.' - ret['result'] = False + ret["comment"] += ". Csf failed to reload." + ret["result"] = False return ret diff --git a/salt/states/cyg.py b/salt/states/cyg.py index 93d2e1d1fbd..c660f7d94fa 100644 --- a/salt/states/cyg.py +++ b/salt/states/cyg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of Cygwin packages. A state module to manage cygwin packages. Packages can be installed @@ -9,24 +9,25 @@ or removed. dos2unix: cyg.installed -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging LOG = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if cyg module is available in __salt__. - ''' - return 'cyg.list' in __salt__ + """ + if "cyg.list" in __salt__: + return True + return (False, "cyg module could not be loaded") -def installed(name, - cyg_arch='x86_64', - mirrors=None): - ''' +def installed(name, cyg_arch="x86_64", mirrors=None): + """ Make sure that a package is installed. name @@ -49,50 +50,54 @@ def installed(name, - mirrors: - http://mirror/without/public/key: "" - http://mirror/with/public/key: http://url/of/public/key - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if cyg_arch not in ['x86', 'x86_64']: - ret['result'] = False - ret['comment'] = 'The \'cyg_arch\' argument must\ - be one of \'x86\' or \'x86_64\'' + if cyg_arch not in ["x86", "x86_64"]: + ret["result"] = False + ret[ + "comment" + ] = "The 'cyg_arch' argument must\ + be one of 'x86' or 'x86_64'" return ret - LOG.debug('Installed State: Initial Mirror list: {0}'.format(mirrors)) + LOG.debug("Installed State: Initial Mirror list: {0}".format(mirrors)) - if not __salt__['cyg.check_valid_package'](name, - cyg_arch=cyg_arch, - mirrors=mirrors): - ret['result'] = False - ret['comment'] = 'Invalid package name.' + if not __salt__["cyg.check_valid_package"]( + name, cyg_arch=cyg_arch, mirrors=mirrors + ): + ret["result"] = False + ret["comment"] = "Invalid package name." return ret - pkgs = __salt__['cyg.list'](name, cyg_arch) + pkgs = __salt__["cyg.list"](name, cyg_arch) if name in pkgs: - ret['result'] = True - ret['comment'] = 'Package is already installed.' + ret["result"] = True + ret["comment"] = "Package is already installed." return ret - if __opts__['test']: - ret['comment'] = 'The package {0} would\ - have been installed'.format(name) + if __opts__["test"]: + ret[ + "comment" + ] = "The package {0} would\ + have been installed".format( + name + ) return ret - if __salt__['cyg.install'](name, - cyg_arch=cyg_arch, - mirrors=mirrors): - ret['result'] = True - ret['changes'][name] = 'Installed' - ret['comment'] = 'Package was successfully installed' + if __salt__["cyg.install"](name, cyg_arch=cyg_arch, mirrors=mirrors): + ret["result"] = True + ret["changes"][name] = "Installed" + ret["comment"] = "Package was successfully installed" else: - ret['result'] = False - ret['comment'] = 'Could not install package.' + ret["result"] = False + ret["comment"] = "Could not install package." return ret -def removed(name, cyg_arch='x86_64', mirrors=None): - ''' +def removed(name, cyg_arch="x86_64", mirrors=None): + """ Make sure that a package is not installed. name @@ -115,42 +120,44 @@ def removed(name, cyg_arch='x86_64', mirrors=None): - mirrors: - http://mirror/without/public/key: "" - http://mirror/with/public/key: http://url/of/public/key - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if cyg_arch not in ['x86', 'x86_64']: - ret['result'] = False - ret['comment'] = 'The \'cyg_arch\' argument must\ - be one of \'x86\' or \'x86_64\'' + if cyg_arch not in ["x86", "x86_64"]: + ret["result"] = False + ret[ + "comment" + ] = "The 'cyg_arch' argument must\ + be one of 'x86' or 'x86_64'" return ret - if not __salt__['cyg.check_valid_package'](name, - cyg_arch=cyg_arch, - mirrors=mirrors): - ret['result'] = False - ret['comment'] = 'Invalid package name.' + if not __salt__["cyg.check_valid_package"]( + name, cyg_arch=cyg_arch, mirrors=mirrors + ): + ret["result"] = False + ret["comment"] = "Invalid package name." return ret - if name not in __salt__['cyg.list'](name, cyg_arch): - ret['result'] = True - ret['comment'] = 'Package is not installed.' + if name not in __salt__["cyg.list"](name, cyg_arch): + ret["result"] = True + ret["comment"] = "Package is not installed." return ret - if __opts__['test']: - ret['comment'] = 'The package {0} would have been removed'.format(name) + if __opts__["test"]: + ret["comment"] = "The package {0} would have been removed".format(name) return ret - if __salt__['cyg.uninstall'](name, cyg_arch): - ret['result'] = True - ret['changes'][name] = 'Removed' - ret['comment'] = 'Package was successfully removed.' + if __salt__["cyg.uninstall"](name, cyg_arch): + ret["result"] = True + ret["changes"][name] = "Removed" + ret["comment"] = "Package was successfully removed." else: - ret['result'] = False - ret['comment'] = 'Could not remove package.' + ret["result"] = False + ret["comment"] = "Could not remove package." return ret -def updated(name=None, cyg_arch='x86_64', mirrors=None): - ''' +def updated(name=None, cyg_arch="x86_64", mirrors=None): + """ Make sure all packages are up to date. name : None @@ -173,37 +180,39 @@ def updated(name=None, cyg_arch='x86_64', mirrors=None): - mirrors: - http://mirror/without/public/key: "" - http://mirror/with/public/key: http://url/of/public/key - ''' - ret = {'name': 'cyg.updated', 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": "cyg.updated", "result": None, "comment": "", "changes": {}} - if cyg_arch not in ['x86', 'x86_64']: - ret['result'] = False - ret['comment'] = 'The \'cyg_arch\' argument must\ - be one of \'x86\' or \'x86_64\'' + if cyg_arch not in ["x86", "x86_64"]: + ret["result"] = False + ret[ + "comment" + ] = "The 'cyg_arch' argument must\ + be one of 'x86' or 'x86_64'" return ret - if __opts__['test']: - ret['comment'] = 'All packages would have been updated' + if __opts__["test"]: + ret["comment"] = "All packages would have been updated" return ret if not mirrors: - LOG.warning('No mirror given, using the default.') + LOG.warning("No mirror given, using the default.") - before = __salt__['cyg.list'](cyg_arch=cyg_arch) - if __salt__['cyg.update'](cyg_arch, mirrors=mirrors): - after = __salt__['cyg.list'](cyg_arch=cyg_arch) + before = __salt__["cyg.list"](cyg_arch=cyg_arch) + if __salt__["cyg.update"](cyg_arch, mirrors=mirrors): + after = __salt__["cyg.list"](cyg_arch=cyg_arch) differ = DictDiffer(after, before) - ret['result'] = True + ret["result"] = True if differ.same(): - ret['comment'] = 'Nothing to update.' + ret["comment"] = "Nothing to update." else: - ret['changes']['added'] = list(differ.added()) - ret['changes']['removed'] = list(differ.removed()) - ret['changes']['changed'] = list(differ.changed()) - ret['comment'] = 'All packages successfully updated.' + ret["changes"]["added"] = list(differ.added()) + ret["changes"]["removed"] = list(differ.removed()) + ret["changes"]["changed"] = list(differ.changed()) + ret["comment"] = "All packages successfully updated." else: - ret['result'] = False - ret['comment'] = 'Could not update packages.' + ret["result"] = False + ret["comment"] = "Could not update packages." return ret @@ -215,18 +224,19 @@ def updated(name=None, cyg_arch='x86_64', mirrors=None): class DictDiffer(object): - ''' + """ Calculate the difference between two dictionaries. (1) items added (2) items removed (3) keys same in both but changed values (4) keys same in both and unchanged values - ''' + """ + def __init__(self, current_dict, past_dict): - ''' + """ Iitialize the differ. - ''' + """ self.current_dict, self.past_dict = current_dict, past_dict self.current_keys, self.past_keys = [ set(d.keys()) for d in (current_dict, past_dict) @@ -234,33 +244,35 @@ class DictDiffer(object): self.intersect = self.current_keys.intersection(self.past_keys) def same(self): - ''' + """ True if the two dicts are the same. - ''' + """ return self.current_dict == self.past_dict def added(self): - ''' + """ Return a set of additions to past_dict. - ''' + """ return self.current_keys - self.intersect def removed(self): - ''' + """ Return a set of things removed from past_dict. - ''' + """ return self.past_keys - self.intersect def changed(self): - ''' + """ Return a set of the keys with changed values. - ''' - return set(o for o in self.intersect - if self.past_dict[o] != self.current_dict[o]) + """ + return set( + o for o in self.intersect if self.past_dict[o] != self.current_dict[o] + ) def unchanged(self): - ''' + """ Return a set of the keys with unchanged values. - ''' - return set(o for o in self.intersect - if self.past_dict[o] == self.current_dict[o]) + """ + return set( + o for o in self.intersect if self.past_dict[o] == self.current_dict[o] + ) diff --git a/salt/states/ddns.py b/salt/states/ddns.py index aae29837d72..dfec659ece4 100644 --- a/salt/states/ddns.py +++ b/salt/states/ddns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Dynamic DNS updates =================== @@ -24,16 +24,18 @@ Example: - data: 111.222.333.444 - nameserver: 123.234.345.456 - keyfile: /srv/salt/dnspy_tsig_key.txt -''' +""" from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - return 'ddns' if 'ddns.update' in __salt__ else False + if "ddns.update" in __salt__: + return "ddns" + return (False, "ddns module could not be loaded") -def present(name, zone, ttl, data, rdtype='A', **kwargs): - ''' +def present(name, zone, ttl, data, rdtype="A", **kwargs): + """ Ensures that the named DNS record is present with the given ttl. name @@ -59,40 +61,41 @@ def present(name, zone, ttl, data, rdtype='A', **kwargs): nameserver, keyfile, keyname). Note that the nsupdate key file can’t be reused by this function, the keyfile and other arguments must follow the `dnspython <http://www.dnspython.org/>`_ spec. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - ret['result'] = None - ret['comment'] = '{0} record "{1}" will be updated'.format(rdtype, name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = '{0} record "{1}" will be updated'.format(rdtype, name) return ret - status = __salt__['ddns.update'](zone, name, ttl, rdtype, data, **kwargs) + status = __salt__["ddns.update"](zone, name, ttl, rdtype, data, **kwargs) if status is None: - ret['result'] = True - ret['comment'] = '{0} record "{1}" already present with ttl of {2}'.format( - rdtype, name, ttl) + ret["result"] = True + ret["comment"] = '{0} record "{1}" already present with ttl of {2}'.format( + rdtype, name, ttl + ) elif status: - ret['result'] = True - ret['comment'] = 'Updated {0} record for "{1}"'.format(rdtype, name) - ret['changes'] = {'name': name, - 'zone': zone, - 'ttl': ttl, - 'rdtype': rdtype, - 'data': data - } + ret["result"] = True + ret["comment"] = 'Updated {0} record for "{1}"'.format(rdtype, name) + ret["changes"] = { + "name": name, + "zone": zone, + "ttl": ttl, + "rdtype": rdtype, + "data": data, + } else: - ret['result'] = False - ret['comment'] = 'Failed to create or update {0} record for "{1}"'.format(rdtype, name) + ret["result"] = False + ret["comment"] = 'Failed to create or update {0} record for "{1}"'.format( + rdtype, name + ) return ret def absent(name, zone, data=None, rdtype=None, **kwargs): - ''' + """ Ensures that the named DNS record is absent. name @@ -116,30 +119,24 @@ def absent(name, zone, data=None, rdtype=None, **kwargs): nameserver, keyfile, keyname). Note that the nsupdate key file can’t be reused by this function, the keyfile and other arguments must follow the `dnspython <http://www.dnspython.org/>`_ spec. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - ret['result'] = None - ret['comment'] = '{0} record "{1}" will be deleted'.format(rdtype, name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = '{0} record "{1}" will be deleted'.format(rdtype, name) return ret - status = __salt__['ddns.delete'](zone, name, rdtype, data, **kwargs) + status = __salt__["ddns.delete"](zone, name, rdtype, data, **kwargs) if status is None: - ret['result'] = True - ret['comment'] = 'No matching DNS record(s) present' + ret["result"] = True + ret["comment"] = "No matching DNS record(s) present" elif status: - ret['result'] = True - ret['comment'] = 'Deleted DNS record(s)' - ret['changes'] = {'Deleted': {'name': name, - 'zone': zone - } - } + ret["result"] = True + ret["comment"] = "Deleted DNS record(s)" + ret["changes"] = {"Deleted": {"name": name, "zone": zone}} else: - ret['result'] = False - ret['comment'] = 'Failed to delete DNS record(s)' + ret["result"] = False + ret["comment"] = "Failed to delete DNS record(s)" return ret diff --git a/salt/states/debconfmod.py b/salt/states/debconfmod.py index a0ef20b1853..919ca91c2f1 100644 --- a/salt/states/debconfmod.py +++ b/salt/states/debconfmod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of debconf selections ================================ @@ -60,30 +60,30 @@ state will also run. .. note:: For boolean types, the value should be ``true`` or ``false``, not ``'true'`` or ``'false'``. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + from salt.ext import six - # Define the module's virtual name -__virtualname__ = 'debconf' +__virtualname__ = "debconf" def __virtual__(): - ''' + """ Confirm this module is on a Debian based system - ''' - if __grains__['os_family'] != 'Debian': - return False + """ + if __grains__["os_family"] != "Debian": + return (False, "debconf state only runs on Debian systems") # Check that debconf was loaded - if 'debconf.show' not in __salt__: - return False + if "debconf.show" not in __salt__: + return (False, "debconf module could not be loaded") return __virtualname__ def set_file(name, source, template=None, context=None, defaults=None, **kwargs): - ''' + """ Set debconf selections from a file or a template .. code-block:: yaml @@ -116,47 +116,46 @@ def set_file(name, source, template=None, context=None, defaults=None, **kwargs) defaults Default context passed to the template. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if context is None: context = {} elif not isinstance(context, dict): - ret['result'] = False - ret['comment'] = 'Context must be formed as a dict' + ret["result"] = False + ret["comment"] = "Context must be formed as a dict" return ret if defaults is None: defaults = {} elif not isinstance(defaults, dict): - ret['result'] = False - ret['comment'] = 'Defaults must be formed as a dict' + ret["result"] = False + ret["comment"] = "Defaults must be formed as a dict" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Debconf selections would have been set.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Debconf selections would have been set." return ret if template: - result = __salt__['debconf.set_template'](source, template, context, defaults, **kwargs) + result = __salt__["debconf.set_template"]( + source, template, context, defaults, **kwargs + ) else: - result = __salt__['debconf.set_file'](source, **kwargs) + result = __salt__["debconf.set_file"](source, **kwargs) if result: - ret['comment'] = 'Debconf selections were set.' + ret["comment"] = "Debconf selections were set." else: - ret['result'] = False - ret['comment'] = 'Unable to set debconf selections from file.' + ret["result"] = False + ret["comment"] = "Unable to set debconf selections from file." return ret def set(name, data, **kwargs): - ''' + """ Set debconf selections .. code-block:: yaml @@ -190,13 +189,10 @@ def set(name, data, **kwargs): value: The answer to the question - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - current = __salt__['debconf.show'](name) + current = __salt__["debconf.show"](name) for (key, args) in six.iteritems(data): # For debconf data, valid booleans are 'true' and 'false'; @@ -206,29 +202,32 @@ def set(name, data, **kwargs): # So we should manually set these values to lowercase ones, # before any str() call is performed. - if args['type'] == 'boolean': - args['value'] = 'true' if args['value'] else 'false' + if args["type"] == "boolean": + args["value"] = "true" if args["value"] else "false" - if current is not None and [key, args['type'], six.text_type(args['value'])] in current: - if ret['comment'] is '': - ret['comment'] = 'Unchanged answers: ' - ret['comment'] += ('{0} ').format(key) + if ( + current is not None + and [key, args["type"], six.text_type(args["value"])] in current + ): + if ret["comment"] is "": + ret["comment"] = "Unchanged answers: " + ret["comment"] += ("{0} ").format(key) else: - if __opts__['test']: - ret['result'] = None - ret['changes'][key] = ('New value: {0}').format(args['value']) + if __opts__["test"]: + ret["result"] = None + ret["changes"][key] = ("New value: {0}").format(args["value"]) else: - if __salt__['debconf.set'](name, key, args['type'], args['value']): - if args['type'] == 'password': - ret['changes'][key] = '(password hidden)' + if __salt__["debconf.set"](name, key, args["type"], args["value"]): + if args["type"] == "password": + ret["changes"][key] = "(password hidden)" else: - ret['changes'][key] = ('{0}').format(args['value']) + ret["changes"][key] = ("{0}").format(args["value"]) else: - ret['result'] = False - ret['comment'] = 'Some settings failed to be applied.' - ret['changes'][key] = 'Failed to set!' + ret["result"] = False + ret["comment"] = "Some settings failed to be applied." + ret["changes"][key] = "Failed to set!" - if not ret['changes']: - ret['comment'] = 'All specified answers are already set' + if not ret["changes"]: + ret["comment"] = "All specified answers are already set" return ret diff --git a/salt/states/dellchassis.py b/salt/states/dellchassis.py index ff8bbf06b04..0c22ca282a5 100644 --- a/salt/states/dellchassis.py +++ b/salt/states/dellchassis.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage chassis via Salt Proxies. .. versionadded:: 2015.8.2 @@ -152,30 +152,40 @@ pillar stated above: The Dell CMC units perform adequately but many iDRACs are **excruciatingly** slow. Some functions can take minutes to execute. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import os +from salt.exceptions import CommandExecutionError + # Import Salt lobs from salt.ext import six -from salt.exceptions import CommandExecutionError # Get logging started log = logging.getLogger(__name__) def __virtual__(): - return 'chassis.cmd' in __salt__ + if "chassis.cmd" in __salt__: + return True + return (False, "chassis module could not be loaded") -def blade_idrac(name, idrac_password=None, idrac_ipmi=None, - idrac_ip=None, idrac_netmask=None, idrac_gateway=None, - idrac_dnsname=None, - idrac_dhcp=None): - ''' +def blade_idrac( + name, + idrac_password=None, + idrac_ipmi=None, + idrac_ip=None, + idrac_netmask=None, + idrac_gateway=None, + idrac_dnsname=None, + idrac_dhcp=None, +): + """ Set parameters for iDRAC in a blade. :param idrac_password: Password to use to connect to the iDRACs directly @@ -193,134 +203,150 @@ def blade_idrac(name, idrac_password=None, idrac_ipmi=None, NOTE: If any of the IP address settings is configured, all of ip, netmask, and gateway must be present - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} if not idrac_password: - (username, password) = __salt__['chassis.chassis_credentials']() + (username, password) = __salt__["chassis.chassis_credentials"]() else: password = idrac_password - module_network = __salt__['chassis.cmd']('network_info', module=name) - current_idrac_ip = module_network['Network']['IP Address'] + module_network = __salt__["chassis.cmd"]("network_info", module=name) + current_idrac_ip = module_network["Network"]["IP Address"] if idrac_ipmi is not None: if idrac_ipmi is True or idrac_ipmi == 1: - idrac_ipmi = '1' + idrac_ipmi = "1" if idrac_ipmi is False or idrac_ipmi == 0: - idrac_ipmi = '0' - current_ipmi = __salt__['dracr.get_general']('cfgIpmiLan', 'cfgIpmiLanEnable', - host=current_idrac_ip, admin_username='root', - admin_password=password) + idrac_ipmi = "0" + current_ipmi = __salt__["dracr.get_general"]( + "cfgIpmiLan", + "cfgIpmiLanEnable", + host=current_idrac_ip, + admin_username="root", + admin_password=password, + ) if current_ipmi != idrac_ipmi: - ch = {'Old': current_ipmi, 'New': idrac_ipmi} - ret['changes']['IPMI'] = ch + ch = {"Old": current_ipmi, "New": idrac_ipmi} + ret["changes"]["IPMI"] = ch if idrac_dnsname is not None: - dnsret = __salt__['dracr.get_dns_dracname'](host=current_idrac_ip, - admin_username='root', - admin_password=password) - current_dnsname = dnsret['[Key=iDRAC.Embedded.1#NIC.1]']['DNSRacName'] + dnsret = __salt__["dracr.get_dns_dracname"]( + host=current_idrac_ip, admin_username="root", admin_password=password + ) + current_dnsname = dnsret["[Key=iDRAC.Embedded.1#NIC.1]"]["DNSRacName"] if current_dnsname != idrac_dnsname: - ch = {'Old': current_dnsname, - 'New': idrac_dnsname} - ret['changes']['DNSRacName'] = ch + ch = {"Old": current_dnsname, "New": idrac_dnsname} + ret["changes"]["DNSRacName"] = ch if idrac_dhcp is not None or idrac_ip or idrac_netmask or idrac_gateway: if idrac_dhcp is True or idrac_dhcp == 1: idrac_dhcp = 1 else: idrac_dhcp = 0 - if six.text_type(module_network['Network']['DHCP Enabled']) == '0' and idrac_dhcp == 1: - ch = {'Old': module_network['Network']['DHCP Enabled'], - 'New': idrac_dhcp} - ret['changes']['DRAC DHCP'] = ch + if ( + six.text_type(module_network["Network"]["DHCP Enabled"]) == "0" + and idrac_dhcp == 1 + ): + ch = {"Old": module_network["Network"]["DHCP Enabled"], "New": idrac_dhcp} + ret["changes"]["DRAC DHCP"] = ch if idrac_dhcp == 0 and all([idrac_ip, idrac_netmask, idrac_netmask]): - current_network = __salt__['chassis.cmd']('network_info', - module=name) + current_network = __salt__["chassis.cmd"]("network_info", module=name) old_ipv4 = {} new_ipv4 = {} - if current_network['Network']['IP Address'] != idrac_ip: - old_ipv4['ip'] = current_network['Network']['IP Address'] - new_ipv4['ip'] = idrac_ip - if current_network['Network']['Subnet Mask'] != idrac_netmask: - old_ipv4['netmask'] = current_network['Network']['Subnet Mask'] - new_ipv4['netmask'] = idrac_netmask - if current_network['Network']['Gateway'] != idrac_gateway: - old_ipv4['gateway'] = current_network['Network']['Gateway'] - new_ipv4['gateway'] = idrac_gateway + if current_network["Network"]["IP Address"] != idrac_ip: + old_ipv4["ip"] = current_network["Network"]["IP Address"] + new_ipv4["ip"] = idrac_ip + if current_network["Network"]["Subnet Mask"] != idrac_netmask: + old_ipv4["netmask"] = current_network["Network"]["Subnet Mask"] + new_ipv4["netmask"] = idrac_netmask + if current_network["Network"]["Gateway"] != idrac_gateway: + old_ipv4["gateway"] = current_network["Network"]["Gateway"] + new_ipv4["gateway"] = idrac_gateway if new_ipv4 != {}: - ret['changes']['Network'] = {} - ret['changes']['Network']['Old'] = old_ipv4 - ret['changes']['Network']['New'] = new_ipv4 + ret["changes"]["Network"] = {} + ret["changes"]["Network"]["Old"] = old_ipv4 + ret["changes"]["Network"]["New"] = new_ipv4 - if ret['changes'] == {}: - ret['comment'] = 'iDRAC on blade is already in the desired state.' + if ret["changes"] == {}: + ret["comment"] = "iDRAC on blade is already in the desired state." return ret - if __opts__['test'] and ret['changes'] != {}: - ret['result'] = None - ret['comment'] = 'iDRAC on blade will change.' + if __opts__["test"] and ret["changes"] != {}: + ret["result"] = None + ret["comment"] = "iDRAC on blade will change." return ret - if 'IPMI' in ret['changes']: - ipmi_result = __salt__['dracr.set_general']('cfgIpmiLan', - 'cfgIpmiLanEnable', - idrac_ipmi, - host=current_idrac_ip, - admin_username='root', - admin_password=password) - if not ipmi_result: - ret['result'] = False - ret['changes']['IPMI']['success'] = False - - if 'DNSRacName' in ret['changes']: - dnsracname_result = __salt__['dracr.set_dns_dracname'](idrac_dnsname, + if "IPMI" in ret["changes"]: + ipmi_result = __salt__["dracr.set_general"]( + "cfgIpmiLan", + "cfgIpmiLanEnable", + idrac_ipmi, host=current_idrac_ip, - admin_username='root', - admin_password=password) - if dnsracname_result['retcode'] == 0: - ret['changes']['DNSRacName']['success'] = True - else: - ret['result'] = False - ret['changes']['DNSRacName']['success'] = False - ret['changes']['DNSRacName']['return'] = dnsracname_result + admin_username="root", + admin_password=password, + ) + if not ipmi_result: + ret["result"] = False + ret["changes"]["IPMI"]["success"] = False - if 'DRAC DHCP' in ret['changes']: - dhcp_result = __salt__['chassis.cmd']('set_niccfg', dhcp=idrac_dhcp) - if dhcp_result['retcode']: - ret['changes']['DRAC DHCP']['success'] = True + if "DNSRacName" in ret["changes"]: + dnsracname_result = __salt__["dracr.set_dns_dracname"]( + idrac_dnsname, + host=current_idrac_ip, + admin_username="root", + admin_password=password, + ) + if dnsracname_result["retcode"] == 0: + ret["changes"]["DNSRacName"]["success"] = True else: - ret['result'] = False - ret['changes']['DRAC DHCP']['success'] = False - ret['changes']['DRAC DHCP']['return'] = dhcp_result + ret["result"] = False + ret["changes"]["DNSRacName"]["success"] = False + ret["changes"]["DNSRacName"]["return"] = dnsracname_result - if 'Network' in ret['changes']: - network_result = __salt__['chassis.cmd']('set_niccfg', ip=idrac_ip, - netmask=idrac_netmask, - gateway=idrac_gateway, - module=name) - if network_result['retcode'] == 0: - ret['changes']['Network']['success'] = True + if "DRAC DHCP" in ret["changes"]: + dhcp_result = __salt__["chassis.cmd"]("set_niccfg", dhcp=idrac_dhcp) + if dhcp_result["retcode"]: + ret["changes"]["DRAC DHCP"]["success"] = True else: - ret['result'] = False - ret['changes']['Network']['success'] = False - ret['changes']['Network']['return'] = network_result + ret["result"] = False + ret["changes"]["DRAC DHCP"]["success"] = False + ret["changes"]["DRAC DHCP"]["return"] = dhcp_result + + if "Network" in ret["changes"]: + network_result = __salt__["chassis.cmd"]( + "set_niccfg", + ip=idrac_ip, + netmask=idrac_netmask, + gateway=idrac_gateway, + module=name, + ) + if network_result["retcode"] == 0: + ret["changes"]["Network"]["success"] = True + else: + ret["result"] = False + ret["changes"]["Network"]["success"] = False + ret["changes"]["Network"]["return"] = network_result return ret -def chassis(name, chassis_name=None, password=None, datacenter=None, - location=None, mode=None, idrac_launch=None, slot_names=None, - blade_power_states=None): - ''' +def chassis( + name, + chassis_name=None, + password=None, + datacenter=None, + location=None, + mode=None, + idrac_launch=None, + slot_names=None, + blade_power_states=None, +): + """ Manage a Dell Chassis. chassis_name @@ -382,149 +408,149 @@ def chassis(name, chassis_name=None, password=None, datacenter=None, - server-1: on - server-2: off - server-3: powercycle - ''' - ret = {'name': chassis_name, - 'chassis_name': chassis_name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = { + "name": chassis_name, + "chassis_name": chassis_name, + "result": True, + "changes": {}, + "comment": "", + } - chassis_cmd = 'chassis.cmd' - cfg_tuning = 'cfgRacTuning' - mode_cmd = 'cfgRacTuneChassisMgmtAtServer' - launch_cmd = 'cfgRacTuneIdracDNSLaunchEnable' + chassis_cmd = "chassis.cmd" + cfg_tuning = "cfgRacTuning" + mode_cmd = "cfgRacTuneChassisMgmtAtServer" + launch_cmd = "cfgRacTuneIdracDNSLaunchEnable" - inventory = __salt__[chassis_cmd]('inventory') + inventory = __salt__[chassis_cmd]("inventory") if idrac_launch: idrac_launch = six.text_type(idrac_launch) - current_name = __salt__[chassis_cmd]('get_chassis_name') + current_name = __salt__[chassis_cmd]("get_chassis_name") if chassis_name != current_name: - ret['changes'].update({'Name': - {'Old': current_name, - 'New': chassis_name}}) + ret["changes"].update({"Name": {"Old": current_name, "New": chassis_name}}) - current_dc = __salt__[chassis_cmd]('get_chassis_datacenter') + current_dc = __salt__[chassis_cmd]("get_chassis_datacenter") if datacenter and datacenter != current_dc: - ret['changes'].update({'Datacenter': - {'Old': current_dc, - 'New': datacenter}}) + ret["changes"].update({"Datacenter": {"Old": current_dc, "New": datacenter}}) if password: - ret['changes'].update({'Password': - {'Old': '******', - 'New': '******'}}) + ret["changes"].update({"Password": {"Old": "******", "New": "******"}}) if location: - current_location = __salt__[chassis_cmd]('get_chassis_location') + current_location = __salt__[chassis_cmd]("get_chassis_location") if location != current_location: - ret['changes'].update({'Location': - {'Old': current_location, - 'New': location}}) + ret["changes"].update( + {"Location": {"Old": current_location, "New": location}} + ) if mode: - current_mode = __salt__[chassis_cmd]('get_general', cfg_tuning, mode_cmd) + current_mode = __salt__[chassis_cmd]("get_general", cfg_tuning, mode_cmd) if mode != current_mode: - ret['changes'].update({'Management Mode': - {'Old': current_mode, - 'New': mode}}) + ret["changes"].update( + {"Management Mode": {"Old": current_mode, "New": mode}} + ) if idrac_launch: - current_launch_method = __salt__[chassis_cmd]('get_general', cfg_tuning, launch_cmd) + current_launch_method = __salt__[chassis_cmd]( + "get_general", cfg_tuning, launch_cmd + ) if idrac_launch != current_launch_method: - ret['changes'].update({'iDrac Launch Method': - {'Old': current_launch_method, - 'New': idrac_launch}}) + ret["changes"].update( + { + "iDrac Launch Method": { + "Old": current_launch_method, + "New": idrac_launch, + } + } + ) if slot_names: - current_slot_names = __salt__[chassis_cmd]('list_slotnames') + current_slot_names = __salt__[chassis_cmd]("list_slotnames") for s in slot_names: key = s.keys()[0] new_name = s[key] - if key.startswith('slot-'): + if key.startswith("slot-"): key = key[5:] - current_slot_name = current_slot_names.get(key).get('slotname') + current_slot_name = current_slot_names.get(key).get("slotname") if current_slot_name != new_name: old = {key: current_slot_name} new = {key: new_name} - if ret['changes'].get('Slot Names') is None: - ret['changes'].update({'Slot Names': - {'Old': {}, - 'New': {}}}) - ret['changes']['Slot Names']['Old'].update(old) - ret['changes']['Slot Names']['New'].update(new) + if ret["changes"].get("Slot Names") is None: + ret["changes"].update({"Slot Names": {"Old": {}, "New": {}}}) + ret["changes"]["Slot Names"]["Old"].update(old) + ret["changes"]["Slot Names"]["New"].update(new) current_power_states = {} target_power_states = {} if blade_power_states: for b in blade_power_states: key = b.keys()[0] - status = __salt__[chassis_cmd]('server_powerstatus', module=key) - current_power_states[key] = status.get('status', -1) - if b[key] == 'powerdown': + status = __salt__[chassis_cmd]("server_powerstatus", module=key) + current_power_states[key] = status.get("status", -1) + if b[key] == "powerdown": if current_power_states[key] != -1 and current_power_states[key]: - target_power_states[key] = 'powerdown' - if b[key] == 'powerup': + target_power_states[key] = "powerdown" + if b[key] == "powerup": if current_power_states[key] != -1 and not current_power_states[key]: - target_power_states[key] = 'powerup' - if b[key] == 'powercycle': + target_power_states[key] = "powerup" + if b[key] == "powercycle": if current_power_states[key] != -1 and not current_power_states[key]: - target_power_states[key] = 'powerup' + target_power_states[key] = "powerup" if current_power_states[key] != -1 and current_power_states[key]: - target_power_states[key] = 'powercycle' + target_power_states[key] = "powercycle" for k, v in six.iteritems(target_power_states): old = {k: current_power_states[k]} new = {k: v} - if ret['changes'].get('Blade Power States') is None: - ret['changes'].update({'Blade Power States': - {'Old': {}, - 'New': {}}}) - ret['changes']['Blade Power States']['Old'].update(old) - ret['changes']['Blade Power States']['New'].update(new) + if ret["changes"].get("Blade Power States") is None: + ret["changes"].update({"Blade Power States": {"Old": {}, "New": {}}}) + ret["changes"]["Blade Power States"]["Old"].update(old) + ret["changes"]["Blade Power States"]["New"].update(new) - if ret['changes'] == {}: - ret['comment'] = 'Dell chassis is already in the desired state.' + if ret["changes"] == {}: + ret["comment"] = "Dell chassis is already in the desired state." return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Dell chassis configuration will change.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Dell chassis configuration will change." return ret # Finally, set the necessary configurations on the chassis. - name = __salt__[chassis_cmd]('set_chassis_name', chassis_name) + name = __salt__[chassis_cmd]("set_chassis_name", chassis_name) if location: - location = __salt__[chassis_cmd]('set_chassis_location', location) + location = __salt__[chassis_cmd]("set_chassis_location", location) pw_result = True if password: pw_single = True - if __salt__[chassis_cmd]('change_password', username='root', uid=1, - password=password): - for blade in inventory['server']: - pw_single = __salt__[chassis_cmd]('deploy_password', - username='root', - password=password, - module=blade) + if __salt__[chassis_cmd]( + "change_password", username="root", uid=1, password=password + ): + for blade in inventory["server"]: + pw_single = __salt__[chassis_cmd]( + "deploy_password", username="root", password=password, module=blade + ) if not pw_single: pw_result = False else: pw_result = False if datacenter: - datacenter_result = __salt__[chassis_cmd]('set_chassis_datacenter', - datacenter) + datacenter_result = __salt__[chassis_cmd]("set_chassis_datacenter", datacenter) if mode: - mode = __salt__[chassis_cmd]('set_general', cfg_tuning, mode_cmd, mode) + mode = __salt__[chassis_cmd]("set_general", cfg_tuning, mode_cmd, mode) if idrac_launch: - idrac_launch = __salt__[chassis_cmd]('set_general', cfg_tuning, launch_cmd, idrac_launch) - if ret['changes'].get('Slot Names') is not None: + idrac_launch = __salt__[chassis_cmd]( + "set_general", cfg_tuning, launch_cmd, idrac_launch + ) + if ret["changes"].get("Slot Names") is not None: slot_rets = [] for s in slot_names: key = s.keys()[0] new_name = s[key] - if key.startswith('slot-'): + if key.startswith("slot-"): key = key[5:] - slot_rets.append(__salt__[chassis_cmd]('set_slotname', key, new_name)) + slot_rets.append(__salt__[chassis_cmd]("set_slotname", key, new_name)) if any(slot_rets) is False: slot_names = False @@ -533,22 +559,25 @@ def chassis(name, chassis_name=None, password=None, datacenter=None, powerchange_all_ok = True for k, v in six.iteritems(target_power_states): - powerchange_ok = __salt__[chassis_cmd]('server_power', v, module=k) + powerchange_ok = __salt__[chassis_cmd]("server_power", v, module=k) if not powerchange_ok: powerchange_all_ok = False - if any([name, location, mode, idrac_launch, - slot_names, powerchange_all_ok]) is False: - ret['result'] = False - ret['comment'] = 'There was an error setting the Dell chassis.' + if ( + any([name, location, mode, idrac_launch, slot_names, powerchange_all_ok]) + is False + ): + ret["result"] = False + ret["comment"] = "There was an error setting the Dell chassis." - ret['comment'] = 'Dell chassis was updated.' + ret["comment"] = "Dell chassis was updated." return ret -def switch(name, ip=None, netmask=None, gateway=None, dhcp=None, - password=None, snmp=None): - ''' +def switch( + name, ip=None, netmask=None, gateway=None, dhcp=None, password=None, snmp=None +): + """ Manage switches in a Dell Chassis. name @@ -588,101 +617,120 @@ def switch(name, ip=None, netmask=None, gateway=None, dhcp=None, - password: secret - snmp: public - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - current_nic = __salt__['chassis.cmd']('network_info', module=name) + current_nic = __salt__["chassis.cmd"]("network_info", module=name) try: - if current_nic.get('retcode', 0) != 0: - ret['result'] = False - ret['comment'] = current_nic['stdout'] + if current_nic.get("retcode", 0) != 0: + ret["result"] = False + ret["comment"] = current_nic["stdout"] return ret if ip or netmask or gateway: if not ip: - ip = current_nic['Network']['IP Address'] + ip = current_nic["Network"]["IP Address"] if not netmask: - ip = current_nic['Network']['Subnet Mask'] + ip = current_nic["Network"]["Subnet Mask"] if not gateway: - ip = current_nic['Network']['Gateway'] + ip = current_nic["Network"]["Gateway"] - if current_nic['Network']['DHCP Enabled'] == '0' and dhcp: - ret['changes'].update({'DHCP': {'Old': {'DHCP Enabled': current_nic['Network']['DHCP Enabled']}, - 'New': {'DHCP Enabled': dhcp}}}) + if current_nic["Network"]["DHCP Enabled"] == "0" and dhcp: + ret["changes"].update( + { + "DHCP": { + "Old": {"DHCP Enabled": current_nic["Network"]["DHCP Enabled"]}, + "New": {"DHCP Enabled": dhcp}, + } + } + ) - if ((ip or netmask or gateway) and not dhcp and (ip != current_nic['Network']['IP Address'] or - netmask != current_nic['Network']['Subnet Mask'] or - gateway != current_nic['Network']['Gateway'])): - ret['changes'].update({'IP': {'Old': current_nic['Network'], - 'New': {'IP Address': ip, - 'Subnet Mask': netmask, - 'Gateway': gateway}}}) + if ( + (ip or netmask or gateway) + and not dhcp + and ( + ip != current_nic["Network"]["IP Address"] + or netmask != current_nic["Network"]["Subnet Mask"] + or gateway != current_nic["Network"]["Gateway"] + ) + ): + ret["changes"].update( + { + "IP": { + "Old": current_nic["Network"], + "New": { + "IP Address": ip, + "Subnet Mask": netmask, + "Gateway": gateway, + }, + } + } + ) if password: - if 'New' not in ret['changes']: - ret['changes']['New'] = {} - ret['changes']['New'].update({'Password': '*****'}) + if "New" not in ret["changes"]: + ret["changes"]["New"] = {} + ret["changes"]["New"].update({"Password": "*****"}) if snmp: - if 'New' not in ret['changes']: - ret['changes']['New'] = {} - ret['changes']['New'].update({'SNMP': '*****'}) + if "New" not in ret["changes"]: + ret["changes"]["New"] = {} + ret["changes"]["New"].update({"SNMP": "*****"}) - if ret['changes'] == {}: - ret['comment'] = 'Switch ' + name + ' is already in desired state' + if ret["changes"] == {}: + ret["comment"] = "Switch " + name + " is already in desired state" return ret except AttributeError: - ret['changes'] = {} - ret['comment'] = 'Something went wrong retrieving the switch details' + ret["changes"] = {} + ret["comment"] = "Something went wrong retrieving the switch details" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Switch ' + name + ' configuration will change' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Switch " + name + " configuration will change" return ret # Finally, set the necessary configurations on the chassis. dhcp_ret = net_ret = password_ret = snmp_ret = True if dhcp: - dhcp_ret = __salt__['chassis.cmd']('set_niccfg', module=name, dhcp=dhcp) + dhcp_ret = __salt__["chassis.cmd"]("set_niccfg", module=name, dhcp=dhcp) if ip or netmask or gateway: - net_ret = __salt__['chassis.cmd']('set_niccfg', ip, netmask, gateway, module=name) + net_ret = __salt__["chassis.cmd"]( + "set_niccfg", ip, netmask, gateway, module=name + ) if password: - password_ret = __salt__['chassis.cmd']('deploy_password', 'root', password, module=name) + password_ret = __salt__["chassis.cmd"]( + "deploy_password", "root", password, module=name + ) if snmp: - snmp_ret = __salt__['chassis.cmd']('deploy_snmp', snmp, module=name) + snmp_ret = __salt__["chassis.cmd"]("deploy_snmp", snmp, module=name) if any([password_ret, snmp_ret, net_ret, dhcp_ret]) is False: - ret['result'] = False - ret['comment'] = 'There was an error setting the switch {0}.'.format(name) + ret["result"] = False + ret["comment"] = "There was an error setting the switch {0}.".format(name) - ret['comment'] = 'Dell chassis switch {0} was updated.'.format(name) + ret["comment"] = "Dell chassis switch {0} was updated.".format(name) return ret -def _firmware_update(firmwarefile='', host='', - directory=''): - ''' +def _firmware_update(firmwarefile="", host="", directory=""): + """ Update firmware for a single host - ''' + """ dest = os.path.join(directory, firmwarefile[7:]) - __salt__['cp.get_file'](firmwarefile, dest) + __salt__["cp.get_file"](firmwarefile, dest) - username = __pillar__['proxy']['admin_user'] - password = __pillar__['proxy']['admin_password'] - __salt__['dracr.update_firmware'](dest, - host=host, - admin_username=username, - admin_password=password) + username = __pillar__["proxy"]["admin_user"] + password = __pillar__["proxy"]["admin_password"] + __salt__["dracr.update_firmware"]( + dest, host=host, admin_username=username, admin_password=password + ) -def firmware_update(hosts=None, directory=''): - ''' +def firmware_update(hosts=None, directory=""): + """ State to update the firmware on host using the ``racadm`` command @@ -705,27 +753,31 @@ def firmware_update(hosts=None, directory=''): server-1: salt://firmware.exe directory: /opt/firmwares - ''' + """ ret = {} ret.changes = {} success = True for host, firmwarefile in hosts: try: _firmware_update(firmwarefile, host, directory) - ret['changes'].update({ - 'host': { - 'comment': 'Firmware update submitted for {0}'.format(host), - 'success': True, + ret["changes"].update( + { + "host": { + "comment": "Firmware update submitted for {0}".format(host), + "success": True, + } } - }) + ) except CommandExecutionError as err: success = False - ret['changes'].update({ - 'host': { - 'comment': 'FAILED to update firmware for {0}'.format(host), - 'success': False, - 'reason': six.text_type(err), + ret["changes"].update( + { + "host": { + "comment": "FAILED to update firmware for {0}".format(host), + "success": False, + "reason": six.text_type(err), + } } - }) - ret['result'] = success + ) + ret["result"] = success return ret diff --git a/salt/states/disk.py b/salt/states/disk.py index aca5f7c2d11..23db78d6f8d 100644 --- a/salt/states/disk.py +++ b/salt/states/disk.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Disk monitoring state Monitor the state of disk resources. @@ -42,117 +42,135 @@ specify the ``absolute`` flag: - minimum: 1024 KB - maximum: 1048576 KB - absolute: True -''' +""" from __future__ import absolute_import, print_function, unicode_literals +from os import path + # Import salt libs from salt.ext.six import string_types -from os import path __monitor__ = [ - 'status', + "status", ] -def _validate_int(name, value, limits=(), strip='%'): - ''' +def _validate_int(name, value, limits=(), strip="%"): + """ Validate the named integer within the supplied limits inclusive and strip supplied unit characters - ''' - comment = '' + """ + comment = "" # Must be integral try: if isinstance(value, string_types): - value = value.strip(' ' + strip) + value = value.strip(" " + strip) value = int(value) except (TypeError, ValueError): - comment += '{0} must be an integer '.format(name) + comment += "{0} must be an integer ".format(name) # Must be in range else: if len(limits) == 2: if value < limits[0] or value > limits[1]: - comment += '{0} must be in the range [{1[0]}, {1[1]}] '.format(name, limits) + comment += "{0} must be in the range [{1[0]}, {1[1]}] ".format( + name, limits + ) return value, comment def _status_mount(name, ret, minimum, maximum, absolute, free, data): # Get used space if absolute: - used = int(data[name]['used']) - available = int(data[name]['available']) + used = int(data[name]["used"]) + available = int(data[name]["available"]) else: # POSIX-compliant df output reports percent used as 'capacity' - used = int(data[name]['capacity'].strip('%')) + used = int(data[name]["capacity"].strip("%")) available = 100 - used # Collect return information - ret['data'] = data[name] + ret["data"] = data[name] return _check_min_max(absolute, free, available, used, maximum, minimum, ret) def _status_path(directory, ret, minimum, maximum, absolute, free): if path.isdir(directory) is False: - ret['result'] = False - ret['comment'] += ('Directory {0} does not exist or is not a directory'.format(directory)) + ret["result"] = False + ret["comment"] += "Directory {0} does not exist or is not a directory".format( + directory + ) return ret - data = __salt__['status.diskusage'](directory) + data = __salt__["status.diskusage"](directory) if absolute: - used = int(data[directory]['total']) - int(data[directory]['available']) - available = int(data[directory]['available']) + used = int(data[directory]["total"]) - int(data[directory]["available"]) + available = int(data[directory]["available"]) else: - if int(data[directory]['total']) == 0: + if int(data[directory]["total"]) == 0: used = 0 available = 0 else: - used = round(float(int(data[directory]['total']) - int(data[directory]['available'])) / - int(data[directory]['total']) * 100, 1) - available = round(float(data[directory]['available']) / int(data[directory]['total']) * 100, 1) + used = round( + float(int(data[directory]["total"]) - int(data[directory]["available"])) + / int(data[directory]["total"]) + * 100, + 1, + ) + available = round( + float(data[directory]["available"]) + / int(data[directory]["total"]) + * 100, + 1, + ) - ret['data'] = data + ret["data"] = data return _check_min_max(absolute, free, available, used, maximum, minimum, ret) def _check_min_max(absolute, free, available, used, maximum, minimum, ret): - unit = 'KB' if absolute else '%' + unit = "KB" if absolute else "%" if minimum is not None: if free: if available < minimum: - ret['comment'] = ('Disk available space is below minimum' - ' of {0} {2} at {1} {2}' - ''.format(minimum, available, unit) - ) + ret["comment"] = ( + "Disk available space is below minimum" + " of {0} {2} at {1} {2}" + "".format(minimum, available, unit) + ) return ret else: if used < minimum: - ret['comment'] = ('Disk used space is below minimum' - ' of {0} {2} at {1} {2}' - ''.format(minimum, used, unit) - ) + ret["comment"] = ( + "Disk used space is below minimum" + " of {0} {2} at {1} {2}" + "".format(minimum, used, unit) + ) return ret if maximum is not None: if free: if available > maximum: - ret['comment'] = ('Disk available space is above maximum' - ' of {0} {2} at {1} {2}' - ''.format(maximum, available, unit) - ) + ret["comment"] = ( + "Disk available space is above maximum" + " of {0} {2} at {1} {2}" + "".format(maximum, available, unit) + ) return ret else: if used > maximum: - ret['comment'] = ('Disk used space is above maximum' - ' of {0} {2} at {1} {2}' - ''.format(maximum, used, unit) - ) + ret["comment"] = ( + "Disk used space is above maximum" + " of {0} {2} at {1} {2}" + "".format(maximum, used, unit) + ) return ret - ret['comment'] = 'Disk used space in acceptable range' - ret['result'] = True + ret["comment"] = "Disk used space in acceptable range" + ret["result"] = True return ret def status(name, maximum=None, minimum=None, absolute=False, free=False): - ''' + """ Return the current disk usage stats for the named mount point name @@ -173,38 +191,40 @@ def status(name, maximum=None, minimum=None, absolute=False, free=False): free By default, `minimum` & `maximum` refer to the amount of used space. Set to `True` to evaluate the free space instead. - ''' + """ # Monitoring state, no changes will be made so no test interface needed - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}, - 'data': {}} # Data field for monitoring state + ret = { + "name": name, + "result": False, + "comment": "", + "changes": {}, + "data": {}, + } # Data field for monitoring state # Validate extrema if maximum is not None: if not absolute: - maximum, comment = _validate_int('maximum', maximum, [0, 100]) + maximum, comment = _validate_int("maximum", maximum, [0, 100]) else: - maximum, comment = _validate_int('maximum', maximum, strip='KB') - ret['comment'] += comment + maximum, comment = _validate_int("maximum", maximum, strip="KB") + ret["comment"] += comment if minimum is not None: if not absolute: - minimum, comment = _validate_int('minimum', minimum, [0, 100]) + minimum, comment = _validate_int("minimum", minimum, [0, 100]) else: - minimum, comment = _validate_int('minimum', minimum, strip='KB') - ret['comment'] += comment + minimum, comment = _validate_int("minimum", minimum, strip="KB") + ret["comment"] += comment if minimum is not None and maximum is not None: if minimum >= maximum: - ret['comment'] += 'minimum must be less than maximum ' - if ret['comment']: + ret["comment"] += "minimum must be less than maximum " + if ret["comment"]: return ret - data = __salt__['disk.usage']() + data = __salt__["disk.usage"]() # Validate name if name not in data: - ret['comment'] += ('Disk mount {0} not present. '.format(name)) + ret["comment"] += "Disk mount {0} not present. ".format(name) return _status_path(name, ret, minimum, maximum, absolute, free) else: return _status_mount(name, ret, minimum, maximum, absolute, free, data) diff --git a/salt/states/docker_container.py b/salt/states/docker_container.py index 2b4c6b56aba..7522d80c2ef 100644 --- a/salt/states/docker_container.py +++ b/salt/states/docker_container.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Docker containers .. versionadded:: 2017.7.0 @@ -44,18 +44,20 @@ configuration remains unchanged. To pull from a Docker registry, authentication must be configured. See :ref:`here <docker-authentication>` for more information on how to configure access to docker registries in :ref:`Pillar <pillar>` data. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import os -# Import Salt libs -from salt.exceptions import CommandExecutionError import salt.utils.args import salt.utils.data import salt.utils.docker +# Import Salt libs +from salt.exceptions import CommandExecutionError + # Import 3rd-party libs from salt.ext import six @@ -63,51 +65,51 @@ from salt.ext import six log = logging.getLogger(__name__) # pylint: disable=invalid-name # Define the module's virtual name -__virtualname__ = 'docker_container' -__virtual_aliases__ = ('moby_container',) +__virtualname__ = "docker_container" +__virtual_aliases__ = ("moby_container",) def __virtual__(): - ''' + """ Only load if the docker execution module is available - ''' - if 'docker.version' in __salt__: + """ + if "docker.version" in __salt__: return __virtualname__ - return (False, __salt__.missing_fun_string('docker.version')) + return (False, __salt__.missing_fun_string("docker.version")) def _format_comments(ret, comments): - ''' + """ DRY code for joining comments together and conditionally adding a period at the end, and adding this comment string to the state return dict. - ''' + """ if isinstance(comments, six.string_types): - ret['comment'] = comments + ret["comment"] = comments else: - ret['comment'] = '. '.join(comments) + ret["comment"] = ". ".join(comments) if len(comments) > 1: - ret['comment'] += '.' + ret["comment"] += "." return ret def _check_diff(changes): - ''' + """ Check the diff for signs of incorrect argument handling in previous releases, as discovered here: https://github.com/saltstack/salt/pull/39996#issuecomment-288025200 - ''' + """ for conf_dict in changes: - if conf_dict == 'Networks': + if conf_dict == "Networks": continue for item in changes[conf_dict]: - if changes[conf_dict][item]['new'] is None: - old = changes[conf_dict][item]['old'] - if old == '': + if changes[conf_dict][item]["new"] is None: + old = changes[conf_dict][item]["old"] + if old == "": return True else: try: - if all(x == '' for x in old): + if all(x == "" for x in old): return True except TypeError: # Old value is not an iterable type @@ -116,9 +118,9 @@ def _check_diff(changes): def _parse_networks(networks): - ''' + """ Common logic for parsing the networks - ''' + """ networks = salt.utils.args.split_input(networks or []) if not networks: networks = {} @@ -128,7 +130,7 @@ def _parse_networks(networks): networks = salt.utils.data.repack_dictlist(networks) if not networks: raise CommandExecutionError( - 'Invalid network configuration (see documentation)' + "Invalid network configuration (see documentation)" ) for net_name, net_conf in six.iteritems(networks): if net_conf is None: @@ -137,10 +139,10 @@ def _parse_networks(networks): networks[net_name] = salt.utils.data.repack_dictlist(net_conf) if not networks[net_name]: raise CommandExecutionError( - 'Invalid configuration for network \'{0}\' ' - '(see documentation)'.format(net_name) + "Invalid configuration for network '{0}' " + "(see documentation)".format(net_name) ) - for key in ('links', 'aliases'): + for key in ("links", "aliases"): try: networks[net_name][key] = salt.utils.args.split_input( networks[net_name][key] @@ -156,30 +158,27 @@ def _parse_networks(networks): for key, val in six.iteritems(net_conf): if val is None: errors.append( - 'Config option \'{0}\' for network \'{1}\' is ' - 'missing a value'.format(key, net_name) + "Config option '{0}' for network '{1}' is " + "missing a value".format(key, net_name) ) if errors: - raise CommandExecutionError( - 'Invalid network configuration', info=errors) + raise CommandExecutionError("Invalid network configuration", info=errors) if networks: try: all_networks = [ - x['Name'] for x in __salt__['docker.networks']() - if 'Name' in x + x["Name"] for x in __salt__["docker.networks"]() if "Name" in x ] except CommandExecutionError as exc: raise CommandExecutionError( - 'Failed to get list of existing networks: {0}.'.format(exc) + "Failed to get list of existing networks: {0}.".format(exc) ) else: - missing_networks = [ - x for x in sorted(networks) if x not in all_networks] + missing_networks = [x for x in sorted(networks) if x not in all_networks] if missing_networks: raise CommandExecutionError( - 'The following networks are not present: {0}'.format( - ', '.join(missing_networks) + "The following networks are not present: {0}".format( + ", ".join(missing_networks) ) ) @@ -187,49 +186,50 @@ def _parse_networks(networks): def _resolve_image(ret, image, client_timeout): - ''' + """ Resolve the image ID and pull the image if necessary - ''' - image_id = __salt__['docker.resolve_image_id'](image) + """ + image_id = __salt__["docker.resolve_image_id"](image) if image_id is False: - if not __opts__['test']: + if not __opts__["test"]: # Image not pulled locally, so try pulling it try: - pull_result = __salt__['docker.pull']( - image, - client_timeout=client_timeout, + pull_result = __salt__["docker.pull"]( + image, client_timeout=client_timeout, ) except Exception as exc: # pylint: disable=broad-except raise CommandExecutionError( - 'Failed to pull {0}: {1}'.format(image, exc) + "Failed to pull {0}: {1}".format(image, exc) ) else: - ret['changes']['image'] = pull_result + ret["changes"]["image"] = pull_result # Try resolving again now that we've pulled - image_id = __salt__['docker.resolve_image_id'](image) + image_id = __salt__["docker.resolve_image_id"](image) if image_id is False: # Shouldn't happen unless the pull failed raise CommandExecutionError( - 'Image \'{0}\' not present despite a docker pull ' - 'raising no errors'.format(image) + "Image '{0}' not present despite a docker pull " + "raising no errors".format(image) ) return image_id -def running(name, - image=None, - skip_translate=None, - ignore_collisions=False, - validate_ip_addrs=True, - force=False, - watch_action='force', - start=True, - shutdown_timeout=None, - client_timeout=salt.utils.docker.CLIENT_TIMEOUT, - networks=None, - **kwargs): - ''' +def running( + name, + image=None, + skip_translate=None, + ignore_collisions=False, + validate_ip_addrs=True, + force=False, + watch_action="force", + start=True, + shutdown_timeout=None, + client_timeout=salt.utils.docker.CLIENT_TIMEOUT, + networks=None, + **kwargs +): + """ Ensure that a container with a specific configuration is present and running @@ -1672,15 +1672,12 @@ def running(name, docker_container.running: - image: bar/baz:latest - working_dir: /var/log/nginx - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if image is None: - ret['result'] = False - ret['comment'] = 'The \'image\' argument is required' + ret["result"] = False + ret["comment"] = "The 'image' argument is required" return ret elif not isinstance(image, six.string_types): image = six.text_type(image) @@ -1691,37 +1688,37 @@ def running(name, configured_networks = networks networks = _parse_networks(networks) if networks: - kwargs['networks'] = networks + kwargs["networks"] = networks image_id = _resolve_image(ret, image, client_timeout) except CommandExecutionError as exc: - ret['result'] = False + ret["result"] = False if exc.info is not None: return _format_comments(ret, exc.info) else: - ret['comment'] = exc.__str__() + ret["comment"] = exc.__str__() return ret comments = [] # Pop off the send_signal argument passed by the watch requisite - send_signal = kwargs.pop('send_signal', False) + send_signal = kwargs.pop("send_signal", False) try: - current_image_id = __salt__['docker.inspect_container'](name)['Image'] + current_image_id = __salt__["docker.inspect_container"](name)["Image"] except CommandExecutionError: current_image_id = None except KeyError: - ret['result'] = False + ret["result"] = False comments.append( - 'Unable to detect current image for container \'{0}\'. ' - 'This might be due to a change in the Docker API.'.format(name) + "Unable to detect current image for container '{0}'. " + "This might be due to a change in the Docker API.".format(name) ) return _format_comments(ret, comments) # Shorthand to make the below code more understandable exists = current_image_id is not None - pre_state = __salt__['docker.state'](name) if exists else None + pre_state = __salt__["docker.state"](name) if exists else None # If skip_comparison is True, we're definitely going to be using the temp # container as the new container (because we're forcing the change, or @@ -1729,16 +1726,15 @@ def running(name, # comparison between it and the new container. skip_comparison = force or not exists or current_image_id != image_id - if skip_comparison and __opts__['test']: - ret['result'] = None + if skip_comparison and __opts__["test"]: + ret["result"] = None if force: - ret['changes']['forced_update'] = True + ret["changes"]["forced_update"] = True elif current_image_id != image_id: - ret['changes']['image'] = {'old': current_image_id, 'new': image_id} + ret["changes"]["image"] = {"old": current_image_id, "new": image_id} comments.append( - 'Container \'{0}\' would be {1}'.format( - name, - 'created' if not exists else 'replaced' + "Container '{0}' would be {1}".format( + name, "created" if not exists else "replaced" ) ) return _format_comments(ret, comments) @@ -1746,58 +1742,62 @@ def running(name, # Create temp container (or just create the named container if the # container does not already exist) try: - temp_container = __salt__['docker.create']( + temp_container = __salt__["docker.create"]( image, name=name if not exists else None, skip_translate=skip_translate, ignore_collisions=ignore_collisions, validate_ip_addrs=validate_ip_addrs, client_timeout=client_timeout, - **kwargs) - temp_container_name = temp_container['Name'] + **kwargs + ) + temp_container_name = temp_container["Name"] except KeyError as exc: - ret['result'] = False + ret["result"] = False comments.append( - 'Key \'{0}\' missing from API response, this may be due to a ' - 'change in the Docker Remote API. Please report this on the ' - 'SaltStack issue tracker if it has not already been reported.' - .format(exc) + "Key '{0}' missing from API response, this may be due to a " + "change in the Docker Remote API. Please report this on the " + "SaltStack issue tracker if it has not already been reported.".format(exc) ) return _format_comments(ret, comments) except Exception as exc: # pylint: disable=broad-except - ret['result'] = False + ret["result"] = False msg = exc.__str__() - if isinstance(exc, CommandExecutionError) \ - and isinstance(exc.info, dict) and 'invalid' in exc.info: + if ( + isinstance(exc, CommandExecutionError) + and isinstance(exc.info, dict) + and "invalid" in exc.info + ): msg += ( - '\n\nIf you feel this information is incorrect, the ' - 'skip_translate argument can be used to skip input ' - 'translation for the argument(s) identified as invalid. See ' - 'the documentation for details.' + "\n\nIf you feel this information is incorrect, the " + "skip_translate argument can be used to skip input " + "translation for the argument(s) identified as invalid. See " + "the documentation for details." ) comments.append(msg) return _format_comments(ret, comments) def _replace(orig, new): - rm_kwargs = {'stop': True} + rm_kwargs = {"stop": True} if shutdown_timeout is not None: - rm_kwargs['timeout'] = shutdown_timeout - ret['changes'].setdefault('container_id', {})['removed'] = \ - __salt__['docker.rm'](name, **rm_kwargs) + rm_kwargs["timeout"] = shutdown_timeout + ret["changes"].setdefault("container_id", {})["removed"] = __salt__[ + "docker.rm" + ](name, **rm_kwargs) try: - result = __salt__['docker.rename'](new, orig) + result = __salt__["docker.rename"](new, orig) except CommandExecutionError as exc: result = False - comments.append('Failed to rename temp container: {0}'.format(exc)) + comments.append("Failed to rename temp container: {0}".format(exc)) if result: - comments.append('Replaced container \'{0}\''.format(orig)) + comments.append("Replaced container '{0}'".format(orig)) else: - comments.append('Failed to replace container \'{0}\'') + comments.append("Failed to replace container '{0}'") return result def _delete_temp_container(): - log.debug('Removing temp container \'%s\'', temp_container_name) - __salt__['docker.rm'](temp_container_name) + log.debug("Removing temp container '%s'", temp_container_name) + __salt__["docker.rm"](temp_container_name) # If we're not skipping the comparison, then the assumption is that # temp_container will be discarded, unless the comparison reveals @@ -1805,25 +1805,24 @@ def running(name, # from being cleaned. cleanup_temp = not skip_comparison try: - pre_net_connect = __salt__['docker.inspect_container']( - name if exists else temp_container_name) + pre_net_connect = __salt__["docker.inspect_container"]( + name if exists else temp_container_name + ) for net_name, net_conf in six.iteritems(networks): try: - __salt__['docker.connect_container_to_network']( - temp_container_name, - net_name, - **net_conf) + __salt__["docker.connect_container_to_network"]( + temp_container_name, net_name, **net_conf + ) except CommandExecutionError as exc: # Shouldn't happen, stopped docker containers can be # attached to networks even if the static IP lies outside # of the network's subnet. An exception will be raised once # you try to start the container, however. - ret['result'] = False + ret["result"] = False comments.append(exc.__str__()) return _format_comments(ret, comments) - post_net_connect = __salt__['docker.inspect_container']( - temp_container_name) + post_net_connect = __salt__["docker.inspect_container"](temp_container_name) if configured_networks is not None: # Use set arithmetic to determine the networks which are connected @@ -1834,48 +1833,47 @@ def running(name, # it's not sufficient to do a boolean check on the "networks" # variable. extra_nets = set( - post_net_connect.get('NetworkSettings', {}).get('Networks', {}) + post_net_connect.get("NetworkSettings", {}).get("Networks", {}) ) - set(networks) if extra_nets: for extra_net in extra_nets: - __salt__['docker.disconnect_container_from_network']( - temp_container_name, - extra_net) + __salt__["docker.disconnect_container_from_network"]( + temp_container_name, extra_net + ) # We've made changes, so we need to inspect the container again - post_net_connect = __salt__['docker.inspect_container']( - temp_container_name) + post_net_connect = __salt__["docker.inspect_container"]( + temp_container_name + ) - net_changes = __salt__['docker.compare_container_networks']( - pre_net_connect, post_net_connect) + net_changes = __salt__["docker.compare_container_networks"]( + pre_net_connect, post_net_connect + ) if not skip_comparison: - container_changes = __salt__['docker.compare_containers']( - name, - temp_container_name, - ignore='Hostname', + container_changes = __salt__["docker.compare_containers"]( + name, temp_container_name, ignore="Hostname", ) if container_changes: if _check_diff(container_changes): - ret.setdefault('warnings', []).append( - 'The detected changes may be due to incorrect ' - 'handling of arguments in earlier Salt releases. If ' - 'this warning persists after running the state ' - 'again{0}, and no changes were made to the SLS file, ' - 'then please report this.'.format( - ' without test=True' if __opts__['test'] else '' + ret.setdefault("warnings", []).append( + "The detected changes may be due to incorrect " + "handling of arguments in earlier Salt releases. If " + "this warning persists after running the state " + "again{0}, and no changes were made to the SLS file, " + "then please report this.".format( + " without test=True" if __opts__["test"] else "" ) ) - changes_ptr = ret['changes'].setdefault('container', {}) + changes_ptr = ret["changes"].setdefault("container", {}) changes_ptr.update(container_changes) - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None comments.append( - 'Container \'{0}\' would be {1}'.format( - name, - 'created' if not exists else 'replaced' + "Container '{0}' would be {1}".format( + name, "created" if not exists else "replaced" ) ) else: @@ -1884,45 +1882,44 @@ def running(name, cleanup_temp = False # Replace the container if not _replace(name, temp_container_name): - ret['result'] = False + ret["result"] = False return _format_comments(ret, comments) - ret['changes'].setdefault('container_id', {})['added'] = \ - temp_container['Id'] + ret["changes"].setdefault("container_id", {})[ + "added" + ] = temp_container["Id"] else: # No changes between existing container and temp container. # First check if a requisite is asking to send a signal to the # existing container. if send_signal: - if __opts__['test']: + if __opts__["test"]: comments.append( - 'Signal {0} would be sent to container'.format( - watch_action - ) + "Signal {0} would be sent to container".format(watch_action) ) else: try: - __salt__['docker.signal'](name, signal=watch_action) + __salt__["docker.signal"](name, signal=watch_action) except CommandExecutionError as exc: - ret['result'] = False + ret["result"] = False comments.append( - 'Failed to signal container: {0}'.format(exc) + "Failed to signal container: {0}".format(exc) ) return _format_comments(ret, comments) else: - ret['changes']['signal'] = watch_action + ret["changes"]["signal"] = watch_action comments.append( - 'Sent signal {0} to container'.format(watch_action) + "Sent signal {0} to container".format(watch_action) ) elif container_changes: if not comments: log.warning( - 'docker_container.running: detected changes without ' - 'a specific comment for container \'%s\'', name + "docker_container.running: detected changes without " + "a specific comment for container '%s'", + name, ) comments.append( - 'Container \'{0}\'{1} updated.'.format( - name, - ' would be' if __opts__['test'] else '' + "Container '{0}'{1} updated.".format( + name, " would be" if __opts__["test"] else "" ) ) else: @@ -1930,15 +1927,16 @@ def running(name, # existing container and the temp container were detected, # and no signal was sent to the container. comments.append( - 'Container \'{0}\' is already configured as specified' - .format(name) + "Container '{0}' is already configured as specified".format( + name + ) ) if net_changes: - ret['changes'].setdefault('container', {})['Networks'] = net_changes - if __opts__['test']: - ret['result'] = None - comments.append('Network configuration would be updated') + ret["changes"].setdefault("container", {})["Networks"] = net_changes + if __opts__["test"]: + ret["result"] = None + comments.append("Network configuration would be updated") elif cleanup_temp: # We only need to make network changes if the container # isn't being replaced, since we would already have @@ -1948,20 +1946,19 @@ def running(name, errors = [] disconnected = connected = False try: - if name in __salt__['docker.connected'](net_name): - __salt__['docker.disconnect_container_from_network']( - name, - net_name) + if name in __salt__["docker.connected"](net_name): + __salt__["docker.disconnect_container_from_network"]( + name, net_name + ) disconnected = True except CommandExecutionError as exc: errors.append(exc.__str__()) if net_name in networks: try: - __salt__['docker.connect_container_to_network']( - name, - net_name, - **networks[net_name]) + __salt__["docker.connect_container_to_network"]( + name, net_name, **networks[net_name] + ) connected = True except CommandExecutionError as exc: errors.append(exc.__str__()) @@ -1972,39 +1969,35 @@ def running(name, # to reconnect with the same IP address # from the old subnet. for item in list(net_changes[net_name]): - if net_changes[net_name][item]['old'] is None: + if net_changes[net_name][item]["old"] is None: # Since they'd both be None, just # delete this key from the changes del net_changes[net_name][item] else: - net_changes[net_name][item]['new'] = None + net_changes[net_name][item]["new"] = None if errors: comments.extend(errors) network_failure = True - ret['changes'].setdefault( - 'container', {}).setdefault( - 'Networks', {})[net_name] = net_changes[net_name] + ret["changes"].setdefault("container", {}).setdefault( + "Networks", {} + )[net_name] = net_changes[net_name] if disconnected and connected: comments.append( - 'Reconnected to network \'{0}\' with updated ' - 'configuration'.format(net_name) + "Reconnected to network '{0}' with updated " + "configuration".format(net_name) ) elif disconnected: comments.append( - 'Disconnected from network \'{0}\''.format( - net_name - ) + "Disconnected from network '{0}'".format(net_name) ) elif connected: - comments.append( - 'Connected to network \'{0}\''.format(net_name) - ) + comments.append("Connected to network '{0}'".format(net_name)) if network_failure: - ret['result'] = False + ret["result"] = False return _format_comments(ret, comments) finally: if cleanup_temp: @@ -2012,13 +2005,12 @@ def running(name, if skip_comparison: if not exists: - comments.append('Created container \'{0}\''.format(name)) + comments.append("Created container '{0}'".format(name)) else: if not _replace(name, temp_container): - ret['result'] = False + ret["result"] = False return _format_comments(ret, comments) - ret['changes'].setdefault('container_id', {})['added'] = \ - temp_container['Id'] + ret["changes"].setdefault("container_id", {})["added"] = temp_container["Id"] # "exists" means that a container by the specified name existed prior to # this state being run @@ -2026,91 +2018,99 @@ def running(name, # because the named container did not exist or changes were detected # "cleanup_temp" means that the container already existed and no changes # were detected, so the the temp container was discarded - if not cleanup_temp and (not exists or (exists and start)) \ - or (start and cleanup_temp and pre_state != 'running'): - if __opts__['test']: - ret['result'] = None - comments.append('Container would be started') + if ( + not cleanup_temp + and (not exists or (exists and start)) + or (start and cleanup_temp and pre_state != "running") + ): + if __opts__["test"]: + ret["result"] = None + comments.append("Container would be started") return _format_comments(ret, comments) else: try: - post_state = __salt__['docker.start'](name)['state']['new'] + post_state = __salt__["docker.start"](name)["state"]["new"] except Exception as exc: # pylint: disable=broad-except - ret['result'] = False + ret["result"] = False comments.append( - 'Failed to start container \'{0}\': \'{1}\''.format(name, exc) + "Failed to start container '{0}': '{1}'".format(name, exc) ) return _format_comments(ret, comments) else: - post_state = __salt__['docker.state'](name) + post_state = __salt__["docker.state"](name) - if not __opts__['test'] and post_state == 'running': + if not __opts__["test"] and post_state == "running": # Now that we're certain the container is running, check each modified # network to see if the network went from static (or disconnected) to # automatic IP configuration. If so, grab the automatically-assigned # IPs and munge the changes dict to include them. Note that this can # only be done after the container is started bceause automatic IPs are # assigned at runtime. - contextkey = '.'.join((name, 'docker_container.running')) + contextkey = ".".join((name, "docker_container.running")) def _get_nets(): if contextkey not in __context__: - new_container_info = \ - __salt__['docker.inspect_container'](name) + new_container_info = __salt__["docker.inspect_container"](name) __context__[contextkey] = new_container_info.get( - 'NetworkSettings', {}).get('Networks', {}) + "NetworkSettings", {} + ).get("Networks", {}) return __context__[contextkey] - autoip_keys = __salt__['config.option']( - 'docker.compare_container_networks').get('automatic', []) + + autoip_keys = __salt__["config.option"]( + "docker.compare_container_networks" + ).get("automatic", []) for net_name, net_changes in six.iteritems( - ret['changes'].get('container', {}).get('Networks', {})): - if 'IPConfiguration' in net_changes \ - and net_changes['IPConfiguration']['new'] == 'automatic': + ret["changes"].get("container", {}).get("Networks", {}) + ): + if ( + "IPConfiguration" in net_changes + and net_changes["IPConfiguration"]["new"] == "automatic" + ): for key in autoip_keys: val = _get_nets().get(net_name, {}).get(key) if val: - net_changes[key] = {'old': None, 'new': val} + net_changes[key] = {"old": None, "new": val} try: - net_changes.pop('IPConfiguration') + net_changes.pop("IPConfiguration") except KeyError: pass __context__.pop(contextkey, None) if pre_state != post_state: - ret['changes']['state'] = {'old': pre_state, 'new': post_state} + ret["changes"]["state"] = {"old": pre_state, "new": post_state} if pre_state is not None: comments.append( - 'State changed from \'{0}\' to \'{1}\''.format( - pre_state, post_state - ) + "State changed from '{0}' to '{1}'".format(pre_state, post_state) ) if exists and current_image_id != image_id: - comments.append('Container has a new image') - ret['changes']['image'] = {'old': current_image_id, 'new': image_id} + comments.append("Container has a new image") + ret["changes"]["image"] = {"old": current_image_id, "new": image_id} - if post_state != 'running' and start: - ret['result'] = False - comments.append('Container is not running') + if post_state != "running" and start: + ret["result"] = False + comments.append("Container is not running") return _format_comments(ret, comments) -def run(name, - image=None, - onlyif=None, - unless=None, - creates=None, - bg=False, - failhard=True, - replace=False, - force=False, - skip_translate=None, - ignore_collisions=False, - validate_ip_addrs=True, - client_timeout=salt.utils.docker.CLIENT_TIMEOUT, - **kwargs): - ''' +def run( + name, + image=None, + onlyif=None, + unless=None, + creates=None, + bg=False, + failhard=True, + replace=False, + force=False, + skip_translate=None, + ignore_collisions=False, + validate_ip_addrs=True, + client_timeout=salt.utils.docker.CLIENT_TIMEOUT, + **kwargs +): + """ .. versionadded:: 2018.3.0 .. note:: @@ -2200,24 +2200,19 @@ def run(name, - mynet - require: - docker_network: mynet - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} kwargs = salt.utils.args.clean_kwargs(**kwargs) - for unsupported in ('watch_action', 'start', 'shutdown_timeout', 'follow'): + for unsupported in ("watch_action", "start", "shutdown_timeout", "follow"): if unsupported in kwargs: - ret['result'] = False - ret['comment'] = 'The \'{0}\' argument is not supported'.format( - unsupported - ) + ret["result"] = False + ret["comment"] = "The '{0}' argument is not supported".format(unsupported) return ret if image is None: - ret['result'] = False - ret['comment'] = 'The \'image\' argument is required' + ret["result"] = False + ret["comment"] = "The 'image' argument is required" return ret elif not isinstance(image, six.string_types): image = six.text_type(image) @@ -2228,15 +2223,15 @@ def run(name, return ret try: - if 'networks' in kwargs and kwargs['networks'] is not None: - kwargs['networks'] = _parse_networks(kwargs['networks']) + if "networks" in kwargs and kwargs["networks"] is not None: + kwargs["networks"] = _parse_networks(kwargs["networks"]) _resolve_image(ret, image, client_timeout) except CommandExecutionError as exc: - ret['result'] = False + ret["result"] = False if exc.info is not None: return _format_comments(ret, exc.info) else: - ret['comment'] = exc.__str__() + ret["comment"] = exc.__str__() return ret cret = mod_run_check(onlyif, unless, creates) @@ -2244,10 +2239,10 @@ def run(name, ret.update(cret) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Container would be run{0}'.format( - ' in the background' if bg else '' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Container would be run{0}".format( + " in the background" if bg else "" ) return ret @@ -2263,17 +2258,17 @@ def run(name, # the exit code can be retrieved). We can then remove the container # manually if auto_remove is True. remove = None - for item in ('auto_remove', 'rm'): + for item in ("auto_remove", "rm"): try: val = kwargs.pop(item) except KeyError: continue if remove is not None: if not ignore_collisions: - ret['result'] = False - ret['comment'] = ( - '\'rm\' is an alias for \'auto_remove\', they cannot ' - 'both be used' + ret["result"] = False + ret["comment"] = ( + "'rm' is an alias for 'auto_remove', they cannot " + "both be used" ) return ret else: @@ -2281,12 +2276,12 @@ def run(name, if remove is not None: # We popped off the value, so replace it with False - kwargs['auto_remove'] = False + kwargs["auto_remove"] = False else: remove = False try: - ret['changes'] = __salt__['docker.run_container']( + ret["changes"] = __salt__["docker.run_container"]( image, name=name, skip_translate=skip_translate, @@ -2296,46 +2291,49 @@ def run(name, bg=bg, replace=replace, force=force, - **kwargs) + **kwargs + ) except Exception as exc: # pylint: disable=broad-except - log.exception('Encountered error running container') - ret['result'] = False - ret['comment'] = 'Encountered error running container: {0}'.format(exc) + log.exception("Encountered error running container") + ret["result"] = False + ret["comment"] = "Encountered error running container: {0}".format(exc) else: if bg: - ret['comment'] = 'Container was run in the background' + ret["comment"] = "Container was run in the background" else: try: - retcode = ret['changes']['ExitCode'] + retcode = ret["changes"]["ExitCode"] except KeyError: pass else: - ret['result'] = False if failhard and retcode != 0 else True - ret['comment'] = ( - 'Container ran and exited with a return code of ' - '{0}'.format(retcode) + ret["result"] = False if failhard and retcode != 0 else True + ret["comment"] = ( + "Container ran and exited with a return code of " + "{0}".format(retcode) ) if remove: - id_ = ret.get('changes', {}).get('Id') + id_ = ret.get("changes", {}).get("Id") if id_: try: - __salt__['docker.rm'](ret['changes']['Id']) + __salt__["docker.rm"](ret["changes"]["Id"]) except CommandExecutionError as exc: - ret.setdefault('warnings', []).append( - 'Failed to auto_remove container: {0}'.format(exc) + ret.setdefault("warnings", []).append( + "Failed to auto_remove container: {0}".format(exc) ) return ret -def stopped(name=None, - containers=None, - shutdown_timeout=None, - unpause=False, - error_on_absent=True, - **kwargs): - ''' +def stopped( + name=None, + containers=None, + shutdown_timeout=None, + unpause=False, + error_on_absent=True, + **kwargs +): + """ Ensure that a container (or containers) is stopped name @@ -2381,18 +2379,15 @@ def stopped(name=None, error_on_absent : True By default, this state will return an error if any of the specified containers are absent. Set this to ``False`` to suppress that error. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if not name and not containers: - ret['comment'] = 'One of \'name\' and \'containers\' must be provided' + ret["comment"] = "One of 'name' and 'containers' must be provided" return ret if containers is not None: if not isinstance(containers, list): - ret['comment'] = 'containers must be a list' + ret["comment"] = "containers must be a list" return ret targets = [] for target in containers: @@ -2408,84 +2403,80 @@ def stopped(name=None, containers = {} for target in targets: try: - c_state = __salt__['docker.state'](target) + c_state = __salt__["docker.state"](target) except CommandExecutionError: - containers.setdefault('absent', []).append(target) + containers.setdefault("absent", []).append(target) else: containers.setdefault(c_state, []).append(target) errors = [] - if error_on_absent and 'absent' in containers: + if error_on_absent and "absent" in containers: errors.append( - 'The following container(s) are absent: {0}'.format( - ', '.join(containers['absent']) + "The following container(s) are absent: {0}".format( + ", ".join(containers["absent"]) ) ) - if not unpause and 'paused' in containers: - ret['result'] = False + if not unpause and "paused" in containers: + ret["result"] = False errors.append( - 'The following container(s) are paused: {0}'.format( - ', '.join(containers['paused']) + "The following container(s) are paused: {0}".format( + ", ".join(containers["paused"]) ) ) if errors: - ret['result'] = False - ret['comment'] = '. '.join(errors) + ret["result"] = False + ret["comment"] = ". ".join(errors) return ret - to_stop = containers.get('running', []) + containers.get('paused', []) + to_stop = containers.get("running", []) + containers.get("paused", []) if not to_stop: - ret['result'] = True + ret["result"] = True if len(targets) == 1: - ret['comment'] = 'Container \'{0}\' is '.format(targets[0]) + ret["comment"] = "Container '{0}' is ".format(targets[0]) else: - ret['comment'] = 'All specified containers are ' - if 'absent' in containers: - ret['comment'] += 'absent or ' - ret['comment'] += 'not running' + ret["comment"] = "All specified containers are " + if "absent" in containers: + ret["comment"] += "absent or " + ret["comment"] += "not running" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = ( - 'The following container(s) will be stopped: {0}' - .format(', '.join(to_stop)) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The following container(s) will be stopped: {0}".format( + ", ".join(to_stop) ) return ret stop_errors = [] for target in to_stop: - stop_kwargs = {'unpause': unpause} + stop_kwargs = {"unpause": unpause} if shutdown_timeout: - stop_kwargs['timeout'] = shutdown_timeout - changes = __salt__['docker.stop'](target, **stop_kwargs) - if changes['result'] is True: - ret['changes'][target] = changes + stop_kwargs["timeout"] = shutdown_timeout + changes = __salt__["docker.stop"](target, **stop_kwargs) + if changes["result"] is True: + ret["changes"][target] = changes else: - if 'comment' in changes: - stop_errors.append(changes['comment']) + if "comment" in changes: + stop_errors.append(changes["comment"]) else: - stop_errors.append( - 'Failed to stop container \'{0}\''.format(target) - ) + stop_errors.append("Failed to stop container '{0}'".format(target)) if stop_errors: - ret['comment'] = '; '.join(stop_errors) + ret["comment"] = "; ".join(stop_errors) return ret - ret['result'] = True - ret['comment'] = ( - 'The following container(s) were stopped: {0}' - .format(', '.join(to_stop)) + ret["result"] = True + ret["comment"] = "The following container(s) were stopped: {0}".format( + ", ".join(to_stop) ) return ret def absent(name, force=False): - ''' + """ Ensure that a container is absent name @@ -2507,115 +2498,122 @@ def absent(name, force=False): - foo - bar - baz - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if name not in __salt__['docker.list_containers'](all=True): - ret['result'] = True - ret['comment'] = 'Container \'{0}\' does not exist'.format(name) + if name not in __salt__["docker.list_containers"](all=True): + ret["result"] = True + ret["comment"] = "Container '{0}' does not exist".format(name) return ret - pre_state = __salt__['docker.state'](name) - if pre_state != 'stopped' and not force: - ret['comment'] = ('Container is running, set force to True to ' - 'forcibly remove it') + pre_state = __salt__["docker.state"](name) + if pre_state != "stopped" and not force: + ret["comment"] = ( + "Container is running, set force to True to " "forcibly remove it" + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Container \'{0}\' will be removed'.format(name)) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Container '{0}' will be removed".format(name) return ret try: - ret['changes']['removed'] = __salt__['docker.rm'](name, force=force) + ret["changes"]["removed"] = __salt__["docker.rm"](name, force=force) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = ('Failed to remove container \'{0}\': {1}' - .format(name, exc)) + ret["comment"] = "Failed to remove container '{0}': {1}".format(name, exc) return ret - if name in __salt__['docker.list_containers'](all=True): - ret['comment'] = 'Failed to remove container \'{0}\''.format(name) + if name in __salt__["docker.list_containers"](all=True): + ret["comment"] = "Failed to remove container '{0}'".format(name) else: - if force and pre_state != 'stopped': - method = 'Forcibly' + if force and pre_state != "stopped": + method = "Forcibly" else: - method = 'Successfully' - ret['comment'] = '{0} removed container \'{1}\''.format(method, name) - ret['result'] = True + method = "Successfully" + ret["comment"] = "{0} removed container '{1}'".format(method, name) + ret["result"] = True return ret def mod_run_check(onlyif, unless, creates): - ''' + """ Execute the onlyif/unless/creates logic. Returns a result dict if any of the checks fail, otherwise returns True - ''' - cmd_kwargs = {'use_vt': False, 'bg': False} + """ + cmd_kwargs = {"use_vt": False, "bg": False} if onlyif is not None: if isinstance(onlyif, six.string_types): onlyif = [onlyif] - if not isinstance(onlyif, list) \ - or not all(isinstance(x, six.string_types) for x in onlyif): - return {'comment': 'onlyif is not a string or list of strings', - 'skip_watch': True, - 'result': True} + if not isinstance(onlyif, list) or not all( + isinstance(x, six.string_types) for x in onlyif + ): + return { + "comment": "onlyif is not a string or list of strings", + "skip_watch": True, + "result": True, + } for entry in onlyif: - retcode = __salt__['cmd.retcode']( - entry, - ignore_retcode=True, - python_shell=True) + retcode = __salt__["cmd.retcode"]( + entry, ignore_retcode=True, python_shell=True + ) if retcode != 0: return { - 'comment': 'onlyif command {0} returned exit code of {1}' - .format(entry, retcode), - 'skip_watch': True, - 'result': True + "comment": "onlyif command {0} returned exit code of {1}".format( + entry, retcode + ), + "skip_watch": True, + "result": True, } if unless is not None: if isinstance(unless, six.string_types): unless = [unless] - if not isinstance(unless, list) \ - or not all(isinstance(x, six.string_types) for x in unless): - return {'comment': 'unless is not a string or list of strings', - 'skip_watch': True, - 'result': True} + if not isinstance(unless, list) or not all( + isinstance(x, six.string_types) for x in unless + ): + return { + "comment": "unless is not a string or list of strings", + "skip_watch": True, + "result": True, + } for entry in unless: - retcode = __salt__['cmd.retcode']( - entry, - ignore_retcode=True, - python_shell=True) + retcode = __salt__["cmd.retcode"]( + entry, ignore_retcode=True, python_shell=True + ) if retcode == 0: return { - 'comment': 'unless command {0} returned exit code of {1}' - .format(entry, retcode), - 'skip_watch': True, - 'result': True + "comment": "unless command {0} returned exit code of {1}".format( + entry, retcode + ), + "skip_watch": True, + "result": True, } if creates is not None: if isinstance(creates, six.string_types): creates = [creates] - if not isinstance(creates, list) \ - or not all(isinstance(x, six.string_types) for x in creates): - return {'comment': 'creates is not a string or list of strings', - 'skip_watch': True, - 'result': True} + if not isinstance(creates, list) or not all( + isinstance(x, six.string_types) for x in creates + ): + return { + "comment": "creates is not a string or list of strings", + "skip_watch": True, + "result": True, + } if all(os.path.exists(x) for x in creates): - return {'comment': 'All specified paths in \'creates\' ' - 'argument exist', - 'result': True} + return { + "comment": "All specified paths in 'creates' " "argument exist", + "result": True, + } # No reason to stop, return True return True def mod_watch(name, sfun=None, **kwargs): - ''' + """ The docker_container watcher, called to invoke the watch command. .. note:: @@ -2623,24 +2621,25 @@ def mod_watch(name, sfun=None, **kwargs): :ref:`requisite <requisites>`. It should not be called directly. Parameters for this function should be set by the state being triggered. - ''' - if sfun == 'running': + """ + if sfun == "running": watch_kwargs = copy.deepcopy(kwargs) - if watch_kwargs.get('watch_action', 'force') == 'force': - watch_kwargs['force'] = True + if watch_kwargs.get("watch_action", "force") == "force": + watch_kwargs["force"] = True else: - watch_kwargs['send_signal'] = True - watch_kwargs['force'] = False + watch_kwargs["send_signal"] = True + watch_kwargs["force"] = False return running(name, **watch_kwargs) - if sfun == 'stopped': + if sfun == "stopped": return stopped(name, **salt.utils.args.clean_kwargs(**kwargs)) - if sfun == 'run': + if sfun == "run": return run(name, **salt.utils.args.clean_kwargs(**kwargs)) - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': ('watch requisite is not' - ' implemented for {0}'.format(sfun))} + return { + "name": name, + "changes": {}, + "result": False, + "comment": ("watch requisite is not" " implemented for {0}".format(sfun)), + } diff --git a/salt/states/docker_image.py b/salt/states/docker_image.py index 1ced24da52a..dfcc48e6169 100644 --- a/salt/states/docker_image.py +++ b/salt/states/docker_image.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Docker images .. versionadded:: 2017.7.0 @@ -34,49 +34,53 @@ module (formerly called **dockerng**) in the 2017.7.0 release. To pull from a Docker registry, authentication must be configured. See :ref:`here <docker-authentication>` for more information on how to configure access to docker registries in :ref:`Pillar <pillar>` data. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.args + # Import salt libs import salt.utils.docker -import salt.utils.args -from salt.ext.six.moves import zip -from salt.ext import six from salt.exceptions import CommandExecutionError +from salt.ext import six +from salt.ext.six.moves import zip # Enable proper logging log = logging.getLogger(__name__) # pylint: disable=invalid-name # Define the module's virtual name -__virtualname__ = 'docker_image' -__virtual_aliases__ = ('moby_image',) +__virtualname__ = "docker_image" +__virtual_aliases__ = ("moby_image",) def __virtual__(): - ''' + """ Only load if the docker execution module is available - ''' - if 'docker.version' in __salt__: + """ + if "docker.version" in __salt__: return __virtualname__ - return (False, __salt__.missing_fun_string('docker.version')) + return (False, __salt__.missing_fun_string("docker.version")) -def present(name, - tag=None, - build=None, - load=None, - force=False, - insecure_registry=False, - client_timeout=salt.utils.docker.CLIENT_TIMEOUT, - dockerfile=None, - sls=None, - base='opensuse/python', - saltenv='base', - pillarenv=None, - pillar=None, - **kwargs): - ''' +def present( + name, + tag=None, + build=None, + load=None, + force=False, + insecure_registry=False, + client_timeout=salt.utils.docker.CLIENT_TIMEOUT, + dockerfile=None, + sls=None, + base="opensuse/python", + saltenv="base", + pillarenv=None, + pillar=None, + **kwargs +): + """ .. versionchanged:: 2018.3.0 The ``tag`` argument has been added. It is now required unless pulling from a registry. @@ -128,8 +132,8 @@ def present(name, The ``tag`` must be manually specified using the ``tag`` argument. load - Loads a tar archive created with :py:func:`docker.load - <salt.modules.dockermod.load>` (or the ``docker load`` Docker CLI + Loads a tar archive created with :py:func:`docker.save + <salt.modules.dockermod.save>` (or the ``docker save`` Docker CLI command), and assigns it the specified repo and tag. .. code-block:: yaml @@ -208,11 +212,8 @@ def present(name, ``pillar_roots`` or an external Pillar source. .. versionadded:: 2018.3.0 - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if not isinstance(name, six.string_types): name = six.text_type(name) @@ -220,163 +221,158 @@ def present(name, # At most one of the args that result in an image being built can be used num_build_args = len([x for x in (build, load, sls) if x is not None]) if num_build_args > 1: - ret['comment'] = \ - 'Only one of \'build\', \'load\', or \'sls\' is permitted.' + ret["comment"] = "Only one of 'build', 'load', or 'sls' is permitted." return ret elif num_build_args == 1: # If building, we need the tag to be specified if not tag: - ret['comment'] = ( - 'The \'tag\' argument is required if any one of \'build\', ' - '\'load\', or \'sls\' is used.' + ret["comment"] = ( + "The 'tag' argument is required if any one of 'build', " + "'load', or 'sls' is used." ) return ret if not isinstance(tag, six.string_types): tag = six.text_type(tag) - full_image = ':'.join((name, tag)) + full_image = ":".join((name, tag)) else: if tag: - name = '{0}:{1}'.format(name, tag) + name = "{0}:{1}".format(name, tag) full_image = name try: - image_info = __salt__['docker.inspect_image'](full_image) + image_info = __salt__["docker.inspect_image"](full_image) except CommandExecutionError as exc: msg = exc.__str__() - if '404' in msg: + if "404" in msg: # Image not present image_info = None else: - ret['comment'] = msg + ret["comment"] = msg return ret if image_info is not None: # Specified image is present if not force: - ret['result'] = True - ret['comment'] = 'Image {0} already present'.format(full_image) + ret["result"] = True + ret["comment"] = "Image {0} already present".format(full_image) return ret if build or sls: - action = 'built' + action = "built" elif load: - action = 'loaded' + action = "loaded" else: - action = 'pulled' + action = "pulled" - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if (image_info is not None and force) or image_info is None: - ret['comment'] = 'Image {0} will be {1}'.format(full_image, action) + ret["comment"] = "Image {0} will be {1}".format(full_image, action) return ret if build: # Get the functions default value and args - argspec = salt.utils.args.get_function_argspec(__salt__['docker.build']) + argspec = salt.utils.args.get_function_argspec(__salt__["docker.build"]) # Map any if existing args from kwargs into the build_args dictionary build_args = dict(list(zip(argspec.args, argspec.defaults))) for k in build_args: - if k in kwargs.get('kwargs', {}): - build_args[k] = kwargs.get('kwargs', {}).get(k) + if k in kwargs.get("kwargs", {}): + build_args[k] = kwargs.get("kwargs", {}).get(k) try: # map values passed from the state to the build args - build_args['path'] = build - build_args['repository'] = name - build_args['tag'] = tag - build_args['dockerfile'] = dockerfile - image_update = __salt__['docker.build'](**build_args) + build_args["path"] = build + build_args["repository"] = name + build_args["tag"] = tag + build_args["dockerfile"] = dockerfile + image_update = __salt__["docker.build"](**build_args) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = ( - 'Encountered error building {0} as {1}: {2}'.format( - build, full_image, exc - ) + ret["comment"] = "Encountered error building {0} as {1}: {2}".format( + build, full_image, exc ) return ret - if image_info is None or image_update['Id'] != image_info['Id'][:12]: - ret['changes'] = image_update + if image_info is None or image_update["Id"] != image_info["Id"][:12]: + ret["changes"] = image_update elif sls: _locals = locals() - sls_build_kwargs = {k: _locals[k] for k in ('saltenv', 'pillarenv', 'pillar') - if _locals[k] is not None} + sls_build_kwargs = { + k: _locals[k] + for k in ("saltenv", "pillarenv", "pillar") + if _locals[k] is not None + } try: - image_update = __salt__['docker.sls_build'](repository=name, - tag=tag, - base=base, - mods=sls, - **sls_build_kwargs) + image_update = __salt__["docker.sls_build"]( + repository=name, tag=tag, base=base, mods=sls, **sls_build_kwargs + ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = ( - 'Encountered error using SLS {0} for building {1}: {2}' - .format(sls, full_image, exc) + ret[ + "comment" + ] = "Encountered error using SLS {0} for building {1}: {2}".format( + sls, full_image, exc ) return ret - if image_info is None or image_update['Id'] != image_info['Id'][:12]: - ret['changes'] = image_update + if image_info is None or image_update["Id"] != image_info["Id"][:12]: + ret["changes"] = image_update elif load: try: - image_update = __salt__['docker.load'](path=load, - repository=name, - tag=tag) + image_update = __salt__["docker.load"](path=load, repository=name, tag=tag) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = ( - 'Encountered error loading {0} as {1}: {2}' - .format(load, full_image, exc) + ret["comment"] = "Encountered error loading {0} as {1}: {2}".format( + load, full_image, exc ) return ret - if image_info is None or image_update.get('Layers', []): - ret['changes'] = image_update + if image_info is None or image_update.get("Layers", []): + ret["changes"] = image_update else: try: - image_update = __salt__['docker.pull']( - name, - insecure_registry=insecure_registry, - client_timeout=client_timeout + image_update = __salt__["docker.pull"]( + name, insecure_registry=insecure_registry, client_timeout=client_timeout ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = \ - 'Encountered error pulling {0}: {1}'.format(full_image, exc) + ret["comment"] = "Encountered error pulling {0}: {1}".format( + full_image, exc + ) return ret - if (image_info is not None and image_info['Id'][:12] == image_update - .get('Layers', {}) - .get('Already_Pulled', [None])[0]): + if ( + image_info is not None + and image_info["Id"][:12] + == image_update.get("Layers", {}).get("Already_Pulled", [None])[0] + ): # Image was pulled again (because of force) but was also # already there. No new image was available on the registry. pass - elif image_info is None or image_update.get('Layers', {}).get('Pulled'): + elif image_info is None or image_update.get("Layers", {}).get("Pulled"): # Only add to the changes dict if layers were pulled - ret['changes'] = image_update + ret["changes"] = image_update error = False try: - __salt__['docker.inspect_image'](full_image) + __salt__["docker.inspect_image"](full_image) except CommandExecutionError as exc: msg = exc.__str__() - if '404' not in msg: - error = 'Failed to inspect image \'{0}\' after it was {1}: {2}'.format( + if "404" not in msg: + error = "Failed to inspect image '{0}' after it was {1}: {2}".format( full_image, action, msg ) if error: - ret['comment'] = error + ret["comment"] = error else: - ret['result'] = True - if not ret['changes']: - ret['comment'] = ( - 'Image \'{0}\' was {1}, but there were no changes'.format( - name, action - ) + ret["result"] = True + if not ret["changes"]: + ret["comment"] = "Image '{0}' was {1}, but there were no changes".format( + name, action ) else: - ret['comment'] = 'Image \'{0}\' was {1}'.format(full_image, action) + ret["comment"] = "Image '{0}' was {1}".format(full_image, action) return ret def absent(name=None, images=None, force=False): - ''' + """ Ensure that an image is absent from the Minion. Image names can be specified either using ``repo:tag`` notation, or just the repo name (in which case a tag of ``latest`` is assumed). @@ -428,14 +424,11 @@ def absent(name=None, images=None, force=False): For more granular control, setting a pillar variable named ``docker.force.image_name`` will affect only the named image. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if not name and not images: - ret['comment'] = 'One of \'name\' and \'images\' must be provided' + ret["comment"] = "One of 'name' and 'images' must be provided" return ret elif images is not None: targets = images @@ -444,66 +437,59 @@ def absent(name=None, images=None, force=False): to_delete = [] for target in targets: - resolved_tag = __salt__['docker.resolve_tag'](target) + resolved_tag = __salt__["docker.resolve_tag"](target) if resolved_tag is not False: to_delete.append(resolved_tag) if not to_delete: - ret['result'] = True + ret["result"] = True if len(targets) == 1: - ret['comment'] = 'Image {0} is not present'.format(name) + ret["comment"] = "Image {0} is not present".format(name) else: - ret['comment'] = 'All specified images are not present' + ret["comment"] = "All specified images are not present" return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if len(to_delete) == 1: - ret['comment'] = 'Image {0} will be removed'.format(to_delete[0]) + ret["comment"] = "Image {0} will be removed".format(to_delete[0]) else: - ret['comment'] = ( - 'The following images will be removed: {0}'.format( - ', '.join(to_delete) - ) + ret["comment"] = "The following images will be removed: {0}".format( + ", ".join(to_delete) ) return ret - result = __salt__['docker.rmi'](*to_delete, force=force) - post_tags = __salt__['docker.list_tags']() + result = __salt__["docker.rmi"](*to_delete, force=force) + post_tags = __salt__["docker.list_tags"]() failed = [x for x in to_delete if x in post_tags] if failed: if [x for x in to_delete if x not in post_tags]: - ret['changes'] = result - ret['comment'] = ( - 'The following image(s) failed to be removed: {0}'.format( - ', '.join(failed) - ) + ret["changes"] = result + ret["comment"] = "The following image(s) failed to be removed: {0}".format( + ", ".join(failed) ) else: - ret['comment'] = 'None of the specified images were removed' - if 'Errors' in result: - ret['comment'] += ( - '. The following errors were encountered: {0}' - .format('; '.join(result['Errors'])) + ret["comment"] = "None of the specified images were removed" + if "Errors" in result: + ret["comment"] += ". The following errors were encountered: {0}".format( + "; ".join(result["Errors"]) ) else: - ret['changes'] = result + ret["changes"] = result if len(to_delete) == 1: - ret['comment'] = 'Image {0} was removed'.format(to_delete[0]) + ret["comment"] = "Image {0} was removed".format(to_delete[0]) else: - ret['comment'] = ( - 'The following images were removed: {0}'.format( - ', '.join(to_delete) - ) + ret["comment"] = "The following images were removed: {0}".format( + ", ".join(to_delete) ) - ret['result'] = True + ret["result"] = True return ret def mod_watch(name, sfun=None, **kwargs): - ''' + """ The docker_image watcher, called to invoke the watch command. .. note:: @@ -511,14 +497,15 @@ def mod_watch(name, sfun=None, **kwargs): :ref:`requisite <requisites>`. It should not be called directly. Parameters for this function should be set by the state being triggered. - ''' - if sfun == 'present': + """ + if sfun == "present": # Force image to be updated - kwargs['force'] = True + kwargs["force"] = True return present(name, **kwargs) - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'watch requisite is not implemented for ' - '{0}'.format(sfun)} + return { + "name": name, + "changes": {}, + "result": False, + "comment": "watch requisite is not implemented for " "{0}".format(sfun), + } diff --git a/salt/states/docker_network.py b/salt/states/docker_network.py index 6f15868609b..823e01221f2 100644 --- a/salt/states/docker_network.py +++ b/salt/states/docker_network.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Docker networks .. versionadded:: 2017.7.0 @@ -29,8 +29,9 @@ Management of Docker networks These states were moved from the :mod:`docker <salt.states.docker>` state module (formerly called **dockerng**) in the 2017.7.0 release. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import random @@ -38,68 +39,69 @@ import string # Import Salt libs import salt.utils.docker.translate.network +from salt._compat import ipaddress from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -from salt._compat import ipaddress # Enable proper logging log = logging.getLogger(__name__) # pylint: disable=invalid-name # Define the module's virtual name -__virtualname__ = 'docker_network' -__virtual_aliases__ = ('moby_network',) +__virtualname__ = "docker_network" +__virtual_aliases__ = ("moby_network",) def __virtual__(): - ''' + """ Only load if the docker execution module is available - ''' - if 'docker.version' in __salt__: + """ + if "docker.version" in __salt__: return __virtualname__ - return (False, __salt__.missing_fun_string('docker.version')) + return (False, __salt__.missing_fun_string("docker.version")) def _normalize_pools(existing, desired): - pools = {'existing': {4: None, 6: None}, - 'desired': {4: None, 6: None}} + pools = {"existing": {4: None, 6: None}, "desired": {4: None, 6: None}} - for pool in existing['Config']: - subnet = ipaddress.ip_network(pool.get('Subnet')) - pools['existing'][subnet.version] = pool + for pool in existing["Config"]: + subnet = ipaddress.ip_network(pool.get("Subnet")) + pools["existing"][subnet.version] = pool - for pool in desired['Config']: - subnet = ipaddress.ip_network(pool.get('Subnet')) - if pools['desired'][subnet.version] is not None: - raise ValueError( - 'Only one IPv{0} pool is permitted'.format(subnet.version) - ) + for pool in desired["Config"]: + subnet = ipaddress.ip_network(pool.get("Subnet")) + if pools["desired"][subnet.version] is not None: + raise ValueError("Only one IPv{0} pool is permitted".format(subnet.version)) else: - pools['desired'][subnet.version] = pool + pools["desired"][subnet.version] = pool - if pools['desired'][6] and not pools['desired'][4]: + if pools["desired"][6] and not pools["desired"][4]: raise ValueError( - 'An IPv4 pool is required when an IPv6 pool is used. See the ' - 'documentation for details.' + "An IPv4 pool is required when an IPv6 pool is used. See the " + "documentation for details." ) # The pools will be sorted when comparing - existing['Config'] = [pools['existing'][x] for x in (4, 6) - if pools['existing'][x] is not None] - desired['Config'] = [pools['desired'][x] for x in (4, 6) - if pools['desired'][x] is not None] + existing["Config"] = [ + pools["existing"][x] for x in (4, 6) if pools["existing"][x] is not None + ] + desired["Config"] = [ + pools["desired"][x] for x in (4, 6) if pools["desired"][x] is not None + ] -def present(name, - skip_translate=None, - ignore_collisions=False, - validate_ip_addrs=True, - containers=None, - reconnect=True, - **kwargs): - ''' +def present( + name, + skip_translate=None, + ignore_collisions=False, + validate_ip_addrs=True, + containers=None, + reconnect=True, + **kwargs +): + """ .. versionchanged:: 2018.3.0 Support added for network configuration options other than ``driver`` and ``driver_opts``, as well as IPAM configuration. @@ -523,54 +525,51 @@ def present(name, gateway: 10.0.20.1 - subnet: fe3f:2180:26:1::/123 gateway: fe3f:2180:26:1::1 - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} try: - network = __salt__['docker.inspect_network'](name) + network = __salt__["docker.inspect_network"](name) except CommandExecutionError as exc: msg = exc.__str__() - if '404' in msg: + if "404" in msg: # Network not present network = None else: - ret['comment'] = msg + ret["comment"] = msg return ret # map container's IDs to names to_connect = {} missing_containers = [] stopped_containers = [] - for cname in __utils__['args.split_input'](containers or []): + for cname in __utils__["args.split_input"](containers or []): try: - cinfo = __salt__['docker.inspect_container'](cname) + cinfo = __salt__["docker.inspect_container"](cname) except CommandExecutionError: missing_containers.append(cname) else: try: - cid = cinfo['Id'] + cid = cinfo["Id"] except KeyError: missing_containers.append(cname) else: - if not cinfo.get('State', {}).get('Running', False): + if not cinfo.get("State", {}).get("Running", False): stopped_containers.append(cname) else: - to_connect[cid] = {'Name': cname} + to_connect[cid] = {"Name": cname} if missing_containers: - ret.setdefault('warnings', []).append( - 'The following containers do not exist: {0}.'.format( - ', '.join(missing_containers) + ret.setdefault("warnings", []).append( + "The following containers do not exist: {0}.".format( + ", ".join(missing_containers) ) ) if stopped_containers: - ret.setdefault('warnings', []).append( - 'The following containers are not running: {0}.'.format( - ', '.join(stopped_containers) + ret.setdefault("warnings", []).append( + "The following containers are not running: {0}.".format( + ", ".join(stopped_containers) ) ) @@ -579,42 +578,44 @@ def present(name, disconnected_containers = {} try: - kwargs = __utils__['docker.translate_input']( + kwargs = __utils__["docker.translate_input"]( salt.utils.docker.translate.network, skip_translate=skip_translate, ignore_collisions=ignore_collisions, validate_ip_addrs=validate_ip_addrs, - **__utils__['args.clean_kwargs'](**kwargs)) + **__utils__["args.clean_kwargs"](**kwargs) + ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = exc.__str__() + ret["comment"] = exc.__str__() return ret # Separate out the IPAM config options and build the IPAM config dict ipam_kwargs = {} - ipam_kwarg_names = ['ipam', 'ipam_driver', 'ipam_opts', 'ipam_pools'] + ipam_kwarg_names = ["ipam", "ipam_driver", "ipam_opts", "ipam_pools"] ipam_kwarg_names.extend( - __salt__['docker.get_client_args']('ipam_config')['ipam_config']) + __salt__["docker.get_client_args"]("ipam_config")["ipam_config"] + ) for key in ipam_kwarg_names: try: ipam_kwargs[key] = kwargs.pop(key) except KeyError: pass - if 'ipam' in ipam_kwargs: + if "ipam" in ipam_kwargs: if len(ipam_kwargs) > 1: - ret['comment'] = ( - 'Cannot mix the \'ipam\' argument with any of the IPAM config ' - 'arguments. See documentation for details.' + ret["comment"] = ( + "Cannot mix the 'ipam' argument with any of the IPAM config " + "arguments. See documentation for details." ) return ret - ipam_config = ipam_kwargs['ipam'] + ipam_config = ipam_kwargs["ipam"] else: - ipam_pools = ipam_kwargs.pop('ipam_pools', ()) + ipam_pools = ipam_kwargs.pop("ipam_pools", ()) try: - ipam_config = __utils__['docker.create_ipam_config']( - *ipam_pools, - **ipam_kwargs) + ipam_config = __utils__["docker.create_ipam_config"]( + *ipam_pools, **ipam_kwargs + ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = exc.__str__() + ret["comment"] = exc.__str__() return ret # We'll turn this off if we decide below that creating the network is not @@ -622,18 +623,19 @@ def present(name, create_network = True if network is not None: - log.debug('Docker network \'%s\' already exists', name) + log.debug("Docker network '%s' already exists", name) # Set the comment now to say that it already exists, if we need to # recreate the network with new config we'll update the comment later. - ret['comment'] = ( - 'Network \'{0}\' already exists, and is configured ' - 'as specified'.format(name) + ret["comment"] = ( + "Network '{0}' already exists, and is configured " + "as specified".format(name) ) - log.trace('Details of docker network \'%s\': %s', name, network) + log.trace("Details of docker network '%s': %s", name, network) - temp_net_name = ''.join( - random.choice(string.ascii_lowercase) for _ in range(20)) + temp_net_name = "".join( + random.choice(string.ascii_lowercase) for _ in range(20) + ) try: # When using enable_ipv6, you *must* provide a subnet. But we don't @@ -650,51 +652,52 @@ def present(name, # reliably compare the SLS input to the existing network, as we # wouldng't know if the IPv4 subnet in the existing network was # explicitly configured or was automatically assigned by Docker. - enable_ipv6 = kwargs.pop('enable_ipv6', None) - __salt__['docker.create_network']( + enable_ipv6 = kwargs.pop("enable_ipv6", None) + __salt__["docker.create_network"]( temp_net_name, skip_translate=True, # No need to translate (already did) enable_ipv6=False, - **kwargs) + **kwargs + ) except CommandExecutionError as exc: - ret['comment'] = ( - 'Failed to create temp network for comparison: {0}'.format( - exc.__str__() - ) + ret["comment"] = "Failed to create temp network for comparison: {0}".format( + exc.__str__() ) return ret else: # Replace the value so we can use it later if enable_ipv6 is not None: - kwargs['enable_ipv6'] = enable_ipv6 + kwargs["enable_ipv6"] = enable_ipv6 try: try: - temp_net_info = __salt__['docker.inspect_network'](temp_net_name) + temp_net_info = __salt__["docker.inspect_network"](temp_net_name) except CommandExecutionError as exc: - ret['comment'] = 'Failed to inspect temp network: {0}'.format( + ret["comment"] = "Failed to inspect temp network: {0}".format( exc.__str__() ) return ret else: - temp_net_info['EnableIPv6'] = bool(enable_ipv6) + temp_net_info["EnableIPv6"] = bool(enable_ipv6) # Replace the IPAM configuration in the temp network with the IPAM # config dict we created earlier, for comparison purposes. This is # necessary because we cannot create two networks that have # overlapping subnets (the Docker Engine will throw an error). - temp_net_info['IPAM'] = ipam_config + temp_net_info["IPAM"] = ipam_config - existing_pool_count = len(network['IPAM']['Config']) - desired_pool_count = len(temp_net_info['IPAM']['Config']) + existing_pool_count = len(network["IPAM"]["Config"]) + desired_pool_count = len(temp_net_info["IPAM"]["Config"]) - is_default_pool = lambda x: True \ - if sorted(x) == ['Gateway', 'Subnet'] \ - else False + is_default_pool = ( + lambda x: True if sorted(x) == ["Gateway", "Subnet"] else False + ) - if desired_pool_count == 0 \ - and existing_pool_count == 1 \ - and is_default_pool(network['IPAM']['Config'][0]): + if ( + desired_pool_count == 0 + and existing_pool_count == 1 + and is_default_pool(network["IPAM"]["Config"][0]) + ): # If we're not explicitly configuring an IPAM pool, then we # don't care what the subnet is. Docker networks created with # no explicit IPAM configuration are assigned a single IPAM @@ -711,12 +714,11 @@ def present(name, # IPAM configuration is. At any rate, to avoid IPAM differences # when comparing the existing network to the temp network, we # need to clear the existing network's IPAM configuration. - network['IPAM']['Config'] = [] + network["IPAM"]["Config"] = [] - changes = __salt__['docker.compare_networks']( - network, - temp_net_info, - ignore='Name,Id,Created,Containers') + changes = __salt__["docker.compare_networks"]( + network, temp_net_info, ignore="Name,Id,Created,Containers" + ) if not changes: # No changes to the network, so we'll be keeping the existing @@ -724,61 +726,63 @@ def present(name, create_network = False else: - ret['changes'][name] = changes - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Network would be recreated with new config' + ret["changes"][name] = changes + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Network would be recreated with new config" return ret - if network['Containers']: + if network["Containers"]: # We've removed the network, so there are now no containers # attached to it. However, once we recreate the network # with the new configuration we may need to reconnect the # containers that were previously connected. Even if we're # not reconnecting, we still need to track the containers # so that we can report on which were disconnected. - disconnected_containers = copy.deepcopy(network['Containers']) + disconnected_containers = copy.deepcopy(network["Containers"]) if not containers and reconnect: # Grab the links and aliases from each connected # container so that we have them when we attempt to # reconnect later for cid in disconnected_containers: try: - cinfo = __salt__['docker.inspect_container'](cid) - netinfo = cinfo['NetworkSettings']['Networks'][name] + cinfo = __salt__["docker.inspect_container"](cid) + netinfo = cinfo["NetworkSettings"]["Networks"][name] # Links and Aliases will be None if not # explicitly set, hence using "or" instead of # placing the empty list inside the dict.get - net_links = netinfo.get('Links') or [] - net_aliases = netinfo.get('Aliases') or [] + net_links = netinfo.get("Links") or [] + net_aliases = netinfo.get("Aliases") or [] if net_links: - disconnected_containers[cid]['Links'] = net_links + disconnected_containers[cid]["Links"] = net_links if net_aliases: - disconnected_containers[cid]['Aliases'] = net_aliases + disconnected_containers[cid][ + "Aliases" + ] = net_aliases except (CommandExecutionError, KeyError, ValueError): continue remove_result = _remove_network(network) - if not remove_result['result']: + if not remove_result["result"]: return remove_result # Replace the Containers key with an empty dict so that when we # check for connnected containers below, we correctly see that # there are none connected. - network['Containers'] = {} + network["Containers"] = {} finally: try: - __salt__['docker.remove_network'](temp_net_name) + __salt__["docker.remove_network"](temp_net_name) except CommandExecutionError as exc: - ret.setdefault('warnings', []).append( - 'Failed to remove temp network \'{0}\': {1}.'.format( + ret.setdefault("warnings", []).append( + "Failed to remove temp network '{0}': {1}.".format( temp_net_name, exc.__str__() ) ) if create_network: - log.debug('Network \'%s\' will be created', name) - if __opts__['test']: + log.debug("Network '%s' will be created", name) + if __opts__["test"]: # NOTE: if the container already existed and needed to be # recreated, and we were in test mode, we would have already exited # above with a comment about the network needing to be recreated. @@ -786,30 +790,31 @@ def present(name, # executed to create the network both when it's being recreated and # when it's being created for the first time, the below comment is # still accurate. - ret['result'] = None - ret['comment'] = 'Network will be created' + ret["result"] = None + ret["comment"] = "Network will be created" return ret - kwargs['ipam'] = ipam_config + kwargs["ipam"] = ipam_config try: - __salt__['docker.create_network']( + __salt__["docker.create_network"]( name, skip_translate=True, # No need to translate (already did) - **kwargs) + **kwargs + ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = 'Failed to create network \'{0}\': {1}'.format( - name, exc.__str__()) + ret["comment"] = "Failed to create network '{0}': {1}".format( + name, exc.__str__() + ) return ret else: - action = 'recreated' if network is not None else 'created' - ret['changes'][action] = True - ret['comment'] = 'Network \'{0}\' {1}'.format( + action = "recreated" if network is not None else "created" + ret["changes"][action] = True + ret["comment"] = "Network '{0}' {1}".format( name, - 'created' if network is None - else 'was replaced with updated config' + "created" if network is None else "was replaced with updated config", ) # Make sure the "Containers" key exists for logic below - network = {'Containers': {}} + network = {"Containers": {}} # If no containers were specified in the state but we have disconnected # some in the process of recreating the network, we should reconnect those @@ -824,7 +829,7 @@ def present(name, # list() is used here because we will potentially be modifying the # dictionary during iteration. for cid in list(to_connect): - if cid in network['Containers']: + if cid in network["Containers"]: del to_connect[cid] errors = [] @@ -832,21 +837,25 @@ def present(name, for cid, connect_info in six.iteritems(to_connect): connect_kwargs = {} if cid in disconnected_containers: - for key_name, arg_name in (('IPv4Address', 'ipv4_address'), - ('IPV6Address', 'ipv6_address'), - ('Links', 'links'), - ('Aliases', 'aliases')): + for key_name, arg_name in ( + ("IPv4Address", "ipv4_address"), + ("IPV6Address", "ipv6_address"), + ("Links", "links"), + ("Aliases", "aliases"), + ): try: connect_kwargs[arg_name] = connect_info[key_name] except (KeyError, AttributeError): continue else: - if key_name.endswith('Address'): - connect_kwargs[arg_name] = \ - connect_kwargs[arg_name].rsplit('/', 1)[0] + if key_name.endswith("Address"): + connect_kwargs[arg_name] = connect_kwargs[arg_name].rsplit( + "/", 1 + )[0] try: - __salt__['docker.connect_container_to_network']( - cid, name, **connect_kwargs) + __salt__["docker.connect_container_to_network"]( + cid, name, **connect_kwargs + ) except CommandExecutionError as exc: if not connect_kwargs: errors.append(exc.__str__()) @@ -854,29 +863,27 @@ def present(name, # We failed to reconnect with the container's old IP # configuration. Reconnect using automatic IP config. try: - __salt__['docker.connect_container_to_network']( - cid, name) + __salt__["docker.connect_container_to_network"](cid, name) except CommandExecutionError as exc: errors.append(exc.__str__()) else: - ret['changes'].setdefault( - 'reconnected' if cid in disconnected_containers - else 'connected', - [] - ).append(connect_info['Name']) + ret["changes"].setdefault( + "reconnected" + if cid in disconnected_containers + else "connected", + [], + ).append(connect_info["Name"]) else: - ret['changes'].setdefault( - 'reconnected' if cid in disconnected_containers - else 'connected', - [] - ).append(connect_info['Name']) + ret["changes"].setdefault( + "reconnected" if cid in disconnected_containers else "connected", [] + ).append(connect_info["Name"]) if errors: - if ret['comment']: - ret['comment'] += '. ' - ret['comment'] += '. '.join(errors) + '.' + if ret["comment"]: + ret["comment"] += ". " + ret["comment"] += ". ".join(errors) + "." else: - ret['result'] = True + ret["result"] = True # Figure out if we removed any containers as a result of replacing the # network and did not reconnect them. We only would not have reconnected if @@ -885,13 +892,13 @@ def present(name, # were not part of that list. for cid, c_info in six.iteritems(disconnected_containers): if cid not in to_connect: - ret['changes'].setdefault('disconnected', []).append(c_info['Name']) + ret["changes"].setdefault("disconnected", []).append(c_info["Name"]) return ret def absent(name): - ''' + """ Ensure that a network is absent. name @@ -903,76 +910,67 @@ def absent(name): network_foo: docker_network.absent - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} try: - network = __salt__['docker.inspect_network'](name) + network = __salt__["docker.inspect_network"](name) except CommandExecutionError as exc: msg = exc.__str__() - if '404' in msg: + if "404" in msg: # Network not present network = None else: - ret['comment'] = msg + ret["comment"] = msg return ret if network is None: - ret['result'] = True - ret['comment'] = 'Network \'{0}\' already absent'.format(name) + ret["result"] = True + ret["comment"] = "Network '{0}' already absent".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Network \'{0}\' will be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Network '{0}' will be removed".format(name) return ret return _remove_network(network) def _remove_network(network): - ''' + """ Remove network, including all connected containers - ''' - ret = {'name': network['Name'], - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": network["Name"], "changes": {}, "result": False, "comment": ""} errors = [] - for cid in network['Containers']: + for cid in network["Containers"]: try: - cinfo = __salt__['docker.inspect_container'](cid) + cinfo = __salt__["docker.inspect_container"](cid) except CommandExecutionError: # Fall back to container ID cname = cid else: - cname = cinfo.get('Name', '').lstrip('/') + cname = cinfo.get("Name", "").lstrip("/") try: - __salt__['docker.disconnect_container_from_network'](cid, network['Name']) + __salt__["docker.disconnect_container_from_network"](cid, network["Name"]) except CommandExecutionError as exc: - errors = \ - 'Failed to disconnect container \'{0}\' : {1}'.format( - cname, exc - ) + errors = "Failed to disconnect container '{0}' : {1}".format(cname, exc) else: - ret['changes'].setdefault('disconnected', []).append(cname) + ret["changes"].setdefault("disconnected", []).append(cname) if errors: - ret['comment'] = '\n'.join(errors) + ret["comment"] = "\n".join(errors) return ret try: - __salt__['docker.remove_network'](network['Name']) + __salt__["docker.remove_network"](network["Name"]) except CommandExecutionError as exc: - ret['comment'] = 'Failed to remove network: {0}'.format(exc) + ret["comment"] = "Failed to remove network: {0}".format(exc) else: - ret['changes']['removed'] = True - ret['result'] = True - ret['comment'] = 'Removed network \'{0}\''.format(network['Name']) + ret["changes"]["removed"] = True + ret["result"] = True + ret["comment"] = "Removed network '{0}'".format(network["Name"]) return ret diff --git a/salt/states/docker_volume.py b/salt/states/docker_volume.py index ecff2e77912..d45355fe9e3 100644 --- a/salt/states/docker_volume.py +++ b/salt/states/docker_volume.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Docker volumes .. versionadded:: 2017.7.0 @@ -29,8 +29,9 @@ Management of Docker volumes These states were moved from the :mod:`docker <salt.states.docker>` state module (formerly called **dockerng**) in the 2017.7.0 release. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -40,26 +41,26 @@ import salt.utils.data log = logging.getLogger(__name__) # pylint: disable=invalid-name # Define the module's virtual name -__virtualname__ = 'docker_volume' -__virtual_aliases__ = ('moby_volume',) +__virtualname__ = "docker_volume" +__virtual_aliases__ = ("moby_volume",) def __virtual__(): - ''' + """ Only load if the docker execution module is available - ''' - if 'docker.version' in __salt__: + """ + if "docker.version" in __salt__: return __virtualname__ - return (False, __salt__.missing_fun_string('docker.version')) + return (False, __salt__.missing_fun_string("docker.version")) def _find_volume(name): - ''' + """ Find volume by name on minion - ''' - docker_volumes = __salt__['docker.volumes']()['Volumes'] + """ + docker_volumes = __salt__["docker.volumes"]()["Volumes"] if docker_volumes: - volumes = [v for v in docker_volumes if v['Name'] == name] + volumes = [v for v in docker_volumes if v["Name"] == name] if volumes: return volumes[0] @@ -67,7 +68,7 @@ def _find_volume(name): def present(name, driver=None, driver_opts=None, force=False): - ''' + """ Ensure that a volume is present. .. versionadded:: 2015.8.4 @@ -129,71 +130,69 @@ def present(name, driver=None, driver_opts=None, force=False): - foo: bar - option: value - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if salt.utils.data.is_dictlist(driver_opts): driver_opts = salt.utils.data.repack_dictlist(driver_opts) volume = _find_volume(name) if not volume: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('The volume \'{0}\' will be created'.format(name)) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The volume '{0}' will be created".format(name) return ret try: - ret['changes']['created'] = __salt__['docker.create_volume']( - name, driver=driver, driver_opts=driver_opts) + ret["changes"]["created"] = __salt__["docker.create_volume"]( + name, driver=driver, driver_opts=driver_opts + ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = ('Failed to create volume \'{0}\': {1}' - .format(name, exc)) + ret["comment"] = "Failed to create volume '{0}': {1}".format(name, exc) return ret else: result = True - ret['result'] = result + ret["result"] = result return ret # volume exists, check if driver is the same. - if driver is not None and volume['Driver'] != driver: + if driver is not None and volume["Driver"] != driver: if not force: - ret['comment'] = "Driver for existing volume '{0}' ('{1}')" \ - " does not match specified driver ('{2}')" \ - " and force is False".format( - name, volume['Driver'], driver) - ret['result'] = None if __opts__['test'] else False + ret["comment"] = ( + "Driver for existing volume '{0}' ('{1}')" + " does not match specified driver ('{2}')" + " and force is False".format(name, volume["Driver"], driver) + ) + ret["result"] = None if __opts__["test"] else False return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = "The volume '{0}' will be replaced with a" \ - " new one using the driver '{1}'".format( - name, volume) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "The volume '{0}' will be replaced with a" + " new one using the driver '{1}'".format(name, volume) + ) return ret try: - ret['changes']['removed'] = __salt__['docker.remove_volume'](name) + ret["changes"]["removed"] = __salt__["docker.remove_volume"](name) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = ('Failed to remove volume \'{0}\': {1}' - .format(name, exc)) + ret["comment"] = "Failed to remove volume '{0}': {1}".format(name, exc) return ret else: try: - ret['changes']['created'] = __salt__['docker.create_volume']( - name, driver=driver, driver_opts=driver_opts) + ret["changes"]["created"] = __salt__["docker.create_volume"]( + name, driver=driver, driver_opts=driver_opts + ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = ('Failed to create volume \'{0}\': {1}' - .format(name, exc)) + ret["comment"] = "Failed to create volume '{0}': {1}".format(name, exc) return ret else: result = True - ret['result'] = result + ret["result"] = result return ret - ret['result'] = True - ret['comment'] = 'Volume \'{0}\' already exists.'.format(name) + ret["result"] = True + ret["comment"] = "Volume '{0}' already exists.".format(name) return ret def absent(name, driver=None): - ''' + """ Ensure that a volume is absent. .. versionadded:: 2015.8.4 @@ -210,22 +209,18 @@ def absent(name, driver=None): volume_foo: docker_volume.absent - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} volume = _find_volume(name) if not volume: - ret['result'] = True - ret['comment'] = 'Volume \'{0}\' already absent'.format(name) + ret["result"] = True + ret["comment"] = "Volume '{0}' already absent".format(name) return ret try: - ret['changes']['removed'] = __salt__['docker.remove_volume'](name) - ret['result'] = True + ret["changes"]["removed"] = __salt__["docker.remove_volume"](name) + ret["result"] = True except Exception as exc: # pylint: disable=broad-except - ret['comment'] = ('Failed to remove volume \'{0}\': {1}' - .format(name, exc)) + ret["comment"] = "Failed to remove volume '{0}': {1}".format(name, exc) return ret diff --git a/salt/states/drac.py b/salt/states/drac.py index bf1d9f962a8..d2394e03dd9 100644 --- a/salt/states/drac.py +++ b/salt/states/drac.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Dell DRAC The DRAC module is used to create and manage DRAC cards on Dell servers @@ -35,7 +35,7 @@ Ensure DRAC network is in a consistent state - netmask: 255.255.255.224 - gateway: 10.225.108.1 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import salt.exceptions @@ -43,17 +43,16 @@ import salt.utils.path def __virtual__(): - ''' + """ Ensure the racadm command is installed - ''' - if salt.utils.path.which('racadm'): + """ + if salt.utils.path.which("racadm"): return True - - return False + return (False, "racadm command not found") def present(name, password, permission): - ''' + """ Ensure the user exists on the Dell DRAC name: @@ -64,110 +63,116 @@ def present(name, password, permission): permission The permissions that should be assigned to a user - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - users = __salt__['drac.list_users']() + users = __salt__["drac.list_users"]() - if __opts__['test']: + if __opts__["test"]: if name in users: - ret['comment'] = '`{0}` already exists'.format(name) + ret["comment"] = "`{0}` already exists".format(name) else: - ret['comment'] = '`{0}` will be created'.format(name) - ret['changes'] = {name: 'will be created'} + ret["comment"] = "`{0}` will be created".format(name) + ret["changes"] = {name: "will be created"} return ret if name in users: - ret['comment'] = '`{0}` already exists'.format(name) + ret["comment"] = "`{0}` already exists".format(name) else: - if __salt__['drac.create_user'](name, password, permission, users): - ret['comment'] = '`{0}` user created'.format(name) - ret['changes'] = {name: 'new user created'} + if __salt__["drac.create_user"](name, password, permission, users): + ret["comment"] = "`{0}` user created".format(name) + ret["changes"] = {name: "new user created"} else: - ret['comment'] = 'Unable to create user' - ret['result'] = False + ret["comment"] = "Unable to create user" + ret["result"] = False return ret def absent(name): - ''' + """ Ensure a user does not exist on the Dell DRAC name: The users username - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - users = __salt__['drac.list_users']() + users = __salt__["drac.list_users"]() - if __opts__['test']: + if __opts__["test"]: if name in users: - ret['comment'] = '`{0}` is set to be deleted'.format(name) - ret['changes'] = {name: 'will be deleted'} + ret["comment"] = "`{0}` is set to be deleted".format(name) + ret["changes"] = {name: "will be deleted"} else: - ret['comment'] = '`{0}` does not exist'.format(name) + ret["comment"] = "`{0}` does not exist".format(name) return ret if name in users: - if __salt__['drac.delete_user'](name, users[name]['index']): - ret['comment'] = '`{0}` deleted'.format(name) - ret['changes'] = {name: 'deleted'} + if __salt__["drac.delete_user"](name, users[name]["index"]): + ret["comment"] = "`{0}` deleted".format(name) + ret["changes"] = {name: "deleted"} else: - ret['comment'] = 'Unable to delete user' - ret['result'] = False + ret["comment"] = "Unable to delete user" + ret["result"] = False else: - ret['comment'] = '`{0}` does not exist'.format(name) + ret["comment"] = "`{0}` does not exist".format(name) return ret def network(ip, netmask, gateway): - ''' + """ Ensure the DRAC network settings are consistent - ''' - ret = {'name': ip, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": ip, "result": True, "changes": {}, "comment": ""} - current_network = __salt__['drac.network_info']() + current_network = __salt__["drac.network_info"]() new_network = {} - if ip != current_network['IPv4 settings']['IP Address']: - ret['changes'].update({'IP Address': - {'Old': current_network['IPv4 settings']['IP Address'], - 'New': ip}}) + if ip != current_network["IPv4 settings"]["IP Address"]: + ret["changes"].update( + { + "IP Address": { + "Old": current_network["IPv4 settings"]["IP Address"], + "New": ip, + } + } + ) - if netmask != current_network['IPv4 settings']['Subnet Mask']: - ret['changes'].update({'Netmask': - {'Old': current_network['IPv4 settings']['Subnet Mask'], - 'New': netmask}}) + if netmask != current_network["IPv4 settings"]["Subnet Mask"]: + ret["changes"].update( + { + "Netmask": { + "Old": current_network["IPv4 settings"]["Subnet Mask"], + "New": netmask, + } + } + ) - if gateway != current_network['IPv4 settings']['Gateway']: - ret['changes'].update({'Gateway': - {'Old': current_network['IPv4 settings']['Gateway'], - 'New': gateway}}) + if gateway != current_network["IPv4 settings"]["Gateway"]: + ret["changes"].update( + { + "Gateway": { + "Old": current_network["IPv4 settings"]["Gateway"], + "New": gateway, + } + } + ) - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret - if __salt__['drac.set_network'](ip, netmask, gateway): - if not ret['changes']: - ret['comment'] = 'Network is in the desired state' + if __salt__["drac.set_network"](ip, netmask, gateway): + if not ret["changes"]: + ret["comment"] = "Network is in the desired state" return ret - ret['result'] = False - ret['comment'] = 'unable to configure network' + ret["result"] = False + ret["comment"] = "unable to configure network" return ret diff --git a/salt/states/dvs.py b/salt/states/dvs.py index 1ff39cde00e..81eae6fd6eb 100644 --- a/salt/states/dvs.py +++ b/salt/states/dvs.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Manage VMware distributed virtual switches (DVSs) and their distributed virtual portgroups (DVportgroups). -:codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstaley.com>` +:codeauthor: `Alexandru Bleotu <alexandru.bleotu@morganstaley.com>` Examples ======== @@ -121,6 +121,7 @@ Portgroup --------- .. code-block:: python + 'security_policy': { 'allow_promiscuous': true, 'mac_changes': false, @@ -166,7 +167,6 @@ Note: The mandatory attributes are: ``name``, ``type``. Dependencies ============ - - pyVmomi Python Module @@ -199,13 +199,14 @@ version currently listed in PyPi, run the following: The 5.5.0.2014.1.1 is a known stable version that this original ESXi State Module was developed against. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import traceback import sys +import traceback # Import Salt Libs import salt.exceptions @@ -215,6 +216,7 @@ from salt.ext.six.moves import range # Import Third Party Libs try: from pyVmomi import VmomiSupport + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False @@ -225,45 +227,54 @@ log = logging.getLogger(__name__) def __virtual__(): if not HAS_PYVMOMI: - return False, 'State module did not load: pyVmomi not found' + return False, "State module did not load: pyVmomi not found" # We check the supported vim versions to infer the pyVmomi version - if 'vim25/6.0' in VmomiSupport.versionMap and \ - sys.version_info > (2, 7) and sys.version_info < (2, 7, 9): + if ( + "vim25/6.0" in VmomiSupport.versionMap + and sys.version_info > (2, 7) + and sys.version_info < (2, 7, 9) + ): - return False, ('State module did not load: Incompatible versions ' - 'of Python and pyVmomi present. See Issue #29537.') - return 'dvs' + return ( + False, + ( + "State module did not load: Incompatible versions " + "of Python and pyVmomi present. See Issue #29537." + ), + ) + return "dvs" def mod_init(low): - ''' + """ Init function - ''' + """ return True def _get_datacenter_name(): - ''' + """ Returns the datacenter name configured on the proxy Supported proxies: esxcluster, esxdatacenter - ''' + """ - proxy_type = __salt__['vsphere.get_proxy_type']() + proxy_type = __salt__["vsphere.get_proxy_type"]() details = None - if proxy_type == 'esxcluster': - details = __salt__['esxcluster.get_details']() - elif proxy_type == 'esxdatacenter': - details = __salt__['esxdatacenter.get_details']() + if proxy_type == "esxcluster": + details = __salt__["esxcluster.get_details"]() + elif proxy_type == "esxdatacenter": + details = __salt__["esxdatacenter.get_details"]() if not details: raise salt.exceptions.CommandExecutionError( - 'details for proxy type \'{0}\' not loaded'.format(proxy_type)) - return details['datacenter'] + "details for proxy type '{0}' not loaded".format(proxy_type) + ) + return details["datacenter"] def dvs_configured(name, dvs): - ''' + """ Configures a DVS. Creates a new DVS, if it doesn't exist in the provided datacenter or @@ -271,47 +282,60 @@ def dvs_configured(name, dvs): dvs DVS dict representations (see module sysdocs) - ''' + """ datacenter_name = _get_datacenter_name() - dvs_name = dvs['name'] if dvs.get('name') else name - log.info('Running state {0} for DVS \'{1}\' in datacenter ' - '\'{2}\''.format(name, dvs_name, datacenter_name)) + dvs_name = dvs["name"] if dvs.get("name") else name + log.info( + "Running state {0} for DVS '{1}' in datacenter " + "'{2}'".format(name, dvs_name, datacenter_name) + ) changes_required = False - ret = {'name': name, 'changes': {}, 'result': None, 'comment': None} + ret = {"name": name, "changes": {}, "result": None, "comment": None} comments = [] changes = {} changes_required = False try: - #TODO dvs validation - si = __salt__['vsphere.get_service_instance_via_proxy']() - dvss = __salt__['vsphere.list_dvss'](dvs_names=[dvs_name], - service_instance=si) + # TODO dvs validation + si = __salt__["vsphere.get_service_instance_via_proxy"]() + dvss = __salt__["vsphere.list_dvss"](dvs_names=[dvs_name], service_instance=si) if not dvss: changes_required = True - if __opts__['test']: - comments.append('State {0} will create a new DVS ' - '\'{1}\' in datacenter \'{2}\'' - ''.format(name, dvs_name, datacenter_name)) + if __opts__["test"]: + comments.append( + "State {0} will create a new DVS " + "'{1}' in datacenter '{2}'" + "".format(name, dvs_name, datacenter_name) + ) log.info(comments[-1]) else: - dvs['name'] = dvs_name - __salt__['vsphere.create_dvs'](dvs_dict=dvs, - dvs_name=dvs_name, - service_instance=si) - comments.append('Created a new DVS \'{0}\' in datacenter ' - '\'{1}\''.format(dvs_name, datacenter_name)) + dvs["name"] = dvs_name + __salt__["vsphere.create_dvs"]( + dvs_dict=dvs, dvs_name=dvs_name, service_instance=si + ) + comments.append( + "Created a new DVS '{0}' in datacenter " + "'{1}'".format(dvs_name, datacenter_name) + ) log.info(comments[-1]) - changes.update({'dvs': {'new': dvs}}) + changes.update({"dvs": {"new": dvs}}) else: # DVS already exists. Checking various aspects of the config - props = ['description', 'contact_email', 'contact_name', - 'lacp_api_version', 'link_discovery_protocol', - 'max_mtu', 'network_resource_control_version', - 'network_resource_management_enabled'] - log.trace('DVS \'{0}\' found in datacenter \'{1}\'. Checking ' - 'for any updates in ' - '{2}'.format(dvs_name, datacenter_name, props)) + props = [ + "description", + "contact_email", + "contact_name", + "lacp_api_version", + "link_discovery_protocol", + "max_mtu", + "network_resource_control_version", + "network_resource_management_enabled", + ] + log.trace( + "DVS '{0}' found in datacenter '{1}'. Checking " + "for any updates in " + "{2}".format(dvs_name, datacenter_name, props) + ) props_to_original_values = {} props_to_updated_values = {} current_dvs = dvss[0] @@ -323,7 +347,7 @@ def dvs_configured(name, dvs): # Simple infrastructure traffic resource control compare doesn't # work because num_shares is optional if share_level is not custom # We need to do a dedicated compare for this property - infra_prop = 'infrastructure_traffic_resource_pools' + infra_prop = "infrastructure_traffic_resource_pools" original_infra_res_pools = [] updated_infra_res_pools = [] if infra_prop in dvs: @@ -331,171 +355,192 @@ def dvs_configured(name, dvs): updated_infra_res_pools = dvs[infra_prop] else: for idx in range(len(dvs[infra_prop])): - if 'num_shares' not in dvs[infra_prop][idx] and \ - current_dvs[infra_prop][idx]['share_level'] != \ - 'custom' and \ - 'num_shares' in current_dvs[infra_prop][idx]: + if ( + "num_shares" not in dvs[infra_prop][idx] + and current_dvs[infra_prop][idx]["share_level"] != "custom" + and "num_shares" in current_dvs[infra_prop][idx] + ): - del current_dvs[infra_prop][idx]['num_shares'] - if dvs[infra_prop][idx] != \ - current_dvs[infra_prop][idx]: + del current_dvs[infra_prop][idx]["num_shares"] + if dvs[infra_prop][idx] != current_dvs[infra_prop][idx]: original_infra_res_pools.append( - current_dvs[infra_prop][idx]) - updated_infra_res_pools.append( - dict(dvs[infra_prop][idx])) + current_dvs[infra_prop][idx] + ) + updated_infra_res_pools.append(dict(dvs[infra_prop][idx])) if updated_infra_res_pools: props_to_original_values[ - 'infrastructure_traffic_resource_pools'] = \ - original_infra_res_pools + "infrastructure_traffic_resource_pools" + ] = original_infra_res_pools props_to_updated_values[ - 'infrastructure_traffic_resource_pools'] = \ - updated_infra_res_pools + "infrastructure_traffic_resource_pools" + ] = updated_infra_res_pools if props_to_updated_values: - if __opts__['test']: - changes_string = '' + if __opts__["test"]: + changes_string = "" for p in props_to_updated_values: - if p == 'infrastructure_traffic_resource_pools': - changes_string += \ - '\tinfrastructure_traffic_resource_pools:\n' + if p == "infrastructure_traffic_resource_pools": + changes_string += ( + "\tinfrastructure_traffic_resource_pools:\n" + ) for idx in range(len(props_to_updated_values[p])): d = props_to_updated_values[p][idx] s = props_to_original_values[p][idx] - changes_string += \ - ('\t\t{0} from \'{1}\' to \'{2}\'\n' - ''.format(d['key'], s, d)) + changes_string += ( + "\t\t{0} from '{1}' to '{2}'\n" + "".format(d["key"], s, d) + ) else: - changes_string += \ - ('\t{0} from \'{1}\' to \'{2}\'\n' - ''.format(p, props_to_original_values[p], - props_to_updated_values[p])) + changes_string += "\t{0} from '{1}' to '{2}'\n" "".format( + p, + props_to_original_values[p], + props_to_updated_values[p], + ) comments.append( - 'State dvs_configured will update DVS \'{0}\' ' - 'in datacenter \'{1}\':\n{2}' - ''.format(dvs_name, datacenter_name, changes_string)) + "State dvs_configured will update DVS '{0}' " + "in datacenter '{1}':\n{2}" + "".format(dvs_name, datacenter_name, changes_string) + ) log.info(comments[-1]) else: - __salt__['vsphere.update_dvs']( + __salt__["vsphere.update_dvs"]( dvs_dict=props_to_updated_values, dvs=dvs_name, - service_instance=si) - comments.append('Updated DVS \'{0}\' in datacenter \'{1}\'' - ''.format(dvs_name, datacenter_name)) + service_instance=si, + ) + comments.append( + "Updated DVS '{0}' in datacenter '{1}'" + "".format(dvs_name, datacenter_name) + ) log.info(comments[-1]) - changes.update({'dvs': {'new': props_to_updated_values, - 'old': props_to_original_values}}) - __salt__['vsphere.disconnect'](si) + changes.update( + { + "dvs": { + "new": props_to_updated_values, + "old": props_to_original_values, + } + } + ) + __salt__["vsphere.disconnect"](si) except salt.exceptions.CommandExecutionError as exc: - log.error('Error: {0}\n{1}'.format(exc, traceback.format_exc())) + log.error("Error: {0}\n{1}".format(exc, traceback.format_exc())) if si: - __salt__['vsphere.disconnect'](si) - if not __opts__['test']: - ret['result'] = False - ret.update({'comment': six.text_type(exc), - 'result': False if not __opts__['test'] else None}) + __salt__["vsphere.disconnect"](si) + if not __opts__["test"]: + ret["result"] = False + ret.update( + { + "comment": six.text_type(exc), + "result": False if not __opts__["test"] else None, + } + ) return ret if not comments: # We have no changes - ret.update({'comment': ('DVS \'{0}\' in datacenter \'{1}\' is ' - 'correctly configured. Nothing to be done.' - ''.format(dvs_name, datacenter_name)), - 'result': True}) + ret.update( + { + "comment": ( + "DVS '{0}' in datacenter '{1}' is " + "correctly configured. Nothing to be done." + "".format(dvs_name, datacenter_name) + ), + "result": True, + } + ) else: - ret.update({ - 'comment': '\n'.join(comments), - 'changes': changes, - 'result': None if __opts__['test'] else True, - }) + ret.update( + { + "comment": "\n".join(comments), + "changes": changes, + "result": None if __opts__["test"] else True, + } + ) return ret def _get_diff_dict(dict1, dict2): - ''' + """ Returns a dictionary with the diffs between two dictionaries It will ignore any key that doesn't exist in dict2 - ''' + """ ret_dict = {} for p in dict2.keys(): if p not in dict1: - ret_dict.update({p: {'val1': None, 'val2': dict2[p]}}) + ret_dict.update({p: {"val1": None, "val2": dict2[p]}}) elif dict1[p] != dict2[p]: if isinstance(dict1[p], dict) and isinstance(dict2[p], dict): sub_diff_dict = _get_diff_dict(dict1[p], dict2[p]) if sub_diff_dict: ret_dict.update({p: sub_diff_dict}) else: - ret_dict.update({p: {'val1': dict1[p], 'val2': dict2[p]}}) + ret_dict.update({p: {"val1": dict1[p], "val2": dict2[p]}}) return ret_dict def _get_val2_dict_from_diff_dict(diff_dict): - ''' + """ Returns a dictionaries with the values stored in val2 of a diff dict. - ''' + """ ret_dict = {} for p in diff_dict.keys(): if not isinstance(diff_dict[p], dict): - raise ValueError('Unexpected diff difct \'{0}\''.format(diff_dict)) - if 'val2' in diff_dict[p].keys(): - ret_dict.update({p: diff_dict[p]['val2']}) + raise ValueError("Unexpected diff difct '{0}'".format(diff_dict)) + if "val2" in diff_dict[p].keys(): + ret_dict.update({p: diff_dict[p]["val2"]}) else: - ret_dict.update( - {p: _get_val2_dict_from_diff_dict(diff_dict[p])}) + ret_dict.update({p: _get_val2_dict_from_diff_dict(diff_dict[p])}) return ret_dict def _get_val1_dict_from_diff_dict(diff_dict): - ''' + """ Returns a dictionaries with the values stored in val1 of a diff dict. - ''' + """ ret_dict = {} for p in diff_dict.keys(): if not isinstance(diff_dict[p], dict): - raise ValueError('Unexpected diff difct \'{0}\''.format(diff_dict)) - if 'val1' in diff_dict[p].keys(): - ret_dict.update({p: diff_dict[p]['val1']}) + raise ValueError("Unexpected diff difct '{0}'".format(diff_dict)) + if "val1" in diff_dict[p].keys(): + ret_dict.update({p: diff_dict[p]["val1"]}) else: - ret_dict.update( - {p: _get_val1_dict_from_diff_dict(diff_dict[p])}) + ret_dict.update({p: _get_val1_dict_from_diff_dict(diff_dict[p])}) return ret_dict def _get_changes_from_diff_dict(diff_dict): - ''' + """ Returns a list of string message of the differences in a diff dict. Each inner message is tabulated one tab deeper - ''' + """ changes_strings = [] for p in diff_dict.keys(): if not isinstance(diff_dict[p], dict): - raise ValueError('Unexpected diff difct \'{0}\''.format(diff_dict)) - if sorted(diff_dict[p].keys()) == ['val1', 'val2']: + raise ValueError("Unexpected diff difct '{0}'".format(diff_dict)) + if sorted(diff_dict[p].keys()) == ["val1", "val2"]: # Some string formatting - from_str = diff_dict[p]['val1'] - if isinstance(diff_dict[p]['val1'], six.string_types): - from_str = '\'{0}\''.format(diff_dict[p]['val1']) - elif isinstance(diff_dict[p]['val1'], list): - from_str = '\'{0}\''.format(', '.join(diff_dict[p]['val1'])) - to_str = diff_dict[p]['val2'] - if isinstance(diff_dict[p]['val2'], six.string_types): - to_str = '\'{0}\''.format(diff_dict[p]['val2']) - elif isinstance(diff_dict[p]['val2'], list): - to_str = '\'{0}\''.format(', '.join(diff_dict[p]['val2'])) - changes_strings.append('{0} from {1} to {2}'.format( - p, from_str, to_str)) + from_str = diff_dict[p]["val1"] + if isinstance(diff_dict[p]["val1"], six.string_types): + from_str = "'{0}'".format(diff_dict[p]["val1"]) + elif isinstance(diff_dict[p]["val1"], list): + from_str = "'{0}'".format(", ".join(diff_dict[p]["val1"])) + to_str = diff_dict[p]["val2"] + if isinstance(diff_dict[p]["val2"], six.string_types): + to_str = "'{0}'".format(diff_dict[p]["val2"]) + elif isinstance(diff_dict[p]["val2"], list): + to_str = "'{0}'".format(", ".join(diff_dict[p]["val2"])) + changes_strings.append("{0} from {1} to {2}".format(p, from_str, to_str)) else: sub_changes = _get_changes_from_diff_dict(diff_dict[p]) if sub_changes: - changes_strings.append('{0}:'.format(p)) - changes_strings.extend(['\t{0}'.format(c) - for c in sub_changes]) + changes_strings.append("{0}:".format(p)) + changes_strings.extend(["\t{0}".format(c) for c in sub_changes]) return changes_strings def portgroups_configured(name, dvs, portgroups): - ''' + """ Configures portgroups on a DVS. Creates/updates/removes portgroups in a provided DVS @@ -505,134 +550,163 @@ def portgroups_configured(name, dvs, portgroups): portgroups Portgroup dict representations (see module sysdocs) - ''' + """ datacenter = _get_datacenter_name() - log.info('Running state {0} on DVS \'{1}\', datacenter ' - '\'{2}\''.format(name, dvs, datacenter)) + log.info( + "Running state {0} on DVS '{1}', datacenter " + "'{2}'".format(name, dvs, datacenter) + ) changes_required = False - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': None} + ret = {"name": name, "changes": {}, "result": None, "comment": None} comments = [] changes = {} changes_required = False try: - #TODO portroups validation - si = __salt__['vsphere.get_service_instance_via_proxy']() - current_pgs = __salt__['vsphere.list_dvportgroups']( - dvs=dvs, service_instance=si) + # TODO portroups validation + si = __salt__["vsphere.get_service_instance_via_proxy"]() + current_pgs = __salt__["vsphere.list_dvportgroups"]( + dvs=dvs, service_instance=si + ) expected_pg_names = [] for pg in portgroups: - pg_name = pg['name'] + pg_name = pg["name"] expected_pg_names.append(pg_name) - del pg['name'] - log.info('Checking pg \'{0}\''.format(pg_name)) - filtered_current_pgs = \ - [p for p in current_pgs if p.get('name') == pg_name] + del pg["name"] + log.info("Checking pg '{0}'".format(pg_name)) + filtered_current_pgs = [p for p in current_pgs if p.get("name") == pg_name] if not filtered_current_pgs: changes_required = True - if __opts__['test']: - comments.append('State {0} will create a new portgroup ' - '\'{1}\' in DVS \'{2}\', datacenter ' - '\'{3}\''.format(name, pg_name, dvs, - datacenter)) + if __opts__["test"]: + comments.append( + "State {0} will create a new portgroup " + "'{1}' in DVS '{2}', datacenter " + "'{3}'".format(name, pg_name, dvs, datacenter) + ) else: - __salt__['vsphere.create_dvportgroup']( - portgroup_dict=pg, portgroup_name=pg_name, dvs=dvs, - service_instance=si) - comments.append('Created a new portgroup \'{0}\' in DVS ' - '\'{1}\', datacenter \'{2}\'' - ''.format(pg_name, dvs, datacenter)) + __salt__["vsphere.create_dvportgroup"]( + portgroup_dict=pg, + portgroup_name=pg_name, + dvs=dvs, + service_instance=si, + ) + comments.append( + "Created a new portgroup '{0}' in DVS " + "'{1}', datacenter '{2}'" + "".format(pg_name, dvs, datacenter) + ) log.info(comments[-1]) - changes.update({pg_name: {'new': pg}}) + changes.update({pg_name: {"new": pg}}) else: # Porgroup already exists. Checking the config - log.trace('Portgroup \'{0}\' found in DVS \'{1}\', datacenter ' - '\'{2}\'. Checking for any updates.' - ''.format(pg_name, dvs, datacenter)) + log.trace( + "Portgroup '{0}' found in DVS '{1}', datacenter " + "'{2}'. Checking for any updates." + "".format(pg_name, dvs, datacenter) + ) current_pg = filtered_current_pgs[0] diff_dict = _get_diff_dict(current_pg, pg) if diff_dict: changes_required = True - if __opts__['test']: - changes_strings = \ - _get_changes_from_diff_dict(diff_dict) - log.trace('changes_strings = ' - '{0}'.format(changes_strings)) + if __opts__["test"]: + changes_strings = _get_changes_from_diff_dict(diff_dict) + log.trace("changes_strings = " "{0}".format(changes_strings)) comments.append( - 'State {0} will update portgroup \'{1}\' in ' - 'DVS \'{2}\', datacenter \'{3}\':\n{4}' - ''.format(name, pg_name, dvs, datacenter, - '\n'.join(['\t{0}'.format(c) for c in - changes_strings]))) + "State {0} will update portgroup '{1}' in " + "DVS '{2}', datacenter '{3}':\n{4}" + "".format( + name, + pg_name, + dvs, + datacenter, + "\n".join(["\t{0}".format(c) for c in changes_strings]), + ) + ) else: - __salt__['vsphere.update_dvportgroup']( - portgroup_dict=pg, portgroup=pg_name, dvs=dvs, - service_instance=si) - comments.append('Updated portgroup \'{0}\' in DVS ' - '\'{1}\', datacenter \'{2}\'' - ''.format(pg_name, dvs, datacenter)) + __salt__["vsphere.update_dvportgroup"]( + portgroup_dict=pg, + portgroup=pg_name, + dvs=dvs, + service_instance=si, + ) + comments.append( + "Updated portgroup '{0}' in DVS " + "'{1}', datacenter '{2}'" + "".format(pg_name, dvs, datacenter) + ) log.info(comments[-1]) changes.update( - {pg_name: {'new': - _get_val2_dict_from_diff_dict(diff_dict), - 'old': - _get_val1_dict_from_diff_dict(diff_dict)}}) + { + pg_name: { + "new": _get_val2_dict_from_diff_dict(diff_dict), + "old": _get_val1_dict_from_diff_dict(diff_dict), + } + } + ) # Add the uplink portgroup to the expected pg names - uplink_pg = __salt__['vsphere.list_uplink_dvportgroup']( - dvs=dvs, service_instance=si) - expected_pg_names.append(uplink_pg['name']) + uplink_pg = __salt__["vsphere.list_uplink_dvportgroup"]( + dvs=dvs, service_instance=si + ) + expected_pg_names.append(uplink_pg["name"]) # Remove any extra portgroups for current_pg in current_pgs: - if current_pg['name'] not in expected_pg_names: + if current_pg["name"] not in expected_pg_names: changes_required = True - if __opts__['test']: - comments.append('State {0} will remove ' - 'the portgroup \'{1}\' from DVS \'{2}\', ' - 'datacenter \'{3}\'' - ''.format(name, current_pg['name'], dvs, - datacenter)) + if __opts__["test"]: + comments.append( + "State {0} will remove " + "the portgroup '{1}' from DVS '{2}', " + "datacenter '{3}'" + "".format(name, current_pg["name"], dvs, datacenter) + ) else: - __salt__['vsphere.remove_dvportgroup']( - portgroup=current_pg['name'], dvs=dvs, - service_instance=si) - comments.append('Removed the portgroup \'{0}\' from DVS ' - '\'{1}\', datacenter \'{2}\'' - ''.format(current_pg['name'], dvs, - datacenter)) + __salt__["vsphere.remove_dvportgroup"]( + portgroup=current_pg["name"], dvs=dvs, service_instance=si + ) + comments.append( + "Removed the portgroup '{0}' from DVS " + "'{1}', datacenter '{2}'" + "".format(current_pg["name"], dvs, datacenter) + ) log.info(comments[-1]) - changes.update({current_pg['name']: - {'old': current_pg}}) - __salt__['vsphere.disconnect'](si) + changes.update({current_pg["name"]: {"old": current_pg}}) + __salt__["vsphere.disconnect"](si) except salt.exceptions.CommandExecutionError as exc: - log.error('Error: {0}\n{1}'.format(exc, traceback.format_exc())) + log.error("Error: {0}\n{1}".format(exc, traceback.format_exc())) if si: - __salt__['vsphere.disconnect'](si) - if not __opts__['test']: - ret['result'] = False - ret.update({'comment': exc.strerror, - 'result': False if not __opts__['test'] else None}) + __salt__["vsphere.disconnect"](si) + if not __opts__["test"]: + ret["result"] = False + ret.update( + {"comment": exc.strerror, "result": False if not __opts__["test"] else None} + ) return ret if not changes_required: # We have no changes - ret.update({'comment': ('All portgroups in DVS \'{0}\', datacenter ' - '\'{1}\' exist and are correctly configured. ' - 'Nothing to be done.'.format(dvs, datacenter)), - 'result': True}) + ret.update( + { + "comment": ( + "All portgroups in DVS '{0}', datacenter " + "'{1}' exist and are correctly configured. " + "Nothing to be done.".format(dvs, datacenter) + ), + "result": True, + } + ) else: - ret.update({ - 'comment': '\n'.join(comments), - 'changes': changes, - 'result': None if __opts__['test'] else True, - }) + ret.update( + { + "comment": "\n".join(comments), + "changes": changes, + "result": None if __opts__["test"] else True, + } + ) return ret def uplink_portgroup_configured(name, dvs, uplink_portgroup): - ''' + """ Configures the uplink portgroup on a DVS. The state assumes there is only one uplink portgroup. @@ -642,75 +716,91 @@ def uplink_portgroup_configured(name, dvs, uplink_portgroup): upling_portgroup Uplink portgroup dict representations (see module sysdocs) - ''' + """ datacenter = _get_datacenter_name() - log.info('Running {0} on DVS \'{1}\', datacenter \'{2}\'' - ''.format(name, dvs, datacenter)) + log.info( + "Running {0} on DVS '{1}', datacenter '{2}'" "".format(name, dvs, datacenter) + ) changes_required = False - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': None} + ret = {"name": name, "changes": {}, "result": None, "comment": None} comments = [] changes = {} changes_required = False try: - #TODO portroups validation - si = __salt__['vsphere.get_service_instance_via_proxy']() - current_uplink_portgroup = __salt__['vsphere.list_uplink_dvportgroup']( - dvs=dvs, service_instance=si) - log.trace('current_uplink_portgroup = ' - '{0}'.format(current_uplink_portgroup)) + # TODO portroups validation + si = __salt__["vsphere.get_service_instance_via_proxy"]() + current_uplink_portgroup = __salt__["vsphere.list_uplink_dvportgroup"]( + dvs=dvs, service_instance=si + ) + log.trace("current_uplink_portgroup = " "{0}".format(current_uplink_portgroup)) diff_dict = _get_diff_dict(current_uplink_portgroup, uplink_portgroup) if diff_dict: changes_required = True - if __opts__['test']: - changes_strings = \ - _get_changes_from_diff_dict(diff_dict) - log.trace('changes_strings = ' - '{0}'.format(changes_strings)) + if __opts__["test"]: + changes_strings = _get_changes_from_diff_dict(diff_dict) + log.trace("changes_strings = " "{0}".format(changes_strings)) comments.append( - 'State {0} will update the ' - 'uplink portgroup in DVS \'{1}\', datacenter ' - '\'{2}\':\n{3}' - ''.format(name, dvs, datacenter, - '\n'.join(['\t{0}'.format(c) for c in - changes_strings]))) + "State {0} will update the " + "uplink portgroup in DVS '{1}', datacenter " + "'{2}':\n{3}" + "".format( + name, + dvs, + datacenter, + "\n".join(["\t{0}".format(c) for c in changes_strings]), + ) + ) else: - __salt__['vsphere.update_dvportgroup']( + __salt__["vsphere.update_dvportgroup"]( portgroup_dict=uplink_portgroup, - portgroup=current_uplink_portgroup['name'], + portgroup=current_uplink_portgroup["name"], dvs=dvs, - service_instance=si) - comments.append('Updated the uplink portgroup in DVS ' - '\'{0}\', datacenter \'{1}\'' - ''.format(dvs, datacenter)) + service_instance=si, + ) + comments.append( + "Updated the uplink portgroup in DVS " + "'{0}', datacenter '{1}'" + "".format(dvs, datacenter) + ) log.info(comments[-1]) changes.update( - {'uplink_portgroup': - {'new': _get_val2_dict_from_diff_dict(diff_dict), - 'old': _get_val1_dict_from_diff_dict(diff_dict)}}) - __salt__['vsphere.disconnect'](si) + { + "uplink_portgroup": { + "new": _get_val2_dict_from_diff_dict(diff_dict), + "old": _get_val1_dict_from_diff_dict(diff_dict), + } + } + ) + __salt__["vsphere.disconnect"](si) except salt.exceptions.CommandExecutionError as exc: - log.error('Error: {0}\n{1}'.format(exc, traceback.format_exc())) + log.error("Error: {0}\n{1}".format(exc, traceback.format_exc())) if si: - __salt__['vsphere.disconnect'](si) - if not __opts__['test']: - ret['result'] = False - ret.update({'comment': exc.strerror, - 'result': False if not __opts__['test'] else None}) + __salt__["vsphere.disconnect"](si) + if not __opts__["test"]: + ret["result"] = False + ret.update( + {"comment": exc.strerror, "result": False if not __opts__["test"] else None} + ) return ret if not changes_required: # We have no changes - ret.update({'comment': ('Uplink portgroup in DVS \'{0}\', datacenter ' - '\'{1}\' is correctly configured. ' - 'Nothing to be done.'.format(dvs, datacenter)), - 'result': True}) + ret.update( + { + "comment": ( + "Uplink portgroup in DVS '{0}', datacenter " + "'{1}' is correctly configured. " + "Nothing to be done.".format(dvs, datacenter) + ), + "result": True, + } + ) else: - ret.update({ - 'comment': '\n'.join(comments), - 'changes': changes, - 'result': None if __opts__['test'] else True, - }) + ret.update( + { + "comment": "\n".join(comments), + "changes": changes, + "result": None if __opts__["test"] else True, + } + ) return ret diff --git a/salt/states/elasticsearch.py b/salt/states/elasticsearch.py index 2edce770307..83c498bc6e8 100644 --- a/salt/states/elasticsearch.py +++ b/salt/states/elasticsearch.py @@ -1,56 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" State module to manage Elasticsearch. .. versionadded:: 2017.7.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.json + # Import salt libs from salt.ext import six -import salt.utils.json log = logging.getLogger(__name__) def index_absent(name): - ''' + """ Ensure that the named index is absent. name Name of the index to remove - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - index = __salt__['elasticsearch.index_get'](index=name) + index = __salt__["elasticsearch.index_get"](index=name) if index and name in index: - if __opts__['test']: - ret['comment'] = 'Index {0} will be removed'.format(name) - ret['changes']['old'] = index[name] - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Index {0} will be removed".format(name) + ret["changes"]["old"] = index[name] + ret["result"] = None else: - ret['result'] = __salt__['elasticsearch.index_delete'](index=name) - if ret['result']: - ret['comment'] = 'Successfully removed index {0}'.format(name) - ret['changes']['old'] = index[name] + ret["result"] = __salt__["elasticsearch.index_delete"](index=name) + if ret["result"]: + ret["comment"] = "Successfully removed index {0}".format(name) + ret["changes"]["old"] = index[name] else: - ret['comment'] = 'Failed to remove index {0} for unknown reasons'.format(name) + ret[ + "comment" + ] = "Failed to remove index {0} for unknown reasons".format(name) else: - ret['comment'] = 'Index {0} is already absent'.format(name) + ret["comment"] = "Index {0} is already absent".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def index_present(name, definition=None): - ''' + """ Ensure that the named index is present. name @@ -73,71 +77,98 @@ def index_present(name, definition=None): settings: index: number_of_shards: 10 - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - index_exists = __salt__['elasticsearch.index_exists'](index=name) + index_exists = __salt__["elasticsearch.index_exists"](index=name) if not index_exists: - if __opts__['test']: - ret['comment'] = 'Index {0} does not exist and will be created'.format(name) - ret['changes'] = {'new': definition} - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Index {0} does not exist and will be created".format( + name + ) + ret["changes"] = {"new": definition} + ret["result"] = None else: - output = __salt__['elasticsearch.index_create'](index=name, body=definition) + output = __salt__["elasticsearch.index_create"]( + index=name, body=definition + ) if output: - ret['comment'] = 'Successfully created index {0}'.format(name) - ret['changes'] = {'new': __salt__['elasticsearch.index_get'](index=name)[name]} + ret["comment"] = "Successfully created index {0}".format(name) + ret["changes"] = { + "new": __salt__["elasticsearch.index_get"](index=name)[name] + } else: - ret['result'] = False - ret['comment'] = 'Cannot create index {0}, {1}'.format(name, output) + ret["result"] = False + ret["comment"] = "Cannot create index {0}, {1}".format(name, output) else: - ret['comment'] = 'Index {0} is already present'.format(name) + ret["comment"] = "Index {0} is already present".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def alias_absent(name, index): - ''' + """ Ensure that the index alias is absent. name Name of the index alias to remove index Name of the index for the alias - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - alias = __salt__['elasticsearch.alias_get'](aliases=name, indices=index) - if alias and alias.get(index, {}).get("aliases", {}).get(name, None) is not None: - if __opts__['test']: - ret['comment'] = 'Alias {0} for index {1} will be removed'.format(name, index) - ret['changes']['old'] = alias.get(index, {}).get("aliases", {}).get(name, {}) - ret['result'] = None + alias = __salt__["elasticsearch.alias_get"](aliases=name, indices=index) + if ( + alias + and alias.get(index, {}).get("aliases", {}).get(name, None) is not None + ): + if __opts__["test"]: + ret["comment"] = "Alias {0} for index {1} will be removed".format( + name, index + ) + ret["changes"]["old"] = ( + alias.get(index, {}).get("aliases", {}).get(name, {}) + ) + ret["result"] = None else: - ret['result'] = __salt__['elasticsearch.alias_delete'](aliases=name, indices=index) - if ret['result']: - ret['comment'] = 'Successfully removed alias {0} for index {1}'.format(name, index) - ret['changes']['old'] = alias.get(index, {}).get("aliases", {}).get(name, {}) + ret["result"] = __salt__["elasticsearch.alias_delete"]( + aliases=name, indices=index + ) + if ret["result"]: + ret[ + "comment" + ] = "Successfully removed alias {0} for index {1}".format( + name, index + ) + ret["changes"]["old"] = ( + alias.get(index, {}).get("aliases", {}).get(name, {}) + ) else: - ret['comment'] = 'Failed to remove alias {0} for index {1} for unknown reasons'.format(name, index) + ret[ + "comment" + ] = "Failed to remove alias {0} for index {1} for unknown reasons".format( + name, index + ) else: - ret['comment'] = 'Alias {0} for index {1} is already absent'.format(name, index) + ret["comment"] = "Alias {0} for index {1} is already absent".format( + name, index + ) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def alias_present(name, index, definition=None): - ''' + """ Ensure that the named index alias is present. name @@ -158,82 +189,114 @@ def alias_present(name, index, definition=None): filter: term: user: kimchy - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - alias = __salt__['elasticsearch.alias_get'](aliases=name, indices=index) + alias = __salt__["elasticsearch.alias_get"](aliases=name, indices=index) old = {} if alias: old = alias.get(index, {}).get("aliases", {}).get(name, {}) if not definition: definition = {} - ret['changes'] = __utils__['dictdiffer.deep_diff'](old, definition) + ret["changes"] = __utils__["dictdiffer.deep_diff"](old, definition) - if ret['changes'] or not definition: - if __opts__['test']: + if ret["changes"] or not definition: + if __opts__["test"]: if not old: - ret['comment'] = 'Alias {0} for index {1} does not exist and will be created'.format(name, index) + ret[ + "comment" + ] = "Alias {0} for index {1} does not exist and will be created".format( + name, index + ) else: - ret['comment'] = 'Alias {0} for index {1} exists with wrong configuration and will be overridden'.format(name, index) + ret[ + "comment" + ] = "Alias {0} for index {1} exists with wrong configuration and will be overridden".format( + name, index + ) - ret['result'] = None + ret["result"] = None else: - output = __salt__['elasticsearch.alias_create'](alias=name, indices=index, body=definition) + output = __salt__["elasticsearch.alias_create"]( + alias=name, indices=index, body=definition + ) if output: if not old: - ret['comment'] = 'Successfully created alias {0} for index {1}'.format(name, index) + ret[ + "comment" + ] = "Successfully created alias {0} for index {1}".format( + name, index + ) else: - ret['comment'] = 'Successfully replaced alias {0} for index {1}'.format(name, index) + ret[ + "comment" + ] = "Successfully replaced alias {0} for index {1}".format( + name, index + ) else: - ret['result'] = False - ret['comment'] = 'Cannot create alias {0} for index {1}, {2}'.format(name, index, output) + ret["result"] = False + ret[ + "comment" + ] = "Cannot create alias {0} for index {1}, {2}".format( + name, index, output + ) else: - ret['comment'] = 'Alias {0} for index {1} is already present'.format(name, index) + ret["comment"] = "Alias {0} for index {1} is already present".format( + name, index + ) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def index_template_absent(name): - ''' + """ Ensure that the named index template is absent. name Name of the index to remove - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - index_template = __salt__['elasticsearch.index_template_get'](name=name) + index_template = __salt__["elasticsearch.index_template_get"](name=name) if index_template and name in index_template: - if __opts__['test']: - ret['comment'] = 'Index template {0} will be removed'.format(name) - ret['changes']['old'] = index_template[name] - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Index template {0} will be removed".format(name) + ret["changes"]["old"] = index_template[name] + ret["result"] = None else: - ret['result'] = __salt__['elasticsearch.index_template_delete'](name=name) - if ret['result']: - ret['comment'] = 'Successfully removed index template {0}'.format(name) - ret['changes']['old'] = index_template[name] + ret["result"] = __salt__["elasticsearch.index_template_delete"]( + name=name + ) + if ret["result"]: + ret["comment"] = "Successfully removed index template {0}".format( + name + ) + ret["changes"]["old"] = index_template[name] else: - ret['comment'] = 'Failed to remove index template {0} for unknown reasons'.format(name) + ret[ + "comment" + ] = "Failed to remove index template {0} for unknown reasons".format( + name + ) else: - ret['comment'] = 'Index template {0} is already absent'.format(name) + ret["comment"] = "Index template {0} is already absent".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def index_template_present(name, definition, check_definition=False): - ''' + """ Ensure that the named index template is present. name @@ -254,96 +317,132 @@ def index_template_present(name, definition, check_definition=False): order: 1 settings: number_of_shards: 1 - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - index_template_exists = __salt__['elasticsearch.index_template_exists'](name=name) + index_template_exists = __salt__["elasticsearch.index_template_exists"]( + name=name + ) if not index_template_exists: - if __opts__['test']: - ret['comment'] = 'Index template {0} does not exist and will be created'.format(name) - ret['changes'] = {'new': definition} - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "Index template {0} does not exist and will be created".format(name) + ret["changes"] = {"new": definition} + ret["result"] = None else: - output = __salt__['elasticsearch.index_template_create'](name=name, body=definition) + output = __salt__["elasticsearch.index_template_create"]( + name=name, body=definition + ) if output: - ret['comment'] = 'Successfully created index template {0}'.format(name) - ret['changes'] = {'new': __salt__['elasticsearch.index_template_get'](name=name)[name]} + ret["comment"] = "Successfully created index template {0}".format( + name + ) + ret["changes"] = { + "new": __salt__["elasticsearch.index_template_get"](name=name)[ + name + ] + } else: - ret['result'] = False - ret['comment'] = 'Cannot create index template {0}, {1}'.format(name, output) + ret["result"] = False + ret["comment"] = "Cannot create index template {0}, {1}".format( + name, output + ) else: if check_definition: if isinstance(definition, str): definition_parsed = salt.utils.json.loads(definition) else: definition_parsed = definition - current_template = __salt__['elasticsearch.index_template_get'](name=name)[name] + current_template = __salt__["elasticsearch.index_template_get"]( + name=name + )[name] # Prune empty keys (avoid false positive diff) for key in ("mappings", "aliases", "settings"): if current_template[key] == {} and key not in definition_parsed: del current_template[key] - diff = __utils__['dictdiffer.deep_diff'](current_template, definition_parsed) + diff = __utils__["dictdiffer.deep_diff"]( + current_template, definition_parsed + ) if len(diff) != 0: - if __opts__['test']: - ret['comment'] = 'Index template {0} exist but need to be updated'.format(name) - ret['changes'] = diff - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "Index template {0} exist but need to be updated".format( + name + ) + ret["changes"] = diff + ret["result"] = None else: - output = __salt__['elasticsearch.index_template_create'](name=name, body=definition) + output = __salt__["elasticsearch.index_template_create"]( + name=name, body=definition + ) if output: - ret['comment'] = 'Successfully updated index template {0}'.format(name) - ret['changes'] = diff + ret[ + "comment" + ] = "Successfully updated index template {0}".format(name) + ret["changes"] = diff else: - ret['result'] = False - ret['comment'] = 'Cannot update index template {0}, {1}'.format(name, output) + ret["result"] = False + ret[ + "comment" + ] = "Cannot update index template {0}, {1}".format( + name, output + ) else: - ret['comment'] = 'Index template {0} is already present and up to date'.format(name) + ret[ + "comment" + ] = "Index template {0} is already present and up to date".format( + name + ) else: - ret['comment'] = 'Index template {0} is already present'.format(name) + ret["comment"] = "Index template {0} is already present".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def pipeline_absent(name): - ''' + """ Ensure that the named pipeline is absent name Name of the pipeline to remove - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - pipeline = __salt__['elasticsearch.pipeline_get'](id=name) + pipeline = __salt__["elasticsearch.pipeline_get"](id=name) if pipeline and name in pipeline: - if __opts__['test']: - ret['comment'] = 'Pipeline {0} will be removed'.format(name) - ret['changes']['old'] = pipeline[name] - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Pipeline {0} will be removed".format(name) + ret["changes"]["old"] = pipeline[name] + ret["result"] = None else: - ret['result'] = __salt__['elasticsearch.pipeline_delete'](id=name) - if ret['result']: - ret['comment'] = 'Successfully removed pipeline {0}'.format(name) - ret['changes']['old'] = pipeline[name] + ret["result"] = __salt__["elasticsearch.pipeline_delete"](id=name) + if ret["result"]: + ret["comment"] = "Successfully removed pipeline {0}".format(name) + ret["changes"]["old"] = pipeline[name] else: - ret['comment'] = 'Failed to remove pipeline {0} for unknown reasons'.format(name) + ret[ + "comment" + ] = "Failed to remove pipeline {0} for unknown reasons".format(name) else: - ret['comment'] = 'Pipeline {0} is already absent'.format(name) + ret["comment"] = "Pipeline {0} is already absent".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def pipeline_present(name, definition): - ''' + """ Ensure that the named pipeline is present. name @@ -363,79 +462,101 @@ def pipeline_present(name, definition): - set: field: collector_timestamp_millis value: '{{ '{{' }}_ingest.timestamp{{ '}}' }}' - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - pipeline = __salt__['elasticsearch.pipeline_get'](id=name) + pipeline = __salt__["elasticsearch.pipeline_get"](id=name) old = {} if pipeline and name in pipeline: old = pipeline[name] - ret['changes'] = __utils__['dictdiffer.deep_diff'](old, definition) + ret["changes"] = __utils__["dictdiffer.deep_diff"](old, definition) - if ret['changes'] or not definition: - if __opts__['test']: + if ret["changes"] or not definition: + if __opts__["test"]: if not pipeline: - ret['comment'] = 'Pipeline {0} does not exist and will be created'.format(name) + ret[ + "comment" + ] = "Pipeline {0} does not exist and will be created".format(name) else: - ret['comment'] = 'Pipeline {0} exists with wrong configuration and will be overridden'.format(name) + ret[ + "comment" + ] = "Pipeline {0} exists with wrong configuration and will be overridden".format( + name + ) - ret['result'] = None + ret["result"] = None else: - output = __salt__['elasticsearch.pipeline_create'](id=name, body=definition) + output = __salt__["elasticsearch.pipeline_create"]( + id=name, body=definition + ) if output: if not pipeline: - ret['comment'] = 'Successfully created pipeline {0}'.format(name) + ret["comment"] = "Successfully created pipeline {0}".format( + name + ) else: - ret['comment'] = 'Successfully replaced pipeline {0}'.format(name) + ret["comment"] = "Successfully replaced pipeline {0}".format( + name + ) else: - ret['result'] = False - ret['comment'] = 'Cannot create pipeline {0}, {1}'.format(name, output) + ret["result"] = False + ret["comment"] = "Cannot create pipeline {0}, {1}".format( + name, output + ) else: - ret['comment'] = 'Pipeline {0} is already present'.format(name) + ret["comment"] = "Pipeline {0} is already present".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def search_template_absent(name): - ''' + """ Ensure that the search template is absent name Name of the search template to remove - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - template = __salt__['elasticsearch.search_template_get'](id=name) + template = __salt__["elasticsearch.search_template_get"](id=name) if template: - if __opts__['test']: - ret['comment'] = 'Search template {0} will be removed'.format(name) - ret['changes']['old'] = salt.utils.json.loads(template["template"]) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Search template {0} will be removed".format(name) + ret["changes"]["old"] = salt.utils.json.loads(template["template"]) + ret["result"] = None else: - ret['result'] = __salt__['elasticsearch.search_template_delete'](id=name) - if ret['result']: - ret['comment'] = 'Successfully removed search template {0}'.format(name) - ret['changes']['old'] = salt.utils.json.loads(template["template"]) + ret["result"] = __salt__["elasticsearch.search_template_delete"]( + id=name + ) + if ret["result"]: + ret["comment"] = "Successfully removed search template {0}".format( + name + ) + ret["changes"]["old"] = salt.utils.json.loads(template["template"]) else: - ret['comment'] = 'Failed to remove search template {0} for unknown reasons'.format(name) + ret[ + "comment" + ] = "Failed to remove search template {0} for unknown reasons".format( + name + ) else: - ret['comment'] = 'Search template {0} is already absent'.format(name) + ret["comment"] = "Search template {0} is already absent".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def search_template_present(name, definition): - ''' + """ Ensure that the named search template is present. name @@ -452,41 +573,57 @@ def search_template_present(name, definition): - definition: inline: size: 10 - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - template = __salt__['elasticsearch.search_template_get'](id=name) + template = __salt__["elasticsearch.search_template_get"](id=name) old = {} if template: old = salt.utils.json.loads(template["template"]) - ret['changes'] = __utils__['dictdiffer.deep_diff'](old, definition) + ret["changes"] = __utils__["dictdiffer.deep_diff"](old, definition) - if ret['changes'] or not definition: - if __opts__['test']: + if ret["changes"] or not definition: + if __opts__["test"]: if not template: - ret['comment'] = 'Search template {0} does not exist and will be created'.format(name) + ret[ + "comment" + ] = "Search template {0} does not exist and will be created".format( + name + ) else: - ret['comment'] = 'Search template {0} exists with wrong configuration and will be overridden'.format(name) + ret[ + "comment" + ] = "Search template {0} exists with wrong configuration and will be overridden".format( + name + ) - ret['result'] = None + ret["result"] = None else: - output = __salt__['elasticsearch.search_template_create'](id=name, body=definition) + output = __salt__["elasticsearch.search_template_create"]( + id=name, body=definition + ) if output: if not template: - ret['comment'] = 'Successfully created search template {0}'.format(name) + ret[ + "comment" + ] = "Successfully created search template {0}".format(name) else: - ret['comment'] = 'Successfully replaced search template {0}'.format(name) + ret[ + "comment" + ] = "Successfully replaced search template {0}".format(name) else: - ret['result'] = False - ret['comment'] = 'Cannot create search template {0}, {1}'.format(name, output) + ret["result"] = False + ret["comment"] = "Cannot create search template {0}, {1}".format( + name, output + ) else: - ret['comment'] = 'Search template {0} is already present'.format(name) + ret["comment"] = "Search template {0} is already present".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret diff --git a/salt/states/elasticsearch_index.py b/salt/states/elasticsearch_index.py index 0a4b76557ba..a604616b68e 100644 --- a/salt/states/elasticsearch_index.py +++ b/salt/states/elasticsearch_index.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" State module to manage Elasticsearch indices .. versionadded:: 2015.8.0 .. deprecated:: 2017.7.0 Use elasticsearch state instead -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -17,40 +18,42 @@ log = logging.getLogger(__name__) def absent(name): - ''' + """ Ensure that the named index is absent. name Name of the index to remove - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - index = __salt__['elasticsearch.index_get'](index=name) + index = __salt__["elasticsearch.index_get"](index=name) if index and name in index: - if __opts__['test']: - ret['comment'] = 'Index {0} will be removed'.format(name) - ret['changes']['old'] = index[name] - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Index {0} will be removed".format(name) + ret["changes"]["old"] = index[name] + ret["result"] = None else: - ret['result'] = __salt__['elasticsearch.index_delete'](index=name) - if ret['result']: - ret['comment'] = 'Successfully removed index {0}'.format(name) - ret['changes']['old'] = index[name] + ret["result"] = __salt__["elasticsearch.index_delete"](index=name) + if ret["result"]: + ret["comment"] = "Successfully removed index {0}".format(name) + ret["changes"]["old"] = index[name] else: - ret['comment'] = 'Failed to remove index {0} for unknown reasons'.format(name) + ret[ + "comment" + ] = "Failed to remove index {0} for unknown reasons".format(name) else: - ret['comment'] = 'Index {0} is already absent'.format(name) + ret["comment"] = "Index {0} is already absent".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def present(name, definition=None): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2017.3.0 Marked ``definition`` as optional. @@ -78,29 +81,35 @@ def present(name, definition=None): settings: index: number_of_shards: 10 - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - index_exists = __salt__['elasticsearch.index_exists'](index=name) + index_exists = __salt__["elasticsearch.index_exists"](index=name) if not index_exists: - if __opts__['test']: - ret['comment'] = 'Index {0} does not exist and will be created'.format(name) - ret['changes'] = {'new': definition} - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Index {0} does not exist and will be created".format( + name + ) + ret["changes"] = {"new": definition} + ret["result"] = None else: - output = __salt__['elasticsearch.index_create'](index=name, body=definition) + output = __salt__["elasticsearch.index_create"]( + index=name, body=definition + ) if output: - ret['comment'] = 'Successfully created index {0}'.format(name) - ret['changes'] = {'new': __salt__['elasticsearch.index_get'](index=name)[name]} + ret["comment"] = "Successfully created index {0}".format(name) + ret["changes"] = { + "new": __salt__["elasticsearch.index_get"](index=name)[name] + } else: - ret['result'] = False - ret['comment'] = 'Cannot create index {0}, {1}'.format(name, output) + ret["result"] = False + ret["comment"] = "Cannot create index {0}, {1}".format(name, output) else: - ret['comment'] = 'Index {0} is already present'.format(name) + ret["comment"] = "Index {0} is already present".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret diff --git a/salt/states/elasticsearch_index_template.py b/salt/states/elasticsearch_index_template.py index 9b1e4910096..4d233f44535 100644 --- a/salt/states/elasticsearch_index_template.py +++ b/salt/states/elasticsearch_index_template.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" State module to manage Elasticsearch index templates .. versionadded:: 2015.8.0 .. deprecated:: 2017.7.0 Use elasticsearch state instead -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -17,40 +18,48 @@ log = logging.getLogger(__name__) def absent(name): - ''' + """ Ensure that the named index template is absent. name Name of the index to remove - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - index_template = __salt__['elasticsearch.index_template_get'](name=name) + index_template = __salt__["elasticsearch.index_template_get"](name=name) if index_template and name in index_template: - if __opts__['test']: - ret['comment'] = 'Index template {0} will be removed'.format(name) - ret['changes']['old'] = index_template[name] - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Index template {0} will be removed".format(name) + ret["changes"]["old"] = index_template[name] + ret["result"] = None else: - ret['result'] = __salt__['elasticsearch.index_template_delete'](name=name) - if ret['result']: - ret['comment'] = 'Successfully removed index template {0}'.format(name) - ret['changes']['old'] = index_template[name] + ret["result"] = __salt__["elasticsearch.index_template_delete"]( + name=name + ) + if ret["result"]: + ret["comment"] = "Successfully removed index template {0}".format( + name + ) + ret["changes"]["old"] = index_template[name] else: - ret['comment'] = 'Failed to remove index template {0} for unknown reasons'.format(name) + ret[ + "comment" + ] = "Failed to remove index template {0} for unknown reasons".format( + name + ) else: - ret['comment'] = 'Index template {0} is already absent'.format(name) + ret["comment"] = "Index template {0} is already absent".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret def present(name, definition): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2017.3.0 Marked ``definition`` as required. @@ -74,29 +83,43 @@ def present(name, definition): order: 1 settings: number_of_shards: 1 - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - index_template_exists = __salt__['elasticsearch.index_template_exists'](name=name) + index_template_exists = __salt__["elasticsearch.index_template_exists"]( + name=name + ) if not index_template_exists: - if __opts__['test']: - ret['comment'] = 'Index template {0} does not exist and will be created'.format(name) - ret['changes'] = {'new': definition} - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "Index template {0} does not exist and will be created".format(name) + ret["changes"] = {"new": definition} + ret["result"] = None else: - output = __salt__['elasticsearch.index_template_create'](name=name, body=definition) + output = __salt__["elasticsearch.index_template_create"]( + name=name, body=definition + ) if output: - ret['comment'] = 'Successfully created index template {0}'.format(name) - ret['changes'] = {'new': __salt__['elasticsearch.index_template_get'](name=name)[name]} + ret["comment"] = "Successfully created index template {0}".format( + name + ) + ret["changes"] = { + "new": __salt__["elasticsearch.index_template_get"](name=name)[ + name + ] + } else: - ret['result'] = False - ret['comment'] = 'Cannot create index template {0}, {1}'.format(name, output) + ret["result"] = False + ret["comment"] = "Cannot create index template {0}, {1}".format( + name, output + ) else: - ret['comment'] = 'Index template {0} is already present'.format(name) + ret["comment"] = "Index template {0} is already present".format(name) except Exception as err: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(err) + ret["result"] = False + ret["comment"] = six.text_type(err) return ret diff --git a/salt/states/environ.py b/salt/states/environ.py index 5d87a93f3c7..3a562750a30 100644 --- a/salt/states/environ.py +++ b/salt/states/environ.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Support for getting and setting the environment variables of the current salt process. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt libs @@ -16,28 +17,30 @@ from salt.ext import six def __virtual__(): - ''' + """ No dependency checks, and not renaming, just return True - ''' + """ return True def _norm_key(key): - ''' + """ Normalize windows environment keys - ''' + """ if salt.utils.platform.is_windows(): return key.upper() return key -def setenv(name, - value, - false_unsets=False, - clear_all=False, - update_minion=False, - permanent=False): - ''' +def setenv( + name, + value, + false_unsets=False, + clear_all=False, + update_minion=False, + permanent=False, +): + """ Set the salt process environment variables. name @@ -93,20 +96,17 @@ def setenv(name, - value: foo: bar baz: quux - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} environ = {} if isinstance(value, six.string_types) or value is False: environ[name] = value elif isinstance(value, dict): environ = value else: - ret['result'] = False - ret['comment'] = 'Environ value must be string, dict or False' + ret["result"] = False + ret["comment"] = "Environ value must be string, dict or False" return ret if clear_all is True: @@ -115,10 +115,10 @@ def setenv(name, for key in to_unset: if false_unsets is not True: # This key value will change to '' - ret['changes'].update({key: ''}) + ret["changes"].update({key: ""}) else: # We're going to delete the key - ret['changes'].update({key: None}) + ret["changes"].update({key: None}) current_environ = dict(os.environ) already_set = [] @@ -129,56 +129,57 @@ def setenv(name, # the value to '' def key_exists(): if salt.utils.platform.is_windows(): - permanent_hive = 'HKCU' - permanent_key = 'Environment' - if permanent == 'HKLM': - permanent_hive = 'HKLM' - permanent_key = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + permanent_hive = "HKCU" + permanent_key = "Environment" + if permanent == "HKLM": + permanent_hive = "HKLM" + permanent_key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" - out = __salt__['reg.read_value'](permanent_hive, permanent_key, _norm_key(key)) - return out['success'] is True + out = __salt__["reg.read_value"]( + permanent_hive, permanent_key, _norm_key(key) + ) + return out["success"] is True else: return False + if current_environ.get(_norm_key(key), None) is None and not key_exists(): # The key does not exist in environment if false_unsets is not True: # This key will be added with value '' - ret['changes'].update({key: ''}) + ret["changes"].update({key: ""}) else: # The key exists. if false_unsets is not True: # Check to see if the value will change - if current_environ.get(_norm_key(key), None) != '': + if current_environ.get(_norm_key(key), None) != "": # This key value will change to '' - ret['changes'].update({key: ''}) + ret["changes"].update({key: ""}) else: # We're going to delete the key - ret['changes'].update({key: None}) - elif current_environ.get(_norm_key(key), '') == val: + ret["changes"].update({key: None}) + elif current_environ.get(_norm_key(key), "") == val: already_set.append(key) else: - ret['changes'].update({key: val}) + ret["changes"].update({key: val}) - if __opts__['test']: - if ret['changes']: - ret['comment'] = 'Environ values will be changed' + if __opts__["test"]: + if ret["changes"]: + ret["comment"] = "Environ values will be changed" else: - ret['comment'] = 'Environ values are already set with the correct values' + ret["comment"] = "Environ values are already set with the correct values" return ret - if ret['changes']: - environ_ret = __salt__['environ.setenv'](environ, - false_unsets, - clear_all, - update_minion, - permanent) + if ret["changes"]: + environ_ret = __salt__["environ.setenv"]( + environ, false_unsets, clear_all, update_minion, permanent + ) if not environ_ret: - ret['result'] = False - ret['comment'] = 'Failed to set environ variables' + ret["result"] = False + ret["comment"] = "Failed to set environ variables" return ret - ret['result'] = True - ret['changes'] = environ_ret - ret['comment'] = 'Environ values were set' + ret["result"] = True + ret["changes"] = environ_ret + ret["comment"] = "Environ values were set" else: - ret['comment'] = 'Environ values were already set with the correct values' + ret["comment"] = "Environ values were already set with the correct values" return ret diff --git a/salt/states/eselect.py b/salt/states/eselect.py index 22993ade4b3..112bf5abbcf 100644 --- a/salt/states/eselect.py +++ b/salt/states/eselect.py @@ -1,30 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" Management of Gentoo configuration using eselect ================================================ A state module to manage Gentoo configuration via eselect -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Define a function alias in order not to shadow built-in's -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Only load if the eselect module is available in __salt__ - ''' - return 'eselect' if 'eselect.exec_action' in __salt__ else False + """ + if "eselect.exec_action" in __salt__: + return "eselect" + return (False, "eselect module could not be loaded") def set_(name, target, module_parameter=None, action_parameter=None): - ''' + """ Verify that the given module is set to the given target name @@ -44,42 +44,42 @@ def set_(name, target, module_parameter=None, action_parameter=None): profile: eselect.set: - target: hardened/linux/amd64 - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} - old_target = __salt__['eselect.get_current_target'](name, module_parameter=module_parameter, action_parameter=action_parameter) + old_target = __salt__["eselect.get_current_target"]( + name, module_parameter=module_parameter, action_parameter=action_parameter + ) if target == old_target: - ret['comment'] = 'Target \'{0}\' is already set on \'{1}\' module.'.format( + ret["comment"] = "Target '{0}' is already set on '{1}' module.".format( target, name ) - elif target not in __salt__['eselect.get_target_list'](name, action_parameter=action_parameter): - ret['comment'] = ( - 'Target \'{0}\' is not available for \'{1}\' module.'.format( - target, name - ) - ) - ret['result'] = False - elif __opts__['test']: - ret['comment'] = 'Target \'{0}\' will be set on \'{1}\' module.'.format( + elif target not in __salt__["eselect.get_target_list"]( + name, action_parameter=action_parameter + ): + ret["comment"] = "Target '{0}' is not available for '{1}' module.".format( target, name ) - ret['result'] = None + ret["result"] = False + elif __opts__["test"]: + ret["comment"] = "Target '{0}' will be set on '{1}' module.".format( + target, name + ) + ret["result"] = None else: - result = __salt__['eselect.set_target'](name, target, module_parameter=module_parameter, action_parameter=action_parameter) + result = __salt__["eselect.set_target"]( + name, + target, + module_parameter=module_parameter, + action_parameter=action_parameter, + ) if result: - ret['changes'][name] = {'old': old_target, 'new': target} - ret['comment'] = 'Target \'{0}\' set on \'{1}\' module.'.format( + ret["changes"][name] = {"old": old_target, "new": target} + ret["comment"] = "Target '{0}' set on '{1}' module.".format(target, name) + else: + ret["comment"] = "Target '{0}' failed to be set on '{1}' module.".format( target, name ) - else: - ret['comment'] = ( - 'Target \'{0}\' failed to be set on \'{1}\' module.'.format( - target, name - ) - ) - ret['result'] = False + ret["result"] = False return ret diff --git a/salt/states/esxcluster.py b/salt/states/esxcluster.py index d3bad8c2dde..5368fd97c59 100644 --- a/salt/states/esxcluster.py +++ b/salt/states/esxcluster.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage VMware ESXi Clusters. Dependencies @@ -37,32 +37,34 @@ version currently listed in PyPi, run the following: The 5.5.0.2014.1.1 is a known stable version that this original ESXi State Module was developed against. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import traceback import sys +import traceback # Import Salt Libs import salt.exceptions +from salt.config.schemas.esxcluster import ESXClusterConfigSchema, LicenseSchema from salt.ext import six +from salt.utils import dictupdate from salt.utils.dictdiffer import recursive_diff from salt.utils.listdiffer import list_diff -from salt.config.schemas.esxcluster import ESXClusterConfigSchema, \ - LicenseSchema -from salt.utils import dictupdate # External libraries try: import jsonschema + HAS_JSONSCHEMA = True except ImportError: HAS_JSONSCHEMA = False try: from pyVmomi import VmomiSupport + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False @@ -73,44 +75,54 @@ log = logging.getLogger(__name__) def __virtual__(): if not HAS_JSONSCHEMA: - return False, 'State module did not load: jsonschema not found' + return False, "State module did not load: jsonschema not found" if not HAS_PYVMOMI: - return False, 'State module did not load: pyVmomi not found' + return False, "State module did not load: pyVmomi not found" # We check the supported vim versions to infer the pyVmomi version - if 'vim25/6.0' in VmomiSupport.versionMap and \ - sys.version_info > (2, 7) and sys.version_info < (2, 7, 9): + if ( + "vim25/6.0" in VmomiSupport.versionMap + and sys.version_info > (2, 7) + and sys.version_info < (2, 7, 9) + ): - return False, ('State module did not load: Incompatible versions ' - 'of Python and pyVmomi present. See Issue #29537.') + return ( + False, + ( + "State module did not load: Incompatible versions " + "of Python and pyVmomi present. See Issue #29537." + ), + ) return True def mod_init(low): - ''' + """ Retrieves and adapt the login credentials from the proxy connection module - ''' + """ return True def _get_vsan_datastore(si, cluster_name): - '''Retrieves the vsan_datastore''' + """Retrieves the vsan_datastore""" - log.trace('Retrieving vsan datastore') - vsan_datastores = [ds for ds in - __salt__['vsphere.list_datastores_via_proxy']( - service_instance=si) - if ds['type'] == 'vsan'] + log.trace("Retrieving vsan datastore") + vsan_datastores = [ + ds + for ds in __salt__["vsphere.list_datastores_via_proxy"](service_instance=si) + if ds["type"] == "vsan" + ] if not vsan_datastores: raise salt.exceptions.VMwareObjectRetrievalError( - 'No vSAN datastores where retrieved for cluster ' - '\'{0}\''.format(cluster_name)) + "No vSAN datastores where retrieved for cluster " + "'{0}'".format(cluster_name) + ) return vsan_datastores[0] def cluster_configured(name, cluster_config): - ''' + """ Configures a cluster. Creates a new cluster, if it doesn't exist on the vCenter or reconfigures it if configured differently @@ -151,209 +163,237 @@ def cluster_configured(name, cluster_config): dedup_enabled: true enabled: true - ''' - proxy_type = __salt__['vsphere.get_proxy_type']() - if proxy_type == 'esxdatacenter': - cluster_name, datacenter_name = \ - name, __salt__['esxdatacenter.get_details']()['datacenter'] - elif proxy_type == 'esxcluster': - cluster_name, datacenter_name = \ - __salt__['esxcluster.get_details']()['cluster'], \ - __salt__['esxcluster.get_details']()['datacenter'] + """ + proxy_type = __salt__["vsphere.get_proxy_type"]() + if proxy_type == "esxdatacenter": + cluster_name, datacenter_name = ( + name, + __salt__["esxdatacenter.get_details"]()["datacenter"], + ) + elif proxy_type == "esxcluster": + cluster_name, datacenter_name = ( + __salt__["esxcluster.get_details"]()["cluster"], + __salt__["esxcluster.get_details"]()["datacenter"], + ) else: - raise salt.exceptions.CommandExecutionError('Unsupported proxy {0}' - ''.format(proxy_type)) - log.info('Running {0} for cluster \'{1}\' in datacenter ' - '\'{2}\''.format(name, cluster_name, datacenter_name)) + raise salt.exceptions.CommandExecutionError( + "Unsupported proxy {0}" "".format(proxy_type) + ) + log.info( + "Running {0} for cluster '{1}' in datacenter " + "'{2}'".format(name, cluster_name, datacenter_name) + ) cluster_dict = cluster_config - log.trace('cluster_dict = {0}'.format(cluster_dict)) + log.trace("cluster_dict = {0}".format(cluster_dict)) changes_required = False - ret = {'name': name, - 'changes': {}, 'result': None, 'comment': 'Default'} + ret = {"name": name, "changes": {}, "result": None, "comment": "Default"} comments = [] changes = {} changes_required = False try: - log.trace('Validating cluster_configured state input') + log.trace("Validating cluster_configured state input") schema = ESXClusterConfigSchema.serialize() - log.trace('schema = {0}'.format(schema)) + log.trace("schema = {0}".format(schema)) try: jsonschema.validate(cluster_dict, schema) except jsonschema.exceptions.ValidationError as exc: raise salt.exceptions.InvalidESXClusterPayloadError(exc) current = None - si = __salt__['vsphere.get_service_instance_via_proxy']() + si = __salt__["vsphere.get_service_instance_via_proxy"]() try: - current = __salt__['vsphere.list_cluster'](datacenter_name, - cluster_name, - service_instance=si) + current = __salt__["vsphere.list_cluster"]( + datacenter_name, cluster_name, service_instance=si + ) except salt.exceptions.VMwareObjectRetrievalError: changes_required = True - if __opts__['test']: - comments.append('State {0} will create cluster ' - '\'{1}\' in datacenter \'{2}\'.' - ''.format(name, cluster_name, datacenter_name)) + if __opts__["test"]: + comments.append( + "State {0} will create cluster " + "'{1}' in datacenter '{2}'." + "".format(name, cluster_name, datacenter_name) + ) log.info(comments[-1]) - __salt__['vsphere.disconnect'](si) - ret.update({'result': None, - 'comment': '\n'.join(comments)}) + __salt__["vsphere.disconnect"](si) + ret.update({"result": None, "comment": "\n".join(comments)}) return ret - log.trace('Creating cluster \'{0}\' in datacenter \'{1}\'. ' - ''.format(cluster_name, datacenter_name)) - __salt__['vsphere.create_cluster'](cluster_dict, - datacenter_name, - cluster_name, - service_instance=si) - comments.append('Created cluster \'{0}\' in datacenter \'{1}\'' - ''.format(cluster_name, datacenter_name)) + log.trace( + "Creating cluster '{0}' in datacenter '{1}'. " + "".format(cluster_name, datacenter_name) + ) + __salt__["vsphere.create_cluster"]( + cluster_dict, datacenter_name, cluster_name, service_instance=si + ) + comments.append( + "Created cluster '{0}' in datacenter '{1}'" + "".format(cluster_name, datacenter_name) + ) log.info(comments[-1]) - changes.update({'new': cluster_dict}) + changes.update({"new": cluster_dict}) if current: # Cluster already exists # We need to handle lists sepparately ldiff = None - if 'ha' in cluster_dict and 'options' in cluster_dict['ha']: - ldiff = list_diff(current.get('ha', {}).get('options', []), - cluster_dict.get('ha', {}).get('options', []), - 'key') - log.trace('options diffs = {0}'.format(ldiff.diffs)) + if "ha" in cluster_dict and "options" in cluster_dict["ha"]: + ldiff = list_diff( + current.get("ha", {}).get("options", []), + cluster_dict.get("ha", {}).get("options", []), + "key", + ) + log.trace("options diffs = {0}".format(ldiff.diffs)) # Remove options if exist - del cluster_dict['ha']['options'] - if 'ha' in current and 'options' in current['ha']: - del current['ha']['options'] + del cluster_dict["ha"]["options"] + if "ha" in current and "options" in current["ha"]: + del current["ha"]["options"] diff = recursive_diff(current, cluster_dict) - log.trace('diffs = {0}'.format(diff.diffs)) + log.trace("diffs = {0}".format(diff.diffs)) if not (diff.diffs or (ldiff and ldiff.diffs)): # No differences - comments.append('Cluster \'{0}\' in datacenter \'{1}\' is up ' - 'to date. Nothing to be done.' - ''.format(cluster_name, datacenter_name)) + comments.append( + "Cluster '{0}' in datacenter '{1}' is up " + "to date. Nothing to be done." + "".format(cluster_name, datacenter_name) + ) log.info(comments[-1]) else: changes_required = True - changes_str = '' + changes_str = "" if diff.diffs: - changes_str = '{0}{1}'.format(changes_str, - diff.changes_str) + changes_str = "{0}{1}".format(changes_str, diff.changes_str) if ldiff and ldiff.diffs: - changes_str = '{0}\nha:\n options:\n{1}'.format( + changes_str = "{0}\nha:\n options:\n{1}".format( changes_str, - '\n'.join([' {0}'.format(l) for l in - ldiff.changes_str2.split('\n')])) + "\n".join( + [" {0}".format(l) for l in ldiff.changes_str2.split("\n")] + ), + ) # Apply the changes - if __opts__['test']: + if __opts__["test"]: comments.append( - 'State {0} will update cluster \'{1}\' ' - 'in datacenter \'{2}\':\n{3}' - ''.format(name, cluster_name, - datacenter_name, changes_str)) + "State {0} will update cluster '{1}' " + "in datacenter '{2}':\n{3}" + "".format(name, cluster_name, datacenter_name, changes_str) + ) else: new_values = diff.new_values old_values = diff.old_values if ldiff and ldiff.new_values: dictupdate.update( - new_values, {'ha': {'options': ldiff.new_values}}) + new_values, {"ha": {"options": ldiff.new_values}} + ) if ldiff and ldiff.old_values: dictupdate.update( - old_values, {'ha': {'options': ldiff.old_values}}) - log.trace('new_values = {0}'.format(new_values)) - __salt__['vsphere.update_cluster'](new_values, - datacenter_name, - cluster_name, - service_instance=si) - comments.append('Updated cluster \'{0}\' in datacenter ' - '\'{1}\''.format(cluster_name, - datacenter_name)) + old_values, {"ha": {"options": ldiff.old_values}} + ) + log.trace("new_values = {0}".format(new_values)) + __salt__["vsphere.update_cluster"]( + new_values, datacenter_name, cluster_name, service_instance=si + ) + comments.append( + "Updated cluster '{0}' in datacenter " + "'{1}'".format(cluster_name, datacenter_name) + ) log.info(comments[-1]) - changes.update({'new': new_values, - 'old': old_values}) - __salt__['vsphere.disconnect'](si) + changes.update({"new": new_values, "old": old_values}) + __salt__["vsphere.disconnect"](si) ret_status = True - if __opts__['test'] and changes_required: + if __opts__["test"] and changes_required: ret_status = None - ret.update({'result': ret_status, - 'comment': '\n'.join(comments), - 'changes': changes}) + ret.update( + {"result": ret_status, "comment": "\n".join(comments), "changes": changes} + ) return ret except salt.exceptions.CommandExecutionError as exc: - log.error('Error: {0}\n{1}'.format(exc, traceback.format_exc())) + log.error("Error: {0}\n{1}".format(exc, traceback.format_exc())) if si: - __salt__['vsphere.disconnect'](si) - ret.update({ - 'result': False, - 'comment': six.text_type(exc)}) + __salt__["vsphere.disconnect"](si) + ret.update({"result": False, "comment": six.text_type(exc)}) return ret def vsan_datastore_configured(name, datastore_name): - ''' + """ Configures the cluster's VSAN datastore WARNING: The VSAN datastore is created automatically after the first ESXi host is added to the cluster; the state assumes that the datastore exists and errors if it doesn't. - ''' + """ - cluster_name, datacenter_name = \ - __salt__['esxcluster.get_details']()['cluster'], \ - __salt__['esxcluster.get_details']()['datacenter'] - display_name = '{0}/{1}'.format(datacenter_name, cluster_name) - log.info('Running vsan_datastore_configured for ' - '\'{0}\''.format(display_name)) - ret = {'name': name, - 'changes': {}, 'result': None, - 'comment': 'Default'} + cluster_name, datacenter_name = ( + __salt__["esxcluster.get_details"]()["cluster"], + __salt__["esxcluster.get_details"]()["datacenter"], + ) + display_name = "{0}/{1}".format(datacenter_name, cluster_name) + log.info("Running vsan_datastore_configured for " "'{0}'".format(display_name)) + ret = {"name": name, "changes": {}, "result": None, "comment": "Default"} comments = [] changes = {} changes_required = False try: - si = __salt__['vsphere.get_service_instance_via_proxy']() + si = __salt__["vsphere.get_service_instance_via_proxy"]() # Checking if we need to rename the vsan datastore vsan_ds = _get_vsan_datastore(si, cluster_name) - if vsan_ds['name'] == datastore_name: - comments.append('vSAN datastore is correctly named \'{0}\'. ' - 'Nothing to be done.'.format(vsan_ds['name'])) + if vsan_ds["name"] == datastore_name: + comments.append( + "vSAN datastore is correctly named '{0}'. " + "Nothing to be done.".format(vsan_ds["name"]) + ) log.info(comments[-1]) else: # vsan_ds needs to be updated changes_required = True - if __opts__['test']: - comments.append('State {0} will rename the vSAN datastore to ' - '\'{1}\'.'.format(name, datastore_name)) + if __opts__["test"]: + comments.append( + "State {0} will rename the vSAN datastore to " + "'{1}'.".format(name, datastore_name) + ) log.info(comments[-1]) else: - log.trace('Renaming vSAN datastore \'{0}\' to \'{1}\'' - ''.format(vsan_ds['name'], datastore_name)) - __salt__['vsphere.rename_datastore']( - datastore_name=vsan_ds['name'], + log.trace( + "Renaming vSAN datastore '{0}' to '{1}'" + "".format(vsan_ds["name"], datastore_name) + ) + __salt__["vsphere.rename_datastore"]( + datastore_name=vsan_ds["name"], new_datastore_name=datastore_name, - service_instance=si) - comments.append('Renamed vSAN datastore to \'{0}\'.' - ''.format(datastore_name)) - changes = {'vsan_datastore': {'new': {'name': datastore_name}, - 'old': {'name': vsan_ds['name']}}} + service_instance=si, + ) + comments.append( + "Renamed vSAN datastore to '{0}'." "".format(datastore_name) + ) + changes = { + "vsan_datastore": { + "new": {"name": datastore_name}, + "old": {"name": vsan_ds["name"]}, + } + } log.info(comments[-1]) - __salt__['vsphere.disconnect'](si) + __salt__["vsphere.disconnect"](si) - ret.update({'result': True if (not changes_required) else None if - __opts__['test'] else True, - 'comment': '\n'.join(comments), - 'changes': changes}) + ret.update( + { + "result": True + if (not changes_required) + else None + if __opts__["test"] + else True, + "comment": "\n".join(comments), + "changes": changes, + } + ) return ret except salt.exceptions.CommandExecutionError as exc: - log.error('Error: {0}\n{1}'.format(exc, traceback.format_exc())) + log.error("Error: {0}\n{1}".format(exc, traceback.format_exc())) if si: - __salt__['vsphere.disconnect'](si) - ret.update({ - 'result': False, - 'comment': exc.strerror}) + __salt__["vsphere.disconnect"](si) + ret.update({"result": False, "comment": exc.strerror}) return ret def licenses_configured(name, licenses=None): - ''' + """ Configures licenses on the cluster entity Checks if each license exists on the server: @@ -363,23 +403,19 @@ def licenses_configured(name, licenses=None): - assign it to the cluster if there is space - error if there's no space - if it's assigned to the cluster nothing needs to be done - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': 'Default'} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": "Default"} if not licenses: - raise salt.exceptions.ArgumentValueError('No licenses provided') - cluster_name, datacenter_name = \ - __salt__['esxcluster.get_details']()['cluster'], \ - __salt__['esxcluster.get_details']()['datacenter'] - display_name = '{0}/{1}'.format(datacenter_name, cluster_name) - log.info('Running licenses configured for \'{0}\''.format(display_name)) - log.trace('licenses = {0}'.format(licenses)) - entity = {'type': 'cluster', - 'datacenter': datacenter_name, - 'cluster': cluster_name} - log.trace('entity = {0}'.format(entity)) + raise salt.exceptions.ArgumentValueError("No licenses provided") + cluster_name, datacenter_name = ( + __salt__["esxcluster.get_details"]()["cluster"], + __salt__["esxcluster.get_details"]()["datacenter"], + ) + display_name = "{0}/{1}".format(datacenter_name, cluster_name) + log.info("Running licenses configured for '{0}'".format(display_name)) + log.trace("licenses = {0}".format(licenses)) + entity = {"type": "cluster", "datacenter": datacenter_name, "cluster": cluster_name} + log.trace("entity = {0}".format(entity)) comments = [] changes = {} @@ -389,60 +425,60 @@ def licenses_configured(name, licenses=None): needs_changes = False try: # Validate licenses - log.trace('Validating licenses') + log.trace("Validating licenses") schema = LicenseSchema.serialize() try: - jsonschema.validate({'licenses': licenses}, schema) + jsonschema.validate({"licenses": licenses}, schema) except jsonschema.exceptions.ValidationError as exc: raise salt.exceptions.InvalidLicenseError(exc) - si = __salt__['vsphere.get_service_instance_via_proxy']() + si = __salt__["vsphere.get_service_instance_via_proxy"]() # Retrieve licenses - existing_licenses = __salt__['vsphere.list_licenses']( - service_instance=si) + existing_licenses = __salt__["vsphere.list_licenses"](service_instance=si) remaining_licenses = existing_licenses[:] # Cycle through licenses for license_name, license in licenses.items(): # Check if license already exists - filtered_licenses = [l for l in existing_licenses - if l['key'] == license] + filtered_licenses = [l for l in existing_licenses if l["key"] == license] # TODO Update license description - not of interest right now if not filtered_licenses: # License doesn't exist - add and assign to cluster needs_changes = True - if __opts__['test']: + if __opts__["test"]: # If it doesn't exist it clearly needs to be assigned as # well so we can stop the check here - comments.append('State {0} will add license \'{1}\', ' - 'and assign it to cluster \'{2}\'.' - ''.format(name, license_name, display_name)) + comments.append( + "State {0} will add license '{1}', " + "and assign it to cluster '{2}'." + "".format(name, license_name, display_name) + ) log.info(comments[-1]) continue else: try: - existing_license = __salt__['vsphere.add_license']( - key=license, description=license_name, - service_instance=si) + existing_license = __salt__["vsphere.add_license"]( + key=license, description=license_name, service_instance=si + ) except salt.exceptions.VMwareApiError as ex: comments.append(ex.err_msg) log.error(comments[-1]) has_errors = True continue - comments.append('Added license \'{0}\'.' - ''.format(license_name)) + comments.append("Added license '{0}'." "".format(license_name)) log.info(comments[-1]) else: # License exists let's check if it's assigned to the cluster - comments.append('License \'{0}\' already exists. ' - 'Nothing to be done.'.format(license_name)) + comments.append( + "License '{0}' already exists. " + "Nothing to be done.".format(license_name) + ) log.info(comments[-1]) existing_license = filtered_licenses[0] - log.trace('Checking licensed entities...') - assigned_licenses = __salt__['vsphere.list_assigned_licenses']( - entity=entity, - entity_display_name=display_name, - service_instance=si) + log.trace("Checking licensed entities...") + assigned_licenses = __salt__["vsphere.list_assigned_licenses"]( + entity=entity, entity_display_name=display_name, service_instance=si + ) # Checking if any of the licenses already assigned have the same # name as the new license; the already assigned license would be @@ -452,52 +488,60 @@ def licenses_configured(name, licenses=None): # replaced as well, but searching for those would be very complex # # the name check if good enough for now - already_assigned_license = assigned_licenses[0] if \ - assigned_licenses else None + already_assigned_license = ( + assigned_licenses[0] if assigned_licenses else None + ) - if already_assigned_license and \ - already_assigned_license['key'] == license: + if already_assigned_license and already_assigned_license["key"] == license: # License is already assigned to entity - comments.append('License \'{0}\' already assigned to ' - 'cluster \'{1}\'. Nothing to be done.' - ''.format(license_name, display_name)) + comments.append( + "License '{0}' already assigned to " + "cluster '{1}'. Nothing to be done." + "".format(license_name, display_name) + ) log.info(comments[-1]) continue needs_changes = True # License needs to be assigned to entity - if existing_license['capacity'] <= existing_license['used']: + if existing_license["capacity"] <= existing_license["used"]: # License is already fully used - comments.append('Cannot assign license \'{0}\' to cluster ' - '\'{1}\'. No free capacity available.' - ''.format(license_name, display_name)) + comments.append( + "Cannot assign license '{0}' to cluster " + "'{1}'. No free capacity available." + "".format(license_name, display_name) + ) log.error(comments[-1]) has_errors = True continue # Assign license - if __opts__['test']: - comments.append('State {0} will assign license \'{1}\' ' - 'to cluster \'{2}\'.'.format( - name, license_name, display_name)) + if __opts__["test"]: + comments.append( + "State {0} will assign license '{1}' " + "to cluster '{2}'.".format(name, license_name, display_name) + ) log.info(comments[-1]) else: try: - __salt__['vsphere.assign_license']( + __salt__["vsphere.assign_license"]( license_key=license, license_name=license_name, entity=entity, entity_display_name=display_name, - service_instance=si) + service_instance=si, + ) except salt.exceptions.VMwareApiError as ex: comments.append(ex.err_msg) log.error(comments[-1]) has_errors = True continue - comments.append('Assigned license \'{0}\' to cluster \'{1}\'.' - ''.format(license_name, display_name)) + comments.append( + "Assigned license '{0}' to cluster '{1}'." + "".format(license_name, display_name) + ) log.info(comments[-1]) # Note: Because the already_assigned_license was retrieved # from the assignment license manager it doesn't have a used @@ -506,34 +550,44 @@ def licenses_configured(name, licenses=None): # the value # Hide license keys - assigned_license = __salt__['vsphere.list_assigned_licenses']( - entity=entity, - entity_display_name=display_name, - service_instance=si)[0] - assigned_license['key'] = '<hidden>' + assigned_license = __salt__["vsphere.list_assigned_licenses"]( + entity=entity, entity_display_name=display_name, service_instance=si + )[0] + assigned_license["key"] = "<hidden>" if already_assigned_license: - already_assigned_license['key'] = '<hidden>' - if already_assigned_license and \ - already_assigned_license['capacity'] == sys.maxsize: + already_assigned_license["key"] = "<hidden>" + if ( + already_assigned_license + and already_assigned_license["capacity"] == sys.maxsize + ): - already_assigned_license['capacity'] = 'Unlimited' + already_assigned_license["capacity"] = "Unlimited" - changes[license_name] = {'new': assigned_license, - 'old': already_assigned_license} + changes[license_name] = { + "new": assigned_license, + "old": already_assigned_license, + } continue - __salt__['vsphere.disconnect'](si) + __salt__["vsphere.disconnect"](si) - ret.update({'result': True if (not needs_changes) else None if - __opts__['test'] else False if has_errors else True, - 'comment': '\n'.join(comments), - 'changes': changes if not __opts__['test'] else {}}) + ret.update( + { + "result": True + if (not needs_changes) + else None + if __opts__["test"] + else False + if has_errors + else True, + "comment": "\n".join(comments), + "changes": changes if not __opts__["test"] else {}, + } + ) return ret except salt.exceptions.CommandExecutionError as exc: - log.error('Error: {0}\n{1}'.format(exc, traceback.format_exc())) + log.error("Error: {0}\n{1}".format(exc, traceback.format_exc())) if si: - __salt__['vsphere.disconnect'](si) - ret.update({ - 'result': False, - 'comment': exc.strerror}) + __salt__["vsphere.disconnect"](si) + ret.update({"result": False, "comment": exc.strerror}) return ret diff --git a/salt/states/esxdatacenter.py b/salt/states/esxdatacenter.py index ae83b4d3717..f62353e51d0 100644 --- a/salt/states/esxdatacenter.py +++ b/salt/states/esxdatacenter.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Salt states to create and manage VMware vSphere datacenters (datacenters). -:codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstaley.com>` +:codeauthor: `Alexandru Bleotu <alexandru.bleotu@morganstaley.com>` Dependencies ============ @@ -32,6 +32,7 @@ Example: Proxy minion configuration (connects passthrough to the vCenter): .. code-block:: yaml + proxy: proxytype: esxdatacenter datacenter: target_dc @@ -46,15 +47,17 @@ State configuration: datacenter_state: esxdatacenter.datacenter_configured -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.exceptions + # Import Salt Libs from salt.ext import six -import salt.exceptions # Get Logging Started log = logging.getLogger(__name__) @@ -62,7 +65,7 @@ LOGIN_DETAILS = {} def __virtual__(): - return 'esxdatacenter' + return "esxdatacenter" def mod_init(low): @@ -70,7 +73,7 @@ def mod_init(low): def datacenter_configured(name): - ''' + """ Makes sure a datacenter exists. If the state is run by an ``esxdatacenter`` minion, the name of the @@ -81,47 +84,50 @@ def datacenter_configured(name): name: Datacenter name. Ignored if the proxytype is ``esxdatacenter``. - ''' - proxy_type = __salt__['vsphere.get_proxy_type']() - if proxy_type == 'esxdatacenter': - dc_name = __salt__['esxdatacenter.get_details']()['datacenter'] + """ + proxy_type = __salt__["vsphere.get_proxy_type"]() + if proxy_type == "esxdatacenter": + dc_name = __salt__["esxdatacenter.get_details"]()["datacenter"] else: dc_name = name - log.info('Running datacenter_configured for datacenter \'{0}\'' - ''.format(dc_name)) - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': 'Default'} + log.info("Running datacenter_configured for datacenter '{0}'" "".format(dc_name)) + ret = {"name": name, "changes": {}, "result": None, "comment": "Default"} comments = [] si = None try: - si = __salt__['vsphere.get_service_instance_via_proxy']() - dcs = __salt__['vsphere.list_datacenters_via_proxy']( - datacenter_names=[dc_name], service_instance=si) + si = __salt__["vsphere.get_service_instance_via_proxy"]() + dcs = __salt__["vsphere.list_datacenters_via_proxy"]( + datacenter_names=[dc_name], service_instance=si + ) if not dcs: - if __opts__['test']: - comments.append('State will create ' - 'datacenter \'{0}\'.'.format(dc_name)) + if __opts__["test"]: + comments.append( + "State will create " "datacenter '{0}'.".format(dc_name) + ) else: - log.debug('Creating datacenter \'{0}\'. '.format(dc_name)) - __salt__['vsphere.create_datacenter'](dc_name, si) - comments.append('Created datacenter \'{0}\'.'.format(dc_name)) + log.debug("Creating datacenter '{0}'. ".format(dc_name)) + __salt__["vsphere.create_datacenter"](dc_name, si) + comments.append("Created datacenter '{0}'.".format(dc_name)) log.info(comments[-1]) - ret['changes'].update({'new': {'name': dc_name}}) + ret["changes"].update({"new": {"name": dc_name}}) else: - comments.append('Datacenter \'{0}\' already exists. Nothing to be ' - 'done.'.format(dc_name)) + comments.append( + "Datacenter '{0}' already exists. Nothing to be " + "done.".format(dc_name) + ) log.info(comments[-1]) - __salt__['vsphere.disconnect'](si) - ret['comment'] = '\n'.join(comments) - ret['result'] = None if __opts__['test'] and ret['changes'] else True + __salt__["vsphere.disconnect"](si) + ret["comment"] = "\n".join(comments) + ret["result"] = None if __opts__["test"] and ret["changes"] else True return ret except salt.exceptions.CommandExecutionError as exc: - log.error('Error: {}'.format(exc)) + log.error("Error: {}".format(exc)) if si: - __salt__['vsphere.disconnect'](si) - ret.update({ - 'result': False if not __opts__['test'] else None, - 'comment': six.text_type(exc)}) + __salt__["vsphere.disconnect"](si) + ret.update( + { + "result": False if not __opts__["test"] else None, + "comment": six.text_type(exc), + } + ) return ret diff --git a/salt/states/esxi.py b/salt/states/esxi.py index f836939ea0f..6f4d44306b5 100644 --- a/salt/states/esxi.py +++ b/salt/states/esxi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage VMware ESXi Hosts. .. versionadded:: 2015.8.4 @@ -90,26 +90,33 @@ ESXi Proxy Minion, please refer to the 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 sys import re +import sys + +import salt.utils.files +from salt.config.schemas.esxi import DiskGroupsDiskScsiAddressSchema, HostCacheSchema +from salt.exceptions import ( + ArgumentValueError, + CommandExecutionError, + InvalidConfigError, + VMwareApiError, + VMwareObjectRetrievalError, + VMwareSaltError, +) # Import Salt Libs from salt.ext import six -import salt.utils.files -from salt.exceptions import CommandExecutionError, InvalidConfigError, \ - VMwareObjectRetrievalError, VMwareSaltError, VMwareApiError, \ - ArgumentValueError from salt.utils.decorators import depends -from salt.config.schemas.esxi import DiskGroupsDiskScsiAddressSchema, \ - HostCacheSchema # External libraries try: import jsonschema + HAS_JSONSCHEMA = True except ImportError: HAS_JSONSCHEMA = False @@ -121,11 +128,15 @@ try: from pyVmomi import VmomiSupport # We check the supported vim versions to infer the pyVmomi version - if 'vim25/6.0' in VmomiSupport.versionMap and \ - sys.version_info > (2, 7) and sys.version_info < (2, 7, 9): + if ( + "vim25/6.0" in VmomiSupport.versionMap + and sys.version_info > (2, 7) + and sys.version_info < (2, 7, 9) + ): - log.debug('pyVmomi not loaded: Incompatible versions ' - 'of Python. See Issue #29537.') + log.debug( + "pyVmomi not loaded: Incompatible versions " "of Python. See Issue #29537." + ) raise ImportError() HAS_PYVMOMI = True except ImportError: @@ -133,11 +144,13 @@ except ImportError: def __virtual__(): - return 'esxi.cmd' in __salt__ + if "esxi.cmd" in __salt__: + return True + return (False, "esxi module could not be loaded") -def coredump_configured(name, enabled, dump_ip, host_vnic='vmk0', dump_port=6500): - ''' +def coredump_configured(name, enabled, dump_ip, host_vnic="vmk0", dump_port=6500): + """ Ensures a host's core dump configuration. name @@ -176,106 +189,104 @@ def coredump_configured(name, enabled, dump_ip, host_vnic='vmk0', dump_port=6500 - enabled: True - dump_ip: 'my-coredump-ip.example.com' - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} - esxi_cmd = 'esxi.cmd' - enabled_msg = 'ESXi requires that the core dump must be enabled ' \ - 'before any other parameters may be set.' - host = __pillar__['proxy']['host'] + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} + esxi_cmd = "esxi.cmd" + enabled_msg = ( + "ESXi requires that the core dump must be enabled " + "before any other parameters may be set." + ) + host = __pillar__["proxy"]["host"] - current_config = __salt__[esxi_cmd]('get_coredump_network_config').get(host) - error = current_config.get('Error') + 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: {0}".format(error) return ret - current_config = current_config.get('Coredump Config') - current_enabled = current_config.get('enabled') + current_config = current_config.get("Coredump Config") + current_enabled = current_config.get("enabled") # Configure coredump enabled state, if there are changes. if current_enabled != enabled: - enabled_changes = {'enabled': {'old': current_enabled, 'new': enabled}} + enabled_changes = {"enabled": {"old": current_enabled, "new": enabled}} # Only run the command if not using test=True - if not __opts__['test']: - response = __salt__[esxi_cmd]('coredump_network_enable', - enabled=enabled).get(host) - error = response.get('Error') + if not __opts__["test"]: + response = __salt__[esxi_cmd]( + "coredump_network_enable", enabled=enabled + ).get(host) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret # Allow users to disable core dump, but then return since # nothing else can be set if core dump is disabled. if not enabled: - ret['result'] = True - ret['comment'] = enabled_msg - ret['changes'].update(enabled_changes) + ret["result"] = True + ret["comment"] = enabled_msg + ret["changes"].update(enabled_changes) return ret - ret['changes'].update(enabled_changes) + ret["changes"].update(enabled_changes) elif not enabled: # If current_enabled and enabled match, but are both False, # We must return before configuring anything. This isn't a # failure as core dump may be disabled intentionally. - ret['result'] = True - ret['comment'] = enabled_msg + ret["result"] = True + ret["comment"] = enabled_msg return ret # Test for changes with all remaining configurations. The changes flag is used # To detect changes, and then set_coredump_network_config is called one time. changes = False - current_ip = current_config.get('ip') + current_ip = current_config.get("ip") if current_ip != dump_ip: - ret['changes'].update({'dump_ip': - {'old': current_ip, - 'new': dump_ip}}) + ret["changes"].update({"dump_ip": {"old": current_ip, "new": dump_ip}}) changes = True - current_vnic = current_config.get('host_vnic') + current_vnic = current_config.get("host_vnic") if current_vnic != host_vnic: - ret['changes'].update({'host_vnic': - {'old': current_vnic, - 'new': host_vnic}}) + ret["changes"].update({"host_vnic": {"old": current_vnic, "new": host_vnic}}) changes = True - current_port = current_config.get('port') + current_port = current_config.get("port") if current_port != six.text_type(dump_port): - ret['changes'].update({'dump_port': - {'old': current_port, - 'new': six.text_type(dump_port)}}) + ret["changes"].update( + {"dump_port": {"old": current_port, "new": six.text_type(dump_port)}} + ) changes = True # Only run the command if not using test=True and changes were detected. - if not __opts__['test'] and changes is True: - response = __salt__[esxi_cmd]('set_coredump_network_config', - dump_ip=dump_ip, - host_vnic=host_vnic, - dump_port=dump_port).get(host) - if response.get('success') is False: - msg = response.get('stderr') + if not __opts__["test"] and changes is True: + response = __salt__[esxi_cmd]( + "set_coredump_network_config", + dump_ip=dump_ip, + host_vnic=host_vnic, + dump_port=dump_port, + ).get(host) + if response.get("success") is False: + msg = response.get("stderr") if not msg: - msg = response.get('stdout') - ret['comment'] = 'Error: {0}'.format(msg) + msg = response.get("stdout") + ret["comment"] = "Error: {0}".format(msg) return ret - ret['result'] = True - if ret['changes'] == {}: - ret['comment'] = 'Core Dump configuration is already in the desired state.' + ret["result"] = True + if ret["changes"] == {}: + ret["comment"] = "Core Dump configuration is already in the desired state." return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Core dump configuration will change.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Core dump configuration will change." return ret def password_present(name, password): - ''' + """ Ensures the given password is set on the ESXi host. Passwords cannot be obtained from host, so if a password is set in this state, the ``vsphere.update_host_password`` function will always run (except when using test=True functionality) and the state's @@ -299,37 +310,39 @@ def password_present(name, password): configure-host-password: esxi.password_present: - password: 'new-bad-password' - ''' - ret = {'name': name, - 'result': True, - 'changes': {'old': 'unknown', - 'new': '********'}, - 'comment': 'Host password was updated.'} - esxi_cmd = 'esxi.cmd' + """ + ret = { + "name": name, + "result": True, + "changes": {"old": "unknown", "new": "********"}, + "comment": "Host password was updated.", + } + esxi_cmd = "esxi.cmd" - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Host password will change.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Host password will change." return ret else: try: - __salt__[esxi_cmd]('update_host_password', - new_password=password) + __salt__[esxi_cmd]("update_host_password", new_password=password) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = 'Error: {0}'.format(err) + ret["result"] = False + ret["comment"] = "Error: {0}".format(err) return ret return ret -def ntp_configured(name, - service_running, - ntp_servers=None, - service_policy=None, - service_restart=False, - update_datetime=False): - ''' +def ntp_configured( + name, + service_running, + ntp_servers=None, + service_policy=None, + service_restart=False, + update_datetime=False, +): + """ Ensures a host's NTP server configuration such as setting NTP servers, ensuring the NTP daemon is running or stopped, or restarting the NTP daemon for the ESXi host. @@ -377,128 +390,134 @@ def ntp_configured(name, - service_policy: 'on' - service_restart: True - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} - esxi_cmd = 'esxi.cmd' - host = __pillar__['proxy']['host'] - ntpd = 'ntpd' + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} + esxi_cmd = "esxi.cmd" + host = __pillar__["proxy"]["host"] + ntpd = "ntpd" - ntp_config = __salt__[esxi_cmd]('get_ntp_config').get(host) - ntp_running = __salt__[esxi_cmd]('get_service_running', - service_name=ntpd).get(host) - error = ntp_running.get('Error') + ntp_config = __salt__[esxi_cmd]("get_ntp_config").get(host) + 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: {0}".format(error) return ret ntp_running = ntp_running.get(ntpd) # Configure NTP Servers for the Host if ntp_servers and set(ntp_servers) != set(ntp_config): # Only run the command if not using test=True - if not __opts__['test']: - response = __salt__[esxi_cmd]('set_ntp_config', - ntp_servers=ntp_servers).get(host) - error = response.get('Error') + if not __opts__["test"]: + response = __salt__[esxi_cmd]( + "set_ntp_config", ntp_servers=ntp_servers + ).get(host) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret # Set changes dictionary for ntp_servers - ret['changes'].update({'ntp_servers': - {'old': ntp_config, - 'new': ntp_servers}}) + ret["changes"].update({"ntp_servers": {"old": ntp_config, "new": ntp_servers}}) # Configure service_running state if service_running != ntp_running: # Only run the command if not using test=True - if not __opts__['test']: + if not __opts__["test"]: # Start ntdp if service_running=True if ntp_running is True: - response = __salt__[esxi_cmd]('service_start', - service_name=ntpd).get(host) - error = response.get('Error') + response = __salt__[esxi_cmd]("service_start", service_name=ntpd).get( + host + ) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret # Stop ntpd if service_running=False else: - response = __salt__[esxi_cmd]('service_stop', - service_name=ntpd).get(host) - error = response.get('Error') + response = __salt__[esxi_cmd]("service_stop", service_name=ntpd).get( + host + ) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - ret['changes'].update({'service_running': - {'old': ntp_running, - 'new': service_running}}) + ret["changes"].update( + {"service_running": {"old": ntp_running, "new": service_running}} + ) # Configure service_policy if service_policy: - current_service_policy = __salt__[esxi_cmd]('get_service_policy', - service_name=ntpd).get(host) - error = current_service_policy.get('Error') + current_service_policy = __salt__[esxi_cmd]( + "get_service_policy", service_name=ntpd + ).get(host) + error = current_service_policy.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret current_service_policy = current_service_policy.get(ntpd) if service_policy != current_service_policy: # Only run the command if not using test=True - if not __opts__['test']: - response = __salt__[esxi_cmd]('set_service_policy', - service_name=ntpd, - service_policy=service_policy).get(host) - error = response.get('Error') + if not __opts__["test"]: + response = __salt__[esxi_cmd]( + "set_service_policy", + service_name=ntpd, + service_policy=service_policy, + ).get(host) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - ret['changes'].update({'service_policy': - {'old': current_service_policy, - 'new': service_policy}}) + ret["changes"].update( + { + "service_policy": { + "old": current_service_policy, + "new": service_policy, + } + } + ) # Update datetime, if requested. if update_datetime: # Only run the command if not using test=True - if not __opts__['test']: - response = __salt__[esxi_cmd]('update_host_datetime').get(host) - error = response.get('Error') + if not __opts__["test"]: + response = __salt__[esxi_cmd]("update_host_datetime").get(host) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - ret['changes'].update({'update_datetime': - {'old': '', - 'new': 'Host datetime was updated.'}}) + ret["changes"].update( + {"update_datetime": {"old": "", "new": "Host datetime was updated."}} + ) # Restart ntp_service if service_restart=True if service_restart: # Only run the command if not using test=True - if not __opts__['test']: - response = __salt__[esxi_cmd]('service_restart', - service_name=ntpd).get(host) - error = response.get('Error') + if not __opts__["test"]: + response = __salt__[esxi_cmd]("service_restart", service_name=ntpd).get( + host + ) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - ret['changes'].update({'service_restart': - {'old': '', - 'new': 'NTP Daemon Restarted.'}}) + ret["changes"].update( + {"service_restart": {"old": "", "new": "NTP Daemon Restarted."}} + ) - ret['result'] = True - if ret['changes'] == {}: - ret['comment'] = 'NTP is already in the desired state.' + ret["result"] = True + if ret["changes"] == {}: + ret["comment"] = "NTP is already in the desired state." return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'NTP state will change.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "NTP state will change." return ret -def vmotion_configured(name, enabled, device='vmk0'): - ''' +def vmotion_configured(name, enabled, device="vmk0"): + """ Configures a host's VMotion properties such as enabling VMotion and setting the device VirtualNic that VMotion will use. @@ -523,54 +542,50 @@ def vmotion_configured(name, enabled, device='vmk0'): - enabled: True - device: sample-device - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} - esxi_cmd = 'esxi.cmd' - host = __pillar__['proxy']['host'] + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} + esxi_cmd = "esxi.cmd" + host = __pillar__["proxy"]["host"] - current_vmotion_enabled = __salt__[esxi_cmd]('get_vmotion_enabled').get(host) - current_vmotion_enabled = current_vmotion_enabled.get('VMotion Enabled') + current_vmotion_enabled = __salt__[esxi_cmd]("get_vmotion_enabled").get(host) + current_vmotion_enabled = current_vmotion_enabled.get("VMotion Enabled") # Configure VMotion Enabled state, if changed. if enabled != current_vmotion_enabled: # Only run the command if not using test=True - if not __opts__['test']: + if not __opts__["test"]: # Enable VMotion if enabled=True if enabled is True: - response = __salt__[esxi_cmd]('vmotion_enable', - device=device).get(host) - error = response.get('Error') + 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: {0}".format(error) return ret # Disable VMotion if enabled=False else: - response = __salt__[esxi_cmd]('vmotion_disable').get(host) - error = response.get('Error') + response = __salt__[esxi_cmd]("vmotion_disable").get(host) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - ret['changes'].update({'enabled': - {'old': current_vmotion_enabled, - 'new': enabled}}) + ret["changes"].update( + {"enabled": {"old": current_vmotion_enabled, "new": enabled}} + ) - ret['result'] = True - if ret['changes'] == {}: - ret['comment'] = 'VMotion configuration is already in the desired state.' + ret["result"] = True + if ret["changes"] == {}: + ret["comment"] = "VMotion configuration is already in the desired state." return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'VMotion configuration will change.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "VMotion configuration will change." return ret def vsan_configured(name, enabled, add_disks_to_vsan=False): - ''' + """ Configures a host's VSAN properties such as enabling or disabling VSAN, or adding VSAN-eligible disks to the VSAN system for the host. @@ -595,85 +610,82 @@ def vsan_configured(name, enabled, add_disks_to_vsan=False): - enabled: True - add_disks_to_vsan: True - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} - esxi_cmd = 'esxi.cmd' - host = __pillar__['proxy']['host'] + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} + esxi_cmd = "esxi.cmd" + host = __pillar__["proxy"]["host"] - current_vsan_enabled = __salt__[esxi_cmd]('get_vsan_enabled').get(host) - error = current_vsan_enabled.get('Error') + 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: {0}".format(error) return ret - current_vsan_enabled = current_vsan_enabled.get('VSAN Enabled') + current_vsan_enabled = current_vsan_enabled.get("VSAN Enabled") # Configure VSAN Enabled state, if changed. if enabled != current_vsan_enabled: # Only run the command if not using test=True - if not __opts__['test']: + if not __opts__["test"]: # Enable VSAN if enabled=True if enabled is True: - response = __salt__[esxi_cmd]('vsan_enable').get(host) - error = response.get('Error') + response = __salt__[esxi_cmd]("vsan_enable").get(host) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret # Disable VSAN if enabled=False else: - response = __salt__[esxi_cmd]('vsan_disable').get(host) - error = response.get('Error') + response = __salt__[esxi_cmd]("vsan_disable").get(host) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - ret['changes'].update({'enabled': - {'old': current_vsan_enabled, - 'new': enabled}}) + ret["changes"].update( + {"enabled": {"old": current_vsan_enabled, "new": enabled}} + ) # Add any eligible disks to VSAN, if requested. if add_disks_to_vsan: - current_eligible_disks = __salt__[esxi_cmd]('get_vsan_eligible_disks').get(host) - error = current_eligible_disks.get('Error') + 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: {0}".format(error) return ret - disks = current_eligible_disks.get('Eligible') + disks = current_eligible_disks.get("Eligible") if disks and isinstance(disks, list): # Only run the command if not using test=True - if not __opts__['test']: - response = __salt__[esxi_cmd]('vsan_add_disks').get(host) - error = response.get('Error') + if not __opts__["test"]: + response = __salt__[esxi_cmd]("vsan_add_disks").get(host) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - ret['changes'].update({'add_disks_to_vsan': - {'old': '', - 'new': disks}}) + ret["changes"].update({"add_disks_to_vsan": {"old": "", "new": disks}}) - ret['result'] = True - if ret['changes'] == {}: - ret['comment'] = 'VSAN configuration is already in the desired state.' + ret["result"] = True + if ret["changes"] == {}: + ret["comment"] = "VSAN configuration is already in the desired state." return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'VSAN configuration will change.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "VSAN configuration will change." return ret -def ssh_configured(name, - service_running, - ssh_key=None, - ssh_key_file=None, - service_policy=None, - service_restart=False, - certificate_verify=False): - ''' +def ssh_configured( + name, + service_running, + ssh_key=None, + ssh_key_file=None, + service_policy=None, + service_restart=False, + certificate_verify=False, +): + """ Manage the SSH configuration for a host including whether or not SSH is running or the presence of a given SSH key. Note: Only one ssh key can be uploaded for root. Uploading a second key will replace any existing key. @@ -726,74 +738,72 @@ def ssh_configured(name, - service_restart: True - certificate_verify: True - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} - esxi_cmd = 'esxi.cmd' - host = __pillar__['proxy']['host'] - ssh = 'ssh' + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} + esxi_cmd = "esxi.cmd" + host = __pillar__["proxy"]["host"] + ssh = "ssh" - ssh_running = __salt__[esxi_cmd]('get_service_running', - service_name=ssh).get(host) - error = ssh_running.get('Error') + 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: {0}".format(error) return ret ssh_running = ssh_running.get(ssh) # Configure SSH service_running state, if changed. if service_running != ssh_running: # Only actually run the command if not using test=True - if not __opts__['test']: + if not __opts__["test"]: # Start SSH if service_running=True if service_running is True: - enable = __salt__[esxi_cmd]('service_start', - service_name=ssh).get(host) - error = enable.get('Error') + 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: {0}".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') + 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: {0}".format(error) return ret - ret['changes'].update({'service_running': - {'old': ssh_running, - 'new': service_running}}) + ret["changes"].update( + {"service_running": {"old": ssh_running, "new": service_running}} + ) # If uploading an SSH key or SSH key file, see if there's a current # SSH key and compare the current key to the key set in the state. current_ssh_key, ssh_key_changed = None, False if ssh_key or ssh_key_file: - current_ssh_key = __salt__[esxi_cmd]('get_ssh_key', - certificate_verify=certificate_verify) - error = current_ssh_key.get('Error') + current_ssh_key = __salt__[esxi_cmd]( + "get_ssh_key", certificate_verify=certificate_verify + ) + error = current_ssh_key.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - current_ssh_key = current_ssh_key.get('key') + current_ssh_key = current_ssh_key.get("key") if current_ssh_key: - clean_current_key = _strip_key(current_ssh_key).split(' ') + clean_current_key = _strip_key(current_ssh_key).split(" ") if not ssh_key: - ssh_key = '' + ssh_key = "" # Open ssh key file and read in contents to create one key string - with salt.utils.files.fopen(ssh_key_file, 'r') as key_file: + with salt.utils.files.fopen(ssh_key_file, "r") as key_file: for line in key_file: - if line.startswith('#'): + if line.startswith("#"): # Commented line continue ssh_key = ssh_key + line - clean_ssh_key = _strip_key(ssh_key).split(' ') + clean_ssh_key = _strip_key(ssh_key).split(" ") # Check that the first two list items of clean key lists are equal. - if clean_current_key[0] != clean_ssh_key[0] or clean_current_key[1] != clean_ssh_key[1]: + if ( + clean_current_key[0] != clean_ssh_key[0] + or clean_current_key[1] != clean_ssh_key[1] + ): ssh_key_changed = True else: # If current_ssh_key is None, but we're setting a new key with @@ -802,77 +812,93 @@ def ssh_configured(name, # Upload SSH key, if changed. if ssh_key_changed: - if not __opts__['test']: + if not __opts__["test"]: # Upload key - response = __salt__[esxi_cmd]('upload_ssh_key', - ssh_key=ssh_key, - ssh_key_file=ssh_key_file, - certificate_verify=certificate_verify) - error = response.get('Error') + response = __salt__[esxi_cmd]( + "upload_ssh_key", + ssh_key=ssh_key, + ssh_key_file=ssh_key_file, + certificate_verify=certificate_verify, + ) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - ret['changes'].update({'SSH Key': - {'old': current_ssh_key, - 'new': ssh_key if ssh_key else ssh_key_file}}) + ret["changes"].update( + { + "SSH Key": { + "old": current_ssh_key, + "new": ssh_key if ssh_key else ssh_key_file, + } + } + ) # Configure service_policy if service_policy: - current_service_policy = __salt__[esxi_cmd]('get_service_policy', - service_name=ssh).get(host) - error = current_service_policy.get('Error') + current_service_policy = __salt__[esxi_cmd]( + "get_service_policy", service_name=ssh + ).get(host) + error = current_service_policy.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret current_service_policy = current_service_policy.get(ssh) if service_policy != current_service_policy: # Only run the command if not using test=True - if not __opts__['test']: - response = __salt__[esxi_cmd]('set_service_policy', - service_name=ssh, - service_policy=service_policy).get(host) - error = response.get('Error') + if not __opts__["test"]: + response = __salt__[esxi_cmd]( + "set_service_policy", + service_name=ssh, + service_policy=service_policy, + ).get(host) + error = response.get("Error") if error: - ret['comment'] = 'Error: {0}'.format(error) + ret["comment"] = "Error: {0}".format(error) return ret - ret['changes'].update({'service_policy': - {'old': current_service_policy, - 'new': service_policy}}) + ret["changes"].update( + { + "service_policy": { + "old": current_service_policy, + "new": service_policy, + } + } + ) # Restart ssh_service if service_restart=True if service_restart: # Only run the command if not using test=True - if not __opts__['test']: - response = __salt__[esxi_cmd]('service_restart', - service_name=ssh).get(host) - error = response.get('Error') + if not __opts__["test"]: + 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: {0}".format(error) return ret - ret['changes'].update({'service_restart': - {'old': '', - 'new': 'SSH service restarted.'}}) + ret["changes"].update( + {"service_restart": {"old": "", "new": "SSH service restarted."}} + ) - ret['result'] = True - if ret['changes'] == {}: - ret['comment'] = 'SSH service is already in the desired state.' + ret["result"] = True + if ret["changes"] == {}: + ret["comment"] = "SSH service is already in the desired state." return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'SSH service state will change.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "SSH service state will change." return ret -def syslog_configured(name, - syslog_configs, - firewall=True, - reset_service=True, - reset_syslog_config=False, - reset_configs=None): - ''' +def syslog_configured( + name, + syslog_configs, + firewall=True, + reset_service=True, + reset_syslog_config=False, + reset_configs=None, +): + """ Ensures the specified syslog configuration parameters. By default, this state will reset the syslog service after any new or changed parameters are set successfully. @@ -926,101 +952,105 @@ def syslog_configured(name, - reset_service: True - reset_syslog_config: True - reset_configs: loghost,default-timeout - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} - esxi_cmd = 'esxi.cmd' - host = __pillar__['proxy']['host'] + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} + esxi_cmd = "esxi.cmd" + host = __pillar__["proxy"]["host"] if reset_syslog_config: if not reset_configs: - reset_configs = 'all' + reset_configs = "all" # Only run the command if not using test=True - if not __opts__['test']: - reset = __salt__[esxi_cmd]('reset_syslog_config', - syslog_config=reset_configs).get(host) + if not __opts__["test"]: + reset = __salt__[esxi_cmd]( + "reset_syslog_config", syslog_config=reset_configs + ).get(host) for key, val in six.iteritems(reset): if isinstance(val, bool): continue - if not val.get('success'): - msg = val.get('message') + if not val.get("success"): + msg = val.get("message") if not msg: - msg = 'There was an error resetting a syslog config \'{0}\'.' \ - 'Please check debug logs.'.format(val) - ret['comment'] = 'Error: {0}'.format(msg) + msg = ( + "There was an error resetting a syslog config '{0}'." + "Please check debug logs.".format(val) + ) + ret["comment"] = "Error: {0}".format(msg) return ret - ret['changes'].update({'reset_syslog_config': - {'old': '', - 'new': reset_configs}}) + ret["changes"].update( + {"reset_syslog_config": {"old": "", "new": reset_configs}} + ) - current_firewall = __salt__[esxi_cmd]('get_firewall_status').get(host) - error = current_firewall.get('Error') + 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: {0}".format(error) return ret - current_firewall = current_firewall.get('rulesets').get('syslog') + current_firewall = current_firewall.get("rulesets").get("syslog") if current_firewall != firewall: # Only run the command if not using test=True - if not __opts__['test']: - enabled = __salt__[esxi_cmd]('enable_firewall_ruleset', - ruleset_enable=firewall, - ruleset_name='syslog').get(host) - if enabled.get('retcode') != 0: - err = enabled.get('stderr') - out = enabled.get('stdout') - ret['comment'] = 'Error: {0}'.format(err if err else out) + if not __opts__["test"]: + enabled = __salt__[esxi_cmd]( + "enable_firewall_ruleset", + ruleset_enable=firewall, + ruleset_name="syslog", + ).get(host) + if enabled.get("retcode") != 0: + err = enabled.get("stderr") + out = enabled.get("stdout") + ret["comment"] = "Error: {0}".format(err if err else out) return ret - ret['changes'].update({'firewall': - {'old': current_firewall, - 'new': firewall}}) + ret["changes"].update({"firewall": {"old": current_firewall, "new": firewall}}) - current_syslog_config = __salt__[esxi_cmd]('get_syslog_config').get(host) + current_syslog_config = __salt__[esxi_cmd]("get_syslog_config").get(host) for key, val in six.iteritems(syslog_configs): # 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"] = "'{0}' 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): # Only run the command if not using test=True - if not __opts__['test']: - response = __salt__[esxi_cmd]('set_syslog_config', - syslog_config=key, - config_value=val, - firewall=firewall, - reset_service=reset_service).get(host) - success = response.get(key).get('success') + if not __opts__["test"]: + response = __salt__[esxi_cmd]( + "set_syslog_config", + syslog_config=key, + config_value=val, + firewall=firewall, + reset_service=reset_service, + ).get(host) + success = response.get(key).get("success") if not success: - msg = response.get(key).get('message') + msg = response.get(key).get("message") if not msg: - msg = 'There was an error setting syslog config \'{0}\'. ' \ - 'Please check debug logs.'.format(key) - ret['comment'] = msg + msg = ( + "There was an error setting syslog config '{0}'. " + "Please check debug logs.".format(key) + ) + ret["comment"] = msg return ret - if not ret['changes'].get('syslog_config'): - ret['changes'].update({'syslog_config': {}}) - ret['changes']['syslog_config'].update({key: - {'old': current_val, - 'new': val}}) + if not ret["changes"].get("syslog_config"): + ret["changes"].update({"syslog_config": {}}) + ret["changes"]["syslog_config"].update( + {key: {"old": current_val, "new": val}} + ) - ret['result'] = True - if ret['changes'] == {}: - ret['comment'] = 'Syslog is already in the desired state.' + ret["result"] = True + if ret["changes"] == {}: + ret["comment"] = "Syslog is already in the desired state." return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Syslog state will change.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Syslog state will change." return ret @@ -1028,7 +1058,7 @@ def syslog_configured(name, @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) def diskgroups_configured(name, diskgroups, erase_disks=False): - ''' + """ Configures the disk groups to use for vsan. This function will do the following: @@ -1064,16 +1094,16 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): erase_disks Specifies whether to erase all partitions on all disks member of the disk group before the disk group is created. Default value is False. - ''' - proxy_details = __salt__['esxi.get_details']() - hostname = proxy_details['host'] if not proxy_details.get('vcenter') \ - else proxy_details['esxi_host'] - log.info('Running state {0} for host \'{1}\''.format(name, hostname)) + """ + proxy_details = __salt__["esxi.get_details"]() + hostname = ( + proxy_details["host"] + if not proxy_details.get("vcenter") + else proxy_details["esxi_host"] + ) + log.info("Running state {0} for host '{1}'".format(name, hostname)) # Variable used to return the result of the invocation - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comments': None} + ret = {"name": name, "result": None, "changes": {}, "comments": None} # Signals if errors have been encountered errors = False # Signals if changes are required @@ -1082,165 +1112,209 @@ def diskgroups_configured(name, diskgroups, erase_disks=False): diskgroup_changes = {} si = None try: - log.trace('Validating diskgroups_configured input') + log.trace("Validating diskgroups_configured input") schema = DiskGroupsDiskScsiAddressSchema.serialize() try: - jsonschema.validate({'diskgroups': diskgroups, - 'erase_disks': erase_disks}, schema) + jsonschema.validate( + {"diskgroups": diskgroups, "erase_disks": erase_disks}, schema + ) except jsonschema.exceptions.ValidationError as exc: raise InvalidConfigError(exc) - si = __salt__['vsphere.get_service_instance_via_proxy']() - host_disks = __salt__['vsphere.list_disks'](service_instance=si) + si = __salt__["vsphere.get_service_instance_via_proxy"]() + host_disks = __salt__["vsphere.list_disks"](service_instance=si) if not host_disks: raise VMwareObjectRetrievalError( - 'No disks retrieved from host \'{0}\''.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)) - existing_diskgroups = \ - __salt__['vsphere.list_diskgroups'](service_instance=si) - cache_disk_to_existing_diskgroup_map = \ - {dg['cache_disk']: dg for dg in existing_diskgroups} + "No disks retrieved from host '{0}'".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)) + 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: {0}".format(err)) if si: - __salt__['vsphere.disconnect'](si) - ret.update({ - 'result': False if not __opts__['test'] else None, - 'comment': six.text_type(err)}) + __salt__["vsphere.disconnect"](si) + ret.update( + { + "result": False if not __opts__["test"] else None, + "comment": six.text_type(err), + } + ) return ret # Iterate through all of the disk groups for idx, dg in enumerate(diskgroups): # 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'])) + 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"]) + ) log.error(comments[-1]) errors = True continue # 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_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) bad_scsi_addrs = [] capacity_disk_ids = [] capacity_disk_displays = [] - for scsi_addr in dg['capacity_scsi_addrs']: + for scsi_addr in dg["capacity_scsi_addrs"]: if scsi_addr not in scsi_addr_to_disk_map: bad_scsi_addrs.append(scsi_addr) continue - capacity_disk_ids.append(scsi_addr_to_disk_map[scsi_addr]['id']) + 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])) + "{0} (id:{1})".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]))) + 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])) + ) log.error(comments[-1]) errors = True continue 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 = {0}".format(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}.' - ''.format(name, idx, cache_disk_display, - ', '.join( - ['\'{}\''.format(a) for a in - capacity_disk_displays]))) + if __opts__["test"]: + comments.append( + "State {0} will " + "erase all disks of disk group #{1}; " + "cache disk: '{2}', " + "capacity disk(s): {3}." + "".format( + name, + idx, + cache_disk_display, + ", ".join( + ["'{}'".format(a) for a in capacity_disk_displays] + ), + ) + ) else: # Erase disk group disks for disk_id in [cache_disk_id] + capacity_disk_ids: - __salt__['vsphere.erase_disk_partitions']( - disk_id=disk_id, service_instance=si) - comments.append('Erased disks of diskgroup #{0}; ' - 'cache disk: \'{1}\', capacity disk(s): ' - '{2}'.format( - idx, cache_disk_display, - ', '.join(['\'{0}\''.format(a) for a in - capacity_disk_displays]))) + __salt__["vsphere.erase_disk_partitions"]( + disk_id=disk_id, service_instance=si + ) + comments.append( + "Erased disks of diskgroup #{0}; " + "cache disk: '{1}', capacity disk(s): " + "{2}".format( + idx, + cache_disk_display, + ", ".join( + ["'{0}'".format(a) for a in capacity_disk_displays] + ), + ) + ) log.info(comments[-1]) - if __opts__['test']: - comments.append('State {0} will create ' - 'the disk group #{1}; cache disk: \'{2}\', ' - 'capacity disk(s): {3}.' - .format(name, idx, cache_disk_display, - ', '.join(['\'{0}\''.format(a) for a in - capacity_disk_displays]))) + if __opts__["test"]: + comments.append( + "State {0} will create " + "the disk group #{1}; cache disk: '{2}', " + "capacity disk(s): {3}.".format( + name, + idx, + cache_disk_display, + ", ".join(["'{0}'".format(a) for a in capacity_disk_displays]), + ) + ) log.info(comments[-1]) changes = True continue try: - __salt__['vsphere.create_diskgroup'](cache_disk_id, - capacity_disk_ids, - safety_checks=False, - service_instance=si) + __salt__["vsphere.create_diskgroup"]( + cache_disk_id, + capacity_disk_ids, + safety_checks=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 #{0}: " "{1}.".format(idx, err) + ) log.error(comments[-1]) errors = True continue - comments.append('Created disk group #\'{0}\'.'.format(idx)) + comments.append("Created disk group #'{0}'.".format(idx)) log.info(comments[-1]) - diskgroup_changes[six.text_type(idx)] = \ - {'new': {'cache': cache_disk_display, - 'capacity': capacity_disk_displays}} + diskgroup_changes[six.text_type(idx)] = { + "new": {"cache": cache_disk_display, "capacity": capacity_disk_displays} + } changes = True continue # The diskgroup exists; checking the capacity disks - log.debug('Disk group #{0} exists. Checking capacity disks: ' - '{1}.'.format(idx, capacity_disk_displays)) - existing_diskgroup = \ - cache_disk_to_existing_diskgroup_map.get(cache_disk_id) - existing_capacity_disk_displays = \ - ['{0} (id:{1})'.format([d['scsi_address'] for d in host_disks - if d['id'] == disk_id][0], disk_id) - for disk_id in existing_diskgroup['capacity_disks']] + log.debug( + "Disk group #{0} exists. Checking capacity disks: " + "{1}.".format(idx, capacity_disk_displays) + ) + existing_diskgroup = cache_disk_to_existing_diskgroup_map.get(cache_disk_id) + existing_capacity_disk_displays = [ + "{0} (id:{1})".format( + [d["scsi_address"] for d in host_disks if d["id"] == disk_id][0], + disk_id, + ) + for disk_id in existing_diskgroup["capacity_disks"] + ] # Populate added disks and removed disks and their displays added_capacity_disk_ids = [] added_capacity_disk_displays = [] removed_capacity_disk_ids = [] removed_capacity_disk_displays = [] for disk_id in capacity_disk_ids: - if disk_id not in existing_diskgroup['capacity_disks']: - disk_scsi_addr = [d['scsi_address'] for d in host_disks - if d['id'] == disk_id][0] + if disk_id not in existing_diskgroup["capacity_disks"]: + disk_scsi_addr = [ + d["scsi_address"] for d in host_disks if d["id"] == disk_id + ][0] added_capacity_disk_ids.append(disk_id) added_capacity_disk_displays.append( - '{0} (id:{1})'.format(disk_scsi_addr, disk_id)) - for disk_id in existing_diskgroup['capacity_disks']: + "{0} (id:{1})".format(disk_scsi_addr, disk_id) + ) + for disk_id in existing_diskgroup["capacity_disks"]: if disk_id not in capacity_disk_ids: - disk_scsi_addr = [d['scsi_address'] for d in host_disks - if d['id'] == disk_id][0] + disk_scsi_addr = [ + d["scsi_address"] for d in host_disks if d["id"] == disk_id + ][0] removed_capacity_disk_ids.append(disk_id) removed_capacity_disk_displays.append( - '{0} (id:{1})'.format(disk_scsi_addr, disk_id)) + "{0} (id:{1})".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)) + 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, + ) + ) - #TODO revisit this when removing capacity disks is supported + # 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}; ' - 'operation is not supported.' - ''.format(', '.join(['\'{0}\''.format(id) for id in - removed_capacity_disk_displays]), idx)) + "Error removing capacity disk(s) {0} from disk group #{1}; " + "operation is not supported." + "".format( + ", ".join( + ["'{0}'".format(id) for id in removed_capacity_disk_displays] + ), + idx, + ) + ) log.error(comments[-1]) errors = True continue @@ -1250,63 +1324,83 @@ 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]) - if __opts__['test']: - comments.append('State {0} will add ' - 'capacity disk(s) {1} to disk group #{2}.' - ''.format(name, s, idx)) + s = ", ".join(["'{0}'".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}." + "".format(name, s, idx) + ) log.info(comments[-1]) changes = True continue try: - __salt__['vsphere.add_capacity_to_diskgroup']( + __salt__["vsphere.add_capacity_to_diskgroup"]( cache_disk_id, added_capacity_disk_ids, safety_checks=False, - service_instance=si) + service_instance=si, + ) except VMwareSaltError as err: - comments.append('Error adding capacity disk(s) {0} to ' - 'disk group #{1}: {2}.'.format(s, idx, err)) + comments.append( + "Error adding capacity disk(s) {0} to " + "disk group #{1}: {2}.".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) {0} to disk group #{1}" "".format(s, idx) log.info(com) comments.append(com) - diskgroup_changes[six.text_type(idx)] = \ - {'new': {'cache': cache_disk_display, - 'capacity': capacity_disk_displays}, - 'old': {'cache': cache_disk_display, - 'capacity': existing_capacity_disk_displays}} + diskgroup_changes[six.text_type(idx)] = { + "new": { + "cache": cache_disk_display, + "capacity": capacity_disk_displays, + }, + "old": { + "cache": cache_disk_display, + "capacity": existing_capacity_disk_displays, + }, + } changes = True continue # No capacity needs to be added - s = ('Disk group #{0} is correctly configured. Nothing to be done.' - ''.format(idx)) + s = "Disk group #{0} is correctly configured. Nothing to be done." "".format( + idx + ) log.info(s) comments.append(s) - __salt__['vsphere.disconnect'](si) + __salt__["vsphere.disconnect"](si) - #Build the final return message - result = (True if not (changes or errors) else # no changes/errors - None if __opts__['test'] else # running in test mode - False if errors else True) # found errors; defaults to True - ret.update({'result': result, - 'comment': '\n'.join(comments), - 'changes': diskgroup_changes}) + # Build the final return message + result = ( + True + if not (changes or errors) + else None # no changes/errors + if __opts__["test"] + else False # running in test mode + if errors + else True + ) # found errors; defaults to True + ret.update( + {"result": result, "comment": "\n".join(comments), "changes": diskgroup_changes} + ) return ret @depends(HAS_PYVMOMI) @depends(HAS_JSONSCHEMA) -def host_cache_configured(name, enabled, datastore, swap_size='100%', - dedicated_backing_disk=False, - erase_backing_disk=False): - ''' +def host_cache_configured( + name, + enabled, + datastore, + swap_size="100%", + dedicated_backing_disk=False, + erase_backing_disk=False, +): + """ Configures the host cache used for swapping. It will do the following: @@ -1374,150 +1468,181 @@ def host_cache_configured(name, enabled, datastore, swap_size='100%', erase_backing_disk Specifies whether to erase all partitions on the backing disk before the datastore is created. Default value is False. - ''' - log.trace('enabled = %s', enabled) - log.trace('datastore = %s', datastore) - log.trace('swap_size = %s', swap_size) - log.trace('erase_backing_disk = %s', erase_backing_disk) + """ + log.trace("enabled = %s", enabled) + log.trace("datastore = %s", datastore) + log.trace("swap_size = %s", swap_size) + log.trace("erase_backing_disk = %s", erase_backing_disk) # Variable used to return the result of the invocation - proxy_details = __salt__['esxi.get_details']() - hostname = proxy_details['host'] if not proxy_details.get('vcenter') \ - else proxy_details['esxi_host'] - log.trace('hostname = %s', hostname) - log.info('Running host_cache_swap_configured for host \'%s\'', hostname) - ret = {'name': hostname, - 'comment': 'Default comments', - 'result': None, - 'changes': {}} - result = None if __opts__['test'] else True # We assume success + proxy_details = __salt__["esxi.get_details"]() + hostname = ( + proxy_details["host"] + if not proxy_details.get("vcenter") + else proxy_details["esxi_host"] + ) + log.trace("hostname = %s", hostname) + log.info("Running host_cache_swap_configured for host '%s'", hostname) + ret = { + "name": hostname, + "comment": "Default comments", + "result": None, + "changes": {}, + } + result = None if __opts__["test"] else True # We assume success needs_setting = False comments = [] changes = {} si = None try: - log.debug('Validating host_cache_configured input') + log.debug("Validating host_cache_configured input") schema = HostCacheSchema.serialize() try: - jsonschema.validate({'enabled': enabled, - 'datastore': datastore, - 'swap_size': swap_size, - 'erase_backing_disk': erase_backing_disk}, - schema) + jsonschema.validate( + { + "enabled": enabled, + "datastore": datastore, + "swap_size": swap_size, + "erase_backing_disk": erase_backing_disk, + }, + schema, + ) except jsonschema.exceptions.ValidationError as exc: raise InvalidConfigError(exc) - m = re.match(r'(\d+)(%|GiB)', swap_size) + m = re.match(r"(\d+)(%|GiB)", swap_size) swap_size_value = int(m.group(1)) swap_type = m.group(2) - log.trace('swap_size_value = %s; swap_type = %s', swap_size_value, swap_type) - si = __salt__['vsphere.get_service_instance_via_proxy']() - host_cache = __salt__['vsphere.get_host_cache'](service_instance=si) + log.trace("swap_size_value = %s; swap_type = %s", swap_size_value, swap_type) + si = __salt__["vsphere.get_service_instance_via_proxy"]() + host_cache = __salt__["vsphere.get_host_cache"](service_instance=si) # Check enabled - if host_cache['enabled'] != enabled: - changes.update({'enabled': {'old': host_cache['enabled'], - 'new': enabled}}) + if host_cache["enabled"] != enabled: + changes.update({"enabled": {"old": host_cache["enabled"], "new": enabled}}) needs_setting = True # Check datastores existing_datastores = None - if host_cache.get('datastore'): - existing_datastores = \ - __salt__['vsphere.list_datastores_via_proxy']( - datastore_names=[datastore['name']], - service_instance=si) + if host_cache.get("datastore"): + existing_datastores = __salt__["vsphere.list_datastores_via_proxy"]( + datastore_names=[datastore["name"]], service_instance=si + ) # Retrieve backing disks - existing_disks = __salt__['vsphere.list_disks']( - scsi_addresses=[datastore['backing_disk_scsi_addr']], - service_instance=si) + existing_disks = __salt__["vsphere.list_disks"]( + scsi_addresses=[datastore["backing_disk_scsi_addr"]], service_instance=si + ) if not existing_disks: raise VMwareObjectRetrievalError( - 'Disk with scsi address \'{0}\' was not found in host \'{1}\'' - ''.format(datastore['backing_disk_scsi_addr'], hostname)) + "Disk with scsi address '{0}' was not found in host '{1}'" + "".format(datastore["backing_disk_scsi_addr"], hostname) + ) backing_disk = existing_disks[0] - backing_disk_display = '{0} (id:{1})'.format( - backing_disk['scsi_address'], backing_disk['id']) - log.trace('backing_disk = %s', backing_disk_display) + backing_disk_display = "{0} (id:{1})".format( + backing_disk["scsi_address"], backing_disk["id"] + ) + log.trace("backing_disk = %s", backing_disk_display) existing_datastore = None if not existing_datastores: # Check if disk needs to be erased 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)) + if __opts__["test"]: + comments.append( + "State {0} will erase " + "the backing disk '{1}' on host '{2}'." + "".format(name, backing_disk_display, hostname) + ) log.info(comments[-1]) else: # Erase disk - __salt__['vsphere.erase_disk_partitions']( - disk_id=backing_disk['id'], service_instance=si) - comments.append('Erased backing disk \'{0}\' on host ' - '\'{1}\'.'.format(backing_disk_display, - hostname)) + __salt__["vsphere.erase_disk_partitions"]( + disk_id=backing_disk["id"], service_instance=si + ) + comments.append( + "Erased backing disk '{0}' on host " + "'{1}'.".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)) + 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) + ) log.info(comments[-1]) else: if dedicated_backing_disk: # Check backing disk doesn't already have partitions - partitions = __salt__['vsphere.list_disk_partitions']( - disk_id=backing_disk['id'], service_instance=si) - log.trace('partitions = %s', partitions) + partitions = __salt__["vsphere.list_disk_partitions"]( + disk_id=backing_disk["id"], service_instance=si + ) + log.trace("partitions = %s", partitions) # We will ignore the mbr partitions - non_mbr_partitions = [p for p in partitions - if p['format'] != 'mbr'] + 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)) - __salt__['vsphere.create_vmfs_datastore']( - datastore['name'], existing_disks[0]['id'], - datastore['vmfs_version'], service_instance=si) - comments.append('Created vmfs datastore \'{0}\', backed by ' - 'disk \'{1}\', on host \'{2}\'.' - ''.format(datastore['name'], - backing_disk_display, hostname)) + "Backing disk '{0}' has unexpected partitions" + "".format(backing_disk_display) + ) + __salt__["vsphere.create_vmfs_datastore"]( + datastore["name"], + existing_disks[0]["id"], + datastore["vmfs_version"], + service_instance=si, + ) + comments.append( + "Created vmfs datastore '{0}', backed by " + "disk '{1}', on host '{2}'." + "".format(datastore["name"], backing_disk_display, hostname) + ) log.info(comments[-1]) changes.update( - {'datastore': - {'new': {'name': datastore['name'], - 'backing_disk': backing_disk_display}}}) - existing_datastore = \ - __salt__['vsphere.list_datastores_via_proxy']( - datastore_names=[datastore['name']], - service_instance=si)[0] + { + "datastore": { + "new": { + "name": datastore["name"], + "backing_disk": backing_disk_display, + } + } + } + ) + existing_datastore = __salt__["vsphere.list_datastores_via_proxy"]( + datastore_names=[datastore["name"]], service_instance=si + )[0] needs_setting = True else: # 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'])) - if backing_disk['id'] not in \ - existing_datastores[0]['backing_disk_ids']: + if not existing_datastores[0].get("backing_disk_ids"): + raise VMwareSaltError( + "Datastore '{0}' 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['name'], backing_disk['id'], - ', '.join( - ['\'{0}\''.format(disk) for disk in - existing_datastores[0]['backing_disk_ids']]))) + "Datastore '{0}' is not backed by the correct disk: " + "expected '{1}'; got {2}" + "".format( + datastore["name"], + backing_disk["id"], + ", ".join( + [ + "'{0}'".format(disk) + for disk in existing_datastores[0]["backing_disk_ids"] + ] + ), + ) + ) - comments.append('Datastore \'{0}\' already exists on host \'{1}\' ' - 'and is backed by disk \'{2}\'. Nothing to be ' - 'done.'.format(datastore['name'], hostname, - backing_disk_display)) + comments.append( + "Datastore '{0}' already exists on host '{1}' " + "and is backed by disk '{2}'. Nothing to be " + "done.".format(datastore["name"], hostname, backing_disk_display) + ) existing_datastore = existing_datastores[0] - log.trace('existing_datastore = %s', existing_datastore) + log.trace("existing_datastore = %s", existing_datastore) log.info(comments[-1]) if existing_datastore: @@ -1526,19 +1651,23 @@ def host_cache_configured(name, enabled, datastore, swap_size='100%', # # We support percent, as well as MiB, we will convert the size # to MiB, multiples of 1024 (VMware SDK limitation) - if swap_type == '%': + if swap_type == "%": # Percentage swap size # Convert from bytes to MiB - raw_size_MiB = (swap_size_value/100.0) * \ - (existing_datastore['capacity']/1024/1024) + raw_size_MiB = (swap_size_value / 100.0) * ( + existing_datastore["capacity"] / 1024 / 1024 + ) else: raw_size_MiB = swap_size_value * 1024 - log.trace('raw_size = %sMiB', raw_size_MiB) - swap_size_MiB = int(raw_size_MiB/1024)*1024 - log.trace('adjusted swap_size = %sMiB', swap_size_MiB) + log.trace("raw_size = %sMiB", raw_size_MiB) + swap_size_MiB = int(raw_size_MiB / 1024) * 1024 + log.trace("adjusted swap_size = %sMiB", swap_size_MiB) existing_swap_size_MiB = 0 - m = re.match(r'(\d+)MiB', host_cache.get('swap_size')) if \ - host_cache.get('swap_size') else None + m = ( + re.match(r"(\d+)MiB", host_cache.get("swap_size")) + if host_cache.get("swap_size") + else None + ) if m: # if swap_size from the host is set and has an expected value # we are going to parse it to get the number of MiBs @@ -1546,78 +1675,100 @@ def host_cache_configured(name, enabled, datastore, swap_size='100%', if not existing_swap_size_MiB == swap_size_MiB: needs_setting = True changes.update( - {'swap_size': - {'old': '{}GiB'.format(existing_swap_size_MiB/1024), - 'new': '{}GiB'.format(swap_size_MiB/1024)}}) + { + "swap_size": { + "old": "{}GiB".format(existing_swap_size_MiB / 1024), + "new": "{}GiB".format(swap_size_MiB / 1024), + } + } + ) if needs_setting: - if __opts__['test']: - comments.append('State {0} will configure ' - 'the host cache on host \'{1}\' to: {2}.' - ''.format(name, hostname, - {'enabled': enabled, - 'datastore_name': datastore['name'], - 'swap_size': swap_size})) + if __opts__["test"]: + comments.append( + "State {0} will configure " + "the host cache on host '{1}' to: {2}." + "".format( + name, + hostname, + { + "enabled": enabled, + "datastore_name": datastore["name"], + "swap_size": swap_size, + }, + ) + ) else: - if (existing_datastore['capacity'] / 1024.0**2) < \ - swap_size_MiB: + 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(existing_datastore['name'], - existing_datastore['capacity'] / 1024.0**2, - swap_size_MiB)) - __salt__['vsphere.configure_host_cache']( + "Capacity of host cache datastore '{0}' ({1} MiB) is " + "smaller than the required swap size ({2} MiB)" + "".format( + existing_datastore["name"], + existing_datastore["capacity"] / 1024.0 ** 2, + swap_size_MiB, + ) + ) + __salt__["vsphere.configure_host_cache"]( enabled, - datastore['name'], + datastore["name"], swap_size_MiB=swap_size_MiB, - service_instance=si) - comments.append('Host cache configured on host ' - '\'{0}\'.'.format(hostname)) + service_instance=si, + ) + comments.append( + "Host cache configured on host " "'{0}'.".format(hostname) + ) else: - comments.append('Host cache on host \'{0}\' is already correctly ' - 'configured. Nothing to be done.'.format(hostname)) + comments.append( + "Host cache on host '{0}' is already correctly " + "configured. Nothing to be done.".format(hostname) + ) result = True - __salt__['vsphere.disconnect'](si) + __salt__["vsphere.disconnect"](si) log.info(comments[-1]) - ret.update({'comment': '\n'.join(comments), - 'result': result, - 'changes': changes}) + ret.update( + {"comment": "\n".join(comments), "result": result, "changes": changes} + ) return ret except CommandExecutionError as err: - log.error('Error: %s.', err) + log.error("Error: %s.", err) if si: - __salt__['vsphere.disconnect'](si) - ret.update({ - 'result': False if not __opts__['test'] else None, - 'comment': '{}.'.format(err)}) + __salt__["vsphere.disconnect"](si) + ret.update( + { + "result": False if not __opts__["test"] else None, + "comment": "{}.".format(err), + } + ) return ret def _lookup_syslog_config(config): - ''' + """ Helper function that looks up syslog_config keys available from ``vsphere.get_syslog_config``. - ''' - lookup = {'default-timeout': 'Default Network Retry Timeout', - 'logdir': 'Local Log Output', - 'default-size': 'Local Logging Default Rotation Size', - 'logdir-unique': 'Log To Unique Subdirectory', - 'default-rotate': 'Local Logging Default Rotations', - 'loghost': 'Remote Host'} + """ + lookup = { + "default-timeout": "Default Network Retry Timeout", + "logdir": "Local Log Output", + "default-size": "Local Logging Default Rotation Size", + "logdir-unique": "Log To Unique Subdirectory", + "default-rotate": "Local Logging Default Rotations", + "loghost": "Remote Host", + } return lookup.get(config) def _strip_key(key_string): - ''' + """ Strips an SSH key string of white space and line endings and returns the new string. key_string The string to be stripped. - ''' + """ key_string.strip() - key_string.replace('\n', '') - key_string.replace('\r\n', '') + key_string.replace("\n", "") + key_string.replace("\r\n", "") return key_string diff --git a/salt/states/esxvm.py b/salt/states/esxvm.py index 0a1501d318e..e4c93b04bd1 100644 --- a/salt/states/esxvm.py +++ b/salt/states/esxvm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Salt state to create, update VMware ESXi Virtual Machines. Dependencies @@ -181,12 +181,13 @@ ESXi Proxy Minion, please refer to the 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 sys + import logging +import sys # Import Salt libs import salt.exceptions @@ -196,12 +197,14 @@ from salt.config.schemas.esxvm import ESXVirtualMachineConfigSchema # External libraries try: import jsonschema + HAS_JSONSCHEMA = True except ImportError: HAS_JSONSCHEMA = False try: from pyVmomi import VmomiSupport + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False @@ -211,327 +214,427 @@ log = logging.getLogger(__name__) def __virtual__(): if not HAS_JSONSCHEMA: - return False, 'State module did not load: jsonschema not found' + return False, "State module did not load: jsonschema not found" if not HAS_PYVMOMI: - return False, 'State module did not load: pyVmomi not found' + return False, "State module did not load: pyVmomi not found" # We check the supported vim versions to infer the pyVmomi version - if 'vim25/6.0' in VmomiSupport.versionMap and \ - sys.version_info > (2, 7) and sys.version_info < (2, 7, 9): + if ( + "vim25/6.0" in VmomiSupport.versionMap + and sys.version_info > (2, 7) + and sys.version_info < (2, 7, 9) + ): - return False, ('State module did not load: Incompatible versions ' - 'of Python and pyVmomi present. See Issue #29537.') + return ( + False, + ( + "State module did not load: Incompatible versions " + "of Python and pyVmomi present. See Issue #29537." + ), + ) return True -def vm_configured(name, vm_name, cpu, memory, image, version, interfaces, - disks, scsi_devices, serial_ports, datacenter, datastore, - placement, cd_dvd_drives=None, sata_controllers=None, - advanced_configs=None, template=None, tools=True, - power_on=False, deploy=False): - ''' +def vm_configured( + name, + vm_name, + cpu, + memory, + image, + version, + interfaces, + disks, + scsi_devices, + serial_ports, + datacenter, + datastore, + placement, + cd_dvd_drives=None, + sata_controllers=None, + advanced_configs=None, + template=None, + tools=True, + power_on=False, + deploy=False, +): + """ Selects the correct operation to be executed on a virtual machine, non existing machines will be created, existing ones will be updated if the config differs. - ''' - result = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + """ + result = {"name": name, "result": None, "changes": {}, "comment": ""} - log.trace('Validating virtual machine configuration') + log.trace("Validating virtual machine configuration") schema = ESXVirtualMachineConfigSchema.serialize() - log.trace('schema = %s', schema) + log.trace("schema = %s", schema) try: - jsonschema.validate({'vm_name': vm_name, - 'cpu': cpu, - 'memory': memory, - 'image': image, - 'version': version, - 'interfaces': interfaces, - 'disks': disks, - 'scsi_devices': scsi_devices, - 'serial_ports': serial_ports, - 'cd_dvd_drives': cd_dvd_drives, - 'sata_controllers': sata_controllers, - 'datacenter': datacenter, - 'datastore': datastore, - 'placement': placement, - 'template': template, - 'tools': tools, - 'power_on': power_on, - 'deploy': deploy}, schema) + jsonschema.validate( + { + "vm_name": vm_name, + "cpu": cpu, + "memory": memory, + "image": image, + "version": version, + "interfaces": interfaces, + "disks": disks, + "scsi_devices": scsi_devices, + "serial_ports": serial_ports, + "cd_dvd_drives": cd_dvd_drives, + "sata_controllers": sata_controllers, + "datacenter": datacenter, + "datastore": datastore, + "placement": placement, + "template": template, + "tools": tools, + "power_on": power_on, + "deploy": deploy, + }, + schema, + ) except jsonschema.exceptions.ValidationError as exc: raise salt.exceptions.InvalidConfigError(exc) - service_instance = __salt__['vsphere.get_service_instance_via_proxy']() + service_instance = __salt__["vsphere.get_service_instance_via_proxy"]() try: - __salt__['vsphere.get_vm'](vm_name, vm_properties=['name'], - service_instance=service_instance) + __salt__["vsphere.get_vm"]( + vm_name, vm_properties=["name"], service_instance=service_instance + ) except salt.exceptions.VMwareObjectRetrievalError: - vm_file = __salt__['vsphere.get_vm_config_file']( - vm_name, datacenter, - placement, datastore, - service_instance=service_instance) + vm_file = __salt__["vsphere.get_vm_config_file"]( + vm_name, datacenter, placement, datastore, service_instance=service_instance + ) if vm_file: - if __opts__['test']: - result.update({'comment': 'The virtual machine {0}' - ' will be registered.'.format(vm_name)}) - __salt__['vsphere.disconnect'](service_instance) + if __opts__["test"]: + result.update( + { + "comment": "The virtual machine {0}" + " will be registered.".format(vm_name) + } + ) + __salt__["vsphere.disconnect"](service_instance) return result - result = vm_registered(vm_name, datacenter, placement, - vm_file, power_on=power_on) + result = vm_registered( + vm_name, datacenter, placement, vm_file, power_on=power_on + ) return result else: - if __opts__['test']: - result.update({'comment': 'The virtual machine {0}' - ' will be created.'.format(vm_name)}) - __salt__['vsphere.disconnect'](service_instance) + if __opts__["test"]: + result.update( + { + "comment": "The virtual machine {0}" + " will be created.".format(vm_name) + } + ) + __salt__["vsphere.disconnect"](service_instance) return result if template: result = vm_cloned(name) else: - result = vm_created(name, vm_name, cpu, memory, image, version, - interfaces, disks, scsi_devices, - serial_ports, datacenter, datastore, - placement, cd_dvd_drives=cd_dvd_drives, - advanced_configs=advanced_configs, - power_on=power_on) + result = vm_created( + name, + vm_name, + cpu, + memory, + image, + version, + interfaces, + disks, + scsi_devices, + serial_ports, + datacenter, + datastore, + placement, + cd_dvd_drives=cd_dvd_drives, + advanced_configs=advanced_configs, + power_on=power_on, + ) return result - result = vm_updated(name, vm_name, cpu, memory, image, version, - interfaces, disks, scsi_devices, - serial_ports, datacenter, datastore, - cd_dvd_drives=cd_dvd_drives, - sata_controllers=sata_controllers, - advanced_configs=advanced_configs, - power_on=power_on) - __salt__['vsphere.disconnect'](service_instance) + result = vm_updated( + name, + vm_name, + cpu, + memory, + image, + version, + interfaces, + disks, + scsi_devices, + serial_ports, + datacenter, + datastore, + cd_dvd_drives=cd_dvd_drives, + sata_controllers=sata_controllers, + advanced_configs=advanced_configs, + power_on=power_on, + ) + __salt__["vsphere.disconnect"](service_instance) log.trace(result) return result def vm_cloned(name): - ''' + """ Clones a virtual machine from a template virtual machine if it doesn't exist and a template is defined. - ''' - result = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + result = {"name": name, "result": True, "changes": {}, "comment": ""} return result -def vm_updated(name, vm_name, cpu, memory, image, version, interfaces, - disks, scsi_devices, serial_ports, datacenter, datastore, - cd_dvd_drives=None, sata_controllers=None, - advanced_configs=None, power_on=False): - ''' +def vm_updated( + name, + vm_name, + cpu, + memory, + image, + version, + interfaces, + disks, + scsi_devices, + serial_ports, + datacenter, + datastore, + cd_dvd_drives=None, + sata_controllers=None, + advanced_configs=None, + power_on=False, +): + """ Updates a virtual machine configuration if there is a difference between the given and deployed configuration. - ''' - result = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + """ + result = {"name": name, "result": None, "changes": {}, "comment": ""} - service_instance = __salt__['vsphere.get_service_instance_via_proxy']() - current_config = __salt__['vsphere.get_vm_config']( - vm_name, - datacenter=datacenter, - objects=False, - service_instance=service_instance) + service_instance = __salt__["vsphere.get_service_instance_via_proxy"]() + current_config = __salt__["vsphere.get_vm_config"]( + vm_name, datacenter=datacenter, objects=False, service_instance=service_instance + ) - diffs = __salt__['vsphere.compare_vm_configs']( - {'name': vm_name, - 'cpu': cpu, - 'memory': memory, - 'image': image, - 'version': version, - 'interfaces': interfaces, - 'disks': disks, - 'scsi_devices': scsi_devices, - 'serial_ports': serial_ports, - 'datacenter': datacenter, - 'datastore': datastore, - 'cd_drives': cd_dvd_drives, - 'sata_controllers': sata_controllers, - 'advanced_configs': advanced_configs}, - current_config) + diffs = __salt__["vsphere.compare_vm_configs"]( + { + "name": vm_name, + "cpu": cpu, + "memory": memory, + "image": image, + "version": version, + "interfaces": interfaces, + "disks": disks, + "scsi_devices": scsi_devices, + "serial_ports": serial_ports, + "datacenter": datacenter, + "datastore": datastore, + "cd_drives": cd_dvd_drives, + "sata_controllers": sata_controllers, + "advanced_configs": advanced_configs, + }, + current_config, + ) if not diffs: - result.update({ - 'result': True, - 'changes': {}, - 'comment': 'Virtual machine {0} is already up to date'.format(vm_name)}) + result.update( + { + "result": True, + "changes": {}, + "comment": "Virtual machine {0} is already up to date".format(vm_name), + } + ) return result - if __opts__['test']: - comment = 'State vm_updated will update virtual machine \'{0}\' ' \ - 'in datacenter \'{1}\':\n{2}'.format(vm_name, - datacenter, - '\n'.join([':\n'.join([key, difference.changes_str]) - for key, difference in six.iteritems(diffs)])) - result.update({'result': None, - 'comment': comment}) - __salt__['vsphere.disconnect'](service_instance) + if __opts__["test"]: + comment = ( + "State vm_updated will update virtual machine '{0}' " + "in datacenter '{1}':\n{2}".format( + vm_name, + datacenter, + "\n".join( + [ + ":\n".join([key, difference.changes_str]) + for key, difference in six.iteritems(diffs) + ] + ), + ) + ) + result.update({"result": None, "comment": comment}) + __salt__["vsphere.disconnect"](service_instance) return result try: - changes = __salt__['vsphere.update_vm'](vm_name, cpu, memory, image, - version, interfaces, disks, - scsi_devices, serial_ports, - datacenter, datastore, - cd_dvd_drives=cd_dvd_drives, - sata_controllers=sata_controllers, - advanced_configs=advanced_configs, - service_instance=service_instance) + changes = __salt__["vsphere.update_vm"]( + vm_name, + cpu, + memory, + image, + version, + interfaces, + disks, + scsi_devices, + serial_ports, + datacenter, + datastore, + cd_dvd_drives=cd_dvd_drives, + sata_controllers=sata_controllers, + advanced_configs=advanced_configs, + service_instance=service_instance, + ) except salt.exceptions.CommandExecutionError as exc: - log.error('Error: %s', exc) + log.error("Error: %s", exc) if service_instance: - __salt__['vsphere.disconnect'](service_instance) - result.update({ - 'result': False, - 'comment': six.text_type(exc)}) + __salt__["vsphere.disconnect"](service_instance) + result.update({"result": False, "comment": six.text_type(exc)}) return result if power_on: try: - __salt__['vsphere.power_on_vm'](vm_name, datacenter) + __salt__["vsphere.power_on_vm"](vm_name, datacenter) except salt.exceptions.VMwarePowerOnError as exc: - log.error('Error: %s', exc) + log.error("Error: %s", exc) if service_instance: - __salt__['vsphere.disconnect'](service_instance) - result.update({ - 'result': False, - 'comment': six.text_type(exc)}) + __salt__["vsphere.disconnect"](service_instance) + result.update({"result": False, "comment": six.text_type(exc)}) return result - changes.update({'power_on': True}) + changes.update({"power_on": True}) - __salt__['vsphere.disconnect'](service_instance) + __salt__["vsphere.disconnect"](service_instance) - result = {'name': name, - 'result': True, - 'changes': changes, - 'comment': 'Virtual machine ' - '{0} was updated successfully'.format(vm_name)} + result = { + "name": name, + "result": True, + "changes": changes, + "comment": "Virtual machine " "{0} was updated successfully".format(vm_name), + } return result -def vm_created(name, vm_name, cpu, memory, image, version, interfaces, - disks, scsi_devices, serial_ports, datacenter, datastore, - placement, ide_controllers=None, sata_controllers=None, - cd_dvd_drives=None, advanced_configs=None, power_on=False): - ''' +def vm_created( + name, + vm_name, + cpu, + memory, + image, + version, + interfaces, + disks, + scsi_devices, + serial_ports, + datacenter, + datastore, + placement, + ide_controllers=None, + sata_controllers=None, + cd_dvd_drives=None, + advanced_configs=None, + power_on=False, +): + """ Creates a virtual machine with the given properties if it doesn't exist. - ''' - result = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + """ + result = {"name": name, "result": None, "changes": {}, "comment": ""} - if __opts__['test']: - result['comment'] = 'Virtual machine {0} will be created'.format( - vm_name) + if __opts__["test"]: + result["comment"] = "Virtual machine {0} will be created".format(vm_name) return result - service_instance = __salt__['vsphere.get_service_instance_via_proxy']() + service_instance = __salt__["vsphere.get_service_instance_via_proxy"]() try: - info = __salt__['vsphere.create_vm'](vm_name, cpu, memory, image, - version, datacenter, datastore, - placement, interfaces, disks, - scsi_devices, - serial_ports=serial_ports, - ide_controllers=ide_controllers, - sata_controllers=sata_controllers, - cd_drives=cd_dvd_drives, - advanced_configs=advanced_configs, - service_instance=service_instance) + info = __salt__["vsphere.create_vm"]( + vm_name, + cpu, + memory, + image, + version, + datacenter, + datastore, + placement, + interfaces, + disks, + scsi_devices, + serial_ports=serial_ports, + ide_controllers=ide_controllers, + sata_controllers=sata_controllers, + cd_drives=cd_dvd_drives, + advanced_configs=advanced_configs, + service_instance=service_instance, + ) except salt.exceptions.CommandExecutionError as exc: - log.error('Error: %s', exc) + log.error("Error: %s", exc) if service_instance: - __salt__['vsphere.disconnect'](service_instance) - result.update({ - 'result': False, - 'comment': six.text_type(exc)}) + __salt__["vsphere.disconnect"](service_instance) + result.update({"result": False, "comment": six.text_type(exc)}) return result if power_on: try: - __salt__['vsphere.power_on_vm'](vm_name, datacenter, - service_instance=service_instance) + __salt__["vsphere.power_on_vm"]( + vm_name, datacenter, service_instance=service_instance + ) except salt.exceptions.VMwarePowerOnError as exc: - log.error('Error: %s', exc) + log.error("Error: %s", exc) if service_instance: - __salt__['vsphere.disconnect'](service_instance) - result.update({ - 'result': False, - 'comment': six.text_type(exc)}) + __salt__["vsphere.disconnect"](service_instance) + result.update({"result": False, "comment": six.text_type(exc)}) return result - info['power_on'] = power_on + info["power_on"] = power_on - changes = {'name': vm_name, 'info': info} - __salt__['vsphere.disconnect'](service_instance) - result = {'name': name, - 'result': True, - 'changes': changes, - 'comment': 'Virtual machine ' - '{0} created successfully'.format(vm_name)} + changes = {"name": vm_name, "info": info} + __salt__["vsphere.disconnect"](service_instance) + result = { + "name": name, + "result": True, + "changes": changes, + "comment": "Virtual machine " "{0} created successfully".format(vm_name), + } return result def vm_registered(vm_name, datacenter, placement, vm_file, power_on=False): - ''' + """ Registers a virtual machine if the machine files are available on the main datastore. - ''' - result = {'name': vm_name, - 'result': None, - 'changes': {}, - 'comment': ''} + """ + result = {"name": vm_name, "result": None, "changes": {}, "comment": ""} - vmx_path = '{0}{1}'.format(vm_file.folderPath, vm_file.file[0].path) - log.trace('Registering virtual machine with vmx file: {0}'.format(vmx_path)) - service_instance = __salt__['vsphere.get_service_instance_via_proxy']() + vmx_path = "{0}{1}".format(vm_file.folderPath, vm_file.file[0].path) + log.trace("Registering virtual machine with vmx file: {0}".format(vmx_path)) + service_instance = __salt__["vsphere.get_service_instance_via_proxy"]() try: - __salt__['vsphere.register_vm'](vm_name, datacenter, - placement, vmx_path, - service_instance=service_instance) + __salt__["vsphere.register_vm"]( + vm_name, datacenter, placement, vmx_path, service_instance=service_instance + ) except salt.exceptions.VMwareMultipleObjectsError as exc: - log.error('Error: %s', exc) + log.error("Error: %s", exc) if service_instance: - __salt__['vsphere.disconnect'](service_instance) - result.update({'result': False, - 'comment': six.text_type(exc)}) + __salt__["vsphere.disconnect"](service_instance) + result.update({"result": False, "comment": six.text_type(exc)}) return result except salt.exceptions.VMwareVmRegisterError as exc: - log.error('Error: %s', exc) + log.error("Error: %s", exc) if service_instance: - __salt__['vsphere.disconnect'](service_instance) - result.update({'result': False, - 'comment': six.text_type(exc)}) + __salt__["vsphere.disconnect"](service_instance) + result.update({"result": False, "comment": six.text_type(exc)}) return result if power_on: try: - __salt__['vsphere.power_on_vm'](vm_name, datacenter, - service_instance=service_instance) + __salt__["vsphere.power_on_vm"]( + vm_name, datacenter, service_instance=service_instance + ) except salt.exceptions.VMwarePowerOnError as exc: - log.error('Error: %s', exc) + log.error("Error: %s", exc) if service_instance: - __salt__['vsphere.disconnect'](service_instance) - result.update({ - 'result': False, - 'comment': six.text_type(exc)}) + __salt__["vsphere.disconnect"](service_instance) + result.update({"result": False, "comment": six.text_type(exc)}) return result - __salt__['vsphere.disconnect'](service_instance) - result.update({'result': True, - 'changes': {'name': vm_name, 'power_on': power_on}, - 'comment': 'Virtual machine ' - '{0} registered successfully'.format(vm_name)}) + __salt__["vsphere.disconnect"](service_instance) + result.update( + { + "result": True, + "changes": {"name": vm_name, "power_on": power_on}, + "comment": "Virtual machine " "{0} registered successfully".format(vm_name), + } + ) return result diff --git a/salt/states/etcd_mod.py b/salt/states/etcd_mod.py index 6ab7d71522c..d9974350639 100644 --- a/salt/states/etcd_mod.py +++ b/salt/states/etcd_mod.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Manage etcd Keys ================ @@ -115,37 +115,39 @@ Available Functions - profile: my_etcd_config - watch: - file: /some/file.txt -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals # Define the module's virtual name -__virtualname__ = 'etcd' +__virtualname__ = "etcd" # Function aliases __func_alias__ = { - 'set_': 'set', + "set_": "set", } # Import third party libs try: import salt.utils.etcd_util # pylint: disable=W0611 + HAS_ETCD = True except ImportError: HAS_ETCD = False def __virtual__(): - ''' + """ Only return if python-etcd is installed - ''' - - return __virtualname__ if HAS_ETCD else False + """ + if HAS_ETCD: + return __virtualname__ + return (False, "Unable to import etcd_util") def set_(name, value, profile=None, **kwargs): - ''' + """ Set a key in etcd name @@ -163,37 +165,35 @@ def set_(name, value, profile=None, **kwargs): etcd.host: 127.0.0.1 etcd.port: 4001 - ''' + """ created = False rtn = { - 'name': name, - 'comment': 'Key contains correct value', - 'result': True, - 'changes': {} + "name": name, + "comment": "Key contains correct value", + "result": True, + "changes": {}, } - current = __salt__['etcd.get'](name, profile=profile, **kwargs) + current = __salt__["etcd.get"](name, profile=profile, **kwargs) if not current: created = True - result = __salt__['etcd.set'](name, value, profile=profile, **kwargs) + result = __salt__["etcd.set"](name, value, profile=profile, **kwargs) if result and result != current: if created: - rtn['comment'] = 'New key created' + rtn["comment"] = "New key created" else: - rtn['comment'] = 'Key value updated' - rtn['changes'] = { - name: value - } + rtn["comment"] = "Key value updated" + rtn["changes"] = {name: value} return rtn def wait_set(name, value, profile=None, **kwargs): - ''' + """ Set a key in etcd only if the watch statement calls it. This function is also aliased as ``wait_set``. @@ -211,18 +211,13 @@ def wait_set(name, value, profile=None, **kwargs): etcd.host: 127.0.0.1 etcd.port: 4001 - ''' + """ - return { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + return {"name": name, "changes": {}, "result": True, "comment": ""} def directory(name, profile=None, **kwargs): - ''' + """ Create a directory in etcd. name @@ -236,35 +231,28 @@ def directory(name, profile=None, **kwargs): my_etd_config: etcd.host: 127.0.0.1 etcd.port: 4001 - ''' + """ created = False - rtn = { - 'name': name, - 'comment': 'Directory exists', - 'result': True, - 'changes': {} - } + rtn = {"name": name, "comment": "Directory exists", "result": True, "changes": {}} - current = __salt__['etcd.get'](name, profile=profile, recurse=True, **kwargs) + current = __salt__["etcd.get"](name, profile=profile, recurse=True, **kwargs) if not current: created = True - result = __salt__['etcd.set'](name, None, directory=True, profile=profile, **kwargs) + result = __salt__["etcd.set"](name, None, directory=True, profile=profile, **kwargs) if result and result != current: if created: - rtn['comment'] = 'New directory created' - rtn['changes'] = { - name: 'Created' - } + rtn["comment"] = "New directory created" + rtn["changes"] = {name: "Created"} return rtn def rm(name, recurse=False, profile=None, **kwargs): - ''' + """ Deletes a key from etcd name @@ -282,31 +270,25 @@ def rm(name, recurse=False, profile=None, **kwargs): my_etd_config: etcd.host: 127.0.0.1 etcd.port: 4001 - ''' + """ - rtn = { - 'name': name, - 'result': True, - 'changes': {} - } + rtn = {"name": name, "result": True, "changes": {}} - if not __salt__['etcd.get'](name, profile=profile, **kwargs): - rtn['comment'] = 'Key does not exist' + if not __salt__["etcd.get"](name, profile=profile, **kwargs): + rtn["comment"] = "Key does not exist" return rtn - if __salt__['etcd.rm'](name, recurse=recurse, profile=profile, **kwargs): - rtn['comment'] = 'Key removed' - rtn['changes'] = { - name: 'Deleted' - } + if __salt__["etcd.rm"](name, recurse=recurse, profile=profile, **kwargs): + rtn["comment"] = "Key removed" + rtn["changes"] = {name: "Deleted"} else: - rtn['comment'] = 'Unable to remove key' + rtn["comment"] = "Unable to remove key" return rtn def wait_rm(name, recurse=False, profile=None, **kwargs): - ''' + """ Deletes a key from etcd only if the watch statement calls it. This function is also aliased as ``wait_rm``. @@ -324,18 +306,13 @@ def wait_rm(name, recurse=False, profile=None, **kwargs): my_etd_config: etcd.host: 127.0.0.1 etcd.port: 4001 - ''' + """ - return { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + return {"name": name, "changes": {}, "result": True, "comment": ""} def mod_watch(name, **kwargs): - ''' + """ The etcd watcher, called to invoke the watch command. When called, execute a etcd function based on a watch call requisite. @@ -344,25 +321,20 @@ def mod_watch(name, **kwargs): :ref:`requisite <requisites>`. It should not be called directly. Parameters for this function should be set by the state being triggered. - ''' + """ # Watch to set etcd key - if kwargs.get('sfun') in ['wait_set_key', 'wait_set']: - return set_( - name, - kwargs.get('value'), - kwargs.get('profile')) + if kwargs.get("sfun") in ["wait_set_key", "wait_set"]: + return set_(name, kwargs.get("value"), kwargs.get("profile")) # Watch to rm etcd key - if kwargs.get('sfun') in ['wait_rm_key', 'wait_rm']: - return rm( - name, - kwargs.get('profile')) + if kwargs.get("sfun") in ["wait_rm_key", "wait_rm"]: + return rm(name, kwargs.get("profile")) return { - 'name': name, - 'changes': {}, - 'comment': 'etcd.{0[sfun]} does not work with the watch requisite, ' - 'please use etcd.wait_set or etcd.wait_rm'.format(kwargs), - 'result': False + "name": name, + "changes": {}, + "comment": "etcd.{0[sfun]} does not work with the watch requisite, " + "please use etcd.wait_set or etcd.wait_rm".format(kwargs), + "result": False, } diff --git a/salt/states/ethtool.py b/salt/states/ethtool.py index ffeec1157e6..80e62da1e99 100644 --- a/salt/states/ethtool.py +++ b/salt/states/ethtool.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configuration of network device .. versionadded:: 2016.11.0 @@ -28,10 +28,11 @@ Configuration of network device - name: eth0 - tcp_segmentation_offload: on -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -42,14 +43,16 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Provide ethtool state - ''' - return 'ethtool' if 'ethtool.show_driver' in __salt__ else False + """ + if "ethtool.show_driver" in __salt__: + return "ethtool" + return (False, "ethtool module could not be loaded") def coalesce(name, **kwargs): - ''' + """ Manage coalescing settings of network device name @@ -83,23 +86,27 @@ def coalesce(name, **kwargs): - tx_frames_high: 0 - sample_interval: 0 - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Network device {0} coalescing settings are up to date.'.format(name), + "name": name, + "changes": {}, + "result": True, + "comment": "Network device {0} coalescing settings are up to date.".format( + name + ), } apply_coalescing = False - if 'test' not in kwargs: - kwargs['test'] = __opts__.get('test', False) + if "test" not in kwargs: + kwargs["test"] = __opts__.get("test", False) # Build coalescing settings try: - old = __salt__['ethtool.show_coalesce'](name) + old = __salt__["ethtool.show_coalesce"](name) if not isinstance(old, dict): - ret['result'] = False - ret['comment'] = 'Device {0} coalescing settings are not supported'.format(name) + ret["result"] = False + ret["comment"] = "Device {0} coalescing settings are not supported".format( + name + ) return ret new = {} @@ -109,43 +116,45 @@ def coalesce(name, **kwargs): for key, value in kwargs.items(): if key in old and value != old[key]: new.update({key: value}) - diff.append('{0}: {1}'.format(key, value)) + diff.append("{0}: {1}".format(key, value)) # Dry run - if kwargs['test']: + if kwargs["test"]: if not new: return ret if new: - ret['result'] = None - ret['comment'] = 'Device {0} coalescing settings are set to be ' \ - 'updated:\n{1}'.format(name, '\n'.join(diff)) + ret["result"] = None + ret["comment"] = ( + "Device {0} coalescing settings are set to be " + "updated:\n{1}".format(name, "\n".join(diff)) + ) return ret # Prepare return output if new: apply_coalescing = True - ret['comment'] = 'Device {0} coalescing settings updated.'.format(name) - ret['changes']['ethtool_coalesce'] = '\n'.join(diff) + ret["comment"] = "Device {0} coalescing settings updated.".format(name) + ret["changes"]["ethtool_coalesce"] = "\n".join(diff) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret # Apply coalescing settings if apply_coalescing: try: - __salt__['ethtool.set_coalesce'](name, **new) + __salt__["ethtool.set_coalesce"](name, **new) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret return ret def ring(name, **kwargs): - ''' + """ Manage rx/tx ring parameters of network device Use 'max' word to set with factory maximum @@ -163,23 +172,23 @@ def ring(name, **kwargs): - rx_jumbo: 0 - tx: max - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Network device {0} ring parameters are up to date.'.format(name), + "name": name, + "changes": {}, + "result": True, + "comment": "Network device {0} ring parameters are up to date.".format(name), } apply_ring = False - if 'test' not in kwargs: - kwargs['test'] = __opts__.get('test', False) + if "test" not in kwargs: + kwargs["test"] = __opts__.get("test", False) # Build ring parameters try: - old = __salt__['ethtool.show_ring'](name) + old = __salt__["ethtool.show_ring"](name) if not isinstance(old, dict): - ret['result'] = False - ret['comment'] = 'Device {0} ring parameters are not supported'.format(name) + ret["result"] = False + ret["comment"] = "Device {0} ring parameters are not supported".format(name) return ret new = {} @@ -188,48 +197,50 @@ def ring(name, **kwargs): # Retreive changes to made for key, value in kwargs.items(): if key in old: - if value == 'max': - value = old['{0}_max'.format(key)] + if value == "max": + value = old["{0}_max".format(key)] if value != old[key]: new.update({key: value}) - diff.append('{0}: {1}'.format(key, value)) + diff.append("{0}: {1}".format(key, value)) # Dry run - if kwargs['test']: + if kwargs["test"]: if not new: return ret if new: - ret['result'] = None - ret['comment'] = 'Device {0} ring parameters are set to be ' \ - 'updated:\n{1}'.format(name, '\n'.join(diff)) + ret["result"] = None + ret["comment"] = ( + "Device {0} ring parameters are set to be " + "updated:\n{1}".format(name, "\n".join(diff)) + ) return ret # Prepare return output if new: apply_ring = True - ret['comment'] = 'Device {0} ring parameters updated.'.format(name) - ret['changes']['ethtool_ring'] = '\n'.join(diff) + ret["comment"] = "Device {0} ring parameters updated.".format(name) + ret["changes"]["ethtool_ring"] = "\n".join(diff) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret # Apply ring parameters if apply_ring: try: - __salt__['ethtool.set_ring'](name, **new) + __salt__["ethtool.set_ring"](name, **new) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret return ret def offload(name, **kwargs): - ''' + """ Manage protocol offload and other features of network device name @@ -242,23 +253,25 @@ def offload(name, **kwargs): - name: eth0 - tcp_segmentation_offload: on - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Network device {0} offload settings are up to date.'.format(name), + "name": name, + "changes": {}, + "result": True, + "comment": "Network device {0} offload settings are up to date.".format(name), } apply_offload = False - if 'test' not in kwargs: - kwargs['test'] = __opts__.get('test', False) + if "test" not in kwargs: + kwargs["test"] = __opts__.get("test", False) # Build offload settings try: - old = __salt__['ethtool.show_offload'](name) + old = __salt__["ethtool.show_offload"](name) if not isinstance(old, dict): - ret['result'] = False - ret['comment'] = 'Device {0} offload settings are not supported'.format(name) + ret["result"] = False + ret["comment"] = "Device {0} offload settings are not supported".format( + name + ) return ret new = {} @@ -269,36 +282,38 @@ def offload(name, **kwargs): value = value and "on" or "off" if key in old and value != old[key]: new.update({key: value}) - diff.append('{0}: {1}'.format(key, value)) + diff.append("{0}: {1}".format(key, value)) # Dry run - if kwargs['test']: + if kwargs["test"]: if not new: return ret if new: - ret['result'] = None - ret['comment'] = 'Device {0} offload settings are set to be ' \ - 'updated:\n{1}'.format(name, '\n'.join(diff)) + ret["result"] = None + ret["comment"] = ( + "Device {0} offload settings are set to be " + "updated:\n{1}".format(name, "\n".join(diff)) + ) return ret # Prepare return output if new: apply_offload = True - ret['comment'] = 'Device {0} offload settings updated.'.format(name) - ret['changes']['ethtool_offload'] = '\n'.join(diff) + ret["comment"] = "Device {0} offload settings updated.".format(name) + ret["changes"]["ethtool_offload"] = "\n".join(diff) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret # Apply offload settings if apply_offload: try: - __salt__['ethtool.set_offload'](name, **new) + __salt__["ethtool.set_offload"](name, **new) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret return ret diff --git a/salt/states/event.py b/salt/states/event.py index 167e86571ff..96baf7f8f67 100644 --- a/salt/states/event.py +++ b/salt/states/event.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Send events through Salt's event system during state runs -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,15 +10,17 @@ from __future__ import absolute_import, print_function, unicode_literals import salt.utils.functools -def send(name, - data=None, - preload=None, - with_env=False, - with_grains=False, - with_pillar=False, - show_changed=True, - **kwargs): - ''' +def send( + name, + data=None, + preload=None, + with_env=False, + with_grains=False, + with_pillar=False, + show_changed=True, + **kwargs +): + """ Send an event to the Salt Master .. versionadded:: 2014.7.0 @@ -42,32 +44,34 @@ def send(name, status: "Half-way through the state run!" # ...snip bunch of states below - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if show_changed: - ret['changes'] = {'tag': name, 'data': data} + ret["changes"] = {"tag": name, "data": data} else: - ret['changes'] = {} + ret["changes"] = {} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Event would have been fired' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Event would have been fired" return ret - ret['result'] = __salt__['event.send'](name, - data=data, - preload=preload, - with_env=with_env, - with_grains=with_grains, - with_pillar=with_pillar, - **kwargs) - ret['comment'] = 'Event fired' + ret["result"] = __salt__["event.send"]( + name, + data=data, + preload=preload, + with_env=with_env, + with_grains=with_grains, + with_pillar=with_pillar, + **kwargs + ) + ret["comment"] = "Event fired" return ret def wait(name, sfun=None): - ''' + """ Fire an event on the Salt master event bus if called from a watch statement .. versionadded:: 2014.7.0 @@ -95,10 +99,10 @@ def wait(name, sfun=None): new_web_server_ip: {{ grains['ipv4'] | first() }} - watch: - pkg: apache - ''' + """ # Noop. The state system will call the mod_watch function instead. - return {'name': name, 'changes': {}, 'result': True, 'comment': ''} + return {"name": name, "changes": {}, "result": True, "comment": ""} -mod_watch = salt.utils.functools.alias_function(send, 'mod_watch') -fire_master = salt.utils.functools.alias_function(send, 'fire_master') +mod_watch = salt.utils.functools.alias_function(send, "mod_watch") +fire_master = salt.utils.functools.alias_function(send, "fire_master") diff --git a/salt/states/file.py b/salt/states/file.py index dd5bcec62a8..85c9f05ee9d 100644 --- a/salt/states/file.py +++ b/salt/states/file.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Operations on regular files, special files, directories, and symlinks ===================================================================== @@ -276,10 +276,11 @@ For example: first_of_month: 6 first_of_year: all -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import difflib import itertools @@ -291,8 +292,12 @@ import shutil import sys import time import traceback + +# pylint: disable=no-name-in-module from collections import Iterable, Mapping, defaultdict -from datetime import datetime, date # python3 problem in the making? + +# pylint: enable=no-name-in-module +from datetime import date, datetime # python3 problem in the making? # Import salt libs import salt.loader @@ -309,6 +314,11 @@ import salt.utils.templates import salt.utils.url import salt.utils.versions from salt.exceptions import CommandExecutionError + +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import zip_longest +from salt.ext.six.moves.urllib.parse import urlparse as _urlparse from salt.serializers import DeserializationError from salt.state import get_accumulator_dir as _get_accumulator_dir @@ -317,40 +327,34 @@ if salt.utils.platform.is_windows(): import salt.utils.win_functions import salt.utils.winapi -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import zip_longest -from salt.ext.six.moves.urllib.parse import urlparse as _urlparse # pylint: disable=no-name-in-module + if salt.utils.platform.is_windows(): import pywintypes import win32com.client log = logging.getLogger(__name__) -COMMENT_REGEX = r'^([[:space:]]*){0}[[:space:]]?' +COMMENT_REGEX = r"^([[:space:]]*){0}[[:space:]]?" __NOT_FOUND = object() __func_alias__ = { - 'copy_': 'copy', + "copy_": "copy", } def _get_accumulator_filepath(): - ''' + """ Return accumulator data path. - ''' - return os.path.join( - _get_accumulator_dir(__opts__['cachedir']), - __instance_id__ - ) + """ + return os.path.join(_get_accumulator_dir(__opts__["cachedir"]), __instance_id__) def _load_accumulators(): def _deserialize(path): serial = salt.payload.Serial(__opts__) - ret = {'accumulators': {}, 'accumulators_deps': {}} + ret = {"accumulators": {}, "accumulators_deps": {}} try: - with salt.utils.files.fopen(path, 'rb') as f: + with salt.utils.files.fopen(path, "rb") as f: loaded = serial.load(f) return loaded if loaded else ret except (IOError, NameError): @@ -359,16 +363,15 @@ def _load_accumulators(): loaded = _deserialize(_get_accumulator_filepath()) - return loaded['accumulators'], loaded['accumulators_deps'] + return loaded["accumulators"], loaded["accumulators_deps"] def _persist_accummulators(accumulators, accumulators_deps): - accumm_data = {'accumulators': accumulators, - 'accumulators_deps': accumulators_deps} + accumm_data = {"accumulators": accumulators, "accumulators_deps": accumulators_deps} serial = salt.payload.Serial(__opts__) try: - with salt.utils.files.fopen(_get_accumulator_filepath(), 'w+b') as f: + with salt.utils.files.fopen(_get_accumulator_filepath(), "w+b") as f: serial.dump(accumm_data, f) except NameError: # msgpack error from salt-ssh @@ -376,25 +379,23 @@ def _persist_accummulators(accumulators, accumulators_deps): def _check_user(user, group): - ''' + """ Checks if the named user and group are present on the minion - ''' - err = '' + """ + err = "" if user: - uid = __salt__['file.user_to_uid'](user) - if uid == '': - err += 'User {0} is not available '.format(user) + uid = __salt__["file.user_to_uid"](user) + if uid == "": + err += "User {0} is not available ".format(user) if group: - gid = __salt__['file.group_to_gid'](group) - if gid == '': - err += 'Group {0} is not available'.format(group) + gid = __salt__["file.group_to_gid"](group) + if gid == "": + err += "Group {0} is not available".format(group) return err -def _is_valid_relpath( - relpath, - maxdepth=None): - ''' +def _is_valid_relpath(relpath, maxdepth=None): + """ Performs basic sanity checks on a relative path. Requires POSIX-compatible paths (i.e. the kind obtained through @@ -402,7 +403,7 @@ def _is_valid_relpath( Ensures that the path does not contain directory transversal, and that it does not exceed a stated maximum depth (if specified). - ''' + """ # Check relpath surrounded by slashes, so that `..` can be caught as # a path component at the start, end, and in the middle of the path. sep, pardir = posixpath.sep, posixpath.pardir @@ -419,25 +420,26 @@ def _is_valid_relpath( def _salt_to_os_path(path): - ''' + """ Converts a path from the form received via salt master to the OS's native path format. - ''' + """ return os.path.normpath(path.replace(posixpath.sep, os.path.sep)) def _gen_recurse_managed_files( - name, - source, - keep_symlinks=False, - include_pat=None, - exclude_pat=None, - maxdepth=None, - include_empty=False, - **kwargs): - ''' + name, + source, + keep_symlinks=False, + include_pat=None, + exclude_pat=None, + maxdepth=None, + include_empty=False, + **kwargs +): + """ Generate the list of files managed by a recurse state - ''' + """ # Convert a relative path generated from salt master paths to an OS path # using "name" as the base directory @@ -451,7 +453,8 @@ def _gen_recurse_managed_files( if not _is_valid_relpath(srelpath, maxdepth=maxdepth): continue if not salt.utils.stringutils.check_include_exclude( - srelpath, include_pat, exclude_pat): + srelpath, include_pat, exclude_pat + ): continue # Check for all paths that begin with the symlink # and axe it leaving only the dirs/files below it. @@ -460,8 +463,10 @@ def _gen_recurse_managed_files( _filenames = list(filenames) for filename in _filenames: if filename.startswith(lname): - log.debug('** skipping file ** {0}, it intersects a ' - 'symlink'.format(filename)) + log.debug( + "** skipping file ** {0}, it intersects a " + "symlink".format(filename) + ) filenames.remove(filename) # Create the symlink along with the necessary dirs. # The dir perms/ownership will be adjusted later @@ -485,12 +490,12 @@ def _gen_recurse_managed_files( if not srcpath.endswith(posixpath.sep): # we're searching for things that start with this *directory*. srcpath = srcpath + posixpath.sep - fns_ = __salt__['cp.list_master'](senv, srcpath) + fns_ = __salt__["cp.list_master"](senv, srcpath) # If we are instructed to keep symlinks, then process them. if keep_symlinks: # Make this global so that emptydirs can use it if needed. - symlinks = __salt__['cp.list_master_symlinks'](senv, srcpath) + symlinks = __salt__["cp.list_master_symlinks"](senv, srcpath) fns_ = process_symlinks(fns_, symlinks) for fn_ in fns_: @@ -508,7 +513,8 @@ def _gen_recurse_managed_files( # Check if it is to be excluded. Match only part of the path # relative to the target directory if not salt.utils.stringutils.check_include_exclude( - relname, include_pat, exclude_pat): + relname, include_pat, exclude_pat + ): continue dest = full_path(relname) dirname = os.path.dirname(dest) @@ -523,13 +529,14 @@ def _gen_recurse_managed_files( managed_files.add((dest, src)) if include_empty: - mdirs = __salt__['cp.list_master_dirs'](senv, srcpath) + mdirs = __salt__["cp.list_master_dirs"](senv, srcpath) for mdir in mdirs: relname = posixpath.relpath(mdir, srcpath) if not _is_valid_relpath(relname, maxdepth=maxdepth): continue if not salt.utils.stringutils.check_include_exclude( - relname, include_pat, exclude_pat): + relname, include_pat, exclude_pat + ): continue mdest = full_path(relname) # Check for symlinks that happen to point to an empty dir. @@ -537,8 +544,10 @@ def _gen_recurse_managed_files( islink = False for link in symlinks: if mdir.startswith(link, 0): - log.debug('** skipping empty dir ** {0}, it intersects' - ' a symlink'.format(mdir)) + log.debug( + "** skipping empty dir ** {0}, it intersects" + " a symlink".format(mdir) + ) islink = True break if islink: @@ -551,14 +560,15 @@ def _gen_recurse_managed_files( def _gen_keep_files(name, require, walk_d=None): - ''' + """ Generate the list of files that need to be kept when a dir based function like directory or recurse has a clean. - ''' + """ + def _is_child(path, directory): - ''' + """ Check whether ``path`` is child of ``directory`` - ''' + """ path = os.path.abspath(path) directory = os.path.abspath(directory) @@ -599,19 +609,19 @@ def _gen_keep_files(name, require, walk_d=None): keep = set() if isinstance(require, list): - required_files = [comp for comp in require if 'file' in comp] + required_files = [comp for comp in require if "file" in comp] for comp in required_files: for low in __lowstate__: # A requirement should match either the ID and the name of # another state. - if low['name'] == comp['file'] or low['__id__'] == comp['file']: - fn = low['name'] - fun = low['fun'] + if low["name"] == comp["file"] or low["__id__"] == comp["file"]: + fn = low["name"] + fun = low["fun"] if os.path.isdir(fn): if _is_child(fn, name): - if fun == 'recurse': + if fun == "recurse": fkeep = _gen_recurse_managed_files(**low)[3] - log.debug('Keep from {0}: {1}'.format(fn, fkeep)) + log.debug("Keep from {0}: {1}".format(fn, fkeep)) keep.update(fkeep) elif walk_d: walk_ret = set() @@ -621,29 +631,29 @@ def _gen_keep_files(name, require, walk_d=None): keep.update(_process(fn)) else: keep.add(fn) - log.debug('Files to keep from required states: {0}'.format(list(keep))) + log.debug("Files to keep from required states: {0}".format(list(keep))) return list(keep) def _check_file(name): ret = True - msg = '' + msg = "" if not os.path.isabs(name): ret = False - msg = 'Specified file {0} is not an absolute path'.format(name) + msg = "Specified file {0} is not an absolute path".format(name) elif not os.path.exists(name): ret = False - msg = '{0}: file not found'.format(name) + msg = "{0}: file not found".format(name) return ret, msg def _find_keep_files(root, keep): - ''' + """ Compile a list of valid keep files (and directories). Used by _clean_dir() - ''' + """ real_keep = set() real_keep.add(root) if isinstance(keep, list): @@ -662,10 +672,10 @@ def _find_keep_files(root, keep): def _clean_dir(root, keep, exclude_pat): - ''' + """ Clean out all of the files and directories in a directory (root) while preserving the files in a list (keep) and part of exclude_pat - ''' + """ root = os.path.normcase(root) real_keep = _find_keep_files(root, keep) removed = set() @@ -675,14 +685,15 @@ def _clean_dir(root, keep, exclude_pat): # -- check if this is a part of exclude_pat(only). No need to # check include_pat if not salt.utils.stringutils.check_include_exclude( - os.path.relpath(nfn, root), None, exclude_pat): + os.path.relpath(nfn, root), None, exclude_pat + ): return removed.add(nfn) - if not __opts__['test']: + if not __opts__["test"]: try: os.remove(nfn) except OSError: - __salt__['file.remove'](nfn) + __salt__["file.remove"](nfn) for roots, dirs, files in salt.utils.path.os_walk(root): for name in itertools.chain(dirs, files): @@ -691,25 +702,27 @@ def _clean_dir(root, keep, exclude_pat): def _error(ret, err_msg): - ret['result'] = False - ret['comment'] = err_msg + ret["result"] = False + ret["comment"] = err_msg return ret -def _check_directory(name, - user=None, - group=None, - recurse=False, - dir_mode=None, - file_mode=None, - clean=False, - require=False, - exclude_pat=None, - max_depth=None, - follow_symlinks=False): - ''' +def _check_directory( + name, + user=None, + group=None, + recurse=False, + dir_mode=None, + file_mode=None, + clean=False, + require=False, + exclude_pat=None, + max_depth=None, + follow_symlinks=False, +): + """ Check what changes need to be made on a directory - ''' + """ changes = {} if recurse or clean: assert max_depth is None or not clean @@ -724,37 +737,39 @@ def _check_directory(name, try: recurse_set = _get_recurse_set(recurse) except (TypeError, ValueError) as exc: - return False, '{0}'.format(exc), changes - if 'user' not in recurse_set: + return False, "{0}".format(exc), changes + if "user" not in recurse_set: user = None - if 'group' not in recurse_set: + if "group" not in recurse_set: group = None - if 'mode' not in recurse_set: + if "mode" not in recurse_set: dir_mode = None file_mode = None - check_files = 'ignore_files' not in recurse_set - check_dirs = 'ignore_dirs' not in recurse_set + check_files = "ignore_files" not in recurse_set + check_dirs = "ignore_dirs" not in recurse_set for root, dirs, files in walk_l: if check_files: for fname in files: fchange = {} path = os.path.join(root, fname) - stats = __salt__['file.stats']( - path, None, follow_symlinks - ) - if user is not None and user != stats.get('user'): - fchange['user'] = user - if group is not None and group != stats.get('group'): - fchange['group'] = group - if file_mode is not None and salt.utils.files.normalize_mode(file_mode) != salt.utils.files.normalize_mode(stats.get('mode')): - fchange['mode'] = file_mode + stats = __salt__["file.stats"](path, None, follow_symlinks) + if user is not None and user != stats.get("user"): + fchange["user"] = user + if group is not None and group != stats.get("group"): + fchange["group"] = group + if file_mode is not None and salt.utils.files.normalize_mode( + file_mode + ) != salt.utils.files.normalize_mode(stats.get("mode")): + fchange["mode"] = file_mode if fchange: changes[path] = fchange if check_dirs: for name_ in dirs: path = os.path.join(root, name_) - fchange = _check_dir_meta(path, user, group, dir_mode, follow_symlinks) + fchange = _check_dir_meta( + path, user, group, dir_mode, follow_symlinks + ) if fchange: changes[path] = fchange # Recurse skips root (we always do dirs, not root), so always check root: @@ -770,10 +785,11 @@ def _check_directory(name, return {} else: if not salt.utils.stringutils.check_include_exclude( - os.path.relpath(path, name), None, exclude_pat): + os.path.relpath(path, name), None, exclude_pat + ): return {} else: - return {path: {'removed': 'Removed due to clean'}} + return {path: {"removed": "Removed due to clean"}} for root, dirs, files in walk_l: for fname in files: @@ -782,37 +798,41 @@ def _check_directory(name, changes.update(_check_changes(name_)) if not os.path.isdir(name): - changes[name] = {'directory': 'new'} + changes[name] = {"directory": "new"} if changes: - comments = ['The following files will be changed:\n'] + comments = ["The following files will be changed:\n"] for fn_ in changes: for key, val in six.iteritems(changes[fn_]): - comments.append('{0}: {1} - {2}\n'.format(fn_, key, val)) - return None, ''.join(comments), changes - return True, 'The directory {0} is in the correct state'.format(name), changes + comments.append("{0}: {1} - {2}\n".format(fn_, key, val)) + return None, "".join(comments), changes + return True, "The directory {0} is in the correct state".format(name), changes -def _check_directory_win(name, - win_owner=None, - win_perms=None, - win_deny_perms=None, - win_inheritance=None, - win_perms_reset=None): - ''' +def _check_directory_win( + name, + win_owner=None, + win_perms=None, + win_deny_perms=None, + win_inheritance=None, + win_perms_reset=None, +): + """ Check what changes need to be made on a directory - ''' + """ changes = {} if not os.path.isdir(name): - changes = {name: {'directory': 'new'}} + changes = {name: {"directory": "new"}} else: # Check owner by SID if win_owner is not None: current_owner = salt.utils.win_dacl.get_owner(name) - current_owner_sid = salt.utils.win_functions.get_sid_from_name(current_owner) + current_owner_sid = salt.utils.win_functions.get_sid_from_name( + current_owner + ) expected_owner_sid = salt.utils.win_functions.get_sid_from_name(win_owner) if not current_owner_sid == expected_owner_sid: - changes['owner'] = win_owner + changes["owner"] = win_owner # Check perms perms = salt.utils.win_dacl.get_permissions(name) @@ -828,42 +848,46 @@ def _check_directory_win(name, grant_perms = [] # Check for permissions - if isinstance(win_perms[user]['perms'], six.string_types): + if isinstance(win_perms[user]["perms"], six.string_types): if not salt.utils.win_dacl.has_permission( - name, user, win_perms[user]['perms']): - grant_perms = win_perms[user]['perms'] + name, user, win_perms[user]["perms"] + ): + grant_perms = win_perms[user]["perms"] else: - for perm in win_perms[user]['perms']: + for perm in win_perms[user]["perms"]: if not salt.utils.win_dacl.has_permission( - name, user, perm, exact=False): - grant_perms.append(win_perms[user]['perms']) + name, user, perm, exact=False + ): + grant_perms.append(win_perms[user]["perms"]) if grant_perms: - if 'grant_perms' not in changes: - changes['grant_perms'] = {} - if user not in changes['grant_perms']: - changes['grant_perms'][user] = {} - changes['grant_perms'][user]['perms'] = grant_perms + if "grant_perms" not in changes: + changes["grant_perms"] = {} + if user not in changes["grant_perms"]: + changes["grant_perms"][user] = {} + changes["grant_perms"][user]["perms"] = grant_perms # Check Applies to - if 'applies_to' not in win_perms[user]: - applies_to = 'this_folder_subfolders_files' + if "applies_to" not in win_perms[user]: + applies_to = "this_folder_subfolders_files" else: - applies_to = win_perms[user]['applies_to'] + applies_to = win_perms[user]["applies_to"] if user in perms: user = salt.utils.win_dacl.get_name(user) # Get the proper applies_to text - at_flag = salt.utils.win_dacl.flags().ace_prop['file'][applies_to] - applies_to_text = salt.utils.win_dacl.flags().ace_prop['file'][at_flag] + at_flag = salt.utils.win_dacl.flags().ace_prop["file"][applies_to] + applies_to_text = salt.utils.win_dacl.flags().ace_prop["file"][ + at_flag + ] - if 'grant' in perms[user]: - if not perms[user]['grant']['applies to'] == applies_to_text: - if 'grant_perms' not in changes: - changes['grant_perms'] = {} - if user not in changes['grant_perms']: - changes['grant_perms'][user] = {} - changes['grant_perms'][user]['applies_to'] = applies_to + if "grant" in perms[user]: + if not perms[user]["grant"]["applies to"] == applies_to_text: + if "grant_perms" not in changes: + changes["grant_perms"] = {} + if user not in changes["grant_perms"]: + changes["grant_perms"][user] = {} + changes["grant_perms"][user]["applies_to"] = applies_to # Verify Deny Permissions if win_deny_perms is not None: @@ -876,123 +900,126 @@ def _check_directory_win(name, deny_perms = [] # Check for permissions - if isinstance(win_deny_perms[user]['perms'], six.string_types): + if isinstance(win_deny_perms[user]["perms"], six.string_types): if not salt.utils.win_dacl.has_permission( - name, user, win_deny_perms[user]['perms'], 'deny'): - deny_perms = win_deny_perms[user]['perms'] + name, user, win_deny_perms[user]["perms"], "deny" + ): + deny_perms = win_deny_perms[user]["perms"] else: - for perm in win_deny_perms[user]['perms']: + for perm in win_deny_perms[user]["perms"]: if not salt.utils.win_dacl.has_permission( - name, user, perm, 'deny', exact=False): - deny_perms.append(win_deny_perms[user]['perms']) + name, user, perm, "deny", exact=False + ): + deny_perms.append(win_deny_perms[user]["perms"]) if deny_perms: - if 'deny_perms' not in changes: - changes['deny_perms'] = {} - if user not in changes['deny_perms']: - changes['deny_perms'][user] = {} - changes['deny_perms'][user]['perms'] = deny_perms + if "deny_perms" not in changes: + changes["deny_perms"] = {} + if user not in changes["deny_perms"]: + changes["deny_perms"][user] = {} + changes["deny_perms"][user]["perms"] = deny_perms # Check Applies to - if 'applies_to' not in win_deny_perms[user]: - applies_to = 'this_folder_subfolders_files' + if "applies_to" not in win_deny_perms[user]: + applies_to = "this_folder_subfolders_files" else: - applies_to = win_deny_perms[user]['applies_to'] + applies_to = win_deny_perms[user]["applies_to"] if user in perms: user = salt.utils.win_dacl.get_name(user) # Get the proper applies_to text - at_flag = salt.utils.win_dacl.flags().ace_prop['file'][applies_to] - applies_to_text = salt.utils.win_dacl.flags().ace_prop['file'][at_flag] + at_flag = salt.utils.win_dacl.flags().ace_prop["file"][applies_to] + applies_to_text = salt.utils.win_dacl.flags().ace_prop["file"][ + at_flag + ] - if 'deny' in perms[user]: - if not perms[user]['deny']['applies to'] == applies_to_text: - if 'deny_perms' not in changes: - changes['deny_perms'] = {} - if user not in changes['deny_perms']: - changes['deny_perms'][user] = {} - changes['deny_perms'][user]['applies_to'] = applies_to + if "deny" in perms[user]: + if not perms[user]["deny"]["applies to"] == applies_to_text: + if "deny_perms" not in changes: + changes["deny_perms"] = {} + if user not in changes["deny_perms"]: + changes["deny_perms"][user] = {} + changes["deny_perms"][user]["applies_to"] = applies_to # Check inheritance if win_inheritance is not None: if not win_inheritance == salt.utils.win_dacl.get_inheritance(name): - changes['inheritance'] = win_inheritance + changes["inheritance"] = win_inheritance # Check reset if win_perms_reset: for user_name in perms: if user_name not in win_perms: - if 'grant' in perms[user_name] and not perms[user_name]['grant']['inherited']: - if 'remove_perms' not in changes: - changes['remove_perms'] = {} - changes['remove_perms'].update({user_name: perms[user_name]}) + if ( + "grant" in perms[user_name] + and not perms[user_name]["grant"]["inherited"] + ): + if "remove_perms" not in changes: + changes["remove_perms"] = {} + changes["remove_perms"].update({user_name: perms[user_name]}) if user_name not in win_deny_perms: - if 'deny' in perms[user_name] and not perms[user_name]['deny']['inherited']: - if 'remove_perms' not in changes: - changes['remove_perms'] = {} - changes['remove_perms'].update({user_name: perms[user_name]}) + if ( + "deny" in perms[user_name] + and not perms[user_name]["deny"]["inherited"] + ): + if "remove_perms" not in changes: + changes["remove_perms"] = {} + changes["remove_perms"].update({user_name: perms[user_name]}) if changes: return None, 'The directory "{0}" will be changed'.format(name), changes - return True, 'The directory {0} is in the correct state'.format(name), changes + return True, "The directory {0} is in the correct state".format(name), changes -def _check_dir_meta(name, - user, - group, - mode, - follow_symlinks=False): - ''' +def _check_dir_meta(name, user, group, mode, follow_symlinks=False): + """ Check the changes in directory metadata - ''' + """ try: - stats = __salt__['file.stats'](name, None, follow_symlinks) + stats = __salt__["file.stats"](name, None, follow_symlinks) except CommandExecutionError: stats = {} changes = {} if not stats: - changes['directory'] = 'new' + changes["directory"] = "new" return changes - if (user is not None - and user != stats['user'] - and user != stats.get('uid')): - changes['user'] = user - if (group is not None - and group != stats['group'] - and group != stats.get('gid')): - changes['group'] = group + if user is not None and user != stats["user"] and user != stats.get("uid"): + changes["user"] = user + if group is not None and group != stats["group"] and group != stats.get("gid"): + changes["group"] = group # Normalize the dir mode - smode = salt.utils.files.normalize_mode(stats['mode']) + smode = salt.utils.files.normalize_mode(stats["mode"]) mode = salt.utils.files.normalize_mode(mode) if mode is not None and mode != smode: - changes['mode'] = mode + changes["mode"] = mode return changes def _check_touch(name, atime, mtime): - ''' + """ Check to see if a file needs to be updated or created - ''' + """ ret = { - 'result': None, - 'comment': '', - 'changes': {'new': name}, + "result": None, + "comment": "", + "changes": {"new": name}, } if not os.path.exists(name): - ret['comment'] = 'File {0} is set to be created'.format(name) + ret["comment"] = "File {0} is set to be created".format(name) else: - stats = __salt__['file.stats'](name, follow_symlinks=False) - if ((atime is not None - and six.text_type(atime) != six.text_type(stats['atime'])) or - (mtime is not None - and six.text_type(mtime) != six.text_type(stats['mtime']))): - ret['comment'] = 'Times set to be updated on file {0}'.format(name) - ret['changes'] = {'touched': name} + stats = __salt__["file.stats"](name, follow_symlinks=False) + if ( + atime is not None and six.text_type(atime) != six.text_type(stats["atime"]) + ) or ( + mtime is not None and six.text_type(mtime) != six.text_type(stats["mtime"]) + ): + ret["comment"] = "Times set to be updated on file {0}".format(name) + ret["changes"] = {"touched": name} else: - ret['result'] = True - ret['comment'] = 'File {0} exists and has the correct times'.format(name) + ret["result"] = True + ret["comment"] = "File {0} exists and has the correct times".format(name) return ret @@ -1002,15 +1029,15 @@ def _get_symlink_ownership(path): return owner, owner else: return ( - __salt__['file.get_user'](path, follow_symlinks=False), - __salt__['file.get_group'](path, follow_symlinks=False) + __salt__["file.get_user"](path, follow_symlinks=False), + __salt__["file.get_group"](path, follow_symlinks=False), ) def _check_symlink_ownership(path, user, group, win_owner): - ''' + """ Check if the symlink ownership matches the specified user and group - ''' + """ cur_user, cur_group = _get_symlink_ownership(path) if salt.utils.platform.is_windows(): return win_owner == cur_user @@ -1019,10 +1046,10 @@ def _check_symlink_ownership(path, user, group, win_owner): def _set_symlink_ownership(path, user, group, win_owner): - ''' + """ Set the ownership of a symlink and return a boolean indicating success/failure - ''' + """ if salt.utils.platform.is_windows(): try: salt.utils.win_dacl.set_owner(path, win_owner) @@ -1030,134 +1057,149 @@ def _set_symlink_ownership(path, user, group, win_owner): pass else: try: - __salt__['file.lchown'](path, user, group) + __salt__["file.lchown"](path, user, group) except OSError: pass return _check_symlink_ownership(path, user, group, win_owner) def _symlink_check(name, target, force, user, group, win_owner): - ''' + """ Check the symlink function - ''' + """ changes = {} - if not os.path.exists(name) and not __salt__['file.is_link'](name): - changes['new'] = name - return None, 'Symlink {0} to {1} is set for creation'.format( - name, target - ), changes - if __salt__['file.is_link'](name): - if __salt__['file.readlink'](name) != target: - changes['change'] = name - return None, 'Link {0} target is set to be changed to {1}'.format( - name, target - ), changes + if not os.path.exists(name) and not __salt__["file.is_link"](name): + changes["new"] = name + return ( + None, + "Symlink {0} to {1} is set for creation".format(name, target), + changes, + ) + if __salt__["file.is_link"](name): + if __salt__["file.readlink"](name) != target: + changes["change"] = name + return ( + None, + "Link {0} target is set to be changed to {1}".format(name, target), + changes, + ) else: result = True - msg = 'The symlink {0} is present'.format(name) + msg = "The symlink {0} is present".format(name) if not _check_symlink_ownership(name, user, group, win_owner): result = None - changes['ownership'] = '{0}:{1}'.format(*_get_symlink_ownership(name)) + changes["ownership"] = "{0}:{1}".format(*_get_symlink_ownership(name)) msg += ( - ', but the ownership of the symlink would be changed ' - 'from {2}:{3} to {0}:{1}' + ", but the ownership of the symlink would be changed " + "from {2}:{3} to {0}:{1}" ).format(user, group, *_get_symlink_ownership(name)) return result, msg, changes else: if force: - return None, ('The file or directory {0} is set for removal to ' - 'make way for a new symlink targeting {1}' - .format(name, target)), changes - return False, ('File or directory exists where the symlink {0} ' - 'should be. Did you mean to use force?'.format(name)), changes + return ( + None, + ( + "The file or directory {0} is set for removal to " + "make way for a new symlink targeting {1}".format(name, target) + ), + changes, + ) + return ( + False, + ( + "File or directory exists where the symlink {0} " + "should be. Did you mean to use force?".format(name) + ), + changes, + ) def _hardlink_same(name, target): - ''' + """ Check to see if the inodes match for the name and the target - ''' - res = __salt__['file.stats'](name, None, follow_symlinks=False) - if 'inode' not in res: + """ + res = __salt__["file.stats"](name, None, follow_symlinks=False) + if "inode" not in res: return False - name_i = res['inode'] + name_i = res["inode"] - res = __salt__['file.stats'](target, None, follow_symlinks=False) - if 'inode' not in res: + res = __salt__["file.stats"](target, None, follow_symlinks=False) + if "inode" not in res: return False - target_i = res['inode'] + target_i = res["inode"] return name_i == target_i def _hardlink_check(name, target, force): - ''' + """ Check the hardlink function - ''' + """ changes = {} if not os.path.exists(target): - msg = 'Target {0} for hard link does not exist'.format(target) + msg = "Target {0} for hard link does not exist".format(target) return False, msg, changes elif os.path.isdir(target): - msg = 'Unable to hard link from directory {0}'.format(target) + msg = "Unable to hard link from directory {0}".format(target) return False, msg, changes if os.path.isdir(name): - msg = 'Unable to hard link to directory {0}'.format(name) + msg = "Unable to hard link to directory {0}".format(name) return False, msg, changes elif not os.path.exists(name): - msg = 'Hard link {0} to {1} is set for creation'.format(name, target) - changes['new'] = name + msg = "Hard link {0} to {1} is set for creation".format(name, target) + changes["new"] = name return None, msg, changes - elif __salt__['file.is_hardlink'](name): + elif __salt__["file.is_hardlink"](name): if _hardlink_same(name, target): - msg = 'The hard link {0} is presently targetting {1}'.format(name, target) + msg = "The hard link {0} is presently targetting {1}".format(name, target) return True, msg, changes - msg = 'Link {0} target is set to be changed to {1}'.format(name, target) - changes['change'] = name + msg = "Link {0} target is set to be changed to {1}".format(name, target) + changes["change"] = name return None, msg, changes if force: msg = ( - 'The file or directory {0} is set for removal to ' - 'make way for a new hard link targeting {1}'.format(name, target) + "The file or directory {0} is set for removal to " + "make way for a new hard link targeting {1}".format(name, target) ) return None, msg, changes msg = ( - 'File or directory exists where the hard link {0} ' - 'should be. Did you mean to use force?'.format(name) + "File or directory exists where the hard link {0} " + "should be. Did you mean to use force?".format(name) ) return False, msg, changes def _test_owner(kwargs, user=None): - ''' + """ Convert owner to user, since other config management tools use owner, no need to punish people coming from other systems. PLEASE DO NOT DOCUMENT THIS! WE USE USER, NOT OWNER!!!! - ''' + """ if user: return user - if 'owner' in kwargs: + if "owner" in kwargs: log.warning( - 'Use of argument owner found, "owner" is invalid, please ' - 'use "user"' + 'Use of argument owner found, "owner" is invalid, please ' 'use "user"' ) - return kwargs['owner'] + return kwargs["owner"] return user -def _unify_sources_and_hashes(source=None, source_hash=None, - sources=None, source_hashes=None): - ''' +def _unify_sources_and_hashes( + source=None, source_hash=None, sources=None, source_hashes=None +): + """ Silly little function to give us a standard tuple list for sources and source_hashes - ''' + """ if sources is None: sources = [] @@ -1165,39 +1207,36 @@ def _unify_sources_and_hashes(source=None, source_hash=None, source_hashes = [] if source and sources: - return (False, - "source and sources are mutually exclusive", []) + return (False, "source and sources are mutually exclusive", []) if source_hash and source_hashes: - return (False, - "source_hash and source_hashes are mutually exclusive", []) + return (False, "source_hash and source_hashes are mutually exclusive", []) if source: - return (True, '', [(source, source_hash)]) + return (True, "", [(source, source_hash)]) # Make a nice neat list of tuples exactly len(sources) long.. - return True, '', list(zip_longest(sources, source_hashes[:len(sources)])) + return True, "", list(zip_longest(sources, source_hashes[: len(sources)])) -def _get_template_texts(source_list=None, - template='jinja', - defaults=None, - context=None, - **kwargs): - ''' +def _get_template_texts( + source_list=None, template="jinja", defaults=None, context=None, **kwargs +): + """ Iterate a list of sources and process them as templates. Returns a list of 'chunks' containing the rendered templates. - ''' + """ - ret = {'name': '_get_template_texts', - 'changes': {}, - 'result': True, - 'comment': '', - 'data': []} + ret = { + "name": "_get_template_texts", + "changes": {}, + "result": True, + "comment": "", + "data": [], + } if source_list is None: - return _error(ret, - '_get_template_texts called with empty source_list') + return _error(ret, "_get_template_texts called with empty source_list") txtl = [] @@ -1206,42 +1245,37 @@ def _get_template_texts(source_list=None, tmpctx = defaults if defaults else {} if context: tmpctx.update(context) - rndrd_templ_fn = __salt__['cp.get_template']( - source, - '', - template=template, - saltenv=__env__, - context=tmpctx, - **kwargs + rndrd_templ_fn = __salt__["cp.get_template"]( + source, "", template=template, saltenv=__env__, context=tmpctx, **kwargs ) - msg = 'cp.get_template returned {0} (Called with: {1})' + msg = "cp.get_template returned {0} (Called with: {1})" log.debug(msg.format(rndrd_templ_fn, source)) if rndrd_templ_fn: tmplines = None - with salt.utils.files.fopen(rndrd_templ_fn, 'rb') as fp_: + with salt.utils.files.fopen(rndrd_templ_fn, "rb") as fp_: tmplines = fp_.read() tmplines = salt.utils.stringutils.to_unicode(tmplines) tmplines = tmplines.splitlines(True) if not tmplines: - msg = 'Failed to read rendered template file {0} ({1})' + msg = "Failed to read rendered template file {0} ({1})" log.debug(msg.format(rndrd_templ_fn, source)) - ret['name'] = source + ret["name"] = source return _error(ret, msg.format(rndrd_templ_fn, source)) - txtl.append(''.join(tmplines)) + txtl.append("".join(tmplines)) else: - msg = 'Failed to load template file {0}'.format(source) + msg = "Failed to load template file {0}".format(source) log.debug(msg) - ret['name'] = source + ret["name"] = source return _error(ret, msg) - ret['data'] = txtl + ret["data"] = txtl return ret def _validate_str_list(arg, encoding=None): - ''' + """ ensure ``arg`` is a list of strings - ''' + """ if isinstance(arg, six.binary_type): ret = [salt.utils.stringutils.to_unicode(arg, encoding=encoding)] elif isinstance(arg, six.string_types): @@ -1259,46 +1293,43 @@ def _validate_str_list(arg, encoding=None): def _get_shortcut_ownership(path): - return __salt__['file.get_user'](path, follow_symlinks=False) + return __salt__["file.get_user"](path, follow_symlinks=False) def _check_shortcut_ownership(path, user): - ''' + """ Check if the shortcut ownership matches the specified user - ''' + """ cur_user = _get_shortcut_ownership(path) return cur_user == user def _set_shortcut_ownership(path, user): - ''' + """ Set the ownership of a shortcut and return a boolean indicating success/failure - ''' + """ try: - __salt__['file.lchown'](path, user) + __salt__["file.lchown"](path, user) except OSError: pass return _check_shortcut_ownership(path, user) -def _shortcut_check(name, - target, - arguments, - working_dir, - description, - icon_location, - force, - user): - ''' +def _shortcut_check( + name, target, arguments, working_dir, description, icon_location, force, user +): + """ Check the shortcut function - ''' + """ changes = {} if not os.path.exists(name): - changes['new'] = name - return None, 'Shortcut "{0}" to "{1}" is set for creation'.format( - name, target - ), changes + changes["new"] = name + return ( + None, + 'Shortcut "{0}" to "{1}" is set for creation'.format(name, target), + changes, + ) if os.path.isfile(name): with salt.utils.winapi.Com(): @@ -1308,50 +1339,63 @@ def _shortcut_check(name, if arguments is not None: state_checks.append(scut.Arguments == arguments) if working_dir is not None: - state_checks.append( - scut.WorkingDirectory.lower() == working_dir.lower() - ) + state_checks.append(scut.WorkingDirectory.lower() == working_dir.lower()) if description is not None: state_checks.append(scut.Description == description) if icon_location is not None: - state_checks.append( - scut.IconLocation.lower() == icon_location.lower() - ) + state_checks.append(scut.IconLocation.lower() == icon_location.lower()) if not all(state_checks): - changes['change'] = name - return None, 'Shortcut "{0}" target is set to be changed to "{1}"'.format( - name, target - ), changes + changes["change"] = name + return ( + None, + 'Shortcut "{0}" target is set to be changed to "{1}"'.format( + name, target + ), + changes, + ) else: result = True msg = 'The shortcut "{0}" is present'.format(name) if not _check_shortcut_ownership(name, user): result = None - changes['ownership'] = '{0}'.format(_get_shortcut_ownership(name)) + changes["ownership"] = "{0}".format(_get_shortcut_ownership(name)) msg += ( - ', but the ownership of the shortcut would be changed ' - 'from {1} to {0}' + ", but the ownership of the shortcut would be changed " + "from {1} to {0}" ).format(user, _get_shortcut_ownership(name)) return result, msg, changes else: if force: - return None, ('The link or directory "{0}" is set for removal to ' - 'make way for a new shortcut targeting "{1}"' - .format(name, target)), changes - return False, ('Link or directory exists where the shortcut "{0}" ' - 'should be. Did you mean to use force?'.format(name)), changes + return ( + None, + ( + 'The link or directory "{0}" is set for removal to ' + 'make way for a new shortcut targeting "{1}"'.format(name, target) + ), + changes, + ) + return ( + False, + ( + 'Link or directory exists where the shortcut "{0}" ' + "should be. Did you mean to use force?".format(name) + ), + changes, + ) -def _makedirs(name, - user=None, - group=None, - dir_mode=None, - win_owner=None, - win_perms=None, - win_deny_perms=None, - win_inheritance=None): - ''' +def _makedirs( + name, + user=None, + group=None, + dir_mode=None, + win_owner=None, + win_perms=None, + win_deny_perms=None, + win_inheritance=None, +): + """ Helper function for creating directories when the ``makedirs`` option is set to ``True``. Handles Unix and Windows based systems @@ -1374,7 +1418,7 @@ def _makedirs(name, Raises: CommandExecutionError: If the drive is not mounted on Windows - ''' + """ if salt.utils.platform.is_windows(): # Make sure the drive is mapped before trying to create the # path in windows @@ -1382,28 +1426,30 @@ def _makedirs(name, if not os.path.isdir(drive): raise CommandExecutionError(drive) win_owner = win_owner if win_owner else user - return __salt__['file.makedirs'](path=name, - owner=win_owner, - grant_perms=win_perms, - deny_perms=win_deny_perms, - inheritance=win_inheritance) + return __salt__["file.makedirs"]( + path=name, + owner=win_owner, + grant_perms=win_perms, + deny_perms=win_deny_perms, + inheritance=win_inheritance, + ) else: - return __salt__['file.makedirs'](path=name, - user=user, - group=group, - mode=dir_mode) + return __salt__["file.makedirs"]( + path=name, user=user, group=group, mode=dir_mode + ) def hardlink( - name, - target, - force=False, - makedirs=False, - user=None, - group=None, - dir_mode=None, - **kwargs): - ''' + name, + target, + force=False, + makedirs=False, + user=None, + group=None, + dir_mode=None, + **kwargs +): + """ Create a hard link If the file already exists and is a hard link pointing to any location other than the specified target, the hard link will be replaced. If the hard link @@ -1433,123 +1479,116 @@ def hardlink( dir_mode If directories are to be created, passing this option specifies the permissions for those directories. - ''' + """ name = os.path.expanduser(name) # Make sure that leading zeros stripped by YAML loader are added back dir_mode = salt.utils.files.normalize_mode(dir_mode) user = _test_owner(kwargs, user=user) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.hardlink') + return _error(ret, "Must provide name to file.hardlink") if user is None: - user = __opts__['user'] + user = __opts__["user"] if salt.utils.platform.is_windows(): if group is not None: log.warning( - 'The group argument for {0} has been ignored as this ' - 'is a Windows system.'.format(name) + "The group argument for {0} has been ignored as this " + "is a Windows system.".format(name) ) group = user if group is None: - group = __salt__['file.gid_to_group']( - __salt__['user.info'](user).get('gid', 0) - ) + group = __salt__["file.gid_to_group"](__salt__["user.info"](user).get("gid", 0)) preflight_errors = [] - uid = __salt__['file.user_to_uid'](user) - gid = __salt__['file.group_to_gid'](group) + uid = __salt__["file.user_to_uid"](user) + gid = __salt__["file.group_to_gid"](group) - if uid == '': - preflight_errors.append('User {0} does not exist'.format(user)) + if uid == "": + preflight_errors.append("User {0} does not exist".format(user)) - if gid == '': - preflight_errors.append('Group {0} does not exist'.format(group)) + if gid == "": + preflight_errors.append("Group {0} does not exist".format(group)) if not os.path.isabs(name): preflight_errors.append( - 'Specified file {0} is not an absolute path'.format(name) + "Specified file {0} is not an absolute path".format(name) ) if not os.path.isabs(target): preflight_errors.append( - 'Specified target {0} is not an absolute path'.format(target) + "Specified target {0} is not an absolute path".format(target) ) if preflight_errors: - msg = '. '.join(preflight_errors) + msg = ". ".join(preflight_errors) if len(preflight_errors) > 1: - msg += '.' + msg += "." return _error(ret, msg) - if __opts__['test']: + if __opts__["test"]: presult, pcomment, pchanges = _hardlink_check(name, target, force) - ret['result'] = presult - ret['comment'] = pcomment - ret['changes'] = pchanges + ret["result"] = presult + ret["comment"] = pcomment + ret["changes"] = pchanges return ret # We use zip_longest here because there's a number of issues in pylint's # tracker that complains about not linking the zip builtin. - for direction, item in zip_longest(['to', 'from'], [name, target]): + for direction, item in zip_longest(["to", "from"], [name, target]): if os.path.isdir(item): - msg = 'Unable to hard link {0} directory {1}'.format(direction, item) + msg = "Unable to hard link {0} directory {1}".format(direction, item) return _error(ret, msg) if not os.path.exists(target): - msg = 'Target {0} for hard link does not exist'.format(target) + msg = "Target {0} for hard link does not exist".format(target) return _error(ret, msg) # Check that the directory to write the hard link to exists if not os.path.isdir(os.path.dirname(name)): if makedirs: - __salt__['file.makedirs']( - name, - user=user, - group=group, - mode=dir_mode) + __salt__["file.makedirs"](name, user=user, group=group, mode=dir_mode) else: return _error( ret, - 'Directory {0} for hard link is not present'.format( + "Directory {0} for hard link is not present".format( os.path.dirname(name) - ) + ), ) # If file is not a hard link and we're actually overwriting it, then verify # that this was forced. - if os.path.isfile(name) and not __salt__['file.is_hardlink'](name): + if os.path.isfile(name) and not __salt__["file.is_hardlink"](name): # Remove whatever is in the way. This should then hit the else case # of the file.is_hardlink check below if force: os.remove(name) - ret['changes']['forced'] = 'File for hard link was forcibly replaced' + ret["changes"]["forced"] = "File for hard link was forcibly replaced" # Otherwise throw an error else: - return _error(ret, - ('File exists where the hard link {0} should be' - .format(name))) + return _error( + ret, ("File exists where the hard link {0} should be".format(name)) + ) # If the file is a hard link, then we can simply rewrite its target since # nothing is really being lost here. - if __salt__['file.is_hardlink'](name): + if __salt__["file.is_hardlink"](name): # If the inodes point to the same thing, then there's nothing to do # except for let the user know that this has already happened. if _hardlink_same(name, target): - ret['result'] = True - ret['comment'] = ('Target of hard link {0} is already pointing ' - 'to {1}'.format(name, target)) + ret["result"] = True + ret["comment"] = ( + "Target of hard link {0} is already pointing " + "to {1}".format(name, target) + ) return ret # First remove the old hard link since a reference to it already exists @@ -1557,55 +1596,59 @@ def hardlink( # Now we can remake it try: - __salt__['file.link'](target, name) + __salt__["file.link"](target, name) # Or not... except CommandExecutionError as E: - ret['result'] = False - ret['comment'] = ('Unable to set target of hard link {0} -> ' - '{1}: {2}'.format(name, target, E)) + ret["result"] = False + ret["comment"] = ( + "Unable to set target of hard link {0} -> " + "{1}: {2}".format(name, target, E) + ) return ret # Good to go - ret['result'] = True - ret['comment'] = 'Set target of hard link {0} -> {1}'.format(name, target) - ret['changes']['new'] = name + ret["result"] = True + ret["comment"] = "Set target of hard link {0} -> {1}".format(name, target) + ret["changes"]["new"] = name # The link is not present, so simply make it elif not os.path.exists(name): try: - __salt__['file.link'](target, name) + __salt__["file.link"](target, name) # Or not... except CommandExecutionError as E: - ret['result'] = False - ret['comment'] = ('Unable to create new hard link {0} -> ' - '{1}: {2}'.format(name, target, E)) + ret["result"] = False + ret["comment"] = "Unable to create new hard link {0} -> " "{1}: {2}".format( + name, target, E + ) return ret # Made a new hard link, things are ok - ret['result'] = True - ret['comment'] = 'Created new hard link {0} -> {1}'.format(name, target) - ret['changes']['new'] = name + ret["result"] = True + ret["comment"] = "Created new hard link {0} -> {1}".format(name, target) + ret["changes"]["new"] = name return ret def symlink( - name, - target, - force=False, - backupname=None, - makedirs=False, - user=None, - group=None, - mode=None, - win_owner=None, - win_perms=None, - win_deny_perms=None, - win_inheritance=None, - **kwargs): - ''' + name, + target, + force=False, + backupname=None, + makedirs=False, + user=None, + group=None, + mode=None, + win_owner=None, + win_perms=None, + win_deny_perms=None, + win_inheritance=None, + **kwargs +): + """ Create a symbolic link (symlink, soft link) If the file already exists and is a symlink pointing to any location other @@ -1681,33 +1724,30 @@ def symlink( True to inherit permissions from parent, otherwise False .. versionadded:: 2017.7.7 - ''' + """ name = os.path.expanduser(name) # Make sure that leading zeros stripped by YAML loader are added back mode = salt.utils.files.normalize_mode(mode) user = _test_owner(kwargs, user=user) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.symlink') + return _error(ret, "Must provide name to file.symlink") if user is None: - user = __opts__['user'] + user = __opts__["user"] if salt.utils.platform.is_windows(): # Make sure the user exists in Windows # Salt default is 'root' - if not __salt__['user.info'](user): + if not __salt__["user.info"](user): # User not found, use the account salt is running under # If username not found, use System - user = __salt__['user.current']() + user = __salt__["user.current"]() if not user: - user = 'SYSTEM' + user = "SYSTEM" # If win_owner is not passed, use user if win_owner is None: @@ -1716,16 +1756,14 @@ def symlink( # Group isn't relevant to Windows, use win_perms/win_deny_perms if group is not None: log.warning( - 'The group argument for {0} has been ignored as this ' - 'is a Windows system. Please use the `win_*` parameters to set ' - 'permissions in Windows.'.format(name) + "The group argument for {0} has been ignored as this " + "is a Windows system. Please use the `win_*` parameters to set " + "permissions in Windows.".format(name) ) group = user if group is None: - group = __salt__['file.gid_to_group']( - __salt__['user.info'](user).get('gid', 0) - ) + group = __salt__["file.gid_to_group"](__salt__["user.info"](user).get("gid", 0)) preflight_errors = [] if salt.utils.platform.is_windows(): @@ -1733,7 +1771,7 @@ def symlink( try: salt.utils.win_functions.get_sid_from_name(win_owner) except CommandExecutionError as exc: - preflight_errors.append('User {0} does not exist'.format(win_owner)) + preflight_errors.append("User {0} does not exist".format(win_owner)) # Make sure users passed in win_perms exist if win_perms: @@ -1741,7 +1779,9 @@ def symlink( try: salt.utils.win_functions.get_sid_from_name(name_check) except CommandExecutionError as exc: - preflight_errors.append('User {0} does not exist'.format(name_check)) + preflight_errors.append( + "User {0} does not exist".format(name_check) + ) # Make sure users passed in win_deny_perms exist if win_deny_perms: @@ -1749,104 +1789,117 @@ def symlink( try: salt.utils.win_functions.get_sid_from_name(name_check) except CommandExecutionError as exc: - preflight_errors.append('User {0} does not exist'.format(name_check)) + preflight_errors.append( + "User {0} does not exist".format(name_check) + ) else: - uid = __salt__['file.user_to_uid'](user) - gid = __salt__['file.group_to_gid'](group) + uid = __salt__["file.user_to_uid"](user) + gid = __salt__["file.group_to_gid"](group) - if uid == '': - preflight_errors.append('User {0} does not exist'.format(user)) + if uid == "": + preflight_errors.append("User {0} does not exist".format(user)) - if gid == '': - preflight_errors.append('Group {0} does not exist'.format(group)) + if gid == "": + preflight_errors.append("Group {0} does not exist".format(group)) if not os.path.isabs(name): preflight_errors.append( - 'Specified file {0} is not an absolute path'.format(name) + "Specified file {0} is not an absolute path".format(name) ) if preflight_errors: - msg = '. '.join(preflight_errors) + msg = ". ".join(preflight_errors) if len(preflight_errors) > 1: - msg += '.' + msg += "." return _error(ret, msg) - presult, pcomment, pchanges = _symlink_check(name, - target, - force, - user, - group, - win_owner) + presult, pcomment, pchanges = _symlink_check( + name, target, force, user, group, win_owner + ) if not os.path.isdir(os.path.dirname(name)): if makedirs: - if __opts__['test']: - pcomment += '\n{0} will be created'.format(os.path.dirname(name)) + if __opts__["test"]: + pcomment += "\n{0} will be created".format(os.path.dirname(name)) else: try: - _makedirs(name=name, - user=user, - group=group, - dir_mode=mode, - win_owner=win_owner, - win_perms=win_perms, - win_deny_perms=win_deny_perms, - win_inheritance=win_inheritance) + _makedirs( + name=name, + user=user, + group=group, + dir_mode=mode, + win_owner=win_owner, + win_perms=win_perms, + win_deny_perms=win_deny_perms, + win_inheritance=win_inheritance, + ) except CommandExecutionError as exc: - return _error(ret, 'Drive {0} is not mapped'.format(exc.message)) + return _error(ret, "Drive {0} is not mapped".format(exc.message)) else: - if __opts__['test']: - pcomment += '\nDirectory {0} for symlink is not present' \ - ''.format(os.path.dirname(name)) + if __opts__["test"]: + pcomment += "\nDirectory {0} for symlink is not present" "".format( + os.path.dirname(name) + ) else: return _error( ret, - 'Directory {0} for symlink is not present'.format( + "Directory {0} for symlink is not present".format( os.path.dirname(name) - ) + ), ) - if __opts__['test']: - ret['result'] = presult - ret['comment'] = pcomment - ret['changes'] = pchanges + if __opts__["test"]: + ret["result"] = presult + ret["comment"] = pcomment + ret["changes"] = pchanges return ret - if __salt__['file.is_link'](name): + if __salt__["file.is_link"](name): # The link exists, verify that it matches the target - if os.path.normpath(__salt__['file.readlink'](name)) != os.path.normpath(target): + if os.path.normpath(__salt__["file.readlink"](name)) != os.path.normpath( + target + ): # The target is wrong, delete the link os.remove(name) else: if _check_symlink_ownership(name, user, group, win_owner): # The link looks good! if salt.utils.platform.is_windows(): - ret['comment'] = ('Symlink {0} is present and owned by {1}' - ''.format(name, win_owner)) + ret["comment"] = ( + "Symlink {0} is present and owned by {1}" + "".format(name, win_owner) + ) else: - ret['comment'] = ('Symlink {0} is present and owned by ' - '{1}:{2}'.format(name, user, group)) + ret["comment"] = ( + "Symlink {0} is present and owned by " + "{1}:{2}".format(name, user, group) + ) else: if _set_symlink_ownership(name, user, group, win_owner): if salt.utils.platform.is_windows(): - ret['comment'] = ('Set ownership of symlink {0} to ' - '{1}'.format(name, win_owner)) - ret['changes']['ownership'] = win_owner + ret["comment"] = ( + "Set ownership of symlink {0} to " + "{1}".format(name, win_owner) + ) + ret["changes"]["ownership"] = win_owner else: - ret['comment'] = ('Set ownership of symlink {0} to ' - '{1}:{2}'.format(name, user, group)) - ret['changes']['ownership'] = '{0}:{1}'.format(user, - group) + ret["comment"] = ( + "Set ownership of symlink {0} to " + "{1}:{2}".format(name, user, group) + ) + ret["changes"]["ownership"] = "{0}:{1}".format(user, group) else: - ret['result'] = False + ret["result"] = False if salt.utils.platform.is_windows(): - ret['comment'] += ( - 'Failed to set ownership of symlink ' - '{0} to {1}'.format(name, win_owner)) + ret["comment"] += ( + "Failed to set ownership of symlink " + "{0} to {1}".format(name, win_owner) + ) else: - ret['comment'] += ( - 'Failed to set ownership of symlink {0} to ' - '{1}:{2}'.format(name, user, group)) + ret["comment"] += ( + "Failed to set ownership of symlink {0} to " + "{1}:{2}".format(name, user, group) + ) return ret elif os.path.exists(name): @@ -1855,136 +1908,156 @@ def symlink( if not os.path.isabs(backupname): if backupname == os.path.basename(backupname): backupname = os.path.join( - os.path.dirname(os.path.normpath(name)), - backupname) + os.path.dirname(os.path.normpath(name)), backupname + ) else: - return _error(ret, (('Backupname must be an absolute path ' - 'or a file name: {0}').format(backupname))) + return _error( + ret, + ( + ( + "Backupname must be an absolute path " + "or a file name: {0}" + ).format(backupname) + ), + ) # Make a backup first if os.path.lexists(backupname): if not force: - return _error(ret, (('Symlink & backup dest exists and Force not set.' - ' {0} -> {1} - backup: {2}').format( - name, target, backupname))) + return _error( + ret, + ( + ( + "Symlink & backup dest exists and Force not set." + " {0} -> {1} - backup: {2}" + ).format(name, target, backupname) + ), + ) else: - __salt__['file.remove'](backupname) + __salt__["file.remove"](backupname) try: - __salt__['file.move'](name, backupname) + __salt__["file.move"](name, backupname) except Exception as exc: # pylint: disable=broad-except - ret['changes'] = {} + ret["changes"] = {} log.debug( - 'Encountered error renaming %s to %s', - name, backupname, exc_info=True + "Encountered error renaming %s to %s", + name, + backupname, + exc_info=True, + ) + return _error( + ret, + ( + "Unable to rename {0} to backup {1} -> " + ": {2}".format(name, backupname, exc) + ), ) - return _error(ret, ('Unable to rename {0} to backup {1} -> ' - ': {2}'.format(name, backupname, exc))) elif force: # Remove whatever is in the way - if __salt__['file.is_link'](name): - __salt__['file.remove'](name) - ret['changes']['forced'] = 'Symlink was forcibly replaced' + if __salt__["file.is_link"](name): + __salt__["file.remove"](name) + ret["changes"]["forced"] = "Symlink was forcibly replaced" else: - __salt__['file.remove'](name) + __salt__["file.remove"](name) else: # Otherwise throw an error - fs_entry_type = 'File' if os.path.isfile(name) else \ - 'Directory' if os.path.isdir(name) else \ - 'File system entry' - return _error(ret, - ('{0} exists where the symlink {1} should be' - .format(fs_entry_type, name))) + fs_entry_type = ( + "File" + if os.path.isfile(name) + else "Directory" + if os.path.isdir(name) + else "File system entry" + ) + return _error( + ret, + ( + "{0} exists where the symlink {1} should be".format( + fs_entry_type, name + ) + ), + ) if not os.path.exists(name): # The link is not present, make it try: - __salt__['file.symlink'](target, name) + __salt__["file.symlink"](target, name) except OSError as exc: - ret['result'] = False - ret['comment'] = ('Unable to create new symlink {0} -> ' - '{1}: {2}'.format(name, target, exc)) + ret["result"] = False + ret["comment"] = "Unable to create new symlink {0} -> " "{1}: {2}".format( + name, target, exc + ) return ret else: - ret['comment'] = ('Created new symlink {0} -> ' - '{1}'.format(name, target)) - ret['changes']['new'] = name + ret["comment"] = "Created new symlink {0} -> " "{1}".format(name, target) + ret["changes"]["new"] = name if not _check_symlink_ownership(name, user, group, win_owner): if not _set_symlink_ownership(name, user, group, win_owner): - ret['result'] = False - ret['comment'] += (', but was unable to set ownership to ' - '{0}:{1}'.format(user, group)) + ret["result"] = False + ret["comment"] += ( + ", but was unable to set ownership to " + "{0}:{1}".format(user, group) + ) return ret -def absent(name, - **kwargs): - ''' +def absent(name, **kwargs): + """ Make sure that the named file or directory is absent. If it exists, it will be deleted. This will work to reverse any of the functions in the file state module. If a directory is supplied, it will be recursively deleted. name The path which should be deleted - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.absent') + return _error(ret, "Must provide name to file.absent") if not os.path.isabs(name): - return _error( - ret, 'Specified file {0} is not an absolute path'.format(name) - ) - if name == '/': + return _error(ret, "Specified file {0} is not an absolute path".format(name)) + if name == "/": return _error(ret, 'Refusing to make "/" absent') if os.path.isfile(name) or os.path.islink(name): - if __opts__['test']: - ret['result'] = None - ret['changes']['removed'] = name - ret['comment'] = 'File {0} is set for removal'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["changes"]["removed"] = name + ret["comment"] = "File {0} is set for removal".format(name) return ret try: if salt.utils.platform.is_windows(): - __salt__['file.remove'](name, force=True) + __salt__["file.remove"](name, force=True) else: - __salt__['file.remove'](name) - ret['comment'] = 'Removed file {0}'.format(name) - ret['changes']['removed'] = name + __salt__["file.remove"](name) + ret["comment"] = "Removed file {0}".format(name) + ret["changes"]["removed"] = name return ret except CommandExecutionError as exc: - return _error(ret, '{0}'.format(exc)) + return _error(ret, "{0}".format(exc)) elif os.path.isdir(name): - if __opts__['test']: - ret['result'] = None - ret['changes']['removed'] = name - ret['comment'] = 'Directory {0} is set for removal'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["changes"]["removed"] = name + ret["comment"] = "Directory {0} is set for removal".format(name) return ret try: if salt.utils.platform.is_windows(): - __salt__['file.remove'](name, force=True) + __salt__["file.remove"](name, force=True) else: - __salt__['file.remove'](name) - ret['comment'] = 'Removed directory {0}'.format(name) - ret['changes']['removed'] = name + __salt__["file.remove"](name) + ret["comment"] = "Removed directory {0}".format(name) + ret["changes"]["removed"] = name return ret except (OSError, IOError): - return _error(ret, 'Failed to remove directory {0}'.format(name)) + return _error(ret, "Failed to remove directory {0}".format(name)) - ret['comment'] = 'File {0} is not present'.format(name) + ret["comment"] = "File {0} is not present".format(name) return ret -def tidied(name, - age=0, - matches=None, - rmdirs=False, - size=0, - **kwargs): - ''' +def tidied(name, age=0, matches=None, rmdirs=False, size=0, **kwargs): + """ Remove unwanted files based on specific criteria. Multiple criteria are OR’d together, so a file that is too large but is not old enough will still get tidied. @@ -2017,20 +2090,16 @@ def tidied(name, - matches: - foo - b.*r - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'pchanges': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "pchanges": {}, "result": True, "comment": ""} # Check preconditions if not os.path.isabs(name): - return _error(ret, 'Specified file {0} is not an absolute path'.format(name)) + return _error(ret, "Specified file {0} is not an absolute path".format(name)) if not os.path.isdir(name): - return _error(ret, '{0} does not exist or is not a directory.'.format(name)) + return _error(ret, "{0} does not exist or is not a directory.".format(name)) # Define some variables todelete = [] @@ -2038,7 +2107,7 @@ def tidied(name, # Compile regular expressions if matches is None: - matches = ['.*'] + matches = [".*"] progs = [] for regex in matches: progs.append(re.compile(regex)) @@ -2071,39 +2140,44 @@ def tidied(name, myage = abs(today - date.fromtimestamp(os.path.getatime(path))) mysize = os.path.getsize(path) # Verify against given criteria, collect all elements that should be removed - if (mysize >= size or myage.days >= age) and _matches(name=elem) and deleteme: + if ( + (mysize >= size or myage.days >= age) + and _matches(name=elem) + and deleteme + ): todelete.append(path) # Now delete the stuff if todelete: - if __opts__['test']: - ret['result'] = None - ret['comment'] = '{0} is set for tidy'.format(name) - ret['changes'] = {'removed': todelete} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "{0} is set for tidy".format(name) + ret["changes"] = {"removed": todelete} return ret - ret['changes']['removed'] = [] + ret["changes"]["removed"] = [] # Iterate over collected items try: for path in todelete: if salt.utils.platform.is_windows(): - __salt__['file.remove'](path, force=True) + __salt__["file.remove"](path, force=True) else: - __salt__['file.remove'](path) + __salt__["file.remove"](path) # Remember what we've removed, will appear in the summary - ret['changes']['removed'].append(path) + ret["changes"]["removed"].append(path) except CommandExecutionError as exc: - return _error(ret, '{0}'.format(exc)) + return _error(ret, "{0}".format(exc)) # Set comment for the summary - ret['comment'] = 'Removed {0} files or directories from directory {1}'.format(len(todelete), name) + ret["comment"] = "Removed {0} files or directories from directory {1}".format( + len(todelete), name + ) else: # Set comment in case there was nothing to remove - ret['comment'] = 'Nothing to remove from directory {0}'.format(name) + ret["comment"] = "Nothing to remove from directory {0}".format(name) return ret -def exists(name, - **kwargs): - ''' +def exists(name, **kwargs): + """ Verify that the named file or directory is present or exists. Ensures pre-requisites outside of Salt's purview (e.g., keytabs, private keys, etc.) have been previously satisfied before @@ -2114,84 +2188,79 @@ def exists(name, name Absolute path which must exist - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.exists') + return _error(ret, "Must provide name to file.exists") if not os.path.exists(name): - return _error(ret, 'Specified path {0} does not exist'.format(name)) + return _error(ret, "Specified path {0} does not exist".format(name)) - ret['comment'] = 'Path {0} exists'.format(name) + ret["comment"] = "Path {0} exists".format(name) return ret -def missing(name, - **kwargs): - ''' +def missing(name, **kwargs): + """ Verify that the named file or directory is missing, this returns True only if the named file is missing but does not remove the file if it is present. name Absolute path which must NOT exist - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.missing') + return _error(ret, "Must provide name to file.missing") if os.path.exists(name): - return _error(ret, 'Specified path {0} exists'.format(name)) + return _error(ret, "Specified path {0} exists".format(name)) - ret['comment'] = 'Path {0} is missing'.format(name) + ret["comment"] = "Path {0} is missing".format(name) return ret -def managed(name, - source=None, - source_hash='', - source_hash_name=None, - keep_source=True, - user=None, - group=None, - mode=None, - attrs=None, - template=None, - makedirs=False, - dir_mode=None, - context=None, - replace=True, - defaults=None, - backup='', - show_changes=True, - create=True, - contents=None, - tmp_dir='', - tmp_ext='', - contents_pillar=None, - contents_grains=None, - contents_newline=True, - contents_delimiter=':', - encoding=None, - encoding_errors='strict', - allow_empty=True, - follow_symlinks=True, - check_cmd=None, - skip_verify=False, - win_owner=None, - win_perms=None, - win_deny_perms=None, - win_inheritance=True, - win_perms_reset=False, - **kwargs): - r''' +def managed( + name, + source=None, + source_hash="", + source_hash_name=None, + keep_source=True, + user=None, + group=None, + mode=None, + attrs=None, + template=None, + makedirs=False, + dir_mode=None, + context=None, + replace=True, + defaults=None, + backup="", + show_changes=True, + create=True, + contents=None, + tmp_dir="", + tmp_ext="", + contents_pillar=None, + contents_grains=None, + contents_newline=True, + contents_delimiter=":", + encoding=None, + encoding_errors="strict", + allow_empty=True, + follow_symlinks=True, + check_cmd=None, + skip_verify=False, + win_owner=None, + win_perms=None, + win_deny_perms=None, + win_inheritance=True, + win_perms_reset=False, + **kwargs +): + r""" Manage a given file, this function allows for a file to be downloaded from the salt master and potentially run through a templating system. @@ -2747,29 +2816,26 @@ def managed(name, fred_snuffy: perms: full_control - win_inheritance: False - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") name = os.path.expanduser(name) - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + ret = {"changes": {}, "comment": "", "name": name, "result": True} if not name: - return _error(ret, 'Destination file name is required') + return _error(ret, "Destination file name is required") if mode is not None and salt.utils.platform.is_windows(): - return _error(ret, 'The \'mode\' option is not supported on Windows') + return _error(ret, "The 'mode' option is not supported on Windows") if attrs is not None and salt.utils.platform.is_windows(): - return _error(ret, 'The \'attrs\' option is not supported on Windows') + return _error(ret, "The 'attrs' option is not supported on Windows") try: - keep_mode = mode.lower() == 'keep' + keep_mode = mode.lower() == "keep" if keep_mode: # We're not hard-coding the mode, so set it to None mode = None @@ -2780,27 +2846,26 @@ def managed(name, mode = salt.utils.files.normalize_mode(mode) contents_count = len( - [x for x in (contents, contents_pillar, contents_grains) - if x is not None] + [x for x in (contents, contents_pillar, contents_grains) if x is not None] ) if source and contents_count > 0: return _error( ret, - '\'source\' cannot be used in combination with \'contents\', ' - '\'contents_pillar\', or \'contents_grains\'' + "'source' cannot be used in combination with 'contents', " + "'contents_pillar', or 'contents_grains'", ) elif keep_mode and contents_count > 0: return _error( ret, - 'Mode preservation cannot be used in combination with \'contents\', ' - '\'contents_pillar\', or \'contents_grains\'' + "Mode preservation cannot be used in combination with 'contents', " + "'contents_pillar', or 'contents_grains'", ) elif contents_count > 1: return _error( ret, - 'Only one of \'contents\', \'contents_pillar\', and ' - '\'contents_grains\' is permitted' + "Only one of 'contents', 'contents_pillar', and " + "'contents_grains' is permitted", ) # If no source is specified, set replace to False, as there is nothing @@ -2808,17 +2873,17 @@ def managed(name, if not source and contents_count == 0 and replace: replace = False log.warning( - 'State for file: {0} - Neither \'source\' nor \'contents\' nor ' - '\'contents_pillar\' nor \'contents_grains\' was defined, yet ' - '\'replace\' was set to \'True\'. As there is no source to ' - 'replace the file with, \'replace\' has been set to \'False\' to ' - 'avoid reading the file unnecessarily.'.format(name) + "State for file: {0} - Neither 'source' nor 'contents' nor " + "'contents_pillar' nor 'contents_grains' was defined, yet " + "'replace' was set to 'True'. As there is no source to " + "replace the file with, 'replace' has been set to 'False' to " + "avoid reading the file unnecessarily.".format(name) ) - if 'file_mode' in kwargs: - ret.setdefault('warnings', []).append( - 'The \'file_mode\' argument will be ignored. ' - 'Please use \'mode\' instead to set file permissions.' + if "file_mode" in kwargs: + ret.setdefault("warnings", []).append( + "The 'file_mode' argument will be ignored. " + "Please use 'mode' instead to set file permissions." ) # Use this below to avoid multiple '\0' checks and save some CPU cycles @@ -2826,45 +2891,37 @@ def managed(name, if isinstance(contents_pillar, list): list_contents = [] for nextp in contents_pillar: - nextc = __salt__['pillar.get'](nextp, __NOT_FOUND, - delimiter=contents_delimiter) + nextc = __salt__["pillar.get"]( + nextp, __NOT_FOUND, delimiter=contents_delimiter + ) if nextc is __NOT_FOUND: - return _error( - ret, - 'Pillar {0} does not exist'.format(nextp) - ) + return _error(ret, "Pillar {0} does not exist".format(nextp)) list_contents.append(nextc) use_contents = os.linesep.join(list_contents) else: - use_contents = __salt__['pillar.get'](contents_pillar, __NOT_FOUND, - delimiter=contents_delimiter) + use_contents = __salt__["pillar.get"]( + contents_pillar, __NOT_FOUND, delimiter=contents_delimiter + ) if use_contents is __NOT_FOUND: - return _error( - ret, - 'Pillar {0} does not exist'.format(contents_pillar) - ) + return _error(ret, "Pillar {0} does not exist".format(contents_pillar)) elif contents_grains is not None: if isinstance(contents_grains, list): list_contents = [] for nextg in contents_grains: - nextc = __salt__['grains.get'](nextg, __NOT_FOUND, - delimiter=contents_delimiter) + nextc = __salt__["grains.get"]( + nextg, __NOT_FOUND, delimiter=contents_delimiter + ) if nextc is __NOT_FOUND: - return _error( - ret, - 'Grain {0} does not exist'.format(nextc) - ) + return _error(ret, "Grain {0} does not exist".format(nextc)) list_contents.append(nextc) use_contents = os.linesep.join(list_contents) else: - use_contents = __salt__['grains.get'](contents_grains, __NOT_FOUND, - delimiter=contents_delimiter) + use_contents = __salt__["grains.get"]( + contents_grains, __NOT_FOUND, delimiter=contents_delimiter + ) if use_contents is __NOT_FOUND: - return _error( - ret, - 'Grain {0} does not exist'.format(contents_grains) - ) + return _error(ret, "Grain {0} does not exist".format(contents_grains)) elif contents is not None: use_contents = contents @@ -2875,16 +2932,15 @@ def managed(name, if use_contents is not None: if not allow_empty and not use_contents: if contents_pillar: - contents_id = 'contents_pillar {0}'.format(contents_pillar) + contents_id = "contents_pillar {0}".format(contents_pillar) elif contents_grains: - contents_id = 'contents_grains {0}'.format(contents_grains) + contents_id = "contents_grains {0}".format(contents_grains) else: - contents_id = '\'contents\'' + contents_id = "'contents'" return _error( ret, - '{0} value would result in empty contents. Set allow_empty ' - 'to True to allow the managed file to be empty.' - .format(contents_id) + "{0} value would result in empty contents. Set allow_empty " + "to True to allow the managed file to be empty.".format(contents_id), ) try: @@ -2892,46 +2948,47 @@ def managed(name, if not validated_contents: return _error( ret, - 'Contents specified by contents/contents_pillar/' - 'contents_grains is not a string or list of strings, and ' - 'is not binary data. SLS is likely malformed.' + "Contents specified by contents/contents_pillar/" + "contents_grains is not a string or list of strings, and " + "is not binary data. SLS is likely malformed.", ) - contents = '' + contents = "" for part in validated_contents: for line in part.splitlines(): - contents += line.rstrip('\n').rstrip('\r') + os.linesep + contents += line.rstrip("\n").rstrip("\r") + os.linesep if not contents_newline: # If contents newline is set to False, strip out the newline # character and carriage return character - contents = contents.rstrip('\n').rstrip('\r') + contents = contents.rstrip("\n").rstrip("\r") except UnicodeDecodeError: # Either something terrible happened, or we have binary data. if template: return _error( ret, - 'Contents specified by contents/contents_pillar/' - 'contents_grains appears to be binary data, and' - ' as will not be able to be treated as a Jinja' - ' template.' + "Contents specified by contents/contents_pillar/" + "contents_grains appears to be binary data, and" + " as will not be able to be treated as a Jinja" + " template.", ) contents = use_contents if template: - contents = __salt__['file.apply_template_on_contents']( + contents = __salt__["file.apply_template_on_contents"]( contents, template=template, context=context, defaults=defaults, - saltenv=__env__) + saltenv=__env__, + ) if not isinstance(contents, six.string_types): - if 'result' in contents: - ret['result'] = contents['result'] + if "result" in contents: + ret["result"] = contents["result"] else: - ret['result'] = False - if 'comment' in contents: - ret['comment'] = contents['comment'] + ret["result"] = False + if "comment" in contents: + ret["comment"] = contents["comment"] else: - ret['comment'] = 'Error while applying template on contents' + ret["comment"] = "Error while applying template on contents" return ret user = _test_owner(kwargs, user=user) @@ -2944,81 +3001,85 @@ def managed(name, # Group isn't relevant to Windows, use win_perms/win_deny_perms if group is not None: log.warning( - 'The group argument for {0} has been ignored as this is ' - 'a Windows system. Please use the `win_*` parameters to set ' - 'permissions in Windows.'.format(name) + "The group argument for {0} has been ignored as this is " + "a Windows system. Please use the `win_*` parameters to set " + "permissions in Windows.".format(name) ) group = user if not create: if not os.path.isfile(name): # Don't create a file that is not already present - ret['comment'] = ('File {0} is not present and is not set for ' - 'creation').format(name) + ret["comment"] = ( + "File {0} is not present and is not set for " "creation" + ).format(name) return ret u_check = _check_user(user, group) if u_check: # The specified user or group do not exist return _error(ret, u_check) if not os.path.isabs(name): - return _error( - ret, 'Specified file {0} is not an absolute path'.format(name)) + return _error(ret, "Specified file {0} is not an absolute path".format(name)) if os.path.isdir(name): - ret['comment'] = 'Specified target {0} is a directory'.format(name) - ret['result'] = False + ret["comment"] = "Specified target {0} is a directory".format(name) + ret["result"] = False return ret if context is None: context = {} elif not isinstance(context, dict): - return _error( - ret, 'Context must be formed as a dict') + return _error(ret, "Context must be formed as a dict") if defaults and not isinstance(defaults, dict): - return _error( - ret, 'Defaults must be formed as a dict') + return _error(ret, "Defaults must be formed as a dict") if not replace and os.path.exists(name): ret_perms = {} # Check and set the permissions if necessary if salt.utils.platform.is_windows(): - ret = __salt__['file.check_perms']( + ret = __salt__["file.check_perms"]( path=name, ret=ret, owner=win_owner, grant_perms=win_perms, deny_perms=win_deny_perms, inheritance=win_inheritance, - reset=win_perms_reset) + reset=win_perms_reset, + ) else: - ret, ret_perms = __salt__['file.check_perms']( - name, ret, user, group, mode, attrs, follow_symlinks) - if __opts__['test']: - if isinstance(ret_perms, dict) and \ - 'lmode' in ret_perms and \ - mode != ret_perms['lmode']: - ret['comment'] = ('File {0} will be updated with permissions ' - '{1} from its current ' - 'state of {2}'.format(name, - mode, - ret_perms['lmode'])) + ret, ret_perms = __salt__["file.check_perms"]( + name, ret, user, group, mode, attrs, follow_symlinks + ) + if __opts__["test"]: + if ( + isinstance(ret_perms, dict) + and "lmode" in ret_perms + and mode != ret_perms["lmode"] + ): + ret["comment"] = ( + "File {0} will be updated with permissions " + "{1} from its current " + "state of {2}".format(name, mode, ret_perms["lmode"]) + ) else: - ret['comment'] = 'File {0} not updated'.format(name) - elif not ret['changes'] and ret['result']: - ret['comment'] = ('File {0} exists with proper permissions. ' - 'No changes made.'.format(name)) + ret["comment"] = "File {0} not updated".format(name) + elif not ret["changes"] and ret["result"]: + ret["comment"] = ( + "File {0} exists with proper permissions. " + "No changes made.".format(name) + ) return ret accum_data, _ = _load_accumulators() if name in accum_data: if not context: context = {} - context['accumulator'] = accum_data[name] + context["accumulator"] = accum_data[name] try: - if __opts__['test']: - if 'file.check_managed_changes' in __salt__: - ret['changes'] = __salt__['file.check_managed_changes']( + if __opts__["test"]: + if "file.check_managed_changes" in __salt__: + ret["changes"] = __salt__["file.check_managed_changes"]( name, source, source_hash, @@ -3039,47 +3100,46 @@ def managed(name, if salt.utils.platform.is_windows(): try: - ret = __salt__['file.check_perms']( + ret = __salt__["file.check_perms"]( path=name, ret=ret, owner=win_owner, grant_perms=win_perms, deny_perms=win_deny_perms, inheritance=win_inheritance, - reset=win_perms_reset) + reset=win_perms_reset, + ) except CommandExecutionError as exc: - if exc.strerror.startswith('Path not found'): - ret['changes']['newfile'] = name + if exc.strerror.startswith("Path not found"): + ret["changes"]["newfile"] = name - if isinstance(ret['changes'], tuple): - ret['result'], ret['comment'] = ret['changes'] - elif ret['changes']: - ret['result'] = None - ret['comment'] = 'The file {0} is set to be changed'.format(name) - ret['comment'] += ('\nNote: No changes made, actual changes may\n' - 'be different due to other states.') - if 'diff' in ret['changes'] and not show_changes: - ret['changes']['diff'] = '<show_changes=False>' + if isinstance(ret["changes"], tuple): + ret["result"], ret["comment"] = ret["changes"] + elif ret["changes"]: + ret["result"] = None + ret["comment"] = "The file {0} is set to be changed".format(name) + ret["comment"] += ( + "\nNote: No changes made, actual changes may\n" + "be different due to other states." + ) + if "diff" in ret["changes"] and not show_changes: + ret["changes"]["diff"] = "<show_changes=False>" else: - ret['result'] = True - ret['comment'] = 'The file {0} is in the correct state'.format(name) + ret["result"] = True + ret["comment"] = "The file {0} is in the correct state".format(name) return ret # If the source is a list then find which file exists - source, source_hash = __salt__['file.source_list']( - source, - source_hash, - __env__ - ) + source, source_hash = __salt__["file.source_list"](source, source_hash, __env__) except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = 'Unable to manage file: {0}'.format(exc) + ret["result"] = False + ret["comment"] = "Unable to manage file: {0}".format(exc) return ret # Gather the source file from the server try: - sfn, source_sum, comment_ = __salt__['file.get_managed']( + sfn, source_sum, comment_ = __salt__["file.get_managed"]( name, template, source, @@ -3096,9 +3156,9 @@ def managed(name, **kwargs ) except Exception as exc: # pylint: disable=broad-except - ret['changes'] = {} + ret["changes"] = {} log.debug(traceback.format_exc()) - return _error(ret, 'Unable to manage file: {0}'.format(exc)) + return _error(ret, "Unable to manage file: {0}".format(exc)) tmp_filename = None @@ -3106,19 +3166,19 @@ def managed(name, tmp_filename = salt.utils.files.mkstemp(suffix=tmp_ext, dir=tmp_dir) # if exists copy existing file to tmp to compare - if __salt__['file.file_exists'](name): + if __salt__["file.file_exists"](name): try: - __salt__['file.copy'](name, tmp_filename) + __salt__["file.copy"](name, tmp_filename) except Exception as exc: # pylint: disable=broad-except return _error( ret, - 'Unable to copy file {0} to {1}: {2}'.format( + "Unable to copy file {0} to {1}: {2}".format( name, tmp_filename, exc - ) + ), ) try: - ret = __salt__['file.manage_file']( + ret = __salt__["file.manage_file"]( tmp_filename, sfn, ret, @@ -3145,33 +3205,29 @@ def managed(name, win_perms_reset=win_perms_reset, encoding=encoding, encoding_errors=encoding_errors, - **kwargs) + **kwargs + ) except Exception as exc: # pylint: disable=broad-except - ret['changes'] = {} + ret["changes"] = {} log.debug(traceback.format_exc()) salt.utils.files.remove(tmp_filename) if not keep_source: - if not sfn \ - and source \ - and _urlparse(source).scheme == 'salt': + if not sfn and source and _urlparse(source).scheme == "salt": # The file would not have been cached until manage_file was # run, so check again here for a cached copy. - sfn = __salt__['cp.is_cached'](source, __env__) + sfn = __salt__["cp.is_cached"](source, __env__) if sfn: salt.utils.files.remove(sfn) - return _error(ret, 'Unable to check_cmd file: {0}'.format(exc)) + return _error(ret, "Unable to check_cmd file: {0}".format(exc)) # file being updated to verify using check_cmd - if ret['changes']: + if ret["changes"]: # Reset ret - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + ret = {"changes": {}, "comment": "", "name": name, "result": True} check_cmd_opts = {} - if 'shell' in __grains__: - check_cmd_opts['shell'] = __grains__['shell'] + if "shell" in __grains__: + check_cmd_opts["shell"] = __grains__["shell"] cret = mod_run_check_cmd(check_cmd, tmp_filename, **check_cmd_opts) if isinstance(cret, dict): @@ -3186,16 +3242,13 @@ def managed(name, sfn = tmp_filename else: - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + ret = {"changes": {}, "comment": "", "name": name, "result": True} if comment_ and contents is None: return _error(ret, comment_) else: try: - return __salt__['file.manage_file']( + return __salt__["file.manage_file"]( name, sfn, ret, @@ -3222,34 +3275,33 @@ def managed(name, win_perms_reset=win_perms_reset, encoding=encoding, encoding_errors=encoding_errors, - **kwargs) + **kwargs + ) except Exception as exc: # pylint: disable=broad-except - ret['changes'] = {} + ret["changes"] = {} log.debug(traceback.format_exc()) - return _error(ret, 'Unable to manage file: {0}'.format(exc)) + return _error(ret, "Unable to manage file: {0}".format(exc)) finally: if tmp_filename: salt.utils.files.remove(tmp_filename) if not keep_source: - if not sfn \ - and source \ - and _urlparse(source).scheme == 'salt': + if not sfn and source and _urlparse(source).scheme == "salt": # The file would not have been cached until manage_file was # run, so check again here for a cached copy. - sfn = __salt__['cp.is_cached'](source, __env__) + sfn = __salt__["cp.is_cached"](source, __env__) if sfn: salt.utils.files.remove(sfn) -_RECURSE_TYPES = ['user', 'group', 'mode', 'ignore_files', 'ignore_dirs'] +_RECURSE_TYPES = ["user", "group", "mode", "ignore_files", "ignore_dirs"] def _get_recurse_set(recurse): - ''' + """ Converse *recurse* definition to a set of strings. Raises TypeError or ValueError when *recurse* has wrong structure. - ''' + """ if not recurse: return set() if not isinstance(recurse, list): @@ -3259,19 +3311,24 @@ def _get_recurse_set(recurse): except TypeError: # non-hashable elements recurse_set = None if recurse_set is None or not set(_RECURSE_TYPES) >= recurse_set: - raise ValueError('Types for "recurse" limited to {0}.'.format( - ', '.join('"{0}"'.format(rtype) for rtype in _RECURSE_TYPES))) - if 'ignore_files' in recurse_set and 'ignore_dirs' in recurse_set: - raise ValueError('Must not specify "recurse" options "ignore_files"' - ' and "ignore_dirs" at the same time.') + raise ValueError( + 'Types for "recurse" limited to {0}.'.format( + ", ".join('"{0}"'.format(rtype) for rtype in _RECURSE_TYPES) + ) + ) + if "ignore_files" in recurse_set and "ignore_dirs" in recurse_set: + raise ValueError( + 'Must not specify "recurse" options "ignore_files"' + ' and "ignore_dirs" at the same time.' + ) return recurse_set def _depth_limited_walk(top, max_depth=None): - ''' + """ Walk the directory tree under root up till reaching max_depth. With max_depth=None (default), do not limit depth. - ''' + """ for root, dirs, files in salt.utils.path.os_walk(top): if max_depth is not None: rel_depth = root.count(os.path.sep) - top.count(os.path.sep) @@ -3280,29 +3337,31 @@ def _depth_limited_walk(top, max_depth=None): yield (six.text_type(root), list(dirs), list(files)) -def directory(name, - user=None, - group=None, - recurse=None, - max_depth=None, - dir_mode=None, - file_mode=None, - makedirs=False, - clean=False, - require=None, - exclude_pat=None, - follow_symlinks=False, - force=False, - backupname=None, - allow_symlink=True, - children_only=False, - win_owner=None, - win_perms=None, - win_deny_perms=None, - win_inheritance=True, - win_perms_reset=False, - **kwargs): - r''' +def directory( + name, + user=None, + group=None, + recurse=None, + max_depth=None, + dir_mode=None, + file_mode=None, + makedirs=False, + clean=False, + require=None, + exclude_pat=None, + follow_symlinks=False, + force=False, + backupname=None, + allow_symlink=True, + children_only=False, + win_owner=None, + win_perms=None, + win_deny_perms=None, + win_inheritance=True, + win_perms_reset=False, + **kwargs +): + r""" Ensure that a named directory is present and has the right perms name @@ -3496,20 +3555,17 @@ def directory(name, fred_snuffy: perms: full_control - win_inheritance: False - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.directory') + return _error(ret, "Must provide name to file.directory") # Remove trailing slash, if present and we're not working on "/" itself - if name[-1] == '/' and name != '/': + if name[-1] == "/" and name != "/": name = name[:-1] if max_depth is not None and clean: - return _error(ret, 'Cannot specify both max_depth and clean') + return _error(ret, "Cannot specify both max_depth and clean") user = _test_owner(kwargs, user=user) if salt.utils.platform.is_windows(): @@ -3521,14 +3577,14 @@ def directory(name, # Group isn't relevant to Windows, use win_perms/win_deny_perms if group is not None: log.warning( - 'The group argument for {0} has been ignored as this is ' - 'a Windows system. Please use the `win_*` parameters to set ' - 'permissions in Windows.'.format(name) + "The group argument for {0} has been ignored as this is " + "a Windows system. Please use the `win_*` parameters to set " + "permissions in Windows.".format(name) ) group = user - if 'mode' in kwargs and not dir_mode: - dir_mode = kwargs.get('mode', []) + if "mode" in kwargs and not dir_mode: + dir_mode = kwargs.get("mode", []) if not file_mode: file_mode = dir_mode @@ -3552,52 +3608,59 @@ def directory(name, # Must be an absolute path if not os.path.isabs(name): - return _error( - ret, 'Specified file {0} is not an absolute path'.format(name)) + return _error(ret, "Specified file {0} is not an absolute path".format(name)) # Check for existing file or symlink - if os.path.isfile(name) or (not allow_symlink and os.path.islink(name)) \ - or (force and os.path.islink(name)): + if ( + os.path.isfile(name) + or (not allow_symlink and os.path.islink(name)) + or (force and os.path.islink(name)) + ): # Was a backupname specified if backupname is not None: # Make a backup first if os.path.lexists(backupname): if not force: - return _error(ret, (( - 'File exists where the backup target {0} should go' - ).format(backupname))) + return _error( + ret, + ( + ( + "File exists where the backup target {0} should go" + ).format(backupname) + ), + ) else: - __salt__['file.remove'](backupname) + __salt__["file.remove"](backupname) os.rename(name, backupname) elif force: # Remove whatever is in the way if os.path.isfile(name): - if __opts__['test']: - ret['changes']['forced'] = 'File would be forcibly replaced' + if __opts__["test"]: + ret["changes"]["forced"] = "File would be forcibly replaced" else: os.remove(name) - ret['changes']['forced'] = 'File was forcibly replaced' - elif __salt__['file.is_link'](name): - if __opts__['test']: - ret['changes']['forced'] = 'Symlink would be forcibly replaced' + ret["changes"]["forced"] = "File was forcibly replaced" + elif __salt__["file.is_link"](name): + if __opts__["test"]: + ret["changes"]["forced"] = "Symlink would be forcibly replaced" else: - __salt__['file.remove'](name) - ret['changes']['forced'] = 'Symlink was forcibly replaced' + __salt__["file.remove"](name) + ret["changes"]["forced"] = "Symlink was forcibly replaced" else: - if __opts__['test']: - ret['changes']['forced'] = 'Directory would be forcibly replaced' + if __opts__["test"]: + ret["changes"]["forced"] = "Directory would be forcibly replaced" else: - __salt__['file.remove'](name) - ret['changes']['forced'] = 'Directory was forcibly replaced' + __salt__["file.remove"](name) + ret["changes"]["forced"] = "Directory was forcibly replaced" else: if os.path.isfile(name): return _error( - ret, - 'Specified location {0} exists and is a file'.format(name)) + ret, "Specified location {0} exists and is a file".format(name) + ) elif os.path.islink(name): return _error( - ret, - 'Specified location {0} exists and is a symlink'.format(name)) + ret, "Specified location {0} exists and is a symlink".format(name) + ) # Check directory? if salt.utils.platform.is_windows(): @@ -3607,20 +3670,31 @@ def directory(name, win_perms=win_perms, win_deny_perms=win_deny_perms, win_inheritance=win_inheritance, - win_perms_reset=win_perms_reset) + win_perms_reset=win_perms_reset, + ) else: presult, pcomment, pchanges = _check_directory( - name, user, group, recurse or [], dir_mode, file_mode, clean, - require, exclude_pat, max_depth, follow_symlinks) + name, + user, + group, + recurse or [], + dir_mode, + file_mode, + clean, + require, + exclude_pat, + max_depth, + follow_symlinks, + ) if pchanges: - ret['changes'].update(pchanges) + ret["changes"].update(pchanges) # Don't run through the reset of the function if there are no changes to be # made - if __opts__['test'] or not ret['changes']: - ret['result'] = presult - ret['comment'] = pcomment + if __opts__["test"] or not ret["changes"]: + ret["result"] = presult + ret["comment"] = pcomment return ret if not os.path.isdir(name): @@ -3630,51 +3704,55 @@ def directory(name, if makedirs: # Everything's good, create the parent Dirs try: - _makedirs(name=name, - user=user, - group=group, - dir_mode=dir_mode, - win_owner=win_owner, - win_perms=win_perms, - win_deny_perms=win_deny_perms, - win_inheritance=win_inheritance) + _makedirs( + name=name, + user=user, + group=group, + dir_mode=dir_mode, + win_owner=win_owner, + win_perms=win_perms, + win_deny_perms=win_deny_perms, + win_inheritance=win_inheritance, + ) except CommandExecutionError as exc: - return _error(ret, 'Drive {0} is not mapped'.format(exc.message)) + return _error(ret, "Drive {0} is not mapped".format(exc.message)) else: - return _error( - ret, 'No directory to create {0} in'.format(name)) + return _error(ret, "No directory to create {0} in".format(name)) if salt.utils.platform.is_windows(): - __salt__['file.mkdir']( + __salt__["file.mkdir"]( path=name, owner=win_owner, grant_perms=win_perms, deny_perms=win_deny_perms, inheritance=win_inheritance, - reset=win_perms_reset) + reset=win_perms_reset, + ) else: - __salt__['file.mkdir'](name, user=user, group=group, mode=dir_mode) + __salt__["file.mkdir"](name, user=user, group=group, mode=dir_mode) - ret['changes'][name] = 'New Dir' + ret["changes"][name] = "New Dir" if not os.path.isdir(name): - return _error(ret, 'Failed to create directory {0}'.format(name)) + return _error(ret, "Failed to create directory {0}".format(name)) # issue 32707: skip this __salt__['file.check_perms'] call if children_only == True # Check permissions if not children_only: if salt.utils.platform.is_windows(): - ret = __salt__['file.check_perms']( + ret = __salt__["file.check_perms"]( path=name, ret=ret, owner=win_owner, grant_perms=win_perms, deny_perms=win_deny_perms, inheritance=win_inheritance, - reset=win_perms_reset) + reset=win_perms_reset, + ) else: - ret, perms = __salt__['file.check_perms']( - name, ret, user, group, dir_mode, None, follow_symlinks) + ret, perms = __salt__["file.check_perms"]( + name, ret, user, group, dir_mode, None, follow_symlinks + ) errors = [] if recurse or clean: @@ -3690,50 +3768,58 @@ def directory(name, try: recurse_set = _get_recurse_set(recurse) except (TypeError, ValueError) as exc: - ret['result'] = False - ret['comment'] = '{0}'.format(exc) + ret["result"] = False + ret["comment"] = "{0}".format(exc) # NOTE: Should this be enough to stop the whole check altogether? if recurse_set: - if 'user' in recurse_set: + if "user" in recurse_set: if user or isinstance(user, int): - uid = __salt__['file.user_to_uid'](user) + uid = __salt__["file.user_to_uid"](user) # file.user_to_uid returns '' if user does not exist. Above # check for user is not fatal, so we need to be sure user # exists. if isinstance(uid, six.string_types): - ret['result'] = False - ret['comment'] = 'Failed to enforce ownership for ' \ - 'user {0} (user does not ' \ - 'exist)'.format(user) + ret["result"] = False + ret["comment"] = ( + "Failed to enforce ownership for " + "user {0} (user does not " + "exist)".format(user) + ) else: - ret['result'] = False - ret['comment'] = 'user not specified, but configured as ' \ - 'a target for recursive ownership ' \ - 'management' + ret["result"] = False + ret["comment"] = ( + "user not specified, but configured as " + "a target for recursive ownership " + "management" + ) else: user = None - if 'group' in recurse_set: + if "group" in recurse_set: if group or isinstance(group, int): - gid = __salt__['file.group_to_gid'](group) + gid = __salt__["file.group_to_gid"](group) # As above with user, we need to make sure group exists. if isinstance(gid, six.string_types): - ret['result'] = False - ret['comment'] = 'Failed to enforce group ownership ' \ - 'for group {0}'.format(group) + ret["result"] = False + ret["comment"] = ( + "Failed to enforce group ownership " + "for group {0}".format(group) + ) else: - ret['result'] = False - ret['comment'] = 'group not specified, but configured ' \ - 'as a target for recursive ownership ' \ - 'management' + ret["result"] = False + ret["comment"] = ( + "group not specified, but configured " + "as a target for recursive ownership " + "management" + ) else: group = None - if 'mode' not in recurse_set: + if "mode" not in recurse_set: file_mode = None dir_mode = None - check_files = 'ignore_files' not in recurse_set - check_dirs = 'ignore_dirs' not in recurse_set + check_files = "ignore_files" not in recurse_set + check_dirs = "ignore_dirs" not in recurse_set for root, dirs, files in walk_l: if check_files: @@ -3741,19 +3827,21 @@ def directory(name, full = os.path.join(root, fn_) try: if salt.utils.platform.is_windows(): - ret = __salt__['file.check_perms']( + ret = __salt__["file.check_perms"]( path=full, ret=ret, owner=win_owner, grant_perms=win_perms, deny_perms=win_deny_perms, inheritance=win_inheritance, - reset=win_perms_reset) + reset=win_perms_reset, + ) else: - ret, _ = __salt__['file.check_perms']( - full, ret, user, group, file_mode, None, follow_symlinks) + ret, _ = __salt__["file.check_perms"]( + full, ret, user, group, file_mode, None, follow_symlinks + ) except CommandExecutionError as exc: - if not exc.strerror.startswith('Path not found'): + if not exc.strerror.startswith("Path not found"): errors.append(exc.strerror) if check_dirs: @@ -3761,85 +3849,88 @@ def directory(name, full = os.path.join(root, dir_) try: if salt.utils.platform.is_windows(): - ret = __salt__['file.check_perms']( + ret = __salt__["file.check_perms"]( path=full, ret=ret, owner=win_owner, grant_perms=win_perms, deny_perms=win_deny_perms, inheritance=win_inheritance, - reset=win_perms_reset) + reset=win_perms_reset, + ) else: - ret, _ = __salt__['file.check_perms']( - full, ret, user, group, dir_mode, None, follow_symlinks) + ret, _ = __salt__["file.check_perms"]( + full, ret, user, group, dir_mode, None, follow_symlinks + ) except CommandExecutionError as exc: - if not exc.strerror.startswith('Path not found'): + if not exc.strerror.startswith("Path not found"): errors.append(exc.strerror) if clean: keep = _gen_keep_files(name, require, walk_d) - log.debug('List of kept files when use file.directory with clean: %s', - keep) + log.debug("List of kept files when use file.directory with clean: %s", keep) removed = _clean_dir(name, list(keep), exclude_pat) if removed: - ret['changes']['removed'] = removed - ret['comment'] = 'Files cleaned from directory {0}'.format(name) + ret["changes"]["removed"] = removed + ret["comment"] = "Files cleaned from directory {0}".format(name) # issue 32707: reflect children_only selection in comments - if not ret['comment']: + if not ret["comment"]: if children_only: - ret['comment'] = 'Directory {0}/* updated'.format(name) + ret["comment"] = "Directory {0}/* updated".format(name) else: - if ret['changes']: - ret['comment'] = 'Directory {0} updated'.format(name) + if ret["changes"]: + ret["comment"] = "Directory {0} updated".format(name) - if __opts__['test']: - ret['comment'] = 'Directory {0} not updated'.format(name) - elif not ret['changes'] and ret['result']: + if __opts__["test"]: + ret["comment"] = "Directory {0} not updated".format(name) + elif not ret["changes"] and ret["result"]: orig_comment = None - if ret['comment']: - orig_comment = ret['comment'] + if ret["comment"]: + orig_comment = ret["comment"] - ret['comment'] = 'Directory {0} is in the correct state'.format(name) + ret["comment"] = "Directory {0} is in the correct state".format(name) if orig_comment: - ret['comment'] = '\n'.join([ret['comment'], orig_comment]) + ret["comment"] = "\n".join([ret["comment"], orig_comment]) if errors: - ret['result'] = False - ret['comment'] += '\n\nThe following errors were encountered:\n' + ret["result"] = False + ret["comment"] += "\n\nThe following errors were encountered:\n" for error in errors: - ret['comment'] += '\n- {0}'.format(error) + ret["comment"] += "\n- {0}".format(error) return ret -def recurse(name, - source, - keep_source=True, - clean=False, - require=None, - user=None, - group=None, - dir_mode=None, - file_mode=None, - sym_mode=None, - template=None, - context=None, - replace=True, - defaults=None, - include_empty=False, - backup='', - include_pat=None, - exclude_pat=None, - maxdepth=None, - keep_symlinks=False, - force_symlinks=False, - win_owner=None, - win_perms=None, - win_deny_perms=None, - win_inheritance=True, - **kwargs): - ''' +def recurse( + name, + source, + keep_source=True, + clean=False, + require=None, + user=None, + group=None, + dir_mode=None, + file_mode=None, + sym_mode=None, + template=None, + context=None, + replace=True, + defaults=None, + include_empty=False, + backup="", + include_pat=None, + exclude_pat=None, + maxdepth=None, + keep_symlinks=False, + force_symlinks=False, + win_owner=None, + win_perms=None, + win_deny_perms=None, + win_inheritance=True, + **kwargs +): + """ Recurse through a subdirectory on the master and copy said subdirectory over to the specified path. @@ -4019,10 +4110,10 @@ def recurse(name, True to inherit permissions from parent, otherwise False .. versionadded:: 2017.7.7 - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") name = os.path.expanduser(salt.utils.data.decode(name)) @@ -4030,34 +4121,36 @@ def recurse(name, if salt.utils.platform.is_windows(): if group is not None: log.warning( - 'The group argument for {0} has been ignored as this ' - 'is a Windows system.'.format(name) + "The group argument for {0} has been ignored as this " + "is a Windows system.".format(name) ) group = user ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': {} # { path: [comment, ...] } + "name": name, + "changes": {}, + "result": True, + "comment": {}, # { path: [comment, ...] } } - if 'mode' in kwargs: - ret['result'] = False - ret['comment'] = ( - '\'mode\' is not allowed in \'file.recurse\'. Please use ' - '\'file_mode\' and \'dir_mode\'.' + if "mode" in kwargs: + ret["result"] = False + ret["comment"] = ( + "'mode' is not allowed in 'file.recurse'. Please use " + "'file_mode' and 'dir_mode'." ) return ret - if any([x is not None for x in (dir_mode, file_mode, sym_mode)]) \ - and salt.utils.platform.is_windows(): - return _error(ret, 'mode management is not supported on Windows') + if ( + any([x is not None for x in (dir_mode, file_mode, sym_mode)]) + and salt.utils.platform.is_windows() + ): + return _error(ret, "mode management is not supported on Windows") # Make sure that leading zeros stripped by YAML loader are added back dir_mode = salt.utils.files.normalize_mode(dir_mode) try: - keep_mode = file_mode.lower() == 'keep' + keep_mode = file_mode.lower() == "keep" if keep_mode: # We're not hard-coding the mode, so set it to None file_mode = None @@ -4071,26 +4164,27 @@ def recurse(name, # The specified user or group do not exist return _error(ret, u_check) if not os.path.isabs(name): - return _error( - ret, 'Specified file {0} is not an absolute path'.format(name)) + return _error(ret, "Specified file {0} is not an absolute path".format(name)) # expand source into source_list source_list = _validate_str_list(source) for idx, val in enumerate(source_list): - source_list[idx] = val.rstrip('/') + source_list[idx] = val.rstrip("/") for precheck in source_list: - if not precheck.startswith('salt://'): - return _error(ret, ('Invalid source \'{0}\' ' - '(must be a salt:// URI)'.format(precheck))) + if not precheck.startswith("salt://"): + return _error( + ret, + ("Invalid source '{0}' " "(must be a salt:// URI)".format(precheck)), + ) # Select the first source in source_list that exists try: - source, source_hash = __salt__['file.source_list'](source_list, '', __env__) + source, source_hash = __salt__["file.source_list"](source_list, "", __env__) except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = 'Recurse failed: {0}'.format(exc) + ret["result"] = False + ret["comment"] = "Recurse failed: {0}".format(exc) return ret # Check source path relative to fileserver root, make sure it is a @@ -4098,14 +4192,14 @@ def recurse(name, srcpath, senv = salt.utils.url.parse(source) if senv is None: senv = __env__ - master_dirs = __salt__['cp.list_master_dirs'](saltenv=senv) - if srcpath not in master_dirs \ - and not any((x for x in master_dirs - if x.startswith(srcpath + '/'))): - ret['result'] = False - ret['comment'] = ( - 'The directory \'{0}\' does not exist on the salt fileserver ' - 'in saltenv \'{1}\''.format(srcpath, senv) + master_dirs = __salt__["cp.list_master_dirs"](saltenv=senv) + if srcpath not in master_dirs and not any( + (x for x in master_dirs if x.startswith(srcpath + "/")) + ): + ret["result"] = False + ret["comment"] = ( + "The directory '{0}' does not exist on the salt fileserver " + "in saltenv '{1}'".format(srcpath, senv) ) return ret @@ -4114,23 +4208,25 @@ def recurse(name, if os.path.exists(name): # it is not a dir, but it exists - fail out return _error( - ret, 'The path {0} exists and is not a directory'.format(name)) - if not __opts__['test']: + ret, "The path {0} exists and is not a directory".format(name) + ) + if not __opts__["test"]: if salt.utils.platform.is_windows(): win_owner = win_owner if win_owner else user - __salt__['file.makedirs_perms'](path=name, - owner=win_owner, - grant_perms=win_perms, - deny_perms=win_deny_perms, - inheritance=win_inheritance) + __salt__["file.makedirs_perms"]( + path=name, + owner=win_owner, + grant_perms=win_perms, + deny_perms=win_deny_perms, + inheritance=win_inheritance, + ) else: - __salt__['file.makedirs_perms'](name=name, - user=user, - group=group, - mode=dir_mode) + __salt__["file.makedirs_perms"]( + name=name, user=user, group=group, mode=dir_mode + ) def add_comment(path, comment): - comments = ret['comment'].setdefault(path, []) + comments = ret["comment"].setdefault(path, []) if isinstance(comment, six.string_types): comments.append(comment) else: @@ -4138,34 +4234,32 @@ def recurse(name, def merge_ret(path, _ret): # Use the most "negative" result code (out of True, None, False) - if _ret['result'] is False or ret['result'] is True: - ret['result'] = _ret['result'] + if _ret["result"] is False or ret["result"] is True: + ret["result"] = _ret["result"] # Only include comments about files that changed - if _ret['result'] is not True and _ret['comment']: - add_comment(path, _ret['comment']) + if _ret["result"] is not True and _ret["comment"]: + add_comment(path, _ret["comment"]) - if _ret['changes']: - ret['changes'][path] = _ret['changes'] + if _ret["changes"]: + ret["changes"][path] = _ret["changes"] def manage_file(path, source, replace): if clean and os.path.exists(path) and os.path.isdir(path) and replace: - _ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - if __opts__['test']: - _ret['comment'] = 'Replacing directory {0} with a ' \ - 'file'.format(path) - _ret['result'] = None + _ret = {"name": name, "changes": {}, "result": True, "comment": ""} + if __opts__["test"]: + _ret["comment"] = "Replacing directory {0} with a " "file".format(path) + _ret["result"] = None merge_ret(path, _ret) return else: - __salt__['file.remove'](path) - _ret['changes'] = {'diff': 'Replaced directory with a ' - 'new file'} + __salt__["file.remove"](path) + _ret["changes"] = {"diff": "Replaced directory with a " "new file"} merge_ret(path, _ret) # Conflicts can occur if some kwargs are passed in here pass_kwargs = {} - faults = ['mode', 'makedirs'] + faults = ["mode", "makedirs"] for key in kwargs: if key not in faults: pass_kwargs[key] = kwargs[key] @@ -4176,7 +4270,7 @@ def recurse(name, keep_source=keep_source, user=user, group=group, - mode='keep' if keep_mode else file_mode, + mode="keep" if keep_mode else file_mode, attrs=None, template=template, makedirs=True, @@ -4184,22 +4278,23 @@ def recurse(name, context=context, defaults=defaults, backup=backup, - **pass_kwargs) + **pass_kwargs + ) merge_ret(path, _ret) def manage_directory(path): - if os.path.basename(path) == '..': + if os.path.basename(path) == "..": return if clean and os.path.exists(path) and not os.path.isdir(path): - _ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - if __opts__['test']: - _ret['comment'] = 'Replacing {0} with a directory'.format(path) - _ret['result'] = None + _ret = {"name": name, "changes": {}, "result": True, "comment": ""} + if __opts__["test"]: + _ret["comment"] = "Replacing {0} with a directory".format(path) + _ret["result"] = None merge_ret(path, _ret) return else: - __salt__['file.remove'](path) - _ret['changes'] = {'diff': 'Replaced file with a directory'} + __salt__["file.remove"](path) + _ret["changes"] = {"diff": "Replaced file with a directory"} merge_ret(path, _ret) _ret = directory( @@ -4211,26 +4306,24 @@ def recurse(name, file_mode=None, makedirs=True, clean=False, - require=None) + require=None, + ) merge_ret(path, _ret) mng_files, mng_dirs, mng_symlinks, keep = _gen_recurse_managed_files( - name, - source, - keep_symlinks, - include_pat, - exclude_pat, - maxdepth, - include_empty) + name, source, keep_symlinks, include_pat, exclude_pat, maxdepth, include_empty + ) for srelpath, ltarget in mng_symlinks: - _ret = symlink(os.path.join(name, srelpath), - ltarget, - makedirs=True, - force=force_symlinks, - user=user, - group=group, - mode=sym_mode) + _ret = symlink( + os.path.join(name, srelpath), + ltarget, + makedirs=True, + force=force_symlinks, + user=user, + group=group, + mode=sym_mode, + ) if not _ret: continue merge_ret(os.path.join(name, srelpath), _ret) @@ -4244,32 +4337,33 @@ def recurse(name, keep.update(_gen_keep_files(name, require)) removed = _clean_dir(name, list(keep), exclude_pat) if removed: - if __opts__['test']: - if ret['result']: - ret['result'] = None - add_comment('removed', removed) + if __opts__["test"]: + if ret["result"]: + ret["result"] = None + add_comment("removed", removed) else: - ret['changes']['removed'] = removed + ret["changes"]["removed"] = removed # Flatten comments until salt command line client learns # to display structured comments in a readable fashion - ret['comment'] = '\n'.join('\n#### {0} ####\n{1}'.format( - k, v if isinstance(v, six.string_types) else '\n'.join(v) - ) for (k, v) in six.iteritems(ret['comment'])).strip() - - if not ret['comment']: - ret['comment'] = 'Recursively updated {0}'.format(name) - - if not ret['changes'] and ret['result']: - ret['comment'] = 'The directory {0} is in the correct state'.format( - name + ret["comment"] = "\n".join( + "\n#### {0} ####\n{1}".format( + k, v if isinstance(v, six.string_types) else "\n".join(v) ) + for (k, v) in six.iteritems(ret["comment"]) + ).strip() + + if not ret["comment"]: + ret["comment"] = "Recursively updated {0}".format(name) + + if not ret["changes"] and ret["result"]: + ret["comment"] = "The directory {0} is in the correct state".format(name) return ret def retention_schedule(name, retain, strptime_format=None, timezone=None): - ''' + """ Apply retention scheduling to backup storage directory. .. versionadded:: 2016.11.0 @@ -4331,19 +4425,21 @@ def retention_schedule(name, retain, strptime_format=None, timezone=None): - strptime_format: example_name_%Y%m%dT%H%M%S.tar.bz2 - timezone: None - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {'retained': [], 'deleted': [], 'ignored': []}, - 'result': True, - 'comment': ''} + ret = { + "name": name, + "changes": {"retained": [], "deleted": [], "ignored": []}, + "result": True, + "comment": "", + } if not name: - return _error(ret, 'Must provide name to file.retention_schedule') + return _error(ret, "Must provide name to file.retention_schedule") if not os.path.isdir(name): - return _error(ret, 'Name provided to file.retention must be a directory') + return _error(ret, "Name provided to file.retention must be a directory") # get list of files in directory - all_files = __salt__['file.readdir'](name) + all_files = __salt__["file.readdir"](name) # if strptime_format is set, filter through the list to find names which parse and get their datetimes. beginning_of_unix_time = datetime(1970, 1, 1) @@ -4358,16 +4454,18 @@ def retention_schedule(name, retain, strptime_format=None, timezone=None): return (None, None) def get_file_time_from_mtime(f): - if f == '.' or f == '..': + if f == "." or f == "..": return (None, None) - lstat = __salt__['file.lstat'](os.path.join(name, f)) + lstat = __salt__["file.lstat"](os.path.join(name, f)) if lstat: - mtime = lstat['st_mtime'] + mtime = lstat["st_mtime"] return (datetime.fromtimestamp(mtime, timezone), mtime) - else: # maybe it was deleted since we did the readdir? + else: # maybe it was deleted since we did the readdir? return (None, None) - get_file_time = get_file_time_from_strptime if strptime_format else get_file_time_from_mtime + get_file_time = ( + get_file_time_from_strptime if strptime_format else get_file_time_from_mtime + ) # data structures are nested dicts: # files_by_ymd = year.month.day.hour.unixtime: filename @@ -4376,6 +4474,7 @@ def retention_schedule(name, retain, strptime_format=None, timezone=None): # TODO: move to an ordered dict model and reduce the number of sorts in the rest of the code? def dict_maker(): return defaultdict(dict_maker) + files_by_ymd = dict_maker() files_by_y_week_dow = dict_maker() relevant_files = set() @@ -4392,11 +4491,11 @@ def retention_schedule(name, retain, strptime_format=None, timezone=None): # This is tightly coupled with the file_with_times data-structure above. RETAIN_TO_DEPTH = { - 'first_of_year': 1, - 'first_of_month': 2, - 'first_of_day': 3, - 'first_of_hour': 4, - 'most_recent': 5, + "first_of_year": 1, + "first_of_month": 2, + "first_of_day": 3, + "first_of_hour": 4, + "most_recent": 5, } def get_first(fwt): @@ -4404,7 +4503,7 @@ def retention_schedule(name, retain, strptime_format=None, timezone=None): first_sub_key = sorted(fwt.keys())[0] return get_first(fwt[first_sub_key]) else: - return set([fwt, ]) + return set([fwt]) def get_first_n_at_depth(fwt, depth, n): if depth <= 0: @@ -4424,47 +4523,64 @@ def retention_schedule(name, retain, strptime_format=None, timezone=None): # This is kind of a hack, since 'all' should really mean all, # but I think it's a large enough number that even modern filesystems would # choke if they had this many files in a single directory. - keep_count = sys.maxsize if 'all' == keep_count else int(keep_count) - if 'first_of_week' == retention_rule: - first_of_week_depth = 2 # year + week_of_year = 2 + keep_count = sys.maxsize if "all" == keep_count else int(keep_count) + if "first_of_week" == retention_rule: + first_of_week_depth = 2 # year + week_of_year = 2 # I'm adding 1 to keep_count below because it fixed an off-by one # issue in the tests. I don't understand why, and that bothers me. - retained_files |= get_first_n_at_depth(files_by_y_week_dow, - first_of_week_depth, - keep_count + 1) + retained_files |= get_first_n_at_depth( + files_by_y_week_dow, first_of_week_depth, keep_count + 1 + ) else: - retained_files |= get_first_n_at_depth(files_by_ymd, - RETAIN_TO_DEPTH[retention_rule], - keep_count) + retained_files |= get_first_n_at_depth( + files_by_ymd, RETAIN_TO_DEPTH[retention_rule], keep_count + ) deletable_files = list(relevant_files - retained_files) deletable_files.sort(reverse=True) changes = { - 'retained': sorted(list(retained_files), reverse=True), - 'deleted': deletable_files, - 'ignored': sorted(list(ignored_files), reverse=True), - } - ret['changes'] = changes + "retained": sorted(list(retained_files), reverse=True), + "deleted": deletable_files, + "ignored": sorted(list(ignored_files), reverse=True), + } + ret["changes"] = changes # TODO: track and report how much space was / would be reclaimed - if __opts__['test']: - ret['comment'] = '{0} backups would have been removed from {1}.\n'.format(len(deletable_files), name) + if __opts__["test"]: + ret["comment"] = "{0} backups would have been removed from {1}.\n".format( + len(deletable_files), name + ) if deletable_files: - ret['result'] = None + ret["result"] = None else: for f in deletable_files: - __salt__['file.remove'](os.path.join(name, f)) - ret['comment'] = '{0} backups were removed from {1}.\n'.format(len(deletable_files), name) - ret['changes'] = changes + __salt__["file.remove"](os.path.join(name, f)) + ret["comment"] = "{0} backups were removed from {1}.\n".format( + len(deletable_files), name + ) + ret["changes"] = changes return ret -def line(name, content=None, match=None, mode=None, location=None, - before=None, after=None, show_changes=True, backup=False, - quiet=False, indent=True, create=False, user=None, - group=None, file_mode=None): - ''' +def line( + name, + content=None, + match=None, + mode=None, + location=None, + before=None, + after=None, + show_changes=True, + backup=False, + quiet=False, + indent=True, + create=False, + user=None, + group=None, + file_mode=None, +): + """ Line-based editing of a file. .. versionadded:: 2015.8.0 @@ -4581,22 +4697,13 @@ def line(name, content=None, match=None, mode=None, location=None, - content: my key = my value - before: somekey.*? - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.line') + return _error(ret, "Must provide name to file.line") - managed( - name, - create=create, - user=user, - group=group, - mode=file_mode, - replace=False) + managed(name, create=create, user=user, group=group, mode=file_mode, replace=False) check_res, check_msg = _check_file(name) if not check_res: @@ -4606,46 +4713,60 @@ def line(name, content=None, match=None, mode=None, location=None, # it gets passed when needed. Feature #37092 mode = mode and mode.lower() or mode if mode is None: - return _error(ret, 'Mode was not defined. How to process the file?') + return _error(ret, "Mode was not defined. How to process the file?") - modeswithemptycontent = ['delete'] + modeswithemptycontent = ["delete"] if mode not in modeswithemptycontent and content is None: - return _error(ret, 'Content can only be empty if mode is {0}'.format(modeswithemptycontent)) + return _error( + ret, + "Content can only be empty if mode is {0}".format(modeswithemptycontent), + ) del modeswithemptycontent - changes = __salt__['file.line']( - name, content, match=match, mode=mode, location=location, - before=before, after=after, show_changes=show_changes, - backup=backup, quiet=quiet, indent=indent) + changes = __salt__["file.line"]( + name, + content, + match=match, + mode=mode, + location=location, + before=before, + after=after, + show_changes=show_changes, + backup=backup, + quiet=quiet, + indent=indent, + ) if changes: - ret['changes']['diff'] = changes - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Changes would be made' + ret["changes"]["diff"] = changes + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Changes would be made" else: - ret['result'] = True - ret['comment'] = 'Changes were made' + ret["result"] = True + ret["comment"] = "Changes were made" else: - ret['result'] = True - ret['comment'] = 'No changes needed to be made' + ret["result"] = True + ret["comment"] = "No changes needed to be made" return ret -def replace(name, - pattern, - repl, - count=0, - flags=8, - bufsize=1, - append_if_not_found=False, - prepend_if_not_found=False, - not_found_content=None, - backup='.bak', - show_changes=True, - ignore_if_missing=False, - backslash_literal=False): - r''' +def replace( + name, + pattern, + repl, + count=0, + flags=8, + bufsize=1, + append_if_not_found=False, + prepend_if_not_found=False, + not_found_content=None, + backup=".bak", + show_changes=True, + ignore_if_missing=False, + backslash_literal=False, +): + r""" Maintain an edit in a file. .. versionadded:: 0.17.0 @@ -4785,72 +4906,72 @@ def replace(name, The ``file.replace`` state uses Python's ``re`` module. For more advanced options, see https://docs.python.org/2/library/re.html - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.replace') + return _error(ret, "Must provide name to file.replace") check_res, check_msg = _check_file(name) if not check_res: - if ignore_if_missing and 'file not found' in check_msg: - ret['comment'] = 'No changes needed to be made' + if ignore_if_missing and "file not found" in check_msg: + ret["comment"] = "No changes needed to be made" return ret else: return _error(ret, check_msg) - changes = __salt__['file.replace'](name, - pattern, - repl, - count=count, - flags=flags, - bufsize=bufsize, - append_if_not_found=append_if_not_found, - prepend_if_not_found=prepend_if_not_found, - not_found_content=not_found_content, - backup=backup, - dry_run=__opts__['test'], - show_changes=show_changes, - ignore_if_missing=ignore_if_missing, - backslash_literal=backslash_literal) + changes = __salt__["file.replace"]( + name, + pattern, + repl, + count=count, + flags=flags, + bufsize=bufsize, + append_if_not_found=append_if_not_found, + prepend_if_not_found=prepend_if_not_found, + not_found_content=not_found_content, + backup=backup, + dry_run=__opts__["test"], + show_changes=show_changes, + ignore_if_missing=ignore_if_missing, + backslash_literal=backslash_literal, + ) if changes: - ret['changes']['diff'] = changes - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Changes would have been made' + ret["changes"]["diff"] = changes + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Changes would have been made" else: - ret['result'] = True - ret['comment'] = 'Changes were made' + ret["result"] = True + ret["comment"] = "Changes were made" else: - ret['result'] = True - ret['comment'] = 'No changes needed to be made' + ret["result"] = True + ret["comment"] = "No changes needed to be made" return ret def blockreplace( - name, - marker_start='#-- start managed zone --', - marker_end='#-- end managed zone --', - source=None, - source_hash=None, - template='jinja', - sources=None, - source_hashes=None, - defaults=None, - context=None, - content='', - append_if_not_found=False, - prepend_if_not_found=False, - backup='.bak', - show_changes=True, - append_newline=None): - ''' + name, + marker_start="#-- start managed zone --", + marker_end="#-- end managed zone --", + source=None, + source_hash=None, + template="jinja", + sources=None, + source_hashes=None, + defaults=None, + context=None, + content="", + append_if_not_found=False, + prepend_if_not_found=False, + backup=".bak", + show_changes=True, + append_newline=None, +): + """ Maintain an edit in a file in a zone delimited by two line markers .. versionadded:: 2014.1.0 @@ -5042,25 +5163,24 @@ def blockreplace( text 3 text 4 # END managed zone 42 -- - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.blockreplace') + return _error(ret, "Must provide name to file.blockreplace") if sources is None: sources = [] if source_hashes is None: source_hashes = [] - (ok_, err, sl_) = _unify_sources_and_hashes(source=source, - source_hash=source_hash, - sources=sources, - source_hashes=source_hashes) + (ok_, err, sl_) = _unify_sources_and_hashes( + source=source, + source_hash=source_hash, + sources=sources, + source_hashes=source_hashes, + ) if not ok_: return _error(ret, err) @@ -5074,32 +5194,32 @@ def blockreplace( # if we have multiple accumulators for a file, only apply the one # required at a time deps = accum_deps.get(name, []) - filtered = [a for a in deps if - __low__['__id__'] in deps[a] and a in accumulator] + filtered = [ + a for a in deps if __low__["__id__"] in deps[a] and a in accumulator + ] if not filtered: filtered = [a for a in accumulator] for acc in filtered: acc_content = accumulator[acc] for line in acc_content: - if content == '': + if content == "": content = line else: content += "\n" + line if sl_: - tmpret = _get_template_texts(source_list=sl_, - template=template, - defaults=defaults, - context=context) - if not tmpret['result']: + tmpret = _get_template_texts( + source_list=sl_, template=template, defaults=defaults, context=context + ) + if not tmpret["result"]: return tmpret - text = tmpret['data'] + text = tmpret["data"] for index, item in enumerate(text): content += six.text_type(item) try: - changes = __salt__['file.blockreplace']( + changes = __salt__["file.blockreplace"]( name, marker_start, marker_end, @@ -5107,34 +5227,35 @@ def blockreplace( append_if_not_found=append_if_not_found, prepend_if_not_found=prepend_if_not_found, backup=backup, - dry_run=__opts__['test'], + dry_run=__opts__["test"], show_changes=show_changes, - append_newline=append_newline) + append_newline=append_newline, + ) except Exception as exc: # pylint: disable=broad-except - log.exception('Encountered error managing block') - ret['comment'] = ( - 'Encountered error managing block: {0}. ' - 'See the log for details.'.format(exc) + log.exception("Encountered error managing block") + ret["comment"] = ( + "Encountered error managing block: {0}. " + "See the log for details.".format(exc) ) return ret if changes: - ret['changes']['diff'] = changes - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Changes would be made' + ret["changes"]["diff"] = changes + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Changes would be made" else: - ret['result'] = True - ret['comment'] = 'Changes were made' + ret["result"] = True + ret["comment"] = "Changes were made" else: - ret['result'] = True - ret['comment'] = 'No changes needed to be made' + ret["result"] = True + ret["comment"] = "No changes needed to be made" return ret -def comment(name, regex, char='#', backup='.bak'): - ''' +def comment(name, regex, char="#", backup=".bak"): + """ Comment out specified lines in a file. name @@ -5169,76 +5290,71 @@ def comment(name, regex, char='#', backup='.bak'): - regex: ^bind 127.0.0.1 .. versionadded:: 0.9.5 - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.comment') + return _error(ret, "Must provide name to file.comment") check_res, check_msg = _check_file(name) if not check_res: return _error(ret, check_msg) # remove (?i)-like flags, ^ and $ - unanchor_regex = re.sub(r'^(\(\?[iLmsux]\))?\^?(.*?)\$?$', r'\2', regex) + unanchor_regex = re.sub(r"^(\(\?[iLmsux]\))?\^?(.*?)\$?$", r"\2", regex) comment_regex = char + unanchor_regex # Make sure the pattern appears in the file before continuing - if not __salt__['file.search'](name, regex, multiline=True): - if __salt__['file.search'](name, comment_regex, multiline=True): - ret['comment'] = 'Pattern already commented' - ret['result'] = True + if not __salt__["file.search"](name, regex, multiline=True): + if __salt__["file.search"](name, comment_regex, multiline=True): + ret["comment"] = "Pattern already commented" + ret["result"] = True return ret else: - return _error(ret, '{0}: Pattern not found'.format(unanchor_regex)) + return _error(ret, "{0}: Pattern not found".format(unanchor_regex)) - if __opts__['test']: - ret['changes'][name] = 'updated' - ret['comment'] = 'File {0} is set to be updated'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["changes"][name] = "updated" + ret["comment"] = "File {0} is set to be updated".format(name) + ret["result"] = None return ret - with salt.utils.files.fopen(name, 'rb') as fp_: + with salt.utils.files.fopen(name, "rb") as fp_: slines = fp_.read() if six.PY3: slines = slines.decode(__salt_system_encoding__) slines = slines.splitlines(True) # Perform the edit - __salt__['file.comment_line'](name, regex, char, True, backup) + __salt__["file.comment_line"](name, regex, char, True, backup) - with salt.utils.files.fopen(name, 'rb') as fp_: + with salt.utils.files.fopen(name, "rb") as fp_: nlines = fp_.read() if six.PY3: nlines = nlines.decode(__salt_system_encoding__) nlines = nlines.splitlines(True) # Check the result - ret['result'] = __salt__['file.search'](name, unanchor_regex, multiline=True) + ret["result"] = __salt__["file.search"](name, unanchor_regex, multiline=True) if slines != nlines: - if not __utils__['files.is_text'](name): - ret['changes']['diff'] = 'Replace binary file' + if not __utils__["files.is_text"](name): + ret["changes"]["diff"] = "Replace binary file" else: # Changes happened, add them - ret['changes']['diff'] = ( - ''.join(difflib.unified_diff(slines, nlines)) - ) + ret["changes"]["diff"] = "".join(difflib.unified_diff(slines, nlines)) - if ret['result']: - ret['comment'] = 'Commented lines successfully' + if ret["result"]: + ret["comment"] = "Commented lines successfully" else: - ret['comment'] = 'Expected commented lines not found' + ret["comment"] = "Expected commented lines not found" return ret -def uncomment(name, regex, char='#', backup='.bak'): - ''' +def uncomment(name, regex, char="#", backup=".bak"): + """ Uncomment specified commented lines in a file name @@ -5272,88 +5388,81 @@ def uncomment(name, regex, char='#', backup='.bak'): - regex: EXTRA_GROUPS .. versionadded:: 0.9.5 - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.uncomment') + return _error(ret, "Must provide name to file.uncomment") check_res, check_msg = _check_file(name) if not check_res: return _error(ret, check_msg) # Make sure the pattern appears in the file - if __salt__['file.search']( - name, - '{0}[ \t]*{1}'.format(char, regex.lstrip('^')), - multiline=True): + if __salt__["file.search"]( + name, "{0}[ \t]*{1}".format(char, regex.lstrip("^")), multiline=True + ): # Line exists and is commented pass - elif __salt__['file.search']( - name, - '^[ \t]*{0}'.format(regex.lstrip('^')), - multiline=True): - ret['comment'] = 'Pattern already uncommented' - ret['result'] = True + elif __salt__["file.search"]( + name, "^[ \t]*{0}".format(regex.lstrip("^")), multiline=True + ): + ret["comment"] = "Pattern already uncommented" + ret["result"] = True return ret else: - return _error(ret, '{0}: Pattern not found'.format(regex)) + return _error(ret, "{0}: Pattern not found".format(regex)) - if __opts__['test']: - ret['changes'][name] = 'updated' - ret['comment'] = 'File {0} is set to be updated'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["changes"][name] = "updated" + ret["comment"] = "File {0} is set to be updated".format(name) + ret["result"] = None return ret - with salt.utils.files.fopen(name, 'rb') as fp_: + with salt.utils.files.fopen(name, "rb") as fp_: slines = salt.utils.data.decode(fp_.readlines()) # Perform the edit - __salt__['file.comment_line'](name, regex, char, False, backup) + __salt__["file.comment_line"](name, regex, char, False, backup) - with salt.utils.files.fopen(name, 'rb') as fp_: + with salt.utils.files.fopen(name, "rb") as fp_: nlines = salt.utils.data.decode(fp_.readlines()) # Check the result - ret['result'] = __salt__['file.search']( - name, - '^[ \t]*{0}'.format(regex.lstrip('^')), - multiline=True + ret["result"] = __salt__["file.search"]( + name, "^[ \t]*{0}".format(regex.lstrip("^")), multiline=True ) if slines != nlines: - if not __utils__['files.is_text'](name): - ret['changes']['diff'] = 'Replace binary file' + if not __utils__["files.is_text"](name): + ret["changes"]["diff"] = "Replace binary file" else: # Changes happened, add them - ret['changes']['diff'] = ( - ''.join(difflib.unified_diff(slines, nlines)) - ) + ret["changes"]["diff"] = "".join(difflib.unified_diff(slines, nlines)) - if ret['result']: - ret['comment'] = 'Uncommented lines successfully' + if ret["result"]: + ret["comment"] = "Uncommented lines successfully" else: - ret['comment'] = 'Expected uncommented lines not found' + ret["comment"] = "Expected uncommented lines not found" return ret -def append(name, - text=None, - makedirs=False, - source=None, - source_hash=None, - template='jinja', - sources=None, - source_hashes=None, - defaults=None, - context=None, - ignore_whitespace=True): - ''' +def append( + name, + text=None, + makedirs=False, + source=None, + source_hash=None, + template="jinja", + sources=None, + source_hashes=None, + defaults=None, + context=None, + ignore_whitespace=True, +): + """ Ensure that some text appears at the end of a file. The text will not be appended if it already exists in the file. @@ -5478,14 +5587,11 @@ def append(name, - salt://motd/general-messages.tmpl .. versionadded:: 0.9.5 - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.append') + return _error(ret, "Must provide name to file.append") name = os.path.expanduser(name) @@ -5498,38 +5604,42 @@ def append(name, # Add sources and source_hashes with template support # NOTE: FIX 'text' and any 'source' are mutually exclusive as 'text' # is re-assigned in the original code. - (ok_, err, sl_) = _unify_sources_and_hashes(source=source, - source_hash=source_hash, - sources=sources, - source_hashes=source_hashes) + (ok_, err, sl_) = _unify_sources_and_hashes( + source=source, + source_hash=source_hash, + sources=sources, + source_hashes=source_hashes, + ) if not ok_: return _error(ret, err) if makedirs is True: dirname = os.path.dirname(name) - if __opts__['test']: - ret['comment'] = 'Directory {0} is set to be updated'.format(dirname) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Directory {0} is set to be updated".format(dirname) + ret["result"] = None else: - if not __salt__['file.directory_exists'](dirname): + if not __salt__["file.directory_exists"](dirname): try: _makedirs(name=name) except CommandExecutionError as exc: - return _error(ret, 'Drive {0} is not mapped'.format(exc.message)) + return _error(ret, "Drive {0} is not mapped".format(exc.message)) - check_res, check_msg, check_changes = _check_directory_win(dirname) \ - if salt.utils.platform.is_windows() \ + check_res, check_msg, check_changes = ( + _check_directory_win(dirname) + if salt.utils.platform.is_windows() else _check_directory(dirname) + ) if not check_res: - ret['changes'] = check_changes + ret["changes"] = check_changes return _error(ret, check_msg) check_res, check_msg = _check_file(name) if not check_res: # Try to create the file touch_ret = touch(name, makedirs=makedirs) - if __opts__['test']: + if __opts__["test"]: return touch_ret retry_res, retry_msg = _check_file(name) if not retry_res: @@ -5537,17 +5647,16 @@ def append(name, # Follow the original logic and re-assign 'text' if using source(s)... if sl_: - tmpret = _get_template_texts(source_list=sl_, - template=template, - defaults=defaults, - context=context) - if not tmpret['result']: + tmpret = _get_template_texts( + source_list=sl_, template=template, defaults=defaults, context=context + ) + if not tmpret["result"]: return tmpret - text = tmpret['data'] + text = tmpret["data"] text = _validate_str_list(text) - with salt.utils.files.fopen(name, 'rb') as fp_: + with salt.utils.files.fopen(name, "rb") as fp_: slines = fp_.read() if six.PY3: slines = slines.decode(__salt_system_encoding__) @@ -5557,78 +5666,75 @@ def append(name, try: for chunk in text: if ignore_whitespace: - if __salt__['file.search']( - name, - salt.utils.stringutils.build_whitespace_split_regex(chunk), - multiline=True): - continue - elif __salt__['file.search']( + if __salt__["file.search"]( name, - chunk, - multiline=True): + salt.utils.stringutils.build_whitespace_split_regex(chunk), + multiline=True, + ): + continue + elif __salt__["file.search"](name, chunk, multiline=True): continue for line_item in chunk.splitlines(): - append_lines.append('{0}'.format(line_item)) + append_lines.append("{0}".format(line_item)) except TypeError: - return _error(ret, 'No text found to append. Nothing appended') + return _error(ret, "No text found to append. Nothing appended") - if __opts__['test']: - ret['comment'] = 'File {0} is set to be updated'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "File {0} is set to be updated".format(name) + ret["result"] = None nlines = list(slines) nlines.extend(append_lines) if slines != nlines: - if not __utils__['files.is_text'](name): - ret['changes']['diff'] = 'Replace binary file' + if not __utils__["files.is_text"](name): + ret["changes"]["diff"] = "Replace binary file" else: # Changes happened, add them - ret['changes']['diff'] = ( - '\n'.join(difflib.unified_diff(slines, nlines)) - ) + ret["changes"]["diff"] = "\n".join(difflib.unified_diff(slines, nlines)) else: - ret['comment'] = 'File {0} is in correct state'.format(name) - ret['result'] = True + ret["comment"] = "File {0} is in correct state".format(name) + ret["result"] = True return ret if append_lines: - __salt__['file.append'](name, args=append_lines) - ret['comment'] = 'Appended {0} lines'.format(len(append_lines)) + __salt__["file.append"](name, args=append_lines) + ret["comment"] = "Appended {0} lines".format(len(append_lines)) else: - ret['comment'] = 'File {0} is in correct state'.format(name) + ret["comment"] = "File {0} is in correct state".format(name) - with salt.utils.files.fopen(name, 'rb') as fp_: + with salt.utils.files.fopen(name, "rb") as fp_: nlines = fp_.read() if six.PY3: nlines = nlines.decode(__salt_system_encoding__) nlines = nlines.splitlines() if slines != nlines: - if not __utils__['files.is_text'](name): - ret['changes']['diff'] = 'Replace binary file' + if not __utils__["files.is_text"](name): + ret["changes"]["diff"] = "Replace binary file" else: # Changes happened, add them - ret['changes']['diff'] = ( - '\n'.join(difflib.unified_diff(slines, nlines))) + ret["changes"]["diff"] = "\n".join(difflib.unified_diff(slines, nlines)) - ret['result'] = True + ret["result"] = True return ret -def prepend(name, - text=None, - makedirs=False, - source=None, - source_hash=None, - template='jinja', - sources=None, - source_hashes=None, - defaults=None, - context=None, - header=None): - ''' +def prepend( + name, + text=None, + makedirs=False, + source=None, + source_hash=None, + template="jinja", + sources=None, + source_hashes=None, + defaults=None, + context=None, + header=None, +): + """ Ensure that some text appears at the beginning of a file The text will not be prepended again if it already exists in the file. You @@ -5766,15 +5872,12 @@ def prepend(name, - salt://motd/general-messages.tmpl .. versionadded:: 2014.7.0 - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.prepend') + return _error(ret, "Must provide name to file.prepend") if sources is None: sources = [] @@ -5785,38 +5888,42 @@ def prepend(name, # Add sources and source_hashes with template support # NOTE: FIX 'text' and any 'source' are mutually exclusive as 'text' # is re-assigned in the original code. - (ok_, err, sl_) = _unify_sources_and_hashes(source=source, - source_hash=source_hash, - sources=sources, - source_hashes=source_hashes) + (ok_, err, sl_) = _unify_sources_and_hashes( + source=source, + source_hash=source_hash, + sources=sources, + source_hashes=source_hashes, + ) if not ok_: return _error(ret, err) if makedirs is True: dirname = os.path.dirname(name) - if __opts__['test']: - ret['comment'] = 'Directory {0} is set to be updated'.format(dirname) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Directory {0} is set to be updated".format(dirname) + ret["result"] = None else: - if not __salt__['file.directory_exists'](dirname): + if not __salt__["file.directory_exists"](dirname): try: _makedirs(name=name) except CommandExecutionError as exc: - return _error(ret, 'Drive {0} is not mapped'.format(exc.message)) + return _error(ret, "Drive {0} is not mapped".format(exc.message)) - check_res, check_msg, check_changes = _check_directory_win(dirname) \ - if salt.utils.platform.is_windows() \ + check_res, check_msg, check_changes = ( + _check_directory_win(dirname) + if salt.utils.platform.is_windows() else _check_directory(dirname) + ) if not check_res: - ret['changes'] = check_changes + ret["changes"] = check_changes return _error(ret, check_msg) check_res, check_msg = _check_file(name) if not check_res: # Try to create the file touch_ret = touch(name, makedirs=makedirs) - if __opts__['test']: + if __opts__["test"]: return touch_ret retry_res, retry_msg = _check_file(name) if not retry_res: @@ -5824,17 +5931,16 @@ def prepend(name, # Follow the original logic and re-assign 'text' if using source(s)... if sl_: - tmpret = _get_template_texts(source_list=sl_, - template=template, - defaults=defaults, - context=context) - if not tmpret['result']: + tmpret = _get_template_texts( + source_list=sl_, template=template, defaults=defaults, context=context + ) + if not tmpret["result"]: return tmpret - text = tmpret['data'] + text = tmpret["data"] text = _validate_str_list(text) - with salt.utils.files.fopen(name, 'rb') as fp_: + with salt.utils.files.fopen(name, "rb") as fp_: slines = fp_.read() if six.PY3: slines = slines.decode(__salt_system_encoding__) @@ -5848,48 +5954,47 @@ def prepend(name, # if header kwarg is unset of False, use regex search if not header: - if __salt__['file.search']( - name, - salt.utils.stringutils.build_whitespace_split_regex(chunk), - multiline=True): + if __salt__["file.search"]( + name, + salt.utils.stringutils.build_whitespace_split_regex(chunk), + multiline=True, + ): continue lines = chunk.splitlines() for line in lines: - if __opts__['test']: - ret['comment'] = 'File {0} is set to be updated'.format(name) - ret['result'] = None - test_lines.append('{0}\n'.format(line)) + if __opts__["test"]: + ret["comment"] = "File {0} is set to be updated".format(name) + ret["result"] = None + test_lines.append("{0}\n".format(line)) else: preface.append(line) count += 1 - if __opts__['test']: + if __opts__["test"]: nlines = test_lines + slines if slines != nlines: - if not __utils__['files.is_text'](name): - ret['changes']['diff'] = 'Replace binary file' + if not __utils__["files.is_text"](name): + ret["changes"]["diff"] = "Replace binary file" else: # Changes happened, add them - ret['changes']['diff'] = ( - ''.join(difflib.unified_diff(slines, nlines)) - ) - ret['result'] = None + ret["changes"]["diff"] = "".join(difflib.unified_diff(slines, nlines)) + ret["result"] = None else: - ret['comment'] = 'File {0} is in correct state'.format(name) - ret['result'] = True + ret["comment"] = "File {0} is in correct state".format(name) + ret["result"] = True return ret # if header kwarg is True, use verbatim compare if header: - with salt.utils.files.fopen(name, 'rb') as fp_: + with salt.utils.files.fopen(name, "rb") as fp_: # read as many lines of target file as length of user input contents = fp_.read() if six.PY3: contents = contents.decode(__salt_system_encoding__) contents = contents.splitlines(True) - target_head = contents[0:len(preface)] + target_head = contents[0 : len(preface)] target_lines = [] # strip newline chars from list entries for chunk in target_head: @@ -5897,50 +6002,50 @@ def prepend(name, # compare current top lines in target file with user input # and write user input if they differ if target_lines != preface: - __salt__['file.prepend'](name, *preface) + __salt__["file.prepend"](name, *preface) else: # clear changed lines counter if target file not modified count = 0 else: - __salt__['file.prepend'](name, *preface) + __salt__["file.prepend"](name, *preface) - with salt.utils.files.fopen(name, 'rb') as fp_: + with salt.utils.files.fopen(name, "rb") as fp_: nlines = fp_.read() if six.PY3: nlines = nlines.decode(__salt_system_encoding__) nlines = nlines.splitlines(True) if slines != nlines: - if not __utils__['files.is_text'](name): - ret['changes']['diff'] = 'Replace binary file' + if not __utils__["files.is_text"](name): + ret["changes"]["diff"] = "Replace binary file" else: # Changes happened, add them - ret['changes']['diff'] = ( - ''.join(difflib.unified_diff(slines, nlines)) - ) + ret["changes"]["diff"] = "".join(difflib.unified_diff(slines, nlines)) if count: - ret['comment'] = 'Prepended {0} lines'.format(count) + ret["comment"] = "Prepended {0} lines".format(count) else: - ret['comment'] = 'File {0} is in correct state'.format(name) - ret['result'] = True + ret["comment"] = "File {0} is in correct state".format(name) + ret["result"] = True return ret -def patch(name, - source=None, - source_hash=None, - source_hash_name=None, - skip_verify=False, - template=None, - context=None, - defaults=None, - options='', - reject_file=None, - strip=None, - saltenv=None, - **kwargs): - ''' +def patch( + name, + source=None, + source_hash=None, + source_hash_name=None, + skip_verify=False, + template=None, + context=None, + defaults=None, + options="", + reject_file=None, + strip=None, + saltenv=None, + **kwargs +): + """ Ensure that a patch has been applied to the specified file or directory .. versionchanged:: 2019.2.0 @@ -6061,11 +6166,11 @@ def patch(name, /opt/myfile.txt: file.patch: - source: salt://myfile.patch - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if not salt.utils.path.which('patch'): - ret['comment'] = 'patch executable not found on minion' + if not salt.utils.path.which("patch"): + ret["comment"] = "patch executable not found on minion" return ret # is_dir should be defined if we proceed past the if/else block below, but @@ -6073,49 +6178,45 @@ def patch(name, is_dir = False if not name: - ret['comment'] = 'A file/directory to be patched is required' + ret["comment"] = "A file/directory to be patched is required" return ret else: try: name = os.path.expanduser(name) except Exception: # pylint: disable=broad-except - ret['comment'] = 'Invalid path \'{0}\''.format(name) + ret["comment"] = "Invalid path '{0}'".format(name) return ret else: if not os.path.isabs(name): - ret['comment'] = '{0} is not an absolute path'.format(name) + ret["comment"] = "{0} is not an absolute path".format(name) return ret elif not os.path.exists(name): - ret['comment'] = '{0} does not exist'.format(name) + ret["comment"] = "{0} does not exist".format(name) return ret else: is_dir = os.path.isdir(name) - for deprecated_arg in ('hash', 'dry_run_first'): + for deprecated_arg in ("hash", "dry_run_first"): if deprecated_arg in kwargs: - ret.setdefault('warnings', []).append( - 'The \'{0}\' argument is no longer used and has been ' - 'ignored.'.format(deprecated_arg) + ret.setdefault("warnings", []).append( + "The '{0}' argument is no longer used and has been " + "ignored.".format(deprecated_arg) ) if reject_file is not None: try: reject_file_parent = os.path.dirname(reject_file) except Exception: # pylint: disable=broad-except - ret['comment'] = 'Invalid path \'{0}\' for reject_file'.format( - reject_file - ) + ret["comment"] = "Invalid path '{0}' for reject_file".format(reject_file) return ret else: if not os.path.isabs(reject_file_parent): - ret['comment'] = '\'{0}\' is not an absolute path'.format( - reject_file - ) + ret["comment"] = "'{0}' is not an absolute path".format(reject_file) return ret elif not os.path.isdir(reject_file_parent): - ret['comment'] = ( - 'Parent directory for reject_file \'{0}\' either does ' - 'not exist, or is not a directory'.format(reject_file) + ret["comment"] = ( + "Parent directory for reject_file '{0}' either does " + "not exist, or is not a directory".format(reject_file) ) return ret @@ -6131,7 +6232,7 @@ def patch(name, if not isinstance(option, six.string_types): option = six.text_type(option) - for item in ('-N', '--forward', '-r', '--reject-file', '-o', '--output'): + for item in ("-N", "--forward", "-r", "--reject-file", "-o", "--output"): if option.startswith(item): blacklisted = option break @@ -6141,24 +6242,24 @@ def patch(name, if blacklisted is not None: blacklisted_options.append(blacklisted) - if option.startswith('-p'): + if option.startswith("-p"): try: strip = int(option[2:]) except Exception: # pylint: disable=broad-except - ret['comment'] = ( - 'Invalid format for \'-p\' CLI option. Consider using ' - 'the \'strip\' option for this state.' + ret["comment"] = ( + "Invalid format for '-p' CLI option. Consider using " + "the 'strip' option for this state." ) return ret - elif option.startswith('--strip'): - if '=' in option: + elif option.startswith("--strip"): + if "=" in option: # Assume --strip=N try: - strip = int(option.rsplit('=', 1)[-1]) + strip = int(option.rsplit("=", 1)[-1]) except Exception: # pylint: disable=broad-except - ret['comment'] = ( - 'Invalid format for \'-strip\' CLI option. Consider ' - 'using the \'strip\' option for this state.' + ret["comment"] = ( + "Invalid format for '-strip' CLI option. Consider " + "using the 'strip' option for this state." ) return ret else: @@ -6166,9 +6267,9 @@ def patch(name, try: strip = int(options[index + 1]) except Exception: # pylint: disable=broad-except - ret['comment'] = ( - 'Invalid format for \'-strip\' CLI option. Consider ' - 'using the \'strip\' option for this state.' + ret["comment"] = ( + "Invalid format for '-strip' CLI option. Consider " + "using the 'strip' option for this state." ) return ret else: @@ -6182,22 +6283,18 @@ def patch(name, index += 1 if blacklisted_options: - ret['comment'] = ( - 'The following CLI options are not allowed: {0}'.format( - ', '.join(blacklisted_options) - ) + ret["comment"] = "The following CLI options are not allowed: {0}".format( + ", ".join(blacklisted_options) ) return ret options = sanitized_options try: - source_match = __salt__['file.source_list'](source, - source_hash, - __env__)[0] + source_match = __salt__["file.source_list"](source, source_hash, __env__)[0] except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret else: # Passing the saltenv to file.managed to pull down the patch file is @@ -6206,17 +6303,15 @@ def patch(name, # (and a traceback). Therefore, we will add the saltenv to the source # URL to ensure we pull the file from the correct environment. if saltenv is not None: - source_match_url, source_match_saltenv = \ - salt.utils.url.parse(source_match) - if source_match_url.startswith('salt://'): - if source_match_saltenv is not None \ - and source_match_saltenv != saltenv: - ret.setdefault('warnings', []).append( - 'Ignoring \'saltenv\' option in favor of saltenv ' - 'included in the source URL.' + source_match_url, source_match_saltenv = salt.utils.url.parse(source_match) + if source_match_url.startswith("salt://"): + if source_match_saltenv is not None and source_match_saltenv != saltenv: + ret.setdefault("warnings", []).append( + "Ignoring 'saltenv' option in favor of saltenv " + "included in the source URL." ) else: - source_match += '?saltenv={0}'.format(saltenv) + source_match += "?saltenv={0}".format(saltenv) cleanup = [] @@ -6225,35 +6320,36 @@ def patch(name, cleanup.append(patch_file) try: - orig_test = __opts__['test'] - __opts__['test'] = False - sys.modules[__salt__['test.ping'].__module__].__opts__['test'] = False - result = managed(patch_file, - source=source_match, - source_hash=source_hash, - source_hash_name=source_hash_name, - skip_verify=skip_verify, - template=template, - context=context, - defaults=defaults) + orig_test = __opts__["test"] + __opts__["test"] = False + sys.modules[__salt__["test.ping"].__module__].__opts__["test"] = False + result = managed( + patch_file, + source=source_match, + source_hash=source_hash, + source_hash_name=source_hash_name, + skip_verify=skip_verify, + template=template, + context=context, + defaults=defaults, + ) except Exception as exc: # pylint: disable=broad-except - msg = 'Failed to cache patch file {0}: {1}'.format( - salt.utils.url.redact_http_basic_auth(source_match), - exc + msg = "Failed to cache patch file {0}: {1}".format( + salt.utils.url.redact_http_basic_auth(source_match), exc ) log.exception(msg) - ret['comment'] = msg + ret["comment"] = msg return ret else: - log.debug('file.managed: %s', result) + log.debug("file.managed: %s", result) finally: - __opts__['test'] = orig_test - sys.modules[__salt__['test.ping'].__module__].__opts__['test'] = orig_test + __opts__["test"] = orig_test + sys.modules[__salt__["test.ping"].__module__].__opts__["test"] = orig_test - if not result['result']: + if not result["result"]: log.debug( - 'failed to download %s', - salt.utils.url.redact_http_basic_auth(source_match) + "failed to download %s", + salt.utils.url.redact_http_basic_auth(source_match), ) return result @@ -6261,11 +6357,9 @@ def patch(name, patch_opts = copy.copy(sanitized_options) if options is not None: patch_opts.extend(options) - return __salt__['file.patch']( - name, - patch_file, - options=patch_opts, - dry_run=dry_run) + return __salt__["file.patch"]( + name, patch_file, options=patch_opts, dry_run=dry_run + ) if reject_file is not None: patch_rejects = reject_file @@ -6282,59 +6376,57 @@ def patch(name, # to work on Windows with patch.exe, /dev/null is a non-starter. # Therefore, redirect all patch output to a temp file, which we will # then remove. - patch_opts = ['-N', '-r', patch_rejects, '-o', patch_output] + patch_opts = ["-N", "-r", patch_rejects, "-o", patch_output] if is_dir and strip is not None: - patch_opts.append('-p{0}'.format(strip)) + patch_opts.append("-p{0}".format(strip)) pre_check = _patch(patch_file, patch_opts) - if pre_check['retcode'] != 0: + if pre_check["retcode"] != 0: # Try to reverse-apply hunks from rejects file using a dry-run. # If this returns a retcode of 0, we know that the patch was # already applied. Rejects are written from the base of the # directory, so the strip option doesn't apply here. - reverse_pass = _patch(patch_rejects, ['-R', '-f'], dry_run=True) - already_applied = reverse_pass['retcode'] == 0 + reverse_pass = _patch(patch_rejects, ["-R", "-f"], dry_run=True) + already_applied = reverse_pass["retcode"] == 0 if already_applied: - ret['comment'] = 'Patch was already applied' - ret['result'] = True + ret["comment"] = "Patch was already applied" + ret["result"] = True return ret else: - ret['comment'] = ( - 'Patch would not apply cleanly, no changes made. Results ' - 'of dry-run are below.' + ret["comment"] = ( + "Patch would not apply cleanly, no changes made. Results " + "of dry-run are below." ) if reject_file is None: - ret['comment'] += ( - ' Run state again using the reject_file option to ' - 'save rejects to a persistent file.' + ret["comment"] += ( + " Run state again using the reject_file option to " + "save rejects to a persistent file." ) opts = copy.copy(__opts__) - opts['color'] = False - ret['comment'] += '\n\n' + salt.output.out_format( - pre_check, - 'nested', - opts, - nested_indent=14) + opts["color"] = False + ret["comment"] += "\n\n" + salt.output.out_format( + pre_check, "nested", opts, nested_indent=14 + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The patch would be applied' - ret['changes'] = pre_check + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The patch would be applied" + ret["changes"] = pre_check return ret # If we've made it here, the patch should apply cleanly patch_opts = [] if is_dir and strip is not None: - patch_opts.append('-p{0}'.format(strip)) - ret['changes'] = _patch(patch_file, patch_opts) + patch_opts.append("-p{0}".format(strip)) + ret["changes"] = _patch(patch_file, patch_opts) - if ret['changes']['retcode'] == 0: - ret['comment'] = 'Patch successfully applied' - ret['result'] = True + if ret["changes"]["retcode"] == 0: + ret["comment"] = "Patch successfully applied" + ret["result"] = True else: - ret['comment'] = 'Failed to apply patch' + ret["comment"] = "Failed to apply patch" return ret @@ -6346,13 +6438,12 @@ def patch(name, except OSError as exc: if exc.errno != os.errno.ENOENT: log.error( - 'file.patch: Failed to remove temp file %s: %s', - path, exc + "file.patch: Failed to remove temp file %s: %s", path, exc ) def touch(name, atime=None, mtime=None, makedirs=False): - ''' + """ Replicate the 'nix "touch" command to create a new empty file or update the atime and mtime of an existing file. @@ -6383,21 +6474,19 @@ def touch(name, atime=None, mtime=None, makedirs=False): file.touch .. versionadded:: 0.9.5 - ''' + """ name = os.path.expanduser(name) ret = { - 'name': name, - 'changes': {}, + "name": name, + "changes": {}, } if not name: - return _error(ret, 'Must provide name to file.touch') + return _error(ret, "Must provide name to file.touch") if not os.path.isabs(name): - return _error( - ret, 'Specified file {0} is not an absolute path'.format(name) - ) + return _error(ret, "Specified file {0} is not an absolute path".format(name)) - if __opts__['test']: + if __opts__["test"]: ret.update(_check_touch(name, atime, mtime)) return ret @@ -6405,38 +6494,38 @@ def touch(name, atime=None, mtime=None, makedirs=False): try: _makedirs(name=name) except CommandExecutionError as exc: - return _error(ret, 'Drive {0} is not mapped'.format(exc.message)) + return _error(ret, "Drive {0} is not mapped".format(exc.message)) if not os.path.isdir(os.path.dirname(name)): - return _error( - ret, 'Directory not present to touch file {0}'.format(name) - ) + return _error(ret, "Directory not present to touch file {0}".format(name)) extant = os.path.exists(name) - ret['result'] = __salt__['file.touch'](name, atime, mtime) - if not extant and ret['result']: - ret['comment'] = 'Created empty file {0}'.format(name) - ret['changes']['new'] = name - elif extant and ret['result']: - ret['comment'] = 'Updated times on {0} {1}'.format( - 'directory' if os.path.isdir(name) else 'file', name + ret["result"] = __salt__["file.touch"](name, atime, mtime) + if not extant and ret["result"]: + ret["comment"] = "Created empty file {0}".format(name) + ret["changes"]["new"] = name + elif extant and ret["result"]: + ret["comment"] = "Updated times on {0} {1}".format( + "directory" if os.path.isdir(name) else "file", name ) - ret['changes']['touched'] = name + ret["changes"]["touched"] = name return ret -def copy_(name, - source, - force=False, - makedirs=False, - preserve=False, - user=None, - group=None, - mode=None, - subdir=False, - **kwargs): - ''' +def copy_( + name, + source, + force=False, + makedirs=False, + preserve=False, + user=None, + group=None, + mode=None, + subdir=False, + **kwargs +): + """ If the file defined by the ``source`` option exists on the minion, copy it to the named path. The file will not be overwritten if it already exists, unless the ``force`` option is set to ``True``. @@ -6503,46 +6592,46 @@ def copy_(name, <salt.states.file.managed>` and :mod:`states.file.recurse <salt.states.file.recurse>`. - ''' + """ name = os.path.expanduser(name) source = os.path.expanduser(source) ret = { - 'name': name, - 'changes': {}, - 'comment': 'Copied "{0}" to "{1}"'.format(source, name), - 'result': True} + "name": name, + "changes": {}, + "comment": 'Copied "{0}" to "{1}"'.format(source, name), + "result": True, + } if not name: - return _error(ret, 'Must provide name to file.copy') + return _error(ret, "Must provide name to file.copy") changed = True if not os.path.isabs(name): - return _error( - ret, 'Specified file {0} is not an absolute path'.format(name)) + return _error(ret, "Specified file {0} is not an absolute path".format(name)) if not os.path.exists(source): return _error(ret, 'Source file "{0}" is not present'.format(source)) if preserve: - user = __salt__['file.get_user'](source) - group = __salt__['file.get_group'](source) - mode = __salt__['file.get_mode'](source) + user = __salt__["file.get_user"](source) + group = __salt__["file.get_group"](source) + mode = __salt__["file.get_mode"](source) else: user = _test_owner(kwargs, user=user) if user is None: - user = __opts__['user'] + user = __opts__["user"] if salt.utils.platform.is_windows(): if group is not None: log.warning( - 'The group argument for {0} has been ignored as this is ' - 'a Windows system.'.format(name) + "The group argument for {0} has been ignored as this is " + "a Windows system.".format(name) ) group = user if group is None: - group = __salt__['file.gid_to_group']( - __salt__['user.info'](user).get('gid', 0) + group = __salt__["file.gid_to_group"]( + __salt__["user.info"](user).get("gid", 0) ) u_check = _check_user(user, group) @@ -6551,7 +6640,7 @@ def copy_(name, return _error(ret, u_check) if mode is None: - mode = __salt__['file.get_mode'](source) + mode = __salt__["file.get_mode"](source) if os.path.isdir(name) and subdir: # If the target is a dir, and overwrite_dir is False, copy into the dir @@ -6564,37 +6653,41 @@ def copy_(name, hash2 = salt.utils.hashutils.get_hash(source) if hash1 == hash2: changed = True - ret['comment'] = ' '.join([ret['comment'], '- files are identical but force flag is set']) + ret["comment"] = " ".join( + [ret["comment"], "- files are identical but force flag is set"] + ) if not force: changed = False - elif not __opts__['test'] and changed: + elif not __opts__["test"] and changed: # Remove the destination to prevent problems later try: - __salt__['file.remove'](name) + __salt__["file.remove"](name) except (IOError, OSError): return _error( ret, 'Failed to delete "{0}" in preparation for ' - 'forced move'.format(name) + "forced move".format(name), ) - if __opts__['test']: + if __opts__["test"]: if changed: - ret['comment'] = 'File "{0}" is set to be copied to "{1}"'.format( - source, - name + ret["comment"] = 'File "{0}" is set to be copied to "{1}"'.format( + source, name ) - ret['result'] = None + ret["result"] = None else: - ret['comment'] = ('The target file "{0}" exists and will not be ' - 'overwritten'.format(name)) - ret['result'] = True + ret["comment"] = ( + 'The target file "{0}" exists and will not be ' + "overwritten".format(name) + ) + ret["result"] = True return ret if not changed: - ret['comment'] = ('The target file "{0}" exists and will not be ' - 'overwritten'.format(name)) - ret['result'] = True + ret[ + "comment" + ] = 'The target file "{0}" exists and will not be ' "overwritten".format(name) + ret["result"] = True return ret # Run makedirs @@ -6604,43 +6697,37 @@ def copy_(name, try: _makedirs(name=name, user=user, group=group, dir_mode=mode) except CommandExecutionError as exc: - return _error(ret, 'Drive {0} is not mapped'.format(exc.message)) + return _error(ret, "Drive {0} is not mapped".format(exc.message)) else: - return _error( - ret, - 'The target directory {0} is not present'.format(dname)) + return _error(ret, "The target directory {0} is not present".format(dname)) # All tests pass, move the file into place try: if os.path.isdir(source): shutil.copytree(source, name, symlinks=True) for root, dirs, files in salt.utils.path.os_walk(name): for dir_ in dirs: - __salt__['file.lchown'](os.path.join(root, dir_), user, group) + __salt__["file.lchown"](os.path.join(root, dir_), user, group) for file_ in files: - __salt__['file.lchown'](os.path.join(root, file_), user, group) + __salt__["file.lchown"](os.path.join(root, file_), user, group) else: shutil.copy(source, name) - ret['changes'] = {name: source} + ret["changes"] = {name: source} # Preserve really means just keep the behavior of the cp command. If # the filesystem we're copying to is squashed or doesn't support chown # then we shouldn't be checking anything. if not preserve: if salt.utils.platform.is_windows(): # TODO: Add the other win_* parameters to this function - ret = __salt__['file.check_perms']( - path=name, - ret=ret, - owner=user) + ret = __salt__["file.check_perms"](path=name, ret=ret, owner=user) else: - __salt__['file.check_perms'](name, ret, user, group, mode) + __salt__["file.check_perms"](name, ret, user, group, mode) except (IOError, OSError): - return _error( - ret, 'Failed to copy "{0}" to "{1}"'.format(source, name)) + return _error(ret, 'Failed to copy "{0}" to "{1}"'.format(source, name)) return ret def rename(name, source, force=False, makedirs=False): - ''' + """ If the source file exists on the system, rename it to the named file. The named file will not be overwritten if it already exists unless the force option is set to True. @@ -6658,49 +6745,44 @@ def rename(name, source, force=False, makedirs=False): makedirs If the target subdirectories don't exist create them - ''' + """ name = os.path.expanduser(name) source = os.path.expanduser(source) - ret = { - 'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + ret = {"name": name, "changes": {}, "comment": "", "result": True} if not name: - return _error(ret, 'Must provide name to file.rename') + return _error(ret, "Must provide name to file.rename") if not os.path.isabs(name): - return _error( - ret, 'Specified file {0} is not an absolute path'.format(name)) + return _error(ret, "Specified file {0} is not an absolute path".format(name)) if not os.path.lexists(source): - ret['comment'] = ('Source file "{0}" has already been moved out of ' - 'place').format(source) + ret["comment"] = ( + 'Source file "{0}" has already been moved out of ' "place" + ).format(source) return ret if os.path.lexists(source) and os.path.lexists(name): if not force: - ret['comment'] = ('The target file "{0}" exists and will not be ' - 'overwritten'.format(name)) + ret["comment"] = ( + 'The target file "{0}" exists and will not be ' + "overwritten".format(name) + ) return ret - elif not __opts__['test']: + elif not __opts__["test"]: # Remove the destination to prevent problems later try: - __salt__['file.remove'](name) + __salt__["file.remove"](name) except (IOError, OSError): return _error( ret, 'Failed to delete "{0}" in preparation for ' - 'forced move'.format(name) + "forced move".format(name), ) - if __opts__['test']: - ret['comment'] = 'File "{0}" is set to be moved to "{1}"'.format( - source, - name - ) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = 'File "{0}" is set to be moved to "{1}"'.format(source, name) + ret["result"] = None return ret # Run makedirs @@ -6710,11 +6792,9 @@ def rename(name, source, force=False, makedirs=False): try: _makedirs(name=name) except CommandExecutionError as exc: - return _error(ret, 'Drive {0} is not mapped'.format(exc.message)) + return _error(ret, "Drive {0} is not mapped".format(exc.message)) else: - return _error( - ret, - 'The target directory {0} is not present'.format(dname)) + return _error(ret, "The target directory {0} is not present".format(dname)) # All tests pass, move the file into place try: if os.path.islink(source): @@ -6724,16 +6804,15 @@ def rename(name, source, force=False, makedirs=False): else: shutil.move(source, name) except (IOError, OSError): - return _error( - ret, 'Failed to move "{0}" to "{1}"'.format(source, name)) + return _error(ret, 'Failed to move "{0}" to "{1}"'.format(source, name)) - ret['comment'] = 'Moved "{0}" to "{1}"'.format(source, name) - ret['changes'] = {name: source} + ret["comment"] = 'Moved "{0}" to "{1}"'.format(source, name) + ret["changes"] = {name: source} return ret def accumulated(name, filename, text, **kwargs): - ''' + """ Prepare accumulator which can be used in template in file.managed state. Accumulator dictionary becomes available in template. It can also be used in file.blockreplace. @@ -6788,28 +6867,21 @@ def accumulated(name, filename, text, **kwargs): .. note:: The 'accumulator' data structure is a Python dictionary. Do not expect any loop over the keys in a deterministic order! - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not name: - return _error(ret, 'Must provide name to file.accumulated') + return _error(ret, "Must provide name to file.accumulated") if text is None: - ret['result'] = False - ret['comment'] = 'No text supplied for accumulator' + ret["result"] = False + ret["comment"] = "No text supplied for accumulator" return ret - require_in = __low__.get('require_in', []) - watch_in = __low__.get('watch_in', []) + require_in = __low__.get("require_in", []) + watch_in = __low__.get("watch_in", []) deps = require_in + watch_in - if not [x for x in deps if 'file' in x]: - ret['result'] = False - ret['comment'] = 'Orphaned accumulator {0} in {1}:{2}'.format( - name, - __low__['__sls__'], - __low__['__id__'] + if not [x for x in deps if "file" in x]: + ret["result"] = False + ret["comment"] = "Orphaned accumulator {0} in {1}:{2}".format( + name, __low__["__sls__"], __low__["__id__"] ) return ret if isinstance(text, six.string_types): @@ -6830,29 +6902,33 @@ def accumulated(name, filename, text, **kwargs): for chunk in text: if chunk not in accum_data[filename][name]: accum_data[filename][name].append(chunk) - ret['comment'] = ('Accumulator {0} for file {1} ' - 'was charged by text'.format(name, filename)) + ret["comment"] = ( + "Accumulator {0} for file {1} " + "was charged by text".format(name, filename) + ) _persist_accummulators(accum_data, accum_deps) return ret -def serialize(name, - dataset=None, - dataset_pillar=None, - user=None, - group=None, - mode=None, - backup='', - makedirs=False, - show_changes=True, - create=True, - merge_if_exists=False, - encoding=None, - encoding_errors='strict', - serializer_opts=None, - deserializer_opts=None, - **kwargs): - ''' +def serialize( + name, + dataset=None, + dataset_pillar=None, + user=None, + group=None, + mode=None, + backup="", + makedirs=False, + show_changes=True, + create=True, + merge_if_exists=False, + encoding=None, + encoding_errors="strict", + serializer_opts=None, + deserializer_opts=None, + **kwargs +): + """ Serializes dataset and store it into managed file. Useful for sharing simple configuration files. @@ -6873,8 +6949,8 @@ def serialize(name, .. versionadded:: 2015.8.0 formatter - Write the data as this format. See the list of :py:mod:`serializer - modules <salt.serializers>` for supported output formats. + Write the data as this format. See the list of + :ref:`all-salt.serializers` for supported output formats. encoding If specified, then the specified encoding will be used. Otherwise, the @@ -7024,77 +7100,68 @@ def serialize(name, "engine": "node 0.4.1", "name": "naive" } - ''' - if 'env' in kwargs: + """ + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") name = os.path.expanduser(name) # Set some defaults serializer_options = { - 'yaml.serialize': { - 'default_flow_style': False, - }, - 'json.serialize': { - 'indent': 2, - 'separators': (',', ': '), - 'sort_keys': True, - } + "yaml.serialize": {"default_flow_style": False}, + "json.serialize": {"indent": 2, "separators": (",", ": "), "sort_keys": True}, } deserializer_options = { - 'yaml.deserialize': {}, - 'json.deserialize': {}, + "yaml.deserialize": {}, + "json.deserialize": {}, } if encoding: - serializer_options['yaml.serialize'].update({'allow_unicode': True}) - serializer_options['json.serialize'].update({'ensure_ascii': False}) + serializer_options["yaml.serialize"].update({"allow_unicode": True}) + serializer_options["json.serialize"].update({"ensure_ascii": False}) - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + ret = {"changes": {}, "comment": "", "name": name, "result": True} if not name: - return _error(ret, 'Must provide name to file.serialize') + return _error(ret, "Must provide name to file.serialize") if not create: if not os.path.isfile(name): # Don't create a file that is not already present - ret['comment'] = ('File {0} is not present and is not set for ' - 'creation').format(name) + ret["comment"] = ( + "File {0} is not present and is not set for " "creation" + ).format(name) return ret - formatter = kwargs.pop('formatter', 'yaml').lower() + formatter = kwargs.pop("formatter", "yaml").lower() if len([x for x in (dataset, dataset_pillar) if x]) > 1: - return _error( - ret, 'Only one of \'dataset\' and \'dataset_pillar\' is permitted') + return _error(ret, "Only one of 'dataset' and 'dataset_pillar' is permitted") if dataset_pillar: - dataset = __salt__['pillar.get'](dataset_pillar) + dataset = __salt__["pillar.get"](dataset_pillar) if dataset is None: - return _error( - ret, 'Neither \'dataset\' nor \'dataset_pillar\' was defined') + return _error(ret, "Neither 'dataset' nor 'dataset_pillar' was defined") if salt.utils.platform.is_windows(): if group is not None: log.warning( - 'The group argument for %s has been ignored as this ' - 'is a Windows system.', name + "The group argument for %s has been ignored as this " + "is a Windows system.", + name, ) group = user - serializer_name = '{0}.serialize'.format(formatter) - deserializer_name = '{0}.deserialize'.format(formatter) + serializer_name = "{0}.serialize".format(formatter) + deserializer_name = "{0}.deserialize".format(formatter) if serializer_name not in __serializers__: - return {'changes': {}, - 'comment': '{0} format is not supported'.format( - formatter.capitalize()), - 'name': name, - 'result': False - } + return { + "changes": {}, + "comment": "{0} format is not supported".format(formatter.capitalize()), + "name": name, + "result": False, + } if serializer_opts: serializer_options.setdefault(serializer_name, {}).update( @@ -7110,51 +7177,52 @@ def serialize(name, if os.path.isfile(name): if deserializer_name not in __serializers__: return { - 'changes': {}, - 'comment': 'merge_if_exists is not supported for the {0} ' - 'formatter'.format(formatter), - 'name': name, - 'result': False + "changes": {}, + "comment": "merge_if_exists is not supported for the {0} " + "formatter".format(formatter), + "name": name, + "result": False, } - with salt.utils.files.fopen(name, 'r') as fhr: + with salt.utils.files.fopen(name, "r") as fhr: try: existing_data = __serializers__[deserializer_name]( - fhr, - **deserializer_options.get(deserializer_name, {}) + fhr, **deserializer_options.get(deserializer_name, {}) ) except (TypeError, DeserializationError) as exc: - ret['result'] = False - ret['comment'] = \ - 'Failed to deserialize existing data: {0}'.format(exc) + ret["result"] = False + ret["comment"] = "Failed to deserialize existing data: {0}".format( + exc + ) return False if existing_data is not None: - merged_data = salt.utils.dictupdate.merge_recurse(existing_data, dataset) + merged_data = salt.utils.dictupdate.merge_recurse( + existing_data, dataset + ) if existing_data == merged_data: - ret['result'] = True - ret['comment'] = 'The file {0} is in the correct state'.format(name) + ret["result"] = True + ret["comment"] = "The file {0} is in the correct state".format(name) return ret dataset = merged_data else: if deserializer_opts: - ret.setdefault('warnings', []).append( - 'The \'deserializer_opts\' option is ignored unless ' - 'merge_if_exists is set to True.' + ret.setdefault("warnings", []).append( + "The 'deserializer_opts' option is ignored unless " + "merge_if_exists is set to True." ) contents = __serializers__[serializer_name]( - dataset, - **serializer_options.get(serializer_name, {}) + dataset, **serializer_options.get(serializer_name, {}) ) - contents += '\n' + contents += "\n" # Make sure that any leading zeros stripped by YAML loader are added back mode = salt.utils.files.normalize_mode(mode) - if __opts__['test']: - ret['changes'] = __salt__['file.check_managed_changes']( + if __opts__["test"]: + ret["changes"] = __salt__["file.check_managed_changes"]( name=name, source=None, source_hash={}, @@ -7172,39 +7240,42 @@ def serialize(name, **kwargs ) - if ret['changes']: - ret['result'] = None - ret['comment'] = 'Dataset will be serialized and stored into {0}'.format( - name) + if ret["changes"]: + ret["result"] = None + ret["comment"] = "Dataset will be serialized and stored into {0}".format( + name + ) if not show_changes: - ret['changes']['diff'] = '<show_changes=False>' + ret["changes"]["diff"] = "<show_changes=False>" else: - ret['result'] = True - ret['comment'] = 'The file {0} is in the correct state'.format(name) + ret["result"] = True + ret["comment"] = "The file {0} is in the correct state".format(name) return ret - return __salt__['file.manage_file'](name=name, - sfn='', - ret=ret, - source=None, - source_sum={}, - user=user, - group=group, - mode=mode, - attrs=None, - saltenv=__env__, - backup=backup, - makedirs=makedirs, - template=None, - show_changes=show_changes, - encoding=encoding, - encoding_errors=encoding_errors, - contents=contents) + return __salt__["file.manage_file"]( + name=name, + sfn="", + ret=ret, + source=None, + source_sum={}, + user=user, + group=group, + mode=mode, + attrs=None, + saltenv=__env__, + backup=backup, + makedirs=makedirs, + template=None, + show_changes=show_changes, + encoding=encoding, + encoding_errors=encoding_errors, + contents=contents, + ) -def mknod(name, ntype, major=0, minor=0, user=None, group=None, mode='0600'): - ''' +def mknod(name, ntype, major=0, minor=0, user=None, group=None, mode="0600"): + """ Create a special file similar to the 'nix mknod command. The supported device types are ``p`` (fifo pipe), ``c`` (character device), and ``b`` (block device). Provide the major and minor numbers when specifying a @@ -7272,171 +7343,140 @@ def mknod(name, ntype, major=0, minor=0, user=None, group=None, mode='0600'): - mode: 660 .. versionadded:: 0.17.0 - ''' + """ name = os.path.expanduser(name) - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + ret = {"name": name, "changes": {}, "comment": "", "result": False} if not name: - return _error(ret, 'Must provide name to file.mknod') + return _error(ret, "Must provide name to file.mknod") - if ntype == 'c': + if ntype == "c": # Check for file existence - if __salt__['file.file_exists'](name): - ret['comment'] = ( - 'File {0} exists and is not a character device. Refusing ' - 'to continue'.format(name) + if __salt__["file.file_exists"](name): + ret["comment"] = ( + "File {0} exists and is not a character device. Refusing " + "to continue".format(name) ) # Check if it is a character device - elif not __salt__['file.is_chrdev'](name): - if __opts__['test']: - ret['comment'] = \ - 'Character device {0} is set to be created'.format(name) - ret['result'] = None - else: - ret = __salt__['file.mknod'](name, - ntype, - major, - minor, - user, - group, - mode) - - # Check the major/minor - else: - devmaj, devmin = __salt__['file.get_devmm'](name) - if (major, minor) != (devmaj, devmin): - ret['comment'] = ( - 'Character device {0} exists and has a different ' - 'major/minor {1}/{2}. Refusing to continue' - .format(name, devmaj, devmin) + elif not __salt__["file.is_chrdev"](name): + if __opts__["test"]: + ret["comment"] = "Character device {0} is set to be created".format( + name ) - # Check the perms + ret["result"] = None else: - ret = __salt__['file.check_perms'](name, - None, - user, - group, - mode)[0] - if not ret['changes']: - ret['comment'] = ( - 'Character device {0} is in the correct state'.format( - name - ) - ) - - elif ntype == 'b': - # Check for file existence - if __salt__['file.file_exists'](name): - ret['comment'] = ( - 'File {0} exists and is not a block device. Refusing to ' - 'continue'.format(name) - ) - - # Check if it is a block device - elif not __salt__['file.is_blkdev'](name): - if __opts__['test']: - ret['comment'] = 'Block device {0} is set to be created'.format(name) - ret['result'] = None - else: - ret = __salt__['file.mknod'](name, - ntype, - major, - minor, - user, - group, - mode) + ret = __salt__["file.mknod"]( + name, ntype, major, minor, user, group, mode + ) # Check the major/minor else: - devmaj, devmin = __salt__['file.get_devmm'](name) + devmaj, devmin = __salt__["file.get_devmm"](name) if (major, minor) != (devmaj, devmin): - ret['comment'] = ( - 'Block device {0} exists and has a different major/minor ' - '{1}/{2}. Refusing to continue'.format( + ret["comment"] = ( + "Character device {0} exists and has a different " + "major/minor {1}/{2}. Refusing to continue".format( name, devmaj, devmin ) ) # Check the perms else: - ret = __salt__['file.check_perms'](name, - None, - user, - group, - mode)[0] - if not ret['changes']: - ret['comment'] = ( - 'Block device {0} is in the correct state'.format(name) + ret = __salt__["file.check_perms"](name, None, user, group, mode)[0] + if not ret["changes"]: + ret[ + "comment" + ] = "Character device {0} is in the correct state".format(name) + + elif ntype == "b": + # Check for file existence + if __salt__["file.file_exists"](name): + ret["comment"] = ( + "File {0} exists and is not a block device. Refusing to " + "continue".format(name) + ) + + # Check if it is a block device + elif not __salt__["file.is_blkdev"](name): + if __opts__["test"]: + ret["comment"] = "Block device {0} is set to be created".format(name) + ret["result"] = None + else: + ret = __salt__["file.mknod"]( + name, ntype, major, minor, user, group, mode + ) + + # Check the major/minor + else: + devmaj, devmin = __salt__["file.get_devmm"](name) + if (major, minor) != (devmaj, devmin): + ret["comment"] = ( + "Block device {0} exists and has a different major/minor " + "{1}/{2}. Refusing to continue".format(name, devmaj, devmin) + ) + # Check the perms + else: + ret = __salt__["file.check_perms"](name, None, user, group, mode)[0] + if not ret["changes"]: + ret["comment"] = "Block device {0} is in the correct state".format( + name ) - elif ntype == 'p': + elif ntype == "p": # Check for file existence - if __salt__['file.file_exists'](name): - ret['comment'] = ( - 'File {0} exists and is not a fifo pipe. Refusing to ' - 'continue'.format(name) + if __salt__["file.file_exists"](name): + ret["comment"] = ( + "File {0} exists and is not a fifo pipe. Refusing to " + "continue".format(name) ) # Check if it is a fifo - elif not __salt__['file.is_fifo'](name): - if __opts__['test']: - ret['comment'] = 'Fifo pipe {0} is set to be created'.format( - name - ) - ret['result'] = None + elif not __salt__["file.is_fifo"](name): + if __opts__["test"]: + ret["comment"] = "Fifo pipe {0} is set to be created".format(name) + ret["result"] = None else: - ret = __salt__['file.mknod'](name, - ntype, - major, - minor, - user, - group, - mode) + ret = __salt__["file.mknod"]( + name, ntype, major, minor, user, group, mode + ) # Check the perms else: - ret = __salt__['file.check_perms'](name, - None, - user, - group, - mode)[0] - if not ret['changes']: - ret['comment'] = ( - 'Fifo pipe {0} is in the correct state'.format(name) - ) + ret = __salt__["file.check_perms"](name, None, user, group, mode)[0] + if not ret["changes"]: + ret["comment"] = "Fifo pipe {0} is in the correct state".format(name) else: - ret['comment'] = ( - 'Node type unavailable: \'{0}\'. Available node types are ' - 'character (\'c\'), block (\'b\'), and pipe (\'p\')'.format(ntype) + ret["comment"] = ( + "Node type unavailable: '{0}'. Available node types are " + "character ('c'), block ('b'), and pipe ('p')".format(ntype) ) return ret def mod_run_check_cmd(cmd, filename, **check_cmd_opts): - ''' + """ Execute the check_cmd logic. Return a result dict if ``check_cmd`` succeeds (check_cmd == 0) otherwise return True - ''' + """ - log.debug('running our check_cmd') - _cmd = '{0} {1}'.format(cmd, filename) - cret = __salt__['cmd.run_all'](_cmd, **check_cmd_opts) - if cret['retcode'] != 0: - ret = {'comment': 'check_cmd execution failed', - 'skip_watch': True, - 'result': False} + log.debug("running our check_cmd") + _cmd = "{0} {1}".format(cmd, filename) + cret = __salt__["cmd.run_all"](_cmd, **check_cmd_opts) + if cret["retcode"] != 0: + ret = { + "comment": "check_cmd execution failed", + "skip_watch": True, + "result": False, + } - if cret.get('stdout'): - ret['comment'] += '\n' + cret['stdout'] - if cret.get('stderr'): - ret['comment'] += '\n' + cret['stderr'] + if cret.get("stdout"): + ret["comment"] += "\n" + cret["stdout"] + if cret.get("stderr"): + ret["comment"] += "\n" + cret["stderr"] return ret @@ -7444,12 +7484,14 @@ def mod_run_check_cmd(cmd, filename, **check_cmd_opts): return True -def decode(name, - encoded_data=None, - contents_pillar=None, - encoding_type='base64', - checksum='md5'): - ''' +def decode( + name, + encoded_data=None, + contents_pillar=None, + encoding_type="base64", + checksum="md5", +): + """ Decode an encoded file and write it to disk .. versionadded:: 2016.3.0 @@ -7503,73 +7545,76 @@ def decode(name, - encoding_type: base64 - encoded_data: | {{ salt.pillar.get('path:to:data') | indent(8) }} - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if not (encoded_data or contents_pillar): - raise CommandExecutionError("Specify either the 'encoded_data' or " - "'contents_pillar' argument.") + raise CommandExecutionError( + "Specify either the 'encoded_data' or " "'contents_pillar' argument." + ) elif encoded_data and contents_pillar: - raise CommandExecutionError("Specify only one 'encoded_data' or " - "'contents_pillar' argument.") + raise CommandExecutionError( + "Specify only one 'encoded_data' or " "'contents_pillar' argument." + ) elif encoded_data: content = encoded_data elif contents_pillar: - content = __salt__['pillar.get'](contents_pillar, False) + content = __salt__["pillar.get"](contents_pillar, False) if content is False: - raise CommandExecutionError('Pillar data not found.') + raise CommandExecutionError("Pillar data not found.") else: - raise CommandExecutionError('No contents given.') + raise CommandExecutionError("No contents given.") - dest_exists = __salt__['file.file_exists'](name) + dest_exists = __salt__["file.file_exists"](name) if dest_exists: - instr = __salt__['hashutil.base64_decodestring'](content) - insum = __salt__['hashutil.digest'](instr, checksum) + instr = __salt__["hashutil.base64_decodestring"](content) + insum = __salt__["hashutil.digest"](instr, checksum) del instr # no need to keep in-memory after we have the hash - outsum = __salt__['hashutil.digest_file'](name, checksum) + outsum = __salt__["hashutil.digest_file"](name, checksum) if insum != outsum: - ret['changes'] = { - 'old': outsum, - 'new': insum, + ret["changes"] = { + "old": outsum, + "new": insum, } - if not ret['changes']: - ret['comment'] = 'File is in the correct state.' - ret['result'] = True + if not ret["changes"]: + ret["comment"] = "File is in the correct state." + ret["result"] = True return ret - if __opts__['test'] is True: - ret['comment'] = 'File is set to be updated.' - ret['result'] = None + if __opts__["test"] is True: + ret["comment"] = "File is set to be updated." + ret["result"] = None return ret - ret['result'] = __salt__['hashutil.base64_decodefile'](content, name) - ret['comment'] = 'File was updated.' + ret["result"] = __salt__["hashutil.base64_decodefile"](content, name) + ret["comment"] = "File was updated." - if not ret['changes']: - ret['changes'] = { - 'old': None, - 'new': __salt__['hashutil.digest_file'](name, checksum), + if not ret["changes"]: + ret["changes"] = { + "old": None, + "new": __salt__["hashutil.digest_file"](name, checksum), } return ret def shortcut( - name, - target, - arguments=None, - working_dir=None, - description=None, - icon_location=None, - force=False, - backupname=None, - makedirs=False, - user=None, - **kwargs): - ''' + name, + target, + arguments=None, + working_dir=None, + description=None, + icon_location=None, + force=False, + backupname=None, + makedirs=False, + user=None, + **kwargs +): + """ Create a Windows shortcut If the file already exists and is a shortcut pointing to any location other @@ -7622,23 +7667,20 @@ def shortcut( The default mode for new files and directories corresponds umask of salt process. For existing files and directories it's not enforced. - ''' + """ user = _test_owner(kwargs, user=user) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not salt.utils.platform.is_windows(): - return _error(ret, 'Shortcuts are only supported on Windows') + return _error(ret, "Shortcuts are only supported on Windows") if not name: - return _error(ret, 'Must provide name to file.shortcut') - if not name.endswith('.lnk') and not name.endswith('.url'): + return _error(ret, "Must provide name to file.shortcut") + if not name.endswith(".lnk") and not name.endswith(".url"): return _error(ret, 'Name must end with either ".lnk" or ".url"') # Normalize paths; do this after error checks to avoid invalid input # getting expanded, e.g. '' turning into '.' name = os.path.realpath(os.path.expanduser(name)) - if name.endswith('.lnk'): + if name.endswith(".lnk"): target = os.path.realpath(os.path.expanduser(target)) if working_dir: working_dir = os.path.realpath(os.path.expanduser(working_dir)) @@ -7646,46 +7688,41 @@ def shortcut( icon_location = os.path.realpath(os.path.expanduser(icon_location)) if user is None: - user = __opts__['user'] + user = __opts__["user"] # Make sure the user exists in Windows # Salt default is 'root' - if not __salt__['user.info'](user): + if not __salt__["user.info"](user): # User not found, use the account salt is running under # If username not found, use System - user = __salt__['user.current']() + user = __salt__["user.current"]() if not user: - user = 'SYSTEM' + user = "SYSTEM" preflight_errors = [] - uid = __salt__['file.user_to_uid'](user) + uid = __salt__["file.user_to_uid"](user) - if uid == '': - preflight_errors.append('User {0} does not exist'.format(user)) + if uid == "": + preflight_errors.append("User {0} does not exist".format(user)) if not os.path.isabs(name): preflight_errors.append( - 'Specified file {0} is not an absolute path'.format(name) + "Specified file {0} is not an absolute path".format(name) ) if preflight_errors: - msg = '. '.join(preflight_errors) + msg = ". ".join(preflight_errors) if len(preflight_errors) > 1: - msg += '.' + msg += "." return _error(ret, msg) - presult, pcomment, pchanges = _shortcut_check(name, - target, - arguments, - working_dir, - description, - icon_location, - force, - user) - if __opts__['test']: - ret['result'] = presult - ret['comment'] = pcomment - ret['changes'] = pchanges + presult, pcomment, pchanges = _shortcut_check( + name, target, arguments, working_dir, description, icon_location, force, user + ) + if __opts__["test"]: + ret["result"] = presult + ret["comment"] = pcomment + ret["changes"] = pchanges return ret if not os.path.isdir(os.path.dirname(name)): @@ -7693,13 +7730,13 @@ def shortcut( try: _makedirs(name=name, user=user) except CommandExecutionError as exc: - return _error(ret, 'Drive {0} is not mapped'.format(exc.message)) + return _error(ret, "Drive {0} is not mapped".format(exc.message)) else: return _error( ret, 'Directory "{0}" for shortcut is not present'.format( os.path.dirname(name) - ) + ), ) if os.path.isdir(name) or os.path.islink(name): @@ -7708,36 +7745,50 @@ def shortcut( # Make a backup first if os.path.lexists(backupname): if not force: - return _error(ret, (( - 'File exists where the backup target {0} should go' - ).format(backupname))) + return _error( + ret, + ( + ( + "File exists where the backup target {0} should go" + ).format(backupname) + ), + ) else: - __salt__['file.remove'](backupname) + __salt__["file.remove"](backupname) time.sleep(1) # wait for asynchronous deletion if not os.path.isdir(os.path.dirname(backupname)): if makedirs: try: _makedirs(name=backupname) except CommandExecutionError as exc: - return _error(ret, 'Drive {0} is not mapped'.format(exc.message)) + return _error( + ret, "Drive {0} is not mapped".format(exc.message) + ) else: - return _error(ret, ( - 'Directory does not exist for' - ' backup at "{0}"' - ).format(os.path.dirname(backupname))) + return _error( + ret, + ("Directory does not exist for" ' backup at "{0}"').format( + os.path.dirname(backupname) + ), + ) os.rename(name, backupname) time.sleep(1) # wait for asynchronous rename elif force: # Remove whatever is in the way - __salt__['file.remove'](name) - ret['changes']['forced'] = 'Shortcut was forcibly replaced' + __salt__["file.remove"](name) + ret["changes"]["forced"] = "Shortcut was forcibly replaced" time.sleep(1) # wait for asynchronous deletion else: # Otherwise throw an error - return _error(ret, (( - 'Directory or symlink exists where the' - ' shortcut "{0}" should be' - ).format(name))) + return _error( + ret, + ( + ( + "Directory or symlink exists where the" + ' shortcut "{0}" should be' + ).format(name) + ), + ) # This will just load the shortcut if it already exists # It won't create the file until calling scut.Save() @@ -7751,15 +7802,13 @@ def shortcut( if arguments is not None: state_checks.append(scut.Arguments == arguments) if working_dir is not None: - state_checks.append( - scut.WorkingDirectory.lower() == working_dir.lower() - ) + state_checks.append(scut.WorkingDirectory.lower() == working_dir.lower()) if description is not None: state_checks.append(scut.Description == description) if icon_location is not None: state_checks.append(scut.IconLocation.lower() == icon_location.lower()) - if __salt__['file.file_exists'](name): + if __salt__["file.file_exists"](name): # The shortcut exists, verify that it matches the desired state if not all(state_checks): # The target is wrong, delete it @@ -7767,18 +7816,20 @@ def shortcut( else: if _check_shortcut_ownership(name, user): # The shortcut looks good! - ret['comment'] = ('Shortcut {0} is present and owned by ' - '{1}'.format(name, user)) + ret["comment"] = "Shortcut {0} is present and owned by " "{1}".format( + name, user + ) else: if _set_shortcut_ownership(name, user): - ret['comment'] = ('Set ownership of shortcut {0} to ' - '{1}'.format(name, user)) - ret['changes']['ownership'] = '{0}'.format(user) + ret["comment"] = "Set ownership of shortcut {0} to " "{1}".format( + name, user + ) + ret["changes"]["ownership"] = "{0}".format(user) else: - ret['result'] = False - ret['comment'] += ( - 'Failed to set ownership of shortcut {0} to ' - '{1}'.format(name, user) + ret["result"] = False + ret["comment"] += ( + "Failed to set ownership of shortcut {0} to " + "{1}".format(name, user) ) return ret @@ -7796,29 +7847,28 @@ def shortcut( scut.IconLocation = icon_location scut.Save() except (AttributeError, pywintypes.com_error) as exc: - ret['result'] = False - ret['comment'] = ('Unable to create new shortcut {0} -> ' - '{1}: {2}'.format(name, target, exc)) + ret["result"] = False + ret["comment"] = "Unable to create new shortcut {0} -> " "{1}: {2}".format( + name, target, exc + ) return ret else: - ret['comment'] = ('Created new shortcut {0} -> ' - '{1}'.format(name, target)) - ret['changes']['new'] = name + ret["comment"] = "Created new shortcut {0} -> " "{1}".format(name, target) + ret["changes"]["new"] = name if not _check_shortcut_ownership(name, user): if not _set_shortcut_ownership(name, user): - ret['result'] = False - ret['comment'] += (', but was unable to set ownership to ' - '{0}'.format(user)) + ret["result"] = False + ret["comment"] += ", but was unable to set ownership to " "{0}".format( + user + ) return ret -def cached(name, - source_hash='', - source_hash_name=None, - skip_verify=False, - saltenv='base'): - ''' +def cached( + name, source_hash="", source_hash_name=None, skip_verify=False, saltenv="base" +): + """ .. versionadded:: 2017.7.3 Ensures that a file is saved to the minion's cache. This state is primarily @@ -7894,28 +7944,28 @@ def cached(name, This function will return the cached path of the file, or an empty string if the file is not present in the minion cache. - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': False} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": False} try: parsed = _urlparse(name) except Exception: # pylint: disable=broad-except - ret['comment'] = 'Only URLs or local file paths are valid input' + ret["comment"] = "Only URLs or local file paths are valid input" return ret # This if statement will keep the state from proceeding if a remote source # is specified and no source_hash is presented (unless we're skipping hash # verification). - if not skip_verify \ - and not source_hash \ - and parsed.scheme in salt.utils.files.REMOTE_PROTOS: - ret['comment'] = ( - 'Unable to verify upstream hash of source file {0}, please set ' - 'source_hash or set skip_verify to True'.format( - salt.utils.url.redact_http_basic_auth(name)) + if ( + not skip_verify + and not source_hash + and parsed.scheme in salt.utils.files.REMOTE_PROTOS + ): + ret["comment"] = ( + "Unable to verify upstream hash of source file {0}, please set " + "source_hash or set skip_verify to True".format( + salt.utils.url.redact_http_basic_auth(name) + ) ) return ret @@ -7924,13 +7974,14 @@ def cached(name, # the hash out of a file containing checksums, if that is how the # source_hash was specified. try: - source_sum = __salt__['file.get_source_sum']( + source_sum = __salt__["file.get_source_sum"]( source=name, source_hash=source_hash, source_hash_name=source_hash_name, - saltenv=saltenv) + saltenv=saltenv, + ) except CommandExecutionError as exc: - ret['comment'] = exc.strerror + ret["comment"] = exc.strerror return ret else: if not source_sum: @@ -7939,10 +7990,10 @@ def cached(name, # being raised, which we catch above. Nevertheless, we should # provide useful information in the event that # file.get_source_sum regresses. - ret['comment'] = ( - 'Failed to get source hash from {0}. This may be a bug. ' - 'If this error persists, please report it and set ' - 'skip_verify to True to work around it.'.format(source_hash) + ret["comment"] = ( + "Failed to get source hash from {0}. This may be a bug. " + "If this error persists, please report it and set " + "skip_verify to True to work around it.".format(source_hash) ) return ret else: @@ -7954,65 +8005,59 @@ def cached(name, if os.path.exists(full_path): if not skip_verify and source_sum: # Enforce the hash - local_hash = __salt__['file.get_hash']( - full_path, - source_sum.get('hash_type', __opts__['hash_type'])) - if local_hash == source_sum['hsum']: - ret['result'] = True - ret['comment'] = ( - 'File {0} is present on the minion and has hash ' - '{1}'.format(full_path, local_hash) + local_hash = __salt__["file.get_hash"]( + full_path, source_sum.get("hash_type", __opts__["hash_type"]) + ) + if local_hash == source_sum["hsum"]: + ret["result"] = True + ret["comment"] = ( + "File {0} is present on the minion and has hash " + "{1}".format(full_path, local_hash) ) else: - ret['comment'] = ( - 'File {0} is present on the minion, but the hash ({1}) ' - 'does not match the specified hash ({2})'.format( - full_path, local_hash, source_sum['hsum'] + ret["comment"] = ( + "File {0} is present on the minion, but the hash ({1}) " + "does not match the specified hash ({2})".format( + full_path, local_hash, source_sum["hsum"] ) ) return ret else: - ret['result'] = True - ret['comment'] = 'File {0} is present on the minion'.format( - full_path - ) + ret["result"] = True + ret["comment"] = "File {0} is present on the minion".format(full_path) return ret else: - ret['comment'] = 'File {0} is not present on the minion'.format( - full_path - ) + ret["comment"] = "File {0} is not present on the minion".format(full_path) return ret - local_copy = __salt__['cp.is_cached'](name, saltenv=saltenv) + local_copy = __salt__["cp.is_cached"](name, saltenv=saltenv) if local_copy: # File is already cached - pre_hash = __salt__['file.get_hash']( - local_copy, - source_sum.get('hash_type', __opts__['hash_type'])) + pre_hash = __salt__["file.get_hash"]( + local_copy, source_sum.get("hash_type", __opts__["hash_type"]) + ) if not skip_verify and source_sum: # Get the local copy's hash to compare with the hash that was # specified via source_hash. If it matches, we can exit early from # the state without going any further, because the file is cached # with the correct hash. - if pre_hash == source_sum['hsum']: - ret['result'] = True - ret['comment'] = ( - 'File is already cached to {0} with hash {1}'.format( - local_copy, pre_hash - ) + if pre_hash == source_sum["hsum"]: + ret["result"] = True + ret["comment"] = "File is already cached to {0} with hash {1}".format( + local_copy, pre_hash ) else: pre_hash = None def _try_cache(path, checksum): - ''' + """ This helper is not needed anymore in develop as the fileclient in the develop branch now has means of skipping a download if the existing hash matches one passed to cp.cache_file. Remove this helper and the code that invokes it, once we have merged forward into develop. - ''' + """ if not path or not checksum: return True form = salt.utils.files.HASHES_REVMAP.get(len(checksum)) @@ -8036,63 +8081,59 @@ def cached(name, # matches the cached copy. # Remote, non salt:// sources _will_ download if a copy of the file was # not already present in the minion cache. - if _try_cache(local_copy, source_sum.get('hsum')): + if _try_cache(local_copy, source_sum.get("hsum")): # The _try_cache helper is obsolete in the develop branch. Once merged # forward, remove the helper as well as this if statement, and dedent # the below block. try: - local_copy = __salt__['cp.cache_file']( - name, - saltenv=saltenv, - source_hash=source_sum.get('hsum')) + local_copy = __salt__["cp.cache_file"]( + name, saltenv=saltenv, source_hash=source_sum.get("hsum") + ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = salt.utils.url.redact_http_basic_auth(exc.__str__()) + ret["comment"] = salt.utils.url.redact_http_basic_auth(exc.__str__()) return ret if not local_copy: - ret['comment'] = ( - 'Failed to cache {0}, check minion log for more ' - 'information'.format( - salt.utils.url.redact_http_basic_auth(name)) + ret["comment"] = ( + "Failed to cache {0}, check minion log for more " + "information".format(salt.utils.url.redact_http_basic_auth(name)) ) return ret - post_hash = __salt__['file.get_hash']( - local_copy, - source_sum.get('hash_type', __opts__['hash_type'])) + post_hash = __salt__["file.get_hash"]( + local_copy, source_sum.get("hash_type", __opts__["hash_type"]) + ) if pre_hash != post_hash: - ret['changes']['hash'] = {'old': pre_hash, 'new': post_hash} + ret["changes"]["hash"] = {"old": pre_hash, "new": post_hash} # Check the hash, if we're enforcing one. Note that this will be the first # hash check if the file was not previously cached, and the 2nd hash check # if it was cached and the if not skip_verify and source_sum: - if post_hash == source_sum['hsum']: - ret['result'] = True - ret['comment'] = ( - 'File is already cached to {0} with hash {1}'.format( - local_copy, post_hash - ) + if post_hash == source_sum["hsum"]: + ret["result"] = True + ret["comment"] = "File is already cached to {0} with hash {1}".format( + local_copy, post_hash ) else: - ret['comment'] = ( - 'File is cached to {0}, but the hash ({1}) does not match ' - 'the specified hash ({2})'.format( - local_copy, post_hash, source_sum['hsum'] + ret["comment"] = ( + "File is cached to {0}, but the hash ({1}) does not match " + "the specified hash ({2})".format( + local_copy, post_hash, source_sum["hsum"] ) ) return ret # We're not enforcing a hash, and we already know that the file was # successfully cached, so we know the state was successful. - ret['result'] = True - ret['comment'] = 'File is cached to {0}'.format(local_copy) + ret["result"] = True + ret["comment"] = "File is cached to {0}".format(local_copy) return ret -def not_cached(name, saltenv='base'): - ''' +def not_cached(name, saltenv="base"): + """ .. versionadded:: 2017.7.3 Ensures that a file is saved to the minion's cache. This state is primarily @@ -8111,42 +8152,37 @@ def not_cached(name, saltenv='base'): saltenv Used to specify the environment from which to download a file from the Salt fileserver (i.e. those with ``salt://`` URL). - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': False} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": False} try: parsed = _urlparse(name) except Exception: # pylint: disable=broad-except - ret['comment'] = 'Only URLs or local file paths are valid input' + ret["comment"] = "Only URLs or local file paths are valid input" return ret else: if parsed.scheme in salt.utils.files.LOCAL_PROTOS: full_path = os.path.realpath(os.path.expanduser(parsed.path)) - ret['result'] = True - ret['comment'] = ( - 'File {0} is a local path, no action taken'.format( - full_path - ) + ret["result"] = True + ret["comment"] = "File {0} is a local path, no action taken".format( + full_path ) return ret - local_copy = __salt__['cp.is_cached'](name, saltenv=saltenv) + local_copy = __salt__["cp.is_cached"](name, saltenv=saltenv) if local_copy: try: os.remove(local_copy) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = 'Failed to delete {0}: {1}'.format( + ret["comment"] = "Failed to delete {0}: {1}".format( local_copy, exc.__str__() ) else: - ret['result'] = True - ret['changes']['deleted'] = True - ret['comment'] = '{0} was deleted'.format(local_copy) + ret["result"] = True + ret["changes"]["deleted"] = True + ret["comment"] = "{0} was deleted".format(local_copy) else: - ret['result'] = True - ret['comment'] = '{0} is not cached'.format(name) + ret["result"] = True + ret["comment"] = "{0} is not cached".format(name) return ret diff --git a/salt/states/firewall.py b/salt/states/firewall.py index bc1e5b5c71f..563ff0779a8 100644 --- a/salt/states/firewall.py +++ b/salt/states/firewall.py @@ -1,24 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" State to check firewall configurations .. versionadded:: 2016.3.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - - ''' + """ Load only if network is loaded - ''' - - return 'firewall' if 'network.connect' in __salt__ else False + """ + if "network.connect" in __salt__: + return "firewall" + return (False, "network module could not be loaded") def check(name, port=None, **kwargs): - - ''' + """ Checks if there is an open connection from the minion to the defined host on a specific port. @@ -42,26 +41,23 @@ def check(name, port=None, **kwargs): - port: 80 - proto: 'tcp' - ''' + """ # set name to host as required by the module host = name - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - if 'test' not in kwargs: - kwargs['test'] = __opts__.get('test', False) + if "test" not in kwargs: + kwargs["test"] = __opts__.get("test", False) # check the connection - if kwargs['test']: - ret['result'] = True - ret['comment'] = 'The connection will be tested' + if kwargs["test"]: + ret["result"] = True + ret["comment"] = "The connection will be tested" else: - results = __salt__['network.connect'](host, port, **kwargs) - ret['result'] = results['result'] - ret['comment'] = results['comment'] + results = __salt__["network.connect"](host, port, **kwargs) + ret["result"] = results["result"] + ret["comment"] = results["comment"] return ret diff --git a/salt/states/firewalld.py b/salt/states/firewalld.py index 25cbad170a0..4114e99f430 100644 --- a/salt/states/firewalld.py +++ b/salt/states/firewalld.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of firewalld .. versionadded:: 2015.8.0 @@ -74,25 +74,28 @@ would allow access to the salt master from the 10.0.0.0/8 subnet: - saltmaster - sources: - 10.0.0.0/8 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.path + # Import Salt Libs from salt.exceptions import CommandExecutionError from salt.output import nested -import salt.utils.path log = logging.getLogger(__name__) class ForwardingMapping(object): - ''' + """ Represents a port forwarding statement mapping a local port to a remote port for a specific protocol (TCP or UDP) - ''' + """ + def __init__(self, srcport, destport, protocol, destaddr): self.srcport = srcport self.destport = destport @@ -100,77 +103,87 @@ class ForwardingMapping(object): self.destaddr = destaddr def __eq__(self, other): - return (self.srcport == other.srcport and - self.destport == other.destport and - self.protocol == other.protocol and - self.destaddr == other.destaddr) + return ( + self.srcport == other.srcport + and self.destport == other.destport + and self.protocol == other.protocol + and self.destaddr == other.destaddr + ) def __ne__(self, other): return not self.__eq__(other) # hash is needed for set operations def __hash__(self): - return (hash(self.srcport) ^ - hash(self.destport) ^ - hash(self.protocol) ^ - hash(self.destaddr)) + return ( + hash(self.srcport) + ^ hash(self.destport) + ^ hash(self.protocol) + ^ hash(self.destaddr) + ) def todict(self): - ''' + """ Returns a pretty dictionary meant for command line output. - ''' + """ return { - 'Source port': self.srcport, - 'Destination port': self.destport, - 'Protocol': self.protocol, - 'Destination address': self.destaddr} + "Source port": self.srcport, + "Destination port": self.destport, + "Protocol": self.protocol, + "Destination address": self.destaddr, + } def _parse_forward(mapping): - ''' + """ Parses a port forwarding statement in the form used by this state: from_port:to_port:protocol[:destination] and returns a ForwardingMapping object - ''' - if len(mapping.split(':')) > 3: - (srcport, destport, protocol, destaddr) = mapping.split(':') + """ + if len(mapping.split(":")) > 3: + (srcport, destport, protocol, destaddr) = mapping.split(":") else: - (srcport, destport, protocol) = mapping.split(':') - destaddr = '' + (srcport, destport, protocol) = mapping.split(":") + destaddr = "" return ForwardingMapping(srcport, destport, protocol, destaddr) def __virtual__(): - ''' + """ Ensure the firewall-cmd is available - ''' - if salt.utils.path.which('firewall-cmd'): + """ + if salt.utils.path.which("firewall-cmd"): return True - return (False, 'firewall-cmd is not available, firewalld is probably not installed.') + return ( + False, + "firewall-cmd is not available, firewalld is probably not installed.", + ) -def present(name, - block_icmp=None, - prune_block_icmp=False, - default=None, - masquerade=False, - ports=None, - prune_ports=False, - port_fwd=None, - prune_port_fwd=False, - services=None, - prune_services=False, - interfaces=None, - prune_interfaces=False, - sources=None, - prune_sources=False, - rich_rules=None, - prune_rich_rules=False): +def present( + name, + block_icmp=None, + prune_block_icmp=False, + default=None, + masquerade=False, + ports=None, + prune_ports=False, + port_fwd=None, + prune_port_fwd=False, + services=None, + prune_services=False, + interfaces=None, + prune_interfaces=False, + sources=None, + prune_sources=False, + rich_rules=None, + prune_rich_rules=False, +): - ''' + """ Ensure a zone has specific attributes. name @@ -224,161 +237,166 @@ def present(name, prune_rich_rules : False If ``True``, remove all but the specified rich rules from the zone. - ''' - ret = _present(name, block_icmp, prune_block_icmp, default, masquerade, ports, prune_ports, - port_fwd, prune_port_fwd, services, prune_services, interfaces, prune_interfaces, - sources, prune_sources, rich_rules, prune_rich_rules) + """ + ret = _present( + name, + block_icmp, + prune_block_icmp, + default, + masquerade, + ports, + prune_ports, + port_fwd, + prune_port_fwd, + services, + prune_services, + interfaces, + prune_interfaces, + sources, + prune_sources, + rich_rules, + prune_rich_rules, + ) # Reload firewalld service on changes - if ret['changes'] != {}: - __salt__['firewalld.reload_rules']() + if ret["changes"] != {}: + __salt__["firewalld.reload_rules"]() return ret -def service(name, - ports=None, - protocols=None): - ''' +def service(name, ports=None, protocols=None): + """ Ensure the service exists and encompasses the specified ports and protocols. .. versionadded:: 2016.11.0 - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - if name not in __salt__['firewalld.get_services'](): - __salt__['firewalld.new_service'](name, restart=False) + if name not in __salt__["firewalld.get_services"](): + __salt__["firewalld.new_service"](name, restart=False) ports = ports or [] try: - _current_ports = __salt__['firewalld.get_service_ports'](name) + _current_ports = __salt__["firewalld.get_service_ports"](name) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret new_ports = set(ports) - set(_current_ports) old_ports = set(_current_ports) - set(ports) for port in new_ports: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.add_service_port'](name, port) + __salt__["firewalld.add_service_port"](name, port) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret for port in old_ports: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.remove_service_port'](name, port) + __salt__["firewalld.remove_service_port"](name, port) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if new_ports or old_ports: - ret['changes'].update({'ports': - {'old': _current_ports, - 'new': ports}}) + ret["changes"].update({"ports": {"old": _current_ports, "new": ports}}) protocols = protocols or [] try: - _current_protocols = __salt__['firewalld.get_service_protocols'](name) + _current_protocols = __salt__["firewalld.get_service_protocols"](name) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret new_protocols = set(protocols) - set(_current_protocols) old_protocols = set(_current_protocols) - set(protocols) for protocol in new_protocols: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.add_service_protocol'](name, protocol) + __salt__["firewalld.add_service_protocol"](name, protocol) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret for protocol in old_protocols: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.remove_service_protocol'](name, protocol) + __salt__["firewalld.remove_service_protocol"](name, protocol) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if new_protocols or old_protocols: - ret['changes'].update({'protocols': - {'old': _current_protocols, - 'new': protocols}}) + ret["changes"].update( + {"protocols": {"old": _current_protocols, "new": protocols}} + ) - if ret['changes'] != {}: - __salt__['firewalld.reload_rules']() + if ret["changes"] != {}: + __salt__["firewalld.reload_rules"]() - ret['result'] = True - if ret['changes'] == {}: - ret['comment'] = '\'{0}\' is already in the desired state.'.format( - name) + ret["result"] = True + if ret["changes"] == {}: + ret["comment"] = "'{0}' is already in the desired state.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Configuration for \'{0}\' will change.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Configuration for '{0}' will change.".format(name) return ret - ret['comment'] = '\'{0}\' was configured.'.format(name) + ret["comment"] = "'{0}' was configured.".format(name) return ret -def _present(name, - block_icmp=None, - prune_block_icmp=False, - default=None, - masquerade=False, - ports=None, - prune_ports=False, - port_fwd=None, - prune_port_fwd=False, - services=None, - # TODO: prune_services=False in future release - # prune_services=False, - prune_services=None, - interfaces=None, - prune_interfaces=False, - sources=None, - prune_sources=False, - rich_rules=None, - prune_rich_rules=False): - ''' +def _present( + name, + block_icmp=None, + prune_block_icmp=False, + default=None, + masquerade=False, + ports=None, + prune_ports=False, + port_fwd=None, + prune_port_fwd=False, + services=None, + # TODO: prune_services=False in future release + # prune_services=False, + prune_services=None, + interfaces=None, + prune_interfaces=False, + sources=None, + prune_sources=False, + rich_rules=None, + prune_rich_rules=False, +): + """ Ensure a zone has specific attributes. - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} try: - zones = __salt__['firewalld.get_zones'](permanent=True) + zones = __salt__["firewalld.get_zones"](permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if name not in zones: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.new_zone'](name) + __salt__["firewalld.new_zone"](name) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({name: - {'old': zones, - 'new': name}}) + ret["changes"].update({name: {"old": zones, "new": name}}) if block_icmp or prune_block_icmp: block_icmp = block_icmp or [] @@ -386,33 +404,34 @@ def _present(name, old_icmp_types = [] try: - _current_icmp_blocks = __salt__['firewalld.list_icmp_block'](name, - permanent=True) + _current_icmp_blocks = __salt__["firewalld.list_icmp_block"]( + name, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if block_icmp: try: - _valid_icmp_types = __salt__['firewalld.get_icmp_types']( - permanent=True) + _valid_icmp_types = __salt__["firewalld.get_icmp_types"](permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret # log errors for invalid ICMP types in block_icmp input for icmp_type in set(block_icmp) - set(_valid_icmp_types): - log.error('%s is an invalid ICMP type', icmp_type) + log.error("%s is an invalid ICMP type", icmp_type) block_icmp.remove(icmp_type) new_icmp_types = set(block_icmp) - set(_current_icmp_blocks) for icmp_type in new_icmp_types: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.block_icmp'](name, icmp_type, - permanent=True) + __salt__["firewalld.block_icmp"]( + name, icmp_type, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if prune_block_icmp: @@ -421,12 +440,13 @@ def _present(name, # no need to check against _valid_icmp_types here, because all # elements in old_icmp_types are guaranteed to be in # _current_icmp_blocks, whose elements are inherently valid - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.allow_icmp'](name, icmp_type, - permanent=True) + __salt__["firewalld.allow_icmp"]( + name, icmp_type, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if new_icmp_types or old_icmp_types: @@ -434,86 +454,83 @@ def _present(name, # that they're still present if not prune_block_icmp: block_icmp = list(new_icmp_types | set(_current_icmp_blocks)) - ret['changes'].update({'icmp_types': - {'old': _current_icmp_blocks, - 'new': block_icmp}}) + ret["changes"].update( + {"icmp_types": {"old": _current_icmp_blocks, "new": block_icmp}} + ) # that's the only parameter that can't be permanent or runtime, it's # directly both if default: try: - default_zone = __salt__['firewalld.default_zone']() + default_zone = __salt__["firewalld.default_zone"]() except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if name != default_zone: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.set_default_zone'](name) + __salt__["firewalld.set_default_zone"](name) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({'default': - {'old': default_zone, - 'new': name}}) + ret["changes"].update({"default": {"old": default_zone, "new": name}}) try: - masquerade_ret = __salt__['firewalld.get_masquerade'](name, - permanent=True) + masquerade_ret = __salt__["firewalld.get_masquerade"](name, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if masquerade and not masquerade_ret: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.add_masquerade'](name, permanent=True) + __salt__["firewalld.add_masquerade"](name, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({'masquerade': - {'old': '', - 'new': 'Masquerading successfully set.'}}) + ret["changes"].update( + {"masquerade": {"old": "", "new": "Masquerading successfully set."}} + ) elif not masquerade and masquerade_ret: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.remove_masquerade'](name, - permanent=True) + __salt__["firewalld.remove_masquerade"](name, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({'masquerade': - {'old': '', - 'new': 'Masquerading successfully ' - 'disabled.'}}) + ret["changes"].update( + {"masquerade": {"old": "", "new": "Masquerading successfully " "disabled."}} + ) if ports or prune_ports: ports = ports or [] try: - _current_ports = __salt__['firewalld.list_ports'](name, permanent=True) + _current_ports = __salt__["firewalld.list_ports"](name, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret new_ports = set(ports) - set(_current_ports) old_ports = [] for port in new_ports: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.add_port'](name, port, permanent=True, force_masquerade=False) + __salt__["firewalld.add_port"]( + name, port, permanent=True, force_masquerade=False + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if prune_ports: old_ports = set(_current_ports) - set(ports) for port in old_ports: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.remove_port'](name, port, permanent=True) + __salt__["firewalld.remove_port"](name, port, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if new_ports or old_ports: @@ -521,50 +538,63 @@ def _present(name, # that they're still present if not prune_ports: ports = list(new_ports | set(_current_ports)) - ret['changes'].update({'ports': - {'old': _current_ports, - 'new': ports}}) + ret["changes"].update({"ports": {"old": _current_ports, "new": ports}}) if port_fwd or prune_port_fwd: port_fwd = port_fwd or [] try: - _current_port_fwd = __salt__['firewalld.list_port_fwd'](name, - permanent=True) + _current_port_fwd = __salt__["firewalld.list_port_fwd"]( + name, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret port_fwd = [_parse_forward(fwd) for fwd in port_fwd] _current_port_fwd = [ ForwardingMapping( - srcport=fwd['Source port'], - destport=fwd['Destination port'], - protocol=fwd['Protocol'], - destaddr=fwd['Destination address'] - ) for fwd in _current_port_fwd] + srcport=fwd["Source port"], + destport=fwd["Destination port"], + protocol=fwd["Protocol"], + destaddr=fwd["Destination address"], + ) + for fwd in _current_port_fwd + ] new_port_fwd = set(port_fwd) - set(_current_port_fwd) old_port_fwd = [] for fwd in new_port_fwd: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.add_port_fwd'](name, fwd.srcport, - fwd.destport, fwd.protocol, fwd.destaddr, permanent=True, - force_masquerade=False) + __salt__["firewalld.add_port_fwd"]( + name, + fwd.srcport, + fwd.destport, + fwd.protocol, + fwd.destaddr, + permanent=True, + force_masquerade=False, + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if prune_port_fwd: old_port_fwd = set(_current_port_fwd) - set(port_fwd) for fwd in old_port_fwd: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.remove_port_fwd'](name, fwd.srcport, - fwd.destport, fwd.protocol, fwd.destaddr, permanent=True) + __salt__["firewalld.remove_port_fwd"]( + name, + fwd.srcport, + fwd.destport, + fwd.protocol, + fwd.destaddr, + permanent=True, + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if new_port_fwd or old_port_fwd: @@ -572,41 +602,46 @@ def _present(name, # that they're still present if not prune_port_fwd: port_fwd = list(new_port_fwd | set(_current_port_fwd)) - ret['changes'].update({'port_fwd': - {'old': [fwd.todict() for fwd in - _current_port_fwd], - 'new': [fwd.todict() for fwd in port_fwd]}}) + ret["changes"].update( + { + "port_fwd": { + "old": [fwd.todict() for fwd in _current_port_fwd], + "new": [fwd.todict() for fwd in port_fwd], + } + } + ) if services or prune_services: services = services or [] try: - _current_services = __salt__['firewalld.list_services'](name, - permanent=True) + _current_services = __salt__["firewalld.list_services"]( + name, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret new_services = set(services) - set(_current_services) old_services = [] for new_service in new_services: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.add_service'](new_service, name, - permanent=True) + __salt__["firewalld.add_service"](new_service, name, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if prune_services: old_services = set(_current_services) - set(services) for old_service in old_services: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.remove_service'](old_service, name, - permanent=True) + __salt__["firewalld.remove_service"]( + old_service, name, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if new_services or old_services: @@ -614,40 +649,41 @@ def _present(name, # that they're still present if not prune_services: services = list(new_services | set(_current_services)) - ret['changes'].update({'services': - {'old': _current_services, - 'new': services}}) + ret["changes"].update( + {"services": {"old": _current_services, "new": services}} + ) if interfaces or prune_interfaces: interfaces = interfaces or [] try: - _current_interfaces = __salt__['firewalld.get_interfaces'](name, - permanent=True) + _current_interfaces = __salt__["firewalld.get_interfaces"]( + name, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret new_interfaces = set(interfaces) - set(_current_interfaces) old_interfaces = [] for interface in new_interfaces: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.add_interface'](name, interface, - permanent=True) + __salt__["firewalld.add_interface"](name, interface, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if prune_interfaces: old_interfaces = set(_current_interfaces) - set(interfaces) for interface in old_interfaces: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.remove_interface'](name, interface, - permanent=True) + __salt__["firewalld.remove_interface"]( + name, interface, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if new_interfaces or old_interfaces: @@ -655,39 +691,39 @@ def _present(name, # that they're still present if not prune_interfaces: interfaces = list(new_interfaces | set(_current_interfaces)) - ret['changes'].update({'interfaces': - {'old': _current_interfaces, - 'new': interfaces}}) + ret["changes"].update( + {"interfaces": {"old": _current_interfaces, "new": interfaces}} + ) if sources or prune_sources: sources = sources or [] try: - _current_sources = __salt__['firewalld.get_sources'](name, - permanent=True) + _current_sources = __salt__["firewalld.get_sources"](name, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret new_sources = set(sources) - set(_current_sources) old_sources = [] for source in new_sources: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.add_source'](name, source, permanent=True) + __salt__["firewalld.add_source"](name, source, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if prune_sources: old_sources = set(_current_sources) - set(sources) for source in old_sources: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.remove_source'](name, source, - permanent=True) + __salt__["firewalld.remove_source"]( + name, source, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if new_sources or old_sources: @@ -695,40 +731,41 @@ def _present(name, # that they're still present if not prune_sources: sources = list(new_sources | set(_current_sources)) - ret['changes'].update({'sources': - {'old': _current_sources, - 'new': sources}}) + ret["changes"].update( + {"sources": {"old": _current_sources, "new": sources}} + ) if rich_rules or prune_rich_rules: rich_rules = rich_rules or [] try: - _current_rich_rules = __salt__['firewalld.get_rich_rules'](name, - permanent=True) + _current_rich_rules = __salt__["firewalld.get_rich_rules"]( + name, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret new_rich_rules = set(rich_rules) - set(_current_rich_rules) old_rich_rules = [] for rich_rule in new_rich_rules: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.add_rich_rule'](name, rich_rule, - permanent=True) + __salt__["firewalld.add_rich_rule"](name, rich_rule, permanent=True) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if prune_rich_rules: old_rich_rules = set(_current_rich_rules) - set(rich_rules) for rich_rule in old_rich_rules: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['firewalld.remove_rich_rule'](name, rich_rule, - permanent=True) + __salt__["firewalld.remove_rich_rule"]( + name, rich_rule, permanent=True + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if new_rich_rules or old_rich_rules: @@ -736,29 +773,29 @@ def _present(name, # that they're still present if not prune_rich_rules: rich_rules = list(new_rich_rules | set(_current_rich_rules)) - ret['changes'].update({'rich_rules': - {'old': _current_rich_rules, - 'new': rich_rules}}) + ret["changes"].update( + {"rich_rules": {"old": _current_rich_rules, "new": rich_rules}} + ) # No changes - if ret['changes'] == {}: - ret['result'] = True - ret['comment'] = '\'{0}\' is already in the desired state.'.format(name) + if ret["changes"] == {}: + ret["result"] = True + ret["comment"] = "'{0}' is already in the desired state.".format(name) return ret # test=True and changes predicted - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None # build comment string nested.__opts__ = __opts__ comment = [] - comment.append('Configuration for \'{0}\' will change:'.format(name)) - comment.append(nested.output(ret['changes']).rstrip()) - ret['comment'] = '\n'.join(comment) - ret['changes'] = {} + comment.append("Configuration for '{0}' will change:".format(name)) + comment.append(nested.output(ret["changes"]).rstrip()) + ret["comment"] = "\n".join(comment) + ret["changes"] = {} return ret # Changes were made successfully - ret['result'] = True - ret['comment'] = '\'{0}\' was configured.'.format(name) + ret["result"] = True + ret["comment"] = "'{0}' was configured.".format(name) return ret diff --git a/salt/states/gem.py b/salt/states/gem.py index 3802e98b89f..56e5f9e12ce 100644 --- a/salt/states/gem.py +++ b/salt/states/gem.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of Ruby modules packaged as gems ============================================= @@ -13,34 +13,39 @@ you can specify what ruby version and gemset to target. gem.installed: - user: rvm - ruby: jruby@jgemset -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging +import re import salt.utils.versions -import re -import logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if gem module is available in __salt__ - ''' - return 'gem.list' in __salt__ + """ + if "gem.list" in __salt__: + return True + return (False, "gem module could not be loaded") -def installed(name, # pylint: disable=C0103 - ruby=None, - gem_bin=None, - user=None, - version=None, - rdoc=False, - ri=False, - pre_releases=False, - proxy=None, - source=None): # pylint: disable=C0103 - ''' +def installed( + name, # pylint: disable=C0103 + ruby=None, + gem_bin=None, + user=None, + version=None, + rdoc=False, + ri=False, + pre_releases=False, + proxy=None, + source=None, +): # pylint: disable=C0103 + """ Make sure that a gem is installed. name @@ -80,66 +85,67 @@ def installed(name, # pylint: disable=C0103 source : None Use the specified HTTP gem source server to download gem. Format: http://hostname[:port] - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} - if ruby is not None and not(__salt__['rvm.is_installed'](runas=user) or __salt__['rbenv.is_installed'](runas=user)): - log.warning( - 'Use of argument ruby found, but neither rvm or rbenv is installed' - ) - gems = __salt__['gem.list'](name, ruby, gem_bin=gem_bin, runas=user) + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} + if ruby is not None and not ( + __salt__["rvm.is_installed"](runas=user) + or __salt__["rbenv.is_installed"](runas=user) + ): + log.warning("Use of argument ruby found, but neither rvm or rbenv is installed") + gems = __salt__["gem.list"](name, ruby, gem_bin=gem_bin, runas=user) if name in gems and version is not None: - match = re.match(r'(>=|>|<|<=)', version) + match = re.match(r"(>=|>|<|<=)", version) if match: # Grab the comparison cmpr = match.group() # Clear out 'default:' and any whitespace - installed_version = re.sub('default: ', '', gems[name][0]).strip() + installed_version = re.sub("default: ", "", gems[name][0]).strip() # Clear out comparison from version and whitespace - desired_version = re.sub(cmpr, '', version).strip() + desired_version = re.sub(cmpr, "", version).strip() - if salt.utils.versions.compare(installed_version, - cmpr, - desired_version): - ret['result'] = True - ret['comment'] = 'Installed Gem meets version requirements.' + if salt.utils.versions.compare(installed_version, cmpr, desired_version): + ret["result"] = True + ret["comment"] = "Installed Gem meets version requirements." return ret else: if str(version) in gems[name]: - ret['result'] = True - ret['comment'] = 'Gem is already installed.' + ret["result"] = True + ret["comment"] = "Gem is already installed." return ret elif name in gems and version is None: - ret['result'] = True - ret['comment'] = 'Gem is already installed.' + ret["result"] = True + ret["comment"] = "Gem is already installed." return ret - if __opts__['test']: - ret['comment'] = 'The gem {0} would have been installed'.format(name) + if __opts__["test"]: + ret["comment"] = "The gem {0} would have been installed".format(name) return ret - if __salt__['gem.install'](name, - ruby=ruby, - gem_bin=gem_bin, - runas=user, - version=version, - rdoc=rdoc, - ri=ri, - pre_releases=pre_releases, - proxy=proxy, - source=source): - ret['result'] = True - ret['changes'][name] = 'Installed' - ret['comment'] = 'Gem was successfully installed' + if __salt__["gem.install"]( + name, + ruby=ruby, + gem_bin=gem_bin, + runas=user, + version=version, + rdoc=rdoc, + ri=ri, + pre_releases=pre_releases, + proxy=proxy, + source=source, + ): + ret["result"] = True + ret["changes"][name] = "Installed" + ret["comment"] = "Gem was successfully installed" else: - ret['result'] = False - ret['comment'] = 'Could not install gem.' + ret["result"] = False + ret["comment"] = "Could not install gem." return ret def removed(name, ruby=None, user=None, gem_bin=None): - ''' + """ Make sure that a gem is not installed. name @@ -156,29 +162,29 @@ def removed(name, ruby=None, user=None, gem_bin=None): The user under which to run the ``gem`` command .. versionadded:: 0.17.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if name not in __salt__['gem.list'](name, ruby, gem_bin=gem_bin, runas=user): - ret['result'] = True - ret['comment'] = 'Gem is not installed.' + if name not in __salt__["gem.list"](name, ruby, gem_bin=gem_bin, runas=user): + ret["result"] = True + ret["comment"] = "Gem is not installed." return ret - if __opts__['test']: - ret['comment'] = 'The gem {0} would have been removed'.format(name) + if __opts__["test"]: + ret["comment"] = "The gem {0} would have been removed".format(name) return ret - if __salt__['gem.uninstall'](name, ruby, gem_bin=gem_bin, runas=user): - ret['result'] = True - ret['changes'][name] = 'Removed' - ret['comment'] = 'Gem was successfully removed.' + if __salt__["gem.uninstall"](name, ruby, gem_bin=gem_bin, runas=user): + ret["result"] = True + ret["changes"][name] = "Removed" + ret["comment"] = "Gem was successfully removed." else: - ret['result'] = False - ret['comment'] = 'Could not remove gem.' + ret["result"] = False + ret["comment"] = "Could not remove gem." return ret def sources_add(name, ruby=None, user=None): - ''' + """ Make sure that a gem source is added. name @@ -191,28 +197,28 @@ def sources_add(name, ruby=None, user=None): The user under which to run the ``gem`` command .. versionadded:: 0.17.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if name in __salt__['gem.sources_list'](ruby, runas=user): - ret['result'] = True - ret['comment'] = 'Gem source is already added.' + if name in __salt__["gem.sources_list"](ruby, runas=user): + ret["result"] = True + ret["comment"] = "Gem source is already added." return ret - if __opts__['test']: - ret['comment'] = 'The gem source {0} would have been added.'.format(name) + if __opts__["test"]: + ret["comment"] = "The gem source {0} would have been added.".format(name) return ret - if __salt__['gem.sources_add'](source_uri=name, ruby=ruby, runas=user): - ret['result'] = True - ret['changes'][name] = 'Installed' - ret['comment'] = 'Gem source was successfully added.' + if __salt__["gem.sources_add"](source_uri=name, ruby=ruby, runas=user): + ret["result"] = True + ret["changes"][name] = "Installed" + ret["comment"] = "Gem source was successfully added." else: - ret['result'] = False - ret['comment'] = 'Could not add gem source.' + ret["result"] = False + ret["comment"] = "Could not add gem source." return ret def sources_remove(name, ruby=None, user=None): - ''' + """ Make sure that a gem source is removed. name @@ -225,23 +231,23 @@ def sources_remove(name, ruby=None, user=None): The user under which to run the ``gem`` command .. versionadded:: 0.17.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if name not in __salt__['gem.sources_list'](ruby, runas=user): - ret['result'] = True - ret['comment'] = 'Gem source is already removed.' + if name not in __salt__["gem.sources_list"](ruby, runas=user): + ret["result"] = True + ret["comment"] = "Gem source is already removed." return ret - if __opts__['test']: - ret['comment'] = 'The gem source would have been removed.' + if __opts__["test"]: + ret["comment"] = "The gem source would have been removed." return ret - if __salt__['gem.sources_remove'](source_uri=name, ruby=ruby, runas=user): - ret['result'] = True - ret['changes'][name] = 'Removed' - ret['comment'] = 'Gem source was successfully removed.' + if __salt__["gem.sources_remove"](source_uri=name, ruby=ruby, runas=user): + ret["result"] = True + ret["changes"][name] = "Removed" + ret["comment"] = "Gem source was successfully removed." else: - ret['result'] = False - ret['comment'] = 'Could not remove gem source.' + ret["result"] = False + ret["comment"] = "Could not remove gem source." return ret diff --git a/salt/states/git.py b/salt/states/git.py index ce6455ee713..8dad0436ed7 100644 --- a/salt/states/git.py +++ b/salt/states/git.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" States to manage git repositories and git configuration .. important:: @@ -9,7 +9,7 @@ States to manage git repositories and git configuration .. versionchanged:: 2015.8.8 This state module now requires git 1.6.5 (released 10 October 2009) or newer. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -26,38 +26,37 @@ import salt.utils.files import salt.utils.url import salt.utils.versions from salt.exceptions import CommandExecutionError -from salt.utils.versions import LooseVersion as _LooseVersion # Import 3rd-party libs from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if git is available - ''' - if 'git.version' not in __salt__: - return False - git_ver = _LooseVersion(__salt__['git.version'](versioninfo=False)) - return git_ver >= _LooseVersion('1.6.5') + """ + if "git.version" not in __salt__: + return (False, "git module could not be loaded") + git_ver = _LooseVersion(__salt__["git.version"](versioninfo=False)) + return git_ver >= _LooseVersion("1.6.5") def _revs_equal(rev1, rev2, rev_type): - ''' + """ Shorthand helper function for comparing SHA1s. If rev_type == 'sha1' then the comparison will be done using str.startwith() to allow short SHA1s to compare successfully. NOTE: This means that rev2 must be the short rev. - ''' - if (rev1 is None and rev2 is not None) \ - or (rev2 is None and rev1 is not None): + """ + if (rev1 is None and rev2 is not None) or (rev2 is None and rev1 is not None): return False elif rev1 is rev2 is None: return True - elif rev_type == 'sha1': + elif rev_type == "sha1": return rev1.startswith(rev2) else: return rev1 == rev2 @@ -68,41 +67,42 @@ def _short_sha(sha1): def _format_comments(comments): - ''' + """ Return a joined list - ''' - ret = '. '.join(comments) + """ + ret = ". ".join(comments) if len(comments) > 1: - ret += '.' + ret += "." return ret def _need_branch_change(branch, local_branch): - ''' + """ Short hand for telling when a new branch is needed - ''' + """ return branch is not None and branch != local_branch -def _get_branch_opts(branch, local_branch, all_local_branches, - desired_upstream, git_ver=None): - ''' +def _get_branch_opts( + branch, local_branch, all_local_branches, desired_upstream, git_ver=None +): + """ DRY helper to build list of opts for git.branch, for the purposes of setting upstream tracking branch - ''' + """ if branch is not None and branch not in all_local_branches: # We won't be setting upstream because the act of checking out a new # branch will set upstream for us return None if git_ver is None: - git_ver = _LooseVersion(__salt__['git.version'](versioninfo=False)) + git_ver = _LooseVersion(__salt__["git.version"](versioninfo=False)) ret = [] - if git_ver >= _LooseVersion('1.8.0'): - ret.extend(['--set-upstream-to', desired_upstream]) + if git_ver >= _LooseVersion("1.8.0"): + ret.extend(["--set-upstream-to", desired_upstream]) else: - ret.append('--set-upstream') + ret.append("--set-upstream") # --set-upstream does not assume the current branch, so we have to # tell it which branch we'll be using ret.append(local_branch if branch is None else branch) @@ -111,186 +111,184 @@ def _get_branch_opts(branch, local_branch, all_local_branches, def _get_local_rev_and_branch(target, user, password, output_encoding=None): - ''' + """ Return the local revision for before/after comparisons - ''' - log.info('Checking local revision for %s', target) + """ + log.info("Checking local revision for %s", target) try: - local_rev = __salt__['git.revision']( + local_rev = __salt__["git.revision"]( target, user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: - log.info('No local revision for %s', target) + log.info("No local revision for %s", target) local_rev = None - log.info('Checking local branch for %s', target) + log.info("Checking local branch for %s", target) try: - local_branch = __salt__['git.current_branch']( + local_branch = __salt__["git.current_branch"]( target, user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: - log.info('No local branch for %s', target) + log.info("No local branch for %s", target) local_branch = None return local_rev, local_branch def _strip_exc(exc): - ''' + """ Strip the actual command that was run from exc.strerror to leave just the error message - ''' - return re.sub(r'^Command [\'"].+[\'"] failed: ', '', exc.strerror) + """ + return re.sub(r'^Command [\'"].+[\'"] failed: ', "", exc.strerror) def _uptodate(ret, target, comments=None, local_changes=False): - ret['comment'] = 'Repository {0} is up-to-date'.format(target) + ret["comment"] = "Repository {0} is up-to-date".format(target) if local_changes: - ret['comment'] += ( - ', but with uncommitted changes. Set \'force_reset\' to True to ' - 'purge uncommitted changes.' + ret["comment"] += ( + ", but with uncommitted changes. Set 'force_reset' to True to " + "purge uncommitted changes." ) if comments: # Shouldn't be making any changes if the repo was up to date, but # report on them so we are alerted to potential problems with our # logic. - ret['comment'] += ( - '\n\nChanges {0}made: {1}'.format( - 'that would be ' if __opts__['test'] else '', - _format_comments(comments) - ) + ret["comment"] += "\n\nChanges {0}made: {1}".format( + "that would be " if __opts__["test"] else "", _format_comments(comments) ) return ret def _neutral_test(ret, comment): - ret['result'] = None - ret['comment'] = comment + ret["result"] = None + ret["comment"] = comment return ret def _fail(ret, msg, comments=None): - ret['result'] = False + ret["result"] = False if comments: - msg += '\n\nChanges already made: ' + _format_comments(comments) - ret['comment'] = msg + msg += "\n\nChanges already made: " + _format_comments(comments) + ret["comment"] = msg return ret def _already_cloned(ret, target, branch=None, comments=None): - ret['result'] = True - ret['comment'] = 'Repository already exists at {0}{1}'.format( - target, - ' and is checked out to branch \'{0}\''.format(branch) if branch else '' + ret["result"] = True + ret["comment"] = "Repository already exists at {0}{1}".format( + target, " and is checked out to branch '{0}'".format(branch) if branch else "" ) if comments: - ret['comment'] += ( - '\n\nChanges {0}made: {1}'.format( - 'that would be ' if __opts__['test'] else '', - _format_comments(comments) - ) + ret["comment"] += "\n\nChanges {0}made: {1}".format( + "that would be " if __opts__["test"] else "", _format_comments(comments) ) return ret def _failed_fetch(ret, exc, comments=None): msg = ( - 'Fetch failed. Set \'force_fetch\' to True to force the fetch if the ' - 'failure was due to not being able to fast-forward. Output of the fetch ' - 'command follows:\n\n{0}'.format(_strip_exc(exc)) + "Fetch failed. Set 'force_fetch' to True to force the fetch if the " + "failure was due to not being able to fast-forward. Output of the fetch " + "command follows:\n\n{0}".format(_strip_exc(exc)) ) return _fail(ret, msg, comments) def _failed_submodule_update(ret, exc, comments=None): - msg = 'Failed to update submodules: ' + _strip_exc(exc) + msg = "Failed to update submodules: " + _strip_exc(exc) return _fail(ret, msg, comments) -def _not_fast_forward(ret, rev, pre, post, branch, local_branch, - default_branch, local_changes, comments): - branch_msg = '' +def _not_fast_forward( + ret, rev, pre, post, branch, local_branch, default_branch, local_changes, comments +): + branch_msg = "" if branch is None: - if rev != 'HEAD': + if rev != "HEAD": if local_branch != rev: branch_msg = ( - ' The desired rev ({0}) differs from the name of the ' - 'local branch ({1}), if the desired rev is a branch name ' - 'then a forced update could possibly be avoided by ' - 'setting the \'branch\' argument to \'{0}\' instead.' - .format(rev, local_branch) + " The desired rev ({0}) differs from the name of the " + "local branch ({1}), if the desired rev is a branch name " + "then a forced update could possibly be avoided by " + "setting the 'branch' argument to '{0}' instead.".format( + rev, local_branch + ) ) else: if default_branch is not None and local_branch != default_branch: branch_msg = ( - ' The default remote branch ({0}) differs from the ' - 'local branch ({1}). This could be caused by changing the ' - 'default remote branch, or if the local branch was ' - 'manually changed. Rather than forcing an update, it ' - 'may be advisable to set the \'branch\' argument to ' - '\'{0}\' instead. To ensure that this state follows the ' - '\'{0}\' branch instead of the remote HEAD, set the ' - '\'rev\' argument to \'{0}\'.' - .format(default_branch, local_branch) + " The default remote branch ({0}) differs from the " + "local branch ({1}). This could be caused by changing the " + "default remote branch, or if the local branch was " + "manually changed. Rather than forcing an update, it " + "may be advisable to set the 'branch' argument to " + "'{0}' instead. To ensure that this state follows the " + "'{0}' branch instead of the remote HEAD, set the " + "'rev' argument to '{0}'.".format(default_branch, local_branch) ) pre = _short_sha(pre) post = _short_sha(post) return _fail( ret, - 'Repository would be updated {0}{1}, but {2}. Set \'force_reset\' to ' - 'True{3} to force this update{4}.{5}'.format( - 'from {0} to {1}'.format(pre, post) - if local_changes and pre != post - else 'to {0}'.format(post), - ' (after checking out local branch \'{0}\')'.format(branch) - if _need_branch_change(branch, local_branch) - else '', - 'this is not a fast-forward merge' - if not local_changes - else 'there are uncommitted changes', - ' (or \'remote-changes\')' if local_changes else '', - ' and discard these changes' if local_changes else '', + "Repository would be updated {0}{1}, but {2}. Set 'force_reset' to " + "True{3} to force this update{4}.{5}".format( + "from {0} to {1}".format(pre, post) + if local_changes and pre != post + else "to {0}".format(post), + " (after checking out local branch '{0}')".format(branch) + if _need_branch_change(branch, local_branch) + else "", + "this is not a fast-forward merge" + if not local_changes + else "there are uncommitted changes", + " (or 'remote-changes')" if local_changes else "", + " and discard these changes" if local_changes else "", branch_msg, ), - comments + comments, ) -def latest(name, - rev='HEAD', - target=None, - branch=None, - user=None, - password=None, - update_head=True, - force_checkout=False, - force_clone=False, - force_fetch=False, - force_reset=False, - submodules=False, - bare=False, - mirror=False, - remote='origin', - fetch_tags=True, - sync_tags=True, - depth=None, - identity=None, - https_user=None, - https_pass=None, - onlyif=None, - unless=None, - refspec_branch='*', - refspec_tag='*', - output_encoding=None, - **kwargs): - ''' +def latest( + name, + rev="HEAD", + target=None, + branch=None, + user=None, + password=None, + update_head=True, + force_checkout=False, + force_clone=False, + force_fetch=False, + force_reset=False, + submodules=False, + bare=False, + mirror=False, + remote="origin", + fetch_tags=True, + sync_tags=True, + depth=None, + identity=None, + https_user=None, + https_pass=None, + onlyif=None, + unless=None, + refspec_branch="*", + refspec_tag="*", + output_encoding=None, + **kwargs +): + """ Make sure the repository is cloned to the given directory and is up-to-date. @@ -627,32 +625,27 @@ def latest(name, - require: - pkg: git - ssh_known_hosts: gitlab.example.com - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} kwargs = salt.utils.args.clean_kwargs(**kwargs) if kwargs: - return _fail( - ret, - salt.utils.args.invalid_kwargs(kwargs, raise_exc=False) - ) + return _fail(ret, salt.utils.args.invalid_kwargs(kwargs, raise_exc=False)) if not remote: - return _fail(ret, '\'remote\' argument is required') + return _fail(ret, "'remote' argument is required") if not target: - return _fail(ret, '\'target\' argument is required') + return _fail(ret, "'target' argument is required") if not rev: return _fail( - ret, - '\'{0}\' is not a valid value for the \'rev\' argument'.format(rev) + ret, "'{0}' is not a valid value for the 'rev' argument".format(rev) ) - if force_reset not in (True, False, 'remote-changes'): + if force_reset not in (True, False, "remote-changes"): return _fail( - ret, - '\'force_reset\' must be one of True, False, or \'remote-changes\'' + ret, "'force_reset' must be one of True, False, or 'remote-changes'" ) # Ensure that certain arguments are strings to ensure that comparisons work @@ -662,10 +655,7 @@ def latest(name, if not isinstance(target, six.string_types): target = six.text_type(target) if not os.path.isabs(target): - return _fail( - ret, - 'target \'{0}\' is not an absolute path'.format(target) - ) + return _fail(ret, "target '{0}' is not an absolute path".format(target)) if branch is not None and not isinstance(branch, six.string_types): branch = six.text_type(branch) if user is not None and not isinstance(user, six.string_types): @@ -678,26 +668,20 @@ def latest(name, if isinstance(identity, six.string_types): identity = [identity] elif not isinstance(identity, list): - return _fail(ret, 'identity must be either a list or a string') + return _fail(ret, "identity must be either a list or a string") identity = [os.path.expanduser(x) for x in identity] for ident_path in identity: - if 'salt://' in ident_path: + if "salt://" in ident_path: try: - ident_path = __salt__['cp.cache_file'](ident_path, __env__) + ident_path = __salt__["cp.cache_file"](ident_path, __env__) except IOError as exc: - log.exception('Failed to cache %s', ident_path) + log.exception("Failed to cache %s", ident_path) return _fail( - ret, - 'identity \'{0}\' does not exist.'.format( - ident_path - ) + ret, "identity '{0}' does not exist.".format(ident_path) ) if not os.path.isabs(ident_path): return _fail( - ret, - 'identity \'{0}\' is not an absolute path'.format( - ident_path - ) + ret, "identity '{0}' is not an absolute path".format(ident_path) ) if https_user is not None and not isinstance(https_user, six.string_types): https_user = six.text_type(https_user) @@ -708,59 +692,56 @@ def latest(name, # will be passed where appropriate to ensure that these commands are # authenticated and that the git LFS plugin can download files. use_lfs = bool( - __salt__['git.config_get_regexp']( - r'filter\.lfs\.', - **{'global': True})) - lfs_opts = {'identity': identity} if use_lfs else {} + __salt__["git.config_get_regexp"](r"filter\.lfs\.", **{"global": True}) + ) + lfs_opts = {"identity": identity} if use_lfs else {} if os.path.isfile(target): return _fail( ret, - 'Target \'{0}\' exists and is a regular file, cannot proceed' - .format(target) + "Target '{0}' exists and is a regular file, cannot proceed".format(target), ) try: desired_fetch_url = salt.utils.url.add_http_basic_auth( - name, - https_user, - https_pass, - https_only=True + name, https_user, https_pass, https_only=True ) except ValueError as exc: return _fail(ret, exc.__str__()) - redacted_fetch_url = \ - salt.utils.url.redact_http_basic_auth(desired_fetch_url) + redacted_fetch_url = salt.utils.url.redact_http_basic_auth(desired_fetch_url) if mirror: bare = True # Check to make sure rev and mirror/bare are not both in use - if rev != 'HEAD' and bare: - return _fail(ret, ('\'rev\' is not compatible with the \'mirror\' and ' - '\'bare\' arguments')) + if rev != "HEAD" and bare: + return _fail( + ret, ("'rev' is not compatible with the 'mirror' and " "'bare' arguments") + ) - run_check_cmd_kwargs = {'runas': user, 'password': password} - if 'shell' in __grains__: - run_check_cmd_kwargs['shell'] = __grains__['shell'] + run_check_cmd_kwargs = {"runas": user, "password": password} + if "shell" in __grains__: + run_check_cmd_kwargs["shell"] = __grains__["shell"] # check if git.latest should be applied - cret = mod_run_check( - run_check_cmd_kwargs, onlyif, unless - ) + cret = mod_run_check(run_check_cmd_kwargs, onlyif, unless) if isinstance(cret, dict): ret.update(cret) return ret - refspecs = [ - 'refs/heads/{0}:refs/remotes/{1}/{0}'.format(refspec_branch, remote), - '+refs/tags/{0}:refs/tags/{0}'.format(refspec_tag) - ] if fetch_tags else [] + refspecs = ( + [ + "refs/heads/{0}:refs/remotes/{1}/{0}".format(refspec_branch, remote), + "+refs/tags/{0}:refs/tags/{0}".format(refspec_tag), + ] + if fetch_tags + else [] + ) - log.info('Checking remote revision for %s', name) + log.info("Checking remote revision for %s", name) try: - all_remote_refs = __salt__['git.remote_refs']( + all_remote_refs = __salt__["git.remote_refs"]( name, heads=False, tags=False, @@ -771,25 +752,24 @@ def latest(name, https_pass=https_pass, ignore_retcode=False, saltenv=__env__, - output_encoding=output_encoding) - except CommandExecutionError as exc: - return _fail( - ret, - 'Failed to check remote refs: {0}'.format(_strip_exc(exc)) + output_encoding=output_encoding, ) + except CommandExecutionError as exc: + return _fail(ret, "Failed to check remote refs: {0}".format(_strip_exc(exc))) except NameError as exc: - if 'global name' in exc.message: + if "global name" in exc.message: raise CommandExecutionError( - 'Failed to check remote refs: You may need to install ' - 'GitPython or PyGit2') + "Failed to check remote refs: You may need to install " + "GitPython or PyGit2" + ) raise - if 'HEAD' in all_remote_refs: - head_rev = all_remote_refs['HEAD'] + if "HEAD" in all_remote_refs: + head_rev = all_remote_refs["HEAD"] for refname, refsha in six.iteritems(all_remote_refs): - if refname.startswith('refs/heads/'): + if refname.startswith("refs/heads/"): if refsha == head_rev: - default_branch = refname.partition('refs/heads/')[-1] + default_branch = refname.partition("refs/heads/")[-1] break else: default_branch = None @@ -802,37 +782,36 @@ def latest(name, remote_rev = None remote_rev_type = None else: - if rev == 'HEAD': + if rev == "HEAD": if head_rev is not None: remote_rev = head_rev # Just go with whatever the upstream currently is desired_upstream = None - remote_rev_type = 'sha1' + remote_rev_type = "sha1" else: # Empty remote repo remote_rev = None remote_rev_type = None - elif 'refs/heads/' + rev in all_remote_refs: - remote_rev = all_remote_refs['refs/heads/' + rev] - desired_upstream = '/'.join((remote, rev)) - remote_rev_type = 'branch' - elif 'refs/tags/' + rev + '^{}' in all_remote_refs: + elif "refs/heads/" + rev in all_remote_refs: + remote_rev = all_remote_refs["refs/heads/" + rev] + desired_upstream = "/".join((remote, rev)) + remote_rev_type = "branch" + elif "refs/tags/" + rev + "^{}" in all_remote_refs: # Annotated tag - remote_rev = all_remote_refs['refs/tags/' + rev + '^{}'] - remote_rev_type = 'tag' - elif 'refs/tags/' + rev in all_remote_refs: + remote_rev = all_remote_refs["refs/tags/" + rev + "^{}"] + remote_rev_type = "tag" + elif "refs/tags/" + rev in all_remote_refs: # Non-annotated tag - remote_rev = all_remote_refs['refs/tags/' + rev] - remote_rev_type = 'tag' + remote_rev = all_remote_refs["refs/tags/" + rev] + remote_rev_type = "tag" else: - if len(rev) <= 40 \ - and all(x in string.hexdigits for x in rev): + if len(rev) <= 40 and all(x in string.hexdigits for x in rev): # git ls-remote did not find the rev, and because it's a # hex string <= 40 chars we're going to assume that the # desired rev is a SHA1 rev = rev.lower() remote_rev = rev - remote_rev_type = 'sha1' + remote_rev_type = "sha1" else: remote_rev = None remote_rev_type = None @@ -840,85 +819,77 @@ def latest(name, # For the comment field of the state return dict, the remote location # (and short-sha1, if rev is not a sha1) is referenced several times, # determine it once here and reuse the value below. - if remote_rev_type == 'sha1': - if rev == 'HEAD': - remote_loc = 'remote HEAD (' + remote_rev[:7] + ')' + if remote_rev_type == "sha1": + if rev == "HEAD": + remote_loc = "remote HEAD (" + remote_rev[:7] + ")" else: remote_loc = remote_rev[:7] elif remote_rev is not None: - remote_loc = '{0} ({1})'.format( - desired_upstream if remote_rev_type == 'branch' else rev, - remote_rev[:7] + remote_loc = "{0} ({1})".format( + desired_upstream if remote_rev_type == "branch" else rev, remote_rev[:7] ) else: # Shouldn't happen but log a warning here for future # troubleshooting purposes in the event we find a corner case. log.warning( - 'Unable to determine remote_loc. rev is %s, remote_rev is ' - '%s, remove_rev_type is %s, desired_upstream is %s, and bare ' - 'is%s set', + "Unable to determine remote_loc. rev is %s, remote_rev is " + "%s, remove_rev_type is %s, desired_upstream is %s, and bare " + "is%s set", rev, remote_rev, remote_rev_type, desired_upstream, - ' not' if not bare else '' + " not" if not bare else "", ) remote_loc = None - if depth is not None and remote_rev_type not in ('branch', 'tag'): + if depth is not None and remote_rev_type not in ("branch", "tag"): return _fail( ret, - 'When \'depth\' is used, \'rev\' must be set to the name of a ' - 'branch or tag on the remote repository' + "When 'depth' is used, 'rev' must be set to the name of a " + "branch or tag on the remote repository", ) if remote_rev is None and not bare: - if rev != 'HEAD': + if rev != "HEAD": # A specific rev is desired, but that rev doesn't exist on the # remote repo. return _fail( ret, - 'No revision matching \'{0}\' exists in the remote ' - 'repository'.format(rev) + "No revision matching '{0}' exists in the remote " + "repository".format(rev), ) - git_ver = _LooseVersion(__salt__['git.version'](versioninfo=False)) + git_ver = _LooseVersion(__salt__["git.version"](versioninfo=False)) - check = 'refs' if bare else '.git' + check = "refs" if bare else ".git" gitdir = os.path.join(target, check) comments = [] - if os.path.isdir(gitdir) \ - or __salt__['git.is_worktree']( - target, - user=user, - password=password, - output_encoding=output_encoding): + if os.path.isdir(gitdir) or __salt__["git.is_worktree"]( + target, user=user, password=password, output_encoding=output_encoding + ): # Target directory is a git repository or git worktree try: - all_local_branches = __salt__['git.list_branches']( - target, - user=user, - password=password, - output_encoding=output_encoding) + all_local_branches = __salt__["git.list_branches"]( + target, user=user, password=password, output_encoding=output_encoding + ) all_local_tags = set( - __salt__['git.list_tags']( + __salt__["git.list_tags"]( target, user=user, password=password, - output_encoding=output_encoding + output_encoding=output_encoding, ) ) local_rev, local_branch = _get_local_rev_and_branch( - target, - user, - password, - output_encoding) + target, user, password, output_encoding + ) if not bare and remote_rev is None and local_rev is not None: return _fail( ret, - 'Remote repository is empty, cannot update from a ' - 'non-empty to an empty repository' + "Remote repository is empty, cannot update from a " + "non-empty to an empty repository", ) # Base rev and branch are the ones from which any reset or merge @@ -943,26 +914,29 @@ def latest(name, # eventually, but before we do that we need to find the # current SHA1. try: - base_rev = __salt__['git.rev_parse']( + base_rev = __salt__["git.rev_parse"]( target, - branch + '^{commit}', + branch + "^{commit}", user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError as exc: return _fail( ret, - 'Unable to get position of local branch \'{0}\': ' - '{1}'.format(branch, _strip_exc(exc)), - comments + "Unable to get position of local branch '{0}': " + "{1}".format(branch, _strip_exc(exc)), + comments, ) - remotes = __salt__['git.remotes'](target, - user=user, - password=password, - redact_auth=False, - output_encoding=output_encoding) + remotes = __salt__["git.remotes"]( + target, + user=user, + password=password, + redact_auth=False, + output_encoding=output_encoding, + ) revs_match = _revs_equal(local_rev, remote_rev, remote_rev_type) try: @@ -970,52 +944,55 @@ def latest(name, # there are local changes. local_changes = bool( not bare - and - __salt__['git.diff'](target, - 'HEAD', - user=user, - password=password, - output_encoding=output_encoding) + and __salt__["git.diff"]( + target, + "HEAD", + user=user, + password=password, + output_encoding=output_encoding, + ) ) except CommandExecutionError: # No need to capture the error and log it, the _git_run() # helper in the git execution module will have already logged # the output from the command. log.warning( - 'git.latest: Unable to determine if %s has local changes', - target + "git.latest: Unable to determine if %s has local changes", target ) local_changes = False if local_changes and revs_match: if force_reset is True: msg = ( - '{0} is up-to-date, but with uncommitted changes. ' - 'Since \'force_reset\' is set to True, these local ' - 'changes would be reset. To only reset when there are ' - 'changes in the remote repository, set ' - '\'force_reset\' to \'remote-changes\'.'.format(target) + "{0} is up-to-date, but with uncommitted changes. " + "Since 'force_reset' is set to True, these local " + "changes would be reset. To only reset when there are " + "changes in the remote repository, set " + "'force_reset' to 'remote-changes'.".format(target) ) - if __opts__['test']: - ret['changes']['forced update'] = True + if __opts__["test"]: + ret["changes"]["forced update"] = True if comments: msg += _format_comments(comments) return _neutral_test(ret, msg) - log.debug(msg.replace('would', 'will')) + log.debug(msg.replace("would", "will")) else: log.debug( - '%s up-to-date, but with uncommitted changes. Since ' - '\'force_reset\' is set to %s, no changes will be ' - 'made.', target, force_reset + "%s up-to-date, but with uncommitted changes. Since " + "'force_reset' is set to %s, no changes will be " + "made.", + target, + force_reset, + ) + return _uptodate( + ret, target, _format_comments(comments), local_changes ) - return _uptodate(ret, - target, - _format_comments(comments), - local_changes) - if remote_rev_type == 'sha1' \ - and base_rev is not None \ - and base_rev.startswith(remote_rev): + if ( + remote_rev_type == "sha1" + and base_rev is not None + and base_rev.startswith(remote_rev) + ): # Either we're already checked out to the branch we need and it # is up-to-date, or the branch to which we need to switch is # on the same SHA1 as the desired remote revision. Either way, @@ -1026,13 +1003,14 @@ def latest(name, has_remote_rev = False if remote_rev is not None: try: - __salt__['git.rev_parse']( + __salt__["git.rev_parse"]( target, - remote_rev + '^{commit}', + remote_rev + "^{commit}", user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: # Local checkout doesn't have the remote_rev pass @@ -1042,19 +1020,20 @@ def latest(name, # deleted/changed/force updated. Do some further sanity # checks to determine if we really do have the # remote_rev. - if remote_rev_type == 'branch': + if remote_rev_type == "branch": if remote in remotes: try: # Do a rev-parse on <remote>/<rev> to get # the local SHA1 for it, so we can compare # it to the remote_rev SHA1. - local_copy = __salt__['git.rev_parse']( + local_copy = __salt__["git.rev_parse"]( target, desired_upstream, user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: pass else: @@ -1066,16 +1045,17 @@ def latest(name, # need to fetch. if local_copy == remote_rev: has_remote_rev = True - elif remote_rev_type == 'tag': + elif remote_rev_type == "tag": if rev in all_local_tags: try: - local_tag_sha1 = __salt__['git.rev_parse']( + local_tag_sha1 = __salt__["git.rev_parse"]( target, - rev + '^{commit}', + rev + "^{commit}", user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: # Shouldn't happen if the tag exists # locally but account for this just in @@ -1095,35 +1075,35 @@ def latest(name, # tag's new location. return _fail( ret, - '\'{0}\' is a tag, but the remote ' - 'SHA1 for this tag ({1}) doesn\'t ' - 'match the local SHA1 ({2}). Set ' - '\'force_reset\' to True to force ' - 'this update.'.format( + "'{0}' is a tag, but the remote " + "SHA1 for this tag ({1}) doesn't " + "match the local SHA1 ({2}). Set " + "'force_reset' to True to force " + "this update.".format( rev, _short_sha(remote_rev), - _short_sha(local_tag_sha1) - ) + _short_sha(local_tag_sha1), + ), ) - elif remote_rev_type == 'sha1': + elif remote_rev_type == "sha1": has_remote_rev = True # If fast_forward is not boolean, then we don't yet know if this # will be a fast forward or not, because a fetch is required. - fast_forward = False \ - if (local_changes and force_reset != 'remote-changes') \ - else None + fast_forward = ( + False if (local_changes and force_reset != "remote-changes") else None + ) if has_remote_rev: - if (not revs_match and not update_head) \ - and (branch is None or branch == local_branch): - ret['comment'] = ( - '{0} is already present and local HEAD ({1}) does not ' - 'match, but update_head=False. HEAD has not been ' - 'updated locally.'.format( - remote_loc.capitalize() if rev == 'HEAD' - else remote_loc, - local_rev[:7] + if (not revs_match and not update_head) and ( + branch is None or branch == local_branch + ): + ret["comment"] = ( + "{0} is already present and local HEAD ({1}) does not " + "match, but update_head=False. HEAD has not been " + "updated locally.".format( + remote_loc.capitalize() if rev == "HEAD" else remote_loc, + local_rev[:7], ) ) return ret @@ -1141,14 +1121,15 @@ def latest(name, # considered a fast-forward update. fast_forward = True else: - fast_forward = __salt__['git.merge_base']( + fast_forward = __salt__["git.merge_base"]( target, refs=[base_rev, remote_rev], is_ancestor=True, user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) if fast_forward is False: if force_reset is False: @@ -1161,26 +1142,28 @@ def latest(name, local_branch, default_branch, local_changes, - comments) - merge_action = 'hard-reset' + comments, + ) + merge_action = "hard-reset" elif fast_forward is True: - merge_action = 'fast-forwarded' + merge_action = "fast-forwarded" else: - merge_action = 'updated' + merge_action = "updated" if base_branch is None: # No local branch, no upstream tracking branch upstream = None else: try: - upstream = __salt__['git.rev_parse']( + upstream = __salt__["git.rev_parse"]( target, - base_branch + '@{upstream}', - opts=['--abbrev-ref'], + base_branch + "@{upstream}", + opts=["--abbrev-ref"], user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: # There is a local branch but the rev-parse command # failed, so that means there is no upstream tracking @@ -1193,40 +1176,36 @@ def latest(name, upstream = False if remote in remotes: - fetch_url = remotes[remote]['fetch'] + fetch_url = remotes[remote]["fetch"] else: - log.debug( - 'Remote \'%s\' not found in git checkout at %s', - remote, target - ) + log.debug("Remote '%s' not found in git checkout at %s", remote, target) fetch_url = None if remote_rev is not None and desired_fetch_url != fetch_url: - if __opts__['test']: + if __opts__["test"]: actions = [ - 'Remote \'{0}\' would be changed from {1} to {2}' - .format( + "Remote '{0}' would be changed from {1} to {2}".format( remote, salt.utils.url.redact_http_basic_auth(fetch_url), - redacted_fetch_url + redacted_fetch_url, ) ] if not has_remote_rev: - actions.append('Remote would be fetched') + actions.append("Remote would be fetched") if not revs_match: if update_head: - ret['changes']['revision'] = { - 'old': local_rev, 'new': remote_rev + ret["changes"]["revision"] = { + "old": local_rev, + "new": remote_rev, } if fast_forward is False: - ret['changes']['forced update'] = True + ret["changes"]["forced update"] = True actions.append( - 'Repository would be {0} to {1}'.format( - merge_action, - _short_sha(remote_rev) + "Repository would be {0} to {1}".format( + merge_action, _short_sha(remote_rev) ) ) - if ret['changes']: + if ret["changes"]: return _neutral_test(ret, _format_comments(actions)) else: if not revs_match and not update_head: @@ -1234,72 +1213,67 @@ def latest(name, # URL would be modified, so we can't just say that # the repo is up-to-date, we need to inform the # user of the actions taken. - ret['comment'] = _format_comments(actions) + ret["comment"] = _format_comments(actions) return ret - return _uptodate(ret, - target, - _format_comments(actions)) + return _uptodate(ret, target, _format_comments(actions)) # The fetch_url for the desired remote does not match the # specified URL (or the remote does not exist), so set the # remote URL. - __salt__['git.remote_set'](target, - url=name, - remote=remote, - user=user, - password=password, - https_user=https_user, - https_pass=https_pass, - output_encoding=output_encoding) + __salt__["git.remote_set"]( + target, + url=name, + remote=remote, + user=user, + password=password, + https_user=https_user, + https_pass=https_pass, + output_encoding=output_encoding, + ) if fetch_url is None: comments.append( - 'Remote \'{0}\' set to {1}'.format( - remote, - redacted_fetch_url - ) + "Remote '{0}' set to {1}".format(remote, redacted_fetch_url) ) - ret['changes']['new'] = name + ' => ' + remote + ret["changes"]["new"] = name + " => " + remote else: comments.append( - 'Remote \'{0}\' changed from {1} to {2}'.format( + "Remote '{0}' changed from {1} to {2}".format( remote, salt.utils.url.redact_http_basic_auth(fetch_url), - redacted_fetch_url + redacted_fetch_url, ) ) if remote_rev is not None: - if __opts__['test']: + if __opts__["test"]: actions = [] if not has_remote_rev: - actions.append( - 'Remote \'{0}\' would be fetched'.format(remote) - ) - if (not revs_match) \ - and (update_head or (branch is not None - and branch != local_branch)): - ret['changes']['revision'] = { - 'old': local_rev, 'new': remote_rev + actions.append("Remote '{0}' would be fetched".format(remote)) + if (not revs_match) and ( + update_head or (branch is not None and branch != local_branch) + ): + ret["changes"]["revision"] = { + "old": local_rev, + "new": remote_rev, } if _need_branch_change(branch, local_branch): if branch not in all_local_branches: actions.append( - 'New branch \'{0}\' would be checked ' - 'out, with {1} as a starting ' - 'point'.format(branch, remote_loc) + "New branch '{0}' would be checked " + "out, with {1} as a starting " + "point".format(branch, remote_loc) ) if desired_upstream: actions.append( - 'Tracking branch would be set to {0}' - .format(desired_upstream) + "Tracking branch would be set to {0}".format( + desired_upstream + ) ) else: actions.append( - 'Branch \'{0}\' would be checked out ' - 'and {1} to {2}'.format( - branch, - merge_action, - _short_sha(remote_rev) + "Branch '{0}' would be checked out " + "and {1} to {2}".format( + branch, merge_action, _short_sha(remote_rev) ) ) else: @@ -1307,166 +1281,160 @@ def latest(name, if update_head: if fast_forward is True: actions.append( - 'Repository would be fast-forwarded from ' - '{0} to {1}'.format( + "Repository would be fast-forwarded from " + "{0} to {1}".format( _short_sha(local_rev), - _short_sha(remote_rev) + _short_sha(remote_rev), ) ) else: actions.append( - 'Repository would be {0} from {1} to {2}' - .format( - 'hard-reset' - if force_reset and has_remote_rev - else 'updated', + "Repository would be {0} from {1} to {2}".format( + "hard-reset" + if force_reset and has_remote_rev + else "updated", _short_sha(local_rev), - _short_sha(remote_rev) + _short_sha(remote_rev), ) ) else: actions.append( - 'Local HEAD ({0}) does not match {1} but ' - 'update_head=False, HEAD would not be ' - 'updated locally'.format( - local_rev[:7], - remote_loc - ) + "Local HEAD ({0}) does not match {1} but " + "update_head=False, HEAD would not be " + "updated locally".format(local_rev[:7], remote_loc) ) # Check if upstream needs changing if not upstream and desired_upstream: actions.append( - 'Tracking branch would be set to {0}'.format( + "Tracking branch would be set to {0}".format( desired_upstream ) ) elif upstream and desired_upstream is False: - actions.append( - 'Tracking branch would be unset' - ) + actions.append("Tracking branch would be unset") elif desired_upstream and upstream != desired_upstream: actions.append( - 'Tracking branch would be ' - 'updated to {0}'.format(desired_upstream) + "Tracking branch would be " + "updated to {0}".format(desired_upstream) ) - if ret['changes']: + if ret["changes"]: return _neutral_test(ret, _format_comments(actions)) else: formatted_actions = _format_comments(actions) - if not revs_match \ - and not update_head \ - and formatted_actions: - ret['comment'] = formatted_actions + if not revs_match and not update_head and formatted_actions: + ret["comment"] = formatted_actions return ret - return _uptodate(ret, - target, - _format_comments(actions)) + return _uptodate(ret, target, _format_comments(actions)) if not upstream and desired_upstream: - upstream_action = ( - 'Tracking branch was set to {0}'.format( - desired_upstream - ) + upstream_action = "Tracking branch was set to {0}".format( + desired_upstream ) branch_opts = _get_branch_opts( branch, local_branch, all_local_branches, desired_upstream, - git_ver) + git_ver, + ) elif upstream and desired_upstream is False: # If the remote_rev is a tag or SHA1, and there is an # upstream tracking branch, we will unset it. However, we # can only do this if the git version is 1.8.0 or newer, as # the --unset-upstream option was not added until that # version. - if git_ver >= _LooseVersion('1.8.0'): - upstream_action = 'Tracking branch was unset' - branch_opts = ['--unset-upstream'] + if git_ver >= _LooseVersion("1.8.0"): + upstream_action = "Tracking branch was unset" + branch_opts = ["--unset-upstream"] else: branch_opts = None elif desired_upstream and upstream != desired_upstream: - upstream_action = ( - 'Tracking branch was updated to {0}'.format( - desired_upstream - ) + upstream_action = "Tracking branch was updated to {0}".format( + desired_upstream ) branch_opts = _get_branch_opts( branch, local_branch, all_local_branches, desired_upstream, - git_ver) + git_ver, + ) else: branch_opts = None if branch_opts is not None and local_branch is None: return _fail( ret, - 'Cannot set/unset upstream tracking branch, local ' - 'HEAD refers to nonexistent branch. This may have ' - 'been caused by cloning a remote repository for which ' - 'the default branch was renamed or deleted. If you ' - 'are unable to fix the remote repository, you can ' - 'work around this by setting the \'branch\' argument ' - '(which will ensure that the named branch is created ' - 'if it does not already exist).', - comments + "Cannot set/unset upstream tracking branch, local " + "HEAD refers to nonexistent branch. This may have " + "been caused by cloning a remote repository for which " + "the default branch was renamed or deleted. If you " + "are unable to fix the remote repository, you can " + "work around this by setting the 'branch' argument " + "(which will ensure that the named branch is created " + "if it does not already exist).", + comments, ) - remote_tags = set([ - x.replace('refs/tags/', '') for x in __salt__['git.ls_remote']( - cwd=target, - remote=remote, - opts="--tags", - user=user, - password=password, - identity=identity, - saltenv=__env__, - ignore_retcode=True, - output_encoding=output_encoding) if '^{}' not in x - ]) + remote_tags = set( + [ + x.replace("refs/tags/", "") + for x in __salt__["git.ls_remote"]( + cwd=target, + remote=remote, + opts="--tags", + user=user, + password=password, + identity=identity, + saltenv=__env__, + ignore_retcode=True, + output_encoding=output_encoding, + ) + if "^{}" not in x + ] + ) if all_local_tags != remote_tags: has_remote_rev = False new_tags = remote_tags - all_local_tags deleted_tags = all_local_tags - remote_tags if new_tags: - ret['changes']['new_tags'] = new_tags + ret["changes"]["new_tags"] = new_tags if sync_tags and deleted_tags: # Delete the local copy of the tags to keep up with the # remote repository. for tag_name in deleted_tags: try: - if not __opts__['test']: - __salt__['git.tag']( + if not __opts__["test"]: + __salt__["git.tag"]( target, tag_name, - opts='-d', + opts="-d", user=user, password=password, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError as exc: - ret.setdefault('warnings', []).append( - 'Failed to remove local tag \'{0}\':\n\n' - '{1}\n\n'.format(tag_name, exc) + ret.setdefault("warnings", []).append( + "Failed to remove local tag '{0}':\n\n" + "{1}\n\n".format(tag_name, exc) ) else: - ret['changes'].setdefault( - 'deleted_tags', []).append(tag_name) + ret["changes"].setdefault("deleted_tags", []).append( + tag_name + ) - if ret['changes'].get('deleted_tags'): + if ret["changes"].get("deleted_tags"): comments.append( - 'The following tags {0} removed from the local ' - 'checkout: {1}'.format( - 'would be' if __opts__['test'] - else 'were', - ', '.join(ret['changes']['deleted_tags']) + "The following tags {0} removed from the local " + "checkout: {1}".format( + "would be" if __opts__["test"] else "were", + ", ".join(ret["changes"]["deleted_tags"]), ) ) if not has_remote_rev: try: - fetch_changes = __salt__['git.fetch']( + fetch_changes = __salt__["git.fetch"]( target, remote=remote, force=force_fetch, @@ -1475,42 +1443,45 @@ def latest(name, password=password, identity=identity, saltenv=__env__, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError as exc: return _failed_fetch(ret, exc, comments) else: if fetch_changes: comments.append( - '{0} was fetched, resulting in updated ' - 'refs'.format(name) + "{0} was fetched, resulting in updated " + "refs".format(name) ) try: - __salt__['git.rev_parse']( + __salt__["git.rev_parse"]( target, - remote_rev + '^{commit}', + remote_rev + "^{commit}", user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError as exc: return _fail( ret, - 'Fetch did not successfully retrieve rev \'{0}\' ' - 'from {1}: {2}'.format(rev, name, exc) + "Fetch did not successfully retrieve rev '{0}' " + "from {1}: {2}".format(rev, name, exc), ) - if (not revs_match and not update_head) \ - and (branch is None or branch == local_branch): + if (not revs_match and not update_head) and ( + branch is None or branch == local_branch + ): # Rev now exists locally (was fetched), and since we're # not updating HEAD we'll just exit here. - ret['comment'] = remote_loc.capitalize() \ - if rev == 'HEAD' \ - else remote_loc - ret['comment'] += ( - ' is already present and local HEAD ({0}) does not ' - 'match, but update_head=False. HEAD has not been ' - 'updated locally.'.format(local_rev[:7]) + ret["comment"] = ( + remote_loc.capitalize() if rev == "HEAD" else remote_loc + ) + ret["comment"] += ( + " is already present and local HEAD ({0}) does not " + "match, but update_head=False. HEAD has not been " + "updated locally.".format(local_rev[:7]) ) return ret @@ -1519,18 +1490,18 @@ def latest(name, if base_rev is None: fast_forward = True else: - fast_forward = __salt__['git.merge_base']( + fast_forward = __salt__["git.merge_base"]( target, refs=[base_rev, remote_rev], is_ancestor=True, user=user, password=password, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) - if fast_forward is force_reset is False \ - or (fast_forward is True - and local_changes - and force_reset is False): + if fast_forward is force_reset is False or ( + fast_forward is True and local_changes and force_reset is False + ): return _not_fast_forward( ret, rev, @@ -1540,15 +1511,16 @@ def latest(name, local_branch, default_branch, local_changes, - comments) + comments, + ) if _need_branch_change(branch, local_branch): if local_changes and not force_checkout: return _fail( ret, - 'Local branch \'{0}\' has uncommitted ' - 'changes. Set \'force_checkout\' to True to ' - 'discard them and proceed.'.format(local_branch) + "Local branch '{0}' has uncommitted " + "changes. Set 'force_checkout' to True to " + "discard them and proceed.".format(local_branch), ) # TODO: Maybe re-retrieve all_local_branches to handle @@ -1556,89 +1528,87 @@ def latest(name, # added to the local checkout during a fetch that takes # a long time to complete. if branch not in all_local_branches: - if rev == 'HEAD': + if rev == "HEAD": checkout_rev = remote_rev else: - checkout_rev = desired_upstream \ - if desired_upstream \ - else rev - checkout_opts = ['-b', branch] + checkout_rev = desired_upstream if desired_upstream else rev + checkout_opts = ["-b", branch] else: checkout_rev = branch checkout_opts = [] - __salt__['git.checkout'](target, - checkout_rev, - force=force_checkout, - opts=checkout_opts, - user=user, - password=password, - output_encoding=output_encoding) - if '-b' in checkout_opts: - comments.append( - 'New branch \'{0}\' was checked out, with {1} ' - 'as a starting point'.format( - branch, - remote_loc - ) - ) - else: - comments.append( - '\'{0}\' was checked out'.format(checkout_rev) - ) - - if fast_forward is False: - __salt__['git.reset']( + __salt__["git.checkout"]( target, - opts=['--hard', remote_rev], + checkout_rev, + force=force_checkout, + opts=checkout_opts, user=user, password=password, output_encoding=output_encoding, - **lfs_opts) - ret['changes']['forced update'] = True - if local_changes: - comments.append('Uncommitted changes were discarded') - comments.append( - 'Repository was hard-reset to {0}'.format(remote_loc) ) - elif fast_forward is True \ - and local_changes \ - and force_reset is not False: - __salt__['git.discard_local_changes']( + if "-b" in checkout_opts: + comments.append( + "New branch '{0}' was checked out, with {1} " + "as a starting point".format(branch, remote_loc) + ) + else: + comments.append("'{0}' was checked out".format(checkout_rev)) + + if fast_forward is False: + __salt__["git.reset"]( + target, + opts=["--hard", remote_rev], + user=user, + password=password, + output_encoding=output_encoding, + **lfs_opts + ) + ret["changes"]["forced update"] = True + if local_changes: + comments.append("Uncommitted changes were discarded") + comments.append( + "Repository was hard-reset to {0}".format(remote_loc) + ) + elif ( + fast_forward is True and local_changes and force_reset is not False + ): + __salt__["git.discard_local_changes"]( target, user=user, password=password, - output_encoding=output_encoding) - comments.append('Uncommitted changes were discarded') + output_encoding=output_encoding, + ) + comments.append("Uncommitted changes were discarded") if branch_opts is not None: - __salt__['git.branch']( + __salt__["git.branch"]( target, opts=branch_opts, user=user, password=password, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) comments.append(upstream_action) # Fast-forward to the desired revision - if fast_forward is True \ - and not _revs_equal(base_rev, - remote_rev, - remote_rev_type): - if desired_upstream or rev == 'HEAD': + if fast_forward is True and not _revs_equal( + base_rev, remote_rev, remote_rev_type + ): + if desired_upstream or rev == "HEAD": # Check first to see if we are on a branch before # trying to merge changes. (The call to # git.symbolic_ref will only return output if HEAD # points to a branch.) - if __salt__['git.symbolic_ref']( - target, - 'HEAD', - opts=['--quiet'], - user=user, - password=password, - ignore_retcode=True, - output_encoding=output_encoding): + if __salt__["git.symbolic_ref"]( + target, + "HEAD", + opts=["--quiet"], + user=user, + password=password, + ignore_retcode=True, + output_encoding=output_encoding, + ): - if git_ver >= _LooseVersion('1.8.1.6'): + if git_ver >= _LooseVersion("1.8.1.6"): # --ff-only added in version 1.8.1.6. It's not # 100% necessary, but if we can use it, we'll # ensure that the merge doesn't go through if @@ -1647,71 +1617,70 @@ def latest(name, # attempt this merge if it's not a # fast-forward, but it's an extra layer of # protection. - merge_opts = ['--ff-only'] + merge_opts = ["--ff-only"] else: merge_opts = [] - __salt__['git.merge']( + __salt__["git.merge"]( target, rev=remote_rev, opts=merge_opts, user=user, password=password, output_encoding=output_encoding, - **lfs_opts) + **lfs_opts + ) comments.append( - 'Repository was fast-forwarded to {0}' - .format(remote_loc) + "Repository was fast-forwarded to {0}".format( + remote_loc + ) ) else: return _fail( ret, - 'Unable to fast-forward, HEAD is detached', - comments + "Unable to fast-forward, HEAD is detached", + comments, ) else: # Update is a fast forward, but we cannot merge to that # commit so we'll reset to it. - __salt__['git.reset']( + __salt__["git.reset"]( target, - opts=['--hard', - remote_rev if rev == 'HEAD' else rev], + opts=["--hard", remote_rev if rev == "HEAD" else rev], user=user, password=password, output_encoding=output_encoding, - **lfs_opts) + **lfs_opts + ) comments.append( - 'Repository was reset to {0} (fast-forward)' - .format(rev) + "Repository was reset to {0} (fast-forward)".format(rev) ) # TODO: Figure out how to add submodule update info to # test=True return data, and changes dict. if submodules: try: - __salt__['git.submodule']( + __salt__["git.submodule"]( target, - 'update', - opts=['--init', '--recursive'], + "update", + opts=["--init", "--recursive"], user=user, password=password, identity=identity, saltenv=__env__, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError as exc: return _failed_submodule_update(ret, exc, comments) elif bare: - if __opts__['test']: - msg = ( - 'Bare repository at {0} would be fetched' - .format(target) - ) - if ret['changes']: + if __opts__["test"]: + msg = "Bare repository at {0} would be fetched".format(target) + if ret["changes"]: return _neutral_test(ret, msg) else: return _uptodate(ret, target, msg) try: - fetch_changes = __salt__['git.fetch']( + fetch_changes = __salt__["git.fetch"]( target, remote=remote, force=force_fetch, @@ -1720,51 +1689,43 @@ def latest(name, password=password, identity=identity, saltenv=__env__, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError as exc: return _failed_fetch(ret, exc, comments) else: comments.append( - 'Bare repository at {0} was fetched{1}'.format( + "Bare repository at {0} was fetched{1}".format( target, - ', resulting in updated refs' - if fetch_changes - else '' + ", resulting in updated refs" if fetch_changes else "", ) ) try: - new_rev = __salt__['git.revision']( + new_rev = __salt__["git.revision"]( cwd=target, user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: new_rev = None except Exception as exc: # pylint: disable=broad-except - log.error( - 'Unexpected exception in git.latest state', - exc_info=True - ) + log.error("Unexpected exception in git.latest state", exc_info=True) if isinstance(exc, CommandExecutionError): msg = _strip_exc(exc) else: msg = six.text_type(exc) return _fail(ret, msg, comments) - if not bare and not _revs_equal(new_rev, - remote_rev, - remote_rev_type): - return _fail(ret, 'Failed to update repository', comments) + if not bare and not _revs_equal(new_rev, remote_rev, remote_rev_type): + return _fail(ret, "Failed to update repository", comments) if local_rev != new_rev: - log.info( - 'Repository %s updated: %s => %s', - target, local_rev, new_rev - ) - ret['comment'] = _format_comments(comments) - ret['changes']['revision'] = {'old': local_rev, 'new': new_rev} + log.info("Repository %s updated: %s => %s", target, local_rev, new_rev) + ret["comment"] = _format_comments(comments) + ret["changes"]["revision"] = {"old": local_rev, "new": new_rev} else: return _uptodate(ret, target, _format_comments(comments)) else: @@ -1774,19 +1735,20 @@ def latest(name, # Clone is required, and target directory exists, but the # ``force`` option is enabled, so we need to clear out its # contents to proceed. - if __opts__['test']: - ret['changes']['forced clone'] = True - ret['changes']['new'] = name + ' => ' + target + if __opts__["test"]: + ret["changes"]["forced clone"] = True + ret["changes"]["new"] = name + " => " + target return _neutral_test( ret, - 'Target directory {0} exists. Since force_clone=True, ' - 'the contents of {0} would be deleted, and {1} would ' - 'be cloned into this directory.'.format(target, name) + "Target directory {0} exists. Since force_clone=True, " + "the contents of {0} would be deleted, and {1} would " + "be cloned into this directory.".format(target, name), ) log.debug( - 'Removing contents of %s to clone repository %s in its ' - 'place (force_clone=True set in git.latest state)', - target, name + "Removing contents of %s to clone repository %s in its " + "place (force_clone=True set in git.latest state)", + target, + name, ) removal_errors = {} for target_object in target_contents: @@ -1798,254 +1760,254 @@ def latest(name, removal_errors[target_path] = exc if removal_errors: err_strings = [ - ' {0}\n {1}'.format(k, v) + " {0}\n {1}".format(k, v) for k, v in six.iteritems(removal_errors) ] return _fail( ret, - 'Unable to remove\n{0}'.format('\n'.join(err_strings)), - comments + "Unable to remove\n{0}".format("\n".join(err_strings)), + comments, ) - ret['changes']['forced clone'] = True + ret["changes"]["forced clone"] = True # Clone is required, but target dir exists and is non-empty. We # can't proceed. elif target_contents: return _fail( ret, - 'Target \'{0}\' exists, is non-empty and is not a git ' - 'repository. Set the \'force_clone\' option to True to ' - 'remove this directory\'s contents and proceed with ' - 'cloning the remote repository'.format(target) + "Target '{0}' exists, is non-empty and is not a git " + "repository. Set the 'force_clone' option to True to " + "remove this directory's contents and proceed with " + "cloning the remote repository".format(target), ) - log.debug('Target %s is not found, \'git clone\' is required', target) - if __opts__['test']: - ret['changes']['new'] = name + ' => ' + target + log.debug("Target %s is not found, 'git clone' is required", target) + if __opts__["test"]: + ret["changes"]["new"] = name + " => " + target return _neutral_test( - ret, - 'Repository {0} would be cloned to {1}'.format( - name, target - ) + ret, "Repository {0} would be cloned to {1}".format(name, target) ) try: - clone_opts = ['--mirror'] if mirror else ['--bare'] if bare else [] - if remote != 'origin': - clone_opts.extend(['--origin', remote]) + clone_opts = ["--mirror"] if mirror else ["--bare"] if bare else [] + if remote != "origin": + clone_opts.extend(["--origin", remote]) if depth is not None: - clone_opts.extend(['--depth', six.text_type(depth), '--branch', rev]) + clone_opts.extend(["--depth", six.text_type(depth), "--branch", rev]) # We're cloning a fresh repo, there is no local branch or revision local_branch = local_rev = None try: - __salt__['git.clone'](target, - name, - user=user, - password=password, - opts=clone_opts, - identity=identity, - https_user=https_user, - https_pass=https_pass, - saltenv=__env__, - output_encoding=output_encoding) + __salt__["git.clone"]( + target, + name, + user=user, + password=password, + opts=clone_opts, + identity=identity, + https_user=https_user, + https_pass=https_pass, + saltenv=__env__, + output_encoding=output_encoding, + ) except CommandExecutionError as exc: - msg = 'Clone failed: {0}'.format(_strip_exc(exc)) + msg = "Clone failed: {0}".format(_strip_exc(exc)) return _fail(ret, msg, comments) - ret['changes']['new'] = name + ' => ' + target + ret["changes"]["new"] = name + " => " + target comments.append( - '{0} cloned to {1}{2}'.format( + "{0} cloned to {1}{2}".format( name, target, - ' as mirror' if mirror - else ' as bare repository' if bare - else '' + " as mirror" if mirror else " as bare repository" if bare else "", ) ) if not bare: if not remote_rev: - if rev != 'HEAD': + if rev != "HEAD": # No HEAD means the remote repo is empty, which means # our new clone will also be empty. This state has # failed, since a rev was specified but no matching rev # exists on the remote host. msg = ( - '%s was cloned but is empty, so {0}/{1} ' - 'cannot be checked out'.format(remote, rev) + "%s was cloned but is empty, so {0}/{1} " + "cannot be checked out".format(remote, rev) ) log.error(msg, name) # Disable check for string substitution - return _fail(ret, msg % 'Repository', comments) # pylint: disable=E1321 + return _fail( + ret, msg % "Repository", comments + ) # pylint: disable=E1321 else: - if remote_rev_type == 'tag' \ - and rev not in __salt__['git.list_tags']( - target, - user=user, - password=password, - output_encoding=output_encoding): + if remote_rev_type == "tag" and rev not in __salt__[ + "git.list_tags" + ]( + target, + user=user, + password=password, + output_encoding=output_encoding, + ): return _fail( ret, - 'Revision \'{0}\' does not exist in clone' - .format(rev), - comments + "Revision '{0}' does not exist in clone".format(rev), + comments, ) if branch is not None: - if branch not in \ - __salt__['git.list_branches']( - target, - user=user, - password=password, - output_encoding=output_encoding): - if rev == 'HEAD': + if branch not in __salt__["git.list_branches"]( + target, + user=user, + password=password, + output_encoding=output_encoding, + ): + if rev == "HEAD": checkout_rev = remote_rev else: - checkout_rev = desired_upstream \ - if desired_upstream \ - else rev - __salt__['git.checkout']( + checkout_rev = ( + desired_upstream if desired_upstream else rev + ) + __salt__["git.checkout"]( target, checkout_rev, - opts=['-b', branch], + opts=["-b", branch], user=user, password=password, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) comments.append( - 'Branch \'{0}\' checked out, with {1} ' - 'as a starting point'.format( - branch, - remote_loc - ) + "Branch '{0}' checked out, with {1} " + "as a starting point".format(branch, remote_loc) ) local_rev, local_branch = _get_local_rev_and_branch( - target, - user, - password, - output_encoding=output_encoding) + target, user, password, output_encoding=output_encoding + ) - if local_branch is None \ - and remote_rev is not None \ - and 'HEAD' not in all_remote_refs: + if ( + local_branch is None + and remote_rev is not None + and "HEAD" not in all_remote_refs + ): return _fail( ret, - 'Remote HEAD refers to a ref that does not exist. ' - 'This can happen when the default branch on the ' - 'remote repository is renamed or deleted. If you ' - 'are unable to fix the remote repository, you can ' - 'work around this by setting the \'branch\' argument ' - '(which will ensure that the named branch is created ' - 'if it does not already exist).', - comments + "Remote HEAD refers to a ref that does not exist. " + "This can happen when the default branch on the " + "remote repository is renamed or deleted. If you " + "are unable to fix the remote repository, you can " + "work around this by setting the 'branch' argument " + "(which will ensure that the named branch is created " + "if it does not already exist).", + comments, ) if not _revs_equal(local_rev, remote_rev, remote_rev_type): - __salt__['git.reset']( + __salt__["git.reset"]( target, - opts=['--hard', remote_rev], + opts=["--hard", remote_rev], user=user, password=password, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) comments.append( - 'Repository was reset to {0}'.format(remote_loc) + "Repository was reset to {0}".format(remote_loc) ) try: - upstream = __salt__['git.rev_parse']( + upstream = __salt__["git.rev_parse"]( target, - local_branch + '@{upstream}', - opts=['--abbrev-ref'], + local_branch + "@{upstream}", + opts=["--abbrev-ref"], user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: upstream = False if not upstream and desired_upstream: - upstream_action = ( - 'Tracking branch was set to {0}'.format( - desired_upstream - ) + upstream_action = "Tracking branch was set to {0}".format( + desired_upstream ) branch_opts = _get_branch_opts( branch, local_branch, - __salt__['git.list_branches']( + __salt__["git.list_branches"]( target, user=user, password=password, - output_encoding=output_encoding), + output_encoding=output_encoding, + ), desired_upstream, - git_ver) + git_ver, + ) elif upstream and desired_upstream is False: # If the remote_rev is a tag or SHA1, and there is an # upstream tracking branch, we will unset it. However, # we can only do this if the git version is 1.8.0 or # newer, as the --unset-upstream option was not added # until that version. - if git_ver >= _LooseVersion('1.8.0'): - upstream_action = 'Tracking branch was unset' - branch_opts = ['--unset-upstream'] + if git_ver >= _LooseVersion("1.8.0"): + upstream_action = "Tracking branch was unset" + branch_opts = ["--unset-upstream"] else: branch_opts = None elif desired_upstream and upstream != desired_upstream: - upstream_action = ( - 'Tracking branch was updated to {0}'.format( - desired_upstream - ) + upstream_action = "Tracking branch was updated to {0}".format( + desired_upstream ) branch_opts = _get_branch_opts( branch, local_branch, - __salt__['git.list_branches']( + __salt__["git.list_branches"]( target, user=user, password=password, - output_encoding=output_encoding), + output_encoding=output_encoding, + ), desired_upstream, - git_ver) + git_ver, + ) else: branch_opts = None if branch_opts is not None: - __salt__['git.branch']( + __salt__["git.branch"]( target, opts=branch_opts, user=user, password=password, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) comments.append(upstream_action) if submodules and remote_rev: try: - __salt__['git.submodule']( + __salt__["git.submodule"]( target, - 'update', - opts=['--init', '--recursive'], + "update", + opts=["--init", "--recursive"], user=user, password=password, identity=identity, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError as exc: return _failed_submodule_update(ret, exc, comments) try: - new_rev = __salt__['git.revision']( + new_rev = __salt__["git.revision"]( cwd=target, user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: new_rev = None except Exception as exc: # pylint: disable=broad-except - log.error( - 'Unexpected exception in git.latest state', - exc_info=True - ) + log.error("Unexpected exception in git.latest state", exc_info=True) if isinstance(exc, CommandExecutionError): msg = _strip_exc(exc) else: @@ -2054,22 +2016,24 @@ def latest(name, msg = _format_comments(comments) log.info(msg) - ret['comment'] = msg + ret["comment"] = msg if new_rev is not None: - ret['changes']['revision'] = {'old': None, 'new': new_rev} + ret["changes"]["revision"] = {"old": None, "new": new_rev} return ret -def present(name, - force=False, - bare=True, - template=None, - separate_git_dir=None, - shared=None, - user=None, - password=None, - output_encoding=None): - ''' +def present( + name, + force=False, + bare=True, + template=None, + separate_git_dir=None, + shared=None, + user=None, + password=None, + output_encoding=None, +): + """ Ensure that a repository exists in the given directory .. warning:: @@ -2142,39 +2106,41 @@ def present(name, .. _`git-init(1)`: http://git-scm.com/docs/git-init .. _`worktree`: http://git-scm.com/docs/git-worktree - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # If the named directory is a git repo return True if os.path.isdir(name): - if bare and os.path.isfile(os.path.join(name, 'HEAD')): + if bare and os.path.isfile(os.path.join(name, "HEAD")): return ret - elif not bare and \ - (os.path.isdir(os.path.join(name, '.git')) or - __salt__['git.is_worktree'](name, - user=user, - password=password, - output_encoding=output_encoding)): + elif not bare and ( + os.path.isdir(os.path.join(name, ".git")) + or __salt__["git.is_worktree"]( + name, user=user, password=password, output_encoding=output_encoding + ) + ): return ret # Directory exists and is not a git repo, if force is set destroy the # directory and recreate, otherwise throw an error elif force: # Directory exists, and the ``force`` option is enabled, so we need # to clear out its contents to proceed. - if __opts__['test']: - ret['changes']['new'] = name - ret['changes']['forced init'] = True + if __opts__["test"]: + ret["changes"]["new"] = name + ret["changes"]["forced init"] = True return _neutral_test( ret, - 'Target directory {0} exists. Since force=True, the ' - 'contents of {0} would be deleted, and a {1}repository ' - 'would be initialized in its place.' - .format(name, 'bare ' if bare else '') + "Target directory {0} exists. Since force=True, the " + "contents of {0} would be deleted, and a {1}repository " + "would be initialized in its place.".format( + name, "bare " if bare else "" + ), ) log.debug( - 'Removing contents of %s to initialize %srepository in its ' - 'place (force=True set in git.present state)', - name, 'bare ' if bare else '' + "Removing contents of %s to initialize %srepository in its " + "place (force=True set in git.present state)", + name, + "bare " if bare else "", ) try: if os.path.islink(name): @@ -2182,78 +2148,71 @@ def present(name, else: salt.utils.files.rm_rf(name) except OSError as exc: - return _fail( - ret, - 'Unable to remove {0}: {1}'.format(name, exc) - ) + return _fail(ret, "Unable to remove {0}: {1}".format(name, exc)) else: - ret['changes']['forced init'] = True + ret["changes"]["forced init"] = True elif os.listdir(name): return _fail( ret, - 'Target \'{0}\' exists, is non-empty, and is not a git ' - 'repository. Set the \'force\' option to True to remove ' - 'this directory\'s contents and proceed with initializing a ' - 'repository'.format(name) + "Target '{0}' exists, is non-empty, and is not a git " + "repository. Set the 'force' option to True to remove " + "this directory's contents and proceed with initializing a " + "repository".format(name), ) # Run test is set - if __opts__['test']: - ret['changes']['new'] = name + if __opts__["test"]: + ret["changes"]["new"] = name return _neutral_test( - ret, - 'New {0}repository would be created'.format( - 'bare ' if bare else '' - ) + ret, "New {0}repository would be created".format("bare " if bare else "") ) - __salt__['git.init'](cwd=name, - bare=bare, - template=template, - separate_git_dir=separate_git_dir, - shared=shared, - user=user, - password=password, - output_encoding=output_encoding) + __salt__["git.init"]( + cwd=name, + bare=bare, + template=template, + separate_git_dir=separate_git_dir, + shared=shared, + user=user, + password=password, + output_encoding=output_encoding, + ) - actions = [ - 'Initialized {0}repository in {1}'.format( - 'bare ' if bare else '', - name - ) - ] + actions = ["Initialized {0}repository in {1}".format("bare " if bare else "", name)] if template: - actions.append('Template directory set to {0}'.format(template)) + actions.append("Template directory set to {0}".format(template)) if separate_git_dir: - actions.append('Gitdir set to {0}'.format(separate_git_dir)) - message = '. '.join(actions) + actions.append("Gitdir set to {0}".format(separate_git_dir)) + message = ". ".join(actions) if len(actions) > 1: - message += '.' + message += "." log.info(message) - ret['changes']['new'] = name - ret['comment'] = message + ret["changes"]["new"] = name + ret["comment"] = message return ret -def detached(name, - rev, - target=None, - remote='origin', - user=None, - password=None, - force_clone=False, - force_checkout=False, - fetch_remote=True, - hard_reset=False, - submodules=False, - identity=None, - https_user=None, - https_pass=None, - onlyif=None, - unless=None, - output_encoding=None, - **kwargs): - ''' +def detached( + name, + rev, + target=None, + remote="origin", + user=None, + password=None, + force_clone=False, + force_checkout=False, + fetch_remote=True, + hard_reset=False, + submodules=False, + identity=None, + https_user=None, + https_pass=None, + onlyif=None, + unless=None, + output_encoding=None, + **kwargs +): + """ .. versionadded:: 2016.3.0 Make sure a repository is cloned to the given target directory and is @@ -2341,27 +2300,22 @@ def detached(name, Unicode characters. .. versionadded:: 2018.3.1 - ''' + """ - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} kwargs = salt.utils.args.clean_kwargs(**kwargs) if kwargs: - return _fail( - ret, - salt.utils.args.invalid_kwargs(kwargs, raise_exc=False) - ) + return _fail(ret, salt.utils.args.invalid_kwargs(kwargs, raise_exc=False)) if not rev: return _fail( - ret, - '\'{0}\' is not a valid value for the \'rev\' argument'.format(rev) + ret, "'{0}' is not a valid value for the 'rev' argument".format(rev) ) if not target: return _fail( - ret, - '\'{0}\' is not a valid value for the \'target\' argument'.format(rev) + ret, "'{0}' is not a valid value for the 'target' argument".format(rev) ) # Ensure that certain arguments are strings to ensure that comparisons work @@ -2371,10 +2325,7 @@ def detached(name, if not isinstance(target, six.string_types): target = six.text_type(target) if not os.path.isabs(target): - return _fail( - ret, - 'Target \'{0}\' is not an absolute path'.format(target) - ) + return _fail(ret, "Target '{0}' is not an absolute path".format(target)) if user is not None and not isinstance(user, six.string_types): user = six.text_type(user) if remote is not None and not isinstance(remote, six.string_types): @@ -2383,26 +2334,20 @@ def detached(name, if isinstance(identity, six.string_types): identity = [identity] elif not isinstance(identity, list): - return _fail(ret, 'Identity must be either a list or a string') + return _fail(ret, "Identity must be either a list or a string") identity = [os.path.expanduser(x) for x in identity] for ident_path in identity: - if 'salt://' in ident_path: + if "salt://" in ident_path: try: - ident_path = __salt__['cp.cache_file'](ident_path) + ident_path = __salt__["cp.cache_file"](ident_path) except IOError as exc: - log.error('Failed to cache %s: %s', ident_path, exc) + log.error("Failed to cache %s: %s", ident_path, exc) return _fail( - ret, - 'Identity \'{0}\' does not exist.'.format( - ident_path - ) + ret, "Identity '{0}' does not exist.".format(ident_path) ) if not os.path.isabs(ident_path): return _fail( - ret, - 'Identity \'{0}\' is not an absolute path'.format( - ident_path - ) + ret, "Identity '{0}' is not an absolute path".format(ident_path) ) if https_user is not None and not isinstance(https_user, six.string_types): https_user = six.text_type(https_user) @@ -2412,16 +2357,12 @@ def detached(name, if os.path.isfile(target): return _fail( ret, - 'Target \'{0}\' exists and is a regular file, cannot proceed' - .format(target) + "Target '{0}' exists and is a regular file, cannot proceed".format(target), ) try: desired_fetch_url = salt.utils.url.add_http_basic_auth( - name, - https_user, - https_pass, - https_only=True + name, https_user, https_pass, https_only=True ) except ValueError as exc: return _fail(ret, exc.__str__()) @@ -2429,49 +2370,44 @@ def detached(name, redacted_fetch_url = salt.utils.url.redact_http_basic_auth(desired_fetch_url) # Check if onlyif or unless conditions match - run_check_cmd_kwargs = {'runas': user} - if 'shell' in __grains__: - run_check_cmd_kwargs['shell'] = __grains__['shell'] - cret = mod_run_check( - run_check_cmd_kwargs, onlyif, unless - ) + run_check_cmd_kwargs = {"runas": user} + if "shell" in __grains__: + run_check_cmd_kwargs["shell"] = __grains__["shell"] + cret = mod_run_check(run_check_cmd_kwargs, onlyif, unless) if isinstance(cret, dict): ret.update(cret) return ret # Determine if supplied ref is a hash - remote_rev_type = 'ref' - if len(rev) <= 40 \ - and all(x in string.hexdigits for x in rev): + remote_rev_type = "ref" + if len(rev) <= 40 and all(x in string.hexdigits for x in rev): rev = rev.lower() - remote_rev_type = 'hash' + remote_rev_type = "hash" comments = [] hash_exists_locally = False local_commit_id = None - gitdir = os.path.join(target, '.git') - if os.path.isdir(gitdir) \ - or __salt__['git.is_worktree'](target, - user=user, - password=password, - output_encoding=output_encoding): + gitdir = os.path.join(target, ".git") + if os.path.isdir(gitdir) or __salt__["git.is_worktree"]( + target, user=user, password=password, output_encoding=output_encoding + ): # Target directory is a git repository or git worktree local_commit_id = _get_local_rev_and_branch( - target, - user, - password, - output_encoding=output_encoding)[0] + target, user, password, output_encoding=output_encoding + )[0] - if remote_rev_type is 'hash': + if remote_rev_type is "hash": try: - __salt__['git.describe'](target, - rev, - user=user, - password=password, - ignore_retcode=True, - output_encoding=output_encoding) + __salt__["git.describe"]( + target, + rev, + user=user, + password=password, + ignore_retcode=True, + output_encoding=output_encoding, + ) except CommandExecutionError: hash_exists_locally = False else: @@ -2479,13 +2415,15 @@ def detached(name, hash_exists_locally = True else: # Check that remote is present and set to correct url - remotes = __salt__['git.remotes'](target, - user=user, - password=password, - redact_auth=False, - output_encoding=output_encoding) + remotes = __salt__["git.remotes"]( + target, + user=user, + password=password, + redact_auth=False, + output_encoding=output_encoding, + ) - if remote in remotes and name in remotes[remote]['fetch']: + if remote in remotes and name in remotes[remote]["fetch"]: pass else: # The fetch_url for the desired remote does not match the @@ -2493,29 +2431,26 @@ def detached(name, # remote URL. current_fetch_url = None if remote in remotes: - current_fetch_url = remotes[remote]['fetch'] + current_fetch_url = remotes[remote]["fetch"] - if __opts__['test']: + if __opts__["test"]: return _neutral_test( - ret, - 'Remote {0} would be set to {1}'.format( - remote, name - ) + ret, "Remote {0} would be set to {1}".format(remote, name) ) - __salt__['git.remote_set'](target, - url=name, - remote=remote, - user=user, - password=password, - https_user=https_user, - https_pass=https_pass, - output_encoding=output_encoding) + __salt__["git.remote_set"]( + target, + url=name, + remote=remote, + user=user, + password=password, + https_user=https_user, + https_pass=https_pass, + output_encoding=output_encoding, + ) comments.append( - 'Remote {0} updated from \'{1}\' to \'{2}\''.format( - remote, - current_fetch_url, - name + "Remote {0} updated from '{1}' to '{2}'".format( + remote, current_fetch_url, name ) ) @@ -2527,17 +2462,18 @@ def detached(name, # Clone is required, and target directory exists, but the # ``force`` option is enabled, so we need to clear out its # contents to proceed. - if __opts__['test']: + if __opts__["test"]: return _neutral_test( ret, - 'Target directory {0} exists. Since force_clone=True, ' - 'the contents of {0} would be deleted, and {1} would ' - 'be cloned into this directory.'.format(target, name) + "Target directory {0} exists. Since force_clone=True, " + "the contents of {0} would be deleted, and {1} would " + "be cloned into this directory.".format(target, name), ) log.debug( - 'Removing contents of %s to clone repository %s in its ' - 'place (force_clone=True set in git.detached state)', - target, name + "Removing contents of %s to clone repository %s in its " + "place (force_clone=True set in git.detached state)", + target, + name, ) removal_errors = {} for target_object in target_contents: @@ -2549,56 +2485,52 @@ def detached(name, removal_errors[target_path] = exc if removal_errors: err_strings = [ - ' {0}\n {1}'.format(k, v) + " {0}\n {1}".format(k, v) for k, v in six.iteritems(removal_errors) ] return _fail( ret, - 'Unable to remove\n{0}'.format('\n'.join(err_strings)), - comments + "Unable to remove\n{0}".format("\n".join(err_strings)), + comments, ) - ret['changes']['forced clone'] = True + ret["changes"]["forced clone"] = True elif target_contents: # Clone is required, but target dir exists and is non-empty. We # can't proceed. return _fail( ret, - 'Target \'{0}\' exists, is non-empty and is not a git ' - 'repository. Set the \'force_clone\' option to True to ' - 'remove this directory\'s contents and proceed with ' - 'cloning the remote repository'.format(target) + "Target '{0}' exists, is non-empty and is not a git " + "repository. Set the 'force_clone' option to True to " + "remove this directory's contents and proceed with " + "cloning the remote repository".format(target), ) - log.debug('Target %s is not found, \'git clone\' is required', target) - if __opts__['test']: + log.debug("Target %s is not found, 'git clone' is required", target) + if __opts__["test"]: return _neutral_test( - ret, - 'Repository {0} would be cloned to {1}'.format( - name, target - ) + ret, "Repository {0} would be cloned to {1}".format(name, target) ) try: - clone_opts = ['--no-checkout'] - if remote != 'origin': - clone_opts.extend(['--origin', remote]) + clone_opts = ["--no-checkout"] + if remote != "origin": + clone_opts.extend(["--origin", remote]) - __salt__['git.clone'](target, - name, - user=user, - password=password, - opts=clone_opts, - identity=identity, - https_user=https_user, - https_pass=https_pass, - saltenv=__env__, - output_encoding=output_encoding) - comments.append('{0} cloned to {1}'.format(name, target)) + __salt__["git.clone"]( + target, + name, + user=user, + password=password, + opts=clone_opts, + identity=identity, + https_user=https_user, + https_pass=https_pass, + saltenv=__env__, + output_encoding=output_encoding, + ) + comments.append("{0} cloned to {1}".format(name, target)) except Exception as exc: # pylint: disable=broad-except - log.error( - 'Unexpected exception in git.detached state', - exc_info=True - ) + log.error("Unexpected exception in git.detached state", exc_info=True) if isinstance(exc, CommandExecutionError): msg = _strip_exc(exc) else: @@ -2607,20 +2539,19 @@ def detached(name, # Repository exists and is ready for fetch/checkout refspecs = [ - 'refs/heads/*:refs/remotes/{0}/*'.format(remote), - '+refs/tags/*:refs/tags/*' + "refs/heads/*:refs/remotes/{0}/*".format(remote), + "+refs/tags/*:refs/tags/*", ] if hash_exists_locally or fetch_remote is False: pass else: # Fetch refs from remote - if __opts__['test']: + if __opts__["test"]: return _neutral_test( - ret, - 'Repository remote {0} would be fetched'.format(remote) + ret, "Repository remote {0} would be fetched".format(remote) ) try: - fetch_changes = __salt__['git.fetch']( + fetch_changes = __salt__["git.fetch"]( target, remote=remote, force=True, @@ -2629,36 +2560,31 @@ def detached(name, password=password, identity=identity, saltenv=__env__, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError as exc: - msg = 'Fetch failed' - msg += ':\n\n' + six.text_type(exc) + msg = "Fetch failed" + msg += ":\n\n" + six.text_type(exc) return _fail(ret, msg, comments) else: if fetch_changes: comments.append( - 'Remote {0} was fetched, resulting in updated ' - 'refs'.format(remote) + "Remote {0} was fetched, resulting in updated " + "refs".format(remote) ) # get refs and checkout - checkout_commit_id = '' - if remote_rev_type is 'hash': - if __salt__['git.describe']( - target, - rev, - user=user, - password=password, - output_encoding=output_encoding): + checkout_commit_id = "" + if remote_rev_type is "hash": + if __salt__["git.describe"]( + target, rev, user=user, password=password, output_encoding=output_encoding + ): checkout_commit_id = rev else: - return _fail( - ret, - 'Revision \'{0}\' does not exist'.format(rev) - ) + return _fail(ret, "Revision '{0}' does not exist".format(rev)) else: try: - all_remote_refs = __salt__['git.remote_refs']( + all_remote_refs = __salt__["git.remote_refs"]( target, user=user, password=password, @@ -2666,111 +2592,109 @@ def detached(name, https_user=https_user, https_pass=https_pass, ignore_retcode=False, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) - if 'refs/remotes/'+remote+'/'+rev in all_remote_refs: - checkout_commit_id = all_remote_refs['refs/remotes/' + remote + '/' + rev] - elif 'refs/tags/' + rev in all_remote_refs: - checkout_commit_id = all_remote_refs['refs/tags/' + rev] + if "refs/remotes/" + remote + "/" + rev in all_remote_refs: + checkout_commit_id = all_remote_refs[ + "refs/remotes/" + remote + "/" + rev + ] + elif "refs/tags/" + rev in all_remote_refs: + checkout_commit_id = all_remote_refs["refs/tags/" + rev] else: - return _fail( - ret, - 'Revision \'{0}\' does not exist'.format(rev) - ) + return _fail(ret, "Revision '{0}' does not exist".format(rev)) except CommandExecutionError as exc: return _fail( - ret, - 'Failed to list refs for {0}: {1}'.format(remote, _strip_exc(exc)) + ret, "Failed to list refs for {0}: {1}".format(remote, _strip_exc(exc)) ) if hard_reset: - if __opts__['test']: + if __opts__["test"]: return _neutral_test( - ret, - 'Hard reset to HEAD would be performed on {0}'.format(target) + ret, "Hard reset to HEAD would be performed on {0}".format(target) ) - __salt__['git.reset']( + __salt__["git.reset"]( target, - opts=['--hard', 'HEAD'], + opts=["--hard", "HEAD"], user=user, password=password, - output_encoding=output_encoding) - comments.append( - 'Repository was reset to HEAD before checking out revision' + output_encoding=output_encoding, ) + comments.append("Repository was reset to HEAD before checking out revision") # TODO: implement clean function for git module and add clean flag if checkout_commit_id == local_commit_id: new_rev = None else: - if __opts__['test']: - ret['changes']['HEAD'] = {'old': local_commit_id, 'new': checkout_commit_id} + if __opts__["test"]: + ret["changes"]["HEAD"] = {"old": local_commit_id, "new": checkout_commit_id} return _neutral_test( ret, - 'Commit ID {0} would be checked out at {1}'.format( - checkout_commit_id, - target - ) + "Commit ID {0} would be checked out at {1}".format( + checkout_commit_id, target + ), ) - __salt__['git.checkout'](target, - checkout_commit_id, - force=force_checkout, - user=user, - password=password, - output_encoding=output_encoding) + __salt__["git.checkout"]( + target, + checkout_commit_id, + force=force_checkout, + user=user, + password=password, + output_encoding=output_encoding, + ) comments.append( - 'Commit ID {0} was checked out at {1}'.format( - checkout_commit_id, - target - ) + "Commit ID {0} was checked out at {1}".format(checkout_commit_id, target) ) try: - new_rev = __salt__['git.revision']( + new_rev = __salt__["git.revision"]( cwd=target, user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: new_rev = None if submodules: - __salt__['git.submodule'](target, - 'update', - opts=['--init', '--recursive'], - user=user, - password=password, - identity=identity, - output_encoding=output_encoding) - comments.append( - 'Submodules were updated' + __salt__["git.submodule"]( + target, + "update", + opts=["--init", "--recursive"], + user=user, + password=password, + identity=identity, + output_encoding=output_encoding, ) + comments.append("Submodules were updated") if new_rev is not None: - ret['changes']['HEAD'] = {'old': local_commit_id, 'new': new_rev} + ret["changes"]["HEAD"] = {"old": local_commit_id, "new": new_rev} else: comments.append("Already checked out at correct revision") msg = _format_comments(comments) log.info(msg) - ret['comment'] = msg + ret["comment"] = msg return ret -def cloned(name, - target=None, - branch=None, - user=None, - password=None, - identity=None, - https_user=None, - https_pass=None, - output_encoding=None): - ''' +def cloned( + name, + target=None, + branch=None, + user=None, + password=None, + identity=None, + https_user=None, + https_pass=None, + output_encoding=None, +): + """ .. versionadded:: 2018.3.3,2019.2.0 Ensure that a repository has been cloned to the specified target directory. @@ -2822,36 +2746,35 @@ def cloned(name, This should only be needed if the files in the repository were created with filenames using an encoding other than UTF-8 to handle Unicode characters. - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if target is None: - ret['comment'] = '\'target\' argument is required' + ret["comment"] = "'target' argument is required" return ret elif not isinstance(target, six.string_types): target = six.text_type(target) if not os.path.isabs(target): - ret['comment'] = '\'target\' path must be absolute' + ret["comment"] = "'target' path must be absolute" return ret if branch is not None: if not isinstance(branch, six.string_types): branch = six.text_type(branch) if not branch: - ret['comment'] = 'Invalid \'branch\' argument' + ret["comment"] = "Invalid 'branch' argument" return ret if not os.path.exists(target): need_clone = True else: try: - __salt__['git.status'](target, - user=user, - password=password, - output_encoding=output_encoding) + __salt__["git.status"]( + target, user=user, password=password, output_encoding=output_encoding + ) except Exception as exc: # pylint: disable=broad-except - ret['comment'] = six.text_type(exc) + ret["comment"] = six.text_type(exc) return ret else: need_clone = False @@ -2859,112 +2782,112 @@ def cloned(name, comments = [] def _clone_changes(ret): - ret['changes']['new'] = name + ' => ' + target + ret["changes"]["new"] = name + " => " + target def _branch_changes(ret, old, new): - ret['changes']['branch'] = {'old': old, 'new': new} + ret["changes"]["branch"] = {"old": old, "new": new} if need_clone: - if __opts__['test']: + if __opts__["test"]: _clone_changes(ret) - comment = '{0} would be cloned to {1}{2}'.format( + comment = "{0} would be cloned to {1}{2}".format( name, target, - ' with branch \'{0}\''.format(branch) - if branch is not None - else '' + " with branch '{0}'".format(branch) if branch is not None else "", ) return _neutral_test(ret, comment) - clone_opts = ['--branch', branch] if branch is not None else None + clone_opts = ["--branch", branch] if branch is not None else None try: - __salt__['git.clone'](target, - name, - opts=clone_opts, - user=user, - password=password, - identity=identity, - https_user=https_user, - https_pass=https_pass, - output_encoding=output_encoding) + __salt__["git.clone"]( + target, + name, + opts=clone_opts, + user=user, + password=password, + identity=identity, + https_user=https_user, + https_pass=https_pass, + output_encoding=output_encoding, + ) except CommandExecutionError as exc: - msg = 'Clone failed: {0}'.format(_strip_exc(exc)) + msg = "Clone failed: {0}".format(_strip_exc(exc)) return _fail(ret, msg, comments) comments.append( - '{0} cloned to {1}{2}'.format( + "{0} cloned to {1}{2}".format( name, target, - ' with branch \'{0}\''.format(branch) - if branch is not None - else '' + " with branch '{0}'".format(branch) if branch is not None else "", ) ) _clone_changes(ret) - ret['comment'] = _format_comments(comments) - ret['result'] = True + ret["comment"] = _format_comments(comments) + ret["result"] = True return ret else: if branch is None: return _already_cloned(ret, target, branch, comments) else: - current_branch = __salt__['git.current_branch']( - target, - user=user, - password=password, - output_encoding=output_encoding) + current_branch = __salt__["git.current_branch"]( + target, user=user, password=password, output_encoding=output_encoding + ) if current_branch == branch: return _already_cloned(ret, target, branch, comments) else: - if __opts__['test']: + if __opts__["test"]: _branch_changes(ret, current_branch, branch) return _neutral_test( - ret, - 'Branch would be changed to \'{0}\''.format(branch)) + ret, "Branch would be changed to '{0}'".format(branch) + ) try: - __salt__['git.rev_parse']( + __salt__["git.rev_parse"]( target, rev=branch, user=user, password=password, ignore_retcode=True, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError: # Local head does not exist, so we need to check out a new # branch at the remote rev - checkout_rev = '/'.join(('origin', branch)) - checkout_opts = ['-b', branch] + checkout_rev = "/".join(("origin", branch)) + checkout_opts = ["-b", branch] else: # Local head exists, so we just need to check it out checkout_rev = branch checkout_opts = None try: - __salt__['git.checkout']( + __salt__["git.checkout"]( target, rev=checkout_rev, opts=checkout_opts, user=user, password=password, - output_encoding=output_encoding) + output_encoding=output_encoding, + ) except CommandExecutionError as exc: - msg = 'Failed to change branch to \'{0}\': {1}'.format(branch, exc) + msg = "Failed to change branch to '{0}': {1}".format(branch, exc) return _fail(ret, msg, comments) else: - comments.append('Branch changed to \'{0}\''.format(branch)) + comments.append("Branch changed to '{0}'".format(branch)) _branch_changes(ret, current_branch, branch) - ret['comment'] = _format_comments(comments) - ret['result'] = True + ret["comment"] = _format_comments(comments) + ret["result"] = True return ret -def config_unset(name, - value_regex=None, - repo=None, - user=None, - password=None, - output_encoding=None, - **kwargs): - r''' +def config_unset( + name, + value_regex=None, + repo=None, + user=None, + password=None, + output_encoding=None, + **kwargs +): + r""" .. versionadded:: 2015.8.0 Ensure that the named config key is not present @@ -3046,30 +2969,27 @@ def config_unset(name, git.config_unset: - name: foo.bar - global: True - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'No matching keys are set'} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "No matching keys are set", + } # Sanitize kwargs and make sure that no invalid ones were passed. This # allows us to accept 'global' as an argument to this function without # shadowing global(), while also not allowing unwanted arguments to be # passed. kwargs = salt.utils.args.clean_kwargs(**kwargs) - global_ = kwargs.pop('global', False) - all_ = kwargs.pop('all', False) + global_ = kwargs.pop("global", False) + all_ = kwargs.pop("all", False) if kwargs: - return _fail( - ret, - salt.utils.args.invalid_kwargs(kwargs, raise_exc=False) - ) + return _fail(ret, salt.utils.args.invalid_kwargs(kwargs, raise_exc=False)) if not global_ and not repo: return _fail( - ret, - 'Non-global config options require the \'repo\' argument to be ' - 'set' + ret, "Non-global config options require the 'repo' argument to be " "set" ) if not isinstance(name, six.string_types): @@ -3079,10 +2999,10 @@ def config_unset(name, value_regex = six.text_type(value_regex) # Ensure that the key regex matches the full key name - key = '^' + name.lstrip('^').rstrip('$') + '$' + key = "^" + name.lstrip("^").rstrip("$") + "$" # Get matching keys/values - pre_matches = __salt__['git.config_get_regexp']( + pre_matches = __salt__["git.config_get_regexp"]( cwd=repo, key=key, value_regex=value_regex, @@ -3090,7 +3010,7 @@ def config_unset(name, password=password, ignore_retcode=True, output_encoding=output_encoding, - **{'global': global_} + **{"global": global_} ) if not pre_matches: @@ -3100,31 +3020,30 @@ def config_unset(name, # Perform sanity check on the matches. We can't proceed if the value_regex # matches more than one value in a given key, and 'all' is not set to True if not all_: - greedy_matches = ['{0} ({1})'.format(x, ', '.join(y)) - for x, y in six.iteritems(pre_matches) - if len(y) > 1] + greedy_matches = [ + "{0} ({1})".format(x, ", ".join(y)) + for x, y in six.iteritems(pre_matches) + if len(y) > 1 + ] if greedy_matches: if value_regex is not None: return _fail( ret, - 'Multiple values are matched by value_regex for the ' - 'following keys (set \'all\' to True to force removal): ' - '{0}'.format('; '.join(greedy_matches)) + "Multiple values are matched by value_regex for the " + "following keys (set 'all' to True to force removal): " + "{0}".format("; ".join(greedy_matches)), ) else: return _fail( ret, - 'Multivar(s) matched by the key expression (set \'all\' ' - 'to True to force removal): {0}'.format( - '; '.join(greedy_matches) - ) + "Multivar(s) matched by the key expression (set 'all' " + "to True to force removal): {0}".format("; ".join(greedy_matches)), ) - if __opts__['test']: - ret['changes'] = pre_matches + if __opts__["test"]: + ret["changes"] = pre_matches return _neutral_test( - ret, - '{0} key(s) would have value(s) unset'.format(len(pre_matches)) + ret, "{0} key(s) would have value(s) unset".format(len(pre_matches)) ) if value_regex is None: @@ -3132,7 +3051,7 @@ def config_unset(name, else: # Get all keys matching the key expression, so we can accurately report # on changes made. - pre = __salt__['git.config_get_regexp']( + pre = __salt__["git.config_get_regexp"]( cwd=repo, key=key, value_regex=None, @@ -3140,7 +3059,7 @@ def config_unset(name, password=password, ignore_retcode=True, output_encoding=output_encoding, - **{'global': global_} + **{"global": global_} ) failed = [] @@ -3148,7 +3067,7 @@ def config_unset(name, # through the pre_matches dict and unset each matching key individually. for key_name in pre_matches: try: - __salt__['git.config_unset']( + __salt__["git.config_unset"]( cwd=repo, key=name, value_regex=value_regex, @@ -3156,24 +3075,24 @@ def config_unset(name, user=user, password=password, output_encoding=output_encoding, - **{'global': global_} + **{"global": global_} ) except CommandExecutionError as exc: - msg = 'Failed to unset \'{0}\''.format(key_name) + msg = "Failed to unset '{0}'".format(key_name) if value_regex is not None: - msg += ' using value_regex \'{1}\'' - msg += ': ' + _strip_exc(exc) + msg += " using value_regex '{1}'" + msg += ": " + _strip_exc(exc) log.error(msg) failed.append(key_name) if failed: return _fail( ret, - 'Error(s) occurred unsetting values for the following keys (see ' - 'the minion log for details): {0}'.format(', '.join(failed)) + "Error(s) occurred unsetting values for the following keys (see " + "the minion log for details): {0}".format(", ".join(failed)), ) - post = __salt__['git.config_get_regexp']( + post = __salt__["git.config_get_regexp"]( cwd=repo, key=key, value_regex=None, @@ -3181,20 +3100,20 @@ def config_unset(name, password=password, ignore_retcode=True, output_encoding=output_encoding, - **{'global': global_} + **{"global": global_} ) for key_name in pre: if key_name not in post: - ret['changes'][key_name] = pre[key_name] + ret["changes"][key_name] = pre[key_name] unset = [x for x in pre[key_name] if x not in post[key_name]] if unset: - ret['changes'][key_name] = unset + ret["changes"][key_name] = unset if value_regex is None: post_matches = post else: - post_matches = __salt__['git.config_get_regexp']( + post_matches = __salt__["git.config_get_regexp"]( cwd=repo, key=key, value_regex=value_regex, @@ -3202,30 +3121,30 @@ def config_unset(name, password=password, ignore_retcode=True, output_encoding=output_encoding, - **{'global': global_} + **{"global": global_} ) if post_matches: - failed = ['{0} ({1})'.format(x, ', '.join(y)) - for x, y in six.iteritems(post_matches)] - return _fail( - ret, - 'Failed to unset value(s): {0}'.format('; '.join(failed)) - ) + failed = [ + "{0} ({1})".format(x, ", ".join(y)) for x, y in six.iteritems(post_matches) + ] + return _fail(ret, "Failed to unset value(s): {0}".format("; ".join(failed))) - ret['comment'] = 'Value(s) successfully unset' + ret["comment"] = "Value(s) successfully unset" return ret -def config_set(name, - value=None, - multivar=None, - repo=None, - user=None, - password=None, - output_encoding=None, - **kwargs): - ''' +def config_set( + name, + value=None, + multivar=None, + repo=None, + user=None, + password=None, + output_encoding=None, + **kwargs +): + """ .. versionadded:: 2014.7.0 .. versionchanged:: 2015.8.0 Renamed from ``git.config`` to ``git.config_set``. For earlier @@ -3309,35 +3228,24 @@ def config_set(name, - value: Foo Bar - user: foo - global: True - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if value is not None and multivar is not None: - return _fail( - ret, - 'Only one of \'value\' and \'multivar\' is permitted' - ) + return _fail(ret, "Only one of 'value' and 'multivar' is permitted") # Sanitize kwargs and make sure that no invalid ones were passed. This # allows us to accept 'global' as an argument to this function without # shadowing global(), while also not allowing unwanted arguments to be # passed. kwargs = salt.utils.args.clean_kwargs(**kwargs) - global_ = kwargs.pop('global', False) + global_ = kwargs.pop("global", False) if kwargs: - return _fail( - ret, - salt.utils.args.invalid_kwargs(kwargs, raise_exc=False) - ) + return _fail(ret, salt.utils.args.invalid_kwargs(kwargs, raise_exc=False)) if not global_ and not repo: return _fail( - ret, - 'Non-global config options require the \'repo\' argument to be ' - 'set' + ret, "Non-global config options require the 'repo' argument to be " "set" ) if not isinstance(name, six.string_types): @@ -3345,14 +3253,14 @@ def config_set(name, if value is not None: if not isinstance(value, six.string_types): value = six.text_type(value) - value_comment = '\'' + value + '\'' + value_comment = "'" + value + "'" desired = [value] if multivar is not None: if not isinstance(multivar, list): try: - multivar = multivar.split(',') + multivar = multivar.split(",") except AttributeError: - multivar = six.text_type(multivar).split(',') + multivar = six.text_type(multivar).split(",") else: new_multivar = [] for item in multivar: @@ -3365,37 +3273,35 @@ def config_set(name, desired = multivar # Get current value - pre = __salt__['git.config_get']( + pre = __salt__["git.config_get"]( cwd=repo, key=name, user=user, password=password, ignore_retcode=True, output_encoding=output_encoding, - **{'all': True, 'global': global_} + **{"all": True, "global": global_} ) if desired == pre: - ret['comment'] = '{0}\'{1}\' is already set to {2}'.format( - 'Global key ' if global_ else '', - name, - value_comment + ret["comment"] = "{0}'{1}' is already set to {2}".format( + "Global key " if global_ else "", name, value_comment ) return ret - if __opts__['test']: - ret['changes'] = {'old': pre, 'new': desired} - msg = '{0}\'{1}\' would be {2} {3}'.format( - 'Global key ' if global_ else '', + if __opts__["test"]: + ret["changes"] = {"old": pre, "new": desired} + msg = "{0}'{1}' would be {2} {3}".format( + "Global key " if global_ else "", name, - 'added as' if pre is None else 'set to', - value_comment + "added as" if pre is None else "set to", + value_comment, ) return _neutral_test(ret, msg) try: # Set/update config value - post = __salt__['git.config_set']( + post = __salt__["git.config_set"]( cwd=repo, key=name, value=value, @@ -3403,57 +3309,49 @@ def config_set(name, user=user, password=password, output_encoding=output_encoding, - **{'global': global_} + **{"global": global_} ) except CommandExecutionError as exc: return _fail( ret, - 'Failed to set {0}\'{1}\' to {2}: {3}'.format( - 'global key ' if global_ else '', - name, - value_comment, - _strip_exc(exc) - ) + "Failed to set {0}'{1}' to {2}: {3}".format( + "global key " if global_ else "", name, value_comment, _strip_exc(exc) + ), ) if pre != post: - ret['changes'][name] = {'old': pre, 'new': post} + ret["changes"][name] = {"old": pre, "new": post} if post != desired: return _fail( ret, - 'Failed to set {0}\'{1}\' to {2}'.format( - 'global key ' if global_ else '', - name, - value_comment - ) + "Failed to set {0}'{1}' to {2}".format( + "global key " if global_ else "", name, value_comment + ), ) - ret['comment'] = '{0}\'{1}\' was {2} {3}'.format( - 'Global key ' if global_ else '', + ret["comment"] = "{0}'{1}' was {2} {3}".format( + "Global key " if global_ else "", name, - 'added as' if pre is None else 'set to', - value_comment + "added as" if pre is None else "set to", + value_comment, ) return ret def mod_run_check(cmd_kwargs, onlyif, unless): - ''' + """ Execute the onlyif and unless logic. Return a result dict if: * onlyif failed (onlyif != 0) * unless succeeded (unless == 0) Otherwise, returns ``True`` - ''' + """ cmd_kwargs = copy.deepcopy(cmd_kwargs) - cmd_kwargs.update({ - 'use_vt': False, - 'bg': False, - 'ignore_retcode': True, - 'python_shell': True, - }) + cmd_kwargs.update( + {"use_vt": False, "bg": False, "ignore_retcode": True, "python_shell": True} + ) if onlyif is not None: if not isinstance(onlyif, list): @@ -3464,23 +3362,24 @@ def mod_run_check(cmd_kwargs, onlyif, unless): # Boolean or some other non-string which resolves to True continue try: - if __salt__['cmd.retcode'](command, **cmd_kwargs) == 0: + if __salt__["cmd.retcode"](command, **cmd_kwargs) == 0: # Command exited with a zero retcode continue except Exception as exc: # pylint: disable=broad-except log.exception( - 'The following onlyif command raised an error: %s', - command + "The following onlyif command raised an error: %s", command ) return { - 'comment': 'onlyif raised error ({0}), see log for ' - 'more details'.format(exc), - 'result': False + "comment": "onlyif raised error ({0}), see log for " + "more details".format(exc), + "result": False, } - return {'comment': 'onlyif condition is false', - 'skip_watch': True, - 'result': True} + return { + "comment": "onlyif condition is false", + "skip_watch": True, + "result": True, + } if unless is not None: if not isinstance(unless, list): @@ -3491,22 +3390,23 @@ def mod_run_check(cmd_kwargs, onlyif, unless): # Boolean or some other non-string which resolves to False break try: - if __salt__['cmd.retcode'](command, **cmd_kwargs) != 0: + if __salt__["cmd.retcode"](command, **cmd_kwargs) != 0: # Command exited with a non-zero retcode break except Exception as exc: # pylint: disable=broad-except log.exception( - 'The following unless command raised an error: %s', - command + "The following unless command raised an error: %s", command ) return { - 'comment': 'unless raised error ({0}), see log for ' - 'more details'.format(exc), - 'result': False + "comment": "unless raised error ({0}), see log for " + "more details".format(exc), + "result": False, } else: - return {'comment': 'unless condition is true', - 'skip_watch': True, - 'result': True} + return { + "comment": "unless condition is true", + "skip_watch": True, + "result": True, + } return True diff --git a/salt/states/github.py b/salt/states/github.py index 514accb14ab..9bb14b31837 100644 --- a/salt/states/github.py +++ b/salt/states/github.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Github User State Module .. versionadded:: 2016.3.0. @@ -13,32 +13,35 @@ This state is used to ensure presence of users in the Organization. - name: 'Example TestUser1' - email: example@domain.com - username: 'gitexample' -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import time +from __future__ import absolute_import, print_function, unicode_literals + import datetime import logging +import time + +from salt.exceptions import CommandExecutionError # Import Salt Libs from salt.ext import six -from salt.exceptions import CommandExecutionError from salt.ext.six.moves import range - log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the github module is available in __salt__ - ''' - return 'github' if 'github.list_users' in __salt__ else False + """ + if "github.list_users" in __salt__: + return "github" + return (False, "github module could not be loaded") def present(name, profile="github", **kwargs): - ''' + """ Ensure a user is present .. code-block:: yaml @@ -51,50 +54,47 @@ def present(name, profile="github", **kwargs): name This is the github handle of the user in the organization - ''' + """ - ret = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': '' - } + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - target = __salt__['github.get_user'](name, profile=profile, **kwargs) + target = __salt__["github.get_user"](name, profile=profile, **kwargs) # If the user has a valid github handle and is not in the org already if not target: - ret['result'] = False - ret['comment'] = 'Couldnt find user {0}'.format(name) + ret["result"] = False + ret["comment"] = "Couldnt find user {0}".format(name) elif isinstance(target, bool) and target: - ret['comment'] = 'User {0} is already in the org '.format(name) - ret['result'] = True - elif not target.get('in_org', False) and target.get('membership_state') != 'pending': - if __opts__['test']: - ret['comment'] = 'User {0} will be added to the org'.format(name) + ret["comment"] = "User {0} is already in the org ".format(name) + ret["result"] = True + elif ( + not target.get("in_org", False) and target.get("membership_state") != "pending" + ): + if __opts__["test"]: + ret["comment"] = "User {0} will be added to the org".format(name) return ret # add the user - result = __salt__['github.add_user']( - name, profile=profile, **kwargs - ) + result = __salt__["github.add_user"](name, profile=profile, **kwargs) if result: - ret['changes'].setdefault('old', None) - ret['changes'].setdefault('new', 'User {0} exists in the org now'.format(name)) - ret['result'] = True + ret["changes"].setdefault("old", None) + ret["changes"].setdefault( + "new", "User {0} exists in the org now".format(name) + ) + ret["result"] = True else: - ret['result'] = False - ret['comment'] = 'Failed to add user {0} to the org'.format(name) + ret["result"] = False + ret["comment"] = "Failed to add user {0} to the org".format(name) else: - ret['comment'] = 'User {0} has already been invited.'.format(name) - ret['result'] = True + ret["comment"] = "User {0} has already been invited.".format(name) + ret["result"] = True return ret def absent(name, profile="github", **kwargs): - ''' + """ Ensure a github user is absent .. code-block:: yaml @@ -110,59 +110,60 @@ def absent(name, profile="github", **kwargs): name Github handle of the user in organization - ''' - email = kwargs.get('email') - full_name = kwargs.get('fullname') + """ + email = kwargs.get("email") + full_name = kwargs.get("fullname") ret = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': 'User {0} is absent.'.format(name) + "name": name, + "changes": {}, + "result": None, + "comment": "User {0} is absent.".format(name), } - target = __salt__['github.get_user'](name, profile=profile, **kwargs) + target = __salt__["github.get_user"](name, profile=profile, **kwargs) if target: - if isinstance(target, bool) or target.get('in_org', False): - if __opts__['test']: - ret['comment'] = "User {0} will be deleted".format(name) - ret['result'] = None + if isinstance(target, bool) or target.get("in_org", False): + if __opts__["test"]: + ret["comment"] = "User {0} will be deleted".format(name) + ret["result"] = None return ret - result = __salt__['github.remove_user'](name, profile=profile, **kwargs) + result = __salt__["github.remove_user"](name, profile=profile, **kwargs) if result: - ret['comment'] = 'Deleted user {0}'.format(name) - ret['changes'].setdefault('old', 'User {0} exists'.format(name)) - ret['changes'].setdefault('new', 'User {0} deleted'.format(name)) - ret['result'] = True + ret["comment"] = "Deleted user {0}".format(name) + ret["changes"].setdefault("old", "User {0} exists".format(name)) + ret["changes"].setdefault("new", "User {0} deleted".format(name)) + ret["result"] = True else: - ret['comment'] = 'Failed to delete {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to delete {0}".format(name) + ret["result"] = False else: - ret['comment'] = "User {0} has already been deleted!".format(name) - ret['result'] = True + ret["comment"] = "User {0} has already been deleted!".format(name) + ret["result"] = True else: - ret['comment'] = 'User {0} does not exist'.format(name) - ret['result'] = True + ret["comment"] = "User {0} does not exist".format(name) + ret["result"] = True return ret return ret def team_present( - name, - description=None, - repo_names=None, - privacy='secret', - permission='pull', - members=None, - enforce_mfa=False, - no_mfa_grace_seconds=0, - profile="github", - **kwargs): - ''' + name, + description=None, + repo_names=None, + privacy="secret", + permission="pull", + members=None, + enforce_mfa=False, + no_mfa_grace_seconds=0, + profile="github", + **kwargs +): + """ Ensure a team is present name @@ -218,99 +219,113 @@ def team_present( - enforce_mfa: True .. versionadded:: 2016.11.0 - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - target = __salt__['github.get_team'](name, profile=profile, **kwargs) + target = __salt__["github.get_team"](name, profile=profile, **kwargs) test_comments = [] if target: # Team already exists parameters = {} - if description is not None and target['description'] != description: - parameters['description'] = description - if permission is not None and target['permission'] != permission: - parameters['permission'] = permission - if privacy is not None and target['privacy'] != privacy: - parameters['privacy'] = privacy + if description is not None and target["description"] != description: + parameters["description"] = description + if permission is not None and target["permission"] != permission: + parameters["permission"] = permission + if privacy is not None and target["privacy"] != privacy: + parameters["privacy"] = privacy if len(parameters) > 0: - if __opts__['test']: - test_comments.append('Team properties are set to be edited: {0}' - .format(parameters)) - ret['result'] = None + if __opts__["test"]: + test_comments.append( + "Team properties are set to be edited: {0}".format(parameters) + ) + ret["result"] = None else: - result = __salt__['github.edit_team'](name, profile=profile, - **parameters) + result = __salt__["github.edit_team"]( + name, profile=profile, **parameters + ) if result: - ret['changes']['team'] = { - 'old': 'Team properties were {0}'.format(target), - 'new': 'Team properties (that changed) are {0}'.format(parameters) + ret["changes"]["team"] = { + "old": "Team properties were {0}".format(target), + "new": "Team properties (that changed) are {0}".format( + parameters + ), } else: - ret['result'] = False - ret['comment'] = 'Failed to update team properties.' + ret["result"] = False + ret["comment"] = "Failed to update team properties." return ret manage_repos = repo_names is not None - current_repos = set(__salt__['github.list_team_repos'](name, profile=profile) - .keys()) + current_repos = set( + __salt__["github.list_team_repos"](name, profile=profile).keys() + ) repo_names = set(repo_names or []) repos_to_add = repo_names - current_repos repos_to_remove = current_repos - repo_names if repo_names else [] if repos_to_add: - if __opts__['test']: - test_comments.append('Team {0} will have the following repos ' - 'added: {1}.'.format(name, list(repos_to_add))) - ret['result'] = None + if __opts__["test"]: + test_comments.append( + "Team {0} will have the following repos " + "added: {1}.".format(name, list(repos_to_add)) + ) + ret["result"] = None else: for repo_name in repos_to_add: - result = (__salt__['github.add_team_repo'] - (repo_name, name, profile=profile, **kwargs)) + result = __salt__["github.add_team_repo"]( + repo_name, name, profile=profile, **kwargs + ) if result: - ret['changes'][repo_name] = { - 'old': 'Repo {0} is not in team {1}'.format(repo_name, name), - 'new': 'Repo {0} is in team {1}'.format(repo_name, name) + ret["changes"][repo_name] = { + "old": "Repo {0} is not in team {1}".format( + repo_name, name + ), + "new": "Repo {0} is in team {1}".format(repo_name, name), } else: - ret['result'] = False - ret['comment'] = ('Failed to add repo {0} to team {1}.' - .format(repo_name, name)) + ret["result"] = False + ret["comment"] = "Failed to add repo {0} to team {1}.".format( + repo_name, name + ) return ret if repos_to_remove: - if __opts__['test']: - test_comments.append('Team {0} will have the following repos ' - 'removed: {1}.'.format(name, list(repos_to_remove))) - ret['result'] = None + if __opts__["test"]: + test_comments.append( + "Team {0} will have the following repos " + "removed: {1}.".format(name, list(repos_to_remove)) + ) + ret["result"] = None else: for repo_name in repos_to_remove: - result = (__salt__['github.remove_team_repo'] - (repo_name, name, profile=profile, **kwargs)) + result = __salt__["github.remove_team_repo"]( + repo_name, name, profile=profile, **kwargs + ) if result: - ret['changes'][repo_name] = { - 'old': 'Repo {0} is in team {1}'.format(repo_name, name), - 'new': 'Repo {0} is not in team {1}'.format(repo_name, name) + ret["changes"][repo_name] = { + "old": "Repo {0} is in team {1}".format(repo_name, name), + "new": "Repo {0} is not in team {1}".format( + repo_name, name + ), } else: - ret['result'] = False - ret['comment'] = ('Failed to remove repo {0} from team {1}.' - .format(repo_name, name)) + ret["result"] = False + ret[ + "comment" + ] = "Failed to remove repo {0} from team {1}.".format( + repo_name, name + ) return ret else: # Team does not exist - it will be created. - if __opts__['test']: - ret['comment'] = 'Team {0} is set to be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Team {0} is set to be created.".format(name) + ret["result"] = None return ret - result = __salt__['github.add_team']( + result = __salt__["github.add_team"]( name, description=description, repo_names=repo_names, @@ -320,109 +335,132 @@ def team_present( **kwargs ) if result: - ret['changes']['team'] = {} - ret['changes']['team']['old'] = None - ret['changes']['team']['new'] = 'Team {0} has been created'.format(name) + ret["changes"]["team"] = {} + ret["changes"]["team"]["old"] = None + ret["changes"]["team"]["new"] = "Team {0} has been created".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to create team {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create team {0}.".format(name) return ret manage_members = members is not None - mfa_deadline = datetime.datetime.utcnow() - datetime.timedelta(seconds=no_mfa_grace_seconds) - members_no_mfa = __salt__['github.list_members_without_mfa'](profile=profile) + mfa_deadline = datetime.datetime.utcnow() - datetime.timedelta( + seconds=no_mfa_grace_seconds + ) + members_no_mfa = __salt__["github.list_members_without_mfa"](profile=profile) members_lower = {} for member_name, info in six.iteritems(members or {}): members_lower[member_name.lower()] = info member_change = False - current_members = __salt__['github.list_team_members'](name, profile=profile) + current_members = __salt__["github.list_team_members"](name, profile=profile) for member, member_info in six.iteritems(members or {}): - log.info('Checking member %s in team %s', member, name) + log.info("Checking member %s in team %s", member, name) if member.lower() not in current_members: - if (enforce_mfa and _member_violates_mfa(member, member_info, - mfa_deadline, members_no_mfa)): - if __opts__['test']: - test_comments.append('User {0} will not be added to the ' - 'team because they do not have MFA.' - ''.format(member)) + if enforce_mfa and _member_violates_mfa( + member, member_info, mfa_deadline, members_no_mfa + ): + if __opts__["test"]: + test_comments.append( + "User {0} will not be added to the " + "team because they do not have MFA." + "".format(member) + ) else: # Add to team member_change = True - if __opts__['test']: - test_comments.append('User {0} set to be added to the ' - 'team.'.format(member)) - ret['result'] = None + if __opts__["test"]: + test_comments.append( + "User {0} set to be added to the " "team.".format(member) + ) + ret["result"] = None else: - result = (__salt__['github.add_team_member'] - (member, name, profile=profile, **kwargs)) + result = __salt__["github.add_team_member"]( + member, name, profile=profile, **kwargs + ) if result: - ret['changes'][member] = {} - ret['changes'][member]['old'] = ( - 'User {0} is not in team {1}'.format(member, name)) - ret['changes'][member]['new'] = ( - 'User {0} is in team {1}'.format(member, name)) + ret["changes"][member] = {} + ret["changes"][member][ + "old" + ] = "User {0} is not in team {1}".format(member, name) + ret["changes"][member][ + "new" + ] = "User {0} is in team {1}".format(member, name) else: - ret['result'] = False - ret['comment'] = ('Failed to add user {0} to team ' - '{1}.'.format(member, name)) + ret["result"] = False + ret["comment"] = ( + "Failed to add user {0} to team " + "{1}.".format(member, name) + ) return ret for member in current_members: mfa_violation = False if member in members_lower: - mfa_violation = _member_violates_mfa(member, members_lower[member], - mfa_deadline, members_no_mfa) - if (manage_members and member not in members_lower or - (enforce_mfa and mfa_violation)): + mfa_violation = _member_violates_mfa( + member, members_lower[member], mfa_deadline, members_no_mfa + ) + if ( + manage_members + and member not in members_lower + or (enforce_mfa and mfa_violation) + ): # Remove from team member_change = True - if __opts__['test']: + if __opts__["test"]: if mfa_violation: - test_comments.append('User {0} set to be removed from the ' - 'team because they do not have MFA.' - .format(member)) + test_comments.append( + "User {0} set to be removed from the " + "team because they do not have MFA.".format(member) + ) else: - test_comments.append('User {0} set to be removed from ' - 'the team.'.format(member)) - ret['result'] = None + test_comments.append( + "User {0} set to be removed from " "the team.".format(member) + ) + ret["result"] = None else: - result = (__salt__['github.remove_team_member'] - (member, name, profile=profile, **kwargs)) + result = __salt__["github.remove_team_member"]( + member, name, profile=profile, **kwargs + ) if result: - extra_changes = ' due to MFA violation' if mfa_violation else '' - ret['changes'][member] = { - 'old': 'User {0} is in team {1}'.format(member, name), - 'new': 'User {0} is not in team {1}{2}'.format(member, name, extra_changes) + extra_changes = " due to MFA violation" if mfa_violation else "" + ret["changes"][member] = { + "old": "User {0} is in team {1}".format(member, name), + "new": "User {0} is not in team {1}{2}".format( + member, name, extra_changes + ), } else: - ret['result'] = False - ret['comment'] = ('Failed to remove user {0} from team {1}.' - .format(member, name)) + ret["result"] = False + ret["comment"] = "Failed to remove user {0} from team {1}.".format( + member, name + ) return ret if member_change: # Refresh team cache - __salt__['github.list_team_members'](name, profile=profile, - ignore_cache=False, **kwargs) + __salt__["github.list_team_members"]( + name, profile=profile, ignore_cache=False, **kwargs + ) if len(test_comments) > 0: - ret['comment'] = '\n'.join(test_comments) + ret["comment"] = "\n".join(test_comments) return ret def _member_violates_mfa(member, member_info, mfa_deadline, members_without_mfa): - if member_info.get('mfa_exempt', False): + if member_info.get("mfa_exempt", False): return False enforce_mfa_from = datetime.datetime.strptime( - member_info.get('enforce_mfa_from', '1970/01/01'), '%Y/%m/%d') + member_info.get("enforce_mfa_from", "1970/01/01"), "%Y/%m/%d" + ) return member.lower() in members_without_mfa and (mfa_deadline > enforce_mfa_from) def team_absent(name, profile="github", **kwargs): - ''' + """ Ensure a team is absent. Example: @@ -440,54 +478,50 @@ def team_absent(name, profile="github", **kwargs): This is the name of the team in the organization. .. versionadded:: 2016.11.0 - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - target = __salt__['github.get_team'](name, profile=profile, **kwargs) + target = __salt__["github.get_team"](name, profile=profile, **kwargs) if not target: - ret['comment'] = 'Team {0} does not exist'.format(name) - ret['result'] = True + ret["comment"] = "Team {0} does not exist".format(name) + ret["result"] = True return ret else: - if __opts__['test']: - ret['comment'] = "Team {0} will be deleted".format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Team {0} will be deleted".format(name) + ret["result"] = None return ret - result = __salt__['github.remove_team'](name, profile=profile, **kwargs) + result = __salt__["github.remove_team"](name, profile=profile, **kwargs) if result: - ret['comment'] = 'Deleted team {0}'.format(name) - ret['changes'].setdefault('old', 'Team {0} exists'.format(name)) - ret['changes'].setdefault('new', 'Team {0} deleted'.format(name)) - ret['result'] = True + ret["comment"] = "Deleted team {0}".format(name) + ret["changes"].setdefault("old", "Team {0} exists".format(name)) + ret["changes"].setdefault("new", "Team {0} deleted".format(name)) + ret["result"] = True else: - ret['comment'] = 'Failed to delete {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to delete {0}".format(name) + ret["result"] = False return ret def repo_present( - name, - description=None, - homepage=None, - private=None, - has_issues=None, - has_wiki=None, - has_downloads=None, - auto_init=False, - gitignore_template=None, - license_template=None, - teams=None, - profile="github", - **kwargs): - ''' + name, + description=None, + homepage=None, + private=None, + has_issues=None, + has_wiki=None, + has_downloads=None, + auto_init=False, + gitignore_template=None, + license_template=None, + teams=None, + profile="github", + **kwargs +): + """ Ensure a repository is present name @@ -537,34 +571,29 @@ def repo_present( - description: 'My very important repository' .. versionadded:: 2016.11.0 - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # This is an optimization to cache all repos in the organization up front. # The first use of this state will collect all of the repos and save a bunch # of API calls for future use. - __salt__['github.list_repos'](profile=profile) + __salt__["github.list_repos"](profile=profile) try: - target = __salt__['github.get_repo_info'](name, profile=profile, **kwargs) + target = __salt__["github.get_repo_info"](name, profile=profile, **kwargs) except CommandExecutionError: target = None given_params = { - 'description': description, - 'homepage': homepage, - 'private': private, - 'has_issues': has_issues, - 'has_wiki': has_wiki, - 'has_downloads': has_downloads, - 'auto_init': auto_init, - 'gitignore_template': gitignore_template, - 'license_template': license_template + "description": description, + "homepage": homepage, + "private": private, + "has_issues": has_issues, + "has_wiki": has_wiki, + "has_downloads": has_downloads, + "auto_init": auto_init, + "gitignore_template": gitignore_template, + "license_template": license_template, } # Keep track of current_teams if we've fetched them after creating a new repo @@ -572,53 +601,51 @@ def repo_present( if target: # Repo already exists # Some params are only valid on repo creation - ignore_params = ['auto_init', 'gitignore_template', 'license_template'] + ignore_params = ["auto_init", "gitignore_template", "license_template"] parameters = {} old_parameters = {} for param_name, param_value in six.iteritems(given_params): - if (param_value is not None and param_name not in ignore_params and - target[param_name] is not param_value and - target[param_name] != param_value): + if ( + param_value is not None + and param_name not in ignore_params + and target[param_name] is not param_value + and target[param_name] != param_value + ): parameters[param_name] = param_value old_parameters[param_name] = target[param_name] if len(parameters) > 0: repo_change = { - 'old': 'Repo properties were {0}'.format(old_parameters), - 'new': 'Repo properties (that changed) are {0}'.format(parameters) + "old": "Repo properties were {0}".format(old_parameters), + "new": "Repo properties (that changed) are {0}".format(parameters), } - if __opts__['test']: - ret['changes']['repo'] = repo_change - ret['result'] = None + if __opts__["test"]: + ret["changes"]["repo"] = repo_change + ret["result"] = None else: - result = __salt__['github.edit_repo'](name, profile=profile, - **parameters) + result = __salt__["github.edit_repo"]( + name, profile=profile, **parameters + ) if result: - ret['changes']['repo'] = repo_change + ret["changes"]["repo"] = repo_change else: - ret['result'] = False - ret['comment'] = 'Failed to update repo properties.' + ret["result"] = False + ret["comment"] = "Failed to update repo properties." return ret else: # Repo does not exist - it will be created. - repo_change = { - 'old': None, - 'new': 'Repo {0} has been created'.format(name) - } - if __opts__['test']: - ret['changes']['repo'] = repo_change - ret['result'] = None + repo_change = {"old": None, "new": "Repo {0} has been created".format(name)} + if __opts__["test"]: + ret["changes"]["repo"] = repo_change + ret["result"] = None else: add_params = dict(given_params) add_params.update(kwargs) - result = __salt__['github.add_repo']( - name, - **add_params - ) + result = __salt__["github.add_repo"](name, **add_params) if not result: - ret['result'] = False - ret['comment'] = 'Failed to create repo {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create repo {0}.".format(name) return ret # Turns out that trying to fetch teams for a new repo can 404 immediately @@ -627,109 +654,127 @@ def repo_present( for attempt in range(3): time.sleep(1) try: - current_teams = __salt__['github.get_repo_teams']( - name, profile=profile, **kwargs) + current_teams = __salt__["github.get_repo_teams"]( + name, profile=profile, **kwargs + ) break except CommandExecutionError as e: - log.info("Attempt %s to fetch new repo %s failed", - attempt, - name) + log.info("Attempt %s to fetch new repo %s failed", attempt, name) if current_teams is None: - ret['result'] = False - ret['comment'] = 'Failed to verify repo {0} after creation.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to verify repo {0} after creation.".format( + name + ) return ret - ret['changes']['repo'] = repo_change + ret["changes"]["repo"] = repo_change if teams is not None: - if __opts__['test'] and not target: + if __opts__["test"] and not target: # Assume no teams if we're in test mode and the repo doesn't exist current_teams = [] elif current_teams is None: - current_teams = __salt__['github.get_repo_teams'](name, profile=profile) - current_team_names = set([t['name'] for t in current_teams]) + current_teams = __salt__["github.get_repo_teams"](name, profile=profile) + current_team_names = set([t["name"] for t in current_teams]) # First remove any teams that aren't present for team_name in current_team_names: if team_name not in teams: team_change = { - 'old': 'Repo {0} is in team {1}'.format(name, team_name), - 'new': 'Repo {0} is not in team {1}'.format(name, team_name) + "old": "Repo {0} is in team {1}".format(name, team_name), + "new": "Repo {0} is not in team {1}".format(name, team_name), } - if __opts__['test']: - ret['changes'][team_name] = team_change - ret['result'] = None + if __opts__["test"]: + ret["changes"][team_name] = team_change + ret["result"] = None else: - result = __salt__['github.remove_team_repo'](name, team_name, - profile=profile) + result = __salt__["github.remove_team_repo"]( + name, team_name, profile=profile + ) if result: - ret['changes'][team_name] = team_change + ret["changes"][team_name] = team_change else: - ret['result'] = False - ret['comment'] = ('Failed to remove repo {0} from team {1}.' - .format(name, team_name)) + ret["result"] = False + ret[ + "comment" + ] = "Failed to remove repo {0} from team {1}.".format( + name, team_name + ) return ret # Next add or modify any necessary teams for team_name, permission in six.iteritems(teams): if team_name not in current_team_names: # Need to add repo to team team_change = { - 'old': 'Repo {0} is not in team {1}'.format(name, team_name), - 'new': 'Repo {0} is in team {1}'.format(name, team_name) + "old": "Repo {0} is not in team {1}".format(name, team_name), + "new": "Repo {0} is in team {1}".format(name, team_name), } - if __opts__['test']: - ret['changes'][team_name] = team_change - ret['result'] = None + if __opts__["test"]: + ret["changes"][team_name] = team_change + ret["result"] = None else: - result = __salt__['github.add_team_repo'](name, team_name, - profile=profile, - permission=permission) + result = __salt__["github.add_team_repo"]( + name, team_name, profile=profile, permission=permission + ) if result: - ret['changes'][team_name] = team_change + ret["changes"][team_name] = team_change else: - ret['result'] = False - ret['comment'] = ('Failed to remove repo {0} from team {1}.' - .format(name, team_name)) + ret["result"] = False + ret[ + "comment" + ] = "Failed to remove repo {0} from team {1}.".format( + name, team_name + ) return ret else: - current_permission = (__salt__['github.list_team_repos'] - (team_name, profile=profile) - .get(name.lower(), {}) - .get('permission')) + current_permission = ( + __salt__["github.list_team_repos"](team_name, profile=profile) + .get(name.lower(), {}) + .get("permission") + ) if not current_permission: - ret['result'] = False - ret['comment'] = ('Failed to determine current permission for team ' - '{0} in repo {1}'.format(team_name, name)) + ret["result"] = False + ret["comment"] = ( + "Failed to determine current permission for team " + "{0} in repo {1}".format(team_name, name) + ) return ret elif current_permission != permission: team_change = { - 'old': ('Repo {0} in team {1} has permission {2}' - .format(name, team_name, current_permission)), - 'new': ('Repo {0} in team {1} has permission {2}' - .format(name, team_name, permission)) + "old": ( + "Repo {0} in team {1} has permission {2}".format( + name, team_name, current_permission + ) + ), + "new": ( + "Repo {0} in team {1} has permission {2}".format( + name, team_name, permission + ) + ), } - if __opts__['test']: - ret['changes'][team_name] = team_change - ret['result'] = None + if __opts__["test"]: + ret["changes"][team_name] = team_change + ret["result"] = None else: - result = __salt__['github.add_team_repo'](name, team_name, - profile=profile, - permission=permission) + result = __salt__["github.add_team_repo"]( + name, team_name, profile=profile, permission=permission + ) if result: - ret['changes'][team_name] = team_change + ret["changes"][team_name] = team_change else: - ret['result'] = False - ret['comment'] = ('Failed to set permission on repo {0} from ' - 'team {1} to {2}.' - .format(name, team_name, permission)) + ret["result"] = False + ret["comment"] = ( + "Failed to set permission on repo {0} from " + "team {1} to {2}.".format(name, team_name, permission) + ) return ret return ret def repo_absent(name, profile="github", **kwargs): - ''' + """ Ensure a repo is absent. Example: @@ -746,38 +791,35 @@ def repo_absent(name, profile="github", **kwargs): This is the name of the repository in the organization. .. versionadded:: 2016.11.0 - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} try: - target = __salt__['github.get_repo_info'](name, profile=profile, **kwargs) + target = __salt__["github.get_repo_info"](name, profile=profile, **kwargs) except CommandExecutionError: target = None if not target: - ret['comment'] = 'Repo {0} does not exist'.format(name) - ret['result'] = True + ret["comment"] = "Repo {0} does not exist".format(name) + ret["result"] = True return ret else: - if __opts__['test']: - ret['comment'] = "Repo {0} will be deleted".format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Repo {0} will be deleted".format(name) + ret["result"] = None return ret - result = __salt__['github.remove_repo'](name, profile=profile, **kwargs) + result = __salt__["github.remove_repo"](name, profile=profile, **kwargs) if result: - ret['comment'] = 'Deleted repo {0}'.format(name) - ret['changes'].setdefault('old', 'Repo {0} exists'.format(name)) - ret['changes'].setdefault('new', 'Repo {0} deleted'.format(name)) - ret['result'] = True + ret["comment"] = "Deleted repo {0}".format(name) + ret["changes"].setdefault("old", "Repo {0} exists".format(name)) + ret["changes"].setdefault("new", "Repo {0} deleted".format(name)) + ret["result"] = True else: - ret['comment'] = ('Failed to delete repo {0}. Ensure the delete_repo ' - 'scope is enabled if using OAuth.'.format(name)) - ret['result'] = False + ret["comment"] = ( + "Failed to delete repo {0}. Ensure the delete_repo " + "scope is enabled if using OAuth.".format(name) + ) + ret["result"] = False return ret diff --git a/salt/states/glance.py b/salt/states/glance.py index 8c97b86036e..c8b0ff4754c 100644 --- a/salt/states/glance.py +++ b/salt/states/glance.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Managing Images in OpenStack Glance =================================== -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time @@ -12,20 +13,22 @@ import time # Import OpenStack libs try: - from keystoneclient.exceptions import \ - Unauthorized as kstone_Unauthorized + from keystoneclient.exceptions import Unauthorized as kstone_Unauthorized + HAS_KEYSTONE = True except ImportError: try: - from keystoneclient.apiclient.exceptions import \ - Unauthorized as kstone_Unauthorized + from keystoneclient.apiclient.exceptions import ( + Unauthorized as kstone_Unauthorized, + ) + HAS_KEYSTONE = True except ImportError: HAS_KEYSTONE = False try: - from glanceclient.exc import \ - HTTPUnauthorized as glance_Unauthorized + from glanceclient.exc import HTTPUnauthorized as glance_Unauthorized + HAS_GLANCE = True except ImportError: HAS_GLANCE = False @@ -34,46 +37,55 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if dependencies are loaded - ''' - return HAS_KEYSTONE and HAS_GLANCE + """ + if HAS_KEYSTONE and HAS_GLANCE: + return True + return (False, "Unable to import keystoneclient or glanceclient") def _find_image(name): - ''' + """ Tries to find image with given name, returns - image, 'Found image <name>' - None, 'No such image found' - False, 'Found more than one image with given name' - ''' + """ try: - images = __salt__['glance.image_list'](name=name) + images = __salt__["glance.image_list"](name=name) except kstone_Unauthorized: - return False, 'keystoneclient: Unauthorized' + return False, "keystoneclient: Unauthorized" except glance_Unauthorized: - return False, 'glanceclient: Unauthorized' - log.debug('Got images: {0}'.format(images)) + return False, "glanceclient: Unauthorized" + log.debug("Got images: {0}".format(images)) - if type(images) is dict and len(images) == 1 and 'images' in images: - images = images['images'] + if type(images) is dict and len(images) == 1 and "images" in images: + images = images["images"] images_list = images.values() if type(images) is dict else images if len(images_list) == 0: return None, 'No image with name "{0}"'.format(name) elif len(images_list) == 1: - return images_list[0], 'Found image {0}'.format(name) + return images_list[0], "Found image {0}".format(name) elif len(images_list) > 1: - return False, 'Found more than one image with given name' + return False, "Found more than one image with given name" else: raise NotImplementedError -def image_present(name, visibility='public', protected=None, - checksum=None, location=None, disk_format='raw', wait_for=None, - timeout=30): - ''' +def image_present( + name, + visibility="public", + protected=None, + checksum=None, + location=None, + disk_format="raw", + wait_for=None, + timeout=30, +): + """ Checks if given image is present with properties set as specified. @@ -93,24 +105,25 @@ def image_present(name, visibility='public', protected=None, - location (URL, to copy from) - disk_format ('raw' (default), 'vhd', 'vhdx', 'vmdk', 'vdi', 'iso', 'qcow2', 'aki', 'ari' or 'ami') - ''' - __utils__['versions.warn_until']( - 'Aluminium', + """ + __utils__["versions.warn_until"]( + "Aluminium", ( - 'The glance state module has been deprecated and will be removed in {version}. ' - 'Please update to using the glance_image state module' + "The glance state module has been deprecated and will be removed in {version}. " + "Please update to using the glance_image state module" ), ) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': '', - } - acceptable = ['queued', 'saving', 'active'] + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "", + } + acceptable = ["queued", "saving", "active"] if wait_for is None and checksum is None: - wait_for = 'saving' + wait_for = "saving" elif wait_for is None and checksum is not None: - wait_for = 'active' + wait_for = "active" # Just pop states until we reach the # first acceptable one: @@ -122,136 +135,146 @@ def image_present(name, visibility='public', protected=None, image, msg = _find_image(name) if image is False: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None else: - ret['result'] = False - ret['comment'] = msg + ret["result"] = False + ret["comment"] = msg return ret log.debug(msg) # No image yet and we know where to get one if image is None and location is not None: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'glance.image_present would ' \ - 'create an image from {0}'.format(location) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "glance.image_present would " + "create an image from {0}".format(location) + ) return ret - image = __salt__['glance.image_create'](name=name, - protected=protected, visibility=visibility, - location=location, disk_format=disk_format) - log.debug('Created new image:\n{0}'.format(image)) - ret['changes'] = { - name: - { - 'new': - { - 'id': image['id'] - }, - 'old': None - } - } + image = __salt__["glance.image_create"]( + name=name, + protected=protected, + visibility=visibility, + location=location, + disk_format=disk_format, + ) + log.debug("Created new image:\n{0}".format(image)) + ret["changes"] = {name: {"new": {"id": image["id"]}, "old": None}} timer = timeout # Kinda busy-loopy but I don't think the Glance # API has events we can listen for while timer > 0: - if 'status' in image and \ - image['status'] in acceptable: - log.debug('Image {0} has reached status {1}'.format( - image['name'], image['status'])) + if "status" in image and image["status"] in acceptable: + log.debug( + "Image {0} has reached status {1}".format( + image["name"], image["status"] + ) + ) break else: timer -= 5 time.sleep(5) image, msg = _find_image(name) if not image: - ret['result'] = False - ret['comment'] += 'Created image {0} '.format( - name) + ' vanished:\n' + msg + ret["result"] = False + ret["comment"] += ( + "Created image {0} ".format(name) + " vanished:\n" + msg + ) return ret - if timer <= 0 and image['status'] not in acceptable: - ret['result'] = False - ret['comment'] += 'Image didn\'t reach an acceptable '+\ - 'state ({0}) before timeout:\n'.format(acceptable)+\ - '\tLast status was "{0}".\n'.format(image['status']) + if timer <= 0 and image["status"] not in acceptable: + ret["result"] = False + ret["comment"] += ( + "Image didn't reach an acceptable " + + "state ({0}) before timeout:\n".format(acceptable) + + '\tLast status was "{0}".\n'.format(image["status"]) + ) # There's no image but where would I get one?? elif location is None: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'No location to copy image from specified,\n' +\ - 'glance.image_present would not create one' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "No location to copy image from specified,\n" + + "glance.image_present would not create one" + ) else: - ret['result'] = False - ret['comment'] = 'No location to copy image from specified,\n' +\ - 'not creating a new image.' + ret["result"] = False + ret["comment"] = ( + "No location to copy image from specified,\n" + + "not creating a new image." + ) return ret # If we've created a new image also return its last status: - if name in ret['changes']: - ret['changes'][name]['new']['status'] = image['status'] + if name in ret["changes"]: + ret["changes"][name]["new"]["status"] = image["status"] if visibility: - if image['visibility'] != visibility: - old_value = image['visibility'] - if not __opts__['test']: - image = __salt__['glance.image_update']( - id=image['id'], visibility=visibility) + if image["visibility"] != visibility: + old_value = image["visibility"] + if not __opts__["test"]: + image = __salt__["glance.image_update"]( + id=image["id"], visibility=visibility + ) # Check if image_update() worked: - if image['visibility'] != visibility: - if not __opts__['test']: - ret['result'] = False - elif __opts__['test']: - ret['result'] = None - ret['comment'] += '"visibility" is {0}, '\ - 'should be {1}.\n'.format(image['visibility'], - visibility) + if image["visibility"] != visibility: + if not __opts__["test"]: + ret["result"] = False + elif __opts__["test"]: + ret["result"] = None + ret["comment"] += '"visibility" is {0}, ' "should be {1}.\n".format( + image["visibility"], visibility + ) else: - if 'new' in ret['changes']: - ret['changes']['new']['visibility'] = visibility + if "new" in ret["changes"]: + ret["changes"]["new"]["visibility"] = visibility else: - ret['changes']['new'] = {'visibility': visibility} - if 'old' in ret['changes']: - ret['changes']['old']['visibility'] = old_value + ret["changes"]["new"] = {"visibility": visibility} + if "old" in ret["changes"]: + ret["changes"]["old"]["visibility"] = old_value else: - ret['changes']['old'] = {'visibility': old_value} + ret["changes"]["old"] = {"visibility": old_value} else: - ret['comment'] += '"visibility" is correct ({0}).\n'.format( - visibility) + ret["comment"] += '"visibility" is correct ({0}).\n'.format(visibility) if protected is not None: - if not isinstance(protected, bool) or image['protected'] ^ protected: - if not __opts__['test']: - ret['result'] = False + if not isinstance(protected, bool) or image["protected"] ^ protected: + if not __opts__["test"]: + ret["result"] = False else: - ret['result'] = None - ret['comment'] += '"protected" is {0}, should be {1}.\n'.format( - image['protected'], protected) + ret["result"] = None + ret["comment"] += '"protected" is {0}, should be {1}.\n'.format( + image["protected"], protected + ) else: - ret['comment'] += '"protected" is correct ({0}).\n'.format( - protected) - if 'status' in image and checksum: - if image['status'] == 'active': - if 'checksum' not in image: + ret["comment"] += '"protected" is correct ({0}).\n'.format(protected) + if "status" in image and checksum: + if image["status"] == "active": + if "checksum" not in image: # Refresh our info about the image - image = __salt__['glance.image_show'](image['id']) - if 'checksum' not in image: - if not __opts__['test']: - ret['result'] = False + image = __salt__["glance.image_show"](image["id"]) + if "checksum" not in image: + if not __opts__["test"]: + ret["result"] = False else: - ret['result'] = None - ret['comment'] += 'No checksum available for this image:\n' +\ - '\tImage has status "{0}".'.format(image['status']) - elif image['checksum'] != checksum: - if not __opts__['test']: - ret['result'] = False + ret["result"] = None + ret["comment"] += ( + "No checksum available for this image:\n" + + '\tImage has status "{0}".'.format(image["status"]) + ) + elif image["checksum"] != checksum: + if not __opts__["test"]: + ret["result"] = False else: - ret['result'] = None - ret['comment'] += '"checksum" is {0}, should be {1}.\n'.format( - image['checksum'], checksum) + ret["result"] = None + ret["comment"] += '"checksum" is {0}, should be {1}.\n'.format( + image["checksum"], checksum + ) else: - ret['comment'] += '"checksum" is correct ({0}).\n'.format( - checksum) - elif image['status'] in ['saving', 'queued']: - ret['comment'] += 'Checksum won\'t be verified as image ' +\ - 'hasn\'t reached\n\t "status=active" yet.\n' - log.debug('glance.image_present will return: {0}'.format(ret)) + ret["comment"] += '"checksum" is correct ({0}).\n'.format(checksum) + elif image["status"] in ["saving", "queued"]: + ret["comment"] += ( + "Checksum won't be verified as image " + + 'hasn\'t reached\n\t "status=active" yet.\n' + ) + log.debug("glance.image_present will return: {0}".format(ret)) return ret diff --git a/salt/states/glance_image.py b/salt/states/glance_image.py index d9d9e971c3c..6b501559a4e 100644 --- a/salt/states/glance_image.py +++ b/salt/states/glance_image.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Glance Images ======================================== @@ -21,21 +21,24 @@ Example States delete image: glance_image.absent: - name: cirros -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'glance_image' +__virtualname__ = "glance_image" def __virtual__(): - if 'glanceng.image_get' in __salt__: + if "glanceng.image_get" in __salt__: return __virtualname__ - return (False, 'The glanceng execution module failed to load: shade python module is not available') + return ( + False, + "The glanceng execution module failed to load: shade python module is not available", + ) def present(name, auth=None, **kwargs): - ''' + """ Ensure image exists and is up-to-date name @@ -46,29 +49,26 @@ def present(name, auth=None, **kwargs): description An arbitrary description of the image - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['glanceng.setup_clouds'](auth) + __salt__["glanceng.setup_clouds"](auth) - image = __salt__['glanceng.image_get'](name=name) + image = __salt__["glanceng.image_get"](name=name) if not image: - if __opts__['test']: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Image {} will be created.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Image {} will be created.".format(name) return ret - kwargs['name'] = name - image = __salt__['glanceng.image_create'](**kwargs) - ret['changes'] = image - ret['comment'] = 'Created image' + kwargs["name"] = name + image = __salt__["glanceng.image_create"](**kwargs) + ret["changes"] = image + ret["comment"] = "Created image" return ret # TODO(SamYaple): Compare and update image properties here @@ -76,30 +76,27 @@ def present(name, auth=None, **kwargs): def absent(name, auth=None): - ''' + """ Ensure image does not exist name Name of the image - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - __salt__['glanceng.setup_clouds'](auth) + __salt__["glanceng.setup_clouds"](auth) - image = __salt__['glanceng.image_get'](name=name) + image = __salt__["glanceng.image_get"](name=name) if image: - if __opts__['test']: - ret['result'] = None - ret['changes'] = {'name': name} - ret['comment'] = 'Image {} will be deleted.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {"name": name} + ret["comment"] = "Image {} will be deleted.".format(name) return ret - __salt__['glanceng.image_delete'](name=image) - ret['changes']['id'] = image.id - ret['comment'] = 'Deleted image' + __salt__["glanceng.image_delete"](name=image) + ret["changes"]["id"] = image.id + ret["comment"] = "Deleted image" return ret diff --git a/salt/states/glassfish.py b/salt/states/glassfish.py index 12edf698f30..e1228ae90c9 100644 --- a/salt/states/glassfish.py +++ b/salt/states/glassfish.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Glassfish/Payara server .. versionadded:: Carbon @@ -7,13 +7,14 @@ Management of glassfish using its RESTful API You can setup connection parameters like this .. code-block:: yaml + - server: - ssl: true - url: localhost - port: 4848 - user: admin - password: changeit -''' +""" from __future__ import absolute_import, print_function, unicode_literals try: @@ -21,22 +22,25 @@ try: from salt.ext import six from salt.exceptions import CommandExecutionError import requests + HAS_LIBS = True except ImportError: HAS_LIBS = False def __virtual__(): - ''' + """ Only load if glassfish module is available - ''' - return 'glassfish.enum_connector_c_pool' in __salt__ and HAS_LIBS + """ + if "glassfish.enum_connector_c_pool" in __salt__ and HAS_LIBS: + return True + return (False, "glassfish module could not be loaded") def _json_to_unicode(data): - ''' + """ Encode json values in unicode to match that of the API - ''' + """ ret = {} for key, value in data.items(): if not isinstance(value, six.text_type): @@ -50,105 +54,118 @@ def _json_to_unicode(data): def _is_updated(old_conf, new_conf): - ''' + """ Compare the API results to the current statefile data - ''' + """ changed = {} # Dirty json hacking to get parameters in the same format - new_conf = _json_to_unicode(salt.utils.json.loads( - salt.utils.json.dumps(new_conf, ensure_ascii=False))) - old_conf = salt.utils.json.loads(salt.utils.json.dumps(old_conf, ensure_ascii=False)) + new_conf = _json_to_unicode( + salt.utils.json.loads(salt.utils.json.dumps(new_conf, ensure_ascii=False)) + ) + old_conf = salt.utils.json.loads( + salt.utils.json.dumps(old_conf, ensure_ascii=False) + ) for key, value in old_conf.items(): oldval = six.text_type(value).lower() if key in new_conf: newval = six.text_type(new_conf[key]).lower() - if oldval == 'null' or oldval == 'none': - oldval = '' + if oldval == "null" or oldval == "none": + oldval = "" if key in new_conf and newval != oldval: - changed[key] = {'old': oldval, 'new': newval} + changed[key] = {"old": oldval, "new": newval} return changed def _do_element_present(name, elem_type, data, server=None): - ''' + """ Generic function to create or update an element - ''' - ret = {'changes': {}, 'update': False, 'create': False, 'error': None} + """ + ret = {"changes": {}, "update": False, "create": False, "error": None} try: - elements = __salt__['glassfish.enum_{0}'.format(elem_type)]() + elements = __salt__["glassfish.enum_{0}".format(elem_type)]() except requests.ConnectionError as error: - if __opts__['test']: - ret['changes'] = {'Name': name, 'Params': data} - ret['create'] = True + if __opts__["test"]: + ret["changes"] = {"Name": name, "Params": data} + ret["create"] = True return ret else: - ret['error'] = "Can't connect to the server" + ret["error"] = "Can't connect to the server" return ret if not elements or name not in elements: - ret['changes'] = {'Name': name, 'Params': data} - ret['create'] = True - if not __opts__['test']: + ret["changes"] = {"Name": name, "Params": data} + ret["create"] = True + if not __opts__["test"]: try: - __salt__['glassfish.create_{0}'.format(elem_type)](name, server=server, **data) + __salt__["glassfish.create_{0}".format(elem_type)]( + name, server=server, **data + ) except CommandExecutionError as error: - ret['error'] = error + ret["error"] = error return ret elif elements and any(data): - current_data = __salt__['glassfish.get_{0}'.format(elem_type)](name, server=server) + current_data = __salt__["glassfish.get_{0}".format(elem_type)]( + name, server=server + ) data_diff = _is_updated(current_data, data) if data_diff: - ret['update'] = True - ret['changes'] = data_diff - if not __opts__['test']: + ret["update"] = True + ret["changes"] = data_diff + if not __opts__["test"]: try: - __salt__['glassfish.update_{0}'.format(elem_type)](name, server=server, **data) + __salt__["glassfish.update_{0}".format(elem_type)]( + name, server=server, **data + ) except CommandExecutionError as error: - ret['error'] = error + ret["error"] = error return ret def _do_element_absent(name, elem_type, data, server=None): - ''' + """ Generic function to delete an element - ''' - ret = {'delete': False, 'error': None} + """ + ret = {"delete": False, "error": None} try: - elements = __salt__['glassfish.enum_{0}'.format(elem_type)]() + elements = __salt__["glassfish.enum_{0}".format(elem_type)]() except requests.ConnectionError as error: - if __opts__['test']: - ret['create'] = True + if __opts__["test"]: + ret["create"] = True return ret else: - ret['error'] = "Can't connect to the server" + ret["error"] = "Can't connect to the server" return ret if elements and name in elements: - ret['delete'] = True - if not __opts__['test']: + ret["delete"] = True + if not __opts__["test"]: try: - __salt__['glassfish.delete_{0}'.format(elem_type)](name, server=server, **data) + __salt__["glassfish.delete_{0}".format(elem_type)]( + name, server=server, **data + ) except CommandExecutionError as error: - ret['error'] = error + ret["error"] = error return ret -def connection_factory_present(name, - restype='connection_factory', - description='', - enabled=True, - min_size=1, - max_size=250, - resize_quantity=2, - idle_timeout=300, - wait_timeout=60, - reconnect_on_failure=False, - transaction_support='', - connection_validation=False, - server=None): - ''' +def connection_factory_present( + name, + restype="connection_factory", + description="", + enabled=True, + min_size=1, + max_size=250, + resize_quantity=2, + idle_timeout=300, + wait_timeout=60, + reconnect_on_failure=False, + transaction_support="", + connection_validation=False, + server=None, +): + """ Ensures that the Connection Factory is present name @@ -187,80 +204,80 @@ def connection_factory_present(name, connection_validation Connection validation is required, defaults to ``false`` - ''' - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": None, "changes": {}} # Manage parameters pool_data = {} res_data = {} - pool_name = '{0}-Connection-Pool'.format(name) - if restype == 'topic_connection_factory': - pool_data['connectionDefinitionName'] = 'javax.jms.TopicConnectionFactory' - elif restype == 'queue_connection_factory': - pool_data['connectionDefinitionName'] = 'javax.jms.QueueConnectionFactory' - elif restype == 'connection_factory': - pool_data['connectionDefinitionName'] = 'javax.jms.ConnectionFactory' + pool_name = "{0}-Connection-Pool".format(name) + if restype == "topic_connection_factory": + pool_data["connectionDefinitionName"] = "javax.jms.TopicConnectionFactory" + elif restype == "queue_connection_factory": + pool_data["connectionDefinitionName"] = "javax.jms.QueueConnectionFactory" + elif restype == "connection_factory": + pool_data["connectionDefinitionName"] = "javax.jms.ConnectionFactory" else: - ret['result'] = False - ret['comment'] = 'Invalid restype' + ret["result"] = False + ret["comment"] = "Invalid restype" return ret - pool_data['description'] = description - res_data['description'] = description - res_data['enabled'] = enabled - res_data['poolName'] = pool_name - pool_data['steadyPoolSize'] = min_size - pool_data['maxPoolSize'] = max_size - pool_data['poolResizeQuantity'] = resize_quantity - pool_data['idleTimeoutInSeconds'] = idle_timeout - pool_data['maxWaitTimeInMillis'] = wait_timeout*1000 - pool_data['failAllConnections'] = reconnect_on_failure + pool_data["description"] = description + res_data["description"] = description + res_data["enabled"] = enabled + res_data["poolName"] = pool_name + pool_data["steadyPoolSize"] = min_size + pool_data["maxPoolSize"] = max_size + pool_data["poolResizeQuantity"] = resize_quantity + pool_data["idleTimeoutInSeconds"] = idle_timeout + pool_data["maxWaitTimeInMillis"] = wait_timeout * 1000 + pool_data["failAllConnections"] = reconnect_on_failure if transaction_support: - if transaction_support == 'xa_transaction': - pool_data['transactionSupport'] = 'XATransaction' - elif transaction_support == 'local_transaction': - pool_data['transactionSupport'] = 'LocalTransaction' - elif transaction_support == 'no_transaction': - pool_data['transactionSupport'] = 'NoTransaction' + if transaction_support == "xa_transaction": + pool_data["transactionSupport"] = "XATransaction" + elif transaction_support == "local_transaction": + pool_data["transactionSupport"] = "LocalTransaction" + elif transaction_support == "no_transaction": + pool_data["transactionSupport"] = "NoTransaction" else: - ret['result'] = False - ret['comment'] = 'Invalid transaction_support' + ret["result"] = False + ret["comment"] = "Invalid transaction_support" return ret - pool_data['isConnectionValidationRequired'] = connection_validation + pool_data["isConnectionValidationRequired"] = connection_validation - pool_ret = _do_element_present(pool_name, 'connector_c_pool', pool_data, server) - res_ret = _do_element_present(name, 'connector_resource', res_data, server) + pool_ret = _do_element_present(pool_name, "connector_c_pool", pool_data, server) + res_ret = _do_element_present(name, "connector_resource", res_data, server) - if not pool_ret['error'] and not res_ret['error']: - if not __opts__['test']: - ret['result'] = True + if not pool_ret["error"] and not res_ret["error"]: + if not __opts__["test"]: + ret["result"] = True - if pool_ret['create'] or res_ret['create']: - ret['changes']['pool'] = pool_ret['changes'] - ret['changes']['resource'] = res_ret['changes'] - if __opts__['test']: - ret['comment'] = 'Connection factory set to be created' + if pool_ret["create"] or res_ret["create"]: + ret["changes"]["pool"] = pool_ret["changes"] + ret["changes"]["resource"] = res_ret["changes"] + if __opts__["test"]: + ret["comment"] = "Connection factory set to be created" else: - ret['comment'] = 'Connection factory created' - elif pool_ret['update'] or res_ret['update']: - ret['changes']['pool'] = pool_ret['changes'] - ret['changes']['resource'] = res_ret['changes'] - if __opts__['test']: - ret['comment'] = 'Connection factory set to be updated' + ret["comment"] = "Connection factory created" + elif pool_ret["update"] or res_ret["update"]: + ret["changes"]["pool"] = pool_ret["changes"] + ret["changes"]["resource"] = res_ret["changes"] + if __opts__["test"]: + ret["comment"] = "Connection factory set to be updated" else: - ret['comment'] = 'Connection factory updated' + ret["comment"] = "Connection factory updated" else: - ret['result'] = True - ret['changes'] = {} - ret['comment'] = 'Connection factory is already up-to-date' + ret["result"] = True + ret["changes"] = {} + ret["comment"] = "Connection factory is already up-to-date" else: - ret['result'] = False - ret['comment'] = 'ERROR: {0} // {1}'.format(pool_ret['error'], res_ret['error']) + ret["result"] = False + ret["comment"] = "ERROR: {0} // {1}".format(pool_ret["error"], res_ret["error"]) return ret def connection_factory_absent(name, both=True, server=None): - ''' + """ Ensures the transaction factory is absent. name @@ -268,33 +285,32 @@ def connection_factory_absent(name, both=True, server=None): both Delete both the pool and the resource, defaults to ``true`` - ''' - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} - pool_name = '{0}-Connection-Pool'.format(name) - pool_ret = _do_element_absent(pool_name, 'connector_c_pool', {'cascade': both}, server) + """ + ret = {"name": name, "result": None, "comment": None, "changes": {}} + pool_name = "{0}-Connection-Pool".format(name) + pool_ret = _do_element_absent( + pool_name, "connector_c_pool", {"cascade": both}, server + ) - if not pool_ret['error']: - if __opts__['test'] and pool_ret['delete']: - ret['comment'] = 'Connection Factory set to be deleted' - elif pool_ret['delete']: - ret['result'] = True - ret['comment'] = 'Connection Factory deleted' + if not pool_ret["error"]: + if __opts__["test"] and pool_ret["delete"]: + ret["comment"] = "Connection Factory set to be deleted" + elif pool_ret["delete"]: + ret["result"] = True + ret["comment"] = "Connection Factory deleted" else: - ret['result'] = True - ret['comment'] = 'Connection Factory doesn\'t exist' + ret["result"] = True + ret["comment"] = "Connection Factory doesn't exist" else: - ret['result'] = False - ret['comment'] = 'Error: {0}'.format(pool_ret['error']) + ret["result"] = False + ret["comment"] = "Error: {0}".format(pool_ret["error"]) return ret -def destination_present(name, - physical, - restype='queue', - description='', - enabled=True, - server=None): - ''' +def destination_present( + name, physical, restype="queue", description="", enabled=True, server=None +): + """ Ensures that the JMS Destination Resource (queue or topic) is present name @@ -311,90 +327,92 @@ def destination_present(name, enabled Defaults to ``True`` - ''' - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": None, "changes": {}} params = {} # Set parameters dict - if restype == 'queue': - params['resType'] = 'javax.jms.Queue' - params['className'] = 'com.sun.messaging.Queue' - elif restype == 'topic': - params['resType'] = 'javax.jms.Topic' - params['className'] = 'com.sun.messaging.Topic' + if restype == "queue": + params["resType"] = "javax.jms.Queue" + params["className"] = "com.sun.messaging.Queue" + elif restype == "topic": + params["resType"] = "javax.jms.Topic" + params["className"] = "com.sun.messaging.Topic" else: - ret['result'] = False - ret['comment'] = 'Invalid restype' + ret["result"] = False + ret["comment"] = "Invalid restype" return ret - params['properties'] = {'Name': physical} - params['description'] = description - params['enabled'] = enabled + params["properties"] = {"Name": physical} + params["description"] = description + params["enabled"] = enabled - jms_ret = _do_element_present(name, 'admin_object_resource', params, server) - if not jms_ret['error']: - if not __opts__['test']: - ret['result'] = True - if jms_ret['create'] and __opts__['test']: - ret['comment'] = 'JMS Queue set to be created' - elif jms_ret['create']: - ret['changes'] = jms_ret['changes'] - ret['comment'] = 'JMS queue created' - elif jms_ret['update'] and __opts__['test']: - ret['comment'] = 'JMS Queue set to be updated' - elif jms_ret['update']: - ret['changes'] = jms_ret['changes'] - ret['comment'] = 'JMS Queue updated' + jms_ret = _do_element_present(name, "admin_object_resource", params, server) + if not jms_ret["error"]: + if not __opts__["test"]: + ret["result"] = True + if jms_ret["create"] and __opts__["test"]: + ret["comment"] = "JMS Queue set to be created" + elif jms_ret["create"]: + ret["changes"] = jms_ret["changes"] + ret["comment"] = "JMS queue created" + elif jms_ret["update"] and __opts__["test"]: + ret["comment"] = "JMS Queue set to be updated" + elif jms_ret["update"]: + ret["changes"] = jms_ret["changes"] + ret["comment"] = "JMS Queue updated" else: - ret['result'] = True - ret['comment'] = 'JMS Queue already up-to-date' + ret["result"] = True + ret["comment"] = "JMS Queue already up-to-date" else: - ret['result'] = False - ret['comment'] = 'Error from API: {0}'.format(jms_ret['error']) + ret["result"] = False + ret["comment"] = "Error from API: {0}".format(jms_ret["error"]) return ret def destination_absent(name, server=None): - ''' + """ Ensures that the JMS Destination doesn't exists name Name of the JMS Destination - ''' - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} - jms_ret = _do_element_absent(name, 'admin_object_resource', {}, server) - if not jms_ret['error']: - if __opts__['test'] and jms_ret['delete']: - ret['comment'] = 'JMS Queue set to be deleted' - elif jms_ret['delete']: - ret['result'] = True - ret['comment'] = 'JMS Queue deleted' + """ + ret = {"name": name, "result": None, "comment": None, "changes": {}} + jms_ret = _do_element_absent(name, "admin_object_resource", {}, server) + if not jms_ret["error"]: + if __opts__["test"] and jms_ret["delete"]: + ret["comment"] = "JMS Queue set to be deleted" + elif jms_ret["delete"]: + ret["result"] = True + ret["comment"] = "JMS Queue deleted" else: - ret['result'] = True - ret['comment'] = 'JMS Queue doesn\'t exist' + ret["result"] = True + ret["comment"] = "JMS Queue doesn't exist" else: - ret['result'] = False - ret['comment'] = 'Error: {0}'.format(jms_ret['error']) + ret["result"] = False + ret["comment"] = "Error: {0}".format(jms_ret["error"]) return ret -def jdbc_datasource_present(name, - description='', - enabled=True, - restype='datasource', - vendor='mysql', - sql_url='', - sql_user='', - sql_password='', - min_size=8, - max_size=32, - resize_quantity=2, - idle_timeout=300, - wait_timeout=60, - non_transactional=False, - transaction_isolation='', - isolation_guaranteed=True, - server=None): - ''' +def jdbc_datasource_present( + name, + description="", + enabled=True, + restype="datasource", + vendor="mysql", + sql_url="", + sql_user="", + sql_password="", + min_size=8, + max_size=32, + resize_quantity=2, + idle_timeout=300, + wait_timeout=60, + non_transactional=False, + transaction_isolation="", + isolation_guaranteed=True, + server=None, +): + """ Ensures that the JDBC Datasource exists name @@ -446,202 +464,204 @@ def jdbc_datasource_present(name, isolation_guaranteed All connections use the same isolation level - ''' - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": None, "changes": {}} # Manage parameters - res_name = 'jdbc/{0}'.format(name) + res_name = "jdbc/{0}".format(name) pool_data = {} pool_data_properties = {} res_data = {} - if restype == 'datasource': - pool_data['resType'] = 'javax.sql.DataSource' - elif restype == 'xa_datasource': - pool_data['resType'] = 'javax.sql.XADataSource' - elif restype == 'connection_pool_datasource': - pool_data['resType'] = 'javax.sql.ConnectionPoolDataSource' - elif restype == 'driver': - pool_data['resType'] = 'javax.sql.Driver' + if restype == "datasource": + pool_data["resType"] = "javax.sql.DataSource" + elif restype == "xa_datasource": + pool_data["resType"] = "javax.sql.XADataSource" + elif restype == "connection_pool_datasource": + pool_data["resType"] = "javax.sql.ConnectionPoolDataSource" + elif restype == "driver": + pool_data["resType"] = "javax.sql.Driver" datasources = {} - datasources['mysql'] = { - 'driver': 'com.mysql.jdbc.Driver', - 'datasource': 'com.mysql.jdbc.jdbc2.optional.MysqlDataSource', - 'xa_datasource': 'com.mysql.jdbc.jdbc2.optional.MysqlXADataSource', - 'connection_pool_datasource': 'com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource' + datasources["mysql"] = { + "driver": "com.mysql.jdbc.Driver", + "datasource": "com.mysql.jdbc.jdbc2.optional.MysqlDataSource", + "xa_datasource": "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource", + "connection_pool_datasource": "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource", } - datasources['postgresql'] = { - 'driver': 'org.postgresql.Driver', - 'datasource': 'org.postgresql.ds.PGSimpleDataSource', - 'xa_datasource': 'org.postgresql.xa.PGXADataSource', - 'connection_pool_datasource': 'org.postgresql.ds.PGConnectionPoolDataSource' + datasources["postgresql"] = { + "driver": "org.postgresql.Driver", + "datasource": "org.postgresql.ds.PGSimpleDataSource", + "xa_datasource": "org.postgresql.xa.PGXADataSource", + "connection_pool_datasource": "org.postgresql.ds.PGConnectionPoolDataSource", } - datasources['mssql'] = { - 'driver': 'com.microsoft.sqlserver.jdbc.SQLServerDriver', - 'datasource': 'com.microsoft.sqlserver.jdbc.SQLServerDataSource', - 'xa_datasource': 'com.microsoft.sqlserver.jdbc.SQLServerXADataSource', - 'connection_pool_datasource': 'com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource' + datasources["mssql"] = { + "driver": "com.microsoft.sqlserver.jdbc.SQLServerDriver", + "datasource": "com.microsoft.sqlserver.jdbc.SQLServerDataSource", + "xa_datasource": "com.microsoft.sqlserver.jdbc.SQLServerXADataSource", + "connection_pool_datasource": "com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource", } - if restype == 'driver': - pool_data['driverClassname'] = datasources[vendor]['driver'] + if restype == "driver": + pool_data["driverClassname"] = datasources[vendor]["driver"] else: - pool_data['datasourceClassname'] = datasources[vendor][restype] + pool_data["datasourceClassname"] = datasources[vendor][restype] - pool_data_properties['url'] = sql_url - pool_data_properties['user'] = sql_user - pool_data_properties['password'] = sql_password - pool_data['properties'] = pool_data_properties - pool_data['description'] = description - res_data['description'] = description - res_data['poolName'] = name - res_data['enabled'] = enabled - pool_data['steadyPoolSize'] = min_size - pool_data['maxPoolSize'] = max_size - pool_data['poolResizeQuantity'] = resize_quantity - pool_data['idleTimeoutInSeconds'] = idle_timeout - pool_data['maxWaitTimeInMillis'] = wait_timeout*1000 - pool_data['nonTransactionalConnections'] = non_transactional - pool_data['transactionIsolationLevel'] = transaction_isolation - pool_data['isIsolationLevelGuaranteed'] = isolation_guaranteed + pool_data_properties["url"] = sql_url + pool_data_properties["user"] = sql_user + pool_data_properties["password"] = sql_password + pool_data["properties"] = pool_data_properties + pool_data["description"] = description + res_data["description"] = description + res_data["poolName"] = name + res_data["enabled"] = enabled + pool_data["steadyPoolSize"] = min_size + pool_data["maxPoolSize"] = max_size + pool_data["poolResizeQuantity"] = resize_quantity + pool_data["idleTimeoutInSeconds"] = idle_timeout + pool_data["maxWaitTimeInMillis"] = wait_timeout * 1000 + pool_data["nonTransactionalConnections"] = non_transactional + pool_data["transactionIsolationLevel"] = transaction_isolation + pool_data["isIsolationLevelGuaranteed"] = isolation_guaranteed - pool_ret = _do_element_present(name, 'jdbc_connection_pool', pool_data, server) - res_ret = _do_element_present(res_name, 'jdbc_resource', res_data, server) + pool_ret = _do_element_present(name, "jdbc_connection_pool", pool_data, server) + res_ret = _do_element_present(res_name, "jdbc_resource", res_data, server) - if not pool_ret['error'] and not res_ret['error']: - if not __opts__['test']: - ret['result'] = True + if not pool_ret["error"] and not res_ret["error"]: + if not __opts__["test"]: + ret["result"] = True - if pool_ret['create'] or res_ret['create']: - ret['changes']['pool'] = pool_ret['changes'] - ret['changes']['resource'] = res_ret['changes'] - if __opts__['test']: - ret['comment'] = 'JDBC Datasource set to be created' + if pool_ret["create"] or res_ret["create"]: + ret["changes"]["pool"] = pool_ret["changes"] + ret["changes"]["resource"] = res_ret["changes"] + if __opts__["test"]: + ret["comment"] = "JDBC Datasource set to be created" else: - ret['comment'] = 'JDBC Datasource created' - elif pool_ret['update'] or res_ret['update']: - ret['changes']['pool'] = pool_ret['changes'] - ret['changes']['resource'] = res_ret['changes'] - if __opts__['test']: - ret['comment'] = 'JDBC Datasource set to be updated' + ret["comment"] = "JDBC Datasource created" + elif pool_ret["update"] or res_ret["update"]: + ret["changes"]["pool"] = pool_ret["changes"] + ret["changes"]["resource"] = res_ret["changes"] + if __opts__["test"]: + ret["comment"] = "JDBC Datasource set to be updated" else: - ret['comment'] = 'JDBC Datasource updated' + ret["comment"] = "JDBC Datasource updated" else: - ret['result'] = True - ret['changes'] = {} - ret['comment'] = 'JDBC Datasource is already up-to-date' + ret["result"] = True + ret["changes"] = {} + ret["comment"] = "JDBC Datasource is already up-to-date" else: - ret['result'] = False - ret['comment'] = 'ERROR: {0} // {1}'.format(pool_ret['error'], res_ret['error']) + ret["result"] = False + ret["comment"] = "ERROR: {0} // {1}".format(pool_ret["error"], res_ret["error"]) return ret def jdbc_datasource_absent(name, both=True, server=None): - ''' + """ Ensures the JDBC Datasource doesn't exists name Name of the datasource both Delete both the pool and the resource, defaults to ``true`` - ''' - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} - pool_ret = _do_element_absent(name, 'jdbc_connection_pool', {'cascade': both}, server) + """ + ret = {"name": name, "result": None, "comment": None, "changes": {}} + pool_ret = _do_element_absent( + name, "jdbc_connection_pool", {"cascade": both}, server + ) - if not pool_ret['error']: - if __opts__['test'] and pool_ret['delete']: - ret['comment'] = 'JDBC Datasource set to be deleted' - elif pool_ret['delete']: - ret['result'] = True - ret['comment'] = 'JDBC Datasource deleted' + if not pool_ret["error"]: + if __opts__["test"] and pool_ret["delete"]: + ret["comment"] = "JDBC Datasource set to be deleted" + elif pool_ret["delete"]: + ret["result"] = True + ret["comment"] = "JDBC Datasource deleted" else: - ret['result'] = True - ret['comment'] = 'JDBC Datasource doesn\'t exist' + ret["result"] = True + ret["comment"] = "JDBC Datasource doesn't exist" else: - ret['result'] = False - ret['comment'] = 'Error: {0}'.format(pool_ret['error']) + ret["result"] = False + ret["comment"] = "Error: {0}".format(pool_ret["error"]) return ret def system_properties_present(server=None, **kwargs): - ''' + """ Ensures that the system properties are present properties The system properties - ''' - ret = {'name': '', 'result': None, 'comment': None, 'changes': {}} + """ + ret = {"name": "", "result": None, "comment": None, "changes": {}} - del kwargs['name'] + del kwargs["name"] try: - data = __salt__['glassfish.get_system_properties'](server=server) + data = __salt__["glassfish.get_system_properties"](server=server) except requests.ConnectionError as error: - if __opts__['test']: - ret['changes'] = kwargs - ret['result'] = None + if __opts__["test"]: + ret["changes"] = kwargs + ret["result"] = None return ret else: - ret['error'] = "Can't connect to the server" + ret["error"] = "Can't connect to the server" return ret - ret['changes'] = {'data': data, 'kwargs': kwargs} + ret["changes"] = {"data": data, "kwargs": kwargs} if not data == kwargs: data.update(kwargs) - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['glassfish.update_system_properties'](data, server=server) - ret['changes'] = kwargs - ret['result'] = True - ret['comment'] = 'System properties updated' + __salt__["glassfish.update_system_properties"](data, server=server) + ret["changes"] = kwargs + ret["result"] = True + ret["comment"] = "System properties updated" except CommandExecutionError as error: - ret['comment'] = error - ret['result'] = False + ret["comment"] = error + ret["result"] = False else: - ret['result'] = None - ret['changes'] = kwargs - ret['coment'] = 'System properties would have been updated' + ret["result"] = None + ret["changes"] = kwargs + ret["coment"] = "System properties would have been updated" else: - ret['changes'] = {} - ret['result'] = True - ret['comment'] = 'System properties are already up-to-date' + ret["changes"] = {} + ret["result"] = True + ret["comment"] = "System properties are already up-to-date" return ret def system_properties_absent(name, server=None): - ''' + """ Ensures that the system property doesn't exists name Name of the system property - ''' - ret = {'name': '', 'result': None, 'comment': None, 'changes': {}} + """ + ret = {"name": "", "result": None, "comment": None, "changes": {}} try: - data = __salt__['glassfish.get_system_properties'](server=server) + data = __salt__["glassfish.get_system_properties"](server=server) except requests.ConnectionError as error: - if __opts__['test']: - ret['changes'] = {'Name': name} - ret['result'] = None + if __opts__["test"]: + ret["changes"] = {"Name": name} + ret["result"] = None return ret else: - ret['error'] = "Can't connect to the server" + ret["error"] = "Can't connect to the server" return ret if name in data: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['glassfish.delete_system_properties'](name, server=server) - ret['result'] = True - ret['comment'] = 'System properties deleted' + __salt__["glassfish.delete_system_properties"](name, server=server) + ret["result"] = True + ret["comment"] = "System properties deleted" except CommandExecutionError as error: - ret['comment'] = error - ret['result'] = False + ret["comment"] = error + ret["result"] = False else: - ret['result'] = None - ret['comment'] = 'System properties would have been deleted' - ret['changes'] = {'Name': name} + ret["result"] = None + ret["comment"] = "System properties would have been deleted" + ret["changes"] = {"Name": name} else: - ret['result'] = True - ret['comment'] = 'System properties are already absent' + ret["result"] = True + ret["comment"] = "System properties are already absent" return ret diff --git a/salt/states/glusterfs.py b/salt/states/glusterfs.py index 5df947dc27f..c3ecd631aa2 100644 --- a/salt/states/glusterfs.py +++ b/salt/states/glusterfs.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Manage GlusterFS pool. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, \ - print_function, generators +from __future__ import absolute_import, generators, print_function, unicode_literals + import logging # Import salt libs @@ -16,29 +16,32 @@ from salt.exceptions import SaltCloudException log = logging.getLogger(__name__) RESULT_CODES = [ - 'Peer {0} added successfully.', - 'Probe on localhost not needed', - 'Host {0} is already in the peer group', - 'Host {0} is already part of another cluster', - 'Volume on {0} conflicts with existing volumes', - 'UUID of {0} is the same as local uuid', + "Peer {0} added successfully.", + "Probe on localhost not needed", + "Host {0} is already in the peer group", + "Host {0} is already part of another cluster", + "Volume on {0} conflicts with existing volumes", + "UUID of {0} is the same as local uuid", '{0} responded with "unknown peer". This could happen if {0} doesn\'t have localhost defined', - 'Failed to add peer. Information on {0}\'s logs', - 'Cluster quorum is not met. Changing peers is not allowed.', - 'Failed to update list of missed snapshots from {0}', - 'Conflict comparing list of snapshots from {0}', - 'Peer is already being detached from cluster.'] + "Failed to add peer. Information on {0}'s logs", + "Cluster quorum is not met. Changing peers is not allowed.", + "Failed to update list of missed snapshots from {0}", + "Conflict comparing list of snapshots from {0}", + "Peer is already being detached from cluster.", +] def __virtual__(): - ''' + """ Only load this module if the gluster command exists - ''' - return 'glusterfs' if 'glusterfs.list_volumes' in __salt__ else False + """ + if "glusterfs.list_volumes" in __salt__: + return "glusterfs" + return (False, "glusterfs module could not be loaded") def peered(name): - ''' + """ Check if node is peered. name @@ -57,16 +60,13 @@ def peered(name): - two - three - four - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} try: - suc.check_name(name, 'a-zA-Z0-9._-') + suc.check_name(name, "a-zA-Z0-9._-") except SaltCloudException: - ret['comment'] = 'Invalid characters in peer name.' + ret["comment"] = "Invalid characters in peer name." return ret # Check if the name resolves to one of this minion IP addresses @@ -77,40 +77,55 @@ def peered(name): this_ips = set(salt.utils.network.ip_addrs()) this_ips.update(salt.utils.network.ip_addrs6()) if this_ips.intersection(name_ips): - ret['result'] = True - ret['comment'] = 'Peering with localhost is not needed' + ret["result"] = True + ret["comment"] = "Peering with localhost is not needed" return ret - peers = __salt__['glusterfs.peer_status']() + peers = __salt__["glusterfs.peer_status"]() - if peers and any(name in v['hostnames'] for v in peers.values()): - ret['result'] = True - ret['comment'] = 'Host {0} already peered'.format(name) + if peers and any(name in v["hostnames"] for v in peers.values()): + ret["result"] = True + ret["comment"] = "Host {0} already peered".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Peer {0} will be added.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Peer {0} will be added.".format(name) + ret["result"] = None return ret - if not __salt__['glusterfs.peer'](name): - ret['comment'] = 'Failed to peer with {0}, please check logs for errors'.format(name) + if not __salt__["glusterfs.peer"](name): + ret["comment"] = "Failed to peer with {0}, please check logs for errors".format( + name + ) return ret # Double check that the action succeeded - newpeers = __salt__['glusterfs.peer_status']() - if newpeers and any(name in v['hostnames'] for v in newpeers.values()): - ret['result'] = True - ret['comment'] = 'Host {0} successfully peered'.format(name) - ret['changes'] = {'new': newpeers, 'old': peers} + newpeers = __salt__["glusterfs.peer_status"]() + if newpeers and any(name in v["hostnames"] for v in newpeers.values()): + ret["result"] = True + ret["comment"] = "Host {0} successfully peered".format(name) + ret["changes"] = {"new": newpeers, "old": peers} else: - ret['comment'] = 'Host {0} was successfully peered but did not appear in the list of peers'.format(name) + ret[ + "comment" + ] = "Host {0} was successfully peered but did not appear in the list of peers".format( + name + ) return ret -def volume_present(name, bricks, stripe=False, replica=False, device_vg=False, - transport='tcp', start=False, force=False, arbiter=False): - ''' +def volume_present( + name, + bricks, + stripe=False, + replica=False, + device_vg=False, + transport="tcp", + start=False, + force=False, + arbiter=False, +): + """ Ensure that the volume exists name @@ -158,72 +173,71 @@ def volume_present(name, bricks, stripe=False, replica=False, device_vg=False, - arbiter: True - start: True - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} - if suc.check_name(name, 'a-zA-Z0-9._-'): - ret['comment'] = 'Invalid characters in volume name.' + if suc.check_name(name, "a-zA-Z0-9._-"): + ret["comment"] = "Invalid characters in volume name." return ret - volumes = __salt__['glusterfs.list_volumes']() + volumes = __salt__["glusterfs.list_volumes"]() if name not in volumes: - if __opts__['test']: - comment = 'Volume {0} will be created'.format(name) + if __opts__["test"]: + comment = "Volume {0} will be created".format(name) if start: - comment += ' and started' - ret['comment'] = comment - ret['result'] = None + comment += " and started" + ret["comment"] = comment + ret["result"] = None return ret - vol_created = __salt__['glusterfs.create_volume']( - name, bricks, stripe, - replica, device_vg, - transport, start, force, arbiter) + vol_created = __salt__["glusterfs.create_volume"]( + name, bricks, stripe, replica, device_vg, transport, start, force, arbiter + ) if not vol_created: - ret['comment'] = 'Creation of volume {0} failed'.format(name) + ret["comment"] = "Creation of volume {0} failed".format(name) return ret old_volumes = volumes - volumes = __salt__['glusterfs.list_volumes']() + volumes = __salt__["glusterfs.list_volumes"]() if name in volumes: - ret['changes'] = {'new': volumes, 'old': old_volumes} - ret['comment'] = 'Volume {0} is created'.format(name) + ret["changes"] = {"new": volumes, "old": old_volumes} + ret["comment"] = "Volume {0} is created".format(name) else: - ret['comment'] = 'Volume {0} already exists'.format(name) + ret["comment"] = "Volume {0} already exists".format(name) if start: - if __opts__['test']: + if __opts__["test"]: # volume already exists - ret['comment'] = ret['comment'] + ' and will be started' - ret['result'] = None + ret["comment"] = ret["comment"] + " and will be started" + ret["result"] = None return ret - if int(__salt__['glusterfs.info']()[name]['status']) == 1: - ret['result'] = True - ret['comment'] = ret['comment'] + ' and is started' + if int(__salt__["glusterfs.info"]()[name]["status"]) == 1: + ret["result"] = True + ret["comment"] = ret["comment"] + " and is started" else: - vol_started = __salt__['glusterfs.start_volume'](name) + vol_started = __salt__["glusterfs.start_volume"](name) if vol_started: - ret['result'] = True - ret['comment'] = ret['comment'] + ' and is now started' - if not ret['changes']: - ret['changes'] = {'new': 'started', 'old': 'stopped'} + ret["result"] = True + ret["comment"] = ret["comment"] + " and is now started" + if not ret["changes"]: + ret["changes"] = {"new": "started", "old": "stopped"} else: - ret['comment'] = ret['comment'] + ' but failed to start. Check logs for further information' + ret["comment"] = ( + ret["comment"] + + " but failed to start. Check logs for further information" + ) return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None else: - ret['result'] = True + ret["result"] = True return ret def started(name): - ''' + """ Check if volume has been started name @@ -233,41 +247,38 @@ def started(name): mycluster: glusterfs.started: [] - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} - volinfo = __salt__['glusterfs.info']() + volinfo = __salt__["glusterfs.info"]() if name not in volinfo: - ret['result'] = False - ret['comment'] = 'Volume {0} does not exist'.format(name) + ret["result"] = False + ret["comment"] = "Volume {0} does not exist".format(name) return ret - if int(volinfo[name]['status']) == 1: - ret['comment'] = 'Volume {0} is already started'.format(name) - ret['result'] = True + if int(volinfo[name]["status"]) == 1: + ret["comment"] = "Volume {0} is already started".format(name) + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Volume {0} will be started'.format(name) - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "Volume {0} will be started".format(name) + ret["result"] = None return ret - vol_started = __salt__['glusterfs.start_volume'](name) + vol_started = __salt__["glusterfs.start_volume"](name) if vol_started: - ret['result'] = True - ret['comment'] = 'Volume {0} is started'.format(name) - ret['change'] = {'new': 'started', 'old': 'stopped'} + ret["result"] = True + ret["comment"] = "Volume {0} is started".format(name) + ret["change"] = {"new": "started", "old": "stopped"} else: - ret['result'] = False - ret['comment'] = 'Failed to start volume {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to start volume {0}".format(name) return ret def add_volume_bricks(name, bricks): - ''' + """ Add brick(s) to an existing volume name @@ -290,41 +301,41 @@ def add_volume_bricks(name, bricks): - bricks: - host1:/srv/gluster/drive2 - host2:/srv/gluster/drive3 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} - volinfo = __salt__['glusterfs.info']() + volinfo = __salt__["glusterfs.info"]() if name not in volinfo: - ret['comment'] = 'Volume {0} does not exist'.format(name) + ret["comment"] = "Volume {0} does not exist".format(name) return ret - if int(volinfo[name]['status']) != 1: - ret['comment'] = 'Volume {0} is not started'.format(name) + if int(volinfo[name]["status"]) != 1: + ret["comment"] = "Volume {0} is not started".format(name) return ret - current_bricks = [brick['path'] for brick in volinfo[name]['bricks'].values()] + current_bricks = [brick["path"] for brick in volinfo[name]["bricks"].values()] if not set(bricks) - set(current_bricks): - ret['result'] = True - ret['comment'] = 'Bricks already added in volume {0}'.format(name) + ret["result"] = True + ret["comment"] = "Bricks already added in volume {0}".format(name) return ret - bricks_added = __salt__['glusterfs.add_volume_bricks'](name, bricks) + bricks_added = __salt__["glusterfs.add_volume_bricks"](name, bricks) if bricks_added: - ret['result'] = True - ret['comment'] = 'Bricks successfully added to volume {0}'.format(name) - new_bricks = [brick['path'] for brick in __salt__['glusterfs.info']()[name]['bricks'].values()] - ret['changes'] = {'new': new_bricks, 'old': current_bricks} + ret["result"] = True + ret["comment"] = "Bricks successfully added to volume {0}".format(name) + new_bricks = [ + brick["path"] + for brick in __salt__["glusterfs.info"]()[name]["bricks"].values() + ] + ret["changes"] = {"new": new_bricks, "old": current_bricks} return ret - ret['comment'] = 'Adding bricks to volume {0} failed'.format(name) + ret["comment"] = "Adding bricks to volume {0} failed".format(name) return ret def op_version(name, version): - ''' + """ .. versionadded:: 2019.2.0 Add brick(s) to an existing volume @@ -341,42 +352,47 @@ def op_version(name, version): glusterfs.op_version: - name: volume1 - version: 30707 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} try: - current = int(__salt__['glusterfs.get_op_version'](name)) + current = int(__salt__["glusterfs.get_op_version"](name)) except TypeError: - ret['result'] = False - ret['comment'] = __salt__['glusterfs.get_op_version'](name)[1] + ret["result"] = False + ret["comment"] = __salt__["glusterfs.get_op_version"](name)[1] return ret if current == version: - ret['comment'] = 'Glusterfs cluster.op-version for {0} already set to {1}'.format(name, version) - ret['result'] = True + ret[ + "comment" + ] = "Glusterfs cluster.op-version for {0} already set to {1}".format( + name, version + ) + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'An attempt would be made to set the cluster.op-version for {0} to {1}.'.format(name, version) - ret['result'] = None + elif __opts__["test"]: + ret[ + "comment" + ] = "An attempt would be made to set the cluster.op-version for {0} to {1}.".format( + name, version + ) + ret["result"] = None return ret - result = __salt__['glusterfs.set_op_version'](version) + result = __salt__["glusterfs.set_op_version"](version) if result[0] is False: - ret['comment'] = result[1] + ret["comment"] = result[1] return ret - ret['comment'] = result - ret['changes'] = {'old': current, 'new': version} - ret['result'] = True + ret["comment"] = result + ret["changes"] = {"old": current, "new": version} + ret["result"] = True return ret def max_op_version(name): - ''' + """ .. versionadded:: 2019.2.0 Add brick(s) to an existing volume @@ -390,42 +406,47 @@ def max_op_version(name): glusterfs.max_op_version: - name: volume1 - version: 30707 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} try: - current = int(__salt__['glusterfs.get_op_version'](name)) + current = int(__salt__["glusterfs.get_op_version"](name)) except TypeError: - ret['result'] = False - ret['comment'] = __salt__['glusterfs.get_op_version'](name)[1] + ret["result"] = False + ret["comment"] = __salt__["glusterfs.get_op_version"](name)[1] return ret try: - max_version = int(__salt__['glusterfs.get_max_op_version']()) + max_version = int(__salt__["glusterfs.get_max_op_version"]()) except TypeError: - ret['result'] = False - ret['comment'] = __salt__['glusterfs.get_max_op_version']()[1] + ret["result"] = False + ret["comment"] = __salt__["glusterfs.get_max_op_version"]()[1] return ret if current == max_version: - ret['comment'] = 'The cluster.op-version is already set to the cluster.max-op-version of {0}'.format(current) - ret['result'] = True + ret[ + "comment" + ] = "The cluster.op-version is already set to the cluster.max-op-version of {0}".format( + current + ) + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'An attempt would be made to set the cluster.op-version to {0}.'.format(max_version) - ret['result'] = None + elif __opts__["test"]: + ret[ + "comment" + ] = "An attempt would be made to set the cluster.op-version to {0}.".format( + max_version + ) + ret["result"] = None return ret - result = __salt__['glusterfs.set_op_version'](max_version) + result = __salt__["glusterfs.set_op_version"](max_version) if result[0] is False: - ret['comment'] = result[1] + ret["comment"] = result[1] return ret - ret['comment'] = result - ret['changes'] = {'old': current, 'new': max_version} - ret['result'] = True + ret["comment"] = result + ret["changes"] = {"old": current, "new": max_version} + ret["result"] = True return ret diff --git a/salt/states/gnomedesktop.py b/salt/states/gnomedesktop.py index 9e53b2d2182..4a7d53e6e67 100644 --- a/salt/states/gnomedesktop.py +++ b/salt/states/gnomedesktop.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configuration of the GNOME desktop ======================================== @@ -23,8 +23,9 @@ Control the GNOME settings - user: username - clock_show_date: true - clock_format: 12h -''' +""" from __future__ import absolute_import, print_function, unicode_literals + # Import python libs import logging import re @@ -36,22 +37,19 @@ log = logging.getLogger(__name__) def _check_current_value(gnome_kwargs, value): - ''' + """ Check the current value with the passed value - ''' - current_value = __salt__['gnome.get'](**gnome_kwargs) + """ + current_value = __salt__["gnome.get"](**gnome_kwargs) return six.text_type(current_value) == six.text_type(value) def _do(name, gnome_kwargs, preferences): - ''' + """ worker function for the others to use this handles all the gsetting magic - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} messages = [] @@ -60,187 +58,231 @@ def _do(name, gnome_kwargs, preferences): value = preferences[pref] if isinstance(value, bool): - ftype = 'boolean' + ftype = "boolean" # need to convert boolean values to strings and make lowercase to # pass to gsettings value = six.text_type(value).lower() elif isinstance(value, int): - ftype = 'int' + ftype = "int" elif isinstance(value, six.string_types): - ftype = 'string' + ftype = "string" else: - ftype = 'string' + ftype = "string" - gnome_kwargs.update({'key': key, 'value': value}) + gnome_kwargs.update({"key": key, "value": value}) if _check_current_value(gnome_kwargs, value): - messages.append('{0} is already set to {1}'.format(key, value)) + messages.append("{0} is already set to {1}".format(key, value)) else: - result = __salt__['gnome.set'](**gnome_kwargs) - if result['retcode'] == 0: - messages.append('Setting {0} to {1}'.format(key, value)) - ret['changes'][key] = '{0}:{1}'.format(key, value) - ret['result'] = True + result = __salt__["gnome.set"](**gnome_kwargs) + if result["retcode"] == 0: + messages.append("Setting {0} to {1}".format(key, value)) + ret["changes"][key] = "{0}:{1}".format(key, value) + ret["result"] = True else: - messages.append(result['stdout']) - ret['result'] = False + messages.append(result["stdout"]) + ret["result"] = False - ret['comment'] = ', '.join(messages) + ret["comment"] = ", ".join(messages) return ret -def wm_preferences(name, - user=None, - action_double_click_titlebar=None, - action_middle_click_titlebar=None, - action_right_click_titlebar=None, - application_based=None, - audible_bell=None, - auto_raise=None, - auto_raise_delay=None, - button_layout=None, - disable_workarounds=None, - focus_mode=None, - focus_new_windows=None, - mouse_button_modifier=None, - num_workspaces=None, - raise_on_click=None, - resize_with_right_button=None, - theme=None, - titlebar_font=None, - titlebar_uses_system_font=None, - visual_bell=None, - visual_bell_type=None, - workspace_names=None, - **kwargs): - ''' +def wm_preferences( + name, + user=None, + action_double_click_titlebar=None, + action_middle_click_titlebar=None, + action_right_click_titlebar=None, + application_based=None, + audible_bell=None, + auto_raise=None, + auto_raise_delay=None, + button_layout=None, + disable_workarounds=None, + focus_mode=None, + focus_new_windows=None, + mouse_button_modifier=None, + num_workspaces=None, + raise_on_click=None, + resize_with_right_button=None, + theme=None, + titlebar_font=None, + titlebar_uses_system_font=None, + visual_bell=None, + visual_bell_type=None, + workspace_names=None, + **kwargs +): + """ wm_preferences: sets values in the org.gnome.desktop.wm.preferences schema - ''' - gnome_kwargs = { - 'user': user, - 'schema': 'org.gnome.desktop.wm.preferences' - } + """ + gnome_kwargs = {"user": user, "schema": "org.gnome.desktop.wm.preferences"} - preferences = ['action_double_click_titlebar', - 'action_middle_click_titlebar', 'action_right_click_titlebar', - 'application_based', 'audible_bell', 'auto_raise', - 'auto_raise_delay', 'button_layout', 'disable_workarounds', - 'focus_mode', 'focus_new_windows', 'mouse_button_modifier', - 'num_workspaces', 'raise_on_click', 'resize_with_right_button', - 'theme', 'titlebar_font', 'titlebar_uses_system_font', - 'visual_bell', 'visual_bell_type', 'workspace_names'] + preferences = [ + "action_double_click_titlebar", + "action_middle_click_titlebar", + "action_right_click_titlebar", + "application_based", + "audible_bell", + "auto_raise", + "auto_raise_delay", + "button_layout", + "disable_workarounds", + "focus_mode", + "focus_new_windows", + "mouse_button_modifier", + "num_workspaces", + "raise_on_click", + "resize_with_right_button", + "theme", + "titlebar_font", + "titlebar_uses_system_font", + "visual_bell", + "visual_bell_type", + "workspace_names", + ] preferences_hash = {} for pref in preferences: if pref in locals() and locals()[pref] is not None: - key = re.sub('_', '-', pref) + key = re.sub("_", "-", pref) preferences_hash[key] = locals()[pref] return _do(name, gnome_kwargs, preferences_hash) -def desktop_lockdown(name, - user=None, - disable_application_handlers=None, - disable_command_line=None, - disable_lock_screen=None, - disable_log_out=None, - disable_print_setup=None, - disable_printing=None, - disable_save_to_disk=None, - disable_user_switching=None, - user_administration_disabled=None, - **kwargs): - ''' +def desktop_lockdown( + name, + user=None, + disable_application_handlers=None, + disable_command_line=None, + disable_lock_screen=None, + disable_log_out=None, + disable_print_setup=None, + disable_printing=None, + disable_save_to_disk=None, + disable_user_switching=None, + user_administration_disabled=None, + **kwargs +): + """ desktop_lockdown: sets values in the org.gnome.desktop.lockdown schema - ''' - gnome_kwargs = { - 'user': user, - 'schema': 'org.gnome.desktop.lockdown' - } + """ + gnome_kwargs = {"user": user, "schema": "org.gnome.desktop.lockdown"} - preferences = ['disable_application_handlers', 'disable_command_line', - 'disable_lock_screen', 'disable_log_out', 'disable_print_setup', - 'disable_printing', 'disable_save_to_disk', - 'disable_user_switching', 'user_administration_disabled'] + preferences = [ + "disable_application_handlers", + "disable_command_line", + "disable_lock_screen", + "disable_log_out", + "disable_print_setup", + "disable_printing", + "disable_save_to_disk", + "disable_user_switching", + "user_administration_disabled", + ] preferences_hash = {} for pref in preferences: if pref in locals() and locals()[pref] is not None: - key = re.sub('_', '-', pref) + key = re.sub("_", "-", pref) preferences_hash[key] = locals()[pref] return _do(name, gnome_kwargs, preferences_hash) -def desktop_interface(name, - user=None, - automatic_mnemonics=None, - buttons_have_icons=None, - can_change_accels=None, - clock_format=None, - clock_show_date=None, - clock_show_seconds=None, - cursor_blink=None, - cursor_blink_time=None, - cursor_blink_timeout=None, - cursor_size=None, - cursor_theme=None, - document_font_name=None, - enable_animations=None, - font_name=None, - gtk_color_palette=None, - gtk_color_scheme=None, - gtk_im_module=None, - gtk_im_preedit_style=None, - gtk_im_status_style=None, - gtk_key_theme=None, - gtk_theme=None, - gtk_timeout_initial=None, - gtk_timeout_repeat=None, - icon_theme=None, - menubar_accel=None, - menubar_detachable=None, - menus_have_icons=None, - menus_have_tearoff=None, - monospace_font_name=None, - show_input_method_menu=None, - show_unicode_menu=None, - text_scaling_factor=None, - toolbar_detachable=None, - toolbar_icons_size=None, - toolbar_style=None, - toolkit_accessibility=None, - **kwargs): - ''' +def desktop_interface( + name, + user=None, + automatic_mnemonics=None, + buttons_have_icons=None, + can_change_accels=None, + clock_format=None, + clock_show_date=None, + clock_show_seconds=None, + cursor_blink=None, + cursor_blink_time=None, + cursor_blink_timeout=None, + cursor_size=None, + cursor_theme=None, + document_font_name=None, + enable_animations=None, + font_name=None, + gtk_color_palette=None, + gtk_color_scheme=None, + gtk_im_module=None, + gtk_im_preedit_style=None, + gtk_im_status_style=None, + gtk_key_theme=None, + gtk_theme=None, + gtk_timeout_initial=None, + gtk_timeout_repeat=None, + icon_theme=None, + menubar_accel=None, + menubar_detachable=None, + menus_have_icons=None, + menus_have_tearoff=None, + monospace_font_name=None, + show_input_method_menu=None, + show_unicode_menu=None, + text_scaling_factor=None, + toolbar_detachable=None, + toolbar_icons_size=None, + toolbar_style=None, + toolkit_accessibility=None, + **kwargs +): + """ desktop_interface: sets values in the org.gnome.desktop.interface schema - ''' - gnome_kwargs = { - 'user': user, - 'schema': 'org.gnome.desktop.interface' - } + """ + gnome_kwargs = {"user": user, "schema": "org.gnome.desktop.interface"} - preferences = ['automatic_mnemonics', 'buttons_have_icons', - 'can_change_accels', 'clock_format', 'clock_show_date', - 'clock_show_seconds', 'cursor_blink', 'cursor_blink_time', - 'cursor_blink_timeout', 'cursor_size', 'cursor_theme', - 'document_font_name', 'enable_animations', 'font_name', - 'gtk_color_palette', 'gtk_color_scheme', 'gtk_im_module', - 'gtk_im_preedit_style', 'gtk_im_status_style', 'gtk_key_theme', - 'gtk_theme', 'gtk_timeout_initial', 'gtk_timeout_repeat', - 'icon_theme', 'menubar_accel', 'menubar_detachable', - 'menus_have_icons', 'menus_have_tearoff', 'monospace_font_name', - 'show_input_method_menu', 'show_unicode_menu', - 'text_scaling_factor', 'toolbar_detachable', 'toolbar_icons_size', - 'toolbar_style', 'toolkit_accessibility'] + preferences = [ + "automatic_mnemonics", + "buttons_have_icons", + "can_change_accels", + "clock_format", + "clock_show_date", + "clock_show_seconds", + "cursor_blink", + "cursor_blink_time", + "cursor_blink_timeout", + "cursor_size", + "cursor_theme", + "document_font_name", + "enable_animations", + "font_name", + "gtk_color_palette", + "gtk_color_scheme", + "gtk_im_module", + "gtk_im_preedit_style", + "gtk_im_status_style", + "gtk_key_theme", + "gtk_theme", + "gtk_timeout_initial", + "gtk_timeout_repeat", + "icon_theme", + "menubar_accel", + "menubar_detachable", + "menus_have_icons", + "menus_have_tearoff", + "monospace_font_name", + "show_input_method_menu", + "show_unicode_menu", + "text_scaling_factor", + "toolbar_detachable", + "toolbar_icons_size", + "toolbar_style", + "toolkit_accessibility", + ] preferences_hash = {} for pref in preferences: if pref in locals() and locals()[pref] is not None: - key = re.sub('_', '-', pref) + key = re.sub("_", "-", pref) preferences_hash[key] = locals()[pref] return _do(name, gnome_kwargs, preferences_hash) diff --git a/salt/states/gpg.py b/salt/states/gpg.py index 0349e654202..b3fd22f81fa 100644 --- a/salt/states/gpg.py +++ b/salt/states/gpg.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Management of the GPG keychains =============================== .. versionadded:: 2016.3.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging # Import 3rd-party libs @@ -14,31 +15,29 @@ from salt.ext import six log = logging.getLogger(__name__) -_VALID_TRUST_VALUES = ['expired', - 'unknown', - 'not_trusted', - 'marginally', - 'fully', - 'ultimately'] +_VALID_TRUST_VALUES = [ + "expired", + "unknown", + "not_trusted", + "marginally", + "fully", + "ultimately", +] TRUST_MAP = { - 'expired': 'Expired', - 'unknown': 'Unknown', - 'not_trusted': 'Not Trusted', - 'marginally': 'Marginally', - 'fully': 'Fully Trusted', - 'ultimately': 'Ultimately Trusted' + "expired": "Expired", + "unknown": "Unknown", + "not_trusted": "Not Trusted", + "marginally": "Marginally", + "fully": "Fully Trusted", + "ultimately": "Ultimately Trusted", } -def present(name, - keys=None, - user=None, - keyserver=None, - gnupghome=None, - trust=None, - **kwargs): - ''' +def present( + name, keys=None, user=None, keyserver=None, gnupghome=None, trust=None, **kwargs +): + """ Ensure GPG public key is present in keychain name @@ -63,20 +62,17 @@ def present(name, fully, ultimately - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - _current_keys = __salt__['gpg.list_keys'](user=user, gnupghome=gnupghome) + _current_keys = __salt__["gpg.list_keys"](user=user, gnupghome=gnupghome) current_keys = {} for key in _current_keys: - keyid = key['keyid'] + keyid = key["keyid"] current_keys[keyid] = {} - current_keys[keyid]['trust'] = key['trust'] + current_keys[keyid]["trust"] = key["trust"] if not keys: keys = name @@ -88,60 +84,58 @@ def present(name, if key in current_keys.keys(): if trust: if trust in _VALID_TRUST_VALUES: - if current_keys[key]['trust'] != TRUST_MAP[trust]: + if current_keys[key]["trust"] != TRUST_MAP[trust]: # update trust level - result = __salt__['gpg.trust_key'](keyid=key, - trust_level=trust, - user=user, - ) - if 'result' in result and not result['result']: - ret['result'] = result['result'] - ret['comment'].append(result['comment']) + result = __salt__["gpg.trust_key"]( + keyid=key, trust_level=trust, user=user, + ) + if "result" in result and not result["result"]: + ret["result"] = result["result"] + ret["comment"].append(result["comment"]) else: - ret['comment'].append('Set trust level for {0} to {1}'.format(key, trust)) + ret["comment"].append( + "Set trust level for {0} to {1}".format(key, trust) + ) else: - ret['comment'].append('GPG Public Key {0} already in correct trust state'.format(key)) + ret["comment"].append( + "GPG Public Key {0} already in correct trust state".format( + key + ) + ) else: - ret['comment'].append('Invalid trust level {0}'.format(trust)) + ret["comment"].append("Invalid trust level {0}".format(trust)) - ret['comment'].append('GPG Public Key {0} already in keychain '.format(key)) + ret["comment"].append("GPG Public Key {0} already in keychain ".format(key)) else: - result = __salt__['gpg.receive_keys'](keyserver, - key, - user, - gnupghome, - ) - if 'result' in result and not result['result']: - ret['result'] = result['result'] - ret['comment'].append(result['comment']) + result = __salt__["gpg.receive_keys"](keyserver, key, user, gnupghome,) + if "result" in result and not result["result"]: + ret["result"] = result["result"] + ret["comment"].append(result["comment"]) else: - ret['comment'].append('Adding {0} to GPG keychain'.format(name)) + ret["comment"].append("Adding {0} to GPG keychain".format(name)) if trust: if trust in _VALID_TRUST_VALUES: - result = __salt__['gpg.trust_key'](keyid=key, - trust_level=trust, - user=user, - ) - if 'result' in result and not result['result']: - ret['result'] = result['result'] - ret['comment'].append(result['comment']) + result = __salt__["gpg.trust_key"]( + keyid=key, trust_level=trust, user=user, + ) + if "result" in result and not result["result"]: + ret["result"] = result["result"] + ret["comment"].append(result["comment"]) else: - ret['comment'].append('Set trust level for {0} to {1}'.format(key, trust)) + ret["comment"].append( + "Set trust level for {0} to {1}".format(key, trust) + ) else: - ret['comment'].append('Invalid trust level {0}'.format(trust)) + ret["comment"].append("Invalid trust level {0}".format(trust)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret -def absent(name, - keys=None, - user=None, - gnupghome=None, - **kwargs): - ''' +def absent(name, keys=None, user=None, gnupghome=None, **kwargs): + """ Ensure GPG public key is absent in keychain name @@ -156,18 +150,15 @@ def absent(name, gnupghome Override GNUPG Home directory - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - _current_keys = __salt__['gpg.list_keys']() + _current_keys = __salt__["gpg.list_keys"]() current_keys = [] for key in _current_keys: - current_keys.append(key['keyid']) + current_keys.append(key["keyid"]) if not keys: keys = name @@ -177,16 +168,13 @@ def absent(name, for key in keys: if key in current_keys: - result = __salt__['gpg.delete_key'](key, - user, - gnupghome, - ) - if 'result' in result and not result['result']: - ret['result'] = result['result'] - ret['comment'].append(result['comment']) + result = __salt__["gpg.delete_key"](key, user, gnupghome,) + if "result" in result and not result["result"]: + ret["result"] = result["result"] + ret["comment"].append(result["comment"]) else: - ret['comment'].append('Deleting {0} from GPG keychain'.format(name)) + ret["comment"].append("Deleting {0} from GPG keychain".format(name)) else: - ret['comment'].append('{0} not found in GPG keychain'.format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"].append("{0} not found in GPG keychain".format(name)) + ret["comment"] = "\n".join(ret["comment"]) return ret diff --git a/salt/states/grafana.py b/salt/states/grafana.py index 6ee8ca24e35..a40579a4172 100644 --- a/salt/states/grafana.py +++ b/salt/states/grafana.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Grafana Dashboards This module uses ``elasticsearch``, which can be installed via package, or pip. @@ -165,68 +165,72 @@ The behavior of this module is to create dashboards if they do not exist, to add rows if they do not exist in existing dashboards, and to update rows if they exist in dashboards. The module will not manage rows that are not defined, allowing users to manage their own custom rows. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy # Import Salt libs import salt.utils.json from salt.exceptions import SaltInvocationError -from salt.utils.dictdiffer import DictDiffer # Import 3rd-party from salt.ext.six import string_types +from salt.utils.dictdiffer import DictDiffer def __virtual__(): - ''' + """ Only load if grafana is available. - ''' - return 'grafana' if 'elasticsearch.exists' in __salt__ else False + """ + if "elasticsearch.exists" in __salt__: + return "grafana" + return (False, "elasticsearch module could not be loaded") def _parse_profile(profile): - ''' + """ From a pillar key, or a dictionary, return index and host keys. - ''' + """ if isinstance(profile, string_types): - _profile = __salt__['config.option'](profile) + _profile = __salt__["config.option"](profile) if not _profile: - msg = 'Pillar key for profile {0} not found.'.format(profile) + msg = "Pillar key for profile {0} not found.".format(profile) raise SaltInvocationError(msg) else: _profile = profile - hosts = _profile.get('hosts') - index = _profile.get('index') + hosts = _profile.get("hosts") + index = _profile.get("index") return (hosts, index) def _rows_differ(row, _row): - ''' + """ Check if grafana dashboard row and _row differ - ''' + """ row_copy = copy.deepcopy(row) _row_copy = copy.deepcopy(_row) # Strip id from all panels in both rows, since they are always generated. - for panel in row_copy['panels']: - if 'id' in panel: - del panel['id'] - for _panel in _row_copy['panels']: - if 'id' in _panel: - del _panel['id'] + for panel in row_copy["panels"]: + if "id" in panel: + del panel["id"] + for _panel in _row_copy["panels"]: + if "id" in _panel: + del _panel["id"] diff = DictDiffer(row_copy, _row_copy) return diff.changed() or diff.added() or diff.removed() def dashboard_present( - name, - dashboard=None, - dashboard_from_pillar=None, - rows=None, - rows_from_pillar=None, - profile='grafana'): - ''' + name, + dashboard=None, + dashboard_from_pillar=None, + rows=None, + rows_from_pillar=None, + profile="grafana", +): + """ Ensure the grafana dashboard exists and is managed. name @@ -250,25 +254,26 @@ def dashboard_present( profile A pillar key or dict that contains a list of hosts and an elasticsearch index to use. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} if not profile: - raise SaltInvocationError('profile is a required argument.') + raise SaltInvocationError("profile is a required argument.") if dashboard and dashboard_from_pillar: - raise SaltInvocationError('dashboard and dashboard_from_pillar are' - ' mutually exclusive arguments.') + raise SaltInvocationError( + "dashboard and dashboard_from_pillar are" " mutually exclusive arguments." + ) hosts, index = _parse_profile(profile) if not index: - raise SaltInvocationError('index is a required key in the profile.') + raise SaltInvocationError("index is a required key in the profile.") if not dashboard: - dashboard = __salt__['pillar.get'](dashboard_from_pillar) + dashboard = __salt__["pillar.get"](dashboard_from_pillar) if not rows: rows = [] if rows_from_pillar: for key in rows_from_pillar: - pillar_rows = __salt__['pillar.get'](key) + pillar_rows = __salt__["pillar.get"](key) # Pillar contains a list of rows if isinstance(pillar_rows, list): for row in pillar_rows: @@ -277,103 +282,97 @@ def dashboard_present( else: rows.append(pillar_rows) - exists = __salt__['elasticsearch.exists']( - index=index, id=name, doc_type='dashboard', hosts=hosts + exists = __salt__["elasticsearch.exists"]( + index=index, id=name, doc_type="dashboard", hosts=hosts ) if exists: - _dashboard = __salt__['elasticsearch.get']( - index=index, id=name, doc_type='dashboard', hosts=hosts + _dashboard = __salt__["elasticsearch.get"]( + index=index, id=name, doc_type="dashboard", hosts=hosts ) - _dashboard = _dashboard.get('_source', {}).get('dashboard') + _dashboard = _dashboard.get("_source", {}).get("dashboard") _dashboard = salt.utils.json.loads(_dashboard) else: if not dashboard: - raise SaltInvocationError('Grafana dashboard does not exist and no' - ' dashboard template was provided.') - if __opts__['test']: - ret['comment'] = 'Dashboard {0} is set to be created.'.format( - name + raise SaltInvocationError( + "Grafana dashboard does not exist and no" + " dashboard template was provided." ) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Dashboard {0} is set to be created.".format(name) + ret["result"] = None return ret _dashboard = dashboard update_rows = [] _ids = [] _data = {} - for _n, _row in enumerate(_dashboard['rows']): + for _n, _row in enumerate(_dashboard["rows"]): # Collect the unique ids - for _panel in _row['panels']: - if 'id' in _panel: - _ids.append(_panel['id']) + for _panel in _row["panels"]: + if "id" in _panel: + _ids.append(_panel["id"]) # Collect all of the titles in the existing dashboard - if 'title' in _row: - _data[_row['title']] = _n + if "title" in _row: + _data[_row["title"]] = _n _ids.sort() if not _ids: _ids = [1] for row in rows: - if 'title' not in row: - raise SaltInvocationError('title is a required key for rows.') + if "title" not in row: + raise SaltInvocationError("title is a required key for rows.") # Each panel needs to have a unique ID - for panel in row['panels']: + for panel in row["panels"]: _ids.append(_ids[-1] + 1) - panel['id'] = _ids[-1] - title = row['title'] + panel["id"] = _ids[-1] + title = row["title"] # If the title doesn't exist, we need to add this row if title not in _data: update_rows.append(title) - _dashboard['rows'].append(row) + _dashboard["rows"].append(row) continue # For existing titles, replace the row if it differs _n = _data[title] - if _rows_differ(row, _dashboard['rows'][_n]): - _dashboard['rows'][_n] = row + if _rows_differ(row, _dashboard["rows"][_n]): + _dashboard["rows"][_n] = row update_rows.append(title) if not update_rows: - ret['result'] = True - ret['comment'] = 'Dashboard {0} is up to date'.format(name) + ret["result"] = True + ret["comment"] = "Dashboard {0} is up to date".format(name) return ret - if __opts__['test']: - msg = 'Dashboard {0} is set to be updated.'.format(name) + if __opts__["test"]: + msg = "Dashboard {0} is set to be updated.".format(name) if update_rows: - msg = '{0} The following rows set to be updated: {1}'.format( + msg = "{0} The following rows set to be updated: {1}".format( msg, update_rows ) - ret['comment'] = msg + ret["comment"] = msg return ret body = { - 'user': 'guest', - 'group': 'guest', - 'title': name, - 'dashboard': salt.utils.json.dumps(_dashboard) + "user": "guest", + "group": "guest", + "title": name, + "dashboard": salt.utils.json.dumps(_dashboard), } - updated = __salt__['elasticsearch.index']( - index=index, doc_type='dashboard', body=body, id=name, - hosts=hosts + updated = __salt__["elasticsearch.index"]( + index=index, doc_type="dashboard", body=body, id=name, hosts=hosts ) if updated: - ret['result'] = True - ret['changes']['changed'] = name - msg = 'Updated dashboard {0}.'.format(name) + ret["result"] = True + ret["changes"]["changed"] = name + msg = "Updated dashboard {0}.".format(name) if update_rows: - msg = '{0} The following rows were updated: {1}'.format( - msg, update_rows - ) - ret['comment'] = msg + msg = "{0} The following rows were updated: {1}".format(msg, update_rows) + ret["comment"] = msg else: - ret['result'] = False - msg = 'Failed to update dashboard {0}.'.format(name) - ret['comment'] = msg + ret["result"] = False + msg = "Failed to update dashboard {0}.".format(name) + ret["comment"] = msg return ret -def dashboard_absent( - name, - hosts=None, - profile='grafana'): - ''' +def dashboard_absent(name, hosts=None, profile="grafana"): + """ Ensure the named grafana dashboard is deleted. name @@ -382,35 +381,33 @@ def dashboard_absent( profile A pillar key or dict that contains a list of hosts and an elasticsearch index to use. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} hosts, index = _parse_profile(profile) if not index: - raise SaltInvocationError('index is a required key in the profile.') + raise SaltInvocationError("index is a required key in the profile.") - exists = __salt__['elasticsearch.exists']( - index=index, id=name, doc_type='dashboard', hosts=hosts + exists = __salt__["elasticsearch.exists"]( + index=index, id=name, doc_type="dashboard", hosts=hosts ) if exists: - if __opts__['test']: - ret['comment'] = 'Dashboard {0} is set to be removed.'.format( - name - ) + if __opts__["test"]: + ret["comment"] = "Dashboard {0} is set to be removed.".format(name) return ret - deleted = __salt__['elasticsearch.delete']( - index=index, doc_type='dashboard', id=name, hosts=hosts + deleted = __salt__["elasticsearch.delete"]( + index=index, doc_type="dashboard", id=name, hosts=hosts ) if deleted: - ret['result'] = True - ret['changes']['old'] = name - ret['changes']['new'] = None + ret["result"] = True + ret["changes"]["old"] = name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} dashboard.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} dashboard.".format(name) else: - ret['result'] = True - ret['comment'] = 'Dashboard {0} does not exist.'.format(name) + ret["result"] = True + ret["comment"] = "Dashboard {0} does not exist.".format(name) return ret diff --git a/salt/states/grafana4_dashboard.py b/salt/states/grafana4_dashboard.py index 25997ded376..5621058b075 100644 --- a/salt/states/grafana4_dashboard.py +++ b/salt/states/grafana4_dashboard.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Grafana v4.0 Dashboards .. versionadded:: 2017.7.0 @@ -51,10 +51,11 @@ allowing users to manage their own custom rows. - target: alias(constantLine(50), 'max') title: Imaginary type: graph -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy # Import Salt libs @@ -64,24 +65,28 @@ from salt.utils.dictdiffer import DictDiffer def __virtual__(): - '''Only load if grafana4 module is available''' - return 'grafana4.get_dashboard' in __salt__ + """Only load if grafana4 module is available""" + if "grafana4.get_dashboard" in __salt__: + return True + return (False, "grafana4 module could not be loaded") -_DEFAULT_DASHBOARD_PILLAR = 'grafana_dashboards:default' -_DEFAULT_PANEL_PILLAR = 'grafana_panels:default' -_DEFAULT_ROW_PILLAR = 'grafana_rows:default' -_PINNED_ROWS_PILLAR = 'grafana_pinned_rows' +_DEFAULT_DASHBOARD_PILLAR = "grafana_dashboards:default" +_DEFAULT_PANEL_PILLAR = "grafana_panels:default" +_DEFAULT_ROW_PILLAR = "grafana_rows:default" +_PINNED_ROWS_PILLAR = "grafana_pinned_rows" -def present(name, - base_dashboards_from_pillar=None, - base_panels_from_pillar=None, - base_rows_from_pillar=None, - dashboard=None, - orgname=None, - profile='grafana'): - ''' +def present( + name, + base_dashboards_from_pillar=None, + base_panels_from_pillar=None, + base_rows_from_pillar=None, + dashboard=None, + orgname=None, + profile="grafana", +): + """ Ensure the grafana dashboard exists and is managed. name @@ -105,31 +110,29 @@ def present(name, profile Configuration profile used to connect to the Grafana instance. Default is 'grafana'. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} dashboard = dashboard or {} if isinstance(profile, six.string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) # Add pillar keys for default configuration base_dashboards_from_pillar = [_DEFAULT_DASHBOARD_PILLAR] + ( - base_dashboards_from_pillar or []) - base_panels_from_pillar = [_DEFAULT_PANEL_PILLAR] + ( - base_panels_from_pillar or []) - base_rows_from_pillar = [_DEFAULT_ROW_PILLAR] + ( - base_rows_from_pillar or []) + base_dashboards_from_pillar or [] + ) + base_panels_from_pillar = [_DEFAULT_PANEL_PILLAR] + (base_panels_from_pillar or []) + base_rows_from_pillar = [_DEFAULT_ROW_PILLAR] + (base_rows_from_pillar or []) # Build out all dashboard fields - new_dashboard = _inherited_dashboard( - dashboard, base_dashboards_from_pillar, ret) - if 'title' not in new_dashboard: - new_dashboard['title'] = name - rows = new_dashboard.get('rows', []) + new_dashboard = _inherited_dashboard(dashboard, base_dashboards_from_pillar, ret) + if "title" not in new_dashboard: + new_dashboard["title"] = name + rows = new_dashboard.get("rows", []) for i, row in enumerate(rows): rows[i] = _inherited_row(row, base_rows_from_pillar, ret) for row in rows: - panels = row.get('panels', []) + panels = row.get("panels", []) for i, panel in enumerate(panels): panels[i] = _inherited_panel(panel, base_panels_from_pillar, ret) _auto_adjust_panel_spans(new_dashboard) @@ -137,80 +140,82 @@ def present(name, _ensure_annotations(new_dashboard) # Create dashboard if it does not exist - old_dashboard = __salt__['grafana4.get_dashboard'](name, orgname, profile) + old_dashboard = __salt__["grafana4.get_dashboard"](name, orgname, profile) if not old_dashboard: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Dashboard {0} is set to be created.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Dashboard {0} is set to be created.".format(name) return ret - response = __salt__['grafana4.create_update_dashboard']( - dashboard=new_dashboard, overwrite=True, profile=profile) - if response.get('status') == 'success': - ret['comment'] = 'Dashboard {0} created.'.format(name) - ret['changes']['new'] = 'Dashboard {0} created.'.format(name) + response = __salt__["grafana4.create_update_dashboard"]( + dashboard=new_dashboard, overwrite=True, profile=profile + ) + if response.get("status") == "success": + ret["comment"] = "Dashboard {0} created.".format(name) + ret["changes"]["new"] = "Dashboard {0} created.".format(name) else: - ret['result'] = False - ret['comment'] = ("Failed to create dashboard {0}, " - "response={1}").format(name, response) + ret["result"] = False + ret["comment"] = ("Failed to create dashboard {0}, " "response={1}").format( + name, response + ) return ret # Add unmanaged rows to the dashboard. They appear at the top if they are # marked as pinned. They appear at the bottom otherwise. - managed_row_titles = [row.get('title') - for row in new_dashboard.get('rows', [])] - new_rows = new_dashboard.get('rows', []) - for old_row in old_dashboard.get('rows', []): - if old_row.get('title') not in managed_row_titles: + managed_row_titles = [row.get("title") for row in new_dashboard.get("rows", [])] + new_rows = new_dashboard.get("rows", []) + for old_row in old_dashboard.get("rows", []): + if old_row.get("title") not in managed_row_titles: new_rows.append(copy.deepcopy(old_row)) _ensure_pinned_rows(new_dashboard) _ensure_panel_ids(new_dashboard) # Update dashboard if it differs - dashboard_diff = DictDiffer(_cleaned(new_dashboard), - _cleaned(old_dashboard)) - updated_needed = (dashboard_diff.changed() or - dashboard_diff.added() or - dashboard_diff.removed()) + dashboard_diff = DictDiffer(_cleaned(new_dashboard), _cleaned(old_dashboard)) + updated_needed = ( + dashboard_diff.changed() or dashboard_diff.added() or dashboard_diff.removed() + ) if updated_needed: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ( - str('Dashboard {0} is set to be updated, changes={1}').format( # future lint: blacklisted-function - name, - salt.utils.json.dumps( - _dashboard_diff( - _cleaned(new_dashboard), - _cleaned(old_dashboard) - ), - indent=4 - ) - ) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = str( + "Dashboard {0} is set to be updated, changes={1}" + ).format( # future lint: blacklisted-function + name, + salt.utils.json.dumps( + _dashboard_diff(_cleaned(new_dashboard), _cleaned(old_dashboard)), + indent=4, + ), ) return ret - response = __salt__['grafana4.create_update_dashboard']( - dashboard=new_dashboard, overwrite=True, profile=profile) - if response.get('status') == 'success': - updated_dashboard = __salt__['grafana4.get_dashboard']( - name, orgname, profile) - dashboard_diff = DictDiffer(_cleaned(updated_dashboard), - _cleaned(old_dashboard)) - ret['comment'] = 'Dashboard {0} updated.'.format(name) - ret['changes'] = _dashboard_diff(_cleaned(new_dashboard), - _cleaned(old_dashboard)) + response = __salt__["grafana4.create_update_dashboard"]( + dashboard=new_dashboard, overwrite=True, profile=profile + ) + if response.get("status") == "success": + updated_dashboard = __salt__["grafana4.get_dashboard"]( + name, orgname, profile + ) + dashboard_diff = DictDiffer( + _cleaned(updated_dashboard), _cleaned(old_dashboard) + ) + ret["comment"] = "Dashboard {0} updated.".format(name) + ret["changes"] = _dashboard_diff( + _cleaned(new_dashboard), _cleaned(old_dashboard) + ) else: - ret['result'] = False - ret['comment'] = ("Failed to update dashboard {0}, " - "response={1}").format(name, response) + ret["result"] = False + ret["comment"] = ("Failed to update dashboard {0}, " "response={1}").format( + name, response + ) return ret - ret['comment'] = 'Dashboard present' + ret["comment"] = "Dashboard present" return ret -def absent(name, orgname=None, profile='grafana'): - ''' +def absent(name, orgname=None, profile="grafana"): + """ Ensure the named grafana dashboard is absent. name @@ -222,103 +227,104 @@ def absent(name, orgname=None, profile='grafana'): profile Configuration profile used to connect to the Grafana instance. Default is 'grafana'. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if isinstance(profile, six.string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - existing_dashboard = __salt__['grafana4.get_dashboard']( - name, orgname, profile) + existing_dashboard = __salt__["grafana4.get_dashboard"](name, orgname, profile) if existing_dashboard: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Dashboard {0} is set to be deleted.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Dashboard {0} is set to be deleted.".format(name) return ret - __salt__['grafana4.delete_dashboard'](name, profile=profile) - ret['comment'] = 'Dashboard {0} deleted.'.format(name) - ret['changes']['new'] = 'Dashboard {0} deleted.'.format(name) + __salt__["grafana4.delete_dashboard"](name, profile=profile) + ret["comment"] = "Dashboard {0} deleted.".format(name) + ret["changes"]["new"] = "Dashboard {0} deleted.".format(name) return ret - ret['comment'] = 'Dashboard absent' + ret["comment"] = "Dashboard absent" return ret _IGNORED_DASHBOARD_FIELDS = [ - 'id', - 'uid', - 'originalTitle', - 'version', + "id", + "uid", + "originalTitle", + "version", ] _IGNORED_ROW_FIELDS = [] _IGNORED_PANEL_FIELDS = [ - 'grid', - 'mode', - 'tooltip', + "grid", + "mode", + "tooltip", ] _IGNORED_TARGET_FIELDS = [ - 'textEditor', + "textEditor", ] def _cleaned(_dashboard): - '''Return a copy without fields that can differ.''' + """Return a copy without fields that can differ.""" dashboard = copy.deepcopy(_dashboard) for ignored_dashboard_field in _IGNORED_DASHBOARD_FIELDS: dashboard.pop(ignored_dashboard_field, None) - for row in dashboard.get('rows', []): + for row in dashboard.get("rows", []): for ignored_row_field in _IGNORED_ROW_FIELDS: row.pop(ignored_row_field, None) - for i, panel in enumerate(row.get('panels', [])): + for i, panel in enumerate(row.get("panels", [])): for ignored_panel_field in _IGNORED_PANEL_FIELDS: panel.pop(ignored_panel_field, None) - for target in panel.get('targets', []): + for target in panel.get("targets", []): for ignored_target_field in _IGNORED_TARGET_FIELDS: target.pop(ignored_target_field, None) - row['panels'][i] = _stripped(panel) + row["panels"][i] = _stripped(panel) return dashboard def _inherited_dashboard(dashboard, base_dashboards_from_pillar, ret): - '''Return a dashboard with properties from parents.''' + """Return a dashboard with properties from parents.""" base_dashboards = [] for base_dashboard_from_pillar in base_dashboards_from_pillar: - base_dashboard = __salt__['pillar.get'](base_dashboard_from_pillar) + base_dashboard = __salt__["pillar.get"](base_dashboard_from_pillar) if base_dashboard: base_dashboards.append(base_dashboard) elif base_dashboard_from_pillar != _DEFAULT_DASHBOARD_PILLAR: - ret.setdefault('warnings', []) + ret.setdefault("warnings", []) warning_message = 'Cannot find dashboard pillar "{0}".'.format( - base_dashboard_from_pillar) - if warning_message not in ret['warnings']: - ret['warnings'].append(warning_message) + base_dashboard_from_pillar + ) + if warning_message not in ret["warnings"]: + ret["warnings"].append(warning_message) base_dashboards.append(dashboard) result_dashboard = {} tags = set() for dashboard in base_dashboards: - tags.update(dashboard.get('tags', [])) + tags.update(dashboard.get("tags", [])) result_dashboard.update(dashboard) - result_dashboard['tags'] = list(tags) + result_dashboard["tags"] = list(tags) return result_dashboard def _inherited_row(row, base_rows_from_pillar, ret): - '''Return a row with properties from parents.''' + """Return a row with properties from parents.""" base_rows = [] for base_row_from_pillar in base_rows_from_pillar: - base_row = __salt__['pillar.get'](base_row_from_pillar) + base_row = __salt__["pillar.get"](base_row_from_pillar) if base_row: base_rows.append(base_row) elif base_row_from_pillar != _DEFAULT_ROW_PILLAR: - ret.setdefault('warnings', []) + ret.setdefault("warnings", []) warning_message = 'Cannot find row pillar "{0}".'.format( - base_row_from_pillar) - if warning_message not in ret['warnings']: - ret['warnings'].append(warning_message) + base_row_from_pillar + ) + if warning_message not in ret["warnings"]: + ret["warnings"].append(warning_message) base_rows.append(row) result_row = {} @@ -328,18 +334,19 @@ def _inherited_row(row, base_rows_from_pillar, ret): def _inherited_panel(panel, base_panels_from_pillar, ret): - '''Return a panel with properties from parents.''' + """Return a panel with properties from parents.""" base_panels = [] for base_panel_from_pillar in base_panels_from_pillar: - base_panel = __salt__['pillar.get'](base_panel_from_pillar) + base_panel = __salt__["pillar.get"](base_panel_from_pillar) if base_panel: base_panels.append(base_panel) elif base_panel_from_pillar != _DEFAULT_PANEL_PILLAR: - ret.setdefault('warnings', []) + ret.setdefault("warnings", []) warning_message = 'Cannot find panel pillar "{0}".'.format( - base_panel_from_pillar) - if warning_message not in ret['warnings']: - ret['warnings'].append(warning_message) + base_panel_from_pillar + ) + if warning_message not in ret["warnings"]: + ret["warnings"].append(warning_message) base_panels.append(panel) result_panel = {} @@ -353,19 +360,20 @@ _DEFAULT_PANEL_SPAN = 2.5 def _auto_adjust_panel_spans(dashboard): - '''Adjust panel spans to take up the available width. + """Adjust panel spans to take up the available width. For each group of panels that would be laid out on the same level, scale up the unspecified panel spans to fill up the level. - ''' - for row in dashboard.get('rows', []): + """ + for row in dashboard.get("rows", []): levels = [] current_level = [] levels.append(current_level) - for panel in row.get('panels', []): - current_level_span = sum(panel.get('span', _DEFAULT_PANEL_SPAN) - for panel in current_level) - span = panel.get('span', _DEFAULT_PANEL_SPAN) + for panel in row.get("panels", []): + current_level_span = sum( + panel.get("span", _DEFAULT_PANEL_SPAN) for panel in current_level + ) + span = panel.get("span", _DEFAULT_PANEL_SPAN) if current_level_span + span > _FULL_LEVEL_SPAN: current_level = [panel] levels.append(current_level) @@ -373,143 +381,154 @@ def _auto_adjust_panel_spans(dashboard): current_level.append(panel) for level in levels: - specified_panels = [panel for panel in level if 'span' in panel] - unspecified_panels = [panel for panel in level - if 'span' not in panel] + specified_panels = [panel for panel in level if "span" in panel] + unspecified_panels = [panel for panel in level if "span" not in panel] if not unspecified_panels: continue - specified_span = sum(panel['span'] for panel in specified_panels) + specified_span = sum(panel["span"] for panel in specified_panels) available_span = _FULL_LEVEL_SPAN - specified_span auto_span = float(available_span) / len(unspecified_panels) for panel in unspecified_panels: - panel['span'] = auto_span + panel["span"] = auto_span def _ensure_pinned_rows(dashboard): - '''Pin rows to the top of the dashboard.''' - pinned_row_titles = __salt__['pillar.get'](_PINNED_ROWS_PILLAR) + """Pin rows to the top of the dashboard.""" + pinned_row_titles = __salt__["pillar.get"](_PINNED_ROWS_PILLAR) if not pinned_row_titles: return pinned_row_titles_lower = [] for title in pinned_row_titles: pinned_row_titles_lower.append(title.lower()) - rows = dashboard.get('rows', []) + rows = dashboard.get("rows", []) pinned_rows = [] for i, row in enumerate(rows): - if row.get('title', '').lower() in pinned_row_titles_lower: + if row.get("title", "").lower() in pinned_row_titles_lower: del rows[i] pinned_rows.append(row) rows = pinned_rows + rows def _ensure_panel_ids(dashboard): - '''Assign panels auto-incrementing IDs.''' + """Assign panels auto-incrementing IDs.""" panel_id = 1 - for row in dashboard.get('rows', []): - for panel in row.get('panels', []): - panel['id'] = panel_id + for row in dashboard.get("rows", []): + for panel in row.get("panels", []): + panel["id"] = panel_id panel_id += 1 def _ensure_annotations(dashboard): - '''Explode annotation_tags into annotations.''' - if 'annotation_tags' not in dashboard: + """Explode annotation_tags into annotations.""" + if "annotation_tags" not in dashboard: return - tags = dashboard['annotation_tags'] + tags = dashboard["annotation_tags"] annotations = { - 'enable': True, - 'list': [], + "enable": True, + "list": [], } for tag in tags: - annotations['list'].append({ - 'datasource': "graphite", - 'enable': False, - 'iconColor': "#C0C6BE", - 'iconSize': 13, - 'lineColor': "rgba(255, 96, 96, 0.592157)", - 'name': tag, - 'showLine': True, - 'tags': tag, - }) - del dashboard['annotation_tags'] - dashboard['annotations'] = annotations + annotations["list"].append( + { + "datasource": "graphite", + "enable": False, + "iconColor": "#C0C6BE", + "iconSize": 13, + "lineColor": "rgba(255, 96, 96, 0.592157)", + "name": tag, + "showLine": True, + "tags": tag, + } + ) + del dashboard["annotation_tags"] + dashboard["annotations"] = annotations def _dashboard_diff(_new_dashboard, _old_dashboard): - '''Return a dictionary of changes between dashboards.''' + """Return a dictionary of changes between dashboards.""" diff = {} # Dashboard diff new_dashboard = copy.deepcopy(_new_dashboard) old_dashboard = copy.deepcopy(_old_dashboard) dashboard_diff = DictDiffer(new_dashboard, old_dashboard) - diff['dashboard'] = _stripped({ - 'changed': list(dashboard_diff.changed()) or None, - 'added': list(dashboard_diff.added()) or None, - 'removed': list(dashboard_diff.removed()) or None, - }) + diff["dashboard"] = _stripped( + { + "changed": list(dashboard_diff.changed()) or None, + "added": list(dashboard_diff.added()) or None, + "removed": list(dashboard_diff.removed()) or None, + } + ) # Row diff - new_rows = new_dashboard.get('rows', []) - old_rows = old_dashboard.get('rows', []) + new_rows = new_dashboard.get("rows", []) + old_rows = old_dashboard.get("rows", []) new_rows_by_title = {} old_rows_by_title = {} for row in new_rows: - if 'title' in row: - new_rows_by_title[row['title']] = row + if "title" in row: + new_rows_by_title[row["title"]] = row for row in old_rows: - if 'title' in row: - old_rows_by_title[row['title']] = row + if "title" in row: + old_rows_by_title[row["title"]] = row rows_diff = DictDiffer(new_rows_by_title, old_rows_by_title) - diff['rows'] = _stripped({ - 'added': list(rows_diff.added()) or None, - 'removed': list(rows_diff.removed()) or None, - }) + diff["rows"] = _stripped( + { + "added": list(rows_diff.added()) or None, + "removed": list(rows_diff.removed()) or None, + } + ) for changed_row_title in rows_diff.changed(): old_row = old_rows_by_title[changed_row_title] new_row = new_rows_by_title[changed_row_title] row_diff = DictDiffer(new_row, old_row) - diff['rows'].setdefault('changed', {}) - diff['rows']['changed'][changed_row_title] = _stripped({ - 'changed': list(row_diff.changed()) or None, - 'added': list(row_diff.added()) or None, - 'removed': list(row_diff.removed()) or None, - }) + diff["rows"].setdefault("changed", {}) + diff["rows"]["changed"][changed_row_title] = _stripped( + { + "changed": list(row_diff.changed()) or None, + "added": list(row_diff.added()) or None, + "removed": list(row_diff.removed()) or None, + } + ) # Panel diff old_panels_by_id = {} new_panels_by_id = {} - for row in old_dashboard.get('rows', []): - for panel in row.get('panels', []): - if 'id' in panel: - old_panels_by_id[panel['id']] = panel - for row in new_dashboard.get('rows', []): - for panel in row.get('panels', []): - if 'id' in panel: - new_panels_by_id[panel['id']] = panel + for row in old_dashboard.get("rows", []): + for panel in row.get("panels", []): + if "id" in panel: + old_panels_by_id[panel["id"]] = panel + for row in new_dashboard.get("rows", []): + for panel in row.get("panels", []): + if "id" in panel: + new_panels_by_id[panel["id"]] = panel panels_diff = DictDiffer(new_panels_by_id, old_panels_by_id) - diff['panels'] = _stripped({ - 'added': list(panels_diff.added()) or None, - 'removed': list(panels_diff.removed()) or None, - }) + diff["panels"] = _stripped( + { + "added": list(panels_diff.added()) or None, + "removed": list(panels_diff.removed()) or None, + } + ) for changed_panel_id in panels_diff.changed(): old_panel = old_panels_by_id[changed_panel_id] new_panel = new_panels_by_id[changed_panel_id] panels_diff = DictDiffer(new_panel, old_panel) - diff['panels'].setdefault('changed', {}) - diff['panels']['changed'][changed_panel_id] = _stripped({ - 'changed': list(panels_diff.changed()) or None, - 'added': list(panels_diff.added()) or None, - 'removed': list(panels_diff.removed()) or None, - }) + diff["panels"].setdefault("changed", {}) + diff["panels"]["changed"][changed_panel_id] = _stripped( + { + "changed": list(panels_diff.changed()) or None, + "added": list(panels_diff.added()) or None, + "removed": list(panels_diff.removed()) or None, + } + ) return diff def _stripped(d): - '''Strip falsey entries.''' + """Strip falsey entries.""" ret = {} for k, v in six.iteritems(d): if v: diff --git a/salt/states/grafana4_datasource.py b/salt/states/grafana4_datasource.py index a5d66a233a2..7d7d0df88e6 100644 --- a/salt/states/grafana4_datasource.py +++ b/salt/states/grafana4_datasource.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Grafana v4.0 data sources .. versionadded:: 2017.7.0 @@ -42,7 +42,7 @@ to update data sources if the already exists. - basic_auth_user: myuser - basic_auth_password: mypass - is_default: true -''' +""" from __future__ import absolute_import, print_function, unicode_literals from salt.ext.six import string_types @@ -50,28 +50,32 @@ from salt.utils.dictdiffer import deep_diff def __virtual__(): - '''Only load if grafana4 module is available''' - return 'grafana4.get_datasource' in __salt__ + """Only load if grafana4 module is available""" + if "grafana4.get_datasource" in __salt__: + return True + return (False, "grafana4 module could not be loaded") -def present(name, - type, - url, - access=None, - user=None, - password=None, - database=None, - basic_auth=None, - basic_auth_user=None, - basic_auth_password=None, - tls_auth=None, - json_data=None, - is_default=None, - with_credentials=None, - type_logo_url=None, - orgname=None, - profile='grafana'): - ''' +def present( + name, + type, + url, + access=None, + user=None, + password=None, + database=None, + basic_auth=None, + basic_auth_user=None, + basic_auth_password=None, + tls_auth=None, + json_data=None, + is_default=None, + with_credentials=None, + type_logo_url=None, + orgname=None, + profile="grafana", +): + """ Ensure that a data source is present. name @@ -124,12 +128,12 @@ def present(name, profile Configuration profile used to connect to the Grafana instance. Default is 'grafana'. - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} - datasource = __salt__['grafana4.get_datasource'](name, orgname, profile) + ret = {"name": name, "result": None, "comment": None, "changes": {}} + datasource = __salt__["grafana4.get_datasource"](name, orgname, profile) data = _get_json_data( name=name, type=type, @@ -146,17 +150,18 @@ def present(name, isDefault=is_default, withCredentials=with_credentials, typeLogoUrl=type_logo_url, - defaults=datasource) + defaults=datasource, + ) if not datasource: - if __opts__['test']: - ret['comment'] = 'Datasource {0} will be created'.format(name) + if __opts__["test"]: + ret["comment"] = "Datasource {0} will be created".format(name) return ret - __salt__['grafana4.create_datasource'](profile=profile, **data) - datasource = __salt__['grafana4.get_datasource'](name, profile=profile) - ret['result'] = True - ret['comment'] = 'New data source {0} added'.format(name) - ret['changes'] = data + __salt__["grafana4.create_datasource"](profile=profile, **data) + datasource = __salt__["grafana4.get_datasource"](name, profile=profile) + ret["result"] = True + ret["comment"] = "New data source {0} added".format(name) + ret["changes"] = data return ret # At this stage, the datasource exists; however, the object provided by @@ -166,22 +171,21 @@ def present(name, datasource[key] = None if data == datasource: - ret['comment'] = 'Data source {0} already up-to-date'.format(name) + ret["comment"] = "Data source {0} already up-to-date".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Datasource {0} will be updated'.format(name) + if __opts__["test"]: + ret["comment"] = "Datasource {0} will be updated".format(name) return ret - __salt__['grafana4.update_datasource']( - datasource['id'], profile=profile, **data) - ret['result'] = True - ret['changes'] = deep_diff(datasource, data, ignore=['id', 'orgId', 'readOnly']) - ret['comment'] = 'Data source {0} updated'.format(name) + __salt__["grafana4.update_datasource"](datasource["id"], profile=profile, **data) + ret["result"] = True + ret["changes"] = deep_diff(datasource, data, ignore=["id", "orgId", "readOnly"]) + ret["comment"] = "Data source {0} updated".format(name) return ret -def absent(name, orgname=None, profile='grafana'): - ''' +def absent(name, orgname=None, profile="grafana"): + """ Ensure that a data source is present. name @@ -193,26 +197,26 @@ def absent(name, orgname=None, profile='grafana'): profile Configuration profile used to connect to the Grafana instance. Default is 'grafana'. - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} - datasource = __salt__['grafana4.get_datasource'](name, orgname, profile) + ret = {"name": name, "result": None, "comment": None, "changes": {}} + datasource = __salt__["grafana4.get_datasource"](name, orgname, profile) if not datasource: - ret['result'] = True - ret['comment'] = 'Data source {0} already absent'.format(name) + ret["result"] = True + ret["comment"] = "Data source {0} already absent".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Datasource {0} will be deleted'.format(name) + if __opts__["test"]: + ret["comment"] = "Datasource {0} will be deleted".format(name) return ret - __salt__['grafana4.delete_datasource'](datasource['id'], profile=profile) + __salt__["grafana4.delete_datasource"](datasource["id"], profile=profile) - ret['result'] = True - ret['changes'][name] = 'Absent' - ret['comment'] = 'Data source {0} was deleted'.format(name) + ret["result"] = True + ret["changes"][name] = "Absent" + ret["comment"] = "Data source {0} was deleted".format(name) return ret diff --git a/salt/states/grafana4_org.py b/salt/states/grafana4_org.py index 476e9e55a2c..bb48e953358 100644 --- a/salt/states/grafana4_org.py +++ b/salt/states/grafana4_org.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Grafana v4.0 orgs .. versionadded:: 2017.7.0 @@ -41,35 +41,39 @@ Manage Grafana v4.0 orgs - zip_code: "" - state: "" - country: "" -''' +""" from __future__ import absolute_import, print_function, unicode_literals import salt.utils.dictupdate as dictupdate -from salt.utils.dictdiffer import deep_diff from requests.exceptions import HTTPError # Import 3rd-party libs from salt.ext.six import string_types +from salt.utils.dictdiffer import deep_diff def __virtual__(): - '''Only load if grafana4 module is available''' - return 'grafana4.get_org' in __salt__ + """Only load if grafana4 module is available""" + if "grafana4.get_org" in __salt__: + return True + return (False, "grafana4 module could not be loaded") -def present(name, - users=None, - theme=None, - home_dashboard_id=None, - timezone=None, - address1=None, - address2=None, - city=None, - zip_code=None, - address_state=None, - country=None, - profile='grafana'): - ''' +def present( + name, + users=None, + theme=None, + home_dashboard_id=None, + timezone=None, + address1=None, + address2=None, + city=None, + zip_code=None, + address_state=None, + country=None, + profile="grafana", +): + """ Ensure that an organization is present. name @@ -114,14 +118,14 @@ def present(name, profile Configuration profile used to connect to the Grafana instance. Default is 'grafana'. - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} + ret = {"name": name, "result": None, "comment": None, "changes": {}} create = False try: - org = __salt__['grafana4.get_org'](name, profile) + org = __salt__["grafana4.get_org"](name, profile) except HTTPError as e: if e.response.status_code == 404: create = True @@ -129,96 +133,115 @@ def present(name, raise if create: - if __opts__['test']: - ret['comment'] = 'Org {0} will be created'.format(name) + if __opts__["test"]: + ret["comment"] = "Org {0} will be created".format(name) return ret - __salt__['grafana4.create_org'](profile=profile, name=name) - org = __salt__['grafana4.get_org'](name, profile) - ret['changes'] = org - ret['comment'] = 'New org {0} added'.format(name) + __salt__["grafana4.create_org"](profile=profile, name=name) + org = __salt__["grafana4.get_org"](name, profile) + ret["changes"] = org + ret["comment"] = "New org {0} added".format(name) - data = _get_json_data(address1=address1, address2=address2, - city=city, zipCode=zip_code, state=address_state, country=country, - defaults=org['address']) - if data != org['address']: - if __opts__['test']: - ret['comment'] = 'Org {0} address will be updated'.format(name) + data = _get_json_data( + address1=address1, + address2=address2, + city=city, + zipCode=zip_code, + state=address_state, + country=country, + defaults=org["address"], + ) + if data != org["address"]: + if __opts__["test"]: + ret["comment"] = "Org {0} address will be updated".format(name) return ret - __salt__['grafana4.update_org_address'](name, profile=profile, **data) + __salt__["grafana4.update_org_address"](name, profile=profile, **data) if create: - dictupdate.update(ret['changes']['address'], data) + dictupdate.update(ret["changes"]["address"], data) else: - dictupdate.update(ret['changes'], deep_diff(org['address'], data)) + dictupdate.update(ret["changes"], deep_diff(org["address"], data)) - prefs = __salt__['grafana4.get_org_prefs'](name, profile=profile) - data = _get_json_data(theme=theme, homeDashboardId=home_dashboard_id, - timezone=timezone, defaults=prefs) + prefs = __salt__["grafana4.get_org_prefs"](name, profile=profile) + data = _get_json_data( + theme=theme, + homeDashboardId=home_dashboard_id, + timezone=timezone, + defaults=prefs, + ) if data != prefs: - if __opts__['test']: - ret['comment'] = 'Org {0} prefs will be updated'.format(name) + if __opts__["test"]: + ret["comment"] = "Org {0} prefs will be updated".format(name) return ret - __salt__['grafana4.update_org_prefs'](name, profile=profile, **data) + __salt__["grafana4.update_org_prefs"](name, profile=profile, **data) if create: - dictupdate.update(ret['changes'], data) + dictupdate.update(ret["changes"], data) else: - dictupdate.update(ret['changes'], deep_diff(prefs, data)) + dictupdate.update(ret["changes"], deep_diff(prefs, data)) if users: db_users = {} - for item in __salt__['grafana4.get_org_users'](name, profile=profile): - db_users[item['login']] = { - 'userId': item['userId'], - 'role': item['role'], - } + for item in __salt__["grafana4.get_org_users"](name, profile=profile): + db_users[item["login"]] = { + "userId": item["userId"], + "role": item["role"], + } for username, role in users.items(): if username in db_users: if role is False: - if __opts__['test']: - ret['comment'] = 'Org {0} user {1} will be ' \ - 'deleted'.format(name, username) + if __opts__["test"]: + ret["comment"] = "Org {0} user {1} will be " "deleted".format( + name, username + ) return ret - __salt__['grafana4.delete_org_user']( - db_users[username]['userId'], profile=profile) - elif role != db_users[username]['role']: - if __opts__['test']: - ret['comment'] = 'Org {0} user {1} role will be ' \ - 'updated'.format(name, username) + __salt__["grafana4.delete_org_user"]( + db_users[username]["userId"], profile=profile + ) + elif role != db_users[username]["role"]: + if __opts__["test"]: + ret["comment"] = ( + "Org {0} user {1} role will be " + "updated".format(name, username) + ) return ret - __salt__['grafana4.update_org_user']( - db_users[username]['userId'], loginOrEmail=username, - role=role, profile=profile) + __salt__["grafana4.update_org_user"]( + db_users[username]["userId"], + loginOrEmail=username, + role=role, + profile=profile, + ) elif role: - if __opts__['test']: - ret['comment'] = 'Org {0} user {1} will be created'.format( - name, username) + if __opts__["test"]: + ret["comment"] = "Org {0} user {1} will be created".format( + name, username + ) return ret - __salt__['grafana4.create_org_user']( - loginOrEmail=username, role=role, profile=profile) + __salt__["grafana4.create_org_user"]( + loginOrEmail=username, role=role, profile=profile + ) new_db_users = {} - for item in __salt__['grafana4.get_org_users'](name, profile=profile): - new_db_users[item['login']] = { - 'userId': item['userId'], - 'role': item['role'], - } + for item in __salt__["grafana4.get_org_users"](name, profile=profile): + new_db_users[item["login"]] = { + "userId": item["userId"], + "role": item["role"], + } if create: - dictupdate.update(ret['changes'], new_db_users) + dictupdate.update(ret["changes"], new_db_users) else: - dictupdate.update(ret['changes'], deep_diff(db_users, new_db_users)) + dictupdate.update(ret["changes"], deep_diff(db_users, new_db_users)) - ret['result'] = True + ret["result"] = True if not create: - if ret['changes']: - ret['comment'] = 'Org {0} updated'.format(name) + if ret["changes"]: + ret["comment"] = "Org {0} updated".format(name) else: - ret['changes'] = {} - ret['comment'] = 'Org {0} already up-to-date'.format(name) + ret["changes"] = {} + ret["comment"] = "Org {0} already up-to-date".format(name) return ret -def absent(name, profile='grafana'): - ''' +def absent(name, profile="grafana"): + """ Ensure that a org is present. name @@ -227,26 +250,26 @@ def absent(name, profile='grafana'): profile Configuration profile used to connect to the Grafana instance. Default is 'grafana'. - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} - org = __salt__['grafana4.get_org'](name, profile) + ret = {"name": name, "result": None, "comment": None, "changes": {}} + org = __salt__["grafana4.get_org"](name, profile) if not org: - ret['result'] = True - ret['comment'] = 'Org {0} already absent'.format(name) + ret["result"] = True + ret["comment"] = "Org {0} already absent".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Org {0} will be deleted'.format(name) + if __opts__["test"]: + ret["comment"] = "Org {0} will be deleted".format(name) return ret - __salt__['grafana4.delete_org'](org['id'], profile=profile) + __salt__["grafana4.delete_org"](org["id"], profile=profile) - ret['result'] = True - ret['changes'][name] = 'Absent' - ret['comment'] = 'Org {0} was deleted'.format(name) + ret["result"] = True + ret["changes"][name] = "Absent" + ret["comment"] = "Org {0} was deleted".format(name) return ret diff --git a/salt/states/grafana4_user.py b/salt/states/grafana4_user.py index ee53262ea77..6e444daace4 100644 --- a/salt/states/grafana4_user.py +++ b/salt/states/grafana4_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Grafana v4.0 users .. versionadded:: 2017.7.0 @@ -36,29 +36,27 @@ Manage Grafana v4.0 users - email: "foobar@localhost" - fullname: Foo Bar - is_admin: true -''' +""" from __future__ import absolute_import, print_function, unicode_literals import salt.utils.dictupdate as dictupdate -from salt.utils.dictdiffer import deep_diff # Import 3rd-party libs from salt.ext.six import string_types +from salt.utils.dictdiffer import deep_diff def __virtual__(): - '''Only load if grafana4 module is available''' - return 'grafana4.get_user' in __salt__ + """Only load if grafana4 module is available""" + if "grafana4.get_user" in __salt__: + return True + return (False, "grafana4 module could not be loaded") -def present(name, - password, - email, - is_admin=False, - fullname=None, - theme=None, - profile='grafana'): - ''' +def present( + name, password, email, is_admin=False, fullname=None, theme=None, profile="grafana" +): + """ Ensure that a user is present. name @@ -82,66 +80,68 @@ def present(name, profile Configuration profile used to connect to the Grafana instance. Default is 'grafana'. - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} - user = __salt__['grafana4.get_user'](name, profile) + ret = {"name": name, "result": None, "comment": None, "changes": {}} + user = __salt__["grafana4.get_user"](name, profile) create = not user if create: - if __opts__['test']: - ret['comment'] = 'User {0} will be created'.format(name) + if __opts__["test"]: + ret["comment"] = "User {0} will be created".format(name) return ret - __salt__['grafana4.create_user']( - login=name, - password=password, - email=email, - name=fullname, - profile=profile) - user = __salt__['grafana4.get_user'](name, profile) - ret['changes']['new'] = user + __salt__["grafana4.create_user"]( + login=name, password=password, email=email, name=fullname, profile=profile + ) + user = __salt__["grafana4.get_user"](name, profile) + ret["changes"]["new"] = user - user_data = __salt__['grafana4.get_user_data'](user['id'], profile=profile) - data = _get_json_data(login=name, email=email, name=fullname, theme=theme, - defaults=user_data) - if data != _get_json_data(login=None, email=None, name=None, theme=None, - defaults=user_data): - if __opts__['test']: - ret['comment'] = 'User {0} will be updated'.format(name) + user_data = __salt__["grafana4.get_user_data"](user["id"], profile=profile) + data = _get_json_data( + login=name, email=email, name=fullname, theme=theme, defaults=user_data + ) + if data != _get_json_data( + login=None, email=None, name=None, theme=None, defaults=user_data + ): + if __opts__["test"]: + ret["comment"] = "User {0} will be updated".format(name) return ret - __salt__['grafana4.update_user'](user['id'], profile=profile, **data) + __salt__["grafana4.update_user"](user["id"], profile=profile, **data) dictupdate.update( - ret['changes'], deep_diff( - user_data, __salt__['grafana4.get_user_data'](user['id']))) + ret["changes"], + deep_diff(user_data, __salt__["grafana4.get_user_data"](user["id"])), + ) - if user['isAdmin'] != is_admin: - if __opts__['test']: - ret['comment'] = 'User {0} isAdmin status will be updated'.format( - name) + if user["isAdmin"] != is_admin: + if __opts__["test"]: + ret["comment"] = "User {0} isAdmin status will be updated".format(name) return ret - __salt__['grafana4.update_user_permissions']( - user['id'], isGrafanaAdmin=is_admin, profile=profile) - dictupdate.update(ret['changes'], deep_diff( - user, __salt__['grafana4.get_user'](name, profile))) + __salt__["grafana4.update_user_permissions"]( + user["id"], isGrafanaAdmin=is_admin, profile=profile + ) + dictupdate.update( + ret["changes"], + deep_diff(user, __salt__["grafana4.get_user"](name, profile)), + ) - ret['result'] = True + ret["result"] = True if create: - ret['changes'] = ret['changes']['new'] - ret['comment'] = 'New user {0} added'.format(name) + ret["changes"] = ret["changes"]["new"] + ret["comment"] = "New user {0} added".format(name) else: - if ret['changes']: - ret['comment'] = 'User {0} updated'.format(name) + if ret["changes"]: + ret["comment"] = "User {0} updated".format(name) else: - ret['changes'] = {} - ret['comment'] = 'User {0} already up-to-date'.format(name) + ret["changes"] = {} + ret["comment"] = "User {0} already up-to-date".format(name) return ret -def absent(name, profile='grafana'): - ''' +def absent(name, profile="grafana"): + """ Ensure that a user is present. name @@ -150,37 +150,38 @@ def absent(name, profile='grafana'): profile Configuration profile used to connect to the Grafana instance. Default is 'grafana'. - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} - user = __salt__['grafana4.get_user'](name, profile) + ret = {"name": name, "result": None, "comment": None, "changes": {}} + user = __salt__["grafana4.get_user"](name, profile) if user: - if __opts__['test']: - ret['comment'] = 'User {0} will be deleted'.format(name) + if __opts__["test"]: + ret["comment"] = "User {0} will be deleted".format(name) return ret - orgs = __salt__['grafana4.get_user_orgs'](user['id'], profile=profile) - __salt__['grafana4.delete_user'](user['id'], profile=profile) + orgs = __salt__["grafana4.get_user_orgs"](user["id"], profile=profile) + __salt__["grafana4.delete_user"](user["id"], profile=profile) for org in orgs: - if org['name'] == user['email']: + if org["name"] == user["email"]: # Remove entire Org in the case where auto_assign_org=false: # When set to false, new users will automatically cause a new # organization to be created for that new user (the org name # will be the email) - __salt__['grafana4.delete_org'](org['orgId'], profile=profile) + __salt__["grafana4.delete_org"](org["orgId"], profile=profile) else: - __salt__['grafana4.delete_user_org']( - user['id'], org['orgId'], profile=profile) + __salt__["grafana4.delete_user_org"]( + user["id"], org["orgId"], profile=profile + ) else: - ret['result'] = True - ret['comment'] = 'User {0} already absent'.format(name) + ret["result"] = True + ret["comment"] = "User {0} already absent".format(name) return ret - ret['result'] = True - ret['changes'][name] = 'Absent' - ret['comment'] = 'User {0} was deleted'.format(name) + ret["result"] = True + ret["changes"][name] = "Absent" + ret["comment"] = "User {0} was deleted".format(name) return ret diff --git a/salt/states/grafana_dashboard.py b/salt/states/grafana_dashboard.py index a6a96207324..09e0fde7a16 100644 --- a/salt/states/grafana_dashboard.py +++ b/salt/states/grafana_dashboard.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Grafana v2.0 Dashboards .. versionadded:: 2016.3.0 @@ -36,11 +36,13 @@ The behavior of this module is to create dashboards if they do not exist, to add rows if they do not exist in existing dashboards, and to update rows if they exist in dashboards. The module will not manage rows that are not defined, allowing users to manage their own custom rows. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy + import requests # Import Salt libs @@ -50,23 +52,29 @@ from salt.utils.dictdiffer import DictDiffer def __virtual__(): - '''Only load if grafana v2.0 is configured.''' - return __salt__['config.get']('grafana_version', 1) == 2 + """ + Only load if grafana v2.0 is configured. + """ + if __salt__["config.get"]("grafana_version", 1) == 2: + return True + return (False, "Not configured for grafana_version 2") -_DEFAULT_DASHBOARD_PILLAR = 'grafana_dashboards:default' -_DEFAULT_PANEL_PILLAR = 'grafana_panels:default' -_DEFAULT_ROW_PILLAR = 'grafana_rows:default' -_PINNED_ROWS_PILLAR = 'grafana_pinned_rows' +_DEFAULT_DASHBOARD_PILLAR = "grafana_dashboards:default" +_DEFAULT_PANEL_PILLAR = "grafana_panels:default" +_DEFAULT_ROW_PILLAR = "grafana_rows:default" +_PINNED_ROWS_PILLAR = "grafana_pinned_rows" -def present(name, - base_dashboards_from_pillar=None, - base_panels_from_pillar=None, - base_rows_from_pillar=None, - dashboard=None, - profile='grafana'): - ''' +def present( + name, + base_dashboards_from_pillar=None, + base_panels_from_pillar=None, + base_rows_from_pillar=None, + dashboard=None, + profile="grafana", +): + """ Ensure the grafana dashboard exists and is managed. name @@ -86,8 +94,8 @@ def present(name, profile A pillar key or dict that contains grafana information - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} base_dashboards_from_pillar = base_dashboards_from_pillar or [] base_panels_from_pillar = base_panels_from_pillar or [] @@ -95,24 +103,23 @@ def present(name, dashboard = dashboard or {} if isinstance(profile, six.string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) # Add pillar keys for default configuration - base_dashboards_from_pillar = ([_DEFAULT_DASHBOARD_PILLAR] + - base_dashboards_from_pillar) - base_panels_from_pillar = ([_DEFAULT_PANEL_PILLAR] + - base_panels_from_pillar) + base_dashboards_from_pillar = [ + _DEFAULT_DASHBOARD_PILLAR + ] + base_dashboards_from_pillar + base_panels_from_pillar = [_DEFAULT_PANEL_PILLAR] + base_panels_from_pillar base_rows_from_pillar = [_DEFAULT_ROW_PILLAR] + base_rows_from_pillar # Build out all dashboard fields - new_dashboard = _inherited_dashboard( - dashboard, base_dashboards_from_pillar, ret) - new_dashboard['title'] = name - rows = new_dashboard.get('rows', []) + new_dashboard = _inherited_dashboard(dashboard, base_dashboards_from_pillar, ret) + new_dashboard["title"] = name + rows = new_dashboard.get("rows", []) for i, row in enumerate(rows): rows[i] = _inherited_row(row, base_rows_from_pillar, ret) for row in rows: - panels = row.get('panels', []) + panels = row.get("panels", []) for i, panel in enumerate(panels): panels[i] = _inherited_panel(panel, base_panels_from_pillar, ret) _auto_adjust_panel_spans(new_dashboard) @@ -120,78 +127,77 @@ def present(name, _ensure_annotations(new_dashboard) # Create dashboard if it does not exist - url = 'db/{0}'.format(name) + url = "db/{0}".format(name) old_dashboard = _get(url, profile) if not old_dashboard: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Dashboard {0} is set to be created.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Dashboard {0} is set to be created.".format(name) return ret response = _update(new_dashboard, profile) - if response.get('status') == 'success': - ret['comment'] = 'Dashboard {0} created.'.format(name) - ret['changes']['new'] = 'Dashboard {0} created.'.format(name) + if response.get("status") == "success": + ret["comment"] = "Dashboard {0} created.".format(name) + ret["changes"]["new"] = "Dashboard {0} created.".format(name) else: - ret['result'] = False - ret['comment'] = ("Failed to create dashboard {0}, " - "response={1}").format(name, response) + ret["result"] = False + ret["comment"] = ("Failed to create dashboard {0}, " "response={1}").format( + name, response + ) return ret # Add unmanaged rows to the dashboard. They appear at the top if they are # marked as pinned. They appear at the bottom otherwise. - managed_row_titles = [row.get('title') - for row in new_dashboard.get('rows', [])] - new_rows = new_dashboard.get('rows', []) - for old_row in old_dashboard.get('rows', []): - if old_row.get('title') not in managed_row_titles: + managed_row_titles = [row.get("title") for row in new_dashboard.get("rows", [])] + new_rows = new_dashboard.get("rows", []) + for old_row in old_dashboard.get("rows", []): + if old_row.get("title") not in managed_row_titles: new_rows.append(copy.deepcopy(old_row)) _ensure_pinned_rows(new_dashboard) _ensure_panel_ids(new_dashboard) # Update dashboard if it differs - dashboard_diff = DictDiffer(_cleaned(new_dashboard), - _cleaned(old_dashboard)) - updated_needed = (dashboard_diff.changed() or - dashboard_diff.added() or - dashboard_diff.removed()) + dashboard_diff = DictDiffer(_cleaned(new_dashboard), _cleaned(old_dashboard)) + updated_needed = ( + dashboard_diff.changed() or dashboard_diff.added() or dashboard_diff.removed() + ) if updated_needed: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ( - str('Dashboard {0} is set to be updated, changes={1}').format( # future lint: blacklisted-function - name, - salt.utils.json.dumps( - _dashboard_diff( - _cleaned(new_dashboard), - _cleaned(old_dashboard) - ), - indent=4 - ) - ) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = str( + "Dashboard {0} is set to be updated, changes={1}" + ).format( # future lint: blacklisted-function + name, + salt.utils.json.dumps( + _dashboard_diff(_cleaned(new_dashboard), _cleaned(old_dashboard)), + indent=4, + ), ) return ret response = _update(new_dashboard, profile) - if response.get('status') == 'success': + if response.get("status") == "success": updated_dashboard = _get(url, profile) - dashboard_diff = DictDiffer(_cleaned(updated_dashboard), - _cleaned(old_dashboard)) - ret['comment'] = 'Dashboard {0} updated.'.format(name) - ret['changes'] = _dashboard_diff(_cleaned(new_dashboard), - _cleaned(old_dashboard)) + dashboard_diff = DictDiffer( + _cleaned(updated_dashboard), _cleaned(old_dashboard) + ) + ret["comment"] = "Dashboard {0} updated.".format(name) + ret["changes"] = _dashboard_diff( + _cleaned(new_dashboard), _cleaned(old_dashboard) + ) else: - ret['result'] = False - ret['comment'] = ("Failed to update dashboard {0}, " - "response={1}").format(name, response) + ret["result"] = False + ret["comment"] = ("Failed to update dashboard {0}, " "response={1}").format( + name, response + ) return ret - ret['comment'] = 'Dashboard present' + ret["comment"] = "Dashboard present" return ret -def absent(name, profile='grafana'): - ''' +def absent(name, profile="grafana"): + """ Ensure the named grafana dashboard is absent. name @@ -199,102 +205,104 @@ def absent(name, profile='grafana'): profile A pillar key or dict that contains grafana information - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if isinstance(profile, six.string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - url = 'db/{0}'.format(name) + url = "db/{0}".format(name) existing_dashboard = _get(url, profile) if existing_dashboard: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Dashboard {0} is set to be deleted.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Dashboard {0} is set to be deleted.".format(name) return ret _delete(url, profile) - ret['comment'] = 'Dashboard {0} deleted.'.format(name) - ret['changes']['new'] = 'Dashboard {0} deleted.'.format(name) + ret["comment"] = "Dashboard {0} deleted.".format(name) + ret["changes"]["new"] = "Dashboard {0} deleted.".format(name) return ret - ret['comment'] = 'Dashboard absent' + ret["comment"] = "Dashboard absent" return ret _IGNORED_DASHBOARD_FIELDS = [ - 'id', - 'originalTitle', - 'version', + "id", + "originalTitle", + "version", ] _IGNORED_ROW_FIELDS = [] _IGNORED_PANEL_FIELDS = [ - 'grid', - 'mode', - 'tooltip', + "grid", + "mode", + "tooltip", ] _IGNORED_TARGET_FIELDS = [ - 'textEditor', + "textEditor", ] def _cleaned(_dashboard): - '''Return a copy without fields that can differ.''' + """Return a copy without fields that can differ.""" dashboard = copy.deepcopy(_dashboard) for ignored_dashboard_field in _IGNORED_DASHBOARD_FIELDS: dashboard.pop(ignored_dashboard_field, None) - for row in dashboard.get('rows', []): + for row in dashboard.get("rows", []): for ignored_row_field in _IGNORED_ROW_FIELDS: row.pop(ignored_row_field, None) - for i, panel in enumerate(row.get('panels', [])): + for i, panel in enumerate(row.get("panels", [])): for ignored_panel_field in _IGNORED_PANEL_FIELDS: panel.pop(ignored_panel_field, None) - for target in panel.get('targets', []): + for target in panel.get("targets", []): for ignored_target_field in _IGNORED_TARGET_FIELDS: target.pop(ignored_target_field, None) - row['panels'][i] = _stripped(panel) + row["panels"][i] = _stripped(panel) return dashboard def _inherited_dashboard(dashboard, base_dashboards_from_pillar, ret): - '''Return a dashboard with properties from parents.''' + """Return a dashboard with properties from parents.""" base_dashboards = [] for base_dashboard_from_pillar in base_dashboards_from_pillar: - base_dashboard = __salt__['pillar.get'](base_dashboard_from_pillar) + base_dashboard = __salt__["pillar.get"](base_dashboard_from_pillar) if base_dashboard: base_dashboards.append(base_dashboard) elif base_dashboard_from_pillar != _DEFAULT_DASHBOARD_PILLAR: - ret.setdefault('warnings', []) + ret.setdefault("warnings", []) warning_message = 'Cannot find dashboard pillar "{0}".'.format( - base_dashboard_from_pillar) - if warning_message not in ret['warnings']: - ret['warnings'].append(warning_message) + base_dashboard_from_pillar + ) + if warning_message not in ret["warnings"]: + ret["warnings"].append(warning_message) base_dashboards.append(dashboard) result_dashboard = {} tags = set() for dashboard in base_dashboards: - tags.update(dashboard.get('tags', [])) + tags.update(dashboard.get("tags", [])) result_dashboard.update(dashboard) - result_dashboard['tags'] = list(tags) + result_dashboard["tags"] = list(tags) return result_dashboard def _inherited_row(row, base_rows_from_pillar, ret): - '''Return a row with properties from parents.''' + """Return a row with properties from parents.""" base_rows = [] for base_row_from_pillar in base_rows_from_pillar: - base_row = __salt__['pillar.get'](base_row_from_pillar) + base_row = __salt__["pillar.get"](base_row_from_pillar) if base_row: base_rows.append(base_row) elif base_row_from_pillar != _DEFAULT_ROW_PILLAR: - ret.setdefault('warnings', []) + ret.setdefault("warnings", []) warning_message = 'Cannot find row pillar "{0}".'.format( - base_row_from_pillar) - if warning_message not in ret['warnings']: - ret['warnings'].append(warning_message) + base_row_from_pillar + ) + if warning_message not in ret["warnings"]: + ret["warnings"].append(warning_message) base_rows.append(row) result_row = {} @@ -304,18 +312,19 @@ def _inherited_row(row, base_rows_from_pillar, ret): def _inherited_panel(panel, base_panels_from_pillar, ret): - '''Return a panel with properties from parents.''' + """Return a panel with properties from parents.""" base_panels = [] for base_panel_from_pillar in base_panels_from_pillar: - base_panel = __salt__['pillar.get'](base_panel_from_pillar) + base_panel = __salt__["pillar.get"](base_panel_from_pillar) if base_panel: base_panels.append(base_panel) elif base_panel_from_pillar != _DEFAULT_PANEL_PILLAR: - ret.setdefault('warnings', []) + ret.setdefault("warnings", []) warning_message = 'Cannot find panel pillar "{0}".'.format( - base_panel_from_pillar) - if warning_message not in ret['warnings']: - ret['warnings'].append(warning_message) + base_panel_from_pillar + ) + if warning_message not in ret["warnings"]: + ret["warnings"].append(warning_message) base_panels.append(panel) result_panel = {} @@ -329,19 +338,20 @@ _DEFAULT_PANEL_SPAN = 2.5 def _auto_adjust_panel_spans(dashboard): - '''Adjust panel spans to take up the available width. + """Adjust panel spans to take up the available width. For each group of panels that would be laid out on the same level, scale up the unspecified panel spans to fill up the level. - ''' - for row in dashboard.get('rows', []): + """ + for row in dashboard.get("rows", []): levels = [] current_level = [] levels.append(current_level) - for panel in row.get('panels', []): - current_level_span = sum(panel.get('span', _DEFAULT_PANEL_SPAN) - for panel in current_level) - span = panel.get('span', _DEFAULT_PANEL_SPAN) + for panel in row.get("panels", []): + current_level_span = sum( + panel.get("span", _DEFAULT_PANEL_SPAN) for panel in current_level + ) + span = panel.get("span", _DEFAULT_PANEL_SPAN) if current_level_span + span > _FULL_LEVEL_SPAN: current_level = [panel] levels.append(current_level) @@ -349,196 +359,200 @@ def _auto_adjust_panel_spans(dashboard): current_level.append(panel) for level in levels: - specified_panels = [panel for panel in level if 'span' in panel] - unspecified_panels = [panel for panel in level - if 'span' not in panel] + specified_panels = [panel for panel in level if "span" in panel] + unspecified_panels = [panel for panel in level if "span" not in panel] if not unspecified_panels: continue - specified_span = sum(panel['span'] for panel in specified_panels) + specified_span = sum(panel["span"] for panel in specified_panels) available_span = _FULL_LEVEL_SPAN - specified_span auto_span = float(available_span) / len(unspecified_panels) for panel in unspecified_panels: - panel['span'] = auto_span + panel["span"] = auto_span def _ensure_pinned_rows(dashboard): - '''Pin rows to the top of the dashboard.''' - pinned_row_titles = __salt__['pillar.get'](_PINNED_ROWS_PILLAR) + """Pin rows to the top of the dashboard.""" + pinned_row_titles = __salt__["pillar.get"](_PINNED_ROWS_PILLAR) if not pinned_row_titles: return pinned_row_titles_lower = [] for title in pinned_row_titles: pinned_row_titles_lower.append(title.lower()) - rows = dashboard.get('rows', []) + rows = dashboard.get("rows", []) pinned_rows = [] for i, row in enumerate(rows): - if row.get('title', '').lower() in pinned_row_titles_lower: + if row.get("title", "").lower() in pinned_row_titles_lower: del rows[i] pinned_rows.append(row) rows = pinned_rows + rows def _ensure_panel_ids(dashboard): - '''Assign panels auto-incrementing IDs.''' + """Assign panels auto-incrementing IDs.""" panel_id = 1 - for row in dashboard.get('rows', []): - for panel in row.get('panels', []): - panel['id'] = panel_id + for row in dashboard.get("rows", []): + for panel in row.get("panels", []): + panel["id"] = panel_id panel_id += 1 def _ensure_annotations(dashboard): - '''Explode annotation_tags into annotations.''' - if 'annotation_tags' not in dashboard: + """Explode annotation_tags into annotations.""" + if "annotation_tags" not in dashboard: return - tags = dashboard['annotation_tags'] + tags = dashboard["annotation_tags"] annotations = { - 'enable': True, - 'list': [], + "enable": True, + "list": [], } for tag in tags: - annotations['list'].append({ - 'datasource': "graphite", - 'enable': False, - 'iconColor': "#C0C6BE", - 'iconSize': 13, - 'lineColor': "rgba(255, 96, 96, 0.592157)", - 'name': tag, - 'showLine': True, - 'tags': tag, - }) - del dashboard['annotation_tags'] - dashboard['annotations'] = annotations + annotations["list"].append( + { + "datasource": "graphite", + "enable": False, + "iconColor": "#C0C6BE", + "iconSize": 13, + "lineColor": "rgba(255, 96, 96, 0.592157)", + "name": tag, + "showLine": True, + "tags": tag, + } + ) + del dashboard["annotation_tags"] + dashboard["annotations"] = annotations def _get(url, profile): - '''Get a specific dashboard.''' - request_url = "{0}/api/dashboards/{1}".format(profile.get('grafana_url'), - url) + """Get a specific dashboard.""" + request_url = "{0}/api/dashboards/{1}".format(profile.get("grafana_url"), url) response = requests.get( request_url, headers={ "Accept": "application/json", - "Authorization": "Bearer {0}".format(profile.get('grafana_token')) + "Authorization": "Bearer {0}".format(profile.get("grafana_token")), }, - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) data = response.json() - if data.get('message') == 'Not found': + if data.get("message") == "Not found": return None - if 'dashboard' not in data: + if "dashboard" not in data: return None - return data['dashboard'] + return data["dashboard"] def _delete(url, profile): - '''Delete a specific dashboard.''' - request_url = "{0}/api/dashboards/{1}".format(profile.get('grafana_url'), - url) + """Delete a specific dashboard.""" + request_url = "{0}/api/dashboards/{1}".format(profile.get("grafana_url"), url) response = requests.delete( request_url, headers={ "Accept": "application/json", - "Authorization": "Bearer {0}".format(profile.get('grafana_token')) + "Authorization": "Bearer {0}".format(profile.get("grafana_token")), }, - timeout=profile.get('grafana_timeout'), + timeout=profile.get("grafana_timeout"), ) data = response.json() return data def _update(dashboard, profile): - '''Update a specific dashboard.''' - payload = { - 'dashboard': dashboard, - 'overwrite': True - } - request_url = "{0}/api/dashboards/db".format(profile.get('grafana_url')) + """Update a specific dashboard.""" + payload = {"dashboard": dashboard, "overwrite": True} + request_url = "{0}/api/dashboards/db".format(profile.get("grafana_url")) response = requests.post( request_url, - headers={ - "Authorization": "Bearer {0}".format(profile.get('grafana_token')) - }, - json=payload + headers={"Authorization": "Bearer {0}".format(profile.get("grafana_token"))}, + json=payload, ) return response.json() def _dashboard_diff(_new_dashboard, _old_dashboard): - '''Return a dictionary of changes between dashboards.''' + """Return a dictionary of changes between dashboards.""" diff = {} # Dashboard diff new_dashboard = copy.deepcopy(_new_dashboard) old_dashboard = copy.deepcopy(_old_dashboard) dashboard_diff = DictDiffer(new_dashboard, old_dashboard) - diff['dashboard'] = _stripped({ - 'changed': list(dashboard_diff.changed()) or None, - 'added': list(dashboard_diff.added()) or None, - 'removed': list(dashboard_diff.removed()) or None, - }) + diff["dashboard"] = _stripped( + { + "changed": list(dashboard_diff.changed()) or None, + "added": list(dashboard_diff.added()) or None, + "removed": list(dashboard_diff.removed()) or None, + } + ) # Row diff - new_rows = new_dashboard.get('rows', []) - old_rows = old_dashboard.get('rows', []) + new_rows = new_dashboard.get("rows", []) + old_rows = old_dashboard.get("rows", []) new_rows_by_title = {} old_rows_by_title = {} for row in new_rows: - if 'title' in row: - new_rows_by_title[row['title']] = row + if "title" in row: + new_rows_by_title[row["title"]] = row for row in old_rows: - if 'title' in row: - old_rows_by_title[row['title']] = row + if "title" in row: + old_rows_by_title[row["title"]] = row rows_diff = DictDiffer(new_rows_by_title, old_rows_by_title) - diff['rows'] = _stripped({ - 'added': list(rows_diff.added()) or None, - 'removed': list(rows_diff.removed()) or None, - }) + diff["rows"] = _stripped( + { + "added": list(rows_diff.added()) or None, + "removed": list(rows_diff.removed()) or None, + } + ) for changed_row_title in rows_diff.changed(): old_row = old_rows_by_title[changed_row_title] new_row = new_rows_by_title[changed_row_title] row_diff = DictDiffer(new_row, old_row) - diff['rows'].setdefault('changed', {}) - diff['rows']['changed'][changed_row_title] = _stripped({ - 'changed': list(row_diff.changed()) or None, - 'added': list(row_diff.added()) or None, - 'removed': list(row_diff.removed()) or None, - }) + diff["rows"].setdefault("changed", {}) + diff["rows"]["changed"][changed_row_title] = _stripped( + { + "changed": list(row_diff.changed()) or None, + "added": list(row_diff.added()) or None, + "removed": list(row_diff.removed()) or None, + } + ) # Panel diff old_panels_by_id = {} new_panels_by_id = {} - for row in old_dashboard.get('rows', []): - for panel in row.get('panels', []): - if 'id' in panel: - old_panels_by_id[panel['id']] = panel - for row in new_dashboard.get('rows', []): - for panel in row.get('panels', []): - if 'id' in panel: - new_panels_by_id[panel['id']] = panel + for row in old_dashboard.get("rows", []): + for panel in row.get("panels", []): + if "id" in panel: + old_panels_by_id[panel["id"]] = panel + for row in new_dashboard.get("rows", []): + for panel in row.get("panels", []): + if "id" in panel: + new_panels_by_id[panel["id"]] = panel panels_diff = DictDiffer(new_panels_by_id, old_panels_by_id) - diff['panels'] = _stripped({ - 'added': list(panels_diff.added()) or None, - 'removed': list(panels_diff.removed()) or None, - }) + diff["panels"] = _stripped( + { + "added": list(panels_diff.added()) or None, + "removed": list(panels_diff.removed()) or None, + } + ) for changed_panel_id in panels_diff.changed(): old_panel = old_panels_by_id[changed_panel_id] new_panel = new_panels_by_id[changed_panel_id] panels_diff = DictDiffer(new_panel, old_panel) - diff['panels'].setdefault('changed', {}) - diff['panels']['changed'][changed_panel_id] = _stripped({ - 'changed': list(panels_diff.changed()) or None, - 'added': list(panels_diff.added()) or None, - 'removed': list(panels_diff.removed()) or None, - }) + diff["panels"].setdefault("changed", {}) + diff["panels"]["changed"][changed_panel_id] = _stripped( + { + "changed": list(panels_diff.changed()) or None, + "added": list(panels_diff.added()) or None, + "removed": list(panels_diff.removed()) or None, + } + ) return diff def _stripped(d): - '''Strip falsey entries.''' + """Strip falsey entries.""" ret = {} for k, v in six.iteritems(d): if v: diff --git a/salt/states/grafana_datasource.py b/salt/states/grafana_datasource.py index 16ad68f81eb..06e9b08defc 100644 --- a/salt/states/grafana_datasource.py +++ b/salt/states/grafana_datasource.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Grafana v2.0 data sources .. versionadded:: 2016.3.0 @@ -23,33 +23,36 @@ Manage Grafana v2.0 data sources - basic_auth_user: myuser - basic_auth_password: mypass - is_default: true -''' +""" from __future__ import absolute_import, print_function, unicode_literals import requests - from salt.ext.six import string_types def __virtual__(): - '''Only load if grafana v2.0 is configured.''' - return __salt__['config.get']('grafana_version', 1) == 2 + """Only load if grafana v2.0 is configured.""" + if __salt__["config.get"]("grafana_version", 1) == 2: + return True + return (False, "Not configured for grafana_version 2") -def present(name, - type, - url, - access='proxy', - user='', - password='', - database='', - basic_auth=False, - basic_auth_user='', - basic_auth_password='', - is_default=False, - json_data=None, - profile='grafana'): - ''' +def present( + name, + type, + url, + access="proxy", + user="", + password="", + database="", + basic_auth=False, + basic_auth_user="", + basic_auth_password="", + is_default=False, + json_data=None, + profile="grafana", +): + """ Ensure that a data source is present. name @@ -79,125 +82,136 @@ def present(name, is_default Default: False - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - ret = {'name': name, 'result': None, 'comment': None, 'changes': {}} + ret = {"name": name, "result": None, "comment": None, "changes": {}} datasource = _get_datasource(profile, name) - data = _get_json_data(name, type, url, access, user, password, database, - basic_auth, basic_auth_user, basic_auth_password, is_default, json_data) + data = _get_json_data( + name, + type, + url, + access, + user, + password, + database, + basic_auth, + basic_auth_user, + basic_auth_password, + is_default, + json_data, + ) if datasource: requests.put( - _get_url(profile, datasource['id']), + _get_url(profile, datasource["id"]), data, headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) - ret['result'] = True - ret['changes'] = _diff(datasource, data) - if ret['changes']['new'] or ret['changes']['old']: - ret['comment'] = 'Data source {0} updated'.format(name) + ret["result"] = True + ret["changes"] = _diff(datasource, data) + if ret["changes"]["new"] or ret["changes"]["old"]: + ret["comment"] = "Data source {0} updated".format(name) else: - ret['changes'] = {} - ret['comment'] = 'Data source {0} already up-to-date'.format(name) + ret["changes"] = {} + ret["comment"] = "Data source {0} already up-to-date".format(name) else: requests.post( - '{0}/api/datasources'.format(profile['grafana_url']), + "{0}/api/datasources".format(profile["grafana_url"]), data, headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) - ret['result'] = True - ret['comment'] = 'New data source {0} added'.format(name) - ret['changes'] = data + ret["result"] = True + ret["comment"] = "New data source {0} added".format(name) + ret["changes"] = data return ret -def absent(name, profile='grafana'): - ''' +def absent(name, profile="grafana"): + """ Ensure that a data source is present. name Name of the data source to remove. - ''' + """ if isinstance(profile, string_types): - profile = __salt__['config.option'](profile) + profile = __salt__["config.option"](profile) - ret = {'result': None, 'comment': None, 'changes': {}} + ret = {"result": None, "comment": None, "changes": {}} datasource = _get_datasource(profile, name) if not datasource: - ret['result'] = True - ret['comment'] = 'Data source {0} already absent'.format(name) + ret["result"] = True + ret["comment"] = "Data source {0} already absent".format(name) return ret requests.delete( - _get_url(profile, datasource['id']), + _get_url(profile, datasource["id"]), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) - ret['result'] = True - ret['comment'] = 'Data source {0} was deleted'.format(name) + ret["result"] = True + ret["comment"] = "Data source {0} was deleted".format(name) return ret def _get_url(profile, datasource_id): - return '{0}/api/datasources/{1}'.format( - profile['grafana_url'], - datasource_id - ) + return "{0}/api/datasources/{1}".format(profile["grafana_url"], datasource_id) def _get_datasource(profile, name): response = requests.get( - '{0}/api/datasources'.format(profile['grafana_url']), + "{0}/api/datasources".format(profile["grafana_url"]), headers=_get_headers(profile), - timeout=profile.get('grafana_timeout', 3), + timeout=profile.get("grafana_timeout", 3), ) data = response.json() for datasource in data: - if datasource['name'] == name: + if datasource["name"] == name: return datasource return None def _get_headers(profile): return { - 'Accept': 'application/json', - 'Authorization': 'Bearer {0}'.format(profile['grafana_token']) + "Accept": "application/json", + "Authorization": "Bearer {0}".format(profile["grafana_token"]), } -def _get_json_data(name, - type, - url, - access='proxy', - user='', - password='', - database='', - basic_auth=False, - basic_auth_user='', - basic_auth_password='', - is_default=False, - json_data=None): +def _get_json_data( + name, + type, + url, + access="proxy", + user="", + password="", + database="", + basic_auth=False, + basic_auth_user="", + basic_auth_password="", + is_default=False, + json_data=None, +): return { - 'name': name, - 'type': type, - 'url': url, - 'access': access, - 'user': user, - 'password': password, - 'database': database, - 'basicAuth': basic_auth, - 'basicAuthUser': basic_auth_user, - 'basicAuthPassword': basic_auth_password, - 'isDefault': is_default, - 'jsonData': json_data, + "name": name, + "type": type, + "url": url, + "access": access, + "user": user, + "password": password, + "database": database, + "basicAuth": basic_auth, + "basicAuthUser": basic_auth_user, + "basicAuthPassword": basic_auth_password, + "isDefault": is_default, + "jsonData": json_data, } @@ -206,11 +220,11 @@ def _diff(old, new): old = old.copy() new = new.copy() for key in old_keys: - if key == 'id' or key == 'orgId': + if key == "id" or key == "orgId": del old[key] elif key not in new.keys(): del old[key] elif old[key] == new[key]: del old[key] del new[key] - return {'old': old, 'new': new} + return {"old": old, "new": new} diff --git a/salt/states/grains.py b/salt/states/grains.py index 7121d9701bf..dbfd9444927 100644 --- a/salt/states/grains.py +++ b/salt/states/grains.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage grains on the minion =========================== @@ -10,10 +10,11 @@ file on the minions, By default, this file is located at: ``/etc/salt/grains`` .. note:: This does **NOT** override any grains set in the minion config file. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import re # Import Salt libs @@ -21,7 +22,7 @@ from salt.defaults import DEFAULT_TARGET_DELIM def exists(name, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Ensure that a grain is set name @@ -31,22 +32,19 @@ def exists(name, delimiter=DEFAULT_TARGET_DELIM): A delimiter different from the default can be provided. Check whether a grain exists. Does not attempt to check or set the value. - ''' + """ name = re.sub(delimiter, DEFAULT_TARGET_DELIM, name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Grain exists'} + ret = {"name": name, "changes": {}, "result": True, "comment": "Grain exists"} _non_existent = object() - existing = __salt__['grains.get'](name, _non_existent) + existing = __salt__["grains.get"](name, _non_existent) if existing is _non_existent: - ret['result'] = False - ret['comment'] = 'Grain does not exist' + ret["result"] = False + ret["comment"] = "Grain does not exist" return ret def make_hashable(list_grain, result=None): - ''' + """ Ensure that a list grain is hashable. list_grain @@ -59,7 +57,7 @@ def make_hashable(list_grain, result=None): Make it possible to compare two list grains to each other if the list contains complex objects. - ''' + """ result = result or set() for sublist in list_grain: if type(sublist) == list: @@ -70,7 +68,7 @@ def make_hashable(list_grain, result=None): def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False): - ''' + """ Ensure that a grain is set .. versionchanged:: v2015.8.2 @@ -121,35 +119,32 @@ def present(name, value, delimiter=DEFAULT_TARGET_DELIM, force=False): grains.present: - value: yay - delimiter: ',' - ''' + """ name = re.sub(delimiter, DEFAULT_TARGET_DELIM, name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} _non_existent = object() - existing = __salt__['grains.get'](name, _non_existent) + existing = __salt__["grains.get"](name, _non_existent) if existing == value: - ret['comment'] = 'Grain is already set' + ret["comment"] = "Grain is already set" return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if existing is _non_existent: - ret['comment'] = 'Grain {0} is set to be added'.format(name) - ret['changes'] = {'new': name} + ret["comment"] = "Grain {0} is set to be added".format(name) + ret["changes"] = {"new": name} else: - ret['comment'] = 'Grain {0} is set to be changed'.format(name) - ret['changes'] = {'changed': {name: value}} + ret["comment"] = "Grain {0} is set to be changed".format(name) + ret["changes"] = {"changed": {name: value}} return ret - ret = __salt__['grains.set'](name, value, force=force) - if ret['result'] is True and ret['changes'] != {}: - ret['comment'] = 'Set grain {0} to {1}'.format(name, value) - ret['name'] = name + ret = __salt__["grains.set"](name, value, force=force) + if ret["result"] is True and ret["changes"] != {}: + ret["comment"] = "Set grain {0} to {1}".format(name, value) + ret["name"] = name return ret def list_present(name, value, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ .. versionadded:: 2014.1.0 Ensure the value is present in the list-type grain. Note: If the grain that is @@ -184,67 +179,76 @@ def list_present(name, value, delimiter=DEFAULT_TARGET_DELIM): - value: - web - dev - ''' + """ name = re.sub(delimiter, DEFAULT_TARGET_DELIM, name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} - grain = __salt__['grains.get'](name) + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + grain = __salt__["grains.get"](name) if grain: # check whether grain is a list if not isinstance(grain, list): - ret['result'] = False - ret['comment'] = 'Grain {0} is not a valid list'.format(name) + ret["result"] = False + ret["comment"] = "Grain {0} is not a valid list".format(name) return ret if isinstance(value, list): - if make_hashable(value).issubset(make_hashable(__salt__['grains.get'](name))): - ret['comment'] = 'Value {1} is already in grain {0}'.format(name, value) + if make_hashable(value).issubset( + make_hashable(__salt__["grains.get"](name)) + ): + ret["comment"] = "Value {1} is already in grain {0}".format(name, value) return ret - elif name in __context__.get('pending_grains', {}): + elif name in __context__.get("pending_grains", {}): # elements common to both - intersection = set(value).intersection(__context__.get('pending_grains', {})[name]) + intersection = set(value).intersection( + __context__.get("pending_grains", {})[name] + ) if intersection: - value = list(set(value).difference(__context__['pending_grains'][name])) - ret['comment'] = 'Removed value {0} from update due to context found in "{1}".\n'.format(value, name) - if 'pending_grains' not in __context__: - __context__['pending_grains'] = {} - if name not in __context__['pending_grains']: - __context__['pending_grains'][name] = set() - __context__['pending_grains'][name].update(value) + value = list( + set(value).difference(__context__["pending_grains"][name]) + ) + ret[ + "comment" + ] = 'Removed value {0} from update due to context found in "{1}".\n'.format( + value, name + ) + if "pending_grains" not in __context__: + __context__["pending_grains"] = {} + if name not in __context__["pending_grains"]: + __context__["pending_grains"][name] = set() + __context__["pending_grains"][name].update(value) else: if value in grain: - ret['comment'] = 'Value {1} is already in grain {0}'.format(name, value) + ret["comment"] = "Value {1} is already in grain {0}".format(name, value) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Value {1} is set to be appended to grain {0}'.format(name, value) - ret['changes'] = {'new': grain} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Value {1} is set to be appended to grain {0}".format( + name, value + ) + ret["changes"] = {"new": grain} return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Grain {0} is set to be added'.format(name) - ret['changes'] = {'new': grain} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Grain {0} is set to be added".format(name) + ret["changes"] = {"new": grain} return ret - new_grains = __salt__['grains.append'](name, value) + new_grains = __salt__["grains.append"](name, value) if isinstance(value, list): - if not set(value).issubset(set(__salt__['grains.get'](name))): - ret['result'] = False - ret['comment'] = 'Failed append value {1} to grain {0}'.format(name, value) + if not set(value).issubset(set(__salt__["grains.get"](name))): + ret["result"] = False + ret["comment"] = "Failed append value {1} to grain {0}".format(name, value) return ret else: - if value not in __salt__['grains.get'](name, delimiter=DEFAULT_TARGET_DELIM): - ret['result'] = False - ret['comment'] = 'Failed append value {1} to grain {0}'.format(name, value) + if value not in __salt__["grains.get"](name, delimiter=DEFAULT_TARGET_DELIM): + ret["result"] = False + ret["comment"] = "Failed append value {1} to grain {0}".format(name, value) return ret - ret['comment'] = 'Append value {1} to grain {0}'.format(name, value) - ret['changes'] = {'new': new_grains} + ret["comment"] = "Append value {1} to grain {0}".format(name, value) + ret["changes"] = {"new": new_grains} return ret def list_absent(name, value, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Delete a value from a grain formed as a list. .. versionadded:: 2014.1.0 @@ -277,53 +281,50 @@ def list_absent(name, value, delimiter=DEFAULT_TARGET_DELIM): - value: - web - dev - ''' + """ name = re.sub(delimiter, DEFAULT_TARGET_DELIM, name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} comments = [] - grain = __salt__['grains.get'](name, None) + grain = __salt__["grains.get"](name, None) if grain: if isinstance(grain, list): if not isinstance(value, list): value = [value] for val in value: if val not in grain: - comments.append('Value {1} is absent from ' - 'grain {0}'.format(name, val)) - elif __opts__['test']: - ret['result'] = None - comments.append('Value {1} in grain {0} is set ' - 'to be deleted'.format(name, val)) - if 'deleted' not in ret['changes'].keys(): - ret['changes'] = {'deleted': []} - ret['changes']['deleted'].append(val) + comments.append( + "Value {1} is absent from " "grain {0}".format(name, val) + ) + elif __opts__["test"]: + ret["result"] = None + comments.append( + "Value {1} in grain {0} is set " + "to be deleted".format(name, val) + ) + if "deleted" not in ret["changes"].keys(): + ret["changes"] = {"deleted": []} + ret["changes"]["deleted"].append(val) elif val in grain: - __salt__['grains.remove'](name, val) - comments.append('Value {1} was deleted from ' - 'grain {0}'.format(name, val)) - if 'deleted' not in ret['changes'].keys(): - ret['changes'] = {'deleted': []} - ret['changes']['deleted'].append(val) - ret['comment'] = '\n'.join(comments) + __salt__["grains.remove"](name, val) + comments.append( + "Value {1} was deleted from " "grain {0}".format(name, val) + ) + if "deleted" not in ret["changes"].keys(): + ret["changes"] = {"deleted": []} + ret["changes"]["deleted"].append(val) + ret["comment"] = "\n".join(comments) return ret else: - ret['result'] = False - ret['comment'] = 'Grain {0} is not a valid list'\ - .format(name) + ret["result"] = False + ret["comment"] = "Grain {0} is not a valid list".format(name) else: - ret['comment'] = 'Grain {0} does not exist'.format(name) + ret["comment"] = "Grain {0} does not exist".format(name) return ret -def absent(name, - destructive=False, - delimiter=DEFAULT_TARGET_DELIM, - force=False): - ''' +def absent(name, destructive=False, delimiter=DEFAULT_TARGET_DELIM, force=False): + """ .. versionadded:: 2014.7.0 Delete a grain from the grains config file @@ -356,63 +357,54 @@ def absent(name, grain_name: grains.absent - ''' + """ _non_existent = object() name = re.sub(delimiter, DEFAULT_TARGET_DELIM, name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} - grain = __salt__['grains.get'](name, _non_existent) + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + grain = __salt__["grains.get"](name, _non_existent) if grain is None: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if destructive is True: - ret['comment'] = 'Grain {0} is set to be deleted'.format(name) - ret['changes'] = {'deleted': name} + ret["comment"] = "Grain {0} is set to be deleted".format(name) + ret["changes"] = {"deleted": name} return ret - ret = __salt__['grains.set'](name, - None, - destructive=destructive, - force=force) - if ret['result']: + ret = __salt__["grains.set"](name, None, destructive=destructive, force=force) + if ret["result"]: if destructive is True: - ret['comment'] = 'Grain {0} was deleted'.format(name) - ret['changes'] = {'deleted': name} - ret['name'] = name + ret["comment"] = "Grain {0} was deleted".format(name) + ret["changes"] = {"deleted": name} + ret["name"] = name elif grain is not _non_existent: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if destructive is True: - ret['comment'] = 'Grain {0} is set to be deleted'.format(name) - ret['changes'] = {'deleted': name} + ret["comment"] = "Grain {0} is set to be deleted".format(name) + ret["changes"] = {"deleted": name} else: - ret['comment'] = 'Value for grain {0} is set to be ' \ - 'deleted (None)'.format(name) - ret['changes'] = {'grain': name, 'value': None} + ret[ + "comment" + ] = "Value for grain {0} is set to be " "deleted (None)".format(name) + ret["changes"] = {"grain": name, "value": None} return ret - ret = __salt__['grains.set'](name, - None, - destructive=destructive, - force=force) - if ret['result']: + ret = __salt__["grains.set"](name, None, destructive=destructive, force=force) + if ret["result"]: if destructive is True: - ret['comment'] = 'Grain {0} was deleted'.format(name) - ret['changes'] = {'deleted': name} + ret["comment"] = "Grain {0} was deleted".format(name) + ret["changes"] = {"deleted": name} else: - ret['comment'] = 'Value for grain {0} was set to None'.format(name) - ret['changes'] = {'grain': name, 'value': None} - ret['name'] = name + ret["comment"] = "Value for grain {0} was set to None".format(name) + ret["changes"] = {"grain": name, "value": None} + ret["name"] = name else: - ret['comment'] = 'Grain {0} does not exist'.format(name) + ret["comment"] = "Grain {0} does not exist".format(name) return ret -def append(name, value, convert=False, - delimiter=DEFAULT_TARGET_DELIM): - ''' +def append(name, value, convert=False, delimiter=DEFAULT_TARGET_DELIM): + """ .. versionadded:: 2014.7.0 Append a value to a list in the grains config file. The grain that is being @@ -439,13 +431,10 @@ def append(name, value, convert=False, grain_name: grains.append: - value: to_be_appended - ''' + """ name = re.sub(delimiter, DEFAULT_TARGET_DELIM, name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} - grain = __salt__['grains.get'](name, None) + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + grain = __salt__["grains.get"](name, None) # Check if bool(grain) is False or if the grain is specified in the minions # grains. Grains can be set to a None value by omitting a value in the @@ -453,36 +442,41 @@ def append(name, value, convert=False, if grain or name in __grains__: if isinstance(grain, list): if value in grain: - ret['comment'] = 'Value {1} is already in the list ' \ - 'for grain {0}'.format(name, value) + ret["comment"] = ( + "Value {1} is already in the list " + "for grain {0}".format(name, value) + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Value {1} in grain {0} is set to ' \ - 'be added'.format(name, value) - ret['changes'] = {'added': value} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Value {1} in grain {0} is set to " "be added".format( + name, value + ) + ret["changes"] = {"added": value} return ret - __salt__['grains.append'](name, value) - ret['comment'] = 'Value {1} was added to grain {0}'.format(name, value) - ret['changes'] = {'added': value} + __salt__["grains.append"](name, value) + ret["comment"] = "Value {1} was added to grain {0}".format(name, value) + ret["changes"] = {"added": value} else: if convert is True: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Grain {0} is set to be converted ' \ - 'to list and value {1} will be ' \ - 'added'.format(name, value) - ret['changes'] = {'added': value} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Grain {0} is set to be converted " + "to list and value {1} will be " + "added".format(name, value) + ) + ret["changes"] = {"added": value} return ret grain = [] if grain is None else [grain] grain.append(value) - __salt__['grains.setval'](name, grain) - ret['comment'] = 'Value {1} was added to grain {0}'.format(name, value) - ret['changes'] = {'added': value} + __salt__["grains.setval"](name, grain) + ret["comment"] = "Value {1} was added to grain {0}".format(name, value) + ret["changes"] = {"added": value} else: - ret['result'] = False - ret['comment'] = 'Grain {0} is not a valid list'.format(name) + ret["result"] = False + ret["comment"] = "Grain {0} is not a valid list".format(name) else: - ret['result'] = False - ret['comment'] = 'Grain {0} does not exist'.format(name) + ret["result"] = False + ret["comment"] = "Grain {0} does not exist".format(name) return ret diff --git a/salt/states/group.py b/salt/states/group.py index bc2ea53de15..28da7b0030e 100644 --- a/salt/states/group.py +++ b/salt/states/group.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of user groups ========================= @@ -32,85 +32,83 @@ In Windows, if no domain is specified in the user or group name (i.e. - bar - user1 - user2 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import sys -# Import 3rd-party libs -from salt.ext import six +import sys # Import Salt libs import salt.utils.platform import salt.utils.win_functions +# Import 3rd-party libs +from salt.ext import six -def _changes(name, - gid=None, - addusers=None, - delusers=None, - members=None): - ''' + +def _changes(name, gid=None, addusers=None, delusers=None, members=None): + """ Return a dict of the changes required for a group if the group is present, otherwise return False. - ''' - lgrp = __salt__['group.info'](name) + """ + lgrp = __salt__["group.info"](name) if not lgrp: return False # User and Domain names are not case sensitive in Windows. Let's make them # all lower case so we can compare properly if salt.utils.platform.is_windows(): - if lgrp['members']: - lgrp['members'] = [user.lower() for user in lgrp['members']] + if lgrp["members"]: + lgrp["members"] = [user.lower() for user in lgrp["members"]] if members: - members = [salt.utils.win_functions.get_sam_name(user).lower() for user in members] + members = [ + salt.utils.win_functions.get_sam_name(user).lower() for user in members + ] if addusers: - addusers = [salt.utils.win_functions.get_sam_name(user).lower() for user in addusers] + addusers = [ + salt.utils.win_functions.get_sam_name(user).lower() for user in addusers + ] if delusers: - delusers = [salt.utils.win_functions.get_sam_name(user).lower() for user in delusers] + delusers = [ + salt.utils.win_functions.get_sam_name(user).lower() for user in delusers + ] change = {} ret = {} if gid: try: gid = int(gid) - if lgrp['gid'] != gid: - change['gid'] = gid + if lgrp["gid"] != gid: + change["gid"] = gid except (TypeError, ValueError): - ret['result'] = False - ret['comment'] = 'Invalid gid' + ret["result"] = False + ret["comment"] = "Invalid gid" return ret if members is not None and not members: - if set(lgrp['members']).symmetric_difference(members): - change['delusers'] = set(lgrp['members']) + if set(lgrp["members"]).symmetric_difference(members): + change["delusers"] = set(lgrp["members"]) elif members: # if new member list if different than the current - if set(lgrp['members']).symmetric_difference(members): - change['members'] = members + if set(lgrp["members"]).symmetric_difference(members): + change["members"] = members if addusers: - users_2add = [user for user in addusers if user not in lgrp['members']] + users_2add = [user for user in addusers if user not in lgrp["members"]] if users_2add: - change['addusers'] = users_2add + change["addusers"] = users_2add if delusers: - users_2del = [user for user in delusers if user in lgrp['members']] + users_2del = [user for user in delusers if user in lgrp["members"]] if users_2del: - change['delusers'] = users_2del + change["delusers"] = users_2del return change -def present(name, - gid=None, - system=False, - addusers=None, - delusers=None, - members=None): - r''' +def present(name, gid=None, system=False, addusers=None, delusers=None, members=None): + r""" Ensure that a group is present Args: @@ -162,131 +160,124 @@ def present(name, - members: - DOMAIN\domain_admins - Administrator - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Group {0} is present and up to date'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Group {0} is present and up to date".format(name), + } if members is not None and (addusers is not None or delusers is not None): - ret['result'] = None - ret['comment'] = ( + ret["result"] = None + ret["comment"] = ( 'Error: Conflicting options "members" with "addusers" and/or' - ' "delusers" can not be used together. ') + ' "delusers" can not be used together. ' + ) return ret if addusers and delusers: # -- if trying to add and delete the same user(s) at the same time. if not set(addusers).isdisjoint(set(delusers)): - ret['result'] = None - ret['comment'] = ( - 'Error. Same user(s) can not be added and deleted' - ' simultaneously') + ret["result"] = None + ret["comment"] = ( + "Error. Same user(s) can not be added and deleted" " simultaneously" + ) return ret - changes = _changes(name, - gid, - addusers, - delusers, - members) + changes = _changes(name, gid, addusers, delusers, members) if changes: - ret['comment'] = ( - 'The following group attributes are set to be changed:\n') + ret["comment"] = "The following group attributes are set to be changed:\n" for key, val in six.iteritems(changes): - ret['comment'] += '{0}: {1}\n'.format(key, val) + ret["comment"] += "{0}: {1}\n".format(key, val) - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret for key, val in six.iteritems(changes): - if key == 'gid': - __salt__['group.chgid'](name, gid) + if key == "gid": + __salt__["group.chgid"](name, gid) continue - if key == 'addusers': + if key == "addusers": for user in val: - __salt__['group.adduser'](name, user) + __salt__["group.adduser"](name, user) continue - if key == 'delusers': + if key == "delusers": for user in val: - __salt__['group.deluser'](name, user) + __salt__["group.deluser"](name, user) continue - if key == 'members': - __salt__['group.members'](name, ','.join(members)) + if key == "members": + __salt__["group.members"](name, ",".join(members)) continue # Clear cached group data - sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('group.getent', None) - changes = _changes(name, - gid, - addusers, - delusers, - members) + sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "group.getent", None + ) + changes = _changes(name, gid, addusers, delusers, members) if changes: - ret['result'] = False - ret['comment'] += 'Some changes could not be applied' - ret['changes'] = {'Failed': changes} + ret["result"] = False + ret["comment"] += "Some changes could not be applied" + ret["changes"] = {"Failed": changes} else: - ret['changes'] = {'Final': 'All changes applied successfully'} + ret["changes"] = {"Final": "All changes applied successfully"} if changes is False: # The group is not present, make it! - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Group {0} set to be added'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Group {0} set to be added".format(name) return ret - grps = __salt__['group.getent']() + grps = __salt__["group.getent"]() # Test if gid is free if gid is not None: gid_group = None for lgrp in grps: - if lgrp['gid'] == gid: - gid_group = lgrp['name'] + if lgrp["gid"] == gid: + gid_group = lgrp["name"] break if gid_group is not None: - ret['result'] = False - ret['comment'] = ( - 'Group {0} is not present but gid {1} is already taken by' - ' group {2}'.format(name, gid, gid_group)) + ret["result"] = False + ret["comment"] = ( + "Group {0} is not present but gid {1} is already taken by" + " group {2}".format(name, gid, gid_group) + ) return ret # Group is not present, make it. - if __salt__['group.add'](name, gid=gid, system=system): + if __salt__["group.add"](name, gid=gid, system=system): # if members to be added grp_members = None if members: - grp_members = ','.join(members) + grp_members = ",".join(members) if addusers: - grp_members = ','.join(addusers) + grp_members = ",".join(addusers) if grp_members: - __salt__['group.members'](name, grp_members) + __salt__["group.members"](name, grp_members) # Clear cached group data - sys.modules[__salt__['test.ping'].__module__].__context__.pop( - 'group.getent', None) - ret['comment'] = 'New group {0} created'.format(name) - ret['changes'] = __salt__['group.info'](name) - changes = _changes(name, - gid, - addusers, - delusers, - members) + sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "group.getent", None + ) + ret["comment"] = "New group {0} created".format(name) + ret["changes"] = __salt__["group.info"](name) + changes = _changes(name, gid, addusers, delusers, members) if changes: - ret['result'] = False - ret['comment'] = ( - 'Group {0} has been created but, some changes could not' - ' be applied'.format(name)) - ret['changes'] = {'Failed': changes} + ret["result"] = False + ret["comment"] = ( + "Group {0} has been created but, some changes could not" + " be applied".format(name) + ) + ret["changes"] = {"Failed": changes} else: - ret['result'] = False - ret['comment'] = 'Failed to create new group {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create new group {0}".format(name) return ret def absent(name): - ''' + """ Ensure that the named group is absent Args: @@ -300,26 +291,23 @@ def absent(name): # Removes the local group `db_admin` db_admin: group.absent - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} - grp_info = __salt__['group.info'](name) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + grp_info = __salt__["group.info"](name) if grp_info: # Group already exists. Remove the group. - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Group {0} is set for removal'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Group {0} is set for removal".format(name) return ret - ret['result'] = __salt__['group.delete'](name) - if ret['result']: - ret['changes'] = {name: ''} - ret['comment'] = 'Removed group {0}'.format(name) + ret["result"] = __salt__["group.delete"](name) + if ret["result"]: + ret["changes"] = {name: ""} + ret["comment"] = "Removed group {0}".format(name) return ret else: - ret['comment'] = 'Failed to remove group {0}'.format(name) + ret["comment"] = "Failed to remove group {0}".format(name) return ret else: - ret['comment'] = 'Group not present' + ret["comment"] = "Group not present" return ret diff --git a/salt/states/heat.py b/salt/states/heat.py index c05fecbd753..b2521d94657 100644 --- a/salt/states/heat.py +++ b/salt/states/heat.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Heat ================== @@ -38,9 +38,10 @@ mysql: The spelling mistake in parameter `enviroment` was corrected to `environment`. The `enviroment` spelling mistake has been removed in Salt 3000. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -58,6 +59,7 @@ from salt.ext import six HAS_OSLO = False try: from oslo_serialization import jsonutils + HAS_OSLO = True except ImportError: pass @@ -67,21 +69,24 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the mysql module is in __salt__ - ''' + """ if HAS_OSLO: - return 'heat' - return (False, 'The heat state module cannot be loaded: ' - 'the oslo_serialization python library is not available.') + return "heat" + return ( + False, + "The heat state module cannot be loaded: " + "the oslo_serialization python library is not available.", + ) def _parse_template(tmpl_str): - ''' + """ Parsing template - ''' + """ tmpl_str = tmpl_str.strip() - if tmpl_str.startswith('{'): + if tmpl_str.startswith("{"): tpl = salt.utils.json.loads(tmpl_str) else: try: @@ -91,17 +96,28 @@ def _parse_template(tmpl_str): else: if tpl is None: tpl = {} - if not ('HeatTemplateFormatVersion' in tpl - or 'heat_template_version' in tpl - or 'AWSTemplateFormatVersion' in tpl): - raise ValueError(('Template format version not found.')) + if not ( + "HeatTemplateFormatVersion" in tpl + or "heat_template_version" in tpl + or "AWSTemplateFormatVersion" in tpl + ): + raise ValueError(("Template format version not found.")) return tpl -def deployed(name, template=None, environment=None, params=None, poll=5, - rollback=False, timeout=60, update=False, profile=None, - **connection_args): - ''' +def deployed( + name, + template=None, + environment=None, + params=None, + poll=5, + rollback=False, + timeout=60, + update=False, + profile=None, + **connection_args +): + """ Deploy stack with the specified properties name @@ -133,9 +149,9 @@ def deployed(name, template=None, environment=None, params=None, poll=5, The spelling mistake in parameter `enviroment` was corrected to `environment`. The `enviroment` spelling mistake has been removed in Salt 3000. - ''' + """ log.debug( - 'Deployed with(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', + "Deployed with(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", name, template, environment, @@ -145,29 +161,26 @@ def deployed(name, template=None, environment=None, params=None, poll=5, timeout, update, profile, - connection_args + connection_args, ) - ret = {'name': None, - 'comment': '', - 'changes': {}, - 'result': True} + ret = {"name": None, "comment": "", "changes": {}, "result": True} if not name: - ret['result'] = False - ret['comment'] = 'Name ist not valid' + ret["result"] = False + ret["comment"] = "Name ist not valid" return ret - ret['name'] = name, + ret["name"] = (name,) - existing_stack = __salt__['heat.show_stack'](name, profile=profile) + existing_stack = __salt__["heat.show_stack"](name, profile=profile) - if existing_stack['result'] and not update: - ret['comment'] = 'Stack {0} is deployed'.format(name) + if existing_stack["result"] and not update: + ret["comment"] = "Stack {0} is deployed".format(name) return ret - if existing_stack['result'] and update: + if existing_stack["result"] and update: if template: template_tmp_file = salt.utils.files.mkstemp() - tsfn, source_sum, comment_ = __salt__['file.get_managed']( + tsfn, source_sum, comment_ = __salt__["file.get_managed"]( name=template_tmp_file, template=None, source=template, @@ -175,13 +188,14 @@ def deployed(name, template=None, environment=None, params=None, poll=5, user=None, group=None, mode=None, - saltenv='base', + saltenv="base", context=None, defaults=None, skip_verify=False, - kwargs=None) + kwargs=None, + ) - template_manage_result = __salt__['file.manage_file']( + template_manage_result = __salt__["file.manage_file"]( name=template_tmp_file, sfn=tsfn, ret=None, @@ -190,101 +204,114 @@ def deployed(name, template=None, environment=None, params=None, poll=5, user=None, group=None, mode=None, - saltenv='base', + saltenv="base", backup=None, makedirs=True, template=None, show_changes=False, contents=None, - dir_mode=None) + dir_mode=None, + ) - if (template_manage_result['result']) or \ - ((__opts__['test']) and (template_manage_result['result'] is not False)): - with salt.utils.files.fopen(template_tmp_file, 'r') as tfp_: + if (template_manage_result["result"]) or ( + (__opts__["test"]) and (template_manage_result["result"] is not False) + ): + with salt.utils.files.fopen(template_tmp_file, "r") as tfp_: tpl = salt.utils.stringutils.to_unicode(tfp_.read()) salt.utils.files.safe_rm(template_tmp_file) try: template_parse = _parse_template(tpl) - if 'heat_template_version' in template_parse: + if "heat_template_version" in template_parse: template_new = salt.utils.yaml.safe_dump(template_parse) else: - template_new = jsonutils.dumps(template_parse, indent=2, ensure_ascii=False) + template_new = jsonutils.dumps( + template_parse, indent=2, ensure_ascii=False + ) salt.utils.files.safe_rm(template_tmp_file) except ValueError as ex: - ret['result'] = False - ret['comment'] = 'Error parsing template {0}'.format(ex) + ret["result"] = False + ret["comment"] = "Error parsing template {0}".format(ex) else: - ret['result'] = False - ret['comment'] = 'Can not open template: {0} {1}'.format(template, comment_) + ret["result"] = False + ret["comment"] = "Can not open template: {0} {1}".format( + template, comment_ + ) else: - ret['result'] = False - ret['comment'] = 'Can not open template' - if ret['result'] is True: - template_stack = __salt__['heat.template_stack'](name=name, profile=profile) - if not template_stack['result']: - ret['result'] = False - ret['comment'] = template_stack['comment'] - if ret['result'] is False: + ret["result"] = False + ret["comment"] = "Can not open template" + if ret["result"] is True: + template_stack = __salt__["heat.template_stack"](name=name, profile=profile) + if not template_stack["result"]: + ret["result"] = False + ret["comment"] = template_stack["comment"] + if ret["result"] is False: return ret try: - checksum_template = __salt__['hashutil.digest'](template_new) - checksum_stack = __salt__['hashutil.digest'](template_stack['template']) + checksum_template = __salt__["hashutil.digest"](template_new) + checksum_stack = __salt__["hashutil.digest"](template_stack["template"]) except salt.exceptions.CommandExecutionError as cmdexc: - ret['result'] = False - ret['comment'] = '{0}'.format(cmdexc) + ret["result"] = False + ret["comment"] = "{0}".format(cmdexc) - if ret['result'] is True: + if ret["result"] is True: if checksum_template == checksum_stack: - if __opts__['test']: - ret['result'] = True - ret['comment'] = 'Stack {0} is deployed'.format(name) + if __opts__["test"]: + ret["result"] = True + ret["comment"] = "Stack {0} is deployed".format(name) return ret else: - ret['result'] = False - ret['comment'] = 'Templates have same checksum: {0} {1}'\ - .format(checksum_template, checksum_stack) - if ret['result'] is False: + ret["result"] = False + ret["comment"] = "Templates have same checksum: {0} {1}".format( + checksum_template, checksum_stack + ) + if ret["result"] is False: return ret - if __opts__['test']: + if __opts__["test"]: stack = { - 'result': None, - 'comment': 'Stack {0} is set to be updated'.format(name) + "result": None, + "comment": "Stack {0} is set to be updated".format(name), } else: - stack = __salt__['heat.update_stack'](name=name, - template_file=template, - environment=environment, - parameters=params, poll=poll, - rollback=rollback, - timeout=timeout, - profile=profile) - ret['changes']['stack_name'] = name - ret['changes']['comment'] = 'Update stack' + stack = __salt__["heat.update_stack"]( + name=name, + template_file=template, + environment=environment, + parameters=params, + poll=poll, + rollback=rollback, + timeout=timeout, + profile=profile, + ) + ret["changes"]["stack_name"] = name + ret["changes"]["comment"] = "Update stack" else: - if __opts__['test']: + if __opts__["test"]: stack = { - 'result': None, - 'comment': 'Stack {0} is set to be created'.format(name) + "result": None, + "comment": "Stack {0} is set to be created".format(name), } else: - stack = __salt__['heat.create_stack'](name=name, - template_file=template, - environment=environment, - parameters=params, poll=poll, - rollback=rollback, - timeout=timeout, - profile=profile) - ret['changes']['stack_name'] = name - ret['changes']['comment'] = 'Create stack' - ret['result'] = stack['result'] - ret['comment'] = stack['comment'] + stack = __salt__["heat.create_stack"]( + name=name, + template_file=template, + environment=environment, + parameters=params, + poll=poll, + rollback=rollback, + timeout=timeout, + profile=profile, + ) + ret["changes"]["stack_name"] = name + ret["changes"]["comment"] = "Create stack" + ret["result"] = stack["result"] + ret["comment"] = stack["comment"] return ret def absent(name, poll=5, timeout=60, profile=None): - ''' + """ Ensure that the named stack is absent name @@ -299,38 +326,33 @@ def absent(name, poll=5, timeout=60, profile=None): profile Profile to use - ''' - log.debug( - 'Absent with(%s, %s %s)', - name, poll, profile - ) - ret = {'name': None, - 'comment': '', - 'changes': {}, - 'result': True} + """ + log.debug("Absent with(%s, %s %s)", name, poll, profile) + ret = {"name": None, "comment": "", "changes": {}, "result": True} if not name: - ret['result'] = False - ret['comment'] = 'Name ist not valid' + ret["result"] = False + ret["comment"] = "Name ist not valid" return ret - ret['name'] = name, + ret["name"] = (name,) - existing_stack = __salt__['heat.show_stack'](name, profile=profile) + existing_stack = __salt__["heat.show_stack"](name, profile=profile) - if not existing_stack['result']: - ret['result'] = True - ret['comment'] = 'Stack not exist' + if not existing_stack["result"]: + ret["result"] = True + ret["comment"] = "Stack not exist" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Stack {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Stack {0} is set to be removed".format(name) return ret - stack = __salt__['heat.delete_stack'](name=name, poll=poll, - timeout=timeout, profile=profile) + stack = __salt__["heat.delete_stack"]( + name=name, poll=poll, timeout=timeout, profile=profile + ) - ret['result'] = stack['result'] - ret['comment'] = stack['comment'] - ret['changes']['stack_name'] = name - ret['changes']['comment'] = 'Delete stack' + ret["result"] = stack["result"] + ret["comment"] = stack["comment"] + ret["changes"]["stack_name"] = name + ret["changes"]["comment"] = "Delete stack" return ret diff --git a/salt/states/helm.py b/salt/states/helm.py new file mode 100644 index 00000000000..1f1aabf9642 --- /dev/null +++ b/salt/states/helm.py @@ -0,0 +1,365 @@ +# -*- coding: utf-8 -*- + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +import logging + +# Import Salt libs +from salt.exceptions import CommandExecutionError + +log = logging.getLogger(__name__) + + +def repo_managed( + name, + present=None, + absent=None, + prune=False, + repo_update=False, + namespace=None, + flags=None, + kvflags=None, +): + """ + Make sure the repository is updated. + + name + (string) Not used. + + present + (list) List of repository to be present. It's a list of dict: [{'name': 'local_name', 'url': 'repository_url'}] + + absent + (list) List of local name repository to be absent. + + prune + (boolean - default: False) If True, all repository already present but not in the present list would be removed. + + repo_update + (boolean - default: False) If True, the Helm repository is updated after a repository add or remove. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + Example: + + .. code-block:: yaml + + helm_repository_is_managed: + helm.repo_managed: + - present: + - name: local_name_1 + url: repository_url + - absent: + - local_name_2 + + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Helm repo is managed.", + } + + if "helm.repo_manage" not in __salt__: + ret["result"] = False + ret["comment"] = "'helm.repo_manage' modules not available on this minion." + elif "helm.repo_update" not in __salt__: + ret["result"] = False + ret["comment"] = "'helm.repo_update' modules not available on this minion." + elif __opts__.get("test", False): + ret["result"] = None + ret["comment"] = "Helm repo would have been managed." + else: + try: + result = __salt__["helm.repo_manage"]( + present=present, + absent=absent, + prune=prune, + namespace=namespace, + flags=flags, + kvflags=kvflags, + ) + + if result["failed"]: + ret["comment"] = "Failed to add or remove some repositories." + ret["changes"] = result + ret["result"] = False + + elif result["added"] or result["removed"]: + if repo_update: + result_repo_update = __salt__["helm.repo_update"]( + namespace=namespace, flags=flags, kvflags=kvflags + ) + result.update({"repo_update": result_repo_update}) + + ret["comment"] = "Repositories were added or removed." + ret["changes"] = result + + except CommandExecutionError as err: + ret["result"] = False + ret["comment"] = "Failed to add some repositories: {}.".format(err) + + return ret + + +def repo_updated(name, namespace=None, flags=None, kvflags=None): + """ + Make sure the repository is updated. + To execute after a repository changes. + + name + (string) Not used. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + Example: + + .. code-block:: yaml + + helm_repository_is_updated: + helm.repo_updated + + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Helm repo is updated.", + } + + if "helm.repo_update" not in __salt__: + ret["result"] = False + ret["comment"] = "'helm.repo_update' modules not available on this minion." + elif __opts__.get("test", False): + ret["result"] = None + ret["comment"] = "Helm repo would have been updated." + else: + try: + result = __salt__["helm.repo_update"]( + namespace=namespace, flags=flags, kvflags=kvflags + ) + if not (isinstance(result, bool) and result): + ret["result"] = False + ret["changes"] = result + ret["comment"] = "Failed to sync some repositories." + + except CommandExecutionError as err: + ret["result"] = False + ret["comment"] = "Failed to update some repositories: {}.".format(err) + + return ret + + +def release_present( + name, + chart, + values=None, + version=None, + namespace=None, + set=None, + flags=None, + kvflags=None, +): + """ + Make sure the release name is present. + + name + (string) The release name to install. + + chart + (string) The chart to install. + + values + (string) Absolute path to the values.yaml file. + + version + (string) The exact chart version to install. If this is not specified, the latest version is installed. + + namespace + (string) The namespace scope for this request. + + set + (string or list) Set a values on the command line. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + Example: + + .. code-block:: yaml + + helm_release_is_present: + helm.release_present: + - name: release_name + - chart: repo/chart + + # In dry-run mode. + helm_release_is_present_dry-run: + helm.release_present: + - name: release_name + - chart: repo/chart + - flags: + - dry-run + + # With values.yaml file. + helm_release_is_present_values: + helm.release_present: + - name: release_name + - chart: repo/chart + - kvflags: + values: /path/to/values.yaml + + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Helm release {} is present".format(name), + } + + if "helm.status" not in __salt__: + ret["result"] = False + ret["comment"] = "'helm.status' modules not available on this minion." + elif "helm.install" not in __salt__: + ret["result"] = False + ret["comment"] = "'helm.install' modules not available on this minion." + elif "helm.upgrade" not in __salt__: + ret["result"] = False + ret["comment"] = "'helm.upgrade' modules not available on this minion." + elif __opts__.get("test", False): + ret["result"] = None + ret["comment"] = "Helm release would have been installed or updated." + else: + release_old_status = __salt__["helm.status"](release=name, namespace=namespace) + if isinstance(release_old_status, dict): + release_upgrade = __salt__["helm.upgrade"]( + release=name, + chart=chart, + values=values, + version=version, + namespace=namespace, + set=set, + flags=flags, + kvflags=kvflags, + ) + if isinstance(release_upgrade, bool) and release_upgrade: + release_cur_status = __salt__["helm.status"]( + release=name, namespace=namespace + ) + if isinstance(release_cur_status, dict): + release_cur_status.pop("manifest") + ret["changes"] = release_cur_status + else: + ret["result"] = False + ret["comment"] = release_cur_status + else: + ret["result"] = False + ret["comment"] = release_upgrade + + else: + release_install = __salt__["helm.install"]( + release=name, + chart=chart, + values=values, + version=version, + namespace=namespace, + set=set, + flags=flags, + kvflags=kvflags, + ) + if isinstance(release_install, bool) and release_install: + release_cur_status = __salt__["helm.status"]( + release=name, namespace=namespace + ) + if isinstance(release_cur_status, dict): + release_cur_status.pop("manifest") + ret["changes"] = release_cur_status + else: + ret["result"] = False + ret["comment"] = release_cur_status + else: + ret["result"] = False + ret["comment"] = release_install + + return ret + + +def release_absent(name, namespace=None, flags=None, kvflags=None): + """ + Make sure the release name is absent. + + name + (string) The release name to uninstall. + + namespace + (string) The namespace scope for this request. + + flags + (list) Flags in argument of the command without values. ex: ['help', '--help'] + + kvflags + (dict) Flags in argument of the command with values. ex: {'v': 2, '--v': 4} + + Example: + + .. code-block:: yaml + + helm_release_is_absent: + helm.release_absent: + - name: release_name + + # In dry-run mode. + helm_release_is_absent_dry-run: + helm.release_absent: + - name: release_name + - flags: + - dry-run + + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Helm release {} is absent.".format(name), + } + + if "helm.uninstall" not in __salt__: + ret["result"] = False + ret["comment"] = "'helm.uninstall' modules not available on this minion." + elif "helm.status" not in __salt__: + ret["result"] = False + ret["comment"] = "'helm.status' modules not available on this minion." + elif __opts__.get("test", False): + ret["result"] = None + ret["comment"] = "Helm release would have been uninstalled." + else: + release_status = __salt__["helm.status"](release=name, namespace=namespace) + if isinstance(release_status, dict): + release_uninstall = __salt__["helm.uninstall"]( + release=name, namespace=namespace, flags=flags, kvflags=kvflags + ) + if isinstance(release_uninstall, bool) and release_uninstall: + ret["changes"] = {"absent": name} + else: + ret["result"] = False + ret["comment"] = release_uninstall + + return ret diff --git a/salt/states/hg.py b/salt/states/hg.py index 79f3e5fa40e..ecb4f7c7317 100644 --- a/salt/states/hg.py +++ b/salt/states/hg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Interaction with Mercurial repositories ======================================= @@ -12,10 +12,11 @@ in ~/.ssh/known_hosts, and the remote host has this host's public key. hg.latest: - rev: tip - target: /tmp/example_repo -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import shutil @@ -27,26 +28,30 @@ from salt.states.git import _fail, _neutral_test log = logging.getLogger(__name__) -HG_BINARY = 'hg.exe' if salt.utils.platform.is_windows() else 'hg' +HG_BINARY = "hg.exe" if salt.utils.platform.is_windows() else "hg" def __virtual__(): - ''' + """ Only load if hg is available - ''' - return __salt__['cmd.has_exec'](HG_BINARY) + """ + if __salt__["cmd.has_exec"](HG_BINARY): + return True + return (False, "Command {0} not found".format(HG_BINARY)) -def latest(name, - rev=None, - target=None, - clean=False, - user=None, - identity=None, - force=False, - opts=False, - update_head=True): - ''' +def latest( + name, + rev=None, + target=None, + clean=False, + user=None, + identity=None, + force=False, + opts=False, + update_head=True, +): + """ Make sure the repository is cloned to the given directory and is up to date name @@ -82,18 +87,18 @@ def latest(name, .. versionadded:: 2017.7.0 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not target: return _fail(ret, '"target option is required') - is_repository = ( - os.path.isdir(target) and - os.path.isdir('{0}/.hg'.format(target))) + is_repository = os.path.isdir(target) and os.path.isdir("{0}/.hg".format(target)) if is_repository: - ret = _update_repo(ret, name, target, clean, user, identity, rev, opts, update_head) + ret = _update_repo( + ret, name, target, clean, user, identity, rev, opts, update_head + ) else: if os.path.isdir(target): fail = _handle_existing(ret, target, force) @@ -101,99 +106,104 @@ def latest(name, return fail else: log.debug('target %s is not found, "hg clone" is required', target) - if __opts__['test']: + if __opts__["test"]: return _neutral_test( - ret, - 'Repository {0} is about to be cloned to {1}'.format( - name, target)) + ret, "Repository {0} is about to be cloned to {1}".format(name, target) + ) _clone_repo(ret, target, name, user, identity, rev, opts) return ret def _update_repo(ret, name, target, clean, user, identity, rev, opts, update_head): - ''' + """ Update the repo to a given revision. Using clean passes -C to the hg up - ''' + """ log.debug('target %s is found, "hg pull && hg up is probably required"', target) - current_rev = __salt__['hg.revision'](target, user=user, rev='.') + current_rev = __salt__["hg.revision"](target, user=user, rev=".") if not current_rev: - return _fail( - ret, - 'Seems that {0} is not a valid hg repo'.format(target)) + return _fail(ret, "Seems that {0} is not a valid hg repo".format(target)) - if __opts__['test']: + if __opts__["test"]: test_result = ( - 'Repository {0} update is probably required (current ' - 'revision is {1})').format(target, current_rev) - return _neutral_test( - ret, - test_result) + "Repository {0} update is probably required (current " "revision is {1})" + ).format(target, current_rev) + return _neutral_test(ret, test_result) try: - pull_out = __salt__['hg.pull'](target, user=user, identity=identity, opts=opts, repository=name) + pull_out = __salt__["hg.pull"]( + target, user=user, identity=identity, opts=opts, repository=name + ) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = err + ret["result"] = False + ret["comment"] = err return ret if update_head is False: - changes = 'no changes found' not in pull_out + changes = "no changes found" not in pull_out if changes: - ret['comment'] = 'Update is probably required but update_head=False so we will skip updating.' + ret[ + "comment" + ] = "Update is probably required but update_head=False so we will skip updating." else: - ret['comment'] = 'No changes found and update_head=False so will skip updating.' + ret[ + "comment" + ] = "No changes found and update_head=False so will skip updating." return ret if rev: try: - __salt__['hg.update'](target, rev, force=clean, user=user) + __salt__["hg.update"](target, rev, force=clean, user=user) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = err + ret["result"] = False + ret["comment"] = err return ret else: try: - __salt__['hg.update'](target, 'tip', force=clean, user=user) + __salt__["hg.update"](target, "tip", force=clean, user=user) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = err + ret["result"] = False + ret["comment"] = err return ret - new_rev = __salt__['hg.revision'](cwd=target, user=user, rev='.') + new_rev = __salt__["hg.revision"](cwd=target, user=user, rev=".") if current_rev != new_rev: - revision_text = '{0} => {1}'.format(current_rev, new_rev) - log.info('Repository %s updated: %s', target, revision_text) - ret['comment'] = 'Repository {0} updated.'.format(target) - ret['changes']['revision'] = revision_text - elif 'error:' in pull_out: - return _fail( - ret, - 'An error was thrown by hg:\n{0}'.format(pull_out) - ) + revision_text = "{0} => {1}".format(current_rev, new_rev) + log.info("Repository %s updated: %s", target, revision_text) + ret["comment"] = "Repository {0} updated.".format(target) + ret["changes"]["revision"] = revision_text + elif "error:" in pull_out: + return _fail(ret, "An error was thrown by hg:\n{0}".format(pull_out)) return ret def _handle_existing(ret, target, force): not_empty = os.listdir(target) if not not_empty: - log.debug('target %s found, but directory is empty, automatically deleting', target) + log.debug( + "target %s found, but directory is empty, automatically deleting", target + ) shutil.rmtree(target) elif force: - log.debug('target %s found and is not empty. ' - 'Since force option is in use, deleting anyway.', target) + log.debug( + "target %s found and is not empty. " + "Since force option is in use, deleting anyway.", + target, + ) shutil.rmtree(target) else: - return _fail(ret, 'Directory exists, and is not empty') + return _fail(ret, "Directory exists, and is not empty") def _clone_repo(ret, target, name, user, identity, rev, opts): try: - result = __salt__['hg.clone'](target, name, user=user, identity=identity, opts=opts) + result = __salt__["hg.clone"]( + target, name, user=user, identity=identity, opts=opts + ) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = err + ret["result"] = False + ret["comment"] = err return ret if not os.path.isdir(target): @@ -201,18 +211,18 @@ def _clone_repo(ret, target, name, user, identity, rev, opts): if rev: try: - __salt__['hg.update'](target, rev, user=user) + __salt__["hg.update"](target, rev, user=user) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = err + ret["result"] = False + ret["comment"] = err return ret - new_rev = __salt__['hg.revision'](cwd=target, user=user) - message = 'Repository {0} cloned to {1}'.format(name, target) + new_rev = __salt__["hg.revision"](cwd=target, user=user) + message = "Repository {0} cloned to {1}".format(name, target) log.info(message) - ret['comment'] = message + ret["comment"] = message - ret['changes']['new'] = name - ret['changes']['revision'] = new_rev + ret["changes"]["new"] = name + ret["changes"]["revision"] = new_rev return ret diff --git a/salt/states/highstate_doc.py b/salt/states/highstate_doc.py index 8e33294e203..3d79f3d43ea 100644 --- a/salt/states/highstate_doc.py +++ b/salt/states/highstate_doc.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" To be used with proccessors in module `highstate_doc`. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'highstate_doc' +__virtualname__ = "highstate_doc" def note(name, source=None, contents=None, **kwargs): - ''' + """ Add content to a document generated using `highstate_doc.render`. This state does not preform any tasks on the host. It only is used in highstate_doc lowstate proccessers @@ -36,10 +36,10 @@ def note(name, source=None, contents=None, **kwargs): - name: example - order: 0 - source: salt://{{tpldir}}/HELP.md - ''' - comment = '' + """ + comment = "" if source: - comment += 'include file: {0}\n'.format(source) + comment += "include file: {0}\n".format(source) if contents and len(contents) < 200: comment += contents - return {'name': name, 'result': True, 'comment': comment, 'changes': {}} + return {"name": name, "result": True, "comment": comment, "changes": {}} diff --git a/salt/states/host.py b/salt/states/host.py index 9ec24161132..5d6e3fcd1ec 100644 --- a/salt/states/host.py +++ b/salt/states/host.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of addresses and names in hosts file =============================================== @@ -57,18 +57,35 @@ Or delete all existing names for an address: host.only: - hostnames: [] -''' +You can also include comments: + +.. code-block:: yaml + + server1: + host.present: + - ip: 192.168.0.42 + - names: + - server1 + - florida + - comment: A very important comment + +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt libs -from salt.ext import six +import logging + import salt.utils.validate.net +# Import Salt libs +from salt.ext import six -def present(name, ip, clean=False): # pylint: disable=C0103 - ''' +log = logging.getLogger(__name__) + + +def present(name, ip, comment="", clean=False): # pylint: disable=C0103 + """ Ensures that the named host is present with the given ip name @@ -78,97 +95,126 @@ def present(name, ip, clean=False): # pylint: disable=C0103 The ip addr(s) to apply to the host. Can be a single IP or a list of IP addresses. + comment + A comment to include for the host entry + + .. versionadded:: Sodium + clean Remove any entries which don't match those configured in the ``ip`` option. Default is ``False``. .. versionadded:: 2018.3.4 - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not isinstance(ip, list): ip = [ip] - all_hosts = __salt__['hosts.list_hosts']() + all_hosts = __salt__["hosts.list_hosts"]() comments = [] to_add = set() to_remove = set() + update_comment = set() # First check for IPs not currently in the hosts file to_add.update([(addr, name) for addr in ip if addr not in all_hosts]) + if comment: + update_comment.update([(addr, comment) for addr in ip if addr not in all_hosts]) + # Now sweep through the hosts file and look for entries matching either the # IP address(es) or hostname. - for addr, aliases in six.iteritems(all_hosts): + for addr, host_info in six.iteritems(all_hosts): if addr not in ip: - if name in aliases: + if "aliases" in host_info and name in host_info["aliases"]: # Found match for hostname, but the corresponding IP is not in # our list, so we need to remove it. if clean: to_remove.add((addr, name)) else: - ret.setdefault('warnings', []).append( - 'Host {0} present for IP address {1}. To get rid of ' - 'this warning, either run this state with \'clean\' ' - 'set to True to remove {0} from {1}, or add {1} to ' - 'the \'ip\' argument.'.format(name, addr) + ret.setdefault("warnings", []).append( + "Host {0} present for IP address {1}. To get rid of " + "this warning, either run this state with 'clean' " + "set to True to remove {0} from {1}, or add {1} to " + "the 'ip' argument.".format(name, addr) ) else: - if name in aliases: - # No changes needed for this IP address and hostname - comments.append( - 'Host {0} ({1}) already present'.format(name, addr) - ) + if "aliases" in host_info and name in host_info["aliases"]: + if ( + comment + and "comment" in host_info + and host_info["comment"] != comment + ): + update_comment.add((addr, comment)) + elif comment and "comment" not in host_info: + update_comment.add((addr, comment)) + else: + # No changes needed for this IP address and hostname + comments.append("Host {0} ({1}) already present".format(name, addr)) else: # IP address listed in hosts file, but hostname is not present. # We will need to add it. if salt.utils.validate.net.ip_addr(addr): to_add.add((addr, name)) + if comment: + update_comment.add((addr, comment)) else: - ret['result'] = False + ret["result"] = False comments.append( - 'Invalid IP Address for {0} ({1})'.format(name, addr) + "Invalid IP Address for {0} ({1})".format(name, addr) ) for addr, name in to_add: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None + comments.append("Host {0} ({1}) would be added".format(name, addr)) + else: + if __salt__["hosts.add_host"](addr, name): + comments.append("Added host {0} ({1})".format(name, addr)) + else: + ret["result"] = False + comments.append("Failed to add host {0} ({1})".format(name, addr)) + continue + ret["changes"].setdefault("added", {}).setdefault(addr, []).append(name) + + for addr, comment in update_comment: + if __opts__["test"]: comments.append( - 'Host {0} ({1}) would be added'.format(name, addr) + "Comment for {0} ({1}) would be added".format(addr, comment) ) else: - if __salt__['hosts.add_host'](addr, name): - comments.append('Added host {0} ({1})'.format(name, addr)) + if __salt__["hosts.set_comment"](addr, comment): + comments.append("Set comment for host {0} ({1})".format(addr, comment)) else: - ret['result'] = False - comments.append('Failed to add host {0} ({1})'.format(name, addr)) + ret["result"] = False + comments.append( + "Failed to add comment for host {0} ({1})".format(addr, comment) + ) continue - ret['changes'].setdefault('added', {}).setdefault(addr, []).append(name) + ret["changes"].setdefault("comment_added", {}).setdefault(addr, []).append( + comment + ) for addr, name in to_remove: - if __opts__['test']: - ret['result'] = None - comments.append( - 'Host {0} ({1}) would be removed'.format(name, addr) - ) + if __opts__["test"]: + ret["result"] = None + comments.append("Host {0} ({1}) would be removed".format(name, addr)) else: - if __salt__['hosts.rm_host'](addr, name): - comments.append('Removed host {0} ({1})'.format(name, addr)) + if __salt__["hosts.rm_host"](addr, name): + comments.append("Removed host {0} ({1})".format(name, addr)) else: - ret['result'] = False - comments.append('Failed to remove host {0} ({1})'.format(name, addr)) + ret["result"] = False + comments.append("Failed to remove host {0} ({1})".format(name, addr)) continue - ret['changes'].setdefault('removed', {}).setdefault(addr, []).append(name) + ret["changes"].setdefault("removed", {}).setdefault(addr, []).append(name) - ret['comment'] = '\n'.join(comments) + ret["comment"] = "\n".join(comments) return ret def absent(name, ip): # pylint: disable=C0103 - ''' + """ Ensure that the named host is absent name @@ -176,37 +222,34 @@ def absent(name, ip): # pylint: disable=C0103 ip The ip addr(s) of the host to remove - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} if not isinstance(ip, list): ip = [ip] comments = [] for _ip in ip: - if not __salt__['hosts.has_pair'](_ip, name): - ret['result'] = True - comments.append('Host {0} ({1}) already absent'.format(name, _ip)) + if not __salt__["hosts.has_pair"](_ip, name): + ret["result"] = True + comments.append("Host {0} ({1}) already absent".format(name, _ip)) else: - if __opts__['test']: - comments.append('Host {0} ({1}) needs to be removed'.format(name, _ip)) + if __opts__["test"]: + comments.append("Host {0} ({1}) needs to be removed".format(name, _ip)) else: - if __salt__['hosts.rm_host'](_ip, name): - ret['changes'] = {'host': name} - ret['result'] = True - comments.append('Removed host {0} ({1})'.format(name, _ip)) + if __salt__["hosts.rm_host"](_ip, name): + ret["changes"] = {"host": name} + ret["result"] = True + comments.append("Removed host {0} ({1})".format(name, _ip)) else: - ret['result'] = False - comments.append('Failed to remove host') - ret['comment'] = '\n'.join(comments) + ret["result"] = False + comments.append("Failed to remove host") + ret["comment"] = "\n".join(comments) return ret def only(name, hostnames): - ''' + """ Ensure that only the given hostnames are associated with the given IP address. @@ -221,36 +264,33 @@ def only(name, hostnames): hostname associated with the IP address is removed. If no hostnames are specified, all hostnames associated with the given IP address are removed. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} if isinstance(hostnames, six.string_types): hostnames = [hostnames] - old = ' '.join(__salt__['hosts.get_alias'](name)) - new = ' '.join((x.strip() for x in hostnames)) + old = " ".join(__salt__["hosts.get_alias"](name)) + new = " ".join((x.strip() for x in hostnames)) if old == new: - ret['comment'] = 'IP address {0} already set to "{1}"'.format( - name, new) - ret['result'] = True + ret["comment"] = 'IP address {0} already set to "{1}"'.format(name, new) + ret["result"] = True return ret - if __opts__['test']: - ret['comment'] = 'Would change {0} from "{1}" to "{2}"'.format( - name, old, new) + if __opts__["test"]: + ret["comment"] = 'Would change {0} from "{1}" to "{2}"'.format(name, old, new) return ret - ret['result'] = __salt__['hosts.set_host'](name, new) - if not ret['result']: - ret['comment'] = ('hosts.set_host failed to change {0}' - + ' from "{1}" to "{2}"').format(name, old, new) + ret["result"] = __salt__["hosts.set_host"](name, new) + if not ret["result"]: + ret["comment"] = ( + "hosts.set_host failed to change {0}" + ' from "{1}" to "{2}"' + ).format(name, old, new) return ret - ret['comment'] = 'successfully changed {0} from "{1}" to "{2}"'.format( - name, old, new) - ret['changes'] = {name: {'old': old, 'new': new}} + ret["comment"] = 'successfully changed {0} from "{1}" to "{2}"'.format( + name, old, new + ) + ret["changes"] = {name: {"old": old, "new": new}} return ret diff --git a/salt/states/http.py b/salt/states/http.py index 0d342a60922..7b23e466ca1 100644 --- a/salt/states/http.py +++ b/salt/states/http.py @@ -1,27 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" HTTP monitoring states Perform an HTTP query and statefully return the result .. versionadded:: 2015.5.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import re import time __monitor__ = [ - 'query', - ] + "query", +] log = logging.getLogger(__name__) -def query(name, match=None, match_type='string', status=None, status_type='string', wait_for=None, **kwargs): - ''' +def query( + name, + match=None, + match_type="string", + status=None, + status_type="string", + wait_for=None, + **kwargs +): + """ Perform an HTTP query and statefully return the result Passes through all the parameters described in the @@ -88,97 +97,101 @@ def query(name, match=None, match_type='string', status=None, status_type='strin - 201 - status_type: list - ''' + """ # Monitoring state, but changes may be made over HTTP - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}, - 'data': {}} # Data field for monitoring state + ret = { + "name": name, + "result": None, + "comment": "", + "changes": {}, + "data": {}, + } # Data field for monitoring state if match is None and status is None: - ret['result'] = False - ret['comment'] += ( - ' Either match text (match) or a status code (status) is required.' - ) + ret["result"] = False + ret[ + "comment" + ] += " Either match text (match) or a status code (status) is required." return ret - if 'decode' not in kwargs: - kwargs['decode'] = False - kwargs['text'] = True - kwargs['status'] = True - if __opts__['test']: - kwargs['test'] = True + if "decode" not in kwargs: + kwargs["decode"] = False + kwargs["text"] = True + kwargs["status"] = True + if __opts__["test"]: + kwargs["test"] = True if wait_for: - data = __salt__['http.wait_for_successful_query'](name, wait_for=wait_for, **kwargs) + data = __salt__["http.wait_for_successful_query"]( + name, wait_for=wait_for, **kwargs + ) else: - data = __salt__['http.query'](name, **kwargs) + data = __salt__["http.query"](name, **kwargs) if match is not None: - if match_type == 'string': - if str(match) in data.get('text', ''): - ret['result'] = True - ret['comment'] += ' Match text "{0}" was found.'.format(match) + if match_type == "string": + if str(match) in data.get("text", ""): + ret["result"] = True + ret["comment"] += ' Match text "{0}" was found.'.format(match) else: - ret['result'] = False - ret['comment'] += ' Match text "{0}" was not found.'.format(match) - elif match_type == 'pcre': - if re.search(str(match), str(data.get('text', ''))): - ret['result'] = True - ret['comment'] += ' Match pattern "{0}" was found.'.format(match) + ret["result"] = False + ret["comment"] += ' Match text "{0}" was not found.'.format(match) + elif match_type == "pcre": + if re.search(str(match), str(data.get("text", ""))): + ret["result"] = True + ret["comment"] += ' Match pattern "{0}" was found.'.format(match) else: - ret['result'] = False - ret['comment'] += ' Match pattern "{0}" was not found.'.format(match) + ret["result"] = False + ret["comment"] += ' Match pattern "{0}" was not found.'.format(match) if status is not None: # Deals with case of status_type as a list of strings representing statuses - if status_type == 'list': + if status_type == "list": for stat in status: - if str(data.get('status', '')) == str(stat): - ret['comment'] += ' Status {0} was found.'.format(stat) - if ret['result'] is None: - ret['result'] = True - if ret['result'] is not True: - ret['comment'] += ' Statuses {0} were not found.'.format(status) - ret['result'] = False + if str(data.get("status", "")) == str(stat): + ret["comment"] += " Status {0} was found.".format(stat) + if ret["result"] is None: + ret["result"] = True + if ret["result"] is not True: + ret["comment"] += " Statuses {0} were not found.".format(status) + ret["result"] = False # Deals with the case of status_type representing a regex - elif status_type == 'pcre': - if re.search(str(status), str(data.get('status', ''))): - ret['comment'] += ' Status pattern "{0}" was found.'.format(status) - if ret['result'] is None: - ret['result'] = True + elif status_type == "pcre": + if re.search(str(status), str(data.get("status", ""))): + ret["comment"] += ' Status pattern "{0}" was found.'.format(status) + if ret["result"] is None: + ret["result"] = True else: - ret['comment'] += ' Status pattern "{0}" was not found.'.format(status) - ret['result'] = False + ret["comment"] += ' Status pattern "{0}" was not found.'.format(status) + ret["result"] = False # Deals with the case of status_type as a single string representing a status - elif status_type == 'string': - if str(data.get('status', '')) == str(status): - ret['comment'] += ' Status {0} was found.'.format(status) - if ret['result'] is None: - ret['result'] = True + elif status_type == "string": + if str(data.get("status", "")) == str(status): + ret["comment"] += " Status {0} was found.".format(status) + if ret["result"] is None: + ret["result"] = True else: - ret['comment'] += ' Status {0} was not found.'.format(status) - ret['result'] = False + ret["comment"] += " Status {0} was not found.".format(status) + ret["result"] = False # cleanup spaces in comment - ret['comment'] = ret['comment'].strip() + ret["comment"] = ret["comment"].strip() - if __opts__['test'] is True: - ret['result'] = None - ret['comment'] += ' (TEST MODE' - if 'test_url' in kwargs: - ret['comment'] += ', TEST URL WAS: {0}'.format(kwargs['test_url']) - ret['comment'] += ')' + if __opts__["test"] is True: + ret["result"] = None + ret["comment"] += " (TEST MODE" + if "test_url" in kwargs: + ret["comment"] += ", TEST URL WAS: {0}".format(kwargs["test_url"]) + ret["comment"] += ")" - ret['data'] = data + ret["data"] = data return ret def wait_for_successful_query(name, wait_for=300, **kwargs): - ''' + """ Like query but, repeat and wait until match/match_type or status is fulfilled. State returns result from last query state in case of success or if no successful query was made within wait_for timeout. @@ -194,7 +207,7 @@ def wait_for_successful_query(name, wait_for=300, **kwargs): .. note:: All other arguments are passed to the http.query state. - ''' + """ starttime = time.time() while True: @@ -202,7 +215,7 @@ def wait_for_successful_query(name, wait_for=300, **kwargs): ret = None try: ret = query(name, **kwargs) - if ret['result']: + if ret["result"]: return ret except Exception as exc: # pylint: disable=broad-except caught_exception = exc @@ -212,7 +225,7 @@ def wait_for_successful_query(name, wait_for=300, **kwargs): # workaround pylint bug https://www.logilab.org/ticket/3207 raise caught_exception # pylint: disable=E0702 return ret - elif 'request_interval' in kwargs: + elif "request_interval" in kwargs: # Space requests out by delaying for an interval - log.debug('delaying query for %s seconds.', kwargs['request_interval']) - time.sleep(kwargs['request_interval']) + log.debug("delaying query for %s seconds.", kwargs["request_interval"]) + time.sleep(kwargs["request_interval"]) diff --git a/salt/states/icinga2.py b/salt/states/icinga2.py index e64a3b967b5..27d369bec19 100644 --- a/salt/states/icinga2.py +++ b/salt/states/icinga2.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Icinga2 state ============= @@ -17,28 +17,32 @@ Its output may be stored in a file or in a grain. icinga2.generate_ticket: - name: domain.tld - output: "/tmp/query_id.txt" -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os.path +import salt.utils.files +import salt.utils.stringutils + # Import Salt libs from salt.ext import six -import salt.utils.files -import salt.utils.stringutils from salt.utils.icinga2 import get_certs_path def __virtual__(): - ''' + """ Only load if the icinga2 module is available in __salt__ - ''' - return 'icinga2.generate_ticket' in __salt__ + """ + if "icinga2.generate_ticket" in __salt__: + return True + return (False, "icinga2 module could not be loaded") def generate_ticket(name, output=None, grain=None, key=None, overwrite=True): - ''' + """ Generate an icinga2 ticket on the master. name @@ -58,113 +62,129 @@ def generate_ticket(name, output=None, grain=None, key=None, overwrite=True): overwrite: The file or grain will be overwritten if it already exists (default) - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # Checking if execution is needed. - if output == 'grain': + if output == "grain": if grain and not key: - if not overwrite and grain in __salt__['grains.ls'](): - ret['comment'] = 'No execution needed. Grain {0} already set'.format(grain) + if not overwrite and grain in __salt__["grains.ls"](): + ret["comment"] = "No execution needed. Grain {0} already set".format( + grain + ) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Ticket generation would be executed, storing result in grain: {0}'.format(grain) + elif __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "Ticket generation would be executed, storing result in grain: {0}".format( + grain + ) return ret elif grain: - if grain in __salt__['grains.ls'](): - grain_value = __salt__['grains.get'](grain) + if grain in __salt__["grains.ls"](): + grain_value = __salt__["grains.get"](grain) else: grain_value = {} if not overwrite and key in grain_value: - ret['comment'] = 'No execution needed. Grain {0}:{1} already set'.format(grain, key) + ret[ + "comment" + ] = "No execution needed. Grain {0}:{1} already set".format(grain, key) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Ticket generation would be executed, storing result in grain: {0}:{1}'.format(grain, key) + elif __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "Ticket generation would be executed, storing result in grain: {0}:{1}".format( + grain, key + ) return ret else: - ret['result'] = False - ret['comment'] = "Error: output type 'grain' needs the grain parameter\n" + ret["result"] = False + ret["comment"] = "Error: output type 'grain' needs the grain parameter\n" return ret elif output: if not overwrite and os.path.isfile(output): - ret['comment'] = 'No execution needed. File {0} already set'.format(output) + ret["comment"] = "No execution needed. File {0} already set".format(output) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Ticket generation would be executed, storing result in file: {0}'.format(output) + elif __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "Ticket generation would be executed, storing result in file: {0}".format( + output + ) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Ticket generation would be executed, not storing result' + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = "Ticket generation would be executed, not storing result" return ret # Executing the command. - ticket_res = __salt__['icinga2.generate_ticket'](name) - ticket = ticket_res['stdout'] - if not ticket_res['retcode']: - ret['comment'] = six.text_type(ticket) + ticket_res = __salt__["icinga2.generate_ticket"](name) + ticket = ticket_res["stdout"] + if not ticket_res["retcode"]: + ret["comment"] = six.text_type(ticket) - if output == 'grain': + if output == "grain": if grain and not key: - __salt__['grains.setval'](grain, ticket) - ret['changes']['ticket'] = "Executed. Output into grain: {0}".format(grain) + __salt__["grains.setval"](grain, ticket) + ret["changes"]["ticket"] = "Executed. Output into grain: {0}".format(grain) elif grain: - if grain in __salt__['grains.ls'](): - grain_value = __salt__['grains.get'](grain) + if grain in __salt__["grains.ls"](): + grain_value = __salt__["grains.get"](grain) else: grain_value = {} grain_value[key] = ticket - __salt__['grains.setval'](grain, grain_value) - ret['changes']['ticket'] = "Executed. Output into grain: {0}:{1}".format(grain, key) + __salt__["grains.setval"](grain, grain_value) + ret["changes"]["ticket"] = "Executed. Output into grain: {0}:{1}".format( + grain, key + ) elif output: - ret['changes']['ticket'] = "Executed. Output into {0}".format(output) - with salt.utils.files.fopen(output, 'w') as output_file: + ret["changes"]["ticket"] = "Executed. Output into {0}".format(output) + with salt.utils.files.fopen(output, "w") as output_file: output_file.write(salt.utils.stringutils.to_str(ticket)) else: - ret['changes']['ticket'] = "Executed" + ret["changes"]["ticket"] = "Executed" return ret def generate_cert(name): - ''' + """ Generate an icinga2 certificate and key on the client. name The domain name for which this certificate and key will be generated - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} cert = "{0}{1}.crt".format(get_certs_path(), name) key = "{0}{1}.key".format(get_certs_path(), name) # Checking if execution is needed. if os.path.isfile(cert) and os.path.isfile(key): - ret['comment'] = 'No execution needed. Cert: {0} and key: {1} already generated.'.format(cert, key) + ret[ + "comment" + ] = "No execution needed. Cert: {0} and key: {1} already generated.".format( + cert, key + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Certificate and key generation would be executed' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Certificate and key generation would be executed" return ret # Executing the command. - cert_save = __salt__['icinga2.generate_cert'](name) - if not cert_save['retcode']: - ret['comment'] = "Certificate and key generated" - ret['changes']['cert'] = "Executed. Certificate saved: {0}".format(cert) - ret['changes']['key'] = "Executed. Key saved: {0}".format(key) + cert_save = __salt__["icinga2.generate_cert"](name) + if not cert_save["retcode"]: + ret["comment"] = "Certificate and key generated" + ret["changes"]["cert"] = "Executed. Certificate saved: {0}".format(cert) + ret["changes"]["key"] = "Executed. Key saved: {0}".format(key) return ret def save_cert(name, master): - ''' + """ Save the certificate on master icinga2 node. name @@ -172,32 +192,29 @@ def save_cert(name, master): master Icinga2 master node for which this certificate will be saved - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} cert = "{0}trusted-master.crt".format(get_certs_path()) # Checking if execution is needed. if os.path.isfile(cert): - ret['comment'] = 'No execution needed. Cert: {0} already saved.'.format(cert) + ret["comment"] = "No execution needed. Cert: {0} already saved.".format(cert) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Certificate save for icinga2 master would be executed' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Certificate save for icinga2 master would be executed" return ret # Executing the command. - cert_save = __salt__['icinga2.save_cert'](name, master) - if not cert_save['retcode']: - ret['comment'] = "Certificate for icinga2 master saved" - ret['changes']['cert'] = "Executed. Certificate saved: {0}".format(cert) + cert_save = __salt__["icinga2.save_cert"](name, master) + if not cert_save["retcode"]: + ret["comment"] = "Certificate for icinga2 master saved" + ret["changes"]["cert"] = "Executed. Certificate saved: {0}".format(cert) return ret def request_cert(name, master, ticket, port="5665"): - ''' + """ Request CA certificate from master icinga2 node. name @@ -211,36 +228,35 @@ def request_cert(name, master, ticket, port="5665"): port Icinga2 port, defaults to 5665 - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} cert = "{0}ca.crt".format(get_certs_path()) # Checking if execution is needed. if os.path.isfile(cert): - ret['comment'] = 'No execution needed. Cert: {0} already exists.'.format(cert) + ret["comment"] = "No execution needed. Cert: {0} already exists.".format(cert) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Certificate request from icinga2 master would be executed' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Certificate request from icinga2 master would be executed" return ret # Executing the command. - cert_request = __salt__['icinga2.request_cert'](name, master, ticket, port) - if not cert_request['retcode']: - ret['comment'] = "Certificate request from icinga2 master executed" - ret['changes']['cert'] = "Executed. Certificate requested: {0}".format(cert) + cert_request = __salt__["icinga2.request_cert"](name, master, ticket, port) + if not cert_request["retcode"]: + ret["comment"] = "Certificate request from icinga2 master executed" + ret["changes"]["cert"] = "Executed. Certificate requested: {0}".format(cert) return ret - ret['comment'] = "FAILED. Certificate requested failed with output: {0}".format(cert_request['stdout']) - ret['result'] = False + ret["comment"] = "FAILED. Certificate requested failed with output: {0}".format( + cert_request["stdout"] + ) + ret["result"] = False return ret def node_setup(name, master, ticket): - ''' + """ Setup the icinga2 node. name @@ -251,30 +267,29 @@ def node_setup(name, master, ticket): ticket Authentication ticket generated on icinga2 master - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} cert = "{0}{1}.crt.orig".format(get_certs_path(), name) key = "{0}{1}.key.orig".format(get_certs_path(), name) # Checking if execution is needed. if os.path.isfile(cert) and os.path.isfile(cert): - ret['comment'] = 'No execution needed. Node already configured.' + ret["comment"] = "No execution needed. Node already configured." return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Node setup will be executed.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Node setup will be executed." return ret # Executing the command. - node_setup = __salt__['icinga2.node_setup'](name, master, ticket) - if not node_setup['retcode']: - ret['comment'] = "Node setup executed." - ret['changes']['cert'] = "Node setup finished successfully." + node_setup = __salt__["icinga2.node_setup"](name, master, ticket) + if not node_setup["retcode"]: + ret["comment"] = "Node setup executed." + ret["changes"]["cert"] = "Node setup finished successfully." return ret - ret['comment'] = "FAILED. Node setup failed with outpu: {0}".format(node_setup['stdout']) - ret['result'] = False + ret["comment"] = "FAILED. Node setup failed with outpu: {0}".format( + node_setup["stdout"] + ) + ret["result"] = False return ret diff --git a/salt/states/ifttt.py b/salt/states/ifttt.py index fc41710dd18..db0ec900a04 100644 --- a/salt/states/ifttt.py +++ b/salt/states/ifttt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Trigger an event in IFTTT ========================= @@ -22,26 +22,23 @@ The api key can be specified in the master or minion configuration like below: ifttt: secret_key: bzMRb-KKIAaNOwKEEw792J7Eb-B3z7muhdhYblJn4V6 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the ifttt module is available in __salt__ - ''' - return 'ifttt' if 'ifttt.trigger_event' in __salt__ else False + """ + if "ifttt.trigger_event" in __salt__: + return "ifttt" + return (False, "ifttt module could not be loaded") -def trigger_event(name, - event, - value1=None, - value2=None, - value3=None - ): - ''' +def trigger_event(name, event, value1=None, value2=None, value3=None): + """ Trigger an event in IFTTT .. code-block:: yaml @@ -71,28 +68,24 @@ def trigger_event(name, value3 One of the values that we can send to IFTT. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - ret['comment'] = 'The following trigger would be sent to IFTTT: {0}'.format(event) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The following trigger would be sent to IFTTT: {0}".format( + event + ) + ret["result"] = None return ret - ret['result'] = __salt__['ifttt.trigger_event']( - event=event, - value1=value1, - value2=value2, - value3=value3 + ret["result"] = __salt__["ifttt.trigger_event"]( + event=event, value1=value1, value2=value2, value3=value3 ) - if ret and ret['result']: - ret['result'] = True - ret['comment'] = 'Triggered Event: {0}'.format(name) + if ret and ret["result"]: + ret["result"] = True + ret["comment"] = "Triggered Event: {0}".format(name) else: - ret['comment'] = 'Failed to trigger event: {0}'.format(name) + ret["comment"] = "Failed to trigger event: {0}".format(name) return ret diff --git a/salt/states/incron.py b/salt/states/incron.py index 97479a811a7..e71427d7516 100644 --- a/salt/states/incron.py +++ b/salt/states/incron.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of incron, the inotify cron ============================================== @@ -40,64 +40,58 @@ then a new cron job will be added to the user's crontab. .. versionadded:: 0.17.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals import logging + log = logging.getLogger(__name__) -def _check_cron(user, - path, - mask, - cmd): - ''' +def _check_cron(user, path, mask, cmd): + """ Return the changes - ''' - arg_mask = mask.split(',') + """ + arg_mask = mask.split(",") arg_mask.sort() - lst = __salt__['incron.list_tab'](user) - if cmd.endswith('\n'): + lst = __salt__["incron.list_tab"](user) + if cmd.endswith("\n"): cmd = cmd[:-1] - for cron in lst['crons']: - if path == cron['path'] and cron['cmd'] == cmd: - cron_mask = cron['mask'].split(',') + for cron in lst["crons"]: + if path == cron["path"] and cron["cmd"] == cmd: + cron_mask = cron["mask"].split(",") cron_mask.sort() if cron_mask == arg_mask: - return 'present' + return "present" if any([x in cron_mask for x in arg_mask]): - return 'update' - return 'absent' + return "update" + return "absent" def _get_cron_info(): - ''' + """ Returns the proper group owner and path to the incron directory - ''' - owner = 'root' - if __grains__['os'] == 'FreeBSD': - group = 'wheel' - crontab_dir = '/var/spool/incron' - elif __grains__['os'] == 'OpenBSD': - group = 'crontab' - crontab_dir = '/var/spool/incron' - elif __grains__.get('os_family') == 'Solaris': - group = 'root' - crontab_dir = '/var/spool/incron' + """ + owner = "root" + if __grains__["os"] == "FreeBSD": + group = "wheel" + crontab_dir = "/var/spool/incron" + elif __grains__["os"] == "OpenBSD": + group = "crontab" + crontab_dir = "/var/spool/incron" + elif __grains__.get("os_family") == "Solaris": + group = "root" + crontab_dir = "/var/spool/incron" else: - group = 'root' - crontab_dir = '/var/spool/incron' + group = "root" + crontab_dir = "/var/spool/incron" return owner, group, crontab_dir -def present(name, - path, - mask, - cmd, - user='root'): - ''' +def present(name, path, mask, cmd, user="root"): + """ Verifies that the specified incron job is present for the specified user. For more advanced information about what exactly can be set in the cron timing parameters, check your incron system's documentation. Most Unix-like @@ -120,57 +114,45 @@ def present(name, cmd The cmd that should be executed - ''' - mask = ',' . join(mask) + """ + mask = ",".join(mask) - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} - if __opts__['test']: - status = _check_cron(user, - path, - mask, - cmd) - ret['result'] = None - if status == 'absent': - ret['comment'] = 'Incron {0} is set to be added'.format(name) - elif status == 'present': - ret['result'] = True - ret['comment'] = 'Incron {0} already present'.format(name) - elif status == 'update': - ret['comment'] = 'Incron {0} is set to be updated'.format(name) + ret = {"changes": {}, "comment": "", "name": name, "result": True} + if __opts__["test"]: + status = _check_cron(user, path, mask, cmd) + ret["result"] = None + if status == "absent": + ret["comment"] = "Incron {0} is set to be added".format(name) + elif status == "present": + ret["result"] = True + ret["comment"] = "Incron {0} already present".format(name) + elif status == "update": + ret["comment"] = "Incron {0} is set to be updated".format(name) return ret - data = __salt__['incron.set_job'](user=user, - path=path, - mask=mask, - cmd=cmd) - if data == 'present': - ret['comment'] = 'Incron {0} already present'.format(name) + data = __salt__["incron.set_job"](user=user, path=path, mask=mask, cmd=cmd) + if data == "present": + ret["comment"] = "Incron {0} already present".format(name) return ret - if data == 'new': - ret['comment'] = 'Incron {0} added to {1}\'s incrontab'.format(name, user) - ret['changes'] = {user: name} + if data == "new": + ret["comment"] = "Incron {0} added to {1}'s incrontab".format(name, user) + ret["changes"] = {user: name} return ret - if data == 'updated': - ret['comment'] = 'Incron {0} updated'.format(name) - ret['changes'] = {user: name} + if data == "updated": + ret["comment"] = "Incron {0} updated".format(name) + ret["changes"] = {user: name} return ret - ret['comment'] = ('Incron {0} for user {1} failed to commit with error \n{2}' - .format(name, user, data)) - ret['result'] = False + ret["comment"] = "Incron {0} for user {1} failed to commit with error \n{2}".format( + name, user, data + ) + ret["result"] = False return ret -def absent(name, - path, - mask, - cmd, - user='root'): - ''' +def absent(name, path, mask, cmd, user="root"): + """ Verifies that the specified incron job is absent for the specified user; only the name is matched when removing a incron job. @@ -190,41 +172,32 @@ def absent(name, cmd The cmd that should be executed - ''' + """ - mask = ',' . join(mask) + mask = ",".join(mask) - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - if __opts__['test']: - status = _check_cron(user, - path, - mask, - cmd) - ret['result'] = None - if status == 'absent': - ret['result'] = True - ret['comment'] = 'Incron {0} is absent'.format(name) - elif status == 'present' or status == 'update': - ret['comment'] = 'Incron {0} is set to be removed'.format(name) + if __opts__["test"]: + status = _check_cron(user, path, mask, cmd) + ret["result"] = None + if status == "absent": + ret["result"] = True + ret["comment"] = "Incron {0} is absent".format(name) + elif status == "present" or status == "update": + ret["comment"] = "Incron {0} is set to be removed".format(name) return ret - data = __salt__['incron.rm_job'](user=user, - path=path, - mask=mask, - cmd=cmd) - if data == 'absent': - ret['comment'] = "Incron {0} already absent".format(name) + data = __salt__["incron.rm_job"](user=user, path=path, mask=mask, cmd=cmd) + if data == "absent": + ret["comment"] = "Incron {0} already absent".format(name) return ret - if data == 'removed': - ret['comment'] = ("Incron {0} removed from {1}'s crontab" - .format(name, user)) - ret['changes'] = {user: name} + if data == "removed": + ret["comment"] = "Incron {0} removed from {1}'s crontab".format(name, user) + ret["changes"] = {user: name} return ret - ret['comment'] = ("Incron {0} for user {1} failed to commit with error {2}" - .format(name, user, data)) - ret['result'] = False + ret["comment"] = "Incron {0} for user {1} failed to commit with error {2}".format( + name, user, data + ) + ret["result"] = False return ret diff --git a/salt/states/influxdb08_database.py b/salt/states/influxdb08_database.py index e1ee635ea62..063c44fcce0 100644 --- a/salt/states/influxdb08_database.py +++ b/salt/states/influxdb08_database.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Influxdb 0.8 databases ==================================== @@ -7,23 +7,23 @@ Management of Influxdb 0.8 databases .. versionadded:: 2014.7.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the influxdb08 module is available - ''' - if 'influxdb08.db_exists' in __salt__: - return 'influxdb08_database' - return False + """ + if "influxdb08.db_exists" in __salt__: + return "influxdb08_database" + return (False, "influxdb08 module could not be loaded") def present(name, user=None, password=None, host=None, port=None): - ''' + """ Ensure that the named database is present name @@ -41,36 +41,35 @@ def present(name, user=None, password=None, host=None, port=None): port The port to connect to - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # check if database exists - if not __salt__['influxdb08.db_exists'](name, user, password, host, port): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Database {0} is absent and needs to be created'\ - .format(name) + if not __salt__["influxdb08.db_exists"](name, user, password, host, port): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Database {0} is absent and needs to be created".format( + name + ) return ret - if __salt__['influxdb08.db_create'](name, user, password, host, port): - ret['comment'] = 'Database {0} has been created'.format(name) - ret['changes'][name] = 'Present' + if __salt__["influxdb08.db_create"](name, user, password, host, port): + ret["comment"] = "Database {0} has been created".format(name) + ret["changes"][name] = "Present" return ret else: - ret['comment'] = 'Failed to create database {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create database {0}".format(name) + ret["result"] = False return ret # fallback - ret['comment'] = 'Database {0} is already present, so cannot be created'\ - .format(name) + ret["comment"] = "Database {0} is already present, so cannot be created".format( + name + ) return ret def absent(name, user=None, password=None, host=None, port=None): - ''' + """ Ensure that the named database is absent name @@ -88,29 +87,26 @@ def absent(name, user=None, password=None, host=None, port=None): port The port to connect to - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # check if database exists and remove it - if __salt__['influxdb08.db_exists'](name, user, password, host, port): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Database {0} is present and needs to be removed'\ - .format(name) + if __salt__["influxdb08.db_exists"](name, user, password, host, port): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Database {0} is present and needs to be removed".format( + name + ) return ret - if __salt__['influxdb08.db_remove'](name, user, password, host, port): - ret['comment'] = 'Database {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["influxdb08.db_remove"](name, user, password, host, port): + ret["comment"] = "Database {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['comment'] = 'Failed to remove database {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to remove database {0}".format(name) + ret["result"] = False return ret # fallback - ret['comment'] = 'Database {0} is not present, so it cannot be removed'\ - .format(name) + ret["comment"] = "Database {0} is not present, so it cannot be removed".format(name) return ret diff --git a/salt/states/influxdb08_user.py b/salt/states/influxdb08_user.py index 052c6c99d18..b29b8972c46 100644 --- a/salt/states/influxdb08_user.py +++ b/salt/states/influxdb08_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of InfluxDB 0.8 users ================================ @@ -7,29 +7,25 @@ Management of InfluxDB 0.8 users .. versionadded:: 2014.7.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the influxdb08 module is available - ''' - if 'influxdb08.db_exists' in __salt__: - return 'influxdb08_user' - return False + """ + if "influxdb08.db_exists" in __salt__: + return "influxdb08_user" + return (False, "influxdb08 module could not be loaded") -def present(name, - passwd, - database=None, - user=None, - password=None, - host=None, - port=None): - ''' +def present( + name, passwd, database=None, user=None, password=None, host=None, port=None +): + """ Ensure that the cluster admin or database user is present. name @@ -53,50 +49,46 @@ def present(name, port The port to connect to - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # check if db does not exist - if database and not __salt__['influxdb08.db_exists']( - database, user, password, host, port): - ret['result'] = False - ret['comment'] = 'Database {0} does not exist'.format(database) + if database and not __salt__["influxdb08.db_exists"]( + database, user, password, host, port + ): + ret["result"] = False + ret["comment"] = "Database {0} does not exist".format(database) return ret # check if user exists - if not __salt__['influxdb08.user_exists']( - name, database, user, password, host, port): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0} is not present and needs to be created'\ - .format(name) + if not __salt__["influxdb08.user_exists"]( + name, database, user, password, host, port + ): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User {0} is not present and needs to be created".format( + name + ) return ret # The user is not present, make it! - if __salt__['influxdb08.user_create']( - name, passwd, database, user, password, host, port): - ret['comment'] = 'User {0} has been created'.format(name) - ret['changes'][name] = 'Present' + if __salt__["influxdb08.user_create"]( + name, passwd, database, user, password, host, port + ): + ret["comment"] = "User {0} has been created".format(name) + ret["changes"][name] = "Present" return ret else: - ret['comment'] = 'Failed to create user {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create user {0}".format(name) + ret["result"] = False return ret # fallback - ret['comment'] = 'User {0} is already present'.format(name) + ret["comment"] = "User {0} is already present".format(name) return ret -def absent(name, - database=None, - user=None, - password=None, - host=None, - port=None): - ''' +def absent(name, database=None, user=None, password=None, host=None, port=None): + """ Ensure that the named cluster admin or database user is absent. name @@ -117,31 +109,26 @@ def absent(name, port The port to connect to - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - #check if user exists and remove it - if __salt__['influxdb08.user_exists']( - name, database, user, password, host, port): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0} is present and needs to be removed'\ - .format(name) + # check if user exists and remove it + if __salt__["influxdb08.user_exists"](name, database, user, password, host, port): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User {0} is present and needs to be removed".format(name) return ret - if __salt__['influxdb08.user_remove']( - name, database, user, password, host, port): - ret['comment'] = 'User {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["influxdb08.user_remove"]( + name, database, user, password, host, port + ): + ret["comment"] = "User {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['comment'] = 'Failed to remove user {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to remove user {0}".format(name) + ret["result"] = False return ret # fallback - ret['comment'] = 'User {0} is not present, so it cannot be removed'\ - .format(name) + ret["comment"] = "User {0} is not present, so it cannot be removed".format(name) return ret diff --git a/salt/states/influxdb_continuous_query.py b/salt/states/influxdb_continuous_query.py index bf3210a78f8..e3f13f42bfa 100644 --- a/salt/states/influxdb_continuous_query.py +++ b/salt/states/influxdb_continuous_query.py @@ -1,28 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" Management of Influxdb continuous queries ========================================= .. versionadded:: 2017.7.0 (compatible with InfluxDB version 0.9+) -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the influxdb module is available - ''' - if 'influxdb.db_exists' in __salt__: - return 'influxdb_continuous_query' - return False + """ + if "influxdb.db_exists" in __salt__: + return "influxdb_continuous_query" + return (False, "influxdb module could not be loaded") -def present(name, database, query, resample_time=None, coverage_period=None, **client_args): - ''' +def present( + name, database, query, resample_time=None, coverage_period=None, **client_args +): + """ Ensure that given continuous query is present. name @@ -39,38 +41,37 @@ def present(name, database, query, resample_time=None, coverage_period=None, **c coverage_period : None Duration specifying time period per sample. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'continuous query {0} is already present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "continuous query {0} is already present".format(name), + } - if not __salt__['influxdb.continuous_query_exists'](name=name, - database=database, - **client_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = ' {0} is absent and will be created'\ - .format(name) + if not __salt__["influxdb.continuous_query_exists"]( + name=name, database=database, **client_args + ): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = " {0} is absent and will be created".format(name) return ret - if __salt__['influxdb.create_continuous_query']( + if __salt__["influxdb.create_continuous_query"]( database, name, query, resample_time, coverage_period ): - ret['comment'] = 'continuous query {0} has been created'\ - .format(name) - ret['changes'][name] = 'Present' + ret["comment"] = "continuous query {0} has been created".format(name) + ret["changes"][name] = "Present" return ret else: - ret['comment'] = 'Failed to create continuous query {0}'\ - .format(name) - ret['result'] = False + ret["comment"] = "Failed to create continuous query {0}".format(name) + ret["result"] = False return ret return ret def absent(name, database, **client_args): - ''' + """ Ensure that given continuous query is absent. name @@ -78,28 +79,28 @@ def absent(name, database, **client_args): database Name of the database that the continuous query was defined on. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'continuous query {0} is not present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "continuous query {0} is not present".format(name), + } - if __salt__['influxdb.continuous_query_exists'](database, name, **client_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = ( - 'continuous query {0} is present and needs to be removed' + if __salt__["influxdb.continuous_query_exists"](database, name, **client_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "continuous query {0} is present and needs to be removed" ).format(name) return ret - if __salt__['influxdb.drop_continuous_query'](database, name, **client_args): - ret['comment'] = 'continuous query {0} has been removed'\ - .format(name) - ret['changes'][name] = 'Absent' + if __salt__["influxdb.drop_continuous_query"](database, name, **client_args): + ret["comment"] = "continuous query {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['comment'] = 'Failed to remove continuous query {0}'\ - .format(name) - ret['result'] = False + ret["comment"] = "Failed to remove continuous query {0}".format(name) + ret["result"] = False return ret return ret diff --git a/salt/states/influxdb_database.py b/salt/states/influxdb_database.py index 00455bbd6e8..fbaf46a7c8b 100644 --- a/salt/states/influxdb_database.py +++ b/salt/states/influxdb_database.py @@ -1,79 +1,83 @@ # -*- coding: utf-8 -*- -''' +""" Management of Influxdb databases ================================ (compatible with InfluxDB version 0.9+) -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the influxdb module is available - ''' - if 'influxdb.db_exists' in __salt__: - return 'influxdb_database' - return False + """ + if "influxdb.db_exists" in __salt__: + return "influxdb_database" + return (False, "influxdb module could not be loaded") def present(name, **client_args): - ''' + """ Ensure that given database is present. name Name of the database to create. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Database {0} is already present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Database {0} is already present".format(name), + } - if not __salt__['influxdb.db_exists'](name, **client_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Database {0} is absent and will be created'\ - .format(name) + if not __salt__["influxdb.db_exists"](name, **client_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Database {0} is absent and will be created".format(name) return ret - if __salt__['influxdb.create_db'](name, **client_args): - ret['comment'] = 'Database {0} has been created'.format(name) - ret['changes'][name] = 'Present' + if __salt__["influxdb.create_db"](name, **client_args): + ret["comment"] = "Database {0} has been created".format(name) + ret["changes"][name] = "Present" return ret else: - ret['comment'] = 'Failed to create database {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create database {0}".format(name) + ret["result"] = False return ret return ret def absent(name, **client_args): - ''' + """ Ensure that given database is absent. name Name of the database to remove. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Database {0} is not present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Database {0} is not present".format(name), + } - if __salt__['influxdb.db_exists'](name, **client_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Database {0} is present and needs to be removed'\ - .format(name) + if __salt__["influxdb.db_exists"](name, **client_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Database {0} is present and needs to be removed".format( + name + ) return ret - if __salt__['influxdb.drop_db'](name, **client_args): - ret['comment'] = 'Database {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["influxdb.drop_db"](name, **client_args): + ret["comment"] = "Database {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['comment'] = 'Failed to remove database {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to remove database {0}".format(name) + ret["result"] = False return ret return ret diff --git a/salt/states/influxdb_retention_policy.py b/salt/states/influxdb_retention_policy.py index 6b07f475308..6331babbd4f 100644 --- a/salt/states/influxdb_retention_policy.py +++ b/salt/states/influxdb_retention_policy.py @@ -1,28 +1,28 @@ # -*- coding: utf-8 -*- -''' +""" Management of Influxdb retention policies ========================================= .. versionadded:: 2017.7.0 (compatible with InfluxDB version 0.9+) -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the influxdb module is available - ''' - if 'influxdb.db_exists' in __salt__: - return 'influxdb_retention_policy' - return False + """ + if "influxdb.db_exists" in __salt__: + return "influxdb_retention_policy" + return (False, "influxdb module could not be loaded") def convert_duration(duration): - ''' + """ Convert the a duration string into XXhYYmZZs format duration @@ -30,29 +30,27 @@ def convert_duration(duration): Returns: duration_string String representation of duration in XXhYYmZZs format - ''' + """ # durations must be specified in days, weeks or hours - if duration.endswith('h'): - hours = int(duration.split('h')) + if duration.endswith("h"): + hours = int(duration.split("h")) - elif duration.endswith('d'): - days = duration.split('d') + elif duration.endswith("d"): + days = duration.split("d") hours = int(days[0]) * 24 - elif duration.endswith('w'): - weeks = duration.split('w') + elif duration.endswith("w"): + weeks = duration.split("w") hours = int(weeks[0]) * 24 * 7 - duration_string = str(hours)+'h0m0s' + duration_string = str(hours) + "h0m0s" return duration_string -def present(name, database, duration="7d", - replication=1, default=False, - **client_args): - ''' +def present(name, database, duration="7d", replication=1, default=False, **client_args): + """ Ensure that given retention policy is present. name @@ -60,74 +58,82 @@ def present(name, database, duration="7d", database Database to create retention policy on. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'retention policy {0} is already present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "retention policy {0} is already present".format(name), + } - if not __salt__['influxdb.retention_policy_exists'](name=name, - database=database, - **client_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = ' {0} is absent and will be created'\ - .format(name) + if not __salt__["influxdb.retention_policy_exists"]( + name=name, database=database, **client_args + ): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = " {0} is absent and will be created".format(name) return ret - if __salt__['influxdb.create_retention_policy']( - database, name, - duration, replication, default, **client_args + if __salt__["influxdb.create_retention_policy"]( + database, name, duration, replication, default, **client_args ): - ret['comment'] = 'retention policy {0} has been created'\ - .format(name) - ret['changes'][name] = 'Present' + ret["comment"] = "retention policy {0} has been created".format(name) + ret["changes"][name] = "Present" return ret else: - ret['comment'] = 'Failed to create retention policy {0}'\ - .format(name) - ret['result'] = False + ret["comment"] = "Failed to create retention policy {0}".format(name) + ret["result"] = False return ret else: - current_policy = __salt__['influxdb.get_retention_policy'](database=database, name=name, **client_args) + current_policy = __salt__["influxdb.get_retention_policy"]( + database=database, name=name, **client_args + ) update_policy = False - if current_policy['duration'] != convert_duration(duration): + if current_policy["duration"] != convert_duration(duration): update_policy = True - ret['changes']['duration'] = "Retention changed from {0} to {1}.".format(current_policy['duration'], duration) + ret["changes"]["duration"] = "Retention changed from {0} to {1}.".format( + current_policy["duration"], duration + ) - if current_policy['replicaN'] != replication: + if current_policy["replicaN"] != replication: update_policy = True - ret['changes']['replication'] = "Replication changed from {0} to {1}.".format(current_policy['replicaN'], replication) + ret["changes"][ + "replication" + ] = "Replication changed from {0} to {1}.".format( + current_policy["replicaN"], replication + ) - if current_policy['default'] != default: + if current_policy["default"] != default: update_policy = True - ret['changes']['default'] = "Default changed from {0} to {1}.".format(current_policy['default'], default) + ret["changes"]["default"] = "Default changed from {0} to {1}.".format( + current_policy["default"], default + ) if update_policy: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ' {0} is present and set to be changed'\ - .format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = " {0} is present and set to be changed".format(name) return ret else: - if __salt__['influxdb.alter_retention_policy']( - database, name, - duration, replication, default, **client_args + if __salt__["influxdb.alter_retention_policy"]( + database, name, duration, replication, default, **client_args ): - ret['comment'] = 'retention policy {0} has been changed'\ - .format(name) + ret["comment"] = "retention policy {0} has been changed".format( + name + ) return ret else: - ret['comment'] = 'Failed to update retention policy {0}'\ - .format(name) - ret['result'] = False + ret["comment"] = "Failed to update retention policy {0}".format( + name + ) + ret["result"] = False return ret return ret def absent(name, database, **client_args): - ''' + """ Ensure that given retention policy is absent. name @@ -135,28 +141,28 @@ def absent(name, database, **client_args): database Name of the database that the retention policy was defined on. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'retention policy {0} is not present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "retention policy {0} is not present".format(name), + } - if __salt__['influxdb.retention_policy_exists'](database, name, **client_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = ( - 'retention policy {0} is present and needs to be removed' + if __salt__["influxdb.retention_policy_exists"](database, name, **client_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "retention policy {0} is present and needs to be removed" ).format(name) return ret - if __salt__['influxdb.drop_retention_policy'](database, name, **client_args): - ret['comment'] = 'retention policy {0} has been removed'\ - .format(name) - ret['changes'][name] = 'Absent' + if __salt__["influxdb.drop_retention_policy"](database, name, **client_args): + ret["comment"] = "retention policy {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['comment'] = 'Failed to remove retention policy {0}'\ - .format(name) - ret['result'] = False + ret["comment"] = "Failed to remove retention policy {0}".format(name) + ret["result"] = False return ret return ret diff --git a/salt/states/influxdb_user.py b/salt/states/influxdb_user.py index fd1ded8befd..59fa5d87432 100644 --- a/salt/states/influxdb_user.py +++ b/salt/states/influxdb_user.py @@ -1,30 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" Management of InfluxDB users ============================ (compatible with InfluxDB version 0.9+) -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the influxdb module is available - ''' - if 'influxdb.db_exists' in __salt__: - return 'influxdb_user' - return False + """ + if "influxdb.db_exists" in __salt__: + return "influxdb_user" + return (False, "influxdb module could not be loaded") -def present(name, - passwd, - admin=False, - grants=None, - **client_args): - ''' +def present(name, passwd, admin=False, grants=None, **client_args): + """ Ensure that given user is present. name @@ -57,104 +53,114 @@ def present(name, - grants: foo_db: read bar_db: all - ''' + """ create = False - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User {0} is present and up to date'.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "User {0} is present and up to date".format(name), + } - if not __salt__['influxdb.user_exists'](name, **client_args): + if not __salt__["influxdb.user_exists"](name, **client_args): create = True - if __opts__['test']: - ret['comment'] = 'User {0} will be created'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "User {0} will be created".format(name) + ret["result"] = None return ret else: - if not __salt__['influxdb.create_user']( - name, passwd, admin=admin, **client_args): - ret['comment'] = 'Failed to create user {0}'.format(name) - ret['result'] = False + if not __salt__["influxdb.create_user"]( + name, passwd, admin=admin, **client_args + ): + ret["comment"] = "Failed to create user {0}".format(name) + ret["result"] = False return ret else: - user = __salt__['influxdb.user_info'](name, **client_args) + user = __salt__["influxdb.user_info"](name, **client_args) - if user['admin'] != admin: - if not __opts__['test']: + if user["admin"] != admin: + if not __opts__["test"]: if admin: - __salt__['influxdb.grant_admin_privileges']( - name, **client_args) + __salt__["influxdb.grant_admin_privileges"](name, **client_args) else: - __salt__['influxdb.revoke_admin_privileges']( - name, **client_args) + __salt__["influxdb.revoke_admin_privileges"](name, **client_args) - if admin != __salt__['influxdb.user_info']( - name, **client_args)['admin']: - ret['comment'] = 'Failed to set admin privilege to ' \ - 'user {0}'.format(name) - ret['result'] = False + if ( + admin + != __salt__["influxdb.user_info"](name, **client_args)["admin"] + ): + ret[ + "comment" + ] = "Failed to set admin privilege to " "user {0}".format(name) + ret["result"] = False return ret - ret['changes']['Admin privileges'] = admin + ret["changes"]["Admin privileges"] = admin if grants: - db_privileges = __salt__['influxdb.list_privileges']( - name, **client_args) + db_privileges = __salt__["influxdb.list_privileges"](name, **client_args) for database, privilege in grants.items(): privilege = privilege.lower() if privilege != db_privileges.get(database, privilege): - if not __opts__['test']: - __salt__['influxdb.revoke_privilege']( - database, 'all', name, **client_args) + if not __opts__["test"]: + __salt__["influxdb.revoke_privilege"]( + database, "all", name, **client_args + ) del db_privileges[database] if database not in db_privileges: - ret['changes']['Grant on database {0} to user {1}'.format( - database, name)] = privilege - if not __opts__['test']: - __salt__['influxdb.grant_privilege']( - database, privilege, name, **client_args) + ret["changes"][ + "Grant on database {0} to user {1}".format(database, name) + ] = privilege + if not __opts__["test"]: + __salt__["influxdb.grant_privilege"]( + database, privilege, name, **client_args + ) - if ret['changes']: + if ret["changes"]: if create: - ret['comment'] = 'Created user {0}'.format(name) - ret['changes'][name] = 'User created' + ret["comment"] = "Created user {0}".format(name) + ret["changes"][name] = "User created" else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0} will be updated with the ' \ - 'following changes:'.format(name) - for k, v in ret['changes'].items(): - ret['comment'] += '\n{0} => {1}'.format(k, v) - ret['changes'] = {} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "User {0} will be updated with the " + "following changes:".format(name) + ) + for k, v in ret["changes"].items(): + ret["comment"] += "\n{0} => {1}".format(k, v) + ret["changes"] = {} else: - ret['comment'] = 'Updated user {0}'.format(name) + ret["comment"] = "Updated user {0}".format(name) return ret def absent(name, **client_args): - ''' + """ Ensure that given user is absent. name The name of the user to manage - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User {0} is not present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "User {0} is not present".format(name), + } - if __salt__['influxdb.user_exists'](name, **client_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0} will be removed'.format(name) + if __salt__["influxdb.user_exists"](name, **client_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User {0} will be removed".format(name) return ret else: - if __salt__['influxdb.remove_user'](name, **client_args): - ret['comment'] = 'Removed user {0}'.format(name) - ret['changes'][name] = 'removed' + if __salt__["influxdb.remove_user"](name, **client_args): + ret["comment"] = "Removed user {0}".format(name) + ret["changes"][name] = "removed" return ret else: - ret['comment'] = 'Failed to remove user {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to remove user {0}".format(name) + ret["result"] = False return ret return ret diff --git a/salt/states/infoblox_a.py b/salt/states/infoblox_a.py index 07ae628281c..2247eca90bf 100644 --- a/salt/states/infoblox_a.py +++ b/salt/states/infoblox_a.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Infoblox A record managment. functions accept api_opts: @@ -8,14 +8,14 @@ functions accept api_opts: api_url: server to connect to [default to pillar value] api_username: [default to pillar value] api_password: [default to pillar value] -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def present(name=None, ipv4addr=None, data=None, ensure_data=True, **api_opts): - ''' + """ Ensure infoblox A record. When you wish to update a hostname ensure `name` is set to the hostname @@ -31,67 +31,87 @@ def present(name=None, ipv4addr=None, data=None, ensure_data=True, **api_opts): name: example-ha-0.domain.com ipv4addr: 123.0.31.2 view: Internal - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not data: data = {} - if 'name' not in data: - data.update({'name': name}) - if 'ipv4addr' not in data: - data.update({'ipv4addr': ipv4addr}) + if "name" not in data: + data.update({"name": name}) + if "ipv4addr" not in data: + data.update({"ipv4addr": ipv4addr}) - obj = __salt__['infoblox.get_a'](name=name, ipv4addr=ipv4addr, allow_array=False, **api_opts) + obj = __salt__["infoblox.get_a"]( + name=name, ipv4addr=ipv4addr, allow_array=False, **api_opts + ) if obj is None: # perhaps the user updated the name - obj = __salt__['infoblox.get_a'](name=data['name'], ipv4addr=data['ipv4addr'], allow_array=False, **api_opts) + obj = __salt__["infoblox.get_a"]( + name=data["name"], ipv4addr=data["ipv4addr"], allow_array=False, **api_opts + ) if obj: # warn user that the data was updated and does not match - ret['result'] = False - ret['comment'] = '** please update the name: {0} to equal the updated data name {1}'.format(name, data['name']) + ret["result"] = False + ret[ + "comment" + ] = "** please update the name: {0} to equal the updated data name {1}".format( + name, data["name"] + ) return ret if obj: obj = obj[0] if not ensure_data: - ret['result'] = True - ret['comment'] = 'infoblox record already created (supplied fields not ensured to match)' + ret["result"] = True + ret[ + "comment" + ] = "infoblox record already created (supplied fields not ensured to match)" return ret - diff = __salt__['infoblox.diff_objects'](data, obj) + diff = __salt__["infoblox.diff_objects"](data, obj) if not diff: - ret['result'] = True - ret['comment'] = 'supplied fields already updated (note: removing fields might not update)' + ret["result"] = True + ret[ + "comment" + ] = "supplied fields already updated (note: removing fields might not update)" return ret if diff: - ret['changes'] = {'diff': diff} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'would attempt to update infoblox record' + ret["changes"] = {"diff": diff} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "would attempt to update infoblox record" return ret ## TODO: perhaps need to review the output of new_obj - new_obj = __salt__['infoblox.update_object'](obj['_ref'], data=data, **api_opts) - ret['result'] = True - ret['comment'] = 'infoblox record fields updated (note: removing fields might not update)' + new_obj = __salt__["infoblox.update_object"]( + obj["_ref"], data=data, **api_opts + ) + ret["result"] = True + ret[ + "comment" + ] = "infoblox record fields updated (note: removing fields might not update)" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'would attempt to create infoblox record {0}'.format(data['name']) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "would attempt to create infoblox record {0}".format( + data["name"] + ) return ret - new_obj_ref = __salt__['infoblox.create_a'](data=data, **api_opts) - new_obj = __salt__['infoblox.get_a'](name=name, ipv4addr=ipv4addr, allow_array=False, **api_opts) + new_obj_ref = __salt__["infoblox.create_a"](data=data, **api_opts) + new_obj = __salt__["infoblox.get_a"]( + name=name, ipv4addr=ipv4addr, allow_array=False, **api_opts + ) - ret['result'] = True - ret['comment'] = 'infoblox record created' - ret['changes'] = {'old': 'None', 'new': {'_ref': new_obj_ref, 'data': new_obj}} + ret["result"] = True + ret["comment"] = "infoblox record created" + ret["changes"] = {"old": "None", "new": {"_ref": new_obj_ref, "data": new_obj}} return ret def absent(name=None, ipv4addr=None, **api_opts): - ''' + """ Ensure infoblox A record is removed. State example: @@ -104,21 +124,23 @@ def absent(name=None, ipv4addr=None, **api_opts): infoblox_a.absent: - name: - ipv4addr: 127.0.23.23 - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - obj = __salt__['infoblox.get_a'](name=name, ipv4addr=ipv4addr, allow_array=False, **api_opts) + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} + obj = __salt__["infoblox.get_a"]( + name=name, ipv4addr=ipv4addr, allow_array=False, **api_opts + ) if not obj: - ret['result'] = True - ret['comment'] = 'infoblox already removed' + ret["result"] = True + ret["comment"] = "infoblox already removed" return ret - if __opts__['test']: - ret['result'] = None - ret['changes'] = {'old': obj, 'new': 'absent'} + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {"old": obj, "new": "absent"} return ret - if __salt__['infoblox.delete_a'](name=name, ipv4addr=ipv4addr, **api_opts): - ret['result'] = True - ret['changes'] = {'old': obj, 'new': 'absent'} + if __salt__["infoblox.delete_a"](name=name, ipv4addr=ipv4addr, **api_opts): + ret["result"] = True + ret["changes"] = {"old": obj, "new": "absent"} return ret diff --git a/salt/states/infoblox_cname.py b/salt/states/infoblox_cname.py index ce64fc62d39..d6334f138dc 100644 --- a/salt/states/infoblox_cname.py +++ b/salt/states/infoblox_cname.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Infoblox CNAME managment. functions accept api_opts: @@ -8,14 +8,14 @@ functions accept api_opts: api_url: server to connect to [default to pillar value] api_username: [default to pillar value] api_password: [default to pillar value] -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def present(name=None, data=None, ensure_data=True, **api_opts): - ''' + """ Ensure the CNAME with the given data is present. name @@ -47,78 +47,92 @@ def present(name=None, data=None, ensure_data=True, **api_opts): - api_url: https://INFOBLOX/wapi/v1.2.1 - api_username: username - api_password: passwd - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not data: data = {} - if 'name' not in data: - data.update({'name': name}) + if "name" not in data: + data.update({"name": name}) - obj = __salt__['infoblox.get_cname'](name=name, **api_opts) + obj = __salt__["infoblox.get_cname"](name=name, **api_opts) if obj is None: # perhaps the user updated the name - obj = __salt__['infoblox.get_cname'](name=data['name'], **api_opts) + obj = __salt__["infoblox.get_cname"](name=data["name"], **api_opts) if obj: # warn user that the data was updated and does not match - ret['result'] = False - ret['comment'] = '** please update the name: {0} to equal the updated data name {1}'.format(name, data['name']) + ret["result"] = False + ret[ + "comment" + ] = "** please update the name: {0} to equal the updated data name {1}".format( + name, data["name"] + ) return ret if obj: if not ensure_data: - ret['result'] = True - ret['comment'] = 'infoblox record already created (supplied fields not ensured to match)' + ret["result"] = True + ret[ + "comment" + ] = "infoblox record already created (supplied fields not ensured to match)" return ret - diff = __salt__['infoblox.diff_objects'](data, obj) + diff = __salt__["infoblox.diff_objects"](data, obj) if not diff: - ret['result'] = True - ret['comment'] = 'supplied fields already updated (note: removing fields might not update)' + ret["result"] = True + ret[ + "comment" + ] = "supplied fields already updated (note: removing fields might not update)" return ret if diff: - ret['changes'] = {'diff': diff} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'would attempt to update infoblox record' + ret["changes"] = {"diff": diff} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "would attempt to update infoblox record" return ret - new_obj = __salt__['infoblox.update_object'](obj['_ref'], data=data, **api_opts) - ret['result'] = True - ret['comment'] = 'infoblox record fields updated (note: removing fields might not update)' + new_obj = __salt__["infoblox.update_object"]( + obj["_ref"], data=data, **api_opts + ) + ret["result"] = True + ret[ + "comment" + ] = "infoblox record fields updated (note: removing fields might not update)" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'would attempt to create infoblox record {0}'.format(data['name']) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "would attempt to create infoblox record {0}".format( + data["name"] + ) return ret - new_obj_ref = __salt__['infoblox.create_cname'](data=data, **api_opts) - new_obj = __salt__['infoblox.get_cname'](name=name, **api_opts) + new_obj_ref = __salt__["infoblox.create_cname"](data=data, **api_opts) + new_obj = __salt__["infoblox.get_cname"](name=name, **api_opts) - ret['result'] = True - ret['comment'] = 'infoblox record created' - ret['changes'] = {'old': 'None', 'new': {'_ref': new_obj_ref, 'data': new_obj}} + ret["result"] = True + ret["comment"] = "infoblox record created" + ret["changes"] = {"old": "None", "new": {"_ref": new_obj_ref, "data": new_obj}} return ret def absent(name=None, canonical=None, **api_opts): - ''' + """ Ensure the CNAME with the given name or canonical name is removed - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - obj = __salt__['infoblox.get_cname'](name=name, canonical=canonical, **api_opts) + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} + obj = __salt__["infoblox.get_cname"](name=name, canonical=canonical, **api_opts) if not obj: - ret['result'] = True - ret['comment'] = 'infoblox already removed' + ret["result"] = True + ret["comment"] = "infoblox already removed" return ret - if __opts__['test']: - ret['result'] = None - ret['changes'] = {'old': obj, 'new': 'absent'} + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {"old": obj, "new": "absent"} return ret - if __salt__['infoblox.delete_cname'](name=name, canonical=canonical, **api_opts): - ret['result'] = True - ret['changes'] = {'old': obj, 'new': 'absent'} + if __salt__["infoblox.delete_cname"](name=name, canonical=canonical, **api_opts): + ret["result"] = True + ret["changes"] = {"old": obj, "new": "absent"} return ret diff --git a/salt/states/infoblox_host_record.py b/salt/states/infoblox_host_record.py index f61ba499e37..cf475eac951 100644 --- a/salt/states/infoblox_host_record.py +++ b/salt/states/infoblox_host_record.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Infoblox host record managment. functions accept api_opts: @@ -8,14 +8,14 @@ functions accept api_opts: api_url: server to connect to [default to pillar value] api_username: [default to pillar value] api_password: [default to pillar value] -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def present(name=None, data=None, ensure_data=True, **api_opts): - ''' + """ This will ensure that a host with the provided name exists. This will try to ensure that the state of the host matches the given data If the host is not found then one will be created. @@ -43,93 +43,111 @@ def present(name=None, data=None, ensure_data=True, **api_opts): 'ipv4addr': 'func:nextavailableip:129.97.139.0/24', 'mac': '00:50:56:84:6e:ae'}], 'ipv6addrs': [], } - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if data is None: data = {} - if 'name' not in data: - data.update({'name': name}) + if "name" not in data: + data.update({"name": name}) - obj = __salt__['infoblox.get_host'](name=name, **api_opts) + obj = __salt__["infoblox.get_host"](name=name, **api_opts) if obj is None: # perhaps the user updated the name - obj = __salt__['infoblox.get_host'](name=data['name'], **api_opts) + obj = __salt__["infoblox.get_host"](name=data["name"], **api_opts) if obj: # warn user that the host name was updated and does not match - ret['result'] = False - ret['comment'] = 'please update the name: {0} to equal the updated data name {1}'.format(name, data['name']) + ret["result"] = False + ret[ + "comment" + ] = "please update the name: {0} to equal the updated data name {1}".format( + name, data["name"] + ) return ret if obj: if not ensure_data: - ret['result'] = True - ret['comment'] = 'infoblox record already created (supplied fields not ensured to match)' + ret["result"] = True + ret[ + "comment" + ] = "infoblox record already created (supplied fields not ensured to match)" return ret - obj = __salt__['infoblox.get_host_advanced'](name=name, **api_opts) - diff = __salt__['infoblox.diff_objects'](data, obj) + obj = __salt__["infoblox.get_host_advanced"](name=name, **api_opts) + diff = __salt__["infoblox.diff_objects"](data, obj) if not diff: - ret['result'] = True - ret['comment'] = 'supplied fields already updated (note: removing fields might not update)' + ret["result"] = True + ret[ + "comment" + ] = "supplied fields already updated (note: removing fields might not update)" return ret if diff: - ret['changes'] = {'diff': diff} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'would attempt to update infoblox record' + ret["changes"] = {"diff": diff} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "would attempt to update infoblox record" return ret # replace func:nextavailableip with current ip address if in range # get list of ipaddresses that are defined. obj_addrs = [] - if 'ipv4addrs' in obj: - for addr in obj['ipv4addrs']: - if 'ipv4addr' in addr: - obj_addrs.append(addr['ipv4addr']) - if 'ipv6addrs' in obj: - for addr in obj['ipv6addrs']: - if 'ipv6addr' in addr: - obj_addrs.append(addr['ipv6addr']) + if "ipv4addrs" in obj: + for addr in obj["ipv4addrs"]: + if "ipv4addr" in addr: + obj_addrs.append(addr["ipv4addr"]) + if "ipv6addrs" in obj: + for addr in obj["ipv6addrs"]: + if "ipv6addr" in addr: + obj_addrs.append(addr["ipv6addr"]) # replace func:nextavailableip: if an ip address is already found in that range. - if 'ipv4addrs' in data: - for addr in data['ipv4addrs']: - if 'ipv4addr' in addr: - addrobj = addr['ipv4addr'] - if addrobj.startswith('func:nextavailableip:'): + if "ipv4addrs" in data: + for addr in data["ipv4addrs"]: + if "ipv4addr" in addr: + addrobj = addr["ipv4addr"] + if addrobj.startswith("func:nextavailableip:"): found_matches = 0 for ip in obj_addrs: - if __salt__['infoblox.is_ipaddr_in_ipfunc_range'](ip, addrobj): - addr['ipv4addr'] = ip + if __salt__["infoblox.is_ipaddr_in_ipfunc_range"]( + ip, addrobj + ): + addr["ipv4addr"] = ip found_matches += 1 if found_matches > 1: - ret['comment'] = 'infoblox record cant updated because ipaddress {0} matches multiple func:nextavailableip'.format(ip) - ret['result'] = False + ret[ + "comment" + ] = "infoblox record cant updated because ipaddress {0} matches multiple func:nextavailableip".format( + ip + ) + ret["result"] = False return ret - new_obj = __salt__['infoblox.update_object'](obj['_ref'], data=data, **api_opts) - ret['result'] = True - ret['comment'] = 'infoblox record fields updated (note: removing fields might not update)' - #ret['changes'] = {'diff': diff } + new_obj = __salt__["infoblox.update_object"]( + obj["_ref"], data=data, **api_opts + ) + ret["result"] = True + ret[ + "comment" + ] = "infoblox record fields updated (note: removing fields might not update)" + # ret['changes'] = {'diff': diff } return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'would attempt to create infoblox record {0}'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "would attempt to create infoblox record {0}".format(name) return ret - new_obj_ref = __salt__['infoblox.create_host'](data=data, **api_opts) - new_obj = __salt__['infoblox.get_host'](name=name, **api_opts) + new_obj_ref = __salt__["infoblox.create_host"](data=data, **api_opts) + new_obj = __salt__["infoblox.get_host"](name=name, **api_opts) - ret['result'] = True - ret['comment'] = 'infoblox record created' - ret['changes'] = {'old': 'None', 'new': {'_ref': new_obj_ref, 'data': new_obj}} + ret["result"] = True + ret["comment"] = "infoblox record created" + ret["changes"] = {"old": "None", "new": {"_ref": new_obj_ref, "data": new_obj}} return ret def absent(name=None, ipv4addr=None, mac=None, **api_opts): - ''' + """ Ensure the host with the given Name ipv4addr or mac is removed. State example: @@ -146,21 +164,23 @@ def absent(name=None, ipv4addr=None, mac=None, **api_opts): infoblox_host_record.absent: - name: - mac: 12:02:12:31:23:43 - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - obj = __salt__['infoblox.get_host'](name=name, ipv4addr=ipv4addr, mac=mac, **api_opts) + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} + obj = __salt__["infoblox.get_host"]( + name=name, ipv4addr=ipv4addr, mac=mac, **api_opts + ) if not obj: - ret['result'] = True - ret['comment'] = 'infoblox already removed' + ret["result"] = True + ret["comment"] = "infoblox already removed" return ret - if __opts__['test']: - ret['result'] = None - ret['changes'] = {'old': obj, 'new': 'absent'} + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {"old": obj, "new": "absent"} return ret - if __salt__['infoblox.delete_host'](name=name, mac=mac, **api_opts): - ret['result'] = True - ret['changes'] = {'old': obj, 'new': 'absent'} + if __salt__["infoblox.delete_host"](name=name, mac=mac, **api_opts): + ret["result"] = True + ret["changes"] = {"old": obj, "new": "absent"} return ret diff --git a/salt/states/infoblox_range.py b/salt/states/infoblox_range.py index 4b11c147e3f..f8492b746a6 100644 --- a/salt/states/infoblox_range.py +++ b/salt/states/infoblox_range.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Infoblox host record managment. functions accept api_opts: @@ -8,14 +8,14 @@ functions accept api_opts: api_url: server to connect to [default to pillar value] api_username: [default to pillar value] api_password: [default to pillar value] -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def present(name=None, start_addr=None, end_addr=None, data=None, **api_opts): - ''' + """ Ensure range record is present. infoblox_range.present: @@ -91,57 +91,67 @@ def present(name=None, start_addr=None, end_addr=None, data=None, **api_opts): 'use_unknown_clients': False, 'use_update_dns_on_lease_renewal': False } - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not data: data = {} - if 'name' not in data: - data.update({'name': name}) - if 'start_addr' not in data: - data.update({'start_addr': start_addr}) - if 'end_addr' not in data: - data.update({'end_addr': end_addr}) + if "name" not in data: + data.update({"name": name}) + if "start_addr" not in data: + data.update({"start_addr": start_addr}) + if "end_addr" not in data: + data.update({"end_addr": end_addr}) - obj = __salt__['infoblox.get_ipv4_range'](data['start_addr'], data['end_addr'], **api_opts) + obj = __salt__["infoblox.get_ipv4_range"]( + data["start_addr"], data["end_addr"], **api_opts + ) if obj is None: - obj = __salt__['infoblox.get_ipv4_range'](start_addr=data['start_addr'], end_addr=None, **api_opts) + obj = __salt__["infoblox.get_ipv4_range"]( + start_addr=data["start_addr"], end_addr=None, **api_opts + ) if obj is None: - obj = __salt__['infoblox.get_ipv4_range'](start_addr=None, end_addr=data['end_addr'], **api_opts) + obj = __salt__["infoblox.get_ipv4_range"]( + start_addr=None, end_addr=data["end_addr"], **api_opts + ) if obj: - diff = __salt__['infoblox.diff_objects'](data, obj) + diff = __salt__["infoblox.diff_objects"](data, obj) if not diff: - ret['result'] = True - ret['comment'] = 'supplied fields in correct state' + ret["result"] = True + ret["comment"] = "supplied fields in correct state" return ret if diff: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'would attempt to update record' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "would attempt to update record" return ret - new_obj = __salt__['infoblox.update_object'](obj['_ref'], data=data, **api_opts) - ret['result'] = True - ret['comment'] = 'record fields updated' - ret['changes'] = {'diff': diff} + new_obj = __salt__["infoblox.update_object"]( + obj["_ref"], data=data, **api_opts + ) + ret["result"] = True + ret["comment"] = "record fields updated" + ret["changes"] = {"diff": diff} return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'would attempt to create record {0}'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "would attempt to create record {0}".format(name) return ret - new_obj_ref = __salt__['infoblox.create_ipv4_range'](data, **api_opts) - new_obj = __salt__['infoblox.get_ipv4_range'](data['start_addr'], data['end_addr'], **api_opts) + new_obj_ref = __salt__["infoblox.create_ipv4_range"](data, **api_opts) + new_obj = __salt__["infoblox.get_ipv4_range"]( + data["start_addr"], data["end_addr"], **api_opts + ) - ret['result'] = True - ret['comment'] = 'record created' - ret['changes'] = {'old': 'None', 'new': {'_ref': new_obj_ref, 'data': new_obj}} + ret["result"] = True + ret["comment"] = "record created" + ret["changes"] = {"old": "None", "new": {"_ref": new_obj_ref, "data": new_obj}} return ret def absent(name=None, start_addr=None, end_addr=None, data=None, **api_opts): - ''' + """ Ensure the range is removed Supplying the end of the range is optional. @@ -156,36 +166,44 @@ def absent(name=None, start_addr=None, end_addr=None, data=None, **api_opts): infoblox_range.absent: - name: - start_addr: 127.0.1.20 - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if not data: data = {} - if 'name' not in data: - data.update({'name': name}) - if 'start_addr' not in data: - data.update({'start_addr': start_addr}) - if 'end_addr' not in data: - data.update({'end_addr': end_addr}) + if "name" not in data: + data.update({"name": name}) + if "start_addr" not in data: + data.update({"start_addr": start_addr}) + if "end_addr" not in data: + data.update({"end_addr": end_addr}) - obj = __salt__['infoblox.get_ipv4_range'](data['start_addr'], data['end_addr'], **api_opts) + obj = __salt__["infoblox.get_ipv4_range"]( + data["start_addr"], data["end_addr"], **api_opts + ) if obj is None: - obj = __salt__['infoblox.get_ipv4_range'](start_addr=data['start_addr'], end_addr=None, **api_opts) + obj = __salt__["infoblox.get_ipv4_range"]( + start_addr=data["start_addr"], end_addr=None, **api_opts + ) if obj is None: - obj = __salt__['infoblox.get_ipv4_range'](start_addr=None, end_addr=data['end_addr'], **api_opts) + obj = __salt__["infoblox.get_ipv4_range"]( + start_addr=None, end_addr=data["end_addr"], **api_opts + ) if not obj: - ret['result'] = True - ret['comment'] = 'already deleted' + ret["result"] = True + ret["comment"] = "already deleted" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'would attempt to delete range' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "would attempt to delete range" return ret - if __salt__['infoblox.delete_object'](objref=obj['_ref']): - ret['result'] = True - ret['changes'] = {'old': 'Found {0} - {1}'.format(start_addr, end_addr), - 'new': 'Removed'} + if __salt__["infoblox.delete_object"](objref=obj["_ref"]): + ret["result"] = True + ret["changes"] = { + "old": "Found {0} - {1}".format(start_addr, end_addr), + "new": "Removed", + } return ret diff --git a/salt/states/ini_manage.py b/salt/states/ini_manage.py index 3c3fff8cbd2..94f905de65a 100644 --- a/salt/states/ini_manage.py +++ b/salt/states/ini_manage.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage ini files ================ @@ -8,7 +8,7 @@ Manage ini files :depends: re :platform: all -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -17,18 +17,18 @@ from __future__ import absolute_import, print_function, unicode_literals from salt.ext import six from salt.utils.odict import OrderedDict -__virtualname__ = 'ini' +__virtualname__ = "ini" def __virtual__(): - ''' + """ Only load if the ini module is available - ''' - return __virtualname__ if 'ini.set_option' in __salt__ else False + """ + return __virtualname__ if "ini.set_option" in __salt__ else False -def options_present(name, sections=None, separator='=', strict=False): - ''' +def options_present(name, sections=None, separator="=", strict=False): + """ .. code-block:: yaml /home/saltminion/api-paste.ini: @@ -47,14 +47,15 @@ def options_present(name, sections=None, separator='=', strict=False): used changes dict will contain the list of changes made - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'No anomaly detected' - } - if __opts__['test']: - ret['comment'] = '' + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "No anomaly detected", + } + if __opts__["test"]: + ret["comment"] = "" # pylint: disable=too-many-nested-blocks try: changes = {} @@ -63,7 +64,7 @@ def options_present(name, sections=None, separator='=', strict=False): for sname, sbody in sections.items(): if not isinstance(sbody, (dict, OrderedDict)): options.update({sname: sbody}) - cur_ini = __salt__['ini.get_ini'](name, separator) + cur_ini = __salt__["ini.get_ini"](name, separator) original_top_level_opts = {} original_sections = {} for key, val in cur_ini.items(): @@ -71,80 +72,109 @@ def options_present(name, sections=None, separator='=', strict=False): original_sections.update({key: val}) else: original_top_level_opts.update({key: val}) - if __opts__['test']: + if __opts__["test"]: for option in options: if option in original_top_level_opts: - if six.text_type(original_top_level_opts[option]) == six.text_type(options[option]): - ret['comment'] += 'Unchanged key {0}.\n'.format(option) + if six.text_type( + original_top_level_opts[option] + ) == six.text_type(options[option]): + ret["comment"] += "Unchanged key {0}.\n".format(option) else: - ret['comment'] += 'Changed key {0}.\n'.format(option) - ret['result'] = None + ret["comment"] += "Changed key {0}.\n".format(option) + ret["result"] = None else: - ret['comment'] += 'Changed key {0}.\n'.format(option) - ret['result'] = None + ret["comment"] += "Changed key {0}.\n".format(option) + ret["result"] = None else: - options_updated = __salt__['ini.set_option'](name, options, separator) + options_updated = __salt__["ini.set_option"](name, options, separator) changes.update(options_updated) if strict: for opt_to_remove in set(original_top_level_opts).difference(options): - if __opts__['test']: - ret['comment'] += 'Removed key {0}.\n'.format(opt_to_remove) - ret['result'] = None + if __opts__["test"]: + ret["comment"] += "Removed key {0}.\n".format(opt_to_remove) + ret["result"] = None else: - __salt__['ini.remove_option'](name, None, opt_to_remove, separator) - changes.update({opt_to_remove: {'before': original_top_level_opts[opt_to_remove], - 'after': None}}) - for section_name, section_body in [(sname, sbody) for sname, sbody in sections.items() - if isinstance(sbody, (dict, OrderedDict))]: - section_descr = ' in section ' + section_name if section_name else '' + __salt__["ini.remove_option"]( + name, None, opt_to_remove, separator + ) + changes.update( + { + opt_to_remove: { + "before": original_top_level_opts[opt_to_remove], + "after": None, + } + } + ) + for section_name, section_body in [ + (sname, sbody) + for sname, sbody in sections.items() + if isinstance(sbody, (dict, OrderedDict)) + ]: + section_descr = " in section " + section_name if section_name else "" changes[section_name] = {} if strict: original = cur_ini.get(section_name, {}) - for key_to_remove in set(original.keys()).difference(section_body.keys()): - orig_value = original_sections.get(section_name, {}).get(key_to_remove, '#-#-') - if __opts__['test']: - ret['comment'] += 'Deleted key {0}{1}.\n'.format(key_to_remove, section_descr) - ret['result'] = None + for key_to_remove in set(original.keys()).difference( + section_body.keys() + ): + orig_value = original_sections.get(section_name, {}).get( + key_to_remove, "#-#-" + ) + if __opts__["test"]: + ret["comment"] += "Deleted key {0}{1}.\n".format( + key_to_remove, section_descr + ) + ret["result"] = None else: - __salt__['ini.remove_option'](name, section_name, key_to_remove, separator) - changes[section_name].update({key_to_remove: ''}) - changes[section_name].update({key_to_remove: {'before': orig_value, - 'after': None}}) - if __opts__['test']: + __salt__["ini.remove_option"]( + name, section_name, key_to_remove, separator + ) + changes[section_name].update({key_to_remove: ""}) + changes[section_name].update( + {key_to_remove: {"before": orig_value, "after": None}} + ) + if __opts__["test"]: for option in section_body: - if six.text_type(section_body[option]) == \ - six.text_type(original_sections.get(section_name, {}).get(option, '#-#-')): - ret['comment'] += 'Unchanged key {0}{1}.\n'.format(option, section_descr) + if six.text_type(section_body[option]) == six.text_type( + original_sections.get(section_name, {}).get(option, "#-#-") + ): + ret["comment"] += "Unchanged key {0}{1}.\n".format( + option, section_descr + ) else: - ret['comment'] += 'Changed key {0}{1}.\n'.format(option, section_descr) - ret['result'] = None + ret["comment"] += "Changed key {0}{1}.\n".format( + option, section_descr + ) + ret["result"] = None else: - options_updated = __salt__['ini.set_option'](name, {section_name: section_body}, separator) + options_updated = __salt__["ini.set_option"]( + name, {section_name: section_body}, separator + ) if options_updated: changes[section_name].update(options_updated[section_name]) if not changes[section_name]: del changes[section_name] else: - if not __opts__['test']: - changes = __salt__['ini.set_option'](name, sections, separator) + if not __opts__["test"]: + changes = __salt__["ini.set_option"](name, sections, separator) except (IOError, KeyError) as err: - ret['comment'] = "{0}".format(err) - ret['result'] = False + ret["comment"] = "{0}".format(err) + ret["result"] = False return ret - if 'error' in changes: - ret['result'] = False - ret['comment'] = 'Errors encountered. {0}'.format(changes['error']) - ret['changes'] = {} + if "error" in changes: + ret["result"] = False + ret["comment"] = "Errors encountered. {0}".format(changes["error"]) + ret["changes"] = {} else: for ciname, body in changes.items(): if body: - ret['comment'] = 'Changes take effect' - ret['changes'].update({ciname: changes[ciname]}) + ret["comment"] = "Changes take effect" + ret["changes"].update({ciname: changes[ciname]}) return ret -def options_absent(name, sections=None, separator='='): - ''' +def options_absent(name, sections=None, separator="="): + """ .. code-block:: yaml /home/saltminion/api-paste.ini: @@ -161,22 +191,23 @@ def options_absent(name, sections=None, separator='='): dict will be untouched changes dict will contain the list of changes made - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'No anomaly detected' - } - if __opts__['test']: - ret['result'] = True - ret['comment'] = '' + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "No anomaly detected", + } + if __opts__["test"]: + ret["result"] = True + ret["comment"] = "" for section in sections or {}: - section_name = ' in section ' + section if section else '' + section_name = " in section " + section if section else "" try: - cur_section = __salt__['ini.get_section'](name, section, separator) + cur_section = __salt__["ini.get_section"](name, section, separator) except IOError as err: - ret['comment'] = "{0}".format(err) - ret['result'] = False + ret["comment"] = "{0}".format(err) + ret["result"] = False return ret except AttributeError: cur_section = section @@ -184,44 +215,48 @@ def options_absent(name, sections=None, separator='='): for key in sections[section]: cur_value = cur_section.get(key) if not cur_value: - ret['comment'] += 'Key {0}{1} does not exist.\n'.format(key, section_name) + ret["comment"] += "Key {0}{1} does not exist.\n".format( + key, section_name + ) continue - ret['comment'] += 'Deleted key {0}{1}.\n'.format(key, section_name) - ret['result'] = None + ret["comment"] += "Deleted key {0}{1}.\n".format(key, section_name) + ret["result"] = None else: option = section - if not __salt__['ini.get_option'](name, None, option, separator): - ret['comment'] += 'Key {0} does not exist.\n'.format(option) + if not __salt__["ini.get_option"](name, None, option, separator): + ret["comment"] += "Key {0} does not exist.\n".format(option) continue - ret['comment'] += 'Deleted key {0}.\n'.format(option) - ret['result'] = None + ret["comment"] += "Deleted key {0}.\n".format(option) + ret["result"] = None - if ret['comment'] == '': - ret['comment'] = 'No changes detected.' + if ret["comment"] == "": + ret["comment"] = "No changes detected." return ret sections = sections or {} for section, keys in six.iteritems(sections): for key in keys: try: - current_value = __salt__['ini.remove_option'](name, section, key, separator) + current_value = __salt__["ini.remove_option"]( + name, section, key, separator + ) except IOError as err: - ret['comment'] = "{0}".format(err) - ret['result'] = False + ret["comment"] = "{0}".format(err) + ret["result"] = False return ret if not current_value: continue - if section not in ret['changes']: - ret['changes'].update({section: {}}) - ret['changes'][section].update({key: current_value}) + if section not in ret["changes"]: + ret["changes"].update({section: {}}) + ret["changes"][section].update({key: current_value}) if not isinstance(sections[section], (dict, OrderedDict)): - ret['changes'].update({section: current_value}) + ret["changes"].update({section: current_value}) # break - ret['comment'] = 'Changes take effect' + ret["comment"] = "Changes take effect" return ret -def sections_present(name, sections=None, separator='='): - ''' +def sections_present(name, sections=None, separator="="): + """ .. code-block:: yaml /home/saltminion/api-paste.ini: @@ -236,51 +271,52 @@ def sections_present(name, sections=None, separator='='): options present in file and not specified in sections will be deleted changes dict will contain the sections that changed - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'No anomaly detected' - } - if __opts__['test']: - ret['result'] = True - ret['comment'] = '' + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "No anomaly detected", + } + if __opts__["test"]: + ret["result"] = True + ret["comment"] = "" try: - cur_ini = __salt__['ini.get_ini'](name, separator) + cur_ini = __salt__["ini.get_ini"](name, separator) except IOError as err: - ret['result'] = False - ret['comment'] = "{0}".format(err) + ret["result"] = False + ret["comment"] = "{0}".format(err) return ret for section in sections or {}: if section in cur_ini: - ret['comment'] += 'Section unchanged {0}.\n'.format(section) + ret["comment"] += "Section unchanged {0}.\n".format(section) continue else: - ret['comment'] += 'Created new section {0}.\n'.format(section) - ret['result'] = None - if ret['comment'] == '': - ret['comment'] = 'No changes detected.' + ret["comment"] += "Created new section {0}.\n".format(section) + ret["result"] = None + if ret["comment"] == "": + ret["comment"] = "No changes detected." return ret section_to_update = {} for section_name in sections or []: section_to_update.update({section_name: {}}) try: - changes = __salt__['ini.set_option'](name, section_to_update, separator) + changes = __salt__["ini.set_option"](name, section_to_update, separator) except IOError as err: - ret['result'] = False - ret['comment'] = "{0}".format(err) + ret["result"] = False + ret["comment"] = "{0}".format(err) return ret - if 'error' in changes: - ret['result'] = False - ret['changes'] = 'Errors encountered {0}'.format(changes['error']) + if "error" in changes: + ret["result"] = False + ret["changes"] = "Errors encountered {0}".format(changes["error"]) return ret - ret['changes'] = changes - ret['comment'] = 'Changes take effect' + ret["changes"] = changes + ret["comment"] = "Changes take effect" return ret -def sections_absent(name, sections=None, separator='='): - ''' +def sections_absent(name, sections=None, separator="="): + """ .. code-block:: yaml /home/saltminion/api-paste.ini: @@ -292,39 +328,40 @@ def sections_absent(name, sections=None, separator='='): options present in file and not specified in sections will be deleted changes dict will contain the sections that changed - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'No anomaly detected' - } - if __opts__['test']: - ret['result'] = True - ret['comment'] = '' + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "No anomaly detected", + } + if __opts__["test"]: + ret["result"] = True + ret["comment"] = "" try: - cur_ini = __salt__['ini.get_ini'](name, separator) + cur_ini = __salt__["ini.get_ini"](name, separator) except IOError as err: - ret['result'] = False - ret['comment'] = "{0}".format(err) + ret["result"] = False + ret["comment"] = "{0}".format(err) return ret for section in sections or []: if section not in cur_ini: - ret['comment'] += 'Section {0} does not exist.\n'.format(section) + ret["comment"] += "Section {0} does not exist.\n".format(section) continue - ret['comment'] += 'Deleted section {0}.\n'.format(section) - ret['result'] = None - if ret['comment'] == '': - ret['comment'] = 'No changes detected.' + ret["comment"] += "Deleted section {0}.\n".format(section) + ret["result"] = None + if ret["comment"] == "": + ret["comment"] = "No changes detected." return ret for section in sections or []: try: - cur_section = __salt__['ini.remove_section'](name, section, separator) + cur_section = __salt__["ini.remove_section"](name, section, separator) except IOError as err: - ret['result'] = False - ret['comment'] = "{0}".format(err) + ret["result"] = False + ret["comment"] = "{0}".format(err) return ret if not cur_section: continue - ret['changes'][section] = cur_section - ret['comment'] = 'Changes take effect' + ret["changes"][section] = cur_section + ret["comment"] = "Changes take effect" return ret diff --git a/salt/states/ipmi.py b/salt/states/ipmi.py index fddff7bd287..26fc33a6f31 100644 --- a/salt/states/ipmi.py +++ b/salt/states/ipmi.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage IPMI devices over LAN ============================ @@ -33,7 +33,7 @@ Every call can override the config defaults: - api_host: myipmi.hostname.com - api_user: root - api_pass: apassword -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -51,8 +51,8 @@ def __virtual__(): return (IMPORT_ERR is None, IMPORT_ERR) -def boot_device(name='default', **kwargs): - ''' +def boot_device(name="default", **kwargs): + """ Request power state change name = ``default`` @@ -69,32 +69,32 @@ def boot_device(name='default', **kwargs): - api_pass= - api_port=623 - api_kg=None - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - org = __salt__['ipmi.get_bootdev'](**kwargs) - if 'bootdev' in org: - org = org['bootdev'] + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} + org = __salt__["ipmi.get_bootdev"](**kwargs) + if "bootdev" in org: + org = org["bootdev"] if org == name: - ret['result'] = True - ret['comment'] = 'system already in this state' + ret["result"] = True + ret["comment"] = "system already in this state" return ret - if __opts__['test']: - ret['comment'] = 'would change boot device' - ret['result'] = None - ret['changes'] = {'old': org, 'new': name} + if __opts__["test"]: + ret["comment"] = "would change boot device" + ret["result"] = None + ret["changes"] = {"old": org, "new": name} return ret - outdddd = __salt__['ipmi.set_bootdev'](bootdev=name, **kwargs) - ret['comment'] = 'changed boot device' - ret['result'] = True - ret['changes'] = {'old': org, 'new': name} + outdddd = __salt__["ipmi.set_bootdev"](bootdev=name, **kwargs) + ret["comment"] = "changed boot device" + ret["result"] = True + ret["changes"] = {"old": org, "new": name} return ret -def power(name='power_on', wait=300, **kwargs): - ''' +def power(name="power_on", wait=300, **kwargs): + """ Request power state change name @@ -115,41 +115,50 @@ def power(name='power_on', wait=300, **kwargs): - api_pass= - api_port=623 - api_kg=None - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - org = __salt__['ipmi.get_power'](**kwargs) + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} + org = __salt__["ipmi.get_power"](**kwargs) state_map = { - 'off': 'off', - 'on': 'on', - 'power_off': 'off', - 'power_on': 'on', - 'shutdown': 'off', - 'reset': 'na', - 'boot': 'na' + "off": "off", + "on": "on", + "power_off": "off", + "power_on": "on", + "shutdown": "off", + "reset": "na", + "boot": "na", } if org == state_map[name]: - ret['result'] = True - ret['comment'] = 'system already in this state' + ret["result"] = True + ret["comment"] = "system already in this state" return ret - if __opts__['test']: - ret['comment'] = 'would power: {0} system'.format(name) - ret['result'] = None - ret['changes'] = {'old': org, 'new': name} + if __opts__["test"]: + ret["comment"] = "would power: {0} system".format(name) + ret["result"] = None + ret["changes"] = {"old": org, "new": name} return ret - outdddd = __salt__['ipmi.set_power'](name, wait=wait, **kwargs) - ret['comment'] = 'changed system power' - ret['result'] = True - ret['changes'] = {'old': org, 'new': name} + outdddd = __salt__["ipmi.set_power"](name, wait=wait, **kwargs) + ret["comment"] = "changed system power" + ret["result"] = True + ret["changes"] = {"old": org, "new": name} return ret -def user_present(name, uid, password, channel=14, callback=False, - link_auth=True, ipmi_msg=True, privilege_level='administrator', **kwargs): - ''' +def user_present( + name, + uid, + password, + channel=14, + callback=False, + link_auth=True, + ipmi_msg=True, + privilege_level="administrator", + **kwargs +): + """ Ensure IPMI user and user privileges. name @@ -213,52 +222,58 @@ def user_present(name, uid, password, channel=14, callback=False, - api_pass= - api_port=623 - api_kg=None - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - org_user = __salt__['ipmi.get_user'](uid=uid, channel=channel, **kwargs) + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} + org_user = __salt__["ipmi.get_user"](uid=uid, channel=channel, **kwargs) change = False - if org_user['access']['callback'] != callback: + if org_user["access"]["callback"] != callback: change = True - if org_user['access']['link_auth'] != link_auth: + if org_user["access"]["link_auth"] != link_auth: change = True - if org_user['access']['ipmi_msg'] != ipmi_msg: + if org_user["access"]["ipmi_msg"] != ipmi_msg: change = True - if org_user['access']['privilege_level'] != privilege_level: + if org_user["access"]["privilege_level"] != privilege_level: change = True - if __salt__['ipmi.set_user_password'](uid, mode='test_password', - password=password, **kwargs) is False: + if ( + __salt__["ipmi.set_user_password"]( + uid, mode="test_password", password=password, **kwargs + ) + is False + ): change = True if change is False: - ret['result'] = True - ret['comment'] = 'user already present' + ret["result"] = True + ret["comment"] = "user already present" return ret - if __opts__['test']: - ret['comment'] = 'would (re)create user' - ret['result'] = None - ret['changes'] = {'old': org_user, 'new': name} + if __opts__["test"]: + ret["comment"] = "would (re)create user" + ret["result"] = None + ret["changes"] = {"old": org_user, "new": name} return ret - __salt__['ipmi.ensure_user'](uid, - name, - password, - channel, - callback, - link_auth, - ipmi_msg, - privilege_level, - **kwargs) - current_user = __salt__['ipmi.get_user'](uid=uid, channel=channel, **kwargs) - ret['comment'] = '(re)created user' - ret['result'] = True - ret['changes'] = {'old': org_user, 'new': current_user} + __salt__["ipmi.ensure_user"]( + uid, + name, + password, + channel, + callback, + link_auth, + ipmi_msg, + privilege_level, + **kwargs + ) + current_user = __salt__["ipmi.get_user"](uid=uid, channel=channel, **kwargs) + ret["comment"] = "(re)created user" + ret["result"] = True + ret["changes"] = {"old": org_user, "new": current_user} return ret def user_absent(name, channel=14, **kwargs): - ''' + """ Remove user Delete all user (uid) records having the matching name. @@ -274,24 +289,24 @@ def user_absent(name, channel=14, **kwargs): - api_pass= - api_port=623 - api_kg=None - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - user_id_list = __salt__['ipmi.get_name_uids'](name, channel, **kwargs) + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} + user_id_list = __salt__["ipmi.get_name_uids"](name, channel, **kwargs) if len(user_id_list) == 0: - ret['result'] = True - ret['comment'] = 'user already absent' + ret["result"] = True + ret["comment"] = "user already absent" return ret - if __opts__['test']: - ret['comment'] = 'would delete user(s)' - ret['result'] = None - ret['changes'] = {'delete': user_id_list} + if __opts__["test"]: + ret["comment"] = "would delete user(s)" + ret["result"] = None + ret["changes"] = {"delete": user_id_list} return ret for uid in user_id_list: - __salt__['ipmi.delete_user'](uid, channel, **kwargs) + __salt__["ipmi.delete_user"](uid, channel, **kwargs) - ret['comment'] = 'user(s) removed' - ret['changes'] = {'old': user_id_list, 'new': 'None'} + ret["comment"] = "user(s) removed" + ret["changes"] = {"old": user_id_list, "new": "None"} return ret diff --git a/salt/states/ipset.py b/salt/states/ipset.py index 64e36eaba28..f2c4f474e4a 100644 --- a/salt/states/ipset.py +++ b/salt/states/ipset.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of ipsets ====================== @@ -51,22 +51,25 @@ in IPTables Firewalls. setname: ipset.flush: -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the ipset module is available in __salt__ - ''' - return 'ipset.version' in __salt__ + """ + if "ipset.version" in __salt__: + return True + return (False, "ipset module could not be loaded") -def set_present(name, set_type, family='ipv4', **kwargs): - ''' +def set_present(name, set_type, family="ipv4", **kwargs): + """ .. versionadded:: 2014.7.0 Verify the set exists. @@ -79,92 +82,79 @@ def set_present(name, set_type, family='ipv4', **kwargs): family Networking family, either ipv4 or ipv6 - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - set_check = __salt__['ipset.check_set'](name) + set_check = __salt__["ipset.check_set"](name) if set_check is True: - ret['result'] = True - ret['comment'] = ('ipset set {0} already exists for {1}' - .format(name, family)) + ret["result"] = True + ret["comment"] = "ipset set {0} already exists for {1}".format(name, family) return ret - if __opts__['test']: - ret['comment'] = 'ipset set {0} would be added for {1}'.format( - name, - family) + if __opts__["test"]: + ret["comment"] = "ipset set {0} would be added for {1}".format(name, family) return ret - command = __salt__['ipset.new_set'](name, set_type, family, **kwargs) + command = __salt__["ipset.new_set"](name, set_type, family, **kwargs) if command is True: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = ('ipset set {0} created successfully for {1}' - .format(name, family)) + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "ipset set {0} created successfully for {1}".format( + name, family + ) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to create set {0} for {2}: {1}'.format( - name, - command.strip(), - family + ret["result"] = False + ret["comment"] = "Failed to create set {0} for {2}: {1}".format( + name, command.strip(), family ) return ret -def set_absent(name, family='ipv4', **kwargs): - ''' +def set_absent(name, family="ipv4", **kwargs): + """ .. versionadded:: 2014.7.0 Verify the set is absent. family Networking family, either ipv4 or ipv6 - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - set_check = __salt__['ipset.check_set'](name, family) + set_check = __salt__["ipset.check_set"](name, family) if not set_check: - ret['result'] = True - ret['comment'] = ('ipset set {0} for {1} is already absent' - .format(name, family)) + ret["result"] = True + ret["comment"] = "ipset set {0} for {1} is already absent".format(name, family) return ret - if __opts__['test']: - ret['comment'] = 'ipset set {0} for {1} would be removed'.format( - name, - family) + if __opts__["test"]: + ret["comment"] = "ipset set {0} for {1} would be removed".format(name, family) return ret - flush_set = __salt__['ipset.flush'](name, family) + flush_set = __salt__["ipset.flush"](name, family) if flush_set: - command = __salt__['ipset.delete_set'](name, family) + command = __salt__["ipset.delete_set"](name, family) if command is True: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = ('ipset set {0} deleted successfully for family {1}' - .format(name, family)) + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "ipset set {0} deleted successfully for family {1}".format( + name, family + ) else: - ret['result'] = False - ret['comment'] = ('Failed to delete set {0} for {2}: {1}' - .format(name, command.strip(), family)) + ret["result"] = False + ret["comment"] = "Failed to delete set {0} for {2}: {1}".format( + name, command.strip(), family + ) else: - ret['result'] = False - ret['comment'] = 'Failed to flush set {0} for {2}: {1}'.format( - name, - flush_set.strip(), - family + ret["result"] = False + ret["comment"] = "Failed to flush set {0} for {2}: {1}".format( + name, flush_set.strip(), family ) return ret -def present(name, entry=None, family='ipv4', **kwargs): - ''' +def present(name, entry=None, family="ipv4", **kwargs): + """ .. versionadded:: 2014.7.0 Append a entry to a set @@ -179,15 +169,12 @@ def present(name, entry=None, family='ipv4', **kwargs): family Network family, ipv4 or ipv6. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not entry: - ret['result'] = False - ret['comment'] = ('ipset entry must be specified') + ret["result"] = False + ret["comment"] = "ipset entry must be specified" return ret entries = [] @@ -197,47 +184,50 @@ def present(name, entry=None, family='ipv4', **kwargs): entries.append(entry) for entry in entries: - entry_opts = '' - if ' ' in entry: - entry, entry_opts = entry.split(' ', 1) - if 'timeout' in kwargs and 'timeout' not in entry_opts: - entry_opts = 'timeout {0} {1}'.format(kwargs['timeout'], entry_opts) - if 'comment' in kwargs and 'comment' not in entry_opts: - entry_opts = '{0} comment "{1}"'.format(entry_opts, kwargs['comment']) - _entry = ' '.join([entry, entry_opts.lstrip()]).strip() + entry_opts = "" + if " " in entry: + entry, entry_opts = entry.split(" ", 1) + if "timeout" in kwargs and "timeout" not in entry_opts: + entry_opts = "timeout {0} {1}".format(kwargs["timeout"], entry_opts) + if "comment" in kwargs and "comment" not in entry_opts: + entry_opts = '{0} comment "{1}"'.format(entry_opts, kwargs["comment"]) + _entry = " ".join([entry, entry_opts.lstrip()]).strip() - if __salt__['ipset.check'](kwargs['set_name'], - _entry, - family) is True: - ret['comment'] += 'entry for {0} already in set {1} for {2}\n'.format( - entry, - kwargs['set_name'], - family) + if __salt__["ipset.check"](kwargs["set_name"], _entry, family) is True: + ret["comment"] += "entry for {0} already in set {1} for {2}\n".format( + entry, kwargs["set_name"], family + ) else: - if __opts__['test']: - ret['result'] = None - ret['comment'] += 'entry {0} would be added to set {1} for family {2}\n'.format( - entry, - kwargs['set_name'], - family) + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] += "entry {0} would be added to set {1} for family {2}\n".format( + entry, kwargs["set_name"], family + ) else: - command = __salt__['ipset.add'](kwargs['set_name'], _entry, family, **kwargs) - if 'Error' not in command: - ret['changes'] = {'locale': name} - ret['comment'] += 'entry {0} added to set {1} for family {2}\n'.format( - _entry, - kwargs['set_name'], - family) + command = __salt__["ipset.add"]( + kwargs["set_name"], _entry, family, **kwargs + ) + if "Error" not in command: + ret["changes"] = {"locale": name} + ret[ + "comment" + ] += "entry {0} added to set {1} for family {2}\n".format( + _entry, kwargs["set_name"], family + ) else: - ret['result'] = False - ret['comment'] = 'Failed to add to entry {1} to set {0} for family {2}.\n{3}'.format( - kwargs['set_name'], - _entry, family, command) + ret["result"] = False + ret[ + "comment" + ] = "Failed to add to entry {1} to set {0} for family {2}.\n{3}".format( + kwargs["set_name"], _entry, family, command + ) return ret -def absent(name, entry=None, entries=None, family='ipv4', **kwargs): - ''' +def absent(name, entry=None, entries=None, family="ipv4", **kwargs): + """ .. versionadded:: 2014.7.0 Remove a entry or entries from a chain @@ -249,15 +239,12 @@ def absent(name, entry=None, entries=None, family='ipv4', **kwargs): family Network family, ipv4 or ipv6. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not entry: - ret['result'] = False - ret['comment'] = ('ipset entry must be specified') + ret["result"] = False + ret["comment"] = "ipset entry must be specified" return ret entries = [] @@ -267,50 +254,55 @@ def absent(name, entry=None, entries=None, family='ipv4', **kwargs): entries.append(entry) for entry in entries: - entry_opts = '' - if ' ' in entry: - entry, entry_opts = entry.split(' ', 1) - if 'timeout' in kwargs and 'timeout' not in entry_opts: - entry_opts = 'timeout {0} {1}'.format(kwargs['timeout'], entry_opts) - if 'comment' in kwargs and 'comment' not in entry_opts: - entry_opts = '{0} comment "{1}"'.format(entry_opts, kwargs['comment']) - _entry = ' '.join([entry, entry_opts]).strip() + entry_opts = "" + if " " in entry: + entry, entry_opts = entry.split(" ", 1) + if "timeout" in kwargs and "timeout" not in entry_opts: + entry_opts = "timeout {0} {1}".format(kwargs["timeout"], entry_opts) + if "comment" in kwargs and "comment" not in entry_opts: + entry_opts = '{0} comment "{1}"'.format(entry_opts, kwargs["comment"]) + _entry = " ".join([entry, entry_opts]).strip() - log.debug('_entry %s', _entry) - if not __salt__['ipset.check'](kwargs['set_name'], - _entry, - family) is True: - ret['result'] = True - ret['comment'] += 'ipset entry for {0} not present in set {1} for {2}\n'.format( - _entry, - kwargs['set_name'], - family) + log.debug("_entry %s", _entry) + if not __salt__["ipset.check"](kwargs["set_name"], _entry, family) is True: + ret["result"] = True + ret[ + "comment" + ] += "ipset entry for {0} not present in set {1} for {2}\n".format( + _entry, kwargs["set_name"], family + ) else: - if __opts__['test']: - ret['result'] = None - ret['comment'] += 'ipset entry {0} would be removed from set {1} for {2}\n'.format( - entry, - kwargs['set_name'], - family) + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] += "ipset entry {0} would be removed from set {1} for {2}\n".format( + entry, kwargs["set_name"], family + ) else: - command = __salt__['ipset.delete'](kwargs['set_name'], entry, family, **kwargs) - if 'Error' not in command: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] += 'ipset entry {1} removed from set {0} for {2}\n'.format( - kwargs['set_name'], - _entry, - family) + command = __salt__["ipset.delete"]( + kwargs["set_name"], entry, family, **kwargs + ) + if "Error" not in command: + ret["changes"] = {"locale": name} + ret["result"] = True + ret[ + "comment" + ] += "ipset entry {1} removed from set {0} for {2}\n".format( + kwargs["set_name"], _entry, family + ) else: - ret['result'] = False - ret['comment'] = 'Failed to delete ipset entry from set {0} for {2}. ' \ - 'Attempted entry was {1}.\n' \ - '{3}\n'.format(kwargs['set_name'], _entry, family, command) + ret["result"] = False + ret["comment"] = ( + "Failed to delete ipset entry from set {0} for {2}. " + "Attempted entry was {1}.\n" + "{3}\n".format(kwargs["set_name"], _entry, family, command) + ) return ret -def flush(name, family='ipv4', **kwargs): - ''' +def flush(name, family="ipv4", **kwargs): + """ .. versionadded:: 2014.7.0 Flush current ipset set @@ -318,34 +310,30 @@ def flush(name, family='ipv4', **kwargs): family Networking family, either ipv4 or ipv6 - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - set_check = __salt__['ipset.check_set'](name) + set_check = __salt__["ipset.check_set"](name) if set_check is False: - ret['result'] = False - ret['comment'] = ('ipset set {0} does not exist for {1}' - .format(name, family)) + ret["result"] = False + ret["comment"] = "ipset set {0} does not exist for {1}".format(name, family) return ret - if __opts__['test']: - ret['comment'] = 'ipset entries in set {0} for {1} would be flushed'.format( - name, - family) + if __opts__["test"]: + ret["comment"] = "ipset entries in set {0} for {1} would be flushed".format( + name, family + ) return ret - if __salt__['ipset.flush'](name, family): - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Flushed ipset entries from set {0} for {1}'.format( - name, - family + if __salt__["ipset.flush"](name, family): + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Flushed ipset entries from set {0} for {1}".format( + name, family ) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to flush ipset entries from set {0} for {1}' \ - ''.format(name, family) + ret["result"] = False + ret["comment"] = "Failed to flush ipset entries from set {0} for {1}" "".format( + name, family + ) return ret diff --git a/salt/states/iptables.py b/salt/states/iptables.py index 872862ece2f..fc6c15f4cfe 100644 --- a/salt/states/iptables.py +++ b/salt/states/iptables.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of iptables ====================== @@ -239,22 +239,24 @@ Example rules for IPSec policy: option, an alternate version of this check will be performed using the output of iptables-save. This may have unintended consequences on legacy releases of ``iptables``. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS def __virtual__(): - ''' + """ Only load if the locale module is available in __salt__ - ''' - return 'iptables.version' in __salt__ + """ + if "iptables.version" in __salt__: + return True + return (False, "iptables module could not be loaded") -def chain_present(name, table='filter', family='ipv4'): - ''' +def chain_present(name, table="filter", family="ipv4"): + """ .. versionadded:: 2014.1.0 Verify the chain is exist. @@ -267,46 +269,47 @@ def chain_present(name, table='filter', family='ipv4'): family Networking family, either ipv4 or ipv6 - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - chain_check = __salt__['iptables.check_chain'](table, name, family) + chain_check = __salt__["iptables.check_chain"](table, name, family) if chain_check is True: - ret['result'] = True - ret['comment'] = ('iptables {0} chain is already exist in {1} table for {2}' - .format(name, table, family)) + ret["result"] = True + ret[ + "comment" + ] = "iptables {0} chain is already exist in {1} table for {2}".format( + name, table, family + ) return ret - if __opts__['test']: - ret['comment'] = 'iptables {0} chain in {1} table needs to be set for {2}'.format( - name, - table, - family) + if __opts__["test"]: + ret[ + "comment" + ] = "iptables {0} chain in {1} table needs to be set for {2}".format( + name, table, family + ) return ret - command = __salt__['iptables.new_chain'](table, name, family) + command = __salt__["iptables.new_chain"](table, name, family) if command is True: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = ('iptables {0} chain in {1} table create success for {2}' - .format(name, table, family)) + ret["changes"] = {"locale": name} + ret["result"] = True + ret[ + "comment" + ] = "iptables {0} chain in {1} table create success for {2}".format( + name, table, family + ) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} chain in {1} table: {2} for {3}'.format( - name, - table, - command.strip(), - family + ret["result"] = False + ret["comment"] = "Failed to create {0} chain in {1} table: {2} for {3}".format( + name, table, command.strip(), family ) return ret -def chain_absent(name, table='filter', family='ipv4'): - ''' +def chain_absent(name, table="filter", family="ipv4"): + """ .. versionadded:: 2014.1.0 Verify the chain is absent. @@ -316,50 +319,54 @@ def chain_absent(name, table='filter', family='ipv4'): family Networking family, either ipv4 or ipv6 - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - chain_check = __salt__['iptables.check_chain'](table, name, family) + chain_check = __salt__["iptables.check_chain"](table, name, family) if not chain_check: - ret['result'] = True - ret['comment'] = ('iptables {0} chain is already absent in {1} table for {2}' - .format(name, table, family)) + ret["result"] = True + ret[ + "comment" + ] = "iptables {0} chain is already absent in {1} table for {2}".format( + name, table, family + ) return ret - if __opts__['test']: - ret['comment'] = 'iptables {0} chain in {1} table needs to be removed {2}'.format( - name, - table, - family) + if __opts__["test"]: + ret[ + "comment" + ] = "iptables {0} chain in {1} table needs to be removed {2}".format( + name, table, family + ) return ret - flush_chain = __salt__['iptables.flush'](table, name, family) + flush_chain = __salt__["iptables.flush"](table, name, family) if not flush_chain: - command = __salt__['iptables.delete_chain'](table, name, family) + command = __salt__["iptables.delete_chain"](table, name, family) if command is True: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = ('iptables {0} chain in {1} table delete success for {2}' - .format(name, table, family)) + ret["changes"] = {"locale": name} + ret["result"] = True + ret[ + "comment" + ] = "iptables {0} chain in {1} table delete success for {2}".format( + name, table, family + ) else: - ret['result'] = False - ret['comment'] = ('Failed to delete {0} chain in {1} table: {2} for {3}' - .format(name, table, command.strip(), family)) + ret["result"] = False + ret[ + "comment" + ] = "Failed to delete {0} chain in {1} table: {2} for {3}".format( + name, table, command.strip(), family + ) else: - ret['result'] = False - ret['comment'] = 'Failed to flush {0} chain in {1} table: {2} for {3}'.format( - name, - table, - flush_chain.strip(), - family + ret["result"] = False + ret["comment"] = "Failed to flush {0} chain in {1} table: {2} for {3}".format( + name, table, flush_chain.strip(), family ) return ret -def append(name, table='filter', family='ipv4', **kwargs): - ''' +def append(name, table="filter", family="ipv4", **kwargs): + """ .. versionadded:: 0.17.0 Add a rule to the end of the specified chain. @@ -381,113 +388,107 @@ def append(name, table='filter', family='ipv4', **kwargs): Jump options that doesn't take arguments should be passed in with an empty string. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if 'rules' in kwargs: - ret['changes']['locale'] = [] + if "rules" in kwargs: + ret["changes"]["locale"] = [] comments = [] save = False - for rule in kwargs['rules']: - if 'rules' in rule: - del rule['rules'] - if '__agg__' in rule: - del rule['__agg__'] - if 'save' in rule and rule['save']: + for rule in kwargs["rules"]: + if "rules" in rule: + del rule["rules"] + if "__agg__" in rule: + del rule["__agg__"] + if "save" in rule and rule["save"]: save = True - if rule['save'] is not True: - save_file = rule['save'] + if rule["save"] is not True: + save_file = rule["save"] else: save_file = True - rule['save'] = False + rule["save"] = False _ret = append(**rule) - if 'locale' in _ret['changes']: - ret['changes']['locale'].append(_ret['changes']['locale']) - comments.append(_ret['comment']) - ret['result'] = _ret['result'] + if "locale" in _ret["changes"]: + ret["changes"]["locale"].append(_ret["changes"]["locale"]) + comments.append(_ret["comment"]) + ret["result"] = _ret["result"] if save: if save_file is True: save_file = None - __salt__['iptables.save'](save_file, family=family) - if not ret['changes']['locale']: - del ret['changes']['locale'] - ret['comment'] = '\n'.join(comments) + __salt__["iptables.save"](save_file, family=family) + if not ret["changes"]["locale"]: + del ret["changes"]["locale"] + ret["comment"] = "\n".join(comments) return ret for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in kwargs: del kwargs[ignore] - kwargs['name'] = name - kwargs['table'] = table - rule = __salt__['iptables.build_rule'](family=family, **kwargs) - command = __salt__['iptables.build_rule'](full='True', family=family, command='A', **kwargs) - if __salt__['iptables.check'](table, - kwargs['chain'], - rule, - family) is True: - ret['result'] = True - ret['comment'] = 'iptables rule for {0} already set ({1}) for {2}'.format( - name, - command.strip(), - family) - if 'save' in kwargs and kwargs['save']: - if kwargs['save'] is not True: - filename = kwargs['save'] + kwargs["name"] = name + kwargs["table"] = table + rule = __salt__["iptables.build_rule"](family=family, **kwargs) + command = __salt__["iptables.build_rule"]( + full="True", family=family, command="A", **kwargs + ) + if __salt__["iptables.check"](table, kwargs["chain"], rule, family) is True: + ret["result"] = True + ret["comment"] = "iptables rule for {0} already set ({1}) for {2}".format( + name, command.strip(), family + ) + if "save" in kwargs and kwargs["save"]: + if kwargs["save"] is not True: + filename = kwargs["save"] else: filename = None - saved_rules = __salt__['iptables.get_saved_rules'](family=family) - _rules = __salt__['iptables.get_rules'](family=family) + saved_rules = __salt__["iptables.get_saved_rules"](family=family) + _rules = __salt__["iptables.get_rules"](family=family) __rules = [] for table in _rules: for chain in _rules[table]: - __rules.append(_rules[table][chain].get('rules')) + __rules.append(_rules[table][chain].get("rules")) __saved_rules = [] for table in saved_rules: for chain in saved_rules[table]: - __saved_rules.append(saved_rules[table][chain].get('rules')) + __saved_rules.append(saved_rules[table][chain].get("rules")) # Only save if rules in memory are different than saved rules if __rules != __saved_rules: - out = __salt__['iptables.save'](filename, family=family) - ret['comment'] += ('\nSaved iptables rule {0} for {1}\n' - '{2}\n{3}').format(name, family, command.strip(), out) + out = __salt__["iptables.save"](filename, family=family) + ret["comment"] += ( + "\nSaved iptables rule {0} for {1}\n" "{2}\n{3}" + ).format(name, family, command.strip(), out) return ret - if __opts__['test']: - ret['comment'] = 'iptables rule for {0} needs to be set ({1}) for {2}'.format( - name, - command.strip(), - family) + if __opts__["test"]: + ret["comment"] = "iptables rule for {0} needs to be set ({1}) for {2}".format( + name, command.strip(), family + ) return ret - if __salt__['iptables.append'](table, kwargs['chain'], rule, family): - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Set iptables rule for {0} to: {1} for {2}'.format( - name, - command.strip(), - family) - if 'save' in kwargs: - if kwargs['save']: - if kwargs['save'] is not True: - filename = kwargs['save'] + if __salt__["iptables.append"](table, kwargs["chain"], rule, family): + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Set iptables rule for {0} to: {1} for {2}".format( + name, command.strip(), family + ) + if "save" in kwargs: + if kwargs["save"]: + if kwargs["save"] is not True: + filename = kwargs["save"] else: filename = None - out = __salt__['iptables.save'](filename, family=family) - ret['comment'] = ('Set and saved iptables rule {0} for {1}\n' - '{2}\n{3}').format(name, family, command.strip(), out) + out = __salt__["iptables.save"](filename, family=family) + ret["comment"] = ( + "Set and saved iptables rule {0} for {1}\n" "{2}\n{3}" + ).format(name, family, command.strip(), out) return ret else: - ret['result'] = False - ret['comment'] = ('Failed to set iptables rule for {0}.\n' - 'Attempted rule was {1} for {2}').format( - name, - command.strip(), family) + ret["result"] = False + ret["comment"] = ( + "Failed to set iptables rule for {0}.\n" "Attempted rule was {1} for {2}" + ).format(name, command.strip(), family) return ret -def insert(name, table='filter', family='ipv4', **kwargs): - ''' +def insert(name, table="filter", family="ipv4", **kwargs): + """ .. versionadded:: 2014.1.0 Insert a rule into a chain @@ -513,109 +514,105 @@ def insert(name, table='filter', family='ipv4', **kwargs): Jump options that doesn't take arguments should be passed in with an empty string. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if 'rules' in kwargs: - ret['changes']['locale'] = [] + if "rules" in kwargs: + ret["changes"]["locale"] = [] comments = [] save = False - for rule in kwargs['rules']: - if 'rules' in rule: - del rule['rules'] - if '__agg__' in rule: - del rule['__agg__'] - if 'save' in rule and rule['save']: + for rule in kwargs["rules"]: + if "rules" in rule: + del rule["rules"] + if "__agg__" in rule: + del rule["__agg__"] + if "save" in rule and rule["save"]: save = True - if rule['save'] is not True: - save_file = rule['save'] + if rule["save"] is not True: + save_file = rule["save"] else: save_file = True - rule['save'] = False + rule["save"] = False _ret = insert(**rule) - if 'locale' in _ret['changes']: - ret['changes']['locale'].append(_ret['changes']['locale']) - comments.append(_ret['comment']) - ret['result'] = _ret['result'] + if "locale" in _ret["changes"]: + ret["changes"]["locale"].append(_ret["changes"]["locale"]) + comments.append(_ret["comment"]) + ret["result"] = _ret["result"] if save: if save_file is True: save_file = None - __salt__['iptables.save'](save_file, family=family) - if not ret['changes']['locale']: - del ret['changes']['locale'] - ret['comment'] = '\n'.join(comments) + __salt__["iptables.save"](save_file, family=family) + if not ret["changes"]["locale"]: + del ret["changes"]["locale"] + ret["comment"] = "\n".join(comments) return ret for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in kwargs: del kwargs[ignore] - kwargs['name'] = name - kwargs['table'] = table - rule = __salt__['iptables.build_rule'](family=family, **kwargs) - command = __salt__['iptables.build_rule'](full=True, family=family, command='I', **kwargs) - if __salt__['iptables.check'](table, - kwargs['chain'], - rule, - family) is True: - ret['result'] = True - ret['comment'] = 'iptables rule for {0} already set for {1} ({2})'.format( - name, - family, - command.strip()) - if 'save' in kwargs and kwargs['save']: - if kwargs['save'] is not True: - filename = kwargs['save'] + kwargs["name"] = name + kwargs["table"] = table + rule = __salt__["iptables.build_rule"](family=family, **kwargs) + command = __salt__["iptables.build_rule"]( + full=True, family=family, command="I", **kwargs + ) + if __salt__["iptables.check"](table, kwargs["chain"], rule, family) is True: + ret["result"] = True + ret["comment"] = "iptables rule for {0} already set for {1} ({2})".format( + name, family, command.strip() + ) + if "save" in kwargs and kwargs["save"]: + if kwargs["save"] is not True: + filename = kwargs["save"] else: filename = None - saved_rules = __salt__['iptables.get_saved_rules'](family=family) - _rules = __salt__['iptables.get_rules'](family=family) + saved_rules = __salt__["iptables.get_saved_rules"](family=family) + _rules = __salt__["iptables.get_rules"](family=family) __rules = [] for table in _rules: for chain in _rules[table]: - __rules.append(_rules[table][chain].get('rules')) + __rules.append(_rules[table][chain].get("rules")) __saved_rules = [] for table in saved_rules: for chain in saved_rules[table]: - __saved_rules.append(saved_rules[table][chain].get('rules')) + __saved_rules.append(saved_rules[table][chain].get("rules")) # Only save if rules in memory are different than saved rules if __rules != __saved_rules: - out = __salt__['iptables.save'](filename, family=family) - ret['comment'] += ('\nSaved iptables rule {0} for {1}\n' - '{2}\n{3}').format(name, family, command.strip(), out) + out = __salt__["iptables.save"](filename, family=family) + ret["comment"] += ( + "\nSaved iptables rule {0} for {1}\n" "{2}\n{3}" + ).format(name, family, command.strip(), out) return ret - if __opts__['test']: - ret['comment'] = 'iptables rule for {0} needs to be set for {1} ({2})'.format( - name, - family, - command.strip()) + if __opts__["test"]: + ret["comment"] = "iptables rule for {0} needs to be set for {1} ({2})".format( + name, family, command.strip() + ) return ret - if not __salt__['iptables.insert'](table, kwargs['chain'], kwargs['position'], rule, family): - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Set iptables rule for {0} to: {1} for {2}'.format( - name, - command.strip(), - family) - if 'save' in kwargs: - if kwargs['save']: - out = __salt__['iptables.save'](filename=None, family=family) - ret['comment'] = ('Set and saved iptables rule {0} for {1}\n' - '{2}\n{3}').format(name, family, command.strip(), out) + if not __salt__["iptables.insert"]( + table, kwargs["chain"], kwargs["position"], rule, family + ): + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Set iptables rule for {0} to: {1} for {2}".format( + name, command.strip(), family + ) + if "save" in kwargs: + if kwargs["save"]: + out = __salt__["iptables.save"](filename=None, family=family) + ret["comment"] = ( + "Set and saved iptables rule {0} for {1}\n" "{2}\n{3}" + ).format(name, family, command.strip(), out) return ret else: - ret['result'] = False - ret['comment'] = ('Failed to set iptables rule for {0}.\n' - 'Attempted rule was {1}').format( - name, - command.strip()) + ret["result"] = False + ret["comment"] = ( + "Failed to set iptables rule for {0}.\n" "Attempted rule was {1}" + ).format(name, command.strip()) return ret -def delete(name, table='filter', family='ipv4', **kwargs): - ''' +def delete(name, table="filter", family="ipv4", **kwargs): + """ .. versionadded:: 2014.1.0 Delete a rule to a chain @@ -637,103 +634,97 @@ def delete(name, table='filter', family='ipv4', **kwargs): Jump options that doesn't take arguments should be passed in with an empty string. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if 'rules' in kwargs: - ret['changes']['locale'] = [] + if "rules" in kwargs: + ret["changes"]["locale"] = [] comments = [] save = False - for rule in kwargs['rules']: - if 'rules' in rule: - del rule['rules'] - if '__agg__' in rule: - del rule['__agg__'] - if 'save' in rule and rule['save']: - if rule['save'] is not True: - save_file = rule['save'] + for rule in kwargs["rules"]: + if "rules" in rule: + del rule["rules"] + if "__agg__" in rule: + del rule["__agg__"] + if "save" in rule and rule["save"]: + if rule["save"] is not True: + save_file = rule["save"] else: save_file = True - rule['save'] = False + rule["save"] = False _ret = delete(**rule) - if 'locale' in _ret['changes']: - ret['changes']['locale'].append(_ret['changes']['locale']) - comments.append(_ret['comment']) - ret['result'] = _ret['result'] + if "locale" in _ret["changes"]: + ret["changes"]["locale"].append(_ret["changes"]["locale"]) + comments.append(_ret["comment"]) + ret["result"] = _ret["result"] if save: if save_file is True: save_file = None - __salt__['iptables.save'](save_file, family=family) - if not ret['changes']['locale']: - del ret['changes']['locale'] - ret['comment'] = '\n'.join(comments) + __salt__["iptables.save"](save_file, family=family) + if not ret["changes"]["locale"]: + del ret["changes"]["locale"] + ret["comment"] = "\n".join(comments) return ret for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in kwargs: del kwargs[ignore] - kwargs['name'] = name - kwargs['table'] = table - rule = __salt__['iptables.build_rule'](family=family, **kwargs) - command = __salt__['iptables.build_rule'](full=True, family=family, command='D', **kwargs) + kwargs["name"] = name + kwargs["table"] = table + rule = __salt__["iptables.build_rule"](family=family, **kwargs) + command = __salt__["iptables.build_rule"]( + full=True, family=family, command="D", **kwargs + ) - if not __salt__['iptables.check'](table, - kwargs['chain'], - rule, - family) is True: - if 'position' not in kwargs: - ret['result'] = True - ret['comment'] = 'iptables rule for {0} already absent for {1} ({2})'.format( - name, - family, - command.strip()) + if not __salt__["iptables.check"](table, kwargs["chain"], rule, family) is True: + if "position" not in kwargs: + ret["result"] = True + ret[ + "comment" + ] = "iptables rule for {0} already absent for {1} ({2})".format( + name, family, command.strip() + ) return ret - if __opts__['test']: - ret['comment'] = 'iptables rule for {0} needs to be deleted for {1} ({2})'.format( - name, - family, - command.strip()) + if __opts__["test"]: + ret[ + "comment" + ] = "iptables rule for {0} needs to be deleted for {1} ({2})".format( + name, family, command.strip() + ) return ret - if 'position' in kwargs: - result = __salt__['iptables.delete']( - table, - kwargs['chain'], - family=family, - position=kwargs['position']) + if "position" in kwargs: + result = __salt__["iptables.delete"]( + table, kwargs["chain"], family=family, position=kwargs["position"] + ) else: - result = __salt__['iptables.delete']( - table, - kwargs['chain'], - family=family, - rule=rule) + result = __salt__["iptables.delete"]( + table, kwargs["chain"], family=family, rule=rule + ) if not result: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Delete iptables rule for {0} {1}'.format( - name, - command.strip()) - if 'save' in kwargs: - if kwargs['save']: - out = __salt__['iptables.save'](filename=None, family=family) - ret['comment'] = ('Deleted and saved iptables rule {0} for {1}\n' - '{2}\n{3}').format(name, family, command.strip(), out) + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Delete iptables rule for {0} {1}".format( + name, command.strip() + ) + if "save" in kwargs: + if kwargs["save"]: + out = __salt__["iptables.save"](filename=None, family=family) + ret["comment"] = ( + "Deleted and saved iptables rule {0} for {1}\n" "{2}\n{3}" + ).format(name, family, command.strip(), out) return ret else: - ret['result'] = False - ret['comment'] = ('Failed to delete iptables rule for {0}.\n' - 'Attempted rule was {1}').format( - name, - command.strip()) + ret["result"] = False + ret["comment"] = ( + "Failed to delete iptables rule for {0}.\n" "Attempted rule was {1}" + ).format(name, command.strip()) return ret -def set_policy(name, table='filter', family='ipv4', **kwargs): - ''' +def set_policy(name, table="filter", family="ipv4", **kwargs): + """ .. versionadded:: 2014.1.0 Sets the default policy for iptables firewall tables @@ -747,61 +738,56 @@ def set_policy(name, table='filter', family='ipv4', **kwargs): policy The requested table policy - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in kwargs: del kwargs[ignore] - if __salt__['iptables.get_policy']( - table, - kwargs['chain'], - family) == kwargs['policy']: - ret['result'] = True - ret['comment'] = ('iptables default policy for chain {0} on table {1} for {2} already set to {3}' - .format(kwargs['chain'], table, family, kwargs['policy'])) - return ret - if __opts__['test']: - ret['comment'] = 'iptables default policy for chain {0} on table {1} for {2} needs to be set to {3}'.format( - kwargs['chain'], - table, - family, - kwargs['policy'] + if ( + __salt__["iptables.get_policy"](table, kwargs["chain"], family) + == kwargs["policy"] + ): + ret["result"] = True + ret[ + "comment" + ] = "iptables default policy for chain {0} on table {1} for {2} already set to {3}".format( + kwargs["chain"], table, family, kwargs["policy"] ) return ret - if not __salt__['iptables.set_policy']( - table, - kwargs['chain'], - kwargs['policy'], - family): - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Set default policy for {0} to {1} family {2}'.format( - kwargs['chain'], - kwargs['policy'], - family + if __opts__["test"]: + ret[ + "comment" + ] = "iptables default policy for chain {0} on table {1} for {2} needs to be set to {3}".format( + kwargs["chain"], table, family, kwargs["policy"] ) - if 'save' in kwargs: - if kwargs['save']: - __salt__['iptables.save'](filename=None, family=family) - ret['comment'] = 'Set and saved default policy for {0} to {1} family {2}'.format( - kwargs['chain'], - kwargs['policy'], - family + return ret + if not __salt__["iptables.set_policy"]( + table, kwargs["chain"], kwargs["policy"], family + ): + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Set default policy for {0} to {1} family {2}".format( + kwargs["chain"], kwargs["policy"], family + ) + if "save" in kwargs: + if kwargs["save"]: + __salt__["iptables.save"](filename=None, family=family) + ret[ + "comment" + ] = "Set and saved default policy for {0} to {1} family {2}".format( + kwargs["chain"], kwargs["policy"], family ) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to set iptables default policy' + ret["result"] = False + ret["comment"] = "Failed to set iptables default policy" return ret -def flush(name, table='filter', family='ipv4', **kwargs): - ''' +def flush(name, table="filter", family="ipv4", **kwargs): + """ .. versionadded:: 2014.1.0 Flush current iptables state @@ -812,70 +798,68 @@ def flush(name, table='filter', family='ipv4', **kwargs): family Networking family, either ipv4 or ipv6 - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in kwargs: del kwargs[ignore] - if 'chain' not in kwargs: - kwargs['chain'] = '' - if __opts__['test']: - ret['comment'] = 'iptables rules in {0} table {1} chain {2} family needs to be flushed'.format( - name, - table, - family) + if "chain" not in kwargs: + kwargs["chain"] = "" + if __opts__["test"]: + ret[ + "comment" + ] = "iptables rules in {0} table {1} chain {2} family needs to be flushed".format( + name, table, family + ) return ret - if not __salt__['iptables.flush'](table, kwargs['chain'], family): - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Flush iptables rules in {0} table {1} chain {2} family'.format( - table, - kwargs['chain'], - family + if not __salt__["iptables.flush"](table, kwargs["chain"], family): + ret["changes"] = {"locale": name} + ret["result"] = True + ret[ + "comment" + ] = "Flush iptables rules in {0} table {1} chain {2} family".format( + table, kwargs["chain"], family ) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to flush iptables rules' + ret["result"] = False + ret["comment"] = "Failed to flush iptables rules" return ret def mod_aggregate(low, chunks, running): - ''' + """ The mod_aggregate function which looks up all rules in the available low chunks and merges them into a single rules ref in the present low data - ''' + """ rules = [] agg_enabled = [ - 'append', - 'insert', + "append", + "insert", ] - if low.get('fun') not in agg_enabled: + if low.get("fun") not in agg_enabled: return low for chunk in chunks: - tag = __utils__['state.gen_tag'](chunk) + tag = __utils__["state.gen_tag"](chunk) if tag in running: # Already ran the iptables state, skip aggregation continue - if chunk.get('state') == 'iptables': - if '__agg__' in chunk: + if chunk.get("state") == "iptables": + if "__agg__" in chunk: continue # Check for the same function - if chunk.get('fun') != low.get('fun'): + if chunk.get("fun") != low.get("fun"): continue if chunk not in rules: rules.append(chunk) - chunk['__agg__'] = True + chunk["__agg__"] = True if rules: - if 'rules' in low: - low['rules'].extend(rules) + if "rules" in low: + low["rules"].extend(rules) else: - low['rules'] = rules + low["rules"] = rules return low diff --git a/salt/states/jboss7.py b/salt/states/jboss7.py index e35b720cc87..f2e30b7c945 100644 --- a/salt/states/jboss7.py +++ b/salt/states/jboss7.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage JBoss 7 Application Server via CLI interface .. versionadded:: 2015.5.0 @@ -34,13 +34,14 @@ Example of application deployment from local filesystem: For the sake of brevity, examples for each state assume that jboss_config is contained in the pillar. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time + import logging import re +import time import traceback # Import Salt libs @@ -53,8 +54,10 @@ from salt.ext import six log = logging.getLogger(__name__) -def datasource_exists(name, jboss_config, datasource_properties, recreate=False, profile=None): - ''' +def datasource_exists( + name, jboss_config, datasource_properties, recreate=False, profile=None +): + """ Ensures that a datasource with given properties exist on the jboss instance. If datasource doesn't exist, it is created, otherwise only the properties that are different will be updated. @@ -87,70 +90,109 @@ def datasource_exists(name, jboss_config, datasource_properties, recreate=False, - jboss_config: {{ pillar['jboss'] }} - profile: full-ha - ''' - log.debug(" ======================== STATE: jboss7.datasource_exists (name: %s) ", name) - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + log.debug( + " ======================== STATE: jboss7.datasource_exists (name: %s) ", name + ) + ret = {"name": name, "result": True, "changes": {}, "comment": ""} has_changed = False ds_current_properties = {} - ds_result = __salt__['jboss7.read_datasource'](jboss_config=jboss_config, name=name, profile=profile) - if ds_result['success']: - ds_current_properties = ds_result['result'] + ds_result = __salt__["jboss7.read_datasource"]( + jboss_config=jboss_config, name=name, profile=profile + ) + if ds_result["success"]: + ds_current_properties = ds_result["result"] if recreate: - remove_result = __salt__['jboss7.remove_datasource'](jboss_config=jboss_config, name=name, profile=profile) - if remove_result['success']: - ret['changes']['removed'] = name + remove_result = __salt__["jboss7.remove_datasource"]( + jboss_config=jboss_config, name=name, profile=profile + ) + if remove_result["success"]: + ret["changes"]["removed"] = name else: - ret['result'] = False - ret['comment'] = 'Could not remove datasource. Stdout: '+remove_result['stdout'] + ret["result"] = False + ret["comment"] = ( + "Could not remove datasource. Stdout: " + remove_result["stdout"] + ) return ret has_changed = True # if we are here, we have already made a change - create_result = __salt__['jboss7.create_datasource'](jboss_config=jboss_config, name=name, datasource_properties=datasource_properties, profile=profile) - if create_result['success']: - ret['changes']['created'] = name + create_result = __salt__["jboss7.create_datasource"]( + jboss_config=jboss_config, + name=name, + datasource_properties=datasource_properties, + profile=profile, + ) + if create_result["success"]: + ret["changes"]["created"] = name else: - ret['result'] = False - ret['comment'] = 'Could not create datasource. Stdout: '+create_result['stdout'] + ret["result"] = False + ret["comment"] = ( + "Could not create datasource. Stdout: " + create_result["stdout"] + ) return ret - read_result = __salt__['jboss7.read_datasource'](jboss_config=jboss_config, name=name, profile=profile) - if read_result['success']: - ds_new_properties = read_result['result'] + read_result = __salt__["jboss7.read_datasource"]( + jboss_config=jboss_config, name=name, profile=profile + ) + if read_result["success"]: + ds_new_properties = read_result["result"] else: - ret['result'] = False - ret['comment'] = 'Could not read datasource. Stdout: '+read_result['stdout'] + ret["result"] = False + ret["comment"] = ( + "Could not read datasource. Stdout: " + read_result["stdout"] + ) return ret else: - update_result = __salt__['jboss7.update_datasource'](jboss_config=jboss_config, name=name, new_properties=datasource_properties, profile=profile) - if not update_result['success']: - ret['result'] = False - ret['comment'] = 'Could not update datasource. '+update_result['comment'] + update_result = __salt__["jboss7.update_datasource"]( + jboss_config=jboss_config, + name=name, + new_properties=datasource_properties, + profile=profile, + ) + if not update_result["success"]: + ret["result"] = False + ret["comment"] = ( + "Could not update datasource. " + update_result["comment"] + ) # some changes to the datasource may have already been made, therefore we don't quit here else: - ret['comment'] = 'Datasource updated.' + ret["comment"] = "Datasource updated." - read_result = __salt__['jboss7.read_datasource'](jboss_config=jboss_config, name=name, profile=profile) - ds_new_properties = read_result['result'] + read_result = __salt__["jboss7.read_datasource"]( + jboss_config=jboss_config, name=name, profile=profile + ) + ds_new_properties = read_result["result"] else: - if ds_result['err_code'] in ('JBAS014807', 'WFLYCTL0216'): # ok, resource not exists: - create_result = __salt__['jboss7.create_datasource'](jboss_config=jboss_config, name=name, datasource_properties=datasource_properties, profile=profile) - if create_result['success']: - read_result = __salt__['jboss7.read_datasource'](jboss_config=jboss_config, name=name, profile=profile) - ds_new_properties = read_result['result'] - ret['comment'] = 'Datasource created.' + if ds_result["err_code"] in ( + "JBAS014807", + "WFLYCTL0216", + ): # ok, resource not exists: + create_result = __salt__["jboss7.create_datasource"]( + jboss_config=jboss_config, + name=name, + datasource_properties=datasource_properties, + profile=profile, + ) + if create_result["success"]: + read_result = __salt__["jboss7.read_datasource"]( + jboss_config=jboss_config, name=name, profile=profile + ) + ds_new_properties = read_result["result"] + ret["comment"] = "Datasource created." else: - ret['result'] = False - ret['comment'] = 'Could not create datasource. Stdout: '+create_result['stdout'] + ret["result"] = False + ret["comment"] = ( + "Could not create datasource. Stdout: " + create_result["stdout"] + ) else: - raise CommandExecutionError('Unable to handle error: {0}'.format(ds_result['failure-description'])) + raise CommandExecutionError( + "Unable to handle error: {0}".format(ds_result["failure-description"]) + ) - if ret['result']: + if ret["result"]: log.debug("ds_new_properties=%s", ds_new_properties) log.debug("ds_current_properties=%s", ds_current_properties) diff = dictdiffer.diff(ds_new_properties, ds_current_properties) @@ -158,35 +200,53 @@ def datasource_exists(name, jboss_config, datasource_properties, recreate=False, added = diff.added() if len(added) > 0: has_changed = True - ret['changes']['added'] = __format_ds_changes(added, ds_current_properties, ds_new_properties) + ret["changes"]["added"] = __format_ds_changes( + added, ds_current_properties, ds_new_properties + ) removed = diff.removed() if len(removed) > 0: has_changed = True - ret['changes']['removed'] = __format_ds_changes(removed, ds_current_properties, ds_new_properties) + ret["changes"]["removed"] = __format_ds_changes( + removed, ds_current_properties, ds_new_properties + ) changed = diff.changed() if len(changed) > 0: has_changed = True - ret['changes']['changed'] = __format_ds_changes(changed, ds_current_properties, ds_new_properties) + ret["changes"]["changed"] = __format_ds_changes( + changed, ds_current_properties, ds_new_properties + ) if not has_changed: - ret['comment'] = 'Datasource not changed.' + ret["comment"] = "Datasource not changed." return ret def __format_ds_changes(keys, old_dict, new_dict): - log.debug("__format_ds_changes(keys=%s, old_dict=%s, new_dict=%s)", keys, old_dict, new_dict) - changes = '' + log.debug( + "__format_ds_changes(keys=%s, old_dict=%s, new_dict=%s)", + keys, + old_dict, + new_dict, + ) + changes = "" for key in keys: log.debug("key=%s", key) if key in old_dict and key in new_dict: - changes += key+':'+__get_ds_value(old_dict, key)+'->'+__get_ds_value(new_dict, key)+'\n' + changes += ( + key + + ":" + + __get_ds_value(old_dict, key) + + "->" + + __get_ds_value(new_dict, key) + + "\n" + ) elif key in old_dict: - changes += key+'\n' + changes += key + "\n" elif key in new_dict: - changes += key+':'+__get_ds_value(new_dict, key)+'\n' + changes += key + ":" + __get_ds_value(new_dict, key) + "\n" return changes @@ -195,13 +255,13 @@ def __get_ds_value(dct, key): if key == "password": return "***" elif dct[key] is None: - return 'undefined' + return "undefined" else: return six.text_type(dct[key]) def bindings_exist(name, jboss_config, bindings, profile=None): - ''' + """ Ensures that given JNDI binding are present on the server. If a binding doesn't exist on the server it will be created. If it already exists its value will be changed. @@ -224,54 +284,77 @@ def bindings_exist(name, jboss_config, bindings, profile=None): 'java:global/sampleapp/configurationFile': '/var/opt/sampleapp/config.properties' - jboss_config: {{ pillar['jboss'] }} - ''' - log.debug(" ======================== STATE: jboss7.bindings_exist (name: %s) (profile: %s) ", name, profile) - log.debug('bindings=%s', bindings) - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': 'Bindings not changed.'} + """ + log.debug( + " ======================== STATE: jboss7.bindings_exist (name: %s) (profile: %s) ", + name, + profile, + ) + log.debug("bindings=%s", bindings) + ret = { + "name": name, + "result": True, + "changes": {}, + "comment": "Bindings not changed.", + } has_changed = False for key in bindings: value = six.text_type(bindings[key]) - query_result = __salt__['jboss7.read_simple_binding'](binding_name=key, jboss_config=jboss_config, profile=profile) - if query_result['success']: - current_value = query_result['result']['value'] + query_result = __salt__["jboss7.read_simple_binding"]( + binding_name=key, jboss_config=jboss_config, profile=profile + ) + if query_result["success"]: + current_value = query_result["result"]["value"] if current_value != value: - update_result = __salt__['jboss7.update_simple_binding'](binding_name=key, value=value, jboss_config=jboss_config, profile=profile) - if update_result['success']: + update_result = __salt__["jboss7.update_simple_binding"]( + binding_name=key, + value=value, + jboss_config=jboss_config, + profile=profile, + ) + if update_result["success"]: has_changed = True - __log_binding_change(ret['changes'], 'changed', key, value, current_value) + __log_binding_change( + ret["changes"], "changed", key, value, current_value + ) else: - raise CommandExecutionError(update_result['failure-description']) + raise CommandExecutionError(update_result["failure-description"]) else: - if query_result['err_code'] in ('JBAS014807', 'WFLYCTL0216'): # ok, resource not exists: - create_result = __salt__['jboss7.create_simple_binding'](binding_name=key, value=value, jboss_config=jboss_config, profile=profile) - if create_result['success']: + if query_result["err_code"] in ( + "JBAS014807", + "WFLYCTL0216", + ): # ok, resource not exists: + create_result = __salt__["jboss7.create_simple_binding"]( + binding_name=key, + value=value, + jboss_config=jboss_config, + profile=profile, + ) + if create_result["success"]: has_changed = True - __log_binding_change(ret['changes'], 'added', key, value) + __log_binding_change(ret["changes"], "added", key, value) else: - raise CommandExecutionError(create_result['failure-description']) + raise CommandExecutionError(create_result["failure-description"]) else: - raise CommandExecutionError(query_result['failure-description']) + raise CommandExecutionError(query_result["failure-description"]) if has_changed: - ret['comment'] = 'Bindings changed.' + ret["comment"] = "Bindings changed." return ret def __log_binding_change(changes, type_, key, new, old=None): if type_ not in changes: - changes[type_] = '' + changes[type_] = "" if old is None: - changes[type_] += key + ':' + new + '\n' + changes[type_] += key + ":" + new + "\n" else: - changes[type_] += key + ':' + old + '->' + new + '\n' + changes[type_] += key + ":" + old + "->" + new + "\n" def deployed(name, jboss_config, salt_source=None): - '''Ensures that the given application is deployed on server. + """Ensures that the given application is deployed on server. jboss_config: Dict with connection properties (see state description) @@ -324,90 +407,117 @@ def deployed(name, jboss_config, salt_source=None): downloading from the master but also HTTP, HTTPS, FTP, Amazon S3, and OpenStack Swift. - ''' + """ log.debug(" ======================== STATE: jboss7.deployed (name: %s) ", name) - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - comment = '' + comment = "" validate_success, validate_comment = __validate_arguments(jboss_config, salt_source) if not validate_success: return _error(ret, validate_comment) resolved_source, get_artifact_comment, changed = __get_artifact(salt_source) - log.debug('resolved_source=%s', resolved_source) - log.debug('get_artifact_comment=%s', get_artifact_comment) + log.debug("resolved_source=%s", resolved_source) + log.debug("get_artifact_comment=%s", get_artifact_comment) - comment = __append_comment(new_comment=get_artifact_comment, current_comment=comment) + comment = __append_comment( + new_comment=get_artifact_comment, current_comment=comment + ) if resolved_source is None: return _error(ret, get_artifact_comment) - find_success, deployment, find_comment = __find_deployment(jboss_config, salt_source) + find_success, deployment, find_comment = __find_deployment( + jboss_config, salt_source + ) if not find_success: return _error(ret, find_comment) require_deployment = True - log.debug('deployment=%s', deployment) + log.debug("deployment=%s", deployment) if deployment is not None: - if 'undeploy_force' in salt_source: - if salt_source['undeploy_force']: - ret['changes']['undeployed'] = __undeploy(jboss_config, deployment) + if "undeploy_force" in salt_source: + if salt_source["undeploy_force"]: + ret["changes"]["undeployed"] = __undeploy(jboss_config, deployment) else: if changed: - ret['changes']['undeployed'] = __undeploy(jboss_config, deployment) + ret["changes"]["undeployed"] = __undeploy(jboss_config, deployment) else: require_deployment = False - comment = __append_comment(new_comment='The artifact {} was already deployed'.format(deployment), current_comment=comment) + comment = __append_comment( + new_comment="The artifact {} was already deployed".format( + deployment + ), + current_comment=comment, + ) else: - ret['changes']['undeployed'] = __undeploy(jboss_config, deployment) + ret["changes"]["undeployed"] = __undeploy(jboss_config, deployment) if require_deployment: - deploy_result = __salt__['jboss7.deploy'](jboss_config=jboss_config, source_file=resolved_source) - log.debug('deploy_result=%s', str(deploy_result)) - if deploy_result['success']: - comment = __append_comment(new_comment='Deployment completed.', current_comment=comment) - ret['changes']['deployed'] = resolved_source + deploy_result = __salt__["jboss7.deploy"]( + jboss_config=jboss_config, source_file=resolved_source + ) + log.debug("deploy_result=%s", str(deploy_result)) + if deploy_result["success"]: + comment = __append_comment( + new_comment="Deployment completed.", current_comment=comment + ) + ret["changes"]["deployed"] = resolved_source else: - comment = __append_comment(new_comment='''Deployment failed\nreturn code={retcode}\nstdout='{stdout}'\nstderr='{stderr}'''.format(**deploy_result), current_comment=comment) + comment = __append_comment( + new_comment="""Deployment failed\nreturn code={retcode}\nstdout='{stdout}'\nstderr='{stderr}""".format( + **deploy_result + ), + current_comment=comment, + ) _error(ret, comment) - ret['comment'] = comment + ret["comment"] = comment return ret def __undeploy(jboss_config, deployment): - __salt__['jboss7.undeploy'](jboss_config, deployment) + __salt__["jboss7.undeploy"](jboss_config, deployment) return deployment def __validate_arguments(jboss_config, salt_source): - result, comment = __check_dict_contains(jboss_config, 'jboss_config', ['cli_path', 'controller']) + result, comment = __check_dict_contains( + jboss_config, "jboss_config", ["cli_path", "controller"] + ) if salt_source is None: result = False - comment = __append_comment('No salt_source defined', comment) - result, comment = __check_dict_contains(salt_source, 'salt_source', ['target_file'], comment, result) + comment = __append_comment("No salt_source defined", comment) + result, comment = __check_dict_contains( + salt_source, "salt_source", ["target_file"], comment, result + ) return result, comment def __find_deployment(jboss_config, salt_source=None): result = None success = True - comment = '' - deployments = __salt__['jboss7.list_deployments'](jboss_config) - if salt_source is not None and 'undeploy' in salt_source and salt_source['undeploy']: - deployment_re = re.compile(salt_source['undeploy']) + comment = "" + deployments = __salt__["jboss7.list_deployments"](jboss_config) + if ( + salt_source is not None + and "undeploy" in salt_source + and salt_source["undeploy"] + ): + deployment_re = re.compile(salt_source["undeploy"]) for deployment in deployments: if deployment_re.match(deployment): if result is not None: success = False - comment = "More than one deployment matches regular expression: {0}. \n" \ - "For deployments from Salt file system deployments on JBoss are searched to find one that matches regular expression in 'undeploy' parameter.\n" \ - "Existing deployments: {1}".format(salt_source['undeploy'], ",".join(deployments)) + comment = ( + "More than one deployment matches regular expression: {0}. \n" + "For deployments from Salt file system deployments on JBoss are searched to find one that matches regular expression in 'undeploy' parameter.\n" + "Existing deployments: {1}".format( + salt_source["undeploy"], ",".join(deployments) + ) + ) else: result = deployment @@ -420,18 +530,18 @@ def __get_artifact(salt_source): changed = False if salt_source is None: - log.debug('salt_source == None') - comment = 'No salt_source defined' + log.debug("salt_source == None") + comment = "No salt_source defined" elif isinstance(salt_source, dict): - log.debug('file from salt master') + log.debug("file from salt master") - if 'source' in salt_source: + if "source" in salt_source: try: - sfn, source_sum, comment_ = __salt__['file.get_managed']( - name=salt_source['target_file'], + sfn, source_sum, comment_ = __salt__["file.get_managed"]( + name=salt_source["target_file"], template=None, - source=salt_source['source'], + source=salt_source["source"], source_hash=None, source_hash_name=None, user=None, @@ -442,13 +552,14 @@ def __get_artifact(salt_source): context=None, defaults=None, skip_verify=False, - kwargs=None) + kwargs=None, + ) - manage_result = __salt__['file.manage_file']( - name=salt_source['target_file'], + manage_result = __salt__["file.manage_file"]( + name=salt_source["target_file"], sfn=sfn, ret=None, - source=salt_source['source'], + source=salt_source["source"], source_sum=source_sum, user=None, group=None, @@ -460,29 +571,30 @@ def __get_artifact(salt_source): template=None, show_diff=True, contents=None, - dir_mode=None) + dir_mode=None, + ) - if manage_result['result']: - resolved_source = salt_source['target_file'] + if manage_result["result"]: + resolved_source = salt_source["target_file"] else: - comment = manage_result['comment'] + comment = manage_result["comment"] - if manage_result['changes']: + if manage_result["changes"]: changed = True except Exception as e: # pylint: disable=broad-except log.debug(traceback.format_exc()) - comment = 'Unable to manage file: {0}'.format(e) + comment = "Unable to manage file: {0}".format(e) else: - resolved_source = salt_source['target_file'] - comment = '' + resolved_source = salt_source["target_file"] + comment = "" return resolved_source, comment, changed def reloaded(name, jboss_config, timeout=60, interval=5): - ''' + """ Reloads configuration of jboss server. jboss_config: @@ -507,67 +619,83 @@ def reloaded(name, jboss_config, timeout=60, interval=5): configuration_reloaded: jboss7.reloaded: - jboss_config: {{ pillar['jboss'] }} - ''' + """ log.debug(" ======================== STATE: jboss7.reloaded (name: %s) ", name) - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - status = __salt__['jboss7.status'](jboss_config) - if not status['success'] or status['result'] not in ('running', 'reload-required'): - ret['result'] = False - ret['comment'] = "Cannot reload server configuration, it should be up and in 'running' or 'reload-required' state." + status = __salt__["jboss7.status"](jboss_config) + if not status["success"] or status["result"] not in ("running", "reload-required"): + ret["result"] = False + ret[ + "comment" + ] = "Cannot reload server configuration, it should be up and in 'running' or 'reload-required' state." return ret - result = __salt__['jboss7.reload'](jboss_config) - if result['success'] or \ - 'Operation failed: Channel closed' in result['stdout'] or \ - 'Communication error: java.util.concurrent.ExecutionException: Operation failed' in result['stdout']: + result = __salt__["jboss7.reload"](jboss_config) + if ( + result["success"] + or "Operation failed: Channel closed" in result["stdout"] + or "Communication error: java.util.concurrent.ExecutionException: Operation failed" + in result["stdout"] + ): wait_time = 0 status = None - while (status is None or not status['success'] or status['result'] != 'running') and wait_time < timeout: + while ( + status is None or not status["success"] or status["result"] != "running" + ) and wait_time < timeout: time.sleep(interval) wait_time += interval - status = __salt__['jboss7.status'](jboss_config) + status = __salt__["jboss7.status"](jboss_config) - if status['success'] and status['result'] == 'running': - ret['result'] = True - ret['comment'] = 'Configuration reloaded' - ret['changes']['reloaded'] = 'configuration' + if status["success"] and status["result"] == "running": + ret["result"] = True + ret["comment"] = "Configuration reloaded" + ret["changes"]["reloaded"] = "configuration" else: - ret['result'] = False - ret['comment'] = 'Could not reload the configuration. Timeout ({0} s) exceeded. '.format(timeout) - if not status['success']: - ret['comment'] = __append_comment('Could not connect to JBoss controller.', ret['comment']) + ret["result"] = False + ret[ + "comment" + ] = "Could not reload the configuration. Timeout ({0} s) exceeded. ".format( + timeout + ) + if not status["success"]: + ret["comment"] = __append_comment( + "Could not connect to JBoss controller.", ret["comment"] + ) else: - ret['comment'] = __append_comment(('Server is in {0} state'.format(status['result'])), ret['comment']) + ret["comment"] = __append_comment( + ("Server is in {0} state".format(status["result"])), ret["comment"] + ) else: - ret['result'] = False - ret['comment'] = 'Could not reload the configuration, stdout:'+result['stdout'] + ret["result"] = False + ret["comment"] = ( + "Could not reload the configuration, stdout:" + result["stdout"] + ) return ret -def __check_dict_contains(dct, dict_name, keys, comment='', result=True): +def __check_dict_contains(dct, dict_name, keys, comment="", result=True): for key in keys: if key not in six.iterkeys(dct): result = False - comment = __append_comment("Missing {0} in {1}".format(key, dict_name), comment) + comment = __append_comment( + "Missing {0} in {1}".format(key, dict_name), comment + ) return result, comment -def __append_comment(new_comment, current_comment=''): +def __append_comment(new_comment, current_comment=""): if current_comment is None and new_comment is None: - return '' + return "" if current_comment is None: return new_comment if new_comment is None: return current_comment - return current_comment+'\n'+new_comment + return current_comment + "\n" + new_comment def _error(ret, err_msg): - ret['result'] = False - ret['comment'] = err_msg + ret["result"] = False + ret["comment"] = err_msg return ret diff --git a/salt/states/jenkins.py b/salt/states/jenkins.py index 3485bac60b8..c962b03de47 100644 --- a/salt/states/jenkins.py +++ b/salt/states/jenkins.py @@ -1,26 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Management of Jenkins ===================== .. versionadded:: 2016.3.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import difflib import logging +# Import XML parser +import xml.etree.ElementTree as ET + +import salt.utils.files +import salt.utils.stringutils +from salt.exceptions import CommandExecutionError + # Import Salt libs from salt.ext import six from salt.ext.six.moves import zip -from salt.exceptions import CommandExecutionError -import salt.utils.files -import salt.utils.stringutils -# Import XML parser -import xml.etree.ElementTree as ET log = logging.getLogger(__name__) @@ -39,15 +42,13 @@ def _elements_equal(e1, e2): def _fail(ret, msg): - ret['comment'] = msg - ret['result'] = False + ret["comment"] = msg + ret["result"] = False return ret -def present(name, - config=None, - **kwargs): - ''' +def present(name, config=None, **kwargs): + """ Ensure the job is present in the Jenkins configured jobs name @@ -55,72 +56,72 @@ def present(name, config The Salt URL for the file to use for configuring the job - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ['Job {0} is up to date.'.format(name)]} + ret = { + "name": name, + "result": True, + "changes": {}, + "comment": ["Job {0} is up to date.".format(name)], + } - if __salt__['jenkins.job_exists'](name): - _current_job_config = __salt__['jenkins.get_job_config'](name) + if __salt__["jenkins.job_exists"](name): + _current_job_config = __salt__["jenkins.get_job_config"](name) buf = six.moves.StringIO(_current_job_config) oldXML = ET.fromstring(buf.read()) - cached_source_path = __salt__['cp.cache_file'](config, __env__) + cached_source_path = __salt__["cp.cache_file"](config, __env__) with salt.utils.files.fopen(cached_source_path) as _fp: newXML = ET.fromstring(salt.utils.stringutils.to_unicode(_fp.read())) if not _elements_equal(oldXML, newXML): diff = difflib.unified_diff( - ET.tostringlist(oldXML, encoding='utf8', method='xml'), - ET.tostringlist(newXML, encoding='utf8', method='xml'), lineterm='') + ET.tostringlist(oldXML, encoding="utf8", method="xml"), + ET.tostringlist(newXML, encoding="utf8", method="xml"), + lineterm="", + ) try: - __salt__['jenkins.update_job'](name, config, __env__) + __salt__["jenkins.update_job"](name, config, __env__) except CommandExecutionError as exc: return _fail(ret, exc.strerror) else: - ret['changes'] = ''.join(diff) - ret['comment'].append('Job \'{0}\' updated.'.format(name)) + ret["changes"] = "".join(diff) + ret["comment"].append("Job '{0}' updated.".format(name)) else: - cached_source_path = __salt__['cp.cache_file'](config, __env__) + cached_source_path = __salt__["cp.cache_file"](config, __env__) with salt.utils.files.fopen(cached_source_path) as _fp: new_config_xml = salt.utils.stringutils.to_unicode(_fp.read()) try: - __salt__['jenkins.create_job'](name, config, __env__) + __salt__["jenkins.create_job"](name, config, __env__) except CommandExecutionError as exc: return _fail(ret, exc.strerror) buf = six.moves.StringIO(new_config_xml) - diff = difflib.unified_diff('', buf.readlines(), lineterm='') - ret['changes'][name] = ''.join(diff) - ret['comment'].append('Job \'{0}\' added.'.format(name)) + diff = difflib.unified_diff("", buf.readlines(), lineterm="") + ret["changes"][name] = "".join(diff) + ret["comment"].append("Job '{0}' added.".format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret -def absent(name, - **kwargs): - ''' +def absent(name, **kwargs): + """ Ensure the job is absent from the Jenkins configured jobs name The name of the Jenkins job to remove - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": []} - if __salt__['jenkins.job_exists'](name): + if __salt__["jenkins.job_exists"](name): try: - __salt__['jenkins.delete_job'](name) + __salt__["jenkins.delete_job"](name) except CommandExecutionError as exc: return _fail(ret, exc.strerror) else: - ret['comment'] = 'Job \'{0}\' deleted.'.format(name) + ret["comment"] = "Job '{0}' deleted.".format(name) else: - ret['comment'] = 'Job \'{0}\' already absent.'.format(name) + ret["comment"] = "Job '{0}' already absent.".format(name) return ret diff --git a/salt/states/junos.py b/salt/states/junos.py index a8998ea2a0b..e9cb4928e59 100644 --- a/salt/states/junos.py +++ b/salt/states/junos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" State modules to interact with Junos devices. ============================================== @@ -12,9 +12,10 @@ State modules to interact with Junos devices. use the latest salt code from github until the next release. Refer to :mod:`junos <salt.proxy.junos>` for information on connecting to junos proxy. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging from functools import wraps @@ -25,15 +26,15 @@ def resultdecorator(function): @wraps(function) def wrapper(*args, **kwargs): ret = function(*args, **kwargs) - ret['result'] = ret['changes']['out'] + ret["result"] = ret["changes"]["out"] return ret return wrapper @resultdecorator -def rpc(name, dest=None, format='xml', args=None, **kwargs): - ''' +def rpc(name, dest=None, format="xml", args=None, **kwargs): + """ Executes the given rpc. The returned data can be stored in a file by specifying the destination path with dest as an argument @@ -69,23 +70,18 @@ def rpc(name, dest=None, format='xml', args=None, **kwargs): Amount of information you want. * interface_name: Name of the interface whose information you want. - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if args is not None: - ret['changes'] = __salt__['junos.rpc']( - name, - dest, - format, - *args, - **kwargs) + ret["changes"] = __salt__["junos.rpc"](name, dest, format, *args, **kwargs) else: - ret['changes'] = __salt__['junos.rpc'](name, dest, format, **kwargs) + ret["changes"] = __salt__["junos.rpc"](name, dest, format, **kwargs) return ret @resultdecorator def set_hostname(name, **kwargs): - ''' + """ Changes the hostname of the device. .. code-block:: yaml @@ -111,15 +107,15 @@ def set_hostname(name, **kwargs): If this option is specified, the commit will be rollbacked in \ the given time unless the commit is confirmed. - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.set_hostname'](name, **kwargs) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.set_hostname"](name, **kwargs) return ret @resultdecorator def commit(name, **kwargs): - ''' + """ Commits the changes loaded into the candidate configuration. .. code-block:: yaml @@ -155,15 +151,15 @@ def commit(name, **kwargs): the new configuration. * detail: When true return commit detail. - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.commit'](**kwargs) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.commit"](**kwargs) return ret @resultdecorator def rollback(name, id, **kwargs): - ''' + """ Rollbacks the committed changes. .. code-block:: yaml @@ -190,15 +186,15 @@ def rollback(name, id, **kwargs): * diffs_file: Path to the file where any diffs will be written. (default = None) - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.rollback'](id, **kwargs) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.rollback"](id, **kwargs) return ret @resultdecorator def diff(name, d_id): - ''' + """ Gets the difference between the candidate and the current configuration. .. code-block:: yaml @@ -212,15 +208,15 @@ def diff(name, d_id): Optional * id: The rollback id value [0-49]. (default = 0) - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.diff'](d_id) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.diff"](d_id) return ret @resultdecorator def cli(name, **kwargs): - ''' + """ Executes the CLI commands and reuturns the text output. .. code-block:: yaml @@ -245,15 +241,15 @@ def cli(name, **kwargs): * dest: The destination file where the CLI output can be stored.\ (default = None) - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.cli'](name, **kwargs) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.cli"](name, **kwargs) return ret @resultdecorator def shutdown(name, **kwargs): - ''' + """ Shuts down the device. .. code-block:: yaml @@ -272,15 +268,15 @@ def shutdown(name, **kwargs): Specify time for reboot. (To be used only if reboot=yes) * in_min: Specify delay in minutes for shutdown - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.shutdown'](**kwargs) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.shutdown"](**kwargs) return ret @resultdecorator def install_config(name, **kwargs): - ''' + """ Loads and commits the configuration provided. .. code-block:: yaml @@ -344,15 +340,15 @@ def install_config(name, **kwargs): The file will be stored on the proxy minion. To push the files to the master use :py:func:`cp.push <salt.modules.cp.push>`. - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.install_config'](name, **kwargs) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.install_config"](name, **kwargs) return ret @resultdecorator def zeroize(name): - ''' + """ Resets the device to default factory settings. .. code-block:: yaml @@ -361,15 +357,15 @@ def zeroize(name): junos.zeroize name: can be anything - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.zeroize']() + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.zeroize"]() return ret @resultdecorator def install_os(name, **kwargs): - ''' + """ Installs the given image on the device. After the installation is complete the device is rebooted, if reboot=True is given as a keyworded argument. @@ -397,15 +393,15 @@ def install_os(name, **kwargs): When True the software package will not be SCP’d to the device. \ (default = False) - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.install_os'](name, **kwargs) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.install_os"](name, **kwargs) return ret @resultdecorator def file_copy(name, dest=None, **kwargs): - ''' + """ Copies the file from the local device to the junos device. .. code-block:: yaml @@ -421,15 +417,15 @@ def file_copy(name, dest=None, **kwargs): The sorce path where the file is kept. * dest: The destination path where the file will be copied. - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.file_copy'](name, dest, **kwargs) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.file_copy"](name, dest, **kwargs) return ret @resultdecorator def lock(name): - ''' + """ Attempts an exclusive lock on the candidate configuration. This is a non-blocking call. @@ -443,15 +439,15 @@ def lock(name): lock the config: junos.lock - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.lock']() + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.lock"]() return ret @resultdecorator def unlock(name): - ''' + """ Unlocks the candidate configuration. .. code-block:: yaml @@ -459,15 +455,15 @@ def unlock(name): unlock the config: junos.unlock - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.unlock']() + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.unlock"]() return ret @resultdecorator def load(name, **kwargs): - ''' + """ Loads the configuration provided onto the junos device. .. code-block:: yaml @@ -521,15 +517,15 @@ def load(name, **kwargs): You may reference these variables in your template like so: {{ template_vars["var_name"] }} - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.load'](name, **kwargs) + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.load"](name, **kwargs) return ret @resultdecorator def commit_check(name): - ''' + """ Perform a commit check on the configuration. @@ -538,7 +534,7 @@ def commit_check(name): perform commit check: junos.commit_check - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} - ret['changes'] = __salt__['junos.commit_check']() + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + ret["changes"] = __salt__["junos.commit_check"]() return ret diff --git a/salt/states/kapacitor.py b/salt/states/kapacitor.py index cbaf6cdbda1..e5577f112b6 100644 --- a/salt/states/kapacitor.py +++ b/salt/states/kapacitor.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Kapacitor state module. :configuration: This module accepts connection configuration details either as @@ -15,10 +15,11 @@ Kapacitor state module. overwrite options passed into pillar. .. versionadded:: 2016.11.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import difflib import salt.utils.files @@ -26,17 +27,21 @@ import salt.utils.stringutils def __virtual__(): - return 'kapacitor' if 'kapacitor.version' in __salt__ else False + if "kapacitor.version" in __salt__: + return "kapacitor" + return (False, "kapacitor module could not be loaded") -def task_present(name, - tick_script, - task_type='stream', - database=None, - retention_policy='default', - enable=True, - dbrps=None): - ''' +def task_present( + name, + tick_script, + task_type="stream", + database=None, + retention_policy="default", + enable=True, + dbrps=None, +): + """ Ensure that a task is present and up-to-date in Kapacitor. name @@ -64,139 +69,142 @@ def task_present(name, enable Whether to enable the task or not. Defaults to True. - ''' + """ comments = [] changes = [] - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - task = __salt__['kapacitor.get_task'](name) - old_script = task['script'] if task else '' + task = __salt__["kapacitor.get_task"](name) + old_script = task["script"] if task else "" if not dbrps: dbrps = [] if database and retention_policy: - dbrp = '{0}.{1}'.format(database, retention_policy) + dbrp = "{0}.{1}".format(database, retention_policy) dbrps.append(dbrp) - task_dbrps = [{'db': dbrp[0], 'rp': dbrp[1]} for dbrp in (dbrp.split('.') for dbrp in dbrps)] + task_dbrps = [ + {"db": dbrp[0], "rp": dbrp[1]} for dbrp in (dbrp.split(".") for dbrp in dbrps) + ] - if tick_script.startswith('salt://'): - script_path = __salt__['cp.cache_file'](tick_script, __env__) + if tick_script.startswith("salt://"): + script_path = __salt__["cp.cache_file"](tick_script, __env__) else: script_path = tick_script - with salt.utils.files.fopen(script_path, 'r') as file: - new_script = salt.utils.stringutils.to_unicode(file.read()).replace('\t', ' ') + with salt.utils.files.fopen(script_path, "r") as file: + new_script = salt.utils.stringutils.to_unicode(file.read()).replace( + "\t", " " + ) is_up_to_date = task and ( - old_script == new_script and - task_type == task['type'] and - task['dbrps'] == task_dbrps + old_script == new_script + and task_type == task["type"] + and task["dbrps"] == task_dbrps ) if is_up_to_date: - comments.append('Task script is already up-to-date') + comments.append("Task script is already up-to-date") else: - if __opts__['test']: - ret['result'] = None - comments.append('Task would have been updated') + if __opts__["test"]: + ret["result"] = None + comments.append("Task would have been updated") else: - result = __salt__['kapacitor.define_task']( + result = __salt__["kapacitor.define_task"]( name, script_path, task_type=task_type, database=database, retention_policy=retention_policy, - dbrps=dbrps + dbrps=dbrps, ) - ret['result'] = result['success'] - if not ret['result']: - comments.append('Could not define task') - if result.get('stderr'): - comments.append(result['stderr']) - ret['comment'] = '\n'.join(comments) + ret["result"] = result["success"] + if not ret["result"]: + comments.append("Could not define task") + if result.get("stderr"): + comments.append(result["stderr"]) + ret["comment"] = "\n".join(comments) return ret if old_script != new_script: - ret['changes']['TICKscript diff'] = '\n'.join(difflib.unified_diff( - old_script.splitlines(), - new_script.splitlines(), - )) - comments.append('Task script updated') + ret["changes"]["TICKscript diff"] = "\n".join( + difflib.unified_diff(old_script.splitlines(), new_script.splitlines(),) + ) + comments.append("Task script updated") - if not task or task['type'] != task_type: - ret['changes']['type'] = task_type - comments.append('Task type updated') + if not task or task["type"] != task_type: + ret["changes"]["type"] = task_type + comments.append("Task type updated") - if not task or task['dbrps'] != task_dbrps: - ret['changes']['dbrps'] = task_dbrps - comments.append('Task dbrps updated') + if not task or task["dbrps"] != task_dbrps: + ret["changes"]["dbrps"] = task_dbrps + comments.append("Task dbrps updated") if enable: - if task and task['enabled']: - comments.append('Task is already enabled') + if task and task["enabled"]: + comments.append("Task is already enabled") else: - if __opts__['test']: - ret['result'] = None - comments.append('Task would have been enabled') + if __opts__["test"]: + ret["result"] = None + comments.append("Task would have been enabled") else: - result = __salt__['kapacitor.enable_task'](name) - ret['result'] = result['success'] - if not ret['result']: - comments.append('Could not enable task') - if result.get('stderr'): - comments.append(result['stderr']) - ret['comment'] = '\n'.join(comments) + result = __salt__["kapacitor.enable_task"](name) + ret["result"] = result["success"] + if not ret["result"]: + comments.append("Could not enable task") + if result.get("stderr"): + comments.append(result["stderr"]) + ret["comment"] = "\n".join(comments) return ret - comments.append('Task was enabled') - ret['changes']['enabled'] = {'old': False, 'new': True} + comments.append("Task was enabled") + ret["changes"]["enabled"] = {"old": False, "new": True} else: - if task and not task['enabled']: - comments.append('Task is already disabled') + if task and not task["enabled"]: + comments.append("Task is already disabled") else: - if __opts__['test']: - ret['result'] = None - comments.append('Task would have been disabled') + if __opts__["test"]: + ret["result"] = None + comments.append("Task would have been disabled") else: - result = __salt__['kapacitor.disable_task'](name) - ret['result'] = result['success'] - if not ret['result']: - comments.append('Could not disable task') - if result.get('stderr'): - comments.append(result['stderr']) - ret['comment'] = '\n'.join(comments) + result = __salt__["kapacitor.disable_task"](name) + ret["result"] = result["success"] + if not ret["result"]: + comments.append("Could not disable task") + if result.get("stderr"): + comments.append(result["stderr"]) + ret["comment"] = "\n".join(comments) return ret - comments.append('Task was disabled') - ret['changes']['enabled'] = {'old': True, 'new': False} + comments.append("Task was disabled") + ret["changes"]["enabled"] = {"old": True, "new": False} - ret['comment'] = '\n'.join(comments) + ret["comment"] = "\n".join(comments) return ret def task_absent(name): - ''' + """ Ensure that a task is absent from Kapacitor. name Name of the task. - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - task = __salt__['kapacitor.get_task'](name) + task = __salt__["kapacitor.get_task"](name) if task: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Task would have been deleted' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Task would have been deleted" else: - result = __salt__['kapacitor.delete_task'](name) - ret['result'] = result['success'] - if not ret['result']: - ret['comment'] = 'Could not disable task' - if result.get('stderr'): - ret['comment'] += '\n' + result['stderr'] + result = __salt__["kapacitor.delete_task"](name) + ret["result"] = result["success"] + if not ret["result"]: + ret["comment"] = "Could not disable task" + if result.get("stderr"): + ret["comment"] += "\n" + result["stderr"] return ret - ret['comment'] = 'Task was deleted' - ret['changes'][name] = 'deleted' + ret["comment"] = "Task was deleted" + ret["changes"][name] = "deleted" else: - ret['comment'] = 'Task does not exist' + ret["comment"] = "Task does not exist" return ret diff --git a/salt/states/kernelpkg.py b/salt/states/kernelpkg.py index 7ed558cd388..aab8ffeb2a3 100644 --- a/salt/states/kernelpkg.py +++ b/salt/states/kernelpkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage kernel packages and active kernel version ========================================================================= @@ -43,23 +43,26 @@ Chaining can also be achieved using wait/listen requisites: - at_time: 1 - listen: - kernel: install-latest-kernel -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only make these states available if a kernelpkg provider has been detected or assigned for this minion - ''' - return 'kernelpkg.upgrade' in __salt__ + """ + if "kernelpkg.upgrade" in __salt__: + return True + return (False, "kernelpkg module could not be loaded") def latest_installed(name, **kwargs): # pylint: disable=unused-argument - ''' + """ Ensure that the latest version of the kernel available in the repositories is installed. @@ -73,37 +76,40 @@ def latest_installed(name, **kwargs): # pylint: disable=unused-argument name Arbitrary name for the state. Does not affect behavior. - ''' - installed = __salt__['kernelpkg.list_installed']() - upgrade = __salt__['kernelpkg.latest_available']() - ret = {'name': name} + """ + installed = __salt__["kernelpkg.list_installed"]() + upgrade = __salt__["kernelpkg.latest_available"]() + ret = {"name": name} if upgrade in installed: - ret['result'] = True - ret['comment'] = ('The latest kernel package is already installed: ' - '{0}').format(upgrade) - ret['changes'] = {} + ret["result"] = True + ret["comment"] = ( + "The latest kernel package is already installed: " "{0}" + ).format(upgrade) + ret["changes"] = {} else: - if __opts__['test']: - ret['result'] = None - ret['changes'] = {} - ret['comment'] = ('The latest kernel package will be installed: ' - '{0}').format(upgrade) + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {} + ret["comment"] = ( + "The latest kernel package will be installed: " "{0}" + ).format(upgrade) else: - result = __salt__['kernelpkg.upgrade']() - ret['result'] = True - ret['changes'] = result['upgrades'] - ret['comment'] = ('The latest kernel package has been installed, ' - 'but not activated.') + result = __salt__["kernelpkg.upgrade"]() + ret["result"] = True + ret["changes"] = result["upgrades"] + ret["comment"] = ( + "The latest kernel package has been installed, " "but not activated." + ) return ret def latest_active(name, at_time=None, **kwargs): # pylint: disable=unused-argument - ''' + """ Initiate a reboot if the running kernel is not the latest one installed. .. note:: @@ -132,42 +138,38 @@ def latest_active(name, at_time=None, **kwargs): # pylint: disable=unused-argum at_time The wait time in minutes before the system will be rebooted. - ''' - active = __salt__['kernelpkg.active']() - latest = __salt__['kernelpkg.latest_installed']() - ret = {'name': name} + """ + active = __salt__["kernelpkg.active"]() + latest = __salt__["kernelpkg.latest_installed"]() + ret = {"name": name} - if __salt__['kernelpkg.needs_reboot'](): + if __salt__["kernelpkg.needs_reboot"](): - ret['comment'] = ('The system will be booted to activate ' - 'kernel: {0}').format(latest) + ret["comment"] = ( + "The system will be booted to activate " "kernel: {0}" + ).format(latest) - if __opts__['test']: - ret['result'] = None - ret['changes'] = {'kernel': { - 'old': active, - 'new': latest - }} + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {"kernel": {"old": active, "new": latest}} else: - __salt__['system.reboot'](at_time=at_time) - ret['result'] = True - ret['changes'] = {'kernel': { - 'old': active, - 'new': latest - }} + __salt__["system.reboot"](at_time=at_time) + ret["result"] = True + ret["changes"] = {"kernel": {"old": active, "new": latest}} else: - ret['result'] = True - ret['comment'] = ('The latest installed kernel package ' - 'is active: {0}').format(active) - ret['changes'] = {} + ret["result"] = True + ret["comment"] = ( + "The latest installed kernel package " "is active: {0}" + ).format(active) + ret["changes"] = {} return ret def latest_wait(name, at_time=None, **kwargs): # pylint: disable=unused-argument - ''' + """ Initiate a reboot if the running kernel is not the latest one installed. This is the waitable version of :py:func:`~salt.states.kernelpkg.latest_active` and will not take any action unless triggered by a watch or listen requesite. @@ -194,21 +196,21 @@ def latest_wait(name, at_time=None, **kwargs): # pylint: disable=unused-argumen at_time The wait time in minutes before the system will be rebooted. - ''' - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + return {"name": name, "changes": {}, "result": True, "comment": ""} def mod_watch(name, sfun, **kwargs): - ''' + """ Execute a kernelpkg state based on a watch or listen call - ''' - if sfun in ('latest_active', 'latest_wait'): + """ + if sfun in ("latest_active", "latest_wait"): return latest_active(name, **kwargs) else: - return {'name': name, 'changes': {}, - 'comment': 'kernelpkg.{0} does not work with the watch ' - 'requisite.'.format(sfun), - 'result': False} + return { + "name": name, + "changes": {}, + "comment": "kernelpkg.{0} does not work with the watch " + "requisite.".format(sfun), + "result": False, + } diff --git a/salt/states/keyboard.py b/salt/states/keyboard.py index 8ea9ad823be..5f1433047b0 100644 --- a/salt/states/keyboard.py +++ b/salt/states/keyboard.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of keyboard layouts ============================== @@ -16,72 +16,68 @@ Or it can be managed for XOrg: us: keyboard.xorg -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the keyboard module is available in __salt__ - ''' - return 'keyboard.get_sys' in __salt__ + """ + if "keyboard.get_sys" in __salt__: + return True + return (False, "keyboard module could not be loaded") def system(name): - ''' + """ Set the keyboard layout for the system name The keyboard layout to use - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} - if __salt__['keyboard.get_sys']() == name: - ret['result'] = True - ret['comment'] = 'System layout {0} already set'.format(name) + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + if __salt__["keyboard.get_sys"]() == name: + ret["result"] = True + ret["comment"] = "System layout {0} already set".format(name) return ret - if __opts__['test']: - ret['comment'] = 'System layout {0} needs to be set'.format(name) + if __opts__["test"]: + ret["comment"] = "System layout {0} needs to be set".format(name) return ret - if __salt__['keyboard.set_sys'](name): - ret['changes'] = {'layout': name} - ret['result'] = True - ret['comment'] = 'Set system keyboard layout {0}'.format(name) + if __salt__["keyboard.set_sys"](name): + ret["changes"] = {"layout": name} + ret["result"] = True + ret["comment"] = "Set system keyboard layout {0}".format(name) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to set system keyboard layout' + ret["result"] = False + ret["comment"] = "Failed to set system keyboard layout" return ret def xorg(name): - ''' + """ Set the keyboard layout for XOrg layout The keyboard layout to use - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} - if __salt__['keyboard.get_x']() == name: - ret['result'] = True - ret['comment'] = 'XOrg layout {0} already set'.format(name) + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + if __salt__["keyboard.get_x"]() == name: + ret["result"] = True + ret["comment"] = "XOrg layout {0} already set".format(name) return ret - if __opts__['test']: - ret['comment'] = 'XOrg layout {0} needs to be set'.format(name) + if __opts__["test"]: + ret["comment"] = "XOrg layout {0} needs to be set".format(name) return ret - if __salt__['keyboard.set_x'](name): - ret['changes'] = {'layout': name} - ret['result'] = True - ret['comment'] = 'Set XOrg keyboard layout {0}'.format(name) + if __salt__["keyboard.set_x"](name): + ret["changes"] = {"layout": name} + ret["result"] = True + ret["comment"] = "Set XOrg keyboard layout {0}".format(name) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to set XOrg keyboard layout' + ret["result"] = False + ret["comment"] = "Failed to set XOrg keyboard layout" return ret diff --git a/salt/states/keystone.py b/salt/states/keystone.py index 2a678cefeef..47cf7b33c5e 100644 --- a/salt/states/keystone.py +++ b/salt/states/keystone.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Keystone users ============================ @@ -65,47 +65,58 @@ Management of Keystone users - service_type: compute - description: OpenStack Compute Service -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the keystone module is in __salt__ - ''' - return 'keystone' if 'keystone.auth' in __salt__ else False + """ + if "keystone.auth" in __salt__: + return "keystone" + return (False, "keystone module could not be loaded") _OS_IDENTITY_API_VERSION = 2 -_TENANT_ID = 'tenant_id' +_TENANT_ID = "tenant_id" def _api_version(profile=None, **connection_args): - ''' + """ Sets global variables _OS_IDENTITY_API_VERSION and _TENANT_ID depending on API version. - ''' + """ global _TENANT_ID global _OS_IDENTITY_API_VERSION try: - if float(__salt__['keystone.api_version'](profile=profile, **connection_args).strip('v')) >= 3: - _TENANT_ID = 'project_id' + if ( + float( + __salt__["keystone.api_version"]( + profile=profile, **connection_args + ).strip("v") + ) + >= 3 + ): + _TENANT_ID = "project_id" _OS_IDENTITY_API_VERSION = 3 except KeyError: pass -def user_present(name, - password, - email, - tenant=None, - enabled=True, - roles=None, - profile=None, - password_reset=True, - project=None, - **connection_args): - ''' +def user_present( + name, + password, + email, + tenant=None, + enabled=True, + roles=None, + profile=None, + password_reset=True, + project=None, + **connection_args +): + """ Ensure that the keystone user is present with the specified properties. name @@ -148,11 +159,13 @@ def user_present(name, service: - admin - Member - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User "{0}" will be updated'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'User "{0}" will be updated'.format(name), + } _api_version(profile=profile, **connection_args) @@ -161,175 +174,206 @@ def user_present(name, # Validate tenant if set if tenant is not None: - tenantdata = __salt__['keystone.tenant_get'](name=tenant, - profile=profile, - **connection_args) - if 'Error' in tenantdata: - ret['result'] = False - ret['comment'] = 'Tenant / project "{0}" does not exist'.format(tenant) + tenantdata = __salt__["keystone.tenant_get"]( + name=tenant, profile=profile, **connection_args + ) + if "Error" in tenantdata: + ret["result"] = False + ret["comment"] = 'Tenant / project "{0}" does not exist'.format(tenant) return ret - tenant_id = tenantdata[tenant]['id'] + tenant_id = tenantdata[tenant]["id"] else: tenant_id = None # Check if user is already present - user = __salt__['keystone.user_get'](name=name, profile=profile, - **connection_args) - if 'Error' not in user: + user = __salt__["keystone.user_get"](name=name, profile=profile, **connection_args) + if "Error" not in user: change_email = False change_enabled = False change_tenant = False change_password = False - if user[name].get('email', None) != email: + if user[name].get("email", None) != email: change_email = True - if user[name].get('enabled', None) != enabled: + if user[name].get("enabled", None) != enabled: change_enabled = True - if tenant and (_TENANT_ID not in user[name] or - user[name].get(_TENANT_ID, None) != tenant_id): + if tenant and ( + _TENANT_ID not in user[name] + or user[name].get(_TENANT_ID, None) != tenant_id + ): change_tenant = True - if (password_reset is True and - not __salt__['keystone.user_verify_password'](name=name, - password=password, - profile=profile, - **connection_args)): + if password_reset is True and not __salt__["keystone.user_verify_password"]( + name=name, password=password, profile=profile, **connection_args + ): change_password = True - if __opts__.get('test') and (change_email or change_enabled or change_tenant or change_password): - ret['result'] = None - ret['comment'] = 'User "{0}" will be updated'.format(name) + if __opts__.get("test") and ( + change_email or change_enabled or change_tenant or change_password + ): + ret["result"] = None + ret["comment"] = 'User "{0}" will be updated'.format(name) if change_email is True: - ret['changes']['Email'] = 'Will be updated' + ret["changes"]["Email"] = "Will be updated" if change_enabled is True: - ret['changes']['Enabled'] = 'Will be True' + ret["changes"]["Enabled"] = "Will be True" if change_tenant is True: - ret['changes']['Tenant'] = 'Will be added to "{0}" tenant'.format(tenant) + ret["changes"]["Tenant"] = 'Will be added to "{0}" tenant'.format( + tenant + ) if change_password is True: - ret['changes']['Password'] = 'Will be updated' + ret["changes"]["Password"] = "Will be updated" return ret - ret['comment'] = 'User "{0}" is already present'.format(name) + ret["comment"] = 'User "{0}" is already present'.format(name) if change_email: - __salt__['keystone.user_update'](name=name, email=email, profile=profile, **connection_args) - ret['comment'] = 'User "{0}" has been updated'.format(name) - ret['changes']['Email'] = 'Updated' + __salt__["keystone.user_update"]( + name=name, email=email, profile=profile, **connection_args + ) + ret["comment"] = 'User "{0}" has been updated'.format(name) + ret["changes"]["Email"] = "Updated" if change_enabled: - __salt__['keystone.user_update'](name=name, enabled=enabled, profile=profile, **connection_args) - ret['comment'] = 'User "{0}" has been updated'.format(name) - ret['changes']['Enabled'] = 'Now {0}'.format(enabled) + __salt__["keystone.user_update"]( + name=name, enabled=enabled, profile=profile, **connection_args + ) + ret["comment"] = 'User "{0}" has been updated'.format(name) + ret["changes"]["Enabled"] = "Now {0}".format(enabled) if change_tenant: - __salt__['keystone.user_update'](name=name, tenant=tenant, profile=profile, **connection_args) - ret['comment'] = 'User "{0}" has been updated'.format(name) - ret['changes']['Tenant'] = 'Added to "{0}" tenant'.format(tenant) + __salt__["keystone.user_update"]( + name=name, tenant=tenant, profile=profile, **connection_args + ) + ret["comment"] = 'User "{0}" has been updated'.format(name) + ret["changes"]["Tenant"] = 'Added to "{0}" tenant'.format(tenant) if change_password: - __salt__['keystone.user_password_update'](name=name, password=password, profile=profile, - **connection_args) - ret['comment'] = 'User "{0}" has been updated'.format(name) - ret['changes']['Password'] = 'Updated' + __salt__["keystone.user_password_update"]( + name=name, password=password, profile=profile, **connection_args + ) + ret["comment"] = 'User "{0}" has been updated'.format(name) + ret["changes"]["Password"] = "Updated" if roles: for tenant in roles: - args = dict({'user_name': name, 'tenant_name': - tenant, 'profile': profile}, **connection_args) - tenant_roles = __salt__['keystone.user_role_list'](**args) + args = dict( + {"user_name": name, "tenant_name": tenant, "profile": profile}, + **connection_args + ) + tenant_roles = __salt__["keystone.user_role_list"](**args) for role in roles[tenant]: if role not in tenant_roles: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'User roles "{0}" will been updated'.format(name) + if __opts__.get("test"): + ret["result"] = None + ret[ + "comment" + ] = 'User roles "{0}" will been updated'.format(name) return ret - addargs = dict({'user': name, 'role': role, - 'tenant': tenant, - 'profile': profile}, - **connection_args) - newrole = __salt__['keystone.user_role_add'](**addargs) - if 'roles' in ret['changes']: - ret['changes']['roles'].append(newrole) + addargs = dict( + { + "user": name, + "role": role, + "tenant": tenant, + "profile": profile, + }, + **connection_args + ) + newrole = __salt__["keystone.user_role_add"](**addargs) + if "roles" in ret["changes"]: + ret["changes"]["roles"].append(newrole) else: - ret['changes']['roles'] = [newrole] + ret["changes"]["roles"] = [newrole] roles_to_remove = list(set(tenant_roles) - set(roles[tenant])) for role in roles_to_remove: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'User roles "{0}" will been updated'.format(name) + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'User roles "{0}" will been updated'.format( + name + ) return ret - addargs = dict({'user': name, 'role': role, - 'tenant': tenant, - 'profile': profile}, - **connection_args) - oldrole = __salt__['keystone.user_role_remove'](**addargs) - if 'roles' in ret['changes']: - ret['changes']['roles'].append(oldrole) + addargs = dict( + { + "user": name, + "role": role, + "tenant": tenant, + "profile": profile, + }, + **connection_args + ) + oldrole = __salt__["keystone.user_role_remove"](**addargs) + if "roles" in ret["changes"]: + ret["changes"]["roles"].append(oldrole) else: - ret['changes']['roles'] = [oldrole] + ret["changes"]["roles"] = [oldrole] else: # Create that user! - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Keystone user "{0}" will be added'.format(name) - ret['changes']['User'] = 'Will be created' + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Keystone user "{0}" will be added'.format(name) + ret["changes"]["User"] = "Will be created" return ret - __salt__['keystone.user_create'](name=name, - password=password, - email=email, - tenant_id=tenant_id, - enabled=enabled, - profile=profile, - **connection_args) + __salt__["keystone.user_create"]( + name=name, + password=password, + email=email, + tenant_id=tenant_id, + enabled=enabled, + profile=profile, + **connection_args + ) if roles: for tenant in roles: for role in roles[tenant]: - __salt__['keystone.user_role_add'](user=name, - role=role, - tenant=tenant, - profile=profile, - **connection_args) - ret['comment'] = 'Keystone user {0} has been added'.format(name) - ret['changes']['User'] = 'Created' + __salt__["keystone.user_role_add"]( + user=name, + role=role, + tenant=tenant, + profile=profile, + **connection_args + ) + ret["comment"] = "Keystone user {0} has been added".format(name) + ret["changes"]["User"] = "Created" return ret def user_absent(name, profile=None, **connection_args): - ''' + """ Ensure that the keystone user is absent. name The name of the user that should not exist - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User "{0}" is already absent'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'User "{0}" is already absent'.format(name), + } # Check if user is present - user = __salt__['keystone.user_get'](name=name, profile=profile, - **connection_args) - if 'Error' not in user: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'User "{0}" will be deleted'.format(name) + user = __salt__["keystone.user_get"](name=name, profile=profile, **connection_args) + if "Error" not in user: + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'User "{0}" will be deleted'.format(name) return ret # Delete that user! - __salt__['keystone.user_delete'](name=name, profile=profile, - **connection_args) - ret['comment'] = 'User "{0}" has been deleted'.format(name) - ret['changes']['User'] = 'Deleted' + __salt__["keystone.user_delete"](name=name, profile=profile, **connection_args) + ret["comment"] = 'User "{0}" has been deleted'.format(name) + ret["changes"]["User"] = "Deleted" return ret -def tenant_present(name, description=None, enabled=True, profile=None, - **connection_args): - ''' +def tenant_present( + name, description=None, enabled=True, profile=None, **connection_args +): + """ Ensures that the keystone tenant exists name @@ -340,98 +384,119 @@ def tenant_present(name, description=None, enabled=True, profile=None, enabled Availability state for this tenant - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Tenant / project "{0}" already exists'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Tenant / project "{0}" already exists'.format(name), + } _api_version(profile=profile, **connection_args) # Check if tenant is already present - tenant = __salt__['keystone.tenant_get'](name=name, - profile=profile, - **connection_args) + tenant = __salt__["keystone.tenant_get"]( + name=name, profile=profile, **connection_args + ) - if 'Error' not in tenant: - if tenant[name].get('description', None) != description: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Tenant / project "{0}" will be updated'.format(name) - ret['changes']['Description'] = 'Will be updated' + if "Error" not in tenant: + if tenant[name].get("description", None) != description: + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Tenant / project "{0}" will be updated'.format(name) + ret["changes"]["Description"] = "Will be updated" return ret - __salt__['keystone.tenant_update'](name=name, - description=description, - enabled=enabled, - profile=profile, - **connection_args) - ret['comment'] = 'Tenant / project "{0}" has been updated'.format(name) - ret['changes']['Description'] = 'Updated' - if tenant[name].get('enabled', None) != enabled: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Tenant / project "{0}" will be updated'.format(name) - ret['changes']['Enabled'] = 'Will be {0}'.format(enabled) + __salt__["keystone.tenant_update"]( + name=name, + description=description, + enabled=enabled, + profile=profile, + **connection_args + ) + ret["comment"] = 'Tenant / project "{0}" has been updated'.format(name) + ret["changes"]["Description"] = "Updated" + if tenant[name].get("enabled", None) != enabled: + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Tenant / project "{0}" will be updated'.format(name) + ret["changes"]["Enabled"] = "Will be {0}".format(enabled) return ret - __salt__['keystone.tenant_update'](name=name, - description=description, - enabled=enabled, - profile=profile, - **connection_args) - ret['comment'] = 'Tenant / project "{0}" has been updated'.format(name) - ret['changes']['Enabled'] = 'Now {0}'.format(enabled) + __salt__["keystone.tenant_update"]( + name=name, + description=description, + enabled=enabled, + profile=profile, + **connection_args + ) + ret["comment"] = 'Tenant / project "{0}" has been updated'.format(name) + ret["changes"]["Enabled"] = "Now {0}".format(enabled) else: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Tenant / project "{0}" will be added'.format(name) - ret['changes']['Tenant'] = 'Will be created' + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Tenant / project "{0}" will be added'.format(name) + ret["changes"]["Tenant"] = "Will be created" return ret # Create tenant if _OS_IDENTITY_API_VERSION > 2: - created = __salt__['keystone.project_create'](name=name, domain='default', description=description, - enabled=enabled, profile=profile, **connection_args) + created = __salt__["keystone.project_create"]( + name=name, + domain="default", + description=description, + enabled=enabled, + profile=profile, + **connection_args + ) else: - created = __salt__['keystone.tenant_create'](name=name, description=description, enabled=enabled, - profile=profile, **connection_args) - ret['changes']['Tenant'] = 'Created' if created is True else 'Failed' - ret['result'] = created - ret['comment'] = 'Tenant / project "{0}" has been added'.format(name) + created = __salt__["keystone.tenant_create"]( + name=name, + description=description, + enabled=enabled, + profile=profile, + **connection_args + ) + ret["changes"]["Tenant"] = "Created" if created is True else "Failed" + ret["result"] = created + ret["comment"] = 'Tenant / project "{0}" has been added'.format(name) return ret def tenant_absent(name, profile=None, **connection_args): - ''' + """ Ensure that the keystone tenant is absent. name The name of the tenant that should not exist - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Tenant / project "{0}" is already absent'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Tenant / project "{0}" is already absent'.format(name), + } # Check if tenant is present - tenant = __salt__['keystone.tenant_get'](name=name, - profile=profile, - **connection_args) - if 'Error' not in tenant: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Tenant / project "{0}" will be deleted'.format(name) + tenant = __salt__["keystone.tenant_get"]( + name=name, profile=profile, **connection_args + ) + if "Error" not in tenant: + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Tenant / project "{0}" will be deleted'.format(name) return ret # Delete tenant - __salt__['keystone.tenant_delete'](name=name, profile=profile, - **connection_args) - ret['comment'] = 'Tenant / project "{0}" has been deleted'.format(name) - ret['changes']['Tenant/Project'] = 'Deleted' + __salt__["keystone.tenant_delete"]( + name=name, profile=profile, **connection_args + ) + ret["comment"] = 'Tenant / project "{0}" has been deleted'.format(name) + ret["changes"]["Tenant/Project"] = "Deleted" return ret -def project_present(name, description=None, enabled=True, profile=None, - **connection_args): - ''' +def project_present( + name, description=None, enabled=True, profile=None, **connection_args +): + """ Ensures that the keystone project exists Alias for tenant_present from V2 API to fulfill V3 API naming convention. @@ -454,14 +519,19 @@ def project_present(name, description=None, enabled=True, profile=None, - enabled: True - description: 'Nova Compute Service' - ''' + """ - return tenant_present(name, description=description, enabled=enabled, profile=profile, - **connection_args) + return tenant_present( + name, + description=description, + enabled=enabled, + profile=profile, + **connection_args + ) def project_absent(name, profile=None, **connection_args): - ''' + """ Ensure that the keystone project is absent. Alias for tenant_absent from V2 API to fulfill V3 API naming convention. @@ -476,74 +546,75 @@ def project_absent(name, profile=None, **connection_args): delete_nova: keystone.project_absent: - name: nova - ''' + """ return tenant_absent(name, profile=profile, **connection_args) def role_present(name, profile=None, **connection_args): - '''' + """' Ensures that the keystone role exists name The name of the role that should be present - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Role "{0}" already exists'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Role "{0}" already exists'.format(name), + } # Check if role is already present - role = __salt__['keystone.role_get'](name=name, profile=profile, - **connection_args) + role = __salt__["keystone.role_get"](name=name, profile=profile, **connection_args) - if 'Error' not in role: + if "Error" not in role: return ret else: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Role "{0}" will be added'.format(name) + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Role "{0}" will be added'.format(name) return ret # Create role - __salt__['keystone.role_create'](name, profile=profile, - **connection_args) - ret['comment'] = 'Role "{0}" has been added'.format(name) - ret['changes']['Role'] = 'Created' + __salt__["keystone.role_create"](name, profile=profile, **connection_args) + ret["comment"] = 'Role "{0}" has been added'.format(name) + ret["changes"]["Role"] = "Created" return ret def role_absent(name, profile=None, **connection_args): - ''' + """ Ensure that the keystone role is absent. name The name of the role that should not exist - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Role "{0}" is already absent'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Role "{0}" is already absent'.format(name), + } # Check if role is present - role = __salt__['keystone.role_get'](name=name, profile=profile, - **connection_args) - if 'Error' not in role: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Role "{0}" will be deleted'.format(name) + role = __salt__["keystone.role_get"](name=name, profile=profile, **connection_args) + if "Error" not in role: + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Role "{0}" will be deleted'.format(name) return ret # Delete role - __salt__['keystone.role_delete'](name=name, profile=profile, - **connection_args) - ret['comment'] = 'Role "{0}" has been deleted'.format(name) - ret['changes']['Role'] = 'Deleted' + __salt__["keystone.role_delete"](name=name, profile=profile, **connection_args) + ret["comment"] = 'Role "{0}" has been deleted'.format(name) + ret["changes"]["Role"] = "Deleted" return ret -def service_present(name, service_type, description=None, - profile=None, **connection_args): - ''' +def service_present( + name, service_type, description=None, profile=None, **connection_args +): + """ Ensure service present in Keystone catalog name @@ -554,75 +625,81 @@ def service_present(name, service_type, description=None, description (optional) Description of the service - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Service "{0}" already exists'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Service "{0}" already exists'.format(name), + } # Check if service is already present - role = __salt__['keystone.service_get'](name=name, - profile=profile, - **connection_args) + role = __salt__["keystone.service_get"]( + name=name, profile=profile, **connection_args + ) - if 'Error' not in role: + if "Error" not in role: return ret else: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Service "{0}" will be added'.format(name) + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Service "{0}" will be added'.format(name) return ret # Create service - __salt__['keystone.service_create'](name, service_type, - description, - profile=profile, - **connection_args) - ret['comment'] = 'Service "{0}" has been added'.format(name) - ret['changes']['Service'] = 'Created' + __salt__["keystone.service_create"]( + name, service_type, description, profile=profile, **connection_args + ) + ret["comment"] = 'Service "{0}" has been added'.format(name) + ret["changes"]["Service"] = "Created" return ret def service_absent(name, profile=None, **connection_args): - ''' + """ Ensure that the service doesn't exist in Keystone catalog name The name of the service that should not exist - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Service "{0}" is already absent'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Service "{0}" is already absent'.format(name), + } # Check if service is present - role = __salt__['keystone.service_get'](name=name, - profile=profile, - **connection_args) - if 'Error' not in role: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Service "{0}" will be deleted'.format(name) + role = __salt__["keystone.service_get"]( + name=name, profile=profile, **connection_args + ) + if "Error" not in role: + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Service "{0}" will be deleted'.format(name) return ret # Delete service - __salt__['keystone.service_delete'](name=name, - profile=profile, - **connection_args) - ret['comment'] = 'Service "{0}" has been deleted'.format(name) - ret['changes']['Service'] = 'Deleted' + __salt__["keystone.service_delete"]( + name=name, profile=profile, **connection_args + ) + ret["comment"] = 'Service "{0}" has been deleted'.format(name) + ret["changes"]["Service"] = "Deleted" return ret -def endpoint_present(name, - publicurl=None, - internalurl=None, - adminurl=None, - region=None, - profile=None, - url=None, - interface=None, **connection_args): - ''' +def endpoint_present( + name, + publicurl=None, + internalurl=None, + adminurl=None, + region=None, + profile=None, + url=None, + interface=None, + **connection_args +): + """ Ensure the specified endpoints exists for service name @@ -647,132 +724,149 @@ def endpoint_present(name, The interface type, which describes the visibility of the endpoint. (for V3 API) - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} _api_version(profile=profile, **connection_args) - endpoint = __salt__['keystone.endpoint_get'](name, region, - profile=profile, - interface=interface, - **connection_args) + endpoint = __salt__["keystone.endpoint_get"]( + name, region, profile=profile, interface=interface, **connection_args + ) def _changes(desc): - return ret.get('comment', '') + desc + '\n' + return ret.get("comment", "") + desc + "\n" def _create_endpoint(): if _OS_IDENTITY_API_VERSION > 2: - ret['changes'] = __salt__['keystone.endpoint_create']( + ret["changes"] = __salt__["keystone.endpoint_create"]( name, region=region, url=url, interface=interface, profile=profile, - **connection_args) + **connection_args + ) else: - ret['changes'] = __salt__['keystone.endpoint_create']( + ret["changes"] = __salt__["keystone.endpoint_create"]( name, region=region, publicurl=publicurl, adminurl=adminurl, internalurl=internalurl, profile=profile, - **connection_args) + **connection_args + ) - if endpoint and 'Error' not in endpoint and endpoint.get('region') == region: + if endpoint and "Error" not in endpoint and endpoint.get("region") == region: if _OS_IDENTITY_API_VERSION > 2: change_url = False change_interface = False - if endpoint.get('url', None) != url: - ret['comment'] = _changes('URL changes from "{0}" to "{1}"'.format(endpoint.get('url', None), url)) + if endpoint.get("url", None) != url: + ret["comment"] = _changes( + 'URL changes from "{0}" to "{1}"'.format( + endpoint.get("url", None), url + ) + ) change_url = True - if endpoint.get('interface', None) != interface: - ret['comment'] = _changes('Interface changes from "{0}" to "{1}"'.format(endpoint.get('interface', None), interface)) + if endpoint.get("interface", None) != interface: + ret["comment"] = _changes( + 'Interface changes from "{0}" to "{1}"'.format( + endpoint.get("interface", None), interface + ) + ) change_interface = True - if __opts__.get('test') and (change_url or change_interface): - ret['result'] = None - ret['changes']['Endpoint'] = 'Will be updated' - ret['comment'] += 'Endpoint for service "{0}" will be updated'.format(name) + if __opts__.get("test") and (change_url or change_interface): + ret["result"] = None + ret["changes"]["Endpoint"] = "Will be updated" + ret["comment"] += 'Endpoint for service "{0}" will be updated'.format( + name + ) return ret if change_url: - ret['changes']['url'] = url + ret["changes"]["url"] = url if change_interface: - ret['changes']['interface'] = interface + ret["changes"]["interface"] = interface else: change_publicurl = False change_adminurl = False change_internalurl = False - if endpoint.get('publicurl', None) != publicurl: + if endpoint.get("publicurl", None) != publicurl: change_publicurl = True - ret['comment'] = _changes('Public URL changes from "{0}" to "{1}"'.format( - endpoint.get('publicurl', None), publicurl) - ) - - if endpoint.get('adminurl', None) != adminurl: - change_adminurl = True - ret['comment'] = _changes('Admin URL changes from "{0}" to "{1}"'.format( - endpoint.get('adminurl', None), adminurl) - ) - - if endpoint.get('internalurl', None) != internalurl: - change_internalurl = True - ret['comment'] = _changes( - 'Internal URL changes from "{0}" to "{1}"'.format( - endpoint.get('internalurl', None), - internalurl + ret["comment"] = _changes( + 'Public URL changes from "{0}" to "{1}"'.format( + endpoint.get("publicurl", None), publicurl ) ) - if __opts__.get('test') and (change_publicurl or change_adminurl or change_internalurl): - ret['result'] = None - ret['comment'] += 'Endpoint for service "{0}" will be updated'.format(name) - ret['changes']['Endpoint'] = 'Will be updated' + if endpoint.get("adminurl", None) != adminurl: + change_adminurl = True + ret["comment"] = _changes( + 'Admin URL changes from "{0}" to "{1}"'.format( + endpoint.get("adminurl", None), adminurl + ) + ) + + if endpoint.get("internalurl", None) != internalurl: + change_internalurl = True + ret["comment"] = _changes( + 'Internal URL changes from "{0}" to "{1}"'.format( + endpoint.get("internalurl", None), internalurl + ) + ) + + if __opts__.get("test") and ( + change_publicurl or change_adminurl or change_internalurl + ): + ret["result"] = None + ret["comment"] += 'Endpoint for service "{0}" will be updated'.format( + name + ) + ret["changes"]["Endpoint"] = "Will be updated" return ret if change_publicurl: - ret['changes']['publicurl'] = publicurl + ret["changes"]["publicurl"] = publicurl if change_adminurl: - ret['changes']['adminurl'] = adminurl + ret["changes"]["adminurl"] = adminurl if change_internalurl: - ret['changes']['internalurl'] = internalurl + ret["changes"]["internalurl"] = internalurl - if ret['comment']: # changed - __salt__['keystone.endpoint_delete'](name, region, profile=profile, interface=interface, **connection_args) + if ret["comment"]: # changed + __salt__["keystone.endpoint_delete"]( + name, region, profile=profile, interface=interface, **connection_args + ) _create_endpoint() - ret['comment'] += 'Endpoint for service "{0}" has been updated'.format(name) + ret["comment"] += 'Endpoint for service "{0}" has been updated'.format(name) else: # Add new endpoint - if __opts__.get('test'): - ret['result'] = None - ret['changes']['Endpoint'] = 'Will be created' - ret['comment'] = 'Endpoint for service "{0}" will be added'.format(name) + if __opts__.get("test"): + ret["result"] = None + ret["changes"]["Endpoint"] = "Will be created" + ret["comment"] = 'Endpoint for service "{0}" will be added'.format(name) return ret _create_endpoint() - ret['comment'] = 'Endpoint for service "{0}" has been added'.format(name) + ret["comment"] = 'Endpoint for service "{0}" has been added'.format(name) - if ret['comment'] == '': # => no changes - ret['comment'] = 'Endpoint for service "{0}" already exists'.format(name) + if ret["comment"] == "": # => no changes + ret["comment"] = 'Endpoint for service "{0}" already exists'.format(name) return ret def endpoint_absent(name, region=None, profile=None, interface=None, **connection_args): - ''' + """ Ensure that the endpoint for a service doesn't exist in Keystone catalog name @@ -784,31 +878,35 @@ def endpoint_absent(name, region=None, profile=None, interface=None, **connectio interface The interface type, which describes the visibility of the endpoint. (for V3 API) - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Endpoint for service "{0}"{1} is already absent'.format(name, - ', interface "{0}",'.format(interface) if interface is not None else '')} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Endpoint for service "{0}"{1} is already absent'.format( + name, + ', interface "{0}",'.format(interface) if interface is not None else "", + ), + } # Check if service is present - endpoint = __salt__['keystone.endpoint_get'](name, region, - profile=profile, - interface=interface, - **connection_args) + endpoint = __salt__["keystone.endpoint_get"]( + name, region, profile=profile, interface=interface, **connection_args + ) if not endpoint: return ret else: - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Endpoint for service "{0}" will be deleted'.format(name) + if __opts__.get("test"): + ret["result"] = None + ret["comment"] = 'Endpoint for service "{0}" will be deleted'.format(name) return ret # Delete service - __salt__['keystone.endpoint_delete'](name, region, - profile=profile, - interface=interface, - **connection_args) - ret['comment'] = 'Endpoint for service "{0}"{1} has been deleted'.format(name, - ', interface "{0}",'.format(interface) if interface is not None else '') - ret['changes']['endpoint'] = 'Deleted' + __salt__["keystone.endpoint_delete"]( + name, region, profile=profile, interface=interface, **connection_args + ) + ret["comment"] = 'Endpoint for service "{0}"{1} has been deleted'.format( + name, + ', interface "{0}",'.format(interface) if interface is not None else "", + ) + ret["changes"]["endpoint"] = "Deleted" return ret diff --git a/salt/states/keystone_domain.py b/salt/states/keystone_domain.py index 095a181cc03..c4a6ae93158 100644 --- a/salt/states/keystone_domain.py +++ b/salt/states/keystone_domain.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Keystone Domains ======================================== @@ -25,21 +25,24 @@ Example States delete domain: keystone_domain.absent: - name: domain1 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'keystone_domain' +__virtualname__ = "keystone_domain" def __virtual__(): - if 'keystoneng.domain_get' in __salt__: + if "keystoneng.domain_get" in __salt__: return __virtualname__ - return (False, 'The keystoneng execution module failed to load: shade python module is not available') + return ( + False, + "The keystoneng execution module failed to load: shade python module is not available", + ) def present(name, auth=None, **kwargs): - ''' + """ Ensure domain exists and is up-to-date name @@ -50,72 +53,66 @@ def present(name, auth=None, **kwargs): description An arbitrary description of the domain - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - domain = __salt__['keystoneng.domain_get'](name=name) + domain = __salt__["keystoneng.domain_get"](name=name) if not domain: - if __opts__['test']: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Domain {} will be created.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Domain {} will be created.".format(name) return ret - kwargs['name'] = name - domain = __salt__['keystoneng.domain_create'](**kwargs) - ret['changes'] = domain - ret['comment'] = 'Created domain' + kwargs["name"] = name + domain = __salt__["keystoneng.domain_create"](**kwargs) + ret["changes"] = domain + ret["comment"] = "Created domain" return ret - changes = __salt__['keystoneng.compare_changes'](domain, **kwargs) + changes = __salt__["keystoneng.compare_changes"](domain, **kwargs) if changes: - if __opts__['test']: - ret['result'] = None - ret['changes'] = changes - ret['comment'] = 'Domain {} will be updated.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["changes"] = changes + ret["comment"] = "Domain {} will be updated.".format(name) return ret - kwargs['domain_id'] = domain.id - __salt__['keystoneng.domain_update'](**kwargs) - ret['changes'].update(changes) - ret['comment'] = 'Updated domain' + kwargs["domain_id"] = domain.id + __salt__["keystoneng.domain_update"](**kwargs) + ret["changes"].update(changes) + ret["comment"] = "Updated domain" return ret def absent(name, auth=None): - ''' + """ Ensure domain does not exist name Name of the domain - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - domain = __salt__['keystoneng.domain_get'](name=name) + domain = __salt__["keystoneng.domain_get"](name=name) if domain: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'name': name} - ret['comment'] = 'Domain {} will be deleted.'.format(name) + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"name": name} + ret["comment"] = "Domain {} will be deleted.".format(name) return ret - __salt__['keystoneng.domain_delete'](name=domain) - ret['changes']['id'] = domain.id - ret['comment'] = 'Deleted domain' + __salt__["keystoneng.domain_delete"](name=domain) + ret["changes"]["id"] = domain.id + ret["comment"] = "Deleted domain" return ret diff --git a/salt/states/keystone_endpoint.py b/salt/states/keystone_endpoint.py index 7b19913572a..01a93f7b96d 100644 --- a/salt/states/keystone_endpoint.py +++ b/salt/states/keystone_endpoint.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Keystone Endpoints ========================================== @@ -35,50 +35,53 @@ Example States - url: https://example.org:9292 - region: RegionOne - service_name: glance -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'keystone_endpoint' +__virtualname__ = "keystone_endpoint" def __virtual__(): - if 'keystoneng.endpoint_get' in __salt__: + if "keystoneng.endpoint_get" in __salt__: return __virtualname__ - return (False, 'The keystoneng execution module failed to load: shade python module is not available') + return ( + False, + "The keystoneng execution module failed to load: shade python module is not available", + ) def _common(ret, name, service_name, kwargs): - ''' + """ Returns: tuple whose first element is a bool indicating success or failure and the second element is either a ret dict for salt or an object - ''' - if 'interface' not in kwargs and 'public_url' not in kwargs: - kwargs['interface'] = name - service = __salt__['keystoneng.service_get'](name_or_id=service_name) + """ + if "interface" not in kwargs and "public_url" not in kwargs: + kwargs["interface"] = name + service = __salt__["keystoneng.service_get"](name_or_id=service_name) if not service: - ret['comment'] = 'Cannot find service' - ret['result'] = False + ret["comment"] = "Cannot find service" + ret["result"] = False return (False, ret) filters = kwargs.copy() - filters.pop('enabled', None) - filters.pop('url', None) - filters['service_id'] = service.id - kwargs['service_name_or_id'] = service.id - endpoints = __salt__['keystoneng.endpoint_search'](filters=filters) + filters.pop("enabled", None) + filters.pop("url", None) + filters["service_id"] = service.id + kwargs["service_name_or_id"] = service.id + endpoints = __salt__["keystoneng.endpoint_search"](filters=filters) if len(endpoints) > 1: - ret['comment'] = "Multiple endpoints match criteria" - ret['result'] = False + ret["comment"] = "Multiple endpoints match criteria" + ret["result"] = False return ret endpoint = endpoints[0] if endpoints else None return (True, endpoint) def present(name, service_name, auth=None, **kwargs): - ''' + """ Ensure an endpoint exists and is up-to-date name @@ -95,56 +98,53 @@ def present(name, service_name, auth=None, **kwargs): enabled Boolean to control if endpoint is enabled - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) success, val = _, endpoint = _common(ret, name, service_name, kwargs) if not success: return val if not endpoint: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Endpoint will be created.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Endpoint will be created." return ret # NOTE(SamYaple): Endpoints are returned as a list which can contain # several items depending on the options passed - endpoints = __salt__['keystoneng.endpoint_create'](**kwargs) + endpoints = __salt__["keystoneng.endpoint_create"](**kwargs) if len(endpoints) == 1: - ret['changes'] = endpoints[0] + ret["changes"] = endpoints[0] else: for i, endpoint in enumerate(endpoints): - ret['changes'][i] = endpoint - ret['comment'] = 'Created endpoint' + ret["changes"][i] = endpoint + ret["comment"] = "Created endpoint" return ret - changes = __salt__['keystoneng.compare_changes'](endpoint, **kwargs) + changes = __salt__["keystoneng.compare_changes"](endpoint, **kwargs) if changes: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = changes - ret['comment'] = 'Endpoint will be updated.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = changes + ret["comment"] = "Endpoint will be updated." return ret - kwargs['endpoint_id'] = endpoint.id - __salt__['keystoneng.endpoint_update'](**kwargs) - ret['changes'].update(changes) - ret['comment'] = 'Updated endpoint' + kwargs["endpoint_id"] = endpoint.id + __salt__["keystoneng.endpoint_update"](**kwargs) + ret["changes"].update(changes) + ret["comment"] = "Updated endpoint" return ret def absent(name, service_name, auth=None, **kwargs): - ''' + """ Ensure an endpoint does not exists name @@ -158,27 +158,24 @@ def absent(name, service_name, auth=None, **kwargs): region The region name to assign the endpoint - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) success, val = _, endpoint = _common(ret, name, service_name, kwargs) if not success: return val if endpoint: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'id': endpoint.id} - ret['comment'] = 'Endpoint will be deleted.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"id": endpoint.id} + ret["comment"] = "Endpoint will be deleted." return ret - __salt__['keystoneng.endpoint_delete'](id=endpoint.id) - ret['changes']['id'] = endpoint.id - ret['comment'] = 'Deleted endpoint' + __salt__["keystoneng.endpoint_delete"](id=endpoint.id) + ret["changes"]["id"] = endpoint.id + ret["comment"] = "Deleted endpoint" return ret diff --git a/salt/states/keystone_group.py b/salt/states/keystone_group.py index cfd4af02c0a..375b5e159e0 100644 --- a/salt/states/keystone_group.py +++ b/salt/states/keystone_group.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Keystone Groups ======================================= @@ -25,36 +25,38 @@ Example States - name: group1 - domain: domain1 - description: 'my group' -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'keystone_group' +__virtualname__ = "keystone_group" def __virtual__(): - if 'keystoneng.group_get' in __salt__: + if "keystoneng.group_get" in __salt__: return __virtualname__ - return (False, 'The keystoneng execution module failed to load: shade python module is not available') + return ( + False, + "The keystoneng execution module failed to load: shade python module is not available", + ) def _common(kwargs): - ''' + """ Returns: None if group wasn't found, otherwise a group object - ''' - search_kwargs = {'name': kwargs['name']} - if 'domain' in kwargs: - domain = __salt__['keystoneng.get_entity']( - 'domain', name=kwargs.pop('domain')) - domain_id = domain.id if hasattr(domain, 'id') else domain - search_kwargs['filters'] = {'domain_id': domain_id} - kwargs['domain'] = domain + """ + search_kwargs = {"name": kwargs["name"]} + if "domain" in kwargs: + domain = __salt__["keystoneng.get_entity"]("domain", name=kwargs.pop("domain")) + domain_id = domain.id if hasattr(domain, "id") else domain + search_kwargs["filters"] = {"domain_id": domain_id} + kwargs["domain"] = domain - return __salt__['keystoneng.group_get'](**search_kwargs) + return __salt__["keystoneng.group_get"](**search_kwargs) def present(name, auth=None, **kwargs): - ''' + """ Ensure an group exists and is up-to-date name @@ -65,48 +67,45 @@ def present(name, auth=None, **kwargs): description An arbitrary description of the group - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - __salt__['keystoneng.setup_cloud'](auth) + __salt__["keystoneng.setup_cloud"](auth) - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - kwargs['name'] = name + kwargs["name"] = name group = _common(kwargs) if group is None: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Group will be created.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Group will be created." return ret - group = __salt__['keystoneng.group_create'](**kwargs) - ret['changes'] = group - ret['comment'] = 'Created group' + group = __salt__["keystoneng.group_create"](**kwargs) + ret["changes"] = group + ret["comment"] = "Created group" return ret - changes = __salt__['keystoneng.compare_changes'](group, **kwargs) + changes = __salt__["keystoneng.compare_changes"](group, **kwargs) if changes: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = changes - ret['comment'] = 'Group will be updated.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = changes + ret["comment"] = "Group will be updated." return ret - __salt__['keystoneng.group_update'](**kwargs) - ret['changes'].update(changes) - ret['comment'] = 'Updated group' + __salt__["keystoneng.group_update"](**kwargs) + ret["changes"].update(changes) + ret["comment"] = "Updated group" return ret def absent(name, auth=None, **kwargs): - ''' + """ Ensure group does not exist name @@ -114,28 +113,25 @@ def absent(name, auth=None, **kwargs): domain The name or id of the domain - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['keystoneng.setup_cloud'](auth) + __salt__["keystoneng.setup_cloud"](auth) - kwargs['name'] = name + kwargs["name"] = name group = _common(kwargs) if group: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'id': group.id} - ret['comment'] = 'Group will be deleted.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"id": group.id} + ret["comment"] = "Group will be deleted." return ret - __salt__['keystoneng.group_delete'](name=group) - ret['changes']['id'] = group.id - ret['comment'] = 'Deleted group' + __salt__["keystoneng.group_delete"](name=group) + ret["changes"]["id"] = group.id + ret["comment"] = "Deleted group" return ret diff --git a/salt/states/keystone_project.py b/salt/states/keystone_project.py index bb9327b5db0..98fa36adaca 100644 --- a/salt/states/keystone_project.py +++ b/salt/states/keystone_project.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Keystone Projects ========================================= @@ -26,36 +26,38 @@ Example States - domain: domain1 - enabled: False - description: 'my project' -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'keystone_project' +__virtualname__ = "keystone_project" def __virtual__(): - if 'keystoneng.project_get' in __salt__: + if "keystoneng.project_get" in __salt__: return __virtualname__ - return (False, 'The keystoneng execution module failed to load: shade python module is not available') + return ( + False, + "The keystoneng execution module failed to load: shade python module is not available", + ) def _common(name, kwargs): - ''' + """ Returns: None if project wasn't found, otherwise a group object - ''' - search_kwargs = {'name': name} - if 'domain' in kwargs: - domain = __salt__['keystoneng.get_entity']( - 'domain', name=kwargs.pop('domain')) - domain_id = domain.id if hasattr(domain, 'id') else domain - search_kwargs['domain_id'] = domain_id - kwargs['domain_id'] = domain_id + """ + search_kwargs = {"name": name} + if "domain" in kwargs: + domain = __salt__["keystoneng.get_entity"]("domain", name=kwargs.pop("domain")) + domain_id = domain.id if hasattr(domain, "id") else domain + search_kwargs["domain_id"] = domain_id + kwargs["domain_id"] = domain_id - return __salt__['keystoneng.project_get'](**search_kwargs) + return __salt__["keystoneng.project_get"](**search_kwargs) def present(name, auth=None, **kwargs): - ''' + """ Ensure a project exists and is up-to-date name @@ -66,48 +68,45 @@ def present(name, auth=None, **kwargs): description An arbitrary description of the project - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - kwargs['name'] = name + kwargs["name"] = name project = _common(name, kwargs) if project is None: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Project will be created.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Project will be created." return ret - project = __salt__['keystoneng.project_create'](**kwargs) - ret['changes'] = project - ret['comment'] = 'Created project' + project = __salt__["keystoneng.project_create"](**kwargs) + ret["changes"] = project + ret["comment"] = "Created project" return ret - changes = __salt__['keystoneng.compare_changes'](project, **kwargs) + changes = __salt__["keystoneng.compare_changes"](project, **kwargs) if changes: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = changes - ret['comment'] = 'Project will be updated.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = changes + ret["comment"] = "Project will be updated." return ret - __salt__['keystoneng.project_update'](**kwargs) - ret['changes'].update(changes) - ret['comment'] = 'Updated project' + __salt__["keystoneng.project_update"](**kwargs) + ret["changes"].update(changes) + ret["comment"] = "Updated project" return ret def absent(name, auth=None, **kwargs): - ''' + """ Ensure a project does not exists name @@ -115,28 +114,25 @@ def absent(name, auth=None, **kwargs): domain The name or id of the domain - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - kwargs['name'] = name + kwargs["name"] = name project = _common(name, kwargs) if project: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'id': project.id} - ret['comment'] = 'Project will be deleted.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"id": project.id} + ret["comment"] = "Project will be deleted." return ret - __salt__['keystoneng.project_delete'](name=project) - ret['changes']['id'] = project.id - ret['comment'] = 'Deleted project' + __salt__["keystoneng.project_delete"](name=project) + ret["changes"]["id"] = project.id + ret["comment"] = "Deleted project" return ret diff --git a/salt/states/keystone_role.py b/salt/states/keystone_role.py index d90d45f0a2b..9afd0ec7a55 100644 --- a/salt/states/keystone_role.py +++ b/salt/states/keystone_role.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Keystone Roles ====================================== @@ -24,21 +24,24 @@ Example States keystone_role.present: - name: role1 - description: 'my group' -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'keystone_role' +__virtualname__ = "keystone_role" def __virtual__(): - if 'keystoneng.role_get' in __salt__: + if "keystoneng.role_get" in __salt__: return __virtualname__ - return (False, 'The keystoneng execution module failed to load: shade python module is not available') + return ( + False, + "The keystoneng execution module failed to load: shade python module is not available", + ) def present(name, auth=None, **kwargs): - ''' + """ Ensure an role exists name @@ -46,61 +49,55 @@ def present(name, auth=None, **kwargs): description An arbitrary description of the role - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - kwargs['name'] = name - role = __salt__['keystoneng.role_get'](**kwargs) + kwargs["name"] = name + role = __salt__["keystoneng.role_get"](**kwargs) if not role: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Role will be created.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Role will be created." return ret - role = __salt__['keystoneng.role_create'](**kwargs) - ret['changes']['id'] = role.id - ret['changes']['name'] = role.name - ret['comment'] = 'Created role' + role = __salt__["keystoneng.role_create"](**kwargs) + ret["changes"]["id"] = role.id + ret["changes"]["name"] = role.name + ret["comment"] = "Created role" return ret # NOTE(SamYaple): Update support pending https://review.openstack.org/#/c/496992/ return ret def absent(name, auth=None, **kwargs): - ''' + """ Ensure role does not exist name Name of the role - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - kwargs['name'] = name - role = __salt__['keystoneng.role_get'](**kwargs) + kwargs["name"] = name + role = __salt__["keystoneng.role_get"](**kwargs) if role: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'id': role.id} - ret['comment'] = 'Role will be deleted.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"id": role.id} + ret["comment"] = "Role will be deleted." return ret - __salt__['keystoneng.role_delete'](name=role) - ret['changes']['id'] = role.id - ret['comment'] = 'Deleted role' + __salt__["keystoneng.role_delete"](name=role) + ret["changes"]["id"] = role.id + ret["comment"] = "Deleted role" return ret diff --git a/salt/states/keystone_role_grant.py b/salt/states/keystone_role_grant.py index bf2f23f5584..ad24e86289a 100644 --- a/salt/states/keystone_role_grant.py +++ b/salt/states/keystone_role_grant.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Keystone Role Grants ============================================ @@ -25,116 +25,115 @@ Example States - name: group1 - domain: domain1 - description: 'my group' -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'keystone_role_grant' +__virtualname__ = "keystone_role_grant" def __virtual__(): - if 'keystoneng.role_grant' in __salt__: + if "keystoneng.role_grant" in __salt__: return __virtualname__ - return (False, 'The keystoneng execution module failed to load: shade python module is not available') + return ( + False, + "The keystoneng execution module failed to load: shade python module is not available", + ) def _get_filters(kwargs): - role_kwargs = {'name': kwargs.pop('role')} - if 'role_domain' in kwargs: - domain = __salt__['keystoneng.get_entity']( - 'domain', name=kwargs.pop('role_domain')) + role_kwargs = {"name": kwargs.pop("role")} + if "role_domain" in kwargs: + domain = __salt__["keystoneng.get_entity"]( + "domain", name=kwargs.pop("role_domain") + ) if domain: - role_kwargs['domain_id'] = domain.id \ - if hasattr(domain, 'id') else domain - role = __salt__['keystoneng.role_get'](**role_kwargs) - kwargs['name'] = role - filters = {'role': role.id if hasattr(role, 'id') else role} + role_kwargs["domain_id"] = domain.id if hasattr(domain, "id") else domain + role = __salt__["keystoneng.role_get"](**role_kwargs) + kwargs["name"] = role + filters = {"role": role.id if hasattr(role, "id") else role} - if 'domain' in kwargs: - domain = __salt__['keystoneng.get_entity']( - 'domain', name=kwargs.pop('domain')) - kwargs['domain'] = filters['domain'] = \ - domain.id if hasattr(domain, 'id') else domain + if "domain" in kwargs: + domain = __salt__["keystoneng.get_entity"]("domain", name=kwargs.pop("domain")) + kwargs["domain"] = filters["domain"] = ( + domain.id if hasattr(domain, "id") else domain + ) - if 'project' in kwargs: - project_kwargs = {'name': kwargs.pop('project')} - if 'project_domain' in kwargs: - domain = __salt__['keystoneng.get_entity']( - 'domain', name=kwargs.pop('project_domain')) + if "project" in kwargs: + project_kwargs = {"name": kwargs.pop("project")} + if "project_domain" in kwargs: + domain = __salt__["keystoneng.get_entity"]( + "domain", name=kwargs.pop("project_domain") + ) if domain: - project_kwargs['domain_id'] = domain.id - project = __salt__['keystoneng.get_entity']( - 'project', **project_kwargs) - kwargs['project'] = project - filters['project'] = project.id if hasattr(project, 'id') else project + project_kwargs["domain_id"] = domain.id + project = __salt__["keystoneng.get_entity"]("project", **project_kwargs) + kwargs["project"] = project + filters["project"] = project.id if hasattr(project, "id") else project - if 'user' in kwargs: - user_kwargs = {'name': kwargs.pop('user')} - if 'user_domain' in kwargs: - domain = __salt__['keystoneng.get_entity']( - 'domain', name=kwargs.pop('user_domain')) + if "user" in kwargs: + user_kwargs = {"name": kwargs.pop("user")} + if "user_domain" in kwargs: + domain = __salt__["keystoneng.get_entity"]( + "domain", name=kwargs.pop("user_domain") + ) if domain: - user_kwargs['domain_id'] = domain.id - user = __salt__['keystoneng.get_entity']('user', **user_kwargs) - kwargs['user'] = user - filters['user'] = user.id if hasattr(user, 'id') else user + user_kwargs["domain_id"] = domain.id + user = __salt__["keystoneng.get_entity"]("user", **user_kwargs) + kwargs["user"] = user + filters["user"] = user.id if hasattr(user, "id") else user - if 'group' in kwargs: - group_kwargs = {'name': kwargs['group']} - if 'group_domain' in kwargs: - domain = __salt__['keystoneng.get_entity']( - 'domain', name=kwargs.pop('group_domain')) + if "group" in kwargs: + group_kwargs = {"name": kwargs["group"]} + if "group_domain" in kwargs: + domain = __salt__["keystoneng.get_entity"]( + "domain", name=kwargs.pop("group_domain") + ) if domain: - group_kwargs['domain_id'] = domain.id - group = __salt__['keystoneng.get_entity']('group', **group_kwargs) + group_kwargs["domain_id"] = domain.id + group = __salt__["keystoneng.get_entity"]("group", **group_kwargs) - kwargs['group'] = group - filters['group'] = group.id if hasattr(group, 'id') else group + kwargs["group"] = group + filters["group"] = group.id if hasattr(group, "id") else group return filters, kwargs def present(name, auth=None, **kwargs): - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - if 'role' not in kwargs: - kwargs['role'] = name + if "role" not in kwargs: + kwargs["role"] = name filters, kwargs = _get_filters(kwargs) - grants = __salt__['keystoneng.role_assignment_list'](filters=filters) + grants = __salt__["keystoneng.role_assignment_list"](filters=filters) if not grants: - __salt__['keystoneng.role_grant'](**kwargs) + __salt__["keystoneng.role_grant"](**kwargs) for k, v in filters.items(): - ret['changes'][k] = v - ret['comment'] = 'Granted role assignment' + ret["changes"][k] = v + ret["comment"] = "Granted role assignment" return ret def absent(name, auth=None, **kwargs): - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - if 'role' not in kwargs: - kwargs['role'] = name + if "role" not in kwargs: + kwargs["role"] = name filters, kwargs = _get_filters(kwargs) - grants = __salt__['keystoneng.role_assignment_list'](filters=filters) + grants = __salt__["keystoneng.role_assignment_list"](filters=filters) if grants: - __salt__['keystoneng.role_revoke'](**kwargs) + __salt__["keystoneng.role_revoke"](**kwargs) for k, v in filters.items(): - ret['changes'][k] = v - ret['comment'] = 'Revoked role assignment' + ret["changes"][k] = v + ret["comment"] = "Revoked role assignment" return ret diff --git a/salt/states/keystone_service.py b/salt/states/keystone_service.py index faca6d62357..58b47fbd695 100644 --- a/salt/states/keystone_service.py +++ b/salt/states/keystone_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Keystone Services ========================================= @@ -27,21 +27,24 @@ Example States - type: image - enabled: False - description: 'OpenStack Image' -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'keystone_service' +__virtualname__ = "keystone_service" def __virtual__(): - if 'keystoneng.service_get' in __salt__: + if "keystoneng.service_get" in __salt__: return __virtualname__ - return (False, 'The keystoneng execution module failed to load: shade python module is not available') + return ( + False, + "The keystoneng execution module failed to load: shade python module is not available", + ) def present(name, auth=None, **kwargs): - ''' + """ Ensure an service exists and is up-to-date name @@ -55,73 +58,67 @@ def present(name, auth=None, **kwargs): description An arbitrary description of the service - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - service = __salt__['keystoneng.service_get'](name=name) + service = __salt__["keystoneng.service_get"](name=name) if service is None: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Service will be created.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Service will be created." return ret - kwargs['name'] = name - service = __salt__['keystoneng.service_create'](**kwargs) - ret['changes'] = service - ret['comment'] = 'Created service' + kwargs["name"] = name + service = __salt__["keystoneng.service_create"](**kwargs) + ret["changes"] = service + ret["comment"] = "Created service" return ret - changes = __salt__['keystoneng.compare_changes'](service, **kwargs) + changes = __salt__["keystoneng.compare_changes"](service, **kwargs) if changes: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = changes - ret['comment'] = 'Service will be updated.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = changes + ret["comment"] = "Service will be updated." return ret - kwargs['name'] = service - __salt__['keystoneng.service_update'](**kwargs) - ret['changes'].update(changes) - ret['comment'] = 'Updated service' + kwargs["name"] = service + __salt__["keystoneng.service_update"](**kwargs) + ret["changes"].update(changes) + ret["comment"] = "Updated service" return ret def absent(name, auth=None): - ''' + """ Ensure service does not exist name Name of the service - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - service = __salt__['keystoneng.service_get'](name=name) + service = __salt__["keystoneng.service_get"](name=name) if service: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'id': service.id} - ret['comment'] = 'Service will be deleted.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"id": service.id} + ret["comment"] = "Service will be deleted." return ret - __salt__['keystoneng.service_delete'](name=service) - ret['changes']['id'] = service.id - ret['comment'] = 'Deleted service' + __salt__["keystoneng.service_delete"](name=service) + ret["changes"]["id"] = service.id + ret["comment"] = "Deleted service" return ret diff --git a/salt/states/keystone_user.py b/salt/states/keystone_user.py index a1bfd8d85ec..93a97f16d19 100644 --- a/salt/states/keystone_user.py +++ b/salt/states/keystone_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Keystone Users ====================================== @@ -28,36 +28,38 @@ Example States - password: password123 - email: "user1@example.org" - description: 'my user' -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'keystone_user' +__virtualname__ = "keystone_user" def __virtual__(): - if 'keystoneng.user_get' in __salt__: + if "keystoneng.user_get" in __salt__: return __virtualname__ - return (False, 'The keystoneng execution module failed to load: shade python module is not available') + return ( + False, + "The keystoneng execution module failed to load: shade python module is not available", + ) def _common(kwargs): - ''' + """ Returns: None if user wasn't found, otherwise a user object - ''' - search_kwargs = {'name': kwargs['name']} - if 'domain' in kwargs: - domain = __salt__['keystoneng.get_entity']( - 'domain', name=kwargs.pop('domain')) - domain_id = domain.id if hasattr(domain, 'id') else domain - search_kwargs['domain_id'] = domain_id - kwargs['domain_id'] = domain_id + """ + search_kwargs = {"name": kwargs["name"]} + if "domain" in kwargs: + domain = __salt__["keystoneng.get_entity"]("domain", name=kwargs.pop("domain")) + domain_id = domain.id if hasattr(domain, "id") else domain + search_kwargs["domain_id"] = domain_id + kwargs["domain_id"] = domain_id - return __salt__['keystoneng.user_get'](**search_kwargs) + return __salt__["keystoneng.user_get"](**search_kwargs) def present(name, auth=None, **kwargs): - ''' + """ Ensure domain exists and is up-to-date name @@ -77,49 +79,46 @@ def present(name, auth=None, **kwargs): email The users email address - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - kwargs['name'] = name + kwargs["name"] = name user = _common(kwargs) if user is None: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'User will be created.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "User will be created." return ret - user = __salt__['keystoneng.user_create'](**kwargs) - ret['changes'] = user - ret['comment'] = 'Created user' + user = __salt__["keystoneng.user_create"](**kwargs) + ret["changes"] = user + ret["comment"] = "Created user" return ret - changes = __salt__['keystoneng.compare_changes'](user, **kwargs) + changes = __salt__["keystoneng.compare_changes"](user, **kwargs) if changes: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = changes - ret['comment'] = 'User will be updated.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = changes + ret["comment"] = "User will be updated." return ret - kwargs['name'] = user - __salt__['keystoneng.user_update'](**kwargs) - ret['changes'].update(changes) - ret['comment'] = 'Updated user' + kwargs["name"] = user + __salt__["keystoneng.user_update"](**kwargs) + ret["changes"].update(changes) + ret["comment"] = "Updated user" return ret def absent(name, auth=None, **kwargs): - ''' + """ Ensure user does not exists name @@ -127,28 +126,25 @@ def absent(name, auth=None, **kwargs): domain The name or id of the domain - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['keystoneng.setup_clouds'](auth) + __salt__["keystoneng.setup_clouds"](auth) - kwargs['name'] = name + kwargs["name"] = name user = _common(kwargs) if user: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'id': user.id} - ret['comment'] = 'User will be deleted.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"id": user.id} + ret["comment"] = "User will be deleted." return ret - __salt__['keystoneng.user_delete'](name=user) - ret['changes']['id'] = user.id - ret['comment'] = 'Deleted user' + __salt__["keystoneng.user_delete"](name=user) + ret["changes"]["id"] = user.id + ret["comment"] = "Deleted user" return ret diff --git a/salt/states/keystore.py b/salt/states/keystore.py index 2476f34d060..61cc9a7ec60 100644 --- a/salt/states/keystore.py +++ b/salt/states/keystore.py @@ -1,31 +1,37 @@ # -*- coding: utf-8 -*- -''' +""" State management of a java keystore -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os -__virtualname__ = 'keystore' +__virtualname__ = "keystore" # Init logger log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load this module if the keystore execution module is available - ''' - if 'keystore.list' in __salt__: + """ + if "keystore.list" in __salt__: return __virtualname__ - return (False, ('Cannot load the {0} state module: ' - 'keystore execution module not found'.format(__virtualname__))) + return ( + False, + ( + "Cannot load the {0} state module: " + "keystore execution module not found".format(__virtualname__) + ), + ) def managed(name, passphrase, entries, force_remove=False): - ''' + """ Create or manage a java keystore. name @@ -73,20 +79,17 @@ def managed(name, passphrase, entries, force_remove=False): private_key: /path/to/key2.key - alias: pillarhost certificate: {{ salt.pillar.get('path:to:cert') }} - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} keep_list = [] old_aliases = [] if force_remove: if os.path.exists(name): - existing_entries = __salt__['keystore.list'](name, passphrase) + existing_entries = __salt__["keystore.list"](name, passphrase) for entry in existing_entries: - old_aliases.append(entry.get('alias')) + old_aliases.append(entry.get("alias")) log.debug("Existing aliases list: %s", old_aliases) for entry in entries: @@ -94,58 +97,68 @@ def managed(name, passphrase, entries, force_remove=False): existing_entry = None if os.path.exists(name): if force_remove: - keep_list.append(entry['alias']) + keep_list.append(entry["alias"]) - existing_entry = __salt__['keystore.list'](name, passphrase, entry['alias']) + existing_entry = __salt__["keystore.list"](name, passphrase, entry["alias"]) if existing_entry: - existing_sha1 = existing_entry[0]['sha1'] - new_sha1 = __salt__['x509.read_certificate'](entry['certificate'])['SHA1 Finger Print'] + existing_sha1 = existing_entry[0]["sha1"] + new_sha1 = __salt__["x509.read_certificate"](entry["certificate"])[ + "SHA1 Finger Print" + ] if existing_sha1 == new_sha1: update_entry = False if update_entry: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if existing_entry: - ret['comment'] += "Alias {0} would have been updated\n".format(entry['alias']) + ret["comment"] += "Alias {0} would have been updated\n".format( + entry["alias"] + ) else: - ret['comment'] += "Alias {0} would have been added\n".format(entry['alias']) + ret["comment"] += "Alias {0} would have been added\n".format( + entry["alias"] + ) else: if existing_entry: - result = __salt__['keystore.remove'](entry['alias'], name, passphrase) - result = __salt__['keystore.add'](entry['alias'], - name, - passphrase, - entry['certificate'], - private_key=entry.get('private_key', None) - ) + result = __salt__["keystore.remove"]( + entry["alias"], name, passphrase + ) + result = __salt__["keystore.add"]( + entry["alias"], + name, + passphrase, + entry["certificate"], + private_key=entry.get("private_key", None), + ) if result: - ret['changes'][entry['alias']] = "Updated" - ret['comment'] += "Alias {0} updated.\n".format(entry['alias']) + ret["changes"][entry["alias"]] = "Updated" + ret["comment"] += "Alias {0} updated.\n".format(entry["alias"]) else: - result = __salt__['keystore.add'](entry['alias'], - name, - passphrase, - entry['certificate'], - private_key=entry.get('private_key', None) - ) + result = __salt__["keystore.add"]( + entry["alias"], + name, + passphrase, + entry["certificate"], + private_key=entry.get("private_key", None), + ) if result: - ret['changes'][entry['alias']] = "Added" - ret['comment'] += "Alias {0} added.\n".format(entry['alias']) + ret["changes"][entry["alias"]] = "Added" + ret["comment"] += "Alias {0} added.\n".format(entry["alias"]) if force_remove: # Determine which aliases need to be removed remove_list = list(set(old_aliases) - set(keep_list)) log.debug("Will remove: %s", remove_list) for alias_name in remove_list: - if __opts__['test']: - ret['comment'] += "Alias {0} would have been removed".format(alias_name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] += "Alias {0} would have been removed".format(alias_name) + ret["result"] = None else: - __salt__['keystore.remove'](alias_name, name, passphrase) - ret['changes'][alias_name] = "Removed" - ret['comment'] += "Alias {0} removed.\n".format(alias_name) + __salt__["keystore.remove"](alias_name, name, passphrase) + ret["changes"][alias_name] = "Removed" + ret["comment"] += "Alias {0} removed.\n".format(alias_name) - if not ret['changes'] and not ret['comment']: - ret['comment'] = "No changes made.\n" + if not ret["changes"] and not ret["comment"]: + ret["comment"] = "No changes made.\n" return ret diff --git a/salt/states/kmod.py b/salt/states/kmod.py index ec25b0c6a58..4f684e407cf 100644 --- a/salt/states/kmod.py +++ b/salt/states/kmod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Loading and unloading of kernel modules ======================================= @@ -29,31 +29,33 @@ Multiple modules can be specified for both kmod.present and kmod.absent. - snd_pcm - snd_timer - snd -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the kmod module is available in __salt__ - ''' - return 'kmod.available' in __salt__ + """ + if "kmod.available" in __salt__: + return True + return (False, "kmod module could not be loaded") def _append_comment(ret, comment): - ''' + """ append ``comment`` to ``ret['comment']`` - ''' - if ret['comment']: - ret['comment'] = ret['comment'].rstrip() + '\n' + comment + """ + if ret["comment"]: + ret["comment"] = ret["comment"].rstrip() + "\n" + comment else: - ret['comment'] = comment + ret["comment"] = comment return ret def present(name, persist=False, mods=None): - ''' + """ Ensure that the specified kernel module is loaded name @@ -68,27 +70,26 @@ def present(name, persist=False, mods=None): placeholder .. versionadded:: 2016.3.0 - ''' + """ if not isinstance(mods, (list, tuple)): mods = [name] - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - loaded_mods = __salt__['kmod.mod_list']() + loaded_mods = __salt__["kmod.mod_list"]() if persist: - persist_mods = __salt__['kmod.mod_list'](True) + persist_mods = __salt__["kmod.mod_list"](True) # Intersection of loaded modules and persistent modules loaded_mods = list(set(loaded_mods) & set(persist_mods)) # Intersection of loaded and proposed modules already_loaded = list(set(loaded_mods) & set(mods)) if len(already_loaded) == 1: - comment = 'Kernel module {0} is already present'.format(already_loaded[0]) + comment = "Kernel module {0} is already present".format(already_loaded[0]) _append_comment(ret, comment) elif len(already_loaded) > 1: - comment = 'Kernel modules {0} are already present'.format(', '.join(already_loaded)) + comment = "Kernel modules {0} are already present".format( + ", ".join(already_loaded) + ) _append_comment(ret, comment) if len(already_loaded) == len(mods): @@ -97,70 +98,80 @@ def present(name, persist=False, mods=None): # Complement of proposed modules and already loaded modules not_loaded = list(set(mods) - set(already_loaded)) - if __opts__['test']: - ret['result'] = None - if ret['comment']: - ret['comment'] += '\n' + if __opts__["test"]: + ret["result"] = None + if ret["comment"]: + ret["comment"] += "\n" if len(not_loaded) == 1: - comment = 'Kernel module {0} is set to be loaded'.format(not_loaded[0]) + comment = "Kernel module {0} is set to be loaded".format(not_loaded[0]) else: - comment = 'Kernel modules {0} are set to be loaded'.format(', '.join(not_loaded)) + comment = "Kernel modules {0} are set to be loaded".format( + ", ".join(not_loaded) + ) _append_comment(ret, comment) return ret # Complement of proposed, unloaded modules and available modules - unavailable = list(set(not_loaded) - set(__salt__['kmod.available']())) + unavailable = list(set(not_loaded) - set(__salt__["kmod.available"]())) if unavailable: if len(unavailable) == 1: - comment = 'Kernel module {0} is unavailable'.format(unavailable[0]) + comment = "Kernel module {0} is unavailable".format(unavailable[0]) else: - comment = 'Kernel modules {0} are unavailable'.format(', '.join(unavailable)) + comment = "Kernel modules {0} are unavailable".format( + ", ".join(unavailable) + ) _append_comment(ret, comment) - ret['result'] = False + ret["result"] = False # The remaining modules are not loaded and are available for loading available = list(set(not_loaded) - set(unavailable)) - loaded = {'yes': [], 'no': [], 'failed': []} + loaded = {"yes": [], "no": [], "failed": []} loaded_by_dependency = [] for mod in available: if mod in loaded_by_dependency: - loaded['yes'].append(mod) + loaded["yes"].append(mod) continue - load_result = __salt__['kmod.load'](mod, persist) + load_result = __salt__["kmod.load"](mod, persist) if isinstance(load_result, (list, tuple)): if len(load_result) > 0: for module in load_result: - ret['changes'][module] = 'loaded' + ret["changes"][module] = "loaded" if module != mod: loaded_by_dependency.append(module) - loaded['yes'].append(mod) + loaded["yes"].append(mod) else: - ret['result'] = False - loaded['no'].append(mod) + ret["result"] = False + loaded["no"].append(mod) else: - ret['result'] = False - loaded['failed'].append([mod, load_result]) + ret["result"] = False + loaded["failed"].append([mod, load_result]) # Update comment with results - if len(loaded['yes']) == 1: - _append_comment(ret, 'Loaded kernel module {0}'.format(loaded['yes'][0])) - elif len(loaded['yes']) > 1: - _append_comment(ret, 'Loaded kernel modules {0}'.format(', '.join(loaded['yes']))) + if len(loaded["yes"]) == 1: + _append_comment(ret, "Loaded kernel module {0}".format(loaded["yes"][0])) + elif len(loaded["yes"]) > 1: + _append_comment( + ret, "Loaded kernel modules {0}".format(", ".join(loaded["yes"])) + ) - if len(loaded['no']) == 1: - _append_comment(ret, 'Failed to load kernel module {0}'.format(loaded['no'][0])) - if len(loaded['no']) > 1: - _append_comment(ret, 'Failed to load kernel modules {0}'.format(', '.join(loaded['no']))) + if len(loaded["no"]) == 1: + _append_comment(ret, "Failed to load kernel module {0}".format(loaded["no"][0])) + if len(loaded["no"]) > 1: + _append_comment( + ret, "Failed to load kernel modules {0}".format(", ".join(loaded["no"])) + ) - if loaded['failed']: - for mod, msg in loaded['failed']: - _append_comment(ret, 'Failed to load kernel module {0}: {1}'.format(mod, msg)) + if loaded["failed"]: + for mod, msg in loaded["failed"]: + _append_comment( + ret, "Failed to load kernel module {0}: {1}".format(mod, msg) + ) return ret def absent(name, persist=False, comment=True, mods=None): - ''' + """ Verify that the named kernel module is not loaded name @@ -178,68 +189,83 @@ def absent(name, persist=False, comment=True, mods=None): becomes a placeholder .. versionadded:: 2016.3.0 - ''' + """ if not isinstance(mods, (list, tuple)): mods = [name] - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - loaded_mods = __salt__['kmod.mod_list']() + loaded_mods = __salt__["kmod.mod_list"]() if persist: - persist_mods = __salt__['kmod.mod_list'](True) + persist_mods = __salt__["kmod.mod_list"](True) # Union of loaded modules and persistent modules loaded_mods = list(set(loaded_mods) | set(persist_mods)) # Intersection of proposed modules and loaded modules to_unload = list(set(mods) & set(loaded_mods)) if to_unload: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if len(to_unload) == 1: - _append_comment(ret, 'Kernel module {0} is set to be removed'.format(to_unload[0])) + _append_comment( + ret, "Kernel module {0} is set to be removed".format(to_unload[0]) + ) elif len(to_unload) > 1: - _append_comment(ret, 'Kernel modules {0} are set to be removed'.format(', '.join(to_unload))) + _append_comment( + ret, + "Kernel modules {0} are set to be removed".format( + ", ".join(to_unload) + ), + ) return ret # Unload modules and collect results - unloaded = {'yes': [], 'no': [], 'failed': []} + unloaded = {"yes": [], "no": [], "failed": []} for mod in to_unload: - unload_result = __salt__['kmod.remove'](mod, persist, comment) + unload_result = __salt__["kmod.remove"](mod, persist, comment) if isinstance(unload_result, (list, tuple)): if len(unload_result) > 0: for module in unload_result: - ret['changes'][module] = 'removed' - unloaded['yes'].append(mod) + ret["changes"][module] = "removed" + unloaded["yes"].append(mod) else: - ret['result'] = False - unloaded['no'].append(mod) + ret["result"] = False + unloaded["no"].append(mod) else: - ret['result'] = False - unloaded['failed'].append([mod, unload_result]) + ret["result"] = False + unloaded["failed"].append([mod, unload_result]) # Update comment with results - if len(unloaded['yes']) == 1: - _append_comment(ret, 'Removed kernel module {0}'.format(unloaded['yes'][0])) - elif len(unloaded['yes']) > 1: - _append_comment(ret, 'Removed kernel modules {0}'.format(', '.join(unloaded['yes']))) + if len(unloaded["yes"]) == 1: + _append_comment(ret, "Removed kernel module {0}".format(unloaded["yes"][0])) + elif len(unloaded["yes"]) > 1: + _append_comment( + ret, "Removed kernel modules {0}".format(", ".join(unloaded["yes"])) + ) - if len(unloaded['no']) == 1: - _append_comment(ret, 'Failed to remove kernel module {0}'.format(unloaded['no'][0])) - if len(unloaded['no']) > 1: - _append_comment(ret, 'Failed to remove kernel modules {0}'.format(', '.join(unloaded['no']))) + if len(unloaded["no"]) == 1: + _append_comment( + ret, "Failed to remove kernel module {0}".format(unloaded["no"][0]) + ) + if len(unloaded["no"]) > 1: + _append_comment( + ret, + "Failed to remove kernel modules {0}".format(", ".join(unloaded["no"])), + ) - if unloaded['failed']: - for mod, msg in unloaded['failed']: - _append_comment(ret, 'Failed to remove kernel module {0}: {1}'.format(mod, msg)) + if unloaded["failed"]: + for mod, msg in unloaded["failed"]: + _append_comment( + ret, "Failed to remove kernel module {0}: {1}".format(mod, msg) + ) return ret else: if len(mods) == 1: - ret['comment'] = 'Kernel module {0} is already removed'.format(mods[0]) + ret["comment"] = "Kernel module {0} is already removed".format(mods[0]) else: - ret['comment'] = 'Kernel modules {0} are already removed'.format(', '.join(mods)) + ret["comment"] = "Kernel modules {0} are already removed".format( + ", ".join(mods) + ) return ret diff --git a/salt/states/kubernetes.py b/salt/states/kubernetes.py index 2e78a0dbd5c..8b4a6875f44 100644 --- a/salt/states/kubernetes.py +++ b/salt/states/kubernetes.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage kubernetes resources as salt states ========================================== @@ -79,7 +79,7 @@ The kubernetes module is used to manage different kubernetes resources. key3: value3 .. versionadded: 2017.7.0 -''' +""" from __future__ import absolute_import import copy @@ -92,24 +92,26 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the kubernetes module is available in __salt__ - ''' - return 'kubernetes.ping' in __salt__ + """ + if "kubernetes.ping" in __salt__: + return True + return (False, "kubernetes module could not be loaded") def _error(ret, err_msg): - ''' + """ Helper function to propagate errors to the end user. - ''' - ret['result'] = False - ret['comment'] = err_msg + """ + ret["result"] = False + ret["comment"] = err_msg return ret -def deployment_absent(name, namespace='default', **kwargs): - ''' +def deployment_absent(name, namespace="default", **kwargs): + """ Ensures that the named deployment is absent from the given namespace. name @@ -117,47 +119,43 @@ def deployment_absent(name, namespace='default', **kwargs): namespace The name of the namespace - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - deployment = __salt__['kubernetes.show_deployment'](name, namespace, **kwargs) + deployment = __salt__["kubernetes.show_deployment"](name, namespace, **kwargs) if deployment is None: - ret['result'] = True if not __opts__['test'] else None - ret['comment'] = 'The deployment does not exist' + ret["result"] = True if not __opts__["test"] else None + ret["comment"] = "The deployment does not exist" return ret - if __opts__['test']: - ret['comment'] = 'The deployment is going to be deleted' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The deployment is going to be deleted" + ret["result"] = None return ret - res = __salt__['kubernetes.delete_deployment'](name, namespace, **kwargs) - if res['code'] == 200: - ret['result'] = True - ret['changes'] = { - 'kubernetes.deployment': { - 'new': 'absent', 'old': 'present'}} - ret['comment'] = res['message'] + res = __salt__["kubernetes.delete_deployment"](name, namespace, **kwargs) + if res["code"] == 200: + ret["result"] = True + ret["changes"] = {"kubernetes.deployment": {"new": "absent", "old": "present"}} + ret["comment"] = res["message"] else: - ret['comment'] = 'Something went wrong, response: {0}'.format(res) + ret["comment"] = "Something went wrong, response: {0}".format(res) return ret def deployment_present( - name, - namespace='default', - metadata=None, - spec=None, - source='', - template='', - **kwargs): - ''' + name, + namespace="default", + metadata=None, + spec=None, + source="", + template="", + **kwargs +): + """ Ensures that the named deployment is present inside of the specified namespace with the given metadata and spec. If the deployment exists it will be replaced. @@ -181,17 +179,12 @@ def deployment_present( template Template engine to be used to render the source file. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if (metadata or spec) and source: return _error( - ret, - '\'source\' cannot be used in combination with \'metadata\' or ' - '\'spec\'' + ret, "'source' cannot be used in combination with 'metadata' or " "'spec'" ) if metadata is None: @@ -200,33 +193,14 @@ def deployment_present( if spec is None: spec = {} - deployment = __salt__['kubernetes.show_deployment'](name, namespace, **kwargs) + deployment = __salt__["kubernetes.show_deployment"](name, namespace, **kwargs) if deployment is None: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The deployment is going to be created' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The deployment is going to be created" return ret - res = __salt__['kubernetes.create_deployment'](name=name, - namespace=namespace, - metadata=metadata, - spec=spec, - source=source, - template=template, - saltenv=__env__, - **kwargs) - ret['changes']['{0}.{1}'.format(namespace, name)] = { - 'old': {}, - 'new': res} - else: - if __opts__['test']: - ret['result'] = None - return ret - - # TODO: improve checks # pylint: disable=fixme - log.info('Forcing the recreation of the deployment') - ret['comment'] = 'The deployment is already present. Forcing recreation' - res = __salt__['kubernetes.replace_deployment']( + res = __salt__["kubernetes.create_deployment"]( name=name, namespace=namespace, metadata=metadata, @@ -234,25 +208,43 @@ def deployment_present( source=source, template=template, saltenv=__env__, - **kwargs) + **kwargs + ) + ret["changes"]["{0}.{1}".format(namespace, name)] = {"old": {}, "new": res} + else: + if __opts__["test"]: + ret["result"] = None + return ret - ret['changes'] = { - 'metadata': metadata, - 'spec': spec - } - ret['result'] = True + # TODO: improve checks # pylint: disable=fixme + log.info("Forcing the recreation of the deployment") + ret["comment"] = "The deployment is already present. Forcing recreation" + res = __salt__["kubernetes.replace_deployment"]( + name=name, + namespace=namespace, + metadata=metadata, + spec=spec, + source=source, + template=template, + saltenv=__env__, + **kwargs + ) + + ret["changes"] = {"metadata": metadata, "spec": spec} + ret["result"] = True return ret def service_present( - name, - namespace='default', - metadata=None, - spec=None, - source='', - template='', - **kwargs): - ''' + name, + namespace="default", + metadata=None, + spec=None, + source="", + template="", + **kwargs +): + """ Ensures that the named service is present inside of the specified namespace with the given metadata and spec. If the deployment exists it will be replaced. @@ -276,17 +268,12 @@ def service_present( template Template engine to be used to render the source file. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if (metadata or spec) and source: return _error( - ret, - '\'source\' cannot be used in combination with \'metadata\' or ' - '\'spec\'' + ret, "'source' cannot be used in combination with 'metadata' or " "'spec'" ) if metadata is None: @@ -295,33 +282,33 @@ def service_present( if spec is None: spec = {} - service = __salt__['kubernetes.show_service'](name, namespace, **kwargs) + service = __salt__["kubernetes.show_service"](name, namespace, **kwargs) if service is None: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The service is going to be created' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The service is going to be created" return ret - res = __salt__['kubernetes.create_service'](name=name, - namespace=namespace, - metadata=metadata, - spec=spec, - source=source, - template=template, - saltenv=__env__, - **kwargs) - ret['changes']['{0}.{1}'.format(namespace, name)] = { - 'old': {}, - 'new': res} + res = __salt__["kubernetes.create_service"]( + name=name, + namespace=namespace, + metadata=metadata, + spec=spec, + source=source, + template=template, + saltenv=__env__, + **kwargs + ) + ret["changes"]["{0}.{1}".format(namespace, name)] = {"old": {}, "new": res} else: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # TODO: improve checks # pylint: disable=fixme - log.info('Forcing the recreation of the service') - ret['comment'] = 'The service is already present. Forcing recreation' - res = __salt__['kubernetes.replace_service']( + log.info("Forcing the recreation of the service") + ret["comment"] = "The service is already present. Forcing recreation" + res = __salt__["kubernetes.replace_service"]( name=name, namespace=namespace, metadata=metadata, @@ -330,18 +317,16 @@ def service_present( template=template, old_service=service, saltenv=__env__, - **kwargs) + **kwargs + ) - ret['changes'] = { - 'metadata': metadata, - 'spec': spec - } - ret['result'] = True + ret["changes"] = {"metadata": metadata, "spec": spec} + ret["result"] = True return ret -def service_absent(name, namespace='default', **kwargs): - ''' +def service_absent(name, namespace="default", **kwargs): + """ Ensures that the named service is absent from the given namespace. name @@ -349,123 +334,106 @@ def service_absent(name, namespace='default', **kwargs): namespace The name of the namespace - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - service = __salt__['kubernetes.show_service'](name, namespace, **kwargs) + service = __salt__["kubernetes.show_service"](name, namespace, **kwargs) if service is None: - ret['result'] = True if not __opts__['test'] else None - ret['comment'] = 'The service does not exist' + ret["result"] = True if not __opts__["test"] else None + ret["comment"] = "The service does not exist" return ret - if __opts__['test']: - ret['comment'] = 'The service is going to be deleted' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The service is going to be deleted" + ret["result"] = None return ret - res = __salt__['kubernetes.delete_service'](name, namespace, **kwargs) - if res['code'] == 200: - ret['result'] = True - ret['changes'] = { - 'kubernetes.service': { - 'new': 'absent', 'old': 'present'}} - ret['comment'] = res['message'] + res = __salt__["kubernetes.delete_service"](name, namespace, **kwargs) + if res["code"] == 200: + ret["result"] = True + ret["changes"] = {"kubernetes.service": {"new": "absent", "old": "present"}} + ret["comment"] = res["message"] else: - ret['comment'] = 'Something went wrong, response: {0}'.format(res) + ret["comment"] = "Something went wrong, response: {0}".format(res) return ret def namespace_absent(name, **kwargs): - ''' + """ Ensures that the named namespace is absent. name The name of the namespace - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - namespace = __salt__['kubernetes.show_namespace'](name, **kwargs) + namespace = __salt__["kubernetes.show_namespace"](name, **kwargs) if namespace is None: - ret['result'] = True if not __opts__['test'] else None - ret['comment'] = 'The namespace does not exist' + ret["result"] = True if not __opts__["test"] else None + ret["comment"] = "The namespace does not exist" return ret - if __opts__['test']: - ret['comment'] = 'The namespace is going to be deleted' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The namespace is going to be deleted" + ret["result"] = None return ret - res = __salt__['kubernetes.delete_namespace'](name, **kwargs) + res = __salt__["kubernetes.delete_namespace"](name, **kwargs) if ( - res['code'] == 200 or - ( - isinstance(res['status'], six.string_types) and - 'Terminating' in res['status'] - ) or - ( - isinstance(res['status'], dict) and - res['status']['phase'] == 'Terminating' - )): - ret['result'] = True - ret['changes'] = { - 'kubernetes.namespace': { - 'new': 'absent', 'old': 'present'}} - if res['message']: - ret['comment'] = res['message'] + res["code"] == 200 + or ( + isinstance(res["status"], six.string_types) + and "Terminating" in res["status"] + ) + or (isinstance(res["status"], dict) and res["status"]["phase"] == "Terminating") + ): + ret["result"] = True + ret["changes"] = {"kubernetes.namespace": {"new": "absent", "old": "present"}} + if res["message"]: + ret["comment"] = res["message"] else: - ret['comment'] = 'Terminating' + ret["comment"] = "Terminating" else: - ret['comment'] = 'Something went wrong, response: {0}'.format(res) + ret["comment"] = "Something went wrong, response: {0}".format(res) return ret def namespace_present(name, **kwargs): - ''' + """ Ensures that the named namespace is present. name The name of the namespace. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - namespace = __salt__['kubernetes.show_namespace'](name, **kwargs) + namespace = __salt__["kubernetes.show_namespace"](name, **kwargs) if namespace is None: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The namespace is going to be created' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The namespace is going to be created" return ret - res = __salt__['kubernetes.create_namespace'](name, **kwargs) - ret['result'] = True - ret['changes']['namespace'] = { - 'old': {}, - 'new': res} + res = __salt__["kubernetes.create_namespace"](name, **kwargs) + ret["result"] = True + ret["changes"]["namespace"] = {"old": {}, "new": res} else: - ret['result'] = True if not __opts__['test'] else None - ret['comment'] = 'The namespace already exists' + ret["result"] = True if not __opts__["test"] else None + ret["comment"] = "The namespace already exists" return ret -def secret_absent(name, namespace='default', **kwargs): - ''' +def secret_absent(name, namespace="default", **kwargs): + """ Ensures that the named secret is absent from the given namespace. name @@ -473,46 +441,37 @@ def secret_absent(name, namespace='default', **kwargs): namespace The name of the namespace - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - secret = __salt__['kubernetes.show_secret'](name, namespace, **kwargs) + secret = __salt__["kubernetes.show_secret"](name, namespace, **kwargs) if secret is None: - ret['result'] = True if not __opts__['test'] else None - ret['comment'] = 'The secret does not exist' + ret["result"] = True if not __opts__["test"] else None + ret["comment"] = "The secret does not exist" return ret - if __opts__['test']: - ret['comment'] = 'The secret is going to be deleted' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The secret is going to be deleted" + ret["result"] = None return ret - __salt__['kubernetes.delete_secret'](name, namespace, **kwargs) + __salt__["kubernetes.delete_secret"](name, namespace, **kwargs) # As for kubernetes 1.6.4 doesn't set a code when deleting a secret # The kubernetes module will raise an exception if the kubernetes # server will return an error - ret['result'] = True - ret['changes'] = { - 'kubernetes.secret': { - 'new': 'absent', 'old': 'present'}} - ret['comment'] = 'Secret deleted' + ret["result"] = True + ret["changes"] = {"kubernetes.secret": {"new": "absent", "old": "present"}} + ret["comment"] = "Secret deleted" return ret def secret_present( - name, - namespace='default', - data=None, - source=None, - template=None, - **kwargs): - ''' + name, namespace="default", data=None, source=None, template=None, **kwargs +): + """ Ensures that the named secret is present inside of the specified namespace with the given data. If the secret exists it will be replaced. @@ -532,68 +491,63 @@ def secret_present( template Template engine to be used to render the source file. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if data and source: - return _error( - ret, - '\'source\' cannot be used in combination with \'data\'' - ) + return _error(ret, "'source' cannot be used in combination with 'data'") - secret = __salt__['kubernetes.show_secret'](name, namespace, **kwargs) + secret = __salt__["kubernetes.show_secret"](name, namespace, **kwargs) if secret is None: if data is None: data = {} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The secret is going to be created' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The secret is going to be created" return ret - res = __salt__['kubernetes.create_secret'](name=name, - namespace=namespace, - data=data, - source=source, - template=template, - saltenv=__env__, - **kwargs) - ret['changes']['{0}.{1}'.format(namespace, name)] = { - 'old': {}, - 'new': res} - else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The secret is going to be replaced' - return ret - - # TODO: improve checks # pylint: disable=fixme - log.info('Forcing the recreation of the service') - ret['comment'] = 'The secret is already present. Forcing recreation' - res = __salt__['kubernetes.replace_secret']( + res = __salt__["kubernetes.create_secret"]( name=name, namespace=namespace, data=data, source=source, template=template, saltenv=__env__, - **kwargs) + **kwargs + ) + ret["changes"]["{0}.{1}".format(namespace, name)] = {"old": {}, "new": res} + else: + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The secret is going to be replaced" + return ret - ret['changes'] = { + # TODO: improve checks # pylint: disable=fixme + log.info("Forcing the recreation of the service") + ret["comment"] = "The secret is already present. Forcing recreation" + res = __salt__["kubernetes.replace_secret"]( + name=name, + namespace=namespace, + data=data, + source=source, + template=template, + saltenv=__env__, + **kwargs + ) + + ret["changes"] = { # Omit values from the return. They are unencrypted # and can contain sensitive data. - 'data': list(res['data']) + "data": list(res["data"]) } - ret['result'] = True + ret["result"] = True return ret -def configmap_absent(name, namespace='default', **kwargs): - ''' +def configmap_absent(name, namespace="default", **kwargs): + """ Ensures that the named configmap is absent from the given namespace. name @@ -602,46 +556,37 @@ def configmap_absent(name, namespace='default', **kwargs): namespace The namespace holding the configmap. The 'default' one is going to be used unless a different one is specified. - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - configmap = __salt__['kubernetes.show_configmap'](name, namespace, **kwargs) + configmap = __salt__["kubernetes.show_configmap"](name, namespace, **kwargs) if configmap is None: - ret['result'] = True if not __opts__['test'] else None - ret['comment'] = 'The configmap does not exist' + ret["result"] = True if not __opts__["test"] else None + ret["comment"] = "The configmap does not exist" return ret - if __opts__['test']: - ret['comment'] = 'The configmap is going to be deleted' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The configmap is going to be deleted" + ret["result"] = None return ret - __salt__['kubernetes.delete_configmap'](name, namespace, **kwargs) + __salt__["kubernetes.delete_configmap"](name, namespace, **kwargs) # As for kubernetes 1.6.4 doesn't set a code when deleting a configmap # The kubernetes module will raise an exception if the kubernetes # server will return an error - ret['result'] = True - ret['changes'] = { - 'kubernetes.configmap': { - 'new': 'absent', 'old': 'present'}} - ret['comment'] = 'ConfigMap deleted' + ret["result"] = True + ret["changes"] = {"kubernetes.configmap": {"new": "absent", "old": "present"}} + ret["comment"] = "ConfigMap deleted" return ret def configmap_present( - name, - namespace='default', - data=None, - source=None, - template=None, - **kwargs): - ''' + name, namespace="default", data=None, source=None, template=None, **kwargs +): + """ Ensures that the named configmap is present inside of the specified namespace with the given data. If the configmap exists it will be replaced. @@ -661,64 +606,57 @@ def configmap_present( template Template engine to be used to render the source file. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if data and source: - return _error( - ret, - '\'source\' cannot be used in combination with \'data\'' - ) + return _error(ret, "'source' cannot be used in combination with 'data'") elif data is None: data = {} - configmap = __salt__['kubernetes.show_configmap'](name, namespace, **kwargs) + configmap = __salt__["kubernetes.show_configmap"](name, namespace, **kwargs) if configmap is None: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The configmap is going to be created' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The configmap is going to be created" return ret - res = __salt__['kubernetes.create_configmap'](name=name, - namespace=namespace, - data=data, - source=source, - template=template, - saltenv=__env__, - **kwargs) - ret['changes']['{0}.{1}'.format(namespace, name)] = { - 'old': {}, - 'new': res} - else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The configmap is going to be replaced' - return ret - - # TODO: improve checks # pylint: disable=fixme - log.info('Forcing the recreation of the service') - ret['comment'] = 'The configmap is already present. Forcing recreation' - res = __salt__['kubernetes.replace_configmap']( + res = __salt__["kubernetes.create_configmap"]( name=name, namespace=namespace, data=data, source=source, template=template, saltenv=__env__, - **kwargs) + **kwargs + ) + ret["changes"]["{0}.{1}".format(namespace, name)] = {"old": {}, "new": res} + else: + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The configmap is going to be replaced" + return ret - ret['changes'] = { - 'data': res['data'] - } - ret['result'] = True + # TODO: improve checks # pylint: disable=fixme + log.info("Forcing the recreation of the service") + ret["comment"] = "The configmap is already present. Forcing recreation" + res = __salt__["kubernetes.replace_configmap"]( + name=name, + namespace=namespace, + data=data, + source=source, + template=template, + saltenv=__env__, + **kwargs + ) + + ret["changes"] = {"data": res["data"]} + ret["result"] = True return ret -def pod_absent(name, namespace='default', **kwargs): - ''' +def pod_absent(name, namespace="default", **kwargs): + """ Ensures that the named pod is absent from the given namespace. name @@ -726,50 +664,46 @@ def pod_absent(name, namespace='default', **kwargs): namespace The name of the namespace - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - pod = __salt__['kubernetes.show_pod'](name, namespace, **kwargs) + pod = __salt__["kubernetes.show_pod"](name, namespace, **kwargs) if pod is None: - ret['result'] = True if not __opts__['test'] else None - ret['comment'] = 'The pod does not exist' + ret["result"] = True if not __opts__["test"] else None + ret["comment"] = "The pod does not exist" return ret - if __opts__['test']: - ret['comment'] = 'The pod is going to be deleted' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The pod is going to be deleted" + ret["result"] = None return ret - res = __salt__['kubernetes.delete_pod'](name, namespace, **kwargs) - if res['code'] == 200 or res['code'] is None: - ret['result'] = True - ret['changes'] = { - 'kubernetes.pod': { - 'new': 'absent', 'old': 'present'}} - if res['code'] is None: - ret['comment'] = 'In progress' + res = __salt__["kubernetes.delete_pod"](name, namespace, **kwargs) + if res["code"] == 200 or res["code"] is None: + ret["result"] = True + ret["changes"] = {"kubernetes.pod": {"new": "absent", "old": "present"}} + if res["code"] is None: + ret["comment"] = "In progress" else: - ret['comment'] = res['message'] + ret["comment"] = res["message"] else: - ret['comment'] = 'Something went wrong, response: {0}'.format(res) + ret["comment"] = "Something went wrong, response: {0}".format(res) return ret def pod_present( - name, - namespace='default', - metadata=None, - spec=None, - source='', - template='', - **kwargs): - ''' + name, + namespace="default", + metadata=None, + spec=None, + source="", + template="", + **kwargs +): + """ Ensures that the named pod is present inside of the specified namespace with the given metadata and spec. If the pod exists it will be replaced. @@ -793,17 +727,12 @@ def pod_present( template Template engine to be used to render the source file. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if (metadata or spec) and source: return _error( - ret, - '\'source\' cannot be used in combination with \'metadata\' or ' - '\'spec\'' + ret, "'source' cannot be used in combination with 'metadata' or " "'spec'" ) if metadata is None: @@ -812,46 +741,45 @@ def pod_present( if spec is None: spec = {} - pod = __salt__['kubernetes.show_pod'](name, namespace, **kwargs) + pod = __salt__["kubernetes.show_pod"](name, namespace, **kwargs) if pod is None: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The pod is going to be created' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The pod is going to be created" return ret - res = __salt__['kubernetes.create_pod'](name=name, - namespace=namespace, - metadata=metadata, - spec=spec, - source=source, - template=template, - saltenv=__env__, - **kwargs) - ret['changes']['{0}.{1}'.format(namespace, name)] = { - 'old': {}, - 'new': res} + res = __salt__["kubernetes.create_pod"]( + name=name, + namespace=namespace, + metadata=metadata, + spec=spec, + source=source, + template=template, + saltenv=__env__, + **kwargs + ) + ret["changes"]["{0}.{1}".format(namespace, name)] = {"old": {}, "new": res} else: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # TODO: fix replace_namespaced_pod validation issues - ret['comment'] = 'salt is currently unable to replace a pod without ' \ - 'deleting it. Please perform the removal of the pod requiring ' \ - 'the \'pod_absent\' state if this is the desired behaviour.' - ret['result'] = False + ret["comment"] = ( + "salt is currently unable to replace a pod without " + "deleting it. Please perform the removal of the pod requiring " + "the 'pod_absent' state if this is the desired behaviour." + ) + ret["result"] = False return ret - ret['changes'] = { - 'metadata': metadata, - 'spec': spec - } - ret['result'] = True + ret["changes"] = {"metadata": metadata, "spec": spec} + ret["result"] = True return ret def node_label_absent(name, node, **kwargs): - ''' + """ Ensures that the named label is absent from the node. name @@ -859,41 +787,33 @@ def node_label_absent(name, node, **kwargs): node The name of the node - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - labels = __salt__['kubernetes.node_labels'](node, **kwargs) + labels = __salt__["kubernetes.node_labels"](node, **kwargs) if name not in labels: - ret['result'] = True if not __opts__['test'] else None - ret['comment'] = 'The label does not exist' + ret["result"] = True if not __opts__["test"] else None + ret["comment"] = "The label does not exist" return ret - if __opts__['test']: - ret['comment'] = 'The label is going to be deleted' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The label is going to be deleted" + ret["result"] = None return ret - __salt__['kubernetes.node_remove_label']( - node_name=node, - label_name=name, - **kwargs) + __salt__["kubernetes.node_remove_label"](node_name=node, label_name=name, **kwargs) - ret['result'] = True - ret['changes'] = { - 'kubernetes.node_label': { - 'new': 'absent', 'old': 'present'}} - ret['comment'] = 'Label removed from node' + ret["result"] = True + ret["changes"] = {"kubernetes.node_label": {"new": "absent", "old": "present"}} + ret["comment"] = "Label removed from node" return ret def node_label_folder_absent(name, node, **kwargs): - ''' + """ Ensures the label folder doesn't exist on the specified node. name @@ -901,13 +821,10 @@ def node_label_folder_absent(name, node, **kwargs): node The name of the node - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - labels = __salt__['kubernetes.node_labels'](node, **kwargs) + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + labels = __salt__["kubernetes.node_labels"](node, **kwargs) folder = name.strip("/") + "/" labels_to_drop = [] @@ -919,39 +836,31 @@ def node_label_folder_absent(name, node, **kwargs): new_labels.append(label) if not labels_to_drop: - ret['result'] = True if not __opts__['test'] else None - ret['comment'] = 'The label folder does not exist' + ret["result"] = True if not __opts__["test"] else None + ret["comment"] = "The label folder does not exist" return ret - if __opts__['test']: - ret['comment'] = 'The label folder is going to be deleted' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The label folder is going to be deleted" + ret["result"] = None return ret for label in labels_to_drop: - __salt__['kubernetes.node_remove_label']( - node_name=node, - label_name=label, - **kwargs) + __salt__["kubernetes.node_remove_label"]( + node_name=node, label_name=label, **kwargs + ) - ret['result'] = True - ret['changes'] = { - 'kubernetes.node_label_folder_absent': { - 'old': list(labels), - 'new': new_labels, - } + ret["result"] = True + ret["changes"] = { + "kubernetes.node_label_folder_absent": {"old": list(labels), "new": new_labels} } - ret['comment'] = 'Label folder removed from node' + ret["comment"] = "Label folder removed from node" return ret -def node_label_present( - name, - node, - value, - **kwargs): - ''' +def node_label_present(name, node, value, **kwargs): + """ Ensures that the named label is set on the named node with the given value. If the label exists it will be replaced. @@ -964,46 +873,38 @@ def node_label_present( node Node to change. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - labels = __salt__['kubernetes.node_labels'](node, **kwargs) + labels = __salt__["kubernetes.node_labels"](node, **kwargs) if name not in labels: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The label is going to be set' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The label is going to be set" return ret - __salt__['kubernetes.node_add_label'](label_name=name, - label_value=value, - node_name=node, - **kwargs) + __salt__["kubernetes.node_add_label"]( + label_name=name, label_value=value, node_name=node, **kwargs + ) elif labels[name] == value: - ret['result'] = True - ret['comment'] = 'The label is already set and has the specified value' + ret["result"] = True + ret["comment"] = "The label is already set and has the specified value" return ret else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The label is going to be updated' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The label is going to be updated" return ret - ret['comment'] = 'The label is already set, changing the value' - __salt__['kubernetes.node_add_label']( - node_name=node, - label_name=name, - label_value=value, - **kwargs) + ret["comment"] = "The label is already set, changing the value" + __salt__["kubernetes.node_add_label"]( + node_name=node, label_name=name, label_value=value, **kwargs + ) old_labels = copy.copy(labels) labels[name] = value - ret['changes']['{0}.{1}'.format(node, name)] = { - 'old': old_labels, - 'new': labels} - ret['result'] = True + ret["changes"]["{0}.{1}".format(node, name)] = {"old": old_labels, "new": labels} + ret["result"] = True return ret diff --git a/salt/states/layman.py b/salt/states/layman.py index c45cbc52985..b67519b07a5 100644 --- a/salt/states/layman.py +++ b/salt/states/layman.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Gentoo Overlays using layman ========================================== @@ -9,88 +9,84 @@ A state module to manage Gentoo package overlays via layman sunrise: layman.present -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the layman module is available in __salt__ - ''' - return 'layman' if 'layman.add' in __salt__ else False + """ + if "layman.add" in __salt__: + return "layman" + return (False, "layman module could not be loaded") def present(name): - ''' + """ Verify that the overlay is present name The name of the overlay to add - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} # Overlay already present - if name in __salt__['layman.list_local'](): - ret['comment'] = 'Overlay {0} already present'.format(name) - elif __opts__['test']: - ret['comment'] = 'Overlay {0} is set to be added'.format(name) - ret['result'] = None + if name in __salt__["layman.list_local"](): + ret["comment"] = "Overlay {0} already present".format(name) + elif __opts__["test"]: + ret["comment"] = "Overlay {0} is set to be added".format(name) + ret["result"] = None return ret else: # Does the overlay exist? - if name not in __salt__['layman.list_all'](): - ret['comment'] = 'Overlay {0} not found'.format(name) - ret['result'] = False + if name not in __salt__["layman.list_all"](): + ret["comment"] = "Overlay {0} not found".format(name) + ret["result"] = False else: # Attempt to add the overlay - changes = __salt__['layman.add'](name) + changes = __salt__["layman.add"](name) # The overlay failed to add if len(changes) < 1: - ret['comment'] = 'Overlay {0} failed to add'.format(name) - ret['result'] = False + ret["comment"] = "Overlay {0} failed to add".format(name) + ret["result"] = False # Success else: - ret['changes']['added'] = changes - ret['comment'] = 'Overlay {0} added.'.format(name) + ret["changes"]["added"] = changes + ret["comment"] = "Overlay {0} added.".format(name) return ret def absent(name): - ''' + """ Verify that the overlay is absent name The name of the overlay to delete - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} # Overlay is already absent - if name not in __salt__['layman.list_local'](): - ret['comment'] = 'Overlay {0} already absent'.format(name) - elif __opts__['test']: - ret['comment'] = 'Overlay {0} is set to be deleted'.format(name) - ret['result'] = None + if name not in __salt__["layman.list_local"](): + ret["comment"] = "Overlay {0} already absent".format(name) + elif __opts__["test"]: + ret["comment"] = "Overlay {0} is set to be deleted".format(name) + ret["result"] = None return ret else: # Attempt to delete the overlay - changes = __salt__['layman.delete'](name) + changes = __salt__["layman.delete"](name) # The overlay failed to delete if len(changes) < 1: - ret['comment'] = 'Overlay {0} failed to delete'.format(name) - ret['result'] = False + ret["comment"] = "Overlay {0} failed to delete".format(name) + ret["result"] = False # Success else: - ret['changes']['deleted'] = changes - ret['comment'] = 'Overlay {0} deleted.'.format(name) + ret["changes"]["deleted"] = changes + ret["comment"] = "Overlay {0} deleted.".format(name) return ret diff --git a/salt/states/ldap.py b/salt/states/ldap.py index e4574742c13..cf61261c6d3 100644 --- a/salt/states/ldap.py +++ b/salt/states/ldap.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage entries in an LDAP database ================================== @@ -7,10 +7,11 @@ Manage entries in an LDAP database The ``states.ldap`` state module allows you to manage LDAP entries and their attributes. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import inspect import logging @@ -24,7 +25,7 @@ log = logging.getLogger(__name__) def managed(name, entries, connect_spec=None): - '''Ensure the existence (or not) of LDAP entries and their attributes + """Ensure the existence (or not) of LDAP entries and their attributes Example: @@ -241,16 +242,16 @@ def managed(name, entries, connect_spec=None): * ``False`` if at least one change was unable to be applied. * ``None`` if changes would be applied but it is in test mode. - ''' + """ if connect_spec is None: connect_spec = {} try: - connect_spec.setdefault('url', name) + connect_spec.setdefault("url", name) except AttributeError: # already a connection object pass - connect = __salt__['ldap3.connect'] + connect = __salt__["ldap3.connect"] # hack to get at the ldap3 module to access the ldap3.LDAPError # exception class. https://github.com/saltstack/salt/issues/27578 @@ -289,19 +290,19 @@ def managed(name, entries, connect_spec=None): del dn_set[dn] ret = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': '', + "name": name, + "changes": {}, + "result": None, + "comment": "", } if old == new: - ret['comment'] = 'LDAP entries already set' - ret['result'] = True + ret["comment"] = "LDAP entries already set" + ret["result"] = True return ret - if __opts__['test']: - ret['comment'] = 'Would change LDAP entries' + if __opts__["test"]: + ret["comment"] = "Would change LDAP entries" changed_old = old changed_new = new success_dn_set = dn_set @@ -310,8 +311,8 @@ def managed(name, entries, connect_spec=None): changed_old = OrderedDict() changed_new = OrderedDict() # assume success; these will be changed on error - ret['result'] = True - ret['comment'] = 'Successfully updated LDAP entries' + ret["result"] = True + ret["comment"] = "Successfully updated LDAP entries" errs = [] success_dn_set = OrderedDict() for dn in dn_set: @@ -322,16 +323,16 @@ def managed(name, entries, connect_spec=None): # perform the operation if o: if n: - op = 'modify' + op = "modify" assert o != n - __salt__['ldap3.change'](l, dn, o, n) + __salt__["ldap3.change"](l, dn, o, n) else: - op = 'delete' - __salt__['ldap3.delete'](l, dn) + op = "delete" + __salt__["ldap3.delete"](l, dn) else: - op = 'add' + op = "add" assert n - __salt__['ldap3.add'](l, dn, n) + __salt__["ldap3.add"](l, dn, n) # update these after the op in case an exception # is raised @@ -339,15 +340,18 @@ def managed(name, entries, connect_spec=None): changed_new[dn] = n success_dn_set[dn] = True except ldap3.LDAPError as err: - log.exception('failed to %s entry %s (%s)', op, dn, err) + log.exception("failed to %s entry %s (%s)", op, dn, err) errs.append((op, dn, err)) continue if errs: - ret['result'] = False - ret['comment'] = 'failed to ' \ - + ', '.join((op + ' entry ' + dn + '(' + six.text_type(err) + ')' - for op, dn, err in errs)) + ret["result"] = False + ret["comment"] = "failed to " + ", ".join( + ( + op + " entry " + dn + "(" + six.text_type(err) + ")" + for op, dn, err in errs + ) + ) # set ret['changes']. filter out any unchanged attributes, and # convert the value sets to lists before returning them to the @@ -356,20 +360,24 @@ def managed(name, entries, connect_spec=None): o = changed_old.get(dn, {}) n = changed_new.get(dn, {}) changes = {} - ret['changes'][dn] = changes - for x, xn in ((o, 'old'), (n, 'new')): + ret["changes"][dn] = changes + for x, xn in ((o, "old"), (n, "new")): if not x: changes[xn] = None continue - changes[xn] = dict(((attr, sorted(vals)) - for attr, vals in six.iteritems(x) - if o.get(attr, ()) != n.get(attr, ()))) + changes[xn] = dict( + ( + (attr, sorted(vals)) + for attr, vals in six.iteritems(x) + if o.get(attr, ()) != n.get(attr, ()) + ) + ) return ret def _process_entries(l, entries): - '''Helper for managed() to process entries and return before/after views + """Helper for managed() to process entries and return before/after views Collect the current database state and update it according to the data in :py:func:`managed`'s ``entries`` parameter. Return the @@ -409,7 +417,7 @@ def _process_entries(l, entries): entries are processed in the user-specified order (in case there are dependencies, such as ACL rules specified in an early entry that make it possible to modify a later entry). - ''' + """ old = OrderedDict() new = OrderedDict() @@ -421,12 +429,16 @@ def _process_entries(l, entries): olde = new.get(dn, None) if olde is None: # next check the database - results = __salt__['ldap3.search'](l, dn, 'base') + results = __salt__["ldap3.search"](l, dn, "base") if len(results) == 1: attrs = results[dn] - olde = dict(((attr, OrderedSet(attrs[attr])) - for attr in attrs - if len(attrs[attr]))) + olde = dict( + ( + (attr, OrderedSet(attrs[attr])) + for attr in attrs + if len(attrs[attr]) + ) + ) else: # nothing, so it must be a brand new entry assert len(results) == 0 @@ -440,15 +452,15 @@ def _process_entries(l, entries): # process the directives entry_status = { - 'delete_others': False, - 'mentioned_attributes': set(), + "delete_others": False, + "mentioned_attributes": set(), } for directives in directives_seq: _update_entry(newe, entry_status, directives) - if entry_status['delete_others']: + if entry_status["delete_others"]: to_delete = set() for attr in newe: - if attr not in entry_status['mentioned_attributes']: + if attr not in entry_status["mentioned_attributes"]: to_delete.add(attr) for attr in to_delete: del newe[attr] @@ -456,7 +468,7 @@ def _process_entries(l, entries): def _update_entry(entry, status, directives): - '''Update an entry's attributes using the provided directives + """Update an entry's attributes using the provided directives :param entry: A dict mapping each attribute name to a set of its values @@ -465,37 +477,37 @@ def _update_entry(entry, status, directives): is True or not, and the set of mentioned attributes) :param directives: A dict mapping directive types to directive-specific state - ''' + """ for directive, state in six.iteritems(directives): - if directive == 'delete_others': - status['delete_others'] = state + if directive == "delete_others": + status["delete_others"] = state continue for attr, vals in six.iteritems(state): - status['mentioned_attributes'].add(attr) + status["mentioned_attributes"].add(attr) vals = _toset(vals) - if directive == 'default': + if directive == "default": if vals and (attr not in entry or not entry[attr]): entry[attr] = vals - elif directive == 'add': + elif directive == "add": vals.update(entry.get(attr, ())) if vals: entry[attr] = vals - elif directive == 'delete': + elif directive == "delete": existing_vals = entry.pop(attr, OrderedSet()) if vals: existing_vals -= vals if existing_vals: entry[attr] = existing_vals - elif directive == 'replace': + elif directive == "replace": entry.pop(attr, None) if vals: entry[attr] = vals else: - raise ValueError('unknown directive: ' + directive) + raise ValueError("unknown directive: " + directive) def _toset(thing): - '''helper to convert various things to a set + """helper to convert various things to a set This enables flexibility in what users provide as the list of LDAP entry attribute values. Note that the LDAP spec prohibits @@ -515,7 +527,7 @@ def _toset(thing): numbers, non-iterable objects, etc.) are added as the only member of a new set. - ''' + """ if thing is None: return OrderedSet() if isinstance(thing, six.string_types): diff --git a/salt/states/libcloud_dns.py b/salt/states/libcloud_dns.py index b670fa23eda..ab52a87de94 100644 --- a/salt/states/libcloud_dns.py +++ b/salt/states/libcloud_dns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage DNS records and zones using libcloud :codeauthor: Anthony Shaw <anthonyshaw@apache.org> @@ -45,7 +45,7 @@ Example: :depends: apache-libcloud -''' +""" # Import Python Libs from __future__ import absolute_import @@ -55,7 +55,9 @@ import salt.utils.compat def __virtual__(): - return True + if "libcloud_dns.list_zones" in __salt__: + return True + return (False, "libcloud_dns module could not be loaded") def __init__(opts): @@ -65,14 +67,11 @@ def __init__(opts): def state_result(result, message, name, changes=None): if changes is None: changes = {} - return {'result': result, - 'comment': message, - 'name': name, - 'changes': changes} + return {"result": result, "comment": message, "name": name, "changes": changes} def zone_present(domain, type, profile): - ''' + """ Ensures a record is present. :param domain: Zone name, i.e. the domain name @@ -83,20 +82,20 @@ def zone_present(domain, type, profile): :param profile: The profile key :type profile: ``str`` - ''' - zones = __salt__['libcloud_dns.list_zones'](profile) + """ + zones = __salt__["libcloud_dns.list_zones"](profile) if not type: - type = 'master' - matching_zone = [z for z in zones if z['domain'] == domain] + type = "master" + matching_zone = [z for z in zones if z["domain"] == domain] if len(matching_zone) > 0: - return state_result(True, 'Zone already exists', domain) + return state_result(True, "Zone already exists", domain) else: - result = __salt__['libcloud_dns.create_zone'](domain, profile, type) - return state_result(True, 'Created new zone', domain, result) + result = __salt__["libcloud_dns.create_zone"](domain, profile, type) + return state_result(True, "Created new zone", domain, result) def zone_absent(domain, profile): - ''' + """ Ensures a record is absent. :param domain: Zone name, i.e. the domain name @@ -104,18 +103,18 @@ def zone_absent(domain, profile): :param profile: The profile key :type profile: ``str`` - ''' - zones = __salt__['libcloud_dns.list_zones'](profile) - matching_zone = [z for z in zones if z['domain'] == domain] + """ + zones = __salt__["libcloud_dns.list_zones"](profile) + matching_zone = [z for z in zones if z["domain"] == domain] if len(matching_zone) == 0: - return state_result(True, 'Zone already absent', domain) + return state_result(True, "Zone already absent", domain) else: - result = __salt__['libcloud_dns.delete_zone'](matching_zone[0]['id'], profile) - return state_result(result, 'Deleted zone', domain) + result = __salt__["libcloud_dns.delete_zone"](matching_zone[0]["id"], profile) + return state_result(result, "Deleted zone", domain) def record_present(name, zone, type, data, profile): - ''' + """ Ensures a record is present. :param name: Record name without the domain name (e.g. www). @@ -135,28 +134,29 @@ def record_present(name, zone, type, data, profile): :param profile: The profile key :type profile: ``str`` - ''' - zones = __salt__['libcloud_dns.list_zones'](profile) + """ + zones = __salt__["libcloud_dns.list_zones"](profile) try: - matching_zone = [z for z in zones if z['domain'] == zone][0] + matching_zone = [z for z in zones if z["domain"] == zone][0] except IndexError: - return state_result(False, 'Could not locate zone', name) - records = __salt__['libcloud_dns.list_records'](matching_zone['id'], profile) - matching_records = [record for record in records - if record['name'] == name and - record['type'] == type and - record['data'] == data] + return state_result(False, "Could not locate zone", name) + records = __salt__["libcloud_dns.list_records"](matching_zone["id"], profile) + matching_records = [ + record + for record in records + if record["name"] == name and record["type"] == type and record["data"] == data + ] if len(matching_records) == 0: - result = __salt__['libcloud_dns.create_record']( - name, matching_zone['id'], - type, data, profile) - return state_result(True, 'Created new record', name, result) + result = __salt__["libcloud_dns.create_record"]( + name, matching_zone["id"], type, data, profile + ) + return state_result(True, "Created new record", name, result) else: - return state_result(True, 'Record already exists', name) + return state_result(True, "Record already exists", name) def record_absent(name, zone, type, data, profile): - ''' + """ Ensures a record is absent. :param name: Record name without the domain name (e.g. www). @@ -176,24 +176,28 @@ def record_absent(name, zone, type, data, profile): :param profile: The profile key :type profile: ``str`` - ''' - zones = __salt__['libcloud_dns.list_zones'](profile) + """ + zones = __salt__["libcloud_dns.list_zones"](profile) try: - matching_zone = [z for z in zones if z['domain'] == zone][0] + matching_zone = [z for z in zones if z["domain"] == zone][0] except IndexError: - return state_result(False, 'Zone could not be found', name) - records = __salt__['libcloud_dns.list_records'](matching_zone['id'], profile) - matching_records = [record for record in records - if record['name'] == name and - record['type'] == type and - record['data'] == data] + return state_result(False, "Zone could not be found", name) + records = __salt__["libcloud_dns.list_records"](matching_zone["id"], profile) + matching_records = [ + record + for record in records + if record["name"] == name and record["type"] == type and record["data"] == data + ] if len(matching_records) > 0: result = [] for record in matching_records: - result.append(__salt__['libcloud_dns.delete_record']( - matching_zone['id'], - record['id'], - profile)) - return state_result(all(result), 'Removed {0} records'.format(len(result)), name) + result.append( + __salt__["libcloud_dns.delete_record"]( + matching_zone["id"], record["id"], profile + ) + ) + return state_result( + all(result), "Removed {0} records".format(len(result)), name + ) else: - return state_result(True, 'Records already absent', name) + return state_result(True, "Records already absent", name) diff --git a/salt/states/libcloud_loadbalancer.py b/salt/states/libcloud_loadbalancer.py index f7017f4f4ad..f11cea112bf 100644 --- a/salt/states/libcloud_loadbalancer.py +++ b/salt/states/libcloud_loadbalancer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Apache Libcloud Load Balancer State =================================== @@ -44,10 +44,11 @@ Using States to deploy a load balancer with extended arguments to specify region - ex_region: us-east1 :depends: apache-libcloud -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -57,7 +58,9 @@ log = logging.getLogger(__name__) def __virtual__(): - return True + if "libcloud_loadbalancer.list_balancers" in __salt__: + return True + return (False, "libcloud_loadbalancer module could not be loaded") def __init__(opts): @@ -67,14 +70,13 @@ def __init__(opts): def state_result(result, message, name, changes=None): if changes is None: changes = {} - return {'result': result, - 'comment': message, - 'name': name, - 'changes': changes} + return {"result": result, "comment": message, "name": name, "changes": changes} -def balancer_present(name, port, protocol, profile, algorithm=None, members=None, **libcloud_kwargs): - ''' +def balancer_present( + name, port, protocol, profile, algorithm=None, members=None, **libcloud_kwargs +): + """ Ensures a load balancer is present. :param name: Load Balancer name @@ -95,9 +97,9 @@ def balancer_present(name, port, protocol, profile, algorithm=None, members=None :param members: An optional list of members to create on deployment :type members: ``list`` of ``dict`` (ip, port) - ''' - balancers = __salt__['libcloud_loadbalancer.list_balancers'](profile) - match = [z for z in balancers if z['name'] == name] + """ + balancers = __salt__["libcloud_loadbalancer.list_balancers"](profile) + match = [z for z in balancers if z["name"] == name] if len(match) > 0: return state_result(True, "Balancer already exists", name) else: @@ -105,17 +107,21 @@ def balancer_present(name, port, protocol, profile, algorithm=None, members=None if members is not None: starting_members = [] for m in members: - starting_members.append({'ip': m['ip'], 'port': m['port']}) - balancer = __salt__['libcloud_loadbalancer.create_balancer']( - name, port, protocol, - profile, algorithm=algorithm, + starting_members.append({"ip": m["ip"], "port": m["port"]}) + balancer = __salt__["libcloud_loadbalancer.create_balancer"]( + name, + port, + protocol, + profile, + algorithm=algorithm, members=starting_members, - **libcloud_kwargs) + **libcloud_kwargs + ) return state_result(True, "Created new load balancer", name, balancer) def balancer_absent(name, profile, **libcloud_kwargs): - ''' + """ Ensures a load balancer is absent. :param name: Load Balancer name @@ -123,18 +129,20 @@ def balancer_absent(name, profile, **libcloud_kwargs): :param profile: The profile key :type profile: ``str`` - ''' - balancers = __salt__['libcloud_loadbalancer.list_balancers'](profile) - match = [z for z in balancers if z['name'] == name] + """ + balancers = __salt__["libcloud_loadbalancer.list_balancers"](profile) + match = [z for z in balancers if z["name"] == name] if len(match) == 0: return state_result(True, "Balancer already absent", name) else: - result = __salt__['libcloud_loadbalancer.destroy_balancer'](match[0]['id'], profile, **libcloud_kwargs) + result = __salt__["libcloud_loadbalancer.destroy_balancer"]( + match[0]["id"], profile, **libcloud_kwargs + ) return state_result(result, "Deleted load balancer", name) def member_present(ip, port, balancer_id, profile, **libcloud_kwargs): - ''' + """ Ensure a load balancer member is present :param ip: IP address for the new member @@ -148,17 +156,26 @@ def member_present(ip, port, balancer_id, profile, **libcloud_kwargs): :param profile: The profile key :type profile: ``str`` - ''' - existing_members = __salt__['libcloud_loadbalancer.list_balancer_members'](balancer_id, profile) + """ + existing_members = __salt__["libcloud_loadbalancer.list_balancer_members"]( + balancer_id, profile + ) for member in existing_members: - if member['ip'] == ip and member['port'] == port: + if member["ip"] == ip and member["port"] == port: return state_result(True, "Member already present", balancer_id) - member = __salt__['libcloud_loadbalancer.balancer_attach_member'](balancer_id, ip, port, profile, **libcloud_kwargs) - return state_result(True, "Member added to balancer, id: {0}".format(member['id']), balancer_id, member) + member = __salt__["libcloud_loadbalancer.balancer_attach_member"]( + balancer_id, ip, port, profile, **libcloud_kwargs + ) + return state_result( + True, + "Member added to balancer, id: {0}".format(member["id"]), + balancer_id, + member, + ) def member_absent(ip, port, balancer_id, profile, **libcloud_kwargs): - ''' + """ Ensure a load balancer member is absent, based on IP and Port :param ip: IP address for the member @@ -172,10 +189,14 @@ def member_absent(ip, port, balancer_id, profile, **libcloud_kwargs): :param profile: The profile key :type profile: ``str`` - ''' - existing_members = __salt__['libcloud_loadbalancer.list_balancer_members'](balancer_id, profile) + """ + existing_members = __salt__["libcloud_loadbalancer.list_balancer_members"]( + balancer_id, profile + ) for member in existing_members: - if member['ip'] == ip and member['port'] == port: - result = __salt__['libcloud_loadbalancer.balancer_detach_member'](balancer_id, member['id'], profile, **libcloud_kwargs) + if member["ip"] == ip and member["port"] == port: + result = __salt__["libcloud_loadbalancer.balancer_detach_member"]( + balancer_id, member["id"], profile, **libcloud_kwargs + ) return state_result(result, "Member removed", balancer_id) return state_result(True, "Member already absent", balancer_id) diff --git a/salt/states/libcloud_storage.py b/salt/states/libcloud_storage.py index b5314fb4c47..1ca29dba0ac 100644 --- a/salt/states/libcloud_storage.py +++ b/salt/states/libcloud_storage.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Apache Libcloud Storage State ============================= @@ -62,10 +62,11 @@ This example will download the file from the remote cloud and keep it locally profile: profile1 :depends: apache-libcloud -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -75,7 +76,9 @@ log = logging.getLogger(__name__) def __virtual__(): - return True + if "libcloud_storage.list_containers" in __salt__: + return True + return (False, "libcloud_storage module could not be loaded") def __init__(opts): @@ -83,14 +86,11 @@ def __init__(opts): def state_result(result, message, name, changes): - return {'result': result, - 'comment': message, - 'name': name, - 'changes': changes} + return {"result": result, "comment": message, "name": name, "changes": changes} def container_present(name, profile): - ''' + """ Ensures a container is present. :param name: Container name @@ -98,18 +98,18 @@ def container_present(name, profile): :param profile: The profile key :type profile: ``str`` - ''' - containers = __salt__['libcloud_storage.list_containers'](profile) - match = [z for z in containers if z['name'] == name] + """ + containers = __salt__["libcloud_storage.list_containers"](profile) + match = [z for z in containers if z["name"] == name] if len(match) > 0: return state_result(True, "Container already exists", name, {}) else: - result = __salt__['libcloud_storage.create_container'](name, profile) + result = __salt__["libcloud_storage.create_container"](name, profile) return state_result(True, "Created new container", name, result) def container_absent(name, profile): - ''' + """ Ensures a container is absent. :param name: Container name @@ -117,18 +117,18 @@ def container_absent(name, profile): :param profile: The profile key :type profile: ``str`` - ''' - containers = __salt__['libcloud_storage.list_containers'](profile) - match = [z for z in containers if z['name'] == name] + """ + containers = __salt__["libcloud_storage.list_containers"](profile) + match = [z for z in containers if z["name"] == name] if len(match) == 0: return state_result(True, "Container already absent", name, {}) else: - result = __salt__['libcloud_storage.delete_container'](name, profile) + result = __salt__["libcloud_storage.delete_container"](name, profile) return state_result(result, "Deleted container", name, {}) def object_present(container, name, path, profile): - ''' + """ Ensures a object is presnt. :param container: Container name @@ -142,17 +142,21 @@ def object_present(container, name, path, profile): :param profile: The profile key :type profile: ``str`` - ''' - existing_object = __salt__['libcloud_storage.get_container_object'](container, name, profile) + """ + existing_object = __salt__["libcloud_storage.get_container_object"]( + container, name, profile + ) if existing_object is not None: return state_result(True, "Object already present", name, {}) else: - result = __salt__['libcloud_storage.upload_object'](path, container, name, profile) + result = __salt__["libcloud_storage.upload_object"]( + path, container, name, profile + ) return state_result(result, "Uploaded object", name, {}) def object_absent(container, name, profile): - ''' + """ Ensures a object is absent. :param container: Container name @@ -163,17 +167,19 @@ def object_absent(container, name, profile): :param profile: The profile key :type profile: ``str`` - ''' - existing_object = __salt__['libcloud_storage.get_container_object'](container, name, profile) + """ + existing_object = __salt__["libcloud_storage.get_container_object"]( + container, name, profile + ) if existing_object is None: return state_result(True, "Object already absent", name, {}) else: - result = __salt__['libcloud_storage.delete_object'](container, name, profile) + result = __salt__["libcloud_storage.delete_object"](container, name, profile) return state_result(result, "Deleted object", name, {}) def file_present(container, name, path, profile, overwrite_existing=False): - ''' + """ Ensures a object is downloaded locally. :param container: Container name @@ -190,6 +196,8 @@ def file_present(container, name, path, profile, overwrite_existing=False): :param overwrite_existing: Replace if already exists :type overwrite_existing: ``bool`` - ''' - result = __salt__['libcloud_storage.download_object'](path, container, name, profile, overwrite_existing) + """ + result = __salt__["libcloud_storage.download_object"]( + path, container, name, profile, overwrite_existing + ) return state_result(result, "Downloaded object", name, {}) diff --git a/salt/states/linux_acl.py b/salt/states/linux_acl.py index 392d51c139c..94078a756d3 100644 --- a/salt/states/linux_acl.py +++ b/salt/states/linux_acl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Linux File Access Control Lists The Linux ACL state module requires the `getfacl` and `setfacl` binaries. @@ -51,35 +51,40 @@ Ensure a Linux ACL list does not exist - damian - homer - perms: rwx -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os +import salt.utils.path +from salt.exceptions import CommandExecutionError + # Import salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError -import salt.utils.path log = logging.getLogger(__name__) -__virtualname__ = 'acl' +__virtualname__ = "acl" def __virtual__(): - ''' + """ Ensure getfacl & setfacl exist - ''' - if salt.utils.path.which('getfacl') and salt.utils.path.which('setfacl'): + """ + if salt.utils.path.which("getfacl") and salt.utils.path.which("setfacl"): return __virtualname__ - return False, 'The linux_acl state cannot be loaded: the getfacl or setfacl binary is not in the path.' + return ( + False, + "The linux_acl state cannot be loaded: the getfacl or setfacl binary is not in the path.", + ) -def present(name, acl_type, acl_name='', perms='', recurse=False, force=False): - ''' +def present(name, acl_type, acl_name="", perms="", recurse=False, force=False): + """ Ensure a Linux ACL is present name @@ -99,25 +104,22 @@ def present(name, acl_type, acl_name='', perms='', recurse=False, force=False): force Wipe out old permissions and ensure only the new permissions are set - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - _octal = {'r': 4, 'w': 2, 'x': 1, '-': 0} - _octal_lookup = {0: '-', 1: 'r', 2: 'w', 4: 'x'} + _octal = {"r": 4, "w": 2, "x": 1, "-": 0} + _octal_lookup = {0: "-", 1: "r", 2: "w", 4: "x"} if not os.path.exists(name): - ret['comment'] = '{0} does not exist'.format(name) - ret['result'] = False + ret["comment"] = "{0} does not exist".format(name) + ret["result"] = False return ret - __current_perms = __salt__['acl.getfacl'](name, recursive=recurse) + __current_perms = __salt__["acl.getfacl"](name, recursive=recurse) - if acl_type.startswith(('d:', 'default:')): - _acl_type = ':'.join(acl_type.split(':')[1:]) - _current_perms = __current_perms[name].get('defaults', {}) + if acl_type.startswith(("d:", "default:")): + _acl_type = ":".join(acl_type.split(":")[1:]) + _current_perms = __current_perms[name].get("defaults", {}) _default = True else: _acl_type = acl_type @@ -132,14 +134,18 @@ def present(name, acl_type, acl_name='', perms='', recurse=False, force=False): # We search through the dictionary getfacl returns for the owner of the # file if acl_name is empty. - if acl_name == '': - _search_name = __current_perms[name].get('comment').get(_acl_type, '') + if acl_name == "": + _search_name = __current_perms[name].get("comment").get(_acl_type, "") else: _search_name = acl_name if _current_perms.get(_acl_type, None) or _default: try: - user = [i for i in _current_perms[_acl_type] if next(six.iterkeys(i)) == _search_name].pop() + user = [ + i + for i in _current_perms[_acl_type] + if next(six.iterkeys(i)) == _search_name + ].pop() except (AttributeError, IndexError, StopIteration, KeyError): user = None @@ -149,82 +155,123 @@ def present(name, acl_type, acl_name='', perms='', recurse=False, force=False): for path in __current_perms: acl_found = False for user_acl in __current_perms[path].get(_acl_type, []): - if _search_name in user_acl and user_acl[_search_name]['octal'] == octal_sum: + if ( + _search_name in user_acl + and user_acl[_search_name]["octal"] == octal_sum + ): acl_found = True break if not acl_found: need_refresh = True break if not need_refresh: - ret['comment'] = 'Permissions are in the desired state' + ret["comment"] = "Permissions are in the desired state" else: - _num = user[_search_name]['octal'] - new_perms = '{}{}{}'.format(_octal_lookup[_num & 1], - _octal_lookup[_num & 2], - _octal_lookup[_num & 4]) - changes = {'new': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': perms}, - 'old': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': new_perms}} + _num = user[_search_name]["octal"] + new_perms = "{}{}{}".format( + _octal_lookup[_num & 1], + _octal_lookup[_num & 2], + _octal_lookup[_num & 4], + ) + changes = { + "new": {"acl_name": acl_name, "acl_type": acl_type, "perms": perms}, + "old": { + "acl_name": acl_name, + "acl_type": acl_type, + "perms": new_perms, + }, + } - if __opts__['test']: - ret.update({'comment': 'Updated permissions will be applied for ' - '{0}: {1} -> {2}'.format( - acl_name, - new_perms, - perms), - 'result': None, 'changes': changes}) + if __opts__["test"]: + ret.update( + { + "comment": "Updated permissions will be applied for " + "{0}: {1} -> {2}".format(acl_name, new_perms, perms), + "result": None, + "changes": changes, + } + ) return ret try: if force: - __salt__['acl.wipefacls'](name, recursive=recurse, raise_err=True) + __salt__["acl.wipefacls"]( + name, recursive=recurse, raise_err=True + ) - __salt__['acl.modfacl'](acl_type, acl_name, perms, name, - recursive=recurse, raise_err=True) - ret.update({'comment': 'Updated permissions for ' - '{0}'.format(acl_name), - 'result': True, 'changes': changes}) + __salt__["acl.modfacl"]( + acl_type, + acl_name, + perms, + name, + recursive=recurse, + raise_err=True, + ) + ret.update( + { + "comment": "Updated permissions for " + "{0}".format(acl_name), + "result": True, + "changes": changes, + } + ) except CommandExecutionError as exc: - ret.update({'comment': 'Error updating permissions for ' - '{0}: {1}'.format(acl_name, exc.strerror), - 'result': False}) + ret.update( + { + "comment": "Error updating permissions for " + "{0}: {1}".format(acl_name, exc.strerror), + "result": False, + } + ) else: - changes = {'new': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': perms}} + changes = { + "new": {"acl_name": acl_name, "acl_type": acl_type, "perms": perms} + } - if __opts__['test']: - ret.update({'comment': 'New permissions will be applied for ' - '{0}: {1}'.format(acl_name, perms), - 'result': None, 'changes': changes}) - ret['result'] = None + if __opts__["test"]: + ret.update( + { + "comment": "New permissions will be applied for " + "{0}: {1}".format(acl_name, perms), + "result": None, + "changes": changes, + } + ) + ret["result"] = None return ret try: if force: - __salt__['acl.wipefacls'](name, recursive=recurse, raise_err=True) + __salt__["acl.wipefacls"](name, recursive=recurse, raise_err=True) - __salt__['acl.modfacl'](acl_type, acl_name, perms, name, - recursive=recurse, raise_err=True) - ret.update({'comment': 'Applied new permissions for ' - '{0}'.format(acl_name), - 'result': True, 'changes': changes}) + __salt__["acl.modfacl"]( + acl_type, acl_name, perms, name, recursive=recurse, raise_err=True + ) + ret.update( + { + "comment": "Applied new permissions for " + "{0}".format(acl_name), + "result": True, + "changes": changes, + } + ) except CommandExecutionError as exc: - ret.update({'comment': 'Error updating permissions for {0}: ' - '{1}'.format(acl_name, exc.strerror), - 'result': False}) + ret.update( + { + "comment": "Error updating permissions for {0}: " + "{1}".format(acl_name, exc.strerror), + "result": False, + } + ) else: - ret['comment'] = 'ACL Type does not exist' - ret['result'] = False + ret["comment"] = "ACL Type does not exist" + ret["result"] = False return ret -def absent(name, acl_type, acl_name='', perms='', recurse=False): - ''' +def absent(name, acl_type, acl_name="", perms="", recurse=False): + """ Ensure a Linux ACL does not exist name @@ -241,22 +288,19 @@ def absent(name, acl_type, acl_name='', perms='', recurse=False): recurse Set the permissions recursive in the path - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} if not os.path.exists(name): - ret['comment'] = '{0} does not exist'.format(name) - ret['result'] = False + ret["comment"] = "{0} does not exist".format(name) + ret["result"] = False return ret - __current_perms = __salt__['acl.getfacl'](name, recursive=recurse) + __current_perms = __salt__["acl.getfacl"](name, recursive=recurse) - if acl_type.startswith(('d:', 'default:')): - _acl_type = ':'.join(acl_type.split(':')[1:]) - _current_perms = __current_perms[name].get('defaults', {}) + if acl_type.startswith(("d:", "default:")): + _acl_type = ":".join(acl_type.split(":")[1:]) + _current_perms = __current_perms[name].get("defaults", {}) _default = True else: _acl_type = acl_type @@ -271,14 +315,18 @@ def absent(name, acl_type, acl_name='', perms='', recurse=False): # We search through the dictionary getfacl returns for the owner of the # file if acl_name is empty. - if acl_name == '': - _search_name = __current_perms[name].get('comment').get(_acl_type, '') + if acl_name == "": + _search_name = __current_perms[name].get("comment").get(_acl_type, "") else: _search_name = acl_name if _current_perms.get(_acl_type, None) or _default: try: - user = [i for i in _current_perms[_acl_type] if next(six.iterkeys(i)) == _search_name].pop() + user = [ + i + for i in _current_perms[_acl_type] + if next(six.iterkeys(i)) == _search_name + ].pop() except (AttributeError, IndexError, StopIteration, KeyError): user = None @@ -294,25 +342,25 @@ def absent(name, acl_type, acl_name='', perms='', recurse=False): break if user or need_refresh: - ret['comment'] = 'Removing permissions' + ret["comment"] = "Removing permissions" - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret - __salt__['acl.delfacl'](acl_type, acl_name, perms, name, recursive=recurse) + __salt__["acl.delfacl"](acl_type, acl_name, perms, name, recursive=recurse) else: - ret['comment'] = 'Permissions are in the desired state' + ret["comment"] = "Permissions are in the desired state" else: - ret['comment'] = 'ACL Type does not exist' - ret['result'] = False + ret["comment"] = "ACL Type does not exist" + ret["result"] = False return ret -def list_present(name, acl_type, acl_names=None, perms='', recurse=False, force=False): - ''' +def list_present(name, acl_type, acl_names=None, perms="", recurse=False, force=False): + """ Ensure a Linux ACL list is present Takes a list of acl names and add them to the given path @@ -334,42 +382,38 @@ def list_present(name, acl_type, acl_names=None, perms='', recurse=False, force= force Wipe out old permissions and ensure only the new permissions are set - ''' + """ if acl_names is None: acl_names = [] - ret = {'name': name, - 'result': True, - 'changes': {}, - 'pchanges': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "pchanges": {}, "comment": ""} - _octal = {'r': 4, 'w': 2, 'x': 1, '-': 0} + _octal = {"r": 4, "w": 2, "x": 1, "-": 0} _octal_perms = sum([_octal.get(i, i) for i in perms]) if not os.path.exists(name): - ret['comment'] = '{0} does not exist'.format(name) - ret['result'] = False + ret["comment"] = "{0} does not exist".format(name) + ret["result"] = False return ret - __current_perms = __salt__['acl.getfacl'](name) + __current_perms = __salt__["acl.getfacl"](name) - if acl_type.startswith(('d:', 'default:')): - _acl_type = ':'.join(acl_type.split(':')[1:]) - _current_perms = __current_perms[name].get('defaults', {}) + if acl_type.startswith(("d:", "default:")): + _acl_type = ":".join(acl_type.split(":")[1:]) + _current_perms = __current_perms[name].get("defaults", {}) _default = True else: _acl_type = acl_type _current_perms = __current_perms[name] _default = False - _origin_group = _current_perms.get('comment', {}).get('group', None) - _origin_owner = _current_perms.get('comment', {}).get('owner', None) + _origin_group = _current_perms.get("comment", {}).get("group", None) + _origin_owner = _current_perms.get("comment", {}).get("owner", None) _current_acl_types = [] diff_perms = False for key in _current_perms[acl_type]: for current_acl_name in key.keys(): - _current_acl_types.append(current_acl_name.encode('utf-8')) - diff_perms = _octal_perms == key[current_acl_name]['octal'] - if acl_type == 'user': + _current_acl_types.append(current_acl_name.encode("utf-8")) + diff_perms = _octal_perms == key[current_acl_name]["octal"] + if acl_type == "user": try: _current_acl_types.remove(_origin_owner) except ValueError: @@ -381,11 +425,15 @@ def list_present(name, acl_type, acl_names=None, perms='', recurse=False, force= pass diff_acls = set(_current_acl_types) ^ set(acl_names) if not diff_acls and diff_perms and not force: - ret = {'name': name, - 'result': True, - 'changes': {}, - 'pchanges': {}, - 'comment': 'Permissions and {}s are in the desired state'.format(acl_type)} + ret = { + "name": name, + "result": True, + "changes": {}, + "pchanges": {}, + "comment": "Permissions and {}s are in the desired state".format( + acl_type + ), + } return ret # The getfacl execution module lists default with empty names as being # applied to the user/group that owns the file, e.g., @@ -395,8 +443,8 @@ def list_present(name, acl_type, acl_names=None, perms='', recurse=False, force= # We search through the dictionary getfacl returns for the owner of the # file if acl_names is empty. - if acl_names == '': - _search_names = __current_perms[name].get('comment').get(_acl_type, '') + if acl_names == "": + _search_names = __current_perms[name].get("comment").get(_acl_type, "") else: _search_names = acl_names @@ -413,100 +461,183 @@ def list_present(name, acl_type, acl_names=None, perms='', recurse=False, force= changes = {} for count, search_name in enumerate(_search_names): if search_name in users: - if users[search_name]['octal'] == sum([_octal.get(i, i) for i in perms]): - ret['comment'] = 'Permissions are in the desired state' + if users[search_name]["octal"] == sum( + [_octal.get(i, i) for i in perms] + ): + ret["comment"] = "Permissions are in the desired state" else: - changes.update({'new': {'acl_name': ', '.join(acl_names), - 'acl_type': acl_type, - 'perms': _octal_perms}, - 'old': {'acl_name': ', '.join(acl_names), - 'acl_type': acl_type, - 'perms': six.text_type(users[search_name]['octal'])}}) - if __opts__['test']: - ret.update({'comment': 'Updated permissions will be applied for ' - '{0}: {1} -> {2}'.format( - acl_names, - six.text_type(users[search_name]['octal']), - perms), - 'result': None, 'pchanges': changes}) + changes.update( + { + "new": { + "acl_name": ", ".join(acl_names), + "acl_type": acl_type, + "perms": _octal_perms, + }, + "old": { + "acl_name": ", ".join(acl_names), + "acl_type": acl_type, + "perms": six.text_type(users[search_name]["octal"]), + }, + } + ) + if __opts__["test"]: + ret.update( + { + "comment": "Updated permissions will be applied for " + "{0}: {1} -> {2}".format( + acl_names, + six.text_type(users[search_name]["octal"]), + perms, + ), + "result": None, + "pchanges": changes, + } + ) return ret try: if force: - __salt__['acl.wipefacls'](name, recursive=recurse, raise_err=True) + __salt__["acl.wipefacls"]( + name, recursive=recurse, raise_err=True + ) for acl_name in acl_names: - __salt__['acl.modfacl'](acl_type, acl_name, perms, name, - recursive=recurse, raise_err=True) - ret.update({'comment': 'Updated permissions for ' - '{0}'.format(acl_names), - 'result': True, 'changes': changes}) + __salt__["acl.modfacl"]( + acl_type, + acl_name, + perms, + name, + recursive=recurse, + raise_err=True, + ) + ret.update( + { + "comment": "Updated permissions for " + "{0}".format(acl_names), + "result": True, + "changes": changes, + } + ) except CommandExecutionError as exc: - ret.update({'comment': 'Error updating permissions for ' - '{0}: {1}'.format(acl_names, exc.strerror), - 'result': False}) + ret.update( + { + "comment": "Error updating permissions for " + "{0}: {1}".format(acl_names, exc.strerror), + "result": False, + } + ) else: - changes = {'new': {'acl_name': ', '.join(acl_names), - 'acl_type': acl_type, - 'perms': perms}} + changes = { + "new": { + "acl_name": ", ".join(acl_names), + "acl_type": acl_type, + "perms": perms, + } + } - if __opts__['test']: - ret.update({'comment': 'New permissions will be applied for ' - '{0}: {1}'.format(acl_names, perms), - 'result': None, 'pchanges': changes}) - ret['result'] = None + if __opts__["test"]: + ret.update( + { + "comment": "New permissions will be applied for " + "{0}: {1}".format(acl_names, perms), + "result": None, + "pchanges": changes, + } + ) + ret["result"] = None return ret try: if force: - __salt__['acl.wipefacls'](name, recursive=recurse, raise_err=True) + __salt__["acl.wipefacls"]( + name, recursive=recurse, raise_err=True + ) for acl_name in acl_names: - __salt__['acl.modfacl'](acl_type, acl_name, perms, name, - recursive=recurse, raise_err=True) - ret.update({'comment': 'Applied new permissions for ' - '{0}'.format(', '.join(acl_names)), - 'result': True, 'changes': changes}) + __salt__["acl.modfacl"]( + acl_type, + acl_name, + perms, + name, + recursive=recurse, + raise_err=True, + ) + ret.update( + { + "comment": "Applied new permissions for " + "{0}".format(", ".join(acl_names)), + "result": True, + "changes": changes, + } + ) except CommandExecutionError as exc: - ret.update({'comment': 'Error updating permissions for {0}: ' - '{1}'.format(acl_names, exc.strerror), - 'result': False}) + ret.update( + { + "comment": "Error updating permissions for {0}: " + "{1}".format(acl_names, exc.strerror), + "result": False, + } + ) else: - changes = {'new': {'acl_name': ', '.join(acl_names), - 'acl_type': acl_type, - 'perms': perms}} + changes = { + "new": { + "acl_name": ", ".join(acl_names), + "acl_type": acl_type, + "perms": perms, + } + } - if __opts__['test']: - ret.update({'comment': 'New permissions will be applied for ' - '{0}: {1}'.format(acl_names, perms), - 'result': None, 'pchanges': changes}) - ret['result'] = None + if __opts__["test"]: + ret.update( + { + "comment": "New permissions will be applied for " + "{0}: {1}".format(acl_names, perms), + "result": None, + "pchanges": changes, + } + ) + ret["result"] = None return ret try: if force: - __salt__['acl.wipefacls'](name, recursive=recurse, raise_err=True) + __salt__["acl.wipefacls"](name, recursive=recurse, raise_err=True) for acl_name in acl_names: - __salt__['acl.modfacl'](acl_type, acl_name, perms, name, - recursive=recurse, raise_err=True) - ret.update({'comment': 'Applied new permissions for ' - '{0}'.format(', '.join(acl_names)), - 'result': True, 'changes': changes}) + __salt__["acl.modfacl"]( + acl_type, + acl_name, + perms, + name, + recursive=recurse, + raise_err=True, + ) + ret.update( + { + "comment": "Applied new permissions for " + "{0}".format(", ".join(acl_names)), + "result": True, + "changes": changes, + } + ) except CommandExecutionError as exc: - ret.update({'comment': 'Error updating permissions for {0}: ' - '{1}'.format(acl_names, exc.strerror), - 'result': False}) + ret.update( + { + "comment": "Error updating permissions for {0}: " + "{1}".format(acl_names, exc.strerror), + "result": False, + } + ) else: - ret['comment'] = 'ACL Type does not exist' - ret['result'] = False + ret["comment"] = "ACL Type does not exist" + ret["result"] = False return ret def list_absent(name, acl_type, acl_names=None, recurse=False): - ''' + """ Ensure a Linux ACL list does not exist Takes a list of acl names and remove them from the given path @@ -526,25 +657,22 @@ def list_absent(name, acl_type, acl_names=None, recurse=False): recurse Set the permissions recursive in the path - ''' + """ if acl_names is None: acl_names = [] - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} if not os.path.exists(name): - ret['comment'] = '{0} does not exist'.format(name) - ret['result'] = False + ret["comment"] = "{0} does not exist".format(name) + ret["result"] = False return ret - __current_perms = __salt__['acl.getfacl'](name) + __current_perms = __salt__["acl.getfacl"](name) - if acl_type.startswith(('d:', 'default:')): - _acl_type = ':'.join(acl_type.split(':')[1:]) - _current_perms = __current_perms[name].get('defaults', {}) + if acl_type.startswith(("d:", "default:")): + _acl_type = ":".join(acl_type.split(":")[1:]) + _current_perms = __current_perms[name].get("defaults", {}) _default = True else: _acl_type = acl_type @@ -559,7 +687,7 @@ def list_absent(name, acl_type, acl_names=None, recurse=False): # We search through the dictionary getfacl returns for the owner of the # file if acl_names is empty. if not acl_names: - _search_names = set(__current_perms[name].get('comment').get(_acl_type, '')) + _search_names = set(__current_perms[name].get("comment").get(_acl_type, "")) else: _search_names = set(acl_names) @@ -573,18 +701,18 @@ def list_absent(name, acl_type, acl_names=None, recurse=False): users = None if users: - ret['comment'] = 'Removing permissions' + ret["comment"] = "Removing permissions" - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret for acl_name in acl_names: - __salt__['acl.delfacl'](acl_type, acl_name, name, recursive=recurse) + __salt__["acl.delfacl"](acl_type, acl_name, name, recursive=recurse) else: - ret['comment'] = 'Permissions are in the desired state' + ret["comment"] = "Permissions are in the desired state" else: - ret['comment'] = 'ACL Type does not exist' - ret['result'] = False + ret["comment"] = "ACL Type does not exist" + ret["result"] = False return ret diff --git a/salt/states/locale.py b/salt/states/locale.py index 87b8cbd445f..6f9bd6a8c7d 100644 --- a/salt/states/locale.py +++ b/salt/states/locale.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of languages/locales =============================== @@ -16,7 +16,7 @@ Manage the available locales and the system default: - name: en_US.UTF-8 - require: - locale: us_locale -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -26,51 +26,47 @@ from salt.exceptions import CommandExecutionError def __virtual__(): - ''' + """ Only load if the locale module is available in __salt__ - ''' - if 'locale.get_locale' in __salt__: + """ + if "locale.get_locale" in __salt__: return True - else: - return (False, __salt__.missing_fun_string('locale.get_locale')) + return (False, __salt__.missing_fun_string("locale.get_locale")) def system(name): - ''' + """ Set the locale for the system name The name of the locale to use - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} try: - if __salt__['locale.get_locale']() == name: - ret['result'] = True - ret['comment'] = 'System locale {0} already set'.format(name) + if __salt__["locale.get_locale"]() == name: + ret["result"] = True + ret["comment"] = "System locale {0} already set".format(name) return ret - if __opts__['test']: - ret['comment'] = 'System locale {0} needs to be set'.format(name) + if __opts__["test"]: + ret["comment"] = "System locale {0} needs to be set".format(name) return ret - if __salt__['locale.set_locale'](name): - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Set system locale {0}'.format(name) + if __salt__["locale.set_locale"](name): + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Set system locale {0}".format(name) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to set system locale to {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to set system locale to {0}".format(name) return ret except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = 'Failed to set system locale: {0}'.format(err) + ret["result"] = False + ret["comment"] = "Failed to set system locale: {0}".format(err) return ret def present(name): - ''' + """ Generate a locale if it is not present .. versionadded:: 2014.7.0 @@ -78,24 +74,21 @@ def present(name): name The name of the locale to be present. Some distributions require the charmap to be specified as part of the locale at this point. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} - if __salt__['locale.avail'](name): - ret['result'] = True - ret['comment'] = 'Locale {0} is already present'.format(name) + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + if __salt__["locale.avail"](name): + ret["result"] = True + ret["comment"] = "Locale {0} is already present".format(name) return ret - if __opts__['test']: - ret['comment'] = 'Locale {0} needs to be generated'.format(name) + if __opts__["test"]: + ret["comment"] = "Locale {0} needs to be generated".format(name) return ret - if __salt__['locale.gen_locale'](name): - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Generated locale {0}'.format(name) + if __salt__["locale.gen_locale"](name): + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Generated locale {0}".format(name) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to generate locale {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to generate locale {0}".format(name) return ret diff --git a/salt/states/logadm.py b/salt/states/logadm.py index ccc216e3ebe..21ed472e876 100644 --- a/salt/states/logadm.py +++ b/salt/states/logadm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of logs using Solaris logadm. :maintainer: Jorge Schrauwen <sjorge@blackdot.be> @@ -14,10 +14,11 @@ Management of logs using Solaris logadm. .. note:: TODO -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -27,26 +28,25 @@ import salt.utils.data log = logging.getLogger(__name__) # Define the state's virtual name -__virtualname__ = 'logadm' +__virtualname__ = "logadm" def __virtual__(): - ''' + """ Provides logadm state if we have the module - ''' - if 'logadm.list_conf' in __salt__: + """ + if "logadm.list_conf" in __salt__: return True - else: - return ( - False, - '{0} state module can only if the logadm execution module is present'.format( - __virtualname__ - ) - ) + return ( + False, + "{0} state module can only if the logadm execution module is present".format( + __virtualname__ + ), + ) def rotate(name, **kwargs): - ''' + """ Add a log to the logadm configuration name : string @@ -55,67 +55,70 @@ def rotate(name, **kwargs): kwargs : boolean|string|int optional additional flags and parameters - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # cleanup kwargs kwargs = salt.utils.args.clean_kwargs(**kwargs) # inject name as entryname - if 'entryname' not in kwargs: - kwargs['entryname'] = name + if "entryname" not in kwargs: + kwargs["entryname"] = name # figure out log_file and entryname - if 'log_file' not in kwargs or not kwargs['log_file']: - if 'entryname' in kwargs and kwargs['entryname']: - if kwargs['entryname'].startswith('/'): - kwargs['log_file'] = kwargs['entryname'] + if "log_file" not in kwargs or not kwargs["log_file"]: + if "entryname" in kwargs and kwargs["entryname"]: + if kwargs["entryname"].startswith("/"): + kwargs["log_file"] = kwargs["entryname"] # check for log_file - if 'log_file' not in kwargs or not kwargs['log_file']: - ret['result'] = False - ret['comment'] = 'Missing log_file attribute!' + if "log_file" not in kwargs or not kwargs["log_file"]: + ret["result"] = False + ret["comment"] = "Missing log_file attribute!" else: # lookup old configuration - old_config = __salt__['logadm.list_conf']() + old_config = __salt__["logadm.list_conf"]() # remove existing entry - if kwargs['log_file'] in old_config: - res = __salt__['logadm.remove'](kwargs['entryname'] if 'entryname' in kwargs else kwargs['log_file']) - ret['result'] = 'Error' not in res - if not ret['result']: - ret['comment'] = res['Error'] - ret['changes'] = {} + if kwargs["log_file"] in old_config: + res = __salt__["logadm.remove"]( + kwargs["entryname"] if "entryname" in kwargs else kwargs["log_file"] + ) + ret["result"] = "Error" not in res + if not ret["result"]: + ret["comment"] = res["Error"] + ret["changes"] = {} # add new entry - res = __salt__['logadm.rotate'](name, **kwargs) - ret['result'] = 'Error' not in res - if ret['result']: - new_config = __salt__['logadm.list_conf']() - ret['comment'] = 'Log configuration {}'.format('updated' if kwargs['log_file'] in old_config else 'added') - if kwargs['log_file'] in old_config: - for key, val in salt.utils.data.compare_dicts(old_config[kwargs['log_file']], new_config[kwargs['log_file']]).items(): - ret['changes'][key] = val['new'] + res = __salt__["logadm.rotate"](name, **kwargs) + ret["result"] = "Error" not in res + if ret["result"]: + new_config = __salt__["logadm.list_conf"]() + ret["comment"] = "Log configuration {}".format( + "updated" if kwargs["log_file"] in old_config else "added" + ) + if kwargs["log_file"] in old_config: + for key, val in salt.utils.data.compare_dicts( + old_config[kwargs["log_file"]], new_config[kwargs["log_file"]] + ).items(): + ret["changes"][key] = val["new"] else: - ret['changes'] = new_config[kwargs['log_file']] - log.debug(ret['changes']) + ret["changes"] = new_config[kwargs["log_file"]] + log.debug(ret["changes"]) else: - ret['comment'] = res['Error'] + ret["comment"] = res["Error"] # NOTE: we need to remove the log file first # potentially the log configuraiton can get lost :s - if kwargs['log_file'] in old_config: - ret['changes'] = {kwargs['log_file']: None} + if kwargs["log_file"] in old_config: + ret["changes"] = {kwargs["log_file"]: None} else: - ret['changes'] = {} + ret["changes"] = {} return ret def remove(name, log_file=None): - ''' + """ Remove a log from the logadm configuration name : string @@ -126,45 +129,43 @@ def remove(name, log_file=None): .. note:: If log_file is specified it will be used instead of the entry name. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # retrieve all log configuration - config = __salt__['logadm.list_conf']() + config = __salt__["logadm.list_conf"]() # figure out log_file and name if not log_file: - if name.startswith('/'): + if name.startswith("/"): log_file = name name = None else: for log in config: - if 'entryname' in config[log] and config[log]['entryname'] == name: - log_file = config[log]['log_file'] + if "entryname" in config[log] and config[log]["entryname"] == name: + log_file = config[log]["log_file"] break if not name: for log in config: - if 'log_file' in config[log] and config[log]['log_file'] == log_file: - if 'entryname' in config[log]: - name = config[log]['entryname'] + if "log_file" in config[log] and config[log]["log_file"] == log_file: + if "entryname" in config[log]: + name = config[log]["entryname"] break # remove log if needed if log_file in config: - res = __salt__['logadm.remove'](name if name else log_file) - ret['result'] = 'Error' not in res - if ret['result']: - ret['comment'] = 'Configuration for {} removed.'.format(log_file) - ret['changes'][log_file] = None + res = __salt__["logadm.remove"](name if name else log_file) + ret["result"] = "Error" not in res + if ret["result"]: + ret["comment"] = "Configuration for {} removed.".format(log_file) + ret["changes"][log_file] = None else: - ret['comment'] = res['Error'] + ret["comment"] = res["Error"] else: - ret['result'] = True - ret['comment'] = 'No configuration for {} present.'.format(log_file) + ret["result"] = True + ret["comment"] = "No configuration for {} present.".format(log_file) return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/states/logrotate.py b/salt/states/logrotate.py index 9ba01ccb6cd..2c92dc98e8b 100644 --- a/salt/states/logrotate.py +++ b/salt/states/logrotate.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing logrotate. .. versionadded:: 2017.7.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -12,35 +12,33 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs from salt.ext import six -_DEFAULT_CONF = '/etc/logrotate.conf' +_DEFAULT_CONF = "/etc/logrotate.conf" # Define the module's virtual name -__virtualname__ = 'logrotate' +__virtualname__ = "logrotate" # Define a function alias in order not to shadow built-in's -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ Load only on minions that have the logrotate module. - ''' - if 'logrotate.show_conf' in __salt__: + """ + if "logrotate.show_conf" in __salt__: return __virtualname__ - return False + return (False, "logrotate module could not be loaded") def _convert_if_int(value): - ''' + """ Convert to an int if necessary. :param str value: The value to check/convert. :return: The converted or passed value. :rtype: bool|int|str - ''' + """ try: value = int(six.text_type(value)) except ValueError: @@ -49,7 +47,7 @@ def _convert_if_int(value): def set_(name, key, value, setting=None, conf_file=_DEFAULT_CONF): - ''' + """ Set a new value for a specific configuration line. :param str key: The command or block to configure. @@ -76,17 +74,16 @@ def set_(name, key, value, setting=None, conf_file=_DEFAULT_CONF): - value: rotate - setting: 2 - conf_file: /etc/logrotate.conf - ''' - ret = {'name': name, - 'changes': dict(), - 'comment': six.text_type(), - 'result': None} + """ + ret = {"name": name, "changes": dict(), "comment": six.text_type(), "result": None} try: if setting is None: - current_value = __salt__['logrotate.get'](key=key, conf_file=conf_file) + current_value = __salt__["logrotate.get"](key=key, conf_file=conf_file) else: - current_value = __salt__['logrotate.get'](key=key, value=value, conf_file=conf_file) + current_value = __salt__["logrotate.get"]( + key=key, value=value, conf_file=conf_file + ) except (AttributeError, KeyError): current_value = False @@ -94,38 +91,51 @@ def set_(name, key, value, setting=None, conf_file=_DEFAULT_CONF): value = _convert_if_int(value) if current_value == value: - ret['comment'] = "Command '{0}' already has value: {1}".format(key, value) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = "Command '{0}' will be set to value: {1}".format(key, value) - ret['changes'] = {'old': current_value, - 'new': value} + ret["comment"] = "Command '{0}' already has value: {1}".format(key, value) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Command '{0}' will be set to value: {1}".format( + key, value + ) + ret["changes"] = {"old": current_value, "new": value} else: - ret['changes'] = {'old': current_value, - 'new': value} - ret['result'] = __salt__['logrotate.set'](key=key, value=value, conf_file=conf_file) - if ret['result']: - ret['comment'] = "Set command '{0}' value: {1}".format(key, value) + ret["changes"] = {"old": current_value, "new": value} + ret["result"] = __salt__["logrotate.set"]( + key=key, value=value, conf_file=conf_file + ) + if ret["result"]: + ret["comment"] = "Set command '{0}' value: {1}".format(key, value) else: - ret['comment'] = "Unable to set command '{0}' value: {1}".format(key, value) + ret["comment"] = "Unable to set command '{0}' value: {1}".format( + key, value + ) return ret setting = _convert_if_int(setting) if current_value == setting: - ret['comment'] = "Block '{0}' command '{1}' already has value: {2}".format(key, value, setting) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = "Block '{0}' command '{1}' will be set to value: {2}".format(key, value, setting) - ret['changes'] = {'old': current_value, - 'new': setting} + ret["comment"] = "Block '{0}' command '{1}' already has value: {2}".format( + key, value, setting + ) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Block '{0}' command '{1}' will be set to value: {2}".format( + key, value, setting + ) + ret["changes"] = {"old": current_value, "new": setting} else: - ret['changes'] = {'old': current_value, - 'new': setting} - ret['result'] = __salt__['logrotate.set'](key=key, value=value, setting=setting, - conf_file=conf_file) - if ret['result']: - ret['comment'] = "Set block '{0}' command '{1}' value: {2}".format(key, value, setting) + ret["changes"] = {"old": current_value, "new": setting} + ret["result"] = __salt__["logrotate.set"]( + key=key, value=value, setting=setting, conf_file=conf_file + ) + if ret["result"]: + ret["comment"] = "Set block '{0}' command '{1}' value: {2}".format( + key, value, setting + ) else: - ret['comment'] = "Unable to set block '{0}' command '{1}' value: {2}".format(key, value, setting) + ret[ + "comment" + ] = "Unable to set block '{0}' command '{1}' value: {2}".format( + key, value, setting + ) return ret diff --git a/salt/states/loop.py b/salt/states/loop.py index 524fa56c1a0..25e54e1fafb 100644 --- a/salt/states/loop.py +++ b/salt/states/loop.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Loop state Allows for looping over execution modules. @@ -56,33 +56,29 @@ The function :py:func:`data.subdict_match <salt.utils.data.subdict_match>` check keyid: {{ access_key }} key: {{ secret_key }} instances: "{{ instance }}" -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import time import operator import sys +import time # Initialize logging log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'loop' +__virtualname__ = "loop" def __virtual__(): return True -def until(name, - m_args=None, - m_kwargs=None, - condition=None, - period=1, - timeout=60): - ''' +def until(name, m_args=None, m_kwargs=None, condition=None, period=1, timeout=60): + """ Loop over an execution module until a condition is met. :param str name: The name of the execution module @@ -94,8 +90,8 @@ def until(name, :type period: int or float :param timeout: The timeout in seconds :type timeout: int or float - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} if m_args is None: m_args = () @@ -103,16 +99,16 @@ def until(name, m_kwargs = {} if name not in __salt__: - ret['comment'] = 'Cannot find module {0}'.format(name) + ret["comment"] = "Cannot find module {0}".format(name) elif condition is None: - ret['comment'] = 'An exit condition must be specified' + ret["comment"] = "An exit condition must be specified" elif not isinstance(period, (int, float)): - ret['comment'] = 'Period must be specified as a float in seconds' + ret["comment"] = "Period must be specified as a float in seconds" elif not isinstance(timeout, (int, float)): - ret['comment'] = 'Timeout must be specified as a float in seconds' - elif __opts__['test']: - ret['comment'] = 'The execution module {0} will be run'.format(name) - ret['result'] = None + ret["comment"] = "Timeout must be specified as a float in seconds" + elif __opts__["test"]: + ret["comment"] = "The execution module {0} will be run".format(name) + ret["result"] = None else: if m_args is None: m_args = [] @@ -123,25 +119,28 @@ def until(name, while time.time() < timeout: m_ret = __salt__[name](*m_args, **m_kwargs) if eval(condition): # pylint: disable=W0123 - ret['result'] = True - ret['comment'] = 'Condition {0} was met'.format(condition) + ret["result"] = True + ret["comment"] = "Condition {0} was met".format(condition) break time.sleep(period) else: - ret['comment'] = 'Timed out while waiting for condition {0}'.format(condition) + ret["comment"] = "Timed out while waiting for condition {0}".format( + condition + ) return ret def until_no_eval( - name, - expected, - compare_operator='eq', - timeout=60, - period=1, - init_wait=0, - args=None, - kwargs=None): - ''' + name, + expected, + compare_operator="eq", + timeout=60, + period=1, + init_wait=0, + args=None, + kwargs=None, +): + """ Generic waiter state that waits for a specific salt function to produce an expected result. The state fails if the function does not exist or raises an exception, @@ -163,27 +162,28 @@ def until_no_eval( .. versionadded:: 3000 - ''' - ret = {'name': name, 'comment': '', 'changes': {}, 'result': False} + """ + ret = {"name": name, "comment": "", "changes": {}, "result": False} if name not in __salt__: - ret['comment'] = 'Module.function "{}" is unavailable.'.format(name) + ret["comment"] = 'Module.function "{}" is unavailable.'.format(name) elif not isinstance(period, (int, float)): - ret['comment'] = 'Period must be specified as a float in seconds' + ret["comment"] = "Period must be specified as a float in seconds" elif not isinstance(timeout, (int, float)): - ret['comment'] = 'Timeout must be specified as a float in seconds' + ret["comment"] = "Timeout must be specified as a float in seconds" elif compare_operator in __salt__: comparator = __salt__[compare_operator] elif compare_operator in __utils__: comparator = __utils__[compare_operator] elif not hasattr(operator, compare_operator): - ret['comment'] = 'Invalid operator "{}" supplied.'.format(compare_operator) + ret["comment"] = 'Invalid operator "{}" supplied.'.format(compare_operator) else: comparator = getattr(operator, compare_operator) - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Would have waited for "{}" to produce "{}".' - ''.format(name, expected)) - if ret['comment']: + if __opts__["test"]: + ret["result"] = None + ret["comment"] = 'Would have waited for "{}" to produce "{}".' "".format( + name, expected + ) + if ret["comment"]: return ret if init_wait: @@ -202,23 +202,37 @@ def until_no_eval( res = __salt__[name](*args, **kwargs) except Exception: # pylint: disable=broad-except (exc_type, exc_value, _) = sys.exc_info() - ret['comment'] = 'Exception occurred while executing {}: {}:{}'.format(name, exc_type, exc_value) + ret["comment"] = "Exception occurred while executing {}: {}:{}".format( + name, exc_type, exc_value + ) break res_archive.append(res) cmp_res = comparator(res, expected) - log.debug('%s:until_no_eval:\n' - '\t\tAttempt %s, result: %s, expected: %s, compare result: %s', - __name__, current_attempt, res, expected, cmp_res) + log.debug( + "%s:until_no_eval:\n" + "\t\tAttempt %s, result: %s, expected: %s, compare result: %s", + __name__, + current_attempt, + res, + expected, + cmp_res, + ) if cmp_res: - ret['result'] = True - ret['comment'] = ('Call provided the expected results in {} attempts' - ''.format(current_attempt)) + ret["result"] = True + ret["comment"] = ( + "Call provided the expected results in {} attempts" + "".format(current_attempt) + ) break time.sleep(period) else: - ret['comment'] = ('Call did not produce the expected result after {} attempts' - ''.format(current_attempt)) - log.debug('%s:until_no_eval:\n' - '\t\tResults of all attempts: %s', - __name__, res_archive) + ret["comment"] = ( + "Call did not produce the expected result after {} attempts" + "".format(current_attempt) + ) + log.debug( + "%s:until_no_eval:\n" "\t\tResults of all attempts: %s", + __name__, + res_archive, + ) return ret diff --git a/salt/states/lvm.py b/salt/states/lvm.py index 5cb15d0ed6e..edfefe5a8e2 100644 --- a/salt/states/lvm.py +++ b/salt/states/lvm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Linux logical volumes =================================== @@ -20,7 +20,7 @@ A state module to manage LVMs - size: 10G - stripes: 5 - stripesize: 8K -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -32,16 +32,16 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load the module if lvm is installed - ''' - if salt.utils.path.which('lvm'): - return 'lvm' - return False + """ + if salt.utils.path.which("lvm"): + return "lvm" + return (False, "lvm command not found") def pv_present(name, **kwargs): - ''' + """ Set a Physical Device to be used as an LVM Physical Volume name @@ -50,62 +50,56 @@ def pv_present(name, **kwargs): kwargs Any supported options to pvcreate. See :mod:`linux_lvm <salt.modules.linux_lvm>` for more details. - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} - if __salt__['lvm.pvdisplay'](name, quiet=True): - ret['comment'] = 'Physical Volume {0} already present'.format(name) - elif __opts__['test']: - ret['comment'] = 'Physical Volume {0} is set to be created'.format(name) - ret['result'] = None + if __salt__["lvm.pvdisplay"](name, quiet=True): + ret["comment"] = "Physical Volume {0} already present".format(name) + elif __opts__["test"]: + ret["comment"] = "Physical Volume {0} is set to be created".format(name) + ret["result"] = None return ret else: - changes = __salt__['lvm.pvcreate'](name, **kwargs) + changes = __salt__["lvm.pvcreate"](name, **kwargs) - if __salt__['lvm.pvdisplay'](name): - ret['comment'] = 'Created Physical Volume {0}'.format(name) - ret['changes']['created'] = changes + if __salt__["lvm.pvdisplay"](name): + ret["comment"] = "Created Physical Volume {0}".format(name) + ret["changes"]["created"] = changes else: - ret['comment'] = 'Failed to create Physical Volume {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create Physical Volume {0}".format(name) + ret["result"] = False return ret def pv_absent(name): - ''' + """ Ensure that a Physical Device is not being used by lvm name The device name to initialize. - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} - if not __salt__['lvm.pvdisplay'](name, quiet=True): - ret['comment'] = 'Physical Volume {0} does not exist'.format(name) - elif __opts__['test']: - ret['comment'] = 'Physical Volume {0} is set to be removed'.format(name) - ret['result'] = None + if not __salt__["lvm.pvdisplay"](name, quiet=True): + ret["comment"] = "Physical Volume {0} does not exist".format(name) + elif __opts__["test"]: + ret["comment"] = "Physical Volume {0} is set to be removed".format(name) + ret["result"] = None return ret else: - changes = __salt__['lvm.pvremove'](name) + changes = __salt__["lvm.pvremove"](name) - if __salt__['lvm.pvdisplay'](name, quiet=True): - ret['comment'] = 'Failed to remove Physical Volume {0}'.format(name) - ret['result'] = False + if __salt__["lvm.pvdisplay"](name, quiet=True): + ret["comment"] = "Failed to remove Physical Volume {0}".format(name) + ret["result"] = False else: - ret['comment'] = 'Removed Physical Volume {0}'.format(name) - ret['changes']['removed'] = changes + ret["comment"] = "Removed Physical Volume {0}".format(name) + ret["changes"]["removed"] = changes return ret def vg_present(name, devices=None, **kwargs): - ''' + """ Create an LVM Volume Group name @@ -117,103 +111,100 @@ def vg_present(name, devices=None, **kwargs): kwargs Any supported options to vgcreate. See :mod:`linux_lvm <salt.modules.linux_lvm>` for more details. - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} if isinstance(devices, six.string_types): - devices = devices.split(',') + devices = devices.split(",") - if __salt__['lvm.vgdisplay'](name, quiet=True): - ret['comment'] = 'Volume Group {0} already present'.format(name) + if __salt__["lvm.vgdisplay"](name, quiet=True): + ret["comment"] = "Volume Group {0} already present".format(name) for device in devices: realdev = os.path.realpath(device) - pvs = __salt__['lvm.pvdisplay'](realdev, real=True) + pvs = __salt__["lvm.pvdisplay"](realdev, real=True) if pvs and pvs.get(realdev, None): - if pvs[realdev]['Volume Group Name'] == name: - ret['comment'] = '{0}\n{1}'.format( - ret['comment'], - '{0} is part of Volume Group'.format(device)) - elif pvs[realdev]['Volume Group Name'] in ['', '#orphans_lvm2']: - __salt__['lvm.vgextend'](name, device) - pvs = __salt__['lvm.pvdisplay'](realdev, real=True) - if pvs[realdev]['Volume Group Name'] == name: - ret['changes'].update( - {device: 'added to {0}'.format(name)}) + if pvs[realdev]["Volume Group Name"] == name: + ret["comment"] = "{0}\n{1}".format( + ret["comment"], "{0} is part of Volume Group".format(device) + ) + elif pvs[realdev]["Volume Group Name"] in ["", "#orphans_lvm2"]: + __salt__["lvm.vgextend"](name, device) + pvs = __salt__["lvm.pvdisplay"](realdev, real=True) + if pvs[realdev]["Volume Group Name"] == name: + ret["changes"].update({device: "added to {0}".format(name)}) else: - ret['comment'] = '{0}\n{1}'.format( - ret['comment'], - '{0} could not be added'.format(device)) - ret['result'] = False + ret["comment"] = "{0}\n{1}".format( + ret["comment"], "{0} could not be added".format(device) + ) + ret["result"] = False else: - ret['comment'] = '{0}\n{1}'.format( - ret['comment'], - '{0} is part of {1}'.format( - device, pvs[realdev]['Volume Group Name'])) - ret['result'] = False + ret["comment"] = "{0}\n{1}".format( + ret["comment"], + "{0} is part of {1}".format( + device, pvs[realdev]["Volume Group Name"] + ), + ) + ret["result"] = False else: - ret['comment'] = '{0}\n{1}'.format( - ret['comment'], - 'pv {0} is not present'.format(device)) - ret['result'] = False - elif __opts__['test']: - ret['comment'] = 'Volume Group {0} is set to be created'.format(name) - ret['result'] = None + ret["comment"] = "{0}\n{1}".format( + ret["comment"], "pv {0} is not present".format(device) + ) + ret["result"] = False + elif __opts__["test"]: + ret["comment"] = "Volume Group {0} is set to be created".format(name) + ret["result"] = None return ret else: - changes = __salt__['lvm.vgcreate'](name, devices, **kwargs) + changes = __salt__["lvm.vgcreate"](name, devices, **kwargs) - if __salt__['lvm.vgdisplay'](name): - ret['comment'] = 'Created Volume Group {0}'.format(name) - ret['changes']['created'] = changes + if __salt__["lvm.vgdisplay"](name): + ret["comment"] = "Created Volume Group {0}".format(name) + ret["changes"]["created"] = changes else: - ret['comment'] = 'Failed to create Volume Group {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create Volume Group {0}".format(name) + ret["result"] = False return ret def vg_absent(name): - ''' + """ Remove an LVM volume group name The volume group to remove - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} - if not __salt__['lvm.vgdisplay'](name, quiet=True): - ret['comment'] = 'Volume Group {0} already absent'.format(name) - elif __opts__['test']: - ret['comment'] = 'Volume Group {0} is set to be removed'.format(name) - ret['result'] = None + if not __salt__["lvm.vgdisplay"](name, quiet=True): + ret["comment"] = "Volume Group {0} already absent".format(name) + elif __opts__["test"]: + ret["comment"] = "Volume Group {0} is set to be removed".format(name) + ret["result"] = None return ret else: - changes = __salt__['lvm.vgremove'](name) + changes = __salt__["lvm.vgremove"](name) - if not __salt__['lvm.vgdisplay'](name, quiet=True): - ret['comment'] = 'Removed Volume Group {0}'.format(name) - ret['changes']['removed'] = changes + if not __salt__["lvm.vgdisplay"](name, quiet=True): + ret["comment"] = "Removed Volume Group {0}".format(name) + ret["changes"]["removed"] = changes else: - ret['comment'] = 'Failed to remove Volume Group {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to remove Volume Group {0}".format(name) + ret["result"] = False return ret -def lv_present(name, - vgname=None, - size=None, - extents=None, - snapshot=None, - pv='', - thinvolume=False, - thinpool=False, - force=False, - **kwargs): - ''' +def lv_present( + name, + vgname=None, + size=None, + extents=None, + snapshot=None, + pv="", + thinvolume=False, + thinpool=False, + force=False, + **kwargs +): + """ Create a new Logical Volume name @@ -251,11 +242,8 @@ def lv_present(name, force Assume yes to all prompts - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} _snapshot = None @@ -264,39 +252,43 @@ def lv_present(name, name = snapshot if thinvolume: - lvpath = '/dev/{0}/{1}'.format(vgname.split('/')[0], name) + lvpath = "/dev/{0}/{1}".format(vgname.split("/")[0], name) else: - lvpath = '/dev/{0}/{1}'.format(vgname, name) + lvpath = "/dev/{0}/{1}".format(vgname, name) - if __salt__['lvm.lvdisplay'](lvpath, quiet=True): - ret['comment'] = 'Logical Volume {0} already present'.format(name) - elif __opts__['test']: - ret['comment'] = 'Logical Volume {0} is set to be created'.format(name) - ret['result'] = None + if __salt__["lvm.lvdisplay"](lvpath, quiet=True): + ret["comment"] = "Logical Volume {0} already present".format(name) + elif __opts__["test"]: + ret["comment"] = "Logical Volume {0} is set to be created".format(name) + ret["result"] = None return ret else: - changes = __salt__['lvm.lvcreate'](name, - vgname, - size=size, - extents=extents, - snapshot=_snapshot, - pv=pv, - thinvolume=thinvolume, - thinpool=thinpool, - force=force, - **kwargs) + changes = __salt__["lvm.lvcreate"]( + name, + vgname, + size=size, + extents=extents, + snapshot=_snapshot, + pv=pv, + thinvolume=thinvolume, + thinpool=thinpool, + force=force, + **kwargs + ) - if __salt__['lvm.lvdisplay'](lvpath): - ret['comment'] = 'Created Logical Volume {0}'.format(name) - ret['changes']['created'] = changes + if __salt__["lvm.lvdisplay"](lvpath): + ret["comment"] = "Created Logical Volume {0}".format(name) + ret["changes"]["created"] = changes else: - ret['comment'] = 'Failed to create Logical Volume {0}. Error: {1}'.format(name, changes) - ret['result'] = False + ret["comment"] = "Failed to create Logical Volume {0}. Error: {1}".format( + name, changes + ) + ret["result"] = False return ret def lv_absent(name, vgname=None): - ''' + """ Remove a given existing Logical Volume from a named existing volume group name @@ -304,26 +296,23 @@ def lv_absent(name, vgname=None): vgname The name of the Volume Group on which the Logical Volume resides - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} - lvpath = '/dev/{0}/{1}'.format(vgname, name) - if not __salt__['lvm.lvdisplay'](lvpath, quiet=True): - ret['comment'] = 'Logical Volume {0} already absent'.format(name) - elif __opts__['test']: - ret['comment'] = 'Logical Volume {0} is set to be removed'.format(name) - ret['result'] = None + lvpath = "/dev/{0}/{1}".format(vgname, name) + if not __salt__["lvm.lvdisplay"](lvpath, quiet=True): + ret["comment"] = "Logical Volume {0} already absent".format(name) + elif __opts__["test"]: + ret["comment"] = "Logical Volume {0} is set to be removed".format(name) + ret["result"] = None return ret else: - changes = __salt__['lvm.lvremove'](name, vgname) + changes = __salt__["lvm.lvremove"](name, vgname) - if not __salt__['lvm.lvdisplay'](lvpath, quiet=True): - ret['comment'] = 'Removed Logical Volume {0}'.format(name) - ret['changes']['removed'] = changes + if not __salt__["lvm.lvdisplay"](lvpath, quiet=True): + ret["comment"] = "Removed Logical Volume {0}".format(name) + ret["changes"]["removed"] = changes else: - ret['comment'] = 'Failed to remove Logical Volume {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to remove Logical Volume {0}".format(name) + ret["result"] = False return ret diff --git a/salt/states/lvs_server.py b/salt/states/lvs_server.py index c1c832ca6de..a25958b65e8 100644 --- a/salt/states/lvs_server.py +++ b/salt/states/lvs_server.py @@ -1,28 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Management of LVS (Linux Virtual Server) Real Server ==================================================== -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the lvs module is available in __salt__ - ''' - return 'lvs_server' if 'lvs.get_rules' in __salt__ else False + """ + if "lvs.get_rules" in __salt__: + return "lvs_server" + return (False, "lvs module could not be loaded") -def present(name, - protocol=None, - service_address=None, - server_address=None, - packet_forward_method='dr', - weight=1 - ): - ''' +def present( + name, + protocol=None, + service_address=None, + server_address=None, + packet_forward_method="dr", + weight=1, +): + """ Ensure that the named service is present. name @@ -53,67 +56,98 @@ def present(name, - server_address: 192.168.0.11:8080 - packet_forward_method: dr - weight: 10 - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - #check server - server_check = __salt__['lvs.check_server'](protocol=protocol, - service_address=service_address, - server_address=server_address) + # check server + server_check = __salt__["lvs.check_server"]( + protocol=protocol, + service_address=service_address, + server_address=server_address, + ) if server_check is True: - server_rule_check = __salt__['lvs.check_server'](protocol=protocol, - service_address=service_address, - server_address=server_address, - packet_forward_method=packet_forward_method, - weight=weight) + server_rule_check = __salt__["lvs.check_server"]( + protocol=protocol, + service_address=service_address, + server_address=server_address, + packet_forward_method=packet_forward_method, + weight=weight, + ) if server_rule_check is True: - ret['comment'] = 'LVS Server {0} in service {1}({2}) is present'.format(name, service_address, protocol) + ret["comment"] = "LVS Server {0} in service {1}({2}) is present".format( + name, service_address, protocol + ) return ret else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'LVS Server {0} in service {1}({2}) is present but some options should update'.format(name, service_address, protocol) + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "LVS Server {0} in service {1}({2}) is present but some options should update".format( + name, service_address, protocol + ) return ret else: - server_edit = __salt__['lvs.edit_server'](protocol=protocol, - service_address=service_address, - server_address=server_address, - packet_forward_method=packet_forward_method, - weight=weight) + server_edit = __salt__["lvs.edit_server"]( + protocol=protocol, + service_address=service_address, + server_address=server_address, + packet_forward_method=packet_forward_method, + weight=weight, + ) if server_edit is True: - ret['comment'] = 'LVS Server {0} in service {1}({2}) has been updated'.format(name, service_address, protocol) - ret['changes'][name] = 'Update' + ret[ + "comment" + ] = "LVS Server {0} in service {1}({2}) has been updated".format( + name, service_address, protocol + ) + ret["changes"][name] = "Update" return ret else: - ret['result'] = False - ret['comment'] = 'LVS Server {0} in service {1}({2}) update failed({3})'.format(name, service_address, protocol, server_edit) + ret["result"] = False + ret[ + "comment" + ] = "LVS Server {0} in service {1}({2}) update failed({3})".format( + name, service_address, protocol, server_edit + ) return ret else: - if __opts__['test']: - ret['comment'] = 'LVS Server {0} in service {1}({2}) is not present and needs to be created'.format(name, service_address, protocol) - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "LVS Server {0} in service {1}({2}) is not present and needs to be created".format( + name, service_address, protocol + ) + ret["result"] = None return ret else: - server_add = __salt__['lvs.add_server'](protocol=protocol, - service_address=service_address, - server_address=server_address, - packet_forward_method=packet_forward_method, - weight=weight) + server_add = __salt__["lvs.add_server"]( + protocol=protocol, + service_address=service_address, + server_address=server_address, + packet_forward_method=packet_forward_method, + weight=weight, + ) if server_add is True: - ret['comment'] = 'LVS Server {0} in service {1}({2}) has been created'.format(name, service_address, protocol) - ret['changes'][name] = 'Present' + ret[ + "comment" + ] = "LVS Server {0} in service {1}({2}) has been created".format( + name, service_address, protocol + ) + ret["changes"][name] = "Present" return ret else: - ret['comment'] = 'LVS Service {0} in service {1}({2}) create failed({3})'.format(name, service_address, protocol, server_add) - ret['result'] = False + ret[ + "comment" + ] = "LVS Service {0} in service {1}({2}) create failed({3})".format( + name, service_address, protocol, server_add + ) + ret["result"] = False return ret def absent(name, protocol=None, service_address=None, server_address=None): - ''' + """ Ensure the LVS Real Server in specified service is absent. name @@ -127,33 +161,50 @@ def absent(name, protocol=None, service_address=None, server_address=None): server_address The LVS real server address. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - #check if server exists and remove it - server_check = __salt__['lvs.check_server'](protocol=protocol, - service_address=service_address, - server_address=server_address) + # check if server exists and remove it + server_check = __salt__["lvs.check_server"]( + protocol=protocol, + service_address=service_address, + server_address=server_address, + ) if server_check is True: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'LVS Server {0} in service {1}({2}) is present and needs to be removed'.format(name, service_address, protocol) + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "LVS Server {0} in service {1}({2}) is present and needs to be removed".format( + name, service_address, protocol + ) return ret - server_delete = __salt__['lvs.delete_server'](protocol=protocol, - service_address=service_address, - server_address=server_address) + server_delete = __salt__["lvs.delete_server"]( + protocol=protocol, + service_address=service_address, + server_address=server_address, + ) if server_delete is True: - ret['comment'] = 'LVS Server {0} in service {1}({2}) has been removed'.format(name, service_address, protocol) - ret['changes'][name] = 'Absent' + ret[ + "comment" + ] = "LVS Server {0} in service {1}({2}) has been removed".format( + name, service_address, protocol + ) + ret["changes"][name] = "Absent" return ret else: - ret['comment'] = 'LVS Server {0} in service {1}({2}) removed failed({3})'.format(name, service_address, protocol, server_delete) - ret['result'] = False + ret[ + "comment" + ] = "LVS Server {0} in service {1}({2}) removed failed({3})".format( + name, service_address, protocol, server_delete + ) + ret["result"] = False return ret else: - ret['comment'] = 'LVS Server {0} in service {1}({2}) is not present, so it cannot be removed'.format(name, service_address, protocol) + ret[ + "comment" + ] = "LVS Server {0} in service {1}({2}) is not present, so it cannot be removed".format( + name, service_address, protocol + ) return ret diff --git a/salt/states/lvs_service.py b/salt/states/lvs_service.py index 98f3af36d62..5eb9f05311e 100644 --- a/salt/states/lvs_service.py +++ b/salt/states/lvs_service.py @@ -1,26 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" Management of LVS (Linux Virtual Server) Service ================================================ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the lvs module is available in __salt__ - ''' - return 'lvs_service' if 'lvs.get_rules' in __salt__ else False + """ + if "lvs.get_rules" in __salt__: + return "lvs_service" + return (False, "lvs module could not be loaded") -def present(name, - protocol=None, - service_address=None, - scheduler='wlc', - ): - ''' +def present( + name, protocol=None, service_address=None, scheduler="wlc", +): + """ Ensure that the named service is present. name @@ -42,60 +42,68 @@ def present(name, - service_address: 1.1.1.1:80 - protocol: tcp - scheduler: rr - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - #check service - service_check = __salt__['lvs.check_service'](protocol=protocol, - service_address=service_address) + # check service + service_check = __salt__["lvs.check_service"]( + protocol=protocol, service_address=service_address + ) if service_check is True: - service_rule_check = __salt__['lvs.check_service'](protocol=protocol, - service_address=service_address, - scheduler=scheduler) + service_rule_check = __salt__["lvs.check_service"]( + protocol=protocol, service_address=service_address, scheduler=scheduler + ) if service_rule_check is True: - ret['comment'] = 'LVS Service {0} is present'.format(name) + ret["comment"] = "LVS Service {0} is present".format(name) return ret else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'LVS Service {0} is present but some options should update'.format(name) + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "LVS Service {0} is present but some options should update".format( + name + ) return ret else: - service_edit = __salt__['lvs.edit_service'](protocol=protocol, - service_address=service_address, - scheduler=scheduler) + service_edit = __salt__["lvs.edit_service"]( + protocol=protocol, + service_address=service_address, + scheduler=scheduler, + ) if service_edit is True: - ret['comment'] = 'LVS Service {0} has been updated'.format(name) - ret['changes'][name] = 'Update' + ret["comment"] = "LVS Service {0} has been updated".format(name) + ret["changes"][name] = "Update" return ret else: - ret['result'] = False - ret['comment'] = 'LVS Service {0} update failed'.format(name) + ret["result"] = False + ret["comment"] = "LVS Service {0} update failed".format(name) return ret else: - if __opts__['test']: - ret['comment'] = 'LVS Service {0} is not present and needs to be created'.format(name) - ret['result'] = None + if __opts__["test"]: + ret[ + "comment" + ] = "LVS Service {0} is not present and needs to be created".format(name) + ret["result"] = None return ret else: - service_add = __salt__['lvs.add_service'](protocol=protocol, - service_address=service_address, - scheduler=scheduler) + service_add = __salt__["lvs.add_service"]( + protocol=protocol, service_address=service_address, scheduler=scheduler + ) if service_add is True: - ret['comment'] = 'LVS Service {0} has been created'.format(name) - ret['changes'][name] = 'Present' + ret["comment"] = "LVS Service {0} has been created".format(name) + ret["changes"][name] = "Present" return ret else: - ret['comment'] = 'LVS Service {0} create failed({1})'.format(name, service_add) - ret['result'] = False + ret["comment"] = "LVS Service {0} create failed({1})".format( + name, service_add + ) + ret["result"] = False return ret def absent(name, protocol=None, service_address=None): - ''' + """ Ensure the LVS service is absent. name @@ -106,31 +114,36 @@ def absent(name, protocol=None, service_address=None): service_address The LVS service address - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - #check if service exists and remove it - service_check = __salt__['lvs.check_service'](protocol=protocol, - service_address=service_address) + # check if service exists and remove it + service_check = __salt__["lvs.check_service"]( + protocol=protocol, service_address=service_address + ) if service_check is True: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'LVS Service {0} is present and needs to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "LVS Service {0} is present and needs to be removed".format(name) return ret - service_delete = __salt__['lvs.delete_service'](protocol=protocol, - service_address=service_address) + service_delete = __salt__["lvs.delete_service"]( + protocol=protocol, service_address=service_address + ) if service_delete is True: - ret['comment'] = 'LVS Service {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + ret["comment"] = "LVS Service {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['comment'] = 'LVS Service {0} removed failed({1})'.format(name, service_delete) - ret['result'] = False + ret["comment"] = "LVS Service {0} removed failed({1})".format( + name, service_delete + ) + ret["result"] = False return ret else: - ret['comment'] = 'LVS Service {0} is not present, so it cannot be removed'.format(name) + ret[ + "comment" + ] = "LVS Service {0} is not present, so it cannot be removed".format(name) return ret diff --git a/salt/states/lxc.py b/salt/states/lxc.py index b6bef492097..a8d0c106b11 100644 --- a/salt/states/lxc.py +++ b/salt/states/lxc.py @@ -1,35 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" Manage Linux Containers ======================= -''' +""" from __future__ import absolute_import, print_function, unicode_literals -__docformat__ = 'restructuredtext en' # Import salt libs from salt.exceptions import CommandExecutionError, SaltInvocationError +__docformat__ = "restructuredtext en" + # Container existence/non-existence -def present(name, - running=None, - clone_from=None, - snapshot=False, - profile=None, - network_profile=None, - template=None, - options=None, - image=None, - config=None, - fstype=None, - size=None, - backing=None, - vgname=None, - lvname=None, - thinpool=None, - path=None): - ''' +def present( + name, + running=None, + clone_from=None, + snapshot=False, + profile=None, + network_profile=None, + template=None, + options=None, + image=None, + config=None, + fstype=None, + size=None, + backing=None, + vgname=None, + lvname=None, + thinpool=None, + path=None, +): + """ .. versionchanged:: 2015.8.0 The :mod:`lxc.created <salt.states.lxc.created>` state has been renamed @@ -147,52 +150,51 @@ def present(name, thinpool Name of a pool volume that will be used for thin-provisioning this container. Only applicable if ``backing`` is set to ``lvm``. - ''' - ret = {'name': name, - 'result': True, - 'comment': 'Container \'{0}\' already exists'.format(name), - 'changes': {}} + """ + ret = { + "name": name, + "result": True, + "comment": "Container '{0}' already exists".format(name), + "changes": {}, + } if not any((template, image, clone_from)): # Take a peek into the profile to see if there is a clone source there. # Otherwise, we're assuming this is a template/image creation. Also # check to see if none of the create types are in the profile. If this # is the case, then bail out early. - c_profile = __salt__['lxc.get_container_profile'](profile) - if not any(x for x in c_profile - if x in ('template', 'image', 'clone_from')): - ret['result'] = False - ret['comment'] = ('No template, image, or clone_from parameter ' - 'was found in either the state\'s arguments or ' - 'the LXC profile') + c_profile = __salt__["lxc.get_container_profile"](profile) + if not any(x for x in c_profile if x in ("template", "image", "clone_from")): + ret["result"] = False + ret["comment"] = ( + "No template, image, or clone_from parameter " + "was found in either the state's arguments or " + "the LXC profile" + ) else: try: # Assign the profile's clone_from param to the state, so that # we know to invoke lxc.clone to create the container. - clone_from = c_profile['clone_from'] + clone_from = c_profile["clone_from"] except KeyError: pass # Sanity check(s) - if clone_from and not __salt__['lxc.exists'](clone_from, path=path): - ret['result'] = False - ret['comment'] = ('Clone source \'{0}\' does not exist' - .format(clone_from)) - if not ret['result']: + if clone_from and not __salt__["lxc.exists"](clone_from, path=path): + ret["result"] = False + ret["comment"] = "Clone source '{0}' does not exist".format(clone_from) + if not ret["result"]: return ret - action = 'cloned from {0}'.format(clone_from) if clone_from else 'created' + action = "cloned from {0}".format(clone_from) if clone_from else "created" - state = {'old': __salt__['lxc.state'](name, path=path)} - if __opts__['test']: - if state['old'] is None: - ret['comment'] = ( - 'Container \'{0}\' will be {1}'.format( - name, - 'cloned from {0}'.format(clone_from) if clone_from - else 'created') + state = {"old": __salt__["lxc.state"](name, path=path)} + if __opts__["test"]: + if state["old"] is None: + ret["comment"] = "Container '{0}' will be {1}".format( + name, "cloned from {0}".format(clone_from) if clone_from else "created" ) - ret['result'] = None + ret["result"] = None return ret else: if running is None: @@ -200,44 +202,40 @@ def present(name, # running. Set the result back to True and return return ret elif running: - if state['old'] in ('frozen', 'stopped'): - ret['comment'] = ( - 'Container \'{0}\' would be {1}'.format( - name, - 'unfrozen' if state['old'] == 'frozen' - else 'started' - ) + if state["old"] in ("frozen", "stopped"): + ret["comment"] = "Container '{0}' would be {1}".format( + name, "unfrozen" if state["old"] == "frozen" else "started" ) - ret['result'] = None + ret["result"] = None return ret else: - ret['comment'] += ' and is running' + ret["comment"] += " and is running" return ret else: - if state['old'] in ('frozen', 'running'): - ret['comment'] = ( - 'Container \'{0}\' would be stopped'.format(name) - ) - ret['result'] = None + if state["old"] in ("frozen", "running"): + ret["comment"] = "Container '{0}' would be stopped".format(name) + ret["result"] = None return ret else: - ret['comment'] += ' and is stopped' + ret["comment"] += " and is stopped" return ret - if state['old'] is None: + if state["old"] is None: # Container does not exist try: if clone_from: - result = __salt__['lxc.clone'](name, - clone_from, - profile=profile, - network_profile=network_profile, - snapshot=snapshot, - size=size, - path=path, - backing=backing) + result = __salt__["lxc.clone"]( + name, + clone_from, + profile=profile, + network_profile=network_profile, + snapshot=snapshot, + size=size, + path=path, + backing=backing, + ) else: - result = __salt__['lxc.create']( + result = __salt__["lxc.create"]( name, profile=profile, network_profile=network_profile, @@ -251,87 +249,79 @@ def present(name, vgname=vgname, path=path, lvname=lvname, - thinpool=thinpool) + thinpool=thinpool, + ) except (CommandExecutionError, SaltInvocationError) as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror else: if clone_from: - ret['comment'] = ('Cloned container \'{0}\' as \'{1}\'' - .format(clone_from, name)) + ret["comment"] = "Cloned container '{0}' as '{1}'".format( + clone_from, name + ) else: - ret['comment'] = 'Created container \'{0}\''.format(name) - state['new'] = result['state']['new'] + ret["comment"] = "Created container '{0}'".format(name) + state["new"] = result["state"]["new"] - if ret['result'] is True: + if ret["result"] is True: # Enforce the "running" parameter if running is None: # Don't do anything pass elif running: - c_state = __salt__['lxc.state'](name, path=path) - if c_state == 'running': - ret['comment'] += ' and is running' + c_state = __salt__["lxc.state"](name, path=path) + if c_state == "running": + ret["comment"] += " and is running" else: - error = ', but it could not be started' + error = ", but it could not be started" try: - start_func = 'lxc.unfreeze' if c_state == 'frozen' \ - else 'lxc.start' - state['new'] = __salt__[start_func]( - name, path=path - )['state']['new'] - if state['new'] != 'running': - ret['result'] = False - ret['comment'] += error + start_func = "lxc.unfreeze" if c_state == "frozen" else "lxc.start" + state["new"] = __salt__[start_func](name, path=path)["state"]["new"] + if state["new"] != "running": + ret["result"] = False + ret["comment"] += error except (SaltInvocationError, CommandExecutionError) as exc: - ret['result'] = False - ret['comment'] += '{0}: {1}'.format(error, exc) + ret["result"] = False + ret["comment"] += "{0}: {1}".format(error, exc) else: - if state['old'] is None: - ret['comment'] += ', and the container was started' + if state["old"] is None: + ret["comment"] += ", and the container was started" else: - ret['comment'] = ( - 'Container \'{0}\' was {1}'.format( - name, - 'unfrozen' if state['old'] == 'frozen' - else 'started' - ) + ret["comment"] = "Container '{0}' was {1}".format( + name, "unfrozen" if state["old"] == "frozen" else "started" ) else: - c_state = __salt__['lxc.state'](name, path=path) - if c_state == 'stopped': - if state['old'] is not None: - ret['comment'] += ' and is stopped' + c_state = __salt__["lxc.state"](name, path=path) + if c_state == "stopped": + if state["old"] is not None: + ret["comment"] += " and is stopped" else: - error = ', but it could not be stopped' + error = ", but it could not be stopped" try: - state['new'] = __salt__['lxc.stop']( - name, path=path - )['state']['new'] - if state['new'] != 'stopped': - ret['result'] = False - ret['comment'] += error + state["new"] = __salt__["lxc.stop"](name, path=path)["state"]["new"] + if state["new"] != "stopped": + ret["result"] = False + ret["comment"] += error except (SaltInvocationError, CommandExecutionError) as exc: - ret['result'] = False - ret['comment'] += '{0}: {1}'.format(error, exc) + ret["result"] = False + ret["comment"] += "{0}: {1}".format(error, exc) else: - if state['old'] is None: - ret['comment'] += ', and the container was stopped' + if state["old"] is None: + ret["comment"] += ", and the container was stopped" else: - ret['comment'] = ('Container \'{0}\' was stopped' - .format(name)) + ret["comment"] = "Container '{0}' was stopped".format(name) - if 'new' not in state: + if "new" not in state: # Make sure we know the final state of the container before we return - state['new'] = __salt__['lxc.state'](name, path=path) - if state['old'] != state['new']: - ret['changes']['state'] = state + state["new"] = __salt__["lxc.state"](name, path=path) + if state["old"] != state["new"]: + ret["changes"]["state"] = state return ret def absent(name, stop=False, path=None): - ''' + """ Ensure a container is not present, destroying it if present name @@ -354,34 +344,36 @@ def absent(name, stop=False, path=None): web01: lxc.absent - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Container \'{0}\' does not exist'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Container '{0}' does not exist".format(name), + } - if not __salt__['lxc.exists'](name, path=path): + if not __salt__["lxc.exists"](name, path=path): return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Container \'{0}\' would be destroyed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Container '{0}' would be destroyed".format(name) return ret try: - result = __salt__['lxc.destroy'](name, stop=stop, path=path) + result = __salt__["lxc.destroy"](name, stop=stop, path=path) except (SaltInvocationError, CommandExecutionError) as exc: - ret['result'] = False - ret['comment'] = 'Failed to destroy container: {0}'.format(exc) + ret["result"] = False + ret["comment"] = "Failed to destroy container: {0}".format(exc) else: - ret['changes']['state'] = result['state'] - ret['comment'] = 'Container \'{0}\' was destroyed'.format(name) + ret["changes"]["state"] = result["state"] + ret["comment"] = "Container '{0}' was destroyed".format(name) return ret # Container state (running/frozen/stopped) def running(name, restart=False, path=None): - ''' + """ .. versionchanged:: 2015.5.0 The :mod:`lxc.started <salt.states.lxc.started>` state has been renamed to ``lxc.running`` @@ -415,73 +407,74 @@ def running(name, restart=False, path=None): web02: lxc.running: - restart: True - ''' - ret = {'name': name, - 'result': True, - 'comment': 'Container \'{0}\' is already running'.format(name), - 'changes': {}} + """ + ret = { + "name": name, + "result": True, + "comment": "Container '{0}' is already running".format(name), + "changes": {}, + } - state = {'old': __salt__['lxc.state'](name, path=path)} - if state['old'] is None: - ret['result'] = False - ret['comment'] = 'Container \'{0}\' does not exist'.format(name) + state = {"old": __salt__["lxc.state"](name, path=path)} + if state["old"] is None: + ret["result"] = False + ret["comment"] = "Container '{0}' does not exist".format(name) return ret - elif state['old'] == 'running' and not restart: + elif state["old"] == "running" and not restart: return ret - elif state['old'] == 'stopped' and restart: + elif state["old"] == "stopped" and restart: # No need to restart since container is not running restart = False if restart: - if state['old'] != 'stopped': - action = ('restart', 'restarted') + if state["old"] != "stopped": + action = ("restart", "restarted") else: - action = ('start', 'started') + action = ("start", "started") else: - if state['old'] == 'frozen': - action = ('unfreeze', 'unfrozen') + if state["old"] == "frozen": + action = ("unfreeze", "unfrozen") else: - action = ('start', 'started') + action = ("start", "started") - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Container \'{0}\' would be {1}' - .format(name, action[1])) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Container '{0}' would be {1}".format(name, action[1]) return ret try: - if state['old'] == 'frozen' and not restart: - result = __salt__['lxc.unfreeze'](name, path=path) + if state["old"] == "frozen" and not restart: + result = __salt__["lxc.unfreeze"](name, path=path) else: if restart: - result = __salt__['lxc.restart'](name, path=path) + result = __salt__["lxc.restart"](name, path=path) else: - result = __salt__['lxc.start'](name, path=path) + result = __salt__["lxc.start"](name, path=path) except (CommandExecutionError, SaltInvocationError) as exc: - ret['result'] = False - ret['comment'] = exc.strerror - state['new'] = __salt__['lxc.state'](name, path=path) + ret["result"] = False + ret["comment"] = exc.strerror + state["new"] = __salt__["lxc.state"](name, path=path) else: - state['new'] = result['state']['new'] - if state['new'] != 'running': - ret['result'] = False - ret['comment'] = ('Unable to {0} container \'{1}\'' - .format(action[0], name)) + state["new"] = result["state"]["new"] + if state["new"] != "running": + ret["result"] = False + ret["comment"] = "Unable to {0} container '{1}'".format(action[0], name) else: - ret['comment'] = ('Container \'{0}\' was successfully {1}' - .format(name, action[1])) + ret["comment"] = "Container '{0}' was successfully {1}".format( + name, action[1] + ) try: - ret['changes']['restarted'] = result['restarted'] + ret["changes"]["restarted"] = result["restarted"] except KeyError: pass - if state['old'] != state['new']: - ret['changes']['state'] = state + if state["old"] != state["new"]: + ret["changes"]["state"] = state return ret def frozen(name, start=True, path=None): - ''' + """ .. versionadded:: 2015.5.0 Ensure that a container is frozen @@ -514,61 +507,62 @@ def frozen(name, start=True, path=None): web02: lxc.frozen: - start: False - ''' - ret = {'name': name, - 'result': True, - 'comment': 'Container \'{0}\' is already frozen'.format(name), - 'changes': {}} + """ + ret = { + "name": name, + "result": True, + "comment": "Container '{0}' is already frozen".format(name), + "changes": {}, + } - state = {'old': __salt__['lxc.state'](name, path=path)} - if state['old'] is None: - ret['result'] = False - ret['comment'] = 'Container \'{0}\' does not exist'.format(name) - elif state['old'] == 'stopped' and not start: - ret['result'] = False - ret['comment'] = 'Container \'{0}\' is stopped'.format(name) + state = {"old": __salt__["lxc.state"](name, path=path)} + if state["old"] is None: + ret["result"] = False + ret["comment"] = "Container '{0}' does not exist".format(name) + elif state["old"] == "stopped" and not start: + ret["result"] = False + ret["comment"] = "Container '{0}' is stopped".format(name) - if ret['result'] is False or state['old'] == 'frozen': + if ret["result"] is False or state["old"] == "frozen": return ret - if state['old'] == 'stopped': - action = ('start and freeze', 'started and frozen') + if state["old"] == "stopped": + action = ("start and freeze", "started and frozen") else: - action = ('freeze', 'frozen') + action = ("freeze", "frozen") - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Container \'{0}\' would be {1}' - .format(name, action[1])) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Container '{0}' would be {1}".format(name, action[1]) return ret try: - result = __salt__['lxc.freeze'](name, start=start, path=path) + result = __salt__["lxc.freeze"](name, start=start, path=path) except (CommandExecutionError, SaltInvocationError) as exc: - ret['result'] = False - ret['comment'] = exc.strerror - state['new'] = __salt__['lxc.state'](name, path=path) + ret["result"] = False + ret["comment"] = exc.strerror + state["new"] = __salt__["lxc.state"](name, path=path) else: - state['new'] = result['state']['new'] - if state['new'] != 'frozen': - ret['result'] = False - ret['comment'] = ('Unable to {0} container \'{1}\'' - .format(action[0], name)) + state["new"] = result["state"]["new"] + if state["new"] != "frozen": + ret["result"] = False + ret["comment"] = "Unable to {0} container '{1}'".format(action[0], name) else: - ret['comment'] = ('Container \'{0}\' was successfully {1}' - .format(name, action[1])) + ret["comment"] = "Container '{0}' was successfully {1}".format( + name, action[1] + ) try: - ret['changes']['started'] = result['started'] + ret["changes"]["started"] = result["started"] except KeyError: pass - if state['old'] != state['new']: - ret['changes']['state'] = state + if state["old"] != state["new"]: + ret["changes"]["state"] = state return ret def stopped(name, kill=False, path=None): - ''' + """ Ensure that a container is stopped .. note:: @@ -600,54 +594,55 @@ def stopped(name, kill=False, path=None): web01: lxc.stopped - ''' - ret = {'name': name, - 'result': True, - 'comment': 'Container \'{0}\' is already stopped'.format(name), - 'changes': {}} + """ + ret = { + "name": name, + "result": True, + "comment": "Container '{0}' is already stopped".format(name), + "changes": {}, + } - state = {'old': __salt__['lxc.state'](name, path=path)} - if state['old'] is None: - ret['result'] = False - ret['comment'] = 'Container \'{0}\' does not exist'.format(name) + state = {"old": __salt__["lxc.state"](name, path=path)} + if state["old"] is None: + ret["result"] = False + ret["comment"] = "Container '{0}' does not exist".format(name) return ret - elif state['old'] == 'stopped': + elif state["old"] == "stopped": return ret if kill: - action = ('force-stop', 'force-stopped') + action = ("force-stop", "force-stopped") else: - action = ('stop', 'stopped') + action = ("stop", "stopped") - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Container \'{0}\' would be {1}' - .format(name, action[1])) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Container '{0}' would be {1}".format(name, action[1]) return ret try: - result = __salt__['lxc.stop'](name, kill=kill, path=path) + result = __salt__["lxc.stop"](name, kill=kill, path=path) except (CommandExecutionError, SaltInvocationError) as exc: - ret['result'] = False - ret['comment'] = exc.strerror - state['new'] = __salt__['lxc.state'](name, path=path) + ret["result"] = False + ret["comment"] = exc.strerror + state["new"] = __salt__["lxc.state"](name, path=path) else: - state['new'] = result['state']['new'] - if state['new'] != 'stopped': - ret['result'] = False - ret['comment'] = ('Unable to {0} container \'{1}\'' - .format(action[0], name)) + state["new"] = result["state"]["new"] + if state["new"] != "stopped": + ret["result"] = False + ret["comment"] = "Unable to {0} container '{1}'".format(action[0], name) else: - ret['comment'] = ('Container \'{0}\' was successfully {1}' - .format(name, action[1])) + ret["comment"] = "Container '{0}' was successfully {1}".format( + name, action[1] + ) - if state['old'] != state['new']: - ret['changes']['state'] = state + if state["old"] != state["new"]: + ret["changes"]["state"] = state return ret def set_pass(name, **kwargs): # pylint: disable=W0613 - ''' + """ .. deprecated:: 2015.5.0 This state function has been disabled, as it did not conform to design @@ -664,17 +659,19 @@ def set_pass(name, **kwargs): # pylint: disable=W0613 - name: set_pass - m_name: root - password: secret - ''' - return {'name': name, - 'comment': 'The lxc.set_pass state is no longer supported. Please ' - 'see the LXC states documentation for further ' - 'information.', - 'result': False, - 'changes': {}} + """ + return { + "name": name, + "comment": "The lxc.set_pass state is no longer supported. Please " + "see the LXC states documentation for further " + "information.", + "result": False, + "changes": {}, + } def edited_conf(name, lxc_conf=None, lxc_conf_unset=None): - ''' + """ .. warning:: This state is unsuitable for setting parameters that appear more than @@ -708,18 +705,20 @@ def edited_conf(name, lxc_conf=None, lxc_conf_unset=None): .. _`Issue #35523`: https://github.com/saltstack/salt/issues/35523 - ''' - if __opts__['test']: - return {'name': name, - 'comment': '{0} lxc.conf will be edited'.format(name), - 'result': True, - 'changes': {}} + """ + if __opts__["test"]: + return { + "name": name, + "comment": "{0} lxc.conf will be edited".format(name), + "result": True, + "changes": {}, + } if not lxc_conf_unset: lxc_conf_unset = {} if not lxc_conf: lxc_conf = {} - cret = __salt__['lxc.update_lxc_conf'](name, - lxc_conf=lxc_conf, - lxc_conf_unset=lxc_conf_unset) - cret['name'] = name + cret = __salt__["lxc.update_lxc_conf"]( + name, lxc_conf=lxc_conf, lxc_conf_unset=lxc_conf_unset + ) + cret["name"] = name return cret diff --git a/salt/states/lxd.py b/salt/states/lxd.py index cd965ae9a65..44fe258ee80 100644 --- a/salt/states/lxd.py +++ b/salt/states/lxd.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage LXD profiles. .. versionadded:: 2019.2.0 @@ -26,36 +26,46 @@ Manage LXD profiles. :maturity: new :depends: python-pylxd :platform: Linux -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os.path -# Import salt libs -from salt.exceptions import CommandExecutionError -from salt.exceptions import SaltInvocationError import salt.ext.six as six -__docformat__ = 'restructuredtext en' +# Import salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError -__virtualname__ = 'lxd' +__docformat__ = "restructuredtext en" -_password_config_key = 'core.trust_password' +__virtualname__ = "lxd" + +_password_config_key = "core.trust_password" def __virtual__(): - ''' + """ Only load if the lxd module is available in __salt__ - ''' - return __virtualname__ if 'lxd.version' in __salt__ else False + """ + if "lxd.version" in __salt__: + return __virtualname__ + return (False, "lxd module could not be loaded") -def init(name, storage_backend='dir', trust_password=None, - network_address=None, network_port=None, storage_create_device=None, - storage_create_loop=None, storage_pool=None, - done_file='%SALT_CONFIG_DIR%/lxd_initialized'): - ''' +def init( + name, + storage_backend="dir", + trust_password=None, + network_address=None, + network_port=None, + storage_create_device=None, + storage_create_loop=None, + storage_pool=None, + done_file="%SALT_CONFIG_DIR%/lxd_initialized", +): + """ Initalizes the LXD Daemon, as LXD doesn't tell if its initialized we touch the the done_file and check if it exist. @@ -89,51 +99,51 @@ def init(name, storage_backend='dir', trust_password=None, Path where we check that this method has been called, as it can run only once and theres currently no way to ask LXD if init has been called. - ''' + """ ret = { - 'name': name, - 'storage_backend': storage_backend, - 'trust_password': True if trust_password is not None else False, - 'network_address': network_address, - 'network_port': network_port, - 'storage_create_device': storage_create_device, - 'storage_create_loop': storage_create_loop, - 'storage_pool': storage_pool, - 'done_file': done_file, + "name": name, + "storage_backend": storage_backend, + "trust_password": True if trust_password is not None else False, + "network_address": network_address, + "network_port": network_port, + "storage_create_device": storage_create_device, + "storage_create_loop": storage_create_loop, + "storage_pool": storage_pool, + "done_file": done_file, } # TODO: Get a better path and don't hardcode '/etc/salt' - done_file = done_file.replace('%SALT_CONFIG_DIR%', '/etc/salt') + done_file = done_file.replace("%SALT_CONFIG_DIR%", "/etc/salt") if os.path.exists(done_file): # Success we already did that. - return _success(ret, 'LXD is already initialized') + return _success(ret, "LXD is already initialized") - if __opts__['test']: - return _success(ret, 'Would initialize LXD') + if __opts__["test"]: + return _success(ret, "Would initialize LXD") # We always touch the done_file, so when LXD is already initialized # we don't run this over and over. - __salt__['file.touch'](done_file) + __salt__["file.touch"](done_file) try: - __salt__['lxd.init']( + __salt__["lxd.init"]( storage_backend if storage_backend else None, trust_password if trust_password else None, network_address if network_address else None, network_port if network_port else None, storage_create_device if storage_create_device else None, storage_create_loop if storage_create_loop else None, - storage_pool if storage_pool else None + storage_pool if storage_pool else None, ) except CommandExecutionError as e: return _error(ret, six.text_type(e)) - return _success(ret, 'Initialized the LXD Daemon') + return _success(ret, "Initialized the LXD Daemon") def config_managed(name, value, force_password=False): - ''' + """ Manage a LXD Server config setting. name : @@ -148,52 +158,46 @@ def config_managed(name, value, force_password=False): As we can't retrieve the password from LXD we can't check if the current one is the same as the given one. - ''' + """ ret = { - 'name': name, - 'value': value if name != 'core.trust_password' else True, - 'force_password': force_password + "name": name, + "value": value if name != "core.trust_password" else True, + "force_password": force_password, } try: - current_value = __salt__['lxd.config_get'](name) + current_value = __salt__["lxd.config_get"](name) except CommandExecutionError as e: return _error(ret, six.text_type(e)) - if (name == _password_config_key and - (not force_password or not current_value)): + if name == _password_config_key and (not force_password or not current_value): msg = ( - ('"{0}" is already set ' - '(we don\'t known if the password is correct)').format(name) - ) + '"{0}" is already set ' "(we don't known if the password is correct)" + ).format(name) return _success(ret, msg) elif six.text_type(value) == current_value: - msg = ('"{0}" is already set to "{1}"'.format(name, value)) + msg = '"{0}" is already set to "{1}"'.format(name, value) return _success(ret, msg) - if __opts__['test']: + if __opts__["test"]: if name == _password_config_key: - msg = 'Would set the LXD password' - ret['changes'] = {'password': msg} + msg = "Would set the LXD password" + ret["changes"] = {"password": msg} return _unchanged(ret, msg) else: msg = 'Would set the "{0}" to "{1}"'.format(name, value) - ret['changes'] = {name: msg} + ret["changes"] = {name: msg} return _unchanged(ret, msg) - result_msg = '' + result_msg = "" try: - result_msg = __salt__['lxd.config_set'](name, value)[0] + result_msg = __salt__["lxd.config_set"](name, value)[0] if name == _password_config_key: - ret['changes'] = { - name: 'Changed the password' - } + ret["changes"] = {name: "Changed the password"} else: - ret['changes'] = { - name: 'Changed from "{0}" to {1}"'.format( - current_value, value - ) + ret["changes"] = { + name: 'Changed from "{0}" to {1}"'.format(current_value, value) } except CommandExecutionError as e: return _error(ret, six.text_type(e)) @@ -202,7 +206,7 @@ def config_managed(name, value, force_password=False): def authenticate(name, remote_addr, password, cert, key, verify_cert=True): - ''' + """ Authenticate with a remote peer. .. notes: @@ -240,19 +244,17 @@ def authenticate(name, remote_addr, password, cert, key, verify_cert=True): name: Ignore this. This is just here for salt. - ''' + """ ret = { - 'name': name, - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert + "name": name, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, } try: - client = __salt__['lxd.pylxd_client_get']( - remote_addr, cert, key, verify_cert - ) + client = __salt__["lxd.pylxd_client_get"](remote_addr, cert, key, verify_cert) except SaltInvocationError as e: return _error(ret, six.text_type(e)) except CommandExecutionError as e: @@ -262,45 +264,39 @@ def authenticate(name, remote_addr, password, cert, key, verify_cert=True): return _success(ret, "Already authenticated.") try: - result = __salt__['lxd.authenticate']( + result = __salt__["lxd.authenticate"]( remote_addr, password, cert, key, verify_cert ) except CommandExecutionError as e: return _error(ret, six.text_type(e)) if result is not True: - return _error( - ret, - "Failed to authenticate with peer: {0}".format(remote_addr) - ) + return _error(ret, "Failed to authenticate with peer: {0}".format(remote_addr)) msg = "Successfully authenticated with peer: {0}".format(remote_addr) - ret['changes'] = msg - return _success( - ret, - msg - ) + ret["changes"] = msg + return _success(ret, msg) def _success(ret, success_msg): - ret['result'] = True - ret['comment'] = success_msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = True + ret["comment"] = success_msg + if "changes" not in ret: + ret["changes"] = {} return ret def _unchanged(ret, msg): - ret['result'] = None - ret['comment'] = msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = None + ret["comment"] = msg + if "changes" not in ret: + ret["changes"] = {} return ret def _error(ret, err_msg): - ret['result'] = False - ret['comment'] = err_msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = False + ret["comment"] = err_msg + if "changes" not in ret: + ret["changes"] = {} return ret diff --git a/salt/states/lxd_container.py b/salt/states/lxd_container.py index ea1a5f6425e..5a379c6a5b8 100644 --- a/salt/states/lxd_container.py +++ b/salt/states/lxd_container.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Manage LXD containers. .. versionadded:: 2019.2.0 .. note: - - `pylxd`_ version 2 is required to let this work, + - :ref:`pylxd` version 2 is required to let this work, currently only available via pip. To install on Ubuntu: @@ -20,26 +20,26 @@ Manage LXD containers. - for the config_get() and config_get() methods you need to have lxd-client installed. -.. _: https://github.com/lxc/pylxd/blob/master/doc/source/installation.rst +.. _pylxd: https://github.com/lxc/pylxd/blob/master/doc/source/installation.rst :maintainer: René Jochum <rene@jochums.at> :maturity: new :depends: python-pylxd :platform: Linux -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import salt libs -from salt.exceptions import CommandExecutionError -from salt.exceptions import SaltInvocationError import salt.ext.six as six + +# Import salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext.six.moves import map -__docformat__ = 'restructuredtext en' +__docformat__ = "restructuredtext en" -__virtualname__ = 'lxd_container' +__virtualname__ = "lxd_container" # Keep in sync with: https://github.com/lxc/lxd/blob/master/shared/status.go CONTAINER_STATUS_RUNNING = 103 @@ -48,26 +48,30 @@ CONTAINER_STATUS_STOPPED = 102 def __virtual__(): - ''' + """ Only load if the lxd module is available in __salt__ - ''' - return __virtualname__ if 'lxd.version' in __salt__ else False + """ + if "lxd.version" in __salt__: + return __virtualname__ + return (False, "lxd module could not be loaded") -def present(name, - running=None, - source=None, - profiles=None, - config=None, - devices=None, - architecture='x86_64', - ephemeral=False, - restart_on_change=False, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' +def present( + name, + running=None, + source=None, + profiles=None, + config=None, + devices=None, + architecture="x86_64", + ephemeral=False, + restart_on_change=False, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Create the named container if it does not exist name @@ -81,27 +85,49 @@ def present(name, source : None Can be either a string containing an image alias: + + .. code-block:: none + "xenial/amd64" + or an dict with type "image" with alias: + + .. code-block:: python + {"type": "image", "alias": "xenial/amd64"} + or image with "fingerprint": + + .. code-block:: python + {"type": "image", "fingerprint": "SHA-256"} + or image with "properties": + + .. code-block:: python + {"type": "image", "properties": { "os": "ubuntu", "release": "14.04", "architecture": "x86_64" }} + or none: + + .. code-block:: python + {"type": "none"} + or copy: + + .. code-block:: python + {"type": "copy", "source": "my-old-container"} - profiles : ['default'] List of profiles to apply on this container @@ -109,6 +135,9 @@ def present(name, A config dict or None (None = unset). Can also be a list: + + .. code-block:: python + [{'key': 'boot.autostart', 'value': 1}, {'key': 'security.privileged', 'value': '1'}] @@ -117,15 +146,16 @@ def present(name, architecture : 'x86_64' Can be one of the following: - * unknown - * i686 - * x86_64 - * armv7l - * aarch64 - * ppc - * ppc64 - * ppc64le - * s390x + + * unknown + * i686 + * x86_64 + * armv7l + * aarch64 + * ppc + * ppc64 + * ppc64le + * s390x ephemeral : False Destroy this container after stop? @@ -158,34 +188,33 @@ def present(name, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' + """ if profiles is None: - profiles = ['default'] + profiles = ["default"] if source is None: source = {} ret = { - 'name': name, - 'running': running, - 'profiles': profiles, - 'source': source, - 'config': config, - 'devices': devices, - 'architecture': architecture, - 'ephemeral': ephemeral, - 'restart_on_change': restart_on_change, - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'changes': {} + "name": name, + "running": running, + "profiles": profiles, + "source": source, + "config": config, + "devices": devices, + "architecture": architecture, + "ephemeral": ephemeral, + "restart_on_change": restart_on_change, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "changes": {}, } container = None try: - container = __salt__['lxd.container_get']( + container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: @@ -195,24 +224,22 @@ def present(name, pass if container is None: - if __opts__['test']: + if __opts__["test"]: # Test is on, just return that we would create the container msg = 'Would create the container "{0}"'.format(name) - ret['changes'] = { - 'created': msg - } + ret["changes"] = {"created": msg} if running is True: - msg = msg + ' and start it.' - ret['changes']['started'] = ( - 'Would start the container "{0}"'.format(name) + msg = msg + " and start it." + ret["changes"]["started"] = 'Would start the container "{0}"'.format( + name ) - ret['changes'] = {'created': msg} + ret["changes"] = {"created": msg} return _unchanged(ret, msg) # create the container try: - __salt__['lxd.container_create']( + __salt__["lxd.container_create"]( name, source, profiles, @@ -224,32 +251,24 @@ def present(name, remote_addr, cert, key, - verify_cert + verify_cert, ) except CommandExecutionError as e: return _error(ret, six.text_type(e)) msg = 'Created the container "{0}"'.format(name) - ret['changes'] = { - 'created': msg - } + ret["changes"] = {"created": msg} if running is True: try: - __salt__['lxd.container_start']( - name, - remote_addr, - cert, - key, - verify_cert + __salt__["lxd.container_start"]( + name, remote_addr, cert, key, verify_cert ) except CommandExecutionError as e: return _error(ret, six.text_type(e)) - msg = msg + ' and started it.' - ret['changes'] = { - 'started': 'Started the container "{0}"'.format(name) - } + msg = msg + " and started it." + ret["changes"] = {"started": 'Started the container "{0}"'.format(name)} return _success(ret, msg) @@ -262,7 +281,7 @@ def present(name, profile_changes = [] # Removed profiles for k in old_profiles.difference(new_profiles): - if not __opts__['test']: + if not __opts__["test"]: profile_changes.append('Removed profile "{0}"'.format(k)) old_profiles.discard(k) else: @@ -270,7 +289,7 @@ def present(name, # Added profiles for k in new_profiles.difference(old_profiles): - if not __opts__['test']: + if not __opts__["test"]: profile_changes.append('Added profile "{0}"'.format(k)) old_profiles.add(k) else: @@ -278,95 +297,75 @@ def present(name, if profile_changes: container_changed = True - ret['changes']['profiles'] = profile_changes + ret["changes"]["profiles"] = profile_changes container.profiles = list(old_profiles) # Config and devices changes - config, devices = __salt__['lxd.normalize_input_values']( - config, - devices - ) - changes = __salt__['lxd.sync_config_devices']( - container, config, devices, __opts__['test'] + config, devices = __salt__["lxd.normalize_input_values"](config, devices) + changes = __salt__["lxd.sync_config_devices"]( + container, config, devices, __opts__["test"] ) if changes: container_changed = True - ret['changes'].update(changes) + ret["changes"].update(changes) - is_running = \ - container.status_code == CONTAINER_STATUS_RUNNING + is_running = container.status_code == CONTAINER_STATUS_RUNNING - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['lxd.pylxd_save_object'](container) + __salt__["lxd.pylxd_save_object"](container) except CommandExecutionError as e: return _error(ret, six.text_type(e)) if running != is_running: if running is True: - if __opts__['test']: - changes['running'] = 'Would start the container' + if __opts__["test"]: + changes["running"] = "Would start the container" return _unchanged( ret, - ('Container "{0}" would get changed ' - 'and started.').format(name) + ('Container "{0}" would get changed ' "and started.").format(name), ) else: container.start(wait=True) - changes['running'] = 'Started the container' + changes["running"] = "Started the container" elif running is False: - if __opts__['test']: - changes['stopped'] = 'Would stopped the container' + if __opts__["test"]: + changes["stopped"] = "Would stopped the container" return _unchanged( ret, - ('Container "{0}" would get changed ' - 'and stopped.').format(name) + ('Container "{0}" would get changed ' "and stopped.").format(name), ) else: container.stop(wait=True) - changes['stopped'] = 'Stopped the container' + changes["stopped"] = "Stopped the container" - if ((running is True or running is None) and - is_running and - restart_on_change and - container_changed): + if ( + (running is True or running is None) + and is_running + and restart_on_change + and container_changed + ): - if __opts__['test']: - changes['restarted'] = 'Would restart the container' - return _unchanged( - ret, - 'Would restart the container "{0}"'.format(name) - ) + if __opts__["test"]: + changes["restarted"] = "Would restart the container" + return _unchanged(ret, 'Would restart the container "{0}"'.format(name)) else: container.restart(wait=True) - changes['restarted'] = ( - 'Container "{0}" has been restarted'.format(name) - ) - return _success( - ret, - 'Container "{0}" has been restarted'.format(name) - ) + changes["restarted"] = 'Container "{0}" has been restarted'.format(name) + return _success(ret, 'Container "{0}" has been restarted'.format(name)) if not container_changed: - return _success(ret, 'No changes') + return _success(ret, "No changes") - if __opts__['test']: - return _unchanged( - ret, - 'Container "{0}" would get changed.'.format(name) - ) + if __opts__["test"]: + return _unchanged(ret, 'Container "{0}" would get changed.'.format(name)) - return _success(ret, '{0} changes'.format(len(ret['changes'].keys()))) + return _success(ret, "{0} changes".format(len(ret["changes"].keys()))) -def absent(name, - stop=False, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' +def absent(name, stop=False, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Ensure a LXD container is not present, destroying it if present name : @@ -400,21 +399,19 @@ def absent(name, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' + """ ret = { - 'name': name, - 'stop': stop, - - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'changes': {} + "name": name, + "stop": stop, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "changes": {}, } try: - container = __salt__['lxd.container_get']( + container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: @@ -423,30 +420,23 @@ def absent(name, # Container not found return _success(ret, 'Container "{0}" not found.'.format(name)) - if __opts__['test']: - ret['changes'] = { - 'removed': - 'Container "{0}" would get deleted.'.format(name) - } - return _unchanged(ret, ret['changes']['removed']) + if __opts__["test"]: + ret["changes"] = {"removed": 'Container "{0}" would get deleted.'.format(name)} + return _unchanged(ret, ret["changes"]["removed"]) if stop and container.status_code == CONTAINER_STATUS_RUNNING: container.stop(wait=True) container.delete(wait=True) - ret['changes']['deleted'] = \ - 'Container "{0}" has been deleted.'.format(name) - return _success(ret, ret['changes']['deleted']) + ret["changes"]["deleted"] = 'Container "{0}" has been deleted.'.format(name) + return _success(ret, ret["changes"]["deleted"]) -def running(name, - restart=False, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' +def running( + name, restart=False, remote_addr=None, cert=None, key=None, verify_cert=True +): + """ Ensure a LXD container is running and restart it if restart is True name : @@ -479,21 +469,19 @@ def running(name, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' + """ ret = { - 'name': name, - 'restart': restart, - - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'changes': {} + "name": name, + "restart": restart, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "changes": {}, } try: - container = __salt__['lxd.container_get']( + container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: @@ -506,43 +494,31 @@ def running(name, if is_running: if not restart: - return _success( - ret, - 'The container "{0}" is already running'.format(name) - ) + return _success(ret, 'The container "{0}" is already running'.format(name)) else: - if __opts__['test']: - ret['changes']['restarted'] = ( - 'Would restart the container "{0}"'.format(name) - ) - return _unchanged(ret, ret['changes']['restarted']) + if __opts__["test"]: + ret["changes"][ + "restarted" + ] = 'Would restart the container "{0}"'.format(name) + return _unchanged(ret, ret["changes"]["restarted"]) else: container.restart(wait=True) - ret['changes']['restarted'] = ( - 'Restarted the container "{0}"'.format(name) + ret["changes"]["restarted"] = 'Restarted the container "{0}"'.format( + name ) - return _success(ret, ret['changes']['restarted']) + return _success(ret, ret["changes"]["restarted"]) - if __opts__['test']: - ret['changes']['started'] = ( - 'Would start the container "{0}"'.format(name) - ) - return _unchanged(ret, ret['changes']['started']) + if __opts__["test"]: + ret["changes"]["started"] = 'Would start the container "{0}"'.format(name) + return _unchanged(ret, ret["changes"]["started"]) container.start(wait=True) - ret['changes']['started'] = ( - 'Started the container "{0}"'.format(name) - ) - return _success(ret, ret['changes']['started']) + ret["changes"]["started"] = 'Started the container "{0}"'.format(name) + return _success(ret, ret["changes"]["started"]) -def frozen(name, - start=True, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' +def frozen(name, start=True, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Ensure a LXD container is frozen, start and freeze it if start is true name : @@ -575,21 +551,19 @@ def frozen(name, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' + """ ret = { - 'name': name, - 'start': start, - - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'changes': {} + "name": name, + "start": start, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "changes": {}, } try: - container = __salt__['lxd.container_get']( + container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: @@ -604,46 +578,35 @@ def frozen(name, is_running = container.status_code == CONTAINER_STATUS_RUNNING if not is_running and not start: - return _error(ret, ( - 'Container "{0}" is not running and start is False, ' - 'cannot freeze it').format(name) + return _error( + ret, + ( + 'Container "{0}" is not running and start is False, ' "cannot freeze it" + ).format(name), ) elif not is_running and start: - if __opts__['test']: - ret['changes']['started'] = ( - 'Would start the container "{0}" and freeze it after' - .format(name) - ) - return _unchanged(ret, ret['changes']['started']) + if __opts__["test"]: + ret["changes"][ + "started" + ] = 'Would start the container "{0}" and freeze it after'.format(name) + return _unchanged(ret, ret["changes"]["started"]) else: container.start(wait=True) - ret['changes']['started'] = ( - 'Start the container "{0}"' - .format(name) - ) + ret["changes"]["started"] = 'Start the container "{0}"'.format(name) - if __opts__['test']: - ret['changes']['frozen'] = ( - 'Would freeze the container "{0}"'.format(name) - ) - return _unchanged(ret, ret['changes']['frozen']) + if __opts__["test"]: + ret["changes"]["frozen"] = 'Would freeze the container "{0}"'.format(name) + return _unchanged(ret, ret["changes"]["frozen"]) container.freeze(wait=True) - ret['changes']['frozen'] = ( - 'Froze the container "{0}"'.format(name) - ) + ret["changes"]["frozen"] = 'Froze the container "{0}"'.format(name) - return _success(ret, ret['changes']['frozen']) + return _success(ret, ret["changes"]["frozen"]) -def stopped(name, - kill=False, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' +def stopped(name, kill=False, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Ensure a LXD container is stopped, kill it if kill is true else stop it name : @@ -676,21 +639,19 @@ def stopped(name, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' + """ ret = { - 'name': name, - 'kill': kill, - - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'changes': {} + "name": name, + "kill": kill, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "changes": {}, } try: - container = __salt__['lxd.container_get']( + container = __salt__["lxd.container_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: @@ -702,28 +663,28 @@ def stopped(name, if container.status_code == CONTAINER_STATUS_STOPPED: return _success(ret, 'Container "{0}" is already stopped'.format(name)) - if __opts__['test']: - ret['changes']['stopped'] = \ - 'Would stop the container "{0}"'.format(name) - return _unchanged(ret, ret['changes']['stopped']) + if __opts__["test"]: + ret["changes"]["stopped"] = 'Would stop the container "{0}"'.format(name) + return _unchanged(ret, ret["changes"]["stopped"]) container.stop(force=kill, wait=True) - ret['changes']['stopped'] = \ - 'Stopped the container "{0}"'.format(name) - return _success(ret, ret['changes']['stopped']) + ret["changes"]["stopped"] = 'Stopped the container "{0}"'.format(name) + return _success(ret, ret["changes"]["stopped"]) -def migrated(name, - remote_addr, - cert, - key, - verify_cert, - src_remote_addr, - stop_and_start=False, - src_cert=None, - src_key=None, - src_verify_cert=None): - ''' Ensure a container is migrated to another host +def migrated( + name, + remote_addr, + cert, + key, + verify_cert, + src_remote_addr, + stop_and_start=False, + src_cert=None, + src_key=None, + src_verify_cert=None, +): + """ Ensure a container is migrated to another host If the container is running, it either must be shut down first (use stop_and_start=True) or criu must be installed @@ -784,28 +745,24 @@ def migrated(name, src_verify_cert : Wherever to verify the cert, if None we copy "verify_cert" - ''' + """ ret = { - 'name': name, - - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'src_remote_addr': src_remote_addr, - 'src_and_start': stop_and_start, - 'src_cert': src_cert, - 'src_key': src_key, - - 'changes': {} + "name": name, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "src_remote_addr": src_remote_addr, + "src_and_start": stop_and_start, + "src_cert": src_cert, + "src_key": src_key, + "changes": {}, } dest_container = None try: - dest_container = __salt__['lxd.container_get']( - name, remote_addr, cert, key, - verify_cert, _raw=True + dest_container = __salt__["lxd.container_get"]( + name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: return _error(ret, six.text_type(e)) @@ -814,16 +771,13 @@ def migrated(name, pass if dest_container is not None: - return _success( - ret, - 'Container "{0}" exists on the destination'.format(name) - ) + return _success(ret, 'Container "{0}" exists on the destination'.format(name)) if src_verify_cert is None: src_verify_cert = verify_cert try: - __salt__['lxd.container_get']( + __salt__["lxd.container_get"]( name, src_remote_addr, src_cert, src_key, src_verify_cert, _raw=True ) except CommandExecutionError as e: @@ -832,45 +786,53 @@ def migrated(name, # Container not found return _error(ret, 'Source Container "{0}" not found'.format(name)) - if __opts__['test']: - ret['changes']['migrated'] = ( + if __opts__["test"]: + ret["changes"]["migrated"] = ( 'Would migrate the container "{0}" from "{1}" to "{2}"' ).format(name, src_remote_addr, remote_addr) - return _unchanged(ret, ret['changes']['migrated']) + return _unchanged(ret, ret["changes"]["migrated"]) try: - __salt__['lxd.container_migrate']( - name, stop_and_start, remote_addr, cert, key, - verify_cert, src_remote_addr, src_cert, src_key, src_verify_cert + __salt__["lxd.container_migrate"]( + name, + stop_and_start, + remote_addr, + cert, + key, + verify_cert, + src_remote_addr, + src_cert, + src_key, + src_verify_cert, ) except CommandExecutionError as e: return _error(ret, six.text_type(e)) - ret['changes']['migrated'] = ( + ret["changes"]["migrated"] = ( 'Migrated the container "{0}" from "{1}" to "{2}"' ).format(name, src_remote_addr, remote_addr) - return _success(ret, ret['changes']['migrated']) + return _success(ret, ret["changes"]["migrated"]) def _success(ret, success_msg): - ret['result'] = True - ret['comment'] = success_msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = True + ret["comment"] = success_msg + if "changes" not in ret: + ret["changes"] = {} return ret def _unchanged(ret, msg): - ret['result'] = None - ret['comment'] = msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = None + ret["comment"] = msg + if "changes" not in ret: + ret["changes"] = {} return ret def _error(ret, err_msg): - ret['result'] = False - ret['comment'] = err_msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = False + ret["comment"] = err_msg + if "changes" not in ret: + ret["changes"] = {} return ret diff --git a/salt/states/lxd_image.py b/salt/states/lxd_image.py index 4388abacb15..980edf6df46 100644 --- a/salt/states/lxd_image.py +++ b/salt/states/lxd_image.py @@ -1,12 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Manage LXD images. .. versionadded:: 2019.2.0 +.. link: https://github.com/lxc/pylxd/blob/master/doc/source/installation.rst + .. note: - - `pylxd`_ version 2 is required to let this work, + - :role:`pylxd <link>` version 2 is required to let this work, currently only available via pip. To install on Ubuntu: @@ -20,45 +22,48 @@ Manage LXD images. - for the config_get() and config_get() methods you need to have lxd-client installed. -.. _: https://github.com/lxc/pylxd/blob/master/doc/source/installation.rst :maintainer: René Jochum <rene@jochums.at> :maturity: new :depends: python-pylxd :platform: Linux -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import salt libs -from salt.exceptions import CommandExecutionError -from salt.exceptions import SaltInvocationError import salt.ext.six as six + +# Import salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext.six.moves import map -__docformat__ = 'restructuredtext en' +__docformat__ = "restructuredtext en" -__virtualname__ = 'lxd_image' +__virtualname__ = "lxd_image" def __virtual__(): - ''' + """ Only load if the lxd module is available in __salt__ - ''' - return __virtualname__ if 'lxd.version' in __salt__ else False + """ + if "lxd.version" in __salt__: + return __virtualname__ + return (False, "lxd module could not be loaded") -def present(name, - source, - aliases=None, - public=None, - auto_update=None, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' +def present( + name, + source, + aliases=None, + public=None, + auto_update=None, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Ensure an image exists, copy it else from source name : @@ -150,30 +155,28 @@ def present(name, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' + """ if aliases is None: aliases = [] # Create a copy of aliases, since we're modifying it here aliases = aliases[:] ret = { - 'name': name, - 'source': source, - 'aliases': aliases, - 'public': public, - 'auto_update': auto_update, - - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'changes': {} + "name": name, + "source": source, + "aliases": aliases, + "public": public, + "auto_update": auto_update, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "changes": {}, } image = None try: - image = __salt__['lxd.image_get_by_alias']( + image = __salt__["lxd.image_get_by_alias"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: @@ -183,20 +186,20 @@ def present(name, pass if image is None: - if __opts__['test']: + if __opts__["test"]: # Test is on, just return that we would create the image msg = 'Would create the image "{0}"'.format(name) - ret['changes'] = {'created': msg} + ret["changes"] = {"created": msg} return _unchanged(ret, msg) try: - if source['type'] == 'lxd': - image = __salt__['lxd.image_copy_lxd']( - source['name'], - src_remote_addr=source['remote_addr'], - src_cert=source['cert'], - src_key=source['key'], - src_verify_cert=source.get('verify_cert', True), + if source["type"] == "lxd": + image = __salt__["lxd.image_copy_lxd"]( + source["name"], + src_remote_addr=source["remote_addr"], + src_cert=source["cert"], + src_key=source["key"], + src_verify_cert=source.get("verify_cert", True), remote_addr=remote_addr, cert=cert, key=key, @@ -204,28 +207,28 @@ def present(name, aliases=aliases, public=public, auto_update=auto_update, - _raw=True + _raw=True, ) - if source['type'] == 'file': - if 'saltenv' not in source: - source['saltenv'] = __env__ - image = __salt__['lxd.image_from_file']( - source['filename'], + if source["type"] == "file": + if "saltenv" not in source: + source["saltenv"] = __env__ + image = __salt__["lxd.image_from_file"]( + source["filename"], remote_addr=remote_addr, cert=cert, key=key, verify_cert=verify_cert, aliases=aliases, public=False if public is None else public, - saltenv=source['saltenv'], - _raw=True + saltenv=source["saltenv"], + _raw=True, ) - if source['type'] == 'simplestreams': - image = __salt__['lxd.image_from_simplestreams']( - source['server'], - source['name'], + if source["type"] == "simplestreams": + image = __salt__["lxd.image_from_simplestreams"]( + source["server"], + source["name"], remote_addr=remote_addr, cert=cert, key=key, @@ -233,12 +236,12 @@ def present(name, aliases=aliases, public=False if public is None else public, auto_update=False if auto_update is None else auto_update, - _raw=True + _raw=True, ) - if source['type'] == 'url': - image = __salt__['lxd.image_from_url']( - source['url'], + if source["type"] == "url": + image = __salt__["lxd.image_from_url"]( + source["url"], remote_addr=remote_addr, cert=cert, key=key, @@ -246,7 +249,7 @@ def present(name, aliases=aliases, public=False if public is None else public, auto_update=False if auto_update is None else auto_update, - _raw=True + _raw=True, ) except CommandExecutionError as e: return _error(ret, six.text_type(e)) @@ -255,55 +258,50 @@ def present(name, if name not in aliases: aliases.append(name) - old_aliases = set([six.text_type(a['name']) for a in image.aliases]) + old_aliases = set([six.text_type(a["name"]) for a in image.aliases]) new_aliases = set(map(six.text_type, aliases)) alias_changes = [] # Removed aliases for k in old_aliases.difference(new_aliases): - if not __opts__['test']: - __salt__['lxd.image_alias_delete'](image, k) + if not __opts__["test"]: + __salt__["lxd.image_alias_delete"](image, k) alias_changes.append('Removed alias "{0}"'.format(k)) else: alias_changes.append('Would remove alias "{0}"'.format(k)) # New aliases for k in new_aliases.difference(old_aliases): - if not __opts__['test']: - __salt__['lxd.image_alias_add'](image, k, '') + if not __opts__["test"]: + __salt__["lxd.image_alias_add"](image, k, "") alias_changes.append('Added alias "{0}"'.format(k)) else: alias_changes.append('Would add alias "{0}"'.format(k)) if alias_changes: - ret['changes']['aliases'] = alias_changes + ret["changes"]["aliases"] = alias_changes # Set public if public is not None and image.public != public: - if not __opts__['test']: - ret['changes']['public'] = \ - 'Setting the image public to {0!s}'.format(public) + if not __opts__["test"]: + ret["changes"]["public"] = "Setting the image public to {0!s}".format( + public + ) image.public = public - __salt__['lxd.pylxd_save_object'](image) + __salt__["lxd.pylxd_save_object"](image) else: - ret['changes']['public'] = \ - 'Would set public to {0!s}'.format(public) + ret["changes"]["public"] = "Would set public to {0!s}".format(public) - if __opts__['test'] and ret['changes']: + if __opts__["test"] and ret["changes"]: return _unchanged( - ret, - 'Would do {0} changes'.format(len(ret['changes'].keys())) + ret, "Would do {0} changes".format(len(ret["changes"].keys())) ) - return _success(ret, '{0} changes'.format(len(ret['changes'].keys()))) + return _success(ret, "{0} changes".format(len(ret["changes"].keys()))) -def absent(name, - remote_addr=None, - cert=None, - key=None, - verify_cert=True): - ''' +def absent(name, remote_addr=None, cert=None, key=None, verify_cert=True): + """ name : An alias or fingerprint of the image to check and delete. @@ -331,27 +329,25 @@ def absent(name, Wherever to verify the cert, this is by default True but in the most cases you want to set it off as LXD normaly uses self-signed certificates. - ''' + """ ret = { - 'name': name, - - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'changes': {} + "name": name, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "changes": {}, } image = None try: - image = __salt__['lxd.image_get_by_alias']( + image = __salt__["lxd.image_get_by_alias"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: return _error(ret, six.text_type(e)) except SaltInvocationError as e: try: - image = __salt__['lxd.image_get']( + image = __salt__["lxd.image_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: @@ -359,43 +355,35 @@ def absent(name, except SaltInvocationError as e: return _success(ret, 'Image "{0}" not found.'.format(name)) - if __opts__['test']: - ret['changes'] = { - 'removed': - 'Image "{0}" would get deleted.'.format(name) - } - return _success(ret, ret['changes']['removed']) + if __opts__["test"]: + ret["changes"] = {"removed": 'Image "{0}" would get deleted.'.format(name)} + return _success(ret, ret["changes"]["removed"]) - __salt__['lxd.image_delete']( - image - ) + __salt__["lxd.image_delete"](image) - ret['changes'] = { - 'removed': - 'Image "{0}" has been deleted.'.format(name) - } - return _success(ret, ret['changes']['removed']) + ret["changes"] = {"removed": 'Image "{0}" has been deleted.'.format(name)} + return _success(ret, ret["changes"]["removed"]) def _success(ret, success_msg): - ret['result'] = True - ret['comment'] = success_msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = True + ret["comment"] = success_msg + if "changes" not in ret: + ret["changes"] = {} return ret def _unchanged(ret, msg): - ret['result'] = None - ret['comment'] = msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = None + ret["comment"] = msg + if "changes" not in ret: + ret["changes"] = {} return ret def _error(ret, err_msg): - ret['result'] = False - ret['comment'] = err_msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = False + ret["comment"] = err_msg + if "changes" not in ret: + ret["changes"] = {} return ret diff --git a/salt/states/lxd_profile.py b/salt/states/lxd_profile.py index 33b371a38f1..bb3b25a7f69 100644 --- a/salt/states/lxd_profile.py +++ b/salt/states/lxd_profile.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage LXD profiles. .. versionadded:: 2019.2.0 @@ -26,31 +26,41 @@ Manage LXD profiles. :maturity: new :depends: python-pylxd :platform: Linux -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import salt libs -from salt.exceptions import CommandExecutionError -from salt.exceptions import SaltInvocationError import salt.ext.six as six -__docformat__ = 'restructuredtext en' +# Import salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError -__virtualname__ = 'lxd_profile' +__docformat__ = "restructuredtext en" + +__virtualname__ = "lxd_profile" def __virtual__(): - ''' + """ Only load if the lxd module is available in __salt__ - ''' - return __virtualname__ if 'lxd.version' in __salt__ else False + """ + if "lxd.version" in __salt__: + return __virtualname__ + return (False, "lxd module could not be loaded") -def present(name, description=None, config=None, devices=None, - remote_addr=None, cert=None, key=None, verify_cert=True): - ''' +def present( + name, + description=None, + config=None, + devices=None, + remote_addr=None, + cert=None, + key=None, + verify_cert=True, +): + """ Creates or updates LXD profiles name : @@ -99,24 +109,22 @@ def present(name, description=None, config=None, devices=None, .. _lxd-docs: https://github.com/lxc/lxd/blob/master/doc/rest-api.md#post-10 .. _requests-docs: http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification # noqa - ''' + """ ret = { - 'name': name, - 'description': description, - 'config': config, - 'devices': devices, - - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'changes': {} + "name": name, + "description": description, + "config": config, + "devices": devices, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "changes": {}, } profile = None try: - profile = __salt__['lxd.profile_get']( + profile = __salt__["lxd.profile_get"]( name, remote_addr, cert, key, verify_cert, _raw=True ) except CommandExecutionError as e: @@ -129,72 +137,58 @@ def present(name, description=None, config=None, devices=None, description = six.text_type() if profile is None: - if __opts__['test']: + if __opts__["test"]: # Test is on, just return that we would create the profile msg = 'Would create the profile "{0}"'.format(name) - ret['changes'] = {'created': msg} + ret["changes"] = {"created": msg} return _unchanged(ret, msg) # Create the profile try: - __salt__['lxd.profile_create']( - name, - config, - devices, - description, - remote_addr, - cert, - key, - verify_cert + __salt__["lxd.profile_create"]( + name, config, devices, description, remote_addr, cert, key, verify_cert ) except CommandExecutionError as e: return _error(ret, six.text_type(e)) msg = 'Profile "{0}" has been created'.format(name) - ret['changes'] = {'created': msg} + ret["changes"] = {"created": msg} return _success(ret, msg) - config, devices = __salt__['lxd.normalize_input_values']( - config, - devices - ) + config, devices = __salt__["lxd.normalize_input_values"](config, devices) # # Description change # if six.text_type(profile.description) != six.text_type(description): - ret['changes']['description'] = ( + ret["changes"]["description"] = ( 'Description changed, from "{0}" to "{1}".' ).format(profile.description, description) profile.description = description - changes = __salt__['lxd.sync_config_devices']( - profile, config, devices, __opts__['test'] + changes = __salt__["lxd.sync_config_devices"]( + profile, config, devices, __opts__["test"] ) - ret['changes'].update(changes) + ret["changes"].update(changes) - if not ret['changes']: - return _success(ret, 'No changes') + if not ret["changes"]: + return _success(ret, "No changes") - if __opts__['test']: - return _unchanged( - ret, - 'Profile "{0}" would get changed.'.format(name) - ) + if __opts__["test"]: + return _unchanged(ret, 'Profile "{0}" would get changed.'.format(name)) try: - __salt__['lxd.pylxd_save_object'](profile) + __salt__["lxd.pylxd_save_object"](profile) except CommandExecutionError as e: return _error(ret, six.text_type(e)) - return _success(ret, '{0} changes'.format(len(ret['changes'].keys()))) + return _success(ret, "{0} changes".format(len(ret["changes"].keys()))) -def absent(name, remote_addr=None, cert=None, - key=None, verify_cert=True): - ''' +def absent(name, remote_addr=None, cert=None, key=None, verify_cert=True): + """ Ensure a LXD profile is not present, removing it if present. name : @@ -228,70 +222,58 @@ def absent(name, remote_addr=None, cert=None, See the `requests-docs` for the SSL stuff. .. _requests-docs: http://docs.python-requests.org/en/master/user/advanced/#ssl-cert-verification # noqa - ''' + """ ret = { - 'name': name, - - 'remote_addr': remote_addr, - 'cert': cert, - 'key': key, - 'verify_cert': verify_cert, - - 'changes': {} + "name": name, + "remote_addr": remote_addr, + "cert": cert, + "key": key, + "verify_cert": verify_cert, + "changes": {}, } - if __opts__['test']: + if __opts__["test"]: try: - __salt__['lxd.profile_get']( - name, remote_addr, cert, key, verify_cert - ) + __salt__["lxd.profile_get"](name, remote_addr, cert, key, verify_cert) except CommandExecutionError as e: return _error(ret, six.text_type(e)) except SaltInvocationError as e: # Profile not found return _success(ret, 'Profile "{0}" not found.'.format(name)) - ret['changes'] = { - 'removed': - 'Profile "{0}" would get deleted.'.format(name) - } - return _success(ret, ret['changes']['removed']) + ret["changes"] = {"removed": 'Profile "{0}" would get deleted.'.format(name)} + return _success(ret, ret["changes"]["removed"]) try: - __salt__['lxd.profile_delete']( - name, remote_addr, cert, key, verify_cert - ) + __salt__["lxd.profile_delete"](name, remote_addr, cert, key, verify_cert) except CommandExecutionError as e: return _error(ret, six.text_type(e)) except SaltInvocationError as e: # Profile not found return _success(ret, 'Profile "{0}" not found.'.format(name)) - ret['changes'] = { - 'removed': - 'Profile "{0}" has been deleted.'.format(name) - } - return _success(ret, ret['changes']['removed']) + ret["changes"] = {"removed": 'Profile "{0}" has been deleted.'.format(name)} + return _success(ret, ret["changes"]["removed"]) def _success(ret, success_msg): - ret['result'] = True - ret['comment'] = success_msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = True + ret["comment"] = success_msg + if "changes" not in ret: + ret["changes"] = {} return ret def _unchanged(ret, msg): - ret['result'] = None - ret['comment'] = msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = None + ret["comment"] = msg + if "changes" not in ret: + ret["changes"] = {} return ret def _error(ret, err_msg): - ret['result'] = False - ret['comment'] = err_msg - if 'changes' not in ret: - ret['changes'] = {} + ret["result"] = False + ret["comment"] = err_msg + if "changes" not in ret: + ret["changes"] = {} return ret diff --git a/salt/states/mac_assistive.py b/salt/states/mac_assistive.py index c0ad6bcf079..3f827672768 100644 --- a/salt/states/mac_assistive.py +++ b/salt/states/mac_assistive.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Allows you to manage assistive access on macOS minions with 10.9+ ================================================================= @@ -10,10 +10,11 @@ Install, enable and disable assistive access on macOS minions /usr/bin/osacript: assistive.installed: - enabled: True -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -26,17 +27,18 @@ __virtualname__ = "assistive" def __virtual__(): - ''' + """ Only work on Mac OS - ''' - if salt.utils.platform.is_darwin() \ - and _LooseVersion(__grains__['osrelease']) >= _LooseVersion('10.9'): + """ + if salt.utils.platform.is_darwin() and _LooseVersion( + __grains__["osrelease"] + ) >= _LooseVersion("10.9"): return True - return False + return (False, "Only supported on Mac OS 10.9+") def installed(name, enabled=True): - ''' + """ Make sure that we have the given bundle ID or path to command installed in the assistive access panel. @@ -46,25 +48,22 @@ def installed(name, enabled=True): enable Should assistive access be enabled on this application? - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_installed = __salt__['assistive.installed'](name) + is_installed = __salt__["assistive.installed"](name) if is_installed: - is_enabled = __salt__['assistive.enabled'](name) + is_enabled = __salt__["assistive.enabled"](name) if enabled != is_enabled: - __salt__['assistive.enable'](name, enabled) - ret['comment'] = 'Updated enable to {0}'.format(enabled) + __salt__["assistive.enable"](name, enabled) + ret["comment"] = "Updated enable to {0}".format(enabled) else: - ret['comment'] = 'Already in the correct state' + ret["comment"] = "Already in the correct state" else: - __salt__['assistive.install'](name, enabled) - ret['comment'] = 'Installed {0} into the assistive access panel'.format(name) + __salt__["assistive.install"](name, enabled) + ret["comment"] = "Installed {0} into the assistive access panel".format(name) return ret diff --git a/salt/states/mac_keychain.py b/salt/states/mac_keychain.py index 1e27187f379..a70ff729680 100644 --- a/salt/states/mac_keychain.py +++ b/salt/states/mac_keychain.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installing of certificates to the keychain ========================================== @@ -10,8 +10,8 @@ Install certificats to the macOS keychain /mnt/test.p12: keychain.installed: - password: test123 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -21,20 +21,20 @@ import os import salt.utils.platform log = logging.getLogger(__name__) -__virtualname__ = 'keychain' +__virtualname__ = "keychain" def __virtual__(): - ''' + """ Only work on Mac OS - ''' + """ if salt.utils.platform.is_darwin(): return __virtualname__ - return False + return (False, "Only supported on Mac OS") def installed(name, password, keychain="/Library/Keychains/System.keychain", **kwargs): - ''' + """ Install a p12 certificate file into the macOS keychain name @@ -55,51 +55,62 @@ def installed(name, password, keychain="/Library/Keychains/System.keychain", **k If your keychain is likely to be locked pass the password and it will be unlocked before running the import - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - if 'http' in name or 'salt' in name: - name = __salt__['cp.cache_file'](name) + if "http" in name or "salt" in name: + name = __salt__["cp.cache_file"](name) - certs = __salt__['keychain.list_certs'](keychain) - friendly_name = __salt__['keychain.get_friendly_name'](name, password) + certs = __salt__["keychain.list_certs"](keychain) + friendly_name = __salt__["keychain.get_friendly_name"](name, password) if friendly_name in certs: - file_hash = __salt__['keychain.get_hash'](name, password) - keychain_hash = __salt__['keychain.get_hash'](friendly_name) + file_hash = __salt__["keychain.get_hash"](name, password) + keychain_hash = __salt__["keychain.get_hash"](friendly_name) if file_hash != keychain_hash: - out = __salt__['keychain.uninstall'](friendly_name, keychain, - keychain_password=kwargs.get('keychain_password')) + out = __salt__["keychain.uninstall"]( + friendly_name, + keychain, + keychain_password=kwargs.get("keychain_password"), + ) if "unable" not in out: - ret['comment'] += "Found a certificate with the same name but different hash, removing it.\n" - ret['changes']['uninstalled'] = friendly_name + ret[ + "comment" + ] += "Found a certificate with the same name but different hash, removing it.\n" + ret["changes"]["uninstalled"] = friendly_name # Reset the certs found - certs = __salt__['keychain.list_certs'](keychain) + certs = __salt__["keychain.list_certs"](keychain) else: - ret['result'] = False - ret['comment'] += "Found an incorrect cert but was unable to uninstall it: {0}".format(friendly_name) + ret["result"] = False + ret[ + "comment" + ] += "Found an incorrect cert but was unable to uninstall it: {0}".format( + friendly_name + ) return ret if friendly_name not in certs: - out = __salt__['keychain.install'](name, password, keychain, **kwargs) + out = __salt__["keychain.install"](name, password, keychain, **kwargs) if "imported" in out: - ret['changes']['installed'] = friendly_name + ret["changes"]["installed"] = friendly_name else: - ret['result'] = False - ret['comment'] += "Failed to install {0}".format(friendly_name) + ret["result"] = False + ret["comment"] += "Failed to install {0}".format(friendly_name) else: - ret['comment'] += "{0} already installed.".format(friendly_name) + ret["comment"] += "{0} already installed.".format(friendly_name) return ret -def uninstalled(name, password, keychain="/Library/Keychains/System.keychain", keychain_password=None): - ''' +def uninstalled( + name, + password, + keychain="/Library/Keychains/System.keychain", + keychain_password=None, +): + """ Uninstall a p12 certificate file from the macOS keychain name @@ -122,37 +133,34 @@ def uninstalled(name, password, keychain="/Library/Keychains/System.keychain", k If your keychain is likely to be locked pass the password and it will be unlocked before running the import - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - certs = __salt__['keychain.list_certs'](keychain) + certs = __salt__["keychain.list_certs"](keychain) if ".p12" in name: - if 'http' in name or 'salt' in name: - name = __salt__['cp.cache_file'](name) + if "http" in name or "salt" in name: + name = __salt__["cp.cache_file"](name) - friendly_name = __salt__['keychain.get_friendly_name'](name, password) + friendly_name = __salt__["keychain.get_friendly_name"](name, password) else: friendly_name = name if friendly_name in certs: - out = __salt__['keychain.uninstall'](friendly_name, keychain, keychain_password) + out = __salt__["keychain.uninstall"](friendly_name, keychain, keychain_password) if "unable" not in out: - ret['changes']['uninstalled'] = friendly_name + ret["changes"]["uninstalled"] = friendly_name else: - ret['result'] = False - ret['comment'] += "Failed to uninstall {0}".format(friendly_name) + ret["result"] = False + ret["comment"] += "Failed to uninstall {0}".format(friendly_name) else: - ret['comment'] += "{0} already uninstalled.".format(friendly_name) + ret["comment"] += "{0} already uninstalled.".format(friendly_name) return ret def default_keychain(name, domain="user", user=None): - ''' + """ Set the default keychain to use name @@ -164,26 +172,23 @@ def default_keychain(name, domain="user", user=None): user The user to run as - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not os.path.exists(name): - ret['result'] = False - ret['comment'] += "Keychain not found at {0}".format(name) + ret["result"] = False + ret["comment"] += "Keychain not found at {0}".format(name) else: - out = __salt__['keychain.get_default_keychain'](user, domain) + out = __salt__["keychain.get_default_keychain"](user, domain) if name in out: - ret['comment'] += "{0} was already the default keychain.".format(name) + ret["comment"] += "{0} was already the default keychain.".format(name) else: - out = __salt__['keychain.set_default_keychain'](name, domain, user) + out = __salt__["keychain.set_default_keychain"](name, domain, user) if len(out) == 0: - ret['changes']['default'] = name + ret["changes"]["default"] = name else: - ret['result'] = False - ret['comment'] = 'Failed to install keychain. {0}'.format(out) + ret["result"] = False + ret["comment"] = "Failed to install keychain. {0}".format(out) return ret diff --git a/salt/states/mac_xattr.py b/salt/states/mac_xattr.py index 3fc6b864f3d..a9c88845bed 100644 --- a/salt/states/mac_xattr.py +++ b/salt/states/mac_xattr.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Allows you to manage extended attributes on files or directories ================================================================ @@ -12,8 +12,8 @@ Install, enable and disable assistive access on macOS minions - attributes: - com.file.attr=test - com.apple.quarantine=0x00001111 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -24,16 +24,16 @@ __virtualname__ = "xattr" def __virtual__(): - ''' + """ Only work on Mac OS - ''' - if __grains__['os'] in ['MacOS', 'Darwin']: + """ + if __grains__["os"] in ["MacOS", "Darwin"]: return __virtualname__ - return False + return (False, "Only supported on Mac OS") def exists(name, attributes): - ''' + """ Make sure the given attributes exist on the file/directory name @@ -44,18 +44,15 @@ def exists(name, attributes): an array, with key and value split with an equals sign, if you want to specify a hex value then add 0x to the beginning of the value. - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not os.path.exists(name): - ret['result'] = False - ret['comment'] = "File or directory doesn't exist" + ret["result"] = False + ret["comment"] = "File or directory doesn't exist" return ret - current_attrs = __salt__['xattr.list'](name) + current_attrs = __salt__["xattr.list"](name) current_ids = current_attrs.keys() for attr in attributes: @@ -64,29 +61,34 @@ def exists(name, attributes): if attr_hex: # Remove spaces and new lines so we can match these - current_attrs[attr_id] = __salt__['xattr.read'](name, attr_id, hex=True).replace(" ", "").replace("\n", "") + current_attrs[attr_id] = ( + __salt__["xattr.read"](name, attr_id, hex=True) + .replace(" ", "") + .replace("\n", "") + ) attr_val = attr_val[2:].replace(" ", "") if attr_id not in current_attrs: value_matches = False else: - value_matches = ((current_attrs[attr_id] == attr_val) or - (attr_hex and current_attrs[attr_id] == attr_val)) + value_matches = (current_attrs[attr_id] == attr_val) or ( + attr_hex and current_attrs[attr_id] == attr_val + ) if attr_id in current_ids and value_matches: continue else: - ret['changes'][attr_id] = attr_val - __salt__['xattr.write'](name, attr_id, attr_val, attr_hex) + ret["changes"][attr_id] = attr_val + __salt__["xattr.write"](name, attr_id, attr_val, attr_hex) - if len(ret['changes'].keys()) == 0: - ret['comment'] = 'All values existed correctly.' + if len(ret["changes"].keys()) == 0: + ret["comment"] = "All values existed correctly." return ret def delete(name, attributes): - ''' + """ Make sure the given attributes are deleted from the file/directory name @@ -95,27 +97,24 @@ def delete(name, attributes): attributes The attributes that should be removed from the file/directory, this is accepted as an array. - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not os.path.exists(name): - ret['result'] = False - ret['comment'] = "File or directory doesn't exist" + ret["result"] = False + ret["comment"] = "File or directory doesn't exist" return ret - current_attrs = __salt__['xattr.list'](name) + current_attrs = __salt__["xattr.list"](name) current_ids = current_attrs.keys() for attr in attributes: if attr in current_ids: - __salt__['xattr.delete'](name, attr) - ret['changes'][attr] = 'delete' + __salt__["xattr.delete"](name, attr) + ret["changes"][attr] = "delete" - if len(ret['changes'].keys()) == 0: - ret['comment'] = 'All attributes were already deleted.' + if len(ret["changes"].keys()) == 0: + ret["comment"] = "All attributes were already deleted." return ret diff --git a/salt/states/macdefaults.py b/salt/states/macdefaults.py index b94c3ea18b1..58fa6104beb 100644 --- a/salt/states/macdefaults.py +++ b/salt/states/macdefaults.py @@ -1,32 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" Writing/reading defaults from a macOS minion ============================================ -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.platform log = logging.getLogger(__name__) -__virtualname__ = 'macdefaults' +__virtualname__ = "macdefaults" def __virtual__(): - ''' + """ Only work on Mac OS - ''' + """ if salt.utils.platform.is_darwin(): return __virtualname__ - return False + return (False, "Only supported on Mac OS") -def write(name, domain, value, vtype='string', user=None): - ''' +def write(name, domain, value, vtype="string", user=None): + """ Write a default to the system name @@ -46,11 +47,8 @@ def write(name, domain, value, vtype='string', user=None): The user to write the defaults to - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} def safe_cast(val, to_type, default=None): try: @@ -58,28 +56,34 @@ def write(name, domain, value, vtype='string', user=None): except ValueError: return default - current_value = __salt__['macdefaults.read'](domain, name, user) + current_value = __salt__["macdefaults.read"](domain, name, user) - if (vtype in ['bool', 'boolean']) and ((value in [True, 'TRUE', 'YES'] and current_value == '1') or - (value in [False, 'FALSE', 'NO'] and current_value == '0')): - ret['comment'] += '{0} {1} is already set to {2}'.format(domain, name, value) - elif vtype in ['int', 'integer'] and safe_cast(current_value, int) == safe_cast(value, int): - ret['comment'] += '{0} {1} is already set to {2}'.format(domain, name, value) + if (vtype in ["bool", "boolean"]) and ( + (value in [True, "TRUE", "YES"] and current_value == "1") + or (value in [False, "FALSE", "NO"] and current_value == "0") + ): + ret["comment"] += "{0} {1} is already set to {2}".format(domain, name, value) + elif vtype in ["int", "integer"] and safe_cast(current_value, int) == safe_cast( + value, int + ): + ret["comment"] += "{0} {1} is already set to {2}".format(domain, name, value) elif current_value == value: - ret['comment'] += '{0} {1} is already set to {2}'.format(domain, name, value) + ret["comment"] += "{0} {1} is already set to {2}".format(domain, name, value) else: - out = __salt__['macdefaults.write'](domain, name, value, vtype, user) - if out['retcode'] != 0: - ret['result'] = False - ret['comment'] = 'Failed to write default. {0}'.format(out['stdout']) + out = __salt__["macdefaults.write"](domain, name, value, vtype, user) + if out["retcode"] != 0: + ret["result"] = False + ret["comment"] = "Failed to write default. {0}".format(out["stdout"]) else: - ret['changes']['written'] = '{0} {1} is set to {2}'.format(domain, name, value) + ret["changes"]["written"] = "{0} {1} is set to {2}".format( + domain, name, value + ) return ret def absent(name, domain, user=None): - ''' + """ Make sure the defaults value is absent name @@ -92,17 +96,14 @@ def absent(name, domain, user=None): The user to write the defaults to - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - out = __salt__['macdefaults.delete'](domain, name, user) + out = __salt__["macdefaults.delete"](domain, name, user) - if out['retcode'] != 0: - ret['comment'] += "{0} {1} is already absent".format(domain, name) + if out["retcode"] != 0: + ret["comment"] += "{0} {1} is already absent".format(domain, name) else: - ret['changes']['absent'] = "{0} {1} is now absent".format(domain, name) + ret["changes"]["absent"] = "{0} {1} is now absent".format(domain, name) return ret diff --git a/salt/states/macpackage.py b/salt/states/macpackage.py index e3318303c09..e6d0ab7f86b 100644 --- a/salt/states/macpackage.py +++ b/salt/states/macpackage.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installing of mac pkg files =========================== @@ -22,9 +22,10 @@ Install any kind of pkg, dmg or app file on macOS: - target: /Applications/Xcode.app - version_check: xcodebuild -version=Xcode 7.1\\n.*7B91b -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import os import re @@ -38,17 +39,29 @@ __virtualname__ = "macpackage" def __virtual__(): - ''' + """ Only work on Mac OS - ''' + """ if salt.utils.platform.is_darwin(): return __virtualname__ - return False + return (False, "Only supported on Mac OS") -def installed(name, target="LocalSystem", dmg=False, store=False, app=False, mpkg=False, user=None, onlyif=None, - unless=None, force=False, allow_untrusted=False, version_check=None): - ''' +def installed( + name, + target="LocalSystem", + dmg=False, + store=False, + app=False, + mpkg=False, + user=None, + onlyif=None, + unless=None, + force=False, + allow_untrusted=False, + version_check=None, +): + """ Install a Mac OS Package from a pkg or dmg file, if given a dmg file it will first be mounted in a temporary location @@ -95,20 +108,17 @@ def installed(name, target="LocalSystem", dmg=False, store=False, app=False, mpk version_check: python --version_check=2.7.[0-9] - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} found = [] installing = [] real_pkg = name # Check onlyif, unless first - run_check_cmd_kwargs = {'runas': user, 'python_shell': True} - if 'shell' in __grains__: - run_check_cmd_kwargs['shell'] = __grains__['shell'] + run_check_cmd_kwargs = {"runas": user, "python_shell": True} + if "shell" in __grains__: + run_check_cmd_kwargs["shell"] = __grains__["shell"] cret = _mod_run_check(run_check_cmd_kwargs, onlyif, unless) @@ -123,16 +133,20 @@ def installed(name, target="LocalSystem", dmg=False, store=False, app=False, mpk version_cmd = split[0] expected_version = split[1] try: - version_out = __salt__['cmd.run'](version_cmd, output_loglevel="quiet", ignore_retcode=True) + version_out = __salt__["cmd.run"]( + version_cmd, output_loglevel="quiet", ignore_retcode=True + ) version_out = version_out.strip() except CommandExecutionError: version_out = "" if re.match(expected_version, version_out) is not None: - ret['comment'] += "Version already matches {0}".format(expected_version) + ret["comment"] += "Version already matches {0}".format(expected_version) return ret else: - ret['comment'] += "Version {0} doesn't match {1}. ".format(version_out, expected_version) + ret["comment"] += "Version {0} doesn't match {1}. ".format( + version_out, expected_version + ) if app and target == "LocalSystem": target = "/Applications/" @@ -140,10 +154,10 @@ def installed(name, target="LocalSystem", dmg=False, store=False, app=False, mpk # Mount the dmg first mount_point = None if dmg: - out, mount_point = __salt__['macpackage.mount'](name) - if 'attach failed' in out: - ret['result'] = False - ret['comment'] += 'Unable to mount {0}'.format(name) + out, mount_point = __salt__["macpackage.mount"](name) + if "attach failed" in out: + ret["result"] = False + ret["comment"] += "Unable to mount {0}".format(name) return ret if app: @@ -158,12 +172,12 @@ def installed(name, target="LocalSystem", dmg=False, store=False, app=False, mpk if app: if dmg: # Run with python shell due to the wildcard - cmd = 'ls -d *.app' - out = __salt__['cmd.run'](cmd, cwd=mount_point, python_shell=True) + cmd = "ls -d *.app" + out = __salt__["cmd.run"](cmd, cwd=mount_point, python_shell=True) - if '.app' not in out: - ret['result'] = False - ret['comment'] += 'Unable to find .app in {0}'.format(mount_point) + if ".app" not in out: + ret["result"] = False + ret["comment"] += "Unable to find .app in {0}".format(mount_point) return ret else: pkg_ids = out.split("\n") @@ -184,12 +198,12 @@ def installed(name, target="LocalSystem", dmg=False, store=False, app=False, mpk else: installing = pkg_ids else: - installed_pkgs = __salt__['macpackage.installed_pkgs']() + installed_pkgs = __salt__["macpackage.installed_pkgs"]() if mpkg: - pkg_ids = __salt__['macpackage.get_mpkg_ids'](real_pkg) + pkg_ids = __salt__["macpackage.get_mpkg_ids"](real_pkg) else: - pkg_ids = __salt__['macpackage.get_pkg_id'](real_pkg) + pkg_ids = __salt__["macpackage.get_pkg_id"](real_pkg) if len(pkg_ids) > 0: for p in pkg_ids: @@ -201,69 +215,78 @@ def installed(name, target="LocalSystem", dmg=False, store=False, app=False, mpk return ret if app: - def failed_pkg(f_pkg): - ret['result'] = False - ret['comment'] += '{0} failed to install: {1}'.format(name, out) - if 'failed' in ret['changes']: - ret['changes']['failed'].append(f_pkg) + def failed_pkg(f_pkg): + ret["result"] = False + ret["comment"] += "{0} failed to install: {1}".format(name, out) + + if "failed" in ret["changes"]: + ret["changes"]["failed"].append(f_pkg) else: - ret['changes']['failed'] = [f_pkg] + ret["changes"]["failed"] = [f_pkg] for app in installing: try: - log.info('Copying {0} to {1}'.format(app, target)) + log.info("Copying {0} to {1}".format(app, target)) - out = __salt__['macpackage.install_app'](os.path.join(mount_point, app), target) + out = __salt__["macpackage.install_app"]( + os.path.join(mount_point, app), target + ) if len(out) != 0: failed_pkg(app) else: - ret['comment'] += '{0} installed'.format(app) - if 'installed' in ret['changes']: - ret['changes']['installed'].append(app) + ret["comment"] += "{0} installed".format(app) + if "installed" in ret["changes"]: + ret["changes"]["installed"].append(app) else: - ret['changes']['installed'] = [app] + ret["changes"]["installed"] = [app] except OSError: failed_pkg(app) else: - out = __salt__['macpackage.install'](real_pkg, target, store, allow_untrusted) + out = __salt__["macpackage.install"]( + real_pkg, target, store, allow_untrusted + ) - if out['retcode'] != 0: - ret['result'] = False - ret['comment'] += '. {0} failed to install: {1}'.format(name, out) + if out["retcode"] != 0: + ret["result"] = False + ret["comment"] += ". {0} failed to install: {1}".format(name, out) else: - ret['comment'] += '{0} installed'.format(name) - ret['changes']['installed'] = installing + ret["comment"] += "{0} installed".format(name) + ret["changes"]["installed"] = installing finally: if dmg: # Unmount to be kind - __salt__['macpackage.unmount'](mount_point) + __salt__["macpackage.unmount"](mount_point) return ret def _mod_run_check(cmd_kwargs, onlyif, unless): - ''' + """ Execute the onlyif and unless logic. Return a result dict if: * onlyif failed (onlyif != 0) * unless succeeded (unless == 0) else return True - ''' + """ if onlyif: - if __salt__['cmd.retcode'](onlyif, **cmd_kwargs) != 0: - return {'comment': 'onlyif condition is false', - 'skip_watch': True, - 'result': True} + if __salt__["cmd.retcode"](onlyif, **cmd_kwargs) != 0: + return { + "comment": "onlyif condition is false", + "skip_watch": True, + "result": True, + } if unless: - if __salt__['cmd.retcode'](unless, **cmd_kwargs) == 0: - return {'comment': 'unless condition is true', - 'skip_watch': True, - 'result': True} + if __salt__["cmd.retcode"](unless, **cmd_kwargs) == 0: + return { + "comment": "unless condition is true", + "skip_watch": True, + "result": True, + } # No reason to stop, return True return True diff --git a/salt/states/makeconf.py b/salt/states/makeconf.py index 9c1c8ecb615..07614e9a514 100644 --- a/salt/states/makeconf.py +++ b/salt/states/makeconf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Gentoo make.conf ============================== @@ -10,7 +10,7 @@ A state module to manage Gentoo's ``make.conf`` file makeopts: makeconf.present: - value: '-j3' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import 3rd-party libs @@ -18,16 +18,18 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load if the makeconf module is available in __salt__ - ''' - return 'makeconf' if 'makeconf.get_var' in __salt__ else False + """ + if "makeconf.get_var" in __salt__: + return "makeconf" + return (False, "makeconf module could not be loaded") def _make_set(var): - ''' + """ Force var to be a set - ''' + """ if var is None: return set() if not isinstance(var, list): @@ -39,7 +41,7 @@ def _make_set(var): def present(name, value=None, contains=None, excludes=None): - ''' + """ Verify that the variable is in the ``make.conf`` and has the provided settings. If value is set, contains and excludes will be ignored. @@ -56,16 +58,13 @@ def present(name, value=None, contains=None, excludes=None): excludes Enforce that the value of the variable does not contain the provided value. - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} # Make name all Uppers since make.conf uses all Upper vars upper_name = name.upper() - old_value = __salt__['makeconf.get_var'](upper_name) + old_value = __salt__["makeconf.get_var"](upper_name) # If only checking if variable is present allows for setting the # variable outside of salt states, but the state can still ensure @@ -73,48 +72,48 @@ def present(name, value=None, contains=None, excludes=None): if value is None and contains is None and excludes is None: # variable is present if old_value is not None: - msg = 'Variable {0} is already present in make.conf' - ret['comment'] = msg.format(name) + msg = "Variable {0} is already present in make.conf" + ret["comment"] = msg.format(name) else: - if __opts__['test']: - msg = 'Variable {0} is to be set in make.conf' - ret['comment'] = msg.format(name) - ret['result'] = None + if __opts__["test"]: + msg = "Variable {0} is to be set in make.conf" + ret["comment"] = msg.format(name) + ret["result"] = None else: - changes = __salt__['makeconf.set_var'](upper_name, '') + changes = __salt__["makeconf.set_var"](upper_name, "") # If failed to be set - if changes[upper_name]['new'] is None: - msg = 'Variable {0} failed to be set in make.conf' - ret['comment'] = msg.format(name) - ret['result'] = False + if changes[upper_name]["new"] is None: + msg = "Variable {0} failed to be set in make.conf" + ret["comment"] = msg.format(name) + ret["result"] = False else: - msg = 'Variable {0} set in make.conf' - ret['comment'] = msg.format(name) + msg = "Variable {0} set in make.conf" + ret["comment"] = msg.format(name) elif value is not None: # variable is present and is set to value if old_value is not None and old_value == value: msg = 'Variable {0} is already "{1}" in make.conf' - ret['comment'] = msg.format(name, value) + ret["comment"] = msg.format(name, value) else: - if __opts__['test']: + if __opts__["test"]: msg = 'Variable {0} is to be set to "{1}" in make.conf' - ret['comment'] = msg.format(name, value) - ret['result'] = None + ret["comment"] = msg.format(name, value) + ret["result"] = None else: - changes = __salt__['makeconf.set_var'](upper_name, value) + changes = __salt__["makeconf.set_var"](upper_name, value) # If failed to be set - new_value = __salt__['makeconf.get_var'](upper_name) + new_value = __salt__["makeconf.get_var"](upper_name) if new_value is None or new_value != value: - msg = 'Variable {0} failed to be set in make.conf' - ret['comment'] = msg.format(name) - ret['result'] = False + msg = "Variable {0} failed to be set in make.conf" + ret["comment"] = msg.format(name) + ret["result"] = False else: - msg = 'Variable {0} is set in make.conf' - ret['changes'] = changes - ret['comment'] = msg.format(name) + msg = "Variable {0} is set in make.conf" + ret["changes"] = changes + ret["comment"] = msg.format(name) elif contains is not None or excludes is not None: # Make these into sets to easily compare things @@ -122,9 +121,9 @@ def present(name, value=None, contains=None, excludes=None): excludes_set = _make_set(excludes) old_value_set = _make_set(old_value) if len(contains_set.intersection(excludes_set)) > 0: - msg = 'Variable {0} cannot contain and exclude the same value' - ret['comment'] = msg.format(name) - ret['result'] = False + msg = "Variable {0} cannot contain and exclude the same value" + ret["comment"] = msg.format(name) + ret["result"] = False else: to_append = set() to_trim = set() @@ -133,71 +132,67 @@ def present(name, value=None, contains=None, excludes=None): if excludes is not None: to_trim = excludes_set.intersection(old_value_set) if len(to_append) == 0 and len(to_trim) == 0: - msg = 'Variable {0} is correct in make.conf' - ret['comment'] = msg.format(name) + msg = "Variable {0} is correct in make.conf" + ret["comment"] = msg.format(name) else: - if __opts__['test']: - msg = 'Variable {0} is set to'.format(name) + if __opts__["test"]: + msg = "Variable {0} is set to".format(name) if len(to_append) > 0: msg += ' append "{0}"'.format(list(to_append)) if len(to_trim) > 0: msg += ' trim "{0}"'.format(list(to_trim)) - msg += ' in make.conf' - ret['comment'] = msg - ret['result'] = None + msg += " in make.conf" + ret["comment"] = msg + ret["result"] = None else: for value in to_append: - __salt__['makeconf.append_var'](upper_name, value) + __salt__["makeconf.append_var"](upper_name, value) for value in to_trim: - __salt__['makeconf.trim_var'](upper_name, value) - new_value = __salt__['makeconf.get_var'](upper_name) + __salt__["makeconf.trim_var"](upper_name, value) + new_value = __salt__["makeconf.get_var"](upper_name) # TODO verify appends and trims worked - ret['changes'] = {upper_name: {'old': old_value, - 'new': new_value}} - msg = 'Variable {0} is correct in make.conf' - ret['comment'] = msg.format(name) + ret["changes"] = {upper_name: {"old": old_value, "new": new_value}} + msg = "Variable {0} is correct in make.conf" + ret["comment"] = msg.format(name) # Now finally return return ret def absent(name): - ''' + """ Verify that the variable is not in the ``make.conf``. name The variable name. This will automatically be converted to upper case since variables in ``make.conf`` are in upper case - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} # Make name all Uppers since make.conf uses all Upper vars upper_name = name.upper() - old_value = __salt__['makeconf.get_var'](upper_name) + old_value = __salt__["makeconf.get_var"](upper_name) if old_value is None: - msg = 'Variable {0} is already absent from make.conf' - ret['comment'] = msg.format(name) + msg = "Variable {0} is already absent from make.conf" + ret["comment"] = msg.format(name) else: - if __opts__['test']: - msg = 'Variable {0} is set to be removed from make.conf' - ret['comment'] = msg.format(name) - ret['result'] = None + if __opts__["test"]: + msg = "Variable {0} is set to be removed from make.conf" + ret["comment"] = msg.format(name) + ret["result"] = None else: - __salt__['makeconf.remove_var'](upper_name) + __salt__["makeconf.remove_var"](upper_name) - new_value = __salt__['makeconf.get_var'](upper_name) + new_value = __salt__["makeconf.get_var"](upper_name) if new_value is not None: - msg = 'Variable {0} failed to be removed from make.conf' - ret['comment'] = msg.format(name) - ret['result'] = False + msg = "Variable {0} failed to be removed from make.conf" + ret["comment"] = msg.format(name) + ret["result"] = False else: - msg = 'Variable {0} was removed from make.conf' - ret['comment'] = msg.format(name) - ret['result'] = True + msg = "Variable {0} was removed from make.conf" + ret["comment"] = msg.format(name) + ret["result"] = True return ret diff --git a/salt/states/marathon_app.py b/salt/states/marathon_app.py index 376d70c106c..c6f829ded41 100644 --- a/salt/states/marathon_app.py +++ b/salt/states/marathon_app.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configure Marathon apps via a salt proxy. .. code-block:: yaml @@ -13,145 +13,134 @@ Configure Marathon apps via a salt proxy. instances: 3 .. versionadded:: 2015.8.2 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import salt.utils.configcomparer -__proxyenabled__ = ['marathon'] +__proxyenabled__ = ["marathon"] log = logging.getLogger(__file__) def config(name, config): - ''' + """ Ensure that the marathon app with the given id is present and is configured to match the given config values. :param name: The app name/id :param config: The configuration to apply (dict) :return: A standard Salt changes dictionary - ''' + """ # setup return structure ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': '', + "name": name, + "changes": {}, + "result": False, + "comment": "", } # get existing config if app is present existing_config = None - if __salt__['marathon.has_app'](name): - existing_config = __salt__['marathon.app'](name)['app'] + if __salt__["marathon.has_app"](name): + existing_config = __salt__["marathon.app"](name)["app"] # compare existing config with defined config if existing_config: update_config = copy.deepcopy(existing_config) salt.utils.configcomparer.compare_and_update_config( - config, - update_config, - ret['changes'], + config, update_config, ret["changes"], ) else: # the app is not configured--we need to create it from scratch - ret['changes']['app'] = { - 'new': config, - 'old': None, + ret["changes"]["app"] = { + "new": config, + "old": None, } update_config = config # update the config if we registered any changes - if ret['changes']: + if ret["changes"]: # if test, report there will be an update - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Marathon app {0} is set to be updated'.format( - name - ) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Marathon app {0} is set to be updated".format(name) return ret - update_result = __salt__['marathon.update_app'](name, update_config) - if 'exception' in update_result: - ret['result'] = False - ret['comment'] = 'Failed to update app config for {0}: {1}'.format( - name, - update_result['exception'], + update_result = __salt__["marathon.update_app"](name, update_config) + if "exception" in update_result: + ret["result"] = False + ret["comment"] = "Failed to update app config for {0}: {1}".format( + name, update_result["exception"], ) return ret else: - ret['result'] = True - ret['comment'] = 'Updated app config for {0}'.format(name) + ret["result"] = True + ret["comment"] = "Updated app config for {0}".format(name) return ret - ret['result'] = True - ret['comment'] = 'Marathon app {0} configured correctly'.format(name) + ret["result"] = True + ret["comment"] = "Marathon app {0} configured correctly".format(name) return ret def absent(name): - ''' + """ Ensure that the marathon app with the given id is not present. :param name: The app name/id :return: A standard Salt changes dictionary - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - if not __salt__['marathon.has_app'](name): - ret['result'] = True - ret['comment'] = 'App {0} already absent'.format(name) + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + if not __salt__["marathon.has_app"](name): + ret["result"] = True + ret["comment"] = "App {0} already absent".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'App {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "App {0} is set to be removed".format(name) return ret - if __salt__['marathon.rm_app'](name): - ret['changes'] = {'app': name} - ret['result'] = True - ret['comment'] = 'Removed app {0}'.format(name) + if __salt__["marathon.rm_app"](name): + ret["changes"] = {"app": name} + ret["result"] = True + ret["comment"] = "Removed app {0}".format(name) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to remove app {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to remove app {0}".format(name) return ret def running(name, restart=False, force=True): - ''' + """ Ensure that the marathon app with the given id is present and restart if set. :param name: The app name/id :param restart: Restart the app :param force: Override the current deployment :return: A standard Salt changes dictionary - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - if not __salt__['marathon.has_app'](name): - ret['result'] = False - ret['comment'] = 'App {0} cannot be restarted because it is absent'.format(name) + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + if not __salt__["marathon.has_app"](name): + ret["result"] = False + ret["comment"] = "App {0} cannot be restarted because it is absent".format(name) return ret - if __opts__['test']: - ret['result'] = None - qualifier = 'is' if restart else 'is not' - ret['comment'] = 'App {0} {1} set to be restarted'.format(name, qualifier) + if __opts__["test"]: + ret["result"] = None + qualifier = "is" if restart else "is not" + ret["comment"] = "App {0} {1} set to be restarted".format(name, qualifier) return ret - restart_result = __salt__['marathon.restart_app'](name, restart, force) - if 'exception' in restart_result: - ret['result'] = False - ret['comment'] = 'Failed to restart app {0}: {1}'.format( - name, - restart_result['exception'] + restart_result = __salt__["marathon.restart_app"](name, restart, force) + if "exception" in restart_result: + ret["result"] = False + ret["comment"] = "Failed to restart app {0}: {1}".format( + name, restart_result["exception"] ) return ret else: - ret['changes'] = restart_result - ret['result'] = True - qualifier = 'Restarted' if restart else 'Did not restart' - ret['comment'] = '{0} app {1}'.format(qualifier, name) + ret["changes"] = restart_result + ret["result"] = True + qualifier = "Restarted" if restart else "Did not restart" + ret["comment"] = "{0} app {1}".format(qualifier, name) return ret diff --git a/salt/states/mdadm_raid.py b/salt/states/mdadm_raid.py index d634522c334..87c0e696531 100644 --- a/salt/states/mdadm_raid.py +++ b/salt/states/mdadm_raid.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Managing software RAID with mdadm ================================== @@ -18,10 +18,11 @@ A state module for creating or destroying software RAID devices. - /dev/xvdf - chunk: 256 - run: True -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -31,25 +32,22 @@ import salt.utils.path log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'raid' +__virtualname__ = "raid" def __virtual__(): - ''' + """ mdadm provides raid functions for Linux - ''' - if __grains__['kernel'] != 'Linux': - return False - if not salt.utils.path.which('mdadm'): - return False + """ + if __grains__["kernel"] != "Linux": + return (False, "Only supported on Linux") + if not salt.utils.path.which("mdadm"): + return (False, "Unable to locate command: mdadm") return __virtualname__ -def present(name, - level, - devices, - **kwargs): - ''' +def present(name, level, devices, **kwargs): + """ Verify that the raid is present .. versionchanged:: 2014.7.0 @@ -79,14 +77,11 @@ def present(name, - /dev/xvdf - chunk: 256 - run: True - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} # Device exists - raids = __salt__['raid.list']() + raids = __salt__["raid.list"]() present = raids.get(name) # Decide whether to create or assemble @@ -95,13 +90,13 @@ def present(name, new_devices = [] for dev in devices: - if dev == 'missing' or not __salt__['file.access'](dev, 'f'): + if dev == "missing" or not __salt__["file.access"](dev, "f"): missing.append(dev) continue - superblock = __salt__['raid.examine'](dev, quiet=True) + superblock = __salt__["raid.examine"](dev, quiet=True) - if 'MD_UUID' in superblock: - uuid = superblock['MD_UUID'] + if "MD_UUID" in superblock: + uuid = superblock["MD_UUID"] if uuid not in uuid_dict: uuid_dict[uuid] = [] uuid_dict[uuid].append(dev) @@ -109,15 +104,22 @@ def present(name, new_devices.append(dev) if len(uuid_dict) > 1: - ret['comment'] = 'Devices are a mix of RAID constituents with multiple MD_UUIDs: {0}.'.format( - sorted(uuid_dict.keys())) - ret['result'] = False + ret[ + "comment" + ] = "Devices are a mix of RAID constituents with multiple MD_UUIDs: {0}.".format( + sorted(uuid_dict.keys()) + ) + ret["result"] = False return ret elif len(uuid_dict) == 1: uuid = list(uuid_dict.keys())[0] - if present and present['uuid'] != uuid: - ret['comment'] = 'Devices MD_UUIDs: {0} differs from present RAID uuid {1}.'.format(uuid, present['uuid']) - ret['result'] = False + if present and present["uuid"] != uuid: + ret[ + "comment" + ] = "Devices MD_UUIDs: {0} differs from present RAID uuid {1}.".format( + uuid, present["uuid"] + ) + ret["result"] = False return ret devices_with_superblock = uuid_dict[uuid] @@ -130,90 +132,88 @@ def present(name, elif len(devices_with_superblock) > 0: do_assemble = True do_create = False - verb = 'assembled' + verb = "assembled" else: if len(new_devices) == 0: - ret['comment'] = 'All devices are missing: {0}.'.format(missing) - ret['result'] = False + ret["comment"] = "All devices are missing: {0}.".format(missing) + ret["result"] = False return ret do_assemble = False do_create = True - verb = 'created' + verb = "created" # If running with test use the test_mode with create or assemble - if __opts__['test']: + if __opts__["test"]: if do_assemble: - res = __salt__['raid.assemble'](name, - devices_with_superblock, - test_mode=True, - **kwargs) + res = __salt__["raid.assemble"]( + name, devices_with_superblock, test_mode=True, **kwargs + ) elif do_create: - res = __salt__['raid.create'](name, - level, - new_devices + ['missing'] * len(missing), - test_mode=True, - **kwargs) + res = __salt__["raid.create"]( + name, + level, + new_devices + ["missing"] * len(missing), + test_mode=True, + **kwargs + ) if present: - ret['comment'] = 'Raid {0} already present.'.format(name) + ret["comment"] = "Raid {0} already present.".format(name) if do_assemble or do_create: - ret['comment'] = 'Raid will be {0} with: {1}'.format(verb, res) - ret['result'] = None + ret["comment"] = "Raid will be {0} with: {1}".format(verb, res) + ret["result"] = None if (do_assemble or present) and len(new_devices) > 0: - ret['comment'] += ' New devices will be added: {0}'.format(new_devices) - ret['result'] = None + ret["comment"] += " New devices will be added: {0}".format(new_devices) + ret["result"] = None if len(missing) > 0: - ret['comment'] += ' Missing devices: {0}'.format(missing) + ret["comment"] += " Missing devices: {0}".format(missing) return ret # Attempt to create or assemble the array if do_assemble: - __salt__['raid.assemble'](name, - devices_with_superblock, - **kwargs) + __salt__["raid.assemble"](name, devices_with_superblock, **kwargs) elif do_create: - __salt__['raid.create'](name, - level, - new_devices + ['missing'] * len(missing), - **kwargs) + __salt__["raid.create"]( + name, level, new_devices + ["missing"] * len(missing), **kwargs + ) if not present: - raids = __salt__['raid.list']() + raids = __salt__["raid.list"]() changes = raids.get(name) if changes: - ret['comment'] = 'Raid {0} {1}.'.format(name, verb) - ret['changes'] = changes + ret["comment"] = "Raid {0} {1}.".format(name, verb) + ret["changes"] = changes # Saving config - __salt__['raid.save_config']() + __salt__["raid.save_config"]() else: - ret['comment'] = 'Raid {0} failed to be {1}.'.format(name, verb) - ret['result'] = False + ret["comment"] = "Raid {0} failed to be {1}.".format(name, verb) + ret["result"] = False else: - ret['comment'] = 'Raid {0} already present.'.format(name) + ret["comment"] = "Raid {0} already present.".format(name) - if (do_assemble or present) and len(new_devices) > 0 and ret['result']: + if (do_assemble or present) and len(new_devices) > 0 and ret["result"]: for d in new_devices: - res = __salt__['raid.add'](name, d) + res = __salt__["raid.add"](name, d) if not res: - ret['comment'] += ' Unable to add {0} to {1}.\n'.format(d, name) - ret['result'] = False + ret["comment"] += " Unable to add {0} to {1}.\n".format(d, name) + ret["result"] = False else: - ret['comment'] += ' Added new device {0} to {1}.\n'.format(d, name) - if ret['result']: - ret['changes']['added'] = new_devices + ret["comment"] += " Added new device {0} to {1}.\n".format(d, name) + if ret["result"]: + ret["changes"]["added"] = new_devices if len(missing) > 0: - ret['comment'] += ' Missing devices: {0}'.format(missing) + ret["comment"] += " Missing devices: {0}".format(missing) return ret def absent(name): - ''' + """ Verify that the raid is absent name @@ -224,26 +224,23 @@ def absent(name): /dev/md0: raid: - absent - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} # Raid does not exist - if name not in __salt__['raid.list'](): - ret['comment'] = 'Raid {0} already absent'.format(name) + if name not in __salt__["raid.list"](): + ret["comment"] = "Raid {0} already absent".format(name) return ret - elif __opts__['test']: - ret['comment'] = 'Raid {0} is set to be destroyed'.format(name) - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "Raid {0} is set to be destroyed".format(name) + ret["result"] = None return ret else: # Attempt to destroy raid - ret['result'] = __salt__['raid.destroy'](name) + ret["result"] = __salt__["raid.destroy"](name) - if ret['result']: - ret['comment'] = 'Raid {0} has been destroyed'.format(name) + if ret["result"]: + ret["comment"] = "Raid {0} has been destroyed".format(name) else: - ret['comment'] = 'Raid {0} failed to be destroyed'.format(name) + ret["comment"] = "Raid {0} failed to be destroyed".format(name) return ret diff --git a/salt/states/memcached.py b/salt/states/memcached.py index efc284905af..1ee9ec51823 100644 --- a/salt/states/memcached.py +++ b/salt/states/memcached.py @@ -1,43 +1,46 @@ # -*- coding: utf-8 -*- -''' +""" States for Management of Memcached Keys ======================================= .. versionadded:: 2014.1.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt libs -from salt.modules.memcached import ( - DEFAULT_HOST, - DEFAULT_PORT, - DEFAULT_TIME, - DEFAULT_MIN_COMPRESS_LEN -) from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six -__virtualname__ = 'memcached' +# Import Salt libs +from salt.modules.memcached import ( + DEFAULT_HOST, + DEFAULT_MIN_COMPRESS_LEN, + DEFAULT_PORT, + DEFAULT_TIME, +) + +__virtualname__ = "memcached" def __virtual__(): - ''' + """ Only load if memcache module is available - ''' - return __virtualname__ \ - if '{0}.status'.format(__virtualname__) in __salt__ \ - else False + """ + if "{0}.status".format(__virtualname__) in __salt__: + return __virtualname__ + return (False, "memcached module could not be loaded") -def managed(name, - value=None, - host=DEFAULT_HOST, - port=DEFAULT_PORT, - time=DEFAULT_TIME, - min_compress_len=DEFAULT_MIN_COMPRESS_LEN): - ''' +def managed( + name, + value=None, + host=DEFAULT_HOST, + port=DEFAULT_PORT, + time=DEFAULT_TIME, + min_compress_len=DEFAULT_MIN_COMPRESS_LEN, +): + """ Manage a memcached key. name @@ -58,55 +61,48 @@ def managed(name, foo: memcached.managed: - value: bar - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} try: - cur = __salt__['memcached.get'](name, host, port) + cur = __salt__["memcached.get"](name, host, port) except CommandExecutionError as exc: - ret['comment'] = six.text_type(exc) + ret["comment"] = six.text_type(exc) return ret if cur == value: - ret['result'] = True - ret['comment'] = 'Key \'{0}\' does not need to be updated'.format(name) + ret["result"] = True + ret["comment"] = "Key '{0}' does not need to be updated".format(name) return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if cur is None: - ret['comment'] = 'Key \'{0}\' would be added'.format(name) + ret["comment"] = "Key '{0}' would be added".format(name) else: - ret['comment'] = 'Value of key \'{0}\' would be changed'.format(name) + ret["comment"] = "Value of key '{0}' would be changed".format(name) return ret try: - ret['result'] = __salt__['memcached.set']( + ret["result"] = __salt__["memcached.set"]( name, value, host, port, time, min_compress_len ) except (CommandExecutionError, SaltInvocationError) as exc: - ret['comment'] = six.text_type(exc) + ret["comment"] = six.text_type(exc) else: - if ret['result']: - ret['comment'] = 'Successfully set key \'{0}\''.format(name) + if ret["result"]: + ret["comment"] = "Successfully set key '{0}'".format(name) if cur is not None: - ret['changes'] = {'old': cur, 'new': value} + ret["changes"] = {"old": cur, "new": value} else: - ret['changes'] = {'key added': name, 'value': value} + ret["changes"] = {"key added": name, "value": value} else: - ret['comment'] = 'Failed to set key \'{0}\''.format(name) + ret["comment"] = "Failed to set key '{0}'".format(name) return ret -def absent(name, - value=None, - host=DEFAULT_HOST, - port=DEFAULT_PORT, - time=DEFAULT_TIME): - ''' +def absent(name, value=None, host=DEFAULT_HOST, port=DEFAULT_PORT, time=DEFAULT_TIME): + """ Ensure that a memcached key is not present. name @@ -131,44 +127,40 @@ def absent(name, bar: memcached.absent: - host: 10.0.0.1 - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} try: - cur = __salt__['memcached.get'](name, host, port) + cur = __salt__["memcached.get"](name, host, port) except CommandExecutionError as exc: - ret['comment'] = six.text_type(exc) + ret["comment"] = six.text_type(exc) return ret if value is not None: if cur is not None and cur != value: - ret['result'] = True - ret['comment'] = ( - 'Value of key \'{0}\' (\'{1}\') is not \'{2}\'' - .format(name, cur, value) + ret["result"] = True + ret["comment"] = "Value of key '{0}' ('{1}') is not '{2}'".format( + name, cur, value ) return ret if cur is None: - ret['result'] = True - ret['comment'] = 'Key \'{0}\' does not exist'.format(name) + ret["result"] = True + ret["comment"] = "Key '{0}' does not exist".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Key \'{0}\' would be deleted'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Key '{0}' would be deleted".format(name) return ret try: - ret['result'] = __salt__['memcached.delete'](name, host, port, time) + ret["result"] = __salt__["memcached.delete"](name, host, port, time) except (CommandExecutionError, SaltInvocationError) as exc: - ret['comment'] = six.text_type(exc) + ret["comment"] = six.text_type(exc) else: - if ret['result']: - ret['comment'] = 'Successfully deleted key \'{0}\''.format(name) - ret['changes'] = {'key deleted': name, 'value': cur} + if ret["result"]: + ret["comment"] = "Successfully deleted key '{0}'".format(name) + ret["changes"] = {"key deleted": name, "value": cur} else: - ret['comment'] = 'Failed to delete key \'{0}\''.format(name) + ret["comment"] = "Failed to delete key '{0}'".format(name) return ret diff --git a/salt/states/modjk.py b/salt/states/modjk.py index 15642890773..b539223824a 100644 --- a/salt/states/modjk.py +++ b/salt/states/modjk.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" State to control Apache modjk -''' +""" # Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import 3rd-party libs @@ -14,41 +15,35 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Load this state if modjk is loaded - ''' - - return 'modjk.workers' in __salt__ + """ + if "modjk.workers" in __salt__: + return True + return (False, "modjk module could not be loaded") def _bulk_state(saltfunc, lbn, workers, profile): - ''' + """ Generic function for bulk worker operation - ''' - ret = {'name': lbn, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": lbn, "result": True, "changes": {}, "comment": ""} if not isinstance(workers, list): - ret['result'] = False - ret['comment'] = 'workers should be a list not a {0}'.format( - type(workers) - ) + ret["result"] = False + ret["comment"] = "workers should be a list not a {0}".format(type(workers)) return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret - log.info('executing %s to modjk workers %s', saltfunc, workers) + log.info("executing %s to modjk workers %s", saltfunc, workers) try: cmdret = __salt__[saltfunc](workers, lbn, profile=profile) except KeyError: - ret['result'] = False - ret['comment'] = 'unsupported function {0}'.format( - saltfunc - ) + ret["result"] = False + ret["comment"] = "unsupported function {0}".format(saltfunc) return ret errors = [] @@ -56,16 +51,16 @@ def _bulk_state(saltfunc, lbn, workers, profile): if not ok: errors.append(worker) - ret['changes'] = {'status': cmdret} + ret["changes"] = {"status": cmdret} if errors: - ret['result'] = False - ret['comment'] = '{0} failed on some workers'.format(saltfunc) + ret["result"] = False + ret["comment"] = "{0} failed on some workers".format(saltfunc) return ret -def worker_stopped(name, workers=None, profile='default'): - ''' +def worker_stopped(name, workers=None, profile="default"): + """ Stop all the workers in the modjk load balancer Example: @@ -77,16 +72,14 @@ def worker_stopped(name, workers=None, profile='default'): - workers: - app1 - app2 - ''' + """ if workers is None: workers = [] - return _bulk_state( - 'modjk.bulk_stop', name, workers, profile - ) + return _bulk_state("modjk.bulk_stop", name, workers, profile) -def worker_activated(name, workers=None, profile='default'): - ''' +def worker_activated(name, workers=None, profile="default"): + """ Activate all the workers in the modjk load balancer Example: @@ -98,16 +91,14 @@ def worker_activated(name, workers=None, profile='default'): - workers: - app1 - app2 - ''' + """ if workers is None: workers = [] - return _bulk_state( - 'modjk.bulk_activate', name, workers, profile - ) + return _bulk_state("modjk.bulk_activate", name, workers, profile) -def worker_disabled(name, workers=None, profile='default'): - ''' +def worker_disabled(name, workers=None, profile="default"): + """ Disable all the workers in the modjk load balancer Example: @@ -119,16 +110,14 @@ def worker_disabled(name, workers=None, profile='default'): - workers: - app1 - app2 - ''' + """ if workers is None: workers = [] - return _bulk_state( - 'modjk.bulk_disable', name, workers, profile - ) + return _bulk_state("modjk.bulk_disable", name, workers, profile) -def worker_recover(name, workers=None, profile='default'): - ''' +def worker_recover(name, workers=None, profile="default"): + """ Recover all the workers in the modjk load balancer Example: @@ -140,9 +129,7 @@ def worker_recover(name, workers=None, profile='default'): - workers: - app1 - app2 - ''' + """ if workers is None: workers = [] - return _bulk_state( - 'modjk.bulk_recover', name, workers, profile - ) + return _bulk_state("modjk.bulk_recover", name, workers, profile) diff --git a/salt/states/modjk_worker.py b/salt/states/modjk_worker.py index 30a28c9314e..b58431a234d 100644 --- a/salt/states/modjk_worker.py +++ b/salt/states/modjk_worker.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage modjk workers ==================== @@ -17,24 +17,19 @@ Mandatory Settings: - The modjk load balancer must be configured as stated in the :strong:`modjk` execution module :mod:`documentation <salt.modules.modjk>` -''' +""" from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Check if we have peer access ? - ''' + """ return True -def _send_command(cmd, - worker, - lbn, - target, - profile='default', - tgt_type='glob'): - ''' +def _send_command(cmd, worker, lbn, target, profile="default", tgt_type="glob"): + """ Send a command to the modjk loadbalancer The minion need to be able to publish the commands to the load balancer @@ -42,18 +37,18 @@ def _send_command(cmd, worker_stop - won't get any traffic from the lbn worker_activate - activate the worker worker_disable - will get traffic only for current sessions - ''' + """ ret = { - 'code': False, - 'msg': 'OK', - 'minions': [], + "code": False, + "msg": "OK", + "minions": [], } # Send the command to target - func = 'modjk.{0}'.format(cmd) + func = "modjk.{0}".format(cmd) args = [worker, lbn, profile] - response = __salt__['publish.publish'](target, func, args, tgt_type) + response = __salt__["publish.publish"](target, func, args, tgt_type) # Get errors and list of affeced minions errors = [] @@ -65,27 +60,21 @@ def _send_command(cmd, # parse response if not response: - ret['msg'] = 'no servers answered the published command {0}'.format( - cmd - ) + ret["msg"] = "no servers answered the published command {0}".format(cmd) return ret elif len(errors) > 0: - ret['msg'] = 'the following minions return False' - ret['minions'] = errors + ret["msg"] = "the following minions return False" + ret["minions"] = errors return ret else: - ret['code'] = True - ret['msg'] = 'the commad was published successfully' - ret['minions'] = minions + ret["code"] = True + ret["msg"] = "the commad was published successfully" + ret["minions"] = minions return ret -def _worker_status(target, - worker, - activation, - profile='default', - tgt_type='glob'): - ''' +def _worker_status(target, worker, activation, profile="default", tgt_type="glob"): + """ Check if the worker is in `activation` state in the targeted load balancers The function will return the following dictionary: @@ -93,86 +82,84 @@ def _worker_status(target, errors - list of servers that couldn't find the worker wrong_state - list of servers that the worker was in the wrong state (not activation) - ''' + """ ret = { - 'result': True, - 'errors': [], - 'wrong_state': [], + "result": True, + "errors": [], + "wrong_state": [], } args = [worker, profile] - status = __salt__['publish.publish']( - target, 'modjk.worker_status', args, tgt_type - ) + status = __salt__["publish.publish"](target, "modjk.worker_status", args, tgt_type) # Did we got any respone from someone ? if not status: - ret['result'] = False + ret["result"] = False return ret # Search for errors & status for balancer in status: if not status[balancer]: - ret['errors'].append(balancer) - elif status[balancer]['activation'] != activation: - ret['wrong_state'].append(balancer) + ret["errors"].append(balancer) + elif status[balancer]["activation"] != activation: + ret["wrong_state"].append(balancer) return ret -def _talk2modjk(name, lbn, target, action, profile='default', tgt_type='glob'): - ''' +def _talk2modjk(name, lbn, target, action, profile="default", tgt_type="glob"): + """ Wrapper function for the stop/disable/activate functions - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} action_map = { - 'worker_stop': 'STP', - 'worker_disable': 'DIS', - 'worker_activate': 'ACT', + "worker_stop": "STP", + "worker_disable": "DIS", + "worker_activate": "ACT", } # Check what needs to be done - status = _worker_status( - target, name, action_map[action], profile, tgt_type - ) - if not status['result']: - ret['result'] = False - ret['comment'] = ('no servers answered the published command ' - 'modjk.worker_status') + status = _worker_status(target, name, action_map[action], profile, tgt_type) + if not status["result"]: + ret["result"] = False + ret["comment"] = ( + "no servers answered the published command " "modjk.worker_status" + ) return ret - if status['errors']: - ret['result'] = False - ret['comment'] = ('the following balancers could not find the ' - 'worker {0}: {1}'.format(name, status['errors'])) + if status["errors"]: + ret["result"] = False + ret["comment"] = ( + "the following balancers could not find the " + "worker {0}: {1}".format(name, status["errors"]) + ) return ret - if not status['wrong_state']: - ret['comment'] = ('the worker is in the desired activation state on ' - 'all the balancers') + if not status["wrong_state"]: + ret["comment"] = ( + "the worker is in the desired activation state on " "all the balancers" + ) return ret else: - ret['comment'] = ('the action {0} will be sent to the balancers ' - '{1}'.format(action, status['wrong_state'])) - ret['changes'] = {action: status['wrong_state']} + ret["comment"] = "the action {0} will be sent to the balancers " "{1}".format( + action, status["wrong_state"] + ) + ret["changes"] = {action: status["wrong_state"]} - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # Send the action command to target response = _send_command(action, name, lbn, target, profile, tgt_type) - ret['comment'] = response['msg'] - ret['result'] = response['code'] + ret["comment"] = response["msg"] + ret["result"] = response["code"] return ret -def stop(name, lbn, target, profile='default', tgt_type='glob'): - ''' +def stop(name, lbn, target, profile="default", tgt_type="glob"): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -190,12 +177,12 @@ def stop(name, lbn, target, profile='default', tgt_type='glob'): - lbn: application - target: 'roles:balancer' - tgt_type: grain - ''' - return _talk2modjk(name, lbn, target, 'worker_stop', profile, tgt_type) + """ + return _talk2modjk(name, lbn, target, "worker_stop", profile, tgt_type) -def activate(name, lbn, target, profile='default', tgt_type='glob'): - ''' +def activate(name, lbn, target, profile="default", tgt_type="glob"): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -213,12 +200,12 @@ def activate(name, lbn, target, profile='default', tgt_type='glob'): - lbn: application - target: 'roles:balancer' - tgt_type: grain - ''' - return _talk2modjk(name, lbn, target, 'worker_activate', profile, tgt_type) + """ + return _talk2modjk(name, lbn, target, "worker_activate", profile, tgt_type) -def disable(name, lbn, target, profile='default', tgt_type='glob'): - ''' +def disable(name, lbn, target, profile="default", tgt_type="glob"): + """ .. versionchanged:: 2017.7.0 The ``expr_form`` argument has been renamed to ``tgt_type``, earlier releases must use ``expr_form``. @@ -237,5 +224,5 @@ def disable(name, lbn, target, profile='default', tgt_type='glob'): - lbn: application - target: 'roles:balancer' - tgt_type: grain - ''' - return _talk2modjk(name, lbn, target, 'worker_disable', profile, tgt_type) + """ + return _talk2modjk(name, lbn, target, "worker_disable", profile, tgt_type) diff --git a/salt/states/module.py b/salt/states/module.py index 285108a00ad..e0fda9da07b 100644 --- a/salt/states/module.py +++ b/salt/states/module.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Execution of Salt modules from within states ============================================ @@ -299,7 +299,7 @@ Windows system: } .. _file_roots: https://docs.saltstack.com/en/latest/ref/configuration/master.html#file-roots -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -307,14 +307,14 @@ import salt.loader import salt.utils.args import salt.utils.functools import salt.utils.jid +from salt.exceptions import SaltInvocationError from salt.ext import six from salt.ext.six.moves import range -from salt.exceptions import SaltInvocationError from salt.utils.decorators import with_deprecated def wait(name, **kwargs): - ''' + """ Run a single module function only if the watch statement calls it ``name`` @@ -334,20 +334,17 @@ def wait(name, **kwargs): 2. Another state has a :ref:`watch_in requisite <requisites-watch-in>` which references this state, and the state wth the ``watch_in`` changes. - ''' - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + return {"name": name, "changes": {}, "result": True, "comment": ""} # Alias module.watch to module.wait -watch = salt.utils.functools.alias_function(wait, 'watch') +watch = salt.utils.functools.alias_function(wait, "watch") @with_deprecated(globals(), "Sodium", policy=with_deprecated.OPT_IN) def run(**kwargs): - ''' + """ Run a single module function or a range of module functions in a batch. Supersedes ``module.run`` function, which requires ``m_`` prefix to function-specific parameters. @@ -377,66 +374,84 @@ def run(**kwargs): :return: - ''' + """ - if 'name' in kwargs: - kwargs.pop('name') + if "name" in kwargs: + kwargs.pop("name") ret = { - 'name': list(kwargs), - 'changes': {}, - 'comment': '', - 'result': None, + "name": list(kwargs), + "changes": {}, + "comment": "", + "result": None, } - functions = [func for func in kwargs if '.' in func] + functions = [func for func in kwargs if "." in func] missing = [] tests = [] for func in functions: - func = func.split(':')[0] + func = func.split(":")[0] if func not in __salt__: missing.append(func) - elif __opts__['test']: + elif __opts__["test"]: tests.append(func) if tests or missing: - ret['comment'] = ' '.join([ - missing and "Unavailable function{plr}: " - "{func}.".format(plr=(len(missing) > 1 or ''), - func=(', '.join(missing) or '')) or '', - tests and "Function{plr} {func} to be " - "executed.".format(plr=(len(tests) > 1 or ''), - func=(', '.join(tests)) or '') or '', - ]).strip() - ret['result'] = not (missing or not tests) + ret["comment"] = " ".join( + [ + missing + and "Unavailable function{plr}: " + "{func}.".format( + plr=(len(missing) > 1 or ""), func=(", ".join(missing) or "") + ) + or "", + tests + and "Function{plr} {func} to be " + "executed.".format( + plr=(len(tests) > 1 or ""), func=(", ".join(tests)) or "" + ) + or "", + ] + ).strip() + ret["result"] = not (missing or not tests) - if ret['result'] is None: - ret['result'] = True + if ret["result"] is None: + ret["result"] = True failures = [] success = [] for func in functions: - _func = func.split(':')[0] + _func = func.split(":")[0] try: - func_ret = _call_function(_func, returner=kwargs.get('returner'), - func_args=kwargs.get(func)) - if not _get_result(func_ret, ret['changes'].get('ret', {})): + func_ret = _call_function( + _func, returner=kwargs.get("returner"), func_args=kwargs.get(func) + ) + if not _get_result(func_ret, ret["changes"].get("ret", {})): if isinstance(func_ret, dict): - failures.append("'{0}' failed: {1}".format( - func, func_ret.get('comment', '(error message N/A)'))) + failures.append( + "'{0}' failed: {1}".format( + func, func_ret.get("comment", "(error message N/A)") + ) + ) else: - success.append('{0}: {1}'.format( - func, func_ret.get('comment', 'Success') if isinstance(func_ret, dict) else func_ret)) - ret['changes'][func] = func_ret + success.append( + "{0}: {1}".format( + func, + func_ret.get("comment", "Success") + if isinstance(func_ret, dict) + else func_ret, + ) + ) + ret["changes"][func] = func_ret except (SaltInvocationError, TypeError) as ex: failures.append("'{0}' failed: {1}".format(func, ex)) - ret['comment'] = ', '.join(failures + success) - ret['result'] = not bool(failures) + ret["comment"] = ", ".join(failures + success) + ret["result"] = not bool(failures) return ret -def _call_function(name, returner=None, func_args=None): - ''' +def _call_function(name, returner=None, func_args=None, func_kwargs=None): + """ Calls a function from the specified module. :param str name: module.function of the function to call @@ -444,20 +459,30 @@ def _call_function(name, returner=None, func_args=None): :param list func_args: List with args and dicts of kwargs (one dict per kwarg) to pass to the function. :return: Result of the function call - ''' + """ if func_args is None: func_args = [] - mret = salt.utils.functools.call_function(__salt__[name], *func_args) + + if func_kwargs is None: + func_kwargs = {} + + mret = salt.utils.functools.call_function(__salt__[name], *func_args, **func_kwargs) if returner is not None: returners = salt.loader.returners(__opts__, __salt__) if returner in returners: - returners[returner]({'id': __opts__['id'], 'ret': mret, - 'fun': name, 'jid': salt.utils.jid.gen_jid(__opts__)}) + returners[returner]( + { + "id": __opts__["id"], + "ret": mret, + "fun": name, + "jid": salt.utils.jid.gen_jid(__opts__), + } + ) return mret def _run(name, **kwargs): - ''' + """ .. deprecated:: 2017.7.0 Function name stays the same, behaviour will change. @@ -471,18 +496,15 @@ def _run(name, **kwargs): ``kwargs`` Pass any arguments needed to execute the function - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": None} if name not in __salt__: - ret['comment'] = 'Module function {0} is not available'.format(name) - ret['result'] = False + ret["comment"] = "Module function {0} is not available".format(name) + ret["result"] = False return ret - if __opts__['test']: - ret['comment'] = 'Module function {0} is set to execute'.format(name) + if __opts__["test"]: + ret["comment"] = "Module function {0} is set to execute".format(name) return ret aspec = salt.utils.args.get_function_argspec(__salt__[name]) @@ -502,32 +524,32 @@ def _run(name, **kwargs): defaults[aspec.args[ind]] = aspec.defaults[-minus] # overwrite passed default kwargs for arg in defaults: - if arg == 'name': - if 'm_name' in kwargs: - defaults[arg] = kwargs.pop('m_name') - elif arg == 'fun': - if 'm_fun' in kwargs: - defaults[arg] = kwargs.pop('m_fun') - elif arg == 'state': - if 'm_state' in kwargs: - defaults[arg] = kwargs.pop('m_state') - elif arg == 'saltenv': - if 'm_saltenv' in kwargs: - defaults[arg] = kwargs.pop('m_saltenv') + if arg == "name": + if "m_name" in kwargs: + defaults[arg] = kwargs.pop("m_name") + elif arg == "fun": + if "m_fun" in kwargs: + defaults[arg] = kwargs.pop("m_fun") + elif arg == "state": + if "m_state" in kwargs: + defaults[arg] = kwargs.pop("m_state") + elif arg == "saltenv": + if "m_saltenv" in kwargs: + defaults[arg] = kwargs.pop("m_saltenv") if arg in kwargs: defaults[arg] = kwargs.pop(arg) missing = set() for arg in aspec.args: - if arg == 'name': - rarg = 'm_name' - elif arg == 'fun': - rarg = 'm_fun' - elif arg == 'names': - rarg = 'm_names' - elif arg == 'state': - rarg = 'm_state' - elif arg == 'saltenv': - rarg = 'm_saltenv' + if arg == "name": + rarg = "m_name" + elif arg == "fun": + rarg = "m_fun" + elif arg == "names": + rarg = "m_names" + elif arg == "state": + rarg = "m_state" + elif arg == "saltenv": + rarg = "m_saltenv" else: rarg = arg if rarg not in kwargs and arg not in defaults: @@ -538,24 +560,24 @@ def _run(name, **kwargs): else: args.append(kwargs.pop(rarg)) if missing: - comment = 'The following arguments are missing:' + comment = "The following arguments are missing:" for arg in missing: - comment += ' {0}'.format(arg) - ret['comment'] = comment - ret['result'] = False + comment += " {0}".format(arg) + ret["comment"] = comment + ret["result"] = False return ret if aspec.varargs: - if aspec.varargs == 'name': - rarg = 'm_name' - elif aspec.varargs == 'fun': - rarg = 'm_fun' - elif aspec.varargs == 'names': - rarg = 'm_names' - elif aspec.varargs == 'state': - rarg = 'm_state' - elif aspec.varargs == 'saltenv': - rarg = 'm_saltenv' + if aspec.varargs == "name": + rarg = "m_name" + elif aspec.varargs == "fun": + rarg = "m_fun" + elif aspec.varargs == "names": + rarg = "m_names" + elif aspec.varargs == "state": + rarg = "m_state" + elif aspec.varargs == "saltenv": + rarg = "m_saltenv" else: rarg = aspec.varargs @@ -564,8 +586,8 @@ def _run(name, **kwargs): if not isinstance(varargs, list): msg = "'{0}' must be a list." - ret['comment'] = msg.format(aspec.varargs) - ret['result'] = False + ret["comment"] = msg.format(aspec.varargs) + ret["result"] = False return ret args.extend(varargs) @@ -575,8 +597,8 @@ def _run(name, **kwargs): nkwargs = kwargs.pop(aspec.keywords) if not isinstance(nkwargs, dict): msg = "'{0}' must be a dict." - ret['comment'] = msg.format(aspec.keywords) - ret['result'] = False + ret["comment"] = msg.format(aspec.keywords) + ret["result"] = False return ret try: @@ -585,24 +607,27 @@ def _run(name, **kwargs): else: mret = __salt__[name](*args) except Exception as e: # pylint: disable=broad-except - ret['comment'] = 'Module function {0} threw an exception. Exception: {1}'.format(name, e) - ret['result'] = False + ret[ + "comment" + ] = "Module function {0} threw an exception. Exception: {1}".format(name, e) + ret["result"] = False return ret else: if mret is not None or mret is not {}: - ret['changes']['ret'] = mret + ret["changes"]["ret"] = mret - if 'returner' in kwargs: + if "returner" in kwargs: ret_ret = { - 'id': __opts__['id'], - 'ret': mret, - 'fun': name, - 'jid': salt.utils.jid.gen_jid(__opts__)} + "id": __opts__["id"], + "ret": mret, + "fun": name, + "jid": salt.utils.jid.gen_jid(__opts__), + } returners = salt.loader.returners(__opts__, __salt__) - if kwargs['returner'] in returners: - returners[kwargs['returner']](ret_ret) - ret['comment'] = 'Module function {0} executed'.format(name) - ret['result'] = _get_result(mret, ret['changes']) + if kwargs["returner"] in returners: + returners[kwargs["returner"]](ret_ret) + ret["comment"] = "Module function {0} executed".format(name) + ret["result"] = _get_result(mret, ret["changes"]) return ret @@ -610,17 +635,17 @@ def _run(name, **kwargs): def _get_result(func_ret, changes): res = True # if mret is a dict and there is retcode and its non-zero - if isinstance(func_ret, dict) and func_ret.get('retcode', 0) != 0: + if isinstance(func_ret, dict) and func_ret.get("retcode", 0) != 0: res = False # if its a boolean, return that as the result elif isinstance(func_ret, bool): res = func_ret else: - changes_ret = changes.get('ret', {}) + changes_ret = changes.get("ret", {}) if isinstance(changes_ret, dict): - if isinstance(changes_ret.get('result', {}), bool): - res = changes_ret.get('result', {}) - elif changes_ret.get('retcode', 0) != 0: + if isinstance(changes_ret.get("result", {}), bool): + res = changes_ret.get("result", {}) + elif changes_ret.get("retcode", 0) != 0: res = False # Explore dict in depth to determine if there is a # 'result' key set to False which sets the global @@ -634,7 +659,7 @@ def _get_result(func_ret, changes): def _get_dict_result(node): ret = True for key, val in six.iteritems(node): - if key == 'result' and val is False: + if key == "result" and val is False: ret = False break elif isinstance(val, dict): @@ -644,4 +669,4 @@ def _get_dict_result(node): return ret -mod_watch = salt.utils.functools.alias_function(run, 'mod_watch') +mod_watch = salt.utils.functools.alias_function(run, "mod_watch") diff --git a/salt/states/mongodb_database.py b/salt/states/mongodb_database.py index 579f188e4a1..1563b5c2407 100644 --- a/salt/states/mongodb_database.py +++ b/salt/states/mongodb_database.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of MongoDB Databases =============================== @@ -7,26 +7,21 @@ Management of MongoDB Databases Only deletion is supported, creation doesn't make sense and can be done using :py:func:`mongodb_user.present <salt.states.mongodb_user.present>`. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Define the module's virtual name -__virtualname__ = 'mongodb_database' +__virtualname__ = "mongodb_database" def __virtual__(): - if 'mongodb.db_exists' in __salt__: + if "mongodb.db_exists" in __salt__: return __virtualname__ - return False + return (False, "mongodb module could not be loaded") -def absent(name, - user=None, - password=None, - host=None, - port=None, - authdb=None): - ''' +def absent(name, user=None, password=None, host=None, port=None, authdb=None): + """ Ensure that the named database is absent. Note that creation doesn't make sense in MongoDB. @@ -47,22 +42,22 @@ def absent(name, authdb The database in which to authenticate - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if __salt__['mongodb.db_exists'](name, user, password, host, port, authdb=authdb): - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Database {0} is present and needs to be removed' - ).format(name) + if __salt__["mongodb.db_exists"](name, user, password, host, port, authdb=authdb): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ("Database {0} is present and needs to be removed").format( + name + ) return ret - if __salt__['mongodb.db_remove'](name, user, password, host, port, authdb=authdb): - ret['comment'] = 'Database {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["mongodb.db_remove"]( + name, user, password, host, port, authdb=authdb + ): + ret["comment"] = "Database {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret - ret['comment'] = 'Database {0} is not present'.format(name) + ret["comment"] = "Database {0} is not present".format(name) return ret diff --git a/salt/states/mongodb_user.py b/salt/states/mongodb_user.py index 42a86eea68f..2d684e08964 100644 --- a/salt/states/mongodb_user.py +++ b/salt/states/mongodb_user.py @@ -1,32 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Management of MongoDB Users =========================== :depends: - pymongo Python module -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Define the module's virtual name -__virtualname__ = 'mongodb_user' +__virtualname__ = "mongodb_user" def __virtual__(): - if 'mongodb.user_exists' in __salt__: + if "mongodb.user_exists" in __salt__: return __virtualname__ - return False + return (False, "mongodb module could not be loaded") -def present(name, - passwd, - database="admin", - user=None, - password=None, - host="localhost", - port=27017, - authdb=None, - roles=None): - ''' +def present( + name, + passwd, + database="admin", + user=None, + password=None, + host="localhost", + port=27017, + authdb=None, + roles=None, +): + """ Ensure that the user is present with the specified properties name @@ -76,11 +78,13 @@ def present(name, - userAdmin - dbOwner - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User {0} is already present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "User {0} is already present".format(name), + } # setup default empty roles if not provided to preserve previous API interface if roles is None: @@ -90,19 +94,21 @@ def present(name, try: port = int(port) except TypeError: - ret['result'] = False - ret['comment'] = 'Port ({0}) is not an integer.'.format(port) + ret["result"] = False + ret["comment"] = "Port ({0}) is not an integer.".format(port) return ret # check if user exists - users = __salt__['mongodb.user_find'](name, user, password, host, port, database, authdb) + users = __salt__["mongodb.user_find"]( + name, user, password, host, port, database, authdb + ) if len(users) > 0: # check for errors returned in users e.g. # users= (False, 'Failed to connect to MongoDB database localhost:27017') # users= (False, 'not authorized on admin to execute command { usersInfo: "root" }') if not users[0]: - ret['result'] = False - ret['comment'] = "Mongo Err: {0}".format(users[1]) + ret["result"] = False + ret["comment"] = "Mongo Err: {0}".format(users[1]) return ret # check each user occurrence @@ -110,49 +116,73 @@ def present(name, # prepare empty list for current roles current_roles = [] # iterate over user roles and append each to current_roles list - for role in usr['roles']: + for role in usr["roles"]: # check correct database to be sure to fill current_roles only for desired db - if role['db'] == database: - current_roles.append(role['role']) + if role["db"] == database: + current_roles.append(role["role"]) # fill changes if the roles and current roles differ if not set(current_roles) == set(roles): - ret['changes'].update({name: {'database': database, 'roles': {'old': current_roles, 'new': roles}}}) + ret["changes"].update( + { + name: { + "database": database, + "roles": {"old": current_roles, "new": roles}, + } + } + ) - __salt__['mongodb.user_create'](name, passwd, user, password, host, port, database=database, authdb=authdb, roles=roles) + __salt__["mongodb.user_create"]( + name, + passwd, + user, + password, + host, + port, + database=database, + authdb=authdb, + roles=roles, + ) return ret # if the check does not return a boolean, return an error # this may be the case if there is a database connection error if not isinstance(users, list): - ret['comment'] = users - ret['result'] = False + ret["comment"] = users + ret["result"] = False return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('User {0} is not present and needs to be created' - ).format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ("User {0} is not present and needs to be created").format( + name + ) return ret # The user is not present, make it! - if __salt__['mongodb.user_create'](name, passwd, user, password, host, port, database=database, authdb=authdb, roles=roles): - ret['comment'] = 'User {0} has been created'.format(name) - ret['changes'][name] = 'Present' + if __salt__["mongodb.user_create"]( + name, + passwd, + user, + password, + host, + port, + database=database, + authdb=authdb, + roles=roles, + ): + ret["comment"] = "User {0} has been created".format(name) + ret["changes"][name] = "Present" else: - ret['comment'] = 'Failed to create database {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create database {0}".format(name) + ret["result"] = False return ret -def absent(name, - user=None, - password=None, - host=None, - port=None, - database="admin", - authdb=None): - ''' +def absent( + name, user=None, password=None, host=None, port=None, database="admin", authdb=None +): + """ Ensure that the named user is absent name @@ -176,32 +206,34 @@ def absent(name, authdb The database in which to authenticate - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - #check if user exists and remove it - user_exists = __salt__['mongodb.user_exists'](name, user, password, host, port, database=database, authdb=authdb) + # check if user exists and remove it + user_exists = __salt__["mongodb.user_exists"]( + name, user, password, host, port, database=database, authdb=authdb + ) if user_exists is True: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('User {0} is present and needs to be removed' - ).format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ("User {0} is present and needs to be removed").format( + name + ) return ret - if __salt__['mongodb.user_remove'](name, user, password, host, port, database=database, authdb=authdb): - ret['comment'] = 'User {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["mongodb.user_remove"]( + name, user, password, host, port, database=database, authdb=authdb + ): + ret["comment"] = "User {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret # if the check does not return a boolean, return an error # this may be the case if there is a database connection error if not isinstance(user_exists, bool): - ret['comment'] = user_exists - ret['result'] = False + ret["comment"] = user_exists + ret["result"] = False return ret # fallback - ret['comment'] = 'User {0} is not present'.format(name) + ret["comment"] = "User {0} is not present".format(name) return ret diff --git a/salt/states/monit.py b/salt/states/monit.py index 9e7386a7e63..cdf2fbc80de 100644 --- a/salt/states/monit.py +++ b/salt/states/monit.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Monit state =========== @@ -18,82 +18,78 @@ Manage monit states .. note:: Use of these states require that the :mod:`monit <salt.modules.monit>` execution module is available. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only make this state available if the monit module is available. - ''' - return 'monit' if 'monit.summary' in __salt__ else False + """ + if "monit.summary" in __salt__: + return "monit" + return (False, "monit module could not be loaded") def monitor(name): - ''' + """ Get the summary from module monit and try to see if service is being monitored. If not then monitor the service. - ''' - ret = {'result': None, - 'name': name, - 'comment': '', - 'changes': {} - } - result = __salt__['monit.summary'](name) + """ + ret = {"result": None, "name": name, "comment": "", "changes": {}} + result = __salt__["monit.summary"](name) try: for key, value in result.items(): - if 'Running' in value[name]: - ret['comment'] = ('{0} is being being monitored.').format(name) - ret['result'] = True + if "Running" in value[name]: + ret["comment"] = ("{0} is being being monitored.").format(name) + ret["result"] = True else: - if __opts__['test']: - ret['comment'] = 'Service {0} is set to be monitored.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Service {0} is set to be monitored.".format(name) + ret["result"] = None return ret - __salt__['monit.monitor'](name) - ret['comment'] = ('{0} started to be monitored.').format(name) - ret['changes'][name] = 'Running' - ret['result'] = True + __salt__["monit.monitor"](name) + ret["comment"] = ("{0} started to be monitored.").format(name) + ret["changes"][name] = "Running" + ret["result"] = True break except KeyError: - ret['comment'] = ('{0} not found in configuration.').format(name) - ret['result'] = False + ret["comment"] = ("{0} not found in configuration.").format(name) + ret["result"] = False return ret def unmonitor(name): - ''' + """ Get the summary from module monit and try to see if service is being monitored. If it is then stop monitoring the service. - ''' - ret = {'result': None, - 'name': name, - 'comment': '', - 'changes': {} - } - result = __salt__['monit.summary'](name) + """ + ret = {"result": None, "name": name, "comment": "", "changes": {}} + result = __salt__["monit.summary"](name) try: for key, value in result.items(): - if 'Not monitored' in value[name]: - ret['comment'] = ('{0} is not being monitored.').format(name) - ret['result'] = True + if "Not monitored" in value[name]: + ret["comment"] = ("{0} is not being monitored.").format(name) + ret["result"] = True else: - if __opts__['test']: - ret['comment'] = 'Service {0} is set to be unmonitored.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Service {0} is set to be unmonitored.".format( + name + ) + ret["result"] = None return ret - __salt__['monit.unmonitor'](name) - ret['comment'] = ('{0} stopped being monitored.').format(name) - ret['changes'][name] = 'Not monitored' - ret['result'] = True + __salt__["monit.unmonitor"](name) + ret["comment"] = ("{0} stopped being monitored.").format(name) + ret["changes"][name] = "Not monitored" + ret["result"] = True break except KeyError: - ret['comment'] = ('{0} not found in configuration.').format(name) - ret['result'] = False + ret["comment"] = ("{0} not found in configuration.").format(name) + ret["result"] = False return ret diff --git a/salt/states/mount.py b/salt/states/mount.py index 1a7a3d0746c..eee1de38f4e 100644 --- a/salt/states/mount.py +++ b/salt/states/mount.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Mounting of filesystems ======================= @@ -34,50 +34,54 @@ Mount any type of mountable filesystem with the mounted function: - pass_num: 0 - persist: True - mkmnt: True -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os.path import re +from salt.ext import six + # Import salt libs from salt.ext.six import string_types -import logging -from salt.ext import six log = logging.getLogger(__name__) def _size_convert(_re_size): - converted_size = int(_re_size.group('size_value')) - if _re_size.group('size_unit') == 'm': + converted_size = int(_re_size.group("size_value")) + if _re_size.group("size_unit") == "m": converted_size = int(converted_size) * 1024 - if _re_size.group('size_unit') == 'g': + if _re_size.group("size_unit") == "g": converted_size = int(converted_size) * 1024 * 1024 return converted_size -def mounted(name, - device, - fstype, - mkmnt=False, - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - persist=True, - mount=True, - user=None, - match_on='auto', - device_name_regex=None, - extra_mount_invisible_options=None, - extra_mount_invisible_keys=None, - extra_mount_ignore_fs_keys=None, - extra_mount_translate_options=None, - hidden_opts=None, - **kwargs): - ''' +def mounted( + name, + device, + fstype, + mkmnt=False, + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + persist=True, + mount=True, + user=None, + match_on="auto", + device_name_regex=None, + extra_mount_invisible_options=None, + extra_mount_invisible_keys=None, + extra_mount_ignore_fs_keys=None, + extra_mount_translate_options=None, + hidden_opts=None, + **kwargs +): + """ Verify that a device is mounted name @@ -191,95 +195,104 @@ def mounted(name, as part of the state application .. versionadded:: 2015.8.2 - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} update_mount_cache = False if not name: - ret['result'] = False - ret['comment'] = 'Must provide name to mount.mounted' + ret["result"] = False + ret["comment"] = "Must provide name to mount.mounted" return ret if not device: - ret['result'] = False - ret['comment'] = 'Must provide device to mount.mounted' + ret["result"] = False + ret["comment"] = "Must provide device to mount.mounted" return ret if not fstype: - ret['result'] = False - ret['comment'] = 'Must provide fstype to mount.mounted' + ret["result"] = False + ret["comment"] = "Must provide fstype to mount.mounted" return ret if device_name_regex is None: device_name_regex = [] # Defaults is not a valid option on Mac OS - if __grains__['os'] in ['MacOS', 'Darwin'] and opts == 'defaults': - opts = 'noowners' + if __grains__["os"] in ["MacOS", "Darwin"] and opts == "defaults": + opts = "noowners" # Defaults is not a valid option on AIX - if __grains__['os'] in ['AIX']: - if opts == 'defaults': - opts = '' + if __grains__["os"] in ["AIX"]: + if opts == "defaults": + opts = "" # Make sure that opts is correct, it can be a list or a comma delimited # string if isinstance(opts, string_types): - opts = opts.split(',') + opts = opts.split(",") if isinstance(hidden_opts, string_types): - hidden_opts = hidden_opts.split(',') + hidden_opts = hidden_opts.split(",") # remove possible trailing slash - if not name == '/': - name = name.rstrip('/') + if not name == "/": + name = name.rstrip("/") device_list = [] # Get the active data - active = __salt__['mount.active'](extended=True) + active = __salt__["mount.active"](extended=True) real_name = os.path.realpath(name) - if device.startswith('/'): - if 'bind' in opts and real_name in active: + if device.startswith("/"): + if "bind" in opts and real_name in active: _device = device - if active[real_name]['device'].startswith('/'): + if active[real_name]["device"].startswith("/"): # Find the device that the bind really points at. while True: if _device in active: - _real_device = active[_device]['device'] - opts = list(set(opts + active[_device]['opts'] + active[_device]['superopts'])) - active[real_name]['opts'].append('bind') + _real_device = active[_device]["device"] + opts = list( + set( + opts + + active[_device]["opts"] + + active[_device]["superopts"] + ) + ) + active[real_name]["opts"].append("bind") break _device = os.path.dirname(_device) real_device = _real_device else: # Remote file systems act differently. if _device in active: - opts = list(set(opts + active[_device]['opts'] + active[_device]['superopts'])) - active[real_name]['opts'].append('bind') - real_device = active[real_name]['device'] + opts = list( + set( + opts + + active[_device]["opts"] + + active[_device]["superopts"] + ) + ) + active[real_name]["opts"].append("bind") + real_device = active[real_name]["device"] else: real_device = os.path.realpath(device) - elif device.upper().startswith('UUID='): - real_device = device.split('=')[1].strip('"').lower() - elif device.upper().startswith('LABEL='): - _label = device.split('=')[1] - cmd = 'blkid -t LABEL={0}'.format(_label) - res = __salt__['cmd.run_all']('{0}'.format(cmd)) - if res['retcode'] > 0: - ret['comment'] = 'Unable to find device with label {0}.'.format(_label) - ret['result'] = False + elif device.upper().startswith("UUID="): + real_device = device.split("=")[1].strip('"').lower() + elif device.upper().startswith("LABEL="): + _label = device.split("=")[1] + cmd = "blkid -t LABEL={0}".format(_label) + res = __salt__["cmd.run_all"]("{0}".format(cmd)) + if res["retcode"] > 0: + ret["comment"] = "Unable to find device with label {0}.".format(_label) + ret["result"] = False return ret else: # output is a list of entries like this: # /dev/sda: LABEL="<label>" UUID="<uuid>" UUID_SUB="<uuid>" TYPE="btrfs" # exact list of properties varies between filesystems, but we're # only interested in the device in the first column - for line in res['stdout']: - dev_with_label = line.split(':')[0] + for line in res["stdout"]: + dev_with_label = line.split(":")[0] device_list.append(dev_with_label) real_device = device_list[0] else: @@ -292,10 +305,12 @@ def mounted(name, # Note the double-dash escaping. # So, let's call that the canonical device name # We should normalize names of the /dev/vg-name/lv-name type to the canonical name - lvs_match = re.match(r'^/dev/(?P<vg_name>[^/]+)/(?P<lv_name>[^/]+$)', device) + lvs_match = re.match(r"^/dev/(?P<vg_name>[^/]+)/(?P<lv_name>[^/]+$)", device) if lvs_match: - double_dash_escaped = dict((k, re.sub(r'-', '--', v)) for k, v in six.iteritems(lvs_match.groupdict())) - mapper_device = '/dev/mapper/{vg_name}-{lv_name}'.format(**double_dash_escaped) + double_dash_escaped = dict( + (k, re.sub(r"-", "--", v)) for k, v in six.iteritems(lvs_match.groupdict()) + ) + mapper_device = "/dev/mapper/{vg_name}-{lv_name}".format(**double_dash_escaped) if os.path.exists(mapper_device): real_device = mapper_device @@ -304,20 +319,32 @@ def mounted(name, # hostname is included. So if we detect that the device is a FUSE device # then we remove the prefaced string so that the device in state matches # the device in the mount list. - fuse_match = re.match(r'^\w+\#(?P<device_name>.+)', device) + fuse_match = re.match(r"^\w+\#(?P<device_name>.+)", device) if fuse_match: - if 'device_name' in fuse_match.groupdict(): - real_device = fuse_match.group('device_name') + if "device_name" in fuse_match.groupdict(): + real_device = fuse_match.group("device_name") if real_name in active: - if 'superopts' not in active[real_name]: - active[real_name]['superopts'] = [] + if "superopts" not in active[real_name]: + active[real_name]["superopts"] = [] if mount: - device_list.append(active[real_name]['device']) + device_list.append(active[real_name]["device"]) device_list.append(os.path.realpath(device_list[0])) - alt_device = active[real_name]['alt_device'] if 'alt_device' in active[real_name] else None - uuid_device = active[real_name]['device_uuid'] if 'device_uuid' in active[real_name] else None - label_device = active[real_name]['device_label'] if 'device_label' in active[real_name] else None + alt_device = ( + active[real_name]["alt_device"] + if "alt_device" in active[real_name] + else None + ) + uuid_device = ( + active[real_name]["device_uuid"] + if "device_uuid" in active[real_name] + else None + ) + label_device = ( + active[real_name]["device_label"] + if "device_label" in active[real_name] + else None + ) if alt_device and alt_device not in device_list: device_list.append(alt_device) if uuid_device and uuid_device not in device_list: @@ -328,64 +355,64 @@ def mounted(name, opts.sort() mount_invisible_options = [ - '_netdev', - 'actimeo', - 'bg', - 'comment', - 'defaults', - 'delay_connect', - 'direct-io-mode', - 'intr', - 'loop', - 'nointr', - 'nobootwait', - 'nofail', - 'password', - 'reconnect', - 'retry', - 'soft', - 'auto', - 'users', - 'bind', - 'nonempty', - 'transform_symlinks', - 'port', - 'backup-volfile-servers', + "_netdev", + "actimeo", + "bg", + "comment", + "defaults", + "delay_connect", + "direct-io-mode", + "intr", + "loop", + "nointr", + "nobootwait", + "nofail", + "password", + "reconnect", + "retry", + "soft", + "auto", + "users", + "bind", + "nonempty", + "transform_symlinks", + "port", + "backup-volfile-servers", ] if extra_mount_invisible_options: mount_invisible_options.extend(extra_mount_invisible_options) if hidden_opts: - mount_invisible_options = list(set(mount_invisible_options) | set(hidden_opts)) + mount_invisible_options = list( + set(mount_invisible_options) | set(hidden_opts) + ) # options which are provided as key=value (e.g. password=Zohp5ohb) mount_invisible_keys = [ - 'actimeo', - 'comment', - 'credentials', - 'direct-io-mode', - 'password', - 'port', - 'retry', - 'secretfile', + "actimeo", + "comment", + "credentials", + "direct-io-mode", + "password", + "port", + "retry", + "secretfile", ] if extra_mount_invisible_keys: mount_invisible_keys.extend(extra_mount_invisible_keys) # Some filesystems have options which should not force a remount. - mount_ignore_fs_keys = { - 'ramfs': ['size'] - } + mount_ignore_fs_keys = {"ramfs": ["size"]} if extra_mount_ignore_fs_keys: mount_ignore_fs_keys.update(extra_mount_ignore_fs_keys) # Some options are translated once mounted mount_translate_options = { - 'tcp': 'proto=tcp', - 'udp': 'proto=udp', + "tcp": "proto=tcp", + "udp": "proto=udp", } if extra_mount_translate_options: @@ -395,37 +422,41 @@ def mounted(name, if opt in mount_translate_options: opt = mount_translate_options[opt] - keyval_option = opt.split('=')[0] + keyval_option = opt.split("=")[0] if keyval_option in mount_invisible_keys: opt = keyval_option - size_match = re.match(r'size=(?P<size_value>[0-9]+)(?P<size_unit>k|m|g)', opt) + size_match = re.match( + r"size=(?P<size_value>[0-9]+)(?P<size_unit>k|m|g)", opt + ) if size_match: converted_size = _size_convert(size_match) opt = "size={0}k".format(converted_size) # make cifs option user synonym for option username which is reported by /proc/mounts - if fstype in ['cifs'] and opt.split('=')[0] == 'user': - opt = "username={0}".format(opt.split('=')[1]) + if fstype in ["cifs"] and opt.split("=")[0] == "user": + opt = "username={0}".format(opt.split("=")[1]) - if opt.split('=')[0] in mount_ignore_fs_keys.get(fstype, []): - opt = opt.split('=')[0] + if opt.split("=")[0] in mount_ignore_fs_keys.get(fstype, []): + opt = opt.split("=")[0] # convert uid/gid to numeric value from user/group name - name_id_opts = {'uid': 'user.info', - 'gid': 'group.info'} - if opt.split('=')[0] in name_id_opts and len(opt.split('=')) > 1: - _givenid = opt.split('=')[1] - _param = opt.split('=')[0] + name_id_opts = {"uid": "user.info", "gid": "group.info"} + if opt.split("=")[0] in name_id_opts and len(opt.split("=")) > 1: + _givenid = opt.split("=")[1] + _param = opt.split("=")[0] _id = _givenid - if not re.match('[0-9]+$', _givenid): + if not re.match("[0-9]+$", _givenid): _info = __salt__[name_id_opts[_param]](_givenid) if _info and _param in _info: _id = _info[_param] - opt = _param + '=' + six.text_type(_id) + opt = _param + "=" + six.text_type(_id) - _active_superopts = active[real_name].get('superopts', []) + _active_superopts = active[real_name].get("superopts", []) for _active_opt in _active_superopts: - size_match = re.match(r'size=(?P<size_value>[0-9]+)(?P<size_unit>k|m|g)', _active_opt) + size_match = re.match( + r"size=(?P<size_value>[0-9]+)(?P<size_unit>k|m|g)", + _active_opt, + ) if size_match: converted_size = _size_convert(size_match) opt = "size={0}k".format(converted_size) @@ -433,77 +464,124 @@ def mounted(name, _active_opt = "size={0}k".format(converted_size) _active_superopts.append(_active_opt) - if opt not in active[real_name]['opts'] \ - and opt not in _active_superopts \ - and opt not in mount_invisible_options \ - and opt not in mount_ignore_fs_keys.get(fstype, []) \ - and opt not in mount_invisible_keys: - if __opts__['test']: - ret['result'] = None - ret['comment'] = "Remount would be forced because options ({0}) changed".format(opt) + if ( + opt not in active[real_name]["opts"] + and opt not in _active_superopts + and opt not in mount_invisible_options + and opt not in mount_ignore_fs_keys.get(fstype, []) + and opt not in mount_invisible_keys + ): + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "Remount would be forced because options ({0}) changed".format( + opt + ) return ret else: # Some file systems require umounting and mounting if options change # add others to list that require similiar functionality - if fstype in ['nfs', 'cvfs'] or fstype.startswith('fuse'): - ret['changes']['umount'] = "Forced unmount and mount because " \ - + "options ({0}) changed".format(opt) - unmount_result = __salt__['mount.umount'](real_name) + if fstype in ["nfs", "cvfs"] or fstype.startswith("fuse"): + ret["changes"]["umount"] = ( + "Forced unmount and mount because " + + "options ({0}) changed".format(opt) + ) + unmount_result = __salt__["mount.umount"](real_name) if unmount_result is True: - mount_result = __salt__['mount.mount'](real_name, device, mkmnt=mkmnt, fstype=fstype, opts=opts) - ret['result'] = mount_result + mount_result = __salt__["mount.mount"]( + real_name, + device, + mkmnt=mkmnt, + fstype=fstype, + opts=opts, + ) + ret["result"] = mount_result else: - ret['result'] = False - ret['comment'] = 'Unable to unmount {0}: {1}.'.format(real_name, unmount_result) + ret["result"] = False + ret[ + "comment" + ] = "Unable to unmount {0}: {1}.".format( + real_name, unmount_result + ) return ret else: - ret['changes']['umount'] = "Forced remount because " \ - + "options ({0}) changed".format(opt) - remount_result = __salt__['mount.remount'](real_name, device, mkmnt=mkmnt, fstype=fstype, opts=opts) - ret['result'] = remount_result + ret["changes"]["umount"] = ( + "Forced remount because " + + "options ({0}) changed".format(opt) + ) + remount_result = __salt__["mount.remount"]( + real_name, + device, + mkmnt=mkmnt, + fstype=fstype, + opts=opts, + ) + ret["result"] = remount_result # Cleanup after the remount, so we # don't write remount into fstab - if 'remount' in opts: - opts.remove('remount') + if "remount" in opts: + opts.remove("remount") # Update the cache update_mount_cache = True - mount_cache = __salt__['mount.read_mount_cache'](real_name) - if 'opts' in mount_cache: - _missing = [opt for opt in mount_cache['opts'] - if opt not in opts] + mount_cache = __salt__["mount.read_mount_cache"](real_name) + if "opts" in mount_cache: + _missing = [opt for opt in mount_cache["opts"] if opt not in opts] if _missing: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Remount would be forced because' - ' options ({0})' - 'changed'.format(','.join(_missing))) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Remount would be forced because" + " options ({0})" + "changed".format(",".join(_missing)) + ) return ret else: # Some file systems require umounting and mounting if options change # add others to list that require similiar functionality - if fstype in ['nfs', 'cvfs'] or fstype.startswith('fuse'): - ret['changes']['umount'] = "Forced unmount and mount because " \ - + "options ({0}) changed".format(opt) - unmount_result = __salt__['mount.umount'](real_name) + if fstype in ["nfs", "cvfs"] or fstype.startswith("fuse"): + ret["changes"]["umount"] = ( + "Forced unmount and mount because " + + "options ({0}) changed".format(opt) + ) + unmount_result = __salt__["mount.umount"](real_name) if unmount_result is True: - mount_result = __salt__['mount.mount'](real_name, device, mkmnt=mkmnt, fstype=fstype, opts=opts) - ret['result'] = mount_result + mount_result = __salt__["mount.mount"]( + real_name, + device, + mkmnt=mkmnt, + fstype=fstype, + opts=opts, + ) + ret["result"] = mount_result else: - ret['result'] = False - ret['comment'] = 'Unable to unmount {0}: {1}.'.format(real_name, unmount_result) + ret["result"] = False + ret[ + "comment" + ] = "Unable to unmount {0}: {1}.".format( + real_name, unmount_result + ) return ret else: - ret['changes']['umount'] = "Forced remount because " \ - + "options ({0}) changed".format(opt) - remount_result = __salt__['mount.remount'](real_name, device, mkmnt=mkmnt, fstype=fstype, opts=opts) - ret['result'] = remount_result + ret["changes"]["umount"] = ( + "Forced remount because " + + "options ({0}) changed".format(opt) + ) + remount_result = __salt__["mount.remount"]( + real_name, + device, + mkmnt=mkmnt, + fstype=fstype, + opts=opts, + ) + ret["result"] = remount_result # Cleanup after the remount, so we # don't write remount into fstab - if 'remount' in opts: - opts.remove('remount') + if "remount" in opts: + opts.remove("remount") update_mount_cache = True else: @@ -518,201 +596,219 @@ def mounted(name, _device_mismatch_is_ignored = _device break if _device_mismatch_is_ignored: - ret['result'] = True - ret['comment'] = "An umount will not be forced " \ - + "because device matched device_name_regex: " \ - + _device_mismatch_is_ignored - elif __opts__['test']: - ret['result'] = None - ret['comment'] = "An umount would have been forced " \ - + "because devices do not match. Watched: " \ - + device + ret["result"] = True + ret["comment"] = ( + "An umount will not be forced " + + "because device matched device_name_regex: " + + _device_mismatch_is_ignored + ) + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "An umount would have been forced " + + "because devices do not match. Watched: " + + device + ) else: - ret['changes']['umount'] = "Forced unmount because devices " \ - + "don't match. Wanted: " + device + ret["changes"]["umount"] = ( + "Forced unmount because devices " + + "don't match. Wanted: " + + device + ) if real_device != device: - ret['changes']['umount'] += " (" + real_device + ")" - ret['changes']['umount'] += ", current: " + ', '.join(device_list) - out = __salt__['mount.umount'](real_name, user=user) - active = __salt__['mount.active'](extended=True) + ret["changes"]["umount"] += " (" + real_device + ")" + ret["changes"]["umount"] += ", current: " + ", ".join(device_list) + out = __salt__["mount.umount"](real_name, user=user) + active = __salt__["mount.active"](extended=True) if real_name in active: - ret['comment'] = "Unable to unmount" - ret['result'] = None + ret["comment"] = "Unable to unmount" + ret["result"] = None return ret update_mount_cache = True else: - ret['comment'] = 'Target was already mounted' + ret["comment"] = "Target was already mounted" # using a duplicate check so I can catch the results of a umount if real_name not in active: if mount: # The mount is not present! Mount it - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if os.path.exists(name): - ret['comment'] = '{0} would be mounted'.format(name) + ret["comment"] = "{0} would be mounted".format(name) elif mkmnt: - ret['comment'] = '{0} would be created and mounted'.format(name) + ret["comment"] = "{0} would be created and mounted".format(name) else: - ret['comment'] = '{0} does not exist and would not be created'.format(name) + ret[ + "comment" + ] = "{0} does not exist and would not be created".format(name) return ret if not os.path.exists(name) and not mkmnt: - ret['result'] = False - ret['comment'] = 'Mount directory is not present' + ret["result"] = False + ret["comment"] = "Mount directory is not present" return ret - out = __salt__['mount.mount'](name, device, mkmnt, fstype, opts, user=user) - active = __salt__['mount.active'](extended=True) + out = __salt__["mount.mount"](name, device, mkmnt, fstype, opts, user=user) + active = __salt__["mount.active"](extended=True) update_mount_cache = True if isinstance(out, string_types): # Failed to (re)mount, the state has failed! - ret['comment'] = out - ret['result'] = False + ret["comment"] = out + ret["result"] = False return ret elif real_name in active: # (Re)mount worked! - ret['comment'] = 'Target was successfully mounted' - ret['changes']['mount'] = True + ret["comment"] = "Target was successfully mounted" + ret["changes"]["mount"] = True elif not os.path.exists(name): - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if mkmnt: - ret['comment'] = '{0} would be created, but not mounted'.format(name) + ret["comment"] = "{0} would be created, but not mounted".format( + name + ) else: - ret['comment'] = '{0} does not exist and would neither be created nor mounted'.format(name) + ret[ + "comment" + ] = "{0} does not exist and would neither be created nor mounted".format( + name + ) elif mkmnt: - __salt__['file.mkdir'](name, user=user) - ret['comment'] = '{0} was created, not mounted'.format(name) + __salt__["file.mkdir"](name, user=user) + ret["comment"] = "{0} was created, not mounted".format(name) else: - ret['comment'] = '{0} not present and not mounted'.format(name) + ret["comment"] = "{0} not present and not mounted".format(name) else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = '{0} would not be mounted'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "{0} would not be mounted".format(name) else: - ret['comment'] = '{0} not mounted'.format(name) + ret["comment"] = "{0} not mounted".format(name) if persist: - if '/etc/fstab' == config: + if "/etc/fstab" == config: # Override default for Mac OS - if __grains__['os'] in ['MacOS', 'Darwin']: + if __grains__["os"] in ["MacOS", "Darwin"]: config = "/etc/auto_salt" # Override default for AIX - elif 'AIX' in __grains__['os']: + elif "AIX" in __grains__["os"]: config = "/etc/filesystems" - if __opts__['test']: - if __grains__['os'] in ['MacOS', 'Darwin']: - out = __salt__['mount.set_automaster'](name, - device, - fstype, - opts, - config, - test=True) - elif __grains__['os'] in ['AIX']: - out = __salt__['mount.set_filesystems'](name, - device, - fstype, - opts, - mount, - config, - test=True, - match_on=match_on) + if __opts__["test"]: + if __grains__["os"] in ["MacOS", "Darwin"]: + out = __salt__["mount.set_automaster"]( + name, device, fstype, opts, config, test=True + ) + elif __grains__["os"] in ["AIX"]: + out = __salt__["mount.set_filesystems"]( + name, + device, + fstype, + opts, + mount, + config, + test=True, + match_on=match_on, + ) else: - out = __salt__['mount.set_fstab'](name, - device, - fstype, - opts, - dump, - pass_num, - config, - test=True, - match_on=match_on) - if out != 'present': - ret['result'] = None - if out == 'new': + out = __salt__["mount.set_fstab"]( + name, + device, + fstype, + opts, + dump, + pass_num, + config, + test=True, + match_on=match_on, + ) + if out != "present": + ret["result"] = None + if out == "new": if mount: - comment = ('{0} is mounted, but needs to be ' - 'written to the fstab in order to be ' - 'made persistent.').format(name) + comment = ( + "{0} is mounted, but needs to be " + "written to the fstab in order to be " + "made persistent." + ).format(name) else: - comment = ('{0} needs to be ' - 'written to the fstab in order to be ' - 'made persistent.').format(name) - elif out == 'change': + comment = ( + "{0} needs to be " + "written to the fstab in order to be " + "made persistent." + ).format(name) + elif out == "change": if mount: - comment = ('{0} is mounted, but its fstab entry ' - 'must be updated.').format(name) + comment = ( + "{0} is mounted, but its fstab entry " "must be updated." + ).format(name) else: - comment = ('The {0} fstab entry ' - 'must be updated.').format(name) + comment = ("The {0} fstab entry " "must be updated.").format( + name + ) else: - ret['result'] = False - comment = ('Unable to detect fstab status for ' - 'mount point {0} due to unexpected ' - 'output \'{1}\' from call to ' - 'mount.set_fstab. This is most likely ' - 'a bug.').format(name, out) - if 'comment' in ret: - ret['comment'] = '{0}. {1}'.format(ret['comment'], comment) + ret["result"] = False + comment = ( + "Unable to detect fstab status for " + "mount point {0} due to unexpected " + "output '{1}' from call to " + "mount.set_fstab. This is most likely " + "a bug." + ).format(name, out) + if "comment" in ret: + ret["comment"] = "{0}. {1}".format(ret["comment"], comment) else: - ret['comment'] = comment + ret["comment"] = comment return ret else: - if __grains__['os'] in ['MacOS', 'Darwin']: - out = __salt__['mount.set_automaster'](name, - device, - fstype, - opts, - config) - elif __grains__['os'] in ['AIX']: - out = __salt__['mount.set_filesystems'](name, - device, - fstype, - opts, - mount, - config, - match_on=match_on) + if __grains__["os"] in ["MacOS", "Darwin"]: + out = __salt__["mount.set_automaster"]( + name, device, fstype, opts, config + ) + elif __grains__["os"] in ["AIX"]: + out = __salt__["mount.set_filesystems"]( + name, device, fstype, opts, mount, config, match_on=match_on + ) else: - out = __salt__['mount.set_fstab'](name, - device, - fstype, - opts, - dump, - pass_num, - config, - match_on=match_on) + out = __salt__["mount.set_fstab"]( + name, + device, + fstype, + opts, + dump, + pass_num, + config, + match_on=match_on, + ) if update_mount_cache: - cache_result = __salt__['mount.write_mount_cache'](real_name, - device, - mkmnt=mkmnt, - fstype=fstype, - mount_opts=opts) + cache_result = __salt__["mount.write_mount_cache"]( + real_name, device, mkmnt=mkmnt, fstype=fstype, mount_opts=opts + ) - if out == 'present': - ret['comment'] += '. Entry already exists in the fstab.' + if out == "present": + ret["comment"] += ". Entry already exists in the fstab." return ret - if out == 'new': - ret['changes']['persist'] = 'new' - ret['comment'] += '. Added new entry to the fstab.' + if out == "new": + ret["changes"]["persist"] = "new" + ret["comment"] += ". Added new entry to the fstab." return ret - if out == 'change': - ret['changes']['persist'] = 'update' - ret['comment'] += '. Updated the entry in the fstab.' + if out == "change": + ret["changes"]["persist"] = "update" + ret["comment"] += ". Updated the entry in the fstab." return ret - if out == 'bad config': - ret['result'] = False - ret['comment'] += '. However, the fstab was not found.' + if out == "bad config": + ret["result"] = False + ret["comment"] += ". However, the fstab was not found." return ret return ret -def swap(name, persist=True, config='/etc/fstab'): - ''' +def swap(name, persist=True, config="/etc/fstab"): + """ Activates a swap device .. code-block:: yaml @@ -722,99 +818,95 @@ def swap(name, persist=True, config='/etc/fstab'): .. note:: ``swap`` does not currently support LABEL - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} - on_ = __salt__['mount.swaps']() + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + on_ = __salt__["mount.swaps"]() - if __salt__['file.is_link'](name): - real_swap_device = __salt__['file.readlink'](name) - if not real_swap_device.startswith('/'): - real_swap_device = '/dev/{0}'.format(os.path.basename(real_swap_device)) + if __salt__["file.is_link"](name): + real_swap_device = __salt__["file.readlink"](name) + if not real_swap_device.startswith("/"): + real_swap_device = "/dev/{0}".format(os.path.basename(real_swap_device)) else: real_swap_device = name if real_swap_device in on_: - ret['comment'] = 'Swap {0} already active'.format(name) - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Swap {0} is set to be activated'.format(name) + ret["comment"] = "Swap {0} already active".format(name) + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = "Swap {0} is set to be activated".format(name) else: - __salt__['mount.swapon'](real_swap_device) + __salt__["mount.swapon"](real_swap_device) - on_ = __salt__['mount.swaps']() + on_ = __salt__["mount.swaps"]() if real_swap_device in on_: - ret['comment'] = 'Swap {0} activated'.format(name) - ret['changes'] = on_[real_swap_device] + ret["comment"] = "Swap {0} activated".format(name) + ret["changes"] = on_[real_swap_device] else: - ret['comment'] = 'Swap {0} failed to activate'.format(name) - ret['result'] = False + ret["comment"] = "Swap {0} failed to activate".format(name) + ret["result"] = False if persist: - device_key_name = 'device' - if 'AIX' in __grains__['os']: - device_key_name = 'dev' - if '/etc/fstab' == config: + device_key_name = "device" + if "AIX" in __grains__["os"]: + device_key_name = "dev" + if "/etc/fstab" == config: # Override default for AIX config = "/etc/filesystems" - fstab_data = __salt__['mount.filesystems'](config) + fstab_data = __salt__["mount.filesystems"](config) else: - fstab_data = __salt__['mount.fstab'](config) - if __opts__['test']: - if name not in fstab_data and name not in [fstab_data[item]['device'] for item in fstab_data]: - ret['result'] = None + fstab_data = __salt__["mount.fstab"](config) + if __opts__["test"]: + if name not in fstab_data and name not in [ + fstab_data[item]["device"] for item in fstab_data + ]: + ret["result"] = None if name in on_: - ret['comment'] = ('Swap {0} is set to be added to the ' - 'fstab and to be activated').format(name) + ret["comment"] = ( + "Swap {0} is set to be added to the " + "fstab and to be activated" + ).format(name) return ret - if 'none' in fstab_data: - if fstab_data['none'][device_key_name] == name and \ - fstab_data['none']['fstype'] != 'swap': + if "none" in fstab_data: + if ( + fstab_data["none"][device_key_name] == name + and fstab_data["none"]["fstype"] != "swap" + ): return ret - if 'AIX' in __grains__['os']: + if "AIX" in __grains__["os"]: out = None - ret['result'] = False - ret['comment'] += '. swap not present in /etc/filesystems on AIX.' + ret["result"] = False + ret["comment"] += ". swap not present in /etc/filesystems on AIX." return ret else: # present, new, change, bad config # Make sure the entry is in the fstab - out = __salt__['mount.set_fstab']('none', - name, - 'swap', - ['defaults'], - 0, - 0, - config) - if out == 'present': + out = __salt__["mount.set_fstab"]( + "none", name, "swap", ["defaults"], 0, 0, config + ) + if out == "present": return ret - if out == 'new': - ret['changes']['persist'] = 'new' - ret['comment'] += '. Added new entry to the fstab.' + if out == "new": + ret["changes"]["persist"] = "new" + ret["comment"] += ". Added new entry to the fstab." return ret - if out == 'change': - ret['changes']['persist'] = 'update' - ret['comment'] += '. Updated the entry in the fstab.' + if out == "change": + ret["changes"]["persist"] = "update" + ret["comment"] += ". Updated the entry in the fstab." return ret - if out == 'bad config': - ret['result'] = False - ret['comment'] += '. However, the fstab was not found.' + if out == "bad config": + ret["result"] = False + ret["comment"] += ". However, the fstab was not found." return ret return ret -def unmounted(name, - device=None, - config='/etc/fstab', - persist=False, - user=None, - **kwargs): - ''' +def unmounted( + name, device=None, config="/etc/fstab", persist=False, user=None, **kwargs +): + """ .. versionadded:: 0.17.0 Verify that a device is not mounted @@ -837,98 +929,100 @@ def unmounted(name, user The user to own the mount; this defaults to the user salt is running as on the minion - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} update_mount_cache = False if not name: - ret['result'] = False - ret['comment'] = 'Must provide name to mount.unmounted' + ret["result"] = False + ret["comment"] = "Must provide name to mount.unmounted" return ret # Get the active data - active = __salt__['mount.active'](extended=True) + active = __salt__["mount.active"](extended=True) if name not in active: # Nothing to unmount - ret['comment'] = 'Target was already unmounted' + ret["comment"] = "Target was already unmounted" if name in active: # The mount is present! Unmount it - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Mount point {0} is mounted but should not ' - 'be').format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ("Mount point {0} is mounted but should not " "be").format( + name + ) return ret if device: - out = __salt__['mount.umount'](name, device, user=user) + out = __salt__["mount.umount"](name, device, user=user) update_mount_cache = True else: - out = __salt__['mount.umount'](name, user=user) + out = __salt__["mount.umount"](name, user=user) update_mount_cache = True if isinstance(out, string_types): # Failed to umount, the state has failed! - ret['comment'] = out - ret['result'] = False + ret["comment"] = out + ret["result"] = False elif out is True: # umount worked! - ret['comment'] = 'Target was successfully unmounted' - ret['changes']['umount'] = True + ret["comment"] = "Target was successfully unmounted" + ret["changes"]["umount"] = True else: - ret['comment'] = 'Execute set to False, Target was not unmounted' - ret['result'] = True + ret["comment"] = "Execute set to False, Target was not unmounted" + ret["result"] = True if update_mount_cache: - cache_result = __salt__['mount.delete_mount_cache'](name) + cache_result = __salt__["mount.delete_mount_cache"](name) if persist: - device_key_name = 'device' + device_key_name = "device" # Override default for Mac OS - if __grains__['os'] in ['MacOS', 'Darwin'] and config == '/etc/fstab': + if __grains__["os"] in ["MacOS", "Darwin"] and config == "/etc/fstab": config = "/etc/auto_salt" - fstab_data = __salt__['mount.automaster'](config) - elif 'AIX' in __grains__['os']: - device_key_name = 'dev' - if config == '/etc/fstab': + fstab_data = __salt__["mount.automaster"](config) + elif "AIX" in __grains__["os"]: + device_key_name = "dev" + if config == "/etc/fstab": config = "/etc/filesystems" - fstab_data = __salt__['mount.filesystems'](config) + fstab_data = __salt__["mount.filesystems"](config) else: - fstab_data = __salt__['mount.fstab'](config) + fstab_data = __salt__["mount.fstab"](config) if name not in fstab_data: - ret['comment'] += '. fstab entry not found' + ret["comment"] += ". fstab entry not found" else: if device: if fstab_data[name][device_key_name] != device: - ret['comment'] += '. fstab entry for device {0} not found'.format(device) + ret["comment"] += ". fstab entry for device {0} not found".format( + device + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Mount point {0} is unmounted but needs to ' - 'be purged from {1} to be made ' - 'persistent').format(name, config) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Mount point {0} is unmounted but needs to " + "be purged from {1} to be made " + "persistent" + ).format(name, config) return ret else: - if __grains__['os'] in ['MacOS', 'Darwin']: - out = __salt__['mount.rm_automaster'](name, device, config) - elif 'AIX' in __grains__['os']: - out = __salt__['mount.rm_filesystems'](name, device, config) + if __grains__["os"] in ["MacOS", "Darwin"]: + out = __salt__["mount.rm_automaster"](name, device, config) + elif "AIX" in __grains__["os"]: + out = __salt__["mount.rm_filesystems"](name, device, config) else: - out = __salt__['mount.rm_fstab'](name, device, config) + out = __salt__["mount.rm_fstab"](name, device, config) if out is not True: - ret['result'] = False - ret['comment'] += '. Failed to persist purge' + ret["result"] = False + ret["comment"] += ". Failed to persist purge" else: - ret['comment'] += '. Removed target from fstab' - ret['changes']['persist'] = 'purged' + ret["comment"] += ". Removed target from fstab" + ret["changes"]["persist"] = "purged" return ret def mod_watch(name, user=None, **kwargs): - ''' + """ The mounted watcher, called to invoke the watch command. .. note:: @@ -940,62 +1034,74 @@ def mod_watch(name, user=None, **kwargs): name The name of the mount point - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if kwargs['sfun'] == 'mounted': - out = __salt__['mount.remount'](name, kwargs['device'], False, kwargs['fstype'], kwargs['opts'], user=user) + if kwargs["sfun"] == "mounted": + out = __salt__["mount.remount"]( + name, kwargs["device"], False, kwargs["fstype"], kwargs["opts"], user=user + ) if out: - ret['comment'] = '{0} remounted'.format(name) + ret["comment"] = "{0} remounted".format(name) else: - ret['result'] = False - ret['comment'] = '{0} failed to remount: {1}'.format(name, out) + ret["result"] = False + ret["comment"] = "{0} failed to remount: {1}".format(name, out) else: - ret['comment'] = 'Watch not supported in {0} at this time'.format(kwargs['sfun']) + ret["comment"] = "Watch not supported in {0} at this time".format( + kwargs["sfun"] + ) return ret def _convert_to(maybe_device, convert_to): - ''' + """ Convert a device name, UUID or LABEL to a device name, UUID or LABEL. Return the fs_spec required for fstab. - ''' + """ # Fast path. If we already have the information required, we can # save one blkid call - if not convert_to or \ - (convert_to == 'device' and maybe_device.startswith('/')) or \ - maybe_device.startswith('{}='.format(convert_to.upper())): + if ( + not convert_to + or (convert_to == "device" and maybe_device.startswith("/")) + or maybe_device.startswith("{}=".format(convert_to.upper())) + ): return maybe_device # Get the device information - if maybe_device.startswith('/'): - blkid = __salt__['disk.blkid'](maybe_device) + if maybe_device.startswith("/"): + blkid = __salt__["disk.blkid"](maybe_device) else: - blkid = __salt__['disk.blkid'](token=maybe_device) + blkid = __salt__["disk.blkid"](token=maybe_device) result = None if len(blkid) == 1: - if convert_to == 'device': + if convert_to == "device": result = list(blkid.keys())[0] else: key = convert_to.upper() - result = '{}={}'.format(key, list(blkid.values())[0][key]) + result = "{}={}".format(key, list(blkid.values())[0][key]) return result -def fstab_present(name, fs_file, fs_vfstype, fs_mntops='defaults', - fs_freq=0, fs_passno=0, mount_by=None, - config='/etc/fstab', mount=True, match_on='auto', - not_change=False): - '''Makes sure that a fstab mount point is pressent. +def fstab_present( + name, + fs_file, + fs_vfstype, + fs_mntops="defaults", + fs_freq=0, + fs_passno=0, + mount_by=None, + config="/etc/fstab", + mount=True, + match_on="auto", + not_change=False, +): + """Makes sure that a fstab mount point is pressent. name The name of block device. Can be any valid fs_spec value. @@ -1047,134 +1153,146 @@ def fstab_present(name, fs_file, fs_vfstype, fs_mntops='defaults', parameter is set to ``True`` and the line is found, the original content will be preserved. - ''' + """ ret = { - 'name': name, - 'result': False, - 'changes': {}, - 'comment': [], + "name": name, + "result": False, + "changes": {}, + "comment": [], } # Adjust fs_mntops based on the OS - if fs_mntops == 'defaults': - if __grains__['os'] in ['MacOS', 'Darwin']: - fs_mntops = 'noowners' - elif __grains__['os'] == 'AIX': - fs_mntops = '' + if fs_mntops == "defaults": + if __grains__["os"] in ["MacOS", "Darwin"]: + fs_mntops = "noowners" + elif __grains__["os"] == "AIX": + fs_mntops = "" # Adjust the config file based on the OS - if config == '/etc/fstab': - if __grains__['os'] in ['MacOS', 'Darwin']: - config = '/etc/auto_salt' - elif __grains__['os'] == 'AIX': - config = '/etc/filesystems' + if config == "/etc/fstab": + if __grains__["os"] in ["MacOS", "Darwin"]: + config = "/etc/auto_salt" + elif __grains__["os"] == "AIX": + config = "/etc/filesystems" - if not fs_file == '/': - fs_file = fs_file.rstrip('/') + if not fs_file == "/": + fs_file = fs_file.rstrip("/") fs_spec = _convert_to(name, mount_by) # Validate that the device is valid after the conversion if not fs_spec: - msg = 'Device {} cannot be converted to {}' - ret['comment'].append(msg.format(name, mount_by)) + msg = "Device {} cannot be converted to {}" + ret["comment"].append(msg.format(name, mount_by)) return ret - if __opts__['test']: - if __grains__['os'] in ['MacOS', 'Darwin']: - out = __salt__['mount.set_automaster'](name=fs_file, - device=fs_spec, - fstype=fs_vfstype, - opts=fs_mntops, - config=config, - test=True, - not_change=not_change) - elif __grains__['os'] == 'AIX': - out = __salt__['mount.set_filesystems'](name=fs_file, - device=fs_spec, - fstype=fs_vfstype, - opts=fs_mntops, - mount=mount, - config=config, - test=True, - match_on=match_on, - not_change=not_change) + if __opts__["test"]: + if __grains__["os"] in ["MacOS", "Darwin"]: + out = __salt__["mount.set_automaster"]( + name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + config=config, + test=True, + not_change=not_change, + ) + elif __grains__["os"] == "AIX": + out = __salt__["mount.set_filesystems"]( + name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + mount=mount, + config=config, + test=True, + match_on=match_on, + not_change=not_change, + ) else: - out = __salt__['mount.set_fstab'](name=fs_file, - device=fs_spec, - fstype=fs_vfstype, - opts=fs_mntops, - dump=fs_freq, - pass_num=fs_passno, - config=config, - test=True, - match_on=match_on, - not_change=not_change) - ret['result'] = None - if out == 'present': - msg = '{} entry is already in {}.' - ret['comment'].append(msg.format(fs_file, config)) - elif out == 'new': - msg = '{} entry will be written in {}.' - ret['comment'].append(msg.format(fs_file, config)) - elif out == 'change': - msg = '{} entry will be updated in {}.' - ret['comment'].append(msg.format(fs_file, config)) + out = __salt__["mount.set_fstab"]( + name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + dump=fs_freq, + pass_num=fs_passno, + config=config, + test=True, + match_on=match_on, + not_change=not_change, + ) + ret["result"] = None + if out == "present": + msg = "{} entry is already in {}." + ret["comment"].append(msg.format(fs_file, config)) + elif out == "new": + msg = "{} entry will be written in {}." + ret["comment"].append(msg.format(fs_file, config)) + elif out == "change": + msg = "{} entry will be updated in {}." + ret["comment"].append(msg.format(fs_file, config)) else: - ret['result'] = False - msg = '{} entry cannot be created in {}: {}.' - ret['comment'].append(msg.format(fs_file, config, out)) + ret["result"] = False + msg = "{} entry cannot be created in {}: {}." + ret["comment"].append(msg.format(fs_file, config, out)) return ret - if __grains__['os'] in ['MacOS', 'Darwin']: - out = __salt__['mount.set_automaster'](name=fs_file, - device=fs_spec, - fstype=fs_vfstype, - opts=fs_mntops, - config=config, - not_change=not_change) - elif __grains__['os'] == 'AIX': - out = __salt__['mount.set_filesystems'](name=fs_file, - device=fs_spec, - fstype=fs_vfstype, - opts=fs_mntops, - mount=mount, - config=config, - match_on=match_on, - not_change=not_change) + if __grains__["os"] in ["MacOS", "Darwin"]: + out = __salt__["mount.set_automaster"]( + name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + config=config, + not_change=not_change, + ) + elif __grains__["os"] == "AIX": + out = __salt__["mount.set_filesystems"]( + name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + mount=mount, + config=config, + match_on=match_on, + not_change=not_change, + ) else: - out = __salt__['mount.set_fstab'](name=fs_file, - device=fs_spec, - fstype=fs_vfstype, - opts=fs_mntops, - dump=fs_freq, - pass_num=fs_passno, - config=config, - match_on=match_on, - not_change=not_change) + out = __salt__["mount.set_fstab"]( + name=fs_file, + device=fs_spec, + fstype=fs_vfstype, + opts=fs_mntops, + dump=fs_freq, + pass_num=fs_passno, + config=config, + match_on=match_on, + not_change=not_change, + ) - ret['result'] = True - if out == 'present': - msg = '{} entry was already in {}.' - ret['comment'].append(msg.format(fs_file, config)) - elif out == 'new': - ret['changes']['persist'] = out - msg = '{} entry added in {}.' - ret['comment'].append(msg.format(fs_file, config)) - elif out == 'change': - ret['changes']['persist'] = out - msg = '{} entry updated in {}.' - ret['comment'].append(msg.format(fs_file, config)) + ret["result"] = True + if out == "present": + msg = "{} entry was already in {}." + ret["comment"].append(msg.format(fs_file, config)) + elif out == "new": + ret["changes"]["persist"] = out + msg = "{} entry added in {}." + ret["comment"].append(msg.format(fs_file, config)) + elif out == "change": + ret["changes"]["persist"] = out + msg = "{} entry updated in {}." + ret["comment"].append(msg.format(fs_file, config)) else: - ret['result'] = False - msg = '{} entry cannot be changed in {}: {}.' - ret['comment'].append(msg.format(fs_file, config, out)) + ret["result"] = False + msg = "{} entry cannot be changed in {}: {}." + ret["comment"].append(msg.format(fs_file, config, out)) return ret -def fstab_absent(name, fs_file, mount_by=None, config='/etc/fstab'): - ''' +def fstab_absent(name, fs_file, mount_by=None, config="/etc/fstab"): + """ Makes sure that a fstab mount point is absent. name @@ -1195,69 +1313,69 @@ def fstab_absent(name, fs_file, mount_by=None, config='/etc/fstab'): config Place where the fstab file lives - ''' + """ ret = { - 'name': name, - 'result': False, - 'changes': {}, - 'comment': [], + "name": name, + "result": False, + "changes": {}, + "comment": [], } # Adjust the config file based on the OS - if config == '/etc/fstab': - if __grains__['os'] in ['MacOS', 'Darwin']: - config = '/etc/auto_salt' - elif __grains__['os'] == 'AIX': - config = '/etc/filesystems' + if config == "/etc/fstab": + if __grains__["os"] in ["MacOS", "Darwin"]: + config = "/etc/auto_salt" + elif __grains__["os"] == "AIX": + config = "/etc/filesystems" - if not fs_file == '/': - fs_file = fs_file.rstrip('/') + if not fs_file == "/": + fs_file = fs_file.rstrip("/") fs_spec = _convert_to(name, mount_by) - if __grains__['os'] in ['MacOS', 'Darwin']: - fstab_data = __salt__['mount.automaster'](config) - elif __grains__['os'] == 'AIX': - fstab_data = __salt__['mount.filesystems'](config) + if __grains__["os"] in ["MacOS", "Darwin"]: + fstab_data = __salt__["mount.automaster"](config) + elif __grains__["os"] == "AIX": + fstab_data = __salt__["mount.filesystems"](config) else: - fstab_data = __salt__['mount.fstab'](config) + fstab_data = __salt__["mount.fstab"](config) - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if fs_file not in fstab_data: - msg = '{} entry is already missing in {}.' - ret['comment'].append(msg.format(fs_file, config)) + msg = "{} entry is already missing in {}." + ret["comment"].append(msg.format(fs_file, config)) else: - msg = '{} entry will be removed from {}.' - ret['comment'].append(msg.format(fs_file, config)) + msg = "{} entry will be removed from {}." + ret["comment"].append(msg.format(fs_file, config)) return ret if fs_file in fstab_data: - if __grains__['os'] in ['MacOS', 'Darwin']: - out = __salt__['mount.rm_automaster'](name=fs_file, - device=fs_spec, - config=config) - elif __grains__['os'] == 'AIX': - out = __salt__['mount.rm_filesystems'](name=fs_file, - device=fs_spec, - config=config) + if __grains__["os"] in ["MacOS", "Darwin"]: + out = __salt__["mount.rm_automaster"]( + name=fs_file, device=fs_spec, config=config + ) + elif __grains__["os"] == "AIX": + out = __salt__["mount.rm_filesystems"]( + name=fs_file, device=fs_spec, config=config + ) else: - out = __salt__['mount.rm_fstab'](name=fs_file, - device=fs_spec, - config=config) + out = __salt__["mount.rm_fstab"]( + name=fs_file, device=fs_spec, config=config + ) if out is not True: - ret['result'] = False - msg = '{} entry failed when removing from {}.' - ret['comment'].append(msg.format(fs_file, config)) + ret["result"] = False + msg = "{} entry failed when removing from {}." + ret["comment"].append(msg.format(fs_file, config)) else: - ret['result'] = True - ret['changes']['persist'] = 'removed' - msg = '{} entry removed from {}.' - ret['comment'].append(msg.format(fs_file, config)) + ret["result"] = True + ret["changes"]["persist"] = "removed" + msg = "{} entry removed from {}." + ret["comment"].append(msg.format(fs_file, config)) else: - ret['result'] = True - msg = '{} entry is already missing in {}.' - ret['comment'].append(msg.format(fs_file, config)) + ret["result"] = True + msg = "{} entry is already missing in {}." + ret["comment"].append(msg.format(fs_file, config)) return ret diff --git a/salt/states/mssql_database.py b/salt/states/mssql_database.py index 5a54a93ae3b..b10f749d593 100644 --- a/salt/states/mssql_database.py +++ b/salt/states/mssql_database.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Microsoft SQLServer Databases =========================================== @@ -10,31 +10,37 @@ and manage SQL Server Databases yolo: mssql_database.present -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import collections def __virtual__(): - ''' + """ Only load if the mssql module is present - ''' - return 'mssql.version' in __salt__ + """ + if "mssql.version" in __salt__: + return True + return (False, "mssql module could not be loaded") def _normalize_options(options): if type(options) in [dict, collections.OrderedDict]: - return ['{0}={1}'.format(k, v) for k, v in options.items()] + return ["{0}={1}".format(k, v) for k, v in options.items()] if type(options) is list and (not options or type(options[0]) is str): return options # Invalid options - if type(options) is not list or type(options[0]) not in [dict, collections.OrderedDict]: + if type(options) is not list or type(options[0]) not in [ + dict, + collections.OrderedDict, + ]: return [] return [o for d in options for o in _normalize_options(d)] -def present(name, containment='NONE', options=None, **kwargs): - ''' +def present(name, containment="NONE", options=None, **kwargs): + """ Ensure that the named database is present with the specified options name @@ -43,54 +49,61 @@ def present(name, containment='NONE', options=None, **kwargs): Defaults to NONE options Can be a list of strings, a dictionary, or a list of dictionaries - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if __salt__['mssql.db_exists'](name, **kwargs): - ret['comment'] = 'Database {0} is already present (Not going to try to set its options)'.format(name) + if __salt__["mssql.db_exists"](name, **kwargs): + ret[ + "comment" + ] = "Database {0} is already present (Not going to try to set its options)".format( + name + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Database {0} is set to be added'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Database {0} is set to be added".format(name) return ret - db_created = __salt__['mssql.db_create'](name, containment=containment, new_database_options=_normalize_options(options), **kwargs) - if db_created is not True: # Non-empty strings are also evaluated to True, so we cannot use if not db_created: - ret['result'] = False - ret['comment'] += 'Database {0} failed to be created: {1}'.format(name, db_created) + db_created = __salt__["mssql.db_create"]( + name, + containment=containment, + new_database_options=_normalize_options(options), + **kwargs + ) + if ( + db_created is not True + ): # Non-empty strings are also evaluated to True, so we cannot use if not db_created: + ret["result"] = False + ret["comment"] += "Database {0} failed to be created: {1}".format( + name, db_created + ) return ret - ret['comment'] += 'Database {0} has been added'.format(name) - ret['changes'][name] = 'Present' + ret["comment"] += "Database {0} has been added".format(name) + ret["changes"][name] = "Present" return ret def absent(name, **kwargs): - ''' + """ Ensure that the named database is absent name The name of the database to remove - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if not __salt__['mssql.db_exists'](name): - ret['comment'] = 'Database {0} is not present'.format(name) + if not __salt__["mssql.db_exists"](name): + ret["comment"] = "Database {0} is not present".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Database {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Database {0} is set to be removed".format(name) return ret - if __salt__['mssql.db_remove'](name, **kwargs): - ret['comment'] = 'Database {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["mssql.db_remove"](name, **kwargs): + ret["comment"] = "Database {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret # else: - ret['result'] = False - ret['comment'] = 'Database {0} failed to be removed'.format(name) + ret["result"] = False + ret["comment"] = "Database {0} failed to be removed".format(name) return ret diff --git a/salt/states/mssql_login.py b/salt/states/mssql_login.py index 44a67e2b900..1572d163a0c 100644 --- a/salt/states/mssql_login.py +++ b/salt/states/mssql_login.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Microsoft SQLServer Logins ======================================== @@ -11,31 +11,39 @@ and manage SQL Server Logins frank: mssql_login.present - domain: mydomain -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import collections def __virtual__(): - ''' + """ Only load if the mssql module is present - ''' - return 'mssql.version' in __salt__ + """ + if "mssql.version" in __salt__: + return True + return (False, "mssql module could not be loaded") def _normalize_options(options): if type(options) in [dict, collections.OrderedDict]: - return ['{0}={1}'.format(k, v) for k, v in options.items()] + return ["{0}={1}".format(k, v) for k, v in options.items()] if type(options) is list and (not options or type(options[0]) is str): return options # Invalid options - if type(options) is not list or type(options[0]) not in [dict, collections.OrderedDict]: + if type(options) is not list or type(options[0]) not in [ + dict, + collections.OrderedDict, + ]: return [] return [o for d in options for o in _normalize_options(d)] -def present(name, password=None, domain=None, server_roles=None, options=None, **kwargs): - ''' +def present( + name, password=None, domain=None, server_roles=None, options=None, **kwargs +): + """ Checks existance of the named login. If not present, creates the login with the specified roles and options. @@ -53,64 +61,64 @@ def present(name, password=None, domain=None, server_roles=None, options=None, * Add this login to all the server roles in the list options Can be a list of strings, a dictionary, or a list of dictionaries - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if bool(password) == bool(domain): - ret['result'] = False - ret['comment'] = 'One and only one of password and domain should be specifies' + ret["result"] = False + ret["comment"] = "One and only one of password and domain should be specifies" return ret - if __salt__['mssql.login_exists'](name, domain=domain, **kwargs): - ret['comment'] = 'Login {0} is already present (Not going to try to set its password)'.format(name) + if __salt__["mssql.login_exists"](name, domain=domain, **kwargs): + ret[ + "comment" + ] = "Login {0} is already present (Not going to try to set its password)".format( + name + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Login {0} is set to be added'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Login {0} is set to be added".format(name) return ret - login_created = __salt__['mssql.login_create'](name, - new_login_password=password, - new_login_domain=domain, - new_login_roles=server_roles, - new_login_options=_normalize_options(options), - **kwargs) + login_created = __salt__["mssql.login_create"]( + name, + new_login_password=password, + new_login_domain=domain, + new_login_roles=server_roles, + new_login_options=_normalize_options(options), + **kwargs + ) # Non-empty strings are also evaluated to True, so we cannot use if not login_created: if login_created is not True: - ret['result'] = False - ret['comment'] = 'Login {0} failed to be added: {1}'.format(name, login_created) + ret["result"] = False + ret["comment"] = "Login {0} failed to be added: {1}".format(name, login_created) return ret - ret['comment'] = 'Login {0} has been added. '.format(name) - ret['changes'][name] = 'Present' + ret["comment"] = "Login {0} has been added. ".format(name) + ret["changes"][name] = "Present" return ret def absent(name, **kwargs): - ''' + """ Ensure that the named login is absent name The name of the login to remove - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if not __salt__['mssql.login_exists'](name): - ret['comment'] = 'Login {0} is not present'.format(name) + if not __salt__["mssql.login_exists"](name): + ret["comment"] = "Login {0} is not present".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Login {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Login {0} is set to be removed".format(name) return ret - if __salt__['mssql.login_remove'](name, **kwargs): - ret['comment'] = 'Login {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["mssql.login_remove"](name, **kwargs): + ret["comment"] = "Login {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret # else: - ret['result'] = False - ret['comment'] = 'Login {0} failed to be removed'.format(name) + ret["result"] = False + ret["comment"] = "Login {0} failed to be removed".format(name) return ret diff --git a/salt/states/mssql_role.py b/salt/states/mssql_role.py index c0de770715a..304bc35ef78 100644 --- a/salt/states/mssql_role.py +++ b/salt/states/mssql_role.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Microsoft SQLServer Databases =========================================== @@ -10,19 +10,21 @@ and manage SQL Server Roles yolo: mssql_role.present -''' +""" from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the mssql module is present - ''' - return 'mssql.version' in __salt__ + """ + if "mssql.version" in __salt__: + return True + return (False, "mssql module could not be loaded") def present(name, owner=None, grants=None, **kwargs): - ''' + """ Ensure that the named database is present with the specified options name @@ -31,54 +33,58 @@ def present(name, owner=None, grants=None, **kwargs): Adds owner using AUTHORIZATION option Grants Can only be a list of strings - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if __salt__['mssql.role_exists'](name, **kwargs): - ret['comment'] = 'Role {0} is already present (Not going to try to set its grants)'.format(name) + if __salt__["mssql.role_exists"](name, **kwargs): + ret[ + "comment" + ] = "Role {0} is already present (Not going to try to set its grants)".format( + name + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Role {0} is set to be added'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Role {0} is set to be added".format(name) return ret - role_created = __salt__['mssql.role_create'](name, owner=owner, grants=grants, **kwargs) - if role_created is not True: # Non-empty strings are also evaluated to True, so we cannot use if not role_created: - ret['result'] = False - ret['comment'] += 'Role {0} failed to be created: {1}'.format(name, role_created) + role_created = __salt__["mssql.role_create"]( + name, owner=owner, grants=grants, **kwargs + ) + if ( + role_created is not True + ): # Non-empty strings are also evaluated to True, so we cannot use if not role_created: + ret["result"] = False + ret["comment"] += "Role {0} failed to be created: {1}".format( + name, role_created + ) return ret - ret['comment'] += 'Role {0} has been added'.format(name) - ret['changes'][name] = 'Present' + ret["comment"] += "Role {0} has been added".format(name) + ret["changes"][name] = "Present" return ret def absent(name, **kwargs): - ''' + """ Ensure that the named database is absent name The name of the database to remove - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if not __salt__['mssql.role_exists'](name): - ret['comment'] = 'Role {0} is not present'.format(name) + if not __salt__["mssql.role_exists"](name): + ret["comment"] = "Role {0} is not present".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Role {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Role {0} is set to be removed".format(name) return ret - if __salt__['mssql.role_remove'](name, **kwargs): - ret['comment'] = 'Role {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["mssql.role_remove"](name, **kwargs): + ret["comment"] = "Role {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret # else: - ret['result'] = False - ret['comment'] = 'Role {0} failed to be removed'.format(name) + ret["result"] = False + ret["comment"] = "Role {0} failed to be removed".format(name) return ret diff --git a/salt/states/mssql_user.py b/salt/states/mssql_user.py index e81f7338281..ba758e5a04b 100644 --- a/salt/states/mssql_user.py +++ b/salt/states/mssql_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Microsoft SQLServer Users ======================================= @@ -11,31 +11,39 @@ and manage SQL Server Users frank: mssql_user.present: - database: yolo -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import collections def __virtual__(): - ''' + """ Only load if the mssql module is present - ''' - return 'mssql.version' in __salt__ + """ + if "mssql.version" in __salt__: + return True + return (False, "mssql module could not be loaded") def _normalize_options(options): if type(options) in [dict, collections.OrderedDict]: - return ['{0}={1}'.format(k, v) for k, v in options.items()] + return ["{0}={1}".format(k, v) for k, v in options.items()] if type(options) is list and (not options or type(options[0]) is str): return options # Invalid options - if type(options) is not list or type(options[0]) not in [dict, collections.OrderedDict]: + if type(options) is not list or type(options[0]) not in [ + dict, + collections.OrderedDict, + ]: return [] return [o for d in options for o in _normalize_options(d)] -def present(name, login=None, domain=None, database=None, roles=None, options=None, **kwargs): - ''' +def present( + name, login=None, domain=None, database=None, roles=None, options=None, **kwargs +): + """ Checks existance of the named user. If not present, creates the user with the specified roles and options. @@ -52,63 +60,66 @@ def present(name, login=None, domain=None, database=None, roles=None, options=No Add this user to all the roles in the list options Can be a list of strings, a dictionary, or a list of dictionaries - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if domain and not login: - ret['result'] = False - ret['comment'] = 'domain cannot be set without login' + ret["result"] = False + ret["comment"] = "domain cannot be set without login" return ret - if __salt__['mssql.user_exists'](name, domain=domain, database=database, **kwargs): - ret['comment'] = 'User {0} is already present (Not going to try to set its roles or options)'.format(name) + if __salt__["mssql.user_exists"](name, domain=domain, database=database, **kwargs): + ret[ + "comment" + ] = "User {0} is already present (Not going to try to set its roles or options)".format( + name + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0} is set to be added'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User {0} is set to be added".format(name) return ret - user_created = __salt__['mssql.user_create'](name, login=login, - domain=domain, - database=database, - roles=roles, - options=_normalize_options(options), - **kwargs) - if user_created is not True: # Non-empty strings are also evaluated to True, so we cannot use if not user_created: - ret['result'] = False - ret['comment'] += 'User {0} failed to be added: {1}'.format(name, user_created) + user_created = __salt__["mssql.user_create"]( + name, + login=login, + domain=domain, + database=database, + roles=roles, + options=_normalize_options(options), + **kwargs + ) + if ( + user_created is not True + ): # Non-empty strings are also evaluated to True, so we cannot use if not user_created: + ret["result"] = False + ret["comment"] += "User {0} failed to be added: {1}".format(name, user_created) return ret - ret['comment'] += 'User {0} has been added'.format(name) - ret['changes'][name] = 'Present' + ret["comment"] += "User {0} has been added".format(name) + ret["changes"][name] = "Present" return ret def absent(name, **kwargs): - ''' + """ Ensure that the named user is absent name The username of the user to remove - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if not __salt__['mssql.user_exists'](name): - ret['comment'] = 'User {0} is not present'.format(name) + if not __salt__["mssql.user_exists"](name): + ret["comment"] = "User {0} is not present".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User {0} is set to be removed".format(name) return ret - if __salt__['mssql.user_remove'](name, **kwargs): - ret['comment'] = 'User {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["mssql.user_remove"](name, **kwargs): + ret["comment"] = "User {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret # else: - ret['result'] = False - ret['comment'] = 'User {0} failed to be removed'.format(name) + ret["result"] = False + ret["comment"] = "User {0} failed to be removed".format(name) return ret diff --git a/salt/states/msteams.py b/salt/states/msteams.py index efd356a40a8..05736f021e8 100644 --- a/salt/states/msteams.py +++ b/salt/states/msteams.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Send a message card to Microsoft Teams ====================================== @@ -20,7 +20,7 @@ The hook_url can be specified in the master or minion configuration like below: msteams: hook_url: https://outlook.office.com/webhook/837 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -30,18 +30,16 @@ from salt.exceptions import SaltInvocationError def __virtual__(): - ''' + """ Only load if the msteams module is available in __salt__ - ''' - return 'msteams' if 'msteams.post_card' in __salt__ else False + """ + if "msteams.post_card" in __salt__: + return "msteams" + return (False, "msteams module could not be loaded") -def post_card(name, - message, - hook_url=None, - title=None, - theme_color=None): - ''' +def post_card(name, message, hook_url=None, title=None, theme_color=None): + """ Send a message to a Microsft Teams channel .. code-block:: yaml @@ -65,35 +63,33 @@ def post_card(name, The title for the card posted to the channel theme_color A hex code for the desired highlight color - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - ret['comment'] = 'The following message is to be sent to Teams: {0}'.format(message) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The following message is to be sent to Teams: {0}".format( + message + ) + ret["result"] = None return ret if not message: - ret['comment'] = 'Teams message is missing: {0}'.format(message) + ret["comment"] = "Teams message is missing: {0}".format(message) return ret try: - result = __salt__['msteams.post_card']( - message=message, - hook_url=hook_url, - title=title, - theme_color=theme_color, + result = __salt__["msteams.post_card"]( + message=message, hook_url=hook_url, title=title, theme_color=theme_color, ) except SaltInvocationError as sie: - ret['comment'] = 'Failed to send message ({0}): {1}'.format(sie, name) + ret["comment"] = "Failed to send message ({0}): {1}".format(sie, name) else: if isinstance(result, bool) and result: - ret['result'] = True - ret['comment'] = 'Sent message: {0}'.format(name) + ret["result"] = True + ret["comment"] = "Sent message: {0}".format(name) else: - ret['comment'] = 'Failed to send message ({0}): {1}'.format(result['message'], name) + ret["comment"] = "Failed to send message ({0}): {1}".format( + result["message"], name + ) return ret diff --git a/salt/states/mysql_database.py b/salt/states/mysql_database.py index ff64f2a98e0..52c1b491781 100644 --- a/salt/states/mysql_database.py +++ b/salt/states/mysql_database.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of MySQL databases (schemas) ======================================= @@ -13,136 +13,154 @@ Databases can be set as either absent or present. frank: mysql_database.present -''' +""" from __future__ import absolute_import, print_function, unicode_literals -import sys import logging - +import sys log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the mysql module is available in __salt__ - ''' - return 'mysql.db_exists' in __salt__ + """ + if "mysql.db_exists" in __salt__: + return True + return (False, "mysql module could not be loaded") def _get_mysql_error(): - ''' + """ Look in module context for a MySQL error. Eventually we should make a less ugly way of doing this. - ''' - return sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('mysql.error', None) + """ + return sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "mysql.error", None + ) def present(name, character_set=None, collate=None, **connection_args): - ''' + """ Ensure that the named database is present with the specified properties name The name of the database to manage - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Database {0} is already present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Database {0} is already present".format(name), + } # check if database exists - existing = __salt__['mysql.db_get'](name, **connection_args) + existing = __salt__["mysql.db_get"](name, **connection_args) if existing: alter = False - if character_set and character_set != existing.get('character_set'): - log.debug('character set differes from %s : %s', - character_set, existing.get('character_set')) + if character_set and character_set != existing.get("character_set"): + log.debug( + "character set differes from %s : %s", + character_set, + existing.get("character_set"), + ) alter = True - if collate and collate != existing.get('collate'): - log.debug('collate set differs from %s : %s', - collate, existing.get('collate')) + if collate and collate != existing.get("collate"): + log.debug( + "collate set differs from %s : %s", collate, existing.get("collate") + ) alter = True if alter: - __salt__['mysql.alter_db']( - name, character_set=character_set, collate=collate, - **connection_args) - current = __salt__['mysql.db_get'](name, **connection_args) - if existing.get('collate', None) != current.get('collate', None): - ret['changes'].update({'collate': { - 'before': existing.get('collate', None), - 'now': current.get('collate', None)}}) - if existing.get('character_set', None) != current.get('character_set', None): - ret['changes'].update({'character_set': { - 'before': existing.get('character_set', None), - 'now': current.get('character_set', None)}}) + __salt__["mysql.alter_db"]( + name, character_set=character_set, collate=collate, **connection_args + ) + current = __salt__["mysql.db_get"](name, **connection_args) + if existing.get("collate", None) != current.get("collate", None): + ret["changes"].update( + { + "collate": { + "before": existing.get("collate", None), + "now": current.get("collate", None), + } + } + ) + if existing.get("character_set", None) != current.get("character_set", None): + ret["changes"].update( + { + "character_set": { + "before": existing.get("character_set", None), + "now": current.get("character_set", None), + } + } + ) return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Database {0} is not present and needs to be created' - ).format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ("Database {0} is not present and needs to be created").format( + name + ) return ret # The database is not present, make it! - if __salt__['mysql.db_create']( - name, character_set=character_set, collate=collate, - **connection_args): - ret['comment'] = 'The database {0} has been created'.format(name) - ret['changes'][name] = 'Present' + if __salt__["mysql.db_create"]( + name, character_set=character_set, collate=collate, **connection_args + ): + ret["comment"] = "The database {0} has been created".format(name) + ret["changes"][name] = "Present" else: - ret['comment'] = 'Failed to create database {0}'.format(name) + ret["comment"] = "Failed to create database {0}".format(name) err = _get_mysql_error() if err is not None: - ret['comment'] += ' ({0})'.format(err) - ret['result'] = False + ret["comment"] += " ({0})".format(err) + ret["result"] = False return ret def absent(name, **connection_args): - ''' + """ Ensure that the named database is absent name The name of the database to remove - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - #check if db exists and remove it - if __salt__['mysql.db_exists'](name, **connection_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = \ - 'Database {0} is present and needs to be removed'.format(name) + # check if db exists and remove it + if __salt__["mysql.db_exists"](name, **connection_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Database {0} is present and needs to be removed".format( + name + ) return ret - if __salt__['mysql.db_remove'](name, **connection_args): - ret['comment'] = 'Database {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["mysql.db_remove"](name, **connection_args): + ret["comment"] = "Database {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = 'Unable to remove database {0} ' \ - '({1})'.format(name, err) - ret['result'] = False + ret["comment"] = "Unable to remove database {0} " "({1})".format( + name, err + ) + ret["result"] = False return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret # fallback - ret['comment'] = ('Database {0} is not present, so it cannot be removed' - ).format(name) + ret["comment"] = ("Database {0} is not present, so it cannot be removed").format( + name + ) return ret diff --git a/salt/states/mysql_grants.py b/salt/states/mysql_grants.py index d6023bbf865..0b1f2316e48 100644 --- a/salt/states/mysql_grants.py +++ b/salt/states/mysql_grants.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of MySQL grants (user permissions) ============================================= @@ -44,40 +44,44 @@ instructions. - grant: select - database: somedb.sometable - user: joe -''' +""" from __future__ import absolute_import, print_function, unicode_literals import sys def __virtual__(): - ''' + """ Only load if the mysql module is available - ''' - return 'mysql.grant_exists' in __salt__ + """ + if "mysql.grant_exists" in __salt__: + return True + return (False, "mysql module could not be loaded") def _get_mysql_error(): - ''' + """ Look in module context for a MySQL error. Eventually we should make a less ugly way of doing this. - ''' - return sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('mysql.error', None) + """ + return sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "mysql.error", None + ) -def present(name, - grant=None, - database=None, - user=None, - host='localhost', - grant_option=False, - escape=True, - revoke_first=False, - ssl_option=False, - **connection_args): - ''' +def present( + name, + grant=None, + database=None, + user=None, + host="localhost", + grant_option=False, + escape=True, + revoke_first=False, + ssl_option=False, + **connection_args +): + """ Ensure that the grant is present with the specified properties name @@ -138,80 +142,84 @@ def present(name, options. Default is ``False`` (no ssl options will be used) - ''' - comment = 'Grant {0} on {1} to {2}@{3} is already present' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': comment.format(grant, database, user, host) - } + """ + comment = "Grant {0} on {1} to {2}@{3} is already present" + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": comment.format(grant, database, user, host), + } # check if grant exists - if __salt__['mysql.grant_exists']( + if __salt__["mysql.grant_exists"]( grant, database, user, host, grant_option, escape, **connection_args ): return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret - if revoke_first and not __opts__['test']: + if revoke_first and not __opts__["test"]: # for each grant, break into tokens and see if its on the same # user/db/table as ours. (there is probably only one) - user_grants = __salt__['mysql.user_grants'](user, host, **connection_args) + user_grants = __salt__["mysql.user_grants"](user, host, **connection_args) if not user_grants: user_grants = [] for user_grant in user_grants: - token_grants = __salt__['mysql.tokenize_grant'](user_grant) - db_part = database.rpartition('.') + token_grants = __salt__["mysql.tokenize_grant"](user_grant) + db_part = database.rpartition(".") my_db = db_part[0] my_table = db_part[2] - my_db = __salt__['mysql.quote_identifier'](my_db, (my_table is '*')) - my_table = __salt__['mysql.quote_identifier'](my_table) + my_db = __salt__["mysql.quote_identifier"](my_db, (my_table is "*")) + my_table = __salt__["mysql.quote_identifier"](my_table) # Removing per table grants in case of database level grant !!! - if token_grants['database'] == my_db: - grant_to_revoke = ','.join(token_grants['grant']).rstrip(',') - __salt__['mysql.grant_revoke']( + if token_grants["database"] == my_db: + grant_to_revoke = ",".join(token_grants["grant"]).rstrip(",") + __salt__["mysql.grant_revoke"]( grant=grant_to_revoke, database=database, user=user, host=host, grant_option=grant_option, escape=escape, - **connection_args) + **connection_args + ) # The grant is not present, make it! - if __opts__['test']: + if __opts__["test"]: # there is probably better things to make in test mode - ret['result'] = None - ret['comment'] = ('MySQL grant {0} is set to be created').format(name) + ret["result"] = None + ret["comment"] = ("MySQL grant {0} is set to be created").format(name) return ret - if __salt__['mysql.grant_add']( + if __salt__["mysql.grant_add"]( grant, database, user, host, grant_option, escape, ssl_option, **connection_args ): - ret['comment'] = 'Grant {0} on {1} to {2}@{3} has been added' - ret['comment'] = ret['comment'].format(grant, database, user, host) - ret['changes'][name] = 'Present' + ret["comment"] = "Grant {0} on {1} to {2}@{3} has been added" + ret["comment"] = ret["comment"].format(grant, database, user, host) + ret["changes"][name] = "Present" else: - ret['comment'] = 'Failed to execute: "GRANT {0} ON {1} TO {2}@{3}"' - ret['comment'] = ret['comment'].format(grant, database, user, host) + ret["comment"] = 'Failed to execute: "GRANT {0} ON {1} TO {2}@{3}"' + ret["comment"] = ret["comment"].format(grant, database, user, host) err = _get_mysql_error() if err is not None: - ret['comment'] += ' ({0})'.format(err) - ret['result'] = False + ret["comment"] += " ({0})".format(err) + ret["result"] = False return ret -def absent(name, - grant=None, - database=None, - user=None, - host='localhost', - grant_option=False, - escape=True, - **connection_args): - ''' +def absent( + name, + grant=None, + database=None, + user=None, + host="localhost", + grant_option=False, + escape=True, + **connection_args +): + """ Ensure that the grant is absent name @@ -228,60 +236,47 @@ def absent(name, host The network/host that the grant should apply to - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # Check if grant exists, and if so, remove it - if __salt__['mysql.grant_exists']( - grant, - database, - user, host, - grant_option, - escape, - **connection_args): + if __salt__["mysql.grant_exists"]( + grant, database, user, host, grant_option, escape, **connection_args + ): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'MySQL grant {0} is set to be ' \ - 'revoked'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "MySQL grant {0} is set to be " "revoked".format(name) return ret - if __salt__['mysql.grant_revoke']( - grant, - database, - user, - host, - grant_option, - **connection_args): - ret['comment'] = 'Grant {0} on {1} for {2}@{3} has been ' \ - 'revoked'.format(grant, database, user, host) - ret['changes'][name] = 'Absent' + if __salt__["mysql.grant_revoke"]( + grant, database, user, host, grant_option, **connection_args + ): + ret["comment"] = "Grant {0} on {1} for {2}@{3} has been " "revoked".format( + grant, database, user, host + ) + ret["changes"][name] = "Absent" return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = 'Unable to revoke grant {0} on {1} for ' \ - '{2}@{3} ({4})'.format(grant, database, - user, host, err) - ret['result'] = False + ret["comment"] = ( + "Unable to revoke grant {0} on {1} for " + "{2}@{3} ({4})".format(grant, database, user, host, err) + ) + ret["result"] = False return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = 'Unable to determine if grant {0} on {1} for ' \ - '{2}@{3} exists ({4})'.format(grant, database, - user, host, err) - ret['result'] = False + ret["comment"] = ( + "Unable to determine if grant {0} on {1} for " + "{2}@{3} exists ({4})".format(grant, database, user, host, err) + ) + ret["result"] = False return ret # fallback - ret['comment'] = ('Grant {0} on {1} to {2}@{3} is not present, so it' - ' cannot be revoked').format( - grant, - database, - user, - host - ) + ret["comment"] = ( + "Grant {0} on {1} to {2}@{3} is not present, so it" " cannot be revoked" + ).format(grant, database, user, host) return ret diff --git a/salt/states/mysql_query.py b/salt/states/mysql_query.py index 6425f8e6bdc..afcbf44aaee 100644 --- a/salt/states/mysql_query.py +++ b/salt/states/mysql_query.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Execution of MySQL queries ========================== @@ -18,12 +18,13 @@ Its output may be stored in a file or in a grain. - database: my_database - query: "SELECT * FROM table;" - output: "/tmp/query_id.txt" -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import sys + import os.path +import sys # Import Salt libs import salt.utils.files @@ -34,33 +35,37 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load if the mysql module is available in __salt__ - ''' - return 'mysql.query' in __salt__ + """ + if "mysql.query" in __salt__: + return True + return (False, "mysql module could not be loaded") def _get_mysql_error(): - ''' + """ Look in module context for a MySQL error. Eventually we should make a less ugly way of doing this. - ''' - return sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('mysql.error', None) + """ + return sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "mysql.error", None + ) -def run_file(name, - database, - query_file=None, - output=None, - grain=None, - key=None, - overwrite=True, - saltenv=None, - check_db_exists=True, - **connection_args): - ''' +def run_file( + name, + database, + query_file=None, + output=None, + grain=None, + key=None, + overwrite=True, + saltenv=None, + check_db_exists=True, + **connection_args +): + """ Execute an arbitrary query on the specified database .. versionadded:: 2017.7.0 @@ -96,142 +101,153 @@ def run_file(name, The state run will check that the specified database exists (default=True) before running any queries - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Database {0} is already present'.format(database)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Database {0} is already present".format(database), + } - if any([query_file.startswith(proto) for proto in ['http://', 'https://', 'salt://', 's3://', 'swift://']]): - query_file = __salt__['cp.cache_file'](query_file, saltenv=saltenv or __env__) + if any( + [ + query_file.startswith(proto) + for proto in ["http://", "https://", "salt://", "s3://", "swift://"] + ] + ): + query_file = __salt__["cp.cache_file"](query_file, saltenv=saltenv or __env__) if not os.path.exists(query_file): - ret['comment'] = 'File {0} does not exist'.format(query_file) - ret['result'] = False + ret["comment"] = "File {0} does not exist".format(query_file) + ret["result"] = False return ret # check if database exists - if check_db_exists and not __salt__['mysql.db_exists'](database, **connection_args): + if check_db_exists and not __salt__["mysql.db_exists"](database, **connection_args): err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret - ret['result'] = None - ret['comment'] = ('Database {0} is not present' - ).format(database) + ret["result"] = None + ret["comment"] = ("Database {0} is not present").format(database) return ret # Check if execution needed - if output == 'grain': + if output == "grain": if grain is not None and key is None: - if not overwrite and grain in __salt__['grains.ls'](): - ret['comment'] = 'No execution needed. Grain ' + grain\ - + ' already set' + if not overwrite and grain in __salt__["grains.ls"](): + ret["comment"] = "No execution needed. Grain " + grain + " already set" return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Query would execute, storing result in '\ - + 'grain: ' + grain + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Query would execute, storing result in " + "grain: " + grain + ) return ret elif grain is not None: - if grain in __salt__['grains.ls'](): - grain_value = __salt__['grains.get'](grain) + if grain in __salt__["grains.ls"](): + grain_value = __salt__["grains.get"](grain) else: grain_value = {} if not overwrite and key in grain_value: - ret['comment'] = 'No execution needed. Grain ' + grain\ - + ':' + key + ' already set' + ret["comment"] = ( + "No execution needed. Grain " + grain + ":" + key + " already set" + ) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Query would execute, storing result in '\ - + 'grain: ' + grain + ':' + key + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Query would execute, storing result in " + + "grain: " + + grain + + ":" + + key + ) return ret else: - ret['result'] = False - ret['comment'] = "Error: output type 'grain' needs the grain "\ - + "parameter\n" + ret["result"] = False + ret["comment"] = ( + "Error: output type 'grain' needs the grain " + "parameter\n" + ) return ret elif output is not None: if not overwrite and os.path.isfile(output): - ret['comment'] = 'No execution needed. File ' + output\ - + ' already set' + ret["comment"] = "No execution needed. File " + output + " already set" return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Query would execute, storing result in '\ - + 'file: ' + output + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Query would execute, storing result in " + "file: " + output + ) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Query would execute, not storing result' + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = "Query would execute, not storing result" return ret # The database is present, execute the query - query_result = __salt__['mysql.file_query'](database, query_file, **connection_args) + query_result = __salt__["mysql.file_query"](database, query_file, **connection_args) if query_result is False: - ret['result'] = False + ret["result"] = False return ret mapped_results = [] - if 'results' in query_result: - for res in query_result['results']: + if "results" in query_result: + for res in query_result["results"]: mapped_line = {} - for idx, col in enumerate(query_result['columns']): + for idx, col in enumerate(query_result["columns"]): mapped_line[col] = res[idx] mapped_results.append(mapped_line) - query_result['results'] = mapped_results + query_result["results"] = mapped_results - ret['comment'] = six.text_type(query_result) + ret["comment"] = six.text_type(query_result) - if output == 'grain': + if output == "grain": if grain is not None and key is None: - __salt__['grains.setval'](grain, query_result) - ret['changes']['query'] = "Executed. Output into grain: "\ - + grain + __salt__["grains.setval"](grain, query_result) + ret["changes"]["query"] = "Executed. Output into grain: " + grain elif grain is not None: - if grain in __salt__['grains.ls'](): - grain_value = __salt__['grains.get'](grain) + if grain in __salt__["grains.ls"](): + grain_value = __salt__["grains.get"](grain) else: grain_value = {} grain_value[key] = query_result - __salt__['grains.setval'](grain, grain_value) - ret['changes']['query'] = "Executed. Output into grain: "\ - + grain + ":" + key + __salt__["grains.setval"](grain, grain_value) + ret["changes"]["query"] = ( + "Executed. Output into grain: " + grain + ":" + key + ) elif output is not None: - ret['changes']['query'] = "Executed. Output into " + output - with salt.utils.files.fopen(output, 'w') as output_file: - if 'results' in query_result: - for res in query_result['results']: + ret["changes"]["query"] = "Executed. Output into " + output + with salt.utils.files.fopen(output, "w") as output_file: + if "results" in query_result: + for res in query_result["results"]: for col, val in six.iteritems(res): output_file.write( - salt.utils.stringutils.to_str( - col + ':' + val + '\n' - ) + salt.utils.stringutils.to_str(col + ":" + val + "\n") ) else: - output_file.write( - salt.utils.stringutils.to_str(query_result) - ) + output_file.write(salt.utils.stringutils.to_str(query_result)) else: - ret['changes']['query'] = "Executed" + ret["changes"]["query"] = "Executed" return ret -def run(name, - database, - query, - output=None, - grain=None, - key=None, - overwrite=True, - check_db_exists=True, - **connection_args): - ''' +def run( + name, + database, + query, + output=None, + grain=None, + key=None, + overwrite=True, + check_db_exists=True, + **connection_args +): + """ Execute an arbitrary query on the specified database name @@ -261,121 +277,123 @@ def run(name, check_db_exists: The state run will check that the specified database exists (default=True) before running any queries - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Database {0} is already present'.format(database)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Database {0} is already present".format(database), + } # check if database exists - if check_db_exists and not __salt__['mysql.db_exists'](database, **connection_args): + if check_db_exists and not __salt__["mysql.db_exists"](database, **connection_args): err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret - ret['result'] = None - ret['comment'] = ('Database {0} is not present' - ).format(name) + ret["result"] = None + ret["comment"] = ("Database {0} is not present").format(name) return ret # Check if execution needed - if output == 'grain': + if output == "grain": if grain is not None and key is None: - if not overwrite and grain in __salt__['grains.ls'](): - ret['comment'] = 'No execution needed. Grain ' + grain\ - + ' already set' + if not overwrite and grain in __salt__["grains.ls"](): + ret["comment"] = "No execution needed. Grain " + grain + " already set" return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Query would execute, storing result in '\ - + 'grain: ' + grain + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Query would execute, storing result in " + "grain: " + grain + ) return ret elif grain is not None: - if grain in __salt__['grains.ls'](): - grain_value = __salt__['grains.get'](grain) + if grain in __salt__["grains.ls"](): + grain_value = __salt__["grains.get"](grain) else: grain_value = {} if not overwrite and key in grain_value: - ret['comment'] = 'No execution needed. Grain ' + grain\ - + ':' + key + ' already set' + ret["comment"] = ( + "No execution needed. Grain " + grain + ":" + key + " already set" + ) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Query would execute, storing result in '\ - + 'grain: ' + grain + ':' + key + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Query would execute, storing result in " + + "grain: " + + grain + + ":" + + key + ) return ret else: - ret['result'] = False - ret['comment'] = "Error: output type 'grain' needs the grain "\ - + "parameter\n" + ret["result"] = False + ret["comment"] = ( + "Error: output type 'grain' needs the grain " + "parameter\n" + ) return ret elif output is not None: if not overwrite and os.path.isfile(output): - ret['comment'] = 'No execution needed. File ' + output\ - + ' already set' + ret["comment"] = "No execution needed. File " + output + " already set" return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Query would execute, storing result in '\ - + 'file: ' + output + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Query would execute, storing result in " + "file: " + output + ) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Query would execute, not storing result' + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = "Query would execute, not storing result" return ret # The database is present, execute the query - query_result = __salt__['mysql.query'](database, query, **connection_args) + query_result = __salt__["mysql.query"](database, query, **connection_args) mapped_results = [] - if 'results' in query_result: - for res in query_result['results']: + if "results" in query_result: + for res in query_result["results"]: mapped_line = {} - for idx, col in enumerate(query_result['columns']): + for idx, col in enumerate(query_result["columns"]): mapped_line[col] = res[idx] mapped_results.append(mapped_line) - query_result['results'] = mapped_results + query_result["results"] = mapped_results - ret['comment'] = six.text_type(query_result) + ret["comment"] = six.text_type(query_result) - if output == 'grain': + if output == "grain": if grain is not None and key is None: - __salt__['grains.setval'](grain, query_result) - ret['changes']['query'] = "Executed. Output into grain: "\ - + grain + __salt__["grains.setval"](grain, query_result) + ret["changes"]["query"] = "Executed. Output into grain: " + grain elif grain is not None: - if grain in __salt__['grains.ls'](): - grain_value = __salt__['grains.get'](grain) + if grain in __salt__["grains.ls"](): + grain_value = __salt__["grains.get"](grain) else: grain_value = {} grain_value[key] = query_result - __salt__['grains.setval'](grain, grain_value) - ret['changes']['query'] = "Executed. Output into grain: "\ - + grain + ":" + key + __salt__["grains.setval"](grain, grain_value) + ret["changes"]["query"] = ( + "Executed. Output into grain: " + grain + ":" + key + ) elif output is not None: - ret['changes']['query'] = "Executed. Output into " + output - with salt.utils.files.fopen(output, 'w') as output_file: - if 'results' in query_result: - for res in query_result['results']: + ret["changes"]["query"] = "Executed. Output into " + output + with salt.utils.files.fopen(output, "w") as output_file: + if "results" in query_result: + for res in query_result["results"]: for col, val in six.iteritems(res): output_file.write( - salt.utils.stringutils.to_str( - col + ':' + val + '\n' - ) + salt.utils.stringutils.to_str(col + ":" + val + "\n") ) else: if isinstance(query_result, six.text_type): - output_file.write( - salt.utils.stringutils.to_str(query_result) - ) + output_file.write(salt.utils.stringutils.to_str(query_result)) else: for col, val in six.iteritems(query_result): output_file.write( - salt.utils.stringutils.to_str( - '{0}:{1}\n'.format(col, val) - ) + salt.utils.stringutils.to_str("{0}:{1}\n".format(col, val)) ) else: - ret['changes']['query'] = "Executed" + ret["changes"]["query"] = "Executed" return ret diff --git a/salt/states/mysql_user.py b/salt/states/mysql_user.py index 43e7cb5ba9c..f4c1a30c67f 100644 --- a/salt/states/mysql_user.py +++ b/salt/states/mysql_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of MySQL users ========================= @@ -39,7 +39,7 @@ overridden in states using the following arguments: ``connection_host``, This state is not able to grant permissions for the user. See :py:mod:`salt.states.mysql_grants` for further instructions. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -50,32 +50,36 @@ import salt.utils.data def __virtual__(): - ''' + """ Only load if the mysql module is in __salt__ - ''' - return 'mysql.user_create' in __salt__ + """ + if "mysql.user_create" in __salt__: + return True + return (False, "mysql module could not be loaded") def _get_mysql_error(): - ''' + """ Look in module context for a MySQL error. Eventually we should make a less ugly way of doing this. - ''' - return sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('mysql.error', None) + """ + return sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "mysql.error", None + ) -def present(name, - host='localhost', - password=None, - password_hash=None, - allow_passwordless=False, - unix_socket=False, - password_column=None, - auth_plugin='mysql_native_password', - **connection_args): - ''' +def present( + name, + host="localhost", + password=None, + password_hash=None, + allow_passwordless=False, + unix_socket=False, + password_column=None, + auth_plugin="mysql_native_password", + **connection_args +): + """ Ensure that the named user is present with the specified properties. A passwordless user can be configured by omitting ``password`` and ``password_hash``, and setting ``allow_passwordless`` to ``True``. @@ -112,187 +116,190 @@ def present(name, unix_socket If ``True`` and allow_passwordless is ``True``, the unix_socket auth plugin will be used. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User {0}@{1} is already present'.format(name, host)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "User {0}@{1} is already present".format(name, host), + } passwordless = not any((password, password_hash)) # check if user exists with the same password (or passwordless login) if passwordless: if not salt.utils.data.is_true(allow_passwordless): - ret['comment'] = 'Either password or password_hash must be ' \ - 'specified, unless allow_passwordless is True' - ret['result'] = False + ret["comment"] = ( + "Either password or password_hash must be " + "specified, unless allow_passwordless is True" + ) + ret["result"] = False return ret else: - if __salt__['mysql.user_exists'](name, - host, - passwordless=True, - unix_socket=unix_socket, - password_column=password_column, - **connection_args): - ret['comment'] += ' with passwordless login' + if __salt__["mysql.user_exists"]( + name, + host, + passwordless=True, + unix_socket=unix_socket, + password_column=password_column, + **connection_args + ): + ret["comment"] += " with passwordless login" return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret else: - if __salt__['mysql.user_exists'](name, - host, - password, - password_hash, - unix_socket=unix_socket, - password_column=password_column, - **connection_args): - if auth_plugin == 'mysql_native_password': - ret['comment'] += ' with the desired password' + if __salt__["mysql.user_exists"]( + name, + host, + password, + password_hash, + unix_socket=unix_socket, + password_column=password_column, + **connection_args + ): + if auth_plugin == "mysql_native_password": + ret["comment"] += " with the desired password" if password_hash and not password: - ret['comment'] += ' hash' + ret["comment"] += " hash" else: - ret['comment'] += '. Unable to verify password.' + ret["comment"] += ". Unable to verify password." return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret # check if user exists with a different password - if __salt__['mysql.user_exists'](name, - host, - unix_socket=unix_socket, - **connection_args): + if __salt__["mysql.user_exists"]( + name, host, unix_socket=unix_socket, **connection_args + ): # The user is present, change the password - if __opts__['test']: - ret['comment'] = \ - 'Password for user {0}@{1} is set to be '.format(name, host) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Password for user {0}@{1} is set to be ".format( + name, host + ) + ret["result"] = None if passwordless: - ret['comment'] += 'cleared' + ret["comment"] += "cleared" if not salt.utils.data.is_true(allow_passwordless): - ret['comment'] += ', but allow_passwordless != True' - ret['result'] = False + ret["comment"] += ", but allow_passwordless != True" + ret["result"] = False else: - ret['comment'] += 'changed' + ret["comment"] += "changed" return ret - if __salt__['mysql.user_chpass'](name, - host, - password, - password_hash, - allow_passwordless, - unix_socket, - **connection_args): - ret['comment'] = \ - 'Password for user {0}@{1} has been ' \ - '{2}'.format(name, host, - 'cleared' if passwordless else 'changed') - ret['changes'][name] = 'Updated' + if __salt__["mysql.user_chpass"]( + name, + host, + password, + password_hash, + allow_passwordless, + unix_socket, + **connection_args + ): + ret["comment"] = "Password for user {0}@{1} has been " "{2}".format( + name, host, "cleared" if passwordless else "changed" + ) + ret["changes"][name] = "Updated" else: - ret['comment'] = \ - 'Failed to {0} password for user ' \ - '{1}@{2}'.format('clear' if passwordless else 'change', - name, host) + ret["comment"] = "Failed to {0} password for user " "{1}@{2}".format( + "clear" if passwordless else "change", name, host + ) err = _get_mysql_error() if err is not None: - ret['comment'] += ' ({0})'.format(err) + ret["comment"] += " ({0})".format(err) if passwordless and not salt.utils.data.is_true(allow_passwordless): - ret['comment'] += '. Note: allow_passwordless must be True ' \ - 'to permit passwordless login.' - ret['result'] = False + ret["comment"] += ( + ". Note: allow_passwordless must be True " + "to permit passwordless login." + ) + ret["result"] = False else: err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret # The user is not present, make it! - if __opts__['test']: - ret['comment'] = \ - 'User {0}@{1} is set to be added'.format(name, host) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "User {0}@{1} is set to be added".format(name, host) + ret["result"] = None if passwordless: - ret['comment'] += ' with passwordless login' + ret["comment"] += " with passwordless login" if not salt.utils.data.is_true(allow_passwordless): - ret['comment'] += ', but allow_passwordless != True' - ret['result'] = False + ret["comment"] += ", but allow_passwordless != True" + ret["result"] = False return ret - if __salt__['mysql.user_create'](name, - host, - password, - password_hash, - allow_passwordless, - unix_socket=unix_socket, - password_column=password_column, - auth_plugin=auth_plugin, - **connection_args): - ret['comment'] = \ - 'The user {0}@{1} has been added'.format(name, host) + if __salt__["mysql.user_create"]( + name, + host, + password, + password_hash, + allow_passwordless, + unix_socket=unix_socket, + password_column=password_column, + auth_plugin=auth_plugin, + **connection_args + ): + ret["comment"] = "The user {0}@{1} has been added".format(name, host) if passwordless: - ret['comment'] += ' with passwordless login' - ret['changes'][name] = 'Present' + ret["comment"] += " with passwordless login" + ret["changes"][name] = "Present" else: - ret['comment'] = 'Failed to create user {0}@{1}'.format(name, host) + ret["comment"] = "Failed to create user {0}@{1}".format(name, host) err = _get_mysql_error() if err is not None: - ret['comment'] += ' ({0})'.format(err) - ret['result'] = False + ret["comment"] += " ({0})".format(err) + ret["result"] = False return ret -def absent(name, - host='localhost', - **connection_args): - ''' +def absent(name, host="localhost", **connection_args): + """ Ensure that the named user is absent name The name of the user to remove - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # Check if user exists, and if so, remove it - if __salt__['mysql.user_exists'](name, host, **connection_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0}@{1} is set to be removed'.format( - name, - host) + if __salt__["mysql.user_exists"](name, host, **connection_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User {0}@{1} is set to be removed".format(name, host) return ret - if __salt__['mysql.user_remove'](name, host, **connection_args): - ret['comment'] = 'User {0}@{1} has been removed'.format(name, host) - ret['changes'][name] = 'Absent' + if __salt__["mysql.user_remove"](name, host, **connection_args): + ret["comment"] = "User {0}@{1} has been removed".format(name, host) + ret["changes"][name] = "Absent" return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret else: err = _get_mysql_error() if err is not None: - ret['comment'] = err - ret['result'] = False + ret["comment"] = err + ret["result"] = False return ret # fallback - ret['comment'] = ( - 'User {0}@{1} is not present, so it cannot be removed' - ).format(name, host) + ret["comment"] = ("User {0}@{1} is not present, so it cannot be removed").format( + name, host + ) return ret diff --git a/salt/states/net_napalm_yang.py b/salt/states/net_napalm_yang.py index 8b9726786f5..404e2b35d60 100644 --- a/salt/states/net_napalm_yang.py +++ b/salt/states/net_napalm_yang.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" NAPALM YANG state ================= @@ -20,21 +20,11 @@ Please check Installation_ for complete details. .. _NAPALM: https://napalm.readthedocs.io .. _Installation: https://napalm.readthedocs.io/en/latest/installation.html -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging -log = logging.getLogger(__file__) - -# Import third party libs -try: - # pylint: disable=unused-import - import napalm_yang - HAS_NAPALM_YANG = True - # pylint: enable=unused-import -except ImportError: - HAS_NAPALM_YANG = False - # Import salt modules import salt.utils.files import salt.utils.json @@ -42,11 +32,24 @@ import salt.utils.napalm import salt.utils.stringutils import salt.utils.yaml +log = logging.getLogger(__file__) + +# Import third party libs +try: + # pylint: disable=unused-import + import napalm_yang + + HAS_NAPALM_YANG = True + # pylint: enable=unused-import +except ImportError: + HAS_NAPALM_YANG = False + + # ------------------------------------------------------------------------------ # state properties # ------------------------------------------------------------------------------ -__virtualname__ = 'napalm_yang' +__virtualname__ = "napalm_yang" # ------------------------------------------------------------------------------ # global variables @@ -58,14 +61,18 @@ __virtualname__ = 'napalm_yang' def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. This module in particular requires also napalm-yang. - ''' + """ if not HAS_NAPALM_YANG: - return (False, 'Unable to load napalm_yang execution module: please install napalm-yang!') + return ( + False, + "Unable to load napalm_yang execution module: please install napalm-yang!", + ) return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ------------------------------------------------------------------------------ # helper functions -- will not be exported # ------------------------------------------------------------------------------ @@ -75,10 +82,8 @@ def __virtual__(): # ------------------------------------------------------------------------------ -def managed(name, - data, - **kwargs): - ''' +def managed(name, data, **kwargs): + """ Manage the device configuration given the input data structured according to the YANG models. @@ -139,73 +144,74 @@ def managed(name, Et2: config: description: "description example" - ''' - models = kwargs.get('models', None) + """ + models = kwargs.get("models", None) if isinstance(models, tuple) and isinstance(models[0], list): models = models[0] ret = salt.utils.napalm.default_ret(name) - test = kwargs.get('test', False) or __opts__.get('test', False) - debug = kwargs.get('debug', False) or __opts__.get('debug', False) - commit = kwargs.get('commit', True) or __opts__.get('commit', True) - replace = kwargs.get('replace', False) or __opts__.get('replace', False) - return_compliance_report = kwargs.get('compliance_report', False) or __opts__.get('compliance_report', False) - profiles = kwargs.get('profiles', []) - temp_file = __salt__['temp.file']() - log.debug('Creating temp file: %s', temp_file) - if 'to_dict' not in data: - data = {'to_dict': data} + test = kwargs.get("test", False) or __opts__.get("test", False) + debug = kwargs.get("debug", False) or __opts__.get("debug", False) + commit = kwargs.get("commit", True) or __opts__.get("commit", True) + replace = kwargs.get("replace", False) or __opts__.get("replace", False) + return_compliance_report = kwargs.get("compliance_report", False) or __opts__.get( + "compliance_report", False + ) + profiles = kwargs.get("profiles", []) + temp_file = __salt__["temp.file"]() + log.debug("Creating temp file: %s", temp_file) + if "to_dict" not in data: + data = {"to_dict": data} data = [data] - with salt.utils.files.fopen(temp_file, 'w') as file_handle: + with salt.utils.files.fopen(temp_file, "w") as file_handle: salt.utils.yaml.safe_dump( salt.utils.json.loads(salt.utils.json.dumps(data)), file_handle, - encoding='utf-8' + encoding="utf-8", ) - device_config = __salt__['napalm_yang.parse'](*models, - config=True, - profiles=profiles) - log.debug('Parsed the config from the device:') + device_config = __salt__["napalm_yang.parse"]( + *models, config=True, profiles=profiles + ) + log.debug("Parsed the config from the device:") log.debug(device_config) - compliance_report = __salt__['napalm_yang.compliance_report'](device_config, - *models, - filepath=temp_file) - log.debug('Compliance report:') + compliance_report = __salt__["napalm_yang.compliance_report"]( + device_config, *models, filepath=temp_file + ) + log.debug("Compliance report:") log.debug(compliance_report) - complies = compliance_report.get('complies', False) + complies = compliance_report.get("complies", False) if complies: - ret.update({ - 'result': True, - 'comment': 'Already configured as required.' - }) - log.debug('All good here.') + ret.update({"result": True, "comment": "Already configured as required."}) + log.debug("All good here.") return ret - log.debug('Does not comply, trying to generate and load config') - data = data[0]['to_dict'] - if '_kwargs' in data: - data.pop('_kwargs') - loaded_changes = __salt__['napalm_yang.load_config'](data, - *models, - profiles=profiles, - test=test, - debug=debug, - commit=commit, - replace=replace) - log.debug('Loaded config result:') + log.debug("Does not comply, trying to generate and load config") + data = data[0]["to_dict"] + if "_kwargs" in data: + data.pop("_kwargs") + loaded_changes = __salt__["napalm_yang.load_config"]( + data, + *models, + profiles=profiles, + test=test, + debug=debug, + commit=commit, + replace=replace + ) + log.debug("Loaded config result:") log.debug(loaded_changes) - __salt__['file.remove'](temp_file) - loaded_changes['compliance_report'] = compliance_report - return salt.utils.napalm.loaded_ret(ret, - loaded_changes, - test, - debug, - opts=__opts__, - compliance_report=return_compliance_report) + __salt__["file.remove"](temp_file) + loaded_changes["compliance_report"] = compliance_report + return salt.utils.napalm.loaded_ret( + ret, + loaded_changes, + test, + debug, + opts=__opts__, + compliance_report=return_compliance_report, + ) -def configured(name, - data, - **kwargs): - ''' +def configured(name, data, **kwargs): + """ Configure the network device, given the input data strucuted according to the YANG models. @@ -272,23 +278,25 @@ def configured(name, Et2: config: description: "description example" - ''' - models = kwargs.get('models', None) + """ + models = kwargs.get("models", None) if isinstance(models, tuple) and isinstance(models[0], list): models = models[0] ret = salt.utils.napalm.default_ret(name) - test = kwargs.get('test', False) or __opts__.get('test', False) - debug = kwargs.get('debug', False) or __opts__.get('debug', False) - commit = kwargs.get('commit', True) or __opts__.get('commit', True) - replace = kwargs.get('replace', False) or __opts__.get('replace', False) - profiles = kwargs.get('profiles', []) - if '_kwargs' in data: - data.pop('_kwargs') - loaded_changes = __salt__['napalm_yang.load_config'](data, - *models, - profiles=profiles, - test=test, - debug=debug, - commit=commit, - replace=replace) + test = kwargs.get("test", False) or __opts__.get("test", False) + debug = kwargs.get("debug", False) or __opts__.get("debug", False) + commit = kwargs.get("commit", True) or __opts__.get("commit", True) + replace = kwargs.get("replace", False) or __opts__.get("replace", False) + profiles = kwargs.get("profiles", []) + if "_kwargs" in data: + data.pop("_kwargs") + loaded_changes = __salt__["napalm_yang.load_config"]( + data, + *models, + profiles=profiles, + test=test, + debug=debug, + commit=commit, + replace=replace + ) return salt.utils.napalm.loaded_ret(ret, loaded_changes, test, debug) diff --git a/salt/states/netacl.py b/salt/states/netacl.py index 1b3930b2677..622c1b9c1de 100644 --- a/salt/states/netacl.py +++ b/salt/states/netacl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Network ACL =========== @@ -32,10 +32,13 @@ Please check Installation_ for complete details. .. _NAPALM: https://napalm.readthedocs.io .. _Installation: https://napalm.readthedocs.io/en/latest/installation.html -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + +import salt.utils.napalm + log = logging.getLogger(__file__) # Import third party libs @@ -45,18 +48,18 @@ try: import capirca.aclgen import capirca.lib.policy import capirca.lib.aclgenerator + HAS_CAPIRCA = True # pylint: enable=W0611 except ImportError: HAS_CAPIRCA = False -import salt.utils.napalm # ------------------------------------------------------------------------------ # state properties # ------------------------------------------------------------------------------ -__virtualname__ = 'netacl' +__virtualname__ = "netacl" # ------------------------------------------------------------------------------ # global variables @@ -68,14 +71,18 @@ __virtualname__ = 'netacl' def __virtual__(): - ''' + """ This module requires both NAPALM and Capirca. - ''' + """ if HAS_CAPIRCA and salt.utils.napalm.virtual(__opts__, __virtualname__, __file__): return __virtualname__ else: - return (False, 'The netacl state cannot be loaded: \ - Please install capirca and napalm.') + return ( + False, + "The netacl state cannot be loaded: \ + Please install capirca and napalm.", + ) + # ------------------------------------------------------------------------------ # helper functions -- will not be exported @@ -86,25 +93,27 @@ def __virtual__(): # ------------------------------------------------------------------------------ -def term(name, - filter_name, - term_name, - filter_options=None, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=False, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d', - test=False, - commit=True, - debug=False, - source_service=None, - destination_service=None, - **term_fields): - ''' +def term( + name, + filter_name, + term_name, + filter_options=None, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=False, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", + test=False, + commit=True, + debug=False, + source_service=None, + destination_service=None, + **term_fields +): + """ Manage the configuration of a specific policy term. filter_name @@ -416,49 +425,53 @@ def term(name, When passing retrieved pillar data into the state file, it is strongly recommended to use the json serializer explicitly (`` | json``), instead of relying on the default Python serializer. - ''' + """ ret = salt.utils.napalm.default_ret(name) - test = __opts__['test'] or test + test = __opts__["test"] or test if not filter_options: filter_options = [] - loaded = __salt__['netacl.load_term_config'](filter_name, - term_name, - filter_options=filter_options, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar, - revision_id=revision_id if revision_id else name, - revision_no=revision_no, - revision_date=revision_date, - revision_date_format=revision_date_format, - source_service=source_service, - destination_service=destination_service, - test=test, - commit=commit, - debug=debug, - **term_fields) + loaded = __salt__["netacl.load_term_config"]( + filter_name, + term_name, + filter_options=filter_options, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + revision_id=revision_id if revision_id else name, + revision_no=revision_no, + revision_date=revision_date, + revision_date_format=revision_date_format, + source_service=source_service, + destination_service=destination_service, + test=test, + commit=commit, + debug=debug, + **term_fields + ) return salt.utils.napalm.loaded_ret(ret, loaded, test, debug) -def filter(name, # pylint: disable=redefined-builtin - filter_name, - filter_options=None, - terms=None, - prepend=True, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=False, - only_lower_merge=False, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d', - test=False, - commit=True, - debug=False): - ''' +def filter( + name, # pylint: disable=redefined-builtin + filter_name, + filter_options=None, + terms=None, + prepend=True, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=False, + only_lower_merge=False, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", + test=False, + commit=True, + debug=False, +): + """ Generate and load the configuration of a policy filter. filter_name @@ -646,48 +659,52 @@ def filter(name, # pylint: disable=redefined-builtin When passing retrieved pillar data into the state file, it is strongly recommended to use the json serializer explicitly (`` | json``), instead of relying on the default Python serializer. - ''' + """ ret = salt.utils.napalm.default_ret(name) - test = __opts__['test'] or test + test = __opts__["test"] or test if not filter_options: filter_options = [] if not terms: terms = [] - loaded = __salt__['netacl.load_filter_config'](filter_name, - filter_options=filter_options, - terms=terms, - prepend=prepend, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar, - only_lower_merge=only_lower_merge, - revision_id=revision_id if revision_id else name, - revision_no=revision_no, - revision_date=revision_date, - revision_date_format=revision_date_format, - test=test, - commit=commit, - debug=debug) + loaded = __salt__["netacl.load_filter_config"]( + filter_name, + filter_options=filter_options, + terms=terms, + prepend=prepend, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + only_lower_merge=only_lower_merge, + revision_id=revision_id if revision_id else name, + revision_no=revision_no, + revision_date=revision_date, + revision_date_format=revision_date_format, + test=test, + commit=commit, + debug=debug, + ) return salt.utils.napalm.loaded_ret(ret, loaded, test, debug) -def managed(name, - filters=None, - prepend=True, - pillar_key='acl', - pillarenv=None, - saltenv=None, - merge_pillar=False, - only_lower_merge=False, - revision_id=None, - revision_no=None, - revision_date=True, - revision_date_format='%Y/%m/%d', - test=False, - commit=True, - debug=False): - ''' +def managed( + name, + filters=None, + prepend=True, + pillar_key="acl", + pillarenv=None, + saltenv=None, + merge_pillar=False, + only_lower_merge=False, + revision_id=None, + revision_no=None, + revision_date=True, + revision_date_format="%Y/%m/%d", + test=False, + commit=True, + debug=False, +): + """ Manage the whole firewall configuration. filters @@ -943,23 +960,25 @@ def managed(name, When passing retrieved pillar data into the state file, it is strongly recommended to use the json serializer explicitly (`` | json``), instead of relying on the default Python serializer. - ''' + """ ret = salt.utils.napalm.default_ret(name) - test = __opts__['test'] or test + test = __opts__["test"] or test if not filters: filters = [] - loaded = __salt__['netacl.load_policy_config'](filters=filters, - prepend=prepend, - pillar_key=pillar_key, - pillarenv=pillarenv, - saltenv=saltenv, - merge_pillar=merge_pillar, - only_lower_merge=only_lower_merge, - revision_id=revision_id if revision_id else name, - revision_no=revision_no, - revision_date=revision_date, - revision_date_format=revision_date_format, - test=test, - commit=commit, - debug=debug) + loaded = __salt__["netacl.load_policy_config"]( + filters=filters, + prepend=prepend, + pillar_key=pillar_key, + pillarenv=pillarenv, + saltenv=saltenv, + merge_pillar=merge_pillar, + only_lower_merge=only_lower_merge, + revision_id=revision_id if revision_id else name, + revision_no=revision_no, + revision_date=revision_date, + revision_date_format=revision_date_format, + test=test, + commit=commit, + debug=debug, + ) return salt.utils.napalm.loaded_ret(ret, loaded, test, debug) diff --git a/salt/states/netconfig.py b/salt/states/netconfig.py index b900c35f00b..7bd5d17562c 100644 --- a/salt/states/netconfig.py +++ b/salt/states/netconfig.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Network Config ============== @@ -16,22 +16,24 @@ Dependencies - :mod:`Network-related basic features execution module <salt.modules.napalm_network>` .. versionadded:: 2017.7.0 -''' +""" # Import Salt libs from __future__ import absolute_import, print_function, unicode_literals -import logging -log = logging.getLogger(__name__) +import logging # import Salt libs import salt.utils.napalm +log = logging.getLogger(__name__) + + # ---------------------------------------------------------------------------------------------------------------------- # state properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'netconfig' +__virtualname__ = "netconfig" # ---------------------------------------------------------------------------------------------------------------------- # global variables @@ -43,81 +45,89 @@ __virtualname__ = 'netconfig' def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- -def _update_config(template_name, - template_source=None, - template_hash=None, - template_hash_name=None, - template_user='root', - template_group='root', - template_mode='755', - template_attrs='--------------e----', - saltenv=None, - template_engine='jinja', - skip_verify=False, - defaults=None, - test=False, - commit=True, - debug=False, - replace=False, - **template_vars): - ''' +def _update_config( + template_name, + template_source=None, + template_hash=None, + template_hash_name=None, + template_user="root", + template_group="root", + template_mode="755", + template_attrs="--------------e----", + saltenv=None, + template_engine="jinja", + skip_verify=False, + defaults=None, + test=False, + commit=True, + debug=False, + replace=False, + **template_vars +): + """ Call the necessary functions in order to execute the state. For the moment this only calls the ``net.load_template`` function from the :mod:`Network-related basic features execution module <salt.modules.napalm_network>`, but this may change in time. - ''' + """ + + return __salt__["net.load_template"]( + template_name, + template_source=template_source, + template_hash=template_hash, + template_hash_name=template_hash_name, + template_user=template_user, + template_group=template_group, + template_mode=template_mode, + template_attrs=template_attrs, + saltenv=saltenv, + template_engine=template_engine, + skip_verify=skip_verify, + defaults=defaults, + test=test, + commit=commit, + debug=debug, + replace=replace, + **template_vars + ) - return __salt__['net.load_template'](template_name, - template_source=template_source, - template_hash=template_hash, - template_hash_name=template_hash_name, - template_user=template_user, - template_group=template_group, - template_mode=template_mode, - template_attrs=template_attrs, - saltenv=saltenv, - template_engine=template_engine, - skip_verify=skip_verify, - defaults=defaults, - test=test, - commit=commit, - debug=debug, - replace=replace, - **template_vars) # ---------------------------------------------------------------------------------------------------------------------- # callable functions # ---------------------------------------------------------------------------------------------------------------------- -def replace_pattern(name, - pattern, - repl, - count=0, - flags=8, - bufsize=1, - append_if_not_found=False, - prepend_if_not_found=False, - not_found_content=None, - search_only=False, - show_changes=True, - backslash_literal=False, - source='running', - path=None, - test=False, - replace=True, - debug=False, - commit=True): - ''' +def replace_pattern( + name, + pattern, + repl, + count=0, + flags=8, + bufsize=1, + append_if_not_found=False, + prepend_if_not_found=False, + not_found_content=None, + search_only=False, + show_changes=True, + backslash_literal=False, + source="running", + path=None, + test=False, + replace=True, + debug=False, + commit=True, +): + """ .. versionadded:: 2019.2.0 Replace occurrences of a pattern in the configuration source. If @@ -216,60 +226,64 @@ def replace_pattern(name, - pattern: OLD-POLICY-NAME - repl: new-policy-name - debug: true - ''' + """ ret = salt.utils.napalm.default_ret(name) # the user can override the flags the equivalent CLI args # which have higher precedence - test = __salt__['config.merge']('test', test) - debug = __salt__['config.merge']('debug', debug) - commit = __salt__['config.merge']('commit', commit) - replace = __salt__['config.merge']('replace', replace) # this might be a bit risky - replace_ret = __salt__['net.replace_pattern'](pattern, - repl, - count=count, - flags=flags, - bufsize=bufsize, - append_if_not_found=append_if_not_found, - prepend_if_not_found=prepend_if_not_found, - not_found_content=not_found_content, - search_only=search_only, - show_changes=show_changes, - backslash_literal=backslash_literal, - source=source, - path=path, - test=test, - replace=replace, - debug=debug, - commit=commit) + test = __salt__["config.merge"]("test", test) + debug = __salt__["config.merge"]("debug", debug) + commit = __salt__["config.merge"]("commit", commit) + replace = __salt__["config.merge"]("replace", replace) # this might be a bit risky + replace_ret = __salt__["net.replace_pattern"]( + pattern, + repl, + count=count, + flags=flags, + bufsize=bufsize, + append_if_not_found=append_if_not_found, + prepend_if_not_found=prepend_if_not_found, + not_found_content=not_found_content, + search_only=search_only, + show_changes=show_changes, + backslash_literal=backslash_literal, + source=source, + path=path, + test=test, + replace=replace, + debug=debug, + commit=commit, + ) return salt.utils.napalm.loaded_ret(ret, replace_ret, test, debug) -def saved(name, - source='running', - user=None, - group=None, - mode=None, - attrs=None, - makedirs=False, - dir_mode=None, - replace=True, - backup='', - show_changes=True, - create=True, - tmp_dir='', - tmp_ext='', - encoding=None, - encoding_errors='strict', - allow_empty=False, - follow_symlinks=True, - check_cmd=None, - win_owner=None, - win_perms=None, - win_deny_perms=None, - win_inheritance=True, - win_perms_reset=False, - **kwargs): - ''' +def saved( + name, + source="running", + user=None, + group=None, + mode=None, + attrs=None, + makedirs=False, + dir_mode=None, + replace=True, + backup="", + show_changes=True, + create=True, + tmp_dir="", + tmp_ext="", + encoding=None, + encoding_errors="strict", + allow_empty=False, + follow_symlinks=True, + check_cmd=None, + win_owner=None, + win_perms=None, + win_deny_perms=None, + win_inheritance=True, + win_perms_reset=False, + **kwargs +): + """ .. versionadded:: 2019.2.0 Save the configuration to a file on the local file system. @@ -415,62 +429,61 @@ def saved(name, on the 3rd of August 2018, at 5:15PM, on the Minion ``core1.lon01``, the configuration would saved in the file: ``/var/backups/core01.lon01/1533316558.cfg`` - ''' - ret = __salt__['net.config'](source=source) - if not ret['result']: - return { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': ret['comment'] - } - return __states__['file.managed'](name, - user=user, - group=group, - mode=mode, - attrs=attrs, - makedirs=makedirs, - dir_mode=dir_mode, - replace=replace, - backup=backup, - show_changes=show_changes, - create=create, - contents=ret['out'][source], - tmp_dir=tmp_dir, - tmp_ext=tmp_ext, - encoding=encoding, - encoding_errors=encoding_errors, - allow_empty=allow_empty, - follow_symlinks=follow_symlinks, - check_cmd=check_cmd, - win_owner=win_owner, - win_perms=win_perms, - win_deny_perms=win_deny_perms, - win_inheritance=win_inheritance, - win_perms_reset=win_perms_reset, - **kwargs) + """ + ret = __salt__["net.config"](source=source) + if not ret["result"]: + return {"name": name, "changes": {}, "result": False, "comment": ret["comment"]} + return __states__["file.managed"]( + name, + user=user, + group=group, + mode=mode, + attrs=attrs, + makedirs=makedirs, + dir_mode=dir_mode, + replace=replace, + backup=backup, + show_changes=show_changes, + create=create, + contents=ret["out"][source], + tmp_dir=tmp_dir, + tmp_ext=tmp_ext, + encoding=encoding, + encoding_errors=encoding_errors, + allow_empty=allow_empty, + follow_symlinks=follow_symlinks, + check_cmd=check_cmd, + win_owner=win_owner, + win_perms=win_perms, + win_deny_perms=win_deny_perms, + win_inheritance=win_inheritance, + win_perms_reset=win_perms_reset, + **kwargs + ) -def managed(name, - template_name=None, - template_source=None, - template_hash=None, - template_hash_name=None, - saltenv='base', - template_engine='jinja', - skip_verify=False, - context=None, - defaults=None, - test=False, - commit=True, - debug=False, - replace=False, - commit_in=None, - commit_at=None, - revert_in=None, - revert_at=None, - **template_vars): - ''' +def managed( + name, + template_name=None, + template_source=None, + template_hash=None, + template_hash_name=None, + saltenv="base", + template_engine="jinja", + skip_verify=False, + context=None, + defaults=None, + test=False, + commit=True, + debug=False, + replace=False, + commit_in=None, + commit_at=None, + revert_in=None, + revert_at=None, + **template_vars +): + """ Manages the configuration on network devices. By default this state will commit the changes on the device. If there are no changes required, it does not commit @@ -811,45 +824,47 @@ def managed(name, } } } - ''' + """ ret = salt.utils.napalm.default_ret(name) # the user can override the flags the equivalent CLI args # which have higher precedence - test = __salt__['config.merge']('test', test) - debug = __salt__['config.merge']('debug', debug) - commit = __salt__['config.merge']('commit', commit) - replace = __salt__['config.merge']('replace', replace) # this might be a bit risky - skip_verify = __salt__['config.merge']('skip_verify', skip_verify) - commit_in = __salt__['config.merge']('commit_in', commit_in) - commit_at = __salt__['config.merge']('commit_at', commit_at) - revert_in = __salt__['config.merge']('revert_in', revert_in) - revert_at = __salt__['config.merge']('revert_at', revert_at) + test = __salt__["config.merge"]("test", test) + debug = __salt__["config.merge"]("debug", debug) + commit = __salt__["config.merge"]("commit", commit) + replace = __salt__["config.merge"]("replace", replace) # this might be a bit risky + skip_verify = __salt__["config.merge"]("skip_verify", skip_verify) + commit_in = __salt__["config.merge"]("commit_in", commit_in) + commit_at = __salt__["config.merge"]("commit_at", commit_at) + revert_in = __salt__["config.merge"]("revert_in", revert_in) + revert_at = __salt__["config.merge"]("revert_at", revert_at) - config_update_ret = _update_config(template_name=template_name, - template_source=template_source, - template_hash=template_hash, - template_hash_name=template_hash_name, - saltenv=saltenv, - template_engine=template_engine, - skip_verify=skip_verify, - context=context, - defaults=defaults, - test=test, - commit=commit, - commit_in=commit_in, - commit_at=commit_at, - revert_in=revert_in, - revert_at=revert_at, - debug=debug, - replace=replace, - **template_vars) + config_update_ret = _update_config( + template_name=template_name, + template_source=template_source, + template_hash=template_hash, + template_hash_name=template_hash_name, + saltenv=saltenv, + template_engine=template_engine, + skip_verify=skip_verify, + context=context, + defaults=defaults, + test=test, + commit=commit, + commit_in=commit_in, + commit_at=commit_at, + revert_in=revert_in, + revert_at=revert_at, + debug=debug, + replace=replace, + **template_vars + ) return salt.utils.napalm.loaded_ret(ret, config_update_ret, test, debug) def commit_cancelled(name): - ''' + """ .. versionadded:: 2019.2.0 Cancel a commit scheduled to be executed via the ``commit_in`` and @@ -865,23 +880,18 @@ def commit_cancelled(name): '20180726083540640360': netconfig.commit_cancelled - ''' - cancelled = { - 'name': name, - 'result': None, - 'changes': {}, - 'comment': '' - } - if __opts__['test']: - cancelled['comment'] = 'It would cancel commit #{}'.format(name) + """ + cancelled = {"name": name, "result": None, "changes": {}, "comment": ""} + if __opts__["test"]: + cancelled["comment"] = "It would cancel commit #{}".format(name) return cancelled - ret = __salt__['net.cancel_commit'](name) + ret = __salt__["net.cancel_commit"](name) cancelled.update(ret) return cancelled def commit_confirmed(name): - ''' + """ .. versionadded:: 2019.2.0 Confirm a commit scheduled to be reverted via the ``revert_in`` and @@ -897,16 +907,11 @@ def commit_confirmed(name): '20180726083540640360': netconfig.commit_confirmed - ''' - confirmed = { - 'name': name, - 'result': None, - 'changes': {}, - 'comment': '' - } - if __opts__['test']: - confirmed['comment'] = 'It would confirm commit #{}'.format(name) + """ + confirmed = {"name": name, "result": None, "changes": {}, "comment": ""} + if __opts__["test"]: + confirmed["comment"] = "It would confirm commit #{}".format(name) return confirmed - ret = __salt__['net.confirm_commit'](name) + ret = __salt__["net.confirm_commit"](name) confirmed.update(ret) return confirmed diff --git a/salt/states/netntp.py b/salt/states/netntp.py index 2a190a5f6a3..9b05225c66e 100644 --- a/salt/states/netntp.py +++ b/salt/states/netntp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Network NTP =========== @@ -24,27 +24,30 @@ Dependencies .. _netaddr: https://pythonhosted.org/netaddr/ .. _dnspython: http://www.dnspython.org/ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -# Import 3rd-party libs -from salt.ext import six +import logging # import NAPALM utils import salt.utils.napalm +# Import 3rd-party libs +from salt.ext import six + try: from netaddr import IPAddress from netaddr.core import AddrFormatError + HAS_NETADDR = True except ImportError: HAS_NETADDR = False try: import dns.resolver + HAS_DNSRESOLVER = True except ImportError: HAS_DNSRESOLVER = False @@ -53,7 +56,7 @@ except ImportError: # state properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'netntp' +__virtualname__ = "netntp" log = logging.getLogger(__name__) @@ -67,11 +70,12 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -79,32 +83,27 @@ def __virtual__(): def _default_ret(name): - ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': '' - } + ret = {"name": name, "changes": {}, "result": False, "comment": ""} return ret def _retrieve_ntp_peers(): - '''Retrieves configured NTP peers''' + """Retrieves configured NTP peers""" - return __salt__['ntp.peers']() + return __salt__["ntp.peers"]() def _retrieve_ntp_servers(): - '''Retrieves configured NTP servers''' + """Retrieves configured NTP servers""" - return __salt__['ntp.servers']() + return __salt__["ntp.servers"]() def _check(peers): - '''Checks whether the input is a valid list of peers and transforms domain names into IP Addresses''' + """Checks whether the input is a valid list of peers and transforms domain names into IP Addresses""" if not isinstance(peers, list): return False @@ -113,7 +112,9 @@ def _check(peers): if not isinstance(peer, six.string_types): return False - if not HAS_NETADDR: # if does not have this lib installed, will simply try to load what user specified + if ( + not HAS_NETADDR + ): # if does not have this lib installed, will simply try to load what user specified # if the addresses are not correctly specified, will trow error when loading the actual config return True @@ -149,30 +150,30 @@ def _clean(lst): def _set_ntp_peers(peers): - '''Calls ntp.set_peers.''' + """Calls ntp.set_peers.""" - return __salt__['ntp.set_peers'](*peers, commit=False) + return __salt__["ntp.set_peers"](*peers, commit=False) def _set_ntp_servers(servers): - '''Calls ntp.set_servers.''' + """Calls ntp.set_servers.""" - return __salt__['ntp.set_servers'](*servers, commit=False) + return __salt__["ntp.set_servers"](*servers, commit=False) def _delete_ntp_peers(peers): - '''Calls ntp.delete_peers.''' + """Calls ntp.delete_peers.""" - return __salt__['ntp.delete_peers'](*peers, commit=False) + return __salt__["ntp.delete_peers"](*peers, commit=False) def _delete_ntp_servers(servers): - '''Calls ntp.delete_servers.''' + """Calls ntp.delete_servers.""" - return __salt__['ntp.delete_servers'](*servers, commit=False) + return __salt__["ntp.delete_servers"](*servers, commit=False) def _exec_fun(name, *kargs): @@ -183,35 +184,36 @@ def _exec_fun(name, *kargs): return None -def _check_diff_and_configure(fun_name, peers_servers, name='peers'): +def _check_diff_and_configure(fun_name, peers_servers, name="peers"): _ret = _default_ret(fun_name) - _options = ['peers', 'servers'] + _options = ["peers", "servers"] if name not in _options: return _ret - _retrieve_fun = '_retrieve_ntp_{what}'.format(what=name) - ntp_list_output = _exec_fun(_retrieve_fun) # contains only IP Addresses as dictionary keys + _retrieve_fun = "_retrieve_ntp_{what}".format(what=name) + ntp_list_output = _exec_fun( + _retrieve_fun + ) # contains only IP Addresses as dictionary keys - if ntp_list_output.get('result', False) is False: - _ret['comment'] = 'Cannot retrieve NTP {what} from the device: {reason}'.format( - what=name, - reason=ntp_list_output.get('comment') + if ntp_list_output.get("result", False) is False: + _ret["comment"] = "Cannot retrieve NTP {what} from the device: {reason}".format( + what=name, reason=ntp_list_output.get("comment") ) return _ret - configured_ntp_list = set(ntp_list_output.get('out', {})) + configured_ntp_list = set(ntp_list_output.get("out", {})) desired_ntp_list = set(peers_servers) if configured_ntp_list == desired_ntp_list: - _ret.update({ - 'comment': 'NTP {what} already configured as needed.'.format( - what=name - ), - 'result': True - }) + _ret.update( + { + "comment": "NTP {what} already configured as needed.".format(what=name), + "result": True, + } + ) return _ret list_to_set = list(desired_ntp_list - configured_ntp_list) @@ -222,19 +224,16 @@ def _check_diff_and_configure(fun_name, peers_servers, name='peers'): changes = {} if list_to_set: - changes['added'] = list_to_set + changes["added"] = list_to_set if list_to_delete: - changes['removed'] = list_to_delete + changes["removed"] = list_to_delete - _ret.update({ - 'changes': changes - }) + _ret.update({"changes": changes}) - if __opts__['test'] is True: - _ret.update({ - 'result': None, - 'comment': 'Testing mode: configuration was not changed!' - }) + if __opts__["test"] is True: + _ret.update( + {"result": None, "comment": "Testing mode: configuration was not changed!"} + ) return _ret # <---- Retrieve existing NTP peers and determine peers to be added/removed ---------------------------------------> @@ -244,37 +243,37 @@ def _check_diff_and_configure(fun_name, peers_servers, name='peers'): expected_config_change = False successfully_changed = True - comment = '' + comment = "" if list_to_set: - _set_fun = '_set_ntp_{what}'.format(what=name) + _set_fun = "_set_ntp_{what}".format(what=name) _set = _exec_fun(_set_fun, list_to_set) - if _set.get('result'): + if _set.get("result"): expected_config_change = True else: # something went wrong... successfully_changed = False - comment += 'Cannot set NTP {what}: {reason}'.format( - what=name, - reason=_set.get('comment') + comment += "Cannot set NTP {what}: {reason}".format( + what=name, reason=_set.get("comment") ) if list_to_delete: - _delete_fun = '_delete_ntp_{what}'.format(what=name) + _delete_fun = "_delete_ntp_{what}".format(what=name) _removed = _exec_fun(_delete_fun, list_to_delete) - if _removed.get('result'): + if _removed.get("result"): expected_config_change = True else: # something went wrong... successfully_changed = False - comment += 'Cannot remove NTP {what}: {reason}'.format( - what=name, - reason=_removed.get('comment') + comment += "Cannot remove NTP {what}: {reason}".format( + what=name, reason=_removed.get("comment") ) - _ret.update({ - 'successfully_changed': successfully_changed, - 'expected_config_change': expected_config_change, - 'comment': comment - }) + _ret.update( + { + "successfully_changed": successfully_changed, + "expected_config_change": expected_config_change, + "comment": comment, + } + ) return _ret @@ -286,7 +285,7 @@ def _check_diff_and_configure(fun_name, peers_servers, name='peers'): def managed(name, peers=None, servers=None): - ''' + """ Manages the configuration of NTP peers and servers on the device, as specified in the state SLS file. NTP entities not specified in these lists will be removed whilst entities not configured on the device will be set. @@ -330,22 +329,28 @@ def managed(name, peers=None, servers=None): } } } - ''' + """ ret = _default_ret(name) - result = ret.get('result', False) - comment = ret.get('comment', '') - changes = ret.get('changes', {}) + result = ret.get("result", False) + comment = ret.get("comment", "") + changes = ret.get("changes", {}) - if not(isinstance(peers, list) or isinstance(servers, list)): # none of the is a list + if not ( + isinstance(peers, list) or isinstance(servers, list) + ): # none of the is a list return ret # just exit if isinstance(peers, list) and not _check(peers): # check and clean peers - ret['comment'] = 'NTP peers must be a list of valid IP Addresses or Domain Names' + ret[ + "comment" + ] = "NTP peers must be a list of valid IP Addresses or Domain Names" return ret if isinstance(servers, list) and not _check(servers): # check and clean servers - ret['comment'] = 'NTP servers must be a list of valid IP Addresses or Domain Names' + ret[ + "comment" + ] = "NTP servers must be a list of valid IP Addresses or Domain Names" return ret # ----- Retrieve existing NTP peers and determine peers to be added/removed ---------------------------------------> @@ -354,40 +359,41 @@ def managed(name, peers=None, servers=None): expected_config_change = False if isinstance(peers, list): - _peers_ret = _check_diff_and_configure(name, peers, name='peers') - expected_config_change = _peers_ret.get('expected_config_change', False) - successfully_changed = _peers_ret.get('successfully_changed', True) - result = result and _peers_ret.get('result', False) - comment += ('\n' + _peers_ret.get('comment', '')) - _changed_peers = _peers_ret.get('changes', {}) + _peers_ret = _check_diff_and_configure(name, peers, name="peers") + expected_config_change = _peers_ret.get("expected_config_change", False) + successfully_changed = _peers_ret.get("successfully_changed", True) + result = result and _peers_ret.get("result", False) + comment += "\n" + _peers_ret.get("comment", "") + _changed_peers = _peers_ret.get("changes", {}) if _changed_peers: - changes['peers'] = _changed_peers + changes["peers"] = _changed_peers if isinstance(servers, list): - _servers_ret = _check_diff_and_configure(name, servers, name='servers') - expected_config_change = expected_config_change or _servers_ret.get('expected_config_change', False) - successfully_changed = successfully_changed and _servers_ret.get('successfully_changed', True) - result = result and _servers_ret.get('result', False) - comment += ('\n' + _servers_ret.get('comment', '')) - _changed_servers = _servers_ret.get('changes', {}) + _servers_ret = _check_diff_and_configure(name, servers, name="servers") + expected_config_change = expected_config_change or _servers_ret.get( + "expected_config_change", False + ) + successfully_changed = successfully_changed and _servers_ret.get( + "successfully_changed", True + ) + result = result and _servers_ret.get("result", False) + comment += "\n" + _servers_ret.get("comment", "") + _changed_servers = _servers_ret.get("changes", {}) if _changed_servers: - changes['servers'] = _changed_servers + changes["servers"] = _changed_servers - ret.update({ - 'changes': changes - }) + ret.update({"changes": changes}) if not (changes or expected_config_change): - ret.update({ - 'result': True, - 'comment': 'Device configured properly.' - }) + ret.update({"result": True, "comment": "Device configured properly."}) return ret - if __opts__['test'] is True: - ret.update({ - 'result': None, - 'comment': 'This is in testing mode, the device configuration was not changed!' - }) + if __opts__["test"] is True: + ret.update( + { + "result": None, + "comment": "This is in testing mode, the device configuration was not changed!", + } + ) return ret # <---- Call _set_ntp_peers and _delete_ntp_peers as needed -------------------------------------------------------- @@ -395,15 +401,12 @@ def managed(name, peers=None, servers=None): # ----- Try to commit changes -------------------------------------------------------------------------------------> if expected_config_change: # commit only in case there's something to update - config_result, config_comment = __salt__['net.config_control']() + config_result, config_comment = __salt__["net.config_control"]() result = config_result and successfully_changed comment += config_comment # <---- Try to commit changes -------------------------------------------------------------------------------------- - ret.update({ - 'result': result, - 'comment': comment - }) + ret.update({"result": result, "comment": comment}) return ret diff --git a/salt/states/netsnmp.py b/salt/states/netsnmp.py index 2833c24175b..485b5bb9010 100644 --- a/salt/states/netsnmp.py +++ b/salt/states/netsnmp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Network SNMP ============ @@ -16,30 +16,33 @@ Dependencies - :mod:`napalm snmp management module (salt.modules.napalm_snmp) <salt.modules.napalm_snmp>` .. versionadded: 2016.11.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging -log = logging.getLogger(__name__) -# salt lib -from salt.ext import six # import NAPALM utils import salt.utils.json import salt.utils.napalm +# salt lib +from salt.ext import six + +log = logging.getLogger(__name__) + + # ---------------------------------------------------------------------------------------------------------------------- # state properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'netsnmp' +__virtualname__ = "netsnmp" _COMMUNITY_MODE_MAP = { - 'read-only': 'ro', - 'readonly': 'ro', - 'read-write': 'rw', - 'write': 'rw' + "read-only": "ro", + "readonly": "ro", + "read-write": "rw", + "write": "rw", } # ---------------------------------------------------------------------------------------------------------------------- @@ -52,11 +55,12 @@ _COMMUNITY_MODE_MAP = { def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -64,18 +68,18 @@ def __virtual__(): def _ordered_dict_to_dict(config): - ''' + """ Forced the datatype to dict, in case OrderedDict is used. - ''' + """ return salt.utils.json.loads(salt.utils.json.dumps(config)) def _expand_config(config, defaults): - ''' + """ Completed the values of the expected config for the edge cases with the default values. - ''' + """ defaults.update(config) return defaults @@ -83,77 +87,75 @@ def _expand_config(config, defaults): def _valid_dict(dic): - ''' + """ Valid dictionary? - ''' + """ return isinstance(dic, dict) and len(dic) > 0 def _valid_str(value): - ''' + """ Valid str? - ''' + """ return isinstance(value, six.string_types) and len(value) > 0 def _community_defaults(): - ''' + """ Returns the default values of a community. - ''' + """ - return { - 'mode': 'ro' - } + return {"mode": "ro"} def _clear_community_details(community_details): - ''' + """ Clears community details. - ''' + """ - for key in ['acl', 'mode']: + for key in ["acl", "mode"]: _str_elem(community_details, key) - _mode = community_details.get['mode'] = community_details.get('mode').lower() + _mode = community_details.get["mode"] = community_details.get("mode").lower() if _mode in _COMMUNITY_MODE_MAP.keys(): - community_details['mode'] = _COMMUNITY_MODE_MAP.get(_mode) + community_details["mode"] = _COMMUNITY_MODE_MAP.get(_mode) - if community_details['mode'] not in ['ro', 'rw']: - community_details['mode'] = 'ro' # default is read-only + if community_details["mode"] not in ["ro", "rw"]: + community_details["mode"] = "ro" # default is read-only return community_details def _str_elem(config, key): - ''' + """ Re-adds the value of a specific key in the dict, only in case of valid str value. - ''' + """ - _value = config.pop(key, '') + _value = config.pop(key, "") if _valid_str(_value): config[key] = _value def _check_config(config): - ''' + """ Checks the desired config and clears interesting details. - ''' + """ if not _valid_dict(config): - return True, '' + return True, "" - _community = config.get('community') + _community = config.get("community") _community_tmp = {} if not _community: - return False, 'Must specify at least a community.' + return False, "Must specify at least a community." if _valid_str(_community): _community_tmp[_community] = _community_defaults() elif isinstance(_community, list): @@ -167,7 +169,9 @@ def _check_config(config): # list of dicts for _comm_name, _comm_details in six.iteritems(_comm): if _valid_str(_comm_name): - _community_tmp[_comm_name] = _clear_community_details(_comm_details) + _community_tmp[_comm_name] = _clear_community_details( + _comm_details + ) elif _valid_dict(_community): # directly as dict of communities # recommended way... @@ -175,34 +179,34 @@ def _check_config(config): if _valid_str(_comm_name): _community_tmp[_comm_name] = _clear_community_details(_comm_details) else: - return False, 'Please specify a community or a list of communities.' + return False, "Please specify a community or a list of communities." if not _valid_dict(_community_tmp): - return False, 'Please specify at least a valid community!' + return False, "Please specify at least a valid community!" - config['community'] = _community_tmp + config["community"] = _community_tmp - for key in ['location', 'contact', 'chassis_id']: + for key in ["location", "contact", "chassis_id"]: # not mandatory, but should be here only if valid _str_elem(config, key) - return True, '' + return True, "" def _retrieve_device_config(): - ''' + """ Retrieves the SNMP config from the device. - ''' + """ - return __salt__['snmp.config']() + return __salt__["snmp.config"]() def _create_diff_action(diff, diff_key, key, value): - ''' + """ DRY to build diff parts (added, removed, updated). - ''' + """ if diff_key not in diff.keys(): diff[diff_key] = {} @@ -211,78 +215,70 @@ def _create_diff_action(diff, diff_key, key, value): def _create_diff(diff, fun, key, prev, curr): - ''' + """ Builds the diff dictionary. - ''' + """ if not fun(prev): - _create_diff_action(diff, 'added', key, curr) + _create_diff_action(diff, "added", key, curr) elif fun(prev) and not fun(curr): - _create_diff_action(diff, 'removed', key, prev) + _create_diff_action(diff, "removed", key, prev) elif not fun(curr): - _create_diff_action(diff, 'updated', key, curr) + _create_diff_action(diff, "updated", key, curr) def _compute_diff(existing, expected): - ''' + """ Computes the differences between the existing and the expected SNMP config. - ''' + """ diff = {} - for key in ['location', 'contact', 'chassis_id']: + for key in ["location", "contact", "chassis_id"]: if existing.get(key) != expected.get(key): - _create_diff(diff, - _valid_str, - key, - existing.get(key), - expected.get(key)) + _create_diff(diff, _valid_str, key, existing.get(key), expected.get(key)) - for key in ['community']: # for the moment only onen + for key in ["community"]: # for the moment only onen if existing.get(key) != expected.get(key): - _create_diff(diff, - _valid_dict, - key, - existing.get(key), - expected.get(key)) + _create_diff(diff, _valid_dict, key, existing.get(key), expected.get(key)) return diff def _configure(changes): - ''' + """ Calls the configuration template to apply the configuration changes on the device. - ''' + """ cfgred = True reasons = [] - fun = 'update_config' + fun = "update_config" - for key in ['added', 'updated', 'removed']: + for key in ["added", "updated", "removed"]: _updated_changes = changes.get(key, {}) if not _updated_changes: continue - _location = _updated_changes.get('location', '') - _contact = _updated_changes.get('contact', '') - _community = _updated_changes.get('community', {}) - _chassis_id = _updated_changes.get('chassis_id', '') - if key == 'removed': - fun = 'remove_config' - _ret = __salt__['snmp.{fun}'.format(fun=fun)](location=_location, - contact=_contact, - community=_community, - chassis_id=_chassis_id, - commit=False) - cfgred = cfgred and _ret.get('result') - if not _ret.get('result') and _ret.get('comment'): - reasons.append(_ret.get('comment')) + _location = _updated_changes.get("location", "") + _contact = _updated_changes.get("contact", "") + _community = _updated_changes.get("community", {}) + _chassis_id = _updated_changes.get("chassis_id", "") + if key == "removed": + fun = "remove_config" + _ret = __salt__["snmp.{fun}".format(fun=fun)]( + location=_location, + contact=_contact, + community=_community, + chassis_id=_chassis_id, + commit=False, + ) + cfgred = cfgred and _ret.get("result") + if not _ret.get("result") and _ret.get("comment"): + reasons.append(_ret.get("comment")) + + return {"result": cfgred, "comment": "\n".join(reasons) if reasons else ""} - return { - 'result': cfgred, - 'comment': '\n'.join(reasons) if reasons else '' - } # ---------------------------------------------------------------------------------------------------------------------- # callable functions @@ -291,7 +287,7 @@ def _configure(changes): def managed(name, config=None, defaults=None): - ''' + """ Configures the SNMP on the device as specified in the SLS file. SLS Example: @@ -336,18 +332,13 @@ def managed(name, config=None, defaults=None): ------------ Total states run: 1 Total run time: 920.466 ms - ''' + """ result = False - comment = '' + comment = "" changes = {} - ret = { - 'name': name, - 'changes': changes, - 'result': result, - 'comment': comment - } + ret = {"name": name, "changes": changes, "result": result, "comment": comment} # make sure we're working only with dict config = _ordered_dict_to_dict(config) @@ -355,45 +346,41 @@ def managed(name, config=None, defaults=None): expected_config = _expand_config(config, defaults) if not isinstance(expected_config, dict): - ret['comment'] = 'User provided an empty SNMP config!' + ret["comment"] = "User provided an empty SNMP config!" return ret valid, message = _check_config(expected_config) if not valid: # check and clean - ret['comment'] = 'Please provide a valid configuration: {error}'.format(error=message) + ret["comment"] = "Please provide a valid configuration: {error}".format( + error=message + ) return ret # ----- Retrieve existing users configuration and determine differences -------------------------------------------> _device_config = _retrieve_device_config() - if not _device_config.get('result'): - ret['comment'] = 'Cannot retrieve SNMP config from the device: {reason}'.format( - reason=_device_config.get('comment') + if not _device_config.get("result"): + ret["comment"] = "Cannot retrieve SNMP config from the device: {reason}".format( + reason=_device_config.get("comment") ) return ret - device_config = _device_config.get('out', {}) + device_config = _device_config.get("out", {}) if device_config == expected_config: - ret.update({ - 'comment': 'SNMP already configured as needed.', - 'result': True - }) + ret.update({"comment": "SNMP already configured as needed.", "result": True}) return ret diff = _compute_diff(device_config, expected_config) changes.update(diff) - ret.update({ - 'changes': changes - }) + ret.update({"changes": changes}) - if __opts__['test'] is True: - ret.update({ - 'result': None, - 'comment': 'Testing mode: configuration was not changed!' - }) + if __opts__["test"] is True: + ret.update( + {"result": None, "comment": "Testing mode: configuration was not changed!"} + ) return ret # <---- Retrieve existing NTP peers and determine peers to be added/removed ---------------------------------------> @@ -405,22 +392,22 @@ def managed(name, config=None, defaults=None): if diff: _configured = _configure(diff) - if _configured.get('result'): + if _configured.get("result"): expected_config_change = True else: # something went wrong... result = False - comment = 'Cannot push new SNMP config: \n{reason}'.format( - reason=_configured.get('comment') - ) + comment + comment = ( + "Cannot push new SNMP config: \n{reason}".format( + reason=_configured.get("comment") + ) + + comment + ) if expected_config_change: - result, comment = __salt__['net.config_control']() + result, comment = __salt__["net.config_control"]() # <---- Call _set_users and _delete_users as needed -------------------------------------------------------- - ret.update({ - 'result': result, - 'comment': comment - }) + ret.update({"result": result, "comment": comment}) return ret diff --git a/salt/states/netusers.py b/salt/states/netusers.py index 74e2e5346ed..8bf9e888630 100644 --- a/salt/states/netusers.py +++ b/salt/states/netusers.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Network Users ============= @@ -16,27 +16,30 @@ Dependencies - :mod:`Users configuration management module <salt.modules.napalm_users>` .. versionadded:: 2016.11.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging -log = logging.getLogger(__name__) # Python std lib from copy import deepcopy -# salt lib -from salt.ext import six # import NAPALM utils import salt.utils.json import salt.utils.napalm +# salt lib +from salt.ext import six + +log = logging.getLogger(__name__) + + # ---------------------------------------------------------------------------------------------------------------------- # state properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'netusers' +__virtualname__ = "netusers" # ---------------------------------------------------------------------------------------------------------------------- # global variables @@ -48,11 +51,12 @@ __virtualname__ = 'netusers' def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -60,21 +64,21 @@ def __virtual__(): def _retrieve_users(): - '''Retrieves configured users''' + """Retrieves configured users""" - return __salt__['users.config']() + return __salt__["users.config"]() def _ordered_dict_to_dict(probes): - '''.''' + """.""" return salt.utils.json.loads(salt.utils.json.dumps(probes)) def _expand_users(device_users, common_users): - '''Creates a longer list of accepted users on the device.''' + """Creates a longer list of accepted users on the device.""" expected_users = deepcopy(common_users) expected_users.update(device_users) @@ -84,32 +88,33 @@ def _expand_users(device_users, common_users): def _check_users(users): - '''Checks if the input dictionary of users is valid.''' + """Checks if the input dictionary of users is valid.""" - messg = '' + messg = "" valid = True for user, user_details in six.iteritems(users): if not user_details: valid = False - messg += 'Please provide details for username {user}.\n'.format(user=user) + messg += "Please provide details for username {user}.\n".format(user=user) continue - if not (isinstance(user_details.get('level'), int) or 0 <= user_details.get('level') <= 15): + if not ( + isinstance(user_details.get("level"), int) + or 0 <= user_details.get("level") <= 15 + ): # warn! - messg += 'Level must be a integer between 0 and 15 for username {user}. Will assume 0.\n'.format(user=user) + messg += "Level must be a integer between 0 and 15 for username {user}. Will assume 0.\n".format( + user=user + ) return valid, messg def _compute_diff(configured, expected): - '''Computes the differences between the actual config and the expected config''' + """Computes the differences between the actual config and the expected config""" - diff = { - 'add': {}, - 'update': {}, - 'remove': {} - } + diff = {"add": {}, "update": {}, "remove": {}} configured_users = set(configured.keys()) expected_users = set(expected.keys()) @@ -132,34 +137,31 @@ def _compute_diff(configured, expected): if user_configuration.get(field) != field_value: update[username][field] = field_value - diff.update({ - 'add': add, - 'update': update, - 'remove': remove - }) + diff.update({"add": add, "update": update, "remove": remove}) return diff def _set_users(users): - '''Calls users.set_users.''' + """Calls users.set_users.""" - return __salt__['users.set_users'](users, commit=False) + return __salt__["users.set_users"](users, commit=False) def _update_users(users): - '''Calls users.set_users.''' + """Calls users.set_users.""" - return __salt__['users.set_users'](users, commit=False) + return __salt__["users.set_users"](users, commit=False) def _delete_users(users): - '''Calls users.delete_users.''' + """Calls users.delete_users.""" + + return __salt__["users.delete_users"](users, commit=False) - return __salt__['users.delete_users'](users, commit=False) # ---------------------------------------------------------------------------------------------------------------------- # callable functions @@ -168,7 +170,7 @@ def _delete_users(users): def managed(name, users=None, defaults=None): - ''' + """ Manages the configuration of the users on the device, as specified in the state SLS file. Users not defined in that file will be remove whilst users not configured on the device, will be added. @@ -320,18 +322,13 @@ def managed(name, users=None, defaults=None): ------------ Total states run: 1 Total run time: 1.220 s - ''' + """ result = False - comment = '' + comment = "" changes = {} - ret = { - 'name': name, - 'changes': changes, - 'result': result, - 'comment': comment - } + ret = {"name": name, "changes": changes, "result": result, "comment": comment} users = _ordered_dict_to_dict(users) defaults = _ordered_dict_to_dict(defaults) @@ -340,48 +337,44 @@ def managed(name, users=None, defaults=None): valid, message = _check_users(expected_users) if not valid: # check and clean - ret['comment'] = 'Please provide a valid configuration: {error}'.format(error=message) + ret["comment"] = "Please provide a valid configuration: {error}".format( + error=message + ) return ret # ----- Retrieve existing users configuration and determine differences -------------------------------------------> users_output = _retrieve_users() - if not users_output.get('result'): - ret['comment'] = 'Cannot retrieve users from the device: {reason}'.format( - reason=users_output.get('comment') + if not users_output.get("result"): + ret["comment"] = "Cannot retrieve users from the device: {reason}".format( + reason=users_output.get("comment") ) return ret - configured_users = users_output.get('out', {}) + configured_users = users_output.get("out", {}) if configured_users == expected_users: - ret.update({ - 'comment': 'Users already configured as needed.', - 'result': True - }) + ret.update({"comment": "Users already configured as needed.", "result": True}) return ret diff = _compute_diff(configured_users, expected_users) - users_to_add = diff.get('add', {}) - users_to_update = diff.get('update', {}) - users_to_remove = diff.get('remove', {}) + users_to_add = diff.get("add", {}) + users_to_update = diff.get("update", {}) + users_to_remove = diff.get("remove", {}) changes = { - 'added': users_to_add, - 'updated': users_to_update, - 'removed': users_to_remove + "added": users_to_add, + "updated": users_to_update, + "removed": users_to_remove, } - ret.update({ - 'changes': changes - }) + ret.update({"changes": changes}) - if __opts__['test'] is True: - ret.update({ - 'result': None, - 'comment': 'Testing mode: configuration was not changed!' - }) + if __opts__["test"] is True: + ret.update( + {"result": None, "comment": "Testing mode: configuration was not changed!"} + ) return ret # <---- Retrieve existing NTP peers and determine peers to be added/removed ---------------------------------------> @@ -393,32 +386,32 @@ def managed(name, users=None, defaults=None): if users_to_add: _set = _set_users(users_to_add) - if _set.get('result'): + if _set.get("result"): expected_config_change = True else: # something went wrong... successfully_changed = False - comment += 'Cannot configure new users: {reason}'.format( - reason=_set.get('comment') + comment += "Cannot configure new users: {reason}".format( + reason=_set.get("comment") ) if users_to_update: _update = _update_users(users_to_update) - if _update.get('result'): + if _update.get("result"): expected_config_change = True else: # something went wrong... successfully_changed = False - comment += 'Cannot update the users configuration: {reason}'.format( - reason=_update.get('comment') + comment += "Cannot update the users configuration: {reason}".format( + reason=_update.get("comment") ) if users_to_remove: _delete = _delete_users(users_to_remove) - if _delete.get('result'): + if _delete.get("result"): expected_config_change = True else: # something went wrong... successfully_changed = False - comment += 'Cannot remove users: {reason}'.format( - reason=_delete.get('comment') + comment += "Cannot remove users: {reason}".format( + reason=_delete.get("comment") ) # <---- Call _set_users and _delete_users as needed ---------------------------------------------------------------- @@ -426,18 +419,15 @@ def managed(name, users=None, defaults=None): # ----- Try to commit changes -------------------------------------------------------------------------------------> if expected_config_change and successfully_changed: - config_result, config_comment = __salt__['net.config_control']() + config_result, config_comment = __salt__["net.config_control"]() result = config_result comment += config_comment # <---- Try to commit changes -------------------------------------------------------------------------------------- if expected_config_change and result and not comment: - comment = 'Configuration updated!' + comment = "Configuration updated!" - ret.update({ - 'result': result, - 'comment': comment - }) + ret.update({"result": result, "comment": comment}) return ret diff --git a/salt/states/network.py b/salt/states/network.py index 3d8cc46a1c4..87e9fe87bfb 100644 --- a/salt/states/network.py +++ b/salt/states/network.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configuration of network interfaces =================================== @@ -327,37 +327,41 @@ all interfaces are ignored unless specified. When managing bridged interfaces on a Debian or Ubuntu based system, the ports argument is required. Red Hat systems will ignore the argument. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import difflib +# Set up logging +import logging + +import salt.loader + # Import Salt libs import salt.utils.network import salt.utils.platform -import salt.loader # Import 3rd party libs from salt.ext import six -# Set up logging -import logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Confine this module to non-Windows systems with the required execution module available. - ''' - if not salt.utils.platform.is_windows() and 'ip.get_interface' in __salt__: + """ + if salt.utils.platform.is_windows(): + return (False, "Only supported on non-Windows OSs") + if "ip.get_interface" in __salt__: return True - return False + return (False, "ip module could not be loaded") def managed(name, type, enabled=True, **kwargs): - ''' + """ Ensure that the named interface is configured properly. name @@ -372,110 +376,109 @@ def managed(name, type, enabled=True, **kwargs): kwargs The IP parameters for this interface. - ''' + """ # For this function we are purposefully overwriting a bif # to enhance the user experience. This does not look like # it will cause a problem. Just giving a heads up in case # it does create a problem. ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Interface {0} is up to date.'.format(name), + "name": name, + "changes": {}, + "result": True, + "comment": "Interface {0} is up to date.".format(name), } - if 'test' not in kwargs: - kwargs['test'] = __opts__.get('test', False) + if "test" not in kwargs: + kwargs["test"] = __opts__.get("test", False) # set ranged status apply_ranged_setting = False # Build interface try: - old = __salt__['ip.get_interface'](name) - new = __salt__['ip.build_interface'](name, type, enabled, **kwargs) - if kwargs['test']: + old = __salt__["ip.get_interface"](name) + new = __salt__["ip.build_interface"](name, type, enabled, **kwargs) + if kwargs["test"]: if old == new: pass if not old and new: - ret['result'] = None - ret['comment'] = 'Interface {0} is set to be ' \ - 'added.'.format(name) + ret["result"] = None + ret["comment"] = "Interface {0} is set to be " "added.".format(name) elif old != new: - diff = difflib.unified_diff(old, new, lineterm='') - ret['result'] = None - ret['comment'] = 'Interface {0} is set to be ' \ - 'updated:\n{1}'.format(name, '\n'.join(diff)) + diff = difflib.unified_diff(old, new, lineterm="") + ret["result"] = None + ret["comment"] = "Interface {0} is set to be " "updated:\n{1}".format( + name, "\n".join(diff) + ) else: if not old and new: - ret['comment'] = 'Interface {0} ' \ - 'added.'.format(name) - ret['changes']['interface'] = 'Added network interface.' + ret["comment"] = "Interface {0} " "added.".format(name) + ret["changes"]["interface"] = "Added network interface." apply_ranged_setting = True elif old != new: - diff = difflib.unified_diff(old, new, lineterm='') - ret['comment'] = 'Interface {0} ' \ - 'updated.'.format(name) - ret['changes']['interface'] = '\n'.join(diff) + diff = difflib.unified_diff(old, new, lineterm="") + ret["comment"] = "Interface {0} " "updated.".format(name) + ret["changes"]["interface"] = "\n".join(diff) apply_ranged_setting = True except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret # Debian based system can have a type of source # in the interfaces file, we don't ifup or ifdown it - if type == 'source': + if type == "source": return ret # Setup up bond modprobe script if required - if type == 'bond': + if type == "bond": try: - old = __salt__['ip.get_bond'](name) - new = __salt__['ip.build_bond'](name, **kwargs) - if kwargs['test']: + old = __salt__["ip.get_bond"](name) + new = __salt__["ip.build_bond"](name, **kwargs) + if kwargs["test"]: if not old and new: - ret['result'] = None - ret['comment'] = 'Bond interface {0} is set to be ' \ - 'added.'.format(name) + ret["result"] = None + ret["comment"] = "Bond interface {0} is set to be " "added.".format( + name + ) elif old != new: - diff = difflib.unified_diff(old, new, lineterm='') - ret['result'] = None - ret['comment'] = 'Bond interface {0} is set to be ' \ - 'updated:\n{1}'.format(name, '\n'.join(diff)) + diff = difflib.unified_diff(old, new, lineterm="") + ret["result"] = None + ret["comment"] = ( + "Bond interface {0} is set to be " + "updated:\n{1}".format(name, "\n".join(diff)) + ) else: if not old and new: - ret['comment'] = 'Bond interface {0} ' \ - 'added.'.format(name) - ret['changes']['bond'] = 'Added bond {0}.'.format(name) + ret["comment"] = "Bond interface {0} " "added.".format(name) + ret["changes"]["bond"] = "Added bond {0}.".format(name) apply_ranged_setting = True elif old != new: - diff = difflib.unified_diff(old, new, lineterm='') - ret['comment'] = 'Bond interface {0} ' \ - 'updated.'.format(name) - ret['changes']['bond'] = '\n'.join(diff) + diff = difflib.unified_diff(old, new, lineterm="") + ret["comment"] = "Bond interface {0} " "updated.".format(name) + ret["changes"]["bond"] = "\n".join(diff) apply_ranged_setting = True except AttributeError as error: - #TODO Add a way of reversing the interface changes. - ret['result'] = False - ret['comment'] = six.text_type(error) + # TODO Add a way of reversing the interface changes. + ret["result"] = False + ret["comment"] = six.text_type(error) return ret - if kwargs['test']: + if kwargs["test"]: return ret # For Redhat/Centos ranged network if "range" in name: if apply_ranged_setting: try: - ret['result'] = __salt__['service.restart']('network') - ret['comment'] = "network restarted for change of ranged interfaces" + ret["result"] = __salt__["service.restart"]("network") + ret["comment"] = "network restarted for change of ranged interfaces" return ret except Exception as error: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret - ret['result'] = True - ret['comment'] = "no change, passing it" + ret["result"] = True + ret["comment"] = "no change, passing it" return ret # Bring up/shutdown interface @@ -484,61 +487,70 @@ def managed(name, type, enabled=True, **kwargs): interfaces = salt.utils.network.interfaces() interface_status = False if name in interfaces: - interface_status = interfaces[name].get('up') + interface_status = interfaces[name].get("up") else: for iface in interfaces: - if 'secondary' in interfaces[iface]: - for second in interfaces[iface]['secondary']: - if second.get('label', '') == name: + if "secondary" in interfaces[iface]: + for second in interfaces[iface]["secondary"]: + if second.get("label", "") == name: interface_status = True if enabled: - if 'noifupdown' not in kwargs: + if "noifupdown" not in kwargs: if interface_status: - if ret['changes']: + if ret["changes"]: # Interface should restart to validate if it's up - __salt__['ip.down'](name, type) - __salt__['ip.up'](name, type) - ret['changes']['status'] = 'Interface {0} restart to validate'.format(name) + __salt__["ip.down"](name, type) + __salt__["ip.up"](name, type) + ret["changes"][ + "status" + ] = "Interface {0} restart to validate".format(name) else: - __salt__['ip.up'](name, type) - ret['changes']['status'] = 'Interface {0} is up'.format(name) + __salt__["ip.up"](name, type) + ret["changes"]["status"] = "Interface {0} is up".format(name) else: - if 'noifupdown' not in kwargs: + if "noifupdown" not in kwargs: if interface_status: - __salt__['ip.down'](name, type) - ret['changes']['status'] = 'Interface {0} down'.format(name) + __salt__["ip.down"](name, type) + ret["changes"]["status"] = "Interface {0} down".format(name) except Exception as error: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret # Try to enslave bonding interfaces after master was created - if type == 'bond' and 'noifupdown' not in kwargs: + if type == "bond" and "noifupdown" not in kwargs: - if 'slaves' in kwargs and kwargs['slaves']: + if "slaves" in kwargs and kwargs["slaves"]: # Check that there are new slaves for this master - present_slaves = __salt__['cmd.run']( - ['cat', '/sys/class/net/{0}/bonding/slaves'.format(name)]).split() - desired_slaves = kwargs['slaves'].split() + present_slaves = __salt__["cmd.run"]( + ["cat", "/sys/class/net/{0}/bonding/slaves".format(name)] + ).split() + desired_slaves = kwargs["slaves"].split() missing_slaves = set(desired_slaves) - set(present_slaves) # Enslave only slaves missing in master if missing_slaves: - ifenslave_path = __salt__['cmd.run'](['which', 'ifenslave']).strip() + ifenslave_path = __salt__["cmd.run"](["which", "ifenslave"]).strip() if ifenslave_path: - log.info("Adding slaves '%s' to the master %s", - ' '.join(missing_slaves), name) + log.info( + "Adding slaves '%s' to the master %s", + " ".join(missing_slaves), + name, + ) cmd = [ifenslave_path, name] + list(missing_slaves) - __salt__['cmd.run'](cmd, python_shell=False) + __salt__["cmd.run"](cmd, python_shell=False) else: log.error("Command 'ifenslave' not found") - ret['changes']['enslave'] = ( - "Added slaves '{0}' to master '{1}'" - .format(' '.join(missing_slaves), name)) + ret["changes"]["enslave"] = "Added slaves '{0}' to master '{1}'".format( + " ".join(missing_slaves), name + ) else: - log.info("All slaves '%s' are already added to the master %s" - ", no actions required", - ' '.join(missing_slaves), name) + log.info( + "All slaves '%s' are already added to the master %s" + ", no actions required", + " ".join(missing_slaves), + name, + ) if enabled and interface_status: # Interface was restarted, return @@ -547,12 +559,12 @@ def managed(name, type, enabled=True, **kwargs): # TODO: create saltutil.refresh_grains that fires events to the minion daemon grains_info = salt.loader.grains(__opts__, True) __grains__.update(grains_info) - __salt__['saltutil.refresh_modules']() + __salt__["saltutil.refresh_modules"]() return ret def routes(name, **kwargs): - ''' + """ Manage network interface static routes. name @@ -560,62 +572,68 @@ def routes(name, **kwargs): kwargs Named routes - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Interface {0} routes are up to date.'.format(name), + "name": name, + "changes": {}, + "result": True, + "comment": "Interface {0} routes are up to date.".format(name), } apply_routes = False - if 'test' not in kwargs: - kwargs['test'] = __opts__.get('test', False) + if "test" not in kwargs: + kwargs["test"] = __opts__.get("test", False) # Build interface routes try: - old = __salt__['ip.get_routes'](name) - new = __salt__['ip.build_routes'](name, **kwargs) - if kwargs['test']: + old = __salt__["ip.get_routes"](name) + new = __salt__["ip.build_routes"](name, **kwargs) + if kwargs["test"]: if old == new: return ret if not old and new: - ret['result'] = None - ret['comment'] = 'Interface {0} routes are set to be added.'.format(name) + ret["result"] = None + ret["comment"] = "Interface {0} routes are set to be added.".format( + name + ) return ret elif old != new: - diff = difflib.unified_diff(old, new, lineterm='') - ret['result'] = None - ret['comment'] = 'Interface {0} routes are set to be ' \ - 'updated:\n{1}'.format(name, '\n'.join(diff)) + diff = difflib.unified_diff(old, new, lineterm="") + ret["result"] = None + ret["comment"] = ( + "Interface {0} routes are set to be " + "updated:\n{1}".format(name, "\n".join(diff)) + ) return ret if not old and new: apply_routes = True - ret['comment'] = 'Interface {0} routes added.'.format(name) - ret['changes']['network_routes'] = 'Added interface {0} routes.'.format(name) + ret["comment"] = "Interface {0} routes added.".format(name) + ret["changes"]["network_routes"] = "Added interface {0} routes.".format( + name + ) elif old != new: - diff = difflib.unified_diff(old, new, lineterm='') + diff = difflib.unified_diff(old, new, lineterm="") apply_routes = True - ret['comment'] = 'Interface {0} routes updated.'.format(name) - ret['changes']['network_routes'] = '\n'.join(diff) + ret["comment"] = "Interface {0} routes updated.".format(name) + ret["changes"]["network_routes"] = "\n".join(diff) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret # Apply interface routes if apply_routes: try: - __salt__['ip.apply_network_settings'](**kwargs) + __salt__["ip.apply_network_settings"](**kwargs) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret return ret def system(name, **kwargs): - ''' + """ Ensure that global network settings are configured properly. name @@ -624,55 +642,57 @@ def system(name, **kwargs): kwargs The global parameters for the system. - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Global network settings are up to date.', + "name": name, + "changes": {}, + "result": True, + "comment": "Global network settings are up to date.", } apply_net_settings = False - kwargs['test'] = __opts__['test'] + kwargs["test"] = __opts__["test"] # Build global network settings try: - old = __salt__['ip.get_network_settings']() - new = __salt__['ip.build_network_settings'](**kwargs) - if __opts__['test']: + old = __salt__["ip.get_network_settings"]() + new = __salt__["ip.build_network_settings"](**kwargs) + if __opts__["test"]: if old == new: return ret if not old and new: - ret['result'] = None - ret['comment'] = 'Global network settings are set to be added.' + ret["result"] = None + ret["comment"] = "Global network settings are set to be added." return ret elif old != new: - diff = difflib.unified_diff(old, new, lineterm='') - ret['result'] = None - ret['comment'] = 'Global network settings are set to be ' \ - 'updated:\n{0}'.format('\n'.join(diff)) + diff = difflib.unified_diff(old, new, lineterm="") + ret["result"] = None + ret["comment"] = ( + "Global network settings are set to be " + "updated:\n{0}".format("\n".join(diff)) + ) return ret if not old and new: apply_net_settings = True - ret['changes']['network_settings'] = 'Added global network settings.' + ret["changes"]["network_settings"] = "Added global network settings." elif old != new: - diff = difflib.unified_diff(old, new, lineterm='') + diff = difflib.unified_diff(old, new, lineterm="") apply_net_settings = True - ret['changes']['network_settings'] = '\n'.join(diff) + ret["changes"]["network_settings"] = "\n".join(diff) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret except KeyError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret # Apply global network settings if apply_net_settings: try: - __salt__['ip.apply_network_settings'](**kwargs) + __salt__["ip.apply_network_settings"](**kwargs) except AttributeError as error: - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["result"] = False + ret["comment"] = six.text_type(error) return ret return ret diff --git a/salt/states/neutron_network.py b/salt/states/neutron_network.py index 191207e8260..86a2942753b 100644 --- a/salt/states/neutron_network.py +++ b/salt/states/neutron_network.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Neutron Networks ========================================= @@ -27,22 +27,25 @@ Example States - shared: False - external: False - project: project1 -''' +""" from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'neutron_network' +__virtualname__ = "neutron_network" def __virtual__(): - if 'neutronng.list_networks' in __salt__: + if "neutronng.list_networks" in __salt__: return __virtualname__ - return (False, 'The neutronng execution module failed to load:\ - shade python module is not available') + return ( + False, + "The neutronng execution module failed to load:\ + shade python module is not available", + ) def present(name, auth=None, **kwargs): - ''' + """ Ensure a network exists and is up-to-date name @@ -66,96 +69,92 @@ def present(name, auth=None, **kwargs): - physical_network: provider - network_type: vlan - segmentation_id: (vlan id) - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['neutronng.setup_clouds'](auth) + __salt__["neutronng.setup_clouds"](auth) - kwargs['name'] = name - network = __salt__['neutronng.network_get'](name=name) + kwargs["name"] = name + network = __salt__["neutronng.network_get"](name=name) if network is None: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Network will be created.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Network will be created." return ret - if 'vlan' in kwargs: - kwargs['provider'] = {"physical_network": "provider", - "network_type": "vlan", - "segmentation_id": kwargs['vlan']} - del kwargs['vlan'] + if "vlan" in kwargs: + kwargs["provider"] = { + "physical_network": "provider", + "network_type": "vlan", + "segmentation_id": kwargs["vlan"], + } + del kwargs["vlan"] - if 'project' in kwargs: - projectname = kwargs['project'] - project = __salt__['keystoneng.project_get'](name=projectname) + if "project" in kwargs: + projectname = kwargs["project"] + project = __salt__["keystoneng.project_get"](name=projectname) if project: - kwargs['project_id'] = project.id - del kwargs['project'] + kwargs["project_id"] = project.id + del kwargs["project"] else: - ret['result'] = False - ret['comment'] = "Project:{} not found.".format(projectname) + ret["result"] = False + ret["comment"] = "Project:{} not found.".format(projectname) return ret - network = __salt__['neutronng.network_create'](**kwargs) - ret['changes'] = network - ret['comment'] = 'Created network' + network = __salt__["neutronng.network_create"](**kwargs) + ret["changes"] = network + ret["comment"] = "Created network" return ret - changes = __salt__['neutronng.compare_changes'](network, **kwargs) + changes = __salt__["neutronng.compare_changes"](network, **kwargs) # there's no method for network update in shade right now; # can only delete and recreate if changes: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = changes - ret['comment'] = 'Project will be updated.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = changes + ret["comment"] = "Project will be updated." return ret - __salt__['neutronng.network_delete'](name=network) - __salt__['neutronng.network_create'](**kwargs) - ret['changes'].update(changes) - ret['comment'] = 'Updated network' + __salt__["neutronng.network_delete"](name=network) + __salt__["neutronng.network_create"](**kwargs) + ret["changes"].update(changes) + ret["comment"] = "Updated network" return ret def absent(name, auth=None, **kwargs): - ''' + """ Ensure a network does not exists name Name of the network - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['neutronng.setup_clouds'](auth) + __salt__["neutronng.setup_clouds"](auth) - kwargs['name'] = name - network = __salt__['neutronng.network_get'](name=name) + kwargs["name"] = name + network = __salt__["neutronng.network_get"](name=name) if network: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'id': network.id} - ret['comment'] = 'Network will be deleted.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"id": network.id} + ret["comment"] = "Network will be deleted." return ret - __salt__['neutronng.network_delete'](name=network) - ret['changes']['id'] = network.id - ret['comment'] = 'Deleted network' + __salt__["neutronng.network_delete"](name=network) + ret["changes"]["id"] = network.id + ret["comment"] = "Deleted network" return ret diff --git a/salt/states/neutron_secgroup.py b/salt/states/neutron_secgroup.py index 1a62ecd6711..6773388701c 100644 --- a/salt/states/neutron_secgroup.py +++ b/salt/states/neutron_secgroup.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Neutron Security Groups =============================================== @@ -33,22 +33,25 @@ Example States - name: security_group1 - description: "Very Secure Security Group" - project_name: Project1 -''' +""" from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'neutron_secgroup' +__virtualname__ = "neutron_secgroup" def __virtual__(): - if 'neutronng.list_subnets' in __salt__: + if "neutronng.list_subnets" in __salt__: return __virtualname__ - return (False, 'The neutronng execution module failed to load:\ - shade python module is not available') + return ( + False, + "The neutronng execution module failed to load:\ + shade python module is not available", + ) def present(name, auth=None, **kwargs): - ''' + """ Ensure a security group exists. You can supply either project_name or project_id. @@ -68,92 +71,86 @@ def present(name, auth=None, **kwargs): project_id ID of Project - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['neutronng.setup_clouds'](auth) + __salt__["neutronng.setup_clouds"](auth) - if 'project_name' in kwargs: - kwargs['project_id'] = kwargs['project_name'] - del kwargs['project_name'] + if "project_name" in kwargs: + kwargs["project_id"] = kwargs["project_name"] + del kwargs["project_name"] - project = __salt__['keystoneng.project_get']( - name=kwargs['project_id']) + project = __salt__["keystoneng.project_get"](name=kwargs["project_id"]) if project is None: - ret['result'] = False - ret['comment'] = "project does not exist" + ret["result"] = False + ret["comment"] = "project does not exist" return ret - secgroup = __salt__['neutronng.security_group_get']( - name=name, filters={'tenant_id': project.id}) + secgroup = __salt__["neutronng.security_group_get"]( + name=name, filters={"tenant_id": project.id} + ) if secgroup is None: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Security Group will be created.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Security Group will be created." return ret - secgroup = __salt__['neutronng.security_group_create'](**kwargs) - ret['changes'] = secgroup - ret['comment'] = 'Created security group' + secgroup = __salt__["neutronng.security_group_create"](**kwargs) + ret["changes"] = secgroup + ret["comment"] = "Created security group" return ret - changes = __salt__['neutronng.compare_changes'](secgroup, **kwargs) + changes = __salt__["neutronng.compare_changes"](secgroup, **kwargs) if changes: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = changes - ret['comment'] = 'Security Group will be updated.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = changes + ret["comment"] = "Security Group will be updated." return ret - __salt__['neutronng.security_group_update'](secgroup=secgroup, **changes) - ret['changes'].update(changes) - ret['comment'] = 'Updated security group' + __salt__["neutronng.security_group_update"](secgroup=secgroup, **changes) + ret["changes"].update(changes) + ret["comment"] = "Updated security group" return ret def absent(name, auth=None, **kwargs): - ''' + """ Ensure a security group does not exist name Name of the security group - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['neutronng.setup_clouds'](auth) + __salt__["neutronng.setup_clouds"](auth) - kwargs['project_id'] = __salt__['keystoneng.project_get']( - name=kwargs['project_name']) + kwargs["project_id"] = __salt__["keystoneng.project_get"]( + name=kwargs["project_name"] + ) - secgroup = __salt__['neutronng.security_group_get']( - name=name, - filters={'project_id': kwargs['project_id']} + secgroup = __salt__["neutronng.security_group_get"]( + name=name, filters={"project_id": kwargs["project_id"]} ) if secgroup: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'id': secgroup.id} - ret['comment'] = 'Security group will be deleted.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"id": secgroup.id} + ret["comment"] = "Security group will be deleted." return ret - __salt__['neutronng.security_group_delete'](name=secgroup) - ret['changes']['id'] = name - ret['comment'] = 'Deleted security group' + __salt__["neutronng.security_group_delete"](name=secgroup) + ret["changes"]["id"] = name + ret["comment"] = "Deleted security group" return ret diff --git a/salt/states/neutron_secgroup_rule.py b/salt/states/neutron_secgroup_rule.py index ccc6f2f064f..28cf254d5e2 100644 --- a/salt/states/neutron_secgroup_rule.py +++ b/salt/states/neutron_secgroup_rule.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Neutron Security Group Rules ==================================================== @@ -27,24 +27,27 @@ Example States - name: security_group1 - description: "Very Secure Security Group" - project_id: 1dcac318a83b4610b7a7f7ba01465548 -''' +""" from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'neutron_secgroup_rule' +__virtualname__ = "neutron_secgroup_rule" def __virtual__(): - if 'neutronng.list_subnets' in __salt__: + if "neutronng.list_subnets" in __salt__: return __virtualname__ - return (False, 'The neutronng execution module failed to load:\ - shade python module is not available') + return ( + False, + "The neutronng execution module failed to load:\ + shade python module is not available", + ) def _rule_compare(rule1, rule2): - ''' + """ Compare the common keys between security group rules against eachother - ''' + """ commonkeys = set(rule1.keys()).intersection(rule2.keys()) for key in commonkeys: @@ -54,7 +57,7 @@ def _rule_compare(rule1, rule2): def present(name, auth=None, **kwargs): - ''' + """ Ensure a security group rule exists defaults: port_range_min=None, port_range_max=None, protocol=None, @@ -71,65 +74,60 @@ def present(name, auth=None, **kwargs): The protocol that is matched by the security group rule. Valid values are None, tcp, udp, and icmp. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['neutronng.setup_clouds'](auth) + __salt__["neutronng.setup_clouds"](auth) - if 'project_name' in kwargs: - kwargs['project_id'] = kwargs['project_name'] - del kwargs['project_name'] + if "project_name" in kwargs: + kwargs["project_id"] = kwargs["project_name"] + del kwargs["project_name"] - project = __salt__['keystoneng.project_get']( - name=kwargs['project_id']) + project = __salt__["keystoneng.project_get"](name=kwargs["project_id"]) if project is None: - ret['result'] = False - ret['comment'] = "Project does not exist" + ret["result"] = False + ret["comment"] = "Project does not exist" return ret - secgroup = __salt__['neutronng.security_group_get']( - name=name, - filters={'tenant_id': project.id} + secgroup = __salt__["neutronng.security_group_get"]( + name=name, filters={"tenant_id": project.id} ) if secgroup is None: - ret['result'] = False - ret['changes'] = {}, - ret['comment'] = 'Security Group does not exist {}'.format(name) + ret["result"] = False + ret["changes"] = ({},) + ret["comment"] = "Security Group does not exist {}".format(name) return ret # we have to search through all secgroup rules for a possible match rule_exists = None - for rule in secgroup['security_group_rules']: + for rule in secgroup["security_group_rules"]: if _rule_compare(rule, kwargs) is True: rule_exists = True if rule_exists is None: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Security Group rule will be created.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Security Group rule will be created." return ret # The variable differences are a little clumsy right now - kwargs['secgroup_name_or_id'] = secgroup + kwargs["secgroup_name_or_id"] = secgroup - new_rule = __salt__['neutronng.security_group_rule_create'](**kwargs) - ret['changes'] = new_rule - ret['comment'] = 'Created security group rule' + new_rule = __salt__["neutronng.security_group_rule_create"](**kwargs) + ret["changes"] = new_rule + ret["comment"] = "Created security group rule" return ret return ret def absent(name, auth=None, **kwargs): - ''' + """ Ensure a security group rule does not exist name @@ -140,41 +138,37 @@ def absent(name, auth=None, **kwargs): project_id id of project to delete rule from - ''' - rule_id = kwargs['rule_id'] - ret = {'name': rule_id, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + rule_id = kwargs["rule_id"] + ret = {"name": rule_id, "changes": {}, "result": True, "comment": ""} - __salt__['neutronng.setup_clouds'](auth) + __salt__["neutronng.setup_clouds"](auth) - secgroup = __salt__['neutronng.security_group_get']( - name=name, - filters={'tenant_id': kwargs['project_id']} + secgroup = __salt__["neutronng.security_group_get"]( + name=name, filters={"tenant_id": kwargs["project_id"]} ) # no need to delete a rule if the security group doesn't exist if secgroup is None: - ret['comment'] = "security group does not exist" + ret["comment"] = "security group does not exist" return ret # This should probably be done with compare on fields instead of # rule_id in the future rule_exists = None - for rule in secgroup['security_group_rules']: + for rule in secgroup["security_group_rules"]: if _rule_compare(rule, {"id": rule_id}) is True: rule_exists = True if rule_exists: - if __opts__['test']: - ret['result'] = None - ret['changes'] = {'id': kwargs['rule_id']} - ret['comment'] = 'Security group rule will be deleted.' + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {"id": kwargs["rule_id"]} + ret["comment"] = "Security group rule will be deleted." return ret - __salt__['neutronng.security_group_rule_delete'](rule_id=rule_id) - ret['changes']['id'] = rule_id - ret['comment'] = 'Deleted security group rule' + __salt__["neutronng.security_group_rule_delete"](rule_id=rule_id) + ret["changes"]["id"] = rule_id + ret["comment"] = "Deleted security group rule" return ret diff --git a/salt/states/neutron_subnet.py b/salt/states/neutron_subnet.py index 58219019eea..c934c71b1e9 100644 --- a/salt/states/neutron_subnet.py +++ b/salt/states/neutron_subnet.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of OpenStack Neutron Subnets ========================================= @@ -45,22 +45,25 @@ Example States - name: v6subnet1 - network_name_or_id: network1 - ip_version: 6 -''' +""" from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'neutron_subnet' +__virtualname__ = "neutron_subnet" def __virtual__(): - if 'neutronng.list_subnets' in __salt__: + if "neutronng.list_subnets" in __salt__: return __virtualname__ - return (False, 'The neutronng execution module failed to load:\ - shade python module is not available') + return ( + False, + "The neutronng execution module failed to load:\ + shade python module is not available", + ) def present(name, auth=None, **kwargs): - ''' + """ Ensure a subnet exists and is up-to-date name @@ -90,81 +93,75 @@ def present(name, auth=None, **kwargs): ipv6_address_mode IPv6 address mode. Valid values are: ‘dhcpv6-stateful’, ‘dhcpv6-stateless’, or ‘slaac’. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - kwargs = __utils__['args.clean_kwargs'](**kwargs) + kwargs = __utils__["args.clean_kwargs"](**kwargs) - __salt__['neutronng.setup_clouds'](auth) + __salt__["neutronng.setup_clouds"](auth) - kwargs['subnet_name'] = name - subnet = __salt__['neutronng.subnet_get'](name=name) + kwargs["subnet_name"] = name + subnet = __salt__["neutronng.subnet_get"](name=name) if subnet is None: - if __opts__['test']: - ret['result'] = None - ret['changes'] = kwargs - ret['comment'] = 'Subnet will be created.' + if __opts__["test"]: + ret["result"] = None + ret["changes"] = kwargs + ret["comment"] = "Subnet will be created." return ret - new_subnet = __salt__['neutronng.subnet_create'](**kwargs) - ret['changes'] = new_subnet - ret['comment'] = 'Created subnet' + new_subnet = __salt__["neutronng.subnet_create"](**kwargs) + ret["changes"] = new_subnet + ret["comment"] = "Created subnet" return ret - changes = __salt__['neutronng.compare_changes'](subnet, **kwargs) + changes = __salt__["neutronng.compare_changes"](subnet, **kwargs) if changes: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = changes - ret['comment'] = 'Project will be updated.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = changes + ret["comment"] = "Project will be updated." return ret # update_subnet does not support changing cidr, # so we have to delete and recreate the subnet in this case. - if 'cidr' in changes or 'tenant_id' in changes: - __salt__['neutronng.subnet_delete'](name=name) - new_subnet = __salt__['neutronng.subnet_create'](**kwargs) - ret['changes'] = new_subnet - ret['comment'] = 'Deleted and recreated subnet' + if "cidr" in changes or "tenant_id" in changes: + __salt__["neutronng.subnet_delete"](name=name) + new_subnet = __salt__["neutronng.subnet_create"](**kwargs) + ret["changes"] = new_subnet + ret["comment"] = "Deleted and recreated subnet" return ret - __salt__['neutronng.subnet_update'](**kwargs) - ret['changes'].update(changes) - ret['comment'] = 'Updated subnet' + __salt__["neutronng.subnet_update"](**kwargs) + ret["changes"].update(changes) + ret["comment"] = "Updated subnet" return ret def absent(name, auth=None): - ''' + """ Ensure a subnet does not exists name Name of the subnet - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - __salt__['neutronng.setup_clouds'](auth) + __salt__["neutronng.setup_clouds"](auth) - subnet = __salt__['neutronng.subnet_get'](name=name) + subnet = __salt__["neutronng.subnet_get"](name=name) if subnet: - if __opts__['test'] is True: - ret['result'] = None - ret['changes'] = {'id': subnet.id} - ret['comment'] = 'Project will be deleted.' + if __opts__["test"] is True: + ret["result"] = None + ret["changes"] = {"id": subnet.id} + ret["comment"] = "Project will be deleted." return ret - __salt__['neutronng.subnet_delete'](name=subnet) - ret['changes']['id'] = name - ret['comment'] = 'Deleted subnet' + __salt__["neutronng.subnet_delete"](name=subnet) + ret["changes"]["id"] = name + ret["comment"] = "Deleted subnet" return ret diff --git a/salt/states/nexus.py b/salt/states/nexus.py index 9d9ff11504b..6b83b3528a2 100644 --- a/salt/states/nexus.py +++ b/salt/states/nexus.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" This state downloads artifacts from Nexus 3.x. .. versionadded:: 2018.3.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -14,18 +15,18 @@ from salt.ext import six log = logging.getLogger(__name__) -__virtualname__ = 'nexus' +__virtualname__ = "nexus" def __virtual__(): - ''' + """ Set the virtual name for the module - ''' + """ return __virtualname__ -def downloaded(name, artifact, target_dir='/tmp', target_file=None): - ''' +def downloaded(name, artifact, target_dir="/tmp", target_file=None): + """ Ensures that the artifact from nexus exists at given location. If it doesn't exist, then it will be downloaded. If it already exists then the checksum of existing file is checked against checksum in nexus. If it is different then the step will fail. @@ -86,77 +87,78 @@ def downloaded(name, artifact, target_dir='/tmp', target_file=None): version: '1.0' - target_dir: /opt/maven/modules/com/company/release - ''' + """ log.debug(" ======================== STATE: nexus.downloaded (name: %s) ", name) - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} try: fetch_result = __fetch_from_nexus(artifact, target_dir, target_file) except Exception as exc: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = six.text_type(exc) + ret["result"] = False + ret["comment"] = six.text_type(exc) return ret log.debug("fetch_result=%s", fetch_result) - ret['result'] = fetch_result['status'] - ret['comment'] = fetch_result['comment'] - ret['changes'] = fetch_result['changes'] + ret["result"] = fetch_result["status"] + ret["comment"] = fetch_result["comment"] + ret["changes"] = fetch_result["changes"] log.debug("ret=%s", ret) return ret def __fetch_from_nexus(artifact, target_dir, target_file): - nexus_url = artifact['nexus_url'] - repository = artifact['repository'] - group_id = artifact['group_id'] - artifact_id = artifact['artifact_id'] - packaging = artifact['packaging'] if 'packaging' in artifact else 'jar' - classifier = artifact['classifier'] if 'classifier' in artifact else None - username = artifact['username'] if 'username' in artifact else None - password = artifact['password'] if 'password' in artifact else None - version = artifact['version'] if 'version' in artifact else None + nexus_url = artifact["nexus_url"] + repository = artifact["repository"] + group_id = artifact["group_id"] + artifact_id = artifact["artifact_id"] + packaging = artifact["packaging"] if "packaging" in artifact else "jar" + classifier = artifact["classifier"] if "classifier" in artifact else None + username = artifact["username"] if "username" in artifact else None + password = artifact["password"] if "password" in artifact else None + version = artifact["version"] if "version" in artifact else None # determine module function to use - if version == 'latest_snapshot': - function = 'nexus.get_latest_snapshot' + if version == "latest_snapshot": + function = "nexus.get_latest_snapshot" version_param = False - elif version == 'latest': - function = 'nexus.get_latest_release' + elif version == "latest": + function = "nexus.get_latest_release" version_param = False - elif version.endswith('SNAPSHOT'): - function = 'nexus.get_snapshot' + elif version.endswith("SNAPSHOT"): + function = "nexus.get_snapshot" version_param = True else: - function = 'nexus.get_release' + function = "nexus.get_release" version_param = True if version_param: - fetch_result = __salt__[function](nexus_url=nexus_url, - repository=repository, - group_id=group_id, - artifact_id=artifact_id, - packaging=packaging, - classifier=classifier, - target_dir=target_dir, - target_file=target_file, - username=username, - password=password, - version=version) + fetch_result = __salt__[function]( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + packaging=packaging, + classifier=classifier, + target_dir=target_dir, + target_file=target_file, + username=username, + password=password, + version=version, + ) else: - fetch_result = __salt__[function](nexus_url=nexus_url, - repository=repository, - group_id=group_id, - artifact_id=artifact_id, - packaging=packaging, - classifier=classifier, - target_dir=target_dir, - target_file=target_file, - username=username, - password=password) + fetch_result = __salt__[function]( + nexus_url=nexus_url, + repository=repository, + group_id=group_id, + artifact_id=artifact_id, + packaging=packaging, + classifier=classifier, + target_dir=target_dir, + target_file=target_file, + username=username, + password=password, + ) return fetch_result diff --git a/salt/states/nfs_export.py b/salt/states/nfs_export.py index 170da99fd1d..8731bca363f 100644 --- a/salt/states/nfs_export.py +++ b/salt/states/nfs_export.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of NFS exports =============================================== @@ -56,32 +56,29 @@ To ensure an NFS export is absent: nfs_export.absent: - name: '/srv/nfs' -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import salt.utils.path def __virtual__(): - ''' + """ Only work with nfs tools installed - ''' - cmd = 'exportfs' + """ + cmd = "exportfs" if salt.utils.path.which(cmd): return bool(cmd) - return( + return ( False, - 'The nfs_exports state module failed to load: ' - 'the exportfs binary is not in the path' + "The nfs_exports state module failed to load: " + "the exportfs binary is not in the path", ) -def present(name, - clients=None, - hosts=None, - options=None, - exports='/etc/exports'): - ''' +def present(name, clients=None, hosts=None, options=None, exports="/etc/exports"): + """ Ensure that the named export is present with the given options name @@ -129,90 +126,84 @@ def present(name, - 'rw' - 'subtree_check' - ''' + """ path = name - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} if not clients: if not hosts: - ret['result'] = False - ret['comment'] = 'Either \'clients\' or \'hosts\' must be defined' + ret["result"] = False + ret["comment"] = "Either 'clients' or 'hosts' must be defined" return ret # options being None is handled by add_export() - clients = [{'hosts': hosts, 'options': options}] + clients = [{"hosts": hosts, "options": options}] - old = __salt__['nfs3.list_exports'](exports) + old = __salt__["nfs3.list_exports"](exports) if path in old: if old[path] == clients: - ret['result'] = True - ret['comment'] = 'Export {0} already configured'.format(path) + ret["result"] = True + ret["comment"] = "Export {0} already configured".format(path) return ret - ret['changes']['new'] = clients - ret['changes']['old'] = old[path] - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Export {0} would be changed'.format(path) + ret["changes"]["new"] = clients + ret["changes"]["old"] = old[path] + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Export {0} would be changed".format(path) return ret - __salt__['nfs3.del_export'](exports, path) + __salt__["nfs3.del_export"](exports, path) else: - ret['changes']['old'] = None - ret['changes']['new'] = clients - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Export {0} would be added'.format(path) + ret["changes"]["old"] = None + ret["changes"]["new"] = clients + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Export {0} would be added".format(path) return ret - add_export = __salt__['nfs3.add_export'] + add_export = __salt__["nfs3.add_export"] for exp in clients: - add_export(exports, path, exp['hosts'], exp['options']) + add_export(exports, path, exp["hosts"], exp["options"]) - ret['changes']['new'] = clients + ret["changes"]["new"] = clients - try_reload = __salt__['nfs3.reload_exports']() - ret['comment'] = try_reload['stderr'] - ret['result'] = try_reload['result'] + try_reload = __salt__["nfs3.reload_exports"]() + ret["comment"] = try_reload["stderr"] + ret["result"] = try_reload["result"] return ret -def absent(name, exports='/etc/exports'): - ''' +def absent(name, exports="/etc/exports"): + """ Ensure that the named path is not exported name The export path to remove - ''' + """ path = name - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - old = __salt__['nfs3.list_exports'](exports) + old = __salt__["nfs3.list_exports"](exports) if path in old: - if __opts__['test']: - ret['comment'] = 'Export {0} would be removed'.format(path) - ret['changes'][path] = old[path] - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Export {0} would be removed".format(path) + ret["changes"][path] = old[path] + ret["result"] = None return ret - __salt__['nfs3.del_export'](exports, path) - try_reload = __salt__['nfs3.reload_exports']() - if not try_reload['result']: - ret['comment'] = try_reload['stderr'] + __salt__["nfs3.del_export"](exports, path) + try_reload = __salt__["nfs3.reload_exports"]() + if not try_reload["result"]: + ret["comment"] = try_reload["stderr"] else: - ret['comment'] = 'Export {0} removed'.format(path) + ret["comment"] = "Export {0} removed".format(path) - ret['result'] = try_reload['result'] - ret['changes'][path] = old[path] + ret["result"] = try_reload["result"] + ret["changes"][path] = old[path] else: - ret['comment'] = 'Export {0} already absent'.format(path) - ret['result'] = True + ret["comment"] = "Export {0} already absent".format(path) + ret["result"] = True return ret diff --git a/salt/states/nftables.py b/salt/states/nftables.py index 0886f082d5e..bd98a136cfb 100644 --- a/salt/states/nftables.py +++ b/salt/states/nftables.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of nftables ====================== @@ -109,25 +109,30 @@ at some point be deprecated in favor of a more generic `firewall` state. - family: ip - table: filter -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import salt libs from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS -import logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the locale module is available in __salt__ - ''' - return 'nftables' if 'nftables.version' in __salt__ else False + """ + if "nftables.version" in __salt__: + return "nftables" + return (False, "nftables module could not be loaded") -def chain_present(name, table='filter', table_type=None, hook=None, priority=None, family='ipv4'): - ''' +def chain_present( + name, table="filter", table_type=None, hook=None, priority=None, family="ipv4" +): + """ .. versionadded:: 2014.7.0 Verify the chain is exist. @@ -140,93 +145,91 @@ def chain_present(name, table='filter', table_type=None, hook=None, priority=Non family Networking family, either ipv4 or ipv6 - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - chain_check = __salt__['nftables.check_chain'](table, name, family=family) - if chain_check['result'] is True: - ret['result'] = True - ret['comment'] = ('nftables {0} chain is already exist in {1} table for {2}' - .format(name, table, family)) + chain_check = __salt__["nftables.check_chain"](table, name, family=family) + if chain_check["result"] is True: + ret["result"] = True + ret[ + "comment" + ] = "nftables {0} chain is already exist in {1} table for {2}".format( + name, table, family + ) return ret - res = __salt__['nftables.new_chain']( - table, - name, - table_type=table_type, - hook=hook, - priority=priority, - family=family + res = __salt__["nftables.new_chain"]( + table, name, table_type=table_type, hook=hook, priority=priority, family=family ) - if res['result'] is True: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = ('nftables {0} chain in {1} table create success for {2}' - .format(name, table, family)) + if res["result"] is True: + ret["changes"] = {"locale": name} + ret["result"] = True + ret[ + "comment" + ] = "nftables {0} chain in {1} table create success for {2}".format( + name, table, family + ) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} chain in {1} table: {2} for {3}'.format( - name, - table, - res['comment'].strip(), - family + ret["result"] = False + ret["comment"] = "Failed to create {0} chain in {1} table: {2} for {3}".format( + name, table, res["comment"].strip(), family ) return ret -def chain_absent(name, table='filter', family='ipv4'): - ''' +def chain_absent(name, table="filter", family="ipv4"): + """ .. versionadded:: 2014.7.0 Verify the chain is absent. family Networking family, either ipv4 or ipv6 - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - chain_check = __salt__['nftables.check_chain'](table, name, family) + chain_check = __salt__["nftables.check_chain"](table, name, family) if not chain_check: - ret['result'] = True - ret['comment'] = ('nftables {0} chain is already absent in {1} table for {2}' - .format(name, table, family)) + ret["result"] = True + ret[ + "comment" + ] = "nftables {0} chain is already absent in {1} table for {2}".format( + name, table, family + ) return ret - flush_chain = __salt__['nftables.flush'](table, name, family) + flush_chain = __salt__["nftables.flush"](table, name, family) if flush_chain: - command = __salt__['nftables.delete_chain'](table, name, family) + command = __salt__["nftables.delete_chain"](table, name, family) if command is True: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = ('nftables {0} chain in {1} table delete success for {2}' - .format(name, table, family)) + ret["changes"] = {"locale": name} + ret["result"] = True + ret[ + "comment" + ] = "nftables {0} chain in {1} table delete success for {2}".format( + name, table, family + ) else: - ret['result'] = False - ret['comment'] = ('Failed to delete {0} chain in {1} table: {2} for {3}' - .format(name, table, command.strip(), family)) + ret["result"] = False + ret[ + "comment" + ] = "Failed to delete {0} chain in {1} table: {2} for {3}".format( + name, table, command.strip(), family + ) else: - ret['result'] = False - ret['comment'] = 'Failed to flush {0} chain in {1} table: {2} for {3}'.format( - name, - table, - flush_chain.strip(), - family + ret["result"] = False + ret["comment"] = "Failed to flush {0} chain in {1} table: {2} for {3}".format( + name, table, flush_chain.strip(), family ) return ret -def append(name, family='ipv4', **kwargs): - ''' +def append(name, family="ipv4", **kwargs): + """ .. versionadded:: 0.17.0 Append a rule to a chain @@ -242,71 +245,63 @@ def append(name, family='ipv4', **kwargs): that would normally be used for nftables, with one exception: `--state` is specified as `connstate` instead of `state` (not to be confused with `ctstate`). - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in kwargs: del kwargs[ignore] - res = __salt__['nftables.build_rule'](family=family, **kwargs) - if not res['result']: + res = __salt__["nftables.build_rule"](family=family, **kwargs) + if not res["result"]: return res - rule = res['rule'] + rule = res["rule"] - res = __salt__['nftables.build_rule'](full=True, family=family, command='add', **kwargs) - if not res['result']: + res = __salt__["nftables.build_rule"]( + full=True, family=family, command="add", **kwargs + ) + if not res["result"]: return res - command = res['rule'] + command = res["rule"] - res = __salt__['nftables.check'](kwargs['table'], - kwargs['chain'], - rule, - family) - if res['result']: - ret['result'] = True - ret['comment'] = 'nftables rule for {0} already set ({1}) for {2}'.format( - name, - command.strip(), - family) + res = __salt__["nftables.check"](kwargs["table"], kwargs["chain"], rule, family) + if res["result"]: + ret["result"] = True + ret["comment"] = "nftables rule for {0} already set ({1}) for {2}".format( + name, command.strip(), family + ) return ret - if 'test' in __opts__ and __opts__['test']: - ret['comment'] = 'nftables rule for {0} needs to be set ({1}) for {2}'.format( - name, - command.strip(), - family) + if "test" in __opts__ and __opts__["test"]: + ret["comment"] = "nftables rule for {0} needs to be set ({1}) for {2}".format( + name, command.strip(), family + ) return ret - res = __salt__['nftables.append'](kwargs['table'], - kwargs['chain'], - rule, - family) - if res['result']: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Set nftables rule for {0} to: {1} for {2}'.format( - name, - command.strip(), - family) - if 'save' in kwargs: - if kwargs['save']: - __salt__['nftables.save'](filename=None, family=family) - ret['comment'] = ('Set and Saved nftables rule for {0} to: ' - '{1} for {2}'.format(name, command.strip(), family)) + res = __salt__["nftables.append"](kwargs["table"], kwargs["chain"], rule, family) + if res["result"]: + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Set nftables rule for {0} to: {1} for {2}".format( + name, command.strip(), family + ) + if "save" in kwargs: + if kwargs["save"]: + __salt__["nftables.save"](filename=None, family=family) + ret["comment"] = ( + "Set and Saved nftables rule for {0} to: " + "{1} for {2}".format(name, command.strip(), family) + ) return ret else: - ret['result'] = False - ret['comment'] = ('Failed to set nftables rule for {0}.\n' - 'Attempted rule was {1} for {2}.\n' - '{3}').format( - name, - command.strip(), family, res['comment']) + ret["result"] = False + ret["comment"] = ( + "Failed to set nftables rule for {0}.\n" + "Attempted rule was {1} for {2}.\n" + "{3}" + ).format(name, command.strip(), family, res["comment"]) return ret -def insert(name, family='ipv4', **kwargs): - ''' +def insert(name, family="ipv4", **kwargs): + """ .. versionadded:: 2014.7.0 Insert a rule into a chain @@ -322,74 +317,63 @@ def insert(name, family='ipv4', **kwargs): that would normally be used for nftables, with one exception: `--state` is specified as `connstate` instead of `state` (not to be confused with `ctstate`). - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in kwargs: del kwargs[ignore] - res = __salt__['nftables.build_rule'](family=family, **kwargs) - if not res['result']: + res = __salt__["nftables.build_rule"](family=family, **kwargs) + if not res["result"]: return res - rule = res['rule'] + rule = res["rule"] - res = __salt__['nftables.build_rule'](full=True, - family=family, - command='insert', - **kwargs) - if not res['result']: + res = __salt__["nftables.build_rule"]( + full=True, family=family, command="insert", **kwargs + ) + if not res["result"]: return res - command = res['rule'] + command = res["rule"] - res = __salt__['nftables.check'](kwargs['table'], - kwargs['chain'], - rule, - family) - if res['result']: - ret['result'] = True - ret['comment'] = 'nftables rule for {0} already set for {1} ({2})'.format( - name, - family, - command.strip()) + res = __salt__["nftables.check"](kwargs["table"], kwargs["chain"], rule, family) + if res["result"]: + ret["result"] = True + ret["comment"] = "nftables rule for {0} already set for {1} ({2})".format( + name, family, command.strip() + ) return ret - if 'test' in __opts__ and __opts__['test']: - ret['comment'] = 'nftables rule for {0} needs to be set for {1} ({2})'.format( - name, - family, - command.strip()) + if "test" in __opts__ and __opts__["test"]: + ret["comment"] = "nftables rule for {0} needs to be set for {1} ({2})".format( + name, family, command.strip() + ) return ret - res = __salt__['nftables.insert'](kwargs['table'], - kwargs['chain'], - kwargs['position'], - rule, - family) - if res['result']: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Set nftables rule for {0} to: {1} for {2}'.format( - name, - command.strip(), - family) - if 'save' in kwargs: - if kwargs['save']: - __salt__['nftables.save'](filename=None, family=family) - ret['comment'] = ('Set and Saved nftables rule for {0} to: ' - '{1} for {2}'.format(name, command.strip(), family)) + res = __salt__["nftables.insert"]( + kwargs["table"], kwargs["chain"], kwargs["position"], rule, family + ) + if res["result"]: + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Set nftables rule for {0} to: {1} for {2}".format( + name, command.strip(), family + ) + if "save" in kwargs: + if kwargs["save"]: + __salt__["nftables.save"](filename=None, family=family) + ret["comment"] = ( + "Set and Saved nftables rule for {0} to: " + "{1} for {2}".format(name, command.strip(), family) + ) return ret else: - ret['result'] = False - ret['comment'] = ('Failed to set nftables rule for {0}.\n' - 'Attempted rule was {1}').format( - name, - command.strip()) + ret["result"] = False + ret["comment"] = ( + "Failed to set nftables rule for {0}.\n" "Attempted rule was {1}" + ).format(name, command.strip()) return ret -def delete(name, family='ipv4', **kwargs): - ''' +def delete(name, family="ipv4", **kwargs): + """ .. versionadded:: 2014.7.0 Delete a rule to a chain @@ -405,80 +389,73 @@ def delete(name, family='ipv4', **kwargs): that would normally be used for nftables, with one exception: `--state` is specified as `connstate` instead of `state` (not to be confused with `ctstate`). - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in kwargs: del kwargs[ignore] - res = __salt__['nftables.build_rule'](family=family, **kwargs) - if not res['result']: + res = __salt__["nftables.build_rule"](family=family, **kwargs) + if not res["result"]: return res - rule = res['rule'] + rule = res["rule"] - res = __salt__['nftables.build_rule'](full=True, family=family, command='D', **kwargs) - if not res['result']: + res = __salt__["nftables.build_rule"]( + full=True, family=family, command="D", **kwargs + ) + if not res["result"]: return res - command = res['rule'] + command = res["rule"] - res = __salt__['nftables.check'](kwargs['table'], - kwargs['chain'], - rule, - family) + res = __salt__["nftables.check"](kwargs["table"], kwargs["chain"], rule, family) - if not res['result']: - ret['result'] = True - ret['comment'] = 'nftables rule for {0} already absent for {1} ({2})'.format( - name, - family, - command.strip()) + if not res["result"]: + ret["result"] = True + ret["comment"] = "nftables rule for {0} already absent for {1} ({2})".format( + name, family, command.strip() + ) return ret - if 'test' in __opts__ and __opts__['test']: - ret['comment'] = 'nftables rule for {0} needs to be deleted for {1} ({2})'.format( - name, - family, - command.strip()) + if "test" in __opts__ and __opts__["test"]: + ret[ + "comment" + ] = "nftables rule for {0} needs to be deleted for {1} ({2})".format( + name, family, command.strip() + ) return ret - if 'position' in kwargs: - res = __salt__['nftables.delete']( - kwargs['table'], - kwargs['chain'], - family=family, - position=kwargs['position']) + if "position" in kwargs: + res = __salt__["nftables.delete"]( + kwargs["table"], kwargs["chain"], family=family, position=kwargs["position"] + ) else: - res = __salt__['nftables.delete']( - kwargs['table'], - kwargs['chain'], - family=family, - rule=rule) + res = __salt__["nftables.delete"]( + kwargs["table"], kwargs["chain"], family=family, rule=rule + ) - if res['result']: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Delete nftables rule for {0} {1}'.format( - name, - command.strip()) - if 'save' in kwargs: - if kwargs['save']: - __salt__['nftables.save'](filename=None, family=family) - ret['comment'] = ('Deleted and Saved nftables rule for {0} for {1}' - '{2}'.format(name, command.strip(), family)) + if res["result"]: + ret["changes"] = {"locale": name} + ret["result"] = True + ret["comment"] = "Delete nftables rule for {0} {1}".format( + name, command.strip() + ) + if "save" in kwargs: + if kwargs["save"]: + __salt__["nftables.save"](filename=None, family=family) + ret["comment"] = ( + "Deleted and Saved nftables rule for {0} for {1}" + "{2}".format(name, command.strip(), family) + ) return ret else: - ret['result'] = False - ret['comment'] = ('Failed to delete nftables rule for {0}.\n' - 'Attempted rule was {1}').format( - name, - command.strip()) + ret["result"] = False + ret["comment"] = ( + "Failed to delete nftables rule for {0}.\n" "Attempted rule was {1}" + ).format(name, command.strip()) return ret -def flush(name, family='ipv4', **kwargs): - ''' +def flush(name, family="ipv4", **kwargs): + """ .. versionadded:: 2014.7.0 Flush current nftables state @@ -486,56 +463,52 @@ def flush(name, family='ipv4', **kwargs): family Networking family, either ipv4 or ipv6 - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} for ignore in _STATE_INTERNAL_KEYWORDS: if ignore in kwargs: del kwargs[ignore] - if 'table' not in kwargs: - kwargs['table'] = 'filter' + if "table" not in kwargs: + kwargs["table"] = "filter" - res = __salt__['nftables.check_table'](kwargs['table'], family=family) - if not res['result']: - ret['result'] = False - ret['comment'] = 'Failed to flush table {0} in family {1}, table does not exist.'.format( - kwargs['table'], - family + res = __salt__["nftables.check_table"](kwargs["table"], family=family) + if not res["result"]: + ret["result"] = False + ret[ + "comment" + ] = "Failed to flush table {0} in family {1}, table does not exist.".format( + kwargs["table"], family ) return ret - if 'chain' not in kwargs: - kwargs['chain'] = '' + if "chain" not in kwargs: + kwargs["chain"] = "" else: - res = __salt__['nftables.check_chain'](kwargs['table'], - kwargs['chain'], - family=family) - if not res['result']: - ret['result'] = False - ret['comment'] = 'Failed to flush chain {0} in table {1} in family {2}, chain does not exist.'.format( - kwargs['chain'], - kwargs['table'], - family + res = __salt__["nftables.check_chain"]( + kwargs["table"], kwargs["chain"], family=family + ) + if not res["result"]: + ret["result"] = False + ret[ + "comment" + ] = "Failed to flush chain {0} in table {1} in family {2}, chain does not exist.".format( + kwargs["chain"], kwargs["table"], family ) return ret - res = __salt__['nftables.flush'](kwargs['table'], - kwargs['chain'], - family) - if res['result']: - ret['changes'] = {'locale': name} - ret['result'] = True - ret['comment'] = 'Flush nftables rules in {0} table {1} chain {2} family'.format( - kwargs['table'], - kwargs['chain'], - family + res = __salt__["nftables.flush"](kwargs["table"], kwargs["chain"], family) + if res["result"]: + ret["changes"] = {"locale": name} + ret["result"] = True + ret[ + "comment" + ] = "Flush nftables rules in {0} table {1} chain {2} family".format( + kwargs["table"], kwargs["chain"], family ) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to flush nftables rules' + ret["result"] = False + ret["comment"] = "Failed to flush nftables rules" return ret diff --git a/salt/states/npm.py b/salt/states/npm.py index 13af9ed1372..1db94ca113d 100644 --- a/salt/states/npm.py +++ b/salt/states/npm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of NPM Packages ============================ @@ -17,32 +17,32 @@ for the package which provides npm (simply ``npm`` in most cases). Example: npm.installed: - require: - pkg: npm -''' +""" # Import salt libs -from __future__ import absolute_import, unicode_literals, print_function -from salt.exceptions import CommandExecutionError, CommandNotFoundError +from __future__ import absolute_import, print_function, unicode_literals # Import 3rd-party libs import re + +from salt.exceptions import CommandExecutionError, CommandNotFoundError from salt.ext import six def __virtual__(): - ''' + """ Only load if the npm module is available in __salt__ - ''' - return 'npm' if 'npm.list' in __salt__ else False, '\'npm\' binary not found on system' + """ + return ( + "npm" if "npm.list" in __salt__ else False, + "'npm' binary not found on system", + ) -def installed(name, - pkgs=None, - dir=None, - user=None, - force_reinstall=False, - registry=None, - env=None): - ''' +def installed( + name, pkgs=None, dir=None, user=None, force_reinstall=False, registry=None, env=None +): + """ Verify that the given package is installed and is at the correct version (if specified). @@ -91,26 +91,25 @@ def installed(name, force_reinstall Install the package even if it is already installed - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} pkg_list = pkgs if pkgs else [name] try: - installed_pkgs = __salt__['npm.list'](dir=dir, runas=user, env=env, depth=0) + installed_pkgs = __salt__["npm.list"](dir=dir, runas=user, env=env, depth=0) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error looking up \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error looking up '{0}': {1}".format(name, err) return ret else: - installed_pkgs = dict((p, info) - for p, info in six.iteritems(installed_pkgs)) + installed_pkgs = dict((p, info) for p, info in six.iteritems(installed_pkgs)) pkgs_satisfied = [] pkgs_to_install = [] def _pkg_is_installed(pkg, installed_pkgs): - ''' + """ Helper function to determine if a package is installed This performs more complex comparison than just checking @@ -119,25 +118,25 @@ def installed(name, :pkg str: The package to compare :installed_pkgs: A dictionary produced by npm list --json - ''' - if (pkg_name in installed_pkgs and - 'version' in installed_pkgs[pkg_name]): + """ + if pkg_name in installed_pkgs and "version" in installed_pkgs[pkg_name]: return True # Check to see if we are trying to install from a URI - elif '://' in pkg_name: # TODO Better way? + elif "://" in pkg_name: # TODO Better way? for pkg_details in installed_pkgs.values(): try: - pkg_from = pkg_details.get('from', '').split('://')[1] + pkg_from = pkg_details.get("from", "").split("://")[1] # Catch condition where we may have specified package as # git://github.com/foo/bar but packager describes it as # git://github.com/foo/bar.git in the package - if not pkg_from.endswith('.git') and pkg_name.startswith('git://'): - pkg_from += '.git' - if pkg_name.split('://')[1] == pkg_from: + if not pkg_from.endswith(".git") and pkg_name.startswith("git://"): + pkg_from += ".git" + if pkg_name.split("://")[1] == pkg_from: return True except IndexError: pass return False + for pkg in pkg_list: # Valid: # @@ -145,7 +144,7 @@ def installed(name, # @foobar # buffer-equal-constant-time@1.0.1 # coffee-script - matches = re.search(r'^(@?[^@\s]+)(?:@(\S+))?', pkg) + matches = re.search(r"^(@?[^@\s]+)(?:@(\S+))?", pkg) pkg_name, pkg_ver = matches.group(1), matches.group(2) or None if force_reinstall is True: @@ -155,12 +154,13 @@ def installed(name, pkgs_to_install.append(pkg) continue - installed_name_ver = '{0}@{1}'.format(pkg_name, - installed_pkgs[pkg_name]['version']) + installed_name_ver = "{0}@{1}".format( + pkg_name, installed_pkgs[pkg_name]["version"] + ) # If given an explicit version check the installed version matches. if pkg_ver: - if installed_pkgs[pkg_name].get('version') != pkg_ver: + if installed_pkgs[pkg_name].get("version") != pkg_ver: pkgs_to_install.append(pkg) else: pkgs_satisfied.append(installed_name_ver) @@ -170,61 +170,69 @@ def installed(name, pkgs_satisfied.append(installed_name_ver) continue - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None comment_msg = [] if pkgs_to_install: - comment_msg.append('NPM package(s) \'{0}\' are set to be installed' - .format(', '.join(pkgs_to_install))) + comment_msg.append( + "NPM package(s) '{0}' are set to be installed".format( + ", ".join(pkgs_to_install) + ) + ) - ret['changes'] = {'old': [], 'new': pkgs_to_install} + ret["changes"] = {"old": [], "new": pkgs_to_install} if pkgs_satisfied: - comment_msg.append('Package(s) \'{0}\' satisfied by {1}' - .format(', '.join(pkg_list), ', '.join(pkgs_satisfied))) - ret['result'] = True + comment_msg.append( + "Package(s) '{0}' satisfied by {1}".format( + ", ".join(pkg_list), ", ".join(pkgs_satisfied) + ) + ) + ret["result"] = True - ret['comment'] = '. '.join(comment_msg) + ret["comment"] = ". ".join(comment_msg) return ret if not pkgs_to_install: - ret['result'] = True - ret['comment'] = ('Package(s) \'{0}\' satisfied by {1}' - .format(', '.join(pkg_list), ', '.join(pkgs_satisfied))) + ret["result"] = True + ret["comment"] = "Package(s) '{0}' satisfied by {1}".format( + ", ".join(pkg_list), ", ".join(pkgs_satisfied) + ) return ret try: cmd_args = { - 'dir': dir, - 'runas': user, - 'registry': registry, - 'env': env, - 'pkgs': pkg_list, + "dir": dir, + "runas": user, + "registry": registry, + "env": env, + "pkgs": pkg_list, } - call = __salt__['npm.install'](**cmd_args) + call = __salt__["npm.install"](**cmd_args) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error installing \'{0}\': {1}'.format( - ', '.join(pkg_list), err) + ret["result"] = False + ret["comment"] = "Error installing '{0}': {1}".format(", ".join(pkg_list), err) return ret if call and (isinstance(call, list) or isinstance(call, dict)): - ret['result'] = True - ret['changes'] = {'old': [], 'new': pkgs_to_install} - ret['comment'] = 'Package(s) \'{0}\' successfully installed'.format( - ', '.join(pkgs_to_install)) + ret["result"] = True + ret["changes"] = {"old": [], "new": pkgs_to_install} + ret["comment"] = "Package(s) '{0}' successfully installed".format( + ", ".join(pkgs_to_install) + ) else: - ret['result'] = False - ret['comment'] = 'Could not install package(s) \'{0}\''.format( - ', '.join(pkg_list)) + ret["result"] = False + ret["comment"] = "Could not install package(s) '{0}'".format( + ", ".join(pkg_list) + ) return ret def removed(name, dir=None, user=None): - ''' + """ Verify that the given package is not installed. dir @@ -235,39 +243,39 @@ def removed(name, dir=None, user=None): The user to run NPM with .. versionadded:: 0.17.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} try: - installed_pkgs = __salt__['npm.list'](dir=dir, depth=0) + installed_pkgs = __salt__["npm.list"](dir=dir, depth=0) except (CommandExecutionError, CommandNotFoundError) as err: - ret['result'] = False - ret['comment'] = 'Error uninstalling \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error uninstalling '{0}': {1}".format(name, err) return ret if name not in installed_pkgs: - ret['result'] = True - ret['comment'] = 'Package \'{0}\' is not installed'.format(name) + ret["result"] = True + ret["comment"] = "Package '{0}' is not installed".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Package \'{0}\' is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Package '{0}' is set to be removed".format(name) return ret - if __salt__['npm.uninstall'](pkg=name, dir=dir, runas=user): - ret['result'] = True - ret['changes'][name] = 'Removed' - ret['comment'] = 'Package \'{0}\' was successfully removed'.format(name) + if __salt__["npm.uninstall"](pkg=name, dir=dir, runas=user): + ret["result"] = True + ret["changes"][name] = "Removed" + ret["comment"] = "Package '{0}' was successfully removed".format(name) else: - ret['result'] = False - ret['comment'] = 'Error removing package \'{0}\''.format(name) + ret["result"] = False + ret["comment"] = "Error removing package '{0}'".format(name) return ret def bootstrap(name, user=None, silent=True): - ''' + """ Bootstraps a node.js application. Will execute 'npm install --json' on the specified directory. @@ -276,53 +284,53 @@ def bootstrap(name, user=None, silent=True): The user to run NPM with .. versionadded:: 0.17.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if __opts__['test']: + if __opts__["test"]: try: - call = __salt__['npm.install'](dir=name, runas=user, pkg=None, silent=silent, dry_run=True) + call = __salt__["npm.install"]( + dir=name, runas=user, pkg=None, silent=silent, dry_run=True + ) if call: - ret['result'] = None - ret['changes'] = {'old': [], 'new': call} - ret['comment'] = '{0} is set to be bootstrapped'.format(name) + ret["result"] = None + ret["changes"] = {"old": [], "new": call} + ret["comment"] = "{0} is set to be bootstrapped".format(name) else: - ret['result'] = True - ret['comment'] = '{0} is already bootstrapped'.format(name) + ret["result"] = True + ret["comment"] = "{0} is already bootstrapped".format(name) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error Bootstrapping \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error Bootstrapping '{0}': {1}".format(name, err) return ret try: - call = __salt__['npm.install'](dir=name, runas=user, pkg=None, silent=silent) + call = __salt__["npm.install"](dir=name, runas=user, pkg=None, silent=silent) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = False - ret['comment'] = 'Error Bootstrapping \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error Bootstrapping '{0}': {1}".format(name, err) return ret if not call: - ret['result'] = True - ret['comment'] = 'Directory is already bootstrapped' + ret["result"] = True + ret["comment"] = "Directory is already bootstrapped" return ret # npm.install will return a string if it can't parse a JSON result if isinstance(call, six.string_types): - ret['result'] = False - ret['changes'] = call - ret['comment'] = 'Could not bootstrap directory' + ret["result"] = False + ret["changes"] = call + ret["comment"] = "Could not bootstrap directory" else: - ret['result'] = True - ret['changes'] = {name: 'Bootstrapped'} - ret['comment'] = 'Directory was successfully bootstrapped' + ret["result"] = True + ret["changes"] = {name: "Bootstrapped"} + ret["comment"] = "Directory was successfully bootstrapped" return ret -def cache_cleaned(name=None, - user=None, - force=False): - ''' +def cache_cleaned(name=None, user=None, force=False): + """ Ensure that the given package is not cached. If no package is specified, this ensures the entire cache is cleared. @@ -337,42 +345,41 @@ def cache_cleaned(name=None, Force cleaning of cache. Required for npm@5 and greater .. versionadded:: 2016.11.6 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} specific_pkg = None try: - cached_pkgs = __salt__['npm.cache_list'](path=name, runas=user) + cached_pkgs = __salt__["npm.cache_list"](path=name, runas=user) except (CommandExecutionError, CommandNotFoundError) as err: - ret['result'] = False - ret['comment'] = 'Error looking up cached {0}: {1}'.format( - name or 'packages', err) + ret["result"] = False + ret["comment"] = "Error looking up cached {0}: {1}".format( + name or "packages", err + ) return ret if name: - all_cached_pkgs = __salt__['npm.cache_list'](path=None, runas=user) + all_cached_pkgs = __salt__["npm.cache_list"](path=None, runas=user) # The first package is always the cache path cache_root_path = all_cached_pkgs[0] - specific_pkg = '{0}/{1}/'.format(cache_root_path, name) + specific_pkg = "{0}/{1}/".format(cache_root_path, name) if specific_pkg not in cached_pkgs: - ret['result'] = True - ret['comment'] = 'Package {0} is not in the cache'.format(name) + ret["result"] = True + ret["comment"] = "Package {0} is not in the cache".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Cached {0} set to be removed'.format(name or 'packages') + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Cached {0} set to be removed".format(name or "packages") return ret - if __salt__['npm.cache_clean'](path=name, runas=user): - ret['result'] = True - ret['changes'][name or 'cache'] = 'Removed' - ret['comment'] = 'Cached {0} successfully removed'.format( - name or 'packages' - ) + if __salt__["npm.cache_clean"](path=name, runas=user): + ret["result"] = True + ret["changes"][name or "cache"] = "Removed" + ret["comment"] = "Cached {0} successfully removed".format(name or "packages") else: - ret['result'] = False - ret['comment'] = 'Error cleaning cached {0}'.format(name or 'packages') + ret["result"] = False + ret["comment"] = "Error cleaning cached {0}".format(name or "packages") return ret diff --git a/salt/states/ntp.py b/salt/states/ntp.py index e92b7c227af..b6e1e8dec11 100644 --- a/salt/states/ntp.py +++ b/salt/states/ntp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of NTP servers ========================= @@ -14,7 +14,7 @@ This state is used to manage NTP servers. Currently only Windows is supported. - servers: - pool.ntp.org - us.pool.ntp.org -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -26,17 +26,16 @@ import salt.utils.platform # Import 3rd-party libs from salt.ext import six - log = logging.getLogger(__name__) def __virtual__(): - ''' + """ This only supports Windows - ''' + """ if not salt.utils.platform.is_windows(): - return False - return 'ntp' + return (False, "Only Windows supported") + return "ntp" def _check_servers(servers): @@ -50,26 +49,28 @@ def _check_servers(servers): def _get_servers(): try: - return set(__salt__['ntp.get_servers']()) + return set(__salt__["ntp.get_servers"]()) except TypeError: return set([False]) def managed(name, servers=None): - ''' + """ Manage NTP servers servers A list of NTP servers - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'NTP servers already configured as specified'} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "NTP servers already configured as specified", + } if not _check_servers(servers): - ret['result'] = False - ret['comment'] = 'NTP servers must be a list of strings' + ret["result"] = False + ret["comment"] = "NTP servers must be a list of strings" before_servers = _get_servers() desired_servers = set(servers) @@ -77,24 +78,26 @@ def managed(name, servers=None): if before_servers == desired_servers: return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('NTP servers will be updated to: {0}' - .format(', '.join(sorted(desired_servers)))) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "NTP servers will be updated to: {0}".format( + ", ".join(sorted(desired_servers)) + ) return ret - __salt__['ntp.set_servers'](*desired_servers) + __salt__["ntp.set_servers"](*desired_servers) after_servers = _get_servers() if after_servers == desired_servers: - ret['comment'] = 'NTP servers updated' - ret['changes'] = {'old': sorted(before_servers), - 'new': sorted(after_servers)} + ret["comment"] = "NTP servers updated" + ret["changes"] = {"old": sorted(before_servers), "new": sorted(after_servers)} else: - ret['result'] = False - ret['comment'] = 'Failed to update NTP servers' + ret["result"] = False + ret["comment"] = "Failed to update NTP servers" if before_servers != after_servers: - ret['changes'] = {'old': sorted(before_servers), - 'new': sorted(after_servers)} + ret["changes"] = { + "old": sorted(before_servers), + "new": sorted(after_servers), + } return ret diff --git a/salt/states/nxos.py b/salt/states/nxos.py index 71b6bcf5d08..cea40a34568 100644 --- a/salt/states/nxos.py +++ b/salt/states/nxos.py @@ -1,22 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" State module for Cisco NX OS Switches Proxy minions .. versionadded: 2016.11.0 For documentation on setting up the nxos proxy minion look in the documentation for :mod:`salt.proxy.nxos<salt.proxy.nxos>`. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import re def __virtual__(): - return 'nxos.cmd' in __salt__ + if "nxos.cmd" in __salt__: + return True + return (False, "nxos module could not be loaded") -def user_present(name, password=None, roles=None, encrypted=False, crypt_salt=None, algorithm='sha256'): - ''' +def user_present( + name, + password=None, + roles=None, + encrypted=False, + crypt_salt=None, + algorithm="sha256", +): + """ Ensure a user is present with the specified groups name @@ -66,101 +76,102 @@ def user_present(name, password=None, roles=None, encrypted=False, crypt_salt=No - network-admin - vdc-admin - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} change_password = False if password is not None: - change_password = not __salt__['nxos.cmd']('check_password', - username=name, - password=password, - encrypted=encrypted) + change_password = not __salt__["nxos.cmd"]( + "check_password", username=name, password=password, encrypted=encrypted + ) change_roles = False if roles is not None: - cur_roles = __salt__['nxos.cmd']('get_roles', username=name) + cur_roles = __salt__["nxos.cmd"]("get_roles", username=name) change_roles = set(roles) != set(cur_roles) - old_user = __salt__['nxos.cmd']('get_user', username=name) + old_user = __salt__["nxos.cmd"]("get_user", username=name) if not any([change_password, change_roles, not old_user]): - ret['result'] = True - ret['comment'] = 'User already exists' + ret["result"] = True + ret["comment"] = "User already exists" return ret if change_roles is True: remove_roles = set(cur_roles) - set(roles) add_roles = set(roles) - set(cur_roles) - if __opts__['test'] is True: - ret['result'] = None + if __opts__["test"] is True: + ret["result"] = None if not old_user: - ret['comment'] = 'User will be created' + ret["comment"] = "User will be created" if password is not None: - ret['changes']['password'] = True + ret["changes"]["password"] = True if roles is not None: - ret['changes']['role'] = {'add': roles, - 'remove': [], } + ret["changes"]["role"] = { + "add": roles, + "remove": [], + } return ret if change_password is True: - ret['comment'] = 'User will be updated' - ret['changes']['password'] = True + ret["comment"] = "User will be updated" + ret["changes"]["password"] = True if change_roles is True: - ret['comment'] = 'User will be updated' - ret['changes']['roles'] = {'add': list(add_roles), - 'remove': list(remove_roles)} + ret["comment"] = "User will be updated" + ret["changes"]["roles"] = { + "add": list(add_roles), + "remove": list(remove_roles), + } return ret if change_password is True: - new_user = __salt__['nxos.cmd']('set_password', - username=name, - password=password, - encrypted=encrypted, - role=roles[0] if roles else None, - crypt_salt=crypt_salt, - algorithm=algorithm) - ret['changes']['password'] = { - 'new': new_user, - 'old': old_user, + new_user = __salt__["nxos.cmd"]( + "set_password", + username=name, + password=password, + encrypted=encrypted, + role=roles[0] if roles else None, + crypt_salt=crypt_salt, + algorithm=algorithm, + ) + ret["changes"]["password"] = { + "new": new_user, + "old": old_user, } if change_roles is True: for role in add_roles: - __salt__['nxos.cmd']('set_role', username=name, role=role) + __salt__["nxos.cmd"]("set_role", username=name, role=role) for role in remove_roles: - __salt__['nxos.cmd']('unset_role', username=name, role=role) - ret['changes']['roles'] = { - 'new': __salt__['nxos.cmd']('get_roles', username=name), - 'old': cur_roles, + __salt__["nxos.cmd"]("unset_role", username=name, role=role) + ret["changes"]["roles"] = { + "new": __salt__["nxos.cmd"]("get_roles", username=name), + "old": cur_roles, } correct_password = True if password is not None: - correct_password = __salt__['nxos.cmd']('check_password', - username=name, - password=password, - encrypted=encrypted) + correct_password = __salt__["nxos.cmd"]( + "check_password", username=name, password=password, encrypted=encrypted + ) correct_roles = True if roles is not None: - cur_roles = __salt__['nxos.cmd']('get_roles', username=name) + cur_roles = __salt__["nxos.cmd"]("get_roles", username=name) correct_roles = set(roles) != set(cur_roles) if not correct_roles: - ret['comment'] = 'Failed to set correct roles' + ret["comment"] = "Failed to set correct roles" elif not correct_password: - ret['comment'] = 'Failed to set correct password' + ret["comment"] = "Failed to set correct password" else: - ret['comment'] = 'User set correctly' - ret['result'] = True + ret["comment"] = "User set correctly" + ret["result"] = True return ret def user_absent(name): - ''' + """ Ensure a user is not present name @@ -173,41 +184,38 @@ def user_absent(name): delete: nxos.user_absent: - name: daniel - ''' + """ - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - old_user = __salt__['nxos.cmd']('get_user', username=name) + old_user = __salt__["nxos.cmd"]("get_user", username=name) if not old_user: - ret['result'] = True - ret['comment'] = 'User does not exist' + ret["result"] = True + ret["comment"] = "User does not exist" return ret - if __opts__['test'] is True and old_user: - ret['result'] = None - ret['comment'] = 'User will be removed' - ret['changes']['old'] = old_user - ret['changes']['new'] = '' + if __opts__["test"] is True and old_user: + ret["result"] = None + ret["comment"] = "User will be removed" + ret["changes"]["old"] = old_user + ret["changes"]["new"] = "" return ret - __salt__['nxos.cmd']('remove_user', username=name) + __salt__["nxos.cmd"]("remove_user", username=name) - if __salt__['nxos.cmd']('get_user', username=name): - ret['comment'] = 'Failed to remove user' + if __salt__["nxos.cmd"]("get_user", username=name): + ret["comment"] = "Failed to remove user" else: - ret['result'] = True - ret['comment'] = 'User removed' - ret['changes']['old'] = old_user - ret['changes']['new'] = '' + ret["result"] = True + ret["comment"] = "User removed" + ret["changes"]["old"] = old_user + ret["changes"]["new"] = "" return ret def config_present(name): - ''' + """ Ensure a specific configuration line exists in the running config name @@ -228,39 +236,36 @@ def config_present(name): - names: - snmp-server community randoSNMPstringHERE use-acl snmp-acl-ro - snmp-server community AnotherRandomSNMPSTring use-acl snmp-acl-rw - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - matches = __salt__['nxos.cmd']('find', name) + matches = __salt__["nxos.cmd"]("find", name) if matches: - ret['result'] = True - ret['comment'] = 'Config is already set' + ret["result"] = True + ret["comment"] = "Config is already set" - elif __opts__['test'] is True: - ret['result'] = None - ret['comment'] = 'Config will be added' - ret['changes']['new'] = name + elif __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "Config will be added" + ret["changes"]["new"] = name else: - __salt__['nxos.cmd']('add_config', name) - matches = __salt__['nxos.cmd']('find', name) + __salt__["nxos.cmd"]("add_config", name) + matches = __salt__["nxos.cmd"]("find", name) if matches: - ret['result'] = True - ret['comment'] = 'Successfully added config' - ret['changes']['new'] = name + ret["result"] = True + ret["comment"] = "Successfully added config" + ret["changes"]["new"] = name else: - ret['result'] = False - ret['comment'] = 'Failed to add config' + ret["result"] = False + ret["comment"] = "Failed to add config" return ret def config_absent(name): - ''' + """ Ensure a specific configuration line does not exist in the running config name @@ -282,39 +287,36 @@ def config_absent(name): ACLs would be removed because they depend on the existence of the group. - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - matches = __salt__['nxos.cmd']('find', name) + matches = __salt__["nxos.cmd"]("find", name) if not matches: - ret['result'] = True - ret['comment'] = 'Config is already absent' + ret["result"] = True + ret["comment"] = "Config is already absent" - elif __opts__['test'] is True: - ret['result'] = None - ret['comment'] = 'Config will be removed' - ret['changes']['new'] = name + elif __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "Config will be removed" + ret["changes"]["new"] = name else: - __salt__['nxos.cmd']('delete_config', name) - matches = __salt__['nxos.cmd']('find', name) + __salt__["nxos.cmd"]("delete_config", name) + matches = __salt__["nxos.cmd"]("find", name) if not matches: - ret['result'] = True - ret['comment'] = 'Successfully deleted config' - ret['changes']['new'] = name + ret["result"] = True + ret["comment"] = "Successfully deleted config" + ret["changes"]["new"] = name else: - ret['result'] = False - ret['comment'] = 'Failed to delete config' + ret["result"] = False + ret["comment"] = "Failed to delete config" return ret def replace(name, repl, full_match=False): - ''' + """ Replace all instances of a string or full line in the running config name @@ -349,40 +351,39 @@ def replace(name, repl, full_match=False): an exact match of the line, when the group is removed, the ACL is removed, but not readded, because it was not matched. - ''' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} if full_match is False: - search = '^.*{0}.*$'.format(name) + search = "^.*{0}.*$".format(name) else: search = name - matches = __salt__['nxos.cmd']('find', search) + matches = __salt__["nxos.cmd"]("find", search) if not matches: - ret['result'] = True - ret['comment'] = 'Nothing found to replace' + ret["result"] = True + ret["comment"] = "Nothing found to replace" return ret - if __opts__['test'] is True: - ret['result'] = None - ret['comment'] = 'Configs will be changed' - ret['changes']['old'] = matches - ret['changes']['new'] = [re.sub(name, repl, match) for match in matches] + if __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "Configs will be changed" + ret["changes"]["old"] = matches + ret["changes"]["new"] = [re.sub(name, repl, match) for match in matches] return ret - ret['changes'] = __salt__['nxos.cmd']('replace', name, repl, full_match=full_match) + ret["changes"] = __salt__["nxos.cmd"]("replace", name, repl, full_match=full_match) - matches = __salt__['nxos.cmd']('find', search) + matches = __salt__["nxos.cmd"]("find", search) if matches: - ret['result'] = False - ret['comment'] = 'Failed to replace all instances of "{0}"'.format(name) + ret["result"] = False + ret["comment"] = 'Failed to replace all instances of "{0}"'.format(name) else: - ret['result'] = True - ret['comment'] = 'Successfully replaced all instances of "{0}" with "{1}"'.format(name, repl) + ret["result"] = True + ret[ + "comment" + ] = 'Successfully replaced all instances of "{0}" with "{1}"'.format(name, repl) return ret diff --git a/salt/states/openstack_config.py b/salt/states/openstack_config.py index f5d3a33e484..28be0eeedb0 100644 --- a/salt/states/openstack_config.py +++ b/salt/states/openstack_config.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage OpenStack configuration file settings. :maintainer: Jeffrey C. Ollie <jeff@ocjtech.us> @@ -7,31 +7,32 @@ Manage OpenStack configuration file settings. :depends: :platform: linux -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +from salt.exceptions import CommandExecutionError + # Import Salt Libs from salt.ext import six -from salt.exceptions import CommandExecutionError def __virtual__(): - ''' + """ Only load if the openstack_config module is in __salt__ - ''' - if 'openstack_config.get' not in __salt__: + """ + if "openstack_config.get" not in __salt__: + return (False, "openstack_config module could not be loaded") + if "openstack_config.set" not in __salt__: return False - if 'openstack_config.set' not in __salt__: - return False - if 'openstack_config.delete' not in __salt__: + if "openstack_config.delete" not in __salt__: return False return True def present(name, filename, section, value, parameter=None): - ''' + """ Ensure a value is set in an OpenStack configuration file. filename @@ -46,52 +47,47 @@ def present(name, filename, section, value, parameter=None): value The value to set - ''' + """ if parameter is None: parameter = name - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} try: - old_value = __salt__['openstack_config.get'](filename=filename, - section=section, - parameter=parameter) + old_value = __salt__["openstack_config.get"]( + filename=filename, section=section, parameter=parameter + ) if old_value == value: - ret['result'] = True - ret['comment'] = 'The value is already set to the correct value' + ret["result"] = True + ret["comment"] = "The value is already set to the correct value" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Value \'{0}\' is set to be changed to \'{1}\'.'.format( - old_value, - value + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Value '{0}' is set to be changed to '{1}'.".format( + old_value, value ) return ret except CommandExecutionError as err: - if not six.text_type(err).lower().startswith('parameter not found:'): + if not six.text_type(err).lower().startswith("parameter not found:"): raise - __salt__['openstack_config.set'](filename=filename, - section=section, - parameter=parameter, - value=value) + __salt__["openstack_config.set"]( + filename=filename, section=section, parameter=parameter, value=value + ) - ret['changes'] = {'Value': 'Updated'} - ret['result'] = True - ret['comment'] = 'The value has been updated' + ret["changes"] = {"Value": "Updated"} + ret["result"] = True + ret["comment"] = "The value has been updated" return ret def absent(name, filename, section, parameter=None): - ''' + """ Ensure a value is not set in an OpenStack configuration file. filename @@ -103,40 +99,35 @@ def absent(name, filename, section, parameter=None): parameter (optional) The parameter to change. If the parameter is not supplied, the name will be used as the parameter. - ''' + """ if parameter is None: parameter = name - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} try: - old_value = __salt__['openstack_config.get'](filename=filename, - section=section, - parameter=parameter) + old_value = __salt__["openstack_config.get"]( + filename=filename, section=section, parameter=parameter + ) except CommandExecutionError as err: - if six.text_type(err).lower().startswith('parameter not found:'): - ret['result'] = True - ret['comment'] = 'The value is already absent' + if six.text_type(err).lower().startswith("parameter not found:"): + ret["result"] = True + ret["comment"] = "The value is already absent" return ret raise - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Value \'{0}\' is set to be deleted.'.format( - old_value - ) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Value '{0}' is set to be deleted.".format(old_value) return ret - __salt__['openstack_config.delete'](filename=filename, - section=section, - parameter=parameter) + __salt__["openstack_config.delete"]( + filename=filename, section=section, parameter=parameter + ) - ret['changes'] = {'Value': 'Deleted'} - ret['result'] = True - ret['comment'] = 'The value has been deleted' + ret["changes"] = {"Value": "Deleted"} + ret["result"] = True + ret["comment"] = "The value has been deleted" return ret diff --git a/salt/states/openvswitch_bridge.py b/salt/states/openvswitch_bridge.py index 2b3df8432a2..ae7ca522b0a 100644 --- a/salt/states/openvswitch_bridge.py +++ b/salt/states/openvswitch_bridge.py @@ -1,111 +1,117 @@ # -*- coding: utf-8 -*- -''' +""" Management of Open vSwitch bridges. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only make these states available if Open vSwitch module is available. - ''' - return 'openvswitch.bridge_create' in __salt__ + """ + if "openvswitch.bridge_create" in __salt__: + return True + return (False, "openvswitch module could not be loaded") def present(name): - ''' + """ Ensures that the named bridge exists, eventually creates it. Args: name: The name of the bridge. - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_bridge_created = 'Bridge {0} created.'.format(name) - comment_bridge_notcreated = 'Unable to create bridge: {0}.'.format(name) - comment_bridge_exists = 'Bridge {0} already exists.'.format(name) - changes_bridge_created = {name: {'old': 'Bridge {0} does not exist.'.format(name), - 'new': 'Bridge {0} created'.format(name), - } - } + comment_bridge_created = "Bridge {0} created.".format(name) + comment_bridge_notcreated = "Unable to create bridge: {0}.".format(name) + comment_bridge_exists = "Bridge {0} already exists.".format(name) + changes_bridge_created = { + name: { + "old": "Bridge {0} does not exist.".format(name), + "new": "Bridge {0} created".format(name), + } + } - bridge_exists = __salt__['openvswitch.bridge_exists'](name) + bridge_exists = __salt__["openvswitch.bridge_exists"](name) # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if bridge_exists: - ret['result'] = True - ret['comment'] = comment_bridge_exists + ret["result"] = True + ret["comment"] = comment_bridge_exists else: - ret['result'] = None - ret['comment'] = comment_bridge_created + ret["result"] = None + ret["comment"] = comment_bridge_created return ret if bridge_exists: - ret['result'] = True - ret['comment'] = comment_bridge_exists + ret["result"] = True + ret["comment"] = comment_bridge_exists else: - bridge_create = __salt__['openvswitch.bridge_create'](name) + bridge_create = __salt__["openvswitch.bridge_create"](name) if bridge_create: - ret['result'] = True - ret['comment'] = comment_bridge_created - ret['changes'] = changes_bridge_created + ret["result"] = True + ret["comment"] = comment_bridge_created + ret["changes"] = changes_bridge_created else: - ret['result'] = False - ret['comment'] = comment_bridge_notcreated + ret["result"] = False + ret["comment"] = comment_bridge_notcreated return ret def absent(name): - ''' + """ Ensures that the named bridge does not exist, eventually deletes it. Args: name: The name of the bridge. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_bridge_deleted = 'Bridge {0} deleted.'.format(name) - comment_bridge_notdeleted = 'Unable to delete bridge: {0}.'.format(name) - comment_bridge_notexists = 'Bridge {0} does not exist.'.format(name) - changes_bridge_deleted = {name: {'old': 'Bridge {0} exists.'.format(name), - 'new': 'Bridge {0} deleted.'.format(name), - } - } + comment_bridge_deleted = "Bridge {0} deleted.".format(name) + comment_bridge_notdeleted = "Unable to delete bridge: {0}.".format(name) + comment_bridge_notexists = "Bridge {0} does not exist.".format(name) + changes_bridge_deleted = { + name: { + "old": "Bridge {0} exists.".format(name), + "new": "Bridge {0} deleted.".format(name), + } + } - bridge_exists = __salt__['openvswitch.bridge_exists'](name) + bridge_exists = __salt__["openvswitch.bridge_exists"](name) # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if not bridge_exists: - ret['result'] = True - ret['comment'] = comment_bridge_notexists + ret["result"] = True + ret["comment"] = comment_bridge_notexists else: - ret['result'] = None - ret['comment'] = comment_bridge_deleted + ret["result"] = None + ret["comment"] = comment_bridge_deleted return ret if not bridge_exists: - ret['result'] = True - ret['comment'] = comment_bridge_notexists + ret["result"] = True + ret["comment"] = comment_bridge_notexists else: - bridge_delete = __salt__['openvswitch.bridge_delete'](name) + bridge_delete = __salt__["openvswitch.bridge_delete"](name) if bridge_delete: - ret['result'] = True - ret['comment'] = comment_bridge_deleted - ret['changes'] = changes_bridge_deleted + ret["result"] = True + ret["comment"] = comment_bridge_deleted + ret["changes"] = changes_bridge_deleted else: - ret['result'] = False - ret['comment'] = comment_bridge_notdeleted + ret["result"] = False + ret["comment"] = comment_bridge_notdeleted return ret diff --git a/salt/states/openvswitch_port.py b/salt/states/openvswitch_port.py index 6004c83355a..f5872fbfeb4 100644 --- a/salt/states/openvswitch_port.py +++ b/salt/states/openvswitch_port.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Management of Open vSwitch ports. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -11,14 +11,18 @@ from salt.ext import six def __virtual__(): - ''' + """ Only make these states available if Open vSwitch module is available. - ''' - return 'openvswitch.port_add' in __salt__ + """ + if "openvswitch.port_add" in __salt__: + return True + return (False, "openvswitch module could not be loaded") -def present(name, bridge, tunnel_type=None, id=None, remote=None, dst_port=None, internal=False): - ''' +def present( + name, bridge, tunnel_type=None, id=None, remote=None, dst_port=None, internal=False +): + """ Ensures that the named port exists on bridge, eventually creates it. Args: @@ -30,249 +34,342 @@ def present(name, bridge, tunnel_type=None, id=None, remote=None, dst_port=None, dst_port: Port to use when creating tunnelport in the switch. internal: Create an internal port if one does not exist - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} - tunnel_types = ('vlan', 'vxlan', 'gre') + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + tunnel_types = ("vlan", "vxlan", "gre") if tunnel_type and tunnel_type not in tunnel_types: - raise TypeError('The optional type argument must be one of these values: {0}.'.format( - six.text_type(tunnel_types)) + raise TypeError( + "The optional type argument must be one of these values: {0}.".format( + six.text_type(tunnel_types) + ) ) - bridge_exists = __salt__['openvswitch.bridge_exists'](bridge) + bridge_exists = __salt__["openvswitch.bridge_exists"](bridge) port_list = [] if bridge_exists: - port_list = __salt__['openvswitch.port_list'](bridge) + port_list = __salt__["openvswitch.port_list"](bridge) # Comment and change messages comments = {} - comments['comment_bridge_notexists'] = 'Bridge {0} does not exist.'.format(bridge) - comments['comment_port_exists'] = 'Port {0} already exists.'.format(name) - comments['comment_port_created'] = 'Port {0} created on bridge {1}.'.format(name, bridge) - comments['comment_port_notcreated'] = 'Unable to create port {0} on bridge {1}.'.format(name, bridge) - comments['changes_port_created'] = {name: {'old': 'No port named {0} present.'.format(name), - 'new': 'Created port {1} on bridge {0}.'.format(bridge, name), - } - } - comments['comment_port_internal'] = 'Port {0} already exists, but interface type has been changed to internal.'.format(name) - comments['changes_port_internal'] = {'internal': {'old': False, 'new': True}} - comments['comment_port_internal_not_changed'] = 'Port {0} already exists, but the interface type could not be changed to internal.'.format(name) + comments["comment_bridge_notexists"] = "Bridge {0} does not exist.".format(bridge) + comments["comment_port_exists"] = "Port {0} already exists.".format(name) + comments["comment_port_created"] = "Port {0} created on bridge {1}.".format( + name, bridge + ) + comments[ + "comment_port_notcreated" + ] = "Unable to create port {0} on bridge {1}.".format(name, bridge) + comments["changes_port_created"] = { + name: { + "old": "No port named {0} present.".format(name), + "new": "Created port {1} on bridge {0}.".format(bridge, name), + } + } + comments[ + "comment_port_internal" + ] = "Port {0} already exists, but interface type has been changed to internal.".format( + name + ) + comments["changes_port_internal"] = {"internal": {"old": False, "new": True}} + comments[ + "comment_port_internal_not_changed" + ] = "Port {0} already exists, but the interface type could not be changed to internal.".format( + name + ) if tunnel_type: - comments['comment_invalid_ip'] = 'Remote is not valid ip address.' + comments["comment_invalid_ip"] = "Remote is not valid ip address." if tunnel_type == "vlan": - comments['comment_vlan_invalid_id'] = 'VLANs id must be between 0 and 4095.' - comments['comment_vlan_invalid_name'] = 'Could not find network interface {0}.'.format(name) - comments['comment_vlan_port_exists'] = 'Port {0} with access to VLAN {1} already exists on bridge {2}.'.format(name, id, bridge) - comments['comment_vlan_created'] = 'Created port {0} with access to VLAN {1} on bridge {2}.'.format(name, id, bridge) - comments['comment_vlan_notcreated'] = 'Unable to create port {0} with access to VLAN {1} on ' \ - 'bridge {2}.'.format(name, id, bridge) - comments['changes_vlan_created'] = {name: {'old': 'No port named {0} with access to VLAN {1} present on ' - 'bridge {2} present.'.format(name, id, bridge), - 'new': 'Created port {1} with access to VLAN {2} on ' - 'bridge {0}.'.format(bridge, name, id), - } - } + comments["comment_vlan_invalid_id"] = "VLANs id must be between 0 and 4095." + comments[ + "comment_vlan_invalid_name" + ] = "Could not find network interface {0}.".format(name) + comments[ + "comment_vlan_port_exists" + ] = "Port {0} with access to VLAN {1} already exists on bridge {2}.".format( + name, id, bridge + ) + comments[ + "comment_vlan_created" + ] = "Created port {0} with access to VLAN {1} on bridge {2}.".format( + name, id, bridge + ) + comments["comment_vlan_notcreated"] = ( + "Unable to create port {0} with access to VLAN {1} on " + "bridge {2}.".format(name, id, bridge) + ) + comments["changes_vlan_created"] = { + name: { + "old": "No port named {0} with access to VLAN {1} present on " + "bridge {2} present.".format(name, id, bridge), + "new": "Created port {1} with access to VLAN {2} on " + "bridge {0}.".format(bridge, name, id), + } + } elif tunnel_type == "gre": - comments['comment_gre_invalid_id'] = 'Id of GRE tunnel must be an unsigned 32-bit integer.' - comments['comment_gre_interface_exists'] = 'GRE tunnel interface {0} with rempte ip {1} and key {2} ' \ - 'already exists on bridge {3}.'.format(name, remote, id, bridge) - comments['comment_gre_created'] = 'Created GRE tunnel interface {0} with remote ip {1} and key {2} ' \ - 'on bridge {3}.'.format(name, remote, id, bridge) - comments['comment_gre_notcreated'] = 'Unable to create GRE tunnel interface {0} with remote ip {1} and key {2} ' \ - 'on bridge {3}.'.format(name, remote, id, bridge) - comments['changes_gre_created'] = {name: {'old': 'No GRE tunnel interface {0} with remote ip {1} and key {2} ' - 'on bridge {3} present.'.format(name, remote, id, bridge), - 'new': 'Created GRE tunnel interface {0} with remote ip {1} and key {2} ' - 'on bridge {3}.'.format(name, remote, id, bridge), - } - } + comments[ + "comment_gre_invalid_id" + ] = "Id of GRE tunnel must be an unsigned 32-bit integer." + comments["comment_gre_interface_exists"] = ( + "GRE tunnel interface {0} with rempte ip {1} and key {2} " + "already exists on bridge {3}.".format(name, remote, id, bridge) + ) + comments["comment_gre_created"] = ( + "Created GRE tunnel interface {0} with remote ip {1} and key {2} " + "on bridge {3}.".format(name, remote, id, bridge) + ) + comments["comment_gre_notcreated"] = ( + "Unable to create GRE tunnel interface {0} with remote ip {1} and key {2} " + "on bridge {3}.".format(name, remote, id, bridge) + ) + comments["changes_gre_created"] = { + name: { + "old": "No GRE tunnel interface {0} with remote ip {1} and key {2} " + "on bridge {3} present.".format(name, remote, id, bridge), + "new": "Created GRE tunnel interface {0} with remote ip {1} and key {2} " + "on bridge {3}.".format(name, remote, id, bridge), + } + } elif tunnel_type == "vxlan": - comments['comment_dstport'] = ' (dst_port' + six.text_type(dst_port) + ')' if 0 < dst_port <= 65535 else '' - comments['comment_vxlan_invalid_id'] = 'Id of VXLAN tunnel must be an unsigned 64-bit integer.' - comments['comment_vxlan_interface_exists'] = 'VXLAN tunnel interface {0} with rempte ip {1} and key {2} ' \ - 'already exists on bridge {3}{4}.'.format(name, remote, id, bridge, comments['comment_dstport']) - comments['comment_vxlan_created'] = 'Created VXLAN tunnel interface {0} with remote ip {1} and key {2} ' \ - 'on bridge {3}{4}.'.format(name, remote, id, bridge, comments['comment_dstport']) - comments['comment_vxlan_notcreated'] = 'Unable to create VXLAN tunnel interface {0} with remote ip {1} and key {2} ' \ - 'on bridge {3}{4}.'.format(name, remote, id, bridge, comments['comment_dstport']) - comments['changes_vxlan_created'] = {name: {'old': 'No VXLAN tunnel interface {0} with remote ip {1} and key {2} ' - 'on bridge {3}{4} present.'.format(name, remote, id, bridge, comments['comment_dstport']), - 'new': 'Created VXLAN tunnel interface {0} with remote ip {1} and key {2} ' - 'on bridge {3}{4}.'.format(name, remote, id, bridge, comments['comment_dstport']), - } - } + comments["comment_dstport"] = ( + " (dst_port" + six.text_type(dst_port) + ")" + if 0 < dst_port <= 65535 + else "" + ) + comments[ + "comment_vxlan_invalid_id" + ] = "Id of VXLAN tunnel must be an unsigned 64-bit integer." + comments["comment_vxlan_interface_exists"] = ( + "VXLAN tunnel interface {0} with rempte ip {1} and key {2} " + "already exists on bridge {3}{4}.".format( + name, remote, id, bridge, comments["comment_dstport"] + ) + ) + comments["comment_vxlan_created"] = ( + "Created VXLAN tunnel interface {0} with remote ip {1} and key {2} " + "on bridge {3}{4}.".format( + name, remote, id, bridge, comments["comment_dstport"] + ) + ) + comments["comment_vxlan_notcreated"] = ( + "Unable to create VXLAN tunnel interface {0} with remote ip {1} and key {2} " + "on bridge {3}{4}.".format( + name, remote, id, bridge, comments["comment_dstport"] + ) + ) + comments["changes_vxlan_created"] = { + name: { + "old": "No VXLAN tunnel interface {0} with remote ip {1} and key {2} " + "on bridge {3}{4} present.".format( + name, remote, id, bridge, comments["comment_dstport"] + ), + "new": "Created VXLAN tunnel interface {0} with remote ip {1} and key {2} " + "on bridge {3}{4}.".format( + name, remote, id, bridge, comments["comment_dstport"] + ), + } + } # Check VLANs attributes def _check_vlan(): - tag = __salt__['openvswitch.port_get_tag'](name) - interfaces = __salt__['network.interfaces']() + tag = __salt__["openvswitch.port_get_tag"](name) + interfaces = __salt__["network.interfaces"]() if not 0 <= id <= 4095: - ret['result'] = False - ret['comment'] = comments['comment_vlan_invalid_id'] + ret["result"] = False + ret["comment"] = comments["comment_vlan_invalid_id"] elif not internal and name not in interfaces: - ret['result'] = False - ret['comment'] = comments['comment_vlan_invalid_name'] + ret["result"] = False + ret["comment"] = comments["comment_vlan_invalid_name"] elif tag and name in port_list: try: if int(tag[0]) == id: - ret['result'] = True - ret['comment'] = comments['comment_vlan_port_exists'] + ret["result"] = True + ret["comment"] = comments["comment_vlan_port_exists"] except (ValueError, KeyError): pass # Check GRE tunnels attributes def _check_gre(): - interface_options = __salt__['openvswitch.interface_get_options'](name) - interface_type = __salt__['openvswitch.interface_get_type'](name) - if not 0 <= id <= 2**32: - ret['result'] = False - ret['comment'] = comments['comment_gre_invalid_id'] - elif not __salt__['dig.check_ip'](remote): - ret['result'] = False - ret['comment'] = comments['comment_invalid_ip'] + interface_options = __salt__["openvswitch.interface_get_options"](name) + interface_type = __salt__["openvswitch.interface_get_type"](name) + if not 0 <= id <= 2 ** 32: + ret["result"] = False + ret["comment"] = comments["comment_gre_invalid_id"] + elif not __salt__["dig.check_ip"](remote): + ret["result"] = False + ret["comment"] = comments["comment_invalid_ip"] elif interface_options and interface_type and name in port_list: - interface_attroptions = '{key=\"' + six.text_type(id) + '\", remote_ip=\"' + six.text_type(remote) + '\"}' + interface_attroptions = ( + '{key="' + + six.text_type(id) + + '", remote_ip="' + + six.text_type(remote) + + '"}' + ) try: - if interface_type[0] == 'gre' and interface_options[0] == interface_attroptions: - ret['result'] = True - ret['comment'] = comments['comment_gre_interface_exists'] + if ( + interface_type[0] == "gre" + and interface_options[0] == interface_attroptions + ): + ret["result"] = True + ret["comment"] = comments["comment_gre_interface_exists"] except KeyError: pass # Check VXLAN tunnels attributes def _check_vxlan(): - interface_options = __salt__['openvswitch.interface_get_options'](name) - interface_type = __salt__['openvswitch.interface_get_type'](name) - if not 0 <= id <= 2**64: - ret['result'] = False - ret['comment'] = comments['comment_vxlan_invalid_id'] - elif not __salt__['dig.check_ip'](remote): - ret['result'] = False - ret['comment'] = comments['comment_invalid_ip'] + interface_options = __salt__["openvswitch.interface_get_options"](name) + interface_type = __salt__["openvswitch.interface_get_type"](name) + if not 0 <= id <= 2 ** 64: + ret["result"] = False + ret["comment"] = comments["comment_vxlan_invalid_id"] + elif not __salt__["dig.check_ip"](remote): + ret["result"] = False + ret["comment"] = comments["comment_invalid_ip"] elif interface_options and interface_type and name in port_list: - opt_port = 'dst_port=\"' + six.text_type(dst_port) + '\", ' if 0 < dst_port <= 65535 else '' - interface_attroptions = '{{{0}key=\"'.format(opt_port) + six.text_type(id) + '\", remote_ip=\"' + six.text_type(remote) + '\"}' + opt_port = ( + 'dst_port="' + six.text_type(dst_port) + '", ' + if 0 < dst_port <= 65535 + else "" + ) + interface_attroptions = ( + '{{{0}key="'.format(opt_port) + + six.text_type(id) + + '", remote_ip="' + + six.text_type(remote) + + '"}' + ) try: - if interface_type[0] == 'vxlan' and interface_options[0] == interface_attroptions: - ret['result'] = True - ret['comment'] = comments['comment_vxlan_interface_exists'] + if ( + interface_type[0] == "vxlan" + and interface_options[0] == interface_attroptions + ): + ret["result"] = True + ret["comment"] = comments["comment_vxlan_interface_exists"] except KeyError: pass # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if bridge_exists: - if tunnel_type == 'vlan': + if tunnel_type == "vlan": _check_vlan() - if not ret['comment']: - ret['result'] = None - ret['comment'] = comments['comment_vlan_created'] - elif tunnel_type == 'vxlan': + if not ret["comment"]: + ret["result"] = None + ret["comment"] = comments["comment_vlan_created"] + elif tunnel_type == "vxlan": _check_vxlan() - if not ret['comment']: - ret['result'] = None - ret['comment'] = comments['comment_vxlan_created'] - elif tunnel_type == 'gre': + if not ret["comment"]: + ret["result"] = None + ret["comment"] = comments["comment_vxlan_created"] + elif tunnel_type == "gre": _check_gre() - if not ret['comment']: - ret['result'] = None - ret['comment'] = comments['comment_gre_created'] + if not ret["comment"]: + ret["result"] = None + ret["comment"] = comments["comment_gre_created"] else: if name in port_list: - ret['result'] = True - current_type = __salt__['openvswitch.interface_get_type']( - name) + ret["result"] = True + current_type = __salt__["openvswitch.interface_get_type"](name) # The interface type is returned as a single-element list. - if internal and (current_type != ['internal']): - ret['comment'] = comments['comment_port_internal'] + if internal and (current_type != ["internal"]): + ret["comment"] = comments["comment_port_internal"] else: - ret['comment'] = comments['comment_port_exists'] + ret["comment"] = comments["comment_port_exists"] else: - ret['result'] = None - ret['comment'] = comments['comment_port_created'] + ret["result"] = None + ret["comment"] = comments["comment_port_created"] else: - ret['result'] = None - ret['comment'] = comments['comment_bridge_notexists'] + ret["result"] = None + ret["comment"] = comments["comment_bridge_notexists"] return ret if bridge_exists: - if tunnel_type == 'vlan': + if tunnel_type == "vlan": _check_vlan() - if not ret['comment']: - port_create_vlan = __salt__['openvswitch.port_create_vlan'](bridge, name, id, internal) + if not ret["comment"]: + port_create_vlan = __salt__["openvswitch.port_create_vlan"]( + bridge, name, id, internal + ) if port_create_vlan: - ret['result'] = True - ret['comment'] = comments['comment_vlan_created'] - ret['changes'] = comments['changes_vlan_created'] + ret["result"] = True + ret["comment"] = comments["comment_vlan_created"] + ret["changes"] = comments["changes_vlan_created"] else: - ret['result'] = False - ret['comment'] = comments['comment_vlan_notcreated'] - elif tunnel_type == 'vxlan': + ret["result"] = False + ret["comment"] = comments["comment_vlan_notcreated"] + elif tunnel_type == "vxlan": _check_vxlan() - if not ret['comment']: - port_create_vxlan = __salt__['openvswitch.port_create_vxlan'](bridge, name, id, remote, dst_port) + if not ret["comment"]: + port_create_vxlan = __salt__["openvswitch.port_create_vxlan"]( + bridge, name, id, remote, dst_port + ) if port_create_vxlan: - ret['result'] = True - ret['comment'] = comments['comment_vxlan_created'] - ret['changes'] = comments['changes_vxlan_created'] + ret["result"] = True + ret["comment"] = comments["comment_vxlan_created"] + ret["changes"] = comments["changes_vxlan_created"] else: - ret['result'] = False - ret['comment'] = comments['comment_vxlan_notcreated'] - elif tunnel_type == 'gre': + ret["result"] = False + ret["comment"] = comments["comment_vxlan_notcreated"] + elif tunnel_type == "gre": _check_gre() - if not ret['comment']: - port_create_gre = __salt__['openvswitch.port_create_gre'](bridge, name, id, remote) + if not ret["comment"]: + port_create_gre = __salt__["openvswitch.port_create_gre"]( + bridge, name, id, remote + ) if port_create_gre: - ret['result'] = True - ret['comment'] = comments['comment_gre_created'] - ret['changes'] = comments['changes_gre_created'] + ret["result"] = True + ret["comment"] = comments["comment_gre_created"] + ret["changes"] = comments["changes_gre_created"] else: - ret['result'] = False - ret['comment'] = comments['comment_gre_notcreated'] + ret["result"] = False + ret["comment"] = comments["comment_gre_notcreated"] else: if name in port_list: - current_type = __salt__['openvswitch.interface_get_type'](name) + current_type = __salt__["openvswitch.interface_get_type"](name) # The interface type is returned as a single-element list. - if internal and (current_type != ['internal']): + if internal and (current_type != ["internal"]): # We do not have a direct way of only setting the interface # type to internal, so we add the port with the --may-exist # option. - port_add = __salt__['openvswitch.port_add']( - bridge, name, may_exist=True, internal=internal) + port_add = __salt__["openvswitch.port_add"]( + bridge, name, may_exist=True, internal=internal + ) if port_add: - ret['result'] = True - ret['comment'] = comments['comment_port_internal'] - ret['changes'] = comments['changes_port_internal'] + ret["result"] = True + ret["comment"] = comments["comment_port_internal"] + ret["changes"] = comments["changes_port_internal"] else: - ret['result'] = False - ret['comment'] = comments[ - 'comment_port_internal_not_changed'] + ret["result"] = False + ret["comment"] = comments["comment_port_internal_not_changed"] else: - ret['result'] = True - ret['comment'] = comments['comment_port_exists'] + ret["result"] = True + ret["comment"] = comments["comment_port_exists"] else: - port_add = __salt__['openvswitch.port_add'](bridge, name, internal=internal) + port_add = __salt__["openvswitch.port_add"]( + bridge, name, internal=internal + ) if port_add: - ret['result'] = True - ret['comment'] = comments['comment_port_created'] - ret['changes'] = comments['changes_port_created'] + ret["result"] = True + ret["comment"] = comments["comment_port_created"] + ret["changes"] = comments["changes_port_created"] else: - ret['result'] = False - ret['comment'] = comments['comment_port_notcreated'] + ret["result"] = False + ret["comment"] = comments["comment_port_notcreated"] else: - ret['result'] = False - ret['comment'] = comments['comment_bridge_notexists'] + ret["result"] = False + ret["comment"] = comments["comment_bridge_notexists"] return ret def absent(name, bridge=None): - ''' + """ Ensures that the named port exists on bridge, eventually deletes it. If bridge is not set, port is removed from whatever bridge contains it. @@ -280,13 +377,13 @@ def absent(name, bridge=None): name: The name of the port. bridge: The name of the bridge. - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} bridge_exists = False if bridge: - bridge_exists = __salt__['openvswitch.bridge_exists'](bridge) + bridge_exists = __salt__["openvswitch.bridge_exists"](bridge) if bridge_exists: - port_list = __salt__['openvswitch.port_list'](bridge) + port_list = __salt__["openvswitch.port_list"](bridge) else: port_list = () else: @@ -294,46 +391,50 @@ def absent(name, bridge=None): # Comment and change messages comments = {} - comments['comment_bridge_notexists'] = 'Bridge {0} does not exist.'.format(bridge) - comments['comment_port_notexists'] = 'Port {0} does not exist on bridge {1}.'.format(name, bridge) - comments['comment_port_deleted'] = 'Port {0} deleted.'.format(name) - comments['comment_port_notdeleted'] = 'Unable to delete port {0}.'.format(name) - comments['changes_port_deleted'] = {name: {'old': 'Port named {0} may exist.'.format(name), - 'new': 'Deleted port {0}.'.format(name), - } - } + comments["comment_bridge_notexists"] = "Bridge {0} does not exist.".format(bridge) + comments[ + "comment_port_notexists" + ] = "Port {0} does not exist on bridge {1}.".format(name, bridge) + comments["comment_port_deleted"] = "Port {0} deleted.".format(name) + comments["comment_port_notdeleted"] = "Unable to delete port {0}.".format(name) + comments["changes_port_deleted"] = { + name: { + "old": "Port named {0} may exist.".format(name), + "new": "Deleted port {0}.".format(name), + } + } # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if bridge and not bridge_exists: - ret['result'] = None - ret['comment'] = comments['comment_bridge_notexists'] + ret["result"] = None + ret["comment"] = comments["comment_bridge_notexists"] elif name not in port_list: - ret['result'] = True - ret['comment'] = comments['comment_port_notexists'] + ret["result"] = True + ret["comment"] = comments["comment_port_notexists"] else: - ret['result'] = None - ret['comment'] = comments['comment_port_deleted'] + ret["result"] = None + ret["comment"] = comments["comment_port_deleted"] return ret if bridge and not bridge_exists: - ret['result'] = False - ret['comment'] = comments['comment_bridge_notexists'] + ret["result"] = False + ret["comment"] = comments["comment_bridge_notexists"] elif name not in port_list: - ret['result'] = True - ret['comment'] = comments['comment_port_notexists'] + ret["result"] = True + ret["comment"] = comments["comment_port_notexists"] else: if bridge: - port_remove = __salt__['openvswitch.port_remove'](br=bridge, port=name) + port_remove = __salt__["openvswitch.port_remove"](br=bridge, port=name) else: - port_remove = __salt__['openvswitch.port_remove'](br=None, port=name) + port_remove = __salt__["openvswitch.port_remove"](br=None, port=name) if port_remove: - ret['result'] = True - ret['comment'] = comments['comment_port_deleted'] - ret['changes'] = comments['changes_port_deleted'] + ret["result"] = True + ret["comment"] = comments["comment_port_deleted"] + ret["changes"] = comments["changes_port_deleted"] else: - ret['result'] = False - ret['comment'] = comments['comment_port_notdeleted'] + ret["result"] = False + ret["comment"] = comments["comment_port_notdeleted"] return ret diff --git a/salt/states/opsgenie.py b/salt/states/opsgenie.py index 4605d320c9a..1bc9fd008d4 100644 --- a/salt/states/opsgenie.py +++ b/salt/states/opsgenie.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Create/Close an alert in OpsGenie ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -31,11 +31,12 @@ during state runs. - require: - disk: used_space -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging + import inspect +import logging # Import Salt libs import salt.exceptions @@ -44,7 +45,7 @@ log = logging.getLogger(__name__) def create_alert(name=None, api_key=None, reason=None, action_type="Create"): - ''' + """ Create an alert in OpsGenie. Example usage with Salt's requisites and other global state arguments could be found above. @@ -67,61 +68,57 @@ def create_alert(name=None, api_key=None, reason=None, action_type="Create"): OpsGenie supports the default values Create/Close for action_type. You can customize this field with OpsGenie's custom actions for other purposes like adding notes or acknowledging alerts. - ''' + """ _, _, _, values = inspect.getargvalues(inspect.currentframe()) log.info("Arguments values: %s", values) - ret = { - 'result': '', - 'name': '', - 'changes': '', - 'comment': '' - } + ret = {"result": "", "name": "", "changes": "", "comment": ""} if api_key is None or reason is None: - raise salt.exceptions.SaltInvocationError( - 'API Key or Reason cannot be None.') + raise salt.exceptions.SaltInvocationError("API Key or Reason cannot be None.") - if __opts__['test'] is True: - ret[ - 'comment'] = 'Test: {0} alert request will be processed ' \ - 'using the API Key="{1}".'.format(action_type, api_key) + if __opts__["test"] is True: + ret["comment"] = ( + "Test: {0} alert request will be processed " + 'using the API Key="{1}".'.format(action_type, api_key) + ) # Return ``None`` when running with ``test=true``. - ret['result'] = None + ret["result"] = None return ret - response_status_code, response_text = __salt__['opsgenie.post_data']( - api_key=api_key, - name=name, - reason=reason, - action_type=action_type + response_status_code, response_text = __salt__["opsgenie.post_data"]( + api_key=api_key, name=name, reason=reason, action_type=action_type ) if 200 <= response_status_code < 300: log.info( "POST Request has succeeded with message: %s status code: %s", - response_text, response_status_code) - ret[ - 'comment'] = 'Test: {0} alert request will be processed' \ - ' using the API Key="{1}".'.format( - action_type, - api_key) - ret['result'] = True + response_text, + response_status_code, + ) + ret["comment"] = ( + "Test: {0} alert request will be processed" + ' using the API Key="{1}".'.format(action_type, api_key) + ) + ret["result"] = True else: log.error( "POST Request has failed with error: %s status code: %s", - response_text, response_status_code) - ret['result'] = False + response_text, + response_status_code, + ) + ret["result"] = False return ret -def close_alert(name=None, api_key=None, reason="Conditions are met.", - action_type="Close"): - ''' +def close_alert( + name=None, api_key=None, reason="Conditions are met.", action_type="Close" +): + """ Close an alert in OpsGenie. It's a wrapper function for create_alert. Example usage with Salt's requisites and other global state arguments could be found above. @@ -145,9 +142,8 @@ def close_alert(name=None, api_key=None, reason="Conditions are met.", OpsGenie supports the default values Create/Close for action_type. You can customize this field with OpsGenie's custom actions for other purposes like adding notes or acknowledging alerts. - ''' + """ if name is None: - raise salt.exceptions.SaltInvocationError( - 'Name cannot be None.') + raise salt.exceptions.SaltInvocationError("Name cannot be None.") return create_alert(name, api_key, reason, action_type) diff --git a/salt/states/pagerduty.py b/salt/states/pagerduty.py index f4efbbabdca..3dc8f25715a 100644 --- a/salt/states/pagerduty.py +++ b/salt/states/pagerduty.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Create an Event in PagerDuty ============================ @@ -16,21 +16,23 @@ runs. - details: 'This is a much more detailed message' - service_key: 9abcd123456789efabcde362783cdbaf - profile: my-pagerduty-account -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the pygerduty module is available in __salt__ - ''' - return 'pagerduty' if 'pagerduty.create_event' in __salt__ else False + """ + if "pagerduty.create_event" in __salt__: + return "pagerduty" + return (False, "pagerduty module could not be loaded") def create_event(name, details, service_key, profile): - ''' + """ Create an event on the PagerDuty service .. code-block:: yaml @@ -56,20 +58,14 @@ def create_event(name, details, service_key, profile): profile This refers to the configuration profile to use to connect to the PagerDuty service. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} - if __opts__['test']: - ret['comment'] = 'Need to create event: {0}'.format(name) + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + if __opts__["test"]: + ret["comment"] = "Need to create event: {0}".format(name) return ret - __salt__['pagerduty.create_event']( - description=name, - details=details, - service_key=service_key, - profile=profile, + __salt__["pagerduty.create_event"]( + description=name, details=details, service_key=service_key, profile=profile, ) - ret['result'] = True - ret['comment'] = 'Created event: {0}'.format(name) + ret["result"] = True + ret["comment"] = "Created event: {0}".format(name) return ret diff --git a/salt/states/pagerduty_escalation_policy.py b/salt/states/pagerduty_escalation_policy.py index 1fc925b4f44..ae18c07ed5d 100644 --- a/salt/states/pagerduty_escalation_policy.py +++ b/salt/states/pagerduty_escalation_policy.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage PagerDuty escalation policies. @@ -33,21 +33,23 @@ For example: - type: user id: 'bruce+test4@lyft.com' escalation_delay_in_minutes: 15 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the pygerduty module is available in __salt__ - ''' - return 'pagerduty_escalation_policy' if 'pagerduty_util.get_resource' in __salt__ else False + """ + if "pagerduty_util.get_resource" in __salt__: + return "pagerduty_escalation_policy" + return (False, "pagerduty_util module could not be loaded") -def present(profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def present(profile="pagerduty", subdomain=None, api_key=None, **kwargs): + """ Ensure that a pagerduty escalation policy exists. Will create or update as needed. This method accepts as args everything defined in @@ -69,71 +71,74 @@ def present(profile='pagerduty', subdomain=None, api_key=None, **kwargs): In this example, 'Bruce Sherrod' will be looked up and replaced with the PagerDuty id (usually a 7 digit all-caps string, e.g. PX6GQL7) - ''' + """ # for convenience, we accept id, name, or email for users # and we accept the id or name for schedules - for escalation_rule in kwargs['escalation_rules']: - for target in escalation_rule['targets']: + for escalation_rule in kwargs["escalation_rules"]: + for target in escalation_rule["targets"]: target_id = None - if target['type'] == 'user': - user = __salt__['pagerduty_util.get_resource']('users', - target['id'], - ['email', 'name', 'id'], - profile=profile, - subdomain=subdomain, - api_key=api_key) + if target["type"] == "user": + user = __salt__["pagerduty_util.get_resource"]( + "users", + target["id"], + ["email", "name", "id"], + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) if user: - target_id = user['id'] - elif target['type'] == 'schedule': - schedule = __salt__['pagerduty_util.get_resource']('schedules', - target['id'], - ['name', 'id'], - profile=profile, - subdomain=subdomain, - api_key=api_key) + target_id = user["id"] + elif target["type"] == "schedule": + schedule = __salt__["pagerduty_util.get_resource"]( + "schedules", + target["id"], + ["name", "id"], + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) if schedule: - target_id = schedule['schedule']['id'] + target_id = schedule["schedule"]["id"] if target_id is None: - raise Exception('unidentified target: {0}'.format(target)) - target['id'] = target_id + raise Exception("unidentified target: {0}".format(target)) + target["id"] = target_id - r = __salt__['pagerduty_util.resource_present']('escalation_policies', - ['name', 'id'], - _diff, - profile, - subdomain, - api_key, - **kwargs) + r = __salt__["pagerduty_util.resource_present"]( + "escalation_policies", + ["name", "id"], + _diff, + profile, + subdomain, + api_key, + **kwargs + ) return r -def absent(profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def absent(profile="pagerduty", subdomain=None, api_key=None, **kwargs): + """ Ensure that a PagerDuty escalation policy does not exist. Accepts all the arguments that pagerduty_escalation_policy.present accepts; but ignores all arguments except the name. Name can be the escalation policy id or the escalation policy name. - ''' - r = __salt__['pagerduty_util.resource_absent']('escalation_policies', - ['name', 'id'], - profile, - subdomain, - api_key, - **kwargs) + """ + r = __salt__["pagerduty_util.resource_absent"]( + "escalation_policies", ["name", "id"], profile, subdomain, api_key, **kwargs + ) return r def _diff(state_data, resource_object): - '''helper method to compare salt state info with the PagerDuty API json structure, + """helper method to compare salt state info with the PagerDuty API json structure, and determine if we need to update. returns the dict to pass to the PD API to perform the update, or empty dict if no update. - ''' + """ objects_differ = None for k, v in state_data.items(): - if k == 'escalation_rules': + if k == "escalation_rules": v = _escalation_rules_to_string(v) resource_value = _escalation_rules_to_string(resource_object[k]) else: @@ -142,7 +147,7 @@ def _diff(state_data, resource_object): else: resource_value = resource_object[k] if v != resource_value: - objects_differ = '{0} {1} {2}'.format(k, v, resource_value) + objects_differ = "{0} {1} {2}".format(k, v, resource_value) break if objects_differ: @@ -152,10 +157,12 @@ def _diff(state_data, resource_object): def _escalation_rules_to_string(escalation_rules): - 'convert escalation_rules dict to a string for comparison' - result = '' + "convert escalation_rules dict to a string for comparison" + result = "" for rule in escalation_rules: - result += 'escalation_delay_in_minutes: {0} '.format(rule['escalation_delay_in_minutes']) - for target in rule['targets']: - result += '{0}:{1} '.format(target['type'], target['id']) + result += "escalation_delay_in_minutes: {0} ".format( + rule["escalation_delay_in_minutes"] + ) + for target in rule["targets"]: + result += "{0}:{1} ".format(target["type"], target["id"]) return result diff --git a/salt/states/pagerduty_schedule.py b/salt/states/pagerduty_schedule.py index 8c9cc6f8764..21b2daa36a0 100644 --- a/salt/states/pagerduty_schedule.py +++ b/salt/states/pagerduty_schedule.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage PagerDuty schedules. Example: @@ -32,130 +32,135 @@ Example: priority: 1 rotation_turn_length_seconds: 604800 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the pygerduty module is available in __salt__ - ''' - return 'pagerduty_schedule' if 'pagerduty_util.get_resource' in __salt__ else False + """ + if "pagerduty_util.get_resource" in __salt__: + return "pagerduty_schedule" + return (False, "pagerduty_util module could not be loaded") -def present(profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def present(profile="pagerduty", subdomain=None, api_key=None, **kwargs): + """ Ensure that a pagerduty schedule exists. This method accepts as args everything defined in https://developer.pagerduty.com/documentation/rest/schedules/create. This means that most arguments are in a dict called "schedule." User id's can be pagerduty id, or name, or email address. - ''' + """ # for convenience, we accept id, name, or email as the user id. - kwargs['schedule']['name'] = kwargs['name'] # match PD API structure - for schedule_layer in kwargs['schedule']['schedule_layers']: - for user in schedule_layer['users']: - u = __salt__['pagerduty_util.get_resource']('users', - user['user']['id'], - ['email', 'name', 'id'], - profile=profile, - subdomain=subdomain, - api_key=api_key) + kwargs["schedule"]["name"] = kwargs["name"] # match PD API structure + for schedule_layer in kwargs["schedule"]["schedule_layers"]: + for user in schedule_layer["users"]: + u = __salt__["pagerduty_util.get_resource"]( + "users", + user["user"]["id"], + ["email", "name", "id"], + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) if u is None: - raise Exception('unknown user: {0}'.format(user)) - user['user']['id'] = u['id'] - r = __salt__['pagerduty_util.resource_present']('schedules', - ['name', 'id'], - _diff, - profile, - subdomain, - api_key, - **kwargs) + raise Exception("unknown user: {0}".format(user)) + user["user"]["id"] = u["id"] + r = __salt__["pagerduty_util.resource_present"]( + "schedules", ["name", "id"], _diff, profile, subdomain, api_key, **kwargs + ) return r -def absent(profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def absent(profile="pagerduty", subdomain=None, api_key=None, **kwargs): + """ Ensure that a pagerduty schedule does not exist. Name can be pagerduty schedule id or pagerduty schedule name. - ''' - r = __salt__['pagerduty_util.resource_absent']('schedules', - ['name', 'id'], - profile, - subdomain, - api_key, - **kwargs) + """ + r = __salt__["pagerduty_util.resource_absent"]( + "schedules", ["name", "id"], profile, subdomain, api_key, **kwargs + ) return r def _diff(state_data, resource_object): - '''helper method to compare salt state info with the PagerDuty API json structure, + """helper method to compare salt state info with the PagerDuty API json structure, and determine if we need to update. returns the dict to pass to the PD API to perform the update, or empty dict if no update. - ''' + """ - state_data['id'] = resource_object['schedule']['id'] + state_data["id"] = resource_object["schedule"]["id"] objects_differ = None # first check all the easy top-level properties: everything except the schedule_layers. - for k, v in state_data['schedule'].items(): - if k == 'schedule_layers': + for k, v in state_data["schedule"].items(): + if k == "schedule_layers": continue - if v != resource_object['schedule'][k]: - objects_differ = '{0} {1} {2}'.format(k, v, resource_object['schedule'][k]) + if v != resource_object["schedule"][k]: + objects_differ = "{0} {1} {2}".format(k, v, resource_object["schedule"][k]) break # check schedule_layers if not objects_differ: - for layer in state_data['schedule']['schedule_layers']: + for layer in state_data["schedule"]["schedule_layers"]: # find matching layer name resource_layer = None - for resource_layer in resource_object['schedule']['schedule_layers']: + for resource_layer in resource_object["schedule"]["schedule_layers"]: found = False - if layer['name'] == resource_layer['name']: + if layer["name"] == resource_layer["name"]: found = True break if not found: - objects_differ = 'layer {0} missing'.format(layer['name']) + objects_differ = "layer {0} missing".format(layer["name"]) break # set the id, so that we will update this layer instead of creating a new one - layer['id'] = resource_layer['id'] + layer["id"] = resource_layer["id"] # compare contents of layer and resource_layer for k, v in layer.items(): - if k == 'users': + if k == "users": continue - if k == 'start': + if k == "start": continue if v != resource_layer[k]: - objects_differ = 'layer {0} key {1} {2} != {3}'.format(layer['name'], k, v, resource_layer[k]) + objects_differ = "layer {0} key {1} {2} != {3}".format( + layer["name"], k, v, resource_layer[k] + ) break if objects_differ: break # compare layer['users'] - if len(layer['users']) != len(resource_layer['users']): - objects_differ = 'num users in layer {0} {1} != {2}'.format(layer['name'], len(layer['users']), len(resource_layer['users'])) + if len(layer["users"]) != len(resource_layer["users"]): + objects_differ = "num users in layer {0} {1} != {2}".format( + layer["name"], len(layer["users"]), len(resource_layer["users"]) + ) break - for user1 in layer['users']: + for user1 in layer["users"]: found = False user2 = None - for user2 in resource_layer['users']: + for user2 in resource_layer["users"]: # deal with PD API bug: when you submit member_order=N, you get back member_order=N+1 - if user1['member_order'] == user2['member_order'] - 1: + if user1["member_order"] == user2["member_order"] - 1: found = True break if not found: - objects_differ = 'layer {0} no one with member_order {1}'.format(layer['name'], user1['member_order']) + objects_differ = "layer {0} no one with member_order {1}".format( + layer["name"], user1["member_order"] + ) break - if user1['user']['id'] != user2['user']['id']: - objects_differ = 'layer {0} user at member_order {1} {2} != {3}'.format(layer['name'], - user1['member_order'], - user1['user']['id'], - user2['user']['id']) + if user1["user"]["id"] != user2["user"]["id"]: + objects_differ = "layer {0} user at member_order {1} {2} != {3}".format( + layer["name"], + user1["member_order"], + user1["user"]["id"], + user2["user"]["id"], + ) break if objects_differ: return state_data diff --git a/salt/states/pagerduty_service.py b/salt/states/pagerduty_service.py index fdfd502e5e1..99b5aee7f9a 100644 --- a/salt/states/pagerduty_service.py +++ b/salt/states/pagerduty_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage PagerDuty services Escalation policies can be referenced by pagerduty ID or by namea. @@ -14,21 +14,23 @@ For example: - escalation_policy_id: 'my escalation policy' - type: nagios -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the pygerduty module is available in __salt__ - ''' - return 'pagerduty_service' if 'pagerduty_util.get_resource' in __salt__ else False + """ + if "pagerduty_util.get_resource" in __salt__: + return "pagerduty_service" + return (False, "pagerduty_util module could not be loaded") -def present(profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def present(profile="pagerduty", subdomain=None, api_key=None, **kwargs): + """ Ensure pagerduty service exists. This method accepts as arguments everything defined in https://developer.pagerduty.com/documentation/rest/services/create @@ -60,64 +62,59 @@ def present(profile='pagerduty', subdomain=None, api_key=None, **kwargs): type: aws_cloudwatch description: "my cloudwatch service controlled by salt" - ''' + """ # TODO: aws_cloudwatch type should be integrated with boto_sns # for convenience, we accept id, name, or email for users # and we accept the id or name for schedules - kwargs['service']['name'] = kwargs['name'] # make args mirror PD API structure - escalation_policy_id = kwargs['service']['escalation_policy_id'] - escalation_policy = __salt__['pagerduty_util.get_resource']('escalation_policies', - escalation_policy_id, - ['name', 'id'], - profile=profile, - subdomain=subdomain, - api_key=api_key) + kwargs["service"]["name"] = kwargs["name"] # make args mirror PD API structure + escalation_policy_id = kwargs["service"]["escalation_policy_id"] + escalation_policy = __salt__["pagerduty_util.get_resource"]( + "escalation_policies", + escalation_policy_id, + ["name", "id"], + profile=profile, + subdomain=subdomain, + api_key=api_key, + ) if escalation_policy: - kwargs['service']['escalation_policy_id'] = escalation_policy['id'] - r = __salt__['pagerduty_util.resource_present']('services', - ['name', 'id'], - _diff, - profile, - subdomain, - api_key, - **kwargs) + kwargs["service"]["escalation_policy_id"] = escalation_policy["id"] + r = __salt__["pagerduty_util.resource_present"]( + "services", ["name", "id"], _diff, profile, subdomain, api_key, **kwargs + ) return r -def absent(profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def absent(profile="pagerduty", subdomain=None, api_key=None, **kwargs): + """ Ensure a pagerduty service does not exist. Name can be the service name or pagerduty service id. - ''' - r = __salt__['pagerduty_util.resource_absent']('services', - ['name', 'id'], - profile, - subdomain, - api_key, - **kwargs) + """ + r = __salt__["pagerduty_util.resource_absent"]( + "services", ["name", "id"], profile, subdomain, api_key, **kwargs + ) return r def _diff(state_data, resource_object): - '''helper method to compare salt state info with the PagerDuty API json structure, + """helper method to compare salt state info with the PagerDuty API json structure, and determine if we need to update. returns the dict to pass to the PD API to perform the update, or empty dict if no update. - ''' + """ objects_differ = None - for k, v in state_data['service'].items(): - if k == 'escalation_policy_id': - resource_value = resource_object['escalation_policy']['id'] - elif k == 'service_key': + for k, v in state_data["service"].items(): + if k == "escalation_policy_id": + resource_value = resource_object["escalation_policy"]["id"] + elif k == "service_key": # service_key on create must 'foo' but the GET will return 'foo@bar.pagerduty.com' - resource_value = resource_object['service_key'] - if '@' in resource_value: - resource_value = resource_value[0:resource_value.find('@')] + resource_value = resource_object["service_key"] + if "@" in resource_value: + resource_value = resource_value[0 : resource_value.find("@")] else: resource_value = resource_object[k] if v != resource_value: - objects_differ = '{0} {1} {2}'.format(k, v, resource_value) + objects_differ = "{0} {1} {2}".format(k, v, resource_value) break if objects_differ: diff --git a/salt/states/pagerduty_user.py b/salt/states/pagerduty_user.py index 452f3cdcb5f..a85d2615b1f 100644 --- a/salt/states/pagerduty_user.py +++ b/salt/states/pagerduty_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage PagerDuty users. Example: @@ -12,42 +12,37 @@ Example: - email: bruce+test1@lyft.com - requester_id: P1GV5NT -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the pygerduty module is available in __salt__ - ''' - return 'pagerduty_user' if 'pagerduty_util.get_resource' in __salt__ else False + """ + if "pagerduty_util.get_resource" in __salt__: + return "pagerduty_user" + return (False, "pagerduty_util module could not be loaded") -def present(profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def present(profile="pagerduty", subdomain=None, api_key=None, **kwargs): + """ Ensure pagerduty user exists. Arguments match those supported by https://developer.pagerduty.com/documentation/rest/users/create. - ''' - return __salt__['pagerduty_util.resource_present']('users', - ['email', 'name', 'id'], - None, - profile, - subdomain, - api_key, - **kwargs) + """ + return __salt__["pagerduty_util.resource_present"]( + "users", ["email", "name", "id"], None, profile, subdomain, api_key, **kwargs + ) -def absent(profile='pagerduty', subdomain=None, api_key=None, **kwargs): - ''' +def absent(profile="pagerduty", subdomain=None, api_key=None, **kwargs): + """ Ensure pagerduty user does not exist. Name can be pagerduty id, email address, or user name. - ''' - return __salt__['pagerduty_util.resource_absent']('users', - ['email', 'name', 'id'], - profile, - subdomain, - api_key, - **kwargs) + """ + return __salt__["pagerduty_util.resource_absent"]( + "users", ["email", "name", "id"], profile, subdomain, api_key, **kwargs + ) diff --git a/salt/states/panos.py b/salt/states/panos.py index 0bc31041064..37f139058c4 100644 --- a/salt/states/panos.py +++ b/salt/states/panos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" A state module to manage Palo Alto network devices. :codeauthor: ``Spencer Ervin <spencer_ervin@hotmail.com>`` @@ -52,7 +52,7 @@ commit to the device. panos/removelock: panos.remove_config_lock panos/commit: - panos.commit + panos.commit_config Version Specific Configurations =============================== @@ -81,10 +81,11 @@ greater than the passed version. For example, proxy['panos.is_required_version'] .. seealso:: :py:mod:`Palo Alto Proxy Module <salt.proxy.panos>` -''' +""" # Import Python Libs from __future__ import absolute_import + import logging # Import salt libs @@ -95,18 +96,20 @@ log = logging.getLogger(__name__) def __virtual__(): - return 'panos.commit' in __salt__ + if "panos.commit" in __salt__: + return True + return (False, "panos module could not be loaded") def _build_members(members, anycheck=False): - ''' + """ Builds a member formatted string for XML operation. - ''' + """ if isinstance(members, list): # This check will strip down members to a single any statement - if anycheck and 'any' in members: + if anycheck and "any" in members: return "<member>any</member>" response = "" for m in members: @@ -117,147 +120,131 @@ def _build_members(members, anycheck=False): def _default_ret(name): - ''' + """ Set the default response values. - ''' - ret = { - 'name': name, - 'changes': {}, - 'commit': None, - 'result': False, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "commit": None, "result": False, "comment": ""} return ret def _edit_config(xpath, element): - ''' + """ Sends an edit request to the device. - ''' - query = {'type': 'config', - 'action': 'edit', - 'xpath': xpath, - 'element': element} + """ + query = {"type": "config", "action": "edit", "xpath": xpath, "element": element} - response = __proxy__['panos.call'](query) + response = __proxy__["panos.call"](query) return _validate_response(response) def _get_config(xpath): - ''' + """ Retrieves an xpath from the device. - ''' - query = {'type': 'config', - 'action': 'get', - 'xpath': xpath} + """ + query = {"type": "config", "action": "get", "xpath": xpath} - response = __proxy__['panos.call'](query) + response = __proxy__["panos.call"](query) return response def _move_after(xpath, target): - ''' + """ Moves an xpath to the after of its section. - ''' - query = {'type': 'config', - 'action': 'move', - 'xpath': xpath, - 'where': 'after', - 'dst': target} + """ + query = { + "type": "config", + "action": "move", + "xpath": xpath, + "where": "after", + "dst": target, + } - response = __proxy__['panos.call'](query) + response = __proxy__["panos.call"](query) return _validate_response(response) def _move_before(xpath, target): - ''' + """ Moves an xpath to the bottom of its section. - ''' - query = {'type': 'config', - 'action': 'move', - 'xpath': xpath, - 'where': 'before', - 'dst': target} + """ + query = { + "type": "config", + "action": "move", + "xpath": xpath, + "where": "before", + "dst": target, + } - response = __proxy__['panos.call'](query) + response = __proxy__["panos.call"](query) return _validate_response(response) def _move_bottom(xpath): - ''' + """ Moves an xpath to the bottom of its section. - ''' - query = {'type': 'config', - 'action': 'move', - 'xpath': xpath, - 'where': 'bottom'} + """ + query = {"type": "config", "action": "move", "xpath": xpath, "where": "bottom"} - response = __proxy__['panos.call'](query) + response = __proxy__["panos.call"](query) return _validate_response(response) def _move_top(xpath): - ''' + """ Moves an xpath to the top of its section. - ''' - query = {'type': 'config', - 'action': 'move', - 'xpath': xpath, - 'where': 'top'} + """ + query = {"type": "config", "action": "move", "xpath": xpath, "where": "top"} - response = __proxy__['panos.call'](query) + response = __proxy__["panos.call"](query) return _validate_response(response) def _set_config(xpath, element): - ''' + """ Sends a set request to the device. - ''' - query = {'type': 'config', - 'action': 'set', - 'xpath': xpath, - 'element': element} + """ + query = {"type": "config", "action": "set", "xpath": xpath, "element": element} - response = __proxy__['panos.call'](query) + response = __proxy__["panos.call"](query) return _validate_response(response) def _validate_response(response): - ''' + """ Validates a response from a Palo Alto device. Used to verify success of commands. - ''' + """ if not response: - return False, 'Unable to validate response from device.' - elif 'msg' in response: - if 'line' in response['msg']: - if response['msg']['line'] == 'already at the top': + return False, "Unable to validate response from device." + elif "msg" in response: + if "line" in response["msg"]: + if response["msg"]["line"] == "already at the top": return True, response - elif response['msg']['line'] == 'already at the bottom': + elif response["msg"]["line"] == "already at the bottom": return True, response else: return False, response - elif response['msg'] == 'command succeeded': + elif response["msg"] == "command succeeded": return True, response else: return False, response - elif 'status' in response: - if response['status'] == "success": + elif "status" in response: + if response["status"] == "success": return True, response else: return False, response @@ -266,7 +253,7 @@ def _validate_response(response): def add_config_lock(name): - ''' + """ Prevent other users from changing configuration until the lock is released. name: The name of the module function to execute. @@ -278,26 +265,25 @@ def add_config_lock(name): panos/takelock: panos.add_config_lock - ''' + """ ret = _default_ret(name) - ret.update({ - 'changes': __salt__['panos.add_config_lock'](), - 'result': True - }) + ret.update({"changes": __salt__["panos.add_config_lock"](), "result": True}) return ret -def address_exists(name, - addressname=None, - vsys=1, - ipnetmask=None, - iprange=None, - fqdn=None, - description=None, - commit=False): - ''' +def address_exists( + name, + addressname=None, + vsys=1, + ipnetmask=None, + iprange=None, + fqdn=None, + description=None, + commit=False, +): + """ Ensures that an address object exists in the configured state. If it does not exist or is not configured with the specified attributes, it will be adjusted to match the specified values. @@ -354,18 +340,18 @@ def address_exists(name, - description: My fqdn object - commit: False - ''' + """ ret = _default_ret(name) if not addressname: - ret.update({'comment': "The service name field must be provided."}) + ret.update({"comment": "The service name field must be provided."}) return ret # Check if address object currently exists - address = __salt__['panos.get_address'](addressname, vsys)['result'] + address = __salt__["panos.get_address"](addressname, vsys)["result"] - if address and 'entry' in address: - address = address['entry'] + if address and "entry" in address: + address = address["entry"] else: address = {} @@ -379,7 +365,7 @@ def address_exists(name, elif fqdn: element = "<fqdn>{0}</fqdn>".format(fqdn) else: - ret.update({'comment': "A valid address type must be specified."}) + ret.update({"comment": "A valid address type must be specified."}) return ret if description: @@ -390,47 +376,50 @@ def address_exists(name, new_address = xml.to_dict(ET.fromstring(full_element), True) if address == new_address: - ret.update({ - 'comment': 'Address object already exists. No changes required.', - 'result': True - }) + ret.update( + { + "comment": "Address object already exists. No changes required.", + "result": True, + } + ) return ret else: - xpath = "/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/address/" \ - "entry[@name=\'{1}\']".format(vsys, addressname) + xpath = ( + "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/address/" + "entry[@name='{1}']".format(vsys, addressname) + ) result, msg = _edit_config(xpath, full_element) if not result: - ret.update({ - 'comment': msg - }) + ret.update({"comment": msg}) return ret if commit is True: - ret.update({ - 'changes': {'before': address, 'after': new_address}, - 'commit': __salt__['panos.commit'](), - 'comment': 'Address object successfully configured.', - 'result': True - }) + ret.update( + { + "changes": {"before": address, "after": new_address}, + "commit": __salt__["panos.commit"](), + "comment": "Address object successfully configured.", + "result": True, + } + ) else: - ret.update({ - 'changes': {'before': address, 'after': new_address}, - 'comment': 'Service object successfully configured.', - 'result': True - }) + ret.update( + { + "changes": {"before": address, "after": new_address}, + "comment": "Service object successfully configured.", + "result": True, + } + ) return ret -def address_group_exists(name, - groupname=None, - vsys=1, - members=None, - description=None, - commit=False): - ''' +def address_group_exists( + name, groupname=None, vsys=1, members=None, description=None, commit=False +): + """ Ensures that an address group object exists in the configured state. If it does not exist or is not configured with the specified attributes, it will be adjusted to match the specified values. @@ -466,18 +455,18 @@ def address_group_exists(name, - description: A group that needs to exist - commit: False - ''' + """ ret = _default_ret(name) if not groupname: - ret.update({'comment': "The group name field must be provided."}) + ret.update({"comment": "The group name field must be provided."}) return ret # Check if address group object currently exists - group = __salt__['panos.get_address_group'](groupname, vsys)['result'] + group = __salt__["panos.get_address_group"](groupname, vsys)["result"] - if group and 'entry' in group: - group = group['entry'] + if group and "entry" in group: + group = group["entry"] else: group = {} @@ -485,7 +474,7 @@ def address_group_exists(name, if members: element = "<static>{0}</static>".format(_build_members(members, True)) else: - ret.update({'comment': "The group members must be provided."}) + ret.update({"comment": "The group members must be provided."}) return ret if description: @@ -496,42 +485,48 @@ def address_group_exists(name, new_group = xml.to_dict(ET.fromstring(full_element), True) if group == new_group: - ret.update({ - 'comment': 'Address group object already exists. No changes required.', - 'result': True - }) + ret.update( + { + "comment": "Address group object already exists. No changes required.", + "result": True, + } + ) return ret else: - xpath = "/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/address-group/" \ - "entry[@name=\'{1}\']".format(vsys, groupname) + xpath = ( + "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/address-group/" + "entry[@name='{1}']".format(vsys, groupname) + ) result, msg = _edit_config(xpath, full_element) if not result: - ret.update({ - 'comment': msg - }) + ret.update({"comment": msg}) return ret if commit is True: - ret.update({ - 'changes': {'before': group, 'after': new_group}, - 'commit': __salt__['panos.commit'](), - 'comment': 'Address group object successfully configured.', - 'result': True - }) + ret.update( + { + "changes": {"before": group, "after": new_group}, + "commit": __salt__["panos.commit"](), + "comment": "Address group object successfully configured.", + "result": True, + } + ) else: - ret.update({ - 'changes': {'before': group, 'after': new_group}, - 'comment': 'Address group object successfully configured.', - 'result': True - }) + ret.update( + { + "changes": {"before": group, "after": new_group}, + "comment": "Address group object successfully configured.", + "result": True, + } + ) return ret def clone_config(name, xpath=None, newname=None, commit=False): - ''' + """ Clone a specific XPATH and set it to a new name. name: The name of the module function to execute. @@ -553,7 +548,7 @@ def clone_config(name, xpath=None, newname=None, commit=False): - value: rule2 - commit: True - ''' + """ ret = _default_ret(name) if not xpath: @@ -562,32 +557,23 @@ def clone_config(name, xpath=None, newname=None, commit=False): if not newname: return ret - query = {'type': 'config', - 'action': 'clone', - 'xpath': xpath, - 'newname': newname} + query = {"type": "config", "action": "clone", "xpath": xpath, "newname": newname} - result, response = _validate_response(__proxy__['panos.call'](query)) + result, response = _validate_response(__proxy__["panos.call"](query)) - ret.update({ - 'changes': response, - 'result': result - }) + ret.update({"changes": response, "result": result}) if not result: return ret if commit is True: - ret.update({ - 'commit': __salt__['panos.commit'](), - 'result': True - }) + ret.update({"commit": __salt__["panos.commit"](), "result": True}) return ret def commit_config(name): - ''' + """ Commits the candidate configuration to the running configuration. name: The name of the module function to execute. @@ -599,19 +585,16 @@ def commit_config(name): panos/commit: panos.commit_config - ''' + """ ret = _default_ret(name) - ret.update({ - 'commit': __salt__['panos.commit'](), - 'result': True - }) + ret.update({"commit": __salt__["panos.commit"](), "result": True}) return ret def delete_config(name, xpath=None, commit=False): - ''' + """ Deletes a Palo Alto XPATH to a specific value. Use the xpath parameter to specify the location of the object to be deleted. @@ -631,37 +614,29 @@ def delete_config(name, xpath=None, commit=False): - xpath: /config/devices/entry/vsys/entry[@name='vsys1']/address-group/entry[@name='test'] - commit: True - ''' + """ ret = _default_ret(name) if not xpath: return ret - query = {'type': 'config', - 'action': 'delete', - 'xpath': xpath} + query = {"type": "config", "action": "delete", "xpath": xpath} - result, response = _validate_response(__proxy__['panos.call'](query)) + result, response = _validate_response(__proxy__["panos.call"](query)) - ret.update({ - 'changes': response, - 'result': result - }) + ret.update({"changes": response, "result": result}) if not result: return ret if commit is True: - ret.update({ - 'commit': __salt__['panos.commit'](), - 'result': True - }) + ret.update({"commit": __salt__["panos.commit"](), "result": True}) return ret def download_software(name, version=None, synch=False, check=False): - ''' + """ Ensures that a software version is downloaded. name: The name of the module function to execute. @@ -684,57 +659,62 @@ def download_software(name, version=None, synch=False, check=False): - synch: False - check: True - ''' + """ ret = _default_ret(name) if check is True: - __salt__['panos.check_software']() + __salt__["panos.check_software"]() - versions = __salt__['panos.get_software_info']()['result'] + versions = __salt__["panos.get_software_info"]()["result"] - if 'sw-updates' not in versions \ - or 'versions' not in versions['sw-updates'] \ - or 'entry' not in versions['sw-updates']['versions']: - ret.update({ - 'comment': 'Software version is not found in the local software list.', - 'result': False - }) + if ( + "sw-updates" not in versions + or "versions" not in versions["sw-updates"] + or "entry" not in versions["sw-updates"]["versions"] + ): + ret.update( + { + "comment": "Software version is not found in the local software list.", + "result": False, + } + ) return ret - for entry in versions['sw-updates']['versions']['entry']: - if entry['version'] == version and entry['downloaded'] == "yes": - ret.update({ - 'comment': 'Software version is already downloaded.', - 'result': True - }) + for entry in versions["sw-updates"]["versions"]["entry"]: + if entry["version"] == version and entry["downloaded"] == "yes": + ret.update( + {"comment": "Software version is already downloaded.", "result": True} + ) return ret - ret.update({ - 'changes': __salt__['panos.download_software_version'](version=version, synch=synch) - }) + ret.update( + { + "changes": __salt__["panos.download_software_version"]( + version=version, synch=synch + ) + } + ) - versions = __salt__['panos.get_software_info']()['result'] + versions = __salt__["panos.get_software_info"]()["result"] - if 'sw-updates' not in versions \ - or 'versions' not in versions['sw-updates'] \ - or 'entry' not in versions['sw-updates']['versions']: - ret.update({ - 'result': False - }) + if ( + "sw-updates" not in versions + or "versions" not in versions["sw-updates"] + or "entry" not in versions["sw-updates"]["versions"] + ): + ret.update({"result": False}) return ret - for entry in versions['sw-updates']['versions']['entry']: - if entry['version'] == version and entry['downloaded'] == "yes": - ret.update({ - 'result': True - }) + for entry in versions["sw-updates"]["versions"]["entry"]: + if entry["version"] == version and entry["downloaded"] == "yes": + ret.update({"result": True}) return ret return ret def edit_config(name, xpath=None, value=None, commit=False): - ''' + """ Edits a Palo Alto XPATH to a specific value. This will always overwrite the existing value, even if it is not changed. @@ -761,7 +741,7 @@ def edit_config(name, xpath=None, value=None, commit=False): - value: <static><entry name='test'><member>abc</member><member>xyz</member></entry></static> - commit: True - ''' + """ ret = _default_ret(name) # Verify if the current XPATH is equal to the specified value. @@ -774,7 +754,7 @@ def edit_config(name, xpath=None, value=None, commit=False): if "[" in head: head = head.split("[")[0] - current_element = __salt__['panos.get_xpath'](xpath)['result'] + current_element = __salt__["panos.get_xpath"](xpath)["result"] if head and current_element and head in current_element: current_element = current_element[head] @@ -784,39 +764,42 @@ def edit_config(name, xpath=None, value=None, commit=False): new_element = xml.to_dict(ET.fromstring(value), True) if current_element == new_element: - ret.update({ - 'comment': 'XPATH is already equal to the specified value.', - 'result': True - }) + ret.update( + { + "comment": "XPATH is already equal to the specified value.", + "result": True, + } + ) return ret result, msg = _edit_config(xpath, value) - ret.update({ - 'comment': msg, - 'result': result - }) + ret.update({"comment": msg, "result": result}) if not result: return ret if commit is True: - ret.update({ - 'changes': {'before': current_element, 'after': new_element}, - 'commit': __salt__['panos.commit'](), - 'result': True - }) + ret.update( + { + "changes": {"before": current_element, "after": new_element}, + "commit": __salt__["panos.commit"](), + "result": True, + } + ) else: - ret.update({ - 'changes': {'before': current_element, 'after': new_element}, - 'result': True - }) + ret.update( + { + "changes": {"before": current_element, "after": new_element}, + "result": True, + } + ) return ret def move_config(name, xpath=None, where=None, dst=None, commit=False): - ''' + """ Moves a XPATH value to a new location. Use the xpath parameter to specify the location of the object to be moved, the where parameter to @@ -853,7 +836,7 @@ def move_config(name, xpath=None, where=None, dst=None, commit=False): - dst: rule2 - commit: True - ''' + """ ret = _default_ret(name) if not xpath: @@ -862,34 +845,28 @@ def move_config(name, xpath=None, where=None, dst=None, commit=False): if not where: return ret - if where == 'after': + if where == "after": result, msg = _move_after(xpath, dst) - elif where == 'before': + elif where == "before": result, msg = _move_before(xpath, dst) - elif where == 'top': + elif where == "top": result, msg = _move_top(xpath) - elif where == 'bottom': + elif where == "bottom": result, msg = _move_bottom(xpath) - ret.update({ - 'result': result, - 'comment': msg - }) + ret.update({"result": result, "comment": msg}) if not result: return ret if commit is True: - ret.update({ - 'commit': __salt__['panos.commit'](), - 'result': True - }) + ret.update({"commit": __salt__["panos.commit"](), "result": True}) return ret def remove_config_lock(name): - ''' + """ Release config lock previously held. name: The name of the module function to execute. @@ -901,19 +878,16 @@ def remove_config_lock(name): panos/takelock: panos.remove_config_lock - ''' + """ ret = _default_ret(name) - ret.update({ - 'changes': __salt__['panos.remove_config_lock'](), - 'result': True - }) + ret.update({"changes": __salt__["panos.remove_config_lock"](), "result": True}) return ret def rename_config(name, xpath=None, newname=None, commit=False): - ''' + """ Rename a Palo Alto XPATH to a specific value. This will always rename the value even if a change is not needed. name: The name of the module function to execute. @@ -934,7 +908,7 @@ def rename_config(name, xpath=None, newname=None, commit=False): - value: new_address - commit: True - ''' + """ ret = _default_ret(name) if not xpath: @@ -943,59 +917,52 @@ def rename_config(name, xpath=None, newname=None, commit=False): if not newname: return ret - query = {'type': 'config', - 'action': 'rename', - 'xpath': xpath, - 'newname': newname} + query = {"type": "config", "action": "rename", "xpath": xpath, "newname": newname} - result, response = _validate_response(__proxy__['panos.call'](query)) + result, response = _validate_response(__proxy__["panos.call"](query)) - ret.update({ - 'changes': response, - 'result': result - }) + ret.update({"changes": response, "result": result}) if not result: return ret if commit is True: - ret.update({ - 'commit': __salt__['panos.commit'](), - 'result': True - }) + ret.update({"commit": __salt__["panos.commit"](), "result": True}) return ret -def security_rule_exists(name, - rulename=None, - vsys='1', - action=None, - disabled=None, - sourcezone=None, - destinationzone=None, - source=None, - destination=None, - application=None, - service=None, - description=None, - logsetting=None, - logstart=None, - logend=None, - negatesource=None, - negatedestination=None, - profilegroup=None, - datafilter=None, - fileblock=None, - spyware=None, - urlfilter=None, - virus=None, - vulnerability=None, - wildfire=None, - move=None, - movetarget=None, - commit=False): - ''' +def security_rule_exists( + name, + rulename=None, + vsys="1", + action=None, + disabled=None, + sourcezone=None, + destinationzone=None, + source=None, + destination=None, + application=None, + service=None, + description=None, + logsetting=None, + logstart=None, + logend=None, + negatesource=None, + negatedestination=None, + profilegroup=None, + datafilter=None, + fileblock=None, + spyware=None, + urlfilter=None, + virus=None, + vulnerability=None, + wildfire=None, + move=None, + movetarget=None, + commit=False, +): + """ Ensures that a security rule exists on the device. Also, ensure that all configurations are set appropriately. This method will create the rule if it does not exist. If the rule does exist, it will ensure that the @@ -1138,17 +1105,17 @@ def security_rule_exists(name, - move: after - movetarget: rule02 - commit: False - ''' + """ ret = _default_ret(name) if not rulename: return ret # Check if rule currently exists - rule = __salt__['panos.get_security_rule'](rulename, vsys)['result'] + rule = __salt__["panos.get_security_rule"](rulename, vsys)["result"] - if rule and 'entry' in rule: - rule = rule['entry'] + if rule and "entry" in rule: + rule = rule["entry"] else: rule = {} @@ -1157,43 +1124,47 @@ def security_rule_exists(name, if sourcezone: element += "<from>{0}</from>".format(_build_members(sourcezone, True)) else: - ret.update({'comment': "The sourcezone field must be provided."}) + ret.update({"comment": "The sourcezone field must be provided."}) return ret if destinationzone: element += "<to>{0}</to>".format(_build_members(destinationzone, True)) else: - ret.update({'comment': "The destinationzone field must be provided."}) + ret.update({"comment": "The destinationzone field must be provided."}) return ret if source: element += "<source>{0}</source>".format(_build_members(source, True)) else: - ret.update({'comment': "The source field must be provided."}) + ret.update({"comment": "The source field must be provided."}) return if destination: - element += "<destination>{0}</destination>".format(_build_members(destination, True)) + element += "<destination>{0}</destination>".format( + _build_members(destination, True) + ) else: - ret.update({'comment': "The destination field must be provided."}) + ret.update({"comment": "The destination field must be provided."}) return ret if application: - element += "<application>{0}</application>".format(_build_members(application, True)) + element += "<application>{0}</application>".format( + _build_members(application, True) + ) else: - ret.update({'comment': "The application field must be provided."}) + ret.update({"comment": "The application field must be provided."}) return ret if service: element += "<service>{0}</service>".format(_build_members(service, True)) else: - ret.update({'comment': "The service field must be provided."}) + ret.update({"comment": "The service field must be provided."}) return ret if action: element += "<action>{0}</action>".format(action) else: - ret.update({'comment': "The action field must be provided."}) + ret.update({"comment": "The action field must be provided."}) return ret if disabled is not None: @@ -1239,19 +1210,29 @@ def security_rule_exists(name, else: member_string = "" if datafilter: - member_string += "<data-filtering><member>{0}</member></data-filtering>".format(datafilter) + member_string += "<data-filtering><member>{0}</member></data-filtering>".format( + datafilter + ) if fileblock: - member_string += "<file-blocking><member>{0}</member></file-blocking>".format(fileblock) + member_string += "<file-blocking><member>{0}</member></file-blocking>".format( + fileblock + ) if spyware: member_string += "<spyware><member>{0}</member></spyware>".format(spyware) if urlfilter: - member_string += "<url-filtering><member>{0}</member></url-filtering>".format(urlfilter) + member_string += "<url-filtering><member>{0}</member></url-filtering>".format( + urlfilter + ) if virus: member_string += "<virus><member>{0}</member></virus>".format(virus) if vulnerability: - member_string += "<vulnerability><member>{0}</member></vulnerability>".format(vulnerability) + member_string += "<vulnerability><member>{0}</member></vulnerability>".format( + vulnerability + ) if wildfire: - member_string += "<wildfire-analysis><member>{0}</member></wildfire-analysis>".format(wildfire) + member_string += "<wildfire-analysis><member>{0}</member></wildfire-analysis>".format( + wildfire + ) if member_string != "": profile_string = "<profiles>{0}</profiles>".format(member_string) @@ -1265,32 +1246,34 @@ def security_rule_exists(name, config_change = False if rule == new_rule: - ret.update({ - 'comment': 'Security rule already exists. No changes required.' - }) + ret.update({"comment": "Security rule already exists. No changes required."}) else: config_change = True - xpath = "/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/rulebase/" \ - "security/rules/entry[@name=\'{1}\']".format(vsys, rulename) + xpath = ( + "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/rulebase/" + "security/rules/entry[@name='{1}']".format(vsys, rulename) + ) result, msg = _edit_config(xpath, full_element) if not result: - ret.update({ - 'comment': msg - }) + ret.update({"comment": msg}) return ret - ret.update({ - 'changes': {'before': rule, 'after': new_rule}, - 'comment': 'Security rule verified successfully.' - }) + ret.update( + { + "changes": {"before": rule, "after": new_rule}, + "comment": "Security rule verified successfully.", + } + ) if move: - movepath = "/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/rulebase/" \ - "security/rules/entry[@name=\'{1}\']".format(vsys, rulename) + movepath = ( + "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/rulebase/" + "security/rules/entry[@name='{1}']".format(vsys, rulename) + ) move_result = False - move_msg = '' + move_msg = "" if move == "before" and movetarget: move_result, move_msg = _move_before(movepath, movetarget) elif move == "after": @@ -1301,35 +1284,34 @@ def security_rule_exists(name, move_result, move_msg = _move_bottom(movepath) if config_change: - ret.update({ - 'changes': {'before': rule, 'after': new_rule, 'move': move_msg} - }) + ret.update( + {"changes": {"before": rule, "after": new_rule, "move": move_msg}} + ) else: - ret.update({ - 'changes': {'move': move_msg} - }) + ret.update({"changes": {"move": move_msg}}) if not move_result: - ret.update({ - 'comment': move_msg - }) + ret.update({"comment": move_msg}) return ret if commit is True: - ret.update({ - 'commit': __salt__['panos.commit'](), - 'result': True - }) + ret.update({"commit": __salt__["panos.commit"](), "result": True}) else: - ret.update({ - 'result': True - }) + ret.update({"result": True}) return ret -def service_exists(name, servicename=None, vsys=1, protocol=None, port=None, description=None, commit=False): - ''' +def service_exists( + name, + servicename=None, + vsys=1, + protocol=None, + port=None, + description=None, + commit=False, +): + """ Ensures that a service object exists in the configured state. If it does not exist or is not configured with the specified attributes, it will be adjusted to match the specified values. @@ -1371,27 +1353,27 @@ def service_exists(name, servicename=None, vsys=1, protocol=None, port=None, des - port: 500-550 - commit: False - ''' + """ ret = _default_ret(name) if not servicename: - ret.update({'comment': "The service name field must be provided."}) + ret.update({"comment": "The service name field must be provided."}) return ret # Check if service object currently exists - service = __salt__['panos.get_service'](servicename, vsys)['result'] + service = __salt__["panos.get_service"](servicename, vsys)["result"] - if service and 'entry' in service: - service = service['entry'] + if service and "entry" in service: + service = service["entry"] else: service = {} # Verify the arguments - if not protocol and protocol not in ['tcp', 'udp']: - ret.update({'comment': "The protocol must be provided and must be tcp or udp."}) + if not protocol and protocol not in ["tcp", "udp"]: + ret.update({"comment": "The protocol must be provided and must be tcp or udp."}) return ret if not port: - ret.update({'comment': "The port field must be provided."}) + ret.update({"comment": "The port field must be provided."}) return ret element = "<protocol><{0}><port>{1}</port></{0}></protocol>".format(protocol, port) @@ -1404,47 +1386,50 @@ def service_exists(name, servicename=None, vsys=1, protocol=None, port=None, des new_service = xml.to_dict(ET.fromstring(full_element), True) if service == new_service: - ret.update({ - 'comment': 'Service object already exists. No changes required.', - 'result': True - }) + ret.update( + { + "comment": "Service object already exists. No changes required.", + "result": True, + } + ) return ret else: - xpath = "/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/service/" \ - "entry[@name=\'{1}\']".format(vsys, servicename) + xpath = ( + "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/service/" + "entry[@name='{1}']".format(vsys, servicename) + ) result, msg = _edit_config(xpath, full_element) if not result: - ret.update({ - 'comment': msg - }) + ret.update({"comment": msg}) return ret if commit is True: - ret.update({ - 'changes': {'before': service, 'after': new_service}, - 'commit': __salt__['panos.commit'](), - 'comment': 'Service object successfully configured.', - 'result': True - }) + ret.update( + { + "changes": {"before": service, "after": new_service}, + "commit": __salt__["panos.commit"](), + "comment": "Service object successfully configured.", + "result": True, + } + ) else: - ret.update({ - 'changes': {'before': service, 'after': new_service}, - 'comment': 'Service object successfully configured.', - 'result': True - }) + ret.update( + { + "changes": {"before": service, "after": new_service}, + "comment": "Service object successfully configured.", + "result": True, + } + ) return ret -def service_group_exists(name, - groupname=None, - vsys=1, - members=None, - description=None, - commit=False): - ''' +def service_group_exists( + name, groupname=None, vsys=1, members=None, description=None, commit=False +): + """ Ensures that a service group object exists in the configured state. If it does not exist or is not configured with the specified attributes, it will be adjusted to match the specified values. @@ -1480,18 +1465,18 @@ def service_group_exists(name, - description: A group that needs to exist - commit: False - ''' + """ ret = _default_ret(name) if not groupname: - ret.update({'comment': "The group name field must be provided."}) + ret.update({"comment": "The group name field must be provided."}) return ret # Check if service group object currently exists - group = __salt__['panos.get_service_group'](groupname, vsys)['result'] + group = __salt__["panos.get_service_group"](groupname, vsys)["result"] - if group and 'entry' in group: - group = group['entry'] + if group and "entry" in group: + group = group["entry"] else: group = {} @@ -1499,7 +1484,7 @@ def service_group_exists(name, if members: element = "<members>{0}</members>".format(_build_members(members, True)) else: - ret.update({'comment': "The group members must be provided."}) + ret.update({"comment": "The group members must be provided."}) return ret if description: @@ -1510,42 +1495,48 @@ def service_group_exists(name, new_group = xml.to_dict(ET.fromstring(full_element), True) if group == new_group: - ret.update({ - 'comment': 'Service group object already exists. No changes required.', - 'result': True - }) + ret.update( + { + "comment": "Service group object already exists. No changes required.", + "result": True, + } + ) return ret else: - xpath = "/config/devices/entry[@name=\'localhost.localdomain\']/vsys/entry[@name=\'vsys{0}\']/service-group/" \ - "entry[@name=\'{1}\']".format(vsys, groupname) + xpath = ( + "/config/devices/entry[@name='localhost.localdomain']/vsys/entry[@name='vsys{0}']/service-group/" + "entry[@name='{1}']".format(vsys, groupname) + ) result, msg = _edit_config(xpath, full_element) if not result: - ret.update({ - 'comment': msg - }) + ret.update({"comment": msg}) return ret if commit is True: - ret.update({ - 'changes': {'before': group, 'after': new_group}, - 'commit': __salt__['panos.commit'](), - 'comment': 'Service group object successfully configured.', - 'result': True - }) + ret.update( + { + "changes": {"before": group, "after": new_group}, + "commit": __salt__["panos.commit"](), + "comment": "Service group object successfully configured.", + "result": True, + } + ) else: - ret.update({ - 'changes': {'before': group, 'after': new_group}, - 'comment': 'Service group object successfully configured.', - 'result': True - }) + ret.update( + { + "changes": {"before": group, "after": new_group}, + "comment": "Service group object successfully configured.", + "result": True, + } + ) return ret def set_config(name, xpath=None, value=None, commit=False): - ''' + """ Sets a Palo Alto XPATH to a specific value. This will always overwrite the existing value, even if it is not changed. @@ -1570,23 +1561,17 @@ def set_config(name, xpath=None, value=None, commit=False): - value: <hostname>foobar</hostname> - commit: True - ''' + """ ret = _default_ret(name) result, msg = _set_config(xpath, value) - ret.update({ - 'comment': msg, - 'result': result - }) + ret.update({"comment": msg, "result": result}) if not result: return ret if commit is True: - ret.update({ - 'commit': __salt__['panos.commit'](), - 'result': True - }) + ret.update({"commit": __salt__["panos.commit"](), "result": True}) return ret diff --git a/salt/states/pbm.py b/salt/states/pbm.py index 836c95b807d..c2c8b6d4a88 100644 --- a/salt/states/pbm.py +++ b/salt/states/pbm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manages VMware storage policies (called pbm because the vCenter endpoint is /pbm) @@ -11,67 +11,65 @@ Storage policy .. code-block:: python -{ - "name": "salt_storage_policy" - "description": "Managed by Salt. Random capability values.", - "resource_type": "STORAGE", - "subprofiles": [ - { - "capabilities": [ - { - "setting": { - "type": "scalar", - "value": 2 + { + "name": "salt_storage_policy" + "description": "Managed by Salt. Random capability values.", + "resource_type": "STORAGE", + "subprofiles": [ + { + "capabilities": [ + { + "setting": { + "type": "scalar", + "value": 2 + }, + "namespace": "VSAN", + "id": "hostFailuresToTolerate" }, - "namespace": "VSAN", - "id": "hostFailuresToTolerate" - }, - { - "setting": { - "type": "scalar", - "value": 2 + { + "setting": { + "type": "scalar", + "value": 2 + }, + "namespace": "VSAN", + "id": "stripeWidth" }, - "namespace": "VSAN", - "id": "stripeWidth" - }, - { - "setting": { - "type": "scalar", - "value": true + { + "setting": { + "type": "scalar", + "value": true + }, + "namespace": "VSAN", + "id": "forceProvisioning" }, - "namespace": "VSAN", - "id": "forceProvisioning" - }, - { - "setting": { - "type": "scalar", - "value": 50 + { + "setting": { + "type": "scalar", + "value": 50 + }, + "namespace": "VSAN", + "id": "proportionalCapacity" }, - "namespace": "VSAN", - "id": "proportionalCapacity" - }, - { - "setting": { - "type": "scalar", - "value": 0 - }, - "namespace": "VSAN", - "id": "cacheReservation" - } - ], - "name": "Rule-Set 1: VSAN", - "force_provision": null - } - ], -} + { + "setting": { + "type": "scalar", + "value": 0 + }, + "namespace": "VSAN", + "id": "cacheReservation" + } + ], + "name": "Rule-Set 1: VSAN", + "force_provision": null + } + ], + } Dependencies ============ - - pyVmomi Python Module - pyVmomi ------- @@ -88,25 +86,27 @@ PyVmomi can be installed via pip: Python 2.7.9, or newer must be present. This is due to an upstream dependency in pyVmomi 6.0 that is not supported in Python versions 2.7 to 2.7.8. If the version of Python is not in the supported range, you will need to install an - earlier version of pyVmomi. See `Issue #29537`_ for more information. - -.. _Issue #29537: https://github.com/saltstack/salt/issues/29537 -''' + earlier version of pyVmomi. See + `Issue #29537 <https://github.com/saltstack/salt/issues/29537>` for more + information. +""" # Import Python Libs from __future__ import absolute_import -import logging + import copy +import logging import sys # Import Salt Libs -from salt.exceptions import CommandExecutionError, ArgumentValueError +from salt.exceptions import ArgumentValueError, CommandExecutionError from salt.utils.dictdiffer import recursive_diff from salt.utils.listdiffer import list_diff # External libraries try: from pyVmomi import VmomiSupport + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False @@ -117,217 +117,250 @@ log = logging.getLogger(__name__) def __virtual__(): if not HAS_PYVMOMI: - return False, 'State module did not load: pyVmomi not found' + return False, "State module did not load: pyVmomi not found" # We check the supported vim versions to infer the pyVmomi version - if 'vim25/6.0' in VmomiSupport.versionMap and \ - sys.version_info > (2, 7) and sys.version_info < (2, 7, 9): + if ( + "vim25/6.0" in VmomiSupport.versionMap + and sys.version_info > (2, 7) + and sys.version_info < (2, 7, 9) + ): - return False, ('State module did not load: Incompatible versions ' - 'of Python and pyVmomi present. See Issue #29537.') + return ( + False, + ( + "State module did not load: Incompatible versions " + "of Python and pyVmomi present. See Issue #29537." + ), + ) return True def mod_init(low): - ''' + """ Init function - ''' + """ return True def default_vsan_policy_configured(name, policy): - ''' + """ Configures the default VSAN policy on a vCenter. The state assumes there is only one default VSAN policy on a vCenter. policy Dict representation of a policy - ''' + """ # TODO Refactor when recurse_differ supports list_differ # It's going to make the whole thing much easier policy_copy = copy.deepcopy(policy) - proxy_type = __salt__['vsphere.get_proxy_type']() - log.trace('proxy_type = {0}'.format(proxy_type)) + proxy_type = __salt__["vsphere.get_proxy_type"]() + log.trace("proxy_type = {0}".format(proxy_type)) # All allowed proxies have a shim execution module with the same # name which implementes a get_details function # All allowed proxies have a vcenter detail - vcenter = __salt__['{0}.get_details'.format(proxy_type)]()['vcenter'] - log.info('Running {0} on vCenter ' - '\'{1}\''.format(name, vcenter)) - log.trace('policy = {0}'.format(policy)) + vcenter = __salt__["{0}.get_details".format(proxy_type)]()["vcenter"] + log.info("Running {0} on vCenter " "'{1}'".format(name, vcenter)) + log.trace("policy = {0}".format(policy)) changes_required = False - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': None} + ret = {"name": name, "changes": {}, "result": None, "comment": None} comments = [] changes = {} changes_required = False si = None try: - #TODO policy schema validation - si = __salt__['vsphere.get_service_instance_via_proxy']() - current_policy = __salt__['vsphere.list_default_vsan_policy'](si) - log.trace('current_policy = {0}'.format(current_policy)) + # TODO policy schema validation + si = __salt__["vsphere.get_service_instance_via_proxy"]() + current_policy = __salt__["vsphere.list_default_vsan_policy"](si) + log.trace("current_policy = {0}".format(current_policy)) # Building all diffs between the current and expected policy # XXX We simplify the comparison by assuming we have at most 1 # sub_profile - if policy.get('subprofiles'): - if len(policy['subprofiles']) > 1: - raise ArgumentValueError('Multiple sub_profiles ({0}) are not ' - 'supported in the input policy') - subprofile = policy['subprofiles'][0] - current_subprofile = current_policy['subprofiles'][0] - capabilities_differ = list_diff(current_subprofile['capabilities'], - subprofile.get('capabilities', []), - key='id') - del policy['subprofiles'] - if subprofile.get('capabilities'): - del subprofile['capabilities'] - del current_subprofile['capabilities'] + if policy.get("subprofiles"): + if len(policy["subprofiles"]) > 1: + raise ArgumentValueError( + "Multiple sub_profiles ({0}) are not " + "supported in the input policy" + ) + subprofile = policy["subprofiles"][0] + current_subprofile = current_policy["subprofiles"][0] + capabilities_differ = list_diff( + current_subprofile["capabilities"], + subprofile.get("capabilities", []), + key="id", + ) + del policy["subprofiles"] + if subprofile.get("capabilities"): + del subprofile["capabilities"] + del current_subprofile["capabilities"] # Get the subprofile diffs without the capability keys - subprofile_differ = recursive_diff(current_subprofile, - dict(subprofile)) + subprofile_differ = recursive_diff(current_subprofile, dict(subprofile)) - del current_policy['subprofiles'] + del current_policy["subprofiles"] policy_differ = recursive_diff(current_policy, policy) - if policy_differ.diffs or capabilities_differ.diffs or \ - subprofile_differ.diffs: + if policy_differ.diffs or capabilities_differ.diffs or subprofile_differ.diffs: - if 'name' in policy_differ.new_values or \ - 'description' in policy_differ.new_values: + if ( + "name" in policy_differ.new_values + or "description" in policy_differ.new_values + ): raise ArgumentValueError( - '\'name\' and \'description\' of the default VSAN policy ' - 'cannot be updated') + "'name' and 'description' of the default VSAN policy " + "cannot be updated" + ) changes_required = True - if __opts__['test']: + if __opts__["test"]: str_changes = [] if policy_differ.diffs: - str_changes.extend([change for change in - policy_differ.changes_str.split('\n')]) + str_changes.extend( + [change for change in policy_differ.changes_str.split("\n")] + ) if subprofile_differ.diffs or capabilities_differ.diffs: - str_changes.append('subprofiles:') + str_changes.append("subprofiles:") if subprofile_differ.diffs: str_changes.extend( - [' {0}'.format(change) for change in - subprofile_differ.changes_str.split('\n')]) + [ + " {0}".format(change) + for change in subprofile_differ.changes_str.split("\n") + ] + ) if capabilities_differ.diffs: - str_changes.append(' capabilities:') + str_changes.append(" capabilities:") str_changes.extend( - [' {0}'.format(change) for change in - capabilities_differ.changes_str2.split('\n')]) + [ + " {0}".format(change) + for change in capabilities_differ.changes_str2.split( + "\n" + ) + ] + ) comments.append( - 'State {0} will update the default VSAN policy on ' - 'vCenter \'{1}\':\n{2}' - ''.format(name, vcenter, '\n'.join(str_changes))) + "State {0} will update the default VSAN policy on " + "vCenter '{1}':\n{2}" + "".format(name, vcenter, "\n".join(str_changes)) + ) else: - __salt__['vsphere.update_storage_policy']( - policy=current_policy['name'], + __salt__["vsphere.update_storage_policy"]( + policy=current_policy["name"], policy_dict=policy_copy, - service_instance=si) - comments.append('Updated the default VSAN policy in vCenter ' - '\'{0}\''.format(vcenter)) + service_instance=si, + ) + comments.append( + "Updated the default VSAN policy in vCenter " + "'{0}'".format(vcenter) + ) log.info(comments[-1]) new_values = policy_differ.new_values - new_values['subprofiles'] = [subprofile_differ.new_values] - new_values['subprofiles'][0]['capabilities'] = \ - capabilities_differ.new_values - if not new_values['subprofiles'][0]['capabilities']: - del new_values['subprofiles'][0]['capabilities'] - if not new_values['subprofiles'][0]: - del new_values['subprofiles'] + new_values["subprofiles"] = [subprofile_differ.new_values] + new_values["subprofiles"][0][ + "capabilities" + ] = capabilities_differ.new_values + if not new_values["subprofiles"][0]["capabilities"]: + del new_values["subprofiles"][0]["capabilities"] + if not new_values["subprofiles"][0]: + del new_values["subprofiles"] old_values = policy_differ.old_values - old_values['subprofiles'] = [subprofile_differ.old_values] - old_values['subprofiles'][0]['capabilities'] = \ - capabilities_differ.old_values - if not old_values['subprofiles'][0]['capabilities']: - del old_values['subprofiles'][0]['capabilities'] - if not old_values['subprofiles'][0]: - del old_values['subprofiles'] - changes.update({'default_vsan_policy': - {'new': new_values, - 'old': old_values}}) + old_values["subprofiles"] = [subprofile_differ.old_values] + old_values["subprofiles"][0][ + "capabilities" + ] = capabilities_differ.old_values + if not old_values["subprofiles"][0]["capabilities"]: + del old_values["subprofiles"][0]["capabilities"] + if not old_values["subprofiles"][0]: + del old_values["subprofiles"] + changes.update( + {"default_vsan_policy": {"new": new_values, "old": old_values}} + ) log.trace(changes) - __salt__['vsphere.disconnect'](si) + __salt__["vsphere.disconnect"](si) except CommandExecutionError as exc: - log.error('Error: {}'.format(exc)) + log.error("Error: {}".format(exc)) if si: - __salt__['vsphere.disconnect'](si) - if not __opts__['test']: - ret['result'] = False - ret.update({'comment': exc.strerror, - 'result': False if not __opts__['test'] else None}) + __salt__["vsphere.disconnect"](si) + if not __opts__["test"]: + ret["result"] = False + ret.update( + {"comment": exc.strerror, "result": False if not __opts__["test"] else None} + ) return ret if not changes_required: # We have no changes - ret.update({'comment': ('Default VSAN policy in vCenter ' - '\'{0}\' is correctly configured. ' - 'Nothing to be done.'.format(vcenter)), - 'result': True}) + ret.update( + { + "comment": ( + "Default VSAN policy in vCenter " + "'{0}' is correctly configured. " + "Nothing to be done.".format(vcenter) + ), + "result": True, + } + ) else: - ret.update({ - 'comment': '\n'.join(comments), - 'changes': changes, - 'result': None if __opts__['test'] else True, - }) + ret.update( + { + "comment": "\n".join(comments), + "changes": changes, + "result": None if __opts__["test"] else True, + } + ) return ret def storage_policies_configured(name, policies): - ''' + """ Configures storage policies on a vCenter. policies List of dict representation of the required storage policies - ''' + """ comments = [] changes = [] changes_required = False - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': None} - log.trace('policies = {0}'.format(policies)) + ret = {"name": name, "changes": {}, "result": None, "comment": None} + log.trace("policies = {0}".format(policies)) si = None try: - proxy_type = __salt__['vsphere.get_proxy_type']() - log.trace('proxy_type = {0}'.format(proxy_type)) + proxy_type = __salt__["vsphere.get_proxy_type"]() + log.trace("proxy_type = {0}".format(proxy_type)) # All allowed proxies have a shim execution module with the same # name which implementes a get_details function # All allowed proxies have a vcenter detail - vcenter = __salt__['{0}.get_details'.format(proxy_type)]()['vcenter'] - log.info('Running state \'{0}\' on vCenter ' - '\'{1}\''.format(name, vcenter)) - si = __salt__['vsphere.get_service_instance_via_proxy']() - current_policies = __salt__['vsphere.list_storage_policies']( - policy_names=[policy['name'] for policy in policies], - service_instance=si) - log.trace('current_policies = {0}'.format(current_policies)) + vcenter = __salt__["{0}.get_details".format(proxy_type)]()["vcenter"] + log.info("Running state '{0}' on vCenter " "'{1}'".format(name, vcenter)) + si = __salt__["vsphere.get_service_instance_via_proxy"]() + current_policies = __salt__["vsphere.list_storage_policies"]( + policy_names=[policy["name"] for policy in policies], service_instance=si + ) + log.trace("current_policies = {0}".format(current_policies)) # TODO Refactor when recurse_differ supports list_differ # It's going to make the whole thing much easier for policy in policies: policy_copy = copy.deepcopy(policy) - filtered_policies = [p for p in current_policies - if p['name'] == policy['name']] - current_policy = filtered_policies[0] \ - if filtered_policies else None + filtered_policies = [ + p for p in current_policies if p["name"] == policy["name"] + ] + current_policy = filtered_policies[0] if filtered_policies else None if not current_policy: changes_required = True - if __opts__['test']: - comments.append('State {0} will create the storage policy ' - '\'{1}\' on vCenter \'{2}\'' - ''.format(name, policy['name'], vcenter)) + if __opts__["test"]: + comments.append( + "State {0} will create the storage policy " + "'{1}' on vCenter '{2}'" + "".format(name, policy["name"], vcenter) + ) else: - __salt__['vsphere.create_storage_policy']( - policy['name'], policy, service_instance=si) - comments.append('Created storage policy \'{0}\' on ' - 'vCenter \'{1}\''.format(policy['name'], - vcenter)) - changes.append({'new': policy, 'old': None}) + __salt__["vsphere.create_storage_policy"]( + policy["name"], policy, service_instance=si + ) + comments.append( + "Created storage policy '{0}' on " + "vCenter '{1}'".format(policy["name"], vcenter) + ) + changes.append({"new": policy, "old": None}) log.trace(comments[-1]) # Continue with next continue @@ -335,113 +368,143 @@ def storage_policies_configured(name, policies): # Building all diffs between the current and expected policy # XXX We simplify the comparison by assuming we have at most 1 # sub_profile - if policy.get('subprofiles'): - if len(policy['subprofiles']) > 1: - raise ArgumentValueError('Multiple sub_profiles ({0}) are not ' - 'supported in the input policy') - subprofile = policy['subprofiles'][0] - current_subprofile = current_policy['subprofiles'][0] - capabilities_differ = list_diff(current_subprofile['capabilities'], - subprofile.get('capabilities', []), - key='id') - del policy['subprofiles'] - if subprofile.get('capabilities'): - del subprofile['capabilities'] - del current_subprofile['capabilities'] + if policy.get("subprofiles"): + if len(policy["subprofiles"]) > 1: + raise ArgumentValueError( + "Multiple sub_profiles ({0}) are not " + "supported in the input policy" + ) + subprofile = policy["subprofiles"][0] + current_subprofile = current_policy["subprofiles"][0] + capabilities_differ = list_diff( + current_subprofile["capabilities"], + subprofile.get("capabilities", []), + key="id", + ) + del policy["subprofiles"] + if subprofile.get("capabilities"): + del subprofile["capabilities"] + del current_subprofile["capabilities"] # Get the subprofile diffs without the capability keys - subprofile_differ = recursive_diff(current_subprofile, - dict(subprofile)) + subprofile_differ = recursive_diff(current_subprofile, dict(subprofile)) - del current_policy['subprofiles'] + del current_policy["subprofiles"] policy_differ = recursive_diff(current_policy, policy) - if policy_differ.diffs or capabilities_differ.diffs or \ - subprofile_differ.diffs: + if ( + policy_differ.diffs + or capabilities_differ.diffs + or subprofile_differ.diffs + ): changes_required = True - if __opts__['test']: + if __opts__["test"]: str_changes = [] if policy_differ.diffs: str_changes.extend( - [change for change in - policy_differ.changes_str.split('\n')]) - if subprofile_differ.diffs or \ - capabilities_differ.diffs: + [change for change in policy_differ.changes_str.split("\n")] + ) + if subprofile_differ.diffs or capabilities_differ.diffs: - str_changes.append('subprofiles:') + str_changes.append("subprofiles:") if subprofile_differ.diffs: str_changes.extend( - [' {0}'.format(change) for change in - subprofile_differ.changes_str.split('\n')]) + [ + " {0}".format(change) + for change in subprofile_differ.changes_str.split( + "\n" + ) + ] + ) if capabilities_differ.diffs: - str_changes.append(' capabilities:') + str_changes.append(" capabilities:") str_changes.extend( - [' {0}'.format(change) for change in - capabilities_differ.changes_str2.split('\n')]) + [ + " {0}".format(change) + for change in capabilities_differ.changes_str2.split( + "\n" + ) + ] + ) comments.append( - 'State {0} will update the storage policy \'{1}\'' - ' on vCenter \'{2}\':\n{3}' - ''.format(name, policy['name'], vcenter, - '\n'.join(str_changes))) + "State {0} will update the storage policy '{1}'" + " on vCenter '{2}':\n{3}" + "".format(name, policy["name"], vcenter, "\n".join(str_changes)) + ) else: - __salt__['vsphere.update_storage_policy']( - policy=current_policy['name'], + __salt__["vsphere.update_storage_policy"]( + policy=current_policy["name"], policy_dict=policy_copy, - service_instance=si) - comments.append('Updated the storage policy \'{0}\'' - 'in vCenter \'{1}\'' - ''.format(policy['name'], vcenter)) + service_instance=si, + ) + comments.append( + "Updated the storage policy '{0}'" + "in vCenter '{1}'" + "".format(policy["name"], vcenter) + ) log.info(comments[-1]) # Build new/old values to report what was changed new_values = policy_differ.new_values - new_values['subprofiles'] = [subprofile_differ.new_values] - new_values['subprofiles'][0]['capabilities'] = \ - capabilities_differ.new_values - if not new_values['subprofiles'][0]['capabilities']: - del new_values['subprofiles'][0]['capabilities'] - if not new_values['subprofiles'][0]: - del new_values['subprofiles'] + new_values["subprofiles"] = [subprofile_differ.new_values] + new_values["subprofiles"][0][ + "capabilities" + ] = capabilities_differ.new_values + if not new_values["subprofiles"][0]["capabilities"]: + del new_values["subprofiles"][0]["capabilities"] + if not new_values["subprofiles"][0]: + del new_values["subprofiles"] old_values = policy_differ.old_values - old_values['subprofiles'] = [subprofile_differ.old_values] - old_values['subprofiles'][0]['capabilities'] = \ - capabilities_differ.old_values - if not old_values['subprofiles'][0]['capabilities']: - del old_values['subprofiles'][0]['capabilities'] - if not old_values['subprofiles'][0]: - del old_values['subprofiles'] - changes.append({'new': new_values, - 'old': old_values}) + old_values["subprofiles"] = [subprofile_differ.old_values] + old_values["subprofiles"][0][ + "capabilities" + ] = capabilities_differ.old_values + if not old_values["subprofiles"][0]["capabilities"]: + del old_values["subprofiles"][0]["capabilities"] + if not old_values["subprofiles"][0]: + del old_values["subprofiles"] + changes.append({"new": new_values, "old": old_values}) else: # No diffs found - no updates required - comments.append('Storage policy \'{0}\' is up to date. ' - 'Nothing to be done.'.format(policy['name'])) - __salt__['vsphere.disconnect'](si) + comments.append( + "Storage policy '{0}' is up to date. " + "Nothing to be done.".format(policy["name"]) + ) + __salt__["vsphere.disconnect"](si) except CommandExecutionError as exc: - log.error('Error: {0}'.format(exc)) + log.error("Error: {0}".format(exc)) if si: - __salt__['vsphere.disconnect'](si) - if not __opts__['test']: - ret['result'] = False - ret.update({'comment': exc.strerror, - 'result': False if not __opts__['test'] else None}) + __salt__["vsphere.disconnect"](si) + if not __opts__["test"]: + ret["result"] = False + ret.update( + {"comment": exc.strerror, "result": False if not __opts__["test"] else None} + ) return ret if not changes_required: # We have no changes - ret.update({'comment': ('All storage policy in vCenter ' - '\'{0}\' is correctly configured. ' - 'Nothing to be done.'.format(vcenter)), - 'result': True}) + ret.update( + { + "comment": ( + "All storage policy in vCenter " + "'{0}' is correctly configured. " + "Nothing to be done.".format(vcenter) + ), + "result": True, + } + ) else: - ret.update({ - 'comment': '\n'.join(comments), - 'changes': {'storage_policies': changes}, - 'result': None if __opts__['test'] else True, - }) + ret.update( + { + "comment": "\n".join(comments), + "changes": {"storage_policies": changes}, + "result": None if __opts__["test"] else True, + } + ) return ret def default_storage_policy_assigned(name, policy, datastore): - ''' + """ Assigns a default storage policy to a datastore policy @@ -449,54 +512,58 @@ def default_storage_policy_assigned(name, policy, datastore): datastore Name of datastore - ''' - log.info('Running state {0} for policy \'{1}\', datastore \'{2}\'.' - ''.format(name, policy, datastore)) + """ + log.info( + "Running state {0} for policy '{1}', datastore '{2}'." + "".format(name, policy, datastore) + ) changes = {} changes_required = False - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': None} + ret = {"name": name, "changes": {}, "result": None, "comment": None} si = None try: - si = __salt__['vsphere.get_service_instance_via_proxy']() - existing_policy = \ - __salt__['vsphere.list_default_storage_policy_of_datastore']( - datastore=datastore, service_instance=si) - if existing_policy['name'] == policy: - comment = ('Storage policy \'{0}\' is already assigned to ' - 'datastore \'{1}\'. Nothing to be done.' - ''.format(policy, datastore)) + si = __salt__["vsphere.get_service_instance_via_proxy"]() + existing_policy = __salt__["vsphere.list_default_storage_policy_of_datastore"]( + datastore=datastore, service_instance=si + ) + if existing_policy["name"] == policy: + comment = ( + "Storage policy '{0}' is already assigned to " + "datastore '{1}'. Nothing to be done." + "".format(policy, datastore) + ) else: changes_required = True changes = { - 'default_storage_policy': {'old': existing_policy['name'], - 'new': policy}} - if __opts__['test']: - comment = ('State {0} will assign storage policy \'{1}\' to ' - 'datastore \'{2}\'.').format(name, policy, - datastore) + "default_storage_policy": { + "old": existing_policy["name"], + "new": policy, + } + } + if __opts__["test"]: + comment = ( + "State {0} will assign storage policy '{1}' to " "datastore '{2}'." + ).format(name, policy, datastore) else: - __salt__['vsphere.assign_default_storage_policy_to_datastore']( - policy=policy, datastore=datastore, service_instance=si) - comment = ('Storage policy \'{0} was assigned to datastore ' - '\'{1}\'.').format(policy, name) + __salt__["vsphere.assign_default_storage_policy_to_datastore"]( + policy=policy, datastore=datastore, service_instance=si + ) + comment = ( + "Storage policy '{0} was assigned to datastore " "'{1}'." + ).format(policy, name) log.info(comment) except CommandExecutionError as exc: - log.error('Error: {}'.format(exc)) + log.error("Error: {}".format(exc)) if si: - __salt__['vsphere.disconnect'](si) - ret.update({'comment': exc.strerror, - 'result': False if not __opts__['test'] else None}) + __salt__["vsphere.disconnect"](si) + ret.update( + {"comment": exc.strerror, "result": False if not __opts__["test"] else None} + ) return ret - ret['comment'] = comment + ret["comment"] = comment if changes_required: - ret.update({ - 'changes': changes, - 'result': None if __opts__['test'] else True, - }) + ret.update({"changes": changes, "result": None if __opts__["test"] else True}) else: - ret['result'] = True + ret["result"] = True return ret diff --git a/salt/states/pcs.py b/salt/states/pcs.py index ddf8160f7d1..18a59e44fb6 100644 --- a/salt/states/pcs.py +++ b/salt/states/pcs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Pacemaker/Corosync clusters with PCS ================================================== @@ -161,7 +161,7 @@ Create a cluster from scratch: - cibname: cib_for_haproxy .. versionadded:: 2016.3.0 -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -180,73 +180,82 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if pcs package is installed - ''' - if salt.utils.path.which('pcs'): - return 'pcs' - return False + """ + if salt.utils.path.which("pcs"): + return "pcs" + return (False, "Unable to locate command: pcs") def _file_read(path): - ''' + """ Read a file and return content - ''' + """ content = False if os.path.exists(path): - with salt.utils.files.fopen(path, 'r+') as fp_: + with salt.utils.files.fopen(path, "r+") as fp_: content = salt.utils.stringutils.to_unicode(fp_.read()) fp_.close() return content def _file_write(path, content): - ''' + """ Write content to a file - ''' - with salt.utils.files.fopen(path, 'w+') as fp_: + """ + with salt.utils.files.fopen(path, "w+") as fp_: fp_.write(salt.utils.stringutils.to_str(content)) fp_.close() def _get_cibpath(): - ''' + """ Get the path to the directory on the minion where CIB's are saved - ''' - cibpath = os.path.join(__opts__['cachedir'], 'pcs', __env__) - log.trace('cibpath: %s', cibpath) + """ + cibpath = os.path.join(__opts__["cachedir"], "pcs", __env__) + log.trace("cibpath: %s", cibpath) return cibpath def _get_cibfile(cibname): - ''' + """ Get the full path of a cached CIB-file with the name of the CIB - ''' - cibfile = os.path.join(_get_cibpath(), '{0}.{1}'.format(cibname, 'cib')) - log.trace('cibfile: %s', cibfile) + """ + cibfile = os.path.join(_get_cibpath(), "{0}.{1}".format(cibname, "cib")) + log.trace("cibfile: %s", cibfile) return cibfile def _get_cibfile_tmp(cibname): - ''' + """ Get the full path of a temporary CIB-file with the name of the CIB - ''' - cibfile_tmp = '{0}.tmp'.format(_get_cibfile(cibname)) - log.trace('cibfile_tmp: %s', cibfile_tmp) + """ + cibfile_tmp = "{0}.tmp".format(_get_cibfile(cibname)) + log.trace("cibfile_tmp: %s", cibfile_tmp) return cibfile_tmp def _get_cibfile_cksum(cibname): - ''' + """ Get the full path of the file containing a checksum of a CIB-file with the name of the CIB - ''' - cibfile_cksum = '{0}.cksum'.format(_get_cibfile(cibname)) - log.trace('cibfile_cksum: %s', cibfile_cksum) + """ + cibfile_cksum = "{0}.cksum".format(_get_cibfile(cibname)) + log.trace("cibfile_cksum: %s", cibfile_cksum) return cibfile_cksum -def _item_present(name, item, item_id, item_type, show='show', create='create', extra_args=None, cibname=None): - ''' +def _item_present( + name, + item, + item_id, + item_type, + show="show", + create="create", + extra_args=None, + cibname=None, +): + """ Ensure that an item is created name @@ -265,8 +274,8 @@ def _item_present(name, item, item_id, item_type, show='show', create='create', additional options for the pcs command cibname use a cached CIB-file named like cibname instead of the live CIB - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} item_create_required = True cibfile = None @@ -279,86 +288,91 @@ def _item_present(name, item, item_id, item_type, show='show', create='create', # split off key and value (item_id contains =) item_id_key = item_id item_id_value = None - if '=' in item_id: - item_id_key = item_id.split('=')[0].strip() - item_id_value = item_id.replace(item_id.split('=')[0] + '=', '').strip() - log.trace('item_id_key=%s item_id_value=%s', item_id_key, item_id_value) + if "=" in item_id: + item_id_key = item_id.split("=")[0].strip() + item_id_value = item_id.replace(item_id.split("=")[0] + "=", "").strip() + log.trace("item_id_key=%s item_id_value=%s", item_id_key, item_id_value) # constraints, properties, resource defaults or resource op defaults # do not support specifying an id on 'show' command item_id_show = item_id - if item in ['constraint'] or '=' in item_id: + if item in ["constraint"] or "=" in item_id: item_id_show = None - is_existing = __salt__['pcs.item_show'](item=item, - item_id=item_id_show, - item_type=item_type, - show=show, - cibfile=cibfile) + is_existing = __salt__["pcs.item_show"]( + item=item, item_id=item_id_show, item_type=item_type, show=show, cibfile=cibfile + ) log.trace( - 'Output of pcs.item_show item=%s item_id=%s item_type=%s cibfile=%s: %s', - item, item_id_show, item_type, cibfile, is_existing + "Output of pcs.item_show item=%s item_id=%s item_type=%s cibfile=%s: %s", + item, + item_id_show, + item_type, + cibfile, + is_existing, ) # key,value pairs (item_id contains =) - match key and value if item_id_value is not None: - for line in is_existing['stdout'].splitlines(): - if len(line.split(':')) in [2]: - key = line.split(':')[0].strip() - value = line.split(':')[1].strip() + for line in is_existing["stdout"].splitlines(): + if len(line.split(":")) in [2]: + key = line.split(":")[0].strip() + value = line.split(":")[1].strip() if item_id_key in [key]: if item_id_value in [value]: item_create_required = False # constraints match on '(id:<id>)' - elif item in ['constraint']: - for line in is_existing['stdout'].splitlines(): - if '(id:{0})'.format(item_id) in line: + elif item in ["constraint"]: + for line in is_existing["stdout"].splitlines(): + if "(id:{0})".format(item_id) in line: item_create_required = False # item_id was provided, # return code 0 indicates, that resource already exists else: - if is_existing['retcode'] in [0]: + if is_existing["retcode"] in [0]: item_create_required = False if not item_create_required: - ret['comment'] += '{0} {1} ({2}) is already existing\n'.format( + ret["comment"] += "{0} {1} ({2}) is already existing\n".format( six.text_type(item), six.text_type(item_id), six.text_type(item_type) ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] += '{0} {1} ({2}) is set to be created\n'.format( + if __opts__["test"]: + ret["result"] = None + ret["comment"] += "{0} {1} ({2}) is set to be created\n".format( six.text_type(item), six.text_type(item_id), six.text_type(item_type) ) return ret - item_create = __salt__['pcs.item_create']( + item_create = __salt__["pcs.item_create"]( item=item, item_id=item_id, item_type=item_type, create=create, extra_args=extra_args, - cibfile=cibfile) + cibfile=cibfile, + ) - log.trace('Output of pcs.item_create: %s', item_create) + log.trace("Output of pcs.item_create: %s", item_create) - if item_create['retcode'] in [0]: - ret['comment'] += 'Created {0} {1} ({2})\n'.format(item, item_id, item_type) - ret['changes'].update({item_id: {'old': '', 'new': six.text_type(item_id)}}) + if item_create["retcode"] in [0]: + ret["comment"] += "Created {0} {1} ({2})\n".format(item, item_id, item_type) + ret["changes"].update({item_id: {"old": "", "new": six.text_type(item_id)}}) else: - ret['result'] = False - ret['comment'] += 'Failed to create {0} {1} ({2})\n'.format(item, item_id, item_type) + ret["result"] = False + ret["comment"] += "Failed to create {0} {1} ({2})\n".format( + item, item_id, item_type + ) - log.trace('ret: %s', ret) + log.trace("ret: %s", ret) return ret -def auth(name, nodes, pcsuser='hacluster', pcspasswd='hacluster', extra_args=None): - ''' +def auth(name, nodes, pcsuser="hacluster", pcspasswd="hacluster", extra_args=None): + """ Ensure all nodes are authorized to the cluster name @@ -384,68 +398,76 @@ def auth(name, nodes, pcsuser='hacluster', pcspasswd='hacluster', extra_args=Non - pcsuser: hacluster - pcspasswd: hoonetorg - extra_args: [] - ''' + """ - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} auth_required = False - authorized = __salt__['pcs.is_auth'](nodes=nodes) - log.trace('Output of pcs.is_auth: %s', authorized) + authorized = __salt__["pcs.is_auth"](nodes=nodes) + log.trace("Output of pcs.is_auth: %s", authorized) authorized_dict = {} - for line in authorized['stdout'].splitlines(): - node = line.split(':')[0].strip() - auth_state = line.split(':')[1].strip() + for line in authorized["stdout"].splitlines(): + node = line.split(":")[0].strip() + auth_state = line.split(":")[1].strip() if node in nodes: authorized_dict.update({node: auth_state}) - log.trace('authorized_dict: %s', authorized_dict) + log.trace("authorized_dict: %s", authorized_dict) for node in nodes: - if node in authorized_dict and authorized_dict[node] == 'Already authorized': - ret['comment'] += 'Node {0} is already authorized\n'.format(node) + if node in authorized_dict and authorized_dict[node] == "Already authorized": + ret["comment"] += "Node {0} is already authorized\n".format(node) else: auth_required = True - if __opts__['test']: - ret['comment'] += 'Node is set to authorize: {0}\n'.format(node) + if __opts__["test"]: + ret["comment"] += "Node is set to authorize: {0}\n".format(node) if not auth_required: return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret if not isinstance(extra_args, (list, tuple)): extra_args = [] - if '--force' not in extra_args: - extra_args += ['--force'] + if "--force" not in extra_args: + extra_args += ["--force"] - authorize = __salt__['pcs.auth'](nodes=nodes, pcsuser=pcsuser, pcspasswd=pcspasswd, extra_args=extra_args) - log.trace('Output of pcs.auth: %s', authorize) + authorize = __salt__["pcs.auth"]( + nodes=nodes, pcsuser=pcsuser, pcspasswd=pcspasswd, extra_args=extra_args + ) + log.trace("Output of pcs.auth: %s", authorize) authorize_dict = {} - for line in authorize['stdout'].splitlines(): - node = line.split(':')[0].strip() - auth_state = line.split(':')[1].strip() + for line in authorize["stdout"].splitlines(): + node = line.split(":")[0].strip() + auth_state = line.split(":")[1].strip() if node in nodes: authorize_dict.update({node: auth_state}) - log.trace('authorize_dict: %s', authorize_dict) + log.trace("authorize_dict: %s", authorize_dict) for node in nodes: - if node in authorize_dict and authorize_dict[node] == 'Authorized': - ret['comment'] += 'Authorized {0}\n'.format(node) - ret['changes'].update({node: {'old': '', 'new': 'Authorized'}}) + if node in authorize_dict and authorize_dict[node] == "Authorized": + ret["comment"] += "Authorized {0}\n".format(node) + ret["changes"].update({node: {"old": "", "new": "Authorized"}}) else: - ret['result'] = False + ret["result"] = False if node in authorized_dict: - ret['comment'] += 'Authorization check for node {0} returned: {1}\n'.format(node, authorized_dict[node]) + ret[ + "comment" + ] += "Authorization check for node {0} returned: {1}\n".format( + node, authorized_dict[node] + ) if node in authorize_dict: - ret['comment'] += 'Failed to authorize {0} with error {1}\n'.format(node, authorize_dict[node]) + ret["comment"] += "Failed to authorize {0} with error {1}\n".format( + node, authorize_dict[node] + ) return ret -def cluster_setup(name, nodes, pcsclustername='pcscluster', extra_args=None): - ''' +def cluster_setup(name, nodes, pcsclustername="pcscluster", extra_args=None): + """ Setup Pacemaker cluster on nodes. Should be run on one cluster node only (there may be races) @@ -472,69 +494,77 @@ def cluster_setup(name, nodes, pcsclustername='pcscluster', extra_args=None): - extra_args: - '--start' - '--enable' - ''' + """ - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} setup_required = False - config_show = __salt__['pcs.config_show']() - log.trace('Output of pcs.config_show: %s', config_show) + config_show = __salt__["pcs.config_show"]() + log.trace("Output of pcs.config_show: %s", config_show) - for line in config_show['stdout'].splitlines(): - if len(line.split(':')) in [2]: - key = line.split(':')[0].strip() - value = line.split(':')[1].strip() - if key in ['Cluster Name']: + for line in config_show["stdout"].splitlines(): + if len(line.split(":")) in [2]: + key = line.split(":")[0].strip() + value = line.split(":")[1].strip() + if key in ["Cluster Name"]: if value in [pcsclustername]: - ret['comment'] += 'Cluster {0} is already set up\n'.format(pcsclustername) + ret["comment"] += "Cluster {0} is already set up\n".format( + pcsclustername + ) else: setup_required = True - if __opts__['test']: - ret['comment'] += 'Cluster {0} is set to set up\n'.format(pcsclustername) + if __opts__["test"]: + ret["comment"] += "Cluster {0} is set to set up\n".format( + pcsclustername + ) if not setup_required: return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret if not isinstance(extra_args, (list, tuple)): extra_args = [] - setup = __salt__['pcs.cluster_setup'](nodes=nodes, pcsclustername=pcsclustername, extra_args=extra_args) - log.trace('Output of pcs.cluster_setup: %s', setup) + setup = __salt__["pcs.cluster_setup"]( + nodes=nodes, pcsclustername=pcsclustername, extra_args=extra_args + ) + log.trace("Output of pcs.cluster_setup: %s", setup) setup_dict = {} - for line in setup['stdout'].splitlines(): - log.trace('line: %s', line) - log.trace('line.split(:).len: %s', len(line.split(':'))) - if len(line.split(':')) in [2]: - node = line.split(':')[0].strip() - setup_state = line.split(':')[1].strip() + for line in setup["stdout"].splitlines(): + log.trace("line: %s", line) + log.trace("line.split(:).len: %s", len(line.split(":"))) + if len(line.split(":")) in [2]: + node = line.split(":")[0].strip() + setup_state = line.split(":")[1].strip() if node in nodes: setup_dict.update({node: setup_state}) - log.trace('setup_dict: %s', setup_dict) + log.trace("setup_dict: %s", setup_dict) for node in nodes: - if node in setup_dict and setup_dict[node] in ['Succeeded', 'Success']: - ret['comment'] += 'Set up {0}\n'.format(node) - ret['changes'].update({node: {'old': '', 'new': 'Setup'}}) + if node in setup_dict and setup_dict[node] in ["Succeeded", "Success"]: + ret["comment"] += "Set up {0}\n".format(node) + ret["changes"].update({node: {"old": "", "new": "Setup"}}) else: - ret['result'] = False - ret['comment'] += 'Failed to setup {0}\n'.format(node) + ret["result"] = False + ret["comment"] += "Failed to setup {0}\n".format(node) if node in setup_dict: - ret['comment'] += '{0}: setup_dict: {1}\n'.format(node, setup_dict[node]) - ret['comment'] += six.text_type(setup) + ret["comment"] += "{0}: setup_dict: {1}\n".format( + node, setup_dict[node] + ) + ret["comment"] += six.text_type(setup) - log.trace('ret: %s', ret) + log.trace("ret: %s", ret) return ret def cluster_node_present(name, node, extra_args=None): - ''' + """ Add a node to the Pacemaker cluster via PCS Should be run on one cluster node only (there may be races) @@ -557,80 +587,92 @@ def cluster_node_present(name, node, extra_args=None): - extra_args: - '--start' - '--enable' - ''' + """ - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} node_add_required = True current_nodes = [] - is_member_cmd = ['pcs', 'status', 'nodes', 'corosync'] - is_member = __salt__['cmd.run_all'](is_member_cmd, output_loglevel='trace', python_shell=False) - log.trace('Output of pcs status nodes corosync: %s', is_member) + is_member_cmd = ["pcs", "status", "nodes", "corosync"] + is_member = __salt__["cmd.run_all"]( + is_member_cmd, output_loglevel="trace", python_shell=False + ) + log.trace("Output of pcs status nodes corosync: %s", is_member) - for line in is_member['stdout'].splitlines(): - if len(line.split(':')) in [2]: - key = line.split(':')[0].strip() - value = line.split(':')[1].strip() - if key in ['Offline', 'Online']: + for line in is_member["stdout"].splitlines(): + if len(line.split(":")) in [2]: + key = line.split(":")[0].strip() + value = line.split(":")[1].strip() + if key in ["Offline", "Online"]: if len(value.split()) > 0: if node in value.split(): node_add_required = False - ret['comment'] += 'Node {0} is already member of the cluster\n'.format(node) + ret[ + "comment" + ] += "Node {0} is already member of the cluster\n".format(node) else: current_nodes += value.split() if not node_add_required: return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] += 'Node {0} is set to be added to the cluster\n'.format(node) + if __opts__["test"]: + ret["result"] = None + ret["comment"] += "Node {0} is set to be added to the cluster\n".format(node) return ret if not isinstance(extra_args, (list, tuple)): extra_args = [] - node_add = __salt__['pcs.cluster_node_add'](node=node, extra_args=extra_args) - log.trace('Output of pcs.cluster_node_add: %s', node_add) + node_add = __salt__["pcs.cluster_node_add"](node=node, extra_args=extra_args) + log.trace("Output of pcs.cluster_node_add: %s", node_add) node_add_dict = {} - for line in node_add['stdout'].splitlines(): - log.trace('line: %s', line) - log.trace('line.split(:).len: %s', len(line.split(':'))) - if len(line.split(':')) in [2]: - current_node = line.split(':')[0].strip() - current_node_add_state = line.split(':')[1].strip() + for line in node_add["stdout"].splitlines(): + log.trace("line: %s", line) + log.trace("line.split(:).len: %s", len(line.split(":"))) + if len(line.split(":")) in [2]: + current_node = line.split(":")[0].strip() + current_node_add_state = line.split(":")[1].strip() if current_node in current_nodes + [node]: node_add_dict.update({current_node: current_node_add_state}) - log.trace('node_add_dict: %s', node_add_dict) + log.trace("node_add_dict: %s", node_add_dict) for current_node in current_nodes: if current_node in node_add_dict: - if node_add_dict[current_node] not in ['Corosync updated']: - ret['result'] = False - ret['comment'] += 'Failed to update corosync.conf on node {0}\n'.format(current_node) - ret['comment'] += '{0}: node_add_dict: {1}\n'.format(current_node, node_add_dict[current_node]) + if node_add_dict[current_node] not in ["Corosync updated"]: + ret["result"] = False + ret["comment"] += "Failed to update corosync.conf on node {0}\n".format( + current_node + ) + ret["comment"] += "{0}: node_add_dict: {1}\n".format( + current_node, node_add_dict[current_node] + ) else: - ret['result'] = False - ret['comment'] += 'Failed to update corosync.conf on node {0}\n'.format(current_node) + ret["result"] = False + ret["comment"] += "Failed to update corosync.conf on node {0}\n".format( + current_node + ) - if node in node_add_dict and node_add_dict[node] in ['Succeeded', 'Success']: - ret['comment'] += 'Added node {0}\n'.format(node) - ret['changes'].update({node: {'old': '', 'new': 'Added'}}) + if node in node_add_dict and node_add_dict[node] in ["Succeeded", "Success"]: + ret["comment"] += "Added node {0}\n".format(node) + ret["changes"].update({node: {"old": "", "new": "Added"}}) else: - ret['result'] = False - ret['comment'] += 'Failed to add node{0}\n'.format(node) + ret["result"] = False + ret["comment"] += "Failed to add node{0}\n".format(node) if node in node_add_dict: - ret['comment'] += '{0}: node_add_dict: {1}\n'.format(node, node_add_dict[node]) - ret['comment'] += six.text_type(node_add) + ret["comment"] += "{0}: node_add_dict: {1}\n".format( + node, node_add_dict[node] + ) + ret["comment"] += six.text_type(node_add) - log.trace('ret: %s', ret) + log.trace("ret: %s", ret) return ret def cib_present(name, cibname, scope=None, extra_args=None): - ''' + """ Ensure that a CIB-file with the content of the current live CIB is created Should be run on one cluster node only @@ -654,10 +696,10 @@ def cib_present(name, cibname, scope=None, extra_args=None): - cibname: cib_for_galera - scope: None - extra_args: None - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - cib_hash_form = 'sha256' + cib_hash_form = "sha256" cib_create_required = False cib_cksum_required = False @@ -675,78 +717,90 @@ def cib_present(name, cibname, scope=None, extra_args=None): extra_args = [] if os.path.exists(cibfile_tmp): - __salt__['file.remove'](cibfile_tmp) + __salt__["file.remove"](cibfile_tmp) - cib_create = __salt__['pcs.cib_create'](cibfile=cibfile_tmp, scope=scope, extra_args=extra_args) - log.trace('Output of pcs.cib_create: %s', cib_create) + cib_create = __salt__["pcs.cib_create"]( + cibfile=cibfile_tmp, scope=scope, extra_args=extra_args + ) + log.trace("Output of pcs.cib_create: %s", cib_create) - if cib_create['retcode'] not in [0] or not os.path.exists(cibfile_tmp): - ret['result'] = False - ret['comment'] += 'Failed to get live CIB\n' + if cib_create["retcode"] not in [0] or not os.path.exists(cibfile_tmp): + ret["result"] = False + ret["comment"] += "Failed to get live CIB\n" return ret - cib_hash_live = '{0}:{1}'.format(cib_hash_form, __salt__['file.get_hash'](path=cibfile_tmp, form=cib_hash_form)) - log.trace('cib_hash_live: %s', cib_hash_live) + cib_hash_live = "{0}:{1}".format( + cib_hash_form, __salt__["file.get_hash"](path=cibfile_tmp, form=cib_hash_form) + ) + log.trace("cib_hash_live: %s", cib_hash_live) cib_hash_cur = _file_read(path=cibfile_cksum) if cib_hash_cur not in [cib_hash_live]: cib_cksum_required = True - log.trace('cib_hash_cur: %s', cib_hash_cur) + log.trace("cib_hash_cur: %s", cib_hash_cur) - if not os.path.exists(cibfile) or not __salt__['file.check_hash'](path=cibfile, file_hash=cib_hash_live): + if not os.path.exists(cibfile) or not __salt__["file.check_hash"]( + path=cibfile, file_hash=cib_hash_live + ): cib_create_required = True if cib_cksum_required or cib_create_required: cib_required = True if not cib_create_required: - __salt__['file.remove'](cibfile_tmp) - ret['comment'] += 'CIB {0} is already equal to the live CIB\n'.format(cibname) + __salt__["file.remove"](cibfile_tmp) + ret["comment"] += "CIB {0} is already equal to the live CIB\n".format(cibname) if not cib_cksum_required: - ret['comment'] += 'CIB {0} checksum is correct\n'.format(cibname) + ret["comment"] += "CIB {0} checksum is correct\n".format(cibname) if not cib_required: return ret - if __opts__['test']: - __salt__['file.remove'](cibfile_tmp) - ret['result'] = None + if __opts__["test"]: + __salt__["file.remove"](cibfile_tmp) + ret["result"] = None if cib_create_required: - ret['comment'] += 'CIB {0} is set to be created/updated\n'.format(cibname) + ret["comment"] += "CIB {0} is set to be created/updated\n".format(cibname) if cib_cksum_required: - ret['comment'] += 'CIB {0} checksum is set to be created/updated\n'.format(cibname) + ret["comment"] += "CIB {0} checksum is set to be created/updated\n".format( + cibname + ) return ret if cib_create_required: - __salt__['file.move'](cibfile_tmp, cibfile) + __salt__["file.move"](cibfile_tmp, cibfile) - if __salt__['file.check_hash'](path=cibfile, file_hash=cib_hash_live): - ret['comment'] += 'Created/updated CIB {0}\n'.format(cibname) - ret['changes'].update({'cibfile': cibfile}) + if __salt__["file.check_hash"](path=cibfile, file_hash=cib_hash_live): + ret["comment"] += "Created/updated CIB {0}\n".format(cibname) + ret["changes"].update({"cibfile": cibfile}) else: - ret['result'] = False - ret['comment'] += 'Failed to create/update CIB {0}\n'.format(cibname) + ret["result"] = False + ret["comment"] += "Failed to create/update CIB {0}\n".format(cibname) if cib_cksum_required: _file_write(cibfile_cksum, cib_hash_live) if _file_read(cibfile_cksum) in [cib_hash_live]: - ret['comment'] += 'Created/updated checksum {0} of CIB {1}\n'.format(cib_hash_live, cibname) - ret['changes'].update({'cibcksum': cib_hash_live}) + ret["comment"] += "Created/updated checksum {0} of CIB {1}\n".format( + cib_hash_live, cibname + ) + ret["changes"].update({"cibcksum": cib_hash_live}) else: - ret['result'] = False - ret['comment'] += 'Failed to create/update checksum {0} CIB {1}\n'.format(cib_hash_live, cibname) + ret["result"] = False + ret["comment"] += "Failed to create/update checksum {0} CIB {1}\n".format( + cib_hash_live, cibname + ) - log.trace('ret: %s', ret) + log.trace("ret: %s", ret) return ret def cib_pushed(name, cibname, scope=None, extra_args=None): - ''' + """ Ensure that a CIB-file is pushed if it is changed since the creation of it with pcs.cib_present Should be run on one cluster node only @@ -770,10 +824,10 @@ def cib_pushed(name, cibname, scope=None, extra_args=None): - cibname: cib_for_galera - scope: None - extra_args: None - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - cib_hash_form = 'sha256' + cib_hash_form = "sha256" cib_push_required = False @@ -784,42 +838,52 @@ def cib_pushed(name, cibname, scope=None, extra_args=None): extra_args = [] if not os.path.exists(cibfile): - ret['result'] = False - ret['comment'] += 'CIB-file {0} does not exist\n'.format(cibfile) + ret["result"] = False + ret["comment"] += "CIB-file {0} does not exist\n".format(cibfile) return ret - cib_hash_cibfile = '{0}:{1}'.format(cib_hash_form, __salt__['file.get_hash'](path=cibfile, form=cib_hash_form)) - log.trace('cib_hash_cibfile: %s', cib_hash_cibfile) + cib_hash_cibfile = "{0}:{1}".format( + cib_hash_form, __salt__["file.get_hash"](path=cibfile, form=cib_hash_form) + ) + log.trace("cib_hash_cibfile: %s", cib_hash_cibfile) if _file_read(cibfile_cksum) not in [cib_hash_cibfile]: cib_push_required = True if not cib_push_required: - ret['comment'] += 'CIB {0} is not changed since creation through pcs.cib_present\n'.format(cibname) + ret[ + "comment" + ] += "CIB {0} is not changed since creation through pcs.cib_present\n".format( + cibname + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] += 'CIB {0} is set to be pushed as the new live CIB\n'.format(cibname) + if __opts__["test"]: + ret["result"] = None + ret["comment"] += "CIB {0} is set to be pushed as the new live CIB\n".format( + cibname + ) return ret - cib_push = __salt__['pcs.cib_push'](cibfile=cibfile, scope=scope, extra_args=extra_args) - log.trace('Output of pcs.cib_push: %s', cib_push) + cib_push = __salt__["pcs.cib_push"]( + cibfile=cibfile, scope=scope, extra_args=extra_args + ) + log.trace("Output of pcs.cib_push: %s", cib_push) - if cib_push['retcode'] in [0]: - ret['comment'] += 'Pushed CIB {0}\n'.format(cibname) - ret['changes'].update({'cibfile_pushed': cibfile}) + if cib_push["retcode"] in [0]: + ret["comment"] += "Pushed CIB {0}\n".format(cibname) + ret["changes"].update({"cibfile_pushed": cibfile}) else: - ret['result'] = False - ret['comment'] += 'Failed to push CIB {0}\n'.format(cibname) + ret["result"] = False + ret["comment"] += "Failed to push CIB {0}\n".format(cibname) - log.trace('ret: %s', ret) + log.trace("ret: %s", ret) return ret def prop_has_value(name, prop, value, extra_args=None, cibname=None): - ''' + """ Ensure that a property in the cluster is set to a given value Should be run on one cluster node only @@ -845,18 +909,20 @@ def prop_has_value(name, prop, value, extra_args=None, cibname=None): - prop: no-quorum-policy - value: ignore - cibname: cib_for_cluster_settings - ''' - return _item_present(name=name, - item='property', - item_id='{0}={1}'.format(prop, value), - item_type=None, - create='set', - extra_args=extra_args, - cibname=cibname) + """ + return _item_present( + name=name, + item="property", + item_id="{0}={1}".format(prop, value), + item_type=None, + create="set", + extra_args=extra_args, + cibname=cibname, + ) def resource_defaults_to(name, default, value, extra_args=None, cibname=None): - ''' + """ Ensure a resource default in the cluster is set to a given value Should be run on one cluster node only @@ -883,19 +949,21 @@ def resource_defaults_to(name, default, value, extra_args=None, cibname=None): - default: resource-stickiness - value: 100 - cibname: cib_for_cluster_settings - ''' - return _item_present(name=name, - item='resource', - item_id='{0}={1}'.format(default, value), - item_type=None, - show='defaults', - create='defaults', - extra_args=extra_args, - cibname=cibname) + """ + return _item_present( + name=name, + item="resource", + item_id="{0}={1}".format(default, value), + item_type=None, + show="defaults", + create="defaults", + extra_args=extra_args, + cibname=cibname, + ) def resource_op_defaults_to(name, op_default, value, extra_args=None, cibname=None): - ''' + """ Ensure a resource operation default in the cluster is set to a given value Should be run on one cluster node only @@ -922,19 +990,23 @@ def resource_op_defaults_to(name, op_default, value, extra_args=None, cibname=No - op_default: monitor-interval - value: 60s - cibname: cib_for_cluster_settings - ''' - return _item_present(name=name, - item='resource', - item_id='{0}={1}'.format(op_default, value), - item_type=None, - show=['op', 'defaults'], - create=['op', 'defaults'], - extra_args=extra_args, - cibname=cibname) + """ + return _item_present( + name=name, + item="resource", + item_id="{0}={1}".format(op_default, value), + item_type=None, + show=["op", "defaults"], + create=["op", "defaults"], + extra_args=extra_args, + cibname=cibname, + ) -def stonith_present(name, stonith_id, stonith_device_type, stonith_device_options=None, cibname=None): - ''' +def stonith_present( + name, stonith_id, stonith_device_type, stonith_device_options=None, cibname=None +): + """ Ensure that a fencing resource is created Should be run on one cluster node only @@ -969,17 +1041,21 @@ def stonith_present(name, stonith_id, stonith_device_type, stonith_device_option - 'login=hidden' - 'passwd=hoonetorg' - cibname: cib_for_stonith - ''' - return _item_present(name=name, - item='stonith', - item_id=stonith_id, - item_type=stonith_device_type, - extra_args=stonith_device_options, - cibname=cibname) + """ + return _item_present( + name=name, + item="stonith", + item_id=stonith_id, + item_type=stonith_device_type, + extra_args=stonith_device_options, + cibname=cibname, + ) -def resource_present(name, resource_id, resource_type, resource_options=None, cibname=None): - ''' +def resource_present( + name, resource_id, resource_type, resource_options=None, cibname=None +): + """ Ensure that a resource is created Should be run on one cluster node only @@ -1009,17 +1085,21 @@ def resource_present(name, resource_id, resource_type, resource_options=None, ci - 'wsrep_cluster_address=gcomm://node1.example.org,node2.example.org,node3.example.org' - '--master' - cibname: cib_for_galera - ''' - return _item_present(name=name, - item='resource', - item_id=resource_id, - item_type=resource_type, - extra_args=resource_options, - cibname=cibname) + """ + return _item_present( + name=name, + item="resource", + item_id=resource_id, + item_type=resource_type, + extra_args=resource_options, + cibname=cibname, + ) -def constraint_present(name, constraint_id, constraint_type, constraint_options=None, cibname=None): - ''' +def constraint_present( + name, constraint_id, constraint_type, constraint_options=None, cibname=None +): + """ Ensure that a constraint is created Should be run on one cluster node only @@ -1051,11 +1131,13 @@ def constraint_present(name, constraint_id, constraint_type, constraint_options= - 'with' - 'haproxy-clone' - cibname: cib_for_haproxy - ''' - return _item_present(name=name, - item='constraint', - item_id=constraint_id, - item_type=constraint_type, - create=None, - extra_args=constraint_options, - cibname=cibname) + """ + return _item_present( + name=name, + item="constraint", + item_id=constraint_id, + item_type=constraint_type, + create=None, + extra_args=constraint_options, + cibname=cibname, + ) diff --git a/salt/states/pdbedit.py b/salt/states/pdbedit.py index d6bef932f42..00833cb1fea 100644 --- a/salt/states/pdbedit.py +++ b/salt/states/pdbedit.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage accounts in Samba's passdb using pdbedit :maintainer: Jorge Schrauwen <sjorge@blackdot.be> @@ -20,7 +20,7 @@ Manage accounts in Samba's passdb using pdbedit - password_hashed: True - drive: 'X:' - homedir: '\\\\serenity\\mechanic\\profile' -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -32,51 +32,48 @@ import salt.utils.data log = logging.getLogger(__name__) # Define the state's virtual name -__virtualname__ = 'pdbedit' +__virtualname__ = "pdbedit" def __virtual__(): - ''' + """ Provides pdbedit when available - ''' - if 'pdbedit.create' in __salt__: + """ + if "pdbedit.create" in __salt__: return True else: return ( False, - '{0} state module can only be loaded when the pdbedit module is available'.format( + "{0} state module can only be loaded when the pdbedit module is available".format( __virtualname__ - ) + ), ) def absent(name): - ''' + """ Ensure user account is absent name : string username - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # remove if needed - if name in __salt__['pdbedit.list'](False): - res = __salt__['pdbedit.delete'](name) - if res[name] in ['deleted']: # check if we need to update changes - ret['changes'].update(res) - elif res[name] not in ['absent']: # oops something went wrong - ret['result'] = False + if name in __salt__["pdbedit.list"](False): + res = __salt__["pdbedit.delete"](name) + if res[name] in ["deleted"]: # check if we need to update changes + ret["changes"].update(res) + elif res[name] not in ["absent"]: # oops something went wrong + ret["result"] = False else: - ret['comment'] = 'account {login} is absent'.format(login=name) + ret["comment"] = "account {login} is absent".format(login=name) return ret def managed(name, **kwargs): - ''' + """ Manage user account login : string @@ -117,39 +114,36 @@ def managed(name, **kwargs): reset the users allowed logon hours reset_bad_password_count : boolean reset the stored bad login counter - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # save state - saved = __salt__['pdbedit.list'](hashes=True) + saved = __salt__["pdbedit.list"](hashes=True) saved = saved[name] if name in saved else {} # call pdbedit.modify - kwargs['login'] = name - res = __salt__['pdbedit.modify'](**kwargs) + kwargs["login"] = name + res = __salt__["pdbedit.modify"](**kwargs) # calculate changes - if res[name] in ['created']: - ret['changes'] = res - elif res[name] in ['updated']: - ret['changes'][name] = salt.utils.data.compare_dicts( - saved, - __salt__['pdbedit.list'](hashes=True)[name], + if res[name] in ["created"]: + ret["changes"] = res + elif res[name] in ["updated"]: + ret["changes"][name] = salt.utils.data.compare_dicts( + saved, __salt__["pdbedit.list"](hashes=True)[name], ) - elif res[name] not in ['unchanged']: - ret['result'] = False - ret['comment'] = res[name] + elif res[name] not in ["unchanged"]: + ret["result"] = False + ret["comment"] = res[name] return ret def present(name, **kwargs): - ''' + """ Alias for pdbedit.managed - ''' + """ return managed(name, **kwargs) + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/states/pecl.py b/salt/states/pecl.py index 051e536ca3e..e224f016a80 100644 --- a/salt/states/pecl.py +++ b/salt/states/pecl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of PHP Extensions Using pecl ========================================= @@ -17,7 +17,7 @@ requisite to a pkg.installed state for the package which provides pecl pecl.installed: - require: - pkg: php-pear -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs @@ -25,18 +25,18 @@ from salt.ext import six def __virtual__(): - ''' + """ Only load if the pecl module is available in __salt__ - ''' - return 'pecl' if 'pecl.list' in __salt__ else False + """ + if "pecl.list" in __salt__: + return "pecl" + return (False, "pecl module could not be loaded") -def installed(name, - version=None, - defaults=False, - force=False, - preferred_state='stable'): - ''' +def installed( + name, version=None, defaults=False, force=False, preferred_state="stable" +): + """ .. versionadded:: 0.17.0 Make sure that a pecl extension is installed. @@ -58,76 +58,70 @@ def installed(name, preferred_state The pecl extension state to install - ''' + """ # Check to see if we have a designated version if not isinstance(version, six.string_types) and version is not None: version = six.text_type(version) - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if '/' in name: - channel, package = name.split('/') + if "/" in name: + channel, package = name.split("/") else: channel = None package = name - installed_pecls = __salt__['pecl.list'](channel) + installed_pecls = __salt__["pecl.list"](channel) if package in installed_pecls: # The package is only installed if version is absent or matches - if (version is None or version in installed_pecls[package]) \ - and preferred_state in installed_pecls[package]: - ret['result'] = True - ret['comment'] = ('Pecl extension {0} is already installed.' - .format(name)) + if ( + version is None or version in installed_pecls[package] + ) and preferred_state in installed_pecls[package]: + ret["result"] = True + ret["comment"] = "Pecl extension {0} is already installed.".format(name) return ret if version is not None: # Modify the name to include the version and proceed. - name = '{0}-{1}'.format(name, version) + name = "{0}-{1}".format(name, version) - if __opts__['test']: - ret['comment'] = ('Pecl extension {0} would have been installed' - .format(name)) + if __opts__["test"]: + ret["comment"] = "Pecl extension {0} would have been installed".format(name) return ret - if __salt__['pecl.install'](name, defaults=defaults, force=force, - preferred_state=preferred_state): - ret['result'] = True - ret['changes'][name] = 'Installed' - ret['comment'] = ('Pecl extension {0} was successfully installed' - .format(name)) + if __salt__["pecl.install"]( + name, defaults=defaults, force=force, preferred_state=preferred_state + ): + ret["result"] = True + ret["changes"][name] = "Installed" + ret["comment"] = "Pecl extension {0} was successfully installed".format(name) else: - ret['result'] = False - ret['comment'] = 'Could not install pecl extension {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Could not install pecl extension {0}.".format(name) return ret def removed(name): - ''' + """ Make sure that a pecl extension is not installed. name The pecl extension name to uninstall - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} - if name not in __salt__['pecl.list'](): - ret['result'] = True - ret['comment'] = 'Pecl extension {0} is not installed.'.format(name) + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} + if name not in __salt__["pecl.list"](): + ret["result"] = True + ret["comment"] = "Pecl extension {0} is not installed.".format(name) return ret - if __opts__['test']: - ret['comment'] = ('Pecl extension {0} would have been removed' - .format(name)) + if __opts__["test"]: + ret["comment"] = "Pecl extension {0} would have been removed".format(name) return ret - if __salt__['pecl.uninstall'](name): - ret['result'] = True - ret['changes'][name] = 'Removed' - ret['comment'] = ('Pecl extension {0} was successfully removed.' - .format(name)) + if __salt__["pecl.uninstall"](name): + ret["result"] = True + ret["changes"][name] = "Removed" + ret["comment"] = "Pecl extension {0} was successfully removed.".format(name) else: - ret['result'] = False - ret['comment'] = 'Could not remove pecl extension {0}.'.format(name) + ret["result"] = False + ret["comment"] = "Could not remove pecl extension {0}.".format(name) return ret diff --git a/salt/states/pip_state.py b/salt/states/pip_state.py index 755650eb0b3..ad2d3218caa 100644 --- a/salt/states/pip_state.py +++ b/salt/states/pip_state.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of Python Packages Using pip ========================================= @@ -17,82 +17,81 @@ requisite to a pkg.installed state for the package which provides pip pip.installed: - require: - pkg: python-pip -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import re -import types + import logging +import re import sys -try: - import pkg_resources - HAS_PKG_RESOURCES = True -except ImportError: - HAS_PKG_RESOURCES = False +import types + +# Import 3rd-party libs +import salt.ext.six as six # Import salt libs import salt.utils.data import salt.utils.versions -from salt.version import SaltStackVersion as _SaltStackVersion from salt.exceptions import CommandExecutionError, CommandNotFoundError +from salt.version import SaltStackVersion as _SaltStackVersion + +try: + import pkg_resources + + HAS_PKG_RESOURCES = True +except ImportError: + HAS_PKG_RESOURCES = False + -# Import 3rd-party libs -import salt.ext.six as six # pylint: disable=import-error def purge_pip(): - ''' + """ Purge pip and its sub-modules - ''' + """ # Remove references to the loaded pip module above so reloading works - if 'pip' not in sys.modules: + if "pip" not in sys.modules: return pip_related_entries = [ - (k, v) for (k, v) in sys.modules.items() - if getattr(v, '__module__', '').startswith('pip.') - or (isinstance(v, types.ModuleType) and v.__name__.startswith('pip.')) + (k, v) + for (k, v) in sys.modules.items() + if getattr(v, "__module__", "").startswith("pip.") + or (isinstance(v, types.ModuleType) and v.__name__.startswith("pip.")) ] for name, entry in pip_related_entries: sys.modules.pop(name) del entry - if 'pip' in globals(): - del globals()['pip'] - if 'pip' in locals(): - del locals()['pip'] - sys_modules_pip = sys.modules.pop('pip', None) + if "pip" in globals(): + del globals()["pip"] + if "pip" in locals(): + del locals()["pip"] + sys_modules_pip = sys.modules.pop("pip", None) if sys_modules_pip is not None: del sys_modules_pip def pip_has_internal_exceptions_mod(ver): - ''' + """ True when the pip version has the `pip._internal.exceptions` module - ''' - return salt.utils.versions.compare( - ver1=ver, - oper='>=', - ver2='10.0', - ) + """ + return salt.utils.versions.compare(ver1=ver, oper=">=", ver2="10.0",) def pip_has_exceptions_mod(ver): - ''' + """ True when the pip version has the `pip.exceptions` module - ''' + """ if pip_has_internal_exceptions_mod(ver): return False - return salt.utils.versions.compare( - ver1=ver, - oper='>=', - ver2='1.0' - ) + return salt.utils.versions.compare(ver1=ver, oper=">=", ver2="1.0") try: import pip + HAS_PIP = True except ImportError: HAS_PIP = False @@ -100,19 +99,18 @@ except ImportError: if HAS_PIP is True: - if not hasattr(purge_pip, '__pip_ver__'): + if not hasattr(purge_pip, "__pip_ver__"): purge_pip.__pip_ver__ = pip.__version__ elif purge_pip.__pip_ver__ != pip.__version__: purge_pip() import pip + purge_pip.__pip_ver__ = pip.__version__ - if salt.utils.versions.compare(ver1=pip.__version__, - oper='>=', - ver2='10.0'): - from pip._internal.exceptions import InstallationError # pylint: disable=E0611,E0401 - elif salt.utils.versions.compare(ver1=pip.__version__, - oper='>=', - ver2='1.0'): + if salt.utils.versions.compare(ver1=pip.__version__, oper=">=", ver2="10.0"): + from pip._internal.exceptions import ( + InstallationError, + ) # pylint: disable=E0611,E0401 + elif salt.utils.versions.compare(ver1=pip.__version__, oper=">=", ver2="1.0"): from pip.exceptions import InstallationError # pylint: disable=E0611,E0401 else: InstallationError = ValueError @@ -123,65 +121,66 @@ if HAS_PIP is True: log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'pip' +__virtualname__ = "pip" def _from_line(*args, **kwargs): import pip - if salt.utils.versions.compare(ver1=pip.__version__, - oper='>=', - ver2='18.1'): + + if salt.utils.versions.compare(ver1=pip.__version__, oper=">=", ver2="18.1"): import pip._internal.req.constructors # pylint: disable=E0611,E0401 + return pip._internal.req.constructors.install_req_from_line(*args, **kwargs) - elif salt.utils.versions.compare(ver1=pip.__version__, - oper='>=', - ver2='10.0'): + elif salt.utils.versions.compare(ver1=pip.__version__, oper=">=", ver2="10.0"): import pip._internal.req # pylint: disable=E0611,E0401 + return pip._internal.req.InstallRequirement.from_line(*args, **kwargs) else: import pip.req # pylint: disable=E0611,E0401 + return pip.req.InstallRequirement.from_line(*args, **kwargs) def __virtual__(): - ''' + """ Only load if the pip module is available in __salt__ - ''' + """ if HAS_PKG_RESOURCES is False: - return False, 'The pkg_resources python library is not installed' - if 'pip.list' in __salt__: + return False, "The pkg_resources python library is not installed" + if "pip.list" in __salt__: return __virtualname__ return False def _fulfills_version_spec(version, version_spec): - ''' + """ Check version number against version specification info and return a boolean value based on whether or not the version number meets the specified version. - ''' + """ for oper, spec in version_spec: if oper is None: continue - if not salt.utils.versions.compare(ver1=version, oper=oper, ver2=spec, cmp_func=_pep440_version_cmp): + if not salt.utils.versions.compare( + ver1=version, oper=oper, ver2=spec, cmp_func=_pep440_version_cmp + ): return False return True def _check_pkg_version_format(pkg): - ''' + """ Takes a package name and version specification (if any) and checks it using the pip library. - ''' + """ - ret = {'result': False, 'comment': None, - 'prefix': None, 'version_spec': None} + ret = {"result": False, "comment": None, "prefix": None, "version_spec": None} if not HAS_PIP: - ret['comment'] = ( - 'An importable Python 2 pip module is required but could not be ' - 'found on your system. This usually means that the system\'s pip ' - 'package is not installed properly.' + ret["comment"] = ( + "An importable Python 2 pip module is required but could not be " + "found on your system. This usually means that the system's pip " + "package is not installed properly." ) return ret @@ -194,73 +193,71 @@ def _check_pkg_version_format(pkg): # vcs+URL urls are not properly parsed. # The next line is meant to trigger an AttributeError and # handle lower pip versions - log.debug('Installed pip version: %s', pip.__version__) + log.debug("Installed pip version: %s", pip.__version__) install_req = _from_line(pkg) except AttributeError: - log.debug('Installed pip version is lower than 1.2') - supported_vcs = ('git', 'svn', 'hg', 'bzr') + log.debug("Installed pip version is lower than 1.2") + supported_vcs = ("git", "svn", "hg", "bzr") if pkg.startswith(supported_vcs): for vcs in supported_vcs: if pkg.startswith(vcs): from_vcs = True - install_req = _from_line( - pkg.split('{0}+'.format(vcs))[-1] - ) + install_req = _from_line(pkg.split("{0}+".format(vcs))[-1]) break else: install_req = _from_line(pkg) except (ValueError, InstallationError) as exc: - ret['result'] = False - if not from_vcs and '=' in pkg and '==' not in pkg: - ret['comment'] = ( - 'Invalid version specification in package {0}. \'=\' is ' - 'not supported, use \'==\' instead.'.format(pkg) + ret["result"] = False + if not from_vcs and "=" in pkg and "==" not in pkg: + ret["comment"] = ( + "Invalid version specification in package {0}. '=' is " + "not supported, use '==' instead.".format(pkg) ) return ret - ret['comment'] = ( - 'pip raised an exception while parsing \'{0}\': {1}'.format( - pkg, exc - ) + ret["comment"] = "pip raised an exception while parsing '{0}': {1}".format( + pkg, exc ) return ret if install_req.req is None: # This is most likely an url and there's no way to know what will # be installed before actually installing it. - ret['result'] = True - ret['prefix'] = '' - ret['version_spec'] = [] + ret["result"] = True + ret["prefix"] = "" + ret["version_spec"] = [] else: - ret['result'] = True + ret["result"] = True try: - ret['prefix'] = install_req.req.project_name - ret['version_spec'] = install_req.req.specs + ret["prefix"] = install_req.req.project_name + ret["version_spec"] = install_req.req.specs except Exception: # pylint: disable=broad-except - ret['prefix'] = re.sub('[^A-Za-z0-9.]+', '-', install_req.name) + ret["prefix"] = re.sub("[^A-Za-z0-9.]+", "-", install_req.name) if hasattr(install_req, "specifier"): specifier = install_req.specifier else: specifier = install_req.req.specifier - ret['version_spec'] = [(spec.operator, spec.version) for spec in specifier] + ret["version_spec"] = [(spec.operator, spec.version) for spec in specifier] return ret -def _check_if_installed(prefix, - state_pkg_name, - version_spec, - ignore_installed, - force_reinstall, - upgrade, - user, - cwd, - bin_env, - env_vars, - index_url, - extra_index_url, - pip_list=False, - **kwargs): - ''' +def _check_if_installed( + prefix, + state_pkg_name, + version_spec, + ignore_installed, + force_reinstall, + upgrade, + user, + cwd, + bin_env, + env_vars, + index_url, + extra_index_url, + pip_list=False, + **kwargs +): + """ Takes a package name and version specification (if any) and checks it is installed @@ -274,14 +271,15 @@ def _check_if_installed(prefix, result: None means the command failed to run result: True means the package is installed result: False means the package is not installed - ''' - ret = {'result': False, 'comment': None} + """ + ret = {"result": False, "comment": None} # If we are not passed a pip list, get one: pip_list = salt.utils.data.CaseInsensitiveDict( - pip_list or __salt__['pip.list'](prefix, bin_env=bin_env, - user=user, cwd=cwd, - env_vars=env_vars, **kwargs) + pip_list + or __salt__["pip.list"]( + prefix, bin_env=bin_env, user=user, cwd=cwd, env_vars=env_vars, **kwargs + ) ) # If the package was already installed, check @@ -290,12 +288,13 @@ def _check_if_installed(prefix, if force_reinstall is False and not upgrade: # Check desired version (if any) against currently-installed if ( - any(version_spec) and - _fulfills_version_spec(pip_list[prefix], version_spec) + any(version_spec) + and _fulfills_version_spec(pip_list[prefix], version_spec) ) or (not any(version_spec)): - ret['result'] = True - ret['comment'] = ('Python package {0} was already ' - 'installed'.format(state_pkg_name)) + ret["result"] = True + ret["comment"] = "Python package {0} was already " "installed".format( + state_pkg_name + ) return ret if force_reinstall is False and upgrade: # Check desired version (if any) against currently-installed @@ -304,17 +303,24 @@ def _check_if_installed(prefix, include_rc = False if any(version_spec): for spec in version_spec: - if 'a' in spec[1]: + if "a" in spec[1]: include_alpha = True - if 'b' in spec[1]: + if "b" in spec[1]: include_beta = True - if 'rc' in spec[1]: + if "rc" in spec[1]: include_rc = True - available_versions = __salt__['pip.list_all_versions']( - prefix, bin_env=bin_env, include_alpha=include_alpha, - include_beta=include_beta, include_rc=include_rc, user=user, - cwd=cwd, index_url=index_url, extra_index_url=extra_index_url) - desired_version = '' + available_versions = __salt__["pip.list_all_versions"]( + prefix, + bin_env=bin_env, + include_alpha=include_alpha, + include_beta=include_beta, + include_rc=include_rc, + user=user, + cwd=cwd, + index_url=index_url, + extra_index_url=extra_index_url, + ) + desired_version = "" if any(version_spec): for version in reversed(available_versions): if _fulfills_version_spec(version, version_spec): @@ -323,32 +329,41 @@ def _check_if_installed(prefix, else: desired_version = available_versions[-1] if not desired_version: - ret['result'] = True - ret['comment'] = ('Python package {0} was already ' - 'installed and\nthe available upgrade ' - 'doesn\'t fulfills the version ' - 'requirements'.format(prefix)) + ret["result"] = True + ret["comment"] = ( + "Python package {0} was already " + "installed and\nthe available upgrade " + "doesn't fulfills the version " + "requirements".format(prefix) + ) return ret if _pep440_version_cmp(pip_list[prefix], desired_version) == 0: - ret['result'] = True - ret['comment'] = ('Python package {0} was already ' - 'installed'.format(state_pkg_name)) + ret["result"] = True + ret["comment"] = "Python package {0} was already " "installed".format( + state_pkg_name + ) return ret return ret def _pep440_version_cmp(pkg1, pkg2, ignore_epoch=False): - ''' + """ Compares two version strings using pkg_resources.parse_version. Return -1 if version1 < version2, 0 if version1 ==version2, and 1 if version1 > version2. Return None if there was a problem making the comparison. - ''' + """ if HAS_PKG_RESOURCES is False: - log.warning('The pkg_resources packages was not loaded. Please install setuptools.') + log.warning( + "The pkg_resources packages was not loaded. Please install setuptools." + ) return None - normalize = lambda x: six.text_type(x).split('!', 1)[-1] if ignore_epoch else six.text_type(x) + normalize = ( + lambda x: six.text_type(x).split("!", 1)[-1] + if ignore_epoch + else six.text_type(x) + ) pkg1 = normalize(pkg1) pkg2 = normalize(pkg2) @@ -364,54 +379,56 @@ def _pep440_version_cmp(pkg1, pkg2, ignore_epoch=False): return None -def installed(name, - pkgs=None, - pip_bin=None, - requirements=None, - bin_env=None, - use_wheel=False, - no_use_wheel=False, - log=None, - proxy=None, - timeout=None, - repo=None, - editable=None, - find_links=None, - index_url=None, - extra_index_url=None, - no_index=False, - mirrors=None, - build=None, - target=None, - download=None, - download_cache=None, - source=None, - upgrade=False, - force_reinstall=False, - ignore_installed=False, - exists_action=None, - no_deps=False, - no_install=False, - no_download=False, - install_options=None, - global_options=None, - user=None, - cwd=None, - pre_releases=False, - cert=None, - allow_all_external=False, - allow_external=None, - allow_unverified=None, - process_dependency_links=False, - env_vars=None, - use_vt=False, - trusted_host=None, - no_cache_dir=False, - cache_dir=None, - no_binary=None, - extra_args=None, - **kwargs): - ''' +def installed( + name, + pkgs=None, + pip_bin=None, + requirements=None, + bin_env=None, + use_wheel=False, + no_use_wheel=False, + log=None, + proxy=None, + timeout=None, + repo=None, + editable=None, + find_links=None, + index_url=None, + extra_index_url=None, + no_index=False, + mirrors=None, + build=None, + target=None, + download=None, + download_cache=None, + source=None, + upgrade=False, + force_reinstall=False, + ignore_installed=False, + exists_action=None, + no_deps=False, + no_install=False, + no_download=False, + install_options=None, + global_options=None, + user=None, + cwd=None, + pre_releases=False, + cert=None, + allow_all_external=False, + allow_external=None, + allow_unverified=None, + process_dependency_links=False, + env_vars=None, + use_vt=False, + trusted_host=None, + no_cache_dir=False, + cache_dir=None, + no_binary=None, + extra_args=None, + **kwargs +): + """ Make sure the package is installed name @@ -690,17 +707,19 @@ def installed(name, .. _`virtualenv`: http://www.virtualenv.org/en/latest/ - ''' + """ if pip_bin and not bin_env: bin_env = pip_bin # If pkgs is present, ignore name if pkgs: if not isinstance(pkgs, list): - return {'name': name, - 'result': False, - 'changes': {}, - 'comment': 'pkgs argument must be formatted as a list'} + return { + "name": name, + "result": False, + "changes": {}, + "comment": "pkgs argument must be formatted as a list", + } else: pkgs = [name] @@ -708,68 +727,88 @@ def installed(name, # prepro = lambda pkg: pkg if type(pkg) == str else \ # ' '.join((pkg.items()[0][0], pkg.items()[0][1].replace(',', ';'))) # pkgs = ','.join([prepro(pkg) for pkg in pkgs]) - prepro = lambda pkg: pkg if isinstance(pkg, six.string_types) else \ - ' '.join((six.iteritems(pkg)[0][0], six.iteritems(pkg)[0][1])) + prepro = ( + lambda pkg: pkg + if isinstance(pkg, six.string_types) + else " ".join((six.iteritems(pkg)[0][0], six.iteritems(pkg)[0][1])) + ) pkgs = [prepro(pkg) for pkg in pkgs] - ret = {'name': ';'.join(pkgs), 'result': None, - 'comment': '', 'changes': {}} + ret = {"name": ";".join(pkgs), "result": None, "comment": "", "changes": {}} try: - cur_version = __salt__['pip.version'](bin_env) + cur_version = __salt__["pip.version"](bin_env) except (CommandNotFoundError, CommandExecutionError) as err: - ret['result'] = None - ret['comment'] = 'Error installing \'{0}\': {1}'.format(name, err) + ret["result"] = None + ret["comment"] = "Error installing '{0}': {1}".format(name, err) return ret # Check that the pip binary supports the 'use_wheel' option if use_wheel: - min_version = '1.4' - max_version = '9.0.3' - too_low = salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version) - too_high = salt.utils.versions.compare(ver1=cur_version, oper='>', ver2=max_version) + min_version = "1.4" + max_version = "9.0.3" + too_low = salt.utils.versions.compare( + ver1=cur_version, oper="<", ver2=min_version + ) + too_high = salt.utils.versions.compare( + ver1=cur_version, oper=">", ver2=max_version + ) if too_low or too_high: - ret['result'] = False - ret['comment'] = ('The \'use_wheel\' option is only supported in ' - 'pip between {0} and {1}. The version of pip detected ' - 'was {2}.').format(min_version, max_version, cur_version) + ret["result"] = False + ret["comment"] = ( + "The 'use_wheel' option is only supported in " + "pip between {0} and {1}. The version of pip detected " + "was {2}." + ).format(min_version, max_version, cur_version) return ret # Check that the pip binary supports the 'no_use_wheel' option if no_use_wheel: - min_version = '1.4' - max_version = '9.0.3' - too_low = salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version) - too_high = salt.utils.versions.compare(ver1=cur_version, oper='>', ver2=max_version) + min_version = "1.4" + max_version = "9.0.3" + too_low = salt.utils.versions.compare( + ver1=cur_version, oper="<", ver2=min_version + ) + too_high = salt.utils.versions.compare( + ver1=cur_version, oper=">", ver2=max_version + ) if too_low or too_high: - ret['result'] = False - ret['comment'] = ('The \'no_use_wheel\' option is only supported in ' - 'pip between {0} and {1}. The version of pip detected ' - 'was {2}.').format(min_version, max_version, cur_version) + ret["result"] = False + ret["comment"] = ( + "The 'no_use_wheel' option is only supported in " + "pip between {0} and {1}. The version of pip detected " + "was {2}." + ).format(min_version, max_version, cur_version) return ret # Check that the pip binary supports the 'no_binary' option if no_binary: - min_version = '7.0.0' - too_low = salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version) + min_version = "7.0.0" + too_low = salt.utils.versions.compare( + ver1=cur_version, oper="<", ver2=min_version + ) if too_low: - ret['result'] = False - ret['comment'] = ('The \'no_binary\' option is only supported in ' - 'pip {0} and newer. The version of pip detected ' - 'was {1}.').format(min_version, cur_version) + ret["result"] = False + ret["comment"] = ( + "The 'no_binary' option is only supported in " + "pip {0} and newer. The version of pip detected " + "was {1}." + ).format(min_version, cur_version) return ret # Deprecation warning for the repo option if repo is not None: - msg = ('The \'repo\' argument to pip.installed is deprecated and will ' - 'be removed in Salt {version}. Please use \'name\' instead. ' - 'The current value for name, \'{0}\' will be replaced by the ' - 'value of repo, \'{1}\''.format( - name, - repo, - version=_SaltStackVersion.from_name('Lithium').formatted_version - )) - salt.utils.versions.warn_until('Lithium', msg) - ret.setdefault('warnings', []).append(msg) + msg = ( + "The 'repo' argument to pip.installed is deprecated and will " + "be removed in Salt {version}. Please use 'name' instead. " + "The current value for name, '{0}' will be replaced by the " + "value of repo, '{1}'".format( + name, + repo, + version=_SaltStackVersion.from_name("Lithium").formatted_version, + ) + ) + salt.utils.versions.warn_until("Lithium", msg) + ret.setdefault("warnings", []).append(msg) name = repo # Get the packages parsed name and version from the pip library. @@ -779,14 +818,14 @@ def installed(name, comments = [] for pkg in iter(pkgs): out = _check_pkg_version_format(pkg) - if out['result'] is False: - ret['result'] = False - comments.append(out['comment']) - elif out['result'] is True: - pkgs_details.append((out['prefix'], pkg, out['version_spec'])) + if out["result"] is False: + ret["result"] = False + comments.append(out["comment"]) + elif out["result"] is True: + pkgs_details.append((out["prefix"], pkg, out["version_spec"])) - if ret['result'] is False: - ret['comment'] = '\n'.join(comments) + if ret["result"] is False: + ret["comment"] = "\n".join(comments) return ret # If a requirements file is specified, only install the contents of the @@ -797,19 +836,20 @@ def installed(name, if requirements or editable: comments = [] # Append comments if this is a dry run. - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if requirements: # TODO: Check requirements file against currently-installed # packages to provide more accurate state output. - comments.append('Requirements file \'{0}\' will be ' - 'processed.'.format(requirements)) + comments.append( + "Requirements file '{0}' will be " "processed.".format(requirements) + ) if editable: comments.append( - 'Package will be installed in editable mode (i.e. ' + "Package will be installed in editable mode (i.e. " 'setuptools "develop mode") from {0}.'.format(editable) ) - ret['comment'] = ' '.join(comments) + ret["comment"] = " ".join(comments) return ret # No requirements case. @@ -817,7 +857,7 @@ def installed(name, else: # Attempt to pre-cache a the current pip list try: - pip_list = __salt__['pip.list'](bin_env=bin_env, user=user, cwd=cwd) + pip_list = __salt__["pip.list"](bin_env=bin_env, user=user, cwd=cwd) # If we fail, then just send False, and we'll try again in the next function call except Exception as exc: # pylint: disable=broad-except log.exception(exc) @@ -826,61 +866,74 @@ def installed(name, for prefix, state_pkg_name, version_spec in pkgs_details: if prefix: - out = _check_if_installed(prefix, state_pkg_name, version_spec, - ignore_installed, force_reinstall, - upgrade, user, cwd, bin_env, env_vars, - index_url, extra_index_url, pip_list, - **kwargs) + out = _check_if_installed( + prefix, + state_pkg_name, + version_spec, + ignore_installed, + force_reinstall, + upgrade, + user, + cwd, + bin_env, + env_vars, + index_url, + extra_index_url, + pip_list, + **kwargs + ) # If _check_if_installed result is None, something went wrong with # the command running. This way we keep stateful output. - if out['result'] is None: - ret['result'] = False - ret['comment'] = out['comment'] + if out["result"] is None: + ret["result"] = False + ret["comment"] = out["comment"] return ret else: - out = {'result': False, 'comment': None} + out = {"result": False, "comment": None} - result = out['result'] + result = out["result"] # The package is not present. Add it to the pkgs to install. if result is False: # Replace commas (used for version ranges) with semicolons # (which are not supported) in name so it does not treat # them as multiple packages. - target_pkgs.append((prefix, state_pkg_name.replace(',', ';'))) + target_pkgs.append((prefix, state_pkg_name.replace(",", ";"))) # Append comments if this is a dry run. - if __opts__['test']: - msg = 'Python package {0} is set to be installed' - ret['result'] = None - ret['comment'] = msg.format(state_pkg_name) + if __opts__["test"]: + msg = "Python package {0} is set to be installed" + ret["result"] = None + ret["comment"] = msg.format(state_pkg_name) return ret # The package is already present and will not be reinstalled. elif result is True: # Append comment stating its presence - already_installed_comments.append(out['comment']) + already_installed_comments.append(out["comment"]) # The command pip.list failed. Abort. elif result is None: - ret['result'] = None - ret['comment'] = out['comment'] + ret["result"] = None + ret["comment"] = out["comment"] return ret # No packages to install. if not target_pkgs: - ret['result'] = True - aicomms = '\n'.join(already_installed_comments) - last_line = 'All specified packages are already installed' + (' and up-to-date' if upgrade else '') - ret['comment'] = aicomms + ('\n' if aicomms else '') + last_line + ret["result"] = True + aicomms = "\n".join(already_installed_comments) + last_line = "All specified packages are already installed" + ( + " and up-to-date" if upgrade else "" + ) + ret["comment"] = aicomms + ("\n" if aicomms else "") + last_line return ret # Construct the string that will get passed to the install call - pkgs_str = ','.join([state_name for _, state_name in target_pkgs]) + pkgs_str = ",".join([state_name for _, state_name in target_pkgs]) # Call to install the package. Actual installation takes place here - pip_install_call = __salt__['pip.install']( - pkgs='{0}'.format(pkgs_str) if pkgs_str else '', + pip_install_call = __salt__["pip.install"]( + pkgs="{0}".format(pkgs_str) if pkgs_str else "", requirements=requirements, bin_env=bin_env, use_wheel=use_wheel, @@ -927,39 +980,41 @@ def installed(name, **kwargs ) - if pip_install_call and pip_install_call.get('retcode', 1) == 0: - ret['result'] = True + if pip_install_call and pip_install_call.get("retcode", 1) == 0: + ret["result"] = True if requirements or editable: comments = [] if requirements: PIP_REQUIREMENTS_NOCHANGE = [ - 'Requirement already satisfied', - 'Requirement already up-to-date', - 'Requirement not upgraded', - 'Collecting', - 'Cloning', - 'Cleaning up...', + "Requirement already satisfied", + "Requirement already up-to-date", + "Requirement not upgraded", + "Collecting", + "Cloning", + "Cleaning up...", + "Looking in indexes", ] - for line in pip_install_call.get('stdout', '').split('\n'): + for line in pip_install_call.get("stdout", "").split("\n"): if not any( - [ - line.strip().startswith(x) - for x in PIP_REQUIREMENTS_NOCHANGE - ] + [line.strip().startswith(x) for x in PIP_REQUIREMENTS_NOCHANGE] ): - ret['changes']['requirements'] = True - if ret['changes'].get('requirements'): - comments.append('Successfully processed requirements file ' - '{0}.'.format(requirements)) + ret["changes"]["requirements"] = True + if ret["changes"].get("requirements"): + comments.append( + "Successfully processed requirements file " + "{0}.".format(requirements) + ) else: - comments.append('Requirements were already installed.') + comments.append("Requirements were already installed.") if editable: - comments.append('Package successfully installed from VCS ' - 'checkout {0}.'.format(editable)) - ret['changes']['editable'] = True - ret['comment'] = ' '.join(comments) + comments.append( + "Package successfully installed from VCS " + "checkout {0}.".format(editable) + ) + ret["changes"]["editable"] = True + ret["comment"] = " ".join(comments) else: # Check that the packages set to be installed were installed. @@ -967,11 +1022,11 @@ def installed(name, pkg_404_comms = [] already_installed_packages = set() - for line in pip_install_call.get('stdout', '').split('\n'): + for line in pip_install_call.get("stdout", "").split("\n"): # Output for already installed packages: # 'Requirement already up-to-date: jinja2 in /usr/local/lib/python2.7/dist-packages\nCleaning up...' - if line.startswith('Requirement already up-to-date: '): - package = line.split(':', 1)[1].split()[0] + if line.startswith("Requirement already up-to-date: "): + package = line.split(":", 1)[1].split()[0] already_installed_packages.add(package.lower()) for prefix, state_name in target_pkgs: @@ -979,78 +1034,93 @@ def installed(name, # Case for packages that are not an URL if prefix: pipsearch = salt.utils.data.CaseInsensitiveDict( - __salt__['pip.list'](prefix, bin_env, - user=user, cwd=cwd, - env_vars=env_vars, - **kwargs) + __salt__["pip.list"]( + prefix, + bin_env, + user=user, + cwd=cwd, + env_vars=env_vars, + **kwargs + ) ) # If we didn't find the package in the system after # installing it report it if not pipsearch: pkg_404_comms.append( - 'There was no error installing package \'{0}\' ' - 'although it does not show when calling ' - '\'pip.freeze\'.'.format(pkg) + "There was no error installing package '{0}' " + "although it does not show when calling " + "'pip.freeze'.".format(pkg) ) else: - if prefix in pipsearch \ - and prefix.lower() not in already_installed_packages: + if ( + prefix in pipsearch + and prefix.lower() not in already_installed_packages + ): ver = pipsearch[prefix] - ret['changes']['{0}=={1}'.format(prefix, ver)] = 'Installed' + ret["changes"]["{0}=={1}".format(prefix, ver)] = "Installed" # Case for packages that are an URL else: - ret['changes']['{0}==???'.format(state_name)] = 'Installed' + ret["changes"]["{0}==???".format(state_name)] = "Installed" # Set comments - aicomms = '\n'.join(already_installed_comments) - succ_comm = 'All packages were successfully installed'\ - if not pkg_404_comms else '\n'.join(pkg_404_comms) - ret['comment'] = aicomms + ('\n' if aicomms else '') + succ_comm + aicomms = "\n".join(already_installed_comments) + succ_comm = ( + "All packages were successfully installed" + if not pkg_404_comms + else "\n".join(pkg_404_comms) + ) + ret["comment"] = aicomms + ("\n" if aicomms else "") + succ_comm return ret elif pip_install_call: - ret['result'] = False - if 'stdout' in pip_install_call: - error = 'Error: {0} {1}'.format(pip_install_call['stdout'], - pip_install_call['stderr']) + ret["result"] = False + if "stdout" in pip_install_call: + error = "Error: {0} {1}".format( + pip_install_call["stdout"], pip_install_call["stderr"] + ) else: - error = 'Error: {0}'.format(pip_install_call['comment']) + error = "Error: {0}".format(pip_install_call["comment"]) if requirements or editable: comments = [] if requirements: - comments.append('Unable to process requirements file ' - '"{0}".'.format(requirements)) + comments.append( + "Unable to process requirements file " '"{0}".'.format(requirements) + ) if editable: - comments.append('Unable to install from VCS checkout' - '{0}.'.format(editable)) + comments.append( + "Unable to install from VCS checkout" "{0}.".format(editable) + ) comments.append(error) - ret['comment'] = ' '.join(comments) + ret["comment"] = " ".join(comments) else: - pkgs_str = ', '.join([state_name for _, state_name in target_pkgs]) - aicomms = '\n'.join(already_installed_comments) - error_comm = ('Failed to install packages: {0}. ' - '{1}'.format(pkgs_str, error)) - ret['comment'] = aicomms + ('\n' if aicomms else '') + error_comm + pkgs_str = ", ".join([state_name for _, state_name in target_pkgs]) + aicomms = "\n".join(already_installed_comments) + error_comm = "Failed to install packages: {0}. " "{1}".format( + pkgs_str, error + ) + ret["comment"] = aicomms + ("\n" if aicomms else "") + error_comm else: - ret['result'] = False - ret['comment'] = 'Could not install package' + ret["result"] = False + ret["comment"] = "Could not install package" return ret -def removed(name, - requirements=None, - bin_env=None, - log=None, - proxy=None, - timeout=None, - user=None, - cwd=None, - use_vt=False): - ''' +def removed( + name, + requirements=None, + bin_env=None, + log=None, + proxy=None, + timeout=None, + user=None, + cwd=None, + use_vt=False, +): + """ Make sure that a package is not installed. name @@ -1061,50 +1131,48 @@ def removed(name, the pip executable or virtualenenv to use use_vt Use VT terminal emulation (see output while installing) - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} try: - pip_list = __salt__['pip.list'](bin_env=bin_env, user=user, cwd=cwd) + pip_list = __salt__["pip.list"](bin_env=bin_env, user=user, cwd=cwd) except (CommandExecutionError, CommandNotFoundError) as err: - ret['result'] = False - ret['comment'] = 'Error uninstalling \'{0}\': {1}'.format(name, err) + ret["result"] = False + ret["comment"] = "Error uninstalling '{0}': {1}".format(name, err) return ret if name not in pip_list: - ret['result'] = True - ret['comment'] = 'Package is not installed.' + ret["result"] = True + ret["comment"] = "Package is not installed." return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Package {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Package {0} is set to be removed".format(name) return ret - if __salt__['pip.uninstall'](pkgs=name, - requirements=requirements, - bin_env=bin_env, - log=log, - proxy=proxy, - timeout=timeout, - user=user, - cwd=cwd, - use_vt=use_vt): - ret['result'] = True - ret['changes'][name] = 'Removed' - ret['comment'] = 'Package was successfully removed.' + if __salt__["pip.uninstall"]( + pkgs=name, + requirements=requirements, + bin_env=bin_env, + log=log, + proxy=proxy, + timeout=timeout, + user=user, + cwd=cwd, + use_vt=use_vt, + ): + ret["result"] = True + ret["changes"][name] = "Removed" + ret["comment"] = "Package was successfully removed." else: - ret['result'] = False - ret['comment'] = 'Could not remove package.' + ret["result"] = False + ret["comment"] = "Could not remove package." return ret -def uptodate(name, - bin_env=None, - user=None, - cwd=None, - use_vt=False): - ''' +def uptodate(name, bin_env=None, user=None, cwd=None, use_vt=False): + """ .. versionadded:: 2015.5.0 Verify that the system is completely up to date. @@ -1118,36 +1186,35 @@ def uptodate(name, the pip executable or virtualenenv to use use_vt Use VT terminal emulation (see output while installing) - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Failed to update.'} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": "Failed to update."} try: - packages = __salt__['pip.list_upgrades'](bin_env=bin_env, user=user, cwd=cwd) + packages = __salt__["pip.list_upgrades"](bin_env=bin_env, user=user, cwd=cwd) except Exception as e: # pylint: disable=broad-except - ret['comment'] = six.text_type(e) + ret["comment"] = six.text_type(e) return ret if not packages: - ret['comment'] = 'System is already up-to-date.' - ret['result'] = True + ret["comment"] = "System is already up-to-date." + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'System update will be performed' - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "System update will be performed" + ret["result"] = None return ret - updated = __salt__['pip.upgrade'](bin_env=bin_env, user=user, cwd=cwd, use_vt=use_vt) + updated = __salt__["pip.upgrade"]( + bin_env=bin_env, user=user, cwd=cwd, use_vt=use_vt + ) - if updated.get('result') is False: + if updated.get("result") is False: ret.update(updated) elif updated: - ret['changes'] = updated - ret['comment'] = 'Upgrade successful.' - ret['result'] = True + ret["changes"] = updated + ret["comment"] = "Upgrade successful." + ret["result"] = True else: - ret['comment'] = 'Upgrade failed.' + ret["comment"] = "Upgrade failed." return ret diff --git a/salt/states/pkg.py b/salt/states/pkg.py index a13d4184002..b6d0877c9cc 100644 --- a/salt/states/pkg.py +++ b/salt/states/pkg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of packages using OS package managers such as yum or apt-get ========================================================================= @@ -69,10 +69,11 @@ state module package manager which is not case-sensitive (such as :mod:`pkgng <salt.modules.pkgng>`), then this state will fail if the proper case is not used. This will be addressed in a future release of Salt. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import fnmatch import logging import os @@ -82,16 +83,14 @@ import re import salt.utils.pkg import salt.utils.platform import salt.utils.versions -from salt.output import nested -from salt.utils.functools import namespaced_function as _namespaced_function -from salt.utils.odict import OrderedDict as _OrderedDict -from salt.exceptions import ( - CommandExecutionError, MinionError, SaltInvocationError -) -from salt.modules.pkg_resource import _repack_pkgs +from salt.exceptions import CommandExecutionError, MinionError, SaltInvocationError # Import 3rd-party libs from salt.ext import six +from salt.modules.pkg_resource import _repack_pkgs +from salt.output import nested +from salt.utils.functools import namespaced_function as _namespaced_function +from salt.utils.odict import OrderedDict as _OrderedDict # pylint: disable=invalid-name _repack_pkgs = _namespaced_function(_repack_pkgs, globals()) @@ -105,6 +104,7 @@ if salt.utils.platform.is_windows(): import errno import time from functools import cmp_to_key + # pylint: disable=import-error # pylint: enable=unused-import from salt.modules.win_pkg import _get_package_info @@ -116,25 +116,24 @@ if salt.utils.platform.is_windows(): from salt.modules.win_pkg import _repo_process_pkg_sls from salt.modules.win_pkg import _get_latest_pkg_version from salt.modules.win_pkg import _reverse_cmp_pkg_versions + _get_package_info = _namespaced_function(_get_package_info, globals()) get_repo_data = _namespaced_function(get_repo_data, globals()) - _get_repo_details = \ - _namespaced_function(_get_repo_details, globals()) - _refresh_db_conditional = \ - _namespaced_function(_refresh_db_conditional, globals()) + _get_repo_details = _namespaced_function(_get_repo_details, globals()) + _refresh_db_conditional = _namespaced_function(_refresh_db_conditional, globals()) refresh_db = _namespaced_function(refresh_db, globals()) genrepo = _namespaced_function(genrepo, globals()) - _repo_process_pkg_sls = \ - _namespaced_function(_repo_process_pkg_sls, globals()) - _get_latest_pkg_version = \ - _namespaced_function(_get_latest_pkg_version, globals()) - _reverse_cmp_pkg_versions = \ - _namespaced_function(_reverse_cmp_pkg_versions, globals()) + _repo_process_pkg_sls = _namespaced_function(_repo_process_pkg_sls, globals()) + _get_latest_pkg_version = _namespaced_function(_get_latest_pkg_version, globals()) + _reverse_cmp_pkg_versions = _namespaced_function( + _reverse_cmp_pkg_versions, globals() + ) # The following imports are used by the namespaced win_pkg funcs # and need to be included in their globals. # pylint: disable=import-error,unused-import import salt.utils.msgpack as msgpack from salt.utils.versions import LooseVersion + # pylint: enable=import-error,unused-import # pylint: enable=invalid-name @@ -142,41 +141,48 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only make these states available if a pkg provider has been detected or assigned for this minion - ''' - return 'pkg.install' in __salt__ + """ + if "pkg.install" in __salt__: + return True + return (False, "pkg module could not be loaded") def _get_comparison_spec(pkgver): - ''' + """ Return a tuple containing the comparison operator and the version. If no comparison operator was passed, the comparison is assumed to be an "equals" comparison, and "==" will be the operator returned. - ''' + """ oper, verstr = salt.utils.pkg.split_comparison(pkgver.strip()) - if oper in ('=', ''): - oper = '==' + if oper in ("=", ""): + oper = "==" return oper, verstr def _parse_version_string(version_conditions_string): - ''' + """ Returns a list of two-tuples containing (operator, version). - ''' + """ result = [] version_conditions_string = version_conditions_string.strip() if not version_conditions_string: return result - for version_condition in version_conditions_string.split(','): + for version_condition in version_conditions_string.split(","): operator_and_version = _get_comparison_spec(version_condition) result.append(operator_and_version) return result -def _fulfills_version_string(installed_versions, version_conditions_string, ignore_epoch=False, allow_updates=False): - ''' +def _fulfills_version_string( + installed_versions, + version_conditions_string, + ignore_epoch=False, + allow_updates=False, +): + """ Returns True if any of the installed versions match the specified version conditions, otherwise returns False. @@ -201,80 +207,87 @@ def _fulfills_version_string(installed_versions, version_conditions_string, igno This means a package on the Minion can have a newer version than the latest available in the repository without enforcing a re-installation of the package. (Only applicable if only one strict version condition is specified E.G. version: 2.0.6~ubuntu3) - ''' + """ version_conditions = _parse_version_string(version_conditions_string) for installed_version in installed_versions: fullfills_all = True for operator, version_string in version_conditions: - if allow_updates and len(version_conditions) == 1 and operator == '==': - operator = '>=' - fullfills_all = fullfills_all and _fulfills_version_spec([installed_version], operator, version_string, ignore_epoch=ignore_epoch) + if allow_updates and len(version_conditions) == 1 and operator == "==": + operator = ">=" + fullfills_all = fullfills_all and _fulfills_version_spec( + [installed_version], operator, version_string, ignore_epoch=ignore_epoch + ) if fullfills_all: return True return False -def _fulfills_version_spec(versions, oper, desired_version, - ignore_epoch=False): - ''' +def _fulfills_version_spec(versions, oper, desired_version, ignore_epoch=False): + """ Returns True if any of the installed versions match the specified version, otherwise returns False - ''' - cmp_func = __salt__.get('pkg.version_cmp') + """ + cmp_func = __salt__.get("pkg.version_cmp") # stripping "with_origin" dict wrapper if salt.utils.platform.is_freebsd(): - if isinstance(versions, dict) and 'version' in versions: - versions = versions['version'] + if isinstance(versions, dict) and "version" in versions: + versions = versions["version"] for ver in versions: - if (oper == '==' and fnmatch.fnmatch(ver, desired_version)) \ - or salt.utils.versions.compare(ver1=ver, - oper=oper, - ver2=desired_version, - cmp_func=cmp_func, - ignore_epoch=ignore_epoch): + if ( + oper == "==" and fnmatch.fnmatch(ver, desired_version) + ) or salt.utils.versions.compare( + ver1=ver, + oper=oper, + ver2=desired_version, + cmp_func=cmp_func, + ignore_epoch=ignore_epoch, + ): return True return False def _find_unpurge_targets(desired): - ''' + """ Find packages which are marked to be purged but can't yet be removed because they are dependencies for other installed packages. These are the packages which will need to be 'unpurged' because they are part of pkg.installed states. This really just applies to Debian-based Linuxes. - ''' - return [ - x for x in desired - if x in __salt__['pkg.list_pkgs'](purge_desired=True) - ] + """ + return [x for x in desired if x in __salt__["pkg.list_pkgs"](purge_desired=True)] -def _find_download_targets(name=None, - version=None, - pkgs=None, - normalize=True, - skip_suggestions=False, - ignore_epoch=False, - **kwargs): - ''' +def _find_download_targets( + name=None, + version=None, + pkgs=None, + normalize=True, + skip_suggestions=False, + ignore_epoch=False, + **kwargs +): + """ Inspect the arguments to pkg.downloaded and discover what packages need to be downloaded. Return a dict of packages to download. - ''' - cur_pkgs = __salt__['pkg.list_downloaded']() + """ + cur_pkgs = __salt__["pkg.list_downloaded"]() if pkgs: - to_download = _repack_pkgs(pkgs, normalize=normalize) # pylint: disable=not-callable + # pylint: disable=not-callable + to_download = _repack_pkgs(pkgs, normalize=normalize) + # pylint: enable=not-callable if not to_download: # Badly-formatted SLS - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Invalidly formatted pkgs parameter. See ' - 'minion log.'} + return { + "name": name, + "changes": {}, + "result": False, + "comment": "Invalidly formatted pkgs parameter. See " "minion log.", + } else: if normalize: - _normalize_name = \ - __salt__.get('pkg.normalize_name', lambda pkgname: pkgname) + _normalize_name = __salt__.get( + "pkg.normalize_name", lambda pkgname: pkgname + ) to_download = {_normalize_name(name): version} else: to_download = {name: version} @@ -283,20 +296,23 @@ def _find_download_targets(name=None, if name in to_download: # Package already downloaded, no need to download again if cver and version in cver: - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Version {0} of package \'{1}\' is already ' - 'downloaded'.format(version, name)} + return { + "name": name, + "changes": {}, + "result": True, + "comment": "Version {0} of package '{1}' is already " + "downloaded".format(version, name), + } # if cver is not an empty string, the package is already downloaded elif cver and version is None: # The package is downloaded - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Package {0} is already ' - 'downloaded'.format(name)} + return { + "name": name, + "changes": {}, + "result": True, + "comment": "Package {0} is already " "downloaded".format(name), + } version_spec = False if not skip_suggestions: @@ -306,28 +322,27 @@ def _find_download_targets(name=None, pass else: comments = [] - if problems.get('no_suggest'): + if problems.get("no_suggest"): comments.append( - 'The following package(s) were not found, and no ' - 'possible matches were found in the package db: ' - '{0}'.format( - ', '.join(sorted(problems['no_suggest'])) - ) + "The following package(s) were not found, and no " + "possible matches were found in the package db: " + "{0}".format(", ".join(sorted(problems["no_suggest"]))) ) - if problems.get('suggest'): - for pkgname, suggestions in \ - six.iteritems(problems['suggest']): + if problems.get("suggest"): + for pkgname, suggestions in six.iteritems(problems["suggest"]): comments.append( - 'Package \'{0}\' not found (possible matches: ' - '{1})'.format(pkgname, ', '.join(suggestions)) + "Package '{0}' not found (possible matches: " + "{1})".format(pkgname, ", ".join(suggestions)) ) if comments: if len(comments) > 1: - comments.append('') - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': '. '.join(comments).rstrip()} + comments.append("") + return { + "name": name, + "changes": {}, + "result": False, + "comment": ". ".join(comments).rstrip(), + } # Find out which packages will be targeted in the call to pkg.download # Check current downloaded versions against specified versions @@ -345,51 +360,50 @@ def _find_download_targets(name=None, version_spec = True try: - if not _fulfills_version_string(cver.keys(), pkgver, ignore_epoch=ignore_epoch): + if not _fulfills_version_string( + cver.keys(), pkgver, ignore_epoch=ignore_epoch + ): targets[pkgname] = pkgver except CommandExecutionError as exc: problems.append(exc.strerror) continue if problems: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': ' '.join(problems)} + return { + "name": name, + "changes": {}, + "result": False, + "comment": " ".join(problems), + } if not targets: # All specified packages are already downloaded - msg = ( - 'All specified packages{0} are already downloaded' - .format(' (matching specified versions)' if version_spec else '') + msg = "All specified packages{0} are already downloaded".format( + " (matching specified versions)" if version_spec else "" ) - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': msg} + return {"name": name, "changes": {}, "result": True, "comment": msg} return targets -def _find_advisory_targets(name=None, - advisory_ids=None, - **kwargs): - ''' +def _find_advisory_targets(name=None, advisory_ids=None, **kwargs): + """ Inspect the arguments to pkg.patch_installed and discover what advisory patches need to be installed. Return a dict of advisory patches to install. - ''' - cur_patches = __salt__['pkg.list_installed_patches']() + """ + cur_patches = __salt__["pkg.list_installed_patches"]() if advisory_ids: to_download = advisory_ids else: to_download = [name] if cur_patches.get(name, {}): # Advisory patch already installed, no need to install it again - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Advisory patch {0} is already ' - 'installed'.format(name)} + return { + "name": name, + "changes": {}, + "result": True, + "comment": "Advisory patch {0} is already " "installed".format(name), + } # Find out which advisory patches will be targeted in the call to pkg.install targets = [] @@ -402,41 +416,37 @@ def _find_advisory_targets(name=None, if not targets: # All specified packages are already downloaded - msg = ('All specified advisory patches are already installed') - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': msg} + msg = "All specified advisory patches are already installed" + return {"name": name, "changes": {}, "result": True, "comment": msg} return targets -def _find_remove_targets(name=None, - version=None, - pkgs=None, - normalize=True, - ignore_epoch=False, - **kwargs): - ''' +def _find_remove_targets( + name=None, version=None, pkgs=None, normalize=True, ignore_epoch=False, **kwargs +): + """ Inspect the arguments to pkg.removed and discover what packages need to be removed. Return a dict of packages to remove. - ''' - if __grains__['os'] == 'FreeBSD': - kwargs['with_origin'] = True - cur_pkgs = __salt__['pkg.list_pkgs'](versions_as_list=True, **kwargs) + """ + if __grains__["os"] == "FreeBSD": + kwargs["with_origin"] = True + cur_pkgs = __salt__["pkg.list_pkgs"](versions_as_list=True, **kwargs) if pkgs: - to_remove = _repack_pkgs(pkgs, normalize=normalize) # pylint: disable=not-callable + # pylint: disable=not-callable + to_remove = _repack_pkgs(pkgs, normalize=normalize) + # pylint: enable=not-callable if not to_remove: # Badly-formatted SLS - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Invalidly formatted pkgs parameter. See ' - 'minion log.'} + return { + "name": name, + "changes": {}, + "result": False, + "comment": "Invalidly formatted pkgs parameter. See " "minion log.", + } else: - _normalize_name = \ - __salt__.get('pkg.normalize_name', lambda pkgname: pkgname) + _normalize_name = __salt__.get("pkg.normalize_name", lambda pkgname: pkgname) to_remove = {_normalize_name(name): version} version_spec = False @@ -446,10 +456,10 @@ def _find_remove_targets(name=None, problems = [] for pkgname, pkgver in six.iteritems(to_remove): # FreeBSD pkg supports `openjdk` and `java/openjdk7` package names - origin = bool(re.search('/', pkgname)) + origin = bool(re.search("/", pkgname)) - if __grains__['os'] == 'FreeBSD' and origin: - cver = [k for k, v in six.iteritems(cur_pkgs) if v['origin'] == pkgname] + if __grains__["os"] == "FreeBSD" and origin: + cver = [k for k, v in six.iteritems(cur_pkgs) if v["origin"] == pkgname] else: cver = cur_pkgs.get(pkgname, []) @@ -457,7 +467,7 @@ def _find_remove_targets(name=None, if not cver: continue # No version specified and pkg is installed - elif __salt__['pkg_resource.version_clean'](pkgver) is None: + elif __salt__["pkg_resource.version_clean"](pkgver) is None: targets.append(pkgname) continue version_spec = True @@ -466,138 +476,153 @@ def _find_remove_targets(name=None, targets.append(pkgname) else: log.debug( - 'Current version (%s) did not match desired version ' - 'specification (%s), will not remove', cver, pkgver + "Current version (%s) did not match desired version " + "specification (%s), will not remove", + cver, + pkgver, ) except CommandExecutionError as exc: problems.append(exc.strerror) continue if problems: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': ' '.join(problems)} + return { + "name": name, + "changes": {}, + "result": False, + "comment": " ".join(problems), + } if not targets: # All specified packages are already absent - msg = 'All specified packages{0} are already absent'.format( - ' (matching specified versions)' if version_spec else '' + msg = "All specified packages{0} are already absent".format( + " (matching specified versions)" if version_spec else "" ) - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': msg} + return {"name": name, "changes": {}, "result": True, "comment": msg} return targets -def _find_install_targets(name=None, - version=None, - pkgs=None, - sources=None, - skip_suggestions=False, - pkg_verify=False, - normalize=True, - ignore_epoch=False, - reinstall=False, - refresh=False, - **kwargs): - ''' +def _find_install_targets( + name=None, + version=None, + pkgs=None, + sources=None, + skip_suggestions=False, + pkg_verify=False, + normalize=True, + ignore_epoch=False, + reinstall=False, + refresh=False, + **kwargs +): + """ Inspect the arguments to pkg.installed and discover what packages need to be installed. Return a dict of desired packages - ''' + """ was_refreshed = False if all((pkgs, sources)): - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Only one of "pkgs" and "sources" is permitted.'} + return { + "name": name, + "changes": {}, + "result": False, + "comment": 'Only one of "pkgs" and "sources" is permitted.', + } # dict for packages that fail pkg.verify and their altered files altered_files = {} # Get the ignore_types list if any from the pkg_verify argument - if isinstance(pkg_verify, list) \ - and any(x.get('ignore_types') is not None - for x in pkg_verify - if isinstance(x, _OrderedDict) - and 'ignore_types' in x): - ignore_types = next(x.get('ignore_types') - for x in pkg_verify - if 'ignore_types' in x) + if isinstance(pkg_verify, list) and any( + x.get("ignore_types") is not None + for x in pkg_verify + if isinstance(x, _OrderedDict) and "ignore_types" in x + ): + ignore_types = next( + x.get("ignore_types") for x in pkg_verify if "ignore_types" in x + ) else: ignore_types = [] # Get the verify_options list if any from the pkg_verify argument - if isinstance(pkg_verify, list) \ - and any(x.get('verify_options') is not None - for x in pkg_verify - if isinstance(x, _OrderedDict) - and 'verify_options' in x): - verify_options = next(x.get('verify_options') - for x in pkg_verify - if 'verify_options' in x) + if isinstance(pkg_verify, list) and any( + x.get("verify_options") is not None + for x in pkg_verify + if isinstance(x, _OrderedDict) and "verify_options" in x + ): + verify_options = next( + x.get("verify_options") for x in pkg_verify if "verify_options" in x + ) else: verify_options = [] - if __grains__['os'] == 'FreeBSD': - kwargs['with_origin'] = True + if __grains__["os"] == "FreeBSD": + kwargs["with_origin"] = True if salt.utils.platform.is_windows(): # Windows requires a refresh to establish a pkg db if refresh=True, so # add it to the kwargs. - kwargs['refresh'] = refresh + kwargs["refresh"] = refresh - resolve_capabilities = kwargs.get('resolve_capabilities', False) and 'pkg.list_provides' in __salt__ + resolve_capabilities = ( + kwargs.get("resolve_capabilities", False) and "pkg.list_provides" in __salt__ + ) try: - cur_pkgs = __salt__['pkg.list_pkgs'](versions_as_list=True, **kwargs) - cur_prov = resolve_capabilities and __salt__['pkg.list_provides'](**kwargs) or dict() + cur_pkgs = __salt__["pkg.list_pkgs"](versions_as_list=True, **kwargs) + cur_prov = ( + resolve_capabilities and __salt__["pkg.list_provides"](**kwargs) or dict() + ) except CommandExecutionError as exc: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': exc.strerror} + return {"name": name, "changes": {}, "result": False, "comment": exc.strerror} - if salt.utils.platform.is_windows() and kwargs.pop('refresh', False): + if salt.utils.platform.is_windows() and kwargs.pop("refresh", False): # We already refreshed when we called pkg.list_pkgs was_refreshed = True refresh = False if any((pkgs, sources)): if pkgs: - desired = _repack_pkgs(pkgs, normalize=normalize) # pylint: disable=not-callable + # pylint: disable=not-callable + desired = _repack_pkgs(pkgs, normalize=normalize) + # pylint: enable=not-callable elif sources: - desired = __salt__['pkg_resource.pack_sources']( - sources, - normalize=normalize, + desired = __salt__["pkg_resource.pack_sources"]( + sources, normalize=normalize, ) if not desired: # Badly-formatted SLS - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Invalidly formatted \'{0}\' parameter. See ' - 'minion log.'.format('pkgs' if pkgs - else 'sources')} + return { + "name": name, + "changes": {}, + "result": False, + "comment": "Invalidly formatted '{0}' parameter. See " + "minion log.".format("pkgs" if pkgs else "sources"), + } to_unpurge = _find_unpurge_targets(desired) else: if salt.utils.platform.is_windows(): - pkginfo = _get_package_info(name, saltenv=kwargs['saltenv']) # pylint: disable=not-callable + # pylint: disable=not-callable + pkginfo = _get_package_info(name, saltenv=kwargs["saltenv"]) + # pylint: enable=not-callable if not pkginfo: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Package {0} not found in the ' - 'repository.'.format(name)} + return { + "name": name, + "changes": {}, + "result": False, + "comment": "Package {0} not found in the " + "repository.".format(name), + } if version is None: - version = _get_latest_pkg_version(pkginfo) # pylint: disable=not-callable + # pylint: disable=not-callable + version = _get_latest_pkg_version(pkginfo) + # pylint: enable=not-callable if normalize: - _normalize_name = \ - __salt__.get('pkg.normalize_name', lambda pkgname: pkgname) + _normalize_name = __salt__.get( + "pkg.normalize_name", lambda pkgname: pkgname + ) desired = {_normalize_name(name): version} else: desired = {name: version} @@ -605,35 +630,33 @@ def _find_install_targets(name=None, to_unpurge = _find_unpurge_targets(desired) # FreeBSD pkg supports `openjdk` and `java/openjdk7` package names - origin = bool(re.search('/', name)) + origin = bool(re.search("/", name)) - if __grains__['os'] == 'FreeBSD' and origin: - cver = [k for k, v in six.iteritems(cur_pkgs) - if v['origin'] == name] + if __grains__["os"] == "FreeBSD" and origin: + cver = [k for k, v in six.iteritems(cur_pkgs) if v["origin"] == name] else: cver = cur_pkgs.get(name, []) if name not in to_unpurge: - if version and version in cver \ - and not reinstall \ - and not pkg_verify: + if version and version in cver and not reinstall and not pkg_verify: # The package is installed and is the correct version - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Version {0} of package \'{1}\' is already ' - 'installed'.format(version, name)} + return { + "name": name, + "changes": {}, + "result": True, + "comment": "Version {0} of package '{1}' is already " + "installed".format(version, name), + } # if cver is not an empty string, the package is already installed - elif cver and version is None \ - and not reinstall \ - and not pkg_verify: + elif cver and version is None and not reinstall and not pkg_verify: # The package is installed - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Package {0} is already ' - 'installed'.format(name)} + return { + "name": name, + "changes": {}, + "result": True, + "comment": "Package {0} is already " "installed".format(name), + } version_spec = False if not sources: @@ -641,11 +664,19 @@ def _find_install_targets(name=None, # enforced. Takes extra time. Disable for improved performance if not skip_suggestions: # Perform platform-specific pre-flight checks - not_installed = dict([ - (name, version) - for name, version in desired.items() - if not (name in cur_pkgs and (version is None or _fulfills_version_string(cur_pkgs[name], version))) - ]) + not_installed = dict( + [ + (name, version) + for name, version in desired.items() + if not ( + name in cur_pkgs + and ( + version is None + or _fulfills_version_string(cur_pkgs[name], version) + ) + ) + ] + ) if not_installed: try: problems = _preflight_check(not_installed, **kwargs) @@ -653,38 +684,37 @@ def _find_install_targets(name=None, pass else: comments = [] - if problems.get('no_suggest'): + if problems.get("no_suggest"): comments.append( - 'The following package(s) were not found, and no ' - 'possible matches were found in the package db: ' - '{0}'.format( - ', '.join(sorted(problems['no_suggest'])) - ) + "The following package(s) were not found, and no " + "possible matches were found in the package db: " + "{0}".format(", ".join(sorted(problems["no_suggest"]))) ) - if problems.get('suggest'): - for pkgname, suggestions in \ - six.iteritems(problems['suggest']): + if problems.get("suggest"): + for pkgname, suggestions in six.iteritems(problems["suggest"]): comments.append( - 'Package \'{0}\' not found (possible matches: ' - '{1})'.format(pkgname, ', '.join(suggestions)) + "Package '{0}' not found (possible matches: " + "{1})".format(pkgname, ", ".join(suggestions)) ) if comments: if len(comments) > 1: - comments.append('') - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': '. '.join(comments).rstrip()} + comments.append("") + return { + "name": name, + "changes": {}, + "result": False, + "comment": ". ".join(comments).rstrip(), + } # Resolve the latest package version for any packages with "latest" in the # package version - wants_latest = [] \ - if sources \ - else [x for x, y in six.iteritems(desired) if y == 'latest'] + wants_latest = ( + [] if sources else [x for x, y in six.iteritems(desired) if y == "latest"] + ) if wants_latest: - resolved_latest = __salt__['pkg.latest_version'](*wants_latest, - refresh=refresh, - **kwargs) + resolved_latest = __salt__["pkg.latest_version"]( + *wants_latest, refresh=refresh, **kwargs + ) if len(wants_latest) == 1: resolved_latest = {wants_latest[0]: resolved_latest} if refresh: @@ -724,44 +754,50 @@ def _find_install_targets(name=None, if reinstall: to_reinstall[package_name] = version_string continue - elif 'lowpkg.bin_pkg_info' not in __salt__: + elif "lowpkg.bin_pkg_info" not in __salt__: continue # Metadata parser is available, cache the file and derive the # package's name and version - err = 'Unable to cache {0}: {1}' + err = "Unable to cache {0}: {1}" try: - cached_path = __salt__['cp.cache_file'](version_string, saltenv=kwargs['saltenv']) + cached_path = __salt__["cp.cache_file"]( + version_string, saltenv=kwargs["saltenv"] + ) except CommandExecutionError as exc: problems.append(err.format(version_string, exc)) continue if not cached_path: - problems.append(err.format(version_string, 'file not found')) + problems.append(err.format(version_string, "file not found")) continue elif not os.path.exists(cached_path): - problems.append('{0} does not exist on minion'.format(version_string)) + problems.append("{0} does not exist on minion".format(version_string)) continue - source_info = __salt__['lowpkg.bin_pkg_info'](cached_path) + source_info = __salt__["lowpkg.bin_pkg_info"](cached_path) if source_info is None: - warnings.append('Failed to parse metadata for {0}'.format(version_string)) + warnings.append( + "Failed to parse metadata for {0}".format(version_string) + ) continue else: - verstr = source_info['version'] + verstr = source_info["version"] else: verstr = version_string if reinstall: to_reinstall[package_name] = version_string continue - if not __salt__['pkg_resource.check_extra_requirements'](package_name, version_string): + if not __salt__["pkg_resource.check_extra_requirements"]( + package_name, version_string + ): targets[package_name] = version_string continue # No version specified and pkg is installed - elif __salt__['pkg_resource.version_clean'](version_string) is None: + elif __salt__["pkg_resource.version_clean"](version_string) is None: if (not reinstall) and pkg_verify: try: - verify_result = __salt__['pkg.verify']( + verify_result = __salt__["pkg.verify"]( package_name, ignore_types=ignore_types, - verify_options=verify_options + verify_options=verify_options, ) except (CommandExecutionError, SaltInvocationError) as exc: failed_verify = exc.strerror @@ -771,9 +807,11 @@ def _find_install_targets(name=None, altered_files[package_name] = verify_result continue version_fulfilled = False - allow_updates = bool(not sources and kwargs.get('allow_updates')) + allow_updates = bool(not sources and kwargs.get("allow_updates")) try: - version_fulfilled = _fulfills_version_string(cver, verstr, ignore_epoch=ignore_epoch, allow_updates=allow_updates) + version_fulfilled = _fulfills_version_string( + cver, verstr, ignore_epoch=ignore_epoch, allow_updates=allow_updates + ) except CommandExecutionError as exc: problems.append(exc.strerror) continue @@ -785,12 +823,15 @@ def _find_install_targets(name=None, to_reinstall[package_name] = version_string else: version_conditions = _parse_version_string(version_string) - if pkg_verify and any(oper == '==' for oper, version in version_conditions): + if pkg_verify and any( + oper == "==" for oper, version in version_conditions + ): try: - verify_result = __salt__['pkg.verify']( + verify_result = __salt__["pkg.verify"]( package_name, ignore_types=ignore_types, - verify_options=verify_options) + verify_options=verify_options, + ) except (CommandExecutionError, SaltInvocationError) as exc: failed_verify = exc.strerror continue @@ -799,9 +840,10 @@ def _find_install_targets(name=None, altered_files[package_name] = verify_result else: log.debug( - 'Current version (%s) did not match desired version ' - 'specification (%s), adding to installation targets', - cver, version_string + "Current version (%s) did not match desired version " + "specification (%s), adding to installation targets", + cver, + version_string, ) targets[package_name] = version_string @@ -809,35 +851,40 @@ def _find_install_targets(name=None, problems.append(failed_verify) if problems: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': ' '.join(problems)} + return { + "name": name, + "changes": {}, + "result": False, + "comment": " ".join(problems), + } if not any((targets, to_unpurge, to_reinstall)): # All specified packages are installed - msg = 'All specified packages are already installed{0}' + msg = "All specified packages are already installed{0}" msg = msg.format( - ' and are at the desired version' if version_spec and not sources - else '' + " and are at the desired version" if version_spec and not sources else "" ) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': msg} + ret = {"name": name, "changes": {}, "result": True, "comment": msg} if warnings: - ret.setdefault('warnings', []).extend(warnings) + ret.setdefault("warnings", []).extend(warnings) return ret - return (desired, targets, to_unpurge, to_reinstall, altered_files, - warnings, was_refreshed) + return ( + desired, + targets, + to_unpurge, + to_reinstall, + altered_files, + warnings, + was_refreshed, + ) def _verify_install(desired, new_pkgs, ignore_epoch=False, new_caps=None): - ''' + """ Determine whether or not the installed packages match what was requested in the SLS file. - ''' + """ _ok = [] failed = [] if not new_caps: @@ -846,16 +893,16 @@ def _verify_install(desired, new_pkgs, ignore_epoch=False, new_caps=None): # FreeBSD pkg supports `openjdk` and `java/openjdk7` package names. # Homebrew for Mac OSX does something similar with tap names # prefixing package names, separated with a slash. - has_origin = '/' in pkgname + has_origin = "/" in pkgname - if __grains__['os'] == 'FreeBSD' and has_origin: - cver = [k for k, v in six.iteritems(new_pkgs) if v['origin'] == pkgname] - elif __grains__['os'] == 'MacOS' and has_origin: - cver = new_pkgs.get(pkgname, new_pkgs.get(pkgname.split('/')[-1])) - elif __grains__['os'] == 'OpenBSD': - cver = new_pkgs.get(pkgname.split('%')[0]) - elif __grains__['os_family'] == 'Debian': - cver = new_pkgs.get(pkgname.split('=')[0]) + if __grains__["os"] == "FreeBSD" and has_origin: + cver = [k for k, v in six.iteritems(new_pkgs) if v["origin"] == pkgname] + elif __grains__["os"] == "MacOS" and has_origin: + cver = new_pkgs.get(pkgname, new_pkgs.get(pkgname.split("/")[-1])) + elif __grains__["os"] == "OpenBSD": + cver = new_pkgs.get(pkgname.split("%")[0]) + elif __grains__["os_family"] == "Debian": + cver = new_pkgs.get(pkgname.split("=")[0]) else: cver = new_pkgs.get(pkgname) if not cver and pkgname in new_caps: @@ -864,10 +911,10 @@ def _verify_install(desired, new_pkgs, ignore_epoch=False, new_caps=None): if not cver: failed.append(pkgname) continue - elif pkgver == 'latest': + elif pkgver == "latest": _ok.append(pkgname) continue - elif not __salt__['pkg_resource.version_clean'](pkgver): + elif not __salt__["pkg_resource.version_clean"](pkgver): _ok.append(pkgname) continue elif pkgver.endswith("*") and cver[0].startswith(pkgver[:-1]): @@ -881,49 +928,48 @@ def _verify_install(desired, new_pkgs, ignore_epoch=False, new_caps=None): def _get_desired_pkg(name, desired): - ''' + """ Helper function that retrieves and nicely formats the desired pkg (and version if specified) so that helpful information can be printed in the comment for the state. - ''' - if not desired[name] or desired[name].startswith(('<', '>', '=')): - oper = '' + """ + if not desired[name] or desired[name].startswith(("<", ">", "=")): + oper = "" else: - oper = '=' - return '{0}{1}{2}'.format(name, oper, - '' if not desired[name] else desired[name]) + oper = "=" + return "{0}{1}{2}".format(name, oper, "" if not desired[name] else desired[name]) def _preflight_check(desired, fromrepo, **kwargs): - ''' + """ Perform platform-specific checks on desired packages - ''' - if 'pkg.check_db' not in __salt__: + """ + if "pkg.check_db" not in __salt__: return {} - ret = {'suggest': {}, 'no_suggest': []} - pkginfo = __salt__['pkg.check_db']( + ret = {"suggest": {}, "no_suggest": []} + pkginfo = __salt__["pkg.check_db"]( *list(desired.keys()), fromrepo=fromrepo, **kwargs ) for pkgname in pkginfo: - if pkginfo[pkgname]['found'] is False: - if pkginfo[pkgname]['suggestions']: - ret['suggest'][pkgname] = pkginfo[pkgname]['suggestions'] + if pkginfo[pkgname]["found"] is False: + if pkginfo[pkgname]["suggestions"]: + ret["suggest"][pkgname] = pkginfo[pkgname]["suggestions"] else: - ret['no_suggest'].append(pkgname) + ret["no_suggest"].append(pkgname) return ret def _nested_output(obj): - ''' + """ Serialize obj and format for output - ''' + """ nested.__opts__ = __opts__ ret = nested.output(obj).rstrip() return ret def _resolve_capabilities(pkgs, refresh=False, **kwargs): - ''' + """ Resolve capabilities in ``pkgs`` and exchange them with real package names, when the result is distinct. This feature can be turned on while setting the paramter @@ -934,31 +980,32 @@ def _resolve_capabilities(pkgs, refresh=False, **kwargs): In case of ``resolve_capabilities`` is False (disabled) or not supported by the implementation the input is returned unchanged. - ''' - if not pkgs or 'pkg.resolve_capabilities' not in __salt__: + """ + if not pkgs or "pkg.resolve_capabilities" not in __salt__: return pkgs, refresh - ret = __salt__['pkg.resolve_capabilities'](pkgs, refresh=refresh, **kwargs) + ret = __salt__["pkg.resolve_capabilities"](pkgs, refresh=refresh, **kwargs) return ret, False def installed( - name, - version=None, - refresh=None, - fromrepo=None, - skip_verify=False, - skip_suggestions=False, - pkgs=None, - sources=None, - allow_updates=False, - pkg_verify=False, - normalize=True, - ignore_epoch=False, - reinstall=False, - update_holds=False, - **kwargs): - ''' + name, + version=None, + refresh=None, + fromrepo=None, + skip_verify=False, + skip_suggestions=False, + pkgs=None, + sources=None, + allow_updates=False, + pkg_verify=False, + normalize=True, + ignore_epoch=False, + reinstall=False, + update_holds=False, + **kwargs +): + """ Ensure that the package is installed, and that it is the correct version (if specified). @@ -1578,12 +1625,14 @@ def installed( small reductions in waiting time can add up. :ref:`Unless Requisite <unless-requisite>` - ''' + """ if isinstance(pkgs, list) and len(pkgs) == 0: - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'No packages to install provided'} + return { + "name": name, + "changes": {}, + "result": True, + "comment": "No packages to install provided", + } # If just a name (and optionally a version) is passed, just pack them into # the pkgs argument. @@ -1594,7 +1643,7 @@ def installed( else: pkgs = [name] - kwargs['saltenv'] = __env__ + kwargs["saltenv"] = __env__ refresh = salt.utils.pkg.check_refresh(__opts__, refresh) # check if capabilities should be checked and modify the requested packages @@ -1604,86 +1653,106 @@ def installed( if not isinstance(pkg_verify, list): pkg_verify = pkg_verify is True - if (pkg_verify or isinstance(pkg_verify, list)) \ - and 'pkg.verify' not in __salt__: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'pkg.verify not implemented'} + if (pkg_verify or isinstance(pkg_verify, list)) and "pkg.verify" not in __salt__: + return { + "name": name, + "changes": {}, + "result": False, + "comment": "pkg.verify not implemented", + } if not isinstance(version, six.string_types) and version is not None: version = six.text_type(version) - kwargs['allow_updates'] = allow_updates + kwargs["allow_updates"] = allow_updates - result = _find_install_targets(name, version, pkgs, sources, - fromrepo=fromrepo, - skip_suggestions=skip_suggestions, - pkg_verify=pkg_verify, - normalize=normalize, - ignore_epoch=ignore_epoch, - reinstall=reinstall, - refresh=refresh, - **kwargs) + result = _find_install_targets( + name, + version, + pkgs, + sources, + fromrepo=fromrepo, + skip_suggestions=skip_suggestions, + pkg_verify=pkg_verify, + normalize=normalize, + ignore_epoch=ignore_epoch, + reinstall=reinstall, + refresh=refresh, + **kwargs + ) try: - (desired, targets, to_unpurge, to_reinstall, - altered_files, warnings, was_refreshed) = result + ( + desired, + targets, + to_unpurge, + to_reinstall, + altered_files, + warnings, + was_refreshed, + ) = result if was_refreshed: refresh = False except ValueError: # _find_install_targets() found no targets or encountered an error # check that the hold function is available - if 'pkg.hold' in __salt__ and 'hold' in kwargs: + if "pkg.hold" in __salt__ and "hold" in kwargs: try: - action = 'pkg.hold' if kwargs['hold'] else 'pkg.unhold' - hold_ret = __salt__[action]( - name=name, pkgs=pkgs, sources=sources - ) + action = "pkg.hold" if kwargs["hold"] else "pkg.unhold" + hold_ret = __salt__[action](name=name, pkgs=pkgs, sources=sources) except (CommandExecutionError, SaltInvocationError) as exc: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': six.text_type(exc)} + return { + "name": name, + "changes": {}, + "result": False, + "comment": six.text_type(exc), + } - if 'result' in hold_ret and not hold_ret['result']: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'An error was encountered while ' - 'holding/unholding package(s): {0}' - .format(hold_ret['comment'])} + if "result" in hold_ret and not hold_ret["result"]: + return { + "name": name, + "changes": {}, + "result": False, + "comment": "An error was encountered while " + "holding/unholding package(s): {0}".format(hold_ret["comment"]), + } else: - modified_hold = [hold_ret[x] for x in hold_ret - if hold_ret[x]['changes']] - not_modified_hold = [hold_ret[x] for x in hold_ret - if not hold_ret[x]['changes'] - and hold_ret[x]['result']] - failed_hold = [hold_ret[x] for x in hold_ret - if not hold_ret[x]['result']] + modified_hold = [ + hold_ret[x] for x in hold_ret if hold_ret[x]["changes"] + ] + not_modified_hold = [ + hold_ret[x] + for x in hold_ret + if not hold_ret[x]["changes"] and hold_ret[x]["result"] + ] + failed_hold = [ + hold_ret[x] for x in hold_ret if not hold_ret[x]["result"] + ] for i in modified_hold: - result['comment'] += '.\n{0}'.format(i['comment']) - result['result'] = i['result'] - result['changes'][i['name']] = i['changes'] + result["comment"] += ".\n{0}".format(i["comment"]) + result["result"] = i["result"] + result["changes"][i["name"]] = i["changes"] for i in not_modified_hold: - result['comment'] += '.\n{0}'.format(i['comment']) - result['result'] = i['result'] + result["comment"] += ".\n{0}".format(i["comment"]) + result["result"] = i["result"] for i in failed_hold: - result['comment'] += '.\n{0}'.format(i['comment']) - result['result'] = i['result'] + result["comment"] += ".\n{0}".format(i["comment"]) + result["result"] = i["result"] return result - if to_unpurge and 'lowpkg.unpurge' not in __salt__: - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'lowpkg.unpurge not implemented'} + if to_unpurge and "lowpkg.unpurge" not in __salt__: + ret = { + "name": name, + "changes": {}, + "result": False, + "comment": "lowpkg.unpurge not implemented", + } if warnings: - ret.setdefault('warnings', []).extend(warnings) + ret.setdefault("warnings", []).extend(warnings) return ret # Remove any targets not returned by _find_install_targets @@ -1692,26 +1761,26 @@ def installed( pkgs.extend([dict([(x, y)]) for x, y in six.iteritems(to_reinstall)]) elif sources: oldsources = sources - sources = [x for x in oldsources - if next(iter(list(x.keys()))) in targets] - sources.extend([x for x in oldsources - if next(iter(list(x.keys()))) in to_reinstall]) + sources = [x for x in oldsources if next(iter(list(x.keys()))) in targets] + sources.extend( + [x for x in oldsources if next(iter(list(x.keys()))) in to_reinstall] + ) comment = [] - if __opts__['test']: + if __opts__["test"]: if targets: if sources: - summary = ', '.join(targets) + summary = ", ".join(targets) else: - summary = ', '.join([_get_desired_pkg(x, targets) - for x in targets]) - comment.append('The following packages would be ' - 'installed/updated: {0}'.format(summary)) + summary = ", ".join([_get_desired_pkg(x, targets) for x in targets]) + comment.append( + "The following packages would be " + "installed/updated: {0}".format(summary) + ) if to_unpurge: comment.append( - 'The following packages would have their selection status ' - 'changed from \'purge\' to \'install\': {0}' - .format(', '.join(to_unpurge)) + "The following packages would have their selection status " + "changed from 'purge' to 'install': {0}".format(", ".join(to_unpurge)) ) if to_reinstall: # Add a comment for each package in to_reinstall with its @@ -1725,8 +1794,8 @@ def installed( reinstall_targets.append( _get_desired_pkg(reinstall_pkg, to_reinstall) ) - msg = 'The following packages would be reinstalled: ' - msg += ', '.join(reinstall_targets) + msg = "The following packages would be reinstalled: " + msg += ", ".join(reinstall_targets) comment.append(msg) else: for reinstall_pkg in to_reinstall: @@ -1735,57 +1804,61 @@ def installed( else: pkgstr = _get_desired_pkg(reinstall_pkg, to_reinstall) comment.append( - 'Package \'{0}\' would be reinstalled because the ' - 'following files have been altered:'.format(pkgstr) + "Package '{0}' would be reinstalled because the " + "following files have been altered:".format(pkgstr) ) - comment.append( - _nested_output(altered_files[reinstall_pkg]) - ) - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': '\n'.join(comment)} + comment.append(_nested_output(altered_files[reinstall_pkg])) + ret = { + "name": name, + "changes": {}, + "result": None, + "comment": "\n".join(comment), + } if warnings: - ret.setdefault('warnings', []).extend(warnings) + ret.setdefault("warnings", []).extend(warnings) return ret - changes = {'installed': {}} + changes = {"installed": {}} modified_hold = None not_modified_hold = None failed_hold = None if targets or to_reinstall: try: - pkg_ret = __salt__['pkg.install'](name=None, - refresh=refresh, - version=version, - fromrepo=fromrepo, - skip_verify=skip_verify, - pkgs=pkgs, - sources=sources, - reinstall=bool(to_reinstall), - normalize=normalize, - update_holds=update_holds, - ignore_epoch=ignore_epoch, - **kwargs) + pkg_ret = __salt__["pkg.install"]( + name=None, + refresh=refresh, + version=version, + fromrepo=fromrepo, + skip_verify=skip_verify, + pkgs=pkgs, + sources=sources, + reinstall=bool(to_reinstall), + normalize=normalize, + update_holds=update_holds, + ignore_epoch=ignore_epoch, + **kwargs + ) except CommandExecutionError as exc: - ret = {'name': name, 'result': False} + ret = {"name": name, "result": False} if exc.info: # Get information for state return from the exception. - ret['changes'] = exc.info.get('changes', {}) - ret['comment'] = exc.strerror_without_changes + ret["changes"] = exc.info.get("changes", {}) + ret["comment"] = exc.strerror_without_changes else: - ret['changes'] = {} - ret['comment'] = ('An error was encountered while installing ' - 'package(s): {0}'.format(exc)) + ret["changes"] = {} + ret["comment"] = ( + "An error was encountered while installing " + "package(s): {0}".format(exc) + ) if warnings: - ret.setdefault('warnings', []).extend(warnings) + ret.setdefault("warnings", []).extend(warnings) return ret if refresh: refresh = False if isinstance(pkg_ret, dict): - changes['installed'].update(pkg_ret) + changes["installed"].update(pkg_ret) elif isinstance(pkg_ret, six.string_types): comment.append(pkg_ret) # Code below will be looking for a dictionary. If this is a string @@ -1795,168 +1868,174 @@ def installed( # checks reinstall targets works. pkg_ret = {} - if 'pkg.hold' in __salt__ and 'hold' in kwargs: + if "pkg.hold" in __salt__ and "hold" in kwargs: try: - action = 'pkg.hold' if kwargs['hold'] else 'pkg.unhold' - hold_ret = __salt__[action]( - name=name, pkgs=desired - ) + action = "pkg.hold" if kwargs["hold"] else "pkg.unhold" + hold_ret = __salt__[action](name=name, pkgs=desired) except (CommandExecutionError, SaltInvocationError) as exc: comment.append(six.text_type(exc)) - ret = {'name': name, - 'changes': changes, - 'result': False, - 'comment': '\n'.join(comment)} + ret = { + "name": name, + "changes": changes, + "result": False, + "comment": "\n".join(comment), + } if warnings: - ret.setdefault('warnings', []).extend(warnings) + ret.setdefault("warnings", []).extend(warnings) return ret else: - if 'result' in hold_ret and not hold_ret['result']: - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'An error was encountered while ' - 'holding/unholding package(s): {0}' - .format(hold_ret['comment'])} + if "result" in hold_ret and not hold_ret["result"]: + ret = { + "name": name, + "changes": {}, + "result": False, + "comment": "An error was encountered while " + "holding/unholding package(s): {0}".format(hold_ret["comment"]), + } if warnings: - ret.setdefault('warnings', []).extend(warnings) + ret.setdefault("warnings", []).extend(warnings) return ret else: - modified_hold = [hold_ret[x] for x in hold_ret - if hold_ret[x]['changes']] - not_modified_hold = [hold_ret[x] for x in hold_ret - if not hold_ret[x]['changes'] - and hold_ret[x]['result']] - failed_hold = [hold_ret[x] for x in hold_ret - if not hold_ret[x]['result']] + modified_hold = [ + hold_ret[x] for x in hold_ret if hold_ret[x]["changes"] + ] + not_modified_hold = [ + hold_ret[x] + for x in hold_ret + if not hold_ret[x]["changes"] and hold_ret[x]["result"] + ] + failed_hold = [ + hold_ret[x] for x in hold_ret if not hold_ret[x]["result"] + ] if to_unpurge: - changes['purge_desired'] = __salt__['lowpkg.unpurge'](*to_unpurge) + changes["purge_desired"] = __salt__["lowpkg.unpurge"](*to_unpurge) # Analyze pkg.install results for packages in targets if sources: - modified = [x for x in changes['installed'] if x in targets] - not_modified = [x for x in desired - if x not in targets - and x not in to_reinstall] + modified = [x for x in changes["installed"] if x in targets] + not_modified = [ + x for x in desired if x not in targets and x not in to_reinstall + ] failed = [x for x in targets if x not in modified] else: - if __grains__['os'] == 'FreeBSD': - kwargs['with_origin'] = True - new_pkgs = __salt__['pkg.list_pkgs'](versions_as_list=True, **kwargs) - if kwargs.get('resolve_capabilities', False) and 'pkg.list_provides' in __salt__: - new_caps = __salt__['pkg.list_provides'](**kwargs) + if __grains__["os"] == "FreeBSD": + kwargs["with_origin"] = True + new_pkgs = __salt__["pkg.list_pkgs"](versions_as_list=True, **kwargs) + if ( + kwargs.get("resolve_capabilities", False) + and "pkg.list_provides" in __salt__ + ): + new_caps = __salt__["pkg.list_provides"](**kwargs) else: new_caps = {} - _ok, failed = _verify_install(desired, new_pkgs, - ignore_epoch=ignore_epoch, - new_caps=new_caps) + _ok, failed = _verify_install( + desired, new_pkgs, ignore_epoch=ignore_epoch, new_caps=new_caps + ) modified = [x for x in _ok if x in targets] - not_modified = [x for x in _ok - if x not in targets - and x not in to_reinstall] + not_modified = [x for x in _ok if x not in targets and x not in to_reinstall] failed = [x for x in failed if x in targets] # If there was nothing unpurged, just set the changes dict to the contents # of changes['installed']. - if not changes.get('purge_desired'): - changes = changes['installed'] + if not changes.get("purge_desired"): + changes = changes["installed"] if modified: if sources: - summary = ', '.join(modified) + summary = ", ".join(modified) else: - summary = ', '.join([_get_desired_pkg(x, desired) - for x in modified]) + summary = ", ".join([_get_desired_pkg(x, desired) for x in modified]) if len(summary) < 20: - comment.append('The following packages were installed/updated: ' - '{0}'.format(summary)) + comment.append( + "The following packages were installed/updated: " "{0}".format(summary) + ) else: comment.append( - '{0} targeted package{1} {2} installed/updated.'.format( + "{0} targeted package{1} {2} installed/updated.".format( len(modified), - 's' if len(modified) > 1 else '', - 'were' if len(modified) > 1 else 'was' + "s" if len(modified) > 1 else "", + "were" if len(modified) > 1 else "was", ) ) if modified_hold: for i in modified_hold: - change_name = i['name'] + change_name = i["name"] if change_name in changes: - comment.append(i['comment']) - if len(changes[change_name]['new']) > 0: - changes[change_name]['new'] += '\n' - changes[change_name]['new'] += '{0}'.format(i['changes']['new']) - if len(changes[change_name]['old']) > 0: - changes[change_name]['old'] += '\n' - changes[change_name]['old'] += '{0}'.format(i['changes']['old']) + comment.append(i["comment"]) + if len(changes[change_name]["new"]) > 0: + changes[change_name]["new"] += "\n" + changes[change_name]["new"] += "{0}".format(i["changes"]["new"]) + if len(changes[change_name]["old"]) > 0: + changes[change_name]["old"] += "\n" + changes[change_name]["old"] += "{0}".format(i["changes"]["old"]) else: - comment.append(i['comment']) + comment.append(i["comment"]) changes[change_name] = {} - changes[change_name]['new'] = '{0}'.format(i['changes']['new']) + changes[change_name]["new"] = "{0}".format(i["changes"]["new"]) # Any requested packages that were not targeted for install or reinstall if not_modified: if sources: - summary = ', '.join(not_modified) + summary = ", ".join(not_modified) else: - summary = ', '.join([_get_desired_pkg(x, desired) - for x in not_modified]) + summary = ", ".join([_get_desired_pkg(x, desired) for x in not_modified]) if len(not_modified) <= 20: - comment.append('The following packages were already installed: ' - '{0}'.format(summary)) + comment.append( + "The following packages were already installed: " "{0}".format(summary) + ) else: comment.append( - '{0} targeted package{1} {2} already installed'.format( + "{0} targeted package{1} {2} already installed".format( len(not_modified), - 's' if len(not_modified) > 1 else '', - 'were' if len(not_modified) > 1 else 'was' + "s" if len(not_modified) > 1 else "", + "were" if len(not_modified) > 1 else "was", ) ) if not_modified_hold: for i in not_modified_hold: - comment.append(i['comment']) + comment.append(i["comment"]) result = True if failed: if sources: - summary = ', '.join(failed) + summary = ", ".join(failed) else: - summary = ', '.join([_get_desired_pkg(x, desired) - for x in failed]) - comment.insert(0, 'The following packages failed to ' - 'install/update: {0}'.format(summary)) + summary = ", ".join([_get_desired_pkg(x, desired) for x in failed]) + comment.insert( + 0, "The following packages failed to " "install/update: {0}".format(summary) + ) result = False if failed_hold: for i in failed_hold: - comment.append(i['comment']) + comment.append(i["comment"]) result = False # Get the ignore_types list if any from the pkg_verify argument - if isinstance(pkg_verify, list) \ - and any(x.get('ignore_types') is not None - for x in pkg_verify - if isinstance(x, _OrderedDict) - and 'ignore_types' in x): - ignore_types = next(x.get('ignore_types') - for x in pkg_verify - if 'ignore_types' in x) + if isinstance(pkg_verify, list) and any( + x.get("ignore_types") is not None + for x in pkg_verify + if isinstance(x, _OrderedDict) and "ignore_types" in x + ): + ignore_types = next( + x.get("ignore_types") for x in pkg_verify if "ignore_types" in x + ) else: ignore_types = [] # Get the verify_options list if any from the pkg_verify argument - if isinstance(pkg_verify, list) \ - and any(x.get('verify_options') is not None - for x in pkg_verify - if isinstance(x, _OrderedDict) - and 'verify_options' in x): - verify_options = next(x.get('verify_options') - for x in pkg_verify - if 'verify_options' in x) + if isinstance(pkg_verify, list) and any( + x.get("verify_options") is not None + for x in pkg_verify + if isinstance(x, _OrderedDict) and "verify_options" in x + ): + verify_options = next( + x.get("verify_options") for x in pkg_verify if "verify_options" in x + ) else: verify_options = [] @@ -1972,9 +2051,9 @@ def installed( elif pkg_verify: # No need to wrap this in a try/except because we would already # have caught invalid arguments earlier. - verify_result = __salt__['pkg.verify'](reinstall_pkg, - ignore_types=ignore_types, - verify_options=verify_options) + verify_result = __salt__["pkg.verify"]( + reinstall_pkg, ignore_types=ignore_types, verify_options=verify_options + ) if verify_result: failed.append(reinstall_pkg) altered_files[reinstall_pkg] = verify_result @@ -1988,9 +2067,9 @@ def installed( pkgstr = modified_pkg else: pkgstr = _get_desired_pkg(modified_pkg, desired) - msg = 'Package {0} was reinstalled.'.format(pkgstr) + msg = "Package {0} was reinstalled.".format(pkgstr) if modified_pkg in altered_files: - msg += ' The following files were remediated:' + msg += " The following files were remediated:" comment.append(msg) comment.append(_nested_output(altered_files[modified_pkg])) else: @@ -2003,32 +2082,30 @@ def installed( pkgstr = failed_pkg else: pkgstr = _get_desired_pkg(failed_pkg, desired) - msg = ('Reinstall was not successful for package {0}.' - .format(pkgstr)) + msg = "Reinstall was not successful for package {0}.".format(pkgstr) if failed_pkg in altered_files: - msg += ' The following files could not be remediated:' + msg += " The following files could not be remediated:" comment.append(msg) comment.append(_nested_output(altered_files[failed_pkg])) else: comment.append(msg) result = False - ret = {'name': name, - 'changes': changes, - 'result': result, - 'comment': '\n'.join(comment)} + ret = { + "name": name, + "changes": changes, + "result": result, + "comment": "\n".join(comment), + } if warnings: - ret.setdefault('warnings', []).extend(warnings) + ret.setdefault("warnings", []).extend(warnings) return ret -def downloaded(name, - version=None, - pkgs=None, - fromrepo=None, - ignore_epoch=None, - **kwargs): - ''' +def downloaded( + name, version=None, pkgs=None, fromrepo=None, ignore_epoch=None, **kwargs +): + """ .. versionadded:: 2017.7.0 Ensure that the package is downloaded, and that it is the correct version @@ -2086,21 +2163,17 @@ def downloaded(name, pkg.downloaded: - version: 5.0.5-4.63 - fromrepo: "myrepository" - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if 'pkg.list_downloaded' not in __salt__: - ret['result'] = False - ret['comment'] = 'The pkg.downloaded state is not available on ' \ - 'this platform' + if "pkg.list_downloaded" not in __salt__: + ret["result"] = False + ret["comment"] = "The pkg.downloaded state is not available on " "this platform" return ret if isinstance(pkgs, list) and len(pkgs) == 0: - ret['result'] = True - ret['comment'] = 'No packages to download provided' + ret["result"] = True + ret["comment"] = "No packages to download provided" return ret # If just a name (and optionally a version) is passed, just pack them into @@ -2114,74 +2187,76 @@ def downloaded(name, # It doesn't make sense here to received 'downloadonly' as kwargs # as we're explicitly passing 'downloadonly=True' to execution module. - if 'downloadonly' in kwargs: - del kwargs['downloadonly'] + if "downloadonly" in kwargs: + del kwargs["downloadonly"] pkgs, _refresh = _resolve_capabilities(pkgs, **kwargs) # Only downloading not yet downloaded packages - targets = _find_download_targets(name, - version, - pkgs, - fromrepo=fromrepo, - ignore_epoch=ignore_epoch, - **kwargs) - if isinstance(targets, dict) and 'result' in targets: + targets = _find_download_targets( + name, version, pkgs, fromrepo=fromrepo, ignore_epoch=ignore_epoch, **kwargs + ) + if isinstance(targets, dict) and "result" in targets: return targets elif not isinstance(targets, dict): - ret['result'] = False - ret['comment'] = 'An error was encountered while checking targets: ' \ - '{0}'.format(targets) + ret["result"] = False + ret[ + "comment" + ] = "An error was encountered while checking targets: " "{0}".format(targets) return ret - if __opts__['test']: - summary = ', '.join(targets) - ret['comment'] = 'The following packages would be ' \ - 'downloaded: {0}'.format(summary) + if __opts__["test"]: + summary = ", ".join(targets) + ret["comment"] = "The following packages would be " "downloaded: {0}".format( + summary + ) return ret try: - pkg_ret = __salt__['pkg.install'](name=name, - pkgs=pkgs, - version=version, - downloadonly=True, - fromrepo=fromrepo, - ignore_epoch=ignore_epoch, - **kwargs) - ret['result'] = True - ret['changes'].update(pkg_ret) + pkg_ret = __salt__["pkg.install"]( + name=name, + pkgs=pkgs, + version=version, + downloadonly=True, + fromrepo=fromrepo, + ignore_epoch=ignore_epoch, + **kwargs + ) + ret["result"] = True + ret["changes"].update(pkg_ret) except CommandExecutionError as exc: - ret = {'name': name, 'result': False} + ret = {"name": name, "result": False} if exc.info: # Get information for state return from the exception. - ret['changes'] = exc.info.get('changes', {}) - ret['comment'] = exc.strerror_without_changes + ret["changes"] = exc.info.get("changes", {}) + ret["comment"] = exc.strerror_without_changes else: - ret['changes'] = {} - ret['comment'] = 'An error was encountered while downloading ' \ - 'package(s): {0}'.format(exc) + ret["changes"] = {} + ret["comment"] = ( + "An error was encountered while downloading " + "package(s): {0}".format(exc) + ) return ret - new_pkgs = __salt__['pkg.list_downloaded']() + new_pkgs = __salt__["pkg.list_downloaded"]() _ok, failed = _verify_install(targets, new_pkgs, ignore_epoch=ignore_epoch) if failed: - summary = ', '.join([_get_desired_pkg(x, targets) - for x in failed]) - ret['result'] = False - ret['comment'] = 'The following packages failed to ' \ - 'download: {0}'.format(summary) + summary = ", ".join([_get_desired_pkg(x, targets) for x in failed]) + ret["result"] = False + ret["comment"] = "The following packages failed to " "download: {0}".format( + summary + ) - if not ret['changes'] and not ret['comment']: - ret['result'] = True - ret['comment'] = 'Packages downloaded: ' \ - '{0}'.format(', '.join(targets)) + if not ret["changes"] and not ret["comment"]: + ret["result"] = True + ret["comment"] = "Packages downloaded: " "{0}".format(", ".join(targets)) return ret def patch_installed(name, advisory_ids=None, downloadonly=None, **kwargs): - ''' + """ .. versionadded:: 2017.7.0 Ensure that packages related to certain advisory ids are installed. @@ -2199,69 +2274,72 @@ def patch_installed(name, advisory_ids=None, downloadonly=None, **kwargs): - SUSE-SLE-SERVER-12-SP2-2017-185 - SUSE-SLE-SERVER-12-SP2-2017-150 - SUSE-SLE-SERVER-12-SP2-2017-120 - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if 'pkg.list_patches' not in __salt__: - ret['result'] = False - ret['comment'] = 'The pkg.patch_installed state is not available on ' \ - 'this platform' + if "pkg.list_patches" not in __salt__: + ret["result"] = False + ret["comment"] = ( + "The pkg.patch_installed state is not available on " "this platform" + ) return ret if isinstance(advisory_ids, list) and len(advisory_ids) == 0: - ret['result'] = True - ret['comment'] = 'No advisory ids provided' + ret["result"] = True + ret["comment"] = "No advisory ids provided" return ret # Only downloading not yet downloaded packages targets = _find_advisory_targets(name, advisory_ids, **kwargs) - if isinstance(targets, dict) and 'result' in targets: + if isinstance(targets, dict) and "result" in targets: return targets elif not isinstance(targets, list): - ret['result'] = False - ret['comment'] = 'An error was encountered while checking targets: ' \ - '{0}'.format(targets) + ret["result"] = False + ret[ + "comment" + ] = "An error was encountered while checking targets: " "{0}".format(targets) return ret - if __opts__['test']: - summary = ', '.join(targets) - ret['comment'] = 'The following advisory patches would be ' \ - 'downloaded: {0}'.format(summary) + if __opts__["test"]: + summary = ", ".join(targets) + ret[ + "comment" + ] = "The following advisory patches would be " "downloaded: {0}".format(summary) return ret try: - pkg_ret = __salt__['pkg.install'](name=name, - advisory_ids=advisory_ids, - downloadonly=downloadonly, - **kwargs) - ret['result'] = True - ret['changes'].update(pkg_ret) + pkg_ret = __salt__["pkg.install"]( + name=name, advisory_ids=advisory_ids, downloadonly=downloadonly, **kwargs + ) + ret["result"] = True + ret["changes"].update(pkg_ret) except CommandExecutionError as exc: - ret = {'name': name, 'result': False} + ret = {"name": name, "result": False} if exc.info: # Get information for state return from the exception. - ret['changes'] = exc.info.get('changes', {}) - ret['comment'] = exc.strerror_without_changes + ret["changes"] = exc.info.get("changes", {}) + ret["comment"] = exc.strerror_without_changes else: - ret['changes'] = {} - ret['comment'] = ('An error was encountered while downloading ' - 'package(s): {0}'.format(exc)) + ret["changes"] = {} + ret["comment"] = ( + "An error was encountered while downloading " + "package(s): {0}".format(exc) + ) return ret - if not ret['changes'] and not ret['comment']: - status = 'downloaded' if downloadonly else 'installed' - ret['result'] = True - ret['comment'] = ('Advisory patch is not needed or related packages ' - 'are already {0}'.format(status)) + if not ret["changes"] and not ret["comment"]: + status = "downloaded" if downloadonly else "installed" + ret["result"] = True + ret["comment"] = ( + "Advisory patch is not needed or related packages " + "are already {0}".format(status) + ) return ret def patch_downloaded(name, advisory_ids=None, **kwargs): - ''' + """ .. versionadded:: 2017.7.0 Ensure that packages related to certain advisory ids are downloaded. @@ -2279,30 +2357,35 @@ def patch_downloaded(name, advisory_ids=None, **kwargs): - SUSE-SLE-SERVER-12-SP2-2017-185 - SUSE-SLE-SERVER-12-SP2-2017-150 - SUSE-SLE-SERVER-12-SP2-2017-120 - ''' - if 'pkg.list_patches' not in __salt__: - return {'name': name, - 'result': False, - 'changes': {}, - 'comment': 'The pkg.patch_downloaded state is not available on ' - 'this platform'} + """ + if "pkg.list_patches" not in __salt__: + return { + "name": name, + "result": False, + "changes": {}, + "comment": "The pkg.patch_downloaded state is not available on " + "this platform", + } # It doesn't make sense here to received 'downloadonly' as kwargs # as we're explicitly passing 'downloadonly=True' to execution module. - if 'downloadonly' in kwargs: - del kwargs['downloadonly'] - return patch_installed(name=name, advisory_ids=advisory_ids, downloadonly=True, **kwargs) + if "downloadonly" in kwargs: + del kwargs["downloadonly"] + return patch_installed( + name=name, advisory_ids=advisory_ids, downloadonly=True, **kwargs + ) def latest( - name, - refresh=None, - fromrepo=None, - skip_verify=False, - pkgs=None, - watch_flags=True, - **kwargs): - ''' + name, + refresh=None, + fromrepo=None, + skip_verify=False, + pkgs=None, + watch_flags=True, + **kwargs +): + """ Ensure that the named package is installed and the latest available package. If the package can be updated, this state function will update the package. Generally it is better for the @@ -2436,60 +2519,62 @@ def latest( - name: ms-vcpp - report_reboot_exit_codes: False - ''' + """ refresh = salt.utils.pkg.check_refresh(__opts__, refresh) - if kwargs.get('sources'): - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'The "sources" parameter is not supported.'} + if kwargs.get("sources"): + return { + "name": name, + "changes": {}, + "result": False, + "comment": 'The "sources" parameter is not supported.', + } elif pkgs: desired_pkgs = list(_repack_pkgs(pkgs).keys()) # pylint: disable=not-callable if not desired_pkgs: # Badly-formatted SLS - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Invalidly formatted "pkgs" parameter. See ' - 'minion log.'} + return { + "name": name, + "changes": {}, + "result": False, + "comment": 'Invalidly formatted "pkgs" parameter. See ' "minion log.", + } else: if isinstance(pkgs, list) and len(pkgs) == 0: return { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'No packages to install provided' + "name": name, + "changes": {}, + "result": True, + "comment": "No packages to install provided", } else: desired_pkgs = [name] - kwargs['saltenv'] = __env__ + kwargs["saltenv"] = __env__ # check if capabilities should be checked and modify the requested packages # accordingly. - desired_pkgs, refresh = _resolve_capabilities(desired_pkgs, refresh=refresh, **kwargs) + desired_pkgs, refresh = _resolve_capabilities( + desired_pkgs, refresh=refresh, **kwargs + ) try: - avail = __salt__['pkg.latest_version'](*desired_pkgs, - fromrepo=fromrepo, - refresh=refresh, - **kwargs) + avail = __salt__["pkg.latest_version"]( + *desired_pkgs, fromrepo=fromrepo, refresh=refresh, **kwargs + ) except CommandExecutionError as exc: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'An error was encountered while checking the ' - 'newest available version of package(s): {0}' - .format(exc)} + return { + "name": name, + "changes": {}, + "result": False, + "comment": "An error was encountered while checking the " + "newest available version of package(s): {0}".format(exc), + } try: - cur = __salt__['pkg.version'](*desired_pkgs, **kwargs) + cur = __salt__["pkg.version"](*desired_pkgs, **kwargs) except CommandExecutionError as exc: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': exc.strerror} + return {"name": name, "changes": {}, "result": False, "comment": exc.strerror} # Repack the cur/avail data if only a single package is being checked if isinstance(cur, six.string_types): @@ -2504,12 +2589,14 @@ def latest( # Package either a) is up-to-date, or b) does not exist if not cur.get(pkg): # Package does not exist - msg = 'No information found for \'{0}\'.'.format(pkg) + msg = "No information found for '{0}'.".format(pkg) log.error(msg) problems.append(msg) - elif watch_flags \ - and __grains__.get('os') == 'Gentoo' \ - and __salt__['portage_config.is_changed_uses'](pkg): + elif ( + watch_flags + and __grains__.get("os") == "Gentoo" + and __salt__["portage_config.is_changed_uses"](pkg) + ): # Package is up-to-date, but Gentoo USE flags are changing so # we need to add it to the targets targets[pkg] = cur[pkg] @@ -2520,10 +2607,10 @@ def latest( if problems: return { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': ' '.join(problems) + "name": name, + "changes": {}, + "result": False, + "comment": " ".join(problems), } if targets: @@ -2536,32 +2623,32 @@ def latest( else: up_to_date = [x for x in pkgs if x not in targets] - if __opts__['test']: + if __opts__["test"]: comments = [] comments.append( - 'The following packages would be installed/upgraded: ' + - ', '.join(sorted(targets)) + "The following packages would be installed/upgraded: " + + ", ".join(sorted(targets)) ) if up_to_date: up_to_date_count = len(up_to_date) if up_to_date_count <= 10: comments.append( - 'The following packages are already up-to-date: ' + - ', '.join( - ['{0} ({1})'.format(x, cur[x]) - for x in sorted(up_to_date)] + "The following packages are already up-to-date: " + + ", ".join( + ["{0} ({1})".format(x, cur[x]) for x in sorted(up_to_date)] ) ) else: comments.append( - '{0} packages are already up-to-date' - .format(up_to_date_count) + "{0} packages are already up-to-date".format(up_to_date_count) ) - return {'name': name, - 'changes': {}, - 'result': None, - 'comment': '\n'.join(comments)} + return { + "name": name, + "changes": {}, + "result": None, + "comment": "\n".join(comments), + } if salt.utils.platform.is_windows(): # pkg.install execution module on windows ensures the software @@ -2577,157 +2664,191 @@ def latest( # No need to refresh, if a refresh was necessary it would have been # performed above when pkg.latest_version was run. try: - changes = __salt__['pkg.install'](name=None, - refresh=False, - fromrepo=fromrepo, - skip_verify=skip_verify, - pkgs=targeted_pkgs, - **kwargs) + changes = __salt__["pkg.install"]( + name=None, + refresh=False, + fromrepo=fromrepo, + skip_verify=skip_verify, + pkgs=targeted_pkgs, + **kwargs + ) except CommandExecutionError as exc: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'An error was encountered while installing ' - 'package(s): {0}'.format(exc)} + return { + "name": name, + "changes": {}, + "result": False, + "comment": "An error was encountered while installing " + "package(s): {0}".format(exc), + } if changes: # Find failed and successful updates - failed = [x for x in targets - if not changes.get(x) or - changes[x].get('new') != targets[x] and - targets[x] != 'latest'] + failed = [ + x + for x in targets + if not changes.get(x) + or changes[x].get("new") != targets[x] + and targets[x] != "latest" + ] successful = [x for x in targets if x not in failed] comments = [] if failed: - msg = 'The following packages failed to update: ' \ - '{0}'.format(', '.join(sorted(failed))) + msg = "The following packages failed to update: " "{0}".format( + ", ".join(sorted(failed)) + ) comments.append(msg) if successful: - msg = 'The following packages were successfully ' \ - 'installed/upgraded: ' \ - '{0}'.format(', '.join(sorted(successful))) + msg = ( + "The following packages were successfully " + "installed/upgraded: " + "{0}".format(", ".join(sorted(successful))) + ) comments.append(msg) if up_to_date: if len(up_to_date) <= 10: - msg = 'The following packages were already up-to-date: ' \ - '{0}'.format(', '.join(sorted(up_to_date))) + msg = ( + "The following packages were already up-to-date: " + "{0}".format(", ".join(sorted(up_to_date))) + ) else: - msg = '{0} packages were already up-to-date '.format( - len(up_to_date)) + msg = "{0} packages were already up-to-date ".format( + len(up_to_date) + ) comments.append(msg) - return {'name': name, - 'changes': changes, - 'result': False if failed else True, - 'comment': ' '.join(comments)} + return { + "name": name, + "changes": changes, + "result": False if failed else True, + "comment": " ".join(comments), + } else: if len(targets) > 10: - comment = ('{0} targeted packages failed to update. ' - 'See debug log for details.'.format(len(targets))) + comment = ( + "{0} targeted packages failed to update. " + "See debug log for details.".format(len(targets)) + ) elif len(targets) > 1: - comment = ('The following targeted packages failed to update. ' - 'See debug log for details: ({0}).' - .format(', '.join(sorted(targets)))) + comment = ( + "The following targeted packages failed to update. " + "See debug log for details: ({0}).".format( + ", ".join(sorted(targets)) + ) + ) else: - comment = 'Package {0} failed to ' \ - 'update.'.format(next(iter(list(targets.keys())))) + comment = "Package {0} failed to " "update.".format( + next(iter(list(targets.keys()))) + ) if up_to_date: if len(up_to_date) <= 10: - comment += ' The following packages were already ' \ - 'up-to-date: ' \ - '{0}'.format(', '.join(sorted(up_to_date))) + comment += ( + " The following packages were already " + "up-to-date: " + "{0}".format(", ".join(sorted(up_to_date))) + ) else: - comment += '{0} packages were already ' \ - 'up-to-date'.format(len(up_to_date)) + comment += "{0} packages were already " "up-to-date".format( + len(up_to_date) + ) - return {'name': name, - 'changes': changes, - 'result': False, - 'comment': comment} + return { + "name": name, + "changes": changes, + "result": False, + "comment": comment, + } else: if len(desired_pkgs) > 10: - comment = 'All {0} packages are up-to-date.'.format( - len(desired_pkgs)) + comment = "All {0} packages are up-to-date.".format(len(desired_pkgs)) elif len(desired_pkgs) > 1: - comment = 'All packages are up-to-date ' \ - '({0}).'.format(', '.join(sorted(desired_pkgs))) + comment = "All packages are up-to-date " "({0}).".format( + ", ".join(sorted(desired_pkgs)) + ) else: - comment = 'Package {0} is already ' \ - 'up-to-date'.format(desired_pkgs[0]) + comment = "Package {0} is already " "up-to-date".format(desired_pkgs[0]) - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': comment} + return {"name": name, "changes": {}, "result": True, "comment": comment} def _uninstall( - action='remove', + action="remove", name=None, version=None, pkgs=None, normalize=True, ignore_epoch=False, - **kwargs): - ''' + **kwargs +): + """ Common function for package removal - ''' - if action not in ('remove', 'purge'): - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Invalid action \'{0}\'. ' - 'This is probably a bug.'.format(action)} + """ + if action not in ("remove", "purge"): + return { + "name": name, + "changes": {}, + "result": False, + "comment": "Invalid action '{0}'. " + "This is probably a bug.".format(action), + } try: - pkg_params = __salt__['pkg_resource.parse_targets']( - name, - pkgs, - normalize=normalize)[0] + pkg_params = __salt__["pkg_resource.parse_targets"]( + name, pkgs, normalize=normalize + )[0] except MinionError as exc: - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'An error was encountered while parsing targets: ' - '{0}'.format(exc)} - targets = _find_remove_targets(name, version, pkgs, normalize, - ignore_epoch=ignore_epoch, **kwargs) - if isinstance(targets, dict) and 'result' in targets: + return { + "name": name, + "changes": {}, + "result": False, + "comment": "An error was encountered while parsing targets: " + "{0}".format(exc), + } + targets = _find_remove_targets( + name, version, pkgs, normalize, ignore_epoch=ignore_epoch, **kwargs + ) + if isinstance(targets, dict) and "result" in targets: return targets elif not isinstance(targets, list): - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'An error was encountered while checking targets: ' - '{0}'.format(targets)} - if action == 'purge': - old_removed = __salt__['pkg.list_pkgs'](versions_as_list=True, - removed=True, - **kwargs) + return { + "name": name, + "changes": {}, + "result": False, + "comment": "An error was encountered while checking targets: " + "{0}".format(targets), + } + if action == "purge": + old_removed = __salt__["pkg.list_pkgs"]( + versions_as_list=True, removed=True, **kwargs + ) targets.extend([x for x in pkg_params if x in old_removed]) targets.sort() if not targets: - return {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'None of the targeted packages are installed' - '{0}'.format(' or partially installed' - if action == 'purge' else '')} + return { + "name": name, + "changes": {}, + "result": True, + "comment": "None of the targeted packages are installed" + "{0}".format(" or partially installed" if action == "purge" else ""), + } - if __opts__['test']: - return {'name': name, - 'changes': {}, - 'result': None, - 'comment': 'The following packages will be {0}d: ' - '{1}.'.format(action, ', '.join(targets))} + if __opts__["test"]: + return { + "name": name, + "changes": {}, + "result": None, + "comment": "The following packages will be {0}d: " + "{1}.".format(action, ", ".join(targets)), + } - changes = __salt__['pkg.{0}'.format(action)](name, pkgs=pkgs, version=version, **kwargs) - new = __salt__['pkg.list_pkgs'](versions_as_list=True, **kwargs) + changes = __salt__["pkg.{0}".format(action)]( + name, pkgs=pkgs, version=version, **kwargs + ) + new = __salt__["pkg.list_pkgs"](versions_as_list=True, **kwargs) failed = [] for param in pkg_params: - if __grains__['os_family'] in ['Suse', 'RedHat']: + if __grains__["os_family"] in ["Suse", "RedHat"]: # Check if the package version set to be removed is actually removed: if param in new and not pkg_params[param]: failed.append(param) @@ -2736,43 +2857,48 @@ def _uninstall( elif param in new: failed.append(param) - if action == 'purge': - new_removed = __salt__['pkg.list_pkgs'](versions_as_list=True, - removed=True, - **kwargs) + if action == "purge": + new_removed = __salt__["pkg.list_pkgs"]( + versions_as_list=True, removed=True, **kwargs + ) failed.extend([x for x in pkg_params if x in new_removed]) failed.sort() if failed: - return {'name': name, - 'changes': changes, - 'result': False, - 'comment': 'The following packages failed to {0}: ' - '{1}.'.format(action, ', '.join(failed))} + return { + "name": name, + "changes": changes, + "result": False, + "comment": "The following packages failed to {0}: " + "{1}.".format(action, ", ".join(failed)), + } comments = [] not_installed = sorted([x for x in pkg_params if x not in targets]) if not_installed: - comments.append('The following packages were not installed: ' - '{0}'.format(', '.join(not_installed))) - comments.append('The following packages were {0}d: ' - '{1}.'.format(action, ', '.join(targets))) + comments.append( + "The following packages were not installed: " + "{0}".format(", ".join(not_installed)) + ) + comments.append( + "The following packages were {0}d: " + "{1}.".format(action, ", ".join(targets)) + ) else: - comments.append('All targeted packages were {0}d.'.format(action)) + comments.append("All targeted packages were {0}d.".format(action)) - return {'name': name, - 'changes': changes, - 'result': True, - 'comment': ' '.join(comments)} + return { + "name": name, + "changes": changes, + "result": True, + "comment": " ".join(comments), + } -def removed(name, - version=None, - pkgs=None, - normalize=True, - ignore_epoch=False, - **kwargs): - ''' +def removed( + name, version=None, pkgs=None, normalize=True, ignore_epoch=False, **kwargs +): + """ Verify that a package is not installed, calling ``pkg.remove`` if necessary to remove the package. @@ -2853,32 +2979,34 @@ def removed(name, version numbers as well. .. versionadded:: 0.16.0 - ''' - kwargs['saltenv'] = __env__ + """ + kwargs["saltenv"] = __env__ try: - return _uninstall(action='remove', name=name, version=version, - pkgs=pkgs, normalize=normalize, - ignore_epoch=ignore_epoch, **kwargs) + return _uninstall( + action="remove", + name=name, + version=version, + pkgs=pkgs, + normalize=normalize, + ignore_epoch=ignore_epoch, + **kwargs + ) except CommandExecutionError as exc: - ret = {'name': name, 'result': False} + ret = {"name": name, "result": False} if exc.info: # Get information for state return from the exception. - ret['changes'] = exc.info.get('changes', {}) - ret['comment'] = exc.strerror_without_changes + ret["changes"] = exc.info.get("changes", {}) + ret["comment"] = exc.strerror_without_changes else: - ret['changes'] = {} - ret['comment'] = ('An error was encountered while removing ' - 'package(s): {0}'.format(exc)) + ret["changes"] = {} + ret[ + "comment" + ] = "An error was encountered while removing " "package(s): {0}".format(exc) return ret -def purged(name, - version=None, - pkgs=None, - normalize=True, - ignore_epoch=False, - **kwargs): - ''' +def purged(name, version=None, pkgs=None, normalize=True, ignore_epoch=False, **kwargs): + """ Verify that a package is not installed, calling ``pkg.purge`` if necessary to purge the package. All configuration files are also removed. @@ -2959,27 +3087,34 @@ def purged(name, version numbers as well. .. versionadded:: 0.16.0 - ''' - kwargs['saltenv'] = __env__ + """ + kwargs["saltenv"] = __env__ try: - return _uninstall(action='purge', name=name, version=version, - pkgs=pkgs, normalize=normalize, - ignore_epoch=ignore_epoch, **kwargs) + return _uninstall( + action="purge", + name=name, + version=version, + pkgs=pkgs, + normalize=normalize, + ignore_epoch=ignore_epoch, + **kwargs + ) except CommandExecutionError as exc: - ret = {'name': name, 'result': False} + ret = {"name": name, "result": False} if exc.info: # Get information for state return from the exception. - ret['changes'] = exc.info.get('changes', {}) - ret['comment'] = exc.strerror_without_changes + ret["changes"] = exc.info.get("changes", {}) + ret["comment"] = exc.strerror_without_changes else: - ret['changes'] = {} - ret['comment'] = ('An error was encountered while purging ' - 'package(s): {0}'.format(exc)) + ret["changes"] = {} + ret[ + "comment" + ] = "An error was encountered while purging " "package(s): {0}".format(exc) return ret def uptodate(name, refresh=False, pkgs=None, **kwargs): - ''' + """ .. versionadded:: 2014.7.0 .. versionchanged:: 2018.3.0 @@ -3019,77 +3154,83 @@ def uptodate(name, refresh=False, pkgs=None, **kwargs): Any keyword arguments to pass through to ``pkg.upgrade``. .. versionadded:: 2015.5.0 - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Failed to update'} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": "Failed to update"} - if 'pkg.list_upgrades' not in __salt__: - ret['comment'] = 'State pkg.uptodate is not available' + if "pkg.list_upgrades" not in __salt__: + ret["comment"] = "State pkg.uptodate is not available" return ret # emerge --update doesn't appear to support repo notation - if 'fromrepo' in kwargs and __grains__['os'] == 'Gentoo': - ret['comment'] = '\'fromrepo\' argument not supported on this platform' + if "fromrepo" in kwargs and __grains__["os"] == "Gentoo": + ret["comment"] = "'fromrepo' argument not supported on this platform" return ret if isinstance(refresh, bool): pkgs, refresh = _resolve_capabilities(pkgs, refresh=refresh, **kwargs) try: - packages = __salt__['pkg.list_upgrades'](refresh=refresh, **kwargs) - expected = {pkgname: {'new': pkgver, 'old': __salt__['pkg.version'](pkgname)} - for pkgname, pkgver in six.iteritems(packages)} + packages = __salt__["pkg.list_upgrades"](refresh=refresh, **kwargs) + expected = { + pkgname: {"new": pkgver, "old": __salt__["pkg.version"](pkgname)} + for pkgname, pkgver in six.iteritems(packages) + } if isinstance(pkgs, list): packages = [pkg for pkg in packages if pkg in pkgs] - expected = {pkgname: pkgver for pkgname, pkgver in six.iteritems(expected) if pkgname in pkgs} + expected = { + pkgname: pkgver + for pkgname, pkgver in six.iteritems(expected) + if pkgname in pkgs + } except Exception as exc: # pylint: disable=broad-except - ret['comment'] = six.text_type(exc) + ret["comment"] = six.text_type(exc) return ret else: - ret['comment'] = 'refresh must be either True or False' + ret["comment"] = "refresh must be either True or False" return ret if not packages: - ret['comment'] = 'System is already up-to-date' - ret['result'] = True + ret["comment"] = "System is already up-to-date" + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'System update will be performed' - ret['changes'] = expected - ret['result'] = None + elif __opts__["test"]: + ret["comment"] = "System update will be performed" + ret["changes"] = expected + ret["result"] = None return ret try: - ret['changes'] = __salt__['pkg.upgrade'](refresh=refresh, pkgs=pkgs, **kwargs) + ret["changes"] = __salt__["pkg.upgrade"](refresh=refresh, pkgs=pkgs, **kwargs) except CommandExecutionError as exc: if exc.info: # Get information for state return from the exception. - ret['changes'] = exc.info.get('changes', {}) - ret['comment'] = exc.strerror_without_changes + ret["changes"] = exc.info.get("changes", {}) + ret["comment"] = exc.strerror_without_changes else: - ret['changes'] = {} - ret['comment'] = ('An error was encountered while updating ' - 'packages: {0}'.format(exc)) + ret["changes"] = {} + ret[ + "comment" + ] = "An error was encountered while updating " "packages: {0}".format(exc) return ret # If a package list was provided, ensure those packages were updated missing = [] if isinstance(pkgs, list): - missing = [pkg for pkg in six.iterkeys(expected) if pkg not in ret['changes']] + missing = [pkg for pkg in six.iterkeys(expected) if pkg not in ret["changes"]] if missing: - ret['comment'] = 'The following package(s) failed to update: {0}'.format(', '.join(missing)) - ret['result'] = False + ret["comment"] = "The following package(s) failed to update: {0}".format( + ", ".join(missing) + ) + ret["result"] = False else: - ret['comment'] = 'Upgrade ran successfully' - ret['result'] = True + ret["comment"] = "Upgrade ran successfully" + ret["result"] = True return ret def group_installed(name, skip=None, include=None, **kwargs): - ''' + """ .. versionadded:: 2015.8.0 .. versionchanged:: 2016.11.0 @@ -3132,21 +3273,18 @@ def group_installed(name, skip=None, include=None, **kwargs): <salt.modules.yumpkg.install>`, any argument which can be passed to pkg.install may also be included here, and it will be passed on to the call to :py:func:`pkg.install <salt.modules.yumpkg.install>`. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if 'pkg.group_diff' not in __salt__: - ret['comment'] = 'pkg.group_install not available for this platform' + if "pkg.group_diff" not in __salt__: + ret["comment"] = "pkg.group_install not available for this platform" return ret if skip is None: skip = [] else: if not isinstance(skip, list): - ret['comment'] = 'skip must be formatted as a list' + ret["comment"] = "skip must be formatted as a list" return ret for idx, item in enumerate(skip): if not isinstance(item, six.string_types): @@ -3156,88 +3294,89 @@ def group_installed(name, skip=None, include=None, **kwargs): include = [] else: if not isinstance(include, list): - ret['comment'] = 'include must be formatted as a list' + ret["comment"] = "include must be formatted as a list" return ret for idx, item in enumerate(include): if not isinstance(item, six.string_types): include[idx] = six.text_type(item) try: - diff = __salt__['pkg.group_diff'](name) + diff = __salt__["pkg.group_diff"](name) except CommandExecutionError as err: - ret['comment'] = ('An error was encountered while installing/updating ' - 'group \'{0}\': {1}.'.format(name, err)) + ret["comment"] = ( + "An error was encountered while installing/updating " + "group '{0}': {1}.".format(name, err) + ) return ret - mandatory = diff['mandatory']['installed'] + \ - diff['mandatory']['not installed'] + mandatory = diff["mandatory"]["installed"] + diff["mandatory"]["not installed"] invalid_skip = [x for x in mandatory if x in skip] if invalid_skip: - ret['comment'] = ( - 'The following mandatory packages cannot be skipped: {0}' - .format(', '.join(invalid_skip)) + ret[ + "comment" + ] = "The following mandatory packages cannot be skipped: {0}".format( + ", ".join(invalid_skip) ) return ret - targets = diff['mandatory']['not installed'] - targets.extend([x for x in diff['default']['not installed'] - if x not in skip]) + targets = diff["mandatory"]["not installed"] + targets.extend([x for x in diff["default"]["not installed"] if x not in skip]) targets.extend(include) if not targets: - ret['result'] = True - ret['comment'] = 'Group \'{0}\' is already installed'.format(name) + ret["result"] = True + ret["comment"] = "Group '{0}' is already installed".format(name) return ret - partially_installed = diff['mandatory']['installed'] \ - or diff['default']['installed'] \ - or diff['optional']['installed'] + partially_installed = ( + diff["mandatory"]["installed"] + or diff["default"]["installed"] + or diff["optional"]["installed"] + ) - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if partially_installed: - ret['comment'] = ( - 'Group \'{0}\' is partially installed and will be updated' - .format(name) - ) + ret[ + "comment" + ] = "Group '{0}' is partially installed and will be updated".format(name) else: - ret['comment'] = 'Group \'{0}\' will be installed'.format(name) + ret["comment"] = "Group '{0}' will be installed".format(name) return ret try: - ret['changes'] = __salt__['pkg.install'](pkgs=targets, **kwargs) + ret["changes"] = __salt__["pkg.install"](pkgs=targets, **kwargs) except CommandExecutionError as exc: - ret = {'name': name, 'result': False} + ret = {"name": name, "result": False} if exc.info: # Get information for state return from the exception. - ret['changes'] = exc.info.get('changes', {}) - ret['comment'] = exc.strerror_without_changes + ret["changes"] = exc.info.get("changes", {}) + ret["comment"] = exc.strerror_without_changes else: - ret['changes'] = {} - ret['comment'] = ('An error was encountered while ' - 'installing/updating group \'{0}\': {1}' - .format(name, exc)) + ret["changes"] = {} + ret["comment"] = ( + "An error was encountered while " + "installing/updating group '{0}': {1}".format(name, exc) + ) return ret - failed = [x for x in targets if x not in __salt__['pkg.list_pkgs']()] + failed = [x for x in targets if x not in __salt__["pkg.list_pkgs"]()] if failed: - ret['comment'] = ( - 'Failed to install the following packages: {0}' - .format(', '.join(failed)) + ret["comment"] = "Failed to install the following packages: {0}".format( + ", ".join(failed) ) return ret - ret['result'] = True - ret['comment'] = 'Group \'{0}\' was {1}'.format( - name, - 'updated' if partially_installed else 'installed' + ret["result"] = True + ret["comment"] = "Group '{0}' was {1}".format( + name, "updated" if partially_installed else "installed" ) return ret def mod_init(low): - ''' + """ Set a flag to tell the install functions to refresh the package database. This ensures that the package database is refreshed only once during a state run significantly improving the speed of package management @@ -3254,69 +3393,69 @@ def mod_init(low): .. seealso:: :py:func:`salt.modules.ebuild.ex_mod_init` - ''' + """ ret = True - if 'pkg.ex_mod_init' in __salt__: - ret = __salt__['pkg.ex_mod_init'](low) + if "pkg.ex_mod_init" in __salt__: + ret = __salt__["pkg.ex_mod_init"](low) - if low['fun'] == 'installed' or low['fun'] == 'latest': + if low["fun"] == "installed" or low["fun"] == "latest": salt.utils.pkg.write_rtag(__opts__) return ret return False def mod_aggregate(low, chunks, running): - ''' + """ The mod_aggregate function which looks up all packages in the available low chunks and merges them into a single pkgs ref in the present low data - ''' + """ pkgs = [] pkg_type = None agg_enabled = [ - 'installed', - 'latest', - 'removed', - 'purged', + "installed", + "latest", + "removed", + "purged", ] - if low.get('fun') not in agg_enabled: + if low.get("fun") not in agg_enabled: return low for chunk in chunks: - tag = __utils__['state.gen_tag'](chunk) + tag = __utils__["state.gen_tag"](chunk) if tag in running: # Already ran the pkg state, skip aggregation continue - if chunk.get('state') == 'pkg': - if '__agg__' in chunk: + if chunk.get("state") == "pkg": + if "__agg__" in chunk: continue # Check for the same function - if chunk.get('fun') != low.get('fun'): + if chunk.get("fun") != low.get("fun"): continue # Check for the same repo - if chunk.get('fromrepo') != low.get('fromrepo'): + if chunk.get("fromrepo") != low.get("fromrepo"): continue # Check first if 'sources' was passed so we don't aggregate pkgs # and sources together. - if 'sources' in chunk: + if "sources" in chunk: if pkg_type is None: - pkg_type = 'sources' - if pkg_type == 'sources': - pkgs.extend(chunk['sources']) - chunk['__agg__'] = True + pkg_type = "sources" + if pkg_type == "sources": + pkgs.extend(chunk["sources"]) + chunk["__agg__"] = True else: if pkg_type is None: - pkg_type = 'pkgs' - if pkg_type == 'pkgs': + pkg_type = "pkgs" + if pkg_type == "pkgs": # Pull out the pkg names! - if 'pkgs' in chunk: - pkgs.extend(chunk['pkgs']) - chunk['__agg__'] = True - elif 'name' in chunk: - version = chunk.pop('version', None) + if "pkgs" in chunk: + pkgs.extend(chunk["pkgs"]) + chunk["__agg__"] = True + elif "name" in chunk: + version = chunk.pop("version", None) if version is not None: - pkgs.append({chunk['name']: version}) + pkgs.append({chunk["name"]: version}) else: - pkgs.append(chunk['name']) - chunk['__agg__'] = True + pkgs.append(chunk["name"]) + chunk["__agg__"] = True if pkg_type is not None and pkgs: if pkg_type in low: low[pkg_type].extend(pkgs) @@ -3326,7 +3465,7 @@ def mod_aggregate(low, chunks, running): def mod_watch(name, **kwargs): - ''' + """ Install/reinstall a package based on a watch requisite .. note:: @@ -3334,15 +3473,19 @@ def mod_watch(name, **kwargs): :ref:`requisite <requisites>`. It should not be called directly. Parameters for this function should be set by the state being triggered. - ''' - sfun = kwargs.pop('sfun', None) - mapfun = {'purged': purged, - 'latest': latest, - 'removed': removed, - 'installed': installed} + """ + sfun = kwargs.pop("sfun", None) + mapfun = { + "purged": purged, + "latest": latest, + "removed": removed, + "installed": installed, + } if sfun in mapfun: return mapfun[sfun](name, **kwargs) - return {'name': name, - 'changes': {}, - 'comment': 'pkg.{0} does not work with the watch requisite'.format(sfun), - 'result': False} + return { + "name": name, + "changes": {}, + "comment": "pkg.{0} does not work with the watch requisite".format(sfun), + "result": False, + } diff --git a/salt/states/pkgbuild.py b/salt/states/pkgbuild.py index a87f2e5e7ef..97f09b27050 100644 --- a/salt/states/pkgbuild.py +++ b/salt/states/pkgbuild.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The pkgbuild state is the front of Salt package building backend. It automatically builds DEB and RPM packages from specified sources @@ -41,9 +41,10 @@ automatically builds DEB and RPM packages from specified sources - salt://pkg/salt/sources/SaltTesting-2015.5.8.tar.gz /tmp/pkg: pkgbuild.repo -''' +""" # Import python libs from __future__ import absolute_import, print_function + import errno import logging import os @@ -55,41 +56,37 @@ log = logging.getLogger(__name__) def _get_missing_results(results, dest_dir): - ''' + """ Return a list of the filenames specified in the ``results`` argument, which are not present in the dest_dir. - ''' + """ try: present = set(os.listdir(dest_dir)) except OSError as exc: if exc.errno == errno.ENOENT: - log.debug( - 'pkgbuild.built: dest_dir \'{0}\' does not exist' - .format(dest_dir) - ) + log.debug("pkgbuild.built: dest_dir '{0}' does not exist".format(dest_dir)) elif exc.errno == errno.EACCES: - log.error( - 'pkgbuilt.built: cannot access dest_dir \'{0}\'' - .format(dest_dir) - ) + log.error("pkgbuilt.built: cannot access dest_dir '{0}'".format(dest_dir)) present = set() return sorted(set(results).difference(present)) -def built(name, - runas, - dest_dir, - spec, - sources, - tgt, - template=None, - deps=None, - env=None, - results=None, - force=False, - saltenv='base', - log_dir='/var/log/salt/pkgbuild'): - ''' +def built( + name, + runas, + dest_dir, + spec, + sources, + tgt, + template=None, + deps=None, + env=None, + results=None, + force=False, + saltenv="base", + log_dir="/var/log/salt/pkgbuild", +): + """ Ensure that the named package is built and exists in the named directory name @@ -161,79 +158,69 @@ def built(name, under this directory. .. versionadded:: 2015.8.2 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} if not results: - ret['comment'] = '\'results\' argument is required' - ret['result'] = False + ret["comment"] = "'results' argument is required" + ret["result"] = False return ret if isinstance(results, six.string_types): - results = results.split(',') + results = results.split(",") needed = _get_missing_results(results, dest_dir) if not force and not needed: - ret['comment'] = 'All needed packages exist' + ret["comment"] = "All needed packages exist" return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if force: - ret['comment'] = 'Packages will be force-built' + ret["comment"] = "Packages will be force-built" else: - ret['comment'] = 'The following packages need to be built: ' - ret['comment'] += ', '.join(needed) + ret["comment"] = "The following packages need to be built: " + ret["comment"] += ", ".join(needed) return ret # Need the check for None here, if env is not provided then it falls back # to None and it is assumed that the environment is not being overridden. if env is not None and not isinstance(env, dict): - ret['comment'] = ('Invalidly-formatted \'env\' parameter. See ' - 'documentation.') - ret['result'] = False + ret["comment"] = "Invalidly-formatted 'env' parameter. See " "documentation." + ret["result"] = False return ret - func = 'pkgbuild.build' - if __grains__.get('os_family', False) not in ('RedHat', 'Suse'): + func = "pkgbuild.build" + if __grains__.get("os_family", False) not in ("RedHat", "Suse"): for res in results: - if res.endswith('.rpm'): - func = 'rpmbuild.build' + if res.endswith(".rpm"): + func = "rpmbuild.build" break - ret['changes'] = __salt__[func]( - runas, - tgt, - dest_dir, - spec, - sources, - deps, - env, - template, - saltenv, - log_dir) + ret["changes"] = __salt__[func]( + runas, tgt, dest_dir, spec, sources, deps, env, template, saltenv, log_dir + ) needed = _get_missing_results(results, dest_dir) if needed: - ret['comment'] = 'The following packages were not built: ' - ret['comment'] += ', '.join(needed) - ret['result'] = False + ret["comment"] = "The following packages were not built: " + ret["comment"] += ", ".join(needed) + ret["result"] = False else: - ret['comment'] = 'All needed packages were built' + ret["comment"] = "All needed packages were built" return ret -def repo(name, - keyid=None, - env=None, - use_passphrase=False, - gnupghome='/etc/salt/gpgkeys', - runas='builder', - timeout=15.0): - ''' +def repo( + name, + keyid=None, + env=None, + use_passphrase=False, + gnupghome="/etc/salt/gpgkeys", + runas="builder", + timeout=15.0, +): + """ Make a package repository and optionally sign it and packages present The name is directory to turn into a repo. This state is best used @@ -344,43 +331,39 @@ def repo(name, Timeout in seconds to wait for the prompt for inputting the passphrase. - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} - if __opts__['test'] is True: - ret['result'] = None - ret['comment'] = 'Package repo metadata at {0} will be refreshed'.format(name) + if __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "Package repo metadata at {0} will be refreshed".format(name) return ret # Need the check for None here, if env is not provided then it falls back # to None and it is assumed that the environment is not being overridden. if env is not None and not isinstance(env, dict): - ret['comment'] = ('Invalidly-formatted \'env\' parameter. See ' - 'documentation.') + ret["comment"] = "Invalidly-formatted 'env' parameter. See " "documentation." return ret - func = 'pkgbuild.make_repo' - if __grains__.get('os_family', False) not in ('RedHat', 'Suse'): + func = "pkgbuild.make_repo" + if __grains__.get("os_family", False) not in ("RedHat", "Suse"): for file in os.listdir(name): - if file.endswith('.rpm'): - func = 'rpmbuild.make_repo' + if file.endswith(".rpm"): + func = "rpmbuild.make_repo" break res = __salt__[func](name, keyid, env, use_passphrase, gnupghome, runas, timeout) - if res['retcode'] > 0: - ret['result'] = False + if res["retcode"] > 0: + ret["result"] = False else: - ret['changes'] = {'refresh': True} + ret["changes"] = {"refresh": True} - if res['stdout'] and res['stderr']: - ret['comment'] = "{0}\n{1}".format(res['stdout'], res['stderr']) - elif res['stdout']: - ret['comment'] = res['stdout'] - elif res['stderr']: - ret['comment'] = res['stderr'] + if res["stdout"] and res["stderr"]: + ret["comment"] = "{0}\n{1}".format(res["stdout"], res["stderr"]) + elif res["stdout"]: + ret["comment"] = res["stdout"] + elif res["stderr"]: + ret["comment"] = res["stderr"] return ret diff --git a/salt/states/pkgng.py b/salt/states/pkgng.py index 784d70242a3..86d3005487c 100644 --- a/salt/states/pkgng.py +++ b/salt/states/pkgng.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage package remote repo using FreeBSD pkgng ============================================== @@ -12,21 +12,16 @@ typically rather simple: pkgng_clients: pkgng.update_packaging_site: - name: "http://192.168.0.2" -''' +""" from __future__ import absolute_import, print_function, unicode_literals def update_packaging_site(name): - ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': '' - } + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - __salt__['pkgng.update_package_site'](name) -# cmd = 'diff /usr/local/etc/pkg.conf /usr/local/etc/pkg.conf.bak' -# res = __salt__['cmd.run'](cmd) -# ret['changes'] = res - ret['result'] = True + __salt__["pkgng.update_package_site"](name) + # cmd = 'diff /usr/local/etc/pkg.conf /usr/local/etc/pkg.conf.bak' + # res = __salt__['cmd.run'](cmd) + # ret['changes'] = res + ret["result"] = True return ret diff --git a/salt/states/pkgrepo.py b/salt/states/pkgrepo.py index f1ae3a0f6ff..4b7bee232db 100644 --- a/salt/states/pkgrepo.py +++ b/salt/states/pkgrepo.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of APT/DNF/YUM/Zypper package repos ============================================== @@ -80,33 +80,37 @@ package managers are APT, DNF, YUM and Zypper. Here is some example SLS: installed. To check if this package is installed, run ``dpkg -l python-apt``. ``python-apt`` will need to be manually installed if it is not present. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import sys -# Import salt libs -from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS import salt.utils.data import salt.utils.files import salt.utils.pkg.deb import salt.utils.pkg.rpm +# Import salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError + # Import 3rd-party libs from salt.ext import six +from salt.state import STATE_INTERNAL_KEYWORDS as _STATE_INTERNAL_KEYWORDS def __virtual__(): - ''' + """ Only load if modifying repos is available for this package type - ''' - return 'pkg.mod_repo' in __salt__ + """ + if "pkg.mod_repo" in __salt__: + return True + return (False, "pkg module could not be loaded") def managed(name, ppa=None, **kwargs): - ''' + """ This state manages software package repositories. Currently, :mod:`yum <salt.modules.yumpkg>`, :mod:`apt <salt.modules.aptpkg>`, and :mod:`zypper <salt.modules.zypper>` repositories are supported. @@ -304,237 +308,243 @@ def managed(name, ppa=None, **kwargs): Set this to a list of pkg.installed or pkg.latest to trigger the running of apt-get update prior to attempting to install these packages. Setting a require in the pkg state will not work for this. - ''' - if 'refresh_db' in kwargs: - kwargs['refresh'] = kwargs.pop('refresh_db') + """ + if "refresh_db" in kwargs: + kwargs["refresh"] = kwargs.pop("refresh_db") - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if 'pkg.get_repo' not in __salt__: - ret['result'] = False - ret['comment'] = 'Repo management not implemented on this platform' + if "pkg.get_repo" not in __salt__: + ret["result"] = False + ret["comment"] = "Repo management not implemented on this platform" return ret - if 'key_url' in kwargs and ('keyid' in kwargs or 'keyserver' in kwargs): - ret['result'] = False - ret['comment'] = 'You may not use both "keyid"/"keyserver" and ' \ - '"key_url" argument.' + if "key_url" in kwargs and ("keyid" in kwargs or "keyserver" in kwargs): + ret["result"] = False + ret["comment"] = ( + 'You may not use both "keyid"/"keyserver" and ' '"key_url" argument.' + ) - if 'key_text' in kwargs and ('keyid' in kwargs or 'keyserver' in kwargs): - ret['result'] = False - ret['comment'] = 'You may not use both "keyid"/"keyserver" and ' \ - '"key_text" argument.' - if 'key_text' in kwargs and ('key_url' in kwargs): - ret['result'] = False - ret['comment'] = 'You may not use both "key_url" and ' \ - '"key_text" argument.' + if "key_text" in kwargs and ("keyid" in kwargs or "keyserver" in kwargs): + ret["result"] = False + ret["comment"] = ( + 'You may not use both "keyid"/"keyserver" and ' '"key_text" argument.' + ) + if "key_text" in kwargs and ("key_url" in kwargs): + ret["result"] = False + ret["comment"] = 'You may not use both "key_url" and ' '"key_text" argument.' - if 'repo' in kwargs: - ret['result'] = False - ret['comment'] = ('\'repo\' is not a supported argument for this ' - 'state. The \'name\' argument is probably what was ' - 'intended.') + if "repo" in kwargs: + ret["result"] = False + ret["comment"] = ( + "'repo' is not a supported argument for this " + "state. The 'name' argument is probably what was " + "intended." + ) return ret - enabled = kwargs.pop('enabled', None) - disabled = kwargs.pop('disabled', None) + enabled = kwargs.pop("enabled", None) + disabled = kwargs.pop("disabled", None) if enabled is not None and disabled is not None: - ret['result'] = False - ret['comment'] = 'Only one of enabled/disabled is allowed' + ret["result"] = False + ret["comment"] = "Only one of enabled/disabled is allowed" return ret elif enabled is None and disabled is None: # If neither argument was passed we assume the repo will be enabled enabled = True repo = name - if __grains__['os'] in ('Ubuntu', 'Mint'): + if __grains__["os"] in ("Ubuntu", "Mint"): if ppa is not None: # overload the name/repo value for PPAs cleanly # this allows us to have one code-path for PPAs try: - repo = ':'.join(('ppa', ppa)) + repo = ":".join(("ppa", ppa)) except TypeError: - repo = ':'.join(('ppa', six.text_type(ppa))) + repo = ":".join(("ppa", six.text_type(ppa))) - kwargs['disabled'] = not salt.utils.data.is_true(enabled) \ - if enabled is not None \ + kwargs["disabled"] = ( + not salt.utils.data.is_true(enabled) + if enabled is not None else salt.utils.data.is_true(disabled) + ) - elif __grains__['os_family'] in ('RedHat', 'Suse'): - if 'humanname' in kwargs: - kwargs['name'] = kwargs.pop('humanname') - if 'name' not in kwargs: + elif __grains__["os_family"] in ("RedHat", "Suse"): + if "humanname" in kwargs: + kwargs["name"] = kwargs.pop("humanname") + if "name" not in kwargs: # Fall back to the repo name if humanname not provided - kwargs['name'] = repo + kwargs["name"] = repo - kwargs['enabled'] = not salt.utils.data.is_true(disabled) \ - if disabled is not None \ + kwargs["enabled"] = ( + not salt.utils.data.is_true(disabled) + if disabled is not None else salt.utils.data.is_true(enabled) + ) - elif __grains__['os_family'] in ('NILinuxRT', 'Poky'): + elif __grains__["os_family"] in ("NILinuxRT", "Poky"): # opkg is the pkg virtual - kwargs['enabled'] = not salt.utils.data.is_true(disabled) \ - if disabled is not None \ + kwargs["enabled"] = ( + not salt.utils.data.is_true(disabled) + if disabled is not None else salt.utils.data.is_true(enabled) + ) for kwarg in _STATE_INTERNAL_KEYWORDS: kwargs.pop(kwarg, None) try: - pre = __salt__['pkg.get_repo']( - repo, - ppa_auth=kwargs.get('ppa_auth', None) - ) + pre = __salt__["pkg.get_repo"](repo, ppa_auth=kwargs.get("ppa_auth", None)) except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = \ - 'Failed to examine repo \'{0}\': {1}'.format(name, exc) + ret["result"] = False + ret["comment"] = "Failed to examine repo '{0}': {1}".format(name, exc) return ret # This is because of how apt-sources works. This pushes distro logic # out of the state itself and into a module that it makes more sense # to use. Most package providers will simply return the data provided # it doesn't require any "specialized" data massaging. - if 'pkg.expand_repo_def' in __salt__: - sanitizedkwargs = __salt__['pkg.expand_repo_def'](repo=repo, **kwargs) + if "pkg.expand_repo_def" in __salt__: + sanitizedkwargs = __salt__["pkg.expand_repo_def"](repo=repo, **kwargs) else: sanitizedkwargs = kwargs - if __grains__['os_family'] == 'Debian': + if __grains__["os_family"] == "Debian": repo = salt.utils.pkg.deb.strip_uri(repo) if pre: - #22412: Remove file attribute in case same repo is set up multiple times but with different files - pre.pop('file', None) - sanitizedkwargs.pop('file', None) + # 22412: Remove file attribute in case same repo is set up multiple times but with different files + pre.pop("file", None) + sanitizedkwargs.pop("file", None) for kwarg in sanitizedkwargs: if kwarg not in pre: - if kwarg == 'enabled': + if kwarg == "enabled": # On a RedHat-based OS, 'enabled' is assumed to be true if # not explicitly set, so we don't need to update the repo # if it's desired to be enabled and the 'enabled' key is # missing from the repo definition - if __grains__['os_family'] == 'RedHat': + if __grains__["os_family"] == "RedHat": if not salt.utils.data.is_true(sanitizedkwargs[kwarg]): break else: break else: break - elif kwarg == 'comps': + elif kwarg == "comps": if sorted(sanitizedkwargs[kwarg]) != sorted(pre[kwarg]): break - elif kwarg == 'line' and __grains__['os_family'] == 'Debian': + elif kwarg == "line" and __grains__["os_family"] == "Debian": # split the line and sort everything after the URL sanitizedsplit = sanitizedkwargs[kwarg].split() sanitizedsplit[3:] = sorted(sanitizedsplit[3:]) - reposplit, _, pre_comments = \ - [x.strip() for x in pre[kwarg].partition('#')] + reposplit, _, pre_comments = [ + x.strip() for x in pre[kwarg].partition("#") + ] reposplit = reposplit.split() reposplit[3:] = sorted(reposplit[3:]) if sanitizedsplit != reposplit: break - if 'comments' in kwargs: - post_comments = \ - salt.utils.pkg.deb.combine_comments(kwargs['comments']) + if "comments" in kwargs: + post_comments = salt.utils.pkg.deb.combine_comments( + kwargs["comments"] + ) if pre_comments != post_comments: break - elif kwarg == 'comments' and __grains__['os_family'] == 'RedHat': + elif kwarg == "comments" and __grains__["os_family"] == "RedHat": precomments = salt.utils.pkg.rpm.combine_comments(pre[kwarg]) kwargcomments = salt.utils.pkg.rpm.combine_comments( - sanitizedkwargs[kwarg]) + sanitizedkwargs[kwarg] + ) if precomments != kwargcomments: break - elif kwarg == 'architectures' and sanitizedkwargs[kwarg]: + elif kwarg == "architectures" and sanitizedkwargs[kwarg]: if set(sanitizedkwargs[kwarg]) != set(pre[kwarg]): break else: - if __grains__['os_family'] in ('RedHat', 'Suse') \ - and any(isinstance(x, bool) for x in - (sanitizedkwargs[kwarg], pre[kwarg])): + if __grains__["os_family"] in ("RedHat", "Suse") and any( + isinstance(x, bool) for x in (sanitizedkwargs[kwarg], pre[kwarg]) + ): # This check disambiguates 1/0 from True/False - if salt.utils.data.is_true(sanitizedkwargs[kwarg]) != \ - salt.utils.data.is_true(pre[kwarg]): + if salt.utils.data.is_true( + sanitizedkwargs[kwarg] + ) != salt.utils.data.is_true(pre[kwarg]): break else: - if six.text_type(sanitizedkwargs[kwarg]) != six.text_type(pre[kwarg]): + if six.text_type(sanitizedkwargs[kwarg]) != six.text_type( + pre[kwarg] + ): break else: - ret['result'] = True - ret['comment'] = ('Package repo \'{0}\' already configured' - .format(name)) + ret["result"] = True + ret["comment"] = "Package repo '{0}' already configured".format(name) return ret - if __opts__['test']: - ret['comment'] = ( - 'Package repo \'{0}\' would be configured. This may cause pkg ' - 'states to behave differently than stated if this action is ' - 'repeated without test=True, due to the differences in the ' - 'configured repositories.'.format(name) + if __opts__["test"]: + ret["comment"] = ( + "Package repo '{0}' would be configured. This may cause pkg " + "states to behave differently than stated if this action is " + "repeated without test=True, due to the differences in the " + "configured repositories.".format(name) ) if pre: for kwarg in sanitizedkwargs: if sanitizedkwargs.get(kwarg) != pre.get(kwarg): - ret['changes'][kwarg] = {'new': sanitizedkwargs.get(kwarg), - 'old': pre.get(kwarg)} + ret["changes"][kwarg] = { + "new": sanitizedkwargs.get(kwarg), + "old": pre.get(kwarg), + } else: - ret['changes']['repo'] = name + ret["changes"]["repo"] = name return ret # empty file before configure - if kwargs.get('clean_file', False): - with salt.utils.files.fopen(kwargs['file'], 'w'): + if kwargs.get("clean_file", False): + with salt.utils.files.fopen(kwargs["file"], "w"): pass try: - if __grains__['os_family'] == 'Debian': - __salt__['pkg.mod_repo'](repo, saltenv=__env__, **kwargs) + if __grains__["os_family"] == "Debian": + __salt__["pkg.mod_repo"](repo, saltenv=__env__, **kwargs) else: - __salt__['pkg.mod_repo'](repo, **kwargs) + __salt__["pkg.mod_repo"](repo, **kwargs) except Exception as exc: # pylint: disable=broad-except # This is another way to pass information back from the mod_repo # function. - ret['result'] = False - ret['comment'] = \ - 'Failed to configure repo \'{0}\': {1}'.format(name, exc) + ret["result"] = False + ret["comment"] = "Failed to configure repo '{0}': {1}".format(name, exc) return ret try: - post = __salt__['pkg.get_repo']( - repo, - ppa_auth=kwargs.get('ppa_auth', None) - ) + post = __salt__["pkg.get_repo"](repo, ppa_auth=kwargs.get("ppa_auth", None)) if pre: for kwarg in sanitizedkwargs: if post.get(kwarg) != pre.get(kwarg): - ret['changes'][kwarg] = {'new': post.get(kwarg), - 'old': pre.get(kwarg)} + ret["changes"][kwarg] = { + "new": post.get(kwarg), + "old": pre.get(kwarg), + } else: - ret['changes'] = {'repo': repo} + ret["changes"] = {"repo": repo} - ret['result'] = True - ret['comment'] = 'Configured package repo \'{0}\''.format(name) + ret["result"] = True + ret["comment"] = "Configured package repo '{0}'".format(name) except Exception as exc: # pylint: disable=broad-except - ret['result'] = False - ret['comment'] = \ - 'Failed to confirm config of repo \'{0}\': {1}'.format(name, exc) + ret["result"] = False + ret["comment"] = "Failed to confirm config of repo '{0}': {1}".format(name, exc) # Clear cache of available packages, if present, since changes to the # repositories may change the packages that are available. - if ret['changes']: - sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('pkg._avail', None) + if ret["changes"]: + sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "pkg._avail", None + ) return ret def absent(name, **kwargs): - ''' + """ This function deletes the specified repo on the system, if it exists. It is essentially a wrapper around pkg.del_repo. @@ -580,74 +590,67 @@ def absent(name, **kwargs): .. note:: This option will be disregarded unless the ``ppa`` argument is present. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if 'ppa' in kwargs and __grains__['os'] in ('Ubuntu', 'Mint'): - name = kwargs.pop('ppa') - if not name.startswith('ppa:'): - name = 'ppa:' + name + if "ppa" in kwargs and __grains__["os"] in ("Ubuntu", "Mint"): + name = kwargs.pop("ppa") + if not name.startswith("ppa:"): + name = "ppa:" + name - remove_key = any(kwargs.get(x) is not None - for x in ('keyid', 'keyid_ppa')) - if remove_key and 'pkg.del_repo_key' not in __salt__: - ret['result'] = False - ret['comment'] = \ - 'Repo key management is not implemented for this platform' + remove_key = any(kwargs.get(x) is not None for x in ("keyid", "keyid_ppa")) + if remove_key and "pkg.del_repo_key" not in __salt__: + ret["result"] = False + ret["comment"] = "Repo key management is not implemented for this platform" return ret try: - repo = __salt__['pkg.get_repo']( - name, ppa_auth=kwargs.get('ppa_auth', None) - ) + repo = __salt__["pkg.get_repo"](name, ppa_auth=kwargs.get("ppa_auth", None)) except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = \ - 'Failed to configure repo \'{0}\': {1}'.format(name, exc) + ret["result"] = False + ret["comment"] = "Failed to configure repo '{0}': {1}".format(name, exc) return ret if not repo: - ret['comment'] = 'Package repo {0} is absent'.format(name) - ret['result'] = True + ret["comment"] = "Package repo {0} is absent".format(name) + ret["result"] = True return ret - if __opts__['test']: - ret['comment'] = ('Package repo \'{0}\' will be removed. This may ' - 'cause pkg states to behave differently than stated ' - 'if this action is repeated without test=True, due ' - 'to the differences in the configured repositories.' - .format(name)) + if __opts__["test"]: + ret["comment"] = ( + "Package repo '{0}' will be removed. This may " + "cause pkg states to behave differently than stated " + "if this action is repeated without test=True, due " + "to the differences in the configured repositories.".format(name) + ) return ret try: - __salt__['pkg.del_repo'](repo=name, **kwargs) + __salt__["pkg.del_repo"](repo=name, **kwargs) except (CommandExecutionError, SaltInvocationError) as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret - repos = __salt__['pkg.list_repos']() + repos = __salt__["pkg.list_repos"]() if name not in repos: - ret['changes']['repo'] = name - ret['comment'] = 'Removed repo {0}'.format(name) + ret["changes"]["repo"] = name + ret["comment"] = "Removed repo {0}".format(name) if not remove_key: - ret['result'] = True + ret["result"] = True else: try: - removed_keyid = __salt__['pkg.del_repo_key'](name, **kwargs) + removed_keyid = __salt__["pkg.del_repo_key"](name, **kwargs) except (CommandExecutionError, SaltInvocationError) as exc: - ret['result'] = False - ret['comment'] += ', but failed to remove key: {0}'.format(exc) + ret["result"] = False + ret["comment"] += ", but failed to remove key: {0}".format(exc) else: - ret['result'] = True - ret['changes']['keyid'] = removed_keyid - ret['comment'] += ', and keyid {0}'.format(removed_keyid) + ret["result"] = True + ret["changes"]["keyid"] = removed_keyid + ret["comment"] += ", and keyid {0}".format(removed_keyid) else: - ret['result'] = False - ret['comment'] = 'Failed to remove repo {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to remove repo {0}".format(name) return ret diff --git a/salt/states/portage_config.py b/salt/states/portage_config.py index 842b83226be..c0c70cd4d30 100644 --- a/salt/states/portage_config.py +++ b/salt/states/portage_config.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Portage package configuration on Gentoo ===================================================== @@ -11,23 +11,25 @@ A state module to manage Portage configuration on Gentoo portage_config.flags: - use: - openssl -''' +""" from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the portage_config module is available in __salt__ - ''' - return 'portage_config' if 'portage_config.get_missing_flags' in __salt__ else False + """ + if "portage_config.get_missing_flags" in __salt__: + return "portage_config" + return (False, "portage module could not be loaded") def mod_init(low): - ''' + """ Enforce a nice structure on the configuration files. - ''' + """ try: - __salt__['portage_config.enforce_nice_config']() + __salt__["portage_config.enforce_nice_config"]() except Exception: # pylint: disable=broad-except return False return True @@ -35,40 +37,44 @@ def mod_init(low): def _flags_helper(conf, atom, new_flags, test=False): try: - new_flags = __salt__['portage_config.get_missing_flags'](conf, atom, new_flags) + new_flags = __salt__["portage_config.get_missing_flags"](conf, atom, new_flags) except Exception: # pylint: disable=broad-except import traceback - return {'result': False, 'comment': traceback.format_exc()} + + return {"result": False, "comment": traceback.format_exc()} if new_flags: - old_flags = __salt__['portage_config.get_flags_from_package_conf'](conf, atom) + old_flags = __salt__["portage_config.get_flags_from_package_conf"](conf, atom) if not test: - __salt__['portage_config.append_to_package_conf'](conf, atom, new_flags) - return {'result': True, 'changes': {'old': old_flags, 'new': new_flags}} - return {'result': None} + __salt__["portage_config.append_to_package_conf"](conf, atom, new_flags) + return {"result": True, "changes": {"old": old_flags, "new": new_flags}} + return {"result": None} def _mask_helper(conf, atom, test=False): try: - is_present = __salt__['portage_config.is_present'](conf, atom) + is_present = __salt__["portage_config.is_present"](conf, atom) except Exception: # pylint: disable=broad-except import traceback - return {'result': False, 'comment': traceback.format_exc()} + + return {"result": False, "comment": traceback.format_exc()} if not is_present: if not test: - __salt__['portage_config.append_to_package_conf'](conf, string=atom) - return {'result': True} - return {'result': None} + __salt__["portage_config.append_to_package_conf"](conf, string=atom) + return {"result": True} + return {"result": None} -def flags(name, - use=None, - accept_keywords=None, - env=None, - license=None, - properties=None, - unmask=False, - mask=False): - ''' +def flags( + name, + use=None, + accept_keywords=None, + env=None, + license=None, + properties=None, + unmask=False, + mask=False, +): + """ Enforce the given flags on the given package or ``DEPEND`` atom. .. warning:: @@ -93,76 +99,75 @@ def flags(name, A boolean to unmask the package mask A boolean to mask the package - ''' - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": name, "result": True} if use: - result = _flags_helper('use', name, use, __opts__['test']) - if result['result']: - ret['changes']['use'] = result['changes'] - elif result['result'] is False: - ret['result'] = False - ret['comment'] = result['comment'] + result = _flags_helper("use", name, use, __opts__["test"]) + if result["result"]: + ret["changes"]["use"] = result["changes"] + elif result["result"] is False: + ret["result"] = False + ret["comment"] = result["comment"] return ret if accept_keywords: - result = _flags_helper('accept_keywords', name, accept_keywords, __opts__['test']) - if result['result']: - ret['changes']['accept_keywords'] = result['changes'] - elif result['result'] is False: - ret['result'] = False - ret['comment'] = result['comment'] + result = _flags_helper( + "accept_keywords", name, accept_keywords, __opts__["test"] + ) + if result["result"]: + ret["changes"]["accept_keywords"] = result["changes"] + elif result["result"] is False: + ret["result"] = False + ret["comment"] = result["comment"] return ret if env: - result = _flags_helper('env', name, env, __opts__['test']) - if result['result']: - ret['changes']['env'] = result['changes'] - elif result['result'] is False: - ret['result'] = False - ret['comment'] = result['comment'] + result = _flags_helper("env", name, env, __opts__["test"]) + if result["result"]: + ret["changes"]["env"] = result["changes"] + elif result["result"] is False: + ret["result"] = False + ret["comment"] = result["comment"] return ret if license: - result = _flags_helper('license', name, license, __opts__['test']) - if result['result']: - ret['changes']['license'] = result['changes'] - elif result['result'] is False: - ret['result'] = False - ret['comment'] = result['comment'] + result = _flags_helper("license", name, license, __opts__["test"]) + if result["result"]: + ret["changes"]["license"] = result["changes"] + elif result["result"] is False: + ret["result"] = False + ret["comment"] = result["comment"] return ret if properties: - result = _flags_helper('properties', name, properties, __opts__['test']) - if result['result']: - ret['changes']['properties'] = result['changes'] - elif result['result'] is False: - ret['result'] = False - ret['comment'] = result['comment'] + result = _flags_helper("properties", name, properties, __opts__["test"]) + if result["result"]: + ret["changes"]["properties"] = result["changes"] + elif result["result"] is False: + ret["result"] = False + ret["comment"] = result["comment"] return ret if mask: - result = _mask_helper('mask', name, __opts__['test']) - if result['result']: - ret['changes']['mask'] = 'masked' - elif result['result'] is False: - ret['result'] = False - ret['comment'] = result['comment'] + result = _mask_helper("mask", name, __opts__["test"]) + if result["result"]: + ret["changes"]["mask"] = "masked" + elif result["result"] is False: + ret["result"] = False + ret["comment"] = result["comment"] return ret if unmask: - result = _mask_helper('unmask', name, __opts__['test']) - if result['result']: - ret['changes']['unmask'] = 'unmasked' - elif result['result'] is False: - ret['result'] = False - ret['comment'] = result['comment'] + result = _mask_helper("unmask", name, __opts__["test"]) + if result["result"]: + ret["changes"]["unmask"] = "unmasked" + elif result["result"] is False: + ret["result"] = False + ret["comment"] = result["comment"] return ret - if __opts__['test'] and not ret['result']: - ret['result'] = None + if __opts__["test"] and not ret["result"]: + ret["result"] = None return ret diff --git a/salt/states/ports.py b/salt/states/ports.py index 45cb76206a2..6954777d49d 100644 --- a/salt/states/ports.py +++ b/salt/states/ports.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage software from FreeBSD ports .. versionadded:: 2014.1.0 @@ -13,36 +13,36 @@ Manage software from FreeBSD ports .. code-block:: bash salt -t 1200 '*' state.highstate -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import copy import logging + +# Needed by imported function _options_file_exists +import os # pylint: disable=W0611 import sys # Import salt libs import salt.utils.data -from salt.exceptions import SaltInvocationError, CommandExecutionError -from salt.modules.freebsdports import _normalize, _options_file_exists - -# Needed by imported function _options_file_exists -import os # pylint: disable=W0611 +from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext import six +from salt.modules.freebsdports import _normalize, _options_file_exists log = logging.getLogger(__name__) def __virtual__(): - if __grains__.get('os', '') == 'FreeBSD' and 'ports.install' in __salt__: - return 'ports' - return False + if __grains__.get("os", "") == "FreeBSD" and "ports.install" in __salt__: + return "ports" + return (False, "ports module could not be loaded") def _repack_options(options): - ''' + """ Repack the options data - ''' + """ return dict( [ (six.text_type(x), _normalize(y)) @@ -52,26 +52,25 @@ def _repack_options(options): def _get_option_list(options): - ''' + """ Returns the key/value pairs in the passed dict in a commaspace-delimited list in the format "key=value". - ''' - return ', '.join(['{0}={1}'.format(x, y) for x, y in six.iteritems(options)]) + """ + return ", ".join(["{0}={1}".format(x, y) for x, y in six.iteritems(options)]) def _build_option_string(options): - ''' + """ Common function to get a string to append to the end of the state comment - ''' + """ if options: - return ('with the following build options: {0}' - .format(_get_option_list(options))) + return "with the following build options: {0}".format(_get_option_list(options)) else: - return 'with the default build options' + return "with the default build options" def installed(name, options=None): - ''' + """ Verify that the desired port is installed, and that it was compiled with the desired options. @@ -92,95 +91,103 @@ def installed(name, options=None): ports.installed: - options: - IPV6: off - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': '{0} is already installed'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "{0} is already installed".format(name), + } try: - current_options = __salt__['ports.showconfig'](name, default=False, - dict_return=True) - default_options = __salt__['ports.showconfig'](name, default=True, - dict_return=True) + current_options = __salt__["ports.showconfig"]( + name, default=False, dict_return=True + ) + default_options = __salt__["ports.showconfig"]( + name, default=True, dict_return=True + ) # unpack the options from the top-level return dict if current_options: current_options = current_options[next(iter(current_options))] if default_options: default_options = default_options[next(iter(default_options))] except (SaltInvocationError, CommandExecutionError) as exc: - ret['result'] = False - ret['comment'] = ('Unable to get configuration for {0}. Port name may ' - 'be invalid, or ports tree may need to be updated. ' - 'Error message: {1}'.format(name, exc)) + ret["result"] = False + ret["comment"] = ( + "Unable to get configuration for {0}. Port name may " + "be invalid, or ports tree may need to be updated. " + "Error message: {1}".format(name, exc) + ) return ret options = _repack_options(options) if options is not None else {} desired_options = copy.deepcopy(default_options) desired_options.update(options) ports_pre = [ - x['origin'] for x in - six.itervalues(__salt__['pkg.list_pkgs'](with_origin=True)) + x["origin"] for x in six.itervalues(__salt__["pkg.list_pkgs"](with_origin=True)) ] if current_options == desired_options and name in ports_pre: # Port is installed as desired if options: - ret['comment'] += ' ' + _build_option_string(options) + ret["comment"] += " " + _build_option_string(options) return ret if not default_options: if options: - ret['result'] = False - ret['comment'] = ('{0} does not have any build options, yet ' - 'options were specified'.format(name)) + ret["result"] = False + ret["comment"] = ( + "{0} does not have any build options, yet " + "options were specified".format(name) + ) return ret else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = '{0} will be installed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "{0} will be installed".format(name) return ret else: bad_opts = [x for x in options if x not in default_options] if bad_opts: - ret['result'] = False - ret['comment'] = ('The following options are not available for ' - '{0}: {1}'.format(name, ', '.join(bad_opts))) + ret["result"] = False + ret["comment"] = ( + "The following options are not available for " + "{0}: {1}".format(name, ", ".join(bad_opts)) + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = '{0} will be installed '.format(name) - ret['comment'] += _build_option_string(options) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "{0} will be installed ".format(name) + ret["comment"] += _build_option_string(options) return ret if options: - if not __salt__['ports.config'](name, reset=True, **options): - ret['result'] = False - ret['comment'] = 'Unable to set options for {0}'.format(name) + if not __salt__["ports.config"](name, reset=True, **options): + ret["result"] = False + ret["comment"] = "Unable to set options for {0}".format(name) return ret else: - __salt__['ports.rmconfig'](name) + __salt__["ports.rmconfig"](name) if _options_file_exists(name): - ret['result'] = False - ret['comment'] = 'Unable to clear options for {0}'.format(name) + ret["result"] = False + ret["comment"] = "Unable to clear options for {0}".format(name) return ret - ret['changes'] = __salt__['ports.install'](name) + ret["changes"] = __salt__["ports.install"](name) ports_post = [ - x['origin'] for x in - six.itervalues(__salt__['pkg.list_pkgs'](with_origin=True)) + x["origin"] for x in six.itervalues(__salt__["pkg.list_pkgs"](with_origin=True)) ] - err = sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('ports.install_error', None) + err = sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "ports.install_error", None + ) if err or name not in ports_post: - ret['result'] = False - if ret['result']: - ret['comment'] = 'Successfully installed {0}'.format(name) + ret["result"] = False + if ret["result"]: + ret["comment"] = "Successfully installed {0}".format(name) if default_options: - ret['comment'] += ' ' + _build_option_string(options) + ret["comment"] += " " + _build_option_string(options) else: - ret['comment'] = 'Failed to install {0}'.format(name) + ret["comment"] = "Failed to install {0}".format(name) if err: - ret['comment'] += '. Error message:\n{0}'.format(err) + ret["comment"] += ". Error message:\n{0}".format(err) return ret diff --git a/salt/states/postgres_cluster.py b/salt/states/postgres_cluster.py index e0f01b34838..c9cf2d1661d 100644 --- a/salt/states/postgres_cluster.py +++ b/salt/states/postgres_cluster.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of PostgreSQL clusters ================================= @@ -12,26 +12,24 @@ Clusters can be set as either absent or present postgres_cluster.present: - name: 'main' - version: '9.3' -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the deb_postgres module is present - ''' - if 'postgres.cluster_exists' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.cluster_exists" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(version, - name, - port=None, - encoding=None, - locale=None, - datadir=None): - ''' +def present(version, name, port=None, encoding=None, locale=None, datadir=None): + """ Ensure that the named cluster is present with the specified properties. For more information about all of these options see man pg_createcluster(1) @@ -54,53 +52,55 @@ def present(version, Where the cluster is stored .. versionadded:: 2015.XX - ''' - msg = 'Cluster {0}/{1} is already present'.format(version, name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': msg} + """ + msg = "Cluster {0}/{1} is already present".format(version, name) + ret = {"name": name, "changes": {}, "result": True, "comment": msg} - if __salt__['postgres.cluster_exists'](version, name): + if __salt__["postgres.cluster_exists"](version, name): # check cluster config is correct - infos = __salt__['postgres.cluster_list'](verbose=True) - info = infos['{0}/{1}'.format(version, name)] + infos = __salt__["postgres.cluster_list"](verbose=True) + info = infos["{0}/{1}".format(version, name)] # TODO: check locale en encoding configs also - if any((port != info['port'] if port else False, - datadir != info['datadir'] if datadir else False,)): - ret['comment'] = 'Cluster {0}/{1} has wrong parameters ' \ - 'which couldn\'t be changed on fly.' \ - .format(version, name) - ret['result'] = False + if any( + ( + port != info["port"] if port else False, + datadir != info["datadir"] if datadir else False, + ) + ): + ret["comment"] = ( + "Cluster {0}/{1} has wrong parameters " + "which couldn't be changed on fly.".format(version, name) + ) + ret["result"] = False return ret # The cluster is not present, add it! - if __opts__.get('test'): - ret['result'] = None - msg = 'Cluster {0}/{1} is set to be created' - ret['comment'] = msg.format(version, name) + if __opts__.get("test"): + ret["result"] = None + msg = "Cluster {0}/{1} is set to be created" + ret["comment"] = msg.format(version, name) return ret - cluster = __salt__['postgres.cluster_create']( + cluster = __salt__["postgres.cluster_create"]( version=version, name=name, port=port, locale=locale, encoding=encoding, - datadir=datadir) + datadir=datadir, + ) if cluster: - msg = 'The cluster {0}/{1} has been created' - ret['comment'] = msg.format(version, name) - ret['changes']['{0}/{1}'.format(version, name)] = 'Present' + msg = "The cluster {0}/{1} has been created" + ret["comment"] = msg.format(version, name) + ret["changes"]["{0}/{1}".format(version, name)] = "Present" else: - msg = 'Failed to create cluster {0}/{1}' - ret['comment'] = msg.format(version, name) - ret['result'] = False + msg = "Failed to create cluster {0}/{1}" + ret["comment"] = msg.format(version, name) + ret["result"] = False return ret -def absent(version, - name): - ''' +def absent(version, name): + """ Ensure that the named cluster is absent version @@ -110,26 +110,25 @@ def absent(version, The name of the cluster to remove .. versionadded:: 2015.XX - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - #check if cluster exists and remove it - if __salt__['postgres.cluster_exists'](version, name): - if __opts__.get('test'): - ret['result'] = None - msg = 'Cluster {0}/{1} is set to be removed' - ret['comment'] = msg.format(version, name) + # check if cluster exists and remove it + if __salt__["postgres.cluster_exists"](version, name): + if __opts__.get("test"): + ret["result"] = None + msg = "Cluster {0}/{1} is set to be removed" + ret["comment"] = msg.format(version, name) return ret - if __salt__['postgres.cluster_remove'](version, name, True): - msg = 'Cluster {0}/{1} has been removed' - ret['comment'] = msg.format(version, name) - ret['changes'][name] = 'Absent' + if __salt__["postgres.cluster_remove"](version, name, True): + msg = "Cluster {0}/{1} has been removed" + ret["comment"] = msg.format(version, name) + ret["changes"][name] = "Absent" return ret # fallback - ret['comment'] = 'Cluster {0}/{1} is not present, so it cannot ' \ - 'be removed'.format(version, name) + ret["comment"] = ( + "Cluster {0}/{1} is not present, so it cannot " + "be removed".format(version, name) + ) return ret diff --git a/salt/states/postgres_database.py b/salt/states/postgres_database.py index 297de968a11..b1e0be7d5e4 100644 --- a/salt/states/postgres_database.py +++ b/salt/states/postgres_database.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of PostgreSQL databases ================================== @@ -10,34 +10,39 @@ Databases can be set as either absent or present frank: postgres_database.present -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the postgres module is present - ''' - if 'postgres.user_exists' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.user_exists" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(name, - tablespace=None, - encoding=None, - lc_collate=None, - lc_ctype=None, - owner=None, - owner_recurse=False, - template=None, - user=None, - maintenance_db=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def present( + name, + tablespace=None, + encoding=None, + lc_collate=None, + lc_ctype=None, + owner=None, + owner_recurse=False, + template=None, + user=None, + maintenance_db=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Ensure that the named database is present with the specified properties. For more information about all of these options see man createdb(1) @@ -81,94 +86,102 @@ def present(name, Database port if different from config or default .. versionadded:: 0.17.0 - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Database {0} is already present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Database {0} is already present".format(name), + } db_args = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } - dbs = __salt__['postgres.db_list'](**db_args) + dbs = __salt__["postgres.db_list"](**db_args) db_params = dbs.get(name, {}) - if name in dbs and all(( - db_params.get('Tablespace') == tablespace if tablespace else True, + if name in dbs and all( ( - db_params.get('Encoding').lower() == encoding.lower() - if encoding else True - ), - db_params.get('Collate') == lc_collate if lc_collate else True, - db_params.get('Ctype') == lc_ctype if lc_ctype else True, - db_params.get('Owner') == owner if owner else True - )): + db_params.get("Tablespace") == tablespace if tablespace else True, + ( + db_params.get("Encoding").lower() == encoding.lower() + if encoding + else True + ), + db_params.get("Collate") == lc_collate if lc_collate else True, + db_params.get("Ctype") == lc_ctype if lc_ctype else True, + db_params.get("Owner") == owner if owner else True, + ) + ): return ret - elif name in dbs and any(( - db_params.get('Encoding').lower() != encoding.lower() if encoding else False, - db_params.get('Collate') != lc_collate if lc_collate else False, - db_params.get('Ctype') != lc_ctype if lc_ctype else False - )): - ret['comment'] = 'Database {0} has wrong parameters ' \ - 'which couldn\'t be changed on fly.'.format(name) - ret['result'] = False + elif name in dbs and any( + ( + db_params.get("Encoding").lower() != encoding.lower() + if encoding + else False, + db_params.get("Collate") != lc_collate if lc_collate else False, + db_params.get("Ctype") != lc_ctype if lc_ctype else False, + ) + ): + ret["comment"] = ( + "Database {0} has wrong parameters " + "which couldn't be changed on fly.".format(name) + ) + ret["result"] = False return ret # The database is not present, make it! - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if name not in dbs: - ret['comment'] = 'Database {0} is set to be created'.format(name) + ret["comment"] = "Database {0} is set to be created".format(name) else: - ret['comment'] = 'Database {0} exists, but parameters ' \ - 'need to be changed'.format(name) + ret[ + "comment" + ] = "Database {0} exists, but parameters " "need to be changed".format(name) return ret - if ( - name not in dbs and __salt__['postgres.db_create']( - name, - tablespace=tablespace, - encoding=encoding, - lc_collate=lc_collate, - lc_ctype=lc_ctype, - owner=owner, - template=template, - **db_args) + if name not in dbs and __salt__["postgres.db_create"]( + name, + tablespace=tablespace, + encoding=encoding, + lc_collate=lc_collate, + lc_ctype=lc_ctype, + owner=owner, + template=template, + **db_args ): - ret['comment'] = 'The database {0} has been created'.format(name) - ret['changes'][name] = 'Present' - elif ( - name in dbs and __salt__['postgres.db_alter']( - name, - tablespace=tablespace, - owner=owner, owner_recurse=owner_recurse, **db_args) + ret["comment"] = "The database {0} has been created".format(name) + ret["changes"][name] = "Present" + elif name in dbs and __salt__["postgres.db_alter"]( + name, tablespace=tablespace, owner=owner, owner_recurse=owner_recurse, **db_args ): - ret['comment'] = ('Parameters for database {0} have been changed' - ).format(name) - ret['changes'][name] = 'Parameters changed' + ret["comment"] = ("Parameters for database {0} have been changed").format(name) + ret["changes"][name] = "Parameters changed" elif name in dbs: - ret['comment'] = ('Failed to change parameters for database {0}' - ).format(name) - ret['result'] = False + ret["comment"] = ("Failed to change parameters for database {0}").format(name) + ret["result"] = False else: - ret['comment'] = 'Failed to create database {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create database {0}".format(name) + ret["result"] = False return ret -def absent(name, - user=None, - maintenance_db=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def absent( + name, + user=None, + maintenance_db=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Ensure that the named database is absent name @@ -190,32 +203,30 @@ def absent(name, System user all operations should be performed on behalf of .. versionadded:: 0.17.0 - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} db_args = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } - #check if db exists and remove it - if __salt__['postgres.db_exists'](name, **db_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Database {0} is set to be removed'.format(name) + # check if db exists and remove it + if __salt__["postgres.db_exists"](name, **db_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Database {0} is set to be removed".format(name) return ret - if __salt__['postgres.db_remove'](name, **db_args): - ret['comment'] = 'Database {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["postgres.db_remove"](name, **db_args): + ret["comment"] = "Database {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret # fallback - ret['comment'] = 'Database {0} is not present, so it cannot ' \ - 'be removed'.format(name) + ret["comment"] = "Database {0} is not present, so it cannot " "be removed".format( + name + ) return ret diff --git a/salt/states/postgres_extension.py b/salt/states/postgres_extension.py index 56d8a679997..f1ce5edc50b 100644 --- a/salt/states/postgres_extension.py +++ b/salt/states/postgres_extension.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of PostgreSQL extensions =================================== @@ -12,8 +12,8 @@ A module used to install and manage PostgreSQL extensions. .. versionadded:: 2014.7.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -25,26 +25,31 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the postgres module is present - ''' - if 'postgres.create_extension' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.create_extension" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(name, - if_not_exists=None, - schema=None, - ext_version=None, - from_version=None, - user=None, - maintenance_db=None, - db_user=None, - db_password=None, - db_host=None, - db_port=None): - ''' +def present( + name, + if_not_exists=None, + schema=None, + ext_version=None, + from_version=None, + user=None, + maintenance_db=None, + db_user=None, + db_password=None, + db_host=None, + db_port=None, +): + """ Ensure that the named extension is present. .. note:: @@ -87,81 +92,82 @@ def present(name, db_port Database port if different from config or default - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Extension {0} is already present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Extension {0} is already present".format(name), + } db_args = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'user': db_user, - 'password': db_password, - 'host': db_host, - 'port': db_port, + "maintenance_db": maintenance_db, + "runas": user, + "user": db_user, + "password": db_password, + "host": db_host, + "port": db_port, } # check if extension exists - mode = 'create' - mtdata = __salt__['postgres.create_metadata']( - name, - schema=schema, - ext_version=ext_version, - **db_args) + mode = "create" + mtdata = __salt__["postgres.create_metadata"]( + name, schema=schema, ext_version=ext_version, **db_args + ) # The extension is not present, install it! toinstall = postgres._EXTENSION_NOT_INSTALLED in mtdata if toinstall: - mode = 'install' + mode = "install" toupgrade = False if postgres._EXTENSION_INSTALLED in mtdata: - for flag in [ - postgres._EXTENSION_TO_MOVE, - postgres._EXTENSION_TO_UPGRADE - ]: + for flag in [postgres._EXTENSION_TO_MOVE, postgres._EXTENSION_TO_UPGRADE]: if flag in mtdata: toupgrade = True - mode = 'upgrade' + mode = "upgrade" cret = None if toinstall or toupgrade: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None if mode: - ret['comment'] = 'Extension {0} is set to be {1}ed'.format( - name, mode).replace('eed', 'ed') + ret["comment"] = "Extension {0} is set to be {1}ed".format( + name, mode + ).replace("eed", "ed") return ret - cret = __salt__['postgres.create_extension']( + cret = __salt__["postgres.create_extension"]( name=name, if_not_exists=if_not_exists, schema=schema, ext_version=ext_version, from_version=from_version, - **db_args) + **db_args + ) if cret: - if mode.endswith('e'): - suffix = 'd' + if mode.endswith("e"): + suffix = "d" else: - suffix = 'ed' - ret['comment'] = 'The extension {0} has been {1}{2}'.format(name, mode, suffix) - ret['changes'][name] = '{0}{1}'.format(mode.capitalize(), suffix) + suffix = "ed" + ret["comment"] = "The extension {0} has been {1}{2}".format(name, mode, suffix) + ret["changes"][name] = "{0}{1}".format(mode.capitalize(), suffix) elif cret is not None: - ret['comment'] = 'Failed to {1} extension {0}'.format(name, mode) - ret['result'] = False + ret["comment"] = "Failed to {1} extension {0}".format(name, mode) + ret["result"] = False return ret -def absent(name, - if_exists=None, - restrict=None, - cascade=None, - user=None, - maintenance_db=None, - db_user=None, - db_password=None, - db_host=None, - db_port=None): - ''' +def absent( + name, + if_exists=None, + restrict=None, + cascade=None, + user=None, + maintenance_db=None, + db_user=None, + db_password=None, + db_host=None, + db_port=None, +): + """ Ensure that the named extension is absent. name @@ -193,40 +199,36 @@ def absent(name, db_port Database port if different from config or default - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} db_args = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } # check if extension exists and remove it - exists = __salt__['postgres.is_installed_extension'](name, **db_args) + exists = __salt__["postgres.is_installed_extension"](name, **db_args) if exists: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Extension {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Extension {0} is set to be removed".format(name) return ret - if __salt__['postgres.drop_extension'](name, - if_exists=if_exists, - restrict=restrict, - cascade=cascade, - **db_args): - ret['comment'] = 'Extension {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["postgres.drop_extension"]( + name, if_exists=if_exists, restrict=restrict, cascade=cascade, **db_args + ): + ret["comment"] = "Extension {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['result'] = False - ret['comment'] = 'Extension {0} failed to be removed'.format(name) + ret["result"] = False + ret["comment"] = "Extension {0} failed to be removed".format(name) return ret else: - ret['comment'] = 'Extension {0} is not present, so it cannot ' \ - 'be removed'.format(name) + ret[ + "comment" + ] = "Extension {0} is not present, so it cannot " "be removed".format(name) return ret diff --git a/salt/states/postgres_group.py b/salt/states/postgres_group.py index 77ab9424ac3..eab7e3f8fa8 100644 --- a/salt/states/postgres_group.py +++ b/salt/states/postgres_group.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of PostgreSQL groups (roles) ======================================= @@ -9,10 +9,8 @@ The postgres_group module is used to create and manage Postgres groups. frank: postgres_group.present -''' -from __future__ import absolute_import, unicode_literals, print_function - -# Import Python libs +""" +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import logging @@ -20,37 +18,44 @@ import logging # Salt imports from salt.modules import postgres +# Import Python libs + log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the postgres module is present - ''' - if 'postgres.group_create' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.group_create" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(name, - createdb=None, - createroles=None, - encrypted=None, - superuser=None, - inherit=None, - login=None, - replication=None, - password=None, - refresh_password=None, - groups=None, - user=None, - maintenance_db=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def present( + name, + createdb=None, + createroles=None, + encrypted=None, + superuser=None, + inherit=None, + login=None, + replication=None, + password=None, + refresh_password=None, + groups=None, + user=None, + maintenance_db=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Ensure that the named group is present with the specified privileges Please note that the user/group notion in postgresql is just abstract, we have roles, where users can be seen as roles with the ``LOGIN`` privilege @@ -120,73 +125,64 @@ def present(name, db_port Database port if different from config or default - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Group {0} is already present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Group {0} is already present".format(name), + } # default to encrypted passwords if encrypted is not False: encrypted = postgres._DEFAULT_PASSWORDS_ENCRYPTION # maybe encrypt if it's not already and necessary - password = postgres._maybe_encrypt_password(name, - password, - encrypted=encrypted) + password = postgres._maybe_encrypt_password(name, password, encrypted=encrypted) db_args = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } # check if group exists - mode = 'create' - group_attr = __salt__['postgres.role_get']( - name, return_password=not refresh_password, **db_args) + mode = "create" + group_attr = __salt__["postgres.role_get"]( + name, return_password=not refresh_password, **db_args + ) if group_attr is not None: - mode = 'update' + mode = "update" # The user is not present, make it! cret = None update = {} - if mode == 'update': - if ( - createdb is not None - and group_attr['can create databases'] != createdb + if mode == "update": + if createdb is not None and group_attr["can create databases"] != createdb: + update["createdb"] = createdb + if inherit is not None and group_attr["inherits privileges"] != inherit: + update["inherit"] = inherit + if login is not None and group_attr["can login"] != login: + update["login"] = login + if createroles is not None and group_attr["can create roles"] != createroles: + update["createroles"] = createroles + if replication is not None and group_attr["replication"] != replication: + update["replication"] = replication + if superuser is not None and group_attr["superuser"] != superuser: + update["superuser"] = superuser + if password is not None and ( + refresh_password or group_attr["password"] != password ): - update['createdb'] = createdb - if ( - inherit is not None - and group_attr['inherits privileges'] != inherit - ): - update['inherit'] = inherit - if login is not None and group_attr['can login'] != login: - update['login'] = login - if ( - createroles is not None - and group_attr['can create roles'] != createroles - ): - update['createroles'] = createroles - if ( - replication is not None - and group_attr['replication'] != replication - ): - update['replication'] = replication - if superuser is not None and group_attr['superuser'] != superuser: - update['superuser'] = superuser - if password is not None and (refresh_password or group_attr['password'] != password): - update['password'] = True - if mode == 'create' or (mode == 'update' and update): - if __opts__['test']: + update["password"] = True + if mode == "create" or (mode == "update" and update): + if __opts__["test"]: if update: - ret['changes'][name] = update - ret['result'] = None - ret['comment'] = 'Group {0} is set to be {1}d'.format(name, mode) + ret["changes"][name] = update + ret["result"] = None + ret["comment"] = "Group {0} is set to be {1}d".format(name, mode) return ret - cret = __salt__['postgres.group_{0}'.format(mode)]( + cret = __salt__["postgres.group_{0}".format(mode)]( groupname=name, createdb=createdb, createroles=createroles, @@ -197,30 +193,33 @@ def present(name, replication=replication, rolepassword=password, groups=groups, - **db_args) + **db_args + ) else: cret = None if cret: - ret['comment'] = 'The group {0} has been {1}d'.format(name, mode) + ret["comment"] = "The group {0} has been {1}d".format(name, mode) if update: - ret['changes'][name] = update + ret["changes"][name] = update elif cret is not None: - ret['comment'] = 'Failed to create group {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create group {0}".format(name) + ret["result"] = False else: - ret['result'] = True + ret["result"] = True return ret -def absent(name, - user=None, - maintenance_db=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def absent( + name, + user=None, + maintenance_db=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Ensure that the named group is absent name @@ -242,32 +241,30 @@ def absent(name, db_port Database port if different from config or default - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} db_args = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } # check if group exists and remove it - if __salt__['postgres.user_exists'](name, **db_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Group {0} is set to be removed'.format(name) + if __salt__["postgres.user_exists"](name, **db_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Group {0} is set to be removed".format(name) return ret - if __salt__['postgres.group_remove'](name, **db_args): - ret['comment'] = 'Group {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["postgres.group_remove"](name, **db_args): + ret["comment"] = "Group {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['comment'] = 'Group {0} is not present, so it cannot ' \ - 'be removed'.format(name) + ret["comment"] = "Group {0} is not present, so it cannot " "be removed".format( + name + ) return ret diff --git a/salt/states/postgres_initdb.py b/salt/states/postgres_initdb.py index d91aec8a506..302157217a9 100644 --- a/salt/states/postgres_initdb.py +++ b/salt/states/postgres_initdb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Initialization of PostgreSQL data directory =========================================== @@ -20,29 +20,34 @@ data directory. - locale: C - runas: postgres -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the postgres module is present - ''' - if 'postgres.datadir_init' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.datadir_init" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(name, - user=None, - password=None, - auth='password', - encoding='UTF8', - locale=None, - runas=None, - waldir=None, - checksums=False): - ''' +def present( + name, + user=None, + password=None, + auth="password", + encoding="UTF8", + locale=None, + runas=None, + waldir=None, + checksums=False, +): + """ Initialize the PostgreSQL data directory name @@ -78,41 +83,35 @@ def present(name, runas The system user the operation should be performed on behalf of - ''' - _cmt = 'Postgres data directory {0} is already present'.format(name) - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': _cmt} + """ + _cmt = "Postgres data directory {0} is already present".format(name) + ret = {"name": name, "changes": {}, "result": True, "comment": _cmt} - if not __salt__['postgres.datadir_exists'](name=name): - if __opts__['test']: - ret['result'] = None - _cmt = 'Postgres data directory {0} is set to be initialized'\ - .format(name) - ret['comment'] = _cmt + if not __salt__["postgres.datadir_exists"](name=name): + if __opts__["test"]: + ret["result"] = None + _cmt = "Postgres data directory {0} is set to be initialized".format(name) + ret["comment"] = _cmt return ret kwargs = dict( - user=user, - password=password, - auth=auth, - encoding=encoding, - locale=locale, - waldir=waldir, - checksums=checksums, - runas=runas) + user=user, + password=password, + auth=auth, + encoding=encoding, + locale=locale, + waldir=waldir, + checksums=checksums, + runas=runas, + ) - if __salt__['postgres.datadir_init'](name, **kwargs): - _cmt = 'Postgres data directory {0} has been initialized'\ - .format(name) - ret['comment'] = _cmt - ret['changes'][name] = 'Present' + if __salt__["postgres.datadir_init"](name, **kwargs): + _cmt = "Postgres data directory {0} has been initialized".format(name) + ret["comment"] = _cmt + ret["changes"][name] = "Present" else: - _cmt = 'Postgres data directory {0} initialization failed'\ - .format(name) - ret['result'] = False - ret['comment'] = _cmt + _cmt = "Postgres data directory {0} initialization failed".format(name) + ret["result"] = False + ret["comment"] = _cmt return ret diff --git a/salt/states/postgres_language.py b/salt/states/postgres_language.py index 054eef94fd4..7b8d94ae5df 100644 --- a/salt/states/postgres_language.py +++ b/salt/states/postgres_language.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of PostgreSQL languages ================================== @@ -20,27 +20,32 @@ Languages can be set as either absent or present postgres_language.absent: - maintenance_db: testdb -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the postgres module is present - ''' - if 'postgres.language_create' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.language_create" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(name, - maintenance_db, - user=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def present( + name, + maintenance_db, + user=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Ensure that a named language is present in the specified database. @@ -64,51 +69,50 @@ def present(name, db_port Database port if different from config or default - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Language {0} is already installed'.format(name) + "name": name, + "changes": {}, + "result": True, + "comment": "Language {0} is already installed".format(name), } dbargs = { - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } - languages = __salt__['postgres.language_list'](maintenance_db, **dbargs) + languages = __salt__["postgres.language_list"](maintenance_db, **dbargs) if name not in languages: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Language {0} is set to be installed'.format( - name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Language {0} is set to be installed".format(name) return ret - if __salt__['postgres.language_create'](name, maintenance_db, - **dbargs): - ret['comment'] = 'Language {0} has been installed'.format(name) - ret['changes'][name] = 'Present' + if __salt__["postgres.language_create"](name, maintenance_db, **dbargs): + ret["comment"] = "Language {0} has been installed".format(name) + ret["changes"][name] = "Present" else: - ret['comment'] = 'Failed to install language {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to install language {0}".format(name) + ret["result"] = False return ret def absent( - name, - maintenance_db, - user=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' + name, + maintenance_db, + user=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Ensure that a named language is absent in the specified database. @@ -132,35 +136,31 @@ def absent( db_port Database port if different from config or default - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} dbargs = { - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } - if __salt__['postgres.language_exists'](name, maintenance_db, **dbargs): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Language {0} is set to be removed'.format(name) + if __salt__["postgres.language_exists"](name, maintenance_db, **dbargs): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Language {0} is set to be removed".format(name) return ret - if __salt__['postgres.language_remove'](name, **dbargs): - ret['comment'] = 'Language {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["postgres.language_remove"](name, **dbargs): + ret["comment"] = "Language {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['comment'] = 'Failed to remove language {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to remove language {0}".format(name) + ret["result"] = False - ret['comment'] = 'Language {0} is not present ' \ - 'so it cannot be removed'.format(name) + ret["comment"] = "Language {0} is not present " "so it cannot be removed".format( + name + ) return ret diff --git a/salt/states/postgres_privileges.py b/salt/states/postgres_privileges.py index c39e0bea93b..a27bc121334 100644 --- a/salt/states/postgres_privileges.py +++ b/salt/states/postgres_privileges.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of PostgreSQL Privileges =================================== @@ -63,32 +63,37 @@ Setting the grant option is supported as well. - object_name: admins - object_type: group - maintenance_db: testdb -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the postgres module is present - ''' - if 'postgres.privileges_grant' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.privileges_grant" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(name, - object_name, - object_type, - privileges=None, - grant_option=None, - prepend='public', - maintenance_db=None, - user=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def present( + name, + object_name, + object_type, + privileges=None, + grant_option=None, + prepend="public", + maintenance_db=None, + user=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Grant the requested privilege(s) on the specified object to a role name @@ -156,63 +161,70 @@ def present(name, db_port Database port if different from config or default - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'The requested privilege(s) are already set' + "name": name, + "changes": {}, + "result": True, + "comment": "The requested privilege(s) are already set", } - privileges = ','.join(privileges) if privileges else None + privileges = ",".join(privileges) if privileges else None kwargs = { - 'privileges': privileges, - 'grant_option': grant_option, - 'prepend': prepend, - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "privileges": privileges, + "grant_option": grant_option, + "prepend": prepend, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } - if not __salt__['postgres.has_privileges']( - name, object_name, object_type, **kwargs): - _privs = object_name if object_type == 'group' else privileges + if not __salt__["postgres.has_privileges"]( + name, object_name, object_type, **kwargs + ): + _privs = object_name if object_type == "group" else privileges - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('The privilege(s): {0} are' - ' set to be granted to {1}').format(_privs, name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "The privilege(s): {0} are" " set to be granted to {1}" + ).format(_privs, name) return ret - if __salt__['postgres.privileges_grant']( - name, object_name, object_type, **kwargs): - ret['comment'] = ('The privilege(s): {0} have ' - 'been granted to {1}').format(_privs, name) - ret['changes'][name] = 'Present' + if __salt__["postgres.privileges_grant"]( + name, object_name, object_type, **kwargs + ): + ret["comment"] = ( + "The privilege(s): {0} have " "been granted to {1}" + ).format(_privs, name) + ret["changes"][name] = "Present" else: - ret['comment'] = ('Failed to grant privilege(s):' - ' {0} to {1}').format(_privs, name) - ret['result'] = False + ret["comment"] = ("Failed to grant privilege(s):" " {0} to {1}").format( + _privs, name + ) + ret["result"] = False return ret -def absent(name, - object_name, - object_type, - privileges=None, - prepend='public', - maintenance_db=None, - user=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def absent( + name, + object_name, + object_type, + privileges=None, + prepend="public", + maintenance_db=None, + user=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Revoke the requested privilege(s) on the specificed object(s) name @@ -275,46 +287,48 @@ def absent(name, db_port Database port if different from config or default - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': ('The requested privilege(s) are ' - 'not set so cannot be revoked') + "name": name, + "changes": {}, + "result": True, + "comment": ("The requested privilege(s) are " "not set so cannot be revoked"), } - privileges = ','.join(privileges) if privileges else None + privileges = ",".join(privileges) if privileges else None kwargs = { - 'privileges': privileges, - 'prepend': prepend, - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "privileges": privileges, + "prepend": prepend, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } - if __salt__['postgres.has_privileges']( - name, object_name, object_type, **kwargs): - _privs = object_name if object_type == 'group' else privileges + if __salt__["postgres.has_privileges"](name, object_name, object_type, **kwargs): + _privs = object_name if object_type == "group" else privileges - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('The privilege(s): {0} are' - ' set to be revoked from {1}').format(_privs, name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "The privilege(s): {0} are" " set to be revoked from {1}" + ).format(_privs, name) return ret - if __salt__['postgres.privileges_revoke']( - name, object_name, object_type, **kwargs): - ret['comment'] = ('The privilege(s): {0} have ' - 'been revoked from {1}').format(_privs, name) - ret['changes'][name] = 'Absent' + if __salt__["postgres.privileges_revoke"]( + name, object_name, object_type, **kwargs + ): + ret["comment"] = ( + "The privilege(s): {0} have " "been revoked from {1}" + ).format(_privs, name) + ret["changes"][name] = "Absent" else: - ret['comment'] = ('Failed to revoke privilege(s):' - ' {0} from {1}').format(_privs, name) - ret['result'] = False + ret["comment"] = ("Failed to revoke privilege(s):" " {0} from {1}").format( + _privs, name + ) + ret["result"] = False return ret diff --git a/salt/states/postgres_schema.py b/salt/states/postgres_schema.py index 984f9f8cc5e..493b0fb9f38 100644 --- a/salt/states/postgres_schema.py +++ b/salt/states/postgres_schema.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of PostgreSQL schemas ================================ @@ -9,30 +9,38 @@ The postgres_schemas module is used to create and manage Postgres schemas. public: postgres_schema.present 'dbname' 'name' -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging - log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the postgres module is present - ''' - if 'postgres.schema_exists' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.schema_exists" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(dbname, name, - owner=None, user=None, - db_user=None, db_password=None, - db_host=None, db_port=None): - ''' +def present( + dbname, + name, + owner=None, + user=None, + db_user=None, + db_password=None, + db_host=None, + db_port=None, +): + """ Ensure that the named schema is present in the database. dbname @@ -55,61 +63,62 @@ def present(dbname, name, db_port Database port if different from config or default - ''' - ret = {'dbname': dbname, - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Schema {0} is already present in ' - 'database {1}'.format(name, dbname)} + """ + ret = { + "dbname": dbname, + "name": name, + "changes": {}, + "result": True, + "comment": "Schema {0} is already present in " + "database {1}".format(name, dbname), + } db_args = { - 'db_user': db_user, - 'db_password': db_password, - 'db_host': db_host, - 'db_port': db_port, - 'user': user + "db_user": db_user, + "db_password": db_password, + "db_host": db_host, + "db_port": db_port, + "user": user, } # check if schema exists - schema_attr = __salt__['postgres.schema_get'](dbname, name, **db_args) + schema_attr = __salt__["postgres.schema_get"](dbname, name, **db_args) cret = None # The schema is not present, make it! if schema_attr is None: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Schema {0} is set to be created' \ - ' in database {1}.'.format(name, dbname) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Schema {0} is set to be created" + " in database {1}.".format(name, dbname) + ) return ret - cret = __salt__['postgres.schema_create'](dbname, - name, - owner=owner, - **db_args) + cret = __salt__["postgres.schema_create"](dbname, name, owner=owner, **db_args) else: - msg = 'Schema {0} already exists in database {1}' + msg = "Schema {0} already exists in database {1}" cret = None if cret: - msg = 'Schema {0} has been created in database {1}' - ret['result'] = True - ret['changes'][name] = 'Present' + msg = "Schema {0} has been created in database {1}" + ret["result"] = True + ret["changes"][name] = "Present" elif cret is not None: - msg = 'Failed to create schema {0} in database {1}' - ret['result'] = False + msg = "Failed to create schema {0} in database {1}" + ret["result"] = False else: - msg = 'Schema {0} already exists in database {1}' - ret['result'] = True + msg = "Schema {0} already exists in database {1}" + ret["result"] = True - ret['comment'] = msg.format(name, dbname) + ret["comment"] = msg.format(name, dbname) return ret -def absent(dbname, name, user=None, - db_user=None, db_password=None, - db_host=None, db_port=None): - ''' +def absent( + dbname, name, user=None, db_user=None, db_password=None, db_host=None, db_port=None +): + """ Ensure that the named schema is absent. dbname @@ -132,39 +141,40 @@ def absent(dbname, name, user=None, db_port Database port if different from config or default - ''' - ret = {'name': name, - 'dbname': dbname, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "dbname": dbname, "changes": {}, "result": True, "comment": ""} db_args = { - 'db_user': db_user, - 'db_password': db_password, - 'db_host': db_host, - 'db_port': db_port, - 'user': user - } + "db_user": db_user, + "db_password": db_password, + "db_host": db_host, + "db_port": db_port, + "user": user, + } # check if schema exists and remove it - if __salt__['postgres.schema_exists'](dbname, name, **db_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Schema {0} is set to be removed' \ - ' from database {1}'.format(name, dbname) + if __salt__["postgres.schema_exists"](dbname, name, **db_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "Schema {0} is set to be removed" + " from database {1}".format(name, dbname) + ) return ret - elif __salt__['postgres.schema_remove'](dbname, name, **db_args): - ret['comment'] = 'Schema {0} has been removed' \ - ' from database {1}'.format(name, dbname) - ret['changes'][name] = 'Absent' + elif __salt__["postgres.schema_remove"](dbname, name, **db_args): + ret["comment"] = "Schema {0} has been removed" " from database {1}".format( + name, dbname + ) + ret["changes"][name] = "Absent" return ret else: - ret['result'] = False - ret['comment'] = 'Schema {0} failed to be removed'.format(name) + ret["result"] = False + ret["comment"] = "Schema {0} failed to be removed".format(name) return ret else: - ret['comment'] = 'Schema {0} is not present in database {1},' \ - ' so it cannot be removed'.format(name, dbname) + ret["comment"] = ( + "Schema {0} is not present in database {1}," + " so it cannot be removed".format(name, dbname) + ) return ret diff --git a/salt/states/postgres_tablespace.py b/salt/states/postgres_tablespace.py index 190d99180ad..bca30441fb8 100644 --- a/salt/states/postgres_tablespace.py +++ b/salt/states/postgres_tablespace.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of PostgreSQL tablespace =================================== @@ -14,10 +14,10 @@ A module used to create and manage PostgreSQL tablespaces. .. versionadded:: 2015.8.0 -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.utils.dictupdate as dictupdate @@ -27,25 +27,30 @@ from salt.ext.six import iteritems def __virtual__(): - ''' + """ Only load if the postgres module is present and is new enough (has ts funcs) - ''' - if 'postgres.tablespace_exists' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.tablespace_exists" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(name, - directory, - options=None, - owner=None, - user=None, - maintenance_db=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def present( + name, + directory, + options=None, + owner=None, + user=None, + maintenance_db=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Ensure that the named tablespace is present with the specified properties. For more information about all of these options run ``man 7 create_tablespace``. @@ -91,49 +96,57 @@ def present(name, db_port Database port if different from config or default - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Tablespace {0} is already present'.format(name)} - dbargs = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Tablespace {0} is already present".format(name), } - tblspaces = __salt__['postgres.tablespace_list'](**dbargs) + dbargs = { + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, + } + tblspaces = __salt__["postgres.tablespace_list"](**dbargs) if name not in tblspaces: # not there, create it - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Tablespace {0} is set to be created'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Tablespace {0} is set to be created".format(name) return ret - if __salt__['postgres.tablespace_create'](name, directory, options, - owner, **dbargs): - ret['comment'] = 'The tablespace {0} has been created'.format(name) - ret['changes'][name] = 'Present' + if __salt__["postgres.tablespace_create"]( + name, directory, options, owner, **dbargs + ): + ret["comment"] = "The tablespace {0} has been created".format(name) + ret["changes"][name] = "Present" return ret # already exists, make sure it's got the right config - if tblspaces[name]['Location'] != directory and not __opts__['test']: - ret['comment'] = """Tablespace {0} is not at the right location. This is + if tblspaces[name]["Location"] != directory and not __opts__["test"]: + ret[ + "comment" + ] = """Tablespace {0} is not at the right location. This is unfixable without dropping and recreating the tablespace.""".format( - name) - ret['result'] = False + name + ) + ret["result"] = False return ret - if owner and not tblspaces[name]['Owner'] == owner: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Tablespace {0} owner to be altered'.format(name) - if (__salt__['postgres.tablespace_alter'](name, new_owner=owner) - and not __opts__['test']): - ret['comment'] = 'Tablespace {0} owner changed'.format(name) - ret['changes'][name] = {'owner': owner} - ret['result'] = True + if owner and not tblspaces[name]["Owner"] == owner: + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Tablespace {0} owner to be altered".format(name) + if ( + __salt__["postgres.tablespace_alter"](name, new_owner=owner) + and not __opts__["test"] + ): + ret["comment"] = "Tablespace {0} owner changed".format(name) + ret["changes"][name] = {"owner": owner} + ret["result"] = True if options: # options comes from postgres as a sort of json(ish) string, but it @@ -143,29 +156,34 @@ def present(name, # TODO remove options that exist if possible for k, v in iteritems(options): # if 'seq_page_cost=1.1' not in '{seq_page_cost=1.1,...}' - if '{0}={1}'.format(k, v) not in tblspaces[name]['Opts']: - if __opts__['test']: - ret['result'] = None - ret['comment'] = """Tablespace {0} options to be - altered""".format(name) + if "{0}={1}".format(k, v) not in tblspaces[name]["Opts"]: + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = """Tablespace {0} options to be + altered""".format( + name + ) break # we know it's going to be altered, no reason to cont - if __salt__['postgres.tablespace_alter'](name, - set_option={k: v}): - ret['comment'] = 'Tablespace {0} opts changed'.format(name) - dictupdate.update(ret['changes'], {name: {'options': {k: v}}}) - ret['result'] = True + if __salt__["postgres.tablespace_alter"](name, set_option={k: v}): + ret["comment"] = "Tablespace {0} opts changed".format(name) + dictupdate.update(ret["changes"], {name: {"options": {k: v}}}) + ret["result"] = True return ret -def absent(name, - user=None, - maintenance_db=None, - db_user=None, - db_password=None, - db_host=None, - db_port=None): - ''' +def absent( + name, + user=None, + maintenance_db=None, + db_user=None, + db_password=None, + db_host=None, + db_port=None, +): + """ Ensure that the named tablespace is absent. name @@ -188,32 +206,30 @@ def absent(name, db_port Database port if different from config or default - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} db_args = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } - #check if tablespace exists and remove it - if __salt__['postgres.tablespace_exists'](name, **db_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Tablespace {0} is set to be removed'.format(name) + # check if tablespace exists and remove it + if __salt__["postgres.tablespace_exists"](name, **db_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Tablespace {0} is set to be removed".format(name) return ret - if __salt__['postgres.tablespace_remove'](name, **db_args): - ret['comment'] = 'Tablespace {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["postgres.tablespace_remove"](name, **db_args): + ret["comment"] = "Tablespace {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret # fallback - ret['comment'] = 'Tablespace {0} is not present, so it cannot ' \ - 'be removed'.format(name) + ret["comment"] = "Tablespace {0} is not present, so it cannot " "be removed".format( + name + ) return ret diff --git a/salt/states/postgres_user.py b/salt/states/postgres_user.py index f1eadab30d7..b560c538338 100644 --- a/salt/states/postgres_user.py +++ b/salt/states/postgres_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of PostgreSQL users (roles) ====================================== @@ -9,51 +9,58 @@ The postgres_users module is used to create and manage Postgres users. frank: postgres_user.present -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import datetime import logging -# Import salt libs +from salt.ext import six # Salt imports from salt.modules import postgres -from salt.ext import six + +# Import salt libs + log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the postgres module is present - ''' - if 'postgres.user_exists' not in __salt__: - return (False, 'Unable to load postgres module. Make sure `postgres.bins_dir` is set.') + """ + if "postgres.user_exists" not in __salt__: + return ( + False, + "Unable to load postgres module. Make sure `postgres.bins_dir` is set.", + ) return True -def present(name, - createdb=None, - createroles=None, - encrypted=None, - superuser=None, - replication=None, - inherit=None, - login=None, - password=None, - default_password=None, - refresh_password=None, - valid_until=None, - groups=None, - user=None, - maintenance_db=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def present( + name, + createdb=None, + createroles=None, + encrypted=None, + superuser=None, + replication=None, + inherit=None, + login=None, + password=None, + default_password=None, + refresh_password=None, + valid_until=None, + groups=None, + user=None, + maintenance_db=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Ensure that the named user is present with the specified privileges Please note that the user/group notion in postgresql is just abstract, we have roles, where users can be seens as roles with the LOGIN privilege @@ -132,103 +139,97 @@ def present(name, db_port Postgres database port, if different from config or default. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User {0} is already present'.format(name)} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "User {0} is already present".format(name), + } # default to encrypted passwords if encrypted is not False: encrypted = postgres._DEFAULT_PASSWORDS_ENCRYPTION # maybe encrypt if it's not already and necessary - password = postgres._maybe_encrypt_password(name, - password, - encrypted=encrypted) + password = postgres._maybe_encrypt_password(name, password, encrypted=encrypted) if default_password is not None: - default_password = postgres._maybe_encrypt_password(name, - default_password, - encrypted=encrypted) + default_password = postgres._maybe_encrypt_password( + name, default_password, encrypted=encrypted + ) db_args = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } # check if user exists - mode = 'create' - user_attr = __salt__['postgres.role_get']( - name, return_password=not refresh_password, **db_args) + mode = "create" + user_attr = __salt__["postgres.role_get"]( + name, return_password=not refresh_password, **db_args + ) if user_attr is not None: - mode = 'update' + mode = "update" cret = None update = {} - if mode == 'update': - user_groups = user_attr.get('groups', []) - if ( - createdb is not None - and user_attr['can create databases'] != createdb + if mode == "update": + user_groups = user_attr.get("groups", []) + if createdb is not None and user_attr["can create databases"] != createdb: + update["createdb"] = createdb + if inherit is not None and user_attr["inherits privileges"] != inherit: + update["inherit"] = inherit + if login is not None and user_attr["can login"] != login: + update["login"] = login + if createroles is not None and user_attr["can create roles"] != createroles: + update["createroles"] = createroles + if replication is not None and user_attr["replication"] != replication: + update["replication"] = replication + if superuser is not None and user_attr["superuser"] != superuser: + update["superuser"] = superuser + if password is not None and ( + refresh_password or user_attr["password"] != password ): - update['createdb'] = createdb - if ( - inherit is not None - and user_attr['inherits privileges'] != inherit - ): - update['inherit'] = inherit - if login is not None and user_attr['can login'] != login: - update['login'] = login - if ( - createroles is not None - and user_attr['can create roles'] != createroles - ): - update['createroles'] = createroles - if ( - replication is not None - and user_attr['replication'] != replication - ): - update['replication'] = replication - if superuser is not None and user_attr['superuser'] != superuser: - update['superuser'] = superuser - if password is not None and (refresh_password or user_attr['password'] != password): - update['password'] = True + update["password"] = True if valid_until is not None: - valid_until_dt = __salt__['postgres.psql_query']( - 'SELECT \'{0}\'::timestamp(0) as dt;'.format( - valid_until.replace('\'', '\'\'')), - **db_args)[0]['dt'] + valid_until_dt = __salt__["postgres.psql_query"]( + "SELECT '{0}'::timestamp(0) as dt;".format( + valid_until.replace("'", "''") + ), + **db_args + )[0]["dt"] try: valid_until_dt = datetime.datetime.strptime( - valid_until_dt, '%Y-%m-%d %H:%M:%S') + valid_until_dt, "%Y-%m-%d %H:%M:%S" + ) except ValueError: valid_until_dt = None - if valid_until_dt != user_attr['expiry time']: - update['valid_until'] = valid_until + if valid_until_dt != user_attr["expiry time"]: + update["valid_until"] = valid_until if groups is not None: lgroups = groups if isinstance(groups, (six.string_types, six.text_type)): - lgroups = lgroups.split(',') + lgroups = lgroups.split(",") if isinstance(lgroups, list): missing_groups = [a for a in lgroups if a not in user_groups] if missing_groups: - update['groups'] = missing_groups + update["groups"] = missing_groups - if mode == 'create' and password is None: + if mode == "create" and password is None: password = default_password - if mode == 'create' or (mode == 'update' and update): - if __opts__['test']: + if mode == "create" or (mode == "update" and update): + if __opts__["test"]: if update: - ret['changes'][name] = update - ret['result'] = None - ret['comment'] = 'User {0} is set to be {1}d'.format(name, mode) + ret["changes"][name] = update + ret["result"] = None + ret["comment"] = "User {0} is set to be {1}d".format(name, mode) return ret - cret = __salt__['postgres.user_{0}'.format(mode)]( + cret = __salt__["postgres.user_{0}".format(mode)]( username=name, createdb=createdb, createroles=createroles, @@ -240,33 +241,36 @@ def present(name, rolepassword=password, valid_until=valid_until, groups=groups, - **db_args) + **db_args + ) else: cret = None if cret: - ret['comment'] = 'The user {0} has been {1}d'.format(name, mode) + ret["comment"] = "The user {0} has been {1}d".format(name, mode) if update: - ret['changes'][name] = update + ret["changes"][name] = update else: - ret['changes'][name] = 'Present' + ret["changes"][name] = "Present" elif cret is not None: - ret['comment'] = 'Failed to create user {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create user {0}".format(name) + ret["result"] = False else: - ret['result'] = True + ret["result"] = True return ret -def absent(name, - user=None, - maintenance_db=None, - db_password=None, - db_host=None, - db_port=None, - db_user=None): - ''' +def absent( + name, + user=None, + maintenance_db=None, + db_password=None, + db_host=None, + db_port=None, + db_user=None, +): + """ Ensure that the named user is absent name @@ -288,36 +292,34 @@ def absent(name, db_port Database port if different from config or default - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} db_args = { - 'maintenance_db': maintenance_db, - 'runas': user, - 'host': db_host, - 'user': db_user, - 'port': db_port, - 'password': db_password, + "maintenance_db": maintenance_db, + "runas": user, + "host": db_host, + "user": db_user, + "port": db_port, + "password": db_password, } # check if user exists and remove it - if __salt__['postgres.user_exists'](name, **db_args): - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0} is set to be removed'.format(name) + if __salt__["postgres.user_exists"](name, **db_args): + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User {0} is set to be removed".format(name) return ret - if __salt__['postgres.user_remove'](name, **db_args): - ret['comment'] = 'User {0} has been removed'.format(name) - ret['changes'][name] = 'Absent' + if __salt__["postgres.user_remove"](name, **db_args): + ret["comment"] = "User {0} has been removed".format(name) + ret["changes"][name] = "Absent" return ret else: - ret['result'] = False - ret['comment'] = 'User {0} failed to be removed'.format(name) + ret["result"] = False + ret["comment"] = "User {0} failed to be removed".format(name) return ret else: - ret['comment'] = 'User {0} is not present, so it cannot ' \ - 'be removed'.format(name) + ret["comment"] = "User {0} is not present, so it cannot " "be removed".format( + name + ) return ret diff --git a/salt/states/powerpath.py b/salt/states/powerpath.py index 03d00cfdc1a..3654fdccaaa 100644 --- a/salt/states/powerpath.py +++ b/salt/states/powerpath.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Powerpath configuration support =============================== @@ -10,91 +10,85 @@ only addition/deletion of licenses is supported. key: powerpath.license_present: [] -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def license_present(name): - ''' + """ Ensures that the specified PowerPath license key is present on the host. name The license key to ensure is present - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if not __salt__['powerpath.has_powerpath'](): - ret['result'] = False - ret['comment'] = 'PowerPath is not installed.' + if not __salt__["powerpath.has_powerpath"](): + ret["result"] = False + ret["comment"] = "PowerPath is not installed." return ret - licenses = [l['key'] for l in __salt__['powerpath.list_licenses']()] + licenses = [l["key"] for l in __salt__["powerpath.list_licenses"]()] if name in licenses: - ret['result'] = True - ret['comment'] = 'License key {0} already present'.format(name) + ret["result"] = True + ret["comment"] = "License key {0} already present".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'License key {0} is set to be added'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "License key {0} is set to be added".format(name) return ret - data = __salt__['powerpath.add_license'](name) - if data['result']: - ret['changes'] = {name: 'added'} - ret['result'] = True - ret['comment'] = data['output'] + data = __salt__["powerpath.add_license"](name) + if data["result"]: + ret["changes"] = {name: "added"} + ret["result"] = True + ret["comment"] = data["output"] return ret else: - ret['result'] = False - ret['comment'] = data['output'] + ret["result"] = False + ret["comment"] = data["output"] return ret def license_absent(name): - ''' + """ Ensures that the specified PowerPath license key is absent on the host. name The license key to ensure is absent - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if not __salt__['powerpath.has_powerpath'](): - ret['result'] = False - ret['comment'] = 'PowerPath is not installed.' + if not __salt__["powerpath.has_powerpath"](): + ret["result"] = False + ret["comment"] = "PowerPath is not installed." return ret - licenses = [l['key'] for l in __salt__['powerpath.list_licenses']()] + licenses = [l["key"] for l in __salt__["powerpath.list_licenses"]()] if name not in licenses: - ret['result'] = True - ret['comment'] = 'License key {0} not present'.format(name) + ret["result"] = True + ret["comment"] = "License key {0} not present".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'License key {0} is set to be removed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "License key {0} is set to be removed".format(name) return ret - data = __salt__['powerpath.remove_license'](name) - if data['result']: - ret['changes'] = {name: 'removed'} - ret['result'] = True - ret['comment'] = data['output'] + data = __salt__["powerpath.remove_license"](name) + if data["result"]: + ret["changes"] = {name: "removed"} + ret["result"] = True + ret["comment"] = data["output"] return ret else: - ret['result'] = False - ret['comment'] = data['output'] + ret["result"] = False + ret["comment"] = data["output"] return ret diff --git a/salt/states/probes.py b/salt/states/probes.py index 24725bc4c0e..a7fffca4eb3 100644 --- a/salt/states/probes.py +++ b/salt/states/probes.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Network Probes =============== @@ -16,27 +16,30 @@ Dependencies - :mod:`napalm probes management module <salt.modules.napalm_probes>` .. versionadded: 2016.11.0 -''' +""" from __future__ import absolute_import # python std lib import logging -log = logging.getLogger(__name__) - from copy import deepcopy +import salt.utils.json + +# import NAPALM utils +import salt.utils.napalm + # salt modules from salt.ext import six -import salt.utils.json -# import NAPALM utils -import salt.utils.napalm + +log = logging.getLogger(__name__) + # ---------------------------------------------------------------------------------------------------------------------- # state properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'probes' +__virtualname__ = "probes" # ---------------------------------------------------------------------------------------------------------------------- # global variables @@ -48,11 +51,12 @@ __virtualname__ = 'probes' def __virtual__(): - ''' + """ NAPALM library must be installed for this module to work and run in a (proxy) minion. - ''' + """ return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__) + # ---------------------------------------------------------------------------------------------------------------------- # helper functions -- will not be exported # ---------------------------------------------------------------------------------------------------------------------- @@ -60,45 +64,48 @@ def __virtual__(): def _default_ret(name): - ''' + """ Returns a default structure of the dictionary to be returned as output of the state functions. - ''' + """ - return { - 'name': name, - 'result': False, - 'changes': {}, - 'comment': '' - } + return {"name": name, "result": False, "changes": {}, "comment": ""} def _retrieve_rpm_probes(): - ''' + """ Will retrieve the probes from the network device using salt module "probes" throught NAPALM proxy. - ''' + """ - return __salt__['probes.config']() + return __salt__["probes.config"]() def _expand_probes(probes, defaults): - ''' + """ Updates the probes dictionary with different levels of default values. - ''' + """ expected_probes = {} for probe_name, probe_test in six.iteritems(probes): if probe_name not in expected_probes.keys(): expected_probes[probe_name] = {} - probe_defaults = probe_test.pop('defaults', {}) + probe_defaults = probe_test.pop("defaults", {}) for test_name, test_details in six.iteritems(probe_test): - test_defaults = test_details.pop('defaults', {}) - expected_test_details = deepcopy(defaults) # copy first the general defaults - expected_test_details.update(probe_defaults) # update with more specific defaults if any - expected_test_details.update(test_defaults) # update with the most specific defaults if possible - expected_test_details.update(test_details) # update with the actual config of the test + test_defaults = test_details.pop("defaults", {}) + expected_test_details = deepcopy( + defaults + ) # copy first the general defaults + expected_test_details.update( + probe_defaults + ) # update with more specific defaults if any + expected_test_details.update( + test_defaults + ) # update with the most specific defaults if possible + expected_test_details.update( + test_details + ) # update with the actual config of the test if test_name not in expected_probes[probe_name].keys(): expected_probes[probe_name][test_name] = expected_test_details @@ -107,11 +114,13 @@ def _expand_probes(probes, defaults): def _clean_probes(probes): - ''' + """ Will remove empty and useless values from the probes dictionary. - ''' + """ - probes = _ordered_dict_to_dict(probes) # make sure we are working only with dict-type + probes = _ordered_dict_to_dict( + probes + ) # make sure we are working only with dict-type probes_copy = deepcopy(probes) for probe_name, probe_tests in six.iteritems(probes_copy): if not probe_tests: @@ -128,9 +137,9 @@ def _clean_probes(probes): def _compare_probes(configured_probes, expected_probes): - ''' + """ Compares configured probes on the device with the expected configuration and returns the differences. - ''' + """ new_probes = {} update_probes = {} @@ -138,15 +147,11 @@ def _compare_probes(configured_probes, expected_probes): # noth configured => configure with expected probes if not configured_probes: - return { - 'add': expected_probes - } + return {"add": expected_probes} # noting expected => remove everything if not expected_probes: - return { - 'remove': configured_probes - } + return {"remove": configured_probes} configured_probes_keys_set = set(configured_probes.keys()) expected_probes_keys_set = set(expected_probes.keys()) @@ -173,16 +178,14 @@ def _compare_probes(configured_probes, expected_probes): for test_name in new_tests_keys_set: if probe_name not in new_probes.keys(): new_probes[probe_name] = {} - new_probes[probe_name].update({ - test_name: probe_tests.pop(test_name) - }) + new_probes[probe_name].update({test_name: probe_tests.pop(test_name)}) # old tests for common probes for test_name in remove_tests_keys_set: if probe_name not in remove_probes.keys(): remove_probes[probe_name] = {} - remove_probes[probe_name].update({ - test_name: configured_probe_tests.pop(test_name) - }) + remove_probes[probe_name].update( + {test_name: configured_probe_tests.pop(test_name)} + ) # common tests for common probes for test_name, test_params in six.iteritems(probe_tests): configured_test_params = configured_probe_tests.get(test_name, {}) @@ -190,57 +193,53 @@ def _compare_probes(configured_probes, expected_probes): if test_params != configured_test_params: if probe_name not in update_probes.keys(): update_probes[probe_name] = {} - update_probes[probe_name].update({ - test_name: test_params - }) + update_probes[probe_name].update({test_name: test_params}) - return { - 'add': new_probes, - 'update': update_probes, - 'remove': remove_probes - } + return {"add": new_probes, "update": update_probes, "remove": remove_probes} def _ordered_dict_to_dict(probes): - '''Mandatory to be dict type in order to be used in the NAPALM Jinja template.''' + """Mandatory to be dict type in order to be used in the NAPALM Jinja template.""" return salt.utils.json.loads(salt.utils.json.dumps(probes)) def _set_rpm_probes(probes): - ''' + """ Calls the Salt module "probes" to configure the probes on the device. - ''' + """ - return __salt__['probes.set_probes']( + return __salt__["probes.set_probes"]( _ordered_dict_to_dict(probes), # make sure this does not contain ordered dicts - commit=False + commit=False, ) def _schedule_probes(probes): - ''' + """ Calls the Salt module "probes" to schedule the configured probes on the device. - ''' + """ - return __salt__['probes.schedule_probes']( + return __salt__["probes.schedule_probes"]( _ordered_dict_to_dict(probes), # make sure this does not contain ordered dicts - commit=False + commit=False, ) def _delete_rpm_probes(probes): - ''' + """ Calls the Salt module "probes" to delete probes from the device. - ''' + """ - return __salt__['probes.delete_probes']( - _ordered_dict_to_dict(probes), # not mandatory, but let's make sure we catch all cases - commit=False + return __salt__["probes.delete_probes"]( + _ordered_dict_to_dict( + probes + ), # not mandatory, but let's make sure we catch all cases + commit=False, ) @@ -251,7 +250,7 @@ def _delete_rpm_probes(probes): def managed(name, probes, defaults=None): - ''' + """ Ensure the networks device is configured as specified in the state SLS file. Probes not specified will be removed, while probes not confiured as expected will trigger config updates. @@ -334,31 +333,37 @@ def managed(name, probes, defaults=None): probe_count: 15 test_interval: 3 probe_type: icmp-ping - ''' + """ ret = _default_ret(name) result = True - comment = '' + comment = "" - rpm_probes_config = _retrieve_rpm_probes() # retrieves the RPM config from the device - if not rpm_probes_config.get('result'): - ret.update({ - 'result': False, - 'comment': 'Cannot retrieve configurtion of the probes from the device: {reason}'.format( - reason=rpm_probes_config.get('comment') - ) - }) + rpm_probes_config = ( + _retrieve_rpm_probes() + ) # retrieves the RPM config from the device + if not rpm_probes_config.get("result"): + ret.update( + { + "result": False, + "comment": "Cannot retrieve configurtion of the probes from the device: {reason}".format( + reason=rpm_probes_config.get("comment") + ), + } + ) return ret # build expect probes config dictionary # using default values - configured_probes = rpm_probes_config.get('out', {}) + configured_probes = rpm_probes_config.get("out", {}) if not isinstance(defaults, dict): defaults = {} expected_probes = _expand_probes(probes, defaults) - _clean_probes(configured_probes) # let's remove the unnecessary data from the configured probes + _clean_probes( + configured_probes + ) # let's remove the unnecessary data from the configured probes _clean_probes(expected_probes) # also from the expected data # ----- Compare expected config with the existing config ----------------------------------------------------------> @@ -369,57 +374,56 @@ def managed(name, probes, defaults=None): # ----- Call set_probes and delete_probes as needed ---------------------------------------------------------------> - add_probes = diff.get('add') - update_probes = diff.get('update') - remove_probes = diff.get('remove') + add_probes = diff.get("add") + update_probes = diff.get("update") + remove_probes = diff.get("remove") changes = { - 'added': _ordered_dict_to_dict(add_probes), - 'updated': _ordered_dict_to_dict(update_probes), - 'removed': _ordered_dict_to_dict(remove_probes) + "added": _ordered_dict_to_dict(add_probes), + "updated": _ordered_dict_to_dict(update_probes), + "removed": _ordered_dict_to_dict(remove_probes), } - ret.update({ - 'changes': changes - }) + ret.update({"changes": changes}) - if __opts__['test'] is True: - ret.update({ - 'comment': 'Testing mode: configuration was not changed!', - 'result': None - }) + if __opts__["test"] is True: + ret.update( + {"comment": "Testing mode: configuration was not changed!", "result": None} + ) return ret - config_change_expected = False # to check if something changed and a commit would be needed + config_change_expected = ( + False # to check if something changed and a commit would be needed + ) if add_probes: added = _set_rpm_probes(add_probes) - if added.get('result'): + if added.get("result"): config_change_expected = True else: result = False - comment += 'Cannot define new probes: {reason}\n'.format( - reason=added.get('comment') + comment += "Cannot define new probes: {reason}\n".format( + reason=added.get("comment") ) if update_probes: updated = _set_rpm_probes(update_probes) - if updated.get('result'): + if updated.get("result"): config_change_expected = True else: result = False - comment += 'Cannot update probes: {reason}\n'.format( - reason=updated.get('comment') + comment += "Cannot update probes: {reason}\n".format( + reason=updated.get("comment") ) if remove_probes: removed = _delete_rpm_probes(remove_probes) - if removed.get('result'): + if removed.get("result"): config_change_expected = True else: result = False - comment += 'Cannot remove probes! {reason}\n'.format( - reason=removed.get('comment') + comment += "Cannot remove probes! {reason}\n".format( + reason=removed.get("comment") ) # <---- Call set_probes and delete_probes as needed ---------------------------------------------------------------- @@ -428,27 +432,24 @@ def managed(name, probes, defaults=None): if config_change_expected: # if any changes expected, try to commit - result, comment = __salt__['net.config_control']() + result, comment = __salt__["net.config_control"]() # <---- Try to save changes ---------------------------------------------------------------------------------------- # ----- Try to schedule the probes --------------------------------------------------------------------------------> add_scheduled = _schedule_probes(add_probes) - if add_scheduled.get('result'): + if add_scheduled.get("result"): # if able to load the template to schedule the probes, try to commit the scheduling data # (yes, a second commit is needed) # on devices such as Juniper, RPM probes do not need to be scheduled # therefore the template is empty and won't try to commit empty changes - result, comment = __salt__['net.config_control']() + result, comment = __salt__["net.config_control"]() if config_change_expected: - if result and comment == '': # if any changes and was able to apply them - comment = 'Probes updated successfully!' + if result and comment == "": # if any changes and was able to apply them + comment = "Probes updated successfully!" - ret.update({ - 'result': result, - 'comment': comment - }) + ret.update({"result": result, "comment": comment}) return ret diff --git a/salt/states/process.py b/salt/states/process.py index 401bf35fb2e..8251c1b0351 100644 --- a/salt/states/process.py +++ b/salt/states/process.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Process Management ================== @@ -10,16 +10,18 @@ Ensure a process matching a given pattern is absent. httpd-absent: process.absent: - name: apache2 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - return 'ps.pkill' in __salt__ + if "ps.pkill" in __salt__: + return True + return (False, "ps module could not be loaded") def absent(name, user=None, signal=None): - ''' + """ Ensures that the named command is not running. name @@ -30,32 +32,27 @@ def absent(name, user=None, signal=None): signal Signal to send to the process(es). - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - running = __salt__['ps.pgrep'](name, user=user) - ret['result'] = None + if __opts__["test"]: + running = __salt__["ps.pgrep"](name, user=user) + ret["result"] = None if running: - ret['comment'] = ('{0} processes will ' - 'be killed').format(len(running)) + ret["comment"] = ("{0} processes will " "be killed").format(len(running)) else: - ret['comment'] = 'No matching processes running' + ret["comment"] = "No matching processes running" return ret if signal: - status = __salt__['ps.pkill'](name, user=user, - signal=signal, full=True) + status = __salt__["ps.pkill"](name, user=user, signal=signal, full=True) else: - status = __salt__['ps.pkill'](name, user=user, full=True) + status = __salt__["ps.pkill"](name, user=user, full=True) - ret['result'] = True + ret["result"] = True if status: - ret['comment'] = 'Killed {0} processes'.format(len(status['killed'])) - ret['changes'] = status + ret["comment"] = "Killed {0} processes".format(len(status["killed"])) + ret["changes"] = status else: - ret['comment'] = 'No matching processes running' + ret["comment"] = "No matching processes running" return ret diff --git a/salt/states/proxy.py b/salt/states/proxy.py index 37ad0b01e54..135d7bdea35 100644 --- a/salt/states/proxy.py +++ b/salt/states/proxy.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Allows you to manage proxy settings on minions ============================================== @@ -13,9 +13,10 @@ Setup proxy settings on minions - bypass_domains: - localhost - 127.0.0.1 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -25,20 +26,28 @@ import salt.utils.platform from salt.ext import six log = logging.getLogger(__name__) -__virtualname__ = 'proxy' +__virtualname__ = "proxy" def __virtual__(): - ''' + """ Only work on Mac OS and Windows - ''' + """ if salt.utils.platform.is_darwin() or salt.utils.platform.is_windows(): return True - return False + return (False, "Only Mac OS and Windows supported") -def managed(name, port, services=None, user=None, password=None, bypass_domains=None, network_service='Ethernet'): - ''' +def managed( + name, + port, + services=None, + user=None, + password=None, + bypass_domains=None, + network_service="Ethernet", +): + """ Manages proxy settings for this mininon name @@ -63,65 +72,80 @@ def managed(name, port, services=None, user=None, password=None, bypass_domains= network_service The network service to apply the changes to, this only necessary on macOS - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - valid_services = ['http', 'https', 'ftp'] + valid_services = ["http", "https", "ftp"] if services is None: services = valid_services # Darwin - if __grains__['os'] in ['MacOS', 'Darwin']: - ret['changes'] = {'new': []} + if __grains__["os"] in ["MacOS", "Darwin"]: + ret["changes"] = {"new": []} for service in services: - current_settings = __salt__['proxy.get_{0}_proxy'.format(service)]() + current_settings = __salt__["proxy.get_{0}_proxy".format(service)]() - if current_settings.get('server') == name and current_settings.get('port') == six.text_type(port): - ret['comment'] += '{0} proxy settings already set.\n'.format(service) - elif __salt__['proxy.set_{0}_proxy'.format(service)](name, port, user, password, network_service): - ret['comment'] += '{0} proxy settings updated correctly\n'.format(service) - ret['changes']['new'].append({'service': service, 'server': name, 'port': port, 'user': user}) + if current_settings.get("server") == name and current_settings.get( + "port" + ) == six.text_type(port): + ret["comment"] += "{0} proxy settings already set.\n".format(service) + elif __salt__["proxy.set_{0}_proxy".format(service)]( + name, port, user, password, network_service + ): + ret["comment"] += "{0} proxy settings updated correctly\n".format( + service + ) + ret["changes"]["new"].append( + {"service": service, "server": name, "port": port, "user": user} + ) else: - ret['result'] = False - ret['comment'] += 'Failed to set {0} proxy settings.\n' + ret["result"] = False + ret["comment"] += "Failed to set {0} proxy settings.\n" if bypass_domains is not None: - current_domains = __salt__['proxy.get_proxy_bypass']() + current_domains = __salt__["proxy.get_proxy_bypass"]() - if len(set(current_domains).intersection(bypass_domains)) == len(bypass_domains): - ret['comment'] += 'Proxy bypass domains are already set correctly.\n' - elif __salt__['proxy.set_proxy_bypass'](bypass_domains, network_service): - ret['comment'] += 'Proxy bypass domains updated correctly\n' - ret['changes']['new'].append({'bypass_domains': list(set(bypass_domains).difference(current_domains))}) + if len(set(current_domains).intersection(bypass_domains)) == len( + bypass_domains + ): + ret["comment"] += "Proxy bypass domains are already set correctly.\n" + elif __salt__["proxy.set_proxy_bypass"](bypass_domains, network_service): + ret["comment"] += "Proxy bypass domains updated correctly\n" + ret["changes"]["new"].append( + { + "bypass_domains": list( + set(bypass_domains).difference(current_domains) + ) + } + ) else: - ret['result'] = False - ret['comment'] += 'Failed to set bypass proxy domains.\n' + ret["result"] = False + ret["comment"] += "Failed to set bypass proxy domains.\n" - if len(ret['changes']['new']) == 0: - del ret['changes']['new'] + if len(ret["changes"]["new"]) == 0: + del ret["changes"]["new"] return ret # Windows - Needs its own branch as all settings need to be set at the same time - if __grains__['os'] in ['Windows']: + if __grains__["os"] in ["Windows"]: changes_needed = False - current_settings = __salt__['proxy.get_proxy_win']() - current_domains = __salt__['proxy.get_proxy_bypass']() + current_settings = __salt__["proxy.get_proxy_win"]() + current_domains = __salt__["proxy.get_proxy_bypass"]() - if current_settings.get('enabled', False) is True: + if current_settings.get("enabled", False) is True: for service in services: # We need to update one of our proxy servers if service not in current_settings: changes_needed = True break - if current_settings[service]['server'] != name or current_settings[service]['port'] != six.text_type(port): + if current_settings[service]["server"] != name or current_settings[ + service + ]["port"] != six.text_type(port): changes_needed = True break else: @@ -129,16 +153,18 @@ def managed(name, port, services=None, user=None, password=None, bypass_domains= changes_needed = True # We need to update our bypass domains - if len(set(current_domains).intersection(bypass_domains)) != len(bypass_domains): + if len(set(current_domains).intersection(bypass_domains)) != len( + bypass_domains + ): changes_needed = True if changes_needed: - if __salt__['proxy.set_proxy_win'](name, port, services, bypass_domains): - ret['comment'] = 'Proxy settings updated correctly' + if __salt__["proxy.set_proxy_win"](name, port, services, bypass_domains): + ret["comment"] = "Proxy settings updated correctly" else: - ret['result'] = False - ret['comment'] = 'Failed to set {0} proxy settings.' + ret["result"] = False + ret["comment"] = "Failed to set {0} proxy settings." else: - ret['comment'] = 'Proxy settings already correct.' + ret["comment"] = "Proxy settings already correct." return ret diff --git a/salt/states/pushover.py b/salt/states/pushover.py index 2904fcdfcfc..1ea968902b7 100644 --- a/salt/states/pushover.py +++ b/salt/states/pushover.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Send a message to PushOver ========================== @@ -26,31 +26,35 @@ The api key can be specified in the master or minion configuration like below: pushover: token: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the pushover module is available in __salt__ - ''' - return 'pushover' if 'pushover.post_message' in __salt__ else False + """ + if "pushover.post_message" in __salt__: + return "pushover" + return (False, "pushover module could not be loaded") -def post_message(name, - user=None, - device=None, - message=None, - title=None, - priority=None, - expire=None, - retry=None, - sound=None, - api_version=1, - token=None): - ''' +def post_message( + name, + user=None, + device=None, + message=None, + title=None, + priority=None, + expire=None, + retry=None, + sound=None, + api_version=1, + token=None, +): + """ Send a message to a PushOver channel. .. code-block:: yaml @@ -98,26 +102,25 @@ def post_message(name, The token for PushOver to use for authentication, if not specified in the configuration options of master or minion. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if __opts__['test']: - ret['comment'] = 'The following message is to be sent to PushOver: {0}'.format(message) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The following message is to be sent to PushOver: {0}".format( + message + ) + ret["result"] = None return ret if not user: - ret['comment'] = 'PushOver user is missing: {0}'.format(user) + ret["comment"] = "PushOver user is missing: {0}".format(user) return ret if not message: - ret['comment'] = 'PushOver message is missing: {0}'.format(message) + ret["comment"] = "PushOver message is missing: {0}".format(message) return ret - result = __salt__['pushover.post_message']( + result = __salt__["pushover.post_message"]( user=user, message=message, title=title, @@ -129,9 +132,9 @@ def post_message(name, ) if result: - ret['result'] = True - ret['comment'] = 'Sent message: {0}'.format(name) + ret["result"] = True + ret["comment"] = "Sent message: {0}".format(name) else: - ret['comment'] = 'Failed to send message: {0}'.format(name) + ret["comment"] = "Failed to send message: {0}".format(name) return ret diff --git a/salt/states/pyenv.py b/salt/states/pyenv.py index bbaeec6fdd4..a5c1c512585 100644 --- a/salt/states/pyenv.py +++ b/salt/states/pyenv.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Managing python installations with pyenv ======================================== @@ -47,62 +47,62 @@ This is how a state configuration could look like: .. note:: Git needs to be installed and available via PATH if pyenv is to be installed automatically by the module. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import re def _check_pyenv(ret, user=None): - ''' + """ Check to see if pyenv is installed. - ''' - if not __salt__['pyenv.is_installed'](user): - ret['result'] = False - ret['comment'] = 'pyenv is not installed.' + """ + if not __salt__["pyenv.is_installed"](user): + ret["result"] = False + ret["comment"] = "pyenv is not installed." return ret def _python_installed(ret, python, user=None): - ''' + """ Check to see if given python is installed. - ''' - default = __salt__['pyenv.default'](runas=user) - for version in __salt__['pyenv.versions'](user): + """ + default = __salt__["pyenv.default"](runas=user) + for version in __salt__["pyenv.versions"](user): if version == python: - ret['result'] = True - ret['comment'] = 'Requested python exists.' - ret['default'] = default == python + ret["result"] = True + ret["comment"] = "Requested python exists." + ret["default"] = default == python break return ret def _check_and_install_python(ret, python, default=False, user=None): - ''' + """ Verify that python is installed, install if unavailable - ''' + """ ret = _python_installed(ret, python, user=user) - if not ret['result']: - if __salt__['pyenv.install_python'](python, runas=user): - ret['result'] = True - ret['changes'][python] = 'Installed' - ret['comment'] = 'Successfully installed python' - ret['default'] = default + if not ret["result"]: + if __salt__["pyenv.install_python"](python, runas=user): + ret["result"] = True + ret["changes"][python] = "Installed" + ret["comment"] = "Successfully installed python" + ret["default"] = default else: - ret['result'] = False - ret['comment'] = 'Could not install python.' + ret["result"] = False + ret["comment"] = "Could not install python." return ret if default: - __salt__['pyenv.default'](python, runas=user) + __salt__["pyenv.default"](python, runas=user) return ret def installed(name, default=False, user=None): - ''' + """ Verify that the specified python is installed with pyenv. pyenv is installed if necessary. @@ -118,20 +118,20 @@ def installed(name, default=False, user=None): .. versionadded:: 0.17.0 .. versionadded:: 0.16.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if name.startswith('python-'): - name = re.sub(r'^python-', '', name) + if name.startswith("python-"): + name = re.sub(r"^python-", "", name) - if __opts__['test']: - ret['comment'] = 'python {0} is set to be installed'.format(name) + if __opts__["test"]: + ret["comment"] = "python {0} is set to be installed".format(name) return ret ret = _check_pyenv(ret, user) - if ret['result'] is False: - if not __salt__['pyenv.install'](user): - ret['comment'] = 'pyenv failed to install' + if ret["result"] is False: + if not __salt__["pyenv.install"](user): + ret["comment"] = "pyenv failed to install" return ret else: return _check_and_install_python(ret, name, default, user=user) @@ -140,32 +140,32 @@ def installed(name, default=False, user=None): def _check_and_uninstall_python(ret, python, user=None): - ''' + """ Verify that python is uninstalled - ''' + """ ret = _python_installed(ret, python, user=user) - if ret['result']: - if ret['default']: - __salt__['pyenv.default']('system', runas=user) + if ret["result"]: + if ret["default"]: + __salt__["pyenv.default"]("system", runas=user) - if __salt__['pyenv.uninstall_python'](python, runas=user): - ret['result'] = True - ret['changes'][python] = 'Uninstalled' - ret['comment'] = 'Successfully removed python' + if __salt__["pyenv.uninstall_python"](python, runas=user): + ret["result"] = True + ret["changes"][python] = "Uninstalled" + ret["comment"] = "Successfully removed python" return ret else: - ret['result'] = False - ret['comment'] = 'Failed to uninstall python' + ret["result"] = False + ret["comment"] = "Failed to uninstall python" return ret else: - ret['result'] = True - ret['comment'] = 'python {0} is already absent'.format(python) + ret["result"] = True + ret["comment"] = "python {0} is already absent".format(python) return ret def absent(name, user=None): - ''' + """ Verify that the specified python is not installed with pyenv. pyenv is installed if necessary. @@ -178,27 +178,27 @@ def absent(name, user=None): .. versionadded:: 0.17.0 .. versionadded:: 0.16.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if name.startswith('python-'): - name = re.sub(r'^python-', '', name) + if name.startswith("python-"): + name = re.sub(r"^python-", "", name) - if __opts__['test']: - ret['comment'] = 'python {0} is set to be uninstalled'.format(name) + if __opts__["test"]: + ret["comment"] = "python {0} is set to be uninstalled".format(name) return ret ret = _check_pyenv(ret, user) - if ret['result'] is False: - ret['result'] = True - ret['comment'] = 'pyenv not installed, {0} not either'.format(name) + if ret["result"] is False: + ret["result"] = True + ret["comment"] = "pyenv not installed, {0} not either".format(name) return ret else: return _check_and_uninstall_python(ret, name, user=user) def install_pyenv(name, user=None): - ''' + """ Install pyenv if not installed. Allows you to require pyenv be installed prior to installing the plugins. Useful if you want to install pyenv plugins via the git or file modules and need them installed before @@ -209,11 +209,11 @@ def install_pyenv(name, user=None): user: None The user to run pyenv as. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if __opts__['test']: - ret['comment'] = 'pyenv is set to be installed' + if __opts__["test"]: + ret["comment"] = "pyenv is set to be installed" return ret return _check_and_install_python(ret, user) diff --git a/salt/states/pyrax_queues.py b/salt/states/pyrax_queues.py index cd76ca01271..fd3d39be1f8 100644 --- a/salt/states/pyrax_queues.py +++ b/salt/states/pyrax_queues.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Rackspace Queues ======================= @@ -20,21 +20,21 @@ This module is greatly inspired by boto_* modules from SaltStack code source. myqueue: pyrax_queues.absent: - provider: my-pyrax -''' +""" from __future__ import absolute_import, print_function, unicode_literals import salt.utils.openstack.pyrax as suop def __virtual__(): - ''' + """ Only load if pyrax is available. - ''' + """ return suop.HAS_PYRAX def present(name, provider): - ''' + """ Ensure the RackSpace queue exists. name @@ -42,34 +42,42 @@ def present(name, provider): provider Salt Cloud Provider - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_present = list(__salt__['cloud.action']('queues_exists', provider=provider, name=name)[provider].values())[0] + is_present = list( + __salt__["cloud.action"]("queues_exists", provider=provider, name=name)[ + provider + ].values() + )[0] if not is_present: - if __opts__['test']: - msg = 'Rackspace queue {0} is set to be created.'.format(name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "Rackspace queue {0} is set to be created.".format(name) + ret["comment"] = msg + ret["result"] = None return ret - created = __salt__['cloud.action']('queues_create', provider=provider, name=name) + created = __salt__["cloud.action"]( + "queues_create", provider=provider, name=name + ) if created: - queue = __salt__['cloud.action']('queues_show', provider=provider, name=name) - ret['changes']['old'] = {} - ret['changes']['new'] = {'queue': queue} + queue = __salt__["cloud.action"]( + "queues_show", provider=provider, name=name + ) + ret["changes"]["old"] = {} + ret["changes"]["new"] = {"queue": queue} else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} Rackspace queue.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0} Rackspace queue.".format(name) return ret else: - ret['comment'] = '{0} present.'.format(name) + ret["comment"] = "{0} present.".format(name) return ret def absent(name, provider): - ''' + """ Ensure the named Rackspace queue is deleted. name @@ -77,26 +85,31 @@ def absent(name, provider): provider Salt Cloud provider - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - is_present = list(__salt__['cloud.action']('queues_exists', provider=provider, name=name)[provider].values())[0] + is_present = list( + __salt__["cloud.action"]("queues_exists", provider=provider, name=name)[ + provider + ].values() + )[0] if is_present: - if __opts__['test']: - ret['comment'] = 'Rackspace queue {0} is set to be removed.'.format( - name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "Rackspace queue {0} is set to be removed.".format(name) + ret["result"] = None return ret - queue = __salt__['cloud.action']('queues_show', provider=provider, name=name) - deleted = __salt__['cloud.action']('queues_delete', provider=provider, name=name) + queue = __salt__["cloud.action"]("queues_show", provider=provider, name=name) + deleted = __salt__["cloud.action"]( + "queues_delete", provider=provider, name=name + ) if deleted: - ret['changes']['old'] = queue - ret['changes']['new'] = {} + ret["changes"]["old"] = queue + ret["changes"]["new"] = {} else: - ret['result'] = False - ret['comment'] = 'Failed to delete {0} Rackspace queue.'.format(name) + ret["result"] = False + ret["comment"] = "Failed to delete {0} Rackspace queue.".format(name) else: - ret['comment'] = '{0} does not exist.'.format(name) + ret["comment"] = "{0} does not exist.".format(name) return ret diff --git a/salt/states/quota.py b/salt/states/quota.py index d035d119ae4..a807f2cbc7d 100644 --- a/salt/states/quota.py +++ b/salt/states/quota.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of POSIX Quotas ========================== @@ -11,20 +11,22 @@ The quota can be managed for the system: quota.mode: mode: off quotatype: user -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the quota module is available in __salt__ - ''' - return 'quota' if 'quota.report' in __salt__ else False + """ + if "quota.report" in __salt__: + return "quota" + return (False, "quota module could not be loaded") def mode(name, mode, quotatype): - ''' + """ Set the quota for the system name @@ -35,28 +37,24 @@ def mode(name, mode, quotatype): quotatype Must be ``user`` or ``group`` - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} - fun = 'off' + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + fun = "off" if mode is True: - fun = 'on' - if __salt__['quota.get_mode'](name)[name][quotatype] == fun: - ret['result'] = True - ret['comment'] = 'Quota for {0} already set to {1}'.format(name, fun) + fun = "on" + if __salt__["quota.get_mode"](name)[name][quotatype] == fun: + ret["result"] = True + ret["comment"] = "Quota for {0} already set to {1}".format(name, fun) return ret - if __opts__['test']: - ret['comment'] = 'Quota for {0} needs to be set to {1}'.format(name, - fun) + if __opts__["test"]: + ret["comment"] = "Quota for {0} needs to be set to {1}".format(name, fun) return ret - if __salt__['quota.{0}'.format(fun)](name): - ret['changes'] = {'quota': name} - ret['result'] = True - ret['comment'] = 'Set quota for {0} to {1}'.format(name, fun) + if __salt__["quota.{0}".format(fun)](name): + ret["changes"] = {"quota": name} + ret["result"] = True + ret["comment"] = "Set quota for {0} to {1}".format(name, fun) return ret else: - ret['result'] = False - ret['comment'] = 'Failed to set quota for {0} to {1}'.format(name, fun) + ret["result"] = False + ret["comment"] = "Failed to set quota for {0} to {1}".format(name, fun) return ret diff --git a/salt/states/rabbitmq_cluster.py b/salt/states/rabbitmq_cluster.py index 535d37fe68f..5273bf18029 100644 --- a/salt/states/rabbitmq_cluster.py +++ b/salt/states/rabbitmq_cluster.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage RabbitMQ Clusters ======================== @@ -11,8 +11,8 @@ Example: rabbitmq_cluster.join: - user: rabbit - host: rabbit.example.com -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -25,14 +25,16 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if RabbitMQ is installed. - ''' - return salt.utils.path.which('rabbitmqctl') is not None + """ + if salt.utils.path.which("rabbitmqctl"): + return True + return (False, "Command not found: rabbitmqctl") -def joined(name, host, user='rabbit', ram_node=None, runas='root'): - ''' +def joined(name, host, user="rabbit", ram_node=None, runas="root"): + """ Ensure the current node joined to a cluster with node user@host name @@ -45,37 +47,33 @@ def joined(name, host, user='rabbit', ram_node=None, runas='root'): Join node as a RAM node runas The user to run the rabbitmq command as - ''' + """ - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - status = __salt__['rabbitmq.cluster_status']() - if '{0}@{1}'.format(user, host) in status: - ret['comment'] = 'Already in cluster' + status = __salt__["rabbitmq.cluster_status"]() + if "{0}@{1}".format(user, host) in status: + ret["comment"] = "Already in cluster" return ret - if not __opts__['test']: - result = __salt__['rabbitmq.join_cluster'](host, - user, - ram_node, - runas=runas) - if 'Error' in result: - ret['result'] = False - ret['comment'] = result['Error'] + if not __opts__["test"]: + result = __salt__["rabbitmq.join_cluster"](host, user, ram_node, runas=runas) + if "Error" in result: + ret["result"] = False + ret["comment"] = result["Error"] return ret - elif 'Join' in result: - ret['comment'] = result['Join'] + elif "Join" in result: + ret["comment"] = result["Join"] # If we've reached this far before returning, we have changes. - ret['changes'] = {'old': '', 'new': '{0}@{1}'.format(user, host)} + ret["changes"] = {"old": "", "new": "{0}@{1}".format(user, host)} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Node is set to join cluster {0}@{1}'.format( - user, host) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Node is set to join cluster {0}@{1}".format(user, host) return ret # Alias join to preserve backward compat -join = salt.utils.functools.alias_function(joined, 'join') +join = salt.utils.functools.alias_function(joined, "join") diff --git a/salt/states/rabbitmq_plugin.py b/salt/states/rabbitmq_plugin.py index 1a45b96dcb7..92a31efb1c1 100644 --- a/salt/states/rabbitmq_plugin.py +++ b/salt/states/rabbitmq_plugin.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage RabbitMQ Plugins ======================= @@ -11,10 +11,11 @@ Example: some_plugin: rabbitmq_plugin.enabled: [] -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs @@ -24,91 +25,91 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if RabbitMQ is installed. - ''' - if __salt__['cmd.has_exec']('rabbitmqctl'): + """ + if __salt__["cmd.has_exec"]("rabbitmqctl"): return True - return False + return (False, "Command not found: rabbitmqctl") def enabled(name, runas=None): - ''' + """ Ensure the RabbitMQ plugin is enabled. name The name of the plugin runas The user to run the rabbitmq-plugin command as - ''' + """ - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} try: - plugin_enabled = __salt__['rabbitmq.plugin_is_enabled'](name, runas=runas) + plugin_enabled = __salt__["rabbitmq.plugin_is_enabled"](name, runas=runas) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = 'Error: {0}'.format(err) + ret["result"] = False + ret["comment"] = "Error: {0}".format(err) return ret if plugin_enabled: - ret['comment'] = 'Plugin \'{0}\' is already enabled.'.format(name) + ret["comment"] = "Plugin '{0}' is already enabled.".format(name) return ret - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['rabbitmq.enable_plugin'](name, runas=runas) + __salt__["rabbitmq.enable_plugin"](name, runas=runas) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = 'Error: {0}'.format(err) + ret["result"] = False + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({'old': '', 'new': name}) + ret["changes"].update({"old": "", "new": name}) - if __opts__['test'] and ret['changes']: - ret['result'] = None - ret['comment'] = 'Plugin \'{0}\' is set to be enabled.'.format(name) + if __opts__["test"] and ret["changes"]: + ret["result"] = None + ret["comment"] = "Plugin '{0}' is set to be enabled.".format(name) return ret - ret['comment'] = 'Plugin \'{0}\' was enabled.'.format(name) + ret["comment"] = "Plugin '{0}' was enabled.".format(name) return ret def disabled(name, runas=None): - ''' + """ Ensure the RabbitMQ plugin is disabled. name The name of the plugin runas The user to run the rabbitmq-plugin command as - ''' + """ - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} try: - plugin_enabled = __salt__['rabbitmq.plugin_is_enabled'](name, runas=runas) + plugin_enabled = __salt__["rabbitmq.plugin_is_enabled"](name, runas=runas) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = 'Error: {0}'.format(err) + ret["result"] = False + ret["comment"] = "Error: {0}".format(err) return ret if not plugin_enabled: - ret['comment'] = 'Plugin \'{0}\' is already disabled.'.format(name) + ret["comment"] = "Plugin '{0}' is already disabled.".format(name) return ret - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['rabbitmq.disable_plugin'](name, runas=runas) + __salt__["rabbitmq.disable_plugin"](name, runas=runas) except CommandExecutionError as err: - ret['result'] = False - ret['comment'] = 'Error: {0}'.format(err) + ret["result"] = False + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({'old': name, 'new': ''}) + ret["changes"].update({"old": name, "new": ""}) - if __opts__['test'] and ret['changes']: - ret['result'] = None - ret['comment'] = 'Plugin \'{0}\' is set to be disabled.'.format(name) + if __opts__["test"] and ret["changes"]: + ret["result"] = None + ret["comment"] = "Plugin '{0}' is set to be disabled.".format(name) return ret - ret['comment'] = 'Plugin \'{0}\' was disabled.'.format(name) + ret["comment"] = "Plugin '{0}' was disabled.".format(name) return ret diff --git a/salt/states/rabbitmq_policy.py b/salt/states/rabbitmq_policy.py index 69e12a4f948..4fb56147ba7 100644 --- a/salt/states/rabbitmq_policy.py +++ b/salt/states/rabbitmq_policy.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage RabbitMQ Policies ======================== @@ -16,32 +16,32 @@ Example: - name: HA - pattern: '.*' - definition: '{"ha-mode": "all"}' -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import json # Import python libs import logging + import salt.utils.path -import json log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if RabbitMQ is installed. - ''' - return salt.utils.path.which('rabbitmqctl') is not None + """ + if salt.utils.path.which("rabbitmqctl"): + return True + return (False, "Command not found: rabbitmqctl") -def present(name, - pattern, - definition, - priority=0, - vhost='/', - runas=None, - apply_to=None): - ''' +def present( + name, pattern, definition, priority=0, vhost="/", runas=None, apply_to=None +): + """ Ensure the RabbitMQ policy exists. Reference: http://www.rabbitmq.com/ha.html @@ -60,74 +60,78 @@ def present(name, Name of the user to run the command as apply_to Apply policy to 'queues', 'exchanges' or 'all' (default to 'all') - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} result = {} - policies = __salt__['rabbitmq.list_policies'](vhost=vhost, runas=runas) + policies = __salt__["rabbitmq.list_policies"](vhost=vhost, runas=runas) policy = policies.get(vhost, {}).get(name) updates = [] if policy: - if policy.get('pattern') != pattern: - updates.append('Pattern') - current_definition = policy.get('definition') - current_definition = json.loads(current_definition) if current_definition else '' - new_definition = json.loads(definition) if definition else '' + if policy.get("pattern") != pattern: + updates.append("Pattern") + current_definition = policy.get("definition") + current_definition = ( + json.loads(current_definition) if current_definition else "" + ) + new_definition = json.loads(definition) if definition else "" if current_definition != new_definition: - updates.append('Definition') - if apply_to and (policy.get('apply-to') != apply_to): - updates.append('Applyto') - if int(policy.get('priority')) != priority: - updates.append('Priority') + updates.append("Definition") + if apply_to and (policy.get("apply-to") != apply_to): + updates.append("Applyto") + if int(policy.get("priority")) != priority: + updates.append("Priority") if policy and not updates: - ret['comment'] = 'Policy {0} {1} is already present'.format(vhost, name) + ret["comment"] = "Policy {0} {1} is already present".format(vhost, name) return ret if not policy: - ret['changes'].update({'old': {}, 'new': name}) - if __opts__['test']: - ret['comment'] = 'Policy {0} {1} is set to be created'.format(vhost, name) + ret["changes"].update({"old": {}, "new": name}) + if __opts__["test"]: + ret["comment"] = "Policy {0} {1} is set to be created".format(vhost, name) else: - log.debug('Policy doesn\'t exist - Creating') - result = __salt__['rabbitmq.set_policy'](vhost, - name, - pattern, - definition, - priority=priority, - runas=runas, - apply_to=apply_to) + log.debug("Policy doesn't exist - Creating") + result = __salt__["rabbitmq.set_policy"]( + vhost, + name, + pattern, + definition, + priority=priority, + runas=runas, + apply_to=apply_to, + ) elif updates: - ret['changes'].update({'old': policy, 'new': updates}) - if __opts__['test']: - ret['comment'] = 'Policy {0} {1} is set to be updated'.format(vhost, name) + ret["changes"].update({"old": policy, "new": updates}) + if __opts__["test"]: + ret["comment"] = "Policy {0} {1} is set to be updated".format(vhost, name) else: - log.debug('Policy exists but needs updating') - result = __salt__['rabbitmq.set_policy'](vhost, - name, - pattern, - definition, - priority=priority, - runas=runas, - apply_to=apply_to) + log.debug("Policy exists but needs updating") + result = __salt__["rabbitmq.set_policy"]( + vhost, + name, + pattern, + definition, + priority=priority, + runas=runas, + apply_to=apply_to, + ) - if 'Error' in result: - ret['result'] = False - ret['comment'] = result['Error'] - elif ret['changes'] == {}: - ret['comment'] = '\'{0}\' is already in the desired state.'.format(name) - elif __opts__['test']: - ret['result'] = None - elif 'Set' in result: - ret['comment'] = result['Set'] + if "Error" in result: + ret["result"] = False + ret["comment"] = result["Error"] + elif ret["changes"] == {}: + ret["comment"] = "'{0}' is already in the desired state.".format(name) + elif __opts__["test"]: + ret["result"] = None + elif "Set" in result: + ret["comment"] = result["Set"] return ret -def absent(name, - vhost='/', - runas=None): - ''' +def absent(name, vhost="/", runas=None): + """ Ensure the named policy is absent Reference: http://www.rabbitmq.com/ha.html @@ -136,30 +140,29 @@ def absent(name, The name of the policy to remove runas Name of the user to run the command as - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - policy_exists = __salt__['rabbitmq.policy_exists']( - vhost, name, runas=runas) + policy_exists = __salt__["rabbitmq.policy_exists"](vhost, name, runas=runas) if not policy_exists: - ret['comment'] = 'Policy \'{0} {1}\' is not present.'.format(vhost, name) + ret["comment"] = "Policy '{0} {1}' is not present.".format(vhost, name) return ret - if not __opts__['test']: - result = __salt__['rabbitmq.delete_policy'](vhost, name, runas=runas) - if 'Error' in result: - ret['result'] = False - ret['comment'] = result['Error'] + if not __opts__["test"]: + result = __salt__["rabbitmq.delete_policy"](vhost, name, runas=runas) + if "Error" in result: + ret["result"] = False + ret["comment"] = result["Error"] return ret - elif 'Deleted' in result: - ret['comment'] = 'Deleted' + elif "Deleted" in result: + ret["comment"] = "Deleted" # If we've reached this far before returning, we have changes. - ret['changes'] = {'new': '', 'old': name} + ret["changes"] = {"new": "", "old": name} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Policy \'{0} {1}\' will be removed.'.format(vhost, name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Policy '{0} {1}' will be removed.".format(vhost, name) return ret diff --git a/salt/states/rabbitmq_upstream.py b/salt/states/rabbitmq_upstream.py index a9890af650d..8f058830f1e 100644 --- a/salt/states/rabbitmq_upstream.py +++ b/salt/states/rabbitmq_upstream.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage RabbitMQ Upstreams ========================= @@ -16,12 +16,13 @@ Example: - max_hops: 1 .. versionadded:: 3000 -''' +""" # Import python libs from __future__ import absolute_import -import logging + import json +import logging # Import salt libs import salt.utils.data @@ -32,30 +33,36 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the appropriate rabbitmq module functions are loaded. - ''' - requirements = ['rabbitmq.list_upstreams', - 'rabbitmq.upstream_exists', - 'rabbitmq.set_upstream', - 'rabbitmq.delete_upstream'] - return all(req in __salt__ for req in requirements) + """ + requirements = [ + "rabbitmq.list_upstreams", + "rabbitmq.upstream_exists", + "rabbitmq.set_upstream", + "rabbitmq.delete_upstream", + ] + if all(req in __salt__ for req in requirements): + return True + return (False, "rabbitmq module could not be loaded") -def present(name, - uri, - prefetch_count=None, - reconnect_delay=None, - ack_mode=None, - trust_user_id=None, - exchange=None, - max_hops=None, - expires=None, - message_ttl=None, - ha_policy=None, - queue=None, - runas=None): - ''' +def present( + name, + uri, + prefetch_count=None, + reconnect_delay=None, + ack_mode=None, + trust_user_id=None, + exchange=None, + max_hops=None, + expires=None, + message_ttl=None, + ha_policy=None, + queue=None, + runas=None, +): + """ Ensure the RabbitMQ upstream exists. :param str name: The name of the upstream connection @@ -112,48 +119,50 @@ def present(name, .. versionadded:: 3000 - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} action = None try: - current_upstreams = __salt__['rabbitmq.list_upstreams'](runas=runas) + current_upstreams = __salt__["rabbitmq.list_upstreams"](runas=runas) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret - new_config = salt.utils.data.filter_falsey({ - 'uri': uri, - 'prefetch-count': prefetch_count, - 'reconnect-delay': reconnect_delay, - 'ack-mode': ack_mode, - 'trust-user-id': trust_user_id, - 'exchange': exchange, - 'max-hops': max_hops, - 'expires': expires, - 'message-ttl': message_ttl, - 'ha-policy': ha_policy, - 'queue': queue, - }) + new_config = salt.utils.data.filter_falsey( + { + "uri": uri, + "prefetch-count": prefetch_count, + "reconnect-delay": reconnect_delay, + "ack-mode": ack_mode, + "trust-user-id": trust_user_id, + "exchange": exchange, + "max-hops": max_hops, + "expires": expires, + "message-ttl": message_ttl, + "ha-policy": ha_policy, + "queue": queue, + } + ) if name in current_upstreams: - current_config = json.loads(current_upstreams.get(name, '')) + current_config = json.loads(current_upstreams.get(name, "")) diff_config = salt.utils.dictdiffer.deep_diff(current_config, new_config) if diff_config: - action = 'update' + action = "update" else: - ret['result'] = True - ret['comment'] = 'Upstream "{}" already present as specified.'.format(name) + ret["result"] = True + ret["comment"] = 'Upstream "{}" already present as specified.'.format(name) else: - action = 'create' - diff_config = {'old': None, 'new': new_config} + action = "create" + diff_config = {"old": None, "new": new_config} if action: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Upstream "{}" would have been {}d.'.format(name, action) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = 'Upstream "{}" would have been {}d.'.format(name, action) else: try: - res = __salt__['rabbitmq.set_upstream']( + res = __salt__["rabbitmq.set_upstream"]( name, uri, prefetch_count=prefetch_count, @@ -166,46 +175,47 @@ def present(name, message_ttl=message_ttl, ha_policy=ha_policy, queue=queue, - runas=runas) - ret['result'] = res - ret['comment'] = 'Upstream "{}" {}d.'.format(name, action) - ret['changes'] = diff_config + runas=runas, + ) + ret["result"] = res + ret["comment"] = 'Upstream "{}" {}d.'.format(name, action) + ret["changes"] = diff_config except CommandExecutionError as exp: - ret['comment'] = 'Error trying to {} upstream: {}'.format(action, exp) + ret["comment"] = "Error trying to {} upstream: {}".format(action, exp) return ret def absent(name, runas=None): - ''' + """ Ensure the named upstream is absent. :param str name: The name of the upstream to remove :param str runas: User to run the command .. versionadded:: 3000 - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} try: - upstream_exists = __salt__['rabbitmq.upstream_exists'](name, runas=runas) + upstream_exists = __salt__["rabbitmq.upstream_exists"](name, runas=runas) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if upstream_exists: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Upstream "{}" would have been deleted.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = 'Upstream "{}" would have been deleted.'.format(name) else: try: - res = __salt__['rabbitmq.delete_upstream'](name, runas=runas) + res = __salt__["rabbitmq.delete_upstream"](name, runas=runas) if res: - ret['result'] = True - ret['comment'] = 'Upstream "{}" has been deleted.'.format(name) - ret['changes'] = {'old': name, 'new': None} + ret["result"] = True + ret["comment"] = 'Upstream "{}" has been deleted.'.format(name) + ret["changes"] = {"old": name, "new": None} except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) else: - ret['result'] = True - ret['comment'] = 'The upstream "{}" is already absent.'.format(name) + ret["result"] = True + ret["comment"] = 'The upstream "{}" is already absent.'.format(name) return ret diff --git a/salt/states/rabbitmq_user.py b/salt/states/rabbitmq_user.py index 69434395e4c..d1b8bd4273e 100644 --- a/salt/states/rabbitmq_user.py +++ b/salt/states/rabbitmq_user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage RabbitMQ Users ===================== @@ -20,39 +20,42 @@ Example: - '.*' - '.*' - runas: rabbitmq -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs import salt.utils.path -from salt.ext import six from salt.exceptions import CommandExecutionError +from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if RabbitMQ is installed. - ''' - return salt.utils.path.which('rabbitmqctl') is not None + """ + if salt.utils.path.which("rabbitmqctl"): + return True + return (False, "Command not found: rabbitmqctl") def _check_perms_changes(name, newperms, runas=None, existing=None): - ''' + """ Check whether Rabbitmq user's permissions need to be changed. - ''' + """ if not newperms: return False if existing is None: try: - existing = __salt__['rabbitmq.list_user_permissions'](name, runas=runas) + existing = __salt__["rabbitmq.list_user_permissions"](name, runas=runas) except CommandExecutionError as err: - log.error('Error: %s', err) + log.error("Error: %s", err) return False perm_need_change = False @@ -64,7 +67,7 @@ def _check_perms_changes(name, newperms, runas=None, existing=None): # This checks for setting permissions to nothing in the state, # when previous state runs have already set permissions to # nothing. We don't want to report a change in this case. - if existing_vhost == '' and perms == ['', '', '']: + if existing_vhost == "" and perms == ["", "", ""]: continue perm_need_change = True else: @@ -74,23 +77,18 @@ def _check_perms_changes(name, newperms, runas=None, existing=None): def _get_current_tags(name, runas=None): - ''' + """ Whether Rabbitmq user's tags need to be changed - ''' + """ try: - return list(__salt__['rabbitmq.list_users'](runas=runas)[name]) + return list(__salt__["rabbitmq.list_users"](runas=runas)[name]) except CommandExecutionError as err: - log.error('Error: %s', err) + log.error("Error: %s", err) return [] -def present(name, - password=None, - force=False, - tags=None, - perms=(), - runas=None): - ''' +def present(name, password=None, force=False, tags=None, perms=(), runas=None): + """ Ensure the RabbitMQ user exists. name @@ -105,73 +103,74 @@ def present(name, A list of dicts with vhost keys and 3-tuple values runas Name of the user to run the command - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} try: - user = __salt__['rabbitmq.user_exists'](name, runas=runas) + user = __salt__["rabbitmq.user_exists"](name, runas=runas) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret passwd_reqs_update = False if user and password is not None: try: - if not __salt__['rabbitmq.check_password'](name, - password, runas=runas): + if not __salt__["rabbitmq.check_password"](name, password, runas=runas): passwd_reqs_update = True - log.debug('RabbitMQ user %s password update required', name) + log.debug("RabbitMQ user %s password update required", name) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if user and not any((force, perms, tags, passwd_reqs_update)): - log.debug(('RabbitMQ user \'%s\' exists, password is up to' - ' date and force is not set.'), name) - ret['comment'] = 'User \'{0}\' is already present.'.format(name) - ret['result'] = True + log.debug( + ( + "RabbitMQ user '%s' exists, password is up to" + " date and force is not set." + ), + name, + ) + ret["comment"] = "User '{0}' is already present.".format(name) + ret["result"] = True return ret if not user: - ret['changes'].update({'user': - {'old': '', - 'new': name}}) - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User \'{0}\' is set to be created.'.format(name) + ret["changes"].update({"user": {"old": "", "new": name}}) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User '{0}' is set to be created.".format(name) return ret - log.debug( - 'RabbitMQ user \'%s\' doesn\'t exist - Creating.', name) + log.debug("RabbitMQ user '%s' doesn't exist - Creating.", name) try: - __salt__['rabbitmq.add_user'](name, password, runas=runas) + __salt__["rabbitmq.add_user"](name, password, runas=runas) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret else: - log.debug('RabbitMQ user \'%s\' exists', name) + log.debug("RabbitMQ user '%s' exists", name) if force or passwd_reqs_update: if password is not None: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['rabbitmq.change_password'](name, password, runas=runas) + __salt__["rabbitmq.change_password"]( + name, password, runas=runas + ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({'password': - {'old': '', - 'new': 'Set password.'}}) + ret["changes"].update({"password": {"old": "", "new": "Set password."}}) else: - if not __opts__['test']: - log.debug('Password for %s is not set - Clearing password.', name) + if not __opts__["test"]: + log.debug("Password for %s is not set - Clearing password.", name) try: - __salt__['rabbitmq.clear_password'](name, runas=runas) + __salt__["rabbitmq.clear_password"](name, runas=runas) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({'password': - {'old': 'Removed password.', - 'new': ''}}) + ret["changes"].update( + {"password": {"old": "Removed password.", "new": ""}} + ) if tags is not None: current_tags = _get_current_tags(name, runas=runas) @@ -180,93 +179,86 @@ def present(name, # Diff the tags sets. Symmetric difference operator ^ will give us # any element in one set, but not both if set(tags) ^ set(current_tags): - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['rabbitmq.set_user_tags'](name, tags, runas=runas) + __salt__["rabbitmq.set_user_tags"](name, tags, runas=runas) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({'tags': - {'old': current_tags, - 'new': tags}}) + ret["changes"].update({"tags": {"old": current_tags, "new": tags}}) try: - existing_perms = __salt__['rabbitmq.list_user_permissions'](name, runas=runas) + existing_perms = __salt__["rabbitmq.list_user_permissions"](name, runas=runas) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if _check_perms_changes(name, perms, runas=runas, existing=existing_perms): for vhost_perm in perms: for vhost, perm in six.iteritems(vhost_perm): - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['rabbitmq.set_permissions']( + __salt__["rabbitmq.set_permissions"]( vhost, name, perm[0], perm[1], perm[2], runas=runas ) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret new_perms = {vhost: perm} if existing_perms != new_perms: - if ret['changes'].get('perms') is None: - ret['changes'].update({'perms': - {'old': {}, - 'new': {}}}) - ret['changes']['perms']['old'].update(existing_perms) - ret['changes']['perms']['new'].update(new_perms) + if ret["changes"].get("perms") is None: + ret["changes"].update({"perms": {"old": {}, "new": {}}}) + ret["changes"]["perms"]["old"].update(existing_perms) + ret["changes"]["perms"]["new"].update(new_perms) - ret['result'] = True - if ret['changes'] == {}: - ret['comment'] = '\'{0}\' is already in the desired state.'.format(name) + ret["result"] = True + if ret["changes"] == {}: + ret["comment"] = "'{0}' is already in the desired state.".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Configuration for \'{0}\' will change.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Configuration for '{0}' will change.".format(name) return ret - ret['comment'] = '\'{0}\' was configured.'.format(name) + ret["comment"] = "'{0}' was configured.".format(name) return ret -def absent(name, - runas=None): - ''' +def absent(name, runas=None): + """ Ensure the named user is absent name The name of the user to remove runas User to run the command - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} try: - user_exists = __salt__['rabbitmq.user_exists'](name, runas=runas) + user_exists = __salt__["rabbitmq.user_exists"](name, runas=runas) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret if user_exists: - if not __opts__['test']: + if not __opts__["test"]: try: - __salt__['rabbitmq.delete_user'](name, runas=runas) + __salt__["rabbitmq.delete_user"](name, runas=runas) except CommandExecutionError as err: - ret['comment'] = 'Error: {0}'.format(err) + ret["comment"] = "Error: {0}".format(err) return ret - ret['changes'].update({'name': - {'old': name, - 'new': ''}}) + ret["changes"].update({"name": {"old": name, "new": ""}}) else: - ret['result'] = True - ret['comment'] = 'The user \'{0}\' is not present.'.format(name) + ret["result"] = True + ret["comment"] = "The user '{0}' is not present.".format(name) return ret - if __opts__['test'] and ret['changes']: - ret['result'] = None - ret['comment'] = 'The user \'{0}\' will be removed.'.format(name) + if __opts__["test"] and ret["changes"]: + ret["result"] = None + ret["comment"] = "The user '{0}' will be removed.".format(name) return ret - ret['result'] = True - ret['comment'] = 'The user \'{0}\' was removed.'.format(name) + ret["result"] = True + ret["comment"] = "The user '{0}' was removed.".format(name) return ret diff --git a/salt/states/rabbitmq_vhost.py b/salt/states/rabbitmq_vhost.py index b5bec720448..30ab5426a0c 100644 --- a/salt/states/rabbitmq_vhost.py +++ b/salt/states/rabbitmq_vhost.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage RabbitMQ Virtual Hosts ============================= @@ -13,10 +13,11 @@ Example: - conf: .* - write: .* - read: .* -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -26,14 +27,16 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if RabbitMQ is installed. - ''' - return salt.utils.path.which('rabbitmqctl') is not None + """ + if salt.utils.path.which("rabbitmqctl"): + return True + return (False, "Command not found: rabbitmqctl") def present(name): - ''' + """ Ensure the RabbitMQ VHost exists. name @@ -65,36 +68,36 @@ def present(name): Name of the user to run the command .. deprecated:: 2015.8.0 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - vhost_exists = __salt__['rabbitmq.vhost_exists'](name) + vhost_exists = __salt__["rabbitmq.vhost_exists"](name) if vhost_exists: - ret['comment'] = 'Virtual Host \'{0}\' already exists.'.format(name) + ret["comment"] = "Virtual Host '{0}' already exists.".format(name) return ret - if not __opts__['test']: - result = __salt__['rabbitmq.add_vhost'](name) - if 'Error' in result: - ret['result'] = False - ret['comment'] = result['Error'] + if not __opts__["test"]: + result = __salt__["rabbitmq.add_vhost"](name) + if "Error" in result: + ret["result"] = False + ret["comment"] = result["Error"] return ret - elif 'Added' in result: - ret['comment'] = result['Added'] + elif "Added" in result: + ret["comment"] = result["Added"] # If we've reached this far before returning, we have changes. - ret['changes'] = {'old': '', 'new': name} + ret["changes"] = {"old": "", "new": name} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Virtual Host \'{0}\' will be created.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Virtual Host '{0}' will be created.".format(name) return ret def absent(name): - ''' + """ Ensure the RabbitMQ Virtual Host is absent name @@ -103,29 +106,29 @@ def absent(name): User to run the command .. deprecated:: 2015.8.0 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - vhost_exists = __salt__['rabbitmq.vhost_exists'](name) + vhost_exists = __salt__["rabbitmq.vhost_exists"](name) if not vhost_exists: - ret['comment'] = 'Virtual Host \'{0}\' is not present.'.format(name) + ret["comment"] = "Virtual Host '{0}' is not present.".format(name) return ret - if not __opts__['test']: - result = __salt__['rabbitmq.delete_vhost'](name) - if 'Error' in result: - ret['result'] = False - ret['comment'] = result['Error'] + if not __opts__["test"]: + result = __salt__["rabbitmq.delete_vhost"](name) + if "Error" in result: + ret["result"] = False + ret["comment"] = result["Error"] return ret - elif 'Deleted' in result: - ret['comment'] = result['Deleted'] + elif "Deleted" in result: + ret["comment"] = result["Deleted"] # If we've reached this far before returning, we have changes. - ret['changes'] = {'new': '', 'old': name} + ret["changes"] = {"new": "", "old": name} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Virtual Host \'{0}\' will be removed.'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Virtual Host '{0}' will be removed.".format(name) return ret diff --git a/salt/states/rbac_solaris.py b/salt/states/rbac_solaris.py index 2bd1268ced7..f6e1ff337ae 100644 --- a/salt/states/rbac_solaris.py +++ b/salt/states/rbac_solaris.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Solaris RBAC :maintainer: Jorge Schrauwen <sjorge@blackdot.be> @@ -19,8 +19,8 @@ Management of Solaris RBAC - System Power - authorizations: - solaris.audit.* -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -28,28 +28,28 @@ import logging log = logging.getLogger(__name__) # Define the state's virtual name -__virtualname__ = 'rbac' +__virtualname__ = "rbac" def __virtual__(): - ''' + """ Provides rbac on Solaris like platforms - ''' - if 'rbac.profile_list' in __salt__ and \ - 'user.list_users' in __salt__ and \ - __grains__['kernel'] == 'SunOS': + """ + if ( + "rbac.profile_list" in __salt__ + and "user.list_users" in __salt__ + and __grains__["kernel"] == "SunOS" + ): return True else: return ( False, - '{0} state module can only be loaded on Solaris'.format( - __virtualname__ - ) + "{0} state module can only be loaded on Solaris".format(__virtualname__), ) def managed(name, roles=None, profiles=None, authorizations=None): - ''' + """ Manage RBAC properties for user name : string @@ -67,118 +67,134 @@ def managed(name, roles=None, profiles=None, authorizations=None): Set the property to `None` to not manage it. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ## check properties - if name not in __salt__['user.list_users'](): - ret['result'] = False - ret['comment'] = 'User {0} does not exist!'.format(name) + if name not in __salt__["user.list_users"](): + ret["result"] = False + ret["comment"] = "User {0} does not exist!".format(name) return ret if roles and not isinstance(roles, (list)): - ret['result'] = False - ret['comment'] = 'Property roles is not None or list!' + ret["result"] = False + ret["comment"] = "Property roles is not None or list!" return ret if profiles and not isinstance(profiles, (list)): - ret['result'] = False - ret['comment'] = 'Property profiles is not None or list!' + ret["result"] = False + ret["comment"] = "Property profiles is not None or list!" return ret if authorizations and not isinstance(authorizations, (list)): - ret['result'] = False - ret['comment'] = 'Property authorizations is not None or list!' + ret["result"] = False + ret["comment"] = "Property authorizations is not None or list!" return ret - log.debug('rbac.managed - roles=%s, profiles=%s, authorizations=%s', - roles, profiles, authorizations + log.debug( + "rbac.managed - roles=%s, profiles=%s, authorizations=%s", + roles, + profiles, + authorizations, ) ## update roles if isinstance(roles, (list)): # compute changed - roles_current = __salt__['rbac.role_get'](name) + roles_current = __salt__["rbac.role_get"](name) roles_add = [r for r in roles if r not in roles_current] roles_rm = [r for r in roles_current if r not in roles] # execute and verify changes if roles_add: - res_roles_add = __salt__['rbac.role_add'](name, ','.join(roles_add).strip()) - roles_current = __salt__['rbac.role_get'](name) + res_roles_add = __salt__["rbac.role_add"](name, ",".join(roles_add).strip()) + roles_current = __salt__["rbac.role_get"](name) for role in roles_add: - if 'roles' not in ret['changes']: - ret['changes']['roles'] = {} - ret['changes']['roles'][role] = 'Added' if role in roles_current else 'Failed' - if ret['changes']['roles'][role] == 'Failed': - ret['result'] = False + if "roles" not in ret["changes"]: + ret["changes"]["roles"] = {} + ret["changes"]["roles"][role] = ( + "Added" if role in roles_current else "Failed" + ) + if ret["changes"]["roles"][role] == "Failed": + ret["result"] = False if roles_rm: - res_roles_rm = __salt__['rbac.role_rm'](name, ','.join(roles_rm).strip()) + res_roles_rm = __salt__["rbac.role_rm"](name, ",".join(roles_rm).strip()) - roles_current = __salt__['rbac.role_get'](name) + roles_current = __salt__["rbac.role_get"](name) for role in roles_rm: - if 'roles' not in ret['changes']: - ret['changes']['roles'] = {} - ret['changes']['roles'][role] = 'Removed' if role not in roles_current else 'Failed' - if ret['changes']['roles'][role] == 'Failed': - ret['result'] = False + if "roles" not in ret["changes"]: + ret["changes"]["roles"] = {} + ret["changes"]["roles"][role] = ( + "Removed" if role not in roles_current else "Failed" + ) + if ret["changes"]["roles"][role] == "Failed": + ret["result"] = False ## update profiles if isinstance(profiles, (list)): # compute changed - profiles_current = __salt__['rbac.profile_get'](name) + profiles_current = __salt__["rbac.profile_get"](name) profiles_add = [r for r in profiles if r not in profiles_current] profiles_rm = [r for r in profiles_current if r not in profiles] # execute and verify changes if profiles_add: - res_profiles_add = __salt__['rbac.profile_add'](name, ','.join(profiles_add).strip()) - profiles_current = __salt__['rbac.profile_get'](name) + res_profiles_add = __salt__["rbac.profile_add"]( + name, ",".join(profiles_add).strip() + ) + profiles_current = __salt__["rbac.profile_get"](name) for profile in profiles_add: - if 'profiles' not in ret['changes']: - ret['changes']['profiles'] = {} - ret['changes']['profiles'][profile] = 'Added' if profile in profiles_current else 'Failed' - if ret['changes']['profiles'][profile] == 'Failed': - ret['result'] = False + if "profiles" not in ret["changes"]: + ret["changes"]["profiles"] = {} + ret["changes"]["profiles"][profile] = ( + "Added" if profile in profiles_current else "Failed" + ) + if ret["changes"]["profiles"][profile] == "Failed": + ret["result"] = False if profiles_rm: - res_profiles_rm = __salt__['rbac.profile_rm'](name, ','.join(profiles_rm).strip()) + res_profiles_rm = __salt__["rbac.profile_rm"]( + name, ",".join(profiles_rm).strip() + ) - profiles_current = __salt__['rbac.profile_get'](name) + profiles_current = __salt__["rbac.profile_get"](name) for profile in profiles_rm: - if 'profiles' not in ret['changes']: - ret['changes']['profiles'] = {} - ret['changes']['profiles'][profile] = 'Removed' if profile not in profiles_current else 'Failed' - if ret['changes']['profiles'][profile] == 'Failed': - ret['result'] = False + if "profiles" not in ret["changes"]: + ret["changes"]["profiles"] = {} + ret["changes"]["profiles"][profile] = ( + "Removed" if profile not in profiles_current else "Failed" + ) + if ret["changes"]["profiles"][profile] == "Failed": + ret["result"] = False ## update auths if isinstance(authorizations, (list)): # compute changed - auths_current = __salt__['rbac.auth_get'](name, False) + auths_current = __salt__["rbac.auth_get"](name, False) auths_add = [r for r in authorizations if r not in auths_current] auths_rm = [r for r in auths_current if r not in authorizations] # execute and verify changes if auths_add: - res_auths_add = __salt__['rbac.auth_add'](name, ','.join(auths_add).strip()) - auths_current = __salt__['rbac.auth_get'](name) + res_auths_add = __salt__["rbac.auth_add"](name, ",".join(auths_add).strip()) + auths_current = __salt__["rbac.auth_get"](name) for auth in auths_add: - if 'authorizations' not in ret['changes']: - ret['changes']['authorizations'] = {} - ret['changes']['authorizations'][auth] = 'Added' if auth in auths_current else 'Failed' - if ret['changes']['authorizations'][auth] == 'Failed': - ret['result'] = False + if "authorizations" not in ret["changes"]: + ret["changes"]["authorizations"] = {} + ret["changes"]["authorizations"][auth] = ( + "Added" if auth in auths_current else "Failed" + ) + if ret["changes"]["authorizations"][auth] == "Failed": + ret["result"] = False if auths_rm: - res_auths_rm = __salt__['rbac.auth_rm'](name, ','.join(auths_rm).strip()) + res_auths_rm = __salt__["rbac.auth_rm"](name, ",".join(auths_rm).strip()) - auths_current = __salt__['rbac.auth_get'](name) + auths_current = __salt__["rbac.auth_get"](name) for auth in auths_rm: - if 'authorizations' not in ret['changes']: - ret['changes']['authorizations'] = {} - ret['changes']['authorizations'][auth] = 'Removed' if auth not in auths_current else 'Failed' - if ret['changes']['authorizations'][auth] == 'Failed': - ret['result'] = False + if "authorizations" not in ret["changes"]: + ret["changes"]["authorizations"] = {} + ret["changes"]["authorizations"][auth] = ( + "Removed" if auth not in auths_current else "Failed" + ) + if ret["changes"]["authorizations"][auth] == "Failed": + ret["result"] = False return ret diff --git a/salt/states/rbenv.py b/salt/states/rbenv.py index af7eab203b4..c45b91c9fc7 100644 --- a/salt/states/rbenv.py +++ b/salt/states/rbenv.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Managing Ruby installations with rbenv ====================================== @@ -51,63 +51,64 @@ and 2.x using rbenv on Ubuntu/Debian: - default: True - require: - pkg: rbenv-deps -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import copy # Import python libs import re -import copy def _check_rbenv(ret, user=None): - ''' + """ Check to see if rbenv is installed. - ''' - if not __salt__['rbenv.is_installed'](user): - ret['result'] = False - ret['comment'] = 'Rbenv is not installed.' + """ + if not __salt__["rbenv.is_installed"](user): + ret["result"] = False + ret["comment"] = "Rbenv is not installed." return ret def _ruby_installed(ret, ruby, user=None): - ''' + """ Check to see if given ruby is installed. - ''' - default = __salt__['rbenv.default'](runas=user) - for version in __salt__['rbenv.versions'](user): + """ + default = __salt__["rbenv.default"](runas=user) + for version in __salt__["rbenv.versions"](user): if version == ruby: - ret['result'] = True - ret['comment'] = 'Requested ruby exists' - ret['default'] = default == ruby + ret["result"] = True + ret["comment"] = "Requested ruby exists" + ret["default"] = default == ruby break return ret def _check_and_install_ruby(ret, ruby, default=False, user=None): - ''' + """ Verify that ruby is installed, install if unavailable - ''' + """ ret = _ruby_installed(ret, ruby, user=user) - if not ret['result']: - if __salt__['rbenv.install_ruby'](ruby, runas=user): - ret['result'] = True - ret['changes'][ruby] = 'Installed' - ret['comment'] = 'Successfully installed ruby' - ret['default'] = default + if not ret["result"]: + if __salt__["rbenv.install_ruby"](ruby, runas=user): + ret["result"] = True + ret["changes"][ruby] = "Installed" + ret["comment"] = "Successfully installed ruby" + ret["default"] = default else: - ret['result'] = False - ret['comment'] = 'Failed to install ruby' + ret["result"] = False + ret["comment"] = "Failed to install ruby" return ret if default: - __salt__['rbenv.default'](ruby, runas=user) + __salt__["rbenv.default"](ruby, runas=user) return ret def installed(name, default=False, user=None): - ''' + """ Verify that the specified ruby is installed with rbenv. Rbenv is installed if necessary. @@ -123,57 +124,57 @@ def installed(name, default=False, user=None): .. versionadded:: 0.17.0 .. versionadded:: 0.16.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} rbenv_installed_ret = copy.deepcopy(ret) - if name.startswith('ruby-'): - name = re.sub(r'^ruby-', '', name) + if name.startswith("ruby-"): + name = re.sub(r"^ruby-", "", name) - if __opts__['test']: + if __opts__["test"]: ret = _ruby_installed(ret, name, user=user) - if not ret['result']: - ret['comment'] = 'Ruby {0} is set to be installed'.format(name) + if not ret["result"]: + ret["comment"] = "Ruby {0} is set to be installed".format(name) else: - ret['comment'] = 'Ruby {0} is already installed'.format(name) + ret["comment"] = "Ruby {0} is already installed".format(name) return ret rbenv_installed_ret = _check_and_install_rbenv(rbenv_installed_ret, user) - if rbenv_installed_ret['result'] is False: - ret['result'] = False - ret['comment'] = 'Rbenv failed to install' + if rbenv_installed_ret["result"] is False: + ret["result"] = False + ret["comment"] = "Rbenv failed to install" return ret else: return _check_and_install_ruby(ret, name, default, user=user) def _check_and_uninstall_ruby(ret, ruby, user=None): - ''' + """ Verify that ruby is uninstalled - ''' + """ ret = _ruby_installed(ret, ruby, user=user) - if ret['result']: - if ret['default']: - __salt__['rbenv.default']('system', runas=user) + if ret["result"]: + if ret["default"]: + __salt__["rbenv.default"]("system", runas=user) - if __salt__['rbenv.uninstall_ruby'](ruby, runas=user): - ret['result'] = True - ret['changes'][ruby] = 'Uninstalled' - ret['comment'] = 'Successfully removed ruby' + if __salt__["rbenv.uninstall_ruby"](ruby, runas=user): + ret["result"] = True + ret["changes"][ruby] = "Uninstalled" + ret["comment"] = "Successfully removed ruby" return ret else: - ret['result'] = False - ret['comment'] = 'Failed to uninstall ruby' + ret["result"] = False + ret["comment"] = "Failed to uninstall ruby" return ret else: - ret['result'] = True - ret['comment'] = 'Ruby {0} is already absent'.format(ruby) + ret["result"] = True + ret["comment"] = "Ruby {0} is already absent".format(ruby) return ret def absent(name, user=None): - ''' + """ Verify that the specified ruby is not installed with rbenv. Rbenv is installed if necessary. @@ -186,52 +187,52 @@ def absent(name, user=None): .. versionadded:: 0.17.0 .. versionadded:: 0.16.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if name.startswith('ruby-'): - name = re.sub(r'^ruby-', '', name) + if name.startswith("ruby-"): + name = re.sub(r"^ruby-", "", name) ret = _check_rbenv(ret, user) - if ret['result'] is False: - ret['result'] = True - ret['comment'] = 'Rbenv not installed, {0} not either'.format(name) + if ret["result"] is False: + ret["result"] = True + ret["comment"] = "Rbenv not installed, {0} not either".format(name) return ret else: - if __opts__['test']: + if __opts__["test"]: ret = _ruby_installed(ret, name, user=user) - if ret['result']: - ret['result'] = None - ret['comment'] = 'Ruby {0} is set to be uninstalled'.format(name) + if ret["result"]: + ret["result"] = None + ret["comment"] = "Ruby {0} is set to be uninstalled".format(name) else: - ret['result'] = True - ret['comment'] = 'Ruby {0} is already uninstalled'.format(name) + ret["result"] = True + ret["comment"] = "Ruby {0} is already uninstalled".format(name) return ret return _check_and_uninstall_ruby(ret, name, user=user) def _check_and_install_rbenv(ret, user=None): - ''' + """ Verify that rbenv is installed, install if unavailable - ''' + """ ret = _check_rbenv(ret, user) - if ret['result'] is False: - if __salt__['rbenv.install'](user): - ret['result'] = True - ret['comment'] = 'Rbenv installed' + if ret["result"] is False: + if __salt__["rbenv.install"](user): + ret["result"] = True + ret["comment"] = "Rbenv installed" else: - ret['result'] = False - ret['comment'] = 'Rbenv failed to install' + ret["result"] = False + ret["comment"] = "Rbenv failed to install" else: - ret['result'] = True - ret['comment'] = 'Rbenv is already installed' + ret["result"] = True + ret["comment"] = "Rbenv is already installed" return ret def install_rbenv(name, user=None): - ''' + """ Install rbenv if not installed. Allows you to require rbenv be installed prior to installing the plugins. Useful if you want to install rbenv plugins via the git or file modules and need them installed before @@ -242,17 +243,17 @@ def install_rbenv(name, user=None): user: None The user to run rbenv as. - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if __opts__['test']: + if __opts__["test"]: ret = _check_rbenv(ret, user=user) - if ret['result'] is False: - ret['result'] = None - ret['comment'] = 'Rbenv is set to be installed' + if ret["result"] is False: + ret["result"] = None + ret["comment"] = "Rbenv is set to be installed" else: - ret['result'] = True - ret['comment'] = 'Rbenv is already installed' + ret["result"] = True + ret["comment"] = "Rbenv is already installed" return ret return _check_and_install_rbenv(ret, user) diff --git a/salt/states/rdp.py b/salt/states/rdp.py index 5f3f959895c..10e8bcfc693 100644 --- a/salt/states/rdp.py +++ b/salt/states/rdp.py @@ -1,63 +1,59 @@ # -*- coding: utf-8 -*- -''' +""" Manage RDP Service on Windows servers -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Load only if network_win is loaded - ''' - return 'rdp' if 'rdp.enable' in __salt__ else False + """ + if "rdp.enable" in __salt__: + return "rdp" + return (False, "rdp module could not be loaded") def enabled(name): - ''' + """ Enable the RDP service and make sure access to the RDP port is allowed in the firewall configuration - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - stat = __salt__['rdp.status']() + stat = __salt__["rdp.status"]() if not stat: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'RDP will be enabled' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "RDP will be enabled" return ret - ret['result'] = __salt__['rdp.enable']() - ret['changes'] = {'RDP was enabled': True} + ret["result"] = __salt__["rdp.enable"]() + ret["changes"] = {"RDP was enabled": True} return ret - ret['comment'] = 'RDP is enabled' + ret["comment"] = "RDP is enabled" return ret def disabled(name): - ''' + """ Disable the RDP service - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - stat = __salt__['rdp.status']() + stat = __salt__["rdp.status"]() if stat: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'RDP will be disabled' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "RDP will be disabled" return ret - ret['result'] = __salt__['rdp.disable']() - ret['changes'] = {'RDP was disabled': True} + ret["result"] = __salt__["rdp.disable"]() + ret["changes"] = {"RDP was disabled": True} return ret - ret['comment'] = 'RDP is disabled' + ret["comment"] = "RDP is disabled" return ret diff --git a/salt/states/redismod.py b/salt/states/redismod.py index 7265783241c..7c62940fb01 100644 --- a/salt/states/redismod.py +++ b/salt/states/redismod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Redis server ========================== @@ -27,24 +27,25 @@ overridden in states using the following arguments: ``host``, ``post``, ``db``, - port: 6379 - db: 0 - password: somuchkittycat -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + import copy -__virtualname__ = 'redis' +__virtualname__ = "redis" def __virtual__(): - ''' + """ Only load if the redis module is in __salt__ - ''' - if 'redis.set_key' in __salt__: + """ + if "redis.set_key" in __salt__: return __virtualname__ - return False + return (False, "redis module could not be loaded") def string(name, value, expire=None, expireat=None, **connection_args): - ''' + """ Ensure that the key exists in redis with the value specified name @@ -58,31 +59,33 @@ def string(name, value, expire=None, expireat=None, **connection_args): expireat Sets expiration time for key via UNIX timestamp, overrides `expire` - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Key already set to defined value'} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Key already set to defined value", + } - old_key = __salt__['redis.get_key'](name, **connection_args) + old_key = __salt__["redis.get_key"](name, **connection_args) if old_key != value: - __salt__['redis.set_key'](name, value, **connection_args) - ret['changes'][name] = 'Value updated' - ret['comment'] = 'Key updated to new value' + __salt__["redis.set_key"](name, value, **connection_args) + ret["changes"][name] = "Value updated" + ret["comment"] = "Key updated to new value" if expireat: - __salt__['redis.expireat'](name, expireat, **connection_args) - ret['changes']['expireat'] = 'Key expires at {0}'.format(expireat) + __salt__["redis.expireat"](name, expireat, **connection_args) + ret["changes"]["expireat"] = "Key expires at {0}".format(expireat) elif expire: - __salt__['redis.expire'](name, expire, **connection_args) - ret['changes']['expire'] = 'TTL set to {0} seconds'.format(expire) + __salt__["redis.expire"](name, expire, **connection_args) + ret["changes"]["expire"] = "TTL set to {0} seconds".format(expire) return ret def absent(name, keys=None, **connection_args): - ''' + """ Ensure key absent from redis name @@ -90,35 +93,44 @@ def absent(name, keys=None, **connection_args): keys list of keys to ensure absent, name will be ignored if this is used - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Key(s) specified already absent'} + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Key(s) specified already absent", + } if keys: if not isinstance(keys, list): - ret['result'] = False - ret['comment'] = '`keys` not formed as a list type' + ret["result"] = False + ret["comment"] = "`keys` not formed as a list type" return ret - delete_list = [key for key in keys - if __salt__['redis.exists'](key, **connection_args)] + delete_list = [ + key for key in keys if __salt__["redis.exists"](key, **connection_args) + ] if not delete_list: return ret - __salt__['redis.delete'](*delete_list, **connection_args) - ret['changes']['deleted'] = delete_list - ret['comment'] = 'Keys deleted' + __salt__["redis.delete"](*delete_list, **connection_args) + ret["changes"]["deleted"] = delete_list + ret["comment"] = "Keys deleted" return ret - if __salt__['redis.exists'](name, **connection_args): - __salt__['redis.delete'](name, **connection_args) - ret['comment'] = 'Key deleted' - ret['changes']['deleted'] = [name] + if __salt__["redis.exists"](name, **connection_args): + __salt__["redis.delete"](name, **connection_args) + ret["comment"] = "Key deleted" + ret["changes"]["deleted"] = [name] return ret -def slaveof(name, sentinel_host=None, sentinel_port=None, sentinel_password=None, **connection_args): - ''' +def slaveof( + name, + sentinel_host=None, + sentinel_port=None, + sentinel_password=None, + **connection_args +): + """ Set this redis instance as a slave. .. versionadded: 2016.3.0 @@ -132,42 +144,48 @@ def slaveof(name, sentinel_host=None, sentinel_port=None, sentinel_password=None sentinel_port Port of the sentinel to check for the master - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Failed to setup slave'} + """ + ret = { + "name": name, + "changes": {}, + "result": False, + "comment": "Failed to setup slave", + } kwargs = copy.copy(connection_args) - sentinel_master = __salt__['redis.sentinel_get_master_ip'](name, sentinel_host, sentinel_port, sentinel_password) - if sentinel_master['master_host'] in __salt__['network.ip_addrs'](): - ret['result'] = True - ret['comment'] = 'Minion is the master: {0}'.format(name) + sentinel_master = __salt__["redis.sentinel_get_master_ip"]( + name, sentinel_host, sentinel_port, sentinel_password + ) + if sentinel_master["master_host"] in __salt__["network.ip_addrs"](): + ret["result"] = True + ret["comment"] = "Minion is the master: {0}".format(name) return ret - first_master = __salt__['redis.get_master_ip'](**connection_args) + first_master = __salt__["redis.get_master_ip"](**connection_args) if first_master == sentinel_master: - ret['result'] = True - ret['comment'] = 'Minion already slave of master: {0}'.format(name) + ret["result"] = True + ret["comment"] = "Minion already slave of master: {0}".format(name) return ret - if __opts__['test'] is True: - ret['comment'] = 'Minion will be made a slave of {0}: {1}'.format(name, sentinel_master['host']) - ret['result'] = None + if __opts__["test"] is True: + ret["comment"] = "Minion will be made a slave of {0}: {1}".format( + name, sentinel_master["host"] + ) + ret["result"] = None return ret kwargs.update(**sentinel_master) - __salt__['redis.slaveof'](**kwargs) + __salt__["redis.slaveof"](**kwargs) - current_master = __salt__['redis.get_master_ip'](**connection_args) + current_master = __salt__["redis.get_master_ip"](**connection_args) if current_master != sentinel_master: return ret - ret['result'] = True - ret['changes'] = { - 'old': first_master, - 'new': current_master, + ret["result"] = True + ret["changes"] = { + "old": first_master, + "new": current_master, } - ret['comment'] = 'Minion successfully connected to master: {0}'.format(name) + ret["comment"] = "Minion successfully connected to master: {0}".format(name) return ret diff --git a/salt/states/reg.py b/salt/states/reg.py index af3510aabc9..53c39d17da7 100644 --- a/salt/states/reg.py +++ b/salt/states/reg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Manage the Windows registry =========================== @@ -68,60 +68,73 @@ Value: - There are 3 value names: ``RTHDVCPL``, ``NvBackend``, and ``BTMTrayAgent`` - Each value name has a corresponding value -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging + import salt.utils.stringutils log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Load this state if the reg module exists - ''' - if 'reg.read_value' not in __utils__: - return (False, 'reg state module failed to load: ' - 'missing util function: reg.read_value') + """ + if "reg.read_value" not in __utils__: + return ( + False, + "reg state module failed to load: missing util function: reg.read_value", + ) - if 'reg.set_value' not in __utils__: - return (False, 'reg state module failed to load: ' - 'missing util function: reg.set_value') + if "reg.set_value" not in __utils__: + return ( + False, + "reg state module failed to load: missing util function: reg.set_value", + ) - if 'reg.delete_value' not in __utils__: - return (False, 'reg state module failed to load: ' - 'missing util function: reg.delete_value') + if "reg.delete_value" not in __utils__: + return ( + False, + "reg state module failed to load: " + "missing util function: reg.delete_value", + ) - if 'reg.delete_key_recursive' not in __utils__: - return (False, 'reg state module failed to load: ' - 'missing util function: reg.delete_key_recursive') + if "reg.delete_key_recursive" not in __utils__: + return ( + False, + "reg state module failed to load: " + "missing util function: reg.delete_key_recursive", + ) - return 'reg' + return "reg" def _parse_key(key): - ''' + """ split the hive from the key - ''' + """ splt = key.split("\\") hive = splt.pop(0) - key = '\\'.join(splt) + key = "\\".join(splt) return hive, key -def present(name, - vname=None, - vdata=None, - vtype='REG_SZ', - use_32bit_registry=False, - win_owner=None, - win_perms=None, - win_deny_perms=None, - win_inheritance=True, - win_perms_reset=False): - r''' +def present( + name, + vname=None, + vdata=None, + vtype="REG_SZ", + use_32bit_registry=False, + win_owner=None, + win_perms=None, + win_deny_perms=None, + win_inheritance=True, + win_perms_reset=False, +): + r""" Ensure a registry key or value is present. Args: @@ -381,86 +394,89 @@ def present(name, applies_to: this_key_subkeys - win_inheritance: True - win_perms_reset: True - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'pchanges': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "pchanges": {}, "comment": ""} hive, key = _parse_key(name) # Determine what to do - reg_current = __utils__['reg.read_value'](hive=hive, - key=key, - vname=vname, - use_32bit_registry=use_32bit_registry) + reg_current = __utils__["reg.read_value"]( + hive=hive, key=key, vname=vname, use_32bit_registry=use_32bit_registry + ) # Cast the vdata according to the vtype - vdata_decoded = __utils__['reg.cast_vdata'](vdata=vdata, vtype=vtype) + vdata_decoded = __utils__["reg.cast_vdata"](vdata=vdata, vtype=vtype) # Check if the key already exists # If so, check perms # We check `vdata` and `success` because `vdata` can be None - if vdata_decoded == reg_current['vdata'] and reg_current['success']: - ret['comment'] = '{0} in {1} is already present' \ - ''.format(salt.utils.stringutils.to_unicode(vname, 'utf-8') if vname else '(Default)', - salt.utils.stringutils.to_unicode(name, 'utf-8')) - return __utils__['dacl.check_perms']( - obj_name='\\'.join([hive, key]), - obj_type='registry32' if use_32bit_registry else 'registry', + if vdata_decoded == reg_current["vdata"] and reg_current["success"]: + ret["comment"] = "{0} in {1} is already present" "".format( + salt.utils.stringutils.to_unicode(vname, "utf-8") if vname else "(Default)", + salt.utils.stringutils.to_unicode(name, "utf-8"), + ) + return __utils__["dacl.check_perms"]( + obj_name="\\".join([hive, key]), + obj_type="registry32" if use_32bit_registry else "registry", ret=ret, owner=win_owner, grant_perms=win_perms, deny_perms=win_deny_perms, inheritance=win_inheritance, - reset=win_perms_reset) + reset=win_perms_reset, + ) - add_change = {'Key': r'{0}\{1}'.format(hive, key), - 'Entry': '{0}'.format(salt.utils.stringutils.to_unicode(vname, 'utf-8') if vname else '(Default)'), - 'Value': vdata_decoded, - 'Owner': win_owner, - 'Perms': {'Grant': win_perms, - 'Deny': win_deny_perms}, - 'Inheritance': win_inheritance} + add_change = { + "Key": r"{0}\{1}".format(hive, key), + "Entry": "{0}".format( + salt.utils.stringutils.to_unicode(vname, "utf-8") if vname else "(Default)" + ), + "Value": vdata_decoded, + "Owner": win_owner, + "Perms": {"Grant": win_perms, "Deny": win_deny_perms}, + "Inheritance": win_inheritance, + } # Check for test option - if __opts__['test']: - ret['result'] = None - ret['changes'] = {'reg': {'Will add': add_change}} + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {"reg": {"Will add": add_change}} return ret # Configure the value - ret['result'] = __utils__['reg.set_value'](hive=hive, - key=key, - vname=vname, - vdata=vdata, - vtype=vtype, - use_32bit_registry=use_32bit_registry) + ret["result"] = __utils__["reg.set_value"]( + hive=hive, + key=key, + vname=vname, + vdata=vdata, + vtype=vtype, + use_32bit_registry=use_32bit_registry, + ) - if not ret['result']: - ret['changes'] = {} - ret['comment'] = r'Failed to add {0} to {1}\{2}'.format(vname, hive, key) + if not ret["result"]: + ret["changes"] = {} + ret["comment"] = r"Failed to add {0} to {1}\{2}".format(vname, hive, key) else: - ret['changes'] = {'reg': {'Added': add_change}} - ret['comment'] = r'Added {0} to {1}\{2}'.format(vname, hive, key) + ret["changes"] = {"reg": {"Added": add_change}} + ret["comment"] = r"Added {0} to {1}\{2}".format(vname, hive, key) - if ret['result']: - ret = __utils__['dacl.check_perms']( - obj_name='\\'.join([hive, key]), - obj_type='registry32' if use_32bit_registry else 'registry', + if ret["result"]: + ret = __utils__["dacl.check_perms"]( + obj_name="\\".join([hive, key]), + obj_type="registry32" if use_32bit_registry else "registry", ret=ret, owner=win_owner, grant_perms=win_perms, deny_perms=win_deny_perms, inheritance=win_inheritance, - reset=win_perms_reset) + reset=win_perms_reset, + ) return ret def absent(name, vname=None, use_32bit_registry=False): - r''' + r""" Ensure a registry value is removed. To remove a key use key_absent. Args: @@ -499,49 +515,46 @@ def absent(name, vname=None, use_32bit_registry=False): In the above example the value named ``version`` will be removed from the SOFTWARE\\Salt key in the HKEY_CURRENT_USER hive. If ``vname`` was not passed, the ``(Default)`` value would be deleted. - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} hive, key = _parse_key(name) # Determine what to do - reg_check = __utils__['reg.read_value'](hive=hive, - key=key, - vname=vname, - use_32bit_registry=use_32bit_registry) - if not reg_check['success'] or reg_check['vdata'] == '(value not set)': - ret['comment'] = '{0} is already absent'.format(name) + reg_check = __utils__["reg.read_value"]( + hive=hive, key=key, vname=vname, use_32bit_registry=use_32bit_registry + ) + if not reg_check["success"] or reg_check["vdata"] == "(value not set)": + ret["comment"] = "{0} is already absent".format(name) return ret - remove_change = {'Key': r'{0}\{1}'.format(hive, key), - 'Entry': '{0}'.format(vname if vname else '(Default)')} + remove_change = { + "Key": r"{0}\{1}".format(hive, key), + "Entry": "{0}".format(vname if vname else "(Default)"), + } # Check for test option - if __opts__['test']: - ret['result'] = None - ret['changes'] = {'reg': {'Will remove': remove_change}} + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {"reg": {"Will remove": remove_change}} return ret # Delete the value - ret['result'] = __utils__['reg.delete_value'](hive=hive, - key=key, - vname=vname, - use_32bit_registry=use_32bit_registry) - if not ret['result']: - ret['changes'] = {} - ret['comment'] = r'Failed to remove {0} from {1}'.format(key, hive) + ret["result"] = __utils__["reg.delete_value"]( + hive=hive, key=key, vname=vname, use_32bit_registry=use_32bit_registry + ) + if not ret["result"]: + ret["changes"] = {} + ret["comment"] = r"Failed to remove {0} from {1}".format(key, hive) else: - ret['changes'] = {'reg': {'Removed': remove_change}} - ret['comment'] = r'Removed {0} from {1}'.format(key, hive) + ret["changes"] = {"reg": {"Removed": remove_change}} + ret["comment"] = r"Removed {0} from {1}".format(key, hive) return ret def key_absent(name, use_32bit_registry=False): - r''' + r""" .. versionadded:: 2015.5.4 Ensure a registry key is removed. This will remove the key, subkeys, and all @@ -581,40 +594,34 @@ def key_absent(name, use_32bit_registry=False): - ``HKEY_CURRENT_USER`` is the hive - ``SOFTWARE\DeleteMe`` is the key - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} hive, key = _parse_key(name) # Determine what to do - if not __utils__['reg.read_value'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry)['success']: - ret['comment'] = '{0} is already absent'.format(name) + if not __utils__["reg.read_value"]( + hive=hive, key=key, use_32bit_registry=use_32bit_registry + )["success"]: + ret["comment"] = "{0} is already absent".format(name) return ret - ret['changes'] = { - 'reg': { - 'Removed': { - 'Key': r'{0}\{1}'.format(hive, key)}}} + ret["changes"] = {"reg": {"Removed": {"Key": r"{0}\{1}".format(hive, key)}}} # Check for test option - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # Delete the value - __utils__['reg.delete_key_recursive'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry) - if __utils__['reg.read_value'](hive=hive, - key=key, - use_32bit_registry=use_32bit_registry)['success']: - ret['result'] = False - ret['changes'] = {} - ret['comment'] = 'Failed to remove registry key {0}'.format(name) + __utils__["reg.delete_key_recursive"]( + hive=hive, key=key, use_32bit_registry=use_32bit_registry + ) + if __utils__["reg.read_value"]( + hive=hive, key=key, use_32bit_registry=use_32bit_registry + )["success"]: + ret["result"] = False + ret["changes"] = {} + ret["comment"] = "Failed to remove registry key {0}".format(name) return ret diff --git a/salt/states/rsync.py b/salt/states/rsync.py index e87a0cd934c..5683fb0b7b3 100644 --- a/salt/states/rsync.py +++ b/salt/states/rsync.py @@ -13,7 +13,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -''' +""" State to synchronize files and directories with rsync. .. versionadded:: 2016.3.0 @@ -25,9 +25,10 @@ State to synchronize files and directories with rsync. - source: /home - force: True -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -37,26 +38,34 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only if Rsync is available. :return: - ''' - return salt.utils.path.which('rsync') and 'rsync' or False + """ + if salt.utils.path.which("rsync"): + return True + return (False, "Command not found: rsync") def _get_summary(rsync_out): - ''' + """ Get summary from the rsync successful output. - ''' + """ - return "- " + "\n- ".join([elm for elm in rsync_out.split("\n\n")[-1].replace(" ", "\n").split("\n") if elm]) + return "- " + "\n- ".join( + [ + elm + for elm in rsync_out.split("\n\n")[-1].replace(" ", "\n").split("\n") + if elm + ] + ) def _get_changes(rsync_out): - ''' + """ Get changes from the rsync successful output. - ''' + """ copied = list() deleted = list() @@ -67,27 +76,30 @@ def _get_changes(rsync_out): copied.append(line) ret = { - 'copied': os.linesep.join(sorted(copied)) or "N/A", - 'deleted': os.linesep.join(sorted(deleted)) or "N/A", + "copied": os.linesep.join(sorted(copied)) or "N/A", + "deleted": os.linesep.join(sorted(deleted)) or "N/A", } # Return whether anything really changed - ret['changed'] = not ((ret['copied'] == 'N/A') and (ret['deleted'] == 'N/A')) + ret["changed"] = not ((ret["copied"] == "N/A") and (ret["deleted"] == "N/A")) return ret -def synchronized(name, source, - delete=False, - force=False, - update=False, - passwordfile=None, - exclude=None, - excludefrom=None, - prepare=False, - dryrun=False, - additional_opts=None): - ''' +def synchronized( + name, + source, + delete=False, + force=False, + update=False, + passwordfile=None, + exclude=None, + excludefrom=None, + prepare=False, + dryrun=False, + additional_opts=None, +): + """ Guarantees that the source directory is always copied to the target. name @@ -127,44 +139,49 @@ def synchronized(name, source, Pass additional options to rsync, should be included as a list. .. versionadded:: 2018.3.0 - ''' + """ - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not os.path.exists(name) and not force and not prepare: - ret['result'] = False - ret['comment'] = "Destination directory {dest} was not found.".format(dest=name) + ret["result"] = False + ret["comment"] = "Destination directory {dest} was not found.".format(dest=name) else: if not os.path.exists(name) and prepare: os.makedirs(name) - if __opts__['test']: + if __opts__["test"]: dryrun = True - result = __salt__['rsync.rsync'](source, name, delete=delete, - force=force, update=update, - passwordfile=passwordfile, - exclude=exclude, - excludefrom=excludefrom, - dryrun=dryrun, - additional_opts=additional_opts) + result = __salt__["rsync.rsync"]( + source, + name, + delete=delete, + force=force, + update=update, + passwordfile=passwordfile, + exclude=exclude, + excludefrom=excludefrom, + dryrun=dryrun, + additional_opts=additional_opts, + ) - if __opts__['test'] or dryrun: - ret['result'] = None - ret['comment'] = _get_summary(result['stdout']) + if __opts__["test"] or dryrun: + ret["result"] = None + ret["comment"] = _get_summary(result["stdout"]) return ret # Failed - if result.get('retcode'): - ret['result'] = False - ret['comment'] = result['stderr'] + if result.get("retcode"): + ret["result"] = False + ret["comment"] = result["stderr"] # Changed - elif _get_changes(result['stdout'])['changed']: - ret['comment'] = _get_summary(result['stdout']) - ret['changes'] = _get_changes(result['stdout']) - del ret['changes']['changed'] # Don't need to print the boolean + elif _get_changes(result["stdout"])["changed"]: + ret["comment"] = _get_summary(result["stdout"]) + ret["changes"] = _get_changes(result["stdout"]) + del ret["changes"]["changed"] # Don't need to print the boolean # Clean else: - ret['comment'] = _get_summary(result['stdout']) - ret['changes'] = {} + ret["comment"] = _get_summary(result["stdout"]) + ret["changes"] = {} return ret diff --git a/salt/states/rvm.py b/salt/states/rvm.py index afdfaa0ce25..60f469833c3 100644 --- a/salt/states/rvm.py +++ b/salt/states/rvm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Managing Ruby installations and gemsets with Ruby Version Manager (RVM) ======================================================================= @@ -99,75 +99,75 @@ configuration could look like: - user: rvm - require: - rvm: ruby-1.9.2 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import re def _check_rvm(ret, user=None): - ''' + """ Check to see if rvm is installed. - ''' - if not __salt__['rvm.is_installed'](user): - ret['result'] = False - ret['comment'] = 'RVM is not installed.' + """ + if not __salt__["rvm.is_installed"](user): + ret["result"] = False + ret["comment"] = "RVM is not installed." return ret def _check_and_install_ruby(ret, ruby, default=False, user=None, opts=None, env=None): - ''' + """ Verify that ruby is installed, install if unavailable - ''' + """ ret = _check_ruby(ret, ruby, user=user) - if not ret['result']: - if __salt__['rvm.install_ruby'](ruby, runas=user, opts=opts, env=env): - ret['result'] = True - ret['changes'][ruby] = 'Installed' - ret['comment'] = 'Successfully installed ruby.' - ret['default'] = False + if not ret["result"]: + if __salt__["rvm.install_ruby"](ruby, runas=user, opts=opts, env=env): + ret["result"] = True + ret["changes"][ruby] = "Installed" + ret["comment"] = "Successfully installed ruby." + ret["default"] = False else: - ret['result'] = False - ret['comment'] = 'Could not install ruby.' + ret["result"] = False + ret["comment"] = "Could not install ruby." return ret if default: - __salt__['rvm.set_default'](ruby, runas=user) + __salt__["rvm.set_default"](ruby, runas=user) return ret def _check_ruby(ret, ruby, user=None): - ''' + """ Check that ruby is installed - ''' + """ match_version = True match_micro_version = False - micro_version_regex = re.compile(r'-([0-9]{4}\.[0-9]{2}|p[0-9]+)$') + micro_version_regex = re.compile(r"-([0-9]{4}\.[0-9]{2}|p[0-9]+)$") if micro_version_regex.search(ruby): match_micro_version = True - if re.search('^[a-z]+$', ruby): + if re.search("^[a-z]+$", ruby): match_version = False - ruby = re.sub('^ruby-', '', ruby) + ruby = re.sub("^ruby-", "", ruby) - for impl, version, default in __salt__['rvm.list'](runas=user): - if impl != 'ruby': - version = '{impl}-{version}'.format(impl=impl, version=version) + for impl, version, default in __salt__["rvm.list"](runas=user): + if impl != "ruby": + version = "{impl}-{version}".format(impl=impl, version=version) if not match_micro_version: - version = micro_version_regex.sub('', version) + version = micro_version_regex.sub("", version) if not match_version: - version = re.sub('-.*', '', version) + version = re.sub("-.*", "", version) if version == ruby: - ret['result'] = True - ret['comment'] = 'Requested ruby exists.' - ret['default'] = default + ret["result"] = True + ret["comment"] = "Requested ruby exists." + ret["default"] = default break return ret def installed(name, default=False, user=None, opts=None, env=None): - ''' + """ Verify that the specified ruby is installed with RVM. RVM is installed when necessary. @@ -187,26 +187,30 @@ def installed(name, default=False, user=None, opts=None, env=None): A list of option flags to pass to RVM (ie -C, --patch) .. versionadded:: 0.17.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} - if __opts__['test']: - ret['comment'] = 'Ruby {0} is set to be installed'.format(name) + if __opts__["test"]: + ret["comment"] = "Ruby {0} is set to be installed".format(name) return ret ret = _check_rvm(ret, user) - if ret['result'] is False: - if not __salt__['rvm.install'](runas=user): - ret['comment'] = 'RVM failed to install.' + if ret["result"] is False: + if not __salt__["rvm.install"](runas=user): + ret["comment"] = "RVM failed to install." return ret else: - return _check_and_install_ruby(ret, name, default, user=user, opts=opts, env=env) + return _check_and_install_ruby( + ret, name, default, user=user, opts=opts, env=env + ) else: - return _check_and_install_ruby(ret, name, default, user=user, opts=opts, env=env) + return _check_and_install_ruby( + ret, name, default, user=user, opts=opts, env=env + ) -def gemset_present(name, ruby='default', user=None): - ''' +def gemset_present(name, ruby="default", user=None): + """ Verify that the gemset is present. name @@ -219,35 +223,35 @@ def gemset_present(name, ruby='default', user=None): The user to run rvm as. .. versionadded:: 0.17.0 - ''' - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": None, "comment": "", "changes": {}} ret = _check_rvm(ret, user) - if ret['result'] is False: + if ret["result"] is False: return ret - if '@' in name: - ruby, name = name.split('@') + if "@" in name: + ruby, name = name.split("@") ret = _check_ruby(ret, ruby) - if not ret['result']: - ret['result'] = False - ret['comment'] = 'Requested ruby implementation was not found.' + if not ret["result"]: + ret["result"] = False + ret["comment"] = "Requested ruby implementation was not found." return ret - if name in __salt__['rvm.gemset_list'](ruby, runas=user): - ret['result'] = True - ret['comment'] = 'Gemset already exists.' + if name in __salt__["rvm.gemset_list"](ruby, runas=user): + ret["result"] = True + ret["comment"] = "Gemset already exists." else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Set to install gemset {0}'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Set to install gemset {0}".format(name) return ret - if __salt__['rvm.gemset_create'](ruby, name, runas=user): - ret['result'] = True - ret['comment'] = 'Gemset successfully created.' - ret['changes'][name] = 'created' + if __salt__["rvm.gemset_create"](ruby, name, runas=user): + ret["result"] = True + ret["comment"] = "Gemset successfully created." + ret["changes"][name] = "created" else: - ret['result'] = False - ret['comment'] = 'Gemset could not be created.' + ret["result"] = False + ret["comment"] = "Gemset could not be created." return ret diff --git a/salt/states/salt_proxy.py b/salt/states/salt_proxy.py index c20170d7004..13633b25a43 100644 --- a/salt/states/salt_proxy.py +++ b/salt/states/salt_proxy.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Salt proxy state .. versionadded:: 2015.8.2 @@ -22,16 +22,16 @@ within /etc/salt/proxy (if /etc/salt/proxy doesn't exists) and start the salt-proxy process (default true), if it isn't already running. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals import logging log = logging.getLogger(__name__) -def configure_proxy(name, proxyname='p8000', start=True): - ''' +def configure_proxy(name, proxyname="p8000", start=True): + """ Create the salt proxy file and start the proxy process if required @@ -52,11 +52,7 @@ def configure_proxy(name, proxyname='p8000', start=True): - proxyname: p8000 - start: True - ''' - ret = __salt__['salt_proxy.configure_proxy'](proxyname, - start=start) - ret.update({ - 'name': name, - 'comment': '{0} config messages'.format(name) - }) + """ + ret = __salt__["salt_proxy.configure_proxy"](proxyname, start=start) + ret.update({"name": name, "comment": "{0} config messages".format(name)}) return ret diff --git a/salt/states/saltmod.py b/salt/states/saltmod.py index aa705241075..50666daab03 100644 --- a/salt/states/saltmod.py +++ b/salt/states/saltmod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Control the Salt command interface ================================== @@ -20,8 +20,8 @@ modules. These state functions wrap Salt's :ref:`Python API <python-api>`. * :ref:`Full Orchestrate Tutorial <orchestrate-runner>` * :py:func:`The Orchestrate runner <salt.runners.state.orchestrate>` -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import fnmatch @@ -30,10 +30,11 @@ import sys import threading import time -# Import salt libs -import salt.syspaths import salt.exceptions import salt.output + +# Import salt libs +import salt.syspaths import salt.utils.data import salt.utils.event from salt.ext import six @@ -41,30 +42,25 @@ from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'salt' +__virtualname__ = "salt" def __virtual__(): - ''' + """ Named salt - ''' + """ return __virtualname__ def _fire_args(tag_data): try: - salt.utils.event.fire_args(__opts__, - __orchestration_jid__, - tag_data, - 'run') + salt.utils.event.fire_args(__opts__, __orchestration_jid__, tag_data, "run") except NameError: - log.debug( - 'Unable to fire args event due to missing __orchestration_jid__' - ) + log.debug("Unable to fire args event due to missing __orchestration_jid__") def _parallel_map(func, inputs): - ''' + """ Applies a function to each element of a list, returning the resulting list. A separate thread is created for each element in the input list and the @@ -82,7 +78,7 @@ def _parallel_map(func, inputs): inputs: list of elements that shall be processed. The length of this list also defines the number of threads created. - ''' + """ outputs = len(inputs) * [None] errors = len(inputs) * [None] @@ -92,9 +88,11 @@ def _parallel_map(func, inputs): outputs[index] = func(inputs[index]) except: # pylint: disable=bare-except errors[index] = sys.exc_info() + thread = threading.Thread(target=run_thread) thread.start() return thread + threads = list(six.moves.map(create_thread, six.moves.range(len(inputs)))) for thread in threads: thread.join() @@ -105,32 +103,34 @@ def _parallel_map(func, inputs): return outputs -def state(name, - tgt, - ssh=False, - tgt_type='glob', - ret='', - ret_config=None, - ret_kwargs=None, - highstate=None, - sls=None, - top=None, - saltenv=None, - test=None, - pillar=None, - pillarenv=None, - expect_minions=True, - fail_minions=None, - allow_fail=0, - concurrent=False, - timeout=None, - batch=None, - queue=False, - subset=None, - orchestration_jid=None, - failhard=None, - **kwargs): - ''' +def state( + name, + tgt, + ssh=False, + tgt_type="glob", + ret="", + ret_config=None, + ret_kwargs=None, + highstate=None, + sls=None, + top=None, + saltenv=None, + test=None, + pillar=None, + pillarenv=None, + expect_minions=True, + fail_minions=None, + allow_fail=0, + concurrent=False, + timeout=None, + batch=None, + queue=False, + subset=None, + orchestration_jid=None, + failhard=None, + **kwargs +): + """ Invoke a state run on a given target name @@ -253,97 +253,96 @@ def state(name, - tgt: role:database - tgt_type: grain - highstate: True - ''' - cmd_kw = {'arg': [], 'kwarg': {}, 'ret': ret, 'timeout': timeout} + """ + cmd_kw = {"arg": [], "kwarg": {}, "ret": ret, "timeout": timeout} if ret_config: - cmd_kw['ret_config'] = ret_config + cmd_kw["ret_config"] = ret_config if ret_kwargs: - cmd_kw['ret_kwargs'] = ret_kwargs + cmd_kw["ret_kwargs"] = ret_kwargs - state_ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + state_ret = {"name": name, "changes": {}, "comment": "", "result": True} try: allow_fail = int(allow_fail) except ValueError: - state_ret['result'] = False - state_ret['comment'] = 'Passed invalid value for \'allow_fail\', must be an int' + state_ret["result"] = False + state_ret["comment"] = "Passed invalid value for 'allow_fail', must be an int" return state_ret - cmd_kw['tgt_type'] = tgt_type - cmd_kw['ssh'] = ssh - if 'roster' in kwargs: - cmd_kw['roster'] = kwargs['roster'] - cmd_kw['expect_minions'] = expect_minions + cmd_kw["tgt_type"] = tgt_type + cmd_kw["ssh"] = ssh + if "roster" in kwargs: + cmd_kw["roster"] = kwargs["roster"] + cmd_kw["expect_minions"] = expect_minions if highstate: - fun = 'state.highstate' + fun = "state.highstate" elif top: - fun = 'state.top' - cmd_kw['arg'].append(top) + fun = "state.top" + cmd_kw["arg"].append(top) elif sls: - fun = 'state.sls' + fun = "state.sls" if isinstance(sls, list): - sls = ','.join(sls) - cmd_kw['arg'].append(sls) + sls = ",".join(sls) + cmd_kw["arg"].append(sls) else: - state_ret['comment'] = 'No highstate or sls specified, no execution made' - state_ret['result'] = False + state_ret["comment"] = "No highstate or sls specified, no execution made" + state_ret["result"] = False return state_ret - if test is not None or __opts__.get('test'): - cmd_kw['kwarg']['test'] = test if test is not None else __opts__.get('test') + if test is not None or __opts__.get("test"): + cmd_kw["kwarg"]["test"] = test if test is not None else __opts__.get("test") if pillar: - cmd_kw['kwarg']['pillar'] = pillar + cmd_kw["kwarg"]["pillar"] = pillar if pillarenv is not None: - cmd_kw['kwarg']['pillarenv'] = pillarenv + cmd_kw["kwarg"]["pillarenv"] = pillarenv if saltenv is not None: - cmd_kw['kwarg']['saltenv'] = saltenv + cmd_kw["kwarg"]["saltenv"] = saltenv - cmd_kw['kwarg']['queue'] = queue + cmd_kw["kwarg"]["queue"] = queue if isinstance(concurrent, bool): - cmd_kw['kwarg']['concurrent'] = concurrent + cmd_kw["kwarg"]["concurrent"] = concurrent else: - state_ret['comment'] = ('Must pass in boolean for value of \'concurrent\'') - state_ret['result'] = False + state_ret["comment"] = "Must pass in boolean for value of 'concurrent'" + state_ret["result"] = False return state_ret if batch is not None: - cmd_kw['batch'] = six.text_type(batch) + cmd_kw["batch"] = six.text_type(batch) if subset is not None: - cmd_kw['subset'] = subset + cmd_kw["subset"] = subset - if failhard is True or __opts__.get('failhard'): - cmd_kw['failhard'] = True + if failhard is True or __opts__.get("failhard"): + cmd_kw["failhard"] = True - masterless = __opts__['__role'] == 'minion' and \ - __opts__['file_client'] == 'local' + masterless = __opts__["__role"] == "minion" and __opts__["file_client"] == "local" if not masterless: - _fire_args({'type': 'state', 'tgt': tgt, 'name': name, 'args': cmd_kw}) - cmd_ret = __salt__['saltutil.cmd'](tgt, fun, **cmd_kw) + _fire_args({"type": "state", "tgt": tgt, "name": name, "args": cmd_kw}) + cmd_ret = __salt__["saltutil.cmd"](tgt, fun, **cmd_kw) else: if top: - cmd_kw['topfn'] = ''.join(cmd_kw.pop('arg')) + cmd_kw["topfn"] = "".join(cmd_kw.pop("arg")) elif sls: - cmd_kw['mods'] = ''.join(cmd_kw.pop('arg')) - cmd_kw.update(cmd_kw.pop('kwarg')) + cmd_kw["mods"] = "".join(cmd_kw.pop("arg")) + cmd_kw.update(cmd_kw.pop("kwarg")) tmp_ret = __salt__[fun](**cmd_kw) - cmd_ret = {__opts__['id']: { - 'ret': tmp_ret, - 'out': tmp_ret.get('out', 'highstate') if - isinstance(tmp_ret, dict) else 'highstate' - }} + cmd_ret = { + __opts__["id"]: { + "ret": tmp_ret, + "out": tmp_ret.get("out", "highstate") + if isinstance(tmp_ret, dict) + else "highstate", + } + } try: - state_ret['__jid__'] = cmd_ret[next(iter(cmd_ret))]['jid'] + state_ret["__jid__"] = cmd_ret[next(iter(cmd_ret))]["jid"] except (StopIteration, KeyError): pass @@ -354,38 +353,37 @@ def state(name, if fail_minions is None: fail_minions = () elif isinstance(fail_minions, six.string_types): - fail_minions = [minion.strip() for minion in fail_minions.split(',')] + fail_minions = [minion.strip() for minion in fail_minions.split(",")] elif not isinstance(fail_minions, list): - state_ret.setdefault('warnings', []).append( - '\'fail_minions\' needs to be a list or a comma separated ' - 'string. Ignored.' + state_ret.setdefault("warnings", []).append( + "'fail_minions' needs to be a list or a comma separated " "string. Ignored." ) fail_minions = () if not cmd_ret and expect_minions: - state_ret['result'] = False - state_ret['comment'] = 'No minions returned' + state_ret["result"] = False + state_ret["comment"] = "No minions returned" return state_ret for minion, mdata in six.iteritems(cmd_ret): - if mdata.get('out', '') != 'highstate': - log.warning('Output from salt state not highstate') + if mdata.get("out", "") != "highstate": + log.warning("Output from salt state not highstate") m_ret = False - if 'return' in mdata and 'ret' not in mdata: - mdata['ret'] = mdata.pop('return') + if "return" in mdata and "ret" not in mdata: + mdata["ret"] = mdata.pop("return") m_state = True - if mdata.get('failed', False): + if mdata.get("failed", False): m_state = False else: try: - m_ret = mdata['ret'] + m_ret = mdata["ret"] except KeyError: m_state = False if m_state: - m_state = __utils__['state.check_result'](m_ret, recurse=True) + m_state = __utils__["state.check_result"](m_ret, recurse=True) if not m_state: if minion not in fail_minions: @@ -395,7 +393,7 @@ def state(name, try: for state_item in six.itervalues(m_ret): if isinstance(state_item, dict): - if 'changes' in state_item and state_item['changes']: + if "changes" in state_item and state_item["changes"]: changes[minion] = m_ret break else: @@ -405,42 +403,45 @@ def state(name, no_change.add(minion) if changes: - state_ret['changes'] = {'out': 'highstate', 'ret': changes} + state_ret["changes"] = {"out": "highstate", "ret": changes} if len(fail) > allow_fail: - state_ret['result'] = False - state_ret['comment'] = 'Run failed on minions: {0}'.format(', '.join(fail)) + state_ret["result"] = False + state_ret["comment"] = "Run failed on minions: {0}".format(", ".join(fail)) else: - state_ret['comment'] = 'States ran successfully.' + state_ret["comment"] = "States ran successfully." if changes: - state_ret['comment'] += ' Updating {0}.'.format(', '.join(changes)) + state_ret["comment"] += " Updating {0}.".format(", ".join(changes)) if no_change: - state_ret['comment'] += ' No changes made to {0}.'.format(', '.join(no_change)) - if test or __opts__.get('test'): - if state_ret['changes'] and state_ret['result'] is True: + state_ret["comment"] += " No changes made to {0}.".format( + ", ".join(no_change) + ) + if test or __opts__.get("test"): + if state_ret["changes"] and state_ret["result"] is True: # Test mode with changes is the only case where result should ever be none - state_ret['result'] = None + state_ret["result"] = None return state_ret def function( - name, - tgt, - ssh=False, - tgt_type='glob', - ret='', - ret_config=None, - ret_kwargs=None, - expect_minions=False, - fail_minions=None, - fail_function=None, - arg=None, - kwarg=None, - timeout=None, - batch=None, - subset=None, - failhard=None, - **kwargs): # pylint: disable=unused-argument - ''' + name, + tgt, + ssh=False, + tgt_type="glob", + ret="", + ret_config=None, + ret_kwargs=None, + expect_minions=False, + fail_minions=None, + fail_function=None, + arg=None, + kwarg=None, + timeout=None, + batch=None, + subset=None, + failhard=None, + **kwargs +): # pylint: disable=unused-argument + """ Execute a single module function on a remote minion via salt or salt-ssh name @@ -493,56 +494,52 @@ def function( .. versionadded:: 2019.2.2 - ''' - func_ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + func_ret = {"name": name, "changes": {}, "comment": "", "result": True} if kwarg is None: kwarg = {} if isinstance(arg, six.string_types): - func_ret['warnings'] = [ - 'Please specify \'arg\' as a list of arguments.' - ] + func_ret["warnings"] = ["Please specify 'arg' as a list of arguments."] arg = arg.split() - cmd_kw = {'arg': arg or [], 'kwarg': kwarg, 'ret': ret, 'timeout': timeout} + cmd_kw = {"arg": arg or [], "kwarg": kwarg, "ret": ret, "timeout": timeout} if batch is not None: - cmd_kw['batch'] = six.text_type(batch) + cmd_kw["batch"] = six.text_type(batch) if subset is not None: - cmd_kw['subset'] = subset + cmd_kw["subset"] = subset - cmd_kw['tgt_type'] = tgt_type - cmd_kw['ssh'] = ssh - cmd_kw['expect_minions'] = expect_minions - cmd_kw['_cmd_meta'] = True + cmd_kw["tgt_type"] = tgt_type + cmd_kw["ssh"] = ssh + cmd_kw["expect_minions"] = expect_minions + cmd_kw["_cmd_meta"] = True - if failhard is True or __opts__.get('failhard'): - cmd_kw['failhard'] = True + if failhard is True or __opts__.get("failhard"): + cmd_kw["failhard"] = True if ret_config: - cmd_kw['ret_config'] = ret_config + cmd_kw["ret_config"] = ret_config if ret_kwargs: - cmd_kw['ret_kwargs'] = ret_kwargs + cmd_kw["ret_kwargs"] = ret_kwargs fun = name - if __opts__['test'] is True: - func_ret['comment'] = \ - 'Function {0} would be executed on target {1}'.format(fun, tgt) - func_ret['result'] = None + if __opts__["test"] is True: + func_ret["comment"] = "Function {0} would be executed on target {1}".format( + fun, tgt + ) + func_ret["result"] = None return func_ret try: - _fire_args({'type': 'function', 'tgt': tgt, 'name': name, 'args': cmd_kw}) - cmd_ret = __salt__['saltutil.cmd'](tgt, fun, **cmd_kw) + _fire_args({"type": "function", "tgt": tgt, "name": name, "args": cmd_kw}) + cmd_ret = __salt__["saltutil.cmd"](tgt, fun, **cmd_kw) except Exception as exc: # pylint: disable=broad-except - func_ret['result'] = False - func_ret['comment'] = six.text_type(exc) + func_ret["result"] = False + func_ret["comment"] = six.text_type(exc) return func_ret try: - func_ret['__jid__'] = cmd_ret[next(iter(cmd_ret))]['jid'] + func_ret["__jid__"] = cmd_ret[next(iter(cmd_ret))]["jid"] except (StopIteration, KeyError): pass @@ -552,24 +549,23 @@ def function( if fail_minions is None: fail_minions = () elif isinstance(fail_minions, six.string_types): - fail_minions = [minion.strip() for minion in fail_minions.split(',')] + fail_minions = [minion.strip() for minion in fail_minions.split(",")] elif not isinstance(fail_minions, list): - func_ret.setdefault('warnings', []).append( - '\'fail_minions\' needs to be a list or a comma separated ' - 'string. Ignored.' + func_ret.setdefault("warnings", []).append( + "'fail_minions' needs to be a list or a comma separated " "string. Ignored." ) fail_minions = () for minion, mdata in six.iteritems(cmd_ret): m_ret = False - if mdata.get('retcode'): - func_ret['result'] = False + if mdata.get("retcode"): + func_ret["result"] = False fail.add(minion) - if mdata.get('failed', False): + if mdata.get("failed", False): m_func = False else: - if 'return' in mdata and 'ret' not in mdata: - mdata['ret'] = mdata.pop('return') - m_ret = mdata['ret'] + if "return" in mdata and "ret" not in mdata: + mdata["ret"] = mdata.pop("return") + m_ret = mdata["ret"] m_func = (not fail_function and True) or __salt__[fail_function](m_ret) if m_ret is False: @@ -580,28 +576,27 @@ def function( fail.add(minion) changes[minion] = m_ret if not cmd_ret: - func_ret['result'] = False - func_ret['command'] = 'No minions responded' + func_ret["result"] = False + func_ret["command"] = "No minions responded" else: if changes: - func_ret['changes'] = {'out': 'highstate', 'ret': changes} + func_ret["changes"] = {"out": "highstate", "ret": changes} if fail: - func_ret['result'] = False - func_ret['comment'] = 'Running function {0} failed on minions: {1}'.format(name, ', '.join(fail)) + func_ret["result"] = False + func_ret["comment"] = "Running function {0} failed on minions: {1}".format( + name, ", ".join(fail) + ) else: - func_ret['comment'] = 'Function ran successfully.' + func_ret["comment"] = "Function ran successfully." if changes: - func_ret['comment'] += ' Function {0} ran on {1}.'.format(name, ', '.join(changes)) + func_ret["comment"] += " Function {0} ran on {1}.".format( + name, ", ".join(changes) + ) return func_ret -def wait_for_event( - name, - id_list, - event_id='id', - timeout=300, - node='master'): - ''' +def wait_for_event(name, id_list, event_id="id", timeout=300, node="master"): + """ Watch Salt's event bus and block until a condition is met .. versionadded:: 2014.7.0 @@ -642,21 +637,17 @@ def wait_for_event( - mike - require: - salt: reboot_all_minions - ''' - ret = {'name': name, 'changes': {}, 'comment': '', 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} - if __opts__.get('test'): - ret['comment'] = \ - 'Orchestration would wait for event \'{0}\''.format(name) - ret['result'] = None + if __opts__.get("test"): + ret["comment"] = "Orchestration would wait for event '{0}'".format(name) + ret["result"] = None return ret sevent = salt.utils.event.get_event( - node, - __opts__['sock_dir'], - __opts__['transport'], - opts=__opts__, - listen=True) + node, __opts__["sock_dir"], __opts__["transport"], opts=__opts__, listen=True + ) del_counter = 0 starttime = time.time() @@ -669,49 +660,59 @@ def wait_for_event( log.trace("wait_for_event: No event data; waiting.") continue elif event is None and is_timedout: - ret['comment'] = 'Timeout value reached.' + ret["comment"] = "Timeout value reached." return ret - if fnmatch.fnmatch(event['tag'], name): - val = event['data'].get(event_id) - if val is None and 'data' in event['data']: - val = event['data']['data'].get(event_id) + if fnmatch.fnmatch(event["tag"], name): + val = event["data"].get(event_id) + if val is None and "data" in event["data"]: + val = event["data"]["data"].get(event_id) if val is not None: try: val_idx = id_list.index(val) except ValueError: - log.trace("wait_for_event: Event identifier '%s' not in " - "id_list; skipping.", event_id) + log.trace( + "wait_for_event: Event identifier '%s' not in " + "id_list; skipping.", + event_id, + ) else: del id_list[val_idx] del_counter += 1 - minions_seen = ret['changes'].setdefault('minions_seen', []) + minions_seen = ret["changes"].setdefault("minions_seen", []) minions_seen.append(val) - log.debug("wait_for_event: Event identifier '%s' removed " - "from id_list; %s items remaining.", - val, len(id_list)) + log.debug( + "wait_for_event: Event identifier '%s' removed " + "from id_list; %s items remaining.", + val, + len(id_list), + ) else: - log.trace("wait_for_event: Event identifier '%s' not in event " - "'%s'; skipping.", event_id, event['tag']) + log.trace( + "wait_for_event: Event identifier '%s' not in event " + "'%s'; skipping.", + event_id, + event["tag"], + ) else: - log.debug("wait_for_event: Skipping unmatched event '%s'", - event['tag']) + log.debug("wait_for_event: Skipping unmatched event '%s'", event["tag"]) if len(id_list) == 0: - ret['result'] = True - ret['comment'] = 'All events seen in {0} seconds.'.format( - time.time() - starttime) + ret["result"] = True + ret["comment"] = "All events seen in {0} seconds.".format( + time.time() - starttime + ) return ret if is_timedout: - ret['comment'] = 'Timeout value reached.' + ret["comment"] = "Timeout value reached." return ret def runner(name, **kwargs): - ''' + """ Execute a runner module on the master .. versionadded:: 2014.7.0 @@ -726,52 +727,45 @@ def runner(name, **kwargs): run-manage-up: salt.runner: - name: manage.up - ''' + """ try: jid = __orchestration_jid__ except NameError: - log.debug( - 'Unable to fire args event due to missing __orchestration_jid__' - ) + log.debug("Unable to fire args event due to missing __orchestration_jid__") jid = None - if __opts__.get('test', False): + if __opts__.get("test", False): ret = { - 'name': name, - 'result': None, - 'changes': {}, - 'comment': "Runner function '{0}' would be executed.".format(name) + "name": name, + "result": None, + "changes": {}, + "comment": "Runner function '{0}' would be executed.".format(name), } return ret - out = __salt__['saltutil.runner'](name, - __orchestration_jid__=jid, - __env__=__env__, - full_return=True, - **kwargs) - - runner_return = out.get('return') - if isinstance(runner_return, dict) and 'Error' in runner_return: - out['success'] = False - - success = out.get('success', True) - ret = {'name': name, - 'changes': {'return': runner_return}, - 'result': success} - ret['comment'] = "Runner function '{0}' {1}.".format( - name, - 'executed' if success else 'failed', + out = __salt__["saltutil.runner"]( + name, __orchestration_jid__=jid, __env__=__env__, full_return=True, **kwargs ) - ret['__orchestration__'] = True - if 'jid' in out: - ret['__jid__'] = out['jid'] + runner_return = out.get("return") + if isinstance(runner_return, dict) and "Error" in runner_return: + out["success"] = False + + success = out.get("success", True) + ret = {"name": name, "changes": {"return": runner_return}, "result": success} + ret["comment"] = "Runner function '{0}' {1}.".format( + name, "executed" if success else "failed", + ) + + ret["__orchestration__"] = True + if "jid" in out: + ret["__jid__"] = out["jid"] return ret def parallel_runners(name, runners, **kwargs): # pylint: disable=unused-argument - ''' + """ Executes multiple runner modules on the master in parallel. .. versionadded:: 2017.x.0 (Nitrogen) @@ -806,7 +800,7 @@ def parallel_runners(name, runners, **kwargs): # pylint: disable=unused-argumen - name: state.orchestrate - kwarg: mods: orchestrate_state_2 - ''' + """ # For the sake of consistency, we treat a single string in the same way as # a key without a value. This allows something like # salt.parallel_runners: @@ -820,10 +814,10 @@ def parallel_runners(name, runners, **kwargs): # pylint: disable=unused-argumen # else is considered an error. if not isinstance(runners, dict): return { - 'name': name, - 'result': False, - 'changes': {}, - 'comment': 'The runners parameter must be a string or dict.' + "name": name, + "result": False, + "changes": {}, + "comment": "The runners parameter must be a string or dict.", } # The configuration for each runner is given as a list of key-value pairs. # This is not very useful for what we want to do, but it is the typical @@ -835,50 +829,52 @@ def parallel_runners(name, runners, **kwargs): # pylint: disable=unused-argumen runner_config = {} else: runner_config = salt.utils.data.repack_dictlist(runner_config) - if 'name' not in runner_config: - runner_config['name'] = runner_id + if "name" not in runner_config: + runner_config["name"] = runner_id runners[runner_id] = runner_config try: jid = __orchestration_jid__ except NameError: - log.debug( - 'Unable to fire args event due to missing __orchestration_jid__') + log.debug("Unable to fire args event due to missing __orchestration_jid__") jid = None def call_runner(runner_config): - return __salt__['saltutil.runner'](runner_config['name'], - __orchestration_jid__=jid, - __env__=__env__, - full_return=True, - **(runner_config.get('kwarg', {}))) + return __salt__["saltutil.runner"]( + runner_config["name"], + __orchestration_jid__=jid, + __env__=__env__, + full_return=True, + **(runner_config.get("kwarg", {})) + ) try: outputs = _parallel_map(call_runner, list(six.itervalues(runners))) except salt.exceptions.SaltException as exc: return { - 'name': name, - 'result': False, - 'success': False, - 'changes': {}, - 'comment': 'One of the runners raised an exception: {0}'.format( - exc) + "name": name, + "result": False, + "success": False, + "changes": {}, + "comment": "One of the runners raised an exception: {0}".format(exc), } # We bundle the results of the runners with the IDs of the runners so that # we can easily identify which output belongs to which runner. At the same # time we exctract the actual return value of the runner (saltutil.runner # adds some extra information that is not interesting to us). outputs = { - runner_id: out['return']for runner_id, out in - six.moves.zip(six.iterkeys(runners), outputs) + runner_id: out["return"] + for runner_id, out in six.moves.zip(six.iterkeys(runners), outputs) } # If each of the runners returned its output in the format compatible with # the 'highstate' outputter, we can leverage this fact when merging the # outputs. highstate_output = all( - [out.get('outputter', '') == 'highstate' and 'data' in out for out in - six.itervalues(outputs)] + [ + out.get("outputter", "") == "highstate" and "data" in out + for out in six.itervalues(outputs) + ] ) # The following helper function is used to extract changes from highstate @@ -887,13 +883,15 @@ def parallel_runners(name, runners, **kwargs): # pylint: disable=unused-argumen def extract_changes(obj): if not isinstance(obj, dict): return {} - elif 'changes' in obj: - if (isinstance(obj['changes'], dict) - and obj['changes'].get('out', '') == 'highstate' - and 'ret' in obj['changes']): - return obj['changes']['ret'] + elif "changes" in obj: + if ( + isinstance(obj["changes"], dict) + and obj["changes"].get("out", "") == "highstate" + and "ret" in obj["changes"] + ): + return obj["changes"]["ret"] else: - return obj['changes'] + return obj["changes"] else: found_changes = {} for key, value in six.iteritems(obj): @@ -901,50 +899,52 @@ def parallel_runners(name, runners, **kwargs): # pylint: disable=unused-argumen if change: found_changes[key] = change return found_changes + if highstate_output: - failed_runners = [runner_id for runner_id, out in - six.iteritems(outputs) if - out['data'].get('retcode', 0) != 0] + failed_runners = [ + runner_id + for runner_id, out in six.iteritems(outputs) + if out["data"].get("retcode", 0) != 0 + ] all_successful = not failed_runners if all_successful: - comment = 'All runner functions executed successfully.' + comment = "All runner functions executed successfully." else: runner_comments = [ - 'Runner {0} failed with return value:\n{1}'.format( + "Runner {0} failed with return value:\n{1}".format( runner_id, - salt.output.out_format(outputs[runner_id], - 'nested', - __opts__, - nested_indent=2) - ) for runner_id in failed_runners + salt.output.out_format( + outputs[runner_id], "nested", __opts__, nested_indent=2 + ), + ) + for runner_id in failed_runners ] - comment = '\n'.join(runner_comments) + comment = "\n".join(runner_comments) changes = {} for runner_id, out in six.iteritems(outputs): - runner_changes = extract_changes(out['data']) + runner_changes = extract_changes(out["data"]) if runner_changes: changes[runner_id] = runner_changes else: - failed_runners = [runner_id for runner_id, out in - six.iteritems(outputs) if - out.get('exit_code', 0) != 0] + failed_runners = [ + runner_id + for runner_id, out in six.iteritems(outputs) + if out.get("exit_code", 0) != 0 + ] all_successful = not failed_runners if all_successful: - comment = 'All runner functions executed successfully.' + comment = "All runner functions executed successfully." else: if len(failed_runners) == 1: - comment = 'Runner {0} failed.'.format(failed_runners[0]) + comment = "Runner {0} failed.".format(failed_runners[0]) else: - comment =\ - 'Runners {0} failed.'.format(', '.join(failed_runners)) - changes = {'ret': { - runner_id: out for runner_id, out in six.iteritems(outputs) - }} + comment = "Runners {0} failed.".format(", ".join(failed_runners)) + changes = {"ret": {runner_id: out for runner_id, out in six.iteritems(outputs)}} ret = { - 'name': name, - 'result': all_successful, - 'changes': changes, - 'comment': comment + "name": name, + "result": all_successful, + "changes": changes, + "comment": comment, } # The 'runner' function includes out['jid'] as '__jid__' in the returned @@ -955,7 +955,7 @@ def parallel_runners(name, runners, **kwargs): # pylint: disable=unused-argumen def wheel(name, **kwargs): - ''' + """ Execute a wheel module on the master .. versionadded:: 2014.7.0 @@ -971,42 +971,36 @@ def wheel(name, **kwargs): salt.wheel: - name: key.accept - match: frank - ''' - ret = {'name': name, 'result': False, 'changes': {}, 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} try: jid = __orchestration_jid__ except NameError: - log.debug( - 'Unable to fire args event due to missing __orchestration_jid__' - ) + log.debug("Unable to fire args event due to missing __orchestration_jid__") jid = None - if __opts__.get('test', False): - ret['result'] = None, - ret['changes'] = {} - ret['comment'] = "Wheel function '{0}' would be executed.".format(name) + if __opts__.get("test", False): + ret["result"] = (None,) + ret["changes"] = {} + ret["comment"] = "Wheel function '{0}' would be executed.".format(name) return ret - out = __salt__['saltutil.wheel'](name, - __orchestration_jid__=jid, - __env__=__env__, - **kwargs) - - wheel_return = out.get('return') - if isinstance(wheel_return, dict) and 'Error' in wheel_return: - out['success'] = False - - success = out.get('success', True) - ret = {'name': name, - 'changes': {'return': wheel_return}, - 'result': success} - ret['comment'] = "Wheel function '{0}' {1}.".format( - name, - 'executed' if success else 'failed', + out = __salt__["saltutil.wheel"]( + name, __orchestration_jid__=jid, __env__=__env__, **kwargs ) - ret['__orchestration__'] = True - if 'jid' in out: - ret['__jid__'] = out['jid'] + wheel_return = out.get("return") + if isinstance(wheel_return, dict) and "Error" in wheel_return: + out["success"] = False + + success = out.get("success", True) + ret = {"name": name, "changes": {"return": wheel_return}, "result": success} + ret["comment"] = "Wheel function '{0}' {1}.".format( + name, "executed" if success else "failed", + ) + + ret["__orchestration__"] = True + if "jid" in out: + ret["__jid__"] = out["jid"] return ret diff --git a/salt/states/saltutil.py b/salt/states/saltutil.py index 880e792cb78..cb03f709407 100644 --- a/salt/states/saltutil.py +++ b/salt/states/saltutil.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Saltutil State ============== @@ -8,51 +8,51 @@ from a states. Rather than needing to to use ``module.run`` this state allows fo improved change detection. .. versionadded: 3000 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals import logging # Define the module's virtual name -__virtualname__ = 'saltutil' +__virtualname__ = "saltutil" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Named saltutil - ''' + """ return __virtualname__ def _sync_single(name, module, **kwargs): - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if __opts__['test']: - ret['result'] = None - ret['comment'] = "saltutil.sync_{0} would have been run".format(module) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "saltutil.sync_{0} would have been run".format(module) return ret try: - sync_status = __salt__['saltutil.sync_{0}'.format(module)](**kwargs) + sync_status = __salt__["saltutil.sync_{0}".format(module)](**kwargs) if sync_status: - ret['changes'][module] = sync_status - ret['comment'] = "Updated {0}.".format(module) + ret["changes"][module] = sync_status + ret["comment"] = "Updated {0}.".format(module) except Exception as e: # pylint: disable=broad-except log.error("Failed to run saltutil.sync_%s: %s", module, e) - ret['result'] = False - ret['comment'] = "Failed to run sync_{0}: {1}".format(module, e) + ret["result"] = False + ret["comment"] = "Failed to run sync_{0}: {1}".format(module, e) return ret - if not ret['changes']: - ret['comment'] = "No updates to sync" + if not ret["changes"]: + ret["comment"] = "No updates to sync" return ret def sync_all(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_all module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -61,34 +61,34 @@ def sync_all(name, **kwargs): sync_everything: saltutil.sync_all: - refresh: True - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if __opts__['test']: - ret['result'] = None - ret['comment'] = "saltutil.sync_all would have been run" + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "saltutil.sync_all would have been run" return ret try: - sync_status = __salt__['saltutil.sync_all'](**kwargs) + sync_status = __salt__["saltutil.sync_all"](**kwargs) for key, value in sync_status.items(): if value: - ret['changes'][key] = value - ret['comment'] = "Sync performed" + ret["changes"][key] = value + ret["comment"] = "Sync performed" except Exception as e: # pylint: disable=broad-except log.error("Failed to run saltutil.sync_all: %s", e) - ret['result'] = False - ret['comment'] = "Failed to run sync_all: {0}".format(e) + ret["result"] = False + ret["comment"] = "Failed to run sync_all: {0}".format(e) return ret - if not ret['changes']: - ret['comment'] = "No updates to sync" + if not ret["changes"]: + ret["comment"] = "No updates to sync" return ret def sync_beacons(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_beacons module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -97,12 +97,12 @@ def sync_beacons(name, **kwargs): sync_everything: saltutil.sync_beacons: - refresh: True - ''' + """ return _sync_single(name, "beacons", **kwargs) def sync_clouds(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_clouds module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -111,12 +111,12 @@ def sync_clouds(name, **kwargs): sync_everything: saltutil.sync_clouds: - refresh: True - ''' + """ return _sync_single(name, "clouds", **kwargs) def sync_engines(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_engines module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -125,12 +125,12 @@ def sync_engines(name, **kwargs): sync_everything: saltutil.sync_engines: - refresh: True - ''' + """ return _sync_single(name, "engines", **kwargs) def sync_grains(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_grains module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -139,12 +139,12 @@ def sync_grains(name, **kwargs): sync_everything: saltutil.sync_grains: - refresh: True - ''' + """ return _sync_single(name, "grains", **kwargs) def sync_executors(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_executors module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -153,12 +153,12 @@ def sync_executors(name, **kwargs): sync_everything: saltutil.sync_executors: - refresh: True - ''' + """ return _sync_single(name, "executors", **kwargs) def sync_log_handlers(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_log_handlers module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -167,12 +167,12 @@ def sync_log_handlers(name, **kwargs): sync_everything: saltutil.sync_log_handlers: - refresh: True - ''' + """ return _sync_single(name, "log_handlers", **kwargs) def sync_modules(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_modules module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -181,12 +181,12 @@ def sync_modules(name, **kwargs): sync_everything: saltutil.sync_modules: - refresh: True - ''' + """ return _sync_single(name, "modules", **kwargs) def sync_output(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_output module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -195,12 +195,12 @@ def sync_output(name, **kwargs): sync_everything: saltutil.sync_output: - refresh: True - ''' + """ return _sync_single(name, "output", **kwargs) def sync_outputters(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_outputters module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -209,12 +209,12 @@ def sync_outputters(name, **kwargs): sync_everything: saltutil.sync_outputters: - refresh: True - ''' + """ return _sync_single(name, "outputters", **kwargs) def sync_pillar(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_pillar module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -223,12 +223,12 @@ def sync_pillar(name, **kwargs): sync_everything: saltutil.sync_pillar: - refresh: True - ''' + """ return _sync_single(name, "pillar", **kwargs) def sync_proxymodules(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_proxymodules module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -237,12 +237,12 @@ def sync_proxymodules(name, **kwargs): sync_everything: saltutil.sync_proxymodules: - refresh: True - ''' + """ return _sync_single(name, "proxymodules", **kwargs) def sync_matchers(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_matchers module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -251,12 +251,12 @@ def sync_matchers(name, **kwargs): sync_everything: saltutil.sync_matchers: - refresh: True - ''' + """ return _sync_single(name, "matchers", **kwargs) def sync_renderers(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_renderers module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -265,12 +265,12 @@ def sync_renderers(name, **kwargs): sync_everything: saltutil.sync_renderers: - refresh: True - ''' + """ return _sync_single(name, "renderers", **kwargs) def sync_returners(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_returners module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -279,12 +279,12 @@ def sync_returners(name, **kwargs): sync_everything: saltutil.sync_returners: - refresh: True - ''' + """ return _sync_single(name, "returners", **kwargs) def sync_sdb(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_sdb module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -293,12 +293,12 @@ def sync_sdb(name, **kwargs): sync_everything: saltutil.sync_sdb: - refresh: True - ''' + """ return _sync_single(name, "sdb", **kwargs) def sync_states(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_states module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -307,12 +307,12 @@ def sync_states(name, **kwargs): sync_everything: saltutil.sync_states: - refresh: True - ''' + """ return _sync_single(name, "states", **kwargs) def sync_thorium(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_thorium module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -321,12 +321,12 @@ def sync_thorium(name, **kwargs): sync_everything: saltutil.sync_thorium: - refresh: True - ''' + """ return _sync_single(name, "thorium", **kwargs) def sync_utils(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_utils module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -335,12 +335,12 @@ def sync_utils(name, **kwargs): sync_everything: saltutil.sync_utils: - refresh: True - ''' + """ return _sync_single(name, "utils", **kwargs) def sync_serializers(name, **kwargs): - ''' + """ Performs the same task as saltutil.sync_serializers module See :mod:`saltutil module for full list of options <salt.modules.saltutil>` @@ -349,5 +349,5 @@ def sync_serializers(name, **kwargs): sync_everything: saltutil.sync_serializers: - refresh: True - ''' + """ return _sync_single(name, "serializers", **kwargs) diff --git a/salt/states/schedule.py b/salt/states/schedule.py index b56b0a32092..cf7343af983 100644 --- a/salt/states/schedule.py +++ b/salt/states/schedule.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of the Salt scheduler ============================================== @@ -96,13 +96,12 @@ Management of the Salt scheduler Wednesday and Friday, and 3pm on Tuesday and Thursday. Requires that python-dateutil is installed on the minion. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -def present(name, - **kwargs): - ''' +def present(name, **kwargs): + """ Ensure a job is present in the schedule name @@ -207,68 +206,65 @@ def present(name, Whether the job should run immediately after the skip_during_range time period ends. - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - current_schedule = __salt__['schedule.list'](show_all=True, return_yaml=False) + current_schedule = __salt__["schedule.list"](show_all=True, return_yaml=False) if name in current_schedule: - new_item = __salt__['schedule.build_schedule_item'](name, **kwargs) + new_item = __salt__["schedule.build_schedule_item"](name, **kwargs) # See if the new_item is valid if isinstance(new_item, dict): - if 'result' in new_item and not new_item['result']: - ret['result'] = new_item['result'] - ret['comment'] = new_item['comment'] + if "result" in new_item and not new_item["result"]: + ret["result"] = new_item["result"] + ret["comment"] = new_item["comment"] return ret # The schedule.list gives us an item that is guaranteed to have an # 'enabled' argument. Before comparing, add 'enabled' if it's not # available (assume True, like schedule.list does) - if 'enabled' not in new_item: - new_item['enabled'] = True + if "enabled" not in new_item: + new_item["enabled"] = True if new_item == current_schedule[name]: - ret['comment'].append('Job {0} in correct state'.format(name)) + ret["comment"].append("Job {0} in correct state".format(name)) else: - if 'test' in __opts__ and __opts__['test']: - kwargs['test'] = True - result = __salt__['schedule.modify'](name, **kwargs) - ret['comment'].append(result['comment']) - ret['changes'] = result['changes'] + if "test" in __opts__ and __opts__["test"]: + kwargs["test"] = True + result = __salt__["schedule.modify"](name, **kwargs) + ret["comment"].append(result["comment"]) + ret["changes"] = result["changes"] else: - result = __salt__['schedule.modify'](name, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["schedule.modify"](name, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - ret['comment'].append('Modifying job {0} in schedule'.format(name)) - ret['changes'] = result['changes'] + ret["comment"].append("Modifying job {0} in schedule".format(name)) + ret["changes"] = result["changes"] else: - if 'test' in __opts__ and __opts__['test']: - kwargs['test'] = True - result = __salt__['schedule.add'](name, **kwargs) - ret['comment'].append(result['comment']) + if "test" in __opts__ and __opts__["test"]: + kwargs["test"] = True + result = __salt__["schedule.add"](name, **kwargs) + ret["comment"].append(result["comment"]) else: - result = __salt__['schedule.add'](name, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["schedule.add"](name, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - ret['comment'].append('Adding new job {0} to schedule'.format(name)) + ret["comment"].append("Adding new job {0} to schedule".format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret def absent(name, **kwargs): - ''' + """ Ensure a job is absent from the schedule name @@ -276,36 +272,33 @@ def absent(name, **kwargs): persist Whether the job should persist between minion restarts, defaults to True. - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - current_schedule = __salt__['schedule.list'](show_all=True, return_yaml=False) + current_schedule = __salt__["schedule.list"](show_all=True, return_yaml=False) if name in current_schedule: - if 'test' in __opts__ and __opts__['test']: - kwargs['test'] = True - result = __salt__['schedule.delete'](name, **kwargs) - ret['comment'].append(result['comment']) + if "test" in __opts__ and __opts__["test"]: + kwargs["test"] = True + result = __salt__["schedule.delete"](name, **kwargs) + ret["comment"].append(result["comment"]) else: - result = __salt__['schedule.delete'](name, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["schedule.delete"](name, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - ret['comment'].append('Removed job {0} from schedule'.format(name)) + ret["comment"].append("Removed job {0} from schedule".format(name)) else: - ret['comment'].append('Job {0} not present in schedule'.format(name)) + ret["comment"].append("Job {0} not present in schedule".format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret def enabled(name, **kwargs): - ''' + """ Ensure a job is enabled in the schedule name @@ -314,36 +307,33 @@ def enabled(name, **kwargs): persist Whether the job should persist between minion restarts, defaults to True. - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - current_schedule = __salt__['schedule.list'](show_all=True, return_yaml=False) + current_schedule = __salt__["schedule.list"](show_all=True, return_yaml=False) if name in current_schedule: - if 'test' in __opts__ and __opts__['test']: - kwargs['test'] = True - result = __salt__['schedule.enable_job'](name, **kwargs) - ret['comment'].append(result['comment']) + if "test" in __opts__ and __opts__["test"]: + kwargs["test"] = True + result = __salt__["schedule.enable_job"](name, **kwargs) + ret["comment"].append(result["comment"]) else: - result = __salt__['schedule.enable_job'](name, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["schedule.enable_job"](name, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - ret['comment'].append('Enabled job {0} from schedule'.format(name)) + ret["comment"].append("Enabled job {0} from schedule".format(name)) else: - ret['comment'].append('Job {0} not present in schedule'.format(name)) + ret["comment"].append("Job {0} not present in schedule".format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret def disabled(name, **kwargs): - ''' + """ Ensure a job is disabled in the schedule name @@ -352,29 +342,26 @@ def disabled(name, **kwargs): persist Whether the job should persist between minion restarts, defaults to True. - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': []} + ret = {"name": name, "result": True, "changes": {}, "comment": []} - current_schedule = __salt__['schedule.list'](show_all=True, return_yaml=False) + current_schedule = __salt__["schedule.list"](show_all=True, return_yaml=False) if name in current_schedule: - if 'test' in __opts__ and __opts__['test']: - kwargs['test'] = True - result = __salt__['schedule.disable_job'](name, **kwargs) - ret['comment'].append(result['comment']) + if "test" in __opts__ and __opts__["test"]: + kwargs["test"] = True + result = __salt__["schedule.disable_job"](name, **kwargs) + ret["comment"].append(result["comment"]) else: - result = __salt__['schedule.disable_job'](name, **kwargs) - if not result['result']: - ret['result'] = result['result'] - ret['comment'] = result['comment'] + result = __salt__["schedule.disable_job"](name, **kwargs) + if not result["result"]: + ret["result"] = result["result"] + ret["comment"] = result["comment"] return ret else: - ret['comment'].append('Disabled job {0} from schedule'.format(name)) + ret["comment"].append("Disabled job {0} from schedule".format(name)) else: - ret['comment'].append('Job {0} not present in schedule'.format(name)) + ret["comment"].append("Job {0} not present in schedule".format(name)) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) return ret diff --git a/salt/states/selinux.py b/salt/states/selinux.py index 2078352d02a..8e35ee287a6 100644 --- a/salt/states/selinux.py +++ b/salt/states/selinux.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of SELinux rules =========================== @@ -23,66 +23,64 @@ booleans can be set. .. note:: Use of these states require that the :mod:`selinux <salt.modules.selinux>` execution module is available. -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import 3rd party libs from salt.ext import six def __virtual__(): - ''' + """ Only make this state available if the selinux module is available. - ''' - return 'selinux' if 'selinux.getenforce' in __salt__ else False + """ + if "selinux.getenforce" in __salt__: + return "selinux" + return (False, "selinux module could not be loaded") def _refine_mode(mode): - ''' + """ Return a mode value that is predictable - ''' + """ mode = six.text_type(mode).lower() - if any([mode.startswith('e'), - mode == '1', - mode == 'on']): - return 'Enforcing' - if any([mode.startswith('p'), - mode == '0', - mode == 'off']): - return 'Permissive' - if any([mode.startswith('d')]): - return 'Disabled' - return 'unknown' + if any([mode.startswith("e"), mode == "1", mode == "on"]): + return "Enforcing" + if any([mode.startswith("p"), mode == "0", mode == "off"]): + return "Permissive" + if any([mode.startswith("d")]): + return "Disabled" + return "unknown" def _refine_value(value): - ''' + """ Return a yes/no value, or None if the input is invalid - ''' + """ value = six.text_type(value).lower() - if value in ('1', 'on', 'yes', 'true'): - return 'on' - if value in ('0', 'off', 'no', 'false'): - return 'off' + if value in ("1", "on", "yes", "true"): + return "on" + if value in ("0", "off", "no", "false"): + return "off" return None def _refine_module_state(module_state): - ''' + """ Return a predictable value, or allow us to error out .. versionadded:: 2016.3.0 - ''' + """ module_state = six.text_type(module_state).lower() - if module_state in ('1', 'on', 'yes', 'true', 'enabled'): - return 'enabled' - if module_state in ('0', 'off', 'no', 'false', 'disabled'): - return 'disabled' - return 'unknown' + if module_state in ("1", "on", "yes", "true", "enabled"): + return "enabled" + if module_state in ("0", "off", "no", "false", "disabled"): + return "disabled" + return "unknown" def mode(name): - ''' + """ Verifies the mode SELinux is running in, can be set to enforcing, permissive, or disabled @@ -92,49 +90,45 @@ def mode(name): name The mode to run SELinux in, permissive, enforcing, or disabled. - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} tmode = _refine_mode(name) - if tmode == 'unknown': - ret['comment'] = '{0} is not an accepted mode'.format(name) + if tmode == "unknown": + ret["comment"] = "{0} is not an accepted mode".format(name) return ret # Either the current mode in memory or a non-matching config value # will trigger setenforce - mode = __salt__['selinux.getenforce']() - config = __salt__['selinux.getconfig']() + mode = __salt__["selinux.getenforce"]() + config = __salt__["selinux.getconfig"]() # Just making sure the oldmode reflects the thing that didn't match tmode if mode == tmode and mode != config and tmode != config: mode = config if mode == tmode: - ret['result'] = True - ret['comment'] = 'SELinux is already in {0} mode'.format(tmode) + ret["result"] = True + ret["comment"] = "SELinux is already in {0} mode".format(tmode) return ret # The mode needs to change... - if __opts__['test']: - ret['comment'] = 'SELinux mode is set to be changed to {0}'.format( - tmode) - ret['result'] = None - ret['changes'] = {'old': mode, - 'new': tmode} + if __opts__["test"]: + ret["comment"] = "SELinux mode is set to be changed to {0}".format(tmode) + ret["result"] = None + ret["changes"] = {"old": mode, "new": tmode} return ret - oldmode, mode = mode, __salt__['selinux.setenforce'](tmode) - if mode == tmode or (tmode == 'Disabled' and __salt__['selinux.getconfig']() == tmode): - ret['result'] = True - ret['comment'] = 'SELinux has been set to {0} mode'.format(tmode) - ret['changes'] = {'old': oldmode, - 'new': mode} + oldmode, mode = mode, __salt__["selinux.setenforce"](tmode) + if mode == tmode or ( + tmode == "Disabled" and __salt__["selinux.getconfig"]() == tmode + ): + ret["result"] = True + ret["comment"] = "SELinux has been set to {0} mode".format(tmode) + ret["changes"] = {"old": oldmode, "new": mode} return ret - ret['comment'] = 'Failed to set SELinux to {0} mode'.format(tmode) + ret["comment"] = "Failed to set SELinux to {0} mode".format(tmode) return ret def boolean(name, value, persist=False): - ''' + """ Set up an SELinux boolean name @@ -146,53 +140,48 @@ def boolean(name, value, persist=False): persist Defaults to False, set persist to true to make the boolean apply on a reboot - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} - bools = __salt__['selinux.list_sebool']() + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + bools = __salt__["selinux.list_sebool"]() if name not in bools: - ret['comment'] = 'Boolean {0} is not available'.format(name) - ret['result'] = False + ret["comment"] = "Boolean {0} is not available".format(name) + ret["result"] = False return ret rvalue = _refine_value(value) if rvalue is None: - ret['comment'] = '{0} is not a valid value for the ' \ - 'boolean'.format(value) - ret['result'] = False + ret["comment"] = "{0} is not a valid value for the " "boolean".format(value) + ret["result"] = False return ret - state = bools[name]['State'] == rvalue - default = bools[name]['Default'] == rvalue + state = bools[name]["State"] == rvalue + default = bools[name]["Default"] == rvalue if persist: if state and default: - ret['comment'] = 'Boolean is in the correct state' + ret["comment"] = "Boolean is in the correct state" return ret else: if state: - ret['comment'] = 'Boolean is in the correct state' + ret["comment"] = "Boolean is in the correct state" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Boolean {0} is set to be changed to {1}'.format( - name, rvalue) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Boolean {0} is set to be changed to {1}".format(name, rvalue) return ret - ret['result'] = __salt__['selinux.setsebool'](name, rvalue, persist) - if ret['result']: - ret['comment'] = 'Boolean {0} has been set to {1}'.format(name, rvalue) - ret['changes'].update({'State': {'old': bools[name]['State'], - 'new': rvalue}}) + ret["result"] = __salt__["selinux.setsebool"](name, rvalue, persist) + if ret["result"]: + ret["comment"] = "Boolean {0} has been set to {1}".format(name, rvalue) + ret["changes"].update({"State": {"old": bools[name]["State"], "new": rvalue}}) if persist and not default: - ret['changes'].update({'Default': {'old': bools[name]['Default'], - 'new': rvalue}}) + ret["changes"].update( + {"Default": {"old": bools[name]["Default"], "new": rvalue}} + ) return ret - ret['comment'] = 'Failed to set the boolean {0} to {1}'.format(name, rvalue) + ret["comment"] = "Failed to set the boolean {0} to {1}".format(name, rvalue) return ret -def module(name, module_state='Enabled', version='any', **opts): - ''' +def module(name, module_state="Enabled", version="any", **opts): + """ Enable/Disable and optionally force a specific version for an SELinux module name @@ -215,108 +204,105 @@ def module(name, module_state='Enabled', version='any', **opts): Setting to True removes module .. versionadded:: 2016.3.0 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} - if opts.get('install', False) and opts.get('remove', False): - ret['result'] = False - ret['comment'] = 'Cannot install and remove at the same time' + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + if opts.get("install", False) and opts.get("remove", False): + ret["result"] = False + ret["comment"] = "Cannot install and remove at the same time" return ret - if opts.get('install', False): - module_path = opts.get('source', name) + if opts.get("install", False): + module_path = opts.get("source", name) ret = module_install(module_path) - if not ret['result']: + if not ret["result"]: return ret - elif opts.get('remove', False): + elif opts.get("remove", False): return module_remove(name) - modules = __salt__['selinux.list_semod']() + modules = __salt__["selinux.list_semod"]() if name not in modules: - ret['comment'] = 'Module {0} is not available'.format(name) - ret['result'] = False + ret["comment"] = "Module {0} is not available".format(name) + ret["result"] = False return ret rmodule_state = _refine_module_state(module_state) - if rmodule_state == 'unknown': - ret['comment'] = '{0} is not a valid state for the ' \ - '{1} module.'.format(module_state, module) - ret['result'] = False + if rmodule_state == "unknown": + ret["comment"] = "{0} is not a valid state for the " "{1} module.".format( + module_state, module + ) + ret["result"] = False return ret - if version != 'any': - installed_version = modules[name]['Version'] + if version != "any": + installed_version = modules[name]["Version"] if not installed_version == version: - ret['comment'] = 'Module version is {0} and does not match ' \ - 'the desired version of {1} or you are ' \ - 'using semodule >= 2.4'.format(installed_version, version) - ret['result'] = False + ret["comment"] = ( + "Module version is {0} and does not match " + "the desired version of {1} or you are " + "using semodule >= 2.4".format(installed_version, version) + ) + ret["result"] = False return ret - current_module_state = _refine_module_state(modules[name]['Enabled']) + current_module_state = _refine_module_state(modules[name]["Enabled"]) if rmodule_state == current_module_state: - ret['comment'] = 'Module {0} is in the desired state'.format(name) + ret["comment"] = "Module {0} is in the desired state".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Module {0} is set to be toggled to {1}'.format( - name, module_state) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Module {0} is set to be toggled to {1}".format( + name, module_state + ) return ret - if __salt__['selinux.setsemod'](name, rmodule_state): - ret['comment'] = 'Module {0} has been set to {1}'.format(name, module_state) + if __salt__["selinux.setsemod"](name, rmodule_state): + ret["comment"] = "Module {0} has been set to {1}".format(name, module_state) return ret - ret['result'] = False - ret['comment'] = 'Failed to set the Module {0} to {1}'.format(name, module_state) + ret["result"] = False + ret["comment"] = "Failed to set the Module {0} to {1}".format(name, module_state) return ret def module_install(name): - ''' + """ Installs custom SELinux module from given file name Path to file with module to install .. versionadded:: 2016.11.6 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} - if __salt__['selinux.install_semod'](name): - ret['comment'] = 'Module {0} has been installed'.format(name) + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + if __salt__["selinux.install_semod"](name): + ret["comment"] = "Module {0} has been installed".format(name) return ret - ret['result'] = False - ret['comment'] = 'Failed to install module {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to install module {0}".format(name) return ret def module_remove(name): - ''' + """ Removes SELinux module name The name of the module to remove .. versionadded:: 2016.11.6 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} - modules = __salt__['selinux.list_semod']() + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + modules = __salt__["selinux.list_semod"]() if name not in modules: - ret['comment'] = 'Module {0} is not available'.format(name) - ret['result'] = False + ret["comment"] = "Module {0} is not available".format(name) + ret["result"] = False return ret - if __salt__['selinux.remove_semod'](name): - ret['comment'] = 'Module {0} has been removed'.format(name) + if __salt__["selinux.remove_semod"](name): + ret["comment"] = "Module {0} has been removed".format(name) return ret - ret['result'] = False - ret['comment'] = 'Failed to remove module {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to remove module {0}".format(name) return ret -def fcontext_policy_present(name, sel_type, filetype='a', sel_user=None, sel_level=None): - ''' +def fcontext_policy_present( + name, sel_type, filetype="a", sel_user=None, sel_level=None +): + """ .. versionadded:: 2017.7.0 Makes sure a SELinux policy for a given filespec (name), filetype @@ -338,66 +324,75 @@ def fcontext_policy_present(name, sel_type, filetype='a', sel_user=None, sel_lev sel_level The SELinux MLS range. - ''' - ret = {'name': name, 'result': False, 'changes': {}, 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} new_state = {} old_state = {} - filetype_str = __salt__['selinux.filetype_id_to_string'](filetype) - current_state = __salt__['selinux.fcontext_get_policy']( - name=name, - filetype=filetype, - sel_type=sel_type, - sel_user=sel_user, - sel_level=sel_level) + filetype_str = __salt__["selinux.filetype_id_to_string"](filetype) + current_state = __salt__["selinux.fcontext_get_policy"]( + name=name, + filetype=filetype, + sel_type=sel_type, + sel_user=sel_user, + sel_level=sel_level, + ) if not current_state: - new_state = {name: {'filetype': filetype_str, 'sel_type': sel_type}} - if __opts__['test']: - ret.update({'result': None}) + new_state = {name: {"filetype": filetype_str, "sel_type": sel_type}} + if __opts__["test"]: + ret.update({"result": None}) else: - add_ret = __salt__['selinux.fcontext_add_policy']( - name=name, - filetype=filetype, - sel_type=sel_type, - sel_user=sel_user, - sel_level=sel_level) - if add_ret['retcode'] != 0: - ret.update({'comment': 'Error adding new rule: {0}'.format(add_ret)}) + add_ret = __salt__["selinux.fcontext_add_policy"]( + name=name, + filetype=filetype, + sel_type=sel_type, + sel_user=sel_user, + sel_level=sel_level, + ) + if add_ret["retcode"] != 0: + ret.update({"comment": "Error adding new rule: {0}".format(add_ret)}) else: - ret.update({'result': True}) + ret.update({"result": True}) else: - if current_state['sel_type'] != sel_type: - old_state.update({name: {'sel_type': current_state['sel_type']}}) - new_state.update({name: {'sel_type': sel_type}}) + if current_state["sel_type"] != sel_type: + old_state.update({name: {"sel_type": current_state["sel_type"]}}) + new_state.update({name: {"sel_type": sel_type}}) else: - ret.update({'result': True, - 'comment': 'SELinux policy for "{0}" already present '.format(name) + - 'with specified filetype "{0}" and sel_type "{1}".'.format( - filetype_str, - sel_type)}) + ret.update( + { + "result": True, + "comment": 'SELinux policy for "{0}" already present '.format(name) + + 'with specified filetype "{0}" and sel_type "{1}".'.format( + filetype_str, sel_type + ), + } + ) return ret # Removal of current rule is not neccesary, since adding a new rule for the same # filespec and the same filetype automatically overwrites - if __opts__['test']: - ret.update({'result': None}) + if __opts__["test"]: + ret.update({"result": None}) else: - change_ret = __salt__['selinux.fcontext_add_policy']( - name=name, - filetype=filetype, - sel_type=sel_type, - sel_user=sel_user, - sel_level=sel_level) - if change_ret['retcode'] != 0: - ret.update({'comment': 'Error adding new rule: {0}'.format(change_ret)}) + change_ret = __salt__["selinux.fcontext_add_policy"]( + name=name, + filetype=filetype, + sel_type=sel_type, + sel_user=sel_user, + sel_level=sel_level, + ) + if change_ret["retcode"] != 0: + ret.update({"comment": "Error adding new rule: {0}".format(change_ret)}) else: - ret.update({'result': True}) - if ret['result'] and (new_state or old_state): - ret['changes'].update({'old': old_state, 'new': new_state}) + ret.update({"result": True}) + if ret["result"] and (new_state or old_state): + ret["changes"].update({"old": old_state, "new": new_state}) return ret -def fcontext_policy_absent(name, filetype='a', sel_type=None, sel_user=None, sel_level=None): - ''' +def fcontext_policy_absent( + name, filetype="a", sel_type=None, sel_user=None, sel_level=None +): + """ .. versionadded:: 2017.7.0 Makes sure an SELinux file context policy for a given filespec @@ -419,70 +414,82 @@ def fcontext_policy_absent(name, filetype='a', sel_type=None, sel_user=None, sel sel_level The SELinux MLS range. - ''' - ret = {'name': name, 'result': False, 'changes': {}, 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} new_state = {} old_state = {} - current_state = __salt__['selinux.fcontext_get_policy']( - name=name, - filetype=filetype, - sel_type=sel_type, - sel_user=sel_user, - sel_level=sel_level) + current_state = __salt__["selinux.fcontext_get_policy"]( + name=name, + filetype=filetype, + sel_type=sel_type, + sel_user=sel_user, + sel_level=sel_level, + ) if not current_state: - ret.update({'result': True, - 'comment': 'SELinux policy for "{0}" already absent '.format(name) + - 'with specified filetype "{0}" and sel_type "{1}".'.format( - filetype, - sel_type)}) + ret.update( + { + "result": True, + "comment": 'SELinux policy for "{0}" already absent '.format(name) + + 'with specified filetype "{0}" and sel_type "{1}".'.format( + filetype, sel_type + ), + } + ) return ret else: old_state.update({name: current_state}) - ret['changes'].update({'old': old_state, 'new': new_state}) - if __opts__['test']: - ret.update({'result': None}) + ret["changes"].update({"old": old_state, "new": new_state}) + if __opts__["test"]: + ret.update({"result": None}) else: - remove_ret = __salt__['selinux.fcontext_delete_policy']( - name=name, - filetype=filetype, - sel_type=sel_type or current_state['sel_type'], - sel_user=sel_user, - sel_level=sel_level) - if remove_ret['retcode'] != 0: - ret.update({'comment': 'Error removing policy: {0}'.format(remove_ret)}) + remove_ret = __salt__["selinux.fcontext_delete_policy"]( + name=name, + filetype=filetype, + sel_type=sel_type or current_state["sel_type"], + sel_user=sel_user, + sel_level=sel_level, + ) + if remove_ret["retcode"] != 0: + ret.update({"comment": "Error removing policy: {0}".format(remove_ret)}) else: - ret.update({'result': True}) + ret.update({"result": True}) return ret def fcontext_policy_applied(name, recursive=False): - ''' + """ .. versionadded:: 2017.7.0 Checks and makes sure the SELinux policies for a given filespec are applied. - ''' - ret = {'name': name, 'result': False, 'changes': {}, 'comment': ''} + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - changes_text = __salt__['selinux.fcontext_policy_is_applied'](name, recursive) - if changes_text == '': - ret.update({'result': True, - 'comment': 'SElinux policies are already applied for filespec "{0}"'.format(name)}) + changes_text = __salt__["selinux.fcontext_policy_is_applied"](name, recursive) + if changes_text == "": + ret.update( + { + "result": True, + "comment": 'SElinux policies are already applied for filespec "{0}"'.format( + name + ), + } + ) return ret - if __opts__['test']: - ret.update({'result': None}) + if __opts__["test"]: + ret.update({"result": None}) else: - apply_ret = __salt__['selinux.fcontext_apply_policy'](name, recursive) - if apply_ret['retcode'] != 0: - ret.update({'comment': apply_ret}) + apply_ret = __salt__["selinux.fcontext_apply_policy"](name, recursive) + if apply_ret["retcode"] != 0: + ret.update({"comment": apply_ret}) else: - ret.update({'result': True}) - ret.update({'changes': apply_ret.get('changes')}) + ret.update({"result": True}) + ret.update({"changes": apply_ret.get("changes")}) return ret def port_policy_present(name, sel_type, protocol=None, port=None, sel_range=None): - ''' + """ .. versionadded:: 2019.2.0 Makes sure an SELinux port policy for a given port, protocol and SELinux context type is present. @@ -501,43 +508,45 @@ def port_policy_present(name, sel_type, protocol=None, port=None, sel_range=None sel_range The SELinux MLS/MCS Security Range. - ''' - ret = {'name': name, 'result': False, 'changes': {}, 'comment': ''} - old_state = __salt__['selinux.port_get_policy']( - name=name, - sel_type=sel_type, - protocol=protocol, - port=port, ) + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} + old_state = __salt__["selinux.port_get_policy"]( + name=name, sel_type=sel_type, protocol=protocol, port=port, + ) if old_state: - ret.update({'result': True, - 'comment': 'SELinux policy for "{0}" already present '.format(name) + - 'with specified sel_type "{0}", protocol "{1}" and port "{2}".'.format( - sel_type, protocol, port)}) + ret.update( + { + "result": True, + "comment": 'SELinux policy for "{0}" already present '.format(name) + + 'with specified sel_type "{0}", protocol "{1}" and port "{2}".'.format( + sel_type, protocol, port + ), + } + ) return ret - if __opts__['test']: - ret.update({'result': None}) + if __opts__["test"]: + ret.update({"result": None}) else: - add_ret = __salt__['selinux.port_add_policy']( + add_ret = __salt__["selinux.port_add_policy"]( name=name, sel_type=sel_type, protocol=protocol, port=port, - sel_range=sel_range, ) - if add_ret['retcode'] != 0: - ret.update({'comment': 'Error adding new policy: {0}'.format(add_ret)}) + sel_range=sel_range, + ) + if add_ret["retcode"] != 0: + ret.update({"comment": "Error adding new policy: {0}".format(add_ret)}) else: - ret.update({'result': True}) - new_state = __salt__['selinux.port_get_policy']( - name=name, - sel_type=sel_type, - protocol=protocol, - port=port, ) - ret['changes'].update({'old': old_state, 'new': new_state}) + ret.update({"result": True}) + new_state = __salt__["selinux.port_get_policy"]( + name=name, sel_type=sel_type, protocol=protocol, port=port, + ) + ret["changes"].update({"old": old_state, "new": new_state}) return ret def port_policy_absent(name, sel_type=None, protocol=None, port=None): - ''' + """ .. versionadded:: 2019.2.0 Makes sure an SELinux port policy for a given port, protocol and SELinux context type is absent. @@ -554,34 +563,34 @@ def port_policy_absent(name, sel_type=None, protocol=None, port=None): port The port or port range. Required if name is not formatted. - ''' - ret = {'name': name, 'result': False, 'changes': {}, 'comment': ''} - old_state = __salt__['selinux.port_get_policy']( - name=name, - sel_type=sel_type, - protocol=protocol, - port=port, ) + """ + ret = {"name": name, "result": False, "changes": {}, "comment": ""} + old_state = __salt__["selinux.port_get_policy"]( + name=name, sel_type=sel_type, protocol=protocol, port=port, + ) if not old_state: - ret.update({'result': True, - 'comment': 'SELinux policy for "{0}" already absent '.format(name) + - 'with specified sel_type "{0}", protocol "{1}" and port "{2}".'.format( - sel_type, protocol, port)}) + ret.update( + { + "result": True, + "comment": 'SELinux policy for "{0}" already absent '.format(name) + + 'with specified sel_type "{0}", protocol "{1}" and port "{2}".'.format( + sel_type, protocol, port + ), + } + ) return ret - if __opts__['test']: - ret.update({'result': None}) + if __opts__["test"]: + ret.update({"result": None}) else: - delete_ret = __salt__['selinux.port_delete_policy']( - name=name, - protocol=protocol, - port=port, ) - if delete_ret['retcode'] != 0: - ret.update({'comment': 'Error deleting policy: {0}'.format(delete_ret)}) + delete_ret = __salt__["selinux.port_delete_policy"]( + name=name, protocol=protocol, port=port, + ) + if delete_ret["retcode"] != 0: + ret.update({"comment": "Error deleting policy: {0}".format(delete_ret)}) else: - ret.update({'result': True}) - new_state = __salt__['selinux.port_get_policy']( - name=name, - sel_type=sel_type, - protocol=protocol, - port=port, ) - ret['changes'].update({'old': old_state, 'new': new_state}) + ret.update({"result": True}) + new_state = __salt__["selinux.port_get_policy"]( + name=name, sel_type=sel_type, protocol=protocol, port=port, + ) + ret["changes"].update({"old": old_state, "new": new_state}) return ret diff --git a/salt/states/serverdensity_device.py b/salt/states/serverdensity_device.py index b3e2af8e169..2009797b1d0 100644 --- a/salt/states/serverdensity_device.py +++ b/salt/states/serverdensity_device.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Monitor Server with Server Density ================================== @@ -45,10 +45,11 @@ Example: 'server_name': serverdensity_device.monitored -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -67,36 +68,42 @@ log = logging.getLogger(__name__) def _get_salt_params(): - ''' + """ Try to get all sort of parameters for Server Density server info. NOTE: Missing publicDNS and publicIPs parameters. There might be way of getting them with salt-cloud. - ''' - all_stats = __salt__['status.all_status']() - all_grains = __salt__['grains.items']() + """ + all_stats = __salt__["status.all_status"]() + all_grains = __salt__["grains.items"]() params = {} try: - params['name'] = all_grains['id'] - params['hostname'] = all_grains['host'] - if all_grains['kernel'] == 'Darwin': - sd_os = {'code': 'mac', 'name': 'Mac'} + params["name"] = all_grains["id"] + params["hostname"] = all_grains["host"] + if all_grains["kernel"] == "Darwin": + sd_os = {"code": "mac", "name": "Mac"} else: - sd_os = {'code': all_grains['kernel'].lower(), 'name': all_grains['kernel']} - params['os'] = salt.utils.json.dumps(sd_os) - params['cpuCores'] = all_stats['cpuinfo']['cpu cores'] - params['installedRAM'] = six.text_type(int(all_stats['meminfo']['MemTotal']['value']) / 1024) - params['swapSpace'] = six.text_type(int(all_stats['meminfo']['SwapTotal']['value']) / 1024) - params['privateIPs'] = salt.utils.json.dumps(all_grains['fqdn_ip4']) - params['privateDNS'] = salt.utils.json.dumps(all_grains['fqdn']) + sd_os = {"code": all_grains["kernel"].lower(), "name": all_grains["kernel"]} + params["os"] = salt.utils.json.dumps(sd_os) + params["cpuCores"] = all_stats["cpuinfo"]["cpu cores"] + params["installedRAM"] = six.text_type( + int(all_stats["meminfo"]["MemTotal"]["value"]) / 1024 + ) + params["swapSpace"] = six.text_type( + int(all_stats["meminfo"]["SwapTotal"]["value"]) / 1024 + ) + params["privateIPs"] = salt.utils.json.dumps(all_grains["fqdn_ip4"]) + params["privateDNS"] = salt.utils.json.dumps(all_grains["fqdn"]) except KeyError: pass return params -def monitored(name, group=None, salt_name=True, salt_params=True, agent_version=1, **params): - ''' +def monitored( + name, group=None, salt_name=True, salt_params=True, agent_version=1, **params +): + """ Device is monitored with Server Density. name @@ -147,18 +154,18 @@ def monitored(name, group=None, salt_name=True, salt_params=True, agent_version= - group: web-servers - cpuCores: 2 - os: '{"code": "linux", "name": "Linux"}' - ''' - ret = {'name': name, 'changes': {}, 'result': None, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} params_from_salt = _get_salt_params() if salt_name: - name = params_from_salt.pop('name') - ret['name'] = name + name = params_from_salt.pop("name") + ret["name"] = name else: - params_from_salt.pop('name') + params_from_salt.pop("name") if group: - params['group'] = group + params["group"] = group if agent_version != 2: # Anything different from 2 will fallback into the v1. @@ -172,45 +179,57 @@ def monitored(name, group=None, salt_name=True, salt_params=True, agent_version= else: params_to_use = params - device_in_sd = True if __salt__['serverdensity_device.ls'](name=name) else False - sd_agent_installed = True if 'sd-agent' in __salt__['pkg.list_pkgs']() else False + device_in_sd = True if __salt__["serverdensity_device.ls"](name=name) else False + sd_agent_installed = True if "sd-agent" in __salt__["pkg.list_pkgs"]() else False if device_in_sd and sd_agent_installed: - ret['result'] = True - ret['comment'] = 'Such server name already exists in this Server Density account. And sd-agent is installed' - ret['changes'] = {} + ret["result"] = True + ret[ + "comment" + ] = "Such server name already exists in this Server Density account. And sd-agent is installed" + ret["changes"] = {} return ret if not device_in_sd: - device = __salt__['serverdensity_device.create'](name, **params_from_salt) - agent_key = device['agentKey'] - ret['comment'] = 'Device created in Server Density db.' - ret['changes'] = {'device_created': device} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Device set to be created in Server Density db.' + device = __salt__["serverdensity_device.create"](name, **params_from_salt) + agent_key = device["agentKey"] + ret["comment"] = "Device created in Server Density db." + ret["changes"] = {"device_created": device} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Device set to be created in Server Density db." return ret elif device_in_sd: - device = __salt__['serverdensity_device.ls'](name=name)[0] - agent_key = device['agentKey'] - ret['comment'] = 'Device was already in Server Density db.' + device = __salt__["serverdensity_device.ls"](name=name)[0] + agent_key = device["agentKey"] + ret["comment"] = "Device was already in Server Density db." else: - ret['result'] = False - ret['comment'] = 'Failed to create device in Server Density DB and this device does not exist in db either.' - ret['changes'] = {} - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Agent is not installed and device is not in the Server Density DB' + ret["result"] = False + ret[ + "comment" + ] = "Failed to create device in Server Density DB and this device does not exist in db either." + ret["changes"] = {} + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "Agent is not installed and device is not in the Server Density DB" return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Server Density agent is set to be installed and device created in the Server Density DB' + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "Server Density agent is set to be installed and device created in the Server Density DB" return ret - installed_agent = __salt__['serverdensity_device.install_agent'](agent_key, agent_version) + installed_agent = __salt__["serverdensity_device.install_agent"]( + agent_key, agent_version + ) - ret['result'] = True - ret['comment'] = 'Successfully installed agent and created device in Server Density db.' - ret['changes'] = {'created_device': device, 'installed_agent': installed_agent} + ret["result"] = True + ret[ + "comment" + ] = "Successfully installed agent and created device in Server Density db." + ret["changes"] = {"created_device": device, "installed_agent": installed_agent} return ret diff --git a/salt/states/service.py b/salt/states/service.py index 7660f5a3cb3..5685ab73492 100644 --- a/salt/states/service.py +++ b/salt/states/service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Starting or restarting of services and daemons ============================================== @@ -56,37 +56,40 @@ set the reload value to True: More details regarding ``watch`` can be found in the :ref:`Requisites <requisites>` documentation. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import time # Import Salt libs import salt.utils.data import salt.utils.platform -from salt.utils.args import get_function_argspec as _argspec from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six +from salt.utils.args import get_function_argspec as _argspec -SYSTEMD_ONLY = ('no_block', 'unmask', 'unmask_runtime') +SYSTEMD_ONLY = ("no_block", "unmask", "unmask_runtime") -__virtualname__ = 'service' +__virtualname__ = "service" def __virtual__(): - ''' + """ Only make these states available if a service provider has been detected or assigned for this minion - ''' - if 'service.start' in __salt__: + """ + if "service.start" in __salt__: return __virtualname__ else: - return (False, 'No service execution module loaded: ' - 'check support for service management on {0} ' - ''.format(__grains__.get('osfinger', __grains__['os'])) - ) + return ( + False, + "No service execution module loaded: " + "check support for service management on {0} " + "".format(__grains__.get("osfinger", __grains__["os"])), + ) # Double-asterisk deliberately not used here @@ -100,33 +103,33 @@ def _get_systemd_only(func, kwargs): ret[systemd_arg] = kwargs[systemd_arg] else: warnings.append( - 'The \'{0}\' argument is not supported by this ' - 'platform/action'.format(systemd_arg) + "The '{0}' argument is not supported by this " + "platform/action".format(systemd_arg) ) return ret, warnings def _add_warnings(ret, warnings): - current_warnings = ret.setdefault('warnings', []) + current_warnings = ret.setdefault("warnings", []) current_warnings.extend([x for x in warnings if x not in current_warnings]) def _enabled_used_error(ret): - ''' + """ Warn of potential typo. - ''' - ret['result'] = False - ret['comment'] = ( - 'Service {0} uses non-existent option "enabled". ' + - 'Perhaps "enable" option was intended?' - ).format(ret['name']) + """ + ret["result"] = False + ret["comment"] = ( + 'Service {0} uses non-existent option "enabled". ' + + 'Perhaps "enable" option was intended?' + ).format(ret["name"]) return ret def _enable(name, started, result=True, **kwargs): - ''' + """ Enable the service - ''' + """ ret = {} # is service available? @@ -134,71 +137,78 @@ def _enable(name, started, result=True, **kwargs): if not _available(name, ret): return ret except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret # Set default expected result - ret['result'] = result + ret["result"] = result # Check to see if this minion supports enable - if 'service.enable' not in __salt__ or 'service.enabled' not in __salt__: + if "service.enable" not in __salt__ or "service.enabled" not in __salt__: if started is True: - ret['comment'] = ('Enable is not available on this minion,' - ' service {0} started').format(name) + ret["comment"] = ( + "Enable is not available on this minion," " service {0} started" + ).format(name) elif started is None: - ret['comment'] = ('Enable is not available on this minion,' - ' service {0} is in the desired state' - ).format(name) + ret["comment"] = ( + "Enable is not available on this minion," + " service {0} is in the desired state" + ).format(name) else: - ret['comment'] = ('Enable is not available on this minion,' - ' service {0} is dead').format(name) + ret["comment"] = ( + "Enable is not available on this minion," " service {0} is dead" + ).format(name) return ret # Service can be enabled - before_toggle_enable_status = __salt__['service.enabled'](name, **kwargs) + before_toggle_enable_status = __salt__["service.enabled"](name, **kwargs) if before_toggle_enable_status: # Service is enabled if started is True: - ret['comment'] = ('Service {0} is already enabled,' - ' and is running').format(name) + ret["comment"] = ( + "Service {0} is already enabled," " and is running" + ).format(name) elif started is None: # always be sure in this case to reset the changes dict - ret['changes'] = {} - ret['comment'] = ('Service {0} is already enabled,' - ' and is in the desired state').format(name) + ret["changes"] = {} + ret["comment"] = ( + "Service {0} is already enabled," " and is in the desired state" + ).format(name) else: - ret['comment'] = ('Service {0} is already enabled,' - ' and is dead').format(name) + ret["comment"] = ("Service {0} is already enabled," " and is dead").format( + name + ) return ret # Service needs to be enabled - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Service {0} set to be enabled'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Service {0} set to be enabled".format(name) return ret try: - if __salt__['service.enable'](name, **kwargs): + if __salt__["service.enable"](name, **kwargs): # Service has been enabled - ret['changes'] = {} - after_toggle_enable_status = __salt__['service.enabled']( - name, - **kwargs) + ret["changes"] = {} + after_toggle_enable_status = __salt__["service.enabled"](name, **kwargs) # on upstart, certain services like apparmor will always return # False, even if correctly activated # do not trigger a change if before_toggle_enable_status != after_toggle_enable_status: - ret['changes'][name] = True + ret["changes"][name] = True if started is True: - ret['comment'] = ('Service {0} has been enabled,' - ' and is running').format(name) + ret["comment"] = ( + "Service {0} has been enabled," " and is running" + ).format(name) elif started is None: - ret['comment'] = ('Service {0} has been enabled,' - ' and is in the desired state').format(name) + ret["comment"] = ( + "Service {0} has been enabled," " and is in the desired state" + ).format(name) else: - ret['comment'] = ('Service {0} has been enabled,' - ' and is dead').format(name) + ret["comment"] = ( + "Service {0} has been enabled," " and is dead" + ).format(name) return ret except CommandExecutionError as exc: enable_error = exc.strerror @@ -206,19 +216,25 @@ def _enable(name, started, result=True, **kwargs): enable_error = False # Service failed to be enabled - ret['result'] = False + ret["result"] = False if started is True: - ret['comment'] = ('Failed when setting service {0} to start at boot,' - ' but the service is running').format(name) + ret["comment"] = ( + "Failed when setting service {0} to start at boot," + " but the service is running" + ).format(name) elif started is None: - ret['comment'] = ('Failed when setting service {0} to start at boot,' - ' but the service was already running').format(name) + ret["comment"] = ( + "Failed when setting service {0} to start at boot," + " but the service was already running" + ).format(name) else: - ret['comment'] = ('Failed when setting service {0} to start at boot,' - ' and the service is dead').format(name) + ret["comment"] = ( + "Failed when setting service {0} to start at boot," + " and the service is dead" + ).format(name) if enable_error: - ret['comment'] += '. Additional information follows:\n\n{0}'.format( + ret["comment"] += ". Additional information follows:\n\n{0}".format( enable_error ) @@ -226,122 +242,133 @@ def _enable(name, started, result=True, **kwargs): def _disable(name, started, result=True, **kwargs): - ''' + """ Disable the service - ''' + """ ret = {} # is service available? try: if not _available(name, ret): - ret['result'] = True + ret["result"] = True return ret except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret # Set default expected result - ret['result'] = result + ret["result"] = result # is enable/disable available? - if 'service.disable' not in __salt__ or 'service.disabled' not in __salt__: + if "service.disable" not in __salt__ or "service.disabled" not in __salt__: if started is True: - ret['comment'] = ('Disable is not available on this minion,' - ' service {0} started').format(name) + ret["comment"] = ( + "Disable is not available on this minion," " service {0} started" + ).format(name) elif started is None: - ret['comment'] = ('Disable is not available on this minion,' - ' service {0} is in the desired state' - ).format(name) + ret["comment"] = ( + "Disable is not available on this minion," + " service {0} is in the desired state" + ).format(name) else: - ret['comment'] = ('Disable is not available on this minion,' - ' service {0} is dead').format(name) + ret["comment"] = ( + "Disable is not available on this minion," " service {0} is dead" + ).format(name) return ret # Service can be disabled if salt.utils.platform.is_windows(): # service.disabled in Windows returns True for services that are set to # Manual start, so we need to check specifically for Disabled - before_toggle_disable_status = __salt__['service.info'](name)['StartType'] in ['Disabled'] + before_toggle_disable_status = __salt__["service.info"](name)["StartType"] in [ + "Disabled" + ] else: - before_toggle_disable_status = __salt__['service.disabled'](name) + before_toggle_disable_status = __salt__["service.disabled"](name) if before_toggle_disable_status: # Service is disabled if started is True: - ret['comment'] = ('Service {0} is already disabled,' - ' and is running').format(name) + ret["comment"] = ( + "Service {0} is already disabled," " and is running" + ).format(name) elif started is None: # always be sure in this case to reset the changes dict - ret['changes'] = {} - ret['comment'] = ('Service {0} is already disabled,' - ' and is in the desired state').format(name) + ret["changes"] = {} + ret["comment"] = ( + "Service {0} is already disabled," " and is in the desired state" + ).format(name) else: - ret['comment'] = ('Service {0} is already disabled,' - ' and is dead').format(name) + ret["comment"] = ("Service {0} is already disabled," " and is dead").format( + name + ) return ret # Service needs to be disabled - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Service {0} set to be disabled'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Service {0} set to be disabled".format(name) return ret - if __salt__['service.disable'](name, **kwargs): + if __salt__["service.disable"](name, **kwargs): # Service has been disabled - ret['changes'] = {} - after_toggle_disable_status = __salt__['service.disabled'](name) + ret["changes"] = {} + after_toggle_disable_status = __salt__["service.disabled"](name) # on upstart, certain services like apparmor will always return # False, even if correctly activated # do not trigger a change if before_toggle_disable_status != after_toggle_disable_status: - ret['changes'][name] = True + ret["changes"][name] = True if started is True: - ret['comment'] = ('Service {0} has been disabled,' - ' and is running').format(name) + ret["comment"] = ( + "Service {0} has been disabled," " and is running" + ).format(name) elif started is None: - ret['comment'] = ('Service {0} has been disabled,' - ' and is in the desired state').format(name) + ret["comment"] = ( + "Service {0} has been disabled," " and is in the desired state" + ).format(name) else: - ret['comment'] = ('Service {0} has been disabled,' - ' and is dead').format(name) + ret["comment"] = ("Service {0} has been disabled," " and is dead").format( + name + ) return ret # Service failed to be disabled - ret['result'] = False + ret["result"] = False if started is True: - ret['comment'] = ('Failed when setting service {0} to not start' - ' at boot, and is running').format(name) + ret["comment"] = ( + "Failed when setting service {0} to not start" " at boot, and is running" + ).format(name) elif started is None: - ret['comment'] = ('Failed when setting service {0} to not start' - ' at boot, but the service was already running' - ).format(name) + ret["comment"] = ( + "Failed when setting service {0} to not start" + " at boot, but the service was already running" + ).format(name) else: - ret['comment'] = ('Failed when setting service {0} to not start' - ' at boot, and the service is dead').format(name) + ret["comment"] = ( + "Failed when setting service {0} to not start" + " at boot, and the service is dead" + ).format(name) return ret def _available(name, ret): - ''' + """ Check if the service is available - ''' + """ avail = False - if 'service.available' in __salt__: - avail = __salt__['service.available'](name) - elif 'service.get_all' in __salt__: - avail = name in __salt__['service.get_all']() + if "service.available" in __salt__: + avail = __salt__["service.available"](name) + elif "service.get_all" in __salt__: + avail = name in __salt__["service.get_all"]() if not avail: - ret['result'] = False - ret['comment'] = 'The named service {0} is not available'.format(name) + ret["result"] = False + ret["comment"] = "The named service {0} is not available".format(name) return avail -def running(name, - enable=None, - sig=None, - init_delay=None, - **kwargs): - ''' +def running(name, enable=None, sig=None, init_delay=None, **kwargs): + """ Ensure that the service is running name @@ -396,14 +423,11 @@ def running(name, another state changes ( example: a file.managed state that creates the service's config file ). More details regarding ``watch`` can be found in the :ref:`Requisites <requisites>` documentation. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # Check for common error: using enabled option instead of enable - if 'enabled' in kwargs: + if "enabled" in kwargs: return _enabled_used_error(ret) # Convert enable to boolean in case user passed a string value @@ -413,36 +437,46 @@ def running(name, # Check if the service is available try: if not _available(name, ret): - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Service {0} not present; if created in this state run, it would have been started'.format(name) + if __opts__.get("test"): + ret["result"] = None + ret[ + "comment" + ] = "Service {0} not present; if created in this state run, it would have been started".format( + name + ) return ret except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret - status_kwargs, warnings = _get_systemd_only(__salt__['service.status'], kwargs) + status_kwargs, warnings = _get_systemd_only(__salt__["service.status"], kwargs) if warnings: _add_warnings(ret, warnings) # lot of custom init script won't or mis implement the status # command, so it is just an indicator but can not be fully trusted - before_toggle_status = __salt__['service.status'](name, sig, **status_kwargs) - if 'service.enabled' in __salt__: - before_toggle_enable_status = __salt__['service.enabled'](name) + before_toggle_status = __salt__["service.status"](name, sig, **status_kwargs) + if "service.enabled" in __salt__: + before_toggle_enable_status = __salt__["service.enabled"](name) else: before_toggle_enable_status = True - unmask_ret = {'comment': ''} - if kwargs.get('unmask', False): - unmask_ret = unmasked(name, kwargs.get('unmask_runtime', False)) + unmask_ret = {"comment": ""} + if kwargs.get("unmask", False): + unmask_ret = unmasked(name, kwargs.get("unmask_runtime", False)) # See if the service is already running if before_toggle_status: - ret['comment'] = '\n'.join( - [_f for _f in ['The service {0} is already running'.format(name), - unmask_ret['comment']] if _f] + ret["comment"] = "\n".join( + [ + _f + for _f in [ + "The service {0} is already running".format(name), + unmask_ret["comment"], + ] + if _f + ] ) if enable is True and not before_toggle_enable_status: ret.update(_enable(name, None, **kwargs)) @@ -451,32 +485,38 @@ def running(name, return ret # Run the tests - if __opts__['test']: - ret['result'] = None - ret['comment'] = '\n'.join( - [_f for _f in ['Service {0} is set to start'.format(name), - unmask_ret['comment']] if _f]) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "\n".join( + [ + _f + for _f in [ + "Service {0} is set to start".format(name), + unmask_ret["comment"], + ] + if _f + ] + ) return ret # Conditionally add systemd-specific args to call to service.start - start_kwargs, warnings = \ - _get_systemd_only(__salt__['service.start'], kwargs) + start_kwargs, warnings = _get_systemd_only(__salt__["service.start"], kwargs) if warnings: _add_warnings(ret, warnings) - if salt.utils.platform.is_windows() and kwargs.get('timeout', False): - start_kwargs.update({'timeout': kwargs.get('timeout')}) + if salt.utils.platform.is_windows() and kwargs.get("timeout", False): + start_kwargs.update({"timeout": kwargs.get("timeout")}) try: - func_ret = __salt__['service.start'](name, **start_kwargs) + func_ret = __salt__["service.start"](name, **start_kwargs) except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret if not func_ret: - ret['result'] = False - ret['comment'] = 'Service {0} failed to start'.format(name) + ret["result"] = False + ret["comment"] = "Service {0} failed to start".format(name) if enable is True: ret.update(_enable(name, False, result=False, **kwargs)) elif enable is False: @@ -487,46 +527,45 @@ def running(name, time.sleep(init_delay) # only force a change state if we have explicitly detected them - after_toggle_status = __salt__['service.status'](name, sig, **status_kwargs) - if 'service.enabled' in __salt__: - after_toggle_enable_status = __salt__['service.enabled'](name) + after_toggle_status = __salt__["service.status"](name, sig, **status_kwargs) + if "service.enabled" in __salt__: + after_toggle_enable_status = __salt__["service.enabled"](name) else: after_toggle_enable_status = True if ( - (before_toggle_enable_status != after_toggle_enable_status) or - (before_toggle_status != after_toggle_status) - ) and not ret.get('changes', {}): - ret['changes'][name] = after_toggle_status + (before_toggle_enable_status != after_toggle_enable_status) + or (before_toggle_status != after_toggle_status) + ) and not ret.get("changes", {}): + ret["changes"][name] = after_toggle_status if after_toggle_status: - ret['comment'] = 'Started Service {0}'.format(name) + ret["comment"] = "Started Service {0}".format(name) else: - ret['comment'] = 'Service {0} failed to start'.format(name) - ret['result'] = False + ret["comment"] = "Service {0} failed to start".format(name) + ret["result"] = False if enable is True: - ret.update(_enable(name, after_toggle_status, result=after_toggle_status, **kwargs)) + ret.update( + _enable(name, after_toggle_status, result=after_toggle_status, **kwargs) + ) elif enable is False: - ret.update(_disable(name, after_toggle_status, result=after_toggle_status, **kwargs)) - - if init_delay: - ret['comment'] = ( - '{0}\nDelayed return for {1} seconds' - .format(ret['comment'], init_delay) + ret.update( + _disable(name, after_toggle_status, result=after_toggle_status, **kwargs) ) - if kwargs.get('unmask', False): - ret['comment'] = '\n'.join([ret['comment'], unmask_ret['comment']]) + if init_delay: + ret["comment"] = "{0}\nDelayed return for {1} seconds".format( + ret["comment"], init_delay + ) + + if kwargs.get("unmask", False): + ret["comment"] = "\n".join([ret["comment"], unmask_ret["comment"]]) return ret -def dead(name, - enable=None, - sig=None, - init_delay=None, - **kwargs): - ''' +def dead(name, enable=None, sig=None, init_delay=None, **kwargs): + """ Ensure that the named service is dead by stopping the service if it is running name @@ -550,14 +589,11 @@ def dead(name, **For systemd minions only.** Stops the service using ``--no-block``. .. versionadded:: 2017.7.0 - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # Check for common error: using enabled option instead of enable - if 'enabled' in kwargs: + if "enabled" in kwargs: return _enabled_used_error(ret) # Convert enable to boolean in case user passed a string value @@ -567,39 +603,45 @@ def dead(name, # Check if the service is available try: if not _available(name, ret): - if __opts__.get('test'): - ret['result'] = None - ret['comment'] = 'Service {0} not present; if created in this state run, it would have been stopped'.format(name) + if __opts__.get("test"): + ret["result"] = None + ret[ + "comment" + ] = "Service {0} not present; if created in this state run, it would have been stopped".format( + name + ) else: # A non-available service is OK here, don't let the state fail # because of it. - ret['result'] = True + ret["result"] = True return ret except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret - status_kwargs, warnings = _get_systemd_only(__salt__['service.status'], kwargs) + status_kwargs, warnings = _get_systemd_only(__salt__["service.status"], kwargs) if warnings: _add_warnings(ret, warnings) # lot of custom init script won't or mis implement the status # command, so it is just an indicator but can not be fully trusted - before_toggle_status = __salt__['service.status'](name, sig, **status_kwargs) - if 'service.enabled' in __salt__: + before_toggle_status = __salt__["service.status"](name, sig, **status_kwargs) + if "service.enabled" in __salt__: if salt.utils.platform.is_windows(): # service.enabled in Windows returns True for services that are set # to Auto start, but services set to Manual can also be disabled - before_toggle_enable_status = __salt__['service.info'](name)['StartType'] in ['Auto', 'Manual'] + before_toggle_enable_status = __salt__["service.info"](name)[ + "StartType" + ] in ["Auto", "Manual"] else: - before_toggle_enable_status = __salt__['service.enabled'](name) + before_toggle_enable_status = __salt__["service.enabled"](name) else: before_toggle_enable_status = True # See if the service is already dead if not before_toggle_status: - ret['comment'] = 'The service {0} is already dead'.format(name) + ret["comment"] = "The service {0} is already dead".format(name) if enable is True and not before_toggle_enable_status: ret.update(_enable(name, None, **kwargs)) elif enable is False and before_toggle_enable_status: @@ -607,23 +649,23 @@ def dead(name, return ret # Run the tests - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Service {0} is set to be killed'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Service {0} is set to be killed".format(name) return ret # Conditionally add systemd-specific args to call to service.start - stop_kwargs, warnings = _get_systemd_only(__salt__['service.stop'], kwargs) + stop_kwargs, warnings = _get_systemd_only(__salt__["service.stop"], kwargs) if warnings: _add_warnings(ret, warnings) - if salt.utils.platform.is_windows() and kwargs.get('timeout', False): - stop_kwargs.update({'timeout': kwargs.get('timeout')}) + if salt.utils.platform.is_windows() and kwargs.get("timeout", False): + stop_kwargs.update({"timeout": kwargs.get("timeout")}) - func_ret = __salt__['service.stop'](name, **stop_kwargs) + func_ret = __salt__["service.stop"](name, **stop_kwargs) if not func_ret: - ret['result'] = False - ret['comment'] = 'Service {0} failed to die'.format(name) + ret["result"] = False + ret["comment"] = "Service {0} failed to die".format(name) if enable is True: ret.update(_enable(name, True, result=False, **kwargs)) elif enable is False: @@ -634,34 +676,40 @@ def dead(name, time.sleep(init_delay) # only force a change state if we have explicitly detected them - after_toggle_status = __salt__['service.status'](name, **status_kwargs) - if 'service.enabled' in __salt__: - after_toggle_enable_status = __salt__['service.enabled'](name) + after_toggle_status = __salt__["service.status"](name, **status_kwargs) + if "service.enabled" in __salt__: + after_toggle_enable_status = __salt__["service.enabled"](name) else: after_toggle_enable_status = True if ( - (before_toggle_enable_status != after_toggle_enable_status) or - (before_toggle_status != after_toggle_status) - ) and not ret.get('changes', {}): - ret['changes'][name] = after_toggle_status + (before_toggle_enable_status != after_toggle_enable_status) + or (before_toggle_status != after_toggle_status) + ) and not ret.get("changes", {}): + ret["changes"][name] = after_toggle_status # be sure to stop, in case we mis detected in the check if after_toggle_status: - ret['result'] = False - ret['comment'] = 'Service {0} failed to die'.format(name) + ret["result"] = False + ret["comment"] = "Service {0} failed to die".format(name) else: - ret['comment'] = 'Service {0} was killed'.format(name) + ret["comment"] = "Service {0} was killed".format(name) if enable is True: - ret.update(_enable(name, after_toggle_status, result=not after_toggle_status, **kwargs)) + ret.update( + _enable(name, after_toggle_status, result=not after_toggle_status, **kwargs) + ) elif enable is False: - ret.update(_disable(name, after_toggle_status, result=not after_toggle_status, **kwargs)) + ret.update( + _disable( + name, after_toggle_status, result=not after_toggle_status, **kwargs + ) + ) return ret def enabled(name, **kwargs): - ''' + """ Ensure that the service is enabled on boot, only use this state if you don't want to manage the running process, remember that if you want to enable a running service to use the enable: True option for the running @@ -669,18 +717,15 @@ def enabled(name, **kwargs): name The name of the init or rc script used to manage the service - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ret.update(_enable(name, None, **kwargs)) return ret def disabled(name, **kwargs): - ''' + """ Ensure that the service is disabled on boot, only use this state if you don't want to manage the running process, remember that if you want to disable a service to use the enable: False option for the running or dead @@ -688,18 +733,15 @@ def disabled(name, **kwargs): name The name of the init or rc script used to manage the service - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ret.update(_disable(name, None, **kwargs)) return ret def masked(name, runtime=False): - ''' + """ .. versionadded:: 2017.7.0 .. note:: @@ -740,51 +782,45 @@ def masked(name, runtime=False): .. _systemd: https://freedesktop.org/wiki/Software/systemd/ - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if 'service.masked' not in __salt__: - ret['comment'] = 'Service masking not available on this minion' - ret['result'] = False + if "service.masked" not in __salt__: + ret["comment"] = "Service masking not available on this minion" + ret["result"] = False return ret - mask_type = 'runtime masked' if runtime else 'masked' - expected_changes = {mask_type: {'old': False, 'new': True}} + mask_type = "runtime masked" if runtime else "masked" + expected_changes = {mask_type: {"old": False, "new": True}} try: - if __salt__['service.masked'](name, runtime): - ret['comment'] = 'Service {0} is already {1}'.format( - name, - mask_type, - ) + if __salt__["service.masked"](name, runtime): + ret["comment"] = "Service {0} is already {1}".format(name, mask_type,) return ret - if __opts__['test']: - ret['result'] = None - ret['changes'] = expected_changes - ret['comment'] = 'Service {0} would be {1}'.format(name, mask_type) + if __opts__["test"]: + ret["result"] = None + ret["changes"] = expected_changes + ret["comment"] = "Service {0} would be {1}".format(name, mask_type) return ret - __salt__['service.mask'](name, runtime) + __salt__["service.mask"](name, runtime) - if __salt__['service.masked'](name, runtime): - ret['changes'] = expected_changes - ret['comment'] = 'Service {0} was {1}'.format(name, mask_type) + if __salt__["service.masked"](name, runtime): + ret["changes"] = expected_changes + ret["comment"] = "Service {0} was {1}".format(name, mask_type) else: - ret['comment'] = 'Failed to mask service {0}'.format(name) + ret["comment"] = "Failed to mask service {0}".format(name) return ret except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret def unmasked(name, runtime=False): - ''' + """ .. versionadded:: 2017.7.0 .. note:: @@ -809,56 +845,55 @@ def unmasked(name, runtime=False): .. _systemd: https://freedesktop.org/wiki/Software/systemd/ - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if 'service.masked' not in __salt__: - ret['comment'] = 'Service masking not available on this minion' - ret['result'] = False + if "service.masked" not in __salt__: + ret["comment"] = "Service masking not available on this minion" + ret["result"] = False return ret - mask_type = 'runtime masked' if runtime else 'masked' - action = 'runtime unmasked' if runtime else 'unmasked' - expected_changes = {mask_type: {'old': True, 'new': False}} + mask_type = "runtime masked" if runtime else "masked" + action = "runtime unmasked" if runtime else "unmasked" + expected_changes = {mask_type: {"old": True, "new": False}} try: - if not __salt__['service.masked'](name, runtime): - ret['comment'] = 'Service {0} was already {1}'.format(name, action) + if not __salt__["service.masked"](name, runtime): + ret["comment"] = "Service {0} was already {1}".format(name, action) return ret - if __opts__['test']: - ret['result'] = None - ret['changes'] = expected_changes - ret['comment'] = 'Service {0} would be {1}'.format(name, action) + if __opts__["test"]: + ret["result"] = None + ret["changes"] = expected_changes + ret["comment"] = "Service {0} would be {1}".format(name, action) return ret - __salt__['service.unmask'](name, runtime) + __salt__["service.unmask"](name, runtime) - if not __salt__['service.masked'](name, runtime): - ret['changes'] = expected_changes - ret['comment'] = 'Service {0} was {1}'.format(name, action) + if not __salt__["service.masked"](name, runtime): + ret["changes"] = expected_changes + ret["comment"] = "Service {0} was {1}".format(name, action) else: - ret['comment'] = 'Failed to unmask service {0}'.format(name) + ret["comment"] = "Failed to unmask service {0}".format(name) return ret except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret -def mod_watch(name, - sfun=None, - sig=None, - reload=False, - full_restart=False, - init_delay=None, - force=False, - **kwargs): - ''' +def mod_watch( + name, + sfun=None, + sig=None, + reload=False, + full_restart=False, + init_delay=None, + force=False, + **kwargs +): + """ The service watcher, called to invoke the watch command. When called, it will restart or reload the named service. @@ -893,59 +928,56 @@ def mod_watch(name, init_delay Add a sleep command (in seconds) before the service is restarted/reloaded - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} past_participle = None - status_kwargs, warnings = _get_systemd_only(__salt__['service.status'], kwargs) + status_kwargs, warnings = _get_systemd_only(__salt__["service.status"], kwargs) if warnings: _add_warnings(ret, warnings) - if sfun == 'dead': - verb = 'stop' - past_participle = verb + 'ped' - if __salt__['service.status'](name, sig, **status_kwargs): - func = __salt__['service.stop'] + if sfun == "dead": + verb = "stop" + past_participle = verb + "ped" + if __salt__["service.status"](name, sig, **status_kwargs): + func = __salt__["service.stop"] else: - ret['result'] = True - ret['comment'] = 'Service is already {0}'.format(past_participle) + ret["result"] = True + ret["comment"] = "Service is already {0}".format(past_participle) return ret - elif sfun == 'running': - if __salt__['service.status'](name, sig, **status_kwargs): - if 'service.reload' in __salt__ and reload: - if 'service.force_reload' in __salt__ and force: - func = __salt__['service.force_reload'] - verb = 'forcefully reload' + elif sfun == "running": + if __salt__["service.status"](name, sig, **status_kwargs): + if "service.reload" in __salt__ and reload: + if "service.force_reload" in __salt__ and force: + func = __salt__["service.force_reload"] + verb = "forcefully reload" else: - func = __salt__['service.reload'] - verb = 'reload' - elif 'service.full_restart' in __salt__ and full_restart: - func = __salt__['service.full_restart'] - verb = 'fully restart' + func = __salt__["service.reload"] + verb = "reload" + elif "service.full_restart" in __salt__ and full_restart: + func = __salt__["service.full_restart"] + verb = "fully restart" else: - func = __salt__['service.restart'] - verb = 'restart' + func = __salt__["service.restart"] + verb = "restart" else: - func = __salt__['service.start'] - verb = 'start' + func = __salt__["service.start"] + verb = "start" if not past_participle: - past_participle = verb + 'ed' + past_participle = verb + "ed" else: - ret['comment'] = 'Unable to trigger watch for service.{0}'.format(sfun) - ret['result'] = False + ret["comment"] = "Unable to trigger watch for service.{0}".format(sfun) + ret["result"] = False return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Service is set to be {0}'.format(past_participle) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Service is set to be {0}".format(past_participle) return ret - if verb == 'start' and 'service.stop' in __salt__: + if verb == "start" and "service.stop" in __salt__: # stop service before start - __salt__['service.stop'](name) + __salt__["service.stop"](name) func_kwargs, warnings = _get_systemd_only(func, kwargs) if warnings: @@ -954,15 +986,18 @@ def mod_watch(name, try: result = func(name, **func_kwargs) except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret if init_delay: time.sleep(init_delay) - ret['changes'] = {name: result} - ret['result'] = result - ret['comment'] = 'Service {0}'.format(past_participle) if result else \ - 'Failed to {0} the service'.format(verb) + ret["changes"] = {name: result} + ret["result"] = result + ret["comment"] = ( + "Service {0}".format(past_participle) + if result + else "Failed to {0} the service".format(verb) + ) return ret diff --git a/salt/states/slack.py b/salt/states/slack.py index 8eb0b823e33..9078d5c42bd 100644 --- a/salt/states/slack.py +++ b/salt/states/slack.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Send a message to Slack ======================= @@ -23,24 +23,26 @@ The api key can be specified in the master or minion configuration like below: slack: api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs from salt.exceptions import SaltInvocationError def __virtual__(): - ''' + """ Only load if the slack module is available in __salt__ - ''' - return 'slack' if 'slack.post_message' in __salt__ else False + """ + if "slack.post_message" in __salt__: + return "slack" + return (False, "slack module could not be loaded") def post_message(name, **kwargs): - ''' + """ Send a message to a Slack channel. .. code-block:: yaml @@ -90,7 +92,7 @@ def post_message(name, **kwargs): An optional flag indicating whether the value is short enough to be displayed side-by-side with other values. - identifier + webhook The identifier of WebHook. channel @@ -101,64 +103,65 @@ def post_message(name, **kwargs): icon_emoji Icon to use instead of WebHook default. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - if not kwargs.get('api_key') and not kwargs.get('webhook'): - ret['comment'] = 'Please specify api_key or webhook.' + if not kwargs.get("api_key") and not kwargs.get("webhook"): + ret["comment"] = "Please specify api_key or webhook." return ret - if kwargs.get('api_key') and kwargs.get('webhook'): - ret['comment'] = 'Please specify only either api_key or webhook.' + if kwargs.get("api_key") and kwargs.get("webhook"): + ret["comment"] = "Please specify only either api_key or webhook." return ret - if kwargs.get('api_key') and not kwargs.get('channel'): - ret['comment'] = 'Slack channel is missing.' + if kwargs.get("api_key") and not kwargs.get("channel"): + ret["comment"] = "Slack channel is missing." return ret - if kwargs.get('api_key') and not kwargs.get('from_name'): - ret['comment'] = 'Slack from name is missing.' + if kwargs.get("api_key") and not kwargs.get("from_name"): + ret["comment"] = "Slack from name is missing." return ret - if not kwargs.get('message'): - ret['comment'] = 'Slack message is missing.' + if not kwargs.get("message"): + ret["comment"] = "Slack message is missing." return ret - if __opts__['test']: - ret['comment'] = 'The following message is to be sent to Slack: {0}'.format(kwargs.get('message')) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "The following message is to be sent to Slack: {0}".format( + kwargs.get("message") + ) + ret["result"] = None return ret try: - if kwargs.get('api_key'): - result = __salt__['slack.post_message']( - channel=kwargs.get('channel'), - message=kwargs.get('message'), - from_name=kwargs.get('from_name'), - api_key=kwargs.get('api_key'), - icon=kwargs.get('icon'), + if kwargs.get("api_key"): + result = __salt__["slack.post_message"]( + channel=kwargs.get("channel"), + message=kwargs.get("message"), + from_name=kwargs.get("from_name"), + api_key=kwargs.get("api_key"), + icon=kwargs.get("icon"), ) - elif kwargs.get('webhook'): - result = __salt__['slack.call_hook']( - message=kwargs.get('message'), - attachment=kwargs.get('attachment'), - color=kwargs.get('color', 'good'), - short=kwargs.get('short'), - identifier=kwargs.get('webhook'), - channel=kwargs.get('channel'), - username=kwargs.get('username'), - icon_emoji=kwargs.get('icon_emoji') + elif kwargs.get("webhook"): + result = __salt__["slack.call_hook"]( + message=kwargs.get("message"), + attachment=kwargs.get("attachment"), + color=kwargs.get("color", "good"), + short=kwargs.get("short"), + identifier=kwargs.get("webhook"), + channel=kwargs.get("channel"), + username=kwargs.get("username"), + icon_emoji=kwargs.get("icon_emoji"), ) except SaltInvocationError as sie: - ret['comment'] = 'Failed to send message ({0}): {1}'.format(sie, name) + ret["comment"] = "Failed to send message ({0}): {1}".format(sie, name) else: if isinstance(result, bool) and result: - ret['result'] = True - ret['comment'] = 'Sent message: {0}'.format(name) + ret["result"] = True + ret["comment"] = "Sent message: {0}".format(name) else: - ret['comment'] = 'Failed to send message ({0}): {1}'.format(result['message'], name) + ret["comment"] = "Failed to send message ({0}): {1}".format( + result["message"], name + ) return ret diff --git a/salt/states/smartos.py b/salt/states/smartos.py index 56b004bbc81..cdfd89327f6 100644 --- a/salt/states/smartos.py +++ b/salt/states/smartos.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of SmartOS Standalone Compute Nodes :maintainer: Jorge Schrauwen <sjorge@blackdot.be> @@ -58,7 +58,7 @@ Management of SmartOS Standalone Compute Nodes label: 'test kvm' owner: 'sjorge' disks: - disk0 + disk0: size: 2048 model: virtio compression: lz4 @@ -111,12 +111,19 @@ Management of SmartOS Standalone Compute Nodes they get removed via add_*, set_*, update_*, and remove_*. Properties must be manually reset to their default value. The same behavior as when using 'vmadm update'. -''' -from __future__ import absolute_import, unicode_literals, print_function + +.. warning:: + + For HVM (bhyve and KVM) brands the `image_uuid` field should go on the boot disks, + this disk should NOT have a size specified. (See man vmadm) + +""" +from __future__ import absolute_import, print_function, unicode_literals + +import json # Import Python libs import logging -import json import os # Import Salt libs @@ -130,30 +137,30 @@ from salt.ext import six log = logging.getLogger(__name__) # Define the state's virtual name -__virtualname__ = 'smartos' +__virtualname__ = "smartos" def __virtual__(): - ''' + """ Provides smartos state provided for SmartOS - ''' - if 'vmadm.create' in __salt__ and 'imgadm.list' in __salt__: + """ + if "vmadm.create" in __salt__ and "imgadm.list" in __salt__: return True else: return ( False, - '{0} state module can only be loaded on SmartOS compute nodes'.format( + "{0} state module can only be loaded on SmartOS compute nodes".format( __virtualname__ - ) + ), ) def _split_docker_uuid(uuid): - ''' + """ Split a smartos docker uuid into repo and tag - ''' + """ if uuid: - uuid = uuid.split(':') + uuid = uuid.split(":") if len(uuid) == 2: tag = uuid[1] repo = uuid[0] @@ -162,63 +169,65 @@ def _split_docker_uuid(uuid): def _is_uuid(uuid): - ''' + """ Check if uuid is a valid smartos uuid Example: e69a0918-055d-11e5-8912-e3ceb6df4cf8 - ''' - if uuid and list((len(x) for x in uuid.split('-'))) == [8, 4, 4, 4, 12]: + """ + if uuid and list((len(x) for x in uuid.split("-"))) == [8, 4, 4, 4, 12]: return True return False def _is_docker_uuid(uuid): - ''' + """ Check if uuid is a valid smartos docker uuid Example plexinc/pms-docker:plexpass - ''' + """ repo, tag = _split_docker_uuid(uuid) return not (not repo and not tag) def _load_config(): - ''' + """ Loads and parses /usbkey/config - ''' + """ config = {} - if os.path.isfile('/usbkey/config'): - with salt.utils.files.fopen('/usbkey/config', 'r') as config_file: + if os.path.isfile("/usbkey/config"): + with salt.utils.files.fopen("/usbkey/config", "r") as config_file: for optval in config_file: optval = salt.utils.stringutils.to_unicode(optval) - if optval[0] == '#': + if optval[0] == "#": continue - if '=' not in optval: + if "=" not in optval: continue - optval = optval.split('=') + optval = optval.split("=") config[optval[0].lower()] = optval[1].strip().strip('"') - log.debug('smartos.config - read /usbkey/config: %s', config) + log.debug("smartos.config - read /usbkey/config: %s", config) return config def _write_config(config): - ''' + """ writes /usbkey/config - ''' + """ try: - with salt.utils.atomicfile.atomic_open('/usbkey/config', 'w') as config_file: + with salt.utils.atomicfile.atomic_open("/usbkey/config", "w") as config_file: config_file.write("#\n# This file was generated by salt\n#\n") for prop in salt.utils.odict.OrderedDict(sorted(config.items())): - if ' ' in six.text_type(config[prop]): - if not config[prop].startswith('"') or not config[prop].endswith('"'): + if " " in six.text_type(config[prop]): + if not config[prop].startswith('"') or not config[prop].endswith( + '"' + ): config[prop] = '"{0}"'.format(config[prop]) config_file.write( salt.utils.stringutils.to_str( "{0}={1}\n".format(prop, config[prop]) ) ) - log.debug('smartos.config - wrote /usbkey/config: %s', config) + log.debug("smartos.config - wrote /usbkey/config: %s", config) except IOError: return False @@ -226,9 +235,9 @@ def _write_config(config): def _parse_vmconfig(config, instances): - ''' + """ Parse vm_present vm config - ''' + """ vmconfig = None if isinstance(config, (salt.utils.odict.OrderedDict)): @@ -244,24 +253,26 @@ def _parse_vmconfig(config, instances): instance_config = config[prop][instance] instance_config[instances[prop]] = instance ## some property are lowercase - if 'mac' in instance_config: - instance_config['mac'] = instance_config['mac'].lower() + if "mac" in instance_config: + instance_config["mac"] = instance_config["mac"].lower() ## calculate mac from vrrp_vrid - if 'vrrp_vrid' in instance_config: - instance_config['mac'] = "00:00:5e:00:01:{0}".format( - hex(int(instance_config['vrrp_vrid'])).split('x')[-1].zfill(2), + if "vrrp_vrid" in instance_config: + instance_config["mac"] = "00:00:5e:00:01:{0}".format( + hex(int(instance_config["vrrp_vrid"])) + .split("x")[-1] + .zfill(2), ) vmconfig[prop].append(instance_config) else: - log.error('smartos.vm_present::parse_vmconfig - failed to parse') + log.error("smartos.vm_present::parse_vmconfig - failed to parse") return vmconfig def _get_instance_changes(current, state): - ''' + """ get modified properties - ''' + """ # get keys current_keys = set(current.keys()) state_keys = set(state.keys()) @@ -269,9 +280,9 @@ def _get_instance_changes(current, state): # compare configs changed = salt.utils.data.compare_dicts(current, state) for change in salt.utils.data.compare_dicts(current, state): - if change in changed and changed[change]['old'] == "": + if change in changed and changed[change]["old"] == "": del changed[change] - if change in changed and changed[change]['new'] == "": + if change in changed and changed[change]["new"] == "": del changed[change] return changed @@ -279,60 +290,64 @@ def _get_instance_changes(current, state): def _copy_lx_vars(vmconfig): # NOTE: documentation on dockerinit: https://github.com/joyent/smartos-live/blob/master/src/dockerinit/README.md - if 'image_uuid' in vmconfig: + if "image_uuid" in vmconfig: # NOTE: retrieve tags and type from image - imgconfig = __salt__['imgadm.get'](vmconfig['image_uuid']).get('manifest', {}) - imgtype = imgconfig.get('type', 'zone-dataset') - imgtags = imgconfig.get('tags', {}) + imgconfig = __salt__["imgadm.get"](vmconfig["image_uuid"]).get("manifest", {}) + imgtype = imgconfig.get("type", "zone-dataset") + imgtags = imgconfig.get("tags", {}) # NOTE: copy kernel_version (if not specified in vmconfig) - if 'kernel_version' not in vmconfig and 'kernel_version' in imgtags: - vmconfig['kernel_version'] = imgtags['kernel_version'] + if "kernel_version" not in vmconfig and "kernel_version" in imgtags: + vmconfig["kernel_version"] = imgtags["kernel_version"] # NOTE: copy docker vars - if imgtype == 'docker': - vmconfig['docker'] = True - vmconfig['kernel_version'] = vmconfig.get('kernel_version', '4.3.0') - if 'internal_metadata' not in vmconfig: - vmconfig['internal_metadata'] = {} + if imgtype == "docker": + vmconfig["docker"] = True + vmconfig["kernel_version"] = vmconfig.get("kernel_version", "4.3.0") + if "internal_metadata" not in vmconfig: + vmconfig["internal_metadata"] = {} - for var in imgtags.get('docker:config', {}): - val = imgtags['docker:config'][var] - var = 'docker:{0}'.format(var.lower()) + for var in imgtags.get("docker:config", {}): + val = imgtags["docker:config"][var] + var = "docker:{0}".format(var.lower()) # NOTE: skip empty values if not val: continue # NOTE: skip or merge user values - if var == 'docker:env': + if var == "docker:env": try: val_config = json.loads( - vmconfig['internal_metadata'].get(var, "") + vmconfig["internal_metadata"].get(var, "") ) except ValueError as e: val_config = [] - for config_env_var in val_config if isinstance(val_config, list) else json.loads(val_config): - config_env_var = config_env_var.split('=') + for config_env_var in ( + val_config + if isinstance(val_config, list) + else json.loads(val_config) + ): + config_env_var = config_env_var.split("=") for img_env_var in val: - if img_env_var.startswith('{0}='.format(config_env_var[0])): + if img_env_var.startswith("{0}=".format(config_env_var[0])): val.remove(img_env_var) - val.append('='.join(config_env_var)) - elif var in vmconfig['internal_metadata']: + val.append("=".join(config_env_var)) + elif var in vmconfig["internal_metadata"]: continue if isinstance(val, list): # NOTE: string-encoded JSON arrays - vmconfig['internal_metadata'][var] = json.dumps(val) + vmconfig["internal_metadata"][var] = json.dumps(val) else: - vmconfig['internal_metadata'][var] = val + vmconfig["internal_metadata"][var] = val return vmconfig def config_present(name, value): - ''' + """ Ensure configuration property is set to value in /usbkey/config name : string @@ -340,218 +355,206 @@ def config_present(name, value): value : string value of property - ''' + """ name = name.lower() - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # load confiration config = _load_config() # handle bool and None value if isinstance(value, (bool)): - value = 'true' if value else 'false' + value = "true" if value else "false" if not value: value = "" if name in config: if six.text_type(config[name]) == six.text_type(value): # we're good - ret['result'] = True - ret['comment'] = 'property {0} already has value "{1}"'.format(name, value) + ret["result"] = True + ret["comment"] = 'property {0} already has value "{1}"'.format(name, value) else: # update property - ret['result'] = True - ret['comment'] = 'updated property {0} with value "{1}"'.format(name, value) - ret['changes'][name] = value + ret["result"] = True + ret["comment"] = 'updated property {0} with value "{1}"'.format(name, value) + ret["changes"][name] = value config[name] = value else: # add property - ret['result'] = True - ret['comment'] = 'added property {0} with value "{1}"'.format(name, value) - ret['changes'][name] = value + ret["result"] = True + ret["comment"] = 'added property {0} with value "{1}"'.format(name, value) + ret["changes"][name] = value config[name] = value # apply change if needed - if not __opts__['test'] and ret['changes']: - ret['result'] = _write_config(config) + if not __opts__["test"] and ret["changes"]: + ret["result"] = _write_config(config) - if not ret['result']: - ret['comment'] = 'Could not add property {0} with value "{1}" to config'.format(name, value) + if not ret["result"]: + ret[ + "comment" + ] = 'Could not add property {0} with value "{1}" to config'.format( + name, value + ) return ret def config_absent(name): - ''' + """ Ensure configuration property is absent in /usbkey/config name : string name of property - ''' + """ name = name.lower() - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # load configuration config = _load_config() if name in config: # delete property - ret['result'] = True - ret['comment'] = 'property {0} deleted'.format(name) - ret['changes'][name] = None + ret["result"] = True + ret["comment"] = "property {0} deleted".format(name) + ret["changes"][name] = None del config[name] else: # we're good - ret['result'] = True - ret['comment'] = 'property {0} is absent'.format(name) + ret["result"] = True + ret["comment"] = "property {0} is absent".format(name) # apply change if needed - if not __opts__['test'] and ret['changes']: - ret['result'] = _write_config(config) + if not __opts__["test"] and ret["changes"]: + ret["result"] = _write_config(config) return ret -def source_present(name, source_type='imgapi'): - ''' +def source_present(name, source_type="imgapi"): + """ Ensure an image source is present on the computenode name : string source url source_type : string source type (imgapi or docker) - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if name in __salt__['imgadm.sources'](): + if name in __salt__["imgadm.sources"](): # source is present - ret['result'] = True - ret['comment'] = 'image source {0} is present'.format(name) + ret["result"] = True + ret["comment"] = "image source {0} is present".format(name) else: # add new source - if __opts__['test']: + if __opts__["test"]: res = {} - ret['result'] = True + ret["result"] = True else: - res = __salt__['imgadm.source_add'](name, source_type) - ret['result'] = (name in res) + res = __salt__["imgadm.source_add"](name, source_type) + ret["result"] = name in res - if ret['result']: - ret['comment'] = 'image source {0} added'.format(name) - ret['changes'][name] = 'added' + if ret["result"]: + ret["comment"] = "image source {0} added".format(name) + ret["changes"][name] = "added" else: - ret['comment'] = 'image source {0} not added'.format(name) - if 'Error' in res: - ret['comment'] = '{0}: {1}'.format(ret['comment'], res['Error']) + ret["comment"] = "image source {0} not added".format(name) + if "Error" in res: + ret["comment"] = "{0}: {1}".format(ret["comment"], res["Error"]) return ret def source_absent(name): - ''' + """ Ensure an image source is absent on the computenode name : string source url - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if name not in __salt__['imgadm.sources'](): + if name not in __salt__["imgadm.sources"](): # source is absent - ret['result'] = True - ret['comment'] = 'image source {0} is absent'.format(name) + ret["result"] = True + ret["comment"] = "image source {0} is absent".format(name) else: # remove source - if __opts__['test']: + if __opts__["test"]: res = {} - ret['result'] = True + ret["result"] = True else: - res = __salt__['imgadm.source_delete'](name) - ret['result'] = (name not in res) + res = __salt__["imgadm.source_delete"](name) + ret["result"] = name not in res - if ret['result']: - ret['comment'] = 'image source {0} deleted'.format(name) - ret['changes'][name] = 'deleted' + if ret["result"]: + ret["comment"] = "image source {0} deleted".format(name) + ret["changes"][name] = "deleted" else: - ret['comment'] = 'image source {0} not deleted'.format(name) - if 'Error' in res: - ret['comment'] = '{0}: {1}'.format(ret['comment'], res['Error']) + ret["comment"] = "image source {0} not deleted".format(name) + if "Error" in res: + ret["comment"] = "{0}: {1}".format(ret["comment"], res["Error"]) return ret def image_present(name): - ''' + """ Ensure image is present on the computenode name : string uuid of image - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if _is_docker_uuid(name) and __salt__['imgadm.docker_to_uuid'](name): + if _is_docker_uuid(name) and __salt__["imgadm.docker_to_uuid"](name): # docker image was imported - ret['result'] = True - ret['comment'] = 'image {0} ({1}) is present'.format( - name, - __salt__['imgadm.docker_to_uuid'](name), + ret["result"] = True + ret["comment"] = "image {0} ({1}) is present".format( + name, __salt__["imgadm.docker_to_uuid"](name), ) - elif name in __salt__['imgadm.list'](): + elif name in __salt__["imgadm.list"](): # image was already imported - ret['result'] = True - ret['comment'] = 'image {0} is present'.format(name) + ret["result"] = True + ret["comment"] = "image {0} is present".format(name) else: # add image if _is_docker_uuid(name): # NOTE: we cannot query available docker images available_images = [name] else: - available_images = __salt__['imgadm.avail']() + available_images = __salt__["imgadm.avail"]() if name in available_images: - if __opts__['test']: - ret['result'] = True + if __opts__["test"]: + ret["result"] = True res = {} if _is_docker_uuid(name): - res['00000000-0000-0000-0000-000000000000'] = name + res["00000000-0000-0000-0000-000000000000"] = name else: res[name] = available_images[name] else: - res = __salt__['imgadm.import'](name) + res = __salt__["imgadm.import"](name) if _is_uuid(name): - ret['result'] = (name in res) + ret["result"] = name in res elif _is_docker_uuid(name): - ret['result'] = __salt__['imgadm.docker_to_uuid'](name) is not None - if ret['result']: - ret['comment'] = 'image {0} imported'.format(name) - ret['changes'] = res + ret["result"] = __salt__["imgadm.docker_to_uuid"](name) is not None + if ret["result"]: + ret["comment"] = "image {0} imported".format(name) + ret["changes"] = res else: - ret['comment'] = 'image {0} was unable to be imported'.format(name) + ret["comment"] = "image {0} was unable to be imported".format(name) else: - ret['result'] = False - ret['comment'] = 'image {0} does not exists'.format(name) + ret["result"] = False + ret["comment"] = "image {0} does not exists".format(name) return ret def image_absent(name): - ''' + """ Ensure image is absent on the computenode name : string @@ -561,140 +564,136 @@ def image_absent(name): computenode.image_absent will only remove the image if it is not used by a vm. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} uuid = None if _is_uuid(name): uuid = name if _is_docker_uuid(name): - uuid = __salt__['imgadm.docker_to_uuid'](name) + uuid = __salt__["imgadm.docker_to_uuid"](name) - if not uuid or uuid not in __salt__['imgadm.list'](): + if not uuid or uuid not in __salt__["imgadm.list"](): # image not imported - ret['result'] = True - ret['comment'] = 'image {0} is absent'.format(name) + ret["result"] = True + ret["comment"] = "image {0} is absent".format(name) else: # check if image in use by vm - if uuid in __salt__['vmadm.list'](order='image_uuid'): - ret['result'] = False - ret['comment'] = 'image {0} currently in use by a vm'.format(name) + if uuid in __salt__["vmadm.list"](order="image_uuid"): + ret["result"] = False + ret["comment"] = "image {0} currently in use by a vm".format(name) else: # delete image - if __opts__['test']: - ret['result'] = True + if __opts__["test"]: + ret["result"] = True else: - image = __salt__['imgadm.get'](uuid) + image = __salt__["imgadm.get"](uuid) image_count = 0 - if image['manifest']['name'] == 'docker-layer': + if image["manifest"]["name"] == "docker-layer": # NOTE: docker images are made of multiple layers, loop over them while image: image_count += 1 - __salt__['imgadm.delete'](image['manifest']['uuid']) - if 'origin' in image['manifest']: - image = __salt__['imgadm.get'](image['manifest']['origin']) + __salt__["imgadm.delete"](image["manifest"]["uuid"]) + if "origin" in image["manifest"]: + image = __salt__["imgadm.get"](image["manifest"]["origin"]) else: image = None else: # NOTE: normal images can just be delete - __salt__['imgadm.delete'](uuid) + __salt__["imgadm.delete"](uuid) - ret['result'] = uuid not in __salt__['imgadm.list']() + ret["result"] = uuid not in __salt__["imgadm.list"]() if image_count: - ret['comment'] = 'image {0} and {1} children deleted'.format(name, image_count) + ret["comment"] = "image {0} and {1} children deleted".format( + name, image_count + ) else: - ret['comment'] = 'image {0} deleted'.format(name) - ret['changes'][name] = None + ret["comment"] = "image {0} deleted".format(name) + ret["changes"][name] = None return ret def image_vacuum(name): - ''' + """ Delete images not in use or installed via image_present .. warning:: Only image_present states that are included via the top file will be detected. - ''' + """ name = name.lower() - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # list of images to keep images = [] # retrieve image_present state data for host - for state in __salt__['state.show_lowstate'](): + for state in __salt__["state.show_lowstate"](): # don't throw exceptions when not highstate run - if 'state' not in state: + if "state" not in state: continue # skip if not from this state module - if state['state'] != __virtualname__: + if state["state"] != __virtualname__: continue # skip if not image_present - if state['fun'] not in ['image_present']: + if state["fun"] not in ["image_present"]: continue # keep images installed via image_present - if 'name' in state: - if _is_uuid(state['name']): - images.append(state['name']) - elif _is_docker_uuid(state['name']): - state['name'] = __salt__['imgadm.docker_to_uuid'](state['name']) - if not state['name']: + if "name" in state: + if _is_uuid(state["name"]): + images.append(state["name"]) + elif _is_docker_uuid(state["name"]): + state["name"] = __salt__["imgadm.docker_to_uuid"](state["name"]) + if not state["name"]: continue - images.append(state['name']) + images.append(state["name"]) # retrieve images in use by vms - for image_uuid in __salt__['vmadm.list'](order='image_uuid'): + for image_uuid in __salt__["vmadm.list"](order="image_uuid"): if image_uuid not in images: images.append(image_uuid) # purge unused images - ret['result'] = True - for image_uuid in __salt__['imgadm.list'](): + ret["result"] = True + for image_uuid in __salt__["imgadm.list"](): if image_uuid in images: continue - image = __salt__['imgadm.get'](image_uuid) - if image['manifest']['name'] == 'docker-layer': + image = __salt__["imgadm.get"](image_uuid) + if image["manifest"]["name"] == "docker-layer": # NOTE: docker images are made of multiple layers, loop over them while image: - image_uuid = image['manifest']['uuid'] - if image_uuid in __salt__['imgadm.delete'](image_uuid): - ret['changes'][image_uuid] = None + image_uuid = image["manifest"]["uuid"] + if image_uuid in __salt__["imgadm.delete"](image_uuid): + ret["changes"][image_uuid] = None else: - ret['result'] = False - ret['comment'] = 'failed to delete images' - if 'origin' in image['manifest']: - image = __salt__['imgadm.get'](image['manifest']['origin']) + ret["result"] = False + ret["comment"] = "failed to delete images" + if "origin" in image["manifest"]: + image = __salt__["imgadm.get"](image["manifest"]["origin"]) else: image = None else: # NOTE: normal images can just be delete - if image_uuid in __salt__['imgadm.delete'](image_uuid): - ret['changes'][image_uuid] = None + if image_uuid in __salt__["imgadm.delete"](image_uuid): + ret["changes"][image_uuid] = None else: - ret['result'] = False - ret['comment'] = 'failed to delete images' + ret["result"] = False + ret["comment"] = "failed to delete images" - if ret['result'] and not ret['changes']: - ret['comment'] = 'no images deleted' - elif ret['result'] and ret['changes']: - ret['comment'] = 'images deleted' + if ret["result"] and not ret["changes"]: + ret["comment"] = "no images deleted" + elif ret["result"] and ret["changes"]: + ret["comment"] = "images deleted" return ret def vm_present(name, vmconfig, config=None): - ''' + """ Ensure vm is present on the computenode name : string @@ -738,246 +737,273 @@ def vm_present(name, vmconfig, config=None): Added support for docker image uuids, added auto_lx_vars configuration, documented some missing configuration options. - ''' + """ name = name.lower() - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # config defaults state_config = config if config else {} config = { - 'kvm_reboot': True, - 'auto_import': False, - 'auto_lx_vars': True, - 'reprovision': False, - 'enforce_tags': True, - 'enforce_routes': True, - 'enforce_internal_metadata': True, - 'enforce_customer_metadata': True, + "kvm_reboot": True, + "auto_import": False, + "auto_lx_vars": True, + "reprovision": False, + "enforce_tags": True, + "enforce_routes": True, + "enforce_internal_metadata": True, + "enforce_customer_metadata": True, } config.update(state_config) - log.debug('smartos.vm_present::%s::config - %s', name, config) + log.debug("smartos.vm_present::%s::config - %s", name, config) # map special vmconfig parameters # collections have set/remove handlers # instances have add/update/remove handlers and a unique id vmconfig_type = { - 'collection': [ - 'tags', - 'customer_metadata', - 'internal_metadata', - 'routes' - ], - 'instance': { - 'nics': 'mac', - 'disks': 'path', - 'filesystems': 'target', - 'pci_devices': 'path', + "collection": ["tags", "customer_metadata", "internal_metadata", "routes"], + "instance": { + "nics": "mac", + "disks": "path", + "filesystems": "target", + "pci_devices": "path", }, - 'create_only': [ - 'filesystems' - ] + "create_only": ["filesystems"], } vmconfig_docker_keep = [ - 'docker:id', - 'docker:restartcount', + "docker:id", + "docker:restartcount", ] vmconfig_docker_array = [ - 'docker:env', - 'docker:cmd', - 'docker:entrypoint', + "docker:env", + "docker:cmd", + "docker:entrypoint", ] # parse vmconfig - vmconfig = _parse_vmconfig(vmconfig, vmconfig_type['instance']) - log.debug('smartos.vm_present::%s::vmconfig - %s', name, vmconfig) + vmconfig = _parse_vmconfig(vmconfig, vmconfig_type["instance"]) + log.debug("smartos.vm_present::%s::vmconfig - %s", name, vmconfig) # set hostname if needed - if 'hostname' not in vmconfig: - vmconfig['hostname'] = name + if "hostname" not in vmconfig: + vmconfig["hostname"] = name # prepare image_uuid - if 'image_uuid' in vmconfig: + if "image_uuid" in vmconfig: # NOTE: lookup uuid from docker uuid (normal uuid's are passed throuhg unmodified) # we must do this again if we end up importing a missing image later! - docker_uuid = __salt__['imgadm.docker_to_uuid'](vmconfig['image_uuid']) - vmconfig['image_uuid'] = docker_uuid if docker_uuid else vmconfig['image_uuid'] + docker_uuid = __salt__["imgadm.docker_to_uuid"](vmconfig["image_uuid"]) + vmconfig["image_uuid"] = docker_uuid if docker_uuid else vmconfig["image_uuid"] # NOTE: import image (if missing and allowed) - if vmconfig['image_uuid'] not in __salt__['imgadm.list'](): - if config['auto_import']: - if not __opts__['test']: - res = __salt__['imgadm.import'](vmconfig['image_uuid']) - vmconfig['image_uuid'] = __salt__['imgadm.docker_to_uuid'](vmconfig['image_uuid']) - if vmconfig['image_uuid'] not in res: - ret['result'] = False - ret['comment'] = 'failed to import image {0}'.format(vmconfig['image_uuid']) + if vmconfig["image_uuid"] not in __salt__["imgadm.list"](): + if config["auto_import"]: + if not __opts__["test"]: + res = __salt__["imgadm.import"](vmconfig["image_uuid"]) + vmconfig["image_uuid"] = __salt__["imgadm.docker_to_uuid"]( + vmconfig["image_uuid"] + ) + if vmconfig["image_uuid"] not in res: + ret["result"] = False + ret["comment"] = "failed to import image {0}".format( + vmconfig["image_uuid"] + ) else: - ret['result'] = False - ret['comment'] = 'image {0} not installed'.format(vmconfig['image_uuid']) + ret["result"] = False + ret["comment"] = "image {0} not installed".format( + vmconfig["image_uuid"] + ) # prepare disk.*.image_uuid - for disk in vmconfig['disks'] if 'disks' in vmconfig else []: - if 'image_uuid' in disk and disk['image_uuid'] not in __salt__['imgadm.list'](): - if config['auto_import']: - if not __opts__['test']: - res = __salt__['imgadm.import'](disk['image_uuid']) - if disk['image_uuid'] not in res: - ret['result'] = False - ret['comment'] = 'failed to import image {0}'.format(disk['image_uuid']) + for disk in vmconfig["disks"] if "disks" in vmconfig else []: + if "image_uuid" in disk and disk["image_uuid"] not in __salt__["imgadm.list"](): + if config["auto_import"]: + if not __opts__["test"]: + res = __salt__["imgadm.import"](disk["image_uuid"]) + if disk["image_uuid"] not in res: + ret["result"] = False + ret["comment"] = "failed to import image {0}".format( + disk["image_uuid"] + ) else: - ret['result'] = False - ret['comment'] = 'image {0} not installed'.format(disk['image_uuid']) + ret["result"] = False + ret["comment"] = "image {0} not installed".format(disk["image_uuid"]) # docker json-array handling - if 'internal_metadata' in vmconfig: + if "internal_metadata" in vmconfig: for var in vmconfig_docker_array: - if var not in vmconfig['internal_metadata']: + if var not in vmconfig["internal_metadata"]: continue - if isinstance(vmconfig['internal_metadata'][var], list): - vmconfig['internal_metadata'][var] = json.dumps( - vmconfig['internal_metadata'][var] + if isinstance(vmconfig["internal_metadata"][var], list): + vmconfig["internal_metadata"][var] = json.dumps( + vmconfig["internal_metadata"][var] ) # copy lx variables - if vmconfig['brand'] == 'lx' and config['auto_lx_vars']: + if vmconfig["brand"] == "lx" and config["auto_lx_vars"]: # NOTE: we can only copy the lx vars after the image has bene imported vmconfig = _copy_lx_vars(vmconfig) # quick abort if things look wrong # NOTE: use explicit check for false, otherwise None also matches! - if ret['result'] is False: + if ret["result"] is False: return ret # check if vm exists - if vmconfig['hostname'] in __salt__['vmadm.list'](order='hostname'): + if vmconfig["hostname"] in __salt__["vmadm.list"](order="hostname"): # update vm - ret['result'] = True + ret["result"] = True # expand vmconfig vmconfig = { - 'state': vmconfig, - 'current': __salt__['vmadm.get'](vmconfig['hostname'], key='hostname'), - 'changed': {}, - 'reprovision_uuid': None + "state": vmconfig, + "current": __salt__["vmadm.get"](vmconfig["hostname"], key="hostname"), + "changed": {}, + "reprovision_uuid": None, } # prepare reprovision - if 'image_uuid' in vmconfig['state']: - vmconfig['reprovision_uuid'] = vmconfig['state']['image_uuid'] - vmconfig['state']['image_uuid'] = vmconfig['current']['image_uuid'] + if "image_uuid" in vmconfig["state"]: + vmconfig["reprovision_uuid"] = vmconfig["state"]["image_uuid"] + vmconfig["state"]["image_uuid"] = vmconfig["current"]["image_uuid"] # disks need some special care - if 'disks' in vmconfig['state']: + if "disks" in vmconfig["state"]: new_disks = [] - for disk in vmconfig['state']['disks']: + for disk in vmconfig["state"]["disks"]: path = False - if 'disks' in vmconfig['current']: - for cdisk in vmconfig['current']['disks']: - if cdisk['path'].endswith(disk['path']): - path = cdisk['path'] + if "disks" in vmconfig["current"]: + for cdisk in vmconfig["current"]["disks"]: + if cdisk["path"].endswith(disk["path"]): + path = cdisk["path"] break if not path: - del disk['path'] + del disk["path"] else: - disk['path'] = path + disk["path"] = path new_disks.append(disk) - vmconfig['state']['disks'] = new_disks + vmconfig["state"]["disks"] = new_disks # process properties - for prop in vmconfig['state']: + for prop in vmconfig["state"]: # skip special vmconfig_types - if prop in vmconfig_type['instance'] or \ - prop in vmconfig_type['collection'] or \ - prop in vmconfig_type['create_only']: + if ( + prop in vmconfig_type["instance"] + or prop in vmconfig_type["collection"] + or prop in vmconfig_type["create_only"] + ): continue # skip unchanged properties - if prop in vmconfig['current']: - if isinstance(vmconfig['current'][prop], (list)) or isinstance(vmconfig['current'][prop], (dict)): - if vmconfig['current'][prop] == vmconfig['state'][prop]: + if prop in vmconfig["current"]: + if isinstance(vmconfig["current"][prop], (list)) or isinstance( + vmconfig["current"][prop], (dict) + ): + if vmconfig["current"][prop] == vmconfig["state"][prop]: continue else: - if "{0}".format(vmconfig['current'][prop]) == "{0}".format(vmconfig['state'][prop]): + if "{0}".format(vmconfig["current"][prop]) == "{0}".format( + vmconfig["state"][prop] + ): continue # add property to changeset - vmconfig['changed'][prop] = vmconfig['state'][prop] + vmconfig["changed"][prop] = vmconfig["state"][prop] # process collections - for collection in vmconfig_type['collection']: + for collection in vmconfig_type["collection"]: # skip create only collections - if collection in vmconfig_type['create_only']: + if collection in vmconfig_type["create_only"]: continue # enforcement - enforce = config['enforce_{0}'.format(collection)] - log.debug('smartos.vm_present::enforce_%s = %s', collection, enforce) + enforce = config["enforce_{0}".format(collection)] + log.debug("smartos.vm_present::enforce_%s = %s", collection, enforce) # dockerinit handling - if collection == 'internal_metadata' and vmconfig['state'].get('docker', False): - if 'internal_metadata' not in vmconfig['state']: - vmconfig['state']['internal_metadata'] = {} + if collection == "internal_metadata" and vmconfig["state"].get( + "docker", False + ): + if "internal_metadata" not in vmconfig["state"]: + vmconfig["state"]["internal_metadata"] = {} # preserve some docker specific metadata (added and needed by dockerinit) for var in vmconfig_docker_keep: - val = vmconfig['current'].get(collection, {}).get(var, None) + val = vmconfig["current"].get(collection, {}).get(var, None) if val is not None: - vmconfig['state']['internal_metadata'][var] = val + vmconfig["state"]["internal_metadata"][var] = val # process add and update for collection - if collection in vmconfig['state'] and vmconfig['state'][collection] is not None: - for prop in vmconfig['state'][collection]: + if ( + collection in vmconfig["state"] + and vmconfig["state"][collection] is not None + ): + for prop in vmconfig["state"][collection]: # skip unchanged properties - if prop in vmconfig['current'][collection] and \ - vmconfig['current'][collection][prop] == vmconfig['state'][collection][prop]: + if ( + prop in vmconfig["current"][collection] + and vmconfig["current"][collection][prop] + == vmconfig["state"][collection][prop] + ): continue # skip update if not enforcing - if not enforce and prop in vmconfig['current'][collection]: + if not enforce and prop in vmconfig["current"][collection]: continue # create set_ dict - if 'set_{0}'.format(collection) not in vmconfig['changed']: - vmconfig['changed']['set_{0}'.format(collection)] = {} + if "set_{0}".format(collection) not in vmconfig["changed"]: + vmconfig["changed"]["set_{0}".format(collection)] = {} # add property to changeset - vmconfig['changed']['set_{0}'.format(collection)][prop] = vmconfig['state'][collection][prop] + vmconfig["changed"]["set_{0}".format(collection)][prop] = vmconfig[ + "state" + ][collection][prop] # process remove for collection - if enforce and collection in vmconfig['current'] and vmconfig['current'][collection] is not None: - for prop in vmconfig['current'][collection]: + if ( + enforce + and collection in vmconfig["current"] + and vmconfig["current"][collection] is not None + ): + for prop in vmconfig["current"][collection]: # skip if exists in state - if collection in vmconfig['state'] and vmconfig['state'][collection] is not None: - if prop in vmconfig['state'][collection]: + if ( + collection in vmconfig["state"] + and vmconfig["state"][collection] is not None + ): + if prop in vmconfig["state"][collection]: continue # create remove_ array - if 'remove_{0}'.format(collection) not in vmconfig['changed']: - vmconfig['changed']['remove_{0}'.format(collection)] = [] + if "remove_{0}".format(collection) not in vmconfig["changed"]: + vmconfig["changed"]["remove_{0}".format(collection)] = [] # remove property - vmconfig['changed']['remove_{0}'.format(collection)].append(prop) + vmconfig["changed"]["remove_{0}".format(collection)].append(prop) # process instances - for instance in vmconfig_type['instance']: + for instance in vmconfig_type["instance"]: # skip create only instances - if instance in vmconfig_type['create_only']: + if instance in vmconfig_type["create_only"]: continue # add or update instances - if instance in vmconfig['state'] and vmconfig['state'][instance] is not None: - for state_cfg in vmconfig['state'][instance]: + if ( + instance in vmconfig["state"] + and vmconfig["state"][instance] is not None + ): + for state_cfg in vmconfig["state"][instance]: add_instance = True # find instance with matching ids - for current_cfg in vmconfig['current'][instance]: - if vmconfig_type['instance'][instance] not in state_cfg: + for current_cfg in vmconfig["current"][instance]: + if vmconfig_type["instance"][instance] not in state_cfg: continue - if state_cfg[vmconfig_type['instance'][instance]] == current_cfg[vmconfig_type['instance'][instance]]: + if ( + state_cfg[vmconfig_type["instance"][instance]] + == current_cfg[vmconfig_type["instance"][instance]] + ): # ids have matched, disable add instance add_instance = False @@ -991,7 +1017,10 @@ def vm_present(name, vmconfig, config=None): # handle new properties for prop in state_cfg: # skip empty props like ips, options,.. - if isinstance(state_cfg[prop], (list)) and not state_cfg[prop]: + if ( + isinstance(state_cfg[prop], (list)) + and not state_cfg[prop] + ): continue if prop not in current_cfg: @@ -1000,137 +1029,193 @@ def vm_present(name, vmconfig, config=None): # update instance if update_cfg: # create update_ array - if 'update_{0}'.format(instance) not in vmconfig['changed']: - vmconfig['changed']['update_{0}'.format(instance)] = [] + if ( + "update_{0}".format(instance) + not in vmconfig["changed"] + ): + vmconfig["changed"][ + "update_{0}".format(instance) + ] = [] - update_cfg[vmconfig_type['instance'][instance]] = state_cfg[vmconfig_type['instance'][instance]] - vmconfig['changed']['update_{0}'.format(instance)].append(update_cfg) + update_cfg[ + vmconfig_type["instance"][instance] + ] = state_cfg[vmconfig_type["instance"][instance]] + vmconfig["changed"][ + "update_{0}".format(instance) + ].append(update_cfg) if add_instance: # create add_ array - if 'add_{0}'.format(instance) not in vmconfig['changed']: - vmconfig['changed']['add_{0}'.format(instance)] = [] + if "add_{0}".format(instance) not in vmconfig["changed"]: + vmconfig["changed"]["add_{0}".format(instance)] = [] # add instance - vmconfig['changed']['add_{0}'.format(instance)].append(state_cfg) + vmconfig["changed"]["add_{0}".format(instance)].append( + state_cfg + ) # remove instances - if instance in vmconfig['current'] and vmconfig['current'][instance] is not None: - for current_cfg in vmconfig['current'][instance]: + if ( + instance in vmconfig["current"] + and vmconfig["current"][instance] is not None + ): + for current_cfg in vmconfig["current"][instance]: remove_instance = True # find instance with matching ids - if instance in vmconfig['state'] and vmconfig['state'][instance] is not None: - for state_cfg in vmconfig['state'][instance]: - if vmconfig_type['instance'][instance] not in state_cfg: + if ( + instance in vmconfig["state"] + and vmconfig["state"][instance] is not None + ): + for state_cfg in vmconfig["state"][instance]: + if vmconfig_type["instance"][instance] not in state_cfg: continue - if state_cfg[vmconfig_type['instance'][instance]] == current_cfg[vmconfig_type['instance'][instance]]: + if ( + state_cfg[vmconfig_type["instance"][instance]] + == current_cfg[vmconfig_type["instance"][instance]] + ): # keep instance if matched remove_instance = False if remove_instance: # create remove_ array - if 'remove_{0}'.format(instance) not in vmconfig['changed']: - vmconfig['changed']['remove_{0}'.format(instance)] = [] + if "remove_{0}".format(instance) not in vmconfig["changed"]: + vmconfig["changed"]["remove_{0}".format(instance)] = [] # remove instance - vmconfig['changed']['remove_{0}'.format(instance)].append( - current_cfg[vmconfig_type['instance'][instance]] + vmconfig["changed"]["remove_{0}".format(instance)].append( + current_cfg[vmconfig_type["instance"][instance]] ) # update vm if we have pending changes kvm_needs_start = False - if not __opts__['test'] and vmconfig['changed']: + if not __opts__["test"] and vmconfig["changed"]: # stop kvm if disk updates and kvm_reboot - if vmconfig['current']['brand'] == 'kvm' and config['kvm_reboot']: - if 'add_disks' in vmconfig['changed'] or \ - 'update_disks' in vmconfig['changed'] or \ - 'remove_disks' in vmconfig['changed']: - if vmconfig['state']['hostname'] in __salt__['vmadm.list'](order='hostname', search='state=running'): + if vmconfig["current"]["brand"] == "kvm" and config["kvm_reboot"]: + if ( + "add_disks" in vmconfig["changed"] + or "update_disks" in vmconfig["changed"] + or "remove_disks" in vmconfig["changed"] + ): + if vmconfig["state"]["hostname"] in __salt__["vmadm.list"]( + order="hostname", search="state=running" + ): kvm_needs_start = True - __salt__['vmadm.stop'](vm=vmconfig['state']['hostname'], key='hostname') + __salt__["vmadm.stop"]( + vm=vmconfig["state"]["hostname"], key="hostname" + ) # do update - rret = __salt__['vmadm.update'](vm=vmconfig['state']['hostname'], key='hostname', **vmconfig['changed']) - if not isinstance(rret, (bool)) and 'Error' in rret: - ret['result'] = False - ret['comment'] = "{0}".format(rret['Error']) + rret = __salt__["vmadm.update"]( + vm=vmconfig["state"]["hostname"], key="hostname", **vmconfig["changed"] + ) + if not isinstance(rret, (bool)) and "Error" in rret: + ret["result"] = False + ret["comment"] = "{0}".format(rret["Error"]) else: - ret['result'] = True - ret['changes'][vmconfig['state']['hostname']] = vmconfig['changed'] + ret["result"] = True + ret["changes"][vmconfig["state"]["hostname"]] = vmconfig["changed"] - if ret['result']: - if __opts__['test']: - ret['changes'][vmconfig['state']['hostname']] = vmconfig['changed'] + if ret["result"]: + if __opts__["test"]: + ret["changes"][vmconfig["state"]["hostname"]] = vmconfig["changed"] - if vmconfig['state']['hostname'] in ret['changes'] and ret['changes'][vmconfig['state']['hostname']]: - ret['comment'] = 'vm {0} updated'.format(vmconfig['state']['hostname']) - if config['kvm_reboot'] and vmconfig['current']['brand'] == 'kvm' and not __opts__['test']: - if vmconfig['state']['hostname'] in __salt__['vmadm.list'](order='hostname', search='state=running'): - __salt__['vmadm.reboot'](vm=vmconfig['state']['hostname'], key='hostname') + if ( + vmconfig["state"]["hostname"] in ret["changes"] + and ret["changes"][vmconfig["state"]["hostname"]] + ): + ret["comment"] = "vm {0} updated".format(vmconfig["state"]["hostname"]) + if ( + config["kvm_reboot"] + and vmconfig["current"]["brand"] == "kvm" + and not __opts__["test"] + ): + if vmconfig["state"]["hostname"] in __salt__["vmadm.list"]( + order="hostname", search="state=running" + ): + __salt__["vmadm.reboot"]( + vm=vmconfig["state"]["hostname"], key="hostname" + ) if kvm_needs_start: - __salt__['vmadm.start'](vm=vmconfig['state']['hostname'], key='hostname') + __salt__["vmadm.start"]( + vm=vmconfig["state"]["hostname"], key="hostname" + ) else: - ret['changes'] = {} - ret['comment'] = 'vm {0} is up to date'.format(vmconfig['state']['hostname']) + ret["changes"] = {} + ret["comment"] = "vm {0} is up to date".format( + vmconfig["state"]["hostname"] + ) # reprovision (if required and allowed) - if 'image_uuid' in vmconfig['current'] and vmconfig['reprovision_uuid'] != vmconfig['current']['image_uuid']: - if config['reprovision']: - rret = __salt__['vmadm.reprovision']( - vm=vmconfig['state']['hostname'], - key='hostname', - image=vmconfig['reprovision_uuid'] + if ( + "image_uuid" in vmconfig["current"] + and vmconfig["reprovision_uuid"] != vmconfig["current"]["image_uuid"] + ): + if config["reprovision"]: + rret = __salt__["vmadm.reprovision"]( + vm=vmconfig["state"]["hostname"], + key="hostname", + image=vmconfig["reprovision_uuid"], ) - if not isinstance(rret, (bool)) and 'Error' in rret: - ret['result'] = False - ret['comment'] = 'vm {0} updated, reprovision failed'.format( - vmconfig['state']['hostname'] + if not isinstance(rret, (bool)) and "Error" in rret: + ret["result"] = False + ret["comment"] = "vm {0} updated, reprovision failed".format( + vmconfig["state"]["hostname"] ) else: - ret['comment'] = 'vm {0} updated and reprovisioned'.format(vmconfig['state']['hostname']) - if vmconfig['state']['hostname'] not in ret['changes']: - ret['changes'][vmconfig['state']['hostname']] = {} - ret['changes'][vmconfig['state']['hostname']]['image_uuid'] = vmconfig['reprovision_uuid'] + ret["comment"] = "vm {0} updated and reprovisioned".format( + vmconfig["state"]["hostname"] + ) + if vmconfig["state"]["hostname"] not in ret["changes"]: + ret["changes"][vmconfig["state"]["hostname"]] = {} + ret["changes"][vmconfig["state"]["hostname"]][ + "image_uuid" + ] = vmconfig["reprovision_uuid"] else: - log.warning('smartos.vm_present::%s::reprovision - ' - 'image_uuid in state does not match current, ' - 'reprovision not allowed', - name) + log.warning( + "smartos.vm_present::%s::reprovision - " + "image_uuid in state does not match current, " + "reprovision not allowed", + name, + ) else: - ret['comment'] = 'vm {0} failed to be updated'.format(vmconfig['state']['hostname']) - if not isinstance(rret, (bool)) and 'Error' in rret: - ret['comment'] = "{0}".format(rret['Error']) + ret["comment"] = "vm {0} failed to be updated".format( + vmconfig["state"]["hostname"] + ) + if not isinstance(rret, (bool)) and "Error" in rret: + ret["comment"] = "{0}".format(rret["Error"]) else: # check required image installed - ret['result'] = True + ret["result"] = True # disks need some special care - if 'disks' in vmconfig: + if "disks" in vmconfig: new_disks = [] - for disk in vmconfig['disks']: - if 'path' in disk: - del disk['path'] + for disk in vmconfig["disks"]: + if "path" in disk: + del disk["path"] new_disks.append(disk) - vmconfig['disks'] = new_disks + vmconfig["disks"] = new_disks # create vm - if ret['result']: - uuid = __salt__['vmadm.create'](**vmconfig) if not __opts__['test'] else True - if not isinstance(uuid, (bool)) and 'Error' in uuid: - ret['result'] = False - ret['comment'] = "{0}".format(uuid['Error']) + if ret["result"]: + uuid = ( + __salt__["vmadm.create"](**vmconfig) if not __opts__["test"] else True + ) + if not isinstance(uuid, (bool)) and "Error" in uuid: + ret["result"] = False + ret["comment"] = "{0}".format(uuid["Error"]) else: - ret['result'] = True - ret['changes'][vmconfig['hostname']] = vmconfig - ret['comment'] = 'vm {0} created'.format(vmconfig['hostname']) + ret["result"] = True + ret["changes"][vmconfig["hostname"]] = vmconfig + ret["comment"] = "vm {0} created".format(vmconfig["hostname"]) return ret def vm_absent(name, archive=False): - ''' + """ Ensure vm is absent on the computenode name : string @@ -1142,40 +1227,39 @@ def vm_absent(name, archive=False): State ID is used as hostname. Hostnames must be unique. - ''' + """ name = name.lower() - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if name not in __salt__['vmadm.list'](order='hostname'): + if name not in __salt__["vmadm.list"](order="hostname"): # we're good - ret['result'] = True - ret['comment'] = 'vm {0} is absent'.format(name) + ret["result"] = True + ret["comment"] = "vm {0} is absent".format(name) else: # delete vm - if not __opts__['test']: + if not __opts__["test"]: # set archive to true if needed if archive: - __salt__['vmadm.update'](vm=name, key='hostname', archive_on_delete=True) + __salt__["vmadm.update"]( + vm=name, key="hostname", archive_on_delete=True + ) - ret['result'] = __salt__['vmadm.delete'](name, key='hostname') + ret["result"] = __salt__["vmadm.delete"](name, key="hostname") else: - ret['result'] = True + ret["result"] = True - if not isinstance(ret['result'], bool) and ret['result'].get('Error'): - ret['result'] = False - ret['comment'] = 'failed to delete vm {0}'.format(name) + if not isinstance(ret["result"], bool) and ret["result"].get("Error"): + ret["result"] = False + ret["comment"] = "failed to delete vm {0}".format(name) else: - ret['comment'] = 'vm {0} deleted'.format(name) - ret['changes'][name] = None + ret["comment"] = "vm {0} deleted".format(name) + ret["changes"][name] = None return ret def vm_running(name): - ''' + """ Ensure vm is in the running state on the computenode name : string @@ -1185,32 +1269,31 @@ def vm_running(name): State ID is used as hostname. Hostnames must be unique. - ''' + """ name = name.lower() - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if name in __salt__['vmadm.list'](order='hostname', search='state=running'): + if name in __salt__["vmadm.list"](order="hostname", search="state=running"): # we're good - ret['result'] = True - ret['comment'] = 'vm {0} already running'.format(name) + ret["result"] = True + ret["comment"] = "vm {0} already running".format(name) else: # start the vm - ret['result'] = True if __opts__['test'] else __salt__['vmadm.start'](name, key='hostname') - if not isinstance(ret['result'], bool) and ret['result'].get('Error'): - ret['result'] = False - ret['comment'] = 'failed to start {0}'.format(name) + ret["result"] = ( + True if __opts__["test"] else __salt__["vmadm.start"](name, key="hostname") + ) + if not isinstance(ret["result"], bool) and ret["result"].get("Error"): + ret["result"] = False + ret["comment"] = "failed to start {0}".format(name) else: - ret['changes'][name] = 'running' - ret['comment'] = 'vm {0} started'.format(name) + ret["changes"][name] = "running" + ret["comment"] = "vm {0} started".format(name) return ret def vm_stopped(name): - ''' + """ Ensure vm is in the stopped state on the computenode name : string @@ -1220,27 +1303,27 @@ def vm_stopped(name): State ID is used as hostname. Hostnames must be unique. - ''' + """ name = name.lower() - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if name in __salt__['vmadm.list'](order='hostname', search='state=stopped'): + if name in __salt__["vmadm.list"](order="hostname", search="state=stopped"): # we're good - ret['result'] = True - ret['comment'] = 'vm {0} already stopped'.format(name) + ret["result"] = True + ret["comment"] = "vm {0} already stopped".format(name) else: # stop the vm - ret['result'] = True if __opts__['test'] else __salt__['vmadm.stop'](name, key='hostname') - if not isinstance(ret['result'], bool) and ret['result'].get('Error'): - ret['result'] = False - ret['comment'] = 'failed to stop {0}'.format(name) + ret["result"] = ( + True if __opts__["test"] else __salt__["vmadm.stop"](name, key="hostname") + ) + if not isinstance(ret["result"], bool) and ret["result"].get("Error"): + ret["result"] = False + ret["comment"] = "failed to stop {0}".format(name) else: - ret['changes'][name] = 'stopped' - ret['comment'] = 'vm {0} stopped'.format(name) + ret["changes"][name] = "stopped" + ret["comment"] = "vm {0} stopped".format(name) return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/states/smtp.py b/salt/states/smtp.py index c4d88190d23..3b1d2516ff9 100644 --- a/salt/states/smtp.py +++ b/salt/states/smtp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Sending Messages via SMTP ========================== @@ -15,26 +15,30 @@ protocol - name: 'This is a server warning message' - profile: my-smtp-account - recipient: admins@example.com -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the SMTP module is available in __salt__ - ''' - return 'smtp' if 'smtp.send_msg' in __salt__ else False + """ + if "smtp.send_msg" in __salt__: + return "smtp" + return (False, "smtp module could not be loaded") -def send_msg(name, - recipient, - subject, - sender=None, - profile=None, - use_ssl='True', - attachments=None): - ''' +def send_msg( + name, + recipient, + subject, + sender=None, + profile=None, + use_ssl="True", + attachments=None, +): + """ Send a message via SMTP .. code-block:: yaml @@ -53,24 +57,18 @@ def send_msg(name, name The message to send via SMTP - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} if profile is None and sender is None: - ret['result'] = False - ret['comment'] = 'Missing parameter sender or profile for state smtp.send_msg' + ret["result"] = False + ret["comment"] = "Missing parameter sender or profile for state smtp.send_msg" return ret - if __opts__['test']: - ret['comment'] = 'Need to send message to {0}: {1}'.format( - recipient, - name, - ) + if __opts__["test"]: + ret["comment"] = "Need to send message to {0}: {1}".format(recipient, name,) return ret - command = __salt__['smtp.send_msg']( + command = __salt__["smtp.send_msg"]( message=name, recipient=recipient, profile=profile, @@ -81,14 +79,15 @@ def send_msg(name, ) if command: - ret['result'] = True + ret["result"] = True if attachments: - atts = ', '.join(attachments) - ret['comment'] = 'Sent message to {0} with attachments ({2}): {1}' \ - .format(recipient, name, atts) + atts = ", ".join(attachments) + ret["comment"] = "Sent message to {0} with attachments ({2}): {1}".format( + recipient, name, atts + ) else: - ret['comment'] = 'Sent message to {0}: {1}'.format(recipient, name) + ret["comment"] = "Sent message to {0}: {1}".format(recipient, name) else: - ret['result'] = False - ret['comment'] = 'Unable to send message to {0}: {1}'.format(recipient, name) + ret["result"] = False + ret["comment"] = "Unable to send message to {0}: {1}".format(recipient, name) return ret diff --git a/salt/states/snapper.py b/salt/states/snapper.py index c49b1141622..3efc5d4d769 100644 --- a/salt/states/snapper.py +++ b/salt/states/snapper.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Managing implicit state and baselines using snapshots ===================================================== @@ -105,34 +105,38 @@ and include this change. :maturity: new :platform: Linux -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import os def __virtual__(): - ''' + """ Only load if the snapper module is available in __salt__ - ''' - return 'snapper' if 'snapper.diff' in __salt__ else False + """ + if "snapper.diff" in __salt__: + return "snapper" + return (False, "snapper module could not be loaded") def _get_baseline_from_tag(config, tag): - ''' + """ Returns the last created baseline snapshot marked with `tag` - ''' + """ last_snapshot = None - for snapshot in __salt__['snapper.list_snapshots'](config): - if tag == snapshot['userdata'].get("baseline_tag"): - if not last_snapshot or last_snapshot['timestamp'] < snapshot['timestamp']: + for snapshot in __salt__["snapper.list_snapshots"](config): + if tag == snapshot["userdata"].get("baseline_tag"): + if not last_snapshot or last_snapshot["timestamp"] < snapshot["timestamp"]: last_snapshot = snapshot return last_snapshot -def baseline_snapshot(name, number=None, tag=None, include_diff=True, config='root', ignore=None): - ''' +def baseline_snapshot( + name, number=None, tag=None, include_diff=True, config="root", ignore=None +): + """ Enforces that no file is modified comparing against a previously defined snapshot identified by number. @@ -152,68 +156,78 @@ def baseline_snapshot(name, number=None, tag=None, include_diff=True, config='ro ignore List of files to ignore. (Default: None) - ''' + """ if not ignore: ignore = [] - ret = {'changes': {}, - 'comment': '', - 'name': name, - 'result': True} + ret = {"changes": {}, "comment": "", "name": name, "result": True} if number is None and tag is None: - ret.update({'result': False, - 'comment': 'Snapshot tag or number must be specified'}) + ret.update( + {"result": False, "comment": "Snapshot tag or number must be specified"} + ) return ret if number and tag: - ret.update({'result': False, - 'comment': 'Cannot use snapshot tag and number at the same time'}) + ret.update( + { + "result": False, + "comment": "Cannot use snapshot tag and number at the same time", + } + ) return ret if tag: snapshot = _get_baseline_from_tag(config, tag) if not snapshot: - ret.update({'result': False, - 'comment': 'Baseline tag "{0}" not found'.format(tag)}) + ret.update( + {"result": False, "comment": 'Baseline tag "{0}" not found'.format(tag)} + ) return ret - number = snapshot['id'] + number = snapshot["id"] - status = __salt__['snapper.status']( - config, num_pre=0, num_post=number) + status = __salt__["snapper.status"](config, num_pre=0, num_post=number) for target in ignore: if os.path.isfile(target): status.pop(target, None) elif os.path.isdir(target): - for target_file in [target_file for target_file in status.keys() if target_file.startswith(target)]: + for target_file in [ + target_file + for target_file in status.keys() + if target_file.startswith(target) + ]: status.pop(target_file, None) for file in status: # Only include diff for modified files if "modified" in status[file]["status"] and include_diff: status[file].pop("status") - status[file].update(__salt__['snapper.diff'](config, - num_pre=0, - num_post=number, - filename=file).get(file, {})) + status[file].update( + __salt__["snapper.diff"]( + config, num_pre=0, num_post=number, filename=file + ).get(file, {}) + ) - if __opts__['test'] and status: - ret['changes'] = status - ret['comment'] = "{0} files changes are set to be undone".format(len(status.keys())) - ret['result'] = None - elif __opts__['test'] and not status: - ret['changes'] = {} - ret['comment'] = "Nothing to be done" - ret['result'] = True - elif not __opts__['test'] and status: - undo = __salt__['snapper.undo'](config, num_pre=number, num_post=0, - files=status.keys()) - ret['changes']['sumary'] = undo - ret['changes']['files'] = status - ret['result'] = True + if __opts__["test"] and status: + ret["changes"] = status + ret["comment"] = "{0} files changes are set to be undone".format( + len(status.keys()) + ) + ret["result"] = None + elif __opts__["test"] and not status: + ret["changes"] = {} + ret["comment"] = "Nothing to be done" + ret["result"] = True + elif not __opts__["test"] and status: + undo = __salt__["snapper.undo"]( + config, num_pre=number, num_post=0, files=status.keys() + ) + ret["changes"]["sumary"] = undo + ret["changes"]["files"] = status + ret["result"] = True else: - ret['comment'] = "No changes were done" - ret['result'] = True + ret["comment"] = "No changes were done" + ret["result"] = True return ret diff --git a/salt/states/solrcloud.py b/salt/states/solrcloud.py index 4079be7a6a5..3da19306250 100644 --- a/salt/states/solrcloud.py +++ b/salt/states/solrcloud.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" States for solrcloud alias and collection configuration .. versionadded:: 2017.7.0 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.json @@ -17,7 +17,7 @@ from salt.ext import six def alias(name, collections, **kwargs): - ''' + """ Create alias and enforce collection list. Use the solrcloud module to get alias members and set them. @@ -28,55 +28,55 @@ def alias(name, collections, **kwargs): The collection name collections list of collections to include in the alias - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': '', + "name": name, + "changes": {}, + "result": False, + "comment": "", } - if __salt__['solrcloud.alias_exists'](name, **kwargs): - alias_content = __salt__['solrcloud.alias_get_collections'](name, **kwargs) + if __salt__["solrcloud.alias_exists"](name, **kwargs): + alias_content = __salt__["solrcloud.alias_get_collections"](name, **kwargs) diff = set(alias_content).difference(set(collections)) if len(diff) == 0: - ret['result'] = True - ret['comment'] = 'Alias is in desired state' + ret["result"] = True + ret["comment"] = "Alias is in desired state" return ret - if __opts__['test']: - ret['comment'] = 'The alias "{0}" will be updated.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = 'The alias "{0}" will be updated.'.format(name) + ret["result"] = None else: - __salt__['solrcloud.alias_set_collections'](name, collections, **kwargs) - ret['comment'] = 'The alias "{0}" has been updated.'.format(name) - ret['result'] = True + __salt__["solrcloud.alias_set_collections"](name, collections, **kwargs) + ret["comment"] = 'The alias "{0}" has been updated.'.format(name) + ret["result"] = True - ret['changes'] = { - 'old': ','.join(alias_content), - 'new': ','.join(collections), + ret["changes"] = { + "old": ",".join(alias_content), + "new": ",".join(collections), } else: - if __opts__['test']: - ret['comment'] = 'The alias "{0}" will be created.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = 'The alias "{0}" will be created.'.format(name) + ret["result"] = None else: - __salt__['solrcloud.alias_set_collections'](name, collections, **kwargs) - ret['comment'] = 'The alias "{0}" has been created.'.format(name) - ret['result'] = True + __salt__["solrcloud.alias_set_collections"](name, collections, **kwargs) + ret["comment"] = 'The alias "{0}" has been created.'.format(name) + ret["result"] = True - ret['changes'] = { - 'old': None, - 'new': ','.join(collections), + ret["changes"] = { + "old": None, + "new": ",".join(collections), } return ret def collection(name, options=None, **kwargs): - ''' + """ Create collection and enforce options. Use the solrcloud module to get collection parameters. @@ -87,12 +87,12 @@ def collection(name, options=None, **kwargs): The collection name options : {} options to ensure - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': '', + "name": name, + "changes": {}, + "result": False, + "comment": "", } if options is None: @@ -111,7 +111,8 @@ def collection(name, options=None, **kwargs): "autoAddReplicas", "collection.configName", "rule", - "snitch"] + "snitch", + ] options = [k for k in six.iteritems(options) if k in updatable_options] @@ -120,40 +121,51 @@ def collection(name, options=None, **kwargs): diff[key] = value if len(diff) == 0: - ret['result'] = True - ret['comment'] = 'Collection options are in desired state' + ret["result"] = True + ret["comment"] = "Collection options are in desired state" return ret else: - if __opts__['test']: - ret['comment'] = 'Collection options "{0}" will be changed.'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = 'Collection options "{0}" will be changed.'.format( + name + ) + ret["result"] = None else: - __salt__['solrcloud.collection_set_options'](name, diff, **kwargs) - ret['comment'] = 'Parameters were updated for collection "{0}".'.format(name) - ret['result'] = True + __salt__["solrcloud.collection_set_options"](name, diff, **kwargs) + ret["comment"] = 'Parameters were updated for collection "{0}".'.format( + name + ) + ret["result"] = True - ret['changes'] = { - 'old': salt.utils.json.dumps(current_options, sort_keys=True, indent=4, separators=(',', ': ')), - 'new': salt.utils.json.dumps(options, sort_keys=True, indent=4, separators=(',', ': ')) + ret["changes"] = { + "old": salt.utils.json.dumps( + current_options, sort_keys=True, indent=4, separators=(",", ": ") + ), + "new": salt.utils.json.dumps( + options, sort_keys=True, indent=4, separators=(",", ": ") + ), } return ret else: - new_changes = salt.utils.json.dumps(options, sort_keys=True, indent=4, separators=(',', ': ')) - if __opts__['test']: - ret['comment'] = 'The collection "{0}" will be created.'.format(name) - ret['result'] = None + new_changes = salt.utils.json.dumps( + options, sort_keys=True, indent=4, separators=(",", ": ") + ) + if __opts__["test"]: + ret["comment"] = 'The collection "{0}" will be created.'.format(name) + ret["result"] = None else: __salt__["solrcloud.collection_create"](name, options, **kwargs) - ret['comment'] = 'The collection "{0}" has been created.'.format(name) - ret['result'] = True + ret["comment"] = 'The collection "{0}" has been created.'.format(name) + ret["result"] = True - ret['changes'] = { - 'old': None, - 'new': str('options=') + new_changes # future lint: disable=blacklisted-function + ret["changes"] = { + "old": None, + "new": str("options=") + + new_changes, # future lint: disable=blacklisted-function } return ret diff --git a/salt/states/splunk.py b/salt/states/splunk.py index 398a20842b7..b5340546f10 100644 --- a/salt/states/splunk.py +++ b/salt/states/splunk.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Splunk User State Module .. versionadded:: 2016.3.0. @@ -12,19 +12,21 @@ This state is used to ensure presence of users in splunk. splunk.present: - name: 'Example TestUser1' - email: example@domain.com -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the splunk module is available in __salt__ - ''' - return 'splunk' if 'splunk.list_users' in __salt__ else False + """ + if "splunk.list_users" in __salt__: + return "splunk" + return (False, "splunk module could not be loaded") def present(email, profile="splunk", **kwargs): - ''' + """ Ensure a user is present .. code-block:: yaml @@ -40,71 +42,73 @@ def present(email, profile="splunk", **kwargs): email This is the email of the user in splunk - ''' + """ - name = kwargs.get('name') + name = kwargs.get("name") - ret = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': '' - } + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - target = __salt__['splunk.get_user'](email, profile=profile, user_details=True) + target = __salt__["splunk.get_user"](email, profile=profile, user_details=True) if not target: - if __opts__['test']: - ret['comment'] = 'User {0} will be created'.format(name) + if __opts__["test"]: + ret["comment"] = "User {0} will be created".format(name) return ret # create the user - result = __salt__['splunk.create_user']( - email, profile=profile, **kwargs - ) + result = __salt__["splunk.create_user"](email, profile=profile, **kwargs) if result: - ret['changes'].setdefault('old', None) - ret['changes'].setdefault('new', 'User {0} exists'.format(name)) - ret['result'] = True + ret["changes"].setdefault("old", None) + ret["changes"].setdefault("new", "User {0} exists".format(name)) + ret["result"] = True else: - ret['result'] = False - ret['comment'] = 'Failed to create {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0}".format(name) return ret else: - ret['comment'] = 'User {0} set to be updated.'.format(name) - if __opts__['test']: - ret['result'] = None + ret["comment"] = "User {0} set to be updated.".format(name) + if __opts__["test"]: + ret["result"] = None return ret # found a user... updating - result = __salt__['splunk.update_user']( - email, profile, **kwargs - ) + result = __salt__["splunk.update_user"](email, profile, **kwargs) if isinstance(result, bool) and result: # no update - ret['result'] = None - ret['comment'] = "No changes" + ret["result"] = None + ret["comment"] = "No changes" else: diff = {} - for field in ['name', 'realname', 'roles', 'defaultApp', 'tz', 'capabilities']: - if field == 'roles': - diff['roles'] = list(set(target.get(field, [])).symmetric_difference(set(result.get(field, [])))) + for field in [ + "name", + "realname", + "roles", + "defaultApp", + "tz", + "capabilities", + ]: + if field == "roles": + diff["roles"] = list( + set(target.get(field, [])).symmetric_difference( + set(result.get(field, [])) + ) + ) elif target.get(field) != result.get(field): diff[field] = result.get(field) newvalues = result - ret['result'] = True - ret['changes']['diff'] = diff - ret['changes']['old'] = target - ret['changes']['new'] = newvalues + ret["result"] = True + ret["changes"]["diff"] = diff + ret["changes"]["old"] = target + ret["changes"]["new"] = newvalues return ret def absent(email, profile="splunk", **kwargs): - ''' + """ Ensure a splunk user is absent .. code-block:: yaml @@ -121,38 +125,38 @@ def absent(email, profile="splunk", **kwargs): name This is the splunk username used to identify the user. - ''' - user_identity = kwargs.get('name') + """ + user_identity = kwargs.get("name") ret = { - 'name': user_identity, - 'changes': {}, - 'result': None, - 'comment': 'User {0} is absent.'.format(user_identity) + "name": user_identity, + "changes": {}, + "result": None, + "comment": "User {0} is absent.".format(user_identity), } - target = __salt__['splunk.get_user'](email, profile=profile) + target = __salt__["splunk.get_user"](email, profile=profile) if not target: - ret['comment'] = 'User {0} does not exist'.format(user_identity) - ret['result'] = True + ret["comment"] = "User {0} does not exist".format(user_identity) + ret["result"] = True return ret - if __opts__['test']: - ret['comment'] = "User {0} is all set to be deleted".format(user_identity) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "User {0} is all set to be deleted".format(user_identity) + ret["result"] = None return ret - result = __salt__['splunk.delete_user'](email, profile=profile) + result = __salt__["splunk.delete_user"](email, profile=profile) if result: - ret['comment'] = 'Deleted user {0}'.format(user_identity) - ret['changes'].setdefault('old', 'User {0} exists'.format(user_identity)) - ret['changes'].setdefault('new', 'User {0} deleted'.format(user_identity)) - ret['result'] = True + ret["comment"] = "Deleted user {0}".format(user_identity) + ret["changes"].setdefault("old", "User {0} exists".format(user_identity)) + ret["changes"].setdefault("new", "User {0} deleted".format(user_identity)) + ret["result"] = True else: - ret['comment'] = 'Failed to delete {0}'.format(user_identity) - ret['result'] = False + ret["comment"] = "Failed to delete {0}".format(user_identity) + ret["result"] = False return ret diff --git a/salt/states/splunk_search.py b/salt/states/splunk_search.py index d7dd7e5090e..fcfa87bb8cf 100644 --- a/salt/states/splunk_search.py +++ b/salt/states/splunk_search.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Splunk Search State Module .. versionadded:: 2015.5.0 @@ -12,19 +12,21 @@ This state is used to ensure presence of splunk searches. splunk_search.present: - name: This is the splunk search name - search: index=main sourcetype= -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the splunk_search module is available in __salt__ - ''' - return 'splunk_search' if 'splunk_search.get' in __salt__ else False + """ + if "splunk_search.get" in __salt__: + return "splunk_search" + return (False, "splunk module could not be loaded") def present(name, profile="splunk", **kwargs): - ''' + """ Ensure a search is present .. code-block:: yaml @@ -38,57 +40,48 @@ def present(name, profile="splunk", **kwargs): name This is the name of the search in splunk - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - target = __salt__['splunk_search.get'](name, profile=profile) + target = __salt__["splunk_search.get"](name, profile=profile) if target: - if __opts__['test']: - ret['comment'] = "Would update {0}".format(name) + if __opts__["test"]: + ret["comment"] = "Would update {0}".format(name) return ret # found a search... updating - result = __salt__['splunk_search.update']( - name, profile=profile, **kwargs - ) + result = __salt__["splunk_search.update"](name, profile=profile, **kwargs) if not result: # no update - ret['result'] = True - ret['comment'] = "No changes" + ret["result"] = True + ret["comment"] = "No changes" else: (newvalues, diffs) = result old_content = dict(target.content) old_changes = {} for x in newvalues: old_changes[x] = old_content.get(x, None) - ret['result'] = True - ret['changes']['diff'] = diffs - ret['changes']['old'] = old_changes - ret['changes']['new'] = newvalues + ret["result"] = True + ret["changes"]["diff"] = diffs + ret["changes"]["old"] = old_changes + ret["changes"]["new"] = newvalues else: - if __opts__['test']: - ret['comment'] = "Would create {0}".format(name) + if __opts__["test"]: + ret["comment"] = "Would create {0}".format(name) return ret # creating a new search - result = __salt__['splunk_search.create']( - name, profile=profile, **kwargs - ) + result = __salt__["splunk_search.create"](name, profile=profile, **kwargs) if result: - ret['result'] = True - ret['changes']['old'] = False - ret['changes']['new'] = kwargs + ret["result"] = True + ret["changes"]["old"] = False + ret["changes"]["new"] = kwargs else: - ret['result'] = False - ret['comment'] = 'Failed to create {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to create {0}".format(name) return ret def absent(name, profile="splunk"): - ''' + """ Ensure a search is absent .. code-block:: yaml @@ -100,27 +93,27 @@ def absent(name, profile="splunk"): name This is the name of the search in splunk - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '{0} is absent.'.format(name) + "name": name, + "changes": {}, + "result": True, + "comment": "{0} is absent.".format(name), } - target = __salt__['splunk_search.get'](name, profile=profile) + target = __salt__["splunk_search.get"](name, profile=profile) if target: - if __opts__['test']: + if __opts__["test"]: ret = {} ret["name"] = name - ret['comment'] = "Would delete {0}".format(name) - ret['result'] = None + ret["comment"] = "Would delete {0}".format(name) + ret["result"] = None return ret - result = __salt__['splunk_search.delete'](name, profile=profile) + result = __salt__["splunk_search.delete"](name, profile=profile) if result: - ret['comment'] = '{0} was deleted'.format(name) + ret["comment"] = "{0} was deleted".format(name) else: - ret['comment'] = 'Failed to delete {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to delete {0}".format(name) + ret["result"] = False return ret diff --git a/salt/states/sqlite3.py b/salt/states/sqlite3.py index ac889dc6e11..96181e98a41 100644 --- a/salt/states/sqlite3.py +++ b/salt/states/sqlite3.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of SQLite3 databases =============================== @@ -91,10 +91,10 @@ can be approximated with sqlite3's module functions and module.run: - sql: "DELETE FROM records WHERE id > {{ count[0] }} AND domain_id = {{ domain_id }}" - watch: - sqlite3: zone-insert-12 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs from salt.ext import six @@ -108,14 +108,16 @@ except ImportError: def __virtual__(): - ''' + """ Only load if the sqlite3 module is available - ''' - return HAS_SQLITE3 + """ + if HAS_SQLITE3: + return True + return (False, "Unable to import sqlite3") def row_absent(name, db, table, where_sql, where_args=None): - ''' + """ Makes sure the specified row is absent in db. If multiple rows match where_sql, then the state will fail. @@ -133,56 +135,53 @@ def row_absent(name, db, table, where_sql, where_args=None): where_args The list parameters to substitute in where_sql - ''' - changes = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + changes = {"name": name, "changes": {}, "result": None, "comment": ""} conn = None try: conn = sqlite3.connect(db, detect_types=sqlite3.PARSE_DECLTYPES) conn.row_factory = _dict_factory rows = None if where_args is None: - rows = _query(conn, - "SELECT * FROM `" + table + "` WHERE " + where_sql) + rows = _query(conn, "SELECT * FROM `" + table + "` WHERE " + where_sql) else: - rows = _query(conn, - "SELECT * FROM `" + table + "` WHERE " + where_sql, - where_args) + rows = _query( + conn, "SELECT * FROM `" + table + "` WHERE " + where_sql, where_args + ) if len(rows) > 1: - changes['result'] = False - changes['comment'] = "More than one row matched the specified query" + changes["result"] = False + changes["comment"] = "More than one row matched the specified query" elif len(rows) == 1: - if __opts__['test']: - changes['result'] = True - changes['comment'] = "Row will be removed in " + table - changes['changes']['old'] = rows[0] + if __opts__["test"]: + changes["result"] = True + changes["comment"] = "Row will be removed in " + table + changes["changes"]["old"] = rows[0] else: if where_args is None: - cursor = conn.execute("DELETE FROM `" + - table + "` WHERE " + where_sql) + cursor = conn.execute( + "DELETE FROM `" + table + "` WHERE " + where_sql + ) else: - cursor = conn.execute("DELETE FROM `" + - table + "` WHERE " + where_sql, - where_args) + cursor = conn.execute( + "DELETE FROM `" + table + "` WHERE " + where_sql, where_args + ) conn.commit() if cursor.rowcount == 1: - changes['result'] = True - changes['comment'] = "Row removed" - changes['changes']['old'] = rows[0] + changes["result"] = True + changes["comment"] = "Row removed" + changes["changes"]["old"] = rows[0] else: - changes['result'] = False - changes['comment'] = "Unable to remove row" + changes["result"] = False + changes["comment"] = "Unable to remove row" else: - changes['result'] = True - changes['comment'] = 'Row is absent' + changes["result"] = True + changes["comment"] = "Row is absent" except Exception as e: # pylint: disable=broad-except - changes['result'] = False - changes['comment'] = six.text_type(e) + changes["result"] = False + changes["comment"] = six.text_type(e) finally: if conn: @@ -191,14 +190,8 @@ def row_absent(name, db, table, where_sql, where_args=None): return changes -def row_present(name, - db, - table, - data, - where_sql, - where_args=None, - update=False): - ''' +def row_present(name, db, table, data, where_sql, where_args=None, update=False): + """ Checks to make sure the given row exists. If row exists and update is True then row will be updated with data. Otherwise it will leave existing row unmodified and check it against data. If the existing data @@ -229,34 +222,30 @@ def row_present(name, True will replace the existing row with data When False and the row exists and data does not equal the row data then the state will fail - ''' - changes = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + changes = {"name": name, "changes": {}, "result": None, "comment": ""} conn = None try: conn = sqlite3.connect(db, detect_types=sqlite3.PARSE_DECLTYPES) conn.row_factory = _dict_factory rows = None if where_args is None: - rows = _query(conn, "SELECT * FROM `" + - table + "` WHERE " + where_sql) + rows = _query(conn, "SELECT * FROM `" + table + "` WHERE " + where_sql) else: - rows = _query(conn, "SELECT * FROM `" + - table + "` WHERE " + where_sql, - where_args) + rows = _query( + conn, "SELECT * FROM `" + table + "` WHERE " + where_sql, where_args + ) if len(rows) > 1: - changes['result'] = False - changes['comment'] = 'More than one row matched the specified query' + changes["result"] = False + changes["comment"] = "More than one row matched the specified query" elif len(rows) == 1: for key, value in six.iteritems(data): if key in rows[0] and rows[0][key] != value: if update: - if __opts__['test']: - changes['result'] = True - changes['comment'] = "Row will be update in " + table + if __opts__["test"]: + changes["result"] = True + changes["comment"] = "Row will be update in " + table else: columns = [] @@ -275,33 +264,34 @@ def row_present(name, cursor = conn.execute(sql, params) conn.commit() if cursor.rowcount == 1: - changes['result'] = True - changes['comment'] = "Row updated" - changes['changes']['old'] = rows[0] - changes['changes']['new'] = data + changes["result"] = True + changes["comment"] = "Row updated" + changes["changes"]["old"] = rows[0] + changes["changes"]["new"] = data else: - changes['result'] = False - changes['comment'] = "Row update failed" + changes["result"] = False + changes["comment"] = "Row update failed" else: - changes['result'] = False - changes['comment'] = "Existing data does" + \ - "not match desired state" + changes["result"] = False + changes["comment"] = ( + "Existing data does" + "not match desired state" + ) break - if changes['result'] is None: - changes['result'] = True - changes['comment'] = "Row exists" + if changes["result"] is None: + changes["result"] = True + changes["comment"] = "Row exists" else: - if __opts__['test']: - changes['result'] = True - changes['changes']['new'] = data - changes['comment'] = "Row will be inserted into " + table + if __opts__["test"]: + changes["result"] = True + changes["changes"]["new"] = data + changes["comment"] = "Row will be inserted into " + table else: columns = [] value_stmt = [] values = [] for key, value in six.iteritems(data): - value_stmt.append('?') + value_stmt.append("?") values.append(value) columns.append("`" + key + "`") @@ -313,16 +303,16 @@ def row_present(name, cursor = conn.execute(sql, values) conn.commit() if cursor.rowcount == 1: - changes['result'] = True - changes['changes']['new'] = data - changes['comment'] = 'Inserted row' + changes["result"] = True + changes["changes"]["new"] = data + changes["comment"] = "Inserted row" else: - changes['result'] = False - changes['comment'] = "Unable to insert data" + changes["result"] = False + changes["comment"] = "Unable to insert data" except Exception as e: # pylint: disable=broad-except - changes['result'] = False - changes['comment'] = six.text_type(e) + changes["result"] = False + changes["comment"] = six.text_type(e) finally: if conn: @@ -332,7 +322,7 @@ def row_present(name, def table_absent(name, db): - ''' + """ Make sure the specified table does not exist name @@ -340,38 +330,37 @@ def table_absent(name, db): db The name of the database file - ''' - changes = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + changes = {"name": name, "changes": {}, "result": None, "comment": ""} conn = None try: conn = sqlite3.connect(db, detect_types=sqlite3.PARSE_DECLTYPES) - tables = _query(conn, "SELECT sql FROM sqlite_master " + - " WHERE type='table' AND name=?", [name]) + tables = _query( + conn, + "SELECT sql FROM sqlite_master " + " WHERE type='table' AND name=?", + [name], + ) if len(tables) == 1: - if __opts__['test']: - changes['result'] = True - changes['comment'] = "'" + name + "' will be dropped" + if __opts__["test"]: + changes["result"] = True + changes["comment"] = "'" + name + "' will be dropped" else: conn.execute("DROP TABLE " + name) conn.commit() - changes['changes']['old'] = tables[0][0] - changes['result'] = True - changes['comment'] = "'" + name + "' was dropped" + changes["changes"]["old"] = tables[0][0] + changes["result"] = True + changes["comment"] = "'" + name + "' was dropped" elif len(tables) == 0: - changes['result'] = True - changes['comment'] = "'" + name + "' is already absent" + changes["result"] = True + changes["comment"] = "'" + name + "' is already absent" else: - changes['result'] = False - changes['comment'] = "Multiple tables with the same name='" + \ - name + "'" + changes["result"] = False + changes["comment"] = "Multiple tables with the same name='" + name + "'" except Exception as e: # pylint: disable=broad-except - changes['result'] = False - changes['comment'] = six.text_type(e) + changes["result"] = False + changes["comment"] = six.text_type(e) finally: if conn: @@ -381,7 +370,7 @@ def table_absent(name, db): def table_present(name, db, schema, force=False): - ''' + """ Make sure the specified table exists with the specified schema name @@ -397,17 +386,16 @@ def table_present(name, db, schema, force=False): If the name of the table exists and force is set to False, the state will fail. If force is set to True, the existing table will be replaced with the new table - ''' - changes = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + changes = {"name": name, "changes": {}, "result": None, "comment": ""} conn = None try: conn = sqlite3.connect(db, detect_types=sqlite3.PARSE_DECLTYPES) - tables = _query(conn, - "SELECT sql FROM sqlite_master " + - "WHERE type='table' AND name=?", [name]) + tables = _query( + conn, + "SELECT sql FROM sqlite_master " + "WHERE type='table' AND name=?", + [name], + ) if len(tables) == 1: sql = None @@ -418,27 +406,27 @@ def table_present(name, db, schema, force=False): if sql != tables[0][0]: if force: - if __opts__['test']: - changes['result'] = True - changes['changes']['old'] = tables[0][0] - changes['changes']['new'] = sql - changes['comment'] = "'" + name + "' will be replaced" + if __opts__["test"]: + changes["result"] = True + changes["changes"]["old"] = tables[0][0] + changes["changes"]["new"] = sql + changes["comment"] = "'" + name + "' will be replaced" else: conn.execute("DROP TABLE `" + name + "`") conn.execute(sql) conn.commit() - changes['result'] = True - changes['changes']['old'] = tables[0][0] - changes['changes']['new'] = sql - changes['comment'] = "Replaced '" + name + "'" + changes["result"] = True + changes["changes"]["old"] = tables[0][0] + changes["changes"]["new"] = sql + changes["comment"] = "Replaced '" + name + "'" else: - changes['result'] = False - changes['comment'] = "Expected schema=" + sql + \ - "\nactual schema=" + tables[0][0] + changes["result"] = False + changes["comment"] = ( + "Expected schema=" + sql + "\nactual schema=" + tables[0][0] + ) else: - changes['result'] = True - changes['comment'] = "'" + name + \ - "' exists with matching schema" + changes["result"] = True + changes["comment"] = "'" + name + "' exists with matching schema" elif len(tables) == 0: # Create the table sql = None @@ -446,23 +434,23 @@ def table_present(name, db, schema, force=False): sql = schema else: sql = _get_sql_from_schema(name, schema) - if __opts__['test']: - changes['result'] = True - changes['changes']['new'] = sql - changes['comment'] = "'" + name + "' will be created" + if __opts__["test"]: + changes["result"] = True + changes["changes"]["new"] = sql + changes["comment"] = "'" + name + "' will be created" else: conn.execute(sql) conn.commit() - changes['result'] = True - changes['changes']['new'] = sql - changes['comment'] = "Created table '" + name + "'" + changes["result"] = True + changes["changes"]["new"] = sql + changes["comment"] = "Created table '" + name + "'" else: - changes['result'] = False - changes['comment'] = 'Multiple tables with the same name=' + name + changes["result"] = False + changes["comment"] = "Multiple tables with the same name=" + name except Exception as e: # pylint: disable=broad-except - changes['result'] = False - changes['comment'] = str(e) + changes["result"] = False + changes["comment"] = str(e) finally: if conn: diff --git a/salt/states/ssh_auth.py b/salt/states/ssh_auth.py index fa256707a4f..867d0be3b55 100644 --- a/salt/states/ssh_auth.py +++ b/salt/states/ssh_auth.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Control of entries in SSH authorized_key files ============================================== @@ -58,10 +58,11 @@ to use a YAML 'explicit key', as demonstrated in the second example below. - ssh-dss AAAAB3NzaCL0sQ9fJ5bYTEyY== user@domain - option3="value3" ssh-dss AAAAB3NzaC1kcQ9J5bYTEyY== other@testdomain - AAAAB3NzaC1kcQ9fJFF435bYTEyY== newcomment -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import re import sys @@ -69,39 +70,42 @@ import sys from salt.ext import six -def _present_test(user, name, enc, comment, options, source, config, fingerprint_hash_type): - ''' +def _present_test( + user, name, enc, comment, options, source, config, fingerprint_hash_type +): + """ Run checks for "present" - ''' + """ result = None if source: - keys = __salt__['ssh.check_key_file']( - user, - source, - config, - saltenv=__env__, - fingerprint_hash_type=fingerprint_hash_type) + keys = __salt__["ssh.check_key_file"]( + user, + source, + config, + saltenv=__env__, + fingerprint_hash_type=fingerprint_hash_type, + ) if keys: - comment = '' + comment = "" for key, status in six.iteritems(keys): - if status == 'exists': + if status == "exists": continue - comment += 'Set to {0}: {1}\n'.format(status, key) + comment += "Set to {0}: {1}\n".format(status, key) if comment: return result, comment - err = sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('ssh_auth.error', None) + err = sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "ssh_auth.error", None + ) if err: return False, err else: return ( True, - 'All host keys in file {0} are already present'.format(source) + "All host keys in file {0} are already present".format(source), ) else: # check if this is of form {options} {enc} {key} {comment} - sshre = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$') + sshre = re.compile(r"^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$") fullkey = sshre.search(name) # if it is {key} [comment] if not fullkey: @@ -112,7 +116,7 @@ def _present_test(user, name, enc, comment, options, source, config, fingerprint else: # if there are options, set them if fullkey.group(1): - options = fullkey.group(1).split(',') + options = fullkey.group(1).split(",") # key is of format: {enc} {key} [comment] comps = fullkey.group(2).split() enc = comps[0] @@ -120,63 +124,62 @@ def _present_test(user, name, enc, comment, options, source, config, fingerprint if len(comps) == 3: comment = comps[2] - check = __salt__['ssh.check_key']( - user, - name, - enc, - comment, - options, - config=config, - fingerprint_hash_type=fingerprint_hash_type) - if check == 'update': - comment = ( - 'Key {0} for user {1} is set to be updated' - ).format(name, user) - elif check == 'add': - comment = ( - 'Key {0} for user {1} is set to be added' - ).format(name, user) - elif check == 'exists': + check = __salt__["ssh.check_key"]( + user, + name, + enc, + comment, + options, + config=config, + fingerprint_hash_type=fingerprint_hash_type, + ) + if check == "update": + comment = ("Key {0} for user {1} is set to be updated").format(name, user) + elif check == "add": + comment = ("Key {0} for user {1} is set to be added").format(name, user) + elif check == "exists": result = True - comment = ('The authorized host key {0} is already present ' - 'for user {1}'.format(name, user)) + comment = ( + "The authorized host key {0} is already present " + "for user {1}".format(name, user) + ) return result, comment -def _absent_test(user, name, enc, comment, options, source, config, fingerprint_hash_type): - ''' +def _absent_test( + user, name, enc, comment, options, source, config, fingerprint_hash_type +): + """ Run checks for "absent" - ''' + """ result = None if source: - keys = __salt__['ssh.check_key_file']( - user, - source, - config, - saltenv=__env__, - fingerprint_hash_type=fingerprint_hash_type) + keys = __salt__["ssh.check_key_file"]( + user, + source, + config, + saltenv=__env__, + fingerprint_hash_type=fingerprint_hash_type, + ) if keys: - comment = '' + comment = "" for key, status in list(keys.items()): - if status == 'add': + if status == "add": continue - comment += 'Set to remove: {0}\n'.format(key) + comment += "Set to remove: {0}\n".format(key) if comment: return result, comment - err = sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('ssh_auth.error', None) + err = sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "ssh_auth.error", None + ) if err: return False, err else: - return ( - True, - 'All host keys in file {0} are already absent'.format(source) - ) + return (True, "All host keys in file {0} are already absent".format(source)) else: # check if this is of form {options} {enc} {key} {comment} - sshre = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$') + sshre = re.compile(r"^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$") fullkey = sshre.search(name) # if it is {key} [comment] if not fullkey: @@ -187,7 +190,7 @@ def _absent_test(user, name, enc, comment, options, source, config, fingerprint_ else: # if there are options, set them if fullkey.group(1): - options = fullkey.group(1).split(',') + options = fullkey.group(1).split(",") # key is of format: {enc} {key} [comment] comps = fullkey.group(2).split() enc = comps[0] @@ -195,34 +198,36 @@ def _absent_test(user, name, enc, comment, options, source, config, fingerprint_ if len(comps) == 3: comment = comps[2] - check = __salt__['ssh.check_key']( - user, - name, - enc, - comment, - options, - config=config, - fingerprint_hash_type=fingerprint_hash_type) - if check == 'update' or check == 'exists': - comment = ('Key {0} for user {1} is set for removal').format(name, user) + check = __salt__["ssh.check_key"]( + user, + name, + enc, + comment, + options, + config=config, + fingerprint_hash_type=fingerprint_hash_type, + ) + if check == "update" or check == "exists": + comment = ("Key {0} for user {1} is set for removal").format(name, user) else: - comment = ('Key is already absent') + comment = "Key is already absent" result = True return result, comment def present( - name, - user, - enc='ssh-rsa', - comment='', - source='', - options=None, - config='.ssh/authorized_keys', - fingerprint_hash_type=None, - **kwargs): - ''' + name, + user, + enc="ssh-rsa", + comment="", + source="", + options=None, + config=".ssh/authorized_keys", + fingerprint_hash_type=None, + **kwargs +): + """ Verifies that the specified SSH key is present for the specified user name @@ -266,15 +271,12 @@ def present( fingerprint_hash_type The public key fingerprint hash type that the public key fingerprint was originally hashed with. This defaults to ``sha256`` if not specified. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if source == '': + if source == "": # check if this is of form {options} {enc} {key} {comment} - sshre = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$') + sshre = re.compile(r"^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$") fullkey = sshre.search(name) # if it is {key} [comment] if not fullkey: @@ -285,7 +287,7 @@ def present( else: # if there are options, set them if fullkey.group(1): - options = fullkey.group(1).split(',') + options = fullkey.group(1).split(",") # key is of format: {enc} {key} [comment] comps = fullkey.group(2).split(None, 2) enc = comps[0] @@ -293,111 +295,121 @@ def present( if len(comps) == 3: comment = comps[2] - if __opts__['test']: - ret['result'], ret['comment'] = _present_test( - user, - name, - enc, - comment, - options or [], - source, - config, - fingerprint_hash_type) + if __opts__["test"]: + ret["result"], ret["comment"] = _present_test( + user, + name, + enc, + comment, + options or [], + source, + config, + fingerprint_hash_type, + ) return ret # Get only the path to the file without env referrences to check if exists - if source != '': - source_path = __salt__['cp.get_url']( - source, - None, - saltenv=__env__) + if source != "": + source_path = __salt__["cp.get_url"](source, None, saltenv=__env__) - if source != '' and not source_path: - data = 'no key' - elif source != '' and source_path: - key = __salt__['cp.get_file_str']( - source, - saltenv=__env__) + if source != "" and not source_path: + data = "no key" + elif source != "" and source_path: + key = __salt__["cp.get_file_str"](source, saltenv=__env__) filehasoptions = False # check if this is of form {options} {enc} {key} {comment} - sshre = re.compile(r'^(ssh\-|ecds).*') - key = key.rstrip().split('\n') + sshre = re.compile(r"^(ssh\-|ecds).*") + key = key.rstrip().split("\n") for keyline in key: filehasoptions = sshre.match(keyline) if not filehasoptions: - data = __salt__['ssh.set_auth_key_from_file']( - user, - source, - config=config, - saltenv=__env__, - fingerprint_hash_type=fingerprint_hash_type) + data = __salt__["ssh.set_auth_key_from_file"]( + user, + source, + config=config, + saltenv=__env__, + fingerprint_hash_type=fingerprint_hash_type, + ) else: # Split keyline to get key and comment - keyline = keyline.split(' ') + keyline = keyline.split(" ") key_type = keyline[0] key_value = keyline[1] - key_comment = keyline[2] if len(keyline) > 2 else '' - data = __salt__['ssh.set_auth_key']( - user, - key_value, - enc=key_type, - comment=key_comment, - options=options or [], - config=config, - fingerprint_hash_type=fingerprint_hash_type) + key_comment = keyline[2] if len(keyline) > 2 else "" + data = __salt__["ssh.set_auth_key"]( + user, + key_value, + enc=key_type, + comment=key_comment, + options=options or [], + config=config, + fingerprint_hash_type=fingerprint_hash_type, + ) else: - data = __salt__['ssh.set_auth_key']( - user, - name, - enc=enc, - comment=comment, - options=options or [], - config=config, - fingerprint_hash_type=fingerprint_hash_type) + data = __salt__["ssh.set_auth_key"]( + user, + name, + enc=enc, + comment=comment, + options=options or [], + config=config, + fingerprint_hash_type=fingerprint_hash_type, + ) - if data == 'replace': - ret['changes'][name] = 'Updated' - ret['comment'] = ('The authorized host key {0} for user {1} was ' - 'updated'.format(name, user)) + if data == "replace": + ret["changes"][name] = "Updated" + ret[ + "comment" + ] = "The authorized host key {0} for user {1} was " "updated".format(name, user) return ret - elif data == 'no change': - ret['comment'] = ('The authorized host key {0} is already present ' - 'for user {1}'.format(name, user)) - elif data == 'new': - ret['changes'][name] = 'New' - ret['comment'] = ('The authorized host key {0} for user {1} was added' - .format(name, user)) - elif data == 'no key': - ret['result'] = False - ret['comment'] = ('Failed to add the ssh key. Source file {0} is ' - 'missing'.format(source)) - elif data == 'fail': - ret['result'] = False - err = sys.modules[ - __salt__['test.ping'].__module__ - ].__context__.pop('ssh_auth.error', None) + elif data == "no change": + ret["comment"] = ( + "The authorized host key {0} is already present " + "for user {1}".format(name, user) + ) + elif data == "new": + ret["changes"][name] = "New" + ret["comment"] = "The authorized host key {0} for user {1} was added".format( + name, user + ) + elif data == "no key": + ret["result"] = False + ret[ + "comment" + ] = "Failed to add the ssh key. Source file {0} is " "missing".format(source) + elif data == "fail": + ret["result"] = False + err = sys.modules[__salt__["test.ping"].__module__].__context__.pop( + "ssh_auth.error", None + ) if err: - ret['comment'] = err + ret["comment"] = err else: - ret['comment'] = ('Failed to add the ssh key. Is the home ' - 'directory available, and/or does the key file ' - 'exist?') - elif data == 'invalid' or data == 'Invalid public key': - ret['result'] = False - ret['comment'] = 'Invalid public ssh key, most likely has spaces or invalid syntax' + ret["comment"] = ( + "Failed to add the ssh key. Is the home " + "directory available, and/or does the key file " + "exist?" + ) + elif data == "invalid" or data == "Invalid public key": + ret["result"] = False + ret[ + "comment" + ] = "Invalid public ssh key, most likely has spaces or invalid syntax" return ret -def absent(name, - user, - enc='ssh-rsa', - comment='', - source='', - options=None, - config='.ssh/authorized_keys', - fingerprint_hash_type=None): - ''' +def absent( + name, + user, + enc="ssh-rsa", + comment="", + source="", + options=None, + config=".ssh/authorized_keys", + fingerprint_hash_type=None, +): + """ Verifies that the specified SSH key is absent name @@ -433,51 +445,51 @@ def absent(name, was originally hashed with. This defaults to ``sha256`` if not specified. .. versionadded:: 2016.11.7 - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if __opts__['test']: - ret['result'], ret['comment'] = _absent_test( - user, - name, - enc, - comment, - options or [], - source, - config, - fingerprint_hash_type) + if __opts__["test"]: + ret["result"], ret["comment"] = _absent_test( + user, + name, + enc, + comment, + options or [], + source, + config, + fingerprint_hash_type, + ) return ret # Extract Key from file if source is present - if source != '': - key = __salt__['cp.get_file_str']( - source, - saltenv=__env__) + if source != "": + key = __salt__["cp.get_file_str"](source, saltenv=__env__) filehasoptions = False # check if this is of form {options} {enc} {key} {comment} - sshre = re.compile(r'^(ssh\-|ecds).*') - key = key.rstrip().split('\n') + sshre = re.compile(r"^(ssh\-|ecds).*") + key = key.rstrip().split("\n") for keyline in key: filehasoptions = sshre.match(keyline) if not filehasoptions: - ret['comment'] = __salt__['ssh.rm_auth_key_from_file'](user, - source, - config, - saltenv=__env__, - fingerprint_hash_type=fingerprint_hash_type) + ret["comment"] = __salt__["ssh.rm_auth_key_from_file"]( + user, + source, + config, + saltenv=__env__, + fingerprint_hash_type=fingerprint_hash_type, + ) else: # Split keyline to get key - keyline = keyline.split(' ') - ret['comment'] = __salt__['ssh.rm_auth_key'](user, - keyline[1], - config=config, - fingerprint_hash_type=fingerprint_hash_type) + keyline = keyline.split(" ") + ret["comment"] = __salt__["ssh.rm_auth_key"]( + user, + keyline[1], + config=config, + fingerprint_hash_type=fingerprint_hash_type, + ) else: # Get just the key - sshre = re.compile(r'^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$') + sshre = re.compile(r"^(.*?)\s?((?:ssh\-|ecds)[\w-]+\s.+)$") fullkey = sshre.search(name) # if it is {key} [comment] if not fullkey: @@ -488,39 +500,39 @@ def absent(name, else: # if there are options, set them if fullkey.group(1): - options = fullkey.group(1).split(',') + options = fullkey.group(1).split(",") # key is of format: {enc} {key} [comment] comps = fullkey.group(2).split() enc = comps[0] name = comps[1] if len(comps) == 3: comment = comps[2] - ret['comment'] = __salt__['ssh.rm_auth_key'](user, - name, - config=config, - fingerprint_hash_type=fingerprint_hash_type) + ret["comment"] = __salt__["ssh.rm_auth_key"]( + user, name, config=config, fingerprint_hash_type=fingerprint_hash_type + ) - if ret['comment'] == 'User authorized keys file not present': - ret['result'] = False + if ret["comment"] == "User authorized keys file not present": + ret["result"] = False return ret - elif ret['comment'] == 'Key removed': - ret['changes'][name] = 'Removed' + elif ret["comment"] == "Key removed": + ret["changes"][name] = "Removed" return ret def manage( - name, - ssh_keys, - user, - enc='ssh-rsa', - comment='', - source='', - options=None, - config='.ssh/authorized_keys', - fingerprint_hash_type=None, - **kwargs): - ''' + name, + ssh_keys, + user, + enc="ssh-rsa", + comment="", + source="", + options=None, + config=".ssh/authorized_keys", + fingerprint_hash_type=None, + **kwargs +): + """ .. versionadded:: 3000 Ensures that only the specified ssh_keys are present for the specified user @@ -566,40 +578,46 @@ def manage( fingerprint_hash_type The public key fingerprint hash type that the public key fingerprint was originally hashed with. This defaults to ``sha256`` if not specified. - ''' - ret = {'name': '', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "", "changes": {}, "result": True, "comment": ""} all_potential_keys = [] for ssh_key in ssh_keys: # gather list potential ssh keys for removal comparison # options, enc, and comments could be in the mix - all_potential_keys.extend(ssh_key.split(' ')) - existing_keys = __salt__['ssh.auth_keys'](user=user).keys() + all_potential_keys.extend(ssh_key.split(" ")) + existing_keys = __salt__["ssh.auth_keys"](user=user).keys() remove_keys = set(existing_keys).difference(all_potential_keys) for remove_key in remove_keys: - if __opts__['test']: - remove_comment = '{0} Key set for removal'.format(remove_key) - ret['comment'] = remove_comment - ret['result'] = None + if __opts__["test"]: + remove_comment = "{0} Key set for removal".format(remove_key) + ret["comment"] = remove_comment + ret["result"] = None else: - remove_comment = absent(remove_key, user)['comment'] - ret['changes'][remove_key] = remove_comment + remove_comment = absent(remove_key, user)["comment"] + ret["changes"][remove_key] = remove_comment for ssh_key in ssh_keys: - run_return = present(ssh_key, user, enc, comment, source, - options, config, fingerprint_hash_type, **kwargs) - if run_return['changes']: - ret['changes'].update(run_return['changes']) + run_return = present( + ssh_key, + user, + enc, + comment, + source, + options, + config, + fingerprint_hash_type, + **kwargs + ) + if run_return["changes"]: + ret["changes"].update(run_return["changes"]) else: - ret['comment'] += '\n' + run_return['comment'] - ret['comment'] = ret['comment'].strip() + ret["comment"] += "\n" + run_return["comment"] + ret["comment"] = ret["comment"].strip() - if run_return['result'] is None: - ret['result'] = None - elif not run_return['result']: - ret['result'] = False + if run_return["result"] is None: + ret["result"] = None + elif not run_return["result"]: + ret["result"] = False return ret diff --git a/salt/states/ssh_known_hosts.py b/salt/states/ssh_known_hosts.py index f6d1017db37..b817971f1ea 100644 --- a/salt/states/ssh_known_hosts.py +++ b/salt/states/ssh_known_hosts.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Control of SSH known_hosts entries ================================== @@ -18,8 +18,8 @@ Manage the information stored in the known_hosts files. ssh_known_hosts: - absent - user: root -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import os @@ -29,31 +29,32 @@ import salt.utils.platform from salt.exceptions import CommandNotFoundError # Define the state's virtual name -__virtualname__ = 'ssh_known_hosts' +__virtualname__ = "ssh_known_hosts" def __virtual__(): - ''' + """ Does not work on Windows, requires ssh module functions - ''' + """ if salt.utils.platform.is_windows(): - return False, 'ssh_known_hosts: Does not support Windows' + return False, "ssh_known_hosts: Does not support Windows" return __virtualname__ def present( - name, - user=None, - fingerprint=None, - key=None, - port=None, - enc=None, - config=None, - hash_known_hosts=True, - timeout=5, - fingerprint_hash_type=None): - ''' + name, + user=None, + fingerprint=None, + key=None, + port=None, + enc=None, + config=None, + hash_known_hosts=True, + timeout=5, + fingerprint_hash_type=None, +): + """ Verifies that the specified host is known by the specified user On many systems, specifically those running with openssh 4 or older, the @@ -109,58 +110,61 @@ def present( .. versionadded:: 2016.11.4 .. versionchanged:: 2017.7.0: default changed from ``md5`` to ``sha256`` - ''' - ret = {'name': name, - 'changes': {}, - 'result': None if __opts__['test'] else True, - 'comment': ''} + """ + ret = { + "name": name, + "changes": {}, + "result": None if __opts__["test"] else True, + "comment": "", + } if not user: - config = config or '/etc/ssh/ssh_known_hosts' + config = config or "/etc/ssh/ssh_known_hosts" else: - config = config or '.ssh/known_hosts' + config = config or ".ssh/known_hosts" if not user and not os.path.isabs(config): comment = 'If not specifying a "user", specify an absolute "config".' - ret['result'] = False + ret["result"] = False return dict(ret, comment=comment) - if __opts__['test']: + if __opts__["test"]: if key and fingerprint: comment = 'Specify either "key" or "fingerprint", not both.' - ret['result'] = False + ret["result"] = False return dict(ret, comment=comment) elif key and not enc: comment = 'Required argument "enc" if using "key" argument.' - ret['result'] = False + ret["result"] = False return dict(ret, comment=comment) try: - result = __salt__['ssh.check_known_host'](user, name, - key=key, - fingerprint=fingerprint, - config=config, - port=port, - fingerprint_hash_type=fingerprint_hash_type) + result = __salt__["ssh.check_known_host"]( + user, + name, + key=key, + fingerprint=fingerprint, + config=config, + port=port, + fingerprint_hash_type=fingerprint_hash_type, + ) except CommandNotFoundError as err: - ret['result'] = False - ret['comment'] = 'ssh.check_known_host error: {0}'.format(err) + ret["result"] = False + ret["comment"] = "ssh.check_known_host error: {0}".format(err) return ret - if result == 'exists': - comment = 'Host {0} is already in {1}'.format(name, config) - ret['result'] = True + if result == "exists": + comment = "Host {0} is already in {1}".format(name, config) + ret["result"] = True return dict(ret, comment=comment) - elif result == 'add': - comment = 'Key for {0} is set to be added to {1}'.format(name, - config) + elif result == "add": + comment = "Key for {0} is set to be added to {1}".format(name, config) return dict(ret, comment=comment) else: # 'update' - comment = 'Key for {0} is set to be updated in {1}'.format(name, - config) + comment = "Key for {0} is set to be updated in {1}".format(name, config) return dict(ret, comment=comment) - result = __salt__['ssh.set_known_host']( + result = __salt__["ssh.set_known_host"]( user=user, hostname=name, fingerprint=fingerprint, @@ -170,29 +174,35 @@ def present( config=config, hash_known_hosts=hash_known_hosts, timeout=timeout, - fingerprint_hash_type=fingerprint_hash_type) - if result['status'] == 'exists': - return dict(ret, - comment='{0} already exists in {1}'.format(name, config)) - elif result['status'] == 'error': - return dict(ret, result=False, comment=result['error']) + fingerprint_hash_type=fingerprint_hash_type, + ) + if result["status"] == "exists": + return dict(ret, comment="{0} already exists in {1}".format(name, config)) + elif result["status"] == "error": + return dict(ret, result=False, comment=result["error"]) else: # 'updated' if key: - new_key = result['new'][0]['key'] - return dict(ret, - changes={'old': result['old'], 'new': result['new']}, - comment='{0}\'s key saved to {1} (key: {2})'.format( - name, config, new_key)) + new_key = result["new"][0]["key"] + return dict( + ret, + changes={"old": result["old"], "new": result["new"]}, + comment="{0}'s key saved to {1} (key: {2})".format( + name, config, new_key + ), + ) else: - fingerprint = result['new'][0]['fingerprint'] - return dict(ret, - changes={'old': result['old'], 'new': result['new']}, - comment='{0}\'s key saved to {1} (fingerprint: {2})'.format( - name, config, fingerprint)) + fingerprint = result["new"][0]["fingerprint"] + return dict( + ret, + changes={"old": result["old"], "new": result["new"]}, + comment="{0}'s key saved to {1} (fingerprint: {2})".format( + name, config, fingerprint + ), + ) def absent(name, user=None, config=None): - ''' + """ Verifies that the specified host is not known by the given user name @@ -209,37 +219,37 @@ def absent(name, user=None, config=None): directory, defaults to ".ssh/known_hosts". If no user is specified, defaults to "/etc/ssh/ssh_known_hosts". If present, must be an absolute path when a user is not specified. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if not user: - config = config or '/etc/ssh/ssh_known_hosts' + config = config or "/etc/ssh/ssh_known_hosts" else: - config = config or '.ssh/known_hosts' + config = config or ".ssh/known_hosts" if not user and not os.path.isabs(config): comment = 'If not specifying a "user", specify an absolute "config".' - ret['result'] = False + ret["result"] = False return dict(ret, comment=comment) - known_host = __salt__['ssh.get_known_host_entries'](user=user, hostname=name, config=config) + known_host = __salt__["ssh.get_known_host_entries"]( + user=user, hostname=name, config=config + ) if not known_host: - return dict(ret, comment='Host is already absent') + return dict(ret, comment="Host is already absent") - if __opts__['test']: - comment = 'Key for {0} is set to be removed from {1}'.format(name, - config) - ret['result'] = None + if __opts__["test"]: + comment = "Key for {0} is set to be removed from {1}".format(name, config) + ret["result"] = None return dict(ret, comment=comment) - rm_result = __salt__['ssh.rm_known_host'](user=user, hostname=name, config=config) - if rm_result['status'] == 'error': - return dict(ret, result=False, comment=rm_result['error']) + rm_result = __salt__["ssh.rm_known_host"](user=user, hostname=name, config=config) + if rm_result["status"] == "error": + return dict(ret, result=False, comment=rm_result["error"]) else: - return dict(ret, - changes={'old': known_host, 'new': None}, - result=True, - comment=rm_result['comment']) + return dict( + ret, + changes={"old": known_host, "new": None}, + result=True, + comment=rm_result["comment"], + ) diff --git a/salt/states/stateconf.py b/salt/states/stateconf.py index 80ebcf085b9..514eaae0825 100644 --- a/salt/states/stateconf.py +++ b/salt/states/stateconf.py @@ -1,20 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" Stateconf System ================ The stateconf system is intended for use only with the stateconf renderer. This State module presents the set function. This function does not execute any functionality, but is used to interact with the stateconf renderer. -''' +""" from __future__ import absolute_import, print_function, unicode_literals def _no_op(name, **kwargs): - ''' + """ No-op state to support state config via the stateconf renderer. - ''' - return dict(name=name, result=True, changes={}, comment='') + """ + return dict(name=name, result=True, changes={}, comment="") set = context = _no_op # pylint: disable=C0103 diff --git a/salt/states/status.py b/salt/states/status.py index 32b92eae928..373814eeddb 100644 --- a/salt/states/status.py +++ b/salt/states/status.py @@ -1,81 +1,79 @@ # -*- coding: utf-8 -*- -''' +""" Minion status monitoring Maps to the `status` execution module. -''' +""" from __future__ import absolute_import, print_function, unicode_literals __monitor__ = [ - 'loadavg', - 'process', - ] + "loadavg", + "process", +] def loadavg(name, maximum=None, minimum=None): - ''' + """ Return the current load average for the specified minion. Available values for name are `1-min`, `5-min` and `15-min`. `minimum` and `maximum` values should be passed in as strings. - ''' + """ # Monitoring state, no changes will be made so no test interface needed - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}, - 'data': {}} # Data field for monitoring state + ret = { + "name": name, + "result": False, + "comment": "", + "changes": {}, + "data": {}, + } # Data field for monitoring state - data = __salt__['status.loadavg']() + data = __salt__["status.loadavg"]() if name not in data: - ret['result'] = False - ret['comment'] += 'Requested load average {0} not available '.format( - name - ) + ret["result"] = False + ret["comment"] += "Requested load average {0} not available ".format(name) return ret if minimum and maximum and minimum >= maximum: - ret['comment'] += 'Min must be less than max' - if ret['comment']: + ret["comment"] += "Min must be less than max" + if ret["comment"]: return ret cap = float(data[name]) - ret['data'] = data[name] + ret["data"] = data[name] if minimum: if cap < float(minimum): - ret['comment'] = 'Load avg is below minimum of {0} at {1}'.format( - minimum, cap) + ret["comment"] = "Load avg is below minimum of {0} at {1}".format( + minimum, cap + ) return ret if maximum: if cap > float(maximum): - ret['comment'] = 'Load avg above maximum of {0} at {1}'.format( - maximum, cap) + ret["comment"] = "Load avg above maximum of {0} at {1}".format(maximum, cap) return ret - ret['comment'] = 'Load avg in acceptable range' - ret['result'] = True + ret["comment"] = "Load avg in acceptable range" + ret["result"] = True return ret def process(name): - ''' + """ Return whether the specified signature is found in the process tree. This differs slightly from the services states, in that it may refer to a process that is not managed via the init system. - ''' + """ # Monitoring state, no changes will be made so no test interface needed - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}, - 'data': {}} # Data field for monitoring state + ret = { + "name": name, + "result": False, + "comment": "", + "changes": {}, + "data": {}, + } # Data field for monitoring state - data = __salt__['status.pid'](name) + data = __salt__["status.pid"](name) if not data: - ret['result'] = False - ret['comment'] += 'Process signature "{0}" not found '.format( - name - ) + ret["result"] = False + ret["comment"] += 'Process signature "{0}" not found '.format(name) return ret - ret['data'] = data - ret['comment'] += 'Process signature "{0}" was found '.format( - name - ) - ret['result'] = True + ret["data"] = data + ret["comment"] += 'Process signature "{0}" was found '.format(name) + ret["result"] = True return ret diff --git a/salt/states/statuspage.py b/salt/states/statuspage.py index 0b5f773412f..569d8f2b8c8 100644 --- a/salt/states/statuspage.py +++ b/salt/states/statuspage.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" StatusPage ========== @@ -17,12 +17,13 @@ In the minion configuration file, the following block is required: page_id: <PAGE_ID> .. versionadded:: 2017.7.0 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import logging # import python std lib import time -import logging # import salt from salt.ext import six @@ -31,19 +32,13 @@ from salt.ext import six # module properties # ---------------------------------------------------------------------------------------------------------------------- -__virtualname__ = 'statuspage' +__virtualname__ = "statuspage" log = logging.getLogger(__file__) -_DO_NOT_COMPARE_FIELDS = [ - 'created_at', - 'updated_at' -] +_DO_NOT_COMPARE_FIELDS = ["created_at", "updated_at"] -_MATCH_KEYS = [ - 'id', - 'name' -] +_MATCH_KEYS = ["id", "name"] _PACE = 1 # 1 request per second @@ -53,39 +48,32 @@ _PACE = 1 # 1 request per second def __virtual__(): - ''' + """ Return the execution module virtualname. - ''' - return __virtualname__ + """ + if "statuspage.create" in __salt__: + return True + return (False, "statuspage module could not be loaded") def _default_ret(name): - ''' + """ Default dictionary returned. - ''' - return { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } + """ + return {"name": name, "result": False, "comment": "", "changes": {}} def _compute_diff_ret(): - ''' + """ Default dictionary retuned by the _compute_diff helper. - ''' - return { - 'add': [], - 'update': [], - 'remove': [] - } + """ + return {"add": [], "update": [], "remove": []} def _clear_dict(endpoint_props): - ''' + """ Eliminates None entries from the features of the endpoint dict. - ''' + """ return dict( (prop_name, prop_val) for prop_name, prop_val in six.iteritems(endpoint_props) @@ -94,10 +82,10 @@ def _clear_dict(endpoint_props): def _ignore_keys(endpoint_props): - ''' + """ Ignores some keys that might be different without any important info. These keys are defined under _DO_NOT_COMPARE_FIELDS. - ''' + """ return dict( (prop_name, prop_val) for prop_name, prop_val in six.iteritems(endpoint_props) @@ -106,9 +94,9 @@ def _ignore_keys(endpoint_props): def _unique(list_of_dicts): - ''' + """ Returns an unique list of dictionaries given a list that may contain duplicates. - ''' + """ unique_list = [] for ele in list_of_dicts: if ele not in unique_list: @@ -117,9 +105,9 @@ def _unique(list_of_dicts): def _clear_ignore(endpoint_props): - ''' + """ Both _clear_dict and _ignore_keys in a single iteration. - ''' + """ return dict( (prop_name, prop_val) for prop_name, prop_val in six.iteritems(endpoint_props) @@ -128,19 +116,16 @@ def _clear_ignore(endpoint_props): def _clear_ignore_list(lst): - ''' + """ Apply _clear_ignore to a list. - ''' - return _unique([ - _clear_ignore(ele) - for ele in lst - ]) + """ + return _unique([_clear_ignore(ele) for ele in lst]) def _find_match(ele, lst): - ''' + """ Find a matching element in a list. - ''' + """ for _ele in lst: for match_key in _MATCH_KEYS: if _ele.get(match_key) == ele.get(match_key): @@ -148,27 +133,27 @@ def _find_match(ele, lst): def _update_on_fields(prev_ele, new_ele): - ''' + """ Return a dict with fields that differ between two dicts. - ''' + """ fields_update = dict( (prop_name, prop_val) for prop_name, prop_val in six.iteritems(new_ele) if new_ele.get(prop_name) != prev_ele.get(prop_name) or prop_name in _MATCH_KEYS ) if len(set(fields_update.keys()) | set(_MATCH_KEYS)) > len(set(_MATCH_KEYS)): - if 'id' not in fields_update: + if "id" not in fields_update: # in case of update, the ID is necessary # if not specified in the pillar, # will try to get it from the prev_ele - fields_update['id'] = prev_ele['id'] + fields_update["id"] = prev_ele["id"] return fields_update def _compute_diff(expected_endpoints, configured_endpoints): - ''' + """ Compares configured endpoints with the expected configuration and returns the differences. - ''' + """ new_endpoints = [] update_endpoints = [] remove_endpoints = [] @@ -177,16 +162,12 @@ def _compute_diff(expected_endpoints, configured_endpoints): # noth configured => configure with expected endpoints if not configured_endpoints: - ret.update({ - 'add': expected_endpoints - }) + ret.update({"add": expected_endpoints}) return ret # noting expected => remove everything if not expected_endpoints: - ret.update({ - 'remove': configured_endpoints - }) + ret.update({"remove": configured_endpoints}) return ret expected_endpoints_clear = _clear_ignore_list(expected_endpoints) @@ -195,7 +176,9 @@ def _compute_diff(expected_endpoints, configured_endpoints): for expected_endpoint_clear in expected_endpoints_clear: if expected_endpoint_clear not in configured_endpoints_clear: # none equal => add or update - matching_ele = _find_match(expected_endpoint_clear, configured_endpoints_clear) + matching_ele = _find_match( + expected_endpoint_clear, configured_endpoints_clear + ) if not matching_ele: # new element => add new_endpoints.append(expected_endpoint_clear) @@ -206,30 +189,35 @@ def _compute_diff(expected_endpoints, configured_endpoints): update_endpoints.append(update_fields) for configured_endpoint_clear in configured_endpoints_clear: if configured_endpoint_clear not in expected_endpoints_clear: - matching_ele = _find_match(configured_endpoint_clear, expected_endpoints_clear) + matching_ele = _find_match( + configured_endpoint_clear, expected_endpoints_clear + ) if not matching_ele: # no match found => remove remove_endpoints.append(configured_endpoint_clear) return { - 'add': new_endpoints, - 'update': update_endpoints, - 'remove': remove_endpoints + "add": new_endpoints, + "update": update_endpoints, + "remove": remove_endpoints, } + # ---------------------------------------------------------------------------------------------------------------------- # callable functions # ---------------------------------------------------------------------------------------------------------------------- -def create(name, - endpoint='incidents', - api_url=None, - page_id=None, - api_key=None, - api_version=None, - **kwargs): - ''' +def create( + name, + endpoint="incidents", + api_url=None, + page_id=None, + api_key=None, + api_version=None, + **kwargs +): + """ Insert a new entry under a specific endpoint. endpoint: incidents @@ -259,42 +247,49 @@ def create(name, - endpoint: components - name: my component - group_id: 993vgplshj12 - ''' + """ ret = _default_ret(name) endpoint_sg = endpoint[:-1] # singular - if __opts__['test']: - ret['comment'] = 'The following {endpoint} would be created:'.format(endpoint=endpoint_sg) - ret['result'] = None - ret['changes'][endpoint] = {} + if __opts__["test"]: + ret["comment"] = "The following {endpoint} would be created:".format( + endpoint=endpoint_sg + ) + ret["result"] = None + ret["changes"][endpoint] = {} for karg, warg in six.iteritems(kwargs): - if warg is None or karg.startswith('__'): + if warg is None or karg.startswith("__"): continue - ret['changes'][endpoint][karg] = warg + ret["changes"][endpoint][karg] = warg return ret - sp_create = __salt__['statuspage.create'](endpoint=endpoint, - api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version, - **kwargs) - if not sp_create.get('result'): - ret['comment'] = 'Unable to create {endpoint}: {msg}'.format(endpoint=endpoint_sg, - msg=sp_create.get('comment')) + sp_create = __salt__["statuspage.create"]( + endpoint=endpoint, + api_url=api_url, + page_id=page_id, + api_key=api_key, + api_version=api_version, + **kwargs + ) + if not sp_create.get("result"): + ret["comment"] = "Unable to create {endpoint}: {msg}".format( + endpoint=endpoint_sg, msg=sp_create.get("comment") + ) else: - ret['comment'] = '{endpoint} created!'.format(endpoint=endpoint_sg) - ret['result'] = True - ret['changes'] = sp_create.get('out') + ret["comment"] = "{endpoint} created!".format(endpoint=endpoint_sg) + ret["result"] = True + ret["changes"] = sp_create.get("out") -def update(name, - endpoint='incidents', - id=None, - api_url=None, - page_id=None, - api_key=None, - api_version=None, - **kwargs): - ''' +def update( + name, + endpoint="incidents", + id=None, + api_url=None, + page_id=None, + api_key=None, + api_version=None, + **kwargs +): + """ Update attribute(s) of a specific endpoint. id @@ -323,47 +318,55 @@ def update(name, statuspage.update: - id: dz959yz2nd4l - status: resolved - ''' + """ ret = _default_ret(name) endpoint_sg = endpoint[:-1] # singular if not id: - log.error('Invalid %s ID', endpoint_sg) - ret['comment'] = 'Please specify a valid {endpoint} ID'.format(endpoint=endpoint_sg) + log.error("Invalid %s ID", endpoint_sg) + ret["comment"] = "Please specify a valid {endpoint} ID".format( + endpoint=endpoint_sg + ) return ret - if __opts__['test']: - ret['comment'] = '{endpoint} #{id} would be updated:'.format(endpoint=endpoint_sg, id=id) - ret['result'] = None - ret['changes'][endpoint] = {} + if __opts__["test"]: + ret["comment"] = "{endpoint} #{id} would be updated:".format( + endpoint=endpoint_sg, id=id + ) + ret["result"] = None + ret["changes"][endpoint] = {} for karg, warg in six.iteritems(kwargs): - if warg is None or karg.startswith('__'): + if warg is None or karg.startswith("__"): continue - ret['changes'][endpoint][karg] = warg + ret["changes"][endpoint][karg] = warg return ret - sp_update = __salt__['statuspage.update'](endpoint=endpoint, - id=id, - api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version, - **kwargs) - if not sp_update.get('result'): - ret['comment'] = 'Unable to update {endpoint} #{id}: {msg}'.format(endpoint=endpoint_sg, - id=id, - msg=sp_update.get('comment')) + sp_update = __salt__["statuspage.update"]( + endpoint=endpoint, + id=id, + api_url=api_url, + page_id=page_id, + api_key=api_key, + api_version=api_version, + **kwargs + ) + if not sp_update.get("result"): + ret["comment"] = "Unable to update {endpoint} #{id}: {msg}".format( + endpoint=endpoint_sg, id=id, msg=sp_update.get("comment") + ) else: - ret['comment'] = '{endpoint} #{id} updated!'.format(endpoint=endpoint_sg, id=id) - ret['result'] = True - ret['changes'] = sp_update.get('out') + ret["comment"] = "{endpoint} #{id} updated!".format(endpoint=endpoint_sg, id=id) + ret["result"] = True + ret["changes"] = sp_update.get("out") -def delete(name, - endpoint='incidents', - id=None, - api_url=None, - page_id=None, - api_key=None, - api_version=None): - ''' +def delete( + name, + endpoint="incidents", + id=None, + api_url=None, + page_id=None, + api_key=None, + api_version=None, +): + """ Remove an entry from an endpoint. endpoint: incidents @@ -389,40 +392,48 @@ def delete(name, statuspage.delete: - endpoint: components - id: ftgks51sfs2d - ''' + """ ret = _default_ret(name) endpoint_sg = endpoint[:-1] # singular if not id: - log.error('Invalid %s ID', endpoint_sg) - ret['comment'] = 'Please specify a valid {endpoint} ID'.format(endpoint=endpoint_sg) + log.error("Invalid %s ID", endpoint_sg) + ret["comment"] = "Please specify a valid {endpoint} ID".format( + endpoint=endpoint_sg + ) return ret - if __opts__['test']: - ret['comment'] = '{endpoint} #{id} would be removed!'.format(endpoint=endpoint_sg, id=id) - ret['result'] = None - sp_delete = __salt__['statuspage.delete'](endpoint=endpoint, - id=id, - api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version) - if not sp_delete.get('result'): - ret['comment'] = 'Unable to delete {endpoint} #{id}: {msg}'.format(endpoint=endpoint_sg, - id=id, - msg=sp_delete.get('comment')) + if __opts__["test"]: + ret["comment"] = "{endpoint} #{id} would be removed!".format( + endpoint=endpoint_sg, id=id + ) + ret["result"] = None + sp_delete = __salt__["statuspage.delete"]( + endpoint=endpoint, + id=id, + api_url=api_url, + page_id=page_id, + api_key=api_key, + api_version=api_version, + ) + if not sp_delete.get("result"): + ret["comment"] = "Unable to delete {endpoint} #{id}: {msg}".format( + endpoint=endpoint_sg, id=id, msg=sp_delete.get("comment") + ) else: - ret['comment'] = '{endpoint} #{id} deleted!'.format(endpoint=endpoint_sg, id=id) - ret['result'] = True + ret["comment"] = "{endpoint} #{id} deleted!".format(endpoint=endpoint_sg, id=id) + ret["result"] = True -def managed(name, - config, - api_url=None, - page_id=None, - api_key=None, - api_version=None, - pace=_PACE, - allow_empty=False): - ''' +def managed( + name, + config, + api_url=None, + page_id=None, + api_key=None, + api_version=None, + pace=_PACE, + allow_empty=False, +): + """ Manage the StatusPage configuration. config @@ -468,123 +479,116 @@ def managed(name, - name: incident2 status: investigating impact: minor - ''' + """ complete_diff = {} ret = _default_ret(name) if not config and not allow_empty: - ret.update({ - 'result': False, - 'comment': 'Cannot remove everything. To allow this, please set the option `allow_empty` as True.' - }) + ret.update( + { + "result": False, + "comment": "Cannot remove everything. To allow this, please set the option `allow_empty` as True.", + } + ) return ret is_empty = True for endpoint_name, endpoint_expected_config in six.iteritems(config): if endpoint_expected_config: is_empty = False - endpoint_existing_config_ret = __salt__['statuspage.retrieve'](endpoint=endpoint_name, - api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version) - if not endpoint_existing_config_ret.get('result'): - ret.update({ - 'comment': endpoint_existing_config_ret.get('comment') - }) + endpoint_existing_config_ret = __salt__["statuspage.retrieve"]( + endpoint=endpoint_name, + api_url=api_url, + page_id=page_id, + api_key=api_key, + api_version=api_version, + ) + if not endpoint_existing_config_ret.get("result"): + ret.update({"comment": endpoint_existing_config_ret.get("comment")}) return ret # stop at first error - endpoint_existing_config = endpoint_existing_config_ret.get('out') - complete_diff[endpoint_name] = _compute_diff(endpoint_expected_config, endpoint_existing_config) + endpoint_existing_config = endpoint_existing_config_ret.get("out") + complete_diff[endpoint_name] = _compute_diff( + endpoint_expected_config, endpoint_existing_config + ) if is_empty and not allow_empty: - ret.update({ - 'result': False, - 'comment': 'Cannot remove everything. To allow this, please set the option `allow_empty` as True.' - }) + ret.update( + { + "result": False, + "comment": "Cannot remove everything. To allow this, please set the option `allow_empty` as True.", + } + ) return ret any_changes = False for endpoint_name, endpoint_diff in six.iteritems(complete_diff): - if endpoint_diff.get('add') or endpoint_diff.get('update') or endpoint_diff.get('remove'): + if ( + endpoint_diff.get("add") + or endpoint_diff.get("update") + or endpoint_diff.get("remove") + ): any_changes = True if not any_changes: - ret.update({ - 'result': True, - 'comment': 'No changes required.', - 'changes': {} - }) + ret.update({"result": True, "comment": "No changes required.", "changes": {}}) return ret - ret.update({ - 'changes': complete_diff - }) - if __opts__.get('test'): - ret.update({ - 'comment': 'Testing mode. Would apply the following changes:', - 'result': None - }) + ret.update({"changes": complete_diff}) + if __opts__.get("test"): + ret.update( + { + "comment": "Testing mode. Would apply the following changes:", + "result": None, + } + ) return ret for endpoint_name, endpoint_diff in six.iteritems(complete_diff): endpoint_sg = endpoint_name[:-1] # singular - for new_endpoint in endpoint_diff.get('add'): - log.debug('Defining new %s %s', - endpoint_sg, - new_endpoint - ) - adding = __salt__['statuspage.create'](endpoint=endpoint_name, - api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version, - **new_endpoint) - if not adding.get('result'): - ret.update({ - 'comment': adding.get('comment') - }) + for new_endpoint in endpoint_diff.get("add"): + log.debug("Defining new %s %s", endpoint_sg, new_endpoint) + adding = __salt__["statuspage.create"]( + endpoint=endpoint_name, + api_url=api_url, + page_id=page_id, + api_key=api_key, + api_version=api_version, + **new_endpoint + ) + if not adding.get("result"): + ret.update({"comment": adding.get("comment")}) return ret if pace: - time.sleep(1/pace) - for update_endpoint in endpoint_diff.get('update'): - if 'id' not in update_endpoint: + time.sleep(1 / pace) + for update_endpoint in endpoint_diff.get("update"): + if "id" not in update_endpoint: continue - endpoint_id = update_endpoint.pop('id') - log.debug('Updating %s #%s: %s', - endpoint_sg, - endpoint_id, - update_endpoint - ) - updating = __salt__['statuspage.update'](endpoint=endpoint_name, - id=endpoint_id, - api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version, - **update_endpoint) - if not updating.get('result'): - ret.update({ - 'comment': updating.get('comment') - }) + endpoint_id = update_endpoint.pop("id") + log.debug("Updating %s #%s: %s", endpoint_sg, endpoint_id, update_endpoint) + updating = __salt__["statuspage.update"]( + endpoint=endpoint_name, + id=endpoint_id, + api_url=api_url, + page_id=page_id, + api_key=api_key, + api_version=api_version, + **update_endpoint + ) + if not updating.get("result"): + ret.update({"comment": updating.get("comment")}) return ret if pace: - time.sleep(1/pace) - for remove_endpoint in endpoint_diff.get('remove'): - if 'id' not in remove_endpoint: + time.sleep(1 / pace) + for remove_endpoint in endpoint_diff.get("remove"): + if "id" not in remove_endpoint: continue - endpoint_id = remove_endpoint.pop('id') - log.debug('Removing %s #%s', - endpoint_sg, - endpoint_id - ) - removing = __salt__['statuspage.delete'](endpoint=endpoint_name, - id=endpoint_id, - api_url=api_url, - page_id=page_id, - api_key=api_key, - api_version=api_version) - if not removing.get('result'): - ret.update({ - 'comment': removing.get('comment') - }) + endpoint_id = remove_endpoint.pop("id") + log.debug("Removing %s #%s", endpoint_sg, endpoint_id) + removing = __salt__["statuspage.delete"]( + endpoint=endpoint_name, + id=endpoint_id, + api_url=api_url, + page_id=page_id, + api_key=api_key, + api_version=api_version, + ) + if not removing.get("result"): + ret.update({"comment": removing.get("comment")}) return ret if pace: - time.sleep(1/pace) - ret.update({ - 'result': True, - 'comment': 'StatusPage updated.' - }) + time.sleep(1 / pace) + ret.update({"result": True, "comment": "StatusPage updated."}) return ret diff --git a/salt/states/supervisord.py b/salt/states/supervisord.py index 1cc7bdecb14..0f1b32fd21f 100644 --- a/salt/states/supervisord.py +++ b/salt/states/supervisord.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Interaction with the Supervisor daemon ====================================== @@ -11,13 +11,13 @@ Interaction with the Supervisor daemon - pkg: supervisor - watch: - file: /etc/nginx/sites-enabled/wsgi_server.conf -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -from salt.ext import six +from salt.ext import six log = logging.getLogger(__name__) @@ -25,38 +25,37 @@ log = logging.getLogger(__name__) def _check_error(result, success_message): ret = {} - if 'ERROR' in result: - if any(substring in result for substring in [ - 'already started', - 'not running', - 'process group already active' - ]): - ret['comment'] = success_message + if "ERROR" in result: + if any( + substring in result + for substring in [ + "already started", + "not running", + "process group already active", + ] + ): + ret["comment"] = success_message else: - ret['comment'] = result - ret['result'] = False + ret["comment"] = result + ret["result"] = False else: - ret['comment'] = success_message + ret["comment"] = success_message return ret def _is_stopped_state(state): - if state in ('STOPPED', 'STOPPING', 'EXITED', 'FATAL', 'BACKOFF'): + if state in ("STOPPED", "STOPPING", "EXITED", "FATAL", "BACKOFF"): return True - if state in ('STARTING', 'RUNNING'): + if state in ("STARTING", "RUNNING"): return False return False -def running(name, - restart=False, - update=False, - user=None, - conf_file=None, - bin_env=None, - **kwargs): - ''' +def running( + name, restart=False, update=False, user=None, conf_file=None, bin_env=None, **kwargs +): + """ Ensure the named service is running. name @@ -80,79 +79,75 @@ def running(name, path to supervisorctl bin or path to virtualenv with supervisor installed - ''' - if name.endswith(':*'): + """ + if name.endswith(":*"): name = name[:-1] - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - if 'supervisord.status' not in __salt__: - ret['result'] = False - ret['comment'] = 'Supervisord module not activated. Do you need to install supervisord?' + if "supervisord.status" not in __salt__: + ret["result"] = False + ret[ + "comment" + ] = "Supervisord module not activated. Do you need to install supervisord?" return ret - all_processes = __salt__['supervisord.status']( - user=user, - conf_file=conf_file, - bin_env=bin_env + all_processes = __salt__["supervisord.status"]( + user=user, conf_file=conf_file, bin_env=bin_env ) # parse process groups process_groups = set() for proc in all_processes: - if ':' in proc: - process_groups.add(proc[:proc.index(':') + 1]) + if ":" in proc: + process_groups.add(proc[: proc.index(":") + 1]) process_groups = sorted(process_groups) matches = {} if name in all_processes: - matches[name] = (all_processes[name]['state'].lower() == 'running') + matches[name] = all_processes[name]["state"].lower() == "running" elif name in process_groups: for process in (x for x in all_processes if x.startswith(name)): - matches[process] = ( - all_processes[process]['state'].lower() == 'running' - ) + matches[process] = all_processes[process]["state"].lower() == "running" to_add = not bool(matches) - if __opts__['test']: + if __opts__["test"]: if not to_add: # Process/group already present, check if any need to be started to_start = [x for x, y in six.iteritems(matches) if y is False] if to_start: - ret['result'] = None - if name.endswith(':'): + ret["result"] = None + if name.endswith(":"): # Process group if len(to_start) == len(matches): - ret['comment'] = ( - 'All services in group \'{0}\' will be started' - .format(name) - ) + ret[ + "comment" + ] = "All services in group '{0}' will be started".format(name) else: - ret['comment'] = ( - 'The following services will be started: {0}' - .format(' '.join(to_start)) + ret[ + "comment" + ] = "The following services will be started: {0}".format( + " ".join(to_start) ) else: # Single program - ret['comment'] = 'Service {0} will be started'.format(name) + ret["comment"] = "Service {0} will be started".format(name) else: - if name.endswith(':'): + if name.endswith(":"): # Process group - ret['comment'] = ( - 'All services in group \'{0}\' are already running' - .format(name) - ) + ret[ + "comment" + ] = "All services in group '{0}' are already running".format(name) else: - ret['comment'] = ('Service {0} is already running' - .format(name)) + ret["comment"] = "Service {0} is already running".format(name) else: - ret['result'] = None + ret["result"] = None # Process/group needs to be added - if name.endswith(':'): - _type = 'Group \'{0}\''.format(name) + if name.endswith(":"): + _type = "Group '{0}'".format(name) else: - _type = 'Service {0}'.format(name) - ret['comment'] = '{0} will be added and started'.format(_type) + _type = "Service {0}".format(name) + ret["comment"] = "{0} will be added and started".format(_type) return ret changes = [] @@ -165,33 +160,24 @@ def running(name, # # That is, unless `to_add` somehow manages to contain processes # we don't want running, in which case adding them may be a mistake - comment = 'Updating supervisor' - result = __salt__['supervisord.update']( - user=user, - conf_file=conf_file, - bin_env=bin_env + comment = "Updating supervisor" + result = __salt__["supervisord.update"]( + user=user, conf_file=conf_file, bin_env=bin_env ) ret.update(_check_error(result, comment)) log.debug(comment) - if '{0}: updated'.format(name) in result: + if "{0}: updated".format(name) in result: just_updated = True elif to_add: # Not sure if this condition is precise enough. - comment = 'Adding service: {0}'.format(name) - __salt__['supervisord.reread']( - user=user, - conf_file=conf_file, - bin_env=bin_env - ) + comment = "Adding service: {0}".format(name) + __salt__["supervisord.reread"](user=user, conf_file=conf_file, bin_env=bin_env) # Causes supervisorctl to throw `ERROR: process group already active` # if process group exists. At this moment, I'm not sure how to handle # this outside of grepping out the expected string in `_check_error`. - result = __salt__['supervisord.add']( - name, - user=user, - conf_file=conf_file, - bin_env=bin_env + result = __salt__["supervisord.add"]( + name, user=user, conf_file=conf_file, bin_env=bin_env ) ret.update(_check_error(result, comment)) @@ -202,82 +188,69 @@ def running(name, process_type = None if name in process_groups: - process_type = 'group' + process_type = "group" # check if any processes in this group are stopped is_stopped = False for proc in all_processes: - if proc.startswith(name) \ - and _is_stopped_state(all_processes[proc]['state']): + if proc.startswith(name) and _is_stopped_state( + all_processes[proc]["state"] + ): is_stopped = True break elif name in all_processes: - process_type = 'service' + process_type = "service" - if _is_stopped_state(all_processes[name]['state']): + if _is_stopped_state(all_processes[name]["state"]): is_stopped = True else: is_stopped = False if is_stopped is False: if restart and not just_updated: - comment = 'Restarting{0}: {1}'.format( - process_type is not None and ' {0}'.format(process_type) or '', - name + comment = "Restarting{0}: {1}".format( + process_type is not None and " {0}".format(process_type) or "", name ) log.debug(comment) - result = __salt__['supervisord.restart']( - name, - user=user, - conf_file=conf_file, - bin_env=bin_env + result = __salt__["supervisord.restart"]( + name, user=user, conf_file=conf_file, bin_env=bin_env ) ret.update(_check_error(result, comment)) changes.append(comment) elif just_updated: - comment = 'Not starting updated{0}: {1}'.format( - process_type is not None and ' {0}'.format(process_type) or '', - name + comment = "Not starting updated{0}: {1}".format( + process_type is not None and " {0}".format(process_type) or "", name ) result = comment - ret.update({'comment': comment}) + ret.update({"comment": comment}) else: - comment = 'Not starting already running{0}: {1}'.format( - process_type is not None and ' {0}'.format(process_type) or '', - name + comment = "Not starting already running{0}: {1}".format( + process_type is not None and " {0}".format(process_type) or "", name ) result = comment - ret.update({'comment': comment}) + ret.update({"comment": comment}) elif not just_updated: - comment = 'Starting{0}: {1}'.format( - process_type is not None and ' {0}'.format(process_type) or '', - name + comment = "Starting{0}: {1}".format( + process_type is not None and " {0}".format(process_type) or "", name ) changes.append(comment) log.debug(comment) - result = __salt__['supervisord.start']( - name, - user=user, - conf_file=conf_file, - bin_env=bin_env + result = __salt__["supervisord.start"]( + name, user=user, conf_file=conf_file, bin_env=bin_env ) ret.update(_check_error(result, comment)) log.debug(six.text_type(result)) - if ret['result'] and changes: - ret['changes'][name] = ' '.join(changes) + if ret["result"] and changes: + ret["changes"][name] = " ".join(changes) return ret -def dead(name, - user=None, - conf_file=None, - bin_env=None, - **kwargs): - ''' +def dead(name, user=None, conf_file=None, bin_env=None, **kwargs): + """ Ensure the named service is dead (not running). name @@ -295,28 +268,25 @@ def dead(name, path to supervisorctl bin or path to virtualenv with supervisor installed - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - if __opts__['test']: - ret['result'] = None - ret['comment'] = ( - 'Service {0} is set to be stopped'.format(name)) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Service {0} is set to be stopped".format(name) else: - comment = 'Stopping service: {0}'.format(name) + comment = "Stopping service: {0}".format(name) log.debug(comment) - all_processes = __salt__['supervisord.status']( - user=user, - conf_file=conf_file, - bin_env=bin_env + all_processes = __salt__["supervisord.status"]( + user=user, conf_file=conf_file, bin_env=bin_env ) # parse process groups process_groups = [] for proc in all_processes: - if ':' in proc: - process_groups.append(proc[:proc.index(':') + 1]) + if ":" in proc: + process_groups.append(proc[: proc.index(":") + 1]) process_groups = list(set(process_groups)) is_stopped = None @@ -325,44 +295,40 @@ def dead(name, # check if any processes in this group are stopped is_stopped = False for proc in all_processes: - if proc.startswith(name) \ - and _is_stopped_state(all_processes[proc]['state']): + if proc.startswith(name) and _is_stopped_state( + all_processes[proc]["state"] + ): is_stopped = True break elif name in all_processes: - if _is_stopped_state(all_processes[name]['state']): + if _is_stopped_state(all_processes[name]["state"]): is_stopped = True else: is_stopped = False else: # process name doesn't exist - ret['comment'] = "Service {0} doesn't exist".format(name) + ret["comment"] = "Service {0} doesn't exist".format(name) return ret if is_stopped is True: - ret['comment'] = "Service {0} is not running".format(name) + ret["comment"] = "Service {0} is not running".format(name) else: - result = {name: __salt__['supervisord.stop']( - name, - user=user, - conf_file=conf_file, - bin_env=bin_env - )} + result = { + name: __salt__["supervisord.stop"]( + name, user=user, conf_file=conf_file, bin_env=bin_env + ) + } ret.update(_check_error(result, comment)) - ret['changes'][name] = comment + ret["changes"][name] = comment log.debug(six.text_type(result)) return ret -def mod_watch(name, - restart=True, - update=False, - user=None, - conf_file=None, - bin_env=None, - **kwargs): - ''' +def mod_watch( + name, restart=True, update=False, user=None, conf_file=None, bin_env=None, **kwargs +): + """ The supervisord watcher, called to invoke the watch command. Always restart on watch @@ -371,12 +337,12 @@ def mod_watch(name, :ref:`requisite <requisites>`. It should not be called directly. Parameters for this function should be set by the state being triggered. - ''' + """ return running( name, restart=restart, update=update, user=user, conf_file=conf_file, - bin_env=bin_env + bin_env=bin_env, ) diff --git a/salt/states/svn.py b/salt/states/svn.py index 604bfcd4ea7..0f118829831 100644 --- a/salt/states/svn.py +++ b/salt/states/svn.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage SVN repositories ======================= @@ -16,8 +16,8 @@ requisite to a pkg.installed state for the package which provides subversion http://unladen-swallow.googlecode.com/svn/trunk/: svn.latest: - target: /tmp/swallow -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging @@ -25,32 +25,36 @@ import os # Import salt libs from salt import exceptions -from salt.states.git import _fail, _neutral_test # Import 3rd party libs from salt.ext import six +from salt.states.git import _fail, _neutral_test log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if svn is available - ''' - return __salt__['cmd.has_exec']('svn') + """ + if __salt__["cmd.has_exec"]("svn"): + return True + return (False, "Command not found: svn") -def latest(name, - target=None, - rev=None, - user=None, - username=None, - password=None, - force=False, - externals=True, - trust=False, - trust_failures=None): - ''' +def latest( + name, + target=None, + rev=None, + user=None, + username=None, + password=None, + force=False, + externals=True, + trust=False, + trust_failures=None, +): + """ Checkout or update the working directory to the latest revision from the remote repository. @@ -93,112 +97,124 @@ def latest(name, option as-is. .. versionadded:: 2019.2.0 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not target: - return _fail(ret, 'Target option is required') + return _fail(ret, "Target option is required") - svn_cmd = 'svn.checkout' + svn_cmd = "svn.checkout" cwd, basename = os.path.split(target) opts = tuple() if os.path.exists(target) and not os.path.isdir(target): - return _fail(ret, - 'The path "{0}" exists and is not ' - 'a directory.'.format(target) - ) + return _fail( + ret, 'The path "{0}" exists and is not ' "a directory.".format(target) + ) - if __opts__['test']: + if __opts__["test"]: if rev: new_rev = six.text_type(rev) else: - new_rev = 'HEAD' + new_rev = "HEAD" if not os.path.exists(target): return _neutral_test( - ret, - ('{0} doesn\'t exist and is set to be checked out at revision ' + new_rev + '.').format(target)) + ret, + ( + "{0} doesn't exist and is set to be checked out at revision " + + new_rev + + "." + ).format(target), + ) try: - current_info = __salt__['svn.info'](cwd, target, user=user, username=username, password=password, fmt='dict') - svn_cmd = 'svn.diff' + current_info = __salt__["svn.info"]( + cwd, target, user=user, username=username, password=password, fmt="dict" + ) + svn_cmd = "svn.diff" except exceptions.CommandExecutionError: return _fail( - ret, - ('{0} exists but is not a svn working copy.').format(target)) + ret, ("{0} exists but is not a svn working copy.").format(target) + ) - current_rev = current_info[0]['Revision'] + current_rev = current_info[0]["Revision"] - opts += ('-r', current_rev + ':' + new_rev) + opts += ("-r", current_rev + ":" + new_rev) if trust: - opts += ('--trust-server-cert',) + opts += ("--trust-server-cert",) out = __salt__[svn_cmd](cwd, target, user, username, password, *opts) - return _neutral_test( - ret, - ('{0}').format(out)) + return _neutral_test(ret, ("{0}").format(out)) try: - current_info = __salt__['svn.info'](cwd, target, user=user, username=username, password=password, fmt='dict') - svn_cmd = 'svn.update' + current_info = __salt__["svn.info"]( + cwd, target, user=user, username=username, password=password, fmt="dict" + ) + svn_cmd = "svn.update" except exceptions.CommandExecutionError: pass if rev: - opts += ('-r', six.text_type(rev)) + opts += ("-r", six.text_type(rev)) if force: - opts += ('--force',) + opts += ("--force",) if externals is False: - opts += ('--ignore-externals',) + opts += ("--ignore-externals",) if trust: - opts += ('--trust-server-cert',) + opts += ("--trust-server-cert",) if trust_failures: - opts += ('--trust-server-cert-failures', trust_failures) + opts += ("--trust-server-cert-failures", trust_failures) - if svn_cmd == 'svn.update': + if svn_cmd == "svn.update": out = __salt__[svn_cmd](cwd, basename, user, username, password, *opts) - current_rev = current_info[0]['Revision'] - new_rev = __salt__['svn.info'](cwd=target, - targets=None, - user=user, - username=username, - password=password, - fmt='dict')[0]['Revision'] + current_rev = current_info[0]["Revision"] + new_rev = __salt__["svn.info"]( + cwd=target, + targets=None, + user=user, + username=username, + password=password, + fmt="dict", + )[0]["Revision"] if current_rev != new_rev: - ret['changes']['revision'] = "{0} => {1}".format(current_rev, new_rev) + ret["changes"]["revision"] = "{0} => {1}".format(current_rev, new_rev) else: out = __salt__[svn_cmd](cwd, name, basename, user, username, password, *opts) - ret['changes']['new'] = name - ret['changes']['revision'] = __salt__['svn.info'](cwd=target, - targets=None, - user=user, - username=username, - password=password, - fmt='dict')[0]['Revision'] + ret["changes"]["new"] = name + ret["changes"]["revision"] = __salt__["svn.info"]( + cwd=target, + targets=None, + user=user, + username=username, + password=password, + fmt="dict", + )[0]["Revision"] - ret['comment'] = out + ret["comment"] = out return ret -def export(name, - target=None, - rev=None, - user=None, - username=None, - password=None, - force=False, - overwrite=False, - externals=True, - trust=False, - trust_failures=None): - ''' +def export( + name, + target=None, + rev=None, + user=None, + username=None, + password=None, + force=False, + overwrite=False, + externals=True, + trust=False, + trust_failures=None, +): + """ Export a file or directory from an SVN repository name @@ -243,62 +259,56 @@ def export(name, option as-is. .. versionadded:: 2019.2.0 - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} if not target: - return _fail(ret, 'Target option is required') + return _fail(ret, "Target option is required") - svn_cmd = 'svn.export' + svn_cmd = "svn.export" cwd, basename = os.path.split(target) opts = tuple() if not overwrite and os.path.exists(target) and not os.path.isdir(target): - return _fail(ret, - 'The path "{0}" exists and is not ' - 'a directory.'.format(target) - ) - if __opts__['test']: + return _fail( + ret, 'The path "{0}" exists and is not ' "a directory.".format(target) + ) + if __opts__["test"]: if not os.path.exists(target): return _neutral_test( - ret, - ('{0} doesn\'t exist and is set to be checked out.').format(target)) - svn_cmd = 'svn.list' - rev = 'HEAD' + ret, ("{0} doesn't exist and is set to be checked out.").format(target) + ) + svn_cmd = "svn.list" + rev = "HEAD" out = __salt__[svn_cmd](cwd, target, user, username, password, *opts) - return _neutral_test( - ret, - ('{0}').format(out)) + return _neutral_test(ret, ("{0}").format(out)) if not rev: - rev = 'HEAD' + rev = "HEAD" if force: - opts += ('--force',) + opts += ("--force",) if externals is False: - opts += ('--ignore-externals',) + opts += ("--ignore-externals",) if trust: - opts += ('--trust-server-cert',) + opts += ("--trust-server-cert",) if trust_failures: - opts += ('--trust-server-cert-failures', trust_failures) + opts += ("--trust-server-cert-failures", trust_failures) out = __salt__[svn_cmd](cwd, name, basename, user, username, password, rev, *opts) - ret['changes']['new'] = name - ret['changes']['comment'] = name + ' was Exported to ' + target + ret["changes"]["new"] = name + ret["changes"]["comment"] = name + " was Exported to " + target return ret -def dirty(name, - target, - user=None, - username=None, - password=None, - ignore_unversioned=False): - ''' +def dirty( + name, target, user=None, username=None, password=None, ignore_unversioned=False +): + """ Determine if the working directory has been changed. - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} - return _fail(ret, 'This function is not implemented yet.') + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} + return _fail(ret, "This function is not implemented yet.") diff --git a/salt/states/sysctl.py b/salt/states/sysctl.py index 905e149deef..bae08afeab8 100644 --- a/salt/states/sysctl.py +++ b/salt/states/sysctl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configuration of the kernel using sysctl ======================================== @@ -10,8 +10,8 @@ Control the kernel sysctl system. vm.swappiness: sysctl.present: - value: 20 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import re @@ -24,14 +24,16 @@ from salt.ext import six def __virtual__(): - ''' + """ This state is only available on Minions which support sysctl - ''' - return 'sysctl.show' in __salt__ + """ + if "sysctl.show" in __salt__: + return True + return (False, "sysctl module could not be loaded") -def present(name, value, config=None): - ''' +def present(name, value, config=None, ignore=False): + """ Ensure that the named sysctl value is set in memory and persisted to the named configuration file. The default sysctl configuration file is /etc/sysctl.conf @@ -45,87 +47,85 @@ def present(name, value, config=None): config The location of the sysctl configuration file. If not specified, the proper location will be detected based on platform. - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + + ignore + ..versionadded:: neon + + Adds --ignore to sysctl commands. This suppresses errors in environments + where sysctl settings may have been disabled in kernel boot configuration. + Defaults to False + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} if config is None: # Certain linux systems will ignore /etc/sysctl.conf, get the right # default configuration file. - if 'sysctl.default_config' in __salt__: - config = __salt__['sysctl.default_config']() + if "sysctl.default_config" in __salt__: + config = __salt__["sysctl.default_config"]() else: - config = '/etc/sysctl.conf' + config = "/etc/sysctl.conf" - if __opts__['test']: - current = __salt__['sysctl.show']() - configured = __salt__['sysctl.show'](config_file=config) + if __opts__["test"]: + current = __salt__["sysctl.show"]() + configured = __salt__["sysctl.show"](config_file=config) if configured is None: - ret['result'] = None - ret['comment'] = ( - 'Sysctl option {0} might be changed, we failed to check ' - 'config file at {1}. The file is either unreadable, or ' - 'missing.'.format(name, config) + ret["result"] = None + ret["comment"] = ( + "Sysctl option {0} might be changed, we failed to check " + "config file at {1}. The file is either unreadable, or " + "missing.".format(name, config) ) return ret if name in current and name not in configured: - if re.sub(' +|\t+', ' ', current[name]) != \ - re.sub(' +|\t+', ' ', six.text_type(value)): - ret['result'] = None - ret['comment'] = ( - 'Sysctl option {0} set to be changed to {1}' - .format(name, value) + if re.sub(" +|\t+", " ", current[name]) != re.sub( + " +|\t+", " ", six.text_type(value) + ): + ret["result"] = None + ret["comment"] = "Sysctl option {0} set to be changed to {1}".format( + name, value ) return ret else: - ret['result'] = None - ret['comment'] = ( - 'Sysctl value is currently set on the running system but ' - 'not in a config file. Sysctl option {0} set to be ' - 'changed to {1} in config file.'.format(name, value) + ret["result"] = None + ret["comment"] = ( + "Sysctl value is currently set on the running system but " + "not in a config file. Sysctl option {0} set to be " + "changed to {1} in config file.".format(name, value) ) return ret elif name in configured and name not in current: - ret['result'] = None - ret['comment'] = ( - 'Sysctl value {0} is present in configuration file but is not ' - 'present in the running config. The value {0} is set to be ' - 'changed to {1}'.format(name, value) + ret["result"] = None + ret["comment"] = ( + "Sysctl value {0} is present in configuration file but is not " + "present in the running config. The value {0} is set to be " + "changed to {1}".format(name, value) ) return ret elif name in configured and name in current: - if six.text_type(value).split() == __salt__['sysctl.get'](name).split(): - ret['result'] = True - ret['comment'] = ( - 'Sysctl value {0} = {1} is already set' - .format(name, value) + if six.text_type(value).split() == __salt__["sysctl.get"](name).split(): + ret["result"] = True + ret["comment"] = "Sysctl value {0} = {1} is already set".format( + name, value ) return ret # otherwise, we don't have it set anywhere and need to set it - ret['result'] = None - ret['comment'] = ( - 'Sysctl option {0} would be changed to {1}'.format(name, value) - ) + ret["result"] = None + ret["comment"] = "Sysctl option {0} would be changed to {1}".format(name, value) return ret try: - update = __salt__['sysctl.persist'](name, value, config) + update = __salt__["sysctl.persist"](name, value, config, ignore) except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = ( - 'Failed to set {0} to {1}: {2}'.format(name, value, exc) - ) + ret["result"] = False + ret["comment"] = "Failed to set {0} to {1}: {2}".format(name, value, exc) return ret - if update == 'Updated': - ret['changes'] = {name: value} - ret['comment'] = 'Updated sysctl value {0} = {1}'.format(name, value) - elif update == 'Already set': - ret['comment'] = ( - 'Sysctl value {0} = {1} is already set' - .format(name, value) - ) + if update == "Updated": + ret["changes"] = {name: value} + ret["comment"] = "Updated sysctl value {0} = {1}".format(name, value) + elif update == "Already set": + ret["comment"] = "Sysctl value {0} = {1} is already set".format(name, value) + elif update == "Ignored": + ret["comment"] = "Sysctl value {0} = {1} was ignored".format(name, value) return ret diff --git a/salt/states/syslog_ng.py b/salt/states/syslog_ng.py index 600a42df911..b8fdaf875fb 100644 --- a/salt/states/syslog_ng.py +++ b/salt/states/syslog_ng.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" State module for syslog_ng ========================== @@ -43,80 +43,87 @@ These constructions are also called statements. There are options inside of them You can find more information about syslog-ng's configuration syntax in the Syslog-ng Admin guide: http://www.balabit.com/sites/default/files/documents/syslog-ng-ose-3.5-guides/en/syslog-ng-ose-v3.5-guide-admin/html-single/index.html#syslog-ng.conf.5 -''' -from __future__ import absolute_import, unicode_literals, print_function, \ - generators, with_statement -import logging +""" +from __future__ import ( + absolute_import, + generators, + print_function, + unicode_literals, + with_statement, +) +import logging log = logging.getLogger(__name__) -def config(name, - config, - write=True): - ''' +def config(name, config, write=True): + """ Builds syslog-ng configuration. name : the id of the Salt document config : the parsed YAML code write : if True, it writes the config into the configuration file, otherwise just returns it - ''' - return __salt__['syslog_ng.config'](name, config, write) + """ + return __salt__["syslog_ng.config"](name, config, write) def stopped(name=None): - ''' + """ Kills syslog-ng. - ''' - return __salt__['syslog_ng.stop'](name) + """ + return __salt__["syslog_ng.stop"](name) -def started(name=None, - user=None, - group=None, - chroot=None, - caps=None, - no_caps=False, - pidfile=None, - enable_core=False, - fd_limit=None, - verbose=False, - debug=False, - trace=False, - yydebug=False, - persist_file=None, - control=None, - worker_threads=None, - *args, - **kwargs): - ''' +def started( + name=None, + user=None, + group=None, + chroot=None, + caps=None, + no_caps=False, + pidfile=None, + enable_core=False, + fd_limit=None, + verbose=False, + debug=False, + trace=False, + yydebug=False, + persist_file=None, + control=None, + worker_threads=None, + *args, + **kwargs +): + """ Ensures, that syslog-ng is started via the given parameters. Users shouldn't use this function, if the service module is available on their system. - ''' - return __salt__['syslog_ng.start'](name=name, - user=user, - group=group, - chroot=chroot, - caps=caps, - no_caps=no_caps, - pidfile=pidfile, - enable_core=enable_core, - fd_limit=fd_limit, - verbose=verbose, - debug=debug, - trace=trace, - yydebug=yydebug, - persist_file=persist_file, - control=control, - worker_threads=worker_threads) + """ + return __salt__["syslog_ng.start"]( + name=name, + user=user, + group=group, + chroot=chroot, + caps=caps, + no_caps=no_caps, + pidfile=pidfile, + enable_core=enable_core, + fd_limit=fd_limit, + verbose=verbose, + debug=debug, + trace=trace, + yydebug=yydebug, + persist_file=persist_file, + control=control, + worker_threads=worker_threads, + ) def reloaded(name): - ''' + """ Reloads syslog-ng. - ''' - return __salt__['syslog_ng.reload'](name) + """ + return __salt__["syslog_ng.reload"](name) diff --git a/salt/states/sysrc.py b/salt/states/sysrc.py index 8ca1233377d..852046ac337 100644 --- a/salt/states/sysrc.py +++ b/salt/states/sysrc.py @@ -1,28 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" State to work with sysrc -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import 3rd-party libs from salt.ext import six # define the module's virtual name -__virtualname__ = 'sysrc' +__virtualname__ = "sysrc" def __virtual__(): - ''' + """ Only load if sysrc executable exists - ''' - return __salt__['cmd.has_exec']('sysrc') + """ + if __salt__["cmd.has_exec"]("sysrc"): + return True + return (False, "Command not found: sysrc") def managed(name, value, **kwargs): - ''' + """ Ensure a sysrc variable is set to a specific value. name @@ -42,47 +44,44 @@ def managed(name, value, **kwargs): sysrc.managed: - name: syslogd_flags - value: -ss - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Check the current state - current_state = __salt__['sysrc.get'](name=name, **kwargs) + current_state = __salt__["sysrc.get"](name=name, **kwargs) if current_state is not None: for rcname, rcdict in six.iteritems(current_state): if rcdict[name] == value: - ret['result'] = True - ret['comment'] = '{0} is already set to the desired value.'.format(name) + ret["result"] = True + ret["comment"] = "{0} is already set to the desired value.".format(name) return ret - if __opts__['test'] is True: - ret['comment'] = 'The value of "{0}" will be changed!'.format(name) - ret['changes'] = { - 'old': current_state, - 'new': name+' = '+value+' will be set.' + if __opts__["test"] is True: + ret["comment"] = 'The value of "{0}" will be changed!'.format(name) + ret["changes"] = { + "old": current_state, + "new": name + " = " + value + " will be set.", } # When test=true return none - ret['result'] = None + ret["result"] = None return ret - new_state = __salt__['sysrc.set'](name=name, value=value, **kwargs) + new_state = __salt__["sysrc.set"](name=name, value=value, **kwargs) - ret['comment'] = 'The value of "{0}" was changed!'.format(name) + ret["comment"] = 'The value of "{0}" was changed!'.format(name) - ret['changes'] = { - 'old': current_state, - 'new': new_state - } + ret["changes"] = {"old": current_state, "new": new_state} - ret['result'] = True + ret["result"] = True return ret def absent(name, **kwargs): - ''' + """ Ensure a sysrc variable is absent. name @@ -91,38 +90,35 @@ def absent(name, **kwargs): (optional) The rc file to add the variable to. jail (option) the name or JID of the jail to set the value in. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Check the current state - current_state = __salt__['sysrc.get'](name=name, **kwargs) + current_state = __salt__["sysrc.get"](name=name, **kwargs) if current_state is None: - ret['result'] = True - ret['comment'] = '"{0}" is already absent.'.format(name) + ret["result"] = True + ret["comment"] = '"{0}" is already absent.'.format(name) return ret - if __opts__['test'] is True: - ret['comment'] = '"{0}" will be removed!'.format(name) - ret['changes'] = { - 'old': current_state, - 'new': '"{0}" will be removed.'.format(name) + if __opts__["test"] is True: + ret["comment"] = '"{0}" will be removed!'.format(name) + ret["changes"] = { + "old": current_state, + "new": '"{0}" will be removed.'.format(name), } # When test=true return none - ret['result'] = None + ret["result"] = None return ret - new_state = __salt__['sysrc.remove'](name=name, **kwargs) + new_state = __salt__["sysrc.remove"](name=name, **kwargs) - ret['comment'] = '"{0}" was removed!'.format(name) + ret["comment"] = '"{0}" was removed!'.format(name) - ret['changes'] = { - 'old': current_state, - 'new': new_state - } + ret["changes"] = {"old": current_state, "new": new_state} - ret['result'] = True + ret["result"] = True return ret diff --git a/salt/states/telemetry_alert.py b/salt/states/telemetry_alert.py index d1152effea9..ba27ad7c9d2 100644 --- a/salt/states/telemetry_alert.py +++ b/salt/states/telemetry_alert.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Telemetry alert configurations ===================================== @@ -24,8 +24,9 @@ Example: filter: SERVER_ROLE_MONGOD_PRIMARY escalate_to: "example@pagerduty.com" - name: "**MANAGED BY ORCA DO NOT EDIT BY HAND** manages alarm on testMetric" -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + from salt._compat import string_types # import 3rd party libs @@ -34,11 +35,15 @@ from salt.ext import six def __virtual__(): # Only load if telemetry is available. - return 'telemetry_alert' if 'telemetry.get_alert_config' in __salt__ else False + if "telemetry.get_alert_config" in __salt__: + return "telemetry_alert" + return (False, "telemetry module could not be loaded") -def present(name, deployment_id, metric_name, alert_config, api_key=None, profile='telemetry'): - ''' +def present( + name, deployment_id, metric_name, alert_config, api_key=None, profile="telemetry" +): + """ Ensure the telemetry alert exists. name @@ -72,27 +77,30 @@ def present(name, deployment_id, metric_name, alert_config, api_key=None, profil A dict of telemetry config information. If present, will be used instead of api_key. - ''' + """ - ret = {'name': metric_name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": metric_name, "result": True, "comment": "", "changes": {}} - saved_alert_config = __salt__['telemetry.get_alert_config']( - deployment_id, metric_name, api_key, profile) + saved_alert_config = __salt__["telemetry.get_alert_config"]( + deployment_id, metric_name, api_key, profile + ) post_body = { "deployment": deployment_id, - "filter": alert_config.get('filter'), - "notificationChannel": __salt__['telemetry.get_notification_channel_id'](alert_config.get('escalate_to')).split(), + "filter": alert_config.get("filter"), + "notificationChannel": __salt__["telemetry.get_notification_channel_id"]( + alert_config.get("escalate_to") + ).split(), "condition": { - "metric": metric_name, - "max": alert_config.get('max'), - "min": alert_config.get('min') - } + "metric": metric_name, + "max": alert_config.get("max"), + "min": alert_config.get("min"), + }, } # Diff the alert config with the passed-in attributes difference = [] if saved_alert_config: - #del saved_alert_config["_id"] + # del saved_alert_config["_id"] for k, v in post_body.items(): if k not in saved_alert_config: difference.append("{0}={1} (new)".format(k, v)) @@ -118,45 +126,49 @@ def present(name, deployment_id, metric_name, alert_config, api_key=None, profil api_key, profile, ) - if saved_alert_config: # alert config is present. update, or do nothing + if saved_alert_config: # alert config is present. update, or do nothing # check to see if attributes matches is_present. If so, do nothing. if len(difference) == 0: - ret['comment'] = "alert config {0} present and matching".format(metric_name) + ret["comment"] = "alert config {0} present and matching".format(metric_name) return ret - if __opts__['test']: - msg = 'alert config {0} is to be updated.'.format(metric_name) - ret['comment'] = msg - ret['result'] = "\n".join(difference) + if __opts__["test"]: + msg = "alert config {0} is to be updated.".format(metric_name) + ret["comment"] = msg + ret["result"] = "\n".join(difference) return ret - result, msg = __salt__['telemetry.update_alarm'](*create_or_update_args) + result, msg = __salt__["telemetry.update_alarm"](*create_or_update_args) if result: - ret['changes']['diff'] = difference - ret['comment'] = "Alert updated." + ret["changes"]["diff"] = difference + ret["comment"] = "Alert updated." else: - ret['result'] = False - ret['comment'] = 'Failed to update {0} alert config: {1}'.format(metric_name, msg) + ret["result"] = False + ret["comment"] = "Failed to update {0} alert config: {1}".format( + metric_name, msg + ) else: # alert config is absent. create it. - if __opts__['test']: - msg = 'alert config {0} is to be created.'.format(metric_name) - ret['comment'] = msg - ret['result'] = None + if __opts__["test"]: + msg = "alert config {0} is to be created.".format(metric_name) + ret["comment"] = msg + ret["result"] = None return ret - result, msg = __salt__['telemetry.create_alarm'](*create_or_update_args) + result, msg = __salt__["telemetry.create_alarm"](*create_or_update_args) if result: - ret['changes']['new'] = msg + ret["changes"]["new"] = msg else: - ret['result'] = False - ret['comment'] = 'Failed to create {0} alert config: {1}'.format(metric_name, msg) + ret["result"] = False + ret["comment"] = "Failed to create {0} alert config: {1}".format( + metric_name, msg + ) return ret def absent(name, deployment_id, metric_name, api_key=None, profile="telemetry"): - ''' + """ Ensure the telemetry alert config is deleted name @@ -175,27 +187,41 @@ def absent(name, deployment_id, metric_name, api_key=None, profile="telemetry"): profile A dict with telemetry config data. If present, will be used instead of api_key. - ''' - ret = {'name': metric_name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": metric_name, "result": True, "comment": "", "changes": {}} - is_present = __salt__['telemetry.get_alert_config']( - deployment_id, metric_name, api_key, profile) + is_present = __salt__["telemetry.get_alert_config"]( + deployment_id, metric_name, api_key, profile + ) if is_present: - alert_id = is_present.get('_id') - if __opts__['test']: - ret['comment'] = 'alert {0} is set to be removed from deployment: {1}.'.format(metric_name, deployment_id) - ret['result'] = None + alert_id = is_present.get("_id") + if __opts__["test"]: + ret[ + "comment" + ] = "alert {0} is set to be removed from deployment: {1}.".format( + metric_name, deployment_id + ) + ret["result"] = None return ret - deleted, msg = __salt__['telemetry.delete_alarms']( - deployment_id, alert_id, is_present.get('condition', {}).get('metric'), api_key, profile) + deleted, msg = __salt__["telemetry.delete_alarms"]( + deployment_id, + alert_id, + is_present.get("condition", {}).get("metric"), + api_key, + profile, + ) if deleted: - ret['changes']['old'] = metric_name - ret['changes']['new'] = None + ret["changes"]["old"] = metric_name + ret["changes"]["new"] = None else: - ret['result'] = False - ret['comment'] = 'Failed to delete alert {0} from deployment: {1}'.format(metric_name, msg) + ret["result"] = False + ret["comment"] = "Failed to delete alert {0} from deployment: {1}".format( + metric_name, msg + ) else: - ret['comment'] = 'alarm on {0} does not exist within {1}.'.format(metric_name, deployment_id) + ret["comment"] = "alarm on {0} does not exist within {1}.".format( + metric_name, deployment_id + ) return ret diff --git a/salt/states/test.py b/salt/states/test.py index 4df32531961..b3d138d72ec 100644 --- a/salt/states/test.py +++ b/salt/states/test.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Test States =========== @@ -44,7 +44,7 @@ calls, e.g. running, calling, logging, output filtering etc. - foo - integer: - bar -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -52,131 +52,113 @@ import random # Import Salt libs import salt.utils.data -from salt.state import _gen_tag -from salt.ext import six from salt.exceptions import SaltInvocationError +from salt.ext import six +from salt.state import _gen_tag def nop(name, **kwargs): - ''' + """ A no-op state that does nothing. Useful in conjunction with the `use` requisite, or in templates which could otherwise be empty due to jinja rendering .. versionadded:: 2015.8.1 - ''' + """ return succeed_without_changes(name) def succeed_without_changes(name, **kwargs): # pylint: disable=unused-argument - ''' + """ Returns successful. .. versionadded:: 2014.7.0 name A unique string. - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Success!' - } + """ + comment = kwargs.get("comment", "Success!") + + ret = {"name": name, "changes": {}, "result": True, "comment": comment} return ret def fail_without_changes(name, **kwargs): # pylint: disable=unused-argument - ''' + """ Returns failure. .. versionadded:: 2014.7.0 name: A unique string. - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Failure!' - } + """ + comment = kwargs.get("comment", "Failure!") - if __opts__['test']: - ret['result'] = False - ret['comment'] = 'If we weren\'t testing, this would be a failure!' + ret = {"name": name, "changes": {}, "result": False, "comment": comment} + + if __opts__["test"]: + ret["result"] = False + ret["comment"] = "If we weren't testing, this would be a failure!" return ret def succeed_with_changes(name, **kwargs): # pylint: disable=unused-argument - ''' + """ Returns successful and changes is not empty .. versionadded:: 2014.7.0 name: A unique string. - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Success!' - } + """ + comment = kwargs.get("comment", "Success!") + + ret = {"name": name, "changes": {}, "result": True, "comment": comment} # Following the docs as written here # http://docs.saltstack.com/ref/states/writing.html#return-data - ret['changes'] = { - 'testing': { - 'old': 'Unchanged', - 'new': 'Something pretended to change' - } + ret["changes"] = { + "testing": {"old": "Unchanged", "new": "Something pretended to change"} } - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('If we weren\'t testing, this would be successful ' - 'with changes') + if __opts__["test"]: + ret["result"] = None + ret["comment"] = ( + "If we weren't testing, this would be successful " "with changes" + ) return ret def fail_with_changes(name, **kwargs): # pylint: disable=unused-argument - ''' + """ Returns failure and changes is not empty. .. versionadded:: 2014.7.0 name: A unique string. - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Failure!' - } + """ + comment = kwargs.get("comment", "Failure!") + + ret = {"name": name, "changes": {}, "result": False, "comment": "Failure!"} # Following the docs as written here # http://docs.saltstack.com/ref/states/writing.html#return-data - ret['changes'] = { - 'testing': { - 'old': 'Unchanged', - 'new': 'Something pretended to change' - } + ret["changes"] = { + "testing": {"old": "Unchanged", "new": "Something pretended to change"} } - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('If we weren\'t testing, this would be failed with ' - 'changes') + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "If we weren't testing, this would be failed with " "changes" return ret -def configurable_test_state(name, changes=True, result=True, comment='', warnings=None): - ''' +def configurable_test_state(name, changes=True, result=True, comment="", warnings=None): + """ A configurable test state which determines its output based on the inputs. .. versionadded:: 2014.7.0 @@ -202,71 +184,69 @@ def configurable_test_state(name, changes=True, result=True, comment='', warning warnings: A string (or a list of strings) to fill the warnings field with. Default is None - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': comment - } + """ + ret = {"name": name, "changes": {}, "result": False, "comment": comment} change_data = { - 'testing': { - 'old': 'Unchanged', - 'new': 'Something pretended to change' - } + "testing": {"old": "Unchanged", "new": "Something pretended to change"} } - if changes == 'Random': + if changes == "Random": if random.choice([True, False]): # Following the docs as written here # http://docs.saltstack.com/ref/states/writing.html#return-data - ret['changes'] = change_data + ret["changes"] = change_data elif changes is True: # If changes is True we place our dummy change dictionary into it. # Following the docs as written here # http://docs.saltstack.com/ref/states/writing.html#return-data - ret['changes'] = change_data + ret["changes"] = change_data elif changes is False: - ret['changes'] = {} + ret["changes"] = {} else: - err = ('You have specified the state option \'Changes\' with' - ' invalid arguments. It must be either ' - ' \'True\', \'False\', or \'Random\'') + err = ( + "You have specified the state option 'Changes' with" + " invalid arguments. It must be either " + " 'True', 'False', or 'Random'" + ) raise SaltInvocationError(err) - if result == 'Random': + if result == "Random": # since result is a boolean, if its random we just set it here, - ret['result'] = random.choice([True, False]) + ret["result"] = random.choice([True, False]) elif result is True: - ret['result'] = True + ret["result"] = True elif result is False: - ret['result'] = False + ret["result"] = False else: - raise SaltInvocationError('You have specified the state option ' - '\'Result\' with invalid arguments. It must ' - 'be either \'True\', \'False\', or ' - '\'Random\'') + raise SaltInvocationError( + "You have specified the state option " + "'Result' with invalid arguments. It must " + "be either 'True', 'False', or " + "'Random'" + ) if warnings is None: pass elif isinstance(warnings, six.string_types): - ret['warnings'] = [warnings] + ret["warnings"] = [warnings] elif isinstance(warnings, list): - ret['warnings'] = warnings + ret["warnings"] = warnings else: - raise SaltInvocationError('You have specified the state option ' - '\'Warnings\' with invalid arguments. It must ' - 'be a string or a list of strings') + raise SaltInvocationError( + "You have specified the state option " + "'Warnings' with invalid arguments. It must " + "be a string or a list of strings" + ) - if __opts__['test']: - ret['result'] = True if changes is False else None - ret['comment'] = 'This is a test' if not comment else comment + if __opts__["test"]: + ret["result"] = True if changes is False else None + ret["comment"] = "This is a test" if not comment else comment return ret def show_notification(name, text=None, **kwargs): - ''' + """ Simple notification using text argument. .. versionadded:: 2015.8.0 @@ -276,23 +256,18 @@ def show_notification(name, text=None, **kwargs): text Text to return in the comment. - ''' + """ if not text: - raise SaltInvocationError('Missing required argument text.') + raise SaltInvocationError("Missing required argument text.") - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': text - } + ret = {"name": name, "changes": {}, "result": True, "comment": text} return ret def mod_watch(name, sfun=None, **kwargs): - ''' + """ Call this function via a watch statement .. versionadded:: 2014.7.0 @@ -319,27 +294,25 @@ def mod_watch(name, sfun=None, **kwargs): test.succeed_without_changes: - watch: - test: this_state_will_NOT_return_changes - ''' + """ has_changes = [] - if '__reqs__' in __low__: - for req in __low__['__reqs__']['watch']: + if "__reqs__" in __low__: + for req in __low__["__reqs__"]["watch"]: tag = _gen_tag(req) - if __running__[tag]['changes']: - has_changes.append('{state}: {__id__}'.format(**req)) + if __running__[tag]["changes"]: + has_changes.append("{state}: {__id__}".format(**req)) ret = { - 'name': name, - 'result': kwargs.get('result', True), - 'comment': kwargs.get('comment', 'Watch statement fired.'), - 'changes': kwargs.get('changes', { - 'Requisites with changes': has_changes, - }), + "name": name, + "result": kwargs.get("result", True), + "comment": kwargs.get("comment", "Watch statement fired."), + "changes": kwargs.get("changes", {"Requisites with changes": has_changes}), } return ret def _check_key_type(key_str, key_type=None): - ''' + """ Helper function to get pillar[key_str] and check if its type is key_type @@ -348,8 +321,8 @@ def _check_key_type(key_str, key_type=None): of the values type. Can't check for None. - ''' - value = __salt__['pillar.get'](key_str, None) + """ + value = __salt__["pillar.get"](key_str, None) if value is None: return None elif key_type is not None and not isinstance(value, key_type): @@ -359,11 +332,11 @@ def _check_key_type(key_str, key_type=None): def _if_str_then_list(listing): - ''' + """ Checks if its argument is a list or a str. A str will be turned into a list with the str as its only element. - ''' + """ if isinstance(listing, six.string_types): return [salt.utils.stringutils.to_unicode(listing)] elif not isinstance(listing, list): @@ -371,15 +344,17 @@ def _if_str_then_list(listing): return salt.utils.data.decode_list(listing) -def check_pillar(name, - present=None, - boolean=None, - integer=None, - string=None, - listing=None, - dictionary=None, - verbose=False): - ''' +def check_pillar( + name, + present=None, + boolean=None, + integer=None, + string=None, + listing=None, + dictionary=None, + verbose=False, +): + """ Checks the presence and, optionally, the type of given keys in Pillar. Supported kwargs for types are: @@ -399,10 +374,9 @@ def check_pillar(name, - foo - integer: - bar - ''' - if not (present or boolean or integer or string or - listing or dictionary): - raise SaltInvocationError('Missing required argument text.') + """ + if not (present or boolean or integer or string or listing or dictionary): + raise SaltInvocationError("Missing required argument text.") present = present or [] boolean = boolean or [] @@ -411,12 +385,7 @@ def check_pillar(name, listing = listing or [] dictionary = dictionary or [] - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + ret = {"name": name, "changes": {}, "result": True, "comment": ""} checks = {} fine = {} failed = {} @@ -446,25 +415,25 @@ def check_pillar(name, result = _check_key_type(key, key_type) if result is None: failed[key] = None - ret['result'] = False + ret["result"] = False elif not result: failed[key] = key_type - ret['result'] = False + ret["result"] = False elif verbose: fine[key] = key_type for key, key_type in failed.items(): comment = 'Pillar key "{0}" '.format(key) if key_type is None: - comment += 'is missing.\n' + comment += "is missing.\n" else: - comment += 'is not {0}.\n'.format(key_type) - ret['comment'] += comment + comment += "is not {0}.\n".format(key_type) + ret["comment"] += comment if verbose and fine: - comment = 'Those keys passed the check:\n' + comment = "Those keys passed the check:\n" for key, key_type in fine.items(): - comment += '- {0} ({1})\n'.format(key, key_type) - ret['comment'] += comment + comment += "- {0} ({1})\n".format(key, key_type) + ret["comment"] += comment return ret diff --git a/salt/states/testinframod.py b/salt/states/testinframod.py index 51d2a209872..b04cc6896d5 100644 --- a/salt/states/testinframod.py +++ b/salt/states/testinframod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import logging @@ -9,31 +9,30 @@ log = logging.getLogger(__name__) try: from testinfra import modules + TESTINFRA_PRESENT = True except ImportError: TESTINFRA_PRESENT = False __all__ = [] -__virtualname__ = 'testinfra' +__virtualname__ = "testinfra" def __virtual__(): if TESTINFRA_PRESENT: return __virtualname__ - return False, 'The Testinfra package is not available' + return False, "The Testinfra package is not available" def _wrap_module_function(func_name): def _module_function_wrapper(name, **methods): func = __salt__[func_name] result, passes, fails = func(name, **methods) - comment = '\n'.join(passes + fails) - if __opts__['test'] and not result: + comment = "\n".join(passes + fails) + if __opts__["test"] and not result: result = None - return {'name': name, - 'comment': comment, - 'result': result, - 'changes': {}} + return {"name": name, "comment": comment, "result": result, "changes": {}} + return _module_function_wrapper @@ -44,10 +43,11 @@ def _generate_functions(): modules_ = [module_ for module_ in modules.modules] for module_name in modules_: - func_name = 'testinfra.{0}'.format(module_name) + func_name = "testinfra.{0}".format(module_name) __all__.append(module_name) - log.debug('Generating state for module %s as function %s', - module_name, func_name) + log.debug( + "Generating state for module %s as function %s", module_name, func_name + ) globals()[module_name] = _wrap_module_function(func_name) diff --git a/salt/states/timezone.py b/salt/states/timezone.py index 319534ce36a..ef8c01b3f15 100644 --- a/salt/states/timezone.py +++ b/salt/states/timezone.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of timezones ======================= @@ -26,22 +26,24 @@ will be set to UTC, and the system clock will be an offset of that. The Ubuntu community documentation contains an explanation of this setting, as it applies to systems that dual-boot with Windows. This is explained in greater detail here_. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import salt libs -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError def __virtual__(): - ''' + """ Only load if the timezone module is available in __salt__ - ''' - return 'timezone.get_zone' in __salt__ + """ + if "timezone.get_zone" in __salt__: + return True + return (False, "timezone module could not be loaded") def system(name, utc=True): - ''' + """ Set the timezone for the system. name @@ -49,79 +51,77 @@ def system(name, utc=True): utc Whether or not to set the hardware clock to UTC (default is True) - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # Set up metadata do_utc = False do_zone = False try: - compzone = __salt__['timezone.zone_compare'](name) + compzone = __salt__["timezone.zone_compare"](name) except (SaltInvocationError, CommandExecutionError) as exc: - ret['result'] = False - ret['comment'] = ( - 'Unable to compare desired timezone \'{0}\' to system timezone: {1}' - .format(name, exc) + ret["result"] = False + ret[ + "comment" + ] = "Unable to compare desired timezone '{0}' to system timezone: {1}".format( + name, exc ) return ret myutc = True messages = [] - if __salt__['timezone.get_hwclock']() == 'localtime': + if __salt__["timezone.get_hwclock"]() == "localtime": myutc = False # Check the time zone if compzone is True: - ret['result'] = True - messages.append('Timezone {0} already set'.format(name)) + ret["result"] = True + messages.append("Timezone {0} already set".format(name)) else: do_zone = True # If the user passed in utc, do a check if utc and utc != myutc: - ret['result'] = None + ret["result"] = None do_utc = True elif utc and utc == myutc: - messages.append('UTC already set to {0}'.format(name)) + messages.append("UTC already set to {0}".format(name)) - if ret['result'] is True: - ret['comment'] = ', '.join(messages) + if ret["result"] is True: + ret["comment"] = ", ".join(messages) return ret - if __opts__['test']: + if __opts__["test"]: messages = [] if compzone is False: - messages.append('Timezone {0} needs to be set'.format(name)) + messages.append("Timezone {0} needs to be set".format(name)) if utc and myutc != utc: - messages.append('UTC needs to be set to {0}'.format(utc)) - ret['comment'] = ', '.join(messages) + messages.append("UTC needs to be set to {0}".format(utc)) + ret["comment"] = ", ".join(messages) return ret messages = [] if do_zone: - if __salt__['timezone.set_zone'](name): - ret['changes']['timezone'] = name - messages.append('Set timezone {0}'.format(name)) - ret['result'] = True + if __salt__["timezone.set_zone"](name): + ret["changes"]["timezone"] = name + messages.append("Set timezone {0}".format(name)) + ret["result"] = True else: - messages.append('Failed to set timezone') - ret['result'] = False + messages.append("Failed to set timezone") + ret["result"] = False if do_utc: - clock = 'localtime' + clock = "localtime" if utc: - clock = 'UTC' - if __salt__['timezone.set_hwclock'](clock): - ret['changes']['utc'] = utc - messages.append('Set UTC to {0}'.format(utc)) - ret['result'] = True + clock = "UTC" + if __salt__["timezone.set_hwclock"](clock): + ret["changes"]["utc"] = utc + messages.append("Set UTC to {0}".format(utc)) + ret["result"] = True else: - messages.append('Failed to set UTC to {0}'.format(utc)) - ret['result'] = False + messages.append("Failed to set UTC to {0}".format(utc)) + ret["result"] = False - ret['comment'] = ', '.join(messages) + ret["comment"] = ", ".join(messages) return ret diff --git a/salt/states/tls.py b/salt/states/tls.py index aa8fb9cacf6..eb1426abcb2 100644 --- a/salt/states/tls.py +++ b/salt/states/tls.py @@ -1,75 +1,72 @@ # -*- coding: utf-8 -*- -''' +""" Enforce state for SSL/TLS ========================= -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import time +from __future__ import absolute_import, print_function, unicode_literals + import datetime import logging +import time -__virtualname__ = 'tls' +__virtualname__ = "tls" log = logging.getLogger(__name__) def __virtual__(): - if 'tls.cert_info' not in __salt__: - return False + if "tls.cert_info" not in __salt__: + return (False, "tls module could not be loaded") return __virtualname__ -def valid_certificate(name, - weeks=0, - days=0, - hours=0, - minutes=0, - seconds=0): - ''' +def valid_certificate(name, weeks=0, days=0, hours=0, minutes=0, seconds=0): + """ Verify that a TLS certificate is valid now and (optionally) will be valid for the time specified through weeks, days, hours, minutes, and seconds. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} now = time.time() try: - cert_info = __salt__['tls.cert_info'](name) + cert_info = __salt__["tls.cert_info"](name) except IOError as exc: - ret['comment'] = '{}'.format(exc) - ret['result'] = False - log.error(ret['comment']) + ret["comment"] = "{}".format(exc) + ret["result"] = False + log.error(ret["comment"]) return ret # verify that the cert is valid *now* - if now < cert_info['not_before']: - ret['comment'] = 'Certificate is not yet valid' + if now < cert_info["not_before"]: + ret["comment"] = "Certificate is not yet valid" return ret - if now > cert_info['not_after']: - ret['comment'] = 'Certificate is expired' + if now > cert_info["not_after"]: + ret["comment"] = "Certificate is expired" return ret # verify the cert will be valid for defined time - delta_remaining = datetime.timedelta(seconds=cert_info['not_after']-now) + delta_remaining = datetime.timedelta(seconds=cert_info["not_after"] - now) delta_kind_map = { - 'weeks': weeks, - 'days': days, - 'hours': hours, - 'minutes': minutes, - 'seconds': seconds, + "weeks": weeks, + "days": days, + "hours": hours, + "minutes": minutes, + "seconds": seconds, } delta_min = datetime.timedelta(**delta_kind_map) # if ther eisn't enough time remaining, we consider it a failure if delta_remaining < delta_min: - ret['comment'] = 'Certificate will expire in {0}, which is less than {1}'.format(delta_remaining, delta_min) + ret[ + "comment" + ] = "Certificate will expire in {0}, which is less than {1}".format( + delta_remaining, delta_min + ) return ret - ret['result'] = True - ret['comment'] = 'Certificate is valid for {0}'.format(delta_remaining) + ret["result"] = True + ret["comment"] = "Certificate is valid for {0}".format(delta_remaining) return ret diff --git a/salt/states/tomcat.py b/salt/states/tomcat.py index 1c486f22ea7..9258d23a92e 100644 --- a/salt/states/tomcat.py +++ b/salt/states/tomcat.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Apache Tomcat web applications ===================================== @@ -53,31 +53,34 @@ Notes Linux OS Version: 3.10.0-327.22.2.el7.x86_64 -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals from salt.ext import six # Private def __virtual__(): - ''' + """ Load if the module tomcat exists - ''' - - return 'tomcat' if 'tomcat.status' in __salt__ else False + """ + if "tomcat.status" in __salt__: + return "tomcat" + return (False, "tomcat module could not be loaded") # Functions -def war_deployed(name, - war, - force=False, - url='http://localhost:8080/manager', - timeout=180, - temp_war_location=None, - version=True): - ''' +def war_deployed( + name, + war, + force=False, + url="http://localhost:8080/manager", + timeout=180, + temp_war_location=None, + version=True, +): + """ Enforce that the WAR will be deployed and started in the context path, while making use of WAR versions in the filename. @@ -128,102 +131,106 @@ def war_deployed(name, be deployed to the context path ``salt-powered-jenkins##1.2.4``. To avoid this either specify a version yourself, or set version to ``False``. - ''' + """ # Prepare - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} # if version is defined or False, we don't want to overwrite if version is True: - version = __salt__['tomcat.extract_war_version'](war) or '' + version = __salt__["tomcat.extract_war_version"](war) or "" elif not version: - version = '' + version = "" - webapps = __salt__['tomcat.ls'](url, timeout) + webapps = __salt__["tomcat.ls"](url, timeout) deploy = False undeploy = False status = True # Gathered/specified new WAR version string - specified_ver = 'version {0}'.format(version) if version else 'no version' + specified_ver = "version {0}".format(version) if version else "no version" # Determine what to do try: # Printed version strings, here to throw exception if no webapps[name] - current_ver = 'version ' + webapps[name]['version'] \ - if webapps[name]['version'] else 'no version' + current_ver = ( + "version " + webapps[name]["version"] + if webapps[name]["version"] + else "no version" + ) # `endswith` on the supposed string will cause Exception if empty - if (not webapps[name]['version'].endswith(version) - or (version == '' and webapps[name]['version'] != version) - or force): + if ( + not webapps[name]["version"].endswith(version) + or (version == "" and webapps[name]["version"] != version) + or force + ): deploy = True undeploy = True - ret['changes']['undeploy'] = ('undeployed {0} with {1}'. - format(name, current_ver)) - ret['changes']['deploy'] = ('will deploy {0} with {1}'. - format(name, specified_ver)) + ret["changes"]["undeploy"] = "undeployed {0} with {1}".format( + name, current_ver + ) + ret["changes"]["deploy"] = "will deploy {0} with {1}".format( + name, specified_ver + ) else: deploy = False - ret['comment'] = ('{0} with {1} is already deployed'. - format(name, specified_ver)) - if webapps[name]['mode'] != 'running': - ret['changes']['start'] = 'starting {0}'.format(name) + ret["comment"] = "{0} with {1} is already deployed".format( + name, specified_ver + ) + if webapps[name]["mode"] != "running": + ret["changes"]["start"] = "starting {0}".format(name) status = False else: return ret except Exception: # pylint: disable=broad-except deploy = True - ret['changes']['deploy'] = ('deployed {0} with {1}'. - format(name, specified_ver)) + ret["changes"]["deploy"] = "deployed {0} with {1}".format(name, specified_ver) # Test - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # make sure the webapp is up if deployed if deploy is False: if status is False: - ret['comment'] = __salt__['tomcat.start'](name, url, - timeout=timeout) - ret['result'] = ret['comment'].startswith('OK') + ret["comment"] = __salt__["tomcat.start"](name, url, timeout=timeout) + ret["result"] = ret["comment"].startswith("OK") return ret # Undeploy if undeploy: - un = __salt__['tomcat.undeploy'](name, url, timeout=timeout) - if un.startswith('FAIL'): - ret['result'] = False - ret['comment'] = un + un = __salt__["tomcat.undeploy"](name, url, timeout=timeout) + if un.startswith("FAIL"): + ret["result"] = False + ret["comment"] = un return ret # Deploy - deploy_res = __salt__['tomcat.deploy_war'](war, - name, - 'yes', - url, - __env__, - timeout, - temp_war_location=temp_war_location, - version=version) + deploy_res = __salt__["tomcat.deploy_war"]( + war, + name, + "yes", + url, + __env__, + timeout, + temp_war_location=temp_war_location, + version=version, + ) # Return - if deploy_res.startswith('OK'): - ret['result'] = True - ret['comment'] = six.text_type(__salt__['tomcat.ls'](url, timeout)[name]) - ret['changes']['deploy'] = ('deployed {0} with {1}'. - format(name, specified_ver)) + if deploy_res.startswith("OK"): + ret["result"] = True + ret["comment"] = six.text_type(__salt__["tomcat.ls"](url, timeout)[name]) + ret["changes"]["deploy"] = "deployed {0} with {1}".format(name, specified_ver) else: - ret['result'] = False - ret['comment'] = deploy_res - ret['changes'].pop('deploy') + ret["result"] = False + ret["comment"] = deploy_res + ret["changes"].pop("deploy") return ret -def wait(name, url='http://localhost:8080/manager', timeout=180): - ''' +def wait(name, url="http://localhost:8080/manager", timeout=180): + """ Wait for the Tomcat Manager to load. Notice that if tomcat is not running we won't wait for it start and the @@ -257,21 +264,23 @@ def wait(name, url='http://localhost:8080/manager', timeout=180): - war: salt://jenkins-1.2.4.war - require: - tomcat: wait-for-tomcatmanager - ''' + """ - result = __salt__['tomcat.status'](url, timeout) - ret = {'name': name, - 'result': result, - 'changes': {}, - 'comment': ('tomcat manager is ready' if result - else 'tomcat manager is not ready') - } + result = __salt__["tomcat.status"](url, timeout) + ret = { + "name": name, + "result": result, + "changes": {}, + "comment": ( + "tomcat manager is ready" if result else "tomcat manager is not ready" + ), + } return ret -def mod_watch(name, url='http://localhost:8080/manager', timeout=180): - ''' +def mod_watch(name, url="http://localhost:8080/manager", timeout=180): + """ The tomcat watcher, called to invoke the watch command. When called, it will reload the webapp in question @@ -280,24 +289,18 @@ def mod_watch(name, url='http://localhost:8080/manager', timeout=180): :ref:`requisite <requisites>`. It should not be called directly. Parameters for this function should be set by the state being triggered. - ''' + """ - msg = __salt__['tomcat.reload'](name, url, timeout) - result = msg.startswith('OK') + msg = __salt__["tomcat.reload"](name, url, timeout) + result = msg.startswith("OK") - ret = {'name': name, - 'result': result, - 'changes': {name: result}, - 'comment': msg - } + ret = {"name": name, "result": result, "changes": {name: result}, "comment": msg} return ret -def undeployed(name, - url='http://localhost:8080/manager', - timeout=180): - ''' +def undeployed(name, url="http://localhost:8080/manager", timeout=180): + """ Enforce that the WAR will be undeployed from the server name @@ -316,34 +319,31 @@ def undeployed(name, - name: /ran - require: - service: application-service - ''' + """ # Prepare - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - if not __salt__['tomcat.status'](url, timeout): - ret['comment'] = 'Tomcat Manager does not respond' - ret['result'] = False + if not __salt__["tomcat.status"](url, timeout): + ret["comment"] = "Tomcat Manager does not respond" + ret["result"] = False return ret try: - version = __salt__['tomcat.ls'](url, timeout)[name]['version'] - ret['changes'] = {'undeploy': version} + version = __salt__["tomcat.ls"](url, timeout)[name]["version"] + ret["changes"] = {"undeploy": version} except KeyError: return ret # Test - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret - undeploy = __salt__['tomcat.undeploy'](name, url, timeout=timeout) - if undeploy.startswith('FAIL'): - ret['result'] = False - ret['comment'] = undeploy + undeploy = __salt__["tomcat.undeploy"](name, url, timeout=timeout) + if undeploy.startswith("FAIL"): + ret["result"] = False + ret["comment"] = undeploy return ret return ret diff --git a/salt/states/trafficserver.py b/salt/states/trafficserver.py index 9f125b8a06a..16181053973 100644 --- a/salt/states/trafficserver.py +++ b/salt/states/trafficserver.py @@ -1,24 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" Control Apache Traffic Server ============================= .. versionadded:: 2015.8.0 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the Traffic Server module is available in __salt__ - ''' - return 'trafficserver' if 'trafficserver.set_config' in __salt__ else False + """ + if "trafficserver.set_config" in __salt__: + return "trafficserver" + return (False, "trafficserver module could not be loaded") def bounce_cluster(name): - ''' + """ Bounce all Traffic Server nodes in the cluster. Bouncing Traffic Server shuts down and immediately restarts Traffic Server, node-by-node. @@ -26,25 +28,22 @@ def bounce_cluster(name): bounce_ats_cluster: trafficserver.bounce_cluster - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Bouncing cluster' + if __opts__["test"]: + ret["comment"] = "Bouncing cluster" return ret - __salt__['trafficserver.bounce_cluster']() + __salt__["trafficserver.bounce_cluster"]() - ret['result'] = True - ret['comment'] = 'Bounced cluster' + ret["result"] = True + ret["comment"] = "Bounced cluster" return ret def bounce_local(name, drain=False): - ''' + """ Bounce Traffic Server on the local node. Bouncing Traffic Server shuts down and immediately restarts the Traffic Server node. @@ -61,80 +60,71 @@ def bounce_local(name, drain=False): bounce_ats_local: trafficserver.bounce_local - drain: True - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Bouncing local node' + if __opts__["test"]: + ret["comment"] = "Bouncing local node" return ret if drain: - __salt__['trafficserver.bounce_local'](drain=True) - ret['result'] = True - ret['comment'] = 'Bounced local node with drain option' + __salt__["trafficserver.bounce_local"](drain=True) + ret["result"] = True + ret["comment"] = "Bounced local node with drain option" return ret else: - __salt__['trafficserver.bounce_local']() - ret['result'] = True - ret['comment'] = 'Bounced local node' + __salt__["trafficserver.bounce_local"]() + ret["result"] = True + ret["comment"] = "Bounced local node" return ret def clear_cluster(name): - ''' + """ Clears accumulated statistics on all nodes in the cluster. .. code-block:: yaml clear_ats_cluster: trafficserver.clear_cluster - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Clearing cluster statistics' + if __opts__["test"]: + ret["comment"] = "Clearing cluster statistics" return ret - __salt__['trafficserver.clear_cluster']() + __salt__["trafficserver.clear_cluster"]() - ret['result'] = True - ret['comment'] = 'Cleared cluster statistics' + ret["result"] = True + ret["comment"] = "Cleared cluster statistics" return ret def clear_node(name): - ''' + """ Clears accumulated statistics on the local node. .. code-block:: yaml clear_ats_node: trafficserver.clear_node - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Clearing local node statistics' + if __opts__["test"]: + ret["comment"] = "Clearing local node statistics" return ret - __salt__['trafficserver.clear_node']() + __salt__["trafficserver.clear_node"]() - ret['result'] = True - ret['comment'] = 'Cleared local node statistics' + ret["result"] = True + ret["comment"] = "Cleared local node statistics" return ret def restart_cluster(name): - ''' + """ Restart the traffic_manager process and the traffic_server process on all the nodes in a cluster. @@ -143,25 +133,22 @@ def restart_cluster(name): restart_ats_cluster: trafficserver.restart_cluster - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Restarting cluster' + if __opts__["test"]: + ret["comment"] = "Restarting cluster" return ret - __salt__['trafficserver.restart_cluster']() + __salt__["trafficserver.restart_cluster"]() - ret['result'] = True - ret['comment'] = 'Restarted cluster' + ret["result"] = True + ret["comment"] = "Restarted cluster" return ret def restart_local(name, drain=False): - ''' + """ Restart the traffic_manager and traffic_server processes on the local node. This option modifies the behavior of traffic_line -b and traffic_line -L @@ -177,30 +164,27 @@ def restart_local(name, drain=False): restart_ats_local_drain: trafficserver.restart_local - drain: True - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Restarting local node' + if __opts__["test"]: + ret["comment"] = "Restarting local node" return ret if drain: - __salt__['trafficserver.restart_local'](drain=True) - ret['result'] = True - ret['comment'] = 'Restarted local node with drain option' + __salt__["trafficserver.restart_local"](drain=True) + ret["result"] = True + ret["comment"] = "Restarted local node with drain option" return ret else: - __salt__['trafficserver.restart_local']() - ret['result'] = True - ret['comment'] = 'Restarted local node' + __salt__["trafficserver.restart_local"]() + ret["result"] = True + ret["comment"] = "Restarted local node" return ret def config(name, value): - ''' + """ Set Traffic Server configuration variable values. .. code-block:: yaml @@ -216,78 +200,66 @@ def config(name, value): - name: proxy.config.proxy_name - value: cdn.site.domain.tld - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Configuring {0} to {1}'.format( - name, - value, - ) + if __opts__["test"]: + ret["comment"] = "Configuring {0} to {1}".format(name, value,) return ret - __salt__['trafficserver.set_config'](name, value) + __salt__["trafficserver.set_config"](name, value) - ret['result'] = True - ret['comment'] = 'Configured {0} to {1}'.format(name, value) + ret["result"] = True + ret["comment"] = "Configured {0} to {1}".format(name, value) return ret def shutdown(name): - ''' + """ Shut down Traffic Server on the local node. .. code-block:: yaml shutdown_ats: trafficserver.shutdown - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Shutting down local node' + if __opts__["test"]: + ret["comment"] = "Shutting down local node" return ret - __salt__['trafficserver.shutdown']() + __salt__["trafficserver.shutdown"]() - ret['result'] = True - ret['comment'] = 'Shutdown local node' + ret["result"] = True + ret["comment"] = "Shutdown local node" return ret def startup(name): - ''' + """ Start Traffic Server on the local node. .. code-block:: yaml startup_ats: trafficserver.startup - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Starting up local node' + if __opts__["test"]: + ret["comment"] = "Starting up local node" return ret - __salt__['trafficserver.startup']() + __salt__["trafficserver.startup"]() - ret['result'] = True - ret['comment'] = 'Starting up local node' + ret["result"] = True + ret["comment"] = "Starting up local node" return ret def refresh(name): - ''' + """ Initiate a Traffic Server configuration file reread. Use this command to update the running configuration after any configuration file modification. @@ -298,75 +270,66 @@ def refresh(name): refresh_ats: trafficserver.refresh - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Refreshing local node configuration' + if __opts__["test"]: + ret["comment"] = "Refreshing local node configuration" return ret - __salt__['trafficserver.refresh']() + __salt__["trafficserver.refresh"]() - ret['result'] = True - ret['comment'] = 'Refreshed local node configuration' + ret["result"] = True + ret["comment"] = "Refreshed local node configuration" return ret def zero_cluster(name): - ''' + """ Reset performance statistics to zero across the cluster. .. code-block:: yaml zero_ats_cluster: trafficserver.zero_cluster - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Zeroing cluster statistics' + if __opts__["test"]: + ret["comment"] = "Zeroing cluster statistics" return ret - __salt__['trafficserver.zero_cluster']() + __salt__["trafficserver.zero_cluster"]() - ret['result'] = True - ret['comment'] = 'Zeroed cluster statistics' + ret["result"] = True + ret["comment"] = "Zeroed cluster statistics" return ret def zero_node(name): - ''' + """ Reset performance statistics to zero on the local node. .. code-block:: yaml zero_ats_node: trafficserver.zero_node - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Zeroing local node statistics' + if __opts__["test"]: + ret["comment"] = "Zeroing local node statistics" return ret - __salt__['trafficserver.zero_node']() + __salt__["trafficserver.zero_node"]() - ret['result'] = True - ret['comment'] = 'Zeroed local node statistics' + ret["result"] = True + ret["comment"] = "Zeroed local node statistics" return ret def offline(name, path): - ''' + """ Mark a cache storage device as offline. The storage is identified by a path which must match exactly a path specified in storage.config. This removes the storage from the cache and redirects requests that would have used this @@ -379,18 +342,15 @@ def offline(name, path): offline_ats_path: trafficserver.offline: - path: /path/to/cache - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Setting {0} to offline'.format(path) + if __opts__["test"]: + ret["comment"] = "Setting {0} to offline".format(path) return ret - __salt__['trafficserver.offline'](path) + __salt__["trafficserver.offline"](path) - ret['result'] = True - ret['comment'] = 'Set {0} as offline'.format(path) + ret["result"] = True + ret["comment"] = "Set {0} as offline".format(path) return ret diff --git a/salt/states/tuned.py b/salt/states/tuned.py index 0f89c3b2719..f03a49a3732 100644 --- a/salt/states/tuned.py +++ b/salt/states/tuned.py @@ -1,22 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Interface to Red Hat tuned-adm module :maintainer: Syed Ali <alicsyed@gmail.com> :maturity: new :depends: cmd.run :platform: Linux -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.exceptions def profile(name): - ''' + """ This state module allows you to modify system tuned parameters Example tuned.sls file to set profile to virtual-guest @@ -31,63 +31,62 @@ def profile(name): To see a valid list of states call execution module: :py:func:`tuned.list <salt.modules.tuned.list_>` - ''' + """ # create data-structure to return with default value - ret = {'name': '', 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": "", "changes": {}, "result": False, "comment": ""} ret[name] = name profile = name # get the current state of tuned-adm - current_state = __salt__['tuned.active']() + current_state = __salt__["tuned.active"]() - valid_profiles = __salt__['tuned.list']() + valid_profiles = __salt__["tuned.list"]() # check valid profiles, and return error if profile name is not valid if profile not in valid_profiles: - raise salt.exceptions.SaltInvocationError('Invalid Profile Name') + raise salt.exceptions.SaltInvocationError("Invalid Profile Name") # if current state is same as requested state, return without doing much if profile in current_state: - ret['result'] = True - ret['comment'] = 'System already in the correct state' + ret["result"] = True + ret["comment"] = "System already in the correct state" return ret # test mode - if __opts__['test'] is True: - ret['comment'] = 'The state of "{0}" will be changed.'.format( - current_state) - ret['changes'] = { - 'old': current_state, - 'new': 'Profile will be set to {0}'.format(profile), + if __opts__["test"] is True: + ret["comment"] = 'The state of "{0}" will be changed.'.format(current_state) + ret["changes"] = { + "old": current_state, + "new": "Profile will be set to {0}".format(profile), } # return None when testing - ret['result'] = None + ret["result"] = None return ret # we come to this stage if current state is different that requested state # we there have to set the new state request - new_state = __salt__['tuned.profile'](profile) + new_state = __salt__["tuned.profile"](profile) # create the comment data structure - ret['comment'] = 'The state of "{0}" was changed!'.format(profile) + ret["comment"] = 'The state of "{0}" was changed!'.format(profile) # fill in the ret data structure - ret['changes'] = { - 'old': current_state, - 'new': new_state, + ret["changes"] = { + "old": current_state, + "new": new_state, } - ret['result'] = True + ret["result"] = True # return with the dictionary data structure return ret def off(name=None): - ''' + """ Turns 'tuned' off. Example tuned.sls file for turning tuned off: @@ -98,38 +97,37 @@ def off(name=None): To see a valid list of states call execution module: :py:func:`tuned.list <salt.modules.tuned.list_>` - ''' + """ # create data-structure to return with default value - ret = {'name': 'off', 'changes': {}, 'result': False, 'comment': 'off'} + ret = {"name": "off", "changes": {}, "result": False, "comment": "off"} # check the current state of tuned - current_state = __salt__['tuned.active']() + current_state = __salt__["tuned.active"]() # if profile is already off, then don't do anything - if current_state == 'off': - ret['result'] = True - ret['comment'] = 'System already in the correct state' + if current_state == "off": + ret["result"] = True + ret["comment"] = "System already in the correct state" return ret # test mode - if __opts__['test'] is True: - ret['comment'] = 'The state of "{0}" will be changed.'.format( - current_state) - ret['changes'] = { - 'old': current_state, - 'new': 'Profile will be set to off', + if __opts__["test"] is True: + ret["comment"] = 'The state of "{0}" will be changed.'.format(current_state) + ret["changes"] = { + "old": current_state, + "new": "Profile will be set to off", } # return None when testing - ret['result'] = None + ret["result"] = None return ret # actually execute the off statement - if __salt__['tuned.off'](): - ret['result'] = True - ret['changes'] = { - 'old': current_state, - 'new': 'off', + if __salt__["tuned.off"](): + ret["result"] = True + ret["changes"] = { + "old": current_state, + "new": "off", } return ret diff --git a/salt/states/uptime.py b/salt/states/uptime.py index 9d017d53d54..43667fb9fdb 100644 --- a/salt/states/uptime.py +++ b/salt/states/uptime.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Monitor Web Server with Uptime ============================== @@ -32,44 +32,45 @@ Example: uptime.monitored: - polling: 600 # every hour -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the uptime module is present - ''' - return 'uptime.checks_list' in __salt__ + """ + if "uptime.checks_list" in __salt__: + return True + return (False, "uptime module could not be loaded") def monitored(name, **params): - ''' + """ Makes sure an URL is monitored by uptime. Checks if URL is already monitored, and if not, adds it. - ''' + """ - ret = {'name': name, 'changes': {}, 'result': None, 'comment': ''} - if __salt__['uptime.check_exists'](name=name): - ret['result'] = True - ret['comment'] = 'URL {0} is already monitored'.format(name) - ret['changes'] = {} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + if __salt__["uptime.check_exists"](name=name): + ret["result"] = True + ret["comment"] = "URL {0} is already monitored".format(name) + ret["changes"] = {} return ret - if not __opts__['test']: - url_monitored = __salt__['uptime.create'](name, **params) + if not __opts__["test"]: + url_monitored = __salt__["uptime.create"](name, **params) if url_monitored: - ret['result'] = True - msg = 'Successfully added the URL {0} to uptime' - ret['comment'] = msg.format(name) - ret['changes'] = {'url_monitored': url_monitored} + ret["result"] = True + msg = "Successfully added the URL {0} to uptime" + ret["comment"] = msg.format(name) + ret["changes"] = {"url_monitored": url_monitored} else: - ret['result'] = False - ret['comment'] = 'Failed to add {0} to uptime'.format(name) - ret['changes'] = {} + ret["result"] = False + ret["comment"] = "Failed to add {0} to uptime".format(name) + ret["changes"] = {} else: - msg = 'URL {0} is going to be added to uptime' - ret.update(result=None, - comment=msg.format(name)) + msg = "URL {0} is going to be added to uptime" + ret.update(result=None, comment=msg.format(name)) return ret diff --git a/salt/states/user.py b/salt/states/user.py index 7b05e277c74..689df6bfb1c 100644 --- a/salt/states/user.py +++ b/salt/states/user.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of user accounts =========================== @@ -22,11 +22,12 @@ as either absent or present testuser: user.absent -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import logging +import os # Import Salt libs import salt.utils.data @@ -36,15 +37,15 @@ import salt.utils.user from salt.exceptions import CommandExecutionError # Import 3rd-party libs -from salt.ext.six import string_types, iteritems +from salt.ext.six import iteritems, string_types log = logging.getLogger(__name__) def _group_changes(cur, wanted, remove=False): - ''' + """ Determine if the groups need to be changed - ''' + """ old = set(cur) new = set(wanted) if (remove and old != new) or (not remove and not new.issubset(old)): @@ -52,37 +53,39 @@ def _group_changes(cur, wanted, remove=False): return False -def _changes(name, - uid=None, - gid=None, - groups=None, - optional_groups=None, - remove_groups=True, - home=None, - createhome=True, - password=None, - enforce_password=True, - empty_password=False, - shell=None, - fullname='', - roomnumber='', - workphone='', - homephone='', - other='', - loginclass=None, - date=None, - mindays=0, - maxdays=999999, - inactdays=0, - warndays=7, - expire=None, - win_homedrive=None, - win_profile=None, - win_logonscript=None, - win_description=None, - allow_uid_change=False, - allow_gid_change=False): - ''' +def _changes( + name, + uid=None, + gid=None, + groups=None, + optional_groups=None, + remove_groups=True, + home=None, + createhome=True, + password=None, + enforce_password=True, + empty_password=False, + shell=None, + fullname="", + roomnumber="", + workphone="", + homephone="", + other="", + loginclass=None, + date=None, + mindays=0, + maxdays=999999, + inactdays=0, + warndays=7, + expire=None, + win_homedrive=None, + win_profile=None, + win_logonscript=None, + win_description=None, + allow_uid_change=False, + allow_gid_change=False, +): + """ Return a dict of the changes required for a user if the user is present, otherwise return False. @@ -91,173 +94,177 @@ def _changes(name, Updated in 2014.7.0 to include support for shadow attributes, all attributes supported as integers only. - ''' + """ - if 'shadow.info' in __salt__: - lshad = __salt__['shadow.info'](name) + if "shadow.info" in __salt__: + lshad = __salt__["shadow.info"](name) - lusr = __salt__['user.info'](name) + lusr = __salt__["user.info"](name) if not lusr: return False change = {} if groups is None: - groups = lusr['groups'] + groups = lusr["groups"] wanted_groups = sorted(set((groups or []) + (optional_groups or []))) - if uid and lusr['uid'] != uid: - change['uid'] = uid - if gid is not None and lusr['gid'] not in (gid, __salt__['file.group_to_gid'](gid)): - change['gid'] = gid - default_grp = __salt__['file.gid_to_group']( - gid if gid is not None else lusr['gid'] - ) + if uid and lusr["uid"] != uid: + change["uid"] = uid + if gid is not None and lusr["gid"] not in (gid, __salt__["file.group_to_gid"](gid)): + change["gid"] = gid + default_grp = __salt__["file.gid_to_group"](gid if gid is not None else lusr["gid"]) # remove the default group from the list for comparison purposes - if default_grp in lusr['groups']: - lusr['groups'].remove(default_grp) - if name in lusr['groups'] and name not in wanted_groups: - lusr['groups'].remove(name) + if default_grp in lusr["groups"]: + lusr["groups"].remove(default_grp) + if name in lusr["groups"] and name not in wanted_groups: + lusr["groups"].remove(name) # remove default group from wanted_groups, as this requirement is # already met if default_grp in wanted_groups: wanted_groups.remove(default_grp) - if _group_changes(lusr['groups'], wanted_groups, remove_groups): - change['groups'] = wanted_groups - if home and lusr['home'] != home: - change['home'] = home + if _group_changes(lusr["groups"], wanted_groups, remove_groups): + change["groups"] = wanted_groups + if home and lusr["home"] != home: + change["home"] = home if createhome: - newhome = home if home else lusr['home'] + newhome = home if home else lusr["home"] if newhome is not None and not os.path.isdir(newhome): - change['homeDoesNotExist'] = newhome - if shell and lusr['shell'] != shell: - change['shell'] = shell - if 'shadow.info' in __salt__ and 'shadow.default_hash' in __salt__: + change["homeDoesNotExist"] = newhome + if shell and lusr["shell"] != shell: + change["shell"] = shell + if "shadow.info" in __salt__ and "shadow.default_hash" in __salt__: if password and not empty_password: - default_hash = __salt__['shadow.default_hash']() - if lshad['passwd'] == default_hash \ - or lshad['passwd'] != default_hash and enforce_password: - if lshad['passwd'] != password: - change['passwd'] = password - if empty_password and lshad['passwd'] != '': - change['empty_password'] = True - if date is not None and lshad['lstchg'] != date: - change['date'] = date - if mindays is not None and lshad['min'] != mindays: - change['mindays'] = mindays - if maxdays is not None and lshad['max'] != maxdays: - change['maxdays'] = maxdays - if inactdays is not None and lshad['inact'] != inactdays: - change['inactdays'] = inactdays - if warndays is not None and lshad['warn'] != warndays: - change['warndays'] = warndays - if expire and lshad['expire'] != expire: - change['expire'] = expire - elif 'shadow.info' in __salt__ and salt.utils.platform.is_windows(): - if expire and expire is not -1 and salt.utils.dateutils.strftime(lshad['expire']) != salt.utils.dateutils.strftime(expire): - change['expire'] = expire + default_hash = __salt__["shadow.default_hash"]() + if ( + lshad["passwd"] == default_hash + or lshad["passwd"] != default_hash + and enforce_password + ): + if lshad["passwd"] != password: + change["passwd"] = password + if empty_password and lshad["passwd"] != "": + change["empty_password"] = True + if date is not None and lshad["lstchg"] != date: + change["date"] = date + if mindays is not None and lshad["min"] != mindays: + change["mindays"] = mindays + if maxdays is not None and lshad["max"] != maxdays: + change["maxdays"] = maxdays + if inactdays is not None and lshad["inact"] != inactdays: + change["inactdays"] = inactdays + if warndays is not None and lshad["warn"] != warndays: + change["warndays"] = warndays + if expire and lshad["expire"] != expire: + change["expire"] = expire + elif "shadow.info" in __salt__ and salt.utils.platform.is_windows(): + if ( + expire + and expire is not -1 + and salt.utils.dateutils.strftime(lshad["expire"]) + != salt.utils.dateutils.strftime(expire) + ): + change["expire"] = expire # GECOS fields fullname = salt.utils.data.decode(fullname) - lusr['fullname'] = salt.utils.data.decode(lusr['fullname']) - if fullname is not None and lusr['fullname'] != fullname: - change['fullname'] = fullname - if win_homedrive and lusr['homedrive'] != win_homedrive: - change['win_homedrive'] = win_homedrive - if win_profile and lusr['profile'] != win_profile: - change['win_profile'] = win_profile - if win_logonscript and lusr['logonscript'] != win_logonscript: - change['win_logonscript'] = win_logonscript - if win_description and lusr['description'] != win_description: - change['win_description'] = win_description + lusr["fullname"] = salt.utils.data.decode(lusr["fullname"]) + if fullname is not None and lusr["fullname"] != fullname: + change["fullname"] = fullname + if win_homedrive and lusr["homedrive"] != win_homedrive: + change["win_homedrive"] = win_homedrive + if win_profile and lusr["profile"] != win_profile: + change["win_profile"] = win_profile + if win_logonscript and lusr["logonscript"] != win_logonscript: + change["win_logonscript"] = win_logonscript + if win_description and lusr["description"] != win_description: + change["win_description"] = win_description # MacOS doesn't have full GECOS support, so check for the "ch" functions # and ignore these parameters if these functions do not exist. - if 'user.chroomnumber' in __salt__ \ - and roomnumber is not None: + if "user.chroomnumber" in __salt__ and roomnumber is not None: roomnumber = salt.utils.data.decode(roomnumber) - lusr['roomnumber'] = salt.utils.data.decode(lusr['roomnumber']) - if lusr['roomnumber'] != roomnumber: - change['roomnumber'] = roomnumber - if 'user.chworkphone' in __salt__ \ - and workphone is not None: + lusr["roomnumber"] = salt.utils.data.decode(lusr["roomnumber"]) + if lusr["roomnumber"] != roomnumber: + change["roomnumber"] = roomnumber + if "user.chworkphone" in __salt__ and workphone is not None: workphone = salt.utils.data.decode(workphone) - lusr['workphone'] = salt.utils.data.decode(lusr['workphone']) - if lusr['workphone'] != workphone: - change['workphone'] = workphone - if 'user.chhomephone' in __salt__ \ - and homephone is not None: + lusr["workphone"] = salt.utils.data.decode(lusr["workphone"]) + if lusr["workphone"] != workphone: + change["workphone"] = workphone + if "user.chhomephone" in __salt__ and homephone is not None: homephone = salt.utils.data.decode(homephone) - lusr['homephone'] = salt.utils.data.decode(lusr['homephone']) - if lusr['homephone'] != homephone: - change['homephone'] = homephone - if 'user.chother' in __salt__ and other is not None: + lusr["homephone"] = salt.utils.data.decode(lusr["homephone"]) + if lusr["homephone"] != homephone: + change["homephone"] = homephone + if "user.chother" in __salt__ and other is not None: other = salt.utils.data.decode(other) - lusr['other'] = salt.utils.data.decode(lusr['other']) - if lusr['other'] != other: - change['other'] = other + lusr["other"] = salt.utils.data.decode(lusr["other"]) + if lusr["other"] != other: + change["other"] = other # OpenBSD/FreeBSD login class - if __grains__['kernel'] in ('OpenBSD', 'FreeBSD'): + if __grains__["kernel"] in ("OpenBSD", "FreeBSD"): if loginclass: - if __salt__['user.get_loginclass'](name) != loginclass: - change['loginclass'] = loginclass + if __salt__["user.get_loginclass"](name) != loginclass: + change["loginclass"] = loginclass errors = [] - if not allow_uid_change and 'uid' in change: + if not allow_uid_change and "uid" in change: errors.append( - 'Changing uid ({0} -> {1}) not permitted, set allow_uid_change to ' - 'True to force this change. Note that this will not change file ' - 'ownership.'.format(lusr['uid'], uid) + "Changing uid ({0} -> {1}) not permitted, set allow_uid_change to " + "True to force this change. Note that this will not change file " + "ownership.".format(lusr["uid"], uid) ) - if not allow_gid_change and 'gid' in change: + if not allow_gid_change and "gid" in change: errors.append( - 'Changing gid ({0} -> {1}) not permitted, set allow_gid_change to ' - 'True to force this change. Note that this will not change file ' - 'ownership.'.format(lusr['gid'], gid) + "Changing gid ({0} -> {1}) not permitted, set allow_gid_change to " + "True to force this change. Note that this will not change file " + "ownership.".format(lusr["gid"], gid) ) if errors: raise CommandExecutionError( - 'Encountered error checking for needed changes', - info=errors + "Encountered error checking for needed changes", info=errors ) return change -def present(name, - uid=None, - gid=None, - gid_from_name=False, - groups=None, - optional_groups=None, - remove_groups=True, - home=None, - createhome=True, - password=None, - hash_password=False, - enforce_password=True, - empty_password=False, - shell=None, - unique=True, - system=False, - fullname=None, - roomnumber=None, - workphone=None, - homephone=None, - other=None, - loginclass=None, - date=None, - mindays=None, - maxdays=None, - inactdays=None, - warndays=None, - expire=None, - win_homedrive=None, - win_profile=None, - win_logonscript=None, - win_description=None, - nologinit=False, - allow_uid_change=False, - allow_gid_change=False): - ''' +def present( + name, + uid=None, + gid=None, + gid_from_name=False, + groups=None, + optional_groups=None, + remove_groups=True, + home=None, + createhome=True, + password=None, + hash_password=False, + enforce_password=True, + empty_password=False, + shell=None, + unique=True, + system=False, + fullname=None, + roomnumber=None, + workphone=None, + homephone=None, + other=None, + loginclass=None, + date=None, + mindays=None, + maxdays=None, + inactdays=None, + warndays=None, + expire=None, + win_homedrive=None, + win_profile=None, + win_logonscript=None, + win_description=None, + nologinit=False, + allow_uid_change=False, + allow_gid_change=False, +): + """ Ensure that the named user is present with the specified properties name @@ -327,7 +334,7 @@ def present(name, databases. .. note:: - Not supported on Windows or Mac OS. + Not supported on Windows. password A password hash to set for the user. This field is only supported on @@ -443,31 +450,41 @@ def present(name, A brief description of the purpose of the users account. .. versionchanged:: 2015.8.0 - ''' + """ # First check if a password is set. If password is set, check if # hash_password is True, then hash it. if password and hash_password: - log.debug('Hashing a clear text password') + log.debug("Hashing a clear text password") # in case a password is already set, it will contain a Salt # which should be re-used to generate the new hash, other- # wise the Salt will be generated randomly, causing the # hash to change each time and thereby making the # user.present state non-idempotent. algorithms = { - '1': 'md5', - '2a': 'blowfish', - '5': 'sha256', - '6': 'sha512', + "1": "md5", + "2a": "blowfish", + "5": "sha256", + "6": "sha512", } try: - _, algo, shadow_salt, shadow_hash = __salt__['shadow.info'](name)['passwd'].split('$', 4) - if algo == '1': - log.warning('Using MD5 for hashing passwords is considered insecure!') - log.debug('Re-using existing shadow salt for hashing password using {}'.format(algorithms.get(algo))) - password = __salt__['shadow.gen_password'](password, crypt_salt=shadow_salt, algorithm=algorithms.get(algo)) + _, algo, shadow_salt, shadow_hash = __salt__["shadow.info"](name)[ + "passwd" + ].split("$", 4) + if algo == "1": + log.warning("Using MD5 for hashing passwords is considered insecure!") + log.debug( + "Re-using existing shadow salt for hashing password using {}".format( + algorithms.get(algo) + ) + ) + password = __salt__["shadow.gen_password"]( + password, crypt_salt=shadow_salt, algorithm=algorithms.get(algo) + ) except ValueError: - log.info('No existing shadow salt found, defaulting to a randomly generated new one') - password = __salt__['shadow.gen_password'](password) + log.info( + "No existing shadow salt found, defaulting to a randomly generated new one" + ) + password = __salt__["shadow.gen_password"](password) if fullname is not None: fullname = salt.utils.data.decode(fullname) @@ -480,39 +497,43 @@ def present(name, if other is not None: other = salt.utils.data.decode(other) - # createhome not supported on Windows or Mac - if __grains__['kernel'] in ('Darwin', 'Windows'): + # createhome not supported on Windows + if __grains__["kernel"] == "Windows": createhome = False - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User {0} is present and up to date'.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "User {0} is present and up to date".format(name), + } # the comma is used to separate field in GECOS, thus resulting into # salt adding the end of fullname each time this function is called for gecos_field in [fullname, roomnumber, workphone]: - if isinstance(gecos_field, string_types) and ',' in gecos_field: - ret['comment'] = "Unsupported char ',' in {0}".format(gecos_field) - ret['result'] = False + if isinstance(gecos_field, string_types) and "," in gecos_field: + ret["comment"] = "Unsupported char ',' in {0}".format(gecos_field) + ret["result"] = False return ret if groups: - missing_groups = [x for x in groups if not __salt__['group.info'](x)] + missing_groups = [x for x in groups if not __salt__["group.info"](x)] if missing_groups: - ret['comment'] = 'The following group(s) are not present: ' \ - '{0}'.format(','.join(missing_groups)) - ret['result'] = False + ret["comment"] = "The following group(s) are not present: " "{0}".format( + ",".join(missing_groups) + ) + ret["result"] = False return ret if optional_groups: - present_optgroups = [x for x in optional_groups - if __salt__['group.info'](x)] - for missing_optgroup in [x for x in optional_groups - if x not in present_optgroups]: + present_optgroups = [x for x in optional_groups if __salt__["group.info"](x)] + for missing_optgroup in [ + x for x in optional_groups if x not in present_optgroups + ]: log.debug( 'Optional group "%s" for user "%s" is not present', - missing_optgroup, name + missing_optgroup, + name, ) else: present_optgroups = None @@ -523,200 +544,201 @@ def present(name, for isected in set(groups).intersection(optional_groups): log.warning( 'Group "%s" specified in both groups and optional_groups ' - 'for user %s', isected, name + "for user %s", + isected, + name, ) if gid_from_name: - gid = __salt__['file.group_to_gid'](name) - if gid == '': - ret['comment'] = 'Default group with name "{0}" is not present'.format(name) - ret['result'] = False + gid = __salt__["file.group_to_gid"](name) + if gid == "": + ret["comment"] = 'Default group with name "{0}" is not present'.format(name) + ret["result"] = False return ret try: - changes = _changes(name, - uid, - gid, - groups, - present_optgroups, - remove_groups, - home, - createhome, - password, - enforce_password, - empty_password, - shell, - fullname, - roomnumber, - workphone, - homephone, - other, - loginclass, - date, - mindays, - maxdays, - inactdays, - warndays, - expire, - win_homedrive, - win_profile, - win_logonscript, - win_description, - allow_uid_change, - allow_gid_change) + changes = _changes( + name, + uid, + gid, + groups, + present_optgroups, + remove_groups, + home, + createhome, + password, + enforce_password, + empty_password, + shell, + fullname, + roomnumber, + workphone, + homephone, + other, + loginclass, + date, + mindays, + maxdays, + inactdays, + warndays, + expire, + win_homedrive, + win_profile, + win_logonscript, + win_description, + allow_uid_change, + allow_gid_change, + ) except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret if changes: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('The following user attributes are set to be ' - 'changed:\n') + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The following user attributes are set to be " "changed:\n" for key, val in iteritems(changes): - if key == 'passwd': - val = 'XXX-REDACTED-XXX' - elif key == 'group' and not remove_groups: - key = 'ensure groups' - ret['comment'] += '{0}: {1}\n'.format(key, val) + if key == "passwd": + val = "XXX-REDACTED-XXX" + elif key == "group" and not remove_groups: + key = "ensure groups" + ret["comment"] += "{0}: {1}\n".format(key, val) return ret # The user is present - if 'shadow.info' in __salt__: - lshad = __salt__['shadow.info'](name) - if __grains__['kernel'] in ('OpenBSD', 'FreeBSD'): - lcpre = __salt__['user.get_loginclass'](name) - pre = __salt__['user.info'](name) + if "shadow.info" in __salt__: + lshad = __salt__["shadow.info"](name) + if __grains__["kernel"] in ("OpenBSD", "FreeBSD"): + lcpre = __salt__["user.get_loginclass"](name) + pre = __salt__["user.info"](name) for key, val in iteritems(changes): - if key == 'passwd' and not empty_password: - __salt__['shadow.set_password'](name, password) + if key == "passwd" and not empty_password: + __salt__["shadow.set_password"](name, password) continue - if key == 'passwd' and empty_password: + if key == "passwd" and empty_password: log.warning("No password will be set when empty_password=True") continue - if key == 'empty_password' and val: - __salt__['shadow.del_password'](name) + if key == "empty_password" and val: + __salt__["shadow.del_password"](name) continue - if key == 'date': - __salt__['shadow.set_date'](name, date) + if key == "date": + __salt__["shadow.set_date"](name, date) continue # run chhome once to avoid any possible bad side-effect - if key == 'home' and 'homeDoesNotExist' not in changes: - if __grains__['kernel'] in ('Darwin', 'Windows'): - __salt__['user.chhome'](name, val) + if key == "home" and "homeDoesNotExist" not in changes: + if __grains__["kernel"] in ("Darwin", "Windows"): + __salt__["user.chhome"](name, val) else: - __salt__['user.chhome'](name, val, persist=False) + __salt__["user.chhome"](name, val, persist=False) continue - if key == 'homeDoesNotExist': - if __grains__['kernel'] in ('Darwin', 'Windows'): - __salt__['user.chhome'](name, val) + if key == "homeDoesNotExist": + if __grains__["kernel"] in ("Darwin", "Windows"): + __salt__["user.chhome"](name, val) else: - __salt__['user.chhome'](name, val, persist=True) + __salt__["user.chhome"](name, val, persist=True) if not os.path.isdir(val): - __salt__['file.mkdir'](val, pre['uid'], pre['gid'], 0o755) + __salt__["file.mkdir"](val, pre["uid"], pre["gid"], 0o755) continue - if key == 'mindays': - __salt__['shadow.set_mindays'](name, mindays) + if key == "mindays": + __salt__["shadow.set_mindays"](name, mindays) continue - if key == 'maxdays': - __salt__['shadow.set_maxdays'](name, maxdays) + if key == "maxdays": + __salt__["shadow.set_maxdays"](name, maxdays) continue - if key == 'inactdays': - __salt__['shadow.set_inactdays'](name, inactdays) + if key == "inactdays": + __salt__["shadow.set_inactdays"](name, inactdays) continue - if key == 'warndays': - __salt__['shadow.set_warndays'](name, warndays) + if key == "warndays": + __salt__["shadow.set_warndays"](name, warndays) continue - if key == 'expire': - __salt__['shadow.set_expire'](name, expire) + if key == "expire": + __salt__["shadow.set_expire"](name, expire) continue - if key == 'win_homedrive': - __salt__['user.update'](name=name, homedrive=val) + if key == "win_homedrive": + __salt__["user.update"](name=name, homedrive=val) continue - if key == 'win_profile': - __salt__['user.update'](name=name, profile=val) + if key == "win_profile": + __salt__["user.update"](name=name, profile=val) continue - if key == 'win_logonscript': - __salt__['user.update'](name=name, logonscript=val) + if key == "win_logonscript": + __salt__["user.update"](name=name, logonscript=val) continue - if key == 'win_description': - __salt__['user.update'](name=name, description=val) + if key == "win_description": + __salt__["user.update"](name=name, description=val) continue - if key == 'groups': - __salt__['user.ch{0}'.format(key)]( - name, val, not remove_groups - ) + if key == "groups": + __salt__["user.ch{0}".format(key)](name, val, not remove_groups) else: - __salt__['user.ch{0}'.format(key)](name, val) + __salt__["user.ch{0}".format(key)](name, val) - post = __salt__['user.info'](name) + post = __salt__["user.info"](name) spost = {} - if 'shadow.info' in __salt__ and lshad['passwd'] != password: - spost = __salt__['shadow.info'](name) - if __grains__['kernel'] in ('OpenBSD', 'FreeBSD'): - lcpost = __salt__['user.get_loginclass'](name) + if "shadow.info" in __salt__ and lshad["passwd"] != password: + spost = __salt__["shadow.info"](name) + if __grains__["kernel"] in ("OpenBSD", "FreeBSD"): + lcpost = __salt__["user.get_loginclass"](name) # See if anything changed for key in post: if post[key] != pre[key]: - ret['changes'][key] = post[key] - if 'shadow.info' in __salt__: + ret["changes"][key] = post[key] + if "shadow.info" in __salt__: for key in spost: if lshad[key] != spost[key]: - if key == 'passwd': - ret['changes'][key] = 'XXX-REDACTED-XXX' + if key == "passwd": + ret["changes"][key] = "XXX-REDACTED-XXX" else: - ret['changes'][key] = spost[key] - if __grains__['kernel'] in ('OpenBSD', 'FreeBSD') and lcpost != lcpre: - ret['changes']['loginclass'] = lcpost - if ret['changes']: - ret['comment'] = 'Updated user {0}'.format(name) - changes = _changes(name, - uid, - gid, - groups, - present_optgroups, - remove_groups, - home, - createhome, - password, - enforce_password, - empty_password, - shell, - fullname, - roomnumber, - workphone, - homephone, - other, - loginclass, - date, - mindays, - maxdays, - inactdays, - warndays, - expire, - win_homedrive, - win_profile, - win_logonscript, - win_description, - allow_uid_change=True, - allow_gid_change=True) + ret["changes"][key] = spost[key] + if __grains__["kernel"] in ("OpenBSD", "FreeBSD") and lcpost != lcpre: + ret["changes"]["loginclass"] = lcpost + if ret["changes"]: + ret["comment"] = "Updated user {0}".format(name) + changes = _changes( + name, + uid, + gid, + groups, + present_optgroups, + remove_groups, + home, + createhome, + password, + enforce_password, + empty_password, + shell, + fullname, + roomnumber, + workphone, + homephone, + other, + loginclass, + date, + mindays, + maxdays, + inactdays, + warndays, + expire, + win_homedrive, + win_profile, + win_logonscript, + win_description, + allow_uid_change=True, + allow_gid_change=True, + ) # allow_uid_change and allow_gid_change passed as True to avoid race # conditions where a uid/gid is modified outside of Salt. If an # unauthorized change was requested, it would have been caught the # first time we ran _changes(). if changes: - ret['comment'] = 'These values could not be changed: {0}'.format( - changes - ) - ret['result'] = False + ret["comment"] = "These values could not be changed: {0}".format(changes) + ret["result"] = False return ret if changes is False: # The user is not present, make it! - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0} set to be added'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User {0} set to be added".format(name) return ret if groups and present_optgroups: groups.extend(present_optgroups) @@ -726,146 +748,176 @@ def present(name, # Setup params specific to Linux and Windows to be passed to the # add.user function if not salt.utils.platform.is_windows(): - params = {'name': name, - 'uid': uid, - 'gid': gid, - 'groups': groups, - 'home': home, - 'shell': shell, - 'unique': unique, - 'system': system, - 'fullname': fullname, - 'roomnumber': roomnumber, - 'workphone': workphone, - 'homephone': homephone, - 'other': other, - 'createhome': createhome, - 'nologinit': nologinit, - 'loginclass': loginclass} + params = { + "name": name, + "uid": uid, + "gid": gid, + "groups": groups, + "home": home, + "shell": shell, + "unique": unique, + "system": system, + "fullname": fullname, + "roomnumber": roomnumber, + "workphone": workphone, + "homephone": homephone, + "other": other, + "createhome": createhome, + "nologinit": nologinit, + "loginclass": loginclass, + } else: - params = ({'name': name, - 'password': password, - 'fullname': fullname, - 'description': win_description, - 'groups': groups, - 'home': home, - 'homedrive': win_homedrive, - 'profile': win_profile, - 'logonscript': win_logonscript}) + params = { + "name": name, + "password": password, + "fullname": fullname, + "description": win_description, + "groups": groups, + "home": home, + "homedrive": win_homedrive, + "profile": win_profile, + "logonscript": win_logonscript, + } - if __salt__['user.add'](**params): - ret['comment'] = 'New user {0} created'.format(name) - ret['changes'] = __salt__['user.info'](name) + if __salt__["user.add"](**params): + ret["comment"] = "New user {0} created".format(name) + ret["changes"] = __salt__["user.info"](name) if not createhome: # pwd incorrectly reports presence of home - ret['changes']['home'] = '' - if 'shadow.info' in __salt__ \ - and not salt.utils.platform.is_windows() \ - and not salt.utils.platform.is_darwin(): + ret["changes"]["home"] = "" + if ( + "shadow.info" in __salt__ + and not salt.utils.platform.is_windows() + and not salt.utils.platform.is_darwin() + ): if password and not empty_password: - __salt__['shadow.set_password'](name, password) - spost = __salt__['shadow.info'](name) - if spost['passwd'] != password: - ret['comment'] = 'User {0} created but failed to set' \ - ' password to' \ - ' {1}'.format(name, 'XXX-REDACTED-XXX') - ret['result'] = False - ret['changes']['password'] = 'XXX-REDACTED-XXX' + __salt__["shadow.set_password"](name, password) + spost = __salt__["shadow.info"](name) + if spost["passwd"] != password: + ret["comment"] = ( + "User {0} created but failed to set" + " password to" + " {1}".format(name, "XXX-REDACTED-XXX") + ) + ret["result"] = False + ret["changes"]["password"] = "XXX-REDACTED-XXX" if empty_password and not password: - __salt__['shadow.del_password'](name) - spost = __salt__['shadow.info'](name) - if spost['passwd'] != '': - ret['comment'] = 'User {0} created but failed to ' \ - 'empty password'.format(name) - ret['result'] = False - ret['changes']['password'] = '' + __salt__["shadow.del_password"](name) + spost = __salt__["shadow.info"](name) + if spost["passwd"] != "": + ret["comment"] = ( + "User {0} created but failed to " + "empty password".format(name) + ) + ret["result"] = False + ret["changes"]["password"] = "" if date is not None: - __salt__['shadow.set_date'](name, date) - spost = __salt__['shadow.info'](name) - if spost['lstchg'] != date: - ret['comment'] = 'User {0} created but failed to set' \ - ' last change date to' \ - ' {1}'.format(name, date) - ret['result'] = False - ret['changes']['date'] = date + __salt__["shadow.set_date"](name, date) + spost = __salt__["shadow.info"](name) + if spost["lstchg"] != date: + ret["comment"] = ( + "User {0} created but failed to set" + " last change date to" + " {1}".format(name, date) + ) + ret["result"] = False + ret["changes"]["date"] = date if mindays: - __salt__['shadow.set_mindays'](name, mindays) - spost = __salt__['shadow.info'](name) - if spost['min'] != mindays: - ret['comment'] = 'User {0} created but failed to set' \ - ' minimum days to' \ - ' {1}'.format(name, mindays) - ret['result'] = False - ret['changes']['mindays'] = mindays + __salt__["shadow.set_mindays"](name, mindays) + spost = __salt__["shadow.info"](name) + if spost["min"] != mindays: + ret["comment"] = ( + "User {0} created but failed to set" + " minimum days to" + " {1}".format(name, mindays) + ) + ret["result"] = False + ret["changes"]["mindays"] = mindays if maxdays: - __salt__['shadow.set_maxdays'](name, maxdays) - spost = __salt__['shadow.info'](name) - if spost['max'] != maxdays: - ret['comment'] = 'User {0} created but failed to set' \ - ' maximum days to' \ - ' {1}'.format(name, maxdays) - ret['result'] = False - ret['changes']['maxdays'] = maxdays + __salt__["shadow.set_maxdays"](name, maxdays) + spost = __salt__["shadow.info"](name) + if spost["max"] != maxdays: + ret["comment"] = ( + "User {0} created but failed to set" + " maximum days to" + " {1}".format(name, maxdays) + ) + ret["result"] = False + ret["changes"]["maxdays"] = maxdays if inactdays: - __salt__['shadow.set_inactdays'](name, inactdays) - spost = __salt__['shadow.info'](name) - if spost['inact'] != inactdays: - ret['comment'] = 'User {0} created but failed to set' \ - ' inactive days to' \ - ' {1}'.format(name, inactdays) - ret['result'] = False - ret['changes']['inactdays'] = inactdays + __salt__["shadow.set_inactdays"](name, inactdays) + spost = __salt__["shadow.info"](name) + if spost["inact"] != inactdays: + ret["comment"] = ( + "User {0} created but failed to set" + " inactive days to" + " {1}".format(name, inactdays) + ) + ret["result"] = False + ret["changes"]["inactdays"] = inactdays if warndays: - __salt__['shadow.set_warndays'](name, warndays) - spost = __salt__['shadow.info'](name) - if spost['warn'] != warndays: - ret['comment'] = 'User {0} created but failed to set' \ - ' warn days to' \ - ' {1}'.format(name, warndays) - ret['result'] = False - ret['changes']['warndays'] = warndays + __salt__["shadow.set_warndays"](name, warndays) + spost = __salt__["shadow.info"](name) + if spost["warn"] != warndays: + ret["comment"] = ( + "User {0} created but failed to set" + " warn days to" + " {1}".format(name, warndays) + ) + ret["result"] = False + ret["changes"]["warndays"] = warndays if expire: - __salt__['shadow.set_expire'](name, expire) - spost = __salt__['shadow.info'](name) - if spost['expire'] != expire: - ret['comment'] = 'User {0} created but failed to set' \ - ' expire days to' \ - ' {1}'.format(name, expire) - ret['result'] = False - ret['changes']['expire'] = expire + __salt__["shadow.set_expire"](name, expire) + spost = __salt__["shadow.info"](name) + if spost["expire"] != expire: + ret["comment"] = ( + "User {0} created but failed to set" + " expire days to" + " {1}".format(name, expire) + ) + ret["result"] = False + ret["changes"]["expire"] = expire elif salt.utils.platform.is_windows(): if password and not empty_password: - if not __salt__['user.setpassword'](name, password): - ret['comment'] = 'User {0} created but failed to set' \ - ' password to' \ - ' {1}'.format(name, 'XXX-REDACTED-XXX') - ret['result'] = False - ret['changes']['passwd'] = 'XXX-REDACTED-XXX' + if not __salt__["user.setpassword"](name, password): + ret["comment"] = ( + "User {0} created but failed to set" + " password to" + " {1}".format(name, "XXX-REDACTED-XXX") + ) + ret["result"] = False + ret["changes"]["passwd"] = "XXX-REDACTED-XXX" if expire: - __salt__['shadow.set_expire'](name, expire) - spost = __salt__['shadow.info'](name) - if salt.utils.dateutils.strftime(spost['expire']) != salt.utils.dateutils.strftime(expire): - ret['comment'] = 'User {0} created but failed to set' \ - ' expire days to' \ - ' {1}'.format(name, expire) - ret['result'] = False - ret['changes']['expiration_date'] = spost['expire'] + __salt__["shadow.set_expire"](name, expire) + spost = __salt__["shadow.info"](name) + if salt.utils.dateutils.strftime( + spost["expire"] + ) != salt.utils.dateutils.strftime(expire): + ret["comment"] = ( + "User {0} created but failed to set" + " expire days to" + " {1}".format(name, expire) + ) + ret["result"] = False + ret["changes"]["expiration_date"] = spost["expire"] elif salt.utils.platform.is_darwin() and password and not empty_password: - if not __salt__['shadow.set_password'](name, password): - ret['comment'] = 'User {0} created but failed to set' \ - ' password to' \ - ' {1}'.format(name, 'XXX-REDACTED-XXX') - ret['result'] = False - ret['changes']['passwd'] = 'XXX-REDACTED-XXX' + if not __salt__["shadow.set_password"](name, password): + ret["comment"] = ( + "User {0} created but failed to set" + " password to" + " {1}".format(name, "XXX-REDACTED-XXX") + ) + ret["result"] = False + ret["changes"]["passwd"] = "XXX-REDACTED-XXX" else: - ret['comment'] = 'Failed to create new user {0}'.format(name) - ret['result'] = False + ret["comment"] = "Failed to create new user {0}".format(name) + ret["result"] = False return ret def absent(name, purge=False, force=False): - ''' + """ Ensure that the named user is absent name @@ -879,33 +931,30 @@ def absent(name, purge=False, force=False): If the user is logged in, the absent state will fail. Set the force option to True to remove the user even if they are logged in. Not supported in FreeBSD and Solaris, Default is ``False``. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - lusr = __salt__['user.info'](name) + lusr = __salt__["user.info"](name) if lusr: # The user is present, make it not present - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User {0} set for removal'.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User {0} set for removal".format(name) return ret beforegroups = set(salt.utils.user.get_group_list(name)) - ret['result'] = __salt__['user.delete'](name, purge, force) - aftergroups = set([g for g in beforegroups if __salt__['group.info'](g)]) - if ret['result']: - ret['changes'] = {} + ret["result"] = __salt__["user.delete"](name, purge, force) + aftergroups = set([g for g in beforegroups if __salt__["group.info"](g)]) + if ret["result"]: + ret["changes"] = {} for g in beforegroups - aftergroups: - ret['changes']['{0} group'.format(g)] = 'removed' - ret['changes'][name] = 'removed' - ret['comment'] = 'Removed user {0}'.format(name) + ret["changes"]["{0} group".format(g)] = "removed" + ret["changes"][name] = "removed" + ret["comment"] = "Removed user {0}".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to remove user {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to remove user {0}".format(name) return ret - ret['comment'] = 'User {0} is not present'.format(name) + ret["comment"] = "User {0} is not present".format(name) return ret diff --git a/salt/states/vagrant.py b/salt/states/vagrant.py index db5e15799a2..92e32b9baf9 100644 --- a/salt/states/vagrant.py +++ b/salt/states/vagrant.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Manage Vagrant VMs ================== @@ -48,34 +48,34 @@ value will select the ``primary`` (or only) machine in the Vagrantfile. table: sdb # ... using this table name. create_table: True # if not present -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import fnmatch +import salt.ext.six as six + # Import Salt libs import salt.utils.args from salt.exceptions import CommandExecutionError, SaltInvocationError -import salt.ext.six as six -__virtualname__ = 'vagrant' +__virtualname__ = "vagrant" def __virtual__(): - ''' + """ Only if vagrant module is available. :return: - ''' - - if 'vagrant.version' in __salt__: + """ + if "vagrant.version" in __salt__: return __virtualname__ - return False + return (False, "vagrant module could not be loaded") def _vagrant_call(node, function, section, comment, status_when_done=None, **kwargs): - ''' + """ Helper to call the vagrant functions. Wildcards supported. :param node: The Salt-id or wildcard @@ -84,51 +84,51 @@ def _vagrant_call(node, function, section, comment, status_when_done=None, **kwa :param comment: what the state reply should say :param status_when_done: the Vagrant status expected for this state :return: the dictionary for the state reply - ''' - ret = {'name': node, 'changes': {}, 'result': True, 'comment': ''} + """ + ret = {"name": node, "changes": {}, "result": True, "comment": ""} targeted_nodes = [] if isinstance(node, six.string_types): try: # use shortcut if a single node name - if __salt__['vagrant.get_vm_info'](node): + if __salt__["vagrant.get_vm_info"](node): targeted_nodes = [node] except SaltInvocationError: pass if not targeted_nodes: # the shortcut failed, do this the hard way - all_domains = __salt__['vagrant.list_domains']() + all_domains = __salt__["vagrant.list_domains"]() targeted_nodes = fnmatch.filter(all_domains, node) changed_nodes = [] ignored_nodes = [] for node in targeted_nodes: if status_when_done: try: - present_state = __salt__['vagrant.vm_state'](node)[0] - if present_state['state'] == status_when_done: + present_state = __salt__["vagrant.vm_state"](node)[0] + if present_state["state"] == status_when_done: continue # no change is needed except (IndexError, SaltInvocationError, CommandExecutionError): pass try: - response = __salt__['vagrant.{0}'.format(function)](node, **kwargs) + response = __salt__["vagrant.{0}".format(function)](node, **kwargs) if isinstance(response, dict): - response = response['name'] - changed_nodes.append({'node': node, function: response}) + response = response["name"] + changed_nodes.append({"node": node, function: response}) except (SaltInvocationError, CommandExecutionError) as err: - ignored_nodes.append({'node': node, 'issue': six.text_type(err)}) + ignored_nodes.append({"node": node, "issue": six.text_type(err)}) if not changed_nodes: - ret['result'] = True - ret['comment'] = 'No changes seen' + ret["result"] = True + ret["comment"] = "No changes seen" if ignored_nodes: - ret['changes'] = {'ignored': ignored_nodes} + ret["changes"] = {"ignored": ignored_nodes} else: - ret['changes'] = {section: changed_nodes} - ret['comment'] = comment + ret["changes"] = {section: changed_nodes} + ret["comment"] = comment return ret def running(name, **kwargs): - r''' + r""" Defines and starts a new VM with specified arguments, or restart a VM (or group of VMs). (Runs ``vagrant up``.) @@ -163,68 +163,70 @@ def running(name, **kwargs): - vagrant_runas: my_username - machine: machine1 - ''' - if '*' in name or '?' in name: + """ + if "*" in name or "?" in name: - return _vagrant_call(name, 'start', 'restarted', - "Machine has been restarted", "running") + return _vagrant_call( + name, "start", "restarted", "Machine has been restarted", "running" + ) else: - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': '{0} is already running'.format(name) - } + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "{0} is already running".format(name), + } try: - info = __salt__['vagrant.vm_state'](name) - if info[0]['state'] != 'running': - __salt__['vagrant.start'](name) - ret['changes'][name] = 'Machine started' - ret['comment'] = 'Node {0} started'.format(name) + info = __salt__["vagrant.vm_state"](name) + if info[0]["state"] != "running": + __salt__["vagrant.start"](name) + ret["changes"][name] = "Machine started" + ret["comment"] = "Node {0} started".format(name) except (SaltInvocationError, CommandExecutionError): # there was no viable existing machine to start ret, kwargs = _find_init_change(name, ret, **kwargs) - kwargs['start'] = True - __salt__['vagrant.init'](name, **kwargs) - ret['changes'][name] = 'Node defined and started' - ret['comment'] = 'Node {0} defined and started'.format(name) + kwargs["start"] = True + __salt__["vagrant.init"](name, **kwargs) + ret["changes"][name] = "Node defined and started" + ret["comment"] = "Node {0} defined and started".format(name) return ret def _find_init_change(name, ret, **kwargs): - ''' + """ look for changes from any previous init of machine. :return: modified ret and kwargs - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) - if 'vm' in kwargs: - kwargs.update(kwargs.pop('vm')) + if "vm" in kwargs: + kwargs.update(kwargs.pop("vm")) # the state processing eats 'runas' so we rename - kwargs['runas'] = kwargs.pop('vagrant_runas', '') + kwargs["runas"] = kwargs.pop("vagrant_runas", "") try: - vm_ = __salt__['vagrant.get_vm_info'](name) + vm_ = __salt__["vagrant.get_vm_info"](name) except SaltInvocationError: vm_ = {} for key, value in kwargs.items(): - ret['changes'][key] = {'old': None, 'new': value} + ret["changes"][key] = {"old": None, "new": value} if vm_: # test for changed values for key in vm_: - value = vm_[key] or '' # supply a blank if value is None - if key != 'name': # will be missing in kwargs - new = kwargs.get(key, '') + value = vm_[key] or "" # supply a blank if value is None + if key != "name": # will be missing in kwargs + new = kwargs.get(key, "") if new != value: - if key == 'machine' and new == '': + if key == "machine" and new == "": continue # we don't know the default machine name - ret['changes'][key] = {'old': value, 'new': new} + ret["changes"][key] = {"old": value, "new": new} return ret, kwargs def initialized(name, **kwargs): - r''' + r""" Defines a new VM with specified arguments, but does not start it. :param name: the Salt_id node name you wish your VM to have. @@ -259,29 +261,30 @@ def initialized(name, **kwargs): start_nodes: vagrant.start: - name: node_name? - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'The VM is already correctly defined' - } + """ + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "The VM is already correctly defined", + } # define a machine to start later ret, kwargs = _find_init_change(name, ret, **kwargs) - if ret['changes'] == {}: + if ret["changes"] == {}: return ret - kwargs['start'] = False - __salt__['vagrant.init'](name, **kwargs) - ret['changes'][name] = 'Node initialized' - ret['comment'] = 'Node {0} defined but not started.'.format(name) + kwargs["start"] = False + __salt__["vagrant.init"](name, **kwargs) + ret["changes"][name] = "Node initialized" + ret["comment"] = "Node {0} defined but not started.".format(name) return ret def stopped(name): - ''' + """ Stops a VM (or VMs) by shutting it (them) down nicely. (Runs ``vagrant halt``) :param name: May be a Salt_id node, or a POSIX-style wildcard string. @@ -290,14 +293,15 @@ def stopped(name): node_name: vagrant.stopped - ''' + """ - return _vagrant_call(name, 'shutdown', 'stopped', - 'Machine has been shut down', 'poweroff') + return _vagrant_call( + name, "shutdown", "stopped", "Machine has been shut down", "poweroff" + ) def powered_off(name): - ''' + """ Stops a VM (or VMs) by power off. (Runs ``vagrant halt``.) This method is provided for compatibility with other VM-control @@ -309,14 +313,15 @@ def powered_off(name): node_name: vagrant.unpowered - ''' + """ - return _vagrant_call(name, 'stop', 'unpowered', - 'Machine has been powered off', 'poweroff') + return _vagrant_call( + name, "stop", "unpowered", "Machine has been powered off", "poweroff" + ) def destroyed(name): - ''' + """ Stops a VM (or VMs) and removes all references to it (them). (Runs ``vagrant destroy``.) Subsequent re-use of the same machine will requere another operation of ``vagrant.running`` @@ -328,14 +333,13 @@ def destroyed(name): node_name: vagrant.destroyed - ''' + """ - return _vagrant_call(name, 'destroy', 'destroyed', - 'Machine has been removed') + return _vagrant_call(name, "destroy", "destroyed", "Machine has been removed") def paused(name): - ''' + """ Stores the state of a VM (or VMs) for fast restart. (Runs ``vagrant suspend``.) :param name: May be a Salt_id node or a POSIX-style wildcard string. @@ -344,14 +348,13 @@ def paused(name): node_name: vagrant.paused - ''' + """ - return _vagrant_call(name, 'pause', 'paused', - 'Machine has been suspended', 'saved') + return _vagrant_call(name, "pause", "paused", "Machine has been suspended", "saved") def rebooted(name): - ''' + """ Reboots a running, paused, or stopped VM (or VMs). (Runs ``vagrant reload``.) The will re-run the provisioning @@ -362,6 +365,6 @@ def rebooted(name): node_name: vagrant.reloaded - ''' + """ - return _vagrant_call(name, 'reboot', 'rebooted', 'Machine has been reloaded') + return _vagrant_call(name, "reboot", "rebooted", "Machine has been reloaded") diff --git a/salt/states/vault.py b/salt/states/vault.py index 8579df22507..a4060b9d2f5 100644 --- a/salt/states/vault.py +++ b/salt/states/vault.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" States for managing Hashicorp Vault. Currently handles policies. Configuration instructions are documented in the execution module docs. @@ -9,17 +9,18 @@ Currently handles policies. Configuration instructions are documented in the exe .. versionadded:: 2017.7.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging + import difflib +import logging log = logging.getLogger(__name__) def policy_present(name, rules): - ''' + """ Ensure a Vault policy with the given name and rules is present. name @@ -42,82 +43,86 @@ def policy_present(name, rules): policy = "write" } - ''' + """ url = "v1/sys/policy/{0}".format(name) - response = __utils__['vault.make_request']('GET', url) + response = __utils__["vault.make_request"]("GET", url) try: if response.status_code == 200: - return _handle_existing_policy(name, rules, response.json()['rules']) + return _handle_existing_policy(name, rules, response.json()["rules"]) elif response.status_code == 404: return _create_new_policy(name, rules) else: response.raise_for_status() except Exception as e: # pylint: disable=broad-except return { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Failed to get policy: {0}'.format(e) + "name": name, + "changes": {}, + "result": False, + "comment": "Failed to get policy: {0}".format(e), } def _create_new_policy(name, rules): - if __opts__['test']: + if __opts__["test"]: return { - 'name': name, - 'changes': {name: {'old': '', 'new': rules}}, - 'result': None, - 'comment': 'Policy would be created' + "name": name, + "changes": {name: {"old": "", "new": rules}}, + "result": None, + "comment": "Policy would be created", } - payload = {'rules': rules} + payload = {"rules": rules} url = "v1/sys/policy/{0}".format(name) - response = __utils__['vault.make_request']('PUT', url, json=payload) + response = __utils__["vault.make_request"]("PUT", url, json=payload) if response.status_code not in [200, 204]: return { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Failed to create policy: {0}'.format(response.reason) + "name": name, + "changes": {}, + "result": False, + "comment": "Failed to create policy: {0}".format(response.reason), } return { - 'name': name, - 'result': True, - 'changes': {name: {'old': None, 'new': rules}}, - 'comment': 'Policy was created' + "name": name, + "result": True, + "changes": {name: {"old": None, "new": rules}}, + "comment": "Policy was created", } def _handle_existing_policy(name, new_rules, existing_rules): - ret = {'name': name} + ret = {"name": name} if new_rules == existing_rules: - ret['result'] = True - ret['changes'] = {} - ret['comment'] = 'Policy exists, and has the correct content' + ret["result"] = True + ret["changes"] = {} + ret["comment"] = "Policy exists, and has the correct content" return ret - change = ''.join(difflib.unified_diff(existing_rules.splitlines(True), new_rules.splitlines(True))) - if __opts__['test']: - ret['result'] = None - ret['changes'] = {name: {'change': change}} - ret['comment'] = 'Policy would be changed' + change = "".join( + difflib.unified_diff( + existing_rules.splitlines(True), new_rules.splitlines(True) + ) + ) + if __opts__["test"]: + ret["result"] = None + ret["changes"] = {name: {"change": change}} + ret["comment"] = "Policy would be changed" return ret - payload = {'rules': new_rules} + payload = {"rules": new_rules} url = "v1/sys/policy/{0}".format(name) - response = __utils__['vault.make_request']('PUT', url, json=payload) + response = __utils__["vault.make_request"]("PUT", url, json=payload) if response.status_code not in [200, 204]: return { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': 'Failed to change policy: {0}'.format(response.reason) + "name": name, + "changes": {}, + "result": False, + "comment": "Failed to change policy: {0}".format(response.reason), } - ret['result'] = True - ret['changes'] = {name: {'change': change}} - ret['comment'] = 'Policy was updated' + ret["result"] = True + ret["changes"] = {name: {"change": change}} + ret["comment"] = "Policy was updated" return ret diff --git a/salt/states/vbox_guest.py b/salt/states/vbox_guest.py index f11405a9fd1..ba6bd5731ac 100644 --- a/salt/states/vbox_guest.py +++ b/salt/states/vbox_guest.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" VirtualBox Guest Additions installer state -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -11,7 +11,7 @@ log = logging.getLogger(__name__) def additions_installed(name, reboot=False, upgrade_os=False): - ''' + """ Ensure that the VirtualBox Guest Additions are installed. Uses the CD, connected by VirtualBox. @@ -23,37 +23,37 @@ def additions_installed(name, reboot=False, upgrade_os=False): upgrade_os : False Upgrade OS (to ensure the latests version of kernel and developer tools installed). - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} - current_state = __salt__['vbox_guest.additions_version']() + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + current_state = __salt__["vbox_guest.additions_version"]() if current_state: - ret['result'] = True - ret['comment'] = 'System already in the correct state' + ret["result"] = True + ret["comment"] = "System already in the correct state" return ret - if __opts__['test']: - ret['comment'] = ('The state of VirtualBox Guest Additions will be ' - 'changed.') - ret['changes'] = { - 'old': current_state, - 'new': True, + if __opts__["test"]: + ret["comment"] = "The state of VirtualBox Guest Additions will be " "changed." + ret["changes"] = { + "old": current_state, + "new": True, } - ret['result'] = None + ret["result"] = None return ret - new_state = __salt__['vbox_guest.additions_install']( - reboot=reboot, upgrade_os=upgrade_os) + new_state = __salt__["vbox_guest.additions_install"]( + reboot=reboot, upgrade_os=upgrade_os + ) - ret['comment'] = 'The state of VirtualBox Guest Additions was changed!' - ret['changes'] = { - 'old': current_state, - 'new': new_state, + ret["comment"] = "The state of VirtualBox Guest Additions was changed!" + ret["changes"] = { + "old": current_state, + "new": new_state, } - ret['result'] = bool(new_state) + ret["result"] = bool(new_state) return ret def additions_removed(name, force=False): - ''' + """ Ensure that the VirtualBox Guest Additions are removed. Uses the CD, connected by VirtualBox. @@ -65,36 +65,35 @@ def additions_removed(name, force=False): reference. force Force VirtualBox Guest Additions removing. - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} - current_state = __salt__['vbox_guest.additions_version']() + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + current_state = __salt__["vbox_guest.additions_version"]() if not current_state: - ret['result'] = True - ret['comment'] = 'System already in the correct state' + ret["result"] = True + ret["comment"] = "System already in the correct state" return ret - if __opts__['test']: - ret['comment'] = ('The state of VirtualBox Guest Additions will be ' - 'changed.') - ret['changes'] = { - 'old': current_state, - 'new': True, + if __opts__["test"]: + ret["comment"] = "The state of VirtualBox Guest Additions will be " "changed." + ret["changes"] = { + "old": current_state, + "new": True, } - ret['result'] = None + ret["result"] = None return ret - new_state = __salt__['vbox_guest.additions_remove'](force=force) + new_state = __salt__["vbox_guest.additions_remove"](force=force) - ret['comment'] = 'The state of VirtualBox Guest Additions was changed!' - ret['changes'] = { - 'old': current_state, - 'new': new_state, + ret["comment"] = "The state of VirtualBox Guest Additions was changed!" + ret["changes"] = { + "old": current_state, + "new": new_state, } - ret['result'] = bool(new_state) + ret["result"] = bool(new_state) return ret def grant_access_to_shared_folders_to(name, users=None): - ''' + """ Grant access to auto-mounted shared folders to the users. User is specified by its name. To grant access for several users use @@ -105,33 +104,37 @@ def grant_access_to_shared_folders_to(name, users=None): users List of names of users to grant access to auto-mounted shared folders to. If specified, `name` will not be taken into account. - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} - current_state = __salt__['vbox_guest.list_shared_folders_users']() + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + current_state = __salt__["vbox_guest.list_shared_folders_users"]() if users is None: users = [name] if current_state == users: - ret['result'] = True - ret['comment'] = 'System already in the correct state' + ret["result"] = True + ret["comment"] = "System already in the correct state" return ret - if __opts__['test']: - ret['comment'] = ('List of users who have access to auto-mounted ' - 'shared folders will be changed') - ret['changes'] = { - 'old': current_state, - 'new': users, + if __opts__["test"]: + ret["comment"] = ( + "List of users who have access to auto-mounted " + "shared folders will be changed" + ) + ret["changes"] = { + "old": current_state, + "new": users, } - ret['result'] = None + ret["result"] = None return ret - new_state = __salt__['vbox_guest.grant_access_to_shared_folders_to']( - name=name, users=users) + new_state = __salt__["vbox_guest.grant_access_to_shared_folders_to"]( + name=name, users=users + ) - ret['comment'] = ('List of users who have access to auto-mounted shared ' - 'folders was changed') - ret['changes'] = { - 'old': current_state, - 'new': new_state, + ret["comment"] = ( + "List of users who have access to auto-mounted shared " "folders was changed" + ) + ret["changes"] = { + "old": current_state, + "new": new_state, } - ret['result'] = True + ret["result"] = True return ret diff --git a/salt/states/victorops.py b/salt/states/victorops.py index d3e1caf9034..6f46195a0c2 100644 --- a/salt/states/victorops.py +++ b/salt/states/victorops.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Create an Event in VictorOps ============================ @@ -15,21 +15,23 @@ VictorOps service during state runs. - message_type: 'CRITICAL' - entity_id: 'webserver/diskspace' - state_message: 'Webserver diskspace is low.' -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the victorops module is available in __salt__ - ''' - return 'victorops' if 'victorops.create_event' in __salt__ else False + """ + if "victorops.create_event" in __salt__: + return "victorops" + return (False, "victorops module could not be loaded") -def create_event(name, message_type, routing_key='everyone', **kwargs): - ''' +def create_event(name, message_type, routing_key="everyone", **kwargs): + """ Create an event on the VictorOps service .. code-block:: yaml @@ -91,25 +93,22 @@ def create_event(name, message_type, routing_key='everyone', **kwargs): ack_author The user that acknowledged the incident. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - if __opts__['test']: - ret['comment'] = 'Need to create event: {0}'.format(name) + if __opts__["test"]: + ret["comment"] = "Need to create event: {0}".format(name) return ret - res = __salt__['victorops.create_event']( - message_type=message_type, - routing_key=routing_key, - **kwargs + res = __salt__["victorops.create_event"]( + message_type=message_type, routing_key=routing_key, **kwargs ) - if res['result'] == 'success': - ret['result'] = True - ret['comment'] = 'Created event: {0} for entity {1}'.format(name, res['entity_id']) + if res["result"] == "success": + ret["result"] = True + ret["comment"] = "Created event: {0} for entity {1}".format( + name, res["entity_id"] + ) else: - ret['result'] = False - ret['comment'] = 'Failed to create event: {0}'.format(res['message']) + ret["result"] = False + ret["comment"] = "Failed to create event: {0}".format(res["message"]) return ret diff --git a/salt/states/virt.py b/salt/states/virt.py index 500509fcc0a..3651ba74b75 100644 --- a/salt/states/virt.py +++ b/salt/states/virt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage virt =========== @@ -10,19 +10,14 @@ for the generation and signing of certificates for systems running libvirt: libvirt_keys: virt.keys -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import fnmatch import os -try: - import libvirt # pylint: disable=import-error - HAS_LIBVIRT = True -except ImportError: - HAS_LIBVIRT = False - # Import Salt libs import salt.utils.args import salt.utils.files @@ -33,23 +28,30 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six -__virtualname__ = 'virt' +try: + import libvirt # pylint: disable=import-error + + HAS_LIBVIRT = True +except ImportError: + HAS_LIBVIRT = False + + +__virtualname__ = "virt" def __virtual__(): - ''' + """ Only if virt module is available. :return: - ''' - - if 'virt.node_info' in __salt__: + """ + if "virt.node_info" in __salt__: return __virtualname__ - return False + return (False, "virt module could not be loaded") -def keys(name, basepath='/etc/pki', **kwargs): - ''' +def keys(name, basepath="/etc/pki", **kwargs): + """ Manage libvirt keys. name @@ -89,65 +91,68 @@ def keys(name, basepath='/etc/pki', **kwargs): .. versionadded:: 2018.3.0 - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} # Grab all kwargs to make them available as pillar values # rename them to something hopefully unique to avoid # overriding anything existing pillar_kwargs = {} for key, value in six.iteritems(kwargs): - pillar_kwargs['ext_pillar_virt.{0}'.format(key)] = value + pillar_kwargs["ext_pillar_virt.{0}".format(key)] = value - pillar = __salt__['pillar.ext']({'libvirt': '_'}, pillar_kwargs) + pillar = __salt__["pillar.ext"]({"libvirt": "_"}, pillar_kwargs) paths = { - 'serverkey': os.path.join(basepath, 'libvirt', - 'private', 'serverkey.pem'), - 'servercert': os.path.join(basepath, 'libvirt', - 'servercert.pem'), - 'clientkey': os.path.join(basepath, 'libvirt', - 'private', 'clientkey.pem'), - 'clientcert': os.path.join(basepath, 'libvirt', - 'clientcert.pem'), - 'cacert': os.path.join(basepath, 'CA', 'cacert.pem') + "serverkey": os.path.join(basepath, "libvirt", "private", "serverkey.pem"), + "servercert": os.path.join(basepath, "libvirt", "servercert.pem"), + "clientkey": os.path.join(basepath, "libvirt", "private", "clientkey.pem"), + "clientcert": os.path.join(basepath, "libvirt", "clientcert.pem"), + "cacert": os.path.join(basepath, "CA", "cacert.pem"), } for key in paths: - p_key = 'libvirt.{0}.pem'.format(key) + p_key = "libvirt.{0}.pem".format(key) if p_key not in pillar: continue if not os.path.exists(os.path.dirname(paths[key])): os.makedirs(os.path.dirname(paths[key])) if os.path.isfile(paths[key]): - with salt.utils.files.fopen(paths[key], 'r') as fp_: + with salt.utils.files.fopen(paths[key], "r") as fp_: if salt.utils.stringutils.to_unicode(fp_.read()) != pillar[p_key]: - ret['changes'][key] = 'update' + ret["changes"][key] = "update" else: - ret['changes'][key] = 'new' + ret["changes"][key] = "new" - if not ret['changes']: - ret['comment'] = 'All keys are correct' - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Libvirt keys are set to be updated' - ret['changes'] = {} + if not ret["changes"]: + ret["comment"] = "All keys are correct" + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = "Libvirt keys are set to be updated" + ret["changes"] = {} else: - for key in ret['changes']: - with salt.utils.files.fopen(paths[key], 'w+') as fp_: + for key in ret["changes"]: + with salt.utils.files.fopen(paths[key], "w+") as fp_: fp_.write( - salt.utils.stringutils.to_str( - pillar['libvirt.{0}.pem'.format(key)] - ) + salt.utils.stringutils.to_str(pillar["libvirt.{0}.pem".format(key)]) ) - ret['comment'] = 'Updated libvirt certs and keys' + ret["comment"] = "Updated libvirt certs and keys" return ret -def _virt_call(domain, function, section, comment, state=None, - connection=None, username=None, password=None, **kwargs): - ''' +def _virt_call( + domain, + function, + section, + comment, + state=None, + connection=None, + username=None, + password=None, + **kwargs +): + """ Helper to call the virt functions. Wildcards supported. :param domain: the domain to apply the function on. Can contain wildcards. @@ -156,9 +161,9 @@ def _virt_call(domain, function, section, comment, state=None, :param comment: comment to return :param state: the expected final state of the VM. If None the VM state won't be checked. :return: the salt state return - ''' - ret = {'name': domain, 'changes': {}, 'result': True, 'comment': ''} - targeted_domains = fnmatch.filter(__salt__['virt.list_domains'](), domain) + """ + ret = {"name": domain, "changes": {}, "result": True, "comment": ""} + targeted_domains = fnmatch.filter(__salt__["virt.list_domains"](), domain) changed_domains = list() ignored_domains = list() noaction_domains = list() @@ -167,35 +172,39 @@ def _virt_call(domain, function, section, comment, state=None, action_needed = True # If a state has been provided, use it to see if we have something to do if state is not None: - domain_state = __salt__['virt.vm_state'](targeted_domain) + domain_state = __salt__["virt.vm_state"](targeted_domain) action_needed = domain_state.get(targeted_domain) != state if action_needed: - response = __salt__['virt.{0}'.format(function)](targeted_domain, - connection=connection, - username=username, - password=password, - **kwargs) + response = __salt__["virt.{0}".format(function)]( + targeted_domain, + connection=connection, + username=username, + password=password, + **kwargs + ) if isinstance(response, dict): - response = response['name'] - changed_domains.append({'domain': targeted_domain, function: response}) + response = response["name"] + changed_domains.append({"domain": targeted_domain, function: response}) else: noaction_domains.append(targeted_domain) except libvirt.libvirtError as err: - ignored_domains.append({'domain': targeted_domain, 'issue': six.text_type(err)}) + ignored_domains.append( + {"domain": targeted_domain, "issue": six.text_type(err)} + ) if not changed_domains: - ret['result'] = not ignored_domains and bool(targeted_domains) - ret['comment'] = 'No changes had happened' + ret["result"] = not ignored_domains and bool(targeted_domains) + ret["comment"] = "No changes had happened" if ignored_domains: - ret['changes'] = {'ignored': ignored_domains} + ret["changes"] = {"ignored": ignored_domains} else: - ret['changes'] = {section: changed_domains} - ret['comment'] = comment + ret["changes"] = {section: changed_domains} + ret["comment"] = comment return ret def stopped(name, connection=None, username=None, password=None): - ''' + """ Stops a VM by shutting it down nicely. .. versionadded:: 2016.3.0 @@ -214,14 +223,22 @@ def stopped(name, connection=None, username=None, password=None): domain_name: virt.stopped - ''' + """ - return _virt_call(name, 'shutdown', 'stopped', 'Machine has been shut down', state='shutdown', - connection=connection, username=username, password=password) + return _virt_call( + name, + "shutdown", + "stopped", + "Machine has been shut down", + state="shutdown", + connection=connection, + username=username, + password=password, + ) def powered_off(name, connection=None, username=None, password=None): - ''' + """ Stops a VM by power off. .. versionadded:: 2016.3.0 @@ -240,33 +257,42 @@ def powered_off(name, connection=None, username=None, password=None): domain_name: virt.stopped - ''' - return _virt_call(name, 'stop', 'unpowered', 'Machine has been powered off', state='shutdown', - connection=connection, username=username, password=password) + """ + return _virt_call( + name, + "stop", + "unpowered", + "Machine has been powered off", + state="shutdown", + connection=connection, + username=username, + password=password, + ) -def running(name, - cpu=None, - mem=None, - image=None, - vm_type=None, - disk_profile=None, - disks=None, - nic_profile=None, - interfaces=None, - graphics=None, - seed=True, - install=True, - pub_key=None, - priv_key=None, - update=False, - connection=None, - username=None, - password=None, - os_type=None, - arch=None, - boot=None): - ''' +def running( + name, + cpu=None, + mem=None, + vm_type=None, + disk_profile=None, + disks=None, + nic_profile=None, + interfaces=None, + graphics=None, + seed=True, + install=True, + pub_key=None, + priv_key=None, + update=False, + connection=None, + username=None, + password=None, + os_type=None, + arch=None, + boot=None, +): + """ Starts an existing guest, or defines and starts a new VM with specified arguments. .. versionadded:: 2016.3.0 @@ -274,9 +300,6 @@ def running(name, :param name: name of the virtual machine to run :param cpu: number of CPUs for the virtual machine to create :param mem: amount of memory in MiB for the new virtual machine - :param image: disk image to use for the first disk of the new VM - - .. deprecated:: 2019.2.0 :param vm_type: force virtual machine type for the new VM. The default value is taken from the host capabilities. This could be useful for example to use ``'qemu'`` type instead of the ``'kvm'`` one. @@ -406,100 +429,108 @@ def running(name, type: address address: 192.168.0.125 - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': '{0} is running'.format(name) - } + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "{0} is running".format(name), + } try: try: - domain_state = __salt__['virt.vm_state'](name) - if domain_state.get(name) != 'running': - action_msg = 'started' + domain_state = __salt__["virt.vm_state"](name) + if domain_state.get(name) != "running": + action_msg = "started" if update: - status = __salt__['virt.update'](name, - cpu=cpu, - mem=mem, - disk_profile=disk_profile, - disks=disks, - nic_profile=nic_profile, - interfaces=interfaces, - graphics=graphics, - live=False, - connection=connection, - username=username, - password=password, - boot=boot) - if status['definition']: - action_msg = 'updated and started' - __salt__['virt.start'](name) - ret['changes'][name] = 'Domain {0}'.format(action_msg) - ret['comment'] = 'Domain {0} {1}'.format(name, action_msg) + status = __salt__["virt.update"]( + name, + cpu=cpu, + mem=mem, + disk_profile=disk_profile, + disks=disks, + nic_profile=nic_profile, + interfaces=interfaces, + graphics=graphics, + live=False, + connection=connection, + username=username, + password=password, + boot=boot, + ) + if status["definition"]: + action_msg = "updated and started" + __salt__["virt.start"](name) + ret["changes"][name] = "Domain {0}".format(action_msg) + ret["comment"] = "Domain {0} {1}".format(name, action_msg) else: if update: - status = __salt__['virt.update'](name, - cpu=cpu, - mem=mem, - disk_profile=disk_profile, - disks=disks, - nic_profile=nic_profile, - interfaces=interfaces, - graphics=graphics, - connection=connection, - username=username, - password=password, - boot=boot) - ret['changes'][name] = status - if status.get('errors', None): - ret['comment'] = 'Domain {0} updated, but some live update(s) failed'.format(name) - elif not status['definition']: - ret['comment'] = 'Domain {0} exists and is running'.format(name) + status = __salt__["virt.update"]( + name, + cpu=cpu, + mem=mem, + disk_profile=disk_profile, + disks=disks, + nic_profile=nic_profile, + interfaces=interfaces, + graphics=graphics, + connection=connection, + username=username, + password=password, + boot=boot, + ) + ret["changes"][name] = status + if status.get("errors", None): + ret[ + "comment" + ] = "Domain {0} updated, but some live update(s) failed".format( + name + ) + elif not status["definition"]: + ret["comment"] = "Domain {0} exists and is running".format(name) else: - ret['comment'] = 'Domain {0} updated, restart to fully apply the changes'.format(name) + ret[ + "comment" + ] = "Domain {0} updated, restart to fully apply the changes".format( + name + ) else: - ret['comment'] = 'Domain {0} exists and is running'.format(name) + ret["comment"] = "Domain {0} exists and is running".format(name) except CommandExecutionError: - if image: - salt.utils.versions.warn_until( - 'Sodium', - '\'image\' parameter has been deprecated. Rather use the \'disks\' parameter ' - 'to override or define the image. \'image\' will be removed in {version}.' - ) - __salt__['virt.init'](name, - cpu=cpu, - mem=mem, - os_type=os_type, - arch=arch, - image=image, - hypervisor=vm_type, - disk=disk_profile, - disks=disks, - nic=nic_profile, - interfaces=interfaces, - graphics=graphics, - seed=seed, - install=install, - pub_key=pub_key, - priv_key=priv_key, - connection=connection, - username=username, - password=password, - boot=boot) - ret['changes'][name] = 'Domain defined and started' - ret['comment'] = 'Domain {0} defined and started'.format(name) + __salt__["virt.init"]( + name, + cpu=cpu, + mem=mem, + os_type=os_type, + arch=arch, + hypervisor=vm_type, + disk=disk_profile, + disks=disks, + nic=nic_profile, + interfaces=interfaces, + graphics=graphics, + seed=seed, + install=install, + pub_key=pub_key, + priv_key=priv_key, + connection=connection, + username=username, + password=password, + boot=boot, + ) + ret["changes"][name] = "Domain defined and started" + ret["comment"] = "Domain {0} defined and started".format(name) except libvirt.libvirtError as err: # Something bad happened when starting / updating the VM, report it - ret['comment'] = six.text_type(err) - ret['result'] = False + ret["comment"] = six.text_type(err) + ret["result"] = False return ret def snapshot(name, suffix=None, connection=None, username=None, password=None): - ''' + """ Takes a snapshot of a particular VM or by a UNIX-style wildcard. .. versionadded:: 2016.3.0 @@ -523,15 +554,23 @@ def snapshot(name, suffix=None, connection=None, username=None, password=None): domain*: virt.snapshot: - suffix: periodic - ''' + """ - return _virt_call(name, 'snapshot', 'saved', 'Snapshot has been taken', suffix=suffix, - connection=connection, username=username, password=password) + return _virt_call( + name, + "snapshot", + "saved", + "Snapshot has been taken", + suffix=suffix, + connection=connection, + username=username, + password=password, + ) # Deprecated states def rebooted(name, connection=None, username=None, password=None): - ''' + """ Reboots VMs .. versionadded:: 2016.3.0 @@ -547,14 +586,21 @@ def rebooted(name, connection=None, username=None, password=None): :param password: password to connect with, overriding defaults .. versionadded:: 2019.2.0 - ''' + """ - return _virt_call(name, 'reboot', 'rebooted', "Machine has been rebooted", - connection=connection, username=username, password=password) + return _virt_call( + name, + "reboot", + "rebooted", + "Machine has been rebooted", + connection=connection, + username=username, + password=password, + ) def unpowered(name): - ''' + """ .. deprecated:: 2016.3.0 Use :py:func:`~salt.modules.virt.powered_off` instead. @@ -566,13 +612,13 @@ def unpowered(name): domain_name: virt.stopped - ''' + """ - return _virt_call(name, 'stop', 'unpowered', 'Machine has been powered off') + return _virt_call(name, "stop", "unpowered", "Machine has been powered off") def saved(name, suffix=None): - ''' + """ .. deprecated:: 2016.3.0 Use :py:func:`~salt.modules.virt.snapshot` instead. @@ -589,13 +635,17 @@ def saved(name, suffix=None): domain*: virt.saved: - suffix: periodic - ''' + """ - return _virt_call(name, 'snapshot', 'saved', 'Snapshots has been taken', suffix=suffix) + return _virt_call( + name, "snapshot", "saved", "Snapshots has been taken", suffix=suffix + ) -def reverted(name, snapshot=None, cleanup=False): # pylint: disable=redefined-outer-name - ''' +def reverted( + name, snapshot=None, cleanup=False +): # pylint: disable=redefined-outer-name + """ .. deprecated:: 2016.3.0 Reverts to the particular snapshot. @@ -612,59 +662,71 @@ def reverted(name, snapshot=None, cleanup=False): # pylint: disable=redefined-o virt.reverted: - snapshot: snapshot_name - cleanup: False - ''' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} try: - domains = fnmatch.filter(__salt__['virt.list_domains'](), name) + domains = fnmatch.filter(__salt__["virt.list_domains"](), name) if not domains: - ret['comment'] = 'No domains found for criteria "{0}"'.format(name) + ret["comment"] = 'No domains found for criteria "{0}"'.format(name) else: ignored_domains = list() if len(domains) > 1: - ret['changes'] = {'reverted': list()} + ret["changes"] = {"reverted": list()} for domain in domains: result = {} try: - result = __salt__['virt.revert_snapshot'](domain, snapshot=snapshot, cleanup=cleanup) - result = {'domain': domain, 'current': result['reverted'], 'deleted': result['deleted']} + result = __salt__["virt.revert_snapshot"]( + domain, snapshot=snapshot, cleanup=cleanup + ) + result = { + "domain": domain, + "current": result["reverted"], + "deleted": result["deleted"], + } except CommandExecutionError as err: if len(domains) > 1: - ignored_domains.append({'domain': domain, 'issue': six.text_type(err)}) + ignored_domains.append( + {"domain": domain, "issue": six.text_type(err)} + ) if len(domains) > 1: if result: - ret['changes']['reverted'].append(result) + ret["changes"]["reverted"].append(result) else: - ret['changes'] = result + ret["changes"] = result break - ret['result'] = len(domains) != len(ignored_domains) - if ret['result']: - ret['comment'] = 'Domain{0} has been reverted'.format(len(domains) > 1 and "s" or "") + ret["result"] = len(domains) != len(ignored_domains) + if ret["result"]: + ret["comment"] = "Domain{0} has been reverted".format( + len(domains) > 1 and "s" or "" + ) if ignored_domains: - ret['changes']['ignored'] = ignored_domains - if not ret['changes']['reverted']: - ret['changes'].pop('reverted') + ret["changes"]["ignored"] = ignored_domains + if not ret["changes"]["reverted"]: + ret["changes"].pop("reverted") except libvirt.libvirtError as err: - ret['comment'] = six.text_type(err) + ret["comment"] = six.text_type(err) except CommandExecutionError as err: - ret['comment'] = six.text_type(err) + ret["comment"] = six.text_type(err) return ret -def network_running(name, - bridge, - forward, - vport=None, - tag=None, - ipv4_config=None, - ipv6_config=None, - autostart=True, - connection=None, - username=None, - password=None): - ''' +def network_running( + name, + bridge, + forward, + vport=None, + tag=None, + ipv4_config=None, + ipv6_config=None, + autostart=True, + connection=None, + username=None, + password=None, +): + """ Defines and starts a new network with specified arguments. :param bridge: Bridge name @@ -726,55 +788,59 @@ def network_running(name, end: 192.168.42.150 - autostart: True - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - info = __salt__['virt.network_info'](name, connection=connection, username=username, password=password) + info = __salt__["virt.network_info"]( + name, connection=connection, username=username, password=password + ) if info: - if info[name]['active']: - ret['comment'] = 'Network {0} exists and is running'.format(name) + if info[name]["active"]: + ret["comment"] = "Network {0} exists and is running".format(name) else: - __salt__['virt.network_start'](name, connection=connection, username=username, password=password) - ret['changes'][name] = 'Network started' - ret['comment'] = 'Network {0} started'.format(name) + __salt__["virt.network_start"]( + name, connection=connection, username=username, password=password + ) + ret["changes"][name] = "Network started" + ret["comment"] = "Network {0} started".format(name) else: - __salt__['virt.network_define'](name, - bridge, - forward, - vport=vport, - tag=tag, - ipv4_config=ipv4_config, - ipv6_config=ipv6_config, - autostart=autostart, - start=True, - connection=connection, - username=username, - password=password) - ret['changes'][name] = 'Network defined and started' - ret['comment'] = 'Network {0} defined and started'.format(name) + __salt__["virt.network_define"]( + name, + bridge, + forward, + vport=vport, + tag=tag, + ipv4_config=ipv4_config, + ipv6_config=ipv6_config, + autostart=autostart, + start=True, + connection=connection, + username=username, + password=password, + ) + ret["changes"][name] = "Network defined and started" + ret["comment"] = "Network {0} defined and started".format(name) except libvirt.libvirtError as err: - ret['result'] = False - ret['comment'] = err.get_error_message() + ret["result"] = False + ret["comment"] = err.get_error_message() return ret -def pool_running(name, - ptype=None, - target=None, - permissions=None, - source=None, - transient=False, - autostart=True, - connection=None, - username=None, - password=None): - ''' +def pool_running( + name, + ptype=None, + target=None, + permissions=None, + source=None, + transient=False, + autostart=True, + connection=None, + username=None, + password=None, +): + """ Defines and starts a new pool with specified arguments. .. versionadded:: 2019.2.0 @@ -819,134 +885,170 @@ def pool_running(name, format: cifs - autostart: True - ''' - ret = {'name': name, - 'changes': {}, - 'result': True if not __opts__['test'] else None, - 'comment': '' - } + """ + ret = { + "name": name, + "changes": {}, + "result": True if not __opts__["test"] else None, + "comment": "", + } try: - info = __salt__['virt.pool_info'](name, connection=connection, username=username, password=password) + info = __salt__["virt.pool_info"]( + name, connection=connection, username=username, password=password + ) needs_autostart = False if info: - needs_autostart = info[name]['autostart'] and not autostart or not info[name]['autostart'] and autostart + needs_autostart = ( + info[name]["autostart"] + and not autostart + or not info[name]["autostart"] + and autostart + ) # Update can happen for both running and stopped pools - needs_update = __salt__['virt.pool_update'](name, - ptype=ptype, - target=target, - permissions=permissions, - source_devices=(source or {}).get('devices'), - source_dir=(source or {}).get('dir'), - source_initiator=(source or {}).get('initiator'), - source_adapter=(source or {}).get('adapter'), - source_hosts=(source or {}).get('hosts'), - source_auth=(source or {}).get('auth'), - source_name=(source or {}).get('name'), - source_format=(source or {}).get('format'), - test=True, - connection=connection, - username=username, - password=password) + needs_update = __salt__["virt.pool_update"]( + name, + ptype=ptype, + target=target, + permissions=permissions, + source_devices=(source or {}).get("devices"), + source_dir=(source or {}).get("dir"), + source_initiator=(source or {}).get("initiator"), + source_adapter=(source or {}).get("adapter"), + source_hosts=(source or {}).get("hosts"), + source_auth=(source or {}).get("auth"), + source_name=(source or {}).get("name"), + source_format=(source or {}).get("format"), + test=True, + connection=connection, + username=username, + password=password, + ) if needs_update: - if not __opts__['test']: - __salt__['virt.pool_update'](name, - ptype=ptype, - target=target, - permissions=permissions, - source_devices=(source or {}).get('devices'), - source_dir=(source or {}).get('dir'), - source_initiator=(source or {}).get('initiator'), - source_adapter=(source or {}).get('adapter'), - source_hosts=(source or {}).get('hosts'), - source_auth=(source or {}).get('auth'), - source_name=(source or {}).get('name'), - source_format=(source or {}).get('format'), - connection=connection, - username=username, - password=password) + if not __opts__["test"]: + __salt__["virt.pool_update"]( + name, + ptype=ptype, + target=target, + permissions=permissions, + source_devices=(source or {}).get("devices"), + source_dir=(source or {}).get("dir"), + source_initiator=(source or {}).get("initiator"), + source_adapter=(source or {}).get("adapter"), + source_hosts=(source or {}).get("hosts"), + source_auth=(source or {}).get("auth"), + source_name=(source or {}).get("name"), + source_format=(source or {}).get("format"), + connection=connection, + username=username, + password=password, + ) action = "started" - if info[name]['state'] == 'running': + if info[name]["state"] == "running": action = "restarted" - if not __opts__['test']: - __salt__['virt.pool_stop'](name, connection=connection, username=username, password=password) + if not __opts__["test"]: + __salt__["virt.pool_stop"]( + name, + connection=connection, + username=username, + password=password, + ) - if not __opts__['test']: - __salt__['virt.pool_build'](name, connection=connection, username=username, password=password) - __salt__['virt.pool_start'](name, connection=connection, username=username, password=password) + if not __opts__["test"]: + __salt__["virt.pool_build"]( + name, + connection=connection, + username=username, + password=password, + ) + __salt__["virt.pool_start"]( + name, + connection=connection, + username=username, + password=password, + ) - autostart_str = ', autostart flag changed' if needs_autostart else '' - ret['changes'][name] = 'Pool updated, built{0} and {1}'.format(autostart_str, action) - ret['comment'] = 'Pool {0} updated, built{1} and {2}'.format(name, autostart_str, action) + autostart_str = ", autostart flag changed" if needs_autostart else "" + ret["changes"][name] = "Pool updated, built{0} and {1}".format( + autostart_str, action + ) + ret["comment"] = "Pool {0} updated, built{1} and {2}".format( + name, autostart_str, action + ) else: - if info[name]['state'] == 'running': - ret['comment'] = 'Pool {0} unchanged and is running'.format(name) - ret['result'] = True + if info[name]["state"] == "running": + ret["comment"] = "Pool {0} unchanged and is running".format(name) + ret["result"] = True else: - ret['changes'][name] = 'Pool started' - ret['comment'] = 'Pool {0} started'.format(name) - if not __opts__['test']: - __salt__['virt.pool_start'](name, connection=connection, username=username, password=password) + ret["changes"][name] = "Pool started" + ret["comment"] = "Pool {0} started".format(name) + if not __opts__["test"]: + __salt__["virt.pool_start"]( + name, + connection=connection, + username=username, + password=password, + ) else: needs_autostart = autostart - if not __opts__['test']: - __salt__['virt.pool_define'](name, - ptype=ptype, - target=target, - permissions=permissions, - source_devices=(source or {}).get('devices'), - source_dir=(source or {}).get('dir'), - source_initiator=(source or {}).get('initiator'), - source_adapter=(source or {}).get('adapter'), - source_hosts=(source or {}).get('hosts'), - source_auth=(source or {}).get('auth'), - source_name=(source or {}).get('name'), - source_format=(source or {}).get('format'), - transient=transient, - start=False, - connection=connection, - username=username, - password=password) + if not __opts__["test"]: + __salt__["virt.pool_define"]( + name, + ptype=ptype, + target=target, + permissions=permissions, + source_devices=(source or {}).get("devices"), + source_dir=(source or {}).get("dir"), + source_initiator=(source or {}).get("initiator"), + source_adapter=(source or {}).get("adapter"), + source_hosts=(source or {}).get("hosts"), + source_auth=(source or {}).get("auth"), + source_name=(source or {}).get("name"), + source_format=(source or {}).get("format"), + transient=transient, + start=False, + connection=connection, + username=username, + password=password, + ) - __salt__['virt.pool_build'](name, - connection=connection, - username=username, - password=password) + __salt__["virt.pool_build"]( + name, connection=connection, username=username, password=password + ) - __salt__['virt.pool_start'](name, - connection=connection, - username=username, - password=password) + __salt__["virt.pool_start"]( + name, connection=connection, username=username, password=password + ) if needs_autostart: - ret['changes'][name] = 'Pool defined, started and marked for autostart' - ret['comment'] = 'Pool {0} defined, started and marked for autostart'.format(name) + ret["changes"][name] = "Pool defined, started and marked for autostart" + ret[ + "comment" + ] = "Pool {0} defined, started and marked for autostart".format(name) else: - ret['changes'][name] = 'Pool defined and started' - ret['comment'] = 'Pool {0} defined and started'.format(name) + ret["changes"][name] = "Pool defined and started" + ret["comment"] = "Pool {0} defined and started".format(name) if needs_autostart: - if not __opts__['test']: - __salt__['virt.pool_set_autostart'](name, - state='on' if autostart else 'off', - connection=connection, - username=username, - password=password) + if not __opts__["test"]: + __salt__["virt.pool_set_autostart"]( + name, + state="on" if autostart else "off", + connection=connection, + username=username, + password=password, + ) except libvirt.libvirtError as err: - ret['comment'] = err.get_error_message() - ret['result'] = False + ret["comment"] = err.get_error_message() + ret["result"] = False return ret -def pool_deleted(name, - purge=False, - connection=None, - username=None, - password=None): - ''' +def pool_deleted(name, purge=False, connection=None, username=None, password=None): + """ Deletes a virtual storage pool. :param name: the name of the pool to delete. @@ -971,81 +1073,107 @@ def pool_deleted(name, - purge: True .. versionadded:: 3000 - ''' - ret = {'name': name, 'changes': {}, 'result': True, 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} try: - info = __salt__['virt.pool_info'](name, connection=connection, username=username, password=password) + info = __salt__["virt.pool_info"]( + name, connection=connection, username=username, password=password + ) if info: - ret['changes']['stopped'] = False - ret['changes']['deleted'] = False - ret['changes']['undefined'] = False - ret['changes']['deleted_volumes'] = [] + ret["changes"]["stopped"] = False + ret["changes"]["deleted"] = False + ret["changes"]["undefined"] = False + ret["changes"]["deleted_volumes"] = [] unsupported = [] - if info[name]['state'] == 'running': + if info[name]["state"] == "running": if purge: - unsupported_volume_delete = ['iscsi', 'iscsi-direct', 'mpath', 'scsi'] - if info[name]['type'] not in unsupported_volume_delete: - __salt__['virt.pool_refresh'](name, - connection=connection, - username=username, - password=password) - volumes = __salt__['virt.pool_list_volumes'](name, - connection=connection, - username=username, - password=password) + unsupported_volume_delete = [ + "iscsi", + "iscsi-direct", + "mpath", + "scsi", + ] + if info[name]["type"] not in unsupported_volume_delete: + __salt__["virt.pool_refresh"]( + name, + connection=connection, + username=username, + password=password, + ) + volumes = __salt__["virt.pool_list_volumes"]( + name, + connection=connection, + username=username, + password=password, + ) for volume in volumes: # Not supported for iSCSI and SCSI drivers - deleted = __opts__['test'] - if not __opts__['test']: - deleted = __salt__['virt.volume_delete'](name, - volume, - connection=connection, - username=username, - password=password) + deleted = __opts__["test"] + if not __opts__["test"]: + deleted = __salt__["virt.volume_delete"]( + name, + volume, + connection=connection, + username=username, + password=password, + ) if deleted: - ret['changes']['deleted_volumes'].append(volume) + ret["changes"]["deleted_volumes"].append(volume) else: - unsupported.append('deleting volume') + unsupported.append("deleting volume") - if not __opts__['test']: - ret['changes']['stopped'] = __salt__['virt.pool_stop'](name, - connection=connection, - username=username, - password=password) + if not __opts__["test"]: + ret["changes"]["stopped"] = __salt__["virt.pool_stop"]( + name, + connection=connection, + username=username, + password=password, + ) else: - ret['changes']['stopped'] = True + ret["changes"]["stopped"] = True if purge: - supported_pool_delete = ['dir', 'fs', 'netfs', 'logical', 'vstorage', 'zfs'] - if info[name]['type'] in supported_pool_delete: - if not __opts__['test']: - ret['changes']['deleted'] = __salt__['virt.pool_delete'](name, - connection=connection, - username=username, - password=password) + supported_pool_delete = [ + "dir", + "fs", + "netfs", + "logical", + "vstorage", + "zfs", + ] + if info[name]["type"] in supported_pool_delete: + if not __opts__["test"]: + ret["changes"]["deleted"] = __salt__["virt.pool_delete"]( + name, + connection=connection, + username=username, + password=password, + ) else: - ret['changes']['deleted'] = True + ret["changes"]["deleted"] = True else: - unsupported.append('deleting pool') + unsupported.append("deleting pool") - if not __opts__['test']: - ret['changes']['undefined'] = __salt__['virt.pool_undefine'](name, - connection=connection, - username=username, - password=password) + if not __opts__["test"]: + ret["changes"]["undefined"] = __salt__["virt.pool_undefine"]( + name, connection=connection, username=username, password=password + ) else: - ret['changes']['undefined'] = True - ret['result'] = None + ret["changes"]["undefined"] = True + ret["result"] = None if unsupported: - ret['comment'] = 'Unsupported actions for pool of type "{0}": {1}'.format(info[name]['type'], - ', '.join(unsupported)) + ret[ + "comment" + ] = 'Unsupported actions for pool of type "{0}": {1}'.format( + info[name]["type"], ", ".join(unsupported) + ) else: - ret['comment'] = 'Storage pool could not be found: {0}'.format(name) + ret["comment"] = "Storage pool could not be found: {0}".format(name) except libvirt.libvirtError as err: - ret['comment'] = 'Failed deleting pool: {0}'.format(err.get_error_message()) - ret['result'] = False + ret["comment"] = "Failed deleting pool: {0}".format(err.get_error_message()) + ret["result"] = False return ret diff --git a/salt/states/virtualenv_mod.py b/salt/states/virtualenv_mod.py index 4bbe95162f6..a052432ecee 100644 --- a/salt/states/virtualenv_mod.py +++ b/salt/states/virtualenv_mod.py @@ -1,20 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Setup of Python virtualenv sandboxes. .. versionadded:: 0.17.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os -# Import Salt libs -import salt.version import salt.utils.functools import salt.utils.platform import salt.utils.versions + +# Import Salt libs +import salt.version from salt.exceptions import CommandExecutionError, CommandNotFoundError # Import 3rd-party libs @@ -23,46 +25,50 @@ from salt.ext import six log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'virtualenv' +__virtualname__ = "virtualenv" def __virtual__(): - return __virtualname__ + if "virtualenv.create" in __salt__: + return __virtualname__ + return (False, "virtualenv module could not be loaded") -def managed(name, - venv_bin=None, - requirements=None, - system_site_packages=False, - distribute=False, - use_wheel=False, - clear=False, - python=None, - extra_search_dir=None, - never_download=None, - prompt=None, - user=None, - cwd=None, - index_url=None, - extra_index_url=None, - pre_releases=False, - no_deps=False, - pip_download=None, - pip_download_cache=None, - pip_exists_action=None, - pip_ignore_installed=False, - proxy=None, - use_vt=False, - env_vars=None, - no_use_wheel=False, - pip_upgrade=False, - pip_pkgs=None, - pip_no_cache_dir=False, - pip_cache_dir=None, - process_dependency_links=False, - no_binary=None, - **kwargs): - ''' +def managed( + name, + venv_bin=None, + requirements=None, + system_site_packages=False, + distribute=False, + use_wheel=False, + clear=False, + python=None, + extra_search_dir=None, + never_download=None, + prompt=None, + user=None, + cwd=None, + index_url=None, + extra_index_url=None, + pre_releases=False, + no_deps=False, + pip_download=None, + pip_download_cache=None, + pip_exists_action=None, + pip_ignore_installed=False, + proxy=None, + use_vt=False, + env_vars=None, + no_use_wheel=False, + pip_upgrade=False, + pip_pkgs=None, + pip_no_cache_dir=False, + pip_cache_dir=None, + process_dependency_links=False, + no_binary=None, + **kwargs +): + """ Create a virtualenv and optionally manage it with pip name @@ -79,7 +85,7 @@ def managed(name, use_wheel: False Prefer wheel archives (requires pip >= 1.4). - python : None + python: None Python executable used to build the virtualenv user: None @@ -134,67 +140,66 @@ def managed(name, - requirements: salt://REQUIREMENTS.txt - env_vars: PATH_VAR: '/usr/local/bin/' - ''' - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - if 'virtualenv.create' not in __salt__: - ret['result'] = False - ret['comment'] = 'Virtualenv was not detected on this system' + if "virtualenv.create" not in __salt__: + ret["result"] = False + ret["comment"] = "Virtualenv was not detected on this system" return ret if salt.utils.platform.is_windows(): - venv_py = os.path.join(name, 'Scripts', 'python.exe') + venv_py = os.path.join(name, "Scripts", "python.exe") else: - venv_py = os.path.join(name, 'bin', 'python') + venv_py = os.path.join(name, "bin", "python") venv_exists = os.path.exists(venv_py) # Bail out early if the specified requirements file can't be found - if requirements and requirements.startswith('salt://'): - cached_requirements = __salt__['cp.is_cached'](requirements, __env__) + if requirements and requirements.startswith("salt://"): + cached_requirements = __salt__["cp.is_cached"](requirements, __env__) if not cached_requirements: # It's not cached, let's cache it. - cached_requirements = __salt__['cp.cache_file']( - requirements, __env__ - ) + cached_requirements = __salt__["cp.cache_file"](requirements, __env__) # Check if the master version has changed. - if cached_requirements and __salt__['cp.hash_file'](requirements, __env__) != \ - __salt__['cp.hash_file'](cached_requirements, __env__): - cached_requirements = __salt__['cp.cache_file']( - requirements, __env__ - ) + if cached_requirements and __salt__["cp.hash_file"]( + requirements, __env__ + ) != __salt__["cp.hash_file"](cached_requirements, __env__): + cached_requirements = __salt__["cp.cache_file"](requirements, __env__) if not cached_requirements: - ret.update({ - 'result': False, - 'comment': 'pip requirements file \'{0}\' not found'.format( - requirements - ) - }) + ret.update( + { + "result": False, + "comment": "pip requirements file '{0}' not found".format( + requirements + ), + } + ) return ret requirements = cached_requirements # If it already exists, grab the version for posterity if venv_exists and clear: - ret['changes']['cleared_packages'] = \ - __salt__['pip.freeze'](bin_env=name) - ret['changes']['old'] = \ - __salt__['cmd.run_stderr']('{0} -V'.format(venv_py)).strip('\n') + ret["changes"]["cleared_packages"] = __salt__["pip.freeze"](bin_env=name) + ret["changes"]["old"] = __salt__["cmd.run_stderr"]( + "{0} -V".format(venv_py) + ).strip("\n") # Create (or clear) the virtualenv - if __opts__['test']: + if __opts__["test"]: if venv_exists and clear: - ret['result'] = None - ret['comment'] = 'Virtualenv {0} is set to be cleared'.format(name) + ret["result"] = None + ret["comment"] = "Virtualenv {0} is set to be cleared".format(name) return ret if venv_exists and not clear: - ret['comment'] = 'Virtualenv {0} is already created'.format(name) + ret["comment"] = "Virtualenv {0} is already created".format(name) return ret - ret['result'] = None - ret['comment'] = 'Virtualenv {0} is set to be created'.format(name) + ret["result"] = None + ret["comment"] = "Virtualenv {0} is set to be created".format(name) return ret if not venv_exists or (venv_exists and clear): try: - venv_ret = __salt__['virtualenv.create']( + venv_ret = __salt__["virtualenv.create"]( name, venv_bin=venv_bin, system_site_packages=system_site_packages, @@ -209,91 +214,106 @@ def managed(name, **kwargs ) except CommandNotFoundError as err: - ret['result'] = False - ret['comment'] = 'Failed to create virtualenv: {0}'.format(err) + ret["result"] = False + ret["comment"] = "Failed to create virtualenv: {0}".format(err) return ret - if venv_ret['retcode'] != 0: - ret['result'] = False - ret['comment'] = venv_ret['stdout'] + venv_ret['stderr'] + if venv_ret["retcode"] != 0: + ret["result"] = False + ret["comment"] = venv_ret["stdout"] + venv_ret["stderr"] return ret - ret['result'] = True - ret['changes']['new'] = __salt__['cmd.run_stderr']( - '{0} -V'.format(venv_py)).strip('\n') + ret["result"] = True + ret["changes"]["new"] = __salt__["cmd.run_stderr"]( + "{0} -V".format(venv_py) + ).strip("\n") if clear: - ret['comment'] = 'Cleared existing virtualenv' + ret["comment"] = "Cleared existing virtualenv" else: - ret['comment'] = 'Created new virtualenv' + ret["comment"] = "Created new virtualenv" elif venv_exists: - ret['comment'] = 'virtualenv exists' + ret["comment"] = "virtualenv exists" # Check that the pip binary supports the 'use_wheel' option if use_wheel: - min_version = '1.4' - max_version = '9.0.3' - cur_version = __salt__['pip.version'](bin_env=name) - too_low = salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version) - too_high = salt.utils.versions.compare(ver1=cur_version, oper='>', ver2=max_version) + min_version = "1.4" + max_version = "9.0.3" + cur_version = __salt__["pip.version"](bin_env=name) + too_low = salt.utils.versions.compare( + ver1=cur_version, oper="<", ver2=min_version + ) + too_high = salt.utils.versions.compare( + ver1=cur_version, oper=">", ver2=max_version + ) if too_low or too_high: - ret['result'] = False - ret['comment'] = ('The \'use_wheel\' option is only supported in ' - 'pip between {0} and {1}. The version of pip detected ' - 'was {2}.').format(min_version, max_version, cur_version) + ret["result"] = False + ret["comment"] = ( + "The 'use_wheel' option is only supported in " + "pip between {0} and {1}. The version of pip detected " + "was {2}." + ).format(min_version, max_version, cur_version) return ret # Check that the pip binary supports the 'no_use_wheel' option if no_use_wheel: - min_version = '1.4' - max_version = '9.0.3' - cur_version = __salt__['pip.version'](bin_env=name) - too_low = salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version) - too_high = salt.utils.versions.compare(ver1=cur_version, oper='>', ver2=max_version) + min_version = "1.4" + max_version = "9.0.3" + cur_version = __salt__["pip.version"](bin_env=name) + too_low = salt.utils.versions.compare( + ver1=cur_version, oper="<", ver2=min_version + ) + too_high = salt.utils.versions.compare( + ver1=cur_version, oper=">", ver2=max_version + ) if too_low or too_high: - ret['result'] = False - ret['comment'] = ('The \'no_use_wheel\' option is only supported in ' - 'pip between {0} and {1}. The version of pip detected ' - 'was {2}.').format(min_version, max_version, cur_version) + ret["result"] = False + ret["comment"] = ( + "The 'no_use_wheel' option is only supported in " + "pip between {0} and {1}. The version of pip detected " + "was {2}." + ).format(min_version, max_version, cur_version) return ret # Check that the pip binary supports the 'no_binary' option if no_binary: - min_version = '7.0.0' - cur_version = __salt__['pip.version'](bin_env=name) - too_low = salt.utils.versions.compare(ver1=cur_version, oper='<', ver2=min_version) + min_version = "7.0.0" + cur_version = __salt__["pip.version"](bin_env=name) + too_low = salt.utils.versions.compare( + ver1=cur_version, oper="<", ver2=min_version + ) if too_low: - ret['result'] = False - ret['comment'] = ('The \'no_binary\' option is only supported in ' - 'pip {0} and newer. The version of pip detected ' - 'was {1}.').format(min_version, cur_version) + ret["result"] = False + ret["comment"] = ( + "The 'no_binary' option is only supported in " + "pip {0} and newer. The version of pip detected " + "was {1}." + ).format(min_version, cur_version) return ret # Populate the venv via a requirements file if requirements or pip_pkgs: try: - before = set(__salt__['pip.freeze'](bin_env=name, user=user, use_vt=use_vt)) + before = set(__salt__["pip.freeze"](bin_env=name, user=user, use_vt=use_vt)) except CommandExecutionError as exc: - ret['result'] = False - ret['comment'] = exc.strerror + ret["result"] = False + ret["comment"] = exc.strerror return ret if requirements: if isinstance(requirements, six.string_types): - req_canary = requirements.split(',')[0] + req_canary = requirements.split(",")[0] elif isinstance(requirements, list): req_canary = requirements[0] else: - raise TypeError( - 'pip requirements must be either a string or a list' - ) + raise TypeError("pip requirements must be either a string or a list") if req_canary != os.path.abspath(req_canary): cwd = os.path.dirname(os.path.abspath(req_canary)) - pip_ret = __salt__['pip.install']( + pip_ret = __salt__["pip.install"]( pkgs=pip_pkgs, requirements=requirements, process_dependency_links=process_dependency_links, @@ -319,22 +339,23 @@ def managed(name, cache_dir=pip_cache_dir, **kwargs ) - ret['result'] &= pip_ret['retcode'] == 0 - if pip_ret['retcode'] > 0: - ret['comment'] = '{0}\n{1}\n{2}'.format(ret['comment'], - pip_ret['stdout'], - pip_ret['stderr']) + ret["result"] &= pip_ret["retcode"] == 0 + if pip_ret["retcode"] > 0: + ret["comment"] = "{0}\n{1}\n{2}".format( + ret["comment"], pip_ret["stdout"], pip_ret["stderr"] + ) - after = set(__salt__['pip.freeze'](bin_env=name)) + after = set(__salt__["pip.freeze"](bin_env=name)) new = list(after - before) old = list(before - after) if new or old: - ret['changes']['packages'] = { - 'new': new if new else '', - 'old': old if old else ''} + ret["changes"]["packages"] = { + "new": new if new else "", + "old": old if old else "", + } return ret -manage = salt.utils.functools.alias_function(managed, 'manage') +manage = salt.utils.functools.alias_function(managed, "manage") diff --git a/salt/states/webutil.py b/salt/states/webutil.py index bfb7dd2d549..b2ac9eb779c 100644 --- a/salt/states/webutil.py +++ b/salt/states/webutil.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Support for htpasswd module. Requires the apache2-utils package for Debian-based distros. .. versionadded:: 2014.7.0 @@ -13,7 +13,7 @@ Support for htpasswd module. Requires the apache2-utils package for Debian-based - options: d - force: true -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -21,21 +21,28 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.path - -__virtualname__ = 'webutil' +__virtualname__ = "webutil" def __virtual__(): - ''' + """ depends on webutil module - ''' - - return __virtualname__ if salt.utils.path.which('htpasswd') else False + """ + if salt.utils.path.which("htpasswd"): + return __virtualname__ + return (False, "Command not found: htpasswd") -def user_exists(name, password=None, htpasswd_file=None, options='', - force=False, runas=None, update=False): - ''' +def user_exists( + name, + password=None, + htpasswd_file=None, + options="", + force=False, + runas=None, + update=False, +): + """ Make sure the user is inside the specified htpasswd file name @@ -60,52 +67,51 @@ def user_exists(name, password=None, htpasswd_file=None, options='', Update an existing user's password if it's different from what's in the htpasswd file (unlike force, which updates regardless) - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": None} - exists = __salt__['file.grep']( - htpasswd_file, '^{0}:'.format(name))['retcode'] == 0 + exists = __salt__["file.grep"](htpasswd_file, "^{0}:".format(name))["retcode"] == 0 # If user exists, but we're supposed to update the password, find out if # it's changed, but not if we're forced to update the file regardless. password_changed = False if exists and update and not force: - password_changed = not __salt__['webutil.verify']( - htpasswd_file, name, password, opts=options, runas=runas) + password_changed = not __salt__["webutil.verify"]( + htpasswd_file, name, password, opts=options, runas=runas + ) if not exists or password_changed or force: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User \'{0}\' is set to be added to htpasswd file'.format(name) - ret['changes'] = {name: True} + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "User '{0}' is set to be added to htpasswd file".format( + name + ) + ret["changes"] = {name: True} return ret - useradd_ret = __salt__['webutil.useradd'](htpasswd_file, name, - password, opts=options, - runas=runas) - if useradd_ret['retcode'] == 0: - ret['result'] = True - ret['comment'] = useradd_ret['stderr'] - ret['changes'] = {name: True} + useradd_ret = __salt__["webutil.useradd"]( + htpasswd_file, name, password, opts=options, runas=runas + ) + if useradd_ret["retcode"] == 0: + ret["result"] = True + ret["comment"] = useradd_ret["stderr"] + ret["changes"] = {name: True} return ret else: - ret['result'] = False - ret['comment'] = useradd_ret['stderr'] + ret["result"] = False + ret["comment"] = useradd_ret["stderr"] return ret - if __opts__['test'] and ret['changes']: - ret['result'] = None + if __opts__["test"] and ret["changes"]: + ret["result"] = None else: - ret['result'] = True - ret['comment'] = 'User already known' + ret["result"] = True + ret["comment"] = "User already known" return ret def user_absent(name, htpasswd_file=None, runas=None): - ''' + """ Make sure the user is not in the specified htpasswd file name @@ -117,34 +123,33 @@ def user_absent(name, htpasswd_file=None, runas=None): runas The system user to run htpasswd command with - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": None} - exists = __salt__['file.grep']( - htpasswd_file, '^{0}:'.format(name))['retcode'] == 0 + exists = __salt__["file.grep"](htpasswd_file, "^{0}:".format(name))["retcode"] == 0 if not exists: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None else: - ret['result'] = True - ret['comment'] = 'User already not in file' + ret["result"] = True + ret["comment"] = "User already not in file" else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'User \'{0}\' is set to be removed from htpasswd file'.format(name) - ret['changes'] = {name: True} + if __opts__["test"]: + ret["result"] = None + ret[ + "comment" + ] = "User '{0}' is set to be removed from htpasswd file".format(name) + ret["changes"] = {name: True} else: - userdel_ret = __salt__['webutil.userdel']( - htpasswd_file, name, runas=runas, all_results=True) + userdel_ret = __salt__["webutil.userdel"]( + htpasswd_file, name, runas=runas, all_results=True + ) - ret['result'] = userdel_ret['retcode'] == 0 - ret['comment'] = userdel_ret['stderr'] + ret["result"] = userdel_ret["retcode"] == 0 + ret["comment"] = userdel_ret["stderr"] - if ret['result']: - ret['changes'] = {name: True} + if ret["result"]: + ret["changes"] = {name: True} return ret diff --git a/salt/states/win_certutil.py b/salt/states/win_certutil.py index d1ed62f4f08..b59832db571 100644 --- a/salt/states/win_certutil.py +++ b/salt/states/win_certutil.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installing of certificates to the Windows Certificate Manager ============================================================= @@ -10,9 +10,10 @@ Install certificates to the Windows Certificate Manager salt://certs/cert.cer: certutil.add_store: - store: TrustedPublisher -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -23,16 +24,16 @@ __virtualname__ = "certutil" def __virtual__(): - ''' + """ Only work on Windows - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ - return False + return (False, "Only Windows supported") -def add_store(name, store, saltenv='base'): - ''' +def add_store(name, store, saltenv="base"): + """ Store a certificate to the given store name @@ -46,35 +47,32 @@ def add_store(name, store, saltenv='base'): The salt environment to use, this is ignored if a local path is specified - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - cert_file = __salt__['cp.cache_file'](name, saltenv) + cert_file = __salt__["cp.cache_file"](name, saltenv) if cert_file is False: - ret['result'] = False - ret['comment'] += 'Certificate file not found.' + ret["result"] = False + ret["comment"] += "Certificate file not found." else: - cert_serial = __salt__['certutil.get_cert_serial'](cert_file) - serials = __salt__['certutil.get_stored_cert_serials'](store) + cert_serial = __salt__["certutil.get_cert_serial"](cert_file) + serials = __salt__["certutil.get_stored_cert_serials"](store) if cert_serial not in serials: - out = __salt__['certutil.add_store'](name, store) + out = __salt__["certutil.add_store"](name, store) if "successfully" in out: - ret['changes']['added'] = name + ret["changes"]["added"] = name else: - ret['result'] = False - ret['comment'] += "Failed to store certificate {0}".format(name) + ret["result"] = False + ret["comment"] += "Failed to store certificate {0}".format(name) else: - ret['comment'] += "{0} already stored.".format(name) + ret["comment"] += "{0} already stored.".format(name) return ret -def del_store(name, store, saltenv='base'): - ''' +def del_store(name, store, saltenv="base"): + """ Remove a certificate in the given store name @@ -88,28 +86,25 @@ def del_store(name, store, saltenv='base'): The salt environment to use, this is ignored if a local path is specified - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - cert_file = __salt__['cp.cache_file'](name, saltenv) + cert_file = __salt__["cp.cache_file"](name, saltenv) if cert_file is False: - ret['result'] = False - ret['comment'] += 'Certificate file not found.' + ret["result"] = False + ret["comment"] += "Certificate file not found." else: - cert_serial = __salt__['certutil.get_cert_serial'](cert_file) - serials = __salt__['certutil.get_stored_cert_serials'](store) + cert_serial = __salt__["certutil.get_cert_serial"](cert_file) + serials = __salt__["certutil.get_stored_cert_serials"](store) if cert_serial in serials: - out = __salt__['certutil.del_store'](cert_file, store) + out = __salt__["certutil.del_store"](cert_file, store) if "successfully" in out: - ret['changes']['removed'] = name + ret["changes"]["removed"] = name else: - ret['result'] = False - ret['comment'] += "Failed to remove the certificate {0}".format(name) + ret["result"] = False + ret["comment"] += "Failed to remove the certificate {0}".format(name) else: - ret['comment'] += "{0} already removed.".format(name) + ret["comment"] += "{0} already removed.".format(name) return ret diff --git a/salt/states/win_dacl.py b/salt/states/win_dacl.py index dbc424f4ec4..4d0df5a78b0 100644 --- a/salt/states/win_dacl.py +++ b/salt/states/win_dacl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Windows Object Access Control Lists Ensure an ACL is present @@ -69,154 +69,162 @@ Ensure an object is not inheriting permissions - name: HKEY_LOCAL_MACHINE\\SOFTWARE\\mykey - objectType: Registry - copy_inherited_acl: False -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'win_dacl' +__virtualname__ = "win_dacl" def __virtual__(): - ''' + """ Load this state if the win_acl module exists - ''' - return 'win_dacl' if 'win_dacl.add_ace' in __salt__ else False + """ + if "win_dacl.add_ace" in __salt__: + return "win_dacl" + return (False, "win_dacl module could not be loaded") def present(name, objectType, user, permission, acetype, propagation): - ''' + """ Ensure an ACE is present - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} - tRet = __salt__['win_dacl.check_ace'](name, objectType, user, permission, acetype, propagation, True) - if tRet['result']: - if not tRet['Exists']: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The ACE is set to be added.' - ret['changes']['Added ACEs'] = (( - '{0} {1} {2} on {3}' - ).format(user, acetype, permission, propagation)) + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} + tRet = __salt__["win_dacl.check_ace"]( + name, objectType, user, permission, acetype, propagation, True + ) + if tRet["result"]: + if not tRet["Exists"]: + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The ACE is set to be added." + ret["changes"]["Added ACEs"] = ("{0} {1} {2} on {3}").format( + user, acetype, permission, propagation + ) return ret - addRet = __salt__['win_dacl.add_ace'](name, objectType, user, permission, acetype, propagation) - if addRet['result']: - ret['result'] = True - ret['changes'] = dict(ret['changes'], **addRet['changes']) + addRet = __salt__["win_dacl.add_ace"]( + name, objectType, user, permission, acetype, propagation + ) + if addRet["result"]: + ret["result"] = True + ret["changes"] = dict(ret["changes"], **addRet["changes"]) else: - ret['result'] = False - ret['comment'] = ' '.join([ret['comment'], addRet['comment']]) + ret["result"] = False + ret["comment"] = " ".join([ret["comment"], addRet["comment"]]) else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The ACE is present.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The ACE is present." else: - ret['result'] = False - ret['comment'] = tRet['comment'] + ret["result"] = False + ret["comment"] = tRet["comment"] return ret def absent(name, objectType, user, permission, acetype, propagation): - ''' + """ Ensure an ACL does not exist - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} - tRet = __salt__['win_dacl.check_ace'](name, objectType, user, permission, acetype, propagation, True) - if tRet['result']: - if tRet['Exists']: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The ACE is set to be removed.' - ret['changes']['Removed ACEs'] = (( - '{0} {1} {2} on {3}' - ).format(user, acetype, permission, propagation)) + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} + tRet = __salt__["win_dacl.check_ace"]( + name, objectType, user, permission, acetype, propagation, True + ) + if tRet["result"]: + if tRet["Exists"]: + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The ACE is set to be removed." + ret["changes"]["Removed ACEs"] = ("{0} {1} {2} on {3}").format( + user, acetype, permission, propagation + ) return ret - addRet = __salt__['win_dacl.rm_ace'](name, objectType, user, permission, acetype, propagation) - if addRet['result']: - ret['result'] = True - ret['changes'] = dict(ret['changes'], **addRet['changes']) + addRet = __salt__["win_dacl.rm_ace"]( + name, objectType, user, permission, acetype, propagation + ) + if addRet["result"]: + ret["result"] = True + ret["changes"] = dict(ret["changes"], **addRet["changes"]) else: - ret['result'] = False - ret['comment'] = ' '.join([ret['comment'], addRet['comment']]) + ret["result"] = False + ret["comment"] = " ".join([ret["comment"], addRet["comment"]]) else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'The ACE is not present.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "The ACE is not present." else: - ret['result'] = False - ret['comment'] = tRet['comment'] + ret["result"] = False + ret["comment"] = tRet["comment"] return ret def inherit(name, objectType, clear_existing_acl=False): - ''' + """ Ensure an object is inheriting ACLs from its parent - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} - tRet = __salt__['win_dacl.check_inheritance'](name, objectType) - if tRet['result']: - if not tRet['Inheritance']: - if __opts__['test']: - ret['result'] = None - ret['changes']['Inheritance'] = "Enabled" - ret['comment'] = 'Inheritance is set to be enabled.' - ret['changes']['Existing ACLs'] = ( - 'Are set to be removed' if clear_existing_acl else 'Are set to be kept') + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} + tRet = __salt__["win_dacl.check_inheritance"](name, objectType) + if tRet["result"]: + if not tRet["Inheritance"]: + if __opts__["test"]: + ret["result"] = None + ret["changes"]["Inheritance"] = "Enabled" + ret["comment"] = "Inheritance is set to be enabled." + ret["changes"]["Existing ACLs"] = ( + "Are set to be removed" + if clear_existing_acl + else "Are set to be kept" + ) return ret - eRet = __salt__['win_dacl.enable_inheritance'](name, objectType, clear_existing_acl) - if eRet['result']: - ret['result'] = True - ret['changes'] = dict(ret['changes'], **eRet['changes']) + eRet = __salt__["win_dacl.enable_inheritance"]( + name, objectType, clear_existing_acl + ) + if eRet["result"]: + ret["result"] = True + ret["changes"] = dict(ret["changes"], **eRet["changes"]) else: - ret['result'] = False - ret['comment'] = ' '.join([ret['comment'], eRet['comment']]) + ret["result"] = False + ret["comment"] = " ".join([ret["comment"], eRet["comment"]]) else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Inheritance is enabled.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Inheritance is enabled." else: - ret['result'] = False - ret['comment'] = tRet['comment'] + ret["result"] = False + ret["comment"] = tRet["comment"] return ret def disinherit(name, objectType, copy_inherited_acl=True): - ''' + """ Ensure an object is not inheriting ACLs from its parent - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} - tRet = __salt__['win_dacl.check_inheritance'](name, objectType) - if tRet['result']: - if tRet['Inheritance']: - if __opts__['test']: - ret['result'] = None - ret['changes']['Inheritance'] = "Disabled" - ret['comment'] = 'Inheritance is set to be disabled.' - ret['changes']['Inherited ACLs'] = ( - 'Are set to be kept' if copy_inherited_acl else 'Are set to be removed') + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} + tRet = __salt__["win_dacl.check_inheritance"](name, objectType) + if tRet["result"]: + if tRet["Inheritance"]: + if __opts__["test"]: + ret["result"] = None + ret["changes"]["Inheritance"] = "Disabled" + ret["comment"] = "Inheritance is set to be disabled." + ret["changes"]["Inherited ACLs"] = ( + "Are set to be kept" + if copy_inherited_acl + else "Are set to be removed" + ) return ret - eRet = __salt__['win_dacl.disable_inheritance'](name, objectType, copy_inherited_acl) - ret['result'] = eRet['result'] - if eRet['result']: - ret['changes'] = dict(ret['changes'], **eRet['changes']) + eRet = __salt__["win_dacl.disable_inheritance"]( + name, objectType, copy_inherited_acl + ) + ret["result"] = eRet["result"] + if eRet["result"]: + ret["changes"] = dict(ret["changes"], **eRet["changes"]) else: - ret['comment'] = ' '.join([ret['comment'], eRet['comment']]) + ret["comment"] = " ".join([ret["comment"], eRet["comment"]]) else: - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Inheritance is disabled.' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Inheritance is disabled." else: - ret['result'] = False - ret['comment'] = tRet['comment'] + ret["result"] = False + ret["comment"] = tRet["comment"] return ret diff --git a/salt/states/win_dism.py b/salt/states/win_dism.py index e1067d9d271..3f5a4a6addc 100644 --- a/salt/states/win_dism.py +++ b/salt/states/win_dism.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installing of Windows features using DISM ========================================= @@ -12,8 +12,8 @@ Install windows features/capabilties with DISM NetFx3: dism.feature_installed -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -28,21 +28,19 @@ __virtualname__ = "dism" def __virtual__(): - ''' + """ Only work on Windows where the DISM module is available - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Module only available on Windows' + return False, "Module only available on Windows" return __virtualname__ -def capability_installed(name, - source=None, - limit_access=False, - image=None, - restart=False): - ''' +def capability_installed( + name, source=None, limit_access=False, image=None, restart=False +): + """ Install a DISM capability Args: @@ -64,45 +62,40 @@ def capability_installed(name, install_dotnet35: dism.capability_installed: - name: NetFX3~~~~ - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - old = __salt__['dism.installed_capabilities']() + old = __salt__["dism.installed_capabilities"]() if name in old: - ret['comment'] = 'The capability {0} is already installed'.format(name) + ret["comment"] = "The capability {0} is already installed".format(name) return ret - if __opts__['test']: - ret['changes']['capability'] = '{0} will be installed'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["changes"]["capability"] = "{0} will be installed".format(name) + ret["result"] = None return ret # Install the capability - status = __salt__['dism.add_capability']( - name, source, limit_access, image, restart) + status = __salt__["dism.add_capability"](name, source, limit_access, image, restart) - if status['retcode'] not in [0, 1641, 3010]: - ret['comment'] = 'Failed to install {0}: {1}'\ - .format(name, status['stdout']) - ret['result'] = False + if status["retcode"] not in [0, 1641, 3010]: + ret["comment"] = "Failed to install {0}: {1}".format(name, status["stdout"]) + ret["result"] = False - new = __salt__['dism.installed_capabilities']() + new = __salt__["dism.installed_capabilities"]() changes = salt.utils.data.compare_lists(old, new) if changes: - ret['comment'] = 'Installed {0}'.format(name) - ret['changes'] = status - ret['changes']['capability'] = changes + ret["comment"] = "Installed {0}".format(name) + ret["changes"] = status + ret["changes"]["capability"] = changes return ret def capability_removed(name, image=None, restart=False): - ''' + """ Uninstall a DISM capability Args: @@ -121,50 +114,48 @@ def capability_removed(name, image=None, restart=False): remove_dotnet35: dism.capability_removed: - name: NetFX3~~~~ - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - old = __salt__['dism.installed_capabilities']() + old = __salt__["dism.installed_capabilities"]() if name not in old: - ret['comment'] = 'The capability {0} is already removed'.format(name) + ret["comment"] = "The capability {0} is already removed".format(name) return ret - if __opts__['test']: - ret['changes']['capability'] = '{0} will be removed'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["changes"]["capability"] = "{0} will be removed".format(name) + ret["result"] = None return ret # Remove the capability - status = __salt__['dism.remove_capability'](name, image, restart) + status = __salt__["dism.remove_capability"](name, image, restart) - if status['retcode'] not in [0, 1641, 3010]: - ret['comment'] = 'Failed to remove {0}: {1}' \ - .format(name, status['stdout']) - ret['result'] = False + if status["retcode"] not in [0, 1641, 3010]: + ret["comment"] = "Failed to remove {0}: {1}".format(name, status["stdout"]) + ret["result"] = False - new = __salt__['dism.installed_capabilities']() + new = __salt__["dism.installed_capabilities"]() changes = salt.utils.data.compare_lists(old, new) if changes: - ret['comment'] = 'Removed {0}'.format(name) - ret['changes'] = status - ret['changes']['capability'] = changes + ret["comment"] = "Removed {0}".format(name) + ret["changes"] = status + ret["changes"]["capability"] = changes return ret -def feature_installed(name, - package=None, - source=None, - limit_access=False, - enable_parent=False, - image=None, - restart=False): - ''' +def feature_installed( + name, + package=None, + source=None, + limit_access=False, + enable_parent=False, + image=None, + restart=False, +): + """ Install a DISM feature Args: @@ -191,45 +182,42 @@ def feature_installed(name, install_telnet_client: dism.feature_installed: - name: TelnetClient - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - old = __salt__['dism.installed_features']() + old = __salt__["dism.installed_features"]() if name in old: - ret['comment'] = 'The feature {0} is already installed'.format(name) + ret["comment"] = "The feature {0} is already installed".format(name) return ret - if __opts__['test']: - ret['changes']['feature'] = '{0} will be installed'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["changes"]["feature"] = "{0} will be installed".format(name) + ret["result"] = None return ret # Install the feature - status = __salt__['dism.add_feature']( - name, package, source, limit_access, enable_parent, image, restart) + status = __salt__["dism.add_feature"]( + name, package, source, limit_access, enable_parent, image, restart + ) - if status['retcode'] not in [0, 1641, 3010]: - ret['comment'] = 'Failed to install {0}: {1}' \ - .format(name, status['stdout']) - ret['result'] = False + if status["retcode"] not in [0, 1641, 3010]: + ret["comment"] = "Failed to install {0}: {1}".format(name, status["stdout"]) + ret["result"] = False - new = __salt__['dism.installed_features']() + new = __salt__["dism.installed_features"]() changes = salt.utils.data.compare_lists(old, new) if changes: - ret['comment'] = 'Installed {0}'.format(name) - ret['changes'] = status - ret['changes']['feature'] = changes + ret["comment"] = "Installed {0}".format(name) + ret["changes"] = status + ret["changes"]["feature"] = changes return ret def feature_removed(name, remove_payload=False, image=None, restart=False): - ''' + """ Disables a feature. Args: @@ -251,49 +239,42 @@ def feature_removed(name, remove_payload=False, image=None, restart=False): dism.feature_removed: - name: TelnetClient - remove_payload: True - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} - old = __salt__['dism.installed_features']() + old = __salt__["dism.installed_features"]() if name not in old: - ret['comment'] = 'The feature {0} is already removed'.format(name) + ret["comment"] = "The feature {0} is already removed".format(name) return ret - if __opts__['test']: - ret['changes']['feature'] = '{0} will be removed'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["changes"]["feature"] = "{0} will be removed".format(name) + ret["result"] = None return ret # Remove the feature - status = __salt__['dism.remove_feature']( - name, remove_payload, image, restart) + status = __salt__["dism.remove_feature"](name, remove_payload, image, restart) - if status['retcode'] not in [0, 1641, 3010]: - ret['comment'] = 'Failed to remove {0}: {1}' \ - .format(name, status['stdout']) - ret['result'] = False + if status["retcode"] not in [0, 1641, 3010]: + ret["comment"] = "Failed to remove {0}: {1}".format(name, status["stdout"]) + ret["result"] = False - new = __salt__['dism.installed_features']() + new = __salt__["dism.installed_features"]() changes = salt.utils.data.compare_lists(old, new) if changes: - ret['comment'] = 'Removed {0}'.format(name) - ret['changes'] = status - ret['changes']['feature'] = changes + ret["comment"] = "Removed {0}".format(name) + ret["changes"] = status + ret["changes"]["feature"] = changes return ret -def package_installed(name, - ignore_check=False, - prevent_pending=False, - image=None, - restart=False): - ''' +def package_installed( + name, ignore_check=False, prevent_pending=False, image=None, restart=False +): + """ Install a package. Args: @@ -315,58 +296,56 @@ def package_installed(name, install_KB123123123: dism.package_installed: - name: C:\\Packages\\KB123123123.cab - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # Fail if using a non-existent package path - if '~' not in name and not os.path.exists(name): - if __opts__['test']: - ret['result'] = None + if "~" not in name and not os.path.exists(name): + if __opts__["test"]: + ret["result"] = None else: - ret['result'] = False - ret['comment'] = 'Package path {0} does not exist'.format(name) + ret["result"] = False + ret["comment"] = "Package path {0} does not exist".format(name) return ret - old = __salt__['dism.installed_packages']() + old = __salt__["dism.installed_packages"]() # Get package info so we can see if it's already installed - package_info = __salt__['dism.package_info'](name) + package_info = __salt__["dism.package_info"](name) - if package_info['Package Identity'] in old: - ret['comment'] = 'The package {0} is already installed: {1}'\ - .format(name, package_info['Package Identity']) + if package_info["Package Identity"] in old: + ret["comment"] = "The package {0} is already installed: {1}".format( + name, package_info["Package Identity"] + ) return ret - if __opts__['test']: - ret['changes']['package'] = '{0} will be installed'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["changes"]["package"] = "{0} will be installed".format(name) + ret["result"] = None return ret # Install the package - status = __salt__['dism.add_package']( - name, ignore_check, prevent_pending, image, restart) + status = __salt__["dism.add_package"]( + name, ignore_check, prevent_pending, image, restart + ) - if status['retcode'] not in [0, 1641, 3010]: - ret['comment'] = 'Failed to install {0}: {1}' \ - .format(name, status['stdout']) - ret['result'] = False + if status["retcode"] not in [0, 1641, 3010]: + ret["comment"] = "Failed to install {0}: {1}".format(name, status["stdout"]) + ret["result"] = False - new = __salt__['dism.installed_packages']() + new = __salt__["dism.installed_packages"]() changes = salt.utils.data.compare_lists(old, new) if changes: - ret['comment'] = 'Installed {0}'.format(name) - ret['changes'] = status - ret['changes']['package'] = changes + ret["comment"] = "Installed {0}".format(name) + ret["changes"] = status + ret["changes"]["package"] = changes return ret def package_removed(name, image=None, restart=False): - ''' + """ Uninstall a package Args: @@ -392,52 +371,50 @@ def package_removed(name, image=None, restart=False): remove_KB1231231: dism.package_installed: - name: Package_for_KB1231231~31bf3856ad364e35~amd64~~10.0.1.3 - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # Fail if using a non-existent package path - if '~' not in name and not os.path.exists(name): - if __opts__['test']: - ret['result'] = None + if "~" not in name and not os.path.exists(name): + if __opts__["test"]: + ret["result"] = None else: - ret['result'] = False - ret['comment'] = 'Package path {0} does not exist'.format(name) + ret["result"] = False + ret["comment"] = "Package path {0} does not exist".format(name) return ret - old = __salt__['dism.installed_packages']() + old = __salt__["dism.installed_packages"]() # Get package info so we can see if it's already removed - package_info = __salt__['dism.package_info'](name) + package_info = __salt__["dism.package_info"](name) # If `Package Identity` isn't returned or if they passed a cab file, if # `Package Identity` isn't in the list of installed packages - if 'Package Identity' not in package_info or \ - package_info['Package Identity'] not in old: - ret['comment'] = 'The package {0} is already removed'.format(name) + if ( + "Package Identity" not in package_info + or package_info["Package Identity"] not in old + ): + ret["comment"] = "The package {0} is already removed".format(name) return ret - if __opts__['test']: - ret['changes']['package'] = '{0} will be removed'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["changes"]["package"] = "{0} will be removed".format(name) + ret["result"] = None return ret # Remove the package - status = __salt__['dism.remove_package'](name, image, restart) + status = __salt__["dism.remove_package"](name, image, restart) - if status['retcode'] not in [0, 1641, 3010]: - ret['comment'] = 'Failed to remove {0}: {1}' \ - .format(name, status['stdout']) - ret['result'] = False + if status["retcode"] not in [0, 1641, 3010]: + ret["comment"] = "Failed to remove {0}: {1}".format(name, status["stdout"]) + ret["result"] = False - new = __salt__['dism.installed_packages']() + new = __salt__["dism.installed_packages"]() changes = salt.utils.data.compare_lists(old, new) if changes: - ret['comment'] = 'Removed {0}'.format(name) - ret['changes'] = status - ret['changes']['package'] = changes + ret["comment"] = "Removed {0}".format(name) + ret["changes"] = status + ret["changes"]["package"] = changes return ret diff --git a/salt/states/win_dns_client.py b/salt/states/win_dns_client.py index 4d4d0c3fce9..666c1d0315c 100644 --- a/salt/states/win_dns_client.py +++ b/salt/states/win_dns_client.py @@ -1,19 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" Module for configuring DNS Client on Windows systems -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Load if the module win_dns_client is loaded - ''' - return 'win_dns_client' if 'win_dns_client.add_dns' in __salt__ else False + """ + if "win_dns_client.add_dns" in __salt__: + return "win_dns_client" + return (False, "win_dns_client module could not be loaded") -def dns_exists(name, servers=None, interface='Local Area Connection', replace=False): - ''' +def dns_exists(name, servers=None, interface="Local Area Connection", replace=False): + """ Configure the DNS server list in the specified interface Example: @@ -26,110 +28,111 @@ def dns_exists(name, servers=None, interface='Local Area Connection', replace=Fa - servers: - 8.8.8.8 - 8.8.8.9 - ''' - ret = {'name': name, - 'result': True, - 'changes': {'Servers Reordered': [], 'Servers Added': [], 'Servers Removed': []}, - 'comment': ''} + """ + ret = { + "name": name, + "result": True, + "changes": { + "Servers Reordered": [], + "Servers Added": [], + "Servers Removed": [], + }, + "comment": "", + } - if __opts__['test']: - ret['comment'] = 'DNS Servers are set to be updated' - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "DNS Servers are set to be updated" + ret["result"] = None else: - ret['comment'] = 'DNS Servers have been updated' + ret["comment"] = "DNS Servers have been updated" # Validate syntax if not isinstance(servers, list): - ret['result'] = False - ret['comment'] = 'servers entry is not a list !' + ret["result"] = False + ret["comment"] = "servers entry is not a list !" return ret # Do nothing is already configured - configured_list = __salt__['win_dns_client.get_dns_servers'](interface) + configured_list = __salt__["win_dns_client.get_dns_servers"](interface) if configured_list == servers: - ret['comment'] = '{0} are already configured'.format(servers) - ret['changes'] = {} - ret['result'] = True + ret["comment"] = "{0} are already configured".format(servers) + ret["changes"] = {} + ret["result"] = True return ret # add the DNS servers for i, server in enumerate(servers): - if __opts__['test']: + if __opts__["test"]: if server in configured_list: if configured_list.index(server) != i: - ret['changes']['Servers Reordered'].append(server) + ret["changes"]["Servers Reordered"].append(server) else: - ret['changes']['Servers Added'].append(server) + ret["changes"]["Servers Added"].append(server) else: - if not __salt__['win_dns_client.add_dns'](server, interface, i + 1): - ret['comment'] = ( - 'Failed to add {0} as DNS server number {1}' - ).format(server, i + 1) - ret['result'] = False - ret['changes'] = {} + if not __salt__["win_dns_client.add_dns"](server, interface, i + 1): + ret["comment"] = ("Failed to add {0} as DNS server number {1}").format( + server, i + 1 + ) + ret["result"] = False + ret["changes"] = {} return ret else: if server in configured_list: if configured_list.index(server) != i: - ret['changes']['Servers Reordered'].append(server) + ret["changes"]["Servers Reordered"].append(server) else: - ret['changes']['Servers Added'].append(server) + ret["changes"]["Servers Added"].append(server) # remove dns servers that weren't in our list if replace: for i, server in enumerate(configured_list): if server not in servers: - if __opts__['test']: - ret['changes']['Servers Removed'].append(server) + if __opts__["test"]: + ret["changes"]["Servers Removed"].append(server) else: - if not __salt__['win_dns_client.rm_dns'](server, interface): - ret['comment'] = ( - 'Failed to remove {0} from DNS server list').format(server) - ret['result'] = False + if not __salt__["win_dns_client.rm_dns"](server, interface): + ret["comment"] = ( + "Failed to remove {0} from DNS server list" + ).format(server) + ret["result"] = False return ret else: - ret['changes']['Servers Removed'].append(server) + ret["changes"]["Servers Removed"].append(server) return ret -def dns_dhcp(name, interface='Local Area Connection'): - ''' +def dns_dhcp(name, interface="Local Area Connection"): + """ Configure the DNS server list from DHCP Server - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} # Check the config - config = __salt__['win_dns_client.get_dns_config'](interface) - if config == 'dhcp': - ret['comment'] = '{0} already configured with DNS from DHCP'.format( - interface) + config = __salt__["win_dns_client.get_dns_config"](interface) + if config == "dhcp": + ret["comment"] = "{0} already configured with DNS from DHCP".format(interface) return ret else: - ret['changes'] = {'dns': 'configured from DHCP'} + ret["changes"] = {"dns": "configured from DHCP"} - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # change the configuration - ret['result'] = __salt__['win_dns_client.dns_dhcp'](interface) - if not ret['result']: - ret['changes'] = {} - ret['comment'] = ( - 'Could not configure "{0}" DNS servers from DHCP' - ).format(interface) + ret["result"] = __salt__["win_dns_client.dns_dhcp"](interface) + if not ret["result"]: + ret["changes"] = {} + ret["comment"] = ('Could not configure "{0}" DNS servers from DHCP').format( + interface + ) return ret -def primary_suffix(name, - suffix=None, - updates=False): - ''' +def primary_suffix(name, suffix=None, updates=False): + """ .. versionadded:: 2014.7.0 Configure the global primary DNS suffix of a DHCP client. @@ -147,20 +150,15 @@ def primary_suffix(name, win_dns_client.primary_suffix: - suffix: sub.domain.tld - updates: True - ''' + """ - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'No changes needed' - } + ret = {"name": name, "changes": {}, "result": True, "comment": "No changes needed"} suffix = str(suffix) if not isinstance(updates, bool): - ret['result'] = False - ret['comment'] = '\'updates\' must be a boolean value' + ret["result"] = False + ret["comment"] = "'updates' must be a boolean value" return ret # TODO: waiting for an implementation of @@ -171,84 +169,95 @@ def primary_suffix(name, # manually reg_data = { - 'suffix': { - 'hive': 'HKEY_LOCAL_MACHINE', - 'key': r'SYSTEM\CurrentControlSet\services\Tcpip\Parameters', - 'vname': 'NV Domain', - 'vtype': 'REG_SZ', - 'old': None, - 'new': suffix - }, - 'updates': { - 'hive': 'HKEY_LOCAL_MACHINE', - 'key': r'SYSTEM\CurrentControlSet\services\Tcpip\Parameters', - 'vname': 'SyncDomainWithMembership', - 'vtype': 'REG_DWORD', - 'old': None, - 'new': updates - } + "suffix": { + "hive": "HKEY_LOCAL_MACHINE", + "key": r"SYSTEM\CurrentControlSet\services\Tcpip\Parameters", + "vname": "NV Domain", + "vtype": "REG_SZ", + "old": None, + "new": suffix, + }, + "updates": { + "hive": "HKEY_LOCAL_MACHINE", + "key": r"SYSTEM\CurrentControlSet\services\Tcpip\Parameters", + "vname": "SyncDomainWithMembership", + "vtype": "REG_DWORD", + "old": None, + "new": updates, + }, } - reg_data['suffix']['old'] = __salt__['reg.read_value']( - reg_data['suffix']['hive'], - reg_data['suffix']['key'], - reg_data['suffix']['vname'],)['vdata'] + reg_data["suffix"]["old"] = __salt__["reg.read_value"]( + reg_data["suffix"]["hive"], + reg_data["suffix"]["key"], + reg_data["suffix"]["vname"], + )["vdata"] - reg_data['updates']['old'] = bool(__salt__['reg.read_value']( - reg_data['updates']['hive'], - reg_data['updates']['key'], - reg_data['updates']['vname'],)['vdata']) + reg_data["updates"]["old"] = bool( + __salt__["reg.read_value"]( + reg_data["updates"]["hive"], + reg_data["updates"]["key"], + reg_data["updates"]["vname"], + )["vdata"] + ) - updates_operation = 'enabled' if reg_data['updates']['new'] else 'disabled' + updates_operation = "enabled" if reg_data["updates"]["new"] else "disabled" # No changes to suffix needed - if reg_data['suffix']['new'] == reg_data['suffix']['old']: + if reg_data["suffix"]["new"] == reg_data["suffix"]["old"]: # No changes to updates policy needed - if reg_data['updates']['new'] == reg_data['updates']['old']: + if reg_data["updates"]["new"] == reg_data["updates"]["old"]: return ret # Changes to update policy needed else: - ret['comment'] = '{0} suffix updates'.format(updates_operation) - ret['changes'] = { - 'old': { - 'updates': reg_data['updates']['old']}, - 'new': { - 'updates': reg_data['updates']['new']}} + ret["comment"] = "{0} suffix updates".format(updates_operation) + ret["changes"] = { + "old": {"updates": reg_data["updates"]["old"]}, + "new": {"updates": reg_data["updates"]["new"]}, + } # Changes to suffix needed else: # Changes to updates policy needed - if reg_data['updates']['new'] != reg_data['updates']['old']: - ret['comment'] = 'Updated primary DNS suffix ({0}) and {1} suffix updates'.format(suffix, updates_operation) - ret['changes'] = { - 'old': { - 'suffix': reg_data['suffix']['old'], - 'updates': reg_data['updates']['old']}, - 'new': { - 'suffix': reg_data['suffix']['new'], - 'updates': reg_data['updates']['new']}} + if reg_data["updates"]["new"] != reg_data["updates"]["old"]: + ret[ + "comment" + ] = "Updated primary DNS suffix ({0}) and {1} suffix updates".format( + suffix, updates_operation + ) + ret["changes"] = { + "old": { + "suffix": reg_data["suffix"]["old"], + "updates": reg_data["updates"]["old"], + }, + "new": { + "suffix": reg_data["suffix"]["new"], + "updates": reg_data["updates"]["new"], + }, + } # No changes to updates policy needed else: - ret['comment'] = 'Updated primary DNS suffix ({0})'.format(suffix) - ret['changes'] = { - 'old': { - 'suffix': reg_data['suffix']['old']}, - 'new': { - 'suffix': reg_data['suffix']['new']}} + ret["comment"] = "Updated primary DNS suffix ({0})".format(suffix) + ret["changes"] = { + "old": {"suffix": reg_data["suffix"]["old"]}, + "new": {"suffix": reg_data["suffix"]["new"]}, + } - suffix_result = __salt__['reg.set_value']( - reg_data['suffix']['hive'], - reg_data['suffix']['key'], - reg_data['suffix']['vname'], - reg_data['suffix']['new'], - reg_data['suffix']['vtype']) + suffix_result = __salt__["reg.set_value"]( + reg_data["suffix"]["hive"], + reg_data["suffix"]["key"], + reg_data["suffix"]["vname"], + reg_data["suffix"]["new"], + reg_data["suffix"]["vtype"], + ) - updates_result = __salt__['reg.set_value']( - reg_data['updates']['hive'], - reg_data['updates']['key'], - reg_data['updates']['vname'], - reg_data['updates']['new'], - reg_data['updates']['vtype']) + updates_result = __salt__["reg.set_value"]( + reg_data["updates"]["hive"], + reg_data["updates"]["key"], + reg_data["updates"]["vname"], + reg_data["updates"]["new"], + reg_data["updates"]["vtype"], + ) - ret['result'] = suffix_result & updates_result + ret["result"] = suffix_result & updates_result return ret diff --git a/salt/states/win_firewall.py b/salt/states/win_firewall.py index 423b849ae4e..a9e0e140eae 100644 --- a/salt/states/win_firewall.py +++ b/salt/states/win_firewall.py @@ -1,22 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" State for configuring Windows Firewall -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs from salt.exceptions import CommandExecutionError, SaltInvocationError def __virtual__(): - ''' + """ Load if the module firewall is loaded - ''' - return 'win_firewall' if 'firewall.get_config' in __salt__ else False + """ + if "firewall.get_config" in __salt__: + return "win_firewall" + return (False, "firewall module could not be loaded") -def disabled(name='allprofiles'): - ''' +def disabled(name="allprofiles"): + """ Disable all the firewall profiles (Windows only) Args: @@ -41,63 +43,59 @@ def disabled(name='allprofiles'): disable_all: win_firewall.disabled: - name: allprofiles - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - profile_map = {'domainprofile': 'Domain', - 'privateprofile': 'Private', - 'publicprofile': 'Public', - 'allprofiles': 'All'} + profile_map = { + "domainprofile": "Domain", + "privateprofile": "Private", + "publicprofile": "Public", + "allprofiles": "All", + } # Make sure the profile name is valid if name not in profile_map: - raise SaltInvocationError('Invalid profile name: {0}'.format(name)) + raise SaltInvocationError("Invalid profile name: {0}".format(name)) - current_config = __salt__['firewall.get_config']() - if name != 'allprofiles' and profile_map[name] not in current_config: - ret['result'] = False - ret['comment'] = 'Profile {0} does not exist in firewall.get_config' \ - ''.format(name) + current_config = __salt__["firewall.get_config"]() + if name != "allprofiles" and profile_map[name] not in current_config: + ret["result"] = False + ret["comment"] = "Profile {0} does not exist in firewall.get_config" "".format( + name + ) return ret for key in current_config: if current_config[key]: - if name == 'allprofiles' or key == profile_map[name]: - ret['changes'][key] = 'disabled' + if name == "allprofiles" or key == profile_map[name]: + ret["changes"][key] = "disabled" - if __opts__['test']: - ret['result'] = not ret['changes'] or None - ret['comment'] = ret['changes'] - ret['changes'] = {} + if __opts__["test"]: + ret["result"] = not ret["changes"] or None + ret["comment"] = ret["changes"] + ret["changes"] = {} return ret # Disable it - if ret['changes']: + if ret["changes"]: try: - ret['result'] = __salt__['firewall.disable'](name) + ret["result"] = __salt__["firewall.disable"](name) except CommandExecutionError: - ret['comment'] = 'Firewall Profile {0} could not be disabled' \ - ''.format(profile_map[name]) + ret["comment"] = "Firewall Profile {0} could not be disabled" "".format( + profile_map[name] + ) else: - if name == 'allprofiles': - msg = 'All the firewall profiles are disabled' + if name == "allprofiles": + msg = "All the firewall profiles are disabled" else: - msg = 'Firewall profile {0} is disabled'.format(name) - ret['comment'] = msg + msg = "Firewall profile {0} is disabled".format(name) + ret["comment"] = msg return ret -def add_rule(name, - localport, - protocol='tcp', - action='allow', - dir='in', - remoteip='any'): - ''' +def add_rule(name, localport, protocol="tcp", action="allow", dir="in", remoteip="any"): + """ Add a new inbound or outbound rule to the firewall policy Args: @@ -155,37 +153,33 @@ def add_rule(name, - localport: 445 - protocol: tcp - action: allow - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} # Check if rule exists - if not __salt__['firewall.rule_exists'](name): - ret['changes'] = {'new rule': name} + if not __salt__["firewall.rule_exists"](name): + ret["changes"] = {"new rule": name} else: - ret['comment'] = 'A rule with that name already exists' + ret["comment"] = "A rule with that name already exists" return ret - if __opts__['test']: - ret['result'] = not ret['changes'] or None - ret['comment'] = ret['changes'] - ret['changes'] = {} + if __opts__["test"]: + ret["result"] = not ret["changes"] or None + ret["comment"] = ret["changes"] + ret["changes"] = {} return ret # Add rule try: - __salt__['firewall.add_rule']( - name, localport, protocol, action, dir, remoteip) + __salt__["firewall.add_rule"](name, localport, protocol, action, dir, remoteip) except CommandExecutionError: - ret['comment'] = 'Could not add rule' + ret["comment"] = "Could not add rule" return ret -def enabled(name='allprofiles'): - ''' +def enabled(name="allprofiles"): + """ Enable all the firewall profiles (Windows only) Args: @@ -210,51 +204,52 @@ def enabled(name='allprofiles'): enable_all: win_firewall.enabled: - name: allprofiles - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - profile_map = {'domainprofile': 'Domain', - 'privateprofile': 'Private', - 'publicprofile': 'Public', - 'allprofiles': 'All'} + profile_map = { + "domainprofile": "Domain", + "privateprofile": "Private", + "publicprofile": "Public", + "allprofiles": "All", + } # Make sure the profile name is valid if name not in profile_map: - raise SaltInvocationError('Invalid profile name: {0}'.format(name)) + raise SaltInvocationError("Invalid profile name: {0}".format(name)) - current_config = __salt__['firewall.get_config']() - if name != 'allprofiles' and profile_map[name] not in current_config: - ret['result'] = False - ret['comment'] = 'Profile {0} does not exist in firewall.get_config' \ - ''.format(name) + current_config = __salt__["firewall.get_config"]() + if name != "allprofiles" and profile_map[name] not in current_config: + ret["result"] = False + ret["comment"] = "Profile {0} does not exist in firewall.get_config" "".format( + name + ) return ret for key in current_config: if not current_config[key]: - if name == 'allprofiles' or key == profile_map[name]: - ret['changes'][key] = 'enabled' + if name == "allprofiles" or key == profile_map[name]: + ret["changes"][key] = "enabled" - if __opts__['test']: - ret['result'] = not ret['changes'] or None - ret['comment'] = ret['changes'] - ret['changes'] = {} + if __opts__["test"]: + ret["result"] = not ret["changes"] or None + ret["comment"] = ret["changes"] + ret["changes"] = {} return ret # Enable it - if ret['changes']: + if ret["changes"]: try: - ret['result'] = __salt__['firewall.enable'](name) + ret["result"] = __salt__["firewall.enable"](name) except CommandExecutionError: - ret['comment'] = 'Firewall Profile {0} could not be enabled' \ - ''.format(profile_map[name]) + ret["comment"] = "Firewall Profile {0} could not be enabled" "".format( + profile_map[name] + ) else: - if name == 'allprofiles': - msg = 'All the firewall profiles are enabled' + if name == "allprofiles": + msg = "All the firewall profiles are enabled" else: - msg = 'Firewall profile {0} is enabled'.format(name) - ret['comment'] = msg + msg = "Firewall profile {0} is enabled".format(name) + ret["comment"] = msg return ret diff --git a/salt/states/win_iis.py b/salt/states/win_iis.py index 663d88e16af..3b2d4964664 100644 --- a/salt/states/win_iis.py +++ b/salt/states/win_iis.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Microsoft IIS site management This module provides the ability to add/remove websites and application pools @@ -7,37 +7,39 @@ from Microsoft IIS. .. versionadded:: 2016.3.0 -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + from salt.ext.six.moves import map - # Define the module's virtual name -__virtualname__ = 'win_iis' +__virtualname__ = "win_iis" def __virtual__(): - ''' + """ Load only on minions that have the win_iis module. - ''' - if 'win_iis.create_site' in __salt__: + """ + if "win_iis.create_site" in __salt__: return __virtualname__ - return False + return (False, "win_iis module could not be loaded") -def _get_binding_info(hostheader='', ipaddress='*', port=80): - ''' +def _get_binding_info(hostheader="", ipaddress="*", port=80): + """ Combine the host header, IP address, and TCP port into bindingInformation format. - ''' - ret = r'{0}:{1}:{2}'.format(ipaddress, port, hostheader.replace(' ', '')) + """ + ret = r"{0}:{1}:{2}".format(ipaddress, port, hostheader.replace(" ", "")) return ret -def deployed(name, sourcepath, apppool='', hostheader='', ipaddress='*', port=80, protocol='http'): - ''' +def deployed( + name, sourcepath, apppool="", hostheader="", ipaddress="*", port=80, protocol="http" +): + """ Ensure the website has been deployed. .. note: @@ -82,33 +84,28 @@ def deployed(name, sourcepath, apppool='', hostheader='', ipaddress='*', port=80 - ipaddress: '*' - port: 443 - protocol: https - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - current_sites = __salt__['win_iis.list_sites']() + current_sites = __salt__["win_iis.list_sites"]() if name in current_sites: - ret['comment'] = 'Site already present: {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Site will be created: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} + ret["comment"] = "Site already present: {0}".format(name) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Site will be created: {0}".format(name) + ret["changes"] = {"old": None, "new": name} else: - ret['comment'] = 'Created site: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} - ret['result'] = __salt__['win_iis.create_site'](name, sourcepath, apppool, - hostheader, ipaddress, port, - protocol) + ret["comment"] = "Created site: {0}".format(name) + ret["changes"] = {"old": None, "new": name} + ret["result"] = __salt__["win_iis.create_site"]( + name, sourcepath, apppool, hostheader, ipaddress, port, protocol + ) return ret def remove_site(name): - ''' + """ Delete a website from IIS. :param str name: The IIS site name. @@ -120,32 +117,29 @@ def remove_site(name): defaultwebsite-remove: win_iis.remove_site: - name: Default Web Site - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - current_sites = __salt__['win_iis.list_sites']() + current_sites = __salt__["win_iis.list_sites"]() if name not in current_sites: - ret['comment'] = 'Site has already been removed: {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Site will be removed: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} + ret["comment"] = "Site has already been removed: {0}".format(name) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Site will be removed: {0}".format(name) + ret["changes"] = {"old": name, "new": None} else: - ret['comment'] = 'Removed site: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} - ret['result'] = __salt__['win_iis.remove_site'](name) + ret["comment"] = "Removed site: {0}".format(name) + ret["changes"] = {"old": name, "new": None} + ret["result"] = __salt__["win_iis.remove_site"](name) return ret -def create_binding(name, site, hostheader='', ipaddress='*', port=80, protocol='http', sslflags=0): - ''' +def create_binding( + name, site, hostheader="", ipaddress="*", port=80, protocol="http", sslflags=0 +): + """ Create an IIS binding. .. note: @@ -181,33 +175,29 @@ def create_binding(name, site, hostheader='', ipaddress='*', port=80, protocol=' - port: 443 - protocol: https - sslflags: 0 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": str(), "result": None} binding_info = _get_binding_info(hostheader, ipaddress, port) - current_bindings = __salt__['win_iis.list_bindings'](site) + current_bindings = __salt__["win_iis.list_bindings"](site) if binding_info in current_bindings: - ret['comment'] = 'Binding already present: {0}'.format(binding_info) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Binding will be created: {0}'.format(binding_info) - ret['changes'] = {'old': None, - 'new': binding_info} + ret["comment"] = "Binding already present: {0}".format(binding_info) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Binding will be created: {0}".format(binding_info) + ret["changes"] = {"old": None, "new": binding_info} else: - ret['comment'] = 'Created binding: {0}'.format(binding_info) - ret['changes'] = {'old': None, - 'new': binding_info} - ret['result'] = __salt__['win_iis.create_binding'](site, hostheader, ipaddress, - port, protocol, sslflags) + ret["comment"] = "Created binding: {0}".format(binding_info) + ret["changes"] = {"old": None, "new": binding_info} + ret["result"] = __salt__["win_iis.create_binding"]( + site, hostheader, ipaddress, port, protocol, sslflags + ) return ret -def remove_binding(name, site, hostheader='', ipaddress='*', port=80): - ''' +def remove_binding(name, site, hostheader="", ipaddress="*", port=80): + """ Remove an IIS binding. :param str site: The IIS site name. @@ -233,33 +223,29 @@ def remove_binding(name, site, hostheader='', ipaddress='*', port=80): - hostheader: site0.local - ipaddress: '*' - port: 443 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": str(), "result": None} binding_info = _get_binding_info(hostheader, ipaddress, port) - current_bindings = __salt__['win_iis.list_bindings'](site) + current_bindings = __salt__["win_iis.list_bindings"](site) if binding_info not in current_bindings: - ret['comment'] = 'Binding has already been removed: {0}'.format(binding_info) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Binding will be removed: {0}'.format(binding_info) - ret['changes'] = {'old': binding_info, - 'new': None} + ret["comment"] = "Binding has already been removed: {0}".format(binding_info) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Binding will be removed: {0}".format(binding_info) + ret["changes"] = {"old": binding_info, "new": None} else: - ret['comment'] = 'Removed binding: {0}'.format(binding_info) - ret['changes'] = {'old': binding_info, - 'new': None} - ret['result'] = __salt__['win_iis.remove_binding'](site, hostheader, - ipaddress, port) + ret["comment"] = "Removed binding: {0}".format(binding_info) + ret["changes"] = {"old": binding_info, "new": None} + ret["result"] = __salt__["win_iis.remove_binding"]( + site, hostheader, ipaddress, port + ) return ret -def create_cert_binding(name, site, hostheader='', ipaddress='*', port=443, sslflags=0): - ''' +def create_cert_binding(name, site, hostheader="", ipaddress="*", port=443, sslflags=0): + """ Assign a certificate to an IIS binding. .. note: @@ -296,40 +282,38 @@ def create_cert_binding(name, site, hostheader='', ipaddress='*', port=443, sslf - sslflags: 1 .. versionadded:: 2016.11.0 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": str(), "result": None} binding_info = _get_binding_info(hostheader, ipaddress, port) - current_cert_bindings = __salt__['win_iis.list_cert_bindings'](site) + current_cert_bindings = __salt__["win_iis.list_cert_bindings"](site) if binding_info in current_cert_bindings: - current_name = current_cert_bindings[binding_info]['certificatehash'] + current_name = current_cert_bindings[binding_info]["certificatehash"] if name == current_name: - ret['comment'] = 'Certificate binding already present: {0}'.format(name) - ret['result'] = True + ret["comment"] = "Certificate binding already present: {0}".format(name) + ret["result"] = True return ret - ret['comment'] = ('Certificate binding already present with a different' - ' thumbprint: {0}'.format(current_name)) - ret['result'] = False - elif __opts__['test']: - ret['comment'] = 'Certificate binding will be created: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} + ret["comment"] = ( + "Certificate binding already present with a different" + " thumbprint: {0}".format(current_name) + ) + ret["result"] = False + elif __opts__["test"]: + ret["comment"] = "Certificate binding will be created: {0}".format(name) + ret["changes"] = {"old": None, "new": name} else: - ret['comment'] = 'Created certificate binding: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} - ret['result'] = __salt__['win_iis.create_cert_binding'](name, site, hostheader, - ipaddress, port, sslflags) + ret["comment"] = "Created certificate binding: {0}".format(name) + ret["changes"] = {"old": None, "new": name} + ret["result"] = __salt__["win_iis.create_cert_binding"]( + name, site, hostheader, ipaddress, port, sslflags + ) return ret -def remove_cert_binding(name, site, hostheader='', ipaddress='*', port=443): - ''' +def remove_cert_binding(name, site, hostheader="", ipaddress="*", port=443): + """ Remove a certificate from an IIS binding. .. note: @@ -365,36 +349,34 @@ def remove_cert_binding(name, site, hostheader='', ipaddress='*', port=443): - port: 443 .. versionadded:: 2016.11.0 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": str(), "result": None} binding_info = _get_binding_info(hostheader, ipaddress, port) - current_cert_bindings = __salt__['win_iis.list_cert_bindings'](site) + current_cert_bindings = __salt__["win_iis.list_cert_bindings"](site) if binding_info not in current_cert_bindings: - ret['comment'] = 'Certificate binding has already been removed: {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Certificate binding will be removed: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} + ret["comment"] = "Certificate binding has already been removed: {0}".format( + name + ) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Certificate binding will be removed: {0}".format(name) + ret["changes"] = {"old": name, "new": None} else: - current_name = current_cert_bindings[binding_info]['certificatehash'] + current_name = current_cert_bindings[binding_info]["certificatehash"] if name == current_name: - ret['comment'] = 'Removed certificate binding: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} - ret['result'] = __salt__['win_iis.remove_cert_binding'](name, site, hostheader, - ipaddress, port) + ret["comment"] = "Removed certificate binding: {0}".format(name) + ret["changes"] = {"old": name, "new": None} + ret["result"] = __salt__["win_iis.remove_cert_binding"]( + name, site, hostheader, ipaddress, port + ) return ret def create_apppool(name): - ''' + """ Create an IIS application pool. .. note: @@ -412,33 +394,28 @@ def create_apppool(name): site0-apppool: win_iis.create_apppool: - name: site0 - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - current_apppools = __salt__['win_iis.list_apppools']() + current_apppools = __salt__["win_iis.list_apppools"]() if name in current_apppools: - ret['comment'] = 'Application pool already present: {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Application pool will be created: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} + ret["comment"] = "Application pool already present: {0}".format(name) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Application pool will be created: {0}".format(name) + ret["changes"] = {"old": None, "new": name} else: - ret['comment'] = 'Created application pool: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} - ret['result'] = __salt__['win_iis.create_apppool'](name) + ret["comment"] = "Created application pool: {0}".format(name) + ret["changes"] = {"old": None, "new": name} + ret["result"] = __salt__["win_iis.create_apppool"](name) return ret def remove_apppool(name): # Remove IIS AppPool - ''' + """ Remove an IIS application pool. :param str name: The name of the IIS application pool. @@ -450,32 +427,27 @@ def remove_apppool(name): defaultapppool-remove: win_iis.remove_apppool: - name: DefaultAppPool - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - current_apppools = __salt__['win_iis.list_apppools']() + current_apppools = __salt__["win_iis.list_apppools"]() if name not in current_apppools: - ret['comment'] = 'Application pool has already been removed: {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Application pool will be removed: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} + ret["comment"] = "Application pool has already been removed: {0}".format(name) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Application pool will be removed: {0}".format(name) + ret["changes"] = {"old": name, "new": None} else: - ret['comment'] = 'Removed application pool: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} - ret['result'] = __salt__['win_iis.remove_apppool'](name) + ret["comment"] = "Removed application pool: {0}".format(name) + ret["changes"] = {"old": name, "new": None} + ret["result"] = __salt__["win_iis.remove_apppool"](name) return ret def container_setting(name, container, settings=None): - ''' + """ Set the value of the setting for an IIS container. :param str name: The name of the IIS container. @@ -509,69 +481,81 @@ def container_setting(name, container, settings=None): logFile.logFormat: W3C logFile.period: Daily limits.maxUrlSegments: 32 - ''' + """ - identityType_map2string = {0: 'LocalSystem', 1: 'LocalService', 2: 'NetworkService', 3: 'SpecificUser', 4: 'ApplicationPoolIdentity'} - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + identityType_map2string = { + 0: "LocalSystem", + 1: "LocalService", + 2: "NetworkService", + 3: "SpecificUser", + 4: "ApplicationPoolIdentity", + } + ret = {"name": name, "changes": {}, "comment": str(), "result": None} if not settings: - ret['comment'] = 'No settings to change provided.' - ret['result'] = True + ret["comment"] = "No settings to change provided." + ret["result"] = True return ret ret_settings = { - 'changes': {}, - 'failures': {}, + "changes": {}, + "failures": {}, } - current_settings = __salt__['win_iis.get_container_setting'](name=name, - container=container, - settings=settings.keys()) + current_settings = __salt__["win_iis.get_container_setting"]( + name=name, container=container, settings=settings.keys() + ) for setting in settings: # map identity type from numeric to string for comparing - if setting == 'processModel.identityType' and settings[setting] in identityType_map2string.keys(): + if ( + setting == "processModel.identityType" + and settings[setting] in identityType_map2string.keys() + ): settings[setting] = identityType_map2string[settings[setting]] if str(settings[setting]) != str(current_settings[setting]): - ret_settings['changes'][setting] = {'old': current_settings[setting], - 'new': settings[setting]} - if not ret_settings['changes']: - ret['comment'] = 'Settings already contain the provided values.' - ret['result'] = True + ret_settings["changes"][setting] = { + "old": current_settings[setting], + "new": settings[setting], + } + if not ret_settings["changes"]: + ret["comment"] = "Settings already contain the provided values." + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Settings will be changed.' - ret['changes'] = ret_settings + elif __opts__["test"]: + ret["comment"] = "Settings will be changed." + ret["changes"] = ret_settings return ret - __salt__['win_iis.set_container_setting'](name=name, container=container, settings=settings) + __salt__["win_iis.set_container_setting"]( + name=name, container=container, settings=settings + ) - new_settings = __salt__['win_iis.get_container_setting'](name=name, - container=container, - settings=settings.keys()) + new_settings = __salt__["win_iis.get_container_setting"]( + name=name, container=container, settings=settings.keys() + ) for setting in settings: if str(settings[setting]) != str(new_settings[setting]): - ret_settings['failures'][setting] = {'old': current_settings[setting], - 'new': new_settings[setting]} - ret_settings['changes'].pop(setting, None) + ret_settings["failures"][setting] = { + "old": current_settings[setting], + "new": new_settings[setting], + } + ret_settings["changes"].pop(setting, None) - if ret_settings['failures']: - ret['comment'] = 'Some settings failed to change.' - ret['changes'] = ret_settings - ret['result'] = False + if ret_settings["failures"]: + ret["comment"] = "Some settings failed to change." + ret["changes"] = ret_settings + ret["result"] = False else: - ret['comment'] = 'Set settings to contain the provided values.' - ret['changes'] = ret_settings['changes'] - ret['result'] = True + ret["comment"] = "Set settings to contain the provided values." + ret["changes"] = ret_settings["changes"] + ret["result"] = True return ret def create_app(name, site, sourcepath, apppool=None): - ''' + """ Create an IIS application. .. note: @@ -605,32 +589,26 @@ def create_app(name, site, sourcepath, apppool=None): - site: site0 - sourcepath: C:\\inetpub\\site0\\v1 - apppool: site0 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": str(), "result": None} - current_apps = __salt__['win_iis.list_apps'](site) + current_apps = __salt__["win_iis.list_apps"](site) if name in current_apps: - ret['comment'] = 'Application already present: {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Application will be created: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} + ret["comment"] = "Application already present: {0}".format(name) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Application will be created: {0}".format(name) + ret["changes"] = {"old": None, "new": name} else: - ret['comment'] = 'Created application: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} - ret['result'] = __salt__['win_iis.create_app'](name, site, sourcepath, - apppool) + ret["comment"] = "Created application: {0}".format(name) + ret["changes"] = {"old": None, "new": name} + ret["result"] = __salt__["win_iis.create_app"](name, site, sourcepath, apppool) return ret def remove_app(name, site): - ''' + """ Remove an IIS application. :param str name: The application name. @@ -644,31 +622,26 @@ def remove_app(name, site): win_iis.remove_app: - name: v1 - site: site0 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": str(), "result": None} - current_apps = __salt__['win_iis.list_apps'](site) + current_apps = __salt__["win_iis.list_apps"](site) if name not in current_apps: - ret['comment'] = 'Application has already been removed: {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Application will be removed: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} + ret["comment"] = "Application has already been removed: {0}".format(name) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Application will be removed: {0}".format(name) + ret["changes"] = {"old": name, "new": None} else: - ret['comment'] = 'Removed application: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} - ret['result'] = __salt__['win_iis.remove_app'](name, site) + ret["comment"] = "Removed application: {0}".format(name) + ret["changes"] = {"old": name, "new": None} + ret["result"] = __salt__["win_iis.remove_app"](name, site) return ret -def create_vdir(name, site, sourcepath, app='/'): - ''' +def create_vdir(name, site, sourcepath, app="/"): + """ Create an IIS virtual directory. .. note: @@ -702,33 +675,27 @@ def create_vdir(name, site, sourcepath, app='/'): - site: site0 - sourcepath: C:\\inetpub\\vdirs\\foo - app: v1 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": str(), "result": None} - current_vdirs = __salt__['win_iis.list_vdirs'](site, app) + current_vdirs = __salt__["win_iis.list_vdirs"](site, app) if name in current_vdirs: - ret['comment'] = 'Virtual directory already present: {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Virtual directory will be created: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} + ret["comment"] = "Virtual directory already present: {0}".format(name) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Virtual directory will be created: {0}".format(name) + ret["changes"] = {"old": None, "new": name} else: - ret['comment'] = 'Created virtual directory: {0}'.format(name) - ret['changes'] = {'old': None, - 'new': name} - ret['result'] = __salt__['win_iis.create_vdir'](name, site, sourcepath, - app) + ret["comment"] = "Created virtual directory: {0}".format(name) + ret["changes"] = {"old": None, "new": name} + ret["result"] = __salt__["win_iis.create_vdir"](name, site, sourcepath, app) return ret -def remove_vdir(name, site, app='/'): - ''' +def remove_vdir(name, site, app="/"): + """ Remove an IIS virtual directory. :param str name: The virtual directory name. @@ -753,33 +720,28 @@ def remove_vdir(name, site, app='/'): - name: foo - site: site0 - app: v1 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": str(), "result": None} - current_vdirs = __salt__['win_iis.list_vdirs'](site, app) + current_vdirs = __salt__["win_iis.list_vdirs"](site, app) if name not in current_vdirs: - ret['comment'] = 'Virtual directory has already been removed: {0}'.format(name) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'Virtual directory will be removed: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} + ret["comment"] = "Virtual directory has already been removed: {0}".format(name) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Virtual directory will be removed: {0}".format(name) + ret["changes"] = {"old": name, "new": None} else: - ret['comment'] = 'Removed virtual directory: {0}'.format(name) - ret['changes'] = {'old': name, - 'new': None} - ret['result'] = __salt__['win_iis.remove_vdir'](name, site, app) + ret["comment"] = "Removed virtual directory: {0}".format(name) + ret["changes"] = {"old": name, "new": None} + ret["result"] = __salt__["win_iis.remove_vdir"](name, site, app) return ret def set_app(name, site, settings=None): # pylint: disable=anomalous-backslash-in-string - r''' + r""" .. versionadded:: 2017.7.0 Set the value of the setting for an IIS web application. @@ -813,63 +775,65 @@ def set_app(name, site, settings=None): password: pass physicalPath: c:\inetpub\wwwroot applicationPool: appPool0 - ''' + """ # pylint: enable=anomalous-backslash-in-string - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + ret = {"name": name, "changes": {}, "comment": str(), "result": None} if not settings: - ret['comment'] = 'No settings to change provided.' - ret['result'] = True + ret["comment"] = "No settings to change provided." + ret["result"] = True return ret ret_settings = { - 'changes': {}, - 'failures': {}, + "changes": {}, + "failures": {}, } - current_settings = __salt__['win_iis.get_webapp_settings'](name=name, - site=site, - settings=settings.keys()) + current_settings = __salt__["win_iis.get_webapp_settings"]( + name=name, site=site, settings=settings.keys() + ) for setting in settings: if str(settings[setting]) != str(current_settings[setting]): - ret_settings['changes'][setting] = {'old': current_settings[setting], - 'new': settings[setting]} - if not ret_settings['changes']: - ret['comment'] = 'Settings already contain the provided values.' - ret['result'] = True + ret_settings["changes"][setting] = { + "old": current_settings[setting], + "new": settings[setting], + } + if not ret_settings["changes"]: + ret["comment"] = "Settings already contain the provided values." + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Settings will be changed.' - ret['changes'] = ret_settings + elif __opts__["test"]: + ret["comment"] = "Settings will be changed." + ret["changes"] = ret_settings return ret - __salt__['win_iis.set_webapp_settings'](name=name, site=site, - settings=settings) - new_settings = __salt__['win_iis.get_webapp_settings'](name=name, site=site, settings=settings.keys()) + __salt__["win_iis.set_webapp_settings"](name=name, site=site, settings=settings) + new_settings = __salt__["win_iis.get_webapp_settings"]( + name=name, site=site, settings=settings.keys() + ) for setting in settings: if str(settings[setting]) != str(new_settings[setting]): - ret_settings['failures'][setting] = {'old': current_settings[setting], - 'new': new_settings[setting]} - ret_settings['changes'].pop(setting, None) + ret_settings["failures"][setting] = { + "old": current_settings[setting], + "new": new_settings[setting], + } + ret_settings["changes"].pop(setting, None) - if ret_settings['failures']: - ret['comment'] = 'Some settings failed to change.' - ret['changes'] = ret_settings - ret['result'] = False + if ret_settings["failures"]: + ret["comment"] = "Some settings failed to change." + ret["changes"] = ret_settings + ret["result"] = False else: - ret['comment'] = 'Set settings to contain the provided values.' - ret['changes'] = ret_settings['changes'] - ret['result'] = True + ret["comment"] = "Set settings to contain the provided values." + ret["changes"] = ret_settings["changes"] + ret["result"] = True return ret def webconfiguration_settings(name, settings=None): - r''' + r""" Set the value of webconfiguration settings. :param str name: The name of the IIS PSPath containing the settings. @@ -922,69 +886,88 @@ def webconfiguration_settings(name, settings=None): system.applicationHost/sites: 'Collection[{name: site0}].logFile.directory': 'C:\logs\iis\site0' - ''' + """ - ret = {'name': name, - 'changes': {}, - 'comment': str(), - 'result': None} + ret = {"name": name, "changes": {}, "comment": str(), "result": None} if not settings: - ret['comment'] = 'No settings to change provided.' - ret['result'] = True + ret["comment"] = "No settings to change provided." + ret["result"] = True return ret ret_settings = { - 'changes': {}, - 'failures': {}, + "changes": {}, + "failures": {}, } settings_list = list() for filter, filter_settings in settings.items(): for setting_name, value in filter_settings.items(): - settings_list.append({'filter': filter, 'name': setting_name, 'value': value}) + settings_list.append( + {"filter": filter, "name": setting_name, "value": value} + ) - current_settings_list = __salt__['win_iis.get_webconfiguration_settings'](name=name, settings=settings_list) + current_settings_list = __salt__["win_iis.get_webconfiguration_settings"]( + name=name, settings=settings_list + ) for idx, setting in enumerate(settings_list): - is_collection = setting['name'].split('.')[-1] == 'Collection' + is_collection = setting["name"].split(".")[-1] == "Collection" # If this is a new setting and not an update to an existing setting if len(current_settings_list) <= idx: - ret_settings['changes'][setting['filter'] + '.' + setting['name']] = {'old': {}, - 'new': settings_list[idx]['value']} - elif ((is_collection and list(map(dict, setting['value'])) != list(map(dict, current_settings_list[idx]['value']))) - or (not is_collection and str(setting['value']) != str(current_settings_list[idx]['value']))): - ret_settings['changes'][setting['filter'] + '.' + setting['name']] = {'old': current_settings_list[idx]['value'], - 'new': settings_list[idx]['value']} - if not ret_settings['changes']: - ret['comment'] = 'Settings already contain the provided values.' - ret['result'] = True + ret_settings["changes"][setting["filter"] + "." + setting["name"]] = { + "old": {}, + "new": settings_list[idx]["value"], + } + elif ( + is_collection + and list(map(dict, setting["value"])) + != list(map(dict, current_settings_list[idx]["value"])) + ) or ( + not is_collection + and str(setting["value"]) != str(current_settings_list[idx]["value"]) + ): + ret_settings["changes"][setting["filter"] + "." + setting["name"]] = { + "old": current_settings_list[idx]["value"], + "new": settings_list[idx]["value"], + } + if not ret_settings["changes"]: + ret["comment"] = "Settings already contain the provided values." + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Settings will be changed.' - ret['changes'] = ret_settings + elif __opts__["test"]: + ret["comment"] = "Settings will be changed." + ret["changes"] = ret_settings return ret - success = __salt__['win_iis.set_webconfiguration_settings'](name=name, settings=settings_list) + success = __salt__["win_iis.set_webconfiguration_settings"]( + name=name, settings=settings_list + ) - new_settings_list = __salt__['win_iis.get_webconfiguration_settings'](name=name, settings=settings_list) + new_settings_list = __salt__["win_iis.get_webconfiguration_settings"]( + name=name, settings=settings_list + ) for idx, setting in enumerate(settings_list): - is_collection = setting['name'].split('.')[-1] == 'Collection' - if ((is_collection and setting['value'] != new_settings_list[idx]['value']) - or (not is_collection and str(setting['value']) != str(new_settings_list[idx]['value']))): - ret_settings['failures'][setting['filter'] + '.' + setting['name']] = {'old': current_settings_list[idx]['value'], - 'new': new_settings_list[idx]['value']} - ret_settings['changes'].get(setting['filter'] + '.' + setting['name'], None) + is_collection = setting["name"].split(".")[-1] == "Collection" + if (is_collection and setting["value"] != new_settings_list[idx]["value"]) or ( + not is_collection + and str(setting["value"]) != str(new_settings_list[idx]["value"]) + ): + ret_settings["failures"][setting["filter"] + "." + setting["name"]] = { + "old": current_settings_list[idx]["value"], + "new": new_settings_list[idx]["value"], + } + ret_settings["changes"].get(setting["filter"] + "." + setting["name"], None) - if ret_settings['failures']: - ret['comment'] = 'Some settings failed to change.' - ret['changes'] = ret_settings - ret['result'] = False + if ret_settings["failures"]: + ret["comment"] = "Some settings failed to change." + ret["changes"] = ret_settings + ret["result"] = False else: - ret['comment'] = 'Set settings to contain the provided values.' - ret['changes'] = ret_settings['changes'] - ret['result'] = success + ret["comment"] = "Set settings to contain the provided values." + ret["changes"] = ret_settings["changes"] + ret["result"] = success return ret diff --git a/salt/states/win_lgpo.py b/salt/states/win_lgpo.py index ac429b6f510..d9a33b7b1e2 100644 --- a/salt/states/win_lgpo.py +++ b/salt/states/win_lgpo.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Windows Local Group Policy ================================= @@ -103,9 +103,10 @@ Multiple policy configuration "Set the intranet update service for detecting updates": http://mywsus "Set the intranet statistics server": http://mywsus - cumulative_rights_assignments: True -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import salt libs @@ -120,41 +121,41 @@ import salt.utils.win_functions from salt.ext import six log = logging.getLogger(__name__) -__virtualname__ = 'lgpo' -__func_alias__ = {'set_': 'set'} +__virtualname__ = "lgpo" +__func_alias__ = {"set_": "set"} def __virtual__(): - ''' + """ load this state if the win_lgpo module exists - ''' - return __virtualname__ if 'lgpo.set' in __salt__ else False + """ + if "lgpo.set" in __salt__: + return __virtualname__ + return (False, "lgpo module could not be loaded") def _compare_policies(new_policy, current_policy): - ''' + """ Helper function that returns ``True`` if the policies are the same, otherwise ``False`` - ''' + """ # Compared dicts, lists, and strings if isinstance(new_policy, (six.string_types, six.integer_types)): return new_policy == current_policy elif isinstance(new_policy, list): if isinstance(current_policy, list): - return salt.utils.data.compare_lists(new_policy, - current_policy) == {} + return salt.utils.data.compare_lists(new_policy, current_policy) == {} else: return False elif isinstance(new_policy, dict): if isinstance(current_policy, dict): - return salt.utils.data.compare_dicts(new_policy, - current_policy) == {} + return salt.utils.data.compare_dicts(new_policy, current_policy) == {} else: return False def _convert_to_unicode(data): - ''' + """ Helper function that makes sure all items in the dictionary are unicode for comparing the existing state with the desired state. This function is only needed for Python 2 and can be removed once we've migrated to Python 3. @@ -168,28 +169,30 @@ def _convert_to_unicode(data): character separated by `/x00`) In Python 3 it returns a utf-8 string. This will just remove all the null bytes (`/x00`), again comparing apples to apples. - ''' + """ if isinstance(data, six.string_types): - data = data.replace('\x00', '') + data = data.replace("\x00", "") return salt.utils.stringutils.to_unicode(data) elif isinstance(data, dict): - return dict((_convert_to_unicode(k), - _convert_to_unicode(v)) - for k, v in data.items()) + return dict( + (_convert_to_unicode(k), _convert_to_unicode(v)) for k, v in data.items() + ) elif isinstance(data, list): return list(_convert_to_unicode(v) for v in data) else: return data -def set_(name, - setting=None, - policy_class=None, - computer_policy=None, - user_policy=None, - cumulative_rights_assignments=True, - adml_language='en-US'): - ''' +def set_( + name, + setting=None, + policy_class=None, + computer_policy=None, + user_policy=None, + cumulative_rights_assignments=True, + adml_language="en-US", +): + """ Ensure the specified policy is set. .. warning:: @@ -226,150 +229,168 @@ def set_(name, adml_language (str): The adml language to use for AMDX policy data/display conversions. Default is ``en-US`` - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} - policy_classes = ['machine', 'computer', 'user', 'both'] + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} + policy_classes = ["machine", "computer", "user", "both"] class_map = { - 'computer': 'Computer Configuration', - 'machine': 'Computer Configuration', - 'user': 'User Configuration' + "computer": "Computer Configuration", + "machine": "Computer Configuration", + "user": "User Configuration", } if not setting and not computer_policy and not user_policy: - msg = 'At least one of the parameters setting, computer_policy, or ' \ - 'user_policy must be specified.' - ret['result'] = False - ret['comment'] = msg + msg = ( + "At least one of the parameters setting, computer_policy, or " + "user_policy must be specified." + ) + ret["result"] = False + ret["comment"] = msg return ret if setting and not policy_class: - msg = 'A single policy setting was specified but the policy_class ' \ - 'was not specified.' - ret['result'] = False - ret['comment'] = msg + msg = ( + "A single policy setting was specified but the policy_class " + "was not specified." + ) + ret["result"] = False + ret["comment"] = msg return ret if setting and (computer_policy or user_policy): - msg = 'The setting and computer_policy/user_policy parameters are ' \ - 'mutually exclusive. Please specify either a policy name and ' \ - 'setting or a computer_policy and/or user_policy dict' - ret['result'] = False - ret['comment'] = msg + msg = ( + "The setting and computer_policy/user_policy parameters are " + "mutually exclusive. Please specify either a policy name and " + "setting or a computer_policy and/or user_policy dict" + ) + ret["result"] = False + ret["comment"] = msg return ret if policy_class and policy_class.lower() not in policy_classes: - msg = 'The policy_class parameter must be one of the following: {0}' - ret['result'] = False - ret['comment'] = msg + msg = "The policy_class parameter must be one of the following: {0}" + ret["result"] = False + ret["comment"] = msg return ret if not setting: if computer_policy and user_policy: - policy_class = 'both' + policy_class = "both" elif computer_policy: - policy_class = 'machine' + policy_class = "machine" elif user_policy: - policy_class = 'user' + policy_class = "user" if computer_policy and not isinstance(computer_policy, dict): - msg = 'The computer_policy must be specified as a dict.' - ret['result'] = False - ret['comment'] = msg + msg = "The computer_policy must be specified as a dict." + ret["result"] = False + ret["comment"] = msg return ret if user_policy and not isinstance(user_policy, dict): - msg = 'The user_policy must be specified as a dict.' - ret['result'] = False - ret['comment'] = msg + msg = "The user_policy must be specified as a dict." + ret["result"] = False + ret["comment"] = msg return ret else: user_policy = {} computer_policy = {} - if policy_class.lower() == 'both': + if policy_class.lower() == "both": user_policy[name] = setting computer_policy[name] = setting - elif policy_class.lower() == 'user': + elif policy_class.lower() == "user": user_policy[name] = setting - elif policy_class.lower() in ['machine', 'computer']: + elif policy_class.lower() in ["machine", "computer"]: computer_policy[name] = setting pol_data = { - 'user': { - 'requested_policy': user_policy, - 'policy_lookup': {}}, - 'machine': { - 'requested_policy': computer_policy, - 'policy_lookup': {}}} + "user": {"requested_policy": user_policy, "policy_lookup": {}}, + "machine": {"requested_policy": computer_policy, "policy_lookup": {}}, + } current_policy = {} deprecation_comments = [] for p_class, p_data in six.iteritems(pol_data): - if p_data['requested_policy']: - for p_name, _ in six.iteritems(p_data['requested_policy']): - lookup = __salt__['lgpo.get_policy_info']( + if p_data["requested_policy"]: + for p_name, _ in six.iteritems(p_data["requested_policy"]): + lookup = __salt__["lgpo.get_policy_info"]( policy_name=p_name, policy_class=p_class, - adml_language=adml_language) - if lookup['policy_found']: - pol_data[p_class]['policy_lookup'][p_name] = lookup + adml_language=adml_language, + ) + if lookup["policy_found"]: + pol_data[p_class]["policy_lookup"][p_name] = lookup # Since we found the policy, let's get the current setting # as well current_policy.setdefault(class_map[p_class], {}) - current_policy[class_map[p_class]][p_name] = __salt__['lgpo.get_policy']( + current_policy[class_map[p_class]][p_name] = __salt__[ + "lgpo.get_policy" + ]( policy_name=p_name, policy_class=p_class, adml_language=adml_language, - return_value_only=True) + return_value_only=True, + ) # Validate element names - if isinstance(p_data['requested_policy'][p_name], dict): + if isinstance(p_data["requested_policy"][p_name], dict): valid_names = [] - for element in lookup['policy_elements']: - valid_names.extend(element['element_aliases']) - for e_name in p_data['requested_policy'][p_name]: + for element in lookup["policy_elements"]: + valid_names.extend(element["element_aliases"]) + for e_name in p_data["requested_policy"][p_name]: if e_name not in valid_names: - new_e_name = e_name.split(':')[-1].strip() + new_e_name = e_name.split(":")[-1].strip() # If we find an invalid name, test the new # format. If found, replace the old with the # new if new_e_name in valid_names: - msg = 'The LGPO module changed the way ' \ - 'it gets policy element names.\n'\ - '"{0}" is no longer valid.\n'\ - 'Please use "{1}" instead.' \ - ''.format(e_name, new_e_name) - salt.utils.versions.warn_until('Phosphorus', msg) - pol_data[p_class]['requested_policy'][p_name][new_e_name] = \ - pol_data[p_class]['requested_policy'][p_name].pop(e_name) + msg = ( + "The LGPO module changed the way " + "it gets policy element names.\n" + '"{0}" is no longer valid.\n' + 'Please use "{1}" instead.' + "".format(e_name, new_e_name) + ) + salt.utils.versions.warn_until("Phosphorus", msg) + pol_data[p_class]["requested_policy"][p_name][ + new_e_name + ] = pol_data[p_class]["requested_policy"][ + p_name + ].pop( + e_name + ) deprecation_comments.append(msg) else: - msg = 'Invalid element name: {0}'.format(e_name) - ret['comment'] = '\n'.join([ret['comment'], msg]).strip() - ret['result'] = False + msg = "Invalid element name: {0}".format(e_name) + ret["comment"] = "\n".join( + [ret["comment"], msg] + ).strip() + ret["result"] = False else: - ret['comment'] = '\n'.join([ret['comment'], lookup['message']]).strip() - ret['result'] = False - if not ret['result']: - deprecation_comments.append(ret['comment']) - ret['comment'] = '\n'.join(deprecation_comments).strip() + ret["comment"] = "\n".join( + [ret["comment"], lookup["message"]] + ).strip() + ret["result"] = False + if not ret["result"]: + deprecation_comments.append(ret["comment"]) + ret["comment"] = "\n".join(deprecation_comments).strip() return ret - log.debug('pol_data == %s', pol_data) - log.debug('current policy == %s', current_policy) + log.debug("pol_data == %s", pol_data) + log.debug("current policy == %s", current_policy) # compare policies policy_changes = [] for p_class, p_data in six.iteritems(pol_data): - requested_policy = p_data.get('requested_policy') + requested_policy = p_data.get("requested_policy") if requested_policy: for p_name, p_setting in six.iteritems(requested_policy): if p_name in current_policy[class_map[p_class]]: # compare - log.debug('need to compare %s from current/requested ' - 'policy', p_name) + log.debug( + "need to compare %s from current/requested " "policy", p_name + ) changes = False requested_policy_json = salt.utils.json.dumps( - p_data['requested_policy'][p_name], - sort_keys=True).lower() + p_data["requested_policy"][p_name], sort_keys=True + ).lower() current_policy_json = salt.utils.json.dumps( - current_policy[class_map[p_class]][p_name], - sort_keys=True).lower() + current_policy[class_map[p_class]][p_name], sort_keys=True + ).lower() - requested_policy_check = salt.utils.json.loads(requested_policy_json) + requested_policy_check = salt.utils.json.loads( + requested_policy_json + ) current_policy_check = salt.utils.json.loads(current_policy_json) if six.PY2: @@ -377,72 +398,101 @@ def set_(name, # Are the requested and current policies identical policies_are_equal = _compare_policies( - requested_policy_check, current_policy_check) + requested_policy_check, current_policy_check + ) if not policies_are_equal: - if p_data['policy_lookup'][p_name]['rights_assignment'] and cumulative_rights_assignments: - for user in p_data['requested_policy'][p_name]: - if user not in current_policy[class_map[p_class]][p_name]: + if ( + p_data["policy_lookup"][p_name]["rights_assignment"] + and cumulative_rights_assignments + ): + for user in p_data["requested_policy"][p_name]: + if ( + user + not in current_policy[class_map[p_class]][p_name] + ): user = salt.utils.win_functions.get_sam_name(user) - if user not in current_policy[class_map[p_class]][p_name]: + if ( + user + not in current_policy[class_map[p_class]][ + p_name + ] + ): changes = True else: changes = True if changes: - log.debug('%s current policy != requested policy', p_name) - log.debug('We compared %s to %s', requested_policy_json, current_policy_json) + log.debug("%s current policy != requested policy", p_name) + log.debug( + "We compared %s to %s", + requested_policy_json, + current_policy_json, + ) policy_changes.append(p_name) else: msg = '"{0}" is already set'.format(p_name) log.debug(msg) else: policy_changes.append(p_name) - log.debug('policy %s is not set, we will configure it', p_name) - if __opts__['test']: + log.debug("policy %s is not set, we will configure it", p_name) + if __opts__["test"]: if policy_changes: - msg = 'The following policies are set to change:\n{0}' \ - ''.format('\n'.join(policy_changes)) - ret['result'] = None + msg = "The following policies are set to change:\n{0}" "".format( + "\n".join(policy_changes) + ) + ret["result"] = None else: - msg = 'All specified policies are properly configured' + msg = "All specified policies are properly configured" deprecation_comments.append(msg) - ret['comment'] = '\n'.join(deprecation_comments).strip() + ret["comment"] = "\n".join(deprecation_comments).strip() else: if policy_changes: - _ret = __salt__['lgpo.set']( - computer_policy=pol_data['machine']['requested_policy'], - user_policy=pol_data['user']['requested_policy'], + _ret = __salt__["lgpo.set"]( + computer_policy=pol_data["machine"]["requested_policy"], + user_policy=pol_data["user"]["requested_policy"], cumulative_rights_assignments=cumulative_rights_assignments, - adml_language=adml_language) + adml_language=adml_language, + ) if _ret: - ret['result'] = _ret + ret["result"] = _ret new_policy = {} for p_class, p_data in six.iteritems(pol_data): - if p_data['requested_policy']: - for p_name, p_setting in six.iteritems(p_data['requested_policy']): + if p_data["requested_policy"]: + for p_name, p_setting in six.iteritems( + p_data["requested_policy"] + ): new_policy.setdefault(class_map[p_class], {}) - new_policy[class_map[p_class]][p_name] = __salt__['lgpo.get_policy']( + new_policy[class_map[p_class]][p_name] = __salt__[ + "lgpo.get_policy" + ]( policy_name=p_name, policy_class=p_class, adml_language=adml_language, - return_value_only=True) - ret['changes'] = salt.utils.dictdiffer.deep_diff( - old=current_policy, new=new_policy) - if ret['changes']: - msg = 'The following policies changed:\n{0}' \ - ''.format('\n'.join(policy_changes)) + return_value_only=True, + ) + ret["changes"] = salt.utils.dictdiffer.deep_diff( + old=current_policy, new=new_policy + ) + if ret["changes"]: + msg = "The following policies changed:\n{0}" "".format( + "\n".join(policy_changes) + ) else: - msg = 'The following policies are in the correct ' \ - 'state:\n{0}'.format('\n'.join(policy_changes)) + msg = ( + "The following policies are in the correct " + "state:\n{0}".format("\n".join(policy_changes)) + ) else: - msg = 'Errors occurred while attempting to configure ' \ - 'policies: {0}'.format(_ret) - ret['result'] = False + msg = ( + "Errors occurred while attempting to configure " + "policies: {0}".format(_ret) + ) + ret["result"] = False deprecation_comments.append(msg) - ret['comment'] = '\n'.join(deprecation_comments).strip() + ret["comment"] = "\n".join(deprecation_comments).strip() else: - msg = 'All specified policies are properly configured' + msg = "All specified policies are properly configured" deprecation_comments.append(msg) - ret['comment'] = '\n'.join(deprecation_comments).strip() + ret["comment"] = "\n".join(deprecation_comments).strip() return ret diff --git a/salt/states/win_license.py b/salt/states/win_license.py index ded6e113a1a..2ebb668f301 100644 --- a/salt/states/win_license.py +++ b/salt/states/win_license.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation and activation of windows licenses =============================================== @@ -9,63 +9,61 @@ Install and activate windows licenses XXXXX-XXXXX-XXXXX-XXXXX-XXXXX: license.activate -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs import salt.utils.platform log = logging.getLogger(__name__) -__virtualname__ = 'license' +__virtualname__ = "license" def __virtual__(): - ''' + """ Only work on Windows - ''' + """ if salt.utils.platform.is_windows(): return __virtualname__ - return False + return (False, "Only Windows OS supported") def activate(name): - ''' + """ Install and activate the given product key name The 5x5 product key given to you by Microsoft - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} product_key = name - license_info = __salt__['license.info']() + license_info = __salt__["license.info"]() licensed = False key_match = False if license_info is not None: - licensed = license_info['licensed'] - key_match = license_info['partial_key'] in product_key + licensed = license_info["licensed"] + key_match = license_info["partial_key"] in product_key if not key_match: - out = __salt__['license.install'](product_key) + out = __salt__["license.install"](product_key) licensed = False - if 'successfully' not in out: - ret['result'] = False - ret['comment'] += 'Unable to install the given product key is it valid?' + if "successfully" not in out: + ret["result"] = False + ret["comment"] += "Unable to install the given product key is it valid?" return ret if not licensed: - out = __salt__['license.activate']() - if 'successfully' not in out: - ret['result'] = False - ret['comment'] += 'Unable to activate the given product key.' + out = __salt__["license.activate"]() + if "successfully" not in out: + ret["result"] = False + ret["comment"] += "Unable to activate the given product key." return ret - ret['comment'] += 'Windows is now activated.' + ret["comment"] += "Windows is now activated." else: - ret['comment'] += 'Windows is already activated.' + ret["comment"] += "Windows is already activated." return ret diff --git a/salt/states/win_network.py b/salt/states/win_network.py index 7f7bc54cc9a..accc24b0e2f 100644 --- a/salt/states/win_network.py +++ b/salt/states/win_network.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Configuration of network interfaces on Windows hosts ==================================================== @@ -58,8 +58,8 @@ default gateway using the ``gateway`` parameter: - ip_addrs: - 10.2.3.4/24 - gateway: 10.2.3.1 -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -68,153 +68,145 @@ import logging import salt.utils.data import salt.utils.platform import salt.utils.validate.net -from salt.ext.six.moves import range from salt.exceptions import CommandExecutionError from salt.ext import six +from salt.ext.six.moves import range # Set up logging log = logging.getLogger(__name__) -__VALID_PROTO = ('static', 'dhcp') +__VALID_PROTO = ("static", "dhcp") # Define the module's virtual name -__virtualname__ = 'network' +__virtualname__ = "network" def __virtual__(): - ''' + """ Confine this module to Windows systems with the required execution module available. - ''' - if salt.utils.platform.is_windows() and 'ip.get_interface' in __salt__: + """ + if salt.utils.platform.is_windows() and "ip.get_interface" in __salt__: return __virtualname__ - return False + return (False, "ip module could not be loaded") def _validate(dns_proto, dns_servers, ip_proto, ip_addrs, gateway): - ''' + """ Ensure that the configuration passed is formatted correctly and contains valid IP addresses, etc. - ''' + """ errors = [] # Validate DNS configuration - if dns_proto == 'dhcp': + if dns_proto == "dhcp": if dns_servers is not None: errors.append( - 'The dns_servers param cannot be set if unless dns_proto is ' - 'set to \'static\'' + "The dns_servers param cannot be set if unless dns_proto is " + "set to 'static'" ) else: - if str(dns_servers).lower() in ['none', '[]']: + if str(dns_servers).lower() in ["none", "[]"]: pass elif not isinstance(dns_servers, list): - errors.append( - 'The dns_servers param must be formatted as a list' - ) + errors.append("The dns_servers param must be formatted as a list") else: - bad_ips = [x for x in dns_servers - if not salt.utils.validate.net.ipv4_addr(x)] + bad_ips = [ + x for x in dns_servers if not salt.utils.validate.net.ipv4_addr(x) + ] if bad_ips: - errors.append('Invalid DNS server IPs: {0}' - .format(', '.join(bad_ips))) + errors.append("Invalid DNS server IPs: {0}".format(", ".join(bad_ips))) # Validate IP configuration - if ip_proto == 'dhcp': + if ip_proto == "dhcp": if ip_addrs is not None: errors.append( - 'The ip_addrs param cannot be set if unless ip_proto is set ' - 'to \'static\'' + "The ip_addrs param cannot be set if unless ip_proto is set " + "to 'static'" ) if gateway is not None: errors.append( - 'A gateway IP cannot be set if unless ip_proto is set to ' - '\'static\'' + "A gateway IP cannot be set if unless ip_proto is set to " "'static'" ) else: if not ip_addrs: - errors.append( - 'The ip_addrs param is required to set static IPs' - ) + errors.append("The ip_addrs param is required to set static IPs") elif not isinstance(ip_addrs, list): - errors.append( - 'The ip_addrs param must be formatted as a list' - ) + errors.append("The ip_addrs param must be formatted as a list") else: - bad_ips = [x for x in ip_addrs - if not salt.utils.validate.net.ipv4_addr(x)] + bad_ips = [x for x in ip_addrs if not salt.utils.validate.net.ipv4_addr(x)] if bad_ips: - errors.append('The following static IPs are invalid: ' - '{0}'.format(', '.join(bad_ips))) + errors.append( + "The following static IPs are invalid: " + "{0}".format(", ".join(bad_ips)) + ) # Validate default gateway if gateway is not None: if not salt.utils.validate.net.ipv4_addr(gateway): - errors.append('Gateway IP {0} is invalid'.format(gateway)) + errors.append("Gateway IP {0} is invalid".format(gateway)) return errors def _addrdict_to_ip_addrs(addrs): - ''' + """ Extracts a list of IP/CIDR expressions from a list of addrdicts, as retrieved from ip.get_interface - ''' + """ return [ - '{0}/{1}'.format(x['IP Address'], x['Subnet'].rsplit('/', 1)[-1]) - for x in addrs + "{0}/{1}".format(x["IP Address"], x["Subnet"].rsplit("/", 1)[-1]) for x in addrs ] def _changes(cur, dns_proto, dns_servers, ip_proto, ip_addrs, gateway): - ''' + """ Compares the current interface against the desired configuration and returns a dictionary describing the changes that need to be made. - ''' + """ changes = {} - cur_dns_proto = ( - 'static' if 'Statically Configured DNS Servers' in cur - else 'dhcp' - ) - if cur_dns_proto == 'static': - if isinstance(cur['Statically Configured DNS Servers'], list): - cur_dns_servers = cur['Statically Configured DNS Servers'] + cur_dns_proto = "static" if "Statically Configured DNS Servers" in cur else "dhcp" + if cur_dns_proto == "static": + if isinstance(cur["Statically Configured DNS Servers"], list): + cur_dns_servers = cur["Statically Configured DNS Servers"] else: - cur_dns_servers = [cur['Statically Configured DNS Servers']] - if set(dns_servers or ['None']) != set(cur_dns_servers): - changes['dns_servers'] = dns_servers - elif 'DNS servers configured through DHCP' in cur: - cur_dns_servers = cur['DNS servers configured through DHCP'] - if dns_proto == 'static': + cur_dns_servers = [cur["Statically Configured DNS Servers"]] + if set(dns_servers or ["None"]) != set(cur_dns_servers): + changes["dns_servers"] = dns_servers + elif "DNS servers configured through DHCP" in cur: + cur_dns_servers = cur["DNS servers configured through DHCP"] + if dns_proto == "static": # If we're currently set to 'dhcp' but moving to 'static', specify the changes. - if set(dns_servers or ['None']) != set(cur_dns_servers): - changes['dns_servers'] = dns_servers + if set(dns_servers or ["None"]) != set(cur_dns_servers): + changes["dns_servers"] = dns_servers - cur_ip_proto = 'static' if cur['DHCP enabled'] == 'No' else 'dhcp' - cur_ip_addrs = _addrdict_to_ip_addrs(cur.get('ip_addrs', [])) - cur_gateway = cur.get('Default Gateway') + cur_ip_proto = "static" if cur["DHCP enabled"] == "No" else "dhcp" + cur_ip_addrs = _addrdict_to_ip_addrs(cur.get("ip_addrs", [])) + cur_gateway = cur.get("Default Gateway") if dns_proto != cur_dns_proto: - changes['dns_proto'] = dns_proto + changes["dns_proto"] = dns_proto if ip_proto != cur_ip_proto: - changes['ip_proto'] = ip_proto + changes["ip_proto"] = ip_proto if set(ip_addrs or []) != set(cur_ip_addrs): - if ip_proto == 'static': - changes['ip_addrs'] = ip_addrs + if ip_proto == "static": + changes["ip_addrs"] = ip_addrs if gateway != cur_gateway: - if ip_proto == 'static': - changes['gateway'] = gateway + if ip_proto == "static": + changes["gateway"] = gateway return changes -def managed(name, - dns_proto=None, - dns_servers=None, - ip_proto=None, - ip_addrs=None, - gateway=None, - enabled=True, - **kwargs): - ''' +def managed( + name, + dns_proto=None, + dns_servers=None, + ip_proto=None, + ip_addrs=None, + gateway=None, + enabled=True, + **kwargs +): + """ Ensure that the named interface is configured properly. Args: @@ -271,12 +263,12 @@ def managed(name, - dns_proto: static - dns_servers: [] - ip_proto: dhcp - ''' + """ ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Interface \'{0}\' is up to date'.format(name) + "name": name, + "changes": {}, + "result": True, + "comment": "Interface '{0}' is up to date".format(name), } dns_proto = six.text_type(dns_proto).lower() @@ -284,159 +276,170 @@ def managed(name, errors = [] if dns_proto not in __VALID_PROTO: - ret['result'] = False - errors.append('dns_proto must be one of the following: {0}' - .format(', '.join(__VALID_PROTO))) + ret["result"] = False + errors.append( + "dns_proto must be one of the following: {0}".format( + ", ".join(__VALID_PROTO) + ) + ) if ip_proto not in __VALID_PROTO: - errors.append('ip_proto must be one of the following: {0}' - .format(', '.join(__VALID_PROTO))) + errors.append( + "ip_proto must be one of the following: {0}".format( + ", ".join(__VALID_PROTO) + ) + ) if errors: - ret['result'] = False - ret['comment'] = '\n'.join(errors) + ret["result"] = False + ret["comment"] = "\n".join(errors) return ret try: - currently_enabled = __salt__['ip.is_enabled'](name) + currently_enabled = __salt__["ip.is_enabled"](name) except CommandExecutionError: currently_enabled = False if not enabled: if currently_enabled: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Interface \'{0}\' will be disabled' - .format(name)) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Interface '{0}' will be disabled".format(name) else: - ret['result'] = __salt__['ip.disable'](name) - if not ret['result']: - ret['comment'] = ('Failed to disable interface \'{0}\'' - .format(name)) + ret["result"] = __salt__["ip.disable"](name) + if not ret["result"]: + ret["comment"] = "Failed to disable interface '{0}'".format(name) else: - ret['comment'] += ' (already disabled)' + ret["comment"] += " (already disabled)" return ret else: if not currently_enabled: - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Interface \'{0}\' will be enabled' - .format(name)) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Interface '{0}' will be enabled".format(name) else: - if not __salt__['ip.enable'](name): - ret['result'] = False - ret['comment'] = ('Failed to enable interface \'{0}\' to ' - 'make changes'.format(name)) + if not __salt__["ip.enable"](name): + ret["result"] = False + ret["comment"] = ( + "Failed to enable interface '{0}' to " + "make changes".format(name) + ) return ret errors = _validate(dns_proto, dns_servers, ip_proto, ip_addrs, gateway) if errors: - ret['result'] = False - ret['comment'] = ('The following SLS configuration errors were ' - 'detected:\n- {0}'.format('\n- '.join(errors))) + ret["result"] = False + ret["comment"] = ( + "The following SLS configuration errors were " + "detected:\n- {0}".format("\n- ".join(errors)) + ) return ret - old = __salt__['ip.get_interface'](name) + old = __salt__["ip.get_interface"](name) if not old: - ret['result'] = False - ret['comment'] = ('Unable to get current configuration for ' - 'interface \'{0}\''.format(name)) + ret["result"] = False + ret["comment"] = ( + "Unable to get current configuration for " + "interface '{0}'".format(name) + ) return ret - changes = _changes(old, - dns_proto, - dns_servers, - ip_proto, - ip_addrs, - gateway) + changes = _changes(old, dns_proto, dns_servers, ip_proto, ip_addrs, gateway) # If dns_servers is the default `None` make no changes # To clear the list, pass an empty dict - if str(dns_servers).lower() == 'none': - changes.pop('dns_servers', None) + if str(dns_servers).lower() == "none": + changes.pop("dns_servers", None) if not changes: return ret - if __opts__['test']: + if __opts__["test"]: comments = [] - if 'dns_proto' in changes: - comments.append('DNS protocol will be changed to: {0}' - .format(changes['dns_proto'])) - if dns_proto == 'static' and 'dns_servers' in changes: - if len(changes['dns_servers']) == 0: - comments.append('The list of DNS servers will be cleared') + if "dns_proto" in changes: + comments.append( + "DNS protocol will be changed to: {0}".format(changes["dns_proto"]) + ) + if dns_proto == "static" and "dns_servers" in changes: + if len(changes["dns_servers"]) == 0: + comments.append("The list of DNS servers will be cleared") else: comments.append( - 'DNS servers will be set to the following: {0}' - .format(', '.join(changes['dns_servers'])) + "DNS servers will be set to the following: {0}".format( + ", ".join(changes["dns_servers"]) + ) ) - if 'ip_proto' in changes: - comments.append('IP protocol will be changed to: {0}' - .format(changes['ip_proto'])) - if ip_proto == 'static': - if 'ip_addrs' in changes: + if "ip_proto" in changes: + comments.append( + "IP protocol will be changed to: {0}".format(changes["ip_proto"]) + ) + if ip_proto == "static": + if "ip_addrs" in changes: comments.append( - 'IP addresses will be set to the following: {0}' - .format(', '.join(changes['ip_addrs'])) + "IP addresses will be set to the following: {0}".format( + ", ".join(changes["ip_addrs"]) + ) ) - if 'gateway' in changes: - if changes['gateway'] is None: - comments.append('Default gateway will be removed') + if "gateway" in changes: + if changes["gateway"] is None: + comments.append("Default gateway will be removed") else: comments.append( - 'Default gateway will be set to {0}' - .format(changes['gateway']) + "Default gateway will be set to {0}".format( + changes["gateway"] + ) ) - ret['result'] = None - ret['comment'] = ('The following changes will be made to ' - 'interface \'{0}\':\n- {1}' - .format(name, '\n- '.join(comments))) + ret["result"] = None + ret["comment"] = ( + "The following changes will be made to " + "interface '{0}':\n- {1}".format(name, "\n- ".join(comments)) + ) return ret - if changes.get('dns_proto') == 'dhcp': - __salt__['ip.set_dhcp_dns'](name) + if changes.get("dns_proto") == "dhcp": + __salt__["ip.set_dhcp_dns"](name) - elif 'dns_servers' in changes: - if len(changes['dns_servers']) == 0: + elif "dns_servers" in changes: + if len(changes["dns_servers"]) == 0: # To clear the list of DNS servers you have to pass []. Later # changes gets passed like *args and a single empty list is # converted to an empty tuple. So, you have to add [] here - changes['dns_servers'] = [[]] + changes["dns_servers"] = [[]] - __salt__['ip.set_static_dns'](name, *changes['dns_servers']) + __salt__["ip.set_static_dns"](name, *changes["dns_servers"]) - if changes.get('ip_proto') == 'dhcp': - __salt__['ip.set_dhcp_ip'](name) - elif changes.get('ip_addrs') or changes.get('gateway') or changes.get('ip_proto') == 'static': - if changes.get('gateway') and not changes.get('ip_addrs'): - changes['ip_addrs'] = ip_addrs - if changes.get('ip_proto') == 'static' and not changes.get('ip_addrs'): - changes['ip_addrs'] = ip_addrs - for idx in range(len(changes['ip_addrs'])): + if changes.get("ip_proto") == "dhcp": + __salt__["ip.set_dhcp_ip"](name) + elif ( + changes.get("ip_addrs") + or changes.get("gateway") + or changes.get("ip_proto") == "static" + ): + if changes.get("gateway") and not changes.get("ip_addrs"): + changes["ip_addrs"] = ip_addrs + if changes.get("ip_proto") == "static" and not changes.get("ip_addrs"): + changes["ip_addrs"] = ip_addrs + for idx in range(len(changes["ip_addrs"])): if idx == 0: - __salt__['ip.set_static_ip']( - name, - changes['ip_addrs'][idx], - gateway=gateway, - append=False + __salt__["ip.set_static_ip"]( + name, changes["ip_addrs"][idx], gateway=gateway, append=False ) else: - __salt__['ip.set_static_ip']( - name, - changes['ip_addrs'][idx], - gateway=None, - append=True + __salt__["ip.set_static_ip"]( + name, changes["ip_addrs"][idx], gateway=None, append=True ) - new = __salt__['ip.get_interface'](name) - ret['changes'] = salt.utils.data.compare_dicts(old, new) + new = __salt__["ip.get_interface"](name) + ret["changes"] = salt.utils.data.compare_dicts(old, new) if _changes(new, dns_proto, dns_servers, ip_proto, ip_addrs, gateway): - ret['result'] = False - ret['comment'] = ('Failed to set desired configuration settings ' - 'for interface \'{0}\''.format(name)) + ret["result"] = False + ret["comment"] = ( + "Failed to set desired configuration settings " + "for interface '{0}'".format(name) + ) else: - ret['comment'] = ('Successfully updated configuration for ' - 'interface \'{0}\''.format(name)) + ret[ + "comment" + ] = "Successfully updated configuration for " "interface '{0}'".format(name) return ret diff --git a/salt/states/win_path.py b/salt/states/win_path.py index 6b17a544c8e..f18f1bd1555 100644 --- a/salt/states/win_path.py +++ b/salt/states/win_path.py @@ -1,31 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Manage the Windows System PATH -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import os +import salt.utils.stringutils + # Import Salt libs from salt.ext import six -import salt.utils.stringutils def __virtual__(): - ''' + """ Load this state if the win_path module exists - ''' - return 'win_path' if 'win_path.rehash' in __salt__ else False + """ + if "win_path.rehash" in __salt__: + return "win_path" + return (False, "win_path module could not be loaded") def _format_comments(ret, comments): - ret['comment'] = ' '.join(comments) + ret["comment"] = " ".join(comments) return ret def absent(name): - ''' + """ Remove the directory from the SYSTEM path Example: @@ -34,35 +37,32 @@ def absent(name): 'C:\\sysinternals': win_path.absent - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - if not __salt__['win_path.exists'](name): - ret['comment'] = '{0} is not in the PATH'.format(name) + if not __salt__["win_path.exists"](name): + ret["comment"] = "{0} is not in the PATH".format(name) return ret - if __opts__['test']: - ret['comment'] = '{0} would be removed from the PATH'.format(name) - ret['result'] = None + if __opts__["test"]: + ret["comment"] = "{0} would be removed from the PATH".format(name) + ret["result"] = None return ret - __salt__['win_path.remove'](name) + __salt__["win_path.remove"](name) - if __salt__['win_path.exists'](name): - ret['comment'] = 'Failed to remove {0} from the PATH'.format(name) - ret['result'] = False + if __salt__["win_path.exists"](name): + ret["comment"] = "Failed to remove {0} from the PATH".format(name) + ret["result"] = False else: - ret['comment'] = 'Removed {0} from the PATH'.format(name) - ret['changes']['removed'] = name + ret["comment"] = "Removed {0} from the PATH".format(name) + ret["changes"]["removed"] = name return ret def exists(name, index=None): - ''' + """ Add the directory to the system PATH at index location index @@ -88,24 +88,21 @@ def exists(name, index=None): 'C:\\mystuff': win_path.exists: - index: -1 - ''' + """ try: name = os.path.normpath(salt.utils.stringutils.to_unicode(name)) except TypeError: name = six.text_type(name) - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} if index is not None and not isinstance(index, six.integer_types): - ret['comment'] = 'Index must be an integer' - ret['result'] = False + ret["comment"] = "Index must be an integer" + ret["result"] = False return ret def _get_path_lowercase(): - return [x.lower() for x in __salt__['win_path.get_path']()] + return [x.lower() for x in __salt__["win_path.get_path"]()] def _index(path=None): if path is None: @@ -124,22 +121,22 @@ def exists(name, index=None): return pos def _changes(old, new): - return {'index': {'old': old, 'new': new}} + return {"index": {"old": old, "new": new}} pre_path = _get_path_lowercase() num_dirs = len(pre_path) if index is not None: if index > num_dirs: - ret.setdefault('warnings', []).append( - 'There are only {0} directories in the PATH, using an index ' - 'of {0} instead of {1}.'.format(num_dirs, index) + ret.setdefault("warnings", []).append( + "There are only {0} directories in the PATH, using an index " + "of {0} instead of {1}.".format(num_dirs, index) ) index = num_dirs elif index <= -num_dirs: - ret.setdefault('warnings', []).append( - 'There are only {0} directories in the PATH, using an index ' - 'of 0 instead of {1}.'.format(num_dirs, index) + ret.setdefault("warnings", []).append( + "There are only {0} directories in the PATH, using an index " + "of 0 instead of {1}.".format(num_dirs, index) ) index = 0 @@ -152,81 +149,71 @@ def exists(name, index=None): if index is None: # We're not enforcing the index, and the directory is in the PATH. # There's nothing to do here. - comments.append('{0} already exists in the PATH.'.format(name)) + comments.append("{0} already exists in the PATH.".format(name)) return _format_comments(ret, comments) else: if index == old_index: comments.append( - '{0} already exists in the PATH at index {1}.'.format( - name, index - ) + "{0} already exists in the PATH at index {1}.".format(name, index) ) return _format_comments(ret, comments) else: - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None comments.append( - '{0} would be moved from index {1} to {2}.'.format( + "{0} would be moved from index {1} to {2}.".format( name, old_index, index ) ) - ret['changes'] = _changes(old_index, index) + ret["changes"] = _changes(old_index, index) return _format_comments(ret, comments) else: # Directory does not exist in PATH - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None comments.append( - '{0} would be added to the PATH{1}.'.format( - name, - ' at index {0}'.format(index) if index is not None else '' + "{0} would be added to the PATH{1}.".format( + name, " at index {0}".format(index) if index is not None else "" ) ) - ret['changes'] = _changes(old_index, index) + ret["changes"] = _changes(old_index, index) return _format_comments(ret, comments) try: - ret['result'] = __salt__['win_path.add'](name, index=index, rehash=False) + ret["result"] = __salt__["win_path.add"](name, index=index, rehash=False) except Exception as exc: # pylint: disable=broad-except - comments.append('Encountered error: {0}.'.format(exc)) - ret['result'] = False + comments.append("Encountered error: {0}.".format(exc)) + ret["result"] = False - if ret['result']: - ret['result'] = __salt__['win_path.rehash']() - if not ret['result']: - comments.append( - 'Updated registry with new PATH, but failed to rehash.' - ) + if ret["result"]: + ret["result"] = __salt__["win_path.rehash"]() + if not ret["result"]: + comments.append("Updated registry with new PATH, but failed to rehash.") new_index = _index() - if ret['result']: + if ret["result"]: # If we have not already determined a False result based on the return # from either win_path.add or win_path.rehash, check the new_index. - ret['result'] = new_index is not None \ - if index is None \ - else index == new_index + ret["result"] = new_index is not None if index is None else index == new_index if index is not None and old_index is not None: comments.append( - '{0} {1} from index {2} to {3}.'.format( - 'Moved' if ret['result'] else 'Failed to move', - name, - old_index, - index + "{0} {1} from index {2} to {3}.".format( + "Moved" if ret["result"] else "Failed to move", name, old_index, index ) ) else: comments.append( - '{0} {1} to the PATH{2}.'.format( - 'Added' if ret['result'] else 'Failed to add', + "{0} {1} to the PATH{2}.".format( + "Added" if ret["result"] else "Failed to add", name, - ' at index {0}'.format(index) if index is not None else '' + " at index {0}".format(index) if index is not None else "", ) ) if old_index != new_index: - ret['changes'] = _changes(old_index, new_index) + ret["changes"] = _changes(old_index, new_index) return _format_comments(ret, comments) diff --git a/salt/states/win_pki.py b/salt/states/win_pki.py index 2cfcbcbdec8..0d51f0d3aa6 100644 --- a/salt/states/win_pki.py +++ b/salt/states/win_pki.py @@ -1,35 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" Microsoft certificate management via the Pki PowerShell module. :platform: Windows .. versionadded:: 2016.11.0 -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function - -_DEFAULT_CONTEXT = 'LocalMachine' -_DEFAULT_FORMAT = 'cer' -_DEFAULT_STORE = 'My' +from __future__ import absolute_import, print_function, unicode_literals # import 3rd party libs from salt.ext import six +_DEFAULT_CONTEXT = "LocalMachine" +_DEFAULT_FORMAT = "cer" +_DEFAULT_STORE = "My" + def __virtual__(): - ''' + """ Load only on minions that have the win_pki module. - ''' - if 'win_pki.get_stores' in __salt__: + """ + if "win_pki.get_stores" in __salt__: return True - return False + return (False, "win_pki module could not be loaded") -def import_cert(name, cert_format=_DEFAULT_FORMAT, context=_DEFAULT_CONTEXT, store=_DEFAULT_STORE, - exportable=True, password='', saltenv='base'): - ''' +def import_cert( + name, + cert_format=_DEFAULT_FORMAT, + context=_DEFAULT_CONTEXT, + store=_DEFAULT_STORE, + exportable=True, + password="", + saltenv="base", +): + """ Import the certificate file into the given certificate store. :param str name: The path of the certificate file to import. @@ -61,48 +68,56 @@ def import_cert(name, cert_format=_DEFAULT_FORMAT, context=_DEFAULT_CONTEXT, sto - exportable: True - password: TestPassword - saltenv: base - ''' - ret = {'name': name, - 'changes': dict(), - 'comment': six.text_type(), - 'result': None} + """ + ret = {"name": name, "changes": dict(), "comment": six.text_type(), "result": None} - store_path = r'Cert:\{0}\{1}'.format(context, store) + store_path = r"Cert:\{0}\{1}".format(context, store) - cached_source_path = __salt__['cp.cache_file'](name, saltenv) - current_certs = __salt__['win_pki.get_certs'](context=context, store=store) + cached_source_path = __salt__["cp.cache_file"](name, saltenv) + current_certs = __salt__["win_pki.get_certs"](context=context, store=store) if password: - cert_props = __salt__['win_pki.get_cert_file'](name=cached_source_path, cert_format=cert_format, password=password) + cert_props = __salt__["win_pki.get_cert_file"]( + name=cached_source_path, cert_format=cert_format, password=password + ) else: - cert_props = __salt__['win_pki.get_cert_file'](name=cached_source_path, cert_format=cert_format) + cert_props = __salt__["win_pki.get_cert_file"]( + name=cached_source_path, cert_format=cert_format + ) - if cert_props['thumbprint'] in current_certs: - ret['comment'] = ("Certificate '{0}' already contained in store:" - ' {1}').format(cert_props['thumbprint'], store_path) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = ("Certificate '{0}' will be imported into store:" - ' {1}').format(cert_props['thumbprint'], store_path) - ret['changes'] = {'old': None, - 'new': cert_props['thumbprint']} + if cert_props["thumbprint"] in current_certs: + ret["comment"] = ( + "Certificate '{0}' already contained in store:" " {1}" + ).format(cert_props["thumbprint"], store_path) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = ( + "Certificate '{0}' will be imported into store:" " {1}" + ).format(cert_props["thumbprint"], store_path) + ret["changes"] = {"old": None, "new": cert_props["thumbprint"]} else: - ret['changes'] = {'old': None, - 'new': cert_props['thumbprint']} - ret['result'] = __salt__['win_pki.import_cert'](name=name, cert_format=cert_format, - context=context, store=store, - exportable=exportable, password=password, - saltenv=saltenv) - if ret['result']: - ret['comment'] = ("Certificate '{0}' imported into store:" - ' {1}').format(cert_props['thumbprint'], store_path) + ret["changes"] = {"old": None, "new": cert_props["thumbprint"]} + ret["result"] = __salt__["win_pki.import_cert"]( + name=name, + cert_format=cert_format, + context=context, + store=store, + exportable=exportable, + password=password, + saltenv=saltenv, + ) + if ret["result"]: + ret["comment"] = ("Certificate '{0}' imported into store:" " {1}").format( + cert_props["thumbprint"], store_path + ) else: - ret['comment'] = ("Certificate '{0}' unable to be imported into store:" - ' {1}').format(cert_props['thumbprint'], store_path) + ret["comment"] = ( + "Certificate '{0}' unable to be imported into store:" " {1}" + ).format(cert_props["thumbprint"], store_path) return ret def remove_cert(name, thumbprint, context=_DEFAULT_CONTEXT, store=_DEFAULT_STORE): - ''' + """ Remove the certificate from the given certificate store. :param str thumbprint: The thumbprint value of the target certificate. @@ -126,32 +141,35 @@ def remove_cert(name, thumbprint, context=_DEFAULT_CONTEXT, store=_DEFAULT_STORE - thumbprint: 9988776655443322111000AAABBBCCCDDDEEEFFF - context: LocalMachine - store: My - ''' - ret = {'name': name, - 'changes': dict(), - 'comment': six.text_type(), - 'result': None} + """ + ret = {"name": name, "changes": dict(), "comment": six.text_type(), "result": None} - store_path = r'Cert:\{0}\{1}'.format(context, store) - current_certs = __salt__['win_pki.get_certs'](context=context, store=store) + store_path = r"Cert:\{0}\{1}".format(context, store) + current_certs = __salt__["win_pki.get_certs"](context=context, store=store) if thumbprint not in current_certs: - ret['comment'] = "Certificate '{0}' already removed from store: {1}".format(thumbprint, - store_path) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = "Certificate '{0}' will be removed from store: {1}".format(thumbprint, - store_path) - ret['changes'] = {'old': thumbprint, - 'new': None} + ret["comment"] = "Certificate '{0}' already removed from store: {1}".format( + thumbprint, store_path + ) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "Certificate '{0}' will be removed from store: {1}".format( + thumbprint, store_path + ) + ret["changes"] = {"old": thumbprint, "new": None} else: - ret['changes'] = {'old': thumbprint, - 'new': None} - ret['result'] = __salt__['win_pki.remove_cert'](thumbprint=thumbprint, context=context, - store=store) - if ret['result']: - ret['comment'] = "Certificate '{0}' removed from store: {1}".format(thumbprint, store_path) + ret["changes"] = {"old": thumbprint, "new": None} + ret["result"] = __salt__["win_pki.remove_cert"]( + thumbprint=thumbprint, context=context, store=store + ) + if ret["result"]: + ret["comment"] = "Certificate '{0}' removed from store: {1}".format( + thumbprint, store_path + ) else: - ret['comment'] = "Certificate '{0}' unable to be removed from store: {1}".format(thumbprint, - store_path) + ret[ + "comment" + ] = "Certificate '{0}' unable to be removed from store: {1}".format( + thumbprint, store_path + ) return ret diff --git a/salt/states/win_powercfg.py b/salt/states/win_powercfg.py index 87541e0acc9..8756aa0dabd 100644 --- a/salt/states/win_powercfg.py +++ b/salt/states/win_powercfg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This module allows you to control the power settings of a windows minion via powercfg. @@ -13,10 +13,11 @@ powercfg. powercfg.set_timeout: - value: 30 - power: dc -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs @@ -25,20 +26,20 @@ import salt.utils.platform log = logging.getLogger(__name__) -__virtualname__ = 'powercfg' +__virtualname__ = "powercfg" def __virtual__(): - ''' + """ Only work on Windows - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'PowerCFG: Module only works on Windows' + return False, "PowerCFG: Module only works on Windows" return __virtualname__ -def set_timeout(name, value, power='ac', scheme=None): - ''' +def set_timeout(name, value, power="ac", scheme=None): + """ Set the sleep timeouts of specific items such as disk, monitor, etc. Args: @@ -85,66 +86,66 @@ def set_timeout(name, value, power='ac', scheme=None): powercfg.set_timeout: - value: 10 - power: ac - ''' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": True, "comment": "", "changes": {}} # Validate name values name = name.lower() - if name not in ['monitor', 'disk', 'standby', 'hibernate']: - ret['result'] = False - ret['comment'] = '"{0}" is not a valid setting'.format(name) - log.debug(ret['comment']) + if name not in ["monitor", "disk", "standby", "hibernate"]: + ret["result"] = False + ret["comment"] = '"{0}" is not a valid setting'.format(name) + log.debug(ret["comment"]) return ret # Validate power values power = power.lower() - if power not in ['ac', 'dc']: - ret['result'] = False - ret['comment'] = '"{0}" is not a power type'.format(power) - log.debug(ret['comment']) + if power not in ["ac", "dc"]: + ret["result"] = False + ret["comment"] = '"{0}" is not a power type'.format(power) + log.debug(ret["comment"]) return ret # Get current settings - old = __salt__['powercfg.get_{0}_timeout'.format(name)](scheme=scheme) + old = __salt__["powercfg.get_{0}_timeout".format(name)](scheme=scheme) # Check current settings if old[power] == value: - ret['comment'] = '{0} timeout on {1} power is already set to {2}' \ - ''.format(name.capitalize(), power.upper(), value) + ret["comment"] = "{0} timeout on {1} power is already set to {2}" "".format( + name.capitalize(), power.upper(), value + ) return ret else: - ret['comment'] = '{0} timeout on {1} power will be set to {2}' \ - ''.format(name.capitalize(), power.upper(), value) + ret["comment"] = "{0} timeout on {1} power will be set to {2}" "".format( + name.capitalize(), power.upper(), value + ) # Check for test=True - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # Set the timeout value - __salt__['powercfg.set_{0}_timeout'.format(name)]( - timeout=value, - power=power, - scheme=scheme) + __salt__["powercfg.set_{0}_timeout".format(name)]( + timeout=value, power=power, scheme=scheme + ) # Get the setting after the change - new = __salt__['powercfg.get_{0}_timeout'.format(name)](scheme=scheme) + new = __salt__["powercfg.get_{0}_timeout".format(name)](scheme=scheme) changes = salt.utils.data.compare_dicts(old, new) if changes: - ret['changes'] = {name: changes} - ret['comment'] = '{0} timeout on {1} power set to {2}' \ - ''.format(name.capitalize(), power.upper(), value) - log.debug(ret['comment']) + ret["changes"] = {name: changes} + ret["comment"] = "{0} timeout on {1} power set to {2}" "".format( + name.capitalize(), power.upper(), value + ) + log.debug(ret["comment"]) else: - ret['changes'] = {} - ret['comment'] = 'Failed to set {0} timeout on {1} power to {2}' \ - ''.format(name, power.upper(), value) - log.debug(ret['comment']) - ret['result'] = False + ret["changes"] = {} + ret["comment"] = "Failed to set {0} timeout on {1} power to {2}" "".format( + name, power.upper(), value + ) + log.debug(ret["comment"]) + ret["result"] = False return ret diff --git a/salt/states/win_servermanager.py b/salt/states/win_servermanager.py index 6c8494ce487..5987cdecf07 100644 --- a/salt/states/win_servermanager.py +++ b/salt/states/win_servermanager.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage Windows features via the ServerManager powershell module. Can install and remove roles/features. @@ -7,28 +7,32 @@ remove roles/features. :platform: Windows Server 2008R2 or greater :depends: win_servermanager.install :depends: win_servermanager.remove -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import salt modules import salt.utils.data def __virtual__(): - ''' + """ Load only if win_servermanager is loaded - ''' - return 'win_servermanager' if 'win_servermanager.install' in __salt__ else False + """ + if "win_servermanager.install" in __salt__: + return "win_servermanager" + return (False, "win_servermanager module could not be loaded") -def installed(name, - features=None, - recurse=False, - restart=False, - source=None, - exclude=None, - **kwargs): - ''' +def installed( + name, + features=None, + recurse=False, + restart=False, + source=None, + exclude=None, + **kwargs +): + """ Install the windows feature. To install a single feature, use the ``name`` parameter. To install multiple features, use the ``features`` parameter. @@ -114,89 +118,84 @@ def installed(name, - SNMP-Service - exclude: - Web-Server - ''' - if 'force' in kwargs: - kwargs.pop('force') + """ + if "force" in kwargs: + kwargs.pop("force") - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} # Check if features is not passed, use name. Split commas if features is None: - features = name.split(',') + features = name.split(",") # Make sure features is a list, split commas if not isinstance(features, list): - features = features.split(',') + features = features.split(",") # Determine if the feature is installed - old = __salt__['win_servermanager.list_installed']() + old = __salt__["win_servermanager.list_installed"]() cur_feat = [] for feature in features: if feature not in old: - ret['changes'][feature] = \ - 'Will be installed recurse={0}'.format(recurse) + ret["changes"][feature] = "Will be installed recurse={0}".format(recurse) elif recurse: - ret['changes'][feature] = \ - 'Already installed but might install sub-features' + ret["changes"][feature] = "Already installed but might install sub-features" else: cur_feat.append(feature) if cur_feat: - cur_feat.insert(0, 'The following features are already installed:') - ret['comment'] = '\n- '.join(cur_feat) + cur_feat.insert(0, "The following features are already installed:") + ret["comment"] = "\n- ".join(cur_feat) - if not ret['changes']: + if not ret["changes"]: return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # Install the features - status = __salt__['win_servermanager.install']( - features, recurse=recurse, restart=restart, source=source, - exclude=exclude) + status = __salt__["win_servermanager.install"]( + features, recurse=recurse, restart=restart, source=source, exclude=exclude + ) - ret['result'] = status['Success'] + ret["result"] = status["Success"] # Show items failed to install fail_feat = [] new_feat = [] rem_feat = [] - for feature in status['Features']: + for feature in status["Features"]: # Features that failed to install or be removed - if not status['Features'][feature].get('Success', True): - fail_feat.append('- {0}'.format(feature)) + if not status["Features"][feature].get("Success", True): + fail_feat.append("- {0}".format(feature)) # Features that installed - elif '(exclude)' not in status['Features'][feature]['Message']: - new_feat.append('- {0}'.format(feature)) + elif "(exclude)" not in status["Features"][feature]["Message"]: + new_feat.append("- {0}".format(feature)) # Show items that were removed because they were part of `exclude` - elif '(exclude)' in status['Features'][feature]['Message']: - rem_feat.append('- {0}'.format(feature)) + elif "(exclude)" in status["Features"][feature]["Message"]: + rem_feat.append("- {0}".format(feature)) if fail_feat: - fail_feat.insert(0, 'Failed to install the following:') + fail_feat.insert(0, "Failed to install the following:") if new_feat: - new_feat.insert(0, 'Installed the following:') + new_feat.insert(0, "Installed the following:") if rem_feat: - rem_feat.insert(0, 'Removed the following (exclude):') + rem_feat.insert(0, "Removed the following (exclude):") - ret['comment'] = '\n'.join(fail_feat + new_feat + rem_feat) + ret["comment"] = "\n".join(fail_feat + new_feat + rem_feat) # Get the changes - new = __salt__['win_servermanager.list_installed']() - ret['changes'] = salt.utils.data.compare_dicts(old, new) + new = __salt__["win_servermanager.list_installed"]() + ret["changes"] = salt.utils.data.compare_dicts(old, new) return ret def removed(name, features=None, remove_payload=False, restart=False): - ''' + """ Remove the windows feature To remove a single feature, use the ``name`` parameter. To remove multiple features, use the ``features`` parameter. @@ -257,70 +256,68 @@ def removed(name, features=None, remove_payload=False, restart=False): - XPX-Viewer - SNMP-Service - restart: True - ''' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + """ + ret = {"name": name, "result": True, "changes": {}, "comment": ""} # Check if features is not passed, use name. Split commas if features is None: - features = name.split(',') + features = name.split(",") # Make sure features is a list, split commas if not isinstance(features, list): - features = features.split(',') + features = features.split(",") # Determine if the feature is installed - old = __salt__['win_servermanager.list_installed']() + old = __salt__["win_servermanager.list_installed"]() rem_feat = [] for feature in features: if feature in old: - ret['changes'][feature] = 'Will be removed' + ret["changes"][feature] = "Will be removed" else: rem_feat.append(feature) if rem_feat: - rem_feat.insert(0, 'The following features are not installed:') - ret['comment'] = '\n- '.join(rem_feat) + rem_feat.insert(0, "The following features are not installed:") + ret["comment"] = "\n- ".join(rem_feat) - if not ret['changes']: + if not ret["changes"]: return ret - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret # Remove the features - status = __salt__['win_servermanager.remove']( - features, remove_payload=remove_payload, restart=restart) + status = __salt__["win_servermanager.remove"]( + features, remove_payload=remove_payload, restart=restart + ) - ret['result'] = status['Success'] + ret["result"] = status["Success"] # Some items failed to uninstall fail_feat = [] rem_feat = [] - for feature in status['Features']: + for feature in status["Features"]: # Use get because sometimes 'Success' isn't defined such as when the # feature is already uninstalled - if not status['Features'][feature].get('Success', True): + if not status["Features"][feature].get("Success", True): # Show items that failed to uninstall - fail_feat.append('- {0}'.format(feature)) + fail_feat.append("- {0}".format(feature)) else: # Show items that uninstalled - rem_feat.append('- {0}'.format(feature)) + rem_feat.append("- {0}".format(feature)) if fail_feat: - fail_feat.insert(0, 'Failed to remove the following:') + fail_feat.insert(0, "Failed to remove the following:") if rem_feat: - rem_feat.insert(0, 'Removed the following:') + rem_feat.insert(0, "Removed the following:") - ret['comment'] = '\n'.join(fail_feat + rem_feat) + ret["comment"] = "\n".join(fail_feat + rem_feat) # Get the changes - new = __salt__['win_servermanager.list_installed']() - ret['changes'] = salt.utils.data.compare_dicts(old, new) + new = __salt__["win_servermanager.list_installed"]() + ret["changes"] = salt.utils.data.compare_dicts(old, new) return ret diff --git a/salt/states/win_smtp_server.py b/salt/states/win_smtp_server.py index 29edb382163..d218cc9d937 100644 --- a/salt/states/win_smtp_server.py +++ b/salt/states/win_smtp_server.py @@ -1,33 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing IIS SMTP server configuration on Windows servers. -''' +""" -from __future__ import absolute_import, unicode_literals, print_function - -# Import 3rd-party libs -from salt.ext import six +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.utils.args -_DEFAULT_SERVER = 'SmtpSvc/1' +# Import 3rd-party libs +from salt.ext import six + +_DEFAULT_SERVER = "SmtpSvc/1" def __virtual__(): - ''' + """ Load only on minions that have the win_smtp_server module. - ''' - if 'win_smtp_server.get_server_setting' in __salt__: + """ + if "win_smtp_server.get_server_setting" in __salt__: return True - return False + return (False, "win_smtp_server module could not be loaded") def _merge_dicts(*args): - ''' + """ Shallow copy and merge dicts together, giving precedence to last in. - ''' + """ ret = dict() for arg in args: ret.update(arg) @@ -35,9 +35,9 @@ def _merge_dicts(*args): def _normalize_server_settings(**settings): - ''' + """ Convert setting values that has been improperly converted to a dict back to a string. - ''' + """ ret = dict() settings = salt.utils.args.clean_kwargs(**settings) @@ -52,7 +52,7 @@ def _normalize_server_settings(**settings): def server_setting(name, settings=None, server=_DEFAULT_SERVER): - ''' + """ Ensure the value is set for the specified setting. .. note:: @@ -74,62 +74,65 @@ def server_setting(name, settings=None, server=_DEFAULT_SERVER): MaxMessageSize: 16777216 MaxRecipients: 10000 MaxSessionSize: 16777216 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': six.text_type(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": six.text_type(), "result": None} if not settings: - ret['comment'] = 'No settings to change provided.' - ret['result'] = True + ret["comment"] = "No settings to change provided." + ret["result"] = True return ret ret_settings = dict() - ret_settings['changes'] = {} - ret_settings['failures'] = {} + ret_settings["changes"] = {} + ret_settings["failures"] = {} - current_settings = __salt__['win_smtp_server.get_server_setting'](settings=settings.keys(), - server=server) + current_settings = __salt__["win_smtp_server.get_server_setting"]( + settings=settings.keys(), server=server + ) for key in settings: # Some fields are formatted like '{data}'. Salt/Python converts these to dicts # automatically on input, so convert them back to the proper format. settings = _normalize_server_settings(**settings) if six.text_type(settings[key]) != six.text_type(current_settings[key]): - ret_settings['changes'][key] = {'old': current_settings[key], - 'new': settings[key]} - if not ret_settings['changes']: - ret['comment'] = 'Settings already contain the provided values.' - ret['result'] = True + ret_settings["changes"][key] = { + "old": current_settings[key], + "new": settings[key], + } + if not ret_settings["changes"]: + ret["comment"] = "Settings already contain the provided values." + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Settings will be changed.' - ret['changes'] = ret_settings + elif __opts__["test"]: + ret["comment"] = "Settings will be changed." + ret["changes"] = ret_settings return ret - __salt__['win_smtp_server.set_server_setting'](settings=settings, server=server) - new_settings = __salt__['win_smtp_server.get_server_setting'](settings=settings.keys(), - server=server) + __salt__["win_smtp_server.set_server_setting"](settings=settings, server=server) + new_settings = __salt__["win_smtp_server.get_server_setting"]( + settings=settings.keys(), server=server + ) for key in settings: if six.text_type(new_settings[key]) != six.text_type(settings[key]): - ret_settings['failures'][key] = {'old': current_settings[key], - 'new': new_settings[key]} - ret_settings['changes'].pop(key, None) + ret_settings["failures"][key] = { + "old": current_settings[key], + "new": new_settings[key], + } + ret_settings["changes"].pop(key, None) - if ret_settings['failures']: - ret['comment'] = 'Some settings failed to change.' - ret['changes'] = ret_settings - ret['result'] = False + if ret_settings["failures"]: + ret["comment"] = "Some settings failed to change." + ret["changes"] = ret_settings + ret["result"] = False else: - ret['comment'] = 'Set settings to contain the provided values.' - ret['changes'] = ret_settings['changes'] - ret['result'] = True + ret["comment"] = "Set settings to contain the provided values." + ret["changes"] = ret_settings["changes"] + ret["result"] = True return ret def active_log_format(name, log_format, server=_DEFAULT_SERVER): - ''' + """ Manage the active log format for the SMTP server. :param str log_format: The log format name. @@ -142,30 +145,31 @@ def active_log_format(name, log_format, server=_DEFAULT_SERVER): smtp-log-format: win_smtp_server.active_log_format: - log_format: Microsoft IIS Log File Format - ''' - ret = {'name': name, - 'changes': {}, - 'comment': six.text_type(), - 'result': None} - current_log_format = __salt__['win_smtp_server.get_log_format'](server) + """ + ret = {"name": name, "changes": {}, "comment": six.text_type(), "result": None} + current_log_format = __salt__["win_smtp_server.get_log_format"](server) if log_format == current_log_format: - ret['comment'] = 'LogPluginClsid already contains the id of the provided log format.' - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'LogPluginClsid will be changed.' - ret['changes'] = {'old': current_log_format, - 'new': log_format} + ret[ + "comment" + ] = "LogPluginClsid already contains the id of the provided log format." + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "LogPluginClsid will be changed." + ret["changes"] = {"old": current_log_format, "new": log_format} else: - ret['comment'] = 'Set LogPluginClsid to contain the id of the provided log format.' - ret['changes'] = {'old': current_log_format, - 'new': log_format} - ret['result'] = __salt__['win_smtp_server.set_log_format'](log_format, server) + ret[ + "comment" + ] = "Set LogPluginClsid to contain the id of the provided log format." + ret["changes"] = {"old": current_log_format, "new": log_format} + ret["result"] = __salt__["win_smtp_server.set_log_format"](log_format, server) return ret -def connection_ip_list(name, addresses=None, grant_by_default=False, server=_DEFAULT_SERVER): - ''' +def connection_ip_list( + name, addresses=None, grant_by_default=False, server=_DEFAULT_SERVER +): + """ Manage IP list for SMTP connections. :param str addresses: A dictionary of IP + subnet pairs. @@ -203,35 +207,32 @@ def connection_ip_list(name, addresses=None, grant_by_default=False, server=_DEF win_smtp_server.connection_ip_list: - addresses: {} - grant_by_default: True - ''' - ret = {'name': name, - 'changes': {}, - 'comment': six.text_type(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": six.text_type(), "result": None} if not addresses: addresses = dict() - current_addresses = __salt__['win_smtp_server.get_connection_ip_list'](server=server) + current_addresses = __salt__["win_smtp_server.get_connection_ip_list"]( + server=server + ) if addresses == current_addresses: - ret['comment'] = 'IPGrant already contains the provided addresses.' - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'IPGrant will be changed.' - ret['changes'] = {'old': current_addresses, - 'new': addresses} + ret["comment"] = "IPGrant already contains the provided addresses." + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "IPGrant will be changed." + ret["changes"] = {"old": current_addresses, "new": addresses} else: - ret['comment'] = 'Set IPGrant to contain the provided addresses.' - ret['changes'] = {'old': current_addresses, - 'new': addresses} - ret['result'] = __salt__['win_smtp_server.set_connection_ip_list'](addresses=addresses, - grant_by_default=grant_by_default, - server=server) + ret["comment"] = "Set IPGrant to contain the provided addresses." + ret["changes"] = {"old": current_addresses, "new": addresses} + ret["result"] = __salt__["win_smtp_server.set_connection_ip_list"]( + addresses=addresses, grant_by_default=grant_by_default, server=server + ) return ret def relay_ip_list(name, addresses=None, server=_DEFAULT_SERVER): - ''' + """ Manage IP list for SMTP relay connections. Due to the unusual way that Windows stores the relay IPs, it is advisable to retrieve @@ -299,30 +300,27 @@ def relay_ip_list(name, addresses=None, server=_DEFAULT_SERVER): smtp-relay-list: win_smtp_server.relay_ip_list: - addresses: [] - ''' - ret = {'name': name, - 'changes': {}, - 'comment': six.text_type(), - 'result': None} - current_addresses = __salt__['win_smtp_server.get_relay_ip_list'](server=server) + """ + ret = {"name": name, "changes": {}, "comment": six.text_type(), "result": None} + current_addresses = __salt__["win_smtp_server.get_relay_ip_list"](server=server) # Fix if we were passed None as a string. if addresses: - if addresses[0] == 'None': + if addresses[0] == "None": addresses[0] = None elif addresses is None: addresses = [None] if addresses == current_addresses: - ret['comment'] = 'RelayIpList already contains the provided addresses.' - ret['result'] = True - elif __opts__['test']: - ret['comment'] = 'RelayIpList will be changed.' - ret['changes'] = {'old': current_addresses, - 'new': addresses} + ret["comment"] = "RelayIpList already contains the provided addresses." + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "RelayIpList will be changed." + ret["changes"] = {"old": current_addresses, "new": addresses} else: - ret['comment'] = 'Set RelayIpList to contain the provided addresses.' - ret['changes'] = {'old': current_addresses, - 'new': addresses} - ret['result'] = __salt__['win_smtp_server.set_relay_ip_list'](addresses=addresses, server=server) + ret["comment"] = "Set RelayIpList to contain the provided addresses." + ret["changes"] = {"old": current_addresses, "new": addresses} + ret["result"] = __salt__["win_smtp_server.set_relay_ip_list"]( + addresses=addresses, server=server + ) return ret diff --git a/salt/states/win_snmp.py b/salt/states/win_snmp.py index cac7439c043..f6930ef5df3 100644 --- a/salt/states/win_snmp.py +++ b/salt/states/win_snmp.py @@ -1,28 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" Module for managing SNMP service settings on Windows servers. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function - +from __future__ import absolute_import, print_function, unicode_literals # Import 3rd party libs from salt.ext import six def __virtual__(): - ''' + """ Load only on minions that have the win_snmp module. - ''' - if 'win_snmp.get_agent_settings' in __salt__: + """ + if "win_snmp.get_agent_settings" in __salt__: return True - return False + return (False, "win_snmp module could not be loaded") def agent_settings(name, contact, location, services=None): - ''' + """ Manage the SNMP sysContact, sysLocation, and sysServices settings. :param str contact: The SNMP contact. @@ -40,60 +39,60 @@ def agent_settings(name, contact, location, services=None): - services: - Physical - Internet - ''' - ret = {'name': name, - 'changes': {}, - 'comment': six.text_type(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": six.text_type(), "result": None} - ret_settings = {'changes': dict(), - 'failures': dict()} + ret_settings = {"changes": dict(), "failures": dict()} if not services: - services = ['None'] + services = ["None"] # Filter services for unique items, and sort them for comparison purposes. services = sorted(set(services)) - settings = {'contact': contact, 'location': location, 'services': services} + settings = {"contact": contact, "location": location, "services": services} - current_settings = __salt__['win_snmp.get_agent_settings']() + current_settings = __salt__["win_snmp.get_agent_settings"]() for setting in settings: if six.text_type(settings[setting]) != six.text_type(current_settings[setting]): - ret_settings['changes'][setting] = {'old': current_settings[setting], - 'new': settings[setting]} - if not ret_settings['changes']: - ret['comment'] = 'Agent settings already contain the provided values.' - ret['result'] = True + ret_settings["changes"][setting] = { + "old": current_settings[setting], + "new": settings[setting], + } + if not ret_settings["changes"]: + ret["comment"] = "Agent settings already contain the provided values." + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Agent settings will be changed.' - ret['changes'] = ret_settings + elif __opts__["test"]: + ret["comment"] = "Agent settings will be changed." + ret["changes"] = ret_settings return ret - __salt__['win_snmp.set_agent_settings'](**settings) - new_settings = __salt__['win_snmp.get_agent_settings']() + __salt__["win_snmp.set_agent_settings"](**settings) + new_settings = __salt__["win_snmp.get_agent_settings"]() for setting in settings: if settings[setting] != new_settings[setting]: - ret_settings['failures'][setting] = {'old': current_settings[setting], - 'new': new_settings[setting]} - ret_settings['changes'].pop(setting, None) + ret_settings["failures"][setting] = { + "old": current_settings[setting], + "new": new_settings[setting], + } + ret_settings["changes"].pop(setting, None) - if ret_settings['failures']: - ret['comment'] = 'Some agent settings failed to change.' - ret['changes'] = ret_settings - ret['result'] = False + if ret_settings["failures"]: + ret["comment"] = "Some agent settings failed to change." + ret["changes"] = ret_settings + ret["result"] = False else: - ret['comment'] = 'Set agent settings to contain the provided values.' - ret['changes'] = ret_settings['changes'] - ret['result'] = True + ret["comment"] = "Set agent settings to contain the provided values." + ret["changes"] = ret_settings["changes"] + ret["result"] = True return ret def auth_traps_enabled(name, status=True): - ''' + """ Manage the sending of authentication traps. :param bool status: The enabled status. @@ -105,33 +104,28 @@ def auth_traps_enabled(name, status=True): snmp-auth-traps: win_snmp.auth_traps_enabled: - status: True - ''' - ret = {'name': name, - 'changes': {}, - 'comment': six.text_type(), - 'result': None} + """ + ret = {"name": name, "changes": {}, "comment": six.text_type(), "result": None} - vname = 'EnableAuthenticationTraps' - current_status = __salt__['win_snmp.get_auth_traps_enabled']() + vname = "EnableAuthenticationTraps" + current_status = __salt__["win_snmp.get_auth_traps_enabled"]() if status == current_status: - ret['comment'] = '{0} already contains the provided value.'.format(vname) - ret['result'] = True - elif __opts__['test']: - ret['comment'] = '{0} will be changed.'.format(vname) - ret['changes'] = {'old': current_status, - 'new': status} + ret["comment"] = "{0} already contains the provided value.".format(vname) + ret["result"] = True + elif __opts__["test"]: + ret["comment"] = "{0} will be changed.".format(vname) + ret["changes"] = {"old": current_status, "new": status} else: - ret['comment'] = 'Set {0} to contain the provided value.'.format(vname) - ret['changes'] = {'old': current_status, - 'new': status} - ret['result'] = __salt__['win_snmp.set_auth_traps_enabled'](status=status) + ret["comment"] = "Set {0} to contain the provided value.".format(vname) + ret["changes"] = {"old": current_status, "new": status} + ret["result"] = __salt__["win_snmp.set_auth_traps_enabled"](status=status) return ret def community_names(name, communities=None): - ''' + """ Manage the SNMP accepted community names and their permissions. :param str communities: A dictionary of SNMP communities and permissions. @@ -145,25 +139,23 @@ def community_names(name, communities=None): - communities: TestCommunity: Read Only OtherCommunity: Read Write - ''' - ret = {'name': name, - 'changes': dict(), - 'comment': six.text_type(), - 'result': None} + """ + ret = {"name": name, "changes": dict(), "comment": six.text_type(), "result": None} - ret_communities = {'changes': dict(), - 'failures': dict()} + ret_communities = {"changes": dict(), "failures": dict()} if not communities: communities = dict() - current_communities = __salt__['win_snmp.get_community_names']() + current_communities = __salt__["win_snmp.get_community_names"]() # Note any existing communities that should be removed. for current_vname in current_communities: if current_vname not in communities: - ret_communities['changes'][current_vname] = {'old': current_communities[current_vname], - 'new': None} + ret_communities["changes"][current_vname] = { + "old": current_communities[current_vname], + "new": None, + } # Note any new communities or existing communities that should be changed. for vname in communities: @@ -171,27 +163,31 @@ def community_names(name, communities=None): if vname in current_communities: current_vdata = current_communities[vname] if communities[vname] != current_vdata: - ret_communities['changes'][vname] = {'old': current_vdata, - 'new': communities[vname]} + ret_communities["changes"][vname] = { + "old": current_vdata, + "new": communities[vname], + } - if not ret_communities['changes']: - ret['comment'] = 'Communities already contain the provided values.' - ret['result'] = True + if not ret_communities["changes"]: + ret["comment"] = "Communities already contain the provided values." + ret["result"] = True return ret - elif __opts__['test']: - ret['comment'] = 'Communities will be changed.' - ret['changes'] = ret_communities + elif __opts__["test"]: + ret["comment"] = "Communities will be changed." + ret["changes"] = ret_communities return ret - __salt__['win_snmp.set_community_names'](communities=communities) - new_communities = __salt__['win_snmp.get_community_names']() + __salt__["win_snmp.set_community_names"](communities=communities) + new_communities = __salt__["win_snmp.get_community_names"]() # Verify that any communities that needed to be removed were removed. for new_vname in new_communities: if new_vname not in communities: - ret_communities['failures'][new_vname] = {'old': current_communities[new_vname], - 'new': new_communities[new_vname]} - ret_communities['changes'].pop(new_vname, None) + ret_communities["failures"][new_vname] = { + "old": current_communities[new_vname], + "new": new_communities[new_vname], + } + ret_communities["changes"].pop(new_vname, None) # Verify that any new communities or existing communities that # needed to be changed were changed. @@ -200,16 +196,18 @@ def community_names(name, communities=None): if vname in new_communities: new_vdata = new_communities[vname] if communities[vname] != new_vdata: - ret_communities['failures'][vname] = {'old': current_communities[vname], - 'new': new_vdata} - ret_communities['changes'].pop(vname, None) + ret_communities["failures"][vname] = { + "old": current_communities[vname], + "new": new_vdata, + } + ret_communities["changes"].pop(vname, None) - if ret_communities['failures']: - ret['comment'] = 'Some communities failed to change.' - ret['changes'] = ret_communities - ret['result'] = False + if ret_communities["failures"]: + ret["comment"] = "Some communities failed to change." + ret["changes"] = ret_communities + ret["result"] = False else: - ret['comment'] = 'Set communities to contain the provided values.' - ret['changes'] = ret_communities['changes'] - ret['result'] = True + ret["comment"] = "Set communities to contain the provided values." + ret["changes"] = ret_communities["changes"] + ret["result"] = True return ret diff --git a/salt/states/win_system.py b/salt/states/win_system.py index 45d87ad8cd3..fe9af2b9ca0 100644 --- a/salt/states/win_system.py +++ b/salt/states/win_system.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Windows system information ======================================== @@ -15,8 +15,8 @@ description. This is Erik's computer, don't touch!: system.computer_desc: [] -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -28,152 +28,159 @@ import salt.utils.platform # Import 3rd party libs from salt.ext import six - log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'system' +__virtualname__ = "system" def __virtual__(): - ''' + """ This only supports Windows - ''' - if salt.utils.platform.is_windows() and 'system.get_computer_desc' in __salt__: + """ + if salt.utils.platform.is_windows() and "system.get_computer_desc" in __salt__: return __virtualname__ - return False + return (False, "system module could not be loaded") def computer_desc(name): - ''' + """ Manage the computer's description field name The desired computer description - ''' + """ # Just in case someone decides to enter a numeric description name = six.text_type(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Computer description already set to \'{0}\''.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Computer description already set to '{0}'".format(name), + } - before_desc = __salt__['system.get_computer_desc']() + before_desc = __salt__["system.get_computer_desc"]() if before_desc == name: return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = ('Computer description will be changed to \'{0}\'' - .format(name)) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Computer description will be changed to '{0}'".format(name) return ret - result = __salt__['system.set_computer_desc'](name) - if result['Computer Description'] == name: - ret['comment'] = ('Computer description successfully changed to \'{0}\'' - .format(name)) - ret['changes'] = {'old': before_desc, 'new': name} + result = __salt__["system.set_computer_desc"](name) + if result["Computer Description"] == name: + ret["comment"] = "Computer description successfully changed to '{0}'".format( + name + ) + ret["changes"] = {"old": before_desc, "new": name} else: - ret['result'] = False - ret['comment'] = ('Unable to set computer description to ' - '\'{0}\''.format(name)) + ret["result"] = False + ret["comment"] = "Unable to set computer description to " "'{0}'".format(name) return ret -computer_description = salt.utils.functools.alias_function(computer_desc, 'computer_description') +computer_description = salt.utils.functools.alias_function( + computer_desc, "computer_description" +) def computer_name(name): - ''' + """ Manage the computer's name name The desired computer name - ''' + """ # Just in case someone decides to enter a numeric description name = six.text_type(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Computer name already set to \'{0}\''.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Computer name already set to '{0}'".format(name), + } - before_name = __salt__['system.get_computer_name']() - pending_name = __salt__['system.get_pending_computer_name']() + before_name = __salt__["system.get_computer_name"]() + pending_name = __salt__["system.get_pending_computer_name"]() if before_name == name and pending_name is None: return ret elif pending_name == name.upper(): - ret['comment'] = ('The current computer name is \'{0}\', but will be ' - 'changed to \'{1}\' on the next reboot' - .format(before_name, name)) + ret["comment"] = ( + "The current computer name is '{0}', but will be " + "changed to '{1}' on the next reboot".format(before_name, name) + ) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Computer name will be changed to \'{0}\''.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Computer name will be changed to '{0}'".format(name) return ret - result = __salt__['system.set_computer_name'](name) + result = __salt__["system.set_computer_name"](name) if result is not False: - after_name = result['Computer Name']['Current'] - after_pending = result['Computer Name'].get('Pending') - if ((after_pending is not None and after_pending == name) or - (after_pending is None and after_name == name)): - ret['comment'] = 'Computer name successfully set to \'{0}\''.format(name) + after_name = result["Computer Name"]["Current"] + after_pending = result["Computer Name"].get("Pending") + if (after_pending is not None and after_pending == name) or ( + after_pending is None and after_name == name + ): + ret["comment"] = "Computer name successfully set to '{0}'".format(name) if after_pending is not None: - ret['comment'] += ' (reboot required for change to take effect)' - ret['changes'] = {'old': before_name, 'new': name} + ret["comment"] += " (reboot required for change to take effect)" + ret["changes"] = {"old": before_name, "new": name} else: - ret['result'] = False - ret['comment'] = 'Unable to set computer name to \'{0}\''.format(name) + ret["result"] = False + ret["comment"] = "Unable to set computer name to '{0}'".format(name) return ret def hostname(name): - ''' + """ .. versionadded:: 2016.3.0 Manage the hostname of the computer name The hostname to set - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - current_hostname = __salt__['system.get_hostname']() + current_hostname = __salt__["system.get_hostname"]() if current_hostname.upper() == name.upper(): - ret['comment'] = "Hostname is already set to '{0}'".format(name) + ret["comment"] = "Hostname is already set to '{0}'".format(name) return ret - out = __salt__['system.set_hostname'](name) + out = __salt__["system.set_hostname"](name) if out: - ret['comment'] = "The current hostname is '{0}', " \ - "but will be changed to '{1}' on the next reboot".format(current_hostname, name) - ret['changes'] = {'hostname': name} + ret["comment"] = ( + "The current hostname is '{0}', " + "but will be changed to '{1}' on the next reboot".format( + current_hostname, name + ) + ) + ret["changes"] = {"hostname": name} else: - ret['result'] = False - ret['comment'] = 'Unable to set hostname' + ret["result"] = False + ret["comment"] = "Unable to set hostname" return ret -def join_domain(name, - username=None, - password=None, - account_ou=None, - account_exists=False, - restart=False): - ''' +def join_domain( + name, + username=None, + password=None, + account_ou=None, + account_exists=False, + restart=False, +): + """ Checks if a computer is joined to the Domain. If the computer is not in the Domain, it will be joined. @@ -213,53 +220,62 @@ def join_domain(name, - username: myaccount@mydomain.local.com - password: mysecretpassword - restart: True - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Computer already added to \'{0}\''.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Computer already added to '{0}'".format(name), + } - current_domain_dic = __salt__['system.get_domain_workgroup']() - if 'Domain' in current_domain_dic: - current_domain = current_domain_dic['Domain'] - elif 'Workgroup' in current_domain_dic: - current_domain = 'Workgroup' + current_domain_dic = __salt__["system.get_domain_workgroup"]() + if "Domain" in current_domain_dic: + current_domain = current_domain_dic["Domain"] + elif "Workgroup" in current_domain_dic: + current_domain = "Workgroup" else: current_domain = None if name.lower() == current_domain.lower(): - ret['comment'] = 'Computer already added to \'{0}\''.format(name) + ret["comment"] = "Computer already added to '{0}'".format(name) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Computer will be added to \'{0}\''.format(name) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Computer will be added to '{0}'".format(name) return ret - result = __salt__['system.join_domain'](domain=name, - username=username, - password=password, - account_ou=account_ou, - account_exists=account_exists, - restart=restart) + result = __salt__["system.join_domain"]( + domain=name, + username=username, + password=password, + account_ou=account_ou, + account_exists=account_exists, + restart=restart, + ) if result is not False: - ret['comment'] = 'Computer added to \'{0}\''.format(name) + ret["comment"] = "Computer added to '{0}'".format(name) if restart: - ret['comment'] += '\nSystem will restart' + ret["comment"] += "\nSystem will restart" else: - ret['comment'] += '\nSystem needs to be restarted' - ret['changes'] = {'old': current_domain, - 'new': name} + ret["comment"] += "\nSystem needs to be restarted" + ret["changes"] = {"old": current_domain, "new": name} else: - ret['comment'] = 'Computer failed to join \'{0}\''.format(name) - ret['result'] = False + ret["comment"] = "Computer failed to join '{0}'".format(name) + ret["result"] = False return ret -def reboot(name, message=None, timeout=5, force_close=True, in_seconds=False, - only_on_pending_reboot=True): - ''' +def reboot( + name, + message=None, + timeout=5, + force_close=True, + in_seconds=False, + only_on_pending_reboot=True, +): + """ Reboot the computer :param str message: @@ -295,17 +311,29 @@ def reboot(name, message=None, timeout=5, force_close=True, in_seconds=False, pending reboot. If this is False, the reboot will always occur. The default value is True. - ''' + """ - return shutdown(name, message=message, timeout=timeout, - force_close=force_close, reboot=True, - in_seconds=in_seconds, - only_on_pending_reboot=only_on_pending_reboot) + return shutdown( + name, + message=message, + timeout=timeout, + force_close=force_close, + reboot=True, + in_seconds=in_seconds, + only_on_pending_reboot=only_on_pending_reboot, + ) -def shutdown(name, message=None, timeout=5, force_close=True, reboot=False, - in_seconds=False, only_on_pending_reboot=False): - ''' +def shutdown( + name, + message=None, + timeout=5, + force_close=True, + reboot=False, + in_seconds=False, + only_on_pending_reboot=False, +): + """ Shutdown the computer :param str message: @@ -349,43 +377,46 @@ def shutdown(name, message=None, timeout=5, force_close=True, reboot=False, pending reboot. If this is False, the shutdown will always occur. The default value is False. - ''' + """ - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} if reboot: - action = 'reboot' + action = "reboot" else: - action = 'shutdown' + action = "shutdown" - if only_on_pending_reboot and not __salt__['system.get_pending_reboot'](): - if __opts__['test']: - ret['comment'] = ('System {0} will be skipped because ' - 'no reboot is pending').format(action) + if only_on_pending_reboot and not __salt__["system.get_pending_reboot"](): + if __opts__["test"]: + ret["comment"] = ( + "System {0} will be skipped because " "no reboot is pending" + ).format(action) else: - ret['comment'] = ('System {0} has been skipped because ' - 'no reboot was pending').format(action) + ret["comment"] = ( + "System {0} has been skipped because " "no reboot was pending" + ).format(action) return ret - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Will attempt to schedule a {0}'.format(action) + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Will attempt to schedule a {0}".format(action) return ret - ret['result'] = __salt__['system.shutdown'](message=message, - timeout=timeout, - force_close=force_close, - reboot=reboot, - in_seconds=in_seconds, - only_on_pending_reboot=False) + ret["result"] = __salt__["system.shutdown"]( + message=message, + timeout=timeout, + force_close=force_close, + reboot=reboot, + in_seconds=in_seconds, + only_on_pending_reboot=False, + ) - if ret['result']: - ret['changes'] = {'old': 'No reboot or shutdown was scheduled', - 'new': 'A {0} has been scheduled'.format(action)} - ret['comment'] = 'Request to {0} was successful'.format(action) + if ret["result"]: + ret["changes"] = { + "old": "No reboot or shutdown was scheduled", + "new": "A {0} has been scheduled".format(action), + } + ret["comment"] = "Request to {0} was successful".format(action) else: - ret['comment'] = 'Request to {0} failed'.format(action) + ret["comment"] = "Request to {0} failed".format(action) return ret diff --git a/salt/states/win_wua.py b/salt/states/win_wua.py index 5812fb3f96f..9cec6ce6388 100644 --- a/salt/states/win_wua.py +++ b/salt/states/win_wua.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Installation of Windows Updates using the Windows Update Agent .. versionadded:: 2017.7.0 @@ -48,37 +48,39 @@ For removal: - updates: - KB3194343 - bb1dbb26-3fb6-45fd-bb05-e3c8e379195c -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt libs -from salt.ext import six import salt.utils.data import salt.utils.platform import salt.utils.win_update +# Import Salt libs +from salt.ext import six + log = logging.getLogger(__name__) -__virtualname__ = 'wua' +__virtualname__ = "wua" def __virtual__(): - ''' + """ Only valid on Windows machines - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'WUA: Only available on Window systems' + return False, "WUA: Only available on Window systems" if not salt.utils.win_update.HAS_PYWIN32: - return False, 'WUA: Requires PyWin32 libraries' + return False, "WUA: Requires PyWin32 libraries" return __virtualname__ def installed(name, updates=None): - ''' + """ Ensure Microsoft Updates are installed. Updates will be downloaded if needed. @@ -126,17 +128,14 @@ def installed(name, updates=None): - updates: - KB3194343 - 28cf1b09-2b1a-458c-9bd1-971d1b26b211 - ''' + """ if isinstance(updates, six.string_types): updates = [updates] if not updates: updates = name - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} wua = salt.utils.win_update.WindowsUpdateAgent() @@ -145,7 +144,7 @@ def installed(name, updates=None): # No updates found if install_list.count() == 0: - ret['comment'] = 'No updates found' + ret["comment"] = "No updates found" return ret # List of updates to download @@ -161,21 +160,20 @@ def installed(name, updates=None): if not salt.utils.data.is_true(item.IsInstalled): install.updates.Add(item) else: - installed_updates.extend('KB' + kb for kb in item.KBArticleIDs) + installed_updates.extend("KB" + kb for kb in item.KBArticleIDs) if install.count() == 0: - ret['comment'] = 'Updates already installed: ' - ret['comment'] += '\n - '.join(installed_updates) + ret["comment"] = "Updates already installed: " + ret["comment"] += "\n - ".join(installed_updates) return ret # Return comment of changes if test. - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Updates will be installed:' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Updates will be installed:" for update in install.updates: - ret['comment'] += '\n' - ret['comment'] += ': '.join( - [update.Identity.UpdateID, update.Title]) + ret["comment"] += "\n" + ret["comment"] += ": ".join([update.Identity.UpdateID, update.Title]) return ret # Download updates @@ -190,29 +188,33 @@ def installed(name, updates=None): # Verify the installation for item in install.list(): - if not salt.utils.data.is_true(post_info[item]['Installed']): - ret['changes']['failed'] = { - item: {'Title': post_info[item]['Title'][:40] + '...', - 'KBs': post_info[item]['KBs']} + if not salt.utils.data.is_true(post_info[item]["Installed"]): + ret["changes"]["failed"] = { + item: { + "Title": post_info[item]["Title"][:40] + "...", + "KBs": post_info[item]["KBs"], + } } - ret['result'] = False + ret["result"] = False else: - ret['changes']['installed'] = { - item: {'Title': post_info[item]['Title'][:40] + '...', - 'NeedsReboot': post_info[item]['NeedsReboot'], - 'KBs': post_info[item]['KBs']} + ret["changes"]["installed"] = { + item: { + "Title": post_info[item]["Title"][:40] + "...", + "NeedsReboot": post_info[item]["NeedsReboot"], + "KBs": post_info[item]["KBs"], + } } - if ret['changes'].get('failed', False): - ret['comment'] = 'Updates failed' + if ret["changes"].get("failed", False): + ret["comment"] = "Updates failed" else: - ret['comment'] = 'Updates installed successfully' + ret["comment"] = "Updates installed successfully" return ret def removed(name, updates=None): - ''' + """ Ensure Microsoft Updates are uninstalled. Args: @@ -259,17 +261,14 @@ def removed(name, updates=None): - updates: - KB3194343 - 28cf1b09-2b1a-458c-9bd1-971d1b26b211 - ''' + """ if isinstance(updates, six.string_types): updates = [updates] if not updates: updates = name - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} wua = salt.utils.win_update.WindowsUpdateAgent() @@ -278,7 +277,7 @@ def removed(name, updates=None): # No updates found if updates.count() == 0: - ret['comment'] = 'No updates found' + ret["comment"] = "No updates found" return ret # List of updates to uninstall @@ -288,21 +287,20 @@ def removed(name, updates=None): if salt.utils.data.is_true(item.IsInstalled): uninstall.updates.Add(item) else: - removed_updates.extend('KB' + kb for kb in item.KBArticleIDs) + removed_updates.extend("KB" + kb for kb in item.KBArticleIDs) if uninstall.count() == 0: - ret['comment'] = 'Updates already removed: ' - ret['comment'] += '\n - '.join(removed_updates) + ret["comment"] = "Updates already removed: " + ret["comment"] += "\n - ".join(removed_updates) return ret # Return comment of changes if test. - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Updates will be removed:' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Updates will be removed:" for update in uninstall.updates: - ret['comment'] += '\n' - ret['comment'] += ': '.join( - [update.Identity.UpdateID, update.Title]) + ret["comment"] += "\n" + ret["comment"] += ": ".join([update.Identity.UpdateID, update.Title]) return ret # Install updates @@ -314,36 +312,42 @@ def removed(name, updates=None): # Verify the installation for item in uninstall.list(): - if salt.utils.data.is_true(post_info[item]['Installed']): - ret['changes']['failed'] = { - item: {'Title': post_info[item]['Title'][:40] + '...', - 'KBs': post_info[item]['KBs']} + if salt.utils.data.is_true(post_info[item]["Installed"]): + ret["changes"]["failed"] = { + item: { + "Title": post_info[item]["Title"][:40] + "...", + "KBs": post_info[item]["KBs"], + } } - ret['result'] = False + ret["result"] = False else: - ret['changes']['removed'] = { - item: {'Title': post_info[item]['Title'][:40] + '...', - 'NeedsReboot': post_info[item]['NeedsReboot'], - 'KBs': post_info[item]['KBs']} + ret["changes"]["removed"] = { + item: { + "Title": post_info[item]["Title"][:40] + "...", + "NeedsReboot": post_info[item]["NeedsReboot"], + "KBs": post_info[item]["KBs"], + } } - if ret['changes'].get('failed', False): - ret['comment'] = 'Updates failed' + if ret["changes"].get("failed", False): + ret["comment"] = "Updates failed" else: - ret['comment'] = 'Updates removed successfully' + ret["comment"] = "Updates removed successfully" return ret -def uptodate(name, - software=True, - drivers=False, - skip_hidden=False, - skip_mandatory=False, - skip_reboot=True, - categories=None, - severities=None,): - ''' +def uptodate( + name, + software=True, + drivers=False, + skip_hidden=False, + skip_mandatory=False, + skip_reboot=True, + categories=None, + severities=None, +): + """ Ensure Microsoft Updates that match the passed criteria are installed. Updates will be downloaded if needed. @@ -424,23 +428,25 @@ def uptodate(name, wua.uptodate: - severities: - Critical - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} wua = salt.utils.win_update.WindowsUpdateAgent() available_updates = wua.available( - skip_hidden=skip_hidden, skip_installed=True, - skip_mandatory=skip_mandatory, skip_reboot=skip_reboot, - software=software, drivers=drivers, categories=categories, - severities=severities) + skip_hidden=skip_hidden, + skip_installed=True, + skip_mandatory=skip_mandatory, + skip_reboot=skip_reboot, + software=software, + drivers=drivers, + categories=categories, + severities=severities, + ) # No updates found if available_updates.count() == 0: - ret['comment'] = 'No updates found' + ret["comment"] = "No updates found" return ret updates = list(available_updates.list().keys()) @@ -461,13 +467,12 @@ def uptodate(name, install.updates.Add(item) # Return comment of changes if test. - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Updates will be installed:' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Updates will be installed:" for update in install.updates: - ret['comment'] += '\n' - ret['comment'] += ': '.join( - [update.Identity.UpdateID, update.Title]) + ret["comment"] += "\n" + ret["comment"] += ": ".join([update.Identity.UpdateID, update.Title]) return ret # Download updates @@ -483,22 +488,26 @@ def uptodate(name, # Verify the installation for item in install.list(): - if not salt.utils.data.is_true(post_info[item]['Installed']): - ret['changes']['failed'] = { - item: {'Title': post_info[item]['Title'][:40] + '...', - 'KBs': post_info[item]['KBs']} + if not salt.utils.data.is_true(post_info[item]["Installed"]): + ret["changes"]["failed"] = { + item: { + "Title": post_info[item]["Title"][:40] + "...", + "KBs": post_info[item]["KBs"], + } } - ret['result'] = False + ret["result"] = False else: - ret['changes']['installed'] = { - item: {'Title': post_info[item]['Title'][:40] + '...', - 'NeedsReboot': post_info[item]['NeedsReboot'], - 'KBs': post_info[item]['KBs']} + ret["changes"]["installed"] = { + item: { + "Title": post_info[item]["Title"][:40] + "...", + "NeedsReboot": post_info[item]["NeedsReboot"], + "KBs": post_info[item]["KBs"], + } } - if ret['changes'].get('failed', False): - ret['comment'] = 'Updates failed' + if ret["changes"].get("failed", False): + ret["comment"] = "Updates failed" else: - ret['comment'] = 'Updates installed successfully' + ret["comment"] = "Updates installed successfully" return ret diff --git a/salt/states/win_wusa.py b/salt/states/win_wusa.py index 0e8942ce70e..af7eb49d304 100644 --- a/salt/states/win_wusa.py +++ b/salt/states/win_wusa.py @@ -1,42 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" Microsoft Updates (KB) Management This module provides the ability to enforce KB installations from files (.msu), without WSUS or Windows Update .. versionadded:: 2018.3.4 -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import logging # Import salt libs import salt.utils.platform import salt.utils.url -from salt.exceptions import SaltInvocationError -from salt.exceptions import CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'wusa' +__virtualname__ = "wusa" def __virtual__(): - ''' + """ Load only on Windows - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'Only available on Windows systems' + return False, "Only available on Windows systems" return __virtualname__ def installed(name, source): - ''' + """ Ensure an update is installed on the minion Args: @@ -54,11 +54,8 @@ def installed(name, source): KB123456: wusa.installed: - source: salt://kb123456.msu - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Input validation if not name: @@ -67,48 +64,49 @@ def installed(name, source): raise SaltInvocationError('Must specify a "source" file to install') # Is the KB already installed - if __salt__['wusa.is_installed'](name): - ret['result'] = True - ret['comment'] = '{0} already installed'.format(name) + if __salt__["wusa.is_installed"](name): + ret["result"] = True + ret["comment"] = "{0} already installed".format(name) return ret # Check for test=True - if __opts__['test'] is True: - ret['result'] = None - ret['comment'] = '{0} would be installed'.format(name) - ret['result'] = None + if __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "{0} would be installed".format(name) + ret["result"] = None return ret # Cache the file - cached_source_path = __salt__['cp.cache_file'](path=source, saltenv=__env__) + cached_source_path = __salt__["cp.cache_file"](path=source, saltenv=__env__) if not cached_source_path: msg = 'Unable to cache {0} from saltenv "{1}"'.format( - salt.utils.url.redact_http_basic_auth(source), __env__) - ret['comment'] = msg + salt.utils.url.redact_http_basic_auth(source), __env__ + ) + ret["comment"] = msg return ret # Install the KB - additional_comment = '' + additional_comment = "" try: - __salt__['wusa.install'](cached_source_path) + __salt__["wusa.install"](cached_source_path) except CommandExecutionError as exc: additional_comment = exc.message # Verify successful install - if __salt__['wusa.is_installed'](name): - ret['comment'] = '{0} was installed. {1}'.format(name, additional_comment) - ret['changes'] = {'old': False, 'new': True} - ret['result'] = True + if __salt__["wusa.is_installed"](name): + ret["comment"] = "{0} was installed. {1}".format(name, additional_comment) + ret["changes"] = {"old": False, "new": True} + ret["result"] = True else: - ret['comment'] = '{0} failed to install. {1}'.format(name, additional_comment) + ret["comment"] = "{0} failed to install. {1}".format(name, additional_comment) return ret def uninstalled(name): - ''' + """ Ensure an update is uninstalled from the minion Args: @@ -122,34 +120,31 @@ def uninstalled(name): KB123456: wusa.uninstalled - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Is the KB already uninstalled - if not __salt__['wusa.is_installed'](name): - ret['result'] = True - ret['comment'] = '{0} already uninstalled'.format(name) + if not __salt__["wusa.is_installed"](name): + ret["result"] = True + ret["comment"] = "{0} already uninstalled".format(name) return ret # Check for test=True - if __opts__['test'] is True: - ret['result'] = None - ret['comment'] = '{0} would be uninstalled'.format(name) - ret['result'] = None + if __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "{0} would be uninstalled".format(name) + ret["result"] = None return ret # Uninstall the KB - __salt__['wusa.uninstall'](name) + __salt__["wusa.uninstall"](name) # Verify successful uninstall - if not __salt__['wusa.is_installed'](name): - ret['comment'] = '{0} was uninstalled'.format(name) - ret['changes'] = {'old': True, 'new': False} - ret['result'] = True + if not __salt__["wusa.is_installed"](name): + ret["comment"] = "{0} was uninstalled".format(name) + ret["changes"] = {"old": True, "new": False} + ret["result"] = True else: - ret['comment'] = '{0} failed to uninstall'.format(name) + ret["comment"] = "{0} failed to uninstall".format(name) return ret diff --git a/salt/states/winrepo.py b/salt/states/winrepo.py index 67c0fd95bae..1ccc4edf785 100644 --- a/salt/states/winrepo.py +++ b/salt/states/winrepo.py @@ -1,27 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Manage Windows Package Repository -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import itertools # Python Libs import os import stat -import itertools + +import salt.config # Salt Modules import salt.runner -import salt.config import salt.syspaths import salt.utils.path def __virtual__(): - return 'winrepo' + return "winrepo" def genrepo(name, force=False, allow_empty=False): - ''' + """ Refresh the winrepo.p file of the repository (salt-run winrepo.genrepo) If ``force`` is ``True`` no checks will be made and the repository will be @@ -39,19 +41,16 @@ def genrepo(name, force=False, allow_empty=False): winrepo: winrepo.genrepo - ''' + """ - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} master_config = salt.config.master_config( - os.path.join(salt.syspaths.CONFIG_DIR, 'master') + os.path.join(salt.syspaths.CONFIG_DIR, "master") ) - winrepo_dir = master_config['winrepo_dir'] - winrepo_cachefile = master_config['winrepo_cachefile'] + winrepo_dir = master_config["winrepo_dir"] + winrepo_cachefile = master_config["winrepo_cachefile"] # We're actually looking for the full path to the cachefile here, so # prepend the winrepo_dir @@ -62,34 +61,36 @@ def genrepo(name, force=False, allow_empty=False): execute = False if not force: if not os.path.exists(winrepo_dir): - ret['result'] = False - ret['comment'] = '{0} is missing'.format(winrepo_dir) + ret["result"] = False + ret["comment"] = "{0} is missing".format(winrepo_dir) return ret elif not os.path.exists(winrepo_cachefile): execute = True - ret['comment'] = '{0} is missing'.format(winrepo_cachefile) + ret["comment"] = "{0} is missing".format(winrepo_cachefile) else: winrepo_cachefile_mtime = os.stat(winrepo_cachefile)[stat.ST_MTIME] for root, dirs, files in salt.utils.path.os_walk(winrepo_dir): for name in itertools.chain(files, dirs): full_path = os.path.join(root, name) if os.stat(full_path)[stat.ST_MTIME] > winrepo_cachefile_mtime: - ret['comment'] = 'mtime({0}) < mtime({1})'.format(winrepo_cachefile, full_path) + ret["comment"] = "mtime({0}) < mtime({1})".format( + winrepo_cachefile, full_path + ) execute = True break - if __opts__['test']: - ret['result'] = None + if __opts__["test"]: + ret["result"] = None return ret if not execute and not force: return ret runner = salt.runner.RunnerClient(master_config) - runner_ret = runner.cmd('winrepo.genrepo', []) - ret['changes'] = {'winrepo': runner_ret} + runner_ret = runner.cmd("winrepo.genrepo", []) + ret["changes"] = {"winrepo": runner_ret} if isinstance(runner_ret, dict) and runner_ret == {} and not allow_empty: os.remove(winrepo_cachefile) - ret['result'] = False - ret['comment'] = 'winrepo.genrepo returned empty' + ret["result"] = False + ret["comment"] = "winrepo.genrepo returned empty" return ret diff --git a/salt/states/wordpress.py b/salt/states/wordpress.py index 33bdf00f6fd..d1c7226e610 100644 --- a/salt/states/wordpress.py +++ b/salt/states/wordpress.py @@ -1,20 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" This state module is used to manage Wordpress installations :depends: wp binary from http://wp-cli.org/ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - return 'wordpress.show_plugin' in __salt__ + if "wordpress.show_plugin" in __salt__: + return True + return (False, "wordpress module could not be loaded") def installed(name, user, admin_user, admin_password, admin_email, title, url): - ''' + """ Run the initial setup of wordpress name @@ -48,38 +50,35 @@ def installed(name, user, admin_user, admin_password, admin_email, title, url): - admin_email: dwallace@example.com - admin_password: password123 - url: https://blog.dwallace.com - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} - check = __salt__['wordpress.is_installed'](name, user) + check = __salt__["wordpress.is_installed"](name, user) if check: - ret['result'] = True - ret['comment'] = 'Wordpress is already installed: {0}'.format(name) + ret["result"] = True + ret["comment"] = "Wordpress is already installed: {0}".format(name) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Wordpress will be installed: {0}'.format(name) + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = "Wordpress will be installed: {0}".format(name) return ret - resp = __salt__['wordpress.install'](name, user, admin_user, admin_password, admin_email, title, url) + resp = __salt__["wordpress.install"]( + name, user, admin_user, admin_password, admin_email, title, url + ) if resp: - ret['result'] = True - ret['comment'] = 'Wordpress Installed: {0}'.format(name) - ret['changes'] = { - 'new': resp - } + ret["result"] = True + ret["comment"] = "Wordpress Installed: {0}".format(name) + ret["changes"] = {"new": resp} else: - ret['comment'] = 'Failed to install wordpress: {0}'.format(name) + ret["comment"] = "Failed to install wordpress: {0}".format(name) return ret def activated(name, path, user): - ''' + """ Activate wordpress plugins name @@ -97,46 +96,43 @@ def activated(name, path, user): wordpress.activated: - path: /var/www/html - user: apache - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} - check = __salt__['wordpress.show_plugin'](name, path, user) + check = __salt__["wordpress.show_plugin"](name, path, user) - if check['status'] == 'active': - ret['result'] = True - ret['comment'] = 'Plugin already activated: {0}'.format(name) + if check["status"] == "active": + ret["result"] = True + ret["comment"] = "Plugin already activated: {0}".format(name) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Plugin will be activated: {0}'.format(name) + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = "Plugin will be activated: {0}".format(name) return ret - resp = __salt__['wordpress.activate'](name, path, user) + resp = __salt__["wordpress.activate"](name, path, user) if resp is True: - ret['result'] = True - ret['comment'] = 'Plugin activated: {0}'.format(name) - ret['changes'] = { - 'old': check, - 'new': __salt__['wordpress.show_plugin'](name, path, user) + ret["result"] = True + ret["comment"] = "Plugin activated: {0}".format(name) + ret["changes"] = { + "old": check, + "new": __salt__["wordpress.show_plugin"](name, path, user), } elif resp is None: - ret['result'] = True - ret['comment'] = 'Plugin already activated: {0}'.format(name) - ret['changes'] = { - 'old': check, - 'new': __salt__['wordpress.show_plugin'](name, path, user) + ret["result"] = True + ret["comment"] = "Plugin already activated: {0}".format(name) + ret["changes"] = { + "old": check, + "new": __salt__["wordpress.show_plugin"](name, path, user), } else: - ret['comment'] = 'Plugin failed to activate: {0}'.format(name) + ret["comment"] = "Plugin failed to activate: {0}".format(name) return ret def deactivated(name, path, user): - ''' + """ Deactivate wordpress plugins name @@ -154,39 +150,36 @@ def deactivated(name, path, user): wordpress.deactivated: - path: /var/www/html - user: apache - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} - check = __salt__['wordpress.show_plugin'](name, path, user) + check = __salt__["wordpress.show_plugin"](name, path, user) - if check['status'] == 'inactive': - ret['result'] = True - ret['comment'] = 'Plugin already deactivated: {0}'.format(name) + if check["status"] == "inactive": + ret["result"] = True + ret["comment"] = "Plugin already deactivated: {0}".format(name) return ret - elif __opts__['test']: - ret['result'] = None - ret['comment'] = 'Plugin will be deactivated: {0}'.format(name) + elif __opts__["test"]: + ret["result"] = None + ret["comment"] = "Plugin will be deactivated: {0}".format(name) return ret - resp = __salt__['wordpress.deactivate'](name, path, user) + resp = __salt__["wordpress.deactivate"](name, path, user) if resp is True: - ret['result'] = True - ret['comment'] = 'Plugin deactivated: {0}'.format(name) - ret['changes'] = { - 'old': check, - 'new': __salt__['wordpress.show_plugin'](name, path, user) + ret["result"] = True + ret["comment"] = "Plugin deactivated: {0}".format(name) + ret["changes"] = { + "old": check, + "new": __salt__["wordpress.show_plugin"](name, path, user), } elif resp is None: - ret['result'] = True - ret['comment'] = 'Plugin already deactivated: {0}'.format(name) - ret['changes'] = { - 'old': check, - 'new': __salt__['wordpress.show_plugin'](name, path, user) + ret["result"] = True + ret["comment"] = "Plugin already deactivated: {0}".format(name) + ret["changes"] = { + "old": check, + "new": __salt__["wordpress.show_plugin"](name, path, user), } else: - ret['comment'] = 'Plugin failed to deactivate: {0}'.format(name) + ret["comment"] = "Plugin failed to deactivate: {0}".format(name) return ret diff --git a/salt/states/x509.py b/salt/states/x509.py index 3774f7d5eb0..341e180e7ef 100644 --- a/salt/states/x509.py +++ b/salt/states/x509.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage X509 Certificates .. versionadded:: 2015.8.0 @@ -24,7 +24,7 @@ For remote signing, peers must be permitted to remotely call the peer: .*: - - x509.sign_remote_certificate + - sign_remote_certificate /srv/salt/top.sls @@ -50,12 +50,12 @@ the mine where it can be easily retrieved by other minions. salt-minion: service.running: - enable: True - - listen: - - file: /etc/salt/minion.d/signing_policies.conf + - watch: + - file: /etc/salt/minion.d/x509.conf - /etc/salt/minion.d/signing_policies.conf: + /etc/salt/minion.d/x509.conf: file.managed: - - source: salt://signing_policies.conf + - source: salt://x509.conf /etc/pki: file.directory @@ -84,22 +84,17 @@ the mine where it can be easily retrieved by other minions. - require: - file: /etc/pki - mine.send: - module.run: - - func: x509.get_pem_entries - - kwargs: - glob_path: /etc/pki/ca.crt - - onchanges: - - x509: /etc/pki/ca.crt - The signing policy defines properties that override any property requested or included in a CRL. It also can define a restricted list of minions which are allowed to remotely invoke this signing policy. -/srv/salt/signing_policies.conf +/srv/salt/x509.conf .. code-block:: yaml + mine_functions: + x509.get_pem_entries: [/etc/pki/ca.crt] + x509_signing_policies: www: - minions: 'www' @@ -152,14 +147,15 @@ This state creates a private key then requests a certificate signed by ca accord bits: 4096 backup: True -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import copy import datetime import os import re -import copy # Import Salt Libs import salt.exceptions @@ -174,30 +170,29 @@ except ImportError: def __virtual__(): - ''' + """ only load this module if the corresponding execution module is loaded - ''' - if 'x509.get_pem_entry' in __salt__: - return 'x509' + """ + if "x509.get_pem_entry" in __salt__: + return "x509" else: - return (False, 'Could not load x509 state: m2crypto unavailable') + return (False, "Could not load x509 state: m2crypto unavailable") def _revoked_to_list(revs): - ''' + """ Turn the mess of OrderedDicts and Lists into a list of dicts for use in the CRL module. - ''' + """ list_ = [] for rev in revs: - for rev_name, props in six.iteritems( - rev): # pylint: disable=unused-variable + for rev_name, props in six.iteritems(rev): # pylint: disable=unused-variable dict_ = {} for prop in props: for propname, val in six.iteritems(prop): if isinstance(val, datetime.datetime): - val = val.strftime('%Y-%m-%d %H:%M:%S') + val = val.strftime("%Y-%m-%d %H:%M:%S") dict_[propname] = val list_.append(dict_) @@ -205,15 +200,17 @@ def _revoked_to_list(revs): def _get_file_args(name, **kwargs): - valid_file_args = ['user', - 'group', - 'mode', - 'makedirs', - 'dir_mode', - 'backup', - 'create', - 'follow_symlinks', - 'check_cmd'] + valid_file_args = [ + "user", + "group", + "mode", + "makedirs", + "dir_mode", + "backup", + "create", + "follow_symlinks", + "check_cmd", + ] file_args = {} extra_args = {} for k, v in kwargs.items(): @@ -221,36 +218,39 @@ def _get_file_args(name, **kwargs): file_args[k] = v else: extra_args[k] = v - file_args['name'] = name + file_args["name"] = name return file_args, extra_args -def _check_private_key(name, bits=2048, passphrase=None, - new=False, overwrite=False): +def _check_private_key(name, bits=2048, passphrase=None, new=False, overwrite=False): current_bits = 0 if os.path.isfile(name): try: - current_bits = __salt__['x509.get_private_key_size']( - private_key=name, passphrase=passphrase) + current_bits = __salt__["x509.get_private_key_size"]( + private_key=name, passphrase=passphrase + ) except salt.exceptions.SaltInvocationError: pass except RSAError: if not overwrite: raise salt.exceptions.CommandExecutionError( - 'The provided passphrase cannot decrypt the private key.') + "The provided passphrase cannot decrypt the private key." + ) return current_bits == bits and not new -def private_key_managed(name, - bits=2048, - passphrase=None, - cipher='aes_128_cbc', - new=False, - overwrite=False, - verbose=True, - **kwargs): - ''' +def private_key_managed( + name, + bits=2048, + passphrase=None, + cipher="aes_128_cbc", + new=False, + overwrite=False, + verbose=True, + **kwargs +): + """ Manage a private key's existence. name: @@ -296,31 +296,33 @@ def private_key_managed(name, - prereq: - x509: /etc/pki/www.crt {%- endif %} - ''' + """ file_args, kwargs = _get_file_args(name, **kwargs) new_key = False if _check_private_key( - name, bits=bits, passphrase=passphrase, new=new, overwrite=overwrite): - file_args['contents'] = __salt__['x509.get_pem_entry']( - name, pem_type='RSA PRIVATE KEY') + name, bits=bits, passphrase=passphrase, new=new, overwrite=overwrite + ): + file_args["contents"] = __salt__["x509.get_pem_entry"]( + name, pem_type="RSA PRIVATE KEY" + ) else: new_key = True - file_args['contents'] = __salt__['x509.create_private_key']( - text=True, bits=bits, passphrase=passphrase, cipher=cipher, verbose=verbose) + file_args["contents"] = __salt__["x509.create_private_key"]( + text=True, bits=bits, passphrase=passphrase, cipher=cipher, verbose=verbose + ) # Ensure the key contents are a string before passing it along - file_args['contents'] = salt.utils.stringutils.to_str(file_args['contents']) + file_args["contents"] = salt.utils.stringutils.to_str(file_args["contents"]) - ret = __states__['file.managed'](**file_args) - if ret['changes'] and new_key: - ret['changes'] = {'new': 'New private key generated'} + ret = __states__["file.managed"](**file_args) + if ret["changes"] and new_key: + ret["changes"] = {"new": "New private key generated"} return ret -def csr_managed(name, - **kwargs): - ''' +def csr_managed(name, **kwargs): + """ Manage a Certificate Signing Request name: @@ -345,30 +347,28 @@ def csr_managed(name, - ST: Utah - L: Salt Lake City - keyUsage: 'critical dataEncipherment' - ''' + """ try: - old = __salt__['x509.read_csr'](name) + old = __salt__["x509.read_csr"](name) except salt.exceptions.SaltInvocationError: - old = '{0} is not a valid csr.'.format(name) + old = "{0} is not a valid csr.".format(name) file_args, kwargs = _get_file_args(name, **kwargs) - file_args['contents'] = __salt__['x509.create_csr'](text=True, **kwargs) + file_args["contents"] = __salt__["x509.create_csr"](text=True, **kwargs) - ret = __states__['file.managed'](**file_args) - if ret['changes']: - new = __salt__['x509.read_csr'](file_args['contents']) + ret = __states__["file.managed"](**file_args) + if ret["changes"]: + new = __salt__["x509.read_csr"](file_args["contents"]) if old != new: - ret['changes'] = {"Old": old, "New": new} + ret["changes"] = {"Old": old, "New": new} return ret -def certificate_managed(name, - days_remaining=90, - managed_private_key=None, - append_certs=None, - **kwargs): - ''' +def certificate_managed( + name, days_remaining=90, managed_private_key=None, append_certs=None, **kwargs +): + """ Manage a Certificate name @@ -425,9 +425,9 @@ def certificate_managed(name, - days_remaining: 30 - backup: True - ''' - if 'path' in kwargs: - name = kwargs.pop('path') + """ + if "path" in kwargs: + name = kwargs.pop("path") file_args, kwargs = _get_file_args(name, **kwargs) @@ -435,160 +435,184 @@ def certificate_managed(name, new_private_key = False if managed_private_key: private_key_args = { - 'name': name, - 'new': False, - 'overwrite': False, - 'bits': 2048, - 'passphrase': None, - 'cipher': 'aes_128_cbc', - 'verbose': True + "name": name, + "new": False, + "overwrite": False, + "bits": 2048, + "passphrase": None, + "cipher": "aes_128_cbc", + "verbose": True, } private_key_args.update(managed_private_key) - kwargs['public_key_passphrase'] = private_key_args['passphrase'] + kwargs["public_key_passphrase"] = private_key_args["passphrase"] - if private_key_args['new']: + if private_key_args["new"]: rotate_private_key = True - private_key_args['new'] = False + private_key_args["new"] = False - if _check_private_key(private_key_args['name'], - bits=private_key_args['bits'], - passphrase=private_key_args['passphrase'], - new=private_key_args['new'], - overwrite=private_key_args['overwrite']): - private_key = __salt__['x509.get_pem_entry']( - private_key_args['name'], pem_type='RSA PRIVATE KEY') + if _check_private_key( + private_key_args["name"], + bits=private_key_args["bits"], + passphrase=private_key_args["passphrase"], + new=private_key_args["new"], + overwrite=private_key_args["overwrite"], + ): + private_key = __salt__["x509.get_pem_entry"]( + private_key_args["name"], pem_type="RSA PRIVATE KEY" + ) else: new_private_key = True - private_key = __salt__['x509.create_private_key'](text=True, bits=private_key_args['bits'], passphrase=private_key_args[ - 'passphrase'], cipher=private_key_args['cipher'], verbose=private_key_args['verbose']) + private_key = __salt__["x509.create_private_key"]( + text=True, + bits=private_key_args["bits"], + passphrase=private_key_args["passphrase"], + cipher=private_key_args["cipher"], + verbose=private_key_args["verbose"], + ) - kwargs['public_key'] = private_key + kwargs["public_key"] = private_key current_days_remaining = 0 current_comp = {} if os.path.isfile(name): try: - current = __salt__['x509.read_certificate'](certificate=name) + current = __salt__["x509.read_certificate"](certificate=name) current_comp = copy.deepcopy(current) - if 'serial_number' not in kwargs: - current_comp.pop('Serial Number') - if 'signing_cert' not in kwargs: + if "serial_number" not in kwargs: + current_comp.pop("Serial Number") + if "signing_cert" not in kwargs: try: - current_comp['X509v3 Extensions']['authorityKeyIdentifier'] = ( - re.sub(r'serial:([0-9A-F]{2}:)*[0-9A-F]{2}', 'serial:--', - current_comp['X509v3 Extensions']['authorityKeyIdentifier'])) + current_comp["X509v3 Extensions"][ + "authorityKeyIdentifier" + ] = re.sub( + r"serial:([0-9A-F]{2}:)*[0-9A-F]{2}", + "serial:--", + current_comp["X509v3 Extensions"]["authorityKeyIdentifier"], + ) except KeyError: pass - current_comp.pop('Not Before') - current_comp.pop('MD5 Finger Print') - current_comp.pop('SHA1 Finger Print') - current_comp.pop('SHA-256 Finger Print') - current_notafter = current_comp.pop('Not After') + current_comp.pop("Not Before") + current_comp.pop("MD5 Finger Print") + current_comp.pop("SHA1 Finger Print") + current_comp.pop("SHA-256 Finger Print") + current_notafter = current_comp.pop("Not After") current_days_remaining = ( - datetime.datetime.strptime(current_notafter, '%Y-%m-%d %H:%M:%S') - - datetime.datetime.now()).days + datetime.datetime.strptime(current_notafter, "%Y-%m-%d %H:%M:%S") + - datetime.datetime.now() + ).days if days_remaining == 0: days_remaining = current_days_remaining - 1 except salt.exceptions.SaltInvocationError: - current = '{0} is not a valid Certificate.'.format(name) + current = "{0} is not a valid Certificate.".format(name) else: - current = '{0} does not exist.'.format(name) + current = "{0} does not exist.".format(name) - if 'ca_server' in kwargs and 'signing_policy' not in kwargs: + if "ca_server" in kwargs and "signing_policy" not in kwargs: raise salt.exceptions.SaltInvocationError( - 'signing_policy must be specified if ca_server is.') + "signing_policy must be specified if ca_server is." + ) - new = __salt__['x509.create_certificate'](testrun=True, **kwargs) + new = __salt__["x509.create_certificate"](testrun=True, **kwargs) if isinstance(new, dict): new_comp = copy.deepcopy(new) - new.pop('Issuer Public Key') - if 'serial_number' not in kwargs: - new_comp.pop('Serial Number') - if 'signing_cert' not in kwargs: + new.pop("Issuer Public Key") + if "serial_number" not in kwargs: + new_comp.pop("Serial Number") + if "signing_cert" not in kwargs: try: - new_comp['X509v3 Extensions']['authorityKeyIdentifier'] = ( - re.sub(r'serial:([0-9A-F]{2}:)*[0-9A-F]{2}', 'serial:--', - new_comp['X509v3 Extensions']['authorityKeyIdentifier'])) + new_comp["X509v3 Extensions"]["authorityKeyIdentifier"] = re.sub( + r"serial:([0-9A-F]{2}:)*[0-9A-F]{2}", + "serial:--", + new_comp["X509v3 Extensions"]["authorityKeyIdentifier"], + ) except KeyError: pass - new_comp.pop('Not Before') - new_comp.pop('Not After') - new_comp.pop('MD5 Finger Print') - new_comp.pop('SHA1 Finger Print') - new_comp.pop('SHA-256 Finger Print') - new_issuer_public_key = new_comp.pop('Issuer Public Key') + new_comp.pop("Not Before") + new_comp.pop("Not After") + new_comp.pop("MD5 Finger Print") + new_comp.pop("SHA1 Finger Print") + new_comp.pop("SHA-256 Finger Print") + new_issuer_public_key = new_comp.pop("Issuer Public Key") else: new_comp = new new_certificate = False - if (current_comp == new_comp and - current_days_remaining > days_remaining and - __salt__['x509.verify_signature'](name, new_issuer_public_key)): - certificate = __salt__['x509.get_pem_entry']( - name, pem_type='CERTIFICATE') + if ( + current_comp == new_comp + and current_days_remaining > days_remaining + and __salt__["x509.verify_signature"](name, new_issuer_public_key) + ): + certificate = __salt__["x509.get_pem_entry"](name, pem_type="CERTIFICATE") else: if rotate_private_key and not new_private_key: new_private_key = True - private_key = __salt__['x509.create_private_key']( - text=True, bits=private_key_args['bits'], verbose=private_key_args['verbose']) - kwargs['public_key'] = private_key + private_key = __salt__["x509.create_private_key"]( + text=True, + bits=private_key_args["bits"], + verbose=private_key_args["verbose"], + ) + kwargs["public_key"] = private_key new_certificate = True - certificate = __salt__['x509.create_certificate'](text=True, **kwargs) + certificate = __salt__["x509.create_certificate"](text=True, **kwargs) - file_args['contents'] = '' + file_args["contents"] = "" private_ret = {} if managed_private_key: - if private_key_args['name'] == name: - file_args['contents'] = private_key + if private_key_args["name"] == name: + file_args["contents"] = private_key else: private_file_args = copy.deepcopy(file_args) unique_private_file_args, _ = _get_file_args(**private_key_args) private_file_args.update(unique_private_file_args) - private_file_args['contents'] = private_key - private_ret = __states__['file.managed'](**private_file_args) - if not private_ret['result']: + private_file_args["contents"] = private_key + private_ret = __states__["file.managed"](**private_file_args) + if not private_ret["result"]: return private_ret - file_args['contents'] += salt.utils.stringutils.to_str(certificate) + file_args["contents"] += salt.utils.stringutils.to_str(certificate) if not append_certs: append_certs = [] for append_cert in append_certs: - file_args[ - 'contents'] += __salt__['x509.get_pem_entry'](append_cert, pem_type='CERTIFICATE') + file_args["contents"] += __salt__["x509.get_pem_entry"]( + append_cert, pem_type="CERTIFICATE" + ) - file_args['show_changes'] = False - ret = __states__['file.managed'](**file_args) + file_args["show_changes"] = False + ret = __states__["file.managed"](**file_args) - if ret['changes']: - ret['changes'] = {'Certificate': ret['changes']} + if ret["changes"]: + ret["changes"] = {"Certificate": ret["changes"]} else: - ret['changes'] = {} - if private_ret and private_ret['changes']: - ret['changes']['Private Key'] = private_ret['changes'] + ret["changes"] = {} + if private_ret and private_ret["changes"]: + ret["changes"]["Private Key"] = private_ret["changes"] if new_private_key: - ret['changes']['Private Key'] = 'New private key generated' + ret["changes"]["Private Key"] = "New private key generated" if new_certificate: - ret['changes']['Certificate'] = { - 'Old': current, - 'New': __salt__['x509.read_certificate'](certificate=certificate)} + ret["changes"]["Certificate"] = { + "Old": current, + "New": __salt__["x509.read_certificate"](certificate=certificate), + } return ret -def crl_managed(name, - signing_private_key, - signing_private_key_passphrase=None, - signing_cert=None, - revoked=None, - days_valid=100, - digest="", - days_remaining=30, - include_expired=False, - **kwargs): - ''' +def crl_managed( + name, + signing_private_key, + signing_private_key_passphrase=None, + signing_cert=None, + revoked=None, + days_valid=100, + digest="", + days_remaining=30, + include_expired=False, + **kwargs +): + """ Manage a Certificate Revocation List name @@ -646,7 +670,7 @@ def crl_managed(name, - not_after: 2016-01-01 00:00:00 - revocation_date: 2015-02-25 00:00:00 - reason: cessationOfOperation - ''' + """ if revoked is None: revoked = [] @@ -657,51 +681,59 @@ def crl_managed(name, if os.path.isfile(name): try: - current = __salt__['x509.read_crl'](crl=name) + current = __salt__["x509.read_crl"](crl=name) current_comp = current.copy() - current_comp.pop('Last Update') - current_notafter = current_comp.pop('Next Update') + current_comp.pop("Last Update") + current_notafter = current_comp.pop("Next Update") current_days_remaining = ( - datetime.datetime.strptime(current_notafter, '%Y-%m-%d %H:%M:%S') - - datetime.datetime.now()).days + datetime.datetime.strptime(current_notafter, "%Y-%m-%d %H:%M:%S") + - datetime.datetime.now() + ).days if days_remaining == 0: days_remaining = current_days_remaining - 1 except salt.exceptions.SaltInvocationError: - current = '{0} is not a valid CRL.'.format(name) + current = "{0} is not a valid CRL.".format(name) else: - current = '{0} does not exist.'.format(name) + current = "{0} does not exist.".format(name) - new_crl = __salt__['x509.create_crl'](text=True, signing_private_key=signing_private_key, signing_private_key_passphrase=signing_private_key_passphrase, - signing_cert=signing_cert, revoked=revoked, days_valid=days_valid, digest=digest, include_expired=include_expired) + new_crl = __salt__["x509.create_crl"]( + text=True, + signing_private_key=signing_private_key, + signing_private_key_passphrase=signing_private_key_passphrase, + signing_cert=signing_cert, + revoked=revoked, + days_valid=days_valid, + digest=digest, + include_expired=include_expired, + ) - new = __salt__['x509.read_crl'](crl=new_crl) + new = __salt__["x509.read_crl"](crl=new_crl) new_comp = new.copy() - new_comp.pop('Last Update') - new_comp.pop('Next Update') + new_comp.pop("Last Update") + new_comp.pop("Next Update") file_args, kwargs = _get_file_args(name, **kwargs) new_crl_created = False - if (current_comp == new_comp and - current_days_remaining > days_remaining and - __salt__['x509.verify_crl'](name, signing_cert)): - file_args['contents'] = __salt__[ - 'x509.get_pem_entry'](name, pem_type='X509 CRL') + if ( + current_comp == new_comp + and current_days_remaining > days_remaining + and __salt__["x509.verify_crl"](name, signing_cert) + ): + file_args["contents"] = __salt__["x509.get_pem_entry"]( + name, pem_type="X509 CRL" + ) else: new_crl_created = True - file_args['contents'] = new_crl + file_args["contents"] = new_crl - ret = __states__['file.managed'](**file_args) + ret = __states__["file.managed"](**file_args) if new_crl_created: - ret['changes'] = {'Old': current, 'New': __salt__[ - 'x509.read_crl'](crl=new_crl)} + ret["changes"] = {"Old": current, "New": __salt__["x509.read_crl"](crl=new_crl)} return ret -def pem_managed(name, - text, - backup=False, - **kwargs): - ''' +def pem_managed(name, text, backup=False, **kwargs): + """ Manage the contents of a PEM file directly with the content in text, ensuring correct formatting. name: @@ -712,8 +744,8 @@ def pem_managed(name, kwargs: Any arguments supported by :py:func:`file.managed <salt.states.file.managed>` are supported. - ''' + """ file_args, kwargs = _get_file_args(name, **kwargs) - file_args['contents'] = __salt__['x509.get_pem_entry'](text=text) + file_args["contents"] = __salt__["x509.get_pem_entry"](text=text) - return __states__['file.managed'](**file_args) + return __states__["file.managed"](**file_args) diff --git a/salt/states/xml.py b/salt/states/xml.py index 216f443c657..e6d4f622555 100644 --- a/salt/states/xml.py +++ b/salt/states/xml.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" XML Manager =========== State managment of XML files -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -14,17 +14,17 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the XML execution module is available. - ''' - if 'xml.get_value' in __salt__: - return 'xml' + """ + if "xml.get_value" in __salt__: + return "xml" else: return False, "The xml execution module is not available" def value_present(name, xpath, value, **kwargs): - ''' + """ .. versionadded:: 3000 Manages a given XML file @@ -45,32 +45,29 @@ def value_present(name, xpath, value, **kwargs): - name: /tmp/test.xml - xpath: .//playwright[@id='1'] - value: William Shakespeare - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - if 'test' not in kwargs: - kwargs['test'] = __opts__.get('test', False) + if "test" not in kwargs: + kwargs["test"] = __opts__.get("test", False) - current_value = __salt__['xml.get_value'](name, xpath) + current_value = __salt__["xml.get_value"](name, xpath) if not current_value: - ret['result'] = False - ret['comment'] = 'xpath query {0} not found in {1}'.format(xpath, name) + ret["result"] = False + ret["comment"] = "xpath query {0} not found in {1}".format(xpath, name) return ret if current_value != value: - if kwargs['test']: - ret['result'] = None - ret['comment'] = '{0} will be updated'.format(name) - ret['changes'] = {name: {'old': current_value, 'new': value}} + if kwargs["test"]: + ret["result"] = None + ret["comment"] = "{0} will be updated".format(name) + ret["changes"] = {name: {"old": current_value, "new": value}} else: - results = __salt__['xml.set_value'](name, xpath, value) - ret['result'] = results - ret['comment'] = '{0} updated'.format(name) - ret['changes'] = {name: {'old': current_value, 'new': value}} + results = __salt__["xml.set_value"](name, xpath, value) + ret["result"] = results + ret["comment"] = "{0} updated".format(name) + ret["changes"] = {name: {"old": current_value, "new": value}} else: - ret['comment'] = '{0} is already present'.format(value) + ret["comment"] = "{0} is already present".format(value) return ret diff --git a/salt/states/xmpp.py b/salt/states/xmpp.py index 1c23d7f3c40..b67c31abbe7 100644 --- a/salt/states/xmpp.py +++ b/salt/states/xmpp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Sending Messages over XMPP ========================== @@ -15,21 +15,23 @@ protocol - name: 'This is a server warning message' - profile: my-xmpp-account - recipient: admins@xmpp.example.com/salt -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def __virtual__(): - ''' + """ Only load if the XMPP module is available in __salt__ - ''' - return 'xmpp' if 'xmpp.send_msg' in __salt__ else False + """ + if "xmpp.send_msg" in __salt__: + return "xmpp" + return (False, "xmpp module could not be loaded") def send_msg(name, recipient, profile): - ''' + """ Send a message to an XMPP user .. code-block:: yaml @@ -42,32 +44,21 @@ def send_msg(name, recipient, profile): name The message to send to the XMPP user - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} - if __opts__['test']: - ret['comment'] = 'Need to send message to {0}: {1}'.format( - recipient, - name, - ) + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} + if __opts__["test"]: + ret["comment"] = "Need to send message to {0}: {1}".format(recipient, name,) return ret - __salt__['xmpp.send_msg_multi']( - message=name, - recipients=[recipient], - profile=profile, + __salt__["xmpp.send_msg_multi"]( + message=name, recipients=[recipient], profile=profile, ) - ret['result'] = True - ret['comment'] = 'Sent message to {0}: {1}'.format(recipient, name) + ret["result"] = True + ret["comment"] = "Sent message to {0}: {1}".format(recipient, name) return ret -def send_msg_multi(name, - profile, - recipients=None, - rooms=None): - ''' +def send_msg_multi(name, profile, recipients=None, rooms=None): + """ Send a message to an list of recipients or rooms .. code-block:: yaml @@ -83,34 +74,28 @@ def send_msg_multi(name, name The message to send to the XMPP user - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} if recipients is None and rooms is None: - ret['comment'] = "Recipients and rooms are empty, no need to send" + ret["comment"] = "Recipients and rooms are empty, no need to send" return ret - comment = '' + comment = "" if recipients: - comment += ' users {0}'.format(recipients) + comment += " users {0}".format(recipients) if rooms: - comment += ' rooms {0}'.format(rooms) - comment += ', message: {0}'.format(name) + comment += " rooms {0}".format(rooms) + comment += ", message: {0}".format(name) - if __opts__['test']: - ret['comment'] = 'Need to send' + comment + if __opts__["test"]: + ret["comment"] = "Need to send" + comment return ret - __salt__['xmpp.send_msg_multi']( - message=name, - recipients=recipients, - rooms=rooms, - profile=profile, + __salt__["xmpp.send_msg_multi"]( + message=name, recipients=recipients, rooms=rooms, profile=profile, ) - ret['result'] = True - ret['comment'] = 'Sent message to' + comment + ret["result"] = True + ret["comment"] = "Sent message to" + comment return ret diff --git a/salt/states/zabbix_action.py b/salt/states/zabbix_action.py index 9d9dc091b17..e17340ff44a 100644 --- a/salt/states/zabbix_action.py +++ b/salt/states/zabbix_action.py @@ -1,19 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" .. versionadded:: 2017.7 Management of Zabbix Action object over Zabbix API. :codeauthor: Jakub Sliva <jakub.sliva@ultimum.io> -''' -from __future__ import absolute_import -from __future__ import unicode_literals -import logging +""" +from __future__ import absolute_import, unicode_literals + import json +import logging try: from salt.ext import six from salt.exceptions import SaltException + IMPORTS_OK = True except ImportError: IMPORTS_OK = False @@ -23,17 +24,17 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only make these states available if Zabbix module and run_query function is available and all 3rd party modules imported. - ''' - if 'zabbix.run_query' in __salt__ and IMPORTS_OK: + """ + if "zabbix.run_query" in __salt__ and IMPORTS_OK: return True - return False, 'Import zabbix or other needed modules failed.' + return False, "Import zabbix or other needed modules failed." def present(name, params, **kwargs): - ''' + """ Creates Zabbix Action object or if differs update it according defined parameters :param name: Zabbix Action name @@ -73,86 +74,143 @@ def present(name, params, **kwargs): - groupid: query_object: hostgroup query_name: Databases - ''' - zabbix_id_mapper = __salt__['zabbix.get_zabbix_id_mapper']() + """ + zabbix_id_mapper = __salt__["zabbix.get_zabbix_id_mapper"]() - dry_run = __opts__['test'] - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + dry_run = __opts__["test"] + ret = {"name": name, "result": False, "comment": "", "changes": {}} # Create input params substituting functions with their results - params['name'] = name - params['operations'] = params['operations'] if 'operations' in params else [] - if 'filter' in params: - params['filter']['conditions'] = params['filter']['conditions'] if 'conditions' in params['filter'] else [] + params["name"] = name + params["operations"] = params["operations"] if "operations" in params else [] + if "filter" in params: + params["filter"]["conditions"] = ( + params["filter"]["conditions"] if "conditions" in params["filter"] else [] + ) - input_params = __salt__['zabbix.substitute_params'](params, **kwargs) - log.info('Zabbix Action: input params: %s', six.text_type(json.dumps(input_params, indent=4))) + input_params = __salt__["zabbix.substitute_params"](params, **kwargs) + log.info( + "Zabbix Action: input params: %s", + six.text_type(json.dumps(input_params, indent=4)), + ) - search = {'output': 'extend', - 'selectOperations': 'extend', - 'selectFilter': 'extend', - 'filter': { - 'name': name - }} + search = { + "output": "extend", + "selectOperations": "extend", + "selectFilter": "extend", + "filter": {"name": name}, + } # GET Action object if exists - action_get = __salt__['zabbix.run_query']('action.get', search, **kwargs) - log.info('Zabbix Action: action.get result: %s', six.text_type(json.dumps(action_get, indent=4))) + action_get = __salt__["zabbix.run_query"]("action.get", search, **kwargs) + log.info( + "Zabbix Action: action.get result: %s", + six.text_type(json.dumps(action_get, indent=4)), + ) - existing_obj = __salt__['zabbix.substitute_params'](action_get[0], **kwargs) \ - if action_get and len(action_get) == 1 else False + existing_obj = ( + __salt__["zabbix.substitute_params"](action_get[0], **kwargs) + if action_get and len(action_get) == 1 + else False + ) if existing_obj: - diff_params = __salt__['zabbix.compare_params'](input_params, existing_obj) - log.info('Zabbix Action: input params: {%s', six.text_type(json.dumps(input_params, indent=4))) - log.info('Zabbix Action: Object comparison result. Differences: %s', six.text_type(diff_params)) + diff_params = __salt__["zabbix.compare_params"](input_params, existing_obj) + log.info( + "Zabbix Action: input params: {%s", + six.text_type(json.dumps(input_params, indent=4)), + ) + log.info( + "Zabbix Action: Object comparison result. Differences: %s", + six.text_type(diff_params), + ) if diff_params: - diff_params[zabbix_id_mapper['action']] = existing_obj[zabbix_id_mapper['action']] + diff_params[zabbix_id_mapper["action"]] = existing_obj[ + zabbix_id_mapper["action"] + ] # diff_params['name'] = 'VMs' - BUG - https://support.zabbix.com/browse/ZBX-12078 - log.info('Zabbix Action: update params: %s', six.text_type(json.dumps(diff_params, indent=4))) + log.info( + "Zabbix Action: update params: %s", + six.text_type(json.dumps(diff_params, indent=4)), + ) if dry_run: - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" would be fixed.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" differs ' - 'in following parameters: {1}'.format(name, diff_params), - 'new': 'Zabbix Action "{0}" would correspond to definition.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" would be fixed.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" differs ' + "in following parameters: {1}".format(name, diff_params), + "new": 'Zabbix Action "{0}" would correspond to definition.'.format( + name + ), + } + } else: - action_update = __salt__['zabbix.run_query']('action.update', diff_params, **kwargs) - log.info('Zabbix Action: action.update result: %s', six.text_type(action_update)) + action_update = __salt__["zabbix.run_query"]( + "action.update", diff_params, **kwargs + ) + log.info( + "Zabbix Action: action.update result: %s", + six.text_type(action_update), + ) if action_update: - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" updated.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" differed ' - 'in following parameters: {1}'.format(name, diff_params), - 'new': 'Zabbix Action "{0}" fixed.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" updated.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" differed ' + "in following parameters: {1}".format(name, diff_params), + "new": 'Zabbix Action "{0}" fixed.'.format(name), + } + } else: - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" already exists and corresponds to a definition.'.format(name) + ret["result"] = True + ret[ + "comment" + ] = 'Zabbix Action "{0}" already exists and corresponds to a definition.'.format( + name + ) else: if dry_run: - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" would be created.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" does not exist.'.format(name), - 'new': 'Zabbix Action "{0}" would be created according definition.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" would be created.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" does not exist.'.format(name), + "new": 'Zabbix Action "{0}" would be created according definition.'.format( + name + ), + } + } else: # ACTION.CREATE - action_create = __salt__['zabbix.run_query']('action.create', input_params, **kwargs) - log.info('Zabbix Action: action.create result: %s', six.text_type(action_create)) + action_create = __salt__["zabbix.run_query"]( + "action.create", input_params, **kwargs + ) + log.info( + "Zabbix Action: action.create result: %s", six.text_type(action_create) + ) if action_create: - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" created.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" did not exist.'.format(name), - 'new': 'Zabbix Action "{0}" created according definition.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" created.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" did not exist.'.format(name), + "new": 'Zabbix Action "{0}" created according definition.'.format( + name + ), + } + } return ret def absent(name, **kwargs): - ''' + """ Makes the Zabbix Action to be absent (either does not exist or delete it). :param name: Zabbix Action name @@ -165,31 +223,43 @@ def absent(name, **kwargs): zabbix-action-absent: zabbix_action.absent: - name: Action name - ''' - dry_run = __opts__['test'] - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + dry_run = __opts__["test"] + ret = {"name": name, "result": False, "comment": "", "changes": {}} try: - object_id = __salt__['zabbix.get_object_id_by_params']('action', {'filter': {'name': name}}, **kwargs) + object_id = __salt__["zabbix.get_object_id_by_params"]( + "action", {"filter": {"name": name}}, **kwargs + ) except SaltException: object_id = False if not object_id: - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" does not exist.'.format(name) + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" does not exist.'.format(name) else: if dry_run: - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" would be deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" exists.'.format(name), - 'new': 'Zabbix Action "{0}" would be deleted.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" would be deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" exists.'.format(name), + "new": 'Zabbix Action "{0}" would be deleted.'.format(name), + } + } else: - action_delete = __salt__['zabbix.run_query']('action.delete', [object_id], **kwargs) + action_delete = __salt__["zabbix.run_query"]( + "action.delete", [object_id], **kwargs + ) if action_delete: - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" existed.'.format(name), - 'new': 'Zabbix Action "{0}" deleted.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" existed.'.format(name), + "new": 'Zabbix Action "{0}" deleted.'.format(name), + } + } return ret diff --git a/salt/states/zabbix_host.py b/salt/states/zabbix_host.py index 755e4850aa2..5fe9c36dcf9 100644 --- a/salt/states/zabbix_host.py +++ b/salt/states/zabbix_host.py @@ -1,26 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" Management of Zabbix hosts. :codeauthor: Jiri Kotlin <jiri.kotlin@ultimum.io> -''' +""" from __future__ import absolute_import, print_function, unicode_literals + from copy import deepcopy -from salt.ext import six + import salt.utils.json +from salt.ext import six def __virtual__(): - ''' + """ Only make these states available if Zabbix module is available. - ''' - return 'zabbix.host_create' in __salt__ + """ + if "zabbix.host_create" in __salt__: + return True + return (False, "zabbix module could not be loaded") def present(host, groups, interfaces, **kwargs): - ''' + """ Ensures that the host exists, eventually creates new host. NOTE: please use argument visible_name instead of name to not mess with name from salt sls. This function accepts all standard host properties: keyword argument names differ depending on your zabbix version, see: @@ -67,40 +71,42 @@ def present(host, groups, interfaces, **kwargs): - asset_tag: jlm3937 - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': host, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": host, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_host_created = 'Host {0} created.'.format(host) - comment_host_updated = 'Host {0} updated.'.format(host) - comment_host_notcreated = 'Unable to create host: {0}. '.format(host) - comment_host_exists = 'Host {0} already exists.'.format(host) - changes_host_created = {host: {'old': 'Host {0} does not exist.'.format(host), - 'new': 'Host {0} created.'.format(host), - } - } + comment_host_created = "Host {0} created.".format(host) + comment_host_updated = "Host {0} updated.".format(host) + comment_host_notcreated = "Unable to create host: {0}. ".format(host) + comment_host_exists = "Host {0} already exists.".format(host) + changes_host_created = { + host: { + "old": "Host {0} does not exist.".format(host), + "new": "Host {0} created.".format(host), + } + } def _interface_format(interfaces_data): - ''' + """ Formats interfaces from SLS file into valid JSON usable for zabbix API. Completes JSON with default values. :param interfaces_data: list of interfaces data from SLS file - ''' + """ if not interfaces_data: return list() - interface_attrs = ('ip', 'dns', 'main', 'type', 'useip', 'port') + interface_attrs = ("ip", "dns", "main", "type", "useip", "port") interfaces_json = salt.utils.json.loads(salt.utils.json.dumps(interfaces_data)) interfaces_dict = dict() @@ -114,26 +120,46 @@ def present(host, groups, interfaces, **kwargs): interfaces_dict[intf_name][key] = value interfaces_list = list() - interface_ports = {'agent': ['1', '10050'], 'snmp': ['2', '161'], 'ipmi': ['3', '623'], - 'jmx': ['4', '12345']} + interface_ports = { + "agent": ["1", "10050"], + "snmp": ["2", "161"], + "ipmi": ["3", "623"], + "jmx": ["4", "12345"], + } for key, value in interfaces_dict.items(): # Load interface values or default values - interface_type = interface_ports[value['type'].lower()][0] - main = '1' if six.text_type(value.get('main', 'true')).lower() == 'true' else '0' - useip = '1' if six.text_type(value.get('useip', 'true')).lower() == 'true' else '0' - interface_ip = value.get('ip', '') - dns = value.get('dns', key) - port = six.text_type(value.get('port', interface_ports[value['type'].lower()][1])) + interface_type = interface_ports[value["type"].lower()][0] + main = ( + "1" + if six.text_type(value.get("main", "true")).lower() == "true" + else "0" + ) + useip = ( + "1" + if six.text_type(value.get("useip", "true")).lower() == "true" + else "0" + ) + interface_ip = value.get("ip", "") + dns = value.get("dns", key) + port = six.text_type( + value.get("port", interface_ports[value["type"].lower()][1]) + ) - interfaces_list.append({'type': interface_type, - 'main': main, - 'useip': useip, - 'ip': interface_ip, - 'dns': dns, - 'port': port}) + interfaces_list.append( + { + "type": interface_type, + "main": main, + "useip": useip, + "ip": interface_ip, + "dns": dns, + "port": port, + } + ) - interfaces_list_sorted = sorted(interfaces_list, key=lambda k: k['main'], reverse=True) + interfaces_list_sorted = sorted( + interfaces_list, key=lambda k: k["main"], reverse=True + ) return interfaces_list_sorted @@ -143,11 +169,11 @@ def present(host, groups, interfaces, **kwargs): groupids = [] for group in groups: if isinstance(group, six.string_types): - groupid = __salt__['zabbix.hostgroup_get'](name=group, **connection_args) + groupid = __salt__["zabbix.hostgroup_get"](name=group, **connection_args) try: - groupids.append(int(groupid[0]['groupid'])) + groupids.append(int(groupid[0]["groupid"])) except TypeError: - ret['comment'] = 'Invalid group {0}'.format(group) + ret["comment"] = "Invalid group {0}".format(group) return ret else: groupids.append(group) @@ -155,32 +181,41 @@ def present(host, groups, interfaces, **kwargs): # Get and validate proxyid proxy_hostid = "0" - if 'proxy_host' in kwargs: + if "proxy_host" in kwargs: # Test if proxy_host given as name - if isinstance(kwargs['proxy_host'], six.string_types): + if isinstance(kwargs["proxy_host"], six.string_types): try: - proxy_hostid = __salt__['zabbix.run_query']('proxy.get', {"output": "proxyid", - "selectInterface": "extend", - "filter": {"host": "{0}".format(kwargs['proxy_host'])}}, - **connection_args)[0]['proxyid'] + proxy_hostid = __salt__["zabbix.run_query"]( + "proxy.get", + { + "output": "proxyid", + "selectInterface": "extend", + "filter": {"host": "{0}".format(kwargs["proxy_host"])}, + }, + **connection_args + )[0]["proxyid"] except TypeError: - ret['comment'] = 'Invalid proxy_host {0}'.format(kwargs['proxy_host']) + ret["comment"] = "Invalid proxy_host {0}".format(kwargs["proxy_host"]) return ret # Otherwise lookup proxy_host as proxyid else: try: - proxy_hostid = __salt__['zabbix.run_query']('proxy.get', {"proxyids": - "{0}".format(kwargs['proxy_host']), - "output": "proxyid"}, - **connection_args)[0]['proxyid'] + proxy_hostid = __salt__["zabbix.run_query"]( + "proxy.get", + { + "proxyids": "{0}".format(kwargs["proxy_host"]), + "output": "proxyid", + }, + **connection_args + )[0]["proxyid"] except TypeError: - ret['comment'] = 'Invalid proxy_host {0}'.format(kwargs['proxy_host']) + ret["comment"] = "Invalid proxy_host {0}".format(kwargs["proxy_host"]) return ret - if 'inventory' not in kwargs: + if "inventory" not in kwargs: inventory = {} else: - inventory = kwargs['inventory'] + inventory = kwargs["inventory"] if inventory is None: inventory = {} # Create dict of requested inventory items @@ -189,54 +224,59 @@ def present(host, groups, interfaces, **kwargs): for k, v in inv_item.items(): new_inventory[k] = str(v) - host_exists = __salt__['zabbix.host_exists'](host, **connection_args) + host_exists = __salt__["zabbix.host_exists"](host, **connection_args) if host_exists: - host = __salt__['zabbix.host_get'](host=host, **connection_args)[0] - hostid = host['hostid'] + host = __salt__["zabbix.host_get"](host=host, **connection_args)[0] + hostid = host["hostid"] update_proxy = False update_hostgroups = False update_interfaces = False update_inventory = False - cur_proxy_hostid = host['proxy_hostid'] + cur_proxy_hostid = host["proxy_hostid"] if proxy_hostid != cur_proxy_hostid: update_proxy = True - hostgroups = __salt__['zabbix.hostgroup_get'](hostids=hostid, **connection_args) + hostgroups = __salt__["zabbix.hostgroup_get"](hostids=hostid, **connection_args) cur_hostgroups = list() for hostgroup in hostgroups: - cur_hostgroups.append(int(hostgroup['groupid'])) + cur_hostgroups.append(int(hostgroup["groupid"])) if set(groups) != set(cur_hostgroups): update_hostgroups = True - hostinterfaces = __salt__['zabbix.hostinterface_get'](hostids=hostid, **connection_args) + hostinterfaces = __salt__["zabbix.hostinterface_get"]( + hostids=hostid, **connection_args + ) if hostinterfaces: - hostinterfaces = sorted(hostinterfaces, key=lambda k: k['main']) + hostinterfaces = sorted(hostinterfaces, key=lambda k: k["main"]) hostinterfaces_copy = deepcopy(hostinterfaces) for hostintf in hostinterfaces_copy: - hostintf.pop('interfaceid') - hostintf.pop('bulk') - hostintf.pop('hostid') - interface_diff = [x for x in interfaces_formated if x not in hostinterfaces_copy] + \ - [y for y in hostinterfaces_copy if y not in interfaces_formated] + hostintf.pop("interfaceid") + hostintf.pop("bulk") + hostintf.pop("hostid") + interface_diff = [ + x for x in interfaces_formated if x not in hostinterfaces_copy + ] + [y for y in hostinterfaces_copy if y not in interfaces_formated] if interface_diff: update_interfaces = True elif not hostinterfaces and interfaces: update_interfaces = True - cur_inventory = __salt__['zabbix.host_inventory_get'](hostids=hostid, **connection_args) + cur_inventory = __salt__["zabbix.host_inventory_get"]( + hostids=hostid, **connection_args + ) if cur_inventory: # Remove blank inventory items cur_inventory = {k: v for k, v in cur_inventory.items() if v} # Remove persistent inventory keys for comparison - cur_inventory.pop('hostid', None) - cur_inventory.pop('inventory_mode', None) + cur_inventory.pop("hostid", None) + cur_inventory.pop("inventory_mode", None) if new_inventory and not cur_inventory: update_inventory = True @@ -244,94 +284,112 @@ def present(host, groups, interfaces, **kwargs): update_inventory = True # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if host_exists: - if update_hostgroups or update_interfaces or update_proxy or update_inventory: - ret['result'] = None - ret['comment'] = comment_host_updated + if ( + update_hostgroups + or update_interfaces + or update_proxy + or update_inventory + ): + ret["result"] = None + ret["comment"] = comment_host_updated else: - ret['result'] = True - ret['comment'] = comment_host_exists + ret["result"] = True + ret["comment"] = comment_host_exists else: - ret['result'] = None - ret['comment'] = comment_host_created - ret['changes'] = changes_host_created + ret["result"] = None + ret["comment"] = comment_host_created + ret["changes"] = changes_host_created return ret error = [] if host_exists: - ret['result'] = True + ret["result"] = True if update_hostgroups or update_interfaces or update_proxy or update_inventory: if update_inventory: # combine connection_args, inventory, and clear_old sum_kwargs = dict(new_inventory) sum_kwargs.update(connection_args) - sum_kwargs['clear_old'] = True + sum_kwargs["clear_old"] = True - hostupdate = __salt__['zabbix.host_inventory_set'](hostid, **sum_kwargs) - ret['changes']['inventory'] = str(new_inventory) - if 'error' in hostupdate: - error.append(hostupdate['error']) + hostupdate = __salt__["zabbix.host_inventory_set"](hostid, **sum_kwargs) + ret["changes"]["inventory"] = str(new_inventory) + if "error" in hostupdate: + error.append(hostupdate["error"]) if update_proxy: - hostupdate = __salt__['zabbix.host_update'](hostid, proxy_hostid=proxy_hostid, **connection_args) - ret['changes']['proxy_hostid'] = six.text_type(proxy_hostid) - if 'error' in hostupdate: - error.append(hostupdate['error']) + hostupdate = __salt__["zabbix.host_update"]( + hostid, proxy_hostid=proxy_hostid, **connection_args + ) + ret["changes"]["proxy_hostid"] = six.text_type(proxy_hostid) + if "error" in hostupdate: + error.append(hostupdate["error"]) if update_hostgroups: - hostupdate = __salt__['zabbix.host_update'](hostid, groups=groups, **connection_args) - ret['changes']['groups'] = six.text_type(groups) - if 'error' in hostupdate: - error.append(hostupdate['error']) + hostupdate = __salt__["zabbix.host_update"]( + hostid, groups=groups, **connection_args + ) + ret["changes"]["groups"] = six.text_type(groups) + if "error" in hostupdate: + error.append(hostupdate["error"]) if update_interfaces: if hostinterfaces: for interface in hostinterfaces: - __salt__['zabbix.hostinterface_delete'](interfaceids=interface['interfaceid'], - **connection_args) + __salt__["zabbix.hostinterface_delete"]( + interfaceids=interface["interfaceid"], **connection_args + ) - hostid = __salt__['zabbix.host_get'](name=host, **connection_args)[0]['hostid'] + hostid = __salt__["zabbix.host_get"](name=host, **connection_args)[0][ + "hostid" + ] for interface in interfaces_formated: - updatedint = __salt__['zabbix.hostinterface_create'](hostid=hostid, - ip=interface['ip'], - dns=interface['dns'], - main=interface['main'], - type=interface['type'], - useip=interface['useip'], - port=interface['port'], - **connection_args) + updatedint = __salt__["zabbix.hostinterface_create"]( + hostid=hostid, + ip=interface["ip"], + dns=interface["dns"], + main=interface["main"], + type=interface["type"], + useip=interface["useip"], + port=interface["port"], + **connection_args + ) - if 'error' in updatedint: - error.append(updatedint['error']) + if "error" in updatedint: + error.append(updatedint["error"]) - ret['changes']['interfaces'] = six.text_type(interfaces_formated) + ret["changes"]["interfaces"] = six.text_type(interfaces_formated) - ret['comment'] = comment_host_updated + ret["comment"] = comment_host_updated else: - ret['comment'] = comment_host_exists + ret["comment"] = comment_host_exists else: - host_create = __salt__['zabbix.host_create'](host, - groups, - interfaces_formated, - proxy_hostid=proxy_hostid, - inventory=new_inventory, - **connection_args) + host_create = __salt__["zabbix.host_create"]( + host, + groups, + interfaces_formated, + proxy_hostid=proxy_hostid, + inventory=new_inventory, + **connection_args + ) - if 'error' not in host_create: - ret['result'] = True - ret['comment'] = comment_host_created - ret['changes'] = changes_host_created + if "error" not in host_create: + ret["result"] = True + ret["comment"] = comment_host_created + ret["changes"] = changes_host_created else: - ret['result'] = False - ret['comment'] = comment_host_notcreated + six.text_type(host_create['error']) + ret["result"] = False + ret["comment"] = comment_host_notcreated + six.text_type( + host_create["error"] + ) # error detected if error: - ret['changes'] = {} - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["changes"] = {} + ret["result"] = False + ret["comment"] = six.text_type(error) return ret @@ -353,61 +411,65 @@ def absent(name, **kwargs): zabbix_host.absent """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_host_deleted = 'Host {0} deleted.'.format(name) - comment_host_notdeleted = 'Unable to delete host: {0}. '.format(name) - comment_host_notexists = 'Host {0} does not exist.'.format(name) - changes_host_deleted = {name: {'old': 'Host {0} exists.'.format(name), - 'new': 'Host {0} deleted.'.format(name), - } - } + comment_host_deleted = "Host {0} deleted.".format(name) + comment_host_notdeleted = "Unable to delete host: {0}. ".format(name) + comment_host_notexists = "Host {0} does not exist.".format(name) + changes_host_deleted = { + name: { + "old": "Host {0} exists.".format(name), + "new": "Host {0} deleted.".format(name), + } + } connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - host_exists = __salt__['zabbix.host_exists'](name, **connection_args) + host_exists = __salt__["zabbix.host_exists"](name, **connection_args) # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if not host_exists: - ret['result'] = True - ret['comment'] = comment_host_notexists + ret["result"] = True + ret["comment"] = comment_host_notexists else: - ret['result'] = None - ret['comment'] = comment_host_deleted + ret["result"] = None + ret["comment"] = comment_host_deleted return ret - host_get = __salt__['zabbix.host_get'](name, **connection_args) + host_get = __salt__["zabbix.host_get"](name, **connection_args) if not host_get: - ret['result'] = True - ret['comment'] = comment_host_notexists + ret["result"] = True + ret["comment"] = comment_host_notexists else: try: - hostid = host_get[0]['hostid'] - host_delete = __salt__['zabbix.host_delete'](hostid, **connection_args) + hostid = host_get[0]["hostid"] + host_delete = __salt__["zabbix.host_delete"](hostid, **connection_args) except KeyError: host_delete = False - if host_delete and 'error' not in host_delete: - ret['result'] = True - ret['comment'] = comment_host_deleted - ret['changes'] = changes_host_deleted + if host_delete and "error" not in host_delete: + ret["result"] = True + ret["comment"] = comment_host_deleted + ret["changes"] = changes_host_deleted else: - ret['result'] = False - ret['comment'] = comment_host_notdeleted + six.text_type(host_delete['error']) + ret["result"] = False + ret["comment"] = comment_host_notdeleted + six.text_type( + host_delete["error"] + ) return ret def assign_templates(host, templates, **kwargs): - ''' + """ Ensures that templates are assigned to the host. .. versionadded:: 2017.7.0 @@ -426,57 +488,63 @@ def assign_templates(host, templates, **kwargs): - "Template OS Linux" - "Template App MySQL" - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': host, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": host, "changes": {}, "result": False, "comment": ""} # Set comments - comment_host_templates_updated = 'Templates updated.' - comment_host_templ_notupdated = 'Unable to update templates on host: {0}.'.format(host) - comment_host_templates_in_sync = 'Templates already synced.' + comment_host_templates_updated = "Templates updated." + comment_host_templ_notupdated = "Unable to update templates on host: {0}.".format( + host + ) + comment_host_templates_in_sync = "Templates already synced." update_host_templates = False curr_template_ids = list() requested_template_ids = list() - hostid = '' + hostid = "" - host_exists = __salt__['zabbix.host_exists'](host, **connection_args) + host_exists = __salt__["zabbix.host_exists"](host, **connection_args) # Fail out if host does not exist if not host_exists: - ret['result'] = False - ret['comment'] = comment_host_templ_notupdated + ret["result"] = False + ret["comment"] = comment_host_templ_notupdated return ret - host_info = __salt__['zabbix.host_get'](host=host, **connection_args)[0] - hostid = host_info['hostid'] + host_info = __salt__["zabbix.host_get"](host=host, **connection_args)[0] + hostid = host_info["hostid"] if not templates: templates = list() # Get current templateids for host - host_templates = __salt__['zabbix.host_get'](hostids=hostid, - output='[{"hostid"}]', - selectParentTemplates='["templateid"]', - **connection_args) - for template_id in host_templates[0]['parentTemplates']: - curr_template_ids.append(template_id['templateid']) + host_templates = __salt__["zabbix.host_get"]( + hostids=hostid, + output='[{"hostid"}]', + selectParentTemplates='["templateid"]', + **connection_args + ) + for template_id in host_templates[0]["parentTemplates"]: + curr_template_ids.append(template_id["templateid"]) # Get requested templateids for template in templates: try: - template_id = __salt__['zabbix.template_get'](host=template, **connection_args)[0]['templateid'] + template_id = __salt__["zabbix.template_get"]( + host=template, **connection_args + )[0]["templateid"] requested_template_ids.append(template_id) except TypeError: - ret['result'] = False - ret['comment'] = 'Unable to find template: {0}.'.format(template) + ret["result"] = False + ret["comment"] = "Unable to find template: {0}.".format(template) return ret # remove any duplications @@ -486,30 +554,36 @@ def assign_templates(host, templates, **kwargs): update_host_templates = True # Set change output - changes_host_templates_modified = {host: {'old': 'Host templates: ' + ", ".join(curr_template_ids), - 'new': 'Host templates: ' + ', '.join(requested_template_ids)}} + changes_host_templates_modified = { + host: { + "old": "Host templates: " + ", ".join(curr_template_ids), + "new": "Host templates: " + ", ".join(requested_template_ids), + } + } # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if update_host_templates: - ret['result'] = None - ret['comment'] = comment_host_templates_updated + ret["result"] = None + ret["comment"] = comment_host_templates_updated else: - ret['result'] = True - ret['comment'] = comment_host_templates_in_sync + ret["result"] = True + ret["comment"] = comment_host_templates_in_sync return ret # Attempt to perform update - ret['result'] = True + ret["result"] = True if update_host_templates: - update_output = __salt__['zabbix.host_update'](hostid, templates=(requested_template_ids), **connection_args) + update_output = __salt__["zabbix.host_update"]( + hostid, templates=(requested_template_ids), **connection_args + ) if update_output is False: - ret['result'] = False - ret['comment'] = comment_host_templ_notupdated + ret["result"] = False + ret["comment"] = comment_host_templ_notupdated return ret - ret['comment'] = comment_host_templates_updated - ret['changes'] = changes_host_templates_modified + ret["comment"] = comment_host_templates_updated + ret["changes"] = changes_host_templates_modified else: - ret['comment'] = comment_host_templates_in_sync + ret["comment"] = comment_host_templates_in_sync return ret diff --git a/salt/states/zabbix_hostgroup.py b/salt/states/zabbix_hostgroup.py index ea8e349c8c8..d7bb754f8c1 100644 --- a/salt/states/zabbix_hostgroup.py +++ b/salt/states/zabbix_hostgroup.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Management of Zabbix host groups. :codeauthor: Jiri Kotlin <jiri.kotlin@ultimum.io> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -15,14 +15,16 @@ from salt.ext import six def __virtual__(): - ''' + """ Only make these states available if Zabbix module is available. - ''' - return 'zabbix.hostgroup_create' in __salt__ + """ + if "zabbix.hostgroup_create" in __salt__: + return True + return (False, "zabbix module could not be loaded") def present(name, **kwargs): - ''' + """ Ensures that the host group exists, eventually creates new host group. .. versionadded:: 2016.3.0 @@ -39,57 +41,61 @@ def present(name, **kwargs): - name: 'My hostgroup name' - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_hostgroup_created = 'Host group {0} created.'.format(name) - comment_hostgroup_notcreated = 'Unable to create host group: {0}. '.format(name) - comment_hostgroup_exists = 'Host group {0} already exists.'.format(name) - changes_hostgroup_created = {name: {'old': 'Host group {0} does not exist.'.format(name), - 'new': 'Host group {0} created.'.format(name), - } - } + comment_hostgroup_created = "Host group {0} created.".format(name) + comment_hostgroup_notcreated = "Unable to create host group: {0}. ".format(name) + comment_hostgroup_exists = "Host group {0} already exists.".format(name) + changes_hostgroup_created = { + name: { + "old": "Host group {0} does not exist.".format(name), + "new": "Host group {0} created.".format(name), + } + } - hostgroup_exists = __salt__['zabbix.hostgroup_exists'](name, **connection_args) + hostgroup_exists = __salt__["zabbix.hostgroup_exists"](name, **connection_args) # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if hostgroup_exists: - ret['result'] = True - ret['comment'] = comment_hostgroup_exists + ret["result"] = True + ret["comment"] = comment_hostgroup_exists else: - ret['result'] = None - ret['comment'] = comment_hostgroup_created - ret['changes'] = changes_hostgroup_created + ret["result"] = None + ret["comment"] = comment_hostgroup_created + ret["changes"] = changes_hostgroup_created return ret if hostgroup_exists: - ret['result'] = True - ret['comment'] = comment_hostgroup_exists + ret["result"] = True + ret["comment"] = comment_hostgroup_exists else: - hostgroup_create = __salt__['zabbix.hostgroup_create'](name, **connection_args) + hostgroup_create = __salt__["zabbix.hostgroup_create"](name, **connection_args) - if 'error' not in hostgroup_create: - ret['result'] = True - ret['comment'] = comment_hostgroup_created - ret['changes'] = changes_hostgroup_created + if "error" not in hostgroup_create: + ret["result"] = True + ret["comment"] = comment_hostgroup_created + ret["changes"] = changes_hostgroup_created else: - ret['result'] = False - ret['comment'] = comment_hostgroup_notcreated + six.text_type(hostgroup_create['error']) + ret["result"] = False + ret["comment"] = comment_hostgroup_notcreated + six.text_type( + hostgroup_create["error"] + ) return ret def absent(name, **kwargs): - ''' + """ Ensures that the host group does not exist, eventually delete host group. .. versionadded:: 2016.3.0 @@ -105,58 +111,64 @@ def absent(name, **kwargs): zabbix_hostgroup.absent: - name: 'My hostgroup name' - ''' + """ - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_hostgroup_deleted = 'Host group {0} deleted.'.format(name) - comment_hostgroup_notdeleted = 'Unable to delete host group: {0}. '.format(name) - comment_hostgroup_notexists = 'Host group {0} does not exist.'.format(name) - changes_hostgroup_deleted = {name: {'old': 'Host group {0} exists.'.format(name), - 'new': 'Host group {0} deleted.'.format(name), - } - } + comment_hostgroup_deleted = "Host group {0} deleted.".format(name) + comment_hostgroup_notdeleted = "Unable to delete host group: {0}. ".format(name) + comment_hostgroup_notexists = "Host group {0} does not exist.".format(name) + changes_hostgroup_deleted = { + name: { + "old": "Host group {0} exists.".format(name), + "new": "Host group {0} deleted.".format(name), + } + } connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - hostgroup_exists = __salt__['zabbix.hostgroup_exists'](name, **connection_args) + hostgroup_exists = __salt__["zabbix.hostgroup_exists"](name, **connection_args) # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if not hostgroup_exists: - ret['result'] = True - ret['comment'] = comment_hostgroup_notexists + ret["result"] = True + ret["comment"] = comment_hostgroup_notexists else: - ret['result'] = None - ret['comment'] = comment_hostgroup_deleted - ret['changes'] = changes_hostgroup_deleted + ret["result"] = None + ret["comment"] = comment_hostgroup_deleted + ret["changes"] = changes_hostgroup_deleted return ret - hostgroup_get = __salt__['zabbix.hostgroup_get'](name, **connection_args) + hostgroup_get = __salt__["zabbix.hostgroup_get"](name, **connection_args) if not hostgroup_get: - ret['result'] = True - ret['comment'] = comment_hostgroup_notexists + ret["result"] = True + ret["comment"] = comment_hostgroup_notexists else: try: - groupid = hostgroup_get[0]['groupid'] - hostgroup_delete = __salt__['zabbix.hostgroup_delete'](groupid, **connection_args) + groupid = hostgroup_get[0]["groupid"] + hostgroup_delete = __salt__["zabbix.hostgroup_delete"]( + groupid, **connection_args + ) except KeyError: hostgroup_delete = False - if hostgroup_delete and 'error' not in hostgroup_delete: - ret['result'] = True - ret['comment'] = comment_hostgroup_deleted - ret['changes'] = changes_hostgroup_deleted + if hostgroup_delete and "error" not in hostgroup_delete: + ret["result"] = True + ret["comment"] = comment_hostgroup_deleted + ret["changes"] = changes_hostgroup_deleted else: - ret['result'] = False - ret['comment'] = comment_hostgroup_notdeleted + six.text_type(hostgroup_delete['error']) + ret["result"] = False + ret["comment"] = comment_hostgroup_notdeleted + six.text_type( + hostgroup_delete["error"] + ) return ret diff --git a/salt/states/zabbix_mediatype.py b/salt/states/zabbix_mediatype.py index 70b20e99077..f611021c7aa 100644 --- a/salt/states/zabbix_mediatype.py +++ b/salt/states/zabbix_mediatype.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Management of Zabbix mediatypes. :codeauthor: Raymond Kuiper <qix@the-wired.net> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -14,14 +14,16 @@ from salt.ext import six def __virtual__(): - ''' + """ Only make these states available if Zabbix module is available. - ''' - return 'zabbix.mediatype_create' in __salt__ + """ + if "zabbix.mediatype_create" in __salt__: + return True + return (False, "zabbix module could not be loaded") def present(name, mediatype, **kwargs): - ''' + """ Creates new mediatype. NOTE: This function accepts all standard mediatype properties: keyword argument names differ depending on your zabbix version, see: @@ -42,39 +44,41 @@ def present(name, mediatype, **kwargs): - smtp_hello: zabbix.example.com - smtp_email: zabbix@example.com - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_mediatype_created = 'Mediatype {0} created.'.format(name) - comment_mediatype_updated = 'Mediatype {0} updated.'.format(name) - comment_mediatype_notcreated = 'Unable to create mediatype: {0}. '.format(name) - comment_mediatype_exists = 'Mediatype {0} already exists.'.format(name) - changes_mediatype_created = {name: {'old': 'Mediatype {0} does not exist.'.format(name), - 'new': 'Mediatype {0} created.'.format(name), - } - } + comment_mediatype_created = "Mediatype {0} created.".format(name) + comment_mediatype_updated = "Mediatype {0} updated.".format(name) + comment_mediatype_notcreated = "Unable to create mediatype: {0}. ".format(name) + comment_mediatype_exists = "Mediatype {0} already exists.".format(name) + changes_mediatype_created = { + name: { + "old": "Mediatype {0} does not exist.".format(name), + "new": "Mediatype {0} created.".format(name), + } + } # Zabbix API expects script parameters as a string of arguments seperated by newline characters - if 'exec_params' in kwargs: - if isinstance(kwargs['exec_params'], list): - kwargs['exec_params'] = '\n'.join(kwargs['exec_params'])+'\n' + if "exec_params" in kwargs: + if isinstance(kwargs["exec_params"], list): + kwargs["exec_params"] = "\n".join(kwargs["exec_params"]) + "\n" else: - kwargs['exec_params'] = six.text_type(kwargs['exec_params'])+'\n' + kwargs["exec_params"] = six.text_type(kwargs["exec_params"]) + "\n" - mediatype_exists = __salt__['zabbix.mediatype_get'](name, **connection_args) + mediatype_exists = __salt__["zabbix.mediatype_get"](name, **connection_args) if mediatype_exists: mediatypeobj = mediatype_exists[0] - mediatypeid = int(mediatypeobj['mediatypeid']) + mediatypeid = int(mediatypeobj["mediatypeid"]) update_email = False update_email_port = False update_email_security = False @@ -88,239 +92,301 @@ def present(name, mediatype, **kwargs): update_eztext = False update_status = False - if int(mediatype) == 0 and 'smtp_server' in kwargs and 'smtp_helo' in kwargs and 'smtp_email' in kwargs: - if (int(mediatype) != int(mediatypeobj['type']) or - kwargs['smtp_server'] != mediatypeobj['smtp_server'] or - kwargs['smtp_email'] != mediatypeobj['smtp_email'] or - kwargs['smtp_helo'] != mediatypeobj['smtp_helo']): + if ( + int(mediatype) == 0 + and "smtp_server" in kwargs + and "smtp_helo" in kwargs + and "smtp_email" in kwargs + ): + if ( + int(mediatype) != int(mediatypeobj["type"]) + or kwargs["smtp_server"] != mediatypeobj["smtp_server"] + or kwargs["smtp_email"] != mediatypeobj["smtp_email"] + or kwargs["smtp_helo"] != mediatypeobj["smtp_helo"] + ): update_email = True - if int(mediatype) == 0 and 'smtp_port' in kwargs: - if int(kwargs['smtp_port']) != int(mediatypeobj['smtp_port']): + if int(mediatype) == 0 and "smtp_port" in kwargs: + if int(kwargs["smtp_port"]) != int(mediatypeobj["smtp_port"]): update_email_port = True - if int(mediatype) == 0 and 'smtp_security' in kwargs: - if int(kwargs['smtp_security']) != int(mediatypeobj['smtp_security']): + if int(mediatype) == 0 and "smtp_security" in kwargs: + if int(kwargs["smtp_security"]) != int(mediatypeobj["smtp_security"]): update_email_security = True - if int(mediatype) == 0 and 'smtp_verify_peer' in kwargs: - if int(kwargs['smtp_verify_peer']) != int(mediatypeobj['smtp_verify_peer']): + if int(mediatype) == 0 and "smtp_verify_peer" in kwargs: + if int(kwargs["smtp_verify_peer"]) != int(mediatypeobj["smtp_verify_peer"]): update_email_verify_peer = True - if int(mediatype) == 0 and 'smtp_verify_host' in kwargs: - if int(kwargs['smtp_verify_host']) != int(mediatypeobj['smtp_verify_host']): + if int(mediatype) == 0 and "smtp_verify_host" in kwargs: + if int(kwargs["smtp_verify_host"]) != int(mediatypeobj["smtp_verify_host"]): update_email_verify_host = True - if int(mediatype) == 0 and 'smtp_authentication' in kwargs and 'username' in kwargs and 'passwd' in kwargs: - if (int(kwargs['smtp_authentication']) != int(mediatypeobj['smtp_authentication']) or - kwargs['username'] != mediatypeobj['username'] or - kwargs['passwd'] != mediatypeobj['passwd']): + if ( + int(mediatype) == 0 + and "smtp_authentication" in kwargs + and "username" in kwargs + and "passwd" in kwargs + ): + if ( + int(kwargs["smtp_authentication"]) + != int(mediatypeobj["smtp_authentication"]) + or kwargs["username"] != mediatypeobj["username"] + or kwargs["passwd"] != mediatypeobj["passwd"] + ): update_email_auth = True - if int(mediatype) == 1 and 'exec_path' in kwargs: - if (int(mediatype) != int(mediatypeobj['type']) or - kwargs['exec_path'] != mediatypeobj['exec_path']): + if int(mediatype) == 1 and "exec_path" in kwargs: + if ( + int(mediatype) != int(mediatypeobj["type"]) + or kwargs["exec_path"] != mediatypeobj["exec_path"] + ): update_script = True - if int(mediatype) == 1 and 'exec_params' in kwargs: - if kwargs['exec_params'] != mediatypeobj['exec_params']: + if int(mediatype) == 1 and "exec_params" in kwargs: + if kwargs["exec_params"] != mediatypeobj["exec_params"]: update_script_params = True - if int(mediatype) == 2 and 'gsm_modem' in kwargs: - if (int(mediatype) != int(mediatypeobj['type']) or - kwargs['gsm_modem'] != mediatypeobj['gsm_modem']): + if int(mediatype) == 2 and "gsm_modem" in kwargs: + if ( + int(mediatype) != int(mediatypeobj["type"]) + or kwargs["gsm_modem"] != mediatypeobj["gsm_modem"] + ): update_sms = True - if int(mediatype) == 3 and 'username' in kwargs and 'passwd' in kwargs: - if (int(mediatype) != int(mediatypeobj['type']) or - kwargs['username'] != mediatypeobj['username'] or - kwargs['passwd'] != mediatypeobj['passwd']): + if int(mediatype) == 3 and "username" in kwargs and "passwd" in kwargs: + if ( + int(mediatype) != int(mediatypeobj["type"]) + or kwargs["username"] != mediatypeobj["username"] + or kwargs["passwd"] != mediatypeobj["passwd"] + ): update_jabber = True - if int(mediatype) == 100 and 'username' in kwargs and 'passwd' in kwargs and 'exec_path' in kwargs: - if (int(mediatype) != int(mediatypeobj['type']) or - kwargs['username'] != mediatypeobj['username'] or - kwargs['passwd'] != mediatypeobj['passwd'] or - kwargs['exec_path'] != mediatypeobj['exec_path']): + if ( + int(mediatype) == 100 + and "username" in kwargs + and "passwd" in kwargs + and "exec_path" in kwargs + ): + if ( + int(mediatype) != int(mediatypeobj["type"]) + or kwargs["username"] != mediatypeobj["username"] + or kwargs["passwd"] != mediatypeobj["passwd"] + or kwargs["exec_path"] != mediatypeobj["exec_path"] + ): update_eztext = True - if 'status' in kwargs: - if int(kwargs['status']) != int(mediatypeobj['status']): + if "status" in kwargs: + if int(kwargs["status"]) != int(mediatypeobj["status"]): update_status = True # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if mediatype_exists: if update_status: - ret['result'] = None - ret['comment'] = comment_mediatype_updated + ret["result"] = None + ret["comment"] = comment_mediatype_updated else: - ret['result'] = True - ret['comment'] = comment_mediatype_exists + ret["result"] = True + ret["comment"] = comment_mediatype_exists else: - ret['result'] = None - ret['comment'] = comment_mediatype_created + ret["result"] = None + ret["comment"] = comment_mediatype_created return ret error = [] if mediatype_exists: - if (update_email or update_email_port or update_email_security or - update_email_verify_peer or update_email_verify_host or update_email_auth or - update_script or update_script_params or update_sms or - update_jabber or update_eztext or update_status): - ret['result'] = True - ret['comment'] = comment_mediatype_updated + if ( + update_email + or update_email_port + or update_email_security + or update_email_verify_peer + or update_email_verify_host + or update_email_auth + or update_script + or update_script_params + or update_sms + or update_jabber + or update_eztext + or update_status + ): + ret["result"] = True + ret["comment"] = comment_mediatype_updated if update_email: - updated_email = __salt__['zabbix.mediatype_update'](mediatypeid, - type=mediatype, - smtp_server=kwargs['smtp_server'], - smtp_helo=kwargs['smtp_helo'], - smtp_email=kwargs['smtp_email'], - **connection_args) - if 'error' in updated_email: - error.append(updated_email['error']) + updated_email = __salt__["zabbix.mediatype_update"]( + mediatypeid, + type=mediatype, + smtp_server=kwargs["smtp_server"], + smtp_helo=kwargs["smtp_helo"], + smtp_email=kwargs["smtp_email"], + **connection_args + ) + if "error" in updated_email: + error.append(updated_email["error"]) else: - ret['changes']['smtp_server'] = kwargs['smtp_server'] - ret['changes']['smtp_helo'] = kwargs['smtp_helo'] - ret['changes']['smtp_email'] = kwargs['smtp_email'] + ret["changes"]["smtp_server"] = kwargs["smtp_server"] + ret["changes"]["smtp_helo"] = kwargs["smtp_helo"] + ret["changes"]["smtp_email"] = kwargs["smtp_email"] if update_email_port: - updated_email_port = __salt__['zabbix.mediatype_update'](mediatypeid, - smtp_port=kwargs['smtp_port'], - **connection_args) - if 'error' in updated_email_port: - error.append(updated_email_port['error']) + updated_email_port = __salt__["zabbix.mediatype_update"]( + mediatypeid, smtp_port=kwargs["smtp_port"], **connection_args + ) + if "error" in updated_email_port: + error.append(updated_email_port["error"]) else: - ret['changes']['smtp_port'] = kwargs['smtp_port'] + ret["changes"]["smtp_port"] = kwargs["smtp_port"] if update_email_security: - updated_email_security = __salt__['zabbix.mediatype_update'](mediatypeid, - smtp_security=kwargs['smtp_security'], - **connection_args) - if 'error' in updated_email_security: - error.append(updated_email_security['error']) + updated_email_security = __salt__["zabbix.mediatype_update"]( + mediatypeid, + smtp_security=kwargs["smtp_security"], + **connection_args + ) + if "error" in updated_email_security: + error.append(updated_email_security["error"]) else: - ret['changes']['smtp_security'] = kwargs['smtp_security'] + ret["changes"]["smtp_security"] = kwargs["smtp_security"] if update_email_verify_peer: - updated_email_verify_peer = __salt__['zabbix.mediatype_update'](mediatypeid, - smtp_verify_peer=kwargs['smtp_verify_peer'], - **connection_args) - if 'error' in updated_email_verify_peer: - error.append(updated_email_verify_peer['error']) + updated_email_verify_peer = __salt__["zabbix.mediatype_update"]( + mediatypeid, + smtp_verify_peer=kwargs["smtp_verify_peer"], + **connection_args + ) + if "error" in updated_email_verify_peer: + error.append(updated_email_verify_peer["error"]) else: - ret['changes']['smtp_verify_peer'] = kwargs['smtp_verify_peer'] + ret["changes"]["smtp_verify_peer"] = kwargs["smtp_verify_peer"] if update_email_verify_host: - updated_email_verify_host = __salt__['zabbix.mediatype_update'](mediatypeid, - smtp_verify_host=kwargs['smtp_verify_host'], - **connection_args) - if 'error' in updated_email_verify_host: - error.append(updated_email_verify_host['error']) + updated_email_verify_host = __salt__["zabbix.mediatype_update"]( + mediatypeid, + smtp_verify_host=kwargs["smtp_verify_host"], + **connection_args + ) + if "error" in updated_email_verify_host: + error.append(updated_email_verify_host["error"]) else: - ret['changes']['smtp_verify_host'] = kwargs['smtp_verify_host'] + ret["changes"]["smtp_verify_host"] = kwargs["smtp_verify_host"] if update_email_auth: - updated_email_auth = __salt__['zabbix.mediatype_update'](mediatypeid, - username=kwargs['username'], - passwd=kwargs['passwd'], - smtp_authentication=kwargs['smtp_authentication'], - **connection_args) - if 'error' in updated_email_auth: - error.append(updated_email_auth['error']) + updated_email_auth = __salt__["zabbix.mediatype_update"]( + mediatypeid, + username=kwargs["username"], + passwd=kwargs["passwd"], + smtp_authentication=kwargs["smtp_authentication"], + **connection_args + ) + if "error" in updated_email_auth: + error.append(updated_email_auth["error"]) else: - ret['changes']['smtp_authentication'] = kwargs['smtp_authentication'] - ret['changes']['username'] = kwargs['username'] + ret["changes"]["smtp_authentication"] = kwargs[ + "smtp_authentication" + ] + ret["changes"]["username"] = kwargs["username"] if update_script: - updated_script = __salt__['zabbix.mediatype_update'](mediatypeid, - type=mediatype, - exec_path=kwargs['exec_path'], - **connection_args) - if 'error' in updated_script: - error.append(updated_script['error']) + updated_script = __salt__["zabbix.mediatype_update"]( + mediatypeid, + type=mediatype, + exec_path=kwargs["exec_path"], + **connection_args + ) + if "error" in updated_script: + error.append(updated_script["error"]) else: - ret['changes']['exec_path'] = kwargs['exec_path'] + ret["changes"]["exec_path"] = kwargs["exec_path"] if update_script_params: - updated_script_params = __salt__['zabbix.mediatype_update'](mediatypeid, - exec_params=kwargs['exec_params'], - **connection_args) - if 'error' in updated_script_params: - error.append(updated_script['error']) + updated_script_params = __salt__["zabbix.mediatype_update"]( + mediatypeid, exec_params=kwargs["exec_params"], **connection_args + ) + if "error" in updated_script_params: + error.append(updated_script["error"]) else: - ret['changes']['exec_params'] = kwargs['exec_params'] + ret["changes"]["exec_params"] = kwargs["exec_params"] if update_sms: - updated_sms = __salt__['zabbix.mediatype_update'](mediatypeid, - type=mediatype, - gsm_modem=kwargs['gsm_modem'], - **connection_args) - if 'error' in updated_sms: - error.append(updated_sms['error']) + updated_sms = __salt__["zabbix.mediatype_update"]( + mediatypeid, + type=mediatype, + gsm_modem=kwargs["gsm_modem"], + **connection_args + ) + if "error" in updated_sms: + error.append(updated_sms["error"]) else: - ret['changes']['gsm_modem'] = kwargs['gsm_modem'] + ret["changes"]["gsm_modem"] = kwargs["gsm_modem"] if update_jabber: - updated_jabber = __salt__['zabbix.mediatype_update'](mediatypeid, - type=mediatype, - username=kwargs['username'], - passwd=kwargs['passwd'], - **connection_args) - if 'error' in updated_jabber: - error.append(updated_jabber['error']) + updated_jabber = __salt__["zabbix.mediatype_update"]( + mediatypeid, + type=mediatype, + username=kwargs["username"], + passwd=kwargs["passwd"], + **connection_args + ) + if "error" in updated_jabber: + error.append(updated_jabber["error"]) else: - ret['changes']['username'] = kwargs['username'] + ret["changes"]["username"] = kwargs["username"] if update_eztext: - updated_eztext = __salt__['zabbix.mediatype_update'](mediatypeid, - type=mediatype, - username=kwargs['username'], - passwd=kwargs['passwd'], - exec_path=kwargs['exec_path'], - **connection_args) - if 'error' in updated_eztext: - error.append(updated_eztext['error']) + updated_eztext = __salt__["zabbix.mediatype_update"]( + mediatypeid, + type=mediatype, + username=kwargs["username"], + passwd=kwargs["passwd"], + exec_path=kwargs["exec_path"], + **connection_args + ) + if "error" in updated_eztext: + error.append(updated_eztext["error"]) else: - ret['changes']['username'] = kwargs['username'] - ret['changes']['exec_path'] = kwargs['exec_path'] + ret["changes"]["username"] = kwargs["username"] + ret["changes"]["exec_path"] = kwargs["exec_path"] if update_status: - updated_status = __salt__['zabbix.mediatype_update'](mediatypeid, - status=kwargs['status'], - **connection_args) - if 'error' in updated_status: - error.append(updated_status['error']) + updated_status = __salt__["zabbix.mediatype_update"]( + mediatypeid, status=kwargs["status"], **connection_args + ) + if "error" in updated_status: + error.append(updated_status["error"]) else: - ret['changes']['status'] = kwargs['status'] + ret["changes"]["status"] = kwargs["status"] else: - ret['result'] = True - ret['comment'] = comment_mediatype_exists + ret["result"] = True + ret["comment"] = comment_mediatype_exists else: - mediatype_create = __salt__['zabbix.mediatype_create'](name, mediatype, **kwargs) + mediatype_create = __salt__["zabbix.mediatype_create"]( + name, mediatype, **kwargs + ) - if 'error' not in mediatype_create: - ret['result'] = True - ret['comment'] = comment_mediatype_created - ret['changes'] = changes_mediatype_created + if "error" not in mediatype_create: + ret["result"] = True + ret["comment"] = comment_mediatype_created + ret["changes"] = changes_mediatype_created else: - ret['result'] = False - ret['comment'] = comment_mediatype_notcreated + six.text_type(mediatype_create['error']) + ret["result"] = False + ret["comment"] = comment_mediatype_notcreated + six.text_type( + mediatype_create["error"] + ) # error detected if error: - ret['changes'] = {} - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["changes"] = {} + ret["result"] = False + ret["comment"] = six.text_type(error) return ret def absent(name, **kwargs): - ''' + """ Ensures that the mediatype does not exist, eventually deletes the mediatype. :param name: name of the mediatype @@ -333,54 +399,60 @@ def absent(name, **kwargs): delete_mediatype: zabbix_mediatype.absent: - name: 'Email' - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_mediatype_deleted = 'Mediatype {0} deleted.'.format(name) - comment_mediatype_notdeleted = 'Unable to delete mediatype: {0}. '.format(name) - comment_mediatype_notexists = 'Mediatype {0} does not exist.'.format(name) - changes_mediatype_deleted = {name: {'old': 'Mediatype {0} exists.'.format(name), - 'new': 'Mediatype {0} deleted.'.format(name), - } - } + comment_mediatype_deleted = "Mediatype {0} deleted.".format(name) + comment_mediatype_notdeleted = "Unable to delete mediatype: {0}. ".format(name) + comment_mediatype_notexists = "Mediatype {0} does not exist.".format(name) + changes_mediatype_deleted = { + name: { + "old": "Mediatype {0} exists.".format(name), + "new": "Mediatype {0} deleted.".format(name), + } + } - mediatype_exists = __salt__['zabbix.mediatype_get'](name, **connection_args) + mediatype_exists = __salt__["zabbix.mediatype_get"](name, **connection_args) # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if not mediatype_exists: - ret['result'] = True - ret['comment'] = comment_mediatype_notexists + ret["result"] = True + ret["comment"] = comment_mediatype_notexists else: - ret['result'] = None - ret['comment'] = comment_mediatype_deleted + ret["result"] = None + ret["comment"] = comment_mediatype_deleted return ret if not mediatype_exists: - ret['result'] = True - ret['comment'] = comment_mediatype_notexists + ret["result"] = True + ret["comment"] = comment_mediatype_notexists else: try: - mediatypeid = mediatype_exists[0]['mediatypeid'] - mediatype_delete = __salt__['zabbix.mediatype_delete'](mediatypeid, **connection_args) + mediatypeid = mediatype_exists[0]["mediatypeid"] + mediatype_delete = __salt__["zabbix.mediatype_delete"]( + mediatypeid, **connection_args + ) except KeyError: mediatype_delete = False - if mediatype_delete and 'error' not in mediatype_delete: - ret['result'] = True - ret['comment'] = comment_mediatype_deleted - ret['changes'] = changes_mediatype_deleted + if mediatype_delete and "error" not in mediatype_delete: + ret["result"] = True + ret["comment"] = comment_mediatype_deleted + ret["changes"] = changes_mediatype_deleted else: - ret['result'] = False - ret['comment'] = comment_mediatype_notdeleted + six.text_type(mediatype_delete['error']) + ret["result"] = False + ret["comment"] = comment_mediatype_notdeleted + six.text_type( + mediatype_delete["error"] + ) return ret diff --git a/salt/states/zabbix_template.py b/salt/states/zabbix_template.py index f6a55176c90..d21aeefd3a2 100644 --- a/salt/states/zabbix_template.py +++ b/salt/states/zabbix_template.py @@ -1,19 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" .. versionadded:: 2017.7 Management of Zabbix Template object over Zabbix API. :codeauthor: Jakub Sliva <jakub.sliva@ultimum.io> -''' -from __future__ import absolute_import -from __future__ import unicode_literals -import logging +""" +from __future__ import absolute_import, unicode_literals + import json +import logging try: from salt.ext import six from salt.exceptions import SaltException + IMPORTS_OK = True except ImportError: IMPORTS_OK = False @@ -21,16 +22,23 @@ except ImportError: log = logging.getLogger(__name__) -TEMPLATE_RELATIONS = ['groups', 'hosts', 'macros'] -TEMPLATE_COMPONENT_ORDER = ('applications', - 'items', - 'gitems', - 'graphs', - 'screens', - 'httpTests', - 'triggers', - 'discoveries') -DISCOVERYRULE_COMPONENT_ORDER = ('itemprototypes', 'triggerprototypes', 'graphprototypes', 'hostprototypes') +TEMPLATE_RELATIONS = ["groups", "hosts", "macros"] +TEMPLATE_COMPONENT_ORDER = ( + "applications", + "items", + "gitems", + "graphs", + "screens", + "httpTests", + "triggers", + "discoveries", +) +DISCOVERYRULE_COMPONENT_ORDER = ( + "itemprototypes", + "triggerprototypes", + "graphprototypes", + "hostprototypes", +) TEMPLATE_COMPONENT_DEF = { # 'component': {'qtype': 'component type to query', # 'qidname': 'component id name', @@ -42,162 +50,229 @@ TEMPLATE_COMPONENT_DEF = { # 'output': {'output': 'extend', 'selectApplications': 'extend', 'templated': 'true'}, # 'inherited': 'attribute name for inheritance toggling', # 'filter': 'child component unique identification attribute name', - 'applications': {'qtype': 'application', - 'qidname': 'applicationid', - 'qselectpid': 'templateids', - 'ptype': 'template', - 'pid': 'templateid', - 'pid_ref_name': 'hostid', - 'res_id_name': 'applicationids', - 'output': {'output': 'extend', 'templated': 'true'}, - 'inherited': 'inherited', - 'adjust': True, - 'filter': 'name', - 'ro_attrs': ['applicationid', 'flags', 'templateids']}, - 'items': {'qtype': 'item', - 'qidname': 'itemid', - 'qselectpid': 'templateids', - 'ptype': 'template', - 'pid': 'templateid', - 'pid_ref_name': 'hostid', - 'res_id_name': 'itemids', - 'output': {'output': 'extend', 'selectApplications': 'extend', 'templated': 'true'}, - 'inherited': 'inherited', - 'adjust': False, - 'filter': 'name', - 'ro_attrs': ['itemid', 'error', 'flags', 'lastclock', 'lastns', - 'lastvalue', 'prevvalue', 'state', 'templateid']}, - 'triggers': {'qtype': 'trigger', - 'qidname': 'triggerid', - 'qselectpid': 'templateids', - 'ptype': 'template', - 'pid': 'templateid', - 'pid_ref_name': None, - 'res_id_name': 'triggerids', - 'output': {'output': 'extend', 'selectDependencies': 'expand', - 'templated': 'true', 'expandExpression': 'true'}, - 'inherited': 'inherited', - 'adjust': False, - 'filter': 'description', - 'ro_attrs': ['error', 'flags', 'lastchange', 'state', 'templateid', 'value']}, - 'graphs': {'qtype': 'graph', - 'qidname': 'graphid', - 'qselectpid': 'templateids', - 'ptype': 'template', - 'pid': 'templateid', - 'pid_ref_name': None, - 'res_id_name': 'graphids', - 'output': {'output': 'extend', 'selectGraphItems': 'extend', 'templated': 'true'}, - 'inherited': 'inherited', - 'adjust': False, - 'filter': 'name', - 'ro_attrs': ['graphid', 'flags', 'templateid']}, - 'gitems': {'qtype': 'graphitem', - 'qidname': 'itemid', - 'qselectpid': 'graphids', - 'ptype': 'graph', - 'pid': 'graphid', - 'pid_ref_name': None, - 'res_id_name': None, - 'output': {'output': 'extend'}, - 'inherited': 'inherited', - 'adjust': False, - 'filter': 'name', - 'ro_attrs': ['gitemid']}, + "applications": { + "qtype": "application", + "qidname": "applicationid", + "qselectpid": "templateids", + "ptype": "template", + "pid": "templateid", + "pid_ref_name": "hostid", + "res_id_name": "applicationids", + "output": {"output": "extend", "templated": "true"}, + "inherited": "inherited", + "adjust": True, + "filter": "name", + "ro_attrs": ["applicationid", "flags", "templateids"], + }, + "items": { + "qtype": "item", + "qidname": "itemid", + "qselectpid": "templateids", + "ptype": "template", + "pid": "templateid", + "pid_ref_name": "hostid", + "res_id_name": "itemids", + "output": { + "output": "extend", + "selectApplications": "extend", + "templated": "true", + }, + "inherited": "inherited", + "adjust": False, + "filter": "name", + "ro_attrs": [ + "itemid", + "error", + "flags", + "lastclock", + "lastns", + "lastvalue", + "prevvalue", + "state", + "templateid", + ], + }, + "triggers": { + "qtype": "trigger", + "qidname": "triggerid", + "qselectpid": "templateids", + "ptype": "template", + "pid": "templateid", + "pid_ref_name": None, + "res_id_name": "triggerids", + "output": { + "output": "extend", + "selectDependencies": "expand", + "templated": "true", + "expandExpression": "true", + }, + "inherited": "inherited", + "adjust": False, + "filter": "description", + "ro_attrs": ["error", "flags", "lastchange", "state", "templateid", "value"], + }, + "graphs": { + "qtype": "graph", + "qidname": "graphid", + "qselectpid": "templateids", + "ptype": "template", + "pid": "templateid", + "pid_ref_name": None, + "res_id_name": "graphids", + "output": { + "output": "extend", + "selectGraphItems": "extend", + "templated": "true", + }, + "inherited": "inherited", + "adjust": False, + "filter": "name", + "ro_attrs": ["graphid", "flags", "templateid"], + }, + "gitems": { + "qtype": "graphitem", + "qidname": "itemid", + "qselectpid": "graphids", + "ptype": "graph", + "pid": "graphid", + "pid_ref_name": None, + "res_id_name": None, + "output": {"output": "extend"}, + "inherited": "inherited", + "adjust": False, + "filter": "name", + "ro_attrs": ["gitemid"], + }, # "Template screen" - 'screens': {'qtype': 'templatescreen', - 'qidname': 'screenid', - 'qselectpid': 'templateids', - 'ptype': 'template', - 'pid': 'templateid', - 'pid_ref_name': 'templateid', - 'res_id_name': 'screenids', - 'output': {'output': 'extend', 'selectUsers': 'extend', 'selectUserGroups': 'extend', - 'selectScreenItems': 'extend', 'noInheritance': 'true'}, - 'inherited': 'noInheritance', - 'adjust': False, - 'filter': 'name', - 'ro_attrs': ['screenid']}, + "screens": { + "qtype": "templatescreen", + "qidname": "screenid", + "qselectpid": "templateids", + "ptype": "template", + "pid": "templateid", + "pid_ref_name": "templateid", + "res_id_name": "screenids", + "output": { + "output": "extend", + "selectUsers": "extend", + "selectUserGroups": "extend", + "selectScreenItems": "extend", + "noInheritance": "true", + }, + "inherited": "noInheritance", + "adjust": False, + "filter": "name", + "ro_attrs": ["screenid"], + }, # "LLD rule" - 'discoveries': {'qtype': 'discoveryrule', - 'qidname': 'itemid', - 'qselectpid': 'templateids', - 'ptype': 'template', - 'pid': 'templateid', - 'pid_ref_name': 'hostid', - 'res_id_name': 'itemids', - 'output': {'output': 'extend', 'selectFilter': 'extend', 'templated': 'true'}, - 'inherited': 'inherited', - 'adjust': False, - 'filter': 'key_', - 'ro_attrs': ['itemid', 'error', 'state', 'templateid']}, + "discoveries": { + "qtype": "discoveryrule", + "qidname": "itemid", + "qselectpid": "templateids", + "ptype": "template", + "pid": "templateid", + "pid_ref_name": "hostid", + "res_id_name": "itemids", + "output": {"output": "extend", "selectFilter": "extend", "templated": "true"}, + "inherited": "inherited", + "adjust": False, + "filter": "key_", + "ro_attrs": ["itemid", "error", "state", "templateid"], + }, # "Web scenario" - 'httpTests': {'qtype': 'httptest', - 'qidname': 'httptestid', - 'qselectpid': 'templateids', - 'ptype': 'template', - 'pid': 'templateid', - 'pid_ref_name': 'hostid', - 'res_id_name': 'httptestids', - 'output': {'output': 'extend', 'selectSteps': 'extend', 'templated': 'true'}, - 'inherited': 'inherited', - 'adjust': False, - 'filter': 'name', - 'ro_attrs': ['httptestid', 'nextcheck', 'templateid']}, + "httpTests": { + "qtype": "httptest", + "qidname": "httptestid", + "qselectpid": "templateids", + "ptype": "template", + "pid": "templateid", + "pid_ref_name": "hostid", + "res_id_name": "httptestids", + "output": {"output": "extend", "selectSteps": "extend", "templated": "true"}, + "inherited": "inherited", + "adjust": False, + "filter": "name", + "ro_attrs": ["httptestid", "nextcheck", "templateid"], + }, # discoveries => discoveryrule - 'itemprototypes': {'qtype': 'itemprototype', - 'qidname': 'itemid', - 'qselectpid': 'discoveryids', - 'ptype': 'discoveryrule', - 'pid': 'itemid', - 'pid_ref_name': 'ruleid', - # exception only in case of itemprototype - needs both parent ruleid and hostid - 'pid_ref_name2': 'hostid', - 'res_id_name': 'itemids', - 'output': {'output': 'extend', 'selectSteps': 'extend', 'selectApplications': 'extend', - 'templated': 'true'}, - 'adjust': False, - 'inherited': 'inherited', - 'filter': 'name', - 'ro_attrs': ['itemid', 'templateid']}, - 'triggerprototypes': {'qtype': 'triggerprototype', - 'qidname': 'triggerid', - 'qselectpid': 'discoveryids', - 'ptype': 'discoveryrule', - 'pid': 'itemid', - 'pid_ref_name': None, - 'res_id_name': 'triggerids', - 'output': {'output': 'extend', 'selectTags': 'extend', 'selectDependencies': 'extend', - 'templated': 'true', 'expandExpression': 'true'}, - 'inherited': 'inherited', - 'adjust': False, - 'filter': 'description', - 'ro_attrs': ['triggerid', 'templateid']}, - 'graphprototypes': {'qtype': 'graphprototype', - 'qidname': 'graphid', - 'qselectpid': 'discoveryids', - 'ptype': 'discoveryrule', - 'pid': 'itemid', - 'pid_ref_name': None, - 'res_id_name': 'graphids', - 'output': {'output': 'extend', 'selectGraphItems': 'extend', 'templated': 'true'}, - 'inherited': 'inherited', - 'adjust': False, - 'filter': 'name', - 'ro_attrs': ['graphid', 'templateid']}, - 'hostprototypes': {'qtype': 'hostprototype', - 'qidname': 'hostid', - 'qselectpid': 'discoveryids', - 'ptype': 'discoveryrule', - 'pid': 'itemid', - 'pid_ref_name': 'ruleid', - 'res_id_name': 'hostids', - 'output': {'output': 'extend', 'selectGroupLinks': 'expand', 'selectGroupPrototypes': 'expand', - 'selectTemplates': 'expand'}, - 'inherited': 'inherited', - 'adjust': False, - 'filter': 'host', - 'ro_attrs': ['hostid', 'templateid']} + "itemprototypes": { + "qtype": "itemprototype", + "qidname": "itemid", + "qselectpid": "discoveryids", + "ptype": "discoveryrule", + "pid": "itemid", + "pid_ref_name": "ruleid", + # exception only in case of itemprototype - needs both parent ruleid and hostid + "pid_ref_name2": "hostid", + "res_id_name": "itemids", + "output": { + "output": "extend", + "selectSteps": "extend", + "selectApplications": "extend", + "templated": "true", + }, + "adjust": False, + "inherited": "inherited", + "filter": "name", + "ro_attrs": ["itemid", "templateid"], + }, + "triggerprototypes": { + "qtype": "triggerprototype", + "qidname": "triggerid", + "qselectpid": "discoveryids", + "ptype": "discoveryrule", + "pid": "itemid", + "pid_ref_name": None, + "res_id_name": "triggerids", + "output": { + "output": "extend", + "selectTags": "extend", + "selectDependencies": "extend", + "templated": "true", + "expandExpression": "true", + }, + "inherited": "inherited", + "adjust": False, + "filter": "description", + "ro_attrs": ["triggerid", "templateid"], + }, + "graphprototypes": { + "qtype": "graphprototype", + "qidname": "graphid", + "qselectpid": "discoveryids", + "ptype": "discoveryrule", + "pid": "itemid", + "pid_ref_name": None, + "res_id_name": "graphids", + "output": { + "output": "extend", + "selectGraphItems": "extend", + "templated": "true", + }, + "inherited": "inherited", + "adjust": False, + "filter": "name", + "ro_attrs": ["graphid", "templateid"], + }, + "hostprototypes": { + "qtype": "hostprototype", + "qidname": "hostid", + "qselectpid": "discoveryids", + "ptype": "discoveryrule", + "pid": "itemid", + "pid_ref_name": "ruleid", + "res_id_name": "hostids", + "output": { + "output": "extend", + "selectGroupLinks": "expand", + "selectGroupPrototypes": "expand", + "selectTemplates": "expand", + }, + "inherited": "inherited", + "adjust": False, + "filter": "host", + "ro_attrs": ["hostid", "templateid"], + }, } # CHANGE_STACK = [{'component': 'items', 'action': 'create', 'params': dict|list}] @@ -205,76 +280,93 @@ CHANGE_STACK = [] def __virtual__(): - ''' + """ Only make these states available if Zabbix module and run_query function is available and all 3rd party modules imported. - ''' - if 'zabbix.run_query' in __salt__ and IMPORTS_OK: + """ + if "zabbix.run_query" in __salt__ and IMPORTS_OK: return True - return False, 'Import zabbix or other needed modules failed.' + return False, "Import zabbix or other needed modules failed." def _diff_and_merge_host_list(defined, existing): - ''' + """ If Zabbix template is to be updated then list of assigned hosts must be provided in all or nothing manner to prevent some externally assigned hosts to be detached. :param defined: list of hosts defined in sls :param existing: list of hosts taken from live Zabbix :return: list to be updated (combinated or empty list) - ''' + """ try: - defined_host_ids = set([host['hostid'] for host in defined]) - existing_host_ids = set([host['hostid'] for host in existing]) + defined_host_ids = set([host["hostid"] for host in defined]) + existing_host_ids = set([host["hostid"] for host in existing]) except KeyError: - raise SaltException('List of hosts in template not defined correctly.') + raise SaltException("List of hosts in template not defined correctly.") diff = defined_host_ids - existing_host_ids - return [{'hostid': six.text_type(hostid)} for hostid in diff | existing_host_ids] if diff else [] + return ( + [{"hostid": six.text_type(hostid)} for hostid in diff | existing_host_ids] + if diff + else [] + ) def _get_existing_template_c_list(component, parent_id, **kwargs): - ''' + """ Make a list of given component type not inherited from other templates because Zabbix API returns only list of all and list of inherited component items so we have to do a difference list. :param component: Template component (application, item, etc...) :param parent_id: ID of existing template the component is assigned to :return List of non-inherited (own) components - ''' + """ c_def = TEMPLATE_COMPONENT_DEF[component] - q_params = dict(c_def['output']) - q_params.update({c_def['qselectpid']: parent_id}) + q_params = dict(c_def["output"]) + q_params.update({c_def["qselectpid"]: parent_id}) - existing_clist_all = __salt__['zabbix.run_query'](c_def['qtype'] + '.get', q_params, **kwargs) + existing_clist_all = __salt__["zabbix.run_query"]( + c_def["qtype"] + ".get", q_params, **kwargs + ) # in some cases (e.g. templatescreens) the logic is reversed (even name of the flag is different!) - if c_def['inherited'] == 'inherited': - q_params.update({c_def['inherited']: 'true'}) - existing_clist_inherited = __salt__['zabbix.run_query'](c_def['qtype'] + '.get', q_params, **kwargs) + if c_def["inherited"] == "inherited": + q_params.update({c_def["inherited"]: "true"}) + existing_clist_inherited = __salt__["zabbix.run_query"]( + c_def["qtype"] + ".get", q_params, **kwargs + ) else: existing_clist_inherited = [] if existing_clist_inherited: - return [c_all for c_all in existing_clist_all if c_all not in existing_clist_inherited] + return [ + c_all + for c_all in existing_clist_all + if c_all not in existing_clist_inherited + ] return existing_clist_all def _adjust_object_lists(obj): - ''' + """ For creation or update of object that have attribute which contains a list Zabbix awaits plain list of IDs while querying Zabbix for same object returns list of dicts :param obj: Zabbix object parameters - ''' + """ for subcomp in TEMPLATE_COMPONENT_DEF: - if subcomp in obj and TEMPLATE_COMPONENT_DEF[subcomp]['adjust']: - obj[subcomp] = [item[TEMPLATE_COMPONENT_DEF[subcomp]['qidname']] for item in obj[subcomp]] + if subcomp in obj and TEMPLATE_COMPONENT_DEF[subcomp]["adjust"]: + obj[subcomp] = [ + item[TEMPLATE_COMPONENT_DEF[subcomp]["qidname"]] + for item in obj[subcomp] + ] -def _manage_component(component, parent_id, defined, existing, template_id=None, **kwargs): - ''' +def _manage_component( + component, parent_id, defined, existing, template_id=None, **kwargs +): + """ Takes particular component list, compares it with existing, call appropriate API methods - create, update, delete. :param component: component name @@ -282,12 +374,12 @@ def _manage_component(component, parent_id, defined, existing, template_id=None, :param defined: list of defined items of named component :param existing: list of existing items of named component :param template_id: In case that component need also template ID for creation (although parent_id is given?!?!?) - ''' - zabbix_id_mapper = __salt__['zabbix.get_zabbix_id_mapper']() + """ + zabbix_id_mapper = __salt__["zabbix.get_zabbix_id_mapper"]() - dry_run = __opts__['test'] + dry_run = __opts__["test"] c_def = TEMPLATE_COMPONENT_DEF[component] - compare_key = c_def['filter'] + compare_key = c_def["filter"] defined_set = set([item[compare_key] for item in defined]) existing_set = set([item[compare_key] for item in existing]) @@ -299,48 +391,89 @@ def _manage_component(component, parent_id, defined, existing, template_id=None, create_list = [item for item in defined if item[compare_key] in create_set] for object_params in create_list: if parent_id: - object_params.update({c_def['pid_ref_name']: parent_id}) + object_params.update({c_def["pid_ref_name"]: parent_id}) - if 'pid_ref_name2' in c_def: - object_params.update({c_def['pid_ref_name2']: template_id}) + if "pid_ref_name2" in c_def: + object_params.update({c_def["pid_ref_name2"]: template_id}) _adjust_object_lists(object_params) if not dry_run: - object_create = __salt__['zabbix.run_query'](c_def['qtype'] + '.create', object_params, **kwargs) + object_create = __salt__["zabbix.run_query"]( + c_def["qtype"] + ".create", object_params, **kwargs + ) if object_create: - object_ids = object_create[c_def['res_id_name']] - CHANGE_STACK.append({'component': component, 'action': 'create', 'params': object_params, - c_def['filter']: object_params[c_def['filter']], 'object_id': object_ids}) + object_ids = object_create[c_def["res_id_name"]] + CHANGE_STACK.append( + { + "component": component, + "action": "create", + "params": object_params, + c_def["filter"]: object_params[c_def["filter"]], + "object_id": object_ids, + } + ) else: - CHANGE_STACK.append({'component': component, 'action': 'create', 'params': object_params, - 'object_id': 'CREATED '+TEMPLATE_COMPONENT_DEF[component]['qtype']+' ID'}) + CHANGE_STACK.append( + { + "component": component, + "action": "create", + "params": object_params, + "object_id": "CREATED " + + TEMPLATE_COMPONENT_DEF[component]["qtype"] + + " ID", + } + ) delete_list = [item for item in existing if item[compare_key] in delete_set] for object_del in delete_list: - object_id_name = zabbix_id_mapper[c_def['qtype']] - CHANGE_STACK.append({'component': component, 'action': 'delete', 'params': [object_del[object_id_name]]}) + object_id_name = zabbix_id_mapper[c_def["qtype"]] + CHANGE_STACK.append( + { + "component": component, + "action": "delete", + "params": [object_del[object_id_name]], + } + ) if not dry_run: - __salt__['zabbix.run_query'](c_def['qtype'] + '.delete', [object_del[object_id_name]], **kwargs) + __salt__["zabbix.run_query"]( + c_def["qtype"] + ".delete", [object_del[object_id_name]], **kwargs + ) for object_name in update_set: - ditem = next((item for item in defined if item[compare_key] == object_name), None) - eitem = next((item for item in existing if item[compare_key] == object_name), None) - diff_params = __salt__['zabbix.compare_params'](ditem, eitem, True) + ditem = next( + (item for item in defined if item[compare_key] == object_name), None + ) + eitem = next( + (item for item in existing if item[compare_key] == object_name), None + ) + diff_params = __salt__["zabbix.compare_params"](ditem, eitem, True) - if diff_params['new']: - diff_params['new'][zabbix_id_mapper[c_def['qtype']]] = eitem[zabbix_id_mapper[c_def['qtype']]] - diff_params['old'][zabbix_id_mapper[c_def['qtype']]] = eitem[zabbix_id_mapper[c_def['qtype']]] - _adjust_object_lists(diff_params['new']) - _adjust_object_lists(diff_params['old']) - CHANGE_STACK.append({'component': component, 'action': 'update', 'params': diff_params['new']}) + if diff_params["new"]: + diff_params["new"][zabbix_id_mapper[c_def["qtype"]]] = eitem[ + zabbix_id_mapper[c_def["qtype"]] + ] + diff_params["old"][zabbix_id_mapper[c_def["qtype"]]] = eitem[ + zabbix_id_mapper[c_def["qtype"]] + ] + _adjust_object_lists(diff_params["new"]) + _adjust_object_lists(diff_params["old"]) + CHANGE_STACK.append( + { + "component": component, + "action": "update", + "params": diff_params["new"], + } + ) if not dry_run: - __salt__['zabbix.run_query'](c_def['qtype'] + '.update', diff_params['new'], **kwargs) + __salt__["zabbix.run_query"]( + c_def["qtype"] + ".update", diff_params["new"], **kwargs + ) def is_present(name, **kwargs): - ''' + """ Check if Zabbix Template already exists. :param name: Zabbix Template name @@ -353,27 +486,29 @@ def is_present(name, **kwargs): does_zabbix-template-exist: zabbix_template.is_present: - name: Template OS Linux - ''' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} try: - object_id = __salt__['zabbix.get_object_id_by_params']('template', {'filter': {'name': name}}, **kwargs) + object_id = __salt__["zabbix.get_object_id_by_params"]( + "template", {"filter": {"name": name}}, **kwargs + ) except SaltException: object_id = False if not object_id: - ret['result'] = False - ret['comment'] = 'Zabbix Template "{0}" does not exist.'.format(name) + ret["result"] = False + ret["comment"] = 'Zabbix Template "{0}" does not exist.'.format(name) else: - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" exists.'.format(name) + ret["result"] = True + ret["comment"] = 'Zabbix Template "{0}" exists.'.format(name) return ret # pylint: disable=too-many-statements,too-many-locals def present(name, params, static_host_list=True, **kwargs): - ''' + """ Creates Zabbix Template object or if differs update it according defined parameters. See Zabbix API documentation. Zabbix API version: >3.0 @@ -495,12 +630,12 @@ def present(name, params, static_host_list=True, **kwargs): resourceid: query_object: graph query_name: Ceph OSD avg fill graph - ''' - zabbix_id_mapper = __salt__['zabbix.get_zabbix_id_mapper']() + """ + zabbix_id_mapper = __salt__["zabbix.get_zabbix_id_mapper"]() - dry_run = __opts__['test'] - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - params['host'] = name + dry_run = __opts__["test"] + ret = {"name": name, "result": False, "comment": "", "changes": {}} + params["host"] = name del CHANGE_STACK[:] @@ -514,14 +649,18 @@ def present(name, params, static_host_list=True, **kwargs): discovery_components = [] for attr in params: - if attr in TEMPLATE_COMPONENT_ORDER and six.text_type(attr) != 'discoveries': + if attr in TEMPLATE_COMPONENT_ORDER and six.text_type(attr) != "discoveries": template_components[attr] = params[attr] - elif six.text_type(attr) == 'discoveries': + elif six.text_type(attr) == "discoveries": d_rules = [] for d_rule in params[attr]: - d_rule_components = {'query_pid': {'component': attr, - 'filter_val': d_rule[TEMPLATE_COMPONENT_DEF[attr]['filter']]}} + d_rule_components = { + "query_pid": { + "component": attr, + "filter_val": d_rule[TEMPLATE_COMPONENT_DEF[attr]["filter"]], + } + } for proto_name in DISCOVERYRULE_COMPONENT_ORDER: if proto_name in d_rule: d_rule_components[proto_name] = d_rule[proto_name] @@ -542,37 +681,52 @@ def present(name, params, static_host_list=True, **kwargs): # if a component is not defined, it means to remove existing items during update (empty list) for attr in TEMPLATE_RELATIONS: - template_definition[attr] = params[attr] if attr in params and params[attr] else [] + template_definition[attr] = ( + params[attr] if attr in params and params[attr] else [] + ) - defined_obj = __salt__['zabbix.substitute_params'](template_definition, **kwargs) - log.info('SUBSTITUTED template_definition: %s', six.text_type(json.dumps(defined_obj, indent=4))) + defined_obj = __salt__["zabbix.substitute_params"](template_definition, **kwargs) + log.info( + "SUBSTITUTED template_definition: %s", + six.text_type(json.dumps(defined_obj, indent=4)), + ) - tmpl_get = __salt__['zabbix.run_query']('template.get', - {'output': 'extend', 'selectGroups': 'groupid', 'selectHosts': 'hostid', - 'selectTemplates': 'templateid', 'selectMacros': 'extend', - 'filter': {'host': name}}, - **kwargs) - log.info('TEMPLATE get result: %s', six.text_type(json.dumps(tmpl_get, indent=4))) + tmpl_get = __salt__["zabbix.run_query"]( + "template.get", + { + "output": "extend", + "selectGroups": "groupid", + "selectHosts": "hostid", + "selectTemplates": "templateid", + "selectMacros": "extend", + "filter": {"host": name}, + }, + **kwargs + ) + log.info("TEMPLATE get result: %s", six.text_type(json.dumps(tmpl_get, indent=4))) - existing_obj = __salt__['zabbix.substitute_params'](tmpl_get[0], **kwargs) \ - if tmpl_get and len(tmpl_get) == 1 else False + existing_obj = ( + __salt__["zabbix.substitute_params"](tmpl_get[0], **kwargs) + if tmpl_get and len(tmpl_get) == 1 + else False + ) if existing_obj: - template_id = existing_obj[zabbix_id_mapper['template']] + template_id = existing_obj[zabbix_id_mapper["template"]] if not static_host_list: # Prepare objects for comparison defined_wo_hosts = defined_obj - if 'hosts' in defined_obj: - defined_hosts = defined_obj['hosts'] - del defined_wo_hosts['hosts'] + if "hosts" in defined_obj: + defined_hosts = defined_obj["hosts"] + del defined_wo_hosts["hosts"] else: defined_hosts = [] existing_wo_hosts = existing_obj - if 'hosts' in existing_obj: - existing_hosts = existing_obj['hosts'] - del existing_wo_hosts['hosts'] + if "hosts" in existing_obj: + existing_hosts = existing_obj["hosts"] + del existing_wo_hosts["hosts"] else: existing_hosts = [] @@ -581,126 +735,213 @@ def present(name, params, static_host_list=True, **kwargs): hosts_list = _diff_and_merge_host_list(defined_hosts, existing_hosts) # Compare objects without hosts - diff_params = __salt__['zabbix.compare_params'](defined_wo_hosts, existing_wo_hosts, True) + diff_params = __salt__["zabbix.compare_params"]( + defined_wo_hosts, existing_wo_hosts, True + ) # Merge comparison results together - if ('new' in diff_params and 'hosts' in diff_params['new']) or hosts_list: - diff_params['new']['hosts'] = hosts_list + if ("new" in diff_params and "hosts" in diff_params["new"]) or hosts_list: + diff_params["new"]["hosts"] = hosts_list else: - diff_params = __salt__['zabbix.compare_params'](defined_obj, existing_obj, True) + diff_params = __salt__["zabbix.compare_params"]( + defined_obj, existing_obj, True + ) - if diff_params['new']: - diff_params['new'][zabbix_id_mapper['template']] = template_id - diff_params['old'][zabbix_id_mapper['template']] = template_id - log.info('TEMPLATE: update params: %s', six.text_type(json.dumps(diff_params, indent=4))) + if diff_params["new"]: + diff_params["new"][zabbix_id_mapper["template"]] = template_id + diff_params["old"][zabbix_id_mapper["template"]] = template_id + log.info( + "TEMPLATE: update params: %s", + six.text_type(json.dumps(diff_params, indent=4)), + ) - CHANGE_STACK.append({'component': 'template', 'action': 'update', 'params': diff_params['new']}) + CHANGE_STACK.append( + { + "component": "template", + "action": "update", + "params": diff_params["new"], + } + ) if not dry_run: - tmpl_update = __salt__['zabbix.run_query']('template.update', diff_params['new'], **kwargs) - log.info('TEMPLATE update result: %s', six.text_type(tmpl_update)) + tmpl_update = __salt__["zabbix.run_query"]( + "template.update", diff_params["new"], **kwargs + ) + log.info("TEMPLATE update result: %s", six.text_type(tmpl_update)) else: - CHANGE_STACK.append({'component': 'template', 'action': 'create', 'params': defined_obj}) + CHANGE_STACK.append( + {"component": "template", "action": "create", "params": defined_obj} + ) if not dry_run: - tmpl_create = __salt__['zabbix.run_query']('template.create', defined_obj, **kwargs) - log.info('TEMPLATE create result: %s', six.text_type(tmpl_create)) + tmpl_create = __salt__["zabbix.run_query"]( + "template.create", defined_obj, **kwargs + ) + log.info("TEMPLATE create result: %s", six.text_type(tmpl_create)) if tmpl_create: - template_id = tmpl_create['templateids'][0] + template_id = tmpl_create["templateids"][0] - log.info('\n\ntemplate_components: %s', json.dumps(template_components, indent=4)) - log.info('\n\ndiscovery_components: %s', json.dumps(discovery_components, indent=4)) - log.info('\n\nCurrent CHANGE_STACK: %s', six.text_type(json.dumps(CHANGE_STACK, indent=4))) + log.info("\n\ntemplate_components: %s", json.dumps(template_components, indent=4)) + log.info("\n\ndiscovery_components: %s", json.dumps(discovery_components, indent=4)) + log.info( + "\n\nCurrent CHANGE_STACK: %s", + six.text_type(json.dumps(CHANGE_STACK, indent=4)), + ) if existing_obj or not dry_run: for component in TEMPLATE_COMPONENT_ORDER: - log.info('\n\n\n\n\nCOMPONENT: %s\n\n', six.text_type(json.dumps(component))) + log.info( + "\n\n\n\n\nCOMPONENT: %s\n\n", six.text_type(json.dumps(component)) + ) # 1) query for components which belongs to the template - existing_c_list = _get_existing_template_c_list(component, template_id, **kwargs) - existing_c_list_subs = __salt__['zabbix.substitute_params'](existing_c_list, **kwargs) \ - if existing_c_list else [] + existing_c_list = _get_existing_template_c_list( + component, template_id, **kwargs + ) + existing_c_list_subs = ( + __salt__["zabbix.substitute_params"](existing_c_list, **kwargs) + if existing_c_list + else [] + ) if component in template_components: - defined_c_list_subs = __salt__['zabbix.substitute_params']( + defined_c_list_subs = __salt__["zabbix.substitute_params"]( template_components[component], - extend_params={TEMPLATE_COMPONENT_DEF[component]['qselectpid']: template_id}, - filter_key=TEMPLATE_COMPONENT_DEF[component]['filter'], - **kwargs) + extend_params={ + TEMPLATE_COMPONENT_DEF[component]["qselectpid"]: template_id + }, + filter_key=TEMPLATE_COMPONENT_DEF[component]["filter"], + **kwargs + ) else: defined_c_list_subs = [] # 2) take lists of particular component and compare -> do create, update and delete actions - _manage_component(component, template_id, defined_c_list_subs, existing_c_list_subs, **kwargs) + _manage_component( + component, + template_id, + defined_c_list_subs, + existing_c_list_subs, + **kwargs + ) - log.info('\n\nCurrent CHANGE_STACK: %s', six.text_type(json.dumps(CHANGE_STACK, indent=4))) + log.info( + "\n\nCurrent CHANGE_STACK: %s", + six.text_type(json.dumps(CHANGE_STACK, indent=4)), + ) for d_rule_component in discovery_components: # query for parent id -> "query_pid": {"filter_val": "vfs.fs.discovery", "component": "discoveries"} - q_def = d_rule_component['query_pid'] - c_def = TEMPLATE_COMPONENT_DEF[q_def['component']] - q_object = c_def['qtype'] - q_params = dict(c_def['output']) - q_params.update({c_def['qselectpid']: template_id}) - q_params.update({'filter': {c_def['filter']: q_def['filter_val']}}) + q_def = d_rule_component["query_pid"] + c_def = TEMPLATE_COMPONENT_DEF[q_def["component"]] + q_object = c_def["qtype"] + q_params = dict(c_def["output"]) + q_params.update({c_def["qselectpid"]: template_id}) + q_params.update({"filter": {c_def["filter"]: q_def["filter_val"]}}) - parent_id = __salt__['zabbix.get_object_id_by_params'](q_object, q_params, **kwargs) + parent_id = __salt__["zabbix.get_object_id_by_params"]( + q_object, q_params, **kwargs + ) for proto_name in DISCOVERYRULE_COMPONENT_ORDER: - log.info('\n\n\n\n\nPROTOTYPE_NAME: %s\n\n', six.text_type(json.dumps(proto_name))) - existing_p_list = _get_existing_template_c_list(proto_name, parent_id, **kwargs) - existing_p_list_subs = __salt__['zabbix.substitute_params'](existing_p_list, **kwargs)\ - if existing_p_list else [] + log.info( + "\n\n\n\n\nPROTOTYPE_NAME: %s\n\n", + six.text_type(json.dumps(proto_name)), + ) + existing_p_list = _get_existing_template_c_list( + proto_name, parent_id, **kwargs + ) + existing_p_list_subs = ( + __salt__["zabbix.substitute_params"](existing_p_list, **kwargs) + if existing_p_list + else [] + ) if proto_name in d_rule_component: - defined_p_list_subs = __salt__['zabbix.substitute_params']( + defined_p_list_subs = __salt__["zabbix.substitute_params"]( d_rule_component[proto_name], - extend_params={c_def['qselectpid']: template_id}, - **kwargs) + extend_params={c_def["qselectpid"]: template_id}, + **kwargs + ) else: defined_p_list_subs = [] - _manage_component(proto_name, - parent_id, - defined_p_list_subs, - existing_p_list_subs, - template_id=template_id, - **kwargs) + _manage_component( + proto_name, + parent_id, + defined_p_list_subs, + existing_p_list_subs, + template_id=template_id, + **kwargs + ) - log.info('\n\nCurrent CHANGE_STACK: %s', six.text_type(json.dumps(CHANGE_STACK, indent=4))) + log.info( + "\n\nCurrent CHANGE_STACK: %s", + six.text_type(json.dumps(CHANGE_STACK, indent=4)), + ) if not CHANGE_STACK: - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" already exists and corresponds to a definition.'.format(name) + ret["result"] = True + ret[ + "comment" + ] = 'Zabbix Template "{0}" already exists and corresponds to a definition.'.format( + name + ) else: - tmpl_action = next((item for item in CHANGE_STACK - if item['component'] == 'template' and item['action'] == 'create'), None) + tmpl_action = next( + ( + item + for item in CHANGE_STACK + if item["component"] == "template" and item["action"] == "create" + ), + None, + ) if tmpl_action: - ret['result'] = True + ret["result"] = True if dry_run: - ret['comment'] = 'Zabbix Template "{0}" would be created.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" does not exist.'.format(name), - 'new': 'Zabbix Template "{0}" would be created ' - 'according definition.'.format(name)}} + ret["comment"] = 'Zabbix Template "{0}" would be created.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" does not exist.'.format(name), + "new": 'Zabbix Template "{0}" would be created ' + "according definition.".format(name), + } + } else: - ret['comment'] = 'Zabbix Template "{0}" created.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" did not exist.'.format(name), - 'new': 'Zabbix Template "{0}" created according definition.'.format(name)}} + ret["comment"] = 'Zabbix Template "{0}" created.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" did not exist.'.format(name), + "new": 'Zabbix Template "{0}" created according definition.'.format( + name + ), + } + } else: - ret['result'] = True + ret["result"] = True if dry_run: - ret['comment'] = 'Zabbix Template "{0}" would be updated.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" differs.'.format(name), - 'new': 'Zabbix Template "{0}" would be updated ' - 'according definition.'.format(name)}} + ret["comment"] = 'Zabbix Template "{0}" would be updated.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" differs.'.format(name), + "new": 'Zabbix Template "{0}" would be updated ' + "according definition.".format(name), + } + } else: - ret['comment'] = 'Zabbix Template "{0}" updated.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" differed.'.format(name), - 'new': 'Zabbix Template "{0}" updated according definition.'.format(name)}} + ret["comment"] = 'Zabbix Template "{0}" updated.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" differed.'.format(name), + "new": 'Zabbix Template "{0}" updated according definition.'.format( + name + ), + } + } return ret def absent(name, **kwargs): - ''' + """ Makes the Zabbix Template to be absent (either does not exist or delete it). :param name: Zabbix Template name @@ -713,30 +954,42 @@ def absent(name, **kwargs): zabbix-template-absent: zabbix_template.absent: - name: Ceph OSD - ''' - dry_run = __opts__['test'] - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + dry_run = __opts__["test"] + ret = {"name": name, "result": False, "comment": "", "changes": {}} try: - object_id = __salt__['zabbix.get_object_id_by_params']('template', {'filter': {'name': name}}, **kwargs) + object_id = __salt__["zabbix.get_object_id_by_params"]( + "template", {"filter": {"name": name}}, **kwargs + ) except SaltException: object_id = False if not object_id: - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" does not exist.'.format(name) + ret["result"] = True + ret["comment"] = 'Zabbix Template "{0}" does not exist.'.format(name) else: if dry_run: - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" would be deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" exists.'.format(name), - 'new': 'Zabbix Template "{0}" would be deleted.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Template "{0}" would be deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" exists.'.format(name), + "new": 'Zabbix Template "{0}" would be deleted.'.format(name), + } + } else: - tmpl_delete = __salt__['zabbix.run_query']('template.delete', [object_id], **kwargs) + tmpl_delete = __salt__["zabbix.run_query"]( + "template.delete", [object_id], **kwargs + ) if tmpl_delete: - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" existed.'.format(name), - 'new': 'Zabbix Template "{0}" deleted.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Template "{0}" deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" existed.'.format(name), + "new": 'Zabbix Template "{0}" deleted.'.format(name), + } + } return ret diff --git a/salt/states/zabbix_user.py b/salt/states/zabbix_user.py index 632390b30b8..4f4d4c577c2 100644 --- a/salt/states/zabbix_user.py +++ b/salt/states/zabbix_user.py @@ -1,31 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Management of Zabbix users. :codeauthor: Jiri Kotlin <jiri.kotlin@ultimum.io> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + from copy import deepcopy # Import Salt libs import salt.utils.json -from salt.ext import six from salt.exceptions import SaltException +from salt.ext import six def __virtual__(): - ''' + """ Only make these states available if Zabbix module is available. - ''' - return 'zabbix.user_create' in __salt__ + """ + if "zabbix.user_create" in __salt__: + return True + return (False, "zabbix module could not be loaded") def admin_password_present(name, password=None, **kwargs): - ''' + """ Initial change of Zabbix Admin password to password taken from one of the sources (only the most prioritized one): 1. 'password' parameter 2. '_connection_password' parameter @@ -52,33 +55,37 @@ def admin_password_present(name, password=None, **kwargs): zabbix-admin-password: zabbix_user.admin_password_present: - password: SECRET_PASS - ''' - dry_run = __opts__['test'] - default_zabbix_user = 'Admin' - default_zabbix_password = 'zabbix' - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + """ + dry_run = __opts__["test"] + default_zabbix_user = "Admin" + default_zabbix_password = "zabbix" + ret = {"name": name, "changes": {}, "result": False, "comment": ""} passwords = [] connection_args = {} - connection_args['_connection_user'] = default_zabbix_user - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + connection_args["_connection_user"] = default_zabbix_user + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - config_password = __salt__['config.option']('zabbix.password', None) + config_password = __salt__["config.option"]("zabbix.password", None) if config_password: passwords.append(config_password) - if '_connection_password' in kwargs: - passwords.append(kwargs['_connection_password']) + if "_connection_password" in kwargs: + passwords.append(kwargs["_connection_password"]) if password: passwords.append(password) # get unique list in preserved order and reverse it seen = set() - unique_passwords = [six.text_type(x) for x in passwords if x not in seen and not seen.add(x)] + unique_passwords = [ + six.text_type(x) for x in passwords if x not in seen and not seen.add(x) + ] unique_passwords.reverse() if not unique_passwords: - ret['comment'] = 'Could not find any Zabbix Admin password setting! See documentation.' + ret[ + "comment" + ] = "Could not find any Zabbix Admin password setting! See documentation." return ret else: desired_password = unique_passwords[0] @@ -86,40 +93,49 @@ def admin_password_present(name, password=None, **kwargs): unique_passwords.append(default_zabbix_password) for pwd in unique_passwords: - connection_args['_connection_password'] = pwd + connection_args["_connection_password"] = pwd try: - user_get = __salt__['zabbix.user_get'](default_zabbix_user, **connection_args) + user_get = __salt__["zabbix.user_get"]( + default_zabbix_user, **connection_args + ) except SaltException as err: - if 'Login name or password is incorrect' in six.text_type(err): + if "Login name or password is incorrect" in six.text_type(err): user_get = False else: raise if user_get: if pwd == desired_password: - ret['result'] = True - ret['comment'] = 'Admin password is correct.' + ret["result"] = True + ret["comment"] = "Admin password is correct." return ret else: break if user_get: if not dry_run: - user_update = __salt__['zabbix.user_update'](user_get[0]['userid'], - passwd=desired_password, - **connection_args) + user_update = __salt__["zabbix.user_update"]( + user_get[0]["userid"], passwd=desired_password, **connection_args + ) if user_update: - ret['result'] = True - ret['changes']['passwd'] = 'changed to \'' + six.text_type(desired_password) + '\'' + ret["result"] = True + ret["changes"]["passwd"] = ( + "changed to '" + six.text_type(desired_password) + "'" + ) else: - ret['result'] = None - ret['comment'] = 'Password for user ' + six.text_type(default_zabbix_user) \ - + ' updated to \'' + six.text_type(desired_password) + '\'' + ret["result"] = None + ret["comment"] = ( + "Password for user " + + six.text_type(default_zabbix_user) + + " updated to '" + + six.text_type(desired_password) + + "'" + ) return ret def present(alias, passwd, usrgrps, medias=None, password_reset=False, **kwargs): - ''' + """ Ensures that the user exists, eventually creates new user. NOTE: use argument firstname instead of name to not mess values with name from salt sls. @@ -163,43 +179,45 @@ def present(alias, passwd, usrgrps, medias=None, password_reset=False, **kwargs) - severity: D - sendto: '+42032132588568' - ''' + """ if medias is None: medias = [] connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': alias, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": alias, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_user_created = 'User {0} created.'.format(alias) - comment_user_updated = 'User {0} updated.'.format(alias) - comment_user_notcreated = 'Unable to create user: {0}. '.format(alias) - comment_user_exists = 'User {0} already exists.'.format(alias) - changes_user_created = {alias: {'old': 'User {0} does not exist.'.format(alias), - 'new': 'User {0} created.'.format(alias), - } - } + comment_user_created = "User {0} created.".format(alias) + comment_user_updated = "User {0} updated.".format(alias) + comment_user_notcreated = "Unable to create user: {0}. ".format(alias) + comment_user_exists = "User {0} already exists.".format(alias) + changes_user_created = { + alias: { + "old": "User {0} does not exist.".format(alias), + "new": "User {0} created.".format(alias), + } + } def _media_format(medias_data): - ''' + """ Formats medias from SLS file into valid JSON usable for zabbix API. Completes JSON with default values. :param medias_data: list of media data from SLS file - ''' + """ if not medias_data: return list() medias_json = salt.utils.json.loads(salt.utils.json.dumps(medias_data)) - medias_attr = ('active', 'mediatype', 'period', 'severity', 'sendto') - media_type = {'mail': 1, 'jabber': 2, 'sms': 3} - media_severities = ('D', 'H', 'A', 'W', 'I', 'N') + medias_attr = ("active", "mediatype", "period", "severity", "sendto") + media_type = {"mail": 1, "jabber": 2, "sms": 3} + media_severities = ("D", "H", "A", "W", "I", "N") medias_dict = dict() for media in medias_json: @@ -213,147 +231,168 @@ def present(alias, passwd, usrgrps, medias=None, password_reset=False, **kwargs) medias_list = list() for key, value in medias_dict.items(): # Load media values or default values - active = '0' if six.text_type(value.get('active', 'true')).lower() == 'true' else '1' - mediatype_sls = six.text_type(value.get('mediatype', 'mail')).lower() + active = ( + "0" + if six.text_type(value.get("active", "true")).lower() == "true" + else "1" + ) + mediatype_sls = six.text_type(value.get("mediatype", "mail")).lower() mediatypeid = six.text_type(media_type.get(mediatype_sls, 1)) - period = value.get('period', '1-7,00:00-24:00') - sendto = value.get('sendto', key) + period = value.get("period", "1-7,00:00-24:00") + sendto = value.get("sendto", key) - severity_sls = value.get('severity', 'HD') + severity_sls = value.get("severity", "HD") severity_bin = six.text_type() for sev in media_severities: if sev in severity_sls: - severity_bin += '1' + severity_bin += "1" else: - severity_bin += '0' + severity_bin += "0" severity = six.text_type(int(severity_bin, 2)) - medias_list.append({'active': active, - 'mediatypeid': mediatypeid, - 'period': period, - 'sendto': sendto, - 'severity': severity}) + medias_list.append( + { + "active": active, + "mediatypeid": mediatypeid, + "period": period, + "sendto": sendto, + "severity": severity, + } + ) return medias_list - user_exists = __salt__['zabbix.user_exists'](alias, **connection_args) + user_exists = __salt__["zabbix.user_exists"](alias, **connection_args) if user_exists: - user = __salt__['zabbix.user_get'](alias, **connection_args)[0] - userid = user['userid'] + user = __salt__["zabbix.user_get"](alias, **connection_args)[0] + userid = user["userid"] update_usrgrps = False update_medias = False - usergroups = __salt__['zabbix.usergroup_get'](userids=userid, **connection_args) + usergroups = __salt__["zabbix.usergroup_get"](userids=userid, **connection_args) cur_usrgrps = list() for usergroup in usergroups: - cur_usrgrps.append(int(usergroup['usrgrpid'])) + cur_usrgrps.append(int(usergroup["usrgrpid"])) if set(cur_usrgrps) != set(usrgrps): update_usrgrps = True - user_medias = __salt__['zabbix.user_getmedia'](userid, **connection_args) + user_medias = __salt__["zabbix.user_getmedia"](userid, **connection_args) medias_formated = _media_format(medias) if user_medias: user_medias_copy = deepcopy(user_medias) for user_med in user_medias_copy: - user_med.pop('userid') - user_med.pop('mediaid') - media_diff = [x for x in medias_formated if x not in user_medias_copy] + \ - [y for y in user_medias_copy if y not in medias_formated] + user_med.pop("userid") + user_med.pop("mediaid") + media_diff = [x for x in medias_formated if x not in user_medias_copy] + [ + y for y in user_medias_copy if y not in medias_formated + ] if media_diff: update_medias = True elif not user_medias and medias: update_medias = True # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if user_exists: if update_usrgrps or password_reset or update_medias: - ret['result'] = None - ret['comment'] = comment_user_updated + ret["result"] = None + ret["comment"] = comment_user_updated else: - ret['result'] = True - ret['comment'] = comment_user_exists + ret["result"] = True + ret["comment"] = comment_user_exists else: - ret['result'] = None - ret['comment'] = comment_user_created + ret["result"] = None + ret["comment"] = comment_user_created error = [] if user_exists: - ret['result'] = True + ret["result"] = True if update_usrgrps or password_reset or update_medias: - ret['comment'] = comment_user_updated + ret["comment"] = comment_user_updated if update_usrgrps: - __salt__['zabbix.user_update'](userid, usrgrps=usrgrps, **connection_args) - updated_groups = __salt__['zabbix.usergroup_get'](userids=userid, **connection_args) + __salt__["zabbix.user_update"]( + userid, usrgrps=usrgrps, **connection_args + ) + updated_groups = __salt__["zabbix.usergroup_get"]( + userids=userid, **connection_args + ) cur_usrgrps = list() for usergroup in updated_groups: - cur_usrgrps.append(int(usergroup['usrgrpid'])) + cur_usrgrps.append(int(usergroup["usrgrpid"])) usrgrp_diff = list(set(usrgrps) - set(cur_usrgrps)) if usrgrp_diff: - error.append('Unable to update grpup(s): {0}'.format(usrgrp_diff)) + error.append("Unable to update grpup(s): {0}".format(usrgrp_diff)) - ret['changes']['usrgrps'] = six.text_type(updated_groups) + ret["changes"]["usrgrps"] = six.text_type(updated_groups) if password_reset: - updated_password = __salt__['zabbix.user_update'](userid, passwd=passwd, **connection_args) - if 'error' in updated_password: - error.append(updated_groups['error']) + updated_password = __salt__["zabbix.user_update"]( + userid, passwd=passwd, **connection_args + ) + if "error" in updated_password: + error.append(updated_groups["error"]) else: - ret['changes']['passwd'] = 'updated' + ret["changes"]["passwd"] = "updated" if update_medias: for user_med in user_medias: - deletedmed = __salt__['zabbix.user_deletemedia'](user_med['mediaid'], **connection_args) - if 'error' in deletedmed: - error.append(deletedmed['error']) + deletedmed = __salt__["zabbix.user_deletemedia"]( + user_med["mediaid"], **connection_args + ) + if "error" in deletedmed: + error.append(deletedmed["error"]) for media in medias_formated: - updatemed = __salt__['zabbix.user_addmedia'](userids=userid, - active=media['active'], - mediatypeid=media['mediatypeid'], - period=media['period'], - sendto=media['sendto'], - severity=media['severity'], - **connection_args) + updatemed = __salt__["zabbix.user_addmedia"]( + userids=userid, + active=media["active"], + mediatypeid=media["mediatypeid"], + period=media["period"], + sendto=media["sendto"], + severity=media["severity"], + **connection_args + ) - if 'error' in updatemed: - error.append(updatemed['error']) + if "error" in updatemed: + error.append(updatemed["error"]) - ret['changes']['medias'] = six.text_type(medias_formated) + ret["changes"]["medias"] = six.text_type(medias_formated) else: - ret['comment'] = comment_user_exists + ret["comment"] = comment_user_exists else: - user_create = __salt__['zabbix.user_create'](alias, passwd, usrgrps, **kwargs) + user_create = __salt__["zabbix.user_create"](alias, passwd, usrgrps, **kwargs) - if 'error' not in user_create: - ret['result'] = True - ret['comment'] = comment_user_created - ret['changes'] = changes_user_created + if "error" not in user_create: + ret["result"] = True + ret["comment"] = comment_user_created + ret["changes"] = changes_user_created else: - ret['result'] = False - ret['comment'] = comment_user_notcreated + six.text_type(user_create['error']) + ret["result"] = False + ret["comment"] = comment_user_notcreated + six.text_type( + user_create["error"] + ) # error detected if error: - ret['changes'] = {} - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["changes"] = {} + ret["result"] = False + ret["comment"] = six.text_type(error) return ret def absent(name, **kwargs): - ''' + """ Ensures that the user does not exist, eventually delete user. .. versionadded:: 2016.3.0 @@ -368,54 +407,58 @@ def absent(name, **kwargs): George: zabbix_user.absent - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_user_deleted = 'USer {0} deleted.'.format(name) - comment_user_notdeleted = 'Unable to delete user: {0}. '.format(name) - comment_user_notexists = 'User {0} does not exist.'.format(name) - changes_user_deleted = {name: {'old': 'User {0} exists.'.format(name), - 'new': 'User {0} deleted.'.format(name), - } - } + comment_user_deleted = "USer {0} deleted.".format(name) + comment_user_notdeleted = "Unable to delete user: {0}. ".format(name) + comment_user_notexists = "User {0} does not exist.".format(name) + changes_user_deleted = { + name: { + "old": "User {0} exists.".format(name), + "new": "User {0} deleted.".format(name), + } + } - user_get = __salt__['zabbix.user_get'](name, **connection_args) + user_get = __salt__["zabbix.user_get"](name, **connection_args) # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if not user_get: - ret['result'] = True - ret['comment'] = comment_user_notexists + ret["result"] = True + ret["comment"] = comment_user_notexists else: - ret['result'] = None - ret['comment'] = comment_user_deleted - ret['changes'] = changes_user_deleted + ret["result"] = None + ret["comment"] = comment_user_deleted + ret["changes"] = changes_user_deleted if not user_get: - ret['result'] = True - ret['comment'] = comment_user_notexists + ret["result"] = True + ret["comment"] = comment_user_notexists else: try: - userid = user_get[0]['userid'] - user_delete = __salt__['zabbix.user_delete'](userid, **connection_args) + userid = user_get[0]["userid"] + user_delete = __salt__["zabbix.user_delete"](userid, **connection_args) except KeyError: user_delete = False - if user_delete and 'error' not in user_delete: - ret['result'] = True - ret['comment'] = comment_user_deleted - ret['changes'] = changes_user_deleted + if user_delete and "error" not in user_delete: + ret["result"] = True + ret["comment"] = comment_user_deleted + ret["changes"] = changes_user_deleted else: - ret['result'] = False - ret['comment'] = comment_user_notdeleted + six.text_type(user_delete['error']) + ret["result"] = False + ret["comment"] = comment_user_notdeleted + six.text_type( + user_delete["error"] + ) return ret diff --git a/salt/states/zabbix_usergroup.py b/salt/states/zabbix_usergroup.py index 5110f1b58f4..c7d854a1cf0 100644 --- a/salt/states/zabbix_usergroup.py +++ b/salt/states/zabbix_usergroup.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Management of Zabbix user groups. :codeauthor: Jiri Kotlin <jiri.kotlin@ultimum.io> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -14,14 +14,16 @@ from salt.ext import six def __virtual__(): - ''' + """ Only make these states available if Zabbix module is available. - ''' - return 'zabbix.usergroup_create' in __salt__ + """ + if "zabbix.usergroup_create" in __salt__: + return True + return (False, "zabbix module could not be loaded") def present(name, **kwargs): - ''' + """ Creates new user group. NOTE: This function accepts all standard user group properties: keyword argument names differ depending on your zabbix version, see: @@ -43,54 +45,56 @@ def present(name, **kwargs): - debug_mode: 0 - users_status: 0 - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_usergroup_created = 'User group {0} created.'.format(name) - comment_usergroup_updated = 'User group {0} updated.'.format(name) - comment_usergroup_notcreated = 'Unable to create user group: {0}. '.format(name) - comment_usergroup_exists = 'User group {0} already exists.'.format(name) - changes_usergroup_created = {name: {'old': 'User group {0} does not exist.'.format(name), - 'new': 'User group {0} created.'.format(name), - } - } + comment_usergroup_created = "User group {0} created.".format(name) + comment_usergroup_updated = "User group {0} updated.".format(name) + comment_usergroup_notcreated = "Unable to create user group: {0}. ".format(name) + comment_usergroup_exists = "User group {0} already exists.".format(name) + changes_usergroup_created = { + name: { + "old": "User group {0} does not exist.".format(name), + "new": "User group {0} created.".format(name), + } + } - usergroup_exists = __salt__['zabbix.usergroup_exists'](name, **connection_args) + usergroup_exists = __salt__["zabbix.usergroup_exists"](name, **connection_args) if usergroup_exists: - usergroup = __salt__['zabbix.usergroup_get'](name, **connection_args)[0] - usrgrpid = int(usergroup['usrgrpid']) + usergroup = __salt__["zabbix.usergroup_get"](name, **connection_args)[0] + usrgrpid = int(usergroup["usrgrpid"]) update_debug_mode = False update_gui_access = False update_users_status = False update_rights = False - if 'debug_mode' in kwargs: - if int(kwargs['debug_mode']) != int(usergroup['debug_mode']): + if "debug_mode" in kwargs: + if int(kwargs["debug_mode"]) != int(usergroup["debug_mode"]): update_debug_mode = True - if 'gui_access' in kwargs: - if int(kwargs['gui_access']) != int(usergroup['gui_access']): + if "gui_access" in kwargs: + if int(kwargs["gui_access"]) != int(usergroup["gui_access"]): update_gui_access = True - if 'rights' in kwargs: + if "rights" in kwargs: # Older versions of Zabbix do not return the list of rights for the user group, handle this gracefully try: - if usergroup['rights']: + if usergroup["rights"]: # Make sure right values are strings so we can compare them with the current user group rights - for right in kwargs['rights']: + for right in kwargs["rights"]: for key in right: right[key] = six.text_type(right[key]) - if sorted(kwargs['rights']) != sorted(usergroup['rights']): + if sorted(kwargs["rights"]) != sorted(usergroup["rights"]): update_rights = True else: update_rights = True @@ -98,92 +102,104 @@ def present(name, **kwargs): # As we don't know the current permissions, overwrite them as provided in the state. update_rights = True - if 'users_status' in kwargs: - if int(kwargs['users_status']) != int(usergroup['users_status']): + if "users_status" in kwargs: + if int(kwargs["users_status"]) != int(usergroup["users_status"]): update_users_status = True # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if usergroup_exists: - if update_debug_mode or update_gui_access or update_rights or update_users_status: - ret['result'] = None - ret['comment'] = comment_usergroup_updated + if ( + update_debug_mode + or update_gui_access + or update_rights + or update_users_status + ): + ret["result"] = None + ret["comment"] = comment_usergroup_updated else: - ret['result'] = True - ret['comment'] = comment_usergroup_exists + ret["result"] = True + ret["comment"] = comment_usergroup_exists else: - ret['result'] = None - ret['comment'] = comment_usergroup_created + ret["result"] = None + ret["comment"] = comment_usergroup_created return ret error = [] if usergroup_exists: - if update_debug_mode or update_gui_access or update_rights or update_users_status: - ret['result'] = True - ret['comment'] = comment_usergroup_updated + if ( + update_debug_mode + or update_gui_access + or update_rights + or update_users_status + ): + ret["result"] = True + ret["comment"] = comment_usergroup_updated if update_debug_mode: - updated_debug = __salt__['zabbix.usergroup_update'](usrgrpid, - debug_mode=kwargs['debug_mode'], - **connection_args) - if 'error' in updated_debug: - error.append(updated_debug['error']) + updated_debug = __salt__["zabbix.usergroup_update"]( + usrgrpid, debug_mode=kwargs["debug_mode"], **connection_args + ) + if "error" in updated_debug: + error.append(updated_debug["error"]) else: - ret['changes']['debug_mode'] = kwargs['debug_mode'] + ret["changes"]["debug_mode"] = kwargs["debug_mode"] if update_gui_access: - updated_gui = __salt__['zabbix.usergroup_update'](usrgrpid, - gui_access=kwargs['gui_access'], - **connection_args) - if 'error' in updated_gui: - error.append(updated_gui['error']) + updated_gui = __salt__["zabbix.usergroup_update"]( + usrgrpid, gui_access=kwargs["gui_access"], **connection_args + ) + if "error" in updated_gui: + error.append(updated_gui["error"]) else: - ret['changes']['gui_access'] = kwargs['gui_access'] + ret["changes"]["gui_access"] = kwargs["gui_access"] if update_rights: - updated_rights = __salt__['zabbix.usergroup_update'](usrgrpid, - rights=kwargs['rights'], - **connection_args) - if 'error' in updated_rights: - error.append(updated_rights['error']) + updated_rights = __salt__["zabbix.usergroup_update"]( + usrgrpid, rights=kwargs["rights"], **connection_args + ) + if "error" in updated_rights: + error.append(updated_rights["error"]) else: - ret['changes']['rights'] = kwargs['rights'] + ret["changes"]["rights"] = kwargs["rights"] if update_users_status: - updated_status = __salt__['zabbix.usergroup_update'](usrgrpid, - users_status=kwargs['users_status'], - **connection_args) - if 'error' in updated_status: - error.append(updated_status['error']) + updated_status = __salt__["zabbix.usergroup_update"]( + usrgrpid, users_status=kwargs["users_status"], **connection_args + ) + if "error" in updated_status: + error.append(updated_status["error"]) else: - ret['changes']['users_status'] = kwargs['users_status'] + ret["changes"]["users_status"] = kwargs["users_status"] else: - ret['result'] = True - ret['comment'] = comment_usergroup_exists + ret["result"] = True + ret["comment"] = comment_usergroup_exists else: - usergroup_create = __salt__['zabbix.usergroup_create'](name, **kwargs) + usergroup_create = __salt__["zabbix.usergroup_create"](name, **kwargs) - if 'error' not in usergroup_create: - ret['result'] = True - ret['comment'] = comment_usergroup_created - ret['changes'] = changes_usergroup_created + if "error" not in usergroup_create: + ret["result"] = True + ret["comment"] = comment_usergroup_created + ret["changes"] = changes_usergroup_created else: - ret['result'] = False - ret['comment'] = comment_usergroup_notcreated + six.text_type(usergroup_create['error']) + ret["result"] = False + ret["comment"] = comment_usergroup_notcreated + six.text_type( + usergroup_create["error"] + ) # error detected if error: - ret['changes'] = {} - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["changes"] = {} + ret["result"] = False + ret["comment"] = six.text_type(error) return ret def absent(name, **kwargs): - ''' + """ Ensures that the user group does not exist, eventually delete user group. .. versionadded:: 2016.3.0 @@ -198,56 +214,62 @@ def absent(name, **kwargs): delete_thai_monks_usrgrp: zabbix_usergroup.absent: - name: 'Thai monks' - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages - comment_usergroup_deleted = 'User group {0} deleted.'.format(name) - comment_usergroup_notdeleted = 'Unable to delete user group: {0}. '.format(name) - comment_usergroup_notexists = 'User group {0} does not exist.'.format(name) - changes_usergroup_deleted = {name: {'old': 'User group {0} exists.'.format(name), - 'new': 'User group {0} deleted.'.format(name), - } - } + comment_usergroup_deleted = "User group {0} deleted.".format(name) + comment_usergroup_notdeleted = "Unable to delete user group: {0}. ".format(name) + comment_usergroup_notexists = "User group {0} does not exist.".format(name) + changes_usergroup_deleted = { + name: { + "old": "User group {0} exists.".format(name), + "new": "User group {0} deleted.".format(name), + } + } - usergroup_exists = __salt__['zabbix.usergroup_exists'](name, **connection_args) + usergroup_exists = __salt__["zabbix.usergroup_exists"](name, **connection_args) # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if not usergroup_exists: - ret['result'] = True - ret['comment'] = comment_usergroup_notexists + ret["result"] = True + ret["comment"] = comment_usergroup_notexists else: - ret['result'] = None - ret['comment'] = comment_usergroup_deleted + ret["result"] = None + ret["comment"] = comment_usergroup_deleted return ret - usergroup_get = __salt__['zabbix.usergroup_get'](name, **connection_args) + usergroup_get = __salt__["zabbix.usergroup_get"](name, **connection_args) if not usergroup_get: - ret['result'] = True - ret['comment'] = comment_usergroup_notexists + ret["result"] = True + ret["comment"] = comment_usergroup_notexists else: try: - usrgrpid = usergroup_get[0]['usrgrpid'] - usergroup_delete = __salt__['zabbix.usergroup_delete'](usrgrpid, **connection_args) + usrgrpid = usergroup_get[0]["usrgrpid"] + usergroup_delete = __salt__["zabbix.usergroup_delete"]( + usrgrpid, **connection_args + ) except KeyError: usergroup_delete = False - if usergroup_delete and 'error' not in usergroup_delete: - ret['result'] = True - ret['comment'] = comment_usergroup_deleted - ret['changes'] = changes_usergroup_deleted + if usergroup_delete and "error" not in usergroup_delete: + ret["result"] = True + ret["comment"] = comment_usergroup_deleted + ret["changes"] = changes_usergroup_deleted else: - ret['result'] = False - ret['comment'] = comment_usergroup_notdeleted + six.text_type(usergroup_delete['error']) + ret["result"] = False + ret["comment"] = comment_usergroup_notdeleted + six.text_type( + usergroup_delete["error"] + ) return ret diff --git a/salt/states/zabbix_usermacro.py b/salt/states/zabbix_usermacro.py index d37017ec5d3..874904edc00 100644 --- a/salt/states/zabbix_usermacro.py +++ b/salt/states/zabbix_usermacro.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Management of Zabbix usermacros. :codeauthor: Raymond Kuiper <qix@the-wired.net> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -13,14 +13,16 @@ from salt.ext import six def __virtual__(): - ''' + """ Only make these states available if Zabbix module is available. - ''' - return 'zabbix.usermacro_create' in __salt__ + """ + if "zabbix.usermacro_create" in __salt__: + return True + return (False, "zabbix module could not be loaded") def present(name, value, hostid=None, **kwargs): - ''' + """ Creates a new usermacro. :param name: name of the usermacro @@ -39,121 +41,145 @@ def present(name, value, hostid=None, **kwargs): - value: 'public' - hostid: 21 - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages if hostid: - comment_usermacro_created = 'Usermacro {0} created on hostid {1}.'.format(name, hostid) - comment_usermacro_updated = 'Usermacro {0} updated on hostid {1}.'.format(name, hostid) - comment_usermacro_notcreated = 'Unable to create usermacro: {0} on hostid {1}. '.format(name, hostid) - comment_usermacro_exists = 'Usermacro {0} already exists on hostid {1}.'.format(name, hostid) - changes_usermacro_created = {name: {'old': 'Usermacro {0} does not exist on hostid {1}.'.format(name, hostid), - 'new': 'Usermacro {0} created on hostid {1}.'.format(name, hostid), - } - } + comment_usermacro_created = "Usermacro {0} created on hostid {1}.".format( + name, hostid + ) + comment_usermacro_updated = "Usermacro {0} updated on hostid {1}.".format( + name, hostid + ) + comment_usermacro_notcreated = "Unable to create usermacro: {0} on hostid {1}. ".format( + name, hostid + ) + comment_usermacro_exists = "Usermacro {0} already exists on hostid {1}.".format( + name, hostid + ) + changes_usermacro_created = { + name: { + "old": "Usermacro {0} does not exist on hostid {1}.".format( + name, hostid + ), + "new": "Usermacro {0} created on hostid {1}.".format(name, hostid), + } + } else: - comment_usermacro_created = 'Usermacro {0} created.'.format(name) - comment_usermacro_updated = 'Usermacro {0} updated.'.format(name) - comment_usermacro_notcreated = 'Unable to create usermacro: {0}. '.format(name) - comment_usermacro_exists = 'Usermacro {0} already exists.'.format(name) - changes_usermacro_created = {name: {'old': 'Usermacro {0} does not exist.'.format(name), - 'new': 'Usermacro {0} created.'.format(name), - } - } + comment_usermacro_created = "Usermacro {0} created.".format(name) + comment_usermacro_updated = "Usermacro {0} updated.".format(name) + comment_usermacro_notcreated = "Unable to create usermacro: {0}. ".format(name) + comment_usermacro_exists = "Usermacro {0} already exists.".format(name) + changes_usermacro_created = { + name: { + "old": "Usermacro {0} does not exist.".format(name), + "new": "Usermacro {0} created.".format(name), + } + } # Zabbix API expects script parameters as a string of arguments seperated by newline characters - if 'exec_params' in kwargs: - if isinstance(kwargs['exec_params'], list): - kwargs['exec_params'] = '\n'.join(kwargs['exec_params'])+'\n' + if "exec_params" in kwargs: + if isinstance(kwargs["exec_params"], list): + kwargs["exec_params"] = "\n".join(kwargs["exec_params"]) + "\n" else: - kwargs['exec_params'] = six.text_type(kwargs['exec_params'])+'\n' + kwargs["exec_params"] = six.text_type(kwargs["exec_params"]) + "\n" if hostid: - usermacro_exists = __salt__['zabbix.usermacro_get'](name, hostids=hostid, **connection_args) + usermacro_exists = __salt__["zabbix.usermacro_get"]( + name, hostids=hostid, **connection_args + ) else: - usermacro_exists = __salt__['zabbix.usermacro_get'](name, globalmacro=True, **connection_args) + usermacro_exists = __salt__["zabbix.usermacro_get"]( + name, globalmacro=True, **connection_args + ) if usermacro_exists: usermacroobj = usermacro_exists[0] if hostid: - usermacroid = int(usermacroobj['hostmacroid']) + usermacroid = int(usermacroobj["hostmacroid"]) else: - usermacroid = int(usermacroobj['globalmacroid']) + usermacroid = int(usermacroobj["globalmacroid"]) update_value = False - if six.text_type(value) != usermacroobj['value']: + if six.text_type(value) != usermacroobj["value"]: update_value = True # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if usermacro_exists: if update_value: - ret['result'] = None - ret['comment'] = comment_usermacro_updated + ret["result"] = None + ret["comment"] = comment_usermacro_updated else: - ret['result'] = True - ret['comment'] = comment_usermacro_exists + ret["result"] = True + ret["comment"] = comment_usermacro_exists else: - ret['result'] = None - ret['comment'] = comment_usermacro_created + ret["result"] = None + ret["comment"] = comment_usermacro_created return ret error = [] if usermacro_exists: if update_value: - ret['result'] = True - ret['comment'] = comment_usermacro_updated + ret["result"] = True + ret["comment"] = comment_usermacro_updated if hostid: - updated_value = __salt__['zabbix.usermacro_update'](usermacroid, - value=value, - **connection_args) + updated_value = __salt__["zabbix.usermacro_update"]( + usermacroid, value=value, **connection_args + ) else: - updated_value = __salt__['zabbix.usermacro_updateglobal'](usermacroid, - value=value, - **connection_args) + updated_value = __salt__["zabbix.usermacro_updateglobal"]( + usermacroid, value=value, **connection_args + ) if not isinstance(updated_value, int): - if 'error' in updated_value: - error.append(updated_value['error']) + if "error" in updated_value: + error.append(updated_value["error"]) else: - ret['changes']['value'] = value + ret["changes"]["value"] = value else: - ret['result'] = True - ret['comment'] = comment_usermacro_exists + ret["result"] = True + ret["comment"] = comment_usermacro_exists else: if hostid: - usermacro_create = __salt__['zabbix.usermacro_create'](name, value, hostid, **connection_args) + usermacro_create = __salt__["zabbix.usermacro_create"]( + name, value, hostid, **connection_args + ) else: - usermacro_create = __salt__['zabbix.usermacro_createglobal'](name, value, **connection_args) + usermacro_create = __salt__["zabbix.usermacro_createglobal"]( + name, value, **connection_args + ) - if 'error' not in usermacro_create: - ret['result'] = True - ret['comment'] = comment_usermacro_created - ret['changes'] = changes_usermacro_created + if "error" not in usermacro_create: + ret["result"] = True + ret["comment"] = comment_usermacro_created + ret["changes"] = changes_usermacro_created else: - ret['result'] = False - ret['comment'] = comment_usermacro_notcreated + six.text_type(usermacro_create['error']) + ret["result"] = False + ret["comment"] = comment_usermacro_notcreated + six.text_type( + usermacro_create["error"] + ) # error detected if error: - ret['changes'] = {} - ret['result'] = False - ret['comment'] = six.text_type(error) + ret["changes"] = {} + ret["result"] = False + ret["comment"] = six.text_type(error) return ret def absent(name, hostid=None, **kwargs): - ''' + """ Ensures that the mediatype does not exist, eventually deletes the mediatype. :param name: name of the usermacro @@ -169,69 +195,89 @@ def absent(name, hostid=None, **kwargs): zabbix_usermacro.absent: - name: '{$SNMP_COMMUNITY}' - ''' + """ connection_args = {} - if '_connection_user' in kwargs: - connection_args['_connection_user'] = kwargs['_connection_user'] - if '_connection_password' in kwargs: - connection_args['_connection_password'] = kwargs['_connection_password'] - if '_connection_url' in kwargs: - connection_args['_connection_url'] = kwargs['_connection_url'] + if "_connection_user" in kwargs: + connection_args["_connection_user"] = kwargs["_connection_user"] + if "_connection_password" in kwargs: + connection_args["_connection_password"] = kwargs["_connection_password"] + if "_connection_url" in kwargs: + connection_args["_connection_url"] = kwargs["_connection_url"] - ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} # Comment and change messages if hostid: - comment_usermacro_deleted = 'Usermacro {0} deleted from hostid {1}.'.format(name, hostid) - comment_usermacro_notdeleted = 'Unable to delete usermacro: {0} from hostid {1}.'.format(name, hostid) - comment_usermacro_notexists = 'Usermacro {0} does not exist on hostid {1}.'.format(name, hostid) - changes_usermacro_deleted = {name: {'old': 'Usermacro {0} exists on hostid {1}.'.format(name, hostid), - 'new': 'Usermacro {0} deleted from {1}.'.format(name, hostid), - } - } + comment_usermacro_deleted = "Usermacro {0} deleted from hostid {1}.".format( + name, hostid + ) + comment_usermacro_notdeleted = "Unable to delete usermacro: {0} from hostid {1}.".format( + name, hostid + ) + comment_usermacro_notexists = "Usermacro {0} does not exist on hostid {1}.".format( + name, hostid + ) + changes_usermacro_deleted = { + name: { + "old": "Usermacro {0} exists on hostid {1}.".format(name, hostid), + "new": "Usermacro {0} deleted from {1}.".format(name, hostid), + } + } else: - comment_usermacro_deleted = 'Usermacro {0} deleted.'.format(name) - comment_usermacro_notdeleted = 'Unable to delete usermacro: {0}.'.format(name) - comment_usermacro_notexists = 'Usermacro {0} does not exist.'.format(name) - changes_usermacro_deleted = {name: {'old': 'Usermacro {0} exists.'.format(name), - 'new': 'Usermacro {0} deleted.'.format(name), - } - } + comment_usermacro_deleted = "Usermacro {0} deleted.".format(name) + comment_usermacro_notdeleted = "Unable to delete usermacro: {0}.".format(name) + comment_usermacro_notexists = "Usermacro {0} does not exist.".format(name) + changes_usermacro_deleted = { + name: { + "old": "Usermacro {0} exists.".format(name), + "new": "Usermacro {0} deleted.".format(name), + } + } if hostid: - usermacro_exists = __salt__['zabbix.usermacro_get'](name, hostids=hostid, **connection_args) + usermacro_exists = __salt__["zabbix.usermacro_get"]( + name, hostids=hostid, **connection_args + ) else: - usermacro_exists = __salt__['zabbix.usermacro_get'](name, globalmacro=True, **connection_args) + usermacro_exists = __salt__["zabbix.usermacro_get"]( + name, globalmacro=True, **connection_args + ) # Dry run, test=true mode - if __opts__['test']: + if __opts__["test"]: if not usermacro_exists: - ret['result'] = True - ret['comment'] = comment_usermacro_notexists + ret["result"] = True + ret["comment"] = comment_usermacro_notexists else: - ret['result'] = None - ret['comment'] = comment_usermacro_deleted + ret["result"] = None + ret["comment"] = comment_usermacro_deleted return ret if not usermacro_exists: - ret['result'] = True - ret['comment'] = comment_usermacro_notexists + ret["result"] = True + ret["comment"] = comment_usermacro_notexists else: try: if hostid: - usermacroid = usermacro_exists[0]['hostmacroid'] - usermacro_delete = __salt__['zabbix.usermacro_delete'](usermacroid, **connection_args) + usermacroid = usermacro_exists[0]["hostmacroid"] + usermacro_delete = __salt__["zabbix.usermacro_delete"]( + usermacroid, **connection_args + ) else: - usermacroid = usermacro_exists[0]['globalmacroid'] - usermacro_delete = __salt__['zabbix.usermacro_deleteglobal'](usermacroid, **connection_args) + usermacroid = usermacro_exists[0]["globalmacroid"] + usermacro_delete = __salt__["zabbix.usermacro_deleteglobal"]( + usermacroid, **connection_args + ) except KeyError: usermacro_delete = False - if usermacro_delete and 'error' not in usermacro_delete: - ret['result'] = True - ret['comment'] = comment_usermacro_deleted - ret['changes'] = changes_usermacro_deleted + if usermacro_delete and "error" not in usermacro_delete: + ret["result"] = True + ret["comment"] = comment_usermacro_deleted + ret["changes"] = changes_usermacro_deleted else: - ret['result'] = False - ret['comment'] = comment_usermacro_notdeleted + six.text_type(usermacro_delete['error']) + ret["result"] = False + ret["comment"] = comment_usermacro_notdeleted + six.text_type( + usermacro_delete["error"] + ) return ret diff --git a/salt/states/zabbix_valuemap.py b/salt/states/zabbix_valuemap.py index a34ccf25fcb..b863b347758 100644 --- a/salt/states/zabbix_valuemap.py +++ b/salt/states/zabbix_valuemap.py @@ -1,19 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" .. versionadded:: 2017.7 Management of Zabbix Valuemap object over Zabbix API. :codeauthor: Jakub Sliva <jakub.sliva@ultimum.io> -''' -from __future__ import absolute_import -from __future__ import unicode_literals -import logging +""" +from __future__ import absolute_import, unicode_literals + import json +import logging try: from salt.ext import six from salt.exceptions import SaltException + IMPORTS_OK = True except ImportError: IMPORTS_OK = False @@ -23,17 +24,17 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only make these states available if Zabbix module and run_query function is available and all 3rd party modules imported. - ''' - if 'zabbix.run_query' in __salt__ and IMPORTS_OK: + """ + if "zabbix.run_query" in __salt__ and IMPORTS_OK: return True - return False, 'Import zabbix or other needed modules failed.' + return False, "Import zabbix or other needed modules failed." def present(name, params, **kwargs): - ''' + """ Creates Zabbix Value map object or if differs update it according defined parameters :param name: Zabbix Value map name @@ -53,81 +54,131 @@ def present(name, params, **kwargs): newvalue: one - value: 2 newvalue: two - ''' - zabbix_id_mapper = __salt__['zabbix.get_zabbix_id_mapper']() + """ + zabbix_id_mapper = __salt__["zabbix.get_zabbix_id_mapper"]() - dry_run = __opts__['test'] - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + dry_run = __opts__["test"] + ret = {"name": name, "result": False, "comment": "", "changes": {}} # Create input params substituting functions with their results - params['name'] = name - input_params = __salt__['zabbix.substitute_params'](params, **kwargs) - log.info('Zabbix Value map: input params: %s', six.text_type(json.dumps(input_params, indent=4))) + params["name"] = name + input_params = __salt__["zabbix.substitute_params"](params, **kwargs) + log.info( + "Zabbix Value map: input params: %s", + six.text_type(json.dumps(input_params, indent=4)), + ) - search = {'output': 'extend', - 'selectMappings': 'extend', - 'filter': { - 'name': name - }} + search = {"output": "extend", "selectMappings": "extend", "filter": {"name": name}} # GET Value map object if exists - valuemap_get = __salt__['zabbix.run_query']('valuemap.get', search, **kwargs) - log.info('Zabbix Value map: valuemap.get result: %s', six.text_type(json.dumps(valuemap_get, indent=4))) + valuemap_get = __salt__["zabbix.run_query"]("valuemap.get", search, **kwargs) + log.info( + "Zabbix Value map: valuemap.get result: %s", + six.text_type(json.dumps(valuemap_get, indent=4)), + ) - existing_obj = __salt__['zabbix.substitute_params'](valuemap_get[0], **kwargs) \ - if valuemap_get and len(valuemap_get) == 1 else False + existing_obj = ( + __salt__["zabbix.substitute_params"](valuemap_get[0], **kwargs) + if valuemap_get and len(valuemap_get) == 1 + else False + ) if existing_obj: - diff_params = __salt__['zabbix.compare_params'](input_params, existing_obj) - log.info('Zabbix Value map: input params: {%s', six.text_type(json.dumps(input_params, indent=4))) - log.info('Zabbix Value map: Object comparison result. Differences: %s', six.text_type(diff_params)) + diff_params = __salt__["zabbix.compare_params"](input_params, existing_obj) + log.info( + "Zabbix Value map: input params: {%s", + six.text_type(json.dumps(input_params, indent=4)), + ) + log.info( + "Zabbix Value map: Object comparison result. Differences: %s", + six.text_type(diff_params), + ) if diff_params: - diff_params[zabbix_id_mapper['valuemap']] = existing_obj[zabbix_id_mapper['valuemap']] - log.info('Zabbix Value map: update params: %s', six.text_type(json.dumps(diff_params, indent=4))) + diff_params[zabbix_id_mapper["valuemap"]] = existing_obj[ + zabbix_id_mapper["valuemap"] + ] + log.info( + "Zabbix Value map: update params: %s", + six.text_type(json.dumps(diff_params, indent=4)), + ) if dry_run: - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" would be fixed.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" differs ' - 'in following parameters: {1}'.format(name, diff_params), - 'new': 'Zabbix Value map "{0}" would correspond to definition.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" would be fixed.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" differs ' + "in following parameters: {1}".format(name, diff_params), + "new": 'Zabbix Value map "{0}" would correspond to definition.'.format( + name + ), + } + } else: - valuemap_update = __salt__['zabbix.run_query']('valuemap.update', diff_params, **kwargs) - log.info('Zabbix Value map: valuemap.update result: %s', six.text_type(valuemap_update)) + valuemap_update = __salt__["zabbix.run_query"]( + "valuemap.update", diff_params, **kwargs + ) + log.info( + "Zabbix Value map: valuemap.update result: %s", + six.text_type(valuemap_update), + ) if valuemap_update: - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" updated.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" differed ' - 'in following parameters: {1}'.format(name, diff_params), - 'new': 'Zabbix Value map "{0}" fixed.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" updated.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" differed ' + "in following parameters: {1}".format(name, diff_params), + "new": 'Zabbix Value map "{0}" fixed.'.format(name), + } + } else: - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" already exists and corresponds to a definition.'.format(name) + ret["result"] = True + ret[ + "comment" + ] = 'Zabbix Value map "{0}" already exists and corresponds to a definition.'.format( + name + ) else: if dry_run: - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" would be created.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" does not exist.'.format(name), - 'new': 'Zabbix Value map "{0}" would be created ' - 'according definition.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" would be created.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" does not exist.'.format(name), + "new": 'Zabbix Value map "{0}" would be created ' + "according definition.".format(name), + } + } else: # ACTION.CREATE - valuemap_create = __salt__['zabbix.run_query']('valuemap.create', input_params, **kwargs) - log.info('Zabbix Value map: valuemap.create result: %s', six.text_type(valuemap_create)) + valuemap_create = __salt__["zabbix.run_query"]( + "valuemap.create", input_params, **kwargs + ) + log.info( + "Zabbix Value map: valuemap.create result: %s", + six.text_type(valuemap_create), + ) if valuemap_create: - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" created.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" did not exist.'.format(name), - 'new': 'Zabbix Value map "{0}" created according definition.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" created.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" did not exist.'.format(name), + "new": 'Zabbix Value map "{0}" created according definition.'.format( + name + ), + } + } return ret def absent(name, **kwargs): - ''' + """ Makes the Zabbix Value map to be absent (either does not exist or delete it). :param name: Zabbix Value map name @@ -140,31 +191,43 @@ def absent(name, **kwargs): zabbix-valuemap-absent: zabbix_valuemap.absent: - name: Value map name - ''' - dry_run = __opts__['test'] - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + dry_run = __opts__["test"] + ret = {"name": name, "result": False, "comment": "", "changes": {}} try: - object_id = __salt__['zabbix.get_object_id_by_params']('valuemap', {'filter': {'name': name}}, **kwargs) + object_id = __salt__["zabbix.get_object_id_by_params"]( + "valuemap", {"filter": {"name": name}}, **kwargs + ) except SaltException: object_id = False if not object_id: - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" does not exist.'.format(name) + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" does not exist.'.format(name) else: if dry_run: - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" would be deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" exists.'.format(name), - 'new': 'Zabbix Value map "{0}" would be deleted.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" would be deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" exists.'.format(name), + "new": 'Zabbix Value map "{0}" would be deleted.'.format(name), + } + } else: - valuemap_delete = __salt__['zabbix.run_query']('valuemap.delete', [object_id], **kwargs) + valuemap_delete = __salt__["zabbix.run_query"]( + "valuemap.delete", [object_id], **kwargs + ) if valuemap_delete: - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" existed.'.format(name), - 'new': 'Zabbix Value map "{0}" deleted.'.format(name)}} + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" existed.'.format(name), + "new": 'Zabbix Value map "{0}" deleted.'.format(name), + } + } return ret diff --git a/salt/states/zcbuildout.py b/salt/states/zcbuildout.py index 0c8dd0248d5..2678eb5fc9a 100644 --- a/salt/states/zcbuildout.py +++ b/salt/states/zcbuildout.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of zc.buildout ========================= @@ -34,7 +34,7 @@ Available Functions - unless: /bin/test_something_installed - onlyif: /bin/test_else_installed -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -45,19 +45,21 @@ import sys from salt.ext.six import string_types # Define the module's virtual name -__virtualname__ = 'buildout' +__virtualname__ = "buildout" log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if zc.buildout libs available - ''' - return __virtualname__ + """ + if "buildout.buildout" in __salt__: + return __virtualname__ + return (False, "buildout module could not be loaded") -INVALID_RESPONSE = 'We did not get any expectable answer from docker' -VALID_RESPONSE = '' +INVALID_RESPONSE = "We did not get any expectable answer from docker" +VALID_RESPONSE = "" NOTSET = object() MAPPING_CACHE = {} FN_CACHE = {} @@ -69,76 +71,79 @@ def __salt(fn): return FN_CACHE[fn] -def _ret_status(exec_status=None, - name='', - comment='', - result=None, - quiet=False, - changes=None): +def _ret_status( + exec_status=None, name="", comment="", result=None, quiet=False, changes=None +): if not changes: changes = {} if exec_status is None: exec_status = {} if exec_status: if result is None: - result = exec_status['status'] - scomment = exec_status.get('comment', None) + result = exec_status["status"] + scomment = exec_status.get("comment", None) if scomment: - comment += '\n' + scomment - out = exec_status.get('out', '') + comment += "\n" + scomment + out = exec_status.get("out", "") if not quiet: if out: if isinstance(out, string_types): - comment += '\n' + out - outlog = exec_status.get('outlog', None) + comment += "\n" + out + outlog = exec_status.get("outlog", None) if outlog: if isinstance(outlog, string_types): - comment += '\n' + outlog + comment += "\n" + outlog return { - 'changes': changes, - 'result': result, - 'name': name, - 'comment': comment, + "changes": changes, + "result": result, + "name": name, + "comment": comment, } -def _valid(exec_status=None, name='', comment='', changes=None): - return _ret_status(exec_status=exec_status, - comment=comment, - name=name, - changes=changes, - result=True) +def _valid(exec_status=None, name="", comment="", changes=None): + return _ret_status( + exec_status=exec_status, + comment=comment, + name=name, + changes=changes, + result=True, + ) -def _invalid(exec_status=None, name='', comment='', changes=None): - return _ret_status(exec_status=exec_status, - comment=comment, - name=name, - changes=changes, - result=False) +def _invalid(exec_status=None, name="", comment="", changes=None): + return _ret_status( + exec_status=exec_status, + comment=comment, + name=name, + changes=changes, + result=False, + ) -def installed(name, - config='buildout.cfg', - quiet=False, - parts=None, - user=None, - env=(), - buildout_ver=None, - test_release=False, - distribute=None, - new_st=None, - offline=False, - newest=False, - python=sys.executable, - debug=False, - verbose=False, - unless=None, - onlyif=None, - use_vt=False, - loglevel='debug', - **kwargs): - ''' +def installed( + name, + config="buildout.cfg", + quiet=False, + parts=None, + user=None, + env=(), + buildout_ver=None, + test_release=False, + distribute=None, + new_st=None, + offline=False, + newest=False, + python=sys.executable, + debug=False, + verbose=False, + unless=None, + onlyif=None, + use_vt=False, + loglevel="debug", + **kwargs +): + """ Install buildout in a specific directory It is a thin wrapper to modules.buildout.buildout @@ -202,21 +207,22 @@ def installed(name, loglevel loglevel for buildout commands - ''' + """ ret = {} - if 'group' in kwargs: + if "group" in kwargs: log.warning("Passing 'group' is deprecated, just remove it") - output_loglevel = kwargs.get('output_loglevel', None) + output_loglevel = kwargs.get("output_loglevel", None) if output_loglevel and not loglevel: - log.warning("Passing 'output_loglevel' is deprecated," - ' please use loglevel instead') + log.warning( + "Passing 'output_loglevel' is deprecated," " please use loglevel instead" + ) try: test_release = int(test_release) except ValueError: test_release = None - func = __salt('buildout.buildout') + func = __salt("buildout.buildout") kwargs = dict( directory=name, config=config, @@ -235,7 +241,7 @@ def installed(name, onlyif=onlyif, unless=unless, use_vt=use_vt, - loglevel=loglevel + loglevel=loglevel, ) ret.update(_ret_status(func(**kwargs), name, quiet=quiet)) return ret diff --git a/salt/states/zenoss.py b/salt/states/zenoss.py index 820da4b9d72..f7762367577 100644 --- a/salt/states/zenoss.py +++ b/salt/states/zenoss.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" State to manage monitoring in Zenoss. .. versionadded:: 2016.3.0 @@ -16,26 +16,27 @@ Allows for setting a state of minions in Zenoss using the Zenoss API. Currently - device_class: /Servers/Linux - collector: localhost - prod_state: 1000 -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if the Zenoss execution module is available. - ''' - if 'zenoss.add_device' in __salt__: - return 'zenoss' + """ + if "zenoss.add_device" in __salt__: + return "zenoss" else: return False, "The zenoss execution module is not available" -def monitored(name, device_class=None, collector='localhost', prod_state=None): - ''' +def monitored(name, device_class=None, collector="localhost", prod_state=None): + """ Ensure a device is monitored. The 'name' given will be used for Zenoss device name and should be resolvable. .. code-block:: yaml @@ -46,47 +47,53 @@ def monitored(name, device_class=None, collector='localhost', prod_state=None): - device_class: /Servers/Linux - collector: localhost - prod_state: 1000 - ''' + """ ret = {} - ret['name'] = name + ret["name"] = name # If device is already monitored, return early - device = __salt__['zenoss.find_device'](name) + device = __salt__["zenoss.find_device"](name) if device: - ret['result'] = True - ret['changes'] = None - ret['comment'] = '{0} is already monitored'.format(name) + ret["result"] = True + ret["changes"] = None + ret["comment"] = "{0} is already monitored".format(name) # if prod_state is set, ensure it matches with the current state - if prod_state is not None and device['productionState'] != prod_state: - if __opts__['test']: - ret['comment'] = '{0} is already monitored but prodState will be updated'.format(name) - ret['result'] = None + if prod_state is not None and device["productionState"] != prod_state: + if __opts__["test"]: + ret[ + "comment" + ] = "{0} is already monitored but prodState will be updated".format( + name + ) + ret["result"] = None else: - __salt__['zenoss.set_prod_state'](prod_state, name) - ret['comment'] = '{0} is already monitored but prodState was updated'.format(name) + __salt__["zenoss.set_prod_state"](prod_state, name) + ret[ + "comment" + ] = "{0} is already monitored but prodState was updated".format(name) - ret['changes'] = { - 'old': 'prodState == {0}'.format(device['productionState']), - 'new': 'prodState == {0}'.format(prod_state) - } + ret["changes"] = { + "old": "prodState == {0}".format(device["productionState"]), + "new": "prodState == {0}".format(prod_state), + } return ret # Device not yet in Zenoss - if __opts__['test']: - ret['comment'] = 'The state of "{0}" will be changed.'.format(name) - ret['changes'] = {'old': 'monitored == False', 'new': 'monitored == True'} - ret['result'] = None + if __opts__["test"]: + ret["comment"] = 'The state of "{0}" will be changed.'.format(name) + ret["changes"] = {"old": "monitored == False", "new": "monitored == True"} + ret["result"] = None return ret # Add and check result - if __salt__['zenoss.add_device'](name, device_class, collector, prod_state): - ret['result'] = True - ret['changes'] = {'old': 'monitored == False', 'new': 'monitored == True'} - ret['comment'] = '{0} has been added to Zenoss'.format(name) + if __salt__["zenoss.add_device"](name, device_class, collector, prod_state): + ret["result"] = True + ret["changes"] = {"old": "monitored == False", "new": "monitored == True"} + ret["comment"] = "{0} has been added to Zenoss".format(name) else: - ret['result'] = False - ret['changes'] = None - ret['comment'] = 'Unable to add {0} to Zenoss'.format(name) + ret["result"] = False + ret["changes"] = None + ret["comment"] = "Unable to add {0} to Zenoss".format(name) return ret diff --git a/salt/states/zfs.py b/salt/states/zfs.py index 782ae487da4..a66077c2508 100644 --- a/salt/states/zfs.py +++ b/salt/states/zfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" States for managing zfs datasets :maintainer: Jorge Schrauwen <sjorge@blackdot.be> @@ -42,8 +42,8 @@ States for managing zfs datasets test/shares/moka@tsukune: zfs.snapshot_absent -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -55,26 +55,26 @@ from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) # Define the state's virtual name -__virtualname__ = 'zfs' +__virtualname__ = "zfs" # Compare modifiers for zfs.schedule_snapshot -comp_hour = {'minute': 0} -comp_day = {'minute': 0, 'hour': 0} -comp_month = {'minute': 0, 'hour': 0, 'day': 1} -comp_year = {'minute': 0, 'hour': 0, 'day': 1, 'month': 1} +comp_hour = {"minute": 0} +comp_day = {"minute": 0, "hour": 0} +comp_month = {"minute": 0, "hour": 0, "day": 1} +comp_year = {"minute": 0, "hour": 0, "day": 1, "month": 1} def __virtual__(): - ''' + """ Provides zfs state - ''' - if not __grains__.get('zfs_support'): - return False, 'The zfs state cannot be loaded: zfs not supported' + """ + if not __grains__.get("zfs_support"): + return False, "The zfs state cannot be loaded: zfs not supported" return __virtualname__ def _absent(name, dataset_type, force=False, recursive=False): - ''' + """ internal shared function for *_absent name : string @@ -86,53 +86,43 @@ def _absent(name, dataset_type, force=False, recursive=False): recursive : boolean also destroy all the child datasets - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ## log configuration dataset_type = dataset_type.lower() - log.debug('zfs.%s_absent::%s::config::force = %s', - dataset_type, name, force) - log.debug('zfs.%s_absent::%s::config::recursive = %s', - dataset_type, name, recursive) + log.debug("zfs.%s_absent::%s::config::force = %s", dataset_type, name, force) + log.debug( + "zfs.%s_absent::%s::config::recursive = %s", dataset_type, name, recursive + ) ## destroy dataset if needed - if __salt__['zfs.exists'](name, **{'type': dataset_type}): + if __salt__["zfs.exists"](name, **{"type": dataset_type}): ## NOTE: dataset found with the name and dataset_type - if not __opts__['test']: - mod_res = __salt__['zfs.destroy'](name, **{'force': force, 'recursive': recursive}) + if not __opts__["test"]: + mod_res = __salt__["zfs.destroy"]( + name, **{"force": force, "recursive": recursive} + ) else: - mod_res = OrderedDict([('destroyed', True)]) + mod_res = OrderedDict([("destroyed", True)]) - ret['result'] = mod_res['destroyed'] - if ret['result']: - ret['changes'][name] = 'destroyed' - ret['comment'] = '{0} {1} was destroyed'.format( - dataset_type, - name, - ) + ret["result"] = mod_res["destroyed"] + if ret["result"]: + ret["changes"][name] = "destroyed" + ret["comment"] = "{0} {1} was destroyed".format(dataset_type, name,) else: - ret['comment'] = 'failed to destroy {0} {1}'.format( - dataset_type, - name, - ) - if 'error' in mod_res: - ret['comment'] = mod_res['error'] + ret["comment"] = "failed to destroy {0} {1}".format(dataset_type, name,) + if "error" in mod_res: + ret["comment"] = mod_res["error"] else: ## NOTE: no dataset found with name of the dataset_type - ret['comment'] = '{0} {1} is absent'.format( - dataset_type, - name - ) + ret["comment"] = "{0} {1} is absent".format(dataset_type, name) return ret def filesystem_absent(name, force=False, recursive=False): - ''' + """ ensure filesystem is absent on the system name : string @@ -147,19 +137,21 @@ def filesystem_absent(name, force=False, recursive=False): If a volume with ``name`` exists, this state will succeed without destroying the volume specified by ``name``. This module is dataset type sensitive. - ''' - if not __utils__['zfs.is_dataset'](name): - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'invalid dataset name: {0}'.format(name)} + """ + if not __utils__["zfs.is_dataset"](name): + ret = { + "name": name, + "changes": {}, + "result": False, + "comment": "invalid dataset name: {0}".format(name), + } else: - ret = _absent(name, 'filesystem', force, recursive) + ret = _absent(name, "filesystem", force, recursive) return ret def volume_absent(name, force=False, recursive=False): - ''' + """ ensure volume is absent on the system name : string @@ -174,19 +166,21 @@ def volume_absent(name, force=False, recursive=False): If a filesystem with ``name`` exists, this state will succeed without destroying the filesystem specified by ``name``. This module is dataset type sensitive. - ''' - if not __utils__['zfs.is_dataset'](name): - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'invalid dataset name: {0}'.format(name)} + """ + if not __utils__["zfs.is_dataset"](name): + ret = { + "name": name, + "changes": {}, + "result": False, + "comment": "invalid dataset name: {0}".format(name), + } else: - ret = _absent(name, 'volume', force, recursive) + ret = _absent(name, "volume", force, recursive) return ret def snapshot_absent(name, force=False, recursive=False): - ''' + """ ensure snapshot is absent on the system name : string @@ -196,19 +190,21 @@ def snapshot_absent(name, force=False, recursive=False): recursive : boolean also destroy all the child datasets (zfs destroy -r) - ''' - if not __utils__['zfs.is_snapshot'](name): - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'invalid snapshot name: {0}'.format(name)} + """ + if not __utils__["zfs.is_snapshot"](name): + ret = { + "name": name, + "changes": {}, + "result": False, + "comment": "invalid snapshot name: {0}".format(name), + } else: - ret = _absent(name, 'snapshot', force, recursive) + ret = _absent(name, "snapshot", force, recursive) return ret def bookmark_absent(name, force=False, recursive=False): - ''' + """ ensure bookmark is absent on the system name : string @@ -218,19 +214,21 @@ def bookmark_absent(name, force=False, recursive=False): recursive : boolean also destroy all the child datasets (zfs destroy -r) - ''' - if not __utils__['zfs.is_bookmark'](name): - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': 'invalid bookmark name: {0}'.format(name)} + """ + if not __utils__["zfs.is_bookmark"](name): + ret = { + "name": name, + "changes": {}, + "result": False, + "comment": "invalid bookmark name: {0}".format(name), + } else: - ret = _absent(name, 'bookmark', force, recursive) + ret = _absent(name, "bookmark", force, recursive) return ret def hold_absent(name, snapshot, recursive=False): - ''' + """ ensure hold is absent on the system name : string @@ -240,67 +238,60 @@ def hold_absent(name, snapshot, recursive=False): recursive : boolean recursively releases a hold with the given tag on the snapshots of all descendent file systems. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ## log configuration - log.debug('zfs.hold_absent::%s::config::snapshot = %s', - name, snapshot) - log.debug('zfs.hold_absent::%s::config::recursive = %s', - name, recursive) + log.debug("zfs.hold_absent::%s::config::snapshot = %s", name, snapshot) + log.debug("zfs.hold_absent::%s::config::recursive = %s", name, recursive) ## check we have a snapshot/tag name - if not __utils__['zfs.is_snapshot'](snapshot): - ret['result'] = False - ret['comment'] = 'invalid snapshot name: {0}'.format(snapshot) + if not __utils__["zfs.is_snapshot"](snapshot): + ret["result"] = False + ret["comment"] = "invalid snapshot name: {0}".format(snapshot) return ret - if __utils__['zfs.is_snapshot'](name) or \ - __utils__['zfs.is_bookmark'](name) or \ - name == 'error': - ret['result'] = False - ret['comment'] = 'invalid tag name: {0}'.format(name) + if ( + __utils__["zfs.is_snapshot"](name) + or __utils__["zfs.is_bookmark"](name) + or name == "error" + ): + ret["result"] = False + ret["comment"] = "invalid tag name: {0}".format(name) return ret ## release hold if required - holds = __salt__['zfs.holds'](snapshot) + holds = __salt__["zfs.holds"](snapshot) if name in holds: ## NOTE: hold found for snapshot, release it - if not __opts__['test']: - mod_res = __salt__['zfs.release'](name, snapshot, **{'recursive': recursive}) + if not __opts__["test"]: + mod_res = __salt__["zfs.release"]( + name, snapshot, **{"recursive": recursive} + ) else: - mod_res = OrderedDict([('released', True)]) + mod_res = OrderedDict([("released", True)]) - ret['result'] = mod_res['released'] - if ret['result']: - ret['changes'] = {snapshot: {name: 'released'}} - ret['comment'] = 'hold {0} released'.format( - name, - ) + ret["result"] = mod_res["released"] + if ret["result"]: + ret["changes"] = {snapshot: {name: "released"}} + ret["comment"] = "hold {0} released".format(name,) else: - ret['comment'] = 'failed to release hold {0}'.format( - name, - ) - if 'error' in mod_res: - ret['comment'] = mod_res['error'] - elif 'error' in holds: + ret["comment"] = "failed to release hold {0}".format(name,) + if "error" in mod_res: + ret["comment"] = mod_res["error"] + elif "error" in holds: ## NOTE: we have an error - ret['result'] = False - ret['comment'] = holds['error'] + ret["result"] = False + ret["comment"] = holds["error"] else: ## NOTE: no hold found with name for snapshot - ret['comment'] = 'hold {0} is absent'.format( - name, - ) + ret["comment"] = "hold {0} is absent".format(name,) return ret def hold_present(name, snapshot, recursive=False): - ''' + """ ensure hold is present on the system name : string @@ -310,70 +301,62 @@ def hold_present(name, snapshot, recursive=False): recursive : boolean recursively add hold with the given tag on the snapshots of all descendent file systems. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ## log configuration - log.debug('zfs.hold_present::%s::config::snapshot = %s', - name, snapshot) - log.debug('zfs.hold_present::%s::config::recursive = %s', - name, recursive) + log.debug("zfs.hold_present::%s::config::snapshot = %s", name, snapshot) + log.debug("zfs.hold_present::%s::config::recursive = %s", name, recursive) ## check we have a snapshot/tag name - if not __utils__['zfs.is_snapshot'](snapshot): - ret['result'] = False - ret['comment'] = 'invalid snapshot name: {0}'.format(snapshot) + if not __utils__["zfs.is_snapshot"](snapshot): + ret["result"] = False + ret["comment"] = "invalid snapshot name: {0}".format(snapshot) return ret - if __utils__['zfs.is_snapshot'](name) or \ - __utils__['zfs.is_bookmark'](name) or \ - name == 'error': - ret['result'] = False - ret['comment'] = 'invalid tag name: {0}'.format(name) + if ( + __utils__["zfs.is_snapshot"](name) + or __utils__["zfs.is_bookmark"](name) + or name == "error" + ): + ret["result"] = False + ret["comment"] = "invalid tag name: {0}".format(name) return ret ## place hold if required - holds = __salt__['zfs.holds'](snapshot) + holds = __salt__["zfs.holds"](snapshot) if name in holds: ## NOTE: hold with name already exists for snapshot - ret['comment'] = 'hold {0} is present for {1}'.format( - name, - snapshot, - ) + ret["comment"] = "hold {0} is present for {1}".format(name, snapshot,) else: ## NOTE: no hold found with name for snapshot - if not __opts__['test']: - mod_res = __salt__['zfs.hold'](name, snapshot, **{'recursive': recursive}) + if not __opts__["test"]: + mod_res = __salt__["zfs.hold"](name, snapshot, **{"recursive": recursive}) else: - mod_res = OrderedDict([('held', True)]) + mod_res = OrderedDict([("held", True)]) - ret['result'] = mod_res['held'] - if ret['result']: - ret['changes'] = OrderedDict([ - (snapshot, OrderedDict([ - (name, 'held'), - ])), - ]) - ret['comment'] = 'hold {0} added to {1}'.format( - name, - snapshot, - ) + ret["result"] = mod_res["held"] + if ret["result"]: + ret["changes"] = OrderedDict([(snapshot, OrderedDict([(name, "held")]))]) + ret["comment"] = "hold {0} added to {1}".format(name, snapshot) else: - ret['comment'] = 'failed to add hold {0} to {1}'.format( - name, - snapshot, - ) - if 'error' in mod_res: - ret['comment'] = mod_res['error'] + ret["comment"] = "failed to add hold {0} to {1}".format(name, snapshot) + if "error" in mod_res: + ret["comment"] = mod_res["error"] return ret -def _dataset_present(dataset_type, name, volume_size=None, sparse=False, create_parent=False, properties=None, cloned_from=None): - ''' +def _dataset_present( + dataset_type, + name, + volume_size=None, + sparse=False, + create_parent=False, + properties=None, + cloned_from=None, +): + """ internal handler for filesystem_present/volume_present dataset_type : string @@ -406,152 +389,153 @@ def _dataset_present(dataset_type, name, volume_size=None, sparse=False, create_ The sparse parameter is ignored when using ``cloned_from``. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ## fallback dataset_type to filesystem if out of range - if dataset_type not in ['filesystem', 'volume']: - dataset_type = 'filesystem' + if dataset_type not in ["filesystem", "volume"]: + dataset_type = "filesystem" ## ensure properties are zfs values if volume_size: - volume_size = __utils__['zfs.from_size'](volume_size) + volume_size = __utils__["zfs.from_size"](volume_size) if properties: - properties = __utils__['zfs.from_auto_dict'](properties) + properties = __utils__["zfs.from_auto_dict"](properties) elif properties is None: properties = {} ## log configuration - log.debug('zfs.%s_present::%s::config::volume_size = %s', - dataset_type, name, volume_size) - log.debug('zfs.%s_present::%s::config::sparse = %s', - dataset_type, name, sparse) - log.debug('zfs.%s_present::%s::config::create_parent = %s', - dataset_type, name, create_parent) - log.debug('zfs.%s_present::%s::config::cloned_from = %s', - dataset_type, name, cloned_from) - log.debug('zfs.%s_present::%s::config::properties = %s', - dataset_type, name, properties) + log.debug( + "zfs.%s_present::%s::config::volume_size = %s", dataset_type, name, volume_size + ) + log.debug("zfs.%s_present::%s::config::sparse = %s", dataset_type, name, sparse) + log.debug( + "zfs.%s_present::%s::config::create_parent = %s", + dataset_type, + name, + create_parent, + ) + log.debug( + "zfs.%s_present::%s::config::cloned_from = %s", dataset_type, name, cloned_from + ) + log.debug( + "zfs.%s_present::%s::config::properties = %s", dataset_type, name, properties + ) ## check we have valid filesystem name/volume name/clone snapshot - if not __utils__['zfs.is_dataset'](name): - ret['result'] = False - ret['comment'] = 'invalid dataset name: {0}'.format(name) + if not __utils__["zfs.is_dataset"](name): + ret["result"] = False + ret["comment"] = "invalid dataset name: {0}".format(name) return ret - if cloned_from and not __utils__['zfs.is_snapshot'](cloned_from): - ret['result'] = False - ret['comment'] = '{0} is not a snapshot'.format(cloned_from) + if cloned_from and not __utils__["zfs.is_snapshot"](cloned_from): + ret["result"] = False + ret["comment"] = "{0} is not a snapshot".format(cloned_from) return ret ## ensure dataset is in correct state ## NOTE: update the dataset - if __salt__['zfs.exists'](name, **{'type': dataset_type}): + if __salt__["zfs.exists"](name, **{"type": dataset_type}): ## NOTE: fetch current volume properties - properties_current = __salt__['zfs.get']( - name, - type=dataset_type, - fields='value', - depth=0, - parsable=True, + properties_current = __salt__["zfs.get"]( + name, type=dataset_type, fields="value", depth=0, parsable=True, ).get(name, OrderedDict()) ## NOTE: add volsize to properties if volume_size: - properties['volsize'] = volume_size + properties["volsize"] = volume_size ## NOTE: build list of properties to update properties_update = [] for prop in properties: ## NOTE: skip unexisting properties if prop not in properties_current: - log.warning('zfs.%s_present::%s::update - unknown property: %s', - dataset_type, name, prop) + log.warning( + "zfs.%s_present::%s::update - unknown property: %s", + dataset_type, + name, + prop, + ) continue ## NOTE: compare current and wanted value - if properties_current[prop]['value'] != properties[prop]: + if properties_current[prop]["value"] != properties[prop]: properties_update.append(prop) ## NOTE: update pool properties for prop in properties_update: - if not __opts__['test']: - mod_res = __salt__['zfs.set'](name, **{prop: properties[prop]}) + if not __opts__["test"]: + mod_res = __salt__["zfs.set"](name, **{prop: properties[prop]}) else: - mod_res = OrderedDict([('set', True)]) + mod_res = OrderedDict([("set", True)]) - if mod_res['set']: - if name not in ret['changes']: - ret['changes'][name] = {} - ret['changes'][name][prop] = properties[prop] + if mod_res["set"]: + if name not in ret["changes"]: + ret["changes"][name] = {} + ret["changes"][name][prop] = properties[prop] else: - ret['result'] = False - if ret['comment'] == '': - ret['comment'] = 'The following properties were not updated:' - ret['comment'] = '{0} {1}'.format(ret['comment'], prop) + ret["result"] = False + if ret["comment"] == "": + ret["comment"] = "The following properties were not updated:" + ret["comment"] = "{0} {1}".format(ret["comment"], prop) ## NOTE: update comment - if ret['result'] and name in ret['changes']: - ret['comment'] = '{0} {1} was updated'.format(dataset_type, name) - elif ret['result']: - ret['comment'] = '{0} {1} is uptodate'.format(dataset_type, name) + if ret["result"] and name in ret["changes"]: + ret["comment"] = "{0} {1} was updated".format(dataset_type, name) + elif ret["result"]: + ret["comment"] = "{0} {1} is uptodate".format(dataset_type, name) else: - ret['comment'] = '{0} {1} failed to be updated'.format(dataset_type, name) + ret["comment"] = "{0} {1} failed to be updated".format(dataset_type, name) ## NOTE: create or clone the dataset else: - mod_res_action = 'cloned' if cloned_from else 'created' - if __opts__['test']: + mod_res_action = "cloned" if cloned_from else "created" + if __opts__["test"]: ## NOTE: pretend to create/clone - mod_res = OrderedDict([ - (mod_res_action, True), - ]) + mod_res = OrderedDict([(mod_res_action, True)]) elif cloned_from: ## NOTE: add volsize to properties if volume_size: - properties['volsize'] = volume_size + properties["volsize"] = volume_size ## NOTE: clone the dataset - mod_res = __salt__['zfs.clone'](cloned_from, name, **{ - 'create_parent': create_parent, - 'properties': properties, - }) + mod_res = __salt__["zfs.clone"]( + cloned_from, + name, + **{"create_parent": create_parent, "properties": properties} + ) else: ## NOTE: create the dataset - mod_res = __salt__['zfs.create'](name, **{ - 'create_parent': create_parent, - 'properties': properties, - 'volume_size': volume_size, - 'sparse': sparse, - }) - - ret['result'] = mod_res[mod_res_action] - if ret['result']: - ret['changes'][name] = mod_res_action - if properties: - ret['changes'][name] = properties - ret['comment'] = '{0} {1} was {2}'.format( - dataset_type, + mod_res = __salt__["zfs.create"]( name, - mod_res_action, + **{ + "create_parent": create_parent, + "properties": properties, + "volume_size": volume_size, + "sparse": sparse, + } + ) + + ret["result"] = mod_res[mod_res_action] + if ret["result"]: + ret["changes"][name] = mod_res_action + if properties: + ret["changes"][name] = properties + ret["comment"] = "{0} {1} was {2}".format( + dataset_type, name, mod_res_action, ) else: - ret['comment'] = 'failed to {0} {1} {2}'.format( - mod_res_action[:-1], - dataset_type, - name, + ret["comment"] = "failed to {0} {1} {2}".format( + mod_res_action[:-1], dataset_type, name, ) - if 'error' in mod_res: - ret['comment'] = mod_res['error'] + if "error" in mod_res: + ret["comment"] = mod_res["error"] return ret def filesystem_present(name, create_parent=False, properties=None, cloned_from=None): - ''' + """ ensure filesystem exists and has properties set name : string @@ -572,9 +556,9 @@ def filesystem_present(name, create_parent=False, properties=None, cloned_from=N Properties do not get cloned, if you specify the properties in the state file they will be applied on a subsequent run. - ''' + """ return _dataset_present( - 'filesystem', + "filesystem", name, create_parent=create_parent, properties=properties, @@ -582,8 +566,15 @@ def filesystem_present(name, create_parent=False, properties=None, cloned_from=N ) -def volume_present(name, volume_size, sparse=False, create_parent=False, properties=None, cloned_from=None): - ''' +def volume_present( + name, + volume_size, + sparse=False, + create_parent=False, + properties=None, + cloned_from=None, +): + """ ensure volume exists and has properties set name : string @@ -614,9 +605,9 @@ def volume_present(name, volume_size, sparse=False, create_parent=False, propert The sparse parameter is ignored when using ``cloned_from``. - ''' + """ return _dataset_present( - 'volume', + "volume", name, volume_size, sparse=sparse, @@ -627,7 +618,7 @@ def volume_present(name, volume_size, sparse=False, create_parent=False, propert def bookmark_present(name, snapshot): - ''' + """ ensure bookmark exists name : string @@ -635,59 +626,55 @@ def bookmark_present(name, snapshot): snapshot : string name of snapshot - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ## log configuration - log.debug('zfs.bookmark_present::%s::config::snapshot = %s', - name, snapshot) + log.debug("zfs.bookmark_present::%s::config::snapshot = %s", name, snapshot) ## check we have valid snapshot/bookmark name - if not __utils__['zfs.is_snapshot'](snapshot): - ret['result'] = False - ret['comment'] = 'invalid snapshot name: {0}'.format(name) + if not __utils__["zfs.is_snapshot"](snapshot): + ret["result"] = False + ret["comment"] = "invalid snapshot name: {0}".format(name) return ret - if '#' not in name and '/' not in name: + if "#" not in name and "/" not in name: ## NOTE: simple snapshot name # take the snapshot name and replace the snapshot but with the simple name # e.g. pool/fs@snap + bm --> pool/fs#bm - name = '{0}#{1}'.format(snapshot[:snapshot.index('@')], name) - ret['name'] = name + name = "{0}#{1}".format(snapshot[: snapshot.index("@")], name) + ret["name"] = name - if not __utils__['zfs.is_bookmark'](name): - ret['result'] = False - ret['comment'] = 'invalid bookmark name: {0}'.format(name) + if not __utils__["zfs.is_bookmark"](name): + ret["result"] = False + ret["comment"] = "invalid bookmark name: {0}".format(name) return ret ## ensure bookmark exists - if not __salt__['zfs.exists'](name, **{'type': 'bookmark'}): + if not __salt__["zfs.exists"](name, **{"type": "bookmark"}): ## NOTE: bookmark the snapshot - if not __opts__['test']: - mod_res = __salt__['zfs.bookmark'](snapshot, name) + if not __opts__["test"]: + mod_res = __salt__["zfs.bookmark"](snapshot, name) else: - mod_res = OrderedDict([('bookmarked', True)]) + mod_res = OrderedDict([("bookmarked", True)]) - ret['result'] = mod_res['bookmarked'] - if ret['result']: - ret['changes'][name] = snapshot - ret['comment'] = '{0} bookmarked as {1}'.format(snapshot, name) + ret["result"] = mod_res["bookmarked"] + if ret["result"]: + ret["changes"][name] = snapshot + ret["comment"] = "{0} bookmarked as {1}".format(snapshot, name) else: - ret['comment'] = 'failed to bookmark {0}'.format(snapshot) - if 'error' in mod_res: - ret['comment'] = mod_res['error'] + ret["comment"] = "failed to bookmark {0}".format(snapshot) + if "error" in mod_res: + ret["comment"] = mod_res["error"] else: ## NOTE: bookmark already exists - ret['comment'] = 'bookmark is present' + ret["comment"] = "bookmark is present" return ret def snapshot_present(name, recursive=False, properties=None): - ''' + """ ensure snapshot exists and has properties set name : string @@ -700,55 +687,52 @@ def snapshot_present(name, recursive=False, properties=None): .. note: Properties are only set at creation time - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ## log configuration - log.debug('zfs.snapshot_present::%s::config::recursive = %s', - name, recursive) - log.debug('zfs.snapshot_present::%s::config::properties = %s', - name, properties) + log.debug("zfs.snapshot_present::%s::config::recursive = %s", name, recursive) + log.debug("zfs.snapshot_present::%s::config::properties = %s", name, properties) ## ensure properties are zfs values if properties: - properties = __utils__['zfs.from_auto_dict'](properties) + properties = __utils__["zfs.from_auto_dict"](properties) ## check we have valid snapshot name - if not __utils__['zfs.is_snapshot'](name): - ret['result'] = False - ret['comment'] = 'invalid snapshot name: {0}'.format(name) + if not __utils__["zfs.is_snapshot"](name): + ret["result"] = False + ret["comment"] = "invalid snapshot name: {0}".format(name) return ret ## ensure snapshot exits - if not __salt__['zfs.exists'](name, **{'type': 'snapshot'}): + if not __salt__["zfs.exists"](name, **{"type": "snapshot"}): ## NOTE: create the snapshot - if not __opts__['test']: - mod_res = __salt__['zfs.snapshot'](name, **{'recursive': recursive, 'properties': properties}) + if not __opts__["test"]: + mod_res = __salt__["zfs.snapshot"]( + name, **{"recursive": recursive, "properties": properties} + ) else: - mod_res = OrderedDict([('snapshotted', True)]) + mod_res = OrderedDict([("snapshotted", True)]) - ret['result'] = mod_res['snapshotted'] - if ret['result']: - ret['changes'][name] = 'snapshotted' + ret["result"] = mod_res["snapshotted"] + if ret["result"]: + ret["changes"][name] = "snapshotted" if properties: - ret['changes'][name] = properties - ret['comment'] = 'snapshot {0} was created'.format(name) + ret["changes"][name] = properties + ret["comment"] = "snapshot {0} was created".format(name) else: - ret['comment'] = 'failed to create snapshot {0}'.format(name) - if 'error' in mod_res: - ret['comment'] = mod_res['error'] + ret["comment"] = "failed to create snapshot {0}".format(name) + if "error" in mod_res: + ret["comment"] = mod_res["error"] else: ## NOTE: snapshot already exists - ret['comment'] = 'snapshot is present' + ret["comment"] = "snapshot is present" return ret def promoted(name): - ''' + """ ensure a dataset is not a clone name : string @@ -759,49 +743,51 @@ def promoted(name): only one dataset can be the origin, if you promote a clone the original will now point to the promoted dataset - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ## check we if we have a valid dataset name - if not __utils__['zfs.is_dataset'](name): - ret['result'] = False - ret['comment'] = 'invalid dataset name: {0}'.format(name) + if not __utils__["zfs.is_dataset"](name): + ret["result"] = False + ret["comment"] = "invalid dataset name: {0}".format(name) return ret ## ensure dataset is the primary instance - if not __salt__['zfs.exists'](name, **{'type': 'filesystem,volume'}): + if not __salt__["zfs.exists"](name, **{"type": "filesystem,volume"}): ## NOTE: we don't have a dataset - ret['result'] = False - ret['comment'] = 'dataset {0} does not exist'.format(name) + ret["result"] = False + ret["comment"] = "dataset {0} does not exist".format(name) else: ## NOTE: check if we have a blank origin (-) - if __salt__['zfs.get'](name, **{'properties': 'origin', 'fields': 'value', 'parsable': True})[name]['origin']['value'] == '-': + if ( + __salt__["zfs.get"]( + name, **{"properties": "origin", "fields": "value", "parsable": True} + )[name]["origin"]["value"] + == "-" + ): ## NOTE: we're already promoted - ret['comment'] = '{0} already promoted'.format(name) + ret["comment"] = "{0} already promoted".format(name) else: ## NOTE: promote dataset - if not __opts__['test']: - mod_res = __salt__['zfs.promote'](name) + if not __opts__["test"]: + mod_res = __salt__["zfs.promote"](name) else: - mod_res = OrderedDict([('promoted', True)]) + mod_res = OrderedDict([("promoted", True)]) - ret['result'] = mod_res['promoted'] - if ret['result']: - ret['changes'][name] = 'promoted' - ret['comment'] = '{0} promoted'.format(name) + ret["result"] = mod_res["promoted"] + if ret["result"]: + ret["changes"][name] = "promoted" + ret["comment"] = "{0} promoted".format(name) else: - ret['comment'] = 'failed to promote {0}'.format(name) - if 'error' in mod_res: - ret['comment'] = mod_res['error'] + ret["comment"] = "failed to promote {0}".format(name) + if "error" in mod_res: + ret["comment"] = mod_res["error"] return ret def _schedule_snapshot_retrieve(dataset, prefix, snapshots): - ''' + """ Update snapshots dict with current snapshots dataset: string @@ -812,36 +798,40 @@ def _schedule_snapshot_retrieve(dataset, prefix, snapshots): snapshots : OrderedDict preseeded OrderedDict with configuration - ''' + """ ## NOTE: retrieve all snapshots for the dataset - for snap in sorted(__salt__['zfs.list'](dataset, **{'recursive': True, 'depth': 1, 'type': 'snapshot'}).keys()): + for snap in sorted( + __salt__["zfs.list"]( + dataset, **{"recursive": True, "depth": 1, "type": "snapshot"} + ).keys() + ): ## NOTE: we only want the actualy name ## myzpool/data@zbck-20171201_000248 -> zbck-20171201_000248 - snap_name = snap[snap.index('@')+1:] + snap_name = snap[snap.index("@") + 1 :] ## NOTE: we only want snapshots matching our prefix - if not snap_name.startswith('{0}-'.format(prefix)): + if not snap_name.startswith("{0}-".format(prefix)): continue ## NOTE: retrieve the holds for this snapshot - snap_holds = __salt__['zfs.holds'](snap) + snap_holds = __salt__["zfs.holds"](snap) ## NOTE: this snapshot has no holds, eligable for pruning if not snap_holds: - snapshots['_prunable'].append(snap) + snapshots["_prunable"].append(snap) ## NOTE: update snapshots based on holds (if any) ## we are only interested in the ones from our schedule ## if we find any others we skip them for hold in snap_holds: - if hold in snapshots['_schedule'].keys(): + if hold in snapshots["_schedule"].keys(): snapshots[hold].append(snap) return snapshots def _schedule_snapshot_prepare(dataset, prefix, snapshots): - ''' + """ Update snapshots dict with info for a new snapshot dataset: string @@ -852,18 +842,18 @@ def _schedule_snapshot_prepare(dataset, prefix, snapshots): snapshots : OrderedDict preseeded OrderedDict with configuration - ''' + """ ## NOTE: generate new snapshot name - snapshot_create_name = '{dataset}@{prefix}-{timestamp}'.format( + snapshot_create_name = "{dataset}@{prefix}-{timestamp}".format( dataset=dataset, prefix=prefix, - timestamp=datetime.now().strftime('%Y%m%d_%H%M%S') + timestamp=datetime.now().strftime("%Y%m%d_%H%M%S"), ) ## NOTE: figure out if we need to create the snapshot timestamp_now = datetime.now().replace(second=0, microsecond=0) - snapshots['_create'][snapshot_create_name] = [] - for hold, hold_count in snapshots['_schedule'].items(): + snapshots["_create"][snapshot_create_name] = [] + for hold, hold_count in snapshots["_schedule"].items(): ## NOTE: skip hold if we don't keep snapshots for it if hold_count == 0: continue @@ -872,35 +862,37 @@ def _schedule_snapshot_prepare(dataset, prefix, snapshots): if snapshots[hold]: ## NOTE: extract datetime from snapshot name timestamp = datetime.strptime( - snapshots[hold][-1], - '{0}@{1}-%Y%m%d_%H%M%S'.format(dataset, prefix), + snapshots[hold][-1], "{0}@{1}-%Y%m%d_%H%M%S".format(dataset, prefix), ).replace(second=0, microsecond=0) ## NOTE: compare current timestamp to timestamp from snapshot - if hold == 'minute' and \ - timestamp_now <= timestamp: + if hold == "minute" and timestamp_now <= timestamp: continue - elif hold == 'hour' and \ - timestamp_now.replace(**comp_hour) <= timestamp.replace(**comp_hour): + elif hold == "hour" and timestamp_now.replace( + **comp_hour + ) <= timestamp.replace(**comp_hour): continue - elif hold == 'day' and \ - timestamp_now.replace(**comp_day) <= timestamp.replace(**comp_day): + elif hold == "day" and timestamp_now.replace( + **comp_day + ) <= timestamp.replace(**comp_day): continue - elif hold == 'month' and \ - timestamp_now.replace(**comp_month) <= timestamp.replace(**comp_month): + elif hold == "month" and timestamp_now.replace( + **comp_month + ) <= timestamp.replace(**comp_month): continue - elif hold == 'year' and \ - timestamp_now.replace(**comp_year) <= timestamp.replace(**comp_year): + elif hold == "year" and timestamp_now.replace( + **comp_year + ) <= timestamp.replace(**comp_year): continue ## NOTE: add hold entry for snapshot - snapshots['_create'][snapshot_create_name].append(hold) + snapshots["_create"][snapshot_create_name].append(hold) return snapshots def scheduled_snapshot(name, prefix, recursive=True, schedule=None): - ''' + """ maintain a set of snapshots based on a schedule name : string @@ -925,56 +917,49 @@ def scheduled_snapshot(name, prefix, recursive=True, schedule=None): switched to localtime from gmtime so times now take into account timezones. - ''' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": True, "comment": ""} ## initialize defaults - schedule_holds = ['minute', 'hour', 'day', 'month', 'year'] - snapshots = OrderedDict([ - ('_create', OrderedDict()), - ('_prunable', []), - ('_schedule', OrderedDict()), - ]) + schedule_holds = ["minute", "hour", "day", "month", "year"] + snapshots = OrderedDict( + [("_create", OrderedDict()), ("_prunable", []), ("_schedule", OrderedDict())] + ) ## strict configuration validation ## NOTE: we need a valid dataset - if not __utils__['zfs.is_dataset'](name): - ret['result'] = False - ret['comment'] = 'invalid dataset name: {0}'.format(name) + if not __utils__["zfs.is_dataset"](name): + ret["result"] = False + ret["comment"] = "invalid dataset name: {0}".format(name) - if not __salt__['zfs.exists'](name, **{'type': 'filesystem,volume'}): - ret['comment'] = 'dataset {0} does not exist'.format(name) - ret['result'] = False + if not __salt__["zfs.exists"](name, **{"type": "filesystem,volume"}): + ret["comment"] = "dataset {0} does not exist".format(name) + ret["result"] = False ## NOTE: prefix must be 4 or longer if not prefix or len(prefix) < 4: - ret['comment'] = 'prefix ({0}) must be at least 4 long'.format(prefix) - ret['result'] = False + ret["comment"] = "prefix ({0}) must be at least 4 long".format(prefix) + ret["result"] = False ## NOTE: validate schedule total_count = 0 for hold in schedule_holds: snapshots[hold] = [] if hold not in schedule: - snapshots['_schedule'][hold] = 0 + snapshots["_schedule"][hold] = 0 elif isinstance(schedule[hold], int): - snapshots['_schedule'][hold] = schedule[hold] + snapshots["_schedule"][hold] = schedule[hold] else: - ret['result'] = False - ret['comment'] = 'schedule value for {0} is not an integer'.format( - hold, - ) + ret["result"] = False + ret["comment"] = "schedule value for {0} is not an integer".format(hold,) break - total_count += snapshots['_schedule'][hold] - if ret['result'] and total_count == 0: - ret['result'] = False - ret['comment'] = 'schedule is not valid, you need to keep atleast 1 snapshot' + total_count += snapshots["_schedule"][hold] + if ret["result"] and total_count == 0: + ret["result"] = False + ret["comment"] = "schedule is not valid, you need to keep atleast 1 snapshot" ## NOTE: return if configuration is not valid - if not ret['result']: + if not ret["result"]: return ret ## retrieve existing snapshots @@ -984,97 +969,97 @@ def scheduled_snapshot(name, prefix, recursive=True, schedule=None): snapshots = _schedule_snapshot_prepare(name, prefix, snapshots) ## log configuration - log.debug('zfs.scheduled_snapshot::%s::config::recursive = %s', - name, recursive) - log.debug('zfs.scheduled_snapshot::%s::config::prefix = %s', - name, prefix) - log.debug('zfs.scheduled_snapshot::%s::snapshots = %s', - name, snapshots) + log.debug("zfs.scheduled_snapshot::%s::config::recursive = %s", name, recursive) + log.debug("zfs.scheduled_snapshot::%s::config::prefix = %s", name, prefix) + log.debug("zfs.scheduled_snapshot::%s::snapshots = %s", name, snapshots) ## create snapshot(s) - for snapshot_name, snapshot_holds in snapshots['_create'].items(): + for snapshot_name, snapshot_holds in snapshots["_create"].items(): ## NOTE: skip if new snapshot has no holds if not snapshot_holds: continue ## NOTE: create snapshot - if not __opts__['test']: - mod_res = __salt__['zfs.snapshot'](snapshot_name, **{'recursive': recursive}) + if not __opts__["test"]: + mod_res = __salt__["zfs.snapshot"]( + snapshot_name, **{"recursive": recursive} + ) else: - mod_res = OrderedDict([('snapshotted', True)]) + mod_res = OrderedDict([("snapshotted", True)]) - if not mod_res['snapshotted']: - ret['result'] = False - ret['comment'] = 'error creating snapshot ({0})'.format(snapshot_name) + if not mod_res["snapshotted"]: + ret["result"] = False + ret["comment"] = "error creating snapshot ({0})".format(snapshot_name) else: ## NOTE: create holds (if we have a snapshot) for hold in snapshot_holds: - if not __opts__['test']: - mod_res = __salt__['zfs.hold'](hold, snapshot_name, **{'recursive': recursive}) + if not __opts__["test"]: + mod_res = __salt__["zfs.hold"]( + hold, snapshot_name, **{"recursive": recursive} + ) else: - mod_res = OrderedDict([('held', True)]) + mod_res = OrderedDict([("held", True)]) - if not mod_res['held']: - ret['result'] = False - ret['comment'] = "error adding hold ({0}) to snapshot ({1})".format( - hold, - snapshot_name, + if not mod_res["held"]: + ret["result"] = False + ret["comment"] = "error adding hold ({0}) to snapshot ({1})".format( + hold, snapshot_name, ) break snapshots[hold].append(snapshot_name) - if ret['result']: - ret['comment'] = 'scheduled snapshots updated' - if 'created' not in ret['changes']: - ret['changes']['created'] = [] - ret['changes']['created'].append(snapshot_name) + if ret["result"]: + ret["comment"] = "scheduled snapshots updated" + if "created" not in ret["changes"]: + ret["changes"]["created"] = [] + ret["changes"]["created"].append(snapshot_name) ## prune hold(s) - for hold, hold_count in snapshots['_schedule'].items(): - while ret['result'] and len(snapshots[hold]) > hold_count: + for hold, hold_count in snapshots["_schedule"].items(): + while ret["result"] and len(snapshots[hold]) > hold_count: ## NOTE: pop oldest snapshot snapshot_name = snapshots[hold].pop(0) ## NOTE: release hold for snapshot - if not __opts__['test']: - mod_res = __salt__['zfs.release'](hold, snapshot_name, **{'recursive': recursive}) + if not __opts__["test"]: + mod_res = __salt__["zfs.release"]( + hold, snapshot_name, **{"recursive": recursive} + ) else: - mod_res = OrderedDict([('released', True)]) + mod_res = OrderedDict([("released", True)]) - if not mod_res['released']: - ret['result'] = False - ret['comment'] = "error adding hold ({0}) to snapshot ({1})".format( - hold, - snapshot_name, + if not mod_res["released"]: + ret["result"] = False + ret["comment"] = "error adding hold ({0}) to snapshot ({1})".format( + hold, snapshot_name, ) ## NOTE: mark as prunable - if not __salt__['zfs.holds'](snapshot_name): - snapshots['_prunable'].append(snapshot_name) + if not __salt__["zfs.holds"](snapshot_name): + snapshots["_prunable"].append(snapshot_name) ## prune snapshot(s) - for snapshot_name in snapshots['_prunable']: + for snapshot_name in snapshots["_prunable"]: ## NOTE: destroy snapshot - if not __opts__['test']: - mod_res = __salt__['zfs.destroy'](snapshot_name, **{'recursive': recursive}) + if not __opts__["test"]: + mod_res = __salt__["zfs.destroy"](snapshot_name, **{"recursive": recursive}) else: - mod_res = OrderedDict([('destroyed', True)]) + mod_res = OrderedDict([("destroyed", True)]) - if not mod_res['destroyed']: - ret['result'] = False - ret['comment'] = "error prunding snapshot ({1})".format( - snapshot_name, - ) + if not mod_res["destroyed"]: + ret["result"] = False + ret["comment"] = "error prunding snapshot ({1})".format(snapshot_name,) break - if ret['result'] and snapshots['_prunable']: - ret['comment'] = 'scheduled snapshots updated' - ret['changes']['pruned'] = snapshots['_prunable'] + if ret["result"] and snapshots["_prunable"]: + ret["comment"] = "scheduled snapshots updated" + ret["changes"]["pruned"] = snapshots["_prunable"] - if ret['result'] and not ret['changes']: - ret['comment'] = 'scheduled snapshots are up to date' + if ret["result"] and not ret["changes"]: + ret["comment"] = "scheduled snapshots are up to date" return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/states/zk_concurrency.py b/salt/states/zk_concurrency.py index 96d88e9ffcc..14b0259e99b 100644 --- a/salt/states/zk_concurrency.py +++ b/salt/states/zk_concurrency.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Control concurrency of steps within state execution using zookeeper =================================================================== @@ -44,155 +44,184 @@ steps are executing with a single path. This example would allow the file state to change, but would limit the concurrency of the trafficserver service restart to 4. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # TODO: use depends decorator to make these per function deps, instead of all or nothing REQUIRED_FUNCS = ( - 'zk_concurrency.lock', - 'zk_concurrency.unlock', - 'zk_concurrency.party_members', + "zk_concurrency.lock", + "zk_concurrency.unlock", + "zk_concurrency.party_members", ) -__virtualname__ = 'zk_concurrency' +__virtualname__ = "zk_concurrency" def __virtual__(): if not all(func in __salt__ for func in REQUIRED_FUNCS): - return False - + return (False, "zk_concurrency module could not be loaded") return __virtualname__ -def lock(name, - zk_hosts=None, - identifier=None, - max_concurrency=1, - timeout=None, - ephemeral_lease=False, - profile=None, - scheme=None, - username=None, - password=None, - default_acl=None): - ''' +def lock( + name, + zk_hosts=None, + identifier=None, + max_concurrency=1, + timeout=None, + ephemeral_lease=False, + profile=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Block state execution until you are able to get the lock (or hit the timeout) - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - conn_kwargs = {'profile': profile, 'scheme': scheme, - 'username': username, 'password': password, 'default_acl': default_acl} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + conn_kwargs = { + "profile": profile, + "scheme": scheme, + "username": username, + "password": password, + "default_acl": default_acl, + } - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Attempt to acquire lock' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Attempt to acquire lock" return ret if identifier is None: - identifier = __grains__['id'] + identifier = __grains__["id"] - locked = __salt__['zk_concurrency.lock'](name, - zk_hosts, - identifier=identifier, - max_concurrency=max_concurrency, - timeout=timeout, - ephemeral_lease=ephemeral_lease, - **conn_kwargs) + locked = __salt__["zk_concurrency.lock"]( + name, + zk_hosts, + identifier=identifier, + max_concurrency=max_concurrency, + timeout=timeout, + ephemeral_lease=ephemeral_lease, + **conn_kwargs + ) if locked: - ret['result'] = True - ret['comment'] = 'lock acquired' + ret["result"] = True + ret["comment"] = "lock acquired" else: - ret['comment'] = 'Unable to acquire lock' + ret["comment"] = "Unable to acquire lock" return ret -def unlock(name, - zk_hosts=None, # in case you need to unlock without having run lock (failed execution for example) - identifier=None, - max_concurrency=1, - ephemeral_lease=False, - profile=None, - scheme=None, - username=None, - password=None, - default_acl=None): - ''' +def unlock( + name, + zk_hosts=None, # in case you need to unlock without having run lock (failed execution for example) + identifier=None, + max_concurrency=1, + ephemeral_lease=False, + profile=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Remove lease from semaphore. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - conn_kwargs = {'profile': profile, 'scheme': scheme, - 'username': username, 'password': password, 'default_acl': default_acl} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + conn_kwargs = { + "profile": profile, + "scheme": scheme, + "username": username, + "password": password, + "default_acl": default_acl, + } - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Released lock if it is here' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Released lock if it is here" return ret if identifier is None: - identifier = __grains__['id'] + identifier = __grains__["id"] - unlocked = __salt__['zk_concurrency.unlock'](name, - zk_hosts=zk_hosts, - identifier=identifier, - max_concurrency=max_concurrency, - ephemeral_lease=ephemeral_lease, - **conn_kwargs) + unlocked = __salt__["zk_concurrency.unlock"]( + name, + zk_hosts=zk_hosts, + identifier=identifier, + max_concurrency=max_concurrency, + ephemeral_lease=ephemeral_lease, + **conn_kwargs + ) if unlocked: - ret['result'] = True + ret["result"] = True else: - ret['comment'] = 'Unable to find lease for path {0}'.format(name) + ret["comment"] = "Unable to find lease for path {0}".format(name) return ret -def min_party(name, - zk_hosts, - min_nodes, - blocking=False, - profile=None, - scheme=None, - username=None, - password=None, - default_acl=None): - ''' +def min_party( + name, + zk_hosts, + min_nodes, + blocking=False, + profile=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Ensure that there are `min_nodes` in the party at `name`, optionally blocking if not available. - ''' - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} - conn_kwargs = {'profile': profile, 'scheme': scheme, - 'username': username, 'password': password, 'default_acl': default_acl} + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} + conn_kwargs = { + "profile": profile, + "scheme": scheme, + "username": username, + "password": password, + "default_acl": default_acl, + } - if __opts__['test']: - ret['result'] = None - ret['comment'] = 'Attempt to ensure min_party' + if __opts__["test"]: + ret["result"] = None + ret["comment"] = "Attempt to ensure min_party" return ret - nodes = __salt__['zk_concurrency.party_members'](name, zk_hosts, min_nodes, blocking=blocking, **conn_kwargs) + nodes = __salt__["zk_concurrency.party_members"]( + name, zk_hosts, min_nodes, blocking=blocking, **conn_kwargs + ) if not isinstance(nodes, list): - raise Exception('Error from zk_concurrency.party_members, return was not a list: {0}'.format(nodes)) + raise Exception( + "Error from zk_concurrency.party_members, return was not a list: {0}".format( + nodes + ) + ) num_nodes = len(nodes) if num_nodes >= min_nodes or blocking: - ret['result'] = None if __opts__['test'] else True + ret["result"] = None if __opts__["test"] else True if not blocking: - ret['comment'] = 'Currently {0} nodes, which is >= {1}'.format(num_nodes, min_nodes) + ret["comment"] = "Currently {0} nodes, which is >= {1}".format( + num_nodes, min_nodes + ) else: - ret['comment'] = 'Blocked until {0} nodes were available. Unblocked after {1} nodes became available'.format(min_nodes, num_nodes) + ret[ + "comment" + ] = "Blocked until {0} nodes were available. Unblocked after {1} nodes became available".format( + min_nodes, num_nodes + ) else: - ret['result'] = False - ret['comment'] = 'Currently {0} nodes, which is < {1}'.format(num_nodes, min_nodes) + ret["result"] = False + ret["comment"] = "Currently {0} nodes, which is < {1}".format( + num_nodes, min_nodes + ) return ret diff --git a/salt/states/zone.py b/salt/states/zone.py index 5406c918954..f7e54af3b31 100644 --- a/salt/states/zone.py +++ b/salt/states/zone.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Management of Solaris Zones :maintainer: Jorge Schrauwen <sjorge@blackdot.be> @@ -108,7 +108,7 @@ Or we can remove the limit altogether! - name: omipkg1 - property: cpu-shares -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -118,38 +118,38 @@ import logging import salt.utils.args import salt.utils.atomicfile import salt.utils.files -from salt.modules.zonecfg import _parse_value, _zonecfg_resource_default_selectors from salt.exceptions import CommandExecutionError -from salt.utils.odict import OrderedDict +from salt.modules.zonecfg import _parse_value, _zonecfg_resource_default_selectors from salt.utils.dictupdate import merge as merge_dict +from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) __func_alias__ = { - 'import_': 'import', + "import_": "import", } # Define the state's virtual name -__virtualname__ = 'zone' +__virtualname__ = "zone" def __virtual__(): - ''' + """ Provides zone state on Solaris - ''' - if 'zonecfg.create' in __salt__ and 'zoneadm.install' in __salt__: + """ + if "zonecfg.create" in __salt__ and "zoneadm.install" in __salt__: return True else: return ( False, - '{0} state module can only be loaded on Solaris platforms'.format( + "{0} state module can only be loaded on Solaris platforms".format( __virtualname__ - ) + ), ) def property_present(name, property, value): - ''' + """ Ensure property has a certain value name : string @@ -159,48 +159,57 @@ def property_present(name, property, value): value : string value of property - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} ## sanitize input value = _parse_value(value) - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: ## zone exists - zonecfg = __salt__['zonecfg.info'](name, show_all=True) + zonecfg = __salt__["zonecfg.info"](name, show_all=True) if property not in zonecfg or zonecfg[property] != _parse_value(value): - if __opts__['test']: - ret['result'] = True + if __opts__["test"]: + ret["result"] = True else: # update property - zonecfg_res = __salt__['zonecfg.set_property'](name, property, value) - ret['result'] = zonecfg_res['status'] - if 'messages' in zonecfg_res: - ret['comment'] = zonecfg_res['message'] - if ret['result']: - ret['changes'][property] = _parse_value(value) - if ret['comment'] == '': - ret['comment'] = 'The property {0} is was updated to {1}.'.format(property, value) - elif ret['comment'] == '': - if ret['comment'] == '': - ret['comment'] = 'The property {0} is was not updated to {1}!'.format(property, value) + zonecfg_res = __salt__["zonecfg.set_property"](name, property, value) + ret["result"] = zonecfg_res["status"] + if "messages" in zonecfg_res: + ret["comment"] = zonecfg_res["message"] + if ret["result"]: + ret["changes"][property] = _parse_value(value) + if ret["comment"] == "": + ret["comment"] = "The property {0} is was updated to {1}.".format( + property, value + ) + elif ret["comment"] == "": + if ret["comment"] == "": + ret[ + "comment" + ] = "The property {0} is was not updated to {1}!".format( + property, value + ) else: - ret['result'] = True - ret['comment'] = 'The property {0} is already set to {1}.'.format(property, value) + ret["result"] = True + ret["comment"] = "The property {0} is already set to {1}.".format( + property, value + ) else: ## zone does not exist - ret['result'] = False - ret['comment'] = 'The zone {0} is not in the configured, installed, or booted state.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "The zone {0} is not in the configured, installed, or booted state.".format( + name + ) return ret def property_absent(name, property): - ''' + """ Ensure property is absent name : string @@ -212,49 +221,54 @@ def property_absent(name, property): This does a zoneacfg clear call. So the property may be reset to a default value! Does has the side effect of always having to be called. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: ## zone exists - zonecfg = __salt__['zonecfg.info'](name, show_all=True) + zonecfg = __salt__["zonecfg.info"](name, show_all=True) if property in zonecfg: - if __opts__['test']: - ret['result'] = True + if __opts__["test"]: + ret["result"] = True else: # clear property - zonecfg_res = __salt__['zonecfg.clear_property'](name, property) - zonecfg_new = __salt__['zonecfg.info'](name, show_all=True) - ret['result'] = zonecfg_res['status'] - if 'messages' in zonecfg_res: - ret['comment'] = zonecfg_res['message'] - if ret['result']: + zonecfg_res = __salt__["zonecfg.clear_property"](name, property) + zonecfg_new = __salt__["zonecfg.info"](name, show_all=True) + ret["result"] = zonecfg_res["status"] + if "messages" in zonecfg_res: + ret["comment"] = zonecfg_res["message"] + if ret["result"]: if property not in zonecfg_new: - ret['changes'][property] = None + ret["changes"][property] = None elif zonecfg[property] != zonecfg_new[property]: - ret['changes'][property] = zonecfg_new[property] - if ret['comment'] == '': - ret['comment'] = 'The property {0} was cleared!'.format(property) - elif ret['comment'] == '': - if ret['comment'] == '': - ret['comment'] = 'The property {0} did not get cleared!'.format(property) + ret["changes"][property] = zonecfg_new[property] + if ret["comment"] == "": + ret["comment"] = "The property {0} was cleared!".format(property) + elif ret["comment"] == "": + if ret["comment"] == "": + ret["comment"] = "The property {0} did not get cleared!".format( + property + ) else: - ret['result'] = True - ret['comment'] = 'The property {0} does not exist!'.format(property) + ret["result"] = True + ret["comment"] = "The property {0} does not exist!".format(property) else: ## zone does not exist - ret['result'] = False - ret['comment'] = 'The zone {0} is not in the configured, installed, or booted state.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "The zone {0} is not in the configured, installed, or booted state.".format( + name + ) return ret -def resource_present(name, resource_type, resource_selector_property, resource_selector_value, **kwargs): - ''' +def resource_present( + name, resource_type, resource_selector_property, resource_selector_value, **kwargs +): + """ Ensure resource exists with provided properties name : string @@ -277,11 +291,8 @@ def resource_present(name, resource_type, resource_selector_property, resource_s You can set both resource_selector_property and resource_selector_value to None for resources that do not require them. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # sanitize input kwargs = salt.utils.args.clean_kwargs(**kwargs) @@ -289,133 +300,156 @@ def resource_present(name, resource_type, resource_selector_property, resource_s for k, v in kwargs.items(): kwargs[k] = _parse_value(kwargs[k]) - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: ## zone exists - zonecfg = __salt__['zonecfg.info'](name, show_all=True) + zonecfg = __salt__["zonecfg.info"](name, show_all=True) ## update kwargs zonecfg_kwargs = {} zonecfg_kwargs.update(kwargs) - zonecfg_kwargs['zone'] = name - zonecfg_kwargs['resource_type'] = resource_type - zonecfg_kwargs['resource_selector'] = resource_selector_property + zonecfg_kwargs["zone"] = name + zonecfg_kwargs["resource_type"] = resource_type + zonecfg_kwargs["resource_selector"] = resource_selector_property if resource_selector_property: zonecfg_kwargs[resource_selector_property] = resource_selector_value ## check update or add if resource_type in zonecfg: for resource in zonecfg[resource_type]: - if not resource_selector_property or resource[resource_selector_property] == resource_selector_value: - ret['result'] = True + if ( + not resource_selector_property + or resource[resource_selector_property] == resource_selector_value + ): + ret["result"] = True if resource_selector_property: - ret['comment'] = 'the {0} resource {1} is up to date.'.format( - resource_type, - resource_selector_value, + ret["comment"] = "the {0} resource {1} is up to date.".format( + resource_type, resource_selector_value, ) else: - ret['comment'] = 'the {0} resource is up to date.'.format( + ret["comment"] = "the {0} resource is up to date.".format( resource_type, ) ## check if update reauired for key in kwargs: - log.debug('zone.resource_preent - key=%s value=%s current_value=%s', + log.debug( + "zone.resource_preent - key=%s value=%s current_value=%s", key, resource[key] if key in resource else None, _parse_value(kwargs[key]), ) # note: something odd with ncpus property, we fix it here for now - if key == 'ncpus' and key in kwargs: - kwargs[key] = '{0:.2f}'.format(float(kwargs[key])) + if key == "ncpus" and key in kwargs: + kwargs[key] = "{0:.2f}".format(float(kwargs[key])) if key not in resource: - ret['result'] = None + ret["result"] = None elif resource[key] != _parse_value(kwargs[key]): - ret['result'] = None + ret["result"] = None ## do update - if ret['result'] is None: - if __opts__['test']: - ret['result'] = True + if ret["result"] is None: + if __opts__["test"]: + ret["result"] = True else: ## update resource - zonecfg_res = __salt__['zonecfg.update_resource'](**zonecfg_kwargs) - ret['result'] = zonecfg_res['status'] - if 'message' in zonecfg_res: - ret['comment'] = zonecfg_res['message'] + zonecfg_res = __salt__["zonecfg.update_resource"]( + **zonecfg_kwargs + ) + ret["result"] = zonecfg_res["status"] + if "message" in zonecfg_res: + ret["comment"] = zonecfg_res["message"] - if ret['result']: - ret['changes'][resource_type] = {} + if ret["result"]: + ret["changes"][resource_type] = {} if resource_selector_property: - ret['changes'][resource_type][resource_selector_value] = {} - for key in kwargs if ret['result'] else []: + ret["changes"][resource_type][ + resource_selector_value + ] = {} + for key in kwargs if ret["result"] else []: if resource_selector_property: - ret['changes'][resource_type][resource_selector_value][key] = _parse_value(kwargs[key]) + ret["changes"][resource_type][ + resource_selector_value + ][key] = _parse_value(kwargs[key]) else: - ret['changes'][resource_type][key] = _parse_value(kwargs[key]) - if ret['comment'] == '': + ret["changes"][resource_type][key] = _parse_value( + kwargs[key] + ) + if ret["comment"] == "": if resource_selector_property: - ret['comment'] = 'The {0} resource {1} was updated.'.format( - resource_type, - resource_selector_value, + ret[ + "comment" + ] = "The {0} resource {1} was updated.".format( + resource_type, resource_selector_value, ) else: - ret['comment'] = 'The {0} resource was updated.'.format( + ret[ + "comment" + ] = "The {0} resource was updated.".format( resource_type, ) - elif ret['comment'] == '': + elif ret["comment"] == "": if resource_selector_property: - ret['comment'] = 'The {0} resource {1} was not updated.'.format( - resource_type, - resource_selector_value, + ret[ + "comment" + ] = "The {0} resource {1} was not updated.".format( + resource_type, resource_selector_value, ) else: - ret['comment'] = 'The {0} resource was not updated.'.format( + ret[ + "comment" + ] = "The {0} resource was not updated.".format( resource_type, ) - if ret['result'] is None: + if ret["result"] is None: ## add - if __opts__['test']: - ret['result'] = True + if __opts__["test"]: + ret["result"] = True else: ## add resource - if 'resource_selector' in zonecfg_kwargs: - del zonecfg_kwargs['resource_selector'] - zonecfg_res = __salt__['zonecfg.add_resource'](**zonecfg_kwargs) - ret['result'] = zonecfg_res['status'] - if 'message' in zonecfg_res: - ret['comment'] = zonecfg_res['message'] + if "resource_selector" in zonecfg_kwargs: + del zonecfg_kwargs["resource_selector"] + zonecfg_res = __salt__["zonecfg.add_resource"](**zonecfg_kwargs) + ret["result"] = zonecfg_res["status"] + if "message" in zonecfg_res: + ret["comment"] = zonecfg_res["message"] - if ret['result']: - ret['changes'][resource_type] = {} + if ret["result"]: + ret["changes"][resource_type] = {} if resource_selector_property: - ret['changes'][resource_type][resource_selector_value] = {} - for key in kwargs if ret['result'] else []: + ret["changes"][resource_type][resource_selector_value] = {} + for key in kwargs if ret["result"] else []: if resource_selector_property: - ret['changes'][resource_type][resource_selector_value][key] = _parse_value(kwargs[key]) + ret["changes"][resource_type][resource_selector_value][ + key + ] = _parse_value(kwargs[key]) else: - ret['changes'][resource_type][key] = _parse_value(kwargs[key]) - if ret['comment'] == '': - ret['comment'] = 'The {0} resource {1} was added.'.format( - resource_type, - resource_selector_value, + ret["changes"][resource_type][key] = _parse_value(kwargs[key]) + if ret["comment"] == "": + ret["comment"] = "The {0} resource {1} was added.".format( + resource_type, resource_selector_value, ) - elif ret['comment'] == '': - ret['comment'] = 'The {0} resource {1} was not added.'.format( - resource_type, - resource_selector_value, + elif ret["comment"] == "": + ret["comment"] = "The {0} resource {1} was not added.".format( + resource_type, resource_selector_value, ) else: ## zone does not exist - ret['result'] = False - ret['comment'] = 'The zone {0} is not in the configured, installed, or booted state.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "The zone {0} is not in the configured, installed, or booted state.".format( + name + ) return ret -def resource_absent(name, resource_type, resource_selector_property, resource_selector_value): - ''' +def resource_absent( + name, resource_type, resource_selector_property, resource_selector_value +): + """ Ensure resource is absent name : string @@ -435,11 +469,8 @@ def resource_absent(name, resource_type, resource_selector_property, resource_se You can set both resource_selector_property and resource_selector_value to None for resources that do not require them. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # sanitize input if resource_selector_property: @@ -447,75 +478,78 @@ def resource_absent(name, resource_type, resource_selector_property, resource_se else: resource_selector_value = None - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: ## zone exists - zonecfg = __salt__['zonecfg.info'](name, show_all=True) + zonecfg = __salt__["zonecfg.info"](name, show_all=True) if resource_type in zonecfg: for resource in zonecfg[resource_type]: - if __opts__['test']: - ret['result'] = True + if __opts__["test"]: + ret["result"] = True elif not resource_selector_property: - zonecfg_res = __salt__['zonecfg.remove_resource']( + zonecfg_res = __salt__["zonecfg.remove_resource"]( zone=name, resource_type=resource_type, resource_key=None, resource_value=None, ) - ret['result'] = zonecfg_res['status'] - if zonecfg_res['status']: - ret['changes'][resource_type] = 'removed' - if ret['comment'] == '': - ret['comment'] = 'The {0} resource was removed.'.format( + ret["result"] = zonecfg_res["status"] + if zonecfg_res["status"]: + ret["changes"][resource_type] = "removed" + if ret["comment"] == "": + ret["comment"] = "The {0} resource was removed.".format( resource_type, ) - elif 'messages' in zonecfg_res: - ret['comment'] = zonecfg_res['message'] + elif "messages" in zonecfg_res: + ret["comment"] = zonecfg_res["message"] else: - ret['comment'] = 'The {0} resource was not removed.'.format( + ret["comment"] = "The {0} resource was not removed.".format( resource_type, ) elif resource[resource_selector_property] == resource_selector_value: - zonecfg_res = __salt__['zonecfg.remove_resource']( + zonecfg_res = __salt__["zonecfg.remove_resource"]( zone=name, resource_type=resource_type, resource_key=resource_selector_property, resource_value=resource_selector_value, ) - ret['result'] = zonecfg_res['status'] - if zonecfg_res['status']: - ret['changes'][resource_type] = {} - ret['changes'][resource_type][resource_selector_value] = 'removed' - if ret['comment'] == '': - ret['comment'] = 'The {0} resource {1} was removed.'.format( - resource_type, - resource_selector_value, + ret["result"] = zonecfg_res["status"] + if zonecfg_res["status"]: + ret["changes"][resource_type] = {} + ret["changes"][resource_type][ + resource_selector_value + ] = "removed" + if ret["comment"] == "": + ret["comment"] = "The {0} resource {1} was removed.".format( + resource_type, resource_selector_value, ) - elif 'messages' in zonecfg_res: - ret['comment'] = zonecfg_res['message'] + elif "messages" in zonecfg_res: + ret["comment"] = zonecfg_res["message"] else: - ret['comment'] = 'The {0} resource {1} was not removed.'.format( - resource_type, - resource_selector_value, + ret["comment"] = "The {0} resource {1} was not removed.".format( + resource_type, resource_selector_value, ) # resource already absent - if ret['result'] is None: - ret['result'] = True - ret['comment'] = 'The {0} resource {1} was absent.'.format( - resource_type, - resource_selector_value, + if ret["result"] is None: + ret["result"] = True + ret["comment"] = "The {0} resource {1} was absent.".format( + resource_type, resource_selector_value, ) else: ## zone does not exist - ret['result'] = False - ret['comment'] = 'The zone {0} is not in the configured, installed, or booted state.'.format(name) + ret["result"] = False + ret[ + "comment" + ] = "The zone {0} is not in the configured, installed, or booted state.".format( + name + ) return ret def booted(name, single=False): - ''' + """ Ensure zone is booted name : string @@ -523,53 +557,49 @@ def booted(name, single=False): single : boolean boot in single usermode - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True) + zones = __salt__["zoneadm.list"](installed=True) if name in zones: ## zone exists - if zones[name]['state'] == 'running': + if zones[name]["state"] == "running": ## zone is running - ret['result'] = True - ret['comment'] = 'Zone {0} already booted'.format(name) + ret["result"] = True + ret["comment"] = "Zone {0} already booted".format(name) else: ## try and boot the zone - if not __opts__['test']: - zoneadm_res = __salt__['zoneadm.boot'](name, single) - if __opts__['test'] or zoneadm_res['status']: - ret['result'] = True - ret['changes'][name] = 'booted' - ret['comment'] = 'Zone {0} booted'.format(name) + if not __opts__["test"]: + zoneadm_res = __salt__["zoneadm.boot"](name, single) + if __opts__["test"] or zoneadm_res["status"]: + ret["result"] = True + ret["changes"][name] = "booted" + ret["comment"] = "Zone {0} booted".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to boot {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to boot {0}".format(name) else: ## zone does not exist - ret['comment'] = [] - ret['comment'].append( - 'The zone {0} is not in the installed or booted state.'.format(name) + ret["comment"] = [] + ret["comment"].append( + "The zone {0} is not in the installed or booted state.".format(name) ) for zone in zones: - if zones[zone]['uuid'] == name: - ret['comment'].append( - 'The zone {0} has a uuid of {1}, please use the zone name instead!'.format( - zone, - name, + if zones[zone]["uuid"] == name: + ret["comment"].append( + "The zone {0} has a uuid of {1}, please use the zone name instead!".format( + zone, name, ) ) - ret['result'] = False - ret['comment'] = "\n".join(ret['comment']) + ret["result"] = False + ret["comment"] = "\n".join(ret["comment"]) return ret def halted(name, graceful=True): - ''' + """ Ensure zone is halted name : string @@ -577,53 +607,53 @@ def halted(name, graceful=True): graceful : boolean use shutdown instead of halt if true - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True) + zones = __salt__["zoneadm.list"](installed=True) if name in zones: ## zone exists - if zones[name]['state'] != 'running': + if zones[name]["state"] != "running": ## zone is not running - ret['result'] = True - ret['comment'] = 'Zone {0} already halted'.format(name) + ret["result"] = True + ret["comment"] = "Zone {0} already halted".format(name) else: ## try and halt the zone - if not __opts__['test']: - zoneadm_res = __salt__['zoneadm.shutdown'](name) if graceful else __salt__['zoneadm.halt'](name) - if __opts__['test'] or zoneadm_res['status']: - ret['result'] = True - ret['changes'][name] = 'halted' - ret['comment'] = 'Zone {0} halted'.format(name) + if not __opts__["test"]: + zoneadm_res = ( + __salt__["zoneadm.shutdown"](name) + if graceful + else __salt__["zoneadm.halt"](name) + ) + if __opts__["test"] or zoneadm_res["status"]: + ret["result"] = True + ret["changes"][name] = "halted" + ret["comment"] = "Zone {0} halted".format(name) else: - ret['result'] = False - ret['comment'] = 'Failed to halt {0}'.format(name) + ret["result"] = False + ret["comment"] = "Failed to halt {0}".format(name) else: ## zone does not exist - ret['comment'] = [] - ret['comment'].append( - 'The zone {0} is not in the installed state.'.format(name) + ret["comment"] = [] + ret["comment"].append( + "The zone {0} is not in the installed state.".format(name) ) for zone in zones: - if zones[zone]['uuid'] == name: - ret['comment'].append( - 'The zone {0} has a uuid of {1}, please use the zone name instead!'.format( - zone, - name, + if zones[zone]["uuid"] == name: + ret["comment"].append( + "The zone {0} has a uuid of {1}, please use the zone name instead!".format( + zone, name, ) ) ## note: a non existing zone is not running, we do not consider this a failure - ret['result'] = True - ret['comment'] = "\n".join(ret['comment']) + ret["result"] = True + ret["comment"] = "\n".join(ret["comment"]) return ret def export(name, path, replace=False): - ''' + """ Export a zones configuration name : string @@ -633,113 +663,113 @@ def export(name, path, replace=False): replace : boolean replace the file if it exists - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: ## zone exists - if __opts__['test']: + if __opts__["test"]: ## pretend we did the correct thing - ret['result'] = True - ret['comment'] = 'Zone configartion for {0} exported to {1}'.format( - name, - path, + ret["result"] = True + ret["comment"] = "Zone configartion for {0} exported to {1}".format( + name, path, ) - ret['changes'][name] = 'exported' - if __salt__['file.file_exists'](path) and not replace: - ret['result'] = False - ret['changes'] = {} - ret['comment'] = 'File {0} exists, zone configuration for {1} not exported.'.format( - path, - name, + ret["changes"][name] = "exported" + if __salt__["file.file_exists"](path) and not replace: + ret["result"] = False + ret["changes"] = {} + ret[ + "comment" + ] = "File {0} exists, zone configuration for {1} not exported.".format( + path, name, ) else: ## export and update file cfg_tmp = salt.utils.files.mkstemp() - __salt__['zonecfg.export'](name, cfg_tmp) - if not __salt__['file.file_exists'](path): + __salt__["zonecfg.export"](name, cfg_tmp) + if not __salt__["file.file_exists"](path): ## move cfg_tmp to path try: - __salt__['file.move'](cfg_tmp, path) + __salt__["file.move"](cfg_tmp, path) except CommandExecutionError: - if __salt__['file.file_exists'](cfg_tmp): - __salt__['file.remove'](cfg_tmp) - ret['result'] = False - ret['comment'] = 'Unable to export zone configuration for {0} to {1}!'.format( - name, - path, + if __salt__["file.file_exists"](cfg_tmp): + __salt__["file.remove"](cfg_tmp) + ret["result"] = False + ret[ + "comment" + ] = "Unable to export zone configuration for {0} to {1}!".format( + name, path, ) else: - ret['result'] = True - ret['comment'] = 'Zone configuration for {0} was exported to {1}.'.format( - name, - path, + ret["result"] = True + ret[ + "comment" + ] = "Zone configuration for {0} was exported to {1}.".format( + name, path, ) - ret['changes'][name] = 'exported' + ret["changes"][name] = "exported" else: - cfg_diff = __salt__['file.get_diff'](path, cfg_tmp) + cfg_diff = __salt__["file.get_diff"](path, cfg_tmp) if not cfg_diff: - ret['result'] = True - ret['comment'] = 'Zone configuration for {0} was already exported to {1}.'.format( - name, - path + ret["result"] = True + ret[ + "comment" + ] = "Zone configuration for {0} was already exported to {1}.".format( + name, path ) - if __salt__['file.file_exists'](cfg_tmp): - __salt__['file.remove'](cfg_tmp) + if __salt__["file.file_exists"](cfg_tmp): + __salt__["file.remove"](cfg_tmp) else: if replace: try: - __salt__['file.move'](cfg_tmp, path) + __salt__["file.move"](cfg_tmp, path) except CommandExecutionError: - if __salt__['file.file_exists'](cfg_tmp): - __salt__['file.remove'](cfg_tmp) - ret['result'] = False - ret['comment'] = 'Unable to be re-export zone configuration for {0} to {1}!'.format( - name, - path, + if __salt__["file.file_exists"](cfg_tmp): + __salt__["file.remove"](cfg_tmp) + ret["result"] = False + ret[ + "comment" + ] = "Unable to be re-export zone configuration for {0} to {1}!".format( + name, path, ) else: - ret['result'] = True - ret['comment'] = 'Zone configuration for {0} was re-exported to {1}.'.format( - name, - path, + ret["result"] = True + ret[ + "comment" + ] = "Zone configuration for {0} was re-exported to {1}.".format( + name, path, ) - ret['changes'][name] = 'exported' + ret["changes"][name] = "exported" else: - ret['result'] = False - ret['comment'] = 'Zone configuration for {0} is different from the one exported to {1}!'.format( - name, - path + ret["result"] = False + ret[ + "comment" + ] = "Zone configuration for {0} is different from the one exported to {1}!".format( + name, path ) - if __salt__['file.file_exists'](cfg_tmp): - __salt__['file.remove'](cfg_tmp) + if __salt__["file.file_exists"](cfg_tmp): + __salt__["file.remove"](cfg_tmp) else: ## zone does not exist - ret['comment'] = [] - ret['comment'].append( - 'The zone {0} does not exist.'.format(name) - ) + ret["comment"] = [] + ret["comment"].append("The zone {0} does not exist.".format(name)) for zone in zones: - if zones[zone]['uuid'] == name: - ret['comment'].append( - 'The zone {0} has a uuid of {1}, please use the zone name instead!'.format( - name, - path, + if zones[zone]["uuid"] == name: + ret["comment"].append( + "The zone {0} has a uuid of {1}, please use the zone name instead!".format( + name, path, ) ) - ret['result'] = False - ret['comment'] = "\n".join(ret['comment']) + ret["result"] = False + ret["comment"] = "\n".join(ret["comment"]) return ret -def import_(name, path, mode='import', nodataset=False, brand_opts=None): - ''' +def import_(name, path, mode="import", nodataset=False, brand_opts=None): + """ Import a zones configuration name : string @@ -764,83 +794,84 @@ def import_(name, path, mode='import', nodataset=False, brand_opts=None): omipkg1: zone.import: - path: /foo/bar/baz - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name not in zones: - if __opts__['test']: - ret['result'] = True - ret['comment'] = 'Zone {0} was imported from {1}.'.format( - name, - path, - ) - ret['changes'][name] = 'imported' + if __opts__["test"]: + ret["result"] = True + ret["comment"] = "Zone {0} was imported from {1}.".format(name, path,) + ret["changes"][name] = "imported" else: - if __salt__['file.file_exists'](path): - res_import = __salt__['zonecfg.import'](name, path) - if not res_import['status']: - ret['result'] = False - ret['comment'] = 'Unable to import zone configuration for {0}!'.format(name) + if __salt__["file.file_exists"](path): + res_import = __salt__["zonecfg.import"](name, path) + if not res_import["status"]: + ret["result"] = False + ret[ + "comment" + ] = "Unable to import zone configuration for {0}!".format(name) else: - ret['result'] = True - ret['changes'][name] = 'imported' - ret['comment'] = 'Zone {0} was imported from {1}.'.format( - name, - path, + ret["result"] = True + ret["changes"][name] = "imported" + ret["comment"] = "Zone {0} was imported from {1}.".format( + name, path, ) - if mode.lower() == 'attach': - res_attach = __salt__['zoneadm.attach'](name, False, brand_opts) - ret['result'] = res_attach['status'] - if res_attach['status']: - ret['changes'][name] = 'attached' - ret['comment'] = 'Zone {0} was attached from {1}.'.format( - name, - path, + if mode.lower() == "attach": + res_attach = __salt__["zoneadm.attach"](name, False, brand_opts) + ret["result"] = res_attach["status"] + if res_attach["status"]: + ret["changes"][name] = "attached" + ret["comment"] = "Zone {0} was attached from {1}.".format( + name, path, ) else: - ret['comment'] = [] - ret['comment'].append('Failed to attach zone {0} from {1}!'.format( - name, - path, - )) - if 'message' in res_attach: - ret['comment'].append(res_attach['message']) - ret['comment'] = "\n".join(ret['comment']) - if mode.lower() == 'install': - res_install = __salt__['zoneadm.install'](name, nodataset, brand_opts) - ret['result'] = res_install['status'] - if res_install['status']: - ret['changes'][name] = 'installed' - ret['comment'] = 'Zone {0} was installed from {1}.'.format( - name, - path, + ret["comment"] = [] + ret["comment"].append( + "Failed to attach zone {0} from {1}!".format( + name, path, + ) + ) + if "message" in res_attach: + ret["comment"].append(res_attach["message"]) + ret["comment"] = "\n".join(ret["comment"]) + if mode.lower() == "install": + res_install = __salt__["zoneadm.install"]( + name, nodataset, brand_opts + ) + ret["result"] = res_install["status"] + if res_install["status"]: + ret["changes"][name] = "installed" + ret["comment"] = "Zone {0} was installed from {1}.".format( + name, path, ) else: - ret['comment'] = [] - ret['comment'].append('Failed to install zone {0} from {1}!'.format( - name, - path, - )) - if 'message' in res_install: - ret['comment'].append(res_install['message']) - ret['comment'] = "\n".join(ret['comment']) + ret["comment"] = [] + ret["comment"].append( + "Failed to install zone {0} from {1}!".format( + name, path, + ) + ) + if "message" in res_install: + ret["comment"].append(res_install["message"]) + ret["comment"] = "\n".join(ret["comment"]) else: - ret['result'] = False - ret['comment'] = 'The file {0} does not exists, unable to import!'.format(path) + ret["result"] = False + ret[ + "comment" + ] = "The file {0} does not exists, unable to import!".format(path) else: ## zone exist - ret['result'] = True - ret['comment'] = 'Zone {0} already exists, not importing configuration.'.format(name) + ret["result"] = True + ret["comment"] = "Zone {0} already exists, not importing configuration.".format( + name + ) return ret def present(name, brand, zonepath, properties=None, resources=None): - ''' + """ Ensure a zone with certain properties and resources name : string @@ -879,11 +910,8 @@ def present(name, brand, zonepath, properties=None, resources=None): ```resource_selector_property``` if the default is not the one you want. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': []} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": []} ## sanitize defaults if not properties: @@ -893,33 +921,35 @@ def present(name, brand, zonepath, properties=None, resources=None): properties.append(OrderedDict({"brand": brand})) properties.append(OrderedDict({"zonepath": zonepath})) - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) ## test mode only has limited support - if __opts__['test']: - ret['result'] = None - ret['comment'].append('Cannot determine of changes would happen to the zone {0}.'.format(name)) + if __opts__["test"]: + ret["result"] = None + ret["comment"].append( + "Cannot determine of changes would happen to the zone {0}.".format(name) + ) ## create zone if needed if name not in zones: - if __opts__['test']: + if __opts__["test"]: ## we pretend we created the zone - res_create = {'status': True} - ret['comment'] = [] + res_create = {"status": True} + ret["comment"] = [] else: ## create and install - res_create = __salt__['zonecfg.create'](name, brand, zonepath) - if res_create['status']: - ret['result'] = True - ret['changes'][name] = 'created' - ret['comment'].append('The zone {0} was created.'.format(name)) + res_create = __salt__["zonecfg.create"](name, brand, zonepath) + if res_create["status"]: + ret["result"] = True + ret["changes"][name] = "created" + ret["comment"].append("The zone {0} was created.".format(name)) - if not __opts__['test']: - ret['result'] = True + if not __opts__["test"]: + ret["result"] = True if isinstance(properties, list): for prop in properties: if not isinstance(prop, OrderedDict) or len(prop) != 1: - log.warning('zone.present - failed to parse property: %s', prop) + log.warning("zone.present - failed to parse property: %s", prop) continue for key, value in prop.items(): res = None @@ -928,68 +958,89 @@ def present(name, brand, zonepath, properties=None, resources=None): elif value: res = property_present(name, key, value) if res: - ret['result'] = ret['result'] if res['result'] else False - ret['comment'].append(res['comment']) - if len(res['changes']) > 0: - if 'property' not in ret['changes']: - ret['changes']['property'] = {} - ret['changes']['property'] = merge_dict(ret['changes']['property'], res['changes']) + ret["result"] = ret["result"] if res["result"] else False + ret["comment"].append(res["comment"]) + if len(res["changes"]) > 0: + if "property" not in ret["changes"]: + ret["changes"]["property"] = {} + ret["changes"]["property"] = merge_dict( + ret["changes"]["property"], res["changes"] + ) if isinstance(resources, list): for resource in resources: if not isinstance(prop, OrderedDict) or len(prop) != 1: - log.warning('zone.present - failed to parse resource: %s', resource) + log.warning("zone.present - failed to parse resource: %s", resource) continue for key, value in resource.items(): - zonecfg = __salt__['zonecfg.info'](name, show_all=True) + zonecfg = __salt__["zonecfg.info"](name, show_all=True) resource_cfg = {} - resource_cfg['resource_type'] = key + resource_cfg["resource_type"] = key if isinstance(value, list): for respv in value: resource_cfg.update(dict(respv)) resource_prune = False resource_selector_property = None - if 'resource_prune' in resource_cfg: - resource_prune = resource_cfg['resource_prune'] - del resource_cfg['resource_prune'] - if 'resource_selector_property' in resource_cfg: - resource_selector_property = resource_cfg['resource_selector_property'] - del resource_cfg['resource_selector_property'] - if not resource_selector_property and key in _zonecfg_resource_default_selectors: - resource_selector_property = _zonecfg_resource_default_selectors[key] + if "resource_prune" in resource_cfg: + resource_prune = resource_cfg["resource_prune"] + del resource_cfg["resource_prune"] + if "resource_selector_property" in resource_cfg: + resource_selector_property = resource_cfg[ + "resource_selector_property" + ] + del resource_cfg["resource_selector_property"] + if ( + not resource_selector_property + and key in _zonecfg_resource_default_selectors + ): + resource_selector_property = _zonecfg_resource_default_selectors[ + key + ] res = None if resource_prune: res = resource_absent( name, - resource_cfg['resource_type'], + resource_cfg["resource_type"], resource_selector_property=resource_selector_property, - resource_selector_value=resource_cfg[resource_selector_property] if resource_selector_property else None, + resource_selector_value=resource_cfg[ + resource_selector_property + ] + if resource_selector_property + else None, ) else: - resource_cfg['resource_selector_property'] = resource_selector_property + resource_cfg[ + "resource_selector_property" + ] = resource_selector_property if resource_selector_property in resource_cfg: - resource_cfg['resource_selector_value'] = resource_cfg[resource_selector_property] + resource_cfg["resource_selector_value"] = resource_cfg[ + resource_selector_property + ] else: - resource_cfg['resource_selector_value'] = None - resource_cfg['name'] = name # we do this last because name can also be a attrib value + resource_cfg["resource_selector_value"] = None + resource_cfg[ + "name" + ] = name # we do this last because name can also be a attrib value res = resource_present(**resource_cfg) if res: - ret['result'] = ret['result'] if res['result'] else False - ret['comment'].append(res['comment']) - if len(res['changes']) > 0: - if 'resource' not in ret['changes']: - ret['changes']['resource'] = {} - ret['changes']['resource'] = merge_dict(ret['changes']['resource'], res['changes']) + ret["result"] = ret["result"] if res["result"] else False + ret["comment"].append(res["comment"]) + if len(res["changes"]) > 0: + if "resource" not in ret["changes"]: + ret["changes"]["resource"] = {} + ret["changes"]["resource"] = merge_dict( + ret["changes"]["resource"], res["changes"] + ) - if isinstance(ret['comment'], list): - ret['comment'] = "\n".join(ret['comment']) + if isinstance(ret["comment"], list): + ret["comment"] = "\n".join(ret["comment"]) return ret def absent(name, uninstall=False): - ''' + """ Ensure a zone is absent name : string @@ -997,66 +1048,63 @@ def absent(name, uninstall=False): uninstall : boolean when true, uninstall instead of detaching the zone first. - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: - if __opts__['test']: - ret['result'] = True - ret['changes'][name] = 'removed' - ret['comment'] = 'Zone {0} was removed.'.format(name) + if __opts__["test"]: + ret["result"] = True + ret["changes"][name] = "removed" + ret["comment"] = "Zone {0} was removed.".format(name) else: - ret['result'] = True - if uninstall and zones[name]['state'] in ['running', 'installed']: - res_halt = __salt__['zoneadm.halt'](name) - res_uninstall = __salt__['zoneadm.uninstall'](name) - ret['result'] = res_uninstall['status'] - if ret['result']: - ret['changes'][name] = 'uninstalled' - ret['comment'] = 'The zone {0} was uninstalled.'.format(name) + ret["result"] = True + if uninstall and zones[name]["state"] in ["running", "installed"]: + res_halt = __salt__["zoneadm.halt"](name) + res_uninstall = __salt__["zoneadm.uninstall"](name) + ret["result"] = res_uninstall["status"] + if ret["result"]: + ret["changes"][name] = "uninstalled" + ret["comment"] = "The zone {0} was uninstalled.".format(name) else: - ret['comment'] = [] - ret['comment'].append('Failed to uninstall zone {0}!'.format(name)) - if 'message' in res_uninstall: - ret['comment'].append(res_uninstall['message']) - ret['comment'] = "\n".join(ret['comment']) - elif zones[name]['state'] == 'installed': - res_detach = __salt__['zoneadm.detach'](name) - ret['result'] = res_detach['status'] - if ret['result']: - ret['changes'][name] = 'detached' - ret['comment'] = 'The zone {0} was detached.'.format(name) + ret["comment"] = [] + ret["comment"].append("Failed to uninstall zone {0}!".format(name)) + if "message" in res_uninstall: + ret["comment"].append(res_uninstall["message"]) + ret["comment"] = "\n".join(ret["comment"]) + elif zones[name]["state"] == "installed": + res_detach = __salt__["zoneadm.detach"](name) + ret["result"] = res_detach["status"] + if ret["result"]: + ret["changes"][name] = "detached" + ret["comment"] = "The zone {0} was detached.".format(name) else: - ret['comment'] = [] - ret['comment'].append('Failed to detach zone {0}!'.format(name)) - if 'message' in res_detach: - ret['comment'].append(res_detach['message']) - ret['comment'] = "\n".join(ret['comment']) - if ret['result']: - res_delete = __salt__['zonecfg.delete'](name) - ret['result'] = res_delete['status'] - if ret['result']: - ret['changes'][name] = 'deleted' - ret['comment'] = 'The zone {0} was delete.'.format(name) + ret["comment"] = [] + ret["comment"].append("Failed to detach zone {0}!".format(name)) + if "message" in res_detach: + ret["comment"].append(res_detach["message"]) + ret["comment"] = "\n".join(ret["comment"]) + if ret["result"]: + res_delete = __salt__["zonecfg.delete"](name) + ret["result"] = res_delete["status"] + if ret["result"]: + ret["changes"][name] = "deleted" + ret["comment"] = "The zone {0} was delete.".format(name) else: - ret['comment'] = [] - ret['comment'].append('Failed to delete zone {0}!'.format(name)) - if 'message' in res_delete: - ret['comment'].append(res_delete['message']) - ret['comment'] = "\n".join(ret['comment']) + ret["comment"] = [] + ret["comment"].append("Failed to delete zone {0}!".format(name)) + if "message" in res_delete: + ret["comment"].append(res_delete["message"]) + ret["comment"] = "\n".join(ret["comment"]) else: - ret['result'] = True - ret['comment'] = 'Zone {0} does not exist.'.format(name) + ret["result"] = True + ret["comment"] = "Zone {0} does not exist.".format(name) return ret def attached(name, force=False): - ''' + """ Ensure zone is attached name : string @@ -1064,82 +1112,76 @@ def attached(name, force=False): force : boolean force attach the zone - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: - if zones[name]['state'] == 'configured': - if __opts__['test']: - res_attach = {'status': True} + if zones[name]["state"] == "configured": + if __opts__["test"]: + res_attach = {"status": True} else: - res_attach = __salt__['zoneadm.attach'](name, force) - ret['result'] = res_attach['status'] - if ret['result']: - ret['changes'][name] = 'attached' - ret['comment'] = 'The zone {0} was attached.'.format(name) + res_attach = __salt__["zoneadm.attach"](name, force) + ret["result"] = res_attach["status"] + if ret["result"]: + ret["changes"][name] = "attached" + ret["comment"] = "The zone {0} was attached.".format(name) else: - ret['comment'] = [] - ret['comment'].append('Failed to attach zone {0}!'.format(name)) - if 'message' in res_attach: - ret['comment'].append(res_attach['message']) - ret['comment'] = "\n".join(ret['comment']) + ret["comment"] = [] + ret["comment"].append("Failed to attach zone {0}!".format(name)) + if "message" in res_attach: + ret["comment"].append(res_attach["message"]) + ret["comment"] = "\n".join(ret["comment"]) else: - ret['result'] = True - ret['comment'] = 'zone {0} already attached.'.format(name) + ret["result"] = True + ret["comment"] = "zone {0} already attached.".format(name) else: - ret['result'] = False - ret['comment'] = 'zone {0} is not configured!'.format(name) + ret["result"] = False + ret["comment"] = "zone {0} is not configured!".format(name) return ret def detached(name): - ''' + """ Ensure zone is detached name : string name of the zone - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: - if zones[name]['state'] != 'configured': - if __opts__['test']: - res_detach = {'status': True} + if zones[name]["state"] != "configured": + if __opts__["test"]: + res_detach = {"status": True} else: - res_detach = __salt__['zoneadm.detach'](name) - ret['result'] = res_detach['status'] - if ret['result']: - ret['changes'][name] = 'detached' - ret['comment'] = 'The zone {0} was detached.'.format(name) + res_detach = __salt__["zoneadm.detach"](name) + ret["result"] = res_detach["status"] + if ret["result"]: + ret["changes"][name] = "detached" + ret["comment"] = "The zone {0} was detached.".format(name) else: - ret['comment'] = [] - ret['comment'].append('Failed to detach zone {0}!'.format(name)) - if 'message' in res_detach: - ret['comment'].append(res_detach['message']) - ret['comment'] = "\n".join(ret['comment']) + ret["comment"] = [] + ret["comment"].append("Failed to detach zone {0}!".format(name)) + if "message" in res_detach: + ret["comment"].append(res_detach["message"]) + ret["comment"] = "\n".join(ret["comment"]) else: - ret['result'] = True - ret['comment'] = 'zone {0} already detached.'.format(name) + ret["result"] = True + ret["comment"] = "zone {0} already detached.".format(name) else: ## note: a non existing zone is not attached, we do not consider this a failure - ret['result'] = True - ret['comment'] = 'zone {0} is not configured!'.format(name) + ret["result"] = True + ret["comment"] = "zone {0} is not configured!".format(name) return ret def installed(name, nodataset=False, brand_opts=None): - ''' + """ Ensure zone is installed name : string @@ -1149,77 +1191,72 @@ def installed(name, nodataset=False, brand_opts=None): brand_opts : boolean brand specific options to pass - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: - if zones[name]['state'] == 'configured': - if __opts__['test']: - res_install = {'status': True} + if zones[name]["state"] == "configured": + if __opts__["test"]: + res_install = {"status": True} else: - res_install = __salt__['zoneadm.install'](name, nodataset, brand_opts) - ret['result'] = res_install['status'] - if ret['result']: - ret['changes'][name] = 'installed' - ret['comment'] = 'The zone {0} was installed.'.format(name) + res_install = __salt__["zoneadm.install"](name, nodataset, brand_opts) + ret["result"] = res_install["status"] + if ret["result"]: + ret["changes"][name] = "installed" + ret["comment"] = "The zone {0} was installed.".format(name) else: - ret['comment'] = [] - ret['comment'].append('Failed to install zone {0}!'.format(name)) - if 'message' in res_install: - ret['comment'].append(res_install['message']) - ret['comment'] = "\n".join(ret['comment']) + ret["comment"] = [] + ret["comment"].append("Failed to install zone {0}!".format(name)) + if "message" in res_install: + ret["comment"].append(res_install["message"]) + ret["comment"] = "\n".join(ret["comment"]) else: - ret['result'] = True - ret['comment'] = 'zone {0} already installed.'.format(name) + ret["result"] = True + ret["comment"] = "zone {0} already installed.".format(name) else: - ret['result'] = False - ret['comment'] = 'zone {0} is not configured!'.format(name) + ret["result"] = False + ret["comment"] = "zone {0} is not configured!".format(name) return ret def uninstalled(name): - ''' + """ Ensure zone is uninstalled name : string name of the zone - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - zones = __salt__['zoneadm.list'](installed=True, configured=True) + zones = __salt__["zoneadm.list"](installed=True, configured=True) if name in zones: - if zones[name]['state'] != 'configured': - if __opts__['test']: - res_uninstall = {'status': True} + if zones[name]["state"] != "configured": + if __opts__["test"]: + res_uninstall = {"status": True} else: - res_uninstall = __salt__['zoneadm.uninstall'](name) - ret['result'] = res_uninstall['status'] - if ret['result']: - ret['changes'][name] = 'uninstalled' - ret['comment'] = 'The zone {0} was uninstalled.'.format(name) + res_uninstall = __salt__["zoneadm.uninstall"](name) + ret["result"] = res_uninstall["status"] + if ret["result"]: + ret["changes"][name] = "uninstalled" + ret["comment"] = "The zone {0} was uninstalled.".format(name) else: - ret['comment'] = [] - ret['comment'].append('Failed to uninstall zone {0}!'.format(name)) - if 'message' in res_uninstall: - ret['comment'].append(res_uninstall['message']) - ret['comment'] = "\n".join(ret['comment']) + ret["comment"] = [] + ret["comment"].append("Failed to uninstall zone {0}!".format(name)) + if "message" in res_uninstall: + ret["comment"].append(res_uninstall["message"]) + ret["comment"] = "\n".join(ret["comment"]) else: - ret['result'] = True - ret['comment'] = 'zone {0} already uninstalled.'.format(name) + ret["result"] = True + ret["comment"] = "zone {0} already uninstalled.".format(name) else: ## note: a non existing zone is not installed, we do not consider this a failure - ret['result'] = True - ret['comment'] = 'zone {0} is not configured!'.format(name) + ret["result"] = True + ret["comment"] = "zone {0} is not configured!".format(name) return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/states/zookeeper.py b/salt/states/zookeeper.py index 46f6e41e39b..525bf65b782 100644 --- a/salt/states/zookeeper.py +++ b/salt/states/zookeeper.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :depends: kazoo :configuration: See :py:mod:`salt.modules.zookeeper` for setup instructions. @@ -24,18 +24,18 @@ The following options can be included in the acl dictionary: :type admin: bool :param all: All permissions. :type all: bool -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -__virtualname__ = 'zookeeper' +__virtualname__ = "zookeeper" def __virtual__(): - if 'zookeeper.create' in __salt__: + if "zookeeper.create" in __salt__: return __virtualname__ - return False + return (False, "zookeeper module could not be loaded") def _check_acls(left, right): @@ -44,9 +44,22 @@ def _check_acls(left, right): return first and second -def present(name, value, acls=None, ephemeral=False, sequence=False, makepath=False, version=-1, - profile=None, hosts=None, scheme=None, username=None, password=None, default_acl=None): - ''' +def present( + name, + value, + acls=None, + ephemeral=False, + sequence=False, + makepath=False, + version=-1, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Make sure znode is present in the correct state with the correct acls name @@ -117,92 +130,114 @@ def present(name, value, acls=None, ephemeral=False, sequence=False, makepath=Fa delete: true admin: true - makepath: True - ''' + """ - ret = {'name': name, - 'result': False, - 'comment': 'Failed to setup znode {0}'.format(name), - 'changes': {}} - connkwargs = {'profile': profile, 'hosts': hosts, 'scheme': scheme, - 'username': username, 'password': password, - 'default_acl': default_acl} + ret = { + "name": name, + "result": False, + "comment": "Failed to setup znode {0}".format(name), + "changes": {}, + } + connkwargs = { + "profile": profile, + "hosts": hosts, + "scheme": scheme, + "username": username, + "password": password, + "default_acl": default_acl, + } if acls is None: chk_acls = [] else: - chk_acls = [__salt__['zookeeper.make_digest_acl'](**acl) for acl in acls] - if __salt__['zookeeper.exists'](name, **connkwargs): - cur_value = __salt__['zookeeper.get'](name, **connkwargs) - cur_acls = __salt__['zookeeper.get_acls'](name, **connkwargs) + chk_acls = [__salt__["zookeeper.make_digest_acl"](**acl) for acl in acls] + if __salt__["zookeeper.exists"](name, **connkwargs): + cur_value = __salt__["zookeeper.get"](name, **connkwargs) + cur_acls = __salt__["zookeeper.get_acls"](name, **connkwargs) if cur_value == value and _check_acls(cur_acls, chk_acls): - ret['result'] = True - ret['comment'] = 'Znode {0} is already set to the correct value with the correct acls'.format(name) + ret["result"] = True + ret[ + "comment" + ] = "Znode {0} is already set to the correct value with the correct acls".format( + name + ) return ret - elif __opts__['test'] is True: - ret['result'] = None - ret['comment'] = 'Znode {0} is will be updated'.format(name) - ret['changes']['old'] = {} - ret['changes']['new'] = {} + elif __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "Znode {0} is will be updated".format(name) + ret["changes"]["old"] = {} + ret["changes"]["new"] = {} if value != cur_value: - ret['changes']['old']['value'] = cur_value - ret['changes']['new']['value'] = value + ret["changes"]["old"]["value"] = cur_value + ret["changes"]["new"]["value"] = value if not _check_acls(chk_acls, cur_acls): - ret['changes']['old']['acls'] = cur_acls - ret['changes']['new']['acls'] = chk_acls + ret["changes"]["old"]["acls"] = cur_acls + ret["changes"]["new"]["acls"] = chk_acls return ret else: value_result, acl_result = True, True changes = {} if value != cur_value: - __salt__['zookeeper.set'](name, value, version, **connkwargs) - new_value = __salt__['zookeeper.get'](name, **connkwargs) + __salt__["zookeeper.set"](name, value, version, **connkwargs) + new_value = __salt__["zookeeper.get"](name, **connkwargs) value_result = new_value == value - changes.setdefault('new', {}).setdefault('value', new_value) - changes.setdefault('old', {}).setdefault('value', cur_value) + changes.setdefault("new", {}).setdefault("value", new_value) + changes.setdefault("old", {}).setdefault("value", cur_value) if chk_acls and not _check_acls(chk_acls, cur_acls): - __salt__['zookeeper.set_acls'](name, acls, version, **connkwargs) - new_acls = __salt__['zookeeper.get_acls'](name, **connkwargs) + __salt__["zookeeper.set_acls"](name, acls, version, **connkwargs) + new_acls = __salt__["zookeeper.get_acls"](name, **connkwargs) acl_result = _check_acls(new_acls, chk_acls) - changes.setdefault('new', {}).setdefault('acls', new_acls) - changes.setdefault('old', {}).setdefault('value', cur_acls) - ret['changes'] = changes + changes.setdefault("new", {}).setdefault("acls", new_acls) + changes.setdefault("old", {}).setdefault("value", cur_acls) + ret["changes"] = changes if value_result and acl_result: - ret['result'] = True - ret['comment'] = 'Znode {0} successfully updated'.format(name) + ret["result"] = True + ret["comment"] = "Znode {0} successfully updated".format(name) return ret - if __opts__['test'] is True: - ret['result'] = None - ret['comment'] = '{0} is will be created'.format(name) - ret['changes']['old'] = {} - ret['changes']['new'] = {} - ret['changes']['new']['acls'] = chk_acls - ret['changes']['new']['value'] = value + if __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "{0} is will be created".format(name) + ret["changes"]["old"] = {} + ret["changes"]["new"] = {} + ret["changes"]["new"]["acls"] = chk_acls + ret["changes"]["new"]["value"] = value return ret - __salt__['zookeeper.create'](name, value, acls, ephemeral, sequence, makepath, **connkwargs) + __salt__["zookeeper.create"]( + name, value, acls, ephemeral, sequence, makepath, **connkwargs + ) value_result, acl_result = True, True - changes = {'old': {}} + changes = {"old": {}} - new_value = __salt__['zookeeper.get'](name, **connkwargs) + new_value = __salt__["zookeeper.get"](name, **connkwargs) value_result = new_value == value - changes.setdefault('new', {}).setdefault('value', new_value) + changes.setdefault("new", {}).setdefault("value", new_value) - new_acls = __salt__['zookeeper.get_acls'](name, **connkwargs) + new_acls = __salt__["zookeeper.get_acls"](name, **connkwargs) acl_result = acls is None or _check_acls(new_acls, chk_acls) - changes.setdefault('new', {}).setdefault('acls', new_acls) + changes.setdefault("new", {}).setdefault("acls", new_acls) - ret['changes'] = changes + ret["changes"] = changes if value_result and acl_result: - ret['result'] = True - ret['comment'] = 'Znode {0} successfully created'.format(name) + ret["result"] = True + ret["comment"] = "Znode {0} successfully created".format(name) return ret -def absent(name, version=-1, recursive=False, profile=None, hosts=None, scheme=None, - username=None, password=None, default_acl=None): - ''' +def absent( + name, + version=-1, + recursive=False, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Make sure znode is absent name @@ -240,45 +275,61 @@ def absent(name, version=-1, recursive=False, profile=None, hosts=None, scheme=N zookeeper.absent: - name: /test - recursive: True - ''' - ret = {'name': name, - 'result': False, - 'comment': 'Failed to delete znode {0}'.format(name), - 'changes': {}} - connkwargs = {'profile': profile, 'hosts': hosts, 'scheme': scheme, - 'username': username, 'password': password, - 'default_acl': default_acl} + """ + ret = { + "name": name, + "result": False, + "comment": "Failed to delete znode {0}".format(name), + "changes": {}, + } + connkwargs = { + "profile": profile, + "hosts": hosts, + "scheme": scheme, + "username": username, + "password": password, + "default_acl": default_acl, + } - if __salt__['zookeeper.exists'](name, **connkwargs) is False: - ret['result'] = True - ret['comment'] = 'Znode {0} does not exist'.format(name) + if __salt__["zookeeper.exists"](name, **connkwargs) is False: + ret["result"] = True + ret["comment"] = "Znode {0} does not exist".format(name) return ret changes = {} - changes['value'] = __salt__['zookeeper.get'](name, **connkwargs) - changes['acls'] = __salt__['zookeeper.get_acls'](name, **connkwargs) + changes["value"] = __salt__["zookeeper.get"](name, **connkwargs) + changes["acls"] = __salt__["zookeeper.get_acls"](name, **connkwargs) if recursive is True: - changes['children'] = __salt__['zookeeper.get_children'](name, **connkwargs) + changes["children"] = __salt__["zookeeper.get_children"](name, **connkwargs) - if __opts__['test'] is True: - ret['result'] = None - ret['comment'] = 'Znode {0} will be removed'.format(name) - ret['changes']['old'] = changes + if __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "Znode {0} will be removed".format(name) + ret["changes"]["old"] = changes return ret - __salt__['zookeeper.delete'](name, version, recursive, **connkwargs) + __salt__["zookeeper.delete"](name, version, recursive, **connkwargs) - if __salt__['zookeeper.exists'](name, **connkwargs) is False: - ret['result'] = True - ret['comment'] = 'Znode {0} has been removed'.format(name) - ret['changes']['old'] = changes + if __salt__["zookeeper.exists"](name, **connkwargs) is False: + ret["result"] = True + ret["comment"] = "Znode {0} has been removed".format(name) + ret["changes"]["old"] = changes return ret -def acls(name, acls, version=-1, profile=None, hosts=None, scheme=None, - username=None, password=None, default_acl=None): - ''' +def acls( + name, + acls, + version=-1, + profile=None, + hosts=None, + scheme=None, + username=None, + password=None, + default_acl=None, +): + """ Update acls on a znode name @@ -321,42 +372,49 @@ def acls(name, acls, version=-1, profile=None, hosts=None, scheme=None, - username: gtmanfred password: test all: True - ''' - ret = {'name': name, - 'result': False, - 'comment': 'Failed to set acls on znode {0}'.format(name), - 'changes': {}} - connkwargs = {'profile': profile, 'hosts': hosts, 'scheme': scheme, - 'username': username, 'password': password, - 'default_acl': default_acl} + """ + ret = { + "name": name, + "result": False, + "comment": "Failed to set acls on znode {0}".format(name), + "changes": {}, + } + connkwargs = { + "profile": profile, + "hosts": hosts, + "scheme": scheme, + "username": username, + "password": password, + "default_acl": default_acl, + } if isinstance(acls, dict): acls = [acls] - chk_acls = [__salt__['zookeeper.make_digest_acl'](**acl) for acl in acls] + chk_acls = [__salt__["zookeeper.make_digest_acl"](**acl) for acl in acls] - if not __salt__['zookeeper.exists'](name, **connkwargs): - ret['comment'] += ': Znode does not exist' + if not __salt__["zookeeper.exists"](name, **connkwargs): + ret["comment"] += ": Znode does not exist" return ret - cur_acls = __salt__['zookeeper.get_acls'](name, **connkwargs) + cur_acls = __salt__["zookeeper.get_acls"](name, **connkwargs) if _check_acls(cur_acls, chk_acls): - ret['result'] = True - ret['comment'] = 'Znode {0} acls already set'.format(name) + ret["result"] = True + ret["comment"] = "Znode {0} acls already set".format(name) return ret - if __opts__['test'] is True: - ret['result'] = None - ret['comment'] = 'Znode {0} acls will be updated'.format(name) - ret['changes']['old'] = cur_acls - ret['changes']['new'] = chk_acls + if __opts__["test"] is True: + ret["result"] = None + ret["comment"] = "Znode {0} acls will be updated".format(name) + ret["changes"]["old"] = cur_acls + ret["changes"]["new"] = chk_acls return ret - __salt__['zookeeper.set_acls'](name, acls, version, **connkwargs) + __salt__["zookeeper.set_acls"](name, acls, version, **connkwargs) - new_acls = __salt__['zookeeper.get_acls'](name, **connkwargs) - ret['changes'] = {'old': cur_acls, 'new': new_acls} + new_acls = __salt__["zookeeper.get_acls"](name, **connkwargs) + ret["changes"] = {"old": cur_acls, "new": new_acls} if _check_acls(new_acls, chk_acls): - ret['result'] = True - ret['comment'] = 'Znode {0} acls updated'.format(name) + ret["result"] = True + ret["comment"] = "Znode {0} acls updated".format(name) return ret - ret['comment'] = 'Znode {0} acls failed to update'.format(name) + ret["comment"] = "Znode {0} acls failed to update".format(name) return ret diff --git a/salt/states/zpool.py b/salt/states/zpool.py index 683f95f92ec..cb9ed17274d 100644 --- a/salt/states/zpool.py +++ b/salt/states/zpool.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" States for managing zpools :maintainer: Jorge Schrauwen <sjorge@blackdot.be> @@ -68,12 +68,13 @@ States for managing zpools Filesystem properties are also not updated, this should be managed by the zfs state module. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import Python libs import os -import logging # Import Salt libs from salt.utils.odict import OrderedDict @@ -81,20 +82,20 @@ from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) # Define the state's virtual name -__virtualname__ = 'zpool' +__virtualname__ = "zpool" def __virtual__(): - ''' + """ Provides zpool state - ''' - if not __grains__.get('zfs_support'): - return False, 'The zpool state cannot be loaded: zfs not supported' + """ + if not __grains__.get("zfs_support"): + return False, "The zpool state cannot be loaded: zfs not supported" return __virtualname__ def _layout_to_vdev(layout, device_dir=None): - ''' + """ Turn the layout data into usable vdevs spedcification We need to support 2 ways of passing the layout: @@ -117,7 +118,7 @@ def _layout_to_vdev(layout, device_dir=None): disk2 disk3 - ''' + """ vdevs = [] # NOTE: check device_dir exists @@ -131,7 +132,7 @@ def _layout_to_vdev(layout, device_dir=None): if isinstance(vdev, OrderedDict): vdevs.extend(_layout_to_vdev(vdev, device_dir)) else: - if device_dir and vdev[0] != '/': + if device_dir and vdev[0] != "/": vdev = os.path.join(device_dir, vdev) vdevs.append(vdev) @@ -141,21 +142,21 @@ def _layout_to_vdev(layout, device_dir=None): elif isinstance(layout, OrderedDict): for vdev in layout: # NOTE: extract the vdev type and disks in the vdev - vdev_type = vdev.split('-')[0] + vdev_type = vdev.split("-")[0] vdev_disk = layout[vdev] # NOTE: skip appending the dummy type 'disk' - if vdev_type != 'disk': + if vdev_type != "disk": vdevs.append(vdev_type) # NOTE: ensure the disks are a list (legacy layout are not) if not isinstance(vdev_disk, list): - vdev_disk = vdev_disk.split(' ') + vdev_disk = vdev_disk.split(" ") # NOTE: also append the actualy disks behind the type # also prepend device_dir to disks if required for disk in vdev_disk: - if device_dir and disk[0] != '/': + if device_dir and disk[0] != "/": disk = os.path.join(device_dir, disk) vdevs.append(disk) @@ -166,8 +167,10 @@ def _layout_to_vdev(layout, device_dir=None): return vdevs -def present(name, properties=None, filesystem_properties=None, layout=None, config=None): - ''' +def present( + name, properties=None, filesystem_properties=None, layout=None, config=None +): + """ ensure storage pool is present on the system name : string @@ -244,23 +247,20 @@ def present(name, properties=None, filesystem_properties=None, layout=None, conf Creating a 3-way mirror! While you probably expect it to be mirror root vdev with 2 devices + a root vdev of 1 device! - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # config defaults default_config = { - 'import': True, - 'import_dirs': None, - 'device_dir': None, - 'force': False + "import": True, + "import_dirs": None, + "device_dir": None, + "force": False, } - if __grains__['kernel'] == 'SunOS': - default_config['device_dir'] = '/dev/dsk' - elif __grains__['kernel'] == 'Linux': - default_config['device_dir'] = '/dev' + if __grains__["kernel"] == "SunOS": + default_config["device_dir"] = "/dev/dsk" + elif __grains__["kernel"] == "Linux": + default_config["device_dir"] = "/dev" # merge state config if config: @@ -269,43 +269,48 @@ def present(name, properties=None, filesystem_properties=None, layout=None, conf # ensure properties are zfs values if properties: - properties = __utils__['zfs.from_auto_dict'](properties) + properties = __utils__["zfs.from_auto_dict"](properties) elif properties is None: properties = {} if filesystem_properties: - filesystem_properties = __utils__['zfs.from_auto_dict'](filesystem_properties) + filesystem_properties = __utils__["zfs.from_auto_dict"](filesystem_properties) elif filesystem_properties is None: filesystem_properties = {} # parse layout - vdevs = _layout_to_vdev(layout, config['device_dir']) + vdevs = _layout_to_vdev(layout, config["device_dir"]) if vdevs: vdevs.insert(0, name) # log configuration - log.debug('zpool.present::%s::config - %s', name, config) - log.debug('zpool.present::%s::vdevs - %s', name, vdevs) - log.debug('zpool.present::%s::properties - %s', name, properties) - log.debug('zpool.present::%s::filesystem_properties - %s', name, filesystem_properties) + log.debug("zpool.present::%s::config - %s", name, config) + log.debug("zpool.present::%s::vdevs - %s", name, vdevs) + log.debug("zpool.present::%s::properties - %s", name, properties) + log.debug( + "zpool.present::%s::filesystem_properties - %s", name, filesystem_properties + ) # ensure the pool is present - ret['result'] = False + ret["result"] = False # don't do anything because this is a test - if __opts__['test']: - ret['result'] = True - if __salt__['zpool.exists'](name): - ret['changes'][name] = 'uptodate' + if __opts__["test"]: + if __salt__["zpool.exists"](name): + ret["result"] = True + ret["comment"] = "storage pool {0} is {1}".format(name, "uptodate") else: - ret['changes'][name] = 'imported' if config['import'] else 'created' - ret['comment'] = 'storage pool {0} was {1}'.format(name, ret['changes'][name]) + ret["result"] = None + ret["changes"][name] = "imported" if config["import"] else "created" + ret["comment"] = "storage pool {0} would have been {1}".format( + name, ret["changes"][name] + ) # update pool - elif __salt__['zpool.exists'](name): - ret['result'] = True + elif __salt__["zpool.exists"](name): + ret["result"] = True # fetch current pool properties - properties_current = __salt__['zpool.get'](name, parsable=True) + properties_current = __salt__["zpool.get"](name, parsable=True) # build list of properties to update properties_update = [] @@ -313,7 +318,9 @@ def present(name, properties=None, filesystem_properties=None, layout=None, conf for prop in properties: # skip unexisting properties if prop not in properties_current: - log.warning('zpool.present::%s::update - unknown property: %s', name, prop) + log.warning( + "zpool.present::%s::update - unknown property: %s", name, prop + ) continue # compare current and wanted value @@ -322,66 +329,70 @@ def present(name, properties=None, filesystem_properties=None, layout=None, conf # update pool properties for prop in properties_update: - res = __salt__['zpool.set'](name, prop, properties[prop]) + res = __salt__["zpool.set"](name, prop, properties[prop]) - if res['set']: - if name not in ret['changes']: - ret['changes'][name] = {} - ret['changes'][name][prop] = properties[prop] + if res["set"]: + if name not in ret["changes"]: + ret["changes"][name] = {} + ret["changes"][name][prop] = properties[prop] else: - ret['result'] = False - if ret['comment'] == '': - ret['comment'] = 'The following properties were not updated:' - ret['comment'] = '{0} {1}'.format(ret['comment'], prop) + ret["result"] = False + if ret["comment"] == "": + ret["comment"] = "The following properties were not updated:" + ret["comment"] = "{0} {1}".format(ret["comment"], prop) - if ret['result']: - ret['comment'] = 'properties updated' if ret['changes'] else 'no update needed' + if ret["result"]: + ret["comment"] = ( + "properties updated" if ret["changes"] else "no update needed" + ) # import or create the pool (at least try to anyway) else: # import pool - if config['import']: - mod_res = __salt__['zpool.import']( - name, - force=config['force'], - dir=config['import_dirs'], + if config["import"]: + mod_res = __salt__["zpool.import"]( + name, force=config["force"], dir=config["import_dirs"], ) - ret['result'] = mod_res['imported'] - if ret['result']: - ret['changes'][name] = 'imported' - ret['comment'] = 'storage pool {0} was imported'.format(name) + ret["result"] = mod_res["imported"] + if ret["result"]: + ret["changes"][name] = "imported" + ret["comment"] = "storage pool {0} was imported".format(name) # create pool - if not ret['result'] and vdevs: - log.debug('zpool.present::%s::creating', name) + if not ret["result"] and vdevs: + log.debug("zpool.present::%s::creating", name) # execute zpool.create - mod_res = __salt__['zpool.create']( + mod_res = __salt__["zpool.create"]( *vdevs, - force=config['force'], + force=config["force"], properties=properties, filesystem_properties=filesystem_properties ) - ret['result'] = mod_res['created'] - if ret['result']: - ret['changes'][name] = 'created' - ret['comment'] = 'storage pool {0} was created'.format(name) - elif 'error' in mod_res: - ret['comment'] = mod_res['error'] + ret["result"] = mod_res["created"] + if ret["result"]: + ret["changes"][name] = "created" + ret["comment"] = "storage pool {0} was created".format(name) + elif "error" in mod_res: + ret["comment"] = mod_res["error"] else: - ret['comment'] = 'could not create storage pool {0}'.format(name) + ret["comment"] = "could not create storage pool {0}".format(name) # give up, we cannot import the pool and we do not have a layout to create it - if not ret['result'] and not vdevs: - ret['comment'] = 'storage pool {0} was not imported, no (valid) layout specified for creation'.format(name) + if not ret["result"] and not vdevs: + ret[ + "comment" + ] = "storage pool {0} was not imported, no (valid) layout specified for creation".format( + name + ) return ret def absent(name, export=False, force=False): - ''' + """ ensure storage pool is absent on the system name : string @@ -391,45 +402,45 @@ def absent(name, export=False, force=False): force : boolean force destroy or export - ''' - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": name, "changes": {}, "result": None, "comment": ""} # log configuration - log.debug('zpool.absent::%s::config::force = %s', name, force) - log.debug('zpool.absent::%s::config::export = %s', name, export) + log.debug("zpool.absent::%s::config::force = %s", name, force) + log.debug("zpool.absent::%s::config::export = %s", name, export) # ensure the pool is absent - if __salt__['zpool.exists'](name): # looks like we need to do some work + if __salt__["zpool.exists"](name): # looks like we need to do some work mod_res = {} - ret['result'] = False + ret["result"] = False # NOTE: handle test - if __opts__['test']: - ret['result'] = True + if __opts__["test"]: + ret["result"] = True # NOTE: try to export the pool elif export: - mod_res = __salt__['zpool.export'](name, force=force) - ret['result'] = mod_res['exported'] + mod_res = __salt__["zpool.export"](name, force=force) + ret["result"] = mod_res["exported"] # NOTE: try to destroy the pool else: - mod_res = __salt__['zpool.destroy'](name, force=force) - ret['result'] = mod_res['destroyed'] + mod_res = __salt__["zpool.destroy"](name, force=force) + ret["result"] = mod_res["destroyed"] - if ret['result']: # update the changes and comment - ret['changes'][name] = 'exported' if export else 'destroyed' - ret['comment'] = 'storage pool {0} was {1}'.format(name, ret['changes'][name]) - elif 'error' in mod_res: - ret['comment'] = mod_res['error'] + if ret["result"]: # update the changes and comment + ret["changes"][name] = "exported" if export else "destroyed" + ret["comment"] = "storage pool {0} was {1}".format( + name, ret["changes"][name] + ) + elif "error" in mod_res: + ret["comment"] = mod_res["error"] else: # we are looking good - ret['result'] = True - ret['comment'] = 'storage pool {0} is absent'.format(name) + ret["result"] = True + ret["comment"] = "storage pool {0} is absent".format(name) return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/syspaths.py b/salt/syspaths.py index ff60b73f4e1..78613403511 100644 --- a/salt/syspaths.py +++ b/salt/syspaths.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" salt.syspaths ~~~~~~~~~~~~~ @@ -12,23 +12,37 @@ These values are static values and must be considered as secondary to any paths that are set in the master/minion config files. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import sys -import os.path + import logging +import os.path +import sys __PLATFORM = sys.platform.lower() typo_warning = True log = logging.getLogger(__name__) -EXPECTED_VARIABLES = ('ROOT_DIR', 'CONFIG_DIR', 'CACHE_DIR', 'SOCK_DIR', - 'SRV_ROOT_DIR', 'BASE_FILE_ROOTS_DIR', 'HOME_DIR', - 'BASE_PILLAR_ROOTS_DIR', 'BASE_THORIUM_ROOTS_DIR', - 'BASE_MASTER_ROOTS_DIR', 'LOGS_DIR', 'PIDFILE_DIR', - 'SPM_PARENT_PATH', 'SPM_FORMULA_PATH', 'SPM_PILLAR_PATH', - 'SPM_REACTOR_PATH', 'SHARE_DIR') +EXPECTED_VARIABLES = ( + "ROOT_DIR", + "CONFIG_DIR", + "CACHE_DIR", + "SOCK_DIR", + "SRV_ROOT_DIR", + "BASE_FILE_ROOTS_DIR", + "HOME_DIR", + "BASE_PILLAR_ROOTS_DIR", + "BASE_THORIUM_ROOTS_DIR", + "BASE_MASTER_ROOTS_DIR", + "LOGS_DIR", + "PIDFILE_DIR", + "SPM_PARENT_PATH", + "SPM_FORMULA_PATH", + "SPM_PILLAR_PATH", + "SPM_REACTOR_PATH", + "SHARE_DIR", +) try: # Let's try loading the system paths from the generated module at @@ -36,7 +50,10 @@ try: import salt._syspaths as __generated_syspaths # pylint: disable=no-name-in-module except ImportError: import types - __generated_syspaths = types.ModuleType(str('salt._syspaths')) # future lint: blacklisted-function + + __generated_syspaths = types.ModuleType( + str("salt._syspaths") + ) # future lint: blacklisted-function for key in EXPECTED_VARIABLES: setattr(__generated_syspaths, key, None) else: @@ -45,134 +62,140 @@ else: continue else: if typo_warning: - log.warning('Possible Typo?') - log.warning('To dissolve this warning add `[variable] = None` to _syspaths.py') + log.warning("Possible Typo?") + log.warning( + "To dissolve this warning add `[variable] = None` to _syspaths.py" + ) typo_warning = False - log.warning('Variable %s is missing, value set to None', key) - setattr(__generated_syspaths, key, None) # missing variables defaulted to None + log.warning("Variable %s is missing, value set to None", key) + setattr( + __generated_syspaths, key, None + ) # missing variables defaulted to None # Let's find out the path of this module -if 'SETUP_DIRNAME' in globals(): +if "SETUP_DIRNAME" in globals(): # This is from the exec() call in Salt's setup.py - __THIS_FILE = os.path.join(SETUP_DIRNAME, 'salt', 'syspaths.py') # pylint: disable=E0602 + # pylint: disable=undefined-variable + __THIS_FILE = os.path.join(SETUP_DIRNAME, "salt", "syspaths.py") + # pylint: enable=undefined-variable else: __THIS_FILE = __file__ # These values are always relative to salt's installation directory INSTALL_DIR = os.path.dirname(os.path.realpath(__THIS_FILE)) -CLOUD_DIR = os.path.join(INSTALL_DIR, 'cloud') -BOOTSTRAP = os.path.join(CLOUD_DIR, 'deploy', 'bootstrap-salt.sh') +CLOUD_DIR = os.path.join(INSTALL_DIR, "cloud") +BOOTSTRAP = os.path.join(CLOUD_DIR, "deploy", "bootstrap-salt.sh") ROOT_DIR = __generated_syspaths.ROOT_DIR if ROOT_DIR is None: # The installation time value was not provided, let's define the default - if __PLATFORM.startswith('win'): - ROOT_DIR = r'c:\salt' + if __PLATFORM.startswith("win"): + ROOT_DIR = r"c:\salt" else: - ROOT_DIR = '/' + ROOT_DIR = "/" CONFIG_DIR = __generated_syspaths.CONFIG_DIR if CONFIG_DIR is None: - if __PLATFORM.startswith('win'): - CONFIG_DIR = os.path.join(ROOT_DIR, 'conf') - elif 'freebsd' in __PLATFORM: - CONFIG_DIR = os.path.join(ROOT_DIR, 'usr', 'local', 'etc', 'salt') - elif 'netbsd' in __PLATFORM: - CONFIG_DIR = os.path.join(ROOT_DIR, 'usr', 'pkg', 'etc', 'salt') - elif 'sunos5' in __PLATFORM: - CONFIG_DIR = os.path.join(ROOT_DIR, 'opt', 'local', 'etc', 'salt') + if __PLATFORM.startswith("win"): + CONFIG_DIR = os.path.join(ROOT_DIR, "conf") + elif "freebsd" in __PLATFORM: + CONFIG_DIR = os.path.join(ROOT_DIR, "usr", "local", "etc", "salt") + elif "netbsd" in __PLATFORM: + CONFIG_DIR = os.path.join(ROOT_DIR, "usr", "pkg", "etc", "salt") + elif "sunos5" in __PLATFORM: + CONFIG_DIR = os.path.join(ROOT_DIR, "opt", "local", "etc", "salt") else: - CONFIG_DIR = os.path.join(ROOT_DIR, 'etc', 'salt') + CONFIG_DIR = os.path.join(ROOT_DIR, "etc", "salt") SHARE_DIR = __generated_syspaths.SHARE_DIR if SHARE_DIR is None: - if __PLATFORM.startswith('win'): - SHARE_DIR = os.path.join(ROOT_DIR, 'share') - elif 'freebsd' in __PLATFORM: - SHARE_DIR = os.path.join(ROOT_DIR, 'usr', 'local', 'share', 'salt') - elif 'netbsd' in __PLATFORM: - SHARE_DIR = os.path.join(ROOT_DIR, 'usr', 'share', 'salt') - elif 'sunos5' in __PLATFORM: - SHARE_DIR = os.path.join(ROOT_DIR, 'usr', 'share', 'salt') + if __PLATFORM.startswith("win"): + SHARE_DIR = os.path.join(ROOT_DIR, "share") + elif "freebsd" in __PLATFORM: + SHARE_DIR = os.path.join(ROOT_DIR, "usr", "local", "share", "salt") + elif "netbsd" in __PLATFORM: + SHARE_DIR = os.path.join(ROOT_DIR, "usr", "share", "salt") + elif "sunos5" in __PLATFORM: + SHARE_DIR = os.path.join(ROOT_DIR, "usr", "share", "salt") else: - SHARE_DIR = os.path.join(ROOT_DIR, 'usr', 'share', 'salt') + SHARE_DIR = os.path.join(ROOT_DIR, "usr", "share", "salt") CACHE_DIR = __generated_syspaths.CACHE_DIR if CACHE_DIR is None: - CACHE_DIR = os.path.join(ROOT_DIR, 'var', 'cache', 'salt') + CACHE_DIR = os.path.join(ROOT_DIR, "var", "cache", "salt") SOCK_DIR = __generated_syspaths.SOCK_DIR if SOCK_DIR is None: - SOCK_DIR = os.path.join(ROOT_DIR, 'var', 'run', 'salt') + SOCK_DIR = os.path.join(ROOT_DIR, "var", "run", "salt") SRV_ROOT_DIR = __generated_syspaths.SRV_ROOT_DIR if SRV_ROOT_DIR is None: - SRV_ROOT_DIR = os.path.join(ROOT_DIR, 'srv') + SRV_ROOT_DIR = os.path.join(ROOT_DIR, "srv") BASE_FILE_ROOTS_DIR = __generated_syspaths.BASE_FILE_ROOTS_DIR if BASE_FILE_ROOTS_DIR is None: - BASE_FILE_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, 'salt') + BASE_FILE_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, "salt") BASE_PILLAR_ROOTS_DIR = __generated_syspaths.BASE_PILLAR_ROOTS_DIR if BASE_PILLAR_ROOTS_DIR is None: - BASE_PILLAR_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, 'pillar') + BASE_PILLAR_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, "pillar") BASE_THORIUM_ROOTS_DIR = __generated_syspaths.BASE_THORIUM_ROOTS_DIR if BASE_THORIUM_ROOTS_DIR is None: - BASE_THORIUM_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, 'thorium') + BASE_THORIUM_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, "thorium") BASE_MASTER_ROOTS_DIR = __generated_syspaths.BASE_MASTER_ROOTS_DIR if BASE_MASTER_ROOTS_DIR is None: - BASE_MASTER_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, 'salt-master') + BASE_MASTER_ROOTS_DIR = os.path.join(SRV_ROOT_DIR, "salt-master") LOGS_DIR = __generated_syspaths.LOGS_DIR if LOGS_DIR is None: - LOGS_DIR = os.path.join(ROOT_DIR, 'var', 'log', 'salt') + LOGS_DIR = os.path.join(ROOT_DIR, "var", "log", "salt") PIDFILE_DIR = __generated_syspaths.PIDFILE_DIR if PIDFILE_DIR is None: - PIDFILE_DIR = os.path.join(ROOT_DIR, 'var', 'run') + PIDFILE_DIR = os.path.join(ROOT_DIR, "var", "run") SPM_PARENT_PATH = __generated_syspaths.SPM_PARENT_PATH if SPM_PARENT_PATH is None: - SPM_PARENT_PATH = os.path.join(SRV_ROOT_DIR, 'spm') + SPM_PARENT_PATH = os.path.join(SRV_ROOT_DIR, "spm") SPM_FORMULA_PATH = __generated_syspaths.SPM_FORMULA_PATH if SPM_FORMULA_PATH is None: - SPM_FORMULA_PATH = os.path.join(SPM_PARENT_PATH, 'salt') + SPM_FORMULA_PATH = os.path.join(SPM_PARENT_PATH, "salt") SPM_PILLAR_PATH = __generated_syspaths.SPM_PILLAR_PATH if SPM_PILLAR_PATH is None: - SPM_PILLAR_PATH = os.path.join(SPM_PARENT_PATH, 'pillar') + SPM_PILLAR_PATH = os.path.join(SPM_PARENT_PATH, "pillar") SPM_REACTOR_PATH = __generated_syspaths.SPM_REACTOR_PATH if SPM_REACTOR_PATH is None: - SPM_REACTOR_PATH = os.path.join(SPM_PARENT_PATH, 'reactor') + SPM_REACTOR_PATH = os.path.join(SPM_PARENT_PATH, "reactor") HOME_DIR = __generated_syspaths.HOME_DIR if HOME_DIR is None: - HOME_DIR = os.path.expanduser('~') + HOME_DIR = os.path.expanduser("~") __all__ = [ - 'ROOT_DIR', - 'SHARE_DIR', - 'CONFIG_DIR', - 'CACHE_DIR', - 'SOCK_DIR', - 'SRV_ROOT_DIR', - 'BASE_FILE_ROOTS_DIR', - 'BASE_PILLAR_ROOTS_DIR', - 'BASE_MASTER_ROOTS_DIR', - 'BASE_THORIUM_ROOTS_DIR', - 'LOGS_DIR', - 'PIDFILE_DIR', - 'INSTALL_DIR', - 'CLOUD_DIR', - 'BOOTSTRAP', - 'SPM_PARENT_PATH', - 'SPM_FORMULA_PATH', - 'SPM_PILLAR_PATH', - 'SPM_REACTOR_PATH' + "ROOT_DIR", + "SHARE_DIR", + "CONFIG_DIR", + "CACHE_DIR", + "SOCK_DIR", + "SRV_ROOT_DIR", + "BASE_FILE_ROOTS_DIR", + "BASE_PILLAR_ROOTS_DIR", + "BASE_MASTER_ROOTS_DIR", + "BASE_THORIUM_ROOTS_DIR", + "LOGS_DIR", + "PIDFILE_DIR", + "INSTALL_DIR", + "CLOUD_DIR", + "BOOTSTRAP", + "SPM_PARENT_PATH", + "SPM_FORMULA_PATH", + "SPM_PILLAR_PATH", + "SPM_REACTOR_PATH", ] diff --git a/salt/template.py b/salt/template.py index 225c4f30e7f..1c95b6908c2 100644 --- a/salt/template.py +++ b/salt/template.py @@ -1,22 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Manage basic template commands -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import Python libs -import time -import os import codecs import logging +import os + +# Import Python libs +import time # Import Salt libs import salt.utils.data import salt.utils.files +import salt.utils.sanitizers import salt.utils.stringio import salt.utils.versions -import salt.utils.sanitizers # Import 3rd-party libs from salt.ext import six @@ -28,20 +29,22 @@ log = logging.getLogger(__name__) # FIXME: we should make the default encoding of a .sls file a configurable # option in the config, and default it to 'utf-8'. # -SLS_ENCODING = 'utf-8' # this one has no BOM. +SLS_ENCODING = "utf-8" # this one has no BOM. SLS_ENCODER = codecs.getencoder(SLS_ENCODING) -def compile_template(template, - renderers, - default, - blacklist, - whitelist, - saltenv='base', - sls='', - input_data='', - **kwargs): - ''' +def compile_template( + template, + renderers, + default, + blacklist, + whitelist, + saltenv="base", + sls="", + input_data="", + **kwargs +): + """ Take the path to a template and return the high data structure derived from the template. @@ -51,29 +54,29 @@ def compile_template(template, Mask value for debugging purposes (prevent sensitive information etc) example: "mask_value="pass*". All "passwd", "password", "pass" will be masked (as text). - ''' + """ # if any error occurs, we return an empty dictionary ret = {} - log.debug('compile template: %s', template) + log.debug("compile template: %s", template) - if 'env' in kwargs: + if "env" in kwargs: # "env" is not supported; Use "saltenv". - kwargs.pop('env') + kwargs.pop("env") - if template != ':string:': + if template != ":string:": # Template was specified incorrectly if not isinstance(template, six.string_types): - log.error('Template was specified incorrectly: %s', template) + log.error("Template was specified incorrectly: %s", template) return ret # Template does not exist if not os.path.isfile(template): - log.error('Template does not exist: %s', template) + log.error("Template does not exist: %s", template) return ret # Template is an empty file if salt.utils.files.is_empty(template): - log.debug('Template is an empty file: %s', template) + log.debug("Template is an empty file: %s", template) return ret with codecs.open(template, encoding=SLS_ENCODING) as ifile: @@ -81,29 +84,31 @@ def compile_template(template, input_data = ifile.read() if not input_data.strip(): # Template is nothing but whitespace - log.error('Template is nothing but whitespace: %s', template) + log.error("Template is nothing but whitespace: %s", template) return ret # Get the list of render funcs in the render pipe line. - render_pipe = template_shebang(template, renderers, default, blacklist, whitelist, input_data) + render_pipe = template_shebang( + template, renderers, default, blacklist, whitelist, input_data + ) - windows_newline = '\r\n' in input_data + windows_newline = "\r\n" in input_data input_data = StringIO(input_data) for render, argline in render_pipe: if salt.utils.stringio.is_readable(input_data): - input_data.seek(0) # pylint: disable=no-member + input_data.seek(0) # pylint: disable=no-member render_kwargs = dict(renderers=renderers, tmplpath=template) render_kwargs.update(kwargs) if argline: - render_kwargs['argline'] = argline + render_kwargs["argline"] = argline start = time.time() ret = render(input_data, saltenv, sls, **render_kwargs) log.profile( - 'Time (in seconds) to render \'%s\' using \'%s\' renderer: %s', + "Time (in seconds) to render '%s' using '%s' renderer: %s", template, - render.__module__.split('.')[-1], - time.time() - start + render.__module__.split(".")[-1], + time.time() - start, ) if ret is None: # The file is empty or is being written elsewhere @@ -115,9 +120,13 @@ def compile_template(template, # yaml, mako, or another engine which renders to a data # structure) we don't want to log this. if salt.utils.stringio.is_readable(ret): - log.debug('Rendered data from file: %s:\n%s', template, - salt.utils.sanitizers.mask_args_value(salt.utils.data.decode(ret.read()), - kwargs.get('mask_value'))) # pylint: disable=no-member + log.debug( + "Rendered data from file: %s:\n%s", + template, + salt.utils.sanitizers.mask_args_value( + salt.utils.data.decode(ret.read()), kwargs.get("mask_value") + ), + ) # pylint: disable=no-member ret.seek(0) # pylint: disable=no-member # Preserve newlines from original template @@ -130,8 +139,8 @@ def compile_template(template, contents = ret if isinstance(contents, six.string_types): - if '\r\n' not in contents: - contents = contents.replace('\n', '\r\n') + if "\r\n" not in contents: + contents = contents.replace("\n", "\r\n") ret = StringIO(contents) if is_stringio else contents else: if is_stringio: @@ -140,18 +149,18 @@ def compile_template(template, def compile_template_str(template, renderers, default, blacklist, whitelist): - ''' + """ Take template as a string and return the high data structure derived from the template. - ''' + """ fn_ = salt.utils.files.mkstemp() - with salt.utils.files.fopen(fn_, 'wb') as ofile: + with salt.utils.files.fopen(fn_, "wb") as ofile: ofile.write(SLS_ENCODER(template)[0]) return compile_template(fn_, renderers, default, blacklist, whitelist) def template_shebang(template, renderers, default, blacklist, whitelist, input_data): - ''' + """ Check the template shebang line and return the list of renderers specified in the pipe. @@ -167,17 +176,17 @@ def template_shebang(template, renderers, default, blacklist, whitelist, input_d #!mako|yaml_odict #!mako|yaml_odict|stateconf - ''' - line = '' + """ + line = "" # Open up the first line of the sls template - if template == ':string:': + if template == ":string:": line = input_data.split()[0] else: - with salt.utils.files.fopen(template, 'r') as ifile: + with salt.utils.files.fopen(template, "r") as ifile: line = salt.utils.stringutils.to_unicode(ifile.readline()) # Check if it starts with a shebang and not a path - if line.startswith('#!') and not line.startswith('#!/'): + if line.startswith("#!") and not line.startswith("#!/"): # pull out the shebang data # If the shebang does not contain recognized/not-blacklisted/whitelisted # renderers, do not fall back to the default renderer @@ -191,43 +200,45 @@ def template_shebang(template, renderers, default, blacklist, whitelist, input_d # OLD_STYLE_RENDERERS = {} -for comb in ('yaml_jinja', - 'yaml_mako', - 'yaml_wempy', - 'json_jinja', - 'json_mako', - 'json_wempy', - 'yamlex_jinja', - 'yamlexyamlex_mako', - 'yamlexyamlex_wempy'): +for comb in ( + "yaml_jinja", + "yaml_mako", + "yaml_wempy", + "json_jinja", + "json_mako", + "json_wempy", + "yamlex_jinja", + "yamlexyamlex_mako", + "yamlexyamlex_wempy", +): - fmt, tmpl = comb.split('_') - OLD_STYLE_RENDERERS[comb] = '{0}|{1}'.format(tmpl, fmt) + fmt, tmpl = comb.split("_") + OLD_STYLE_RENDERERS[comb] = "{0}|{1}".format(tmpl, fmt) def check_render_pipe_str(pipestr, renderers, blacklist, whitelist): - ''' + """ Check that all renderers specified in the pipe string are available. If so, return the list of render functions in the pipe as (render_func, arg_str) tuples; otherwise return []. - ''' + """ if pipestr is None: return [] - parts = [r.strip() for r in pipestr.split('|')] + parts = [r.strip() for r in pipestr.split("|")] # Note: currently, | is not allowed anywhere in the shebang line except # as pipes between renderers. results = [] try: if parts[0] == pipestr and pipestr in OLD_STYLE_RENDERERS: - parts = OLD_STYLE_RENDERERS[pipestr].split('|') + parts = OLD_STYLE_RENDERERS[pipestr].split("|") for part in parts: - name, argline = (part + ' ').split(' ', 1) - if whitelist and name not in whitelist or \ - blacklist and name in blacklist: + name, argline = (part + " ").split(" ", 1) + if whitelist and name not in whitelist or blacklist and name in blacklist: log.warning( 'The renderer "%s" is disallowed by configuration and ' - 'will be skipped.', name + "will be skipped.", + name, ) continue results.append((renderers[name], argline.strip())) diff --git a/salt/templates/__init__.py b/salt/templates/__init__.py index 8c486fdc67e..30c8ee2f0bb 100644 --- a/salt/templates/__init__.py +++ b/salt/templates/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Templates Directory -''' +""" diff --git a/salt/templates/virt/libvirt_domain.jinja b/salt/templates/virt/libvirt_domain.jinja index fdaea168f2b..454c616dceb 100644 --- a/salt/templates/virt/libvirt_domain.jinja +++ b/salt/templates/virt/libvirt_domain.jinja @@ -15,6 +15,12 @@ {% if 'cmdline' in boot %} <cmdline>{{ boot.cmdline }}</cmdline> {% endif %} + {% if 'loader' in boot %} + <loader readonly='yes' type='pflash'>{{ boot.loader }}</loader> + {% endif %} + {% if 'nvram' in boot %} + <nvram template='{{boot.nvram}}'></nvram> + {% endif %} {% endif %} {% for dev in boot_dev %} <boot dev='{{ dev }}' /> diff --git a/salt/thorium/__init__.py b/salt/thorium/__init__.py index b6a7423b6c6..99ae1276468 100644 --- a/salt/thorium/__init__.py +++ b/salt/thorium/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" The thorium system allows for advanced event tracking and reactions -''' +""" # Needed: # Use a top file to load sls files locally # use the existing state system to compile a low state @@ -11,16 +11,17 @@ The thorium system allows for advanced event tracking and reactions # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import time -import logging import traceback # Import Salt libs import salt.cache -import salt.state import salt.loader import salt.payload +import salt.state from salt.exceptions import SaltRenderError # Import 3rd-party libs @@ -30,93 +31,88 @@ log = logging.getLogger(__name__) class ThorState(salt.state.HighState): - ''' + """ Compile the thorium state and manage it in the thorium runtime - ''' + """ + def __init__( - self, - opts, - grains=False, - grain_keys=None, - pillar=False, - pillar_keys=None): + self, opts, grains=False, grain_keys=None, pillar=False, pillar_keys=None + ): self.grains = grains self.grain_keys = grain_keys self.pillar = pillar self.pillar_keys = pillar_keys - opts['file_roots'] = opts['thorium_roots'] - opts['saltenv'] = opts['thoriumenv'] - opts['state_top'] = opts['thorium_top'] - opts['file_client'] = 'local' + opts["file_roots"] = opts["thorium_roots"] + opts["saltenv"] = opts["thoriumenv"] + opts["state_top"] = opts["thorium_top"] + opts["file_client"] = "local" self.opts = opts - if opts.get('minion_data_cache'): + if opts.get("minion_data_cache"): self.cache = salt.cache.factory(opts) - salt.state.HighState.__init__(self, self.opts, loader='thorium') + salt.state.HighState.__init__(self, self.opts, loader="thorium") self.returners = salt.loader.returners(self.opts, {}) - self.reg_ret = self.opts.get('register_returner', None) + self.reg_ret = self.opts.get("register_returner", None) regdata = {} if self.reg_ret is not None: try: - regdata = self.returners['{0}.load_reg'.format(self.reg_ret)]() + regdata = self.returners["{0}.load_reg".format(self.reg_ret)]() except Exception as exc: # pylint: disable=broad-except log.error(exc) - self.state.inject_globals = {'__reg__': regdata} - self.event = salt.utils.event.get_master_event( - self.opts, - self.opts['sock_dir']) + self.state.inject_globals = {"__reg__": regdata} + self.event = salt.utils.event.get_master_event(self.opts, self.opts["sock_dir"]) def gather_cache(self): - ''' + """ Gather the specified data from the minion data cache - ''' - cache = {'grains': {}, 'pillar': {}} + """ + cache = {"grains": {}, "pillar": {}} if self.grains or self.pillar: - if self.opts.get('minion_data_cache'): - minions = self.cache.list('minions') + if self.opts.get("minion_data_cache"): + minions = self.cache.list("minions") if not minions: return cache for minion in minions: - total = self.cache.fetch('minions/{0}'.format(minion), 'data') + total = self.cache.fetch("minions/{0}".format(minion), "data") - if 'pillar' in total: + if "pillar" in total: if self.pillar_keys: for key in self.pillar_keys: - if key in total['pillar']: - cache['pillar'][minion][key] = total['pillar'][key] + if key in total["pillar"]: + cache["pillar"][minion][key] = total["pillar"][key] else: - cache['pillar'][minion] = total['pillar'] + cache["pillar"][minion] = total["pillar"] else: - cache['pillar'][minion] = {} + cache["pillar"][minion] = {} - if 'grains' in total: + if "grains" in total: if self.grain_keys: for key in self.grain_keys: - if key in total['grains']: - cache['grains'][minion][key] = total['grains'][key] + if key in total["grains"]: + cache["grains"][minion][key] = total["grains"][key] else: - cache['grains'][minion] = total['grains'] + cache["grains"][minion] = total["grains"] else: - cache['grains'][minion] = {} + cache["grains"][minion] = {} return cache def start_runtime(self): - ''' + """ Start the system! - ''' + """ while True: try: self.call_runtime() except Exception: # pylint: disable=broad-except - log.error('Exception in Thorium: ', exc_info=True) - time.sleep(self.opts['thorium_interval']) + log.error("Exception in Thorium: ", exc_info=True) + time.sleep(self.opts["thorium_interval"]) def get_chunks(self, exclude=None, whitelist=None): - ''' + """ Compile the top file and return the lowstate for the thorium runtime to iterate over - ''' + """ ret = {} err = [] try: @@ -130,17 +126,17 @@ class ThorState(salt.state.HighState): err += self.verify_tops(top) matches = self.top_matches(top) if not matches: - msg = 'No Top file found!' + msg = "No Top file found!" raise SaltRenderError(msg) matches = self.matches_whitelist(matches, whitelist) high, errors = self.render_highstate(matches) if exclude: if isinstance(exclude, six.string_types): - exclude = exclude.split(',') - if '__exclude__' in high: - high['__exclude__'].extend(exclude) + exclude = exclude.split(",") + if "__exclude__" in high: + high["__exclude__"].extend(exclude) else: - high['__exclude__'] = exclude + high["__exclude__"] = exclude err += errors high, ext_errors = self.state.reconcile_extend(high) err += ext_errors @@ -150,9 +146,9 @@ class ThorState(salt.state.HighState): return self.state.compile_high_data(high) def get_events(self): - ''' + """ iterate over the available events and return a list of events - ''' + """ ret = [] while True: event = self.event.get_event(wait=1, full=True) @@ -161,13 +157,13 @@ class ThorState(salt.state.HighState): ret.append(event) def call_runtime(self): - ''' + """ Execute the runtime - ''' + """ cache = self.gather_cache() chunks = self.get_chunks() - interval = self.opts['thorium_interval'] - recompile = self.opts.get('thorium_recompile', 300) + interval = self.opts["thorium_interval"] + recompile = self.opts.get("thorium_recompile", 300) r_start = time.time() while True: events = self.get_events() @@ -175,7 +171,7 @@ class ThorState(salt.state.HighState): time.sleep(interval) continue start = time.time() - self.state.inject_globals['__events__'] = events + self.state.inject_globals["__events__"] = events self.state.call_chunks(chunks) elapsed = time.time() - start left = interval - elapsed @@ -186,5 +182,5 @@ class ThorState(salt.state.HighState): cache = self.gather_cache() chunks = self.get_chunks() if self.reg_ret is not None: - self.returners['{0}.save_reg'.format(self.reg_ret)](chunks) + self.returners["{0}.save_reg".format(self.reg_ret)](chunks) r_start = time.time() diff --git a/salt/thorium/calc.py b/salt/thorium/calc.py index 5bebe4abac0..f354145c0ef 100644 --- a/salt/thorium/calc.py +++ b/salt/thorium/calc.py @@ -1,32 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" Used to manage the thorium register. The thorium register is where compound values are stored and computed, such as averages etc. .. versionadded:: 2016.11.0 :depends: statistics PyPi module -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals try: import statistics + HAS_STATS = True except ImportError: HAS_STATS = False def __virtual__(): - ''' + """ The statistics module must be pip installed - ''' + """ return HAS_STATS def calc(name, num, oper, minimum=0, maximum=0, ref=None): - ''' + """ Perform a calculation on the ``num`` most recent values. Requires a list. Valid values for ``oper`` are: @@ -48,14 +49,11 @@ def calc(name, num, oper, minimum=0, maximum=0, ref=None): - name: myregentry - num: 5 - oper: mean - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} if name not in __reg__: - ret['comment'] = '{0} not found in register'.format(name) - ret['result'] = False + ret["comment"] = "{0} not found in register".format(name) + ret["result"] = False def opadd(vals): sum = 0 @@ -70,20 +68,20 @@ def calc(name, num, oper, minimum=0, maximum=0, ref=None): return prod ops = { - 'add': opadd, - 'mul': opmul, - 'mean': statistics.mean, - 'median': statistics.median, - 'median_low': statistics.median_low, - 'median_high': statistics.median_high, - 'median_grouped': statistics.median_grouped, - 'mode': statistics.mode, + "add": opadd, + "mul": opmul, + "mean": statistics.mean, + "median": statistics.median, + "median_low": statistics.median_low, + "median_high": statistics.median_high, + "median_grouped": statistics.median_grouped, + "mode": statistics.mode, } count = 0 vals = [] - __reg__[name]['val'].reverse() - for regitem in __reg__[name]['val']: + __reg__[name]["val"].reverse() + for regitem in __reg__[name]["val"]: count += 1 if count > num: break @@ -95,21 +93,21 @@ def calc(name, num, oper, minimum=0, maximum=0, ref=None): answer = ops[oper](vals) if minimum > 0 and answer < minimum: - ret['result'] = False + ret["result"] = False if 0 < maximum < answer: - ret['result'] = False + ret["result"] = False - ret['changes'] = { - 'Number of values': len(vals), - 'Operator': oper, - 'Answer': answer, + ret["changes"] = { + "Number of values": len(vals), + "Operator": oper, + "Answer": answer, } return ret def add(name, num, minimum=0, maximum=0, ref=None): - ''' + """ Adds together the ``num`` most recent values. Requires a list. USAGE: @@ -120,19 +118,14 @@ def add(name, num, minimum=0, maximum=0, ref=None): calc.add: - name: myregentry - num: 5 - ''' + """ return calc( - name=name, - num=num, - oper='add', - minimum=minimum, - maximum=maximum, - ref=ref + name=name, num=num, oper="add", minimum=minimum, maximum=maximum, ref=ref ) def mul(name, num, minimum=0, maximum=0, ref=None): - ''' + """ Multiplies together the ``num`` most recent values. Requires a list. USAGE: @@ -143,19 +136,14 @@ def mul(name, num, minimum=0, maximum=0, ref=None): calc.mul: - name: myregentry - num: 5 - ''' + """ return calc( - name=name, - num=num, - oper='mul', - minimum=minimum, - maximum=maximum, - ref=ref + name=name, num=num, oper="mul", minimum=minimum, maximum=maximum, ref=ref ) def mean(name, num, minimum=0, maximum=0, ref=None): - ''' + """ Calculates the mean of the ``num`` most recent values. Requires a list. USAGE: @@ -166,19 +154,14 @@ def mean(name, num, minimum=0, maximum=0, ref=None): calc.mean: - name: myregentry - num: 5 - ''' + """ return calc( - name=name, - num=num, - oper='mean', - minimum=minimum, - maximum=maximum, - ref=ref + name=name, num=num, oper="mean", minimum=minimum, maximum=maximum, ref=ref ) def median(name, num, minimum=0, maximum=0, ref=None): - ''' + """ Calculates the mean of the ``num`` most recent values. Requires a list. USAGE: @@ -189,19 +172,14 @@ def median(name, num, minimum=0, maximum=0, ref=None): calc.median: - name: myregentry - num: 5 - ''' + """ return calc( - name=name, - num=num, - oper='median', - minimum=minimum, - maximum=maximum, - ref=ref + name=name, num=num, oper="median", minimum=minimum, maximum=maximum, ref=ref ) def median_low(name, num, minimum=0, maximum=0, ref=None): - ''' + """ Calculates the low mean of the ``num`` most recent values. Requires a list. USAGE: @@ -212,19 +190,14 @@ def median_low(name, num, minimum=0, maximum=0, ref=None): calc.median_low: - name: myregentry - num: 5 - ''' + """ return calc( - name=name, - num=num, - oper='median_low', - minimum=minimum, - maximum=maximum, - ref=ref + name=name, num=num, oper="median_low", minimum=minimum, maximum=maximum, ref=ref ) def median_high(name, num, minimum=0, maximum=0, ref=None): - ''' + """ Calculates the high mean of the ``num`` most recent values. Requires a list. USAGE: @@ -235,19 +208,19 @@ def median_high(name, num, minimum=0, maximum=0, ref=None): calc.median_high: - name: myregentry - num: 5 - ''' + """ return calc( name=name, num=num, - oper='median_high', + oper="median_high", minimum=minimum, maximum=maximum, - ref=ref + ref=ref, ) def median_grouped(name, num, minimum=0, maximum=0, ref=None): - ''' + """ Calculates the grouped mean of the ``num`` most recent values. Requires a list. @@ -259,19 +232,19 @@ def median_grouped(name, num, minimum=0, maximum=0, ref=None): calc.median_grouped: - name: myregentry - num: 5 - ''' + """ return calc( name=name, num=num, - oper='median_grouped', + oper="median_grouped", minimum=minimum, maximum=maximum, - ref=ref + ref=ref, ) def mode(name, num, minimum=0, maximum=0, ref=None): - ''' + """ Calculates the mode of the ``num`` most recent values. Requires a list. USAGE: @@ -282,12 +255,7 @@ def mode(name, num, minimum=0, maximum=0, ref=None): calc.mode: - name: myregentry - num: 5 - ''' + """ return calc( - name=name, - num=num, - oper='mode', - minimum=minimum, - maximum=maximum, - ref=ref + name=name, num=num, oper="mode", minimum=minimum, maximum=maximum, ref=ref ) diff --git a/salt/thorium/check.py b/salt/thorium/check.py index 41b47d93970..fada6f68bb8 100644 --- a/salt/thorium/check.py +++ b/salt/thorium/check.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" The check Thorium state is used to create gateways to commands, the checks make it easy to make states that watch registers for changes and then just succeed or fail based on the state of the register, this creates the pattern of having a command execution get gated by a check state via a requisite. -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import salt.utils.stringutils @@ -15,7 +16,7 @@ log = logging.getLogger(__file__) def gt(name, value): - ''' + """ Only succeed if the value in the given register location is greater than the given value @@ -33,22 +34,19 @@ def gt(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if __reg__[name]['val'] > value: - ret['result'] = True + if __reg__[name]["val"] > value: + ret["result"] = True return ret def gte(name, value): - ''' + """ Only succeed if the value in the given register location is greater or equal than the given value @@ -66,22 +64,19 @@ def gte(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if __reg__[name]['val'] >= value: - ret['result'] = True + if __reg__[name]["val"] >= value: + ret["result"] = True return ret def lt(name, value): - ''' + """ Only succeed if the value in the given register location is less than the given value @@ -99,22 +94,19 @@ def lt(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if __reg__[name]['val'] < value: - ret['result'] = True + if __reg__[name]["val"] < value: + ret["result"] = True return ret def lte(name, value): - ''' + """ Only succeed if the value in the given register location is less than or equal the given value @@ -132,22 +124,19 @@ def lte(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if __reg__[name]['val'] <= value: - ret['result'] = True + if __reg__[name]["val"] <= value: + ret["result"] = True return ret def eq(name, value): - ''' + """ Only succeed if the value in the given register location is equal to the given value @@ -165,22 +154,19 @@ def eq(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if __reg__[name]['val'] == value: - ret['result'] = True + if __reg__[name]["val"] == value: + ret["result"] = True return ret def ne(name, value): - ''' + """ Only succeed if the value in the given register location is not equal to the given value @@ -198,29 +184,28 @@ def ne(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if __reg__[name]['val'] != value: - ret['result'] = True + if __reg__[name]["val"] != value: + ret["result"] = True return ret -def contains(name, - value, - count_lt=None, - count_lte=None, - count_eq=None, - count_gte=None, - count_gt=None, - count_ne=None): - ''' +def contains( + name, + value, + count_lt=None, + count_lte=None, + count_eq=None, + count_gte=None, + count_gt=None, + count_ne=None, +): + """ Only succeed if the value in the given register location contains the given value @@ -238,44 +223,42 @@ def contains(name, - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret try: - count_compare = count_lt or count_lte or count_eq or\ - count_gte or count_gt or count_ne + count_compare = ( + count_lt or count_lte or count_eq or count_gte or count_gt or count_ne + ) if count_compare: - occurrences = __reg__[name]['val'].count(value) - log.debug('%s appears %s times', value, occurrences) - ret['result'] = True + occurrences = __reg__[name]["val"].count(value) + log.debug("%s appears %s times", value, occurrences) + ret["result"] = True if count_lt: - ret['result'] &= occurrences < count_lt + ret["result"] &= occurrences < count_lt if count_lte: - ret['result'] &= occurrences <= count_lte + ret["result"] &= occurrences <= count_lte if count_eq: - ret['result'] &= occurrences == count_eq + ret["result"] &= occurrences == count_eq if count_gte: - ret['result'] &= occurrences >= count_gte + ret["result"] &= occurrences >= count_gte if count_gt: - ret['result'] &= occurrences > count_gt + ret["result"] &= occurrences > count_gt if count_ne: - ret['result'] &= occurrences != count_ne + ret["result"] &= occurrences != count_ne else: - if value in __reg__[name]['val']: - ret['result'] = True + if value in __reg__[name]["val"]: + ret["result"] = True except TypeError: pass return ret def event(name): - ''' + """ Chekcs for a specific event match and returns result True if the match happens @@ -292,21 +275,18 @@ def event(name): - func: test.ping - require: - check: salt/foo/*/bar - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': False} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": False} for event in __events__: - if salt.utils.stringutils.expr_match(event['tag'], name): - ret['result'] = True + if salt.utils.stringutils.expr_match(event["tag"], name): + ret["result"] = True return ret def len_gt(name, value): - ''' + """ Only succeed if length of the given register location is greater than the given value. @@ -324,22 +304,19 @@ def len_gt(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if len(__reg__[name]['val']) > value: - ret['result'] = True + if len(__reg__[name]["val"]) > value: + ret["result"] = True return ret def len_gte(name, value): - ''' + """ Only succeed if the length of the given register location is greater or equal than the given value @@ -357,22 +334,19 @@ def len_gte(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if len(__reg__[name]['val']) >= value: - ret['result'] = True + if len(__reg__[name]["val"]) >= value: + ret["result"] = True return ret def len_lt(name, value): - ''' + """ Only succeed if the length of the given register location is less than the given value. @@ -390,22 +364,19 @@ def len_lt(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if len(__reg__[name]['val']) < value: - ret['result'] = True + if len(__reg__[name]["val"]) < value: + ret["result"] = True return ret def len_lte(name, value): - ''' + """ Only succeed if the length of the given register location is less than or equal the given value @@ -423,22 +394,19 @@ def len_lte(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if len(__reg__[name]['val']) <= value: - ret['result'] = True + if len(__reg__[name]["val"]) <= value: + ret["result"] = True return ret def len_eq(name, value): - ''' + """ Only succeed if the length of the given register location is equal to the given value. @@ -456,22 +424,19 @@ def len_eq(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if __reg__[name]['val'] == value: - ret['result'] = True + if __reg__[name]["val"] == value: + ret["result"] = True return ret def len_ne(name, value): - ''' + """ Only succeed if the length of the given register location is not equal to the given value. @@ -489,15 +454,12 @@ def len_ne(name, value): - func: test.ping - require: - check: foo - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} if name not in __reg__: - ret['result'] = False - ret['comment'] = 'Value {0} not in register'.format(name) + ret["result"] = False + ret["comment"] = "Value {0} not in register".format(name) return ret - if len(__reg__[name]['val']) != value: - ret['result'] = True + if len(__reg__[name]["val"]) != value: + ret["result"] = True return ret diff --git a/salt/thorium/file.py b/salt/thorium/file.py index 332852dd0df..850504255a7 100644 --- a/salt/thorium/file.py +++ b/salt/thorium/file.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Writes matches to disk to verify activity, helpful when testing Normally this is used by giving the name of the file (without a path) that the @@ -36,10 +36,11 @@ it JSON compliant: Be warned that if you do this, then the file will be saved, but not in a format that can be re-imported into Python. -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import salt libs @@ -49,7 +50,7 @@ import salt.utils.json def save(name, filter=False): - ''' + """ Save the register to <salt cachedir>/thorium/saves/<name>, or to an absolute path. @@ -65,20 +66,17 @@ def save(name, filter=False): /tmp/foo: file.save - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} - if name.startswith('/'): + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} + if name.startswith("/"): tgt_dir = os.path.dirname(name) fn_ = name else: - tgt_dir = os.path.join(__opts__['cachedir'], 'thorium', 'saves') + tgt_dir = os.path.join(__opts__["cachedir"], "thorium", "saves") fn_ = os.path.join(tgt_dir, name) if not os.path.isdir(tgt_dir): os.makedirs(tgt_dir) - with salt.utils.files.fopen(fn_, 'w+') as fp_: + with salt.utils.files.fopen(fn_, "w+") as fp_: if filter is True: salt.utils.json.dump(salt.utils.data.simple_types_filter(__reg__), fp_) else: diff --git a/salt/thorium/key.py b/salt/thorium/key.py index a4f4800b439..5a5c902b38c 100644 --- a/salt/thorium/key.py +++ b/salt/thorium/key.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" The key Thorium State is used to apply changes to the accepted/rejected/pending keys .. versionadded:: 2016.11.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import time # Import salt libs @@ -13,16 +14,16 @@ import salt.key def _get_key_api(): - ''' + """ Return the key api hook - ''' - if 'keyapi' not in __context__: - __context__['keyapi'] = salt.key.Key(__opts__) - return __context__['keyapi'] + """ + if "keyapi" not in __context__: + __context__["keyapi"] = salt.key.Key(__opts__) + return __context__["keyapi"] def timeout(name, delete=0, reject=0): - ''' + """ If any minion's status is older than the timeout value then apply the given action to the timed out key. This example will remove keys to minions that have not checked in for 300 seconds (5 minutes) @@ -39,25 +40,22 @@ def timeout(name, delete=0, reject=0): - require: - status: statreg - delete: 300 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} now = time.time() - ktr = 'key_start_tracker' + ktr = "key_start_tracker" if ktr not in __context__: __context__[ktr] = {} remove = set() reject_set = set() keyapi = _get_key_api() - current = keyapi.list_status('acc') - for id_ in current.get('minions', []): - if id_ in __reg__['status']['val']: + current = keyapi.list_status("acc") + for id_ in current.get("minions", []): + if id_ in __reg__["status"]["val"]: # minion is reporting, check timeout and mark for removal - if delete and (now - __reg__['status']['val'][id_]['recv_time']) > delete: + if delete and (now - __reg__["status"]["val"][id_]["recv_time"]) > delete: remove.add(id_) - if reject and (now - __reg__['status']['val'][id_]['recv_time']) > reject: + if reject and (now - __reg__["status"]["val"][id_]["recv_time"]) > reject: reject_set.add(id_) else: # No report from minion recorded, mark for change if thorium has @@ -71,10 +69,10 @@ def timeout(name, delete=0, reject=0): reject_set.add(id_) for id_ in remove: keyapi.delete_key(id_) - __reg__['status']['val'].pop(id_, None) + __reg__["status"]["val"].pop(id_, None) __context__[ktr].pop(id_, None) for id_ in reject_set: keyapi.reject(id_) - __reg__['status']['val'].pop(id_, None) + __reg__["status"]["val"].pop(id_, None) __context__[ktr].pop(id_, None) return ret diff --git a/salt/thorium/local.py b/salt/thorium/local.py index e70a26c8356..60d7fa1e146 100644 --- a/salt/thorium/local.py +++ b/salt/thorium/local.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Run remote execution commands via the local client -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -9,15 +9,8 @@ from __future__ import absolute_import, print_function, unicode_literals import salt.client -def cmd(name, - tgt, - func, - arg=(), - tgt_type='glob', - ret='', - kwarg=None, - **kwargs): - ''' +def cmd(name, tgt, func, arg=(), tgt_type="glob", ret="", kwarg=None, **kwargs): + """ Execute a remote execution command USAGE: @@ -42,18 +35,11 @@ def cmd(name, - func: test.sleep - kwarg: length: 30 - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} local = salt.client.get_local_client(mopts=__opts__) - jid = local.cmd_async(tgt, - func, - arg, - tgt_type=tgt_type, - ret=ret, - kwarg=kwarg, - **kwargs) - ret['changes']['jid'] = jid + jid = local.cmd_async( + tgt, func, arg, tgt_type=tgt_type, ret=ret, kwarg=kwarg, **kwargs + ) + ret["changes"]["jid"] = jid return ret diff --git a/salt/thorium/reg.py b/salt/thorium/reg.py index 2d85e77d2ed..59d43e53f42 100644 --- a/salt/thorium/reg.py +++ b/salt/thorium/reg.py @@ -1,21 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Used to manage the thorium register. The thorium register is where compound values are stored and computed, such as averages etc. -''' +""" # import python libs from __future__ import absolute_import, division, print_function, unicode_literals + import salt.utils.stringutils __func_alias__ = { - 'set_': 'set', - 'list_': 'list', + "set_": "set", + "list_": "list", } def set_(name, add, match): - ''' + """ Add a value to the named set USAGE: @@ -26,29 +27,26 @@ def set_(name, add, match): reg.set: - add: bar - match: my/custom/event - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} if name not in __reg__: __reg__[name] = {} - __reg__[name]['val'] = set() + __reg__[name]["val"] = set() for event in __events__: - if salt.utils.stringutils.expr_match(event['tag'], match): + if salt.utils.stringutils.expr_match(event["tag"], match): try: - val = event['data']['data'].get(add) + val = event["data"]["data"].get(add) except KeyError: - val = event['data'].get(add) + val = event["data"].get(add) if val is None: - val = 'None' - ret['changes'][add] = val - __reg__[name]['val'].add(val) + val = "None" + ret["changes"][add] = val + __reg__[name]["val"].add(val) return ret def list_(name, add, match, stamp=False, prune=0): - ''' + """ Add the specified values to the named list If ``stamp`` is True, then the timestamp from the event will also be added @@ -64,36 +62,33 @@ def list_(name, add, match, stamp=False, prune=0): - add: bar - match: my/custom/event - stamp: True - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} if not isinstance(add, list): - add = add.split(',') + add = add.split(",") if name not in __reg__: __reg__[name] = {} - __reg__[name]['val'] = [] + __reg__[name]["val"] = [] for event in __events__: try: - event_data = event['data']['data'] + event_data = event["data"]["data"] except KeyError: - event_data = event['data'] - if salt.utils.stringutils.expr_match(event['tag'], match): + event_data = event["data"] + if salt.utils.stringutils.expr_match(event["tag"], match): item = {} for key in add: if key in event_data: item[key] = event_data[key] if stamp is True: - item['time'] = event['data']['_stamp'] - __reg__[name]['val'].append(item) + item["time"] = event["data"]["_stamp"] + __reg__[name]["val"].append(item) if prune > 0: - __reg__[name]['val'] = __reg__[name]['val'][:prune] + __reg__[name]["val"] = __reg__[name]["val"][:prune] return ret def mean(name, add, match): - ''' + """ Accept a numeric value from the matched events and store a running average of the values in the given register. If the specified value is not numeric it will be skipped @@ -106,35 +101,32 @@ def mean(name, add, match): reg.mean: - add: data_field - match: my/custom/event - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} if name not in __reg__: __reg__[name] = {} - __reg__[name]['val'] = 0 - __reg__[name]['total'] = 0 - __reg__[name]['count'] = 0 + __reg__[name]["val"] = 0 + __reg__[name]["total"] = 0 + __reg__[name]["count"] = 0 for event in __events__: try: - event_data = event['data']['data'] + event_data = event["data"]["data"] except KeyError: - event_data = event['data'] - if salt.utils.stringutils.expr_match(event['tag'], match): + event_data = event["data"] + if salt.utils.stringutils.expr_match(event["tag"], match): if add in event_data: try: comp = int(event_data) except ValueError: continue - __reg__[name]['total'] += comp - __reg__[name]['count'] += 1 - __reg__[name]['val'] = __reg__[name]['total'] / __reg__[name]['count'] + __reg__[name]["total"] += comp + __reg__[name]["count"] += 1 + __reg__[name]["val"] = __reg__[name]["total"] / __reg__[name]["count"] return ret def clear(name): - ''' + """ Clear the namespace from the register USAGE: @@ -144,18 +136,15 @@ def clear(name): clearns: reg.clear: - name: myregister - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} if name in __reg__: __reg__[name].clear() return ret def delete(name): - ''' + """ Delete the namespace from the register USAGE: @@ -165,11 +154,8 @@ def delete(name): deletens: reg.delete: - name: myregister - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} if name in __reg__: del __reg__[name] return ret diff --git a/salt/thorium/runner.py b/salt/thorium/runner.py index 9545eac35c7..47fb94fc0de 100644 --- a/salt/thorium/runner.py +++ b/salt/thorium/runner.py @@ -1,19 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" React by calling asynchronous runners -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + # import salt libs import salt.runner -def cmd( - name, - func=None, - arg=(), - **kwargs): - ''' +def cmd(name, func=None, arg=(), **kwargs): + """ Execute a runner asynchronous: USAGE: @@ -33,21 +30,14 @@ def cmd( - kwargs: provider: my-ec2-config instances: myinstance - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} if func is None: func = name local_opts = {} local_opts.update(__opts__) - local_opts['async'] = True # ensure this will be run asynchronous - local_opts.update({ - 'fun': func, - 'arg': arg, - 'kwarg': kwargs - }) + local_opts["async"] = True # ensure this will be run asynchronous + local_opts.update({"fun": func, "arg": arg, "kwarg": kwargs}) runner = salt.runner.Runner(local_opts) runner.run() return ret diff --git a/salt/thorium/status.py b/salt/thorium/status.py index 326ae18aad0..69ec9391548 100644 --- a/salt/thorium/status.py +++ b/salt/thorium/status.py @@ -1,38 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" This thorium state is used to track the status beacon events and keep track of the active status of minions .. versionadded:: 2016.11.0 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time + import fnmatch +import time def reg(name): - ''' + """ Activate this register to turn on a minion status tracking register, this register keeps the current status beacon data and the time that each beacon was last checked in. - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} now = time.time() - if 'status' not in __reg__: - __reg__['status'] = {} - __reg__['status']['val'] = {} + if "status" not in __reg__: + __reg__["status"] = {} + __reg__["status"]["val"] = {} for event in __events__: - if fnmatch.fnmatch(event['tag'], 'salt/beacon/*/status/*'): + if fnmatch.fnmatch(event["tag"], "salt/beacon/*/status/*"): # Got one! - idata = {'recv_time': now} - for key in event['data']['data']: - if key in ('id', 'recv_time'): + idata = {"recv_time": now} + for key in event["data"]["data"]: + if key in ("id", "recv_time"): continue - idata[key] = event['data']['data'][key] - __reg__['status']['val'][event['data']['id']] = idata - ret['changes'][event['data']['id']] = True + idata[key] = event["data"]["data"][key] + __reg__["status"]["val"][event["data"]["id"]] = idata + ret["changes"][event["data"]["id"]] = True return ret diff --git a/salt/thorium/timer.py b/salt/thorium/timer.py index a8cc5eeeea3..9c27c30826e 100644 --- a/salt/thorium/timer.py +++ b/salt/thorium/timer.py @@ -1,15 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" Allow for flow based timers. These timers allow for a sleep to exist across multiple runs of the flow -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import time def hold(name, seconds): - ''' + """ Wait for a given period of time, then fire a result of True, requiring this state allows for an action to be blocked for evaluation based on time @@ -21,17 +22,14 @@ def hold(name, seconds): hold_on_a_moment: timer.hold: - seconds: 30 - ''' - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + """ + ret = {"name": name, "result": False, "comment": "", "changes": {}} start = time.time() - if 'timer' not in __context__: - __context__['timer'] = {} - if name not in __context__['timer']: - __context__['timer'][name] = start - if (start - __context__['timer'][name]) > seconds: - ret['result'] = True - __context__['timer'][name] = start + if "timer" not in __context__: + __context__["timer"] = {} + if name not in __context__["timer"]: + __context__["timer"][name] = start + if (start - __context__["timer"][name]) > seconds: + ret["result"] = True + __context__["timer"][name] = start return ret diff --git a/salt/thorium/wheel.py b/salt/thorium/wheel.py index e3c4bf17014..c05f16ffd36 100644 --- a/salt/thorium/wheel.py +++ b/salt/thorium/wheel.py @@ -1,19 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" React by calling asynchronous runners -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + # import salt libs import salt.wheel -def cmd( - name, - fun=None, - arg=(), - **kwargs): - ''' +def cmd(name, fun=None, arg=(), **kwargs): + """ Execute a runner asynchronous: USAGE: @@ -24,16 +21,11 @@ def cmd( wheel.cmd: - fun: key.delete - match: minion_id - ''' - ret = {'name': name, - 'changes': {}, - 'comment': '', - 'result': True} + """ + ret = {"name": name, "changes": {}, "comment": "", "result": True} if fun is None: fun = name client = salt.wheel.WheelClient(__opts__) - low = {'fun': fun, - 'arg': arg, - 'kwargs': kwargs} + low = {"fun": fun, "arg": arg, "kwargs": kwargs} client.cmd_async(low) return ret diff --git a/salt/tokens/__init__.py b/salt/tokens/__init__.py index bdd5d4ff196..6894cf9cd88 100644 --- a/salt/tokens/__init__.py +++ b/salt/tokens/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" salt.tokens ~~~~~~~~~~~~ @@ -14,4 +14,4 @@ :list_tokens: list all tokens in storage -''' +""" diff --git a/salt/tokens/localfs.py b/salt/tokens/localfs.py index 3660ee31869..e89a4757d32 100644 --- a/salt/tokens/localfs.py +++ b/salt/tokens/localfs.py @@ -1,28 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" Stores eauth tokens in the filesystem of the master. Location is configured by the master config option 'token_dir' -''' +""" from __future__ import absolute_import, print_function, unicode_literals import hashlib -import os import logging +import os +import salt.payload import salt.utils.files import salt.utils.path -import salt.payload - from salt.ext import six log = logging.getLogger(__name__) -__virtualname__ = 'localfs' +__virtualname__ = "localfs" def mk_token(opts, tdata): - ''' + """ Mint a new token using the config option hash_type and store tdata with 'token' attribute set to the token. This module uses the hash of random 512 bytes as a token. @@ -30,75 +29,73 @@ def mk_token(opts, tdata): :param opts: Salt master config options :param tdata: Token data to be stored with 'token' attirbute of this dict set to the token. :returns: tdata with token if successful. Empty dict if failed. - ''' - hash_type = getattr(hashlib, opts.get('hash_type', 'md5')) + """ + hash_type = getattr(hashlib, opts.get("hash_type", "md5")) tok = six.text_type(hash_type(os.urandom(512)).hexdigest()) - t_path = os.path.join(opts['token_dir'], tok) - temp_t_path = '{}.tmp'.format(t_path) + t_path = os.path.join(opts["token_dir"], tok) + temp_t_path = "{}.tmp".format(t_path) while os.path.isfile(t_path): tok = six.text_type(hash_type(os.urandom(512)).hexdigest()) - t_path = os.path.join(opts['token_dir'], tok) - tdata['token'] = tok + t_path = os.path.join(opts["token_dir"], tok) + tdata["token"] = tok serial = salt.payload.Serial(opts) try: with salt.utils.files.set_umask(0o177): - with salt.utils.files.fopen(temp_t_path, 'w+b') as fp_: + with salt.utils.files.fopen(temp_t_path, "w+b") as fp_: fp_.write(serial.dumps(tdata)) os.rename(temp_t_path, t_path) except (IOError, OSError): - log.warning( - 'Authentication failure: can not write token file "%s".', t_path) + log.warning('Authentication failure: can not write token file "%s".', t_path) return {} return tdata def get_token(opts, tok): - ''' + """ Fetch the token data from the store. :param opts: Salt master config options :param tok: Token value to get :returns: Token data if successful. Empty dict if failed. - ''' - t_path = os.path.join(opts['token_dir'], tok) + """ + t_path = os.path.join(opts["token_dir"], tok) if not os.path.isfile(t_path): return {} serial = salt.payload.Serial(opts) try: - with salt.utils.files.fopen(t_path, 'rb') as fp_: + with salt.utils.files.fopen(t_path, "rb") as fp_: tdata = serial.loads(fp_.read()) return tdata except (IOError, OSError): - log.warning( - 'Authentication failure: can not read token file "%s".', t_path) + log.warning('Authentication failure: can not read token file "%s".', t_path) return {} def rm_token(opts, tok): - ''' + """ Remove token from the store. :param opts: Salt master config options :param tok: Token to remove :returns: Empty dict if successful. None if failed. - ''' - t_path = os.path.join(opts['token_dir'], tok) + """ + t_path = os.path.join(opts["token_dir"], tok) try: os.remove(t_path) return {} except (IOError, OSError): - log.warning('Could not remove token %s', tok) + log.warning("Could not remove token %s", tok) def list_tokens(opts): - ''' + """ List all tokens in the store. :param opts: Salt master config options :returns: List of dicts (tokens) - ''' + """ ret = [] - for (dirpath, dirnames, filenames) in salt.utils.path.os_walk(opts['token_dir']): + for (dirpath, dirnames, filenames) in salt.utils.path.os_walk(opts["token_dir"]): for token in filenames: ret.append(token) return ret diff --git a/salt/tokens/rediscluster.py b/salt/tokens/rediscluster.py index dec80bdcd7f..062d2f80128 100644 --- a/salt/tokens/rediscluster.py +++ b/salt/tokens/rediscluster.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -''' +""" Provide token storage in Redis cluster. To get started simply start a redis cluster and assign all hashslots to the connected nodes. @@ -13,57 +13,60 @@ Default values for these configs are as follow: eauth_redis_port: 6379 :depends: - redis-py-cluster Python package -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import hashlib +import logging +import os + +import salt.payload +from salt.ext import six try: import rediscluster + HAS_REDIS = True except ImportError: HAS_REDIS = False -import os -import logging -import hashlib - -import salt.payload - -from salt.ext import six - log = logging.getLogger(__name__) -__virtualname__ = 'rediscluster' +__virtualname__ = "rediscluster" def __virtual__(): if not HAS_REDIS: - return False, 'Could not use redis for tokens; '\ - 'rediscluster python client is not installed.' + return ( + False, + "Could not use redis for tokens; " + "rediscluster python client is not installed.", + ) return __virtualname__ def _redis_client(opts): - ''' + """ Connect to the redis host and return a StrictRedisCluster client object. If connection fails then return None. - ''' + """ redis_host = opts.get("eauth_redis_host", "localhost") redis_port = opts.get("eauth_redis_port", 6379) try: - return rediscluster.StrictRedisCluster(host=redis_host, port=redis_port, decode_responses=True) + return rediscluster.StrictRedisCluster( + host=redis_host, port=redis_port, decode_responses=True + ) except rediscluster.exceptions.RedisClusterException as err: log.warning( - 'Failed to connect to redis at %s:%s - %s', - redis_host, redis_port, err + "Failed to connect to redis at %s:%s - %s", redis_host, redis_port, err ) return None def mk_token(opts, tdata): - ''' + """ Mint a new token using the config option hash_type and store tdata with 'token' attribute set to the token. This module uses the hash of random 512 bytes as a token. @@ -71,42 +74,40 @@ def mk_token(opts, tdata): :param opts: Salt master config options :param tdata: Token data to be stored with 'token' attirbute of this dict set to the token. :returns: tdata with token if successful. Empty dict if failed. - ''' + """ redis_client = _redis_client(opts) if not redis_client: return {} - hash_type = getattr(hashlib, opts.get('hash_type', 'md5')) + hash_type = getattr(hashlib, opts.get("hash_type", "md5")) tok = six.text_type(hash_type(os.urandom(512)).hexdigest()) try: while redis_client.get(tok) is not None: tok = six.text_type(hash_type(os.urandom(512)).hexdigest()) except Exception as err: # pylint: disable=broad-except log.warning( - 'Authentication failure: cannot get token %s from redis: %s', - tok, err + "Authentication failure: cannot get token %s from redis: %s", tok, err ) return {} - tdata['token'] = tok + tdata["token"] = tok serial = salt.payload.Serial(opts) try: redis_client.set(tok, serial.dumps(tdata)) except Exception as err: # pylint: disable=broad-except log.warning( - 'Authentication failure: cannot save token %s to redis: %s', - tok, err + "Authentication failure: cannot save token %s to redis: %s", tok, err ) return {} return tdata def get_token(opts, tok): - ''' + """ Fetch the token data from the store. :param opts: Salt master config options :param tok: Token value to get :returns: Token data if successful. Empty dict if failed. - ''' + """ redis_client = _redis_client(opts) if not redis_client: return {} @@ -116,20 +117,19 @@ def get_token(opts, tok): return tdata except Exception as err: # pylint: disable=broad-except log.warning( - 'Authentication failure: cannot get token %s from redis: %s', - tok, err + "Authentication failure: cannot get token %s from redis: %s", tok, err ) return {} def rm_token(opts, tok): - ''' + """ Remove token from the store. :param opts: Salt master config options :param tok: Token to remove :returns: Empty dict if successful. None if failed. - ''' + """ redis_client = _redis_client(opts) if not redis_client: return @@ -137,23 +137,23 @@ def rm_token(opts, tok): redis_client.delete(tok) return {} except Exception as err: # pylint: disable=broad-except - log.warning('Could not remove token %s: %s', tok, err) + log.warning("Could not remove token %s: %s", tok, err) def list_tokens(opts): - ''' + """ List all tokens in the store. :param opts: Salt master config options :returns: List of dicts (token_data) - ''' + """ ret = [] redis_client = _redis_client(opts) if not redis_client: return [] serial = salt.payload.Serial(opts) try: - return [k.decode('utf8') for k in redis_client.keys()] + return [k.decode("utf8") for k in redis_client.keys()] except Exception as err: # pylint: disable=broad-except - log.warning('Failed to list keys: %s', err) + log.warning("Failed to list keys: %s", err) return [] diff --git a/salt/tops/__init__.py b/salt/tops/__init__.py index eb2d4a56323..d494d6d29fe 100644 --- a/salt/tops/__init__.py +++ b/salt/tops/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Tops Directory -''' +""" diff --git a/salt/tops/cobbler.py b/salt/tops/cobbler.py index 4e5e2940f6c..23fe4c2fcf5 100644 --- a/salt/tops/cobbler.py +++ b/salt/tops/cobbler.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Cobbler Tops ============ @@ -18,33 +18,34 @@ the Cobbler tops and Cobbler pillar modules. Module Documentation ==================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging -import salt.ext.six.moves.xmlrpc_client # pylint: disable=E0611 +import salt.ext.six.moves.xmlrpc_client # pylint: disable=E0611 # Set up logging log = logging.getLogger(__name__) -__opts__ = {'cobbler.url': 'http://localhost/cobbler_api', - 'cobbler.user': None, - 'cobbler.password': None - } +__opts__ = { + "cobbler.url": "http://localhost/cobbler_api", + "cobbler.user": None, + "cobbler.password": None, +} def top(**kwargs): - ''' + """ Look up top data in Cobbler for a minion. - ''' - url = __opts__['cobbler.url'] - user = __opts__['cobbler.user'] - password = __opts__['cobbler.password'] + """ + url = __opts__["cobbler.url"] + user = __opts__["cobbler.user"] + password = __opts__["cobbler.password"] - minion_id = kwargs['opts']['id'] + minion_id = kwargs["opts"]["id"] log.info("Querying cobbler for information for %r", minion_id) try: @@ -53,9 +54,7 @@ def top(**kwargs): server.login(user, password) data = server.get_blended_data(None, minion_id) except Exception: # pylint: disable=broad-except - log.exception( - 'Could not connect to cobbler.' - ) + log.exception("Could not connect to cobbler.") return {} - return {data['status']: data['mgmt_classes']} + return {data["status"]: data["mgmt_classes"]} diff --git a/salt/tops/ext_nodes.py b/salt/tops/ext_nodes.py index c7dea5f4907..ba6891362c1 100644 --- a/salt/tops/ext_nodes.py +++ b/salt/tops/ext_nodes.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" External Nodes Classifier ========================= @@ -44,7 +44,7 @@ The above essentially is the same as a top.sls containing the following: '*': - basepackages - database -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -58,46 +58,42 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only run if properly configured - ''' - if __opts__['master_tops'].get('ext_nodes'): + """ + if __opts__["master_tops"].get("ext_nodes"): return True return False def top(**kwargs): - ''' + """ Run the command configured - ''' - if 'id' not in kwargs['opts']: + """ + if "id" not in kwargs["opts"]: return {} - cmd = '{0} {1}'.format( - __opts__['master_tops']['ext_nodes'], - kwargs['opts']['id'] - ) + cmd = "{0} {1}".format(__opts__["master_tops"]["ext_nodes"], kwargs["opts"]["id"]) ndata = salt.utils.yaml.safe_load( - subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE).communicate()[0] + subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).communicate()[0] ) if not ndata: - log.info('master_tops ext_nodes call did not return any data') + log.info("master_tops ext_nodes call did not return any data") ret = {} - if 'environment' in ndata: - env = ndata['environment'] + if "environment" in ndata: + env = ndata["environment"] else: - env = 'base' + env = "base" - if 'classes' in ndata: - if isinstance(ndata['classes'], dict): - ret[env] = list(ndata['classes']) - elif isinstance(ndata['classes'], list): - ret[env] = ndata['classes'] + if "classes" in ndata: + if isinstance(ndata["classes"], dict): + ret[env] = list(ndata["classes"]) + elif isinstance(ndata["classes"], list): + ret[env] = ndata["classes"] else: return ret else: - log.info('master_tops ext_nodes call did not have a dictionary with a "classes" key.') + log.info( + 'master_tops ext_nodes call did not have a dictionary with a "classes" key.' + ) return ret diff --git a/salt/tops/mongo.py b/salt/tops/mongo.py index 461a098ae7c..328b4298b06 100644 --- a/salt/tops/mongo.py +++ b/salt/tops/mongo.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Read tops data from a mongodb collection This module will load tops data from a mongo collection. It uses the node's id @@ -40,7 +40,7 @@ Configuring the Mongo Tops Subsystem Module Documentation ==================== -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -50,22 +50,25 @@ import re # Import third party libs try: import pymongo + HAS_PYMONGO = True except ImportError: HAS_PYMONGO = False -__opts__ = {'mongo.db': 'salt', - 'mongo.host': 'salt', - 'mongo.password': '', - 'mongo.port': 27017, - 'mongo.user': ''} +__opts__ = { + "mongo.db": "salt", + "mongo.host": "salt", + "mongo.password": "", + "mongo.port": 27017, + "mongo.user": "", +} def __virtual__(): if not HAS_PYMONGO: return False - return 'mongo' + return "mongo" # Set up logging @@ -73,7 +76,7 @@ log = logging.getLogger(__name__) def top(**kwargs): - ''' + """ Connect to a mongo database and read per-node tops data. Parameters: @@ -93,49 +96,54 @@ def top(**kwargs): * `states_field`: The name of the field providing a list of states. * `environment_field`: The name of the field providing the environment. Defaults to ``environment``. - ''' - host = __opts__['mongo.host'] - port = __opts__['mongo.port'] - collection = __opts__['master_tops']['mongo'].get('collection', 'tops') - id_field = __opts__['master_tops']['mongo'].get('id_field', '_id') - re_pattern = __opts__['master_tops']['mongo'].get('re_pattern', '') - re_replace = __opts__['master_tops']['mongo'].get('re_replace', '') - states_field = __opts__['master_tops']['mongo'].get('states_field', 'states') - environment_field = __opts__['master_tops']['mongo'].get('environment_field', 'environment') + """ + host = __opts__["mongo.host"] + port = __opts__["mongo.port"] + collection = __opts__["master_tops"]["mongo"].get("collection", "tops") + id_field = __opts__["master_tops"]["mongo"].get("id_field", "_id") + re_pattern = __opts__["master_tops"]["mongo"].get("re_pattern", "") + re_replace = __opts__["master_tops"]["mongo"].get("re_replace", "") + states_field = __opts__["master_tops"]["mongo"].get("states_field", "states") + environment_field = __opts__["master_tops"]["mongo"].get( + "environment_field", "environment" + ) - log.info('connecting to %s:%s for mongo ext_tops', host, port) + log.info("connecting to %s:%s for mongo ext_tops", host, port) conn = pymongo.MongoClient(host, port) - log.debug('using database \'%s\'', __opts__['mongo.db']) - mdb = conn[__opts__['mongo.db']] + log.debug("using database '%s'", __opts__["mongo.db"]) + mdb = conn[__opts__["mongo.db"]] - user = __opts__.get('mongo.user') - password = __opts__.get('mongo.password') + user = __opts__.get("mongo.user") + password = __opts__.get("mongo.password") if user and password: - log.debug('authenticating as \'%s\'', user) + log.debug("authenticating as '%s'", user) mdb.authenticate(user, password) # Do the regex string replacement on the minion id - minion_id = kwargs['opts']['id'] + minion_id = kwargs["opts"]["id"] if re_pattern: minion_id = re.sub(re_pattern, re_replace, minion_id) log.info( - 'ext_tops.mongo: looking up tops def for {\'%s\': \'%s\'} in mongo', - id_field, minion_id + "ext_tops.mongo: looking up tops def for {'%s': '%s'} in mongo", + id_field, + minion_id, ) - result = mdb[collection].find_one({id_field: minion_id}, projection=[states_field, environment_field]) + result = mdb[collection].find_one( + {id_field: minion_id}, projection=[states_field, environment_field] + ) if result and states_field in result: if environment_field in result: environment = result[environment_field] else: - environment = 'base' - log.debug('ext_tops.mongo: found document, returning states') + environment = "base" + log.debug("ext_tops.mongo: found document, returning states") return {environment: result[states_field]} else: # If we can't find the minion the database it's not necessarily an # error. - log.debug('ext_tops.mongo: no document found in collection %s', collection) + log.debug("ext_tops.mongo: no document found in collection %s", collection) return {} diff --git a/salt/tops/reclass_adapter.py b/salt/tops/reclass_adapter.py index 1f19d850430..6ef5b91b76e 100644 --- a/salt/tops/reclass_adapter.py +++ b/salt/tops/reclass_adapter.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Read tops data from a reclass database .. |reclass| replace:: **reclass** @@ -45,50 +45,53 @@ note of the differing data types for ``ext_pillar`` and ``master_tops``): If you want to run reclass from source, rather than installing it, you can either let the master know via the ``PYTHONPATH`` environment variable, or by setting the configuration option, like in the example above. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import sys + +from salt.exceptions import SaltInvocationError +from salt.ext import six +from salt.utils.reclass import ( + filter_out_source_path_option, + prepend_reclass_source_path, + set_inventory_base_uri_default, +) + # This file cannot be called reclass.py, because then the module import would # not work. Thanks to the __virtual__ function, however, the plugin still # responds to the name 'reclass'. -import sys -from salt.utils.reclass import ( - prepend_reclass_source_path, - filter_out_source_path_option, - set_inventory_base_uri_default -) - -from salt.exceptions import SaltInvocationError -from salt.ext import six # Define the module's virtual name -__virtualname__ = 'reclass' +__virtualname__ = "reclass" def __virtual__(retry=False): try: import reclass # pylint: disable=unused-import + return __virtualname__ except ImportError: if retry: return False - opts = __opts__.get('master_tops', {}).get('reclass', {}) + opts = __opts__.get("master_tops", {}).get("reclass", {}) prepend_reclass_source_path(opts) return __virtual__(retry=True) def top(**kwargs): - ''' + """ Query |reclass| for the top data (states of the minions). - ''' + """ # If reclass is installed, __virtual__ put it onto the search path, so we # don't need to protect against ImportError: # pylint: disable=3rd-party-module-not-gated,no-name-in-module from reclass.adapters.salt import top as reclass_top from reclass.errors import ReclassException + # pylint: enable=3rd-party-module-not-gated,no-name-in-module try: @@ -96,7 +99,7 @@ def top(**kwargs): # one is expected to extract the arguments to the master_tops plugin # by parsing the configuration file data. I therefore use this adapter # to hide this internality. - reclass_opts = __opts__['master_tops']['reclass'] + reclass_opts = __opts__["master_tops"]["reclass"] # the source path we used above isn't something reclass needs to care # about, so filter it: @@ -109,7 +112,7 @@ def top(**kwargs): # Salt expects the top data to be filtered by minion_id, so we better # let it know which minion it is dealing with. Unfortunately, we must # extract these data (see #6930): - minion_id = kwargs['opts']['id'] + minion_id = kwargs["opts"]["id"] # I purposely do not pass any of __opts__ or __salt__ or __grains__ # to reclass, as I consider those to be Salt-internal and reclass @@ -118,29 +121,30 @@ def top(**kwargs): return reclass_top(minion_id, **reclass_opts) except ImportError as e: - if 'reclass' in six.text_type(e): + if "reclass" in six.text_type(e): raise SaltInvocationError( - 'master_tops.reclass: cannot find reclass module ' - 'in {0}'.format(sys.path) + "master_tops.reclass: cannot find reclass module " + "in {0}".format(sys.path) ) else: raise except TypeError as e: - if 'unexpected keyword argument' in six.text_type(e): + if "unexpected keyword argument" in six.text_type(e): arg = six.text_type(e).split()[-1] raise SaltInvocationError( - 'master_tops.reclass: unexpected option: {0}'.format(arg) + "master_tops.reclass: unexpected option: {0}".format(arg) ) else: raise except KeyError as e: - if 'reclass' in six.text_type(e): - raise SaltInvocationError('master_tops.reclass: no configuration ' - 'found in master config') + if "reclass" in six.text_type(e): + raise SaltInvocationError( + "master_tops.reclass: no configuration " "found in master config" + ) else: raise except ReclassException as e: - raise SaltInvocationError('master_tops.reclass: {0}'.format(six.text_type(e))) + raise SaltInvocationError("master_tops.reclass: {0}".format(six.text_type(e))) diff --git a/salt/tops/saltclass.py b/salt/tops/saltclass.py index 4d480e895b2..1475aed97ce 100644 --- a/salt/tops/saltclass.py +++ b/salt/tops/saltclass.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Saltclass Configuration ======================= @@ -206,10 +206,11 @@ Not using ``^`` as the first entry will simply merge the lists Currently you can't have both a variable and an escaped variable in the same string as the escaped one will not be correctly rendered - '\${xx}' will stay as is instead of being rendered as '${xx}' -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import salt.utils.saltclass as sc @@ -218,18 +219,18 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only run if properly configured - ''' - if __opts__['master_tops'].get('saltclass'): + """ + if __opts__["master_tops"].get("saltclass"): return True return False def top(**kwargs): - ''' + """ Compile tops - ''' + """ # Node definitions path will be retrieved from args (or set to default), # then added to 'salt_data' dict that is passed to the 'get_pillars' # function. The dictionary contains: @@ -243,28 +244,28 @@ def top(**kwargs): # If successful, the function will return a pillar dict for minion_id. # If path has not been set, make a default - _opts = __opts__['master_tops']['saltclass'] - if 'path' not in _opts: - path = '/srv/saltclass' - log.warning('path variable unset, using default: %s', path) + _opts = __opts__["master_tops"]["saltclass"] + if "path" not in _opts: + path = "/srv/saltclass" + log.warning("path variable unset, using default: %s", path) else: - path = _opts['path'] + path = _opts["path"] # Create a dict that will contain our salt objects # to send to get_tops function - if 'id' not in kwargs['opts']: - log.warning('Minion id not found - Returning empty dict') + if "id" not in kwargs["opts"]: + log.warning("Minion id not found - Returning empty dict") return {} else: - minion_id = kwargs['opts']['id'] + minion_id = kwargs["opts"]["id"] salt_data = { - '__opts__': kwargs['opts'], - '__salt__': {}, - '__grains__': kwargs['grains'], - '__pillar__': {}, - 'minion_id': minion_id, - 'path': path + "__opts__": kwargs["opts"], + "__salt__": {}, + "__grains__": kwargs["grains"], + "__pillar__": {}, + "minion_id": minion_id, + "path": path, } return sc.get_tops(minion_id, salt_data) diff --git a/salt/tops/varstack_top.py b/salt/tops/varstack_top.py index 4a8d409df64..df7039e32c9 100644 --- a/salt/tops/varstack_top.py +++ b/salt/tops/varstack_top.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Use `Varstack <https://github.com/conversis/varstack>`_ to provide tops data .. |varstack| replace:: **varstack** @@ -41,7 +41,7 @@ Ie, if my.fqdn.yaml file contains: these will be returned as {'base': ['sudo', 'openssh', 'apache', 'salt.minion']} and managed by salt as if given from a top.sls file. -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -51,21 +51,21 @@ except ImportError: varstack = None # Define the module's virtual name -__virtualname__ = 'varstack' +__virtualname__ = "varstack" def __virtual__(): - return (False, 'varstack not installed') if varstack is None else __virtualname__ + return (False, "varstack not installed") if varstack is None else __virtualname__ def top(**kwargs): - ''' + """ Query |varstack| for the top data (states of the minions). - ''' + """ - conf = __opts__['master_tops']['varstack'] - __grains__ = kwargs['grains'] + conf = __opts__["master_tops"]["varstack"] + __grains__ = kwargs["grains"] vs_ = varstack.Varstack(config_filename=conf) ret = vs_.evaluate(__grains__) - return {'base': ret['states']} + return {"base": ret["states"]} diff --git a/salt/transport/__init__.py b/salt/transport/__init__.py index 25c1facc952..c81b6e572e5 100644 --- a/salt/transport/__init__.py +++ b/salt/transport/__init__.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Encapsulate the different transports available to Salt. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs @@ -17,20 +18,20 @@ log = logging.getLogger(__name__) def iter_transport_opts(opts): - ''' + """ Yield transport, opts for all master configured transports - ''' + """ transports = set() - for transport, opts_overrides in six.iteritems(opts.get('transport_opts', {})): + for transport, opts_overrides in six.iteritems(opts.get("transport_opts", {})): t_opts = dict(opts) t_opts.update(opts_overrides) - t_opts['transport'] = transport + t_opts["transport"] = transport transports.add(transport) yield transport, t_opts - if opts['transport'] not in transports: - yield opts['transport'], opts + if opts["transport"] not in transports: + yield opts["transport"], opts # for backwards compatibility @@ -38,21 +39,23 @@ class Channel(object): @staticmethod def factory(opts, **kwargs): salt.utils.versions.warn_until( - 'Sodium', - 'Stop using salt.transport.Channel and instead use salt.transport.client.ReqChannel', - stacklevel=3 + "Sodium", + "Stop using salt.transport.Channel and instead use salt.transport.client.ReqChannel", + stacklevel=3, ) from salt.transport.client import ReqChannel + return ReqChannel.factory(opts, **kwargs) class MessageClientPool(object): def __init__(self, tgt, opts, args=None, kwargs=None): - sock_pool_size = opts['sock_pool_size'] if 'sock_pool_size' in opts else 1 + sock_pool_size = opts["sock_pool_size"] if "sock_pool_size" in opts else 1 if sock_pool_size < 1: log.warning( - 'sock_pool_size is not correctly set, the option should be ' - 'greater than 0 but is instead %s', sock_pool_size + "sock_pool_size is not correctly set, the option should be " + "greater than 0 but is instead %s", + sock_pool_size, ) sock_pool_size = 1 diff --git a/salt/transport/client.py b/salt/transport/client.py index f96b7f74f33..26f40fd3e66 100644 --- a/salt/transport/client.py +++ b/salt/transport/client.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Encapsulate the different transports available to Salt. This includes client side transport, for the ReqServer and the Publisher -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs @@ -16,9 +17,10 @@ log = logging.getLogger(__name__) class ReqChannel(object): - ''' + """ Factory class to create a Sync communication channels to the ReqServer - ''' + """ + @staticmethod def factory(opts, **kwargs): # All Sync interfaces are just wrappers around the Async ones @@ -26,22 +28,24 @@ class ReqChannel(object): return sync def close(self): - ''' + """ Close the channel - ''' + """ raise NotImplementedError() def send(self, load, tries=3, timeout=60, raw=False): - ''' + """ Send "load" to the master. - ''' + """ raise NotImplementedError() - def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout=60): - ''' + def crypted_transfer_decode_dictentry( + self, load, dictkey=None, tries=3, timeout=60 + ): + """ Send "load" to the master in a way that the load is only readable by the minion and the master (not other minions etc.) - ''' + """ raise NotImplementedError() def __enter__(self): @@ -52,25 +56,27 @@ class ReqChannel(object): class PushChannel(object): - ''' + """ Factory class to create Sync channel for push side of push/pull IPC - ''' + """ + @staticmethod def factory(opts, **kwargs): sync = SyncWrapper(AsyncPushChannel.factory, (opts,), kwargs) return sync def send(self, load, tries=3, timeout=60): - ''' + """ Send load across IPC push - ''' + """ raise NotImplementedError() class PullChannel(object): - ''' + """ Factory class to create Sync channel for pull side of push/pull IPC - ''' + """ + @staticmethod def factory(opts, **kwargs): sync = SyncWrapper(AsyncPullChannel.factory, (opts,), kwargs) @@ -79,9 +85,10 @@ class PullChannel(object): # TODO: better doc strings class AsyncChannel(object): - ''' + """ Parent class for Async communication channels - ''' + """ + # Resolver is used by Tornado TCPClient. # This static field is shared between # AsyncReqChannel and AsyncPubChannel. @@ -92,67 +99,69 @@ class AsyncChannel(object): @classmethod def _config_resolver(cls, num_threads=10): import salt.ext.tornado.netutil + salt.ext.tornado.netutil.Resolver.configure( - 'salt.ext.tornado.netutil.ThreadedResolver', - num_threads=num_threads) + "salt.ext.tornado.netutil.ThreadedResolver", num_threads=num_threads + ) cls._resolver_configured = True # TODO: better doc strings class AsyncReqChannel(AsyncChannel): - ''' + """ Factory class to create a Async communication channels to the ReqServer - ''' + """ + @classmethod def factory(cls, opts, **kwargs): # Default to ZeroMQ for now - ttype = 'zeromq' + ttype = "zeromq" # determine the ttype - if 'transport' in opts: - ttype = opts['transport'] - elif 'transport' in opts.get('pillar', {}).get('master', {}): - ttype = opts['pillar']['master']['transport'] + if "transport" in opts: + ttype = opts["transport"] + elif "transport" in opts.get("pillar", {}).get("master", {}): + ttype = opts["pillar"]["master"]["transport"] # switch on available ttypes - if ttype == 'zeromq': + if ttype == "zeromq": import salt.transport.zeromq + return salt.transport.zeromq.AsyncZeroMQReqChannel(opts, **kwargs) - elif ttype == 'tcp': + elif ttype == "tcp": if not cls._resolver_configured: # TODO: add opt to specify number of resolver threads AsyncChannel._config_resolver() import salt.transport.tcp + return salt.transport.tcp.AsyncTCPReqChannel(opts, **kwargs) - elif ttype == 'local': - raise Exception( - 'There\'s no AsyncLocalChannel implementation yet' - ) + elif ttype == "local": + raise Exception("There's no AsyncLocalChannel implementation yet") # import salt.transport.local # return salt.transport.local.AsyncLocalChannel(opts, **kwargs) else: - raise Exception( - 'Channels are only defined for tcp, zeromq, and local' - ) + raise Exception("Channels are only defined for tcp, zeromq, and local") # return NewKindOfChannel(opts, **kwargs) def send(self, load, tries=3, timeout=60, raw=False): - ''' + """ Send "load" to the master. - ''' + """ raise NotImplementedError() - def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout=60): - ''' + def crypted_transfer_decode_dictentry( + self, load, dictkey=None, tries=3, timeout=60 + ): + """ Send "load" to the master in a way that the load is only readable by the minion and the master (not other minions etc.) - ''' + """ raise NotImplementedError() def close(self): - ''' + """ Close the channel - ''' + """ raise NotImplementedError() def __enter__(self): @@ -163,61 +172,60 @@ class AsyncReqChannel(AsyncChannel): class AsyncPubChannel(AsyncChannel): - ''' + """ Factory class to create subscription channels to the master's Publisher - ''' + """ + @classmethod def factory(cls, opts, **kwargs): # Default to ZeroMQ for now - ttype = 'zeromq' + ttype = "zeromq" # determine the ttype - if 'transport' in opts: - ttype = opts['transport'] - elif 'transport' in opts.get('pillar', {}).get('master', {}): - ttype = opts['pillar']['master']['transport'] + if "transport" in opts: + ttype = opts["transport"] + elif "transport" in opts.get("pillar", {}).get("master", {}): + ttype = opts["pillar"]["master"]["transport"] # switch on available ttypes - if ttype == 'detect': - opts['detect_mode'] = True - log.info('Transport is set to detect; using %s', ttype) - if ttype == 'zeromq': + if ttype == "detect": + opts["detect_mode"] = True + log.info("Transport is set to detect; using %s", ttype) + if ttype == "zeromq": import salt.transport.zeromq + return salt.transport.zeromq.AsyncZeroMQPubChannel(opts, **kwargs) - elif ttype == 'tcp': + elif ttype == "tcp": if not cls._resolver_configured: # TODO: add opt to specify number of resolver threads AsyncChannel._config_resolver() import salt.transport.tcp + return salt.transport.tcp.AsyncTCPPubChannel(opts, **kwargs) - elif ttype == 'local': # TODO: - raise Exception( - 'There\'s no AsyncLocalPubChannel implementation yet' - ) + elif ttype == "local": # TODO: + raise Exception("There's no AsyncLocalPubChannel implementation yet") # import salt.transport.local # return salt.transport.local.AsyncLocalPubChannel(opts, **kwargs) else: - raise Exception( - 'Channels are only defined for tcp, zeromq, and local' - ) + raise Exception("Channels are only defined for tcp, zeromq, and local") # return NewKindOfChannel(opts, **kwargs) def connect(self): - ''' + """ Return a future which completes when connected to the remote publisher - ''' + """ raise NotImplementedError() def close(self): - ''' + """ Close the channel - ''' + """ raise NotImplementedError() def on_recv(self, callback): - ''' + """ When jobs are received pass them (decoded) to callback - ''' + """ raise NotImplementedError() def __enter__(self): @@ -228,32 +236,37 @@ class AsyncPubChannel(AsyncChannel): class AsyncPushChannel(object): - ''' + """ Factory class to create IPC Push channels - ''' + """ + @staticmethod def factory(opts, **kwargs): - ''' + """ If we have additional IPC transports other than UxD and TCP, add them here - ''' + """ # FIXME for now, just UXD # Obviously, this makes the factory approach pointless, but we'll extend later import salt.transport.ipc + return salt.transport.ipc.IPCMessageClient(opts, **kwargs) class AsyncPullChannel(object): - ''' + """ Factory class to create IPC pull channels - ''' + """ + @staticmethod def factory(opts, **kwargs): - ''' + """ If we have additional IPC transports other than UXD and TCP, add them here - ''' + """ import salt.transport.ipc + return salt.transport.ipc.IPCMessageServer(opts, **kwargs) + ## Additional IPC messaging patterns should provide interfaces here, ala router/dealer, pub/sub, etc # EOF diff --git a/salt/transport/frame.py b/salt/transport/frame.py index 68bc26cc577..17832c0829e 100644 --- a/salt/transport/frame.py +++ b/salt/transport/frame.py @@ -1,39 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" Helper functions for transport components to handle message framing -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import salt.utils.msgpack from salt.ext import six def frame_msg(body, header=None, raw_body=False): # pylint: disable=unused-argument - ''' + """ Frame the given message with our wire protocol - ''' + """ framed_msg = {} if header is None: header = {} - framed_msg['head'] = header - framed_msg['body'] = body + framed_msg["head"] = header + framed_msg["body"] = body return salt.utils.msgpack.dumps(framed_msg) def frame_msg_ipc(body, header=None, raw_body=False): # pylint: disable=unused-argument - ''' + """ Frame the given message with our wire protocol for IPC For IPC, we don't need to be backwards compatible, so use the more efficient "use_bin_type=True" on Python 3. - ''' + """ framed_msg = {} if header is None: header = {} - framed_msg['head'] = header - framed_msg['body'] = body + framed_msg["head"] = header + framed_msg["body"] = body if six.PY2: return salt.utils.msgpack.dumps(framed_msg) else: @@ -41,10 +42,10 @@ def frame_msg_ipc(body, header=None, raw_body=False): # pylint: disable=unused- def _decode_embedded_list(src): - ''' + """ Convert enbedded bytes to strings if possible. List helper. - ''' + """ output = [] for elem in src: if isinstance(elem, dict): @@ -61,10 +62,10 @@ def _decode_embedded_list(src): def _decode_embedded_dict(src): - ''' + """ Convert enbedded bytes to strings if possible. Dict helper. - ''' + """ output = {} for key, val in six.iteritems(src): if isinstance(val, dict): @@ -86,7 +87,7 @@ def _decode_embedded_dict(src): def decode_embedded_strs(src): - ''' + """ Convert enbedded bytes to strings if possible. This is necessary because Python 3 makes a distinction between these types. @@ -95,7 +96,7 @@ def decode_embedded_strs(src): and "encoding='utf-8'" when decoding. Unfortunately, this would break backwards compatibility due to a change in wire protocol, so this less than ideal solution is used instead. - ''' + """ if not six.PY3: return src diff --git a/salt/transport/ipc.py b/salt/transport/ipc.py index 89fb31fb4fe..215ebf5b8b2 100644 --- a/salt/transport/ipc.py +++ b/salt/transport/ipc.py @@ -1,29 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" IPC transport classes -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import sys + import errno import logging import socket +import sys import time # Import Tornado libs import salt.ext.tornado +import salt.ext.tornado.concurrent import salt.ext.tornado.gen import salt.ext.tornado.netutil -import salt.ext.tornado.concurrent -from salt.ext.tornado.locks import Lock -from salt.ext.tornado.ioloop import IOLoop, TimeoutError as TornadoTimeoutError -from salt.ext.tornado.iostream import IOStream, StreamClosedError -# Import Salt libs -import salt.utils.msgpack import salt.transport.client import salt.transport.frame + +# Import Salt libs +import salt.utils.msgpack from salt.ext import six +from salt.ext.tornado.ioloop import IOLoop +from salt.ext.tornado.ioloop import TimeoutError as TornadoTimeoutError +from salt.ext.tornado.iostream import IOStream, StreamClosedError +from salt.ext.tornado.locks import Lock log = logging.getLogger(__name__) @@ -46,11 +49,12 @@ class FutureWithTimeout(salt.ext.tornado.concurrent.Future): if timeout < 0.1: timeout = 0.1 self._timeout_handle = self.io_loop.add_timeout( - self.io_loop.time() + timeout, self._timeout_callback) + self.io_loop.time() + timeout, self._timeout_callback + ) else: self._timeout_handle = None - if hasattr(self._future, '_future_with_timeout'): + if hasattr(self._future, "_future_with_timeout"): # Reusing a future that has previously been used. # Due to this, no need to call add_done_callback() # because we did that before. @@ -82,12 +86,13 @@ class FutureWithTimeout(salt.ext.tornado.concurrent.Future): class IPCServer(object): - ''' + """ A Tornado IPC server very similar to Tornado's TCPServer class but using either UNIX domain sockets or TCP sockets - ''' + """ + def __init__(self, socket_path, io_loop=None, payload_handler=None): - ''' + """ Create a new Tornado IPC server :param str/int socket_path: Path on the filesystem for the @@ -101,7 +106,7 @@ class IPCServer(object): :param IOLoop io_loop: A Tornado ioloop to handle scheduling :param func payload_handler: A function to customize handling of incoming data. - ''' + """ self.socket_path = socket_path self._started = False self.payload_handler = payload_handler @@ -112,18 +117,18 @@ class IPCServer(object): self._closing = False def start(self): - ''' + """ Perform the work necessary to start up a Tornado IPC server Blocks until socket is established - ''' + """ # Start up the ioloop - log.trace('IPCServer: binding to socket: %s', self.socket_path) + log.trace("IPCServer: binding to socket: %s", self.socket_path) if isinstance(self.socket_path, int): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.setblocking(0) - self.sock.bind(('127.0.0.1', self.socket_path)) + self.sock.bind(("127.0.0.1", self.socket_path)) # Based on default used in tornado.netutil.bind_sockets() self.sock.listen(128) else: @@ -131,93 +136,96 @@ class IPCServer(object): with salt.utils.asynchronous.current_ioloop(self.io_loop): salt.ext.tornado.netutil.add_accept_handler( - self.sock, - self.handle_connection, + self.sock, self.handle_connection, ) self._started = True @salt.ext.tornado.gen.coroutine def handle_stream(self, stream): - ''' + """ Override this to handle the streams as they arrive :param IOStream stream: An IOStream for processing See https://tornado.readthedocs.io/en/latest/iostream.html#tornado.iostream.IOStream for additional details. - ''' + """ + @salt.ext.tornado.gen.coroutine def _null(msg): raise salt.ext.tornado.gen.Return(None) def write_callback(stream, header): - if header.get('mid'): + if header.get("mid"): + @salt.ext.tornado.gen.coroutine def return_message(msg): pack = salt.transport.frame.frame_msg_ipc( - msg, - header={'mid': header['mid']}, - raw_body=True, + msg, header={"mid": header["mid"]}, raw_body=True, ) yield stream.write(pack) + return return_message else: return _null + # msgpack deprecated `encoding` starting with version 0.5.2 if salt.utils.msgpack.version >= (0, 5, 2): # Under Py2 we still want raw to be set to True - msgpack_kwargs = {'raw': six.PY2} + msgpack_kwargs = {"raw": six.PY2} else: if six.PY2: - msgpack_kwargs = {'encoding': None} + msgpack_kwargs = {"encoding": None} else: - msgpack_kwargs = {'encoding': 'utf-8'} + msgpack_kwargs = {"encoding": "utf-8"} unpacker = salt.utils.msgpack.Unpacker(**msgpack_kwargs) while not stream.closed(): try: wire_bytes = yield stream.read_bytes(4096, partial=True) unpacker.feed(wire_bytes) for framed_msg in unpacker: - body = framed_msg['body'] - self.io_loop.spawn_callback(self.payload_handler, body, write_callback(stream, framed_msg['head'])) + body = framed_msg["body"] + self.io_loop.spawn_callback( + self.payload_handler, + body, + write_callback(stream, framed_msg["head"]), + ) except StreamClosedError: - log.trace('Client disconnected from IPC %s', self.socket_path) + log.trace("Client disconnected from IPC %s", self.socket_path) break except socket.error as exc: # On occasion an exception will occur with # an error code of 0, it's a spurious exception. if exc.errno == 0: - log.trace('Exception occured with error number 0, ' - 'spurious exception: %s', exc) + log.trace( + "Exception occured with error number 0, " + "spurious exception: %s", + exc, + ) else: - log.error('Exception occurred while ' - 'handling stream: %s', exc) + log.error("Exception occurred while " "handling stream: %s", exc) except Exception as exc: # pylint: disable=broad-except - log.error('Exception occurred while ' - 'handling stream: %s', exc) + log.error("Exception occurred while " "handling stream: %s", exc) def handle_connection(self, connection, address): - log.trace('IPCServer: Handling connection ' - 'to address: %s', address) + log.trace("IPCServer: Handling connection " "to address: %s", address) try: with salt.utils.asynchronous.current_ioloop(self.io_loop): - stream = IOStream( - connection, - ) + stream = IOStream(connection,) self.io_loop.spawn_callback(self.handle_stream, stream) except Exception as exc: # pylint: disable=broad-except - log.error('IPC streaming error: %s', exc) + log.error("IPC streaming error: %s", exc) def close(self): - ''' + """ Routines to handle any cleanup before the instance shuts down. Sockets and filehandles should be closed explicitly, to prevent leaks. - ''' + """ if self._closing: return self._closing = True - if hasattr(self.sock, 'close'): + if hasattr(self.sock, "close"): self.sock.close() # pylint: disable=W1701 @@ -228,11 +236,12 @@ class IPCServer(object): # This is raised when Python's GC has collected objects which # would be needed when calling self.close() pass + # pylint: enable=W1701 class IPCClient(object): - ''' + """ A Tornado IPC client very similar to Tornado's TCPClient class but using either UNIX domain sockets or TCP sockets @@ -246,16 +255,17 @@ class IPCClient(object): It may also be of type 'int', in which case it is used as the port for a tcp localhost connection. - ''' + """ + def __init__(self, socket_path, io_loop=None): - ''' + """ Create a new IPC client IPC clients cannot bind to ports, but must connect to existing IPC servers. Clients can then send messages to the server. - ''' + """ self.io_loop = io_loop or salt.ext.tornado.ioloop.IOLoop.current() self.socket_path = socket_path self._closing = False @@ -263,25 +273,27 @@ class IPCClient(object): # msgpack deprecated `encoding` starting with version 0.5.2 if salt.utils.msgpack.version >= (0, 5, 2): # Under Py2 we still want raw to be set to True - msgpack_kwargs = {'raw': six.PY2} + msgpack_kwargs = {"raw": six.PY2} else: if six.PY2: - msgpack_kwargs = {'encoding': None} + msgpack_kwargs = {"encoding": None} else: - msgpack_kwargs = {'encoding': 'utf-8'} + msgpack_kwargs = {"encoding": "utf-8"} self.unpacker = salt.utils.msgpack.Unpacker(**msgpack_kwargs) def connected(self): return self.stream is not None and not self.stream.closed() def connect(self, callback=None, timeout=None): - ''' + """ Connect to the IPC socket - ''' - if hasattr(self, '_connecting_future') and not self._connecting_future.done(): # pylint: disable=E0203 - future = self._connecting_future # pylint: disable=E0203 + """ + # pylint: disable=access-member-before-definition + if hasattr(self, "_connecting_future") and not self._connecting_future.done(): + future = self._connecting_future + # pylint: enable=access-member-before-definition else: - if hasattr(self, '_connecting_future'): + if hasattr(self, "_connecting_future"): # read previous future result to prevent the "unhandled future exception" error self._connecting_future.exception() # pylint: disable=E0203 future = salt.ext.tornado.concurrent.Future() @@ -289,21 +301,23 @@ class IPCClient(object): self._connect(timeout=timeout) if callback is not None: + def handle_future(future): response = future.result() self.io_loop.add_callback(callback, response) + future.add_done_callback(handle_future) return future @salt.ext.tornado.gen.coroutine def _connect(self, timeout=None): - ''' + """ Connect to a running IPCServer - ''' + """ if isinstance(self.socket_path, int): sock_type = socket.AF_INET - sock_addr = ('127.0.0.1', self.socket_path) + sock_addr = ("127.0.0.1", self.socket_path) else: sock_type = socket.AF_UNIX sock_addr = self.socket_path @@ -318,11 +332,9 @@ class IPCClient(object): if self.stream is None: with salt.utils.asynchronous.current_ioloop(self.io_loop): - self.stream = IOStream( - socket.socket(sock_type, socket.SOCK_STREAM) - ) + self.stream = IOStream(socket.socket(sock_type, socket.SOCK_STREAM)) try: - log.trace('IPCClient: Connecting to socket: %s', self.socket_path) + log.trace("IPCClient: Connecting to socket: %s", self.socket_path) yield self.stream.connect(sock_addr) self._connecting_future.set_result(True) break @@ -347,20 +359,21 @@ class IPCClient(object): # This is raised when Python's GC has collected objects which # would be needed when calling self.close() pass + # pylint: enable=W1701 def close(self): - ''' + """ Routines to handle any cleanup before the instance shuts down. Sockets and filehandles should be closed explicitly, to prevent leaks. - ''' + """ if self._closing: return self._closing = True - log.debug('Closing %s instance', self.__class__.__name__) + log.debug("Closing %s instance", self.__class__.__name__) if self.stream is not None and not self.stream.closed(): try: @@ -372,7 +385,7 @@ class IPCClient(object): class IPCMessageClient(IPCClient): - ''' + """ Salt IPC message client Create an IPC client to send messages to an IPC server @@ -400,19 +413,20 @@ class IPCMessageClient(IPCClient): # Send some data ipc_client.send('Hello world') - ''' + """ + # FIXME timeout unimplemented # FIXME tries unimplemented @salt.ext.tornado.gen.coroutine def send(self, msg, timeout=None, tries=None): - ''' + """ Send a message to an IPC socket If the socket is not currently connected, a connection will be established. :param dict msg: The message to be sent :param int timeout: Timeout when sending message (Currently unimplemented) - ''' + """ if not self.connected(): yield self.connect() pack = salt.transport.frame.frame_msg_ipc(msg, raw_body=True) @@ -420,7 +434,7 @@ class IPCMessageClient(IPCClient): class IPCMessageServer(IPCServer): - ''' + """ Salt IPC message server Creates a message server which can create and bind to a socket on a given @@ -450,16 +464,17 @@ class IPCMessageServer(IPCServer): print(payload) See IPCMessageClient() for an example of sending messages to an IPCMessageServer instance - ''' + """ class IPCMessagePublisher(object): - ''' + """ A Tornado IPC Publisher similar to Tornado's TCPServer class but using either UNIX domain sockets or TCP sockets - ''' + """ + def __init__(self, opts, socket_path, io_loop=None): - ''' + """ Create a new Tornado IPC server :param dict opts: Salt options :param str/int socket_path: Path on the filesystem for the @@ -471,7 +486,7 @@ class IPCMessagePublisher(object): which case it is used as the port for a tcp localhost connection. :param IOLoop io_loop: A Tornado ioloop to handle scheduling - ''' + """ self.opts = opts self.socket_path = socket_path self._started = False @@ -483,18 +498,18 @@ class IPCMessagePublisher(object): self.streams = set() def start(self): - ''' + """ Perform the work necessary to start up a Tornado IPC server Blocks until socket is established - ''' + """ # Start up the ioloop - log.trace('IPCMessagePublisher: binding to socket: %s', self.socket_path) + log.trace("IPCMessagePublisher: binding to socket: %s", self.socket_path) if isinstance(self.socket_path, int): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.setblocking(0) - self.sock.bind(('127.0.0.1', self.socket_path)) + self.sock.bind(("127.0.0.1", self.socket_path)) # Based on default used in salt.ext.tornado.netutil.bind_sockets() self.sock.listen(128) else: @@ -502,8 +517,7 @@ class IPCMessagePublisher(object): with salt.utils.asynchronous.current_ioloop(self.io_loop): salt.ext.tornado.netutil.add_accept_handler( - self.sock, - self.handle_connection, + self.sock, self.handle_connection, ) self._started = True @@ -512,18 +526,18 @@ class IPCMessagePublisher(object): try: yield stream.write(pack) except StreamClosedError: - log.trace('Client disconnected from IPC %s', self.socket_path) + log.trace("Client disconnected from IPC %s", self.socket_path) self.streams.discard(stream) except Exception as exc: # pylint: disable=broad-except - log.error('Exception occurred while handling stream: %s', exc) + log.error("Exception occurred while handling stream: %s", exc) if not stream.closed(): stream.close() self.streams.discard(stream) def publish(self, msg): - ''' + """ Send message to all connected sockets - ''' + """ if not self.streams: return @@ -533,17 +547,17 @@ class IPCMessagePublisher(object): self.io_loop.spawn_callback(self._write, stream, pack) def handle_connection(self, connection, address): - log.trace('IPCServer: Handling connection to address: %s', address) + log.trace("IPCServer: Handling connection to address: %s", address) try: kwargs = {} - if self.opts['ipc_write_buffer'] > 0: - kwargs['max_write_buffer_size'] = self.opts['ipc_write_buffer'] - log.trace('Setting IPC connection write buffer: %s', (self.opts['ipc_write_buffer'])) - with salt.utils.asynchronous.current_ioloop(self.io_loop): - stream = IOStream( - connection, - **kwargs + if self.opts["ipc_write_buffer"] > 0: + kwargs["max_write_buffer_size"] = self.opts["ipc_write_buffer"] + log.trace( + "Setting IPC connection write buffer: %s", + (self.opts["ipc_write_buffer"]), ) + with salt.utils.asynchronous.current_ioloop(self.io_loop): + stream = IOStream(connection, **kwargs) self.streams.add(stream) def discard_after_closed(): @@ -551,21 +565,21 @@ class IPCMessagePublisher(object): stream.set_close_callback(discard_after_closed) except Exception as exc: # pylint: disable=broad-except - log.error('IPC streaming error: %s', exc) + log.error("IPC streaming error: %s", exc) def close(self): - ''' + """ Routines to handle any cleanup before the instance shuts down. Sockets and filehandles should be closed explicitly, to prevent leaks. - ''' + """ if self._closing: return self._closing = True for stream in self.streams: stream.close() self.streams.clear() - if hasattr(self.sock, 'close'): + if hasattr(self.sock, "close"): self.sock.close() # pylint: disable=W1701 @@ -576,11 +590,12 @@ class IPCMessagePublisher(object): # This is raised when Python's GC has collected objects which # would be needed when calling self.close() pass + # pylint: enable=W1701 class IPCMessageSubscriber(IPCClient): - ''' + """ Salt IPC message subscriber Create an IPC client to receive messages from IPC publisher @@ -611,10 +626,10 @@ class IPCMessageSubscriber(IPCClient): # Wait for some data package = ipc_subscriber.read_sync() - ''' + """ + def __init__(self, socket_path, io_loop=None): - super(IPCMessageSubscriber, self).__init__( - socket_path, io_loop=io_loop) + super(IPCMessageSubscriber, self).__init__(socket_path, io_loop=io_loop) self._read_stream_future = None self._saved_data = [] self._read_in_progress = Lock() @@ -631,14 +646,16 @@ class IPCMessageSubscriber(IPCClient): try: while True: if self._read_stream_future is None: - self._read_stream_future = self.stream.read_bytes(4096, partial=True) + self._read_stream_future = self.stream.read_bytes( + 4096, partial=True + ) if timeout is None: wire_bytes = yield self._read_stream_future else: - wire_bytes = yield FutureWithTimeout(self.io_loop, - self._read_stream_future, - timeout) + wire_bytes = yield FutureWithTimeout( + self.io_loop, self._read_stream_future, timeout + ) self._read_stream_future = None # Remove the timeout once we get some data or an exception @@ -650,12 +667,12 @@ class IPCMessageSubscriber(IPCClient): first_sync_msg = True for framed_msg in self.unpacker: if callback: - self.io_loop.spawn_callback(callback, framed_msg['body']) + self.io_loop.spawn_callback(callback, framed_msg["body"]) elif first_sync_msg: - ret = framed_msg['body'] + ret = framed_msg["body"] first_sync_msg = False else: - self._saved_data.append(framed_msg['body']) + self._saved_data.append(framed_msg["body"]) if not first_sync_msg: # We read at least one piece of data and we're on sync run break @@ -664,11 +681,11 @@ class IPCMessageSubscriber(IPCClient): # Keep 'self._read_stream_future' alive. ret = None except StreamClosedError as exc: - log.trace('Subscriber disconnected from IPC %s', self.socket_path) + log.trace("Subscriber disconnected from IPC %s", self.socket_path) self._read_stream_future = None exc_to_raise = exc except Exception as exc: # pylint: disable=broad-except - log.error('Exception occurred in Subscriber while handling stream: %s', exc) + log.error("Exception occurred in Subscriber while handling stream: %s", exc) self._read_stream_future = None exc_to_raise = exc @@ -679,7 +696,7 @@ class IPCMessageSubscriber(IPCClient): raise salt.ext.tornado.gen.Return(ret) def read_sync(self, timeout=None): - ''' + """ Read a message from an IPC socket The socket must already be connected. @@ -687,35 +704,38 @@ class IPCMessageSubscriber(IPCClient): :param int timeout: Timeout when receiving message :return: message data if successful. None if timed out. Will raise an exception for all other error conditions. - ''' + """ if self._saved_data: return self._saved_data.pop(0) return self.io_loop.run_sync(lambda: self._read(timeout)) @salt.ext.tornado.gen.coroutine def read_async(self, callback): - ''' + """ Asynchronously read messages and invoke a callback when they are ready. :param callback: A callback with the received data - ''' + """ while not self.connected(): try: yield self.connect(timeout=5) except StreamClosedError: - log.trace('Subscriber closed stream on IPC %s before connect', self.socket_path) + log.trace( + "Subscriber closed stream on IPC %s before connect", + self.socket_path, + ) yield salt.ext.tornado.gen.sleep(1) except Exception as exc: # pylint: disable=broad-except - log.error('Exception occurred while Subscriber connecting: %s', exc) + log.error("Exception occurred while Subscriber connecting: %s", exc) yield salt.ext.tornado.gen.sleep(1) yield self._read(None, callback) def close(self): - ''' + """ Routines to handle any cleanup before the instance shuts down. Sockets and filehandles should be closed explicitly, to prevent leaks. - ''' + """ if self._closing: return super(IPCMessageSubscriber, self).close() @@ -731,4 +751,5 @@ class IPCMessageSubscriber(IPCClient): def __del__(self): if IPCMessageSubscriber in globals(): self.close() + # pylint: enable=W1701 diff --git a/salt/transport/local.py b/salt/transport/local.py index 25e521cde36..d8e546f342a 100644 --- a/salt/transport/local.py +++ b/salt/transport/local.py @@ -2,6 +2,7 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs @@ -12,45 +13,47 @@ log = logging.getLogger(__name__) class LocalChannel(ReqChannel): - ''' + """ Local channel for testing purposes - ''' + """ + def __init__(self, opts, **kwargs): self.opts = opts self.kwargs = kwargs self.tries = 0 def close(self): - ''' + """ Close the local channel. Currently a NOOP - ''' + """ def send(self, load, tries=3, timeout=60, raw=False): if self.tries == 0: - log.debug('LocalChannel load: %s', load) - #data = json.loads(load) - #{'path': 'apt-cacher-ng/map.jinja', 'saltenv': 'base', 'cmd': '_serve_file', 'loc': 0} - #f = open(data['path']) - with salt.utils.files.fopen(load['path']) as f: + log.debug("LocalChannel load: %s", load) + # data = json.loads(load) + # {'path': 'apt-cacher-ng/map.jinja', 'saltenv': 'base', 'cmd': '_serve_file', 'loc': 0} + # f = open(data['path']) + with salt.utils.files.fopen(load["path"]) as f: ret = { - 'data': ''.join(f.readlines()), - 'dest': load['path'], + "data": "".join(f.readlines()), + "dest": load["path"], } - print('returning', ret) + print("returning", ret) else: # end of buffer ret = { - 'data': None, - 'dest': None, + "data": None, + "dest": None, } self.tries = self.tries + 1 return ret - def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout=60): - super(LocalChannel, self).crypted_transfer_decode_dictentry(load, - dictkey=dictkey, - tries=tries, - timeout=timeout) + def crypted_transfer_decode_dictentry( + self, load, dictkey=None, tries=3, timeout=60 + ): + super(LocalChannel, self).crypted_transfer_decode_dictentry( + load, dictkey=dictkey, tries=tries, timeout=timeout + ) diff --git a/salt/transport/mixins/auth.py b/salt/transport/mixins/auth.py index e7a0b96eee3..003cbd82754 100644 --- a/salt/transport/mixins/auth.py +++ b/salt/transport/mixins/auth.py @@ -2,31 +2,34 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import multiprocessing -import ctypes -import logging -import os -import hashlib -import shutil + import binascii +import ctypes +import hashlib +import logging +import multiprocessing +import os +import shutil # Import Salt Libs import salt.crypt -import salt.payload +import salt.ext.tornado.gen import salt.master +import salt.payload import salt.transport.frame import salt.utils.event import salt.utils.files import salt.utils.minions import salt.utils.stringutils import salt.utils.verify -from salt.utils.cache import CacheCli # Import Third Party Libs from salt.ext import six -import salt.ext.tornado.gen +from salt.utils.cache import CacheCli + try: from M2Crypto import RSA + HAS_M2 = True except ImportError: HAS_M2 = False @@ -42,63 +45,75 @@ log = logging.getLogger(__name__) # TODO: rename class AESPubClientMixin(object): def _verify_master_signature(self, payload): - if self.opts.get('sign_pub_messages'): - if not payload.get('sig', False): - raise salt.crypt.AuthenticationError('Message signing is enabled but the payload has no signature.') + if self.opts.get("sign_pub_messages"): + if not payload.get("sig", False): + raise salt.crypt.AuthenticationError( + "Message signing is enabled but the payload has no signature." + ) # Verify that the signature is valid - master_pubkey_path = os.path.join(self.opts['pki_dir'], 'minion_master.pub') - if not salt.crypt.verify_signature(master_pubkey_path, payload['load'], payload.get('sig')): - raise salt.crypt.AuthenticationError('Message signature failed to validate.') + master_pubkey_path = os.path.join(self.opts["pki_dir"], "minion_master.pub") + if not salt.crypt.verify_signature( + master_pubkey_path, payload["load"], payload.get("sig") + ): + raise salt.crypt.AuthenticationError( + "Message signature failed to validate." + ) @salt.ext.tornado.gen.coroutine def _decode_payload(self, payload): # we need to decrypt it - log.trace('Decoding payload: %s', payload) - if payload['enc'] == 'aes': + log.trace("Decoding payload: %s", payload) + if payload["enc"] == "aes": self._verify_master_signature(payload) try: - payload['load'] = self.auth.crypticle.loads(payload['load']) + payload["load"] = self.auth.crypticle.loads(payload["load"]) except salt.crypt.AuthenticationError: yield self.auth.authenticate() - payload['load'] = self.auth.crypticle.loads(payload['load']) + payload["load"] = self.auth.crypticle.loads(payload["load"]) raise salt.ext.tornado.gen.Return(payload) # TODO: rename? class AESReqServerMixin(object): - ''' + """ Mixin to house all of the master-side auth crypto - ''' + """ def pre_fork(self, _): - ''' + """ Pre-fork we need to create the zmq router device - ''' - if 'aes' not in salt.master.SMaster.secrets: + """ + if "aes" not in salt.master.SMaster.secrets: # TODO: This is still needed only for the unit tests # 'tcp_test.py' and 'zeromq_test.py'. Fix that. In normal # cases, 'aes' is already set in the secrets. - salt.master.SMaster.secrets['aes'] = { - 'secret': multiprocessing.Array( + salt.master.SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( ctypes.c_char, - salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()) + salt.utils.stringutils.to_bytes( + salt.crypt.Crypticle.generate_key_string() + ), ), - 'reload': salt.crypt.Crypticle.generate_key_string + "reload": salt.crypt.Crypticle.generate_key_string, } def post_fork(self, _, __): self.serial = salt.payload.Serial(self.opts) - self.crypticle = salt.crypt.Crypticle(self.opts, salt.master.SMaster.secrets['aes']['secret'].value) + self.crypticle = salt.crypt.Crypticle( + self.opts, salt.master.SMaster.secrets["aes"]["secret"].value + ) # other things needed for _auth # Create the event manager - self.event = salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) + self.event = salt.utils.event.get_master_event( + self.opts, self.opts["sock_dir"], listen=False + ) self.auto_key = salt.daemons.masterapi.AutoKey(self.opts) # only create a con_cache-client if the con_cache is active - if self.opts['con_cache']: + if self.opts["con_cache"]: self.cache_cli = CacheCli(self.opts) else: self.cache_cli = False @@ -108,61 +123,60 @@ class AESReqServerMixin(object): self.master_key = salt.crypt.MasterKeys(self.opts) def _encrypt_private(self, ret, dictkey, target): - ''' + """ The server equivalent of ReqChannel.crypted_transfer_decode_dictentry - ''' + """ # encrypt with a specific AES key - pubfn = os.path.join(self.opts['pki_dir'], - 'minions', - target) + pubfn = os.path.join(self.opts["pki_dir"], "minions", target) key = salt.crypt.Crypticle.generate_key_string() - pcrypt = salt.crypt.Crypticle( - self.opts, - key) + pcrypt = salt.crypt.Crypticle(self.opts, key) try: pub = salt.crypt.get_rsa_pub_key(pubfn) except (ValueError, IndexError, TypeError): return self.crypticle.dumps({}) except IOError: - log.error('AES key not found') - return {'error': 'AES key not found'} + log.error("AES key not found") + return {"error": "AES key not found"} pret = {} if not six.PY2: key = salt.utils.stringutils.to_bytes(key) if HAS_M2: - pret['key'] = pub.public_encrypt(key, RSA.pkcs1_oaep_padding) + pret["key"] = pub.public_encrypt(key, RSA.pkcs1_oaep_padding) else: cipher = PKCS1_OAEP.new(pub) - pret['key'] = cipher.encrypt(key) - pret[dictkey] = pcrypt.dumps( - ret if ret is not False else {} - ) + pret["key"] = cipher.encrypt(key) + pret[dictkey] = pcrypt.dumps(ret if ret is not False else {}) return pret def _update_aes(self): - ''' + """ Check to see if a fresh AES key is available and update the components of the worker - ''' - if salt.master.SMaster.secrets['aes']['secret'].value != self.crypticle.key_string: - self.crypticle = salt.crypt.Crypticle(self.opts, salt.master.SMaster.secrets['aes']['secret'].value) + """ + if ( + salt.master.SMaster.secrets["aes"]["secret"].value + != self.crypticle.key_string + ): + self.crypticle = salt.crypt.Crypticle( + self.opts, salt.master.SMaster.secrets["aes"]["secret"].value + ) return True return False def _decode_payload(self, payload): # we need to decrypt it - if payload['enc'] == 'aes': + if payload["enc"] == "aes": try: - payload['load'] = self.crypticle.loads(payload['load']) + payload["load"] = self.crypticle.loads(payload["load"]) except salt.crypt.AuthenticationError: if not self._update_aes(): raise - payload['load'] = self.crypticle.loads(payload['load']) + payload["load"] = self.crypticle.loads(payload["load"]) return payload def _auth(self, load): - ''' + """ Authenticate the client, use the sent public key to encrypt the AES key which was generated at start up. @@ -175,120 +189,123 @@ class AESReqServerMixin(object): # Make an RSA key with the pub key # Encrypt the AES key as an encrypted salt.payload # Package the return and return it - ''' + """ - if not salt.utils.verify.valid_id(self.opts, load['id']): - log.info('Authentication request from invalid id %s', load['id']) - return {'enc': 'clear', - 'load': {'ret': False}} - log.info('Authentication request from %s', load['id']) + if not salt.utils.verify.valid_id(self.opts, load["id"]): + log.info("Authentication request from invalid id %s", load["id"]) + return {"enc": "clear", "load": {"ret": False}} + log.info("Authentication request from %s", load["id"]) # 0 is default which should be 'unlimited' - if self.opts['max_minions'] > 0: + if self.opts["max_minions"] > 0: # use the ConCache if enabled, else use the minion utils if self.cache_cli: minions = self.cache_cli.get_cached() else: minions = self.ckminions.connected_ids() if len(minions) > 1000: - log.info('With large numbers of minions it is advised ' - 'to enable the ConCache with \'con_cache: True\' ' - 'in the masters configuration file.') + log.info( + "With large numbers of minions it is advised " + "to enable the ConCache with 'con_cache: True' " + "in the masters configuration file." + ) - if not len(minions) <= self.opts['max_minions']: + if not len(minions) <= self.opts["max_minions"]: # we reject new minions, minions that are already # connected must be allowed for the mine, highstate, etc. - if load['id'] not in minions: - msg = ('Too many minions connected (max_minions={0}). ' - 'Rejecting connection from id ' - '{1}'.format(self.opts['max_minions'], - load['id'])) + if load["id"] not in minions: + msg = ( + "Too many minions connected (max_minions={0}). " + "Rejecting connection from id " + "{1}".format(self.opts["max_minions"], load["id"]) + ) log.info(msg) - eload = {'result': False, - 'act': 'full', - 'id': load['id'], - 'pub': load['pub']} + eload = { + "result": False, + "act": "full", + "id": load["id"], + "pub": load["pub"], + } - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) - return {'enc': 'clear', - 'load': {'ret': 'full'}} + if self.opts.get("auth_events") is True: + self.event.fire_event( + eload, salt.utils.event.tagify(prefix="auth") + ) + return {"enc": "clear", "load": {"ret": "full"}} # Check if key is configured to be auto-rejected/signed - auto_reject = self.auto_key.check_autoreject(load['id']) - auto_sign = self.auto_key.check_autosign(load['id'], load.get(u'autosign_grains', None)) + auto_reject = self.auto_key.check_autoreject(load["id"]) + auto_sign = self.auto_key.check_autosign( + load["id"], load.get("autosign_grains", None) + ) - pubfn = os.path.join(self.opts['pki_dir'], - 'minions', - load['id']) - pubfn_pend = os.path.join(self.opts['pki_dir'], - 'minions_pre', - load['id']) - pubfn_rejected = os.path.join(self.opts['pki_dir'], - 'minions_rejected', - load['id']) - pubfn_denied = os.path.join(self.opts['pki_dir'], - 'minions_denied', - load['id']) - if self.opts['open_mode']: + pubfn = os.path.join(self.opts["pki_dir"], "minions", load["id"]) + pubfn_pend = os.path.join(self.opts["pki_dir"], "minions_pre", load["id"]) + pubfn_rejected = os.path.join( + self.opts["pki_dir"], "minions_rejected", load["id"] + ) + pubfn_denied = os.path.join(self.opts["pki_dir"], "minions_denied", load["id"]) + if self.opts["open_mode"]: # open mode is turned on, nuts to checks and overwrite whatever # is there pass elif os.path.isfile(pubfn_rejected): # The key has been rejected, don't place it in pending - log.info('Public key rejected for %s. Key is present in ' - 'rejection key dir.', load['id']) - eload = {'result': False, - 'id': load['id'], - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) - return {'enc': 'clear', - 'load': {'ret': False}} + log.info( + "Public key rejected for %s. Key is present in " "rejection key dir.", + load["id"], + ) + eload = {"result": False, "id": load["id"], "pub": load["pub"]} + if self.opts.get("auth_events") is True: + self.event.fire_event(eload, salt.utils.event.tagify(prefix="auth")) + return {"enc": "clear", "load": {"ret": False}} elif os.path.isfile(pubfn): # The key has been accepted, check it - with salt.utils.files.fopen(pubfn, 'r') as pubfn_handle: - if pubfn_handle.read().strip() != load['pub'].strip(): + with salt.utils.files.fopen(pubfn, "r") as pubfn_handle: + if pubfn_handle.read().strip() != load["pub"].strip(): log.error( - 'Authentication attempt from %s failed, the public ' - 'keys did not match. This may be an attempt to compromise ' - 'the Salt cluster.', load['id'] + "Authentication attempt from %s failed, the public " + "keys did not match. This may be an attempt to compromise " + "the Salt cluster.", + load["id"], ) # put denied minion key into minions_denied - with salt.utils.files.fopen(pubfn_denied, 'w+') as fp_: - fp_.write(load['pub']) - eload = {'result': False, - 'id': load['id'], - 'act': 'denied', - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) - return {'enc': 'clear', - 'load': {'ret': False}} + with salt.utils.files.fopen(pubfn_denied, "w+") as fp_: + fp_.write(load["pub"]) + eload = { + "result": False, + "id": load["id"], + "act": "denied", + "pub": load["pub"], + } + if self.opts.get("auth_events") is True: + self.event.fire_event( + eload, salt.utils.event.tagify(prefix="auth") + ) + return {"enc": "clear", "load": {"ret": False}} elif not os.path.isfile(pubfn_pend): # The key has not been accepted, this is a new minion if os.path.isdir(pubfn_pend): # The key path is a directory, error out - log.info('New public key %s is a directory', load['id']) - eload = {'result': False, - 'id': load['id'], - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) - return {'enc': 'clear', - 'load': {'ret': False}} + log.info("New public key %s is a directory", load["id"]) + eload = {"result": False, "id": load["id"], "pub": load["pub"]} + if self.opts.get("auth_events") is True: + self.event.fire_event(eload, salt.utils.event.tagify(prefix="auth")) + return {"enc": "clear", "load": {"ret": False}} if auto_reject: key_path = pubfn_rejected - log.info('New public key for %s rejected via autoreject_file', load['id']) - key_act = 'reject' + log.info( + "New public key for %s rejected via autoreject_file", load["id"] + ) + key_act = "reject" key_result = False elif not auto_sign: key_path = pubfn_pend - log.info('New public key for %s placed in pending', load['id']) - key_act = 'pend' + log.info("New public key for %s placed in pending", load["id"]) + key_act = "pend" key_result = True else: # The key is being automatically accepted, don't do anything @@ -297,16 +314,17 @@ class AESReqServerMixin(object): if key_path is not None: # Write the key to the appropriate location - with salt.utils.files.fopen(key_path, 'w+') as fp_: - fp_.write(load['pub']) - ret = {'enc': 'clear', - 'load': {'ret': key_result}} - eload = {'result': key_result, - 'act': key_act, - 'id': load['id'], - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) + with salt.utils.files.fopen(key_path, "w+") as fp_: + fp_.write(load["pub"]) + ret = {"enc": "clear", "load": {"ret": key_result}} + eload = { + "result": key_result, + "act": key_act, + "id": load["id"], + "pub": load["pub"], + } + if self.opts.get("auth_events") is True: + self.event.fire_event(eload, salt.utils.event.tagify(prefix="auth")) return ret elif os.path.isfile(pubfn_pend): @@ -319,16 +337,19 @@ class AESReqServerMixin(object): shutil.move(pubfn_pend, pubfn_rejected) except (IOError, OSError): pass - log.info('Pending public key for %s rejected via ' - 'autoreject_file', load['id']) - ret = {'enc': 'clear', - 'load': {'ret': False}} - eload = {'result': False, - 'act': 'reject', - 'id': load['id'], - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) + log.info( + "Pending public key for %s rejected via " "autoreject_file", + load["id"], + ) + ret = {"enc": "clear", "load": {"ret": False}} + eload = { + "result": False, + "act": "reject", + "id": load["id"], + "pub": load["pub"], + } + if self.opts.get("auth_events") is True: + self.event.fire_event(eload, salt.utils.event.tagify(prefix="auth")) return ret elif not auto_sign: @@ -336,99 +357,104 @@ class AESReqServerMixin(object): # Check if the keys are the same and error out if this is the # case. Otherwise log the fact that the minion is still # pending. - with salt.utils.files.fopen(pubfn_pend, 'r') as pubfn_handle: - if pubfn_handle.read() != load['pub']: + with salt.utils.files.fopen(pubfn_pend, "r") as pubfn_handle: + if pubfn_handle.read() != load["pub"]: log.error( - 'Authentication attempt from %s failed, the public ' - 'key in pending did not match. This may be an ' - 'attempt to compromise the Salt cluster.', load['id'] + "Authentication attempt from %s failed, the public " + "key in pending did not match. This may be an " + "attempt to compromise the Salt cluster.", + load["id"], ) # put denied minion key into minions_denied - with salt.utils.files.fopen(pubfn_denied, 'w+') as fp_: - fp_.write(load['pub']) - eload = {'result': False, - 'id': load['id'], - 'act': 'denied', - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) - return {'enc': 'clear', - 'load': {'ret': False}} + with salt.utils.files.fopen(pubfn_denied, "w+") as fp_: + fp_.write(load["pub"]) + eload = { + "result": False, + "id": load["id"], + "act": "denied", + "pub": load["pub"], + } + if self.opts.get("auth_events") is True: + self.event.fire_event( + eload, salt.utils.event.tagify(prefix="auth") + ) + return {"enc": "clear", "load": {"ret": False}} else: log.info( - 'Authentication failed from host %s, the key is in ' - 'pending and needs to be accepted with salt-key ' - '-a %s', load['id'], load['id'] + "Authentication failed from host %s, the key is in " + "pending and needs to be accepted with salt-key " + "-a %s", + load["id"], + load["id"], ) - eload = {'result': True, - 'act': 'pend', - 'id': load['id'], - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) - return {'enc': 'clear', - 'load': {'ret': True}} + eload = { + "result": True, + "act": "pend", + "id": load["id"], + "pub": load["pub"], + } + if self.opts.get("auth_events") is True: + self.event.fire_event( + eload, salt.utils.event.tagify(prefix="auth") + ) + return {"enc": "clear", "load": {"ret": True}} else: # This key is in pending and has been configured to be # auto-signed. Check to see if it is the same key, and if # so, pass on doing anything here, and let it get automatically # accepted below. - with salt.utils.files.fopen(pubfn_pend, 'r') as pubfn_handle: - if pubfn_handle.read() != load['pub']: + with salt.utils.files.fopen(pubfn_pend, "r") as pubfn_handle: + if pubfn_handle.read() != load["pub"]: log.error( - 'Authentication attempt from %s failed, the public ' - 'keys in pending did not match. This may be an ' - 'attempt to compromise the Salt cluster.', load['id'] + "Authentication attempt from %s failed, the public " + "keys in pending did not match. This may be an " + "attempt to compromise the Salt cluster.", + load["id"], ) # put denied minion key into minions_denied - with salt.utils.files.fopen(pubfn_denied, 'w+') as fp_: - fp_.write(load['pub']) - eload = {'result': False, - 'id': load['id'], - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) - return {'enc': 'clear', - 'load': {'ret': False}} + with salt.utils.files.fopen(pubfn_denied, "w+") as fp_: + fp_.write(load["pub"]) + eload = {"result": False, "id": load["id"], "pub": load["pub"]} + if self.opts.get("auth_events") is True: + self.event.fire_event( + eload, salt.utils.event.tagify(prefix="auth") + ) + return {"enc": "clear", "load": {"ret": False}} else: os.remove(pubfn_pend) else: # Something happened that I have not accounted for, FAIL! - log.warning('Unaccounted for authentication failure') - eload = {'result': False, - 'id': load['id'], - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) - return {'enc': 'clear', - 'load': {'ret': False}} + log.warning("Unaccounted for authentication failure") + eload = {"result": False, "id": load["id"], "pub": load["pub"]} + if self.opts.get("auth_events") is True: + self.event.fire_event(eload, salt.utils.event.tagify(prefix="auth")) + return {"enc": "clear", "load": {"ret": False}} - log.info('Authentication accepted from %s', load['id']) + log.info("Authentication accepted from %s", load["id"]) # only write to disk if you are adding the file, and in open mode, # which implies we accept any key from a minion. - if not os.path.isfile(pubfn) and not self.opts['open_mode']: - with salt.utils.files.fopen(pubfn, 'w+') as fp_: - fp_.write(load['pub']) - elif self.opts['open_mode']: - disk_key = '' + if not os.path.isfile(pubfn) and not self.opts["open_mode"]: + with salt.utils.files.fopen(pubfn, "w+") as fp_: + fp_.write(load["pub"]) + elif self.opts["open_mode"]: + disk_key = "" if os.path.isfile(pubfn): - with salt.utils.files.fopen(pubfn, 'r') as fp_: + with salt.utils.files.fopen(pubfn, "r") as fp_: disk_key = fp_.read() - if load['pub'] and load['pub'] != disk_key: - log.debug('Host key change detected in open mode.') - with salt.utils.files.fopen(pubfn, 'w+') as fp_: - fp_.write(load['pub']) - elif not load['pub']: - log.error('Public key is empty: {0}'.format(load['id'])) - return {'enc': 'clear', - 'load': {'ret': False}} + if load["pub"] and load["pub"] != disk_key: + log.debug("Host key change detected in open mode.") + with salt.utils.files.fopen(pubfn, "w+") as fp_: + fp_.write(load["pub"]) + elif not load["pub"]: + log.error("Public key is empty: {0}".format(load["id"])) + return {"enc": "clear", "load": {"ret": False}} pub = None # the con_cache is enabled, send the minion id to the cache if self.cache_cli: - self.cache_cli.put_cache([load['id']]) + self.cache_cli.put_cache([load["id"]]) # The key payload may sometimes be corrupt when using auto-accept # and an empty request comes in @@ -436,85 +462,91 @@ class AESReqServerMixin(object): pub = salt.crypt.get_rsa_pub_key(pubfn) except (ValueError, IndexError, TypeError) as err: log.error('Corrupt public key "%s": %s', pubfn, err) - return {'enc': 'clear', - 'load': {'ret': False}} + return {"enc": "clear", "load": {"ret": False}} if not HAS_M2: cipher = PKCS1_OAEP.new(pub) - ret = {'enc': 'pub', - 'pub_key': self.master_key.get_pub_str(), - 'publish_port': self.opts['publish_port']} + ret = { + "enc": "pub", + "pub_key": self.master_key.get_pub_str(), + "publish_port": self.opts["publish_port"], + } # sign the master's pubkey (if enabled) before it is # sent to the minion that was just authenticated - if self.opts['master_sign_pubkey']: + if self.opts["master_sign_pubkey"]: # append the pre-computed signature to the auth-reply if self.master_key.pubkey_signature(): - log.debug('Adding pubkey signature to auth-reply') + log.debug("Adding pubkey signature to auth-reply") log.debug(self.master_key.pubkey_signature()) - ret.update({'pub_sig': self.master_key.pubkey_signature()}) + ret.update({"pub_sig": self.master_key.pubkey_signature()}) else: # the master has its own signing-keypair, compute the master.pub's # signature and append that to the auth-reply # get the key_pass for the signing key - key_pass = salt.utils.sdb.sdb_get(self.opts['signing_key_pass'], self.opts) + key_pass = salt.utils.sdb.sdb_get( + self.opts["signing_key_pass"], self.opts + ) log.debug("Signing master public key before sending") - pub_sign = salt.crypt.sign_message(self.master_key.get_sign_paths()[1], - ret['pub_key'], key_pass) - ret.update({'pub_sig': binascii.b2a_base64(pub_sign)}) + pub_sign = salt.crypt.sign_message( + self.master_key.get_sign_paths()[1], ret["pub_key"], key_pass + ) + ret.update({"pub_sig": binascii.b2a_base64(pub_sign)}) if not HAS_M2: mcipher = PKCS1_OAEP.new(self.master_key.key) - if self.opts['auth_mode'] >= 2: - if 'token' in load: + if self.opts["auth_mode"] >= 2: + if "token" in load: try: if HAS_M2: - mtoken = self.master_key.key.private_decrypt(load['token'], - RSA.pkcs1_oaep_padding) + mtoken = self.master_key.key.private_decrypt( + load["token"], RSA.pkcs1_oaep_padding + ) else: - mtoken = mcipher.decrypt(load['token']) - aes = '{0}_|-{1}'.format(salt.master.SMaster.secrets['aes']['secret'].value, mtoken) + mtoken = mcipher.decrypt(load["token"]) + aes = "{0}_|-{1}".format( + salt.master.SMaster.secrets["aes"]["secret"].value, mtoken + ) except Exception: # pylint: disable=broad-except # Token failed to decrypt, send back the salty bacon to # support older minions pass else: - aes = salt.master.SMaster.secrets['aes']['secret'].value + aes = salt.master.SMaster.secrets["aes"]["secret"].value if HAS_M2: - ret['aes'] = pub.public_encrypt(aes, RSA.pkcs1_oaep_padding) + ret["aes"] = pub.public_encrypt(aes, RSA.pkcs1_oaep_padding) else: - ret['aes'] = cipher.encrypt(aes) + ret["aes"] = cipher.encrypt(aes) else: - if 'token' in load: + if "token" in load: try: if HAS_M2: - mtoken = self.master_key.key.private_decrypt(load['token'], - RSA.pkcs1_oaep_padding) - ret['token'] = pub.public_encrypt(mtoken, RSA.pkcs1_oaep_padding) + mtoken = self.master_key.key.private_decrypt( + load["token"], RSA.pkcs1_oaep_padding + ) + ret["token"] = pub.public_encrypt( + mtoken, RSA.pkcs1_oaep_padding + ) else: - mtoken = mcipher.decrypt(load['token']) - ret['token'] = cipher.encrypt(mtoken) + mtoken = mcipher.decrypt(load["token"]) + ret["token"] = cipher.encrypt(mtoken) except Exception: # pylint: disable=broad-except # Token failed to decrypt, send back the salty bacon to # support older minions pass - aes = salt.master.SMaster.secrets['aes']['secret'].value + aes = salt.master.SMaster.secrets["aes"]["secret"].value if HAS_M2: - ret['aes'] = pub.public_encrypt(aes, - RSA.pkcs1_oaep_padding) + ret["aes"] = pub.public_encrypt(aes, RSA.pkcs1_oaep_padding) else: - ret['aes'] = cipher.encrypt(aes) + ret["aes"] = cipher.encrypt(aes) # Be aggressive about the signature digest = salt.utils.stringutils.to_bytes(hashlib.sha256(aes).hexdigest()) - ret['sig'] = salt.crypt.private_encrypt(self.master_key.key, digest) - eload = {'result': True, - 'act': 'accept', - 'id': load['id'], - 'pub': load['pub']} - if self.opts.get('auth_events') is True: - self.event.fire_event(eload, salt.utils.event.tagify(prefix='auth')) + ret["sig"] = salt.crypt.private_encrypt(self.master_key.key, digest) + eload = {"result": True, "act": "accept", "id": load["id"], "pub": load["pub"]} + if self.opts.get("auth_events") is True: + self.event.fire_event(eload, salt.utils.event.tagify(prefix="auth")) return ret diff --git a/salt/transport/server.py b/salt/transport/server.py index 9de5165483b..8770570975a 100644 --- a/salt/transport/server.py +++ b/salt/transport/server.py @@ -1,100 +1,109 @@ # -*- coding: utf-8 -*- -''' +""" Encapsulate the different transports available to Salt. This includes server side transport, for the ReqServer and the Publisher -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals class ReqServerChannel(object): - ''' + """ Factory class to create a communication channels to the ReqServer - ''' + """ + def __init__(self, opts): self.opts = opts @staticmethod def factory(opts, **kwargs): # Default to ZeroMQ for now - ttype = 'zeromq' + ttype = "zeromq" # determine the ttype - if 'transport' in opts: - ttype = opts['transport'] - elif 'transport' in opts.get('pillar', {}).get('master', {}): - ttype = opts['pillar']['master']['transport'] + if "transport" in opts: + ttype = opts["transport"] + elif "transport" in opts.get("pillar", {}).get("master", {}): + ttype = opts["pillar"]["master"]["transport"] # switch on available ttypes - if ttype == 'zeromq': + if ttype == "zeromq": import salt.transport.zeromq + return salt.transport.zeromq.ZeroMQReqServerChannel(opts) - elif ttype == 'tcp': + elif ttype == "tcp": import salt.transport.tcp + return salt.transport.tcp.TCPReqServerChannel(opts) - elif ttype == 'local': + elif ttype == "local": import salt.transport.local + return salt.transport.local.LocalServerChannel(opts) else: - raise Exception('Channels are only defined for ZeroMQ and TCP') + raise Exception("Channels are only defined for ZeroMQ and TCP") # return NewKindOfChannel(opts, **kwargs) def pre_fork(self, process_manager): - ''' + """ Do anything necessary pre-fork. Since this is on the master side this will primarily be bind and listen (or the equivalent for your network library) - ''' + """ def post_fork(self, payload_handler, io_loop): - ''' + """ Do anything you need post-fork. This should handle all incoming payloads and call payload_handler. You will also be passed io_loop, for all of your asynchronous needs - ''' + """ class PubServerChannel(object): - ''' + """ Factory class to create subscription channels to the master's Publisher - ''' + """ + @staticmethod def factory(opts, **kwargs): # Default to ZeroMQ for now - ttype = 'zeromq' + ttype = "zeromq" # determine the ttype - if 'transport' in opts: - ttype = opts['transport'] - elif 'transport' in opts.get('pillar', {}).get('master', {}): - ttype = opts['pillar']['master']['transport'] + if "transport" in opts: + ttype = opts["transport"] + elif "transport" in opts.get("pillar", {}).get("master", {}): + ttype = opts["pillar"]["master"]["transport"] # switch on available ttypes - if ttype == 'zeromq': + if ttype == "zeromq": import salt.transport.zeromq + return salt.transport.zeromq.ZeroMQPubServerChannel(opts, **kwargs) - elif ttype == 'tcp': + elif ttype == "tcp": import salt.transport.tcp + return salt.transport.tcp.TCPPubServerChannel(opts) - elif ttype == 'local': # TODO: + elif ttype == "local": # TODO: import salt.transport.local + return salt.transport.local.LocalPubServerChannel(opts, **kwargs) else: - raise Exception('Channels are only defined for ZeroMQ and TCP') + raise Exception("Channels are only defined for ZeroMQ and TCP") # return NewKindOfChannel(opts, **kwargs) def pre_fork(self, process_manager, kwargs=None): - ''' + """ Do anything necessary pre-fork. Since this is on the master side this will primarily be used to create IPC channels and create our daemon process to do the actual publishing - ''' + """ def publish(self, load): - ''' + """ Publish "load" to minions - ''' + """ raise NotImplementedError() + # EOF diff --git a/salt/transport/tcp.py b/salt/transport/tcp.py index 12ef24e86f4..6d9759ace02 100644 --- a/salt/transport/tcp.py +++ b/salt/transport/tcp.py @@ -1,25 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" TCP transport classes Wire protocol: "len(payload) msgpack({'head': SOMEHEADER, 'body': SOMEBODY})" -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import errno import logging import os import socket import sys -import time import threading +import time import traceback import weakref # Import Salt Libs import salt.crypt +import salt.exceptions + +# Import Tornado Libs +import salt.ext.tornado +import salt.ext.tornado.concurrent +import salt.ext.tornado.gen +import salt.ext.tornado.iostream +import salt.ext.tornado.netutil +import salt.ext.tornado.tcpclient +import salt.ext.tornado.tcpserver +import salt.payload +import salt.transport.client +import salt.transport.frame +import salt.transport.ipc +import salt.transport.mixins.auth +import salt.transport.server import salt.utils.asynchronous import salt.utils.event import salt.utils.files @@ -27,27 +44,11 @@ import salt.utils.msgpack import salt.utils.platform import salt.utils.process import salt.utils.verify -import salt.payload -import salt.exceptions -import salt.transport.frame -import salt.transport.ipc -import salt.transport.client -import salt.transport.server -import salt.transport.mixins.auth +from salt.exceptions import SaltClientError, SaltReqTimeoutError from salt.ext import six from salt.ext.six.moves import queue # pylint: disable=import-error -from salt.exceptions import SaltReqTimeoutError, SaltClientError from salt.transport import iter_transport_opts -# Import Tornado Libs -import salt.ext.tornado -import salt.ext.tornado.tcpserver -import salt.ext.tornado.gen -import salt.ext.tornado.concurrent -import salt.ext.tornado.tcpclient -import salt.ext.tornado.netutil -import salt.ext.tornado.iostream - # pylint: disable=import-error,no-name-in-module if six.PY2: import urlparse @@ -58,6 +59,7 @@ else: # Import third party libs try: from M2Crypto import RSA + HAS_M2 = True except ImportError: HAS_M2 = False @@ -81,37 +83,39 @@ log = logging.getLogger(__name__) def _set_tcp_keepalive(sock, opts): - ''' + """ Ensure that TCP keepalives are set for the socket. - ''' - if hasattr(socket, 'SO_KEEPALIVE'): - if opts.get('tcp_keepalive', False): + """ + if hasattr(socket, "SO_KEEPALIVE"): + if opts.get("tcp_keepalive", False): sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - if hasattr(socket, 'SOL_TCP'): - if hasattr(socket, 'TCP_KEEPIDLE'): - tcp_keepalive_idle = opts.get('tcp_keepalive_idle', -1) + if hasattr(socket, "SOL_TCP"): + if hasattr(socket, "TCP_KEEPIDLE"): + tcp_keepalive_idle = opts.get("tcp_keepalive_idle", -1) if tcp_keepalive_idle > 0: sock.setsockopt( - socket.SOL_TCP, socket.TCP_KEEPIDLE, - int(tcp_keepalive_idle)) - if hasattr(socket, 'TCP_KEEPCNT'): - tcp_keepalive_cnt = opts.get('tcp_keepalive_cnt', -1) + socket.SOL_TCP, socket.TCP_KEEPIDLE, int(tcp_keepalive_idle) + ) + if hasattr(socket, "TCP_KEEPCNT"): + tcp_keepalive_cnt = opts.get("tcp_keepalive_cnt", -1) if tcp_keepalive_cnt > 0: sock.setsockopt( - socket.SOL_TCP, socket.TCP_KEEPCNT, - int(tcp_keepalive_cnt)) - if hasattr(socket, 'TCP_KEEPINTVL'): - tcp_keepalive_intvl = opts.get('tcp_keepalive_intvl', -1) + socket.SOL_TCP, socket.TCP_KEEPCNT, int(tcp_keepalive_cnt) + ) + if hasattr(socket, "TCP_KEEPINTVL"): + tcp_keepalive_intvl = opts.get("tcp_keepalive_intvl", -1) if tcp_keepalive_intvl > 0: sock.setsockopt( - socket.SOL_TCP, socket.TCP_KEEPINTVL, - int(tcp_keepalive_intvl)) - if hasattr(socket, 'SIO_KEEPALIVE_VALS'): + socket.SOL_TCP, + socket.TCP_KEEPINTVL, + int(tcp_keepalive_intvl), + ) + if hasattr(socket, "SIO_KEEPALIVE_VALS"): # Windows doesn't support TCP_KEEPIDLE, TCP_KEEPCNT, nor # TCP_KEEPINTVL. Instead, it has its own proprietary # SIO_KEEPALIVE_VALS. - tcp_keepalive_idle = opts.get('tcp_keepalive_idle', -1) - tcp_keepalive_intvl = opts.get('tcp_keepalive_intvl', -1) + tcp_keepalive_idle = opts.get("tcp_keepalive_idle", -1) + tcp_keepalive_intvl = opts.get("tcp_keepalive_intvl", -1) # Windows doesn't support changing something equivalent to # TCP_KEEPCNT. if tcp_keepalive_idle > 0 or tcp_keepalive_intvl > 0: @@ -128,22 +132,29 @@ def _set_tcp_keepalive(sock, opts): tcp_keepalive_intvl = 1 # The values expected are in milliseconds, so multiply by # 1000. - sock.ioctl(socket.SIO_KEEPALIVE_VALS, ( - 1, int(tcp_keepalive_idle * 1000), - int(tcp_keepalive_intvl * 1000))) + sock.ioctl( + socket.SIO_KEEPALIVE_VALS, + ( + 1, + int(tcp_keepalive_idle * 1000), + int(tcp_keepalive_intvl * 1000), + ), + ) else: sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 0) if USE_LOAD_BALANCER: + class LoadBalancerServer(SignalHandlingProcess): - ''' + """ Raw TCP server which runs in its own process and will listen for incoming connections. Each incoming connection will be sent via multiprocessing queue to the workers. Since the queue is shared amongst workers, only one worker will handle a given connection. - ''' + """ + # TODO: opts! # Based on default used in salt.ext.tornado.netutil.bind_sockets() backlog = 128 @@ -160,18 +171,18 @@ if USE_LOAD_BALANCER: # Windows. def __setstate__(self, state): self.__init__( - state['opts'], - state['socket_queue'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["opts"], + state["socket_queue"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'socket_queue': self.socket_queue, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "socket_queue": self.socket_queue, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def close(self): @@ -183,17 +194,18 @@ if USE_LOAD_BALANCER: # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def run(self): - ''' + """ Start the load balancer - ''' + """ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) _set_tcp_keepalive(self._socket, self.opts) self._socket.setblocking(1) - self._socket.bind((self.opts['interface'], int(self.opts['ret_port']))) + self._socket.bind((self.opts["interface"], int(self.opts["ret_port"]))) self._socket.listen(self.backlog) while True: @@ -209,28 +221,32 @@ if USE_LOAD_BALANCER: # ECONNABORTED indicates that there was a connection # but it was closed while still in the accept queue. # (observed on FreeBSD). - if salt.ext.tornado.util.errno_from_exception(e) == errno.ECONNABORTED: + if ( + salt.ext.tornado.util.errno_from_exception(e) + == errno.ECONNABORTED + ): continue raise # TODO: move serial down into message library class AsyncTCPReqChannel(salt.transport.client.ReqChannel): - ''' + """ Encapsulate sending routines to tcp. Note: this class returns a singleton - ''' + """ + # This class is only a singleton per minion/master pair # mapping of io_loop -> {key -> channel} instance_map = weakref.WeakKeyDictionary() def __new__(cls, opts, **kwargs): - ''' + """ Only create one instance of channel per __key() - ''' + """ # do we have any mapping for this io_loop - io_loop = kwargs.get('io_loop') or salt.ext.tornado.ioloop.IOLoop.current() + io_loop = kwargs.get("io_loop") or salt.ext.tornado.ioloop.IOLoop.current() if io_loop not in cls.instance_map: cls.instance_map[io_loop] = weakref.WeakValueDictionary() loop_instance_map = cls.instance_map[io_loop] @@ -238,7 +254,7 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel): key = cls.__key(opts, **kwargs) obj = loop_instance_map.get(key) if obj is None: - log.debug('Initializing new AsyncTCPReqChannel for %s', key) + log.debug("Initializing new AsyncTCPReqChannel for %s", key) # we need to make a local variable for this, as we are going to store # it in a WeakValueDictionary-- which will remove the item if no one # references it-- this forces a reference while we return to the caller @@ -251,18 +267,19 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel): else: with obj._refcount_lock: obj._refcount += 1 - log.debug('Re-using AsyncTCPReqChannel for %s', key) + log.debug("Re-using AsyncTCPReqChannel for %s", key) return obj @classmethod def __key(cls, opts, **kwargs): - if 'master_uri' in kwargs: - opts['master_uri'] = kwargs['master_uri'] - return (opts['pki_dir'], # where the keys are stored - opts['id'], # minion ID - opts['master_uri'], - kwargs.get('crypt', 'aes'), # TODO: use the same channel for crypt - ) + if "master_uri" in kwargs: + opts["master_uri"] = kwargs["master_uri"] + return ( + opts["pki_dir"], # where the keys are stored + opts["id"], # minion ID + opts["master_uri"], + kwargs.get("crypt", "aes"), # TODO: use the same channel for crypt + ) # has to remain empty for singletons, since __init__ will *always* be called def __init__(self, opts, **kwargs): @@ -275,24 +292,29 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel): self.serial = salt.payload.Serial(self.opts) # crypt defaults to 'aes' - self.crypt = kwargs.get('crypt', 'aes') + self.crypt = kwargs.get("crypt", "aes") - self.io_loop = kwargs.get('io_loop') or salt.ext.tornado.ioloop.IOLoop.current() + self.io_loop = kwargs.get("io_loop") or salt.ext.tornado.ioloop.IOLoop.current() - if self.crypt != 'clear': + if self.crypt != "clear": self.auth = salt.crypt.AsyncAuth(self.opts, io_loop=self.io_loop) - resolver = kwargs.get('resolver') + resolver = kwargs.get("resolver") - parse = urlparse.urlparse(self.opts['master_uri']) - master_host, master_port = parse.netloc.rsplit(':', 1) + parse = urlparse.urlparse(self.opts["master_uri"]) + master_host, master_port = parse.netloc.rsplit(":", 1) self.master_addr = (master_host, int(master_port)) self._closing = False - self.message_client = SaltMessageClientPool(self.opts, - args=(self.opts, master_host, int(master_port),), - kwargs={'io_loop': self.io_loop, 'resolver': resolver, - 'source_ip': self.opts.get('source_ip'), - 'source_port': self.opts.get('source_ret_port')}) + self.message_client = SaltMessageClientPool( + self.opts, + args=(self.opts, master_host, int(master_port),), + kwargs={ + "io_loop": self.io_loop, + "resolver": resolver, + "source_ip": self.opts.get("source_ip"), + "source_port": self.opts.get("source_ret_port"), + }, + ) def close(self): if self._closing: @@ -303,12 +325,12 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel): with self._refcount_lock: self._refcount -= 1 log.debug( - 'This is not the last %s instance. Not closing yet.', - self.__class__.__name__ + "This is not the last %s instance. Not closing yet.", + self.__class__.__name__, ) return - log.debug('Closing %s instance', self.__class__.__name__) + log.debug("Closing %s instance", self.__class__.__name__) self._closing = True self.message_client.close() @@ -335,25 +357,30 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel): if exc.errno != errno.EBADF: # If its not a bad file descriptor error, raise raise + # pylint: enable=W1701 def _package_load(self, load): return { - 'enc': self.crypt, - 'load': load, + "enc": self.crypt, + "load": load, } @salt.ext.tornado.gen.coroutine - def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout=60): + def crypted_transfer_decode_dictentry( + self, load, dictkey=None, tries=3, timeout=60 + ): if not self.auth.authenticated: yield self.auth.authenticate() - ret = yield self.message_client.send(self._package_load(self.auth.crypticle.dumps(load)), timeout=timeout) + ret = yield self.message_client.send( + self._package_load(self.auth.crypticle.dumps(load)), timeout=timeout + ) key = self.auth.get_keys() if HAS_M2: - aes = key.private_decrypt(ret['key'], RSA.pkcs1_oaep_padding) + aes = key.private_decrypt(ret["key"], RSA.pkcs1_oaep_padding) else: cipher = PKCS1_OAEP.new(key) - aes = cipher.decrypt(ret['key']) + aes = cipher.decrypt(ret["key"]) pcrypt = salt.crypt.Crypticle(self.opts, aes) data = pcrypt.loads(ret[dictkey]) if six.PY3: @@ -362,17 +389,18 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel): @salt.ext.tornado.gen.coroutine def _crypted_transfer(self, load, tries=3, timeout=60): - ''' + """ In case of authentication errors, try to renegotiate authentication and retry the method. Indeed, we can fail too early in case of a master restart during a minion state execution call - ''' + """ + @salt.ext.tornado.gen.coroutine def _do_transfer(): - data = yield self.message_client.send(self._package_load(self.auth.crypticle.dumps(load)), - timeout=timeout, - ) + data = yield self.message_client.send( + self._package_load(self.auth.crypticle.dumps(load)), timeout=timeout, + ) # we may not have always data # as for example for saltcall ret submission, this is a blind # communication, we do not subscribe to return events, we just @@ -400,67 +428,64 @@ class AsyncTCPReqChannel(salt.transport.client.ReqChannel): @salt.ext.tornado.gen.coroutine def send(self, load, tries=3, timeout=60, raw=False): - ''' + """ Send a request, return a future which will complete when we send the message - ''' + """ try: - if self.crypt == 'clear': + if self.crypt == "clear": ret = yield self._uncrypted_transfer(load, tries=tries, timeout=timeout) else: ret = yield self._crypted_transfer(load, tries=tries, timeout=timeout) except salt.ext.tornado.iostream.StreamClosedError: # Convert to 'SaltClientError' so that clients can handle this # exception more appropriately. - raise SaltClientError('Connection to master lost') + raise SaltClientError("Connection to master lost") raise salt.ext.tornado.gen.Return(ret) -class AsyncTCPPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.transport.client.AsyncPubChannel): - def __init__(self, - opts, - **kwargs): +class AsyncTCPPubChannel( + salt.transport.mixins.auth.AESPubClientMixin, salt.transport.client.AsyncPubChannel +): + def __init__(self, opts, **kwargs): self.opts = opts self.serial = salt.payload.Serial(self.opts) - self.crypt = kwargs.get('crypt', 'aes') - self.io_loop = kwargs.get('io_loop') or salt.ext.tornado.ioloop.IOLoop.current() + self.crypt = kwargs.get("crypt", "aes") + self.io_loop = kwargs.get("io_loop") or salt.ext.tornado.ioloop.IOLoop.current() self.connected = False self._closing = False self._reconnected = False - self.event = salt.utils.event.get_event( - 'minion', - opts=self.opts, - listen=False - ) + self.event = salt.utils.event.get_event("minion", opts=self.opts, listen=False) def close(self): if self._closing: return self._closing = True - if hasattr(self, 'message_client'): + if hasattr(self, "message_client"): self.message_client.close() # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def _package_load(self, load): return { - 'enc': self.crypt, - 'load': load, + "enc": self.crypt, + "load": load, } @salt.ext.tornado.gen.coroutine def send_id(self, tok, force_auth): - ''' + """ Send the minion id to the master so that the master may better track the connection state of the minion. In case of authentication errors, try to renegotiate authentication and retry the method. - ''' - load = {'id': self.opts['id'], 'tok': tok} + """ + load = {"id": self.opts["id"], "tok": tok} @salt.ext.tornado.gen.coroutine def _do_transfer(): @@ -471,7 +496,10 @@ class AsyncTCPPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.tran if force_auth or not self.auth.authenticated: count = 0 - while count <= self.opts['tcp_authentication_retries'] or self.opts['tcp_authentication_retries'] < 0: + while ( + count <= self.opts["tcp_authentication_retries"] + or self.opts["tcp_authentication_retries"] < 0 + ): try: yield self.auth.authenticate() break @@ -494,46 +522,39 @@ class AsyncTCPPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.tran # may have been restarted yield self.send_id(self.tok, self._reconnected) self.connected = True - self.event.fire_event( - {'master': self.opts['master']}, - '__master_connected' - ) + self.event.fire_event({"master": self.opts["master"]}, "__master_connected") if self._reconnected: # On reconnects, fire a master event to notify that the minion is # available. - if self.opts.get('__role') == 'syndic': - data = 'Syndic {0} started at {1}'.format( - self.opts['id'], - time.asctime() - ) - tag = salt.utils.event.tagify( - [self.opts['id'], 'start'], - 'syndic' + if self.opts.get("__role") == "syndic": + data = "Syndic {0} started at {1}".format( + self.opts["id"], time.asctime() ) + tag = salt.utils.event.tagify([self.opts["id"], "start"], "syndic") else: - data = 'Minion {0} started at {1}'.format( - self.opts['id'], - time.asctime() + data = "Minion {0} started at {1}".format( + self.opts["id"], time.asctime() ) - tag = salt.utils.event.tagify( - [self.opts['id'], 'start'], - 'minion' - ) - load = {'id': self.opts['id'], - 'cmd': '_minion_event', - 'pretag': None, - 'tok': self.tok, - 'data': data, - 'tag': tag} + tag = salt.utils.event.tagify([self.opts["id"], "start"], "minion") + load = { + "id": self.opts["id"], + "cmd": "_minion_event", + "pretag": None, + "tok": self.tok, + "data": data, + "tag": tag, + } req_channel = salt.utils.asynchronous.SyncWrapper( AsyncTCPReqChannel, (self.opts,) ) try: req_channel.send(load, timeout=60) except salt.exceptions.SaltReqTimeoutError: - log.info('fire_master failed: master could not be contacted. Request timed out.') + log.info( + "fire_master failed: master could not be contacted. Request timed out." + ) except Exception: # pylint: disable=broad-except - log.info('fire_master failed: %s', traceback.format_exc()) + log.info("fire_master failed: %s", traceback.format_exc()) finally: # SyncWrapper will call either close() or destroy(), whichever is available del req_channel @@ -544,47 +565,49 @@ class AsyncTCPPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.tran if self._closing: return self.connected = False - self.event.fire_event( - {'master': self.opts['master']}, - '__master_disconnected' - ) + self.event.fire_event({"master": self.opts["master"]}, "__master_disconnected") @salt.ext.tornado.gen.coroutine def connect(self): try: self.auth = salt.crypt.AsyncAuth(self.opts, io_loop=self.io_loop) - self.tok = self.auth.gen_token(b'salt') + self.tok = self.auth.gen_token(b"salt") if not self.auth.authenticated: yield self.auth.authenticate() if self.auth.authenticated: # if this is changed from the default, we assume it was intentional - if int(self.opts.get('publish_port', 4506)) != 4506: - self.publish_port = self.opts.get('publish_port') + if int(self.opts.get("publish_port", 4506)) != 4506: + self.publish_port = self.opts.get("publish_port") # else take the relayed publish_port master reports else: - self.publish_port = self.auth.creds['publish_port'] + self.publish_port = self.auth.creds["publish_port"] self.message_client = SaltMessageClientPool( self.opts, - args=(self.opts, self.opts['master_ip'], int(self.publish_port),), - kwargs={'io_loop': self.io_loop, - 'connect_callback': self.connect_callback, - 'disconnect_callback': self.disconnect_callback, - 'source_ip': self.opts.get('source_ip'), - 'source_port': self.opts.get('source_publish_port')}) + args=(self.opts, self.opts["master_ip"], int(self.publish_port),), + kwargs={ + "io_loop": self.io_loop, + "connect_callback": self.connect_callback, + "disconnect_callback": self.disconnect_callback, + "source_ip": self.opts.get("source_ip"), + "source_port": self.opts.get("source_publish_port"), + }, + ) yield self.message_client.connect() # wait for the client to be connected self.connected = True # TODO: better exception handling... except KeyboardInterrupt: # pylint: disable=try-except-raise raise except Exception as exc: # pylint: disable=broad-except - if '-|RETRY|-' not in six.text_type(exc): - raise SaltClientError('Unable to sign_in to master: {0}'.format(exc)) # TODO: better error message + if "-|RETRY|-" not in six.text_type(exc): + raise SaltClientError( + "Unable to sign_in to master: {0}".format(exc) + ) # TODO: better error message def on_recv(self, callback): - ''' + """ Register an on_recv callback - ''' + """ if callback is None: return self.message_client.on_recv(callback) @@ -598,10 +621,13 @@ class AsyncTCPPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.tran body = salt.transport.frame.decode_embedded_strs(body) ret = yield self._decode_payload(body) callback(ret) + return self.message_client.on_recv(wrap_callback) -class TCPReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.transport.server.ReqServerChannel): +class TCPReqServerChannel( + salt.transport.mixins.auth.AESReqServerMixin, salt.transport.server.ReqServerChannel +): # TODO: opts! backlog = 5 @@ -626,28 +652,33 @@ class TCPReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.tra six.reraise(*sys.exc_info()) self._socket.close() self._socket = None - if hasattr(self.req_server, 'shutdown'): + if hasattr(self.req_server, "shutdown"): try: self.req_server.shutdown() except Exception as exc: # pylint: disable=broad-except - log.exception('TCPReqServerChannel close generated an exception: %s', str(exc)) - elif hasattr(self.req_server, 'stop'): + log.exception( + "TCPReqServerChannel close generated an exception: %s", str(exc) + ) + elif hasattr(self.req_server, "stop"): try: self.req_server.stop() except socket.error as exc: if exc.errno != 9: raise - log.exception('TCPReqServerChannel close generated an exception: %s', str(exc)) + log.exception( + "TCPReqServerChannel close generated an exception: %s", str(exc) + ) # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def pre_fork(self, process_manager): - ''' + """ Pre-fork we need to create the zmq router device - ''' + """ salt.transport.mixins.auth.AESReqServerMixin.pre_fork(self, process_manager) if USE_LOAD_BALANCER: self.socket_queue = multiprocessing.Queue() @@ -659,71 +690,92 @@ class TCPReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.tra self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) _set_tcp_keepalive(self._socket, self.opts) self._socket.setblocking(0) - self._socket.bind((self.opts['interface'], int(self.opts['ret_port']))) + self._socket.bind((self.opts["interface"], int(self.opts["ret_port"]))) def post_fork(self, payload_handler, io_loop): - ''' + """ After forking we need to create all of the local sockets to listen to the router payload_handler: function to call with your payloads - ''' + """ self.payload_handler = payload_handler self.io_loop = io_loop self.serial = salt.payload.Serial(self.opts) with salt.utils.asynchronous.current_ioloop(self.io_loop): if USE_LOAD_BALANCER: - self.req_server = LoadBalancerWorker(self.socket_queue, - self.handle_message, - ssl_options=self.opts.get('ssl')) + self.req_server = LoadBalancerWorker( + self.socket_queue, + self.handle_message, + ssl_options=self.opts.get("ssl"), + ) else: if salt.utils.platform.is_windows(): self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) _set_tcp_keepalive(self._socket, self.opts) self._socket.setblocking(0) - self._socket.bind((self.opts['interface'], int(self.opts['ret_port']))) - self.req_server = SaltMessageServer(self.handle_message, - ssl_options=self.opts.get('ssl'), - io_loop=self.io_loop) + self._socket.bind( + (self.opts["interface"], int(self.opts["ret_port"])) + ) + self.req_server = SaltMessageServer( + self.handle_message, + ssl_options=self.opts.get("ssl"), + io_loop=self.io_loop, + ) self.req_server.add_socket(self._socket) self._socket.listen(self.backlog) - salt.transport.mixins.auth.AESReqServerMixin.post_fork(self, payload_handler, io_loop) + salt.transport.mixins.auth.AESReqServerMixin.post_fork( + self, payload_handler, io_loop + ) @salt.ext.tornado.gen.coroutine def handle_message(self, stream, header, payload): - ''' + """ Handle incoming messages from underylying tcp streams - ''' + """ try: try: payload = self._decode_payload(payload) except Exception: # pylint: disable=broad-except - stream.write(salt.transport.frame.frame_msg('bad load', header=header)) + stream.write(salt.transport.frame.frame_msg("bad load", header=header)) raise salt.ext.tornado.gen.Return() # TODO helper functions to normalize payload? - if not isinstance(payload, dict) or not isinstance(payload.get('load'), dict): - yield stream.write(salt.transport.frame.frame_msg( - 'payload and load must be a dict', header=header)) + if not isinstance(payload, dict) or not isinstance( + payload.get("load"), dict + ): + yield stream.write( + salt.transport.frame.frame_msg( + "payload and load must be a dict", header=header + ) + ) raise salt.ext.tornado.gen.Return() try: - id_ = payload['load'].get('id', '') - if str('\0') in id_: - log.error('Payload contains an id with a null byte: %s', payload) - stream.send(self.serial.dumps('bad load: id contains a null byte')) + id_ = payload["load"].get("id", "") + if str("\0") in id_: + log.error("Payload contains an id with a null byte: %s", payload) + stream.send(self.serial.dumps("bad load: id contains a null byte")) raise salt.ext.tornado.gen.Return() except TypeError: - log.error('Payload contains non-string id: %s', payload) - stream.send(self.serial.dumps('bad load: id {0} is not a string'.format(id_))) + log.error("Payload contains non-string id: %s", payload) + stream.send( + self.serial.dumps("bad load: id {0} is not a string".format(id_)) + ) raise salt.ext.tornado.gen.Return() # intercept the "_auth" commands, since the main daemon shouldn't know # anything about our key auth - if payload['enc'] == 'clear' and payload.get('load', {}).get('cmd') == '_auth': - yield stream.write(salt.transport.frame.frame_msg( - self._auth(payload['load']), header=header)) + if ( + payload["enc"] == "clear" + and payload.get("load", {}).get("cmd") == "_auth" + ): + yield stream.write( + salt.transport.frame.frame_msg( + self._auth(payload["load"]), header=header + ) + ) raise salt.ext.tornado.gen.Return() # TODO: test @@ -731,25 +783,33 @@ class TCPReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.tra ret, req_opts = yield self.payload_handler(payload) except Exception as e: # pylint: disable=broad-except # always attempt to return an error to the minion - stream.write('Some exception handling minion payload') - log.error('Some exception handling a payload from minion', exc_info=True) + stream.write("Some exception handling minion payload") + log.error( + "Some exception handling a payload from minion", exc_info=True + ) stream.close() raise salt.ext.tornado.gen.Return() - req_fun = req_opts.get('fun', 'send') - if req_fun == 'send_clear': + req_fun = req_opts.get("fun", "send") + if req_fun == "send_clear": stream.write(salt.transport.frame.frame_msg(ret, header=header)) - elif req_fun == 'send': - stream.write(salt.transport.frame.frame_msg(self.crypticle.dumps(ret), header=header)) - elif req_fun == 'send_private': - stream.write(salt.transport.frame.frame_msg(self._encrypt_private(ret, - req_opts['key'], - req_opts['tgt'], - ), header=header)) + elif req_fun == "send": + stream.write( + salt.transport.frame.frame_msg( + self.crypticle.dumps(ret), header=header + ) + ) + elif req_fun == "send_private": + stream.write( + salt.transport.frame.frame_msg( + self._encrypt_private(ret, req_opts["key"], req_opts["tgt"],), + header=header, + ) + ) else: - log.error('Unknown req_fun %s', req_fun) + log.error("Unknown req_fun %s", req_fun) # always attempt to return an error to the minion - stream.write('Server-side exception handling payload') + stream.write("Server-side exception handling payload") stream.close() except salt.ext.tornado.gen.Return: raise @@ -757,21 +817,24 @@ class TCPReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, salt.tra # Stream was closed. This could happen if the remote side # closed the connection on its end (eg in a timeout or shutdown # situation). - log.error('Connection was unexpectedly closed', exc_info=True) + log.error("Connection was unexpectedly closed", exc_info=True) except Exception as exc: # pylint: disable=broad-except # Absorb any other exceptions - log.error('Unexpected exception occurred: %s', exc, exc_info=True) + log.error("Unexpected exception occurred: %s", exc, exc_info=True) raise salt.ext.tornado.gen.Return() class SaltMessageServer(salt.ext.tornado.tcpserver.TCPServer, object): - ''' + """ Raw TCP server which will receive all of the TCP streams and re-assemble messages that are sent through to us - ''' + """ + def __init__(self, message_handler, *args, **kwargs): - io_loop = kwargs.pop('io_loop', None) or salt.ext.tornado.ioloop.IOLoop.current() + io_loop = ( + kwargs.pop("io_loop", None) or salt.ext.tornado.ioloop.IOLoop.current() + ) super(SaltMessageServer, self).__init__(*args, **kwargs) self.io_loop = io_loop self.clients = [] @@ -780,10 +843,10 @@ class SaltMessageServer(salt.ext.tornado.tcpserver.TCPServer, object): @salt.ext.tornado.gen.coroutine def handle_stream(self, stream, address): - ''' + """ Handle incoming streams and add messages to the incoming queue - ''' - log.trace('Req client %s connected', address) + """ + log.trace("Req client %s connected", address) self.clients.append((stream, address)) unpacker = salt.utils.msgpack.Unpacker() try: @@ -795,14 +858,16 @@ class SaltMessageServer(salt.ext.tornado.tcpserver.TCPServer, object): framed_msg = salt.transport.frame.decode_embedded_strs( framed_msg ) - header = framed_msg['head'] - self.io_loop.spawn_callback(self.message_handler, stream, header, framed_msg['body']) + header = framed_msg["head"] + self.io_loop.spawn_callback( + self.message_handler, stream, header, framed_msg["body"] + ) except salt.ext.tornado.iostream.StreamClosedError: - log.trace('req client disconnected %s', address) + log.trace("req client disconnected %s", address) self.remove_client((stream, address)) except Exception as e: # pylint: disable=broad-except - log.trace('other master-side exception: %s', e) + log.trace("other master-side exception: %s", e) self.remove_client((stream, address)) stream.close() @@ -813,9 +878,9 @@ class SaltMessageServer(salt.ext.tornado.tcpserver.TCPServer, object): log.trace("Message server client was not in list to remove") def shutdown(self): - ''' + """ Shutdown the whole server - ''' + """ if self._shutting_down: return self._shutting_down = True @@ -831,16 +896,17 @@ class SaltMessageServer(salt.ext.tornado.tcpserver.TCPServer, object): if USE_LOAD_BALANCER: + class LoadBalancerWorker(SaltMessageServer): - ''' + """ This will receive TCP connections from 'LoadBalancerServer' via a multiprocessing queue. Since the queue is shared amongst workers, only one worker will handle a given connection. - ''' + """ + def __init__(self, socket_queue, message_handler, *args, **kwargs): - super(LoadBalancerWorker, self).__init__( - message_handler, *args, **kwargs) + super(LoadBalancerWorker, self).__init__(message_handler, *args, **kwargs) self.socket_queue = socket_queue self._stop = threading.Event() self.thread = threading.Thread(target=self.socket_queue_thread) @@ -863,49 +929,57 @@ if USE_LOAD_BALANCER: # 'salt.ext.tornado.tcpserver.TCPServer'. # 'self._handle_connection' defined in same super class. self.io_loop.spawn_callback( - self._handle_connection, client_socket, address) + self._handle_connection, client_socket, address + ) except (KeyboardInterrupt, SystemExit): pass class TCPClientKeepAlive(salt.ext.tornado.tcpclient.TCPClient): - ''' + """ Override _create_stream() in TCPClient to enable keep alive support. - ''' + """ + def __init__(self, opts, resolver=None): self.opts = opts super(TCPClientKeepAlive, self).__init__(resolver=resolver) - def _create_stream(self, max_buffer_size, af, addr, **kwargs): # pylint: disable=unused-argument,arguments-differ - ''' + def _create_stream( + self, max_buffer_size, af, addr, **kwargs + ): # pylint: disable=unused-argument,arguments-differ + """ Override _create_stream() in TCPClient. Tornado 4.5 added the kwargs 'source_ip' and 'source_port'. Due to this, use **kwargs to swallow these and any future kwargs to maintain compatibility. - ''' + """ # Always connect in plaintext; we'll convert to ssl if necessary # after one connection has completed. sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) _set_tcp_keepalive(sock, self.opts) stream = salt.ext.tornado.iostream.IOStream( - sock, - max_buffer_size=max_buffer_size) + sock, max_buffer_size=max_buffer_size + ) if salt.ext.tornado.version_info < (5,): return stream.connect(addr) return stream, stream.connect(addr) class SaltMessageClientPool(salt.transport.MessageClientPool): - ''' + """ Wrapper class of SaltMessageClient to avoid blocking waiting while writing data to socket. - ''' + """ + def __init__(self, opts, args=None, kwargs=None): - super(SaltMessageClientPool, self).__init__(SaltMessageClient, opts, args=args, kwargs=kwargs) + super(SaltMessageClientPool, self).__init__( + SaltMessageClient, opts, args=args, kwargs=kwargs + ) # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def close(self): @@ -939,12 +1013,22 @@ class SaltMessageClientPool(salt.transport.MessageClientPool): # TODO: limit in-flight messages. # TODO: singleton? Something to not re-create the tcp connection so much class SaltMessageClient(object): - ''' + """ Low-level message sending client - ''' - def __init__(self, opts, host, port, io_loop=None, resolver=None, - connect_callback=None, disconnect_callback=None, - source_ip=None, source_port=None): + """ + + def __init__( + self, + opts, + host, + port, + io_loop=None, + resolver=None, + connect_callback=None, + disconnect_callback=None, + source_ip=None, + source_port=None, + ): self.opts = opts self.host = host self.port = port @@ -982,7 +1066,7 @@ class SaltMessageClient(object): if self._closing: return self._closing = True - if hasattr(self, '_stream') and not self._stream.closed(): + if hasattr(self, "_stream") and not self._stream.closed(): # If _stream_return() hasn't completed, it means the IO # Loop is stopped (such as when using # 'salt.utils.asynchronous.SyncWrapper'). Ensure that @@ -1001,15 +1085,18 @@ class SaltMessageClient(object): # 'StreamClosedError' when the stream is closed. if self._read_until_future.done(): self._read_until_future.exception() - if (self.io_loop != salt.ext.tornado.ioloop.IOLoop.current(instance=False) - or not self._stream_return_future.done()): + if ( + self.io_loop + != salt.ext.tornado.ioloop.IOLoop.current(instance=False) + or not self._stream_return_future.done() + ): self.io_loop.add_future( self._stream_return_future, - lambda future: self._stop_io_loop() + lambda future: self._stop_io_loop(), ) self.io_loop.start() except Exception as e: # pylint: disable=broad-except - log.info('Exception caught in SaltMessageClient.close: %s', str(e)) + log.info("Exception caught in SaltMessageClient.close: %s", str(e)) finally: orig_loop.make_current() self._tcp_client.close() @@ -1023,13 +1110,14 @@ class SaltMessageClient(object): # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def connect(self): - ''' + """ Ask for this client to reconnect to the origin - ''' - if hasattr(self, '_connecting_future') and not self._connecting_future.done(): + """ + if hasattr(self, "_connecting_future") and not self._connecting_future.done(): future = self._connecting_future else: future = salt.ext.tornado.concurrent.Future() @@ -1038,9 +1126,11 @@ class SaltMessageClient(object): # Add the callback only when a new future is created if self.connect_callback is not None: + def handle_future(future): response = future.result() self.io_loop.add_callback(self.connect_callback, response) + future.add_done_callback(handle_future) return future @@ -1048,9 +1138,9 @@ class SaltMessageClient(object): # TODO: tcp backoff opts @salt.ext.tornado.gen.coroutine def _connect(self): - ''' + """ Try to connect for the rest of time! - ''' + """ while True: if self._closing: break @@ -1061,33 +1151,39 @@ class SaltMessageClient(object): ### source_ip and source_port are supported only in Tornado >= 4.5 # See http://www.tornadoweb.org/en/stable/releases/v4.5.0.html # Otherwise will just ignore these args - kwargs = {'source_ip': self.source_ip, - 'source_port': self.source_port} + kwargs = { + "source_ip": self.source_ip, + "source_port": self.source_port, + } else: - log.warning('If you need a certain source IP/port, consider upgrading Tornado >= 4.5') + log.warning( + "If you need a certain source IP/port, consider upgrading Tornado >= 4.5" + ) with salt.utils.asynchronous.current_ioloop(self.io_loop): - self._stream = yield self._tcp_client.connect(self.host, - self.port, - ssl_options=self.opts.get('ssl'), - **kwargs) + self._stream = yield self._tcp_client.connect( + self.host, self.port, ssl_options=self.opts.get("ssl"), **kwargs + ) self._connecting_future.set_result(True) break except Exception as exc: # pylint: disable=broad-except - log.warn('TCP Message Client encountered an exception %r', exc) + log.warn("TCP Message Client encountered an exception %r", exc) yield salt.ext.tornado.gen.sleep(1) # TODO: backoff - #self._connecting_future.set_exception(e) + # self._connecting_future.set_exception(e) @salt.ext.tornado.gen.coroutine def _stream_return(self): try: while not self._closing and ( - not self._connecting_future.done() or - self._connecting_future.result() is not True): + not self._connecting_future.done() + or self._connecting_future.result() is not True + ): yield self._connecting_future unpacker = salt.utils.msgpack.Unpacker() while not self._closing: try: - self._read_until_future = self._stream.read_bytes(4096, partial=True) + self._read_until_future = self._stream.read_bytes( + 4096, partial=True + ) wire_bytes = yield self._read_until_future unpacker.feed(wire_bytes) for framed_msg in unpacker: @@ -1095,9 +1191,9 @@ class SaltMessageClient(object): framed_msg = salt.transport.frame.decode_embedded_strs( framed_msg ) - header = framed_msg['head'] - body = framed_msg['body'] - message_id = header.get('mid') + header = framed_msg["head"] + body = framed_msg["body"] + message_id = header.get("mid") if message_id in self.send_future_map: self.send_future_map.pop(message_id).set_result(body) @@ -1106,9 +1202,16 @@ class SaltMessageClient(object): if self._on_recv is not None: self.io_loop.spawn_callback(self._on_recv, header, body) else: - log.error('Got response for message_id %s that we are not tracking', message_id) + log.error( + "Got response for message_id %s that we are not tracking", + message_id, + ) except salt.ext.tornado.iostream.StreamClosedError as e: - log.debug('tcp stream to %s:%s closed, unable to recv', self.host, self.port) + log.debug( + "tcp stream to %s:%s closed, unable to recv", + self.host, + self.port, + ) for future in six.itervalues(self.send_future_map): future.set_exception(e) self.send_future_map = {} @@ -1122,13 +1225,15 @@ class SaltMessageClient(object): yield self._connecting_future except TypeError: # This is an invalid transport - if 'detect_mode' in self.opts: - log.info('There was an error trying to use TCP transport; ' - 'attempting to fallback to another transport') + if "detect_mode" in self.opts: + log.info( + "There was an error trying to use TCP transport; " + "attempting to fallback to another transport" + ) else: raise SaltClientError except Exception as e: # pylint: disable=broad-except - log.error('Exception parsing response', exc_info=True) + log.error("Exception parsing response", exc_info=True) for future in six.itervalues(self.send_future_map): future.set_exception(e) self.send_future_map = {} @@ -1145,7 +1250,10 @@ class SaltMessageClient(object): @salt.ext.tornado.gen.coroutine def _stream_send(self): - while not self._connecting_future.done() or self._connecting_future.result() is not True: + while ( + not self._connecting_future.done() + or self._connecting_future.result() is not True + ): yield self._connecting_future while len(self.send_queue) > 0: message_id, item = self.send_queue[0] @@ -1174,7 +1282,7 @@ class SaltMessageClient(object): if self._mid >= self._max_messages: if wrap: # this shouldn't ever happen, but just in case - raise Exception('Unable to find available messageid') + raise Exception("Unable to find available messageid") self._mid = 1 wrap = True else: @@ -1184,14 +1292,16 @@ class SaltMessageClient(object): # TODO: return a message object which takes care of multiplexing? def on_recv(self, callback): - ''' + """ Register a callback for received messages (that we didn't initiate) - ''' + """ if callback is None: self._on_recv = callback else: + def wrap_recv(header, body): callback(body) + self._on_recv = wrap_recv def remove_message_timeout(self, message_id): @@ -1205,43 +1315,50 @@ class SaltMessageClient(object): del self.send_timeout_map[message_id] if message_id in self.send_future_map: self.send_future_map.pop(message_id).set_exception( - SaltReqTimeoutError('Message timed out') + SaltReqTimeoutError("Message timed out") ) def send(self, msg, timeout=None, callback=None, raw=False): - ''' + """ Send given message, and return a future - ''' + """ message_id = self._message_id() - header = {'mid': message_id} + header = {"mid": message_id} future = salt.ext.tornado.concurrent.Future() if callback is not None: + def handle_future(future): response = future.result() self.io_loop.add_callback(callback, response) + future.add_done_callback(handle_future) # Add this future to the mapping self.send_future_map[message_id] = future - if self.opts.get('detect_mode') is True: + if self.opts.get("detect_mode") is True: timeout = 1 if timeout is not None: - send_timeout = self.io_loop.call_later(timeout, self.timeout_message, message_id) + send_timeout = self.io_loop.call_later( + timeout, self.timeout_message, message_id + ) self.send_timeout_map[message_id] = send_timeout # if we don't have a send queue, we need to spawn the callback to do the sending if len(self.send_queue) == 0: self.io_loop.spawn_callback(self._stream_send) - self.send_queue.append((message_id, salt.transport.frame.frame_msg(msg, header=header))) + self.send_queue.append( + (message_id, salt.transport.frame.frame_msg(msg, header=header)) + ) return future class Subscriber(object): - ''' + """ Client object for use with the TCP publisher server - ''' + """ + def __init__(self, stream, address): self.stream = stream self.address = address @@ -1267,15 +1384,17 @@ class Subscriber(object): # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): - ''' + """ TCP publisher - ''' + """ + def __init__(self, opts, io_loop=None): - super(PubServer, self).__init__(ssl_options=opts.get('ssl')) + super(PubServer, self).__init__(ssl_options=opts.get("ssl")) self.io_loop = io_loop self.opts = opts self._closing = False @@ -1283,10 +1402,10 @@ class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): self.aes_funcs = salt.master.AESFuncs(self.opts) self.present = {} self.presence_events = False - if self.opts.get('presence_events', False): + if self.opts.get("presence_events", False): tcp_only = True for transport, _ in iter_transport_opts(self.opts): - if transport != 'tcp': + if transport != "tcp": tcp_only = False if tcp_only: # Only when the transport is TCP only, the presence events will @@ -1296,9 +1415,7 @@ class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): if self.presence_events: self.event = salt.utils.event.get_event( - 'master', - opts=self.opts, - listen=False + "master", opts=self.opts, listen=False ) def close(self): @@ -1309,6 +1426,7 @@ class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def _add_client_present(self, client): @@ -1319,16 +1437,13 @@ class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): else: self.present[id_] = {client} if self.presence_events: - data = {'new': [id_], - 'lost': []} + data = {"new": [id_], "lost": []} self.event.fire_event( - data, - salt.utils.event.tagify('change', 'presence') + data, salt.utils.event.tagify("change", "presence") ) - data = {'present': list(self.present.keys())} + data = {"present": list(self.present.keys())} self.event.fire_event( - data, - salt.utils.event.tagify('present', 'presence') + data, salt.utils.event.tagify("present", "presence") ) def _remove_client_present(self, client): @@ -1350,16 +1465,13 @@ class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): if len(clients) == 0: del self.present[id_] if self.presence_events: - data = {'new': [], - 'lost': [id_]} + data = {"new": [], "lost": [id_]} self.event.fire_event( - data, - salt.utils.event.tagify('change', 'presence') + data, salt.utils.event.tagify("change", "presence") ) - data = {'present': list(self.present.keys())} + data = {"present": list(self.present.keys())} self.event.fire_event( - data, - salt.utils.event.tagify('present', 'presence') + data, salt.utils.event.tagify("present", "presence") ) @salt.ext.tornado.gen.coroutine @@ -1375,30 +1487,34 @@ class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): framed_msg = salt.transport.frame.decode_embedded_strs( framed_msg ) - body = framed_msg['body'] - if body['enc'] != 'aes': + body = framed_msg["body"] + if body["enc"] != "aes": # We only accept 'aes' encoded messages for 'id' continue - crypticle = salt.crypt.Crypticle(self.opts, salt.master.SMaster.secrets['aes']['secret'].value) - load = crypticle.loads(body['load']) + crypticle = salt.crypt.Crypticle( + self.opts, salt.master.SMaster.secrets["aes"]["secret"].value + ) + load = crypticle.loads(body["load"]) if six.PY3: load = salt.transport.frame.decode_embedded_strs(load) - if not self.aes_funcs.verify_minion(load['id'], load['tok']): + if not self.aes_funcs.verify_minion(load["id"], load["tok"]): continue - client.id_ = load['id'] + client.id_ = load["id"] self._add_client_present(client) except salt.ext.tornado.iostream.StreamClosedError as e: - log.debug('tcp stream to %s closed, unable to recv', client.address) + log.debug("tcp stream to %s closed, unable to recv", client.address) client.close() self._remove_client_present(client) self.clients.discard(client) break except Exception as e: # pylint: disable=broad-except - log.error('Exception parsing response from %s', client.address, exc_info=True) + log.error( + "Exception parsing response from %s", client.address, exc_info=True + ) continue def handle_stream(self, stream, address): - log.trace('Subscriber at %s connected', address) + log.trace("Subscriber at %s connected", address) client = Subscriber(stream, address) self.clients.add(client) self.io_loop.spawn_callback(self._stream_read, client) @@ -1406,12 +1522,12 @@ class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): # TODO: ACK the publish through IPC @salt.ext.tornado.gen.coroutine def publish_payload(self, package, _): - log.debug('TCP PubServer sending payload: %s', package) - payload = salt.transport.frame.frame_msg(package['payload']) + log.debug("TCP PubServer sending payload: %s", package) + payload = salt.transport.frame.frame_msg(package["payload"]) to_remove = [] - if 'topic_lst' in package: - topic_lst = package['topic_lst'] + if "topic_lst" in package: + topic_lst = package["topic_lst"] for topic in topic_lst: if topic in self.present: # This will rarely be a list of more than 1 item. It will @@ -1427,7 +1543,7 @@ class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): except salt.ext.tornado.iostream.StreamClosedError: to_remove.append(client) else: - log.debug('Publish target %s not connected', topic) + log.debug("Publish target %s not connected", topic) else: for client in self.clients: try: @@ -1437,11 +1553,13 @@ class PubServer(salt.ext.tornado.tcpserver.TCPServer, object): except salt.ext.tornado.iostream.StreamClosedError: to_remove.append(client) for client in to_remove: - log.debug('Subscriber at %s has disconnected from publisher', client.address) + log.debug( + "Subscriber at %s has disconnected from publisher", client.address + ) client.close() self._remove_client_present(client) self.clients.discard(client) - log.trace('TCP PubServer finished publishing payload') + log.trace("TCP PubServer finished publishing payload") class TCPPubServerChannel(salt.transport.server.PubServerChannel): @@ -1456,23 +1574,22 @@ class TCPPubServerChannel(salt.transport.server.PubServerChannel): self.io_loop = None def __setstate__(self, state): - salt.master.SMaster.secrets = state['secrets'] - self.__init__(state['opts']) + salt.master.SMaster.secrets = state["secrets"] + self.__init__(state["opts"]) def __getstate__(self): - return {'opts': self.opts, - 'secrets': salt.master.SMaster.secrets} + return {"opts": self.opts, "secrets": salt.master.SMaster.secrets} def _publish_daemon(self, **kwargs): - ''' + """ Bind to the interface specified in the configuration file - ''' + """ salt.utils.process.appendproctitle(self.__class__.__name__) - log_queue = kwargs.get('log_queue') + log_queue = kwargs.get("log_queue") if log_queue is not None: salt.log.setup.set_multiprocessing_logging_queue(log_queue) - log_queue_level = kwargs.get('log_queue_level') + log_queue_level = kwargs.get("log_queue_level") if log_queue_level is not None: salt.log.setup.set_multiprocessing_logging_level(log_queue_level) salt.log.setup.setup_multiprocessing_logging(log_queue) @@ -1487,25 +1604,23 @@ class TCPPubServerChannel(salt.transport.server.PubServerChannel): sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) _set_tcp_keepalive(sock, self.opts) sock.setblocking(0) - sock.bind((self.opts['interface'], int(self.opts['publish_port']))) + sock.bind((self.opts["interface"], int(self.opts["publish_port"]))) sock.listen(self.backlog) # pub_server will take ownership of the socket pub_server.add_socket(sock) # Set up Salt IPC server - if self.opts.get('ipc_mode', '') == 'tcp': - pull_uri = int(self.opts.get('tcp_master_publish_pull', 4514)) + if self.opts.get("ipc_mode", "") == "tcp": + pull_uri = int(self.opts.get("tcp_master_publish_pull", 4514)) else: - pull_uri = os.path.join(self.opts['sock_dir'], 'publish_pull.ipc') + pull_uri = os.path.join(self.opts["sock_dir"], "publish_pull.ipc") pull_sock = salt.transport.ipc.IPCMessageServer( - pull_uri, - io_loop=self.io_loop, - payload_handler=pub_server.publish_payload, + pull_uri, io_loop=self.io_loop, payload_handler=pub_server.publish_payload, ) # Securely create socket - log.info('Starting the Salt Puller on %s', pull_uri) + log.info("Starting the Salt Puller on %s", pull_uri) with salt.utils.files.set_umask(0o177): pull_sock.start() @@ -1516,52 +1631,54 @@ class TCPPubServerChannel(salt.transport.server.PubServerChannel): salt.log.setup.shutdown_multiprocessing_logging() def pre_fork(self, process_manager, kwargs=None): - ''' + """ Do anything necessary pre-fork. Since this is on the master side this will primarily be used to create IPC channels and create our daemon process to do the actual publishing - ''' + """ process_manager.add_process(self._publish_daemon, kwargs=kwargs) def publish(self, load): - ''' + """ Publish "load" to minions - ''' - payload = {'enc': 'aes'} + """ + payload = {"enc": "aes"} - crypticle = salt.crypt.Crypticle(self.opts, salt.master.SMaster.secrets['aes']['secret'].value) - payload['load'] = crypticle.dumps(load) - if self.opts['sign_pub_messages']: - master_pem_path = os.path.join(self.opts['pki_dir'], 'master.pem') + crypticle = salt.crypt.Crypticle( + self.opts, salt.master.SMaster.secrets["aes"]["secret"].value + ) + payload["load"] = crypticle.dumps(load) + if self.opts["sign_pub_messages"]: + master_pem_path = os.path.join(self.opts["pki_dir"], "master.pem") log.debug("Signing data packet") - payload['sig'] = salt.crypt.sign_message(master_pem_path, payload['load']) + payload["sig"] = salt.crypt.sign_message(master_pem_path, payload["load"]) # Use the Salt IPC server - if self.opts.get('ipc_mode', '') == 'tcp': - pull_uri = int(self.opts.get('tcp_master_publish_pull', 4514)) + if self.opts.get("ipc_mode", "") == "tcp": + pull_uri = int(self.opts.get("tcp_master_publish_pull", 4514)) else: - pull_uri = os.path.join(self.opts['sock_dir'], 'publish_pull.ipc') + pull_uri = os.path.join(self.opts["sock_dir"], "publish_pull.ipc") # TODO: switch to the actual asynchronous interface - #pub_sock = salt.transport.ipc.IPCMessageClient(self.opts, io_loop=self.io_loop) + # pub_sock = salt.transport.ipc.IPCMessageClient(self.opts, io_loop=self.io_loop) pub_sock = salt.utils.asynchronous.SyncWrapper( - salt.transport.ipc.IPCMessageClient, - (pull_uri,) + salt.transport.ipc.IPCMessageClient, (pull_uri,) ) pub_sock.connect() - int_payload = {'payload': self.serial.dumps(payload)} + int_payload = {"payload": self.serial.dumps(payload)} # add some targeting stuff for lists only (for now) - if load['tgt_type'] == 'list' and not self.opts.get("order_masters", False): - if isinstance(load['tgt'], six.string_types): + if load["tgt_type"] == "list" and not self.opts.get("order_masters", False): + if isinstance(load["tgt"], six.string_types): # Fetch a list of minions that match - _res = self.ckminions.check_minions(load['tgt'], - tgt_type=load['tgt_type']) - match_ids = _res['minions'] + _res = self.ckminions.check_minions( + load["tgt"], tgt_type=load["tgt_type"] + ) + match_ids = _res["minions"] log.debug("Publish Side Match: %s", match_ids) # Send list of miions thru so zmq can target them - int_payload['topic_lst'] = match_ids + int_payload["topic_lst"] = match_ids else: - int_payload['topic_lst'] = load['tgt'] + int_payload["topic_lst"] = load["tgt"] # Send it over IPC! pub_sock.send(int_payload) diff --git a/salt/transport/zeromq.py b/salt/transport/zeromq.py index e03aeaa65c7..092b91db3ad 100644 --- a/salt/transport/zeromq.py +++ b/salt/transport/zeromq.py @@ -1,62 +1,71 @@ # -*- coding: utf-8 -*- -''' +""" Zeromq transport classes -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys + import copy import errno -import signal -import socket import hashlib import logging -import weakref +import os +import signal +import socket +import sys import threading +import weakref from random import randint # Import Salt Libs import salt.auth import salt.crypt + +# Import Tornado Libs +import salt.ext.tornado +import salt.ext.tornado.concurrent +import salt.ext.tornado.gen +import salt.ext.tornado.ioloop import salt.log.setup +import salt.payload +import salt.transport.client +import salt.transport.mixins.auth +import salt.transport.server import salt.utils.event import salt.utils.files import salt.utils.minions import salt.utils.process import salt.utils.stringutils import salt.utils.verify -import salt.utils.zeromq import salt.utils.versions -import salt.payload -import salt.transport.client -import salt.transport.server -import salt.transport.mixins.auth -from salt.ext import six -from salt.exceptions import SaltReqTimeoutError, SaltException -from salt._compat import ipaddress - -from salt.utils.zeromq import zmq, ZMQDefaultLoop, install_zmq, ZMQ_VERSION_INFO, LIBZMQ_VERSION_INFO +import salt.utils.zeromq import zmq.error import zmq.eventloop.ioloop import zmq.eventloop.zmqstream +from salt._compat import ipaddress +from salt.exceptions import SaltException, SaltReqTimeoutError +from salt.ext import six +from salt.utils.zeromq import ( + LIBZMQ_VERSION_INFO, + ZMQ_VERSION_INFO, + ZMQDefaultLoop, + install_zmq, + zmq, +) try: import zmq.utils.monitor + HAS_ZMQ_MONITOR = True except ImportError: HAS_ZMQ_MONITOR = False -# Import Tornado Libs -import salt.ext.tornado -import salt.ext.tornado.ioloop -import salt.ext.tornado.gen -import salt.ext.tornado.concurrent # Import third party libs try: from M2Crypto import RSA + HAS_M2 = True except ImportError: HAS_M2 = False @@ -68,64 +77,79 @@ except ImportError: log = logging.getLogger(__name__) -def _get_master_uri(master_ip, - master_port, - source_ip=None, - source_port=None): - ''' +def _get_master_uri(master_ip, master_port, source_ip=None, source_port=None): + """ Return the ZeroMQ URI to connect the Minion to the Master. It supports different source IP / port, given the ZeroMQ syntax: // Connecting using a IP address and bind to an IP address rc = zmq_connect(socket, "tcp://192.168.1.17:5555;192.168.1.1:5555"); assert (rc == 0); Source: http://api.zeromq.org/4-1:zmq-tcp - ''' + """ from salt.utils.zeromq import ip_bracket - master_uri = 'tcp://{master_ip}:{master_port}'.format( - master_ip=ip_bracket(master_ip), master_port=master_port) + master_uri = "tcp://{master_ip}:{master_port}".format( + master_ip=ip_bracket(master_ip), master_port=master_port + ) if source_ip or source_port: if LIBZMQ_VERSION_INFO >= (4, 1, 6) and ZMQ_VERSION_INFO >= (16, 0, 1): # The source:port syntax for ZeroMQ has been added in libzmq 4.1.6 # which is included in the pyzmq wheels starting with 16.0.1. if source_ip and source_port: - master_uri = 'tcp://{source_ip}:{source_port};{master_ip}:{master_port}'.format( - source_ip=ip_bracket(source_ip), source_port=source_port, - master_ip=ip_bracket(master_ip), master_port=master_port) + master_uri = "tcp://{source_ip}:{source_port};{master_ip}:{master_port}".format( + source_ip=ip_bracket(source_ip), + source_port=source_port, + master_ip=ip_bracket(master_ip), + master_port=master_port, + ) elif source_ip and not source_port: - master_uri = 'tcp://{source_ip}:0;{master_ip}:{master_port}'.format( - source_ip=ip_bracket(source_ip), - master_ip=ip_bracket(master_ip), master_port=master_port) + master_uri = "tcp://{source_ip}:0;{master_ip}:{master_port}".format( + source_ip=ip_bracket(source_ip), + master_ip=ip_bracket(master_ip), + master_port=master_port, + ) elif source_port and not source_ip: - ip_any = '0.0.0.0' if ipaddress.ip_address(master_ip).version == 4 else ip_bracket('::') - master_uri = 'tcp://{ip_any}:{source_port};{master_ip}:{master_port}'.format( - ip_any=ip_any, source_port=source_port, - master_ip=ip_bracket(master_ip), master_port=master_port) + ip_any = ( + "0.0.0.0" + if ipaddress.ip_address(master_ip).version == 4 + else ip_bracket("::") + ) + master_uri = "tcp://{ip_any}:{source_port};{master_ip}:{master_port}".format( + ip_any=ip_any, + source_port=source_port, + master_ip=ip_bracket(master_ip), + master_port=master_port, + ) else: - log.warning('Unable to connect to the Master using a specific source IP / port') - log.warning('Consider upgrading to pyzmq >= 16.0.1 and libzmq >= 4.1.6') - log.warning('Specific source IP / port for connecting to master returner port: configuraion ignored') + log.warning( + "Unable to connect to the Master using a specific source IP / port" + ) + log.warning("Consider upgrading to pyzmq >= 16.0.1 and libzmq >= 4.1.6") + log.warning( + "Specific source IP / port for connecting to master returner port: configuraion ignored" + ) return master_uri class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): - ''' + """ Encapsulate sending routines to ZeroMQ. ZMQ Channels default to 'crypt=aes' - ''' + """ + # This class is only a singleton per minion/master pair # mapping of io_loop -> {key -> channel} instance_map = weakref.WeakKeyDictionary() def __new__(cls, opts, **kwargs): - ''' + """ Only create one instance of channel per __key() - ''' + """ # do we have any mapping for this io_loop - io_loop = kwargs.get('io_loop') + io_loop = kwargs.get("io_loop") if io_loop is None: install_zmq() io_loop = ZMQDefaultLoop.current() @@ -136,7 +160,7 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): key = cls.__key(opts, **kwargs) obj = loop_instance_map.get(key) if obj is None: - log.debug('Initializing new AsyncZeroMQReqChannel for %s', key) + log.debug("Initializing new AsyncZeroMQReqChannel for %s", key) # we need to make a local variable for this, as we are going to store # it in a WeakValueDictionary-- which will remove the item if no one # references it-- this forces a reference while we return to the caller @@ -146,31 +170,42 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): loop_instance_map[key] = obj obj._refcount = 1 obj._refcount_lock = threading.RLock() - log.trace('Inserted key into loop_instance_map id %s for key %s and process %s', - id(loop_instance_map), key, os.getpid()) + log.trace( + "Inserted key into loop_instance_map id %s for key %s and process %s", + id(loop_instance_map), + key, + os.getpid(), + ) else: with obj._refcount_lock: obj._refcount += 1 - log.debug('Re-using AsyncZeroMQReqChannel for %s', key) + log.debug("Re-using AsyncZeroMQReqChannel for %s", key) return obj def __deepcopy__(self, memo): cls = self.__class__ - result = cls.__new__(cls, copy.deepcopy(self.opts, memo)) # pylint: disable=too-many-function-args + # pylint: disable=too-many-function-args + result = cls.__new__(cls, copy.deepcopy(self.opts, memo)) + # pylint: enable=too-many-function-args memo[id(self)] = result for key in self.__dict__: - if key in ('_io_loop', '_refcount', '_refcount_lock'): + if key in ("_io_loop", "_refcount", "_refcount_lock"): continue # The _io_loop has a thread Lock which will fail to be deep # copied. Skip it because it will just be recreated on the # new copy. - if key == 'message_client': + if key == "message_client": # Recreate the message client because it will fail to be deep # copied. The reason is the same as the io_loop skip above. - setattr(result, key, - AsyncReqMessageClientPool(result.opts, - args=(result.opts, self.master_uri,), - kwargs={'io_loop': self._io_loop})) + setattr( + result, + key, + AsyncReqMessageClientPool( + result.opts, + args=(result.opts, self.master_uri,), + kwargs={"io_loop": self._io_loop}, + ), + ) continue setattr(result, key, copy.deepcopy(self.__dict__[key], memo)) @@ -178,11 +213,12 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): @classmethod def __key(cls, opts, **kwargs): - return (opts['pki_dir'], # where the keys are stored - opts['id'], # minion ID - kwargs.get('master_uri', opts.get('master_uri')), # master ID - kwargs.get('crypt', 'aes'), # TODO: use the same channel for crypt - ) + return ( + opts["pki_dir"], # where the keys are stored + opts["id"], # minion ID + kwargs.get("master_uri", opts.get("master_uri")), # master ID + kwargs.get("crypt", "aes"), # TODO: use the same channel for crypt + ) # has to remain empty for singletons, since __init__ will *always* be called def __init__(self, opts, **kwargs): @@ -191,33 +227,38 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): # an init for the singleton instance to call def __singleton_init__(self, opts, **kwargs): self.opts = dict(opts) - self.ttype = 'zeromq' + self.ttype = "zeromq" # crypt defaults to 'aes' - self.crypt = kwargs.get('crypt', 'aes') + self.crypt = kwargs.get("crypt", "aes") - if 'master_uri' in kwargs: - self.opts['master_uri'] = kwargs['master_uri'] + if "master_uri" in kwargs: + self.opts["master_uri"] = kwargs["master_uri"] - self._io_loop = kwargs.get('io_loop') + self._io_loop = kwargs.get("io_loop") if self._io_loop is None: install_zmq() self._io_loop = ZMQDefaultLoop.current() - if self.crypt != 'clear': + if self.crypt != "clear": # we don't need to worry about auth as a kwarg, since its a singleton self.auth = salt.crypt.AsyncAuth(self.opts, io_loop=self._io_loop) - log.debug('Connecting the Minion to the Master URI (for the return server): %s', self.master_uri) - self.message_client = AsyncReqMessageClientPool(self.opts, - args=(self.opts, self.master_uri,), - kwargs={'io_loop': self._io_loop}) + log.debug( + "Connecting the Minion to the Master URI (for the return server): %s", + self.master_uri, + ) + self.message_client = AsyncReqMessageClientPool( + self.opts, + args=(self.opts, self.master_uri,), + kwargs={"io_loop": self._io_loop}, + ) self._closing = False def close(self): - ''' + """ Since the message_client creates sockets and assigns them to the IOLoop we have to specifically destroy them, since we aren't the only ones with references to the FDs - ''' + """ if self._closing: return @@ -226,14 +267,14 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): with self._refcount_lock: self._refcount -= 1 log.debug( - 'This is not the last %s instance. Not closing yet.', - self.__class__.__name__ + "This is not the last %s instance. Not closing yet.", + self.__class__.__name__, ) return - log.debug('Closing %s instance', self.__class__.__name__) + log.debug("Closing %s instance", self.__class__.__name__) self._closing = True - if hasattr(self, 'message_client'): + if hasattr(self, "message_client"): self.message_client.close() # Remove the entry from the instance map so that a closed entry may not @@ -259,31 +300,36 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): if exc.errno != errno.EBADF: # If its not a bad file descriptor error, raise raise + # pylint: enable=W1701 @property def master_uri(self): - if 'master_uri' in self.opts: - return self.opts['master_uri'] + if "master_uri" in self.opts: + return self.opts["master_uri"] # if by chance master_uri is not there.. - if 'master_ip' in self.opts: - return _get_master_uri(self.opts['master_ip'], - self.opts['master_port'], - source_ip=self.opts.get('source_ip'), - source_port=self.opts.get('source_ret_port')) + if "master_ip" in self.opts: + return _get_master_uri( + self.opts["master_ip"], + self.opts["master_port"], + source_ip=self.opts.get("source_ip"), + source_port=self.opts.get("source_ret_port"), + ) # if we've reached here something is very abnormal - raise SaltException('ReqChannel: missing master_uri/master_ip in self.opts') + raise SaltException("ReqChannel: missing master_uri/master_ip in self.opts") def _package_load(self, load): return { - 'enc': self.crypt, - 'load': load, + "enc": self.crypt, + "load": load, } @salt.ext.tornado.gen.coroutine - def crypted_transfer_decode_dictentry(self, load, dictkey=None, tries=3, timeout=60): + def crypted_transfer_decode_dictentry( + self, load, dictkey=None, tries=3, timeout=60 + ): if not self.auth.authenticated: # Return control back to the caller, continue when authentication succeeds yield self.auth.authenticate() @@ -294,7 +340,7 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): tries=tries, ) key = self.auth.get_keys() - if 'key' not in ret: + if "key" not in ret: # Reauth in the case our key is deleted on the master side. yield self.auth.authenticate() ret = yield self.message_client.send( @@ -303,11 +349,10 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): tries=tries, ) if HAS_M2: - aes = key.private_decrypt(ret['key'], - RSA.pkcs1_oaep_padding) + aes = key.private_decrypt(ret["key"], RSA.pkcs1_oaep_padding) else: cipher = PKCS1_OAEP.new(key) - aes = cipher.decrypt(ret['key']) + aes = cipher.decrypt(ret["key"]) pcrypt = salt.crypt.Crypticle(self.opts, aes) data = pcrypt.loads(ret[dictkey]) if six.PY3: @@ -316,7 +361,7 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): @salt.ext.tornado.gen.coroutine def _crypted_transfer(self, load, tries=3, timeout=60, raw=False): - ''' + """ Send a load across the wire, with encryption In case of authentication errors, try to renegotiate authentication @@ -328,7 +373,8 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): :param dict load: A load to send across the wire :param int tries: The number of times to make before failure :param int timeout: The number of seconds on a response before failing - ''' + """ + @salt.ext.tornado.gen.coroutine def _do_transfer(): # Yield control to the caller. When send() completes, resume by populating data with the Future.result @@ -346,6 +392,7 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): if six.PY3 and not raw: data = salt.transport.frame.decode_embedded_strs(data) raise salt.ext.tornado.gen.Return(data) + if not self.auth.authenticated: # Return control back to the caller, resume when authentication succeeds yield self.auth.authenticate() @@ -360,149 +407,154 @@ class AsyncZeroMQReqChannel(salt.transport.client.ReqChannel): @salt.ext.tornado.gen.coroutine def _uncrypted_transfer(self, load, tries=3, timeout=60): - ''' + """ Send a load across the wire in cleartext :param dict load: A load to send across the wire :param int tries: The number of times to make before failure :param int timeout: The number of seconds on a response before failing - ''' + """ ret = yield self.message_client.send( - self._package_load(load), - timeout=timeout, - tries=tries, + self._package_load(load), timeout=timeout, tries=tries, ) raise salt.ext.tornado.gen.Return(ret) @salt.ext.tornado.gen.coroutine def send(self, load, tries=3, timeout=60, raw=False): - ''' + """ Send a request, return a future which will complete when we send the message - ''' - if self.crypt == 'clear': + """ + if self.crypt == "clear": ret = yield self._uncrypted_transfer(load, tries=tries, timeout=timeout) else: - ret = yield self._crypted_transfer(load, tries=tries, timeout=timeout, raw=raw) + ret = yield self._crypted_transfer( + load, tries=tries, timeout=timeout, raw=raw + ) raise salt.ext.tornado.gen.Return(ret) -class AsyncZeroMQPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.transport.client.AsyncPubChannel): - ''' +class AsyncZeroMQPubChannel( + salt.transport.mixins.auth.AESPubClientMixin, salt.transport.client.AsyncPubChannel +): + """ A transport channel backed by ZeroMQ for a Salt Publisher to use to publish commands to connected minions - ''' - def __init__(self, - opts, - **kwargs): + """ + + def __init__(self, opts, **kwargs): self.opts = opts - self.ttype = 'zeromq' - self.io_loop = kwargs.get('io_loop') + self.ttype = "zeromq" + self.io_loop = kwargs.get("io_loop") if self.io_loop is None: install_zmq() self.io_loop = ZMQDefaultLoop.current() - self.hexid = hashlib.sha1(salt.utils.stringutils.to_bytes(self.opts['id'])).hexdigest() + self.hexid = hashlib.sha1( + salt.utils.stringutils.to_bytes(self.opts["id"]) + ).hexdigest() self.auth = salt.crypt.AsyncAuth(self.opts, io_loop=self.io_loop) self.serial = salt.payload.Serial(self.opts) self.context = zmq.Context() self._socket = self.context.socket(zmq.SUB) - if self.opts['zmq_filtering']: + if self.opts["zmq_filtering"]: # TODO: constants file for "broadcast" - self._socket.setsockopt(zmq.SUBSCRIBE, b'broadcast') - if self.opts.get('__role') == 'syndic': - self._socket.setsockopt(zmq.SUBSCRIBE, b'syndic') + self._socket.setsockopt(zmq.SUBSCRIBE, b"broadcast") + if self.opts.get("__role") == "syndic": + self._socket.setsockopt(zmq.SUBSCRIBE, b"syndic") else: self._socket.setsockopt( - zmq.SUBSCRIBE, - salt.utils.stringutils.to_bytes(self.hexid) + zmq.SUBSCRIBE, salt.utils.stringutils.to_bytes(self.hexid) ) else: - self._socket.setsockopt(zmq.SUBSCRIBE, b'') + self._socket.setsockopt(zmq.SUBSCRIBE, b"") - self._socket.setsockopt(zmq.IDENTITY, salt.utils.stringutils.to_bytes(self.opts['id'])) + self._socket.setsockopt( + zmq.IDENTITY, salt.utils.stringutils.to_bytes(self.opts["id"]) + ) # TODO: cleanup all the socket opts stuff - if hasattr(zmq, 'TCP_KEEPALIVE'): + if hasattr(zmq, "TCP_KEEPALIVE"): + self._socket.setsockopt(zmq.TCP_KEEPALIVE, self.opts["tcp_keepalive"]) self._socket.setsockopt( - zmq.TCP_KEEPALIVE, self.opts['tcp_keepalive'] + zmq.TCP_KEEPALIVE_IDLE, self.opts["tcp_keepalive_idle"] ) self._socket.setsockopt( - zmq.TCP_KEEPALIVE_IDLE, self.opts['tcp_keepalive_idle'] + zmq.TCP_KEEPALIVE_CNT, self.opts["tcp_keepalive_cnt"] ) self._socket.setsockopt( - zmq.TCP_KEEPALIVE_CNT, self.opts['tcp_keepalive_cnt'] - ) - self._socket.setsockopt( - zmq.TCP_KEEPALIVE_INTVL, self.opts['tcp_keepalive_intvl'] + zmq.TCP_KEEPALIVE_INTVL, self.opts["tcp_keepalive_intvl"] ) - recon_delay = self.opts['recon_default'] + recon_delay = self.opts["recon_default"] - if self.opts['recon_randomize']: - recon_delay = randint(self.opts['recon_default'], - self.opts['recon_default'] + self.opts['recon_max']) + if self.opts["recon_randomize"]: + recon_delay = randint( + self.opts["recon_default"], + self.opts["recon_default"] + self.opts["recon_max"], + ) log.debug( "Generated random reconnect delay between '%sms' and '%sms' (%s)", - self.opts['recon_default'], - self.opts['recon_default'] + self.opts['recon_max'], - recon_delay + self.opts["recon_default"], + self.opts["recon_default"] + self.opts["recon_max"], + recon_delay, ) log.debug("Setting zmq_reconnect_ivl to '%sms'", recon_delay) self._socket.setsockopt(zmq.RECONNECT_IVL, recon_delay) - if hasattr(zmq, 'RECONNECT_IVL_MAX'): + if hasattr(zmq, "RECONNECT_IVL_MAX"): log.debug( "Setting zmq_reconnect_ivl_max to '%sms'", - self.opts['recon_default'] + self.opts['recon_max'] + self.opts["recon_default"] + self.opts["recon_max"], ) - self._socket.setsockopt( - zmq.RECONNECT_IVL_MAX, self.opts['recon_max'] - ) + self._socket.setsockopt(zmq.RECONNECT_IVL_MAX, self.opts["recon_max"]) - if (self.opts['ipv6'] is True or ':' in self.opts['master_ip']) and hasattr(zmq, 'IPV4ONLY'): + if (self.opts["ipv6"] is True or ":" in self.opts["master_ip"]) and hasattr( + zmq, "IPV4ONLY" + ): # IPv6 sockets work for both IPv6 and IPv4 addresses self._socket.setsockopt(zmq.IPV4ONLY, 0) - if HAS_ZMQ_MONITOR and self.opts['zmq_monitor']: + if HAS_ZMQ_MONITOR and self.opts["zmq_monitor"]: self._monitor = ZeroMQSocketMonitor(self._socket) self._monitor.start_io_loop(self.io_loop) def close(self): - if hasattr(self, '_monitor') and self._monitor is not None: + if hasattr(self, "_monitor") and self._monitor is not None: self._monitor.stop() self._monitor = None - if hasattr(self, '_stream'): + if hasattr(self, "_stream"): if ZMQ_VERSION_INFO < (14, 3, 0): # stream.close() doesn't work properly on pyzmq < 14.3.0 self._stream.io_loop.remove_handler(self._stream.socket) self._stream.socket.close(0) else: self._stream.close(0) - elif hasattr(self, '_socket'): + elif hasattr(self, "_socket"): self._socket.close(0) - if hasattr(self, 'context') and self.context.closed is False: + if hasattr(self, "context") and self.context.closed is False: self.context.term() def destroy(self): # Bacwards compat salt.utils.versions.warn_until( - 'Sodium', - 'Calling {0}.destroy() is deprecated. Please call {0}.close() instead.'.format( + "Sodium", + "Calling {0}.destroy() is deprecated. Please call {0}.close() instead.".format( self.__class__.__name__ ), - stacklevel=3 + stacklevel=3, ) self.close() # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 # TODO: this is the time to see if we are connected, maybe use the req channel to guess? @@ -512,32 +564,37 @@ class AsyncZeroMQPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.t yield self.auth.authenticate() # if this is changed from the default, we assume it was intentional - if int(self.opts.get('publish_port', 4506)) != 4506: - self.publish_port = self.opts.get('publish_port') + if int(self.opts.get("publish_port", 4506)) != 4506: + self.publish_port = self.opts.get("publish_port") # else take the relayed publish_port master reports else: - self.publish_port = self.auth.creds['publish_port'] + self.publish_port = self.auth.creds["publish_port"] - log.debug('Connecting the Minion to the Master publish port, using the URI: %s', self.master_pub) + log.debug( + "Connecting the Minion to the Master publish port, using the URI: %s", + self.master_pub, + ) self._socket.connect(self.master_pub) @property def master_pub(self): - ''' + """ Return the master publish port - ''' - return _get_master_uri(self.opts['master_ip'], - self.publish_port, - source_ip=self.opts.get('source_ip'), - source_port=self.opts.get('source_publish_port')) + """ + return _get_master_uri( + self.opts["master_ip"], + self.publish_port, + source_ip=self.opts.get("source_ip"), + source_port=self.opts.get("source_publish_port"), + ) @salt.ext.tornado.gen.coroutine def _decode_messages(self, messages): - ''' + """ Take the zmq messages, decrypt/decode them into a payload :param list messages: A list of messages to be decoded - ''' + """ messages_len = len(messages) # if it was one message, then its old style if messages_len == 1: @@ -545,14 +602,23 @@ class AsyncZeroMQPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.t # 2 includes a header which says who should do it elif messages_len == 2: message_target = salt.utils.stringutils.to_str(messages[0]) - if (self.opts.get('__role') != 'syndic' and message_target not in ('broadcast', self.hexid)) or \ - (self.opts.get('__role') == 'syndic' and message_target not in ('broadcast', 'syndic')): - log.debug('Publish received for not this minion: %s', message_target) + if ( + self.opts.get("__role") != "syndic" + and message_target not in ("broadcast", self.hexid) + ) or ( + self.opts.get("__role") == "syndic" + and message_target not in ("broadcast", "syndic") + ): + log.debug("Publish received for not this minion: %s", message_target) raise salt.ext.tornado.gen.Return(None) payload = self.serial.loads(messages[1]) else: - raise Exception(('Invalid number of messages ({0}) in zeromq pub' - 'message from master').format(len(messages_len))) + raise Exception( + ( + "Invalid number of messages ({0}) in zeromq pub" + "message from master" + ).format(len(messages_len)) + ) # Yield control back to the caller. When the payload has been decoded, assign # the decoded payload to 'ret' and resume operation ret = yield self._decode_payload(payload) @@ -560,19 +626,21 @@ class AsyncZeroMQPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.t @property def stream(self): - ''' + """ Return the current zmqstream, creating one if necessary - ''' - if not hasattr(self, '_stream'): - self._stream = zmq.eventloop.zmqstream.ZMQStream(self._socket, io_loop=self.io_loop) + """ + if not hasattr(self, "_stream"): + self._stream = zmq.eventloop.zmqstream.ZMQStream( + self._socket, io_loop=self.io_loop + ) return self._stream def on_recv(self, callback): - ''' + """ Register a callback for received messages (that we didn't initiate) :param func callback: A function which should be called when data is received - ''' + """ if callback is None: return self.stream.on_recv(None) @@ -581,43 +649,44 @@ class AsyncZeroMQPubChannel(salt.transport.mixins.auth.AESPubClientMixin, salt.t payload = yield self._decode_messages(messages) if payload is not None: callback(payload) + return self.stream.on_recv(wrap_callback) -class ZeroMQReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, - salt.transport.server.ReqServerChannel): - +class ZeroMQReqServerChannel( + salt.transport.mixins.auth.AESReqServerMixin, salt.transport.server.ReqServerChannel +): def __init__(self, opts): salt.transport.server.ReqServerChannel.__init__(self, opts) self._closing = False def zmq_device(self): - ''' + """ Multiprocessing target for the zmq queue device - ''' + """ self.__setup_signals() - salt.utils.process.appendproctitle('MWorkerQueue') - self.context = zmq.Context(self.opts['worker_threads']) + salt.utils.process.appendproctitle("MWorkerQueue") + self.context = zmq.Context(self.opts["worker_threads"]) # Prepare the zeromq sockets - self.uri = 'tcp://{interface}:{ret_port}'.format(**self.opts) + self.uri = "tcp://{interface}:{ret_port}".format(**self.opts) self.clients = self.context.socket(zmq.ROUTER) - if self.opts['ipv6'] is True and hasattr(zmq, 'IPV4ONLY'): + if self.opts["ipv6"] is True and hasattr(zmq, "IPV4ONLY"): # IPv6 sockets work for both IPv6 and IPv4 addresses self.clients.setsockopt(zmq.IPV4ONLY, 0) - self.clients.setsockopt(zmq.BACKLOG, self.opts.get('zmq_backlog', 1000)) + self.clients.setsockopt(zmq.BACKLOG, self.opts.get("zmq_backlog", 1000)) self._start_zmq_monitor() self.workers = self.context.socket(zmq.DEALER) - if self.opts.get('ipc_mode', '') == 'tcp': - self.w_uri = 'tcp://127.0.0.1:{0}'.format( - self.opts.get('tcp_master_workers', 4515) - ) + if self.opts.get("ipc_mode", "") == "tcp": + self.w_uri = "tcp://127.0.0.1:{0}".format( + self.opts.get("tcp_master_workers", 4515) + ) else: - self.w_uri = 'ipc://{0}'.format( - os.path.join(self.opts['sock_dir'], 'workers.ipc') - ) + self.w_uri = "ipc://{0}".format( + os.path.join(self.opts["sock_dir"], "workers.ipc") + ) - log.info('Setting up the master communication server') + log.info("Setting up the master communication server") self.clients.bind(self.uri) self.workers.bind(self.w_uri) @@ -634,65 +703,66 @@ class ZeroMQReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, break def close(self): - ''' + """ Cleanly shutdown the router socket - ''' + """ if self._closing: return - log.info('MWorkerQueue under PID %s is closing', os.getpid()) + log.info("MWorkerQueue under PID %s is closing", os.getpid()) self._closing = True # pylint: disable=E0203 - if getattr(self, '_monitor', None) is not None: + if getattr(self, "_monitor", None) is not None: self._monitor.stop() self._monitor = None - if getattr(self, '_w_monitor', None) is not None: + if getattr(self, "_w_monitor", None) is not None: self._w_monitor.stop() self._w_monitor = None - if hasattr(self, 'clients') and self.clients.closed is False: + if hasattr(self, "clients") and self.clients.closed is False: self.clients.close() - if hasattr(self, 'workers') and self.workers.closed is False: + if hasattr(self, "workers") and self.workers.closed is False: self.workers.close() - if hasattr(self, 'stream'): + if hasattr(self, "stream"): self.stream.close() - if hasattr(self, '_socket') and self._socket.closed is False: + if hasattr(self, "_socket") and self._socket.closed is False: self._socket.close() - if hasattr(self, 'context') and self.context.closed is False: + if hasattr(self, "context") and self.context.closed is False: self.context.term() # pylint: enable=E0203 def pre_fork(self, process_manager): - ''' + """ Pre-fork we need to create the zmq router device :param func process_manager: An instance of salt.utils.process.ProcessManager - ''' + """ salt.transport.mixins.auth.AESReqServerMixin.pre_fork(self, process_manager) process_manager.add_process(self.zmq_device) def _start_zmq_monitor(self): - ''' + """ Starts ZMQ monitor for debugging purposes. :return: - ''' + """ # Socket monitor shall be used the only for debug # purposes so using threading doesn't look too bad here - if HAS_ZMQ_MONITOR and self.opts['zmq_monitor']: - log.debug('Starting ZMQ monitor') + if HAS_ZMQ_MONITOR and self.opts["zmq_monitor"]: + log.debug("Starting ZMQ monitor") import threading + self._w_monitor = ZeroMQSocketMonitor(self._socket) threading.Thread(target=self._w_monitor.start_poll).start() - log.debug('ZMQ monitor has been started started') + log.debug("ZMQ monitor has been started started") def post_fork(self, payload_handler, io_loop): - ''' + """ After forking we need to create all of the local sockets to listen to the router :param func payload_handler: A function to called to handle incoming payloads as they are picked up off the wire :param IOLoop io_loop: An instance of a Tornado IOLoop, to handle event scheduling - ''' + """ self.payload_handler = payload_handler self.io_loop = io_loop @@ -700,70 +770,80 @@ class ZeroMQReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, self._socket = self.context.socket(zmq.REP) self._start_zmq_monitor() - if self.opts.get('ipc_mode', '') == 'tcp': - self.w_uri = 'tcp://127.0.0.1:{0}'.format( - self.opts.get('tcp_master_workers', 4515) - ) + if self.opts.get("ipc_mode", "") == "tcp": + self.w_uri = "tcp://127.0.0.1:{0}".format( + self.opts.get("tcp_master_workers", 4515) + ) else: - self.w_uri = 'ipc://{0}'.format( - os.path.join(self.opts['sock_dir'], 'workers.ipc') - ) - log.info('Worker binding to socket %s', self.w_uri) + self.w_uri = "ipc://{0}".format( + os.path.join(self.opts["sock_dir"], "workers.ipc") + ) + log.info("Worker binding to socket %s", self.w_uri) self._socket.connect(self.w_uri) - salt.transport.mixins.auth.AESReqServerMixin.post_fork(self, payload_handler, io_loop) + salt.transport.mixins.auth.AESReqServerMixin.post_fork( + self, payload_handler, io_loop + ) - self.stream = zmq.eventloop.zmqstream.ZMQStream(self._socket, io_loop=self.io_loop) + self.stream = zmq.eventloop.zmqstream.ZMQStream( + self._socket, io_loop=self.io_loop + ) self.stream.on_recv_stream(self.handle_message) @salt.ext.tornado.gen.coroutine def handle_message(self, stream, payload): - ''' + """ Handle incoming messages from underlying TCP streams :stream ZMQStream stream: A ZeroMQ stream. See http://zeromq.github.io/pyzmq/api/generated/zmq.eventloop.zmqstream.html :param dict payload: A payload to process - ''' + """ try: payload = self.serial.loads(payload[0]) payload = self._decode_payload(payload) except Exception as exc: # pylint: disable=broad-except exc_type = type(exc).__name__ - if exc_type == 'AuthenticationError': + if exc_type == "AuthenticationError": log.debug( - 'Minion failed to auth to master. Since the payload is ' - 'encrypted, it is not known which minion failed to ' - 'authenticate. It is likely that this is a transient ' - 'failure due to the master rotating its public key.' + "Minion failed to auth to master. Since the payload is " + "encrypted, it is not known which minion failed to " + "authenticate. It is likely that this is a transient " + "failure due to the master rotating its public key." ) else: - log.error('Bad load from minion: %s: %s', exc_type, exc) - stream.send(self.serial.dumps('bad load')) + log.error("Bad load from minion: %s: %s", exc_type, exc) + stream.send(self.serial.dumps("bad load")) raise salt.ext.tornado.gen.Return() # TODO helper functions to normalize payload? - if not isinstance(payload, dict) or not isinstance(payload.get('load'), dict): - log.error('payload and load must be a dict. Payload was: %s and load was %s', payload, payload.get('load')) - stream.send(self.serial.dumps('payload and load must be a dict')) + if not isinstance(payload, dict) or not isinstance(payload.get("load"), dict): + log.error( + "payload and load must be a dict. Payload was: %s and load was %s", + payload, + payload.get("load"), + ) + stream.send(self.serial.dumps("payload and load must be a dict")) raise salt.ext.tornado.gen.Return() try: - id_ = payload['load'].get('id', '') - if str('\0') in id_: - log.error('Payload contains an id with a null byte: %s', payload) - stream.send(self.serial.dumps('bad load: id contains a null byte')) + id_ = payload["load"].get("id", "") + if str("\0") in id_: + log.error("Payload contains an id with a null byte: %s", payload) + stream.send(self.serial.dumps("bad load: id contains a null byte")) raise salt.ext.tornado.gen.Return() except TypeError: - log.error('Payload contains non-string id: %s', payload) - stream.send(self.serial.dumps('bad load: id {0} is not a string'.format(id_))) + log.error("Payload contains non-string id: %s", payload) + stream.send( + self.serial.dumps("bad load: id {0} is not a string".format(id_)) + ) raise salt.ext.tornado.gen.Return() # intercept the "_auth" commands, since the main daemon shouldn't know # anything about our key auth - if payload['enc'] == 'clear' and payload.get('load', {}).get('cmd') == '_auth': - stream.send(self.serial.dumps(self._auth(payload['load']))) + if payload["enc"] == "clear" and payload.get("load", {}).get("cmd") == "_auth": + stream.send(self.serial.dumps(self._auth(payload["load"]))) raise salt.ext.tornado.gen.Return() # TODO: test @@ -773,24 +853,25 @@ class ZeroMQReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, ret, req_opts = yield self.payload_handler(payload) except Exception as e: # pylint: disable=broad-except # always attempt to return an error to the minion - stream.send('Some exception handling minion payload') - log.error('Some exception handling a payload from minion', exc_info=True) + stream.send("Some exception handling minion payload") + log.error("Some exception handling a payload from minion", exc_info=True) raise salt.ext.tornado.gen.Return() - req_fun = req_opts.get('fun', 'send') - if req_fun == 'send_clear': + req_fun = req_opts.get("fun", "send") + if req_fun == "send_clear": stream.send(self.serial.dumps(ret)) - elif req_fun == 'send': + elif req_fun == "send": stream.send(self.serial.dumps(self.crypticle.dumps(ret))) - elif req_fun == 'send_private': - stream.send(self.serial.dumps(self._encrypt_private(ret, - req_opts['key'], - req_opts['tgt'], - ))) + elif req_fun == "send_private": + stream.send( + self.serial.dumps( + self._encrypt_private(ret, req_opts["key"], req_opts["tgt"],) + ) + ) else: - log.error('Unknown req_fun %s', req_fun) + log.error("Unknown req_fun %s", req_fun) # always attempt to return an error to the minion - stream.send('Server-side exception handling payload') + stream.send("Server-side exception handling payload") raise salt.ext.tornado.gen.Return() def __setup_signals(self): @@ -798,19 +879,19 @@ class ZeroMQReqServerChannel(salt.transport.mixins.auth.AESReqServerMixin, signal.signal(signal.SIGTERM, self._handle_signals) def _handle_signals(self, signum, sigframe): - msg = '{0} received a '.format(self.__class__.__name__) + msg = "{0} received a ".format(self.__class__.__name__) if signum == signal.SIGINT: - msg += 'SIGINT' + msg += "SIGINT" elif signum == signal.SIGTERM: - msg += 'SIGTERM' - msg += '. Exiting' + msg += "SIGTERM" + msg += ". Exiting" log.debug(msg) self.close() sys.exit(salt.defaults.exitcodes.EX_OK) def _set_tcp_keepalive(zmq_socket, opts): - ''' + """ Ensure that TCP keepalives are set as specified in "opts". Warning: Failure to set TCP keepalives on the salt-master can result in @@ -821,30 +902,22 @@ def _set_tcp_keepalive(zmq_socket, opts): Warning: Failure to set TCP keepalives on minions can result in frequent or unexpected disconnects! - ''' - if hasattr(zmq, 'TCP_KEEPALIVE') and opts: - if 'tcp_keepalive' in opts: - zmq_socket.setsockopt( - zmq.TCP_KEEPALIVE, opts['tcp_keepalive'] - ) - if 'tcp_keepalive_idle' in opts: - zmq_socket.setsockopt( - zmq.TCP_KEEPALIVE_IDLE, opts['tcp_keepalive_idle'] - ) - if 'tcp_keepalive_cnt' in opts: - zmq_socket.setsockopt( - zmq.TCP_KEEPALIVE_CNT, opts['tcp_keepalive_cnt'] - ) - if 'tcp_keepalive_intvl' in opts: - zmq_socket.setsockopt( - zmq.TCP_KEEPALIVE_INTVL, opts['tcp_keepalive_intvl'] - ) + """ + if hasattr(zmq, "TCP_KEEPALIVE") and opts: + if "tcp_keepalive" in opts: + zmq_socket.setsockopt(zmq.TCP_KEEPALIVE, opts["tcp_keepalive"]) + if "tcp_keepalive_idle" in opts: + zmq_socket.setsockopt(zmq.TCP_KEEPALIVE_IDLE, opts["tcp_keepalive_idle"]) + if "tcp_keepalive_cnt" in opts: + zmq_socket.setsockopt(zmq.TCP_KEEPALIVE_CNT, opts["tcp_keepalive_cnt"]) + if "tcp_keepalive_intvl" in opts: + zmq_socket.setsockopt(zmq.TCP_KEEPALIVE_INTVL, opts["tcp_keepalive_intvl"]) class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel): - ''' + """ Encapsulate synchronous operations for a publisher channel - ''' + """ _sock_data = threading.local() @@ -857,9 +930,9 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel): return salt.ext.tornado.gen.sleep(5) def _publish_daemon(self, log_queue=None): - ''' + """ Bind to the interface specified in the configuration file - ''' + """ salt.utils.process.appendproctitle(self.__class__.__name__) if log_queue: salt.log.setup.set_multiprocessing_logging_queue(log_queue) @@ -872,39 +945,39 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel): _set_tcp_keepalive(pub_sock, self.opts) # if 2.1 >= zmq < 3.0, we only have one HWM setting try: - pub_sock.setsockopt(zmq.HWM, self.opts.get('pub_hwm', 1000)) + pub_sock.setsockopt(zmq.HWM, self.opts.get("pub_hwm", 1000)) # in zmq >= 3.0, there are separate send and receive HWM settings except AttributeError: # Set the High Water Marks. For more information on HWM, see: # http://api.zeromq.org/4-1:zmq-setsockopt - pub_sock.setsockopt(zmq.SNDHWM, self.opts.get('pub_hwm', 1000)) - pub_sock.setsockopt(zmq.RCVHWM, self.opts.get('pub_hwm', 1000)) - if self.opts['ipv6'] is True and hasattr(zmq, 'IPV4ONLY'): + pub_sock.setsockopt(zmq.SNDHWM, self.opts.get("pub_hwm", 1000)) + pub_sock.setsockopt(zmq.RCVHWM, self.opts.get("pub_hwm", 1000)) + if self.opts["ipv6"] is True and hasattr(zmq, "IPV4ONLY"): # IPv6 sockets work for both IPv6 and IPv4 addresses pub_sock.setsockopt(zmq.IPV4ONLY, 0) - pub_sock.setsockopt(zmq.BACKLOG, self.opts.get('zmq_backlog', 1000)) + pub_sock.setsockopt(zmq.BACKLOG, self.opts.get("zmq_backlog", 1000)) pub_sock.setsockopt(zmq.LINGER, -1) - pub_uri = 'tcp://{interface}:{publish_port}'.format(**self.opts) + pub_uri = "tcp://{interface}:{publish_port}".format(**self.opts) # Prepare minion pull socket pull_sock = context.socket(zmq.PULL) pull_sock.setsockopt(zmq.LINGER, -1) - if self.opts.get('ipc_mode', '') == 'tcp': - pull_uri = 'tcp://127.0.0.1:{0}'.format( - self.opts.get('tcp_master_publish_pull', 4514) - ) + if self.opts.get("ipc_mode", "") == "tcp": + pull_uri = "tcp://127.0.0.1:{0}".format( + self.opts.get("tcp_master_publish_pull", 4514) + ) else: - pull_uri = 'ipc://{0}'.format( - os.path.join(self.opts['sock_dir'], 'publish_pull.ipc') - ) + pull_uri = "ipc://{0}".format( + os.path.join(self.opts["sock_dir"], "publish_pull.ipc") + ) salt.utils.zeromq.check_ipc_path_max_len(pull_uri) # Start the minion command publisher - log.info('Starting the Salt Publisher on %s', pub_uri) + log.info("Starting the Salt Publisher on %s", pub_uri) pub_sock.bind(pub_uri) # Securely create socket - log.info('Starting the Salt Puller on %s', pull_uri) + log.info("Starting the Salt Puller on %s", pull_uri) with salt.utils.files.set_umask(0o177): pull_sock.bind(pull_uri) @@ -913,51 +986,63 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel): # Catch and handle EINTR from when this process is sent # SIGUSR1 gracefully so we don't choke and die horribly try: - log.debug('Publish daemon getting data from puller %s', pull_uri) + log.debug("Publish daemon getting data from puller %s", pull_uri) package = pull_sock.recv() - log.debug('Publish daemon received payload. size=%d', len(package)) + log.debug("Publish daemon received payload. size=%d", len(package)) unpacked_package = salt.payload.unpackage(package) if six.PY3: - unpacked_package = salt.transport.frame.decode_embedded_strs(unpacked_package) - payload = unpacked_package['payload'] - log.trace('Accepted unpacked package from puller') - if self.opts['zmq_filtering']: + unpacked_package = salt.transport.frame.decode_embedded_strs( + unpacked_package + ) + payload = unpacked_package["payload"] + log.trace("Accepted unpacked package from puller") + if self.opts["zmq_filtering"]: # if you have a specific topic list, use that - if 'topic_lst' in unpacked_package: - for topic in unpacked_package['topic_lst']: - log.trace('Sending filtered data over publisher %s', pub_uri) + if "topic_lst" in unpacked_package: + for topic in unpacked_package["topic_lst"]: + log.trace( + "Sending filtered data over publisher %s", pub_uri + ) # zmq filters are substring match, hash the topic # to avoid collisions - htopic = salt.utils.stringutils.to_bytes(hashlib.sha1(salt.utils.stringutils.to_bytes(topic)).hexdigest()) + htopic = salt.utils.stringutils.to_bytes( + hashlib.sha1( + salt.utils.stringutils.to_bytes(topic) + ).hexdigest() + ) pub_sock.send(htopic, flags=zmq.SNDMORE) pub_sock.send(payload) - log.trace('Filtered data has been sent') + log.trace("Filtered data has been sent") # Syndic broadcast - if self.opts.get('order_masters'): - log.trace('Sending filtered data to syndic') - pub_sock.send(b'syndic', flags=zmq.SNDMORE) + if self.opts.get("order_masters"): + log.trace("Sending filtered data to syndic") + pub_sock.send(b"syndic", flags=zmq.SNDMORE) pub_sock.send(payload) - log.trace('Filtered data has been sent to syndic') + log.trace("Filtered data has been sent to syndic") # otherwise its a broadcast else: # TODO: constants file for "broadcast" - log.trace('Sending broadcasted data over publisher %s', pub_uri) - pub_sock.send(b'broadcast', flags=zmq.SNDMORE) + log.trace( + "Sending broadcasted data over publisher %s", pub_uri + ) + pub_sock.send(b"broadcast", flags=zmq.SNDMORE) pub_sock.send(payload) - log.trace('Broadcasted data has been sent') + log.trace("Broadcasted data has been sent") else: - log.trace('Sending ZMQ-unfiltered data over publisher %s', pub_uri) + log.trace( + "Sending ZMQ-unfiltered data over publisher %s", pub_uri + ) pub_sock.send(payload) - log.trace('Unfiltered data has been sent') + log.trace("Unfiltered data has been sent") except zmq.ZMQError as exc: if exc.errno == errno.EINTR: continue six.reraise(*sys.exc_info()) except KeyboardInterrupt: - log.trace('Publish daemon caught Keyboard interupt, tearing down') + log.trace("Publish daemon caught Keyboard interupt, tearing down") # Cleanly close the sockets if we're shutting down if pub_sock.closed is False: pub_sock.close() @@ -967,107 +1052,112 @@ class ZeroMQPubServerChannel(salt.transport.server.PubServerChannel): context.term() def pre_fork(self, process_manager, kwargs=None): - ''' + """ Do anything necessary pre-fork. Since this is on the master side this will primarily be used to create IPC channels and create our daemon process to do the actual publishing :param func process_manager: A ProcessManager, from salt.utils.process.ProcessManager - ''' + """ process_manager.add_process(self._publish_daemon, kwargs=kwargs) @property def pub_sock(self): - ''' + """ This thread's zmq publisher socket. This socket is stored on the class so that multiple instantiations in the same thread will re-use a single zmq socket. - ''' + """ try: return self._sock_data.sock except AttributeError: pass def pub_connect(self): - ''' + """ Create and connect this thread's zmq socket. If a publisher socket already exists "pub_close" is called before creating and connecting a new socket. - ''' + """ if self.pub_sock: self.pub_close() ctx = zmq.Context.instance() self._sock_data.sock = ctx.socket(zmq.PUSH) self.pub_sock.setsockopt(zmq.LINGER, -1) - if self.opts.get('ipc_mode', '') == 'tcp': - pull_uri = 'tcp://127.0.0.1:{0}'.format( - self.opts.get('tcp_master_publish_pull', 4514) - ) + if self.opts.get("ipc_mode", "") == "tcp": + pull_uri = "tcp://127.0.0.1:{0}".format( + self.opts.get("tcp_master_publish_pull", 4514) + ) else: - pull_uri = 'ipc://{0}'.format( - os.path.join(self.opts['sock_dir'], 'publish_pull.ipc') - ) + pull_uri = "ipc://{0}".format( + os.path.join(self.opts["sock_dir"], "publish_pull.ipc") + ) log.debug("Connecting to pub server: %s", pull_uri) self.pub_sock.connect(pull_uri) return self._sock_data.sock def pub_close(self): - ''' + """ Disconnect an existing publisher socket and remove it from the local thread's cache. - ''' - if hasattr(self._sock_data, 'sock'): + """ + if hasattr(self._sock_data, "sock"): self._sock_data.sock.close() - delattr(self._sock_data, 'sock') + delattr(self._sock_data, "sock") def publish(self, load): - ''' + """ Publish "load" to minions. This send the load to the publisher daemon process with does the actual sending to minions. :param dict load: A load to be sent across the wire to minions - ''' - payload = {'enc': 'aes'} - crypticle = salt.crypt.Crypticle(self.opts, salt.master.SMaster.secrets['aes']['secret'].value) - payload['load'] = crypticle.dumps(load) - if self.opts['sign_pub_messages']: - master_pem_path = os.path.join(self.opts['pki_dir'], 'master.pem') + """ + payload = {"enc": "aes"} + crypticle = salt.crypt.Crypticle( + self.opts, salt.master.SMaster.secrets["aes"]["secret"].value + ) + payload["load"] = crypticle.dumps(load) + if self.opts["sign_pub_messages"]: + master_pem_path = os.path.join(self.opts["pki_dir"], "master.pem") log.debug("Signing data packet") - payload['sig'] = salt.crypt.sign_message(master_pem_path, payload['load']) - int_payload = {'payload': self.serial.dumps(payload)} + payload["sig"] = salt.crypt.sign_message(master_pem_path, payload["load"]) + int_payload = {"payload": self.serial.dumps(payload)} # add some targeting stuff for lists only (for now) - if load['tgt_type'] == 'list': - int_payload['topic_lst'] = load['tgt'] + if load["tgt_type"] == "list": + int_payload["topic_lst"] = load["tgt"] # If zmq_filtering is enabled, target matching has to happen master side match_targets = ["pcre", "glob", "list"] - if self.opts['zmq_filtering'] and load['tgt_type'] in match_targets: + if self.opts["zmq_filtering"] and load["tgt_type"] in match_targets: # Fetch a list of minions that match - _res = self.ckminions.check_minions(load['tgt'], - tgt_type=load['tgt_type']) - match_ids = _res['minions'] + _res = self.ckminions.check_minions(load["tgt"], tgt_type=load["tgt_type"]) + match_ids = _res["minions"] log.debug("Publish Side Match: %s", match_ids) # Send list of miions thru so zmq can target them - int_payload['topic_lst'] = match_ids + int_payload["topic_lst"] = match_ids payload = self.serial.dumps(int_payload) log.debug( - 'Sending payload to publish daemon. jid=%s size=%d', - load.get('jid', None), len(payload), + "Sending payload to publish daemon. jid=%s size=%d", + load.get("jid", None), + len(payload), ) if not self.pub_sock: self.pub_connect() self.pub_sock.send(payload) - log.debug('Sent payload to publish daemon.') + log.debug("Sent payload to publish daemon.") class AsyncReqMessageClientPool(salt.transport.MessageClientPool): - ''' + """ Wrapper class of AsyncReqMessageClientPool to avoid blocking waiting while writing data to socket. - ''' + """ + def __init__(self, opts, args=None, kwargs=None): - super(AsyncReqMessageClientPool, self).__init__(AsyncReqMessageClient, opts, args=args, kwargs=kwargs) + super(AsyncReqMessageClientPool, self).__init__( + AsyncReqMessageClient, opts, args=args, kwargs=kwargs + ) self._closing = False def close(self): @@ -1086,31 +1176,33 @@ class AsyncReqMessageClientPool(salt.transport.MessageClientPool): def destroy(self): # Bacwards compat salt.utils.versions.warn_until( - 'Sodium', - 'Calling {0}.destroy() is deprecated. Please call {0}.close() instead.'.format( + "Sodium", + "Calling {0}.destroy() is deprecated. Please call {0}.close() instead.".format( self.__class__.__name__ ), - stacklevel=3 + stacklevel=3, ) self.close() # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 # TODO: unit tests! class AsyncReqMessageClient(object): - ''' + """ This class wraps the underlying zeromq REQ socket and gives a future-based interface to sending and recieving messages. This works around the primary limitation of serialized send/recv on the underlying socket by queueing the message sends in this class. In the future if we decide to attempt to multiplex we can manage a pool of REQ/REP sockets-- but for now we'll just do them in serial - ''' + """ + def __init__(self, opts, addr, linger=0, io_loop=None): - ''' + """ Create an asynchronous message client :param dict opts: The salt opts dictionary @@ -1118,7 +1210,7 @@ class AsyncReqMessageClient(object): :param int linger: The number of seconds to linger on a ZMQ socket. See http://api.zeromq.org/2-1:zmq-setsockopt [ZMQ_LINGER] :param IOLoop io_loop: A Tornado IOLoop event scheduler [tornado.ioloop.IOLoop] - ''' + """ self.opts = opts self.addr = addr self.linger = linger @@ -1146,7 +1238,7 @@ class AsyncReqMessageClient(object): return self._closing = True - if hasattr(self, 'stream') and self.stream is not None: + if hasattr(self, "stream") and self.stream is not None: if ZMQ_VERSION_INFO < (14, 3, 0): # stream.close() doesn't work properly on pyzmq < 14.3.0 if self.stream.socket: @@ -1165,21 +1257,22 @@ class AsyncReqMessageClient(object): def destroy(self): # Bacwards compat salt.utils.versions.warn_until( - 'Sodium', - 'Calling {0}.destroy() is deprecated. Please call {0}.close() instead.'.format( + "Sodium", + "Calling {0}.destroy() is deprecated. Please call {0}.close() instead.".format( self.__class__.__name__ ), - stacklevel=3 + stacklevel=3, ) self.close() # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 def _init_socket(self): - if hasattr(self, 'stream'): + if hasattr(self, "stream"): self.stream.close() # pylint: disable=E0203 self.socket.close() # pylint: disable=E0203 del self.stream @@ -1188,22 +1281,22 @@ class AsyncReqMessageClient(object): self.socket = self.context.socket(zmq.REQ) # socket options - if hasattr(zmq, 'RECONNECT_IVL_MAX'): - self.socket.setsockopt( - zmq.RECONNECT_IVL_MAX, 5000 - ) + if hasattr(zmq, "RECONNECT_IVL_MAX"): + self.socket.setsockopt(zmq.RECONNECT_IVL_MAX, 5000) _set_tcp_keepalive(self.socket, self.opts) - if self.addr.startswith('tcp://['): + if self.addr.startswith("tcp://["): # Hint PF type if bracket enclosed IPv6 address - if hasattr(zmq, 'IPV6'): + if hasattr(zmq, "IPV6"): self.socket.setsockopt(zmq.IPV6, 1) - elif hasattr(zmq, 'IPV4ONLY'): + elif hasattr(zmq, "IPV4ONLY"): self.socket.setsockopt(zmq.IPV4ONLY, 0) self.socket.linger = self.linger - log.debug('Trying to connect to: %s', self.addr) + log.debug("Trying to connect to: %s", self.addr) self.socket.connect(self.addr) - self.stream = zmq.eventloop.zmqstream.ZMQStream(self.socket, io_loop=self.io_loop) + self.stream = zmq.eventloop.zmqstream.ZMQStream( + self.socket, io_loop=self.io_loop + ) @salt.ext.tornado.gen.coroutine def _internal_send_recv(self): @@ -1220,13 +1313,14 @@ class AsyncReqMessageClient(object): if not future.done(): data = self.serial.loads(msg[0]) future.set_result(data) + self.stream.on_recv(mark_future) self.stream.send(message) try: ret = yield future except Exception as err: # pylint: disable=broad-except - log.debug('Re-init ZMQ socket: %s', err) + log.debug("Re-init ZMQ socket: %s", err) self._init_socket() # re-init the zmq socket (no other way in zmq) del self.send_queue[0] continue @@ -1243,12 +1337,12 @@ class AsyncReqMessageClient(object): self.io_loop.remove_timeout(timeout) def timeout_message(self, message): - ''' + """ Handle a message timeout by removing it from the sending queue and informing the caller :raises: SaltReqTimeoutError - ''' + """ future = self.send_future_map.pop(message, None) # In a race condition the message might have been sent by the time # we're timing it out. Make sure the future is not None @@ -1256,21 +1350,24 @@ class AsyncReqMessageClient(object): del self.send_timeout_map[message] if future.attempts < future.tries: future.attempts += 1 - log.debug('SaltReqTimeoutError, retrying. (%s/%s)', future.attempts, future.tries) + log.debug( + "SaltReqTimeoutError, retrying. (%s/%s)", + future.attempts, + future.tries, + ) self.send( - message, - timeout=future.timeout, - tries=future.tries, - future=future, + message, timeout=future.timeout, tries=future.tries, future=future, ) else: - future.set_exception(SaltReqTimeoutError('Message timed out')) + future.set_exception(SaltReqTimeoutError("Message timed out")) - def send(self, message, timeout=None, tries=3, future=None, callback=None, raw=False): - ''' + def send( + self, message, timeout=None, tries=3, future=None, callback=None, raw=False + ): + """ Return a future which will be completed when the message has a response - ''' + """ if future is None: future = salt.ext.tornado.concurrent.Future() future.tries = tries @@ -1279,18 +1376,22 @@ class AsyncReqMessageClient(object): # if a future wasn't passed in, we need to serialize the message message = self.serial.dumps(message) if callback is not None: + def handle_future(future): response = future.result() self.io_loop.add_callback(callback, response) + future.add_done_callback(handle_future) # Add this future to the mapping self.send_future_map[message] = future - if self.opts.get('detect_mode') is True: + if self.opts.get("detect_mode") is True: timeout = 1 if timeout is not None: - send_timeout = self.io_loop.call_later(timeout, self.timeout_message, message) + send_timeout = self.io_loop.call_later( + timeout, self.timeout_message, message + ) self.send_timeout_map[message] = send_timeout if len(self.send_queue) == 0: @@ -1305,19 +1406,21 @@ class ZeroMQSocketMonitor(object): __EVENT_MAP = None def __init__(self, socket): - ''' + """ Create ZMQ monitor sockets More information: http://api.zeromq.org/4-0:zmq-socket-monitor - ''' + """ self._socket = socket self._monitor_socket = self._socket.get_monitor_socket() self._monitor_stream = None def start_io_loop(self, io_loop): log.trace("Event monitor start!") - self._monitor_stream = zmq.eventloop.zmqstream.ZMQStream(self._monitor_socket, io_loop=io_loop) + self._monitor_stream = zmq.eventloop.zmqstream.ZMQStream( + self._monitor_socket, io_loop=io_loop + ) self._monitor_stream.on_recv(self.monitor_callback) def start_poll(self): @@ -1336,7 +1439,7 @@ class ZeroMQSocketMonitor(object): if ZeroMQSocketMonitor.__EVENT_MAP is None: event_map = {} for name in dir(zmq): - if name.startswith('EVENT_'): + if name.startswith("EVENT_"): value = getattr(zmq, name) event_map[value] = name ZeroMQSocketMonitor.__EVENT_MAP = event_map @@ -1344,9 +1447,9 @@ class ZeroMQSocketMonitor(object): def monitor_callback(self, msg): evt = zmq.utils.monitor.parse_monitor_message(msg) - evt['description'] = self.event_map[evt['event']] + evt["description"] = self.event_map[evt["event"]] log.debug("ZeroMQ event: %s", evt) - if evt['event'] == zmq.EVENT_MONITOR_STOPPED: + if evt["event"] == zmq.EVENT_MONITOR_STOPPED: self.stop() def stop(self): diff --git a/salt/utils/__init__.py b/salt/utils/__init__.py index f3aaa668310..8f8e34e4326 100644 --- a/salt/utils/__init__.py +++ b/salt/utils/__init__.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Some of the utils used by salt PLEASE DO NOT ADD ANY NEW FUNCTIONS TO THIS FILE. New functions should be organized in other files under salt/utils/. Please consult the dev team if you are unsure where a new function should go. -''' +""" diff --git a/salt/utils/aggregation.py b/salt/utils/aggregation.py index 8973a8fe391..e37a8c37291 100644 --- a/salt/utils/aggregation.py +++ b/salt/utils/aggregation.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" salt.utils.aggregation ~~~~~~~~~~~~~~~~~~~~~~ @@ -101,55 +101,56 @@ TODO: write this part -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging -# Import Salt libs -from salt.utils.odict import OrderedDict - # Import 3rd-party libs from salt.ext import six -__all__ = ['aggregate', 'Aggregate', 'Map', 'Scalar', 'Sequence'] +# Import Salt libs +from salt.utils.odict import OrderedDict + +__all__ = ["aggregate", "Aggregate", "Map", "Scalar", "Sequence"] log = logging.getLogger(__name__) class Aggregate(object): - ''' + """ Aggregation base. - ''' + """ class Map(OrderedDict, Aggregate): - ''' + """ Map aggregation. - ''' + """ class Sequence(list, Aggregate): - ''' + """ Sequence aggregation. - ''' + """ def Scalar(obj): - ''' + """ Shortcut for Sequence creation >>> Scalar('foo') == Sequence(['foo']) True - ''' + """ return Sequence([obj]) def levelise(level): - ''' + """ Describe which levels are allowed to do deep merging. level can be: @@ -169,7 +170,7 @@ def levelise(level): * a list of bool and int values * a string of 0 and 1 characters - ''' + """ if not level: # False, 0, [] ... return False, False @@ -186,9 +187,9 @@ def levelise(level): def mark(obj, map_class=Map, sequence_class=Sequence): - ''' + """ Convert obj into an Aggregate instance - ''' + """ if isinstance(obj, Aggregate): return obj if isinstance(obj, dict): @@ -200,12 +201,12 @@ def mark(obj, map_class=Map, sequence_class=Sequence): def aggregate(obj_a, obj_b, level=False, map_class=Map, sequence_class=Sequence): - ''' + """ Merge obj_b into obj_a. >>> aggregate('first', 'second', True) == ['first', 'second'] True - ''' + """ deep, subdeep = levelise(level) if deep: @@ -222,8 +223,7 @@ def aggregate(obj_a, obj_b, level=False, map_class=Map, sequence_class=Sequence) for key, value in six.iteritems(obj_b): if key in obj_a: - value = aggregate(obj_a[key], value, - subdeep, map_class, sequence_class) + value = aggregate(obj_a[key], value, subdeep, map_class, sequence_class) response[key] = value return response @@ -237,8 +237,8 @@ def aggregate(obj_a, obj_b, level=False, map_class=Map, sequence_class=Sequence) response = copy.copy(obj_b) if isinstance(obj_a, Aggregate) or isinstance(obj_b, Aggregate): - log.info('only one value marked as aggregate. keep `obj_b` value') + log.info("only one value marked as aggregate. keep `obj_b` value") return response - log.debug('no value marked as aggregate. keep `obj_b` value') + log.debug("no value marked as aggregate. keep `obj_b` value") return response diff --git a/salt/utils/args.py b/salt/utils/args.py index 8cc0f351962..20ec0ac0cc3 100644 --- a/salt/utils/args.py +++ b/salt/utils/args.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Functions used for CLI argument handling -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import fnmatch import inspect @@ -12,26 +13,27 @@ import logging import re import shlex -# Import salt libs -from salt.exceptions import SaltInvocationError -from salt.ext import six -from salt.ext.six.moves import map, zip # pylint: disable=import-error,redefined-builtin import salt.utils.data import salt.utils.jid import salt.utils.versions import salt.utils.yaml +# Import salt libs +from salt.exceptions import SaltInvocationError +from salt.ext import six +from salt.ext.six.moves import map, zip + log = logging.getLogger(__name__) if six.PY3: - KWARG_REGEX = re.compile(r'^([^\d\W][\w.-]*)=(?!=)(.*)$', re.UNICODE) + KWARG_REGEX = re.compile(r"^([^\d\W][\w.-]*)=(?!=)(.*)$", re.UNICODE) else: - KWARG_REGEX = re.compile(r'^([^\d\W][\w.-]*)=(?!=)(.*)$') + KWARG_REGEX = re.compile(r"^([^\d\W][\w.-]*)=(?!=)(.*)$") def clean_kwargs(**kwargs): - ''' + """ Return a dict without any of the __pub* keys (or any other keys starting with a dunder) from the kwargs dict passed into the execution module functions. These keys are useful for tracking what was used to invoke @@ -43,28 +45,26 @@ def clean_kwargs(**kwargs): .. code-block:: python kwargs = __utils__['args.clean_kwargs'](**kwargs) - ''' + """ ret = {} for key, val in six.iteritems(kwargs): - if not key.startswith('__'): + if not key.startswith("__"): ret[key] = val return ret def invalid_kwargs(invalid_kwargs, raise_exc=True): - ''' + """ Raise a SaltInvocationError if invalid_kwargs is non-empty - ''' + """ if invalid_kwargs: if isinstance(invalid_kwargs, dict): new_invalid = [ - '{0}={1}'.format(x, y) - for x, y in six.iteritems(invalid_kwargs) + "{0}={1}".format(x, y) for x, y in six.iteritems(invalid_kwargs) ] invalid_kwargs = new_invalid - msg = ( - 'The following keyword arguments are not valid: {0}' - .format(', '.join(invalid_kwargs)) + msg = "The following keyword arguments are not valid: {0}".format( + ", ".join(invalid_kwargs) ) if raise_exc: raise SaltInvocationError(msg) @@ -73,18 +73,23 @@ def invalid_kwargs(invalid_kwargs, raise_exc=True): def condition_input(args, kwargs): - ''' + """ Return a single arg structure for the publisher to safely use - ''' + """ ret = [] for arg in args: - if (six.PY3 and isinstance(arg, six.integer_types) and salt.utils.jid.is_jid(six.text_type(arg))) or \ - (six.PY2 and isinstance(arg, long)): # pylint: disable=incompatible-py3-code,undefined-variable + # pylint: disable=incompatible-py3-code,undefined-variable + if ( + six.PY3 + and isinstance(arg, six.integer_types) + and salt.utils.jid.is_jid(six.text_type(arg)) + ) or (six.PY2 and isinstance(arg, long)): ret.append(six.text_type(arg)) else: ret.append(arg) + # pylint: enable=incompatible-py3-code,undefined-variable if isinstance(kwargs, dict) and kwargs: - kw_ = {'__kwarg__': True} + kw_ = {"__kwarg__": True} for key, val in six.iteritems(kwargs): kw_[key] = val return ret + [kw_] @@ -92,12 +97,12 @@ def condition_input(args, kwargs): def parse_input(args, condition=True, no_parse=None): - ''' + """ Parse out the args and kwargs from a list of input values. Optionally, return the args and kwargs without passing them to condition_input(). Don't pull args with key=val apart if it has a newline in it. - ''' + """ if no_parse is None: no_parse = () _args = [] @@ -106,16 +111,16 @@ def parse_input(args, condition=True, no_parse=None): if isinstance(arg, six.string_types): arg_name, arg_value = parse_kwarg(arg) if arg_name: - _kwargs[arg_name] = yamlify_arg(arg_value) \ - if arg_name not in no_parse \ - else arg_value + _kwargs[arg_name] = ( + yamlify_arg(arg_value) if arg_name not in no_parse else arg_value + ) else: _args.append(yamlify_arg(arg)) elif isinstance(arg, dict): # Yes, we're popping this key off and adding it back if # condition_input is called below, but this is the only way to # gracefully handle both CLI and API input. - if arg.pop('__kwarg__', False) is True: + if arg.pop("__kwarg__", False) is True: _kwargs.update(arg) else: _args.append(arg) @@ -127,7 +132,7 @@ def parse_input(args, condition=True, no_parse=None): def parse_kwarg(string_): - ''' + """ Parses the string and looks for the following kwarg format: "{argument name}={argument value}" @@ -136,7 +141,7 @@ def parse_kwarg(string_): Returns the kwarg name and value, or (None, None) if the regex was not matched. - ''' + """ try: return KWARG_REGEX.match(string_).groups() except AttributeError: @@ -144,13 +149,13 @@ def parse_kwarg(string_): def yamlify_arg(arg): - ''' + """ yaml.safe_load the arg - ''' + """ if not isinstance(arg, six.string_types): return arg - if arg.strip() == '': + if arg.strip() == "": # Because YAML loads empty (or all whitespace) strings as None, we # return the original string # >>> import yaml @@ -160,7 +165,7 @@ def yamlify_arg(arg): # True return arg - elif '_' in arg and all([x in '0123456789_' for x in arg.strip()]): + elif "_" in arg and all([x in "0123456789_" for x in arg.strip()]): # When the stripped string includes just digits and underscores, the # underscores are ignored and the digits are combined together and # loaded as an int. We don't want that, so return the original value. @@ -169,46 +174,50 @@ def yamlify_arg(arg): try: # Explicit late import to avoid circular import. DO NOT MOVE THIS. import salt.utils.yaml + original_arg = arg - if '#' in arg: + if "#" in arg: # Only yamlify if it parses into a non-string type, to prevent # loss of content due to # as comment character parsed_arg = salt.utils.yaml.safe_load(arg) if isinstance(parsed_arg, six.string_types) or parsed_arg is None: return arg return parsed_arg - if arg == 'None': + if arg == "None": arg = None else: arg = salt.utils.yaml.safe_load(arg) if isinstance(arg, dict): # dicts must be wrapped in curly braces - if (isinstance(original_arg, six.string_types) and - not original_arg.startswith('{')): + if isinstance( + original_arg, six.string_types + ) and not original_arg.startswith("{"): return original_arg else: return arg elif isinstance(arg, list): # lists must be wrapped in brackets - if (isinstance(original_arg, six.string_types) and - not original_arg.startswith('[')): + if isinstance( + original_arg, six.string_types + ) and not original_arg.startswith("["): return original_arg else: return arg - elif arg is None \ - or isinstance(arg, (list, float, six.integer_types, six.string_types)): + elif arg is None or isinstance( + arg, (list, float, six.integer_types, six.string_types) + ): # yaml.safe_load will load '|' and '!' as '', don't let it do that. - if arg == '' and original_arg in ('|', '!'): + if arg == "" and original_arg in ("|", "!"): return original_arg # yaml.safe_load will treat '#' as a comment, so a value of '#' # will become None. Keep this value from being stomped as well. - elif arg is None and original_arg.strip().startswith('#'): + elif arg is None and original_arg.strip().startswith("#"): return original_arg # Other times, yaml.safe_load will load '!' as None. Prevent that. - elif arg is None and original_arg == '!': + elif arg is None and original_arg == "!": return original_arg else: return arg @@ -221,26 +230,31 @@ def yamlify_arg(arg): if six.PY3: - from collections import namedtuple # pylint: disable=wrong-import-position,wrong-import-order + from collections import ( + namedtuple, + ) # pylint: disable=wrong-import-position,wrong-import-order - _ArgSpec = namedtuple('ArgSpec', 'args varargs keywords defaults') + _ArgSpec = namedtuple("ArgSpec", "args varargs keywords defaults") def _getargspec(func): - ''' + """ Python 3 wrapper for inspect.getargsspec inspect.getargsspec is deprecated and will be removed in Python 3.6. - ''' - args, varargs, varkw, defaults, kwonlyargs, _, ann = \ - inspect.getfullargspec(func) # pylint: disable=no-member + """ + args, varargs, varkw, defaults, kwonlyargs, _, ann = inspect.getfullargspec( + func + ) # pylint: disable=no-member if kwonlyargs or ann: - raise ValueError('Function has keyword-only arguments or annotations' - ', use getfullargspec() API which can support them') + raise ValueError( + "Function has keyword-only arguments or annotations" + ", use getfullargspec() API which can support them" + ) return _ArgSpec(args, varargs, varkw, defaults) def get_function_argspec(func, is_class_method=None): - ''' + """ A small wrapper around getargspec that also supports callable classes :param is_class_method: Pass True if you are sure that the function being passed is a class method. The reason for this is that on Python 3 @@ -249,9 +263,9 @@ def get_function_argspec(func, is_class_method=None): methods. So, on Python 3, in case of a class method, you'd need the class to which the function belongs to be instantiated and this is not always wanted. - ''' + """ if not callable(func): - raise TypeError('{0} is not a callable'.format(func)) + raise TypeError("{0} is not a callable".format(func)) if six.PY2: if is_class_method is True: @@ -266,9 +280,7 @@ def get_function_argspec(func, is_class_method=None): aspec = inspect.getargspec(func.__call__) del aspec.args[0] # self else: - raise TypeError( - 'Cannot inspect argument list for \'{0}\''.format(func) - ) + raise TypeError("Cannot inspect argument list for '{0}'".format(func)) else: if is_class_method is True: aspec = _getargspec(func) @@ -282,16 +294,14 @@ def get_function_argspec(func, is_class_method=None): aspec = _getargspec(func.__call__) del aspec.args[0] # self else: - raise TypeError( - 'Cannot inspect argument list for \'{0}\''.format(func) - ) + raise TypeError("Cannot inspect argument list for '{0}'".format(func)) return aspec def shlex_split(s, **kwargs): - ''' + """ Only split if variable is a string - ''' + """ if isinstance(s, six.string_types): # On PY2, shlex.split will fail with unicode types if there are # non-ascii characters in the string. So, we need to make sure we @@ -305,26 +315,26 @@ def shlex_split(s, **kwargs): def arg_lookup(fun, aspec=None): - ''' + """ Return a dict containing the arguments and default arguments to the function. - ''' - ret = {'kwargs': {}} + """ + ret = {"kwargs": {}} if aspec is None: aspec = get_function_argspec(fun) if aspec.defaults: - ret['kwargs'] = dict(zip(aspec.args[::-1], aspec.defaults[::-1])) - ret['args'] = [arg for arg in aspec.args if arg not in ret['kwargs']] + ret["kwargs"] = dict(zip(aspec.args[::-1], aspec.defaults[::-1])) + ret["args"] = [arg for arg in aspec.args if arg not in ret["kwargs"]] return ret -def argspec_report(functions, module=''): - ''' +def argspec_report(functions, module=""): + """ Pass in a functions dict as it is returned from the loader and return the argspec function signatures - ''' + """ ret = {} - if '*' in module or '.' in module: + if "*" in module or "." in module: for fun in fnmatch.filter(functions, module): try: aspec = get_function_argspec(functions[fun]) @@ -335,14 +345,14 @@ def argspec_report(functions, module=''): args, varargs, kwargs, defaults = aspec ret[fun] = {} - ret[fun]['args'] = args if args else None - ret[fun]['defaults'] = defaults if defaults else None - ret[fun]['varargs'] = True if varargs else None - ret[fun]['kwargs'] = True if kwargs else None + ret[fun]["args"] = args if args else None + ret[fun]["defaults"] = defaults if defaults else None + ret[fun]["varargs"] = True if varargs else None + ret[fun]["kwargs"] = True if kwargs else None else: # "sys" should just match sys without also matching sysctl - module_dot = module + '.' + module_dot = module + "." for fun in functions: if fun.startswith(module_dot): @@ -355,51 +365,50 @@ def argspec_report(functions, module=''): args, varargs, kwargs, defaults = aspec ret[fun] = {} - ret[fun]['args'] = args if args else None - ret[fun]['defaults'] = defaults if defaults else None - ret[fun]['varargs'] = True if varargs else None - ret[fun]['kwargs'] = True if kwargs else None + ret[fun]["args"] = args if args else None + ret[fun]["defaults"] = defaults if defaults else None + ret[fun]["varargs"] = True if varargs else None + ret[fun]["kwargs"] = True if kwargs else None return ret def split_input(val, mapper=None): - ''' + """ Take an input value and split it into a list, returning the resulting list - ''' + """ if mapper is None: mapper = lambda x: x if isinstance(val, list): return list(map(mapper, val)) try: - return list(map(mapper, [x.strip() for x in val.split(',')])) + return list(map(mapper, [x.strip() for x in val.split(",")])) except AttributeError: - return list(map(mapper, [x.strip() for x in six.text_type(val).split(',')])) + return list(map(mapper, [x.strip() for x in six.text_type(val).split(",")])) def test_mode(**kwargs): - ''' + """ Examines the kwargs passed and returns True if any kwarg which matching "Test" in any variation on capitalization (i.e. "TEST", "Test", "TeSt", etc) contains a True value (as determined by salt.utils.data.is_true). - ''' + """ # Once is_true is moved, remove this import and fix the ref below import salt.utils + for arg, value in six.iteritems(kwargs): try: - if arg.lower() == 'test' and salt.utils.data.is_true(value): + if arg.lower() == "test" and salt.utils.data.is_true(value): return True except AttributeError: continue return False -def format_call(fun, - data, - initial_ret=None, - expected_extra_kws=(), - is_class_method=None): - ''' +def format_call( + fun, data, initial_ret=None, expected_extra_kws=(), is_class_method=None +): + """ Build the required arguments and keyword arguments required for the passed function. @@ -419,17 +428,17 @@ def format_call(fun, and this is not always wanted. :returns: A dictionary with the function required arguments and keyword arguments. - ''' + """ ret = initial_ret is not None and initial_ret or {} - ret['args'] = [] - ret['kwargs'] = {} + ret["args"] = [] + ret["kwargs"] = {} aspec = get_function_argspec(fun, is_class_method=is_class_method) arg_data = arg_lookup(fun, aspec) - args = arg_data['args'] - kwargs = arg_data['kwargs'] + args = arg_data["args"] + kwargs = arg_data["kwargs"] # Since we WILL be changing the data dictionary, let's change a copy of it data = data.copy() @@ -446,23 +455,20 @@ def format_call(fun, while args: arg = args.pop(0) try: - ret['args'].append(data.pop(arg)) + ret["args"].append(data.pop(arg)) except KeyError: missing_args.append(arg) if missing_args: - used_args_count = len(ret['args']) + len(args) + used_args_count = len(ret["args"]) + len(args) args_count = used_args_count + len(missing_args) raise SaltInvocationError( - '{0} takes at least {1} argument{2} ({3} given)'.format( - fun.__name__, - args_count, - args_count > 1 and 's' or '', - used_args_count + "{0} takes at least {1} argument{2} ({3} given)".format( + fun.__name__, args_count, args_count > 1 and "s" or "", used_args_count ) ) - ret['kwargs'].update(kwargs) + ret["kwargs"].update(kwargs) if aspec.keywords: # The function accepts **kwargs, any non expected extra keyword @@ -470,7 +476,7 @@ def format_call(fun, for key, value in six.iteritems(data): if key in expected_extra_kws: continue - ret['kwargs'][key] = value + ret["kwargs"][key] = value # No need to check for extra keyword arguments since they are all # **kwargs now. Return @@ -487,25 +493,25 @@ def format_call(fun, if extra: # Found unexpected keyword arguments, raise an error to the user if len(extra) == 1: - msg = '\'{0[0]}\' is an invalid keyword argument for \'{1}\''.format( + msg = "'{0[0]}' is an invalid keyword argument for '{1}'".format( list(extra.keys()), ret.get( # In case this is being called for a state module - 'full', + "full", # Not a state module, build the name - '{0}.{1}'.format(fun.__module__, fun.__name__) - ) + "{0}.{1}".format(fun.__module__, fun.__name__), + ), ) else: - msg = '{0} and \'{1}\' are invalid keyword arguments for \'{2}\''.format( - ', '.join(['\'{0}\''.format(e) for e in extra][:-1]), + msg = "{0} and '{1}' are invalid keyword arguments for '{2}'".format( + ", ".join(["'{0}'".format(e) for e in extra][:-1]), list(extra.keys())[-1], ret.get( # In case this is being called for a state module - 'full', + "full", # Not a state module, build the name - '{0}.{1}'.format(fun.__module__, fun.__name__) - ) + "{0}.{1}".format(fun.__module__, fun.__name__), + ), ) raise SaltInvocationError(msg) @@ -513,7 +519,7 @@ def format_call(fun, def parse_function(s): - ''' + """ Parse a python-like function call syntax. For example: module.function(arg, arg, kw=arg, kw=arg) @@ -523,9 +529,9 @@ def parse_function(s): Returns a tuple of three values: function name string, arguments list and keyword arguments dictionary. - ''' + """ sh = shlex.shlex(s, posix=True) - sh.escapedquotes = '"\'' + sh.escapedquotes = "\"'" word = [] args = [] kwargs = {} @@ -533,34 +539,34 @@ def parse_function(s): key = None token = None for token in sh: - if token == '(': + if token == "(": break word.append(token) - if not word or token != '(': + if not word or token != "(": return None, None, None - fname = ''.join(word) + fname = "".join(word) word = [] good = False for token in sh: - if token in '[{(': + if token in "[{(": word.append(token) brackets.append(token) - elif (token == ',' or token == ')') and not brackets: + elif (token == "," or token == ")") and not brackets: if key: - kwargs[key] = ''.join(word) + kwargs[key] = "".join(word) elif word: - args.append(''.join(word)) - if token == ')': + args.append("".join(word)) + if token == ")": good = True break key = None word = [] - elif token in ']})': - if not brackets or token != {'[': ']', '{': '}', '(': ')'}[brackets.pop()]: + elif token in "]})": + if not brackets or token != {"[": "]", "{": "}", "(": ")"}[brackets.pop()]: break word.append(token) - elif token == '=' and not brackets: - key = ''.join(word) + elif token == "=" and not brackets: + key = "".join(word) word = [] continue else: @@ -572,7 +578,7 @@ def parse_function(s): def prepare_kwargs(all_kwargs, class_init_kwargs): - ''' + """ Filter out the kwargs used for the init of the class and the kwargs used to invoke the command required. @@ -581,7 +587,7 @@ def prepare_kwargs(all_kwargs, class_init_kwargs): class_init_kwargs The kwargs of the ``__init__`` of the class. - ''' + """ fun_kwargs = {} init_kwargs = {} for karg, warg in six.iteritems(all_kwargs): diff --git a/salt/utils/asynchronous.py b/salt/utils/asynchronous.py index 108f8d3bbed..669e9a81f37 100644 --- a/salt/utils/asynchronous.py +++ b/salt/utils/asynchronous.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" Helpers/utils for working with tornado asynchronous stuff -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import contextlib import sys -import salt.ext.tornado.ioloop import salt.ext.tornado.concurrent -import contextlib +import salt.ext.tornado.ioloop from salt.ext import six from salt.utils import zeromq @contextlib.contextmanager def current_ioloop(io_loop): - ''' + """ A context manager that will set the current ioloop to io_loop for the context - ''' + """ orig_loop = salt.ext.tornado.ioloop.IOLoop.current() io_loop.make_current() try: @@ -28,7 +28,7 @@ def current_ioloop(io_loop): class SyncWrapper(object): - ''' + """ A wrapper to make Async classes synchronous This is uses as a simple wrapper, for example: @@ -40,13 +40,14 @@ class SyncWrapper(object): sync = SyncWrapper(async_factory_method, (arg1, arg2), {'kwarg1': 'val'}) # the sync wrapper will automatically wait on the future ret = sync.async_method() - ''' + """ + def __init__(self, method, args=tuple(), kwargs=None): if kwargs is None: kwargs = {} self.io_loop = zeromq.ZMQDefaultLoop() - kwargs['io_loop'] = self.io_loop + kwargs["io_loop"] = self.io_loop with current_ioloop(self.io_loop): self.asynchronous = method(*args, **kwargs) @@ -55,10 +56,11 @@ class SyncWrapper(object): try: return object.__getattribute__(self, key) except AttributeError: - if key == 'asynchronous': + if key == "asynchronous": six.reraise(*sys.exc_info()) attr = getattr(self.asynchronous, key) - if hasattr(attr, '__call__'): + if hasattr(attr, "__call__"): + def wrap(*args, **kwargs): # Overload the ioloop for the func call-- since it might call .current() with current_ioloop(self.io_loop): @@ -66,6 +68,7 @@ class SyncWrapper(object): if isinstance(ret, salt.ext.tornado.concurrent.Future): ret = self._block_future(ret) return ret + return wrap return attr @@ -75,13 +78,13 @@ class SyncWrapper(object): return future.result() def close(self): - if hasattr(self, 'asynchronous'): - if hasattr(self.asynchronous, 'close'): + if hasattr(self, "asynchronous"): + if hasattr(self.asynchronous, "close"): # Certain things such as streams should be closed before # their associated io_loop is closed to allow for proper # cleanup. self.asynchronous.close() - elif hasattr(self.asynchronous, 'destroy'): + elif hasattr(self.asynchronous, "destroy"): # Certain things such as streams should be closed before # their associated io_loop is closed to allow for proper # cleanup. @@ -89,7 +92,7 @@ class SyncWrapper(object): del self.asynchronous self.io_loop.close() del self.io_loop - elif hasattr(self, 'io_loop'): + elif hasattr(self, "io_loop"): self.io_loop.close() del self.io_loop @@ -101,8 +104,9 @@ class SyncWrapper(object): # pylint: disable=W1701 def __del__(self): - ''' + """ On deletion of the asynchronous wrapper, make sure to clean up the asynchronous stuff - ''' + """ self.close() + # pylint: enable=W1701 diff --git a/salt/utils/atomicfile.py b/salt/utils/atomicfile.py index bcedb00df7d..c2028f84e9b 100644 --- a/salt/utils/atomicfile.py +++ b/salt/utils/atomicfile.py @@ -1,28 +1,28 @@ # -*- coding: utf-8 -*- -''' +""" A module written originally by Armin Ronacher to manage file transfers in an atomic way -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import tempfile -import sys + import errno -import time +import os import random import shutil -from salt.ext import six +import sys +import tempfile +import time # Import salt libs import salt.utils.win_dacl - +from salt.ext import six CAN_RENAME_OPEN_FILE = False -if os.name == 'nt': # pragma: no cover - _rename = lambda src, dst: False # pylint: disable=C0103 - _rename_atomic = lambda src, dst: False # pylint: disable=C0103 +if os.name == "nt": # pragma: no cover + _rename = lambda src, dst: False # pylint: disable=C0103 + _rename_atomic = lambda src, dst: False # pylint: disable=C0103 try: import ctypes @@ -41,8 +41,9 @@ if os.name == 'nt': # pragma: no cover retry = 0 rval = False while not rval and retry < 100: - rval = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING | - _MOVEFILE_WRITE_THROUGH) + rval = _MoveFileEx( + src, dst, _MOVEFILE_REPLACE_EXISTING | _MOVEFILE_WRITE_THROUGH + ) if not rval: time.sleep(0.001) retry += 1 @@ -58,16 +59,21 @@ if os.name == 'nt': # pragma: no cover CAN_RENAME_OPEN_FILE = True def _rename_atomic(src, dst): # pylint: disable=E0102 - tra = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Atomic rename') + tra = _CreateTransaction(None, 0, 0, 0, 0, 1000, "Atomic rename") if tra == -1: return False try: retry = 0 rval = False while not rval and retry < 100: - rval = _MoveFileTransacted(src, dst, None, None, - _MOVEFILE_REPLACE_EXISTING | - _MOVEFILE_WRITE_THROUGH, tra) + rval = _MoveFileTransacted( + src, + dst, + None, + None, + _MOVEFILE_REPLACE_EXISTING | _MOVEFILE_WRITE_THROUGH, + tra, + ) if rval: rval = _CommitTransaction(tra) break @@ -77,6 +83,7 @@ if os.name == 'nt': # pragma: no cover return rval finally: _CloseHandle(tra) + except Exception: # pylint: disable=broad-except pass @@ -90,22 +97,25 @@ if os.name == 'nt': # pragma: no cover except OSError as err: if err.errno != errno.EEXIST: raise - old = '{0}-{1:08x}'.format(dst, random.randint(0, sys.maxint)) + old = "{0}-{1:08x}".format(dst, random.randint(0, sys.maxint)) os.rename(dst, old) os.rename(src, dst) try: os.unlink(old) except Exception: # pylint: disable=broad-except pass + + else: atomic_rename = os.rename # pylint: disable=C0103 CAN_RENAME_OPEN_FILE = True class _AtomicWFile(object): - ''' + """ Helper class for :func:`atomic_open`. - ''' + """ + def __init__(self, fhanle, tmp_filename, filename): self._fh = fhanle self._tmp_filename = tmp_filename @@ -124,7 +134,8 @@ class _AtomicWFile(object): if os.path.isfile(self._filename): if salt.utils.win_dacl.HAS_WIN32: salt.utils.win_dacl.copy_security( - source=self._filename, target=self._tmp_filename) + source=self._filename, target=self._tmp_filename + ) else: shutil.copymode(self._filename, self._tmp_filename) st = os.stat(self._filename) @@ -142,28 +153,28 @@ class _AtomicWFile(object): pass def __repr__(self): - return '<{0} {1}{2}, mode {3}>'.format( + return "<{0} {1}{2}, mode {3}>".format( self.__class__.__name__, - self._fh.closed and 'closed ' or '', + self._fh.closed and "closed " or "", self._filename, - self._fh.mode + self._fh.mode, ) -def atomic_open(filename, mode='w'): - ''' +def atomic_open(filename, mode="w"): + """ Works like a regular `open()` but writes updates into a temporary file instead of the given file and moves it over when the file is closed. The file returned behaves as if it was a regular Python - ''' - if mode in ('r', 'rb', 'r+', 'rb+', 'a', 'ab'): - raise TypeError('Read or append modes don\'t work with atomic_open') + """ + if mode in ("r", "rb", "r+", "rb+", "a", "ab"): + raise TypeError("Read or append modes don't work with atomic_open") kwargs = { - 'prefix': '.___atomic_write', - 'dir': os.path.dirname(filename), - 'delete': False, + "prefix": ".___atomic_write", + "dir": os.path.dirname(filename), + "delete": False, } - if six.PY3 and 'b' not in mode: - kwargs['newline'] = '' + if six.PY3 and "b" not in mode: + kwargs["newline"] = "" ntf = tempfile.NamedTemporaryFile(mode, **kwargs) return _AtomicWFile(ntf, ntf.name, filename) diff --git a/salt/utils/aws.py b/salt/utils/aws.py index cad91d8a9d1..429e30ee0be 100644 --- a/salt/utils/aws.py +++ b/salt/utils/aws.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection library for AWS .. versionadded:: 2015.5.0 @@ -7,59 +7,64 @@ Connection library for AWS This is a base library used by a number of AWS services. :depends: requests -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import binascii +import hashlib +import hmac +import logging +import random +import re + # Import Python libs import sys import time -import binascii from datetime import datetime -import hashlib -import hmac -import logging + import salt.config -import re -import random -from salt.ext import six # Import Salt libs import salt.utils.hashutils import salt.utils.xmlutil as xml from salt._compat import ElementTree as ET +from salt.ext import six + +# pylint: disable=import-error,redefined-builtin,no-name-in-module +from salt.ext.six.moves import map, range, zip +from salt.ext.six.moves.urllib.parse import urlencode, urlparse # Import 3rd-party libs try: import requests + HAS_REQUESTS = True # pylint: disable=W0612 except ImportError: HAS_REQUESTS = False # pylint: disable=W0612 -# pylint: disable=import-error,redefined-builtin,no-name-in-module -from salt.ext.six.moves import map, range, zip -from salt.ext.six.moves.urllib.parse import urlencode, urlparse + # pylint: enable=import-error,redefined-builtin,no-name-in-module log = logging.getLogger(__name__) -DEFAULT_LOCATION = 'us-east-1' -DEFAULT_AWS_API_VERSION = '2014-10-01' +DEFAULT_LOCATION = "us-east-1" +DEFAULT_AWS_API_VERSION = "2014-10-01" AWS_RETRY_CODES = [ - 'RequestLimitExceeded', - 'InsufficientInstanceCapacity', - 'InternalError', - 'Unavailable', - 'InsufficientAddressCapacity', - 'InsufficientReservedInstanceCapacity', + "RequestLimitExceeded", + "InsufficientInstanceCapacity", + "InternalError", + "Unavailable", + "InsufficientAddressCapacity", + "InsufficientReservedInstanceCapacity", ] AWS_METADATA_TIMEOUT = 3.05 AWS_MAX_RETRIES = 7 -IROLE_CODE = 'use-instance-role-credentials' -__AccessKeyId__ = '' -__SecretAccessKey__ = '' -__Token__ = '' -__Expiration__ = '' -__Location__ = '' +IROLE_CODE = "use-instance-role-credentials" +__AccessKeyId__ = "" +__SecretAccessKey__ = "" +__Token__ = "" +__Expiration__ = "" +__Location__ = "" __AssumeCache__ = {} @@ -75,16 +80,16 @@ def sleep_exponential_backoff(attempts): A failure rate of >10% is observed when using the salt-api with an asynchronous client specified (runner_async). """ - time.sleep(random.uniform(1, 2**attempts)) + time.sleep(random.uniform(1, 2 ** attempts)) def creds(provider): - ''' + """ Return the credentials for AWS signing. This could be just the id and key specified in the provider configuration, or if the id or key is set to the literal string 'use-instance-role-credentials' creds will pull the instance role credentials from the meta data, cache them, and provide them instead. - ''' + """ # Declare globals global __AccessKeyId__, __SecretAccessKey__, __Token__, __Expiration__ @@ -92,11 +97,11 @@ def creds(provider): # if id or key is 'use-instance-role-credentials', pull them from meta-data ## if needed - if provider['id'] == IROLE_CODE or provider['key'] == IROLE_CODE: + if provider["id"] == IROLE_CODE or provider["key"] == IROLE_CODE: # Check to see if we have cache credentials that are still good - if __Expiration__ != '': + if __Expiration__ != "": timenow = datetime.utcnow() - timestamp = timenow.strftime('%Y-%m-%dT%H:%M:%SZ') + timestamp = timenow.strftime("%Y-%m-%dT%H:%M:%SZ") if timestamp < __Expiration__: # Current timestamp less than expiration fo cached credentials return __AccessKeyId__, __SecretAccessKey__, __Token__ @@ -106,77 +111,81 @@ def creds(provider): try: result = requests.get( "http://169.254.169.254/latest/meta-data/iam/security-credentials/", - proxies={'http': ''}, timeout=AWS_METADATA_TIMEOUT, + proxies={"http": ""}, + timeout=AWS_METADATA_TIMEOUT, ) result.raise_for_status() role = result.text except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError): - return provider['id'], provider['key'], '' + return provider["id"], provider["key"], "" try: result = requests.get( - "http://169.254.169.254/latest/meta-data/iam/security-credentials/{0}".format(role), - proxies={'http': ''}, timeout=AWS_METADATA_TIMEOUT, + "http://169.254.169.254/latest/meta-data/iam/security-credentials/{0}".format( + role + ), + proxies={"http": ""}, + timeout=AWS_METADATA_TIMEOUT, ) result.raise_for_status() except (requests.exceptions.HTTPError, requests.exceptions.ConnectionError): - return provider['id'], provider['key'], '' + return provider["id"], provider["key"], "" data = result.json() - __AccessKeyId__ = data['AccessKeyId'] - __SecretAccessKey__ = data['SecretAccessKey'] - __Token__ = data['Token'] - __Expiration__ = data['Expiration'] + __AccessKeyId__ = data["AccessKeyId"] + __SecretAccessKey__ = data["SecretAccessKey"] + __Token__ = data["Token"] + __Expiration__ = data["Expiration"] ret_credentials = __AccessKeyId__, __SecretAccessKey__, __Token__ else: - ret_credentials = provider['id'], provider['key'], '' + ret_credentials = provider["id"], provider["key"], "" - if provider.get('role_arn') is not None: + if provider.get("role_arn") is not None: provider_shadow = provider.copy() provider_shadow.pop("role_arn", None) - log.info("Assuming the role: %s", provider.get('role_arn')) - ret_credentials = assumed_creds(provider_shadow, role_arn=provider.get('role_arn'), location='us-east-1') + log.info("Assuming the role: %s", provider.get("role_arn")) + ret_credentials = assumed_creds( + provider_shadow, role_arn=provider.get("role_arn"), location="us-east-1" + ) return ret_credentials def sig2(method, endpoint, params, provider, aws_api_version): - ''' + """ Sign a query against AWS services using Signature Version 2 Signing Process. This is documented at: http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html - ''' + """ timenow = datetime.utcnow() - timestamp = timenow.strftime('%Y-%m-%dT%H:%M:%SZ') + timestamp = timenow.strftime("%Y-%m-%dT%H:%M:%SZ") # Retrieve access credentials from meta-data, or use provided access_key_id, secret_access_key, token = creds(provider) params_with_headers = params.copy() - params_with_headers['AWSAccessKeyId'] = access_key_id - params_with_headers['SignatureVersion'] = '2' - params_with_headers['SignatureMethod'] = 'HmacSHA256' - params_with_headers['Timestamp'] = '{0}'.format(timestamp) - params_with_headers['Version'] = aws_api_version + params_with_headers["AWSAccessKeyId"] = access_key_id + params_with_headers["SignatureVersion"] = "2" + params_with_headers["SignatureMethod"] = "HmacSHA256" + params_with_headers["Timestamp"] = "{0}".format(timestamp) + params_with_headers["Version"] = aws_api_version keys = sorted(params_with_headers.keys()) values = list(list(map(params_with_headers.get, keys))) querystring = urlencode(list(zip(keys, values))) - canonical = '{0}\n{1}\n/\n{2}'.format( - method.encode('utf-8'), - endpoint.encode('utf-8'), - querystring.encode('utf-8'), + canonical = "{0}\n{1}\n/\n{2}".format( + method.encode("utf-8"), endpoint.encode("utf-8"), querystring.encode("utf-8"), ) hashed = hmac.new(secret_access_key, canonical, hashlib.sha256) sig = binascii.b2a_base64(hashed.digest()) - params_with_headers['Signature'] = sig.strip() + params_with_headers["Signature"] = sig.strip() # Add in security token if we have one - if token != '': - params_with_headers['SecurityToken'] = token + if token != "": + params_with_headers["SecurityToken"] = token return params_with_headers @@ -196,34 +205,34 @@ def assumed_creds(prov_dict, role_arn, location=None): return c["AccessKeyId"], c["SecretAccessKey"], c["SessionToken"] version = "2011-06-15" - session_name = valid_session_name_re.sub('', salt.config.get_id({"root_dir": None})[0])[0:63] + session_name = valid_session_name_re.sub( + "", salt.config.get_id({"root_dir": None})[0] + )[0:63] headers, requesturl = sig4( - 'GET', - 'sts.amazonaws.com', + "GET", + "sts.amazonaws.com", params={ "Version": version, "Action": "AssumeRole", "RoleSessionName": session_name, "RoleArn": role_arn, "Policy": '{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1", "Effect":"Allow","Action":"*","Resource":"*"}]}', - "DurationSeconds": "3600" + "DurationSeconds": "3600", }, aws_api_version=version, - data='', - uri='/', + data="", + uri="/", prov_dict=prov_dict, - product='sts', + product="sts", location=location, - requesturl="https://sts.amazonaws.com/" + requesturl="https://sts.amazonaws.com/", ) headers["Accept"] = "application/json" - result = requests.request('GET', requesturl, headers=headers, - data='', - verify=True) + result = requests.request("GET", requesturl, headers=headers, data="", verify=True) if result.status_code >= 400: - log.info('AssumeRole response: %s', result.content) + log.info("AssumeRole response: %s", result.content) result.raise_for_status() resp = result.json() @@ -232,25 +241,38 @@ def assumed_creds(prov_dict, role_arn, location=None): return data["AccessKeyId"], data["SecretAccessKey"], data["SessionToken"] -def sig4(method, endpoint, params, prov_dict, - aws_api_version=DEFAULT_AWS_API_VERSION, location=None, - product='ec2', uri='/', requesturl=None, data='', headers=None, - role_arn=None, payload_hash=None): - ''' +def sig4( + method, + endpoint, + params, + prov_dict, + aws_api_version=DEFAULT_AWS_API_VERSION, + location=None, + product="ec2", + uri="/", + requesturl=None, + data="", + headers=None, + role_arn=None, + payload_hash=None, +): + """ Sign a query against AWS services using Signature Version 4 Signing Process. This is documented at: http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html http://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html - ''' + """ timenow = datetime.utcnow() # Retrieve access credentials from meta-data, or use provided if role_arn is None: access_key_id, secret_access_key, token = creds(prov_dict) else: - access_key_id, secret_access_key, token = assumed_creds(prov_dict, role_arn, location=location) + access_key_id, secret_access_key, token = assumed_creds( + prov_dict, role_arn, location=location + ) if location is None: location = get_region_from_metadata() @@ -258,14 +280,14 @@ def sig4(method, endpoint, params, prov_dict, location = DEFAULT_LOCATION params_with_headers = params.copy() - if product not in ('s3', 'ssm'): - params_with_headers['Version'] = aws_api_version + if product not in ("s3", "ssm"): + params_with_headers["Version"] = aws_api_version keys = sorted(params_with_headers.keys()) values = list(map(params_with_headers.get, keys)) - querystring = urlencode(list(zip(keys, values))).replace('+', '%20') + querystring = urlencode(list(zip(keys, values))).replace("+", "%20") - amzdate = timenow.strftime('%Y%m%dT%H%M%SZ') - datestamp = timenow.strftime('%Y%m%d') + amzdate = timenow.strftime("%Y%m%dT%H%M%SZ") + datestamp = timenow.strftime("%Y%m%d") new_headers = {} if isinstance(headers, dict): new_headers = headers.copy() @@ -275,103 +297,100 @@ def sig4(method, endpoint, params, prov_dict, if not payload_hash: payload_hash = salt.utils.hashutils.sha256_digest(data) - new_headers['X-Amz-date'] = amzdate - new_headers['host'] = endpoint - new_headers['x-amz-content-sha256'] = payload_hash + new_headers["X-Amz-date"] = amzdate + new_headers["host"] = endpoint + new_headers["x-amz-content-sha256"] = payload_hash a_canonical_headers = [] a_signed_headers = [] - if token != '': - new_headers['X-Amz-security-token'] = token + if token != "": + new_headers["X-Amz-security-token"] = token for header in sorted(new_headers.keys(), key=six.text_type.lower): lower_header = header.lower() - a_canonical_headers.append('{0}:{1}'.format(lower_header, new_headers[header].strip())) + a_canonical_headers.append( + "{0}:{1}".format(lower_header, new_headers[header].strip()) + ) a_signed_headers.append(lower_header) - canonical_headers = '\n'.join(a_canonical_headers) + '\n' - signed_headers = ';'.join(a_signed_headers) + canonical_headers = "\n".join(a_canonical_headers) + "\n" + signed_headers = ";".join(a_signed_headers) - algorithm = 'AWS4-HMAC-SHA256' + algorithm = "AWS4-HMAC-SHA256" # Combine elements to create create canonical request - canonical_request = '\n'.join(( - method, - uri, - querystring, - canonical_headers, - signed_headers, - payload_hash - )) + canonical_request = "\n".join( + (method, uri, querystring, canonical_headers, signed_headers, payload_hash) + ) # Create the string to sign - credential_scope = '/'.join((datestamp, location, product, 'aws4_request')) - string_to_sign = '\n'.join(( - algorithm, - amzdate, - credential_scope, - salt.utils.hashutils.sha256_digest(canonical_request) - )) + credential_scope = "/".join((datestamp, location, product, "aws4_request")) + string_to_sign = "\n".join( + ( + algorithm, + amzdate, + credential_scope, + salt.utils.hashutils.sha256_digest(canonical_request), + ) + ) # Create the signing key using the function defined above. - signing_key = _sig_key( - secret_access_key, - datestamp, - location, - product - ) + signing_key = _sig_key(secret_access_key, datestamp, location, product) # Sign the string_to_sign using the signing_key signature = hmac.new( - signing_key, - string_to_sign.encode('utf-8'), - hashlib.sha256).hexdigest() + signing_key, string_to_sign.encode("utf-8"), hashlib.sha256 + ).hexdigest() # Add signing information to the request authorization_header = ( - '{0} Credential={1}/{2}, SignedHeaders={3}, Signature={4}' - ).format( - algorithm, - access_key_id, - credential_scope, - signed_headers, - signature, - ) + "{0} Credential={1}/{2}, SignedHeaders={3}, Signature={4}" + ).format(algorithm, access_key_id, credential_scope, signed_headers, signature,) - new_headers['Authorization'] = authorization_header + new_headers["Authorization"] = authorization_header - requesturl = '{0}?{1}'.format(requesturl, querystring) + requesturl = "{0}?{1}".format(requesturl, querystring) return new_headers, requesturl def _sign(key, msg): - ''' + """ Key derivation functions. See: http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python - ''' - return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest() + """ + return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest() def _sig_key(key, date_stamp, regionName, serviceName): - ''' + """ Get a signature key. See: http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-python - ''' - kDate = _sign(('AWS4' + key).encode('utf-8'), date_stamp) + """ + kDate = _sign(("AWS4" + key).encode("utf-8"), date_stamp) if regionName: kRegion = _sign(kDate, regionName) kService = _sign(kRegion, serviceName) else: kService = _sign(kDate, serviceName) - kSigning = _sign(kService, 'aws4_request') + kSigning = _sign(kService, "aws4_request") return kSigning -def query(params=None, setname=None, requesturl=None, location=None, - return_url=False, return_root=False, opts=None, provider=None, - endpoint=None, product='ec2', sigver='2'): - ''' +def query( + params=None, + setname=None, + requesturl=None, + location=None, + return_url=False, + return_root=False, + opts=None, + provider=None, + endpoint=None, + product="ec2", + sigver="2", +): + """ Perform a query against AWS services using Signature Version 2 Signing Process. This is documented at: @@ -399,15 +418,15 @@ def query(params=None, setname=None, requesturl=None, location=None, - simpledb (SimpleDB) - sns (Simple Notification Service) - sqs (Simple Queue Service) - ''' + """ if params is None: params = {} if opts is None: opts = {} - function = opts.get('function', (None, product)) - providers = opts.get('providers', {}) + function = opts.get("function", (None, product)) + providers = opts.get("providers", {}) if provider is None: prov_dict = providers.get(function[1], {}).get(product, {}) @@ -417,7 +436,7 @@ def query(params=None, setname=None, requesturl=None, location=None, else: prov_dict = providers.get(provider, {}).get(product, {}) - service_url = prov_dict.get('service_url', 'amazonaws.com') + service_url = prov_dict.get("service_url", "amazonaws.com") if not location: location = get_location(opts, prov_dict) @@ -425,61 +444,62 @@ def query(params=None, setname=None, requesturl=None, location=None, if endpoint is None: if not requesturl: endpoint = prov_dict.get( - 'endpoint', - '{0}.{1}.{2}'.format(product, location, service_url) + "endpoint", "{0}.{1}.{2}".format(product, location, service_url) ) - requesturl = 'https://{0}/'.format(endpoint) + requesturl = "https://{0}/".format(endpoint) else: endpoint = urlparse(requesturl).netloc - if endpoint == '': - endpoint_err = ('Could not find a valid endpoint in the ' - 'requesturl: {0}. Looking for something ' - 'like https://some.aws.endpoint/?args').format( - requesturl - ) + if endpoint == "": + endpoint_err = ( + "Could not find a valid endpoint in the " + "requesturl: {0}. Looking for something " + "like https://some.aws.endpoint/?args" + ).format(requesturl) log.error(endpoint_err) if return_url is True: - return {'error': endpoint_err}, requesturl - return {'error': endpoint_err} + return {"error": endpoint_err}, requesturl + return {"error": endpoint_err} - log.debug('Using AWS endpoint: %s', endpoint) - method = 'GET' + log.debug("Using AWS endpoint: %s", endpoint) + method = "GET" aws_api_version = prov_dict.get( - 'aws_api_version', prov_dict.get( - '{0}_api_version'.format(product), - DEFAULT_AWS_API_VERSION - ) + "aws_api_version", + prov_dict.get("{0}_api_version".format(product), DEFAULT_AWS_API_VERSION), ) # Fallback to ec2's id & key if none is found, for this component - if not prov_dict.get('id', None): - prov_dict['id'] = providers.get(provider, {}).get('ec2', {}).get('id', {}) - prov_dict['key'] = providers.get(provider, {}).get('ec2', {}).get('key', {}) + if not prov_dict.get("id", None): + prov_dict["id"] = providers.get(provider, {}).get("ec2", {}).get("id", {}) + prov_dict["key"] = providers.get(provider, {}).get("ec2", {}).get("key", {}) - if sigver == '4': + if sigver == "4": headers, requesturl = sig4( - method, endpoint, params, prov_dict, aws_api_version, location, product, requesturl=requesturl + method, + endpoint, + params, + prov_dict, + aws_api_version, + location, + product, + requesturl=requesturl, ) params_with_headers = {} else: - params_with_headers = sig2( - method, endpoint, params, prov_dict, aws_api_version - ) + params_with_headers = sig2(method, endpoint, params, prov_dict, aws_api_version) headers = {} attempts = 0 while attempts < AWS_MAX_RETRIES: - log.debug('AWS Request: %s', requesturl) - log.trace('AWS Request Parameters: %s', params_with_headers) + log.debug("AWS Request: %s", requesturl) + log.trace("AWS Request Parameters: %s", params_with_headers) try: - result = requests.get(requesturl, headers=headers, params=params_with_headers) - log.debug('AWS Response Status Code: %s', result.status_code) - log.trace( - 'AWS Response Text: %s', - result.text + result = requests.get( + requesturl, headers=headers, params=params_with_headers ) + log.debug("AWS Response Status Code: %s", result.status_code) + log.trace("AWS Response Text: %s", result.text) result.raise_for_status() break except requests.exceptions.HTTPError as exc: @@ -487,32 +507,39 @@ def query(params=None, setname=None, requesturl=None, location=None, data = xml.to_dict(root) # check to see if we should retry the query - err_code = data.get('Errors', {}).get('Error', {}).get('Code', '') + err_code = data.get("Errors", {}).get("Error", {}).get("Code", "") if attempts < AWS_MAX_RETRIES and err_code and err_code in AWS_RETRY_CODES: attempts += 1 log.error( - 'AWS Response Status Code and Error: [%s %s] %s; ' - 'Attempts remaining: %s', - exc.response.status_code, exc, data, attempts + "AWS Response Status Code and Error: [%s %s] %s; " + "Attempts remaining: %s", + exc.response.status_code, + exc, + data, + attempts, ) sleep_exponential_backoff(attempts) continue log.error( - 'AWS Response Status Code and Error: [%s %s] %s', - exc.response.status_code, exc, data + "AWS Response Status Code and Error: [%s %s] %s", + exc.response.status_code, + exc, + data, ) if return_url is True: - return {'error': data}, requesturl - return {'error': data} + return {"error": data}, requesturl + return {"error": data} else: log.error( - 'AWS Response Status Code and Error: [%s %s] %s', - exc.response.status_code, exc, data + "AWS Response Status Code and Error: [%s %s] %s", + exc.response.status_code, + exc, + data, ) if return_url is True: - return {'error': data}, requesturl - return {'error': data} + return {"error": data}, requesturl + return {"error": data} root = ET.fromstring(result.text) items = root[1] @@ -526,7 +553,7 @@ def query(params=None, setname=None, requesturl=None, location=None, children_len = len(root) for item in range(0, children_len): - comps = root[item].tag.split('}') + comps = root[item].tag.split("}") if comps[1] == setname: items = root[item] @@ -541,57 +568,60 @@ def query(params=None, setname=None, requesturl=None, location=None, def get_region_from_metadata(): - ''' + """ Try to get region from instance identity document and cache it .. versionadded:: 2015.5.6 - ''' + """ global __Location__ - if __Location__ == 'do-not-get-from-metadata': - log.debug('Previously failed to get AWS region from metadata. Not trying again.') + if __Location__ == "do-not-get-from-metadata": + log.debug( + "Previously failed to get AWS region from metadata. Not trying again." + ) return None # Cached region - if __Location__ != '': + if __Location__ != "": return __Location__ try: # Connections to instance meta-data must fail fast and never be proxied result = requests.get( "http://169.254.169.254/latest/dynamic/instance-identity/document", - proxies={'http': ''}, timeout=AWS_METADATA_TIMEOUT, + proxies={"http": ""}, + timeout=AWS_METADATA_TIMEOUT, ) except requests.exceptions.RequestException: - log.warning('Failed to get AWS region from instance metadata.', exc_info=True) + log.warning("Failed to get AWS region from instance metadata.", exc_info=True) # Do not try again - __Location__ = 'do-not-get-from-metadata' + __Location__ = "do-not-get-from-metadata" return None try: - region = result.json()['region'] + region = result.json()["region"] __Location__ = region return __Location__ except (ValueError, KeyError): - log.warning('Failed to decode JSON from instance metadata.') + log.warning("Failed to decode JSON from instance metadata.") return None return None def get_location(opts=None, provider=None): - ''' + """ Return the region to use, in this order: opts['location'] provider['location'] get_region_from_metadata() DEFAULT_LOCATION - ''' + """ if opts is None: opts = {} - ret = opts.get('location') + ret = opts.get("location") if ret is None and provider is not None: - ret = provider.get('location') + ret = provider.get("location") if ret is None: ret = get_region_from_metadata() if ret is None: diff --git a/salt/utils/azurearm.py b/salt/utils/azurearm.py index ecb4962c6d3..2dbcfe4ec6c 100644 --- a/salt/utils/azurearm.py +++ b/salt/utils/azurearm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Azure (ARM) Utilities .. versionadded:: 2019.2.0 @@ -19,13 +19,14 @@ Azure (ARM) Utilities * `msrestazure <https://pypi.python.org/pypi/msrestazure>`_ >= 0.4.21 :platform: linux -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -from operator import itemgetter + import importlib import logging import sys +from operator import itemgetter # Import Salt libs import salt.config @@ -33,9 +34,8 @@ import salt.ext.six as six import salt.loader import salt.utils.stringutils import salt.version -from salt.exceptions import ( - SaltInvocationError, SaltSystemExit -) +from salt.exceptions import SaltInvocationError, SaltSystemExit + try: from salt.ext.six.moves import range as six_range except ImportError: @@ -47,19 +47,18 @@ try: UserPassCredentials, ServicePrincipalCredentials, ) - from msrestazure.azure_active_directory import ( - MSIAuthentication - ) + from msrestazure.azure_active_directory import MSIAuthentication from msrestazure.azure_cloud import ( MetadataEndpointError, get_cloud_from_metadata_endpoint, ) from msrestazure.azure_exceptions import CloudError + HAS_AZURE = True except ImportError: HAS_AZURE = False -__opts__ = salt.config.minion_config('/etc/salt/minion') +__opts__ = salt.config.minion_config("/etc/salt/minion") __salt__ = salt.loader.minion_mods(__opts__) log = logging.getLogger(__name__) @@ -73,114 +72,121 @@ def __virtual__(): def _determine_auth(**kwargs): - ''' + """ Acquire Azure ARM Credentials - ''' - if 'profile' in kwargs: - azure_credentials = __salt__['config.option'](kwargs['profile']) + """ + if "profile" in kwargs: + azure_credentials = __salt__["config.option"](kwargs["profile"]) kwargs.update(azure_credentials) - service_principal_creds_kwargs = ['client_id', 'secret', 'tenant'] - user_pass_creds_kwargs = ['username', 'password'] + service_principal_creds_kwargs = ["client_id", "secret", "tenant"] + user_pass_creds_kwargs = ["username", "password"] try: - if kwargs.get('cloud_environment') and kwargs.get('cloud_environment').startswith('http'): - cloud_env = get_cloud_from_metadata_endpoint(kwargs['cloud_environment']) + if kwargs.get("cloud_environment") and kwargs.get( + "cloud_environment" + ).startswith("http"): + cloud_env = get_cloud_from_metadata_endpoint(kwargs["cloud_environment"]) else: - cloud_env_module = importlib.import_module('msrestazure.azure_cloud') - cloud_env = getattr(cloud_env_module, kwargs.get('cloud_environment', 'AZURE_PUBLIC_CLOUD')) + cloud_env_module = importlib.import_module("msrestazure.azure_cloud") + cloud_env = getattr( + cloud_env_module, kwargs.get("cloud_environment", "AZURE_PUBLIC_CLOUD") + ) except (AttributeError, ImportError, MetadataEndpointError): - raise sys.exit('The Azure cloud environment {0} is not available.'.format(kwargs['cloud_environment'])) + raise sys.exit( + "The Azure cloud environment {0} is not available.".format( + kwargs["cloud_environment"] + ) + ) if set(service_principal_creds_kwargs).issubset(kwargs): - if not (kwargs['client_id'] and kwargs['secret'] and kwargs['tenant']): + if not (kwargs["client_id"] and kwargs["secret"] and kwargs["tenant"]): raise SaltInvocationError( - 'The client_id, secret, and tenant parameters must all be ' - 'populated if using service principals.' + "The client_id, secret, and tenant parameters must all be " + "populated if using service principals." ) else: - credentials = ServicePrincipalCredentials(kwargs['client_id'], - kwargs['secret'], - tenant=kwargs['tenant'], - cloud_environment=cloud_env) + credentials = ServicePrincipalCredentials( + kwargs["client_id"], + kwargs["secret"], + tenant=kwargs["tenant"], + cloud_environment=cloud_env, + ) elif set(user_pass_creds_kwargs).issubset(kwargs): - if not (kwargs['username'] and kwargs['password']): + if not (kwargs["username"] and kwargs["password"]): raise SaltInvocationError( - 'The username and password parameters must both be ' - 'populated if using username/password authentication.' + "The username and password parameters must both be " + "populated if using username/password authentication." ) else: - credentials = UserPassCredentials(kwargs['username'], - kwargs['password'], - cloud_environment=cloud_env) - elif 'subscription_id' in kwargs: + credentials = UserPassCredentials( + kwargs["username"], kwargs["password"], cloud_environment=cloud_env + ) + elif "subscription_id" in kwargs: credentials = MSIAuthentication(cloud_environment=cloud_env) else: raise SaltInvocationError( - 'Unable to determine credentials. ' - 'A subscription_id with username and password, ' - 'or client_id, secret, and tenant or a profile with the ' - 'required parameters populated' + "Unable to determine credentials. " + "A subscription_id with username and password, " + "or client_id, secret, and tenant or a profile with the " + "required parameters populated" ) - if 'subscription_id' not in kwargs: - raise SaltInvocationError( - 'A subscription_id must be specified' - ) + if "subscription_id" not in kwargs: + raise SaltInvocationError("A subscription_id must be specified") - subscription_id = salt.utils.stringutils.to_str(kwargs['subscription_id']) + subscription_id = salt.utils.stringutils.to_str(kwargs["subscription_id"]) return credentials, subscription_id, cloud_env def get_client(client_type, **kwargs): - ''' + """ Dynamically load the selected client and return a management client object - ''' - client_map = {'compute': 'ComputeManagement', - 'authorization': 'AuthorizationManagement', - 'dns': 'DnsManagement', - 'storage': 'StorageManagement', - 'managementlock': 'ManagementLock', - 'monitor': 'MonitorManagement', - 'network': 'NetworkManagement', - 'policy': 'Policy', - 'resource': 'ResourceManagement', - 'subscription': 'Subscription', - 'web': 'WebSiteManagement'} + """ + client_map = { + "compute": "ComputeManagement", + "authorization": "AuthorizationManagement", + "dns": "DnsManagement", + "storage": "StorageManagement", + "managementlock": "ManagementLock", + "monitor": "MonitorManagement", + "network": "NetworkManagement", + "policy": "Policy", + "resource": "ResourceManagement", + "subscription": "Subscription", + "web": "WebSiteManagement", + } if client_type not in client_map: raise SaltSystemExit( - 'The Azure ARM client_type {0} specified can not be found.'.format( - client_type) + "The Azure ARM client_type {0} specified can not be found.".format( + client_type + ) ) map_value = client_map[client_type] - if client_type in ['policy', 'subscription']: - module_name = 'resource' - elif client_type in ['managementlock']: - module_name = 'resource.locks' + if client_type in ["policy", "subscription"]: + module_name = "resource" + elif client_type in ["managementlock"]: + module_name = "resource.locks" else: module_name = client_type try: - client_module = importlib.import_module('azure.mgmt.'+module_name) + client_module = importlib.import_module("azure.mgmt." + module_name) # pylint: disable=invalid-name - Client = getattr(client_module, - '{0}Client'.format(map_value)) + Client = getattr(client_module, "{0}Client".format(map_value)) except ImportError: - raise sys.exit( - 'The azure {0} client is not available.'.format(client_type) - ) + raise sys.exit("The azure {0} client is not available.".format(client_type)) credentials, subscription_id, cloud_env = _determine_auth(**kwargs) - if client_type == 'subscription': + if client_type == "subscription": client = Client( - credentials=credentials, - base_url=cloud_env.endpoints.resource_manager, + credentials=credentials, base_url=cloud_env.endpoints.resource_manager, ) else: client = Client( @@ -189,33 +195,31 @@ def get_client(client_type, **kwargs): base_url=cloud_env.endpoints.resource_manager, ) - client.config.add_user_agent('Salt/{0}'.format(salt.version.__version__)) + client.config.add_user_agent("Salt/{0}".format(salt.version.__version__)) return client def log_cloud_error(client, message, **kwargs): - ''' + """ Log an azurearm cloud error exception - ''' + """ try: - cloud_logger = getattr(log, kwargs.get('azurearm_log_level')) + cloud_logger = getattr(log, kwargs.get("azurearm_log_level")) except (AttributeError, TypeError): - cloud_logger = getattr(log, 'error') + cloud_logger = getattr(log, "error") cloud_logger( - 'An AzureARM %s CloudError has occurred: %s', - client.capitalize(), - message + "An AzureARM %s CloudError has occurred: %s", client.capitalize(), message ) return def paged_object_to_list(paged_object): - ''' + """ Extract all pages within a paged object as a list of dictionaries - ''' + """ paged_return = [] while True: try: @@ -230,42 +234,51 @@ def paged_object_to_list(paged_object): def create_object_model(module_name, object_name, **kwargs): - ''' + """ Assemble an object from incoming parameters. - ''' + """ object_kwargs = {} try: - model_module = importlib.import_module('azure.mgmt.{0}.models'.format(module_name)) + model_module = importlib.import_module( + "azure.mgmt.{0}.models".format(module_name) + ) # pylint: disable=invalid-name Model = getattr(model_module, object_name) except ImportError: raise sys.exit( - 'The {0} model in the {1} Azure module is not available.'.format(object_name, module_name) + "The {0} model in the {1} Azure module is not available.".format( + object_name, module_name + ) ) - if '_attribute_map' in dir(Model): + if "_attribute_map" in dir(Model): for attr, items in Model._attribute_map.items(): param = kwargs.get(attr) - if param: - if items['type'][0].isupper() and isinstance(param, dict): - object_kwargs[attr] = create_object_model(module_name, items['type'], **param) - elif items['type'][0] == '{' and isinstance(param, dict): + if param is not None: + if items["type"][0].isupper() and isinstance(param, dict): + object_kwargs[attr] = create_object_model( + module_name, items["type"], **param + ) + elif items["type"][0] == "{" and isinstance(param, dict): object_kwargs[attr] = param - elif items['type'][0] == '[' and isinstance(param, list): + elif items["type"][0] == "[" and isinstance(param, list): obj_list = [] for list_item in param: - if items['type'][1].isupper() and isinstance(list_item, dict): + if items["type"][1].isupper() and isinstance(list_item, dict): obj_list.append( create_object_model( module_name, - items['type'][items['type'].index('[')+1:items['type'].rindex(']')], + items["type"][ + items["type"].index("[") + + 1 : items["type"].rindex("]") + ], **list_item ) ) - elif items['type'][1] == '{' and isinstance(list_item, dict): + elif items["type"][1] == "{" and isinstance(list_item, dict): obj_list.append(list_item) - elif not items['type'][1].isupper() and items['type'][1] != '{': + elif not items["type"][1].isupper() and items["type"][1] != "{": obj_list.append(list_item) object_kwargs[attr] = obj_list else: @@ -276,43 +289,44 @@ def create_object_model(module_name, object_name, **kwargs): def compare_list_of_dicts(old, new, convert_id_to_name=None): - ''' + """ Compare lists of dictionaries representing Azure objects. Only keys found in the "new" dictionaries are compared to the "old" dictionaries, since getting Azure objects from the API returns some read-only data which should not be used in the comparison. A list of parameter names can be passed in order to compare a bare object name to a full Azure ID path for brevity. If string types are found in values, comparison is case insensitive. Return comment should be used to trigger exit from the calling function. - ''' + """ ret = {} if not convert_id_to_name: convert_id_to_name = [] if not isinstance(new, list): - ret['comment'] = 'must be provided as a list of dictionaries!' + ret["comment"] = "must be provided as a list of dictionaries!" return ret if len(new) != len(old): - ret['changes'] = { - 'old': old, - 'new': new - } + ret["changes"] = {"old": old, "new": new} return ret try: - local_configs, remote_configs = [sorted(config, key=itemgetter('name')) for config in (new, old)] + local_configs, remote_configs = [ + sorted(config, key=itemgetter("name")) for config in (new, old) + ] except TypeError: - ret['comment'] = 'configurations must be provided as a list of dictionaries!' + ret["comment"] = "configurations must be provided as a list of dictionaries!" return ret except KeyError: - ret['comment'] = 'configuration dictionaries must contain the "name" key!' + ret["comment"] = 'configuration dictionaries must contain the "name" key!' return ret for idx in six_range(0, len(local_configs)): for key in local_configs[idx]: local_val = local_configs[idx][key] if key in convert_id_to_name: - remote_val = remote_configs[idx].get(key, {}).get('id', '').split('/')[-1] + remote_val = ( + remote_configs[idx].get(key, {}).get("id", "").split("/")[-1] + ) else: remote_val = remote_configs[idx].get(key) if isinstance(local_val, six.string_types): @@ -320,10 +334,7 @@ def compare_list_of_dicts(old, new, convert_id_to_name=None): if isinstance(remote_val, six.string_types): remote_val = remote_val.lower() if local_val != remote_val: - ret['changes'] = { - 'old': remote_configs, - 'new': local_configs - } + ret["changes"] = {"old": remote_configs, "new": local_configs} return ret return ret diff --git a/salt/utils/boto3_elasticsearch.py b/salt/utils/boto3_elasticsearch.py index 0aa84f45edb..6803c76b175 100644 --- a/salt/utils/boto3_elasticsearch.py +++ b/salt/utils/boto3_elasticsearch.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Botocore waiters for elasticsearch that are not present in boto3+botocore (yet). :codeauthor: Herbert Buurman <herbert.buurman@ogd.nl> :depends: boto3 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.utils.versions + # Import Salt libs from salt.exceptions import SaltInvocationError -import salt.utils.versions try: import botocore.waiter @@ -19,76 +20,87 @@ except ImportError: WAITER_CONFIGS = { - 'ESDomainAvailable': { - 'delay': 60, - 'operation': 'DescribeElasticsearchDomainConfig', - 'maxAttempts': 60, - 'acceptors': [{ - 'expected': 'Active', - 'matcher': 'path', - 'state': 'success', - 'argument': 'DomainConfig.ElasticsearchClusterConfig.Status.State', - }, { - 'expected': True, - 'matcher': 'pathAny', - 'state': 'failure', - 'argument': 'DomainConfig.*.Status.PendingDeletion', - }], + "ESDomainAvailable": { + "delay": 60, + "operation": "DescribeElasticsearchDomainConfig", + "maxAttempts": 60, + "acceptors": [ + { + "expected": "Active", + "matcher": "path", + "state": "success", + "argument": "DomainConfig.ElasticsearchClusterConfig.Status.State", + }, + { + "expected": True, + "matcher": "pathAny", + "state": "failure", + "argument": "DomainConfig.*.Status.PendingDeletion", + }, + ], }, - 'ESUpgradeFinished': { - 'delay': 60, - 'operation': 'DescribeElasticsearchDomain', - 'maxAttempts': 60, - 'acceptors': [{ - 'expected': False, - 'matcher': 'path', - 'state': 'success', - 'argument': 'DomainStatus.UpgradeProcessing', - }], + "ESUpgradeFinished": { + "delay": 60, + "operation": "DescribeElasticsearchDomain", + "maxAttempts": 60, + "acceptors": [ + { + "expected": False, + "matcher": "path", + "state": "success", + "argument": "DomainStatus.UpgradeProcessing", + } + ], }, - 'ESDomainDeleted': { - 'delay': 30, - 'operation': 'DescribeElasticsearchDomain', - 'maxAttempts': 60, - 'acceptors': [{ - 'expected': True, - 'matcher': 'path', - 'state': 'retry', - 'argument': 'DomainStatus.Deleted', - }, { - 'expected': False, - 'matcher': 'path', - 'state': 'failure', - 'argument': 'DomainStatus.Processing', - }, { - 'expected': 'ResourceNotFoundException', - 'matcher': 'error', - 'state': 'success', - }], + "ESDomainDeleted": { + "delay": 30, + "operation": "DescribeElasticsearchDomain", + "maxAttempts": 60, + "acceptors": [ + { + "expected": True, + "matcher": "path", + "state": "retry", + "argument": "DomainStatus.Deleted", + }, + { + "expected": False, + "matcher": "path", + "state": "failure", + "argument": "DomainStatus.Processing", + }, + { + "expected": "ResourceNotFoundException", + "matcher": "error", + "state": "success", + }, + ], }, - 'ESDomainCreated': { - 'delay': 30, - 'operation': 'DescribeElasticsearchDomain', - 'maxAttempts': 60, - 'acceptors': [{ - 'expected': True, - 'matcher': 'path', - 'state': 'success', - 'argument': 'DomainStatus.Created', - }], + "ESDomainCreated": { + "delay": 30, + "operation": "DescribeElasticsearchDomain", + "maxAttempts": 60, + "acceptors": [ + { + "expected": True, + "matcher": "path", + "state": "success", + "argument": "DomainStatus.Created", + } + ], }, } def __virtual__(): - ''' + """ Only load if botocore libraries exist. - ''' + """ return salt.utils.versions.check_boto_reqs(check_boto=False) def get_waiter(client, waiter=None, waiter_config=None): - ''' + """ Gets a botocore waiter using either one of the preconfigured models by name ``waiter``, or with a manually supplied ``waiter_config``. @@ -101,19 +113,21 @@ def get_waiter(client, waiter=None, waiter_config=None): Either waiter or waiter_config must be supplied. :returns botocore.waiter - ''' + """ if not any((waiter, waiter_config)): - raise SaltInvocationError('At least one of waiter or waiter_config must be specified.') + raise SaltInvocationError( + "At least one of waiter or waiter_config must be specified." + ) waiter_model = botocore.waiter.WaiterModel( - {'version': 2, 'waiters': {waiter: WAITER_CONFIGS.get(waiter, waiter_config)}} + {"version": 2, "waiters": {waiter: WAITER_CONFIGS.get(waiter, waiter_config)}} ) return botocore.waiter.create_waiter_with_client(waiter, waiter_model, client) def list_waiters(): - ''' + """ Lists the builtin waiter configuration names. :returns list - ''' + """ return WAITER_CONFIGS.keys() diff --git a/salt/utils/boto3mod.py b/salt/utils/boto3mod.py index 2b09510153f..4ebd4ccf8fa 100644 --- a/salt/utils/boto3mod.py +++ b/salt/utils/boto3mod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Boto3 Common Utils ================= @@ -30,21 +30,23 @@ Example Usage: vpc_id = _cache_id('test-vpc') .. versionadded:: 2015.8.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import hashlib import logging import sys from functools import partial -# Import salt libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -from salt.exceptions import SaltInvocationError -from salt.ext import six import salt.utils.stringutils import salt.utils.versions +from salt.exceptions import SaltInvocationError +from salt.ext import six + +# Import salt libs +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin # Import third party libs # pylint: disable=import-error @@ -57,7 +59,7 @@ try: import botocore # pylint: disable=W0611 # pylint: enable=import-error - logging.getLogger('boto3').setLevel(logging.CRITICAL) + logging.getLogger("boto3").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -66,14 +68,14 @@ except ImportError: log = logging.getLogger(__name__) -__virtualname__ = 'boto3' +__virtualname__ = "boto3" def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ has_boto = salt.utils.versions.check_boto_reqs() if has_boto is True: return __virtualname__ @@ -81,12 +83,12 @@ def __virtual__(): def _option(value): - ''' + """ Look up the value for an option. - ''' + """ if value in __opts__: return __opts__[value] - master_opts = __pillar__.get('master', {}) + master_opts = __pillar__.get("master", {}) if value in master_opts: return master_opts[value] if value in __pillar__: @@ -99,23 +101,23 @@ def _get_profile(service, region, key, keyid, profile): _profile = _option(profile) elif isinstance(profile, dict): _profile = profile - key = _profile.get('key', None) - keyid = _profile.get('keyid', None) - region = _profile.get('region', None) + key = _profile.get("key", None) + keyid = _profile.get("keyid", None) + region = _profile.get("region", None) - if not region and _option(service + '.region'): - region = _option(service + '.region') + if not region and _option(service + ".region"): + region = _option(service + ".region") if not region: - region = 'us-east-1' - log.info('Assuming default region %s', region) + region = "us-east-1" + log.info("Assuming default region %s", region) - if not key and _option(service + '.key'): - key = _option(service + '.key') - if not keyid and _option(service + '.keyid'): - keyid = _option(service + '.keyid') + if not key and _option(service + ".key"): + key = _option(service + ".key") + if not keyid and _option(service + ".keyid"): + keyid = _option(service + ".keyid") - label = 'boto_{0}:'.format(service) + label = "boto_{0}:".format(service) if keyid: hash_string = region + keyid + key if six.PY3: @@ -127,10 +129,18 @@ def _get_profile(service, region, key, keyid, profile): return (cxkey, region, key, keyid) -def cache_id(service, name, sub_resource=None, resource_id=None, - invalidate=False, region=None, key=None, keyid=None, - profile=None): - ''' +def cache_id( + service, + name, + sub_resource=None, + resource_id=None, + invalidate=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Cache, invalidate, or retrieve an AWS resource id keyed by name. .. code-block:: python @@ -138,14 +148,13 @@ def cache_id(service, name, sub_resource=None, resource_id=None, __utils__['boto.cache_id']('ec2', 'myinstance', 'i-a1b2c3', profile='custom_profile') - ''' + """ - cxkey, _, _, _ = _get_profile(service, region, key, - keyid, profile) + cxkey, _, _, _ = _get_profile(service, region, key, keyid, profile) if sub_resource: - cxkey = '{0}:{1}:{2}:id'.format(cxkey, sub_resource, name) + cxkey = "{0}:{1}:{2}:id".format(cxkey, sub_resource, name) else: - cxkey = '{0}:{1}:id'.format(cxkey, name) + cxkey = "{0}:{1}:id".format(cxkey, name) if invalidate: if cxkey in __context__: @@ -166,7 +175,7 @@ def cache_id(service, name, sub_resource=None, resource_id=None, def cache_id_func(service): - ''' + """ Returns a partial `cache_id` function for the provided service. .. code-block:: python @@ -174,57 +183,57 @@ def cache_id_func(service): cache_id = __utils__['boto.cache_id_func']('ec2') cache_id('myinstance', 'i-a1b2c3') instance_id = cache_id('myinstance') - ''' + """ return partial(cache_id, service) -def get_connection(service, module=None, region=None, key=None, keyid=None, - profile=None): - ''' +def get_connection( + service, module=None, region=None, key=None, keyid=None, profile=None +): + """ Return a boto connection for the service. .. code-block:: python conn = __utils__['boto.get_connection']('ec2', profile='custom_profile') - ''' + """ module = module or service - cxkey, region, key, keyid = _get_profile(service, region, key, - keyid, profile) - cxkey = cxkey + ':conn3' + cxkey, region, key, keyid = _get_profile(service, region, key, keyid, profile) + cxkey = cxkey + ":conn3" if cxkey in __context__: return __context__[cxkey] try: - session = boto3.session.Session(aws_access_key_id=keyid, - aws_secret_access_key=key, - region_name=region) + session = boto3.session.Session( + aws_access_key_id=keyid, aws_secret_access_key=key, region_name=region + ) if session is None: - raise SaltInvocationError('Region "{0}" is not ' - 'valid.'.format(region)) + raise SaltInvocationError('Region "{0}" is not ' "valid.".format(region)) conn = session.client(module) if conn is None: - raise SaltInvocationError('Region "{0}" is not ' - 'valid.'.format(region)) + raise SaltInvocationError('Region "{0}" is not ' "valid.".format(region)) except boto.exception.NoAuthHandlerFound: - raise SaltInvocationError('No authentication credentials found when ' - 'attempting to make boto {0} connection to ' - 'region "{1}".'.format(service, region)) + raise SaltInvocationError( + "No authentication credentials found when " + "attempting to make boto {0} connection to " + 'region "{1}".'.format(service, region) + ) __context__[cxkey] = conn return conn def get_connection_func(service, module=None): - ''' + """ Returns a partial `get_connection` function for the provided service. .. code-block:: python get_conn = __utils__['boto.get_connection_func']('ec2') conn = get_conn() - ''' + """ return partial(get_connection, service, module=module) @@ -242,37 +251,37 @@ def get_error(e): # assume that none of the data we're looking for exists. aws = {} - message = '' + message = "" if six.PY2: - if hasattr(e, 'status'): - aws['status'] = e.status - if hasattr(e, 'reason'): - aws['reason'] = e.reason - if six.text_type(e) != '': - aws['message'] = six.text_type(e) - if hasattr(e, 'error_code') and e.error_code is not None: - aws['code'] = e.error_code + if hasattr(e, "status"): + aws["status"] = e.status + if hasattr(e, "reason"): + aws["reason"] = e.reason + if six.text_type(e) != "": + aws["message"] = six.text_type(e) + if hasattr(e, "error_code") and e.error_code is not None: + aws["code"] = e.error_code - if 'message' in aws and 'reason' in aws: - message = '{0}: {1}'.format(aws['reason'], aws['message']) - elif 'message' in aws: - message = aws['message'] - elif 'reason' in aws: - message = aws['reason'] + if "message" in aws and "reason" in aws: + message = "{0}: {1}".format(aws["reason"], aws["message"]) + elif "message" in aws: + message = aws["message"] + elif "reason" in aws: + message = aws["reason"] elif six.PY3: message = e.args[0] - r = {'message': message} + r = {"message": message} if aws: - r['aws'] = aws + r["aws"] = aws return r def exactly_n(l, n=1): - ''' + """ Tests that exactly N items in an iterable are "truthy" (neither None, False, nor 0). - ''' + """ i = iter(l) return all(any(i) for j in range(n)) and not any(i) @@ -281,16 +290,21 @@ def exactly_one(l): return exactly_n(l) -def assign_funcs(modname, service, module=None, - get_conn_funcname='_get_conn', cache_id_funcname='_cache_id', - exactly_one_funcname='_exactly_one'): - ''' +def assign_funcs( + modname, + service, + module=None, + get_conn_funcname="_get_conn", + cache_id_funcname="_cache_id", + exactly_one_funcname="_exactly_one", +): + """ Assign _get_conn and _cache_id functions to the named module. .. code-block:: python _utils__['boto.assign_partials'](__name__, 'ec2') - ''' + """ mod = sys.modules[modname] setattr(mod, get_conn_funcname, get_connection_func(service, module=module)) setattr(mod, cache_id_funcname, cache_id_func(service)) @@ -305,8 +319,8 @@ def paged_call(function, *args, **kwargs): """Retrieve full set of values from a boto3 API call that may truncate its results, yielding each page as it is obtained. """ - marker_flag = kwargs.pop('marker_flag', 'NextMarker') - marker_arg = kwargs.pop('marker_arg', 'Marker') + marker_flag = kwargs.pop("marker_flag", "NextMarker") + marker_arg = kwargs.pop("marker_arg", "Marker") while True: ret = function(*args, **kwargs) marker = ret.get(marker_flag) @@ -320,7 +334,10 @@ def ordered(obj): if isinstance(obj, (list, tuple)): return sorted(ordered(x) for x in obj) elif isinstance(obj, dict): - return dict((six.text_type(k) if isinstance(k, six.string_types) else k, ordered(v)) for k, v in obj.items()) + return dict( + (six.text_type(k) if isinstance(k, six.string_types) else k, ordered(v)) + for k, v in obj.items() + ) elif isinstance(obj, six.string_types): return six.text_type(obj) return obj diff --git a/salt/utils/boto_elb_tag.py b/salt/utils/boto_elb_tag.py index 14b136a3f11..d2065612aa3 100644 --- a/salt/utils/boto_elb_tag.py +++ b/salt/utils/boto_elb_tag.py @@ -30,11 +30,11 @@ def __virtual__(): def get_tag_descriptions(): class TagDescriptions(dict): - ''' + """ A TagDescriptions is used to collect the tags associated with ELB resources. See :class:`boto.ec2.elb.LoadBalancer` for more details. - ''' + """ def __init__(self, connection=None): dict.__init__(self) @@ -43,26 +43,26 @@ def get_tag_descriptions(): self._tags = None def startElement(self, name, attrs, connection): - if name == 'member': + if name == "member": self.load_balancer_name = None self.tags = None - if name == 'Tags': + if name == "Tags": self._tags = TagSet() return self._tags return None def endElement(self, name, value, connection): - if name == 'LoadBalancerName': + if name == "LoadBalancerName": self._load_balancer_name = value - elif name == 'member': + elif name == "member": self[self._load_balancer_name] = self._tags class TagSet(dict): - ''' + """ A TagSet is used to collect the tags associated with a particular ELB resource. See :class:`boto.ec2.elb.LoadBalancer` for more details. - ''' + """ def __init__(self, connection=None): dict.__init__(self) @@ -71,17 +71,17 @@ def get_tag_descriptions(): self._current_value = None def startElement(self, name, attrs, connection): - if name == 'member': + if name == "member": self._current_key = None self._current_value = None return None def endElement(self, name, value, connection): - if name == 'Key': + if name == "Key": self._current_key = value - elif name == 'Value': + elif name == "Value": self._current_value = value - elif name == 'member': + elif name == "member": self[self._current_key] = self._current_value return TagDescriptions diff --git a/salt/utils/botomod.py b/salt/utils/botomod.py index 1e6e39c69f1..e44b8521191 100644 --- a/salt/utils/botomod.py +++ b/salt/utils/botomod.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Boto Common Utils ================= @@ -30,22 +30,24 @@ Example Usage: vpc_id = _cache_id('test-vpc') .. versionadded:: 2015.8.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import hashlib import logging import sys from functools import partial -from salt.loader import minion_mods + +import salt.utils.stringutils +import salt.utils.versions +from salt.exceptions import SaltInvocationError # Import salt libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -from salt.exceptions import SaltInvocationError -import salt.utils.stringutils -import salt.utils.versions +from salt.loader import minion_mods # Import third party libs # pylint: disable=import-error @@ -53,8 +55,9 @@ try: # pylint: disable=import-error import boto import boto.exception + # pylint: enable=import-error - logging.getLogger('boto').setLevel(logging.CRITICAL) + logging.getLogger("boto").setLevel(logging.CRITICAL) HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -64,14 +67,14 @@ except ImportError: log = logging.getLogger(__name__) __salt__ = None -__virtualname__ = 'boto' +__virtualname__ = "boto" def __virtual__(): - ''' + """ Only load if boto libraries exist and if boto libraries are greater than a given version. - ''' + """ has_boto_requirements = salt.utils.versions.check_boto_reqs(check_boto3=False) if has_boto_requirements is True: global __salt__ @@ -84,23 +87,23 @@ def __virtual__(): def _get_profile(service, region, key, keyid, profile): if profile: if isinstance(profile, six.string_types): - _profile = __salt__['config.option'](profile) + _profile = __salt__["config.option"](profile) elif isinstance(profile, dict): _profile = profile - key = _profile.get('key', None) - keyid = _profile.get('keyid', None) - region = _profile.get('region', region or None) - if not region and __salt__['config.option'](service + '.region'): - region = __salt__['config.option'](service + '.region') + key = _profile.get("key", None) + keyid = _profile.get("keyid", None) + region = _profile.get("region", region or None) + if not region and __salt__["config.option"](service + ".region"): + region = __salt__["config.option"](service + ".region") if not region: - region = 'us-east-1' - if not key and __salt__['config.option'](service + '.key'): - key = __salt__['config.option'](service + '.key') - if not keyid and __salt__['config.option'](service + '.keyid'): - keyid = __salt__['config.option'](service + '.keyid') + region = "us-east-1" + if not key and __salt__["config.option"](service + ".key"): + key = __salt__["config.option"](service + ".key") + if not keyid and __salt__["config.option"](service + ".keyid"): + keyid = __salt__["config.option"](service + ".keyid") - label = 'boto_{0}:'.format(service) + label = "boto_{0}:".format(service) if keyid: hash_string = region + keyid + key if six.PY3: @@ -112,10 +115,18 @@ def _get_profile(service, region, key, keyid, profile): return (cxkey, region, key, keyid) -def cache_id(service, name, sub_resource=None, resource_id=None, - invalidate=False, region=None, key=None, keyid=None, - profile=None): - ''' +def cache_id( + service, + name, + sub_resource=None, + resource_id=None, + invalidate=False, + region=None, + key=None, + keyid=None, + profile=None, +): + """ Cache, invalidate, or retrieve an AWS resource id keyed by name. .. code-block:: python @@ -123,14 +134,13 @@ def cache_id(service, name, sub_resource=None, resource_id=None, __utils__['boto.cache_id']('ec2', 'myinstance', 'i-a1b2c3', profile='custom_profile') - ''' + """ - cxkey, _, _, _ = _get_profile(service, region, key, - keyid, profile) + cxkey, _, _, _ = _get_profile(service, region, key, keyid, profile) if sub_resource: - cxkey = '{0}:{1}:{2}:id'.format(cxkey, sub_resource, name) + cxkey = "{0}:{1}:{2}:id".format(cxkey, sub_resource, name) else: - cxkey = '{0}:{1}:id'.format(cxkey, name) + cxkey = "{0}:{1}:id".format(cxkey, name) if invalidate: if cxkey in __context__: @@ -151,7 +161,7 @@ def cache_id(service, name, sub_resource=None, resource_id=None, def cache_id_func(service): - ''' + """ Returns a partial ``cache_id`` function for the provided service. .. code-block:: python @@ -159,57 +169,59 @@ def cache_id_func(service): cache_id = __utils__['boto.cache_id_func']('ec2') cache_id('myinstance', 'i-a1b2c3') instance_id = cache_id('myinstance') - ''' + """ return partial(cache_id, service) -def get_connection(service, module=None, region=None, key=None, keyid=None, - profile=None): - ''' +def get_connection( + service, module=None, region=None, key=None, keyid=None, profile=None +): + """ Return a boto connection for the service. .. code-block:: python conn = __utils__['boto.get_connection']('ec2', profile='custom_profile') - ''' + """ # future lint: disable=blacklisted-function module = str(module or service) - module, submodule = (str('boto.') + module).rsplit(str('.'), 1) + module, submodule = (str("boto.") + module).rsplit(str("."), 1) # future lint: enable=blacklisted-function svc_mod = getattr(__import__(module, fromlist=[submodule]), submodule) - cxkey, region, key, keyid = _get_profile(service, region, key, - keyid, profile) - cxkey = cxkey + ':conn' + cxkey, region, key, keyid = _get_profile(service, region, key, keyid, profile) + cxkey = cxkey + ":conn" if cxkey in __context__: return __context__[cxkey] try: - conn = svc_mod.connect_to_region(region, aws_access_key_id=keyid, - aws_secret_access_key=key) + conn = svc_mod.connect_to_region( + region, aws_access_key_id=keyid, aws_secret_access_key=key + ) if conn is None: - raise SaltInvocationError('Region "{0}" is not ' - 'valid.'.format(region)) + raise SaltInvocationError('Region "{0}" is not ' "valid.".format(region)) except boto.exception.NoAuthHandlerFound: - raise SaltInvocationError('No authentication credentials found when ' - 'attempting to make boto {0} connection to ' - 'region "{1}".'.format(service, region)) + raise SaltInvocationError( + "No authentication credentials found when " + "attempting to make boto {0} connection to " + 'region "{1}".'.format(service, region) + ) __context__[cxkey] = conn return conn def get_connection_func(service, module=None): - ''' + """ Returns a partial ``get_connection`` function for the provided service. .. code-block:: python get_conn = __utils__['boto.get_connection_func']('ec2') conn = get_conn() - ''' + """ return partial(get_connection, service, module=module) @@ -217,34 +229,34 @@ def get_error(e): # The returns from boto modules vary greatly between modules. We need to # assume that none of the data we're looking for exists. aws = {} - if hasattr(e, 'status'): - aws['status'] = e.status - if hasattr(e, 'reason'): - aws['reason'] = e.reason - if hasattr(e, 'message') and e.message != '': - aws['message'] = e.message - if hasattr(e, 'error_code') and e.error_code is not None: - aws['code'] = e.error_code + if hasattr(e, "status"): + aws["status"] = e.status + if hasattr(e, "reason"): + aws["reason"] = e.reason + if hasattr(e, "message") and e.message != "": + aws["message"] = e.message + if hasattr(e, "error_code") and e.error_code is not None: + aws["code"] = e.error_code - if 'message' in aws and 'reason' in aws: - message = '{0}: {1}'.format(aws['reason'], aws['message']) - elif 'message' in aws: - message = aws['message'] - elif 'reason' in aws: - message = aws['reason'] + if "message" in aws and "reason" in aws: + message = "{0}: {1}".format(aws["reason"], aws["message"]) + elif "message" in aws: + message = aws["message"] + elif "reason" in aws: + message = aws["reason"] else: - message = '' - r = {'message': message} + message = "" + r = {"message": message} if aws: - r['aws'] = aws + r["aws"] = aws return r def exactly_n(l, n=1): - ''' + """ Tests that exactly N items in an iterable are "truthy" (neither None, False, nor 0). - ''' + """ i = iter(l) return all(any(i) for j in range(n)) and not any(i) @@ -254,32 +266,32 @@ def exactly_one(l): def assign_funcs(modname, service, module=None, pack=None): - ''' + """ Assign _get_conn and _cache_id functions to the named module. .. code-block:: python __utils__['boto.assign_partials'](__name__, 'ec2') - ''' + """ if pack: global __salt__ # pylint: disable=W0601 __salt__ = pack mod = sys.modules[modname] - setattr(mod, '_get_conn', get_connection_func(service, module=module)) - setattr(mod, '_cache_id', cache_id_func(service)) + setattr(mod, "_get_conn", get_connection_func(service, module=module)) + setattr(mod, "_cache_id", cache_id_func(service)) # TODO: Remove this and import salt.utils.data.exactly_one into boto_* modules instead # Leaving this way for now so boto modules can be back ported - setattr(mod, '_exactly_one', exactly_one) + setattr(mod, "_exactly_one", exactly_one) def paged_call(function, *args, **kwargs): - ''' + """ Retrieve full set of values from a boto API call that may truncate its results, yielding each page as it is obtained. - ''' - marker_flag = kwargs.pop('marker_flag', 'marker') - marker_arg = kwargs.pop('marker_flag', 'marker') + """ + marker_flag = kwargs.pop("marker_flag", "marker") + marker_arg = kwargs.pop("marker_flag", "marker") while True: ret = function(*args, **kwargs) marker = ret.get(marker_flag) diff --git a/salt/utils/cache.py b/salt/utils/cache.py index 030a6e63a5a..b9afd2545d1 100644 --- a/salt/utils/cache.py +++ b/salt/utils/cache.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" In-memory caching used by Salt -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import re import time -import logging # Import salt libs import salt.config @@ -25,33 +26,35 @@ log = logging.getLogger(__name__) class CacheFactory(object): - ''' + """ Cache which can use a number of backends - ''' + """ + @classmethod def factory(cls, backend, ttl, *args, **kwargs): - log.info('Factory backend: %s', backend) - if backend == 'memory': + log.info("Factory backend: %s", backend) + if backend == "memory": return CacheDict(ttl, *args, **kwargs) - elif backend == 'disk': - return CacheDisk(ttl, kwargs['minion_cache_path'], *args, **kwargs) + elif backend == "disk": + return CacheDisk(ttl, kwargs["minion_cache_path"], *args, **kwargs) else: - log.error('CacheFactory received unrecognized cache type') + log.error("CacheFactory received unrecognized cache type") class CacheDict(dict): - ''' + """ Subclass of dict that will lazily delete items past ttl - ''' + """ + def __init__(self, ttl, *args, **kwargs): dict.__init__(self, *args, **kwargs) self._ttl = ttl self._key_cache_time = {} def _enforce_ttl_key(self, key): - ''' + """ Enforce the TTL to a specific key, delete if its past TTL - ''' + """ if key not in self._key_cache_time: return if time.time() - self._key_cache_time[key] > self._ttl: @@ -59,16 +62,16 @@ class CacheDict(dict): dict.__delitem__(self, key) def __getitem__(self, key): - ''' + """ Check if the key is ttld out, then do the get - ''' + """ self._enforce_ttl_key(key) return dict.__getitem__(self, key) def __setitem__(self, key, val): - ''' + """ Make sure to update the key cache time - ''' + """ self._key_cache_time[key] = time.time() dict.__setitem__(self, key, val) @@ -78,11 +81,12 @@ class CacheDict(dict): class CacheDisk(CacheDict): - ''' + """ Class that represents itself as a dictionary to a consumer but uses a disk-based backend. Serialization and de-serialization is done with msgpack - ''' + """ + def __init__(self, ttl, path, *args, **kwargs): super(CacheDisk, self).__init__(ttl, *args, **kwargs) self._path = path @@ -90,9 +94,9 @@ class CacheDisk(CacheDict): self._read() def _enforce_ttl_key(self, key): - ''' + """ Enforce the TTL to a specific key, delete if its past TTL - ''' + """ if key not in self._key_cache_time: return if time.time() - self._key_cache_time[key] > self._ttl: @@ -104,38 +108,40 @@ class CacheDisk(CacheDict): return self._dict.__contains__(key) def __getitem__(self, key): - ''' + """ Check if the key is ttld out, then do the get - ''' + """ self._enforce_ttl_key(key) return self._dict.__getitem__(key) def __setitem__(self, key, val): - ''' + """ Make sure to update the key cache time - ''' + """ self._key_cache_time[key] = time.time() self._dict.__setitem__(key, val) # Do the same as the parent but also persist self._write() def __delitem__(self, key): - ''' + """ Make sure to remove the key cache time - ''' + """ del self._key_cache_time[key] self._dict.__delitem__(key) # Do the same as the parent but also persist self._write() def _read(self): - ''' + """ Read in from disk - ''' + """ if not salt.utils.msgpack.HAS_MSGPACK or not os.path.exists(self._path): return - with salt.utils.files.fopen(self._path, 'rb') as fp_: - cache = salt.utils.data.decode(salt.utils.msgpack.load(fp_, encoding=__salt_system_encoding__)) + with salt.utils.files.fopen(self._path, "rb") as fp_: + cache = salt.utils.data.decode( + salt.utils.msgpack.load(fp_, encoding=__salt_system_encoding__) + ) if "CacheDisk_cachetime" in cache: # new format self._dict = cache["CacheDisk_data"] self._key_cache_time = cache["CacheDisk_cachetime"] @@ -145,82 +151,83 @@ class CacheDisk(CacheDict): for key in self._dict: self._key_cache_time[key] = timestamp if log.isEnabledFor(logging.DEBUG): - log.debug('Disk cache retrieved: %s', cache) + log.debug("Disk cache retrieved: %s", cache) def _write(self): - ''' + """ Write out to disk - ''' + """ if not salt.utils.msgpack.HAS_MSGPACK: return # TODO Add check into preflight to ensure dir exists # TODO Dir hashing? - with salt.utils.files.fopen(self._path, 'wb+') as fp_: + with salt.utils.files.fopen(self._path, "wb+") as fp_: cache = { "CacheDisk_data": self._dict, - "CacheDisk_cachetime": self._key_cache_time + "CacheDisk_cachetime": self._key_cache_time, } salt.utils.msgpack.dump(cache, fp_, use_bin_type=True) class CacheCli(object): - ''' + """ Connection client for the ConCache. Should be used by all components that need the list of currently connected minions - ''' + """ def __init__(self, opts): - ''' + """ Sets up the zmq-connection to the ConCache - ''' + """ self.opts = opts - self.serial = salt.payload.Serial(self.opts.get('serial', '')) - self.cache_sock = os.path.join(self.opts['sock_dir'], 'con_cache.ipc') - self.cache_upd_sock = os.path.join( - self.opts['sock_dir'], 'con_upd.ipc') + self.serial = salt.payload.Serial(self.opts.get("serial", "")) + self.cache_sock = os.path.join(self.opts["sock_dir"], "con_cache.ipc") + self.cache_upd_sock = os.path.join(self.opts["sock_dir"], "con_upd.ipc") context = zmq.Context() # the socket for talking to the cache self.creq_out = context.socket(zmq.REQ) self.creq_out.setsockopt(zmq.LINGER, 100) - self.creq_out.connect('ipc://' + self.cache_sock) + self.creq_out.connect("ipc://" + self.cache_sock) # the socket for sending updates to the cache self.cupd_out = context.socket(zmq.PUB) self.cupd_out.setsockopt(zmq.LINGER, 1) - self.cupd_out.connect('ipc://' + self.cache_upd_sock) + self.cupd_out.connect("ipc://" + self.cache_upd_sock) def put_cache(self, minions): - ''' + """ published the given minions to the ConCache - ''' + """ self.cupd_out.send(self.serial.dumps(minions)) def get_cached(self): - ''' + """ queries the ConCache for a list of currently connected minions - ''' - msg = self.serial.dumps('minions') + """ + msg = self.serial.dumps("minions") self.creq_out.send(msg) min_list = self.serial.loads(self.creq_out.recv()) return min_list class CacheRegex(object): - ''' + """ Create a regular expression object cache for the most frequently used patterns to minimize compilation of the same patterns over and over again - ''' - def __init__(self, prepend='', append='', size=1000, - keep_fraction=0.8, max_age=3600): + """ + + def __init__( + self, prepend="", append="", size=1000, keep_fraction=0.8, max_age=3600 + ): self.prepend = prepend self.append = append self.size = size self.clear_size = int(size - size * (keep_fraction)) if self.clear_size >= size: - self.clear_size = int(size/2) + 1 + self.clear_size = int(size / 2) + 1 if self.clear_size > size: self.clear_size = size self.max_age = max_age @@ -228,16 +235,16 @@ class CacheRegex(object): self.timestamp = time.time() def clear(self): - ''' + """ Clear the cache - ''' + """ self.cache.clear() def sweep(self): - ''' + """ Sweep the cache and remove the outdated or least frequently used entries - ''' + """ if self.max_age < time.time() - self.timestamp: self.clear() self.timestamp = time.time() @@ -248,10 +255,10 @@ class CacheRegex(object): del self.cache[paterns[idx][2]] def get(self, pattern): - ''' + """ Get a compiled regular expression object based on pattern and cache it when it is not in the cache already - ''' + """ try: self.cache[pattern][0] += 1 return self.cache[pattern][1] @@ -259,69 +266,74 @@ class CacheRegex(object): pass if len(self.cache) > self.size: self.sweep() - regex = re.compile('{0}{1}{2}'.format( - self.prepend, pattern, self.append)) + regex = re.compile("{0}{1}{2}".format(self.prepend, pattern, self.append)) self.cache[pattern] = [1, regex, pattern, time.time()] return regex class ContextCache(object): def __init__(self, opts, name): - ''' + """ Create a context cache - ''' + """ self.opts = opts - self.cache_path = os.path.join(opts['cachedir'], 'context', '{0}.p'.format(name)) + self.cache_path = os.path.join( + opts["cachedir"], "context", "{0}.p".format(name) + ) self.serial = salt.payload.Serial(self.opts) def cache_context(self, context): - ''' + """ Cache the given context to disk - ''' + """ if not os.path.isdir(os.path.dirname(self.cache_path)): os.mkdir(os.path.dirname(self.cache_path)) - with salt.utils.files.fopen(self.cache_path, 'w+b') as cache: + with salt.utils.files.fopen(self.cache_path, "w+b") as cache: self.serial.dump(context, cache) def get_cache_context(self): - ''' + """ Retrieve a context cache from disk - ''' - with salt.utils.files.fopen(self.cache_path, 'rb') as cache: + """ + with salt.utils.files.fopen(self.cache_path, "rb") as cache: return salt.utils.data.decode(self.serial.load(cache)) def context_cache(func): - ''' + """ A decorator to be used module functions which need to cache their context. To evaluate a __context__ and re-hydrate it if a given key is empty or contains no items, pass a list of keys to evaulate. - ''' + """ + def context_cache_wrap(*args, **kwargs): - func_context = func.__globals__['__context__'] - func_opts = func.__globals__['__opts__'] - func_name = func.__globals__['__name__'] + func_context = func.__globals__["__context__"] + func_opts = func.__globals__["__opts__"] + func_name = func.__globals__["__name__"] context_cache = ContextCache(func_opts, func_name) if not func_context and os.path.isfile(context_cache.cache_path): - salt.utils.dictupdate.update(func_context, context_cache.get_cache_context()) + salt.utils.dictupdate.update( + func_context, context_cache.get_cache_context() + ) else: context_cache.cache_context(func_context) return func(*args, **kwargs) + return context_cache_wrap # test code for the CacheCli -if __name__ == '__main__': +if __name__ == "__main__": - opts = salt.config.master_config('/etc/salt/master') + opts = salt.config.master_config("/etc/salt/master") ccli = CacheCli(opts) - ccli.put_cache(['test1', 'test10', 'test34']) - ccli.put_cache(['test12']) - ccli.put_cache(['test18']) - ccli.put_cache(['test21']) - print('minions: {0}'.format(ccli.get_cached())) + ccli.put_cache(["test1", "test10", "test34"]) + ccli.put_cache(["test12"]) + ccli.put_cache(["test18"]) + ccli.put_cache(["test21"]) + print("minions: {0}".format(ccli.get_cached())) diff --git a/salt/utils/cloud.py b/salt/utils/cloud.py index 02728d65c84..ca1232cb6f2 100644 --- a/salt/utils/cloud.py +++ b/salt/utils/cloud.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Utility functions for salt.cloud -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import codecs import copy import errno @@ -24,9 +25,47 @@ import time import traceback import uuid +import salt.client + +# Import salt cloud libs +import salt.cloud +import salt.config + +# Import salt libs +import salt.crypt +import salt.loader +import salt.template +import salt.utils.compat +import salt.utils.crypt +import salt.utils.data +import salt.utils.event +import salt.utils.files +import salt.utils.msgpack +import salt.utils.path +import salt.utils.platform +import salt.utils.stringutils +import salt.utils.versions +import salt.utils.vt +import salt.utils.yaml +from jinja2 import Template +from salt.exceptions import ( + SaltCloudConfigError, + SaltCloudException, + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout, + SaltCloudPasswordError, + SaltCloudSystemExit, +) + +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import range +from salt.utils.nb_popen import NonBlockingPopen +from salt.utils.validate.path import is_writeable try: import salt.utils.smb + HAS_SMB = True except ImportError: HAS_SMB = False @@ -37,8 +76,9 @@ try: from pypsexec.exceptions import SCMRException from smbprotocol.tree import TreeConnect from smbprotocol.exceptions import SMBResponseException - logging.getLogger('smbprotocol').setLevel(logging.WARNING) - logging.getLogger('pypsexec').setLevel(logging.WARNING) + + logging.getLogger("smbprotocol").setLevel(logging.WARNING) + logging.getLogger("pypsexec").setLevel(logging.WARNING) HAS_PSEXEC = True except ImportError: HAS_PSEXEC = False @@ -51,42 +91,6 @@ try: except ImportError: HAS_WINRM = False -# Import salt libs -import salt.crypt -import salt.client -import salt.config -import salt.loader -import salt.template -import salt.utils.compat -import salt.utils.crypt -import salt.utils.data -import salt.utils.event -import salt.utils.files -import salt.utils.path -import salt.utils.msgpack -import salt.utils.platform -import salt.utils.stringutils -import salt.utils.versions -import salt.utils.vt -import salt.utils.yaml -from salt.utils.nb_popen import NonBlockingPopen -from salt.utils.validate.path import is_writeable - -# Import salt cloud libs -import salt.cloud -from salt.exceptions import ( - SaltCloudConfigError, - SaltCloudException, - SaltCloudSystemExit, - SaltCloudExecutionTimeout, - SaltCloudExecutionFailure, - SaltCloudPasswordError -) - -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin,W0611 -from jinja2 import Template # Let's import pwd and catch the ImportError. We'll raise it if this is not # Windows. This import has to be below where we import salt.utils.platform! @@ -105,107 +109,115 @@ except ImportError: # This is required to support international characters in AWS EC2 tags or any # other kind of metadata provided by particular Cloud vendor. -MSGPACK_ENCODING = 'utf-8' +MSGPACK_ENCODING = "utf-8" NSTATES = { - 0: 'running', - 1: 'rebooting', - 2: 'terminated', - 3: 'pending', + 0: "running", + 1: "rebooting", + 2: "terminated", + 3: "pending", } -SSH_PASSWORD_PROMP_RE = re.compile(r'(?:.*)[Pp]assword(?: for .*)?:\ *$', re.M) -SSH_PASSWORD_PROMP_SUDO_RE = \ - re.compile(r'(?:.*sudo)(?:.*)[Pp]assword(?: for .*)?:', re.M) +SSH_PASSWORD_PROMP_RE = re.compile(r"(?:.*)[Pp]assword(?: for .*)?:\ *$", re.M) +SSH_PASSWORD_PROMP_SUDO_RE = re.compile( + r"(?:.*sudo)(?:.*)[Pp]assword(?: for .*)?:", re.M +) # Get logging started log = logging.getLogger(__name__) -def __render_script(path, vm_=None, opts=None, minion=''): - ''' +def __render_script(path, vm_=None, opts=None, minion=""): + """ Return the rendered script - ''' - log.info('Rendering deploy script: %s', path) + """ + log.info("Rendering deploy script: %s", path) try: - with salt.utils.files.fopen(path, 'r') as fp_: + with salt.utils.files.fopen(path, "r") as fp_: template = Template(salt.utils.stringutils.to_unicode(fp_.read())) return six.text_type(template.render(opts=opts, vm=vm_, minion=minion)) except AttributeError: # Specified renderer was not found - with salt.utils.files.fopen(path, 'r') as fp_: + with salt.utils.files.fopen(path, "r") as fp_: return six.text_type(fp_.read()) def __ssh_gateway_config_dict(gateway): - ''' + """ Return a dictionary with gateway options. The result is used to provide arguments to __ssh_gateway_arguments method. - ''' + """ extended_kwargs = {} if gateway: - extended_kwargs['ssh_gateway'] = gateway['ssh_gateway'] - extended_kwargs['ssh_gateway_key'] = gateway['ssh_gateway_key'] - extended_kwargs['ssh_gateway_user'] = gateway['ssh_gateway_user'] - extended_kwargs['ssh_gateway_command'] = gateway['ssh_gateway_command'] + extended_kwargs["ssh_gateway"] = gateway["ssh_gateway"] + extended_kwargs["ssh_gateway_key"] = gateway["ssh_gateway_key"] + extended_kwargs["ssh_gateway_user"] = gateway["ssh_gateway_user"] + extended_kwargs["ssh_gateway_command"] = gateway["ssh_gateway_command"] return extended_kwargs def __ssh_gateway_arguments(kwargs): - ''' + """ Return ProxyCommand configuration string for ssh/scp command. All gateway options should not include quotes (' or "). To support future user configuration, please make sure to update the dictionary from __ssh_gateway_config_dict and get_ssh_gateway_config (ec2.py) - ''' + """ extended_arguments = "" - ssh_gateway = kwargs.get('ssh_gateway', '') + ssh_gateway = kwargs.get("ssh_gateway", "") ssh_gateway_port = 22 - if ':' in ssh_gateway: - ssh_gateway, ssh_gateway_port = ssh_gateway.split(':') - ssh_gateway_command = kwargs.get('ssh_gateway_command', 'nc -q0 %h %p') + if ":" in ssh_gateway: + ssh_gateway, ssh_gateway_port = ssh_gateway.split(":") + ssh_gateway_command = kwargs.get("ssh_gateway_command", "nc -q0 %h %p") if ssh_gateway: - ssh_gateway_port = kwargs.get('ssh_gateway_port', ssh_gateway_port) - ssh_gateway_key = '-i {0}'.format(kwargs['ssh_gateway_key']) if 'ssh_gateway_key' in kwargs else '' - ssh_gateway_user = kwargs.get('ssh_gateway_user', 'root') + ssh_gateway_port = kwargs.get("ssh_gateway_port", ssh_gateway_port) + ssh_gateway_key = ( + "-i {0}".format(kwargs["ssh_gateway_key"]) + if "ssh_gateway_key" in kwargs + else "" + ) + ssh_gateway_user = kwargs.get("ssh_gateway_user", "root") # Setup ProxyCommand extended_arguments = '-oProxyCommand="ssh {0} {1} {2} {3} {4}@{5} -p {6} {7}"'.format( - # Don't add new hosts to the host key database - '-oStrictHostKeyChecking=no', - # Set hosts key database path to /dev/null, i.e., non-existing - '-oUserKnownHostsFile=/dev/null', - # Don't re-use the SSH connection. Less failures. - '-oControlPath=none', - ssh_gateway_key, - ssh_gateway_user, - ssh_gateway, - ssh_gateway_port, - ssh_gateway_command - ) + # Don't add new hosts to the host key database + "-oStrictHostKeyChecking=no", + # Set hosts key database path to /dev/null, i.e., non-existing + "-oUserKnownHostsFile=/dev/null", + # Don't re-use the SSH connection. Less failures. + "-oControlPath=none", + ssh_gateway_key, + ssh_gateway_user, + ssh_gateway, + ssh_gateway_port, + ssh_gateway_command, + ) log.info( - 'Using SSH gateway %s@%s:%s %s', - ssh_gateway_user, ssh_gateway, ssh_gateway_port, ssh_gateway_command + "Using SSH gateway %s@%s:%s %s", + ssh_gateway_user, + ssh_gateway, + ssh_gateway_port, + ssh_gateway_command, ) return extended_arguments def has_winexe(): - ''' + """ True when winexe is found on the system - ''' - return salt.utils.path.which('winexe') + """ + return salt.utils.path.which("winexe") -def os_script(os_, vm_=None, opts=None, minion=''): - ''' +def os_script(os_, vm_=None, opts=None, minion=""): + """ Return the script as a string for the specific os - ''' + """ if minion: minion = salt_config_to_yaml(minion) @@ -213,38 +225,35 @@ def os_script(os_, vm_=None, opts=None, minion=''): # The user provided an absolute path to the deploy script, let's use it return __render_script(os_, vm_, opts, minion) - if os.path.isabs('{0}.sh'.format(os_)): + if os.path.isabs("{0}.sh".format(os_)): # The user provided an absolute path to the deploy script, although no # extension was provided. Let's use it anyway. - return __render_script('{0}.sh'.format(os_), vm_, opts, minion) + return __render_script("{0}.sh".format(os_), vm_, opts, minion) - for search_path in opts['deploy_scripts_search_path']: + for search_path in opts["deploy_scripts_search_path"]: if os.path.isfile(os.path.join(search_path, os_)): - return __render_script( - os.path.join(search_path, os_), vm_, opts, minion - ) + return __render_script(os.path.join(search_path, os_), vm_, opts, minion) - if os.path.isfile(os.path.join(search_path, '{0}.sh'.format(os_))): + if os.path.isfile(os.path.join(search_path, "{0}.sh".format(os_))): return __render_script( - os.path.join(search_path, '{0}.sh'.format(os_)), - vm_, opts, minion + os.path.join(search_path, "{0}.sh".format(os_)), vm_, opts, minion ) # No deploy script was found, return an empty string - return '' + return "" def gen_keys(keysize=2048): - ''' + """ Generate Salt minion keys and return them as PEM file strings - ''' + """ # Mandate that keys are at least 2048 in size if keysize < 2048: keysize = 2048 tdir = tempfile.mkdtemp() - salt.crypt.gen_keys(tdir, 'minion', keysize) - priv_path = os.path.join(tdir, 'minion.pem') - pub_path = os.path.join(tdir, 'minion.pub') + salt.crypt.gen_keys(tdir, "minion", keysize) + priv_path = os.path.join(tdir, "minion.pem") + pub_path = os.path.join(tdir, "minion.pub") with salt.utils.files.fopen(priv_path) as fp_: priv = salt.utils.stringutils.to_unicode(fp_.read()) with salt.utils.files.fopen(pub_path) as fp_: @@ -254,21 +263,21 @@ def gen_keys(keysize=2048): def accept_key(pki_dir, pub, id_): - ''' + """ If the master config was available then we will have a pki_dir key in the opts directory, this method places the pub key in the accepted keys dir and removes it from the unaccepted keys dir if that is the case. - ''' - for key_dir in 'minions', 'minions_pre', 'minions_rejected': + """ + for key_dir in "minions", "minions_pre", "minions_rejected": key_path = os.path.join(pki_dir, key_dir) if not os.path.exists(key_path): os.makedirs(key_path) - key = os.path.join(pki_dir, 'minions', id_) - with salt.utils.files.fopen(key, 'w+') as fp_: + key = os.path.join(pki_dir, "minions", id_) + with salt.utils.files.fopen(key, "w+") as fp_: fp_.write(salt.utils.stringutils.to_str(pub)) - oldkey = os.path.join(pki_dir, 'minions_pre', id_) + oldkey = os.path.join(pki_dir, "minions_pre", id_) if os.path.isfile(oldkey): with salt.utils.files.fopen(oldkey) as fp_: if fp_.read() == pub: @@ -276,83 +285,79 @@ def accept_key(pki_dir, pub, id_): def remove_key(pki_dir, id_): - ''' + """ This method removes a specified key from the accepted keys dir - ''' - key = os.path.join(pki_dir, 'minions', id_) + """ + key = os.path.join(pki_dir, "minions", id_) if os.path.isfile(key): os.remove(key) - log.debug('Deleted \'%s\'', key) + log.debug("Deleted '%s'", key) def rename_key(pki_dir, id_, new_id): - ''' + """ Rename a key, when an instance has also been renamed - ''' - oldkey = os.path.join(pki_dir, 'minions', id_) - newkey = os.path.join(pki_dir, 'minions', new_id) + """ + oldkey = os.path.join(pki_dir, "minions", id_) + newkey = os.path.join(pki_dir, "minions", new_id) if os.path.isfile(oldkey): os.rename(oldkey, newkey) def minion_config(opts, vm_): - ''' + """ Return a minion's configuration for the provided options and VM - ''' + """ # Don't start with a copy of the default minion opts; they're not always # what we need. Some default options are Null, let's set a reasonable default minion = { - 'master': 'salt', - 'log_level': 'info', - 'hash_type': 'sha256', + "master": "salt", + "log_level": "info", + "hash_type": "sha256", } # Now, let's update it to our needs - minion['id'] = vm_['name'] - master_finger = salt.config.get_cloud_config_value('master_finger', vm_, opts) + minion["id"] = vm_["name"] + master_finger = salt.config.get_cloud_config_value("master_finger", vm_, opts) if master_finger is not None: - minion['master_finger'] = master_finger + minion["master_finger"] = master_finger minion.update( # Get ANY defined minion settings, merging data, in the following order # 1. VM config # 2. Profile config # 3. Global configuration salt.config.get_cloud_config_value( - 'minion', vm_, opts, default={}, search_global=True + "minion", vm_, opts, default={}, search_global=True ) ) - make_master = salt.config.get_cloud_config_value('make_master', vm_, opts) - if 'master' not in minion and make_master is not True: + make_master = salt.config.get_cloud_config_value("make_master", vm_, opts) + if "master" not in minion and make_master is not True: raise SaltCloudConfigError( - 'A master setting was not defined in the minion\'s configuration.' + "A master setting was not defined in the minion's configuration." ) # Get ANY defined grains settings, merging data, in the following order # 1. VM config # 2. Profile config # 3. Global configuration - minion.setdefault('grains', {}).update( + minion.setdefault("grains", {}).update( salt.config.get_cloud_config_value( - 'grains', vm_, opts, default={}, search_global=True + "grains", vm_, opts, default={}, search_global=True ) ) return minion def master_config(opts, vm_): - ''' + """ Return a master's configuration for the provided options and VM - ''' + """ # Let's get a copy of the salt master default options master = copy.deepcopy(salt.config.DEFAULT_MASTER_OPTS) # Some default options are Null, let's set a reasonable default - master.update( - log_level='info', - log_level_logfile='info', - hash_type='sha256' - ) + master.update(log_level="info", log_level_logfile="info", hash_type="sha256") # Get ANY defined master setting, merging data, in the following order # 1. VM config @@ -360,282 +365,262 @@ def master_config(opts, vm_): # 3. Global configuration master.update( salt.config.get_cloud_config_value( - 'master', vm_, opts, default={}, search_global=True + "master", vm_, opts, default={}, search_global=True ) ) return master -def salt_config_to_yaml(configuration, line_break='\n'): - ''' +def salt_config_to_yaml(configuration, line_break="\n"): + """ Return a salt configuration dictionary, master or minion, as a yaml dump - ''' + """ return salt.utils.yaml.safe_dump( - configuration, - line_break=line_break, - default_flow_style=False) + configuration, line_break=line_break, default_flow_style=False + ) def bootstrap(vm_, opts=None): - ''' + """ This is the primary entry point for logging into any system (POSIX or Windows) to install Salt. It will make the decision on its own as to which deploy function to call. - ''' + """ if opts is None: opts = __opts__ deploy_config = salt.config.get_cloud_config_value( - 'deploy', - vm_, opts, default=False) + "deploy", vm_, opts, default=False + ) inline_script_config = salt.config.get_cloud_config_value( - 'inline_script', - vm_, opts, default=None) + "inline_script", vm_, opts, default=None + ) if deploy_config is False and inline_script_config is None: - return { - 'Error': { - 'No Deploy': '\'deploy\' is not enabled. Not deploying.' - } - } + return {"Error": {"No Deploy": "'deploy' is not enabled. Not deploying."}} - if vm_.get('driver') == 'saltify': + if vm_.get("driver") == "saltify": saltify_driver = True else: saltify_driver = False key_filename = salt.config.get_cloud_config_value( - 'key_filename', vm_, opts, search_global=False, + "key_filename", + vm_, + opts, + search_global=False, default=salt.config.get_cloud_config_value( - 'ssh_keyfile', vm_, opts, search_global=False, default=None - ) + "ssh_keyfile", vm_, opts, search_global=False, default=None + ), ) if key_filename is not None and not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined ssh_keyfile \'{0}\' does not exist'.format( - key_filename - ) + "The defined ssh_keyfile '{0}' does not exist".format(key_filename) ) has_ssh_agent = False - if (opts.get('ssh_agent', False) and - 'SSH_AUTH_SOCK' in os.environ and - stat.S_ISSOCK(os.stat(os.environ['SSH_AUTH_SOCK']).st_mode)): + if ( + opts.get("ssh_agent", False) + and "SSH_AUTH_SOCK" in os.environ + and stat.S_ISSOCK(os.stat(os.environ["SSH_AUTH_SOCK"]).st_mode) + ): has_ssh_agent = True - if (key_filename is None and - salt.config.get_cloud_config_value( - 'password', vm_, opts, default=None - ) is None and - salt.config.get_cloud_config_value( - 'win_password', vm_, opts, default=None - ) is None and - has_ssh_agent is False): + if ( + key_filename is None + and salt.config.get_cloud_config_value("password", vm_, opts, default=None) + is None + and salt.config.get_cloud_config_value("win_password", vm_, opts, default=None) + is None + and has_ssh_agent is False + ): raise SaltCloudSystemExit( - 'Cannot deploy Salt in a VM if the \'key_filename\' setting ' - 'is not set and there is no password set for the VM. ' - 'Check the provider docs for \'change_password\' option if it ' - 'is supported by your provider.' + "Cannot deploy Salt in a VM if the 'key_filename' setting " + "is not set and there is no password set for the VM. " + "Check the provider docs for 'change_password' option if it " + "is supported by your provider." ) ret = {} minion_conf = minion_config(opts, vm_) deploy_script_code = os_script( - salt.config.get_cloud_config_value( - 'os', vm_, opts, default='bootstrap-salt' - ), - vm_, opts, minion_conf + salt.config.get_cloud_config_value("os", vm_, opts, default="bootstrap-salt"), + vm_, + opts, + minion_conf, ) ssh_username = salt.config.get_cloud_config_value( - 'ssh_username', vm_, opts, default='root' + "ssh_username", vm_, opts, default="root" ) - if 'file_transport' not in opts: - opts['file_transport'] = vm_.get('file_transport', 'sftp') + if "file_transport" not in opts: + opts["file_transport"] = vm_.get("file_transport", "sftp") # If we haven't generated any keys yet, do so now. - if 'pub_key' not in vm_ and 'priv_key' not in vm_: - log.debug('Generating keys for \'%s\'', vm_['name']) + if "pub_key" not in vm_ and "priv_key" not in vm_: + log.debug("Generating keys for '%s'", vm_["name"]) - vm_['priv_key'], vm_['pub_key'] = gen_keys( - salt.config.get_cloud_config_value( - 'keysize', - vm_, - opts - ) + vm_["priv_key"], vm_["pub_key"] = gen_keys( + salt.config.get_cloud_config_value("keysize", vm_, opts) ) - key_id = vm_.get('name') - if 'append_domain' in vm_: - key_id = '.'.join([key_id, vm_['append_domain']]) + key_id = vm_.get("name") + if "append_domain" in vm_: + key_id = ".".join([key_id, vm_["append_domain"]]) - accept_key( - opts['pki_dir'], vm_['pub_key'], key_id - ) + accept_key(opts["pki_dir"], vm_["pub_key"], key_id) - if 'os' not in vm_: - vm_['os'] = salt.config.get_cloud_config_value( - 'script', - vm_, - opts - ) + if "os" not in vm_: + vm_["os"] = salt.config.get_cloud_config_value("script", vm_, opts) # NOTE: deploy_kwargs is also used to pass inline_script variable content # to run_inline_script function - host = salt.config.get_cloud_config_value('ssh_host', vm_, opts) + host = salt.config.get_cloud_config_value("ssh_host", vm_, opts) deploy_kwargs = { - 'opts': opts, - 'host': host, - 'port': salt.config.get_cloud_config_value( - 'ssh_port', vm_, opts, default=22 + "opts": opts, + "host": host, + "port": salt.config.get_cloud_config_value("ssh_port", vm_, opts, default=22), + "salt_host": vm_.get("salt_host", host), + "username": ssh_username, + "script": deploy_script_code, + "inline_script": inline_script_config, + "name": vm_["name"], + "has_ssh_agent": has_ssh_agent, + "tmp_dir": salt.config.get_cloud_config_value( + "tmp_dir", vm_, opts, default="/tmp/.saltcloud" ), - 'salt_host': vm_.get('salt_host', host), - 'username': ssh_username, - 'script': deploy_script_code, - 'inline_script': inline_script_config, - 'name': vm_['name'], - 'has_ssh_agent': has_ssh_agent, - 'tmp_dir': salt.config.get_cloud_config_value( - 'tmp_dir', vm_, opts, default='/tmp/.saltcloud' + "vm_": vm_, + "start_action": opts["start_action"], + "parallel": opts["parallel"], + "sock_dir": opts["sock_dir"], + "conf_file": opts["conf_file"], + "minion_pem": vm_["priv_key"], + "minion_pub": vm_["pub_key"], + "master_sign_pub_file": salt.config.get_cloud_config_value( + "master_sign_pub_file", vm_, opts, default=None ), - 'vm_': vm_, - 'start_action': opts['start_action'], - 'parallel': opts['parallel'], - 'sock_dir': opts['sock_dir'], - 'conf_file': opts['conf_file'], - 'minion_pem': vm_['priv_key'], - 'minion_pub': vm_['pub_key'], - 'master_sign_pub_file': salt.config.get_cloud_config_value( - 'master_sign_pub_file', vm_, opts, default=None), - 'keep_tmp': opts['keep_tmp'], - 'sudo': salt.config.get_cloud_config_value( - 'sudo', vm_, opts, default=(ssh_username != 'root') + "keep_tmp": opts["keep_tmp"], + "sudo": salt.config.get_cloud_config_value( + "sudo", vm_, opts, default=(ssh_username != "root") ), - 'sudo_password': salt.config.get_cloud_config_value( - 'sudo_password', vm_, opts, default=None + "sudo_password": salt.config.get_cloud_config_value( + "sudo_password", vm_, opts, default=None ), - 'tty': salt.config.get_cloud_config_value( - 'tty', vm_, opts, default=True + "tty": salt.config.get_cloud_config_value("tty", vm_, opts, default=True), + "password": salt.config.get_cloud_config_value( + "password", vm_, opts, search_global=False ), - 'password': salt.config.get_cloud_config_value( - 'password', vm_, opts, search_global=False + "key_filename": key_filename, + "script_args": salt.config.get_cloud_config_value("script_args", vm_, opts), + "script_env": salt.config.get_cloud_config_value("script_env", vm_, opts), + "minion_conf": minion_conf, + "force_minion_config": salt.config.get_cloud_config_value( + "force_minion_config", vm_, opts, default=False ), - 'key_filename': key_filename, - 'script_args': salt.config.get_cloud_config_value( - 'script_args', vm_, opts + "preseed_minion_keys": vm_.get("preseed_minion_keys", None), + "display_ssh_output": salt.config.get_cloud_config_value( + "display_ssh_output", vm_, opts, default=True ), - 'script_env': salt.config.get_cloud_config_value( - 'script_env', vm_, opts + "known_hosts_file": salt.config.get_cloud_config_value( + "known_hosts_file", vm_, opts, default="/dev/null" ), - 'minion_conf': minion_conf, - 'force_minion_config': salt.config.get_cloud_config_value( - 'force_minion_config', vm_, opts, default=False + "file_map": salt.config.get_cloud_config_value( + "file_map", vm_, opts, default=None ), - 'preseed_minion_keys': vm_.get('preseed_minion_keys', None), - 'display_ssh_output': salt.config.get_cloud_config_value( - 'display_ssh_output', vm_, opts, default=True + "maxtries": salt.config.get_cloud_config_value( + "wait_for_passwd_maxtries", vm_, opts, default=15 ), - 'known_hosts_file': salt.config.get_cloud_config_value( - 'known_hosts_file', vm_, opts, default='/dev/null' + "preflight_cmds": salt.config.get_cloud_config_value( + "preflight_cmds", vm_, opts, default=[] ), - 'file_map': salt.config.get_cloud_config_value( - 'file_map', vm_, opts, default=None - ), - 'maxtries': salt.config.get_cloud_config_value( - 'wait_for_passwd_maxtries', vm_, opts, default=15 - ), - 'preflight_cmds': salt.config.get_cloud_config_value( - 'preflight_cmds', vm_, opts, default=[] - ), - 'cloud_grains': {'driver': vm_['driver'], - 'provider': vm_['provider'], - 'profile': vm_['profile'] - } + "cloud_grains": { + "driver": vm_["driver"], + "provider": vm_["provider"], + "profile": vm_["profile"], + }, } inline_script_kwargs = deploy_kwargs.copy() # make a copy at this point # forward any info about possible ssh gateway to deploy script # as some providers need also a 'gateway' configuration - if 'gateway' in vm_: - deploy_kwargs.update({'gateway': vm_['gateway']}) + if "gateway" in vm_: + deploy_kwargs.update({"gateway": vm_["gateway"]}) # Deploy salt-master files, if necessary - if salt.config.get_cloud_config_value('make_master', vm_, opts) is True: - deploy_kwargs['make_master'] = True - deploy_kwargs['master_pub'] = vm_['master_pub'] - deploy_kwargs['master_pem'] = vm_['master_pem'] + if salt.config.get_cloud_config_value("make_master", vm_, opts) is True: + deploy_kwargs["make_master"] = True + deploy_kwargs["master_pub"] = vm_["master_pub"] + deploy_kwargs["master_pem"] = vm_["master_pem"] master_conf = master_config(opts, vm_) - deploy_kwargs['master_conf'] = master_conf + deploy_kwargs["master_conf"] = master_conf - if master_conf.get('syndic_master', None): - deploy_kwargs['make_syndic'] = True + if master_conf.get("syndic_master", None): + deploy_kwargs["make_syndic"] = True - deploy_kwargs['make_minion'] = salt.config.get_cloud_config_value( - 'make_minion', vm_, opts, default=True + deploy_kwargs["make_minion"] = salt.config.get_cloud_config_value( + "make_minion", vm_, opts, default=True ) if saltify_driver: - deploy_kwargs['wait_for_passwd_maxtries'] = 0 # No need to wait/retry with Saltify + deploy_kwargs[ + "wait_for_passwd_maxtries" + ] = 0 # No need to wait/retry with Saltify - win_installer = salt.config.get_cloud_config_value( - 'win_installer', vm_, opts - ) + win_installer = salt.config.get_cloud_config_value("win_installer", vm_, opts) if win_installer: - deploy_kwargs['port'] = salt.config.get_cloud_config_value( - 'smb_port', vm_, opts, default=445 + deploy_kwargs["port"] = salt.config.get_cloud_config_value( + "smb_port", vm_, opts, default=445 ) - deploy_kwargs['win_installer'] = win_installer + deploy_kwargs["win_installer"] = win_installer minion = minion_config(opts, vm_) - deploy_kwargs['master'] = minion['master'] - deploy_kwargs['username'] = salt.config.get_cloud_config_value( - 'win_username', vm_, opts, default='Administrator' + deploy_kwargs["master"] = minion["master"] + deploy_kwargs["username"] = salt.config.get_cloud_config_value( + "win_username", vm_, opts, default="Administrator" ) win_pass = salt.config.get_cloud_config_value( - 'win_password', vm_, opts, default='' + "win_password", vm_, opts, default="" ) if win_pass: - deploy_kwargs['password'] = win_pass - deploy_kwargs['use_winrm'] = salt.config.get_cloud_config_value( - 'use_winrm', vm_, opts, default=False + deploy_kwargs["password"] = win_pass + deploy_kwargs["use_winrm"] = salt.config.get_cloud_config_value( + "use_winrm", vm_, opts, default=False ) - deploy_kwargs['winrm_port'] = salt.config.get_cloud_config_value( - 'winrm_port', vm_, opts, default=5986 + deploy_kwargs["winrm_port"] = salt.config.get_cloud_config_value( + "winrm_port", vm_, opts, default=5986 ) - deploy_kwargs['winrm_use_ssl'] = salt.config.get_cloud_config_value( - 'winrm_use_ssl', vm_, opts, default=True + deploy_kwargs["winrm_use_ssl"] = salt.config.get_cloud_config_value( + "winrm_use_ssl", vm_, opts, default=True ) - deploy_kwargs['winrm_verify_ssl'] = salt.config.get_cloud_config_value( - 'winrm_verify_ssl', vm_, opts, default=True + deploy_kwargs["winrm_verify_ssl"] = salt.config.get_cloud_config_value( + "winrm_verify_ssl", vm_, opts, default=True ) if saltify_driver: - deploy_kwargs['port_timeout'] = 1 # No need to wait/retry with Saltify + deploy_kwargs["port_timeout"] = 1 # No need to wait/retry with Saltify # Store what was used to the deploy the VM event_kwargs = copy.deepcopy(deploy_kwargs) - del event_kwargs['opts'] - del event_kwargs['minion_pem'] - del event_kwargs['minion_pub'] - del event_kwargs['sudo_password'] - if 'password' in event_kwargs: - del event_kwargs['password'] - ret['deploy_kwargs'] = event_kwargs + del event_kwargs["opts"] + del event_kwargs["minion_pem"] + del event_kwargs["minion_pub"] + del event_kwargs["sudo_password"] + if "password" in event_kwargs: + del event_kwargs["password"] + ret["deploy_kwargs"] = event_kwargs fire_event( - 'event', - 'executing deploy script', - 'salt/cloud/{0}/deploying'.format(vm_['name']), - args={'kwargs': event_kwargs}, - sock_dir=opts.get( - 'sock_dir', - os.path.join(__opts__['sock_dir'], 'master')), - transport=opts.get('transport', 'zeromq') + "event", + "executing deploy script", + "salt/cloud/{0}/deploying".format(vm_["name"]), + args={"kwargs": event_kwargs}, + sock_dir=opts.get("sock_dir", os.path.join(__opts__["sock_dir"], "master")), + transport=opts.get("transport", "zeromq"), ) if inline_script_config and deploy_config is False: inline_script_deployed = run_inline_script(**inline_script_kwargs) if inline_script_deployed is not False: - log.info('Inline script(s) ha(s|ve) run on %s', vm_['name']) - ret['deployed'] = False + log.info("Inline script(s) ha(s|ve) run on %s", vm_["name"]) + ret["deployed"] = False return ret else: if win_installer: @@ -646,35 +631,31 @@ def bootstrap(vm_, opts=None): if inline_script_config: inline_script_deployed = run_inline_script(**inline_script_kwargs) if inline_script_deployed is not False: - log.info('Inline script(s) ha(s|ve) run on %s', vm_['name']) + log.info("Inline script(s) ha(s|ve) run on %s", vm_["name"]) if deployed is not False: - ret['deployed'] = True + ret["deployed"] = True if deployed is not True: ret.update(deployed) - log.info('Salt installed on %s', vm_['name']) + log.info("Salt installed on %s", vm_["name"]) return ret - log.error('Failed to start Salt on host %s', vm_['name']) + log.error("Failed to start Salt on host %s", vm_["name"]) return { - 'Error': { - 'Not Deployed': 'Failed to start Salt on host {0}'.format( - vm_['name'] - ) + "Error": { + "Not Deployed": "Failed to start Salt on host {0}".format(vm_["name"]) } } def ssh_usernames(vm_, opts, default_users=None): - ''' + """ Return the ssh_usernames. Defaults to a built-in list of users for trying. - ''' + """ if default_users is None: - default_users = ['root'] + default_users = ["root"] - usernames = salt.config.get_cloud_config_value( - 'ssh_username', vm_, opts - ) + usernames = salt.config.get_cloud_config_value("ssh_username", vm_, opts) if not isinstance(usernames, list): usernames = [usernames] @@ -697,11 +678,11 @@ def ssh_usernames(vm_, opts, default_users=None): def wait_for_fun(fun, timeout=900, **kwargs): - ''' + """ Wait until a function finishes, or times out - ''' + """ start = time.time() - log.debug('Attempting function %s', fun) + log.debug("Attempting function %s", fun) trycount = 0 while True: trycount += 1 @@ -710,21 +691,21 @@ def wait_for_fun(fun, timeout=900, **kwargs): if not isinstance(response, bool): return response except Exception as exc: # pylint: disable=broad-except - log.debug('Caught exception in wait_for_fun: %s', exc) + log.debug("Caught exception in wait_for_fun: %s", exc) time.sleep(1) - log.debug('Retrying function %s on (try %s)', fun, trycount) + log.debug("Retrying function %s on (try %s)", fun, trycount) if time.time() - start > timeout: - log.error('Function timed out: %s', timeout) + log.error("Function timed out: %s", timeout) return False def wait_for_port(host, port=22, timeout=900, gateway=None): - ''' + """ Wait until a connection to the specified port can be made on a specified host. This is usually port 22 (for SSH), but in the case of Windows installations, it might be port 445 (for psexec). It may also be an alternate port for SSH, depending on the base image. - ''' + """ start = time.time() # Assign test ports because if a gateway is defined # we first want to test the gateway before the host. @@ -732,21 +713,23 @@ def wait_for_port(host, port=22, timeout=900, gateway=None): test_ssh_port = port if gateway: - ssh_gateway = gateway['ssh_gateway'] + ssh_gateway = gateway["ssh_gateway"] ssh_gateway_port = 22 - if ':' in ssh_gateway: - ssh_gateway, ssh_gateway_port = ssh_gateway.split(':') - if 'ssh_gateway_port' in gateway: - ssh_gateway_port = gateway['ssh_gateway_port'] + if ":" in ssh_gateway: + ssh_gateway, ssh_gateway_port = ssh_gateway.split(":") + if "ssh_gateway_port" in gateway: + ssh_gateway_port = gateway["ssh_gateway_port"] test_ssh_host = ssh_gateway test_ssh_port = ssh_gateway_port log.debug( - 'Attempting connection to host %s on port %s ' - 'via gateway %s on port %s', - host, port, ssh_gateway, ssh_gateway_port + "Attempting connection to host %s on port %s " "via gateway %s on port %s", + host, + port, + ssh_gateway, + ssh_gateway_port, ) else: - log.debug('Attempting connection to host %s on port %s', host, port) + log.debug("Attempting connection to host %s on port %s", host, port) trycount = 0 while True: trycount += 1 @@ -766,61 +749,76 @@ def wait_for_port(host, port=22, timeout=900, gateway=None): sock.close() break except socket.error as exc: - log.debug('Caught exception in wait_for_port: %s', exc) + log.debug("Caught exception in wait_for_port: %s", exc) time.sleep(1) if time.time() - start > timeout: - log.error('Port connection timed out: %s', timeout) + log.error("Port connection timed out: %s", timeout) return False log.debug( - 'Retrying connection to %s %s on port %s (try %s)', - 'gateway' if gateway else 'host', test_ssh_host, test_ssh_port, trycount + "Retrying connection to %s %s on port %s (try %s)", + "gateway" if gateway else "host", + test_ssh_host, + test_ssh_port, + trycount, ) if not gateway: return True # Let the user know that his gateway is good! - log.debug('Gateway %s on port %s is reachable.', test_ssh_host, test_ssh_port) + log.debug("Gateway %s on port %s is reachable.", test_ssh_host, test_ssh_port) # Now we need to test the host via the gateway. # We will use netcat on the gateway to test the port ssh_args = [] - ssh_args.extend([ - # Don't add new hosts to the host key database - '-oStrictHostKeyChecking=no', - # Set hosts key database path to /dev/null, i.e., non-existing - '-oUserKnownHostsFile=/dev/null', - # Don't re-use the SSH connection. Less failures. - '-oControlPath=none' - ]) + ssh_args.extend( + [ + # Don't add new hosts to the host key database + "-oStrictHostKeyChecking=no", + # Set hosts key database path to /dev/null, i.e., non-existing + "-oUserKnownHostsFile=/dev/null", + # Don't re-use the SSH connection. Less failures. + "-oControlPath=none", + ] + ) # There should never be both a password and an ssh key passed in, so - if 'ssh_gateway_key' in gateway: - ssh_args.extend([ - # tell SSH to skip password authentication - '-oPasswordAuthentication=no', - '-oChallengeResponseAuthentication=no', - # Make sure public key authentication is enabled - '-oPubkeyAuthentication=yes', - # do only use the provided identity file - '-oIdentitiesOnly=yes', - # No Keyboard interaction! - '-oKbdInteractiveAuthentication=no', - # Also, specify the location of the key file - '-i {0}'.format(gateway['ssh_gateway_key']) - ]) + if "ssh_gateway_key" in gateway: + ssh_args.extend( + [ + # tell SSH to skip password authentication + "-oPasswordAuthentication=no", + "-oChallengeResponseAuthentication=no", + # Make sure public key authentication is enabled + "-oPubkeyAuthentication=yes", + # do only use the provided identity file + "-oIdentitiesOnly=yes", + # No Keyboard interaction! + "-oKbdInteractiveAuthentication=no", + # Also, specify the location of the key file + "-i {0}".format(gateway["ssh_gateway_key"]), + ] + ) # Netcat command testing remote port - command = 'nc -z -w5 -q0 {0} {1}'.format(host, port) + command = "nc -z -w5 -q0 {0} {1}".format(host, port) # SSH command - pcmd = 'ssh {0} {1}@{2} -p {3} {4}'.format( - ' '.join(ssh_args), gateway['ssh_gateway_user'], ssh_gateway, - ssh_gateway_port, pipes.quote('date') + pcmd = "ssh {0} {1}@{2} -p {3} {4}".format( + " ".join(ssh_args), + gateway["ssh_gateway_user"], + ssh_gateway, + ssh_gateway_port, + pipes.quote("date"), ) - cmd = 'ssh {0} {1}@{2} -p {3} {4}'.format( - ' '.join(ssh_args), gateway['ssh_gateway_user'], ssh_gateway, - ssh_gateway_port, pipes.quote(command) + cmd = "ssh {0} {1}@{2} -p {3} {4}".format( + " ".join(ssh_args), + gateway["ssh_gateway_user"], + ssh_gateway, + ssh_gateway_port, + pipes.quote(command), ) - log.debug('SSH command: \'%s\'', cmd) + log.debug("SSH command: '%s'", cmd) - kwargs = {'display_ssh_output': False, - 'password': gateway.get('ssh_gateway_password', None)} + kwargs = { + "display_ssh_output": False, + "password": gateway.get("ssh_gateway_password", None), + } trycount = 0 usable_gateway = False gateway_retries = 5 @@ -834,11 +832,14 @@ def wait_for_port(host, port=22, timeout=900, gateway=None): else: gateway_retries -= 1 log.error( - 'Gateway usage seems to be broken, ' - 'password error ? Tries left: %s', gateway_retries) + "Gateway usage seems to be broken, " + "password error ? Tries left: %s", + gateway_retries, + ) if not gateway_retries: raise SaltCloudExecutionFailure( - 'SSH gateway is reachable but we can not login') + "SSH gateway is reachable but we can not login" + ) # then try to reach out the target if usable_gateway: status = _exec_ssh_cmd(cmd, allow_failure=True, **kwargs) @@ -848,17 +849,21 @@ def wait_for_port(host, port=22, timeout=900, gateway=None): return True time.sleep(1) if time.time() - start > timeout: - log.error('Port connection timed out: %s', timeout) + log.error("Port connection timed out: %s", timeout) return False log.debug( - 'Retrying connection to host %s on port %s ' - 'via gateway %s on port %s. (try %s)', - host, port, ssh_gateway, ssh_gateway_port, trycount + "Retrying connection to host %s on port %s " + "via gateway %s on port %s. (try %s)", + host, + port, + ssh_gateway, + ssh_gateway_port, + trycount, ) class Client(object): - ''' + """ Wrap pypsexec.client.Client to fix some stability issues: - Set the service name from a keyword arg, this allows multiple service @@ -866,10 +871,17 @@ class Client(object): - Keep trying service and file deletes since they may not succeed on the first try. Raises an exception if they do not succeed after a timeout period. - ''' + """ - def __init__(self, server, username=None, password=None, port=445, - encrypt=True, service_name=None): + def __init__( + self, + server, + username=None, + password=None, + port=445, + encrypt=True, + service_name=None, + ): self.service_name = service_name self._exe_file = "{0}.exe".format(self.service_name) self._client = PsExecClient(server, username, password, port, encrypt) @@ -888,11 +900,11 @@ class Client(object): return self._client.run_executable(*args, **kwargs) def remove_service(self, wait_timeout=10, sleep_wait=1): - ''' + """ Removes the PAExec service and executable that was created as part of the create_service function. This does not remove any older executables or services from previous runs, use cleanup() instead for that purpose. - ''' + """ # Stops/remove the PAExec service and removes the executable log.debug("Deleting PAExec service at the end of the process") @@ -911,7 +923,7 @@ class Client(object): # delete the PAExec executable smb_tree = TreeConnect( self._client.session, - r"\\{0}\ADMIN$".format(self._client.connection.server_name) + r"\\{0}\ADMIN$".format(self._client.connection.server_name), ) log.info("Connecting to SMB Tree %s", smb_tree.share_name) smb_tree.connect() @@ -933,33 +945,28 @@ class Client(object): def run_winexe_command(cmd, args, host, username, password, port=445): - ''' + """ Run a command remotly via the winexe executable - ''' - creds = "-U '{0}%{1}' //{2}".format( - username, - password, - host - ) - logging_creds = "-U '{0}%XXX-REDACTED-XXX' //{1}".format( - username, - host - ) - cmd = 'winexe {0} {1} {2}'.format(creds, cmd, args) - logging_cmd = 'winexe {0} {1} {2}'.format(logging_creds, cmd, args) + """ + creds = "-U '{0}%{1}' //{2}".format(username, password, host) + logging_creds = "-U '{0}%XXX-REDACTED-XXX' //{1}".format(username, host) + cmd = "winexe {0} {1} {2}".format(creds, cmd, args) + logging_cmd = "winexe {0} {1} {2}".format(logging_creds, cmd, args) return win_cmd(cmd, logging_command=logging_cmd) def run_psexec_command(cmd, args, host, username, password, port=445): - ''' + """ Run a command remotly using the psexec protocol - ''' + """ if has_winexe() and not HAS_PSEXEC: ret_code = run_winexe_command(cmd, args, host, username, password, port) return None, None, ret_code - service_name = 'PS-Exec-{0}'.format(uuid.uuid4()) - stdout, stderr, ret_code = '', '', None - client = Client(host, username, password, port=port, encrypt=False, service_name=service_name) + service_name = "PS-Exec-{0}".format(uuid.uuid4()) + stdout, stderr, ret_code = "", "", None + client = Client( + host, username, password, port=port, encrypt=False, service_name=service_name + ) client.connect() try: client.create_service() @@ -971,14 +978,11 @@ def run_psexec_command(cmd, args, host, username, password, port=445): def wait_for_winexe(host, port, username, password, timeout=900): - ''' + """ Wait until winexe connection can be established. - ''' + """ start = time.time() - log.debug( - 'Attempting winexe connection to host %s on port %s', - host, port - ) + log.debug("Attempting winexe connection to host %s on port %s", host, port) try_count = 0 while True: try_count += 1 @@ -988,11 +992,11 @@ def wait_for_winexe(host, port, username, password, timeout=900): "sc", "query winexesvc", host, username, password, port ) if ret_code == 0: - log.debug('winexe connected...') + log.debug("winexe connected...") return True - log.debug('Return code was %s', ret_code) + log.debug("Return code was %s", ret_code) except socket.error as exc: - log.debug('Caught exception in wait_for_winexesvc: %s', exc) + log.debug("Caught exception in wait_for_winexesvc: %s", exc) if time.time() - start > timeout: return False @@ -1000,9 +1004,9 @@ def wait_for_winexe(host, port, username, password, timeout=900): def wait_for_psexecsvc(host, port, username, password, timeout=900): - ''' + """ Wait until psexec connection can be established. - ''' + """ if has_winexe() and not HAS_PSEXEC: return wait_for_winexe(host, port, username, password, timeout) start = time.time() @@ -1012,111 +1016,98 @@ def wait_for_psexecsvc(host, port, username, password, timeout=900): ret_code = 1 try: stdout, stderr, ret_code = run_psexec_command( - 'cmd.exe', '/c hostname', host, username, password, port=port + "cmd.exe", "/c hostname", host, username, password, port=port ) except Exception as exc: # pylint: disable=broad-except log.exception("Unable to execute command") if ret_code == 0: - log.debug('psexec connected...') + log.debug("psexec connected...") return True if time.time() - start > timeout: return False log.debug( - 'Retrying psexec connection to host {0} on port {1} ' - '(try {2})'.format( - host, - port, - try_count - ) + "Retrying psexec connection to host {0} on port {1} " + "(try {2})".format(host, port, try_count) ) time.sleep(1) -def wait_for_winrm(host, port, username, password, timeout=900, use_ssl=True, verify=True): - ''' +def wait_for_winrm( + host, port, username, password, timeout=900, use_ssl=True, verify=True +): + """ Wait until WinRM connection can be established. - ''' + """ # Ensure the winrm service is listening before attempting to connect wait_for_port(host=host, port=port, timeout=timeout) start = time.time() - log.debug( - 'Attempting WinRM connection to host %s on port %s', - host, port - ) - transport = 'ssl' + log.debug("Attempting WinRM connection to host %s on port %s", host, port) + transport = "ssl" if not use_ssl: - transport = 'ntlm' + transport = "ntlm" trycount = 0 while True: trycount += 1 try: - winrm_kwargs = {'target': host, - 'auth': (username, password), - 'transport': transport} + winrm_kwargs = { + "target": host, + "auth": (username, password), + "transport": transport, + } if not verify: log.debug("SSL validation for WinRM disabled.") - winrm_kwargs['server_cert_validation'] = 'ignore' + winrm_kwargs["server_cert_validation"] = "ignore" s = winrm.Session(**winrm_kwargs) - if hasattr(s.protocol, 'set_timeout'): + if hasattr(s.protocol, "set_timeout"): s.protocol.set_timeout(15) - log.trace('WinRM endpoint url: %s', s.url) - r = s.run_cmd('sc query winrm') + log.trace("WinRM endpoint url: %s", s.url) + r = s.run_cmd("sc query winrm") if r.status_code == 0: - log.debug('WinRM session connected...') + log.debug("WinRM session connected...") return s - log.debug('Return code was %s', r.status_code) + log.debug("Return code was %s", r.status_code) except WinRMTransportError as exc: - log.debug('Caught exception in wait_for_winrm: %s', exc) + log.debug("Caught exception in wait_for_winrm: %s", exc) if time.time() - start > timeout: - log.error('WinRM connection timed out: %s', timeout) + log.error("WinRM connection timed out: %s", timeout) return None log.debug( - 'Retrying WinRM connection to host %s on port %s (try %s)', - host, port, trycount + "Retrying WinRM connection to host %s on port %s (try %s)", + host, + port, + trycount, ) time.sleep(1) -def validate_windows_cred_winexe(host, - username='Administrator', - password=None, - retries=10, - retry_delay=1): - ''' +def validate_windows_cred_winexe( + host, username="Administrator", password=None, retries=10, retry_delay=1 +): + """ Check if the windows credentials are valid - ''' - cmd = "winexe -U '{0}%{1}' //{2} \"hostname\"".format( - username, - password, - host - ) + """ + cmd = "winexe -U '{0}%{1}' //{2} \"hostname\"".format(username, password, host) logging_cmd = "winexe -U '{0}%XXX-REDACTED-XXX' //{1} \"hostname\"".format( - username, - host + username, host ) for i in range(retries): - ret_code = win_cmd( - cmd, - logging_command=logging_cmd - ) + ret_code = win_cmd(cmd, logging_command=logging_cmd) return ret_code == 0 -def validate_windows_cred(host, - username='Administrator', - password=None, - retries=10, - retry_delay=1): - ''' +def validate_windows_cred( + host, username="Administrator", password=None, retries=10, retry_delay=1 +): + """ Check if the windows credentials are valid - ''' + """ for i in range(retries): ret_code = 1 try: stdout, stderr, ret_code = run_psexec_command( - 'cmd.exe', '/c hostname', host, username, password, port=445 + "cmd.exe", "/c hostname", host, username, password, port=445 ) except Exception as exc: # pylint: disable=broad-except log.exception("Exceoption while executing psexec") @@ -1126,55 +1117,69 @@ def validate_windows_cred(host, return ret_code == 0 -def wait_for_passwd(host, port=22, ssh_timeout=15, username='root', - password=None, key_filename=None, maxtries=15, - trysleep=1, display_ssh_output=True, gateway=None, - known_hosts_file='/dev/null', hard_timeout=None): - ''' +def wait_for_passwd( + host, + port=22, + ssh_timeout=15, + username="root", + password=None, + key_filename=None, + maxtries=15, + trysleep=1, + display_ssh_output=True, + gateway=None, + known_hosts_file="/dev/null", + hard_timeout=None, +): + """ Wait until ssh connection can be accessed via password or ssh key - ''' + """ trycount = 0 while trycount < maxtries: connectfail = False try: - kwargs = {'hostname': host, - 'port': port, - 'username': username, - 'password_retries': maxtries, - 'timeout': ssh_timeout, - 'display_ssh_output': display_ssh_output, - 'known_hosts_file': known_hosts_file, - 'ssh_timeout': ssh_timeout, - 'hard_timeout': hard_timeout} + kwargs = { + "hostname": host, + "port": port, + "username": username, + "password_retries": maxtries, + "timeout": ssh_timeout, + "display_ssh_output": display_ssh_output, + "known_hosts_file": known_hosts_file, + "ssh_timeout": ssh_timeout, + "hard_timeout": hard_timeout, + } kwargs.update(__ssh_gateway_config_dict(gateway)) if key_filename: if not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined key_filename \'{0}\' does not exist'.format( + "The defined key_filename '{0}' does not exist".format( key_filename ) ) - kwargs['key_filename'] = key_filename - log.debug('Using %s as the key_filename', key_filename) + kwargs["key_filename"] = key_filename + log.debug("Using %s as the key_filename", key_filename) elif password: - kwargs['password'] = password - log.debug('Using password authentication') + kwargs["password"] = password + log.debug("Using password authentication") trycount += 1 log.debug( - 'Attempting to authenticate as %s (try %s of %s)', - username, trycount, maxtries + "Attempting to authenticate as %s (try %s of %s)", + username, + trycount, + maxtries, ) - status = root_cmd('date', tty=False, sudo=False, **kwargs) + status = root_cmd("date", tty=False, sudo=False, **kwargs) if status != 0: connectfail = True if trycount < maxtries: time.sleep(trysleep) continue - log.error('Authentication failed: status code %s', status) + log.error("Authentication failed: status code %s", status) return False if connectfail is False: return True @@ -1187,54 +1192,56 @@ def wait_for_passwd(host, port=22, ssh_timeout=15, username='root', time.sleep(trysleep) -def deploy_windows(host, - port=445, - timeout=900, - username='Administrator', - password=None, - name=None, - sock_dir=None, - conf_file=None, - start_action=None, - parallel=False, - minion_pub=None, - minion_pem=None, - minion_conf=None, - keep_tmp=False, - script_args=None, - script_env=None, - port_timeout=15, - preseed_minion_keys=None, - win_installer=None, - master=None, - tmp_dir='C:\\salttmp', - opts=None, - master_sign_pub_file=None, - use_winrm=False, - winrm_port=5986, - winrm_use_ssl=True, - winrm_verify_ssl=True, - **kwargs): - ''' +def deploy_windows( + host, + port=445, + timeout=900, + username="Administrator", + password=None, + name=None, + sock_dir=None, + conf_file=None, + start_action=None, + parallel=False, + minion_pub=None, + minion_pem=None, + minion_conf=None, + keep_tmp=False, + script_args=None, + script_env=None, + port_timeout=15, + preseed_minion_keys=None, + win_installer=None, + master=None, + tmp_dir="C:\\salttmp", + opts=None, + master_sign_pub_file=None, + use_winrm=False, + winrm_port=5986, + winrm_use_ssl=True, + winrm_verify_ssl=True, + **kwargs +): + """ Copy the install files to a remote Windows box, and execute them - ''' + """ if not isinstance(opts, dict): opts = {} if use_winrm and not HAS_WINRM: - log.error('WinRM requested but module winrm could not be imported') + log.error("WinRM requested but module winrm could not be imported") return False if not use_winrm and has_winexe() and not HAS_PSEXEC: salt.utils.versions.warn_until( - 'Sodium', - 'Support for winexe has been deprecated and will be removed in ' - 'Sodium, please install pypsexec instead.' + "Sodium", + "Support for winexe has been deprecated and will be removed in " + "Sodium, please install pypsexec instead.", ) starttime = time.mktime(time.localtime()) - log.debug('Deploying %s at %s (Windows)', host, starttime) - log.trace('HAS_WINRM: %s, use_winrm: %s', HAS_WINRM, use_winrm) + log.debug("Deploying %s at %s (Windows)", host, starttime) + log.trace("HAS_WINRM: %s, use_winrm: %s", HAS_WINRM, use_winrm) port_available = wait_for_port(host=host, port=port, timeout=port_timeout * 60) @@ -1245,75 +1252,92 @@ def deploy_windows(host, winrm_session = None if HAS_WINRM and use_winrm: - winrm_session = wait_for_winrm(host=host, port=winrm_port, - username=username, password=password, - timeout=port_timeout * 60, use_ssl=winrm_use_ssl, - verify=winrm_verify_ssl) + winrm_session = wait_for_winrm( + host=host, + port=winrm_port, + username=username, + password=password, + timeout=port_timeout * 60, + use_ssl=winrm_use_ssl, + verify=winrm_verify_ssl, + ) if winrm_session is not None: service_available = True else: - service_available = wait_for_psexecsvc(host=host, port=port, - username=username, password=password, - timeout=port_timeout * 60) + service_available = wait_for_psexecsvc( + host=host, + port=port, + username=username, + password=password, + timeout=port_timeout * 60, + ) if port_available and service_available: - log.debug('SMB port %s on %s is available', port, host) - log.debug('Logging into %s:%s as %s', host, port, username) + log.debug("SMB port %s on %s is available", port, host) + log.debug("Logging into %s:%s as %s", host, port, username) newtimeout = timeout - (time.mktime(time.localtime()) - starttime) smb_conn = salt.utils.smb.get_conn(host, username, password) if smb_conn is False: - log.error('Please install smbprotocol to enable SMB functionality') + log.error("Please install smbprotocol to enable SMB functionality") return False - salt.utils.smb.mkdirs('salttemp', conn=smb_conn) - salt.utils.smb.mkdirs('salt/conf/pki/minion', conn=smb_conn) + salt.utils.smb.mkdirs("salttemp", conn=smb_conn) + salt.utils.smb.mkdirs("salt/conf/pki/minion", conn=smb_conn) if minion_pub: - salt.utils.smb.put_str(minion_pub, 'salt\\conf\\pki\\minion\\minion.pub', conn=smb_conn) + salt.utils.smb.put_str( + minion_pub, "salt\\conf\\pki\\minion\\minion.pub", conn=smb_conn + ) if minion_pem: - salt.utils.smb.put_str(minion_pem, 'salt\\conf\\pki\\minion\\minion.pem', conn=smb_conn) + salt.utils.smb.put_str( + minion_pem, "salt\\conf\\pki\\minion\\minion.pem", conn=smb_conn + ) if master_sign_pub_file: # Read master-sign.pub file - log.debug("Copying master_sign.pub file from %s to minion", master_sign_pub_file) + log.debug( + "Copying master_sign.pub file from %s to minion", master_sign_pub_file + ) try: salt.utils.smb.put_file( master_sign_pub_file, - 'salt\\conf\\pki\\minion\\master_sign.pub', - 'C$', + "salt\\conf\\pki\\minion\\master_sign.pub", + "C$", conn=smb_conn, ) except Exception as e: # pylint: disable=broad-except - log.debug("Exception copying master_sign.pub file %s to minion", master_sign_pub_file) + log.debug( + "Exception copying master_sign.pub file %s to minion", + master_sign_pub_file, + ) # Copy over win_installer # win_installer refers to a file such as: # /root/Salt-Minion-0.17.0-win32-Setup.exe # ..which exists on the same machine as salt-cloud - comps = win_installer.split('/') - local_path = '/'.join(comps[:-1]) + comps = win_installer.split("/") + local_path = "/".join(comps[:-1]) installer = comps[-1] salt.utils.smb.put_file( - win_installer, - 'salttemp\\{0}'.format(installer), - 'C$', - conn=smb_conn, + win_installer, "salttemp\\{0}".format(installer), "C$", conn=smb_conn, ) if use_winrm: - winrm_cmd(winrm_session, 'c:\\salttemp\\{0}'.format(installer), ['/S', '/master={0}'.format(master), - '/minion-name={0}'.format(name)] + winrm_cmd( + winrm_session, + "c:\\salttemp\\{0}".format(installer), + ["/S", "/master={0}".format(master), "/minion-name={0}".format(name)], ) else: - cmd = 'c:\\salttemp\\{0}'.format(installer) + cmd = "c:\\salttemp\\{0}".format(installer) args = "/S /master={0} /minion-name={1}".format(master, name) stdout, stderr, ret_code = run_psexec_command( cmd, args, host, username, password ) if ret_code != 0: - raise Exception('Fail installer {0}'.format(ret_code)) + raise Exception("Fail installer {0}".format(ret_code)) # Copy over minion_conf if minion_conf: @@ -1321,194 +1345,210 @@ def deploy_windows(host, # Let's not just fail regarding this change, specially # since we can handle it raise DeprecationWarning( - '`salt.utils.cloud.deploy_windows` now only accepts ' - 'dictionaries for its `minion_conf` parameter. ' - 'Loading YAML...' + "`salt.utils.cloud.deploy_windows` now only accepts " + "dictionaries for its `minion_conf` parameter. " + "Loading YAML..." ) - minion_grains = minion_conf.pop('grains', {}) + minion_grains = minion_conf.pop("grains", {}) if minion_grains: salt.utils.smb.put_str( - salt_config_to_yaml(minion_grains, line_break='\r\n'), - 'salt\\conf\\grains', - conn=smb_conn + salt_config_to_yaml(minion_grains, line_break="\r\n"), + "salt\\conf\\grains", + conn=smb_conn, ) # Add special windows minion configuration # that must be in the minion config file windows_minion_conf = { - 'ipc_mode': 'tcp', - 'root_dir': 'c:\\salt', - 'pki_dir': '/conf/pki/minion', - 'multiprocessing': False, + "ipc_mode": "tcp", + "root_dir": "c:\\salt", + "pki_dir": "/conf/pki/minion", + "multiprocessing": False, } minion_conf = dict(minion_conf, **windows_minion_conf) salt.utils.smb.put_str( - salt_config_to_yaml(minion_conf, line_break='\r\n'), - 'salt\\conf\\minion', - conn=smb_conn + salt_config_to_yaml(minion_conf, line_break="\r\n"), + "salt\\conf\\minion", + conn=smb_conn, ) # Delete C:\salttmp\ and installer file # Unless keep_tmp is True if not keep_tmp: if use_winrm: - winrm_cmd(winrm_session, 'rmdir', ['/Q', '/S', 'C:\\salttemp\\']) + winrm_cmd(winrm_session, "rmdir", ["/Q", "/S", "C:\\salttemp\\"]) else: - salt.utils.smb.delete_file('salttemp\\{0}'.format(installer), 'C$', conn=smb_conn) - salt.utils.smb.delete_directory('salttemp', 'C$', conn=smb_conn) + salt.utils.smb.delete_file( + "salttemp\\{0}".format(installer), "C$", conn=smb_conn + ) + salt.utils.smb.delete_directory("salttemp", "C$", conn=smb_conn) # Shell out to psexec to ensure salt-minion service started if use_winrm: - winrm_cmd(winrm_session, 'sc', ['stop', 'salt-minion']) + winrm_cmd(winrm_session, "sc", ["stop", "salt-minion"]) time.sleep(5) - winrm_cmd(winrm_session, 'sc', ['start', 'salt-minion']) + winrm_cmd(winrm_session, "sc", ["start", "salt-minion"]) else: stdout, stderr, ret_code = run_psexec_command( - 'cmd.exe', '/c sc stop salt-minion', host, username, password + "cmd.exe", "/c sc stop salt-minion", host, username, password ) if ret_code != 0: return False time.sleep(5) - log.debug('Run psexec: sc start salt-minion') + log.debug("Run psexec: sc start salt-minion") stdout, stderr, ret_code = run_psexec_command( - 'cmd.exe', '/c sc start salt-minion', host, username, password + "cmd.exe", "/c sc start salt-minion", host, username, password ) if ret_code != 0: return False # Fire deploy action fire_event( - 'event', - '{0} has been deployed at {1}'.format(name, host), - 'salt/cloud/{0}/deploy_windows'.format(name), - args={'name': name}, - sock_dir=opts.get( - 'sock_dir', - os.path.join(__opts__['sock_dir'], 'master')), - transport=opts.get('transport', 'zeromq') + "event", + "{0} has been deployed at {1}".format(name, host), + "salt/cloud/{0}/deploy_windows".format(name), + args={"name": name}, + sock_dir=opts.get("sock_dir", os.path.join(__opts__["sock_dir"], "master")), + transport=opts.get("transport", "zeromq"), ) return True return False -def deploy_script(host, - port=22, - timeout=900, - username='root', - password=None, - key_filename=None, - script=None, - name=None, - sock_dir=None, - provider=None, - conf_file=None, - start_action=None, - make_master=False, - master_pub=None, - master_pem=None, - master_conf=None, - minion_pub=None, - minion_pem=None, - minion_conf=None, - keep_tmp=False, - script_args=None, - script_env=None, - ssh_timeout=15, - maxtries=15, - make_syndic=False, - make_minion=True, - display_ssh_output=True, - preseed_minion_keys=None, - parallel=False, - sudo_password=None, - sudo=False, - tty=None, - vm_=None, - opts=None, - tmp_dir='/tmp/.saltcloud', - file_map=None, - master_sign_pub_file=None, - cloud_grains=None, - force_minion_config=False, - **kwargs): - ''' +def deploy_script( + host, + port=22, + timeout=900, + username="root", + password=None, + key_filename=None, + script=None, + name=None, + sock_dir=None, + provider=None, + conf_file=None, + start_action=None, + make_master=False, + master_pub=None, + master_pem=None, + master_conf=None, + minion_pub=None, + minion_pem=None, + minion_conf=None, + keep_tmp=False, + script_args=None, + script_env=None, + ssh_timeout=15, + maxtries=15, + make_syndic=False, + make_minion=True, + display_ssh_output=True, + preseed_minion_keys=None, + parallel=False, + sudo_password=None, + sudo=False, + tty=None, + vm_=None, + opts=None, + tmp_dir="/tmp/.saltcloud", + file_map=None, + master_sign_pub_file=None, + cloud_grains=None, + force_minion_config=False, + **kwargs +): + """ Copy a deploy script to a remote server, execute it, and remove it - ''' + """ if not isinstance(opts, dict): opts = {} vm_ = vm_ or {} # if None, default to empty dict cloud_grains = cloud_grains or {} - tmp_dir = '{0}-{1}'.format(tmp_dir.rstrip('/'), uuid.uuid4()) + tmp_dir = "{0}-{1}".format(tmp_dir.rstrip("/"), uuid.uuid4()) deploy_command = salt.config.get_cloud_config_value( - 'deploy_command', vm_, opts, - default=os.path.join(tmp_dir, 'deploy.sh')) + "deploy_command", vm_, opts, default=os.path.join(tmp_dir, "deploy.sh") + ) if key_filename is not None and not os.path.isfile(key_filename): raise SaltCloudConfigError( - 'The defined key_filename \'{0}\' does not exist'.format( - key_filename - ) + "The defined key_filename '{0}' does not exist".format(key_filename) ) gateway = None - if 'gateway' in kwargs: - gateway = kwargs['gateway'] + if "gateway" in kwargs: + gateway = kwargs["gateway"] starttime = time.localtime() - log.debug( - 'Deploying %s at %s', - host, time.strftime('%Y-%m-%d %H:%M:%S', starttime) - ) - known_hosts_file = kwargs.get('known_hosts_file', '/dev/null') - hard_timeout = opts.get('hard_timeout', None) + log.debug("Deploying %s at %s", host, time.strftime("%Y-%m-%d %H:%M:%S", starttime)) + known_hosts_file = kwargs.get("known_hosts_file", "/dev/null") + hard_timeout = opts.get("hard_timeout", None) if wait_for_port(host=host, port=port, gateway=gateway): - log.debug('SSH port %s on %s is available', port, host) - if wait_for_passwd(host, port=port, username=username, - password=password, key_filename=key_filename, - ssh_timeout=ssh_timeout, - display_ssh_output=display_ssh_output, - gateway=gateway, known_hosts_file=known_hosts_file, - maxtries=maxtries, hard_timeout=hard_timeout): + log.debug("SSH port %s on %s is available", port, host) + if wait_for_passwd( + host, + port=port, + username=username, + password=password, + key_filename=key_filename, + ssh_timeout=ssh_timeout, + display_ssh_output=display_ssh_output, + gateway=gateway, + known_hosts_file=known_hosts_file, + maxtries=maxtries, + hard_timeout=hard_timeout, + ): - log.debug('Logging into %s:%s as %s', host, port, username) + log.debug("Logging into %s:%s as %s", host, port, username) ssh_kwargs = { - 'hostname': host, - 'port': port, - 'username': username, - 'timeout': ssh_timeout, - 'display_ssh_output': display_ssh_output, - 'sudo_password': sudo_password, - 'sftp': opts.get('use_sftp', False) + "hostname": host, + "port": port, + "username": username, + "timeout": ssh_timeout, + "display_ssh_output": display_ssh_output, + "sudo_password": sudo_password, + "sftp": opts.get("use_sftp", False), } ssh_kwargs.update(__ssh_gateway_config_dict(gateway)) if key_filename: - log.debug('Using %s as the key_filename', key_filename) - ssh_kwargs['key_filename'] = key_filename - elif password and kwargs.get('has_ssh_agent', False) is False: - ssh_kwargs['password'] = password + log.debug("Using %s as the key_filename", key_filename) + ssh_kwargs["key_filename"] = key_filename + elif password and kwargs.get("has_ssh_agent", False) is False: + ssh_kwargs["password"] = password - if root_cmd('test -e \'{0}\''.format(tmp_dir), tty, sudo, - allow_failure=True, **ssh_kwargs): - ret = root_cmd(('sh -c "( mkdir -p -m 700 \'{0}\' )"').format(tmp_dir), - tty, sudo, **ssh_kwargs) + if root_cmd( + "test -e '{0}'".format(tmp_dir), + tty, + sudo, + allow_failure=True, + **ssh_kwargs + ): + ret = root_cmd( + ("sh -c \"( mkdir -p -m 700 '{0}' )\"").format(tmp_dir), + tty, + sudo, + **ssh_kwargs + ) if ret: raise SaltCloudSystemExit( - 'Can\'t create temporary ' - 'directory in {0} !'.format(tmp_dir) + "Can't create temporary " "directory in {0} !".format(tmp_dir) ) if sudo: - comps = tmp_dir.lstrip('/').rstrip('/').split('/') + comps = tmp_dir.lstrip("/").rstrip("/").split("/") if len(comps) > 0: - if len(comps) > 1 or comps[0] != 'tmp': + if len(comps) > 1 or comps[0] != "tmp": ret = root_cmd( 'chown {0} "{1}"'.format(username, tmp_dir), - tty, sudo, **ssh_kwargs + tty, + sudo, + **ssh_kwargs ) if ret: raise SaltCloudSystemExit( - 'Cant set {0} ownership on {1}'.format( - username, tmp_dir)) + "Cant set {0} ownership on {1}".format( + username, tmp_dir + ) + ) if not isinstance(file_map, dict): file_map = {} @@ -1524,170 +1564,198 @@ def deploy_script(host, log.error( 'The local file "%s" does not exist, and will not be ' 'copied to "%s" on the target system', - local_file, remote_file + local_file, + remote_file, ) file_map_fail.append({local_file: remote_file}) continue if os.path.isdir(local_file): dir_name = os.path.basename(local_file) - remote_dir = os.path.join(os.path.dirname(remote_file), - dir_name) + remote_dir = os.path.join(os.path.dirname(remote_file), dir_name) else: remote_dir = os.path.dirname(remote_file) if remote_dir not in remote_dirs: - root_cmd('mkdir -p \'{0}\''.format(remote_dir), tty, sudo, **ssh_kwargs) - if ssh_kwargs['username'] != 'root': + root_cmd( + "mkdir -p '{0}'".format(remote_dir), tty, sudo, **ssh_kwargs + ) + if ssh_kwargs["username"] != "root": root_cmd( - 'chown {0} \'{1}\''.format( - ssh_kwargs['username'], remote_dir + "chown {0} '{1}'".format( + ssh_kwargs["username"], remote_dir ), - tty, sudo, **ssh_kwargs + tty, + sudo, + **ssh_kwargs ) remote_dirs.append(remote_dir) - ssh_file( - opts, remote_file, kwargs=ssh_kwargs, local_file=local_file - ) + ssh_file(opts, remote_file, kwargs=ssh_kwargs, local_file=local_file) file_map_success.append({local_file: remote_file}) # Minion configuration if minion_pem: - ssh_file(opts, '{0}/minion.pem'.format(tmp_dir), minion_pem, ssh_kwargs) - ret = root_cmd('chmod 600 \'{0}/minion.pem\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) + ssh_file(opts, "{0}/minion.pem".format(tmp_dir), minion_pem, ssh_kwargs) + ret = root_cmd( + "chmod 600 '{0}/minion.pem'".format(tmp_dir), + tty, + sudo, + **ssh_kwargs + ) if ret: raise SaltCloudSystemExit( - 'Can\'t set perms on {0}/minion.pem'.format(tmp_dir)) + "Can't set perms on {0}/minion.pem".format(tmp_dir) + ) if minion_pub: - ssh_file(opts, '{0}/minion.pub'.format(tmp_dir), minion_pub, ssh_kwargs) + ssh_file(opts, "{0}/minion.pub".format(tmp_dir), minion_pub, ssh_kwargs) if master_sign_pub_file: - ssh_file(opts, '{0}/master_sign.pub'.format(tmp_dir), kwargs=ssh_kwargs, local_file=master_sign_pub_file) + ssh_file( + opts, + "{0}/master_sign.pub".format(tmp_dir), + kwargs=ssh_kwargs, + local_file=master_sign_pub_file, + ) if minion_conf: if not isinstance(minion_conf, dict): # Let's not just fail regarding this change, specially # since we can handle it raise DeprecationWarning( - '`salt.utils.cloud.deploy_script now only accepts ' - 'dictionaries for it\'s `minion_conf` parameter. ' - 'Loading YAML...' + "`salt.utils.cloud.deploy_script now only accepts " + "dictionaries for it's `minion_conf` parameter. " + "Loading YAML..." ) - minion_grains = minion_conf.pop('grains', {}) + minion_grains = minion_conf.pop("grains", {}) if minion_grains: ssh_file( opts, - '{0}/grains'.format(tmp_dir), + "{0}/grains".format(tmp_dir), salt_config_to_yaml(minion_grains), - ssh_kwargs + ssh_kwargs, ) - if cloud_grains and opts.get('enable_cloud_grains', True): - minion_conf['grains'] = {'salt-cloud': cloud_grains} + if cloud_grains and opts.get("enable_cloud_grains", True): + minion_conf["grains"] = {"salt-cloud": cloud_grains} ssh_file( opts, - '{0}/minion'.format(tmp_dir), + "{0}/minion".format(tmp_dir), salt_config_to_yaml(minion_conf), - ssh_kwargs + ssh_kwargs, ) # Master configuration if master_pem: - ssh_file(opts, '{0}/master.pem'.format(tmp_dir), master_pem, ssh_kwargs) - ret = root_cmd('chmod 600 \'{0}/master.pem\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) + ssh_file(opts, "{0}/master.pem".format(tmp_dir), master_pem, ssh_kwargs) + ret = root_cmd( + "chmod 600 '{0}/master.pem'".format(tmp_dir), + tty, + sudo, + **ssh_kwargs + ) if ret: raise SaltCloudSystemExit( - 'Cant set perms on {0}/master.pem'.format(tmp_dir)) + "Cant set perms on {0}/master.pem".format(tmp_dir) + ) if master_pub: - ssh_file(opts, '{0}/master.pub'.format(tmp_dir), master_pub, ssh_kwargs) + ssh_file(opts, "{0}/master.pub".format(tmp_dir), master_pub, ssh_kwargs) if master_conf: if not isinstance(master_conf, dict): # Let's not just fail regarding this change, specially # since we can handle it raise DeprecationWarning( - '`salt.utils.cloud.deploy_script now only accepts ' - 'dictionaries for it\'s `master_conf` parameter. ' - 'Loading from YAML ...' + "`salt.utils.cloud.deploy_script now only accepts " + "dictionaries for it's `master_conf` parameter. " + "Loading from YAML ..." ) ssh_file( opts, - '{0}/master'.format(tmp_dir), + "{0}/master".format(tmp_dir), salt_config_to_yaml(master_conf), - ssh_kwargs + ssh_kwargs, ) # XXX: We need to make these paths configurable - preseed_minion_keys_tempdir = '{0}/preseed-minion-keys'.format( - tmp_dir) + preseed_minion_keys_tempdir = "{0}/preseed-minion-keys".format(tmp_dir) if preseed_minion_keys is not None: # Create remote temp dir ret = root_cmd( - 'mkdir \'{0}\''.format(preseed_minion_keys_tempdir), - tty, sudo, **ssh_kwargs + "mkdir '{0}'".format(preseed_minion_keys_tempdir), + tty, + sudo, + **ssh_kwargs ) if ret: raise SaltCloudSystemExit( - 'Cant create {0}'.format(preseed_minion_keys_tempdir)) + "Cant create {0}".format(preseed_minion_keys_tempdir) + ) ret = root_cmd( - 'chmod 700 \'{0}\''.format(preseed_minion_keys_tempdir), - tty, sudo, **ssh_kwargs + "chmod 700 '{0}'".format(preseed_minion_keys_tempdir), + tty, + sudo, + **ssh_kwargs ) if ret: raise SaltCloudSystemExit( - 'Can\'t set perms on {0}'.format( - preseed_minion_keys_tempdir)) - if ssh_kwargs['username'] != 'root': + "Can't set perms on {0}".format(preseed_minion_keys_tempdir) + ) + if ssh_kwargs["username"] != "root": root_cmd( - 'chown {0} \'{1}\''.format( - ssh_kwargs['username'], preseed_minion_keys_tempdir + "chown {0} '{1}'".format( + ssh_kwargs["username"], preseed_minion_keys_tempdir ), - tty, sudo, **ssh_kwargs + tty, + sudo, + **ssh_kwargs ) # Copy pre-seed minion keys for minion_id, minion_key in six.iteritems(preseed_minion_keys): - rpath = os.path.join( - preseed_minion_keys_tempdir, minion_id - ) + rpath = os.path.join(preseed_minion_keys_tempdir, minion_id) ssh_file(opts, rpath, minion_key, ssh_kwargs) - if ssh_kwargs['username'] != 'root': + if ssh_kwargs["username"] != "root": root_cmd( - 'chown -R root \'{0}\''.format( - preseed_minion_keys_tempdir - ), - tty, sudo, **ssh_kwargs + "chown -R root '{0}'".format(preseed_minion_keys_tempdir), + tty, + sudo, + **ssh_kwargs ) if ret: raise SaltCloudSystemExit( - 'Can\'t set ownership for {0}'.format( - preseed_minion_keys_tempdir)) + "Can't set ownership for {0}".format( + preseed_minion_keys_tempdir + ) + ) # Run any pre-flight commands before running deploy scripts - preflight_cmds = kwargs.get('preflight_cmds', []) + preflight_cmds = kwargs.get("preflight_cmds", []) for command in preflight_cmds: cmd_ret = root_cmd(command, tty, sudo, **ssh_kwargs) if cmd_ret: raise SaltCloudSystemExit( - 'Pre-flight command failed: \'{0}\''.format(command) + "Pre-flight command failed: '{0}'".format(command) ) # The actual deploy script if script: # got strange escaping issues with sudoer, going onto a # subshell fixes that - ssh_file(opts, '{0}/deploy.sh'.format(tmp_dir), script, ssh_kwargs) + ssh_file(opts, "{0}/deploy.sh".format(tmp_dir), script, ssh_kwargs) ret = root_cmd( - ('sh -c "( chmod +x \'{0}/deploy.sh\' )";' - 'exit $?').format(tmp_dir), - tty, sudo, **ssh_kwargs) + ("sh -c \"( chmod +x '{0}/deploy.sh' )\";" "exit $?").format( + tmp_dir + ), + tty, + sudo, + **ssh_kwargs + ) if ret: raise SaltCloudSystemExit( - 'Can\'t set perms on {0}/deploy.sh'.format(tmp_dir)) + "Can't set perms on {0}/deploy.sh".format(tmp_dir) + ) time_used = time.mktime(time.localtime()) - time.mktime(starttime) newtimeout = timeout - time_used @@ -1699,134 +1767,155 @@ def deploy_script(host, if start_action and not parallel: queue = multiprocessing.Queue() process = multiprocessing.Process( - target=check_auth, kwargs=dict( - name=name, sock_dir=sock_dir, - timeout=newtimeout, queue=queue - ) + target=check_auth, + kwargs=dict( + name=name, sock_dir=sock_dir, timeout=newtimeout, queue=queue + ), ) - log.debug('Starting new process to wait for salt-minion') + log.debug("Starting new process to wait for salt-minion") process.start() # Run the deploy script if script: - if 'bootstrap-salt' in script: - deploy_command += ' -c \'{0}\''.format(tmp_dir) + if "bootstrap-salt" in script: + deploy_command += " -c '{0}'".format(tmp_dir) if force_minion_config: - deploy_command += ' -F' + deploy_command += " -F" if make_syndic is True: - deploy_command += ' -S' + deploy_command += " -S" if make_master is True: - deploy_command += ' -M' + deploy_command += " -M" if make_minion is False: - deploy_command += ' -N' + deploy_command += " -N" if keep_tmp is True: - deploy_command += ' -K' + deploy_command += " -K" if preseed_minion_keys is not None: - deploy_command += ' -k \'{0}\''.format( + deploy_command += " -k '{0}'".format( preseed_minion_keys_tempdir ) if script_args: - deploy_command += ' {0}'.format(script_args) + deploy_command += " {0}".format(script_args) if script_env: if not isinstance(script_env, dict): raise SaltCloudSystemExit( - 'The \'script_env\' configuration setting NEEDS ' - 'to be a dictionary not a {0}'.format( - type(script_env) - ) + "The 'script_env' configuration setting NEEDS " + "to be a dictionary not a {0}".format(type(script_env)) ) - environ_script_contents = ['#!/bin/sh'] + environ_script_contents = ["#!/bin/sh"] for key, value in six.iteritems(script_env): environ_script_contents.append( - 'setenv {0} \'{1}\' >/dev/null 2>&1 || ' - 'export {0}=\'{1}\''.format(key, value) + "setenv {0} '{1}' >/dev/null 2>&1 || " + "export {0}='{1}'".format(key, value) ) environ_script_contents.append(deploy_command) # Upload our environ setter wrapper ssh_file( opts, - '{0}/environ-deploy-wrapper.sh'.format(tmp_dir), - '\n'.join(environ_script_contents), - ssh_kwargs + "{0}/environ-deploy-wrapper.sh".format(tmp_dir), + "\n".join(environ_script_contents), + ssh_kwargs, ) root_cmd( - 'chmod +x \'{0}/environ-deploy-wrapper.sh\''.format(tmp_dir), - tty, sudo, **ssh_kwargs + "chmod +x '{0}/environ-deploy-wrapper.sh'".format(tmp_dir), + tty, + sudo, + **ssh_kwargs ) # The deploy command is now our wrapper - deploy_command = '\'{0}/environ-deploy-wrapper.sh\''.format( - tmp_dir, - ) + deploy_command = "'{0}/environ-deploy-wrapper.sh'".format(tmp_dir,) if root_cmd(deploy_command, tty, sudo, **ssh_kwargs) != 0: raise SaltCloudSystemExit( - 'Executing the command \'{0}\' failed'.format( - deploy_command - ) + "Executing the command '{0}' failed".format(deploy_command) ) - log.debug('Executed command \'%s\'', deploy_command) + log.debug("Executed command '%s'", deploy_command) # Remove the deploy script if not keep_tmp: - root_cmd('rm -f \'{0}/deploy.sh\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) - log.debug('Removed %s/deploy.sh', tmp_dir) + root_cmd( + "rm -f '{0}/deploy.sh'".format(tmp_dir), tty, sudo, **ssh_kwargs + ) + log.debug("Removed %s/deploy.sh", tmp_dir) if script_env: root_cmd( - 'rm -f \'{0}/environ-deploy-wrapper.sh\''.format( - tmp_dir - ), - tty, sudo, **ssh_kwargs + "rm -f '{0}/environ-deploy-wrapper.sh'".format(tmp_dir), + tty, + sudo, + **ssh_kwargs ) - log.debug('Removed %s/environ-deploy-wrapper.sh', tmp_dir) + log.debug("Removed %s/environ-deploy-wrapper.sh", tmp_dir) if keep_tmp: - log.debug('Not removing deployment files from %s/', tmp_dir) + log.debug("Not removing deployment files from %s/", tmp_dir) else: # Remove minion configuration if minion_pub: - root_cmd('rm -f \'{0}/minion.pub\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) - log.debug('Removed %s/minion.pub', tmp_dir) + root_cmd( + "rm -f '{0}/minion.pub'".format(tmp_dir), + tty, + sudo, + **ssh_kwargs + ) + log.debug("Removed %s/minion.pub", tmp_dir) if minion_pem: - root_cmd('rm -f \'{0}/minion.pem\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) - log.debug('Removed %s/minion.pem', tmp_dir) + root_cmd( + "rm -f '{0}/minion.pem'".format(tmp_dir), + tty, + sudo, + **ssh_kwargs + ) + log.debug("Removed %s/minion.pem", tmp_dir) if minion_conf: - root_cmd('rm -f \'{0}/grains\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) - log.debug('Removed %s/grains', tmp_dir) - root_cmd('rm -f \'{0}/minion\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) - log.debug('Removed %s/minion', tmp_dir) + root_cmd( + "rm -f '{0}/grains'".format(tmp_dir), tty, sudo, **ssh_kwargs + ) + log.debug("Removed %s/grains", tmp_dir) + root_cmd( + "rm -f '{0}/minion'".format(tmp_dir), tty, sudo, **ssh_kwargs + ) + log.debug("Removed %s/minion", tmp_dir) if master_sign_pub_file: - root_cmd('rm -f {0}/master_sign.pub'.format(tmp_dir), - tty, sudo, **ssh_kwargs) - log.debug('Removed %s/master_sign.pub', tmp_dir) + root_cmd( + "rm -f {0}/master_sign.pub".format(tmp_dir), + tty, + sudo, + **ssh_kwargs + ) + log.debug("Removed %s/master_sign.pub", tmp_dir) # Remove master configuration if master_pub: - root_cmd('rm -f \'{0}/master.pub\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) - log.debug('Removed %s/master.pub', tmp_dir) + root_cmd( + "rm -f '{0}/master.pub'".format(tmp_dir), + tty, + sudo, + **ssh_kwargs + ) + log.debug("Removed %s/master.pub", tmp_dir) if master_pem: - root_cmd('rm -f \'{0}/master.pem\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) - log.debug('Removed %s/master.pem', tmp_dir) + root_cmd( + "rm -f '{0}/master.pem'".format(tmp_dir), + tty, + sudo, + **ssh_kwargs + ) + log.debug("Removed %s/master.pem", tmp_dir) if master_conf: - root_cmd('rm -f \'{0}/master\''.format(tmp_dir), - tty, sudo, **ssh_kwargs) - log.debug('Removed %s/master', tmp_dir) + root_cmd( + "rm -f '{0}/master'".format(tmp_dir), tty, sudo, **ssh_kwargs + ) + log.debug("Removed %s/master", tmp_dir) # Remove pre-seed keys directory if preseed_minion_keys is not None: root_cmd( - 'rm -rf \'{0}\''.format( - preseed_minion_keys_tempdir - ), tty, sudo, **ssh_kwargs + "rm -rf '{0}'".format(preseed_minion_keys_tempdir), + tty, + sudo, + **ssh_kwargs ) - log.debug('Removed %s', preseed_minion_keys_tempdir) + log.debug("Removed %s", preseed_minion_keys_tempdir) if start_action and not parallel: queuereturn = queue.get() @@ -1838,126 +1927,144 @@ def deploy_script(host, # ) # for line in output: # print(line) - log.info('Executing %s on the salt-minion', start_action) + log.info("Executing %s on the salt-minion", start_action) root_cmd( - 'salt-call {0}'.format(start_action), - tty, sudo, **ssh_kwargs - ) - log.info( - 'Finished executing %s on the salt-minion', - start_action + "salt-call {0}".format(start_action), tty, sudo, **ssh_kwargs ) + log.info("Finished executing %s on the salt-minion", start_action) # Fire deploy action fire_event( - 'event', - '{0} has been deployed at {1}'.format(name, host), - 'salt/cloud/{0}/deploy_script'.format(name), - args={ - 'name': name, - 'host': host - }, + "event", + "{0} has been deployed at {1}".format(name, host), + "salt/cloud/{0}/deploy_script".format(name), + args={"name": name, "host": host}, sock_dir=opts.get( - 'sock_dir', - os.path.join(__opts__['sock_dir'], 'master')), - transport=opts.get('transport', 'zeromq') + "sock_dir", os.path.join(__opts__["sock_dir"], "master") + ), + transport=opts.get("transport", "zeromq"), ) if file_map_fail or file_map_success: return { - 'File Upload Success': file_map_success, - 'File Upload Failure': file_map_fail, + "File Upload Success": file_map_success, + "File Upload Failure": file_map_fail, } return True return False -def run_inline_script(host, - name=None, - port=22, - timeout=900, - username='root', - key_filename=None, - inline_script=None, - ssh_timeout=15, - display_ssh_output=True, - parallel=False, - sudo_password=None, - sudo=False, - password=None, - tty=None, - opts=None, - tmp_dir='/tmp/.saltcloud-inline_script', - **kwargs): - ''' +def run_inline_script( + host, + name=None, + port=22, + timeout=900, + username="root", + key_filename=None, + inline_script=None, + ssh_timeout=15, + display_ssh_output=True, + parallel=False, + sudo_password=None, + sudo=False, + password=None, + tty=None, + opts=None, + tmp_dir="/tmp/.saltcloud-inline_script", + **kwargs +): + """ Run the inline script commands, one by one :**kwargs: catch all other things we may get but don't actually need/use - ''' + """ gateway = None - if 'gateway' in kwargs: - gateway = kwargs['gateway'] + if "gateway" in kwargs: + gateway = kwargs["gateway"] starttime = time.mktime(time.localtime()) - log.debug('Deploying %s at %s', host, starttime) + log.debug("Deploying %s at %s", host, starttime) - known_hosts_file = kwargs.get('known_hosts_file', '/dev/null') + known_hosts_file = kwargs.get("known_hosts_file", "/dev/null") if wait_for_port(host=host, port=port, gateway=gateway): - log.debug('SSH port %s on %s is available', port, host) + log.debug("SSH port %s on %s is available", port, host) newtimeout = timeout - (time.mktime(time.localtime()) - starttime) - if wait_for_passwd(host, port=port, username=username, - password=password, key_filename=key_filename, - ssh_timeout=ssh_timeout, - display_ssh_output=display_ssh_output, - gateway=gateway, known_hosts_file=known_hosts_file): + if wait_for_passwd( + host, + port=port, + username=username, + password=password, + key_filename=key_filename, + ssh_timeout=ssh_timeout, + display_ssh_output=display_ssh_output, + gateway=gateway, + known_hosts_file=known_hosts_file, + ): - log.debug('Logging into %s:%s as %s', host, port, username) + log.debug("Logging into %s:%s as %s", host, port, username) newtimeout = timeout - (time.mktime(time.localtime()) - starttime) ssh_kwargs = { - 'hostname': host, - 'port': port, - 'username': username, - 'timeout': ssh_timeout, - 'display_ssh_output': display_ssh_output, - 'sudo_password': sudo_password, - 'sftp': opts.get('use_sftp', False) + "hostname": host, + "port": port, + "username": username, + "timeout": ssh_timeout, + "display_ssh_output": display_ssh_output, + "sudo_password": sudo_password, + "sftp": opts.get("use_sftp", False), } ssh_kwargs.update(__ssh_gateway_config_dict(gateway)) if key_filename: - log.debug('Using %s as the key_filename', key_filename) - ssh_kwargs['key_filename'] = key_filename - elif password and 'has_ssh_agent' in kwargs and kwargs['has_ssh_agent'] is False: - ssh_kwargs['password'] = password + log.debug("Using %s as the key_filename", key_filename) + ssh_kwargs["key_filename"] = key_filename + elif ( + password + and "has_ssh_agent" in kwargs + and kwargs["has_ssh_agent"] is False + ): + ssh_kwargs["password"] = password # TODO: write some tests ??? # TODO: check edge cases (e.g. ssh gateways, salt deploy disabled, etc.) - if root_cmd('test -e \\"{0}\\"'.format(tmp_dir), tty, sudo, - allow_failure=True, **ssh_kwargs) and inline_script: - log.debug('Found inline script to execute.') + if ( + root_cmd( + 'test -e \\"{0}\\"'.format(tmp_dir), + tty, + sudo, + allow_failure=True, + **ssh_kwargs + ) + and inline_script + ): + log.debug("Found inline script to execute.") for cmd_line in inline_script: - log.info('Executing inline command: %s', cmd_line) - ret = root_cmd('sh -c "( {0} )"'.format(cmd_line), - tty, sudo, allow_failure=True, **ssh_kwargs) + log.info("Executing inline command: %s", cmd_line) + ret = root_cmd( + 'sh -c "( {0} )"'.format(cmd_line), + tty, + sudo, + allow_failure=True, + **ssh_kwargs + ) if ret: - log.info('[%s] Output: %s', cmd_line, ret) + log.info("[%s] Output: %s", cmd_line, ret) # TODO: ensure we send the correct return value return True def filter_event(tag, data, defaults): - ''' + """ Accept a tag, a dict and a list of default keys to return from the dict, and check them against the cloud configuration for that tag - ''' + """ ret = {} keys = [] use_defaults = True - for ktag in __opts__.get('filter_events', {}): + for ktag in __opts__.get("filter_events", {}): if tag != ktag: continue - keys = __opts__['filter_events'][ktag]['keys'] - use_defaults = __opts__['filter_events'][ktag].get('use_defaults', True) + keys = __opts__["filter_events"][ktag]["keys"] + use_defaults = __opts__["filter_events"][ktag].get("use_defaults", True) if use_defaults is False: defaults = [] @@ -1977,11 +2084,13 @@ def filter_event(tag, data, defaults): return ret -def fire_event(key, msg, tag, sock_dir, args=None, transport='zeromq'): - ''' +def fire_event(key, msg, tag, sock_dir, args=None, transport="zeromq"): + """ Fire deploy action - ''' - with salt.utils.event.get_event('master', sock_dir, transport, listen=False) as event: + """ + with salt.utils.event.get_event( + "master", sock_dir, transport, listen=False + ) as event: try: event.fire_event(msg, tag) except ValueError: @@ -1999,8 +2108,8 @@ def fire_event(key, msg, tag, sock_dir, args=None, transport='zeromq'): def _exec_ssh_cmd(cmd, error_msg=None, allow_failure=False, **kwargs): if error_msg is None: - error_msg = 'A wrong password has been issued while establishing ssh session.' - password_retries = kwargs.get('password_retries', 3) + error_msg = "A wrong password has been issued while establishing ssh session." + password_retries = kwargs.get("password_retries", 3) try: stdout, stderr = None, None proc = salt.utils.vt.Terminal( @@ -2008,8 +2117,8 @@ def _exec_ssh_cmd(cmd, error_msg=None, allow_failure=False, **kwargs): shell=True, log_stdout=True, log_stderr=True, - stream_stdout=kwargs.get('display_ssh_output', True), - stream_stderr=kwargs.get('display_ssh_output', True) + stream_stdout=kwargs.get("display_ssh_output", True), + stream_stderr=kwargs.get("display_ssh_output", True), ) sent_password = 0 while proc.has_unread_data: @@ -2017,18 +2126,19 @@ def _exec_ssh_cmd(cmd, error_msg=None, allow_failure=False, **kwargs): if stdout and SSH_PASSWORD_PROMP_RE.search(stdout): # if authenticating with an SSH key and 'sudo' is found # in the password prompt - if ('key_filename' in kwargs and kwargs['key_filename'] + if ( + "key_filename" in kwargs + and kwargs["key_filename"] and SSH_PASSWORD_PROMP_SUDO_RE.search(stdout) ): - proc.sendline(kwargs['sudo_password']) + proc.sendline(kwargs["sudo_password"]) # elif authenticating via password and haven't exhausted our # password_retires - elif ( - kwargs.get('password', None) - and (sent_password < password_retries) + elif kwargs.get("password", None) and ( + sent_password < password_retries ): sent_password += 1 - proc.sendline(kwargs['password']) + proc.sendline(kwargs["password"]) # else raise an error as we are not authenticating properly # * not authenticating with an SSH key # * not authenticating with a Password @@ -2038,14 +2148,14 @@ def _exec_ssh_cmd(cmd, error_msg=None, allow_failure=False, **kwargs): time.sleep(0.5) if proc.exitstatus != 0 and allow_failure is False: raise SaltCloudSystemExit( - 'Command \'{0}\' failed. Exit code: {1}'.format( - cmd, proc.exitstatus - ) + "Command '{0}' failed. Exit code: {1}".format(cmd, proc.exitstatus) ) return proc.exitstatus except salt.utils.vt.TerminalException as err: trace = traceback.format_exc() - log.error(error_msg.format(cmd, err, trace)) # pylint: disable=str-format-in-logging + log.error( + error_msg.format(cmd, err, trace) + ) # pylint: disable=str-format-in-logging finally: proc.close(terminate=True, kill=True) # Signal an error @@ -2053,9 +2163,9 @@ def _exec_ssh_cmd(cmd, error_msg=None, allow_failure=False, **kwargs): def scp_file(dest_path, contents=None, kwargs=None, local_file=None): - ''' + """ Use scp or sftp to copy a file to a server - ''' + """ file_to_upload = None try: if contents is not None: @@ -2069,69 +2179,73 @@ def scp_file(dest_path, contents=None, kwargs=None, local_file=None): if exc.errno != errno.EBADF: six.reraise(*sys.exc_info()) - log.debug('Uploading %s to %s', dest_path, kwargs['hostname']) + log.debug("Uploading %s to %s", dest_path, kwargs["hostname"]) ssh_args = [ # Don't add new hosts to the host key database - '-oStrictHostKeyChecking=no', + "-oStrictHostKeyChecking=no", # Set hosts key database path to /dev/null, i.e., non-existing - '-oUserKnownHostsFile=/dev/null', + "-oUserKnownHostsFile=/dev/null", # Don't re-use the SSH connection. Less failures. - '-oControlPath=none' + "-oControlPath=none", ] if local_file is not None: file_to_upload = local_file if os.path.isdir(local_file): - ssh_args.append('-r') + ssh_args.append("-r") - if 'key_filename' in kwargs: + if "key_filename" in kwargs: # There should never be both a password and an ssh key passed in, so - ssh_args.extend([ - # tell SSH to skip password authentication - '-oPasswordAuthentication=no', - '-oChallengeResponseAuthentication=no', - # Make sure public key authentication is enabled - '-oPubkeyAuthentication=yes', - # do only use the provided identity file - '-oIdentitiesOnly=yes', - # No Keyboard interaction! - '-oKbdInteractiveAuthentication=no', - # Also, specify the location of the key file - '-i {0}'.format(kwargs['key_filename']) - ]) + ssh_args.extend( + [ + # tell SSH to skip password authentication + "-oPasswordAuthentication=no", + "-oChallengeResponseAuthentication=no", + # Make sure public key authentication is enabled + "-oPubkeyAuthentication=yes", + # do only use the provided identity file + "-oIdentitiesOnly=yes", + # No Keyboard interaction! + "-oKbdInteractiveAuthentication=no", + # Also, specify the location of the key file + "-i {0}".format(kwargs["key_filename"]), + ] + ) - if 'port' in kwargs: - ssh_args.append('-oPort={0}'.format(kwargs['port'])) + if "port" in kwargs: + ssh_args.append("-oPort={0}".format(kwargs["port"])) ssh_args.append(__ssh_gateway_arguments(kwargs)) try: - if socket.inet_pton(socket.AF_INET6, kwargs['hostname']): - ipaddr = '[{0}]'.format(kwargs['hostname']) + if socket.inet_pton(socket.AF_INET6, kwargs["hostname"]): + ipaddr = "[{0}]".format(kwargs["hostname"]) else: - ipaddr = kwargs['hostname'] + ipaddr = kwargs["hostname"] except socket.error: - ipaddr = kwargs['hostname'] + ipaddr = kwargs["hostname"] if file_to_upload is None: log.warning( - 'No source file to upload. Please make sure that either file ' - 'contents or the path to a local file are provided.' + "No source file to upload. Please make sure that either file " + "contents or the path to a local file are provided." ) cmd = ( - 'scp {0} {1} {2[username]}@{4}:{3} || ' + "scp {0} {1} {2[username]}@{4}:{3} || " 'echo "put {1} {3}" | sftp {0} {2[username]}@{4} || ' 'rsync -avz -e "ssh {0}" {1} {2[username]}@{2[hostname]}:{3}'.format( - ' '.join(ssh_args), file_to_upload, kwargs, dest_path, ipaddr + " ".join(ssh_args), file_to_upload, kwargs, dest_path, ipaddr ) ) - log.debug('SCP command: \'%s\'', cmd) - retcode = _exec_ssh_cmd(cmd, - error_msg='Failed to upload file \'{0}\': {1}\n{2}', - password_retries=3, - **kwargs) + log.debug("SCP command: '%s'", cmd) + retcode = _exec_ssh_cmd( + cmd, + error_msg="Failed to upload file '{0}': {1}\n{2}", + password_retries=3, + **kwargs + ) finally: if contents is not None: try: @@ -2143,19 +2257,19 @@ def scp_file(dest_path, contents=None, kwargs=None, local_file=None): def ssh_file(opts, dest_path, contents=None, kwargs=None, local_file=None): - ''' + """ Copies a file to the remote SSH target using either sftp or scp, as configured. - ''' - if opts.get('file_transport', 'sftp') == 'sftp': + """ + if opts.get("file_transport", "sftp") == "sftp": return sftp_file(dest_path, contents, kwargs, local_file) return scp_file(dest_path, contents, kwargs, local_file) def sftp_file(dest_path, contents=None, kwargs=None, local_file=None): - ''' + """ Use sftp to upload a file to a server - ''' + """ put_args = [] if kwargs is None: @@ -2180,60 +2294,69 @@ def sftp_file(dest_path, contents=None, kwargs=None, local_file=None): if local_file is not None: file_to_upload = local_file if os.path.isdir(local_file): - put_args = ['-r'] + put_args = ["-r"] - log.debug('Uploading %s to %s (sftp)', dest_path, kwargs.get('hostname')) + log.debug("Uploading %s to %s (sftp)", dest_path, kwargs.get("hostname")) ssh_args = [ # Don't add new hosts to the host key database - '-oStrictHostKeyChecking=no', + "-oStrictHostKeyChecking=no", # Set hosts key database path to /dev/null, i.e., non-existing - '-oUserKnownHostsFile=/dev/null', + "-oUserKnownHostsFile=/dev/null", # Don't re-use the SSH connection. Less failures. - '-oControlPath=none' + "-oControlPath=none", ] - if 'key_filename' in kwargs: + if "key_filename" in kwargs: # There should never be both a password and an ssh key passed in, so - ssh_args.extend([ - # tell SSH to skip password authentication - '-oPasswordAuthentication=no', - '-oChallengeResponseAuthentication=no', - # Make sure public key authentication is enabled - '-oPubkeyAuthentication=yes', - # do only use the provided identity file - '-oIdentitiesOnly=yes', - # No Keyboard interaction! - '-oKbdInteractiveAuthentication=no', - # Also, specify the location of the key file - '-oIdentityFile={0}'.format(kwargs['key_filename']) - ]) + ssh_args.extend( + [ + # tell SSH to skip password authentication + "-oPasswordAuthentication=no", + "-oChallengeResponseAuthentication=no", + # Make sure public key authentication is enabled + "-oPubkeyAuthentication=yes", + # do only use the provided identity file + "-oIdentitiesOnly=yes", + # No Keyboard interaction! + "-oKbdInteractiveAuthentication=no", + # Also, specify the location of the key file + "-oIdentityFile={0}".format(kwargs["key_filename"]), + ] + ) - if 'port' in kwargs: - ssh_args.append('-oPort={0}'.format(kwargs['port'])) + if "port" in kwargs: + ssh_args.append("-oPort={0}".format(kwargs["port"])) ssh_args.append(__ssh_gateway_arguments(kwargs)) try: - if socket.inet_pton(socket.AF_INET6, kwargs['hostname']): - ipaddr = '[{0}]'.format(kwargs['hostname']) + if socket.inet_pton(socket.AF_INET6, kwargs["hostname"]): + ipaddr = "[{0}]".format(kwargs["hostname"]) else: - ipaddr = kwargs['hostname'] + ipaddr = kwargs["hostname"] except socket.error: - ipaddr = kwargs['hostname'] + ipaddr = kwargs["hostname"] if file_to_upload is None: log.warning( - 'No source file to upload. Please make sure that either file ' - 'contents or the path to a local file are provided.' + "No source file to upload. Please make sure that either file " + "contents or the path to a local file are provided." ) cmd = 'echo "put {0} {1} {2}" | sftp {3} {4[username]}@{5}'.format( - ' '.join(put_args), file_to_upload, dest_path, ' '.join(ssh_args), kwargs, ipaddr + " ".join(put_args), + file_to_upload, + dest_path, + " ".join(ssh_args), + kwargs, + ipaddr, + ) + log.debug("SFTP command: '%s'", cmd) + retcode = _exec_ssh_cmd( + cmd, + error_msg="Failed to upload file '{0}': {1}\n{2}", + password_retries=3, + **kwargs ) - log.debug('SFTP command: \'%s\'', cmd) - retcode = _exec_ssh_cmd(cmd, - error_msg='Failed to upload file \'{0}\': {1}\n{2}', - password_retries=3, - **kwargs) finally: if contents is not None: try: @@ -2245,10 +2368,10 @@ def sftp_file(dest_path, contents=None, kwargs=None, local_file=None): def win_cmd(command, **kwargs): - ''' + """ Wrapper for commands to be run against Windows boxes - ''' - logging_command = kwargs.get('logging_command', None) + """ + logging_command = kwargs.get("logging_command", None) try: proc = NonBlockingPopen( @@ -2256,155 +2379,150 @@ def win_cmd(command, **kwargs): shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE, - stream_stds=kwargs.get('display_ssh_output', True), + stream_stds=kwargs.get("display_ssh_output", True), logging_command=logging_command, ) if logging_command is None: - log.debug( - 'Executing command(PID %s): \'%s\'', - proc.pid, command - ) + log.debug("Executing command(PID %s): '%s'", proc.pid, command) else: - log.debug( - 'Executing command(PID %s): \'%s\'', - proc.pid, logging_command - ) + log.debug("Executing command(PID %s): '%s'", proc.pid, logging_command) proc.poll_and_read_until_finish() proc.communicate() return proc.returncode except Exception as err: # pylint: disable=broad-except - log.exception('Failed to execute command \'%s\'', logging_command) + log.exception("Failed to execute command '%s'", logging_command) # Signal an error return 1 def winrm_cmd(session, command, flags, **kwargs): - ''' + """ Wrapper for commands to be run against Windows boxes using WinRM. - ''' - log.debug('Executing WinRM command: %s %s', command, flags) + """ + log.debug("Executing WinRM command: %s %s", command, flags) r = session.run_cmd(command, flags) return r.status_code def root_cmd(command, tty, sudo, allow_failure=False, **kwargs): - ''' + """ Wrapper for commands to be run as root - ''' + """ logging_command = command - sudo_password = kwargs.get('sudo_password', None) + sudo_password = kwargs.get("sudo_password", None) if sudo: if sudo_password is None: - command = 'sudo {0}'.format(command) + command = "sudo {0}".format(command) logging_command = command else: logging_command = 'sudo -S "XXX-REDACTED-XXX" {0}'.format(command) - command = 'sudo -S {0}'.format(command) + command = "sudo -S {0}".format(command) - log.debug('Using sudo to run command %s', logging_command) + log.debug("Using sudo to run command %s", logging_command) ssh_args = [] if tty: # Use double `-t` on the `ssh` command, it's necessary when `sudo` has # `requiretty` enforced. - ssh_args.extend(['-t', '-t']) + ssh_args.extend(["-t", "-t"]) - known_hosts_file = kwargs.get('known_hosts_file', '/dev/null') - host_key_checking = 'no' - if known_hosts_file != '/dev/null': - host_key_checking = 'yes' + known_hosts_file = kwargs.get("known_hosts_file", "/dev/null") + host_key_checking = "no" + if known_hosts_file != "/dev/null": + host_key_checking = "yes" - ssh_args.extend([ - # Don't add new hosts to the host key database - '-oStrictHostKeyChecking={0}'.format(host_key_checking), - # Set hosts key database path to /dev/null, i.e., non-existing - '-oUserKnownHostsFile={0}'.format(known_hosts_file), - # Don't re-use the SSH connection. Less failures. - '-oControlPath=none' - ]) + ssh_args.extend( + [ + # Don't add new hosts to the host key database + "-oStrictHostKeyChecking={0}".format(host_key_checking), + # Set hosts key database path to /dev/null, i.e., non-existing + "-oUserKnownHostsFile={0}".format(known_hosts_file), + # Don't re-use the SSH connection. Less failures. + "-oControlPath=none", + ] + ) - if 'key_filename' in kwargs: + if "key_filename" in kwargs: # There should never be both a password and an ssh key passed in, so - ssh_args.extend([ - # tell SSH to skip password authentication - '-oPasswordAuthentication=no', - '-oChallengeResponseAuthentication=no', - # Make sure public key authentication is enabled - '-oPubkeyAuthentication=yes', - # do only use the provided identity file - '-oIdentitiesOnly=yes', - # No Keyboard interaction! - '-oKbdInteractiveAuthentication=no', - # Also, specify the location of the key file - '-i {0}'.format(kwargs['key_filename']) - ]) - if 'ssh_timeout' in kwargs: - ssh_args.extend(['-oConnectTimeout={0}'.format(kwargs['ssh_timeout'])]) + ssh_args.extend( + [ + # tell SSH to skip password authentication + "-oPasswordAuthentication=no", + "-oChallengeResponseAuthentication=no", + # Make sure public key authentication is enabled + "-oPubkeyAuthentication=yes", + # do only use the provided identity file + "-oIdentitiesOnly=yes", + # No Keyboard interaction! + "-oKbdInteractiveAuthentication=no", + # Also, specify the location of the key file + "-i {0}".format(kwargs["key_filename"]), + ] + ) + if "ssh_timeout" in kwargs: + ssh_args.extend(["-oConnectTimeout={0}".format(kwargs["ssh_timeout"])]) ssh_args.extend([__ssh_gateway_arguments(kwargs)]) - if 'port' in kwargs: - ssh_args.extend(['-p {0}'.format(kwargs['port'])]) + if "port" in kwargs: + ssh_args.extend(["-p {0}".format(kwargs["port"])]) - cmd = 'ssh {0} {1[username]}@{1[hostname]} '.format( - ' '.join(ssh_args), - kwargs - ) + cmd = "ssh {0} {1[username]}@{1[hostname]} ".format(" ".join(ssh_args), kwargs) logging_command = cmd + logging_command cmd = cmd + pipes.quote(command) - hard_timeout = kwargs.get('hard_timeout') + hard_timeout = kwargs.get("hard_timeout") if hard_timeout is not None: - logging_command = 'timeout {0} {1}'.format(hard_timeout, logging_command) - cmd = 'timeout {0} {1}'.format(hard_timeout, cmd) + logging_command = "timeout {0} {1}".format(hard_timeout, logging_command) + cmd = "timeout {0} {1}".format(hard_timeout, cmd) - log.debug('SSH command: \'%s\'', logging_command) + log.debug("SSH command: '%s'", logging_command) retcode = _exec_ssh_cmd(cmd, allow_failure=allow_failure, **kwargs) return retcode def check_auth(name, sock_dir=None, queue=None, timeout=300): - ''' + """ This function is called from a multiprocess instance, to wait for a minion to become available to receive salt commands - ''' - with salt.utils.event.SaltEvent('master', sock_dir, listen=True) as event: + """ + with salt.utils.event.SaltEvent("master", sock_dir, listen=True) as event: starttime = time.mktime(time.localtime()) newtimeout = timeout - log.debug('In check_auth, waiting for %s to become available', name) + log.debug("In check_auth, waiting for %s to become available", name) while newtimeout > 0: newtimeout = timeout - (time.mktime(time.localtime()) - starttime) ret = event.get_event(full=True) if ret is None: continue - if ret['tag'] == 'salt/minion/{0}/start'.format(name): + if ret["tag"] == "salt/minion/{0}/start".format(name): queue.put(name) newtimeout = 0 - log.debug('Minion %s is ready to receive commands', name) + log.debug("Minion %s is ready to receive commands", name) def ip_to_int(ip): - ''' + """ Converts an IP address to an integer - ''' + """ ret = 0 - for octet in ip.split('.'): + for octet in ip.split("."): ret = ret * 256 + int(octet) return ret def is_public_ip(ip): - ''' + """ Determines whether an IP address falls within one of the private IP ranges - ''' - if ':' in ip: + """ + if ":" in ip: # ipv6 - if ip.startswith('fe80:'): + if ip.startswith("fe80:"): # ipv6 link local return False return True @@ -2425,54 +2543,51 @@ def is_public_ip(ip): def check_name(name, safe_chars): - ''' + """ Check whether the specified name contains invalid characters - ''' - regexp = re.compile('[^{0}]'.format(safe_chars)) + """ + regexp = re.compile("[^{0}]".format(safe_chars)) if regexp.search(name): raise SaltCloudException( - '{0} contains characters not supported by this cloud provider. ' - 'Valid characters are: {1}'.format( - name, safe_chars - ) + "{0} contains characters not supported by this cloud provider. " + "Valid characters are: {1}".format(name, safe_chars) ) def remove_sshkey(host, known_hosts=None): - ''' + """ Remove a host from the known_hosts file - ''' + """ if known_hosts is None: - if 'HOME' in os.environ: - known_hosts = '{0}/.ssh/known_hosts'.format(os.environ['HOME']) + if "HOME" in os.environ: + known_hosts = "{0}/.ssh/known_hosts".format(os.environ["HOME"]) else: try: - known_hosts = '{0}/.ssh/known_hosts'.format( + known_hosts = "{0}/.ssh/known_hosts".format( pwd.getpwuid(os.getuid()).pwd_dir ) except Exception: # pylint: disable=broad-except pass if known_hosts is not None: - log.debug( - 'Removing ssh key for %s from known hosts file %s', - host, known_hosts - ) + log.debug("Removing ssh key for %s from known hosts file %s", host, known_hosts) else: - log.debug('Removing ssh key for %s from known hosts file', host) + log.debug("Removing ssh key for %s from known hosts file", host) - cmd = 'ssh-keygen -R {0}'.format(host) + cmd = "ssh-keygen -R {0}".format(host) subprocess.call(cmd, shell=True) -def wait_for_ip(update_callback, - update_args=None, - update_kwargs=None, - timeout=5 * 60, - interval=5, - interval_multiplier=1, - max_failures=10): - ''' +def wait_for_ip( + update_callback, + update_args=None, + update_kwargs=None, + timeout=5 * 60, + interval=5, + interval_multiplier=1, + max_failures=10, +): + """ Helper function that waits for an IP address for a specific maximum amount of time. @@ -2493,7 +2608,7 @@ def wait_for_ip(update_callback, :returns: The update_callback returned data :raises: SaltCloudExecutionTimeout - ''' + """ if update_args is None: update_args = () if update_kwargs is None: @@ -2502,29 +2617,29 @@ def wait_for_ip(update_callback, duration = timeout while True: log.debug( - 'Waiting for VM IP. Giving up in 00:%02d:%02d.', - int(timeout // 60), int(timeout % 60) + "Waiting for VM IP. Giving up in 00:%02d:%02d.", + int(timeout // 60), + int(timeout % 60), ) data = update_callback(*update_args, **update_kwargs) if data is False: log.debug( - '\'update_callback\' has returned \'False\', which is ' - 'considered a failure. Remaining Failures: %s.', max_failures + "'update_callback' has returned 'False', which is " + "considered a failure. Remaining Failures: %s.", + max_failures, ) max_failures -= 1 if max_failures <= 0: raise SaltCloudExecutionFailure( - 'Too many failures occurred while waiting for ' - 'the IP address.' + "Too many failures occurred while waiting for " "the IP address." ) elif data is not None: return data if timeout < 0: raise SaltCloudExecutionTimeout( - 'Unable to get IP for 00:{0:02d}:{1:02d}.'.format( - int(duration // 60), - int(duration % 60) + "Unable to get IP for 00:{0:02d}:{1:02d}.".format( + int(duration // 60), int(duration % 60) ) ) time.sleep(interval) @@ -2534,24 +2649,22 @@ def wait_for_ip(update_callback, interval *= interval_multiplier if interval > timeout: interval = timeout + 1 - log.info('Interval multiplier in effect; interval is ' - 'now %ss.', interval) + log.info("Interval multiplier in effect; interval is " "now %ss.", interval) def list_nodes_select(nodes, selection, call=None): - ''' + """ Return a list of the VMs that are on the provider, with select fields - ''' - if call == 'action': + """ + if call == "action": raise SaltCloudSystemExit( - 'The list_nodes_select function must be called ' - 'with -f or --function.' + "The list_nodes_select function must be called " "with -f or --function." ) - if 'error' in nodes: + if "error" in nodes: raise SaltCloudSystemExit( - 'An error occurred while listing nodes: {0}'.format( - nodes['error']['Errors']['Error']['Message'] + "An error occurred while listing nodes: {0}".format( + nodes["error"]["Errors"]["Error"]["Message"] ) ) @@ -2568,47 +2681,47 @@ def list_nodes_select(nodes, selection, call=None): return ret -def lock_file(filename, interval=.5, timeout=15): - ''' +def lock_file(filename, interval=0.5, timeout=15): + """ Lock a file; if it is already locked, then wait for it to become available before locking it. Note that these locks are only recognized by Salt Cloud, and not other programs or platforms. - ''' - log.trace('Attempting to obtain lock for %s', filename) - lock = filename + '.lock' + """ + log.trace("Attempting to obtain lock for %s", filename) + lock = filename + ".lock" start = time.time() while True: if os.path.exists(lock): if time.time() - start >= timeout: - log.warning('Unable to obtain lock for %s', filename) + log.warning("Unable to obtain lock for %s", filename) return False time.sleep(interval) else: break - with salt.utils.files.fopen(lock, 'a'): + with salt.utils.files.fopen(lock, "a"): pass def unlock_file(filename): - ''' + """ Unlock a locked file Note that these locks are only recognized by Salt Cloud, and not other programs or platforms. - ''' - log.trace('Removing lock for %s', filename) - lock = filename + '.lock' + """ + log.trace("Removing lock for %s", filename) + lock = filename + ".lock" try: os.remove(lock) except OSError as exc: - log.trace('Unable to remove lock for %s: %s', filename, exc) + log.trace("Unable to remove lock for %s: %s", filename, exc) def cachedir_index_add(minion_id, profile, driver, provider, base=None): - ''' + """ Add an entry to the cachedir index. This generally only needs to happen when a new instance is created. This entry should contain: @@ -2621,30 +2734,34 @@ def cachedir_index_add(minion_id, profile, driver, provider, base=None): The intent of this function is to speed up lookups for the cloud roster for salt-ssh. However, other code that makes use of profile information can also make use of this function. - ''' + """ base = init_cachedir(base) - index_file = os.path.join(base, 'index.p') + index_file = os.path.join(base, "index.p") lock_file(index_file) if os.path.exists(index_file): - mode = 'rb' if six.PY3 else 'r' + mode = "rb" if six.PY3 else "r" with salt.utils.files.fopen(index_file, mode) as fh_: - index = salt.utils.data.decode(salt.utils.msgpack.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + index = salt.utils.data.decode( + salt.utils.msgpack.msgpack.load(fh_, encoding=MSGPACK_ENCODING) + ) else: index = {} - prov_comps = provider.split(':') + prov_comps = provider.split(":") - index.update({ - minion_id: { - 'id': minion_id, - 'profile': profile, - 'driver': driver, - 'provider': prov_comps[0], + index.update( + { + minion_id: { + "id": minion_id, + "profile": profile, + "driver": driver, + "provider": prov_comps[0], + } } - }) + ) - mode = 'wb' if six.PY3 else 'w' + mode = "wb" if six.PY3 else "w" with salt.utils.files.fopen(index_file, mode) as fh_: salt.utils.msgpack.dump(index, fh_, encoding=MSGPACK_ENCODING) @@ -2652,25 +2769,27 @@ def cachedir_index_add(minion_id, profile, driver, provider, base=None): def cachedir_index_del(minion_id, base=None): - ''' + """ Delete an entry from the cachedir index. This generally only needs to happen when an instance is deleted. - ''' + """ base = init_cachedir(base) - index_file = os.path.join(base, 'index.p') + index_file = os.path.join(base, "index.p") lock_file(index_file) if os.path.exists(index_file): - mode = 'rb' if six.PY3 else 'r' + mode = "rb" if six.PY3 else "r" with salt.utils.files.fopen(index_file, mode) as fh_: - index = salt.utils.data.decode(salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + index = salt.utils.data.decode( + salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING) + ) else: return if minion_id in index: del index[minion_id] - mode = 'wb' if six.PY3 else 'w' + mode = "wb" if six.PY3 else "w" with salt.utils.files.fopen(index_file, mode) as fh_: salt.utils.msgpack.dump(index, fh_, encoding=MSGPACK_ENCODING) @@ -2678,14 +2797,12 @@ def cachedir_index_del(minion_id, base=None): def init_cachedir(base=None): - ''' + """ Initialize the cachedir needed for Salt Cloud to keep track of minions - ''' + """ if base is None: - base = __opts__['cachedir'] - needed_dirs = (base, - os.path.join(base, 'requested'), - os.path.join(base, 'active')) + base = __opts__["cachedir"] + needed_dirs = (base, os.path.join(base, "requested"), os.path.join(base, "active")) for dir_ in needed_dirs: if not os.path.exists(dir_): os.makedirs(dir_) @@ -2696,14 +2813,9 @@ def init_cachedir(base=None): # FIXME: This function seems used nowhere. Dead code? def request_minion_cachedir( - minion_id, - opts=None, - fingerprint='', - pubkey=None, - provider=None, - base=None, + minion_id, opts=None, fingerprint="", pubkey=None, provider=None, base=None, ): - ''' + """ Creates an entry in the requested/ cachedir. This means that Salt Cloud has made a request to a cloud provider to create an instance, but it has not yet verified that the instance properly exists. @@ -2711,35 +2823,34 @@ def request_minion_cachedir( If the fingerprint is unknown, a raw pubkey can be passed in, and a fingerprint will be calculated. If both are empty, then the fingerprint will be set to None. - ''' + """ if base is None: - base = __opts__['cachedir'] + base = __opts__["cachedir"] if not fingerprint and pubkey is not None: - fingerprint = salt.utils.crypt.pem_finger(key=pubkey, sum_type=(opts and opts.get('hash_type') or 'sha256')) + fingerprint = salt.utils.crypt.pem_finger( + key=pubkey, sum_type=(opts and opts.get("hash_type") or "sha256") + ) init_cachedir(base) data = { - 'minion_id': minion_id, - 'fingerprint': fingerprint, - 'provider': provider, + "minion_id": minion_id, + "fingerprint": fingerprint, + "provider": provider, } - fname = '{0}.p'.format(minion_id) - path = os.path.join(base, 'requested', fname) - mode = 'wb' if six.PY3 else 'w' + fname = "{0}.p".format(minion_id) + path = os.path.join(base, "requested", fname) + mode = "wb" if six.PY3 else "w" with salt.utils.files.fopen(path, mode) as fh_: salt.utils.msgpack.dump(data, fh_, encoding=MSGPACK_ENCODING) def change_minion_cachedir( - minion_id, - cachedir, - data=None, - base=None, + minion_id, cachedir, data=None, base=None, ): - ''' + """ Changes the info inside a minion's cachedir entry. The type of cachedir must be specified (i.e., 'requested' or 'active'). A dict is also passed in which contains the data to be changed. @@ -2751,77 +2862,78 @@ def change_minion_cachedir( 'requested', {'fingerprint': '26:5c:8c:de:be:fe:89:c0:02:ed:27:65:0e:bb:be:60'}, ) - ''' + """ if not isinstance(data, dict): return False if base is None: - base = __opts__['cachedir'] + base = __opts__["cachedir"] - fname = '{0}.p'.format(minion_id) + fname = "{0}.p".format(minion_id) path = os.path.join(base, cachedir, fname) - with salt.utils.files.fopen(path, 'r') as fh_: + with salt.utils.files.fopen(path, "r") as fh_: cache_data = salt.utils.data.decode( - salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING) + ) cache_data.update(data) - with salt.utils.files.fopen(path, 'w') as fh_: + with salt.utils.files.fopen(path, "w") as fh_: salt.utils.msgpack.dump(cache_data, fh_, encoding=MSGPACK_ENCODING) def activate_minion_cachedir(minion_id, base=None): - ''' + """ Moves a minion from the requested/ cachedir into the active/ cachedir. This means that Salt Cloud has verified that a requested instance properly exists, and should be expected to exist from here on out. - ''' + """ if base is None: - base = __opts__['cachedir'] + base = __opts__["cachedir"] - fname = '{0}.p'.format(minion_id) - src = os.path.join(base, 'requested', fname) - dst = os.path.join(base, 'active') + fname = "{0}.p".format(minion_id) + src = os.path.join(base, "requested", fname) + dst = os.path.join(base, "active") shutil.move(src, dst) def delete_minion_cachedir(minion_id, provider, opts, base=None): - ''' + """ Deletes a minion's entry from the cloud cachedir. It will search through all cachedirs to find the minion's cache file. Needs `update_cachedir` set to True. - ''' + """ if isinstance(opts, dict): __opts__.update(opts) - if __opts__.get('update_cachedir', False) is False: + if __opts__.get("update_cachedir", False) is False: return if base is None: - base = __opts__['cachedir'] + base = __opts__["cachedir"] - driver = next(six.iterkeys(__opts__['providers'][provider])) - fname = '{0}.p'.format(minion_id) - for cachedir in 'requested', 'active': + driver = next(six.iterkeys(__opts__["providers"][provider])) + fname = "{0}.p".format(minion_id) + for cachedir in "requested", "active": path = os.path.join(base, cachedir, driver, provider, fname) - log.debug('path: %s', path) + log.debug("path: %s", path) if os.path.exists(path): os.remove(path) def list_cache_nodes_full(opts=None, provider=None, base=None): - ''' + """ Return a list of minion data from the cloud cache, rather from the cloud providers themselves. This is the cloud cache version of list_nodes_full(). - ''' + """ if opts is None: opts = __opts__ - if opts.get('update_cachedir', False) is False: + if opts.get("update_cachedir", False) is False: return if base is None: - base = os.path.join(opts['cachedir'], 'active') + base = os.path.join(opts["cachedir"], "active") minions = {} # First, get a list of all drivers in use @@ -2840,16 +2952,17 @@ def list_cache_nodes_full(opts=None, provider=None, base=None): # Finally, get a list of full minion data fpath = os.path.join(min_dir, fname) minion_id = fname[:-2] # strip '.p' from end of msgpack filename - mode = 'rb' if six.PY3 else 'r' + mode = "rb" if six.PY3 else "r" with salt.utils.files.fopen(fpath, mode) as fh_: minions[driver][prov][minion_id] = salt.utils.data.decode( - salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING) + ) return minions def update_bootstrap(config, url=None): - ''' + """ Update the salt-bootstrap script url can be one of: @@ -2857,34 +2970,35 @@ def update_bootstrap(config, url=None): - The URL to fetch the bootstrap script from - The absolute path to the bootstrap - The content of the bootstrap script - ''' - default_url = config.get('bootstrap_script_url', - 'https://bootstrap.saltstack.com') + """ + default_url = config.get("bootstrap_script_url", "https://bootstrap.saltstack.com") if not url: url = default_url if not url: - raise ValueError('Cant get any source to update') - if url.startswith('http') or '://' in url: - log.debug('Updating the bootstrap-salt.sh script to latest stable') + raise ValueError("Cant get any source to update") + if url.startswith("http") or "://" in url: + log.debug("Updating the bootstrap-salt.sh script to latest stable") try: import requests except ImportError: - return {'error': ( - 'Updating the bootstrap-salt.sh script requires the ' - 'Python requests library to be installed' - )} + return { + "error": ( + "Updating the bootstrap-salt.sh script requires the " + "Python requests library to be installed" + ) + } req = requests.get(url) if req.status_code != 200: - return {'error': ( - 'Failed to download the latest stable version of the ' - 'bootstrap-salt.sh script from {0}. HTTP error: ' - '{1}'.format( - url, req.status_code + return { + "error": ( + "Failed to download the latest stable version of the " + "bootstrap-salt.sh script from {0}. HTTP error: " + "{1}".format(url, req.status_code) ) - )} + } script_content = req.text if url == default_url: - script_name = 'bootstrap-salt.sh' + script_name = "bootstrap-salt.sh" else: script_name = os.path.basename(url) elif os.path.exists(url): @@ -2894,37 +3008,28 @@ def update_bootstrap(config, url=None): # in last case, assuming we got a script content else: script_content = url - script_name = '{0}.sh'.format( - hashlib.sha1(script_content).hexdigest() - ) + script_name = "{0}.sh".format(hashlib.sha1(script_content).hexdigest()) if not script_content: - raise ValueError('No content in bootstrap script !') + raise ValueError("No content in bootstrap script !") # Get the path to the built-in deploy scripts directory - builtin_deploy_dir = os.path.join( - os.path.dirname(__file__), - 'deploy' - ) + builtin_deploy_dir = os.path.join(os.path.dirname(__file__), "deploy") # Compute the search path from the current loaded opts conf_file # value deploy_d_from_conf_file = os.path.join( - os.path.dirname(config['conf_file']), - 'cloud.deploy.d' + os.path.dirname(config["conf_file"]), "cloud.deploy.d" ) # Compute the search path using the install time defined # syspaths.CONF_DIR - deploy_d_from_syspaths = os.path.join( - config['config_dir'], - 'cloud.deploy.d' - ) + deploy_d_from_syspaths = os.path.join(config["config_dir"], "cloud.deploy.d") # Get a copy of any defined search paths, flagging them not to # create parent deploy_scripts_search_paths = [] - for entry in config.get('deploy_scripts_search_path', []): + for entry in config.get("deploy_scripts_search_path", []): if entry.startswith(builtin_deploy_dir): # We won't write the updated script to the built-in deploy # directory @@ -2940,13 +3045,9 @@ def update_bootstrap(config, url=None): # 'cloud.deploy.d' from conf_file and syspaths is not included, add # them if deploy_d_from_conf_file not in deploy_scripts_search_paths: - deploy_scripts_search_paths.append( - (deploy_d_from_conf_file, True) - ) + deploy_scripts_search_paths.append((deploy_d_from_conf_file, True)) if deploy_d_from_syspaths not in deploy_scripts_search_paths: - deploy_scripts_search_paths.append( - (deploy_d_from_syspaths, True) - ) + deploy_scripts_search_paths.append((deploy_d_from_syspaths, True)) finished = [] finished_full = [] @@ -2961,37 +3062,37 @@ def update_bootstrap(config, url=None): try: os.makedirs(entry) except (OSError, IOError) as err: - log.info('Failed to create directory \'%s\'', entry) + log.info("Failed to create directory '%s'", entry) continue if not is_writeable(entry): - log.debug('The \'%s\' is not writeable. Continuing...', entry) + log.debug("The '%s' is not writeable. Continuing...", entry) continue deploy_path = os.path.join(entry, script_name) try: finished_full.append(deploy_path) - with salt.utils.files.fopen(deploy_path, 'w') as fp_: + with salt.utils.files.fopen(deploy_path, "w") as fp_: fp_.write(salt.utils.stringutils.to_str(script_content)) except (OSError, IOError) as err: - log.debug('Failed to write the updated script: %s', err) + log.debug("Failed to write the updated script: %s", err) continue - return {'Success': {'Files updated': finished_full}} + return {"Success": {"Files updated": finished_full}} def cache_node_list(nodes, provider, opts): - ''' + """ If configured to do so, update the cloud cachedir with the current list of nodes. Also fires configured events pertaining to the node list. .. versionadded:: 2014.7.0 - ''' - if 'update_cachedir' not in opts or not opts['update_cachedir']: + """ + if "update_cachedir" not in opts or not opts["update_cachedir"]: return - base = os.path.join(init_cachedir(), 'active') - driver = next(six.iterkeys(opts['providers'][provider])) + base = os.path.join(init_cachedir(), "active") + driver = next(six.iterkeys(opts["providers"][provider])) prov_dir = os.path.join(base, driver, provider) if not os.path.exists(prov_dir): os.makedirs(prov_dir) @@ -3001,40 +3102,40 @@ def cache_node_list(nodes, provider, opts): for node in nodes: diff_node_cache(prov_dir, node, nodes[node], opts) - path = os.path.join(prov_dir, '{0}.p'.format(node)) - mode = 'wb' if six.PY3 else 'w' + path = os.path.join(prov_dir, "{0}.p".format(node)) + mode = "wb" if six.PY3 else "w" with salt.utils.files.fopen(path, mode) as fh_: salt.utils.msgpack.dump(nodes[node], fh_, encoding=MSGPACK_ENCODING) def cache_node(node, provider, opts): - ''' + """ Cache node individually .. versionadded:: 2014.7.0 - ''' + """ if isinstance(opts, dict): __opts__.update(opts) - if 'update_cachedir' not in __opts__ or not __opts__['update_cachedir']: + if "update_cachedir" not in __opts__ or not __opts__["update_cachedir"]: return - if not os.path.exists(os.path.join(__opts__['cachedir'], 'active')): + if not os.path.exists(os.path.join(__opts__["cachedir"], "active")): init_cachedir() - base = os.path.join(__opts__['cachedir'], 'active') - provider, driver = provider.split(':') + base = os.path.join(__opts__["cachedir"], "active") + provider, driver = provider.split(":") prov_dir = os.path.join(base, driver, provider) if not os.path.exists(prov_dir): os.makedirs(prov_dir) - path = os.path.join(prov_dir, '{0}.p'.format(node['name'])) - mode = 'wb' if six.PY3 else 'w' + path = os.path.join(prov_dir, "{0}.p".format(node["name"])) + mode = "wb" if six.PY3 else "w" with salt.utils.files.fopen(path, mode) as fh_: salt.utils.msgpack.dump(node, fh_, encoding=MSGPACK_ENCODING) def missing_node_cache(prov_dir, node_list, provider, opts): - ''' + """ Check list of nodes to see if any nodes which were previously known about in the cache have been removed from the node list. @@ -3046,7 +3147,7 @@ def missing_node_cache(prov_dir, node_list, provider, opts): diff_cache_events: True .. versionadded:: 2014.7.0 - ''' + """ cached_nodes = [] for node in os.listdir(prov_dir): cached_nodes.append(os.path.splitext(node)[0]) @@ -3054,21 +3155,21 @@ def missing_node_cache(prov_dir, node_list, provider, opts): for node in cached_nodes: if node not in node_list: delete_minion_cachedir(node, provider, opts) - if 'diff_cache_events' in opts and opts['diff_cache_events']: + if "diff_cache_events" in opts and opts["diff_cache_events"]: fire_event( - 'event', - 'cached node missing from provider', - 'salt/cloud/{0}/cache_node_missing'.format(node), - args={'missing node': node}, + "event", + "cached node missing from provider", + "salt/cloud/{0}/cache_node_missing".format(node), + args={"missing node": node}, sock_dir=opts.get( - 'sock_dir', - os.path.join(__opts__['sock_dir'], 'master')), - transport=opts.get('transport', 'zeromq') + "sock_dir", os.path.join(__opts__["sock_dir"], "master") + ), + transport=opts.get("transport", "zeromq"), ) def diff_node_cache(prov_dir, node, new_data, opts): - ''' + """ Check new node data against current cache. If data differ, fire an event which consists of the new node data. @@ -3080,35 +3181,34 @@ def diff_node_cache(prov_dir, node, new_data, opts): diff_cache_events: True .. versionadded:: 2014.7.0 - ''' - if 'diff_cache_events' not in opts or not opts['diff_cache_events']: + """ + if "diff_cache_events" not in opts or not opts["diff_cache_events"]: return if node is None: return - path = '{0}.p'.format(os.path.join(prov_dir, node)) + path = "{0}.p".format(os.path.join(prov_dir, node)) if not os.path.exists(path): event_data = _strip_cache_events(new_data, opts) fire_event( - 'event', - 'new node found', - 'salt/cloud/{0}/cache_node_new'.format(node), - args={'new_data': event_data}, - sock_dir=opts.get( - 'sock_dir', - os.path.join(__opts__['sock_dir'], 'master')), - transport=opts.get('transport', 'zeromq') + "event", + "new node found", + "salt/cloud/{0}/cache_node_new".format(node), + args={"new_data": event_data}, + sock_dir=opts.get("sock_dir", os.path.join(__opts__["sock_dir"], "master")), + transport=opts.get("transport", "zeromq"), ) return - with salt.utils.files.fopen(path, 'r') as fh_: + with salt.utils.files.fopen(path, "r") as fh_: try: cache_data = salt.utils.data.decode( - salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING)) + salt.utils.msgpack.load(fh_, encoding=MSGPACK_ENCODING) + ) except ValueError: - log.warning('Cache for %s was corrupt: Deleting', node) + log.warning("Cache for %s was corrupt: Deleting", node) cache_data = {} # Perform a simple diff between the old and the new data, and if it differs, @@ -3117,22 +3217,20 @@ def diff_node_cache(prov_dir, node, new_data, opts): diff = salt.utils.compat.cmp(new_data, cache_data) if diff != 0: fire_event( - 'event', - 'node data differs', - 'salt/cloud/{0}/cache_node_diff'.format(node), + "event", + "node data differs", + "salt/cloud/{0}/cache_node_diff".format(node), args={ - 'new_data': _strip_cache_events(new_data, opts), - 'cache_data': _strip_cache_events(cache_data, opts), + "new_data": _strip_cache_events(new_data, opts), + "cache_data": _strip_cache_events(cache_data, opts), }, - sock_dir=opts.get( - 'sock_dir', - os.path.join(__opts__['sock_dir'], 'master')), - transport=opts.get('transport', 'zeromq') + sock_dir=opts.get("sock_dir", os.path.join(__opts__["sock_dir"], "master")), + transport=opts.get("transport", "zeromq"), ) def _strip_cache_events(data, opts): - ''' + """ Strip out user-configured sensitive event data. The fields to be stripped are configured in the main Salt Cloud configuration file, usually ``/etc/salt/cloud``. @@ -3144,9 +3242,9 @@ def _strip_cache_events(data, opts): - priv_key .. versionadded:: 2014.7.0 - ''' + """ event_data = copy.deepcopy(data) - strip_fields = opts.get('cache_event_strip_fields', []) + strip_fields = opts.get("cache_event_strip_fields", []) for field in strip_fields: if field in event_data: del event_data[field] @@ -3155,7 +3253,7 @@ def _strip_cache_events(data, opts): def _salt_cloud_force_ascii(exc): - ''' + """ Helper method to try its best to convert any Unicode text into ASCII without stack tracing since salt internally does not handle Unicode strings @@ -3163,62 +3261,69 @@ def _salt_cloud_force_ascii(exc): `py:module: salt.utils.cloud` is imported this method register's with python's codecs module for proper automatic conversion in case of encoding errors. - ''' + """ if not isinstance(exc, (UnicodeEncodeError, UnicodeTranslateError)): - raise TypeError('Can\'t handle {0}'.format(exc)) + raise TypeError("Can't handle {0}".format(exc)) unicode_trans = { # Convert non-breaking space to space - u'\xa0': u' ', + "\xa0": " ", # Convert en dash to dash - u'\u2013': u'-', + "\u2013": "-", } - if exc.object[exc.start:exc.end] in unicode_trans: - return unicode_trans[exc.object[exc.start:exc.end]], exc.end + if exc.object[exc.start : exc.end] in unicode_trans: + return unicode_trans[exc.object[exc.start : exc.end]], exc.end # There's nothing else we can do, raise the exception raise exc -codecs.register_error('salt-cloud-force-ascii', _salt_cloud_force_ascii) +codecs.register_error("salt-cloud-force-ascii", _salt_cloud_force_ascii) def retrieve_password_from_keyring(credential_id, username): - ''' + """ Retrieve particular user's password for a specified credential set from system keyring. - ''' + """ try: import keyring # pylint: disable=import-error + return keyring.get_password(credential_id, username) except ImportError: - log.error('USE_KEYRING configured as a password, but no keyring module is installed') + log.error( + "USE_KEYRING configured as a password, but no keyring module is installed" + ) return False def _save_password_in_keyring(credential_id, username, password): - ''' + """ Saves provider password in system keyring - ''' + """ try: import keyring # pylint: disable=import-error + return keyring.set_password(credential_id, username, password) except ImportError: - log.error('Tried to store password in keyring, but no keyring module is installed') + log.error( + "Tried to store password in keyring, but no keyring module is installed" + ) return False def store_password_in_keyring(credential_id, username, password=None): - ''' + """ Interactively prompts user for a password and stores it in system keyring - ''' + """ try: # pylint: disable=import-error import keyring import keyring.errors + # pylint: enable=import-error if password is None: - prompt = 'Please enter password for {0}: '.format(credential_id) + prompt = "Please enter password for {0}: ".format(credential_id) try: password = getpass.getpass(prompt) except EOFError: @@ -3227,37 +3332,44 @@ def store_password_in_keyring(credential_id, username, password=None): if not password: # WE should raise something else here to be able to use this # as/from an API - raise RuntimeError('Invalid password provided.') + raise RuntimeError("Invalid password provided.") try: _save_password_in_keyring(credential_id, username, password) except keyring.errors.PasswordSetError as exc: - log.debug('Problem saving password in the keyring: %s', exc) + log.debug("Problem saving password in the keyring: %s", exc) except ImportError: - log.error('Tried to store password in keyring, but no keyring module is installed') + log.error( + "Tried to store password in keyring, but no keyring module is installed" + ) return False def _unwrap_dict(dictionary, index_string): - ''' + """ Accepts index in form of a string Returns final value Example: dictionary = {'a': {'b': {'c': 'foobar'}}} index_string = 'a,b,c' returns 'foobar' - ''' - index = index_string.split(',') + """ + index = index_string.split(",") for k in index: dictionary = dictionary[k] return dictionary -def run_func_until_ret_arg(fun, kwargs, fun_call=None, - argument_being_watched=None, required_argument_response=None): - ''' +def run_func_until_ret_arg( + fun, + kwargs, + fun_call=None, + argument_being_watched=None, + required_argument_response=None, +): + """ Waits until the function retrieves some required argument. NOTE: Tested with ec2 describe_volumes and describe_snapshots only. - ''' + """ status = None while status != required_argument_response: f_result = fun(kwargs, call=fun_call) @@ -3270,8 +3382,10 @@ def run_func_until_ret_arg(fun, kwargs, fun_call=None, r_set[k] = v status = _unwrap_dict(r_set, argument_being_watched) log.debug( - 'Function: %s, Watched arg: %s, Response: %s', - six.text_type(fun).split(' ')[1], argument_being_watched, status + "Function: %s, Watched arg: %s, Response: %s", + six.text_type(fun).split(" ")[1], + argument_being_watched, + status, ) time.sleep(5) @@ -3279,26 +3393,24 @@ def run_func_until_ret_arg(fun, kwargs, fun_call=None, def get_salt_interface(vm_, opts): - ''' + """ Return the salt_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. - ''' + """ salt_host = salt.config.get_cloud_config_value( - 'salt_interface', vm_, opts, default=False, - search_global=False + "salt_interface", vm_, opts, default=False, search_global=False ) if salt_host is False: salt_host = salt.config.get_cloud_config_value( - 'ssh_interface', vm_, opts, default='public_ips', - search_global=False + "ssh_interface", vm_, opts, default="public_ips", search_global=False ) return salt_host def check_key_path_and_mode(provider, key_path): - ''' + """ Checks that the key_path exists and the key_mode is either 0400 or 0600. Returns True or False. @@ -3311,19 +3423,23 @@ def check_key_path_and_mode(provider, key_path): key_path The key_path to ensure that it exists and to check adequate permissions against. - ''' + """ if not os.path.exists(key_path): log.error( - 'The key file \'%s\' used in the \'%s\' provider configuration ' - 'does not exist.\n', key_path, provider + "The key file '%s' used in the '%s' provider configuration " + "does not exist.\n", + key_path, + provider, ) return False key_mode = stat.S_IMODE(os.stat(key_path).st_mode) if key_mode not in (0o400, 0o600): log.error( - 'The key file \'%s\' used in the \'%s\' provider configuration ' - 'needs to be set to mode 0400 or 0600.\n', key_path, provider + "The key file '%s' used in the '%s' provider configuration " + "needs to be set to mode 0400 or 0600.\n", + key_path, + provider, ) return False @@ -3331,47 +3447,45 @@ def check_key_path_and_mode(provider, key_path): def userdata_template(opts, vm_, userdata): - ''' + """ Use the configured templating engine to template the userdata file - ''' + """ # No userdata, no need to template anything if userdata is None: return userdata userdata_template = salt.config.get_cloud_config_value( - 'userdata_template', vm_, opts, search_global=False, default=None + "userdata_template", vm_, opts, search_global=False, default=None ) if userdata_template is False: return userdata # Use the cloud profile's userdata_template, otherwise get it from the # master configuration file. - renderer = opts.get('userdata_template') \ - if userdata_template is None \ + renderer = ( + opts.get("userdata_template") + if userdata_template is None else userdata_template + ) if renderer is None: return userdata else: render_opts = opts.copy() render_opts.update(vm_) rend = salt.loader.render(render_opts, {}) - blacklist = opts['renderer_blacklist'] - whitelist = opts['renderer_whitelist'] + blacklist = opts["renderer_blacklist"] + whitelist = opts["renderer_whitelist"] templated = salt.template.compile_template( - ':string:', - rend, - renderer, - blacklist, - whitelist, - input_data=userdata, + ":string:", rend, renderer, blacklist, whitelist, input_data=userdata, ) if not isinstance(templated, six.string_types): # template renderers like "jinja" should return a StringIO try: - templated = ''.join(templated.readlines()) + templated = "".join(templated.readlines()) except AttributeError: log.warning( - 'Templated userdata resulted in non-string result (%s), ' - 'converting to string', templated + "Templated userdata resulted in non-string result (%s), " + "converting to string", + templated, ) templated = six.text_type(templated) diff --git a/salt/utils/color.py b/salt/utils/color.py index 929b10c4802..89900702d92 100644 --- a/salt/utils/color.py +++ b/salt/utils/color.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Functions used for CLI color themes. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -16,33 +17,34 @@ log = logging.getLogger(__name__) def get_color_theme(theme): - ''' + """ Return the color theme to use - ''' + """ # Keep the heavy lifting out of the module space import salt.utils.data import salt.utils.files import salt.utils.yaml + if not os.path.isfile(theme): - log.warning('The named theme %s if not available', theme) + log.warning("The named theme %s if not available", theme) try: - with salt.utils.files.fopen(theme, 'rb') as fp_: + with salt.utils.files.fopen(theme, "rb") as fp_: colors = salt.utils.data.decode(salt.utils.yaml.safe_load(fp_)) ret = {} for color in colors: - ret[color] = '\033[{0}m'.format(colors[color]) + ret[color] = "\033[{0}m".format(colors[color]) if not isinstance(colors, dict): - log.warning('The theme file %s is not a dict', theme) + log.warning("The theme file %s is not a dict", theme) return {} return ret except Exception: # pylint: disable=broad-except - log.warning('Failed to read the color theme %s', theme) + log.warning("Failed to read the color theme %s", theme) return {} def get_colors(use=True, theme=None): - ''' + """ Return the colors as an easy to use dict. Pass `False` to deactivate all colors by setting them to empty strings. Pass a string containing only the name of a single color to be used in place of all colors. Examples: @@ -53,40 +55,40 @@ def get_colors(use=True, theme=None): no_colors = get_colors(False) # disable all colors red_colors = get_colors('RED') # set all colors to red - ''' + """ colors = { - 'BLACK': TextFormat('black'), - 'DARK_GRAY': TextFormat('bold', 'black'), - 'RED': TextFormat('red'), - 'LIGHT_RED': TextFormat('bold', 'red'), - 'GREEN': TextFormat('green'), - 'LIGHT_GREEN': TextFormat('bold', 'green'), - 'YELLOW': TextFormat('yellow'), - 'LIGHT_YELLOW': TextFormat('bold', 'yellow'), - 'BLUE': TextFormat('blue'), - 'LIGHT_BLUE': TextFormat('bold', 'blue'), - 'MAGENTA': TextFormat('magenta'), - 'LIGHT_MAGENTA': TextFormat('bold', 'magenta'), - 'CYAN': TextFormat('cyan'), - 'LIGHT_CYAN': TextFormat('bold', 'cyan'), - 'LIGHT_GRAY': TextFormat('white'), - 'WHITE': TextFormat('bold', 'white'), - 'DEFAULT_COLOR': TextFormat('default'), - 'ENDC': TextFormat('reset'), + "BLACK": TextFormat("black"), + "DARK_GRAY": TextFormat("bold", "black"), + "RED": TextFormat("red"), + "LIGHT_RED": TextFormat("bold", "red"), + "GREEN": TextFormat("green"), + "LIGHT_GREEN": TextFormat("bold", "green"), + "YELLOW": TextFormat("yellow"), + "LIGHT_YELLOW": TextFormat("bold", "yellow"), + "BLUE": TextFormat("blue"), + "LIGHT_BLUE": TextFormat("bold", "blue"), + "MAGENTA": TextFormat("magenta"), + "LIGHT_MAGENTA": TextFormat("bold", "magenta"), + "CYAN": TextFormat("cyan"), + "LIGHT_CYAN": TextFormat("bold", "cyan"), + "LIGHT_GRAY": TextFormat("white"), + "WHITE": TextFormat("bold", "white"), + "DEFAULT_COLOR": TextFormat("default"), + "ENDC": TextFormat("reset"), } if theme: colors.update(get_color_theme(theme)) if not use: for color in colors: - colors[color] = '' + colors[color] = "" if isinstance(use, six.string_types): # Try to set all of the colors to the passed color if use in colors: for color in colors: # except for color reset - if color == 'ENDC': + if color == "ENDC": continue colors[color] = colors[use] diff --git a/salt/utils/compat.py b/salt/utils/compat.py index 40e11e93b7e..50924cedd1b 100644 --- a/salt/utils/compat.py +++ b/salt/utils/compat.py @@ -1,32 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" Compatibility functions for utils -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import sys + import copy -import types import importlib +import sys +import types # Import salt libs import salt.loader def pack_dunder(name): - ''' + """ Compatibility helper function to make __utils__ available on demand. - ''' + """ # TODO: Deprecate starting with Beryllium mod = sys.modules[name] - if not hasattr(mod, '__utils__'): - setattr(mod, '__utils__', salt.loader.utils(mod.__opts__)) + if not hasattr(mod, "__utils__"): + setattr(mod, "__utils__", salt.loader.utils(mod.__opts__)) def deepcopy_bound(name): - ''' + """ Compatibility helper function to allow copy.deepcopy copy bound methods which is broken on Python 2.6, due to the following bug: https://bugs.python.org/issue1515 @@ -37,9 +38,13 @@ def deepcopy_bound(name): - Not Py3 compatible. The intended use case is deepcopy compat for Py2.6 - ''' + """ + def _deepcopy_method(x, memo): - return type(x)(x.im_func, copy.deepcopy(x.im_self, memo), x.im_class) # pylint: disable=incompatible-py3-code + # pylint: disable=incompatible-py3-code + return type(x)(x.im_func, copy.deepcopy(x.im_self, memo), x.im_class) + # pylint: enable=incompatible-py3-code + try: pre_dispatch = copy._deepcopy_dispatch copy._deepcopy_dispatch[types.MethodType] = _deepcopy_method @@ -50,21 +55,21 @@ def deepcopy_bound(name): def cmp(x, y): - ''' + """ Compatibility helper function to replace the ``cmp`` function from Python 2. The ``cmp`` function is no longer available in Python 3. cmp(x, y) -> integer Return negative if x<y, zero if x==y, positive if x>y. - ''' + """ return (x > y) - (x < y) def reload(mod): - ''' + """ Compatibility helper function to replace the ``reload`` builtin from Python 2. - ''' + """ try: return importlib.reload(mod) except AttributeError: diff --git a/salt/utils/configcomparer.py b/salt/utils/configcomparer.py index f2ed21ee2e6..4cf4d5de387 100644 --- a/salt/utils/configcomparer.py +++ b/salt/utils/configcomparer.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Utilities for comparing and updating configurations while keeping track of changes in a way that can be easily reported in a state. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -11,25 +11,25 @@ from __future__ import absolute_import, print_function, unicode_literals from salt.ext import six -def compare_and_update_config(config, update_config, changes, namespace=''): - ''' +def compare_and_update_config(config, update_config, changes, namespace=""): + """ Recursively compare two configs, writing any needed changes to the update_config and capturing changes in the changes dict. - ''' + """ if isinstance(config, dict): if not update_config: if config: # the updated config is more valid--report that we are using it changes[namespace] = { - 'new': config, - 'old': update_config, + "new": config, + "old": update_config, } return config elif not isinstance(update_config, dict): # new config is a dict, other isn't--new one wins changes[namespace] = { - 'new': config, - 'old': update_config, + "new": config, + "old": update_config, } return config else: @@ -39,12 +39,9 @@ def compare_and_update_config(config, update_config, changes, namespace=''): for key, value in six.iteritems(config): _namespace = key if namespace: - _namespace = '{0}.{1}'.format(namespace, _namespace) + _namespace = "{0}.{1}".format(namespace, _namespace) update_config[key] = compare_and_update_config( - value, - update_config.get(key, None), - changes, - namespace=_namespace, + value, update_config.get(key, None), changes, namespace=_namespace, ) return update_config @@ -53,38 +50,35 @@ def compare_and_update_config(config, update_config, changes, namespace=''): if config: # the updated config is more valid--report that we are using it changes[namespace] = { - 'new': config, - 'old': update_config, + "new": config, + "old": update_config, } return config elif not isinstance(update_config, list): # new config is a list, other isn't--new one wins changes[namespace] = { - 'new': config, - 'old': update_config, + "new": config, + "old": update_config, } return config else: # iterate through config list, ensuring that each index in the # update_config list is the same for idx, item in enumerate(config): - _namespace = '[{0}]'.format(idx) + _namespace = "[{0}]".format(idx) if namespace: - _namespace = '{0}{1}'.format(namespace, _namespace) + _namespace = "{0}{1}".format(namespace, _namespace) _update = None if len(update_config) > idx: _update = update_config[idx] if _update: update_config[idx] = compare_and_update_config( - config[idx], - _update, - changes, - namespace=_namespace, + config[idx], _update, changes, namespace=_namespace, ) else: changes[_namespace] = { - 'new': config[idx], - 'old': _update, + "new": config[idx], + "old": _update, } update_config.append(config[idx]) @@ -93,20 +87,20 @@ def compare_and_update_config(config, update_config, changes, namespace=''): for idx, old_item in enumerate(update_config): if idx < len(config): continue - _namespace = '[{0}]'.format(idx) + _namespace = "[{0}]".format(idx) if namespace: - _namespace = '{0}{1}'.format(namespace, _namespace) + _namespace = "{0}{1}".format(namespace, _namespace) changes[_namespace] = { - 'new': None, - 'old': old_item, + "new": None, + "old": old_item, } - del update_config[len(config):] + del update_config[len(config) :] return update_config else: if config != update_config: changes[namespace] = { - 'new': config, - 'old': update_config, + "new": config, + "old": update_config, } return config diff --git a/salt/utils/configparser.py b/salt/utils/configparser.py index 7a3b1cd2ef1..ceebcf30de3 100644 --- a/salt/utils/configparser.py +++ b/salt/utils/configparser.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Custom configparser classes -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import re # Import Salt libs @@ -22,7 +23,7 @@ except ImportError: # pylint: disable=string-substitution-usage-error class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-variable - ''' + """ Custom ConfigParser which reads and writes git config files. READ A GIT CONFIG FILE INTO THE PARSER OBJECT @@ -44,20 +45,24 @@ class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-var >>> with salt.utils.files.fopen('/home/user/.git/config', 'w') as fh: ... conf.write(fh) >>> - ''' - DEFAULTSECT = 'DEFAULT' - SPACEINDENT = ' ' * 8 + """ - def __init__(self, defaults=None, dict_type=_default_dict, # pylint: disable=useless-super-delegation - allow_no_value=True): - ''' + DEFAULTSECT = "DEFAULT" + SPACEINDENT = " " * 8 + + # pylint: disable=useless-super-delegation + def __init__( + self, defaults=None, dict_type=_default_dict, allow_no_value=True, + ): + """ Changes default value for allow_no_value from False to True - ''' - super(GitConfigParser, self).__init__( - defaults, dict_type, allow_no_value) + """ + super(GitConfigParser, self).__init__(defaults, dict_type, allow_no_value) + + # pylint: enable=useless-super-delegation def _read(self, fp, fpname): - ''' + """ Makes the following changes from the RawConfigParser: 1. Strip leading tabs from non-section-header lines. @@ -66,31 +71,31 @@ class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-var 4. Drops support for continuation lines. 5. Multiple values for a given option are stored as a list. 6. Keys and values are decoded to the system encoding. - ''' - cursect = None # None, or a dictionary + """ + cursect = None # None, or a dictionary optname = None lineno = 0 - e = None # None, or an exception + e = None # None, or an exception while True: line = salt.utils.stringutils.to_unicode(fp.readline()) if not line: break lineno = lineno + 1 # comment or blank line? - if line.strip() == '' or line[0] in '#;': + if line.strip() == "" or line[0] in "#;": continue - if line.split(None, 1)[0].lower() == 'rem' and line[0] in 'rR': + if line.split(None, 1)[0].lower() == "rem" and line[0] in "rR": # no leading whitespace continue # Replace space indentation with a tab. Allows parser to work # properly in cases where someone has edited the git config by hand # and indented using spaces instead of tabs. if line.startswith(self.SPACEINDENT): - line = '\t' + line[len(self.SPACEINDENT):] + line = "\t" + line[len(self.SPACEINDENT) :] # is it a section header? mo = self.SECTCRE.match(line) if mo: - sectname = mo.group('header') + sectname = mo.group("header") if sectname in self._sections: cursect = self._sections[sectname] elif sectname == self.DEFAULTSECT: @@ -105,26 +110,27 @@ class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-var raise MissingSectionHeaderError( # pylint: disable=undefined-variable salt.utils.stringutils.to_str(fpname), lineno, - salt.utils.stringutils.to_str(line)) + salt.utils.stringutils.to_str(line), + ) # an option line? else: mo = self._optcre.match(line.lstrip()) if mo: - optname, vi, optval = mo.group('option', 'vi', 'value') + optname, vi, optval = mo.group("option", "vi", "value") optname = self.optionxform(optname.rstrip()) if optval is None: - optval = '' + optval = "" if optval: - if vi in ('=', ':') and ';' in optval: + if vi in ("=", ":") and ";" in optval: # ';' is a comment delimiter only if it follows # a spacing character - pos = optval.find(';') - if pos != -1 and optval[pos-1].isspace(): + pos = optval.find(";") + if pos != -1 and optval[pos - 1].isspace(): optval = optval[:pos] optval = optval.strip() # Empty strings should be considered as blank strings if optval in ('""', "''"): - optval = '' + optval = "" self._add_option(cursect, optname, optval) else: # a non-fatal parsing error occurred. set up the @@ -139,35 +145,37 @@ class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-var raise e # pylint: disable=raising-bad-type def _string_check(self, value, allow_list=False): - ''' + """ Based on the string-checking code from the SafeConfigParser's set() function, this enforces string values for config options. - ''' + """ if self._optcre is self.OPTCRE or value: is_list = isinstance(value, list) if is_list and not allow_list: - raise TypeError('option value cannot be a list unless allow_list is True') + raise TypeError( + "option value cannot be a list unless allow_list is True" + ) elif not is_list: value = [value] if not all(isinstance(x, six.string_types) for x in value): - raise TypeError('option values must be strings') + raise TypeError("option values must be strings") def get(self, section, option, as_list=False): - ''' + """ Adds an optional "as_list" argument to ensure a list is returned. This is helpful when iterating over an option which may or may not be a multivar. - ''' + """ ret = super(GitConfigParser, self).get(section, option) if as_list and not isinstance(ret, list): ret = [ret] return ret - def set(self, section, option, value=''): - ''' + def set(self, section, option, value=""): + """ This is overridden from the RawConfigParser merely to change the default value for the 'value' argument. - ''' + """ self._string_check(value) super(GitConfigParser, self).set(section, option, value) @@ -186,17 +194,19 @@ class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-var sectdict[key] = [sectdict[key]] sectdict[key].append(value) else: - raise TypeError('Expected str or list for option value, got %s' % type(value).__name__) + raise TypeError( + "Expected str or list for option value, got %s" % type(value).__name__ + ) - def set_multivar(self, section, option, value=''): - ''' + def set_multivar(self, section, option, value=""): + """ This function is unique to the GitConfigParser. It will add another value for the option if it already exists, converting the option's value to a list if applicable. If "value" is a list, then any existing values for the specified section and option will be replaced with the list being passed. - ''' + """ self._string_check(value, allow_list=True) if not section or section == self.DEFAULTSECT: sectdict = self._defaults @@ -205,15 +215,16 @@ class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-var sectdict = self._sections[section] except KeyError: raise NoSectionError( # pylint: disable=undefined-variable - salt.utils.stringutils.to_str(section)) + salt.utils.stringutils.to_str(section) + ) key = self.optionxform(option) self._add_option(sectdict, key, value) def remove_option_regexp(self, section, option, expr): - ''' + """ Remove an option with a value matching the expression. Works on single values and multivars. - ''' + """ if not section or section == self.DEFAULTSECT: sectdict = self._defaults else: @@ -221,7 +232,8 @@ class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-var sectdict = self._sections[section] except KeyError: raise NoSectionError( # pylint: disable=undefined-variable - salt.utils.stringutils.to_str(section)) + salt.utils.stringutils.to_str(section) + ) option = self.optionxform(option) if option not in sectdict: return False @@ -243,7 +255,7 @@ class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-var return existed def write(self, fp_): - ''' + """ Makes the following changes from the RawConfigParser: 1. Prepends options with a tab character. @@ -251,20 +263,22 @@ class GitConfigParser(RawConfigParser, object): # pylint: disable=undefined-var 3. When an option's value is a list, a line for each option is written. This allows us to support multivars like a remote's "fetch" option. 4. Drops support for continuation lines. - ''' - convert = salt.utils.stringutils.to_bytes \ - if 'b' in fp_.mode \ + """ + convert = ( + salt.utils.stringutils.to_bytes + if "b" in fp_.mode else salt.utils.stringutils.to_str + ) if self._defaults: - fp_.write(convert('[%s]\n' % self.DEFAULTSECT)) + fp_.write(convert("[%s]\n" % self.DEFAULTSECT)) for (key, value) in six.iteritems(self._defaults): - value = salt.utils.stringutils.to_unicode(value).replace('\n', '\n\t') - fp_.write(convert('%s = %s\n' % (key, value))) + value = salt.utils.stringutils.to_unicode(value).replace("\n", "\n\t") + fp_.write(convert("%s = %s\n" % (key, value))) for section in self._sections: - fp_.write(convert('[%s]\n' % section)) + fp_.write(convert("[%s]\n" % section)) for (key, value) in six.iteritems(self._sections[section]): if (value is not None) or (self._optcre == self.OPTCRE): if not isinstance(value, list): value = [value] for item in value: - fp_.write(convert('\t%s\n' % ' = '.join((key, item)).rstrip())) + fp_.write(convert("\t%s\n" % " = ".join((key, item)).rstrip())) diff --git a/salt/utils/context.py b/salt/utils/context.py index 948f92c5e94..3a1b6609b8d 100644 --- a/salt/utils/context.py +++ b/salt/utils/context.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) :codeauthor: Thomas Jackson (jacksontj.89@gmail.com) @@ -8,29 +8,32 @@ ~~~~~~~~~~~~~~~~~~ Context managers used throughout Salt's source code. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs import copy import threading -try: - from collections.abc import MutableMapping -except ImportError: - from collections import MutableMapping - from contextlib import contextmanager from salt.ext import six +try: + from collections.abc import MutableMapping +except ImportError: + # pylint: disable=no-name-in-module + from collections import MutableMapping + + # pylint: enable=no-name-in-module + @contextmanager def func_globals_inject(func, **overrides): - ''' + """ Override specific variables within a function's global context. - ''' + """ # recognize methods - if hasattr(func, 'im_func'): + if hasattr(func, "im_func"): func = func.__func__ # Get a reference to the function globals dictionary @@ -62,13 +65,13 @@ def func_globals_inject(func, **overrides): class ContextDict(MutableMapping): - ''' + """ A context manager that saves some per-thread state globally. Intended for use with Tornado's StackContext. Provide arbitrary data as kwargs upon creation, then allow any children to override the values of the parent. - ''' + """ def __init__(self, threadsafe=False, **data): # state should be thread local, so this object can be threadsafe @@ -82,10 +85,10 @@ class ContextDict(MutableMapping): @property def active(self): - '''Determine if this ContextDict is currently overridden + """Determine if this ContextDict is currently overridden Since the ContextDict can be overridden in each thread, we check whether the _state.data is set or not. - ''' + """ try: return self._state.data is not None except AttributeError: @@ -93,10 +96,12 @@ class ContextDict(MutableMapping): # TODO: rename? def clone(self, **kwargs): - ''' + """ Clone this context, and return the ChildContextDict - ''' - child = ChildContextDict(parent=self, threadsafe=self._threadsafe, overrides=kwargs) + """ + child = ChildContextDict( + parent=self, threadsafe=self._threadsafe, overrides=kwargs + ) return child def __setitem__(self, key, val): @@ -147,9 +152,10 @@ class ContextDict(MutableMapping): class ChildContextDict(MutableMapping): - '''An overrideable child of ContextDict + """An overrideable child of ContextDict + + """ - ''' def __init__(self, parent, overrides=None, threadsafe=False): self.parent = parent self._data = {} if overrides is None else overrides @@ -185,7 +191,7 @@ class ChildContextDict(MutableMapping): return iter(self._data) def __enter__(self): - if hasattr(self.parent._state, 'data'): + if hasattr(self.parent._state, "data"): # Save old data to support nested calls self._old_data = self.parent._state.data self.parent._state.data = self._data @@ -195,11 +201,11 @@ class ChildContextDict(MutableMapping): class NamespacedDictWrapper(MutableMapping, dict): - ''' + """ Create a dict which wraps another dict with a specific prefix of key(s) MUST inherit from dict to serialize through msgpack correctly - ''' + """ def __init__(self, d, pre_keys, override_name=None): # pylint: disable=W0231 self.__dict = d @@ -209,11 +215,12 @@ class NamespacedDictWrapper(MutableMapping, dict): self.pre_keys = pre_keys if override_name is not None: import salt.utils.versions + salt.utils.versions.warn_until( - 'Sodium', - 'Overriding the class name is no longer supported. Please ' - 'remove the override_name argument before it is removed in ' - 'Salt Sodium.' + "Sodium", + "Overriding the class name is no longer supported. Please " + "remove the override_name argument before it is removed in " + "Salt Sodium.", ) super(NamespacedDictWrapper, self).__init__(self._dict()) @@ -242,12 +249,12 @@ class NamespacedDictWrapper(MutableMapping, dict): return iter(self._dict()) def __copy__(self): - return type(self)(copy.copy(self.__dict), - copy.copy(self.pre_keys)) + return type(self)(copy.copy(self.__dict), copy.copy(self.pre_keys)) def __deepcopy__(self, memo): - return type(self)(copy.deepcopy(self.__dict, memo), - copy.deepcopy(self.pre_keys, memo)) + return type(self)( + copy.deepcopy(self.__dict, memo), copy.deepcopy(self.pre_keys, memo) + ) def __str__(self): return self._dict().__str__() diff --git a/salt/utils/crypt.py b/salt/utils/crypt.py index 10b744044ee..777a256bed8 100644 --- a/salt/utils/crypt.py +++ b/salt/utils/crypt.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Functions dealing with encryption -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -10,27 +10,26 @@ import hashlib import logging import os -log = logging.getLogger(__name__) - # Import Salt libs import salt.loader import salt.utils.files from salt.exceptions import SaltInvocationError +log = logging.getLogger(__name__) + + try: import Crypto.Random + HAS_CRYPTO = True except ImportError: HAS_CRYPTO = False -def decrypt(data, - rend, - translate_newlines=False, - renderers=None, - opts=None, - valid_rend=None): - ''' +def decrypt( + data, rend, translate_newlines=False, renderers=None, opts=None, valid_rend=None +): + """ .. versionadded:: 2017.7.0 Decrypt a data structure using the specified renderer. Written originally @@ -71,12 +70,12 @@ def decrypt(data, A list containing valid renderers, used to restrict the renderers which this function will be allowed to use. If not passed, no restriction will be made. - ''' + """ try: if valid_rend and rend not in valid_rend: raise SaltInvocationError( - '\'{0}\' is not a valid decryption renderer. Valid choices ' - 'are: {1}'.format(rend, ', '.join(valid_rend)) + "'{0}' is not a valid decryption renderer. Valid choices " + "are: {1}".format(rend, ", ".join(valid_rend)) ) except TypeError as exc: # SaltInvocationError inherits TypeError, so check for it first and @@ -84,24 +83,24 @@ def decrypt(data, if isinstance(exc, SaltInvocationError): raise # 'valid' argument is not iterable - log.error('Non-iterable value %s passed for valid_rend', valid_rend) + log.error("Non-iterable value %s passed for valid_rend", valid_rend) if renderers is None: if opts is None: - raise TypeError('opts are required') + raise TypeError("opts are required") renderers = salt.loader.render(opts, {}) rend_func = renderers.get(rend) if rend_func is None: raise SaltInvocationError( - 'Decryption renderer \'{0}\' is not available'.format(rend) + "Decryption renderer '{0}' is not available".format(rend) ) return rend_func(data, translate_newlines=translate_newlines) def reinit_crypto(): - ''' + """ When a fork arises, pycrypto needs to reinit From its doc:: @@ -109,32 +108,32 @@ def reinit_crypto(): you must call Random.atfork() in both the parent and child processes after using os.fork() - ''' + """ if HAS_CRYPTO: Crypto.Random.atfork() -def pem_finger(path=None, key=None, sum_type='sha256'): - ''' +def pem_finger(path=None, key=None, sum_type="sha256"): + """ Pass in either a raw pem string, or the path on disk to the location of a pem file, and the type of cryptographic hash to use. The default is SHA256. The fingerprint of the pem will be returned. If neither a key nor a path are passed in, a blank string will be returned. - ''' + """ if not key: if not os.path.isfile(path): - return '' + return "" - with salt.utils.files.fopen(path, 'rb') as fp_: - key = b''.join([x for x in fp_.readlines() if x.strip()][1:-1]) + with salt.utils.files.fopen(path, "rb") as fp_: + key = b"".join([x for x in fp_.readlines() if x.strip()][1:-1]) pre = getattr(hashlib, sum_type)(key).hexdigest() - finger = '' + finger = "" for ind, _ in enumerate(pre): if ind % 2: # Is odd - finger += '{0}:'.format(pre[ind]) + finger += "{0}:".format(pre[ind]) else: finger += pre[ind] - return finger.rstrip(':') + return finger.rstrip(":") diff --git a/salt/utils/ctx.py b/salt/utils/ctx.py index 6b772172a8a..cc4c6ed2c59 100644 --- a/salt/utils/ctx.py +++ b/salt/utils/ctx.py @@ -1,21 +1,23 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, with_statement, print_function, unicode_literals +from __future__ import absolute_import, print_function, unicode_literals, with_statement + import threading class ClassProperty(property): - ''' + """ Use a classmethod as a property http://stackoverflow.com/a/1383402/1258307 - ''' + """ + def __get__(self, cls, owner): return self.fget.__get__(None, owner)() # pylint: disable=no-member class RequestContext(object): - ''' + """ A context manager that saves some per-thread state globally. Intended for use with Tornado's StackContext. https://gist.github.com/simon-weber/7755289 @@ -24,7 +26,7 @@ class RequestContext(object): .. code:: python from raas.utils.ctx import RequestContext current_request_handler = RequestContext.current - ''' + """ _state = threading.local() _state.current_request = {} @@ -35,7 +37,7 @@ class RequestContext(object): @ClassProperty @classmethod def current(cls): - if not hasattr(cls._state, 'current_request'): + if not hasattr(cls._state, "current_request"): return {} return cls._state.current_request diff --git a/salt/utils/data.py b/salt/utils/data.py index 8f84c2ea42e..e7c2d4f6bf6 100644 --- a/salt/utils/data.py +++ b/salt/utils/data.py @@ -1,22 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Functions for manipulating, inspecting, or otherwise working with data types and data structures. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import copy import fnmatch +import functools import logging import re -import functools - -try: - from collections.abc import Mapping, MutableMapping, Sequence -except ImportError: - from collections import Mapping, MutableMapping, Sequence # Import Salt libs import salt.utils.dictupdate @@ -24,13 +19,22 @@ import salt.utils.stringutils import salt.utils.yaml from salt.defaults import DEFAULT_TARGET_DELIM from salt.exceptions import SaltException +from salt.ext import six + +# Import 3rd-party libs +from salt.ext.six.moves import range # pylint: disable=redefined-builtin +from salt.ext.six.moves import zip # pylint: disable=redefined-builtin from salt.utils.decorators.jinja import jinja_filter from salt.utils.odict import OrderedDict -# Import 3rd-party libs -from salt.ext.six.moves import zip # pylint: disable=redefined-builtin -from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=redefined-builtin +try: + from collections.abc import Mapping, MutableMapping, Sequence +except ImportError: + # pylint: disable=no-name-in-module + from collections import Mapping, MutableMapping, Sequence + + # pylint: enable=no-name-in-module + try: import jmespath @@ -41,15 +45,16 @@ log = logging.getLogger(__name__) class CaseInsensitiveDict(MutableMapping): - ''' + """ Inspired by requests' case-insensitive dict implementation, but works with non-string keys as well. - ''' + """ + def __init__(self, init=None, **kwargs): - ''' + """ Force internal dict to be ordered to ensure a consistent iteration order, irrespective of case. - ''' + """ self._data = OrderedDict() self.update(init or {}, **kwargs) @@ -79,25 +84,25 @@ class CaseInsensitiveDict(MutableMapping): return repr(dict(six.iteritems(self))) def items_lower(self): - ''' + """ Returns a generator iterating over keys and values, with the keys all being lowercase. - ''' + """ return ((key, val[1]) for key, val in six.iteritems(self._data)) def copy(self): - ''' + """ Returns a copy of the object - ''' + """ return CaseInsensitiveDict(six.iteritems(self._data)) def __change_case(data, attr, preserve_dict_class=False): - ''' + """ Calls data.attr() if data has an attribute/method called attr. Processes data recursively if data is a Mapping or Sequence. For Mapping, processes both keys and values. - ''' + """ try: return getattr(data, attr)() except AttributeError: @@ -107,73 +112,80 @@ def __change_case(data, attr, preserve_dict_class=False): if isinstance(data, Mapping): return (data_type if preserve_dict_class else dict)( - (__change_case(key, attr, preserve_dict_class), - __change_case(val, attr, preserve_dict_class)) + ( + __change_case(key, attr, preserve_dict_class), + __change_case(val, attr, preserve_dict_class), + ) for key, val in six.iteritems(data) ) if isinstance(data, Sequence): return data_type( - __change_case(item, attr, preserve_dict_class) for item in data) + __change_case(item, attr, preserve_dict_class) for item in data + ) return data def to_lowercase(data, preserve_dict_class=False): - ''' + """ Recursively changes everything in data to lowercase. - ''' - return __change_case(data, 'lower', preserve_dict_class) + """ + return __change_case(data, "lower", preserve_dict_class) def to_uppercase(data, preserve_dict_class=False): - ''' + """ Recursively changes everything in data to uppercase. - ''' - return __change_case(data, 'upper', preserve_dict_class) + """ + return __change_case(data, "upper", preserve_dict_class) -@jinja_filter('compare_dicts') +@jinja_filter("compare_dicts") def compare_dicts(old=None, new=None): - ''' + """ Compare before and after results from various salt functions, returning a dict describing the changes that were made. - ''' + """ ret = {} for key in set((new or {})).union((old or {})): if key not in old: # New key - ret[key] = {'old': '', - 'new': new[key]} + ret[key] = {"old": "", "new": new[key]} elif key not in new: # Key removed - ret[key] = {'new': '', - 'old': old[key]} + ret[key] = {"new": "", "old": old[key]} elif new[key] != old[key]: # Key modified - ret[key] = {'old': old[key], - 'new': new[key]} + ret[key] = {"old": old[key], "new": new[key]} return ret -@jinja_filter('compare_lists') +@jinja_filter("compare_lists") def compare_lists(old=None, new=None): - ''' + """ Compare before and after results from various salt functions, returning a dict describing the changes that were made - ''' + """ ret = {} for item in new: if item not in old: - ret.setdefault('new', []).append(item) + ret.setdefault("new", []).append(item) for item in old: if item not in new: - ret.setdefault('old', []).append(item) + ret.setdefault("old", []).append(item) return ret -def decode(data, encoding=None, errors='strict', keep=False, - normalize=False, preserve_dict_class=False, preserve_tuples=False, - to_str=False): - ''' +def decode( + data, + encoding=None, + errors="strict", + keep=False, + normalize=False, + preserve_dict_class=False, + preserve_tuples=False, + to_str=False, +): + """ Generic function which will decode whichever type is passed, if necessary. Optionally use to_str=True to ensure strings are str types and not unicode on Python 2. @@ -199,22 +211,51 @@ def decode(data, encoding=None, errors='strict', keep=False, two strings above, in which "й" is represented as two code points (i.e. one for the base character, and one for the breve mark). Normalizing allows for a more reliable test case. - ''' - _decode_func = salt.utils.stringutils.to_unicode \ - if not to_str \ + """ + _decode_func = ( + salt.utils.stringutils.to_unicode + if not to_str else salt.utils.stringutils.to_str + ) if isinstance(data, Mapping): - return decode_dict(data, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + return decode_dict( + data, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) if isinstance(data, list): - return decode_list(data, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + return decode_list( + data, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) if isinstance(data, tuple): - return decode_tuple(data, encoding, errors, keep, normalize, - preserve_dict_class, to_str) \ - if preserve_tuples \ - else decode_list(data, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + return ( + decode_tuple( + data, encoding, errors, keep, normalize, preserve_dict_class, to_str + ) + if preserve_tuples + else decode_list( + data, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) + ) try: data = _decode_func(data, encoding, errors, normalize) except TypeError: @@ -228,25 +269,45 @@ def decode(data, encoding=None, errors='strict', keep=False, return data -def decode_dict(data, encoding=None, errors='strict', keep=False, - normalize=False, preserve_dict_class=False, - preserve_tuples=False, to_str=False): - ''' +def decode_dict( + data, + encoding=None, + errors="strict", + keep=False, + normalize=False, + preserve_dict_class=False, + preserve_tuples=False, + to_str=False, +): + """ Decode all string values to Unicode. Optionally use to_str=True to ensure strings are str types and not unicode on Python 2. - ''' - _decode_func = salt.utils.stringutils.to_unicode \ - if not to_str \ + """ + _decode_func = ( + salt.utils.stringutils.to_unicode + if not to_str else salt.utils.stringutils.to_str + ) # Make sure we preserve OrderedDicts ret = data.__class__() if preserve_dict_class else {} for key, value in six.iteritems(data): if isinstance(key, tuple): - key = decode_tuple(key, encoding, errors, keep, normalize, - preserve_dict_class, to_str) \ - if preserve_tuples \ - else decode_list(key, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + key = ( + decode_tuple( + key, encoding, errors, keep, normalize, preserve_dict_class, to_str + ) + if preserve_tuples + else decode_list( + key, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) + ) else: try: key = _decode_func(key, encoding, errors, normalize) @@ -260,17 +321,50 @@ def decode_dict(data, encoding=None, errors='strict', keep=False, raise if isinstance(value, list): - value = decode_list(value, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + value = decode_list( + value, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) elif isinstance(value, tuple): - value = decode_tuple(value, encoding, errors, keep, normalize, - preserve_dict_class, to_str) \ - if preserve_tuples \ - else decode_list(value, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + value = ( + decode_tuple( + value, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + to_str, + ) + if preserve_tuples + else decode_list( + value, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) + ) elif isinstance(value, Mapping): - value = decode_dict(value, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + value = decode_dict( + value, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) else: try: value = _decode_func(value, encoding, errors, normalize) @@ -287,30 +381,66 @@ def decode_dict(data, encoding=None, errors='strict', keep=False, return ret -def decode_list(data, encoding=None, errors='strict', keep=False, - normalize=False, preserve_dict_class=False, - preserve_tuples=False, to_str=False): - ''' +def decode_list( + data, + encoding=None, + errors="strict", + keep=False, + normalize=False, + preserve_dict_class=False, + preserve_tuples=False, + to_str=False, +): + """ Decode all string values to Unicode. Optionally use to_str=True to ensure strings are str types and not unicode on Python 2. - ''' - _decode_func = salt.utils.stringutils.to_unicode \ - if not to_str \ + """ + _decode_func = ( + salt.utils.stringutils.to_unicode + if not to_str else salt.utils.stringutils.to_str + ) ret = [] for item in data: if isinstance(item, list): - item = decode_list(item, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + item = decode_list( + item, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) elif isinstance(item, tuple): - item = decode_tuple(item, encoding, errors, keep, normalize, - preserve_dict_class, to_str) \ - if preserve_tuples \ - else decode_list(item, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + item = ( + decode_tuple( + item, encoding, errors, keep, normalize, preserve_dict_class, to_str + ) + if preserve_tuples + else decode_list( + item, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) + ) elif isinstance(item, Mapping): - item = decode_dict(item, encoding, errors, keep, normalize, - preserve_dict_class, preserve_tuples, to_str) + item = decode_dict( + item, + encoding, + errors, + keep, + normalize, + preserve_dict_class, + preserve_tuples, + to_str, + ) else: try: item = _decode_func(item, encoding, errors, normalize) @@ -327,21 +457,35 @@ def decode_list(data, encoding=None, errors='strict', keep=False, return ret -def decode_tuple(data, encoding=None, errors='strict', keep=False, - normalize=False, preserve_dict_class=False, to_str=False): - ''' +def decode_tuple( + data, + encoding=None, + errors="strict", + keep=False, + normalize=False, + preserve_dict_class=False, + to_str=False, +): + """ Decode all string values to Unicode. Optionally use to_str=True to ensure strings are str types and not unicode on Python 2. - ''' + """ return tuple( - decode_list(data, encoding, errors, keep, normalize, - preserve_dict_class, True, to_str) + decode_list( + data, encoding, errors, keep, normalize, preserve_dict_class, True, to_str + ) ) -def encode(data, encoding=None, errors='strict', keep=False, - preserve_dict_class=False, preserve_tuples=False): - ''' +def encode( + data, + encoding=None, + errors="strict", + keep=False, + preserve_dict_class=False, + preserve_tuples=False, +): + """ Generic function which will encode whichever type is passed, if necessary If `strict` is True, and `keep` is False, and we fail to encode, a @@ -349,18 +493,23 @@ def encode(data, encoding=None, errors='strict', keep=False, original value to silently be returned in cases where encoding fails. This can be useful for cases where the data passed to this function is likely to contain binary blobs. - ''' + """ if isinstance(data, Mapping): - return encode_dict(data, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + return encode_dict( + data, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) if isinstance(data, list): - return encode_list(data, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + return encode_list( + data, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) if isinstance(data, tuple): - return encode_tuple(data, encoding, errors, keep, preserve_dict_class) \ - if preserve_tuples \ - else encode_list(data, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + return ( + encode_tuple(data, encoding, errors, keep, preserve_dict_class) + if preserve_tuples + else encode_list( + data, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) + ) try: return salt.utils.stringutils.to_bytes(data, encoding, errors) except TypeError: @@ -374,20 +523,29 @@ def encode(data, encoding=None, errors='strict', keep=False, return data -@jinja_filter('json_decode_dict') # Remove this for Aluminium -@jinja_filter('json_encode_dict') -def encode_dict(data, encoding=None, errors='strict', keep=False, - preserve_dict_class=False, preserve_tuples=False): - ''' +@jinja_filter("json_decode_dict") # Remove this for Aluminium +@jinja_filter("json_encode_dict") +def encode_dict( + data, + encoding=None, + errors="strict", + keep=False, + preserve_dict_class=False, + preserve_tuples=False, +): + """ Encode all string values to bytes - ''' + """ ret = data.__class__() if preserve_dict_class else {} for key, value in six.iteritems(data): if isinstance(key, tuple): - key = encode_tuple(key, encoding, errors, keep, preserve_dict_class) \ - if preserve_tuples \ - else encode_list(key, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + key = ( + encode_tuple(key, encoding, errors, keep, preserve_dict_class) + if preserve_tuples + else encode_list( + key, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) + ) else: try: key = salt.utils.stringutils.to_bytes(key, encoding, errors) @@ -401,16 +559,21 @@ def encode_dict(data, encoding=None, errors='strict', keep=False, raise if isinstance(value, list): - value = encode_list(value, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + value = encode_list( + value, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) elif isinstance(value, tuple): - value = encode_tuple(value, encoding, errors, keep, preserve_dict_class) \ - if preserve_tuples \ - else encode_list(value, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + value = ( + encode_tuple(value, encoding, errors, keep, preserve_dict_class) + if preserve_tuples + else encode_list( + value, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) + ) elif isinstance(value, Mapping): - value = encode_dict(value, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + value = encode_dict( + value, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) else: try: value = salt.utils.stringutils.to_bytes(value, encoding, errors) @@ -427,26 +590,37 @@ def encode_dict(data, encoding=None, errors='strict', keep=False, return ret -@jinja_filter('json_decode_list') # Remove this for Aluminium -@jinja_filter('json_encode_list') -def encode_list(data, encoding=None, errors='strict', keep=False, - preserve_dict_class=False, preserve_tuples=False): - ''' +@jinja_filter("json_decode_list") # Remove this for Aluminium +@jinja_filter("json_encode_list") +def encode_list( + data, + encoding=None, + errors="strict", + keep=False, + preserve_dict_class=False, + preserve_tuples=False, +): + """ Encode all string values to bytes - ''' + """ ret = [] for item in data: if isinstance(item, list): - item = encode_list(item, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + item = encode_list( + item, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) elif isinstance(item, tuple): - item = encode_tuple(item, encoding, errors, keep, preserve_dict_class) \ - if preserve_tuples \ - else encode_list(item, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + item = ( + encode_tuple(item, encoding, errors, keep, preserve_dict_class) + if preserve_tuples + else encode_list( + item, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) + ) elif isinstance(item, Mapping): - item = encode_dict(item, encoding, errors, keep, - preserve_dict_class, preserve_tuples) + item = encode_dict( + item, encoding, errors, keep, preserve_dict_class, preserve_tuples + ) else: try: item = salt.utils.stringutils.to_bytes(item, encoding, errors) @@ -463,42 +637,37 @@ def encode_list(data, encoding=None, errors='strict', keep=False, return ret -def encode_tuple(data, encoding=None, errors='strict', keep=False, - preserve_dict_class=False): - ''' +def encode_tuple( + data, encoding=None, errors="strict", keep=False, preserve_dict_class=False +): + """ Encode all string values to Unicode - ''' - return tuple( - encode_list(data, encoding, errors, keep, preserve_dict_class, True)) + """ + return tuple(encode_list(data, encoding, errors, keep, preserve_dict_class, True)) -@jinja_filter('exactly_n_true') +@jinja_filter("exactly_n_true") def exactly_n(iterable, amount=1): - ''' + """ Tests that exactly N items in an iterable are "truthy" (neither None, False, nor 0). - ''' + """ i = iter(iterable) return all(any(i) for j in range(amount)) and not any(i) -@jinja_filter('exactly_one_true') +@jinja_filter("exactly_one_true") def exactly_one(iterable): - ''' + """ Check if only one item is not None, False, or 0 in an iterable. - ''' + """ return exactly_n(iterable) -def filter_by(lookup_dict, - lookup, - traverse, - merge=None, - default='default', - base=None): - ''' +def filter_by(lookup_dict, lookup, traverse, merge=None, default="default", base=None): + """ Common code to filter data structures like grains and pillar - ''' + """ ret = None # Default value would be an empty list if lookup not found val = traverse_dict_and_list(traverse, lookup, []) @@ -507,10 +676,10 @@ def filter_by(lookup_dict, # lookup_dict keys for each in val if isinstance(val, list) else [val]: for key in lookup_dict: - test_key = key if isinstance(key, six.string_types) \ - else six.text_type(key) - test_each = each if isinstance(each, six.string_types) \ - else six.text_type(each) + test_key = key if isinstance(key, six.string_types) else six.text_type(key) + test_each = ( + each if isinstance(each, six.string_types) else six.text_type(each) + ) if fnmatch.fnmatchcase(test_each, test_key): ret = lookup_dict[key] break @@ -528,14 +697,13 @@ def filter_by(lookup_dict, elif isinstance(base_values, Mapping): if not isinstance(ret, Mapping): raise SaltException( - 'filter_by default and look-up values must both be ' - 'dictionaries.') + "filter_by default and look-up values must both be " "dictionaries." + ) ret = salt.utils.dictupdate.update(copy.deepcopy(base_values), ret) if merge: if not isinstance(merge, Mapping): - raise SaltException( - 'filter_by merge argument must be a dictionary.') + raise SaltException("filter_by merge argument must be a dictionary.") if ret is None: ret = merge @@ -546,12 +714,12 @@ def filter_by(lookup_dict, def traverse_dict(data, key, default=None, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Traverse a dict using a colon-delimited (or otherwise delimited, using the 'delimiter' param) target string. The target 'foo:bar:baz' will return data['foo']['bar']['baz'] if this value exists, and will otherwise return the dict in the default argument. - ''' + """ ptr = data try: for each in key.split(delimiter): @@ -562,9 +730,9 @@ def traverse_dict(data, key, default=None, delimiter=DEFAULT_TARGET_DELIM): return ptr -@jinja_filter('traverse') +@jinja_filter("traverse") def traverse_dict_and_list(data, key, default=None, delimiter=DEFAULT_TARGET_DELIM): - ''' + """ Traverse a dict or list using a colon-delimited (or otherwise delimited, using the 'delimiter' param) target string. The target 'foo:bar:0' will return data['foo']['bar'][0] if this value exists, and will otherwise @@ -573,7 +741,7 @@ def traverse_dict_and_list(data, key, default=None, delimiter=DEFAULT_TARGET_DEL The target 'foo:bar:0' will return data['foo']['bar'][0] if data like {'foo':{'bar':['baz']}} , if data like {'foo':{'bar':{'0':'baz'}}} then return data['foo']['bar']['0'] - ''' + """ ptr = data for each in key.split(delimiter): if isinstance(ptr, list): @@ -605,18 +773,17 @@ def traverse_dict_and_list(data, key, default=None, delimiter=DEFAULT_TARGET_DEL return ptr -def subdict_match(data, - expr, - delimiter=DEFAULT_TARGET_DELIM, - regex_match=False, - exact_match=False): - ''' +def subdict_match( + data, expr, delimiter=DEFAULT_TARGET_DELIM, regex_match=False, exact_match=False +): + """ Check for a match in a dictionary using a delimiter character to denote levels of subdicts, and also allowing the delimiter character to be matched. Thus, 'foo:bar:baz' will match data['foo'] == 'bar:baz' and data['foo']['bar'] == 'baz'. The latter would take priority over the former, as more deeply-nested matches are tried first. - ''' + """ + def _match(target, pattern, regex_match=False, exact_match=False): # The reason for using six.text_type first and _then_ using # to_unicode as a fallback is because we want to eventually have @@ -640,48 +807,54 @@ def subdict_match(data, try: return re.match(pattern, target) except Exception: # pylint: disable=broad-except - log.error('Invalid regex \'%s\' in match', pattern) + log.error("Invalid regex '%s' in match", pattern) return False else: - return target == pattern if exact_match \ - else fnmatch.fnmatch(target, pattern) + return ( + target == pattern if exact_match else fnmatch.fnmatch(target, pattern) + ) def _dict_match(target, pattern, regex_match=False, exact_match=False): ret = False - wildcard = pattern.startswith('*:') + wildcard = pattern.startswith("*:") if wildcard: pattern = pattern[2:] - if pattern == '*': + if pattern == "*": # We are just checking that the key exists ret = True if not ret and pattern in target: # We might want to search for a key ret = True - if not ret and subdict_match(target, - pattern, - regex_match=regex_match, - exact_match=exact_match): + if not ret and subdict_match( + target, pattern, regex_match=regex_match, exact_match=exact_match + ): ret = True if not ret and wildcard: for key in target: if isinstance(target[key], dict): - if _dict_match(target[key], - pattern, - regex_match=regex_match, - exact_match=exact_match): + if _dict_match( + target[key], + pattern, + regex_match=regex_match, + exact_match=exact_match, + ): return True elif isinstance(target[key], list): for item in target[key]: - if _match(item, - pattern, - regex_match=regex_match, - exact_match=exact_match): - return True - elif _match(target[key], + if _match( + item, pattern, regex_match=regex_match, - exact_match=exact_match): + exact_match=exact_match, + ): + return True + elif _match( + target[key], + pattern, + regex_match=regex_match, + exact_match=exact_match, + ): return True return ret @@ -695,7 +868,7 @@ def subdict_match(data, # want to use are 3, 2, and 1, in that order. for idx in range(num_splits - 1, 0, -1): key = delimiter.join(splits[:idx]) - if key == '*': + if key == "*": # We are matching on everything under the top level, so we need to # treat the match as the entire data being passed in matchstr = expr @@ -703,54 +876,55 @@ def subdict_match(data, else: matchstr = delimiter.join(splits[idx:]) match = traverse_dict_and_list(data, key, {}, delimiter=delimiter) - log.debug("Attempting to match '%s' in '%s' using delimiter '%s'", - matchstr, key, delimiter) + log.debug( + "Attempting to match '%s' in '%s' using delimiter '%s'", + matchstr, + key, + delimiter, + ) if match == {}: continue if isinstance(match, dict): - if _dict_match(match, - matchstr, - regex_match=regex_match, - exact_match=exact_match): + if _dict_match( + match, matchstr, regex_match=regex_match, exact_match=exact_match + ): return True continue if isinstance(match, (list, tuple)): # We are matching a single component to a single list member for member in match: if isinstance(member, dict): - if _dict_match(member, - matchstr, - regex_match=regex_match, - exact_match=exact_match): + if _dict_match( + member, + matchstr, + regex_match=regex_match, + exact_match=exact_match, + ): return True - if _match(member, - matchstr, - regex_match=regex_match, - exact_match=exact_match): + if _match( + member, matchstr, regex_match=regex_match, exact_match=exact_match + ): return True continue - if _match(match, - matchstr, - regex_match=regex_match, - exact_match=exact_match): + if _match(match, matchstr, regex_match=regex_match, exact_match=exact_match): return True return False -@jinja_filter('substring_in_list') +@jinja_filter("substring_in_list") def substr_in_list(string_to_search_for, list_to_search): - ''' + """ Return a boolean value that indicates whether or not a given string is present in any of the strings which comprise a list - ''' + """ return any(string_to_search_for in s for s in list_to_search) def is_dictlist(data): - ''' + """ Returns True if data is a list of one-element dicts (as found in many SLS schemas), otherwise returns False - ''' + """ if isinstance(data, list): for element in data: if isinstance(element, dict): @@ -762,15 +936,11 @@ def is_dictlist(data): return False -def repack_dictlist(data, - strict=False, - recurse=False, - key_cb=None, - val_cb=None): - ''' +def repack_dictlist(data, strict=False, recurse=False, key_cb=None, val_cb=None): + """ Takes a list of one-element dicts (as found in many SLS schemas) and repacks into a single dictionary. - ''' + """ if isinstance(data, six.string_types): try: data = salt.utils.yaml.safe_load(data) @@ -791,21 +961,21 @@ def repack_dictlist(data, if isinstance(element, dict): if len(element) != 1: log.error( - 'Invalid input for repack_dictlist: key/value pairs ' - 'must contain only one element (data passed: %s).', - element + "Invalid input for repack_dictlist: key/value pairs " + "must contain only one element (data passed: %s).", + element, ) return {} else: log.error( - 'Invalid input for repack_dictlist: element %s is ' - 'not a string/dict/numeric value', element + "Invalid input for repack_dictlist: element %s is " + "not a string/dict/numeric value", + element, ) return {} else: log.error( - 'Invalid input for repack_dictlist, data passed is not a list ' - '(%s)', data + "Invalid input for repack_dictlist, data passed is not a list " "(%s)", data ) return {} @@ -821,8 +991,8 @@ def repack_dictlist(data, ret[key_cb(key)] = repack_dictlist(val, recurse=recurse) elif strict: log.error( - 'Invalid input for repack_dictlist: nested dictlist ' - 'found, but recurse is set to False' + "Invalid input for repack_dictlist: nested dictlist " + "found, but recurse is set to False" ) return {} else: @@ -832,17 +1002,17 @@ def repack_dictlist(data, return ret -@jinja_filter('is_list') +@jinja_filter("is_list") def is_list(value): - ''' + """ Check if a variable is a list. - ''' + """ return isinstance(value, list) -@jinja_filter('is_iter') +@jinja_filter("is_iter") def is_iter(thing, ignore=six.string_types): - ''' + """ Test if an object is iterable, but not a string type. Test if an object is an iterator or is iterable itself. By default this @@ -853,7 +1023,7 @@ def is_iter(thing, ignore=six.string_types): dictionaries or named tuples. Based on https://bitbucket.org/petershinners/yter - ''' + """ if ignore and isinstance(thing, ignore): return False try: @@ -863,9 +1033,9 @@ def is_iter(thing, ignore=six.string_types): return False -@jinja_filter('sorted_ignorecase') +@jinja_filter("sorted_ignorecase") def sorted_ignorecase(to_sort): - ''' + """ Sort a list of strings ignoring case. >>> L = ['foo', 'Foo', 'bar', 'Bar'] @@ -874,19 +1044,19 @@ def sorted_ignorecase(to_sort): >>> sorted(L, key=lambda x: x.lower()) ['bar', 'Bar', 'foo', 'Foo'] >>> - ''' + """ return sorted(to_sort, key=lambda x: x.lower()) def is_true(value=None): - ''' + """ Returns a boolean value representing the "truth" of the value passed. The rules for what is a "True" value are: 1. Integer/float values greater than 0 2. The string values "True" and "true" 3. Any object for which bool(obj) returns True - ''' + """ # First, try int/float conversion try: value = int(value) @@ -901,23 +1071,23 @@ def is_true(value=None): if isinstance(value, (six.integer_types, float)): return value > 0 if isinstance(value, six.string_types): - return six.text_type(value).lower() == 'true' + return six.text_type(value).lower() == "true" return bool(value) -@jinja_filter('mysql_to_dict') +@jinja_filter("mysql_to_dict") def mysql_to_dict(data, key): - ''' + """ Convert MySQL-style output to a python dictionary - ''' + """ ret = {} - headers = [''] + headers = [""] for line in data: if not line: continue - if line.startswith('+'): + if line.startswith("+"): continue - comps = line.split('|') + comps = line.split("|") for comp in range(len(comps)): comps[comp] = comps[comp].strip() if len(headers) > 1: @@ -934,10 +1104,10 @@ def mysql_to_dict(data, key): def simple_types_filter(data): - ''' + """ Convert the data list, dictionary into simple types, i.e., int, float, string, bool, etc. - ''' + """ if data is None: return data @@ -971,10 +1141,10 @@ def simple_types_filter(data): def stringify(data): - ''' + """ Given an iterable, returns its items as a list, with any non-string items converted to unicode strings. - ''' + """ ret = [] for item in data: if six.PY2 and isinstance(item, str): @@ -985,9 +1155,9 @@ def stringify(data): return ret -@jinja_filter('json_query') +@jinja_filter("json_query") def json_query(data, expr): - ''' + """ Query data using JMESPath language (http://jmespath.org). Requires the https://github.com/jmespath/jmespath.py library. @@ -1009,16 +1179,16 @@ def json_query(data, expr): .. code-block:: text [80, 25, 22] - ''' + """ if jmespath is None: - err = 'json_query requires jmespath module installed' + err = "json_query requires jmespath module installed" log.error(err) raise RuntimeError(err) return jmespath.search(expr, data) def _is_not_considered_falsey(value, ignore_types=()): - ''' + """ Helper function for filter_falsey to determine if something is not to be considered falsey. @@ -1026,12 +1196,12 @@ def _is_not_considered_falsey(value, ignore_types=()): :param list ignore_types: The types to ignore when considering the value. :return bool - ''' + """ return isinstance(value, bool) or type(value) in ignore_types or value def filter_falsey(data, recurse_depth=None, ignore_types=()): - ''' + """ Helper function to remove items from an iterable with falsey value. Removes ``None``, ``{}`` and ``[]``, 0, '' (but does not remove ``False``). Recurses into sub-iterables if ``recurse`` is set to ``True``. @@ -1045,37 +1215,42 @@ def filter_falsey(data, recurse_depth=None, ignore_types=()): :return type(data) .. versionadded:: 3000 - ''' + """ filter_element = ( - functools.partial(filter_falsey, - recurse_depth=recurse_depth-1, - ignore_types=ignore_types) - if recurse_depth else lambda x: x + functools.partial( + filter_falsey, recurse_depth=recurse_depth - 1, ignore_types=ignore_types + ) + if recurse_depth + else lambda x: x ) if isinstance(data, dict): - processed_elements = [(key, filter_element(value)) for key, value in six.iteritems(data)] - return type(data)([ - (key, value) - for key, value in processed_elements - if _is_not_considered_falsey(value, ignore_types=ignore_types) - ]) + processed_elements = [ + (key, filter_element(value)) for key, value in six.iteritems(data) + ] + return type(data)( + [ + (key, value) + for key, value in processed_elements + if _is_not_considered_falsey(value, ignore_types=ignore_types) + ] + ) if is_iter(data): processed_elements = (filter_element(value) for value in data) - return type(data)([ - value for value in processed_elements - if _is_not_considered_falsey(value, ignore_types=ignore_types) - ]) + return type(data)( + [ + value + for value in processed_elements + if _is_not_considered_falsey(value, ignore_types=ignore_types) + ] + ) return data def recursive_diff( - old, - new, - ignore_keys=None, - ignore_order=False, - ignore_missing_keys=False): - ''' + old, new, ignore_keys=None, ignore_order=False, ignore_missing_keys=False +): + """ Performs a recursive diff on mappings and/or iterables and returns the result in a {'old': values, 'new': values}-style. Compares dicts and sets unordered (obviously), OrderedDicts and Lists ordered @@ -1090,12 +1265,16 @@ def recursive_diff( but missing in ``new``. Only works for regular dicts. :return dict: Returns dict with keys 'old' and 'new' containing the differences. - ''' + """ ignore_keys = ignore_keys or [] res = {} ret_old = copy.deepcopy(old) ret_new = copy.deepcopy(new) - if isinstance(old, OrderedDict) and isinstance(new, OrderedDict) and not ignore_order: + if ( + isinstance(old, OrderedDict) + and isinstance(new, OrderedDict) + and not ignore_order + ): append_old, append_new = [], [] if len(old) != len(new): min_length = min(len(old), len(new)) @@ -1114,13 +1293,14 @@ def recursive_diff( new[key_new], ignore_keys=ignore_keys, ignore_order=ignore_order, - ignore_missing_keys=ignore_missing_keys) + ignore_missing_keys=ignore_missing_keys, + ) if not res: # Equal del ret_old[key_old] del ret_new[key_new] else: - ret_old[key_old] = res['old'] - ret_new[key_new] = res['new'] + ret_old[key_old] = res["old"] + ret_new[key_new] = res["new"] else: if key_old in ignore_keys: del ret_old[key_old] @@ -1131,7 +1311,7 @@ def recursive_diff( ret_old[item] = old[item] for item in append_new: ret_new[item] = new[item] - ret = {'old': ret_old, 'new': ret_new} if ret_old or ret_new else {} + ret = {"old": ret_old, "new": ret_new} if ret_old or ret_new else {} elif isinstance(old, Mapping) and isinstance(new, Mapping): # Compare unordered for key in set(list(old) + list(new)): @@ -1146,16 +1326,17 @@ def recursive_diff( new[key], ignore_keys=ignore_keys, ignore_order=ignore_order, - ignore_missing_keys=ignore_missing_keys) + ignore_missing_keys=ignore_missing_keys, + ) if not res: # Equal del ret_old[key] del ret_new[key] else: - ret_old[key] = res['old'] - ret_new[key] = res['new'] - ret = {'old': ret_old, 'new': ret_new} if ret_old or ret_new else {} + ret_old[key] = res["old"] + ret_new[key] = res["new"] + ret = {"old": ret_old, "new": ret_new} if ret_old or ret_new else {} elif isinstance(old, set) and isinstance(new, set): - ret = {'old': old - new, 'new': new - old} if old - new or new - old else {} + ret = {"old": old - new, "new": new - old} if old - new or new - old else {} elif is_iter(old) and is_iter(new): # Create a list so we can edit on an index-basis. list_old = list(ret_old) @@ -1168,7 +1349,8 @@ def recursive_diff( item_new, ignore_keys=ignore_keys, ignore_order=ignore_order, - ignore_missing_keys=ignore_missing_keys) + ignore_missing_keys=ignore_missing_keys, + ) if not res: list_old.remove(item_old) list_new.remove(item_new) @@ -1181,19 +1363,24 @@ def recursive_diff( iter_new, ignore_keys=ignore_keys, ignore_order=ignore_order, - ignore_missing_keys=ignore_missing_keys) + ignore_missing_keys=ignore_missing_keys, + ) if not res: # Equal remove_indices.append(index) else: - list_old[index] = res['old'] - list_new[index] = res['new'] + list_old[index] = res["old"] + list_new[index] = res["new"] for index in reversed(remove_indices): list_old.pop(index) list_new.pop(index) # Instantiate a new whatever-it-was using the list as iterable source. # This may not be the most optimized in way of speed and memory usage, # but it will work for all iterable types. - ret = {'old': type(old)(list_old), 'new': type(new)(list_new)} if list_old or list_new else {} + ret = ( + {"old": type(old)(list_old), "new": type(new)(list_new)} + if list_old or list_new + else {} + ) else: - ret = {} if old == new else {'old': ret_old, 'new': ret_new} + ret = {} if old == new else {"old": ret_old, "new": ret_new} return ret diff --git a/salt/utils/dateutils.py b/salt/utils/dateutils.py index d871d74952a..60fd0ab534b 100644 --- a/salt/utils/dateutils.py +++ b/salt/utils/dateutils.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Convenience functions for dealing with datetime classes -''' +""" from __future__ import absolute_import, division, print_function, unicode_literals @@ -10,25 +10,26 @@ import datetime # Import Salt libs import salt.utils.stringutils -from salt.utils.decorators.jinja import jinja_filter # Import 3rd-party libs from salt.ext import six +from salt.utils.decorators.jinja import jinja_filter try: import timelib + HAS_TIMELIB = True except ImportError: HAS_TIMELIB = False def date_cast(date): - ''' + """ Casts any object into a datetime.datetime object date any datetime, time string representation... - ''' + """ if date is None: return datetime.datetime.now() elif isinstance(date, datetime.datetime): @@ -40,8 +41,7 @@ def date_cast(date): try: if HAS_TIMELIB: # py3: yes, timelib.strtodatetime wants bytes, not str :/ - return timelib.strtodatetime( - salt.utils.stringutils.to_bytes(date)) + return timelib.strtodatetime(salt.utils.stringutils.to_bytes(date)) except ValueError: pass @@ -54,16 +54,17 @@ def date_cast(date): return datetime.datetime.fromtimestamp(date) except Exception: # pylint: disable=broad-except if HAS_TIMELIB: - raise ValueError('Unable to parse {0}'.format(date)) + raise ValueError("Unable to parse {0}".format(date)) raise RuntimeError( - 'Unable to parse {0}. Consider installing timelib'.format(date)) + "Unable to parse {0}. Consider installing timelib".format(date) + ) -@jinja_filter('date_format') -@jinja_filter('strftime') +@jinja_filter("date_format") +@jinja_filter("strftime") def strftime(date=None, format="%Y-%m-%d"): - ''' + """ Converts date into a time-based string date @@ -85,14 +86,14 @@ def strftime(date=None, format="%Y-%m-%d"): >>> src = '1040814000' >>> strftime(src) '2002-12-25' - ''' + """ return date_cast(date).strftime(format) def total_seconds(td): - ''' + """ Takes a timedelta and returns the total number of seconds represented by the object. Wrapper for the total_seconds() method which does not exist in versions of Python < 2.7. - ''' - return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 + """ + return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10 ** 6) / 10 ** 6 diff --git a/salt/utils/debug.py b/salt/utils/debug.py index 65cd4288117..d9b574d762c 100644 --- a/salt/utils/debug.py +++ b/salt/utils/debug.py @@ -1,17 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Print a stacktrace when sent a SIGUSR1 for debugging -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import inspect + # Import python libs import os -import sys -import time import signal +import sys import tempfile +import time import traceback -import inspect # Import salt libs import salt.utils.files @@ -19,88 +20,88 @@ import salt.utils.stringutils def _makepretty(printout, stack): - ''' + """ Pretty print the stack trace and environment information for debugging those hard to reproduce user problems. :) - ''' - printout.write('======== Salt Debug Stack Trace =========\n') + """ + printout.write("======== Salt Debug Stack Trace =========\n") traceback.print_stack(stack, file=printout) - printout.write('=========================================\n') + printout.write("=========================================\n") def _handle_sigusr1(sig, stack): - ''' + """ Signal handler for SIGUSR1, only available on Unix-like systems - ''' + """ # When running in the foreground, do the right thing # and spit out the debug info straight to the console if sys.stderr.isatty(): output = sys.stderr _makepretty(output, stack) else: - filename = 'salt-debug-{0}.log'.format(int(time.time())) + filename = "salt-debug-{0}.log".format(int(time.time())) destfile = os.path.join(tempfile.gettempdir(), filename) - with salt.utils.files.fopen(destfile, 'w') as output: + with salt.utils.files.fopen(destfile, "w") as output: _makepretty(output, stack) def _handle_sigusr2(sig, stack): - ''' + """ Signal handler for SIGUSR2, only available on Unix-like systems - ''' + """ try: import yappi except ImportError: return if yappi.is_running(): yappi.stop() - filename = 'callgrind.salt-{0}-{1}'.format(int(time.time()), os.getpid()) + filename = "callgrind.salt-{0}-{1}".format(int(time.time()), os.getpid()) destfile = os.path.join(tempfile.gettempdir(), filename) - yappi.get_func_stats().save(destfile, type='CALLGRIND') + yappi.get_func_stats().save(destfile, type="CALLGRIND") if sys.stderr.isatty(): - sys.stderr.write('Saved profiling data to: {0}\n'.format(destfile)) + sys.stderr.write("Saved profiling data to: {0}\n".format(destfile)) yappi.clear_stats() else: if sys.stderr.isatty(): - sys.stderr.write('Profiling started\n') + sys.stderr.write("Profiling started\n") yappi.start() def enable_sig_handler(signal_name, handler): - ''' + """ Add signal handler for signal name if it exists on given platform - ''' + """ if hasattr(signal, signal_name): signal.signal(getattr(signal, signal_name), handler) def enable_sigusr1_handler(): - ''' + """ Pretty print a stack trace to the console or a debug log under /tmp when any of the salt daemons such as salt-master are sent a SIGUSR1 - ''' - enable_sig_handler('SIGUSR1', _handle_sigusr1) + """ + enable_sig_handler("SIGUSR1", _handle_sigusr1) # Also canonical BSD-way of printing progress is SIGINFO # which on BSD-derivatives can be sent via Ctrl+T - enable_sig_handler('SIGINFO', _handle_sigusr1) + enable_sig_handler("SIGINFO", _handle_sigusr1) def enable_sigusr2_handler(): - ''' + """ Toggle YAPPI profiler - ''' - enable_sig_handler('SIGUSR2', _handle_sigusr2) + """ + enable_sig_handler("SIGUSR2", _handle_sigusr2) def inspect_stack(): - ''' + """ Return a string of which function we are currently in. - ''' - return {'co_name': inspect.stack()[1][3]} + """ + return {"co_name": inspect.stack()[1][3]} def caller_name(skip=2, include_lineno=False): - ''' + """ Get a name of a caller in the format module.class.method `skip` specifies how many levels of stack to skip while getting caller @@ -109,11 +110,11 @@ def caller_name(skip=2, include_lineno=False): An empty string is returned if skipped levels exceed stack height Source: https://gist.github.com/techtonik/2151727 - ''' + """ stack = inspect.stack() start = 0 + skip if len(stack) < start + 1: - return '' + return "" parentframe = stack[start][0] name = [] @@ -128,16 +129,16 @@ def caller_name(skip=2, include_lineno=False): if module: name.append(module.__name__) # detect classname - if 'self' in parentframe.f_locals: + if "self" in parentframe.f_locals: # I don't know any way to detect call from the object method # XXX: there seems to be no way to detect static method call - it will # be just a function call - name.append(parentframe.f_locals['self'].__class__.__name__) + name.append(parentframe.f_locals["self"].__class__.__name__) codename = parentframe.f_code.co_name - if codename != '<module>': # top level usually - name.append(codename) # function or a method + if codename != "<module>": # top level usually + name.append(codename) # function or a method del parentframe - fullname = '.'.join(name) + fullname = ".".join(name) if include_lineno and lineno: - fullname += ':{}'.format(lineno) + fullname += ":{}".format(lineno) return fullname diff --git a/salt/utils/decorators/__init__.py b/salt/utils/decorators/__init__.py index 45d69072c73..04f055a6e8f 100644 --- a/salt/utils/decorators/__init__.py +++ b/salt/utils/decorators/__init__.py @@ -1,47 +1,49 @@ # -*- coding: utf-8 -*- -''' +""" Helpful decorators for module writing -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import inspect import logging import subprocess import sys import time -from functools import wraps from collections import defaultdict +from functools import wraps # Import salt libs import salt.utils.args import salt.utils.data import salt.utils.versions from salt.exceptions import CommandExecutionError, SaltConfigurationError -from salt.log import LOG_LEVELS # Import 3rd-party libs from salt.ext import six +from salt.log import LOG_LEVELS IS_WINDOWS = False -if getattr(sys, 'getwindowsversion', False): +if getattr(sys, "getwindowsversion", False): IS_WINDOWS = True log = logging.getLogger(__name__) class Depends(object): - ''' + """ This decorator will check the module when it is loaded and check that the dependencies passed in are in the globals of the module. If not, it will cause the function to be unloaded (or replaced). - ''' + """ + # kind -> Dependency -> list of things that depend on it dependency_dict = defaultdict(lambda: defaultdict(dict)) def __init__(self, *dependencies, **kwargs): - ''' + """ The decorator is instantiated with a list of dependencies (string of global name) @@ -81,20 +83,21 @@ class Depends(object): not supported since this uses shell=False when calling subprocess.Popen(). - ''' + """ log.trace( - 'Depends decorator instantiated with dep list of %s and kwargs %s', - dependencies, kwargs + "Depends decorator instantiated with dep list of %s and kwargs %s", + dependencies, + kwargs, ) self.dependencies = dependencies self.params = kwargs def __call__(self, function): - ''' + """ The decorator is "__call__"d with the function, we take that function and determine which module and function name it is to store in the class wide dependency_dict - ''' + """ try: # This inspect call may fail under certain conditions in the loader. # Possibly related to a Python bug here: @@ -102,73 +105,76 @@ class Depends(object): frame = inspect.stack()[1][0] # due to missing *.py files under esky we cannot use inspect.getmodule # module name is something like salt.loaded.int.modules.test - _, kind, mod_name = frame.f_globals['__name__'].rsplit('.', 2) + _, kind, mod_name = frame.f_globals["__name__"].rsplit(".", 2) fun_name = function.__name__ for dep in self.dependencies: - self.dependency_dict[kind][dep][(mod_name, fun_name)] = (frame, self.params) + self.dependency_dict[kind][dep][(mod_name, fun_name)] = ( + frame, + self.params, + ) except Exception as exc: # pylint: disable=broad-except log.exception( - 'Exception encountered when attempting to inspect frame in ' - 'dependency decorator' + "Exception encountered when attempting to inspect frame in " + "dependency decorator" ) return function @staticmethod def run_command(dependency, mod_name, func_name): - full_name = '{0}.{1}'.format(mod_name, func_name) - log.trace('Running \'%s\' for \'%s\'', dependency, full_name) + full_name = "{0}.{1}".format(mod_name, func_name) + log.trace("Running '%s' for '%s'", dependency, full_name) if IS_WINDOWS: args = salt.utils.args.shlex_split(dependency, posix=False) else: args = salt.utils.args.shlex_split(dependency) - log.trace('Command after shlex_split: %s', args) - proc = subprocess.Popen(args, - shell=False, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + log.trace("Command after shlex_split: %s", args) + proc = subprocess.Popen( + args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) output = proc.communicate()[0] retcode = proc.returncode - log.trace('Output from \'%s\': %s', dependency, output) - log.trace('Retcode from \'%s\': %d', dependency, retcode) + log.trace("Output from '%s': %s", dependency, output) + log.trace("Retcode from '%s': %d", dependency, retcode) return retcode @classmethod def enforce_dependencies(cls, functions, kind, tgt_mod): - ''' + """ This is a class global method to enforce the dependencies that you currently know about. It will modify the "functions" dict and remove/replace modules that are missing dependencies. - ''' + """ for dependency, dependent_dict in six.iteritems(cls.dependency_dict[kind]): for (mod_name, func_name), (frame, params) in six.iteritems(dependent_dict): if mod_name != tgt_mod: continue # Imports from local context take presedence over those from the global context. - dep_found = frame.f_locals.get(dependency) or frame.f_globals.get(dependency) + dep_found = frame.f_locals.get(dependency) or frame.f_globals.get( + dependency + ) # Default to version ``None`` if not found, which will be less than anything. - dep_version = getattr(dep_found, '__version__', None) - if 'retcode' in params or 'nonzero_retcode' in params: + dep_version = getattr(dep_found, "__version__", None) + if "retcode" in params or "nonzero_retcode" in params: try: retcode = cls.run_command(dependency, mod_name, func_name) except OSError as exc: if exc.errno == errno.ENOENT: log.trace( - 'Failed to run command %s, %s not found', - dependency, exc.filename + "Failed to run command %s, %s not found", + dependency, + exc.filename, ) else: - log.trace( - 'Failed to run command \'%s\': %s', dependency, exc - ) + log.trace("Failed to run command '%s': %s", dependency, exc) retcode = -1 - if 'retcode' in params: - if params['retcode'] == retcode: + if "retcode" in params: + if params["retcode"] == retcode: continue - elif 'nonzero_retcode' in params: - if params['nonzero_retcode']: + elif "nonzero_retcode" in params: + if params["nonzero_retcode"]: if retcode != 0: continue else: @@ -178,60 +184,69 @@ class Depends(object): # check if dependency is loaded elif dependency is True: log.trace( - 'Dependency for %s.%s exists, not unloading', - mod_name, func_name + "Dependency for %s.%s exists, not unloading", + mod_name, + func_name, ) continue # check if you have the dependency elif dep_found: - if 'version' in params: - if salt.utils.versions.version_cmp( - dep_version, - params['version'] - ) >= 0: + if "version" in params: + if ( + salt.utils.versions.version_cmp( + dep_version, params["version"] + ) + >= 0 + ): log.trace( - 'Dependency (%s) already loaded inside %s with ' - 'version (%s), required (%s), skipping', - dependency, mod_name, dep_version, params['version'] + "Dependency (%s) already loaded inside %s with " + "version (%s), required (%s), skipping", + dependency, + mod_name, + dep_version, + params["version"], ) continue else: log.trace( - 'Dependency (%s) already loaded inside %s, skipping', - dependency, mod_name + "Dependency (%s) already loaded inside %s, skipping", + dependency, + mod_name, ) continue log.trace( - 'Unloading %s.%s because dependency (%s%s) is not met', + "Unloading %s.%s because dependency (%s%s) is not met", mod_name, func_name, dependency, - ' version {}'.format(params['version']) if 'version' in params else '', + " version {}".format(params["version"]) + if "version" in params + else "", ) # if not, unload the function if frame: try: - func_name = frame.f_globals['__func_alias__'][func_name] + func_name = frame.f_globals["__func_alias__"][func_name] except (AttributeError, KeyError): pass - mod_key = '{0}.{1}'.format(mod_name, func_name) + mod_key = "{0}.{1}".format(mod_name, func_name) # if we don't have this module loaded, skip it! if mod_key not in functions: continue try: - fallback_function = params.get('fallback_function') + fallback_function = params.get("fallback_function") if fallback_function is not None: functions[mod_key] = fallback_function else: del functions[mod_key] except AttributeError: # we already did??? - log.trace('%s already removed, skipping', mod_key) + log.trace("%s already removed, skipping", mod_key) continue @@ -239,35 +254,37 @@ depends = Depends def timing(function): - ''' + """ Decorator wrapper to log execution time, for profiling purposes - ''' + """ + @wraps(function) def wrapped(*args, **kwargs): start_time = time.time() ret = function(*args, **salt.utils.args.clean_kwargs(**kwargs)) end_time = time.time() - if function.__module__.startswith('salt.loaded.int.'): + if function.__module__.startswith("salt.loaded.int."): mod_name = function.__module__[16:] else: mod_name = function.__module__ - fstr = 'Function %s.%s took %.{0}f seconds to execute'.format( + fstr = "Function %s.%s took %.{0}f seconds to execute".format( sys.float_info.dig ) log.profile(fstr, mod_name, function.__name__, end_time - start_time) return ret + return wrapped def memoize(func): - ''' + """ Memoize aka cache the return output of a function given a specific set of arguments .. versionedited:: 2016.3.4 Added **kwargs support. - ''' + """ cache = {} @wraps(func) @@ -279,7 +296,9 @@ def memoize(func): else: str_args.append(arg) - args_ = ','.join(list(str_args) + ['{0}={1}'.format(k, kwargs[k]) for k in sorted(kwargs)]) + args_ = ",".join( + list(str_args) + ["{0}={1}".format(k, kwargs[k]) for k in sorted(kwargs)] + ) if args_ not in cache: cache[args_] = func(*args, **kwargs) return cache[args_] @@ -288,23 +307,24 @@ def memoize(func): class _DeprecationDecorator(object): - ''' + """ Base mix-in class for the deprecation decorator. Takes care of a common functionality, used in its derivatives. - ''' + """ OPT_IN = 1 OPT_OUT = 2 def __init__(self, globals, version): - ''' + """ Constructor. :param globals: Module globals. Important for finding out replacement functions :param version: Expiration version :return: - ''' + """ from salt.version import SaltStackVersion, __saltstack_version__ + self._globals = globals self._exp_version_name = version self._exp_version = SaltStackVersion.from_name(self._exp_version_name) @@ -314,23 +334,23 @@ class _DeprecationDecorator(object): self._orig_f_name = None def _get_args(self, kwargs): - ''' + """ Discard all keywords which aren't function-specific from the kwargs. :param kwargs: :return: - ''' + """ _args = list() _kwargs = salt.utils.args.clean_kwargs(**kwargs) return _args, _kwargs def _call_function(self, kwargs): - ''' + """ Call target function that has been decorated. :return: - ''' + """ if self._raise_later: raise self._raise_later # pylint: disable=E0702 @@ -339,35 +359,41 @@ class _DeprecationDecorator(object): try: return self._function(*args, **kwargs) except TypeError as error: - error = six.text_type(error).replace(self._function, self._orig_f_name) # Hide hidden functions + error = six.text_type(error).replace( + self._function, self._orig_f_name + ) # Hide hidden functions log.error( 'Function "%s" was not properly called: %s', - self._orig_f_name, error + self._orig_f_name, + error, ) return self._function.__doc__ except Exception as error: # pylint: disable=broad-except log.error( 'Unhandled exception occurred in function "%s: %s', - self._function.__name__, error + self._function.__name__, + error, ) six.reraise(*sys.exc_info()) else: - raise CommandExecutionError("Function is deprecated, but the successor function was not found.") + raise CommandExecutionError( + "Function is deprecated, but the successor function was not found." + ) def __call__(self, function): - ''' + """ Callable method of the decorator object when the decorated function is gets called. :param function: :return: - ''' + """ self._function = function self._orig_f_name = self._function.__name__ class _IsDeprecated(_DeprecationDecorator): - ''' + """ This decorator should be used only with the deprecated functions to mark them as deprecated and alter its behavior a corresponding way. The usage is only suitable if deprecation process is renaming @@ -410,53 +436,70 @@ class _IsDeprecated(_DeprecationDecorator): def baz(): pass - ''' + """ def __init__(self, globals, version, with_successor=None): - ''' + """ Constructor of the decorator 'is_deprecated'. :param globals: Module globals :param version: Version to be deprecated :param with_successor: Successor function (optional) :return: - ''' + """ _DeprecationDecorator.__init__(self, globals, version) self._successor = with_successor def __call__(self, function): - ''' + """ Callable method of the decorator object when the decorated function is gets called. :param function: :return: - ''' + """ _DeprecationDecorator.__call__(self, function) @wraps(function) def _decorate(*args, **kwargs): - ''' + """ Decorator function. :param args: :param kwargs: :return: - ''' + """ if self._curr_version < self._exp_version: - msg = ['The function "{f_name}" is deprecated and will ' - 'expire in version "{version_name}".'.format(f_name=self._function.__name__, - version_name=self._exp_version_name)] + msg = [ + 'The function "{f_name}" is deprecated and will ' + 'expire in version "{version_name}".'.format( + f_name=self._function.__name__, + version_name=self._exp_version_name, + ) + ] if self._successor: - msg.append('Use successor "{successor}" instead.'.format(successor=self._successor)) - log.warning(' '.join(msg)) + msg.append( + 'Use successor "{successor}" instead.'.format( + successor=self._successor + ) + ) + log.warning(" ".join(msg)) else: - msg = ['The lifetime of the function "{f_name}" expired.'.format(f_name=self._function.__name__)] + msg = [ + 'The lifetime of the function "{f_name}" expired.'.format( + f_name=self._function.__name__ + ) + ] if self._successor: - msg.append('Please use its successor "{successor}" instead.'.format(successor=self._successor)) - log.warning(' '.join(msg)) - raise CommandExecutionError(' '.join(msg)) + msg.append( + 'Please use its successor "{successor}" instead.'.format( + successor=self._successor + ) + ) + log.warning(" ".join(msg)) + raise CommandExecutionError(" ".join(msg)) return self._call_function(kwargs) + return _decorate @@ -464,7 +507,7 @@ is_deprecated = _IsDeprecated class _WithDeprecated(_DeprecationDecorator): - ''' + """ This decorator should be used with the successor functions to mark them as a new and alter its behavior in a corresponding way. It is used alone if a function content or function signature @@ -531,13 +574,16 @@ class _WithDeprecated(_DeprecationDecorator): def an_old_function(): "This is a deprecated function" - ''' - MODULE_NAME = '__virtualname__' - CFG_USE_DEPRECATED = 'use_deprecated' - CFG_USE_SUPERSEDED = 'use_superseded' + """ - def __init__(self, globals, version, with_name=None, policy=_DeprecationDecorator.OPT_OUT): - ''' + MODULE_NAME = "__virtualname__" + CFG_USE_DEPRECATED = "use_deprecated" + CFG_USE_SUPERSEDED = "use_superseded" + + def __init__( + self, globals, version, with_name=None, policy=_DeprecationDecorator.OPT_OUT + ): + """ Constructor of the decorator 'with_deprecated' :param globals: @@ -545,108 +591,158 @@ class _WithDeprecated(_DeprecationDecorator): :param with_name: :param policy: :return: - ''' + """ _DeprecationDecorator.__init__(self, globals, version) self._with_name = with_name self._policy = policy def _set_function(self, function): - ''' + """ Based on the configuration, set to execute an old or a new function. :return: - ''' + """ full_name = "{m_name}.{f_name}".format( - m_name=self._globals.get(self.MODULE_NAME, '') or self._globals['__name__'].split('.')[-1], - f_name=function.__name__) + m_name=self._globals.get(self.MODULE_NAME, "") + or self._globals["__name__"].split(".")[-1], + f_name=function.__name__, + ) if full_name.startswith("."): - self._raise_later = CommandExecutionError('Module not found for function "{f_name}"'.format( - f_name=function.__name__)) + self._raise_later = CommandExecutionError( + 'Module not found for function "{f_name}"'.format( + f_name=function.__name__ + ) + ) - opts = self._globals.get('__opts__', '{}') - pillar = self._globals.get('__pillar__', '{}') + opts = self._globals.get("__opts__", "{}") + pillar = self._globals.get("__pillar__", "{}") - use_deprecated = (full_name in opts.get(self.CFG_USE_DEPRECATED, list()) or - full_name in pillar.get(self.CFG_USE_DEPRECATED, list())) + use_deprecated = full_name in opts.get( + self.CFG_USE_DEPRECATED, list() + ) or full_name in pillar.get(self.CFG_USE_DEPRECATED, list()) - use_superseded = (full_name in opts.get(self.CFG_USE_SUPERSEDED, list()) or - full_name in pillar.get(self.CFG_USE_SUPERSEDED, list())) + use_superseded = full_name in opts.get( + self.CFG_USE_SUPERSEDED, list() + ) or full_name in pillar.get(self.CFG_USE_SUPERSEDED, list()) if use_deprecated and use_superseded: - raise SaltConfigurationError("Function '{0}' is mentioned both in deprecated " - "and superseded sections. Please remove any of that.".format(full_name)) - old_function = self._globals.get(self._with_name or "_{0}".format(function.__name__)) + raise SaltConfigurationError( + "Function '{0}' is mentioned both in deprecated " + "and superseded sections. Please remove any of that.".format(full_name) + ) + old_function = self._globals.get( + self._with_name or "_{0}".format(function.__name__) + ) if self._policy == self.OPT_IN: self._function = function if use_superseded else old_function else: self._function = old_function if use_deprecated else function def _is_used_deprecated(self): - ''' + """ Returns True, if a component configuration explicitly is asking to use an old version of the deprecated function. :return: - ''' + """ func_path = "{m_name}.{f_name}".format( - m_name=self._globals.get(self.MODULE_NAME, '') or self._globals['__name__'].split('.')[-1], - f_name=self._orig_f_name) + m_name=self._globals.get(self.MODULE_NAME, "") + or self._globals["__name__"].split(".")[-1], + f_name=self._orig_f_name, + ) - return func_path in self._globals.get('__opts__').get( - self.CFG_USE_DEPRECATED, list()) or func_path in self._globals.get('__pillar__').get( - self.CFG_USE_DEPRECATED, list()) or (self._policy == self.OPT_IN - and not (func_path in self._globals.get('__opts__', {}).get( - self.CFG_USE_SUPERSEDED, list())) - and not (func_path in self._globals.get('__pillar__', {}).get( - self.CFG_USE_SUPERSEDED, list()))), func_path + return ( + func_path + in self._globals.get("__opts__").get(self.CFG_USE_DEPRECATED, list()) + or func_path + in self._globals.get("__pillar__").get(self.CFG_USE_DEPRECATED, list()) + or ( + self._policy == self.OPT_IN + and not ( + func_path + in self._globals.get("__opts__", {}).get( + self.CFG_USE_SUPERSEDED, list() + ) + ) + and not ( + func_path + in self._globals.get("__pillar__", {}).get( + self.CFG_USE_SUPERSEDED, list() + ) + ) + ), + func_path, + ) def __call__(self, function): - ''' + """ Callable method of the decorator object when the decorated function is gets called. :param function: :return: - ''' + """ _DeprecationDecorator.__call__(self, function) @wraps(function) def _decorate(*args, **kwargs): - ''' + """ Decorator function. :param args: :param kwargs: :return: - ''' + """ self._set_function(function) is_deprecated, func_path = self._is_used_deprecated() if is_deprecated: if self._curr_version < self._exp_version: msg = list() if self._with_name: - msg.append('The function "{f_name}" is deprecated and will ' - 'expire in version "{version_name}".'.format( - f_name=self._with_name.startswith("_") and self._orig_f_name or self._with_name, - version_name=self._exp_version_name)) - msg.append('Use its successor "{successor}" instead.'.format(successor=self._orig_f_name)) + msg.append( + 'The function "{f_name}" is deprecated and will ' + 'expire in version "{version_name}".'.format( + f_name=self._with_name.startswith("_") + and self._orig_f_name + or self._with_name, + version_name=self._exp_version_name, + ) + ) + msg.append( + 'Use its successor "{successor}" instead.'.format( + successor=self._orig_f_name + ) + ) else: - msg.append('The function "{f_name}" is using its deprecated version and will ' - 'expire in version "{version_name}".'.format(f_name=func_path, - version_name=self._exp_version_name)) - log.warning(' '.join(msg)) + msg.append( + 'The function "{f_name}" is using its deprecated version and will ' + 'expire in version "{version_name}".'.format( + f_name=func_path, version_name=self._exp_version_name + ) + ) + log.warning(" ".join(msg)) else: msg_patt = 'The lifetime of the function "{f_name}" expired.' - if '_' + self._orig_f_name == self._function.__name__: - msg = [msg_patt.format(f_name=self._orig_f_name), - 'Please turn off its deprecated version in the configuration'] + if "_" + self._orig_f_name == self._function.__name__: + msg = [ + msg_patt.format(f_name=self._orig_f_name), + "Please turn off its deprecated version in the configuration", + ] else: - msg = ['Although function "{f_name}" is called, an alias "{f_alias}" ' - 'is configured as its deprecated version.'.format( - f_name=self._orig_f_name, f_alias=self._with_name or self._orig_f_name), - msg_patt.format(f_name=self._with_name or self._orig_f_name), - 'Please use its successor "{successor}" instead.'.format(successor=self._orig_f_name)] - log.error(' '.join(msg)) - raise CommandExecutionError(' '.join(msg)) + msg = [ + 'Although function "{f_name}" is called, an alias "{f_alias}" ' + "is configured as its deprecated version.".format( + f_name=self._orig_f_name, + f_alias=self._with_name or self._orig_f_name, + ), + msg_patt.format( + f_name=self._with_name or self._orig_f_name + ), + 'Please use its successor "{successor}" instead.'.format( + successor=self._orig_f_name + ), + ] + log.error(" ".join(msg)) + raise CommandExecutionError(" ".join(msg)) return self._call_function(kwargs) _decorate.__doc__ = self._function.__doc__ @@ -658,12 +754,13 @@ with_deprecated = _WithDeprecated def ignores_kwargs(*kwarg_names): - ''' + """ Decorator to filter out unexpected keyword arguments from the call kwarg_names: List of argument names to ignore - ''' + """ + def _ignores_kwargs(fn): @wraps(fn) def __ignores_kwargs(*args, **kwargs): @@ -672,14 +769,17 @@ def ignores_kwargs(*kwarg_names): if name in kwargs_filtered: del kwargs_filtered[name] return fn(*args, **kwargs_filtered) + return __ignores_kwargs + return _ignores_kwargs def ensure_unicode_args(function): - ''' + """ Decodes all arguments passed to the wrapped function - ''' + """ + @wraps(function) def wrapped(*args, **kwargs): if six.PY2: @@ -689,4 +789,5 @@ def ensure_unicode_args(function): ) else: return function(*args, **kwargs) + return wrapped diff --git a/salt/utils/decorators/jinja.py b/salt/utils/decorators/jinja.py index 22d68369cff..9f6adc41283 100644 --- a/salt/utils/decorators/jinja.py +++ b/salt/utils/decorators/jinja.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Jinja-specific decorators -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -12,23 +12,24 @@ log = logging.getLogger(__name__) class JinjaFilter(object): - ''' + """ This decorator is used to specify that a function is to be loaded as a Jinja filter. - ''' + """ + salt_jinja_filters = {} def __init__(self, name=None): - ''' - ''' + """ + """ self.name = name def __call__(self, function): - ''' - ''' + """ + """ name = self.name or function.__name__ if name not in self.salt_jinja_filters: - log.debug('Marking \'%s\' as a jinja filter', name) + log.debug("Marking '%s' as a jinja filter", name) self.salt_jinja_filters[name] = function return function @@ -37,23 +38,24 @@ jinja_filter = JinjaFilter class JinjaTest(object): - ''' + """ This decorator is used to specify that a function is to be loaded as a Jinja test. - ''' + """ + salt_jinja_tests = {} def __init__(self, name=None): - ''' - ''' + """ + """ self.name = name def __call__(self, function): - ''' - ''' + """ + """ name = self.name or function.__name__ if name not in self.salt_jinja_tests: - log.debug('Marking \'%s\' as a jinja test', name) + log.debug("Marking '%s' as a jinja test", name) self.salt_jinja_tests[name] = function return function @@ -62,23 +64,24 @@ jinja_test = JinjaTest class JinjaGlobal(object): - ''' + """ This decorator is used to specify that a function is to be loaded as a Jinja global. - ''' + """ + salt_jinja_globals = {} def __init__(self, name=None): - ''' - ''' + """ + """ self.name = name def __call__(self, function): - ''' - ''' + """ + """ name = self.name or function.__name__ if name not in self.salt_jinja_globals: - log.debug('Marking \'%s\' as a jinja global', name) + log.debug("Marking '%s' as a jinja global", name) self.salt_jinja_globals[name] = function return function diff --git a/salt/utils/decorators/path.py b/salt/utils/decorators/path.py index 4adacf0e4e1..e082998a06f 100644 --- a/salt/utils/decorators/path.py +++ b/salt/utils/decorators/path.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Decorators for salt.utils.path -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs @@ -11,33 +11,37 @@ from salt.utils.decorators.signature import identical_signature_wrapper def which(exe): - ''' + """ Decorator wrapper for salt.utils.path.which - ''' + """ + def wrapper(function): def wrapped(*args, **kwargs): if salt.utils.path.which(exe) is None: raise CommandNotFoundError( - 'The \'{0}\' binary was not found in $PATH.'.format(exe) + "The '{0}' binary was not found in $PATH.".format(exe) ) return function(*args, **kwargs) + return identical_signature_wrapper(function, wrapped) + return wrapper def which_bin(exes): - ''' + """ Decorator wrapper for salt.utils.path.which_bin - ''' + """ + def wrapper(function): def wrapped(*args, **kwargs): if salt.utils.path.which_bin(exes) is None: raise CommandNotFoundError( - 'None of provided binaries({0}) was not found ' - 'in $PATH.'.format( - ['\'{0}\''.format(exe) for exe in exes] - ) + "None of provided binaries({0}) was not found " + "in $PATH.".format(["'{0}'".format(exe) for exe in exes]) ) return function(*args, **kwargs) + return identical_signature_wrapper(function, wrapped) + return wrapper diff --git a/salt/utils/decorators/signature.py b/salt/utils/decorators/signature.py index 8bb6c195285..18b8615346d 100644 --- a/salt/utils/decorators/signature.py +++ b/salt/utils/decorators/signature.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" A decorator which returns a function with the same signature of the function which is being wrapped. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import inspect from functools import wraps @@ -16,14 +17,14 @@ from salt.ext import six def identical_signature_wrapper(original_function, wrapped_function): - ''' + """ Return a function with identical signature as ``original_function``'s which will call the ``wrapped_function``. - ''' - context = {'__wrapped__': wrapped_function} + """ + context = {"__wrapped__": wrapped_function} function_def = compile( - 'def {0}({1}):\n' - ' return __wrapped__({2})'.format( + "def {0}({1}):\n" + " return __wrapped__({2})".format( # Keep the original function name original_function.__name__, # The function signature including defaults, i.e., 'timeout=1' @@ -32,12 +33,12 @@ def identical_signature_wrapper(original_function, wrapped_function): )[1:-1], # The function signature without the defaults inspect.formatargspec( - formatvalue=lambda val: '', + formatvalue=lambda val: "", *salt.utils.args.get_function_argspec(original_function) - )[1:-1] + )[1:-1], ), - '<string>', - 'exec' + "<string>", + "exec", ) six.exec_(function_def, context) return wraps(original_function)(context[original_function.__name__]) diff --git a/salt/utils/decorators/state.py b/salt/utils/decorators/state.py index 3c32d1ba836..752b0b56c83 100644 --- a/salt/utils/decorators/state.py +++ b/salt/utils/decorators/state.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Decorators for salt.state :codeauthor: :email:`Bo Maryniuk (bo@suse.de)` -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging # Import salt libs @@ -21,7 +22,7 @@ class OutputUnifier(object): self.policies = [] for pls in policies: if not hasattr(self, pls): - raise SaltException('Unknown policy: {0}'.format(pls)) + raise SaltException("Unknown policy: {0}".format(pls)) else: self.policies.append(getattr(self, pls)) @@ -32,36 +33,44 @@ class OutputUnifier(object): try: result = pls(result) except Exception as exc: # pylint: disable=broad-except - log.debug('An exception occurred in this state: %s', exc, - exc_info_on_loglevel=logging.DEBUG) + log.debug( + "An exception occurred in this state: %s", + exc, + exc_info_on_loglevel=logging.DEBUG, + ) result = { - 'result': False, - 'name': 'later', - 'changes': {}, - 'comment': 'An exception occurred in this state: {0}'.format(exc) + "result": False, + "name": "later", + "changes": {}, + "comment": "An exception occurred in this state: {0}".format( + exc + ), } return result + return _func def content_check(self, result): - ''' + """ Checks for specific types in the state output. Raises an Exception in case particular rule is broken. :param result: :return: - ''' + """ if not isinstance(result, dict): - err_msg = 'Malformed state return. Data must be a dictionary type.' - elif not isinstance(result.get('changes'), dict): + err_msg = "Malformed state return. Data must be a dictionary type." + elif not isinstance(result.get("changes"), dict): err_msg = "'Changes' should be a dictionary." else: missing = [] - for val in ['name', 'result', 'changes', 'comment']: + for val in ["name", "result", "changes", "comment"]: if val not in result: missing.append(val) if missing: - err_msg = 'The following keys were not present in the state return: {0}.'.format(', '.join(missing)) + err_msg = "The following keys were not present in the state return: {0}.".format( + ", ".join(missing) + ) else: err_msg = None @@ -71,7 +80,7 @@ class OutputUnifier(object): return result def unify(self, result): - ''' + """ While comments as a list are allowed, comments needs to be strings for backward compatibility. See such claim here: https://github.com/saltstack/salt/pull/43070 @@ -82,12 +91,12 @@ class OutputUnifier(object): :param result: :return: - ''' - if isinstance(result.get('comment'), list): - result['comment'] = u'\n'.join([ - salt.utils.stringutils.to_unicode(elm) for elm in result['comment'] - ]) - if result.get('result') is not None: - result['result'] = bool(result['result']) + """ + if isinstance(result.get("comment"), list): + result["comment"] = "\n".join( + [salt.utils.stringutils.to_unicode(elm) for elm in result["comment"]] + ) + if result.get("result") is not None: + result["result"] = bool(result["result"]) return result diff --git a/salt/utils/dictdiffer.py b/salt/utils/dictdiffer.py index 30e87e88543..5479bf7e398 100644 --- a/salt/utils/dictdiffer.py +++ b/salt/utils/dictdiffer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Calculate the difference between two dictionaries as: (1) items added (2) items removed @@ -10,25 +10,35 @@ Available at repository: https://github.com/hughdbrown/dictdiffer Added the ability to recursively compare dictionaries -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import copy -from collections import Mapping + from salt.ext import six +try: + from collections.abc import Mapping +except ImportError: + # pylint: disable=no-name-in-module + from collections import Mapping + + # pylint: enable=no-name-in-module + def diff(current_dict, past_dict): return DictDiffer(current_dict, past_dict) class DictDiffer(object): - ''' + """ Calculate the difference between two dictionaries as: (1) items added (2) items removed (3) keys same in both but changed values (4) keys same in both and unchanged values - ''' + """ + def __init__(self, current_dict, past_dict): self.current_dict, self.past_dict = current_dict, past_dict self.set_current, self.set_past = set(list(current_dict)), set(list(past_dict)) @@ -41,10 +51,14 @@ class DictDiffer(object): return self.set_past - self.intersect def changed(self): - return set(o for o in self.intersect if self.past_dict[o] != self.current_dict[o]) + return set( + o for o in self.intersect if self.past_dict[o] != self.current_dict[o] + ) def unchanged(self): - return set(o for o in self.intersect if self.past_dict[o] == self.current_dict[o]) + return set( + o for o in self.intersect if self.past_dict[o] == self.current_dict[o] + ) def deep_diff(old, new, ignore=None): @@ -58,8 +72,7 @@ def deep_diff(old, new, ignore=None): tmps = [] tmp_old, tmp_new, reentrant = stack.pop() for key in set(list(tmp_old) + list(tmp_new)): - if key in tmp_old and key in tmp_new \ - and tmp_old[key] == tmp_new[key]: + if key in tmp_old and key in tmp_new and tmp_old[key] == tmp_new[key]: del tmp_old[key] del tmp_new[key] continue @@ -68,20 +81,21 @@ def deep_diff(old, new, ignore=None): del tmp_old[key] if key in tmp_new and key in ignore: del tmp_new[key] - if isinstance(tmp_old.get(key), Mapping) \ - and isinstance(tmp_new.get(key), Mapping): + if isinstance(tmp_old.get(key), Mapping) and isinstance( + tmp_new.get(key), Mapping + ): tmps.append((tmp_old[key], tmp_new[key], False)) if tmps: stack.extend([(tmp_old, tmp_new, True)] + tmps) if old: - res['old'] = old + res["old"] = old if new: - res['new'] = new + res["new"] = new return res def recursive_diff(past_dict, current_dict, ignore_missing_keys=True): - ''' + """ Returns a RecursiveDictDiffer object that computes the recursive diffs between two dictionaries @@ -96,12 +110,12 @@ def recursive_diff(past_dict, current_dict, ignore_missing_keys=True): current_dict, but exist in the past_dict. If true, the diff will not contain the missing keys. Default is True. - ''' + """ return RecursiveDictDiffer(past_dict, current_dict, ignore_missing_keys) class RecursiveDictDiffer(DictDiffer): - ''' + """ Calculates a recursive diff between the current_dict and the past_dict creating a diff in the format @@ -139,11 +153,12 @@ class RecursiveDictDiffer(DictDiffer): common_key3: changed_key3 from <old_int> to <new_int> - ''' - NONE_VALUE = '<_null_>' + """ + + NONE_VALUE = "<_null_>" def __init__(self, past_dict, current_dict, ignore_missing_keys): - ''' + """ past_dict Past dictionary. @@ -154,232 +169,254 @@ class RecursiveDictDiffer(DictDiffer): Flag specifying whether to ignore keys that no longer exist in the current_dict, but exist in the past_dict. If true, the diff will not contain the missing keys. - ''' + """ super(RecursiveDictDiffer, self).__init__(current_dict, past_dict) - self._diffs = \ - self._get_diffs(self.current_dict, self.past_dict, - ignore_missing_keys) + self._diffs = self._get_diffs( + self.current_dict, self.past_dict, ignore_missing_keys + ) # Ignores unet values when assessing the changes self.ignore_unset_values = True @classmethod def _get_diffs(cls, dict1, dict2, ignore_missing_keys): - ''' + """ Returns a dict with the differences between dict1 and dict2 Notes: Keys that only exist in dict2 are not included in the diff if ignore_missing_keys is True, otherwise they are Simple compares are done on lists - ''' + """ ret_dict = {} for p in dict1.keys(): if p not in dict2: - ret_dict.update({p: {'new': dict1[p], 'old': cls.NONE_VALUE}}) + ret_dict.update({p: {"new": dict1[p], "old": cls.NONE_VALUE}}) elif dict1[p] != dict2[p]: if isinstance(dict1[p], dict) and isinstance(dict2[p], dict): - sub_diff_dict = cls._get_diffs(dict1[p], dict2[p], - ignore_missing_keys) + sub_diff_dict = cls._get_diffs( + dict1[p], dict2[p], ignore_missing_keys + ) if sub_diff_dict: ret_dict.update({p: sub_diff_dict}) else: - ret_dict.update({p: {'new': dict1[p], 'old': dict2[p]}}) + ret_dict.update({p: {"new": dict1[p], "old": dict2[p]}}) if not ignore_missing_keys: for p in dict2.keys(): if p not in dict1.keys(): - ret_dict.update({p: {'new': cls.NONE_VALUE, - 'old': dict2[p]}}) + ret_dict.update({p: {"new": cls.NONE_VALUE, "old": dict2[p]}}) return ret_dict @classmethod - def _get_values(cls, diff_dict, type='new'): - ''' + def _get_values(cls, diff_dict, type="new"): + """ Returns a dictionaries with the 'new' values in a diff dict. type Which values to return, 'new' or 'old' - ''' + """ ret_dict = {} for p in diff_dict.keys(): if type in diff_dict[p].keys(): ret_dict.update({p: diff_dict[p][type]}) else: - ret_dict.update( - {p: cls._get_values(diff_dict[p], type=type)}) + ret_dict.update({p: cls._get_values(diff_dict[p], type=type)}) return ret_dict @classmethod def _get_changes(cls, diff_dict): - ''' + """ Returns a list of string message with the differences in a diff dict. Each inner difference is tabulated two space deeper - ''' + """ changes_strings = [] for p in sorted(diff_dict.keys()): - if sorted(diff_dict[p].keys()) == ['new', 'old']: + if sorted(diff_dict[p].keys()) == ["new", "old"]: # Some string formatting - old_value = diff_dict[p]['old'] - if diff_dict[p]['old'] == cls.NONE_VALUE: - old_value = 'nothing' - elif isinstance(diff_dict[p]['old'], six.string_types): - old_value = '\'{0}\''.format(diff_dict[p]['old']) - elif isinstance(diff_dict[p]['old'], list): - old_value = '\'{0}\''.format( - ', '.join(diff_dict[p]['old'])) - new_value = diff_dict[p]['new'] - if diff_dict[p]['new'] == cls.NONE_VALUE: - new_value = 'nothing' - elif isinstance(diff_dict[p]['new'], six.string_types): - new_value = '\'{0}\''.format(diff_dict[p]['new']) - elif isinstance(diff_dict[p]['new'], list): - new_value = '\'{0}\''.format(', '.join(diff_dict[p]['new'])) - changes_strings.append('{0} from {1} to {2}'.format( - p, old_value, new_value)) + old_value = diff_dict[p]["old"] + if diff_dict[p]["old"] == cls.NONE_VALUE: + old_value = "nothing" + elif isinstance(diff_dict[p]["old"], six.string_types): + old_value = "'{0}'".format(diff_dict[p]["old"]) + elif isinstance(diff_dict[p]["old"], list): + old_value = "'{0}'".format(", ".join(diff_dict[p]["old"])) + new_value = diff_dict[p]["new"] + if diff_dict[p]["new"] == cls.NONE_VALUE: + new_value = "nothing" + elif isinstance(diff_dict[p]["new"], six.string_types): + new_value = "'{0}'".format(diff_dict[p]["new"]) + elif isinstance(diff_dict[p]["new"], list): + new_value = "'{0}'".format(", ".join(diff_dict[p]["new"])) + changes_strings.append( + "{0} from {1} to {2}".format(p, old_value, new_value) + ) else: sub_changes = cls._get_changes(diff_dict[p]) if sub_changes: - changes_strings.append('{0}:'.format(p)) - changes_strings.extend([' {0}'.format(c) - for c in sub_changes]) + changes_strings.append("{0}:".format(p)) + changes_strings.extend([" {0}".format(c) for c in sub_changes]) return changes_strings def added(self): - ''' + """ Returns all keys that have been added. If the keys are in child dictionaries they will be represented with . notation - ''' + """ + def _added(diffs, prefix): keys = [] for key in diffs.keys(): - if isinstance(diffs[key], dict) and 'old' not in diffs[key]: - keys.extend(_added(diffs[key], - prefix='{0}{1}.'.format(prefix, key))) - elif diffs[key]['old'] == self.NONE_VALUE: - if isinstance(diffs[key]['new'], dict): + if isinstance(diffs[key], dict) and "old" not in diffs[key]: + keys.extend( + _added(diffs[key], prefix="{0}{1}.".format(prefix, key)) + ) + elif diffs[key]["old"] == self.NONE_VALUE: + if isinstance(diffs[key]["new"], dict): keys.extend( - _added(diffs[key]['new'], - prefix='{0}{1}.'.format(prefix, key))) + _added( + diffs[key]["new"], prefix="{0}{1}.".format(prefix, key) + ) + ) else: - keys.append('{0}{1}'.format(prefix, key)) + keys.append("{0}{1}".format(prefix, key)) return keys - return sorted(_added(self._diffs, prefix='')) + return sorted(_added(self._diffs, prefix="")) def removed(self): - ''' + """ Returns all keys that have been removed. If the keys are in child dictionaries they will be represented with . notation - ''' + """ + def _removed(diffs, prefix): keys = [] for key in diffs.keys(): - if isinstance(diffs[key], dict) and 'old' not in diffs[key]: - keys.extend(_removed(diffs[key], - prefix='{0}{1}.'.format(prefix, key))) - elif diffs[key]['new'] == self.NONE_VALUE: - keys.append('{0}{1}'.format(prefix, key)) - elif isinstance(diffs[key]['new'], dict): + if isinstance(diffs[key], dict) and "old" not in diffs[key]: keys.extend( - _removed(diffs[key]['new'], - prefix='{0}{1}.'.format(prefix, key))) + _removed(diffs[key], prefix="{0}{1}.".format(prefix, key)) + ) + elif diffs[key]["new"] == self.NONE_VALUE: + keys.append("{0}{1}".format(prefix, key)) + elif isinstance(diffs[key]["new"], dict): + keys.extend( + _removed( + diffs[key]["new"], prefix="{0}{1}.".format(prefix, key) + ) + ) return keys - return sorted(_removed(self._diffs, prefix='')) + return sorted(_removed(self._diffs, prefix="")) def changed(self): - ''' + """ Returns all keys that have been changed. If the keys are in child dictionaries they will be represented with . notation - ''' + """ + def _changed(diffs, prefix): keys = [] for key in diffs.keys(): if not isinstance(diffs[key], dict): continue - if isinstance(diffs[key], dict) and 'old' not in diffs[key]: - keys.extend(_changed(diffs[key], - prefix='{0}{1}.'.format(prefix, key))) + if isinstance(diffs[key], dict) and "old" not in diffs[key]: + keys.extend( + _changed(diffs[key], prefix="{0}{1}.".format(prefix, key)) + ) continue if self.ignore_unset_values: - if 'old' in diffs[key] and 'new' in diffs[key] and \ - diffs[key]['old'] != self.NONE_VALUE and \ - diffs[key]['new'] != self.NONE_VALUE: - if isinstance(diffs[key]['new'], dict): + if ( + "old" in diffs[key] + and "new" in diffs[key] + and diffs[key]["old"] != self.NONE_VALUE + and diffs[key]["new"] != self.NONE_VALUE + ): + if isinstance(diffs[key]["new"], dict): keys.extend( - _changed(diffs[key]['new'], - prefix='{0}{1}.'.format(prefix, key))) + _changed( + diffs[key]["new"], + prefix="{0}{1}.".format(prefix, key), + ) + ) else: - keys.append('{0}{1}'.format(prefix, key)) + keys.append("{0}{1}".format(prefix, key)) elif isinstance(diffs[key], dict): keys.extend( - _changed(diffs[key], - prefix='{0}{1}.'.format(prefix, key))) + _changed(diffs[key], prefix="{0}{1}.".format(prefix, key)) + ) else: - if 'old' in diffs[key] and 'new' in diffs[key]: - if isinstance(diffs[key]['new'], dict): + if "old" in diffs[key] and "new" in diffs[key]: + if isinstance(diffs[key]["new"], dict): keys.extend( - _changed(diffs[key]['new'], - prefix='{0}{1}.'.format(prefix, key))) + _changed( + diffs[key]["new"], + prefix="{0}{1}.".format(prefix, key), + ) + ) else: - keys.append('{0}{1}'.format(prefix, key)) + keys.append("{0}{1}".format(prefix, key)) elif isinstance(diffs[key], dict): keys.extend( - _changed(diffs[key], - prefix='{0}{1}.'.format(prefix, key))) + _changed(diffs[key], prefix="{0}{1}.".format(prefix, key)) + ) return keys - return sorted(_changed(self._diffs, prefix='')) + return sorted(_changed(self._diffs, prefix="")) def unchanged(self): - ''' + """ Returns all keys that have been unchanged. If the keys are in child dictionaries they will be represented with . notation - ''' + """ + def _unchanged(current_dict, diffs, prefix): keys = [] for key in current_dict.keys(): if key not in diffs: - keys.append('{0}{1}'.format(prefix, key)) + keys.append("{0}{1}".format(prefix, key)) elif isinstance(current_dict[key], dict): - if 'new' in diffs[key]: + if "new" in diffs[key]: # There is a diff continue else: keys.extend( - _unchanged(current_dict[key], - diffs[key], - prefix='{0}{1}.'.format(prefix, key))) + _unchanged( + current_dict[key], + diffs[key], + prefix="{0}{1}.".format(prefix, key), + ) + ) return keys - return sorted(_unchanged(self.current_dict, self._diffs, prefix='')) + + return sorted(_unchanged(self.current_dict, self._diffs, prefix="")) @property def diffs(self): - '''Returns a dict with the recursive diffs current_dict - past_dict''' + """Returns a dict with the recursive diffs current_dict - past_dict""" return self._diffs @property def new_values(self): - '''Returns a dictionary with the new values''' - return self._get_values(self._diffs, type='new') + """Returns a dictionary with the new values""" + return self._get_values(self._diffs, type="new") @property def old_values(self): - '''Returns a dictionary with the old values''' - return self._get_values(self._diffs, type='old') + """Returns a dictionary with the old values""" + return self._get_values(self._diffs, type="old") @property def changes_str(self): - '''Returns a string describing the changes''' - return '\n'.join(self._get_changes(self._diffs)) + """Returns a string describing the changes""" + return "\n".join(self._get_changes(self._diffs)) diff --git a/salt/utils/dicttrim.py b/salt/utils/dicttrim.py index 0d255dcaee8..9156d0c6014 100644 --- a/salt/utils/dicttrim.py +++ b/salt/utils/dicttrim.py @@ -7,30 +7,29 @@ import salt.payload def _trim_dict_in_dict(data, max_val_size, replace_with): - ''' + """ Takes a dictionary, max_val_size and replace_with and recursively loops through and replaces any values that are greater than max_val_size. - ''' + """ for key in data: if isinstance(data[key], dict): - _trim_dict_in_dict(data[key], - max_val_size, - replace_with) + _trim_dict_in_dict(data[key], max_val_size, replace_with) else: if sys.getsizeof(data[key]) > max_val_size: data[key] = replace_with def trim_dict( - data, - max_dict_bytes, - percent=50.0, - stepper_size=10, - replace_with='VALUE_TRIMMED', - is_msgpacked=False, - use_bin_type=False): - ''' + data, + max_dict_bytes, + percent=50.0, + stepper_size=10, + replace_with="VALUE_TRIMMED", + is_msgpacked=False, + use_bin_type=False, +): + """ Takes a dictionary and iterates over its keys, looking for large values and replacing them with a trimmed string. @@ -62,8 +61,8 @@ def trim_dict( with "use_bin_type=True". This also means that the msgpack data should be decoded with "encoding='utf-8'". - ''' - serializer = salt.payload.Serial({'serial': 'msgpack'}) + """ + serializer = salt.payload.Serial({"serial": "msgpack"}) if is_msgpacked: dict_size = sys.getsizeof(data) else: @@ -71,7 +70,7 @@ def trim_dict( if dict_size > max_dict_bytes: if is_msgpacked: if use_bin_type: - data = serializer.loads(data, encoding='utf-8') + data = serializer.loads(data, encoding="utf-8") else: data = serializer.loads(data) while True: @@ -80,9 +79,7 @@ def trim_dict( try: for key in data: if isinstance(data[key], dict): - _trim_dict_in_dict(data[key], - max_val_size, - replace_with) + _trim_dict_in_dict(data[key], max_val_size, replace_with) else: if sys.getsizeof(data[key]) > max_val_size: data[key] = replace_with diff --git a/salt/utils/dictupdate.py b/salt/utils/dictupdate.py index 456e7133730..c7df0774697 100644 --- a/salt/utils/dictupdate.py +++ b/salt/utils/dictupdate.py @@ -1,34 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" Alex Martelli's soulution for recursive dict update from http://stackoverflow.com/a/3233356 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -try: - from collections.abc import Mapping -except ImportError: - from collections import Mapping - # Import 3rd-party libs import copy import logging # Import salt libs import salt.ext.six as six -from salt.defaults import DEFAULT_TARGET_DELIM import salt.utils.data +from salt.defaults import DEFAULT_TARGET_DELIM from salt.exceptions import SaltInvocationError -from salt.utils.odict import OrderedDict from salt.utils.decorators.jinja import jinja_filter +from salt.utils.odict import OrderedDict + +try: + from collections.abc import Mapping +except ImportError: + # pylint: disable=no-name-in-module + from collections import Mapping + + # pylint: enable=no-name-in-module + log = logging.getLogger(__name__) def update(dest, upd, recursive_update=True, merge_lists=False): - ''' + """ Recursive version of the default dict.update Merges upd recursively into dest @@ -44,10 +48,9 @@ def update(dest, upd, recursive_update=True, merge_lists=False): .. versionchanged: 2016.11.6 When merging lists, duplicate values are removed. Values already present in the ``dest`` list are not added from the ``upd`` list. - ''' - if (not isinstance(dest, Mapping)) \ - or (not isinstance(upd, Mapping)): - raise TypeError('Cannot update using non-dict types in dictupdate.update()') + """ + if (not isinstance(dest, Mapping)) or (not isinstance(upd, Mapping)): + raise TypeError("Cannot update using non-dict types in dictupdate.update()") updkeys = list(upd.keys()) if not set(list(dest.keys())) & set(updkeys): recursive_update = False @@ -58,8 +61,7 @@ def update(dest, upd, recursive_update=True, merge_lists=False): dest_subkey = dest.get(key, None) except AttributeError: dest_subkey = None - if isinstance(dest_subkey, Mapping) \ - and isinstance(val, Mapping): + if isinstance(dest_subkey, Mapping) and isinstance(val, Mapping): ret = update(dest_subkey, val, merge_lists=merge_lists) dest[key] = ret elif isinstance(dest_subkey, list) and isinstance(val, list): @@ -99,6 +101,7 @@ def merge_recurse(obj_a, obj_b, merge_lists=False): def merge_aggregate(obj_a, obj_b): from salt.serializers.yamlex import merge_recursive as _yamlex_merge_recursive + return _yamlex_merge_recursive(obj_a, obj_b, level=1) @@ -109,42 +112,35 @@ def merge_overwrite(obj_a, obj_b, merge_lists=False): return merge_recurse(obj_a, obj_b, merge_lists=merge_lists) -def merge(obj_a, obj_b, strategy='smart', renderer='yaml', merge_lists=False): - if strategy == 'smart': - if renderer.split('|')[-1] == 'yamlex' or renderer.startswith('yamlex_'): - strategy = 'aggregate' +def merge(obj_a, obj_b, strategy="smart", renderer="yaml", merge_lists=False): + if strategy == "smart": + if renderer.split("|")[-1] == "yamlex" or renderer.startswith("yamlex_"): + strategy = "aggregate" else: - strategy = 'recurse' + strategy = "recurse" - if strategy == 'list': + if strategy == "list": merged = merge_list(obj_a, obj_b) - elif strategy == 'recurse': + elif strategy == "recurse": merged = merge_recurse(obj_a, obj_b, merge_lists) - elif strategy == 'aggregate': + elif strategy == "aggregate": #: level = 1 merge at least root data merged = merge_aggregate(obj_a, obj_b) - elif strategy == 'overwrite': + elif strategy == "overwrite": merged = merge_overwrite(obj_a, obj_b, merge_lists) - elif strategy == 'none': + elif strategy == "none": # If we do not want to merge, there is only one pillar passed, so we can safely use the default recurse, # we just do not want to log an error merged = merge_recurse(obj_a, obj_b) else: - log.warning( - 'Unknown merging strategy \'%s\', fallback to recurse', - strategy - ) + log.warning("Unknown merging strategy '%s', fallback to recurse", strategy) merged = merge_recurse(obj_a, obj_b) return merged -def ensure_dict_key( - in_dict, - keys, - delimiter=DEFAULT_TARGET_DELIM, - ordered_dict=False): - ''' +def ensure_dict_key(in_dict, keys, delimiter=DEFAULT_TARGET_DELIM, ordered_dict=False): + """ Ensures that in_dict contains the series of recursive keys defined in keys. :param dict in_dict: The dict to work with. @@ -154,7 +150,7 @@ def ensure_dict_key( Default: create regular dicts. :rtype: dict :return: Returns the modified in-place `in_dict`. - ''' + """ if delimiter in keys: a_keys = keys.split(delimiter) else: @@ -162,18 +158,16 @@ def ensure_dict_key( dict_pointer = in_dict while a_keys: current_key = a_keys.pop(0) - if current_key not in dict_pointer or not isinstance(dict_pointer[current_key], dict): + if current_key not in dict_pointer or not isinstance( + dict_pointer[current_key], dict + ): dict_pointer[current_key] = OrderedDict() if ordered_dict else {} dict_pointer = dict_pointer[current_key] return in_dict -def _dict_rpartition( - in_dict, - keys, - delimiter=DEFAULT_TARGET_DELIM, - ordered_dict=False): - ''' +def _dict_rpartition(in_dict, keys, delimiter=DEFAULT_TARGET_DELIM, ordered_dict=False): + """ Helper function to: - Ensure all but the last key in `keys` exist recursively in `in_dict`. - Return the dict at the one-to-last key, and the last key @@ -185,31 +179,26 @@ def _dict_rpartition( Default: create regular dicts. :rtype: tuple(dict, str) :return: (The dict at the one-to-last key, the last key) - ''' + """ if delimiter in keys: all_but_last_keys, _, last_key = keys.rpartition(delimiter) - ensure_dict_key(in_dict, - all_but_last_keys, - delimiter=delimiter, - ordered_dict=ordered_dict) - dict_pointer = salt.utils.data.traverse_dict(in_dict, - all_but_last_keys, - default=None, - delimiter=delimiter) + ensure_dict_key( + in_dict, all_but_last_keys, delimiter=delimiter, ordered_dict=ordered_dict + ) + dict_pointer = salt.utils.data.traverse_dict( + in_dict, all_but_last_keys, default=None, delimiter=delimiter + ) else: dict_pointer = in_dict last_key = keys return dict_pointer, last_key -@jinja_filter('set_dict_key_value') +@jinja_filter("set_dict_key_value") def set_dict_key_value( - in_dict, - keys, - value, - delimiter=DEFAULT_TARGET_DELIM, - ordered_dict=False): - ''' + in_dict, keys, value, delimiter=DEFAULT_TARGET_DELIM, ordered_dict=False +): + """ Ensures that in_dict contains the series of recursive keys defined in keys. Also sets whatever is at the end of `in_dict` traversed with `keys` to `value`. @@ -221,23 +210,19 @@ def set_dict_key_value( Default: create regular dicts. :rtype: dict :return: Returns the modified in-place `in_dict`. - ''' - dict_pointer, last_key = _dict_rpartition(in_dict, - keys, - delimiter=delimiter, - ordered_dict=ordered_dict) + """ + dict_pointer, last_key = _dict_rpartition( + in_dict, keys, delimiter=delimiter, ordered_dict=ordered_dict + ) dict_pointer[last_key] = value return in_dict -@jinja_filter('update_dict_key_value') +@jinja_filter("update_dict_key_value") def update_dict_key_value( - in_dict, - keys, - value, - delimiter=DEFAULT_TARGET_DELIM, - ordered_dict=False): - ''' + in_dict, keys, value, delimiter=DEFAULT_TARGET_DELIM, ordered_dict=False +): + """ Ensures that in_dict contains the series of recursive keys defined in keys. Also updates the dict, that is at the end of `in_dict` traversed with `keys`, with `value`. @@ -250,32 +235,32 @@ def update_dict_key_value( Default: create regular dicts. :rtype: dict :return: Returns the modified in-place `in_dict`. - ''' - dict_pointer, last_key = _dict_rpartition(in_dict, - keys, - delimiter=delimiter, - ordered_dict=ordered_dict) + """ + dict_pointer, last_key = _dict_rpartition( + in_dict, keys, delimiter=delimiter, ordered_dict=ordered_dict + ) if last_key not in dict_pointer or dict_pointer[last_key] is None: dict_pointer[last_key] = OrderedDict() if ordered_dict else {} try: dict_pointer[last_key].update(value) except AttributeError: - raise SaltInvocationError('The last key contains a {}, which cannot update.' - ''.format(type(dict_pointer[last_key]))) + raise SaltInvocationError( + "The last key contains a {}, which cannot update." + "".format(type(dict_pointer[last_key])) + ) except (ValueError, TypeError): - raise SaltInvocationError('Cannot update {} with a {}.' - ''.format(type(dict_pointer[last_key]), type(value))) + raise SaltInvocationError( + "Cannot update {} with a {}." + "".format(type(dict_pointer[last_key]), type(value)) + ) return in_dict -@jinja_filter('append_dict_key_value') +@jinja_filter("append_dict_key_value") def append_dict_key_value( - in_dict, - keys, - value, - delimiter=DEFAULT_TARGET_DELIM, - ordered_dict=False): - ''' + in_dict, keys, value, delimiter=DEFAULT_TARGET_DELIM, ordered_dict=False +): + """ Ensures that in_dict contains the series of recursive keys defined in keys. Also appends `value` to the list that is at the end of `in_dict` traversed with `keys`. @@ -288,29 +273,27 @@ def append_dict_key_value( Default: create regular dicts. :rtype: dict :return: Returns the modified in-place `in_dict`. - ''' - dict_pointer, last_key = _dict_rpartition(in_dict, - keys, - delimiter=delimiter, - ordered_dict=ordered_dict) + """ + dict_pointer, last_key = _dict_rpartition( + in_dict, keys, delimiter=delimiter, ordered_dict=ordered_dict + ) if last_key not in dict_pointer or dict_pointer[last_key] is None: dict_pointer[last_key] = [] try: dict_pointer[last_key].append(value) except AttributeError: - raise SaltInvocationError('The last key contains a {}, which cannot append.' - ''.format(type(dict_pointer[last_key]))) + raise SaltInvocationError( + "The last key contains a {}, which cannot append." + "".format(type(dict_pointer[last_key])) + ) return in_dict -@jinja_filter('extend_dict_key_value') +@jinja_filter("extend_dict_key_value") def extend_dict_key_value( - in_dict, - keys, - value, - delimiter=DEFAULT_TARGET_DELIM, - ordered_dict=False): - ''' + in_dict, keys, value, delimiter=DEFAULT_TARGET_DELIM, ordered_dict=False +): + """ Ensures that in_dict contains the series of recursive keys defined in keys. Also extends the list, that is at the end of `in_dict` traversed with `keys`, with `value`. @@ -323,20 +306,22 @@ def extend_dict_key_value( Default: create regular dicts. :rtype: dict :return: Returns the modified in-place `in_dict`. - ''' + """ dict_pointer, last_key = _dict_rpartition( - in_dict, - keys, - delimiter=delimiter, - ordered_dict=ordered_dict) + in_dict, keys, delimiter=delimiter, ordered_dict=ordered_dict + ) if last_key not in dict_pointer or dict_pointer[last_key] is None: dict_pointer[last_key] = [] try: dict_pointer[last_key].extend(value) except AttributeError: - raise SaltInvocationError('The last key contains a {}, which cannot extend.' - ''.format(type(dict_pointer[last_key]))) + raise SaltInvocationError( + "The last key contains a {}, which cannot extend." + "".format(type(dict_pointer[last_key])) + ) except TypeError: - raise SaltInvocationError('Cannot extend {} with a {}.' - ''.format(type(dict_pointer[last_key]), type(value))) + raise SaltInvocationError( + "Cannot extend {} with a {}." + "".format(type(dict_pointer[last_key]), type(value)) + ) return in_dict diff --git a/salt/utils/dns.py b/salt/utils/dns.py index 5b50fbf6329..b154763d2ba 100644 --- a/salt/utils/dns.py +++ b/salt/utils/dns.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Compendium of generic DNS utilities # Examples: dns.lookup(name, rdtype, ...) @@ -9,12 +9,13 @@ dns.srv_rec(data) dns.srv_data('my1.example.com', 389, prio=10, weight=100) dns.srv_name('ldap/tcp', 'example.com') -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import base64 import binascii +import functools import hashlib import itertools import logging @@ -24,98 +25,73 @@ import shlex import socket import ssl import string -import functools + +import salt.modules.cmdmod # Import Salt libs import salt.utils.files import salt.utils.network import salt.utils.path import salt.utils.stringutils -import salt.modules.cmdmod from salt._compat import ipaddress -from salt.utils.odict import OrderedDict # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import zip # pylint: disable=redefined-builtin +from salt.utils.odict import OrderedDict # Integrations try: import dns.resolver + HAS_DNSPYTHON = True except ImportError: HAS_DNSPYTHON = False try: import tldextract + HAS_TLDEXTRACT = True except ImportError: HAS_TLDEXTRACT = False -HAS_DIG = salt.utils.path.which('dig') is not None -DIG_OPTIONS = '+search +fail +noall +answer +nocl +nottl' -HAS_DRILL = salt.utils.path.which('drill') is not None -HAS_HOST = salt.utils.path.which('host') is not None -HAS_NSLOOKUP = salt.utils.path.which('nslookup') is not None +HAS_DIG = salt.utils.path.which("dig") is not None +DIG_OPTIONS = "+search +fail +noall +answer +nocl +nottl" +HAS_DRILL = salt.utils.path.which("drill") is not None +HAS_HOST = salt.utils.path.which("host") is not None +HAS_NSLOOKUP = salt.utils.path.which("nslookup") is not None -__salt__ = { - 'cmd.run_all': salt.modules.cmdmod.run_all -} +__salt__ = {"cmd.run_all": salt.modules.cmdmod.run_all} log = logging.getLogger(__name__) class RFC(object): - ''' + """ Simple holding class for all RFC/IANA registered lists & standards - ''' + """ + # https://tools.ietf.org/html/rfc6844#section-3 - CAA_TAGS = ( - 'issue', - 'issuewild', - 'iodef' - ) + CAA_TAGS = ("issue", "issuewild", "iodef") # http://www.iana.org/assignments/dns-sshfp-rr-parameters/dns-sshfp-rr-parameters.xhtml - SSHFP_ALGO = OrderedDict(( - (1, 'rsa'), - (2, 'dsa'), - (3, 'ecdsa'), - (4, 'ed25519'), - )) + SSHFP_ALGO = OrderedDict(((1, "rsa"), (2, "dsa"), (3, "ecdsa"), (4, "ed25519"),)) - SSHFP_HASH = OrderedDict(( - (1, 'sha1'), - (2, 'sha256'), - )) + SSHFP_HASH = OrderedDict(((1, "sha1"), (2, "sha256"),)) # http://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml - TLSA_USAGE = OrderedDict(( - (0, 'pkixta'), - (1, 'pkixee'), - (2, 'daneta'), - (3, 'daneee'), - )) - - TLSA_SELECT = OrderedDict(( - (0, 'cert'), - (1, 'spki'), - )) - - TLSA_MATCHING = OrderedDict(( - (0, 'full'), - (1, 'sha256'), - (2, 'sha512'), - )) - - SRV_PROTO = ( - 'tcp', - 'udp', - 'sctp' + TLSA_USAGE = OrderedDict( + ((0, "pkixta"), (1, "pkixee"), (2, "daneta"), (3, "daneee"),) ) + TLSA_SELECT = OrderedDict(((0, "cert"), (1, "spki"),)) + + TLSA_MATCHING = OrderedDict(((0, "full"), (1, "sha256"), (2, "sha512"),)) + + SRV_PROTO = ("tcp", "udp", "sctp") + @staticmethod def validate(lookup, ref, match=None): if lookup in ref: return lookup - elif match == 'in': + elif match == "in": return [code for code, name in ref.items() if lookup in name][-1] else: # OrderedDicts only!(?) @@ -128,11 +104,11 @@ def _to_port(port): assert 1 <= port <= 65535 return port except (ValueError, AssertionError): - raise ValueError('Invalid port {0}'.format(port)) + raise ValueError("Invalid port {0}".format(port)) def _tree(domain, tld=False): - ''' + """ Split out a domain in its parents Leverages tldextract to take the TLDs from publicsuffix.org @@ -141,23 +117,30 @@ def _tree(domain, tld=False): :param domain: dc2.ams2.example.com :param tld: Include TLD in list :return: [ 'dc2.ams2.example.com', 'ams2.example.com', 'example.com'] - ''' - domain = domain.rstrip('.') - assert '.' in domain, 'Provide a decent domain' + """ + domain = domain.rstrip(".") + assert "." in domain, "Provide a decent domain" if not tld: if HAS_TLDEXTRACT: tld = tldextract.extract(domain).suffix else: - tld = re.search(r'((?:(?:ac|biz|com?|info|edu|gov|mil|name|net|n[oi]m|org)\.)?[^.]+)$', domain).group() - log.info('Without tldextract, dns.util resolves the TLD of {0} to {1}'.format(domain, tld)) + tld = re.search( + r"((?:(?:ac|biz|com?|info|edu|gov|mil|name|net|n[oi]m|org)\.)?[^.]+)$", + domain, + ).group() + log.info( + "Without tldextract, dns.util resolves the TLD of {0} to {1}".format( + domain, tld + ) + ) res = [domain] while True: - idx = domain.find('.') + idx = domain.find(".") if idx < 0: break - domain = domain[idx + 1:] + domain = domain[idx + 1 :] if domain == tld: break res.append(domain) @@ -167,13 +150,13 @@ def _tree(domain, tld=False): def _weighted_order(recs): res = [] - weights = [rec['weight'] for rec in recs] + weights = [rec["weight"] for rec in recs] while weights: rnd = random.random() * sum(weights) for i, w in enumerate(weights): rnd -= w if rnd < 0: - res.append(recs.pop(i)['name']) + res.append(recs.pop(i)["name"]) weights.pop(i) break @@ -192,7 +175,7 @@ def _cast(rec_data, rec_cast): def _data2rec(schema, rec_data): - ''' + """ schema = OrderedDict({ 'prio': int, 'weight': int, @@ -202,29 +185,33 @@ def _data2rec(schema, rec_data): rec_data = '10 20 25 myawesome.nl' res = {'prio': 10, 'weight': 20, 'port': 25 'name': 'myawesome.nl'} - ''' + """ try: - rec_fields = rec_data.split(' ') + rec_fields = rec_data.split(" ") # spaces in digest fields are allowed assert len(rec_fields) >= len(schema) if len(rec_fields) > len(schema): cutoff = len(schema) - 1 - rec_fields = rec_fields[0:cutoff] + [''.join(rec_fields[cutoff:])] + rec_fields = rec_fields[0:cutoff] + ["".join(rec_fields[cutoff:])] if len(schema) == 1: res = _cast(rec_fields[0], next(iter(schema.values()))) else: - res = dict(( - (field_name, _cast(rec_field, rec_cast)) - for (field_name, rec_cast), rec_field in zip(schema.items(), rec_fields) - )) + res = dict( + ( + (field_name, _cast(rec_field, rec_cast)) + for (field_name, rec_cast), rec_field in zip( + schema.items(), rec_fields + ) + ) + ) return res except (AssertionError, AttributeError, TypeError, ValueError) as e: - raise ValueError('Unable to cast "{0}" as "{2}": {1}'.format( - rec_data, - e, - ' '.join(schema.keys()) - )) + raise ValueError( + 'Unable to cast "{0}" as "{2}": {1}'.format( + rec_data, e, " ".join(schema.keys()) + ) + ) def _data2rec_group(schema, recs_data, group_key): @@ -248,66 +235,69 @@ def _data2rec_group(schema, recs_data, group_key): res[idx].append(rdata) return res except (AssertionError, ValueError) as e: - raise ValueError('Unable to cast "{0}" as a group of "{1}": {2}'.format( - ','.join(recs_data), - ' '.join(schema.keys()), - e - )) + raise ValueError( + 'Unable to cast "{0}" as a group of "{1}": {2}'.format( + ",".join(recs_data), " ".join(schema.keys()), e + ) + ) def _rec2data(*rdata): - return ' '.join(rdata) + return " ".join(rdata) def _data_clean(data): data = data.strip(string.whitespace) - if data.startswith(('"', '\'')) and data.endswith(('"', '\'')): + if data.startswith(('"', "'")) and data.endswith(('"', "'")): return data[1:-1] else: return data def _lookup_dig(name, rdtype, timeout=None, servers=None, secure=None): - ''' + """ Use dig to lookup addresses :param name: Name of record to search :param rdtype: DNS record type :param timeout: server response timeout :param servers: [] of servers to use :return: [] of records or False if error - ''' - cmd = 'dig {0} -t {1} '.format(DIG_OPTIONS, rdtype) + """ + cmd = "dig {0} -t {1} ".format(DIG_OPTIONS, rdtype) if servers: - cmd += ''.join(['@{0} '.format(srv) for srv in servers]) + cmd += "".join(["@{0} ".format(srv) for srv in servers]) if timeout is not None: if servers: timeout = int(float(timeout) / len(servers)) else: timeout = int(timeout) - cmd += '+time={0} '.format(timeout) + cmd += "+time={0} ".format(timeout) if secure: - cmd += '+dnssec +adflag ' + cmd += "+dnssec +adflag " - cmd = __salt__['cmd.run_all']('{0} {1}'.format(cmd, name), python_shell=False, output_loglevel='quiet') + cmd = __salt__["cmd.run_all"]( + "{0} {1}".format(cmd, name), python_shell=False, output_loglevel="quiet" + ) - if 'ignoring invalid type' in cmd['stderr']: - raise ValueError('Invalid DNS type {}'.format(rdtype)) - elif cmd['retcode'] != 0: + if "ignoring invalid type" in cmd["stderr"]: + raise ValueError("Invalid DNS type {}".format(rdtype)) + elif cmd["retcode"] != 0: log.warning( - 'dig returned (%s): %s', - cmd['retcode'], cmd['stderr'].strip(string.whitespace + ';') + "dig returned (%s): %s", + cmd["retcode"], + cmd["stderr"].strip(string.whitespace + ";"), ) return False - elif not cmd['stdout']: + elif not cmd["stdout"]: return [] validated = False res = [] - for line in cmd['stdout'].splitlines(): + for line in cmd["stdout"].splitlines(): _, rtype, rdata = line.split(None, 2) - if rtype == 'CNAME' and rdtype != 'CNAME': + if rtype == "CNAME" and rdtype != "CNAME": continue - elif rtype == 'RRSIG': + elif rtype == "RRSIG": validated = True continue res.append(_data_clean(rdata)) @@ -319,49 +309,49 @@ def _lookup_dig(name, rdtype, timeout=None, servers=None, secure=None): def _lookup_drill(name, rdtype, timeout=None, servers=None, secure=None): - ''' + """ Use drill to lookup addresses :param name: Name of record to search :param rdtype: DNS record type :param timeout: command return timeout :param servers: [] of servers to use :return: [] of records or False if error - ''' - cmd = 'drill ' + """ + cmd = "drill " if secure: - cmd += '-D -o ad ' - cmd += '{0} {1} '.format(rdtype, name) + cmd += "-D -o ad " + cmd += "{0} {1} ".format(rdtype, name) if servers: - cmd += ''.join(['@{0} '.format(srv) for srv in servers]) - cmd = __salt__['cmd.run_all']( - cmd, timeout=timeout, - python_shell=False, output_loglevel='quiet') + cmd += "".join(["@{0} ".format(srv) for srv in servers]) + cmd = __salt__["cmd.run_all"]( + cmd, timeout=timeout, python_shell=False, output_loglevel="quiet" + ) - if cmd['retcode'] != 0: - log.warning('drill returned (%s): %s', cmd['retcode'], cmd['stderr']) + if cmd["retcode"] != 0: + log.warning("drill returned (%s): %s", cmd["retcode"], cmd["stderr"]) return False - lookup_res = iter(cmd['stdout'].splitlines()) + lookup_res = iter(cmd["stdout"].splitlines()) validated = False res = [] try: - line = '' - while 'ANSWER SECTION' not in line: + line = "" + while "ANSWER SECTION" not in line: line = next(lookup_res) while True: line = next(lookup_res) line = line.strip() - if not line or line.startswith(';;'): + if not line or line.startswith(";;"): break l_type, l_rec = line.split(None, 4)[-2:] - if l_type == 'CNAME' and rdtype != 'CNAME': + if l_type == "CNAME" and rdtype != "CNAME": continue - elif l_type == 'RRSIG': + elif l_type == "RRSIG": validated = True continue elif l_type != rdtype: - raise ValueError('Invalid DNS type {}'.format(rdtype)) + raise ValueError("Invalid DNS type {}".format(rdtype)) res.append(_data_clean(l_rec)) @@ -375,67 +365,67 @@ def _lookup_drill(name, rdtype, timeout=None, servers=None, secure=None): def _lookup_gai(name, rdtype, timeout=None): - ''' + """ Use Python's socket interface to lookup addresses :param name: Name of record to search :param rdtype: A or AAAA :param timeout: ignored :return: [] of addresses or False if error - ''' + """ try: - sock_t = { - 'A': socket.AF_INET, - 'AAAA': socket.AF_INET6 - }[rdtype] + sock_t = {"A": socket.AF_INET, "AAAA": socket.AF_INET6}[rdtype] except KeyError: - raise ValueError('Invalid DNS type {} for gai lookup'.format(rdtype)) + raise ValueError("Invalid DNS type {} for gai lookup".format(rdtype)) if timeout: - log.info('Ignoring timeout on gai resolver; fix resolv.conf to do that') + log.info("Ignoring timeout on gai resolver; fix resolv.conf to do that") try: - addresses = [sock[4][0] for sock in socket.getaddrinfo(name, None, sock_t, 0, socket.SOCK_RAW)] + addresses = [ + sock[4][0] + for sock in socket.getaddrinfo(name, None, sock_t, 0, socket.SOCK_RAW) + ] return addresses except socket.gaierror: return False def _lookup_host(name, rdtype, timeout=None, server=None): - ''' + """ Use host to lookup addresses :param name: Name of record to search :param server: Server to query :param rdtype: DNS record type :param timeout: server response wait :return: [] of records or False if error - ''' - cmd = 'host -t {0} '.format(rdtype) + """ + cmd = "host -t {0} ".format(rdtype) if timeout: - cmd += '-W {0} '.format(int(timeout)) + cmd += "-W {0} ".format(int(timeout)) cmd += name if server is not None: - cmd += ' {0}'.format(server) + cmd += " {0}".format(server) - cmd = __salt__['cmd.run_all'](cmd, python_shell=False, output_loglevel='quiet') + cmd = __salt__["cmd.run_all"](cmd, python_shell=False, output_loglevel="quiet") - if 'invalid type' in cmd['stderr']: - raise ValueError('Invalid DNS type {}'.format(rdtype)) - elif cmd['retcode'] != 0: - log.warning('host returned (%s): %s', cmd['retcode'], cmd['stderr']) + if "invalid type" in cmd["stderr"]: + raise ValueError("Invalid DNS type {}".format(rdtype)) + elif cmd["retcode"] != 0: + log.warning("host returned (%s): %s", cmd["retcode"], cmd["stderr"]) return False - elif 'has no' in cmd['stdout']: + elif "has no" in cmd["stdout"]: return [] res = [] - _stdout = cmd['stdout'] if server is None else cmd['stdout'].split('\n\n')[-1] + _stdout = cmd["stdout"] if server is None else cmd["stdout"].split("\n\n")[-1] for line in _stdout.splitlines(): - if rdtype != 'CNAME' and 'is an alias' in line: + if rdtype != "CNAME" and "is an alias" in line: continue - line = line.split(' ', 3)[-1] - for prefix in ('record', 'address', 'handled by', 'alias for'): + line = line.split(" ", 3)[-1] + for prefix in ("record", "address", "handled by", "alias for"): if line.startswith(prefix): - line = line[len(prefix) + 1:] + line = line[len(prefix) + 1 :] break res.append(_data_clean(line)) @@ -443,14 +433,14 @@ def _lookup_host(name, rdtype, timeout=None, server=None): def _lookup_dnspython(name, rdtype, timeout=None, servers=None, secure=None): - ''' + """ Use dnspython to lookup addresses :param name: Name of record to search :param rdtype: DNS record type :param timeout: query timeout :param server: [] of server(s) to try in order :return: [] of records or False if error - ''' + """ resolver = dns.resolver.Resolver() if timeout is not None: @@ -461,50 +451,54 @@ def _lookup_dnspython(name, rdtype, timeout=None, servers=None, secure=None): resolver.ednsflags += dns.flags.DO try: - res = [_data_clean(rr.to_text()) - for rr in resolver.query(name, rdtype, raise_on_no_answer=False)] + res = [ + _data_clean(rr.to_text()) + for rr in resolver.query(name, rdtype, raise_on_no_answer=False) + ] return res except dns.rdatatype.UnknownRdatatype: - raise ValueError('Invalid DNS type {}'.format(rdtype)) - except (dns.resolver.NXDOMAIN, - dns.resolver.YXDOMAIN, - dns.resolver.NoNameservers, - dns.exception.Timeout): + raise ValueError("Invalid DNS type {}".format(rdtype)) + except ( + dns.resolver.NXDOMAIN, + dns.resolver.YXDOMAIN, + dns.resolver.NoNameservers, + dns.exception.Timeout, + ): return False def _lookup_nslookup(name, rdtype, timeout=None, server=None): - ''' + """ Use nslookup to lookup addresses :param name: Name of record to search :param rdtype: DNS record type :param timeout: server response timeout :param server: server to query :return: [] of records or False if error - ''' - cmd = 'nslookup -query={0} {1}'.format(rdtype, name) + """ + cmd = "nslookup -query={0} {1}".format(rdtype, name) if timeout is not None: - cmd += ' -timeout={0}'.format(int(timeout)) + cmd += " -timeout={0}".format(int(timeout)) if server is not None: - cmd += ' {0}'.format(server) + cmd += " {0}".format(server) - cmd = __salt__['cmd.run_all'](cmd, python_shell=False, output_loglevel='quiet') + cmd = __salt__["cmd.run_all"](cmd, python_shell=False, output_loglevel="quiet") - if cmd['retcode'] != 0: + if cmd["retcode"] != 0: log.warning( - 'nslookup returned (%s): %s', - cmd['retcode'], - cmd['stdout'].splitlines()[-1].strip(string.whitespace + ';') + "nslookup returned (%s): %s", + cmd["retcode"], + cmd["stdout"].splitlines()[-1].strip(string.whitespace + ";"), ) return False - lookup_res = iter(cmd['stdout'].splitlines()) + lookup_res = iter(cmd["stdout"].splitlines()) res = [] try: line = next(lookup_res) - if 'unknown query type' in line: - raise ValueError('Invalid DNS type {}'.format(rdtype)) + if "unknown query type" in line: + raise ValueError("Invalid DNS type {}".format(rdtype)) while True: if name in line: @@ -513,22 +507,22 @@ def _lookup_nslookup(name, rdtype, timeout=None, server=None): while True: line = line.strip() - if not line or line.startswith('*'): + if not line or line.startswith("*"): break - elif rdtype != 'CNAME' and 'canonical name' in line: + elif rdtype != "CNAME" and "canonical name" in line: name = line.split()[-1][:-1] line = next(lookup_res) continue - elif rdtype == 'SOA': - line = line.split('=') - elif line.startswith('Name:'): + elif rdtype == "SOA": + line = line.split("=") + elif line.startswith("Name:"): line = next(lookup_res) - line = line.split(':', 1) + line = line.split(":", 1) elif line.startswith(name): - if '=' in line: - line = line.split('=', 1) + if "=" in line: + line = line.split("=", 1) else: - line = line.split(' ') + line = line.split(" ") res.append(_data_clean(line[-1])) line = next(lookup_res) @@ -536,8 +530,8 @@ def _lookup_nslookup(name, rdtype, timeout=None, server=None): except StopIteration: pass - if rdtype == 'SOA': - return [' '.join(res[1:])] + if rdtype == "SOA": + return [" ".join(res[1:])] else: return res @@ -550,9 +544,9 @@ def lookup( timeout=None, walk=False, walk_tld=False, - secure=None + secure=None, ): - ''' + """ Lookup DNS records and return their data :param name: name to lookup @@ -564,50 +558,61 @@ def lookup( :param walk_tld: Include the final domain in the walk :param secure: return only DNSSEC secured responses :return: [] of record data - ''' + """ # opts = __opts__.get('dns', {}) opts = {} - method = method or opts.get('method', 'auto') - secure = secure or opts.get('secure', None) - servers = servers or opts.get('servers', None) - timeout = timeout or opts.get('timeout', False) + method = method or opts.get("method", "auto") + secure = secure or opts.get("secure", None) + servers = servers or opts.get("servers", None) + timeout = timeout or opts.get("timeout", False) rdtype = rdtype.upper() # pylint: disable=bad-whitespace,multiple-spaces-before-keyword query_methods = ( - ('gai', _lookup_gai, not any((rdtype not in ('A', 'AAAA'), servers, secure))), - ('dnspython', _lookup_dnspython, HAS_DNSPYTHON), - ('dig', _lookup_dig, HAS_DIG), - ('drill', _lookup_drill, HAS_DRILL), - ('host', _lookup_host, HAS_HOST and not secure), - ('nslookup', _lookup_nslookup, HAS_NSLOOKUP and not secure), + ("gai", _lookup_gai, not any((rdtype not in ("A", "AAAA"), servers, secure))), + ("dnspython", _lookup_dnspython, HAS_DNSPYTHON), + ("dig", _lookup_dig, HAS_DIG), + ("drill", _lookup_drill, HAS_DRILL), + ("host", _lookup_host, HAS_HOST and not secure), + ("nslookup", _lookup_nslookup, HAS_NSLOOKUP and not secure), ) # pylint: enable=bad-whitespace,multiple-spaces-before-keyword try: - if method == 'auto': + if method == "auto": # The first one not to bork on the conditions becomes the function - method, resolver = next(((rname, rcb) for rname, rcb, rtest in query_methods if rtest)) + method, resolver = next( + ((rname, rcb) for rname, rcb, rtest in query_methods if rtest) + ) else: # The first one not to bork on the conditions becomes the function. And the name must match. - resolver = next((rcb for rname, rcb, rtest in query_methods if rname == method and rtest)) + resolver = next( + ( + rcb + for rname, rcb, rtest in query_methods + if rname == method and rtest + ) + ) except StopIteration: log.error( - 'Unable to lookup %s/%s: Resolver method %s invalid, unsupported ' - 'or unable to perform query', method, rdtype, name + "Unable to lookup %s/%s: Resolver method %s invalid, unsupported " + "or unable to perform query", + method, + rdtype, + name, ) return False res_kwargs = { - 'rdtype': rdtype, + "rdtype": rdtype, } if servers: if not isinstance(servers, (list, tuple)): servers = [servers] - if method in ('dnspython', 'dig', 'drill'): - res_kwargs['servers'] = servers + if method in ("dnspython", "dig", "drill"): + res_kwargs["servers"] = servers else: if timeout: timeout /= len(servers) @@ -620,30 +625,32 @@ def lookup( s_res = resolv_func(server=server, **res_kwargs) if s_res: return s_res + return _wrapper + resolver = _multi_srvr(resolver) if not walk: name = [name] else: idx = 0 - if rdtype in ('SRV', 'TLSA'): # The only RRs I know that have 2 name components - idx = name.find('.') + 1 - idx = name.find('.', idx) + 1 + if rdtype in ("SRV", "TLSA"): # The only RRs I know that have 2 name components + idx = name.find(".") + 1 + idx = name.find(".", idx) + 1 domain = name[idx:] rname = name[0:idx] name = _tree(domain, walk_tld) - if walk == 'name': + if walk == "name": name = [rname + domain for domain in name] if timeout: timeout /= len(name) if secure: - res_kwargs['secure'] = secure + res_kwargs["secure"] = secure if timeout: - res_kwargs['timeout'] = timeout + res_kwargs["timeout"] = timeout for rname in name: res = resolver(name=rname, **res_kwargs) @@ -661,9 +668,9 @@ def query( timeout=None, walk=False, walk_tld=False, - secure=None + secure=None, ): - ''' + """ Query DNS for information. Where `lookup()` returns record data, `query()` tries to interpret the data and return its results @@ -676,45 +683,49 @@ def query( :param walk: Walk the DNS upwards looking for the record type or name/recordtype if walk='name'. :param walk_tld: Include the top-level domain in the walk :return: [] of records - ''' + """ rdtype = rdtype.upper() qargs = { - 'method': method, - 'servers': servers, - 'timeout': timeout, - 'walk': walk, - 'walk_tld': walk_tld, - 'secure': secure + "method": method, + "servers": servers, + "timeout": timeout, + "walk": walk, + "walk_tld": walk_tld, + "secure": secure, } - if rdtype == 'PTR' and not name.endswith('arpa'): + if rdtype == "PTR" and not name.endswith("arpa"): name = ptr_name(name) - if rdtype == 'SPF': + if rdtype == "SPF": # 'SPF' has become a regular 'TXT' again - qres = [answer for answer in lookup(name, 'TXT', **qargs) if answer.startswith('v=spf')] + qres = [ + answer + for answer in lookup(name, "TXT", **qargs) + if answer.startswith("v=spf") + ] if not qres: qres = lookup(name, rdtype, **qargs) else: qres = lookup(name, rdtype, **qargs) rec_map = { - 'A': a_rec, - 'AAAA': aaaa_rec, - 'CAA': caa_rec, - 'MX': mx_rec, - 'SOA': soa_rec, - 'SPF': spf_rec, - 'SRV': srv_rec, - 'SSHFP': sshfp_rec, - 'TLSA': tlsa_rec, + "A": a_rec, + "AAAA": aaaa_rec, + "CAA": caa_rec, + "MX": mx_rec, + "SOA": soa_rec, + "SPF": spf_rec, + "SRV": srv_rec, + "SSHFP": sshfp_rec, + "TLSA": tlsa_rec, } if not qres or rdtype not in rec_map: return qres - elif rdtype in ('A', 'AAAA', 'SSHFP', 'TLSA'): + elif rdtype in ("A", "AAAA", "SSHFP", "TLSA"): res = [rec_map[rdtype](res) for res in qres] - elif rdtype in ('SOA', 'SPF'): + elif rdtype in ("SOA", "SPF"): res = rec_map[rdtype](qres[0]) else: res = rec_map[rdtype](qres) @@ -723,7 +734,7 @@ def query( def host(name, ip4=True, ip6=True, **kwargs): - ''' + """ Return a list of addresses for name ip6: @@ -732,147 +743,141 @@ def host(name, ip4=True, ip6=True, **kwargs): Return IPv4 addresses the rest is passed on to lookup() - ''' + """ res = {} if ip6: - ip6 = lookup(name, 'AAAA', **kwargs) + ip6 = lookup(name, "AAAA", **kwargs) if ip6: - res['ip6'] = ip6 + res["ip6"] = ip6 if ip4: - ip4 = lookup(name, 'A', **kwargs) + ip4 = lookup(name, "A", **kwargs) if ip4: - res['ip4'] = ip4 + res["ip4"] = ip4 return res def a_rec(rdata): - ''' + """ Validate and parse DNS record data for an A record :param rdata: DNS record data :return: { 'address': ip } - ''' - rschema = OrderedDict(( - ('address', ipaddress.IPv4Address), - )) + """ + rschema = OrderedDict((("address", ipaddress.IPv4Address),)) return _data2rec(rschema, rdata) def aaaa_rec(rdata): - ''' + """ Validate and parse DNS record data for an AAAA record :param rdata: DNS record data :return: { 'address': ip } - ''' - rschema = OrderedDict(( - ('address', ipaddress.IPv6Address), - )) + """ + rschema = OrderedDict((("address", ipaddress.IPv6Address),)) return _data2rec(rschema, rdata) def caa_rec(rdatas): - ''' + """ Validate and parse DNS record data for a CAA record :param rdata: DNS record data :return: dict w/fields - ''' - rschema = OrderedDict(( - ('flags', lambda flag: ['critical'] if int(flag) > 0 else []), - ('tag', RFC.CAA_TAGS), - ('value', lambda val: val.strip('\',"')) - )) + """ + rschema = OrderedDict( + ( + ("flags", lambda flag: ["critical"] if int(flag) > 0 else []), + ("tag", RFC.CAA_TAGS), + ("value", lambda val: val.strip("',\"")), + ) + ) - res = _data2rec_group(rschema, rdatas, 'tag') + res = _data2rec_group(rschema, rdatas, "tag") - for tag in ('issue', 'issuewild'): + for tag in ("issue", "issuewild"): tag_res = res.get(tag, False) if not tag_res: continue for idx, val in enumerate(tag_res): - if ';' not in val: + if ";" not in val: continue - val, params = val.split(';', 1) - params = dict(param.split('=') for param in shlex.split(params)) + val, params = val.split(";", 1) + params = dict(param.split("=") for param in shlex.split(params)) tag_res[idx] = {val: params} return res def mx_data(target, preference=10): - ''' + """ Generate MX record data :param target: server :param preference: preference number :return: DNS record data - ''' + """ return _rec2data(int(preference), target) def mx_rec(rdatas): - ''' + """ Validate and parse DNS record data for MX record(s) :param rdata: DNS record data :return: dict w/fields - ''' - rschema = OrderedDict(( - ('preference', int), - ('name', str), - )) - return _data2rec_group(rschema, rdatas, 'preference') + """ + rschema = OrderedDict((("preference", int), ("name", str),)) + return _data2rec_group(rschema, rdatas, "preference") def ptr_name(rdata): - ''' + """ Return PTR name of given IP :param rdata: IP address :return: PTR record name - ''' + """ try: return ipaddress.ip_address(rdata).reverse_pointer except ValueError: - log.error( - 'Unable to generate PTR record; %s is not a valid IP address', - rdata - ) + log.error("Unable to generate PTR record; %s is not a valid IP address", rdata) return False def soa_rec(rdata): - ''' + """ Validate and parse DNS record data for SOA record(s) :param rdata: DNS record data :return: dict w/fields - ''' - rschema = OrderedDict(( - ('mname', str), - ('rname', str), - ('serial', int), - ('refresh', int), - ('retry', int), - ('expire', int), - ('minimum', int), - )) + """ + rschema = OrderedDict( + ( + ("mname", str), + ("rname", str), + ("serial", int), + ("refresh", int), + ("retry", int), + ("expire", int), + ("minimum", int), + ) + ) return _data2rec(rschema, rdata) def spf_rec(rdata): - ''' + """ Validate and parse DNS record data for SPF record(s) :param rdata: DNS record data :return: dict w/fields - ''' - spf_fields = rdata.split(' ') - if not spf_fields.pop(0).startswith('v=spf'): - raise ValueError('Not an SPF record') + """ + spf_fields = rdata.split(" ") + if not spf_fields.pop(0).startswith("v=spf"): + raise ValueError("Not an SPF record") res = OrderedDict() mods = set() for mech_spec in spf_fields: - if mech_spec.startswith(('exp', 'redirect')): + if mech_spec.startswith(("exp", "redirect")): # It's a modifier - mod, val = mech_spec.split('=', 1) + mod, val = mech_spec.split("=", 1) if mod in mods: - raise KeyError('Modifier {0} can only appear once'.format(mod)) + raise KeyError("Modifier {0} can only appear once".format(mod)) mods.add(mod) continue @@ -885,14 +890,14 @@ def spf_rec(rdata): # return query(val, 'SPF', **qargs) mech = {} - if mech_spec[0] in ('+', '-', '~', '?'): - mech['qualifier'] = mech_spec[0] + if mech_spec[0] in ("+", "-", "~", "?"): + mech["qualifier"] = mech_spec[0] mech_spec = mech_spec[1:] - if ':' in mech_spec: - mech_spec, val = mech_spec.split(':', 1) - elif '/' in mech_spec: - idx = mech_spec.find('/') + if ":" in mech_spec: + mech_spec, val = mech_spec.split(":", 1) + elif "/" in mech_spec: + idx = mech_spec.find("/") mech_spec = mech_spec[0:idx] val = mech_spec[idx:] else: @@ -901,102 +906,102 @@ def spf_rec(rdata): res[mech_spec] = mech if not val: continue - elif mech_spec in ('ip4', 'ip6'): + elif mech_spec in ("ip4", "ip6"): val = ipaddress.ip_interface(val) assert val.version == int(mech_spec[-1]) - mech['value'] = val + mech["value"] = val return res def srv_data(target, port, prio=10, weight=10): - ''' + """ Generate SRV record data :param target: :param port: :param prio: :param weight: :return: - ''' + """ return _rec2data(prio, weight, port, target) -def srv_name(svc, proto='tcp', domain=None): - ''' +def srv_name(svc, proto="tcp", domain=None): + """ Generate SRV record name :param svc: ldap, 389 etc :param proto: tcp, udp, sctp etc. :param domain: name to append :return: - ''' + """ proto = RFC.validate(proto, RFC.SRV_PROTO) if isinstance(svc, int) or svc.isdigit(): svc = _to_port(svc) if domain: - domain = '.' + domain - return '_{0}._{1}{2}'.format(svc, proto, domain) + domain = "." + domain + return "_{0}._{1}{2}".format(svc, proto, domain) def srv_rec(rdatas): - ''' + """ Validate and parse DNS record data for SRV record(s) :param rdata: DNS record data :return: dict w/fields - ''' - rschema = OrderedDict(( - ('prio', int), - ('weight', int), - ('port', _to_port), - ('name', str), - )) - return _data2rec_group(rschema, rdatas, 'prio') + """ + rschema = OrderedDict( + (("prio", int), ("weight", int), ("port", _to_port), ("name", str),) + ) + return _data2rec_group(rschema, rdatas, "prio") def sshfp_data(key_t, hash_t, pub): - ''' + """ Generate an SSHFP record :param key_t: rsa/dsa/ecdsa/ed25519 :param hash_t: sha1/sha256 :param pub: the SSH public key - ''' - key_t = RFC.validate(key_t, RFC.SSHFP_ALGO, 'in') + """ + key_t = RFC.validate(key_t, RFC.SSHFP_ALGO, "in") hash_t = RFC.validate(hash_t, RFC.SSHFP_HASH) hasher = hashlib.new(hash_t) - hasher.update( - base64.b64decode(pub) - ) + hasher.update(base64.b64decode(pub)) ssh_fp = hasher.hexdigest() return _rec2data(key_t, hash_t, ssh_fp) def sshfp_rec(rdata): - ''' + """ Validate and parse DNS record data for TLSA record(s) :param rdata: DNS record data :return: dict w/fields - ''' - rschema = OrderedDict(( - ('algorithm', RFC.SSHFP_ALGO), - ('fp_hash', RFC.SSHFP_HASH), - ('fingerprint', lambda val: val.lower()) # resolvers are inconsistent on this one - )) + """ + rschema = OrderedDict( + ( + ("algorithm", RFC.SSHFP_ALGO), + ("fp_hash", RFC.SSHFP_HASH), + ( + "fingerprint", + lambda val: val.lower(), + ), # resolvers are inconsistent on this one + ) + ) return _data2rec(rschema, rdata) def tlsa_data(pub, usage, selector, matching): - ''' + """ Generate a TLSA rec :param pub: Pub key in PEM format :param usage: :param selector: :param matching: :return: TLSA data portion - ''' + """ usage = RFC.validate(usage, RFC.TLSA_USAGE) selector = RFC.validate(selector, RFC.TLSA_SELECT) matching = RFC.validate(matching, RFC.TLSA_MATCHING) @@ -1006,38 +1011,32 @@ def tlsa_data(pub, usage, selector, matching): cert_fp = binascii.b2a_hex(pub) else: hasher = hashlib.new(RFC.TLSA_MATCHING[matching]) - hasher.update( - pub - ) + hasher.update(pub) cert_fp = hasher.hexdigest() return _rec2data(usage, selector, matching, cert_fp) def tlsa_rec(rdata): - ''' + """ Validate and parse DNS record data for TLSA record(s) :param rdata: DNS record data :return: dict w/fields - ''' - rschema = OrderedDict(( - ('usage', RFC.TLSA_USAGE), - ('selector', RFC.TLSA_SELECT), - ('matching', RFC.TLSA_MATCHING), - ('pub', str) - )) + """ + rschema = OrderedDict( + ( + ("usage", RFC.TLSA_USAGE), + ("selector", RFC.TLSA_SELECT), + ("matching", RFC.TLSA_MATCHING), + ("pub", str), + ) + ) return _data2rec(rschema, rdata) -def service( - svc, - proto='tcp', - domain=None, - walk=False, - secure=None -): - ''' +def service(svc, proto="tcp", domain=None, walk=False, secure=None): + """ Find an SRV service in a domain or its parents :param svc: service to find (ldap, 389, etc) :param proto: protocol the service talks (tcp, udp, etc) @@ -1048,8 +1047,8 @@ def service( [ prio1server1, prio1server2 ], [ prio2server1, prio2server2 ], ] (the servers will already be weighted according to the SRV rules) - ''' - qres = query(srv_name(svc, proto, domain), 'SRV', walk=walk, secure=secure) + """ + qres = query(srv_name(svc, proto, domain), "SRV", walk=walk, secure=secure) if not qres: return False @@ -1060,8 +1059,8 @@ def service( return res -def services(services_file='/etc/services'): - ''' +def services(services_file="/etc/services"): + """ Parse through system-known services :return: { 'svc': [ @@ -1071,21 +1070,21 @@ def services(services_file='/etc/services'): }, ], } - ''' + """ res = {} - with salt.utils.files.fopen(services_file, 'r') as svc_defs: + with salt.utils.files.fopen(services_file, "r") as svc_defs: for svc_def in svc_defs.readlines(): svc_def = salt.utils.stringutils.to_unicode(svc_def.strip()) - if not svc_def or svc_def.startswith('#'): + if not svc_def or svc_def.startswith("#"): continue - elif '#' in svc_def: - svc_def, comment = svc_def.split('#', 1) + elif "#" in svc_def: + svc_def, comment = svc_def.split("#", 1) comment = comment.strip() else: comment = None svc_def = svc_def.split() - port, proto = svc_def.pop(1).split('/') + port, proto = svc_def.pop(1).split("/") port = int(port) for name in svc_def: @@ -1093,25 +1092,25 @@ def services(services_file='/etc/services'): pp_res = svc_res.get(port, False) if not pp_res: svc = { - 'port': port, - 'proto': proto, + "port": port, + "proto": proto, } if comment: - svc['desc'] = comment + svc["desc"] = comment svc_res[port] = svc else: - curr_proto = pp_res['proto'] + curr_proto = pp_res["proto"] if isinstance(curr_proto, (list, tuple)): curr_proto.append(proto) else: - pp_res['proto'] = [curr_proto, proto] + pp_res["proto"] = [curr_proto, proto] - curr_desc = pp_res.get('desc', False) + curr_desc = pp_res.get("desc", False) if comment: if not curr_desc: - pp_res['desc'] = comment + pp_res["desc"] = comment elif comment != curr_desc: - pp_res['desc'] = '{0}, {1}'.format(curr_desc, comment) + pp_res["desc"] = "{0}, {1}".format(curr_desc, comment) res[name] = svc_res for svc, data in res.items(): @@ -1124,17 +1123,17 @@ def services(services_file='/etc/services'): return res -def parse_resolv(src='/etc/resolv.conf'): - ''' +def parse_resolv(src="/etc/resolv.conf"): + """ Parse a resolver configuration file (traditionally /etc/resolv.conf) - ''' + """ nameservers = [] ip4_nameservers = [] ip6_nameservers = [] search = [] sortlist = [] - domain = '' + domain = "" options = [] try: @@ -1146,8 +1145,10 @@ def parse_resolv(src='/etc/resolv.conf'): try: (directive, arg) = (line[0].lower(), line[1:]) # Drop everything after # or ; (comments) - arg = list(itertools.takewhile(lambda x: x[0] not in ('#', ';'), arg)) - if directive == 'nameserver': + arg = list( + itertools.takewhile(lambda x: x[0] not in ("#", ";"), arg) + ) + if directive == "nameserver": addr = arg[0] try: ip_addr = ipaddress.ip_address(addr) @@ -1160,12 +1161,12 @@ def parse_resolv(src='/etc/resolv.conf'): elif version == 6 and ip_addr not in ip6_nameservers: ip6_nameservers.append(ip_addr) except ValueError as exc: - log.error('%s: %s', src, exc) - elif directive == 'domain': + log.error("%s: %s", src, exc) + elif directive == "domain": domain = arg[0] - elif directive == 'search': + elif directive == "search": search = arg - elif directive == 'sortlist': + elif directive == "sortlist": # A sortlist is specified by IP address netmask pairs. # The netmask is optional and defaults to the natural # netmask of the net. The IP address and optional @@ -1174,18 +1175,19 @@ def parse_resolv(src='/etc/resolv.conf'): try: ip_net = ipaddress.ip_network(ip_raw) except ValueError as exc: - log.error('%s: %s', src, exc) + log.error("%s: %s", src, exc) else: - if '/' not in ip_raw: + if "/" not in ip_raw: # No netmask has been provided, guess # the "natural" one if ip_net.version == 4: ip_addr = six.text_type(ip_net.network_address) # pylint: disable=protected-access - mask = salt.utils.network.natural_ipv4_netmask(ip_addr) + mask = salt.utils.network.natural_ipv4_netmask( + ip_addr + ) ip_net = ipaddress.ip_network( - '{0}{1}'.format(ip_addr, mask), - strict=False + "{0}{1}".format(ip_addr, mask), strict=False ) if ip_net.version == 6: # TODO @@ -1193,7 +1195,7 @@ def parse_resolv(src='/etc/resolv.conf'): if ip_net not in sortlist: sortlist.append(ip_net) - elif directive == 'options': + elif directive == "options": # Options allows certain internal resolver variables to # be modified. if arg[0] not in options: @@ -1205,19 +1207,16 @@ def parse_resolv(src='/etc/resolv.conf'): # The domain and search keywords are mutually exclusive. If more # than one instance of these keywords is present, the last instance # will override. - log.debug( - '%s: The domain and search keywords are mutually exclusive.', - src - ) + log.debug("%s: The domain and search keywords are mutually exclusive.", src) return { - 'nameservers': nameservers, - 'ip4_nameservers': ip4_nameservers, - 'ip6_nameservers': ip6_nameservers, - 'sortlist': [ip.with_netmask for ip in sortlist], - 'domain': domain, - 'search': search, - 'options': options + "nameservers": nameservers, + "ip4_nameservers": ip4_nameservers, + "ip6_nameservers": ip6_nameservers, + "sortlist": [ip.with_netmask for ip in sortlist], + "domain": domain, + "search": search, + "options": options, } except IOError: return {} diff --git a/salt/utils/doc.py b/salt/utils/doc.py index 452f5ade665..547e5737c66 100644 --- a/salt/utils/doc.py +++ b/salt/utils/doc.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Functions for analyzing/parsing docstrings -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import logging import re @@ -14,20 +15,21 @@ log = logging.getLogger(__name__) def strip_rst(docs): - ''' + """ Strip/replace reStructuredText directives in docstrings - ''' + """ for func, docstring in six.iteritems(docs): - log.debug('Stripping docstring for %s', func) + log.debug("Stripping docstring for %s", func) if not docstring: continue docstring_new = docstring if six.PY3 else salt.utils.data.encode(docstring) for regex, repl in ( - (r' *.. code-block:: \S+\n{1,2}', ''), - ('.. note::', 'Note:'), - ('.. warning::', 'Warning:'), - ('.. versionadded::', 'New in version'), - ('.. versionchanged::', 'Changed in version')): + (r" *.. code-block:: \S+\n{1,2}", ""), + (".. note::", "Note:"), + (".. warning::", "Warning:"), + (".. versionadded::", "New in version"), + (".. versionchanged::", "Changed in version"), + ): if six.PY2: regex = salt.utils.data.encode(regex) repl = salt.utils.data.encode(repl) @@ -35,9 +37,11 @@ def strip_rst(docs): docstring_new = re.sub(regex, repl, docstring_new) except Exception: # pylint: disable=broad-except log.debug( - 'Exception encountered while matching regex %r to ' - 'docstring for function %s', regex, func, - exc_info=True + "Exception encountered while matching regex %r to " + "docstring for function %s", + regex, + func, + exc_info=True, ) if six.PY2: docstring_new = salt.utils.data.decode(docstring_new) @@ -47,7 +51,7 @@ def strip_rst(docs): def parse_docstring(docstring): - ''' + """ Parse a docstring into its parts. Currently only parses dependencies, can be extended to parse whatever is @@ -58,26 +62,26 @@ def parse_docstring(docstring): 'full': full docstring, 'deps': list of dependencies (empty list if none) } - ''' + """ # First try with regex search for :depends: - ret = {'full': docstring} - regex = r'([ \t]*):depends:[ \t]+- (\w+)[^\n]*\n(\1[ \t]+- (\w+)[^\n]*\n)*' + ret = {"full": docstring} + regex = r"([ \t]*):depends:[ \t]+- (\w+)[^\n]*\n(\1[ \t]+- (\w+)[^\n]*\n)*" match = re.search(regex, docstring, re.M) if match: deps = [] - regex = r'- (\w+)' + regex = r"- (\w+)" for line in match.group(0).strip().splitlines(): deps.append(re.search(regex, line).group(1)) - ret['deps'] = deps + ret["deps"] = deps return ret # Try searching for a one-liner instead else: - txt = 'Required python modules: ' + txt = "Required python modules: " data = docstring.splitlines() dep_list = list(x for x in data if x.strip().startswith(txt)) if not dep_list: - ret['deps'] = [] + ret["deps"] = [] return ret - deps = dep_list[0].replace(txt, '').strip().split(', ') - ret['deps'] = deps + deps = dep_list[0].replace(txt, "").strip().split(", ") + ret["deps"] = deps return ret diff --git a/salt/utils/docker/__init__.py b/salt/utils/docker/__init__.py index 0cbe5783e1c..9d8acba3902 100644 --- a/salt/utils/docker/__init__.py +++ b/salt/utils/docker/__init__.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Common logic used by the docker state and execution module This module contains logic to accommodate docker/salt CLI usage, as well as input as formatted by states. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging @@ -15,15 +16,16 @@ import logging import salt.utils.args import salt.utils.data import salt.utils.docker.translate -from salt.utils.docker.translate.helpers import split as _split from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.utils.args import get_function_argspec as _argspec # Import 3rd-party libs from salt.ext import six +from salt.utils.args import get_function_argspec as _argspec +from salt.utils.docker.translate.helpers import split as _split try: import docker + HAS_DOCKER_PY = True except ImportError: HAS_DOCKER_PY = False @@ -52,56 +54,54 @@ log = logging.getLogger(__name__) def get_client_args(limit=None): if not HAS_DOCKER_PY: - raise CommandExecutionError('docker Python module not imported') + raise CommandExecutionError("docker Python module not imported") limit = salt.utils.args.split_input(limit or []) ret = {} - if not limit or any(x in limit for x in - ('create_container', 'host_config', 'connect_container_to_network')): + if not limit or any( + x in limit + for x in ("create_container", "host_config", "connect_container_to_network") + ): try: - ret['create_container'] = \ - _argspec(docker.APIClient.create_container).args + ret["create_container"] = _argspec(docker.APIClient.create_container).args except AttributeError: try: - ret['create_container'] = \ - _argspec(docker.Client.create_container).args + ret["create_container"] = _argspec(docker.Client.create_container).args except AttributeError: - raise CommandExecutionError( - 'Coult not get create_container argspec' - ) + raise CommandExecutionError("Coult not get create_container argspec") try: - ret['host_config'] = \ - _argspec(docker.types.HostConfig.__init__).args + ret["host_config"] = _argspec(docker.types.HostConfig.__init__).args except AttributeError: try: - ret['host_config'] = \ - _argspec(docker.utils.create_host_config).args + ret["host_config"] = _argspec(docker.utils.create_host_config).args except AttributeError: - raise CommandExecutionError( - 'Could not get create_host_config argspec' - ) + raise CommandExecutionError("Could not get create_host_config argspec") try: - ret['connect_container_to_network'] = \ - _argspec(docker.types.EndpointConfig.__init__).args + ret["connect_container_to_network"] = _argspec( + docker.types.EndpointConfig.__init__ + ).args except AttributeError: try: - ret['connect_container_to_network'] = \ - _argspec(docker.utils.utils.create_endpoint_config).args + ret["connect_container_to_network"] = _argspec( + docker.utils.utils.create_endpoint_config + ).args except AttributeError: try: - ret['connect_container_to_network'] = \ - _argspec(docker.utils.create_endpoint_config).args + ret["connect_container_to_network"] = _argspec( + docker.utils.create_endpoint_config + ).args except AttributeError: raise CommandExecutionError( - 'Could not get connect_container_to_network argspec' + "Could not get connect_container_to_network argspec" ) for key, wrapped_func in ( - ('logs', docker.api.container.ContainerApiMixin.logs), - ('create_network', docker.api.network.NetworkApiMixin.create_network)): + ("logs", docker.api.container.ContainerApiMixin.logs), + ("create_network", docker.api.network.NetworkApiMixin.create_network), + ): if not limit or key in limit: try: func_ref = wrapped_func @@ -109,8 +109,9 @@ def get_client_args(limit=None): try: # create_network is decorated, so we have to dig into the # closure created by functools.wraps - ret[key] = \ - _argspec(func_ref.__func__.__closure__[0].cell_contents).args + ret[key] = _argspec( + func_ref.__func__.__closure__[0].cell_contents + ).args except (AttributeError, IndexError): # functools.wraps changed (unlikely), bail out ret[key] = [] @@ -125,21 +126,21 @@ def get_client_args(limit=None): # Function moved, bail out ret[key] = [] - if not limit or 'ipam_config' in limit: + if not limit or "ipam_config" in limit: try: - ret['ipam_config'] = _argspec(docker.types.IPAMPool.__init__).args + ret["ipam_config"] = _argspec(docker.types.IPAMPool.__init__).args except AttributeError: try: - ret['ipam_config'] = _argspec(docker.utils.create_ipam_pool).args + ret["ipam_config"] = _argspec(docker.utils.create_ipam_pool).args except AttributeError: - raise CommandExecutionError('Could not get ipam args') + raise CommandExecutionError("Could not get ipam args") for item in ret: # The API version is passed automagically by the API code that imports # these classes/functions and is not an arg that we will be passing, so # remove it if present. Similarly, don't include "self" if it shows up # in the arglist. - for argname in ('version', 'self'): + for argname in ("version", "self"): try: ret[item].remove(argname) except ValueError: @@ -149,32 +150,34 @@ def get_client_args(limit=None): # arglist. This keeps us from accidentally allowing args that docker-py has # moved from the create_container function to the either the host or # endpoint config. - for item in ('host_config', 'connect_container_to_network'): + for item in ("host_config", "connect_container_to_network"): for val in ret.get(item, []): try: - ret['create_container'].remove(val) + ret["create_container"].remove(val) except ValueError: # Arg is not in create_container arglist pass - for item in ('create_container', 'host_config', 'connect_container_to_network'): + for item in ("create_container", "host_config", "connect_container_to_network"): if limit and item not in limit: ret.pop(item, None) try: - ret['logs'].remove('container') + ret["logs"].remove("container") except (KeyError, ValueError, TypeError): pass return ret -def translate_input(translator, - skip_translate=None, - ignore_collisions=False, - validate_ip_addrs=True, - **kwargs): - ''' +def translate_input( + translator, + skip_translate=None, + ignore_collisions=False, + validate_ip_addrs=True, + **kwargs +): + """ Translate CLI/SLS input into the format the API expects. The ``translator`` argument must be a module containing translation functions, within salt.utils.docker.translate. A ``skip_translate`` kwarg can be passed to @@ -182,7 +185,7 @@ def translate_input(translator, list or an iterable containing strings (e.g. a list or tuple), and members of that tuple will have their translation skipped. Optionally, skip_translate can be set to True to skip *all* translation. - ''' + """ kwargs = copy.deepcopy(salt.utils.args.clean_kwargs(**kwargs)) invalid = {} collisions = [] @@ -198,8 +201,8 @@ def translate_input(translator, skip_translate = _split(skip_translate) except AttributeError: pass - if not hasattr(skip_translate, '__iter__'): - log.error('skip_translate is not an iterable, ignoring') + if not hasattr(skip_translate, "__iter__"): + log.error("skip_translate is not an iterable, ignoring") skip_translate = () try: @@ -213,16 +216,17 @@ def translate_input(translator, # ipam_pools is designed to be passed as a list of actual # dictionaries, but if each of the dictionaries passed has a single # element, it will be incorrectly repacked. - if key != 'ipam_pools' and salt.utils.data.is_dictlist(kwargs[key]): + if key != "ipam_pools" and salt.utils.data.is_dictlist(kwargs[key]): kwargs[key] = salt.utils.data.repack_dictlist(kwargs[key]) try: kwargs[key] = getattr(translator, real_key)( kwargs[key], validate_ip_addrs=validate_ip_addrs, - skip_translate=skip_translate) + skip_translate=skip_translate, + ) except AttributeError: - log.debug('No translation function for argument \'%s\'', key) + log.debug("No translation function for argument '%s'", key) continue except SaltInvocationError as exc: kwargs.pop(key) @@ -250,64 +254,66 @@ def translate_input(translator, except Exception as exc: # pylint: disable=broad-except error_message = exc.__str__() - log.error( - 'Error translating input: \'%s\'', error_message, exc_info=True) + log.error("Error translating input: '%s'", error_message, exc_info=True) else: error_message = None error_data = {} if error_message is not None: - error_data['error_message'] = error_message + error_data["error_message"] = error_message if invalid: - error_data['invalid'] = invalid + error_data["invalid"] = invalid if collisions and not ignore_collisions: for item in collisions: - error_data.setdefault('collisions', []).append( - '\'{0}\' is an alias for \'{1}\', they cannot both be used' - .format(translator.ALIASES_REVMAP[item], item) + error_data.setdefault("collisions", []).append( + "'{0}' is an alias for '{1}', they cannot both be used".format( + translator.ALIASES_REVMAP[item], item + ) ) if error_data: - raise CommandExecutionError( - 'Failed to translate input', info=error_data) + raise CommandExecutionError("Failed to translate input", info=error_data) return kwargs def create_ipam_config(*pools, **kwargs): - ''' + """ Builds an IP address management (IPAM) config dictionary - ''' + """ kwargs = salt.utils.args.clean_kwargs(**kwargs) try: # docker-py 2.0 and newer pool_args = salt.utils.args.get_function_argspec( - docker.types.IPAMPool.__init__).args + docker.types.IPAMPool.__init__ + ).args create_pool = docker.types.IPAMPool create_config = docker.types.IPAMConfig except AttributeError: # docker-py < 2.0 pool_args = salt.utils.args.get_function_argspec( - docker.utils.create_ipam_pool).args + docker.utils.create_ipam_pool + ).args create_pool = docker.utils.create_ipam_pool create_config = docker.utils.create_ipam_config - for primary_key, alias_key in (('driver', 'ipam_driver'), - ('options', 'ipam_opts')): + for primary_key, alias_key in (("driver", "ipam_driver"), ("options", "ipam_opts")): if alias_key in kwargs: alias_val = kwargs.pop(alias_key) if primary_key in kwargs: log.warning( - 'docker.create_ipam_config: Both \'%s\' and \'%s\' ' - 'passed. Ignoring \'%s\'', - alias_key, primary_key, alias_key + "docker.create_ipam_config: Both '%s' and '%s' " + "passed. Ignoring '%s'", + alias_key, + primary_key, + alias_key, ) else: kwargs[primary_key] = alias_val - if salt.utils.data.is_dictlist(kwargs.get('options')): - kwargs['options'] = salt.utils.data.repack_dictlist(kwargs['options']) + if salt.utils.data.is_dictlist(kwargs.get("options")): + kwargs["options"] = salt.utils.data.repack_dictlist(kwargs["options"]) # Get all of the IPAM pool args that were passed as individual kwargs # instead of in the *pools tuple @@ -327,13 +333,13 @@ def create_ipam_config(*pools, **kwargs): # range, or map of aux addresses, even when no subnet is passed. # However, attempting to use this IPAM pool when creating the network # will cause the Docker Engine to throw an error. - if any('Subnet' not in pool for pool in pool_configs): - raise SaltInvocationError('A subnet is required in each IPAM pool') + if any("Subnet" not in pool for pool in pool_configs): + raise SaltInvocationError("A subnet is required in each IPAM pool") else: - kwargs['pool_configs'] = pool_configs + kwargs["pool_configs"] = pool_configs ret = create_config(**kwargs) - pool_dicts = ret.get('Config') + pool_dicts = ret.get("Config") if pool_dicts: # When you inspect a network with custom IPAM configuration, only # arguments which were explictly passed are reflected. By contrast, diff --git a/salt/utils/docker/translate/container.py b/salt/utils/docker/translate/container.py index 265cde74ba7..62d75dee1b5 100644 --- a/salt/utils/docker/translate/container.py +++ b/salt/utils/docker/translate/container.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Functions to translate input for container creation -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt libs @@ -17,122 +18,113 @@ from salt.ext.six.moves import range # pylint: disable=import-error,redefined-b from . import helpers ALIASES = { - 'cmd': 'command', - 'cpuset': 'cpuset_cpus', - 'dns_option': 'dns_opt', - 'env': 'environment', - 'expose': 'ports', - 'interactive': 'stdin_open', - 'ipc': 'ipc_mode', - 'label': 'labels', - 'memory': 'mem_limit', - 'memory_swap': 'memswap_limit', - 'publish': 'port_bindings', - 'publish_all': 'publish_all_ports', - 'restart': 'restart_policy', - 'rm': 'auto_remove', - 'sysctl': 'sysctls', - 'security_opts': 'security_opt', - 'ulimit': 'ulimits', - 'user_ns_mode': 'userns_mode', - 'volume': 'volumes', - 'workdir': 'working_dir', + "cmd": "command", + "cpuset": "cpuset_cpus", + "dns_option": "dns_opt", + "env": "environment", + "expose": "ports", + "interactive": "stdin_open", + "ipc": "ipc_mode", + "label": "labels", + "memory": "mem_limit", + "memory_swap": "memswap_limit", + "publish": "port_bindings", + "publish_all": "publish_all_ports", + "restart": "restart_policy", + "rm": "auto_remove", + "sysctl": "sysctls", + "security_opts": "security_opt", + "ulimit": "ulimits", + "user_ns_mode": "userns_mode", + "volume": "volumes", + "workdir": "working_dir", } ALIASES_REVMAP = dict([(y, x) for x, y in six.iteritems(ALIASES)]) def _merge_keys(kwargs): - ''' + """ The log_config is a mixture of the CLI options --log-driver and --log-opt (which we support in Salt as log_driver and log_opt, respectively), but it must be submitted to the host config in the format {'Type': log_driver, 'Config': log_opt}. So, we need to construct this argument to be passed to the API from those two arguments. - ''' - log_driver = kwargs.pop('log_driver', helpers.NOTSET) - log_opt = kwargs.pop('log_opt', helpers.NOTSET) - if 'log_config' not in kwargs: - if log_driver is not helpers.NOTSET \ - or log_opt is not helpers.NOTSET: - kwargs['log_config'] = { - 'Type': log_driver - if log_driver is not helpers.NOTSET - else 'none', - 'Config': log_opt - if log_opt is not helpers.NOTSET - else {} + """ + log_driver = kwargs.pop("log_driver", helpers.NOTSET) + log_opt = kwargs.pop("log_opt", helpers.NOTSET) + if "log_config" not in kwargs: + if log_driver is not helpers.NOTSET or log_opt is not helpers.NOTSET: + kwargs["log_config"] = { + "Type": log_driver if log_driver is not helpers.NOTSET else "none", + "Config": log_opt if log_opt is not helpers.NOTSET else {}, } def _post_processing(kwargs, skip_translate, invalid): - ''' + """ Additional container-specific post-translation processing - ''' + """ # Don't allow conflicting options to be set - if kwargs.get('port_bindings') is not None \ - and kwargs.get('publish_all_ports'): - kwargs.pop('port_bindings') - invalid['port_bindings'] = 'Cannot be used when publish_all_ports=True' - if kwargs.get('hostname') is not None \ - and kwargs.get('network_mode') == 'host': - kwargs.pop('hostname') - invalid['hostname'] = 'Cannot be used when network_mode=True' + if kwargs.get("port_bindings") is not None and kwargs.get("publish_all_ports"): + kwargs.pop("port_bindings") + invalid["port_bindings"] = "Cannot be used when publish_all_ports=True" + if kwargs.get("hostname") is not None and kwargs.get("network_mode") == "host": + kwargs.pop("hostname") + invalid["hostname"] = "Cannot be used when network_mode=True" # Make sure volumes and ports are defined to match the binds and port_bindings - if kwargs.get('binds') is not None \ - and (skip_translate is True or - all(x not in skip_translate - for x in ('binds', 'volume', 'volumes'))): + if kwargs.get("binds") is not None and ( + skip_translate is True + or all(x not in skip_translate for x in ("binds", "volume", "volumes")) + ): # Make sure that all volumes defined in "binds" are included in the # "volumes" param. auto_volumes = [] - if isinstance(kwargs['binds'], dict): - for val in six.itervalues(kwargs['binds']): + if isinstance(kwargs["binds"], dict): + for val in six.itervalues(kwargs["binds"]): try: - if 'bind' in val: - auto_volumes.append(val['bind']) + if "bind" in val: + auto_volumes.append(val["bind"]) except TypeError: continue else: - if isinstance(kwargs['binds'], list): - auto_volume_defs = kwargs['binds'] + if isinstance(kwargs["binds"], list): + auto_volume_defs = kwargs["binds"] else: try: - auto_volume_defs = helpers.split(kwargs['binds']) + auto_volume_defs = helpers.split(kwargs["binds"]) except AttributeError: auto_volume_defs = [] for val in auto_volume_defs: try: - auto_volumes.append(helpers.split(val, ':')[1]) + auto_volumes.append(helpers.split(val, ":")[1]) except IndexError: continue if auto_volumes: - actual_volumes = kwargs.setdefault('volumes', []) - actual_volumes.extend([x for x in auto_volumes - if x not in actual_volumes]) + actual_volumes = kwargs.setdefault("volumes", []) + actual_volumes.extend([x for x in auto_volumes if x not in actual_volumes]) # Sort list to make unit tests more reliable actual_volumes.sort() - if kwargs.get('port_bindings') is not None \ - and all(x not in skip_translate - for x in ('port_bindings', 'expose', 'ports')): + if kwargs.get("port_bindings") is not None and all( + x not in skip_translate for x in ("port_bindings", "expose", "ports") + ): # Make sure that all ports defined in "port_bindings" are included in # the "ports" param. - ports_to_bind = list(kwargs['port_bindings']) + ports_to_bind = list(kwargs["port_bindings"]) if ports_to_bind: - ports_to_open = set(kwargs.get('ports', [])) + ports_to_open = set(kwargs.get("ports", [])) ports_to_open.update([helpers.get_port_def(x) for x in ports_to_bind]) - kwargs['ports'] = list(ports_to_open) + kwargs["ports"] = list(ports_to_open) - if 'ports' in kwargs \ - and all(x not in skip_translate for x in ('expose', 'ports')): + if "ports" in kwargs and all(x not in skip_translate for x in ("expose", "ports")): # TCP ports should only be passed as the port number. Normalize the # input so a port definition of 80/tcp becomes just 80 instead of # (80, 'tcp'). - for index, _ in enumerate(kwargs['ports']): + for index, _ in enumerate(kwargs["ports"]): try: - if kwargs['ports'][index][1] == 'tcp': - kwargs['ports'][index] = ports_to_open[index][0] + if kwargs["ports"][index][1] == "tcp": + kwargs["ports"][index] = ports_to_open[index][0] except TypeError: continue @@ -143,19 +135,19 @@ def auto_remove(val, **kwargs): # pylint: disable=unused-argument def binds(val, **kwargs): # pylint: disable=unused-argument - ''' + """ On the CLI, these are passed as multiple instances of a given CLI option. In Salt, we accept these as a comma-delimited list but the API expects a Python list. - ''' + """ if not isinstance(val, dict): if not isinstance(val, list): try: val = helpers.split(val) except AttributeError: raise SaltInvocationError( - '\'{0}\' is not a dictionary or list of bind ' - 'definitions'.format(val) + "'{0}' is not a dictionary or list of bind " + "definitions".format(val) ) return val @@ -165,18 +157,18 @@ def blkio_weight(val, **kwargs): # pylint: disable=unused-argument def blkio_weight_device(val, **kwargs): # pylint: disable=unused-argument - ''' + """ CLI input is a list of PATH:WEIGHT pairs, but the API expects a list of dictionaries in the format [{'Path': path, 'Weight': weight}] - ''' - val = helpers.map_vals(val, 'Path', 'Weight') + """ + val = helpers.map_vals(val, "Path", "Weight") for idx in range(len(val)): try: - val[idx]['Weight'] = int(val[idx]['Weight']) + val[idx]["Weight"] = int(val[idx]["Weight"]) except (TypeError, ValueError): raise SaltInvocationError( - 'Weight \'{Weight}\' for path \'{Path}\' is not an ' - 'integer'.format(**val[idx]) + "Weight '{Weight}' for path '{Path}' is not an " + "integer".format(**val[idx]) ) return val @@ -247,7 +239,7 @@ def dns_search(val, **kwargs): # pylint: disable=unused-argument def dns(val, **kwargs): val = helpers.translate_stringlist(val) - if kwargs.get('validate_ip_addrs', True): + if kwargs.get("validate_ip_addrs", True): for item in val: helpers.validate_ip(item) return val @@ -262,12 +254,12 @@ def entrypoint(val, **kwargs): # pylint: disable=unused-argument def environment(val, **kwargs): # pylint: disable=unused-argument - return helpers.translate_key_val(val, delimiter='=') + return helpers.translate_key_val(val, delimiter="=") def extra_hosts(val, **kwargs): - val = helpers.translate_key_val(val, delimiter=':') - if kwargs.get('validate_ip_addrs', True): + val = helpers.translate_key_val(val, delimiter=":") + if kwargs.get("validate_ip_addrs", True): for key in val: helpers.validate_ip(val[key]) return val @@ -298,7 +290,7 @@ def labels(val, **kwargs): # pylint: disable=unused-argument def links(val, **kwargs): # pylint: disable=unused-argument - return helpers.translate_key_val(val, delimiter=':') + return helpers.translate_key_val(val, delimiter=":") def log_driver(val, **kwargs): # pylint: disable=unused-argument @@ -306,11 +298,11 @@ def log_driver(val, **kwargs): # pylint: disable=unused-argument def log_opt(val, **kwargs): # pylint: disable=unused-argument - return helpers.translate_key_val(val, delimiter='=') + return helpers.translate_key_val(val, delimiter="=") def lxc_conf(val, **kwargs): # pylint: disable=unused-argument - return helpers.translate_key_val(val, delimiter='=') + return helpers.translate_key_val(val, delimiter="=") def mac_address(val, **kwargs): # pylint: disable=unused-argument @@ -358,7 +350,7 @@ def pids_limit(val, **kwargs): # pylint: disable=unused-argument def port_bindings(val, **kwargs): - ''' + """ On the CLI, these are passed as multiple instances of a given CLI option. In Salt, we accept these as a comma-delimited list but the API expects a Python dictionary mapping ports to their bindings. The format the API @@ -367,8 +359,8 @@ def port_bindings(val, **kwargs): TCP (the default). For reference, see the "Port bindings" section in the docker-py documentation at the following URL: http://docker-py.readthedocs.io/en/stable/api.html - ''' - validate_ip_addrs = kwargs.get('validate_ip_addrs', True) + """ + validate_ip_addrs = kwargs.get("validate_ip_addrs", True) if not isinstance(val, dict): if not isinstance(val, list): try: @@ -381,21 +373,21 @@ def port_bindings(val, **kwargs): val[idx] = six.text_type(val[idx]) def _format_port(port_num, proto): - return six.text_type(port_num) + '/udp' if proto.lower() == 'udp' else port_num + return ( + six.text_type(port_num) + "/udp" if proto.lower() == "udp" else port_num + ) bindings = {} for binding in val: - bind_parts = helpers.split(binding, ':') + bind_parts = helpers.split(binding, ":") num_bind_parts = len(bind_parts) if num_bind_parts == 1: # Single port or port range being passed through (no # special mapping) container_port = six.text_type(bind_parts[0]) - if container_port == '': - raise SaltInvocationError( - 'Empty port binding definition found' - ) - container_port, _, proto = container_port.partition('/') + if container_port == "": + raise SaltInvocationError("Empty port binding definition found") + container_port, _, proto = container_port.partition("/") try: start, end = helpers.get_port_range(container_port) except ValueError as exc: @@ -407,22 +399,20 @@ def port_bindings(val, **kwargs): for port_num in range(start, end + 1) ] elif num_bind_parts == 2: - if bind_parts[0] == '': + if bind_parts[0] == "": raise SaltInvocationError( - 'Empty host port in port binding definition ' - '\'{0}\''.format(binding) + "Empty host port in port binding definition " + "'{0}'".format(binding) ) - if bind_parts[1] == '': + if bind_parts[1] == "": raise SaltInvocationError( - 'Empty container port in port binding definition ' - '\'{0}\''.format(binding) + "Empty container port in port binding definition " + "'{0}'".format(binding) ) - container_port, _, proto = bind_parts[1].partition('/') + container_port, _, proto = bind_parts[1].partition("/") try: - cport_start, cport_end = \ - helpers.get_port_range(container_port) - hport_start, hport_end = \ - helpers.get_port_range(bind_parts[0]) + cport_start, cport_end = helpers.get_port_range(container_port) + hport_start, hport_end = helpers.get_port_range(bind_parts[0]) except ValueError as exc: # Using __str__() to avoid deprecation warning for # using the message attribute of the ValueError. @@ -430,9 +420,9 @@ def port_bindings(val, **kwargs): if (hport_end - hport_start) != (cport_end - cport_start): # Port range is mismatched raise SaltInvocationError( - 'Host port range ({0}) does not have the same ' - 'number of ports as the container port range ' - '({1})'.format(bind_parts[0], container_port) + "Host port range ({0}) does not have the same " + "number of ports as the container port range " + "({1})".format(bind_parts[0], container_port) ) cport_list = list(range(cport_start, cport_end + 1)) hport_list = list(range(hport_start, hport_end + 1)) @@ -444,21 +434,19 @@ def port_bindings(val, **kwargs): host_ip, host_port = bind_parts[0:2] if validate_ip_addrs: helpers.validate_ip(host_ip) - container_port, _, proto = bind_parts[2].partition('/') + container_port, _, proto = bind_parts[2].partition("/") try: - cport_start, cport_end = \ - helpers.get_port_range(container_port) + cport_start, cport_end = helpers.get_port_range(container_port) except ValueError as exc: # Using __str__() to avoid deprecation warning for # using the message attribute of the ValueError. raise SaltInvocationError(exc.__str__()) cport_list = list(range(cport_start, cport_end + 1)) - if host_port == '': + if host_port == "": hport_list = [None] * len(cport_list) else: try: - hport_start, hport_end = \ - helpers.get_port_range(host_port) + hport_start, hport_end = helpers.get_port_range(host_port) except ValueError as exc: # Using __str__() to avoid deprecation warning for # using the message attribute of the ValueError. @@ -468,20 +456,24 @@ def port_bindings(val, **kwargs): if (hport_end - hport_start) != (cport_end - cport_start): # Port range is mismatched raise SaltInvocationError( - 'Host port range ({0}) does not have the same ' - 'number of ports as the container port range ' - '({1})'.format(host_port, container_port) + "Host port range ({0}) does not have the same " + "number of ports as the container port range " + "({1})".format(host_port, container_port) ) - bind_vals = [( - _format_port(val, proto), - (host_ip,) if hport_list[idx] is None - else (host_ip, hport_list[idx]) - ) for idx, val in enumerate(cport_list)] + bind_vals = [ + ( + _format_port(val, proto), + (host_ip,) + if hport_list[idx] is None + else (host_ip, hport_list[idx]), + ) + for idx, val in enumerate(cport_list) + ] else: raise SaltInvocationError( - '\'{0}\' is an invalid port binding definition (at most ' - '3 components are allowed, found {1})'.format( + "'{0}' is an invalid port binding definition (at most " + "3 components are allowed, found {1})".format( binding, num_bind_parts ) ) @@ -502,7 +494,7 @@ def port_bindings(val, **kwargs): # bindings try: # Convert 1234/udp to 1234 - bindings[cport][idx] = int(cport.split('/')[0]) + bindings[cport][idx] = int(cport.split("/")[0]) except AttributeError: # Port was tcp, the AttributeError # signifies that the split failed @@ -515,12 +507,12 @@ def port_bindings(val, **kwargs): def ports(val, **kwargs): # pylint: disable=unused-argument - ''' + """ Like cap_add, cap_drop, etc., this option can be specified multiple times, and each time can be a port number or port range. Ultimately, the API expects a list, but elements in the list are ints when the port is TCP, and a tuple (port_num, 'udp') when the port is UDP. - ''' + """ if not isinstance(val, list): try: val = helpers.split(val) @@ -529,7 +521,7 @@ def ports(val, **kwargs): # pylint: disable=unused-argument val = [val] else: raise SaltInvocationError( - '\'{0}\' is not a valid port definition'.format(val) + "'{0}' is not a valid port definition".format(val) ) new_ports = set() for item in val: @@ -537,20 +529,20 @@ def ports(val, **kwargs): # pylint: disable=unused-argument new_ports.add(item) continue try: - item, _, proto = item.partition('/') + item, _, proto = item.partition("/") except AttributeError: raise SaltInvocationError( - '\'{0}\' is not a valid port definition'.format(item) + "'{0}' is not a valid port definition".format(item) ) try: - range_start, range_end = \ - helpers.get_port_range(item) + range_start, range_end = helpers.get_port_range(item) except ValueError as exc: # Using __str__() to avoid deprecation warning for using # the "message" attribute of the ValueError. raise SaltInvocationError(exc.__str__()) - new_ports.update([helpers.get_port_def(x, proto) - for x in range(range_start, range_end + 1)]) + new_ports.update( + [helpers.get_port_def(x, proto) for x in range(range_start, range_end + 1)] + ) return list(new_ports) @@ -567,32 +559,28 @@ def read_only(val, **kwargs): # pylint: disable=unused-argument def restart_policy(val, **kwargs): # pylint: disable=unused-argument - ''' + """ CLI input is in the format NAME[:RETRY_COUNT] but the API expects {'Name': name, 'MaximumRetryCount': retry_count}. We will use the 'fill' kwarg here to make sure the mapped result uses '0' for the count if this optional value was omitted. - ''' - val = helpers.map_vals( - val, - 'Name', - 'MaximumRetryCount', - fill='0') + """ + val = helpers.map_vals(val, "Name", "MaximumRetryCount", fill="0") # map_vals() converts the input into a list of dicts, but the API # wants just a dict, so extract the value from the single-element # list. If there was more than one element in the list, then # invalid input was passed (i.e. a comma-separated list, when what # we wanted was a single value). if len(val) != 1: - raise SaltInvocationError('Only one policy is permitted') + raise SaltInvocationError("Only one policy is permitted") val = val[0] try: # The count needs to be an integer - val['MaximumRetryCount'] = int(val['MaximumRetryCount']) + val["MaximumRetryCount"] = int(val["MaximumRetryCount"]) except (TypeError, ValueError): # Non-numeric retry count passed raise SaltInvocationError( - 'Retry count \'{0}\' is non-numeric'.format(val['MaximumRetryCount']) + "Retry count '{0}' is non-numeric".format(val["MaximumRetryCount"]) ) return val @@ -618,11 +606,11 @@ def stop_timeout(val, **kwargs): # pylint: disable=unused-argument def storage_opt(val, **kwargs): # pylint: disable=unused-argument - return helpers.translate_key_val(val, delimiter='=') + return helpers.translate_key_val(val, delimiter="=") def sysctls(val, **kwargs): # pylint: disable=unused-argument - return helpers.translate_key_val(val, delimiter='=') + return helpers.translate_key_val(val, delimiter="=") def tmpfs(val, **kwargs): # pylint: disable=unused-argument @@ -638,34 +626,33 @@ def ulimits(val, **kwargs): # pylint: disable=unused-argument for idx in range(len(val)): if not isinstance(val[idx], dict): try: - ulimit_name, limits = \ - helpers.split(val[idx], '=', 1) - comps = helpers.split(limits, ':', 1) + ulimit_name, limits = helpers.split(val[idx], "=", 1) + comps = helpers.split(limits, ":", 1) except (AttributeError, ValueError): raise SaltInvocationError( - 'Ulimit definition \'{0}\' is not in the format ' - 'type=soft_limit[:hard_limit]'.format(val[idx]) + "Ulimit definition '{0}' is not in the format " + "type=soft_limit[:hard_limit]".format(val[idx]) ) if len(comps) == 1: comps *= 2 soft_limit, hard_limit = comps try: - val[idx] = {'Name': ulimit_name, - 'Soft': int(soft_limit), - 'Hard': int(hard_limit)} + val[idx] = { + "Name": ulimit_name, + "Soft": int(soft_limit), + "Hard": int(hard_limit), + } except (TypeError, ValueError): raise SaltInvocationError( - 'Limit \'{0}\' contains non-numeric value(s)'.format( - val[idx] - ) + "Limit '{0}' contains non-numeric value(s)".format(val[idx]) ) return val def user(val, **kwargs): # pylint: disable=unused-argument - ''' + """ This can be either a string or a numeric uid - ''' + """ if not isinstance(val, six.integer_types): # Try to convert to integer. This will fail if the value is a # username. This is OK, as we check below to make sure that the @@ -677,9 +664,9 @@ def user(val, **kwargs): # pylint: disable=unused-argument except (TypeError, ValueError): pass if not isinstance(val, (six.integer_types, six.string_types)): - raise SaltInvocationError('Value must be a username or uid') + raise SaltInvocationError("Value must be a username or uid") elif isinstance(val, six.integer_types) and val < 0: - raise SaltInvocationError('\'{0}\' is an invalid uid'.format(val)) + raise SaltInvocationError("'{0}' is an invalid uid".format(val)) return val @@ -692,15 +679,13 @@ def volume_driver(val, **kwargs): # pylint: disable=unused-argument def volumes(val, **kwargs): # pylint: disable=unused-argument - ''' + """ Should be a list of absolute paths - ''' + """ val = helpers.translate_stringlist(val) for item in val: if not os.path.isabs(item): - raise SaltInvocationError( - '\'{0}\' is not an absolute path'.format(item) - ) + raise SaltInvocationError("'{0}' is not an absolute path".format(item)) return val @@ -709,13 +694,13 @@ def volumes_from(val, **kwargs): # pylint: disable=unused-argument def working_dir(val, **kwargs): # pylint: disable=unused-argument - ''' + """ Must be an absolute path - ''' + """ try: is_abs = os.path.isabs(val) except AttributeError: is_abs = False if not is_abs: - raise SaltInvocationError('\'{0}\' is not an absolute path'.format(val)) + raise SaltInvocationError("'{0}' is not an absolute path".format(val)) return val diff --git a/salt/utils/docker/translate/helpers.py b/salt/utils/docker/translate/helpers.py index bc99f0d62f3..f03034bf795 100644 --- a/salt/utils/docker/translate/helpers.py +++ b/salt/utils/docker/translate/helpers.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Functions to translate input in the docker CLI format to the format desired by by the API. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt libs @@ -14,17 +15,17 @@ from salt.exceptions import SaltInvocationError # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import range, zip # pylint: disable=import-error,redefined-builtin +from salt.ext.six.moves import range, zip NOTSET = object() -def split(item, sep=',', maxsplit=-1): +def split(item, sep=",", maxsplit=-1): return [x.strip() for x in item.split(sep, maxsplit)] -def get_port_def(port_num, proto='tcp'): - ''' +def get_port_def(port_num, proto="tcp"): + """ Given a port number and protocol, returns the port definition expected by docker-py. For TCP ports this is simply an integer, for UDP ports this is (port_num, 'udp'). @@ -43,84 +44,83 @@ def get_port_def(port_num, proto='tcp'): raised if the port number is non-numeric. This function either needs to be run on known good input, or should be run within a try/except that catches these two exceptions. - ''' + """ try: - port_num, _, port_num_proto = port_num.partition('/') + port_num, _, port_num_proto = port_num.partition("/") except AttributeError: pass else: if port_num_proto: proto = port_num_proto try: - if proto.lower() == 'udp': - return int(port_num), 'udp' + if proto.lower() == "udp": + return int(port_num), "udp" except AttributeError: pass return int(port_num) def get_port_range(port_def): - ''' + """ Given a port number or range, return a start and end to that range. Port ranges are defined as a string containing two numbers separated by a dash (e.g. '4505-4506'). A ValueError will be raised if bad input is provided. - ''' + """ if isinstance(port_def, six.integer_types): # Single integer, start/end of range is the same return port_def, port_def try: - comps = [int(x) for x in split(port_def, '-')] + comps = [int(x) for x in split(port_def, "-")] if len(comps) == 1: range_start = range_end = comps[0] else: range_start, range_end = comps if range_start > range_end: - raise ValueError('start > end') + raise ValueError("start > end") except (TypeError, ValueError) as exc: - if exc.__str__() == 'start > end': + if exc.__str__() == "start > end": msg = ( - 'Start of port range ({0}) cannot be greater than end of ' - 'port range ({1})'.format(range_start, range_end) + "Start of port range ({0}) cannot be greater than end of " + "port range ({1})".format(range_start, range_end) ) else: - msg = '\'{0}\' is non-numeric or an invalid port range'.format( - port_def - ) + msg = "'{0}' is non-numeric or an invalid port range".format(port_def) raise ValueError(msg) else: return range_start, range_end def map_vals(val, *names, **extra_opts): - ''' + """ Many arguments come in as a list of VAL1:VAL2 pairs, but map to a list of dicts in the format {NAME1: VAL1, NAME2: VAL2}. This function provides common code to handle these instances. - ''' - fill = extra_opts.pop('fill', NOTSET) + """ + fill = extra_opts.pop("fill", NOTSET) expected_num_elements = len(names) val = translate_stringlist(val) for idx, item in enumerate(val): if not isinstance(item, dict): - elements = [x.strip() for x in item.split(':')] + elements = [x.strip() for x in item.split(":")] num_elements = len(elements) if num_elements < expected_num_elements: if fill is NOTSET: raise SaltInvocationError( - '\'{0}\' contains {1} value(s) (expected {2})'.format( + "'{0}' contains {1} value(s) (expected {2})".format( item, num_elements, expected_num_elements ) ) elements.extend([fill] * (expected_num_elements - num_elements)) elif num_elements > expected_num_elements: raise SaltInvocationError( - '\'{0}\' contains {1} value(s) (expected {2})'.format( + "'{0}' contains {1} value(s) (expected {2})".format( item, num_elements, - expected_num_elements if fill is NOTSET - else 'up to {0}'.format(expected_num_elements) + expected_num_elements + if fill is NOTSET + else "up to {0}".format(expected_num_elements), ) ) val[idx] = dict(zip(names, elements)) @@ -130,9 +130,7 @@ def map_vals(val, *names, **extra_opts): def validate_ip(val): try: if not salt.utils.network.is_ip(val): - raise SaltInvocationError( - '\'{0}\' is not a valid IP address'.format(val) - ) + raise SaltInvocationError("'{0}' is not a valid IP address".format(val)) except RuntimeError: pass @@ -140,9 +138,7 @@ def validate_ip(val): def validate_subnet(val): try: if not salt.utils.network.is_subnet(val): - raise SaltInvocationError( - '\'{0}\' is not a valid subnet'.format(val) - ) + raise SaltInvocationError("'{0}' is not a valid subnet".format(val)) except RuntimeError: pass @@ -156,7 +152,7 @@ def translate_int(val): try: val = int(val) except (TypeError, ValueError): - raise SaltInvocationError('\'{0}\' is not an integer'.format(val)) + raise SaltInvocationError("'{0}' is not an integer".format(val)) return val @@ -165,19 +161,19 @@ def translate_bool(val): def translate_dict(val): - ''' + """ Not really translating, just raising an exception if it's not a dict - ''' + """ if not isinstance(val, dict): - raise SaltInvocationError('\'{0}\' is not a dictionary'.format(val)) + raise SaltInvocationError("'{0}' is not a dictionary".format(val)) return val def translate_command(val): - ''' + """ Input should either be a single string, or a list of strings. This is used for the two args that deal with commands ("command" and "entrypoint"). - ''' + """ if isinstance(val, six.string_types): return val elif isinstance(val, list): @@ -191,10 +187,10 @@ def translate_command(val): def translate_bytes(val): - ''' + """ These values can be expressed as an integer number of bytes, or a string expression (i.e. 100mb, 1gb, etc.). - ''' + """ try: val = int(val) except (TypeError, ValueError): @@ -204,13 +200,13 @@ def translate_bytes(val): def translate_stringlist(val): - ''' + """ On the CLI, these are passed as multiple instances of a given CLI option. In Salt, we accept these as a comma-delimited list but the API expects a Python list. This function accepts input and returns it back as a Python list of strings. If the input is a string which is a comma-separated list of items, split that string and return it. - ''' + """ if not isinstance(val, list): try: val = split(val) @@ -223,45 +219,45 @@ def translate_stringlist(val): def translate_device_rates(val, numeric_rate=True): - ''' + """ CLI input is a list of PATH:RATE pairs, but the API expects a list of dictionaries in the format [{'Path': path, 'Rate': rate}] - ''' - val = map_vals(val, 'Path', 'Rate') + """ + val = map_vals(val, "Path", "Rate") for idx in range(len(val)): try: - is_abs = os.path.isabs(val[idx]['Path']) + is_abs = os.path.isabs(val[idx]["Path"]) except AttributeError: is_abs = False if not is_abs: raise SaltInvocationError( - 'Path \'{Path}\' is not absolute'.format(**val[idx]) + "Path '{Path}' is not absolute".format(**val[idx]) ) # Attempt to convert to an integer. Will fail if rate was specified as # a shorthand (e.g. 1mb), this is OK as we will check to make sure the # value is an integer below if that is what is required. try: - val[idx]['Rate'] = int(val[idx]['Rate']) + val[idx]["Rate"] = int(val[idx]["Rate"]) except (TypeError, ValueError): pass if numeric_rate: try: - val[idx]['Rate'] = int(val[idx]['Rate']) + val[idx]["Rate"] = int(val[idx]["Rate"]) except ValueError: raise SaltInvocationError( - 'Rate \'{Rate}\' for path \'{Path}\' is ' - 'non-numeric'.format(**val[idx]) + "Rate '{Rate}' for path '{Path}' is " + "non-numeric".format(**val[idx]) ) return val -def translate_key_val(val, delimiter='='): - ''' +def translate_key_val(val, delimiter="="): + """ CLI input is a list of key/val pairs, but the API expects a dictionary in the format {key: val} - ''' + """ if isinstance(val, dict): return val val = translate_stringlist(val) @@ -271,18 +267,18 @@ def translate_key_val(val, delimiter='='): lvalue, rvalue = split(item, delimiter, 1) except (AttributeError, TypeError, ValueError): raise SaltInvocationError( - '\'{0}\' is not a key{1}value pair'.format(item, delimiter) + "'{0}' is not a key{1}value pair".format(item, delimiter) ) new_val[lvalue] = rvalue return new_val def translate_labels(val): - ''' + """ Can either be a list of label names, or a list of name=value pairs. The API can accept either a list of label names or a dictionary mapping names to values, so the value we translate will be different depending on the input. - ''' + """ if not isinstance(val, dict): if not isinstance(val, list): val = split(val) @@ -290,15 +286,15 @@ def translate_labels(val): for item in val: if isinstance(item, dict): if len(item) != 1: - raise SaltInvocationError('Invalid label(s)') + raise SaltInvocationError("Invalid label(s)") key = next(iter(item)) val = item[key] else: try: - key, val = split(item, '=', 1) + key, val = split(item, "=", 1) except ValueError: key = item - val = '' + val = "" if not isinstance(key, six.string_types): key = six.text_type(key) if not isinstance(val, six.string_types): diff --git a/salt/utils/docker/translate/network.py b/salt/utils/docker/translate/network.py index 7b7e561406d..f19def73833 100644 --- a/salt/utils/docker/translate/network.py +++ b/salt/utils/docker/translate/network.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Functions to translate input for network creation -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -15,25 +15,27 @@ from salt.ext import six from . import helpers ALIASES = { - 'driver_opt': 'options', - 'driver_opts': 'options', - 'ipv6': 'enable_ipv6', + "driver_opt": "options", + "driver_opts": "options", + "ipv6": "enable_ipv6", } IPAM_ALIASES = { - 'ip_range': 'iprange', - 'aux_address': 'aux_addresses', + "ip_range": "iprange", + "aux_address": "aux_addresses", } # ALIASES is a superset of IPAM_ALIASES ALIASES.update(IPAM_ALIASES) ALIASES_REVMAP = dict([(y, x) for x, y in six.iteritems(ALIASES)]) -DEFAULTS = {'check_duplicate': True} +DEFAULTS = {"check_duplicate": True} -def _post_processing(kwargs, skip_translate, invalid): # pylint: disable=unused-argument - ''' +def _post_processing( + kwargs, skip_translate, invalid +): # pylint: disable=unused-argument + """ Additional network-specific post-translation processing - ''' + """ # If any defaults were not expicitly passed, add them for item in DEFAULTS: if item not in kwargs: @@ -46,7 +48,7 @@ def driver(val, **kwargs): # pylint: disable=unused-argument def options(val, **kwargs): # pylint: disable=unused-argument - return helpers.translate_key_val(val, delimiter='=') + return helpers.translate_key_val(val, delimiter="=") def ipam(val, **kwargs): # pylint: disable=unused-argument @@ -87,13 +89,12 @@ def ipam_opts(val, **kwargs): # pylint: disable=unused-argument def ipam_pools(val, **kwargs): # pylint: disable=unused-argument - if not hasattr(val, '__iter__') \ - or not all(isinstance(x, dict) for x in val): + if not hasattr(val, "__iter__") or not all(isinstance(x, dict) for x in val): # Can't do a simple dictlist check because each dict may have more than # one element. - raise SaltInvocationError('ipam_pools must be a list of dictionaries') - skip_translate = kwargs.get('skip_translate', ()) - if not (skip_translate is True or 'ipam_pools' in skip_translate): + raise SaltInvocationError("ipam_pools must be a list of dictionaries") + skip_translate = kwargs.get("skip_translate", ()) + if not (skip_translate is True or "ipam_pools" in skip_translate): _globals = globals() for ipam_dict in val: for key in list(ipam_dict): @@ -110,7 +111,7 @@ def ipam_pools(val, **kwargs): # pylint: disable=unused-argument def subnet(val, **kwargs): # pylint: disable=unused-argument - validate_ip_addrs = kwargs.get('validate_ip_addrs', True) + validate_ip_addrs = kwargs.get("validate_ip_addrs", True) val = helpers.translate_str(val) if validate_ip_addrs: helpers.validate_subnet(val) @@ -118,7 +119,7 @@ def subnet(val, **kwargs): # pylint: disable=unused-argument def iprange(val, **kwargs): # pylint: disable=unused-argument - validate_ip_addrs = kwargs.get('validate_ip_addrs', True) + validate_ip_addrs = kwargs.get("validate_ip_addrs", True) val = helpers.translate_str(val) if validate_ip_addrs: helpers.validate_subnet(val) @@ -126,7 +127,7 @@ def iprange(val, **kwargs): # pylint: disable=unused-argument def gateway(val, **kwargs): # pylint: disable=unused-argument - validate_ip_addrs = kwargs.get('validate_ip_addrs', True) + validate_ip_addrs = kwargs.get("validate_ip_addrs", True) val = helpers.translate_str(val) if validate_ip_addrs: helpers.validate_ip(val) @@ -134,8 +135,8 @@ def gateway(val, **kwargs): # pylint: disable=unused-argument def aux_addresses(val, **kwargs): # pylint: disable=unused-argument - validate_ip_addrs = kwargs.get('validate_ip_addrs', True) - val = helpers.translate_key_val(val, delimiter='=') + validate_ip_addrs = kwargs.get("validate_ip_addrs", True) + val = helpers.translate_key_val(val, delimiter="=") if validate_ip_addrs: for address in six.itervalues(val): helpers.validate_ip(address) diff --git a/salt/utils/environment.py b/salt/utils/environment.py index 2bd37e17c45..d6b8a57a8f5 100644 --- a/salt/utils/environment.py +++ b/salt/utils/environment.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Environment utilities. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import os def get_module_environment(env=None, function=None): - ''' + """ Get module optional environment. To setup an environment option for a particular module, @@ -42,24 +43,34 @@ def get_module_environment(env=None, function=None): :param env: :param function: name of a particular function :return: dict - ''' + """ result = {} if not env: env = {} - for env_src in [env.get('__opts__', {}), env.get('__pillar__', {})]: - fname = env.get('__file__', '') - physical_name = os.path.basename(fname).split('.')[0] + for env_src in [env.get("__opts__", {}), env.get("__pillar__", {})]: + fname = env.get("__file__", "") + physical_name = os.path.basename(fname).split(".")[0] section = os.path.basename(os.path.dirname(fname)) - m_names = [env.get('__virtualname__')] + m_names = [env.get("__virtualname__")] if physical_name not in m_names: m_names.append(physical_name) for m_name in m_names: if not m_name: continue - result.update(env_src.get('system-environment', {}).get( - section, {}).get(m_name, {}).get('_', {}).copy()) + result.update( + env_src.get("system-environment", {}) + .get(section, {}) + .get(m_name, {}) + .get("_", {}) + .copy() + ) if function is not None: - result.update(env_src.get('system-environment', {}).get( - section, {}).get(m_name, {}).get(function, {}).copy()) + result.update( + env_src.get("system-environment", {}) + .get(section, {}) + .get(m_name, {}) + .get(function, {}) + .copy() + ) return result diff --git a/salt/utils/error.py b/salt/utils/error.py index a94d20c4690..030bf3a5b79 100644 --- a/salt/utils/error.py +++ b/salt/utils/error.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Utilities to enable exception reraising across the master commands -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.exceptions @@ -13,19 +13,19 @@ import salt.utils.event from salt.ext.six.moves import builtins as exceptions -def raise_error(name=None, args=None, message=''): - ''' +def raise_error(name=None, args=None, message=""): + """ Raise an exception with __name__ from name, args from args If args is None Otherwise message from message\ If name is empty then use "Exception" - ''' - name = name or 'Exception' + """ + name = name or "Exception" if hasattr(salt.exceptions, name): ex = getattr(salt.exceptions, name) elif hasattr(exceptions, name): ex = getattr(exceptions, name) else: - name = 'SaltException' + name = "SaltException" ex = getattr(salt.exceptions, name) if args is not None: raise ex(*args) @@ -34,18 +34,18 @@ def raise_error(name=None, args=None, message=''): def pack_exception(exc): - if hasattr(exc, 'pack'): + if hasattr(exc, "pack"): packed_exception = exc.pack() else: - packed_exception = {'message': exc.__unicode__(), 'args': exc.args} + packed_exception = {"message": exc.__unicode__(), "args": exc.args} return packed_exception -def fire_exception(exc, opts, job=None, node='minion'): - ''' +def fire_exception(exc, opts, job=None, node="minion"): + """ Fire raw exception across the event bus - ''' + """ if job is None: job = {} event = salt.utils.event.SaltEvent(node, opts=opts, listen=False) - event.fire_event(pack_exception(exc), '_salt_error') + event.fire_event(pack_exception(exc), "_salt_error") diff --git a/salt/utils/etcd_util.py b/salt/utils/etcd_util.py index 21423dbd801..0b40319b146 100644 --- a/salt/utils/etcd_util.py +++ b/salt/utils/etcd_util.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Utilities for working with etcd .. versionadded:: 2014.7.0 @@ -49,20 +49,22 @@ You may also use the newer syntax and bypass the generator function. It should be noted that some usages of etcd require a profile to be specified, rather than top-level configurations. This being the case, it is better to always use a named configuration profile, as shown above. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging +from salt.exceptions import CommandExecutionError + # Import salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError # Import third party libs try: import etcd from urllib3.exceptions import ReadTimeoutError, MaxRetryError + HAS_LIBS = True except ImportError: HAS_LIBS = False @@ -78,10 +80,21 @@ class EtcdUtilWatchTimeout(Exception): class EtcdClient(object): - def __init__(self, opts, profile=None, - host=None, port=None, username=None, password=None, ca=None, client_key=None, client_cert=None, **kwargs): - opts_pillar = opts.get('pillar', {}) - opts_master = opts_pillar.get('master', {}) + def __init__( + self, + opts, + profile=None, + host=None, + port=None, + username=None, + password=None, + ca=None, + client_key=None, + client_cert=None, + **kwargs + ): + opts_pillar = opts.get("pillar", {}) + opts_master = opts_pillar.get("master", {}) opts_merged = {} opts_merged.update(opts_master) @@ -93,34 +106,31 @@ class EtcdClient(object): else: self.conf = opts_merged - host = host or self.conf.get('etcd.host', '127.0.0.1') - port = port or self.conf.get('etcd.port', 2379) - username = username or self.conf.get('etcd.username') - password = password or self.conf.get('etcd.password') - ca_cert = ca or self.conf.get('etcd.ca') - cli_key = client_key or self.conf.get('etcd.client_key') - cli_cert = client_cert or self.conf.get('etcd.client_cert') + host = host or self.conf.get("etcd.host", "127.0.0.1") + port = port or self.conf.get("etcd.port", 2379) + username = username or self.conf.get("etcd.username") + password = password or self.conf.get("etcd.password") + ca_cert = ca or self.conf.get("etcd.ca") + cli_key = client_key or self.conf.get("etcd.client_key") + cli_cert = client_cert or self.conf.get("etcd.client_cert") auth = {} if username and password: auth = { - 'username': six.text_type(username), - 'password': six.text_type(password) + "username": six.text_type(username), + "password": six.text_type(password), } certs = {} if ca_cert and not (cli_cert or cli_key): - certs = { - 'ca_cert': six.text_type(ca_cert), - 'protocol': 'https' - } + certs = {"ca_cert": six.text_type(ca_cert), "protocol": "https"} if ca_cert and cli_cert and cli_key: cert = (cli_cert, cli_key) certs = { - 'ca_cert': six.text_type(ca_cert), - 'cert': cert, - 'protocol': 'https' + "ca_cert": six.text_type(ca_cert), + "cert": cert, + "protocol": "https", } xargs = auth.copy() @@ -130,20 +140,15 @@ class EtcdClient(object): self.client = etcd.Client(host, port, **xargs) else: raise CommandExecutionError( - '(unable to import etcd, ' - 'module most likely not installed)' + "(unable to import etcd, " "module most likely not installed)" ) def watch(self, key, recurse=False, timeout=0, index=None): - ret = { - 'key': key, - 'value': None, - 'changed': False, - 'mIndex': 0, - 'dir': False - } + ret = {"key": key, "value": None, "changed": False, "mIndex": 0, "dir": False} try: - result = self.read(key, recursive=recurse, wait=True, timeout=timeout, waitIndex=index) + result = self.read( + key, recursive=recurse, wait=True, timeout=timeout, waitIndex=index + ) except EtcdUtilWatchTimeout: try: result = self.read(key) @@ -153,13 +158,16 @@ class EtcdClient(object): except ValueError: return {} if result and getattr(result, "dir"): - ret['dir'] = True - ret['value'] = getattr(result, 'value') - ret['mIndex'] = getattr(result, 'modifiedIndex') + ret["dir"] = True + ret["value"] = getattr(result, "value") + ret["mIndex"] = getattr(result, "modifiedIndex") return ret except (etcd.EtcdConnectionFailed, MaxRetryError): # This gets raised when we can't contact etcd at all - log.error("etcd: failed to perform 'watch' operation on key %s due to connection error", key) + log.error( + "etcd: failed to perform 'watch' operation on key %s due to connection error", + key, + ) return {} except ValueError: return {} @@ -168,11 +176,11 @@ class EtcdClient(object): return {} if recurse: - ret['key'] = getattr(result, 'key', None) - ret['value'] = getattr(result, 'value', None) - ret['dir'] = getattr(result, 'dir', None) - ret['changed'] = True - ret['mIndex'] = getattr(result, 'modifiedIndex') + ret["key"] = getattr(result, "key", None) + ret["value"] = getattr(result, "value", None) + ret["dir"] = getattr(result, "dir", None) + ret["changed"] = True + ret["mIndex"] = getattr(result, "modifiedIndex") return ret def get(self, key, recurse=False): @@ -183,19 +191,30 @@ class EtcdClient(object): # anything here but return return None except etcd.EtcdConnectionFailed: - log.error("etcd: failed to perform 'get' operation on key %s due to connection error", key) + log.error( + "etcd: failed to perform 'get' operation on key %s due to connection error", + key, + ) return None except ValueError: return None - return getattr(result, 'value', None) + return getattr(result, "value", None) def read(self, key, recursive=False, wait=False, timeout=None, waitIndex=None): try: if waitIndex: - result = self.client.read(key, recursive=recursive, wait=wait, timeout=timeout, waitIndex=waitIndex) + result = self.client.read( + key, + recursive=recursive, + wait=wait, + timeout=timeout, + waitIndex=waitIndex, + ) else: - result = self.client.read(key, recursive=recursive, wait=wait, timeout=timeout) + result = self.client.read( + key, recursive=recursive, wait=wait, timeout=timeout + ) except (etcd.EtcdConnectionFailed, etcd.EtcdKeyNotFound) as err: log.error("etcd: %s", err) raise @@ -224,24 +243,26 @@ class EtcdClient(object): except ValueError: # python-etcd doesn't fully support python 2.6 and ends up throwing this for *any* exception because # it uses the newer {} format syntax - log.error("etcd: error. python-etcd does not fully support python 2.6, no error information available") + log.error( + "etcd: error. python-etcd does not fully support python 2.6, no error information available" + ) raise except Exception as err: # pylint: disable=broad-except - log.error('etcd: uncaught exception %s', err) + log.error("etcd: uncaught exception %s", err) raise return result - def _flatten(self, data, path=''): + def _flatten(self, data, path=""): if len(data.keys()) == 0: return {path: {}} - path = path.strip('/') + path = path.strip("/") flat = {} for k, v in six.iteritems(data): - k = k.strip('/') + k = k.strip("/") if path: - p = '/{0}/{1}'.format(path, k) + p = "/{0}/{1}".format(path, k) else: - p = '/{0}'.format(k) + p = "/{0}".format(k) if isinstance(v, dict): ret = self._flatten(v, p) flat.update(ret) @@ -249,9 +270,9 @@ class EtcdClient(object): flat[p] = v return flat - def update(self, fields, path=''): + def update(self, fields, path=""): if not isinstance(fields, dict): - log.error('etcd.update: fields is not type dict') + log.error("etcd.update: fields is not type dict") return None fields = self._flatten(fields, path) keys = {} @@ -276,41 +297,41 @@ class EtcdClient(object): except (etcd.EtcdNotFile, etcd.EtcdRootReadOnly, ValueError) as err: # If EtcdNotFile is raised, then this key is a directory and # really this is a name collision. - log.error('etcd: %s', err) + log.error("etcd: %s", err) return None except MaxRetryError as err: log.error("etcd: Could not connect to etcd server: %s", err) return None except Exception as err: # pylint: disable=broad-except - log.error('etcd: uncaught exception %s', err) + log.error("etcd: uncaught exception %s", err) raise - return getattr(result, 'value') + return getattr(result, "value") def write_directory(self, key, value, ttl=None): if value is not None: - log.info('etcd: non-empty value passed for directory: %s', value) + log.info("etcd: non-empty value passed for directory: %s", value) try: # directories can't have values, but have to have it passed result = self.client.write(key, None, ttl=ttl, dir=True) except etcd.EtcdNotFile: # When a directory already exists, python-etcd raises an EtcdNotFile # exception. In this case, we just catch and return True for success. - log.info('etcd: directory already exists: %s', key) + log.info("etcd: directory already exists: %s", key) return True except (etcd.EtcdNotDir, etcd.EtcdRootReadOnly, ValueError) as err: # If EtcdNotDir is raised, then the specified path is a file and # thus this is an error. - log.error('etcd: %s', err) + log.error("etcd: %s", err) return None except MaxRetryError as err: log.error("etcd: Could not connect to etcd server: %s", err) return None except Exception as err: # pylint: disable=broad-except - log.error('etcd: uncaught exception %s', err) + log.error("etcd: uncaught exception %s", err) raise - return getattr(result, 'dir') + return getattr(result, "dir") def ls(self, path): ret = {} @@ -319,14 +340,17 @@ class EtcdClient(object): except (etcd.EtcdKeyNotFound, ValueError): return {} except etcd.EtcdConnectionFailed: - log.error("etcd: failed to perform 'ls' operation on path %s due to connection error", path) + log.error( + "etcd: failed to perform 'ls' operation on path %s due to connection error", + path, + ) return None for item in items.children: if item.dir is True: if item.key == path: continue - dir_name = '{0}/'.format(item.key) + dir_name = "{0}/".format(item.key) ret[dir_name] = {} else: ret[item.key] = item.value @@ -341,33 +365,42 @@ class EtcdClient(object): return True else: return False - except (etcd.EtcdNotFile, etcd.EtcdRootReadOnly, etcd.EtcdDirNotEmpty, etcd.EtcdKeyNotFound, ValueError) as err: - log.error('etcd: %s', err) + except ( + etcd.EtcdNotFile, + etcd.EtcdRootReadOnly, + etcd.EtcdDirNotEmpty, + etcd.EtcdKeyNotFound, + ValueError, + ) as err: + log.error("etcd: %s", err) return None except MaxRetryError as err: - log.error('etcd: Could not connect to etcd server: %s', err) + log.error("etcd: Could not connect to etcd server: %s", err) return None except Exception as err: # pylint: disable=broad-except - log.error('etcd: uncaught exception %s', err) + log.error("etcd: uncaught exception %s", err) raise def tree(self, path): - ''' + """ .. versionadded:: 2014.7.0 Recurse through etcd and return all values - ''' + """ ret = {} try: items = self.read(path) except (etcd.EtcdKeyNotFound, ValueError): return None except etcd.EtcdConnectionFailed: - log.error("etcd: failed to perform 'tree' operation on path %s due to connection error", path) + log.error( + "etcd: failed to perform 'tree' operation on path %s due to connection error", + path, + ) return None for item in items.children: - comps = six.text_type(item.key).split('/') + comps = six.text_type(item.key).split("/") if item.dir is True: if item.key == path: continue diff --git a/salt/utils/event.py b/salt/utils/event.py index 0a3074a6f70..234217a0112 100644 --- a/salt/utils/event.py +++ b/salt/utils/event.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage events Events are all fired off via a zeromq 'pub' socket, and listened to with local @@ -48,34 +48,29 @@ For example: Namespaced tag 'salt.runner.manage.status.start' -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import datetime +import fnmatch +import hashlib +import logging # Import python libs import os import time -import fnmatch -import hashlib -import logging -import datetime - -try: - from collections.abc import MutableMapping -except ImportError: - from collections import MutableMapping - from multiprocessing.util import Finalize -from salt.ext.six.moves import range - -# Import third party libs -from salt.ext import six -import salt.ext.tornado.ioloop -import salt.ext.tornado.iostream # Import salt libs import salt.config +import salt.defaults.exitcodes +import salt.ext.tornado.ioloop +import salt.ext.tornado.iostream +import salt.log.setup import salt.payload +import salt.transport.client +import salt.transport.ipc import salt.utils.asynchronous import salt.utils.cache import salt.utils.dicttrim @@ -84,98 +79,119 @@ import salt.utils.platform import salt.utils.process import salt.utils.stringutils import salt.utils.zeromq -import salt.log.setup -import salt.defaults.exitcodes -import salt.transport.ipc -import salt.transport.client + +# Import third party libs +from salt.ext import six +from salt.ext.six.moves import range + +try: + from collections.abc import MutableMapping +except ImportError: + # pylint: disable=no-name-in-module + from collections import MutableMapping + + # pylint: enable=no-name-in-module + log = logging.getLogger(__name__) # The SUB_EVENT set is for functions that require events fired based on # component executions, like the state system -SUB_EVENT = ('state.highstate', 'state.sls') +SUB_EVENT = ("state.highstate", "state.sls") -TAGEND = str('\n\n') # long tag delimiter -TAGPARTER = str('/') # name spaced tag delimiter -SALT = 'salt' # base prefix for all salt/ events +TAGEND = str("\n\n") # long tag delimiter +TAGPARTER = str("/") # name spaced tag delimiter +SALT = "salt" # base prefix for all salt/ events # dict map of namespaced base tag prefixes for salt events TAGS = { - 'auth': 'auth', # prefix for all salt/auth events - 'job': 'job', # prefix for all salt/job events (minion jobs) - 'key': 'key', # prefix for all salt/key events - 'minion': 'minion', # prefix for all salt/minion events - # (minion sourced events) - 'syndic': 'syndic', # prefix for all salt/syndic events - # (syndic minion sourced events) - 'run': 'run', # prefix for all salt/run events (salt runners) - 'wheel': 'wheel', # prefix for all salt/wheel events - 'cloud': 'cloud', # prefix for all salt/cloud events - 'fileserver': 'fileserver', # prefix for all salt/fileserver events - 'queue': 'queue', # prefix for all salt/queue events + "auth": "auth", # prefix for all salt/auth events + "job": "job", # prefix for all salt/job events (minion jobs) + "key": "key", # prefix for all salt/key events + "minion": "minion", # prefix for all salt/minion events + # (minion sourced events) + "syndic": "syndic", # prefix for all salt/syndic events + # (syndic minion sourced events) + "run": "run", # prefix for all salt/run events (salt runners) + "wheel": "wheel", # prefix for all salt/wheel events + "cloud": "cloud", # prefix for all salt/cloud events + "fileserver": "fileserver", # prefix for all salt/fileserver events + "queue": "queue", # prefix for all salt/queue events } def get_event( - node, sock_dir=None, transport='zeromq', - opts=None, listen=True, io_loop=None, keep_loop=False, raise_errors=False): - ''' + node, + sock_dir=None, + transport="zeromq", + opts=None, + listen=True, + io_loop=None, + keep_loop=False, + raise_errors=False, +): + """ Return an event object suitable for the named transport :param IOLoop io_loop: Pass in an io_loop if you want asynchronous operation for obtaining events. Eg use of set_event_handler() API. Otherwise, operation will be synchronous. - ''' - sock_dir = sock_dir or opts['sock_dir'] + """ + sock_dir = sock_dir or opts["sock_dir"] # TODO: AIO core is separate from transport - if node == 'master': - return MasterEvent(sock_dir, - opts, - listen=listen, - io_loop=io_loop, - keep_loop=keep_loop, - raise_errors=raise_errors) - return SaltEvent(node, - sock_dir, - opts, - listen=listen, - io_loop=io_loop, - keep_loop=keep_loop, - raise_errors=raise_errors) + if node == "master": + return MasterEvent( + sock_dir, + opts, + listen=listen, + io_loop=io_loop, + keep_loop=keep_loop, + raise_errors=raise_errors, + ) + return SaltEvent( + node, + sock_dir, + opts, + listen=listen, + io_loop=io_loop, + keep_loop=keep_loop, + raise_errors=raise_errors, + ) def get_master_event(opts, sock_dir, listen=True, io_loop=None, raise_errors=False): - ''' + """ Return an event object suitable for the named transport - ''' + """ # TODO: AIO core is separate from transport - if opts['transport'] in ('zeromq', 'tcp', 'detect'): - return MasterEvent(sock_dir, opts, listen=listen, io_loop=io_loop, raise_errors=raise_errors) + if opts["transport"] in ("zeromq", "tcp", "detect"): + return MasterEvent( + sock_dir, opts, listen=listen, io_loop=io_loop, raise_errors=raise_errors + ) -def fire_args(opts, jid, tag_data, prefix=''): - ''' +def fire_args(opts, jid, tag_data, prefix=""): + """ Fire an event containing the arguments passed to an orchestration job - ''' + """ try: - tag_suffix = [jid, 'args'] + tag_suffix = [jid, "args"] except NameError: pass else: tag = tagify(tag_suffix, prefix) try: - _event = get_master_event(opts, opts['sock_dir'], listen=False) + _event = get_master_event(opts, opts["sock_dir"], listen=False) _event.fire_event(tag_data, tag=tag) except Exception as exc: # pylint: disable=broad-except # Don't let a problem here hold up the rest of the orchestration log.warning( - 'Failed to fire args event %s with data %s: %s', - tag, tag_data, exc + "Failed to fire args event %s with data %s: %s", tag, tag_data, exc ) -def tagify(suffix='', prefix='', base=SALT): - ''' +def tagify(suffix="", prefix="", base=SALT): + """ convenience function to build a namespaced event tag string from joining with the TABPART character the base, prefix and suffix @@ -185,9 +201,9 @@ def tagify(suffix='', prefix='', base=SALT): If suffix is a list Then join all string elements of suffix individually Else use string suffix - ''' + """ parts = [base, TAGS.get(prefix, prefix)] - if hasattr(suffix, 'append'): # list so extend parts + if hasattr(suffix, "append"): # list so extend parts parts.extend(suffix) else: # string so append parts.append(suffix) @@ -201,16 +217,23 @@ def tagify(suffix='', prefix='', base=SALT): class SaltEvent(object): - ''' + """ Warning! Use the get_event function or the code will not be RAET compatible The base class used to manage salt events - ''' + """ + def __init__( - self, node, sock_dir=None, - opts=None, listen=True, io_loop=None, - keep_loop=False, raise_errors=False): - ''' + self, + node, + sock_dir=None, + opts=None, + listen=True, + io_loop=None, + keep_loop=False, + raise_errors=False, + ): + """ :param IOLoop io_loop: Pass in an io_loop if you want asynchronous operation for obtaining events. Eg use of set_event_handler() API. Otherwise, operation @@ -219,8 +242,8 @@ class SaltEvent(object): the io loop or destroy it when the event handle is destroyed. This is useful when using event loops from within third party asynchronous code - ''' - self.serial = salt.payload.Serial({'serial': 'msgpack'}) + """ + self.serial = salt.payload.Serial({"serial": "msgpack"}) self.keep_loop = keep_loop if io_loop is not None: self.io_loop = io_loop @@ -236,19 +259,19 @@ class SaltEvent(object): if opts is None: opts = {} - if node == 'master': + if node == "master": self.opts = salt.config.DEFAULT_MASTER_OPTS.copy() else: self.opts = salt.config.DEFAULT_MINION_OPTS.copy() self.opts.update(opts) if sock_dir is None: - sock_dir = self.opts['sock_dir'] + sock_dir = self.opts["sock_dir"] else: - self.opts['sock_dir'] = sock_dir + self.opts["sock_dir"] = sock_dir - if salt.utils.platform.is_windows() and 'ipc_mode' not in opts: - self.opts['ipc_mode'] = 'tcp' + if salt.utils.platform.is_windows() and "ipc_mode" not in opts: + self.opts["ipc_mode"] = "tcp" self.puburi, self.pulluri = self.__load_uri(sock_dir, node) self.pending_tags = [] self.pending_events = [] @@ -262,57 +285,51 @@ class SaltEvent(object): @classmethod def __load_cache_regex(cls): - ''' + """ Initialize the regular expression cache and put it in the class namespace. The regex search strings will be prepend with '^' - ''' + """ # This is in the class namespace, to minimize cache memory # usage and maximize cache hits # The prepend='^' is to reduce differences in behavior between # the default 'startswith' and the optional 'regex' match_type - cls.cache_regex = salt.utils.cache.CacheRegex(prepend='^') + cls.cache_regex = salt.utils.cache.CacheRegex(prepend="^") def __load_uri(self, sock_dir, node): - ''' + """ Return the string URI for the location of the pull and pub sockets to use for firing and listening to events - ''' - if node == 'master': - if self.opts['ipc_mode'] == 'tcp': - puburi = int(self.opts['tcp_master_pub_port']) - pulluri = int(self.opts['tcp_master_pull_port']) + """ + if node == "master": + if self.opts["ipc_mode"] == "tcp": + puburi = int(self.opts["tcp_master_pub_port"]) + pulluri = int(self.opts["tcp_master_pull_port"]) else: - puburi = os.path.join( - sock_dir, - 'master_event_pub.ipc' - ) - pulluri = os.path.join( - sock_dir, - 'master_event_pull.ipc' - ) + puburi = os.path.join(sock_dir, "master_event_pub.ipc") + pulluri = os.path.join(sock_dir, "master_event_pull.ipc") else: - if self.opts['ipc_mode'] == 'tcp': - puburi = int(self.opts['tcp_pub_port']) - pulluri = int(self.opts['tcp_pull_port']) + if self.opts["ipc_mode"] == "tcp": + puburi = int(self.opts["tcp_pub_port"]) + pulluri = int(self.opts["tcp_pull_port"]) else: - hash_type = getattr(hashlib, self.opts['hash_type']) + hash_type = getattr(hashlib, self.opts["hash_type"]) # Only use the first 10 chars to keep longer hashes from exceeding the # max socket path length. - id_hash = hash_type(salt.utils.stringutils.to_bytes(self.opts['id'])).hexdigest()[:10] + id_hash = hash_type( + salt.utils.stringutils.to_bytes(self.opts["id"]) + ).hexdigest()[:10] puburi = os.path.join( - sock_dir, - 'minion_event_{0}_pub.ipc'.format(id_hash) + sock_dir, "minion_event_{0}_pub.ipc".format(id_hash) ) pulluri = os.path.join( - sock_dir, - 'minion_event_{0}_pull.ipc'.format(id_hash) + sock_dir, "minion_event_{0}_pull.ipc".format(id_hash) ) - log.debug('%s PUB socket URI: %s', self.__class__.__name__, puburi) - log.debug('%s PULL socket URI: %s', self.__class__.__name__, pulluri) + log.debug("%s PUB socket URI: %s", self.__class__.__name__, puburi) + log.debug("%s PULL socket URI: %s", self.__class__.__name__, pulluri) return puburi, pulluri def subscribe(self, tag=None, match_type=None): - ''' + """ Subscribe to events matching the passed tag. If you do not subscribe to a tag, events will be discarded by calls to @@ -320,16 +337,16 @@ class SaltEvent(object): jobs are outstanding it is important to subscribe to prevent one call to get_event from discarding a response required by a subsequent call to get_event. - ''' + """ if tag is None: return match_func = self._get_match_func(match_type) self.pending_tags.append([tag, match_func]) def unsubscribe(self, tag, match_type=None): - ''' + """ Un-subscribe to events matching the passed tag. - ''' + """ if tag is None: return match_func = self._get_match_func(match_type) @@ -339,13 +356,15 @@ class SaltEvent(object): old_events = self.pending_events self.pending_events = [] for evt in old_events: - if any(pmatch_func(evt['tag'], ptag) for ptag, pmatch_func in self.pending_tags): + if any( + pmatch_func(evt["tag"], ptag) for ptag, pmatch_func in self.pending_tags + ): self.pending_events.append(evt) def connect_pub(self, timeout=None): - ''' + """ Establish the publish connection - ''' + """ if self.cpub: return True @@ -353,20 +372,19 @@ class SaltEvent(object): with salt.utils.asynchronous.current_ioloop(self.io_loop): if self.subscriber is None: self.subscriber = salt.transport.ipc.IPCMessageSubscriber( - self.puburi, - io_loop=self.io_loop + self.puburi, io_loop=self.io_loop ) try: self.io_loop.run_sync( - lambda: self.subscriber.connect(timeout=timeout)) + lambda: self.subscriber.connect(timeout=timeout) + ) self.cpub = True except Exception: # pylint: disable=broad-except pass else: if self.subscriber is None: self.subscriber = salt.transport.ipc.IPCMessageSubscriber( - self.puburi, - io_loop=self.io_loop + self.puburi, io_loop=self.io_loop ) # For the asynchronous case, the connect will be defered to when @@ -375,9 +393,9 @@ class SaltEvent(object): return self.cpub def close_pub(self): - ''' + """ Close the publish connection (if established) - ''' + """ if not self.cpub: return @@ -387,10 +405,10 @@ class SaltEvent(object): self.cpub = False def connect_pull(self, timeout=1): - ''' + """ Establish a connection with the event pull socket Default timeout is 1 s - ''' + """ if self.cpush: return True @@ -398,20 +416,17 @@ class SaltEvent(object): with salt.utils.asynchronous.current_ioloop(self.io_loop): if self.pusher is None: self.pusher = salt.transport.ipc.IPCMessageClient( - self.pulluri, - io_loop=self.io_loop + self.pulluri, io_loop=self.io_loop ) try: - self.io_loop.run_sync( - lambda: self.pusher.connect(timeout=timeout)) + self.io_loop.run_sync(lambda: self.pusher.connect(timeout=timeout)) self.cpush = True except Exception: # pylint: disable=broad-except pass else: if self.pusher is None: self.pusher = salt.transport.ipc.IPCMessageClient( - self.pulluri, - io_loop=self.io_loop + self.pulluri, io_loop=self.io_loop ) # For the asynchronous case, the connect will be deferred to when # fire_event() is invoked. @@ -419,9 +434,9 @@ class SaltEvent(object): return self.cpush def close_pull(self): - ''' + """ Close the pusher connection (if established) - ''' + """ if not self.cpush: return @@ -432,21 +447,23 @@ class SaltEvent(object): @classmethod def unpack(cls, raw, serial=None): if serial is None: - serial = salt.payload.Serial({'serial': 'msgpack'}) + serial = salt.payload.Serial({"serial": "msgpack"}) if six.PY2: mtag, sep, mdata = raw.partition(TAGEND) # split tag from data - data = serial.loads(mdata, encoding='utf-8') + data = serial.loads(mdata, encoding="utf-8") else: - mtag, sep, mdata = raw.partition(salt.utils.stringutils.to_bytes(TAGEND)) # split tag from data + mtag, sep, mdata = raw.partition( + salt.utils.stringutils.to_bytes(TAGEND) + ) # split tag from data mtag = salt.utils.stringutils.to_str(mtag) - data = serial.loads(mdata, encoding='utf-8') + data = serial.loads(mdata, encoding="utf-8") return mtag, data def _get_match_func(self, match_type=None): if match_type is None: - match_type = self.opts['event_match_type'] - return getattr(self, '_match_tag_{0}'.format(match_type), None) + match_type = self.opts["event_match_type"] + return getattr(self, "_match_tag_{0}".format(match_type), None) def _check_pending(self, tag, match_func=None): """Check the pending_events list for events that match the tag @@ -463,59 +480,64 @@ class SaltEvent(object): self.pending_events = [] ret = None for evt in old_events: - if match_func(evt['tag'], tag): + if match_func(evt["tag"], tag): if ret is None: ret = evt - log.trace('get_event() returning cached event = %s', ret) + log.trace("get_event() returning cached event = %s", ret) else: self.pending_events.append(evt) - elif any(pmatch_func(evt['tag'], ptag) for ptag, pmatch_func in self.pending_tags): + elif any( + pmatch_func(evt["tag"], ptag) for ptag, pmatch_func in self.pending_tags + ): self.pending_events.append(evt) else: - log.trace('get_event() discarding cached event that no longer has any subscriptions = %s', evt) + log.trace( + "get_event() discarding cached event that no longer has any subscriptions = %s", + evt, + ) return ret @staticmethod def _match_tag_startswith(event_tag, search_tag): - ''' + """ Check if the event_tag matches the search check. Uses startswith to check. Return True (matches) or False (no match) - ''' + """ return event_tag.startswith(search_tag) @staticmethod def _match_tag_endswith(event_tag, search_tag): - ''' + """ Check if the event_tag matches the search check. Uses endswith to check. Return True (matches) or False (no match) - ''' + """ return event_tag.endswith(search_tag) @staticmethod def _match_tag_find(event_tag, search_tag): - ''' + """ Check if the event_tag matches the search check. Uses find to check. Return True (matches) or False (no match) - ''' + """ return event_tag.find(search_tag) >= 0 def _match_tag_regex(self, event_tag, search_tag): - ''' + """ Check if the event_tag matches the search check. Uses regular expression search to check. Return True (matches) or False (no match) - ''' + """ return self.cache_regex.get(search_tag).search(event_tag) is not None def _match_tag_fnmatch(self, event_tag, search_tag): - ''' + """ Check if the event_tag matches the search check. Uses fnmatch to check. Return True (matches) or False (no match) - ''' + """ return fnmatch.fnmatch(event_tag, search_tag) def _get_event(self, wait, tag, match_func=None, no_block=False): @@ -545,9 +567,9 @@ class SaltEvent(object): if raw is None: break mtag, data = self.unpack(raw, self.serial) - ret = {'data': data, 'tag': mtag} + ret = {"data": data, "tag": mtag} except KeyboardInterrupt: - return {'tag': 'salt/event/exit', 'data': {}} + return {"tag": "salt/event/exit", "data": {}} except salt.ext.tornado.iostream.StreamClosedError: if self.raise_errors: raise @@ -556,28 +578,33 @@ class SaltEvent(object): except RuntimeError: return None - if not match_func(ret['tag'], tag): + if not match_func(ret["tag"], tag): # tag not match - if any(pmatch_func(ret['tag'], ptag) for ptag, pmatch_func in self.pending_tags): - log.trace('get_event() caching unwanted event = %s', ret) + if any( + pmatch_func(ret["tag"], ptag) + for ptag, pmatch_func in self.pending_tags + ): + log.trace("get_event() caching unwanted event = %s", ret) self.pending_events.append(ret) if wait: # only update the wait timeout if we had one wait = timeout_at - time.time() continue - log.trace('get_event() received = %s', ret) + log.trace("get_event() received = %s", ret) return ret - log.trace('_get_event() waited %s seconds and received nothing', wait) + log.trace("_get_event() waited %s seconds and received nothing", wait) return None - def get_event(self, - wait=5, - tag='', - full=False, - match_type=None, - no_block=False, - auto_reconnect=False): - ''' + def get_event( + self, + wait=5, + tag="", + full=False, + match_type=None, + no_block=False, + auto_reconnect=False, + ): + """ Get a single publication. If no publication is available, then block for up to ``wait`` seconds. Return publication if it is available or ``None`` if no publication is @@ -621,7 +648,7 @@ class SaltEvent(object): If a caller is not going to call get_event immediately after sending a request, it MUST subscribe the result to ensure the response is not lost should other regions of code call get_event for other purposes. - ''' + """ log.trace("Get event. tag: %s", tag) assert self._run_io_loop_sync @@ -648,12 +675,12 @@ class SaltEvent(object): if ret is None or full: return ret else: - return ret['data'] + return ret["data"] def get_event_noblock(self): - ''' + """ Get the raw event without blocking or any other niceties - ''' + """ assert self._run_io_loop_sync if not self.cpub: @@ -663,13 +690,13 @@ class SaltEvent(object): if raw is None: return None mtag, data = self.unpack(raw, self.serial) - return {'data': data, 'tag': mtag} + return {"data": data, "tag": mtag} def get_event_block(self): - ''' + """ Get the raw event in a blocking fashion. This is slower, but it decreases the possibility of dropped events. - ''' + """ assert self._run_io_loop_sync if not self.cpub: @@ -679,33 +706,32 @@ class SaltEvent(object): if raw is None: return None mtag, data = self.unpack(raw, self.serial) - return {'data': data, 'tag': mtag} + return {"data": data, "tag": mtag} - def iter_events(self, tag='', full=False, match_type=None, auto_reconnect=False): - ''' + def iter_events(self, tag="", full=False, match_type=None, auto_reconnect=False): + """ Creates a generator that continuously listens for events - ''' + """ while True: - data = self.get_event(tag=tag, full=full, match_type=match_type, - auto_reconnect=auto_reconnect) + data = self.get_event( + tag=tag, full=full, match_type=match_type, auto_reconnect=auto_reconnect + ) if data is None: continue yield data def fire_event(self, data, tag, timeout=1000): - ''' + """ Send a single event into the publisher with payload dict "data" and event identifier "tag" The default is 1000 ms - ''' + """ if not six.text_type(tag): # no empty tags allowed - raise ValueError('Empty tag.') + raise ValueError("Empty tag.") if not isinstance(data, MutableMapping): # data must be dict - raise ValueError( - 'Dict object expected, not \'{0}\'.'.format(data) - ) + raise ValueError("Dict object expected, not '{0}'.".format(data)) if not self.cpush: if timeout is not None: @@ -715,7 +741,7 @@ class SaltEvent(object): if not self.connect_pull(timeout=timeout_s): return False - data['_stamp'] = datetime.datetime.utcnow().isoformat() + data["_stamp"] = datetime.datetime.utcnow().isoformat() tagend = TAGEND if six.PY2: @@ -729,16 +755,19 @@ class SaltEvent(object): serialized_data = salt.utils.dicttrim.trim_dict( dump_data, - self.opts['max_event_size'], + self.opts["max_event_size"], is_msgpacked=True, - use_bin_type=six.PY3 + use_bin_type=six.PY3, ) - log.debug('Sending event: tag = %s; data = %s', tag, data) - event = b''.join([ - salt.utils.stringutils.to_bytes(tag), - salt.utils.stringutils.to_bytes(tagend), - serialized_data]) - msg = salt.utils.stringutils.to_bytes(event, 'utf-8') + log.debug("Sending event: tag = %s; data = %s", tag, data) + event = b"".join( + [ + salt.utils.stringutils.to_bytes(tag), + salt.utils.stringutils.to_bytes(tagend), + serialized_data, + ] + ) + msg = salt.utils.stringutils.to_bytes(event, "utf-8") if self._run_io_loop_sync: with salt.utils.asynchronous.current_ioloop(self.io_loop): try: @@ -751,18 +780,13 @@ class SaltEvent(object): return True def fire_master(self, data, tag, timeout=1000): - '''' + """' Send a single event to the master, with the payload "data" and the event identifier "tag". Default timeout is 1000ms - ''' - msg = { - 'tag': tag, - 'data': data, - 'events': None, - 'pretag': None - } + """ + msg = {"tag": tag, "data": data, "events": None, "pretag": None} return self.fire_event(msg, "fire_master", timeout) def destroy(self): @@ -774,93 +798,89 @@ class SaltEvent(object): self.io_loop.close() def _fire_ret_load_specific_fun(self, load, fun_index=0): - ''' + """ Helper function for fire_ret_load - ''' - if isinstance(load['fun'], list): + """ + if isinstance(load["fun"], list): # Multi-function job - fun = load['fun'][fun_index] + fun = load["fun"][fun_index] # 'retcode' was already validated to exist and be non-zero # for the given function in the caller. - if isinstance(load['retcode'], list): + if isinstance(load["retcode"], list): # Multi-function ordered - ret = load.get('return') + ret = load.get("return") if isinstance(ret, list) and len(ret) > fun_index: ret = ret[fun_index] else: ret = {} - retcode = load['retcode'][fun_index] + retcode = load["retcode"][fun_index] else: - ret = load.get('return', {}) + ret = load.get("return", {}) ret = ret.get(fun, {}) - retcode = load['retcode'][fun] + retcode = load["retcode"][fun] else: # Single-function job - fun = load['fun'] - ret = load.get('return', {}) - retcode = load['retcode'] + fun = load["fun"] + ret = load.get("return", {}) + retcode = load["retcode"] try: for tag, data in six.iteritems(ret): - data['retcode'] = retcode - tags = tag.split('_|-') - if data.get('result') is False: + data["retcode"] = retcode + tags = tag.split("_|-") + if data.get("result") is False: self.fire_event( - data, - '{0}.{1}'.format(tags[0], tags[-1]) + data, "{0}.{1}".format(tags[0], tags[-1]) ) # old dup event - data['jid'] = load['jid'] - data['id'] = load['id'] - data['success'] = False - data['return'] = 'Error: {0}.{1}'.format( - tags[0], tags[-1]) - data['fun'] = fun - data['user'] = load['user'] + data["jid"] = load["jid"] + data["id"] = load["id"] + data["success"] = False + data["return"] = "Error: {0}.{1}".format(tags[0], tags[-1]) + data["fun"] = fun + data["user"] = load["user"] self.fire_event( data, - tagify([load['jid'], - 'sub', - load['id'], - 'error', - fun], - 'job')) + tagify([load["jid"], "sub", load["id"], "error", fun], "job"), + ) except Exception: # pylint: disable=broad-except pass def fire_ret_load(self, load): - ''' + """ Fire events based on information in the return load - ''' - if load.get('retcode') and load.get('fun'): - if isinstance(load['fun'], list): + """ + if load.get("retcode") and load.get("fun"): + if isinstance(load["fun"], list): # Multi-function job - if isinstance(load['retcode'], list): + if isinstance(load["retcode"], list): multifunc_ordered = True else: multifunc_ordered = False - for fun_index in range(0, len(load['fun'])): - fun = load['fun'][fun_index] + for fun_index in range(0, len(load["fun"])): + fun = load["fun"][fun_index] if multifunc_ordered: - if (len(load['retcode']) > fun_index and - load['retcode'][fun_index] and - fun in SUB_EVENT): + if ( + len(load["retcode"]) > fun_index + and load["retcode"][fun_index] + and fun in SUB_EVENT + ): # Minion fired a bad retcode, fire an event self._fire_ret_load_specific_fun(load, fun_index) else: - if load['retcode'].get(fun, 0) and fun in SUB_EVENT: + if load["retcode"].get(fun, 0) and fun in SUB_EVENT: # Minion fired a bad retcode, fire an event self._fire_ret_load_specific_fun(load, fun_index) else: # Single-function job - if load['fun'] in SUB_EVENT: + if load["fun"] in SUB_EVENT: # Minion fired a bad retcode, fire an event self._fire_ret_load_specific_fun(load) def set_event_handler(self, event_handler): - ''' + """ Invoke the event_handler callback each time an event arrives. - ''' + """ assert not self._run_io_loop_sync if not self.cpub: @@ -876,6 +896,7 @@ class SaltEvent(object): self.destroy() except Exception: # pylint: disable=broad-except pass + # pylint: enable=W1701 def __enter__(self): @@ -886,42 +907,46 @@ class SaltEvent(object): class MasterEvent(SaltEvent): - ''' + """ Warning! Use the get_event function or the code will not be RAET compatible Create a master event management object - ''' + """ + def __init__( - self, - sock_dir, - opts=None, - listen=True, - io_loop=None, - keep_loop=False, - raise_errors=False): + self, + sock_dir, + opts=None, + listen=True, + io_loop=None, + keep_loop=False, + raise_errors=False, + ): super(MasterEvent, self).__init__( - 'master', + "master", sock_dir, opts, listen=listen, io_loop=io_loop, keep_loop=keep_loop, - raise_errors=raise_errors) + raise_errors=raise_errors, + ) class LocalClientEvent(MasterEvent): - ''' + """ Warning! Use the get_event function or the code will not be RAET compatible This class is just used to differentiate who is handling the events, specially on logs, but it's the same as MasterEvent. - ''' + """ class NamespacedEvent(object): - ''' + """ A wrapper for sending events within a specific base namespace - ''' + """ + def __init__(self, event, base, print_func=None): self.event = event self.base = base @@ -943,62 +968,66 @@ class NamespacedEvent(object): class MinionEvent(SaltEvent): - ''' + """ Warning! Use the get_event function or the code will not be RAET compatible Create a master event management object - ''' + """ + def __init__(self, opts, listen=True, io_loop=None, raise_errors=False): super(MinionEvent, self).__init__( - 'minion', sock_dir=opts.get('sock_dir'), - opts=opts, listen=listen, io_loop=io_loop, - raise_errors=raise_errors) + "minion", + sock_dir=opts.get("sock_dir"), + opts=opts, + listen=listen, + io_loop=io_loop, + raise_errors=raise_errors, + ) class AsyncEventPublisher(object): - ''' + """ An event publisher class intended to run in an ioloop (within a single process) TODO: remove references to "minion_event" whenever we need to use this for other things - ''' + """ + def __init__(self, opts, io_loop=None): self.opts = salt.config.DEFAULT_MINION_OPTS.copy() - default_minion_sock_dir = self.opts['sock_dir'] + default_minion_sock_dir = self.opts["sock_dir"] self.opts.update(opts) self.io_loop = io_loop or salt.ext.tornado.ioloop.IOLoop.current() self._closing = False - hash_type = getattr(hashlib, self.opts['hash_type']) + hash_type = getattr(hashlib, self.opts["hash_type"]) # Only use the first 10 chars to keep longer hashes from exceeding the # max socket path length. - id_hash = hash_type(salt.utils.stringutils.to_bytes(self.opts['id'])).hexdigest()[:10] + id_hash = hash_type( + salt.utils.stringutils.to_bytes(self.opts["id"]) + ).hexdigest()[:10] epub_sock_path = os.path.join( - self.opts['sock_dir'], - 'minion_event_{0}_pub.ipc'.format(id_hash) + self.opts["sock_dir"], "minion_event_{0}_pub.ipc".format(id_hash) ) if os.path.exists(epub_sock_path): os.unlink(epub_sock_path) epull_sock_path = os.path.join( - self.opts['sock_dir'], - 'minion_event_{0}_pull.ipc'.format(id_hash) + self.opts["sock_dir"], "minion_event_{0}_pull.ipc".format(id_hash) ) if os.path.exists(epull_sock_path): os.unlink(epull_sock_path) - if self.opts['ipc_mode'] == 'tcp': - epub_uri = int(self.opts['tcp_pub_port']) - epull_uri = int(self.opts['tcp_pull_port']) + if self.opts["ipc_mode"] == "tcp": + epub_uri = int(self.opts["tcp_pub_port"]) + epull_uri = int(self.opts["tcp_pull_port"]) else: epub_uri = epub_sock_path epull_uri = epull_sock_path - log.debug('%s PUB socket URI: %s', - self.__class__.__name__, epub_uri) - log.debug('%s PULL socket URI: %s', - self.__class__.__name__, epull_uri) + log.debug("%s PUB socket URI: %s", self.__class__.__name__, epub_uri) + log.debug("%s PULL socket URI: %s", self.__class__.__name__, epull_uri) - minion_sock_dir = self.opts['sock_dir'] + minion_sock_dir = self.opts["sock_dir"] if not os.path.isdir(minion_sock_dir): # Let's try to create the directory defined on the configuration @@ -1006,7 +1035,7 @@ class AsyncEventPublisher(object): try: os.makedirs(minion_sock_dir, 0o755) except OSError as exc: - log.error('Could not create SOCK_DIR: %s', exc) + log.error("Could not create SOCK_DIR: %s", exc) # Let's not fail yet and try using the default path if minion_sock_dir == default_minion_sock_dir: # We're already trying the default system path, stop now! @@ -1016,60 +1045,57 @@ class AsyncEventPublisher(object): try: os.makedirs(default_minion_sock_dir, 0o755) except OSError as exc: - log.error('Could not create SOCK_DIR: %s', exc) + log.error("Could not create SOCK_DIR: %s", exc) # Let's stop at this stage raise self.publisher = salt.transport.ipc.IPCMessagePublisher( - self.opts, - epub_uri, - io_loop=self.io_loop + self.opts, epub_uri, io_loop=self.io_loop ) self.puller = salt.transport.ipc.IPCMessageServer( - epull_uri, - io_loop=self.io_loop, - payload_handler=self.handle_publish + epull_uri, io_loop=self.io_loop, payload_handler=self.handle_publish ) - log.info('Starting pull socket on %s', epull_uri) + log.info("Starting pull socket on %s", epull_uri) with salt.utils.files.set_umask(0o177): self.publisher.start() self.puller.start() def handle_publish(self, package, _): - ''' + """ Get something from epull, publish it out epub, and return the package (or None) - ''' + """ try: self.publisher.publish(package) return package # Add an extra fallback in case a forked process leeks through except Exception: # pylint: disable=broad-except - log.critical('Unexpected error while polling minion events', - exc_info=True) + log.critical("Unexpected error while polling minion events", exc_info=True) return None def close(self): if self._closing: return self._closing = True - if hasattr(self, 'publisher'): + if hasattr(self, "publisher"): self.publisher.close() - if hasattr(self, 'puller'): + if hasattr(self, "puller"): self.puller.close() # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 class EventPublisher(salt.utils.process.SignalHandlingProcess): - ''' + """ The interface that takes master events and republishes them out to anyone who wants to listen - ''' + """ + def __init__(self, opts, **kwargs): super(EventPublisher, self).__init__(**kwargs) self.opts = salt.config.DEFAULT_MASTER_OPTS.copy() @@ -1081,59 +1107,51 @@ class EventPublisher(salt.utils.process.SignalHandlingProcess): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['opts'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["opts"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def run(self): - ''' + """ Bind the pub and pull sockets for events - ''' + """ salt.utils.process.appendproctitle(self.__class__.__name__) self.io_loop = salt.ext.tornado.ioloop.IOLoop() with salt.utils.asynchronous.current_ioloop(self.io_loop): - if self.opts['ipc_mode'] == 'tcp': - epub_uri = int(self.opts['tcp_master_pub_port']) - epull_uri = int(self.opts['tcp_master_pull_port']) + if self.opts["ipc_mode"] == "tcp": + epub_uri = int(self.opts["tcp_master_pub_port"]) + epull_uri = int(self.opts["tcp_master_pull_port"]) else: - epub_uri = os.path.join( - self.opts['sock_dir'], - 'master_event_pub.ipc' - ) - epull_uri = os.path.join( - self.opts['sock_dir'], - 'master_event_pull.ipc' - ) + epub_uri = os.path.join(self.opts["sock_dir"], "master_event_pub.ipc") + epull_uri = os.path.join(self.opts["sock_dir"], "master_event_pull.ipc") self.publisher = salt.transport.ipc.IPCMessagePublisher( - self.opts, - epub_uri, - io_loop=self.io_loop + self.opts, epub_uri, io_loop=self.io_loop ) self.puller = salt.transport.ipc.IPCMessageServer( - epull_uri, - io_loop=self.io_loop, - payload_handler=self.handle_publish, + epull_uri, io_loop=self.io_loop, payload_handler=self.handle_publish, ) # Start the master event publisher with salt.utils.files.set_umask(0o177): self.publisher.start() self.puller.start() - if (self.opts['ipc_mode'] != 'tcp' and ( - self.opts['publisher_acl'] or - self.opts['external_auth'])): - os.chmod(os.path.join( - self.opts['sock_dir'], 'master_event_pub.ipc'), 0o666) + if self.opts["ipc_mode"] != "tcp" and ( + self.opts["publisher_acl"] or self.opts["external_auth"] + ): + os.chmod( + os.path.join(self.opts["sock_dir"], "master_event_pub.ipc"), + 0o666, + ) # Make sure the IO loop and respective sockets are closed and # destroyed @@ -1142,27 +1160,26 @@ class EventPublisher(salt.utils.process.SignalHandlingProcess): self.io_loop.start() def handle_publish(self, package, _): - ''' + """ Get something from epull, publish it out epub, and return the package (or None) - ''' + """ try: self.publisher.publish(package) return package # Add an extra fallback in case a forked process leeks through except Exception: # pylint: disable=broad-except - log.critical('Unexpected error while polling master events', - exc_info=True) + log.critical("Unexpected error while polling master events", exc_info=True) return None def close(self): if self._closing: return self._closing = True - if hasattr(self, 'publisher'): + if hasattr(self, "publisher"): self.publisher.close() - if hasattr(self, 'puller'): + if hasattr(self, "puller"): self.puller.close() - if hasattr(self, 'io_loop'): + if hasattr(self, "io_loop"): self.io_loop.close() def _handle_signals(self, signum, sigframe): @@ -1172,20 +1189,22 @@ class EventPublisher(salt.utils.process.SignalHandlingProcess): # pylint: disable=W1701 def __del__(self): self.close() + # pylint: enable=W1701 class EventReturn(salt.utils.process.SignalHandlingProcess): - ''' + """ A dedicated process which listens to the master event bus and queues and forwards events to the specified returner. - ''' + """ + def __init__(self, opts, **kwargs): - ''' + """ Initialize the EventReturn system Return an EventReturn instance - ''' + """ # This is required because the process is forked and the module no # longer exists in the global namespace. import salt.minion @@ -1193,10 +1212,12 @@ class EventReturn(salt.utils.process.SignalHandlingProcess): super(EventReturn, self).__init__(**kwargs) self.opts = opts - self.event_return_queue = self.opts['event_return_queue'] - self.event_return_queue_max_seconds = self.opts.get('event_return_queue_max_seconds', 0) + self.event_return_queue = self.opts["event_return_queue"] + self.event_return_queue_max_seconds = self.opts.get( + "event_return_queue_max_seconds", 0 + ) local_minion_opts = self.opts.copy() - local_minion_opts['file_client'] = 'local' + local_minion_opts["file_client"] = "local" self.minion = salt.minion.MasterMinion(local_minion_opts) self.event_queue = [] self.stop = False @@ -1206,16 +1227,16 @@ class EventReturn(salt.utils.process.SignalHandlingProcess): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['opts'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["opts"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def _handle_signals(self, signum, sigframe): @@ -1226,19 +1247,19 @@ class EventReturn(salt.utils.process.SignalHandlingProcess): super(EventReturn, self)._handle_signals(signum, sigframe) def flush_events(self): - if isinstance(self.opts['event_return'], list): + if isinstance(self.opts["event_return"], list): # Multiple event returners - for r in self.opts['event_return']: - log.debug('Calling event returner %s, one of many.', r) - event_return = '{0}.event_return'.format(r) + for r in self.opts["event_return"]: + log.debug("Calling event returner %s, one of many.", r) + event_return = "{0}.event_return".format(r) self._flush_event_single(event_return) else: # Only a single event returner - log.debug('Calling event returner %s, only one ' - 'configured.', self.opts['event_return']) - event_return = '{0}.event_return'.format( - self.opts['event_return'] - ) + log.debug( + "Calling event returner %s, only one " "configured.", + self.opts["event_return"], + ) + event_return = "{0}.event_return".format(self.opts["event_return"]) self._flush_event_single(event_return) del self.event_queue[:] @@ -1247,31 +1268,37 @@ class EventReturn(salt.utils.process.SignalHandlingProcess): try: self.minion.returners[event_return](self.event_queue) except Exception as exc: # pylint: disable=broad-except - log.error('Could not store events - returner \'%s\' raised ' - 'exception: %s', event_return, exc) + log.error( + "Could not store events - returner '%s' raised " "exception: %s", + event_return, + exc, + ) # don't waste processing power unnecessarily on converting a # potentially huge dataset to a string if log.level <= logging.DEBUG: - log.debug('Event data that caused an exception: %s', - self.event_queue) + log.debug( + "Event data that caused an exception: %s", self.event_queue + ) else: - log.error('Could not store return for event(s) - returner ' - '\'%s\' not found.', event_return) + log.error( + "Could not store return for event(s) - returner " "'%s' not found.", + event_return, + ) def run(self): - ''' + """ Spin up the multiprocess event returner - ''' + """ salt.utils.process.appendproctitle(self.__class__.__name__) - self.event = get_event('master', opts=self.opts, listen=True) + self.event = get_event("master", opts=self.opts, listen=True) events = self.event.iter_events(full=True) - self.event.fire_event({}, 'salt/event_listen/start') + self.event.fire_event({}, "salt/event_listen/start") try: # events below is a generator, we will iterate until we get the salt/event/exit tag oldestevent = None for event in events: - if event['tag'] == 'salt/event/exit': + if event["tag"] == "salt/event/exit": # We're done eventing self.stop = True if self._filter(event): @@ -1288,7 +1315,9 @@ class EventReturn(salt.utils.process.SignalHandlingProcess): oldestevent = rightnow age_in_seconds = (rightnow - oldestevent).seconds if age_in_seconds > 0: - log.debug('Oldest event in queue is %s seconds old.', age_in_seconds) + log.debug( + "Oldest event in queue is %s seconds old.", age_in_seconds + ) if age_in_seconds >= self.event_return_queue_max_seconds: too_long_in_queue = True oldestevent = None @@ -1296,12 +1325,17 @@ class EventReturn(salt.utils.process.SignalHandlingProcess): too_long_in_queue = False if too_long_in_queue: - log.debug('Oldest event has been in queue too long, will flush queue') + log.debug( + "Oldest event has been in queue too long, will flush queue" + ) # If we are over the max queue size or the oldest item in the queue has been there too long # then flush the queue - if len(self.event_queue) >= self.event_return_queue or too_long_in_queue: - log.debug('Flushing %s events.', len(self.event_queue)) + if ( + len(self.event_queue) >= self.event_return_queue + or too_long_in_queue + ): + log.debug("Flushing %s events.", len(self.event_queue)) self.flush_events() oldestevent = None if self.stop: @@ -1311,26 +1345,26 @@ class EventReturn(salt.utils.process.SignalHandlingProcess): # No matter what, make sure we flush the queue even when we are exiting # and there will be no more events. if self.event_queue: - log.debug('Flushing %s events.', len(self.event_queue)) + log.debug("Flushing %s events.", len(self.event_queue)) self.flush_events() def _filter(self, event): - ''' + """ Take an event and run it through configured filters. Returns True if event should be stored, else False - ''' - tag = event['tag'] - if self.opts['event_return_whitelist']: + """ + tag = event["tag"] + if self.opts["event_return_whitelist"]: ret = False else: ret = True - for whitelist_match in self.opts['event_return_whitelist']: + for whitelist_match in self.opts["event_return_whitelist"]: if fnmatch.fnmatch(tag, whitelist_match): ret = True break - for blacklist_match in self.opts['event_return_blacklist']: + for blacklist_match in self.opts["event_return_blacklist"]: if fnmatch.fnmatch(tag, blacklist_match): ret = False break @@ -1338,11 +1372,12 @@ class EventReturn(salt.utils.process.SignalHandlingProcess): class StateFire(object): - ''' + """ Evaluate the data from a state run and fire events on the master and minion for each returned chunk that is not "green" This object is made to only run on a minion - ''' + """ + def __init__(self, opts, auth=None): self.opts = opts if not auth: @@ -1351,7 +1386,7 @@ class StateFire(object): self.auth = auth def fire_master(self, data, tag, preload=None): - ''' + """ Fire an event off on the master server CLI Example: @@ -1359,52 +1394,56 @@ class StateFire(object): .. code-block:: bash salt '*' event.fire_master 'stuff to be in the event' 'tag' - ''' + """ load = {} if preload: load.update(preload) - load.update({ - 'id': self.opts['id'], - 'tag': tag, - 'data': data, - 'cmd': '_minion_event', - 'tok': self.auth.gen_token(b'salt'), - }) + load.update( + { + "id": self.opts["id"], + "tag": tag, + "data": data, + "cmd": "_minion_event", + "tok": self.auth.gen_token(b"salt"), + } + ) with salt.transport.client.ReqChannel.factory(self.opts) as channel: try: channel.send(load) except Exception as exc: # pylint: disable=broad-except - log.info('An exception occurred on fire_master: %s', exc, exc_info_on_loglevel=logging.DEBUG) + log.info( + "An exception occurred on fire_master: %s", + exc, + exc_info_on_loglevel=logging.DEBUG, + ) return True def fire_running(self, running): - ''' + """ Pass in a state "running" dict, this is the return dict from a state call. The dict will be processed and fire events. By default yellows and reds fire events on the master and minion, but this can be configured. - ''' - load = {'id': self.opts['id'], - 'events': [], - 'cmd': '_minion_event'} - for stag in sorted( - running, - key=lambda k: running[k].get('__run_num__', 0)): - if running[stag]['result'] and not running[stag]['changes']: + """ + load = {"id": self.opts["id"], "events": [], "cmd": "_minion_event"} + for stag in sorted(running, key=lambda k: running[k].get("__run_num__", 0)): + if running[stag]["result"] and not running[stag]["changes"]: continue - tag = 'state_{0}_{1}'.format( - six.text_type(running[stag]['result']), - 'True' if running[stag]['changes'] else 'False') - load['events'].append({ - 'tag': tag, - 'data': running[stag], - }) + tag = "state_{0}_{1}".format( + six.text_type(running[stag]["result"]), + "True" if running[stag]["changes"] else "False", + ) + load["events"].append({"tag": tag, "data": running[stag]}) with salt.transport.client.ReqChannel.factory(self.opts) as channel: try: channel.send(load) except Exception as exc: # pylint: disable=broad-except - log.info('An exception occurred on fire_master: %s', exc, exc_info_on_loglevel=logging.DEBUG) + log.info( + "An exception occurred on fire_master: %s", + exc, + exc_info_on_loglevel=logging.DEBUG, + ) return True diff --git a/salt/utils/extend.py b/salt/utils/extend.py index 506dabd120d..8c2a4a8efd2 100644 --- a/salt/utils/extend.py +++ b/salt/utils/extend.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" SaltStack Extend ~~~~~~~~~~~~~~~~ @@ -11,39 +11,41 @@ directory. This tool uses Jinja2 for templating. This tool is accessed using `salt-extend` :codeauthor: Anthony Shaw <anthonyshaw@apache.org> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -from datetime import date import logging -import tempfile import os -import sys import shutil +import sys +import tempfile +from datetime import date + +import salt.utils.files +import salt.version from jinja2 import Template +from salt.ext.six.moves import zip # Import Salt libs from salt.serializers.yaml import deserialize -from salt.ext.six.moves import zip from salt.utils.odict import OrderedDict -import salt.utils.files -import salt.version log = logging.getLogger(__name__) try: import click + HAS_CLICK = True except ImportError as ie: HAS_CLICK = False -TEMPLATE_FILE_NAME = 'template.yml' +TEMPLATE_FILE_NAME = "template.yml" def _get_template(path, option_key): - ''' + """ Get the contents of a template file and provide it as a module type :param path: path to the template.yml file @@ -54,15 +56,15 @@ def _get_template(path, option_key): :returns: Details about the template :rtype: ``tuple`` - ''' - with salt.utils.files.fopen(path, 'r') as template_f: + """ + with salt.utils.files.fopen(path, "r") as template_f: template = deserialize(template_f) - info = (option_key, template.get('description', ''), template) + info = (option_key, template.get("description", ""), template) return info def _fetch_templates(src): - ''' + """ Fetch all of the templates in the src directory :param src: The source path @@ -70,9 +72,9 @@ def _fetch_templates(src): :rtype: ``list`` of ``tuple`` :returns: ``list`` of ('key', 'description') - ''' + """ templates = [] - log.debug('Listing contents of %s', src) + log.debug("Listing contents of %s", src) for item in os.listdir(src): s = os.path.join(src, item) if os.path.isdir(s): @@ -80,13 +82,16 @@ def _fetch_templates(src): if os.path.isfile(template_path): templates.append(_get_template(template_path, item)) else: - log.debug("Directory does not contain %s %s", template_path, - TEMPLATE_FILE_NAME) + log.debug( + "Directory does not contain %s %s", + template_path, + TEMPLATE_FILE_NAME, + ) return templates def _mergetree(src, dst): - ''' + """ Akin to shutils.copytree but over existing directories, does a recursive merge copy. :param src: The source path @@ -94,7 +99,7 @@ def _mergetree(src, dst): :param dst: The destination path :type dst: ``str`` - ''' + """ for item in os.listdir(src): s = os.path.join(src, item) d = os.path.join(dst, item) @@ -110,7 +115,7 @@ def _mergetree(src, dst): def _mergetreejinja(src, dst, context): - ''' + """ Merge directory A to directory B, apply Jinja2 templating to both the file/folder names AND to the contents of the files @@ -122,7 +127,7 @@ def _mergetreejinja(src, dst, context): :param context: The dictionary to inject into the Jinja template as context :type context: ``dict`` - ''' + """ for item in os.listdir(src): s = os.path.join(src, item) d = os.path.join(dst, item) @@ -137,15 +142,15 @@ def _mergetreejinja(src, dst, context): if item != TEMPLATE_FILE_NAME: d = Template(d).render(context) log.info("Copying file %s to %s", s, d) - with salt.utils.files.fopen(s, 'r') as source_file: + with salt.utils.files.fopen(s, "r") as source_file: src_contents = salt.utils.stringutils.to_unicode(source_file.read()) dest_contents = Template(src_contents).render(context) - with salt.utils.files.fopen(d, 'w') as dest_file: + with salt.utils.files.fopen(d, "w") as dest_file: dest_file.write(salt.utils.stringutils.to_str(dest_contents)) def _prompt_user_variable(var_name, default_value): - ''' + """ Prompt the user to enter the value of a variable :param var_name: The question to ask the user @@ -156,12 +161,12 @@ def _prompt_user_variable(var_name, default_value): :rtype: ``str`` :returns: the value from the user - ''' + """ return click.prompt(var_name, default=default_value) def _prompt_choice(var_name, options): - ''' + """ Prompt the user to choose between a list of options, index each one by adding an enumerator based on https://github.com/audreyr/cookiecutter/blob/master/cookiecutter/prompt.py#L51 @@ -173,28 +178,32 @@ def _prompt_choice(var_name, options): :rtype: ``tuple`` :returns: The selected user - ''' + """ choice_map = OrderedDict( - ('{0}'.format(i), value) for i, value in enumerate(options, 1) if value[0] != 'test' + ("{0}".format(i), value) + for i, value in enumerate(options, 1) + if value[0] != "test" ) choices = choice_map.keys() - default = '1' + default = "1" - choice_lines = ['{0} - {1} - {2}'.format(c[0], c[1][0], c[1][1]) for c in choice_map.items()] - prompt = '\n'.join(( - 'Select {0}:'.format(var_name), - '\n'.join(choice_lines), - 'Choose from {0}'.format(', '.join(choices)) - )) - - user_choice = click.prompt( - prompt, type=click.Choice(choices), default=default + choice_lines = [ + "{0} - {1} - {2}".format(c[0], c[1][0], c[1][1]) for c in choice_map.items() + ] + prompt = "\n".join( + ( + "Select {0}:".format(var_name), + "\n".join(choice_lines), + "Choose from {0}".format(", ".join(choices)), + ) ) + + user_choice = click.prompt(prompt, type=click.Choice(choices), default=default) return choice_map[user_choice] def apply_template(template_dir, output_dir, context): - ''' + """ Apply the template from the template directory to the output using the supplied context dict. @@ -206,12 +215,19 @@ def apply_template(template_dir, output_dir, context): :param context: The dictionary to inject into the Jinja template as context :type context: ``dict`` - ''' + """ _mergetreejinja(template_dir, output_dir, context) -def run(extension=None, name=None, description=None, salt_dir=None, merge=False, temp_dir=None): - ''' +def run( + extension=None, + name=None, + description=None, + salt_dir=None, + merge=False, + temp_dir=None, +): + """ A template factory for extending the salt ecosystem :param extension: The extension type, e.g. 'module', 'state', if omitted, user will be prompted @@ -231,19 +247,19 @@ def run(extension=None, name=None, description=None, salt_dir=None, merge=False, :param temp_dir: The directory for generated code, if omitted, system temp will be used :type temp_dir: ``str`` - ''' + """ if not HAS_CLICK: print("click is not installed, please install using pip") sys.exit(1) if salt_dir is None: - salt_dir = '.' + salt_dir = "." - MODULE_OPTIONS = _fetch_templates(os.path.join(salt_dir, 'templates')) + MODULE_OPTIONS = _fetch_templates(os.path.join(salt_dir, "templates")) if extension is None: - print('Choose which type of extension you are developing for SaltStack') - extension_type = 'Extension type' + print("Choose which type of extension you are developing for SaltStack") + extension_type = "Extension type" chosen_extension = _prompt_choice(extension_type, MODULE_OPTIONS) else: if extension not in list(zip(*MODULE_OPTIONS))[0]: @@ -256,30 +272,30 @@ def run(extension=None, name=None, description=None, salt_dir=None, merge=False, extension_context = chosen_extension[2] if name is None: - print('Enter the short name for the module (e.g. mymodule)') - name = _prompt_user_variable('Module name', '') + print("Enter the short name for the module (e.g. mymodule)") + name = _prompt_user_variable("Module name", "") if description is None: - description = _prompt_user_variable('Short description of the module', '') + description = _prompt_user_variable("Short description of the module", "") - template_dir = 'templates/{0}'.format(extension_type) + template_dir = "templates/{0}".format(extension_type) module_name = name param_dict = { "version": salt.version.SaltStackVersion.next_release().name, "module_name": module_name, "short_description": description, - "release_date": date.today().strftime('%Y-%m-%d'), - "year": date.today().strftime('%Y'), + "release_date": date.today().strftime("%Y-%m-%d"), + "year": date.today().strftime("%Y"), } # get additional questions from template additional_context = {} - for key, val in extension_context.get('questions', {}).items(): + for key, val in extension_context.get("questions", {}).items(): # allow templates to be used in default values. - default = Template(val.get('default', '')).render(param_dict) + default = Template(val.get("default", "")).render(param_dict) - prompt_var = _prompt_user_variable(val['question'], default) + prompt_var = _prompt_user_variable(val["question"], default) additional_context[key] = prompt_var context = param_dict.copy() @@ -289,10 +305,7 @@ def run(extension=None, name=None, description=None, salt_dir=None, merge=False, if temp_dir is None: temp_dir = tempfile.mkdtemp() - apply_template( - template_dir, - temp_dir, - context) + apply_template(template_dir, temp_dir, context) if not merge: path = temp_dir @@ -300,9 +313,9 @@ def run(extension=None, name=None, description=None, salt_dir=None, merge=False, _mergetree(temp_dir, salt_dir) path = salt_dir - log.info('New module stored in %s', path) + log.info("New module stored in %s", path) return path -if __name__ == '__main__': +if __name__ == "__main__": run() diff --git a/salt/utils/extmods.py b/salt/utils/extmods.py index f6f049a4da3..7043b1b13a9 100644 --- a/salt/utils/extmods.py +++ b/salt/utils/extmods.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Functions used to sync external modules -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging @@ -34,107 +34,110 @@ def _listdir_recursively(rootdir): file_list = [] for root, dirs, files in salt.utils.path.os_walk(rootdir): for filename in files: - relpath = os.path.relpath(root, rootdir).strip('.') + relpath = os.path.relpath(root, rootdir).strip(".") file_list.append(os.path.join(relpath, filename)) return file_list -def sync(opts, - form, - saltenv=None, - extmod_whitelist=None, - extmod_blacklist=None): - ''' +def sync(opts, form, saltenv=None, extmod_whitelist=None, extmod_blacklist=None): + """ Sync custom modules into the extension_modules directory - ''' + """ if saltenv is None: - saltenv = ['base'] + saltenv = ["base"] if extmod_whitelist is None: - extmod_whitelist = opts['extmod_whitelist'] + extmod_whitelist = opts["extmod_whitelist"] elif isinstance(extmod_whitelist, six.string_types): - extmod_whitelist = {form: extmod_whitelist.split(',')} + extmod_whitelist = {form: extmod_whitelist.split(",")} elif not isinstance(extmod_whitelist, dict): - log.error('extmod_whitelist must be a string or dictionary: %s', - extmod_whitelist) + log.error( + "extmod_whitelist must be a string or dictionary: %s", extmod_whitelist + ) if extmod_blacklist is None: - extmod_blacklist = opts['extmod_blacklist'] + extmod_blacklist = opts["extmod_blacklist"] elif isinstance(extmod_blacklist, six.string_types): - extmod_blacklist = {form: extmod_blacklist.split(',')} + extmod_blacklist = {form: extmod_blacklist.split(",")} elif not isinstance(extmod_blacklist, dict): - log.error('extmod_blacklist must be a string or dictionary: %s', - extmod_blacklist) + log.error( + "extmod_blacklist must be a string or dictionary: %s", extmod_blacklist + ) if isinstance(saltenv, six.string_types): - saltenv = saltenv.split(',') + saltenv = saltenv.split(",") ret = [] remote = set() - source = salt.utils.url.create('_' + form) - mod_dir = os.path.join(opts['extension_modules'], '{0}'.format(form)) + source = salt.utils.url.create("_" + form) + mod_dir = os.path.join(opts["extension_modules"], "{0}".format(form)) touched = False with salt.utils.files.set_umask(0o077): try: if not os.path.isdir(mod_dir): - log.info('Creating module dir \'%s\'', mod_dir) + log.info("Creating module dir '%s'", mod_dir) try: os.makedirs(mod_dir) except (IOError, OSError): log.error( - 'Cannot create cache module directory %s. Check ' - 'permissions.', mod_dir + "Cannot create cache module directory %s. Check " + "permissions.", + mod_dir, ) fileclient = salt.fileclient.get_file_client(opts) for sub_env in saltenv: - log.info( - 'Syncing %s for environment \'%s\'', form, sub_env - ) + log.info("Syncing %s for environment '%s'", form, sub_env) cache = [] - log.info( - 'Loading cache from {0}, for {1})'.format(source, sub_env) - ) + log.info("Loading cache from {0}, for {1})".format(source, sub_env)) # Grab only the desired files (.py, .pyx, .so) cache.extend( fileclient.cache_dir( - source, sub_env, include_empty=False, - include_pat=r'E@\.(pyx?|so|zip)$', exclude_pat=None + source, + sub_env, + include_empty=False, + include_pat=r"E@\.(pyx?|so|zip)$", + exclude_pat=None, ) ) local_cache_dir = os.path.join( - opts['cachedir'], - 'files', - sub_env, - '_{0}'.format(form) - ) - log.debug('Local cache dir: \'%s\'', local_cache_dir) + opts["cachedir"], "files", sub_env, "_{0}".format(form) + ) + log.debug("Local cache dir: '%s'", local_cache_dir) for fn_ in cache: relpath = os.path.relpath(fn_, local_cache_dir) - relname = os.path.splitext(relpath)[0].replace(os.sep, '.') - if extmod_whitelist and form in extmod_whitelist and relname not in extmod_whitelist[form]: + relname = os.path.splitext(relpath)[0].replace(os.sep, ".") + if ( + extmod_whitelist + and form in extmod_whitelist + and relname not in extmod_whitelist[form] + ): continue - if extmod_blacklist and form in extmod_blacklist and relname in extmod_blacklist[form]: + if ( + extmod_blacklist + and form in extmod_blacklist + and relname in extmod_blacklist[form] + ): continue remote.add(relpath) dest = os.path.join(mod_dir, relpath) - log.info('Copying \'%s\' to \'%s\'', fn_, dest) + log.info("Copying '%s' to '%s'", fn_, dest) if os.path.isfile(dest): # The file is present, if the sum differs replace it - hash_type = opts.get('hash_type', 'md5') + hash_type = opts.get("hash_type", "md5") src_digest = salt.utils.hashutils.get_hash(fn_, hash_type) dst_digest = salt.utils.hashutils.get_hash(dest, hash_type) if src_digest != dst_digest: # The downloaded file differs, replace! shutil.copyfile(fn_, dest) - ret.append('{0}.{1}'.format(form, relname)) + ret.append("{0}.{1}".format(form, relname)) else: dest_dir = os.path.dirname(dest) if not os.path.isdir(dest_dir): os.makedirs(dest_dir) shutil.copyfile(fn_, dest) - ret.append('{0}.{1}'.format(form, relname)) + ret.append("{0}.{1}".format(form, relname)) touched = bool(ret) - if opts['clean_dynamic_modules'] is True: + if opts["clean_dynamic_modules"] is True: current = set(_listdir_recursively(mod_dir)) for fn_ in current - remote: full = os.path.join(mod_dir, fn_) @@ -150,5 +153,5 @@ def sync(opts, touched = True shutil.rmtree(emptydir, ignore_errors=True) except Exception as exc: # pylint: disable=broad-except - log.error('Failed to sync %s module: %s', form, exc) + log.error("Failed to sync %s module: %s", form, exc) return ret, touched diff --git a/salt/utils/filebuffer.py b/salt/utils/filebuffer.py index 8d8ded412d9..e130b32b8dc 100644 --- a/salt/utils/filebuffer.py +++ b/salt/utils/filebuffer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,8 +7,8 @@ ~~~~~~~~~~~~~~~~~~~~~ This utility allows parsing a file in chunks. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.utils.files @@ -17,13 +17,13 @@ from salt.exceptions import SaltException class InvalidFileMode(SaltException): - ''' + """ An invalid file mode was used to open the file passed to the buffer - ''' + """ class BufferedReader(object): - ''' + """ This object allows iterating through the contents of a file keeping X configurable bytes in memory which can be used to, for example, do regex search/matching on more than a single line. @@ -51,13 +51,15 @@ class BufferedReader(object): :type mode: str :param mode: The mode the file should be opened. **Only read modes**. - ''' - def __init__(self, path, max_in_mem=256 * 1024, chunk_size=32 * 1024, - mode='r'): - if 'a' in mode or 'w' in mode: + """ + + def __init__(self, path, max_in_mem=256 * 1024, chunk_size=32 * 1024, mode="r"): + if "a" in mode or "w" in mode: raise InvalidFileMode("Cannot open file in write or append mode") self.__path = path - self.__file = salt.utils.files.fopen(self.__path, mode) # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + self.__file = salt.utils.files.fopen(self.__path, mode) + # pylint: enable=resource-leakage self.__max_in_mem = max_in_mem self.__chunk_size = chunk_size self.__buffered = None @@ -72,18 +74,18 @@ class BufferedReader(object): return self def next(self): - ''' + """ Return the next iteration by popping `chunk_size` from the left and appending `chunk_size` to the right if there's info on the file left to be read. - ''' + """ if self.__buffered is None: # Use floor division to force multiplier to an integer multiplier = self.__max_in_mem // self.__chunk_size self.__buffered = "" else: multiplier = 1 - self.__buffered = self.__buffered[self.__chunk_size:] + self.__buffered = self.__buffered[self.__chunk_size :] data = self.__file.read(self.__chunk_size * multiplier) # Data is a byte object in Python 3 diff --git a/salt/utils/files.py b/salt/utils/files.py index ebb9dcacafb..0cc5c90e277 100644 --- a/salt/utils/files.py +++ b/salt/utils/files.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Functions for working with files -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import codecs @@ -18,20 +18,23 @@ import subprocess import tempfile import time +import salt.modules.selinux + # Import Salt libs import salt.utils.path import salt.utils.platform import salt.utils.stringutils -import salt.modules.selinux from salt.exceptions import CommandExecutionError, FileLockError, MinionError -from salt.utils.decorators.jinja import jinja_filter # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range from salt.ext.six.moves.urllib.parse import quote # pylint: disable=no-name-in-module +from salt.utils.decorators.jinja import jinja_filter + try: import fcntl + HAS_FCNTL = True except ImportError: # fcntl is not available on windows @@ -39,26 +42,26 @@ except ImportError: log = logging.getLogger(__name__) -LOCAL_PROTOS = ('', 'file') -REMOTE_PROTOS = ('http', 'https', 'ftp', 'swift', 's3') -VALID_PROTOS = ('salt', 'file') + REMOTE_PROTOS -TEMPFILE_PREFIX = '__salt.tmp.' +LOCAL_PROTOS = ("", "file") +REMOTE_PROTOS = ("http", "https", "ftp", "swift", "s3") +VALID_PROTOS = ("salt", "file") + REMOTE_PROTOS +TEMPFILE_PREFIX = "__salt.tmp." HASHES = { - 'sha512': 128, - 'sha384': 96, - 'sha256': 64, - 'sha224': 56, - 'sha1': 40, - 'md5': 32, + "sha512": 128, + "sha384": 96, + "sha256": 64, + "sha224": 56, + "sha1": 40, + "md5": 32, } HASHES_REVMAP = dict([(y, x) for x, y in six.iteritems(HASHES)]) def __clean_tmp(tmp): - ''' + """ Remove temporary files - ''' + """ try: rm_rf(tmp) except Exception: # pylint: disable=broad-except @@ -66,32 +69,40 @@ def __clean_tmp(tmp): def guess_archive_type(name): - ''' + """ Guess an archive type (tar, zip, or rar) by its file extension - ''' + """ name = name.lower() - for ending in ('tar', 'tar.gz', 'tgz', - 'tar.bz2', 'tbz2', 'tbz', - 'tar.xz', 'txz', - 'tar.lzma', 'tlz'): - if name.endswith('.' + ending): - return 'tar' - for ending in ('zip', 'rar'): - if name.endswith('.' + ending): + for ending in ( + "tar", + "tar.gz", + "tgz", + "tar.bz2", + "tbz2", + "tbz", + "tar.xz", + "txz", + "tar.lzma", + "tlz", + ): + if name.endswith("." + ending): + return "tar" + for ending in ("zip", "rar"): + if name.endswith("." + ending): return ending return None def mkstemp(*args, **kwargs): - ''' + """ Helper function which does exactly what ``tempfile.mkstemp()`` does but accepts another argument, ``close_fd``, which, by default, is true and closes the fd before returning the file path. Something commonly done throughout Salt's code. - ''' - if 'prefix' not in kwargs: - kwargs['prefix'] = '__salt.tmp.' - close_fd = kwargs.pop('close_fd', True) + """ + if "prefix" not in kwargs: + kwargs["prefix"] = "__salt.tmp." + close_fd = kwargs.pop("close_fd", True) fd_, f_path = tempfile.mkstemp(*args, **kwargs) if close_fd is False: return fd_, f_path @@ -101,14 +112,14 @@ def mkstemp(*args, **kwargs): def recursive_copy(source, dest): - ''' + """ Recursively copy the source directory to the destination, leaving files with the source does not explicitly overwrite. (identical to cp -r on a unix machine) - ''' + """ for root, _, files in salt.utils.path.os_walk(source): - path_from_source = root.replace(source, '').lstrip(os.sep) + path_from_source = root.replace(source, "").lstrip(os.sep) target_directory = os.path.join(dest, path_from_source) if not os.path.exists(target_directory): os.makedirs(target_directory) @@ -118,30 +129,26 @@ def recursive_copy(source, dest): shutil.copyfile(file_path_from_source, target_path) -def copyfile(source, dest, backup_mode='', cachedir=''): - ''' +def copyfile(source, dest, backup_mode="", cachedir=""): + """ Copy files from a source to a destination in an atomic way, and if specified cache the file. - ''' + """ if not os.path.isfile(source): - raise IOError( - '[Errno 2] No such file or directory: {0}'.format(source) - ) + raise IOError("[Errno 2] No such file or directory: {0}".format(source)) if not os.path.isdir(os.path.dirname(dest)): - raise IOError( - '[Errno 2] No such file or directory: {0}'.format(dest) - ) + raise IOError("[Errno 2] No such file or directory: {0}".format(dest)) bname = os.path.basename(dest) dname = os.path.dirname(os.path.abspath(dest)) tgt = mkstemp(prefix=bname, dir=dname) shutil.copyfile(source, tgt) - bkroot = '' + bkroot = "" if cachedir: - bkroot = os.path.join(cachedir, 'file_backup') - if backup_mode == 'minion' or backup_mode == 'both' and bkroot: + bkroot = os.path.join(cachedir, "file_backup") + if backup_mode == "minion" or backup_mode == "both" and bkroot: if os.path.exists(dest): backup_minion(dest, bkroot) - if backup_mode == 'master' or backup_mode == 'both' and bkroot: + if backup_mode == "master" or backup_mode == "both" and bkroot: # TODO, backup to master pass # Get current file stats to they can be replicated after the new file is @@ -165,15 +172,15 @@ def copyfile(source, dest, backup_mode='', cachedir=''): os.chown(dest, fstat.st_uid, fstat.st_gid) os.chmod(dest, fstat.st_mode) # If SELINUX is available run a restorecon on the file - rcon = salt.utils.path.which('restorecon') + rcon = salt.utils.path.which("restorecon") if rcon: policy = False try: policy = salt.modules.selinux.getenforce() except (ImportError, CommandExecutionError): pass - if policy == 'Enforcing': - with fopen(os.devnull, 'w') as dev_null: + if policy == "Enforcing": + with fopen(os.devnull, "w") as dev_null: cmd = [rcon, dest] subprocess.call(cmd, stdout=dev_null, stderr=dev_null) if os.path.isfile(tgt): @@ -182,11 +189,11 @@ def copyfile(source, dest, backup_mode='', cachedir=''): def rename(src, dst): - ''' + """ On Windows, os.rename() will fail with a WindowsError exception if a file exists at the destination path. This function checks for this error and if found, it deletes the destination path first. - ''' + """ try: os.rename(src, dst) except OSError as exc: @@ -197,21 +204,18 @@ def rename(src, dst): except OSError as exc: if exc.errno != errno.ENOENT: raise MinionError( - 'Error: Unable to remove {0}: {1}'.format( - dst, - exc.strerror - ) + "Error: Unable to remove {0}: {1}".format(dst, exc.strerror) ) os.rename(src, dst) def process_read_exception(exc, path, ignore=None): - ''' + """ Common code for raising exceptions when reading a file fails The ignore argument can be an iterable of integer error codes (or a single integer error code) that should be ignored. - ''' + """ if ignore is not None: if isinstance(ignore, six.integer_types): ignore = (ignore,) @@ -222,14 +226,12 @@ def process_read_exception(exc, path, ignore=None): return if exc.errno == errno.ENOENT: - raise CommandExecutionError('{0} does not exist'.format(path)) + raise CommandExecutionError("{0} does not exist".format(path)) elif exc.errno == errno.EACCES: - raise CommandExecutionError( - 'Permission denied reading from {0}'.format(path) - ) + raise CommandExecutionError("Permission denied reading from {0}".format(path)) else: raise CommandExecutionError( - 'Error {0} encountered reading from {1}: {2}'.format( + "Error {0} encountered reading from {1}: {2}".format( exc.errno, path, exc.strerror ) ) @@ -237,28 +239,26 @@ def process_read_exception(exc, path, ignore=None): @contextlib.contextmanager def wait_lock(path, lock_fn=None, timeout=5, sleep=0.1, time_start=None): - ''' + """ Obtain a write lock. If one exists, wait for it to release first - ''' + """ if not isinstance(path, six.string_types): - raise FileLockError('path must be a string') + raise FileLockError("path must be a string") if lock_fn is None: - lock_fn = path + '.w' + lock_fn = path + ".w" if time_start is None: time_start = time.time() obtained_lock = False def _raise_error(msg, race=False): - ''' + """ Raise a FileLockError - ''' + """ raise FileLockError(msg, time_start=time_start) try: if os.path.exists(lock_fn) and not os.path.isfile(lock_fn): - _raise_error( - 'lock_fn {0} exists and is not a file'.format(lock_fn) - ) + _raise_error("lock_fn {0} exists and is not a file".format(lock_fn)) open_flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY while time.time() - time_start < timeout: @@ -270,19 +270,18 @@ def wait_lock(path, lock_fn=None, timeout=5, sleep=0.1, time_start=None): except (IOError, OSError) as exc: if exc.errno != errno.EEXIST: _raise_error( - 'Error {0} encountered obtaining file lock {1}: {2}' - .format(exc.errno, lock_fn, exc.strerror) + "Error {0} encountered obtaining file lock {1}: {2}".format( + exc.errno, lock_fn, exc.strerror + ) ) - log.trace( - 'Lock file %s exists, sleeping %f seconds', lock_fn, sleep - ) + log.trace("Lock file %s exists, sleeping %f seconds", lock_fn, sleep) time.sleep(sleep) else: # Write the lock file - with os.fdopen(fh_, 'w'): + with os.fdopen(fh_, "w"): pass # Lock successfully acquired - log.trace('Write lock %s obtained', lock_fn) + log.trace("Write lock %s obtained", lock_fn) obtained_lock = True # Transfer control back to the code inside the with block yield @@ -291,8 +290,8 @@ def wait_lock(path, lock_fn=None, timeout=5, sleep=0.1, time_start=None): else: _raise_error( - 'Timeout of {0} seconds exceeded waiting for lock_fn {1} ' - 'to be released'.format(timeout, lock_fn) + "Timeout of {0} seconds exceeded waiting for lock_fn {1} " + "to be released".format(timeout, lock_fn) ) except FileLockError: @@ -300,22 +299,19 @@ def wait_lock(path, lock_fn=None, timeout=5, sleep=0.1, time_start=None): except Exception as exc: # pylint: disable=broad-except _raise_error( - 'Error encountered obtaining file lock {0}: {1}'.format( - lock_fn, - exc - ) + "Error encountered obtaining file lock {0}: {1}".format(lock_fn, exc) ) finally: if obtained_lock: os.remove(lock_fn) - log.trace('Write lock for %s (%s) released', path, lock_fn) + log.trace("Write lock for %s (%s) released", path, lock_fn) def get_umask(): - ''' + """ Returns the current umask - ''' + """ ret = os.umask(0) # pylint: disable=blacklisted-function os.umask(ret) # pylint: disable=blacklisted-function return ret @@ -323,9 +319,9 @@ def get_umask(): @contextlib.contextmanager def set_umask(mask): - ''' + """ Temporarily set the umask and restore once the contextmanager exits - ''' + """ if mask is None or salt.utils.platform.is_windows(): # Don't attempt on Windows, or if no mask was passed yield @@ -338,7 +334,7 @@ def set_umask(mask): def fopen(*args, **kwargs): - ''' + """ Wrapper around open() built-in to set CLOEXEC on the fd. This flag specifies that the file descriptor should be closed when an exec @@ -349,7 +345,7 @@ def fopen(*args, **kwargs): survive into the new program after exec. NB! We still have small race condition between open and fcntl. - ''' + """ if six.PY3: try: # Don't permit stdin/stdout/stderr to be opened. The boolean False @@ -357,44 +353,45 @@ def fopen(*args, **kwargs): # and 1, respectively. if args[0] in (0, 1, 2): raise TypeError( - '{0} is not a permitted file descriptor'.format(args[0]) + "{0} is not a permitted file descriptor".format(args[0]) ) except IndexError: pass binary = None # ensure 'binary' mode is always used on Windows in Python 2 - if ((six.PY2 and salt.utils.platform.is_windows() and 'binary' not in kwargs) or - kwargs.pop('binary', False)): + if ( + six.PY2 and salt.utils.platform.is_windows() and "binary" not in kwargs + ) or kwargs.pop("binary", False): if len(args) > 1: args = list(args) - if 'b' not in args[1]: - args[1] = args[1].replace('t', 'b') - if 'b' not in args[1]: - args[1] += 'b' - elif kwargs.get('mode'): - if 'b' not in kwargs['mode']: - kwargs['mode'] = kwargs['mode'].replace('t', 'b') - if 'b' not in kwargs['mode']: - kwargs['mode'] += 'b' + if "b" not in args[1]: + args[1] = args[1].replace("t", "b") + if "b" not in args[1]: + args[1] += "b" + elif kwargs.get("mode"): + if "b" not in kwargs["mode"]: + kwargs["mode"] = kwargs["mode"].replace("t", "b") + if "b" not in kwargs["mode"]: + kwargs["mode"] += "b" else: # the default is to read - kwargs['mode'] = 'rb' - elif six.PY3 and 'encoding' not in kwargs: + kwargs["mode"] = "rb" + elif six.PY3 and "encoding" not in kwargs: # In Python 3, if text mode is used and the encoding # is not specified, set the encoding to 'utf-8'. binary = False if len(args) > 1: args = list(args) - if 'b' in args[1]: + if "b" in args[1]: binary = True - if kwargs.get('mode', None): - if 'b' in kwargs['mode']: + if kwargs.get("mode", None): + if "b" in kwargs["mode"]: binary = True if not binary: - kwargs['encoding'] = __salt_system_encoding__ + kwargs["encoding"] = __salt_system_encoding__ - if six.PY3 and not binary and not kwargs.get('newline', None): - kwargs['newline'] = '' + if six.PY3 and not binary and not kwargs.get("newline", None): + kwargs["newline"] = "" f_handle = open(*args, **kwargs) # pylint: disable=resource-leakage @@ -402,9 +399,9 @@ def fopen(*args, **kwargs): # modify the file descriptor on systems with fcntl # unix and unix-like systems only try: - FD_CLOEXEC = fcntl.FD_CLOEXEC # pylint: disable=C0103 + FD_CLOEXEC = fcntl.FD_CLOEXEC # pylint: disable=C0103 except AttributeError: - FD_CLOEXEC = 1 # pylint: disable=C0103 + FD_CLOEXEC = 1 # pylint: disable=C0103 old_flags = fcntl.fcntl(f_handle.fileno(), fcntl.F_GETFD) fcntl.fcntl(f_handle.fileno(), fcntl.F_SETFD, old_flags | FD_CLOEXEC) @@ -413,11 +410,11 @@ def fopen(*args, **kwargs): @contextlib.contextmanager def flopen(*args, **kwargs): - ''' + """ Shortcut for fopen with lock and context manager. - ''' + """ filename, args = args[0], args[1:] - writing = 'wa' + writing = "wa" with fopen(filename, *args, **kwargs) as f_handle: try: if is_fcntl_available(check_sunos=True): @@ -433,7 +430,7 @@ def flopen(*args, **kwargs): @contextlib.contextmanager def fpopen(*args, **kwargs): - ''' + """ Shortcut for fopen with extra uid, gid, and mode options. Supported optional Keyword Arguments: @@ -452,20 +449,21 @@ def fpopen(*args, **kwargs): made. Same applies if the path is already owned by this gid. Must be int. Works only on unix/unix-like systems. - ''' + """ # Remove uid, gid and mode from kwargs if present - uid = kwargs.pop('uid', -1) # -1 means no change to current uid - gid = kwargs.pop('gid', -1) # -1 means no change to current gid - mode = kwargs.pop('mode', None) + uid = kwargs.pop("uid", -1) # -1 means no change to current uid + gid = kwargs.pop("gid", -1) # -1 means no change to current gid + mode = kwargs.pop("mode", None) with fopen(*args, **kwargs) as f_handle: path = args[0] d_stat = os.stat(path) - if hasattr(os, 'chown'): + if hasattr(os, "chown"): # if uid and gid are both -1 then go ahead with # no changes at all - if (d_stat.st_uid != uid or d_stat.st_gid != gid) and \ - [i for i in (uid, gid) if i != -1]: + if (d_stat.st_uid != uid or d_stat.st_gid != gid) and [ + i for i in (uid, gid) if i != -1 + ]: os.chown(path, uid, gid) if mode is not None: @@ -477,10 +475,10 @@ def fpopen(*args, **kwargs): def safe_walk(top, topdown=True, onerror=None, followlinks=True, _seen=None): - ''' + """ A clone of the python os.walk function with some checks for recursive symlinks. Unlike os.walk this follows symlinks by default. - ''' + """ if _seen is None: _seen = set() @@ -527,9 +525,9 @@ def safe_walk(top, topdown=True, onerror=None, followlinks=True, _seen=None): def safe_rm(tgt): - ''' + """ Safely remove a file - ''' + """ try: os.remove(tgt) except (IOError, OSError): @@ -537,12 +535,13 @@ def safe_rm(tgt): def rm_rf(path): - ''' + """ Platform-independent recursive delete. Includes code from http://stackoverflow.com/a/2656405 - ''' + """ + def _onerror(func, path, exc_info): - ''' + """ Error handler for `shutil.rmtree`. If the error is due to an access error (read only file) @@ -551,13 +550,14 @@ def rm_rf(path): If the error is for another reason it re-raises the error. Usage : `shutil.rmtree(path, onerror=onerror)` - ''' + """ if salt.utils.platform.is_windows() and not os.access(path, os.W_OK): # Is the error an access error ? os.chmod(path, stat.S_IWUSR) func(path) else: raise # pylint: disable=E0704 + if os.path.islink(path) or not os.path.isdir(path): os.remove(path) else: @@ -569,11 +569,11 @@ def rm_rf(path): shutil.rmtree(path, onerror=_onerror) -@jinja_filter('is_empty') +@jinja_filter("is_empty") def is_empty(filename): - ''' + """ Is a file empty? - ''' + """ try: return os.stat(filename).st_size == 0 except OSError: @@ -582,19 +582,19 @@ def is_empty(filename): def is_fcntl_available(check_sunos=False): - ''' + """ Simple function to check if the ``fcntl`` module is available or not. If ``check_sunos`` is passed as ``True`` an additional check to see if host is SunOS is also made. For additional information see: http://goo.gl/159FF8 - ''' + """ if check_sunos and salt.utils.platform.is_sunos(): return False return HAS_FCNTL def safe_filename_leaf(file_basename): - ''' + """ Input the basename of a file, without the directory tree, and returns a safe name to use i.e. only the required characters are converted by urllib.quote If the input is a PY2 String, output a PY2 String. If input is Unicode output Unicode. @@ -604,72 +604,78 @@ def safe_filename_leaf(file_basename): .. versionadded:: 2017.7.2 :codeauthor: Damon Atkins <https://github.com/damon-atkins> - ''' + """ + def _replace(re_obj): - return quote(re_obj.group(0), safe='') + return quote(re_obj.group(0), safe="") + if not isinstance(file_basename, six.text_type): # the following string is not prefixed with u - return re.sub('[\\\\:/*?"<>|]', - _replace, - six.text_type(file_basename, 'utf8').encode('ascii', 'backslashreplace')) + return re.sub( + '[\\\\:/*?"<>|]', + _replace, + six.text_type(file_basename, "utf8").encode("ascii", "backslashreplace"), + ) # the following string is prefixed with u return re.sub('[\\\\:/*?"<>|]', _replace, file_basename, flags=re.UNICODE) def safe_filepath(file_path_name, dir_sep=None): - ''' + """ Input the full path and filename, splits on directory separator and calls safe_filename_leaf for each part of the path. dir_sep allows coder to force a directory separate to a particular character .. versionadded:: 2017.7.2 :codeauthor: Damon Atkins <https://github.com/damon-atkins> - ''' + """ if not dir_sep: dir_sep = os.sep # Normally if file_path_name or dir_sep is Unicode then the output will be Unicode # This code ensure the output type is the same as file_path_name - if not isinstance(file_path_name, six.text_type) and isinstance(dir_sep, six.text_type): - dir_sep = dir_sep.encode('ascii') # This should not be executed under PY3 + if not isinstance(file_path_name, six.text_type) and isinstance( + dir_sep, six.text_type + ): + dir_sep = dir_sep.encode("ascii") # This should not be executed under PY3 # splitdrive only set drive on windows platform (drive, path) = os.path.splitdrive(file_path_name) - path = dir_sep.join([safe_filename_leaf(file_section) for file_section in path.rsplit(dir_sep)]) + path = dir_sep.join( + [safe_filename_leaf(file_section) for file_section in path.rsplit(dir_sep)] + ) if drive: path = dir_sep.join([drive, path]) return path -@jinja_filter('is_text_file') +@jinja_filter("is_text_file") def is_text(fp_, blocksize=512): - ''' + """ Uses heuristics to guess whether the given file is text or binary, by reading a single block of bytes from the file. If more than 30% of the chars in the block are non-text, or there are NUL ('\x00') bytes in the block, assume this is a binary file. - ''' + """ int2byte = (lambda x: bytes((x,))) if six.PY3 else chr - text_characters = ( - b''.join(int2byte(i) for i in range(32, 127)) + - b'\n\r\t\f\b') + text_characters = b"".join(int2byte(i) for i in range(32, 127)) + b"\n\r\t\f\b" try: block = fp_.read(blocksize) except AttributeError: # This wasn't an open filehandle, so treat it as a file path and try to # open the file try: - with fopen(fp_, 'rb') as fp2_: + with fopen(fp_, "rb") as fp2_: block = fp2_.read(blocksize) except IOError: # Unable to open file, bail out and return false return False - if b'\x00' in block: + if b"\x00" in block: # Files with null bytes are binary return False elif not block: # An empty file is considered a valid text file return True try: - block.decode('utf-8') + block.decode("utf-8") return True except UnicodeDecodeError: pass @@ -678,16 +684,16 @@ def is_text(fp_, blocksize=512): return float(len(nontext)) / len(block) <= 0.30 -@jinja_filter('is_bin_file') +@jinja_filter("is_bin_file") def is_binary(path): - ''' + """ Detects if the file is a binary, returns bool. Returns True if the file is a bin, False if the file is not and None if the file is not available. - ''' + """ if not os.path.isfile(path): return False try: - with fopen(path, 'rb') as fp_: + with fopen(path, "rb") as fp_: try: data = fp_.read(2048) if six.PY3: @@ -700,9 +706,9 @@ def is_binary(path): def remove(path): - ''' + """ Runs os.remove(path) and suppresses the OSError if the file doesn't exist - ''' + """ try: os.remove(path) except OSError as exc: @@ -710,11 +716,11 @@ def remove(path): raise -@jinja_filter('list_files') +@jinja_filter("list_files") def list_files(directory): - ''' + """ Return a list of all files found under directory (and its subdirectories) - ''' + """ ret = set() ret.add(directory) for root, dirs, files in safe_walk(directory): @@ -727,46 +733,45 @@ def list_files(directory): def st_mode_to_octal(mode): - ''' + """ Convert the st_mode value from a stat(2) call (as returned from os.stat()) to an octal mode. - ''' + """ try: return oct(mode)[-4:] except (TypeError, IndexError): - return '' + return "" def normalize_mode(mode): - ''' + """ Return a mode value, normalized to a string and containing a leading zero if it does not have one. Allow "keep" as a valid mode (used by file state/module to preserve mode from the Salt fileserver in file states). - ''' + """ if mode is None: return None if not isinstance(mode, six.string_types): mode = six.text_type(mode) if six.PY3: - mode = mode.replace('0o', '0') + mode = mode.replace("0o", "0") # Strip any quotes any initial zeroes, then though zero-pad it up to 4. # This ensures that somethign like '00644' is normalized to '0644' - return mode.strip('"').strip('\'').lstrip('0').zfill(4) + return mode.strip('"').strip("'").lstrip("0").zfill(4) def human_size_to_bytes(human_size): - ''' + """ Convert human-readable units to bytes - ''' - size_exp_map = {'K': 1, 'M': 2, 'G': 3, 'T': 4, 'P': 5} + """ + size_exp_map = {"K": 1, "M": 2, "G": 3, "T": 4, "P": 5} human_size_str = six.text_type(human_size) - match = re.match(r'^(\d+)([KMGTP])?$', human_size_str) + match = re.match(r"^(\d+)([KMGTP])?$", human_size_str) if not match: raise ValueError( - 'Size must be all digits, with an optional unit type ' - '(K, M, G, T, or P)' + "Size must be all digits, with an optional unit type " "(K, M, G, T, or P)" ) size_num = int(match.group(1)) unit_multiplier = 1024 ** size_exp_map.get(match.group(2), 0) @@ -774,12 +779,12 @@ def human_size_to_bytes(human_size): def backup_minion(path, bkroot): - ''' + """ Backup a file on the minion - ''' + """ dname, bname = os.path.split(path) if salt.utils.platform.is_windows(): - src_dir = dname.replace(':', '_') + src_dir = dname.replace(":", "_") else: src_dir = dname[1:] if not salt.utils.platform.is_windows(): @@ -787,13 +792,11 @@ def backup_minion(path, bkroot): msecs = six.text_type(int(time.time() * 1000000))[-6:] if salt.utils.platform.is_windows(): # ':' is an illegal filesystem path character on Windows - stamp = time.strftime('%a_%b_%d_%H-%M-%S_%Y') + stamp = time.strftime("%a_%b_%d_%H-%M-%S_%Y") else: - stamp = time.strftime('%a_%b_%d_%H:%M:%S_%Y') - stamp = '{0}{1}_{2}'.format(stamp[:-4], msecs, stamp[-4:]) - bkpath = os.path.join(bkroot, - src_dir, - '{0}_{1}'.format(bname, stamp)) + stamp = time.strftime("%a_%b_%d_%H:%M:%S_%Y") + stamp = "{0}{1}_{2}".format(stamp[:-4], msecs, stamp[-4:]) + bkpath = os.path.join(bkroot, src_dir, "{0}_{1}".format(bname, stamp)) if not os.path.isdir(os.path.dirname(bkpath)): os.makedirs(os.path.dirname(bkpath)) shutil.copyfile(path, bkpath) @@ -803,7 +806,7 @@ def backup_minion(path, bkroot): def get_encoding(path): - ''' + """ Detect a file's encoding using the following: - Check for Byte Order Marks (BOM) - Check for UTF-8 Markers @@ -819,12 +822,13 @@ def get_encoding(path): Raises: CommandExecutionError: If the encoding cannot be detected - ''' + """ + def check_ascii(_data): # If all characters can be decoded to ASCII, then it's ASCII try: - _data.decode('ASCII') - log.debug('Found ASCII') + _data.decode("ASCII") + log.debug("Found ASCII") except UnicodeDecodeError: return False else: @@ -835,26 +839,26 @@ def get_encoding(path): # https://docs.python.org/2/library/codecs.html # https://docs.python.org/3/library/codecs.html boms = [ - ('UTF-32-BE', salt.utils.stringutils.to_bytes(codecs.BOM_UTF32_BE)), - ('UTF-32-LE', salt.utils.stringutils.to_bytes(codecs.BOM_UTF32_LE)), - ('UTF-16-BE', salt.utils.stringutils.to_bytes(codecs.BOM_UTF16_BE)), - ('UTF-16-LE', salt.utils.stringutils.to_bytes(codecs.BOM_UTF16_LE)), - ('UTF-8', salt.utils.stringutils.to_bytes(codecs.BOM_UTF8)), - ('UTF-7', salt.utils.stringutils.to_bytes('\x2b\x2f\x76\x38\x2D')), - ('UTF-7', salt.utils.stringutils.to_bytes('\x2b\x2f\x76\x38')), - ('UTF-7', salt.utils.stringutils.to_bytes('\x2b\x2f\x76\x39')), - ('UTF-7', salt.utils.stringutils.to_bytes('\x2b\x2f\x76\x2b')), - ('UTF-7', salt.utils.stringutils.to_bytes('\x2b\x2f\x76\x2f')), + ("UTF-32-BE", salt.utils.stringutils.to_bytes(codecs.BOM_UTF32_BE)), + ("UTF-32-LE", salt.utils.stringutils.to_bytes(codecs.BOM_UTF32_LE)), + ("UTF-16-BE", salt.utils.stringutils.to_bytes(codecs.BOM_UTF16_BE)), + ("UTF-16-LE", salt.utils.stringutils.to_bytes(codecs.BOM_UTF16_LE)), + ("UTF-8", salt.utils.stringutils.to_bytes(codecs.BOM_UTF8)), + ("UTF-7", salt.utils.stringutils.to_bytes("\x2b\x2f\x76\x38\x2D")), + ("UTF-7", salt.utils.stringutils.to_bytes("\x2b\x2f\x76\x38")), + ("UTF-7", salt.utils.stringutils.to_bytes("\x2b\x2f\x76\x39")), + ("UTF-7", salt.utils.stringutils.to_bytes("\x2b\x2f\x76\x2b")), + ("UTF-7", salt.utils.stringutils.to_bytes("\x2b\x2f\x76\x2f")), ] for _encoding, bom in boms: if _data.startswith(bom): - log.debug('Found BOM for %s', _encoding) + log.debug("Found BOM for %s", _encoding) return _encoding return False def check_utf8_markers(_data): try: - decoded = _data.decode('UTF-8') + decoded = _data.decode("UTF-8") except UnicodeDecodeError: return False else: @@ -874,12 +878,12 @@ def get_encoding(path): return True if not os.path.isfile(path): - raise CommandExecutionError('Not a file') + raise CommandExecutionError("Not a file") try: - with fopen(path, 'rb') as fp_: + with fopen(path, "rb") as fp_: data = fp_.read(2048) except os.error: - raise CommandExecutionError('Failed to open file') + raise CommandExecutionError("Failed to open file") # Check for Unicode BOM encoding = check_bom(data) @@ -888,7 +892,7 @@ def get_encoding(path): # Check for UTF-8 markers if check_utf8_markers(data): - return 'UTF-8' + return "UTF-8" # Check system encoding if check_system_encoding(data): @@ -896,6 +900,6 @@ def get_encoding(path): # Check for ASCII first if check_ascii(data): - return 'ASCII' + return "ASCII" - raise CommandExecutionError('Could not detect file encoding') + raise CommandExecutionError("Could not detect file encoding") diff --git a/salt/utils/find.py b/salt/utils/find.py index b78385dbf8b..c9c5e09c918 100644 --- a/salt/utils/find.py +++ b/salt/utils/find.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Approximate the Unix find(1) command and return a list of paths that meet the specified criteria. @@ -81,37 +81,42 @@ the following: size: file size in bytes type: file type user: user name -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import re -import stat import shutil +import stat import sys import time -from subprocess import Popen, PIPE -try: - import grp - import pwd - # TODO: grp and pwd are both used in the code, we better make sure that - # that code never gets run if importing them does not succeed -except ImportError: - pass +from subprocess import PIPE, Popen -# Import 3rd-party libs -from salt.ext import six +import salt.defaults.exitcodes # Import salt libs import salt.utils.args import salt.utils.hashutils import salt.utils.path import salt.utils.stringutils -import salt.defaults.exitcodes + +# Import 3rd-party libs +from salt.ext import six from salt.utils.filebuffer import BufferedReader +try: + import grp + import pwd + + # TODO: grp and pwd are both used in the code, we better make sure that + # that code never gets run if importing them does not succeed +except ImportError: + pass + + # Set up logger log = logging.getLogger(__name__) @@ -119,22 +124,25 @@ _REQUIRES_PATH = 1 _REQUIRES_STAT = 2 _REQUIRES_CONTENTS = 4 -_FILE_TYPES = {'b': stat.S_IFBLK, - 'c': stat.S_IFCHR, - 'd': stat.S_IFDIR, - 'f': stat.S_IFREG, - 'l': stat.S_IFLNK, - 'p': stat.S_IFIFO, - 's': stat.S_IFSOCK, - stat.S_IFBLK: 'b', - stat.S_IFCHR: 'c', - stat.S_IFDIR: 'd', - stat.S_IFREG: 'f', - stat.S_IFLNK: 'l', - stat.S_IFIFO: 'p', - stat.S_IFSOCK: 's'} +_FILE_TYPES = { + "b": stat.S_IFBLK, + "c": stat.S_IFCHR, + "d": stat.S_IFDIR, + "f": stat.S_IFREG, + "l": stat.S_IFLNK, + "p": stat.S_IFIFO, + "s": stat.S_IFSOCK, + stat.S_IFBLK: "b", + stat.S_IFCHR: "c", + stat.S_IFDIR: "d", + stat.S_IFREG: "f", + stat.S_IFLNK: "l", + stat.S_IFIFO: "p", + stat.S_IFSOCK: "s", +} -_INTERVAL_REGEX = re.compile(r''' +_INTERVAL_REGEX = re.compile( + r""" ^\s* (?P<modifier>[+-]?) (?: (?P<week> \d+ (?:\.\d*)? ) \s* [wW] )? \s* @@ -143,14 +151,15 @@ _INTERVAL_REGEX = re.compile(r''' (?: (?P<minute> \d+ (?:\.\d*)? ) \s* [mM] )? \s* (?: (?P<second> \d+ (?:\.\d*)? ) \s* [sS] )? \s* $ - ''', - flags=re.VERBOSE) + """, + flags=re.VERBOSE, +) _PATH_DEPTH_IGNORED = (os.path.sep, os.path.curdir, os.path.pardir) def _parse_interval(value): - ''' + """ Convert an interval string like 1w3d6h into the number of seconds, time resolution (1 unit of the smallest specified time unit) and the modifier( '+', '-', or ''). @@ -159,41 +168,45 @@ def _parse_interval(value): h = hour m = minute s = second - ''' + """ match = _INTERVAL_REGEX.match(six.text_type(value)) if match is None: - raise ValueError('invalid time interval: \'{0}\''.format(value)) + raise ValueError("invalid time interval: '{0}'".format(value)) result = 0 resolution = None - for name, multiplier in [('second', 1), - ('minute', 60), - ('hour', 60 * 60), - ('day', 60 * 60 * 24), - ('week', 60 * 60 * 24 * 7)]: + for name, multiplier in [ + ("second", 1), + ("minute", 60), + ("hour", 60 * 60), + ("day", 60 * 60 * 24), + ("week", 60 * 60 * 24 * 7), + ]: if match.group(name) is not None: result += float(match.group(name)) * multiplier if resolution is None: resolution = multiplier - return result, resolution, match.group('modifier') + return result, resolution, match.group("modifier") def _parse_size(value): scalar = value.strip() - if scalar.startswith(('-', '+')): + if scalar.startswith(("-", "+")): style = scalar[0] scalar = scalar[1:] else: - style = '=' + style = "=" if len(scalar) > 0: - multiplier = {'b': 2 ** 0, - 'k': 2 ** 10, - 'm': 2 ** 20, - 'g': 2 ** 30, - 't': 2 ** 40}.get(scalar[-1].lower()) + multiplier = { + "b": 2 ** 0, + "k": 2 ** 10, + "m": 2 ** 20, + "g": 2 ** 30, + "t": 2 ** 40, + }.get(scalar[-1].lower()) if multiplier: scalar = scalar[:-1].strip() else: @@ -209,10 +222,10 @@ def _parse_size(value): except ValueError: raise ValueError('invalid size: "{0}"'.format(value)) - if style == '-': + if style == "-": min_size = 0 max_size = num - elif style == '+': + elif style == "+": min_size = num max_size = six.MAXSIZE else: @@ -223,50 +236,54 @@ def _parse_size(value): class Option(object): - ''' + """ Abstract base class for all find options. - ''' + """ + def requires(self): return _REQUIRES_PATH class NameOption(Option): - ''' + """ Match files with a case-sensitive glob filename pattern. Note: this is the 'basename' portion of a pathname. The option name is 'name', e.g. {'name' : '*.txt'}. - ''' + """ + def __init__(self, key, value): - self.regex = re.compile(value.replace('.', '\\.') - .replace('?', '.?') - .replace('*', '.*') + '$') + self.regex = re.compile( + value.replace(".", "\\.").replace("?", ".?").replace("*", ".*") + "$" + ) def match(self, dirname, filename, fstat): return self.regex.match(filename) class InameOption(Option): - ''' + """ Match files with a case-insensitive glob filename pattern. Note: this is the 'basename' portion of a pathname. The option name is 'iname', e.g. {'iname' : '*.TXT'}. - ''' + """ + def __init__(self, key, value): - self.regex = re.compile(value.replace('.', '\\.') - .replace('?', '.?') - .replace('*', '.*') + '$', - re.IGNORECASE) + self.regex = re.compile( + value.replace(".", "\\.").replace("?", ".?").replace("*", ".*") + "$", + re.IGNORECASE, + ) def match(self, dirname, filename, fstat): return self.regex.match(filename) class RegexOption(Option): - ''' + """ Match files with a case-sensitive regular expression. Note: this is the 'basename' portion of a pathname. The option name is 'regex', e.g. {'regex' : '.*\\.txt'}. - ''' + """ + def __init__(self, key, value): try: self.regex = re.compile(value) @@ -278,11 +295,12 @@ class RegexOption(Option): class IregexOption(Option): - ''' + """ Match files with a case-insensitive regular expression. Note: this is the 'basename' portion of a pathname. The option name is 'iregex', e.g. {'iregex' : '.*\\.txt'}. - ''' + """ + def __init__(self, key, value): try: self.regex = re.compile(value, re.IGNORECASE) @@ -294,7 +312,7 @@ class IregexOption(Option): class TypeOption(Option): - ''' + """ Match files by their file type(s). The file type(s) are specified as an optionally comma and/or space separated list of letters. @@ -306,10 +324,11 @@ class TypeOption(Option): p = FIFO (named pipe) s = socket The option name is 'type', e.g. {'type' : 'd'} or {'type' : 'bc'}. - ''' + """ + def __init__(self, key, value): # remove whitespace and commas - value = "".join(value.strip().replace(',', '').split()) + value = "".join(value.strip().replace(",", "").split()) self.ftypes = set() for ftype in value: try: @@ -325,15 +344,16 @@ class TypeOption(Option): class OwnerOption(Option): - ''' + """ Match files by their owner name(s) and/or uid(s), e.g. 'root'. The names are a space and/or comma separated list of names and/or integers. A match occurs when the file's uid matches any user specified. The option name is 'owner', e.g. {'owner' : 'root'}. - ''' + """ + def __init__(self, key, value): self.uids = set() - for name in value.replace(',', ' ').split(): + for name in value.replace(",", " ").split(): if name.isdigit(): self.uids.add(int(name)) else: @@ -350,15 +370,16 @@ class OwnerOption(Option): class GroupOption(Option): - ''' + """ Match files by their group name(s) and/or uid(s), e.g. 'admin'. The names are a space and/or comma separated list of names and/or integers. A match occurs when the file's gid matches any group specified. The option name is 'group', e.g. {'group' : 'admin'}. - ''' + """ + def __init__(self, key, value): self.gids = set() - for name in value.replace(',', ' ').split(): + for name in value.replace(",", " ").split(): if name.isdigit(): self.gids.add(int(name)) else: @@ -375,7 +396,7 @@ class GroupOption(Option): class SizeOption(Option): - ''' + """ Match files by their size. Prefix the size with '-' to find files the specified size and smaller. Prefix the size with '+' to find files the specified size and larger. @@ -387,7 +408,8 @@ class SizeOption(Option): g = gigabytes t = terabytes The option name is 'size', e.g. {'size' : '+1G'}. - ''' + """ + def __init__(self, key, value): self.min_size, self.max_size = _parse_size(value) @@ -399,7 +421,7 @@ class SizeOption(Option): class MtimeOption(Option): - ''' + """ Match files modified since the specified time. The option name is 'mtime', e.g. {'mtime' : '3d'}. The value format is [<num>w] [<num>[d]] [<num>h] [<num>m] [<num>s] @@ -410,7 +432,8 @@ class MtimeOption(Option): m = minute s = second Whitespace is ignored in the value. - ''' + """ + def __init__(self, key, value): secs, resolution, modifier = _parse_interval(value) self.mtime = time.time() - int(secs / resolution) * resolution @@ -420,16 +443,17 @@ class MtimeOption(Option): return _REQUIRES_STAT def match(self, dirname, filename, fstat): - if self.modifier == '-': + if self.modifier == "-": return fstat[stat.ST_MTIME] >= self.mtime else: return fstat[stat.ST_MTIME] <= self.mtime class GrepOption(Option): - '''Match files when a pattern occurs within the file. + """Match files when a pattern occurs within the file. The option name is 'grep', e.g. {'grep' : '(foo)|(bar}'}. - ''' + """ + def __init__(self, key, value): try: self.regex = re.compile(value) @@ -443,7 +467,7 @@ class GrepOption(Option): if not stat.S_ISREG(fstat[stat.ST_MODE]): return None dfilename = os.path.join(dirname, filename) - with BufferedReader(dfilename, mode='rb') as bread: + with BufferedReader(dfilename, mode="rb") as bread: for chunk in bread: if self.regex.search(chunk): return dfilename @@ -451,7 +475,7 @@ class GrepOption(Option): class PrintOption(Option): - ''' + """ Return information about a matched file. Print options are specified as a comma and/or space separated list of one or more of the following: @@ -464,17 +488,18 @@ class PrintOption(Option): size = file size in bytes type = file type user = user name - ''' + """ + def __init__(self, key, value): self.need_stat = False self.print_title = False self.fmt = [] - for arg in value.replace(',', ' ').split(): + for arg in value.replace(",", " ").split(): self.fmt.append(arg) - if arg not in ['name', 'path']: + if arg not in ["name", "path"]: self.need_stat = True if len(self.fmt) == 0: - self.fmt.append('path') + self.fmt.append("path") def requires(self): return _REQUIRES_STAT if self.need_stat else _REQUIRES_PATH @@ -482,39 +507,37 @@ class PrintOption(Option): def execute(self, fullpath, fstat, test=False): result = [] for arg in self.fmt: - if arg == 'path': + if arg == "path": result.append(fullpath) - elif arg == 'name': + elif arg == "name": result.append(os.path.basename(fullpath)) - elif arg == 'size': + elif arg == "size": result.append(fstat[stat.ST_SIZE]) - elif arg == 'type': - result.append( - _FILE_TYPES.get(stat.S_IFMT(fstat[stat.ST_MODE]), '?') - ) - elif arg == 'mode': + elif arg == "type": + result.append(_FILE_TYPES.get(stat.S_IFMT(fstat[stat.ST_MODE]), "?")) + elif arg == "mode": # PY3 compatibility: Use radix value 8 on int type-cast explicitly result.append(int(oct(fstat[stat.ST_MODE])[-3:], 8)) - elif arg == 'mtime': + elif arg == "mtime": result.append(fstat[stat.ST_MTIME]) - elif arg == 'user': + elif arg == "user": uid = fstat[stat.ST_UID] try: result.append(pwd.getpwuid(uid).pw_name) except KeyError: result.append(uid) - elif arg == 'group': + elif arg == "group": gid = fstat[stat.ST_GID] try: result.append(grp.getgrgid(gid).gr_name) except KeyError: result.append(gid) - elif arg == 'md5': + elif arg == "md5": if stat.S_ISREG(fstat[stat.ST_MODE]): - md5digest = salt.utils.hashutils.get_hash(fullpath, 'md5') + md5digest = salt.utils.hashutils.get_hash(fullpath, "md5") result.append(md5digest) else: - result.append('') + result.append("") if len(result) == 1: return result[0] @@ -523,7 +546,7 @@ class PrintOption(Option): class DeleteOption(TypeOption): - ''' + """ Deletes matched file. Delete options are one or more of the following: a: all file types @@ -534,10 +557,11 @@ class DeleteOption(TypeOption): f: plain file l: symlink s: socket - ''' + """ + def __init__(self, key, value): - if 'a' in value: - value = 'bcdpfls' + if "a" in value: + value = "bcdpfls" super(DeleteOption, self).__init__(key, value) def execute(self, fullpath, fstat, test=False): @@ -554,34 +578,31 @@ class DeleteOption(TypeOption): class ExecOption(Option): - ''' + """ Execute the given command, {} replaced by filename. Quote the {} if commands might include whitespace. - ''' + """ + def __init__(self, key, value): self.command = value def execute(self, fullpath, fstat, test=False): try: - command = self.command.replace('{}', fullpath) + command = self.command.replace("{}", fullpath) print(salt.utils.args.shlex_split(command)) - p = Popen(salt.utils.args.shlex_split(command), - stdout=PIPE, - stderr=PIPE) + p = Popen(salt.utils.args.shlex_split(command), stdout=PIPE, stderr=PIPE) (out, err) = p.communicate() if err: log.error( - 'Error running command: %s\n\n%s', + "Error running command: %s\n\n%s", command, - salt.utils.stringutils.to_str(err)) + salt.utils.stringutils.to_str(err), + ) return "{0}:\n{1}\n".format(command, salt.utils.stringutils.to_str(out)) except Exception as e: # pylint: disable=broad-except - log.error( - 'Exception while executing command "%s":\n\n%s', - command, - e) - return '{0}: Failed'.format(fullpath) + log.error('Exception while executing command "%s":\n\n%s', command, e) + return "{0}: Failed".format(fullpath) class Finder(object): @@ -590,20 +611,22 @@ class Finder(object): self.maxdepth = None self.mindepth = 0 self.test = False - criteria = {_REQUIRES_PATH: list(), - _REQUIRES_STAT: list(), - _REQUIRES_CONTENTS: list()} - if 'mindepth' in options: - self.mindepth = options['mindepth'] - del options['mindepth'] - if 'maxdepth' in options: - self.maxdepth = options['maxdepth'] - del options['maxdepth'] - if 'test' in options: - self.test = options['test'] - del options['test'] + criteria = { + _REQUIRES_PATH: list(), + _REQUIRES_STAT: list(), + _REQUIRES_CONTENTS: list(), + } + if "mindepth" in options: + self.mindepth = options["mindepth"] + del options["mindepth"] + if "maxdepth" in options: + self.maxdepth = options["maxdepth"] + del options["maxdepth"] + if "test" in options: + self.test = options["test"] + del options["test"] for key, value in six.iteritems(options): - if key.startswith('_'): + if key.startswith("_"): # this is a passthrough object, continue continue if value is None or len(str(value)) == 0: @@ -612,7 +635,7 @@ class Finder(object): obj = globals()[key.title() + "Option"](key, value) except KeyError: raise ValueError('invalid option "{0}"'.format(key)) - if hasattr(obj, 'match'): + if hasattr(obj, "match"): requires = obj.requires() if requires & _REQUIRES_CONTENTS: criteria[_REQUIRES_CONTENTS].append(obj) @@ -620,22 +643,24 @@ class Finder(object): criteria[_REQUIRES_STAT].append(obj) else: criteria[_REQUIRES_PATH].append(obj) - if hasattr(obj, 'execute'): + if hasattr(obj, "execute"): self.actions.append(obj) if len(self.actions) == 0: - self.actions.append(PrintOption('print', '')) + self.actions.append(PrintOption("print", "")) # order criteria so that least expensive checks are done first - self.criteria = criteria[_REQUIRES_PATH] + \ - criteria[_REQUIRES_STAT] + \ - criteria[_REQUIRES_CONTENTS] + self.criteria = ( + criteria[_REQUIRES_PATH] + + criteria[_REQUIRES_STAT] + + criteria[_REQUIRES_CONTENTS] + ) def find(self, path): - ''' + """ Generate filenames in path that satisfy criteria specified in the constructor. This method is a generator and should be repeatedly called until there are no more results. - ''' + """ if self.mindepth < 1: dirpath, name = os.path.split(path) match, fstat = self._check_criteria(dirpath, name, path) @@ -646,7 +671,9 @@ class Finder(object): for dirpath, dirs, files in salt.utils.path.os_walk(path): relpath = os.path.relpath(dirpath, path) depth = path_depth(relpath) + 1 - if depth >= self.mindepth and (self.maxdepth is None or self.maxdepth >= depth): + if depth >= self.mindepth and ( + self.maxdepth is None or self.maxdepth >= depth + ): for name in dirs + files: fullpath = os.path.join(dirpath, name) match, fstat = self._check_criteria(dirpath, name, fullpath) @@ -695,9 +722,9 @@ def path_depth(path): def find(path, options): - ''' + """ WRITEME - ''' + """ finder = Finder(options) for path in finder.find(path): yield path @@ -705,24 +732,24 @@ def find(path, options): def _main(): if len(sys.argv) < 2: - sys.stderr.write('usage: {0} path [options]\n'.format(sys.argv[0])) + sys.stderr.write("usage: {0} path [options]\n".format(sys.argv[0])) sys.exit(salt.defaults.exitcodes.EX_USAGE) path = sys.argv[1] criteria = {} for arg in sys.argv[2:]: - key, value = arg.split('=') + key, value = arg.split("=") criteria[key] = value try: finder = Finder(criteria) except ValueError as ex: - sys.stderr.write('error: {0}\n'.format(ex)) + sys.stderr.write("error: {0}\n".format(ex)) sys.exit(salt.defaults.exitcodes.EX_GENERIC) for result in finder.find(path): print(result) -if __name__ == '__main__': +if __name__ == "__main__": _main() diff --git a/salt/utils/fsutils.py b/salt/utils/fsutils.py index 9ae3187247c..86af76f273b 100644 --- a/salt/utils/fsutils.py +++ b/salt/utils/fsutils.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Run-time utilities -''' +""" # # Copyright (C) 2014 SUSE LLC # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import re -import os +from __future__ import absolute_import, print_function, unicode_literals + import logging +import os +import re # Import Salt libs import salt.utils.files @@ -23,78 +24,79 @@ log = logging.getLogger(__name__) def _verify_run(out, cmd=None): - ''' + """ Crash to the log if command execution was not successful. - ''' - if out.get('retcode', 0) and out['stderr']: + """ + if out.get("retcode", 0) and out["stderr"]: if cmd: - log.debug('Command: \'%s\'', cmd) + log.debug("Command: '%s'", cmd) - log.debug('Return code: %s', out.get('retcode')) - log.debug('Error output:\n%s', out.get('stderr', 'N/A')) + log.debug("Return code: %s", out.get("retcode")) + log.debug("Error output:\n%s", out.get("stderr", "N/A")) - raise CommandExecutionError(out['stderr']) + raise CommandExecutionError(out["stderr"]) def _get_mounts(fs_type=None): - ''' + """ List mounted filesystems. - ''' + """ mounts = {} - with salt.utils.files.fopen('/proc/mounts') as fhr: + with salt.utils.files.fopen("/proc/mounts") as fhr: for line in fhr.readlines(): line = salt.utils.stringutils.to_unicode(line) - device, mntpnt, fstype, options, fs_freq, fs_passno = line.strip().split(" ") + device, mntpnt, fstype, options, fs_freq, fs_passno = line.strip().split( + " " + ) if fs_type and fstype != fs_type: continue if mounts.get(device) is None: mounts[device] = [] - data = { - 'mount_point': mntpnt, - 'options': options.split(",") - } + data = {"mount_point": mntpnt, "options": options.split(",")} if not fs_type: - data['type'] = fstype + data["type"] = fstype mounts[device].append(data) return mounts # TODO: Due to "blkid -o export" strongly differs from system to system, this must go away in favor of _blkid() below! def _blkid_output(out, fs_type=None): - ''' + """ Parse blkid output. - ''' + """ flt = lambda data: [el for el in data if el.strip()] data = {} - for dev_meta in flt(out.split('\n\n')): + for dev_meta in flt(out.split("\n\n")): dev = {} - for items in flt(dev_meta.strip().split('\n')): - key, val = items.split('=', 1) + for items in flt(dev_meta.strip().split("\n")): + key, val = items.split("=", 1) dev[key.lower()] = val - if fs_type and dev.get('type', '') == fs_type or not fs_type: - if 'type' in dev and fs_type: - dev.pop('type') - data[dev.pop('devname')] = dev + if fs_type and dev.get("type", "") == fs_type or not fs_type: + if "type" in dev and fs_type: + dev.pop("type") + data[dev.pop("devname")] = dev if fs_type: mounts = _get_mounts(fs_type) for device in six.iterkeys(mounts): if data.get(device): - data[device]['mounts'] = mounts[device] + data[device]["mounts"] = mounts[device] return data def _blkid(fs_type=None): - ''' + """ Return available media devices. :param fs_type: Filter only devices that are formatted by that file system. - ''' + """ flt = lambda data: [el for el in data if el.strip()] data = dict() - for dev_meta in flt(os.popen("blkid -o full").read().split(os.linesep)): # No __salt__ around at this point. + for dev_meta in flt( + os.popen("blkid -o full").read().split(os.linesep) + ): # No __salt__ around at this point. dev_meta = dev_meta.strip() if not dev_meta: continue @@ -102,24 +104,24 @@ def _blkid(fs_type=None): dev_name = device.pop(0)[:-1] data[dev_name] = dict() for k_set in device: - ks_key, ks_value = [elm.replace('"', '') for elm in k_set.split("=")] + ks_key, ks_value = [elm.replace('"', "") for elm in k_set.split("=")] data[dev_name][ks_key.lower()] = ks_value if fs_type: mounts = _get_mounts(fs_type) for device in six.iterkeys(mounts): if data.get(device): - data[device]['mounts'] = mounts[device] + data[device]["mounts"] = mounts[device] return data def _is_device(path): - ''' + """ Return True if path is a physical device. - ''' - out = __salt__['cmd.run_all']('file -i {0}'.format(path)) + """ + out = __salt__["cmd.run_all"]("file -i {0}".format(path)) _verify_run(out) # Always [device, mime, charset]. See (file --help) - return re.split(r'\s+', out['stdout'])[1][:-1] == 'inode/blockdevice' + return re.split(r"\s+", out["stdout"])[1][:-1] == "inode/blockdevice" diff --git a/salt/utils/functools.py b/salt/utils/functools.py index f995922b303..690b3af7b80 100644 --- a/salt/utils/functools.py +++ b/salt/utils/functools.py @@ -1,34 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" Utility functions to modify other functions -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import logging # Import Python libs import types -import logging + +import salt.utils.args # Import salt libs from salt.exceptions import SaltInvocationError -import salt.utils.args -from salt.ext.six.moves import zip # Import 3rd-party libs from salt.ext import six +from salt.ext.six.moves import zip log = logging.getLogger(__name__) def namespaced_function(function, global_dict, defaults=None, preserve_context=False): - ''' + """ Redefine (clone) a function under a different globals() namespace scope preserve_context: Allow keeping the context taken from orignal namespace, and extend it with globals() taken from new targetted namespace. - ''' + """ if defaults is None: defaults = function.__defaults__ @@ -41,36 +43,37 @@ def namespaced_function(function, global_dict, defaults=None, preserve_context=F global_dict, name=function.__name__, argdefs=defaults, - closure=function.__closure__ + closure=function.__closure__, ) new_namespaced_function.__dict__.update(function.__dict__) return new_namespaced_function def alias_function(fun, name, doc=None): - ''' + """ Copy a function - ''' - alias_fun = types.FunctionType(fun.__code__, - fun.__globals__, - str(name), # future lint: disable=blacklisted-function - fun.__defaults__, - fun.__closure__) + """ + alias_fun = types.FunctionType( + fun.__code__, + fun.__globals__, + str(name), # future lint: disable=blacklisted-function + fun.__defaults__, + fun.__closure__, + ) alias_fun.__dict__.update(fun.__dict__) if doc and isinstance(doc, six.string_types): alias_fun.__doc__ = doc else: orig_name = fun.__name__ - alias_msg = ('\nThis function is an alias of ' - '``{0}``.\n'.format(orig_name)) - alias_fun.__doc__ = alias_msg + (fun.__doc__ or '') + alias_msg = "\nThis function is an alias of " "``{0}``.\n".format(orig_name) + alias_fun.__doc__ = alias_msg + (fun.__doc__ or "") return alias_fun def parse_function(function_arguments): - ''' + """ Helper function to parse function_arguments (module.run format) into args and kwargs. This function is similar to salt.utils.data.repack_dictlist, except that this @@ -80,7 +83,7 @@ def parse_function(function_arguments): :rtype: dict :return: Dictionary with ``args`` and ``kwargs`` keyword. - ''' + """ function_args = [] function_kwargs = {} for item in function_arguments: @@ -88,22 +91,30 @@ def parse_function(function_arguments): function_kwargs.update(item) else: function_args.append(item) - return {'args': function_args, 'kwargs': function_kwargs} + return {"args": function_args, "kwargs": function_kwargs} def call_function(salt_function, *args, **kwargs): - ''' + """ Calls a function from the specified module. :param function salt_function: Function reference to call :return: The result of the function call - ''' + """ argspec = salt.utils.args.get_function_argspec(salt_function) # function_kwargs is initialized to a dictionary of keyword arguments the function to be run accepts - function_kwargs = dict(zip(argspec.args[-len(argspec.defaults or []):], # pylint: disable=incompatible-py3-code - argspec.defaults or [])) + function_kwargs = dict( + zip( + argspec.args[ + -len(argspec.defaults or []) : + ], # pylint: disable=incompatible-py3-code + argspec.defaults or [], + ) + ) # expected_args is initialized to a list of positional arguments that the function to be run accepts - expected_args = argspec.args[:len(argspec.args or []) - len(argspec.defaults or [])] + expected_args = argspec.args[ + : len(argspec.args or []) - len(argspec.defaults or []) + ] function_args, kw_to_arg_type = [], {} for funcset in reversed(args or []): if not isinstance(funcset, dict): @@ -133,10 +144,16 @@ def call_function(salt_function, *args, **kwargs): for arg in argspec.args[_passed_prm:]: if arg not in function_kwargs: missing.append(arg) + else: + # Found the expected argument as a keyword + # increase the _passed_prm count + _passed_prm += 1 if missing: - raise SaltInvocationError('Missing arguments: {0}'.format(', '.join(missing))) + raise SaltInvocationError("Missing arguments: {0}".format(", ".join(missing))) elif _exp_prm > _passed_prm: - raise SaltInvocationError('Function expects {0} positional parameters, ' - 'got only {1}'.format(_exp_prm, _passed_prm)) + raise SaltInvocationError( + "Function expects {0} positional parameters, " + "got only {1}".format(_exp_prm, _passed_prm) + ) return salt_function(*function_args, **function_kwargs) diff --git a/salt/utils/gitfs.py b/salt/utils/gitfs.py index 5c2ee31ec10..297938f3dc2 100644 --- a/salt/utils/gitfs.py +++ b/salt/utils/gitfs.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Classes which provide the shared base for GitFS, git_pillar, and winrepo -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import contextlib import copy import errno @@ -19,10 +20,12 @@ import stat import subprocess import sys import time -import salt.ext.tornado.ioloop import weakref from datetime import datetime +import salt.ext.tornado.ioloop +import salt.fileserver + # Import salt libs import salt.utils.configparser import salt.utils.data @@ -36,55 +39,49 @@ import salt.utils.stringutils import salt.utils.url import salt.utils.user import salt.utils.versions -import salt.fileserver from salt.config import DEFAULT_MASTER_OPTS as _DEFAULT_MASTER_OPTS -from salt.utils.odict import OrderedDict -from salt.utils.process import os_is_running as pid_exists -from salt.exceptions import ( - FileserverConfigError, - GitLockError, - get_error_message -) -from salt.utils.event import tagify -from salt.utils.versions import LooseVersion as _LooseVersion +from salt.exceptions import FileserverConfigError, GitLockError, get_error_message # Import third party libs from salt.ext import six +from salt.utils.event import tagify +from salt.utils.odict import OrderedDict +from salt.utils.process import os_is_running as pid_exists +from salt.utils.versions import LooseVersion as _LooseVersion -VALID_REF_TYPES = _DEFAULT_MASTER_OPTS['gitfs_ref_types'] +VALID_REF_TYPES = _DEFAULT_MASTER_OPTS["gitfs_ref_types"] # Optional per-remote params that can only be used on a per-remote basis, and # thus do not have defaults in salt/config.py. -PER_REMOTE_ONLY = ('name',) +PER_REMOTE_ONLY = ("name",) # Params which are global only and cannot be overridden for a single remote. GLOBAL_ONLY = () SYMLINK_RECURSE_DEPTH = 100 # Auth support (auth params can be global or per-remote, too) -AUTH_PROVIDERS = ('pygit2',) -AUTH_PARAMS = ('user', 'password', 'pubkey', 'privkey', 'passphrase', - 'insecure_auth') +AUTH_PROVIDERS = ("pygit2",) +AUTH_PARAMS = ("user", "password", "pubkey", "privkey", "passphrase", "insecure_auth") # GitFS only: params which can be overridden for a single saltenv. Aside from # 'ref', this must be a subset of the per-remote params passed to the # constructor for the GitProvider subclasses. -PER_SALTENV_PARAMS = ('mountpoint', 'root', 'ref') +PER_SALTENV_PARAMS = ("mountpoint", "root", "ref") _RECOMMEND_GITPYTHON = ( - 'GitPython is installed, you may wish to set %s_provider to ' - '\'gitpython\' to use GitPython for %s support.' + "GitPython is installed, you may wish to set %s_provider to " + "'gitpython' to use GitPython for %s support." ) _RECOMMEND_PYGIT2 = ( - 'pygit2 is installed, you may wish to set %s_provider to ' - '\'pygit2\' to use pygit2 for for %s support.' + "pygit2 is installed, you may wish to set %s_provider to " + "'pygit2' to use pygit2 for for %s support." ) _INVALID_REPO = ( - 'Cache path %s (corresponding remote: %s) exists but is not a valid ' - 'git repository. You will need to manually delete this directory on the ' - 'master to continue to use this %s remote.' + "Cache path %s (corresponding remote: %s) exists but is not a valid " + "git repository. You will need to manually delete this directory on the " + "master to continue to use this %s remote." ) log = logging.getLogger(__name__) @@ -93,6 +90,7 @@ log = logging.getLogger(__name__) try: import git import gitdb + GITPYTHON_VERSION = _LooseVersion(git.__version__) except Exception: # pylint: disable=broad-except GITPYTHON_VERSION = None @@ -100,8 +98,9 @@ except Exception: # pylint: disable=broad-except try: # Squelch warning on cent7 due to them upgrading cffi import warnings + with warnings.catch_warnings(): - warnings.simplefilter('ignore') + warnings.simplefilter("ignore") import pygit2 PYGIT2_VERSION = _LooseVersion(pygit2.__version__) LIBGIT2_VERSION = _LooseVersion(pygit2.LIBGIT2_VERSION) @@ -109,7 +108,7 @@ try: # Work around upstream bug where bytestrings were being decoded using the # default encoding (which is usually ascii on Python 2). This was fixed # on 2 Feb 2018, so releases prior to 0.26.3 will need a workaround. - if PYGIT2_VERSION <= _LooseVersion('0.26.3'): + if PYGIT2_VERSION <= _LooseVersion("0.26.3"): try: import pygit2.ffi import pygit2.remote @@ -119,10 +118,11 @@ try: # useless. pass else: + def __maybe_string(ptr): if not ptr: return None - return pygit2.ffi.string(ptr).decode('utf-8') + return pygit2.ffi.string(ptr).decode("utf-8") pygit2.remote.maybe_string = __maybe_string @@ -141,39 +141,39 @@ except Exception as exc: # pylint: disable=broad-except PYGIT2_VERSION = None LIBGIT2_VERSION = None if not isinstance(exc, ImportError): - log.exception('Failed to import pygit2') + log.exception("Failed to import pygit2") # pylint: enable=import-error # Minimum versions for backend providers -GITPYTHON_MINVER = _LooseVersion('0.3') -PYGIT2_MINVER = _LooseVersion('0.20.3') -LIBGIT2_MINVER = _LooseVersion('0.20.0') +GITPYTHON_MINVER = _LooseVersion("0.3") +PYGIT2_MINVER = _LooseVersion("0.20.3") +LIBGIT2_MINVER = _LooseVersion("0.20.0") def enforce_types(key, val): - ''' + """ Force params to be strings unless they should remain a different type - ''' + """ non_string_params = { - 'ssl_verify': bool, - 'insecure_auth': bool, - 'disable_saltenv_mapping': bool, - 'saltenv_whitelist': 'stringlist', - 'saltenv_blacklist': 'stringlist', - 'refspecs': 'stringlist', - 'ref_types': 'stringlist', - 'update_interval': int, + "ssl_verify": bool, + "insecure_auth": bool, + "disable_saltenv_mapping": bool, + "saltenv_whitelist": "stringlist", + "saltenv_blacklist": "stringlist", + "refspecs": "stringlist", + "ref_types": "stringlist", + "update_interval": int, } def _find_global(key): for item in non_string_params: try: - if key.endswith('_' + item): + if key.endswith("_" + item): ret = item break except TypeError: - if key.endswith('_' + six.text_type(item)): + if key.endswith("_" + six.text_type(item)): ret = item break else: @@ -186,73 +186,82 @@ def enforce_types(key, val): return six.text_type(val) expected = non_string_params[key] - if expected == 'stringlist': + if expected == "stringlist": if not isinstance(val, (six.string_types, list)): val = six.text_type(val) if isinstance(val, six.string_types): - return [x.strip() for x in val.split(',')] + return [x.strip() for x in val.split(",")] return [six.text_type(x) for x in val] else: try: return expected(val) except Exception as exc: # pylint: disable=broad-except log.error( - 'Failed to enforce type for key=%s with val=%s, falling back ' - 'to a string', key, val + "Failed to enforce type for key=%s with val=%s, falling back " + "to a string", + key, + val, ) return six.text_type(val) def failhard(role): - ''' + """ Fatal configuration issue, raise an exception - ''' - raise FileserverConfigError('Failed to load {0}'.format(role)) + """ + raise FileserverConfigError("Failed to load {0}".format(role)) class GitProvider(object): - ''' + """ Base class for gitfs/git_pillar provider classes. Should never be used directly. self.provider should be set in the sub-class' __init__ function before invoking the parent class' __init__. - ''' - def __init__(self, opts, remote, per_remote_defaults, per_remote_only, - override_params, cache_root, role='gitfs'): + """ + + def __init__( + self, + opts, + remote, + per_remote_defaults, + per_remote_only, + override_params, + cache_root, + role="gitfs", + ): self.opts = opts self.role = role self.global_saltenv = salt.utils.data.repack_dictlist( - self.opts.get('{0}_saltenv'.format(self.role), []), + self.opts.get("{0}_saltenv".format(self.role), []), strict=True, recurse=True, key_cb=six.text_type, - val_cb=lambda x, y: six.text_type(y)) + val_cb=lambda x, y: six.text_type(y), + ) self.conf = copy.deepcopy(per_remote_defaults) # Remove the 'salt://' from the beginning of any globally-defined # per-saltenv mountpoints for saltenv, saltenv_conf in six.iteritems(self.global_saltenv): - if 'mountpoint' in saltenv_conf: - self.global_saltenv[saltenv]['mountpoint'] = \ - salt.utils.url.strip_proto( - self.global_saltenv[saltenv]['mountpoint'] - ) + if "mountpoint" in saltenv_conf: + self.global_saltenv[saltenv]["mountpoint"] = salt.utils.url.strip_proto( + self.global_saltenv[saltenv]["mountpoint"] + ) - per_remote_collisions = [x for x in override_params - if x in per_remote_only] + per_remote_collisions = [x for x in override_params if x in per_remote_only] if per_remote_collisions: log.critical( - 'The following parameter names are restricted to per-remote ' - 'use only: %s. This is a bug, please report it.', - ', '.join(per_remote_collisions) + "The following parameter names are restricted to per-remote " + "use only: %s. This is a bug, please report it.", + ", ".join(per_remote_collisions), ) try: valid_per_remote_params = override_params + per_remote_only except TypeError: - valid_per_remote_params = \ - list(override_params) + list(per_remote_only) + valid_per_remote_params = list(override_params) + list(per_remote_only) if isinstance(remote, dict): self.id = next(iter(remote)) @@ -263,56 +272,57 @@ class GitProvider(object): strict=True, recurse=True, key_cb=six.text_type, - val_cb=enforce_types) + val_cb=enforce_types, + ) if not per_remote_conf: log.critical( - 'Invalid per-remote configuration for %s remote \'%s\'. ' - 'If no per-remote parameters are being specified, there ' - 'may be a trailing colon after the URL, which should be ' - 'removed. Check the master configuration file.', - self.role, self.id + "Invalid per-remote configuration for %s remote '%s'. " + "If no per-remote parameters are being specified, there " + "may be a trailing colon after the URL, which should be " + "removed. Check the master configuration file.", + self.role, + self.id, ) failhard(self.role) per_remote_errors = False - for param in (x for x in per_remote_conf - if x not in valid_per_remote_params): + for param in ( + x for x in per_remote_conf if x not in valid_per_remote_params + ): per_remote_errors = True - if param in AUTH_PARAMS \ - and self.provider not in AUTH_PROVIDERS: + if param in AUTH_PARAMS and self.provider not in AUTH_PROVIDERS: msg = ( - '{0} authentication parameter \'{1}\' (from remote ' - '\'{2}\') is only supported by the following ' - 'provider(s): {3}. Current {0}_provider is \'{4}\'.' - .format( + "{0} authentication parameter '{1}' (from remote " + "'{2}') is only supported by the following " + "provider(s): {3}. Current {0}_provider is '{4}'.".format( self.role, param, self.id, - ', '.join(AUTH_PROVIDERS), - self.provider + ", ".join(AUTH_PROVIDERS), + self.provider, ) ) - if self.role == 'gitfs': + if self.role == "gitfs": msg += ( - 'See the GitFS Walkthrough in the Salt ' - 'documentation for further information.' + "See the GitFS Walkthrough in the Salt " + "documentation for further information." ) log.critical(msg) else: msg = ( - 'Invalid {0} configuration parameter \'{1}\' in ' - 'remote \'{2}\'. Valid parameters are: {3}.'.format( + "Invalid {0} configuration parameter '{1}' in " + "remote '{2}'. Valid parameters are: {3}.".format( self.role, param, self.id, - ', '.join(valid_per_remote_params) + ", ".join(valid_per_remote_params), ) ) - if self.role == 'gitfs': + if self.role == "gitfs": msg += ( - ' See the GitFS Walkthrough in the Salt ' - 'documentation for further information.' + " See the GitFS Walkthrough in the Salt " + "documentation for further information." ) log.critical(msg) @@ -327,108 +337,113 @@ class GitProvider(object): # Winrepo doesn't support the 'root' option, but it still must be part # of the GitProvider object because other code depends on it. Add it as # an empty string. - if 'root' not in self.conf: - self.conf['root'] = '' + if "root" not in self.conf: + self.conf["root"] = "" - if self.role == 'winrepo' and 'name' not in self.conf: + if self.role == "winrepo" and "name" not in self.conf: # Ensure that winrepo has the 'name' parameter set if it wasn't # provided. Default to the last part of the URL, minus the .git if # it is present. - self.conf['name'] = self.url.rsplit('/', 1)[-1] + self.conf["name"] = self.url.rsplit("/", 1)[-1] # Remove trailing .git from name - if self.conf['name'].lower().endswith('.git'): - self.conf['name'] = self.conf['name'][:-4] + if self.conf["name"].lower().endswith(".git"): + self.conf["name"] = self.conf["name"][:-4] - if 'mountpoint' in self.conf: + if "mountpoint" in self.conf: # Remove the 'salt://' from the beginning of the mountpoint, as # well as any additional leading/trailing slashes - self.conf['mountpoint'] = \ - salt.utils.url.strip_proto(self.conf['mountpoint']).strip('/') + self.conf["mountpoint"] = salt.utils.url.strip_proto( + self.conf["mountpoint"] + ).strip("/") else: # For providers which do not use a mountpoint, assume the # filesystem is mounted at the root of the fileserver. - self.conf['mountpoint'] = '' + self.conf["mountpoint"] = "" - if 'saltenv' not in self.conf: - self.conf['saltenv'] = {} + if "saltenv" not in self.conf: + self.conf["saltenv"] = {} else: - for saltenv, saltenv_conf in six.iteritems(self.conf['saltenv']): - if 'mountpoint' in saltenv_conf: - saltenv_ptr = self.conf['saltenv'][saltenv] - saltenv_ptr['mountpoint'] = \ - salt.utils.url.strip_proto(saltenv_ptr['mountpoint']) + for saltenv, saltenv_conf in six.iteritems(self.conf["saltenv"]): + if "mountpoint" in saltenv_conf: + saltenv_ptr = self.conf["saltenv"][saltenv] + saltenv_ptr["mountpoint"] = salt.utils.url.strip_proto( + saltenv_ptr["mountpoint"] + ) for key, val in six.iteritems(self.conf): if key not in PER_SALTENV_PARAMS and not hasattr(self, key): setattr(self, key, val) for key in PER_SALTENV_PARAMS: - if key != 'ref': - setattr(self, '_' + key, self.conf[key]) + if key != "ref": + setattr(self, "_" + key, self.conf[key]) self.add_conf_overlay(key) - if not hasattr(self, 'refspecs'): + if not hasattr(self, "refspecs"): # This was not specified as a per-remote overrideable parameter # when instantiating an instance of a GitBase subclass. Make sure # that we set this attribute so we at least have a sane default and # are able to fetch. - key = '{0}_refspecs'.format(self.role) + key = "{0}_refspecs".format(self.role) try: default_refspecs = _DEFAULT_MASTER_OPTS[key] except KeyError: log.critical( - 'The \'%s\' option has no default value in ' - 'salt/config/__init__.py.', key + "The '%s' option has no default value in " + "salt/config/__init__.py.", + key, ) failhard(self.role) - setattr(self, 'refspecs', default_refspecs) + setattr(self, "refspecs", default_refspecs) log.debug( - 'The \'refspecs\' option was not explicitly defined as a ' - 'configurable parameter. Falling back to %s for %s remote ' - '\'%s\'.', default_refspecs, self.role, self.id + "The 'refspecs' option was not explicitly defined as a " + "configurable parameter. Falling back to %s for %s remote " + "'%s'.", + default_refspecs, + self.role, + self.id, ) # Discard the conf dictionary since we have set all of the config # params as attributes - delattr(self, 'conf') + delattr(self, "conf") # Normalize components of the ref_types configuration and check for # invalid configuration. - if hasattr(self, 'ref_types'): + if hasattr(self, "ref_types"): self.ref_types = [x.lower() for x in self.ref_types] - invalid_ref_types = [x for x in self.ref_types - if x not in VALID_REF_TYPES] + invalid_ref_types = [x for x in self.ref_types if x not in VALID_REF_TYPES] if invalid_ref_types: log.critical( - 'The following ref_types for %s remote \'%s\' are ' - 'invalid: %s. The supported values are: %s', + "The following ref_types for %s remote '%s' are " + "invalid: %s. The supported values are: %s", self.role, self.id, - ', '.join(invalid_ref_types), - ', '.join(VALID_REF_TYPES), + ", ".join(invalid_ref_types), + ", ".join(VALID_REF_TYPES), ) failhard(self.role) if not isinstance(self.url, six.string_types): log.critical( - 'Invalid %s remote \'%s\'. Remotes must be strings, you ' - 'may need to enclose the URL in quotes', self.role, self.id + "Invalid %s remote '%s'. Remotes must be strings, you " + "may need to enclose the URL in quotes", + self.role, + self.id, ) failhard(self.role) - hash_type = getattr(hashlib, self.opts.get('hash_type', 'md5')) + hash_type = getattr(hashlib, self.opts.get("hash_type", "md5")) if six.PY3: # We loaded this data from yaml configuration files, so, its safe # to use UTF-8 - self.hash = hash_type(self.id.encode('utf-8')).hexdigest() + self.hash = hash_type(self.id.encode("utf-8")).hexdigest() else: self.hash = hash_type(self.id).hexdigest() - self.cachedir_basename = getattr(self, 'name', self.hash) + self.cachedir_basename = getattr(self, "name", self.hash) self.cachedir = salt.utils.path.join(cache_root, self.cachedir_basename) - self.linkdir = salt.utils.path.join(cache_root, - 'links', - self.cachedir_basename) + self.linkdir = salt.utils.path.join(cache_root, "links", self.cachedir_basename) if not os.path.isdir(self.cachedir): os.makedirs(self.cachedir) @@ -436,73 +451,79 @@ class GitProvider(object): try: self.new = self.init_remote() except Exception as exc: # pylint: disable=broad-except - msg = ('Exception caught while initializing {0} remote \'{1}\': ' - '{2}'.format(self.role, self.id, exc)) + msg = "Exception caught while initializing {0} remote '{1}': " "{2}".format( + self.role, self.id, exc + ) if isinstance(self, GitPython): - msg += ' Perhaps git is not available.' + msg += " Perhaps git is not available." log.critical(msg, exc_info=True) failhard(self.role) def _get_envs_from_ref_paths(self, refs): - ''' + """ Return the names of remote refs (stripped of the remote name) and tags which are map to the branches and tags. - ''' + """ + def _check_ref(env_set, rname): - ''' + """ Add the appropriate saltenv(s) to the set - ''' + """ if rname in self.saltenv_revmap: env_set.update(self.saltenv_revmap[rname]) else: if rname == self.base: - env_set.add('base') + env_set.add("base") elif not self.disable_saltenv_mapping: env_set.add(rname) - use_branches = 'branch' in self.ref_types - use_tags = 'tag' in self.ref_types + use_branches = "branch" in self.ref_types + use_tags = "tag" in self.ref_types ret = set() if salt.utils.stringutils.is_hex(self.base): # gitfs_base or per-saltenv 'base' may point to a commit ID, which # would not show up in the refs. Make sure we include it. - ret.add('base') + ret.add("base") for ref in salt.utils.data.decode(refs): - if ref.startswith('refs/'): + if ref.startswith("refs/"): ref = ref[5:] - rtype, rname = ref.split('/', 1) - if rtype == 'remotes' and use_branches: - parted = rname.partition('/') + rtype, rname = ref.split("/", 1) + if rtype == "remotes" and use_branches: + parted = rname.partition("/") rname = parted[2] if parted[2] else parted[0] _check_ref(ret, rname) - elif rtype == 'tags' and use_tags: + elif rtype == "tags" and use_tags: _check_ref(ret, rname) return ret - def _get_lock_file(self, lock_type='update'): - return salt.utils.path.join(self.gitdir, lock_type + '.lk') + def _get_lock_file(self, lock_type="update"): + return salt.utils.path.join(self.gitdir, lock_type + ".lk") @classmethod def add_conf_overlay(cls, name): - ''' + """ Programmatically determine config value based on the desired saltenv - ''' - def _getconf(self, tgt_env='base'): - strip_sep = lambda x: x.rstrip(os.sep) \ - if name in ('root', 'mountpoint') \ - else x - if self.role != 'gitfs': - return strip_sep(getattr(self, '_' + name)) + """ + + def _getconf(self, tgt_env="base"): + strip_sep = ( + lambda x: x.rstrip(os.sep) if name in ("root", "mountpoint") else x + ) + if self.role != "gitfs": + return strip_sep(getattr(self, "_" + name)) # Get saltenv-specific configuration saltenv_conf = self.saltenv.get(tgt_env, {}) - if name == 'ref': + if name == "ref": + def _get_per_saltenv(tgt_env): if name in saltenv_conf: return saltenv_conf[name] - elif tgt_env in self.global_saltenv \ - and name in self.global_saltenv[tgt_env]: + elif ( + tgt_env in self.global_saltenv + and name in self.global_saltenv[tgt_env] + ): return self.global_saltenv[tgt_env][name] else: return None @@ -513,27 +534,33 @@ class GitProvider(object): all_saltenvs_ref = self.all_saltenvs if per_saltenv_ref and all_saltenvs_ref != per_saltenv_ref: log.debug( - 'The per-saltenv configuration has mapped the ' - '\'%s\' branch/tag to saltenv \'%s\' for %s ' - 'remote \'%s\', but this remote has ' - 'all_saltenvs set to \'%s\'. The per-saltenv ' - 'mapping will be ignored in favor of \'%s\'.', - per_saltenv_ref, tgt_env, self.role, self.id, - all_saltenvs_ref, all_saltenvs_ref + "The per-saltenv configuration has mapped the " + "'%s' branch/tag to saltenv '%s' for %s " + "remote '%s', but this remote has " + "all_saltenvs set to '%s'. The per-saltenv " + "mapping will be ignored in favor of '%s'.", + per_saltenv_ref, + tgt_env, + self.role, + self.id, + all_saltenvs_ref, + all_saltenvs_ref, ) return all_saltenvs_ref except AttributeError: # all_saltenvs not configured for this remote pass - if tgt_env == 'base': + if tgt_env == "base": return self.base elif self.disable_saltenv_mapping: if per_saltenv_ref is None: log.debug( - 'saltenv mapping is diabled for %s remote \'%s\' ' - 'and saltenv \'%s\' is not explicitly mapped', - self.role, self.id, tgt_env + "saltenv mapping is diabled for %s remote '%s' " + "and saltenv '%s' is not explicitly mapped", + self.role, + self.id, + tgt_env, ) return per_saltenv_ref else: @@ -541,19 +568,21 @@ class GitProvider(object): if name in saltenv_conf: return strip_sep(saltenv_conf[name]) - elif tgt_env in self.global_saltenv \ - and name in self.global_saltenv[tgt_env]: + elif ( + tgt_env in self.global_saltenv and name in self.global_saltenv[tgt_env] + ): return strip_sep(self.global_saltenv[tgt_env][name]) else: - return strip_sep(getattr(self, '_' + name)) + return strip_sep(getattr(self, "_" + name)) + setattr(cls, name, _getconf) def check_root(self): - ''' + """ Check if the relative root path exists in the checked-out copy of the remote. Return the full path to that relative root if it does exist, otherwise return None. - ''' + """ # No need to pass an environment to self.root() here since per-saltenv # configuration is a gitfs-only feature and check_root() is not used # for gitfs. @@ -561,17 +590,19 @@ class GitProvider(object): if os.path.isdir(root_dir): return root_dir log.error( - 'Root path \'%s\' not present in %s remote \'%s\', ' - 'skipping.', self.root(), self.role, self.id + "Root path '%s' not present in %s remote '%s', " "skipping.", + self.root(), + self.role, + self.id, ) return None def clean_stale_refs(self): - ''' + """ Remove stale refs so that they are no longer seen as fileserver envs - ''' + """ cleaned = [] - cmd_str = 'git remote prune origin' + cmd_str = "git remote prune origin" # Attempt to force all output to plain ascii english, which is what some parsing code # may expect. @@ -587,37 +618,43 @@ class GitProvider(object): cwd=os.path.dirname(self.gitdir), env=env, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + stderr=subprocess.STDOUT, + ) output = cmd.communicate()[0] if six.PY3: output = output.decode(__salt_system_encoding__) if cmd.returncode != 0: log.warning( - 'Failed to prune stale branches for %s remote \'%s\'. ' - 'Output from \'%s\' follows:\n%s', - self.role, self.id, cmd_str, output + "Failed to prune stale branches for %s remote '%s'. " + "Output from '%s' follows:\n%s", + self.role, + self.id, + cmd_str, + output, ) else: - marker = ' * [pruned] ' - for line in salt.utils.itertools.split(output, '\n'): + marker = " * [pruned] " + for line in salt.utils.itertools.split(output, "\n"): if line.startswith(marker): - cleaned.append(line[len(marker):].strip()) + cleaned.append(line[len(marker) :].strip()) if cleaned: log.debug( - '%s pruned the following stale refs: %s', - self.role, ', '.join(cleaned) + "%s pruned the following stale refs: %s", + self.role, + ", ".join(cleaned), ) return cleaned - def clear_lock(self, lock_type='update'): - ''' + def clear_lock(self, lock_type="update"): + """ Clear update.lk - ''' + """ lock_file = self._get_lock_file(lock_type=lock_type) def _add_error(errlist, exc): - msg = ('Unable to remove update lock for {0} ({1}): {2} ' - .format(self.url, lock_file, exc)) + msg = "Unable to remove update lock for {0} ({1}): {2} ".format( + self.url, lock_file, exc + ) log.debug(msg) errlist.append(msg) @@ -641,24 +678,22 @@ class GitProvider(object): else: _add_error(failed, exc) else: - msg = 'Removed {0} lock for {1} remote \'{2}\''.format( - lock_type, - self.role, - self.id + msg = "Removed {0} lock for {1} remote '{2}'".format( + lock_type, self.role, self.id ) log.debug(msg) success.append(msg) return success, failed def enforce_git_config(self): - ''' + """ For the config options which need to be maintained in the git config, ensure that the git config file is configured as desired. - ''' - git_config = os.path.join(self.gitdir, 'config') + """ + git_config = os.path.join(self.gitdir, "config") conf = salt.utils.configparser.GitConfigParser() if not conf.read(git_config): - log.error('Failed to read from git config file %s', git_config) + log.error("Failed to read from git config file %s", git_config) else: # We are currently enforcing the following git config items: # 1. Fetch URL @@ -669,7 +704,7 @@ class GitProvider(object): # 1. URL try: - url = conf.get(remote_section, 'url') + url = conf.get(remote_section, "url") except salt.utils.configparser.NoSectionError: # First time we've init'ed this repo, we need to add the # section for the remote to the git config @@ -677,21 +712,25 @@ class GitProvider(object): conf_changed = True url = None log.debug( - 'Current fetch URL for %s remote \'%s\': %s (desired: %s)', - self.role, self.id, url, self.url + "Current fetch URL for %s remote '%s': %s (desired: %s)", + self.role, + self.id, + url, + self.url, ) if url != self.url: - conf.set(remote_section, 'url', self.url) + conf.set(remote_section, "url", self.url) log.debug( - 'Fetch URL for %s remote \'%s\' set to %s', - self.role, self.id, self.url + "Fetch URL for %s remote '%s' set to %s", + self.role, + self.id, + self.url, ) conf_changed = True # 2. refspecs try: - refspecs = sorted( - conf.get(remote_section, 'fetch', as_list=True)) + refspecs = sorted(conf.get(remote_section, "fetch", as_list=True)) except salt.utils.configparser.NoOptionError: # No 'fetch' option present in the remote section. Should never # happen, but if it does for some reason, don't let it cause a @@ -699,169 +738,184 @@ class GitProvider(object): refspecs = [] desired_refspecs = sorted(self.refspecs) log.debug( - 'Current refspecs for %s remote \'%s\': %s (desired: %s)', - self.role, self.id, refspecs, desired_refspecs + "Current refspecs for %s remote '%s': %s (desired: %s)", + self.role, + self.id, + refspecs, + desired_refspecs, ) if refspecs != desired_refspecs: - conf.set_multivar(remote_section, 'fetch', self.refspecs) + conf.set_multivar(remote_section, "fetch", self.refspecs) log.debug( - 'Refspecs for %s remote \'%s\' set to %s', - self.role, self.id, desired_refspecs + "Refspecs for %s remote '%s' set to %s", + self.role, + self.id, + desired_refspecs, ) conf_changed = True # 3. http.sslVerify try: - ssl_verify = conf.get('http', 'sslVerify') + ssl_verify = conf.get("http", "sslVerify") except salt.utils.configparser.NoSectionError: - conf.add_section('http') + conf.add_section("http") ssl_verify = None except salt.utils.configparser.NoOptionError: ssl_verify = None desired_ssl_verify = six.text_type(self.ssl_verify).lower() log.debug( - 'Current http.sslVerify for %s remote \'%s\': %s (desired: %s)', - self.role, self.id, ssl_verify, desired_ssl_verify + "Current http.sslVerify for %s remote '%s': %s (desired: %s)", + self.role, + self.id, + ssl_verify, + desired_ssl_verify, ) if ssl_verify != desired_ssl_verify: - conf.set('http', 'sslVerify', desired_ssl_verify) + conf.set("http", "sslVerify", desired_ssl_verify) log.debug( - 'http.sslVerify for %s remote \'%s\' set to %s', - self.role, self.id, desired_ssl_verify + "http.sslVerify for %s remote '%s' set to %s", + self.role, + self.id, + desired_ssl_verify, ) conf_changed = True # Write changes, if necessary if conf_changed: - with salt.utils.files.fopen(git_config, 'w') as fp_: + with salt.utils.files.fopen(git_config, "w") as fp_: conf.write(fp_) log.debug( - 'Config updates for %s remote \'%s\' written to %s', - self.role, self.id, git_config + "Config updates for %s remote '%s' written to %s", + self.role, + self.id, + git_config, ) def fetch(self): - ''' + """ Fetch the repo. If the local copy was updated, return True. If the local copy was already up-to-date, return False. This function requires that a _fetch() function be implemented in a sub-class. - ''' + """ try: - with self.gen_lock(lock_type='update'): - log.debug('Fetching %s remote \'%s\'', self.role, self.id) + with self.gen_lock(lock_type="update"): + log.debug("Fetching %s remote '%s'", self.role, self.id) # Run provider-specific fetch code return self._fetch() except GitLockError as exc: if exc.errno == errno.EEXIST: log.warning( - 'Update lock file is present for %s remote \'%s\', ' - 'skipping. If this warning persists, it is possible that ' - 'the update process was interrupted, but the lock could ' - 'also have been manually set. Removing %s or running ' - '\'salt-run cache.clear_git_lock %s type=update\' will ' - 'allow updates to continue for this remote.', + "Update lock file is present for %s remote '%s', " + "skipping. If this warning persists, it is possible that " + "the update process was interrupted, but the lock could " + "also have been manually set. Removing %s or running " + "'salt-run cache.clear_git_lock %s type=update' will " + "allow updates to continue for this remote.", self.role, self.id, - self._get_lock_file(lock_type='update'), + self._get_lock_file(lock_type="update"), self.role, ) return False - def _lock(self, lock_type='update', failhard=False): - ''' + def _lock(self, lock_type="update", failhard=False): + """ Place a lock file if (and only if) it does not already exist. - ''' + """ try: - fh_ = os.open(self._get_lock_file(lock_type), - os.O_CREAT | os.O_EXCL | os.O_WRONLY) - with os.fdopen(fh_, 'wb'): + fh_ = os.open( + self._get_lock_file(lock_type), os.O_CREAT | os.O_EXCL | os.O_WRONLY + ) + with os.fdopen(fh_, "wb"): # Write the lock file and close the filehandle - os.write(fh_, salt.utils.stringutils.to_bytes(six.text_type(os.getpid()))) + os.write( + fh_, salt.utils.stringutils.to_bytes(six.text_type(os.getpid())) + ) except (OSError, IOError) as exc: if exc.errno == errno.EEXIST: - with salt.utils.files.fopen(self._get_lock_file(lock_type), 'r') as fd_: + with salt.utils.files.fopen(self._get_lock_file(lock_type), "r") as fd_: try: - pid = int(salt.utils.stringutils.to_unicode(fd_.readline()).rstrip()) + pid = int( + salt.utils.stringutils.to_unicode(fd_.readline()).rstrip() + ) except ValueError: # Lock file is empty, set pid to 0 so it evaluates as # False. pid = 0 - global_lock_key = self.role + '_global_lock' + global_lock_key = self.role + "_global_lock" lock_file = self._get_lock_file(lock_type=lock_type) if self.opts[global_lock_key]: msg = ( - '{0} is enabled and {1} lockfile {2} is present for ' - '{3} remote \'{4}\'.'.format( - global_lock_key, - lock_type, - lock_file, - self.role, - self.id, + "{0} is enabled and {1} lockfile {2} is present for " + "{3} remote '{4}'.".format( + global_lock_key, lock_type, lock_file, self.role, self.id, ) ) if pid: - msg += ' Process {0} obtained the lock'.format(pid) + msg += " Process {0} obtained the lock".format(pid) if not pid_exists(pid): - msg += (' but this process is not running. The ' - 'update may have been interrupted. If ' - 'using multi-master with shared gitfs ' - 'cache, the lock may have been obtained ' - 'by another master.') + msg += ( + " but this process is not running. The " + "update may have been interrupted. If " + "using multi-master with shared gitfs " + "cache, the lock may have been obtained " + "by another master." + ) log.warning(msg) if failhard: six.reraise(*sys.exc_info()) return elif pid and pid_exists(pid): - log.warning('Process %d has a %s %s lock (%s)', - pid, self.role, lock_type, lock_file) + log.warning( + "Process %d has a %s %s lock (%s)", + pid, + self.role, + lock_type, + lock_file, + ) if failhard: raise return else: if pid: log.warning( - 'Process %d has a %s %s lock (%s), but this ' - 'process is not running. Cleaning up lock file.', - pid, self.role, lock_type, lock_file + "Process %d has a %s %s lock (%s), but this " + "process is not running. Cleaning up lock file.", + pid, + self.role, + lock_type, + lock_file, ) success, fail = self.clear_lock() if success: - return self._lock(lock_type='update', - failhard=failhard) + return self._lock(lock_type="update", failhard=failhard) elif failhard: raise return else: - msg = 'Unable to set {0} lock for {1} ({2}): {3} '.format( - lock_type, - self.id, - self._get_lock_file(lock_type), - exc + msg = "Unable to set {0} lock for {1} ({2}): {3} ".format( + lock_type, self.id, self._get_lock_file(lock_type), exc ) log.error(msg, exc_info=True) raise GitLockError(exc.errno, msg) - msg = 'Set {0} lock for {1} remote \'{2}\''.format( - lock_type, - self.role, - self.id - ) + msg = "Set {0} lock for {1} remote '{2}'".format(lock_type, self.role, self.id) log.debug(msg) return msg def lock(self): - ''' + """ Place an lock file and report on the success/failure. This is an interface to be used by the fileserver runner, so it is hard-coded to perform an update lock. We aren't using the gen_lock() contextmanager here because the lock is meant to stay and not be automatically removed. - ''' + """ success = [] failed = [] try: - result = self._lock(lock_type='update') + result = self._lock(lock_type="update") except GitLockError as exc: failed.append(exc.strerror) else: @@ -870,14 +924,13 @@ class GitProvider(object): return success, failed @contextlib.contextmanager - def gen_lock(self, lock_type='update', timeout=0, poll_interval=0.5): - ''' + def gen_lock(self, lock_type="update", timeout=0, poll_interval=0.5): + """ Set and automatically clear a lock - ''' + """ if not isinstance(lock_type, six.string_types): raise GitLockError( - errno.EINVAL, - 'Invalid lock_type \'{0}\''.format(lock_type) + errno.EINVAL, "Invalid lock_type '{0}'".format(lock_type) ) # Make sure that we have a positive integer timeout, otherwise just set @@ -890,8 +943,10 @@ class GitProvider(object): if timeout < 0: timeout = 0 - if not isinstance(poll_interval, (six.integer_types, float)) \ - or poll_interval < 0: + if ( + not isinstance(poll_interval, (six.integer_types, float)) + or poll_interval < 0 + ): poll_interval = 0.5 if poll_interval > timeout: @@ -913,9 +968,12 @@ class GitProvider(object): raise GitLockError(exc.errno, exc.strerror) else: log.debug( - 'A %s lock is already present for %s remote ' - '\'%s\', sleeping %f second(s)', - lock_type, self.role, self.id, poll_interval + "A %s lock is already present for %s remote " + "'%s', sleeping %f second(s)", + lock_type, + self.role, + self.id, + poll_interval, ) time.sleep(poll_interval) continue @@ -924,81 +982,75 @@ class GitProvider(object): self.clear_lock(lock_type=lock_type) def init_remote(self): - ''' + """ This function must be overridden in a sub-class - ''' + """ raise NotImplementedError() def checkout(self): - ''' + """ This function must be overridden in a sub-class - ''' + """ raise NotImplementedError() def dir_list(self, tgt_env): - ''' + """ This function must be overridden in a sub-class - ''' + """ raise NotImplementedError() def env_is_exposed(self, tgt_env): - ''' + """ Check if an environment is exposed by comparing it against a whitelist and blacklist. - ''' + """ return salt.utils.stringutils.check_whitelist_blacklist( - tgt_env, - whitelist=self.saltenv_whitelist, - blacklist=self.saltenv_blacklist, + tgt_env, whitelist=self.saltenv_whitelist, blacklist=self.saltenv_blacklist, ) def _fetch(self): - ''' + """ Provider-specific code for fetching, must be implemented in a sub-class. - ''' + """ raise NotImplementedError() def envs(self): - ''' + """ This function must be overridden in a sub-class - ''' + """ raise NotImplementedError() def file_list(self, tgt_env): - ''' + """ This function must be overridden in a sub-class - ''' + """ raise NotImplementedError() def find_file(self, path, tgt_env): - ''' + """ This function must be overridden in a sub-class - ''' + """ raise NotImplementedError() def get_checkout_target(self): - ''' + """ Resolve dynamically-set branch - ''' - if self.role == 'git_pillar' and self.branch == '__env__': + """ + if self.role == "git_pillar" and self.branch == "__env__": try: return self.all_saltenvs except AttributeError: # all_saltenvs not configured for this remote pass - target = self.opts.get('pillarenv') \ - or self.opts.get('saltenv') \ - or 'base' - return self.base \ - if target == 'base' \ - else six.text_type(target) + target = self.opts.get("pillarenv") or self.opts.get("saltenv") or "base" + return self.base if target == "base" else six.text_type(target) return self.branch def get_tree(self, tgt_env): - ''' + """ Return a tree object for the specified environment - ''' + """ if not self.env_is_exposed(tgt_env): return None @@ -1008,54 +1060,71 @@ class GitProvider(object): for ref_type in self.ref_types: try: - func_name = 'get_tree_from_{0}'.format(ref_type) + func_name = "get_tree_from_{0}".format(ref_type) func = getattr(self, func_name) except AttributeError: log.error( - '%s class is missing function \'%s\'', - self.__class__.__name__, func_name + "%s class is missing function '%s'", + self.__class__.__name__, + func_name, ) else: candidate = func(tgt_ref) if candidate is not None: return candidate + if self.fallback: + for ref_type in self.ref_types: + try: + func_name = "get_tree_from_{0}".format(ref_type) + func = getattr(self, func_name) + except AttributeError: + log.error( + "%s class is missing function '%s'", + self.__class__.__name__, + func_name, + ) + else: + candidate = func(self.fallback) + if candidate is not None: + return candidate + # No matches found return None def get_url(self): - ''' + """ Examine self.id and assign self.url (and self.branch, for git_pillar) - ''' - if self.role in ('git_pillar', 'winrepo'): + """ + if self.role in ("git_pillar", "winrepo"): # With winrepo and git_pillar, the remote is specified in the # format '<branch> <url>', so that we can get a unique identifier # to hash for each remote. try: self.branch, self.url = self.id.split(None, 1) except ValueError: - self.branch = self.conf['branch'] + self.branch = self.conf["branch"] self.url = self.id else: self.url = self.id @property def linkdir_walk(self): - ''' + """ Return the expected result of an os.walk on the linkdir, based on the mountpoint value. - ''' + """ try: # Use cached linkdir_walk if we've already run this return self._linkdir_walk except AttributeError: self._linkdir_walk = [] try: - parts = self._mountpoint.split('/') + parts = self._mountpoint.split("/") except AttributeError: log.error( - '%s class is missing a \'_mountpoint\' attribute', - self.__class__.__name__ + "%s class is missing a '_mountpoint' attribute", + self.__class__.__name__, ) else: for idx, item in enumerate(parts[:-1]): @@ -1063,11 +1132,13 @@ class GitProvider(object): dirs = [parts[idx + 1]] except IndexError: dirs = [] - self._linkdir_walk.append(( - salt.utils.path.join(self.linkdir, *parts[:idx + 1]), - dirs, - [] - )) + self._linkdir_walk.append( + ( + salt.utils.path.join(self.linkdir, *parts[: idx + 1]), + dirs, + [], + ) + ) try: # The linkdir itself goes at the beginning self._linkdir_walk.insert(0, (self.linkdir, [parts[0]], [])) @@ -1076,46 +1147,60 @@ class GitProvider(object): return self._linkdir_walk def setup_callbacks(self): - ''' + """ Only needed in pygit2, included in the base class for simplicty of use - ''' + """ def verify_auth(self): - ''' + """ Override this function in a sub-class to implement auth checking. - ''' + """ self.credentials = None return True def write_file(self, blob, dest): - ''' + """ This function must be overridden in a sub-class - ''' + """ raise NotImplementedError() class GitPython(GitProvider): - ''' + """ Interface to GitPython - ''' - def __init__(self, opts, remote, per_remote_defaults, per_remote_only, - override_params, cache_root, role='gitfs'): - self.provider = 'gitpython' + """ + + def __init__( + self, + opts, + remote, + per_remote_defaults, + per_remote_only, + override_params, + cache_root, + role="gitfs", + ): + self.provider = "gitpython" super(GitPython, self).__init__( - opts, remote, per_remote_defaults, per_remote_only, - override_params, cache_root, role + opts, + remote, + per_remote_defaults, + per_remote_only, + override_params, + cache_root, + role, ) def checkout(self): - ''' + """ Checkout the configured branch/tag. We catch an "Exception" class here instead of a specific exception class because the exceptions raised by GitPython when running these functions vary in different versions of GitPython. - ''' + """ tgt_ref = self.get_checkout_target() try: - head_sha = self.repo.rev_parse('HEAD').hexsha + head_sha = self.repo.rev_parse("HEAD").hexsha except Exception: # pylint: disable=broad-except # Should only happen the first time we are checking out, since # we fetch first before ever checking anything out. @@ -1123,11 +1208,18 @@ class GitPython(GitProvider): # 'origin/' + tgt_ref ==> matches a branch head # 'tags/' + tgt_ref + '@{commit}' ==> matches tag's commit - for rev_parse_target, checkout_ref in ( - ('origin/' + tgt_ref, 'origin/' + tgt_ref), - ('tags/' + tgt_ref, 'tags/' + tgt_ref)): + checkout_refs = [ + ("origin/" + tgt_ref, False), + ("tags/" + tgt_ref, False), + ] + if self.fallback: + checkout_refs += [ + ("origin/" + self.fallback, True), + ("tags/" + self.fallback, True), + ] + for checkout_ref, fallback in checkout_refs: try: - target_sha = self.repo.rev_parse(rev_parse_target).hexsha + target_sha = self.repo.rev_parse(checkout_ref).hexsha except Exception: # pylint: disable=broad-except # ref does not exist continue @@ -1137,13 +1229,14 @@ class GitPython(GitProvider): return self.check_root() try: - with self.gen_lock(lock_type='checkout'): + with self.gen_lock(lock_type="checkout"): self.repo.git.checkout(checkout_ref) log.debug( - '%s remote \'%s\' has been checked out to %s', + "%s remote '%s' has been checked out to %s%s", self.role, self.id, - checkout_ref + checkout_ref, + " as fallback" if fallback else "", ) except GitLockError as exc: if exc.errno == errno.EEXIST: @@ -1152,33 +1245,36 @@ class GitPython(GitProvider): # function. raise GitLockError( exc.errno, - 'Checkout lock exists for {0} remote \'{1}\'' - .format(self.role, self.id) + "Checkout lock exists for {0} remote '{1}'".format( + self.role, self.id + ), ) else: log.error( - 'Error %d encountered obtaining checkout lock ' - 'for %s remote \'%s\'', + "Error %d encountered obtaining checkout lock " + "for %s remote '%s'", exc.errno, self.role, - self.id + self.id, ) return None except Exception: # pylint: disable=broad-except continue return self.check_root() log.error( - 'Failed to checkout %s from %s remote \'%s\': remote ref does ' - 'not exist', tgt_ref, self.role, self.id + "Failed to checkout %s from %s remote '%s': remote ref does " "not exist", + tgt_ref, + self.role, + self.id, ) return None def init_remote(self): - ''' + """ Initialize/attach to a remote using GitPython. Return a boolean which will let the calling function know whether or not a new repo was initialized by this function. - ''' + """ new = False if not os.listdir(self.cachedir): # Repo cachedir is empty, initialize a new repo there @@ -1192,15 +1288,15 @@ class GitPython(GitProvider): log.error(_INVALID_REPO, self.cachedir, self.url, self.role) return new - self.gitdir = salt.utils.path.join(self.repo.working_dir, '.git') + self.gitdir = salt.utils.path.join(self.repo.working_dir, ".git") self.enforce_git_config() return new def dir_list(self, tgt_env): - ''' + """ Get list of directories for the target environment using GitPython - ''' + """ ret = set() tree = self.get_tree(tgt_env) if not tree: @@ -1214,7 +1310,8 @@ class GitPython(GitProvider): else: relpath = lambda path: path add_mountpoint = lambda path: salt.utils.path.join( - self.mountpoint(tgt_env), path, use_posixpath=True) + self.mountpoint(tgt_env), path, use_posixpath=True + ) for blob in tree.traverse(): if isinstance(blob, git.Tree): ret.add(add_mountpoint(relpath(blob.path))) @@ -1223,18 +1320,18 @@ class GitPython(GitProvider): return ret def envs(self): - ''' + """ Check the refs and return a list of the ones which can be used as salt environments. - ''' + """ ref_paths = [x.path for x in self.repo.refs] return self._get_envs_from_ref_paths(ref_paths) def _fetch(self): - ''' + """ Fetch the repo. If the local copy was updated, return True. If the local copy was already up-to-date, return False. - ''' + """ origin = self.repo.remotes[0] try: fetch_results = origin.fetch() @@ -1245,23 +1342,21 @@ class GitPython(GitProvider): for fetchinfo in fetch_results: if fetchinfo.old_commit is not None: log.debug( - '%s has updated \'%s\' for remote \'%s\' ' - 'from %s to %s', + "%s has updated '%s' for remote '%s' " "from %s to %s", self.role, fetchinfo.name, self.id, fetchinfo.old_commit.hexsha[:7], - fetchinfo.commit.hexsha[:7] + fetchinfo.commit.hexsha[:7], ) new_objs = True - elif fetchinfo.flags in (fetchinfo.NEW_TAG, - fetchinfo.NEW_HEAD): + elif fetchinfo.flags in (fetchinfo.NEW_TAG, fetchinfo.NEW_HEAD): log.debug( - '%s has fetched new %s \'%s\' for remote \'%s\'', + "%s has fetched new %s '%s' for remote '%s'", self.role, - 'tag' if fetchinfo.flags == fetchinfo.NEW_TAG else 'head', + "tag" if fetchinfo.flags == fetchinfo.NEW_TAG else "head", fetchinfo.name, - self.id + self.id, ) new_objs = True @@ -1269,9 +1364,9 @@ class GitPython(GitProvider): return True if (new_objs or cleaned) else None def file_list(self, tgt_env): - ''' + """ Get file list for the target environment using GitPython - ''' + """ files = set() symlinks = {} tree = self.get_tree(tgt_env) @@ -1287,7 +1382,8 @@ class GitPython(GitProvider): else: relpath = lambda path: path add_mountpoint = lambda path: salt.utils.path.join( - self.mountpoint(tgt_env), path, use_posixpath=True) + self.mountpoint(tgt_env), path, use_posixpath=True + ) for file_blob in tree.traverse(): if not isinstance(file_blob, git.Blob): continue @@ -1303,9 +1399,9 @@ class GitPython(GitProvider): return files, symlinks def find_file(self, path, tgt_env): - ''' + """ Find the specified file in the specified environment - ''' + """ tree = self.get_tree(tgt_env) if not tree: # Branch/tag/SHA not found in repo @@ -1330,7 +1426,8 @@ class GitPython(GitProvider): link_tgt = salt.utils.stringutils.to_str(stream.read()) stream.close() path = salt.utils.path.join( - os.path.dirname(path), link_tgt, use_posixpath=True) + os.path.dirname(path), link_tgt, use_posixpath=True + ) else: blob = file_blob if isinstance(blob, git.Tree): @@ -1346,85 +1443,95 @@ class GitPython(GitProvider): return None, None, None def get_tree_from_branch(self, ref): - ''' + """ Return a git.Tree object matching a head ref fetched into refs/remotes/origin/ - ''' + """ try: return git.RemoteReference( - self.repo, - 'refs/remotes/origin/{0}'.format(ref)).commit.tree + self.repo, "refs/remotes/origin/{0}".format(ref) + ).commit.tree except ValueError: return None def get_tree_from_tag(self, ref): - ''' + """ Return a git.Tree object matching a tag ref fetched into refs/tags/ - ''' + """ try: - return git.TagReference( - self.repo, - 'refs/tags/{0}'.format(ref)).commit.tree + return git.TagReference(self.repo, "refs/tags/{0}".format(ref)).commit.tree except ValueError: return None def get_tree_from_sha(self, ref): - ''' + """ Return a git.Tree object matching a SHA - ''' + """ try: return self.repo.rev_parse(ref).tree except (gitdb.exc.ODBError, AttributeError): return None def write_file(self, blob, dest): - ''' + """ Using the blob object, write the file to the destination path - ''' - with salt.utils.files.fopen(dest, 'wb+') as fp_: + """ + with salt.utils.files.fopen(dest, "wb+") as fp_: blob.stream_data(fp_) class Pygit2(GitProvider): - ''' + """ Interface to Pygit2 - ''' - def __init__(self, opts, remote, per_remote_defaults, per_remote_only, - override_params, cache_root, role='gitfs'): - self.provider = 'pygit2' + """ + + def __init__( + self, + opts, + remote, + per_remote_defaults, + per_remote_only, + override_params, + cache_root, + role="gitfs", + ): + self.provider = "pygit2" super(Pygit2, self).__init__( - opts, remote, per_remote_defaults, per_remote_only, - override_params, cache_root, role + opts, + remote, + per_remote_defaults, + per_remote_only, + override_params, + cache_root, + role, ) def peel(self, obj): - ''' + """ Compatibility function for pygit2.Reference objects. Older versions of pygit2 use .get_object() to return the object to which the reference points, while newer versions use .peel(). In pygit2 0.27.4, .get_object() was removed. This function will try .peel() first and fall back to .get_object(). - ''' + """ try: return obj.peel() except AttributeError: return obj.get_object() def checkout(self): - ''' + """ Checkout the configured branch/tag - ''' + """ tgt_ref = self.get_checkout_target() - local_ref = 'refs/heads/' + tgt_ref - remote_ref = 'refs/remotes/origin/' + tgt_ref - tag_ref = 'refs/tags/' + tgt_ref + local_ref = "refs/heads/" + tgt_ref + remote_ref = "refs/remotes/origin/" + tgt_ref + tag_ref = "refs/tags/" + tgt_ref try: - local_head = self.repo.lookup_reference('HEAD') + local_head = self.repo.lookup_reference("HEAD") except KeyError: - log.warning( - 'HEAD not present in %s remote \'%s\'', self.role, self.id - ) + log.warning("HEAD not present in %s remote '%s'", self.role, self.id) return None try: @@ -1433,8 +1540,7 @@ class Pygit2(GitProvider): # Shouldn't happen, but just in case a future pygit2 API change # breaks things, avoid a traceback and log an error. log.error( - 'Unable to get SHA of HEAD for %s remote \'%s\'', - self.role, self.id + "Unable to get SHA of HEAD for %s remote '%s'", self.role, self.id ) return None except KeyError: @@ -1443,11 +1549,11 @@ class Pygit2(GitProvider): refs = self.repo.listall_references() def _perform_checkout(checkout_ref, branch=True): - ''' + """ DRY function for checking out either a branch or a tag - ''' + """ try: - with self.gen_lock(lock_type='checkout'): + with self.gen_lock(lock_type="checkout"): # Checkout the local branch corresponding to the # remote ref. self.repo.checkout(checkout_ref) @@ -1461,20 +1567,26 @@ class Pygit2(GitProvider): # function. raise GitLockError( exc.errno, - 'Checkout lock exists for {0} remote \'{1}\'' - .format(self.role, self.id) + "Checkout lock exists for {0} remote '{1}'".format( + self.role, self.id + ), ) else: log.error( - 'Error %d encountered obtaining checkout lock ' - 'for %s remote \'%s\'', + "Error %d encountered obtaining checkout lock " + "for %s remote '%s'", exc.errno, self.role, - self.id + self.id, ) return False try: + if remote_ref not in refs and tag_ref not in refs and self.fallback: + tgt_ref = self.fallback + local_ref = "refs/heads/" + tgt_ref + remote_ref = "refs/remotes/origin/" + tgt_ref + tag_ref = "refs/tags/" + tgt_ref if remote_ref in refs: # Get commit id for the remote ref oid = self.peel(self.repo.lookup_reference(remote_ref)).id @@ -1484,13 +1596,14 @@ class Pygit2(GitProvider): self.repo.create_reference(local_ref, oid) try: - target_sha = \ - self.peel(self.repo.lookup_reference(remote_ref)).hex + target_sha = self.peel(self.repo.lookup_reference(remote_ref)).hex except KeyError: log.error( - 'pygit2 was unable to get SHA for %s in %s remote ' - '\'%s\'', local_ref, self.role, self.id, - exc_info=True + "pygit2 was unable to get SHA for %s in %s remote " "'%s'", + local_ref, + self.role, + self.id, + exc_info=True, ) return None @@ -1515,18 +1628,23 @@ class Pygit2(GitProvider): # Therefore, there is no need to add a local reference. If # head_ref == local_ref, then the local reference for HEAD # in refs/heads/ already exists and again, no need to add. - if isinstance(head_ref, six.string_types) \ - and head_ref not in refs and head_ref != local_ref: - branch_name = head_ref.partition('refs/heads/')[-1] + if ( + isinstance(head_ref, six.string_types) + and head_ref not in refs + and head_ref != local_ref + ): + branch_name = head_ref.partition("refs/heads/")[-1] if not branch_name: # Shouldn't happen, but log an error if it does log.error( - 'pygit2 was unable to resolve branch name from ' - 'HEAD ref \'%s\' in %s remote \'%s\'', - head_ref, self.role, self.id + "pygit2 was unable to resolve branch name from " + "HEAD ref '%s' in %s remote '%s'", + head_ref, + self.role, + self.id, ) return None - remote_head = 'refs/remotes/origin/' + branch_name + remote_head = "refs/remotes/origin/" + branch_name if remote_head not in refs: # No remote ref for HEAD exists. This can happen in # the first-time git_pillar checkout when when the @@ -1537,8 +1655,7 @@ class Pygit2(GitProvider): # remote_ref. remote_head = remote_ref self.repo.create_reference( - head_ref, - self.repo.lookup_reference(remote_head).target + head_ref, self.repo.lookup_reference(remote_head).target ) if not _perform_checkout(local_ref, branch=True): @@ -1549,10 +1666,11 @@ class Pygit2(GitProvider): elif tag_ref in refs: tag_obj = self.repo.revparse_single(tag_ref) - if not isinstance(tag_obj, pygit2.Commit): + if not isinstance(tag_obj, (pygit2.Commit, pygit2.Tag)): log.error( - '%s does not correspond to pygit2.Commit object', - tag_ref + "%s does not correspond to pygit2 Commit or Tag object. It is of type %s", + tag_ref, + type(tag_obj), ) else: try: @@ -1565,13 +1683,15 @@ class Pygit2(GitProvider): # Shouldn't happen, but could if a future pygit2 # API change breaks things. log.error( - 'Unable to resolve %s from %s remote \'%s\' ' - 'to either an annotated or non-annotated tag', - tag_ref, self.role, self.id, - exc_info=True + "Unable to resolve %s from %s remote '%s' " + "to either an annotated or non-annotated tag", + tag_ref, + self.role, + self.id, + exc_info=True, ) return None - log.debug('SHA of tag %s: %s', tgt_ref, tag_sha) + log.debug("SHA of tag %s: %s", tgt_ref, tag_sha) if head_sha != tag_sha: if not _perform_checkout(tag_ref, branch=False): @@ -1583,21 +1703,26 @@ class Pygit2(GitProvider): raise except Exception as exc: # pylint: disable=broad-except log.error( - 'Failed to checkout %s from %s remote \'%s\': %s', - tgt_ref, self.role, self.id, exc, - exc_info=True + "Failed to checkout %s from %s remote '%s': %s", + tgt_ref, + self.role, + self.id, + exc, + exc_info=True, ) return None log.error( - 'Failed to checkout %s from %s remote \'%s\': remote ref ' - 'does not exist', tgt_ref, self.role, self.id + "Failed to checkout %s from %s remote '%s': remote ref " "does not exist", + tgt_ref, + self.role, + self.id, ) return None def clean_stale_refs(self, local_refs=None): # pylint: disable=arguments-differ - ''' + """ Clean stale local refs so they don't appear as fileserver environments - ''' + """ try: if pygit2.GIT_FETCH_PRUNE: # Don't need to clean anything, pygit2 can do it by itself @@ -1607,23 +1732,24 @@ class Pygit2(GitProvider): pass if self.credentials is not None: log.debug( - 'The installed version of pygit2 (%s) does not support ' - 'detecting stale refs for authenticated remotes, saltenvs ' - 'will not reflect branches/tags removed from remote \'%s\'', - PYGIT2_VERSION, self.id + "The installed version of pygit2 (%s) does not support " + "detecting stale refs for authenticated remotes, saltenvs " + "will not reflect branches/tags removed from remote '%s'", + PYGIT2_VERSION, + self.id, ) return [] return super(Pygit2, self).clean_stale_refs() def init_remote(self): - ''' + """ Initialize/attach to a remote using pygit2. Return a boolean which will let the calling function know whether or not a new repo was initialized by this function. - ''' + """ # https://github.com/libgit2/pygit2/issues/339 # https://github.com/libgit2/libgit2/issues/2122 - home = os.path.expanduser('~') + home = os.path.expanduser("~") pygit2.settings.search_path[pygit2.GIT_CONFIG_LEVEL_GLOBAL] = home new = False if not os.listdir(self.cachedir): @@ -1638,23 +1764,24 @@ class Pygit2(GitProvider): log.error(_INVALID_REPO, self.cachedir, self.url, self.role) return new - self.gitdir = salt.utils.path.join(self.repo.workdir, '.git') + self.gitdir = salt.utils.path.join(self.repo.workdir, ".git") self.enforce_git_config() - git_config = os.path.join(self.gitdir, 'config') - if os.path.exists(git_config) and PYGIT2_VERSION >= _LooseVersion('0.28.0'): + git_config = os.path.join(self.gitdir, "config") + if os.path.exists(git_config) and PYGIT2_VERSION >= _LooseVersion("0.28.0"): self.repo.config.add_file(git_config) return new def dir_list(self, tgt_env): - ''' + """ Get a list of directories for the target environment using pygit2 - ''' + """ + def _traverse(tree, blobs, prefix): - ''' + """ Traverse through a pygit2 Tree object recursively, accumulating all the empty directories within it in the "blobs" list - ''' + """ for entry in iter(tree): if entry.oid not in self.repo: # Entry is a submodule, skip it @@ -1667,8 +1794,9 @@ class Pygit2(GitProvider): ) if blob: _traverse( - blob, blobs, salt.utils.path.join( - prefix, entry.name, use_posixpath=True) + blob, + blobs, + salt.utils.path.join(prefix, entry.name, use_posixpath=True), ) ret = set() @@ -1690,7 +1818,8 @@ class Pygit2(GitProvider): if tree: _traverse(tree, blobs, self.root(tgt_env)) add_mountpoint = lambda path: salt.utils.path.join( - self.mountpoint(tgt_env), path, use_posixpath=True) + self.mountpoint(tgt_env), path, use_posixpath=True + ) for blob in blobs: ret.add(add_mountpoint(relpath(blob))) if self.mountpoint(tgt_env): @@ -1698,29 +1827,29 @@ class Pygit2(GitProvider): return ret def envs(self): - ''' + """ Check the refs and return a list of the ones which can be used as salt environments. - ''' + """ ref_paths = self.repo.listall_references() return self._get_envs_from_ref_paths(ref_paths) def _fetch(self): - ''' + """ Fetch the repo. If the local copy was updated, return True. If the local copy was already up-to-date, return False. - ''' + """ origin = self.repo.remotes[0] refs_pre = self.repo.listall_references() fetch_kwargs = {} # pygit2 radically changed fetchiing in 0.23.2 if self.remotecallbacks is not None: - fetch_kwargs['callbacks'] = self.remotecallbacks + fetch_kwargs["callbacks"] = self.remotecallbacks else: if self.credentials is not None: origin.credentials = self.credentials try: - fetch_kwargs['prune'] = pygit2.GIT_FETCH_PRUNE + fetch_kwargs["prune"] = pygit2.GIT_FETCH_PRUNE except AttributeError: # pruning only available in pygit2 >= 0.26.2 pass @@ -1728,57 +1857,65 @@ class Pygit2(GitProvider): fetch_results = origin.fetch(**fetch_kwargs) except GitError as exc: # pylint: disable=broad-except exc_str = get_error_message(exc).lower() - if 'unsupported url protocol' in exc_str \ - and isinstance(self.credentials, pygit2.Keypair): + if "unsupported url protocol" in exc_str and isinstance( + self.credentials, pygit2.Keypair + ): log.error( - 'Unable to fetch SSH-based %s remote \'%s\'. ' - 'You may need to add ssh:// to the repo string or ' - 'libgit2 must be compiled with libssh2 to support ' - 'SSH authentication.', self.role, self.id, - exc_info=True + "Unable to fetch SSH-based %s remote '%s'. " + "You may need to add ssh:// to the repo string or " + "libgit2 must be compiled with libssh2 to support " + "SSH authentication.", + self.role, + self.id, + exc_info=True, ) - elif 'authentication required but no callback set' in exc_str: + elif "authentication required but no callback set" in exc_str: log.error( - '%s remote \'%s\' requires authentication, but no ' - 'authentication configured', self.role, self.id, - exc_info=True + "%s remote '%s' requires authentication, but no " + "authentication configured", + self.role, + self.id, + exc_info=True, ) else: log.error( - 'Error occurred fetching %s remote \'%s\': %s', - self.role, self.id, exc, - exc_info=True + "Error occurred fetching %s remote '%s': %s", + self.role, + self.id, + exc, + exc_info=True, ) return False try: # pygit2.Remote.fetch() returns a dict in pygit2 < 0.21.0 - received_objects = fetch_results['received_objects'] + received_objects = fetch_results["received_objects"] except (AttributeError, TypeError): # pygit2.Remote.fetch() returns a class instance in # pygit2 >= 0.21.0 received_objects = fetch_results.received_objects if received_objects != 0: log.debug( - '%s received %s objects for remote \'%s\'', - self.role, received_objects, self.id + "%s received %s objects for remote '%s'", + self.role, + received_objects, + self.id, ) else: - log.debug('%s remote \'%s\' is up-to-date', self.role, self.id) + log.debug("%s remote '%s' is up-to-date", self.role, self.id) refs_post = self.repo.listall_references() cleaned = self.clean_stale_refs(local_refs=refs_post) - return True \ - if (received_objects or refs_pre != refs_post or cleaned) \ - else None + return True if (received_objects or refs_pre != refs_post or cleaned) else None def file_list(self, tgt_env): - ''' + """ Get file list for the target environment using pygit2 - ''' + """ + def _traverse(tree, blobs, prefix): - ''' + """ Traverse through a pygit2 Tree object recursively, accumulating all the file paths and symlink info in the "blobs" dict - ''' + """ for entry in iter(tree): if entry.oid not in self.repo: # Entry is a submodule, skip it @@ -1786,15 +1923,17 @@ class Pygit2(GitProvider): obj = self.repo[entry.oid] if isinstance(obj, pygit2.Blob): repo_path = salt.utils.path.join( - prefix, entry.name, use_posixpath=True) - blobs.setdefault('files', []).append(repo_path) + prefix, entry.name, use_posixpath=True + ) + blobs.setdefault("files", []).append(repo_path) if stat.S_ISLNK(tree[entry.name].filemode): link_tgt = self.repo[tree[entry.name].oid].data - blobs.setdefault('symlinks', {})[repo_path] = link_tgt + blobs.setdefault("symlinks", {})[repo_path] = link_tgt elif isinstance(obj, pygit2.Tree): _traverse( - obj, blobs, salt.utils.path.join( - prefix, entry.name, use_posixpath=True) + obj, + blobs, + salt.utils.path.join(prefix, entry.name, use_posixpath=True), ) files = set() @@ -1820,17 +1959,18 @@ class Pygit2(GitProvider): if tree: _traverse(tree, blobs, self.root(tgt_env)) add_mountpoint = lambda path: salt.utils.path.join( - self.mountpoint(tgt_env), path, use_posixpath=True) - for repo_path in blobs.get('files', []): + self.mountpoint(tgt_env), path, use_posixpath=True + ) + for repo_path in blobs.get("files", []): files.add(add_mountpoint(relpath(repo_path))) - for repo_path, link_tgt in six.iteritems(blobs.get('symlinks', {})): + for repo_path, link_tgt in six.iteritems(blobs.get("symlinks", {})): symlinks[add_mountpoint(relpath(repo_path))] = link_tgt return files, symlinks def find_file(self, path, tgt_env): - ''' + """ Find the specified file in the specified environment - ''' + """ tree = self.get_tree(tgt_env) if not tree: # Branch/tag/SHA not found in repo @@ -1853,7 +1993,8 @@ class Pygit2(GitProvider): # in the blob data. link_tgt = self.repo[entry.oid].data path = salt.utils.path.join( - os.path.dirname(path), link_tgt, use_posixpath=True) + os.path.dirname(path), link_tgt, use_posixpath=True + ) else: blob = self.repo[entry.oid] if isinstance(blob, pygit2.Tree): @@ -1868,67 +2009,67 @@ class Pygit2(GitProvider): return None, None, None def get_tree_from_branch(self, ref): - ''' + """ Return a pygit2.Tree object matching a head ref fetched into refs/remotes/origin/ - ''' + """ try: - return self.peel(self.repo.lookup_reference( - 'refs/remotes/origin/{0}'.format(ref))).tree + return self.peel( + self.repo.lookup_reference("refs/remotes/origin/{0}".format(ref)) + ).tree except KeyError: return None def get_tree_from_tag(self, ref): - ''' + """ Return a pygit2.Tree object matching a tag ref fetched into refs/tags/ - ''' + """ try: - return self.peel(self.repo.lookup_reference( - 'refs/tags/{0}'.format(ref))).tree + return self.peel( + self.repo.lookup_reference("refs/tags/{0}".format(ref)) + ).tree except KeyError: return None def get_tree_from_sha(self, ref): - ''' + """ Return a pygit2.Tree object matching a SHA - ''' + """ try: return self.repo.revparse_single(ref).tree except (KeyError, TypeError, ValueError, AttributeError): return None def setup_callbacks(self): - ''' + """ Assign attributes for pygit2 callbacks - ''' - if PYGIT2_VERSION >= _LooseVersion('0.23.2'): - self.remotecallbacks = pygit2.RemoteCallbacks( - credentials=self.credentials) + """ + if PYGIT2_VERSION >= _LooseVersion("0.23.2"): + self.remotecallbacks = pygit2.RemoteCallbacks(credentials=self.credentials) if not self.ssl_verify: # Override the certificate_check function with a lambda that # just returns True, thus skipping the cert check. - self.remotecallbacks.certificate_check = \ - lambda *args, **kwargs: True + self.remotecallbacks.certificate_check = lambda *args, **kwargs: True else: self.remotecallbacks = None if not self.ssl_verify: warnings.warn( - 'pygit2 does not support disabling the SSL certificate ' - 'check in versions prior to 0.23.2 (installed: {0}). ' - 'Fetches for self-signed certificates will fail.'.format( + "pygit2 does not support disabling the SSL certificate " + "check in versions prior to 0.23.2 (installed: {0}). " + "Fetches for self-signed certificates will fail.".format( PYGIT2_VERSION ) ) def verify_auth(self): - ''' + """ Check the username and password/keypair info for validity. If valid, set a 'credentials' attribute consisting of the appropriate Pygit2 credentials object. Return False if a required auth param is not present. Return True if the required auth parameters are present (or auth is not configured), otherwise failhard if there is a problem with authenticaion. - ''' + """ self.credentials = None if os.path.isabs(self.url): @@ -1939,59 +2080,68 @@ class Pygit2(GitProvider): return True def _incomplete_auth(missing): - ''' + """ Helper function to log errors about missing auth parameters - ''' + """ log.critical( - 'Incomplete authentication information for %s remote ' - '\'%s\'. Missing parameters: %s', - self.role, self.id, ', '.join(missing) + "Incomplete authentication information for %s remote " + "'%s'. Missing parameters: %s", + self.role, + self.id, + ", ".join(missing), ) failhard(self.role) def _key_does_not_exist(key_type, path): - ''' + """ Helper function to log errors about missing key file - ''' + """ log.critical( - 'SSH %s (%s) for %s remote \'%s\' could not be found, path ' - 'may be incorrect. Note that it may be necessary to clear ' - 'git_pillar locks to proceed once this is resolved and the ' - 'master has been started back up. A warning will be logged ' - 'if this is the case, with instructions.', - key_type, path, self.role, self.id + "SSH %s (%s) for %s remote '%s' could not be found, path " + "may be incorrect. Note that it may be necessary to clear " + "git_pillar locks to proceed once this is resolved and the " + "master has been started back up. A warning will be logged " + "if this is the case, with instructions.", + key_type, + path, + self.role, + self.id, ) failhard(self.role) - transport, _, address = self.url.partition('://') + transport, _, address = self.url.partition("://") if not address: # Assume scp-like SSH syntax (user@domain.tld:relative/path.git) - transport = 'ssh' + transport = "ssh" address = self.url transport = transport.lower() - if transport in ('git', 'file'): + if transport in ("git", "file"): # These transports do not use auth return True - elif 'ssh' in transport: - required_params = ('pubkey', 'privkey') - user = address.split('@')[0] + elif "ssh" in transport: + required_params = ("pubkey", "privkey") + user = address.split("@")[0] if user == address: # No '@' sign == no user. This is a problem. log.critical( - 'Keypair specified for %s remote \'%s\', but remote URL ' - 'is missing a username', self.role, self.id + "Keypair specified for %s remote '%s', but remote URL " + "is missing a username", + self.role, + self.id, ) failhard(self.role) self.user = user if all(bool(getattr(self, x, None)) for x in required_params): - keypair_params = [getattr(self, x, None) for x in - ('user', 'pubkey', 'privkey', 'passphrase')] + keypair_params = [ + getattr(self, x, None) + for x in ("user", "pubkey", "privkey", "passphrase") + ] # Check pubkey and privkey to make sure file exists - for idx, key_type in ((1, 'pubkey'), (2, 'privkey')): + for idx, key_type in ((1, "pubkey"), (2, "privkey")): key_path = keypair_params[idx] if key_path is not None: try: @@ -2002,15 +2152,14 @@ class Pygit2(GitProvider): self.credentials = pygit2.Keypair(*keypair_params) return True else: - missing_auth = [x for x in required_params - if not bool(getattr(self, x, None))] + missing_auth = [ + x for x in required_params if not bool(getattr(self, x, None)) + ] _incomplete_auth(missing_auth) - elif 'http' in transport: - required_params = ('user', 'password') - password_ok = all( - bool(getattr(self, x, None)) for x in required_params - ) + elif "http" in transport: + required_params = ("user", "password") + password_ok = all(bool(getattr(self, x, None)) for x in required_params) no_password_auth = not any( bool(getattr(self, x, None)) for x in required_params ) @@ -2019,52 +2168,67 @@ class Pygit2(GitProvider): # http(s). return True if password_ok: - if transport == 'http' and not self.insecure_auth: + if transport == "http" and not self.insecure_auth: log.critical( - 'Invalid configuration for %s remote \'%s\'. ' - 'Authentication is disabled by default on http ' - 'remotes. Either set %s_insecure_auth to True in the ' - 'master configuration file, set a per-remote config ' - 'option named \'insecure_auth\' to True, or use https ' - 'or ssh-based authentication.', - self.role, self.id, self.role + "Invalid configuration for %s remote '%s'. " + "Authentication is disabled by default on http " + "remotes. Either set %s_insecure_auth to True in the " + "master configuration file, set a per-remote config " + "option named 'insecure_auth' to True, or use https " + "or ssh-based authentication.", + self.role, + self.id, + self.role, ) failhard(self.role) self.credentials = pygit2.UserPass(self.user, self.password) return True else: - missing_auth = [x for x in required_params - if not bool(getattr(self, x, None))] + missing_auth = [ + x for x in required_params if not bool(getattr(self, x, None)) + ] _incomplete_auth(missing_auth) else: log.critical( - 'Invalid configuration for %s remote \'%s\'. Unsupported ' - 'transport \'%s\'.', self.role, self.id, transport + "Invalid configuration for %s remote '%s'. Unsupported " + "transport '%s'.", + self.role, + self.id, + transport, ) failhard(self.role) def write_file(self, blob, dest): - ''' + """ Using the blob object, write the file to the destination path - ''' - with salt.utils.files.fopen(dest, 'wb+') as fp_: + """ + with salt.utils.files.fopen(dest, "wb+") as fp_: fp_.write(blob.data) GIT_PROVIDERS = { - 'pygit2': Pygit2, - 'gitpython': GitPython, + "pygit2": Pygit2, + "gitpython": GitPython, } class GitBase(object): - ''' + """ Base class for gitfs/git_pillar - ''' - def __init__(self, opts, remotes=None, per_remote_overrides=(), - per_remote_only=PER_REMOTE_ONLY, global_only=GLOBAL_ONLY, - git_providers=None, cache_root=None, init_remotes=True): - ''' + """ + + def __init__( + self, + opts, + remotes=None, + per_remote_overrides=(), + per_remote_only=PER_REMOTE_ONLY, + global_only=GLOBAL_ONLY, + git_providers=None, + cache_root=None, + init_remotes=True, + ): + """ IMPORTANT: If specifying a cache_root, understand that this is also where the remotes will be cloned. A non-default cache_root is only really designed right now for winrepo, as its repos need to be checked @@ -2102,58 +2266,63 @@ class GitBase(object): git_providers=git_providers) gitfs.fetch_remotes() - ''' + """ self.opts = opts - self.git_providers = git_providers if git_providers is not None \ - else GIT_PROVIDERS + self.git_providers = ( + git_providers if git_providers is not None else GIT_PROVIDERS + ) self.verify_provider() if cache_root is not None: self.cache_root = self.remote_root = cache_root else: - self.cache_root = salt.utils.path.join(self.opts['cachedir'], - self.role) - self.remote_root = salt.utils.path.join(self.cache_root, 'remotes') - self.env_cache = salt.utils.path.join(self.cache_root, 'envs.p') - self.hash_cachedir = salt.utils.path.join(self.cache_root, 'hash') + self.cache_root = salt.utils.path.join(self.opts["cachedir"], self.role) + self.remote_root = salt.utils.path.join(self.cache_root, "remotes") + self.env_cache = salt.utils.path.join(self.cache_root, "envs.p") + self.hash_cachedir = salt.utils.path.join(self.cache_root, "hash") self.file_list_cachedir = salt.utils.path.join( - self.opts['cachedir'], 'file_lists', self.role) + self.opts["cachedir"], "file_lists", self.role + ) if init_remotes: self.init_remotes( remotes if remotes is not None else [], per_remote_overrides, per_remote_only, - global_only) + global_only, + ) - def init_remotes(self, remotes, per_remote_overrides=(), - per_remote_only=PER_REMOTE_ONLY, - global_only=GLOBAL_ONLY): - ''' + def init_remotes( + self, + remotes, + per_remote_overrides=(), + per_remote_only=PER_REMOTE_ONLY, + global_only=GLOBAL_ONLY, + ): + """ Initialize remotes - ''' + """ # The global versions of the auth params (gitfs_user, # gitfs_password, etc.) default to empty strings. If any of them # are defined and the provider is not one that supports auth, then # error out and do not proceed. override_params = copy.deepcopy(per_remote_overrides) global_auth_params = [ - '{0}_{1}'.format(self.role, x) for x in AUTH_PARAMS - if self.opts['{0}_{1}'.format(self.role, x)] + "{0}_{1}".format(self.role, x) + for x in AUTH_PARAMS + if self.opts["{0}_{1}".format(self.role, x)] ] if self.provider in AUTH_PROVIDERS: override_params += AUTH_PARAMS elif global_auth_params: msg = ( - '{0} authentication was configured, but the \'{1}\' ' - '{0}_provider does not support authentication. The ' - 'providers for which authentication is supported in {0} ' - 'are: {2}.'.format( - self.role, self.provider, ', '.join(AUTH_PROVIDERS) - ) + "{0} authentication was configured, but the '{1}' " + "{0}_provider does not support authentication. The " + "providers for which authentication is supported in {0} " + "are: {2}.".format(self.role, self.provider, ", ".join(AUTH_PROVIDERS)) ) - if self.role == 'gitfs': + if self.role == "gitfs": msg += ( - ' See the GitFS Walkthrough in the Salt documentation ' - 'for further information.' + " See the GitFS Walkthrough in the Salt documentation " + "for further information." ) log.critical(msg) failhard(self.role) @@ -2162,11 +2331,12 @@ class GitBase(object): global_values = set(override_params) global_values.update(set(global_only)) for param in global_values: - key = '{0}_{1}'.format(self.role, param) + key = "{0}_{1}".format(self.role, param) if key not in self.opts: log.critical( - 'Key \'%s\' not present in global configuration. This is ' - 'a bug, please report it.', key + "Key '%s' not present in global configuration. This is " + "a bug, please report it.", + key, ) failhard(self.role) per_remote_defaults[param] = enforce_types(key, self.opts[key]) @@ -2180,13 +2350,13 @@ class GitBase(object): per_remote_only, override_params, self.cache_root, - self.role + self.role, ) - if hasattr(repo_obj, 'repo'): + if hasattr(repo_obj, "repo"): # Sanity check and assign the credential parameter repo_obj.verify_auth() repo_obj.setup_callbacks() - if self.opts['__role'] == 'minion' and repo_obj.new: + if self.opts["__role"] == "minion" and repo_obj.new: # Perform initial fetch on masterless minion repo_obj.fetch() @@ -2195,22 +2365,23 @@ class GitBase(object): repo_obj.saltenv_revmap = {} for saltenv, saltenv_conf in six.iteritems(repo_obj.saltenv): - if 'ref' in saltenv_conf: - ref = saltenv_conf['ref'] - repo_obj.saltenv_revmap.setdefault( - ref, []).append(saltenv) + if "ref" in saltenv_conf: + ref = saltenv_conf["ref"] + repo_obj.saltenv_revmap.setdefault(ref, []).append(saltenv) - if saltenv == 'base': + if saltenv == "base": # Remove redundant 'ref' config for base saltenv - repo_obj.saltenv[saltenv].pop('ref') + repo_obj.saltenv[saltenv].pop("ref") if ref != repo_obj.base: log.warning( - 'The \'base\' environment has been ' - 'defined in the \'saltenv\' param for %s ' - 'remote %s and will override the ' - 'branch/tag specified by %s_base (or a ' - 'per-remote \'base\' parameter).', - self.role, repo_obj.id, self.role + "The 'base' environment has been " + "defined in the 'saltenv' param for %s " + "remote %s and will override the " + "branch/tag specified by %s_base (or a " + "per-remote 'base' parameter).", + self.role, + repo_obj.id, + self.role, ) # Rewrite 'base' config param repo_obj.base = ref @@ -2225,9 +2396,8 @@ class GitBase(object): # Add the global saltenv map to the reverse map, skipping envs # explicitly mapped in the per-remote 'saltenv' param. for key, conf in six.iteritems(repo_obj.global_saltenv): - if key not in all_envs and 'ref' in conf: - repo_obj.saltenv_revmap.setdefault( - conf['ref'], []).append(key) + if key not in all_envs and "ref" in conf: + repo_obj.saltenv_revmap.setdefault(conf["ref"], []).append(key) self.remotes.append(repo_obj) @@ -2240,9 +2410,11 @@ class GitBase(object): if collisions: for dirname in collisions: log.critical( - 'The following %s remotes have conflicting cachedirs: ' - '%s. Resolve this using a per-remote parameter called ' - '\'name\'.', self.role, ', '.join(cachedir_map[dirname]) + "The following %s remotes have conflicting cachedirs: " + "%s. Resolve this using a per-remote parameter called " + "'name'.", + self.role, + ", ".join(cachedir_map[dirname]), ) failhard(self.role) @@ -2250,9 +2422,9 @@ class GitBase(object): self.write_remote_map() def clear_old_remotes(self): - ''' + """ Remove cache directories for remotes no longer configured - ''' + """ try: cachedir_ls = os.listdir(self.cache_root) except OSError: @@ -2265,7 +2437,7 @@ class GitBase(object): pass to_remove = [] for item in cachedir_ls: - if item in ('hash', 'refs'): + if item in ("hash", "refs"): continue path = salt.utils.path.join(self.cache_root, item) if os.path.isdir(path): @@ -2277,12 +2449,14 @@ class GitBase(object): shutil.rmtree(rdir) except OSError as exc: log.error( - 'Unable to remove old %s remote cachedir %s: %s', - self.role, rdir, exc + "Unable to remove old %s remote cachedir %s: %s", + self.role, + rdir, + exc, ) failed.append(rdir) else: - log.debug('%s removed old cachedir %s', self.role, rdir) + log.debug("%s removed old cachedir %s", self.role, rdir) for fdir in failed: to_remove.remove(fdir) ret = bool(to_remove) @@ -2291,24 +2465,22 @@ class GitBase(object): return ret def clear_cache(self): - ''' + """ Completely clear cache - ''' + """ errors = [] for rdir in (self.cache_root, self.file_list_cachedir): if os.path.exists(rdir): try: shutil.rmtree(rdir) except OSError as exc: - errors.append( - 'Unable to delete {0}: {1}'.format(rdir, exc) - ) + errors.append("Unable to delete {0}: {1}".format(rdir, exc)) return errors - def clear_lock(self, remote=None, lock_type='update'): - ''' + def clear_lock(self, remote=None, lock_type="update"): + """ Clear update.lk for all remotes - ''' + """ cleared = [] errors = [] for repo in self.remotes: @@ -2328,22 +2500,23 @@ class GitBase(object): return cleared, errors def fetch_remotes(self, remotes=None): - ''' + """ Fetch all remotes and return a boolean to let the calling function know whether or not any remotes were updated in the process of fetching - ''' + """ if remotes is None: remotes = [] elif not isinstance(remotes, list): log.error( - 'Invalid \'remotes\' argument (%s) for fetch_remotes. ' - 'Must be a list of strings', remotes + "Invalid 'remotes' argument (%s) for fetch_remotes. " + "Must be a list of strings", + remotes, ) remotes = [] changed = False for repo in self.remotes: - name = getattr(repo, 'name', None) + name = getattr(repo, "name", None) if not remotes or (repo.id, name) in remotes: try: if repo.fetch(): @@ -2356,16 +2529,18 @@ class GitBase(object): changed = True except Exception as exc: # pylint: disable=broad-except log.error( - 'Exception caught while fetching %s remote \'%s\': %s', - self.role, repo.id, exc, - exc_info=True + "Exception caught while fetching %s remote '%s': %s", + self.role, + repo.id, + exc, + exc_info=True, ) return changed def lock(self, remote=None): - ''' + """ Place an update.lk - ''' + """ locked = [] errors = [] for repo in self.remotes: @@ -2385,7 +2560,7 @@ class GitBase(object): return locked, errors def update(self, remotes=None): - ''' + """ .. versionchanged:: 2018.3.0 The remotes argument was added. This being a list of remote URLs, it will only update matching remotes. This actually matches on @@ -2393,20 +2568,19 @@ class GitBase(object): Execute a git fetch on all of the repos and perform maintenance on the fileserver cache. - ''' + """ # data for the fileserver event - data = {'changed': False, - 'backend': 'gitfs'} + data = {"changed": False, "backend": "gitfs"} - data['changed'] = self.clear_old_remotes() + data["changed"] = self.clear_old_remotes() if self.fetch_remotes(remotes=remotes): - data['changed'] = True + data["changed"] = True # A masterless minion will need a new env cache file even if no changes # were fetched. - refresh_env_cache = self.opts['__role'] == 'minion' + refresh_env_cache = self.opts["__role"] == "minion" - if data['changed'] is True or not os.path.isfile(self.env_cache): + if data["changed"] is True or not os.path.isfile(self.env_cache): env_cachedir = os.path.dirname(self.env_cache) if not os.path.exists(env_cachedir): os.makedirs(env_cachedir) @@ -2415,56 +2589,55 @@ class GitBase(object): if refresh_env_cache: new_envs = self.envs(ignore_cache=True) serial = salt.payload.Serial(self.opts) - with salt.utils.files.fopen(self.env_cache, 'wb+') as fp_: + with salt.utils.files.fopen(self.env_cache, "wb+") as fp_: fp_.write(serial.dumps(new_envs)) - log.trace('Wrote env cache data to %s', self.env_cache) + log.trace("Wrote env cache data to %s", self.env_cache) # if there is a change, fire an event - if self.opts.get('fileserver_events', False): + if self.opts.get("fileserver_events", False): event = salt.utils.event.get_event( - 'master', - self.opts['sock_dir'], - self.opts['transport'], - opts=self.opts, - listen=False) - event.fire_event( - data, - tagify(['gitfs', 'update'], prefix='fileserver') + "master", + self.opts["sock_dir"], + self.opts["transport"], + opts=self.opts, + listen=False, ) + event.fire_event(data, tagify(["gitfs", "update"], prefix="fileserver")) try: salt.fileserver.reap_fileserver_cache_dir( - self.hash_cachedir, - self.find_file + self.hash_cachedir, self.find_file ) except (OSError, IOError): # Hash file won't exist if no files have yet been served up pass def update_intervals(self): - ''' + """ Returns a dictionary mapping remote IDs to their intervals, designed to be used for variable update intervals in salt.master.FileserverUpdate. A remote's ID is defined here as a tuple of the GitPython/Pygit2 object's "id" and "name" attributes, with None being assumed as the "name" value if the attribute is not present. - ''' - return {(repo.id, getattr(repo, 'name', None)): repo.update_interval - for repo in self.remotes} + """ + return { + (repo.id, getattr(repo, "name", None)): repo.update_interval + for repo in self.remotes + } def verify_provider(self): - ''' + """ Determine which provider to use - ''' - if 'verified_{0}_provider'.format(self.role) in self.opts: - self.provider = self.opts['verified_{0}_provider'.format(self.role)] + """ + if "verified_{0}_provider".format(self.role) in self.opts: + self.provider = self.opts["verified_{0}_provider".format(self.role)] else: - desired_provider = self.opts.get('{0}_provider'.format(self.role)) + desired_provider = self.opts.get("{0}_provider".format(self.role)) if not desired_provider: if self.verify_pygit2(quiet=True): - self.provider = 'pygit2' + self.provider = "pygit2" elif self.verify_gitpython(quiet=True): - self.provider = 'gitpython' + self.provider = "gitpython" else: # Ensure non-lowercase providers work try: @@ -2475,55 +2648,53 @@ class GitBase(object): desired_provider = six.text_type(desired_provider).lower() if desired_provider not in self.git_providers: log.critical( - 'Invalid %s_provider \'%s\'. Valid choices are: %s', + "Invalid %s_provider '%s'. Valid choices are: %s", self.role, desired_provider, - ', '.join(self.git_providers) + ", ".join(self.git_providers), ) failhard(self.role) - elif desired_provider == 'pygit2' and self.verify_pygit2(): - self.provider = 'pygit2' - elif desired_provider == 'gitpython' and self.verify_gitpython(): - self.provider = 'gitpython' - if not hasattr(self, 'provider'): - log.critical( - 'No suitable %s provider module is installed.', self.role - ) + elif desired_provider == "pygit2" and self.verify_pygit2(): + self.provider = "pygit2" + elif desired_provider == "gitpython" and self.verify_gitpython(): + self.provider = "gitpython" + if not hasattr(self, "provider"): + log.critical("No suitable %s provider module is installed.", self.role) failhard(self.role) def verify_gitpython(self, quiet=False): - ''' + """ Check if GitPython is available and at a compatible version (>= 0.3.0) - ''' + """ + def _recommend(): - if PYGIT2_VERSION and 'pygit2' in self.git_providers: + if PYGIT2_VERSION and "pygit2" in self.git_providers: log.error(_RECOMMEND_PYGIT2, self.role, self.role) if not GITPYTHON_VERSION: if not quiet: log.error( - '%s is configured but could not be loaded, is GitPython ' - 'installed?', self.role + "%s is configured but could not be loaded, is GitPython " + "installed?", + self.role, ) _recommend() return False - elif 'gitpython' not in self.git_providers: + elif "gitpython" not in self.git_providers: return False errors = [] if GITPYTHON_VERSION < GITPYTHON_MINVER: errors.append( - '{0} is configured, but the GitPython version is earlier than ' - '{1}. Version {2} detected.'.format( - self.role, - GITPYTHON_MINVER, - GITPYTHON_VERSION + "{0} is configured, but the GitPython version is earlier than " + "{1}. Version {2} detected.".format( + self.role, GITPYTHON_MINVER, GITPYTHON_VERSION ) ) - if not salt.utils.path.which('git'): + if not salt.utils.path.which("git"): errors.append( - 'The git command line utility is required when using the ' - '\'gitpython\' {0}_provider.'.format(self.role) + "The git command line utility is required when using the " + "'gitpython' {0}_provider.".format(self.role) ) if errors: @@ -2533,54 +2704,53 @@ class GitBase(object): _recommend() return False - self.opts['verified_{0}_provider'.format(self.role)] = 'gitpython' - log.debug('gitpython %s_provider enabled', self.role) + self.opts["verified_{0}_provider".format(self.role)] = "gitpython" + log.debug("gitpython %s_provider enabled", self.role) return True def verify_pygit2(self, quiet=False): - ''' + """ Check if pygit2/libgit2 are available and at a compatible version. Pygit2 must be at least 0.20.3 and libgit2 must be at least 0.20.0. - ''' + """ + def _recommend(): - if GITPYTHON_VERSION and 'gitpython' in self.git_providers: + if GITPYTHON_VERSION and "gitpython" in self.git_providers: log.error(_RECOMMEND_GITPYTHON, self.role, self.role) if not PYGIT2_VERSION: if not quiet: log.error( - '%s is configured but could not be loaded, are pygit2 ' - 'and libgit2 installed?', self.role + "%s is configured but could not be loaded, are pygit2 " + "and libgit2 installed?", + self.role, ) _recommend() return False - elif 'pygit2' not in self.git_providers: + elif "pygit2" not in self.git_providers: return False errors = [] if PYGIT2_VERSION < PYGIT2_MINVER: errors.append( - '{0} is configured, but the pygit2 version is earlier than ' - '{1}. Version {2} detected.'.format( - self.role, - PYGIT2_MINVER, - PYGIT2_VERSION + "{0} is configured, but the pygit2 version is earlier than " + "{1}. Version {2} detected.".format( + self.role, PYGIT2_MINVER, PYGIT2_VERSION ) ) if LIBGIT2_VERSION < LIBGIT2_MINVER: errors.append( - '{0} is configured, but the libgit2 version is earlier than ' - '{1}. Version {2} detected.'.format( - self.role, - LIBGIT2_MINVER, - LIBGIT2_VERSION + "{0} is configured, but the libgit2 version is earlier than " + "{1}. Version {2} detected.".format( + self.role, LIBGIT2_MINVER, LIBGIT2_VERSION ) ) - if not getattr(pygit2, 'GIT_FETCH_PRUNE', False) \ - and not salt.utils.path.which('git'): + if not getattr(pygit2, "GIT_FETCH_PRUNE", False) and not salt.utils.path.which( + "git" + ): errors.append( - 'The git command line utility is required when using the ' - '\'pygit2\' {0}_provider.'.format(self.role) + "The git command line utility is required when using the " + "'pygit2' {0}_provider.".format(self.role) ) if errors: @@ -2590,44 +2760,35 @@ class GitBase(object): _recommend() return False - self.opts['verified_{0}_provider'.format(self.role)] = 'pygit2' - log.debug('pygit2 %s_provider enabled', self.role) + self.opts["verified_{0}_provider".format(self.role)] = "pygit2" + log.debug("pygit2 %s_provider enabled", self.role) return True def write_remote_map(self): - ''' + """ Write the remote_map.txt - ''' - remote_map = salt.utils.path.join(self.cache_root, 'remote_map.txt') + """ + remote_map = salt.utils.path.join(self.cache_root, "remote_map.txt") try: - with salt.utils.files.fopen(remote_map, 'w+') as fp_: - timestamp = \ - datetime.now().strftime('%d %b %Y %H:%M:%S.%f') - fp_.write( - '# {0}_remote map as of {1}\n'.format( - self.role, - timestamp - ) - ) + with salt.utils.files.fopen(remote_map, "w+") as fp_: + timestamp = datetime.now().strftime("%d %b %Y %H:%M:%S.%f") + fp_.write("# {0}_remote map as of {1}\n".format(self.role, timestamp)) for repo in self.remotes: fp_.write( salt.utils.stringutils.to_str( - '{0} = {1}\n'.format( - repo.cachedir_basename, - repo.id - ) + "{0} = {1}\n".format(repo.cachedir_basename, repo.id) ) ) except OSError: pass else: - log.info('Wrote new %s remote map to %s', self.role, remote_map) + log.info("Wrote new %s remote map to %s", self.role, remote_map) def do_checkout(self, repo): - ''' + """ Common code for git_pillar/winrepo to handle locking and checking out of a repo. - ''' + """ time_start = time.time() while time.time() - time_start <= 5: try: @@ -2638,43 +2799,53 @@ class GitBase(object): continue else: log.error( - 'Error %d encountered while obtaining checkout ' - 'lock for %s remote \'%s\': %s', + "Error %d encountered while obtaining checkout " + "lock for %s remote '%s': %s", exc.errno, repo.role, repo.id, exc, - exc_info=True + exc_info=True, ) break else: log.error( - 'Timed out waiting for checkout lock to be released for ' - '%s remote \'%s\'. If this error persists, run \'salt-run ' - 'cache.clear_git_lock %s type=checkout\' to clear it.', - self.role, repo.id, self.role + "Timed out waiting for checkout lock to be released for " + "%s remote '%s'. If this error persists, run 'salt-run " + "cache.clear_git_lock %s type=checkout' to clear it.", + self.role, + repo.id, + self.role, ) return None class GitFS(GitBase): - ''' + """ Functionality specific to the git fileserver backend - ''' - role = 'gitfs' + """ + + role = "gitfs" instance_map = weakref.WeakKeyDictionary() - def __new__(cls, opts, remotes=None, per_remote_overrides=(), - per_remote_only=PER_REMOTE_ONLY, git_providers=None, - cache_root=None, init_remotes=True): - ''' + def __new__( + cls, + opts, + remotes=None, + per_remote_overrides=(), + per_remote_only=PER_REMOTE_ONLY, + git_providers=None, + cache_root=None, + init_remotes=True, + ): + """ If we are not initializing remotes (such as in cases where we just want to load the config so that we can run clear_cache), then just return a new __init__'ed object. Otherwise, check the instance map and re-use an instance if one exists for the current process. Weak references are used to ensure that we garbage collect instances for threads which have exited. - ''' + """ # No need to get the ioloop reference if we're not initializing remotes io_loop = salt.ext.tornado.ioloop.IOLoop.current() if init_remotes else None if not init_remotes or io_loop not in cls.instance_map: @@ -2687,41 +2858,50 @@ class GitFS(GitBase): remotes if remotes is not None else [], per_remote_overrides=per_remote_overrides, per_remote_only=per_remote_only, - git_providers=git_providers if git_providers is not None - else GIT_PROVIDERS, + git_providers=git_providers + if git_providers is not None + else GIT_PROVIDERS, cache_root=cache_root, - init_remotes=init_remotes) + init_remotes=init_remotes, + ) if not init_remotes: - log.debug('Created gitfs object with uninitialized remotes') + log.debug("Created gitfs object with uninitialized remotes") else: - log.debug('Created gitfs object for process %s', os.getpid()) + log.debug("Created gitfs object for process %s", os.getpid()) # Add to the instance map so we can re-use later cls.instance_map[io_loop] = obj return obj - log.debug('Re-using gitfs object for process %s', os.getpid()) + log.debug("Re-using gitfs object for process %s", os.getpid()) return cls.instance_map[io_loop] - def __init__(self, opts, remotes, per_remote_overrides=(), # pylint: disable=super-init-not-called - per_remote_only=PER_REMOTE_ONLY, git_providers=None, - cache_root=None, init_remotes=True): + # pylint: disable=super-init-not-called + def __init__( + self, + opts, + remotes, + per_remote_overrides=(), + per_remote_only=PER_REMOTE_ONLY, + git_providers=None, + cache_root=None, + init_remotes=True, + ): # Initialization happens above in __new__(), so don't do anything here pass + # pylint: enable=super-init-not-called + def dir_list(self, load): - ''' + """ Return a list of all directories on the master - ''' - return self._file_lists(load, 'dirs') + """ + return self._file_lists(load, "dirs") def envs(self, ignore_cache=False): - ''' + """ Return a list of refs that can be used as environments - ''' + """ if not ignore_cache: - cache_match = salt.fileserver.check_env_cache( - self.opts, - self.env_cache - ) + cache_match = salt.fileserver.check_env_cache(self.opts, self.env_cache) if cache_match is not None: return cache_match ret = set() @@ -2732,27 +2912,23 @@ class GitFS(GitBase): ret.update([x for x in repo_envs if repo.env_is_exposed(x)]) return sorted(ret) - def find_file(self, path, tgt_env='base', **kwargs): # pylint: disable=W0613 - ''' + def find_file(self, path, tgt_env="base", **kwargs): # pylint: disable=W0613 + """ Find the first file to match the path and ref, read the file out of git and send the path to the newly cached file - ''' - fnd = {'path': '', - 'rel': ''} - if os.path.isabs(path) or \ - (not salt.utils.stringutils.is_hex(tgt_env) and tgt_env not in self.envs()): + """ + fnd = {"path": "", "rel": ""} + if os.path.isabs(path): return fnd - dest = salt.utils.path.join(self.cache_root, 'refs', tgt_env, path) - hashes_glob = salt.utils.path.join(self.hash_cachedir, - tgt_env, - '{0}.hash.*'.format(path)) - blobshadest = salt.utils.path.join(self.hash_cachedir, - tgt_env, - '{0}.hash.blob_sha1'.format(path)) - lk_fn = salt.utils.path.join(self.hash_cachedir, - tgt_env, - '{0}.lk'.format(path)) + dest = salt.utils.path.join(self.cache_root, "refs", tgt_env, path) + hashes_glob = salt.utils.path.join( + self.hash_cachedir, tgt_env, "{0}.hash.*".format(path) + ) + blobshadest = salt.utils.path.join( + self.hash_cachedir, tgt_env, "{0}.hash.blob_sha1".format(path) + ) + lk_fn = salt.utils.path.join(self.hash_cachedir, tgt_env, "{0}.lk".format(path)) destdir = os.path.dirname(dest) hashdir = os.path.dirname(blobshadest) if not os.path.isdir(destdir): @@ -2771,10 +2947,17 @@ class GitFS(GitBase): os.makedirs(hashdir) for repo in self.remotes: - if repo.mountpoint(tgt_env) \ - and not path.startswith(repo.mountpoint(tgt_env) + os.sep): + if repo.mountpoint(tgt_env) and not path.startswith( + repo.mountpoint(tgt_env) + os.sep + ): continue - repo_path = path[len(repo.mountpoint(tgt_env)):].lstrip(os.sep) + if ( + not salt.utils.stringutils.is_hex(tgt_env) + and tgt_env not in self.envs() + and not repo.fallback + ): + continue + repo_path = path[len(repo.mountpoint(tgt_env)) :].lstrip(os.sep) if repo.root(tgt_env): repo_path = salt.utils.path.join(repo.root(tgt_env), repo_path) @@ -2783,7 +2966,7 @@ class GitFS(GitBase): continue def _add_file_stat(fnd, mode): - ''' + """ Add a the mode to the return dict. In other fileserver backends we stat the file to get its mode, and add the stat result (passed through list() for better serialization) to the 'stat' @@ -2791,24 +2974,24 @@ class GitFS(GitBase): stat result for anything but the mode at this time, we can avoid unnecessary work by just manually creating the list and not running an os.stat() on all files in the repo. - ''' + """ if mode is not None: - fnd['stat'] = [mode] + fnd["stat"] = [mode] return fnd salt.fileserver.wait_lock(lk_fn, dest) try: - with salt.utils.files.fopen(blobshadest, 'r') as fp_: + with salt.utils.files.fopen(blobshadest, "r") as fp_: sha = salt.utils.stringutils.to_unicode(fp_.read()) if sha == blob_hexsha: - fnd['rel'] = path - fnd['path'] = dest + fnd["rel"] = path + fnd["path"] = dest return _add_file_stat(fnd, blob_mode) except IOError as exc: if exc.errno != errno.ENOENT: six.reraise(*sys.exc_info()) - with salt.utils.files.fopen(lk_fn, 'w'): + with salt.utils.files.fopen(lk_fn, "w"): pass for filename in glob.glob(hashes_glob): @@ -2818,14 +3001,14 @@ class GitFS(GitBase): pass # Write contents of file to their destination in the FS cache repo.write_file(blob, dest) - with salt.utils.files.fopen(blobshadest, 'w+') as fp_: + with salt.utils.files.fopen(blobshadest, "w+") as fp_: fp_.write(blob_hexsha) try: os.remove(lk_fn) except OSError: pass - fnd['rel'] = path - fnd['path'] = dest + fnd["rel"] = path + fnd["path"] = dest return _add_file_stat(fnd, blob_mode) # No matching file was found in tgt_env. Return a dict with empty paths @@ -2833,58 +3016,58 @@ class GitFS(GitBase): return fnd def serve_file(self, load, fnd): - ''' + """ Return a chunk from a file based on the data received - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - ret = {'data': '', - 'dest': ''} - required_load_keys = set(['path', 'loc', 'saltenv']) + ret = {"data": "", "dest": ""} + required_load_keys = set(["path", "loc", "saltenv"]) if not all(x in load for x in required_load_keys): log.debug( - 'Not all of the required keys present in payload. Missing: %s', - ', '.join(required_load_keys.difference(load)) + "Not all of the required keys present in payload. Missing: %s", + ", ".join(required_load_keys.difference(load)), ) return ret - if not fnd['path']: + if not fnd["path"]: return ret - ret['dest'] = fnd['rel'] - gzip = load.get('gzip', None) - fpath = os.path.normpath(fnd['path']) - with salt.utils.files.fopen(fpath, 'rb') as fp_: - fp_.seek(load['loc']) - data = fp_.read(self.opts['file_buffer_size']) + ret["dest"] = fnd["rel"] + gzip = load.get("gzip", None) + fpath = os.path.normpath(fnd["path"]) + with salt.utils.files.fopen(fpath, "rb") as fp_: + fp_.seek(load["loc"]) + data = fp_.read(self.opts["file_buffer_size"]) if data and six.PY3 and not salt.utils.files.is_binary(fpath): data = data.decode(__salt_system_encoding__) if gzip and data: data = salt.utils.gzip_util.compress(data, gzip) - ret['gzip'] = gzip - ret['data'] = data + ret["gzip"] = gzip + ret["data"] = data return ret def file_hash(self, load, fnd): - ''' + """ Return a file hash, the hash type is set in the master config file - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if not all(x in load for x in ('path', 'saltenv')): - return '', None - ret = {'hash_type': self.opts['hash_type']} - relpath = fnd['rel'] - path = fnd['path'] - hashdest = salt.utils.path.join(self.hash_cachedir, - load['saltenv'], - '{0}.hash.{1}'.format(relpath, - self.opts['hash_type'])) + if not all(x in load for x in ("path", "saltenv")): + return "", None + ret = {"hash_type": self.opts["hash_type"]} + relpath = fnd["rel"] + path = fnd["path"] + hashdest = salt.utils.path.join( + self.hash_cachedir, + load["saltenv"], + "{0}.hash.{1}".format(relpath, self.opts["hash_type"]), + ) try: - with salt.utils.files.fopen(hashdest, 'rb') as fp_: - ret['hsum'] = fp_.read() + with salt.utils.files.fopen(hashdest, "rb") as fp_: + ret["hsum"] = fp_.read() return ret except IOError as exc: if exc.errno != errno.ENOENT: @@ -2896,57 +3079,59 @@ class GitFS(GitBase): if exc.errno != errno.EEXIST: six.reraise(*sys.exc_info()) - ret['hsum'] = salt.utils.hashutils.get_hash(path, self.opts['hash_type']) - with salt.utils.files.fopen(hashdest, 'w+') as fp_: - fp_.write(ret['hsum']) + ret["hsum"] = salt.utils.hashutils.get_hash(path, self.opts["hash_type"]) + with salt.utils.files.fopen(hashdest, "w+") as fp_: + fp_.write(ret["hsum"]) return ret def _file_lists(self, load, form): - ''' + """ Return a dict containing the file lists for files and dirs - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") if not os.path.isdir(self.file_list_cachedir): try: os.makedirs(self.file_list_cachedir) except os.error: - log.error('Unable to make cachedir %s', self.file_list_cachedir) + log.error("Unable to make cachedir %s", self.file_list_cachedir) return [] list_cache = salt.utils.path.join( self.file_list_cachedir, - '{0}.p'.format(load['saltenv'].replace(os.path.sep, '_|-')) + "{0}.p".format(load["saltenv"].replace(os.path.sep, "_|-")), ) w_lock = salt.utils.path.join( self.file_list_cachedir, - '.{0}.w'.format(load['saltenv'].replace(os.path.sep, '_|-')) + ".{0}.w".format(load["saltenv"].replace(os.path.sep, "_|-")), + ) + cache_match, refresh_cache, save_cache = salt.fileserver.check_file_list_cache( + self.opts, form, list_cache, w_lock ) - cache_match, refresh_cache, save_cache = \ - salt.fileserver.check_file_list_cache( - self.opts, form, list_cache, w_lock - ) if cache_match is not None: return cache_match if refresh_cache: - log.trace('Start rebuilding gitfs file_list cache') - ret = {'files': set(), 'symlinks': {}, 'dirs': set()} - if salt.utils.stringutils.is_hex(load['saltenv']) \ - or load['saltenv'] in self.envs(): - for repo in self.remotes: + log.trace("Start rebuilding gitfs file_list cache") + ret = {"files": set(), "symlinks": {}, "dirs": set()} + for repo in self.remotes: + if ( + salt.utils.stringutils.is_hex(load["saltenv"]) + or load["saltenv"] in self.envs() + or repo.fallback + ): start = time.time() - repo_files, repo_symlinks = repo.file_list(load['saltenv']) - ret['files'].update(repo_files) - ret['symlinks'].update(repo_symlinks) - ret['dirs'].update(repo.dir_list(load['saltenv'])) + repo_files, repo_symlinks = repo.file_list(load["saltenv"]) + ret["files"].update(repo_files) + ret["symlinks"].update(repo_symlinks) + ret["dirs"].update(repo.dir_list(load["saltenv"])) log.profile( - 'gitfs file_name cache rebuild repo=%s duration=%s seconds', - repo.id, - time.time() - start + "gitfs file_name cache rebuild repo=%s duration=%s seconds", + repo.id, + time.time() - start, ) - ret['files'] = sorted(ret['files']) - ret['dirs'] = sorted(ret['dirs']) + ret["files"] = sorted(ret["files"]) + ret["dirs"] = sorted(ret["dirs"]) if save_cache: salt.fileserver.write_file_list_cache( @@ -2955,74 +3140,81 @@ class GitFS(GitBase): # NOTE: symlinks are organized in a dict instead of a list, however # the 'symlinks' key will be defined above so it will never get to # the default value in the call to ret.get() below. - log.trace('Finished rebuilding gitfs file_list cache') + log.trace("Finished rebuilding gitfs file_list cache") return ret.get(form, []) # Shouldn't get here, but if we do, this prevents a TypeError - return {} if form == 'symlinks' else [] + return {} if form == "symlinks" else [] def file_list(self, load): - ''' + """ Return a list of all files on the file server in a specified environment - ''' - return self._file_lists(load, 'files') + """ + return self._file_lists(load, "files") def file_list_emptydirs(self, load): # pylint: disable=W0613 - ''' + """ Return a list of all empty directories on the master - ''' + """ # Cannot have empty dirs in git return [] def symlink_list(self, load): - ''' + """ Return a dict of all symlinks based on a given path in the repo - ''' - if 'env' in load: + """ + if "env" in load: # "env" is not supported; Use "saltenv". - load.pop('env') + load.pop("env") - if not salt.utils.stringutils.is_hex(load['saltenv']) \ - and load['saltenv'] not in self.envs(): + if ( + not salt.utils.stringutils.is_hex(load["saltenv"]) + and load["saltenv"] not in self.envs() + ): return {} - if 'prefix' in load: - prefix = load['prefix'].strip('/') + if "prefix" in load: + prefix = load["prefix"].strip("/") else: - prefix = '' - symlinks = self._file_lists(load, 'symlinks') - return dict([(key, val) - for key, val in six.iteritems(symlinks) - if key.startswith(prefix)]) + prefix = "" + symlinks = self._file_lists(load, "symlinks") + return dict( + [ + (key, val) + for key, val in six.iteritems(symlinks) + if key.startswith(prefix) + ] + ) class GitPillar(GitBase): - ''' + """ Functionality specific to the git external pillar - ''' - role = 'git_pillar' + """ + + role = "git_pillar" def checkout(self): - ''' + """ Checkout the targeted branches/tags from the git_pillar remotes - ''' + """ self.pillar_dirs = OrderedDict() self.pillar_linked_dirs = [] for repo in self.remotes: cachedir = self.do_checkout(repo) if cachedir is not None: # Figure out which environment this remote should be assigned - if repo.branch == '__env__' and hasattr(repo, 'all_saltenvs'): - env = self.opts.get('pillarenv') \ - or self.opts.get('saltenv') \ - or 'base' + if repo.branch == "__env__" and hasattr(repo, "all_saltenvs"): + env = ( + self.opts.get("pillarenv") or self.opts.get("saltenv") or "base" + ) elif repo.env: env = repo.env else: if repo.branch == repo.base: - env = 'base' + env = "base" else: tgt = repo.get_checkout_target() - env = 'base' if tgt == repo.base else tgt + env = "base" if tgt == repo.base else tgt if repo._mountpoint: if self.link_mountpoint(repo): self.pillar_dirs[repo.linkdir] = env @@ -3031,32 +3223,32 @@ class GitPillar(GitBase): self.pillar_dirs[cachedir] = env def link_mountpoint(self, repo): - ''' + """ Ensure that the mountpoint is present in the correct location and points at the correct path - ''' + """ lcachelink = salt.utils.path.join(repo.linkdir, repo._mountpoint) lcachedest = salt.utils.path.join(repo.cachedir, repo.root()).rstrip(os.sep) wipe_linkdir = False create_link = False try: - with repo.gen_lock(lock_type='mountpoint', timeout=10): + with repo.gen_lock(lock_type="mountpoint", timeout=10): walk_results = list(os.walk(repo.linkdir, followlinks=False)) if walk_results != repo.linkdir_walk: log.debug( - 'Results of walking %s differ from expected results', - repo.linkdir + "Results of walking %s differ from expected results", + repo.linkdir, ) - log.debug('Walk results: %s', walk_results) - log.debug('Expected results: %s', repo.linkdir_walk) + log.debug("Walk results: %s", walk_results) + log.debug("Expected results: %s", repo.linkdir_walk) wipe_linkdir = True else: - if not all(not salt.utils.path.islink(x[0]) - and os.path.isdir(x[0]) - for x in walk_results[:-1]): + if not all( + not salt.utils.path.islink(x[0]) and os.path.isdir(x[0]) + for x in walk_results[:-1] + ): log.debug( - 'Linkdir parents of %s are not all directories', - lcachelink + "Linkdir parents of %s are not all directories", lcachelink ) wipe_linkdir = True elif not salt.utils.path.islink(lcachelink): @@ -3065,25 +3257,27 @@ class GitPillar(GitBase): try: ldest = salt.utils.path.readlink(lcachelink) except Exception: # pylint: disable=broad-except - log.debug( - 'Failed to read destination of %s', lcachelink - ) + log.debug("Failed to read destination of %s", lcachelink) wipe_linkdir = True else: if ldest != lcachedest: log.debug( - 'Destination of %s (%s) does not match ' - 'the expected value (%s)', - lcachelink, ldest, lcachedest + "Destination of %s (%s) does not match " + "the expected value (%s)", + lcachelink, + ldest, + lcachedest, ) # Since we know that the parent dirs of the # link are set up properly, all we need to do # is remove the symlink and let it be created # below. try: - if salt.utils.platform.is_windows() \ - and not ldest.startswith('\\\\') \ - and os.path.isdir(ldest): + if ( + salt.utils.platform.is_windows() + and not ldest.startswith("\\\\") + and os.path.isdir(ldest) + ): # On Windows, symlinks to directories # must be removed as if they were # themselves directories. @@ -3092,9 +3286,10 @@ class GitPillar(GitBase): os.remove(lcachelink) except Exception as exc: # pylint: disable=broad-except log.exception( - 'Failed to remove existing git_pillar ' - 'mountpoint link %s: %s', - lcachelink, exc.__str__() + "Failed to remove existing git_pillar " + "mountpoint link %s: %s", + lcachelink, + exc.__str__(), ) wipe_linkdir = False create_link = True @@ -3109,11 +3304,12 @@ class GitPillar(GitBase): try: ldirname = os.path.dirname(lcachelink) os.makedirs(ldirname) - log.debug('Successfully made linkdir parent %s', ldirname) + log.debug("Successfully made linkdir parent %s", ldirname) except OSError as exc: log.error( - 'Failed to os.makedirs() linkdir parent %s: %s', - ldirname, exc.__str__() + "Failed to os.makedirs() linkdir parent %s: %s", + ldirname, + exc.__str__(), ) return False @@ -3121,43 +3317,50 @@ class GitPillar(GitBase): try: os.symlink(lcachedest, lcachelink) log.debug( - 'Successfully linked %s to cachedir %s', - lcachelink, lcachedest + "Successfully linked %s to cachedir %s", + lcachelink, + lcachedest, ) return True except OSError as exc: log.error( - 'Failed to create symlink to %s at path %s: %s', - lcachedest, lcachelink, exc.__str__() + "Failed to create symlink to %s at path %s: %s", + lcachedest, + lcachelink, + exc.__str__(), ) return False except GitLockError: log.error( - 'Timed out setting mountpoint lock for %s remote \'%s\'. If ' - 'this error persists, it may be because an earlier %s ' - 'checkout was interrupted. The lock can be cleared by running ' - '\'salt-run cache.clear_git_lock %s type=mountpoint\', or by ' - 'manually removing %s.', - self.role, repo.id, self.role, self.role, - repo._get_lock_file(lock_type='mountpoint') + "Timed out setting mountpoint lock for %s remote '%s'. If " + "this error persists, it may be because an earlier %s " + "checkout was interrupted. The lock can be cleared by running " + "'salt-run cache.clear_git_lock %s type=mountpoint', or by " + "manually removing %s.", + self.role, + repo.id, + self.role, + self.role, + repo._get_lock_file(lock_type="mountpoint"), ) return False return True class WinRepo(GitBase): - ''' + """ Functionality specific to the winrepo runner - ''' - role = 'winrepo' + """ + + role = "winrepo" # Need to define this in case we try to reference it before checking # out the repos. winrepo_dirs = {} def checkout(self): - ''' + """ Checkout the targeted branches/tags from the winrepo remotes - ''' + """ self.winrepo_dirs = {} for repo in self.remotes: cachedir = self.do_checkout(repo) diff --git a/salt/utils/github.py b/salt/utils/github.py index 87d12a204e6..fff60fc8e4a 100644 --- a/salt/utils/github.py +++ b/salt/utils/github.py @@ -1,15 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Connection library for GitHub -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.http + # Import Salt libs import salt.utils.json -import salt.utils.http # Import 3rd-party libs from salt.ext import six @@ -18,7 +20,7 @@ log = logging.getLogger(__name__) def get_user_pubkeys(users): - ''' + """ Retrieve a set of public keys from GitHub for the specified list of users. Expects input in list format. Optionally, a value in the list may be a dict whose value is a list of key IDs to be returned. If this is not done, then @@ -38,9 +40,9 @@ def get_user_pubkeys(users): 'user2', 'user3', ] - ''' + """ if not isinstance(users, list): - return {'Error': 'A list of users is expected'} + return {"Error": "A list of users is expected"} ret = {} for user in users: @@ -50,22 +52,17 @@ def get_user_pubkeys(users): key_ids = user[tmp_user] user = tmp_user - url = 'https://api.github.com/users/{0}/keys'.format(user) - result = salt.utils.http.query( - url, - 'GET', - decode=False, - text=True, - ) + url = "https://api.github.com/users/{0}/keys".format(user) + result = salt.utils.http.query(url, "GET", decode=False, text=True,) - keys = salt.utils.json.loads(result['text']) + keys = salt.utils.json.loads(result["text"]) ret[user] = {} for key in keys: if len(key_ids) > 0: - if six.text_type(key['id']) in key_ids: - ret[user][key['id']] = key['key'] + if six.text_type(key["id"]) in key_ids: + ret[user][key["id"]] = key["key"] else: - ret[user][key['id']] = key['key'] + ret[user][key["id"]] = key["key"] return ret diff --git a/salt/utils/gzip_util.py b/salt/utils/gzip_util.py index 61cab061a6d..65bc8d67778 100644 --- a/salt/utils/gzip_util.py +++ b/salt/utils/gzip_util.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" salt.utils.gzip ~~~~~~~~~~~~~~~ Helper module for handling gzip consistently between 2.7+ and 2.6- -''' +""" -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import gzip @@ -19,8 +19,7 @@ from salt.ext.six import BytesIO class GzipFile(gzip.GzipFile): - def __init__(self, filename=None, mode=None, - compresslevel=9, fileobj=None): + def __init__(self, filename=None, mode=None, compresslevel=9, fileobj=None): gzip.GzipFile.__init__(self, filename, mode, compresslevel, fileobj) ### Context manager (stolen from Python 2.7)### @@ -34,29 +33,28 @@ class GzipFile(gzip.GzipFile): def open(filename, mode="rb", compresslevel=9): - if hasattr(gzip.GzipFile, '__enter__'): + if hasattr(gzip.GzipFile, "__enter__"): return gzip.open(filename, mode, compresslevel) else: return GzipFile(filename, mode, compresslevel) -def open_fileobj(fileobj, mode='rb', compresslevel=9): - if hasattr(gzip.GzipFile, '__enter__'): +def open_fileobj(fileobj, mode="rb", compresslevel=9): + if hasattr(gzip.GzipFile, "__enter__"): return gzip.GzipFile( - filename='', mode=mode, fileobj=fileobj, - compresslevel=compresslevel + filename="", mode=mode, fileobj=fileobj, compresslevel=compresslevel ) return GzipFile( - filename='', mode=mode, fileobj=fileobj, compresslevel=compresslevel + filename="", mode=mode, fileobj=fileobj, compresslevel=compresslevel ) def compress(data, compresslevel=9): - ''' + """ Returns the data compressed at gzip level compression. - ''' + """ buf = BytesIO() - with open_fileobj(buf, 'wb', compresslevel) as ogz: + with open_fileobj(buf, "wb", compresslevel) as ogz: if six.PY3 and not isinstance(data, bytes): data = data.encode(__salt_system_encoding__) ogz.write(data) @@ -66,13 +64,13 @@ def compress(data, compresslevel=9): def uncompress(data): buf = BytesIO(data) - with open_fileobj(buf, 'rb') as igz: + with open_fileobj(buf, "rb") as igz: unc = igz.read() return unc def compress_file(fh_, compresslevel=9, chunk_size=1048576): - ''' + """ Generator that reads chunk_size bytes at a time from a file/filehandle and yields the compressed result of each read. @@ -81,22 +79,22 @@ def compress_file(fh_, compresslevel=9, chunk_size=1048576): to form a compressed file. This function is designed to break up a file into compressed chunks for transport and decompression/reassembly on a remote host. - ''' + """ try: bytes_read = int(chunk_size) if bytes_read != chunk_size: raise ValueError except ValueError: - raise ValueError('chunk_size must be an integer') + raise ValueError("chunk_size must be an integer") try: while bytes_read == chunk_size: buf = BytesIO() - with open_fileobj(buf, 'wb', compresslevel) as ogz: + with open_fileobj(buf, "wb", compresslevel) as ogz: try: bytes_read = ogz.write(fh_.read(chunk_size)) except AttributeError: # Open the file and re-attempt the read - fh_ = salt.utils.files.fopen(fh_, 'rb') + fh_ = salt.utils.files.fopen(fh_, "rb") bytes_read = ogz.write(fh_.read(chunk_size)) yield buf.getvalue() finally: diff --git a/salt/utils/hashutils.py b/salt/utils/hashutils.py index fedb1710015..1df610ec442 100644 --- a/salt/utils/hashutils.py +++ b/salt/utils/hashutils.py @@ -1,80 +1,79 @@ # encoding: utf-8 -''' +""" A collection of hashing and encoding utils. -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import base64 import hashlib import hmac -import random import os +import random -# Import Salt libs -from salt.ext import six import salt.utils.files import salt.utils.platform import salt.utils.stringutils +# Import Salt libs +from salt.ext import six from salt.utils.decorators.jinja import jinja_filter -@jinja_filter('base64_encode') +@jinja_filter("base64_encode") def base64_b64encode(instr): - ''' + """ Encode a string as base64 using the "modern" Python interface. Among other possible differences, the "modern" encoder does not include newline ('\\n') characters in the encoded output. - ''' + """ return salt.utils.stringutils.to_unicode( base64.b64encode(salt.utils.stringutils.to_bytes(instr)), - encoding='utf8' if salt.utils.platform.is_windows() else None + encoding="utf8" if salt.utils.platform.is_windows() else None, ) -@jinja_filter('base64_decode') +@jinja_filter("base64_decode") def base64_b64decode(instr): - ''' + """ Decode a base64-encoded string using the "modern" Python interface. - ''' + """ decoded = base64.b64decode(salt.utils.stringutils.to_bytes(instr)) try: return salt.utils.stringutils.to_unicode( - decoded, - encoding='utf8' if salt.utils.platform.is_windows() else None + decoded, encoding="utf8" if salt.utils.platform.is_windows() else None ) except UnicodeDecodeError: return decoded def base64_encodestring(instr): - ''' + """ Encode a byte-like object as base64 using the "modern" Python interface. Among other possible differences, the "modern" encoder includes a newline ('\\n') character after every 76 characters and always at the end of the encoded string. - ''' + """ # Handles PY2 if six.PY2: return salt.utils.stringutils.to_unicode( base64.encodestring(salt.utils.stringutils.to_bytes(instr)), - encoding='utf8' if salt.utils.platform.is_windows() else None + encoding="utf8" if salt.utils.platform.is_windows() else None, ) # Handles PY3 return salt.utils.stringutils.to_unicode( base64.encodebytes(salt.utils.stringutils.to_bytes(instr)), - encoding='utf8' if salt.utils.platform.is_windows() else None + encoding="utf8" if salt.utils.platform.is_windows() else None, ) def base64_decodestring(instr): - ''' + """ Decode a base64-encoded byte-like object using the "modern" Python interface. - ''' + """ bvalue = salt.utils.stringutils.to_bytes(instr) if six.PY3: @@ -86,60 +85,59 @@ def base64_decodestring(instr): try: return salt.utils.stringutils.to_unicode( - decoded, - encoding='utf8' if salt.utils.platform.is_windows() else None + decoded, encoding="utf8" if salt.utils.platform.is_windows() else None ) except UnicodeDecodeError: return decoded -@jinja_filter('md5') +@jinja_filter("md5") def md5_digest(instr): - ''' + """ Generate an md5 hash of a given string. - ''' + """ return salt.utils.stringutils.to_unicode( hashlib.md5(salt.utils.stringutils.to_bytes(instr)).hexdigest() ) -@jinja_filter('sha1') +@jinja_filter("sha1") def sha1_digest(instr): - ''' + """ Generate an sha1 hash of a given string. - ''' + """ if six.PY3: b = salt.utils.stringutils.to_bytes(instr) return hashlib.sha1(b).hexdigest() return hashlib.sha1(instr).hexdigest() -@jinja_filter('sha256') +@jinja_filter("sha256") def sha256_digest(instr): - ''' + """ Generate a sha256 hash of a given string. - ''' + """ return salt.utils.stringutils.to_unicode( hashlib.sha256(salt.utils.stringutils.to_bytes(instr)).hexdigest() ) -@jinja_filter('sha512') +@jinja_filter("sha512") def sha512_digest(instr): - ''' + """ Generate a sha512 hash of a given string - ''' + """ return salt.utils.stringutils.to_unicode( hashlib.sha512(salt.utils.stringutils.to_bytes(instr)).hexdigest() ) -@jinja_filter('hmac') +@jinja_filter("hmac") def hmac_signature(string, shared_secret, challenge_hmac): - ''' + """ Verify a challenging hmac signature against a string / shared-secret Returns a boolean if the verification succeeded or failed. - ''' + """ msg = salt.utils.stringutils.to_bytes(string) key = salt.utils.stringutils.to_bytes(shared_secret) challenge = salt.utils.stringutils.to_bytes(challenge_hmac) @@ -148,32 +146,36 @@ def hmac_signature(string, shared_secret, challenge_hmac): return valid_hmac == challenge -@jinja_filter('hmac_compute') +@jinja_filter("hmac_compute") def hmac_compute(string, shared_secret): - ''' + """ Create an hmac digest. - ''' + """ msg = salt.utils.stringutils.to_bytes(string) key = salt.utils.stringutils.to_bytes(shared_secret) hmac_hash = hmac.new(key, msg, hashlib.sha256).hexdigest() return hmac_hash -@jinja_filter('rand_str') -@jinja_filter('random_hash') +@jinja_filter("rand_str") +@jinja_filter("random_hash") def random_hash(size=9999999999, hash_type=None): - ''' + """ Return a hash of a randomized data from random.SystemRandom() - ''' + """ if not hash_type: - hash_type = 'md5' + hash_type = "md5" hasher = getattr(hashlib, hash_type) - return hasher(salt.utils.stringutils.to_bytes(six.text_type(random.SystemRandom().randint(0, size)))).hexdigest() + return hasher( + salt.utils.stringutils.to_bytes( + six.text_type(random.SystemRandom().randint(0, size)) + ) + ).hexdigest() -@jinja_filter('file_hashsum') -def get_hash(path, form='sha256', chunk_size=65536): - ''' +@jinja_filter("file_hashsum") +def get_hash(path, form="sha256", chunk_size=65536): + """ Get the hash sum of a file This is better than ``get_sum`` for the following reasons: @@ -181,50 +183,50 @@ def get_hash(path, form='sha256', chunk_size=65536): - It does not return a string on error. The returned value of ``get_sum`` cannot really be trusted since it is vulnerable to collisions: ``get_sum(..., 'xyz') == 'Hash xyz not supported'`` - ''' + """ hash_type = hasattr(hashlib, form) and getattr(hashlib, form) or None if hash_type is None: - raise ValueError('Invalid hash type: {0}'.format(form)) + raise ValueError("Invalid hash type: {0}".format(form)) - with salt.utils.files.fopen(path, 'rb') as ifile: + with salt.utils.files.fopen(path, "rb") as ifile: hash_obj = hash_type() # read the file in in chunks, not the entire file - for chunk in iter(lambda: ifile.read(chunk_size), b''): + for chunk in iter(lambda: ifile.read(chunk_size), b""): hash_obj.update(chunk) return hash_obj.hexdigest() class DigestCollector(object): - ''' + """ Class to collect digest of the file tree. - ''' + """ - def __init__(self, form='sha256', buff=0x10000): - ''' + def __init__(self, form="sha256", buff=0x10000): + """ Constructor of the class. :param form: - ''' + """ self.__digest = hasattr(hashlib, form) and getattr(hashlib, form)() or None if self.__digest is None: - raise ValueError('Invalid hash type: {0}'.format(form)) + raise ValueError("Invalid hash type: {0}".format(form)) self.__buff = buff def add(self, path): - ''' + """ Update digest with the file content by path. :param path: :return: - ''' - with salt.utils.files.fopen(path, 'rb') as ifile: - for chunk in iter(lambda: ifile.read(self.__buff), b''): + """ + with salt.utils.files.fopen(path, "rb") as ifile: + for chunk in iter(lambda: ifile.read(self.__buff), b""): self.__digest.update(chunk) def digest(self): - ''' + """ Get digest. :return: - ''' + """ return salt.utils.stringutils.to_str(self.__digest.hexdigest() + os.linesep) diff --git a/salt/utils/http.py b/salt/utils/http.py index dee05636795..c5b38bd198a 100644 --- a/salt/utils/http.py +++ b/salt/utils/http.py @@ -1,45 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" Utils for making various web calls. Primarily designed for REST, SOAP, webhooks and the like, but also useful for basic HTTP testing. .. versionadded:: 2015.5.0 -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import cgi +import gzip +import io import logging import os import pprint -import socket -import io -import zlib -import gzip import re - +import socket import ssl -try: - from ssl import CertificateError # pylint: disable=E0611 - from ssl import match_hostname # pylint: disable=E0611 - HAS_MATCHHOSTNAME = True -except ImportError: - # pylint: disable=no-name-in-module - try: - from backports.ssl_match_hostname import CertificateError - from backports.ssl_match_hostname import match_hostname - HAS_MATCHHOSTNAME = True - except ImportError: - try: - from salt.ext.ssl_match_hostname import CertificateError - from salt.ext.ssl_match_hostname import match_hostname - HAS_MATCHHOSTNAME = True - except ImportError: - HAS_MATCHHOSTNAME = False - # pylint: enable=no-name-in-module +import zlib # Import salt libs import salt.config + +# pylint: disable=import-error,no-name-in-module +import salt.ext.six.moves.http_client +import salt.ext.six.moves.http_cookiejar +import salt.ext.six.moves.urllib.request as urllib_request + +# Don't need a try/except block, since Salt depends on tornado +import salt.ext.tornado.httputil +import salt.ext.tornado.simple_httpclient import salt.loader import salt.syspaths import salt.utils.args @@ -50,67 +41,90 @@ import salt.utils.msgpack import salt.utils.network import salt.utils.platform import salt.utils.stringutils +import salt.utils.xmlutil as xml import salt.utils.yaml import salt.version -import salt.utils.xmlutil as xml from salt._compat import ElementTree as ET -from salt.template import compile_template -from salt.utils.decorators.jinja import jinja_filter # Import 3rd party libs from salt.ext import six -# pylint: disable=import-error,no-name-in-module -import salt.ext.six.moves.http_client -import salt.ext.six.moves.http_cookiejar -import salt.ext.six.moves.urllib.request as urllib_request 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, urlparse +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 + +try: + from ssl import CertificateError # pylint: disable=E0611 + from ssl import match_hostname # pylint: disable=E0611 + + HAS_MATCHHOSTNAME = True +except ImportError: + # pylint: disable=no-name-in-module + try: + from backports.ssl_match_hostname import CertificateError + from backports.ssl_match_hostname import match_hostname + + HAS_MATCHHOSTNAME = True + except ImportError: + try: + from salt.ext.ssl_match_hostname import CertificateError + from salt.ext.ssl_match_hostname import match_hostname + + HAS_MATCHHOSTNAME = True + except ImportError: + HAS_MATCHHOSTNAME = False + # pylint: enable=no-name-in-module + + # pylint: enable=import-error,no-name-in-module -# Don't need a try/except block, since Salt depends on tornado -import salt.ext.tornado.httputil -import salt.ext.tornado.simple_httpclient -from salt.ext.tornado.httpclient import HTTPClient try: import salt.ext.tornado.curl_httpclient + HAS_CURL_HTTPCLIENT = True except ImportError: HAS_CURL_HTTPCLIENT = False try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False try: import certifi + HAS_CERTIFI = True except ImportError: HAS_CERTIFI = False log = logging.getLogger(__name__) -USERAGENT = 'Salt/{0}'.format(salt.version.__version__) +USERAGENT = "Salt/{0}".format(salt.version.__version__) def __decompressContent(coding, pgctnt): - ''' + """ Decompress returned HTTP content depending on the specified encoding. Currently supports identity/none, deflate, and gzip, which should cover 99%+ of the content on the internet. - ''' + """ if not pgctnt: return pgctnt - log.trace("Decompressing %s byte content with compression type: %s", len(pgctnt), coding) + log.trace( + "Decompressing %s byte content with compression type: %s", len(pgctnt), coding + ) - if coding == 'deflate': + if coding == "deflate": pgctnt = zlib.decompress(pgctnt, -zlib.MAX_WBITS) - elif coding == 'gzip': + elif coding == "gzip": buf = io.BytesIO(pgctnt) f = gzip.GzipFile(fileobj=buf) pgctnt = f.read() @@ -126,88 +140,95 @@ def __decompressContent(coding, pgctnt): return pgctnt -@jinja_filter('http_query') -def query(url, - method='GET', - params=None, - data=None, - data_file=None, - header_dict=None, - header_list=None, - header_file=None, - username=None, - password=None, - auth=None, - decode=False, - decode_type='auto', - status=False, - headers=False, - text=False, - cookies=None, - cookie_jar=None, - cookie_format='lwp', - persist_session=False, - session_cookie_jar=None, - data_render=False, - data_renderer=None, - header_render=False, - header_renderer=None, - template_dict=None, - test=False, - test_url=None, - node='minion', - port=80, - opts=None, - backend=None, - ca_bundle=None, - verify_ssl=None, - cert=None, - text_out=None, - headers_out=None, - decode_out=None, - stream=False, - streaming_callback=None, - header_callback=None, - handle=False, - agent=USERAGENT, - hide_fields=None, - raise_error=True, - formdata=False, - formdata_fieldname=None, - formdata_filename=None, - **kwargs): - ''' +@jinja_filter("http_query") +def query( + url, + method="GET", + params=None, + data=None, + data_file=None, + header_dict=None, + header_list=None, + header_file=None, + username=None, + password=None, + auth=None, + decode=False, + decode_type="auto", + status=False, + headers=False, + text=False, + cookies=None, + cookie_jar=None, + cookie_format="lwp", + persist_session=False, + session_cookie_jar=None, + data_render=False, + data_renderer=None, + header_render=False, + header_renderer=None, + template_dict=None, + test=False, + test_url=None, + node="minion", + port=80, + opts=None, + backend=None, + ca_bundle=None, + verify_ssl=None, + cert=None, + text_out=None, + headers_out=None, + decode_out=None, + stream=False, + streaming_callback=None, + header_callback=None, + handle=False, + agent=USERAGENT, + hide_fields=None, + raise_error=True, + formdata=False, + formdata_fieldname=None, + formdata_filename=None, + **kwargs +): + """ Query a resource, and decode the return data - ''' + """ ret = {} if opts is None: - if node == 'master': + if node == "master": opts = salt.config.master_config( - os.path.join(salt.syspaths.CONFIG_DIR, 'master') + os.path.join(salt.syspaths.CONFIG_DIR, "master") ) - elif node == 'minion': + elif node == "minion": opts = salt.config.minion_config( - os.path.join(salt.syspaths.CONFIG_DIR, 'minion') + os.path.join(salt.syspaths.CONFIG_DIR, "minion") ) else: opts = {} if not backend: - backend = opts.get('backend', 'tornado') + backend = opts.get("backend", "tornado") - match = re.match(r'https?://((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)($|/)', url) + match = re.match( + r"https?://((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)($|/)", + url, + ) if not match: salt.utils.network.refresh_dns() - if backend == 'requests': + if backend == "requests": if HAS_REQUESTS is False: - ret['error'] = ('http.query has been set to use requests, but the ' - 'requests library does not seem to be installed') - log.error(ret['error']) + ret["error"] = ( + "http.query has been set to use requests, but the " + "requests library does not seem to be installed" + ) + log.error(ret["error"]) return ret else: - requests_log = logging.getLogger('requests') + requests_log = logging.getLogger("requests") requests_log.setLevel(logging.WARNING) # Some libraries don't support separation of url and GET parameters @@ -218,23 +239,21 @@ def query(url, ca_bundle = get_ca_bundle(opts) if verify_ssl is None: - verify_ssl = opts.get('verify_ssl', True) + verify_ssl = opts.get("verify_ssl", True) if cert is None: - cert = opts.get('cert', None) + cert = opts.get("cert", None) if data_file is not None: - data = _render( - data_file, data_render, data_renderer, template_dict, opts - ) + data = _render(data_file, data_render, data_renderer, template_dict, opts) # Make sure no secret fields show up in logs log_url = sanitize_url(url_full, hide_fields) - log.debug('Requesting URL %s using %s method', log_url, method) + log.debug("Requesting URL %s using %s method", log_url, method) log.debug("Using backend: %s", backend) - if method == 'POST' and log.isEnabledFor(logging.TRACE): + if method == "POST" and log.isEnabledFor(logging.TRACE): # Make sure no secret fields show up in logs if isinstance(data, dict): log_data = data.copy() @@ -242,10 +261,10 @@ def query(url, for item in data: for field in hide_fields: if item == field: - log_data[item] = 'XXXXXXXXXX' - log.trace('Request POST Data: %s', pprint.pformat(log_data)) + log_data[item] = "XXXXXXXXXX" + log.trace("Request POST Data: %s", pprint.pformat(log_data)) else: - log.trace('Request POST Data: %s', pprint.pformat(data)) + log.trace("Request POST Data: %s", pprint.pformat(data)) if header_file is not None: header_tpl = _render( @@ -263,9 +282,13 @@ def query(url, header_list = [] if cookie_jar is None: - cookie_jar = os.path.join(opts.get('cachedir', salt.syspaths.CACHE_DIR), 'cookies.txt') + cookie_jar = os.path.join( + opts.get("cachedir", salt.syspaths.CACHE_DIR), "cookies.txt" + ) if session_cookie_jar is None: - session_cookie_jar = os.path.join(opts.get('cachedir', salt.syspaths.CACHE_DIR), 'cookies.session.p') + session_cookie_jar = os.path.join( + opts.get("cachedir", salt.syspaths.CACHE_DIR), "cookies.session.p" + ) if persist_session is True and salt.utils.msgpack.HAS_MSGPACK: # TODO: This is hackish; it will overwrite the session cookie jar with @@ -273,16 +296,16 @@ def query(url, # proper cookie jar. Unfortunately, since session cookies do not # contain expirations, they can't be stored in a proper cookie jar. if os.path.isfile(session_cookie_jar): - with salt.utils.files.fopen(session_cookie_jar, 'rb') as fh_: + with salt.utils.files.fopen(session_cookie_jar, "rb") as fh_: session_cookies = salt.utils.msgpack.load(fh_) if isinstance(session_cookies, dict): header_dict.update(session_cookies) else: - with salt.utils.files.fopen(session_cookie_jar, 'wb') as fh_: - salt.utils.msgpack.dump('', fh_) + with salt.utils.files.fopen(session_cookie_jar, "wb") as fh_: + salt.utils.msgpack.dump("", fh_) for header in header_list: - comps = header.split(':') + comps = header.split(":") if len(comps) < 2: continue header_dict[comps[0].strip()] = comps[1].strip() @@ -292,25 +315,27 @@ def query(url, auth = (username, password) if agent == USERAGENT: - agent = '{0} http.query()'.format(agent) - header_dict['User-agent'] = agent + agent = "{0} http.query()".format(agent) + header_dict["User-agent"] = agent - if backend == 'requests': + if backend == "requests": sess = requests.Session() sess.auth = auth sess.headers.update(header_dict) - log.trace('Request Headers: %s', sess.headers) + log.trace("Request Headers: %s", sess.headers) sess_cookies = sess.cookies sess.verify = verify_ssl - elif backend == 'urllib2': + elif backend == "urllib2": sess_cookies = None else: # Tornado sess_cookies = None if cookies is not None: - if cookie_format == 'mozilla': - sess_cookies = salt.ext.six.moves.http_cookiejar.MozillaCookieJar(cookie_jar) + if cookie_format == "mozilla": + sess_cookies = salt.ext.six.moves.http_cookiejar.MozillaCookieJar( + cookie_jar + ) else: sess_cookies = salt.ext.six.moves.http_cookiejar.LWPCookieJar(cookie_jar) if not os.path.isfile(cookie_jar): @@ -322,33 +347,36 @@ def query(url, return {} else: url = test_url - ret['test'] = True + ret["test"] = True - if backend == 'requests': + if backend == "requests": req_kwargs = {} if stream is True: - if requests.__version__[0] == '0': + if requests.__version__[0] == "0": # 'stream' was called 'prefetch' before 1.0, with flipped meaning - req_kwargs['prefetch'] = False + req_kwargs["prefetch"] = False else: - req_kwargs['stream'] = True + req_kwargs["stream"] = True # Client-side cert handling if cert is not None: if isinstance(cert, six.string_types): if os.path.exists(cert): - req_kwargs['cert'] = cert + req_kwargs["cert"] = cert elif isinstance(cert, list): if os.path.exists(cert[0]) and os.path.exists(cert[1]): - req_kwargs['cert'] = cert + req_kwargs["cert"] = cert else: - log.error('The client-side certificate path that' - ' was passed is not valid: %s', cert) + log.error( + "The client-side certificate path that" + " was passed is not valid: %s", + cert, + ) if formdata: if not formdata_fieldname: - ret['error'] = ('formdata_fieldname is required when formdata=True') - log.error(ret['error']) + ret["error"] = "formdata_fieldname is required when formdata=True" + log.error(ret["error"]) return ret result = sess.request( method, @@ -358,27 +386,26 @@ def query(url, **req_kwargs ) else: - result = sess.request( - method, url, params=params, data=data, **req_kwargs - ) + result = sess.request(method, url, params=params, data=data, **req_kwargs) result.raise_for_status() if stream is True: # fake a HTTP response header - header_callback('HTTP/1.0 {0} MESSAGE'.format(result.status_code)) + header_callback("HTTP/1.0 {0} MESSAGE".format(result.status_code)) # fake streaming the content streaming_callback(result.content) return { - 'handle': result, + "handle": result, } if handle is True: return { - 'handle': result, - 'body': result.content, + "handle": result, + "body": result.content, } - log.debug('Final URL location of Response: %s', - sanitize_url(result.url, hide_fields)) + log.debug( + "Final URL location of Response: %s", sanitize_url(result.url, hide_fields) + ) result_status_code = result.status_code result_headers = result.headers @@ -386,44 +413,45 @@ def query(url, result_cookies = result.cookies body = result.content if not isinstance(body, six.text_type): - body = body.decode(result.encoding or 'utf-8') - ret['body'] = body - elif backend == 'urllib2': + body = body.decode(result.encoding or "utf-8") + ret["body"] = body + elif backend == "urllib2": request = urllib_request.Request(url_full, data) handlers = [ urllib_request.HTTPHandler, - urllib_request.HTTPCookieProcessor(sess_cookies) + urllib_request.HTTPCookieProcessor(sess_cookies), ] - if url.startswith('https'): + if url.startswith("https"): hostname = request.get_host() handlers[0] = urllib_request.HTTPSHandler(1) if not HAS_MATCHHOSTNAME: - log.warning('match_hostname() not available, SSL hostname checking ' - 'not available. THIS CONNECTION MAY NOT BE SECURE!') + log.warning( + "match_hostname() not available, SSL hostname checking " + "not available. THIS CONNECTION MAY NOT BE SECURE!" + ) elif verify_ssl is False: - log.warning('SSL certificate verification has been explicitly ' - 'disabled. THIS CONNECTION MAY NOT BE SECURE!') + log.warning( + "SSL certificate verification has been explicitly " + "disabled. THIS CONNECTION MAY NOT BE SECURE!" + ) else: - if ':' in hostname: - hostname, port = hostname.split(':') + if ":" in hostname: + hostname, port = hostname.split(":") else: port = 443 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((hostname, int(port))) sockwrap = ssl.wrap_socket( - sock, - ca_certs=ca_bundle, - cert_reqs=ssl.CERT_REQUIRED + sock, ca_certs=ca_bundle, cert_reqs=ssl.CERT_REQUIRED ) try: match_hostname(sockwrap.getpeercert(), hostname) except CertificateError as exc: - ret['error'] = ( - 'The certificate was invalid. ' - 'Error returned was: %s', - pprint.pformat(exc) - ) + ret["error"] = ( + "The certificate was invalid. Error returned was: %s", + pprint.pformat(exc), + ) return ret # Client-side cert handling @@ -431,28 +459,35 @@ def query(url, cert_chain = None if isinstance(cert, six.string_types): if os.path.exists(cert): - cert_chain = (cert) + cert_chain = cert elif isinstance(cert, list): if os.path.exists(cert[0]) and os.path.exists(cert[1]): cert_chain = cert else: - log.error('The client-side certificate path that was ' - 'passed is not valid: %s', cert) + log.error( + "The client-side certificate path that was " + "passed is not valid: %s", + cert, + ) return - if hasattr(ssl, 'SSLContext'): + if hasattr(ssl, "SSLContext"): # Python >= 2.7.9 context = ssl.SSLContext.load_cert_chain(*cert_chain) - handlers.append(urllib_request.HTTPSHandler(context=context)) # pylint: disable=E1123 + handlers.append( + urllib_request.HTTPSHandler(context=context) + ) # pylint: disable=E1123 else: # Python < 2.7.9 cert_kwargs = { - 'host': request.get_host(), - 'port': port, - 'cert_file': cert_chain[0] + "host": request.get_host(), + "port": port, + "cert_file": cert_chain[0], } if len(cert_chain) > 1: - cert_kwargs['key_file'] = cert_chain[1] - handlers[0] = salt.ext.six.moves.http_client.HTTPSConnection(**cert_kwargs) + cert_kwargs["key_file"] = cert_chain[1] + handlers[0] = salt.ext.six.moves.http_client.HTTPSConnection( + **cert_kwargs + ) opener = urllib_request.build_opener(*handlers) for header in header_dict: @@ -461,25 +496,29 @@ def query(url, try: result = opener.open(request) except URLError as exc: - return {'Error': six.text_type(exc)} + return {"Error": six.text_type(exc)} if stream is True or handle is True: return { - 'handle': result, - 'body': result.content, + "handle": result, + "body": result.content, } result_status_code = result.code result_headers = dict(result.info()) result_text = result.read() - if 'Content-Type' in result_headers: - res_content_type, res_params = cgi.parse_header(result_headers['Content-Type']) - if res_content_type.startswith('text/') and \ - 'charset' in res_params and \ - not isinstance(result_text, six.text_type): - result_text = result_text.decode(res_params['charset']) + if "Content-Type" in result_headers: + res_content_type, res_params = cgi.parse_header( + result_headers["Content-Type"] + ) + if ( + res_content_type.startswith("text/") + and "charset" in res_params + and not isinstance(result_text, six.text_type) + ): + result_text = result_text.decode(res_params["charset"]) if six.PY3 and isinstance(result_text, bytes): - result_text = result_text.decode('utf-8') - ret['body'] = result_text + result_text = result_text.decode("utf-8") + ret["body"] = result_text else: # Tornado req_kwargs = {} @@ -488,41 +527,52 @@ def query(url, if cert is not None: if isinstance(cert, six.string_types): if os.path.exists(cert): - req_kwargs['client_cert'] = cert + req_kwargs["client_cert"] = cert elif isinstance(cert, list): if os.path.exists(cert[0]) and os.path.exists(cert[1]): - req_kwargs['client_cert'] = cert[0] - req_kwargs['client_key'] = cert[1] + req_kwargs["client_cert"] = cert[0] + req_kwargs["client_key"] = cert[1] else: - log.error('The client-side certificate path that ' - 'was passed is not valid: %s', cert) + log.error( + "The client-side certificate path that " + "was passed is not valid: %s", + cert, + ) if isinstance(data, dict): data = _urlencode(data) if verify_ssl: - req_kwargs['ca_certs'] = ca_bundle + req_kwargs["ca_certs"] = ca_bundle - max_body = opts.get('http_max_body', salt.config.DEFAULT_MINION_OPTS['http_max_body']) - connect_timeout = opts.get('http_connect_timeout', salt.config.DEFAULT_MINION_OPTS['http_connect_timeout']) - timeout = opts.get('http_request_timeout', salt.config.DEFAULT_MINION_OPTS['http_request_timeout']) + max_body = opts.get( + "http_max_body", salt.config.DEFAULT_MINION_OPTS["http_max_body"] + ) + connect_timeout = opts.get( + "http_connect_timeout", + salt.config.DEFAULT_MINION_OPTS["http_connect_timeout"], + ) + timeout = opts.get( + "http_request_timeout", + salt.config.DEFAULT_MINION_OPTS["http_request_timeout"], + ) client_argspec = None - proxy_host = opts.get('proxy_host', None) + proxy_host = opts.get("proxy_host", None) if proxy_host: # tornado requires a str for proxy_host, cannot be a unicode str in py2 proxy_host = salt.utils.stringutils.to_str(proxy_host) - proxy_port = opts.get('proxy_port', None) - proxy_username = opts.get('proxy_username', None) + proxy_port = opts.get("proxy_port", None) + proxy_username = opts.get("proxy_username", None) if proxy_username: # tornado requires a str, cannot be unicode str in py2 proxy_username = salt.utils.stringutils.to_str(proxy_username) - proxy_password = opts.get('proxy_password', None) + proxy_password = opts.get("proxy_password", None) if proxy_password: # tornado requires a str, cannot be unicode str in py2 proxy_password = salt.utils.stringutils.to_str(proxy_password) - no_proxy = opts.get('no_proxy', []) + no_proxy = opts.get("no_proxy", []) # 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 @@ -533,39 +583,47 @@ def query(url, # We want to use curl_http if we have a proxy defined if proxy_host and proxy_port: if HAS_CURL_HTTPCLIENT is False: - ret['error'] = ('proxy_host and proxy_port has been set. This requires pycurl and tornado, ' - 'but the libraries does not seem to be installed') - log.error(ret['error']) + ret["error"] = ( + "proxy_host and proxy_port has been set. This requires pycurl and tornado, " + "but the libraries does not seem to be installed" + ) + log.error(ret["error"]) return ret - salt.ext.tornado.httpclient.AsyncHTTPClient.configure('tornado.curl_httpclient.CurlAsyncHTTPClient') + salt.ext.tornado.httpclient.AsyncHTTPClient.configure( + "tornado.curl_httpclient.CurlAsyncHTTPClient" + ) client_argspec = salt.utils.args.get_function_argspec( - salt.ext.tornado.curl_httpclient.CurlAsyncHTTPClient.initialize) + salt.ext.tornado.curl_httpclient.CurlAsyncHTTPClient.initialize + ) else: client_argspec = salt.utils.args.get_function_argspec( - salt.ext.tornado.simple_httpclient.SimpleAsyncHTTPClient.initialize) + salt.ext.tornado.simple_httpclient.SimpleAsyncHTTPClient.initialize + ) - supports_max_body_size = 'max_body_size' in client_argspec.args + supports_max_body_size = "max_body_size" in client_argspec.args - req_kwargs.update({ - 'method': method, - 'headers': header_dict, - 'auth_username': username, - 'auth_password': password, - 'body': data, - 'validate_cert': verify_ssl, - 'allow_nonstandard_methods': True, - 'streaming_callback': streaming_callback, - 'header_callback': header_callback, - 'connect_timeout': connect_timeout, - 'request_timeout': timeout, - 'proxy_host': proxy_host, - 'proxy_port': proxy_port, - 'proxy_username': proxy_username, - 'proxy_password': proxy_password, - 'raise_error': raise_error, - 'decompress_response': False, - }) + req_kwargs.update( + { + "method": method, + "headers": header_dict, + "auth_username": username, + "auth_password": password, + "body": data, + "validate_cert": verify_ssl, + "allow_nonstandard_methods": True, + "streaming_callback": streaming_callback, + "header_callback": header_callback, + "connect_timeout": connect_timeout, + "request_timeout": timeout, + "proxy_host": proxy_host, + "proxy_port": proxy_port, + "proxy_username": proxy_username, + "proxy_password": proxy_password, + "raise_error": raise_error, + "decompress_response": False, + } + ) # Unicode types will cause a TypeError when Tornado's curl HTTPClient # invokes setopt. Therefore, make sure all arguments we pass which @@ -573,40 +631,46 @@ def query(url, req_kwargs = salt.utils.data.decode(req_kwargs, to_str=True) try: - download_client = HTTPClient(max_body_size=max_body) \ - if supports_max_body_size \ + download_client = ( + HTTPClient(max_body_size=max_body) + if supports_max_body_size else HTTPClient() + ) result = download_client.fetch(url_full, **req_kwargs) except salt.ext.tornado.httpclient.HTTPError as exc: - ret['status'] = exc.code - ret['error'] = six.text_type(exc) + ret["status"] = exc.code + ret["error"] = six.text_type(exc) return ret except socket.gaierror as exc: if status is True: - ret['status'] = 0 - ret['error'] = six.text_type(exc) + ret["status"] = 0 + ret["error"] = six.text_type(exc) return ret if stream is True or handle is True: return { - 'handle': result, - 'body': result.body, + "handle": result, + "body": result.body, } result_status_code = result.code result_headers = result.headers result_text = result.body - if 'Content-Type' in result_headers: - res_content_type, res_params = cgi.parse_header(result_headers['Content-Type']) - if res_content_type.startswith('text/') and \ - 'charset' in res_params and \ - not isinstance(result_text, six.text_type): - result_text = result_text.decode(res_params['charset']) + if "Content-Type" in result_headers: + res_content_type, res_params = cgi.parse_header( + result_headers["Content-Type"] + ) + if ( + res_content_type.startswith("text/") + and "charset" in res_params + and not isinstance(result_text, six.text_type) + ): + result_text = result_text.decode(res_params["charset"]) if six.PY3 and isinstance(result_text, bytes): - result_text = result_text.decode('utf-8') - ret['body'] = result_text - if 'Set-Cookie' in result_headers and cookies is not None: - result_cookies = parse_cookie_header(result_headers['Set-Cookie']) + result_text = result_text.decode("utf-8") + ret["body"] = result_text + if "Set-Cookie" in result_headers and cookies is not None: + result_cookies = parse_cookie_header(result_headers["Set-Cookie"]) for item in result_cookies: sess_cookies.set_cookie(item) else: @@ -615,33 +679,36 @@ def query(url, if isinstance(result_headers, list): result_headers_dict = {} for header in result_headers: - comps = header.split(':') - result_headers_dict[comps[0].strip()] = ':'.join(comps[1:]).strip() + comps = header.split(":") + result_headers_dict[comps[0].strip()] = ":".join(comps[1:]).strip() result_headers = result_headers_dict - log.debug('Response Status Code: %s', result_status_code) - log.trace('Response Headers: %s', result_headers) - log.trace('Response Cookies: %s', sess_cookies) + log.debug("Response Status Code: %s", result_status_code) + log.trace("Response Headers: %s", result_headers) + log.trace("Response Cookies: %s", sess_cookies) # log.trace("Content: %s", result_text) - coding = result_headers.get('Content-Encoding', "identity") + coding = result_headers.get("Content-Encoding", "identity") # Requests will always decompress the content, and working around that is annoying. - if backend != 'requests': + if backend != "requests": result_text = __decompressContent(coding, result_text) try: - log.trace('Response Text: %s', result_text) + log.trace("Response Text: %s", result_text) except UnicodeEncodeError as exc: - log.trace('Cannot Trace Log Response Text: %s. This may be due to ' - 'incompatibilities between requests and logging.', exc) + log.trace( + "Cannot Trace Log Response Text: %s. This may be due to " + "incompatibilities between requests and logging.", + exc, + ) if text_out is not None: - with salt.utils.files.fopen(text_out, 'w') as tof: + with salt.utils.files.fopen(text_out, "w") as tof: tof.write(result_text) if headers_out is not None and os.path.exists(headers_out): - with salt.utils.files.fopen(headers_out, 'w') as hof: + with salt.utils.files.fopen(headers_out, "w") as hof: hof.write(result_headers) if cookies is not None: @@ -649,106 +716,102 @@ def query(url, if persist_session is True and salt.utils.msgpack.HAS_MSGPACK: # TODO: See persist_session above - if 'set-cookie' in result_headers: - with salt.utils.files.fopen(session_cookie_jar, 'wb') as fh_: - session_cookies = result_headers.get('set-cookie', None) + if "set-cookie" in result_headers: + with salt.utils.files.fopen(session_cookie_jar, "wb") as fh_: + session_cookies = result_headers.get("set-cookie", None) if session_cookies is not None: - salt.utils.msgpack.dump({'Cookie': session_cookies}, fh_) + salt.utils.msgpack.dump({"Cookie": session_cookies}, fh_) else: - salt.utils.msgpack.dump('', fh_) + salt.utils.msgpack.dump("", fh_) if status is True: - ret['status'] = result_status_code + ret["status"] = result_status_code if headers is True: - ret['headers'] = result_headers + ret["headers"] = result_headers if decode is True: - if decode_type == 'auto': - content_type = result_headers.get( - 'content-type', 'application/json' - ) - if 'xml' in content_type: - decode_type = 'xml' - elif 'json' in content_type: - decode_type = 'json' - elif 'yaml' in content_type: - decode_type = 'yaml' + if decode_type == "auto": + content_type = result_headers.get("content-type", "application/json") + if "xml" in content_type: + decode_type = "xml" + elif "json" in content_type: + decode_type = "json" + elif "yaml" in content_type: + decode_type = "yaml" else: - decode_type = 'plain' + decode_type = "plain" - valid_decodes = ('json', 'xml', 'yaml', 'plain') + valid_decodes = ("json", "xml", "yaml", "plain") if decode_type not in valid_decodes: - ret['error'] = ( - 'Invalid decode_type specified. ' - 'Valid decode types are: {0}'.format( - pprint.pformat(valid_decodes) - ) + ret["error"] = ( + "Invalid decode_type specified. " + "Valid decode types are: {0}".format(pprint.pformat(valid_decodes)) ) - log.error(ret['error']) + log.error(ret["error"]) return ret - if decode_type == 'json': - ret['dict'] = salt.utils.json.loads(result_text) - elif decode_type == 'xml': - ret['dict'] = [] + if decode_type == "json": + ret["dict"] = salt.utils.json.loads(result_text) + elif decode_type == "xml": + ret["dict"] = [] items = ET.fromstring(result_text) for item in items: - ret['dict'].append(xml.to_dict(item)) - elif decode_type == 'yaml': - ret['dict'] = salt.utils.data.decode(salt.utils.yaml.safe_load(result_text)) + ret["dict"].append(xml.to_dict(item)) + elif decode_type == "yaml": + ret["dict"] = salt.utils.data.decode(salt.utils.yaml.safe_load(result_text)) else: text = True if decode_out: - with salt.utils.files.fopen(decode_out, 'w') as dof: + with salt.utils.files.fopen(decode_out, "w") as dof: dof.write(result_text) if text is True: - ret['text'] = result_text + ret["text"] = result_text return ret def get_ca_bundle(opts=None): - ''' + """ Return the location of the ca bundle file. See the following article: http://tinyurl.com/k7rx42a - ''' - if hasattr(get_ca_bundle, '__return_value__'): + """ + if hasattr(get_ca_bundle, "__return_value__"): return get_ca_bundle.__return_value__ if opts is None: opts = {} - opts_bundle = opts.get('ca_bundle', None) + opts_bundle = opts.get("ca_bundle", None) if opts_bundle is not None and os.path.exists(opts_bundle): return opts_bundle - file_roots = opts.get('file_roots', {'base': [salt.syspaths.SRV_ROOT_DIR]}) + file_roots = opts.get("file_roots", {"base": [salt.syspaths.SRV_ROOT_DIR]}) # Please do not change the order without good reason # Check Salt first - for salt_root in file_roots.get('base', []): - for path in ('cacert.pem', 'ca-bundle.crt'): + for salt_root in file_roots.get("base", []): + for path in ("cacert.pem", "ca-bundle.crt"): cert_path = os.path.join(salt_root, path) if os.path.exists(cert_path): return cert_path locations = ( # Debian has paths that often exist on other distros - '/etc/ssl/certs/ca-certificates.crt', + "/etc/ssl/certs/ca-certificates.crt", # RedHat is also very common - '/etc/pki/tls/certs/ca-bundle.crt', - '/etc/pki/tls/certs/ca-bundle.trust.crt', + "/etc/pki/tls/certs/ca-bundle.crt", + "/etc/pki/tls/certs/ca-bundle.trust.crt", # RedHat's link for Debian compatibility - '/etc/ssl/certs/ca-bundle.crt', + "/etc/ssl/certs/ca-bundle.crt", # SUSE has an unusual path - '/var/lib/ca-certificates/ca-bundle.pem', + "/var/lib/ca-certificates/ca-bundle.pem", # OpenBSD has an unusual path - '/etc/ssl/cert.pem', + "/etc/ssl/cert.pem", ) for path in locations: if os.path.exists(path): @@ -761,12 +824,9 @@ def get_ca_bundle(opts=None): def update_ca_bundle( - target=None, - source=None, - opts=None, - merge_files=None, - ): - ''' + target=None, source=None, opts=None, merge_files=None, +): + """ Attempt to update the CA bundle file from a URL If not specified, the local location on disk (``target``) will be @@ -783,7 +843,7 @@ def update_ca_bundle( A string or list of strings representing files to be appended to the end of the CA bundle file may also be passed through as ``merge_files``. - ''' + """ if opts is None: opts = {} @@ -791,97 +851,98 @@ def update_ca_bundle( target = get_ca_bundle(opts) if target is None: - log.error('Unable to detect location to write CA bundle to') + log.error("Unable to detect location to write CA bundle to") return if source is None: - source = opts.get('ca_bundle_url', 'http://curl.haxx.se/ca/cacert.pem') + source = opts.get("ca_bundle_url", "http://curl.haxx.se/ca/cacert.pem") - log.debug('Attempting to download %s to %s', source, target) - query( - source, - text=True, - decode=False, - headers=False, - status=False, - text_out=target - ) + log.debug("Attempting to download %s to %s", source, target) + query(source, text=True, decode=False, headers=False, status=False, text_out=target) if merge_files is not None: if isinstance(merge_files, six.string_types): merge_files = [merge_files] if not isinstance(merge_files, list): - log.error('A value was passed as merge_files which was not either ' - 'a string or a list') + log.error( + "A value was passed as merge_files which was not either " + "a string or a list" + ) return - merge_content = '' + merge_content = "" for cert_file in merge_files: if os.path.exists(cert_file): - log.debug( - 'Queueing up %s to be appended to %s', - cert_file, target - ) + log.debug("Queueing up %s to be appended to %s", cert_file, target) try: - with salt.utils.files.fopen(cert_file, 'r') as fcf: - merge_content = '\n'.join((merge_content, fcf.read())) + with salt.utils.files.fopen(cert_file, "r") as fcf: + merge_content = "\n".join((merge_content, fcf.read())) except IOError as exc: log.error( - 'Reading from %s caused the following error: %s', - cert_file, exc + "Reading from %s caused the following error: %s", cert_file, exc ) if merge_content: - log.debug('Appending merge_files to %s', target) + log.debug("Appending merge_files to %s", target) try: - with salt.utils.files.fopen(target, 'a') as tfp: - tfp.write('\n') + with salt.utils.files.fopen(target, "a") as tfp: + tfp.write("\n") tfp.write(merge_content) except IOError as exc: - log.error( - 'Writing to %s caused the following error: %s', - target, exc - ) + log.error("Writing to %s caused the following error: %s", target, exc) def _render(template, render, renderer, template_dict, opts): - ''' + """ Render a template - ''' + """ if render: if template_dict is None: template_dict = {} if not renderer: - renderer = opts.get('renderer', 'jinja|yaml') + renderer = opts.get("renderer", "jinja|yaml") rend = salt.loader.render(opts, {}) - blacklist = opts.get('renderer_blacklist') - whitelist = opts.get('renderer_whitelist') - ret = compile_template(template, rend, renderer, blacklist, whitelist, **template_dict) + blacklist = opts.get("renderer_blacklist") + whitelist = opts.get("renderer_whitelist") + ret = compile_template( + template, rend, renderer, blacklist, whitelist, **template_dict + ) if salt.utils.stringio.is_readable(ret): ret = ret.read() - if six.text_type(ret).startswith('#!') and not six.text_type(ret).startswith('#!/'): - ret = six.text_type(ret).split('\n', 1)[1] + if six.text_type(ret).startswith("#!") and not six.text_type(ret).startswith( + "#!/" + ): + ret = six.text_type(ret).split("\n", 1)[1] return ret - with salt.utils.files.fopen(template, 'r') as fh_: + with salt.utils.files.fopen(template, "r") as fh_: return fh_.read() def parse_cookie_header(header): - ''' + """ Parse the "Set-cookie" header, and return a list of cookies. This function is here because Tornado's HTTPClient doesn't handle cookies. - ''' - attribs = ('expires', 'path', 'domain', 'version', 'httponly', 'secure', 'comment', 'max-age') + """ + attribs = ( + "expires", + "path", + "domain", + "version", + "httponly", + "secure", + "comment", + "max-age", + ) # Split into cookie(s); handles headers with multiple cookies defined morsels = [] - for item in header.split(';'): + for item in header.split(";"): item = item.strip() - if ',' in item and 'expires' not in item: - for part in item.split(','): + if "," in item and "expires" not in item: + for part in item.split(","): morsels.append(part) else: morsels.append(item) @@ -891,13 +952,13 @@ def parse_cookie_header(header): cookie = {} value_set = False for morsel in morsels: - parts = morsel.split('=') + parts = morsel.split("=") if parts[0].lower() in attribs: if parts[0] in cookie: cookies.append(cookie) cookie = {} if len(parts) > 1: - cookie[parts[0]] = '='.join(parts[1:]) + cookie[parts[0]] = "=".join(parts[1:]) else: cookie[parts[0]] = True else: @@ -906,7 +967,7 @@ def parse_cookie_header(header): cookies.append(cookie) cookie = {} value_set = False - cookie[parts[0]] = '='.join(parts[1:]) + cookie[parts[0]] = "=".join(parts[1:]) value_set = True if cookie: @@ -915,20 +976,20 @@ def parse_cookie_header(header): # These arguments are required by cookielib.Cookie() reqd = ( - 'version', - 'port', - 'port_specified', - 'domain', - 'domain_specified', - 'domain_initial_dot', - 'path', - 'path_specified', - 'secure', - 'expires', - 'discard', - 'comment', - 'comment_url', - 'rest', + "version", + "port", + "port_specified", + "domain", + "domain_specified", + "domain_initial_dot", + "path", + "path_specified", + "secure", + "expires", + "discard", + "comment", + "comment_url", + "rest", ) ret = [] @@ -942,62 +1003,66 @@ def parse_cookie_header(header): value = cookie.pop(item) # cookielib.Cookie() requires an epoch - if 'expires' in cookie: - cookie['expires'] = salt.ext.six.moves.http_cookiejar.http2time(cookie['expires']) + if "expires" in cookie: + cookie["expires"] = salt.ext.six.moves.http_cookiejar.http2time( + cookie["expires"] + ) # Fill in missing required fields for req in reqd: if req not in cookie: - cookie[req] = '' - if cookie['version'] == '': - cookie['version'] = 0 - if cookie['rest'] == '': - cookie['rest'] = {} - if cookie['expires'] == '': - cookie['expires'] = 0 + cookie[req] = "" + if cookie["version"] == "": + cookie["version"] = 0 + if cookie["rest"] == "": + cookie["rest"] = {} + if cookie["expires"] == "": + cookie["expires"] = 0 - if 'httponly' in cookie: - del cookie['httponly'] - ret.append(salt.ext.six.moves.http_cookiejar.Cookie(name=name, value=value, **cookie)) + if "httponly" in cookie: + del cookie["httponly"] + ret.append( + salt.ext.six.moves.http_cookiejar.Cookie(name=name, value=value, **cookie) + ) return ret def sanitize_url(url, hide_fields): - ''' + """ Make sure no secret fields show up in logs - ''' + """ if isinstance(hide_fields, list): url_comps = splitquery(url) log_url = url_comps[0] if len(url_comps) > 1: - log_url += '?' + log_url += "?" for pair in url_comps[1:]: url_tmp = None for field in hide_fields: - comps_list = pair.split('&') + comps_list = pair.split("&") if url_tmp: - url_tmp = url_tmp.split('&') + url_tmp = url_tmp.split("&") url_tmp = _sanitize_url_components(url_tmp, field) else: url_tmp = _sanitize_url_components(comps_list, field) log_url += url_tmp - return log_url.rstrip('&') + return log_url.rstrip("&") else: return six.text_type(url) def _sanitize_url_components(comp_list, field): - ''' + """ Recursive function to sanitize each component of the url. - ''' + """ if len(comp_list) == 0: - return '' - elif comp_list[0].startswith('{0}='.format(field)): - ret = '{0}=XXXXXXXXXX&'.format(field) + return "" + elif comp_list[0].startswith("{0}=".format(field)): + ret = "{0}=XXXXXXXXXX&".format(field) comp_list.remove(comp_list[0]) return ret + _sanitize_url_components(comp_list, field) else: - ret = '{0}&'.format(comp_list[0]) + ret = "{0}&".format(comp_list[0]) comp_list.remove(comp_list[0]) return ret + _sanitize_url_components(comp_list, field) diff --git a/salt/utils/iam.py b/salt/utils/iam.py index f33989c488e..b8e0e2a4fd2 100644 --- a/salt/utils/iam.py +++ b/salt/utils/iam.py @@ -1,21 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Connection library for Amazon IAM :depends: requests -''' +""" from __future__ import absolute_import, unicode_literals # Import Python libs import logging -import time import pprint +import time + import salt.utils.data -from salt.ext.six.moves import range from salt.ext import six +from salt.ext.six.moves import range try: import requests + HAS_REQUESTS = True # pylint: disable=W0612 except ImportError: HAS_REQUESTS = False # pylint: disable=W0612 @@ -24,44 +26,38 @@ log = logging.getLogger(__name__) def _retry_get_url(url, num_retries=10, timeout=5): - ''' + """ Retry grabbing a URL. Based heavily on boto.utils.retry_url - ''' + """ for i in range(0, num_retries): try: - result = requests.get(url, timeout=timeout, proxies={'http': ''}) - if hasattr(result, 'text'): + result = requests.get(url, timeout=timeout, proxies={"http": ""}) + if hasattr(result, "text"): return result.text - elif hasattr(result, 'content'): + elif hasattr(result, "content"): return result.content else: - return '' + return "" except requests.exceptions.HTTPError as exc: - return '' + return "" except Exception as exc: # pylint: disable=broad-except pass - log.warning( - 'Caught exception reading from URL. Retry no. %s', i - ) + log.warning("Caught exception reading from URL. Retry no. %s", i) log.warning(pprint.pformat(exc)) time.sleep(2 ** i) - log.error( - 'Failed to read from URL for %s times. Giving up.', num_retries - ) - return '' + log.error("Failed to read from URL for %s times. Giving up.", num_retries) + return "" def _convert_key_to_str(key): - ''' + """ Stolen completely from boto.providers - ''' + """ # IMPORTANT: on PY2, the secret key must be str and not unicode to work # properly with hmac.new (see http://bugs.python.org/issue5285) # # pylint: disable=incompatible-py3-code,undefined-variable - return salt.utils.data.encode(key) \ - if six.PY2 and isinstance(key, unicode) \ - else key + return salt.utils.data.encode(key) if six.PY2 and isinstance(key, unicode) else key # pylint: enable=incompatible-py3-code,undefined-variable diff --git a/salt/utils/icinga2.py b/salt/utils/icinga2.py index b2f947802d5..ad83908dd9b 100644 --- a/salt/utils/icinga2.py +++ b/salt/utils/icinga2.py @@ -1,33 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Icinga2 Common Utils ================= This module provides common functionality for icinga2 module and state. .. versionadded:: 2018.8.3 -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import re -# Import Salt libs -import salt.utils.path import salt.modules.cmdmod -__salt__ = { - 'cmd.run_all': salt.modules.cmdmod.run_all -} +# Import Salt libs +import salt.utils.path + +__salt__ = {"cmd.run_all": salt.modules.cmdmod.run_all} log = logging.getLogger(__name__) def get_certs_path(): - icinga2_output = __salt__['cmd.run_all']([salt.utils.path.which('icinga2'), - "--version"], python_shell=False) - version = re.search(r'r\d+\.\d+', icinga2_output['stdout']).group(0) + icinga2_output = __salt__["cmd.run_all"]( + [salt.utils.path.which("icinga2"), "--version"], python_shell=False + ) + version = re.search(r"r\d+\.\d+", icinga2_output["stdout"]).group(0) # Return new certs path for icinga2 >= 2.8 - if int(version.split('.')[1]) >= 8: - return '/var/lib/icinga2/certs/' + if int(version.split(".")[1]) >= 8: + return "/var/lib/icinga2/certs/" # Keep backwords compatibility with older icinga2 - return '/etc/icinga2/pki/' + return "/etc/icinga2/pki/" diff --git a/salt/utils/immutabletypes.py b/salt/utils/immutabletypes.py index b474d1bd1e9..11963f290c6 100644 --- a/salt/utils/immutabletypes.py +++ b/salt/utils/immutabletypes.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,21 +7,25 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~ Immutable types -''' +""" from __future__ import absolute_import, unicode_literals + import copy # Import python libs try: from collections.abc import Mapping, Sequence, Set except ImportError: + # pylint: disable=no-name-in-module from collections import Mapping, Sequence, Set + # pylint: enable=no-name-in-module + class ImmutableDict(Mapping): - ''' + """ An immutable dictionary implementation - ''' + """ def __init__(self, obj): self.__obj = obj @@ -36,22 +40,22 @@ class ImmutableDict(Mapping): return freeze(self.__obj[key]) def __repr__(self): - return '<{0} {1}>'.format(self.__class__.__name__, repr(self.__obj)) + return "<{0} {1}>".format(self.__class__.__name__, repr(self.__obj)) def __deepcopy__(self, memo): return copy.deepcopy(self.__obj) def copy(self): - ''' + """ Return an un-frozen copy of self - ''' + """ return copy.deepcopy(self.__obj) class ImmutableList(Sequence): - ''' + """ An immutable list implementation - ''' + """ def __init__(self, obj): self.__obj = obj @@ -72,16 +76,16 @@ class ImmutableList(Sequence): return freeze(self.__obj[key]) def __repr__(self): - return '<{0} {1}>'.format(self.__class__.__name__, repr(self.__obj)) + return "<{0} {1}>".format(self.__class__.__name__, repr(self.__obj)) def __deepcopy__(self, memo): return copy.deepcopy(self.__obj) class ImmutableSet(Set): - ''' + """ An immutable set implementation - ''' + """ def __init__(self, obj): self.__obj = obj @@ -96,16 +100,16 @@ class ImmutableSet(Set): return key in self.__obj def __repr__(self): - return '<{0} {1}>'.format(self.__class__.__name__, repr(self.__obj)) + return "<{0} {1}>".format(self.__class__.__name__, repr(self.__obj)) def __deepcopy__(self, memo): return copy.deepcopy(self.__obj) def freeze(obj): - ''' + """ Freeze python types by turning them into immutable structures. - ''' + """ if isinstance(obj, dict): return ImmutableDict(obj) if isinstance(obj, list): diff --git a/salt/utils/itertools.py b/salt/utils/itertools.py index 817f461c8a0..39c08d51df8 100644 --- a/salt/utils/itertools.py +++ b/salt/utils/itertools.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Helpful generators and other tools -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import fnmatch import re @@ -13,13 +14,13 @@ import salt.utils.files def split(orig, sep=None): - ''' + """ Generator function for iterating through large strings, particularly useful as a replacement for str.splitlines(). See http://stackoverflow.com/a/3865367 - ''' - exp = re.compile(r'\s+' if sep is None else re.escape(sep)) + """ + exp = re.compile(r"\s+" if sep is None else re.escape(sep)) pos = 0 length = len(orig) while True: @@ -34,27 +35,27 @@ def split(orig, sep=None): yield val break if pos < match.start() or sep is not None: - yield orig[pos:match.start()] + yield orig[pos : match.start()] pos = match.end() def read_file(fh_, chunk_size=1048576): - ''' + """ Generator that reads chunk_size bytes at a time from a file/filehandle and yields it. - ''' + """ try: if chunk_size != int(chunk_size): raise ValueError except ValueError: - raise ValueError('chunk_size must be an integer') + raise ValueError("chunk_size must be an integer") try: while True: try: chunk = fh_.read(chunk_size) except AttributeError: # Open the file and re-attempt the read - fh_ = salt.utils.files.fopen(fh_, 'rb') # pylint: disable=W8470 + fh_ = salt.utils.files.fopen(fh_, "rb") # pylint: disable=W8470 chunk = fh_.read(chunk_size) if not chunk: break @@ -67,11 +68,11 @@ def read_file(fh_, chunk_size=1048576): def fnmatch_multiple(candidates, pattern): - ''' + """ Convenience function which runs fnmatch.fnmatch() on each element of passed iterable. The first matching candidate is returned, or None if there is no matching candidate. - ''' + """ # Make sure that candidates is iterable to avoid a TypeError when we try to # iterate over its items. try: diff --git a/salt/utils/jid.py b/salt/utils/jid.py index 7e6008df3ac..8c8353ba8a9 100644 --- a/salt/utils/jid.py +++ b/salt/utils/jid.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Functions for creating and working with job IDs -''' +""" from __future__ import absolute_import, print_function, unicode_literals -from calendar import month_abbr as months + import datetime import hashlib import os +from calendar import month_abbr as months import salt.utils.stringutils from salt.ext import six @@ -15,41 +16,41 @@ LAST_JID_DATETIME = None def _utc_now(): - ''' + """ Helper method so tests do not have to patch the built-in method. - ''' + """ return datetime.datetime.utcnow() def gen_jid(opts=None): - ''' + """ Generate a jid - ''' + """ if opts is None: salt.utils.versions.warn_until( - 'Sodium', - 'The `opts` argument was not passed into salt.utils.jid.gen_jid(). ' - 'This will be required starting in {version}.' + "Sodium", + "The `opts` argument was not passed into salt.utils.jid.gen_jid(). " + "This will be required starting in {version}.", ) opts = {} global LAST_JID_DATETIME # pylint: disable=global-statement jid_dt = _utc_now() - if not opts.get('unique_jid', False): - return '{0:%Y%m%d%H%M%S%f}'.format(jid_dt) + if not opts.get("unique_jid", False): + return "{0:%Y%m%d%H%M%S%f}".format(jid_dt) if LAST_JID_DATETIME and LAST_JID_DATETIME >= jid_dt: jid_dt = LAST_JID_DATETIME + datetime.timedelta(microseconds=1) LAST_JID_DATETIME = jid_dt - return '{0:%Y%m%d%H%M%S%f}_{1}'.format(jid_dt, os.getpid()) + return "{0:%Y%m%d%H%M%S%f}_{1}".format(jid_dt, os.getpid()) def is_jid(jid): - ''' + """ Returns True if the passed in value is a job id - ''' + """ if not isinstance(jid, six.string_types): return False - if len(jid) != 20 and (len(jid) <= 21 or jid[20] != '_'): + if len(jid) != 20 and (len(jid) <= 21 or jid[20] != "_"): return False try: int(jid[:20]) @@ -59,12 +60,12 @@ def is_jid(jid): def jid_to_time(jid): - ''' + """ Convert a salt job id into the time when the job was invoked - ''' + """ jid = six.text_type(jid) - if len(jid) != 20 and (len(jid) <= 21 or jid[20] != '_'): - return '' + if len(jid) != 20 and (len(jid) <= 21 or jid[20] != "_"): + return "" year = jid[:4] month = jid[4:6] day = jid[6:8] @@ -73,64 +74,61 @@ def jid_to_time(jid): second = jid[12:14] micro = jid[14:20] - ret = '{0}, {1} {2} {3}:{4}:{5}.{6}'.format(year, - months[int(month)], - day, - hour, - minute, - second, - micro) + ret = "{0}, {1} {2} {3}:{4}:{5}.{6}".format( + year, months[int(month)], day, hour, minute, second, micro + ) return ret def format_job_instance(job): - ''' + """ Format the job instance correctly - ''' - ret = {'Function': job.get('fun', 'unknown-function'), - 'Arguments': list(job.get('arg', [])), - # unlikely but safeguard from invalid returns - 'Target': job.get('tgt', 'unknown-target'), - 'Target-type': job.get('tgt_type', 'list'), - 'User': job.get('user', 'root')} + """ + ret = { + "Function": job.get("fun", "unknown-function"), + "Arguments": list(job.get("arg", [])), + # unlikely but safeguard from invalid returns + "Target": job.get("tgt", "unknown-target"), + "Target-type": job.get("tgt_type", "list"), + "User": job.get("user", "root"), + } - if 'metadata' in job: - ret['Metadata'] = job.get('metadata', {}) + if "metadata" in job: + ret["Metadata"] = job.get("metadata", {}) else: - if 'kwargs' in job: - if 'metadata' in job['kwargs']: - ret['Metadata'] = job['kwargs'].get('metadata', {}) + if "kwargs" in job: + if "metadata" in job["kwargs"]: + ret["Metadata"] = job["kwargs"].get("metadata", {}) return ret def format_jid_instance(jid, job): - ''' + """ Format the jid correctly - ''' + """ ret = format_job_instance(job) - ret.update({'StartTime': jid_to_time(jid)}) + ret.update({"StartTime": jid_to_time(jid)}) return ret def format_jid_instance_ext(jid, job): - ''' + """ Format the jid correctly with jid included - ''' + """ ret = format_job_instance(job) - ret.update({ - 'JID': jid, - 'StartTime': jid_to_time(jid)}) + ret.update({"JID": jid, "StartTime": jid_to_time(jid)}) return ret -def jid_dir(jid, job_dir=None, hash_type='sha256'): - ''' +def jid_dir(jid, job_dir=None, hash_type="sha256"): + """ Return the jid_dir for the given job id - ''' + """ if not isinstance(jid, six.string_types): jid = six.text_type(jid) jhash = getattr(hashlib, hash_type)( - salt.utils.stringutils.to_bytes(jid)).hexdigest() + salt.utils.stringutils.to_bytes(jid) + ).hexdigest() parts = [] if job_dir is not None: diff --git a/salt/utils/jinja.py b/salt/utils/jinja.py index 6e4261e68ec..29b208568d1 100644 --- a/salt/utils/jinja.py +++ b/salt/utils/jinja.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Jinja loading utils to enable a more powerful backend for jinja templates -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import atexit import collections import logging @@ -19,14 +20,6 @@ from xml.etree.ElementTree import Element, SubElement, tostring # Import third party libs import jinja2 -from salt.ext import six -from jinja2 import BaseLoader, Markup, TemplateNotFound, nodes -from jinja2.environment import TemplateModule -from jinja2.exceptions import TemplateRuntimeError -from jinja2.ext import Extension - -# Import salt libs -from salt.exceptions import TemplateError import salt.fileclient import salt.utils.data import salt.utils.files @@ -34,93 +27,106 @@ import salt.utils.json import salt.utils.stringutils import salt.utils.url import salt.utils.yaml -from salt.utils.decorators.jinja import jinja_filter, jinja_test, jinja_global +from jinja2 import BaseLoader, Markup, TemplateNotFound, nodes +from jinja2.environment import TemplateModule +from jinja2.exceptions import TemplateRuntimeError +from jinja2.ext import Extension + +# Import salt libs +from salt.exceptions import TemplateError +from salt.ext import six +from salt.utils.decorators.jinja import jinja_filter, jinja_global, jinja_test from salt.utils.odict import OrderedDict log = logging.getLogger(__name__) -__all__ = [ - 'SaltCacheLoader', - 'SerializerExtension' -] +__all__ = ["SaltCacheLoader", "SerializerExtension"] -GLOBAL_UUID = uuid.UUID('91633EBF-1C86-5E33-935A-28061F4B480E') +GLOBAL_UUID = uuid.UUID("91633EBF-1C86-5E33-935A-28061F4B480E") class SaltCacheLoader(BaseLoader): - ''' + """ A special jinja Template Loader for salt. Requested templates are always fetched from the server to guarantee that the file is up to date. Templates are cached like regular salt states and only loaded once per loader instance. - ''' + """ _cached_pillar_client = None _cached_client = None @classmethod def shutdown(cls): - for attr in ('_cached_client', '_cached_pillar_client'): + for attr in ("_cached_client", "_cached_pillar_client"): client = getattr(cls, attr, None) if client is not None: # PillarClient and LocalClient objects do not have a destroy method - if hasattr(client, 'destroy'): + if hasattr(client, "destroy"): client.destroy() setattr(cls, attr, None) - def __init__(self, opts, saltenv='base', encoding='utf-8', - pillar_rend=False, _file_client=None): + def __init__( + self, + opts, + saltenv="base", + encoding="utf-8", + pillar_rend=False, + _file_client=None, + ): self.opts = opts self.saltenv = saltenv self.encoding = encoding self.pillar_rend = pillar_rend if self.pillar_rend: - if saltenv not in self.opts['pillar_roots']: + if saltenv not in self.opts["pillar_roots"]: self.searchpath = [] else: - self.searchpath = opts['pillar_roots'][saltenv] + self.searchpath = opts["pillar_roots"][saltenv] else: - self.searchpath = [os.path.join(opts['cachedir'], 'files', saltenv)] - log.debug('Jinja search path: %s', self.searchpath) + self.searchpath = [os.path.join(opts["cachedir"], "files", saltenv)] + log.debug("Jinja search path: %s", self.searchpath) self.cached = [] self._file_client = _file_client # Instantiate the fileclient self.file_client() def file_client(self): - ''' + """ Return a file client. Instantiates on first call. - ''' + """ # If there was no file_client passed to the class, create a cache_client # and use that. This avoids opening a new file_client every time this # class is instantiated if self._file_client is None: - attr = '_cached_pillar_client' if self.pillar_rend else '_cached_client' + attr = "_cached_pillar_client" if self.pillar_rend else "_cached_client" cached_client = getattr(self, attr, None) if cached_client is None: - cached_client = salt.fileclient.get_file_client(self.opts, self.pillar_rend) + cached_client = salt.fileclient.get_file_client( + self.opts, self.pillar_rend + ) setattr(SaltCacheLoader, attr, cached_client) self._file_client = cached_client return self._file_client def cache_file(self, template): - ''' + """ Cache a file from the salt master - ''' + """ saltpath = salt.utils.url.create(template) - self.file_client().get_file(saltpath, '', True, self.saltenv) + self.file_client().get_file(saltpath, "", True, self.saltenv) def check_cache(self, template): - ''' + """ Cache a file only once - ''' + """ if template not in self.cached: self.cache_file(template) self.cached.append(template) def get_source(self, environment, template): - ''' + """ Salt-specific loader to find imported jinja files. Jinja imports will be interpreted as originating from the top @@ -129,10 +135,10 @@ class SaltCacheLoader(BaseLoader): begins with './' or '../' then the import will be relative to the importing file. - ''' + """ # FIXME: somewhere do seprataor replacement: '\\' => '/' _template = template - if template.split('/', 1)[0] in ('..', '.'): + if template.split("/", 1)[0] in ("..", "."): is_relative = True else: is_relative = False @@ -140,33 +146,34 @@ class SaltCacheLoader(BaseLoader): if is_relative: # Starts with a relative path indicator - if not environment or 'tpldir' not in environment.globals: + if not environment or "tpldir" not in environment.globals: log.warning( 'Relative path "%s" cannot be resolved without an environment', - template + template, ) raise TemplateNotFound - base_path = environment.globals['tpldir'] - _template = os.path.normpath('/'.join((base_path, _template))) - if _template.split('/', 1)[0] == '..': + base_path = environment.globals["tpldir"] + _template = os.path.normpath("/".join((base_path, _template))) + if _template.split("/", 1)[0] == "..": log.warning( 'Discarded template path "%s": attempts to' - ' ascend outside of salt://', template + " ascend outside of salt://", + template, ) raise TemplateNotFound(template) self.check_cache(_template) if environment and template: - tpldir = os.path.dirname(_template).replace('\\', '/') + tpldir = os.path.dirname(_template).replace("\\", "/") tplfile = _template if is_relative: - tpldir = environment.globals.get('tpldir', tpldir) + tpldir = environment.globals.get("tpldir", tpldir) tplfile = template tpldata = { - 'tplfile': tplfile, - 'tpldir': '.' if tpldir == '' else tpldir, - 'tpldot': tpldir.replace('/', '.'), + "tplfile": tplfile, + "tpldir": "." if tpldir == "" else tpldir, + "tpldot": tpldir.replace("/", "."), } environment.globals.update(tpldata) @@ -174,7 +181,7 @@ class SaltCacheLoader(BaseLoader): for spath in self.searchpath: filepath = os.path.join(spath, _template) try: - with salt.utils.files.fopen(filepath, 'rb') as ifile: + with salt.utils.files.fopen(filepath, "rb") as ifile: contents = ifile.read().decode(self.encoding) mtime = os.path.getmtime(filepath) @@ -183,6 +190,7 @@ class SaltCacheLoader(BaseLoader): return os.path.getmtime(filepath) == mtime except OSError: return False + return contents, filepath, uptodate except IOError: # there is no file under current path @@ -197,7 +205,7 @@ atexit.register(SaltCacheLoader.shutdown) class PrintableDict(OrderedDict): - ''' + """ Ensures that dict str() and repr() are YAML friendly. .. code-block:: python @@ -209,37 +217,42 @@ class PrintableDict(OrderedDict): decorated = PrintableDict(mapping) print decorated # {'a': 'b', 'c': None} - ''' + """ + def __str__(self): output = [] for key, value in six.iteritems(self): if isinstance(value, six.string_types): # keeps quotes around strings - output.append('{0!r}: {1!r}'.format(key, value)) # pylint: disable=repr-flag-used-in-string + # pylint: disable=repr-flag-used-in-string + output.append("{0!r}: {1!r}".format(key, value)) + # pylint: enable=repr-flag-used-in-string else: # let default output - output.append('{0!r}: {1!s}'.format(key, value)) # pylint: disable=repr-flag-used-in-string - return '{' + ', '.join(output) + '}' + output.append("{0!r}: {1!s}".format(key, value)) + return "{" + ", ".join(output) + "}" def __repr__(self): # pylint: disable=W0221 output = [] for key, value in six.iteritems(self): # Raw string formatter required here because this is a repr # function. - output.append('{0!r}: {1!r}'.format(key, value)) # pylint: disable=repr-flag-used-in-string - return '{' + ', '.join(output) + '}' + # pylint: disable=repr-flag-used-in-string + output.append("{0!r}: {1!r}".format(key, value)) + # pylint: enable=repr-flag-used-in-string + return "{" + ", ".join(output) + "}" # Additional globals -@jinja_global('raise') +@jinja_global("raise") def jinja_raise(msg): raise TemplateError(msg) # Additional tests -@jinja_test('match') +@jinja_test("match") def test_match(txt, rgx, ignorecase=False, multiline=False): - '''Returns true if a sequence of chars matches a pattern.''' + """Returns true if a sequence of chars matches a pattern.""" flag = 0 if ignorecase: flag |= re.I @@ -249,16 +262,16 @@ def test_match(txt, rgx, ignorecase=False, multiline=False): return True if compiled_rgx.match(txt) else False -@jinja_test('equalto') +@jinja_test("equalto") def test_equalto(value, other): - '''Returns true if two values are equal.''' + """Returns true if two values are equal.""" return value == other # Additional filters -@jinja_filter('skip') +@jinja_filter("skip") def skip_filter(data): - ''' + """ Suppress data output .. code-block:: yaml @@ -269,13 +282,13 @@ def skip_filter(data): will be rendered as empty string, - ''' - return '' + """ + return "" -@jinja_filter('sequence') +@jinja_filter("sequence") def ensure_sequence_filter(data): - ''' + """ Ensure sequenced data. **sequence** @@ -300,15 +313,15 @@ def ensure_sequence_filter(data): foo bar baz - ''' + """ if not isinstance(data, (list, tuple, set, dict)): return [data] return data -@jinja_filter('to_bool') +@jinja_filter("to_bool") def to_bool(val): - ''' + """ Returns the logical value. .. code-block:: jinja @@ -320,13 +333,13 @@ def to_bool(val): .. code-block:: text True - ''' + """ if val is None: return False if isinstance(val, bool): return val if isinstance(val, (six.text_type, six.string_types)): - return val.lower() in ('yes', '1', 'true') + return val.lower() in ("yes", "1", "true") if isinstance(val, six.integer_types): return val > 0 if not isinstance(val, collections.Hashable): @@ -334,29 +347,28 @@ def to_bool(val): return False -@jinja_filter('tojson') +@jinja_filter("tojson") def tojson(val, indent=None): - ''' + """ Implementation of tojson filter (only present in Jinja 2.9 and later). If Jinja 2.9 or later is installed, then the upstream version of this filter will be used. - ''' - options = {'ensure_ascii': True} + """ + options = {"ensure_ascii": True} if indent is not None: - options['indent'] = indent + options["indent"] = indent return ( - salt.utils.json.dumps( - val, **options - ).replace('<', '\\u003c') - .replace('>', '\\u003e') - .replace('&', '\\u0026') - .replace("'", '\\u0027') + salt.utils.json.dumps(val, **options) + .replace("<", "\\u003c") + .replace(">", "\\u003e") + .replace("&", "\\u0026") + .replace("'", "\\u0027") ) -@jinja_filter('quote') +@jinja_filter("quote") def quote(txt): - ''' + """ Wraps a text around quotes. .. code-block:: jinja @@ -369,7 +381,7 @@ def quote(txt): .. code-block:: text 'my_text' - ''' + """ return pipes.quote(txt) @@ -378,9 +390,9 @@ def regex_escape(value): return re.escape(value) -@jinja_filter('regex_search') +@jinja_filter("regex_search") def regex_search(txt, rgx, ignorecase=False, multiline=False): - ''' + """ Searches for a pattern in the text. .. code-block:: jinja @@ -393,7 +405,7 @@ def regex_search(txt, rgx, ignorecase=False, multiline=False): .. code-block:: text ('a', 'd') - ''' + """ flag = 0 if ignorecase: flag |= re.I @@ -405,9 +417,9 @@ def regex_search(txt, rgx, ignorecase=False, multiline=False): return obj.groups() -@jinja_filter('regex_match') +@jinja_filter("regex_match") def regex_match(txt, rgx, ignorecase=False, multiline=False): - ''' + """ Searches for a pattern in the text. .. code-block:: jinja @@ -420,7 +432,7 @@ def regex_match(txt, rgx, ignorecase=False, multiline=False): .. code-block:: text ('a', 'd') - ''' + """ flag = 0 if ignorecase: flag |= re.I @@ -432,9 +444,9 @@ def regex_match(txt, rgx, ignorecase=False, multiline=False): return obj.groups() -@jinja_filter('regex_replace') +@jinja_filter("regex_replace") def regex_replace(txt, rgx, val, ignorecase=False, multiline=False): - r''' + r""" Searches for a pattern and replaces with a sequence of characters. .. code-block:: jinja @@ -447,7 +459,7 @@ def regex_replace(txt, rgx, val, ignorecase=False, multiline=False): .. code-block:: text lets__replace__spaces - ''' + """ flag = 0 if ignorecase: flag |= re.I @@ -457,9 +469,9 @@ def regex_replace(txt, rgx, val, ignorecase=False, multiline=False): return compiled_rgx.sub(val, txt) -@jinja_filter('uuid') +@jinja_filter("uuid") def uuid_(val): - ''' + """ Returns a UUID corresponding to the value passed as argument. .. code-block:: jinja @@ -471,13 +483,8 @@ def uuid_(val): .. code-block:: text f4efeff8-c219-578a-bad7-3dc280612ec8 - ''' - return six.text_type( - uuid.uuid5( - GLOBAL_UUID, - salt.utils.stringutils.to_str(val) - ) - ) + """ + return six.text_type(uuid.uuid5(GLOBAL_UUID, salt.utils.stringutils.to_str(val))) ### List-related filters @@ -485,7 +492,7 @@ def uuid_(val): @jinja_filter() def unique(values): - ''' + """ Removes duplicates from a list. .. code-block:: jinja @@ -498,7 +505,7 @@ def unique(values): .. code-block:: text ['a', 'b', 'c'] - ''' + """ ret = None if isinstance(values, collections.Hashable): ret = set(values) @@ -510,9 +517,9 @@ def unique(values): return ret -@jinja_filter('min') +@jinja_filter("min") def lst_min(obj): - ''' + """ Returns the min value. .. code-block:: jinja @@ -525,13 +532,13 @@ def lst_min(obj): .. code-block:: text 1 - ''' + """ return min(obj) -@jinja_filter('max') +@jinja_filter("max") def lst_max(obj): - ''' + """ Returns the max value. .. code-block:: jinja @@ -544,13 +551,13 @@ def lst_max(obj): .. code-block:: text 4 - ''' + """ return max(obj) -@jinja_filter('avg') +@jinja_filter("avg") def lst_avg(lst): - ''' + """ Returns the average value of a list. .. code-block:: jinja @@ -563,15 +570,15 @@ def lst_avg(lst): .. code-block:: yaml 2.5 - ''' + """ if not isinstance(lst, collections.Hashable): - return float(sum(lst)/len(lst)) + return float(sum(lst) / len(lst)) return float(lst) -@jinja_filter('union') +@jinja_filter("union") def union(lst1, lst2): - ''' + """ Returns the union of two lists. .. code-block:: jinja @@ -584,15 +591,17 @@ def union(lst1, lst2): .. code-block:: text [1, 2, 3, 4, 6] - ''' - if isinstance(lst1, collections.Hashable) and isinstance(lst2, collections.Hashable): + """ + if isinstance(lst1, collections.Hashable) and isinstance( + lst2, collections.Hashable + ): return set(lst1) | set(lst2) return unique(lst1 + lst2) -@jinja_filter('intersect') +@jinja_filter("intersect") def intersect(lst1, lst2): - ''' + """ Returns the intersection of two lists. .. code-block:: jinja @@ -605,15 +614,17 @@ def intersect(lst1, lst2): .. code-block:: text [2, 4] - ''' - if isinstance(lst1, collections.Hashable) and isinstance(lst2, collections.Hashable): + """ + if isinstance(lst1, collections.Hashable) and isinstance( + lst2, collections.Hashable + ): return set(lst1) & set(lst2) return unique([ele for ele in lst1 if ele in lst2]) -@jinja_filter('difference') +@jinja_filter("difference") def difference(lst1, lst2): - ''' + """ Returns the difference of two lists. .. code-block:: jinja @@ -626,15 +637,17 @@ def difference(lst1, lst2): .. code-block:: text [1, 3, 6] - ''' - if isinstance(lst1, collections.Hashable) and isinstance(lst2, collections.Hashable): + """ + if isinstance(lst1, collections.Hashable) and isinstance( + lst2, collections.Hashable + ): return set(lst1) - set(lst2) return unique([ele for ele in lst1 if ele not in lst2]) -@jinja_filter('symmetric_difference') +@jinja_filter("symmetric_difference") def symmetric_difference(lst1, lst2): - ''' + """ Returns the symmetric difference of two lists. .. code-block:: jinja @@ -647,15 +660,21 @@ def symmetric_difference(lst1, lst2): .. code-block:: text [1, 3] - ''' - if isinstance(lst1, collections.Hashable) and isinstance(lst2, collections.Hashable): + """ + if isinstance(lst1, collections.Hashable) and isinstance( + lst2, collections.Hashable + ): return set(lst1) ^ set(lst2) - return unique([ele for ele in union(lst1, lst2) if ele not in intersect(lst1, lst2)]) + return unique( + [ele for ele in union(lst1, lst2) if ele not in intersect(lst1, lst2)] + ) @jinja2.contextfunction def show_full_context(ctx): - return salt.utils.data.simple_types_filter({key: value for key, value in ctx.items()}) + return salt.utils.data.simple_types_filter( + {key: value for key, value in ctx.items()} + ) class SerializerExtension(Extension, object): @@ -825,19 +844,28 @@ class SerializerExtension(Extension, object): .. _`import tag`: http://jinja.pocoo.org/docs/templates/#import ''' - tags = {'load_yaml', 'load_json', 'import_yaml', 'import_json', 'load_text', 'import_text'} + tags = { + "load_yaml", + "load_json", + "import_yaml", + "import_json", + "load_text", + "import_text", + } def __init__(self, environment): super(SerializerExtension, self).__init__(environment) - self.environment.filters.update({ - 'yaml': self.format_yaml, - 'json': self.format_json, - 'xml': self.format_xml, - 'python': self.format_python, - 'load_yaml': self.load_yaml, - 'load_json': self.load_json, - 'load_text': self.load_text, - }) + self.environment.filters.update( + { + "yaml": self.format_yaml, + "json": self.format_json, + "xml": self.format_xml, + "python": self.format_python, + "load_yaml": self.load_yaml, + "load_json": self.load_json, + "load_text": self.load_text, + } + ) if self.environment.finalize is None: self.environment.finalize = self.finalizer @@ -847,12 +875,14 @@ class SerializerExtension(Extension, object): @wraps(finalizer) def wrapper(self, data): return finalizer(self.finalizer(data)) + self.environment.finalize = wrapper def finalizer(self, data): - ''' + """ Ensure that printed mappings are YAML friendly. - ''' + """ + def explore(data): if isinstance(data, (dict, OrderedDict)): return PrintableDict( @@ -861,10 +891,13 @@ class SerializerExtension(Extension, object): elif isinstance(data, (list, tuple, set)): return data.__class__([explore(value) for value in data]) return data + return explore(data) def format_json(self, value, sort_keys=True, indent=None): - json_txt = salt.utils.json.dumps(value, sort_keys=sort_keys, indent=indent).strip() + json_txt = salt.utils.json.dumps( + value, sort_keys=sort_keys, indent=indent + ).strip() try: return Markup(json_txt) except UnicodeDecodeError: @@ -872,9 +905,10 @@ class SerializerExtension(Extension, object): def format_yaml(self, value, flow_style=True): yaml_txt = salt.utils.yaml.safe_dump( - value, default_flow_style=flow_style).strip() - if yaml_txt.endswith(str('\n...')): # future lint: disable=blacklisted-function - yaml_txt = yaml_txt[:len(yaml_txt)-4] + value, default_flow_style=flow_style + ).strip() + if yaml_txt.endswith(str("\n...")): # future lint: disable=blacklisted-function + yaml_txt = yaml_txt[: len(yaml_txt) - 4] try: return Markup(yaml_txt) except UnicodeDecodeError: @@ -888,6 +922,7 @@ class SerializerExtension(Extension, object): :returns: Formatted XML string rendered with newlines and indentation :rtype: str """ + def normalize_iter(value): if isinstance(value, (list, tuple)): if isinstance(value[0], str): @@ -898,7 +933,8 @@ class SerializerExtension(Extension, object): xmlval = list(value.items()) else: raise TemplateRuntimeError( - 'Value is not a dict or list. Cannot render as XML') + "Value is not a dict or list. Cannot render as XML" + ) return xmlval def recurse_tree(xmliter, element=None): @@ -915,16 +951,24 @@ class SerializerExtension(Extension, object): sub.text = six.text_type(attrs) continue if isinstance(attrs, dict): - sub.attrib = {attr: six.text_type(val) for attr, val in attrs.items() - if not isinstance(val, (dict, list))} - for tag, val in [item for item in normalize_iter(attrs) if - isinstance(item[1], (dict, list))]: + sub.attrib = { + attr: six.text_type(val) + for attr, val in attrs.items() + if not isinstance(val, (dict, list)) + } + for tag, val in [ + item + for item in normalize_iter(attrs) + if isinstance(item[1], (dict, list)) + ]: recurse_tree(((tag, val),), sub) return sub - return Markup(minidom.parseString( - tostring(recurse_tree(normalize_iter(value))) - ).toprettyxml(indent=" ")) + return Markup( + minidom.parseString( + tostring(recurse_tree(normalize_iter(value))) + ).toprettyxml(indent=" ") + ) def format_python(self, value): return Markup(pprint.pformat(value).strip()) @@ -935,7 +979,7 @@ class SerializerExtension(Extension, object): try: return salt.utils.data.decode(salt.utils.yaml.safe_load(value)) except salt.utils.yaml.YAMLError as exc: - msg = 'Encountered error loading yaml: ' + msg = "Encountered error loading yaml: " try: # Reported line is off by one, add 1 to correct it line = exc.problem_mark.line + 1 @@ -946,15 +990,13 @@ class SerializerExtension(Extension, object): # to the stringified version of the exception. msg += six.text_type(exc) else: - msg += '{0}\n'.format(problem) + msg += "{0}\n".format(problem) msg += salt.utils.stringutils.get_context( - buf, - line, - marker=' <======================') + buf, line, marker=" <======================" + ) raise TemplateRuntimeError(msg) except AttributeError: - raise TemplateRuntimeError( - 'Unable to load yaml from {0}'.format(value)) + raise TemplateRuntimeError("Unable to load yaml from {0}".format(value)) def load_json(self, value): if isinstance(value, TemplateModule): @@ -962,8 +1004,7 @@ class SerializerExtension(Extension, object): try: return salt.utils.json.loads(value) except (ValueError, TypeError, AttributeError): - raise TemplateRuntimeError( - 'Unable to load json from {0}'.format(value)) + raise TemplateRuntimeError("Unable to load json from {0}".format(value)) def load_text(self, value): if isinstance(value, TemplateModule): @@ -971,58 +1012,54 @@ class SerializerExtension(Extension, object): return value - _load_parsers = {'load_yaml', 'load_json', 'load_text'} + _load_parsers = {"load_yaml", "load_json", "load_text"} def parse(self, parser): - if parser.stream.current.value == 'import_yaml': + if parser.stream.current.value == "import_yaml": return self.parse_yaml(parser) - elif parser.stream.current.value == 'import_json': + elif parser.stream.current.value == "import_json": return self.parse_json(parser) - elif parser.stream.current.value == 'import_text': + elif parser.stream.current.value == "import_text": return self.parse_text(parser) elif parser.stream.current.value in self._load_parsers: return self.parse_load(parser) - parser.fail('Unknown format ' + parser.stream.current.value, - parser.stream.current.lineno) + parser.fail( + "Unknown format " + parser.stream.current.value, + parser.stream.current.lineno, + ) # pylint: disable=E1120,E1121 def parse_load(self, parser): filter_name = parser.stream.current.value lineno = next(parser.stream).lineno if filter_name not in self.environment.filters: - parser.fail('Unable to parse {0}'.format(filter_name), lineno) + parser.fail("Unable to parse {0}".format(filter_name), lineno) - parser.stream.expect('name:as') + parser.stream.expect("name:as") target = parser.parse_assign_target() - macro_name = '_' + parser.free_identifier().name - macro_body = parser.parse_statements( - ('name:endload',), drop_needle=True) + macro_name = "_" + parser.free_identifier().name + macro_body = parser.parse_statements(("name:endload",), drop_needle=True) return [ - nodes.Macro( - macro_name, - [], - [], - macro_body - ).set_lineno(lineno), + nodes.Macro(macro_name, [], [], macro_body).set_lineno(lineno), nodes.Assign( target, nodes.Filter( nodes.Call( - nodes.Name(macro_name, 'load').set_lineno(lineno), + nodes.Name(macro_name, "load").set_lineno(lineno), [], [], None, - None + None, ).set_lineno(lineno), filter_name, [], [], None, - None - ).set_lineno(lineno) - ).set_lineno(lineno) + None, + ).set_lineno(lineno), + ).set_lineno(lineno), ] def parse_yaml(self, parser): @@ -1033,17 +1070,16 @@ class SerializerExtension(Extension, object): return [ import_node, nodes.Assign( - nodes.Name(target, 'store').set_lineno(lineno), + nodes.Name(target, "store").set_lineno(lineno), nodes.Filter( - nodes.Name(target, 'load').set_lineno(lineno), - 'load_yaml', + nodes.Name(target, "load").set_lineno(lineno), + "load_yaml", [], [], None, - None - ) - .set_lineno(lineno) - ).set_lineno(lineno) + None, + ).set_lineno(lineno), + ).set_lineno(lineno), ] def parse_json(self, parser): @@ -1054,17 +1090,16 @@ class SerializerExtension(Extension, object): return [ import_node, nodes.Assign( - nodes.Name(target, 'store').set_lineno(lineno), + nodes.Name(target, "store").set_lineno(lineno), nodes.Filter( - nodes.Name(target, 'load').set_lineno(lineno), - 'load_json', + nodes.Name(target, "load").set_lineno(lineno), + "load_json", [], [], None, - None - ) - .set_lineno(lineno) - ).set_lineno(lineno) + None, + ).set_lineno(lineno), + ).set_lineno(lineno), ] def parse_text(self, parser): @@ -1075,16 +1110,16 @@ class SerializerExtension(Extension, object): return [ import_node, nodes.Assign( - nodes.Name(target, 'store').set_lineno(lineno), + nodes.Name(target, "store").set_lineno(lineno), nodes.Filter( - nodes.Name(target, 'load').set_lineno(lineno), - 'load_text', + nodes.Name(target, "load").set_lineno(lineno), + "load_text", [], [], None, - None - ) - .set_lineno(lineno) - ).set_lineno(lineno) + None, + ).set_lineno(lineno), + ).set_lineno(lineno), ] + # pylint: enable=E1120,E1121 diff --git a/salt/utils/job.py b/salt/utils/job.py index b2096700582..f5f91fbfe2c 100644 --- a/salt/utils/job.py +++ b/salt/utils/job.py @@ -1,63 +1,68 @@ # -*- coding: utf-8 -*- -''' +""" Functions for interacting with the job cache -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import logging # Import Salt libs import salt.minion -import salt.utils.jid import salt.utils.event +import salt.utils.jid import salt.utils.verify log = logging.getLogger(__name__) def store_job(opts, load, event=None, mminion=None): - ''' + """ Store job information using the configured master_job_cache - ''' + """ # Generate EndTime endtime = salt.utils.jid.jid_to_time(salt.utils.jid.gen_jid(opts)) # If the return data is invalid, just ignore it - if any(key not in load for key in ('return', 'jid', 'id')): + if any(key not in load for key in ("return", "jid", "id")): return False - if not salt.utils.verify.valid_id(opts, load['id']): + if not salt.utils.verify.valid_id(opts, load["id"]): return False if mminion is None: mminion = salt.minion.MasterMinion(opts, states=False, rend=False) - job_cache = opts['master_job_cache'] - if load['jid'] == 'req': + job_cache = opts["master_job_cache"] + if load["jid"] == "req": # The minion is returning a standalone job, request a jobid - load['arg'] = load.get('arg', load.get('fun_args', [])) - load['tgt_type'] = 'glob' - load['tgt'] = load['id'] + load["arg"] = load.get("arg", load.get("fun_args", [])) + load["tgt_type"] = "glob" + load["tgt"] = load["id"] - prep_fstr = '{0}.prep_jid'.format(opts['master_job_cache']) + prep_fstr = "{0}.prep_jid".format(opts["master_job_cache"]) try: - load['jid'] = mminion.returners[prep_fstr](nocache=load.get('nocache', False)) + load["jid"] = mminion.returners[prep_fstr]( + nocache=load.get("nocache", False) + ) except KeyError: emsg = "Returner '{0}' does not support function prep_jid".format(job_cache) log.error(emsg) raise KeyError(emsg) # save the load, since we don't have it - saveload_fstr = '{0}.save_load'.format(job_cache) + saveload_fstr = "{0}.save_load".format(job_cache) try: - mminion.returners[saveload_fstr](load['jid'], load) + mminion.returners[saveload_fstr](load["jid"], load) except KeyError: - emsg = "Returner '{0}' does not support function save_load".format(job_cache) + emsg = "Returner '{0}' does not support function save_load".format( + job_cache + ) log.error(emsg) raise KeyError(emsg) - elif salt.utils.jid.is_jid(load['jid']): + elif salt.utils.jid.is_jid(load["jid"]): # Store the jid - jidstore_fstr = '{0}.prep_jid'.format(job_cache) + jidstore_fstr = "{0}.prep_jid".format(job_cache) try: - mminion.returners[jidstore_fstr](False, passed_jid=load['jid']) + mminion.returners[jidstore_fstr](False, passed_jid=load["jid"]) except KeyError: emsg = "Returner '{0}' does not support function prep_jid".format(job_cache) log.error(emsg) @@ -65,33 +70,37 @@ def store_job(opts, load, event=None, mminion=None): if event: # If the return data is invalid, just ignore it - log.info('Got return from %s for job %s', load['id'], load['jid']) - event.fire_event(load, - salt.utils.event.tagify([load['jid'], 'ret', load['id']], 'job')) + log.info("Got return from %s for job %s", load["id"], load["jid"]) + event.fire_event( + load, salt.utils.event.tagify([load["jid"], "ret", load["id"]], "job") + ) event.fire_ret_load(load) # if you have a job_cache, or an ext_job_cache, don't write to # the regular master cache - if not opts['job_cache'] or opts.get('ext_job_cache'): + if not opts["job_cache"] or opts.get("ext_job_cache"): return # do not cache job results if explicitly requested - if load.get('jid') == 'nocache': - log.debug('Ignoring job return with jid for caching %s from %s', - load['jid'], load['id']) + if load.get("jid") == "nocache": + log.debug( + "Ignoring job return with jid for caching %s from %s", + load["jid"], + load["id"], + ) return # otherwise, write to the master cache - savefstr = '{0}.save_load'.format(job_cache) - getfstr = '{0}.get_load'.format(job_cache) - fstr = '{0}.returner'.format(job_cache) - updateetfstr = '{0}.update_endtime'.format(job_cache) - if 'fun' not in load and load.get('return', {}): - ret_ = load.get('return', {}) - if 'fun' in ret_: - load.update({'fun': ret_['fun']}) - if 'user' in ret_: - load.update({'user': ret_['user']}) + savefstr = "{0}.save_load".format(job_cache) + getfstr = "{0}.get_load".format(job_cache) + fstr = "{0}.returner".format(job_cache) + updateetfstr = "{0}.update_endtime".format(job_cache) + if "fun" not in load and load.get("return", {}): + ret_ = load.get("return", {}) + if "fun" in ret_: + load.update({"fun": ret_["fun"]}) + if "user" in ret_: + load.update({"user": ret_["user"]}) # Try to reach returner methods try: @@ -103,50 +112,48 @@ def store_job(opts, load, event=None, mminion=None): log.error(emsg) raise KeyError(emsg) - if job_cache != 'local_cache': + if job_cache != "local_cache": try: - mminion.returners[savefstr](load['jid'], load) + mminion.returners[savefstr](load["jid"], load) except KeyError as e: log.error("Load does not contain 'jid': %s", e) mminion.returners[fstr](load) - if (opts.get('job_cache_store_endtime') - and updateetfstr in mminion.returners): - mminion.returners[updateetfstr](load['jid'], endtime) + if opts.get("job_cache_store_endtime") and updateetfstr in mminion.returners: + mminion.returners[updateetfstr](load["jid"], endtime) def store_minions(opts, jid, minions, mminion=None, syndic_id=None): - ''' + """ Store additional minions matched on lower-level masters using the configured master_job_cache - ''' + """ if mminion is None: mminion = salt.minion.MasterMinion(opts, states=False, rend=False) - job_cache = opts['master_job_cache'] - minions_fstr = '{0}.save_minions'.format(job_cache) + job_cache = opts["master_job_cache"] + minions_fstr = "{0}.save_minions".format(job_cache) try: mminion.returners[minions_fstr](jid, minions, syndic_id=syndic_id) except KeyError: raise KeyError( - 'Returner \'{0}\' does not support function save_minions'.format( - job_cache - ) + "Returner '{0}' does not support function save_minions".format(job_cache) ) def get_retcode(ret): - ''' + """ Determine a retcode for a given return - ''' + """ retcode = 0 # if there is a dict with retcode, use that - if isinstance(ret, dict) and ret.get('retcode', 0) != 0: - return ret['retcode'] + if isinstance(ret, dict) and ret.get("retcode", 0) != 0: + return ret["retcode"] # if its a boolean, False means 1 elif isinstance(ret, bool) and not ret: return 1 return retcode + # vim:set et sts=4 ts=4 tw=80: diff --git a/salt/utils/json.py b/salt/utils/json.py index fd71544b546..18cbac8a759 100644 --- a/salt/utils/json.py +++ b/salt/utils/json.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Functions to work with JSON -''' +""" from __future__ import absolute_import, unicode_literals @@ -25,26 +25,26 @@ JSONEncoder = json.JSONEncoder def __split(raw): - ''' + """ Performs a splitlines on the string. This function exists to make mocking possible in unit tests, since the member functions of the str/unicode builtins cannot be mocked. - ''' + """ return raw.splitlines() def find_json(raw): - ''' + """ Pass in a raw string and load the json when it starts. This allows for a string to start with garbage and end with json but be cleanly loaded - ''' + """ ret = {} lines = __split(raw) for ind, _ in enumerate(lines): try: - working = '\n'.join(lines[ind:]) + working = "\n".join(lines[ind:]) except UnicodeDecodeError: - working = '\n'.join(salt.utils.data.decode(lines[ind:])) + working = "\n".join(salt.utils.data.decode(lines[ind:])) try: ret = json.loads(working) # future lint: blacklisted-function @@ -58,32 +58,32 @@ def find_json(raw): def import_json(): - ''' + """ Import a json module, starting with the quick ones and going down the list) - ''' - for fast_json in ('ujson', 'yajl', 'json'): + """ + for fast_json in ("ujson", "yajl", "json"): try: mod = __import__(fast_json) - log.trace('loaded %s json lib', fast_json) + log.trace("loaded %s json lib", fast_json) return mod except ImportError: continue def load(fp, **kwargs): - ''' + """ .. versionadded:: 2018.3.0 Wraps json.load You can pass an alternate json module (loaded via import_json() above) using the _json_module argument) - ''' - return kwargs.pop('_json_module', json).load(fp, **kwargs) + """ + return kwargs.pop("_json_module", json).load(fp, **kwargs) def loads(s, **kwargs): - ''' + """ .. versionadded:: 2018.3.0 Wraps json.loads and prevents a traceback in the event that a bytestring is @@ -91,8 +91,8 @@ def loads(s, **kwargs): You can pass an alternate json module (loaded via import_json() above) using the _json_module argument) - ''' - json_module = kwargs.pop('_json_module', json) + """ + json_module = kwargs.pop("_json_module", json) try: return json_module.loads(s, **kwargs) except TypeError as exc: @@ -104,7 +104,7 @@ def loads(s, **kwargs): def dump(obj, fp, **kwargs): - ''' + """ .. versionadded:: 2018.3.0 Wraps json.dump, and assumes that ensure_ascii is False (unless explicitly @@ -117,17 +117,17 @@ def dump(obj, fp, **kwargs): You can pass an alternate json module (loaded via import_json() above) using the _json_module argument) - ''' - json_module = kwargs.pop('_json_module', json) - if 'ensure_ascii' not in kwargs: - kwargs['ensure_ascii'] = False + """ + json_module = kwargs.pop("_json_module", json) + if "ensure_ascii" not in kwargs: + kwargs["ensure_ascii"] = False if six.PY2: obj = salt.utils.data.encode(obj) return json_module.dump(obj, fp, **kwargs) # future lint: blacklisted-function def dumps(obj, **kwargs): - ''' + """ .. versionadded:: 2018.3.0 Wraps json.dumps, and assumes that ensure_ascii is False (unless explicitly @@ -140,10 +140,10 @@ def dumps(obj, **kwargs): You can pass an alternate json module (loaded via import_json() above) using the _json_module argument) - ''' - json_module = kwargs.pop('_json_module', json) - if 'ensure_ascii' not in kwargs: - kwargs['ensure_ascii'] = False + """ + json_module = kwargs.pop("_json_module", json) + if "ensure_ascii" not in kwargs: + kwargs["ensure_ascii"] = False if six.PY2: obj = salt.utils.data.encode(obj) return json_module.dumps(obj, **kwargs) # future lint: blacklisted-function diff --git a/salt/utils/kickstart.py b/salt/utils/kickstart.py index c291b533635..5d88f4f144d 100644 --- a/salt/utils/kickstart.py +++ b/salt/utils/kickstart.py @@ -1,21 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Utilities for managing kickstart .. versionadded:: Beryllium -''' +""" from __future__ import absolute_import, unicode_literals -import shlex + import argparse # pylint: disable=minimum-python-version +import shlex + import salt.utils.files import salt.utils.yaml from salt.ext.six.moves import range def clean_args(args): - ''' + """ Cleans up the args that weren't passed in - ''' + """ for arg in args: if not args[arg]: del args[arg] @@ -23,60 +25,93 @@ def clean_args(args): def parse_auth(rule): - ''' + """ Parses the auth/authconfig line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - noargs = ('back', 'test', 'nostart', 'kickstart', 'probe', 'enablecache', - 'disablecache', 'disablenis', 'enableshadow', 'disableshadow', - 'enablemd5', 'disablemd5', 'enableldap', 'enableldapauth', - 'enableldaptls', 'disableldap', 'disableldapauth', - 'enablekrb5kdcdns', 'disablekrb5kdcdns', 'enablekrb5realmdns', - 'disablekrb5realmdns', 'disablekrb5', 'disablehe-siod', - 'enablesmbauth', 'disablesmbauth', 'enablewinbind', - 'enablewinbindauth', 'disablewinbind', 'disablewinbindauth', - 'enablewinbindusedefaultdomain', 'disablewinbindusedefaultdomain', - 'enablewins', 'disablewins') + noargs = ( + "back", + "test", + "nostart", + "kickstart", + "probe", + "enablecache", + "disablecache", + "disablenis", + "enableshadow", + "disableshadow", + "enablemd5", + "disablemd5", + "enableldap", + "enableldapauth", + "enableldaptls", + "disableldap", + "disableldapauth", + "enablekrb5kdcdns", + "disablekrb5kdcdns", + "enablekrb5realmdns", + "disablekrb5realmdns", + "disablekrb5", + "disablehe-siod", + "enablesmbauth", + "disablesmbauth", + "enablewinbind", + "enablewinbindauth", + "disablewinbind", + "disablewinbindauth", + "enablewinbindusedefaultdomain", + "disablewinbindusedefaultdomain", + "enablewins", + "disablewins", + ) for arg in noargs: - parser.add_argument('--{0}'.format(arg), dest=arg, action='store_true') + parser.add_argument("--{0}".format(arg), dest=arg, action="store_true") - parser.add_argument('--enablenis', dest='enablenis', action='store') - parser.add_argument('--hesiodrhs', dest='hesiodrhs', action='store') - parser.add_argument('--krb5adminserver', dest='krb5adminserver', - action='append') - parser.add_argument('--krb5kdc', dest='krb5kdc', action='append') - parser.add_argument('--ldapbasedn', dest='ldapbasedn', action='store') - parser.add_argument('--ldapserver', dest='ldapserver', action='append') - parser.add_argument('--nisserver', dest='nisserver', action='append') - parser.add_argument('--passalgo', dest='passalgo', action='store') - parser.add_argument('--smbidmapgid', dest='smbidmapgid', action='store') - parser.add_argument('--smbidmapuid', dest='smbidmapuid', action='store') - parser.add_argument('--smbrealm', dest='smbrealm', action='store') - parser.add_argument('--smbsecurity', dest='smbsecurity', action='store', - choices=['user', 'server', 'domain', 'dns']) - parser.add_argument('--smbservers', dest='smbservers', action='store') - parser.add_argument('--smbworkgroup', dest='smbworkgroup', action='store') - parser.add_argument('--winbindjoin', dest='winbindjoin', action='store') - parser.add_argument('--winbindseparator', dest='winbindseparator', - action='store') - parser.add_argument('--winbindtemplatehomedir', - dest='winbindtemplatehomedir', action='store') - parser.add_argument('--winbindtemplateprimarygroup', - dest='winbindtemplateprimarygroup', action='store') - parser.add_argument('--winbindtemplateshell', dest='winbindtemplateshell', - action='store') + parser.add_argument("--enablenis", dest="enablenis", action="store") + parser.add_argument("--hesiodrhs", dest="hesiodrhs", action="store") + parser.add_argument("--krb5adminserver", dest="krb5adminserver", action="append") + parser.add_argument("--krb5kdc", dest="krb5kdc", action="append") + parser.add_argument("--ldapbasedn", dest="ldapbasedn", action="store") + parser.add_argument("--ldapserver", dest="ldapserver", action="append") + parser.add_argument("--nisserver", dest="nisserver", action="append") + parser.add_argument("--passalgo", dest="passalgo", action="store") + parser.add_argument("--smbidmapgid", dest="smbidmapgid", action="store") + parser.add_argument("--smbidmapuid", dest="smbidmapuid", action="store") + parser.add_argument("--smbrealm", dest="smbrealm", action="store") + parser.add_argument( + "--smbsecurity", + dest="smbsecurity", + action="store", + choices=["user", "server", "domain", "dns"], + ) + parser.add_argument("--smbservers", dest="smbservers", action="store") + parser.add_argument("--smbworkgroup", dest="smbworkgroup", action="store") + parser.add_argument("--winbindjoin", dest="winbindjoin", action="store") + parser.add_argument("--winbindseparator", dest="winbindseparator", action="store") + parser.add_argument( + "--winbindtemplatehomedir", dest="winbindtemplatehomedir", action="store" + ) + parser.add_argument( + "--winbindtemplateprimarygroup", + dest="winbindtemplateprimarygroup", + action="store", + ) + parser.add_argument( + "--winbindtemplateshell", dest="winbindtemplateshell", action="store" + ) - parser.add_argument('--enablekrb5', dest='enablekrb5', action='store_true') - if '--enablekrb5' in rules: - parser.add_argument('--krb5realm', dest='krb5realm', action='store', - required=True) - parser.add_argument('--enablehesiod', dest='enablehesiod', - action='store_true') - if '--enablehesiod' in rules: - parser.add_argument('--hesiodlhs', dest='hesiodlhs', action='store', - required=True) + parser.add_argument("--enablekrb5", dest="enablekrb5", action="store_true") + if "--enablekrb5" in rules: + parser.add_argument( + "--krb5realm", dest="krb5realm", action="store", required=True + ) + parser.add_argument("--enablehesiod", dest="enablehesiod", action="store_true") + if "--enablehesiod" in rules: + parser.add_argument( + "--hesiodlhs", dest="hesiodlhs", action="store", required=True + ) args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -84,18 +119,17 @@ def parse_auth(rule): def parse_autopart(rule): - ''' + """ Parse the autopart line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--type', dest='type', action='store') - parser.add_argument('--encrypted', dest='encrypted', action='store_true') - parser.add_argument('--passphrase', dest='passphrase', action='store') - parser.add_argument('--escrowcert', dest='escrowcert', action='store') - parser.add_argument('--backuppassphrase', dest='backuppassphrase', - action='store') + parser.add_argument("--type", dest="type", action="store") + parser.add_argument("--encrypted", dest="encrypted", action="store_true") + parser.add_argument("--passphrase", dest="passphrase", action="store") + parser.add_argument("--escrowcert", dest="escrowcert", action="store") + parser.add_argument("--backuppassphrase", dest="backuppassphrase", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -103,14 +137,13 @@ def parse_autopart(rule): def parse_autostep(rule): - ''' + """ Parse the autostep line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--autoscreenshot', dest='autoscreenshot', - action='store') + parser.add_argument("--autoscreenshot", dest="autoscreenshot", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -118,20 +151,20 @@ def parse_autostep(rule): def parse_bootloader(rule): - ''' + """ Parse the bootloader line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--append', dest='append', action='store') - parser.add_argument('--driveorder', dest='driveorder', action='store') - parser.add_argument('--location', dest='location', action='store') - parser.add_argument('--password', dest='password', action='store') - parser.add_argument('--md5pass', dest='md5pass', action='store') - parser.add_argument('--upgrade', dest='upgrade', action='store_true') - parser.add_argument('--timeout', dest='timeout', action='store') - parser.add_argument('--boot-drive', dest='bootdrive', action='store') + parser.add_argument("--append", dest="append", action="store") + parser.add_argument("--driveorder", dest="driveorder", action="store") + parser.add_argument("--location", dest="location", action="store") + parser.add_argument("--password", dest="password", action="store") + parser.add_argument("--md5pass", dest="md5pass", action="store") + parser.add_argument("--upgrade", dest="upgrade", action="store_true") + parser.add_argument("--timeout", dest="timeout", action="store") + parser.add_argument("--boot-drive", dest="bootdrive", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -139,23 +172,22 @@ def parse_bootloader(rule): def parse_btrfs(rule): - ''' + """ Parse the btrfs line TODO: finish up the weird parsing on this one http://fedoraproject.org/wiki/Anaconda/Kickstart#btrfs - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--name', dest='name', action='store') - parser.add_argument('--data', dest='data', action='store') - parser.add_argument('--metadata', dest='metadata', action='store') - parser.add_argument('--label', dest='label', action='store') - parser.add_argument('--noformat', dest='noformat', action='store_true') - parser.add_argument('--useexisting', dest='useexisting', - action='store_true') - parser.add_argument('--subvol', dest='subvol', action='store_true') + parser.add_argument("--name", dest="name", action="store") + parser.add_argument("--data", dest="data", action="store") + parser.add_argument("--metadata", dest="metadata", action="store") + parser.add_argument("--label", dest="label", action="store") + parser.add_argument("--noformat", dest="noformat", action="store_true") + parser.add_argument("--useexisting", dest="useexisting", action="store_true") + parser.add_argument("--subvol", dest="subvol", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -163,19 +195,19 @@ def parse_btrfs(rule): def parse_clearpart(rule): - ''' + """ Parse the clearpart line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--all', dest='all', action='store_true') - parser.add_argument('--drives', dest='drives', action='store') - parser.add_argument('--init_label', dest='init_label', action='store_true') - parser.add_argument('--linux', dest='linux', action='store_true') - parser.add_argument('--none', dest='none', action='store_true') - parser.add_argument('--initlabel', dest='init_label', action='store_true') - parser.add_argument('--list', dest='list', action='store') + parser.add_argument("--all", dest="all", action="store_true") + parser.add_argument("--drives", dest="drives", action="store") + parser.add_argument("--init_label", dest="init_label", action="store_true") + parser.add_argument("--linux", dest="linux", action="store_true") + parser.add_argument("--none", dest="none", action="store_true") + parser.add_argument("--initlabel", dest="init_label", action="store_true") + parser.add_argument("--list", dest="list", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -183,30 +215,30 @@ def parse_clearpart(rule): def parse_device(rule): - ''' + """ Parse the device line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) modulename = rules.pop(0) - parser.add_argument('--opts', dest='opts', action='store') + parser.add_argument("--opts", dest="opts", action="store") args = clean_args(vars(parser.parse_args(rules))) - args['modulename'] = modulename + args["modulename"] = modulename parser = None return args def parse_dmraid(rule): - ''' + """ Parse the dmraid line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--name', dest='name', action='store') - parser.add_argument('--dev', dest='dev', action='store') + parser.add_argument("--name", dest="name", action="store") + parser.add_argument("--dev", dest="dev", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -214,17 +246,17 @@ def parse_dmraid(rule): def parse_driverdisk(rule): - ''' + """ Parse the driverdisk line - ''' - if '--' not in rule: - return {'partition': rule} + """ + if "--" not in rule: + return {"partition": rule} parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--source', dest='source', action='store') - parser.add_argument('--biospart', dest='biospart', action='store') + parser.add_argument("--source", dest="source", action="store") + parser.add_argument("--biospart", dest="biospart", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -232,22 +264,20 @@ def parse_driverdisk(rule): def parse_firewall(rule): - ''' + """ Parse the firewall line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--enable', '--enabled', dest='enable', - action='store_true') - parser.add_argument('--disable', '--disabled', dest='disable', - action='store_true') - parser.add_argument('--port', dest='port', action='store') - parser.add_argument('--service', dest='service', action='store') - parser.add_argument('--ssh', dest='ssh', action='store_true') - parser.add_argument('--smtp', dest='smtp', action='store_true') - parser.add_argument('--http', dest='http', action='store_true') - parser.add_argument('--ftp', dest='ftp', action='store_true') + parser.add_argument("--enable", "--enabled", dest="enable", action="store_true") + parser.add_argument("--disable", "--disabled", dest="disable", action="store_true") + parser.add_argument("--port", dest="port", action="store") + parser.add_argument("--service", dest="service", action="store") + parser.add_argument("--ssh", dest="ssh", action="store_true") + parser.add_argument("--smtp", dest="smtp", action="store_true") + parser.add_argument("--http", dest="http", action="store_true") + parser.add_argument("--ftp", dest="ftp", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -255,17 +285,15 @@ def parse_firewall(rule): def parse_firstboot(rule): - ''' + """ Parse the firstboot line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--enable', '--enabled', dest='enable', - action='store_true') - parser.add_argument('--disable', '--disabled', dest='disable', - action='store_true') - parser.add_argument('--reconfig', dest='reconfig', action='store_true') + parser.add_argument("--enable", "--enabled", dest="enable", action="store_true") + parser.add_argument("--disable", "--disabled", dest="disable", action="store_true") + parser.add_argument("--reconfig", dest="reconfig", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -273,14 +301,14 @@ def parse_firstboot(rule): def parse_group(rule): - ''' + """ Parse the group line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--name', dest='name', action='store') - parser.add_argument('--gid', dest='gid', action='store') + parser.add_argument("--name", dest="name", action="store") + parser.add_argument("--gid", dest="gid", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -288,15 +316,15 @@ def parse_group(rule): def parse_harddrive(rule): - ''' + """ Parse the harddrive line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--biospart', dest='biospart', action='store') - parser.add_argument('--partition', dest='partition', action='store') - parser.add_argument('--dir', dest='dir', action='store') + parser.add_argument("--biospart", dest="biospart", action="store") + parser.add_argument("--partition", dest="partition", action="store") + parser.add_argument("--dir", dest="dir", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -304,16 +332,15 @@ def parse_harddrive(rule): def parse_ignoredisk(rule): - ''' + """ Parse the ignoredisk line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--drives', dest='drives', action='store') - parser.add_argument('--only-use', dest='only-use', action='store') - parser.add_argument('--interactive', dest='interactive', - action='store_true') + parser.add_argument("--drives", dest="drives", action="store") + parser.add_argument("--only-use", dest="only-use", action="store") + parser.add_argument("--interactive", dest="interactive", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -321,21 +348,20 @@ def parse_ignoredisk(rule): def parse_iscsi(rule): - ''' + """ Parse the iscsi line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--ipaddr', dest='ipaddr', action='store') - parser.add_argument('--port', dest='port', action='store') - parser.add_argument('--target', dest='target', action='store') - parser.add_argument('--iface', dest='iface', action='store') - parser.add_argument('--user', dest='user', action='store') - parser.add_argument('--password', dest='password', action='store') - parser.add_argument('--reverse-user', dest='reverse-user', action='store') - parser.add_argument('--reverse-password', dest='reverse-password', - action='store') + parser.add_argument("--ipaddr", dest="ipaddr", action="store") + parser.add_argument("--port", dest="port", action="store") + parser.add_argument("--target", dest="target", action="store") + parser.add_argument("--iface", dest="iface", action="store") + parser.add_argument("--user", dest="user", action="store") + parser.add_argument("--password", dest="password", action="store") + parser.add_argument("--reverse-user", dest="reverse-user", action="store") + parser.add_argument("--reverse-password", dest="reverse-password", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -343,13 +369,13 @@ def parse_iscsi(rule): def parse_iscsiname(rule): - ''' + """ Parse the iscsiname line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - #parser.add_argument('iqn') + # parser.add_argument('iqn') args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -357,34 +383,34 @@ def parse_iscsiname(rule): def parse_keyboard(rule): - ''' + """ Parse the keyboard line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--vckeymap', dest='vckeymap', action='store') - parser.add_argument('--xlayouts', dest='xlayouts', action='store') - parser.add_argument('--switch', dest='switch', action='store') - parser.add_argument('keyboard') + parser.add_argument("--vckeymap", dest="vckeymap", action="store") + parser.add_argument("--xlayouts", dest="xlayouts", action="store") + parser.add_argument("--switch", dest="switch", action="store") + parser.add_argument("keyboard") args = clean_args(vars(parser.parse_args(rules))) - if 'keyboard' in args and 'xlayouts' not in args: - args['xlayouts'] = args['keyboard'] + if "keyboard" in args and "xlayouts" not in args: + args["xlayouts"] = args["keyboard"] parser = None return args def parse_lang(rule): - ''' + """ Parse the lang line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('lang') + parser.add_argument("lang") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -392,32 +418,31 @@ def parse_lang(rule): def parse_logvol(rule): - ''' + """ Parse the logvol line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('mntpoint') - parser.add_argument('--noformat', dest='noformat', action='store_true') - parser.add_argument('--useexisting', dest='useexisting', - action='store_true') - parser.add_argument('--fstype', dest='fstype', action='store') - parser.add_argument('--fsoptions', dest='fsoptions', action='store') - parser.add_argument('--grow', dest='grow', action='store_true') - parser.add_argument('--maxsize', dest='maxsize', action='store') - parser.add_argument('--recommended', dest='recommended', - action='store_true') - parser.add_argument('--percent', dest='percent', action='store_true') - parser.add_argument('--encrypted', dest='encrypted', action='store_true') - parser.add_argument('--passphrase', dest='passphrase', action='store') - parser.add_argument('--escrowcert', dest='escrowcert', action='store') - parser.add_argument('--backuppassphrase', dest='backuppassphrase', - action='store_true') - parser.add_argument('--name', dest='name', action='store') - parser.add_argument('--vgname', dest='vgname', action='store') - parser.add_argument('--size', dest='size', action='store') - parser.add_argument('--label', dest='label', action='store') + parser.add_argument("mntpoint") + parser.add_argument("--noformat", dest="noformat", action="store_true") + parser.add_argument("--useexisting", dest="useexisting", action="store_true") + parser.add_argument("--fstype", dest="fstype", action="store") + parser.add_argument("--fsoptions", dest="fsoptions", action="store") + parser.add_argument("--grow", dest="grow", action="store_true") + parser.add_argument("--maxsize", dest="maxsize", action="store") + parser.add_argument("--recommended", dest="recommended", action="store_true") + parser.add_argument("--percent", dest="percent", action="store_true") + parser.add_argument("--encrypted", dest="encrypted", action="store_true") + parser.add_argument("--passphrase", dest="passphrase", action="store") + parser.add_argument("--escrowcert", dest="escrowcert", action="store") + parser.add_argument( + "--backuppassphrase", dest="backuppassphrase", action="store_true" + ) + parser.add_argument("--name", dest="name", action="store") + parser.add_argument("--vgname", dest="vgname", action="store") + parser.add_argument("--size", dest="size", action="store") + parser.add_argument("--label", dest="label", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -425,17 +450,20 @@ def parse_logvol(rule): def parse_logging(rule): - ''' + """ Parse the logging line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--host', dest='host', action='store') - parser.add_argument('--port', dest='port', action='store') - parser.add_argument('--level', dest='level', action='store', - choices=['debug', 'info', 'warning', 'error', - 'critical']) + parser.add_argument("--host", dest="host", action="store") + parser.add_argument("--port", dest="port", action="store") + parser.add_argument( + "--level", + dest="level", + action="store", + choices=["debug", "info", "warning", "error", "critical"], + ) args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -443,16 +471,16 @@ def parse_logging(rule): def parse_monitor(rule): - ''' + """ Parse the monitor line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--hsync', dest='hsync', action='store') - parser.add_argument('--monitor', dest='monitor', action='store') - parser.add_argument('--noprobe', dest='noprobe', action='store_true') - parser.add_argument('--vsync', dest='vsync', action='store') + parser.add_argument("--hsync", dest="hsync", action="store") + parser.add_argument("--monitor", dest="monitor", action="store") + parser.add_argument("--noprobe", dest="noprobe", action="store_true") + parser.add_argument("--vsync", dest="vsync", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -460,15 +488,15 @@ def parse_monitor(rule): def parse_multipath(rule): - ''' + """ Parse the multipath line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--name', dest='name', action='store') - parser.add_argument('--device', dest='device', action='store') - parser.add_argument('--rule', dest='rule', action='store') + parser.add_argument("--name", dest="name", action="store") + parser.add_argument("--device", dest="device", action="store") + parser.add_argument("--rule", dest="rule", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -476,33 +504,37 @@ def parse_multipath(rule): def parse_network(rule): - ''' + """ Parse the network line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--bootproto', dest='bootproto', action='store', - choices=['dhcp', 'bootp', 'static', 'ibft']) - parser.add_argument('--device', dest='device', action='store') - parser.add_argument('--ip', dest='ip', action='store') - parser.add_argument('--ipv6', dest='ipv6', action='store') - parser.add_argument('--gateway', dest='gateway', action='store') - parser.add_argument('--nodefroute', dest='nodefroute', action='store_true') - parser.add_argument('--nameserver', dest='nameserver', action='store') - parser.add_argument('--nodns', dest='nodns', action='store_true') - parser.add_argument('--netmask', dest='netmask', action='store') - parser.add_argument('--hostname', dest='hostname', action='store') - parser.add_argument('--ethtool', dest='ethtool', action='store') - parser.add_argument('--essid', dest='essid', action='store') - parser.add_argument('--wepkey', dest='wepkey', action='store') - parser.add_argument('--wpakey', dest='wpakey', action='store') - parser.add_argument('--onboot', dest='onboot', action='store') - parser.add_argument('--dhcpclass', dest='dhcpclass', action='store') - parser.add_argument('--mtu', dest='mtu', action='store') - parser.add_argument('--noipv4', dest='noipv4', action='store_true') - parser.add_argument('--noipv6', dest='noipv6', action='store_true') - parser.add_argument('--activate', dest='activate', action='store_true') + parser.add_argument( + "--bootproto", + dest="bootproto", + action="store", + choices=["dhcp", "bootp", "static", "ibft"], + ) + parser.add_argument("--device", dest="device", action="store") + parser.add_argument("--ip", dest="ip", action="store") + parser.add_argument("--ipv6", dest="ipv6", action="store") + parser.add_argument("--gateway", dest="gateway", action="store") + parser.add_argument("--nodefroute", dest="nodefroute", action="store_true") + parser.add_argument("--nameserver", dest="nameserver", action="store") + parser.add_argument("--nodns", dest="nodns", action="store_true") + parser.add_argument("--netmask", dest="netmask", action="store") + parser.add_argument("--hostname", dest="hostname", action="store") + parser.add_argument("--ethtool", dest="ethtool", action="store") + parser.add_argument("--essid", dest="essid", action="store") + parser.add_argument("--wepkey", dest="wepkey", action="store") + parser.add_argument("--wpakey", dest="wpakey", action="store") + parser.add_argument("--onboot", dest="onboot", action="store") + parser.add_argument("--dhcpclass", dest="dhcpclass", action="store") + parser.add_argument("--mtu", dest="mtu", action="store") + parser.add_argument("--noipv4", dest="noipv4", action="store_true") + parser.add_argument("--noipv6", dest="noipv6", action="store_true") + parser.add_argument("--activate", dest="activate", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -510,15 +542,15 @@ def parse_network(rule): def parse_nfs(rule): - ''' + """ Parse the nfs line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--server', dest='server', action='store') - parser.add_argument('--dir', dest='dir', action='store') - parser.add_argument('--opts', dest='opts', action='store') + parser.add_argument("--server", dest="server", action="store") + parser.add_argument("--dir", dest="dir", action="store") + parser.add_argument("--opts", dest="opts", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -526,31 +558,30 @@ def parse_nfs(rule): def parse_partition(rule): - ''' + """ Parse the partition line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('mntpoint') - parser.add_argument('--size', dest='size', action='store') - parser.add_argument('--grow', dest='grow', action='store_true') - parser.add_argument('--maxsize', dest='maxsize', action='store') - parser.add_argument('--noformat', dest='noformat', action='store_true') - parser.add_argument('--onpart', '--usepart', dest='onpart', action='store') - parser.add_argument('--ondisk', '--ondrive', dest='ondisk', action='store') - parser.add_argument('--asprimary', dest='asprimary', action='store_true') - parser.add_argument('--fsprofile', dest='fsprofile', action='store') - parser.add_argument('--fstype', dest='fstype', action='store') - parser.add_argument('--fsoptions', dest='fsoptions', action='store') - parser.add_argument('--label', dest='label', action='store') - parser.add_argument('--recommended', dest='recommended', - action='store_true') - parser.add_argument('--onbiosdisk', dest='onbiosdisk', action='store') - parser.add_argument('--encrypted', dest='encrypted', action='store_true') - parser.add_argument('--passphrase', dest='passphrase', action='store') - parser.add_argument('--escrowcert', dest='escrowcert', action='store') - parser.add_argument('--backupphrase', dest='backupphrase', action='store') + parser.add_argument("mntpoint") + parser.add_argument("--size", dest="size", action="store") + parser.add_argument("--grow", dest="grow", action="store_true") + parser.add_argument("--maxsize", dest="maxsize", action="store") + parser.add_argument("--noformat", dest="noformat", action="store_true") + parser.add_argument("--onpart", "--usepart", dest="onpart", action="store") + parser.add_argument("--ondisk", "--ondrive", dest="ondisk", action="store") + parser.add_argument("--asprimary", dest="asprimary", action="store_true") + parser.add_argument("--fsprofile", dest="fsprofile", action="store") + parser.add_argument("--fstype", dest="fstype", action="store") + parser.add_argument("--fsoptions", dest="fsoptions", action="store") + parser.add_argument("--label", dest="label", action="store") + parser.add_argument("--recommended", dest="recommended", action="store_true") + parser.add_argument("--onbiosdisk", dest="onbiosdisk", action="store") + parser.add_argument("--encrypted", dest="encrypted", action="store_true") + parser.add_argument("--passphrase", dest="passphrase", action="store") + parser.add_argument("--escrowcert", dest="escrowcert", action="store") + parser.add_argument("--backupphrase", dest="backupphrase", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -558,9 +589,9 @@ def parse_partition(rule): def parse_raid(rule): - ''' + """ Parse the raid line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) @@ -571,44 +602,42 @@ def parse_raid(rule): if count == 0: newrules.append(rules[count]) continue - elif rules[count].startswith('--'): + elif rules[count].startswith("--"): newrules.append(rules[count]) continue else: partitions.append(rules[count]) rules = newrules - parser.add_argument('mntpoint') - parser.add_argument('--level', dest='level', action='store') - parser.add_argument('--device', dest='device', action='store') - parser.add_argument('--spares', dest='spares', action='store') - parser.add_argument('--fstype', dest='fstype', action='store') - parser.add_argument('--fsoptions', dest='fsoptions', action='store') - parser.add_argument('--label', dest='label', action='store') - parser.add_argument('--noformat', dest='noformat', action='store_true') - parser.add_argument('--useexisting', dest='useexisting', - action='store_true') - parser.add_argument('--encrypted', dest='encrypted', action='store_true') - parser.add_argument('--passphrase', dest='passphrase', action='store') - parser.add_argument('--escrowcert', dest='escrowcert', action='store') - parser.add_argument('--backuppassphrase', dest='backuppassphrase', - action='store') + parser.add_argument("mntpoint") + parser.add_argument("--level", dest="level", action="store") + parser.add_argument("--device", dest="device", action="store") + parser.add_argument("--spares", dest="spares", action="store") + parser.add_argument("--fstype", dest="fstype", action="store") + parser.add_argument("--fsoptions", dest="fsoptions", action="store") + parser.add_argument("--label", dest="label", action="store") + parser.add_argument("--noformat", dest="noformat", action="store_true") + parser.add_argument("--useexisting", dest="useexisting", action="store_true") + parser.add_argument("--encrypted", dest="encrypted", action="store_true") + parser.add_argument("--passphrase", dest="passphrase", action="store") + parser.add_argument("--escrowcert", dest="escrowcert", action="store") + parser.add_argument("--backuppassphrase", dest="backuppassphrase", action="store") args = clean_args(vars(parser.parse_args(rules))) if partitions: - args['partitions'] = partitions + args["partitions"] = partitions parser = None return args def parse_reboot(rule): - ''' + """ Parse the reboot line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--eject', dest='eject', action='store_true') + parser.add_argument("--eject", dest="eject", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -616,22 +645,21 @@ def parse_reboot(rule): def parse_repo(rule): - ''' + """ Parse the repo line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--name', dest='name', action='store') - parser.add_argument('--baseurl', dest='baseurl', action='store') - parser.add_argument('--mirrorlist', dest='mirrorlist', action='store') - parser.add_argument('--cost', dest='cost', action='store') - parser.add_argument('--excludepkgs', dest='excludepkgs', action='store') - parser.add_argument('--includepkgs', dest='includepkgs', action='store') - parser.add_argument('--proxy', dest='proxy', action='store') - parser.add_argument('--ignoregroups', dest='ignoregroups', action='store') - parser.add_argument('--noverifyssl', dest='noverifyssl', - action='store_true') + parser.add_argument("--name", dest="name", action="store") + parser.add_argument("--baseurl", dest="baseurl", action="store") + parser.add_argument("--mirrorlist", dest="mirrorlist", action="store") + parser.add_argument("--cost", dest="cost", action="store") + parser.add_argument("--excludepkgs", dest="excludepkgs", action="store") + parser.add_argument("--includepkgs", dest="includepkgs", action="store") + parser.add_argument("--proxy", dest="proxy", action="store") + parser.add_argument("--ignoregroups", dest="ignoregroups", action="store") + parser.add_argument("--noverifyssl", dest="noverifyssl", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -639,14 +667,14 @@ def parse_repo(rule): def parse_rescue(rule): - ''' + """ Parse the rescue line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--nomount', dest='nomount', action='store_true') - parser.add_argument('--romount', dest='romount', action='store_true') + parser.add_argument("--nomount", dest="nomount", action="store_true") + parser.add_argument("--romount", dest="romount", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -654,16 +682,16 @@ def parse_rescue(rule): def parse_rootpw(rule): - ''' + """ Parse the rootpw line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--iscrypted', dest='iscrypted', action='store_true') - parser.add_argument('--plaintext', dest='plaintext', action='store_true') - parser.add_argument('--lock', dest='lock', action='store_true') - parser.add_argument('password') + parser.add_argument("--iscrypted", dest="iscrypted", action="store_true") + parser.add_argument("--plaintext", dest="plaintext", action="store_true") + parser.add_argument("--lock", dest="lock", action="store_true") + parser.add_argument("password") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -671,15 +699,15 @@ def parse_rootpw(rule): def parse_selinux(rule): - ''' + """ Parse the selinux line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--disabled', dest='disabled', action='store_true') - parser.add_argument('--enforcing', dest='enforcing', action='store_true') - parser.add_argument('--permissive', dest='permissive', action='store_true') + parser.add_argument("--disabled", dest="disabled", action="store_true") + parser.add_argument("--enforcing", dest="enforcing", action="store_true") + parser.add_argument("--permissive", dest="permissive", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -687,14 +715,14 @@ def parse_selinux(rule): def parse_services(rule): - ''' + """ Parse the services line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--disabled', dest='disabled', action='store') - parser.add_argument('--enabled', dest='enabled', action='store') + parser.add_argument("--disabled", dest="disabled", action="store") + parser.add_argument("--enabled", dest="enabled", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -702,16 +730,16 @@ def parse_services(rule): def parse_sshpw(rule): - ''' + """ Parse the sshpw line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--username', dest='username', action='store') - parser.add_argument('--iscrypted', dest='iscrypted', action='store_true') - parser.add_argument('--plaintext', dest='plaintext', action='store_true') - parser.add_argument('--lock', dest='lock', action='store_true') + parser.add_argument("--username", dest="username", action="store") + parser.add_argument("--iscrypted", dest="iscrypted", action="store_true") + parser.add_argument("--plaintext", dest="plaintext", action="store_true") + parser.add_argument("--lock", dest="lock", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -719,17 +747,17 @@ def parse_sshpw(rule): def parse_timezone(rule): - ''' + """ Parse the timezone line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--utc', dest='utc', action='store_true') - parser.add_argument('--nontp', dest='nontp', action='store_true') - parser.add_argument('--ntpservers', dest='ntpservers', action='store') - parser.add_argument('--isUtc', dest='isutc', action='store_true') - parser.add_argument('timezone') + parser.add_argument("--utc", dest="utc", action="store_true") + parser.add_argument("--nontp", dest="nontp", action="store_true") + parser.add_argument("--ntpservers", dest="ntpservers", action="store") + parser.add_argument("--isUtc", dest="isutc", action="store_true") + parser.add_argument("timezone") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -737,25 +765,25 @@ def parse_timezone(rule): def parse_updates(rule): - ''' + """ Parse the updates line - ''' + """ rules = shlex.split(rule) rules.pop(0) if len(rules) > 0: - return {'url': rules[0]} + return {"url": rules[0]} else: return True def parse_upgrade(rule): - ''' + """ Parse the upgrade line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--root-device', dest='root-device', action='store') + parser.add_argument("--root-device", dest="root-device", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -765,16 +793,15 @@ def parse_upgrade(rule): def parse_url(rule): - ''' + """ Parse the url line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--url', dest='url', action='store') - parser.add_argument('--proxy', dest='proxy', action='store') - parser.add_argument('--noverifyssl', dest='noverifyssl', - action='store_true') + parser.add_argument("--url", dest="url", action="store") + parser.add_argument("--proxy", dest="proxy", action="store") + parser.add_argument("--noverifyssl", dest="noverifyssl", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -782,22 +809,22 @@ def parse_url(rule): def parse_user(rule): - ''' + """ Parse the user line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--name', dest='name', action='store') - parser.add_argument('--gecos', dest='gecos', action='store') - parser.add_argument('--groups', dest='groups', action='store') - parser.add_argument('--homedir', dest='homedir', action='store') - parser.add_argument('--lock', dest='lock', action='store_true') - parser.add_argument('--password', dest='password', action='store') - parser.add_argument('--iscrypted', dest='iscrypted', action='store_true') - parser.add_argument('--plaintext', dest='plaintext', action='store_true') - parser.add_argument('--shell', dest='shell', action='store') - parser.add_argument('--uid', dest='uid', action='store') + parser.add_argument("--name", dest="name", action="store") + parser.add_argument("--gecos", dest="gecos", action="store") + parser.add_argument("--groups", dest="groups", action="store") + parser.add_argument("--homedir", dest="homedir", action="store") + parser.add_argument("--lock", dest="lock", action="store_true") + parser.add_argument("--password", dest="password", action="store") + parser.add_argument("--iscrypted", dest="iscrypted", action="store_true") + parser.add_argument("--plaintext", dest="plaintext", action="store_true") + parser.add_argument("--shell", dest="shell", action="store") + parser.add_argument("--uid", dest="uid", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -805,15 +832,15 @@ def parse_user(rule): def parse_vnc(rule): - ''' + """ Parse the vnc line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--host', dest='host', action='store') - parser.add_argument('--port', dest='port', action='store') - parser.add_argument('--password', dest='password', action='store') + parser.add_argument("--host", dest="host", action="store") + parser.add_argument("--port", dest="port", action="store") + parser.add_argument("--password", dest="password", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -821,9 +848,9 @@ def parse_vnc(rule): def parse_volgroup(rule): - ''' + """ Parse the volgroup line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) @@ -834,41 +861,36 @@ def parse_volgroup(rule): if count == 0: newrules.append(rules[count]) continue - elif rules[count].startswith('--'): + elif rules[count].startswith("--"): newrules.append(rules[count]) continue else: partitions.append(rules[count]) rules = newrules - parser.add_argument('name') - parser.add_argument('--noformat', dest='noformat', action='store_true') - parser.add_argument('--useexisting', dest='useexisting', - action='store_true') - parser.add_argument('--pesize', dest='pesize', action='store') - parser.add_argument('--reserved-space', dest='reserved-space', - action='store') - parser.add_argument('--reserved-percent', dest='reserved-percent', - action='store') + parser.add_argument("name") + parser.add_argument("--noformat", dest="noformat", action="store_true") + parser.add_argument("--useexisting", dest="useexisting", action="store_true") + parser.add_argument("--pesize", dest="pesize", action="store") + parser.add_argument("--reserved-space", dest="reserved-space", action="store") + parser.add_argument("--reserved-percent", dest="reserved-percent", action="store") args = clean_args(vars(parser.parse_args(rules))) if partitions: - args['partitions'] = partitions + args["partitions"] = partitions parser = None return args def parse_xconfig(rule): - ''' + """ Parse the xconfig line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--defaultdesktop', dest='defaultdesktop', - action='store') - parser.add_argument('--startxonboot', dest='startxonboot', - action='store_true') + parser.add_argument("--defaultdesktop", dest="defaultdesktop", action="store") + parser.add_argument("--startxonboot", dest="startxonboot", action="store_true") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -876,15 +898,15 @@ def parse_xconfig(rule): def parse_zfcp(rule): - ''' + """ Parse the zfcp line - ''' + """ parser = argparse.ArgumentParser() rules = shlex.split(rule) rules.pop(0) - parser.add_argument('--devnum', dest='devnum', action='store') - parser.add_argument('--fcplun', dest='fcplun', action='store') - parser.add_argument('--wwpn', dest='wwpn', action='store') + parser.add_argument("--devnum", dest="devnum", action="store") + parser.add_argument("--fcplun", dest="fcplun", action="store") + parser.add_argument("--wwpn", dest="wwpn", action="store") args = clean_args(vars(parser.parse_args(rules))) parser = None @@ -892,290 +914,293 @@ def parse_zfcp(rule): def mksls(src, dst=None): - ''' + """ Convert a kickstart file to an SLS file - ''' - mode = 'command' + """ + mode = "command" sls = {} ks_opts = {} - with salt.utils.files.fopen(src, 'r') as fh_: + with salt.utils.files.fopen(src, "r") as fh_: for line in fh_: - if line.startswith('#'): + if line.startswith("#"): continue - if mode == 'command': - if line.startswith('auth ') or line.startswith('authconfig '): - ks_opts['auth'] = parse_auth(line) - elif line.startswith('autopart'): - ks_opts['autopath'] = parse_autopart(line) - elif line.startswith('autostep'): - ks_opts['autostep'] = parse_autostep(line) - elif line.startswith('bootloader'): - ks_opts['bootloader'] = parse_bootloader(line) - elif line.startswith('btrfs'): - ks_opts['btrfs'] = parse_btrfs(line) - elif line.startswith('cdrom'): - ks_opts['cdrom'] = True - elif line.startswith('clearpart'): - ks_opts['clearpart'] = parse_clearpart(line) - elif line.startswith('cmdline'): - ks_opts['cmdline'] = True - elif line.startswith('device'): - ks_opts['device'] = parse_device(line) - elif line.startswith('dmraid'): - ks_opts['dmraid'] = parse_dmraid(line) - elif line.startswith('driverdisk'): - ks_opts['driverdisk'] = parse_driverdisk(line) - elif line.startswith('firewall'): - ks_opts['firewall'] = parse_firewall(line) - elif line.startswith('firstboot'): - ks_opts['firstboot'] = parse_firstboot(line) - elif line.startswith('group'): - ks_opts['group'] = parse_group(line) - elif line.startswith('graphical'): - ks_opts['graphical'] = True - elif line.startswith('halt'): - ks_opts['halt'] = True - elif line.startswith('harddrive'): - ks_opts['harddrive'] = True - elif line.startswith('ignoredisk'): - ks_opts['ignoredisk'] = parse_ignoredisk(line) - elif line.startswith('install'): - ks_opts['install'] = True - elif line.startswith('iscsi'): - ks_opts['iscsi'] = parse_iscsi(line) - elif line.startswith('iscsiname'): - ks_opts['iscsiname'] = parse_iscsiname(line) - elif line.startswith('keyboard'): - ks_opts['keyboard'] = parse_keyboard(line) - elif line.startswith('lang'): - ks_opts['lang'] = parse_lang(line) - elif line.startswith('logvol'): - if 'logvol' not in ks_opts.keys(): - ks_opts['logvol'] = [] - ks_opts['logvol'].append(parse_logvol(line)) - elif line.startswith('logging'): - ks_opts['logging'] = parse_logging(line) - elif line.startswith('mediacheck'): - ks_opts['mediacheck'] = True - elif line.startswith('monitor'): - ks_opts['monitor'] = parse_monitor(line) - elif line.startswith('multipath'): - ks_opts['multipath'] = parse_multipath(line) - elif line.startswith('network'): - if 'network' not in ks_opts.keys(): - ks_opts['network'] = [] - ks_opts['network'].append(parse_network(line)) - elif line.startswith('nfs'): - ks_opts['nfs'] = True - elif line.startswith('part ') or line.startswith('partition'): - if 'part' not in ks_opts.keys(): - ks_opts['part'] = [] - ks_opts['part'].append(parse_partition(line)) - elif line.startswith('poweroff'): - ks_opts['poweroff'] = True - elif line.startswith('raid'): - if 'raid' not in ks_opts.keys(): - ks_opts['raid'] = [] - ks_opts['raid'].append(parse_raid(line)) - elif line.startswith('reboot'): - ks_opts['reboot'] = parse_reboot(line) - elif line.startswith('repo'): - ks_opts['repo'] = parse_repo(line) - elif line.startswith('rescue'): - ks_opts['rescue'] = parse_rescue(line) - elif line.startswith('rootpw'): - ks_opts['rootpw'] = parse_rootpw(line) - elif line.startswith('selinux'): - ks_opts['selinux'] = parse_selinux(line) - elif line.startswith('services'): - ks_opts['services'] = parse_services(line) - elif line.startswith('shutdown'): - ks_opts['shutdown'] = True - elif line.startswith('sshpw'): - ks_opts['sshpw'] = parse_sshpw(line) - elif line.startswith('skipx'): - ks_opts['skipx'] = True - elif line.startswith('text'): - ks_opts['text'] = True - elif line.startswith('timezone'): - ks_opts['timezone'] = parse_timezone(line) - elif line.startswith('updates'): - ks_opts['updates'] = parse_updates(line) - elif line.startswith('upgrade'): - ks_opts['upgrade'] = parse_upgrade(line) - elif line.startswith('url'): - ks_opts['url'] = True - elif line.startswith('user'): - ks_opts['user'] = parse_user(line) - elif line.startswith('vnc'): - ks_opts['vnc'] = parse_vnc(line) - elif line.startswith('volgroup'): - ks_opts['volgroup'] = parse_volgroup(line) - elif line.startswith('xconfig'): - ks_opts['xconfig'] = parse_xconfig(line) - elif line.startswith('zerombr'): - ks_opts['zerombr'] = True - elif line.startswith('zfcp'): - ks_opts['zfcp'] = parse_zfcp(line) + if mode == "command": + if line.startswith("auth ") or line.startswith("authconfig "): + ks_opts["auth"] = parse_auth(line) + elif line.startswith("autopart"): + ks_opts["autopath"] = parse_autopart(line) + elif line.startswith("autostep"): + ks_opts["autostep"] = parse_autostep(line) + elif line.startswith("bootloader"): + ks_opts["bootloader"] = parse_bootloader(line) + elif line.startswith("btrfs"): + ks_opts["btrfs"] = parse_btrfs(line) + elif line.startswith("cdrom"): + ks_opts["cdrom"] = True + elif line.startswith("clearpart"): + ks_opts["clearpart"] = parse_clearpart(line) + elif line.startswith("cmdline"): + ks_opts["cmdline"] = True + elif line.startswith("device"): + ks_opts["device"] = parse_device(line) + elif line.startswith("dmraid"): + ks_opts["dmraid"] = parse_dmraid(line) + elif line.startswith("driverdisk"): + ks_opts["driverdisk"] = parse_driverdisk(line) + elif line.startswith("firewall"): + ks_opts["firewall"] = parse_firewall(line) + elif line.startswith("firstboot"): + ks_opts["firstboot"] = parse_firstboot(line) + elif line.startswith("group"): + ks_opts["group"] = parse_group(line) + elif line.startswith("graphical"): + ks_opts["graphical"] = True + elif line.startswith("halt"): + ks_opts["halt"] = True + elif line.startswith("harddrive"): + ks_opts["harddrive"] = True + elif line.startswith("ignoredisk"): + ks_opts["ignoredisk"] = parse_ignoredisk(line) + elif line.startswith("install"): + ks_opts["install"] = True + elif line.startswith("iscsi"): + ks_opts["iscsi"] = parse_iscsi(line) + elif line.startswith("iscsiname"): + ks_opts["iscsiname"] = parse_iscsiname(line) + elif line.startswith("keyboard"): + ks_opts["keyboard"] = parse_keyboard(line) + elif line.startswith("lang"): + ks_opts["lang"] = parse_lang(line) + elif line.startswith("logvol"): + if "logvol" not in ks_opts.keys(): + ks_opts["logvol"] = [] + ks_opts["logvol"].append(parse_logvol(line)) + elif line.startswith("logging"): + ks_opts["logging"] = parse_logging(line) + elif line.startswith("mediacheck"): + ks_opts["mediacheck"] = True + elif line.startswith("monitor"): + ks_opts["monitor"] = parse_monitor(line) + elif line.startswith("multipath"): + ks_opts["multipath"] = parse_multipath(line) + elif line.startswith("network"): + if "network" not in ks_opts.keys(): + ks_opts["network"] = [] + ks_opts["network"].append(parse_network(line)) + elif line.startswith("nfs"): + ks_opts["nfs"] = True + elif line.startswith("part ") or line.startswith("partition"): + if "part" not in ks_opts.keys(): + ks_opts["part"] = [] + ks_opts["part"].append(parse_partition(line)) + elif line.startswith("poweroff"): + ks_opts["poweroff"] = True + elif line.startswith("raid"): + if "raid" not in ks_opts.keys(): + ks_opts["raid"] = [] + ks_opts["raid"].append(parse_raid(line)) + elif line.startswith("reboot"): + ks_opts["reboot"] = parse_reboot(line) + elif line.startswith("repo"): + ks_opts["repo"] = parse_repo(line) + elif line.startswith("rescue"): + ks_opts["rescue"] = parse_rescue(line) + elif line.startswith("rootpw"): + ks_opts["rootpw"] = parse_rootpw(line) + elif line.startswith("selinux"): + ks_opts["selinux"] = parse_selinux(line) + elif line.startswith("services"): + ks_opts["services"] = parse_services(line) + elif line.startswith("shutdown"): + ks_opts["shutdown"] = True + elif line.startswith("sshpw"): + ks_opts["sshpw"] = parse_sshpw(line) + elif line.startswith("skipx"): + ks_opts["skipx"] = True + elif line.startswith("text"): + ks_opts["text"] = True + elif line.startswith("timezone"): + ks_opts["timezone"] = parse_timezone(line) + elif line.startswith("updates"): + ks_opts["updates"] = parse_updates(line) + elif line.startswith("upgrade"): + ks_opts["upgrade"] = parse_upgrade(line) + elif line.startswith("url"): + ks_opts["url"] = True + elif line.startswith("user"): + ks_opts["user"] = parse_user(line) + elif line.startswith("vnc"): + ks_opts["vnc"] = parse_vnc(line) + elif line.startswith("volgroup"): + ks_opts["volgroup"] = parse_volgroup(line) + elif line.startswith("xconfig"): + ks_opts["xconfig"] = parse_xconfig(line) + elif line.startswith("zerombr"): + ks_opts["zerombr"] = True + elif line.startswith("zfcp"): + ks_opts["zfcp"] = parse_zfcp(line) - if line.startswith('%include'): + if line.startswith("%include"): rules = shlex.split(line) - if not ks_opts['include']: - ks_opts['include'] = [] - ks_opts['include'].append(rules[1]) + if not ks_opts["include"]: + ks_opts["include"] = [] + ks_opts["include"].append(rules[1]) - if line.startswith('%ksappend'): + if line.startswith("%ksappend"): rules = shlex.split(line) - if not ks_opts['ksappend']: - ks_opts['ksappend'] = [] - ks_opts['ksappend'].append(rules[1]) + if not ks_opts["ksappend"]: + ks_opts["ksappend"] = [] + ks_opts["ksappend"].append(rules[1]) - if line.startswith('%packages'): - mode = 'packages' - if 'packages' not in ks_opts.keys(): - ks_opts['packages'] = {'packages': {}} + if line.startswith("%packages"): + mode = "packages" + if "packages" not in ks_opts.keys(): + ks_opts["packages"] = {"packages": {}} parser = argparse.ArgumentParser() opts = shlex.split(line) opts.pop(0) - parser.add_argument('--default', dest='default', action='store_true') - parser.add_argument('--excludedocs', dest='excludedocs', - action='store_true') - parser.add_argument('--ignoremissing', dest='ignoremissing', - action='store_true') - parser.add_argument('--instLangs', dest='instLangs', action='store') - parser.add_argument('--multilib', dest='multilib', action='store_true') - parser.add_argument('--nodefaults', dest='nodefaults', - action='store_true') - parser.add_argument('--optional', dest='optional', action='store_true') - parser.add_argument('--nobase', dest='nobase', action='store_true') + parser.add_argument("--default", dest="default", action="store_true") + parser.add_argument( + "--excludedocs", dest="excludedocs", action="store_true" + ) + parser.add_argument( + "--ignoremissing", dest="ignoremissing", action="store_true" + ) + parser.add_argument("--instLangs", dest="instLangs", action="store") + parser.add_argument("--multilib", dest="multilib", action="store_true") + parser.add_argument( + "--nodefaults", dest="nodefaults", action="store_true" + ) + parser.add_argument("--optional", dest="optional", action="store_true") + parser.add_argument("--nobase", dest="nobase", action="store_true") args = clean_args(vars(parser.parse_args(opts))) - ks_opts['packages']['options'] = args + ks_opts["packages"]["options"] = args continue - if line.startswith('%pre'): - mode = 'pre' + if line.startswith("%pre"): + mode = "pre" parser = argparse.ArgumentParser() opts = shlex.split(line) opts.pop(0) - parser.add_argument('--interpreter', dest='interpreter', - action='store') - parser.add_argument('--erroronfail', dest='erroronfail', - action='store_true') - parser.add_argument('--log', dest='log', action='store') + parser.add_argument("--interpreter", dest="interpreter", action="store") + parser.add_argument( + "--erroronfail", dest="erroronfail", action="store_true" + ) + parser.add_argument("--log", dest="log", action="store") args = clean_args(vars(parser.parse_args(opts))) - ks_opts['pre'] = {'options': args, 'script': ''} + ks_opts["pre"] = {"options": args, "script": ""} continue - if line.startswith('%post'): - mode = 'post' + if line.startswith("%post"): + mode = "post" parser = argparse.ArgumentParser() opts = shlex.split(line) opts.pop(0) - parser.add_argument('--nochroot', dest='nochroot', action='store_true') - parser.add_argument('--interpreter', dest='interpreter', - action='store') - parser.add_argument('--erroronfail', dest='erroronfail', - action='store_true') - parser.add_argument('--log', dest='log', action='store') + parser.add_argument("--nochroot", dest="nochroot", action="store_true") + parser.add_argument("--interpreter", dest="interpreter", action="store") + parser.add_argument( + "--erroronfail", dest="erroronfail", action="store_true" + ) + parser.add_argument("--log", dest="log", action="store") args = clean_args(vars(parser.parse_args(opts))) - ks_opts['post'] = {'options': args, 'script': ''} + ks_opts["post"] = {"options": args, "script": ""} continue - if line.startswith('%end'): + if line.startswith("%end"): mode = None - if mode == 'packages': - if line.startswith('-'): - package = line.replace('-', '', 1).strip() - ks_opts['packages']['packages'][package] = False + if mode == "packages": + if line.startswith("-"): + package = line.replace("-", "", 1).strip() + ks_opts["packages"]["packages"][package] = False else: - ks_opts['packages']['packages'][line.strip()] = True + ks_opts["packages"]["packages"][line.strip()] = True - if mode == 'pre': - ks_opts['pre']['script'] += line + if mode == "pre": + ks_opts["pre"]["script"] += line - if mode == 'post': - ks_opts['post']['script'] += line + if mode == "post": + ks_opts["post"]["script"] += line # Set language - sls[ks_opts['lang']['lang']] = {'locale': ['system']} + sls[ks_opts["lang"]["lang"]] = {"locale": ["system"]} # Set keyboard - sls[ks_opts['keyboard']['xlayouts']] = {'keyboard': ['system']} + sls[ks_opts["keyboard"]["xlayouts"]] = {"keyboard": ["system"]} # Set timezone - sls[ks_opts['timezone']['timezone']] = {'timezone': ['system']} - if 'utc' in ks_opts['timezone'].keys(): - sls[ks_opts['timezone']['timezone']]['timezone'].append('utc') + sls[ks_opts["timezone"]["timezone"]] = {"timezone": ["system"]} + if "utc" in ks_opts["timezone"].keys(): + sls[ks_opts["timezone"]["timezone"]]["timezone"].append("utc") # Set network - if 'network' in ks_opts.keys(): - for interface in ks_opts['network']: - device = interface.get('device', None) + if "network" in ks_opts.keys(): + for interface in ks_opts["network"]: + device = interface.get("device", None) if device is not None: - del interface['device'] - sls[device] = {'proto': interface['bootproto']} - del interface['bootproto'] + del interface["device"] + sls[device] = {"proto": interface["bootproto"]} + del interface["bootproto"] - if 'onboot' in interface.keys(): - if 'no' in interface['onboot']: - sls[device]['enabled'] = False + if "onboot" in interface.keys(): + if "no" in interface["onboot"]: + sls[device]["enabled"] = False else: - sls[device]['enabled'] = True - del interface['onboot'] + sls[device]["enabled"] = True + del interface["onboot"] - if 'noipv4' in interface.keys(): - sls[device]['ipv4'] = {'enabled': False} - del interface['noipv4'] - if 'noipv6' in interface.keys(): - sls[device]['ipv6'] = {'enabled': False} - del interface['noipv6'] + if "noipv4" in interface.keys(): + sls[device]["ipv4"] = {"enabled": False} + del interface["noipv4"] + if "noipv6" in interface.keys(): + sls[device]["ipv6"] = {"enabled": False} + del interface["noipv6"] for option in interface: if type(interface[option]) is bool: - sls[device][option] = {'enabled': [interface[option]]} + sls[device][option] = {"enabled": [interface[option]]} else: sls[device][option] = interface[option] - if 'hostname' in interface: - sls['system'] = { - 'network.system': { - 'enabled': True, - 'hostname': interface['hostname'], - 'apply_hostname': True, + if "hostname" in interface: + sls["system"] = { + "network.system": { + "enabled": True, + "hostname": interface["hostname"], + "apply_hostname": True, } } # Set selinux - if 'selinux' in ks_opts.keys(): - for mode in ks_opts['selinux']: - sls[mode] = {'selinux': ['mode']} + if "selinux" in ks_opts.keys(): + for mode in ks_opts["selinux"]: + sls[mode] = {"selinux": ["mode"]} # Get package data together - if 'nobase' not in ks_opts['packages']['options']: - sls['base'] = {'pkg_group': ['installed']} + if "nobase" not in ks_opts["packages"]["options"]: + sls["base"] = {"pkg_group": ["installed"]} - packages = ks_opts['packages']['packages'] + packages = ks_opts["packages"]["packages"] for package in packages: if not packages[package]: continue if package and packages[package] is True: - if package.startswith('@'): - pkg_group = package.replace('@', '', 1) - sls[pkg_group] = {'pkg_group': ['installed']} + if package.startswith("@"): + pkg_group = package.replace("@", "", 1) + sls[pkg_group] = {"pkg_group": ["installed"]} else: - sls[package] = {'pkg': ['installed']} + sls[package] = {"pkg": ["installed"]} elif packages[package] is False: - sls[package] = {'pkg': ['absent']} + sls[package] = {"pkg": ["absent"]} if dst: - with salt.utils.files.fopen(dst, 'w') as fp_: + with salt.utils.files.fopen(dst, "w") as fp_: salt.utils.yaml.safe_dump(sls, fp_, default_flow_style=False) else: return salt.utils.yaml.safe_dump(sls, default_flow_style=False) diff --git a/salt/utils/kinds.py b/salt/utils/kinds.py index d6595a91b31..660a96d8d80 100644 --- a/salt/utils/kinds.py +++ b/salt/utils/kinds.py @@ -1,17 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Application Kinds of Salt apps. These are used to indicate what kind of Application is using RAET -''' +""" from __future__ import absolute_import, unicode_literals + from collections import namedtuple + from salt.utils.odict import OrderedDict # Python equivalent of an enum -APPL_KINDS = OrderedDict([('master', 0), - ('minion', 1), - ('syndic', 2), - ('caller', 3)]) -APPL_KIND_NAMES = OrderedDict((v, k) for k, v in list(APPL_KINDS.items())) # inverse map -ApplKind = namedtuple('ApplKind', list(APPL_KINDS.keys())) +APPL_KINDS = OrderedDict([("master", 0), ("minion", 1), ("syndic", 2), ("caller", 3)]) +APPL_KIND_NAMES = OrderedDict( + (v, k) for k, v in list(APPL_KINDS.items()) +) # inverse map +ApplKind = namedtuple("ApplKind", list(APPL_KINDS.keys())) applKinds = ApplKind(**APPL_KINDS) diff --git a/salt/utils/lazy.py b/salt/utils/lazy.py index 3cd6489d2d8..5dc064c205e 100644 --- a/salt/utils/lazy.py +++ b/salt/utils/lazy.py @@ -1,29 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" Lazily-evaluated data structures, primarily used by Salt's loader -''' +""" # Import Python Libs from __future__ import absolute_import, unicode_literals + import logging + import salt.exceptions try: from collections.abc import MutableMapping except ImportError: + # pylint: disable=no-name-in-module from collections import MutableMapping + # pylint: enable=no-name-in-module + log = logging.getLogger(__name__) def verify_fun(lazy_obj, fun): - ''' + """ Check that the function passed really exists - ''' + """ if not fun: raise salt.exceptions.SaltInvocationError( - 'Must specify a function to run!\n' - 'ex: manage.up' + "Must specify a function to run!\n" "ex: manage.up" ) if fun not in lazy_obj: # If the requested function isn't available, lets say why @@ -31,13 +35,14 @@ def verify_fun(lazy_obj, fun): class LazyDict(MutableMapping): - ''' + """ A base class of dict which will lazily load keys once they are needed TODO: negative caching? If you ask for 'foo' and it doesn't exist it will look EVERY time unless someone calls load_all() As of now this is left to the class which inherits from this base - ''' + """ + def __init__(self): self.clear() @@ -50,40 +55,40 @@ class LazyDict(MutableMapping): return self.__nonzero__() def clear(self): - ''' + """ Clear the dict - ''' + """ # create a dict to store loaded values in - self._dict = getattr(self, 'mod_dict_class', dict)() + self._dict = getattr(self, "mod_dict_class", dict)() # have we already loded everything? self.loaded = False def _load(self, key): - ''' + """ Load a single item if you have it - ''' + """ raise NotImplementedError() def _load_all(self): - ''' + """ Load all of them - ''' + """ raise NotImplementedError() def _missing(self, key): - ''' + """ Whether or not the key is missing (meaning we know it's not there) - ''' + """ return False def missing_fun_string(self, function_name): - ''' + """ Return the error string for a missing function. Override this to return a more meaningfull error message if possible - ''' - return '\'{0}\' is not available.'.format(function_name) + """ + return "'{0}' is not available.".format(function_name) def __setitem__(self, key, val): self._dict[key] = val @@ -92,19 +97,21 @@ class LazyDict(MutableMapping): del self._dict[key] def __getitem__(self, key): - ''' + """ Check if the key is ttld out, then do the get - ''' + """ if self._missing(key): raise KeyError(key) if key not in self._dict and not self.loaded: # load the item if self._load(key): - log.debug('LazyLoaded %s', key) + log.debug("LazyLoaded %s", key) return self._dict[key] else: - log.debug('Could not LazyLoad %s: %s', key, self.missing_fun_string(key)) + log.debug( + "Could not LazyLoad %s: %s", key, self.missing_fun_string(key) + ) raise KeyError(key) else: return self._dict[key] diff --git a/salt/utils/listdiffer.py b/salt/utils/listdiffer.py index 96594365a5e..294726e0e8a 100644 --- a/salt/utils/listdiffer.py +++ b/salt/utils/listdiffer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Compare lists of dictionaries by a specified key. The following can be retrieved: @@ -15,11 +15,11 @@ The following can be retrieved: (5) String representations of the list diff Note: All dictionaries keys are expected to be strings -''' +""" from __future__ import absolute_import, unicode_literals -from salt.utils.dictdiffer import recursive_diff from salt.ext import six +from salt.utils.dictdiffer import recursive_diff def list_diff(list_a, list_b, key): @@ -27,12 +27,13 @@ def list_diff(list_a, list_b, key): class ListDictDiffer(object): - ''' + """ Calculates the differences between two lists of dictionaries. It matches the items based on a given key and uses the recursive_diff to diff the two values. - ''' + """ + def __init__(self, current_list, next_list, key): self._intersect = [] self._removed = [] @@ -42,18 +43,20 @@ class ListDictDiffer(object): self._key = key for current_item in current_list: if key not in current_item: - raise ValueError('The supplied key \'{0}\' does not ' - 'exist in item, the available keys are: {1}' - ''.format(key, current_item.keys())) + raise ValueError( + "The supplied key '{0}' does not " + "exist in item, the available keys are: {1}" + "".format(key, current_item.keys()) + ) for next_item in next_list: if key not in next_item: - raise ValueError('The supplied key \'{0}\' does not ' - 'exist in item, the available keys are: ' - '{1}'.format(key, next_item.keys())) + raise ValueError( + "The supplied key '{0}' does not " + "exist in item, the available keys are: " + "{1}".format(key, next_item.keys()) + ) if next_item[key] == current_item[key]: - item = {key: next_item[key], - 'old': current_item, - 'new': next_item} + item = {key: next_item[key], "old": current_item, "new": next_item} self._intersect.append(item) break else: @@ -67,59 +70,69 @@ class ListDictDiffer(object): self._added.append(next_item) def _get_recursive_difference(self, type): - '''Returns the recursive diff between dict values''' - if type == 'intersect': - return [recursive_diff(item['old'], item['new']) for item in self._intersect] - elif type == 'added': + """Returns the recursive diff between dict values""" + if type == "intersect": + return [ + recursive_diff(item["old"], item["new"]) for item in self._intersect + ] + elif type == "added": return [recursive_diff({}, item) for item in self._added] - elif type == 'removed': - return [recursive_diff(item, {}, ignore_missing_keys=False) - for item in self._removed] - elif type == 'all': + elif type == "removed": + return [ + recursive_diff(item, {}, ignore_missing_keys=False) + for item in self._removed + ] + elif type == "all": recursive_list = [] - recursive_list.extend([recursive_diff(item['old'], item['new']) for item in self._intersect]) + recursive_list.extend( + [recursive_diff(item["old"], item["new"]) for item in self._intersect] + ) recursive_list.extend([recursive_diff({}, item) for item in self._added]) - recursive_list.extend([recursive_diff(item, {}, - ignore_missing_keys=False) - for item in self._removed]) + recursive_list.extend( + [ + recursive_diff(item, {}, ignore_missing_keys=False) + for item in self._removed + ] + ) return recursive_list else: - raise ValueError('The given type for recursive list matching ' - 'is not supported.') + raise ValueError( + "The given type for recursive list matching " "is not supported." + ) @property def removed(self): - '''Returns the objects which are removed from the list''' + """Returns the objects which are removed from the list""" return self._removed @property def added(self): - '''Returns the objects which are added to the list''' + """Returns the objects which are added to the list""" return self._added @property def intersect(self): - '''Returns the intersect objects''' + """Returns the intersect objects""" return self._intersect - def remove_diff(self, diff_key=None, diff_list='intersect'): - '''Deletes an attribute from all of the intersect objects''' - if diff_list == 'intersect': + def remove_diff(self, diff_key=None, diff_list="intersect"): + """Deletes an attribute from all of the intersect objects""" + if diff_list == "intersect": for item in self._intersect: - item['old'].pop(diff_key, None) - item['new'].pop(diff_key, None) - if diff_list == 'removed': + item["old"].pop(diff_key, None) + item["new"].pop(diff_key, None) + if diff_list == "removed": for item in self._removed: item.pop(diff_key, None) @property def diffs(self): - ''' + """ Returns a list of dictionaries with key value pairs. The values are the differences between the items identified by the key. - ''' + """ differences = [] - for item in self._get_recursive_difference(type='all'): + for item in self._get_recursive_difference(type="all"): if item.diffs: if item.past_dict: differences.append({item.past_dict[self._key]: item.diffs}) @@ -129,64 +142,91 @@ class ListDictDiffer(object): @property def changes_str(self): - '''Returns a string describing the changes''' - changes = '' - for item in self._get_recursive_difference(type='intersect'): + """Returns a string describing the changes""" + changes = "" + for item in self._get_recursive_difference(type="intersect"): if item.diffs: - changes = ''.join([changes, - # Tabulate comment deeper, show the key attribute and the value - # Next line should be tabulated even deeper, - # every change should be tabulated 1 deeper - '\tidentified by {0} {1}:\n\t{2}\n'.format( - self._key, - item.past_dict[self._key], - item.changes_str.replace('\n', '\n\t'))]) - for item in self._get_recursive_difference(type='removed'): + changes = "".join( + [ + changes, + # Tabulate comment deeper, show the key attribute and the value + # Next line should be tabulated even deeper, + # every change should be tabulated 1 deeper + "\tidentified by {0} {1}:\n\t{2}\n".format( + self._key, + item.past_dict[self._key], + item.changes_str.replace("\n", "\n\t"), + ), + ] + ) + for item in self._get_recursive_difference(type="removed"): if item.past_dict: - changes = ''.join([changes, - # Tabulate comment deeper, show the key attribute and the value - '\tidentified by {0} {1}:' - '\n\twill be removed\n'.format(self._key, - item.past_dict[self._key])]) - for item in self._get_recursive_difference(type='added'): + changes = "".join( + [ + changes, + # Tabulate comment deeper, show the key attribute and the value + "\tidentified by {0} {1}:" + "\n\twill be removed\n".format( + self._key, item.past_dict[self._key] + ), + ] + ) + for item in self._get_recursive_difference(type="added"): if item.current_dict: - changes = ''.join([changes, - # Tabulate comment deeper, show the key attribute and the value - '\tidentified by {0} {1}:' - '\n\twill be added\n'.format(self._key, - item.current_dict[self._key])]) + changes = "".join( + [ + changes, + # Tabulate comment deeper, show the key attribute and the value + "\tidentified by {0} {1}:" + "\n\twill be added\n".format( + self._key, item.current_dict[self._key] + ), + ] + ) return changes @property - def changes_str2(self, tab_string=' '): - ''' + def changes_str2(self, tab_string=" "): + """ Returns a string in a more compact format describing the changes. The output better alligns with the one in recursive_diff. - ''' + """ changes = [] - for item in self._get_recursive_difference(type='intersect'): + for item in self._get_recursive_difference(type="intersect"): if item.diffs: - changes.append('{tab}{0}={1} (updated):\n{tab}{tab}{2}' - ''.format(self._key, item.past_dict[self._key], - item.changes_str.replace( - '\n', - '\n{0}{0}'.format(tab_string)), - tab=tab_string)) - for item in self._get_recursive_difference(type='removed'): + changes.append( + "{tab}{0}={1} (updated):\n{tab}{tab}{2}" + "".format( + self._key, + item.past_dict[self._key], + item.changes_str.replace("\n", "\n{0}{0}".format(tab_string)), + tab=tab_string, + ) + ) + for item in self._get_recursive_difference(type="removed"): if item.past_dict: - changes.append('{tab}{0}={1} (removed)'.format( - self._key, item.past_dict[self._key], tab=tab_string)) - for item in self._get_recursive_difference(type='added'): + changes.append( + "{tab}{0}={1} (removed)".format( + self._key, item.past_dict[self._key], tab=tab_string + ) + ) + for item in self._get_recursive_difference(type="added"): if item.current_dict: - changes.append('{tab}{0}={1} (added): {2}'.format( - self._key, item.current_dict[self._key], - dict(item.current_dict), tab=tab_string)) - return '\n'.join(changes) + changes.append( + "{tab}{0}={1} (added): {2}".format( + self._key, + item.current_dict[self._key], + dict(item.current_dict), + tab=tab_string, + ) + ) + return "\n".join(changes) @property def new_values(self): - '''Returns the new values from the diff''' + """Returns the new values from the diff""" + def get_new_values_and_key(item): values = item.new_values if item.past_dict: @@ -196,24 +236,29 @@ class ListDictDiffer(object): values.update({self._key: item.current_dict[self._key]}) return values - return [get_new_values_and_key(el) - for el in self._get_recursive_difference('all') - if el.diffs and el.current_dict] + return [ + get_new_values_and_key(el) + for el in self._get_recursive_difference("all") + if el.diffs and el.current_dict + ] @property def old_values(self): - '''Returns the old values from the diff''' + """Returns the old values from the diff""" + def get_old_values_and_key(item): values = item.old_values values.update({self._key: item.past_dict[self._key]}) return values - return [get_old_values_and_key(el) - for el in self._get_recursive_difference('all') - if el.diffs and el.past_dict] + return [ + get_old_values_and_key(el) + for el in self._get_recursive_difference("all") + if el.diffs and el.past_dict + ] - def changed(self, selection='all'): - ''' + def changed(self, selection="all"): + """ Returns the list of changed values. The key is added to each item. @@ -222,31 +267,35 @@ class ListDictDiffer(object): Supported values are ``all`` - all changed items are included in the output ``intersect`` - changed items present in both lists are included - ''' + """ changed = [] - if selection == 'all': - for recursive_item in self._get_recursive_difference(type='all'): + if selection == "all": + for recursive_item in self._get_recursive_difference(type="all"): # We want the unset values as well recursive_item.ignore_unset_values = False - key_val = six.text_type(recursive_item.past_dict[self._key]) \ - if self._key in recursive_item.past_dict \ - else six.text_type(recursive_item.current_dict[self._key]) + key_val = ( + six.text_type(recursive_item.past_dict[self._key]) + if self._key in recursive_item.past_dict + else six.text_type(recursive_item.current_dict[self._key]) + ) for change in recursive_item.changed(): if change != self._key: - changed.append('.'.join([self._key, key_val, change])) + changed.append(".".join([self._key, key_val, change])) return changed - elif selection == 'intersect': + elif selection == "intersect": # We want the unset values as well - for recursive_item in self._get_recursive_difference(type='intersect'): + for recursive_item in self._get_recursive_difference(type="intersect"): recursive_item.ignore_unset_values = False - key_val = six.text_type(recursive_item.past_dict[self._key]) \ - if self._key in recursive_item.past_dict \ - else six.text_type(recursive_item.current_dict[self._key]) + key_val = ( + six.text_type(recursive_item.past_dict[self._key]) + if self._key in recursive_item.past_dict + else six.text_type(recursive_item.current_dict[self._key]) + ) for change in recursive_item.changed(): if change != self._key: - changed.append('.'.join([self._key, key_val, change])) + changed.append(".".join([self._key, key_val, change])) return changed @property diff --git a/salt/utils/locales.py b/salt/utils/locales.py index aa42b90e352..a2a9daf3aef 100644 --- a/salt/utils/locales.py +++ b/salt/utils/locales.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" the locale utils used by salt -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import sys # Import Salt libs @@ -14,9 +15,9 @@ from salt.utils.decorators import memoize as real_memoize @real_memoize def get_encodings(): - ''' + """ return a list of string encodings to try - ''' + """ encodings = [__salt_system_encoding__] try: @@ -26,7 +27,7 @@ def get_encodings(): if sys_enc and sys_enc not in encodings: encodings.append(sys_enc) - for enc in ['utf-8', 'latin-1']: + for enc in ["utf-8", "latin-1"]: if enc not in encodings: encodings.append(enc) @@ -35,28 +36,28 @@ def get_encodings(): def sdecode(string_): salt.utils.versions.warn_until( - 'Sodium', - 'Use of \'salt.utils.locales.sdecode\' detected. This function ' - 'has been replaced by \'salt.utils.data.decode\' as of ' - 'Salt 2019.2.0. This warning will be removed in Salt Sodium.', - stacklevel=3 + "Sodium", + "Use of 'salt.utils.locales.sdecode' detected. This function " + "has been replaced by 'salt.utils.data.decode' as of " + "Salt 2019.2.0. This warning will be removed in Salt Sodium.", + stacklevel=3, ) return salt.utils.data.decode(string_) def sdecode_if_string(value_): salt.utils.versions.warn_until( - 'Sodium', - 'Use of \'salt.utils.locales.sdecode_if_string\' detected. This ' - 'function has been replaced by \'salt.utils.data.decode\' as of ' - 'Salt 2019.2.0. This warning will be removed in Salt Sodium.', - stacklevel=3 + "Sodium", + "Use of 'salt.utils.locales.sdecode_if_string' detected. This " + "function has been replaced by 'salt.utils.data.decode' as of " + "Salt 2019.2.0. This warning will be removed in Salt Sodium.", + stacklevel=3, ) return salt.utils.data.decode(value_) def split_locale(loc): - ''' + """ Split a locale specifier. The general format is language[_territory][.codeset][@modifier] [charmap] @@ -64,47 +65,48 @@ def split_locale(loc): For example: ca_ES.UTF-8@valencia UTF-8 - ''' + """ + def split(st, char): - ''' + """ Split a string `st` once by `char`; always return a two-element list even if the second element is empty. - ''' + """ split_st = st.split(char, 1) if len(split_st) == 1: - split_st.append('') + split_st.append("") return split_st comps = {} - work_st, comps['charmap'] = split(loc, ' ') - work_st, comps['modifier'] = split(work_st, '@') - work_st, comps['codeset'] = split(work_st, '.') - comps['language'], comps['territory'] = split(work_st, '_') + work_st, comps["charmap"] = split(loc, " ") + work_st, comps["modifier"] = split(work_st, "@") + work_st, comps["codeset"] = split(work_st, ".") + comps["language"], comps["territory"] = split(work_st, "_") return comps def join_locale(comps): - ''' + """ Join a locale specifier split in the format returned by split_locale. - ''' - loc = comps['language'] - if comps.get('territory'): - loc += '_' + comps['territory'] - if comps.get('codeset'): - loc += '.' + comps['codeset'] - if comps.get('modifier'): - loc += '@' + comps['modifier'] - if comps.get('charmap'): - loc += ' ' + comps['charmap'] + """ + loc = comps["language"] + if comps.get("territory"): + loc += "_" + comps["territory"] + if comps.get("codeset"): + loc += "." + comps["codeset"] + if comps.get("modifier"): + loc += "@" + comps["modifier"] + if comps.get("charmap"): + loc += " " + comps["charmap"] return loc def normalize_locale(loc): - ''' + """ Format a locale specifier according to the format returned by `locale -a`. - ''' + """ comps = split_locale(loc) - comps['territory'] = comps['territory'].upper() - comps['codeset'] = comps['codeset'].lower().replace('-', '') - comps['charmap'] = '' + comps["territory"] = comps["territory"].upper() + comps["codeset"] = comps["codeset"].lower().replace("-", "") + comps["charmap"] = "" return join_locale(comps) diff --git a/salt/utils/mac_utils.py b/salt/utils/mac_utils.py index ea63e839626..e6fa93a29fc 100644 --- a/salt/utils/mac_utils.py +++ b/salt/utils/mac_utils.py @@ -1,22 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" Helper functions for use by mac modules .. versionadded:: 2016.3.0 -''' +""" from __future__ import absolute_import, unicode_literals # Import Python Libraries import logging -import subprocess import os import plistlib +import subprocess import time import xml.parsers.expat -try: - import pwd -except ImportError: - # The pwd module is not available on all platforms - pass + +import salt.grains.extra # Import Salt Libs import salt.modules.cmdmod @@ -26,52 +23,66 @@ import salt.utils.path import salt.utils.platform import salt.utils.stringutils import salt.utils.timed_subprocess -import salt.grains.extra -from salt.exceptions import CommandExecutionError, SaltInvocationError,\ - TimedProcTimeoutError +from salt.exceptions import ( + CommandExecutionError, + SaltInvocationError, + TimedProcTimeoutError, +) +from salt.ext import six # Import Third Party Libs from salt.ext.six.moves import range -from salt.ext import six -DEFAULT_SHELL = salt.grains.extra.shell()['shell'] +try: + import pwd +except ImportError: + # The pwd module is not available on all platforms + pass + + +DEFAULT_SHELL = salt.grains.extra.shell()["shell"] # Set up logging log = logging.getLogger(__name__) -__virtualname__ = 'mac_utils' +__virtualname__ = "mac_utils" __salt__ = { - 'cmd.run_all': salt.modules.cmdmod._run_all_quiet, - 'cmd.run': salt.modules.cmdmod._run_quiet, + "cmd.run_all": salt.modules.cmdmod._run_all_quiet, + "cmd.run": salt.modules.cmdmod._run_quiet, } if six.PY2: + class InvalidFileException(Exception): pass + plistlib.InvalidFileException = InvalidFileException def __virtual__(): - ''' + """ Load only on Mac OS - ''' + """ if not salt.utils.platform.is_darwin(): - return (False, 'The mac_utils utility could not be loaded: ' - 'utility only works on MacOS systems.') + return ( + False, + "The mac_utils utility could not be loaded: " + "utility only works on MacOS systems.", + ) return __virtualname__ def _run_all(cmd): - ''' + """ Args: cmd: Returns: - ''' + """ if not isinstance(cmd, list): cmd = salt.utils.args.shlex_split(cmd, posix=False) @@ -79,28 +90,29 @@ def _run_all(cmd): if not isinstance(cmd[idx], six.string_types): cmd[idx] = six.text_type(cmd[idx]) - cmd = ' '.join(cmd) + cmd = " ".join(cmd) run_env = os.environ.copy() - kwargs = {'cwd': None, - 'shell': DEFAULT_SHELL, - 'env': run_env, - 'stdin': None, - 'stdout': subprocess.PIPE, - 'stderr': subprocess.PIPE, - 'with_communicate': True, - 'timeout': None, - 'bg': False, - } + kwargs = { + "cwd": None, + "shell": DEFAULT_SHELL, + "env": run_env, + "stdin": None, + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + "with_communicate": True, + "timeout": None, + "bg": False, + } try: proc = salt.utils.timed_subprocess.TimedProc(cmd, **kwargs) except (OSError, IOError) as exc: raise CommandExecutionError( - 'Unable to run command \'{0}\' with the context \'{1}\', ' - 'reason: {2}'.format(cmd, kwargs, exc) + "Unable to run command '{0}' with the context '{1}', " + "reason: {2}".format(cmd, kwargs, exc) ) ret = {} @@ -108,10 +120,10 @@ def _run_all(cmd): try: proc.run() except TimedProcTimeoutError as exc: - ret['stdout'] = six.text_type(exc) - ret['stderr'] = '' - ret['retcode'] = 1 - ret['pid'] = proc.process.pid + ret["stdout"] = six.text_type(exc) + ret["stderr"] = "" + ret["retcode"] = 1 + ret["pid"] = proc.process.pid return ret out, err = proc.stdout, proc.stderr @@ -121,28 +133,28 @@ def _run_all(cmd): if err is not None: err = salt.utils.stringutils.to_str(err).rstrip() - ret['pid'] = proc.process.pid - ret['retcode'] = proc.process.returncode - ret['stdout'] = out - ret['stderr'] = err + ret["pid"] = proc.process.pid + ret["retcode"] = proc.process.returncode + ret["stdout"] = out + ret["stderr"] = err return ret def _check_launchctl_stderr(ret): - ''' + """ helper class to check the launchctl stderr. launchctl does not always return bad exit code if there is a failure - ''' - err = ret['stderr'].lower() - if 'service is disabled' in err: + """ + err = ret["stderr"].lower() + if "service is disabled" in err: return True return False def execute_return_success(cmd): - ''' + """ Executes the passed command. Returns True if successful :param str cmd: The command to run @@ -151,23 +163,23 @@ def execute_return_success(cmd): :rtype: bool :raises: Error if command fails or is not supported - ''' + """ ret = _run_all(cmd) - log.debug('Execute return success %s: %r', cmd, ret) + log.debug("Execute return success %s: %r", cmd, ret) - if ret['retcode'] != 0 or 'not supported' in ret['stdout'].lower(): - msg = 'Command Failed: {0}\n'.format(cmd) - msg += 'Return Code: {0}\n'.format(ret['retcode']) - msg += 'Output: {0}\n'.format(ret['stdout']) - msg += 'Error: {0}\n'.format(ret['stderr']) + if ret["retcode"] != 0 or "not supported" in ret["stdout"].lower(): + msg = "Command Failed: {0}\n".format(cmd) + msg += "Return Code: {0}\n".format(ret["retcode"]) + msg += "Output: {0}\n".format(ret["stdout"]) + msg += "Error: {0}\n".format(ret["stderr"]) raise CommandExecutionError(msg) return True def execute_return_result(cmd): - ''' + """ Executes the passed command. Returns the standard out if successful :param str cmd: The command to run @@ -177,21 +189,21 @@ def execute_return_result(cmd): :rtype: str :raises: Error if command fails or is not supported - ''' + """ ret = _run_all(cmd) - if ret['retcode'] != 0 or 'not supported' in ret['stdout'].lower(): - msg = 'Command Failed: {0}\n'.format(cmd) - msg += 'Return Code: {0}\n'.format(ret['retcode']) - msg += 'Output: {0}\n'.format(ret['stdout']) - msg += 'Error: {0}\n'.format(ret['stderr']) + if ret["retcode"] != 0 or "not supported" in ret["stdout"].lower(): + msg = "Command Failed: {0}\n".format(cmd) + msg += "Return Code: {0}\n".format(ret["retcode"]) + msg += "Output: {0}\n".format(ret["stdout"]) + msg += "Error: {0}\n".format(ret["stderr"]) raise CommandExecutionError(msg) - return ret['stdout'] + return ret["stdout"] def parse_return(data): - ''' + """ Returns the data portion of a string that is colon separated. :param str data: The string that contains the data to be parsed. Usually the @@ -201,18 +213,18 @@ def parse_return(data): ``Time Zone: America/Denver`` will return: ``America/Denver`` - ''' + """ - if ': ' in data: - return data.split(': ')[1] - if ':\n' in data: - return data.split(':\n')[1] + if ": " in data: + return data.split(": ")[1] + if ":\n" in data: + return data.split(":\n")[1] else: return data def validate_enabled(enabled): - ''' + """ Helper function to validate the enabled parameter. Boolean values are converted to "on" and "off". String values are checked to make sure they are either "on" or "off"/"yes" or "no". Integer ``0`` will return "off". All @@ -224,21 +236,23 @@ def validate_enabled(enabled): :return: "on" or "off" or errors :rtype: str - ''' + """ if isinstance(enabled, six.string_types): - if enabled.lower() not in ['on', 'off', 'yes', 'no']: - msg = '\nMac Power: Invalid String Value for Enabled.\n' \ - 'String values must be \'on\' or \'off\'/\'yes\' or \'no\'.\n' \ - 'Passed: {0}'.format(enabled) + if enabled.lower() not in ["on", "off", "yes", "no"]: + msg = ( + "\nMac Power: Invalid String Value for Enabled.\n" + "String values must be 'on' or 'off'/'yes' or 'no'.\n" + "Passed: {0}".format(enabled) + ) raise SaltInvocationError(msg) - return 'on' if enabled.lower() in ['on', 'yes'] else 'off' + return "on" if enabled.lower() in ["on", "yes"] else "off" - return 'on' if bool(enabled) else 'off' + return "on" if bool(enabled) else "off" def confirm_updated(value, check_fun, normalize_ret=False, wait=5): - ''' + """ Wait up to ``wait`` seconds for a system parameter to be changed before deciding it hasn't changed. @@ -252,12 +266,15 @@ def confirm_updated(value, check_fun, normalize_ret=False, wait=5): :param int wait: The maximum amount of seconds to wait for a system parameter to change - ''' + """ for i in range(wait): state = validate_enabled(check_fun()) if normalize_ret else check_fun() log.debug( - 'Confirm update try: %d func:%r state:%s value:%s', - i, check_fun, state, value, + "Confirm update try: %d func:%r state:%s value:%s", + i, + check_fun, + state, + value, ) if value in state: return True @@ -266,7 +283,7 @@ def confirm_updated(value, check_fun, normalize_ret=False, wait=5): def launchctl(sub_cmd, *args, **kwargs): - ''' + """ Run a launchctl command and raise an error if it fails Args: additional args are passed to launchctl @@ -289,48 +306,48 @@ def launchctl(sub_cmd, *args, **kwargs): import salt.utils.mac_service salt.utils.mac_service.launchctl('debug', 'org.cups.cupsd') - ''' + """ # Get return type - return_stdout = kwargs.pop('return_stdout', False) + return_stdout = kwargs.pop("return_stdout", False) # Construct command - cmd = ['launchctl', sub_cmd] + cmd = ["launchctl", sub_cmd] cmd.extend(args) # Run command - kwargs['python_shell'] = False + kwargs["python_shell"] = False kwargs = salt.utils.args.clean_kwargs(**kwargs) - ret = __salt__['cmd.run_all'](cmd, **kwargs) + ret = __salt__["cmd.run_all"](cmd, **kwargs) error = _check_launchctl_stderr(ret) # Raise an error or return successful result - if ret['retcode'] or error: - out = 'Failed to {0} service:\n'.format(sub_cmd) - out += 'stdout: {0}\n'.format(ret['stdout']) - out += 'stderr: {0}\n'.format(ret['stderr']) - out += 'retcode: {0}'.format(ret['retcode']) + if ret["retcode"] or error: + out = "Failed to {0} service:\n".format(sub_cmd) + out += "stdout: {0}\n".format(ret["stdout"]) + out += "stderr: {0}\n".format(ret["stderr"]) + out += "retcode: {0}".format(ret["retcode"]) raise CommandExecutionError(out) else: - return ret['stdout'] if return_stdout else True + return ret["stdout"] if return_stdout else True def _read_plist_file(root, file_name): - ''' + """ :param root: The root path of the plist file :param file_name: The name of the plist file :return: An empty dictionary if the plist file was invalid, otherwise, a dictionary with plist data - ''' + """ file_path = os.path.join(root, file_name) - log.debug('read_plist: Gathering service info for {}'.format(file_path)) + log.debug("read_plist: Gathering service info for {}".format(file_path)) # Must be a plist file - if not file_path.lower().endswith('.plist'): - log.debug('read_plist: Not a plist file: {}'.format(file_path)) + if not file_path.lower().endswith(".plist"): + log.debug("read_plist: Not a plist file: {}".format(file_path)) return {} # ignore broken symlinks if not os.path.exists(os.path.realpath(file_path)): - log.warning('read_plist: Ignoring broken symlink: {}'.format(file_path)) + log.warning("read_plist: Ignoring broken symlink: {}".format(file_path)) return {} try: @@ -339,13 +356,17 @@ def _read_plist_file(root, file_name): # uses a different API than py3. plist = plistlib.readPlist(file_path) else: - with salt.utils.files.fopen(file_path, 'rb') as handle: + with salt.utils.files.fopen(file_path, "rb") as handle: plist = plistlib.load(handle) except plistlib.InvalidFileException: # Raised in python3 if the file is not XML. # There's nothing we can do; move on to the next one. - log.warning('read_plist: Unable to parse "{}" as it is invalid XML: InvalidFileException.'.format(file_path)) + log.warning( + 'read_plist: Unable to parse "{}" as it is invalid XML: InvalidFileException.'.format( + file_path + ) + ) return {} except xml.parsers.expat.ExpatError: @@ -353,34 +374,46 @@ def _read_plist_file(root, file_name): # Raised by py3 if the file is XML, but with errors. if six.PY3: # There's an error in the XML, so move on. - log.warning('read_plist: Unable to parse "{}" as it is invalid XML: xml.parsers.expat.ExpatError.'.format(file_path)) + log.warning( + 'read_plist: Unable to parse "{}" as it is invalid XML: xml.parsers.expat.ExpatError.'.format( + file_path + ) + ) return {} # Use the system provided plutil program to attempt # conversion from binary. cmd = '/usr/bin/plutil -convert xml1 -o - -- "{0}"'.format(file_path) try: - plist_xml = __salt__['cmd.run'](cmd) + plist_xml = __salt__["cmd.run"](cmd) plist = plistlib.readPlistFromString(plist_xml) except xml.parsers.expat.ExpatError: # There's still an error in the XML, so move on. - log.warning('read_plist: Unable to parse "{}" as it is invalid XML: xml.parsers.expat.ExpatError.'.format(file_path)) + log.warning( + 'read_plist: Unable to parse "{}" as it is invalid XML: xml.parsers.expat.ExpatError.'.format( + file_path + ) + ) return {} - if 'Label' not in plist: + if "Label" not in plist: # not all launchd plists contain a Label key - log.debug('read_plist: Service does not contain a Label key. Skipping {}.'.format(file_path)) + log.debug( + "read_plist: Service does not contain a Label key. Skipping {}.".format( + file_path + ) + ) return {} return { - 'file_name': file_name, - 'file_path': file_path, - 'plist': plist, + "file_name": file_name, + "file_path": file_path, + "plist": plist, } def _available_services(refresh=False): - ''' + """ This is a helper function for getting the available macOS services. The strategy is to look through the known system locations for @@ -388,21 +421,27 @@ def _available_services(refresh=False): populating the list of services. Services can run without a plist file present, but normally services which have an automated startup will have a plist file, so this is a minor compromise. - ''' - if 'available_services' in __context__ and not refresh: - log.debug('Found context for available services.') - __context__['using_cached_services'] = True - return __context__['available_services'] + """ + if "available_services" in __context__ and not refresh: + log.debug("Found context for available services.") + __context__["using_cached_services"] = True + return __context__["available_services"] launchd_paths = { - '/Library/LaunchAgents', - '/Library/LaunchDaemons', - '/System/Library/LaunchAgents', - '/System/Library/LaunchDaemons', + "/Library/LaunchAgents", + "/Library/LaunchDaemons", + "/System/Library/LaunchAgents", + "/System/Library/LaunchDaemons", } - agent_path = '/Users/{}/Library/LaunchAgents' - launchd_paths.update({agent_path.format(user) for user in os.listdir('/Users/') if os.path.isdir(agent_path.format(user))}) + agent_path = "/Users/{}/Library/LaunchAgents" + launchd_paths.update( + { + agent_path.format(user) + for user in os.listdir("/Users/") + if os.path.isdir(agent_path.format(user)) + } + ) result = {} for launch_dir in launchd_paths: @@ -410,19 +449,19 @@ def _available_services(refresh=False): for file_name in files: data = _read_plist_file(root, file_name) if data: - result[data['plist']['Label'].lower()] = data + result[data["plist"]["Label"].lower()] = data # put this in __context__ as this is a time consuming function. # a fix for this issue. https://github.com/saltstack/salt/issues/48414 - __context__['available_services'] = result + __context__["available_services"] = result # this is a fresh gathering of services, set cached to false - __context__['using_cached_services'] = False + __context__["using_cached_services"] = False return result def available_services(refresh=False): - ''' + """ Return a dictionary of all available services on the system :param bool refresh: If you wish to refresh the available services @@ -437,13 +476,13 @@ def available_services(refresh=False): import salt.utils.mac_service salt.utils.mac_service.available_services() - ''' - log.debug('Loading available services') + """ + log.debug("Loading available services") return _available_services(refresh) def console_user(username=False): - ''' + """ Gets the UID or Username of the current console user. :return: The uid or username of the console user. @@ -462,13 +501,13 @@ def console_user(username=False): import salt.utils.mac_service salt.utils.mac_service.console_user() - ''' + """ try: # returns the 'st_uid' stat from the /dev/console file. - uid = os.stat('/dev/console')[4] + uid = os.stat("/dev/console")[4] except (OSError, IndexError): # we should never get here but raise an error if so - raise CommandExecutionError('Failed to get a UID for the console user.') + raise CommandExecutionError("Failed to get a UID for the console user.") if username: return pwd.getpwuid(uid)[0] diff --git a/salt/utils/mako.py b/salt/utils/mako.py index 7e5adaafe24..a3d11e236ba 100644 --- a/salt/utils/mako.py +++ b/salt/utils/mako.py @@ -1,11 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Functions for working with Mako templates -''' +""" from __future__ import absolute_import, unicode_literals try: - from mako.lookup import TemplateCollection, TemplateLookup # pylint: disable=import-error,3rd-party-module-not-gated + from mako.lookup import ( + TemplateCollection, + TemplateLookup, + ) # pylint: disable=import-error,3rd-party-module-not-gated + HAS_MAKO = True except ImportError: HAS_MAKO = False @@ -15,7 +19,10 @@ if HAS_MAKO: import os # Import third-party libs - from salt.ext.six.moves.urllib.parse import urlparse # pylint: disable=import-error,no-name-in-module + # pylint: disable=import-error,no-name-in-module + from salt.ext.six.moves.urllib.parse import urlparse + + # pylint: enable=import-error,no-name-in-module # Import salt libs import salt.fileclient @@ -49,32 +56,31 @@ if HAS_MAKO: """ - def __init__(self, opts, saltenv='base', pillar_rend=False): + def __init__(self, opts, saltenv="base", pillar_rend=False): self.opts = opts self.saltenv = saltenv self._file_client = None self.pillar_rend = pillar_rend - self.lookup = TemplateLookup(directories='/') + self.lookup = TemplateLookup(directories="/") self.cache = {} def file_client(self): - ''' + """ Setup and return file_client - ''' + """ if not self._file_client: self._file_client = salt.fileclient.get_file_client( - self.opts, self.pillar_rend) + self.opts, self.pillar_rend + ) return self._file_client def adjust_uri(self, uri, filename): scheme = urlparse(uri).scheme - if scheme in ('salt', 'file'): + if scheme in ("salt", "file"): return uri elif scheme: raise ValueError( - 'Unsupported URL scheme({0}) in {1}'.format( - scheme, uri - ) + "Unsupported URL scheme({0}) in {1}".format(scheme, uri) ) return self.lookup.adjust_uri(uri, filename) @@ -85,21 +91,20 @@ if HAS_MAKO: salt_uri = uri else: proto = "salt://" - if self.opts['file_client'] == 'local': - searchpath = self.opts['file_roots'][self.saltenv] + if self.opts["file_client"] == "local": + searchpath = self.opts["file_roots"][self.saltenv] else: - searchpath = [os.path.join(self.opts['cachedir'], - 'files', - self.saltenv)] + searchpath = [ + os.path.join(self.opts["cachedir"], "files", self.saltenv) + ] salt_uri = uri if uri.startswith(proto) else salt.utils.url.create(uri) self.cache_file(salt_uri) self.lookup = TemplateLookup(directories=searchpath) - return self.lookup.get_template(salt_uri[len(proto):]) + return self.lookup.get_template(salt_uri[len(proto) :]) def cache_file(self, fpath): if fpath not in self.cache: - self.cache[fpath] = self.file_client().get_file(fpath, - '', - True, - self.saltenv) + self.cache[fpath] = self.file_client().get_file( + fpath, "", True, self.saltenv + ) diff --git a/salt/utils/master.py b/salt/utils/master.py index 8152fb924fb..d040cc132fc 100644 --- a/salt/utils/master.py +++ b/salt/utils/master.py @@ -1,23 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" salt.utils.master ----------------- Utilities that can only be used on a salt master. -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals -import os + import logging +import os import signal -from threading import Thread, Event +from threading import Event, Thread + +import salt.cache +import salt.client +import salt.config # Import salt libs import salt.log -import salt.cache -import salt.client +import salt.payload import salt.pillar import salt.utils.atomicfile import salt.utils.files @@ -25,26 +29,24 @@ import salt.utils.minions import salt.utils.platform import salt.utils.stringutils import salt.utils.verify -import salt.payload from salt.exceptions import SaltException -import salt.config -from salt.utils.cache import CacheCli as cache_cli -from salt.utils.process import Process # Import third party libs from salt.ext import six +from salt.utils.cache import CacheCli as cache_cli +from salt.utils.process import Process from salt.utils.zeromq import zmq log = logging.getLogger(__name__) def get_running_jobs(opts): - ''' + """ Return the running jobs on this minion - ''' + """ ret = [] - proc_dir = os.path.join(opts['cachedir'], 'proc') + proc_dir = os.path.join(opts["cachedir"], "proc") if not os.path.isdir(proc_dir): return ret for fn_ in os.listdir(proc_dir): @@ -57,16 +59,16 @@ def get_running_jobs(opts): # proc files may be removed at any time during this process by # the master process that is executing the JID in question, so # we must ignore ENOENT during this process - log.trace('%s removed during processing by master process', path) + log.trace("%s removed during processing by master process", path) return ret def _read_proc_file(path, opts): - ''' + """ Return a dict of JID metadata, or None - ''' + """ serial = salt.payload.Serial(opts) - with salt.utils.files.fopen(path, 'rb') as fp_: + with salt.utils.files.fopen(path, "rb") as fp_: buf = fp_.read() fp_.close() if buf: @@ -76,62 +78,60 @@ def _read_proc_file(path, opts): try: os.remove(path) except IOError: - log.debug('Unable to remove proc file %s.', path) + log.debug("Unable to remove proc file %s.", path) return None if not isinstance(data, dict): # Invalid serial object return None - if not salt.utils.process.os_is_running(data['pid']): + if not salt.utils.process.os_is_running(data["pid"]): # The process is no longer running, clear out the file and # continue try: os.remove(path) except IOError: - log.debug('Unable to remove proc file %s.', path) + log.debug("Unable to remove proc file %s.", path) return None if not _check_cmdline(data): - pid = data.get('pid') + pid = data.get("pid") if pid: - log.warning( - 'PID %s exists but does not appear to be a salt process.', pid - ) + log.warning("PID %s exists but does not appear to be a salt process.", pid) try: os.remove(path) except IOError: - log.debug('Unable to remove proc file %s.', path) + log.debug("Unable to remove proc file %s.", path) return None return data def _check_cmdline(data): - ''' + """ In some cases where there are an insane number of processes being created on a system a PID can get recycled or assigned to a non-Salt process. On Linux this fn checks to make sure the PID we are checking on is actually a Salt process. For non-Linux systems we punt and just return True - ''' + """ if not salt.utils.platform.is_linux(): return True - pid = data.get('pid') + pid = data.get("pid") if not pid: return False - if not os.path.isdir('/proc'): + if not os.path.isdir("/proc"): return True - path = os.path.join('/proc/{0}/cmdline'.format(pid)) + path = os.path.join("/proc/{0}/cmdline".format(pid)) if not os.path.isfile(path): return False try: - with salt.utils.files.fopen(path, 'rb') as fp_: - return b'salt' in fp_.read() + with salt.utils.files.fopen(path, "rb") as fp_: + return b"salt" in fp_.read() except (OSError, IOError): return False class MasterPillarUtil(object): - ''' + """ Helper utility for easy access to targeted minion grain and pillar data, either from cached data on the master or retrieved on demand, or (by default) both. @@ -154,24 +154,26 @@ class MasterPillarUtil(object): tgt = 'web*' pillar_util = salt.utils.master.MasterPillarUtil(tgt, tgt_type='glob', opts=__opts__) pillar_data = pillar_util.get_minion_pillar() - ''' - def __init__(self, - tgt='', - tgt_type='glob', - saltenv=None, - use_cached_grains=True, - use_cached_pillar=True, - grains_fallback=True, - pillar_fallback=True, - opts=None): + """ - log.debug('New instance of %s created.', - self.__class__.__name__) + def __init__( + self, + tgt="", + tgt_type="glob", + saltenv=None, + use_cached_grains=True, + use_cached_pillar=True, + grains_fallback=True, + pillar_fallback=True, + opts=None, + ): + + log.debug("New instance of %s created.", self.__class__.__name__) if opts is None: - log.error('%s: Missing master opts init arg.', - self.__class__.__name__) - raise SaltException('{0}: Missing master opts init arg.'.format( - self.__class__.__name__)) + log.error("%s: Missing master opts init arg.", self.__class__.__name__) + raise SaltException( + "{0}: Missing master opts init arg.".format(self.__class__.__name__) + ) else: self.opts = opts self.serial = salt.payload.Serial(self.opts) @@ -184,27 +186,35 @@ class MasterPillarUtil(object): self.pillar_fallback = pillar_fallback self.cache = salt.cache.factory(opts) log.debug( - 'Init settings: tgt: \'%s\', tgt_type: \'%s\', saltenv: \'%s\', ' - 'use_cached_grains: %s, use_cached_pillar: %s, ' - 'grains_fallback: %s, pillar_fallback: %s', - tgt, tgt_type, saltenv, use_cached_grains, use_cached_pillar, - grains_fallback, pillar_fallback + "Init settings: tgt: '%s', tgt_type: '%s', saltenv: '%s', " + "use_cached_grains: %s, use_cached_pillar: %s, " + "grains_fallback: %s, pillar_fallback: %s", + tgt, + tgt_type, + saltenv, + use_cached_grains, + use_cached_pillar, + grains_fallback, + pillar_fallback, ) def _get_cached_mine_data(self, *minion_ids): # Return one dict with the cached mine data of the targeted minions mine_data = dict([(minion_id, {}) for minion_id in minion_ids]) - if (not self.opts.get('minion_data_cache', False) - and not self.opts.get('enforce_mine_cache', False)): - log.debug('Skipping cached mine data minion_data_cache' - 'and enfore_mine_cache are both disabled.') + if not self.opts.get("minion_data_cache", False) and not self.opts.get( + "enforce_mine_cache", False + ): + log.debug( + "Skipping cached mine data minion_data_cache" + "and enfore_mine_cache are both disabled." + ) return mine_data if not minion_ids: - minion_ids = self.cache.list('minions') + minion_ids = self.cache.list("minions") for minion_id in minion_ids: if not salt.utils.verify.valid_id(self.opts, minion_id): continue - mdata = self.cache.fetch('minions/{0}'.format(minion_id), 'mine') + mdata = self.cache.fetch("minions/{0}".format(minion_id), "mine") if isinstance(mdata, dict): mine_data[minion_id] = mdata return mine_data @@ -214,38 +224,40 @@ class MasterPillarUtil(object): # minions grains = dict([(minion_id, {}) for minion_id in minion_ids]) pillars = grains.copy() - if not self.opts.get('minion_data_cache', False): - log.debug('Skipping cached data because minion_data_cache is not ' - 'enabled.') + if not self.opts.get("minion_data_cache", False): + log.debug( + "Skipping cached data because minion_data_cache is not " "enabled." + ) return grains, pillars if not minion_ids: - minion_ids = self.cache.list('minions') + minion_ids = self.cache.list("minions") for minion_id in minion_ids: if not salt.utils.verify.valid_id(self.opts, minion_id): continue - mdata = self.cache.fetch('minions/{0}'.format(minion_id), 'data') + mdata = self.cache.fetch("minions/{0}".format(minion_id), "data") if not isinstance(mdata, dict): log.warning( - 'cache.fetch should always return a dict. ReturnedType: %s, MinionId: %s', + "cache.fetch should always return a dict. ReturnedType: %s, MinionId: %s", type(mdata).__name__, - minion_id + minion_id, ) continue - if 'grains' in mdata: - grains[minion_id] = mdata['grains'] - if 'pillar' in mdata: - pillars[minion_id] = mdata['pillar'] + if "grains" in mdata: + grains[minion_id] = mdata["grains"] + if "pillar" in mdata: + pillars[minion_id] = mdata["pillar"] return grains, pillars def _get_live_minion_grains(self, minion_ids): # Returns a dict of grains fetched directly from the minions log.debug('Getting live grains for minions: "%s"', minion_ids) - client = salt.client.get_local_client(self.opts['conf_file']) + client = salt.client.get_local_client(self.opts["conf_file"]) ret = client.cmd( - ','.join(minion_ids), - 'grains.items', - timeout=self.opts['timeout'], - tgt_type='list') + ",".join(minion_ids), + "grains.items", + timeout=self.opts["timeout"], + tgt_type="list", + ) return ret def _get_live_minion_pillar(self, minion_id=None, minion_grains=None): @@ -253,19 +265,13 @@ class MasterPillarUtil(object): if minion_id is None: return {} if not minion_grains: - log.warning( - 'Cannot get pillar data for %s: no grains supplied.', - minion_id - ) + log.warning("Cannot get pillar data for %s: no grains supplied.", minion_id) return {} - log.debug('Getting live pillar for %s', minion_id) + log.debug("Getting live pillar for %s", minion_id) pillar = salt.pillar.Pillar( - self.opts, - minion_grains, - minion_id, - self.saltenv, - self.opts['ext_pillar']) - log.debug('Compiling pillar for %s', minion_id) + self.opts, minion_grains, minion_id, self.saltenv, self.opts["ext_pillar"] + ) + log.debug("Compiling pillar for %s", minion_id) ret = pillar.compile_pillar() return ret @@ -274,23 +280,47 @@ class MasterPillarUtil(object): # on the minion. By default try to use cached grains first, then # fall back to querying the minion directly. ret = {} - cached_grains = kwargs.get('cached_grains', {}) + cached_grains = kwargs.get("cached_grains", {}) cret = {} lret = {} if self.use_cached_grains: - cret = dict([(minion_id, mcache) for (minion_id, mcache) in six.iteritems(cached_grains) if mcache]) - missed_minions = [minion_id for minion_id in minion_ids if minion_id not in cret] - log.debug('Missed cached minion grains for: %s', missed_minions) + cret = dict( + [ + (minion_id, mcache) + for (minion_id, mcache) in six.iteritems(cached_grains) + if mcache + ] + ) + missed_minions = [ + minion_id for minion_id in minion_ids if minion_id not in cret + ] + log.debug("Missed cached minion grains for: %s", missed_minions) if self.grains_fallback: lret = self._get_live_minion_grains(missed_minions) - ret = dict(list(six.iteritems(dict([(minion_id, {}) for minion_id in minion_ids]))) + list(lret.items()) + list(cret.items())) + ret = dict( + list(six.iteritems(dict([(minion_id, {}) for minion_id in minion_ids]))) + + list(lret.items()) + + list(cret.items()) + ) else: lret = self._get_live_minion_grains(minion_ids) - missed_minions = [minion_id for minion_id in minion_ids if minion_id not in lret] - log.debug('Missed live minion grains for: %s', missed_minions) + missed_minions = [ + minion_id for minion_id in minion_ids if minion_id not in lret + ] + log.debug("Missed live minion grains for: %s", missed_minions) if self.grains_fallback: - cret = dict([(minion_id, mcache) for (minion_id, mcache) in six.iteritems(cached_grains) if mcache]) - ret = dict(list(six.iteritems(dict([(minion_id, {}) for minion_id in minion_ids]))) + list(lret.items()) + list(cret.items())) + cret = dict( + [ + (minion_id, mcache) + for (minion_id, mcache) in six.iteritems(cached_grains) + if mcache + ] + ) + ret = dict( + list(six.iteritems(dict([(minion_id, {}) for minion_id in minion_ids]))) + + list(lret.items()) + + list(cret.items()) + ) return ret def _get_minion_pillar(self, *minion_ids, **kwargs): @@ -298,24 +328,68 @@ class MasterPillarUtil(object): # on the minion. By default try use the cached pillar first, then # fall back to rendering pillar on demand with the supplied grains. ret = {} - grains = kwargs.get('grains', {}) - cached_pillar = kwargs.get('cached_pillar', {}) + grains = kwargs.get("grains", {}) + cached_pillar = kwargs.get("cached_pillar", {}) cret = {} lret = {} if self.use_cached_pillar: - cret = dict([(minion_id, mcache) for (minion_id, mcache) in six.iteritems(cached_pillar) if mcache]) - missed_minions = [minion_id for minion_id in minion_ids if minion_id not in cret] - log.debug('Missed cached minion pillars for: %s', missed_minions) + cret = dict( + [ + (minion_id, mcache) + for (minion_id, mcache) in six.iteritems(cached_pillar) + if mcache + ] + ) + missed_minions = [ + minion_id for minion_id in minion_ids if minion_id not in cret + ] + log.debug("Missed cached minion pillars for: %s", missed_minions) if self.pillar_fallback: - lret = dict([(minion_id, self._get_live_minion_pillar(minion_id, grains.get(minion_id, {}))) for minion_id in missed_minions]) - ret = dict(list(six.iteritems(dict([(minion_id, {}) for minion_id in minion_ids]))) + list(lret.items()) + list(cret.items())) + lret = dict( + [ + ( + minion_id, + self._get_live_minion_pillar( + minion_id, grains.get(minion_id, {}) + ), + ) + for minion_id in missed_minions + ] + ) + ret = dict( + list(six.iteritems(dict([(minion_id, {}) for minion_id in minion_ids]))) + + list(lret.items()) + + list(cret.items()) + ) else: - lret = dict([(minion_id, self._get_live_minion_pillar(minion_id, grains.get(minion_id, {}))) for minion_id in minion_ids]) - missed_minions = [minion_id for minion_id in minion_ids if minion_id not in lret] - log.debug('Missed live minion pillars for: %s', missed_minions) + lret = dict( + [ + ( + minion_id, + self._get_live_minion_pillar( + minion_id, grains.get(minion_id, {}) + ), + ) + for minion_id in minion_ids + ] + ) + missed_minions = [ + minion_id for minion_id in minion_ids if minion_id not in lret + ] + log.debug("Missed live minion pillars for: %s", missed_minions) if self.pillar_fallback: - cret = dict([(minion_id, mcache) for (minion_id, mcache) in six.iteritems(cached_pillar) if mcache]) - ret = dict(list(six.iteritems(dict([(minion_id, {}) for minion_id in minion_ids]))) + list(lret.items()) + list(cret.items())) + cret = dict( + [ + (minion_id, mcache) + for (minion_id, mcache) in six.iteritems(cached_pillar) + if mcache + ] + ) + ret = dict( + list(six.iteritems(dict([(minion_id, {}) for minion_id in minion_ids]))) + + list(lret.items()) + + list(cret.items()) + ) return ret def _tgt_to_list(self): @@ -323,15 +397,24 @@ class MasterPillarUtil(object): minion_ids = [] ckminions = salt.utils.minions.CkMinions(self.opts) _res = ckminions.check_minions(self.tgt, self.tgt_type) - minion_ids = _res['minions'] + minion_ids = _res["minions"] if len(minion_ids) == 0: - log.debug('No minions matched for tgt="%s" and tgt_type="%s"', self.tgt, self.tgt_type) + log.debug( + 'No minions matched for tgt="%s" and tgt_type="%s"', + self.tgt, + self.tgt_type, + ) return {} - log.debug('Matching minions for tgt="%s" and tgt_type="%s": %s', self.tgt, self.tgt_type, minion_ids) + log.debug( + 'Matching minions for tgt="%s" and tgt_type="%s": %s', + self.tgt, + self.tgt_type, + minion_ids, + ) return minion_ids def get_minion_pillar(self): - ''' + """ Get pillar data for the targeted minions, either by fetching the cached minion data on the master, or by compiling the minion's pillar data on the master. @@ -348,31 +431,40 @@ class MasterPillarUtil(object): then try to get the minion grains directly from the minion. - Use the minion grains to compile the pillar directly from the master using salt.pillar.Pillar - ''' + """ minion_pillars = {} minion_grains = {} minion_ids = self._tgt_to_list() if self.tgt and not minion_ids: return {} - if any(arg for arg in [self.use_cached_grains, self.use_cached_pillar, self.grains_fallback, self.pillar_fallback]): - log.debug('Getting cached minion data') - cached_minion_grains, cached_minion_pillars = self._get_cached_minion_data(*minion_ids) + if any( + arg + for arg in [ + self.use_cached_grains, + self.use_cached_pillar, + self.grains_fallback, + self.pillar_fallback, + ] + ): + log.debug("Getting cached minion data") + cached_minion_grains, cached_minion_pillars = self._get_cached_minion_data( + *minion_ids + ) else: cached_minion_grains = {} cached_minion_pillars = {} - log.debug('Getting minion grain data for: %s', minion_ids) + log.debug("Getting minion grain data for: %s", minion_ids) minion_grains = self._get_minion_grains( - *minion_ids, - cached_grains=cached_minion_grains) - log.debug('Getting minion pillar data for: %s', minion_ids) + *minion_ids, cached_grains=cached_minion_grains + ) + log.debug("Getting minion pillar data for: %s", minion_ids) minion_pillars = self._get_minion_pillar( - *minion_ids, - grains=minion_grains, - cached_pillar=cached_minion_pillars) + *minion_ids, grains=minion_grains, cached_pillar=cached_minion_pillars + ) return minion_pillars def get_minion_grains(self): - ''' + """ Get grains data for the targeted minions, either by fetching the cached minion data on the master, or by fetching the grains directly on the minion. @@ -383,57 +475,59 @@ class MasterPillarUtil(object): - If the grains data for the minion is cached, use it. - If there is no cached grains data for a minion, then try to get the minion grains directly from the minion. - ''' + """ minion_grains = {} minion_ids = self._tgt_to_list() if not minion_ids: return {} if any(arg for arg in [self.use_cached_grains, self.grains_fallback]): - log.debug('Getting cached minion data.') - cached_minion_grains, cached_minion_pillars = self._get_cached_minion_data(*minion_ids) + log.debug("Getting cached minion data.") + cached_minion_grains, cached_minion_pillars = self._get_cached_minion_data( + *minion_ids + ) else: cached_minion_grains = {} - log.debug('Getting minion grain data for: %s', minion_ids) + log.debug("Getting minion grain data for: %s", minion_ids) minion_grains = self._get_minion_grains( - *minion_ids, - cached_grains=cached_minion_grains) + *minion_ids, cached_grains=cached_minion_grains + ) return minion_grains def get_cached_mine_data(self): - ''' + """ Get cached mine data for the targeted minions. - ''' + """ mine_data = {} minion_ids = self._tgt_to_list() - log.debug('Getting cached mine data for: %s', minion_ids) + log.debug("Getting cached mine data for: %s", minion_ids) mine_data = self._get_cached_mine_data(*minion_ids) return mine_data - def clear_cached_minion_data(self, - clear_pillar=False, - clear_grains=False, - clear_mine=False, - clear_mine_func=None): - ''' + def clear_cached_minion_data( + self, + clear_pillar=False, + clear_grains=False, + clear_mine=False, + clear_mine_func=None, + ): + """ Clear the cached data/files for the targeted minions. - ''' + """ clear_what = [] if clear_pillar: - clear_what.append('pillar') + clear_what.append("pillar") if clear_grains: - clear_what.append('grains') + clear_what.append("grains") if clear_mine: - clear_what.append('mine') + clear_what.append("mine") if clear_mine_func is not None: - clear_what.append('mine_func: \'{0}\''.format(clear_mine_func)) + clear_what.append("mine_func: '{0}'".format(clear_mine_func)) if not clear_what: - log.debug('No cached data types specified for clearing.') + log.debug("No cached data types specified for clearing.") return False minion_ids = self._tgt_to_list() - log.debug('Clearing cached %s data for: %s', - ', '.join(clear_what), - minion_ids) + log.debug("Clearing cached %s data for: %s", ", ".join(clear_what), minion_ids) if clear_pillar == clear_grains: # clear_pillar and clear_grains are both True or both False. # This means we don't deal with pillar/grains caches at all. @@ -445,7 +539,7 @@ class MasterPillarUtil(object): # in the same file, 'data.p' grains, pillars = self._get_cached_minion_data(*minion_ids) try: - c_minions = self.cache.list('minions') + c_minions = self.cache.list("minions") for minion_id in minion_ids: if not salt.utils.verify.valid_id(self.opts, minion_id): continue @@ -453,57 +547,60 @@ class MasterPillarUtil(object): if minion_id not in c_minions: # Cache bank for this minion does not exist. Nothing to do. continue - bank = 'minions/{0}'.format(minion_id) + bank = "minions/{0}".format(minion_id) minion_pillar = pillars.pop(minion_id, False) minion_grains = grains.pop(minion_id, False) - if ((clear_pillar and clear_grains) or - (clear_pillar and not minion_grains) or - (clear_grains and not minion_pillar)): + if ( + (clear_pillar and clear_grains) + or (clear_pillar and not minion_grains) + or (clear_grains and not minion_pillar) + ): # Not saving pillar or grains, so just delete the cache file - self.cache.flush(bank, 'data') + self.cache.flush(bank, "data") elif clear_pillar and minion_grains: - self.cache.store(bank, 'data', {'grains': minion_grains}) + self.cache.store(bank, "data", {"grains": minion_grains}) elif clear_grains and minion_pillar: - self.cache.store(bank, 'data', {'pillar': minion_pillar}) + self.cache.store(bank, "data", {"pillar": minion_pillar}) if clear_mine: # Delete the whole mine file - self.cache.flush(bank, 'mine') + self.cache.flush(bank, "mine") elif clear_mine_func is not None: # Delete a specific function from the mine file - mine_data = self.cache.fetch(bank, 'mine') + mine_data = self.cache.fetch(bank, "mine") if isinstance(mine_data, dict): if mine_data.pop(clear_mine_func, False): - self.cache.store(bank, 'mine', mine_data) + self.cache.store(bank, "mine", mine_data) except (OSError, IOError): return True return True class CacheTimer(Thread): - ''' + """ A basic timer class the fires timer-events every second. This is used for cleanup by the ConnectedCache() - ''' + """ + def __init__(self, opts, event): Thread.__init__(self) self.opts = opts self.stopped = event self.daemon = True - self.serial = salt.payload.Serial(opts.get('serial', '')) - self.timer_sock = os.path.join(self.opts['sock_dir'], 'con_timer.ipc') + self.serial = salt.payload.Serial(opts.get("serial", "")) + self.timer_sock = os.path.join(self.opts["sock_dir"], "con_timer.ipc") def run(self): - ''' + """ main loop that fires the event every second - ''' + """ context = zmq.Context() # the socket for outgoing timer events socket = context.socket(zmq.PUB) socket.setsockopt(zmq.LINGER, 100) - socket.bind('ipc://' + self.timer_sock) + socket.bind("ipc://" + self.timer_sock) count = 0 - log.debug('ConCache-Timer started') + log.debug("ConCache-Timer started") while not self.stopped.wait(1): socket.send(self.serial.dumps(count)) @@ -513,16 +610,16 @@ class CacheTimer(Thread): class CacheWorker(Process): - ''' + """ Worker for ConnectedCache which runs in its own process to prevent blocking of ConnectedCache main-loop when refreshing minion-list - ''' + """ def __init__(self, opts, **kwargs): - ''' + """ Sets up the zmq-connection to the ConCache - ''' + """ super(CacheWorker, self).__init__(**kwargs) self.opts = opts @@ -531,43 +628,43 @@ class CacheWorker(Process): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['opts'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["opts"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def run(self): - ''' + """ Gather currently connected minions and update the cache - ''' + """ new_mins = list(salt.utils.minions.CkMinions(self.opts).connected_ids()) cc = cache_cli(self.opts) cc.get_cached() cc.put_cache([new_mins]) - log.debug('ConCache CacheWorker update finished') + log.debug("ConCache CacheWorker update finished") class ConnectedCache(Process): - ''' + """ Provides access to all minions ids that the master has successfully authenticated. The cache is cleaned up regularly by comparing it to the IPs that have open connections to the master publisher port. - ''' + """ def __init__(self, opts, **kwargs): - ''' + """ starts the timer and inits the cache itself - ''' + """ super(ConnectedCache, self).__init__(**kwargs) - log.debug('ConCache initializing...') + log.debug("ConCache initializing...") # the possible settings for the cache self.opts = opts @@ -575,9 +672,9 @@ class ConnectedCache(Process): # the actual cached minion ids self.minions = [] - self.cache_sock = os.path.join(self.opts['sock_dir'], 'con_cache.ipc') - self.update_sock = os.path.join(self.opts['sock_dir'], 'con_upd.ipc') - self.upd_t_sock = os.path.join(self.opts['sock_dir'], 'con_timer.ipc') + self.cache_sock = os.path.join(self.opts["sock_dir"], "con_cache.ipc") + self.update_sock = os.path.join(self.opts["sock_dir"], "con_upd.ipc") + self.upd_t_sock = os.path.join(self.opts["sock_dir"], "con_timer.ipc") self.cleanup() # the timer provides 1-second intervals to the loop in run() @@ -593,29 +690,29 @@ class ConnectedCache(Process): # process so that a register_after_fork() equivalent will work on Windows. def __setstate__(self, state): self.__init__( - state['opts'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + state["opts"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def signal_handler(self, sig, frame): - ''' + """ handle signals and shutdown - ''' + """ self.stop() def cleanup(self): - ''' + """ remove sockets on shutdown - ''' - log.debug('ConCache cleaning up') + """ + log.debug("ConCache cleaning up") if os.path.exists(self.cache_sock): os.remove(self.cache_sock) if os.path.exists(self.update_sock): @@ -624,10 +721,10 @@ class ConnectedCache(Process): os.remove(self.upd_t_sock) def secure(self): - ''' + """ secure the sockets for root-only access - ''' - log.debug('ConCache securing sockets') + """ + log.debug("ConCache securing sockets") if os.path.exists(self.cache_sock): os.chmod(self.cache_sock, 0o600) if os.path.exists(self.update_sock): @@ -636,9 +733,9 @@ class ConnectedCache(Process): os.chmod(self.upd_t_sock, 0o600) def stop(self): - ''' + """ shutdown cache process - ''' + """ # avoid getting called twice self.cleanup() if self.running: @@ -647,27 +744,27 @@ class ConnectedCache(Process): self.timer.join() def run(self): - ''' + """ Main loop of the ConCache, starts updates in intervals and answers requests from the MWorkers - ''' + """ context = zmq.Context() # the socket for incoming cache requests creq_in = context.socket(zmq.REP) creq_in.setsockopt(zmq.LINGER, 100) - creq_in.bind('ipc://' + self.cache_sock) + creq_in.bind("ipc://" + self.cache_sock) # the socket for incoming cache-updates from workers cupd_in = context.socket(zmq.SUB) - cupd_in.setsockopt(zmq.SUBSCRIBE, b'') + cupd_in.setsockopt(zmq.SUBSCRIBE, b"") cupd_in.setsockopt(zmq.LINGER, 100) - cupd_in.bind('ipc://' + self.update_sock) + cupd_in.bind("ipc://" + self.update_sock) # the socket for the timer-event timer_in = context.socket(zmq.SUB) - timer_in.setsockopt(zmq.SUBSCRIBE, b'') + timer_in.setsockopt(zmq.SUBSCRIBE, b"") timer_in.setsockopt(zmq.LINGER, 100) - timer_in.connect('ipc://' + self.upd_t_sock) + timer_in.connect("ipc://" + self.upd_t_sock) poller = zmq.Poller() poller.register(creq_in, zmq.POLLIN) @@ -675,7 +772,7 @@ class ConnectedCache(Process): poller.register(timer_in, zmq.POLLIN) # our serializer - serial = salt.payload.Serial(self.opts.get('serial', '')) + serial = salt.payload.Serial(self.opts.get("serial", "")) # register a signal handler signal.signal(signal.SIGINT, self.signal_handler) @@ -683,7 +780,7 @@ class ConnectedCache(Process): # secure the sockets from the world self.secure() - log.info('ConCache started') + log.info("ConCache started") while self.running: @@ -693,18 +790,18 @@ class ConnectedCache(Process): except KeyboardInterrupt: self.stop() except zmq.ZMQError as zmq_err: - log.error('ConCache ZeroMQ-Error occurred') + log.error("ConCache ZeroMQ-Error occurred") log.exception(zmq_err) self.stop() # check for next cache-request if socks.get(creq_in) == zmq.POLLIN: msg = serial.loads(creq_in.recv()) - log.debug('ConCache Received request: %s', msg) + log.debug("ConCache Received request: %s", msg) # requests to the minion list are send as str's if isinstance(msg, six.string_types): - if msg == 'minions': + if msg == "minions": # Send reply back to client reply = serial.dumps(self.minions) creq_in.send(reply) @@ -713,11 +810,11 @@ class ConnectedCache(Process): if socks.get(cupd_in) == zmq.POLLIN: new_c_data = serial.loads(cupd_in.recv()) # tell the worker to exit - #cupd_in.send(serial.dumps('ACK')) + # cupd_in.send(serial.dumps('ACK')) # check if the returned data is usable if not isinstance(new_c_data, list): - log.error('ConCache Worker returned unusable result') + log.error("ConCache Worker returned unusable result") del new_c_data continue @@ -730,26 +827,27 @@ class ConnectedCache(Process): try: if len(new_c_data) == 0: - log.debug('ConCache Got empty update from worker') + log.debug("ConCache Got empty update from worker") continue data = new_c_data[0] if isinstance(data, six.string_types): if data not in self.minions: - log.debug('ConCache Adding minion %s to cache', - new_c_data[0]) + log.debug( + "ConCache Adding minion %s to cache", new_c_data[0] + ) self.minions.append(data) elif isinstance(data, list): - log.debug('ConCache Replacing minion list from worker') + log.debug("ConCache Replacing minion list from worker") self.minions = data except IndexError: - log.debug('ConCache Got malformed result dict from worker') + log.debug("ConCache Got malformed result dict from worker") del new_c_data - log.info('ConCache %s entries in cache', len(self.minions)) + log.info("ConCache %s entries in cache", len(self.minions)) # check for next timer-event to start new jobs if socks.get(timer_in) == zmq.POLLIN: @@ -765,49 +863,46 @@ class ConnectedCache(Process): cupd_in.close() timer_in.close() context.term() - log.debug('ConCache Shutting down') + log.debug("ConCache Shutting down") def ping_all_connected_minions(opts): client = salt.client.LocalClient() - if opts['minion_data_cache']: + if opts["minion_data_cache"]: tgt = list(salt.utils.minions.CkMinions(opts).connected_ids()) - form = 'list' + form = "list" else: - tgt = '*' - form = 'glob' - client.cmd(tgt, 'test.ping', tgt_type=form) + tgt = "*" + form = "glob" + client.cmd(tgt, "test.ping", tgt_type=form) def get_master_key(key_user, opts, skip_perm_errors=False): - if key_user == 'root': - if opts.get('user', 'root') != 'root': - key_user = opts.get('user', 'root') - if key_user.startswith('sudo_'): - key_user = opts.get('user', 'root') + if key_user == "root": + if opts.get("user", "root") != "root": + key_user = opts.get("user", "root") + if key_user.startswith("sudo_"): + key_user = opts.get("user", "root") if salt.utils.platform.is_windows(): # The username may contain '\' if it is in Windows # 'DOMAIN\username' format. Fix this for the keyfile path. - key_user = key_user.replace('\\', '_') - keyfile = os.path.join(opts['cachedir'], - '.{0}_key'.format(key_user)) + key_user = key_user.replace("\\", "_") + keyfile = os.path.join(opts["cachedir"], ".{0}_key".format(key_user)) # Make sure all key parent directories are accessible - salt.utils.verify.check_path_traversal(opts['cachedir'], - key_user, - skip_perm_errors) + salt.utils.verify.check_path_traversal(opts["cachedir"], key_user, skip_perm_errors) try: - with salt.utils.files.fopen(keyfile, 'r') as key: + with salt.utils.files.fopen(keyfile, "r") as key: return key.read() except (OSError, IOError): # Fall back to eauth - return '' + return "" def get_values_of_matching_keys(pattern_dict, user_name): - ''' + """ Check a whitelist and/or blacklist to see if the value matches it. - ''' + """ ret = [] for expr in pattern_dict: if salt.utils.stringutils.expr_match(user_name, expr): @@ -816,9 +911,9 @@ def get_values_of_matching_keys(pattern_dict, user_name): # test code for the ConCache class -if __name__ == '__main__': +if __name__ == "__main__": - opts = salt.config.master_config('/etc/salt/master') + opts = salt.config.master_config("/etc/salt/master") conc = ConnectedCache(opts) conc.start() diff --git a/salt/utils/mattermost.py b/salt/utils/mattermost.py index 42290008685..d52d852720e 100644 --- a/salt/utils/mattermost.py +++ b/salt/utils/mattermost.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Library for interacting with Mattermost Incoming Webhooks :configuration: This module can be used by specifying the name of a configuration profile in the minion config, minion pillar, or master @@ -9,64 +9,59 @@ Library for interacting with Mattermost Incoming Webhooks mattermost: hook: 3tdgo8restnxiykdx88wqtxryr api_url: https://example.com -''' +""" from __future__ import absolute_import, unicode_literals import logging + +import salt.ext.six.moves.http_client + +# pylint: enable=import-error,no-name-in-module +import salt.utils.http +from salt.ext import six + # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext.six.moves.urllib.parse import urljoin as _urljoin -import salt.ext.six.moves.http_client from salt.version import __version__ -# pylint: enable=import-error,no-name-in-module -import salt.utils.http - -from salt.ext import six log = logging.getLogger(__name__) -def query(hook=None, - api_url=None, - data=None): - ''' +def query(hook=None, api_url=None, data=None): + """ Mattermost object method function to construct and execute on the API URL. :param api_url: The Mattermost API URL :param hook: The Mattermost hook. :param data: The data to be sent for POST method. :return: The json response from the API call or False. - ''' - method = 'POST' + """ + method = "POST" - ret = {'message': '', - 'res': True} + ret = {"message": "", "res": True} - base_url = _urljoin(api_url, '/hooks/') + base_url = _urljoin(api_url, "/hooks/") url = _urljoin(base_url, six.text_type(hook)) - result = salt.utils.http.query(url, - method, - data=data, - decode=True, - status=True) + result = salt.utils.http.query(url, method, data=data, decode=True, status=True) - if result.get('status', None) == salt.ext.six.moves.http_client.OK: - ret['message'] = 'Message posted {0} correctly'.format(data) + if result.get("status", None) == salt.ext.six.moves.http_client.OK: + ret["message"] = "Message posted {0} correctly".format(data) return ret - elif result.get('status', None) == salt.ext.six.moves.http_client.NO_CONTENT: + elif result.get("status", None) == salt.ext.six.moves.http_client.NO_CONTENT: return True else: log.debug(url) log.debug(data) log.debug(result) - if 'dict' in result: - _result = result['dict'] - if 'error' in _result: - ret['message'] = result['error'] - ret['res'] = False + if "dict" in result: + _result = result["dict"] + if "error" in _result: + ret["message"] = result["error"] + ret["res"] = False return ret - ret['message'] = 'Message not posted' + ret["message"] = "Message not posted" else: - ret['message'] = 'invalid_auth' - ret['res'] = False + ret["message"] = "invalid_auth" + ret["res"] = False return ret diff --git a/salt/utils/memcached.py b/salt/utils/memcached.py index 8e02317a24c..7e1a1341629 100644 --- a/salt/utils/memcached.py +++ b/salt/utils/memcached.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Utilities for working with memcache :depends: - python-memcached @@ -33,25 +33,27 @@ optionally, the name of a profile to be used. It should be noted that some usages of memcached may require a profile to be specified, rather than top-level configurations. This being the case, it is better to always use a named configuration profile, as shown above. -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import logging # Import salt libs from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.ext.six import integer_types from salt.ext import six +from salt.ext.six import integer_types # Import third party libs try: import memcache + HAS_LIBS = True except ImportError: HAS_LIBS = False -DEFAULT_HOST = '127.0.0.1' +DEFAULT_HOST = "127.0.0.1" DEFAULT_PORT = 11211 DEFAULT_TIME = 0 DEFAULT_MIN_COMPRESS_LEN = 0 @@ -60,27 +62,25 @@ DEFAULT_MIN_COMPRESS_LEN = 0 log = logging.getLogger(__name__) # Don't shadow built-ins -__func_alias__ = { - 'set_': 'set' -} +__func_alias__ = {"set_": "set"} # Although utils are often directly imported, it is also possible # to use the loader. def __virtual__(): - ''' + """ Only load if python-memcached is installed - ''' + """ return True if HAS_LIBS else False def get_conn(opts, profile=None, host=None, port=None): - ''' + """ Return a conn object for accessing memcached - ''' + """ if not (host and port): - opts_pillar = opts.get('pillar', {}) - opts_master = opts_pillar.get('master', {}) + opts_pillar = opts.get("pillar", {}) + opts_master = opts_pillar.get("master", {}) opts_merged = {} opts_merged.update(opts_master) @@ -92,53 +92,48 @@ def get_conn(opts, profile=None, host=None, port=None): else: conf = opts_merged - host = conf.get('memcached.host', DEFAULT_HOST) - port = conf.get('memcached.port', DEFAULT_PORT) + host = conf.get("memcached.host", DEFAULT_HOST) + port = conf.get("memcached.port", DEFAULT_PORT) if not six.text_type(port).isdigit(): - raise SaltInvocationError('port must be an integer') + raise SaltInvocationError("port must be an integer") if HAS_LIBS: - return memcache.Client(['{0}:{1}'.format(host, port)]) + return memcache.Client(["{0}:{1}".format(host, port)]) else: raise CommandExecutionError( - '(unable to import memcache, ' - 'module most likely not installed)' + "(unable to import memcache, " "module most likely not installed)" ) def _check_stats(conn): - ''' + """ Helper function to check the stats data passed into it, and raise an exception if none are returned. Otherwise, the stats are returned. - ''' + """ stats = conn.get_stats() if not stats: - raise CommandExecutionError( - 'memcached server is down or does not exist' - ) + raise CommandExecutionError("memcached server is down or does not exist") return stats -def set_(conn, - key, - value, - time=DEFAULT_TIME, - min_compress_len=DEFAULT_MIN_COMPRESS_LEN): - ''' +def set_( + conn, key, value, time=DEFAULT_TIME, min_compress_len=DEFAULT_MIN_COMPRESS_LEN +): + """ Set a key on the memcached server, overwriting the value if it exists. - ''' + """ if not isinstance(time, integer_types): - raise SaltInvocationError('\'time\' must be an integer') + raise SaltInvocationError("'time' must be an integer") if not isinstance(min_compress_len, integer_types): - raise SaltInvocationError('\'min_compress_len\' must be an integer') + raise SaltInvocationError("'min_compress_len' must be an integer") _check_stats(conn) return conn.set(key, value, time, min_compress_len) def get(conn, key): - ''' + """ Retrieve value for a key - ''' + """ _check_stats(conn) return conn.get(key) diff --git a/salt/utils/migrations.py b/salt/utils/migrations.py index dbd95ee26a2..5a0a1694d0a 100644 --- a/salt/utils/migrations.py +++ b/salt/utils/migrations.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Migration tools -''' +""" from __future__ import absolute_import, unicode_literals # Import python libs @@ -13,25 +13,26 @@ import salt.syspaths as syspaths def migrate_paths(opts): - ''' + """ Migrate old minion and master pki file paths to new ones. - ''' - oldpki_dir = os.path.join(syspaths.CONFIG_DIR, 'pki') + """ + oldpki_dir = os.path.join(syspaths.CONFIG_DIR, "pki") if not os.path.exists(oldpki_dir): # There's not even a pki directory, don't bother migrating return - newpki_dir = opts['pki_dir'] + newpki_dir = opts["pki_dir"] - if opts['default_include'].startswith('master'): - keepers = ['master.pem', - 'master.pub', - 'syndic_master.pub', - 'minions', - 'minions_pre', - 'minions_rejected', - ] + if opts["default_include"].startswith("master"): + keepers = [ + "master.pem", + "master.pub", + "syndic_master.pub", + "minions", + "minions_pre", + "minions_rejected", + ] if not os.path.exists(newpki_dir): os.makedirs(newpki_dir) for item in keepers: @@ -40,11 +41,12 @@ def migrate_paths(opts): if os.path.exists(oi_path) and not os.path.exists(ni_path): shutil.move(oi_path, ni_path) - if opts['default_include'].startswith('minion'): - keepers = ['minion_master.pub', - 'minion.pem', - 'minion.pub', - ] + if opts["default_include"].startswith("minion"): + keepers = [ + "minion_master.pub", + "minion.pem", + "minion.pub", + ] if not os.path.exists(newpki_dir): os.makedirs(newpki_dir) for item in keepers: diff --git a/salt/utils/mine.py b/salt/utils/mine.py index fa1f4d90248..4f084808459 100644 --- a/salt/utils/mine.py +++ b/salt/utils/mine.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" This module contains routines used for the salt mine -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import logging # Import salt libs @@ -14,17 +15,13 @@ import salt.utils.data log = logging.getLogger(__name__) -MINE_ITEM_ACL_ID = '__saltmine_acl__' +MINE_ITEM_ACL_ID = "__saltmine_acl__" MINE_ITEM_ACL_VERSION = 1 -MINE_ITEM_ACL_DATA = '__data__' +MINE_ITEM_ACL_DATA = "__data__" -def minion_side_acl_denied( - minion_acl_cache, - mine_minion, - mine_function, - req_minion): - ''' +def minion_side_acl_denied(minion_acl_cache, mine_minion, mine_function, req_minion): + """ Helper function to determine if a ``req_minion`` is not allowed to retrieve ``mine_function``-data from the mine of ``mine_minion``. @@ -40,22 +37,21 @@ def minion_side_acl_denied( False if no ACL has been defined for ``mine_minion``, ``mine_function``. False if an ACL has been defined and it grants access. True if an ACL has been defined and does not grant access. - ''' + """ minion_acl_entry = minion_acl_cache.get(mine_minion, {}).get(mine_function, []) ret = minion_acl_entry and req_minion not in minion_acl_entry if ret: - log.debug('Salt mine request from %s for function %s on minion %s denied.', - req_minion, - mine_function, - mine_minion) + log.debug( + "Salt mine request from %s for function %s on minion %s denied.", + req_minion, + mine_function, + mine_minion, + ) return ret -def wrap_acl_structure( - function_data, - allow_tgt=None, - allow_tgt_type=None): - ''' +def wrap_acl_structure(function_data, allow_tgt=None, allow_tgt_type=None): + """ Helper function to convert an non-ACL mine entry into the new entry which includes ACL data. @@ -67,22 +63,23 @@ def wrap_acl_structure( :rtype: dict :return: Mine entry structured to include ACL data. - ''' + """ res = { MINE_ITEM_ACL_DATA: function_data, MINE_ITEM_ACL_ID: MINE_ITEM_ACL_VERSION, } # Add minion-side ACL if allow_tgt: - res.update(salt.utils.data.filter_falsey({ - 'allow_tgt': allow_tgt, - 'allow_tgt_type': allow_tgt_type, - })) + res.update( + salt.utils.data.filter_falsey( + {"allow_tgt": allow_tgt, "allow_tgt_type": allow_tgt_type} + ) + ) return res def parse_function_definition(function_definition): - ''' + """ Helper function to parse the mine_function definition as provided in config, or pillar. @@ -91,14 +88,14 @@ def parse_function_definition(function_definition): :rtype: tuple :return: Tuple with function_name, function_args, function_kwargs, minion_acl (dict) - ''' + """ function_name = None function_args = [] function_kwargs = {} minion_acl = {} if isinstance(function_definition, dict): # dictionary format for specifying mine function - function_name = function_definition.pop('mine_function', None) + function_name = function_definition.pop("mine_function", None) function_kwargs = function_definition elif isinstance(function_definition, list): for item in function_definition: @@ -107,11 +104,13 @@ def parse_function_definition(function_definition): function_kwargs.update(item) else: function_args.append(item) - function_name = function_kwargs.pop('mine_function', None) + function_name = function_kwargs.pop("mine_function", None) - minion_acl = salt.utils.data.filter_falsey({ - 'allow_tgt': function_kwargs.pop('allow_tgt', None), - 'allow_tgt_type': function_kwargs.pop('allow_tgt_type', None), - }) + minion_acl = salt.utils.data.filter_falsey( + { + "allow_tgt": function_kwargs.pop("allow_tgt", None), + "allow_tgt_type": function_kwargs.pop("allow_tgt_type", None), + } + ) return (function_name, function_args, function_kwargs, minion_acl) diff --git a/salt/utils/minion.py b/salt/utils/minion.py index c947498d2b6..cf716e34cc8 100644 --- a/salt/utils/minion.py +++ b/salt/utils/minion.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Utility functions for minions -''' +""" # Import Python Libs from __future__ import absolute_import, unicode_literals -import os + import logging +import os import threading # Import Salt Libs @@ -19,12 +20,12 @@ log = logging.getLogger(__name__) def running(opts): - ''' + """ Return the running jobs on this minion - ''' + """ ret = [] - proc_dir = os.path.join(opts['cachedir'], 'proc') + proc_dir = os.path.join(opts["cachedir"], "proc") if not os.path.isdir(proc_dir): return ret for fn_ in os.listdir(proc_dir): @@ -42,31 +43,27 @@ def running(opts): def cache_jobs(opts, jid, ret): - ''' + """ Write job information to cache - ''' + """ serial = salt.payload.Serial(opts=opts) - fn_ = os.path.join( - opts['cachedir'], - 'minion_jobs', - jid, - 'return.p') + fn_ = os.path.join(opts["cachedir"], "minion_jobs", jid, "return.p") jdir = os.path.dirname(fn_) if not os.path.isdir(jdir): os.makedirs(jdir) - with salt.utils.files.fopen(fn_, 'w+b') as fp_: + with salt.utils.files.fopen(fn_, "w+b") as fp_: fp_.write(serial.dumps(ret)) def _read_proc_file(path, opts): - ''' + """ Return a dict of JID metadata, or None - ''' + """ serial = salt.payload.Serial(opts) current_thread = threading.currentThread().name pid = os.getpid() - with salt.utils.files.fopen(path, 'rb') as fp_: + with salt.utils.files.fopen(path, "rb") as fp_: buf = fp_.read() fp_.close() if buf: @@ -76,74 +73,72 @@ def _read_proc_file(path, opts): try: os.remove(path) except IOError: - log.debug('Unable to remove proc file %s.', path) + log.debug("Unable to remove proc file %s.", path) return None if not isinstance(data, dict): # Invalid serial object return None - if not salt.utils.process.os_is_running(data['pid']): + if not salt.utils.process.os_is_running(data["pid"]): # The process is no longer running, clear out the file and # continue try: os.remove(path) except IOError: - log.debug('Unable to remove proc file %s.', path) + log.debug("Unable to remove proc file %s.", path) return None - if opts.get('multiprocessing'): - if data.get('pid') == pid: + if opts.get("multiprocessing"): + if data.get("pid") == pid: return None else: - if data.get('pid') != pid: + if data.get("pid") != pid: try: os.remove(path) except IOError: - log.debug('Unable to remove proc file %s.', path) + log.debug("Unable to remove proc file %s.", path) return None - if data.get('jid') == current_thread: + if data.get("jid") == current_thread: return None - if not data.get('jid') in [x.name for x in threading.enumerate()]: + if not data.get("jid") in [x.name for x in threading.enumerate()]: try: os.remove(path) except IOError: - log.debug('Unable to remove proc file %s.', path) + log.debug("Unable to remove proc file %s.", path) return None if not _check_cmdline(data): - pid = data.get('pid') + pid = data.get("pid") if pid: - log.warning( - 'PID %s exists but does not appear to be a salt process.', pid - ) + log.warning("PID %s exists but does not appear to be a salt process.", pid) try: os.remove(path) except IOError: - log.debug('Unable to remove proc file %s.', path) + log.debug("Unable to remove proc file %s.", path) return None return data def _check_cmdline(data): - ''' + """ In some cases where there are an insane number of processes being created on a system a PID can get recycled or assigned to a non-Salt process. On Linux this fn checks to make sure the PID we are checking on is actually a Salt process. For non-Linux systems we punt and just return True - ''' + """ if not salt.utils.platform.is_linux(): return True - pid = data.get('pid') + pid = data.get("pid") if not pid: return False - if not os.path.isdir('/proc'): + if not os.path.isdir("/proc"): return True - path = os.path.join('/proc/{0}/cmdline'.format(pid)) + path = os.path.join("/proc/{0}/cmdline".format(pid)) if not os.path.isfile(path): return False try: - with salt.utils.files.fopen(path, 'rb') as fp_: - if b'salt' in fp_.read(): + with salt.utils.files.fopen(path, "rb") as fp_: + if b"salt" in fp_.read(): return True except (OSError, IOError): return False diff --git a/salt/utils/minions.py b/salt/utils/minions.py index b02ab347776..8a64a49a20b 100644 --- a/salt/utils/minions.py +++ b/salt/utils/minions.py @@ -1,15 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" This module contains routines used to verify the matcher against the minions expected to return -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals -import os + import fnmatch -import re import logging +import os +import re + +import salt.auth.ldap +import salt.cache # Import salt libs import salt.payload @@ -19,17 +23,17 @@ import salt.utils.files import salt.utils.network import salt.utils.stringutils import salt.utils.versions -from salt.defaults import DEFAULT_TARGET_DELIM -from salt.exceptions import CommandExecutionError, SaltCacheError -import salt.auth.ldap -import salt.cache -from salt.ext import six # Import 3rd-party libs from salt._compat import ipaddress +from salt.defaults import DEFAULT_TARGET_DELIM +from salt.exceptions import CommandExecutionError, SaltCacheError +from salt.ext import six + HAS_RANGE = False try: import seco.range # pylint: disable=import-error + HAS_RANGE = True except ImportError: pass @@ -37,14 +41,14 @@ except ImportError: log = logging.getLogger(__name__) TARGET_REX = re.compile( - r'''(?x) + r"""(?x) ( (?P<engine>G|P|I|J|L|N|S|E|R) # Possible target engines (?P<delimiter>(?<=G|P|I|J).)? # Optional delimiter for specific engines @)? # Engine+delimiter are separated by a '@' # character and are optional for the target - (?P<pattern>.+)$''' # The pattern passed to the target engine - ) + (?P<pattern>.+)$""" # The pattern passed to the target engine +) def _nodegroup_regex(nodegroup, words, opers): @@ -53,25 +57,29 @@ def _nodegroup_regex(nodegroup, words, opers): if (set(ret) - opers_set) == set(ret): # No compound operators found in nodegroup definition. Check for # group type specifiers - group_type_re = re.compile('^[A-Z]@') - regex_chars = ['(', '[', '{', '\\', '?', '}', ']', ')'] - if not [x for x in ret if '*' in x or group_type_re.match(x)]: + group_type_re = re.compile("^[A-Z]@") + regex_chars = ["(", "[", "{", "\\", "?", "}", "]", ")"] + if not [x for x in ret if "*" in x or group_type_re.match(x)]: # No group type specifiers and no wildcards. # Treat this as an expression. if [x for x in ret if x in [x for y in regex_chars if y in x]]: - joined = 'E@' + ','.join(ret) + joined = "E@" + ",".join(ret) log.debug( - 'Nodegroup \'%s\' (%s) detected as an expression. ' - 'Assuming compound matching syntax of \'%s\'', - nodegroup, ret, joined + "Nodegroup '%s' (%s) detected as an expression. " + "Assuming compound matching syntax of '%s'", + nodegroup, + ret, + joined, ) else: # Treat this as a list of nodenames. - joined = 'L@' + ','.join(ret) + joined = "L@" + ",".join(ret) log.debug( - 'Nodegroup \'%s\' (%s) detected as list of nodenames. ' - 'Assuming compound matching syntax of \'%s\'', - nodegroup, ret, joined + "Nodegroup '%s' (%s) detected as list of nodenames. " + "Assuming compound matching syntax of '%s'", + nodegroup, + ret, + joined, ) # Return data must be a list of compound matching components # to be fed into compound matcher. Enclose return data in list. @@ -79,16 +87,16 @@ def _nodegroup_regex(nodegroup, words, opers): def parse_target(target_expression): - '''Parse `target_expressing` splitting it into `engine`, `delimiter`, - `pattern` - returns a dict''' + """Parse `target_expressing` splitting it into `engine`, `delimiter`, + `pattern` - returns a dict""" match = TARGET_REX.match(target_expression) if not match: log.warning('Unable to parse target "%s"', target_expression) ret = { - 'engine': None, - 'delimiter': None, - 'pattern': target_expression, + "engine": None, + "delimiter": None, + "pattern": target_expression, } else: ret = match.groupdict() @@ -96,47 +104,49 @@ def parse_target(target_expression): def get_minion_data(minion, opts): - ''' + """ Get the grains/pillar for a specific minion. If minion is None, it will return the grains/pillar for the first minion it finds. Return value is a tuple of the minion ID, grains, and pillar - ''' + """ grains = None pillar = None - if opts.get('minion_data_cache', False): + if opts.get("minion_data_cache", False): cache = salt.cache.factory(opts) if minion is None: - for id_ in cache.list('minions'): - data = cache.fetch('minions/{0}'.format(id_), 'data') + for id_ in cache.list("minions"): + data = cache.fetch("minions/{0}".format(id_), "data") if data is None: continue else: - data = cache.fetch('minions/{0}'.format(minion), 'data') + data = cache.fetch("minions/{0}".format(minion), "data") if data is not None: - grains = data.get('grains', None) - pillar = data.get('pillar', None) + grains = data.get("grains", None) + pillar = data.get("pillar", None) return minion if minion else None, grains, pillar def nodegroup_comp(nodegroup, nodegroups, skip=None, first_call=True): - ''' + """ Recursively expand ``nodegroup`` from ``nodegroups``; ignore nodegroups in ``skip`` If a top-level (non-recursive) call finds no nodegroups, return the original nodegroup definition (for backwards compatibility). Keep track of recursive calls via `first_call` argument - ''' + """ expanded_nodegroup = False if skip is None: skip = set() elif nodegroup in skip: - log.error('Failed nodegroup expansion: illegal nested nodegroup "%s"', nodegroup) - return '' + log.error( + 'Failed nodegroup expansion: illegal nested nodegroup "%s"', nodegroup + ) + return "" if nodegroup not in nodegroups: log.error('Failed nodegroup expansion: unknown nodegroup "%s"', nodegroup) - return '' + return "" nglookup = nodegroups[nodegroup] if isinstance(nglookup, six.string_types): @@ -144,31 +154,36 @@ def nodegroup_comp(nodegroup, nodegroups, skip=None, first_call=True): elif isinstance(nglookup, (list, tuple)): words = nglookup else: - log.error('Nodegroup \'%s\' (%s) is neither a string, list nor tuple', - nodegroup, nglookup) - return '' + log.error( + "Nodegroup '%s' (%s) is neither a string, list nor tuple", + nodegroup, + nglookup, + ) + return "" skip.add(nodegroup) ret = [] - opers = ['and', 'or', 'not', '(', ')'] + opers = ["and", "or", "not", "(", ")"] for word in words: if not isinstance(word, six.string_types): word = six.text_type(word) if word in opers: ret.append(word) - elif len(word) >= 3 and word.startswith('N@'): + elif len(word) >= 3 and word.startswith("N@"): expanded_nodegroup = True - ret.extend(nodegroup_comp(word[2:], nodegroups, skip=skip, first_call=False)) + ret.extend( + nodegroup_comp(word[2:], nodegroups, skip=skip, first_call=False) + ) else: ret.append(word) if ret: - ret.insert(0, '(') - ret.append(')') + ret.insert(0, "(") + ret.append(")") skip.remove(nodegroup) - log.debug('nodegroup_comp(%s) => %s', nodegroup, ret) + log.debug("nodegroup_comp(%s) => %s", nodegroup, ret) # Only return list form if a nodegroup was expanded. Otherwise return # the original string to conserve backwards compat if expanded_nodegroup or not first_call: @@ -184,124 +199,132 @@ def nodegroup_comp(nodegroup, nodegroups, skip=None, first_call=True): return joined log.debug( - 'No nested nodegroups detected. Using original nodegroup ' - 'definition: %s', nodegroups[nodegroup] + "No nested nodegroups detected. Using original nodegroup " "definition: %s", + nodegroups[nodegroup], ) return ret class CkMinions(object): - ''' + """ Used to check what minions should respond from a target Note: This is a best-effort set of the minions that would match a target. Depending on master configuration (grains caching, etc.) and topology (syndics) the list may be a subset-- but we err on the side of too-many minions in this class. - ''' + """ + def __init__(self, opts): self.opts = opts self.serial = salt.payload.Serial(opts) self.cache = salt.cache.factory(opts) # TODO: this is actually an *auth* check - if self.opts.get('transport', 'zeromq') in ('zeromq', 'tcp'): - self.acc = 'minions' + if self.opts.get("transport", "zeromq") in ("zeromq", "tcp"): + self.acc = "minions" else: - self.acc = 'accepted' + self.acc = "accepted" def _check_nodegroup_minions(self, expr, greedy): # pylint: disable=unused-argument - ''' + """ Return minions found by looking at nodegroups - ''' - return self._check_compound_minions(nodegroup_comp(expr, self.opts['nodegroups']), - DEFAULT_TARGET_DELIM, - greedy) + """ + return self._check_compound_minions( + nodegroup_comp(expr, self.opts["nodegroups"]), DEFAULT_TARGET_DELIM, greedy + ) def _check_glob_minions(self, expr, greedy): # pylint: disable=unused-argument - ''' + """ Return the minions found by looking via globs - ''' - return {'minions': fnmatch.filter(self._pki_minions(), expr), - 'missing': []} + """ + return {"minions": fnmatch.filter(self._pki_minions(), expr), "missing": []} - def _check_list_minions(self, expr, greedy, ignore_missing=False): # pylint: disable=unused-argument - ''' + def _check_list_minions( + self, expr, greedy, ignore_missing=False + ): # pylint: disable=unused-argument + """ Return the minions found by looking via a list - ''' + """ if isinstance(expr, six.string_types): - expr = [m for m in expr.split(',') if m] + expr = [m for m in expr.split(",") if m] minions = self._pki_minions() - return {'minions': [x for x in expr if x in minions], - 'missing': [] if ignore_missing else [x for x in expr if x not in minions]} + return { + "minions": [x for x in expr if x in minions], + "missing": [] if ignore_missing else [x for x in expr if x not in minions], + } def _check_pcre_minions(self, expr, greedy): # pylint: disable=unused-argument - ''' + """ Return the minions found by looking via regular expressions - ''' + """ reg = re.compile(expr) - return {'minions': [m for m in self._pki_minions() if reg.match(m)], - 'missing': []} + return { + "minions": [m for m in self._pki_minions() if reg.match(m)], + "missing": [], + } def _pki_minions(self): - ''' + """ Retreive complete minion list from PKI dir. Respects cache if configured - ''' + """ minions = [] - pki_cache_fn = os.path.join(self.opts['pki_dir'], self.acc, '.key_cache') + pki_cache_fn = os.path.join(self.opts["pki_dir"], self.acc, ".key_cache") try: os.makedirs(os.path.dirname(pki_cache_fn)) except OSError: pass try: - if self.opts['key_cache'] and os.path.exists(pki_cache_fn): - log.debug('Returning cached minion list') + if self.opts["key_cache"] and os.path.exists(pki_cache_fn): + log.debug("Returning cached minion list") if six.PY2: with salt.utils.files.fopen(pki_cache_fn) as fn_: return self.serial.load(fn_) else: - with salt.utils.files.fopen(pki_cache_fn, mode='rb') as fn_: + with salt.utils.files.fopen(pki_cache_fn, mode="rb") as fn_: return self.serial.load(fn_) else: - for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(os.path.join(self.opts['pki_dir'], self.acc))): - if not fn_.startswith('.') and os.path.isfile(os.path.join(self.opts['pki_dir'], self.acc, fn_)): + for fn_ in salt.utils.data.sorted_ignorecase( + os.listdir(os.path.join(self.opts["pki_dir"], self.acc)) + ): + if not fn_.startswith(".") and os.path.isfile( + os.path.join(self.opts["pki_dir"], self.acc, fn_) + ): minions.append(fn_) return minions except OSError as exc: log.error( - 'Encountered OSError while evaluating minions in PKI dir: %s', - exc + "Encountered OSError while evaluating minions in PKI dir: %s", exc ) return minions - def _check_cache_minions(self, - expr, - delimiter, - greedy, - search_type, - regex_match=False, - exact_match=False): - ''' + def _check_cache_minions( + self, expr, delimiter, greedy, search_type, regex_match=False, exact_match=False + ): + """ Helper function to search for minions in master caches If 'greedy', then return accepted minions matched by the condition or those absent from the cache. If not 'greedy' return the only minions have cache data and matched by the condition. - ''' - cache_enabled = self.opts.get('minion_data_cache', False) + """ + cache_enabled = self.opts.get("minion_data_cache", False) def list_cached_minions(): - return self.cache.list('minions') + return self.cache.list("minions") if greedy: minions = [] - for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(os.path.join(self.opts['pki_dir'], self.acc))): - if not fn_.startswith('.') and os.path.isfile(os.path.join(self.opts['pki_dir'], self.acc, fn_)): + for fn_ in salt.utils.data.sorted_ignorecase( + os.listdir(os.path.join(self.opts["pki_dir"], self.acc)) + ): + if not fn_.startswith(".") and os.path.isfile( + os.path.join(self.opts["pki_dir"], self.acc, fn_) + ): minions.append(fn_) elif cache_enabled: minions = list_cached_minions() else: - return {'minions': [], - 'missing': []} + return {"minions": [], "missing": []} if cache_enabled: if greedy: @@ -309,92 +332,84 @@ class CkMinions(object): else: cminions = minions if not cminions: - return {'minions': minions, - 'missing': []} + return {"minions": minions, "missing": []} minions = set(minions) for id_ in cminions: if greedy and id_ not in minions: continue - mdata = self.cache.fetch('minions/{0}'.format(id_), 'data') + mdata = self.cache.fetch("minions/{0}".format(id_), "data") if mdata is None: if not greedy: minions.remove(id_) continue search_results = mdata.get(search_type) - if not salt.utils.data.subdict_match(search_results, - expr, - delimiter=delimiter, - regex_match=regex_match, - exact_match=exact_match): + if not salt.utils.data.subdict_match( + search_results, + expr, + delimiter=delimiter, + regex_match=regex_match, + exact_match=exact_match, + ): minions.remove(id_) minions = list(minions) - return {'minions': minions, - 'missing': []} + return {"minions": minions, "missing": []} def _check_grain_minions(self, expr, delimiter, greedy): - ''' + """ Return the minions found by looking via grains - ''' - return self._check_cache_minions(expr, delimiter, greedy, 'grains') + """ + return self._check_cache_minions(expr, delimiter, greedy, "grains") def _check_grain_pcre_minions(self, expr, delimiter, greedy): - ''' + """ Return the minions found by looking via grains with PCRE - ''' - return self._check_cache_minions(expr, - delimiter, - greedy, - 'grains', - regex_match=True) + """ + return self._check_cache_minions( + expr, delimiter, greedy, "grains", regex_match=True + ) def _check_pillar_minions(self, expr, delimiter, greedy): - ''' + """ Return the minions found by looking via pillar - ''' - return self._check_cache_minions(expr, delimiter, greedy, 'pillar') + """ + return self._check_cache_minions(expr, delimiter, greedy, "pillar") def _check_pillar_pcre_minions(self, expr, delimiter, greedy): - ''' + """ Return the minions found by looking via pillar with PCRE - ''' - return self._check_cache_minions(expr, - delimiter, - greedy, - 'pillar', - regex_match=True) + """ + return self._check_cache_minions( + expr, delimiter, greedy, "pillar", regex_match=True + ) def _check_pillar_exact_minions(self, expr, delimiter, greedy): - ''' + """ Return the minions found by looking via pillar - ''' - return self._check_cache_minions(expr, - delimiter, - greedy, - 'pillar', - exact_match=True) + """ + return self._check_cache_minions( + expr, delimiter, greedy, "pillar", exact_match=True + ) def _check_ipcidr_minions(self, expr, greedy): - ''' + """ Return the minions found by looking via ipcidr - ''' - cache_enabled = self.opts.get('minion_data_cache', False) + """ + cache_enabled = self.opts.get("minion_data_cache", False) if greedy: minions = self._pki_minions() elif cache_enabled: - minions = self.cache.list('minions') + minions = self.cache.list("minions") else: - return {'minions': [], - 'missing': []} + return {"minions": [], "missing": []} if cache_enabled: if greedy: - cminions = self.cache.list('minions') + cminions = self.cache.list("minions") else: cminions = minions if cminions is None: - return {'minions': minions, - 'missing': []} + return {"minions": minions, "missing": []} tgt = expr try: @@ -405,19 +420,18 @@ class CkMinions(object): # Target is a network? tgt = ipaddress.ip_network(tgt) except Exception: # pylint: disable=broad-except - log.error('Invalid IP/CIDR target: %s', tgt) - return {'minions': [], - 'missing': []} - proto = 'ipv{0}'.format(tgt.version) + log.error("Invalid IP/CIDR target: %s", tgt) + return {"minions": [], "missing": []} + proto = "ipv{0}".format(tgt.version) minions = set(minions) for id_ in cminions: - mdata = self.cache.fetch('minions/{0}'.format(id_), 'data') + mdata = self.cache.fetch("minions/{0}".format(id_), "data") if mdata is None: if not greedy: minions.remove(id_) continue - grains = mdata.get('grains') + grains = mdata.get("grains") if grains is None or proto not in grains: match = False elif isinstance(tgt, (ipaddress.IPv4Address, ipaddress.IPv6Address)): @@ -428,85 +442,82 @@ class CkMinions(object): if not match and id_ in minions: minions.remove(id_) - return {'minions': list(minions), - 'missing': []} + return {"minions": list(minions), "missing": []} def _check_range_minions(self, expr, greedy): - ''' + """ Return the minions found by looking via range expression - ''' + """ if not HAS_RANGE: raise CommandExecutionError( - 'Range matcher unavailable (unable to import seco.range, ' - 'module most likely not installed)' + "Range matcher unavailable (unable to import seco.range, " + "module most likely not installed)" ) - if not hasattr(self, '_range'): - self._range = seco.range.Range(self.opts['range_server']) + if not hasattr(self, "_range"): + self._range = seco.range.Range(self.opts["range_server"]) try: return self._range.expand(expr) except seco.range.RangeException as exc: - log.error( - 'Range exception in compound match: %s', exc - ) - cache_enabled = self.opts.get('minion_data_cache', False) + log.error("Range exception in compound match: %s", exc) + cache_enabled = self.opts.get("minion_data_cache", False) if greedy: mlist = [] - for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(os.path.join(self.opts['pki_dir'], self.acc))): - if not fn_.startswith('.') and os.path.isfile(os.path.join(self.opts['pki_dir'], self.acc, fn_)): + for fn_ in salt.utils.data.sorted_ignorecase( + os.listdir(os.path.join(self.opts["pki_dir"], self.acc)) + ): + if not fn_.startswith(".") and os.path.isfile( + os.path.join(self.opts["pki_dir"], self.acc, fn_) + ): mlist.append(fn_) - return {'minions': mlist, - 'missing': []} + return {"minions": mlist, "missing": []} elif cache_enabled: - return {'minions': self.cache.list('minions'), - 'missing': []} + return {"minions": self.cache.list("minions"), "missing": []} else: - return {'minions': [], - 'missing': []} + return {"minions": [], "missing": []} def _check_compound_pillar_exact_minions(self, expr, delimiter, greedy): - ''' + """ Return the minions found by looking via compound matcher Disable pillar glob matching - ''' - return self._check_compound_minions(expr, - delimiter, - greedy, - pillar_exact=True) + """ + return self._check_compound_minions(expr, delimiter, greedy, pillar_exact=True) - def _check_compound_minions(self, - expr, - delimiter, - greedy, - pillar_exact=False): # pylint: disable=unused-argument - ''' + def _check_compound_minions( + self, expr, delimiter, greedy, pillar_exact=False + ): # pylint: disable=unused-argument + """ Return the minions found by looking via compound matcher - ''' - if not isinstance(expr, six.string_types) and not isinstance(expr, (list, tuple)): - log.error('Compound target that is neither string, list nor tuple') - return {'minions': [], 'missing': []} + """ + if not isinstance(expr, six.string_types) and not isinstance( + expr, (list, tuple) + ): + log.error("Compound target that is neither string, list nor tuple") + return {"minions": [], "missing": []} minions = set(self._pki_minions()) - log.debug('minions: %s', minions) + log.debug("minions: %s", minions) - nodegroups = self.opts.get('nodegroups', {}) + nodegroups = self.opts.get("nodegroups", {}) - if self.opts.get('minion_data_cache', False): - ref = {'G': self._check_grain_minions, - 'P': self._check_grain_pcre_minions, - 'I': self._check_pillar_minions, - 'J': self._check_pillar_pcre_minions, - 'L': self._check_list_minions, - 'N': None, # nodegroups should already be expanded - 'S': self._check_ipcidr_minions, - 'E': self._check_pcre_minions, - 'R': self._all_minions} + if self.opts.get("minion_data_cache", False): + ref = { + "G": self._check_grain_minions, + "P": self._check_grain_pcre_minions, + "I": self._check_pillar_minions, + "J": self._check_pillar_pcre_minions, + "L": self._check_list_minions, + "N": None, # nodegroups should already be expanded + "S": self._check_ipcidr_minions, + "E": self._check_pcre_minions, + "R": self._all_minions, + } if pillar_exact: - ref['I'] = self._check_pillar_exact_minions - ref['J'] = self._check_pillar_exact_minions + ref["I"] = self._check_pillar_exact_minions + ref["J"] = self._check_pillar_exact_minions results = [] unmatched = [] - opers = ['and', 'or', 'not', '(', ')'] + opers = ["and", "or", "not", "(", ")"] missing = [] if isinstance(expr, six.string_types): @@ -522,150 +533,150 @@ class CkMinions(object): # Easy check first if word in opers: if results: - if results[-1] == '(' and word in ('and', 'or'): + if results[-1] == "(" and word in ("and", "or"): log.error('Invalid beginning operator after "(": %s', word) - return {'minions': [], 'missing': []} - if word == 'not': - if not results[-1] in ('&', '|', '('): - results.append('&') - results.append('(') + return {"minions": [], "missing": []} + if word == "not": + if not results[-1] in ("&", "|", "("): + results.append("&") + results.append("(") results.append(six.text_type(set(minions))) - results.append('-') - unmatched.append('-') - elif word == 'and': - results.append('&') - elif word == 'or': - results.append('|') - elif word == '(': + results.append("-") + unmatched.append("-") + elif word == "and": + results.append("&") + elif word == "or": + results.append("|") + elif word == "(": results.append(word) unmatched.append(word) - elif word == ')': - if not unmatched or unmatched[-1] != '(': - log.error('Invalid compound expr (unexpected ' - 'right parenthesis): %s', - expr) - return {'minions': [], 'missing': []} + elif word == ")": + if not unmatched or unmatched[-1] != "(": + log.error( + "Invalid compound expr (unexpected " + "right parenthesis): %s", + expr, + ) + return {"minions": [], "missing": []} results.append(word) unmatched.pop() - if unmatched and unmatched[-1] == '-': - results.append(')') + if unmatched and unmatched[-1] == "-": + results.append(")") unmatched.pop() else: # Won't get here, unless oper is added - log.error('Unhandled oper in compound expr: %s', - expr) - return {'minions': [], 'missing': []} + log.error("Unhandled oper in compound expr: %s", expr) + return {"minions": [], "missing": []} else: # seq start with oper, fail - if word == 'not': - results.append('(') + if word == "not": + results.append("(") results.append(six.text_type(set(minions))) - results.append('-') - unmatched.append('-') - elif word == '(': + results.append("-") + unmatched.append("-") + elif word == "(": results.append(word) unmatched.append(word) else: log.error( - 'Expression may begin with' - ' binary operator: %s', word + "Expression may begin with" " binary operator: %s", word ) - return {'minions': [], 'missing': []} + return {"minions": [], "missing": []} - elif target_info and target_info['engine']: - if 'N' == target_info['engine']: + elif target_info and target_info["engine"]: + if "N" == target_info["engine"]: # if we encounter a node group, just evaluate it in-place - decomposed = nodegroup_comp(target_info['pattern'], nodegroups) + decomposed = nodegroup_comp(target_info["pattern"], nodegroups) if decomposed: words = decomposed + words continue - engine = ref.get(target_info['engine']) + engine = ref.get(target_info["engine"]) if not engine: # If an unknown engine is called at any time, fail out log.error( 'Unrecognized target engine "%s" for' ' target expression "%s"', - target_info['engine'], + target_info["engine"], word, ) - return {'minions': [], 'missing': []} + return {"minions": [], "missing": []} - engine_args = [target_info['pattern']] - if target_info['engine'] in ('G', 'P', 'I', 'J'): - engine_args.append(target_info['delimiter'] or ':') + engine_args = [target_info["pattern"]] + if target_info["engine"] in ("G", "P", "I", "J"): + engine_args.append(target_info["delimiter"] or ":") engine_args.append(greedy) # ignore missing minions for lists if we exclude them with # a 'not' - if 'L' == target_info['engine']: - engine_args.append(results and results[-1] == '-') + if "L" == target_info["engine"]: + engine_args.append(results and results[-1] == "-") _results = engine(*engine_args) - results.append(six.text_type(set(_results['minions']))) - missing.extend(_results['missing']) - if unmatched and unmatched[-1] == '-': - results.append(')') + results.append(six.text_type(set(_results["minions"]))) + missing.extend(_results["missing"]) + if unmatched and unmatched[-1] == "-": + results.append(")") unmatched.pop() else: # The match is not explicitly defined, evaluate as a glob _results = self._check_glob_minions(word, True) - results.append(six.text_type(set(_results['minions']))) - if unmatched and unmatched[-1] == '-': - results.append(')') + results.append(six.text_type(set(_results["minions"]))) + if unmatched and unmatched[-1] == "-": + results.append(")") unmatched.pop() # Add a closing ')' for each item left in unmatched - results.extend([')' for item in unmatched]) + results.extend([")" for item in unmatched]) - results = ' '.join(results) - log.debug('Evaluating final compound matching expr: %s', - results) + results = " ".join(results) + log.debug("Evaluating final compound matching expr: %s", results) try: minions = list(eval(results)) # pylint: disable=W0123 - return {'minions': minions, 'missing': missing} + return {"minions": minions, "missing": missing} except Exception: # pylint: disable=broad-except - log.error('Invalid compound target: %s', expr) - return {'minions': [], 'missing': []} + log.error("Invalid compound target: %s", expr) + return {"minions": [], "missing": []} - return {'minions': list(minions), - 'missing': []} + return {"minions": list(minions), "missing": []} - def connected_ids(self, subset=None, show_ip=False, show_ipv4=None, include_localhost=None): - ''' + def connected_ids( + self, subset=None, show_ip=False, show_ipv4=None, include_localhost=None + ): + """ Return a set of all connected minion ids, optionally within a subset - ''' + """ if include_localhost is not None: salt.utils.versions.warn_until( - 'Sodium', - 'The \'include_localhost\' argument is no longer required; any' - 'connected localhost minion will always be included.' + "Sodium", + "The 'include_localhost' argument is no longer required; any" + "connected localhost minion will always be included.", ) if show_ipv4 is not None: salt.utils.versions.warn_until( - 'Sodium', - 'The \'show_ipv4\' argument has been renamed to \'show_ip\' as' - 'it now also includes IPv6 addresses for IPv6-connected' - 'minions.' + "Sodium", + "The 'show_ipv4' argument has been renamed to 'show_ip' as" + "it now also includes IPv6 addresses for IPv6-connected" + "minions.", ) minions = set() - if self.opts.get('minion_data_cache', False): - search = self.cache.list('minions') + if self.opts.get("minion_data_cache", False): + search = self.cache.list("minions") if search is None: return minions - addrs = salt.utils.network.local_port_tcp(int(self.opts['publish_port'])) - if '127.0.0.1' in addrs: + addrs = salt.utils.network.local_port_tcp(int(self.opts["publish_port"])) + if "127.0.0.1" in addrs: # Add in the address of a possible locally-connected minion. - addrs.discard('127.0.0.1') + addrs.discard("127.0.0.1") addrs.update(set(salt.utils.network.ip_addrs(include_loopback=False))) - if '::1' in addrs: + if "::1" in addrs: # Add in the address of a possible locally-connected minion. - addrs.discard('::1') + addrs.discard("::1") addrs.update(set(salt.utils.network.ip_addrs6(include_loopback=False))) if subset: search = subset for id_ in search: try: - mdata = self.cache.fetch('minions/{0}'.format(id_), 'data') + mdata = self.cache.fetch("minions/{0}".format(id_), "data") except SaltCacheError: # If a SaltCacheError is explicitly raised during the fetch operation, # permission was denied to open the cached data.p file. Continue on as @@ -674,15 +685,15 @@ class CkMinions(object): continue if mdata is None: continue - grains = mdata.get('grains', {}) - for ipv4 in grains.get('ipv4', []): + grains = mdata.get("grains", {}) + for ipv4 in grains.get("ipv4", []): if ipv4 in addrs: if show_ip: minions.add((id_, ipv4)) else: minions.add(id_) break - for ipv6 in grains.get('ipv6', []): + for ipv6 in grains.get("ipv6", []): if ipv6 in addrs: if show_ip: minions.add((id_, ipv6)) @@ -692,65 +703,73 @@ class CkMinions(object): return minions def _all_minions(self, expr=None): - ''' + """ Return a list of all minions that have auth'd - ''' + """ mlist = [] - for fn_ in salt.utils.data.sorted_ignorecase(os.listdir(os.path.join(self.opts['pki_dir'], self.acc))): - if not fn_.startswith('.') and os.path.isfile(os.path.join(self.opts['pki_dir'], self.acc, fn_)): + for fn_ in salt.utils.data.sorted_ignorecase( + os.listdir(os.path.join(self.opts["pki_dir"], self.acc)) + ): + if not fn_.startswith(".") and os.path.isfile( + os.path.join(self.opts["pki_dir"], self.acc, fn_) + ): mlist.append(fn_) - return {'minions': mlist, 'missing': []} + return {"minions": mlist, "missing": []} - def check_minions(self, - expr, - tgt_type='glob', - delimiter=DEFAULT_TARGET_DELIM, - greedy=True): - ''' + def check_minions( + self, expr, tgt_type="glob", delimiter=DEFAULT_TARGET_DELIM, greedy=True + ): + """ Check the passed regex against the available minions' public keys stored for authentication. This should return a set of ids which match the regex, this will then be used to parse the returns to make sure everyone has checked back in. - ''' + """ try: if expr is None: - expr = '' - check_func = getattr(self, '_check_{0}_minions'.format(tgt_type), None) - if tgt_type in ('grain', - 'grain_pcre', - 'pillar', - 'pillar_pcre', - 'pillar_exact', - 'compound', - 'compound_pillar_exact'): - _res = check_func(expr, delimiter, greedy) # pylint: disable=not-callable + expr = "" + check_func = getattr(self, "_check_{0}_minions".format(tgt_type), None) + if tgt_type in ( + "grain", + "grain_pcre", + "pillar", + "pillar_pcre", + "pillar_exact", + "compound", + "compound_pillar_exact", + ): + # pylint: disable=not-callable + _res = check_func(expr, delimiter, greedy) + # pylint: enable=not-callable else: _res = check_func(expr, greedy) # pylint: disable=not-callable - _res['ssh_minions'] = False - if self.opts.get('enable_ssh_minions', False) is True and isinstance('tgt', six.string_types): - roster = salt.roster.Roster(self.opts, self.opts.get('roster', 'flat')) + _res["ssh_minions"] = False + if self.opts.get("enable_ssh_minions", False) is True and isinstance( + "tgt", six.string_types + ): + roster = salt.roster.Roster(self.opts, self.opts.get("roster", "flat")) ssh_minions = roster.targets(expr, tgt_type) if ssh_minions: - _res['minions'].extend(ssh_minions) - _res['ssh_minions'] = True + _res["minions"].extend(ssh_minions) + _res["ssh_minions"] = True except Exception: # pylint: disable=broad-except log.exception( - 'Failed matching available minions with %s pattern: %s', - tgt_type, expr) - _res = {'minions': [], 'missing': []} + "Failed matching available minions with %s pattern: %s", tgt_type, expr + ) + _res = {"minions": [], "missing": []} return _res def validate_tgt(self, valid, expr, tgt_type, minions=None, expr_form=None): - ''' + """ Return a Bool. This function returns if the expression sent in is within the scope of the valid expression - ''' + """ - v_minions = set(self.check_minions(valid, 'compound').get('minions', [])) + v_minions = set(self.check_minions(valid, "compound").get("minions", [])) if minions is None: _res = self.check_minions(expr, tgt_type) - minions = set(_res['minions']) + minions = set(_res["minions"]) else: minions = set(minions) d_bool = not bool(minions.difference(v_minions)) @@ -759,11 +778,11 @@ class CkMinions(object): return d_bool def match_check(self, regex, fun): - ''' + """ Validate a single regex to function comparison, the function argument can be a list of functions. It is all or nothing for a list of functions - ''' + """ vals = [] if isinstance(fun, six.string_types): fun = [fun] @@ -774,17 +793,19 @@ class CkMinions(object): else: vals.append(False) except Exception: # pylint: disable=broad-except - log.error('Invalid regular expression: %s', regex) + log.error("Invalid regular expression: %s", regex) return vals and all(vals) - def auth_check_expanded(self, - auth_list, - funs, - args, - tgt, - tgt_type='glob', - groups=None, - publish_validate=False): + def auth_check_expanded( + self, + auth_list, + funs, + args, + tgt, + tgt_type="glob", + groups=None, + publish_validate=False, + ): # Here's my thinking # 1. Retrieve anticipated targeted minions @@ -819,15 +840,15 @@ class CkMinions(object): # auth.enable_expanded_auth_matching # and default to False v_tgt_type = tgt_type - if tgt_type.lower() in ('pillar', 'pillar_pcre'): - v_tgt_type = 'pillar_exact' - elif tgt_type.lower() == 'compound': - v_tgt_type = 'compound_pillar_exact' + if tgt_type.lower() in ("pillar", "pillar_pcre"): + v_tgt_type = "pillar_exact" + elif tgt_type.lower() == "compound": + v_tgt_type = "compound_pillar_exact" _res = self.check_minions(tgt, v_tgt_type) - v_minions = set(_res['minions']) + v_minions = set(_res["minions"]) _res = self.check_minions(tgt, tgt_type) - minions = set(_res['minions']) + minions = set(_res["minions"]) mismatch = bool(minions.difference(v_minions)) # If the non-exact match gets more minions than the exact match @@ -859,11 +880,11 @@ class CkMinions(object): continue if isinstance(auth_list_entry, dict): if len(auth_list_entry) != 1: - log.info('Malformed ACL: %s', auth_list_entry) + log.info("Malformed ACL: %s", auth_list_entry) continue allowed_minions.update(set(auth_list_entry.keys())) for key in auth_list_entry: - for match in set(self.check_minions(key, 'compound')): + for match in set(self.check_minions(key, "compound")): if match in auth_dictionary: auth_dictionary[match].extend(auth_list_entry[key]) else: @@ -871,7 +892,9 @@ class CkMinions(object): allowed_minions_from_auth_list = set() for next_entry in allowed_minions: - allowed_minions_from_auth_list.update(set(self.check_minions(next_entry, 'compound'))) + allowed_minions_from_auth_list.update( + set(self.check_minions(next_entry, "compound")) + ) # 'minions' here are all the names of minions matched by the target # if we take out all the allowed minions, and there are any left, then # the target includes minions that are not allowed by eauth @@ -892,34 +915,38 @@ class CkMinions(object): return False return False - def auth_check(self, - auth_list, - funs, - args, - tgt, - tgt_type='glob', - groups=None, - publish_validate=False, - minions=None, - whitelist=None): - ''' + def auth_check( + self, + auth_list, + funs, + args, + tgt, + tgt_type="glob", + groups=None, + publish_validate=False, + minions=None, + whitelist=None, + ): + """ Returns a bool which defines if the requested function is authorized. Used to evaluate the standard structure under external master authentication interfaces, like eauth, peer, peer_run, etc. - ''' - if self.opts.get('auth.enable_expanded_auth_matching', False): - return self.auth_check_expanded(auth_list, funs, args, tgt, tgt_type, groups, publish_validate) + """ + if self.opts.get("auth.enable_expanded_auth_matching", False): + return self.auth_check_expanded( + auth_list, funs, args, tgt, tgt_type, groups, publish_validate + ) if publish_validate: v_tgt_type = tgt_type - if tgt_type.lower() in ('pillar', 'pillar_pcre'): - v_tgt_type = 'pillar_exact' - elif tgt_type.lower() == 'compound': - v_tgt_type = 'compound_pillar_exact' + if tgt_type.lower() in ("pillar", "pillar_pcre"): + v_tgt_type = "pillar_exact" + elif tgt_type.lower() == "compound": + v_tgt_type = "compound_pillar_exact" _res = self.check_minions(tgt, v_tgt_type) - v_minions = set(_res['minions']) + v_minions = set(_res["minions"]) _res = self.check_minions(tgt, tgt_type) - minions = set(_res['minions']) + minions = set(_res["minions"]) mismatch = bool(minions.difference(v_minions)) # If the non-exact match gets more minions than the exact match @@ -946,15 +973,14 @@ class CkMinions(object): continue valid = next(six.iterkeys(ind)) # Check if minions are allowed - if self.validate_tgt( - valid, - tgt, - tgt_type, - minions=minions): + if self.validate_tgt(valid, tgt, tgt_type, minions=minions): # Minions are allowed, verify function in allowed list fun_args = args[num] fun_kwargs = fun_args[-1] if fun_args else None - if isinstance(fun_kwargs, dict) and '__kwarg__' in fun_kwargs: + if ( + isinstance(fun_kwargs, dict) + and "__kwarg__" in fun_kwargs + ): fun_args = list(fun_args) # copy on modify del fun_args[-1] else: @@ -966,12 +992,12 @@ class CkMinions(object): return False def fill_auth_list_from_groups(self, auth_provider, user_groups, auth_list): - ''' + """ Returns a list of authorisation matchers that a user is eligible for. This list is a combination of the provided personal matchers plus the matchers of any group the user is in. - ''' - group_names = [item for item in auth_provider if item.endswith('%')] + """ + group_names = [item for item in auth_provider if item.endswith("%")] if group_names: for group_name in group_names: if group_name.rstrip("%") in user_groups: @@ -979,83 +1005,97 @@ class CkMinions(object): auth_list.append(matcher) return auth_list - def fill_auth_list(self, auth_provider, name, groups, auth_list=None, permissive=None): - ''' + def fill_auth_list( + self, auth_provider, name, groups, auth_list=None, permissive=None + ): + """ Returns a list of authorisation matchers that a user is eligible for. This list is a combination of the provided personal matchers plus the matchers of any group the user is in. - ''' + """ if auth_list is None: auth_list = [] if permissive is None: - permissive = self.opts.get('permissive_acl') + permissive = self.opts.get("permissive_acl") name_matched = False for match in auth_provider: - if match == '*' and not permissive: + if match == "*" and not permissive: continue - if match.endswith('%'): - if match.rstrip('%') in groups: + if match.endswith("%"): + if match.rstrip("%") in groups: auth_list.extend(auth_provider[match]) else: if salt.utils.stringutils.expr_match(match, name): name_matched = True auth_list.extend(auth_provider[match]) - if not permissive and not name_matched and '*' in auth_provider: - auth_list.extend(auth_provider['*']) + if not permissive and not name_matched and "*" in auth_provider: + auth_list.extend(auth_provider["*"]) return auth_list def wheel_check(self, auth_list, fun, args): - ''' + """ Check special API permissions - ''' - return self.spec_check(auth_list, fun, args, 'wheel') + """ + return self.spec_check(auth_list, fun, args, "wheel") def runner_check(self, auth_list, fun, args): - ''' + """ Check special API permissions - ''' - return self.spec_check(auth_list, fun, args, 'runner') + """ + return self.spec_check(auth_list, fun, args, "runner") def spec_check(self, auth_list, fun, args, form): - ''' + """ Check special API permissions - ''' + """ if not auth_list: return False - if form != 'cloud': - comps = fun.split('.') + if form != "cloud": + comps = fun.split(".") if len(comps) != 2: # Hint at a syntax error when command is passed improperly, # rather than returning an authentication error of some kind. # See Issue #21969 for more information. - return {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} + return { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } mod_name = comps[0] fun_name = comps[1] else: fun_name = mod_name = fun for ind in auth_list: if isinstance(ind, six.string_types): - if ind[0] == '@': - if ind[1:] == mod_name or ind[1:] == form or ind == '@{0}s'.format(form): + if ind[0] == "@": + if ( + ind[1:] == mod_name + or ind[1:] == form + or ind == "@{0}s".format(form) + ): return True elif isinstance(ind, dict): if len(ind) != 1: continue valid = next(six.iterkeys(ind)) - if valid[0] == '@': + if valid[0] == "@": if valid[1:] == mod_name: - if self.__fun_check(ind[valid], fun_name, args.get('arg'), args.get('kwarg')): + if self.__fun_check( + ind[valid], fun_name, args.get("arg"), args.get("kwarg") + ): return True - if valid[1:] == form or valid == '@{0}s'.format(form): - if self.__fun_check(ind[valid], fun, args.get('arg'), args.get('kwarg')): + if valid[1:] == form or valid == "@{0}s".format(form): + if self.__fun_check( + ind[valid], fun, args.get("arg"), args.get("kwarg") + ): return True return False def __fun_check(self, valid, fun, args=None, kwargs=None): - ''' + """ Check the given function name (fun) and its arguments (args) against the list of conditions. - ''' + """ if not isinstance(valid, list): valid = [valid] for cond in valid: @@ -1069,15 +1109,17 @@ class CkMinions(object): # Invalid argument continue fname_cond = next(six.iterkeys(cond)) - if self.match_check(fname_cond, fun): # check key that is function name match + if self.match_check( + fname_cond, fun + ): # check key that is function name match if self.__args_check(cond[fname_cond], args, kwargs): return True return False def __args_check(self, valid, args=None, kwargs=None): - ''' + """ valid is a dicts: {'args': [...], 'kwargs': {...}} or a list of such dicts. - ''' + """ if not isinstance(valid, list): valid = [valid] for cond in valid: @@ -1085,7 +1127,7 @@ class CkMinions(object): # Invalid argument continue # whitelist args, kwargs - cond_args = cond.get('args', []) + cond_args = cond.get("args", []) good = True for i, cond_arg in enumerate(cond_args): if args is None or len(args) <= i: @@ -1099,7 +1141,7 @@ class CkMinions(object): if not good: continue # Check kwargs - cond_kwargs = cond.get('kwargs', {}) + cond_kwargs = cond.get("kwargs", {}) for k, v in six.iteritems(cond_kwargs): if kwargs is None or k not in kwargs: good = False diff --git a/salt/utils/mount.py b/salt/utils/mount.py index 4efe6a51c3b..e62aa4e4ea9 100644 --- a/salt/utils/mount.py +++ b/salt/utils/mount.py @@ -1,62 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" Common functions for managing mounts -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import logging import os # Import Salt libs import salt.utils.files import salt.utils.stringutils -import salt.utils.yaml import salt.utils.versions +import salt.utils.yaml log = logging.getLogger(__name__) def _read_file(path): - ''' + """ Reads and returns the contents of a text file - ''' + """ try: - with salt.utils.files.fopen(path, 'rb') as contents: + with salt.utils.files.fopen(path, "rb") as contents: return salt.utils.yaml.safe_load(contents) except (OSError, IOError): return {} def get_cache(opts): - ''' + """ Return the mount cache file location. - ''' - return os.path.join(opts['cachedir'], 'mounts') + """ + return os.path.join(opts["cachedir"], "mounts") def read_cache(opts): - ''' + """ Write the mount cache file. - ''' + """ cache_file = get_cache(opts) return _read_file(cache_file) def write_cache(cache, opts): - ''' + """ Write the mount cache file. - ''' + """ cache_file = get_cache(opts) try: - _cache = salt.utils.stringutils.to_bytes( - salt.utils.yaml.safe_dump(cache) - ) - with salt.utils.files.fopen(cache_file, 'wb+') as fp_: + _cache = salt.utils.stringutils.to_bytes(salt.utils.yaml.safe_dump(cache)) + with salt.utils.files.fopen(cache_file, "wb+") as fp_: fp_.write(_cache) return True except (IOError, OSError): - log.error('Failed to cache mounts', - exc_info_on_loglevel=logging.DEBUG) + log.error("Failed to cache mounts", exc_info_on_loglevel=logging.DEBUG) return False diff --git a/salt/utils/msazure.py b/salt/utils/msazure.py index fc0e0b6fa45..e98895cce68 100644 --- a/salt/utils/msazure.py +++ b/salt/utils/msazure.py @@ -1,67 +1,69 @@ # -*- coding: utf-8 -*- -''' +""" .. versionadded:: 2015.8.0 Utilities for accessing storage container blobs on Azure -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + import logging +# Import salt libs +from salt.exceptions import SaltSystemExit + # Import azure libs HAS_LIBS = False try: import azure + HAS_LIBS = True except ImportError: pass -# Import salt libs -from salt.exceptions import SaltSystemExit log = logging.getLogger(__name__) def get_storage_conn(storage_account=None, storage_key=None, opts=None): - ''' + """ .. versionadded:: 2015.8.0 Return a storage_conn object for the storage account - ''' + """ if opts is None: opts = {} if not storage_account: - storage_account = opts.get('storage_account', None) + storage_account = opts.get("storage_account", None) if not storage_key: - storage_key = opts.get('storage_key', None) + storage_key = opts.get("storage_key", None) return azure.storage.BlobService(storage_account, storage_key) def list_blobs(storage_conn=None, **kwargs): - ''' + """ .. versionadded:: 2015.8.0 List blobs associated with the container - ''' + """ if not storage_conn: storage_conn = get_storage_conn(opts=kwargs) - if 'container' not in kwargs: + if "container" not in kwargs: raise SaltSystemExit( - code=42, - msg='An storage container name must be specified as "container"' + code=42, msg='An storage container name must be specified as "container"' ) data = storage_conn.list_blobs( - container_name=kwargs['container'], - prefix=kwargs.get('prefix', None), - marker=kwargs.get('marker', None), - maxresults=kwargs.get('maxresults', None), - include=kwargs.get('include', None), - delimiter=kwargs.get('delimiter', None), + container_name=kwargs["container"], + prefix=kwargs.get("prefix", None), + marker=kwargs.get("marker", None), + maxresults=kwargs.get("maxresults", None), + include=kwargs.get("include", None), + delimiter=kwargs.get("delimiter", None), ) ret = {} @@ -71,116 +73,116 @@ def list_blobs(storage_conn=None, **kwargs): def put_blob(storage_conn=None, **kwargs): - ''' + """ .. versionadded:: 2015.8.0 Upload a blob - ''' + """ if not storage_conn: storage_conn = get_storage_conn(opts=kwargs) - if 'container' not in kwargs: - raise SaltSystemExit(code=42, msg='The blob container name must be specified as "container"') + if "container" not in kwargs: + raise SaltSystemExit( + code=42, msg='The blob container name must be specified as "container"' + ) - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltSystemExit(code=42, msg='The blob name must be specified as "name"') - if 'blob_path' not in kwargs and 'blob_content' not in kwargs: + if "blob_path" not in kwargs and "blob_content" not in kwargs: raise SaltSystemExit( code=42, msg='Either a path to a file needs to be passed in as "blob_path" ' - 'or the contents of a blob as "blob_content."' + 'or the contents of a blob as "blob_content."', ) blob_kwargs = { - 'container_name': kwargs['container'], - 'blob_name': kwargs['name'], - 'cache_control': kwargs.get('cache_control', None), - 'content_language': kwargs.get('content_language', None), - 'content_md5': kwargs.get('content_md5', None), - 'x_ms_blob_content_type': kwargs.get('blob_content_type', None), - 'x_ms_blob_content_encoding': kwargs.get('blob_content_encoding', None), - 'x_ms_blob_content_language': kwargs.get('blob_content_language', None), - 'x_ms_blob_content_md5': kwargs.get('blob_content_md5', None), - 'x_ms_blob_cache_control': kwargs.get('blob_cache_control', None), - 'x_ms_meta_name_values': kwargs.get('meta_name_values', None), - 'x_ms_lease_id': kwargs.get('lease_id', None), + "container_name": kwargs["container"], + "blob_name": kwargs["name"], + "cache_control": kwargs.get("cache_control", None), + "content_language": kwargs.get("content_language", None), + "content_md5": kwargs.get("content_md5", None), + "x_ms_blob_content_type": kwargs.get("blob_content_type", None), + "x_ms_blob_content_encoding": kwargs.get("blob_content_encoding", None), + "x_ms_blob_content_language": kwargs.get("blob_content_language", None), + "x_ms_blob_content_md5": kwargs.get("blob_content_md5", None), + "x_ms_blob_cache_control": kwargs.get("blob_cache_control", None), + "x_ms_meta_name_values": kwargs.get("meta_name_values", None), + "x_ms_lease_id": kwargs.get("lease_id", None), } - if 'blob_path' in kwargs: + if "blob_path" in kwargs: data = storage_conn.put_block_blob_from_path( - file_path=kwargs['blob_path'], - **blob_kwargs + file_path=kwargs["blob_path"], **blob_kwargs ) - elif 'blob_content' in kwargs: + elif "blob_content" in kwargs: data = storage_conn.put_block_blob_from_bytes( - blob=kwargs['blob_content'], - **blob_kwargs + blob=kwargs["blob_content"], **blob_kwargs ) return data def get_blob(storage_conn=None, **kwargs): - ''' + """ .. versionadded:: 2015.8.0 Download a blob - ''' + """ if not storage_conn: storage_conn = get_storage_conn(opts=kwargs) - if 'container' not in kwargs: - raise SaltSystemExit(code=42, msg='The blob container name must be specified as "container"') + if "container" not in kwargs: + raise SaltSystemExit( + code=42, msg='The blob container name must be specified as "container"' + ) - if 'name' not in kwargs: + if "name" not in kwargs: raise SaltSystemExit(code=42, msg='The blob name must be specified as "name"') - if 'local_path' not in kwargs and 'return_content' not in kwargs: + if "local_path" not in kwargs and "return_content" not in kwargs: raise SaltSystemExit( code=42, msg='Either a local path needs to be passed in as "local_path", ' - 'or "return_content" to return the blob contents directly' + 'or "return_content" to return the blob contents directly', ) blob_kwargs = { - 'container_name': kwargs['container'], - 'blob_name': kwargs['name'], - 'snapshot': kwargs.get('snapshot', None), - 'x_ms_lease_id': kwargs.get('lease_id', None), - 'progress_callback': kwargs.get('progress_callback', None), - 'max_connections': kwargs.get('max_connections', 1), - 'max_retries': kwargs.get('max_retries', 5), - 'retry_wait': kwargs.get('retry_wait', 1), + "container_name": kwargs["container"], + "blob_name": kwargs["name"], + "snapshot": kwargs.get("snapshot", None), + "x_ms_lease_id": kwargs.get("lease_id", None), + "progress_callback": kwargs.get("progress_callback", None), + "max_connections": kwargs.get("max_connections", 1), + "max_retries": kwargs.get("max_retries", 5), + "retry_wait": kwargs.get("retry_wait", 1), } - if 'local_path' in kwargs: + if "local_path" in kwargs: data = storage_conn.get_blob_to_path( - file_path=kwargs['local_path'], - open_mode=kwargs.get('open_mode', 'wb'), - **blob_kwargs - ) - elif 'return_content' in kwargs: - data = storage_conn.get_blob_to_bytes( + file_path=kwargs["local_path"], + open_mode=kwargs.get("open_mode", "wb"), **blob_kwargs ) + elif "return_content" in kwargs: + data = storage_conn.get_blob_to_bytes(**blob_kwargs) return data def object_to_dict(obj): - ''' + """ .. versionadded:: 2015.8.0 Convert an object to a dictionary - ''' + """ if isinstance(obj, list) or isinstance(obj, tuple): ret = [] for item in obj: ret.append(object_to_dict(item)) - elif hasattr(obj, '__dict__'): + elif hasattr(obj, "__dict__"): ret = {} for item in obj.__dict__: - if item.startswith('_'): + if item.startswith("_"): continue ret[item] = object_to_dict(obj.__dict__[item]) else: diff --git a/salt/utils/msgpack.py b/salt/utils/msgpack.py index 1d02aa96ba8..a93763ffb7f 100644 --- a/salt/utils/msgpack.py +++ b/salt/utils/msgpack.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Functions to work with MessagePack -''' +""" # Import Python libs from __future__ import absolute_import + import logging log = logging.getLogger(__name__) @@ -15,8 +16,11 @@ try: import msgpack # There is a serialization issue on ARM and potentially other platforms for some msgpack bindings, check for it - if msgpack.version >= (0, 4, 0) and msgpack.loads(msgpack.dumps([1, 2, 3], use_bin_type=False), - use_list=True) is None: + if ( + msgpack.version >= (0, 4, 0) + and msgpack.loads(msgpack.dumps([1, 2, 3], use_bin_type=False), use_list=True) + is None + ): raise ImportError elif msgpack.loads(msgpack.dumps([1, 2, 3]), use_list=True) is None: raise ImportError @@ -31,18 +35,20 @@ except ImportError: # Don't exit if msgpack is not available, this is to make local mode work without msgpack # sys.exit(salt.defaults.exitcodes.EX_GENERIC) -if HAS_MSGPACK and hasattr(msgpack, 'exceptions'): +if HAS_MSGPACK and hasattr(msgpack, "exceptions"): exceptions = msgpack.exceptions else: + class PackValueError(Exception): - ''' + """ older versions of msgpack do not have PackValueError - ''' + """ class _exceptions(object): - ''' + """ older versions of msgpack do not have an exceptions module - ''' + """ + PackValueError = PackValueError() exceptions = _exceptions() @@ -54,31 +60,32 @@ version = (0, 0, 0) if not HAS_MSGPACK else msgpack.version def _sanitize_msgpack_kwargs(kwargs): - ''' + """ Clean up msgpack keyword arguments based on the version https://github.com/msgpack/msgpack-python/blob/master/ChangeLog.rst - ''' + """ assert isinstance(kwargs, dict) - if version < (0, 6, 0) and kwargs.pop('strict_map_key', None) is not None: - log.info('removing unsupported `strict_map_key` argument from msgpack call') - if version < (0, 5, 5) and kwargs.pop('raw', None) is not None: - log.info('removing unsupported `raw` argument from msgpack call') - if version < (0, 4, 0) and kwargs.pop('use_bin_type', None) is not None: - log.info('removing unsupported `use_bin_type` argument from msgpack call') + if version < (0, 6, 0) and kwargs.pop("strict_map_key", None) is not None: + log.info("removing unsupported `strict_map_key` argument from msgpack call") + if version < (0, 5, 2) and kwargs.pop("raw", None) is not None: + log.info("removing unsupported `raw` argument from msgpack call") + if version < (0, 4, 0) and kwargs.pop("use_bin_type", None) is not None: + log.info("removing unsupported `use_bin_type` argument from msgpack call") return kwargs class Unpacker(msgpack.Unpacker): - ''' + """ Wraps the msgpack.Unpacker and removes non-relevant arguments - ''' + """ + def __init__(self, *args, **kwargs): msgpack.Unpacker.__init__(self, *args, **_sanitize_msgpack_kwargs(kwargs)) def pack(o, stream, **kwargs): - ''' + """ .. versionadded:: 2018.3.4 Wraps msgpack.pack and ensures that the passed object is unwrapped if it is @@ -86,13 +93,13 @@ def pack(o, stream, **kwargs): By default, this function uses the msgpack module and falls back to msgpack_pure, if the msgpack is not available. - ''' + """ # Writes to a stream, there is no return msgpack.pack(o, stream, **_sanitize_msgpack_kwargs(kwargs)) def packb(o, **kwargs): - ''' + """ .. versionadded:: 2018.3.4 Wraps msgpack.packb and ensures that the passed object is unwrapped if it @@ -100,31 +107,31 @@ def packb(o, **kwargs): By default, this function uses the msgpack module and falls back to msgpack_pure, if the msgpack is not available. - ''' + """ return msgpack.packb(o, **_sanitize_msgpack_kwargs(kwargs)) def unpack(stream, **kwargs): - ''' + """ .. versionadded:: 2018.3.4 Wraps msgpack.unpack. By default, this function uses the msgpack module and falls back to msgpack_pure, if the msgpack is not available. - ''' + """ return msgpack.unpack(stream, **_sanitize_msgpack_kwargs(kwargs)) def unpackb(packed, **kwargs): - ''' + """ .. versionadded:: 2018.3.4 Wraps msgpack.unpack. By default, this function uses the msgpack module and falls back to msgpack_pure. - ''' + """ return msgpack.unpackb(packed, **_sanitize_msgpack_kwargs(kwargs)) diff --git a/salt/utils/nacl.py b/salt/utils/nacl.py index a88b7c18586..91840ee4f32 100644 --- a/salt/utils/nacl.py +++ b/salt/utils/nacl.py @@ -1,23 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" Common code shared between the nacl module and runner. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import logging import os -# Import Salt libs -from salt.ext import six import salt.syspaths import salt.utils.files import salt.utils.platform import salt.utils.stringutils import salt.utils.versions -import salt.utils.win_functions import salt.utils.win_dacl +import salt.utils.win_functions + +# Import Salt libs +from salt.ext import six log = logging.getLogger(__name__) @@ -26,9 +28,11 @@ try: import libnacl.secret import libnacl.sealed except (ImportError, OSError) as e: - REQ_ERROR = 'libnacl import error, perhaps missing python libnacl package or should update.' + REQ_ERROR = ( + "libnacl import error, perhaps missing python libnacl package or should update." + ) -__virtualname__ = 'nacl' +__virtualname__ = "nacl" def __virtual__(): @@ -36,38 +40,38 @@ def __virtual__(): def check_requirements(): - ''' + """ Check required libraries are available - ''' + """ return (REQ_ERROR is None, REQ_ERROR) def _get_config(**kwargs): - ''' + """ Return configuration - ''' - sk_file = kwargs.get('sk_file') + """ + sk_file = kwargs.get("sk_file") if not sk_file: - sk_file = os.path.join(kwargs['opts'].get('pki_dir'), 'master/nacl') + sk_file = os.path.join(kwargs["opts"].get("pki_dir"), "master/nacl") - pk_file = kwargs.get('pk_file') + pk_file = kwargs.get("pk_file") if not pk_file: - pk_file = os.path.join(kwargs['opts'].get('pki_dir'), 'master/nacl.pub') + pk_file = os.path.join(kwargs["opts"].get("pki_dir"), "master/nacl.pub") config = { - 'box_type': kwargs.get('box_type', 'sealedbox'), - 'sk': None, - 'sk_file': sk_file, - 'pk': None, - 'pk_file': pk_file, + "box_type": kwargs.get("box_type", "sealedbox"), + "sk": None, + "sk_file": sk_file, + "pk": None, + "pk_file": pk_file, } - config_key = '{0}.config'.format(__virtualname__) + config_key = "{0}.config".format(__virtualname__) try: - config.update(__salt__['config.get'](config_key, {})) + config.update(__salt__["config.get"](config_key, {})) except (NameError, KeyError) as e: # likely using salt-run so fallback to __opts__ - config.update(kwargs['opts'].get(config_key, {})) + config.update(kwargs["opts"].get(config_key, {})) # pylint: disable=C0201 for k in set(config.keys()) & set(kwargs.keys()): config[k] = kwargs[k] @@ -75,44 +79,44 @@ def _get_config(**kwargs): def _get_sk(**kwargs): - ''' + """ Return sk - ''' + """ config = _get_config(**kwargs) key = None - if config['sk']: - key = salt.utils.stringutils.to_str(config['sk']) - sk_file = config['sk_file'] + if config["sk"]: + key = salt.utils.stringutils.to_str(config["sk"]) + sk_file = config["sk_file"] if not key and sk_file: try: - with salt.utils.files.fopen(sk_file, 'rb') as keyf: - key = salt.utils.stringutils.to_unicode(keyf.read()).rstrip('\n') + with salt.utils.files.fopen(sk_file, "rb") as keyf: + key = salt.utils.stringutils.to_unicode(keyf.read()).rstrip("\n") except (IOError, OSError): - raise Exception('no key or sk_file found') + raise Exception("no key or sk_file found") return base64.b64decode(key) def _get_pk(**kwargs): - ''' + """ Return pk - ''' + """ config = _get_config(**kwargs) pubkey = None - if config['pk']: - pubkey = salt.utils.stringutils.to_str(config['pk']) - pk_file = config['pk_file'] + if config["pk"]: + pubkey = salt.utils.stringutils.to_str(config["pk"]) + pk_file = config["pk_file"] if not pubkey and pk_file: try: - with salt.utils.files.fopen(pk_file, 'rb') as keyf: - pubkey = salt.utils.stringutils.to_unicode(keyf.read()).rstrip('\n') + with salt.utils.files.fopen(pk_file, "rb") as keyf: + pubkey = salt.utils.stringutils.to_unicode(keyf.read()).rstrip("\n") except (IOError, OSError): - raise Exception('no pubkey or pk_file found') + raise Exception("no pubkey or pk_file found") pubkey = six.text_type(pubkey) return base64.b64decode(pubkey) def keygen(sk_file=None, pk_file=None, **kwargs): - ''' + """ Use libnacl to generate a keypair. If no `sk_file` is defined return a keypair. @@ -138,66 +142,77 @@ def keygen(sk_file=None, pk_file=None, **kwargs): continue to work to ensure backwards compatbility, but please use the preferred ``sk_file``. - ''' - if 'keyfile' in kwargs: - sk_file = kwargs['keyfile'] + """ + if "keyfile" in kwargs: + sk_file = kwargs["keyfile"] if sk_file is None: kp = libnacl.public.SecretKey() - return {'sk': base64.b64encode(kp.sk), 'pk': base64.b64encode(kp.pk)} + return {"sk": base64.b64encode(kp.sk), "pk": base64.b64encode(kp.pk)} if pk_file is None: - pk_file = '{0}.pub'.format(sk_file) + pk_file = "{0}.pub".format(sk_file) if sk_file and pk_file is None: if not os.path.isfile(sk_file): kp = libnacl.public.SecretKey() - with salt.utils.files.fopen(sk_file, 'wb') as keyf: + with salt.utils.files.fopen(sk_file, "wb") as keyf: keyf.write(base64.b64encode(kp.sk)) if salt.utils.platform.is_windows(): cur_user = salt.utils.win_functions.get_current_user() salt.utils.win_dacl.set_owner(sk_file, cur_user) - salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True) + salt.utils.win_dacl.set_permissions( + sk_file, + cur_user, + "full_control", + "grant", + reset_perms=True, + protected=True, + ) else: # chmod 0600 file os.chmod(sk_file, 1536) - return 'saved sk_file: {0}'.format(sk_file) + return "saved sk_file: {0}".format(sk_file) else: - raise Exception('sk_file:{0} already exist.'.format(sk_file)) + raise Exception("sk_file:{0} already exist.".format(sk_file)) if sk_file is None and pk_file: - raise Exception('sk_file: Must be set inorder to generate a public key.') + raise Exception("sk_file: Must be set inorder to generate a public key.") if os.path.isfile(sk_file) and os.path.isfile(pk_file): - raise Exception('sk_file:{0} and pk_file:{1} already exist.'.format(sk_file, pk_file)) + raise Exception( + "sk_file:{0} and pk_file:{1} already exist.".format(sk_file, pk_file) + ) if os.path.isfile(sk_file) and not os.path.isfile(pk_file): # generate pk using the sk - with salt.utils.files.fopen(sk_file, 'rb') as keyf: - sk = salt.utils.stringutils.to_unicode(keyf.read()).rstrip('\n') + with salt.utils.files.fopen(sk_file, "rb") as keyf: + sk = salt.utils.stringutils.to_unicode(keyf.read()).rstrip("\n") sk = base64.b64decode(sk) kp = libnacl.public.SecretKey(sk) - with salt.utils.files.fopen(pk_file, 'wb') as keyf: + with salt.utils.files.fopen(pk_file, "wb") as keyf: keyf.write(base64.b64encode(kp.pk)) - return 'saved pk_file: {0}'.format(pk_file) + return "saved pk_file: {0}".format(pk_file) kp = libnacl.public.SecretKey() - with salt.utils.files.fopen(sk_file, 'wb') as keyf: + with salt.utils.files.fopen(sk_file, "wb") as keyf: keyf.write(base64.b64encode(kp.sk)) if salt.utils.platform.is_windows(): cur_user = salt.utils.win_functions.get_current_user() salt.utils.win_dacl.set_owner(sk_file, cur_user) - salt.utils.win_dacl.set_permissions(sk_file, cur_user, 'full_control', 'grant', reset_perms=True, protected=True) + salt.utils.win_dacl.set_permissions( + sk_file, cur_user, "full_control", "grant", reset_perms=True, protected=True + ) else: # chmod 0600 file os.chmod(sk_file, 1536) - with salt.utils.files.fopen(pk_file, 'wb') as keyf: + with salt.utils.files.fopen(pk_file, "wb") as keyf: keyf.write(base64.b64encode(kp.pk)) - return 'saved sk_file:{0} pk_file: {1}'.format(sk_file, pk_file) + return "saved sk_file:{0} pk_file: {1}".format(sk_file, pk_file) def enc(data, **kwargs): - ''' + """ Alias to `{box_type}_encrypt` box_type: secretbox, sealedbox(default) @@ -215,27 +230,27 @@ def enc(data, **kwargs): will continue to work to ensure backwards compatibility, but please use the preferred ``sk``. - ''' - if 'keyfile' in kwargs: - kwargs['sk_file'] = kwargs['keyfile'] + """ + if "keyfile" in kwargs: + kwargs["sk_file"] = kwargs["keyfile"] # set boxtype to `secretbox` to maintain backward compatibility - kwargs['box_type'] = 'secretbox' + kwargs["box_type"] = "secretbox" - if 'key' in kwargs: - kwargs['sk'] = kwargs['key'] + if "key" in kwargs: + kwargs["sk"] = kwargs["key"] # set boxtype to `secretbox` to maintain backward compatibility - kwargs['box_type'] = 'secretbox' + kwargs["box_type"] = "secretbox" - box_type = _get_config(**kwargs)['box_type'] - if box_type == 'secretbox': + box_type = _get_config(**kwargs)["box_type"] + if box_type == "secretbox": return secretbox_encrypt(data, **kwargs) return sealedbox_encrypt(data, **kwargs) def enc_file(name, out=None, **kwargs): - ''' + """ This is a helper function to encrypt a file and return its contents. You can provide an optional output file using `out` @@ -250,25 +265,25 @@ def enc_file(name, out=None, **kwargs): salt-call nacl.enc_file name=salt://crt/mycert out=/tmp/cert salt-run nacl.enc_file name=/tmp/id_rsa box_type=secretbox \ sk_file=/etc/salt/pki/master/nacl.pub - ''' + """ try: - data = __salt__['cp.get_file_str'](name) + data = __salt__["cp.get_file_str"](name) except Exception as e: # pylint: disable=broad-except # likly using salt-run so fallback to local filesystem - with salt.utils.files.fopen(name, 'rb') as f: + with salt.utils.files.fopen(name, "rb") as f: data = salt.utils.stringutils.to_unicode(f.read()) d = enc(data, **kwargs) if out: if os.path.isfile(out): - raise Exception('file:{0} already exist.'.format(out)) - with salt.utils.files.fopen(out, 'wb') as f: + raise Exception("file:{0} already exist.".format(out)) + with salt.utils.files.fopen(out, "wb") as f: f.write(salt.utils.stringutils.to_bytes(d)) - return 'Wrote: {0}'.format(out) + return "Wrote: {0}".format(out) return d def dec(data, **kwargs): - ''' + """ Alias to `{box_type}_decrypt` box_type: secretbox, sealedbox(default) @@ -286,27 +301,27 @@ def dec(data, **kwargs): will continue to work to ensure backwards compatibility, but please use the preferred ``sk``. - ''' - if 'keyfile' in kwargs: - kwargs['sk_file'] = kwargs['keyfile'] + """ + if "keyfile" in kwargs: + kwargs["sk_file"] = kwargs["keyfile"] # set boxtype to `secretbox` to maintain backward compatibility - kwargs['box_type'] = 'secretbox' + kwargs["box_type"] = "secretbox" - if 'key' in kwargs: - kwargs['sk'] = kwargs['key'] + if "key" in kwargs: + kwargs["sk"] = kwargs["key"] # set boxtype to `secretbox` to maintain backward compatibility - kwargs['box_type'] = 'secretbox' + kwargs["box_type"] = "secretbox" - box_type = _get_config(**kwargs)['box_type'] - if box_type == 'secretbox': + box_type = _get_config(**kwargs)["box_type"] + if box_type == "secretbox": return secretbox_decrypt(data, **kwargs) return sealedbox_decrypt(data, **kwargs) def dec_file(name, out=None, **kwargs): - ''' + """ This is a helper function to decrypt a file and return its contents. You can provide an optional output file using `out` @@ -321,25 +336,25 @@ def dec_file(name, out=None, **kwargs): salt-call nacl.dec_file name=salt://crt/mycert.nacl out=/tmp/id_rsa salt-run nacl.dec_file name=/tmp/id_rsa.nacl box_type=secretbox \ sk_file=/etc/salt/pki/master/nacl.pub - ''' + """ try: - data = __salt__['cp.get_file_str'](name) + data = __salt__["cp.get_file_str"](name) except Exception as e: # pylint: disable=broad-except # likly using salt-run so fallback to local filesystem - with salt.utils.files.fopen(name, 'rb') as f: + with salt.utils.files.fopen(name, "rb") as f: data = salt.utils.stringutils.to_unicode(f.read()) d = dec(data, **kwargs) if out: if os.path.isfile(out): - raise Exception('file:{0} already exist.'.format(out)) - with salt.utils.files.fopen(out, 'wb') as f: + raise Exception("file:{0} already exist.".format(out)) + with salt.utils.files.fopen(out, "wb") as f: f.write(salt.utils.stringutils.to_bytes(d)) - return 'Wrote: {0}'.format(out) + return "Wrote: {0}".format(out) return d def sealedbox_encrypt(data, **kwargs): - ''' + """ Encrypt data using a public key generated from `nacl.keygen`. The encryptd data can be decrypted using `nacl.sealedbox_decrypt` only with the secret key. @@ -350,7 +365,7 @@ def sealedbox_encrypt(data, **kwargs): salt-run nacl.sealedbox_encrypt datatoenc salt-call --local nacl.sealedbox_encrypt datatoenc pk_file=/etc/salt/pki/master/nacl.pub salt-call --local nacl.sealedbox_encrypt datatoenc pk='vrwQF7cNiNAVQVAiS3bvcbJUnF0cN6fU9YTZD9mBfzQ=' - ''' + """ # ensure data is in bytes data = salt.utils.stringutils.to_bytes(data) @@ -360,7 +375,7 @@ def sealedbox_encrypt(data, **kwargs): def sealedbox_decrypt(data, **kwargs): - ''' + """ Decrypt data using a secret key that was encrypted using a public key with `nacl.sealedbox_encrypt`. CLI Examples: @@ -370,7 +385,7 @@ def sealedbox_decrypt(data, **kwargs): salt-call nacl.sealedbox_decrypt pEXHQM6cuaF7A= salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl salt-call --local nacl.sealedbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo=' - ''' + """ if data is None: return None @@ -384,7 +399,7 @@ def sealedbox_decrypt(data, **kwargs): def secretbox_encrypt(data, **kwargs): - ''' + """ Encrypt data using a secret key generated from `nacl.keygen`. The same secret key can be used to decrypt the data using `nacl.secretbox_decrypt`. @@ -395,7 +410,7 @@ def secretbox_encrypt(data, **kwargs): salt-run nacl.secretbox_encrypt datatoenc salt-call --local nacl.secretbox_encrypt datatoenc sk_file=/etc/salt/pki/master/nacl salt-call --local nacl.secretbox_encrypt datatoenc sk='YmFkcGFzcwo=' - ''' + """ # ensure data is in bytes data = salt.utils.stringutils.to_bytes(data) @@ -405,7 +420,7 @@ def secretbox_encrypt(data, **kwargs): def secretbox_decrypt(data, **kwargs): - ''' + """ Decrypt data that was encrypted using `nacl.secretbox_encrypt` using the secret key that was generated from `nacl.keygen`. @@ -416,7 +431,7 @@ def secretbox_decrypt(data, **kwargs): salt-call nacl.secretbox_decrypt pEXHQM6cuaF7A= salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk_file=/etc/salt/pki/master/nacl salt-call --local nacl.secretbox_decrypt data='pEXHQM6cuaF7A=' sk='YmFkcGFzcwo=' - ''' + """ if data is None: return None diff --git a/salt/utils/namecheap.py b/salt/utils/namecheap.py index 2e6c220b426..1f3ba159697 100644 --- a/salt/utils/namecheap.py +++ b/salt/utils/namecheap.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Namecheap Management library of common functions used by all the namecheap execution modules @@ -14,21 +14,22 @@ pip install requests -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import xml.dom.minidom # Import Salt libs import salt.loader - from salt.ext import six # Import third party libs try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False @@ -41,8 +42,11 @@ __salt__ = None def __virtual__(): if not HAS_REQUESTS: - return False, 'Missing dependency: \'requests\'. The namecheap utils module ' \ - 'cannot be loaded. ' + return ( + False, + "Missing dependency: 'requests'. The namecheap utils module " + "cannot be loaded. ", + ) global __salt__ if not __salt__: __salt__ = salt.loader.minion_mods(__opts__) @@ -50,12 +54,12 @@ def __virtual__(): def post_request(opts): - namecheap_url = __salt__['config.option']('namecheap.url') + namecheap_url = __salt__["config.option"]("namecheap.url") return _handle_request(requests.post(namecheap_url, data=opts, timeout=45)) def get_request(opts): - namecheap_url = __salt__['config.option']('namecheap.url') + namecheap_url = __salt__["config.option"]("namecheap.url") return _handle_request(requests.get(namecheap_url, params=opts, timeout=45)) @@ -69,12 +73,12 @@ def _handle_request(r): response_xml = xml.dom.minidom.parseString(r.text) apiresponse = response_xml.getElementsByTagName("ApiResponse")[0] - if apiresponse.getAttribute('Status') == "ERROR": + if apiresponse.getAttribute("Status") == "ERROR": data = [] errors = apiresponse.getElementsByTagName("Errors")[0] for e in errors.getElementsByTagName("Error"): data.append(e.firstChild.data) - error = ''.join(data) + error = "".join(data) log.info(apiresponse) log.error(error) raise Exception(error) @@ -89,13 +93,15 @@ def xml_to_dict(xml): if len([n for n in xml.childNodes if n.nodeType != xml.TEXT_NODE]) == 0: if len(result) > 0: if xml.firstChild is not None and len(xml.firstChild.data) > 0: - result['data'] = xml.firstChild.data + result["data"] = xml.firstChild.data elif xml.firstChild is not None and len(xml.firstChild.data) > 0: return xml.firstChild.data else: return None - elif xml.childNodes.length == 1 and \ - xml.childNodes[0].nodeType == xml.CDATA_SECTION_NODE: + elif ( + xml.childNodes.length == 1 + and xml.childNodes[0].nodeType == xml.CDATA_SECTION_NODE + ): return xml.childNodes[0].data else: for n in xml.childNodes: @@ -155,9 +161,9 @@ def string_to_value(value): def get_opts(command): opts = {} - opts['ApiUser'] = __salt__['config.option']('namecheap.name') - opts['UserName'] = __salt__['config.option']('namecheap.user') - opts['ApiKey'] = __salt__['config.option']('namecheap.key') - opts['ClientIp'] = __salt__['config.option']('namecheap.client_ip') - opts['Command'] = command + opts["ApiUser"] = __salt__["config.option"]("namecheap.name") + opts["UserName"] = __salt__["config.option"]("namecheap.user") + opts["ApiKey"] = __salt__["config.option"]("namecheap.key") + opts["ClientIp"] = __salt__["config.option"]("namecheap.client_ip") + opts["Command"] = command return opts diff --git a/salt/utils/napalm.py b/salt/utils/napalm.py index 48baa0967ec..2c5779d859a 100644 --- a/salt/utils/napalm.py +++ b/salt/utils/napalm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Utils for the NAPALM modules and proxy. .. seealso:: @@ -13,21 +13,23 @@ Utils for the NAPALM modules and proxy. - :mod:`Users configuration management <salt.modules.napalm_users>` .. versionadded:: 2017.7.0 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import copy -import traceback -import logging import importlib +import logging +import traceback from functools import wraps +import salt.output +import salt.utils.args +import salt.utils.platform + # Import Salt libs from salt.ext import six -import salt.output -import salt.utils.platform -import salt.utils.args # Import third party libs try: @@ -36,17 +38,19 @@ try: # pylint: disable=unused-import,no-name-in-module import napalm import napalm.base as napalm_base + # pylint: enable=unused-import,no-name-in-module HAS_NAPALM = True HAS_NAPALM_BASE = False # doesn't matter anymore, but needed for the logic below try: - NAPALM_MAJOR = int(napalm.__version__.split('.')[0]) + NAPALM_MAJOR = int(napalm.__version__.split(".")[0]) except AttributeError: NAPALM_MAJOR = 0 except ImportError: HAS_NAPALM = False try: import napalm_base + HAS_NAPALM_BASE = True except ImportError: HAS_NAPALM_BASE = False @@ -56,6 +60,7 @@ try: # from napalm-base # this exception has been introduced only in version 0.24.0 from napalm_base.exceptions import ConnectionClosedException + HAS_CONN_CLOSED_EXC_CLASS = True except ImportError: HAS_CONN_CLOSED_EXC_CLASS = False @@ -64,54 +69,58 @@ log = logging.getLogger(__file__) def is_proxy(opts): - ''' + """ Is this a NAPALM proxy? - ''' - return salt.utils.platform.is_proxy() and opts.get('proxy', {}).get('proxytype') == 'napalm' + """ + return ( + salt.utils.platform.is_proxy() + and opts.get("proxy", {}).get("proxytype") == "napalm" + ) def is_always_alive(opts): - ''' + """ Is always alive required? - ''' - return opts.get('proxy', {}).get('always_alive', True) + """ + return opts.get("proxy", {}).get("always_alive", True) def not_always_alive(opts): - ''' + """ Should this proxy be always alive? - ''' + """ return (is_proxy(opts) and not is_always_alive(opts)) or is_minion(opts) def is_minion(opts): - ''' + """ Is this a NAPALM straight minion? - ''' - return not salt.utils.platform.is_proxy() and 'napalm' in opts + """ + return not salt.utils.platform.is_proxy() and "napalm" in opts def virtual(opts, virtualname, filename): - ''' + """ Returns the __virtual__. - ''' - if ((HAS_NAPALM and NAPALM_MAJOR >= 2) or HAS_NAPALM_BASE) and (is_proxy(opts) or is_minion(opts)): + """ + if ((HAS_NAPALM and NAPALM_MAJOR >= 2) or HAS_NAPALM_BASE) and ( + is_proxy(opts) or is_minion(opts) + ): return virtualname else: return ( False, ( '"{vname}"" {filename} cannot be loaded: ' - 'NAPALM is not installed: ``pip install napalm``' + "NAPALM is not installed: ``pip install napalm``" ).format( - vname=virtualname, - filename='({filename})'.format(filename=filename) - ) + vname=virtualname, filename="({filename})".format(filename=filename) + ), ) def call(napalm_device, method, *args, **kwargs): - ''' + """ Calls arbitrary methods from the network driver instance. Please check the readthedocs_ page for the updated list of getters. @@ -150,23 +159,23 @@ def call(napalm_device, method, *args, **kwargs): 'show chassis fan' ] ) - ''' + """ result = False out = None - opts = napalm_device.get('__opts__', {}) - retry = kwargs.pop('__retry', True) # retry executing the task? - force_reconnect = kwargs.get('force_reconnect', False) + opts = napalm_device.get("__opts__", {}) + retry = kwargs.pop("__retry", True) # retry executing the task? + force_reconnect = kwargs.get("force_reconnect", False) if force_reconnect: - log.debug('Forced reconnection initiated') - log.debug('The current opts (under the proxy key):') - log.debug(opts['proxy']) - opts['proxy'].update(**kwargs) - log.debug('Updated to:') - log.debug(opts['proxy']) + log.debug("Forced reconnection initiated") + log.debug("The current opts (under the proxy key):") + log.debug(opts["proxy"]) + opts["proxy"].update(**kwargs) + log.debug("Updated to:") + log.debug(opts["proxy"]) napalm_device = get_device(opts) try: - if not napalm_device.get('UP', False): - raise Exception('not connected') + if not napalm_device.get("UP", False): + raise Exception("not connected") # if connected will try to execute desired command kwargs_copy = {} kwargs_copy.update(kwargs) @@ -175,38 +184,45 @@ def call(napalm_device, method, *args, **kwargs): # to not be sent to NAPALM methods if warg is None: kwargs.pop(karg) - out = getattr(napalm_device.get('DRIVER'), method)(*args, **kwargs) + out = getattr(napalm_device.get("DRIVER"), method)(*args, **kwargs) # calls the method with the specified parameters result = True except Exception as error: # pylint: disable=broad-except # either not connected # either unable to execute the command - hostname = napalm_device.get('HOSTNAME', '[unspecified hostname]') - err_tb = traceback.format_exc() # let's get the full traceback and display for debugging reasons. + hostname = napalm_device.get("HOSTNAME", "[unspecified hostname]") + err_tb = ( + traceback.format_exc() + ) # let's get the full traceback and display for debugging reasons. if isinstance(error, NotImplementedError): - comment = '{method} is not implemented for the NAPALM {driver} driver!'.format( - method=method, - driver=napalm_device.get('DRIVER_NAME') + comment = "{method} is not implemented for the NAPALM {driver} driver!".format( + method=method, driver=napalm_device.get("DRIVER_NAME") ) - elif retry and HAS_CONN_CLOSED_EXC_CLASS and isinstance(error, ConnectionClosedException): + elif ( + retry + and HAS_CONN_CLOSED_EXC_CLASS + and isinstance(error, ConnectionClosedException) + ): # Received disconection whilst executing the operation. # Instructed to retry (default behaviour) # thus trying to re-establish the connection # and re-execute the command # if any of the operations (close, open, call) will rise again ConnectionClosedException # it will fail loudly. - kwargs['__retry'] = False # do not attempt re-executing - comment = 'Disconnected from {device}. Trying to reconnect.'.format(device=hostname) + kwargs["__retry"] = False # do not attempt re-executing + comment = "Disconnected from {device}. Trying to reconnect.".format( + device=hostname + ) log.error(err_tb) log.error(comment) - log.debug('Clearing the connection with %s', hostname) - call(napalm_device, 'close', __retry=False) # safely close the connection + log.debug("Clearing the connection with %s", hostname) + call(napalm_device, "close", __retry=False) # safely close the connection # Make sure we don't leave any TCP connection open behind # if we fail to close properly, we might not be able to access the - log.debug('Re-opening the connection with %s', hostname) - call(napalm_device, 'open', __retry=False) - log.debug('Connection re-opened with %s', hostname) - log.debug('Re-executing %s', method) + log.debug("Re-opening the connection with %s", hostname) + call(napalm_device, "open", __retry=False) + log.debug("Connection re-opened with %s", hostname) + log.debug("Re-executing %s", method) return call(napalm_device, method, *args, **kwargs) # If still not able to reconnect and execute the task, # the proxy keepalive feature (if enabled) will attempt @@ -229,88 +245,91 @@ def call(napalm_device, method, *args, **kwargs): # will know the state of the connection and will try reconnecting. else: comment = 'Cannot execute "{method}" on {device}{port} as {user}. Reason: {error}!'.format( - device=napalm_device.get('HOSTNAME', '[unspecified hostname]'), - port=(':{port}'.format(port=napalm_device.get('OPTIONAL_ARGS', {}).get('port')) - if napalm_device.get('OPTIONAL_ARGS', {}).get('port') else ''), - user=napalm_device.get('USERNAME', ''), + device=napalm_device.get("HOSTNAME", "[unspecified hostname]"), + port=( + ":{port}".format( + port=napalm_device.get("OPTIONAL_ARGS", {}).get("port") + ) + if napalm_device.get("OPTIONAL_ARGS", {}).get("port") + else "" + ), + user=napalm_device.get("USERNAME", ""), method=method, - error=error + error=error, ) log.error(comment) log.error(err_tb) - return { - 'out': {}, - 'result': False, - 'comment': comment, - 'traceback': err_tb - } + return {"out": {}, "result": False, "comment": comment, "traceback": err_tb} finally: - if opts and not_always_alive(opts) and napalm_device.get('CLOSE', True): + if opts and not_always_alive(opts) and napalm_device.get("CLOSE", True): # either running in a not-always-alive proxy # either running in a regular minion # close the connection when the call is over # unless the CLOSE is explicitly set as False - napalm_device['DRIVER'].close() - return { - 'out': out, - 'result': result, - 'comment': '' - } + napalm_device["DRIVER"].close() + return {"out": out, "result": result, "comment": ""} def get_device_opts(opts, salt_obj=None): - ''' + """ Returns the options of the napalm device. :pram: opts :return: the network device opts - ''' + """ network_device = {} # by default, look in the proxy config details - device_dict = opts.get('proxy', {}) if is_proxy(opts) else opts.get('napalm', {}) - if opts.get('proxy') or opts.get('napalm'): - opts['multiprocessing'] = device_dict.get('multiprocessing', False) + device_dict = opts.get("proxy", {}) if is_proxy(opts) else opts.get("napalm", {}) + if opts.get("proxy") or opts.get("napalm"): + opts["multiprocessing"] = device_dict.get("multiprocessing", False) # Most NAPALM drivers are SSH-based, so multiprocessing should default to False. # But the user can be allows one to have a different value for the multiprocessing, which will # override the opts. if not device_dict: # still not able to setup - log.error('Incorrect minion config. Please specify at least the napalm driver name!') + log.error( + "Incorrect minion config. Please specify at least the napalm driver name!" + ) # either under the proxy hier, either under the napalm in the config file - network_device['HOSTNAME'] = device_dict.get('host') or \ - device_dict.get('hostname') or \ - device_dict.get('fqdn') or \ - device_dict.get('ip') - network_device['USERNAME'] = device_dict.get('username') or \ - device_dict.get('user') - network_device['DRIVER_NAME'] = device_dict.get('driver') or \ - device_dict.get('os') - network_device['PASSWORD'] = device_dict.get('passwd') or \ - device_dict.get('password') or \ - device_dict.get('pass') or \ - '' - network_device['TIMEOUT'] = device_dict.get('timeout', 60) - network_device['OPTIONAL_ARGS'] = device_dict.get('optional_args', {}) - network_device['ALWAYS_ALIVE'] = device_dict.get('always_alive', True) - network_device['PROVIDER'] = device_dict.get('provider') - network_device['UP'] = False + network_device["HOSTNAME"] = ( + device_dict.get("host") + or device_dict.get("hostname") + or device_dict.get("fqdn") + or device_dict.get("ip") + ) + network_device["USERNAME"] = device_dict.get("username") or device_dict.get("user") + network_device["DRIVER_NAME"] = device_dict.get("driver") or device_dict.get("os") + network_device["PASSWORD"] = ( + device_dict.get("passwd") + or device_dict.get("password") + or device_dict.get("pass") + or "" + ) + network_device["TIMEOUT"] = device_dict.get("timeout", 60) + network_device["OPTIONAL_ARGS"] = device_dict.get("optional_args", {}) + network_device["ALWAYS_ALIVE"] = device_dict.get("always_alive", True) + network_device["PROVIDER"] = device_dict.get("provider") + network_device["UP"] = False # get driver object form NAPALM - if 'config_lock' not in network_device['OPTIONAL_ARGS']: - network_device['OPTIONAL_ARGS']['config_lock'] = False - if network_device['ALWAYS_ALIVE'] and 'keepalive' not in network_device['OPTIONAL_ARGS']: - network_device['OPTIONAL_ARGS']['keepalive'] = 5 # 5 seconds keepalive + if "config_lock" not in network_device["OPTIONAL_ARGS"]: + network_device["OPTIONAL_ARGS"]["config_lock"] = False + if ( + network_device["ALWAYS_ALIVE"] + and "keepalive" not in network_device["OPTIONAL_ARGS"] + ): + network_device["OPTIONAL_ARGS"]["keepalive"] = 5 # 5 seconds keepalive return network_device def get_device(opts, salt_obj=None): - ''' + """ Initialise the connection with the network device through NAPALM. :param: opts :return: the network device object - ''' - log.debug('Setting up NAPALM connection') + """ + log.debug("Setting up NAPALM connection") network_device = get_device_opts(opts, salt_obj=salt_obj) provider_lib = napalm_base - if network_device.get('PROVIDER'): + if network_device.get("PROVIDER"): # In case the user requires a different provider library, # other than napalm-base. # For example, if napalm-base does not satisfy the requirements @@ -322,41 +341,44 @@ def get_device(opts, salt_obj=None): # Configuration example: # provider: napalm_base_example try: - provider_lib = importlib.import_module(network_device.get('PROVIDER')) + provider_lib = importlib.import_module(network_device.get("PROVIDER")) except ImportError as ierr: - log.error('Unable to import %s', - network_device.get('PROVIDER'), - exc_info=True) - log.error('Falling back to napalm-base') - _driver_ = provider_lib.get_network_driver(network_device.get('DRIVER_NAME')) + log.error( + "Unable to import %s", network_device.get("PROVIDER"), exc_info=True + ) + log.error("Falling back to napalm-base") + _driver_ = provider_lib.get_network_driver(network_device.get("DRIVER_NAME")) try: - network_device['DRIVER'] = _driver_( - network_device.get('HOSTNAME', ''), - network_device.get('USERNAME', ''), - network_device.get('PASSWORD', ''), - timeout=network_device['TIMEOUT'], - optional_args=network_device['OPTIONAL_ARGS'] + network_device["DRIVER"] = _driver_( + network_device.get("HOSTNAME", ""), + network_device.get("USERNAME", ""), + network_device.get("PASSWORD", ""), + timeout=network_device["TIMEOUT"], + optional_args=network_device["OPTIONAL_ARGS"], ) - network_device.get('DRIVER').open() + network_device.get("DRIVER").open() # no exception raised here, means connection established - network_device['UP'] = True + network_device["UP"] = True except napalm_base.exceptions.ConnectionException as error: base_err_msg = "Cannot connect to {hostname}{port} as {username}.".format( - hostname=network_device.get('HOSTNAME', '[unspecified hostname]'), - port=(':{port}'.format(port=network_device.get('OPTIONAL_ARGS', {}).get('port')) - if network_device.get('OPTIONAL_ARGS', {}).get('port') else ''), - username=network_device.get('USERNAME', '') + hostname=network_device.get("HOSTNAME", "[unspecified hostname]"), + port=( + ":{port}".format( + port=network_device.get("OPTIONAL_ARGS", {}).get("port") + ) + if network_device.get("OPTIONAL_ARGS", {}).get("port") + else "" + ), + username=network_device.get("USERNAME", ""), ) log.error(base_err_msg) - log.error( - "Please check error: %s", error - ) + log.error("Please check error: %s", error) raise napalm_base.exceptions.ConnectionException(base_err_msg) return network_device def proxy_napalm_wrap(func): - ''' + """ This decorator is used to make the execution module functions available outside a proxy minion, or when running inside a proxy minion. If we are running in a proxy, retrieve the connection details @@ -364,53 +386,56 @@ def proxy_napalm_wrap(func): use the connection information from the opts. :param func: :return: - ''' + """ + @wraps(func) def func_wrapper(*args, **kwargs): wrapped_global_namespace = func.__globals__ # get __opts__ and __proxy__ from func_globals - proxy = wrapped_global_namespace.get('__proxy__') - opts = copy.deepcopy(wrapped_global_namespace.get('__opts__')) + proxy = wrapped_global_namespace.get("__proxy__") + opts = copy.deepcopy(wrapped_global_namespace.get("__opts__")) # in any case, will inject the `napalm_device` global # the execution modules will make use of this variable from now on # previously they were accessing the device properties through the __proxy__ object - always_alive = opts.get('proxy', {}).get('always_alive', True) + always_alive = opts.get("proxy", {}).get("always_alive", True) # force_reconnect is a magic keyword arg that allows one to establish # a separate connection to the network device running under an always # alive Proxy Minion, using new credentials (overriding the ones # configured in the opts / pillar. - force_reconnect = kwargs.get('force_reconnect', False) + force_reconnect = kwargs.get("force_reconnect", False) if force_reconnect: - log.debug('Usage of reconnect force detected') - log.debug('Opts before merging') - log.debug(opts['proxy']) - opts['proxy'].update(**kwargs) - log.debug('Opts after merging') - log.debug(opts['proxy']) + log.debug("Usage of reconnect force detected") + log.debug("Opts before merging") + log.debug(opts["proxy"]) + opts["proxy"].update(**kwargs) + log.debug("Opts after merging") + log.debug(opts["proxy"]) if is_proxy(opts) and always_alive: # if it is running in a NAPALM Proxy and it's using the default # always alive behaviour, will get the cached copy of the network # device object which should preserve the connection. if force_reconnect: - wrapped_global_namespace['napalm_device'] = get_device(opts) + wrapped_global_namespace["napalm_device"] = get_device(opts) else: - wrapped_global_namespace['napalm_device'] = proxy['napalm.get_device']() + wrapped_global_namespace["napalm_device"] = proxy["napalm.get_device"]() elif is_proxy(opts) and not always_alive: # if still proxy, but the user does not want the SSH session always alive # get a new device instance # which establishes a new connection # which is closed just before the call() function defined above returns - if 'inherit_napalm_device' not in kwargs or ('inherit_napalm_device' in kwargs and - not kwargs['inherit_napalm_device']): + if "inherit_napalm_device" not in kwargs or ( + "inherit_napalm_device" in kwargs + and not kwargs["inherit_napalm_device"] + ): # try to open a new connection # but only if the function does not inherit the napalm driver # for configuration management this is very important, # in order to make sure we are editing the same session. try: - wrapped_global_namespace['napalm_device'] = get_device(opts) + wrapped_global_namespace["napalm_device"] = get_device(opts) except napalm_base.exceptions.ConnectionException as nce: log.error(nce) - return '{base_msg}. See log for details.'.format( + return "{base_msg}. See log for details.".format( base_msg=six.text_type(nce.msg) ) else: @@ -420,49 +445,66 @@ def proxy_napalm_wrap(func): # this is extremely important for configuration-related features # as all actions must be issued within the same configuration session # otherwise we risk to open multiple sessions - wrapped_global_namespace['napalm_device'] = kwargs['inherit_napalm_device'] + wrapped_global_namespace["napalm_device"] = kwargs[ + "inherit_napalm_device" + ] else: # if not a NAPLAM proxy # thus it is running on a regular minion, directly on the network device # or another flavour of Minion from where we can invoke arbitrary # NAPALM commands # get __salt__ from func_globals - log.debug('Not running in a NAPALM Proxy Minion') - _salt_obj = wrapped_global_namespace.get('__salt__') - napalm_opts = _salt_obj['config.get']('napalm', {}) - napalm_inventory = _salt_obj['config.get']('napalm_inventory', {}) - log.debug('NAPALM opts found in the Minion config') + log.debug("Not running in a NAPALM Proxy Minion") + _salt_obj = wrapped_global_namespace.get("__salt__") + napalm_opts = _salt_obj["config.get"]("napalm", {}) + napalm_inventory = _salt_obj["config.get"]("napalm_inventory", {}) + log.debug("NAPALM opts found in the Minion config") log.debug(napalm_opts) clean_kwargs = salt.utils.args.clean_kwargs(**kwargs) napalm_opts.update(clean_kwargs) # no need for deeper merge - log.debug('Merging the found opts with the CLI args') + log.debug("Merging the found opts with the CLI args") log.debug(napalm_opts) - host = napalm_opts.get('host') or napalm_opts.get('hostname') or\ - napalm_opts.get('fqdn') or napalm_opts.get('ip') - if host and napalm_inventory and isinstance(napalm_inventory, dict) and\ - host in napalm_inventory: + host = ( + napalm_opts.get("host") + or napalm_opts.get("hostname") + or napalm_opts.get("fqdn") + or napalm_opts.get("ip") + ) + if ( + host + and napalm_inventory + and isinstance(napalm_inventory, dict) + and host in napalm_inventory + ): inventory_opts = napalm_inventory[host] - log.debug('Found %s in the NAPALM inventory:', host) + log.debug("Found %s in the NAPALM inventory:", host) log.debug(inventory_opts) napalm_opts.update(inventory_opts) - log.debug('Merging the config for %s with the details found in the napalm inventory:', host) + log.debug( + "Merging the config for %s with the details found in the napalm inventory:", + host, + ) log.debug(napalm_opts) opts = copy.deepcopy(opts) # make sure we don't override the original # opts, but just inject the CLI args from the kwargs to into the # object manipulated by ``get_device_opts`` to extract the # connection details, then use then to establish the connection. - opts['napalm'] = napalm_opts - if 'inherit_napalm_device' not in kwargs or ('inherit_napalm_device' in kwargs and - not kwargs['inherit_napalm_device']): + opts["napalm"] = napalm_opts + if "inherit_napalm_device" not in kwargs or ( + "inherit_napalm_device" in kwargs + and not kwargs["inherit_napalm_device"] + ): # try to open a new connection # but only if the function does not inherit the napalm driver # for configuration management this is very important, # in order to make sure we are editing the same session. try: - wrapped_global_namespace['napalm_device'] = get_device(opts, salt_obj=_salt_obj) + wrapped_global_namespace["napalm_device"] = get_device( + opts, salt_obj=_salt_obj + ) except napalm_base.exceptions.ConnectionException as nce: log.error(nce) - return '{base_msg}. See log for details.'.format( + return "{base_msg}. See log for details.".format( base_msg=six.text_type(nce.msg) ) else: @@ -472,82 +514,83 @@ def proxy_napalm_wrap(func): # this is extremely important for configuration-related features # as all actions must be issued within the same configuration session # otherwise we risk to open multiple sessions - wrapped_global_namespace['napalm_device'] = kwargs['inherit_napalm_device'] + wrapped_global_namespace["napalm_device"] = kwargs[ + "inherit_napalm_device" + ] if not_always_alive(opts): # inject the __opts__ only when not always alive # otherwise, we don't want to overload the always-alive proxies - wrapped_global_namespace['napalm_device']['__opts__'] = opts + wrapped_global_namespace["napalm_device"]["__opts__"] = opts ret = func(*args, **kwargs) if force_reconnect: - log.debug('That was a forced reconnect, gracefully clearing up') - device = wrapped_global_namespace['napalm_device'] - closing = call(device, 'close', __retry=False) + log.debug("That was a forced reconnect, gracefully clearing up") + device = wrapped_global_namespace["napalm_device"] + closing = call(device, "close", __retry=False) return ret + return func_wrapper def default_ret(name): - ''' + """ Return the default dict of the state output. - ''' - ret = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': '' - } + """ + ret = {"name": name, "changes": {}, "result": False, "comment": ""} return ret def loaded_ret(ret, loaded, test, debug, compliance_report=False, opts=None): - ''' + """ Return the final state output. ret The initial state output structure. loaded The loaded dictionary. - ''' + """ # Always get the comment changes = {} - ret['comment'] = loaded['comment'] - if 'diff' in loaded: - changes['diff'] = loaded['diff'] - if 'commit_id' in loaded: - changes['commit_id'] = loaded['commit_id'] - if 'compliance_report' in loaded: + ret["comment"] = loaded["comment"] + if "diff" in loaded: + changes["diff"] = loaded["diff"] + if "commit_id" in loaded: + changes["commit_id"] = loaded["commit_id"] + if "compliance_report" in loaded: if compliance_report: - changes['compliance_report'] = loaded['compliance_report'] - if debug and 'loaded_config' in loaded: - changes['loaded_config'] = loaded['loaded_config'] - if changes.get('diff'): - ret['comment'] = '{comment_base}\n\nConfiguration diff:\n\n{diff}'.format(comment_base=ret['comment'], - diff=changes['diff']) - if changes.get('loaded_config'): - ret['comment'] = '{comment_base}\n\nLoaded config:\n\n{loaded_cfg}'.format( - comment_base=ret['comment'], - loaded_cfg=changes['loaded_config']) - if changes.get('compliance_report'): - ret['comment'] = '{comment_base}\n\nCompliance report:\n\n{compliance}'.format( - comment_base=ret['comment'], - compliance=salt.output.string_format(changes['compliance_report'], 'nested', opts=opts)) - if not loaded.get('result', False): + changes["compliance_report"] = loaded["compliance_report"] + if debug and "loaded_config" in loaded: + changes["loaded_config"] = loaded["loaded_config"] + if changes.get("diff"): + ret["comment"] = "{comment_base}\n\nConfiguration diff:\n\n{diff}".format( + comment_base=ret["comment"], diff=changes["diff"] + ) + if changes.get("loaded_config"): + ret["comment"] = "{comment_base}\n\nLoaded config:\n\n{loaded_cfg}".format( + comment_base=ret["comment"], loaded_cfg=changes["loaded_config"] + ) + if changes.get("compliance_report"): + ret["comment"] = "{comment_base}\n\nCompliance report:\n\n{compliance}".format( + comment_base=ret["comment"], + compliance=salt.output.string_format( + changes["compliance_report"], "nested", opts=opts + ), + ) + if not loaded.get("result", False): # Failure of some sort return ret - if not loaded.get('already_configured', True): + if not loaded.get("already_configured", True): # We're making changes if test: - ret['result'] = None + ret["result"] = None return ret # Not test, changes were applied - ret.update({ - 'result': True, - 'changes': changes, - 'comment': "Configuration changed!\n{}".format(loaded['comment']) - }) + ret.update( + { + "result": True, + "changes": changes, + "comment": "Configuration changed!\n{}".format(loaded["comment"]), + } + ) return ret # No changes - ret.update({ - 'result': True, - 'changes': {} - }) + ret.update({"result": True, "changes": {}}) return ret diff --git a/salt/utils/nb_popen.py b/salt/utils/nb_popen.py index 875b7888db3..ded73b0ee3e 100644 --- a/salt/utils/nb_popen.py +++ b/salt/utils/nb_popen.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -12,20 +12,21 @@ found on: http://code.activestate.com/recipes/440554/ -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + +import errno +import logging # Import python libs import os -import sys -import time -import errno import select -import logging -import tempfile import subprocess +import sys +import tempfile +import time -mswindows = (sys.platform == "win32") +mswindows = sys.platform == "win32" try: from win32file import ReadFile, WriteFile @@ -39,36 +40,36 @@ log = logging.getLogger(__name__) class NonBlockingPopen(subprocess.Popen): - #_stdin_logger_name_ = 'salt.utils.nb_popen.STDIN.PID-{pid}' - _stdout_logger_name_ = 'salt.utils.nb_popen.STDOUT.PID-{pid}' - _stderr_logger_name_ = 'salt.utils.nb_popen.STDERR.PID-{pid}' + # _stdin_logger_name_ = 'salt.utils.nb_popen.STDIN.PID-{pid}' + _stdout_logger_name_ = "salt.utils.nb_popen.STDOUT.PID-{pid}" + _stderr_logger_name_ = "salt.utils.nb_popen.STDERR.PID-{pid}" def __init__(self, *args, **kwargs): - self.stream_stds = kwargs.pop('stream_stds', False) + self.stream_stds = kwargs.pop("stream_stds", False) # Half a megabyte in memory is more than enough to start writing to # a temporary file. - self.max_size_in_mem = kwargs.pop('max_size_in_mem', 512000) + self.max_size_in_mem = kwargs.pop("max_size_in_mem", 512000) # Let's configure the std{in, out,err} logging handler names - #self._stdin_logger_name_ = kwargs.pop( + # self._stdin_logger_name_ = kwargs.pop( # 'stdin_logger_name', self._stdin_logger_name_ - #) + # ) self._stdout_logger_name_ = kwargs.pop( - 'stdout_logger_name', self._stdout_logger_name_ + "stdout_logger_name", self._stdout_logger_name_ ) self._stderr_logger_name_ = kwargs.pop( - 'stderr_logger_name', self._stderr_logger_name_ + "stderr_logger_name", self._stderr_logger_name_ ) - logging_command = kwargs.pop('logging_command', None) - stderr = kwargs.get('stderr', None) + logging_command = kwargs.pop("logging_command", None) + stderr = kwargs.get("stderr", None) super(NonBlockingPopen, self).__init__(*args, **kwargs) - #self._stdin_logger = logging.getLogger( + # self._stdin_logger = logging.getLogger( # self._stdin_logger_name_.format(pid=self.pid) - #) + # ) self.stdout_buff = tempfile.SpooledTemporaryFile(self.max_size_in_mem) self._stdout_logger = logging.getLogger( @@ -79,26 +80,24 @@ class NonBlockingPopen(subprocess.Popen): self.stderr_buff = self.stdout_buff self._stderr_logger = self._stdout_logger else: - self.stderr_buff = tempfile.SpooledTemporaryFile( - self.max_size_in_mem - ) + self.stderr_buff = tempfile.SpooledTemporaryFile(self.max_size_in_mem) self._stderr_logger = logging.getLogger( self._stderr_logger_name_.format(pid=self.pid) ) log.info( - 'Running command under pid %s: \'%s\'', + "Running command under pid %s: '%s'", self.pid, - args if logging_command is None else logging_command + args if logging_command is None else logging_command, ) def recv(self, maxsize=None): - return self._recv('stdout', maxsize) + return self._recv("stdout", maxsize) def recv_err(self, maxsize=None): - return self._recv('stderr', maxsize) + return self._recv("stderr", maxsize) - def send_recv(self, input='', maxsize=None): + def send_recv(self, input="", maxsize=None): return self.send(input), self.recv(maxsize), self.recv_err(maxsize) def get_conn_maxsize(self, which, maxsize): @@ -113,6 +112,7 @@ class NonBlockingPopen(subprocess.Popen): setattr(self, which, None) if mswindows: + def send(self, input): if not self.stdin: return None @@ -120,12 +120,12 @@ class NonBlockingPopen(subprocess.Popen): try: x = msvcrt.get_osfhandle(self.stdin.fileno()) (errCode, written) = WriteFile(x, input) - #self._stdin_logger.debug(input.rstrip()) + # self._stdin_logger.debug(input.rstrip()) except ValueError: - return self._close('stdin') + return self._close("stdin") except (subprocess.pywintypes.error, Exception) as why: if why.args[0] in (109, errno.ESHUTDOWN): - return self._close('stdin') + return self._close("stdin") raise return written @@ -149,8 +149,8 @@ class NonBlockingPopen(subprocess.Popen): return self._close(which) raise - getattr(self, '{0}_buff'.format(which)).write(read) - getattr(self, '_{0}_logger'.format(which)).debug(read.rstrip()) + getattr(self, "{0}_buff".format(which)).write(read) + getattr(self, "_{0}_logger".format(which)).debug(read.rstrip()) if self.stream_stds: getattr(sys, which).write(read) @@ -169,10 +169,10 @@ class NonBlockingPopen(subprocess.Popen): try: written = os.write(self.stdin.fileno(), input) - #self._stdin_logger.debug(input.rstrip()) + # self._stdin_logger.debug(input.rstrip()) except OSError as why: if why.args[0] == errno.EPIPE: # broken pipe - return self._close('stdin') + return self._close("stdin") raise return written @@ -188,7 +188,7 @@ class NonBlockingPopen(subprocess.Popen): try: if not select.select([conn], [], [], 0)[0]: - return '' + return "" buff = conn.read(maxsize) if not buff: @@ -197,8 +197,8 @@ class NonBlockingPopen(subprocess.Popen): if self.universal_newlines: buff = self._translate_newlines(buff) - getattr(self, '{0}_buff'.format(which)).write(buff) - getattr(self, '_{0}_logger'.format(which)).debug(buff.rstrip()) + getattr(self, "{0}_buff".format(which)).write(buff) + getattr(self, "_{0}_logger".format(which)).debug(buff.rstrip()) if self.stream_stds: getattr(sys, which).write(buff) diff --git a/salt/utils/network.py b/salt/utils/network.py index 7702cac18c9..136129ad958 100644 --- a/salt/utils/network.py +++ b/salt/utils/network.py @@ -1,26 +1,24 @@ # -*- coding: utf-8 -*- # pylint: disable=invalid-name -''' +""" Define some generic socket functions for network modules -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import collections import itertools -import os -import re -import types -import socket import logging +import os import platform import random +import re +import socket import subprocess +import types from string import ascii_letters, digits -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin - # Import salt libs import salt.utils.args import salt.utils.files @@ -30,12 +28,17 @@ import salt.utils.stringutils import salt.utils.zeromq from salt._compat import ipaddress from salt.exceptions import SaltClientError, SaltSystemExit + +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin from salt.utils.decorators.jinja import jinja_filter from salt.utils.versions import LooseVersion if salt.utils.platform.is_windows(): # inet_pton does not exist in Windows, this is a workaround from salt.ext import win_inet_pton # pylint: disable=unused-import + # Attempt to import win_network import salt.utils.win_network @@ -44,6 +47,7 @@ log = logging.getLogger(__name__) try: import ctypes import ctypes.util + LIBC = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c")) RES_INIT = LIBC.__res_init except (ImportError, OSError, AttributeError, TypeError): @@ -51,18 +55,18 @@ except (ImportError, OSError, AttributeError, TypeError): def sanitize_host(host): - ''' + """ Sanitize host string. https://tools.ietf.org/html/rfc1123#section-2.1 - ''' + """ RFC952_characters = ascii_letters + digits + ".-" return "".join([c for c in host[0:255] if c in RFC952_characters]) def isportopen(host, port): - ''' + """ Return status of a port - ''' + """ if not 1 <= int(port) <= 65535: return False @@ -74,13 +78,14 @@ def isportopen(host, port): def host_to_ips(host): - ''' + """ Returns a list of IP addresses of a given hostname or None if not found. - ''' + """ ips = [] try: for family, socktype, proto, canonname, sockaddr in socket.getaddrinfo( - host, 0, socket.AF_UNSPEC, socket.SOCK_STREAM): + host, 0, socket.AF_UNSPEC, socket.SOCK_STREAM + ): if family == socket.AF_INET: ip, port = sockaddr elif family == socket.AF_INET6: @@ -94,24 +99,34 @@ def host_to_ips(host): def _generate_minion_id(): - ''' + """ Get list of possible host names and convention names. :return: - ''' + """ # There are three types of hostnames: # 1. Network names. How host is accessed from the network. # 2. Host aliases. They might be not available in all the network or only locally (/etc/hosts) # 3. Convention names, an internal nodename. class DistinctList(list): - ''' + """ List, which allows one to append only distinct objects. Needs to work on Python 2.6, because of collections.OrderedDict only since 2.7 version. Override 'filter()' for custom filtering. - ''' - localhost_matchers = [r'localhost.*', r'ip6-.*', r'127[.]\d', r'0\.0\.0\.0', - r'::1.*', r'ipv6-.*', r'fe00::.*', r'fe02::.*', r'1.0.0.*.ip6.arpa'] + """ + + localhost_matchers = [ + r"localhost.*", + r"ip6-.*", + r"127[.]\d", + r"0\.0\.0\.0", + r"::1.*", + r"ipv6-.*", + r"fe00::.*", + r"fe02::.*", + r"1.0.0.*.ip6.arpa", + ] def append(self, p_object): if p_object and p_object not in self and not self.filter(p_object): @@ -124,7 +139,7 @@ def _generate_minion_id(): return self def filter(self, element): - 'Returns True if element needs to be filtered' + "Returns True if element needs to be filtered" for rgx in self.localhost_matchers: if re.match(rgx, element): return True @@ -134,56 +149,76 @@ def _generate_minion_id(): hostname = socket.gethostname() - hosts = DistinctList().append( - salt.utils.stringutils.to_unicode(socket.getfqdn(salt.utils.stringutils.to_bytes(hostname))) - ).append(platform.node()).append(hostname) + hosts = ( + DistinctList() + .append( + salt.utils.stringutils.to_unicode( + socket.getfqdn(salt.utils.stringutils.to_bytes(hostname)) + ) + ) + .append(platform.node()) + .append(hostname) + ) if not hosts: try: - for a_nfo in socket.getaddrinfo(hosts.first() or 'localhost', None, socket.AF_INET, - socket.SOCK_RAW, socket.IPPROTO_IP, socket.AI_CANONNAME): + for a_nfo in socket.getaddrinfo( + hosts.first() or "localhost", + None, + socket.AF_INET, + socket.SOCK_RAW, + socket.IPPROTO_IP, + socket.AI_CANONNAME, + ): if len(a_nfo) > 3: hosts.append(a_nfo[3]) except socket.gaierror: - log.warning('Cannot resolve address {addr} info via socket: {message}'.format( - addr=hosts.first() or 'localhost (N/A)', message=socket.gaierror) + log.warning( + "Cannot resolve address {addr} info via socket: {message}".format( + addr=hosts.first() or "localhost (N/A)", message=socket.gaierror + ) ) # Universal method for everywhere (Linux, Slowlaris, Windows etc) - for f_name in ('/etc/hostname', '/etc/nodename', '/etc/hosts', - r'{win}\system32\drivers\etc\hosts'.format(win=os.getenv('WINDIR'))): + for f_name in ( + "/etc/hostname", + "/etc/nodename", + "/etc/hosts", + r"{win}\system32\drivers\etc\hosts".format(win=os.getenv("WINDIR")), + ): try: with salt.utils.files.fopen(f_name) as f_hdl: for line in f_hdl: line = salt.utils.stringutils.to_unicode(line) - hst = line.strip().split('#')[0].strip().split() + hst = line.strip().split("#")[0].strip().split() if hst: - if hst[0][:4] in ('127.', '::1') or len(hst) == 1: + if hst[0][:4] in ("127.", "::1") or len(hst) == 1: hosts.extend(hst) except IOError: pass # include public and private ipaddresses - return hosts.extend([addr for addr in ip_addrs() - if not ipaddress.ip_address(addr).is_loopback]) + return hosts.extend( + [addr for addr in ip_addrs() if not ipaddress.ip_address(addr).is_loopback] + ) def generate_minion_id(): - ''' + """ Return only first element of the hostname from all possible list. :return: - ''' + """ try: ret = salt.utils.stringutils.to_unicode(_generate_minion_id().first()) except TypeError: ret = None - return ret or 'localhost' + return ret or "localhost" def get_socket(addr, type=socket.SOCK_STREAM, proto=0): - ''' + """ Return a socket object for the addr IP-version agnostic - ''' + """ version = ipaddress.ip_address(addr).version if version == 4: @@ -194,16 +229,20 @@ def get_socket(addr, type=socket.SOCK_STREAM, proto=0): def get_fqhostname(): - ''' + """ Returns the fully qualified hostname - ''' + """ l = [socket.getfqdn()] # try socket.getaddrinfo try: addrinfo = socket.getaddrinfo( - socket.gethostname(), 0, socket.AF_UNSPEC, socket.SOCK_STREAM, - socket.SOL_TCP, socket.AI_CANONNAME + socket.gethostname(), + 0, + socket.AF_UNSPEC, + socket.SOCK_STREAM, + socket.SOL_TCP, + socket.AI_CANONNAME, ) for info in addrinfo: # info struct [family, socktype, proto, canonname, sockaddr] @@ -216,23 +255,23 @@ def get_fqhostname(): def ip_to_host(ip): - ''' + """ Returns the hostname of a given IP - ''' + """ try: hostname, aliaslist, ipaddrlist = socket.gethostbyaddr(ip) except Exception as exc: # pylint: disable=broad-except - log.debug('salt.utils.network.ip_to_host(%r) failed: %s', ip, exc) + log.debug("salt.utils.network.ip_to_host(%r) failed: %s", ip, exc) hostname = None return hostname def is_reachable_host(entity_name): - ''' + """ Returns a bool telling if the entity name is a reachable host (IPv4/IPv6/FQDN/etc). :param hostname: :return: - ''' + """ try: assert type(socket.getaddrinfo(entity_name, 0, 0, 0, 0)) == list ret = True @@ -243,16 +282,16 @@ def is_reachable_host(entity_name): def is_ip(ip_addr): - ''' + """ Returns a bool telling if the passed IP is a valid IPv4 or IPv6 address. - ''' + """ return is_ipv4(ip_addr) or is_ipv6(ip_addr) def is_ipv4(ip_addr): - ''' + """ Returns a bool telling if the value passed to it was a valid IPv4 address - ''' + """ try: return ipaddress.ip_address(ip_addr).version == 4 except ValueError: @@ -260,9 +299,9 @@ def is_ipv4(ip_addr): def is_ipv6(ip_addr): - ''' + """ Returns a bool telling if the value passed to it was a valid IPv6 address - ''' + """ try: return ipaddress.ip_address(ip_addr).version == 6 except ValueError: @@ -270,38 +309,40 @@ def is_ipv6(ip_addr): def is_subnet(cidr): - ''' + """ Returns a bool telling if the passed string is an IPv4 or IPv6 subnet - ''' + """ return is_ipv4_subnet(cidr) or is_ipv6_subnet(cidr) def is_ipv4_subnet(cidr): - ''' + """ Returns a bool telling if the passed string is an IPv4 subnet - ''' + """ try: - return '/' in cidr and bool(ipaddress.IPv4Network(cidr)) + return "/" in cidr and bool(ipaddress.IPv4Network(cidr)) except Exception: # pylint: disable=broad-except return False def is_ipv6_subnet(cidr): - ''' + """ Returns a bool telling if the passed string is an IPv6 subnet - ''' + """ try: - return '/' in cidr and bool(ipaddress.IPv6Network(cidr)) + return "/" in cidr and bool(ipaddress.IPv6Network(cidr)) except Exception: # pylint: disable=broad-except return False -@jinja_filter('is_ip') +@jinja_filter("is_ip") def is_ip_filter(ip_addr, options=None): - ''' + """ Returns a bool telling if the passed IP is a valid IPv4 or IPv6 address. - ''' - return is_ipv4_filter(ip_addr, options=options) or is_ipv6_filter(ip_addr, options=options) + """ + return is_ipv4_filter(ip_addr, options=options) or is_ipv6_filter( + ip_addr, options=options + ) def _ip_options_global(ip_obj, version): @@ -342,27 +383,27 @@ def _ip_options(ip_obj, version, options=None): # will process and IP options options_fun_map = { - 'global': _ip_options_global, - 'link-local': _ip_options_link_local, - 'linklocal': _ip_options_link_local, - 'll': _ip_options_link_local, - 'link_local': _ip_options_link_local, - 'loopback': _ip_options_loopback, - 'lo': _ip_options_loopback, - 'multicast': _ip_options_multicast, - 'private': _ip_options_private, - 'public': _ip_options_global, - 'reserved': _ip_options_reserved, - 'site-local': _ip_options_site_local, - 'sl': _ip_options_site_local, - 'site_local': _ip_options_site_local, - 'unspecified': _ip_options_unspecified + "global": _ip_options_global, + "link-local": _ip_options_link_local, + "linklocal": _ip_options_link_local, + "ll": _ip_options_link_local, + "link_local": _ip_options_link_local, + "loopback": _ip_options_loopback, + "lo": _ip_options_loopback, + "multicast": _ip_options_multicast, + "private": _ip_options_private, + "public": _ip_options_global, + "reserved": _ip_options_reserved, + "site-local": _ip_options_site_local, + "sl": _ip_options_site_local, + "site_local": _ip_options_site_local, + "unspecified": _ip_options_unspecified, } if not options: return six.text_type(ip_obj) # IP version already checked - options_list = [option.strip() for option in options.split(',')] + options_list = [option.strip() for option in options.split(",")] for option, fun in options_fun_map.items(): if option in options_list: @@ -399,9 +440,9 @@ def _is_ipv(ip_addr, version, options=None): return _ip_options(ip_obj, version, options=options) -@jinja_filter('is_ipv4') +@jinja_filter("is_ipv4") def is_ipv4_filter(ip_addr, options=None): - ''' + """ Returns a bool telling if the value passed to it was a valid IPv4 address. ip @@ -412,14 +453,14 @@ def is_ipv4_filter(ip_addr, options=None): options CSV of options regarding the nature of the IP address. E.g.: loopback, multicast, private etc. - ''' + """ _is_ipv4 = _is_ipv(ip_addr, 4, options=options) return isinstance(_is_ipv4, six.string_types) -@jinja_filter('is_ipv6') +@jinja_filter("is_ipv6") def is_ipv6_filter(ip_addr, options=None): - ''' + """ Returns a bool telling if the value passed to it was a valid IPv6 address. ip @@ -430,7 +471,7 @@ def is_ipv6_filter(ip_addr, options=None): options CSV of options regarding the nature of the IP address. E.g.: loopback, multicast, private etc. - ''' + """ _is_ipv6 = _is_ipv(ip_addr, 6, options=options) return isinstance(_is_ipv6, six.string_types) @@ -441,7 +482,9 @@ def _ipv_filter(value, version, options=None): return if isinstance(value, (six.string_types, six.text_type, six.binary_type)): - return _is_ipv(value, version, options=options) # calls is_ipv4 or is_ipv6 for `value` + return _is_ipv( + value, version, options=options + ) # calls is_ipv4 or is_ipv6 for `value` elif isinstance(value, (list, tuple, types.GeneratorType)): # calls is_ipv4 or is_ipv6 for each element in the list # os it filters and returns only those elements having the desired IP version @@ -453,27 +496,27 @@ def _ipv_filter(value, version, options=None): return None -@jinja_filter('ipv4') +@jinja_filter("ipv4") def ipv4(value, options=None): - ''' + """ Filters a list and returns IPv4 values only. - ''' + """ return _ipv_filter(value, 4, options=options) -@jinja_filter('ipv6') +@jinja_filter("ipv6") def ipv6(value, options=None): - ''' + """ Filters a list and returns IPv6 values only. - ''' + """ return _ipv_filter(value, 6, options=options) -@jinja_filter('ipaddr') +@jinja_filter("ipaddr") def ipaddr(value, options=None): - ''' + """ Filters and returns only valid IP objects. - ''' + """ ipv4_obj = ipv4(value, options=options) ipv6_obj = ipv6(value, options=options) if ipv4_obj is None or ipv6_obj is None: @@ -501,11 +544,11 @@ def _filter_ipaddr(value, options, version=None): return ipaddr_filter_out -@jinja_filter('ip_host') +@jinja_filter("ip_host") def ip_host(value, options=None, version=None): - ''' + """ Returns the interfaces IP address, e.g.: 192.168.0.1/28. - ''' + """ ipaddr_filter_out = _filter_ipaddr(value, options=options, version=version) if not ipaddr_filter_out: return @@ -521,210 +564,205 @@ def _network_hosts(ip_addr_entry): ] -@jinja_filter('network_hosts') +@jinja_filter("network_hosts") def network_hosts(value, options=None, version=None): - ''' + """ Return the list of hosts within a network. .. note:: When running this command with a large IPv6 network, the command will take a long time to gather all of the hosts. - ''' + """ ipaddr_filter_out = _filter_ipaddr(value, options=options, version=version) if not ipaddr_filter_out: return if not isinstance(value, (list, tuple, types.GeneratorType)): return _network_hosts(ipaddr_filter_out[0]) - return [ - _network_hosts(ip_a) - for ip_a in ipaddr_filter_out - ] + return [_network_hosts(ip_a) for ip_a in ipaddr_filter_out] def _network_size(ip_addr_entry): return ipaddress.ip_network(ip_addr_entry, strict=False).num_addresses -@jinja_filter('network_size') +@jinja_filter("network_size") def network_size(value, options=None, version=None): - ''' + """ Get the size of a network. - ''' + """ ipaddr_filter_out = _filter_ipaddr(value, options=options, version=version) if not ipaddr_filter_out: return if not isinstance(value, (list, tuple, types.GeneratorType)): return _network_size(ipaddr_filter_out[0]) - return [ - _network_size(ip_a) - for ip_a in ipaddr_filter_out - ] + return [_network_size(ip_a) for ip_a in ipaddr_filter_out] -def natural_ipv4_netmask(ip_addr, fmt='prefixlen'): - ''' +def natural_ipv4_netmask(ip_addr, fmt="prefixlen"): + """ Returns the "natural" mask of an IPv4 address - ''' + """ bits = _ipv4_to_bits(ip_addr) - if bits.startswith('11'): - mask = '24' - elif bits.startswith('1'): - mask = '16' + if bits.startswith("11"): + mask = "24" + elif bits.startswith("1"): + mask = "16" else: - mask = '8' + mask = "8" - if fmt == 'netmask': + if fmt == "netmask": return cidr_to_ipv4_netmask(mask) else: - return '/' + mask + return "/" + mask def rpad_ipv4_network(ip_addr): - ''' + """ Returns an IP network address padded with zeros. Ex: '192.168.3' -> '192.168.3.0' '10.209' -> '10.209.0.0' - ''' - return '.'.join(itertools.islice(itertools.chain(ip_addr.split('.'), '0000'), 0, - 4)) + """ + return ".".join(itertools.islice(itertools.chain(ip_addr.split("."), "0000"), 0, 4)) def cidr_to_ipv4_netmask(cidr_bits): - ''' + """ Returns an IPv4 netmask - ''' + """ try: cidr_bits = int(cidr_bits) if not 1 <= cidr_bits <= 32: - return '' + return "" except ValueError: - return '' + return "" - netmask = '' + netmask = "" for idx in range(4): if idx: - netmask += '.' + netmask += "." if cidr_bits >= 8: - netmask += '255' + netmask += "255" cidr_bits -= 8 else: - netmask += '{0:d}'.format(256 - (2 ** (8 - cidr_bits))) + netmask += "{0:d}".format(256 - (2 ** (8 - cidr_bits))) cidr_bits = 0 return netmask def _number_of_set_bits_to_ipv4_netmask(set_bits): - ''' + """ Returns an IPv4 netmask from the integer representation of that mask. Ex. 0xffffff00 -> '255.255.255.0' - ''' + """ return cidr_to_ipv4_netmask(_number_of_set_bits(set_bits)) def _number_of_set_bits(x): - ''' + """ Returns the number of bits that are set in a 32bit int - ''' + """ # Taken from http://stackoverflow.com/a/4912729. Many thanks! x -= (x >> 1) & 0x55555555 x = ((x >> 2) & 0x33333333) + (x & 0x33333333) - x = ((x >> 4) + x) & 0x0f0f0f0f + x = ((x >> 4) + x) & 0x0F0F0F0F x += x >> 8 x += x >> 16 - return x & 0x0000003f + return x & 0x0000003F def _interfaces_ip(out): - ''' + """ Uses ip to return a dictionary of interfaces with various information about each (up/down state, ip address, netmask, and hwaddr) - ''' + """ ret = dict() def parse_network(value, cols): - ''' + """ Return a tuple of ip, netmask, broadcast based on the current set of cols - ''' + """ brd = None scope = None - if '/' in value: # we have a CIDR in this address - ip, cidr = value.split('/') + if "/" in value: # we have a CIDR in this address + ip, cidr = value.split("/") else: ip = value cidr = 32 - if type_ == 'inet': + if type_ == "inet": mask = cidr_to_ipv4_netmask(int(cidr)) - if 'brd' in cols: - brd = cols[cols.index('brd') + 1] - elif type_ == 'inet6': + if "brd" in cols: + brd = cols[cols.index("brd") + 1] + elif type_ == "inet6": mask = cidr - if 'scope' in cols: - scope = cols[cols.index('scope') + 1] + if "scope" in cols: + scope = cols[cols.index("scope") + 1] return (ip, mask, brd, scope) - groups = re.compile('\r?\n\\d').split(out) + groups = re.compile("\r?\n\\d").split(out) for group in groups: iface = None data = dict() for line in group.splitlines(): - if ' ' not in line: + if " " not in line: continue - match = re.match(r'^\d*:\s+([\w.\-]+)(?:@)?([\w.\-]+)?:\s+<(.+)>', line) + match = re.match(r"^\d*:\s+([\w.\-]+)(?:@)?([\w.\-]+)?:\s+<(.+)>", line) if match: iface, parent, attrs = match.groups() - if 'UP' in attrs.split(','): - data['up'] = True + if "UP" in attrs.split(","): + data["up"] = True else: - data['up'] = False + data["up"] = False if parent: - data['parent'] = parent + data["parent"] = parent continue cols = line.split() if len(cols) >= 2: type_, value = tuple(cols[0:2]) iflabel = cols[-1:][0] - if type_ in ('inet', 'inet6'): - if 'secondary' not in cols: + if type_ in ("inet", "inet6"): + if "secondary" not in cols: ipaddr, netmask, broadcast, scope = parse_network(value, cols) - if type_ == 'inet': - if 'inet' not in data: - data['inet'] = list() + if type_ == "inet": + if "inet" not in data: + data["inet"] = list() addr_obj = dict() - addr_obj['address'] = ipaddr - addr_obj['netmask'] = netmask - addr_obj['broadcast'] = broadcast - addr_obj['label'] = iflabel - data['inet'].append(addr_obj) - elif type_ == 'inet6': - if 'inet6' not in data: - data['inet6'] = list() + addr_obj["address"] = ipaddr + addr_obj["netmask"] = netmask + addr_obj["broadcast"] = broadcast + addr_obj["label"] = iflabel + data["inet"].append(addr_obj) + elif type_ == "inet6": + if "inet6" not in data: + data["inet6"] = list() addr_obj = dict() - addr_obj['address'] = ipaddr - addr_obj['prefixlen'] = netmask - addr_obj['scope'] = scope - data['inet6'].append(addr_obj) + addr_obj["address"] = ipaddr + addr_obj["prefixlen"] = netmask + addr_obj["scope"] = scope + data["inet6"].append(addr_obj) else: - if 'secondary' not in data: - data['secondary'] = list() + if "secondary" not in data: + data["secondary"] = list() ip_, mask, brd, scp = parse_network(value, cols) - data['secondary'].append({ - 'type': type_, - 'address': ip_, - 'netmask': mask, - 'broadcast': brd, - 'label': iflabel, - }) + data["secondary"].append( + { + "type": type_, + "address": ip_, + "netmask": mask, + "broadcast": brd, + "label": iflabel, + } + ) del ip_, mask, brd, scp - elif type_.startswith('link'): - data['hwaddr'] = value + elif type_.startswith("link"): + data["hwaddr"] = value if iface: ret[iface] = data del iface, data @@ -732,30 +770,32 @@ def _interfaces_ip(out): def _interfaces_ifconfig(out): - ''' + """ Uses ifconfig to return a dictionary of interfaces with various information about each (up/down state, ip address, netmask, and hwaddr) - ''' + """ ret = dict() - piface = re.compile(r'^([^\s:]+)') - pmac = re.compile('.*?(?:HWaddr|ether|address:|lladdr) ([0-9a-fA-F:]+)') + piface = re.compile(r"^([^\s:]+)") + pmac = re.compile(".*?(?:HWaddr|ether|address:|lladdr) ([0-9a-fA-F:]+)") if salt.utils.platform.is_sunos(): - pip = re.compile(r'.*?(?:inet\s+)([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*)') - pip6 = re.compile('.*?(?:inet6 )([0-9a-fA-F:]+)') - pmask6 = re.compile(r'.*?(?:inet6 [0-9a-fA-F:]+/(\d+)).*') + pip = re.compile(r".*?(?:inet\s+)([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)(.*)") + pip6 = re.compile(".*?(?:inet6 )([0-9a-fA-F:]+)") + pmask6 = re.compile(r".*?(?:inet6 [0-9a-fA-F:]+/(\d+)).*") else: - pip = re.compile(r'.*?(?:inet addr:|inet [^\d]*)(.*?)\s') - pip6 = re.compile('.*?(?:inet6 addr: (.*?)/|inet6 )([0-9a-fA-F:]+)') - pmask6 = re.compile(r'.*?(?:inet6 addr: [0-9a-fA-F:]+/(\d+)|prefixlen (\d+))(?: Scope:([a-zA-Z]+)| scopeid (0x[0-9a-fA-F]))?') - pmask = re.compile(r'.*?(?:Mask:|netmask )(?:((?:0x)?[0-9a-fA-F]{8})|([\d\.]+))') - pupdown = re.compile('UP') - pbcast = re.compile(r'.*?(?:Bcast:|broadcast )([\d\.]+)') + pip = re.compile(r".*?(?:inet addr:|inet [^\d]*)(.*?)\s") + pip6 = re.compile(".*?(?:inet6 addr: (.*?)/|inet6 )([0-9a-fA-F:]+)") + pmask6 = re.compile( + r".*?(?:inet6 addr: [0-9a-fA-F:]+/(\d+)|prefixlen (\d+))(?: Scope:([a-zA-Z]+)| scopeid (0x[0-9a-fA-F]))?" + ) + pmask = re.compile(r".*?(?:Mask:|netmask )(?:((?:0x)?[0-9a-fA-F]{8})|([\d\.]+))") + pupdown = re.compile("UP") + pbcast = re.compile(r".*?(?:Bcast:|broadcast )([\d\.]+)") - groups = re.compile('\r?\n(?=\\S)').split(out) + groups = re.compile("\r?\n(?=\\S)").split(out) for group in groups: data = dict() - iface = '' + iface = "" updown = False for line in group.splitlines(): miface = piface.match(line) @@ -766,48 +806,57 @@ def _interfaces_ifconfig(out): if miface: iface = miface.group(1) if mmac: - data['hwaddr'] = mmac.group(1) + data["hwaddr"] = mmac.group(1) if salt.utils.platform.is_sunos(): expand_mac = [] - for chunk in data['hwaddr'].split(':'): - expand_mac.append('0{0}'.format(chunk) if len(chunk) < 2 else '{0}'.format(chunk)) - data['hwaddr'] = ':'.join(expand_mac) + for chunk in data["hwaddr"].split(":"): + expand_mac.append( + "0{0}".format(chunk) + if len(chunk) < 2 + else "{0}".format(chunk) + ) + data["hwaddr"] = ":".join(expand_mac) if mip: - if 'inet' not in data: - data['inet'] = list() + if "inet" not in data: + data["inet"] = list() addr_obj = dict() - addr_obj['address'] = mip.group(1) + addr_obj["address"] = mip.group(1) mmask = pmask.match(line) if mmask: if mmask.group(1): mmask = _number_of_set_bits_to_ipv4_netmask( - int(mmask.group(1), 16)) + int(mmask.group(1), 16) + ) else: mmask = mmask.group(2) - addr_obj['netmask'] = mmask + addr_obj["netmask"] = mmask mbcast = pbcast.match(line) if mbcast: - addr_obj['broadcast'] = mbcast.group(1) - data['inet'].append(addr_obj) + addr_obj["broadcast"] = mbcast.group(1) + data["inet"].append(addr_obj) if mupdown: updown = True if mip6: - if 'inet6' not in data: - data['inet6'] = list() + if "inet6" not in data: + data["inet6"] = list() addr_obj = dict() - addr_obj['address'] = mip6.group(1) or mip6.group(2) + addr_obj["address"] = mip6.group(1) or mip6.group(2) mmask6 = pmask6.match(line) if mmask6: - addr_obj['prefixlen'] = mmask6.group(1) or mmask6.group(2) + addr_obj["prefixlen"] = mmask6.group(1) or mmask6.group(2) if not salt.utils.platform.is_sunos(): ipv6scope = mmask6.group(3) or mmask6.group(4) - addr_obj['scope'] = ipv6scope.lower() if ipv6scope is not None else ipv6scope + addr_obj["scope"] = ( + ipv6scope.lower() if ipv6scope is not None else ipv6scope + ) # SunOS sometimes has ::/0 as inet6 addr when using addrconf - if not salt.utils.platform.is_sunos() \ - or addr_obj['address'] != '::' \ - and addr_obj['prefixlen'] != 0: - data['inet6'].append(addr_obj) - data['up'] = updown + if ( + not salt.utils.platform.is_sunos() + or addr_obj["address"] != "::" + and addr_obj["prefixlen"] != 0 + ): + data["inet6"].append(addr_obj) + data["up"] = updown if iface in ret: # SunOS optimization, where interfaces occur twice in 'ifconfig -a' # output with the same name: for ipv4 and then for ipv6 addr family. @@ -817,10 +866,14 @@ def _interfaces_ifconfig(out): # merge items with higher priority for older values # after that merge the inet and inet6 sub items for both ret[iface] = dict(list(data.items()) + list(ret[iface].items())) - if 'inet' in data: - ret[iface]['inet'].extend(x for x in data['inet'] if x not in ret[iface]['inet']) - if 'inet6' in data: - ret[iface]['inet6'].extend(x for x in data['inet6'] if x not in ret[iface]['inet6']) + if "inet" in data: + ret[iface]["inet"].extend( + x for x in data["inet"] if x not in ret[iface]["inet"] + ) + if "inet6" in data: + ret[iface]["inet6"].extend( + x for x in data["inet6"] if x not in ret[iface]["inet6"] + ) else: ret[iface] = data del data @@ -828,58 +881,63 @@ def _interfaces_ifconfig(out): def linux_interfaces(): - ''' + """ Obtain interface information for *NIX/BSD variants - ''' + """ ifaces = dict() - ip_path = salt.utils.path.which('ip') - ifconfig_path = None if ip_path else salt.utils.path.which('ifconfig') + ip_path = salt.utils.path.which("ip") + ifconfig_path = None if ip_path else salt.utils.path.which("ifconfig") if ip_path: cmd1 = subprocess.Popen( - '{0} link show'.format(ip_path), + "{0} link show".format(ip_path), shell=True, close_fds=True, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT).communicate()[0] + stderr=subprocess.STDOUT, + ).communicate()[0] cmd2 = subprocess.Popen( - '{0} addr show'.format(ip_path), + "{0} addr show".format(ip_path), shell=True, close_fds=True, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT).communicate()[0] - ifaces = _interfaces_ip("{0}\n{1}".format( - salt.utils.stringutils.to_str(cmd1), - salt.utils.stringutils.to_str(cmd2))) + stderr=subprocess.STDOUT, + ).communicate()[0] + ifaces = _interfaces_ip( + "{0}\n{1}".format( + salt.utils.stringutils.to_str(cmd1), salt.utils.stringutils.to_str(cmd2) + ) + ) elif ifconfig_path: cmd = subprocess.Popen( - '{0} -a'.format(ifconfig_path), + "{0} -a".format(ifconfig_path), shell=True, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT).communicate()[0] + stderr=subprocess.STDOUT, + ).communicate()[0] ifaces = _interfaces_ifconfig(salt.utils.stringutils.to_str(cmd)) return ifaces def _netbsd_interfaces_ifconfig(out): - ''' + """ Uses ifconfig to return a dictionary of interfaces with various information about each (up/down state, ip address, netmask, and hwaddr) - ''' + """ ret = dict() - piface = re.compile(r'^([^\s:]+)') - pmac = re.compile('.*?address: ([0-9a-f:]+)') + piface = re.compile(r"^([^\s:]+)") + pmac = re.compile(".*?address: ([0-9a-f:]+)") - pip = re.compile(r'.*?inet [^\d]*(.*?)/([\d]*)\s') - pip6 = re.compile(r'.*?inet6 ([0-9a-f:]+)%([a-zA-Z0-9]*)/([\d]*)\s') + pip = re.compile(r".*?inet [^\d]*(.*?)/([\d]*)\s") + pip6 = re.compile(r".*?inet6 ([0-9a-f:]+)%([a-zA-Z0-9]*)/([\d]*)\s") - pupdown = re.compile('UP') - pbcast = re.compile(r'.*?broadcast ([\d\.]+)') + pupdown = re.compile("UP") + pbcast = re.compile(r".*?broadcast ([\d\.]+)") - groups = re.compile('\r?\n(?=\\S)').split(out) + groups = re.compile("\r?\n(?=\\S)").split(out) for group in groups: data = dict() - iface = '' + iface = "" updown = False for line in group.splitlines(): miface = piface.match(line) @@ -890,116 +948,118 @@ def _netbsd_interfaces_ifconfig(out): if miface: iface = miface.group(1) if mmac: - data['hwaddr'] = mmac.group(1) + data["hwaddr"] = mmac.group(1) if mip: - if 'inet' not in data: - data['inet'] = list() + if "inet" not in data: + data["inet"] = list() addr_obj = dict() - addr_obj['address'] = mip.group(1) + addr_obj["address"] = mip.group(1) mmask = mip.group(2) if mip.group(2): - addr_obj['netmask'] = cidr_to_ipv4_netmask(mip.group(2)) + addr_obj["netmask"] = cidr_to_ipv4_netmask(mip.group(2)) mbcast = pbcast.match(line) if mbcast: - addr_obj['broadcast'] = mbcast.group(1) - data['inet'].append(addr_obj) + addr_obj["broadcast"] = mbcast.group(1) + data["inet"].append(addr_obj) if mupdown: updown = True if mip6: - if 'inet6' not in data: - data['inet6'] = list() + if "inet6" not in data: + data["inet6"] = list() addr_obj = dict() - addr_obj['address'] = mip6.group(1) + addr_obj["address"] = mip6.group(1) mmask6 = mip6.group(3) - addr_obj['scope'] = mip6.group(2) - addr_obj['prefixlen'] = mip6.group(3) - data['inet6'].append(addr_obj) - data['up'] = updown + addr_obj["scope"] = mip6.group(2) + addr_obj["prefixlen"] = mip6.group(3) + data["inet6"].append(addr_obj) + data["up"] = updown ret[iface] = data del data return ret def netbsd_interfaces(): - ''' + """ Obtain interface information for NetBSD >= 8 where the ifconfig output diverged from other BSD variants (Netmask is now part of the address) - ''' + """ # NetBSD versions prior to 8.0 can still use linux_interfaces() - if LooseVersion(os.uname()[2]) < LooseVersion('8.0'): + if LooseVersion(os.uname()[2]) < LooseVersion("8.0"): return linux_interfaces() - ifconfig_path = salt.utils.path.which('ifconfig') + ifconfig_path = salt.utils.path.which("ifconfig") cmd = subprocess.Popen( - '{0} -a'.format(ifconfig_path), + "{0} -a".format(ifconfig_path), shell=True, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT).communicate()[0] + stderr=subprocess.STDOUT, + ).communicate()[0] return _netbsd_interfaces_ifconfig(salt.utils.stringutils.to_str(cmd)) def _interfaces_ipconfig(out): - ''' + """ Returns a dictionary of interfaces with various information about each (up/down state, ip address, netmask, and hwaddr) NOTE: This is not used by any function and may be able to be removed in the future. - ''' + """ ifaces = dict() iface = None addr = None - adapter_iface_regex = re.compile(r'adapter (\S.+):$') + adapter_iface_regex = re.compile(r"adapter (\S.+):$") for line in out.splitlines(): if not line: continue # TODO what does Windows call Infiniband and 10/40gige adapters - if line.startswith('Ethernet'): + if line.startswith("Ethernet"): iface = ifaces[adapter_iface_regex.search(line).group(1)] - iface['up'] = True + iface["up"] = True addr = {} continue if iface: - key, val = line.split(',', 1) - key = key.strip(' .') + key, val = line.split(",", 1) + key = key.strip(" .") val = val.strip() - if addr and key == 'Subnet Mask': - addr['netmask'] = val - elif key in ('IP Address', 'IPv4 Address'): - if 'inet' not in iface: - iface['inet'] = list() - addr = {'address': val.rstrip('(Preferred)'), - 'netmask': None, - 'broadcast': None} # TODO find the broadcast - iface['inet'].append(addr) - elif 'IPv6 Address' in key: - if 'inet6' not in iface: - iface['inet'] = list() + if addr and key == "Subnet Mask": + addr["netmask"] = val + elif key in ("IP Address", "IPv4 Address"): + if "inet" not in iface: + iface["inet"] = list() + addr = { + "address": val.rstrip("(Preferred)"), + "netmask": None, + "broadcast": None, + } # TODO find the broadcast + iface["inet"].append(addr) + elif "IPv6 Address" in key: + if "inet6" not in iface: + iface["inet"] = list() # XXX What is the prefixlen!? - addr = {'address': val.rstrip('(Preferred)'), - 'prefixlen': None} - iface['inet6'].append(addr) - elif key == 'Physical Address': - iface['hwaddr'] = val - elif key == 'Media State': + addr = {"address": val.rstrip("(Preferred)"), "prefixlen": None} + iface["inet6"].append(addr) + elif key == "Physical Address": + iface["hwaddr"] = val + elif key == "Media State": # XXX seen used for tunnel adaptors # might be useful - iface['up'] = (val != 'Media disconnected') + iface["up"] = val != "Media disconnected" def win_interfaces(): - ''' + """ Obtain interface information for Windows systems - ''' + """ return salt.utils.win_network.get_interface_info() def interfaces(): - ''' + """ Return a dictionary of information about all the interfaces on the minion - ''' + """ if salt.utils.platform.is_windows(): return win_interfaces() elif salt.utils.platform.is_netbsd(): @@ -1009,130 +1069,134 @@ def interfaces(): def get_net_start(ipaddr, netmask): - ''' + """ Return the address of the network - ''' - net = ipaddress.ip_network('{0}/{1}'.format(ipaddr, netmask), strict=False) + """ + net = ipaddress.ip_network("{0}/{1}".format(ipaddr, netmask), strict=False) return six.text_type(net.network_address) def get_net_size(mask): - ''' + """ Turns an IPv4 netmask into its corresponding prefix length (255.255.255.0 -> 24 as in 192.168.1.10/24). - ''' - binary_str = '' - for octet in mask.split('.'): + """ + binary_str = "" + for octet in mask.split("."): binary_str += bin(int(octet))[2:].zfill(8) - return len(binary_str.rstrip('0')) + return len(binary_str.rstrip("0")) def calc_net(ipaddr, netmask=None): - ''' + """ Takes IP (CIDR notation supported) and optionally netmask and returns the network in CIDR-notation. (The IP can be any IP inside the subnet) - ''' + """ if netmask is not None: - ipaddr = '{0}/{1}'.format(ipaddr, netmask) + ipaddr = "{0}/{1}".format(ipaddr, netmask) return six.text_type(ipaddress.ip_network(ipaddr, strict=False)) def _ipv4_to_bits(ipaddr): - ''' + """ Accepts an IPv4 dotted quad and returns a string representing its binary counterpart - ''' - return ''.join([bin(int(x))[2:].rjust(8, '0') for x in ipaddr.split('.')]) + """ + return "".join([bin(int(x))[2:].rjust(8, "0") for x in ipaddr.split(".")]) def _get_iface_info(iface): - ''' + """ If `iface` is available, return interface info and no error, otherwise return no info and log and return an error - ''' + """ iface_info = interfaces() if iface in iface_info.keys(): return iface_info, False else: - error_msg = ('Interface "{0}" not in available interfaces: "{1}"' - ''.format(iface, '", "'.join(iface_info.keys()))) + error_msg = 'Interface "{0}" not in available interfaces: "{1}"' "".format( + iface, '", "'.join(iface_info.keys()) + ) log.error(error_msg) return None, error_msg def _hw_addr_aix(iface): - ''' + """ Return the hardware address (a.k.a. MAC address) for a given interface on AIX MAC address not available in through interfaces - ''' + """ cmd = subprocess.Popen( - 'entstat -d {0} | grep \'Hardware Address\''.format(iface), + "entstat -d {0} | grep 'Hardware Address'".format(iface), shell=True, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT).communicate()[0] + stderr=subprocess.STDOUT, + ).communicate()[0] if cmd: - comps = cmd.split(' ') + comps = cmd.split(" ") if len(comps) == 3: - mac_addr = comps[2].strip('\'').strip() + mac_addr = comps[2].strip("'").strip() return mac_addr - error_msg = ('Interface "{0}" either not available or does not contain a hardware address'.format(iface)) + error_msg = 'Interface "{0}" either not available or does not contain a hardware address'.format( + iface + ) log.error(error_msg) return error_msg def hw_addr(iface): - ''' + """ Return the hardware address (a.k.a. MAC address) for a given interface .. versionchanged:: 2016.11.4 Added support for AIX - ''' + """ if salt.utils.platform.is_aix(): return _hw_addr_aix iface_info, error = _get_iface_info(iface) if error is False: - return iface_info.get(iface, {}).get('hwaddr', '') + return iface_info.get(iface, {}).get("hwaddr", "") else: return error def interface(iface): - ''' + """ Return the details of `iface` or an error if it does not exist - ''' + """ iface_info, error = _get_iface_info(iface) if error is False: - return iface_info.get(iface, {}).get('inet', '') + return iface_info.get(iface, {}).get("inet", "") else: return error def interface_ip(iface): - ''' + """ Return `iface` IPv4 addr or an error if `iface` does not exist - ''' + """ iface_info, error = _get_iface_info(iface) if error is False: - inet = iface_info.get(iface, {}).get('inet', None) - return inet[0].get('address', '') if inet else '' + inet = iface_info.get(iface, {}).get("inet", None) + return inet[0].get("address", "") if inet else "" else: return error -def _subnets(proto='inet', interfaces_=None): - ''' +def _subnets(proto="inet", interfaces_=None): + """ Returns a list of subnets to which the host belongs - ''' + """ if interfaces_ is None: ifaces = interfaces() elif isinstance(interfaces_, list): @@ -1145,52 +1209,58 @@ def _subnets(proto='inet', interfaces_=None): ret = set() - if proto == 'inet': - subnet = 'netmask' + if proto == "inet": + subnet = "netmask" dflt_cidr = 32 - elif proto == 'inet6': - subnet = 'prefixlen' + elif proto == "inet6": + subnet = "prefixlen" dflt_cidr = 128 else: - log.error('Invalid proto {0} calling subnets()'.format(proto)) + log.error("Invalid proto {0} calling subnets()".format(proto)) return for ip_info in six.itervalues(ifaces): addrs = ip_info.get(proto, []) - addrs.extend([addr for addr in ip_info.get('secondary', []) if addr.get('type') == proto]) + addrs.extend( + [addr for addr in ip_info.get("secondary", []) if addr.get("type") == proto] + ) for intf in addrs: if subnet in intf: - intf = ipaddress.ip_interface('{0}/{1}'.format(intf['address'], intf[subnet])) + intf = ipaddress.ip_interface( + "{0}/{1}".format(intf["address"], intf[subnet]) + ) else: - intf = ipaddress.ip_interface('{0}/{1}'.format(intf['address'], dflt_cidr)) + intf = ipaddress.ip_interface( + "{0}/{1}".format(intf["address"], dflt_cidr) + ) if not intf.is_loopback: ret.add(intf.network) return [six.text_type(net) for net in sorted(ret)] def subnets(interfaces=None): - ''' + """ Returns a list of IPv4 subnets to which the host belongs - ''' - return _subnets('inet', interfaces_=interfaces) + """ + return _subnets("inet", interfaces_=interfaces) def subnets6(): - ''' + """ Returns a list of IPv6 subnets to which the host belongs - ''' - return _subnets('inet6') + """ + return _subnets("inet6") def in_subnet(cidr, addr=None): - ''' + """ Returns True if host or (any of) addrs is within specified subnet, otherwise False - ''' + """ try: cidr = ipaddress.ip_network(cidr) except ValueError: - log.error('Invalid CIDR \'%s\'', cidr) + log.error("Invalid CIDR '%s'", cidr) return False if addr is None: @@ -1202,63 +1272,66 @@ def in_subnet(cidr, addr=None): return any(ipaddress.ip_address(item) in cidr for item in addr) -def _ip_addrs(interface=None, include_loopback=False, interface_data=None, proto='inet'): - ''' +def _ip_addrs( + interface=None, include_loopback=False, interface_data=None, proto="inet" +): + """ Return the full list of IP adresses matching the criteria proto = inet|inet6 - ''' + """ ret = set() - ifaces = interface_data \ - if isinstance(interface_data, dict) \ - else interfaces() + ifaces = interface_data if isinstance(interface_data, dict) else interfaces() if interface is None: target_ifaces = ifaces else: - target_ifaces = dict([(k, v) for k, v in six.iteritems(ifaces) - if k == interface]) + target_ifaces = dict( + [(k, v) for k, v in six.iteritems(ifaces) if k == interface] + ) if not target_ifaces: - log.error('Interface {0} not found.'.format(interface)) + log.error("Interface {0} not found.".format(interface)) for ip_info in six.itervalues(target_ifaces): addrs = ip_info.get(proto, []) - addrs.extend([addr for addr in ip_info.get('secondary', []) if addr.get('type') == proto]) + addrs.extend( + [addr for addr in ip_info.get("secondary", []) if addr.get("type") == proto] + ) for addr in addrs: - addr = ipaddress.ip_address(addr.get('address')) + addr = ipaddress.ip_address(addr.get("address")) if not addr.is_loopback or include_loopback: ret.add(addr) return [six.text_type(addr) for addr in sorted(ret)] def ip_addrs(interface=None, include_loopback=False, interface_data=None): - ''' + """ Returns a list of IPv4 addresses assigned to the host. 127.0.0.1 is ignored, unless 'include_loopback=True' is indicated. If 'interface' is provided, then only IP addresses from that interface will be returned. - ''' - return _ip_addrs(interface, include_loopback, interface_data, 'inet') + """ + return _ip_addrs(interface, include_loopback, interface_data, "inet") def ip_addrs6(interface=None, include_loopback=False, interface_data=None): - ''' + """ Returns a list of IPv6 addresses assigned to the host. ::1 is ignored, unless 'include_loopback=True' is indicated. If 'interface' is provided, then only IP addresses from that interface will be returned. - ''' - return _ip_addrs(interface, include_loopback, interface_data, 'inet6') + """ + return _ip_addrs(interface, include_loopback, interface_data, "inet6") def hex2ip(hex_ip, invert=False): - ''' + """ Convert a hex string to an ip, if a failure occurs the original hex is returned. If 'invert=True' assume that ip from /proc/net/<proto> - ''' + """ if len(hex_ip) == 32: # ipv6 ip_addr = [] for i in range(0, 32, 8): - ip_part = hex_ip[i:i + 8] - ip_part = [ip_part[x:x + 2] for x in range(0, 8, 2)] + ip_part = hex_ip[i : i + 8] + ip_part = [ip_part[x : x + 2] for x in range(0, 8, 2)] if invert: ip_addr.append("{0[3]}{0[2]}:{0[1]}{0[0]}".format(ip_part)) else: @@ -1270,7 +1343,7 @@ def hex2ip(hex_ip, invert=False): else: return address.compressed except ipaddress.AddressValueError as ex: - log.error('hex2ip - ipv6 address error: {0}'.format(ex)) + log.error("hex2ip - ipv6 address error: {0}".format(ex)) return hex_ip try: @@ -1278,78 +1351,76 @@ def hex2ip(hex_ip, invert=False): except ValueError: return hex_ip if invert: - return '{3}.{2}.{1}.{0}'.format(hip >> 24 & 255, - hip >> 16 & 255, - hip >> 8 & 255, - hip & 255) - return '{0}.{1}.{2}.{3}'.format(hip >> 24 & 255, - hip >> 16 & 255, - hip >> 8 & 255, - hip & 255) + return "{3}.{2}.{1}.{0}".format( + hip >> 24 & 255, hip >> 16 & 255, hip >> 8 & 255, hip & 255 + ) + return "{0}.{1}.{2}.{3}".format( + hip >> 24 & 255, hip >> 16 & 255, hip >> 8 & 255, hip & 255 + ) def mac2eui64(mac, prefix=None): - ''' + """ Convert a MAC address to a EUI64 identifier or, with prefix provided, a full IPv6 address - ''' + """ # http://tools.ietf.org/html/rfc4291#section-2.5.1 - eui64 = re.sub(r'[.:-]', '', mac).lower() - eui64 = eui64[0:6] + 'fffe' + eui64[6:] + eui64 = re.sub(r"[.:-]", "", mac).lower() + eui64 = eui64[0:6] + "fffe" + eui64[6:] eui64 = hex(int(eui64[0:2], 16) | 2)[2:].zfill(2) + eui64[2:] if prefix is None: - return ':'.join(re.findall(r'.{4}', eui64)) + return ":".join(re.findall(r".{4}", eui64)) else: try: net = ipaddress.ip_network(prefix, strict=False) - euil = int('0x{0}'.format(eui64), 16) - return '{0}/{1}'.format(net[euil], net.prefixlen) + euil = int("0x{0}".format(eui64), 16) + return "{0}/{1}".format(net[euil], net.prefixlen) except Exception: # pylint: disable=broad-except return def active_tcp(): - ''' + """ Return a dict describing all active tcp connections as quickly as possible - ''' + """ ret = {} - for statf in ['/proc/net/tcp', '/proc/net/tcp6']: + for statf in ["/proc/net/tcp", "/proc/net/tcp6"]: if not os.path.isfile(statf): continue - with salt.utils.files.fopen(statf, 'rb') as fp_: + with salt.utils.files.fopen(statf, "rb") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.strip().startswith('sl'): + if line.strip().startswith("sl"): continue iret = _parse_tcp_line(line) slot = next(iter(iret)) - if iret[slot]['state'] == 1: # 1 is ESTABLISHED - del iret[slot]['state'] + if iret[slot]["state"] == 1: # 1 is ESTABLISHED + del iret[slot]["state"] ret[len(ret)] = iret[slot] return ret def local_port_tcp(port): - ''' + """ Return a set of remote ip addrs attached to the specified local port - ''' - ret = _remotes_on(port, 'local_port') + """ + ret = _remotes_on(port, "local_port") return ret def remote_port_tcp(port): - ''' + """ Return a set of ip addrs the current host is connected to on given port - ''' - ret = _remotes_on(port, 'remote_port') + """ + ret = _remotes_on(port, "remote_port") return ret def _remotes_on(port, which_end): - ''' + """ Return a set of ip addrs active tcp connections - ''' + """ port = int(port) ret = _netlink_tool_remote_on(port, which_end) @@ -1358,19 +1429,21 @@ def _remotes_on(port, which_end): ret = set() proc_available = False - for statf in ['/proc/net/tcp', '/proc/net/tcp6']: + for statf in ["/proc/net/tcp", "/proc/net/tcp6"]: if not os.path.isfile(statf): continue proc_available = True - with salt.utils.files.fopen(statf, 'r') as fp_: + with salt.utils.files.fopen(statf, "r") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line) - if line.strip().startswith('sl'): + if line.strip().startswith("sl"): continue iret = _parse_tcp_line(line) slot = next(iter(iret)) - if iret[slot][which_end] == port and iret[slot]['state'] == 1: # 1 is ESTABLISHED - ret.add(iret[slot]['remote_addr']) + if ( + iret[slot][which_end] == port and iret[slot]["state"] == 1 + ): # 1 is ESTABLISHED + ret.add(iret[slot]["remote_addr"]) if not proc_available: # Fallback to use OS specific tools if salt.utils.platform.is_sunos(): @@ -1392,25 +1465,25 @@ def _remotes_on(port, which_end): def _parse_tcp_line(line): - ''' + """ Parse a single line from the contents of /proc/net/tcp or /proc/net/tcp6 - ''' + """ ret = {} comps = line.strip().split() - slot = comps[0].rstrip(':') + slot = comps[0].rstrip(":") ret[slot] = {} - l_addr, l_port = comps[1].split(':') - r_addr, r_port = comps[2].split(':') - ret[slot]['local_addr'] = hex2ip(l_addr, True) - ret[slot]['local_port'] = int(l_port, 16) - ret[slot]['remote_addr'] = hex2ip(r_addr, True) - ret[slot]['remote_port'] = int(r_port, 16) - ret[slot]['state'] = int(comps[3], 16) + l_addr, l_port = comps[1].split(":") + r_addr, r_port = comps[2].split(":") + ret[slot]["local_addr"] = hex2ip(l_addr, True) + ret[slot]["local_port"] = int(l_port, 16) + ret[slot]["remote_addr"] = hex2ip(r_addr, True) + ret[slot]["remote_port"] = int(r_port, 16) + ret[slot]["state"] = int(comps[3], 16) return ret def _netlink_tool_remote_on(port, which_end): - ''' + """ Returns set of IPv4/IPv6 host addresses of remote established connections on local or remote tcp port. @@ -1422,27 +1495,29 @@ def _netlink_tool_remote_on(port, which_end): LISTEN 0 128 *:22 *:* ESTAB 0 0 127.0.0.1:56726 127.0.0.1:4505 ESTAB 0 0 [::ffff:127.0.0.1]:41323 [::ffff:127.0.0.1]:4505 - ''' + """ remotes = set() valid = False - tcp_end = 'dst' if which_end == 'remote_port' else 'src' + tcp_end = "dst" if which_end == "remote_port" else "src" try: - data = subprocess.check_output(['ss', '-ant', tcp_end, ':{0}'.format(port)]) # pylint: disable=minimum-python-version + data = subprocess.check_output( + ["ss", "-ant", tcp_end, ":{0}".format(port)] + ) # pylint: disable=minimum-python-version except subprocess.CalledProcessError: - log.error('Failed ss') + log.error("Failed ss") raise - except OSError: # not command "No such file or directory" + except OSError: # not command "No such file or directory" return None - lines = salt.utils.stringutils.to_str(data).split('\n') + lines = salt.utils.stringutils.to_str(data).split("\n") for line in lines: - if 'Address:Port' in line: # ss tools may not be valid + if "Address:Port" in line: # ss tools may not be valid valid = True continue - elif 'ESTAB' not in line: + elif "ESTAB" not in line: continue chunks = line.split() - remote_host, remote_port = chunks[4].rsplit(':', 1) + remote_host, remote_port = chunks[4].rsplit(":", 1) remotes.add(remote_host.strip("[]")) @@ -1452,7 +1527,7 @@ def _netlink_tool_remote_on(port, which_end): def _sunos_remotes_on(port, which_end): - ''' + """ SunOS specific helper function. Returns set of ipv4 host addresses of remote established connections on local or remote tcp port. @@ -1465,32 +1540,34 @@ def _sunos_remotes_on(port, which_end): -------------------- -------------------- ----- ------ ----- ------ ----------- 10.0.0.101.4505 10.0.0.1.45329 1064800 0 1055864 0 ESTABLISHED 10.0.0.101.4505 10.0.0.100.50798 1064800 0 1055864 0 ESTABLISHED - ''' + """ remotes = set() try: - data = subprocess.check_output(['netstat', '-f', 'inet', '-n']) # pylint: disable=minimum-python-version + data = subprocess.check_output( + ["netstat", "-f", "inet", "-n"] + ) # pylint: disable=minimum-python-version except subprocess.CalledProcessError: - log.error('Failed netstat') + log.error("Failed netstat") raise - lines = salt.utils.stringutils.to_str(data).split('\n') + lines = salt.utils.stringutils.to_str(data).split("\n") for line in lines: - if 'ESTABLISHED' not in line: + if "ESTABLISHED" not in line: continue chunks = line.split() - local_host, local_port = chunks[0].rsplit('.', 1) - remote_host, remote_port = chunks[1].rsplit('.', 1) + local_host, local_port = chunks[0].rsplit(".", 1) + remote_host, remote_port = chunks[1].rsplit(".", 1) - if which_end == 'remote_port' and int(remote_port) != port: + if which_end == "remote_port" and int(remote_port) != port: continue - if which_end == 'local_port' and int(local_port) != port: + if which_end == "local_port" and int(local_port) != port: continue remotes.add(remote_host) return remotes def _freebsd_remotes_on(port, which_end): - ''' + """ Returns set of ipv4 host addresses of remote established connections on local tcp port port. @@ -1507,19 +1584,19 @@ def _freebsd_remotes_on(port, which_end): $ sudo sockstat -4 -c -p 4506 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS root python2.7 1294 41 tcp4 127.0.0.1:61115 127.0.0.1:4506 - ''' + """ port = int(port) remotes = set() try: - cmd = salt.utils.args.shlex_split('sockstat -4 -c -p {0}'.format(port)) + cmd = salt.utils.args.shlex_split("sockstat -4 -c -p {0}".format(port)) data = subprocess.check_output(cmd) # pylint: disable=minimum-python-version except subprocess.CalledProcessError as ex: log.error('Failed "sockstat" with returncode = {0}'.format(ex.returncode)) raise - lines = salt.utils.stringutils.to_str(data).split('\n') + lines = salt.utils.stringutils.to_str(data).split("\n") for line in lines: chunks = line.split() @@ -1528,7 +1605,7 @@ def _freebsd_remotes_on(port, which_end): # ['root', 'python2.7', '1456', '37', 'tcp4', # '127.0.0.1:4505-', '127.0.0.1:55703'] # print chunks - if 'COMMAND' in chunks[1]: + if "COMMAND" in chunks[1]: continue # ignore header if len(chunks) < 2: continue @@ -1537,11 +1614,13 @@ def _freebsd_remotes_on(port, which_end): # salt-master python2.781106 35 tcp4 192.168.12.34:4506 192.168.12.45:60143 local = chunks[-2] remote = chunks[-1] - lhost, lport = local.split(':') - rhost, rport = remote.split(':') - if which_end == 'local' and int(lport) != port: # ignore if local port not port + lhost, lport = local.split(":") + rhost, rport = remote.split(":") + if which_end == "local" and int(lport) != port: # ignore if local port not port continue - if which_end == 'remote' and int(rport) != port: # ignore if remote port not port + if ( + which_end == "remote" and int(rport) != port + ): # ignore if remote port not port continue remotes.add(rhost) @@ -1550,7 +1629,7 @@ def _freebsd_remotes_on(port, which_end): def _netbsd_remotes_on(port, which_end): - ''' + """ Returns set of ipv4 host addresses of remote established connections on local tcp port port. @@ -1567,19 +1646,19 @@ def _netbsd_remotes_on(port, which_end): $ sudo sockstat -4 -c -n -p 4506 USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS root python2.7 1294 41 tcp 127.0.0.1.61115 127.0.0.1.4506 - ''' + """ port = int(port) remotes = set() try: - cmd = salt.utils.args.shlex_split('sockstat -4 -c -n -p {0}'.format(port)) + cmd = salt.utils.args.shlex_split("sockstat -4 -c -n -p {0}".format(port)) data = subprocess.check_output(cmd) # pylint: disable=minimum-python-version except subprocess.CalledProcessError as ex: log.error('Failed "sockstat" with returncode = {0}'.format(ex.returncode)) raise - lines = salt.utils.stringutils.to_str(data).split('\n') + lines = salt.utils.stringutils.to_str(data).split("\n") for line in lines: chunks = line.split() @@ -1588,19 +1667,21 @@ def _netbsd_remotes_on(port, which_end): # ['root', 'python2.7', '1456', '37', 'tcp', # '127.0.0.1.4505-', '127.0.0.1.55703'] # print chunks - if 'COMMAND' in chunks[1]: + if "COMMAND" in chunks[1]: continue # ignore header if len(chunks) < 2: continue - local = chunks[5].split('.') + local = chunks[5].split(".") lport = local.pop() - lhost = '.'.join(local) - remote = chunks[6].split('.') + lhost = ".".join(local) + remote = chunks[6].split(".") rport = remote.pop() - rhost = '.'.join(remote) - if which_end == 'local' and int(lport) != port: # ignore if local port not port + rhost = ".".join(remote) + if which_end == "local" and int(lport) != port: # ignore if local port not port continue - if which_end == 'remote' and int(rport) != port: # ignore if remote port not port + if ( + which_end == "remote" and int(rport) != port + ): # ignore if remote port not port continue remotes.add(rhost) @@ -1609,7 +1690,7 @@ def _netbsd_remotes_on(port, which_end): def _openbsd_remotes_on(port, which_end): - ''' + """ OpenBSD specific helper function. Returns set of ipv4 host addresses of remote established connections on local or remote tcp port. @@ -1621,32 +1702,34 @@ def _openbsd_remotes_on(port, which_end): Proto Recv-Q Send-Q Local Address Foreign Address (state) tcp 0 0 10.0.0.101.4505 10.0.0.1.45329 ESTABLISHED tcp 0 0 10.0.0.101.4505 10.0.0.100.50798 ESTABLISHED - ''' + """ remotes = set() try: - data = subprocess.check_output(['netstat', '-nf', 'inet']) # pylint: disable=minimum-python-version + data = subprocess.check_output( + ["netstat", "-nf", "inet"] + ) # pylint: disable=minimum-python-version except subprocess.CalledProcessError: - log.error('Failed netstat') + log.error("Failed netstat") raise - lines = data.split('\n') + lines = data.split("\n") for line in lines: - if 'ESTABLISHED' not in line: + if "ESTABLISHED" not in line: continue chunks = line.split() - local_host, local_port = chunks[3].rsplit('.', 1) - remote_host, remote_port = chunks[4].rsplit('.', 1) + local_host, local_port = chunks[3].rsplit(".", 1) + remote_host, remote_port = chunks[4].rsplit(".", 1) - if which_end == 'remote_port' and int(remote_port) != port: + if which_end == "remote_port" and int(remote_port) != port: continue - if which_end == 'local_port' and int(local_port) != port: + if which_end == "local_port" and int(local_port) != port: continue remotes.add(remote_host) return remotes def _windows_remotes_on(port, which_end): - r''' + r""" Windows specific helper function. Returns set of ipv4 host addresses of remote established connections on local or remote tcp port. @@ -1660,31 +1743,33 @@ def _windows_remotes_on(port, which_end): Proto Local Address Foreign Address State TCP 10.2.33.17:3007 130.164.12.233:10123 ESTABLISHED TCP 10.2.33.17:3389 130.164.30.5:10378 ESTABLISHED - ''' + """ remotes = set() try: - data = subprocess.check_output(['netstat', '-n']) # pylint: disable=minimum-python-version + data = subprocess.check_output( + ["netstat", "-n"] + ) # pylint: disable=minimum-python-version except subprocess.CalledProcessError: - log.error('Failed netstat') + log.error("Failed netstat") raise - lines = salt.utils.stringutils.to_str(data).split('\n') + lines = salt.utils.stringutils.to_str(data).split("\n") for line in lines: - if 'ESTABLISHED' not in line: + if "ESTABLISHED" not in line: continue chunks = line.split() - local_host, local_port = chunks[1].rsplit(':', 1) - remote_host, remote_port = chunks[2].rsplit(':', 1) - if which_end == 'remote_port' and int(remote_port) != port: + local_host, local_port = chunks[1].rsplit(":", 1) + remote_host, remote_port = chunks[2].rsplit(":", 1) + if which_end == "remote_port" and int(remote_port) != port: continue - if which_end == 'local_port' and int(local_port) != port: + if which_end == "local_port" and int(local_port) != port: continue remotes.add(remote_host) return remotes def _linux_remotes_on(port, which_end): - ''' + """ Linux specific helper function. Returns set of ip host addresses of remote established connections on local tcp port port. @@ -1699,12 +1784,17 @@ def _linux_remotes_on(port, which_end): Python 10152 root 22u IPv4 0x18a8464a29c8cab5 0t0 TCP 127.0.0.1:55703->127.0.0.1:4505 (ESTABLISHED) Python 10153 root 22u IPv4 0x18a8464a29c8cab5 0t0 TCP [fe80::249a]:4505->[fe80::150]:59367 (ESTABLISHED) - ''' + """ remotes = set() try: data = subprocess.check_output( - ['lsof', '-iTCP:{0:d}'.format(port), '-n', '-P'] # pylint: disable=minimum-python-version + [ + "lsof", + "-iTCP:{0:d}".format(port), + "-n", + "-P", + ] # pylint: disable=minimum-python-version ) except subprocess.CalledProcessError as ex: if ex.returncode == 1: @@ -1715,7 +1805,7 @@ def _linux_remotes_on(port, which_end): log.error('Failed "lsof" with returncode = {0}'.format(ex.returncode)) raise - lines = salt.utils.stringutils.to_str(data).split('\n') + lines = salt.utils.stringutils.to_str(data).split("\n") for line in lines: chunks = line.split() if not chunks: @@ -1723,17 +1813,17 @@ def _linux_remotes_on(port, which_end): # ['Python', '9971', 'root', '37u', 'IPv4', '0x18a8464a29b2b29d', '0t0', # 'TCP', '127.0.0.1:4505->127.0.0.1:55703', '(ESTABLISHED)'] # print chunks - if 'COMMAND' in chunks[0]: + if "COMMAND" in chunks[0]: continue # ignore header - if 'ESTABLISHED' not in chunks[-1]: + if "ESTABLISHED" not in chunks[-1]: continue # ignore if not ESTABLISHED # '127.0.0.1:4505->127.0.0.1:55703' - local, remote = chunks[8].split('->') - _, lport = local.rsplit(':', 1) - rhost, rport = remote.rsplit(':', 1) - if which_end == 'remote_port' and int(rport) != port: + local, remote = chunks[8].split("->") + _, lport = local.rsplit(":", 1) + rhost, rport = remote.rsplit(":", 1) + if which_end == "remote_port" and int(rport) != port: continue - if which_end == 'local_port' and int(lport) != port: + if which_end == "local_port" and int(lport) != port: continue remotes.add(rhost.strip("[]")) @@ -1741,7 +1831,7 @@ def _linux_remotes_on(port, which_end): def _aix_remotes_on(port, which_end): - ''' + """ AIX specific helper function. Returns set of ipv4 host addresses of remote established connections on local or remote tcp port. @@ -1766,33 +1856,35 @@ def _aix_remotes_on(port, which_end): tcp 0 0 127.0.0.1.32777 127.0.0.1.32771 ESTABLISHED tcp4 0 0 127.0.0.1.32771 127.0.0.1.32778 ESTABLISHED tcp 0 0 127.0.0.1.32778 127.0.0.1.32771 ESTABLISHED - ''' + """ remotes = set() try: - data = subprocess.check_output(['netstat', '-f', 'inet', '-n']) # pylint: disable=minimum-python-version + data = subprocess.check_output( + ["netstat", "-f", "inet", "-n"] + ) # pylint: disable=minimum-python-version except subprocess.CalledProcessError: - log.error('Failed netstat') + log.error("Failed netstat") raise - lines = salt.utils.stringutils.to_str(data).split('\n') + lines = salt.utils.stringutils.to_str(data).split("\n") for line in lines: - if 'ESTABLISHED' not in line: + if "ESTABLISHED" not in line: continue chunks = line.split() - local_host, local_port = chunks[3].rsplit('.', 1) - remote_host, remote_port = chunks[4].rsplit('.', 1) + local_host, local_port = chunks[3].rsplit(".", 1) + remote_host, remote_port = chunks[4].rsplit(".", 1) - if which_end == 'remote_port' and int(remote_port) != port: + if which_end == "remote_port" and int(remote_port) != port: continue - if which_end == 'local_port' and int(local_port) != port: + if which_end == "local_port" and int(local_port) != port: continue remotes.add(remote_host) return remotes -@jinja_filter('gen_mac') -def gen_mac(prefix='AC:DE:48'): - ''' +@jinja_filter("gen_mac") +def gen_mac(prefix="AC:DE:48"): + """ Generates a MAC address with the defined OUI prefix. Common prefixes: @@ -1808,38 +1900,40 @@ def gen_mac(prefix='AC:DE:48'): - http://standards.ieee.org/develop/regauth/oui/oui.txt - https://www.wireshark.org/tools/oui-lookup.html - https://en.wikipedia.org/wiki/MAC_address - ''' - return '{0}:{1:02X}:{2:02X}:{3:02X}'.format(prefix, - random.randint(0, 0xff), - random.randint(0, 0xff), - random.randint(0, 0xff)) + """ + return "{0}:{1:02X}:{2:02X}:{3:02X}".format( + prefix, + random.randint(0, 0xFF), + random.randint(0, 0xFF), + random.randint(0, 0xFF), + ) -@jinja_filter('mac_str_to_bytes') +@jinja_filter("mac_str_to_bytes") def mac_str_to_bytes(mac_str): - ''' + """ Convert a MAC address string into bytes. Works with or without separators: b1 = mac_str_to_bytes('08:00:27:13:69:77') b2 = mac_str_to_bytes('080027136977') assert b1 == b2 assert isinstance(b1, bytes) - ''' + """ if len(mac_str) == 12: pass elif len(mac_str) == 17: sep = mac_str[2] - mac_str = mac_str.replace(sep, '') + mac_str = mac_str.replace(sep, "") else: - raise ValueError('Invalid MAC address') - chars = (int(mac_str[s:s+2], 16) for s in range(0, 12, 2)) - return bytes(chars) if six.PY3 else b''.join(chr(x) for x in chars) + raise ValueError("Invalid MAC address") + chars = (int(mac_str[s : s + 2], 16) for s in range(0, 12, 2)) + return bytes(chars) if six.PY3 else b"".join(chr(x) for x in chars) def refresh_dns(): - ''' + """ issue #21397: force glibc to re-read resolv.conf - ''' + """ try: RES_INIT() except NameError: @@ -1847,29 +1941,37 @@ def refresh_dns(): pass -@jinja_filter('dns_check') +@jinja_filter("dns_check") def dns_check(addr, port, safe=False, ipv6=None): - ''' + """ Return an ip address resolved by dns in a format usable in URLs (ipv6 in brackets). Obeys system preference for IPv4/6 address resolution - this can be overridden by the ipv6 flag. Tries to connect to the address before considering it useful. If no address can be reached, the first one resolved is used as a fallback. Does not exit on failure, raises an exception. - ''' + """ ip_addrs = [] - family = socket.AF_INET6 if ipv6 else socket.AF_INET if ipv6 is False else socket.AF_UNSPEC + family = ( + socket.AF_INET6 + if ipv6 + else socket.AF_INET + if ipv6 is False + else socket.AF_UNSPEC + ) try: refresh_dns() addrinfo = socket.getaddrinfo(addr, port, family, socket.SOCK_STREAM) ip_addrs = _test_addrs(addrinfo, port) except TypeError: - err = ('Attempt to resolve address \'{0}\' failed. Invalid or unresolveable address').format(addr) + err = ( + "Attempt to resolve address '{0}' failed. Invalid or unresolveable address" + ).format(addr) raise SaltSystemExit(code=42, msg=err) except socket.error: pass if not ip_addrs: - err = ('DNS lookup or connection check of \'{0}\' failed.').format(addr) + err = ("DNS lookup or connection check of '{0}' failed.").format(addr) if safe: if salt.log.is_console_configured(): # If logging is not configured it also means that either @@ -1883,10 +1985,10 @@ def dns_check(addr, port, safe=False, ipv6=None): def _test_addrs(addrinfo, port): - ''' + """ Attempt to connect to all addresses, return one if it succeeds. Otherwise, return all addrs. - ''' + """ ip_addrs = [] # test for connectivity, short circuit on success for a in addrinfo: @@ -1936,14 +2038,18 @@ def parse_host_port(host_port): port = int(_s_.lstrip(":")) else: if len(_s_) > 1: - raise ValueError('found ambiguous "{}" port in "{}"'.format(_s_, host_port)) + raise ValueError( + 'found ambiguous "{}" port in "{}"'.format(_s_, host_port) + ) else: if _s_.count(":") == 1: host, _hostport_separator_, port = _s_.partition(":") try: port = int(port) except ValueError as _e_: - log.error('host_port "%s" port value "%s" is not an integer.', host_port, port) + log.error( + 'host_port "%s" port value "%s" is not an integer.', host_port, port + ) raise _e_ else: host = _s_ @@ -1958,3 +2064,34 @@ def parse_host_port(host_port): raise ValueError('bad hostname: "{}"'.format(host)) return host, port + + +@jinja_filter("filter_by_networks") +def filter_by_networks(values, networks): + """ + Returns the list of IPs filtered by the network list. + If the network list is an empty sequence, no IPs are returned. + If the network list is None, all IPs are returned. + + {% set networks = ['192.168.0.0/24', 'fe80::/64'] %} + {{ grains['ip_interfaces'] | filter_by_networks(networks) }} + {{ grains['ipv6'] | filter_by_networks(networks) }} + {{ grains['ipv4'] | filter_by_networks(networks) }} + """ + + _filter = lambda ips, networks: [ + ip for ip in ips for net in networks if ipaddress.ip_address(ip) in net + ] + + if networks is not None: + networks = [ipaddress.ip_network(network) for network in networks] + if isinstance(values, collections.Mapping): + return { + interface: _filter(values[interface], networks) for interface in values + } + elif isinstance(values, collections.Sequence): + return _filter(values, networks) + else: + raise ValueError("Do not know how to filter a {}".format(type(values))) + else: + return values diff --git a/salt/utils/nxos_api.py b/salt/utils/nxos_api.py index 4926890ad9f..93f5a3ee05b 100644 --- a/salt/utils/nxos_api.py +++ b/salt/utils/nxos_api.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Util functions for the NXOS API modules. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python std lib @@ -10,57 +10,55 @@ import logging # Import salt libs import salt.utils.http -from salt.ext import six from salt.exceptions import SaltException +from salt.ext import six from salt.utils.args import clean_kwargs log = logging.getLogger(__name__) RPC_INIT_KWARGS = [ - 'transport', - 'host', - 'username', - 'password', - 'port', - 'timeout', - 'verify', - 'rpc_version' + "transport", + "host", + "username", + "password", + "port", + "timeout", + "verify", + "rpc_version", ] def _prepare_connection(**nxos_api_kwargs): - ''' + """ Prepare the connection with the remote network device, and clean up the key value pairs, removing the args used for the connection init. - ''' + """ nxos_api_kwargs = clean_kwargs(**nxos_api_kwargs) init_kwargs = {} # Clean up any arguments that are not required for karg, warg in six.iteritems(nxos_api_kwargs): if karg in RPC_INIT_KWARGS: init_kwargs[karg] = warg - if 'host' not in init_kwargs: - init_kwargs['host'] = 'localhost' - if 'transport' not in init_kwargs: - init_kwargs['transport'] = 'https' - if 'port' not in init_kwargs: - init_kwargs['port'] = 80 if init_kwargs['transport'] == 'http' else 443 - verify = init_kwargs.get('verify', True) + if "host" not in init_kwargs: + init_kwargs["host"] = "localhost" + if "transport" not in init_kwargs: + init_kwargs["transport"] = "https" + if "port" not in init_kwargs: + init_kwargs["port"] = 80 if init_kwargs["transport"] == "http" else 443 + verify = init_kwargs.get("verify", True) if isinstance(verify, bool): - init_kwargs['verify_ssl'] = verify + init_kwargs["verify_ssl"] = verify else: - init_kwargs['ca_bundle'] = verify - if 'rpc_version' not in init_kwargs: - init_kwargs['rpc_version'] = '2.0' - if 'timeout' not in init_kwargs: - init_kwargs['timeout'] = 60 + init_kwargs["ca_bundle"] = verify + if "rpc_version" not in init_kwargs: + init_kwargs["rpc_version"] = "2.0" + if "timeout" not in init_kwargs: + init_kwargs["timeout"] = 60 return init_kwargs -def rpc(commands, - method='cli', - **kwargs): - ''' +def rpc(commands, method="cli", **kwargs): + """ Execute an arbitrary RPC request via the Nexus API. commands @@ -95,47 +93,42 @@ def rpc(commands, Either a boolean, in which case it controls whether we verify the NX-API TLS certificate, or a string, in which case it must be a path to a CA bundle to use. Defaults to ``True``. - ''' + """ init_args = _prepare_connection(**kwargs) - log.error('These are the init args:') + log.error("These are the init args:") log.error(init_args) - url = '{transport}://{host}:{port}/ins'.format( - transport=init_args['transport'], - host=init_args['host'], - port=init_args['port'] + url = "{transport}://{host}:{port}/ins".format( + transport=init_args["transport"], host=init_args["host"], port=init_args["port"] ) - headers = { - 'content-type': 'application/json-rpc' - } + headers = {"content-type": "application/json-rpc"} payload = [] if not isinstance(commands, (list, tuple)): commands = [commands] for index, command in enumerate(commands): - payload.append({ - 'jsonrpc': init_args['rpc_version'], - 'method': method, - 'params': { - 'cmd': command, - 'version': 1 - }, - 'id': index + 1 - }) - opts = { - 'http_request_timeout': init_args['timeout'] - } - response = salt.utils.http.query(url, - method='POST', - opts=opts, - data=json.dumps(payload), - header_dict=headers, - decode=True, - decode_type='json', - **init_args) - if 'error' in response: - raise SaltException(response['error']) - response_list = response['dict'] + payload.append( + { + "jsonrpc": init_args["rpc_version"], + "method": method, + "params": {"cmd": command, "version": 1}, + "id": index + 1, + } + ) + opts = {"http_request_timeout": init_args["timeout"]} + response = salt.utils.http.query( + url, + method="POST", + opts=opts, + data=json.dumps(payload), + header_dict=headers, + decode=True, + decode_type="json", + **init_args + ) + if "error" in response: + raise SaltException(response["error"]) + response_list = response["dict"] if isinstance(response_list, dict): response_list = [response_list] for index, command in enumerate(commands): - response_list[index]['command'] = command + response_list[index]["command"] = command return response_list diff --git a/salt/utils/odict.py b/salt/utils/odict.py index ac27b78f5be..52816a529ab 100644 --- a/salt/utils/odict.py +++ b/salt/utils/odict.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -18,30 +18,38 @@ It's source was submitted here:: http://stackoverflow.com/questions/6190331/ -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -try: - from collections.abc import Callable -except ImportError: - from collections import Callable +from __future__ import absolute_import, print_function, unicode_literals # Import 3rd-party libs from salt.ext import six +try: + from collections.abc import Callable +except ImportError: + # pylint: disable=no-name-in-module + from collections import Callable + + # pylint: enable=no-name-in-module + + try: # pylint: disable=E0611,minimum-python-version import collections class OrderedDict(collections.OrderedDict): __hash__ = None + + except (ImportError, AttributeError): try: import ordereddict class OrderedDict(ordereddict.OrderedDict): # pylint: disable=W0232 __hash_ = None + except ImportError: # {{{ http://code.activestate.com/recipes/576693/ (r9) # Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy. @@ -54,13 +62,13 @@ except (ImportError, AttributeError): from salt.ext.six.moves._dummy_thread import get_ident as _get_ident # pylint: enable=import-error,no-name-in-module -# try: -# from _abcoll import KeysView, ValuesView, ItemsView -# except ImportError: -# pass + # try: + # from _abcoll import KeysView, ValuesView, ItemsView + # except ImportError: + # pass class OrderedDict(dict): - 'Dictionary that remembers insertion order' + "Dictionary that remembers insertion order" # An inherited dict maps keys to values. # The inherited dict provides __getitem__, __len__, __contains__, and get. # The remaining methods are order-aware. @@ -73,26 +81,26 @@ except (ImportError, AttributeError): __hash_ = None def __init__(self, *args, **kwds): # pylint: disable=E1003 - '''Initialize an ordered dictionary. Signature is the same as for + """Initialize an ordered dictionary. Signature is the same as for regular dictionaries, but keyword arguments are not recommended because their insertion order is arbitrary. - ''' + """ super(OrderedDict, self).__init__() # pylint: disable=E1003 if len(args) > 1: raise TypeError( - 'expected at most 1 arguments, got {0}'.format(len(args)) + "expected at most 1 arguments, got {0}".format(len(args)) ) try: self.__root except AttributeError: - self.__root = root = [] # sentinel node + self.__root = root = [] # sentinel node root[:] = [root, root, None] self.__map = {} self.__update(*args, **kwds) def __setitem__(self, key, value, dict_setitem=dict.__setitem__): - 'od.__setitem__(i, y) <==> od[i]=y' + "od.__setitem__(i, y) <==> od[i]=y" # Setting a new item creates a new link which goes at the end of the linked # list, and the inherited dictionary is updated with the new key/value pair. if key not in self: @@ -102,7 +110,7 @@ except (ImportError, AttributeError): dict_setitem(self, key, value) def __delitem__(self, key, dict_delitem=dict.__delitem__): - 'od.__delitem__(y) <==> del od[y]' + "od.__delitem__(y) <==> del od[y]" # Deleting an existing item uses self.__map to find the link which is # then removed by updating the links in the predecessor and successor nodes. dict_delitem(self, key) @@ -111,7 +119,7 @@ except (ImportError, AttributeError): link_next[0] = link_prev def __iter__(self): - 'od.__iter__() <==> iter(od)' + "od.__iter__() <==> iter(od)" root = self.__root curr = root[1] while curr is not root: @@ -119,7 +127,7 @@ except (ImportError, AttributeError): curr = curr[1] def __reversed__(self): - 'od.__reversed__() <==> reversed(od)' + "od.__reversed__() <==> reversed(od)" root = self.__root curr = root[0] while curr is not root: @@ -127,7 +135,7 @@ except (ImportError, AttributeError): curr = curr[0] def clear(self): - 'od.clear() -> None. Remove all items from od.' + "od.clear() -> None. Remove all items from od." try: for node in six.itervalues(self.__map): del node[:] @@ -139,12 +147,12 @@ except (ImportError, AttributeError): dict.clear(self) def popitem(self, last=True): - '''od.popitem() -> (k, v), return and remove a (key, value) pair. + """od.popitem() -> (k, v), return and remove a (key, value) pair. Pairs are returned in LIFO order if last is true or FIFO order if false. - ''' + """ if not self: - raise KeyError('dictionary is empty') + raise KeyError("dictionary is empty") root = self.__root if last: link = root[0] @@ -164,47 +172,47 @@ except (ImportError, AttributeError): # -- the following methods do not depend on the internal structure -- def keys(self): - 'od.keys() -> list of keys in od' + "od.keys() -> list of keys in od" return list(self) def values(self): - 'od.values() -> list of values in od' + "od.values() -> list of values in od" return [self[key] for key in self] def items(self): - 'od.items() -> list of (key, value) pairs in od' + "od.items() -> list of (key, value) pairs in od" return [(key, self[key]) for key in self] def iterkeys(self): - 'od.iterkeys() -> an iterator over the keys in od' + "od.iterkeys() -> an iterator over the keys in od" return iter(self) def itervalues(self): - 'od.itervalues -> an iterator over the values in od' + "od.itervalues -> an iterator over the values in od" for k in self: yield self[k] def iteritems(self): - 'od.iteritems -> an iterator over the (key, value) items in od' + "od.iteritems -> an iterator over the (key, value) items in od" for k in self: yield (k, self[k]) def update(*args, **kwds): # pylint: disable=E0211 - '''od.update(E, **F) -> None. Update od from dict/iterable E and F. + """od.update(E, **F) -> None. Update od from dict/iterable E and F. If E is a dict instance, does: for k in E: od[k] = E[k] If E has a .keys() method, does: for k in E.keys(): od[k] = E[k] Or if E is an iterable of items, does: for k, v in E: od[k] = v In either case, this is followed by: for k, v in F.items(): od[k] = v - ''' + """ if len(args) > 2: raise TypeError( - 'update() takes at most 2 positional ' - 'arguments ({0} given)'.format(len(args)) + "update() takes at most 2 positional " + "arguments ({0} given)".format(len(args)) ) elif not args: - raise TypeError('update() takes at least 1 argument (0 given)') + raise TypeError("update() takes at least 1 argument (0 given)") self = args[0] # Make progressively weaker assumptions about "other" other = () @@ -213,7 +221,7 @@ except (ImportError, AttributeError): if isinstance(other, dict): for key in other: self[key] = other[key] - elif hasattr(other, 'keys'): + elif hasattr(other, "keys"): for key in other: self[key] = other[key] else: @@ -222,15 +230,17 @@ except (ImportError, AttributeError): for key, value in six.iteritems(kwds): self[key] = value - __update = update # let subclasses override update without breaking __init__ + __update = ( + update # let subclasses override update without breaking __init__ + ) __marker = object() def pop(self, key, default=__marker): - '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value. + """od.pop(k[,d]) -> v, remove specified key and return the corresponding value. If key is not found, d is returned if given, otherwise KeyError is raised. - ''' + """ if key in self: result = self[key] del self[key] @@ -240,27 +250,29 @@ except (ImportError, AttributeError): return default def setdefault(self, key, default=None): - 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + "od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od" if key in self: return self[key] self[key] = default return default def __repr__(self, _repr_running={}): # pylint: disable=W0102 - 'od.__repr__() <==> repr(od)' + "od.__repr__() <==> repr(od)" call_key = id(self), _get_ident() if call_key in _repr_running: - return '...' + return "..." _repr_running[call_key] = 1 try: if not self: - return '{0}()'.format(self.__class__.__name__) - return '{0}(\'{1}\')'.format(self.__class__.__name__, list(self.items())) + return "{0}()".format(self.__class__.__name__) + return "{0}('{1}')".format( + self.__class__.__name__, list(self.items()) + ) finally: del _repr_running[call_key] def __reduce__(self): - 'Return state information for pickling' + "Return state information for pickling" items = [[k, self[k]] for k in self] inst_dict = vars(self).copy() for k in vars(OrderedDict()): @@ -270,25 +282,25 @@ except (ImportError, AttributeError): return self.__class__, (items,) def copy(self): - 'od.copy() -> a shallow copy of od' + "od.copy() -> a shallow copy of od" return self.__class__(self) @classmethod def fromkeys(cls, iterable, value=None): - '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S + """OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S and values equal to v (which defaults to None). - ''' + """ d = cls() for key in iterable: d[key] = value return d def __eq__(self, other): - '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + """od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive while comparison to a regular mapping is order-insensitive. - ''' + """ if isinstance(other, OrderedDict): return len(self) == len(other) and self.items() == other.items() return dict.__eq__(self, other) @@ -296,6 +308,7 @@ except (ImportError, AttributeError): def __ne__(self, other): return not self == other + # # -- the following methods are only used in Python 2.7 -- # # def viewkeys(self): @@ -313,11 +326,11 @@ except (ImportError, AttributeError): class DefaultOrderedDict(OrderedDict): - 'Dictionary that remembers insertion order and ' + "Dictionary that remembers insertion order and " + def __init__(self, default_factory=None, *a, **kw): - if (default_factory is not None and - not isinstance(default_factory, Callable)): - raise TypeError('first argument must be callable') + if default_factory is not None and not isinstance(default_factory, Callable): + raise TypeError("first argument must be callable") super(DefaultOrderedDict, self).__init__(*a, **kw) self.default_factory = default_factory @@ -337,7 +350,7 @@ class DefaultOrderedDict(OrderedDict): if self.default_factory is None: args = tuple() else: - args = self.default_factory, + args = (self.default_factory,) return type(self), args, None, None, self.items() def copy(self): @@ -348,10 +361,10 @@ class DefaultOrderedDict(OrderedDict): def __deepcopy__(self): import copy - return type(self)(self.default_factory, - copy.deepcopy(self.items())) + + return type(self)(self.default_factory, copy.deepcopy(self.items())) def __repr__(self, _repr_running={}): # pylint: disable=W0102 - return 'DefaultOrderedDict({0}, {1})'.format(self.default_factory, - super(DefaultOrderedDict, - self).__repr__()) + return "DefaultOrderedDict({0}, {1})".format( + self.default_factory, super(DefaultOrderedDict, self).__repr__() + ) diff --git a/salt/utils/openstack/__init__.py b/salt/utils/openstack/__init__.py index d6a1451e90d..14efa754ad2 100644 --- a/salt/utils/openstack/__init__.py +++ b/salt/utils/openstack/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -''' +""" Init Openstack apis -''' +""" diff --git a/salt/utils/openstack/neutron.py b/salt/utils/openstack/neutron.py index 323c2b1bb24..62dc497276f 100644 --- a/salt/utils/openstack/neutron.py +++ b/salt/utils/openstack/neutron.py @@ -1,15 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Neutron class -''' +""" # Import python libs -from __future__ import absolute_import, with_statement, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals, with_statement + import logging +import salt.utils.versions + +# Import salt libs +from salt import exceptions + # Import third party libs from salt.ext import six + # pylint: disable=import-error HAS_NEUTRON = False try: @@ -24,14 +31,12 @@ HAS_KEYSTONEAUTH = False try: import keystoneauth1.loading import keystoneauth1.session + HAS_KEYSTONEAUTH = True except ImportError: pass # pylint: enable=import-error -# Import salt libs -from salt import exceptions -import salt.utils.versions # Get logging started log = logging.getLogger(__name__) @@ -47,10 +52,25 @@ def check_keystone(): def sanitize_neutronclient(kwargs): variables = ( - 'username', 'user_id', 'password', 'token', 'tenant_name', - 'tenant_id', 'auth_url', 'service_type', 'endpoint_type', - 'region_name', 'verify', 'endpoint_url', 'timeout', 'insecure', - 'ca_cert', 'retries', 'raise_error', 'session', 'auth' + "username", + "user_id", + "password", + "token", + "tenant_name", + "tenant_id", + "auth_url", + "service_type", + "endpoint_type", + "region_name", + "verify", + "endpoint_url", + "timeout", + "insecure", + "ca_cert", + "retries", + "raise_error", + "session", + "auth", ) ret = {} for var in six.iterkeys(kwargs): @@ -62,9 +82,9 @@ def sanitize_neutronclient(kwargs): # Function alias to not shadow built-ins class SaltNeutron(NeutronShell): - ''' + """ Class for all neutronclient functions - ''' + """ def __init__( self, @@ -73,79 +93,109 @@ class SaltNeutron(NeutronShell): auth_url, password=None, region_name=None, - service_type='network', + service_type="network", os_auth_plugin=None, use_keystoneauth=False, **kwargs ): - ''' + """ Set up neutron credentials - ''' + """ salt.utils.versions.warn_until( - 'Aluminium', + "Aluminium", ( - 'The neutron module has been deprecated and will be removed in {version}. ' - 'Please update to using the neutronng module' + "The neutron module has been deprecated and will be removed in {version}. " + "Please update to using the neutronng module" ), ) if not HAS_NEUTRON: return None elif all([use_keystoneauth, HAS_KEYSTONEAUTH]): - self._new_init(username=username, - project_name=tenant_name, - auth_url=auth_url, - region_name=region_name, - service_type=service_type, - os_auth_plugin=os_auth_plugin, - password=password, - **kwargs) + self._new_init( + username=username, + project_name=tenant_name, + auth_url=auth_url, + region_name=region_name, + service_type=service_type, + os_auth_plugin=os_auth_plugin, + password=password, + **kwargs + ) else: - self._old_init(username=username, - tenant_name=tenant_name, - auth_url=auth_url, - region_name=region_name, - service_type=service_type, - os_auth_plugin=os_auth_plugin, - password=password, - **kwargs) + self._old_init( + username=username, + tenant_name=tenant_name, + auth_url=auth_url, + region_name=region_name, + service_type=service_type, + os_auth_plugin=os_auth_plugin, + password=password, + **kwargs + ) - def _new_init(self, username, project_name, auth_url, region_name, service_type, password, os_auth_plugin, auth=None, verify=True, **kwargs): + def _new_init( + self, + username, + project_name, + auth_url, + region_name, + service_type, + password, + os_auth_plugin, + auth=None, + verify=True, + **kwargs + ): if auth is None: auth = {} - loader = keystoneauth1.loading.get_plugin_loader(os_auth_plugin or 'password') + loader = keystoneauth1.loading.get_plugin_loader(os_auth_plugin or "password") self.client_kwargs = kwargs.copy() self.kwargs = auth.copy() - self.kwargs['username'] = username - self.kwargs['project_name'] = project_name - self.kwargs['auth_url'] = auth_url - self.kwargs['password'] = password - if auth_url.endswith('3'): - self.kwargs['user_domain_name'] = kwargs.get('user_domain_name', 'default') - self.kwargs['project_domain_name'] = kwargs.get('project_domain_name', 'default') + self.kwargs["username"] = username + self.kwargs["project_name"] = project_name + self.kwargs["auth_url"] = auth_url + self.kwargs["password"] = password + if auth_url.endswith("3"): + self.kwargs["user_domain_name"] = kwargs.get("user_domain_name", "default") + self.kwargs["project_domain_name"] = kwargs.get( + "project_domain_name", "default" + ) - self.client_kwargs['region_name'] = region_name - self.client_kwargs['service_type'] = service_type + self.client_kwargs["region_name"] = region_name + self.client_kwargs["service_type"] = service_type self.client_kwargs = sanitize_neutronclient(self.client_kwargs) options = loader.load_from_options(**self.kwargs) self.session = keystoneauth1.session.Session(auth=options, verify=verify) self.network_conn = client.Client(session=self.session, **self.client_kwargs) - def _old_init(self, username, tenant_name, auth_url, region_name, service_type, password, os_auth_plugin, auth=None, verify=True, **kwargs): + def _old_init( + self, + username, + tenant_name, + auth_url, + region_name, + service_type, + password, + os_auth_plugin, + auth=None, + verify=True, + **kwargs + ): self.kwargs = kwargs.copy() - self.kwargs['username'] = username - self.kwargs['tenant_name'] = tenant_name - self.kwargs['auth_url'] = auth_url - self.kwargs['service_type'] = service_type - self.kwargs['password'] = password - self.kwargs['region_name'] = region_name - self.kwargs['verify'] = verify + self.kwargs["username"] = username + self.kwargs["tenant_name"] = tenant_name + self.kwargs["auth_url"] = auth_url + self.kwargs["service_type"] = service_type + self.kwargs["password"] = password + self.kwargs["region_name"] = region_name + self.kwargs["verify"] = verify self.kwargs = sanitize_neutronclient(self.kwargs) @@ -155,9 +205,9 @@ class SaltNeutron(NeutronShell): def _fetch(resources, name_or_id): ret = [] for resource in resources: - if resource['id'] == name_or_id: + if resource["id"] == name_or_id: return resource - if resource.get('name') == name_or_id: + if resource.get("name") == name_or_id: ret.append(resource) if len(ret) == 0: raise exceptions.MinionError("Resource not found.") @@ -168,772 +218,821 @@ class SaltNeutron(NeutronShell): def _find_port_id(self, resource): resource = self._fetch_port(resource) - return resource['id'] + return resource["id"] def _find_network_id(self, resource): resource = self._fetch_network(resource) - return resource['id'] + return resource["id"] def _find_subnet_id(self, resource): resource = self._fetch_subnet(resource) - return resource['id'] + return resource["id"] def _find_router_id(self, resource): resource = self._fetch_router(resource) - return resource['id'] + return resource["id"] def _find_security_group_id(self, resource): resource = self._fetch_security_group(resource) - return resource['id'] + return resource["id"] def _find_vpnservice_id(self, resource): resource = self._fetch_vpnservice(resource) - return resource['id'] + return resource["id"] def _find_ipsec_site_connection_id(self, resource): resource = self._fetch_ipsec_site_connection(resource) - return resource['id'] + return resource["id"] def _find_ikepolicy_id(self, resource): resource = self._fetch_ikepolicy(resource) - return resource['id'] + return resource["id"] def _find_ipsecpolicy_id(self, resource): resource = self._fetch_ipsecpolicy(resource) - return resource['id'] + return resource["id"] def _find_firewall_rule_id(self, resource): resource = self._fetch_firewall_rule(resource) - return resource['id'] + return resource["id"] def _fetch_port(self, name_or_id): - resources = self.list_ports()['ports'] + resources = self.list_ports()["ports"] return self._fetch(resources, name_or_id) def _fetch_network(self, name_or_id): - resources = self.list_networks()['networks'] + resources = self.list_networks()["networks"] return self._fetch(resources, name_or_id) def _fetch_subnet(self, name_or_id): - resources = self.list_subnets()['subnets'] + resources = self.list_subnets()["subnets"] return self._fetch(resources, name_or_id) def _fetch_router(self, name_or_id): - resources = self.list_routers()['routers'] + resources = self.list_routers()["routers"] return self._fetch(resources, name_or_id) def _fetch_security_group(self, name_or_id): - resources = self.list_security_groups()['security_groups'] + resources = self.list_security_groups()["security_groups"] return self._fetch(resources, name_or_id) def _fetch_vpnservice(self, name_or_id): - resources = self.list_vpnservices()['vpnservices'] + resources = self.list_vpnservices()["vpnservices"] return self._fetch(resources, name_or_id) def _fetch_ipsec_site_connection(self, name_or_id): - resources = (self.list_ipsec_site_connections() - ['ipsec_site_connections']) + resources = self.list_ipsec_site_connections()["ipsec_site_connections"] return self._fetch(resources, name_or_id) def _fetch_ikepolicy(self, name_or_id): - resources = self.list_ikepolicies()['ikepolicies'] + resources = self.list_ikepolicies()["ikepolicies"] return self._fetch(resources, name_or_id) def _fetch_ipsecpolicy(self, name_or_id): - resources = self.list_ipsecpolicies()['ipsecpolicies'] + resources = self.list_ipsecpolicies()["ipsecpolicies"] return self._fetch(resources, name_or_id) def _fetch_firewall_rule(self, name_or_id): - resources = self.list_firewall_rules()['firewall_rules'] + resources = self.list_firewall_rules()["firewall_rules"] return self._fetch(resources, name_or_id) def _fetch_firewall(self, name_or_id): - resources = self.list_firewalls()['firewalls'] + resources = self.list_firewalls()["firewalls"] return self._fetch(resources, name_or_id) def get_quotas_tenant(self): - ''' + """ Fetches tenant info in server's context for following quota operation - ''' + """ return self.get_quotas_tenant() def list_quotas(self): - ''' + """ Fetches all tenants quotas - ''' + """ return self.network_conn.list_quotas() def show_quota(self, tenant_id): - ''' + """ Fetches information of a certain tenant's quotas - ''' + """ return self.network_conn.show_quota(tenant_id=tenant_id) - def update_quota(self, tenant_id, subnet=None, router=None, - network=None, floatingip=None, port=None, - sec_grp=None, sec_grp_rule=None): - ''' + def update_quota( + self, + tenant_id, + subnet=None, + router=None, + network=None, + floatingip=None, + port=None, + sec_grp=None, + sec_grp_rule=None, + ): + """ Update a tenant's quota - ''' + """ body = {} if subnet: - body['subnet'] = subnet + body["subnet"] = subnet if router: - body['router'] = router + body["router"] = router if network: - body['network'] = network + body["network"] = network if floatingip: - body['floatingip'] = floatingip + body["floatingip"] = floatingip if port: - body['port'] = port + body["port"] = port if sec_grp: - body['security_group'] = sec_grp + body["security_group"] = sec_grp if sec_grp_rule: - body['security_group_rule'] = sec_grp_rule - return self.network_conn.update_quota(tenant_id=tenant_id, - body={'quota': body}) + body["security_group_rule"] = sec_grp_rule + return self.network_conn.update_quota(tenant_id=tenant_id, body={"quota": body}) def delete_quota(self, tenant_id): - ''' + """ Delete the specified tenant's quota value - ''' + """ ret = self.network_conn.delete_quota(tenant_id=tenant_id) return ret if ret else True def list_extensions(self): - ''' + """ Fetches a list of all extensions on server side - ''' + """ return self.network_conn.list_extensions() def list_ports(self): - ''' + """ Fetches a list of all ports for a tenant - ''' + """ return self.network_conn.list_ports() def show_port(self, port): - ''' + """ Fetches information of a certain port - ''' + """ return self._fetch_port(port) def create_port(self, name, network, device_id=None, admin_state_up=True): - ''' + """ Creates a new port - ''' + """ net_id = self._find_network_id(network) - body = {'admin_state_up': admin_state_up, - 'name': name, - 'network_id': net_id} + body = {"admin_state_up": admin_state_up, "name": name, "network_id": net_id} if device_id: - body['device_id'] = device_id - return self.network_conn.create_port(body={'port': body}) + body["device_id"] = device_id + return self.network_conn.create_port(body={"port": body}) def update_port(self, port, name, admin_state_up=True): - ''' + """ Updates a port - ''' + """ port_id = self._find_port_id(port) - body = {'name': name, - 'admin_state_up': admin_state_up} - return self.network_conn.update_port(port=port_id, - body={'port': body}) + body = {"name": name, "admin_state_up": admin_state_up} + return self.network_conn.update_port(port=port_id, body={"port": body}) def delete_port(self, port): - ''' + """ Deletes the specified port - ''' + """ port_id = self._find_port_id(port) ret = self.network_conn.delete_port(port=port_id) return ret if ret else True def list_networks(self): - ''' + """ Fetches a list of all networks for a tenant - ''' + """ return self.network_conn.list_networks() def show_network(self, network): - ''' + """ Fetches information of a certain network - ''' + """ return self._fetch_network(network) - def create_network(self, name, admin_state_up=True, router_ext=None, network_type=None, physical_network=None, segmentation_id=None, shared=None, vlan_transparent=None): - ''' + def create_network( + self, + name, + admin_state_up=True, + router_ext=None, + network_type=None, + physical_network=None, + segmentation_id=None, + shared=None, + vlan_transparent=None, + ): + """ Creates a new network - ''' - body = {'name': name, - 'admin_state_up': admin_state_up} + """ + body = {"name": name, "admin_state_up": admin_state_up} if router_ext: - body['router:external'] = router_ext + body["router:external"] = router_ext if network_type: - body['provider:network_type'] = network_type + body["provider:network_type"] = network_type if physical_network: - body['provider:physical_network'] = physical_network + body["provider:physical_network"] = physical_network if segmentation_id: - body['provider:segmentation_id'] = segmentation_id + body["provider:segmentation_id"] = segmentation_id if shared: - body['shared'] = shared + body["shared"] = shared if vlan_transparent: - body['vlan_transparent'] = vlan_transparent - return self.network_conn.create_network(body={'network': body}) + body["vlan_transparent"] = vlan_transparent + return self.network_conn.create_network(body={"network": body}) def update_network(self, network, name): - ''' + """ Updates a network - ''' + """ net_id = self._find_network_id(network) return self.network_conn.update_network( - network=net_id, body={'network': {'name': name}}) + network=net_id, body={"network": {"name": name}} + ) def delete_network(self, network): - ''' + """ Deletes the specified network - ''' + """ net_id = self._find_network_id(network) ret = self.network_conn.delete_network(network=net_id) return ret if ret else True def list_subnets(self): - ''' + """ Fetches a list of all networks for a tenant - ''' + """ return self.network_conn.list_subnets() def show_subnet(self, subnet): - ''' + """ Fetches information of a certain subnet - ''' + """ return self._fetch_subnet(subnet) def create_subnet(self, network, cidr, name=None, ip_version=4): - ''' + """ Creates a new subnet - ''' + """ net_id = self._find_network_id(network) - body = {'cidr': cidr, - 'ip_version': ip_version, - 'network_id': net_id, - 'name': name} - return self.network_conn.create_subnet(body={'subnet': body}) + body = { + "cidr": cidr, + "ip_version": ip_version, + "network_id": net_id, + "name": name, + } + return self.network_conn.create_subnet(body={"subnet": body}) def update_subnet(self, subnet, name=None): - ''' + """ Updates a subnet - ''' + """ subnet_id = self._find_subnet_id(subnet) return self.network_conn.update_subnet( - subnet=subnet_id, body={'subnet': {'name': name}}) + subnet=subnet_id, body={"subnet": {"name": name}} + ) def delete_subnet(self, subnet): - ''' + """ Deletes the specified subnet - ''' + """ subnet_id = self._find_subnet_id(subnet) ret = self.network_conn.delete_subnet(subnet=subnet_id) return ret if ret else True def list_routers(self): - ''' + """ Fetches a list of all routers for a tenant - ''' + """ return self.network_conn.list_routers() def show_router(self, router): - ''' + """ Fetches information of a certain router - ''' + """ return self._fetch_router(router) def create_router(self, name, ext_network=None, admin_state_up=True): - ''' + """ Creates a new router - ''' - body = {'name': name, - 'admin_state_up': admin_state_up} + """ + body = {"name": name, "admin_state_up": admin_state_up} if ext_network: net_id = self._find_network_id(ext_network) - body['external_gateway_info'] = {'network_id': net_id} - return self.network_conn.create_router(body={'router': body}) + body["external_gateway_info"] = {"network_id": net_id} + return self.network_conn.create_router(body={"router": body}) def update_router(self, router, name=None, admin_state_up=None, **kwargs): - ''' + """ Updates a router - ''' + """ router_id = self._find_router_id(router) body = {} - if 'ext_network' in kwargs: - if kwargs.get('ext_network') is None: - body['external_gateway_info'] = None + if "ext_network" in kwargs: + if kwargs.get("ext_network") is None: + body["external_gateway_info"] = None else: - net_id = self._find_network_id(kwargs.get('ext_network')) - body['external_gateway_info'] = {'network_id': net_id} + net_id = self._find_network_id(kwargs.get("ext_network")) + body["external_gateway_info"] = {"network_id": net_id} if name is not None: - body['name'] = name + body["name"] = name if admin_state_up is not None: - body['admin_state_up'] = admin_state_up - return self.network_conn.update_router( - router=router_id, body={'router': body}) + body["admin_state_up"] = admin_state_up + return self.network_conn.update_router(router=router_id, body={"router": body}) def delete_router(self, router): - ''' + """ Delete the specified router - ''' + """ router_id = self._find_router_id(router) ret = self.network_conn.delete_router(router=router_id) return ret if ret else True def add_interface_router(self, router, subnet): - ''' + """ Adds an internal network interface to the specified router - ''' + """ router_id = self._find_router_id(router) subnet_id = self._find_subnet_id(subnet) return self.network_conn.add_interface_router( - router=router_id, body={'subnet_id': subnet_id}) + router=router_id, body={"subnet_id": subnet_id} + ) def remove_interface_router(self, router, subnet): - ''' + """ Removes an internal network interface from the specified router - ''' + """ router_id = self._find_router_id(router) subnet_id = self._find_subnet_id(subnet) return self.network_conn.remove_interface_router( - router=router_id, body={'subnet_id': subnet_id}) + router=router_id, body={"subnet_id": subnet_id} + ) def add_gateway_router(self, router, network): - ''' + """ Adds an external network gateway to the specified router - ''' + """ router_id = self._find_router_id(router) net_id = self._find_network_id(network) return self.network_conn.add_gateway_router( - router=router_id, body={'network_id': net_id}) + router=router_id, body={"network_id": net_id} + ) def remove_gateway_router(self, router): - ''' + """ Removes an external network gateway from the specified router - ''' + """ router_id = self._find_router_id(router) return self.network_conn.remove_gateway_router(router=router_id) def list_floatingips(self): - ''' + """ Fetch a list of all floatingips for a tenant - ''' + """ return self.network_conn.list_floatingips() def show_floatingip(self, floatingip_id): - ''' + """ Fetches information of a certain floatingip - ''' + """ return self.network_conn.show_floatingip(floatingip_id) def create_floatingip(self, floating_network, port=None): - ''' + """ Creates a new floatingip - ''' + """ net_id = self._find_network_id(floating_network) - body = {'floating_network_id': net_id} + body = {"floating_network_id": net_id} if port: port_id = self._find_port_id(port) - body['port_id'] = port_id + body["port_id"] = port_id - return self.network_conn.create_floatingip(body={'floatingip': body}) + return self.network_conn.create_floatingip(body={"floatingip": body}) def update_floatingip(self, floatingip_id, port=None): - ''' + """ Updates a floatingip, disassociates the floating ip if port is set to `None` - ''' + """ if port is None: - body = {'floatingip': {}} + body = {"floatingip": {}} else: port_id = self._find_port_id(port) - body = {'floatingip': {'port_id': port_id}} - return self.network_conn.update_floatingip( - floatingip=floatingip_id, body=body) + body = {"floatingip": {"port_id": port_id}} + return self.network_conn.update_floatingip(floatingip=floatingip_id, body=body) def delete_floatingip(self, floatingip_id): - ''' + """ Deletes the specified floatingip - ''' + """ ret = self.network_conn.delete_floatingip(floatingip_id) return ret if ret else True def list_security_groups(self): - ''' + """ Fetches a list of all security groups for a tenant - ''' + """ return self.network_conn.list_security_groups() def show_security_group(self, sec_grp): - ''' + """ Fetches information of a certain security group - ''' + """ return self._fetch_security_group(sec_grp) def create_security_group(self, name, desc=None): - ''' + """ Creates a new security group - ''' - body = {'security_group': {'name': name, - 'description': desc}} + """ + body = {"security_group": {"name": name, "description": desc}} return self.network_conn.create_security_group(body=body) def update_security_group(self, sec_grp, name=None, desc=None): - ''' + """ Updates a security group - ''' + """ sec_grp_id = self._find_security_group_id(sec_grp) - body = {'security_group': {}} + body = {"security_group": {}} if name: - body['security_group']['name'] = name + body["security_group"]["name"] = name if desc: - body['security_group']['description'] = desc - return self.network_conn.update_security_group(sec_grp_id, - body=body) + body["security_group"]["description"] = desc + return self.network_conn.update_security_group(sec_grp_id, body=body) def delete_security_group(self, sec_grp): - ''' + """ Deletes the specified security group - ''' + """ sec_grp_id = self._find_security_group_id(sec_grp) ret = self.network_conn.delete_security_group(sec_grp_id) return ret if ret else True def list_security_group_rules(self): - ''' + """ Fetches a list of all security group rules for a tenant - ''' + """ return self.network_conn.list_security_group_rules() def show_security_group_rule(self, sec_grp_rule_id): - ''' + """ Fetches information of a certain security group rule - ''' - return self.network_conn.show_security_group_rule( - sec_grp_rule_id)['security_group_rule'] + """ + return self.network_conn.show_security_group_rule(sec_grp_rule_id)[ + "security_group_rule" + ] def create_security_group_rule( - self, sec_grp, remote_grp_id=None, direction='ingress', - protocol=None, port_range_min=None, port_range_max=None, - ether=None): - ''' + self, + sec_grp, + remote_grp_id=None, + direction="ingress", + protocol=None, + port_range_min=None, + port_range_max=None, + ether=None, + ): + """ Creates a new security group rule - ''' + """ sec_grp_id = self._find_security_group_id(sec_grp) - body = {'security_group_id': sec_grp_id, - 'remote_group_id': remote_grp_id, - 'direction': direction, 'protocol': protocol, - 'port_range_min': port_range_min, - 'port_range_max': port_range_max, - 'ethertype': ether} + body = { + "security_group_id": sec_grp_id, + "remote_group_id": remote_grp_id, + "direction": direction, + "protocol": protocol, + "port_range_min": port_range_min, + "port_range_max": port_range_max, + "ethertype": ether, + } return self.network_conn.create_security_group_rule( - body={'security_group_rule': body}) + body={"security_group_rule": body} + ) def delete_security_group_rule(self, sec_grp_rule_id): - ''' + """ Deletes the specified security group rule - ''' + """ ret = self.network_conn.delete_security_group_rule( - security_group_rule=sec_grp_rule_id) + security_group_rule=sec_grp_rule_id + ) return ret if ret else True def list_vpnservices(self, retrieve_all=True, **kwargs): - ''' + """ Fetches a list of all configured VPN services for a tenant - ''' + """ return self.network_conn.list_vpnservices(retrieve_all, **kwargs) def show_vpnservice(self, vpnservice, **kwargs): - ''' + """ Fetches information of a specific VPN service - ''' + """ vpnservice_id = self._find_vpnservice_id(vpnservice) return self.network_conn.show_vpnservice(vpnservice_id, **kwargs) def create_vpnservice(self, subnet, router, name, admin_state_up=True): - ''' + """ Creates a new VPN service - ''' + """ subnet_id = self._find_subnet_id(subnet) router_id = self._find_router_id(router) - body = {'subnet_id': subnet_id, - 'router_id': router_id, - 'name': name, - 'admin_state_up': admin_state_up} - return self.network_conn.create_vpnservice(body={'vpnservice': body}) + body = { + "subnet_id": subnet_id, + "router_id": router_id, + "name": name, + "admin_state_up": admin_state_up, + } + return self.network_conn.create_vpnservice(body={"vpnservice": body}) def update_vpnservice(self, vpnservice, desc): - ''' + """ Updates a VPN service - ''' + """ vpnservice_id = self._find_vpnservice_id(vpnservice) - body = {'description': desc} - return self.network_conn.update_vpnservice(vpnservice_id, - body={'vpnservice': body}) + body = {"description": desc} + return self.network_conn.update_vpnservice( + vpnservice_id, body={"vpnservice": body} + ) def delete_vpnservice(self, vpnservice): - ''' + """ Deletes the specified VPN service - ''' + """ vpnservice_id = self._find_vpnservice_id(vpnservice) ret = self.network_conn.delete_vpnservice(vpnservice_id) return ret if ret else True def list_ipsec_site_connections(self): - ''' + """ Fetches all configured IPsec Site Connections for a tenant - ''' + """ return self.network_conn.list_ipsec_site_connections() def show_ipsec_site_connection(self, ipsec_site_connection): - ''' + """ Fetches information of a specific IPsecSiteConnection - ''' + """ return self._fetch_ipsec_site_connection(ipsec_site_connection) - def create_ipsec_site_connection(self, name, ipsecpolicy, - ikepolicy, - vpnservice, - peer_cidrs, - peer_address, - peer_id, - psk, - admin_state_up=True, - **kwargs): - ''' + def create_ipsec_site_connection( + self, + name, + ipsecpolicy, + ikepolicy, + vpnservice, + peer_cidrs, + peer_address, + peer_id, + psk, + admin_state_up=True, + **kwargs + ): + """ Creates a new IPsecSiteConnection - ''' + """ ipsecpolicy_id = self._find_ipsecpolicy_id(ipsecpolicy) ikepolicy_id = self._find_ikepolicy_id(ikepolicy) vpnservice_id = self._find_vpnservice_id(vpnservice) - body = {'psk': psk, - 'ipsecpolicy_id': ipsecpolicy_id, - 'admin_state_up': admin_state_up, - 'peer_cidrs': [peer_cidrs], - 'ikepolicy_id': ikepolicy_id, - 'vpnservice_id': vpnservice_id, - 'peer_address': peer_address, - 'peer_id': peer_id, - 'name': name} - if 'initiator' in kwargs: - body['initiator'] = kwargs['initiator'] - if 'mtu' in kwargs: - body['mtu'] = kwargs['mtu'] - if 'dpd_action' in kwargs: - body['dpd'] = {'action': kwargs['dpd_action']} - if 'dpd_interval' in kwargs: - if 'dpd' not in body: - body['dpd'] = {} - body['dpd']['interval'] = kwargs['dpd_interval'] - if 'dpd_timeout' in kwargs: - if 'dpd' not in body: - body['dpd'] = {} - body['dpd']['timeout'] = kwargs['dpd_timeout'] + body = { + "psk": psk, + "ipsecpolicy_id": ipsecpolicy_id, + "admin_state_up": admin_state_up, + "peer_cidrs": [peer_cidrs], + "ikepolicy_id": ikepolicy_id, + "vpnservice_id": vpnservice_id, + "peer_address": peer_address, + "peer_id": peer_id, + "name": name, + } + if "initiator" in kwargs: + body["initiator"] = kwargs["initiator"] + if "mtu" in kwargs: + body["mtu"] = kwargs["mtu"] + if "dpd_action" in kwargs: + body["dpd"] = {"action": kwargs["dpd_action"]} + if "dpd_interval" in kwargs: + if "dpd" not in body: + body["dpd"] = {} + body["dpd"]["interval"] = kwargs["dpd_interval"] + if "dpd_timeout" in kwargs: + if "dpd" not in body: + body["dpd"] = {} + body["dpd"]["timeout"] = kwargs["dpd_timeout"] return self.network_conn.create_ipsec_site_connection( - body={'ipsec_site_connection': body}) + body={"ipsec_site_connection": body} + ) def delete_ipsec_site_connection(self, ipsec_site_connection): - ''' + """ Deletes the specified IPsecSiteConnection - ''' + """ ipsec_site_connection_id = self._find_ipsec_site_connection_id( - ipsec_site_connection) - ret = self.network_conn.delete_ipsec_site_connection( - ipsec_site_connection_id) + ipsec_site_connection + ) + ret = self.network_conn.delete_ipsec_site_connection(ipsec_site_connection_id) return ret if ret else True def list_ikepolicies(self): - ''' + """ Fetches a list of all configured IKEPolicies for a tenant - ''' + """ return self.network_conn.list_ikepolicies() def show_ikepolicy(self, ikepolicy): - ''' + """ Fetches information of a specific IKEPolicy - ''' + """ return self._fetch_ikepolicy(ikepolicy) def create_ikepolicy(self, name, **kwargs): - ''' + """ Creates a new IKEPolicy - ''' - body = {'name': name} - if 'phase1_negotiation_mode' in kwargs: - body['phase1_negotiation_mode'] = kwargs['phase1_negotiation_mode'] - if 'auth_algorithm' in kwargs: - body['auth_algorithm'] = kwargs['auth_algorithm'] - if 'encryption_algorithm' in kwargs: - body['encryption_algorithm'] = kwargs['encryption_algorithm'] - if 'pfs' in kwargs: - body['pfs'] = kwargs['pfs'] - if 'ike_version' in kwargs: - body['ike_version'] = kwargs['ike_version'] - if 'units' in kwargs: - body['lifetime'] = {'units': kwargs['units']} - if 'value' in kwargs: - if 'lifetime' not in body: - body['lifetime'] = {} - body['lifetime']['value'] = kwargs['value'] - return self.network_conn.create_ikepolicy(body={'ikepolicy': body}) + """ + body = {"name": name} + if "phase1_negotiation_mode" in kwargs: + body["phase1_negotiation_mode"] = kwargs["phase1_negotiation_mode"] + if "auth_algorithm" in kwargs: + body["auth_algorithm"] = kwargs["auth_algorithm"] + if "encryption_algorithm" in kwargs: + body["encryption_algorithm"] = kwargs["encryption_algorithm"] + if "pfs" in kwargs: + body["pfs"] = kwargs["pfs"] + if "ike_version" in kwargs: + body["ike_version"] = kwargs["ike_version"] + if "units" in kwargs: + body["lifetime"] = {"units": kwargs["units"]} + if "value" in kwargs: + if "lifetime" not in body: + body["lifetime"] = {} + body["lifetime"]["value"] = kwargs["value"] + return self.network_conn.create_ikepolicy(body={"ikepolicy": body}) def delete_ikepolicy(self, ikepolicy): - ''' + """ Deletes the specified IKEPolicy - ''' + """ ikepolicy_id = self._find_ikepolicy_id(ikepolicy) ret = self.network_conn.delete_ikepolicy(ikepolicy_id) return ret if ret else True def list_ipsecpolicies(self): - ''' + """ Fetches a list of all configured IPsecPolicies for a tenant - ''' + """ return self.network_conn.list_ipsecpolicies() def show_ipsecpolicy(self, ipsecpolicy): - ''' + """ Fetches information of a specific IPsecPolicy - ''' + """ return self._fetch_ipsecpolicy(ipsecpolicy) def create_ipsecpolicy(self, name, **kwargs): - ''' + """ Creates a new IPsecPolicy - ''' - body = {'name': name} - if 'transform_protocol' in kwargs: - body['transform_protocol'] = kwargs['transform_protocol'] - if 'auth_algorithm' in kwargs: - body['auth_algorithm'] = kwargs['auth_algorithm'] - if 'encapsulation_mode' in kwargs: - body['encapsulation_mode'] = kwargs['encapsulation_mode'] - if 'encryption_algorithm' in kwargs: - body['encryption_algorithm'] = kwargs['encryption_algorithm'] - if 'pfs' in kwargs: - body['pfs'] = kwargs['pfs'] - if 'units' in kwargs: - body['lifetime'] = {'units': kwargs['units']} - if 'value' in kwargs: - if 'lifetime' not in body: - body['lifetime'] = {} - body['lifetime']['value'] = kwargs['value'] - return self.network_conn.create_ipsecpolicy(body={'ipsecpolicy': body}) + """ + body = {"name": name} + if "transform_protocol" in kwargs: + body["transform_protocol"] = kwargs["transform_protocol"] + if "auth_algorithm" in kwargs: + body["auth_algorithm"] = kwargs["auth_algorithm"] + if "encapsulation_mode" in kwargs: + body["encapsulation_mode"] = kwargs["encapsulation_mode"] + if "encryption_algorithm" in kwargs: + body["encryption_algorithm"] = kwargs["encryption_algorithm"] + if "pfs" in kwargs: + body["pfs"] = kwargs["pfs"] + if "units" in kwargs: + body["lifetime"] = {"units": kwargs["units"]} + if "value" in kwargs: + if "lifetime" not in body: + body["lifetime"] = {} + body["lifetime"]["value"] = kwargs["value"] + return self.network_conn.create_ipsecpolicy(body={"ipsecpolicy": body}) def delete_ipsecpolicy(self, ipseecpolicy): - ''' + """ Deletes the specified IPsecPolicy - ''' + """ ipseecpolicy_id = self._find_ipsecpolicy_id(ipseecpolicy) ret = self.network_conn.delete_ipsecpolicy(ipseecpolicy_id) return ret if ret else True def list_firewall_rules(self): - ''' + """ Fetches a list of all configured firewall rules for a tenant - ''' + """ return self.network_conn.list_firewall_rules() def show_firewall_rule(self, firewall_rule): - ''' + """ Fetches information of a specific firewall rule - ''' + """ return self._fetch_firewall_rule(firewall_rule) def create_firewall_rule(self, protocol, action, **kwargs): - ''' + """ Create a new firlwall rule - ''' - body = {'protocol': protocol, 'action': action} - if 'tenant_id' in kwargs: - body['tenant_id'] = kwargs['tenant_id'] - if 'name' in kwargs: - body['name'] = kwargs['name'] - if 'description' in kwargs: - body['description'] = kwargs['description'] - if 'ip_version' in kwargs: - body['ip_version'] = kwargs['ip_version'] - if 'source_ip_address' in kwargs: - body['source_ip_address'] = kwargs['source_ip_address'] - if 'destination_port' in kwargs: - body['destination_port'] = kwargs['destination_port'] - if 'shared' in kwargs: - body['shared'] = kwargs['shared'] - if 'enabled' in kwargs: - body['enabled'] = kwargs['enabled'] - return self.network_conn.create_firewall_rule(body={'firewall_rule': body}) + """ + body = {"protocol": protocol, "action": action} + if "tenant_id" in kwargs: + body["tenant_id"] = kwargs["tenant_id"] + if "name" in kwargs: + body["name"] = kwargs["name"] + if "description" in kwargs: + body["description"] = kwargs["description"] + if "ip_version" in kwargs: + body["ip_version"] = kwargs["ip_version"] + if "source_ip_address" in kwargs: + body["source_ip_address"] = kwargs["source_ip_address"] + if "destination_port" in kwargs: + body["destination_port"] = kwargs["destination_port"] + if "shared" in kwargs: + body["shared"] = kwargs["shared"] + if "enabled" in kwargs: + body["enabled"] = kwargs["enabled"] + return self.network_conn.create_firewall_rule(body={"firewall_rule": body}) def delete_firewall_rule(self, firewall_rule): - ''' + """ Deletes the specified firewall rule - ''' + """ firewall_rule_id = self._find_firewall_rule_id(firewall_rule) ret = self.network_conn.delete_firewall_rule(firewall_rule_id) return ret if ret else True - def update_firewall_rule(self, firewall_rule, protocol=None, action=None, - name=None, description=None, ip_version=None, - source_ip_address=None, destination_ip_address=None, source_port=None, - destination_port=None, shared=None, enabled=None): - ''' + def update_firewall_rule( + self, + firewall_rule, + protocol=None, + action=None, + name=None, + description=None, + ip_version=None, + source_ip_address=None, + destination_ip_address=None, + source_port=None, + destination_port=None, + shared=None, + enabled=None, + ): + """ Update a firewall rule - ''' + """ body = {} if protocol: - body['protocol'] = protocol + body["protocol"] = protocol if action: - body['action'] = action + body["action"] = action if name: - body['name'] = name + body["name"] = name if description: - body['description'] = description + body["description"] = description if ip_version: - body['ip_version'] = ip_version + body["ip_version"] = ip_version if source_ip_address: - body['source_ip_address'] = source_ip_address + body["source_ip_address"] = source_ip_address if destination_ip_address: - body['destination_ip_address'] = destination_ip_address + body["destination_ip_address"] = destination_ip_address if source_port: - body['source_port'] = source_port + body["source_port"] = source_port if destination_port: - body['destination_port'] = destination_port + body["destination_port"] = destination_port if shared: - body['shared'] = shared + body["shared"] = shared if enabled: - body['enabled'] = enabled - return self.network_conn.update_firewall_rule(firewall_rule, body={'firewall_rule': body}) + body["enabled"] = enabled + return self.network_conn.update_firewall_rule( + firewall_rule, body={"firewall_rule": body} + ) def list_firewalls(self): - ''' + """ Fetches a list of all firewalls for a tenant - ''' + """ return self.network_conn.list_firewalls() def show_firewall(self, firewall): - ''' + """ Fetches information of a specific firewall - ''' + """ return self._fetch_firewall(firewall) def list_l3_agent_hosting_routers(self, router): - ''' + """ List L3 agents. - ''' + """ return self.network_conn.list_l3_agent_hosting_routers(router) def list_agents(self): - ''' + """ List agents. - ''' + """ return self.network_conn.list_agents() + # The following is a list of functions that need to be incorporated in the # neutron module. This list should be updated as functions are added. # diff --git a/salt/utils/openstack/nova.py b/salt/utils/openstack/nova.py index 0c3dcbd9064..38a747fd1e1 100644 --- a/salt/utils/openstack/nova.py +++ b/salt/utils/openstack/nova.py @@ -1,16 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Nova class -''' +""" # Import Python libs -from __future__ import absolute_import, with_statement, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals, with_statement + import inspect import logging import time +# Import salt libs +import salt.utils.cloud +import salt.utils.files +from salt.exceptions import SaltCloudSystemExit + # Import third party libs from salt.ext import six +from salt.utils.versions import LooseVersion as _LooseVersion HAS_NOVA = False # pylint: disable=import-error @@ -38,31 +45,26 @@ except ImportError: pass # pylint: enable=import-error -# Import salt libs -import salt.utils.cloud -import salt.utils.files -from salt.exceptions import SaltCloudSystemExit -from salt.utils.versions import LooseVersion as _LooseVersion # Get logging started log = logging.getLogger(__name__) # Version added to novaclient.client.Client function -NOVACLIENT_MINVER = '2.6.1' -NOVACLIENT_MAXVER = '6.0.1' +NOVACLIENT_MINVER = "2.6.1" +NOVACLIENT_MAXVER = "6.0.1" # dict for block_device_mapping_v2 CLIENT_BDM2_KEYS = { - 'id': 'uuid', - 'source': 'source_type', - 'dest': 'destination_type', - 'bus': 'disk_bus', - 'device': 'device_name', - 'size': 'volume_size', - 'format': 'guest_format', - 'bootindex': 'boot_index', - 'type': 'device_type', - 'shutdown': 'delete_on_termination', + "id": "uuid", + "source": "source_type", + "dest": "destination_type", + "bus": "disk_bus", + "device": "device_name", + "size": "volume_size", + "format": "guest_format", + "bootindex": "boot_index", + "type": "device_type", + "shutdown": "delete_on_termination", } @@ -74,11 +76,11 @@ def check_nova(): if min_ver <= novaclient_ver <= max_ver: return HAS_NOVA elif novaclient_ver > max_ver: - log.debug('Older novaclient version required. Maximum: %s', - NOVACLIENT_MAXVER) + log.debug( + "Older novaclient version required. Maximum: %s", NOVACLIENT_MAXVER + ) return False - log.debug('Newer novaclient version required. Minimum: %s', - NOVACLIENT_MINVER) + log.debug("Newer novaclient version required. Minimum: %s", NOVACLIENT_MINVER) return False @@ -86,8 +88,10 @@ if check_nova(): try: import novaclient.auth_plugin except ImportError: - log.debug('Using novaclient version 7.0.0 or newer. Authentication ' - 'plugin auth_plugin.py is not available anymore.') + log.debug( + "Using novaclient version 7.0.0 or newer. Authentication " + "plugin auth_plugin.py is not available anymore." + ) # kwargs has to be an object instead of a dictionary for the __post_parse_arg__ @@ -96,7 +100,9 @@ class KwargsStruct(object): self.__dict__.update(entries) -def _parse_block_device_mapping_v2(block_device=None, boot_volume=None, snapshot=None, ephemeral=None, swap=None): +def _parse_block_device_mapping_v2( + block_device=None, boot_volume=None, snapshot=None, ephemeral=None, swap=None +): bdm = [] if block_device is None: block_device = [] @@ -104,15 +110,23 @@ def _parse_block_device_mapping_v2(block_device=None, boot_volume=None, snapshot ephemeral = [] if boot_volume is not None: - bdm_dict = {'uuid': boot_volume, 'source_type': 'volume', - 'destination_type': 'volume', 'boot_index': 0, - 'delete_on_termination': False} + bdm_dict = { + "uuid": boot_volume, + "source_type": "volume", + "destination_type": "volume", + "boot_index": 0, + "delete_on_termination": False, + } bdm.append(bdm_dict) if snapshot is not None: - bdm_dict = {'uuid': snapshot, 'source_type': 'snapshot', - 'destination_type': 'volume', 'boot_index': 0, - 'delete_on_termination': False} + bdm_dict = { + "uuid": snapshot, + "source_type": "snapshot", + "destination_type": "volume", + "boot_index": 0, + "delete_on_termination": False, + } bdm.append(bdm_dict) for device_spec in block_device: @@ -123,28 +137,37 @@ def _parse_block_device_mapping_v2(block_device=None, boot_volume=None, snapshot # Convert the delete_on_termination to a boolean or set it to true by # default for local block devices when not specified. - if 'delete_on_termination' in bdm_dict: - action = bdm_dict['delete_on_termination'] - bdm_dict['delete_on_termination'] = (action == 'remove') - elif bdm_dict.get('destination_type') == 'local': - bdm_dict['delete_on_termination'] = True + if "delete_on_termination" in bdm_dict: + action = bdm_dict["delete_on_termination"] + bdm_dict["delete_on_termination"] = action == "remove" + elif bdm_dict.get("destination_type") == "local": + bdm_dict["delete_on_termination"] = True bdm.append(bdm_dict) for ephemeral_spec in ephemeral: - bdm_dict = {'source_type': 'blank', 'destination_type': 'local', - 'boot_index': -1, 'delete_on_termination': True} - if 'size' in ephemeral_spec: - bdm_dict['volume_size'] = ephemeral_spec['size'] - if 'format' in ephemeral_spec: - bdm_dict['guest_format'] = ephemeral_spec['format'] + bdm_dict = { + "source_type": "blank", + "destination_type": "local", + "boot_index": -1, + "delete_on_termination": True, + } + if "size" in ephemeral_spec: + bdm_dict["volume_size"] = ephemeral_spec["size"] + if "format" in ephemeral_spec: + bdm_dict["guest_format"] = ephemeral_spec["format"] bdm.append(bdm_dict) if swap is not None: - bdm_dict = {'source_type': 'blank', 'destination_type': 'local', - 'boot_index': -1, 'delete_on_termination': True, - 'guest_format': 'swap', 'volume_size': swap} + bdm_dict = { + "source_type": "blank", + "destination_type": "local", + "boot_index": -1, + "delete_on_termination": True, + "guest_format": "swap", + "volume_size": swap, + } bdm.append(bdm_dict) return bdm @@ -152,36 +175,33 @@ def _parse_block_device_mapping_v2(block_device=None, boot_volume=None, snapshot class NovaServer(object): def __init__(self, name, server, password=None): - ''' + """ Make output look like libcloud output for consistency - ''' + """ self.name = name - self.id = server['id'] - self.image = server.get('image', {}).get('id', 'Boot From Volume') - self.size = server['flavor']['id'] - self.state = server['state'] + self.id = server["id"] + self.image = server.get("image", {}).get("id", "Boot From Volume") + self.size = server["flavor"]["id"] + self.state = server["state"] self._uuid = None - self.extra = { - 'metadata': server['metadata'], - 'access_ip': server['accessIPv4'] - } + self.extra = {"metadata": server["metadata"], "access_ip": server["accessIPv4"]} - self.addresses = server.get('addresses', {}) + self.addresses = server.get("addresses", {}) self.public_ips, self.private_ips = [], [] self.fixed_ips, self.floating_ips = [], [] for network in self.addresses.values(): for addr in network: - if salt.utils.cloud.is_public_ip(addr['addr']): - self.public_ips.append(addr['addr']) + if salt.utils.cloud.is_public_ip(addr["addr"]): + self.public_ips.append(addr["addr"]) else: - self.private_ips.append(addr['addr']) - if addr.get('OS-EXT-IPS:type') == 'floating': - self.floating_ips.append(addr['addr']) + self.private_ips.append(addr["addr"]) + if addr.get("OS-EXT-IPS:type") == "floating": + self.floating_ips.append(addr["addr"]) else: - self.fixed_ips.append(addr['addr']) + self.fixed_ips.append(addr["addr"]) if password: - self.extra['password'] = password + self.extra["password"] = password def __str__(self): return self.__dict__ @@ -192,7 +212,7 @@ def get_entry(dict_, key, value, raise_error=True): if entry[key] == value: return entry if raise_error is True: - raise SaltCloudSystemExit('Unable to find {0} in {1}.'.format(key, dict_)) + raise SaltCloudSystemExit("Unable to find {0} in {1}.".format(key, dict_)) return {} @@ -201,28 +221,48 @@ def get_entry_multi(dict_, pairs, raise_error=True): if all([entry[key] == value for key, value in pairs]): return entry if raise_error is True: - raise SaltCloudSystemExit('Unable to find {0} in {1}.'.format(pairs, dict_)) + raise SaltCloudSystemExit("Unable to find {0} in {1}.".format(pairs, dict_)) return {} def get_endpoint_url_v3(catalog, service_type, region_name): for service_entry in catalog: - if service_entry['type'] == service_type: - for endpoint_entry in service_entry['endpoints']: - if (endpoint_entry['region'] == region_name and - endpoint_entry['interface'] == 'public'): - return endpoint_entry['url'] + if service_entry["type"] == service_type: + for endpoint_entry in service_entry["endpoints"]: + if ( + endpoint_entry["region"] == region_name + and endpoint_entry["interface"] == "public" + ): + return endpoint_entry["url"] return None def sanatize_novaclient(kwargs): variables = ( - 'username', 'api_key', 'project_id', 'auth_url', 'insecure', - 'timeout', 'proxy_tenant_id', 'proxy_token', 'region_name', - 'endpoint_type', 'extensions', 'service_type', 'service_name', - 'volume_service_name', 'timings', 'bypass_url', 'os_cache', - 'no_cache', 'http_log_debug', 'auth_system', 'auth_plugin', - 'auth_token', 'cacert', 'tenant_id' + "username", + "api_key", + "project_id", + "auth_url", + "insecure", + "timeout", + "proxy_tenant_id", + "proxy_token", + "region_name", + "endpoint_type", + "extensions", + "service_type", + "service_name", + "volume_service_name", + "timings", + "bypass_url", + "os_cache", + "no_cache", + "http_log_debug", + "auth_system", + "auth_plugin", + "auth_token", + "cacert", + "tenant_id", ) ret = {} for var in kwargs: @@ -234,9 +274,10 @@ def sanatize_novaclient(kwargs): # Function alias to not shadow built-ins class SaltNova(object): - ''' + """ Class for all novaclient functions - ''' + """ + extensions = [] def __init__( @@ -250,124 +291,152 @@ class SaltNova(object): use_keystoneauth=False, **kwargs ): - ''' + """ Set up nova credentials - ''' + """ if all([use_keystoneauth, HAS_KEYSTONEAUTH]): - self._new_init(username=username, - project_id=project_id, - auth_url=auth_url, - region_name=region_name, - password=password, - os_auth_plugin=os_auth_plugin, - **kwargs) + self._new_init( + username=username, + project_id=project_id, + auth_url=auth_url, + region_name=region_name, + password=password, + os_auth_plugin=os_auth_plugin, + **kwargs + ) else: - self._old_init(username=username, - project_id=project_id, - auth_url=auth_url, - region_name=region_name, - password=password, - os_auth_plugin=os_auth_plugin, - **kwargs) + self._old_init( + username=username, + project_id=project_id, + auth_url=auth_url, + region_name=region_name, + password=password, + os_auth_plugin=os_auth_plugin, + **kwargs + ) - def _new_init(self, username, project_id, auth_url, region_name, password, os_auth_plugin, auth=None, verify=True, - **kwargs): + def _new_init( + self, + username, + project_id, + auth_url, + region_name, + password, + os_auth_plugin, + auth=None, + verify=True, + **kwargs + ): if auth is None: auth = {} - loader = keystoneauth1.loading.get_plugin_loader(os_auth_plugin or 'password') + loader = keystoneauth1.loading.get_plugin_loader(os_auth_plugin or "password") self.client_kwargs = kwargs.copy() self.kwargs = auth.copy() if not self.extensions: - if hasattr(OpenStackComputeShell, '_discover_extensions'): - self.extensions = OpenStackComputeShell()._discover_extensions('2.0') + if hasattr(OpenStackComputeShell, "_discover_extensions"): + self.extensions = OpenStackComputeShell()._discover_extensions("2.0") else: - self.extensions = client.discover_extensions('2.0') + self.extensions = client.discover_extensions("2.0") for extension in self.extensions: - extension.run_hooks('__pre_parse_args__') - self.client_kwargs['extensions'] = self.extensions + extension.run_hooks("__pre_parse_args__") + self.client_kwargs["extensions"] = self.extensions - self.kwargs['username'] = username - self.kwargs['project_name'] = project_id - self.kwargs['auth_url'] = auth_url - self.kwargs['password'] = password - if auth_url.endswith('3'): - self.kwargs['user_domain_name'] = kwargs.get('user_domain_name', 'default') - self.kwargs['project_domain_name'] = kwargs.get('project_domain_name', 'default') + self.kwargs["username"] = username + self.kwargs["project_name"] = project_id + self.kwargs["auth_url"] = auth_url + self.kwargs["password"] = password + if auth_url.endswith("3"): + self.kwargs["user_domain_name"] = kwargs.get("user_domain_name", "default") + self.kwargs["project_domain_name"] = kwargs.get( + "project_domain_name", "default" + ) - self.client_kwargs['region_name'] = region_name - self.client_kwargs['service_type'] = 'compute' + self.client_kwargs["region_name"] = region_name + self.client_kwargs["service_type"] = "compute" - if hasattr(self, 'extensions'): + if hasattr(self, "extensions"): # needs an object, not a dictionary self.kwargstruct = KwargsStruct(**self.client_kwargs) for extension in self.extensions: - extension.run_hooks('__post_parse_args__', self.kwargstruct) + extension.run_hooks("__post_parse_args__", self.kwargstruct) self.client_kwargs = self.kwargstruct.__dict__ # Requires novaclient version >= 2.6.1 - self.version = six.text_type(kwargs.get('version', 2)) + self.version = six.text_type(kwargs.get("version", 2)) self.client_kwargs = sanatize_novaclient(self.client_kwargs) options = loader.load_from_options(**self.kwargs) self.session = keystoneauth1.session.Session(auth=options, verify=verify) - conn = client.Client(version=self.version, session=self.session, **self.client_kwargs) - self.kwargs['auth_token'] = conn.client.session.get_token() - identity_service_type = kwargs.get('identity_service_type', 'identity') - self.catalog = conn.client.session.get( - '/auth/catalog', - endpoint_filter={ - 'service_type': identity_service_type - } - ).json().get('catalog', []) - if conn.client.get_endpoint(service_type=identity_service_type).endswith('v3'): + conn = client.Client( + version=self.version, session=self.session, **self.client_kwargs + ) + self.kwargs["auth_token"] = conn.client.session.get_token() + identity_service_type = kwargs.get("identity_service_type", "identity") + self.catalog = ( + conn.client.session.get( + "/auth/catalog", endpoint_filter={"service_type": identity_service_type} + ) + .json() + .get("catalog", []) + ) + if conn.client.get_endpoint(service_type=identity_service_type).endswith("v3"): self._v3_setup(region_name) else: self._v2_setup(region_name) - def _old_init(self, username, project_id, auth_url, region_name, password, os_auth_plugin, **kwargs): + def _old_init( + self, + username, + project_id, + auth_url, + region_name, + password, + os_auth_plugin, + **kwargs + ): self.kwargs = kwargs.copy() if not self.extensions: - if hasattr(OpenStackComputeShell, '_discover_extensions'): - self.extensions = OpenStackComputeShell()._discover_extensions('2.0') + if hasattr(OpenStackComputeShell, "_discover_extensions"): + self.extensions = OpenStackComputeShell()._discover_extensions("2.0") else: - self.extensions = client.discover_extensions('2.0') + self.extensions = client.discover_extensions("2.0") for extension in self.extensions: - extension.run_hooks('__pre_parse_args__') - self.kwargs['extensions'] = self.extensions + extension.run_hooks("__pre_parse_args__") + self.kwargs["extensions"] = self.extensions - self.kwargs['username'] = username - self.kwargs['project_id'] = project_id - self.kwargs['auth_url'] = auth_url - self.kwargs['region_name'] = region_name - self.kwargs['service_type'] = 'compute' + self.kwargs["username"] = username + self.kwargs["project_id"] = project_id + self.kwargs["auth_url"] = auth_url + self.kwargs["region_name"] = region_name + self.kwargs["service_type"] = "compute" # used in novaclient extensions to see if they are rackspace or not, to know if it needs to load # the hooks for that extension or not. This is cleaned up by sanatize_novaclient - self.kwargs['os_auth_url'] = auth_url + self.kwargs["os_auth_url"] = auth_url if os_auth_plugin is not None: novaclient.auth_plugin.discover_auth_systems() auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_plugin) - self.kwargs['auth_plugin'] = auth_plugin - self.kwargs['auth_system'] = os_auth_plugin + self.kwargs["auth_plugin"] = auth_plugin + self.kwargs["auth_system"] = os_auth_plugin - if not self.kwargs.get('api_key', None): - self.kwargs['api_key'] = password + if not self.kwargs.get("api_key", None): + self.kwargs["api_key"] = password # This has to be run before sanatize_novaclient before extra variables are cleaned out. - if hasattr(self, 'extensions'): + if hasattr(self, "extensions"): # needs an object, not a dictionary self.kwargstruct = KwargsStruct(**self.kwargs) for extension in self.extensions: - extension.run_hooks('__post_parse_args__', self.kwargstruct) + extension.run_hooks("__post_parse_args__", self.kwargstruct) self.kwargs = self.kwargstruct.__dict__ self.kwargs = sanatize_novaclient(self.kwargs) # Requires novaclient version >= 2.6.1 - self.kwargs['version'] = six.text_type(kwargs.get('version', 2)) + self.kwargs["version"] = six.text_type(kwargs.get("version", 2)) conn = client.Client(**self.kwargs) try: @@ -377,54 +446,62 @@ class SaltNova(object): "Nova provider requires a 'region_name' to be specified" ) - self.kwargs['auth_token'] = conn.client.auth_token - self.catalog = conn.client.service_catalog.catalog['access']['serviceCatalog'] + self.kwargs["auth_token"] = conn.client.auth_token + self.catalog = conn.client.service_catalog.catalog["access"]["serviceCatalog"] self._v2_setup(region_name) def _v3_setup(self, region_name): if region_name is not None: - self.client_kwargs['bypass_url'] = get_endpoint_url_v3(self.catalog, 'compute', region_name) - log.debug('Using Nova bypass_url: %s', - self.client_kwargs['bypass_url']) + self.client_kwargs["bypass_url"] = get_endpoint_url_v3( + self.catalog, "compute", region_name + ) + log.debug("Using Nova bypass_url: %s", self.client_kwargs["bypass_url"]) - self.compute_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs) + self.compute_conn = client.Client( + version=self.version, session=self.session, **self.client_kwargs + ) - volume_endpoints = get_entry(self.catalog, 'type', 'volume', raise_error=False).get('endpoints', {}) + volume_endpoints = get_entry( + self.catalog, "type", "volume", raise_error=False + ).get("endpoints", {}) if volume_endpoints: if region_name is not None: - self.client_kwargs['bypass_url'] = get_endpoint_url_v3(self.catalog, 'volume', region_name) - log.debug('Using Cinder bypass_url: %s', - self.client_kwargs['bypass_url']) + self.client_kwargs["bypass_url"] = get_endpoint_url_v3( + self.catalog, "volume", region_name + ) + log.debug( + "Using Cinder bypass_url: %s", self.client_kwargs["bypass_url"] + ) - self.volume_conn = client.Client(version=self.version, session=self.session, **self.client_kwargs) - if hasattr(self, 'extensions'): + self.volume_conn = client.Client( + version=self.version, session=self.session, **self.client_kwargs + ) + if hasattr(self, "extensions"): self.expand_extensions() else: self.volume_conn = None def _v2_setup(self, region_name): if region_name is not None: - servers_endpoints = get_entry(self.catalog, 'type', 'compute')['endpoints'] - self.kwargs['bypass_url'] = get_entry( - servers_endpoints, - 'region', - region_name - )['publicURL'] + servers_endpoints = get_entry(self.catalog, "type", "compute")["endpoints"] + self.kwargs["bypass_url"] = get_entry( + servers_endpoints, "region", region_name + )["publicURL"] self.compute_conn = client.Client(**self.kwargs) - volume_endpoints = get_entry(self.catalog, 'type', 'volume', raise_error=False).get('endpoints', {}) + volume_endpoints = get_entry( + self.catalog, "type", "volume", raise_error=False + ).get("endpoints", {}) if volume_endpoints: if region_name is not None: - self.kwargs['bypass_url'] = get_entry( - volume_endpoints, - 'region', - region_name - )['publicURL'] + self.kwargs["bypass_url"] = get_entry( + volume_endpoints, "region", region_name + )["publicURL"] self.volume_conn = client.Client(**self.kwargs) - if hasattr(self, 'extensions'): + if hasattr(self, "extensions"): self.expand_extensions() else: self.volume_conn = None @@ -441,47 +518,52 @@ class SaltNova(object): if not isinstance(value, novaclient.base.Manager): continue if value.__class__.__name__ == attr: - setattr(connection, key, extension.manager_class(connection)) + setattr( + connection, key, extension.manager_class(connection) + ) def get_catalog(self): - ''' + """ Return service catalog - ''' + """ return self.catalog def server_show_libcloud(self, uuid): - ''' + """ Make output look like libcloud output for consistency - ''' + """ server_info = self.server_show(uuid) server = next(six.itervalues(server_info)) server_name = next(six.iterkeys(server_info)) - if not hasattr(self, 'password'): + if not hasattr(self, "password"): self.password = None ret = NovaServer(server_name, server, self.password) return ret def boot(self, name, flavor_id=0, image_id=0, timeout=300, **kwargs): - ''' + """ Boot a cloud server. - ''' + """ nt_ks = self.compute_conn - kwargs['name'] = name - kwargs['flavor'] = flavor_id - kwargs['image'] = image_id or None - ephemeral = kwargs.pop('ephemeral', []) - block_device = kwargs.pop('block_device', []) - boot_volume = kwargs.pop('boot_volume', None) - snapshot = kwargs.pop('snapshot', None) - swap = kwargs.pop('swap', None) - kwargs['block_device_mapping_v2'] = _parse_block_device_mapping_v2( - block_device=block_device, boot_volume=boot_volume, snapshot=snapshot, - ephemeral=ephemeral, swap=swap + kwargs["name"] = name + kwargs["flavor"] = flavor_id + kwargs["image"] = image_id or None + ephemeral = kwargs.pop("ephemeral", []) + block_device = kwargs.pop("block_device", []) + boot_volume = kwargs.pop("boot_volume", None) + snapshot = kwargs.pop("snapshot", None) + swap = kwargs.pop("swap", None) + kwargs["block_device_mapping_v2"] = _parse_block_device_mapping_v2( + block_device=block_device, + boot_volume=boot_volume, + snapshot=snapshot, + ephemeral=ephemeral, + swap=swap, ) response = nt_ks.servers.create(**kwargs) self.uuid = response.id - self.password = getattr(response, 'adminPass', None) + self.password = getattr(response, "adminPass", None) start = time.time() trycount = 0 @@ -490,565 +572,557 @@ class SaltNova(object): try: return self.server_show_libcloud(self.uuid) except Exception as exc: # pylint: disable=broad-except - log.debug( - 'Server information not yet available: %s', exc - ) + log.debug("Server information not yet available: %s", exc) time.sleep(1) if time.time() - start > timeout: - log.error('Timed out after %s seconds ' - 'while waiting for data', timeout) + log.error( + "Timed out after %s seconds " "while waiting for data", timeout + ) return False - log.debug( - 'Retrying server_show() (try %s)', trycount - ) + log.debug("Retrying server_show() (try %s)", trycount) def show_instance(self, name): - ''' + """ Find a server by its name (libcloud) - ''' + """ return self.server_by_name(name) def root_password(self, server_id, password): - ''' + """ Change server(uuid's) root password - ''' + """ nt_ks = self.compute_conn nt_ks.servers.change_password(server_id, password) def server_by_name(self, name): - ''' + """ Find a server by its name - ''' - return self.server_show_libcloud( - self.server_list().get(name, {}).get('id', '') - ) + """ + return self.server_show_libcloud(self.server_list().get(name, {}).get("id", "")) def _volume_get(self, volume_id): - ''' + """ Organize information about a volume from the volume_id - ''' + """ if self.volume_conn is None: - raise SaltCloudSystemExit('No cinder endpoint available') + raise SaltCloudSystemExit("No cinder endpoint available") nt_ks = self.volume_conn volume = nt_ks.volumes.get(volume_id) - response = {'name': volume.display_name, - 'size': volume.size, - 'id': volume.id, - 'description': volume.display_description, - 'attachments': volume.attachments, - 'status': volume.status - } + response = { + "name": volume.display_name, + "size": volume.size, + "id": volume.id, + "description": volume.display_description, + "attachments": volume.attachments, + "status": volume.status, + } return response def volume_list(self, search_opts=None): - ''' + """ List all block volumes - ''' + """ if self.volume_conn is None: - raise SaltCloudSystemExit('No cinder endpoint available') + raise SaltCloudSystemExit("No cinder endpoint available") nt_ks = self.volume_conn volumes = nt_ks.volumes.list(search_opts=search_opts) response = {} for volume in volumes: response[volume.display_name] = { - 'name': volume.display_name, - 'size': volume.size, - 'id': volume.id, - 'description': volume.display_description, - 'attachments': volume.attachments, - 'status': volume.status + "name": volume.display_name, + "size": volume.size, + "id": volume.id, + "description": volume.display_description, + "attachments": volume.attachments, + "status": volume.status, } return response def volume_show(self, name): - ''' + """ Show one volume - ''' + """ if self.volume_conn is None: - raise SaltCloudSystemExit('No cinder endpoint available') + raise SaltCloudSystemExit("No cinder endpoint available") - volumes = self.volume_list( - search_opts={'display_name': name}, - ) + volumes = self.volume_list(search_opts={"display_name": name},) volume = volumes[name] return volume - def volume_create(self, name, size=100, snapshot=None, voltype=None, - availability_zone=None): - ''' + def volume_create( + self, name, size=100, snapshot=None, voltype=None, availability_zone=None + ): + """ Create a block device - ''' + """ if self.volume_conn is None: - raise SaltCloudSystemExit('No cinder endpoint available') + raise SaltCloudSystemExit("No cinder endpoint available") nt_ks = self.volume_conn response = nt_ks.volumes.create( size=size, display_name=name, volume_type=voltype, snapshot_id=snapshot, - availability_zone=availability_zone + availability_zone=availability_zone, ) return self._volume_get(response.id) def volume_delete(self, name): - ''' + """ Delete a block device - ''' + """ if self.volume_conn is None: - raise SaltCloudSystemExit('No cinder endpoint available') + raise SaltCloudSystemExit("No cinder endpoint available") nt_ks = self.volume_conn try: volume = self.volume_show(name) except KeyError as exc: - raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc)) - if volume['status'] == 'deleted': + raise SaltCloudSystemExit( + "Unable to find {0} volume: {1}".format(name, exc) + ) + if volume["status"] == "deleted": return volume - response = nt_ks.volumes.delete(volume['id']) + response = nt_ks.volumes.delete(volume["id"]) return volume - def volume_detach(self, - name, - timeout=300): - ''' + def volume_detach(self, name, timeout=300): + """ Detach a block device - ''' + """ try: volume = self.volume_show(name) except KeyError as exc: - raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc)) - if not volume['attachments']: + raise SaltCloudSystemExit( + "Unable to find {0} volume: {1}".format(name, exc) + ) + if not volume["attachments"]: return True response = self.compute_conn.volumes.delete_server_volume( - volume['attachments'][0]['server_id'], - volume['attachments'][0]['id'] + volume["attachments"][0]["server_id"], volume["attachments"][0]["id"] ) trycount = 0 start = time.time() while True: trycount += 1 try: - response = self._volume_get(volume['id']) - if response['status'] == 'available': + response = self._volume_get(volume["id"]) + if response["status"] == "available": return response except Exception as exc: # pylint: disable=broad-except - log.debug('Volume is detaching: %s', name) + log.debug("Volume is detaching: %s", name) time.sleep(1) if time.time() - start > timeout: - log.error('Timed out after %d seconds ' - 'while waiting for data', timeout) + log.error( + "Timed out after %d seconds " "while waiting for data", timeout + ) return False - log.debug( - 'Retrying volume_show() (try %d)', trycount - ) + log.debug("Retrying volume_show() (try %d)", trycount) - def volume_attach(self, - name, - server_name, - device='/dev/xvdb', - timeout=300): - ''' + def volume_attach(self, name, server_name, device="/dev/xvdb", timeout=300): + """ Attach a block device - ''' + """ try: volume = self.volume_show(name) except KeyError as exc: - raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc)) + raise SaltCloudSystemExit( + "Unable to find {0} volume: {1}".format(name, exc) + ) server = self.server_by_name(server_name) response = self.compute_conn.volumes.create_server_volume( - server.id, - volume['id'], - device=device + server.id, volume["id"], device=device ) trycount = 0 start = time.time() while True: trycount += 1 try: - response = self._volume_get(volume['id']) - if response['status'] == 'in-use': + response = self._volume_get(volume["id"]) + if response["status"] == "in-use": return response except Exception as exc: # pylint: disable=broad-except - log.debug('Volume is attaching: %s', name) + log.debug("Volume is attaching: %s", name) time.sleep(1) if time.time() - start > timeout: - log.error('Timed out after %s seconds ' - 'while waiting for data', timeout) + log.error( + "Timed out after %s seconds " "while waiting for data", timeout + ) return False - log.debug( - 'Retrying volume_show() (try %s)', trycount - ) + log.debug("Retrying volume_show() (try %s)", trycount) def suspend(self, instance_id): - ''' + """ Suspend a server - ''' + """ nt_ks = self.compute_conn response = nt_ks.servers.suspend(instance_id) return True def resume(self, instance_id): - ''' + """ Resume a server - ''' + """ nt_ks = self.compute_conn response = nt_ks.servers.resume(instance_id) return True def lock(self, instance_id): - ''' + """ Lock an instance - ''' + """ nt_ks = self.compute_conn response = nt_ks.servers.lock(instance_id) return True def delete(self, instance_id): - ''' + """ Delete a server - ''' + """ nt_ks = self.compute_conn response = nt_ks.servers.delete(instance_id) return True def flavor_list(self): - ''' + """ Return a list of available flavors (nova flavor-list) - ''' + """ nt_ks = self.compute_conn ret = {} for flavor in nt_ks.flavors.list(): links = {} for link in flavor.links: - links[link['rel']] = link['href'] + links[link["rel"]] = link["href"] ret[flavor.name] = { - 'disk': flavor.disk, - 'id': flavor.id, - 'name': flavor.name, - 'ram': flavor.ram, - 'swap': flavor.swap, - 'vcpus': flavor.vcpus, - 'links': links, + "disk": flavor.disk, + "id": flavor.id, + "name": flavor.name, + "ram": flavor.ram, + "swap": flavor.swap, + "vcpus": flavor.vcpus, + "links": links, } - if hasattr(flavor, 'rxtx_factor'): - ret[flavor.name]['rxtx_factor'] = flavor.rxtx_factor + if hasattr(flavor, "rxtx_factor"): + ret[flavor.name]["rxtx_factor"] = flavor.rxtx_factor return ret list_sizes = flavor_list - def flavor_create(self, - name, # pylint: disable=C0103 - flavor_id=0, # pylint: disable=C0103 - ram=0, - disk=0, - vcpus=1): - ''' + def flavor_create( + self, + name, # pylint: disable=C0103 + flavor_id=0, # pylint: disable=C0103 + ram=0, + disk=0, + vcpus=1, + ): + """ Create a flavor - ''' + """ nt_ks = self.compute_conn nt_ks.flavors.create( name=name, flavorid=flavor_id, ram=ram, disk=disk, vcpus=vcpus ) - return {'name': name, - 'id': flavor_id, - 'ram': ram, - 'disk': disk, - 'vcpus': vcpus} + return {"name": name, "id": flavor_id, "ram": ram, "disk": disk, "vcpus": vcpus} def flavor_delete(self, flavor_id): # pylint: disable=C0103 - ''' + """ Delete a flavor - ''' + """ nt_ks = self.compute_conn nt_ks.flavors.delete(flavor_id) - return 'Flavor deleted: {0}'.format(flavor_id) + return "Flavor deleted: {0}".format(flavor_id) def keypair_list(self): - ''' + """ List keypairs - ''' + """ nt_ks = self.compute_conn ret = {} for keypair in nt_ks.keypairs.list(): ret[keypair.name] = { - 'name': keypair.name, - 'fingerprint': keypair.fingerprint, - 'public_key': keypair.public_key, + "name": keypair.name, + "fingerprint": keypair.fingerprint, + "public_key": keypair.public_key, } return ret def keypair_add(self, name, pubfile=None, pubkey=None): - ''' + """ Add a keypair - ''' + """ nt_ks = self.compute_conn if pubfile: - with salt.utils.files.fopen(pubfile, 'r') as fp_: + with salt.utils.files.fopen(pubfile, "r") as fp_: pubkey = salt.utils.stringutils.to_unicode(fp_.read()) if not pubkey: return False nt_ks.keypairs.create(name, public_key=pubkey) - ret = {'name': name, 'pubkey': pubkey} + ret = {"name": name, "pubkey": pubkey} return ret def keypair_delete(self, name): - ''' + """ Delete a keypair - ''' + """ nt_ks = self.compute_conn nt_ks.keypairs.delete(name) - return 'Keypair deleted: {0}'.format(name) + return "Keypair deleted: {0}".format(name) def image_show(self, image_id): - ''' + """ Show image details and metadata - ''' + """ nt_ks = self.compute_conn image = nt_ks.images.get(image_id) links = {} for link in image.links: - links[link['rel']] = link['href'] + links[link["rel"]] = link["href"] ret = { - 'name': image.name, - 'id': image.id, - 'status': image.status, - 'progress': image.progress, - 'created': image.created, - 'updated': image.updated, - 'metadata': image.metadata, - 'links': links, + "name": image.name, + "id": image.id, + "status": image.status, + "progress": image.progress, + "created": image.created, + "updated": image.updated, + "metadata": image.metadata, + "links": links, } - if hasattr(image, 'minDisk'): - ret['minDisk'] = image.minDisk - if hasattr(image, 'minRam'): - ret['minRam'] = image.minRam + if hasattr(image, "minDisk"): + ret["minDisk"] = image.minDisk + if hasattr(image, "minRam"): + ret["minRam"] = image.minRam return ret def image_list(self, name=None): - ''' + """ List server images - ''' + """ nt_ks = self.compute_conn ret = {} for image in nt_ks.images.list(): links = {} for link in image.links: - links[link['rel']] = link['href'] + links[link["rel"]] = link["href"] ret[image.name] = { - 'name': image.name, - 'id': image.id, - 'status': image.status, - 'progress': image.progress, - 'created': image.created, - 'updated': image.updated, - 'metadata': image.metadata, - 'links': links, + "name": image.name, + "id": image.id, + "status": image.status, + "progress": image.progress, + "created": image.created, + "updated": image.updated, + "metadata": image.metadata, + "links": links, } - if hasattr(image, 'minDisk'): - ret[image.name]['minDisk'] = image.minDisk - if hasattr(image, 'minRam'): - ret[image.name]['minRam'] = image.minRam + if hasattr(image, "minDisk"): + ret[image.name]["minDisk"] = image.minDisk + if hasattr(image, "minRam"): + ret[image.name]["minRam"] = image.minRam if name: return {name: ret[name]} return ret list_images = image_list - def image_meta_set(self, - image_id=None, - name=None, - **kwargs): # pylint: disable=C0103 - ''' + def image_meta_set( + self, image_id=None, name=None, **kwargs + ): # pylint: disable=C0103 + """ Set image metadata - ''' + """ nt_ks = self.compute_conn if name: for image in nt_ks.images.list(): if image.name == name: image_id = image.id # pylint: disable=C0103 if not image_id: - return {'Error': 'A valid image name or id was not specified'} + return {"Error": "A valid image name or id was not specified"} nt_ks.images.set_meta(image_id, kwargs) return {image_id: kwargs} - def image_meta_delete(self, - image_id=None, # pylint: disable=C0103 - name=None, - keys=None): - ''' + def image_meta_delete( + self, image_id=None, name=None, keys=None # pylint: disable=C0103 + ): + """ Delete image metadata - ''' + """ nt_ks = self.compute_conn if name: for image in nt_ks.images.list(): if image.name == name: image_id = image.id # pylint: disable=C0103 - pairs = keys.split(',') + pairs = keys.split(",") if not image_id: - return {'Error': 'A valid image name or id was not specified'} + return {"Error": "A valid image name or id was not specified"} nt_ks.images.delete_meta(image_id, pairs) - return {image_id: 'Deleted: {0}'.format(pairs)} + return {image_id: "Deleted: {0}".format(pairs)} def server_list(self): - ''' + """ List servers - ''' + """ nt_ks = self.compute_conn ret = {} for item in nt_ks.servers.list(): try: ret[item.name] = { - 'id': item.id, - 'name': item.name, - 'state': item.status, - 'accessIPv4': item.accessIPv4, - 'accessIPv6': item.accessIPv6, - 'flavor': {'id': item.flavor['id'], - 'links': item.flavor['links']}, - 'image': {'id': item.image['id'] if item.image else 'Boot From Volume', - 'links': item.image['links'] if item.image else ''}, + "id": item.id, + "name": item.name, + "state": item.status, + "accessIPv4": item.accessIPv4, + "accessIPv6": item.accessIPv6, + "flavor": {"id": item.flavor["id"], "links": item.flavor["links"]}, + "image": { + "id": item.image["id"] if item.image else "Boot From Volume", + "links": item.image["links"] if item.image else "", + }, } except TypeError: pass return ret def server_list_min(self): - ''' + """ List minimal information about servers - ''' + """ nt_ks = self.compute_conn ret = {} for item in nt_ks.servers.list(detailed=False): try: - ret[item.name] = { - 'id': item.id, - 'state': 'Running' - } + ret[item.name] = {"id": item.id, "state": "Running"} except TypeError: pass return ret def server_list_detailed(self): - ''' + """ Detailed list of servers - ''' + """ nt_ks = self.compute_conn ret = {} for item in nt_ks.servers.list(): try: ret[item.name] = { - 'OS-EXT-SRV-ATTR': {}, - 'OS-EXT-STS': {}, - 'accessIPv4': item.accessIPv4, - 'accessIPv6': item.accessIPv6, - 'addresses': item.addresses, - 'created': item.created, - 'flavor': {'id': item.flavor['id'], - 'links': item.flavor['links']}, - 'hostId': item.hostId, - 'id': item.id, - 'image': {'id': item.image['id'] if item.image else 'Boot From Volume', - 'links': item.image['links'] if item.image else ''}, - 'key_name': item.key_name, - 'links': item.links, - 'metadata': item.metadata, - 'name': item.name, - 'state': item.status, - 'tenant_id': item.tenant_id, - 'updated': item.updated, - 'user_id': item.user_id, + "OS-EXT-SRV-ATTR": {}, + "OS-EXT-STS": {}, + "accessIPv4": item.accessIPv4, + "accessIPv6": item.accessIPv6, + "addresses": item.addresses, + "created": item.created, + "flavor": {"id": item.flavor["id"], "links": item.flavor["links"]}, + "hostId": item.hostId, + "id": item.id, + "image": { + "id": item.image["id"] if item.image else "Boot From Volume", + "links": item.image["links"] if item.image else "", + }, + "key_name": item.key_name, + "links": item.links, + "metadata": item.metadata, + "name": item.name, + "state": item.status, + "tenant_id": item.tenant_id, + "updated": item.updated, + "user_id": item.user_id, } except TypeError: continue - ret[item.name]['progress'] = getattr(item, 'progress', '0') + ret[item.name]["progress"] = getattr(item, "progress", "0") - if hasattr(item.__dict__, 'OS-DCF:diskConfig'): - ret[item.name]['OS-DCF'] = { - 'diskConfig': item.__dict__['OS-DCF:diskConfig'] + if hasattr(item.__dict__, "OS-DCF:diskConfig"): + ret[item.name]["OS-DCF"] = { + "diskConfig": item.__dict__["OS-DCF:diskConfig"] } - if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:host'): - ret[item.name]['OS-EXT-SRV-ATTR']['host'] = \ - item.__dict__['OS-EXT-SRV-ATTR:host'] - if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:hypervisor_hostname'): - ret[item.name]['OS-EXT-SRV-ATTR']['hypervisor_hostname'] = \ - item.__dict__['OS-EXT-SRV-ATTR:hypervisor_hostname'] - if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:instance_name'): - ret[item.name]['OS-EXT-SRV-ATTR']['instance_name'] = \ - item.__dict__['OS-EXT-SRV-ATTR:instance_name'] - if hasattr(item.__dict__, 'OS-EXT-STS:power_state'): - ret[item.name]['OS-EXT-STS']['power_state'] = \ - item.__dict__['OS-EXT-STS:power_state'] - if hasattr(item.__dict__, 'OS-EXT-STS:task_state'): - ret[item.name]['OS-EXT-STS']['task_state'] = \ - item.__dict__['OS-EXT-STS:task_state'] - if hasattr(item.__dict__, 'OS-EXT-STS:vm_state'): - ret[item.name]['OS-EXT-STS']['vm_state'] = \ - item.__dict__['OS-EXT-STS:vm_state'] - if hasattr(item.__dict__, 'security_groups'): - ret[item.name]['security_groups'] = \ - item.__dict__['security_groups'] + if hasattr(item.__dict__, "OS-EXT-SRV-ATTR:host"): + ret[item.name]["OS-EXT-SRV-ATTR"]["host"] = item.__dict__[ + "OS-EXT-SRV-ATTR:host" + ] + if hasattr(item.__dict__, "OS-EXT-SRV-ATTR:hypervisor_hostname"): + ret[item.name]["OS-EXT-SRV-ATTR"][ + "hypervisor_hostname" + ] = item.__dict__["OS-EXT-SRV-ATTR:hypervisor_hostname"] + if hasattr(item.__dict__, "OS-EXT-SRV-ATTR:instance_name"): + ret[item.name]["OS-EXT-SRV-ATTR"]["instance_name"] = item.__dict__[ + "OS-EXT-SRV-ATTR:instance_name" + ] + if hasattr(item.__dict__, "OS-EXT-STS:power_state"): + ret[item.name]["OS-EXT-STS"]["power_state"] = item.__dict__[ + "OS-EXT-STS:power_state" + ] + if hasattr(item.__dict__, "OS-EXT-STS:task_state"): + ret[item.name]["OS-EXT-STS"]["task_state"] = item.__dict__[ + "OS-EXT-STS:task_state" + ] + if hasattr(item.__dict__, "OS-EXT-STS:vm_state"): + ret[item.name]["OS-EXT-STS"]["vm_state"] = item.__dict__[ + "OS-EXT-STS:vm_state" + ] + if hasattr(item.__dict__, "security_groups"): + ret[item.name]["security_groups"] = item.__dict__["security_groups"] return ret def server_show(self, server_id): - ''' + """ Show details of one server - ''' + """ ret = {} try: servers = self.server_list_detailed() except AttributeError: - raise SaltCloudSystemExit('Corrupt server in server_list_detailed. Remove corrupt servers.') + raise SaltCloudSystemExit( + "Corrupt server in server_list_detailed. Remove corrupt servers." + ) for server_name, server in six.iteritems(servers): - if six.text_type(server['id']) == server_id: + if six.text_type(server["id"]) == server_id: ret[server_name] = server return ret def secgroup_create(self, name, description): - ''' + """ Create a security group - ''' + """ nt_ks = self.compute_conn nt_ks.security_groups.create(name, description) - ret = {'name': name, 'description': description} + ret = {"name": name, "description": description} return ret def secgroup_delete(self, name): - ''' + """ Delete a security group - ''' + """ nt_ks = self.compute_conn for item in nt_ks.security_groups.list(): if item.name == name: nt_ks.security_groups.delete(item.id) - return {name: 'Deleted security group: {0}'.format(name)} - return 'Security group not found: {0}'.format(name) + return {name: "Deleted security group: {0}".format(name)} + return "Security group not found: {0}".format(name) def secgroup_list(self): - ''' + """ List security groups - ''' + """ nt_ks = self.compute_conn ret = {} for item in nt_ks.security_groups.list(): ret[item.name] = { - 'name': item.name, - 'description': item.description, - 'id': item.id, - 'tenant_id': item.tenant_id, - 'rules': item.rules, + "name": item.name, + "description": item.description, + "id": item.id, + "tenant_id": item.tenant_id, + "rules": item.rules, } return ret def _item_list(self): - ''' + """ List items - ''' + """ nt_ks = self.compute_conn ret = [] for item in nt_ks.items.list(): @@ -1056,185 +1130,200 @@ class SaltNova(object): return ret def _network_show(self, name, network_lst): - ''' + """ Parse the returned network list - ''' + """ for net in network_lst: if net.label == name: return net.__dict__ return {} def network_show(self, name): - ''' + """ Show network information - ''' + """ nt_ks = self.compute_conn net_list = nt_ks.networks.list() return self._network_show(name, net_list) def network_list(self): - ''' + """ List extra private networks - ''' + """ nt_ks = self.compute_conn return [network.__dict__ for network in nt_ks.networks.list()] def _sanatize_network_params(self, kwargs): - ''' + """ Sanatize novaclient network parameters - ''' + """ params = [ - 'label', 'bridge', 'bridge_interface', 'cidr', 'cidr_v6', 'dns1', - 'dns2', 'fixed_cidr', 'gateway', 'gateway_v6', 'multi_host', - 'priority', 'project_id', 'vlan_start', 'vpn_start' + "label", + "bridge", + "bridge_interface", + "cidr", + "cidr_v6", + "dns1", + "dns2", + "fixed_cidr", + "gateway", + "gateway_v6", + "multi_host", + "priority", + "project_id", + "vlan_start", + "vpn_start", ] - for variable in six.iterkeys(kwargs): # iterate over a copy, we might delete some + for variable in six.iterkeys( + kwargs + ): # iterate over a copy, we might delete some if variable not in params: del kwargs[variable] return kwargs def network_create(self, name, **kwargs): - ''' + """ Create extra private network - ''' + """ nt_ks = self.compute_conn - kwargs['label'] = name + kwargs["label"] = name kwargs = self._sanatize_network_params(kwargs) net = nt_ks.networks.create(**kwargs) return net.__dict__ def _server_uuid_from_name(self, name): - ''' + """ Get server uuid from name - ''' - return self.server_list().get(name, {}).get('id', '') + """ + return self.server_list().get(name, {}).get("id", "") def virtual_interface_list(self, name): - ''' + """ Get virtual interfaces on slice - ''' + """ nt_ks = self.compute_conn nets = nt_ks.virtual_interfaces.list(self._server_uuid_from_name(name)) return [network.__dict__ for network in nets] def virtual_interface_create(self, name, net_name): - ''' + """ Add an interfaces to a slice - ''' + """ nt_ks = self.compute_conn serverid = self._server_uuid_from_name(name) - networkid = self.network_show(net_name).get('id', None) + networkid = self.network_show(net_name).get("id", None) if networkid is None: return {net_name: False} nets = nt_ks.virtual_interfaces.create(networkid, serverid) return nets def floating_ip_pool_list(self): - ''' + """ List all floating IP pools .. versionadded:: 2016.3.0 - ''' + """ nt_ks = self.compute_conn pools = nt_ks.floating_ip_pools.list() response = {} for pool in pools: response[pool.name] = { - 'name': pool.name, + "name": pool.name, } return response def floating_ip_list(self): - ''' + """ List floating IPs .. versionadded:: 2016.3.0 - ''' + """ nt_ks = self.compute_conn floating_ips = nt_ks.floating_ips.list() response = {} for floating_ip in floating_ips: response[floating_ip.ip] = { - 'ip': floating_ip.ip, - 'fixed_ip': floating_ip.fixed_ip, - 'id': floating_ip.id, - 'instance_id': floating_ip.instance_id, - 'pool': floating_ip.pool + "ip": floating_ip.ip, + "fixed_ip": floating_ip.fixed_ip, + "id": floating_ip.id, + "instance_id": floating_ip.instance_id, + "pool": floating_ip.pool, } return response def floating_ip_show(self, ip): - ''' + """ Show info on specific floating IP .. versionadded:: 2016.3.0 - ''' + """ nt_ks = self.compute_conn floating_ips = nt_ks.floating_ips.list() for floating_ip in floating_ips: if floating_ip.ip == ip: response = { - 'ip': floating_ip.ip, - 'fixed_ip': floating_ip.fixed_ip, - 'id': floating_ip.id, - 'instance_id': floating_ip.instance_id, - 'pool': floating_ip.pool + "ip": floating_ip.ip, + "fixed_ip": floating_ip.fixed_ip, + "id": floating_ip.id, + "instance_id": floating_ip.instance_id, + "pool": floating_ip.pool, } return response return {} def floating_ip_create(self, pool=None): - ''' + """ Allocate a floating IP .. versionadded:: 2016.3.0 - ''' + """ nt_ks = self.compute_conn floating_ip = nt_ks.floating_ips.create(pool) response = { - 'ip': floating_ip.ip, - 'fixed_ip': floating_ip.fixed_ip, - 'id': floating_ip.id, - 'instance_id': floating_ip.instance_id, - 'pool': floating_ip.pool + "ip": floating_ip.ip, + "fixed_ip": floating_ip.fixed_ip, + "id": floating_ip.id, + "instance_id": floating_ip.instance_id, + "pool": floating_ip.pool, } return response def floating_ip_delete(self, floating_ip): - ''' + """ De-allocate a floating IP .. versionadded:: 2016.3.0 - ''' + """ ip = self.floating_ip_show(floating_ip) nt_ks = self.compute_conn return nt_ks.floating_ips.delete(ip) def floating_ip_associate(self, server_name, floating_ip): - ''' + """ Associate floating IP address to server .. versionadded:: 2016.3.0 - ''' + """ nt_ks = self.compute_conn server_ = self.server_by_name(server_name) - server = nt_ks.servers.get(server_.__dict__['id']) + server = nt_ks.servers.get(server_.__dict__["id"]) server.add_floating_ip(floating_ip) return self.floating_ip_list()[floating_ip] def floating_ip_disassociate(self, server_name, floating_ip): - ''' + """ Disassociate a floating IP from server .. versionadded:: 2016.3.0 - ''' + """ nt_ks = self.compute_conn server_ = self.server_by_name(server_name) - server = nt_ks.servers.get(server_.__dict__['id']) + server = nt_ks.servers.get(server_.__dict__["id"]) server.remove_floating_ip(floating_ip) return self.floating_ip_list()[floating_ip] + # The following is a list of functions that need to be incorporated in the # nova module. This list should be updated as functions are added. # diff --git a/salt/utils/openstack/pyrax/__init__.py b/salt/utils/openstack/pyrax/__init__.py index d9a325a8817..da55d5d44e2 100644 --- a/salt/utils/openstack/pyrax/__init__.py +++ b/salt/utils/openstack/pyrax/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # pylint: disable=import-error try: @@ -9,10 +9,7 @@ try: from salt.utils.openstack.pyrax.authenticate import Authenticate from salt.utils.openstack.pyrax.queues import RackspaceQueues - __all__ = [ - 'Authenticate', - 'RackspaceQueues' - ] + __all__ = ["Authenticate", "RackspaceQueues"] HAS_PYRAX = True except ImportError as err: diff --git a/salt/utils/openstack/pyrax/authenticate.py b/salt/utils/openstack/pyrax/authenticate.py index e75c2f8bd96..567a6a2c1ce 100644 --- a/salt/utils/openstack/pyrax/authenticate.py +++ b/salt/utils/openstack/pyrax/authenticate.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import pyrax # pylint: disable=3rd-party-module-not-gated class Authenticate(object): def __init__(self, username, password, region, **kwargs): - cloud_kwargs = { - 'password': password, - 'region': region - } - pyrax.settings.set('identity_type', kwargs.get('identity_type', 'rackspace')) - pyrax.settings.set('auth_endpoint', kwargs.get('auth_endpoint', 'https://identity.api.rackspacecloud.com/v2.0')) + cloud_kwargs = {"password": password, "region": region} + pyrax.settings.set("identity_type", kwargs.get("identity_type", "rackspace")) + pyrax.settings.set( + "auth_endpoint", + kwargs.get("auth_endpoint", "https://identity.api.rackspacecloud.com/v2.0"), + ) pyrax.set_credentials(username, **cloud_kwargs) self.conn = pyrax diff --git a/salt/utils/openstack/pyrax/queues.py b/salt/utils/openstack/pyrax/queues.py index ca8f9ad9920..1078415cffe 100644 --- a/salt/utils/openstack/pyrax/queues.py +++ b/salt/utils/openstack/pyrax/queues.py @@ -1,20 +1,23 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging -log = logging.getLogger(__name__) # Import pyrax (SDK for Rackspace cloud) third party libs # pylint: disable=3rd-party-module-not-gated import pyrax import pyrax.exceptions # pylint: disable=no-name-in-module -# pylint: enable=3rd-party-module-not-gated # Import salt classes from salt.utils.openstack.pyrax import authenticate +log = logging.getLogger(__name__) + + +# pylint: enable=3rd-party-module-not-gated + class RackspaceQueues(object): def __init__(self, username, password, region, **kwargs): @@ -22,9 +25,9 @@ class RackspaceQueues(object): self.conn = self.auth.conn.queues def create(self, qname): - ''' + """ Create RackSpace Queue. - ''' + """ try: if self.exists(qname): log.error('Queues "%s" already exists. Nothing done.', qname) @@ -34,14 +37,13 @@ class RackspaceQueues(object): return True except pyrax.exceptions as err_msg: - log.error('RackSpace API got some problems during creation: %s', - err_msg) + log.error("RackSpace API got some problems during creation: %s", err_msg) return False def delete(self, qname): - ''' + """ Delete an existings RackSpace Queue. - ''' + """ try: q = self.exists(qname) if not q: @@ -50,31 +52,31 @@ class RackspaceQueues(object): if queue: queue.delete() except pyrax.exceptions as err_msg: - log.error('RackSpace API got some problems during deletion: %s', - err_msg) + log.error("RackSpace API got some problems during deletion: %s", err_msg) return False return True def exists(self, qname): - ''' + """ Check to see if a Queue exists. - ''' + """ try: # First if not exists() -> exit if self.conn.queue_exists(qname): return True return False except pyrax.exceptions as err_msg: - log.error('RackSpace API got some problems during ' - 'existing queue check: %s', - err_msg) + log.error( + "RackSpace API got some problems during " "existing queue check: %s", + err_msg, + ) return False def show(self, qname): - ''' + """ Show information about Queue - ''' + """ try: # First if not exists() -> exit if not self.conn.queue_exists(qname): @@ -84,6 +86,8 @@ class RackspaceQueues(object): if queue.name == qname: return queue except pyrax.exceptions as err_msg: - log.error('RackSpace API got some problems during existing' - ' queue check: %s', err_msg) + log.error( + "RackSpace API got some problems during existing" " queue check: %s", + err_msg, + ) return {} diff --git a/salt/utils/openstack/swift.py b/salt/utils/openstack/swift.py index 24d82cc4921..4fef12c9768 100644 --- a/salt/utils/openstack/swift.py +++ b/salt/utils/openstack/swift.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Swift utility class =================== Author: Anthony Stanton <anthony.stanton@gmail.com> -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libs import logging import sys +from errno import EEXIST from os import makedirs from os.path import dirname, isdir -from errno import EEXIST # Import Salt libs import salt.utils.files @@ -46,11 +46,21 @@ def mkdirs(path): # going to accept any old thing def _sanitize(kwargs): variables = ( - 'user', 'key', 'authurl', - 'retries', 'preauthurl', 'preauthtoken', 'snet', - 'starting_backoff', 'max_backoff', 'tenant_name', - 'os_options', 'auth_version', 'cacert', - 'insecure', 'ssl_compression' + "user", + "key", + "authurl", + "retries", + "preauthurl", + "preauthtoken", + "snet", + "starting_backoff", + "max_backoff", + "tenant_name", + "os_options", + "auth_version", + "cacert", + "insecure", + "ssl_compression", ) ret = {} for var in kwargs: @@ -61,109 +71,113 @@ def _sanitize(kwargs): class SaltSwift(object): - ''' + """ Class for all swiftclient functions - ''' + """ def __init__( - self, - user, - tenant_name, - auth_url, - password=None, - auth_version=2, - **kwargs + self, user, tenant_name, auth_url, password=None, auth_version=2, **kwargs ): - ''' + """ Set up openstack credentials - ''' + """ if not HAS_SWIFT: - log.error('Error:: unable to find swiftclient. Try installing it from the appropriate repository.') + log.error( + "Error:: unable to find swiftclient. Try installing it from the appropriate repository." + ) return None self.kwargs = kwargs.copy() - self.kwargs['user'] = user - self.kwargs['password'] = password - self.kwargs['tenant_name'] = tenant_name - self.kwargs['authurl'] = auth_url - self.kwargs['auth_version'] = auth_version - if 'key' not in self.kwargs: - self.kwargs['key'] = password + self.kwargs["user"] = user + self.kwargs["password"] = password + self.kwargs["tenant_name"] = tenant_name + self.kwargs["authurl"] = auth_url + self.kwargs["auth_version"] = auth_version + if "key" not in self.kwargs: + self.kwargs["key"] = password self.kwargs = _sanitize(self.kwargs) self.conn = client.Connection(**self.kwargs) def get_account(self): - ''' + """ List Swift containers - ''' + """ try: listing = self.conn.get_account() return listing except Exception as exc: # pylint: disable=broad-except - log.error('There was an error::') - if hasattr(exc, 'code') and hasattr(exc, 'msg'): - log.error(' Code: %s: %s', exc.code, exc.msg) - log.error(' Content: \n%s', getattr(exc, 'read', lambda: six.text_type(exc))()) + log.error("There was an error::") + if hasattr(exc, "code") and hasattr(exc, "msg"): + log.error(" Code: %s: %s", exc.code, exc.msg) + log.error( + " Content: \n%s", getattr(exc, "read", lambda: six.text_type(exc))() + ) return False def get_container(self, cont): - ''' + """ List files in a Swift container - ''' + """ try: listing = self.conn.get_container(cont) return listing except Exception as exc: # pylint: disable=broad-except - log.error('There was an error::') - if hasattr(exc, 'code') and hasattr(exc, 'msg'): - log.error(' Code: %s: %s', exc.code, exc.msg) - log.error(' Content: \n%s', getattr(exc, 'read', lambda: six.text_type(exc))()) + log.error("There was an error::") + if hasattr(exc, "code") and hasattr(exc, "msg"): + log.error(" Code: %s: %s", exc.code, exc.msg) + log.error( + " Content: \n%s", getattr(exc, "read", lambda: six.text_type(exc))() + ) return False def put_container(self, cont): - ''' + """ Create a new Swift container - ''' + """ try: self.conn.put_container(cont) return True except Exception as exc: # pylint: disable=broad-except - log.error('There was an error::') - if hasattr(exc, 'code') and hasattr(exc, 'msg'): - log.error(' Code: %s: %s', exc.code, exc.msg) - log.error(' Content: \n%s', getattr(exc, 'read', lambda: six.text_type(exc))()) + log.error("There was an error::") + if hasattr(exc, "code") and hasattr(exc, "msg"): + log.error(" Code: %s: %s", exc.code, exc.msg) + log.error( + " Content: \n%s", getattr(exc, "read", lambda: six.text_type(exc))() + ) return False def delete_container(self, cont): - ''' + """ Delete a Swift container - ''' + """ try: self.conn.delete_container(cont) return True except Exception as exc: # pylint: disable=broad-except - log.error('There was an error::') - if hasattr(exc, 'code') and hasattr(exc, 'msg'): - log.error(' Code: %s: %s', exc.code, exc.msg) - log.error(' Content: \n%s', getattr(exc, 'read', lambda: six.text_type(exc))()) + log.error("There was an error::") + if hasattr(exc, "code") and hasattr(exc, "msg"): + log.error(" Code: %s: %s", exc.code, exc.msg) + log.error( + " Content: \n%s", getattr(exc, "read", lambda: six.text_type(exc))() + ) return False def post_container(self, cont, metadata=None): - ''' + """ Update container metadata - ''' + """ def head_container(self, cont): - ''' + """ Get container metadata - ''' + """ def get_object(self, cont, obj, local_file=None, return_bin=False): - ''' + """ Retrieve a file from Swift - ''' + """ try: if local_file is None and return_bin is False: return False @@ -176,7 +190,9 @@ class SaltSwift(object): dirpath = dirname(local_file) if dirpath and not isdir(dirpath): mkdirs(dirpath) - fp = salt.utils.files.fopen(local_file, 'wb') # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + fp = salt.utils.files.fopen(local_file, "wb") + # pylint: enable=resource-leakage read_length = 0 for chunk in body: @@ -188,47 +204,53 @@ class SaltSwift(object): # ClientException # file/dir exceptions except Exception as exc: # pylint: disable=broad-except - log.error('There was an error::') - if hasattr(exc, 'code') and hasattr(exc, 'msg'): - log.error(' Code: %s: %s', exc.code, exc.msg) - log.error(' Content: \n%s', getattr(exc, 'read', lambda: six.text_type(exc))()) + log.error("There was an error::") + if hasattr(exc, "code") and hasattr(exc, "msg"): + log.error(" Code: %s: %s", exc.code, exc.msg) + log.error( + " Content: \n%s", getattr(exc, "read", lambda: six.text_type(exc))() + ) return False def put_object(self, cont, obj, local_file): - ''' + """ Upload a file to Swift - ''' + """ try: - with salt.utils.files.fopen(local_file, 'rb') as fp_: + with salt.utils.files.fopen(local_file, "rb") as fp_: self.conn.put_object(cont, obj, fp_) return True except Exception as exc: # pylint: disable=broad-except - log.error('There was an error::') - if hasattr(exc, 'code') and hasattr(exc, 'msg'): - log.error(' Code: %s: %s', exc.code, exc.msg) - log.error(' Content: \n%s', getattr(exc, 'read', lambda: six.text_type(exc))()) + log.error("There was an error::") + if hasattr(exc, "code") and hasattr(exc, "msg"): + log.error(" Code: %s: %s", exc.code, exc.msg) + log.error( + " Content: \n%s", getattr(exc, "read", lambda: six.text_type(exc))() + ) return False def delete_object(self, cont, obj): - ''' + """ Delete a file from Swift - ''' + """ try: self.conn.delete_object(cont, obj) return True except Exception as exc: # pylint: disable=broad-except - log.error('There was an error::') - if hasattr(exc, 'code') and hasattr(exc, 'msg'): - log.error(' Code: %s: %s', exc.code, exc.msg) - log.error(' Content: \n%s', getattr(exc, 'read', lambda: six.text_type(exc))()) + log.error("There was an error::") + if hasattr(exc, "code") and hasattr(exc, "msg"): + log.error(" Code: %s: %s", exc.code, exc.msg) + log.error( + " Content: \n%s", getattr(exc, "read", lambda: six.text_type(exc))() + ) return False def head_object(self, cont, obj): - ''' + """ Get object metadata - ''' + """ def post_object(self, cont, obj, metadata): - ''' + """ Update object metadata - ''' + """ diff --git a/salt/utils/oset.py b/salt/utils/oset.py index acfd59b53bf..6722b8025ec 100644 --- a/salt/utils/oset.py +++ b/salt/utils/oset.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Available at repository https://github.com/LuminosoInsight/ordered-set @@ -20,12 +20,13 @@ Rob Speer's changes are as follows: - index() just returns the index of an item - added a __getstate__ and __setstate__ so it can be pickled - added __getitem__ -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + import collections SLICE_ALL = slice(None) -__version__ = '2.0.1' +__version__ = "2.0.1" def is_iterable(obj): @@ -41,7 +42,11 @@ def is_iterable(obj): We don't need to check for the Python 2 `unicode` type, because it doesn't have an `__iter__` attribute anyway. """ - return hasattr(obj, '__iter__') and not isinstance(obj, str) and not isinstance(obj, tuple) + return ( + hasattr(obj, "__iter__") + and not isinstance(obj, str) + and not isinstance(obj, tuple) + ) class OrderedSet(collections.MutableSet): @@ -49,6 +54,7 @@ class OrderedSet(collections.MutableSet): An OrderedSet is a custom MutableSet that remembers its order, so that every entry has an index that can be looked up. """ + def __init__(self, iterable=None): self.items = [] self.map = {} @@ -72,7 +78,7 @@ class OrderedSet(collections.MutableSet): """ if index == SLICE_ALL: return self - elif hasattr(index, '__index__') or isinstance(index, slice): + elif hasattr(index, "__index__") or isinstance(index, slice): result = self.items[index] if isinstance(result, list): return OrderedSet(result) @@ -81,7 +87,9 @@ class OrderedSet(collections.MutableSet): elif is_iterable(index): return OrderedSet([self.items[i] for i in index]) else: - raise TypeError("Don't know how to index an OrderedSet by {}".format(repr(index))) + raise TypeError( + "Don't know how to index an OrderedSet by {}".format(repr(index)) + ) def copy(self): return OrderedSet(self) @@ -118,6 +126,7 @@ class OrderedSet(collections.MutableSet): self.map[key] = len(self.items) self.items.append(key) return self.map[key] + append = add def update(self, sequence): @@ -130,7 +139,9 @@ class OrderedSet(collections.MutableSet): for item in sequence: item_index = self.add(item) except TypeError: - raise ValueError("Argument needs to be an iterable, got {}".format(type(sequence))) + raise ValueError( + "Argument needs to be an iterable, got {}".format(type(sequence)) + ) return item_index def index(self, key): @@ -152,7 +163,7 @@ class OrderedSet(collections.MutableSet): Raises KeyError if the set is empty. """ if not self.items: - raise KeyError('Set is empty') + raise KeyError("Set is empty") elem = self.items[-1] del self.items[-1] diff --git a/salt/utils/pagerduty.py b/salt/utils/pagerduty.py index 99815f9b76e..5f99ad68670 100644 --- a/salt/utils/pagerduty.py +++ b/salt/utils/pagerduty.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Library for interacting with PagerDuty API .. versionadded:: 2014.7.0 @@ -15,10 +15,11 @@ Library for interacting with PagerDuty API my-pagerduty-account: pagerduty.subdomain: mysubdomain pagerduty.api_key: F3Rbyjbve43rfFWf2214 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + import salt.utils.http import salt.utils.json from salt.version import __version__ @@ -26,14 +27,26 @@ from salt.version import __version__ log = logging.getLogger(__name__) -def query(method='GET', profile_dict=None, url=None, path='api/v1', - action=None, api_key=None, service=None, params=None, - data=None, subdomain=None, client_url=None, description=None, - opts=None, verify_ssl=True): - ''' +def query( + method="GET", + profile_dict=None, + url=None, + path="api/v1", + action=None, + api_key=None, + service=None, + params=None, + data=None, + subdomain=None, + client_url=None, + description=None, + opts=None, + verify_ssl=True, +): + """ Query the PagerDuty API - ''' - user_agent = 'SaltStack {0}'.format(__version__) + """ + user_agent = "SaltStack {0}".format(__version__) if opts is None: opts = {} @@ -44,24 +57,20 @@ def query(method='GET', profile_dict=None, url=None, path='api/v1', creds = {} if api_key is not None: - creds['pagerduty.api_key'] = api_key + creds["pagerduty.api_key"] = api_key if service is not None: - creds['pagerduty.service'] = service + creds["pagerduty.service"] = service if subdomain is not None: - creds['pagerduty.subdomain'] = subdomain + creds["pagerduty.subdomain"] = subdomain if client_url is None: - client_url = 'https://{0}.pagerduty.com'.format( - creds['pagerduty.subdomain'] - ) + client_url = "https://{0}.pagerduty.com".format(creds["pagerduty.subdomain"]) if url is None: - url = 'https://{0}.pagerduty.com/{1}/{2}'.format( - creds['pagerduty.subdomain'], - path, - action + url = "https://{0}.pagerduty.com/{1}/{2}".format( + creds["pagerduty.subdomain"], path, action ) if params is None: @@ -70,29 +79,29 @@ def query(method='GET', profile_dict=None, url=None, path='api/v1', if data is None: data = {} - data['client'] = user_agent + data["client"] = user_agent # pagerduty.service is not documented. While it makes sense to have in # some cases, don't force it when it is not defined. - if 'pagerduty.service' in creds and creds['pagerduty.service'] is not None: - data['service_key'] = creds['pagerduty.service'] - data['client_url'] = client_url - if 'event_type' not in data: - data['event_type'] = 'trigger' - if 'description' not in data: + if "pagerduty.service" in creds and creds["pagerduty.service"] is not None: + data["service_key"] = creds["pagerduty.service"] + data["client_url"] = client_url + if "event_type" not in data: + data["event_type"] = "trigger" + if "description" not in data: if not description: - data['description'] = 'SaltStack Event Triggered' + data["description"] = "SaltStack Event Triggered" else: - data['description'] = description + data["description"] = description headers = { - 'User-Agent': user_agent, - 'Authorization': 'Token token={0}'.format(creds['pagerduty.api_key']) + "User-Agent": user_agent, + "Authorization": "Token token={0}".format(creds["pagerduty.api_key"]), } - if method == 'GET': + if method == "GET": data = {} else: - headers['Content-type'] = 'application/json' + headers["Content-type"] = "application/json" result = salt.utils.http.query( url, @@ -105,20 +114,17 @@ def query(method='GET', profile_dict=None, url=None, path='api/v1', opts=opts, ) - return result['text'] + return result["text"] def list_items(action, key, profile_dict=None, api_key=None, opts=None): - ''' + """ List items belonging to an API call. Used for list_services() and list_incidents() - ''' - items = salt.utils.json.loads(query( - profile_dict=profile_dict, - api_key=api_key, - action=action, - opts=opts - )) + """ + items = salt.utils.json.loads( + query(profile_dict=profile_dict, api_key=api_key, action=action, opts=opts) + ) ret = {} for item in items[action]: ret[item[key]] = item diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py index fb9b0987a65..b86615801f7 100644 --- a/salt/utils/parsers.py +++ b/salt/utils/parsers.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,29 +7,29 @@ ~~~~~~~~~~~~~~~~~~ This is where all the black magic happens on all of salt's CLI tools. -''' +""" # pylint: disable=missing-docstring,protected-access,too-many-ancestors,too-few-public-methods # pylint: disable=attribute-defined-outside-init,no-self-use # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys -import types -import signal + import getpass import logging import optparse +import os +import signal +import sys import traceback +import types from functools import partial - # Import salt libs import salt.config as config import salt.defaults.exitcodes +import salt.exceptions import salt.log.setup as log import salt.syspaths as syspaths -import salt.version as version import salt.utils.args import salt.utils.data import salt.utils.files @@ -41,24 +41,21 @@ import salt.utils.user import salt.utils.win_functions import salt.utils.xdg import salt.utils.yaml +import salt.version as version from salt.defaults import DEFAULT_TARGET_DELIM -from salt.utils.validate.path import is_writeable -from salt.utils.verify import verify_log_files -import salt.exceptions from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.utils.validate.path import is_writeable +from salt.utils.verify import verify_log_files logger = logging.getLogger(__name__) def _sorted(mixins_or_funcs): - return sorted( - mixins_or_funcs, key=lambda mf: getattr(mf, '_mixin_prio_', 1000) - ) + return sorted(mixins_or_funcs, key=lambda mf: getattr(mf, "_mixin_prio_", 1000)) class MixinFuncsContainer(list): - def append(self, func): if isinstance(func, types.MethodType): # We only care about unbound methods @@ -76,7 +73,7 @@ class MixInMeta(type): def __new__(mcs, name, bases, attrs): instance = super(MixInMeta, mcs).__new__(mcs, name, bases, attrs) - if not hasattr(instance, '_mixin_setup'): + if not hasattr(instance, "_mixin_setup"): raise RuntimeError( "Don't subclass {0} in {1} if you're not going " "to use it as a salt parser mix-in.".format(mcs.__name__, name) @@ -86,58 +83,51 @@ class MixInMeta(type): class OptionParserMeta(MixInMeta): def __new__(mcs, name, bases, attrs): - instance = super(OptionParserMeta, mcs).__new__(mcs, - name, - bases, - attrs) - if not hasattr(instance, '_mixin_setup_funcs'): + instance = super(OptionParserMeta, mcs).__new__(mcs, name, bases, attrs) + if not hasattr(instance, "_mixin_setup_funcs"): instance._mixin_setup_funcs = MixinFuncsContainer() - if not hasattr(instance, '_mixin_process_funcs'): + if not hasattr(instance, "_mixin_process_funcs"): instance._mixin_process_funcs = MixinFuncsContainer() - if not hasattr(instance, '_mixin_after_parsed_funcs'): + if not hasattr(instance, "_mixin_after_parsed_funcs"): instance._mixin_after_parsed_funcs = MixinFuncsContainer() - if not hasattr(instance, '_mixin_before_exit_funcs'): + if not hasattr(instance, "_mixin_before_exit_funcs"): instance._mixin_before_exit_funcs = MixinFuncsContainer() for base in _sorted(bases + (instance,)): - func = getattr(base, '_mixin_setup', None) + func = getattr(base, "_mixin_setup", None) if func is not None and func not in instance._mixin_setup_funcs: instance._mixin_setup_funcs.append(func) - func = getattr(base, '_mixin_after_parsed', None) - if func is not None and func not in \ - instance._mixin_after_parsed_funcs: + func = getattr(base, "_mixin_after_parsed", None) + if func is not None and func not in instance._mixin_after_parsed_funcs: instance._mixin_after_parsed_funcs.append(func) - func = getattr(base, '_mixin_before_exit', None) - if func is not None and func not in \ - instance._mixin_before_exit_funcs: + func = getattr(base, "_mixin_before_exit", None) + if func is not None and func not in instance._mixin_before_exit_funcs: instance._mixin_before_exit_funcs.append(func) # Mark process_<opt> functions with the base priority for sorting for func in dir(base): - if not func.startswith('process_'): + if not func.startswith("process_"): continue func = getattr(base, func) - if getattr(func, '_mixin_prio_', None) is not None: + if getattr(func, "_mixin_prio_", None) is not None: # Function already has the attribute set, don't override it continue if six.PY2: - func.__func__._mixin_prio_ = getattr( - base, '_mixin_prio_', 1000 - ) + func.__func__._mixin_prio_ = getattr(base, "_mixin_prio_", 1000) else: - func._mixin_prio_ = getattr( - base, '_mixin_prio_', 1000 - ) + func._mixin_prio_ = getattr(base, "_mixin_prio_", 1000) return instance class CustomOption(optparse.Option, object): - def take_action(self, action, dest, *args, **kwargs): # pylint: disable=arguments-differ + def take_action( + self, action, dest, *args, **kwargs + ): # pylint: disable=arguments-differ # see https://github.com/python/cpython/blob/master/Lib/optparse.py#L786 self.explicit = True return optparse.Option.take_action(self, action, dest, *args, **kwargs) @@ -146,10 +136,12 @@ class CustomOption(optparse.Option, object): class OptionParser(optparse.OptionParser, object): VERSION = version.__saltstack_version__.formatted_version - usage = '%prog [options]' + usage = "%prog [options]" - epilog = ('You can find additional help about %prog issuing "man %prog" ' - 'or on http://docs.saltstack.com') + epilog = ( + 'You can find additional help about %prog issuing "man %prog" ' + "or on http://docs.saltstack.com" + ) description = None # Private attributes @@ -159,19 +151,19 @@ class OptionParser(optparse.OptionParser, object): _setup_mp_logging_listener_ = False def __init__(self, *args, **kwargs): - kwargs.setdefault('version', '%prog {0}'.format(self.VERSION)) - kwargs.setdefault('usage', self.usage) + kwargs.setdefault("version", "%prog {0}".format(self.VERSION)) + kwargs.setdefault("usage", self.usage) if self.description: - kwargs.setdefault('description', self.description) + kwargs.setdefault("description", self.description) if self.epilog: - kwargs.setdefault('epilog', self.epilog) + kwargs.setdefault("epilog", self.epilog) - kwargs.setdefault('option_class', CustomOption) + kwargs.setdefault("option_class", CustomOption) optparse.OptionParser.__init__(self, *args, **kwargs) - if self.epilog and '%prog' in self.epilog: - self.epilog = self.epilog.replace('%prog', self.get_prog_name()) + if self.epilog and "%prog" in self.epilog: + self.epilog = self.epilog.replace("%prog", self.get_prog_name()) def add_option_group(self, *args, **kwargs): option_group = optparse.OptionParser.add_option_group(self, *args, **kwargs) @@ -180,14 +172,12 @@ class OptionParser(optparse.OptionParser, object): def parse_args(self, args=None, values=None): options, args = optparse.OptionParser.parse_args(self, args, values) - if 'args_stdin' in options.__dict__ and options.args_stdin is True: + if "args_stdin" in options.__dict__ and options.args_stdin is True: # Read additional options and/or arguments from stdin and combine # them with the options and arguments from the command line. new_inargs = sys.stdin.readlines() - new_inargs = [arg.rstrip('\r\n') for arg in new_inargs] - new_options, new_args = optparse.OptionParser.parse_args( - self, - new_inargs) + new_inargs = [arg.rstrip("\r\n") for arg in new_inargs] + new_options, new_args = optparse.OptionParser.parse_args(self, new_inargs) options.__dict__.update(new_options.__dict__) args.extend(new_args) @@ -202,17 +192,13 @@ class OptionParser(optparse.OptionParser, object): # Let's get some proper sys.stderr logging as soon as possible!!! # This logging handler will be removed once the proper console or # logfile logging is setup. - temp_log_level = getattr(self.options, 'log_level', None) - log.setup_temp_logger( - 'error' if temp_log_level is None else temp_log_level - ) + temp_log_level = getattr(self.options, "log_level", None) + log.setup_temp_logger("error" if temp_log_level is None else temp_log_level) # Gather and run the process_<option> functions in the proper order process_option_funcs = [] for option_key in options.__dict__: - process_option_func = getattr( - self, 'process_{0}'.format(option_key), None - ) + process_option_func = getattr(self, "process_{0}".format(option_key), None) if process_option_func is not None: process_option_funcs.append(process_option_func) @@ -222,27 +208,29 @@ class OptionParser(optparse.OptionParser, object): except Exception as err: # pylint: disable=broad-except logger.exception(err) self.error( - 'Error while processing {0}: {1}'.format( + "Error while processing {0}: {1}".format( process_option_func, traceback.format_exc(err) ) ) # Run the functions on self._mixin_after_parsed_funcs - for mixin_after_parsed_func in self._mixin_after_parsed_funcs: # pylint: disable=no-member + for ( + mixin_after_parsed_func + ) in self._mixin_after_parsed_funcs: # pylint: disable=no-member try: mixin_after_parsed_func(self) except Exception as err: # pylint: disable=broad-except logger.exception(err) self.error( - 'Error while processing {0}: {1}'.format( + "Error while processing {0}: {1}".format( mixin_after_parsed_func, traceback.format_exc(err) ) ) - if self.config.get('conf_file', None) is not None: # pylint: disable=no-member + if self.config.get("conf_file", None) is not None: # pylint: disable=no-member logger.debug( - 'Configuration file path: %s', - self.config['conf_file'] # pylint: disable=no-member + "Configuration file path: %s", + self.config["conf_file"], # pylint: disable=no-member ) # Retain the standard behavior of optparse to return options and args return options, args @@ -257,57 +245,67 @@ class OptionParser(optparse.OptionParser, object): def _add_version_option(self): optparse.OptionParser._add_version_option(self) self.add_option( - '--versions-report', - '-V', - action='store_true', - help='Show program\'s dependencies version number and exit.' + "--versions-report", + "-V", + action="store_true", + help="Show program's dependencies version number and exit.", ) - def print_versions_report(self, file=sys.stdout): # pylint: disable=redefined-builtin - print('\n'.join(version.versions_report()), file=file) + def print_versions_report( + self, file=sys.stdout + ): # pylint: disable=redefined-builtin + print("\n".join(version.versions_report()), file=file) self.exit(salt.defaults.exitcodes.EX_OK) def exit(self, status=0, msg=None): # Run the functions on self._mixin_after_parsed_funcs - for mixin_before_exit_func in self._mixin_before_exit_funcs: # pylint: disable=no-member + for ( + mixin_before_exit_func + ) in self._mixin_before_exit_funcs: # pylint: disable=no-member try: mixin_before_exit_func(self) except Exception as err: # pylint: disable=broad-except logger.exception(err) - logger.error('Error while processing %s: %s', - six.text_type(mixin_before_exit_func), - traceback.format_exc(err)) + logger.error( + "Error while processing %s: %s", + six.text_type(mixin_before_exit_func), + traceback.format_exc(err), + ) if self._setup_mp_logging_listener_ is True: # Stop logging through the queue log.shutdown_multiprocessing_logging() # Stop the logging queue listener process log.shutdown_multiprocessing_logging_listener(daemonizing=True) - if isinstance(msg, six.string_types) and msg and msg[-1] != '\n': - msg = '{0}\n'.format(msg) + if isinstance(msg, six.string_types) and msg and msg[-1] != "\n": + msg = "{0}\n".format(msg) optparse.OptionParser.exit(self, status, msg) def error(self, msg): - ''' + """ error(msg : string) Print a usage message incorporating 'msg' to stderr and exit. This keeps option parsing exit status uniform for all parsing errors. - ''' + """ self.print_usage(sys.stderr) - self.exit(salt.defaults.exitcodes.EX_USAGE, '{0}: error: {1}\n'.format(self.get_prog_name(), msg)) + self.exit( + salt.defaults.exitcodes.EX_USAGE, + "{0}: error: {1}\n".format(self.get_prog_name(), msg), + ) class MergeConfigMixIn(six.with_metaclass(MixInMeta, object)): - ''' + """ This mix-in will simply merge the CLI-passed options, by overriding the configuration file loaded settings. This mix-in should run last. - ''' + """ + _mixin_prio_ = six.MAXSIZE def _mixin_setup(self): - if not hasattr(self, 'setup_config') and not hasattr(self, 'config'): + if not hasattr(self, "setup_config") and not hasattr(self, "config"): # No configuration was loaded on this parser. # There's nothing to do here. return @@ -334,7 +332,7 @@ class MergeConfigMixIn(six.with_metaclass(MixInMeta, object)): if value is not None: # There's an actual value, add it to the config self.config[option.dest] = value - elif value is not None and getattr(option, 'explicit', False): + elif value is not None and getattr(option, "explicit", False): # Only set the value in the config file IF it was explicitly # specified by the user, this makes it possible to tweak settings # on the configuration files bypassing the shell option flags' @@ -359,7 +357,7 @@ class MergeConfigMixIn(six.with_metaclass(MixInMeta, object)): if value is not None: # There's an actual value, add it to the config self.config[option.dest] = value - elif value is not None and getattr(option, 'explicit', False): + elif value is not None and getattr(option, "explicit", False): # Only set the value in the config file IF it was explicitly # specified by the user, this makes it possible to tweak # settings on the configuration files bypassing the shell @@ -369,9 +367,7 @@ class MergeConfigMixIn(six.with_metaclass(MixInMeta, object)): # Let's update the option value with the one from the # configuration file. This allows the parsers to make use # of the updated value by using self.options.<option> - setattr(self.options, - option.dest, - self.config[option.dest]) + setattr(self.options, option.dest, self.config[option.dest]) class SaltfileMixIn(six.with_metaclass(MixInMeta, object)): @@ -379,28 +375,29 @@ class SaltfileMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): self.add_option( - '--saltfile', default=None, - help='Specify the path to a Saltfile. If not passed, one will be ' - 'searched for in the current working directory.' + "--saltfile", + default=None, + help="Specify the path to a Saltfile. If not passed, one will be " + "searched for in the current working directory.", ) def process_saltfile(self): if self.options.saltfile is None: # No one passed a Saltfile as an option, environment variable!? - self.options.saltfile = os.environ.get('SALT_SALTFILE', None) + self.options.saltfile = os.environ.get("SALT_SALTFILE", None) if self.options.saltfile is None: # If we're here, no one passed a Saltfile either to the CLI tool or # as an environment variable. # Is there a Saltfile in the current directory? try: # cwd may not exist if it was removed but salt was run from it - saltfile = os.path.join(os.getcwd(), 'Saltfile') + saltfile = os.path.join(os.getcwd(), "Saltfile") except OSError: - saltfile = '' + saltfile = "" if os.path.isfile(saltfile): self.options.saltfile = saltfile else: - saltfile = os.path.join(os.path.expanduser("~"), '.salt', 'Saltfile') + saltfile = os.path.join(os.path.expanduser("~"), ".salt", "Saltfile") if os.path.isfile(saltfile): self.options.saltfile = saltfile else: @@ -423,8 +420,10 @@ class SaltfileMixIn(six.with_metaclass(MixInMeta, object)): saltfile_config = config._read_conf_file(saltfile) except salt.exceptions.SaltConfigurationError as error: self.error(error.message) - self.exit(salt.defaults.exitcodes.EX_GENERIC, - '{0}: error: {1}\n'.format(self.get_prog_name(), error.message)) + self.exit( + salt.defaults.exitcodes.EX_GENERIC, + "{0}: error: {1}\n".format(self.get_prog_name(), error.message), + ) if not saltfile_config: # No configuration was loaded from the Saltfile @@ -493,10 +492,12 @@ class HardCrashMixin(six.with_metaclass(MixInMeta, object)): _config_filename_ = None def _mixin_setup(self): - hard_crash = os.environ.get('SALT_HARD_CRASH', False) + hard_crash = os.environ.get("SALT_HARD_CRASH", False) self.add_option( - '--hard-crash', action='store_true', default=hard_crash, - help='Raise any original exception rather than exiting gracefully. Default: %default.' + "--hard-crash", + action="store_true", + default=hard_crash, + help="Raise any original exception rather than exiting gracefully. Default: %default.", ) @@ -504,19 +505,21 @@ class NoParseMixin(six.with_metaclass(MixInMeta, object)): _mixin_prio_ = 50 def _mixin_setup(self): - no_parse = os.environ.get('SALT_NO_PARSE', '') + no_parse = os.environ.get("SALT_NO_PARSE", "") self.add_option( - '--no-parse', default=no_parse, - help='Comma-separated list of named CLI arguments (i.e. argname=value) ' - 'which should not be parsed as Python data types', - metavar='argname1,argname2,...', + "--no-parse", + default=no_parse, + help="Comma-separated list of named CLI arguments (i.e. argname=value) " + "which should not be parsed as Python data types", + metavar="argname1,argname2,...", ) def process_no_parse(self): if self.options.no_parse: try: - self.options.no_parse = \ - [x.strip() for x in self.options.no_parse.split(',')] + self.options.no_parse = [ + x.strip() for x in self.options.no_parse.split(",") + ] except AttributeError: self.options.no_parse = [] else: @@ -527,16 +530,18 @@ class ConfigDirMixIn(six.with_metaclass(MixInMeta, object)): _mixin_prio_ = -10 _config_filename_ = None _default_config_dir_ = syspaths.CONFIG_DIR - _default_config_dir_env_var_ = 'SALT_CONFIG_DIR' + _default_config_dir_env_var_ = "SALT_CONFIG_DIR" def _mixin_setup(self): config_dir = os.environ.get(self._default_config_dir_env_var_, None) if not config_dir: config_dir = self._default_config_dir_ - logger.debug('SYSPATHS setup as: %s', six.text_type(syspaths.CONFIG_DIR)) + logger.debug("SYSPATHS setup as: %s", six.text_type(syspaths.CONFIG_DIR)) self.add_option( - '-c', '--config-dir', default=config_dir, - help="Pass in an alternative configuration directory. Default: '%default'." + "-c", + "--config-dir", + default=config_dir, + help="Pass in an alternative configuration directory. Default: '%default'.", ) def process_config_dir(self): @@ -552,13 +557,13 @@ class ConfigDirMixIn(six.with_metaclass(MixInMeta, object)): # Make sure we have an absolute path self.options.config_dir = os.path.abspath(self.options.config_dir) - if hasattr(self, 'setup_config'): - if not hasattr(self, 'config'): + if hasattr(self, "setup_config"): + if not hasattr(self, "config"): self.config = {} try: self.config.update(self.setup_config()) except (IOError, OSError) as exc: - self.error('Failed to load configuration: {0}'.format(exc)) + self.error("Failed to load configuration: {0}".format(exc)) def get_config_file_path(self, configfile=None): if configfile is None: @@ -568,11 +573,13 @@ class ConfigDirMixIn(six.with_metaclass(MixInMeta, object)): class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): _mixin_prio_ = 10 - _default_logging_level_ = 'warning' + _default_logging_level_ = "warning" _default_logging_logfile_ = None - _logfile_config_setting_name_ = 'log_file' - _loglevel_config_setting_name_ = 'log_level' - _logfile_loglevel_config_setting_name_ = 'log_level_logfile' # pylint: disable=invalid-name + _logfile_config_setting_name_ = "log_file" + _loglevel_config_setting_name_ = "log_level" + _logfile_loglevel_config_setting_name_ = ( + "log_level_logfile" # pylint: disable=invalid-name + ) _skip_console_logging_config_ = False def _mixin_setup(self): @@ -580,26 +587,28 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): # This is an attribute available for programmers, so, raise a # RuntimeError to let them know about the proper usage. raise RuntimeError( - 'Please set {0}._default_logging_logfile_'.format( + "Please set {0}._default_logging_logfile_".format( self.__class__.__name__ ) ) group = self.logging_options_group = optparse.OptionGroup( - self, 'Logging Options', - 'Logging options which override any settings defined on the ' - 'configuration files.' + self, + "Logging Options", + "Logging options which override any settings defined on the " + "configuration files.", ) self.add_option_group(group) - if not getattr(self, '_skip_console_logging_config_', False): + if not getattr(self, "_skip_console_logging_config_", False): group.add_option( - '-l', '--log-level', + "-l", + "--log-level", dest=self._loglevel_config_setting_name_, choices=list(log.LOG_LEVELS), - help='Console logging log level. One of {0}. Default: \'{1}\'.'.format( - ', '.join(["'{}'".format(n) for n in log.SORTED_LEVEL_NAMES]), - self._default_logging_level_ - ) + help="Console logging log level. One of {0}. Default: '{1}'.".format( + ", ".join(["'{}'".format(n) for n in log.SORTED_LEVEL_NAMES]), + self._default_logging_level_, + ), ) def _logfile_callback(option, opt, value, parser, *args, **kwargs): @@ -609,25 +618,25 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): setattr(parser.values, self._logfile_config_setting_name_, value) group.add_option( - '--log-file', + "--log-file", dest=self._logfile_config_setting_name_, default=None, - action='callback', - type='string', + action="callback", + type="string", callback=_logfile_callback, - help='Log file path. Default: \'{0}\'.'.format( + help="Log file path. Default: '{0}'.".format( self._default_logging_logfile_ - ) + ), ) group.add_option( - '--log-file-level', + "--log-file-level", dest=self._logfile_loglevel_config_setting_name_, choices=list(log.LOG_LEVELS), - help='Logfile logging log level. One of {0}. Default: \'{1}\'.'.format( - ', '.join(["'{}'".format(n) for n in log.SORTED_LEVEL_NAMES]), - self._default_logging_level_ - ) + help="Logfile logging log level. One of {0}. Default: '{1}'.".format( + ", ".join(["'{}'".format(n) for n in log.SORTED_LEVEL_NAMES]), + self._default_logging_level_, + ), ) def process_log_level(self): @@ -635,15 +644,19 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): # Log level is not set via CLI, checking loaded configuration if self.config.get(self._loglevel_config_setting_name_, None): # Is the regular log level setting set? - setattr(self.options, - self._loglevel_config_setting_name_, - self.config.get(self._loglevel_config_setting_name_)) + setattr( + self.options, + self._loglevel_config_setting_name_, + self.config.get(self._loglevel_config_setting_name_), + ) else: # Nothing is set on the configuration? Let's use the CLI tool # defined default - setattr(self.options, - self._loglevel_config_setting_name_, - self._default_logging_level_) + setattr( + self.options, + self._loglevel_config_setting_name_, + self._default_logging_level_, + ) # Setup extended logging right before the last step self._mixin_after_parsed_funcs.append(self.__setup_extended_logging) @@ -664,15 +677,19 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): # Log file is not set via CLI, checking loaded configuration if self.config.get(self._logfile_config_setting_name_, None): # Is the regular log file setting set? - setattr(self.options, - self._logfile_config_setting_name_, - self.config.get(self._logfile_config_setting_name_)) + setattr( + self.options, + self._logfile_config_setting_name_, + self.config.get(self._logfile_config_setting_name_), + ) else: # Nothing is set on the configuration? Let's use the CLI tool # defined default - setattr(self.options, - self._logfile_config_setting_name_, - self._default_logging_logfile_) + setattr( + self.options, + self._logfile_config_setting_name_, + self._default_logging_logfile_, + ) if self._logfile_config_setting_name_ in self.config: # Remove it from config so it inherits from log_file self.config.pop(self._logfile_config_setting_name_) @@ -682,107 +699,107 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): # Log file level is not set via CLI, checking loaded configuration if self.config.get(self._logfile_loglevel_config_setting_name_, None): # Is the regular log file level setting set? - setattr(self.options, - self._logfile_loglevel_config_setting_name_, - self.config.get(self._logfile_loglevel_config_setting_name_)) + setattr( + self.options, + self._logfile_loglevel_config_setting_name_, + self.config.get(self._logfile_loglevel_config_setting_name_), + ) else: # Nothing is set on the configuration? Let's use the CLI tool # defined default - setattr(self.options, - self._logfile_loglevel_config_setting_name_, - # From the console log level config setting - self.config.get( - self._loglevel_config_setting_name_, - self._default_logging_level_ - )) + setattr( + self.options, + self._logfile_loglevel_config_setting_name_, + # From the console log level config setting + self.config.get( + self._loglevel_config_setting_name_, + self._default_logging_level_, + ), + ) if self._logfile_loglevel_config_setting_name_ in self.config: # Remove it from config so it inherits from log_level_logfile self.config.pop(self._logfile_loglevel_config_setting_name_) def __setup_logfile_logger_config(self): - if self._logfile_loglevel_config_setting_name_ in self.config and not \ - self.config.get(self._logfile_loglevel_config_setting_name_): + if ( + self._logfile_loglevel_config_setting_name_ in self.config + and not self.config.get(self._logfile_loglevel_config_setting_name_) + ): # Remove it from config so it inherits from log_level self.config.pop(self._logfile_loglevel_config_setting_name_) - loglevel = getattr(self.options, - # From the options setting - self._logfile_loglevel_config_setting_name_, - # From the default setting - self._default_logging_level_ - ) - - logfile = getattr(self.options, - # From the options setting - self._logfile_config_setting_name_, - # From the default setting - self._default_logging_logfile_ - ) - - cli_log_path = 'cli_{0}_log_file'.format( - self.get_prog_name().replace('-', '_') + loglevel = getattr( + self.options, + # From the options setting + self._logfile_loglevel_config_setting_name_, + # From the default setting + self._default_logging_level_, ) + + logfile = getattr( + self.options, + # From the options setting + self._logfile_config_setting_name_, + # From the default setting + self._default_logging_logfile_, + ) + + cli_log_path = "cli_{0}_log_file".format(self.get_prog_name().replace("-", "_")) if cli_log_path in self.config and not self.config.get(cli_log_path): # Remove it from config so it inherits from log_level_logfile self.config.pop(cli_log_path) - if self._logfile_config_setting_name_ in self.config and not \ - self.config.get(self._logfile_config_setting_name_): + if self._logfile_config_setting_name_ in self.config and not self.config.get( + self._logfile_config_setting_name_ + ): # Remove it from config so it inherits from log_file self.config.pop(self._logfile_config_setting_name_) - if self.config['verify_env'] and self.config['log_level'] not in ('quiet', ): + if self.config["verify_env"] and self.config["log_level"] not in ("quiet",): # Verify the logfile if it was explicitly set but do not try to # verify the default if logfile is not None: # Logfile is not using Syslog, verify with salt.utils.files.set_umask(0o027): - verify_log_files([logfile], self.config['user']) + verify_log_files([logfile], self.config["user"]) if logfile is None: # Use the default setting if the logfile wasn't explicity set logfile = self._default_logging_logfile_ - cli_log_file_fmt = 'cli_{0}_log_file_fmt'.format( - self.get_prog_name().replace('-', '_') + cli_log_file_fmt = "cli_{0}_log_file_fmt".format( + self.get_prog_name().replace("-", "_") ) - if cli_log_file_fmt in self.config and not \ - self.config.get(cli_log_file_fmt): + if cli_log_file_fmt in self.config and not self.config.get(cli_log_file_fmt): # Remove it from config so it inherits from log_fmt_logfile self.config.pop(cli_log_file_fmt) - if self.config.get('log_fmt_logfile', None) is None: + if self.config.get("log_fmt_logfile", None) is None: # Remove it from config so it inherits from log_fmt_console - self.config.pop('log_fmt_logfile', None) + self.config.pop("log_fmt_logfile", None) log_file_fmt = self.config.get( - 'log_fmt_logfile', + "log_fmt_logfile", self.config.get( - 'log_fmt_console', - self.config.get( - 'log_fmt', - config._DFLT_LOG_FMT_CONSOLE - ) - ) + "log_fmt_console", + self.config.get("log_fmt", config._DFLT_LOG_FMT_CONSOLE), + ), ) - if self.config.get('log_datefmt_logfile', None) is None: + if self.config.get("log_datefmt_logfile", None) is None: # Remove it from config so it inherits from log_datefmt_console - self.config.pop('log_datefmt_logfile', None) + self.config.pop("log_datefmt_logfile", None) - if self.config.get('log_datefmt_console', None) is None: + if self.config.get("log_datefmt_console", None) is None: # Remove it from config so it inherits from log_datefmt - self.config.pop('log_datefmt_console', None) + self.config.pop("log_datefmt_console", None) log_file_datefmt = self.config.get( - 'log_datefmt_logfile', + "log_datefmt_logfile", self.config.get( - 'log_datefmt_console', - self.config.get( - 'log_datefmt', - '%Y-%m-%d %H:%M:%S' - ) - ) + "log_datefmt_console", + self.config.get("log_datefmt", "%Y-%m-%d %H:%M:%S"), + ), ) if not is_writeable(logfile, check_parent=True): @@ -790,38 +807,42 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): # directory (if the log file does not exit), are we the same user # as the one defined in the configuration file? current_user = salt.utils.user.get_user() - if self.config['user'] != current_user: + if self.config["user"] != current_user: # Yep, not the same user! # Is the current user in ACL? - acl = self.config['publisher_acl'] + acl = self.config["publisher_acl"] if salt.utils.stringutils.check_whitelist_blacklist( - current_user, whitelist=six.iterkeys(acl)): + current_user, whitelist=six.iterkeys(acl) + ): # Yep, the user is in ACL! # Let's write the logfile to its home directory instead. xdg_dir = salt.utils.xdg.xdg_config_dir() - user_salt_dir = (xdg_dir if os.path.isdir(xdg_dir) else - os.path.expanduser('~/.salt')) + user_salt_dir = ( + xdg_dir + if os.path.isdir(xdg_dir) + else os.path.expanduser("~/.salt") + ) if not os.path.isdir(user_salt_dir): os.makedirs(user_salt_dir, 0o750) - logfile_basename = os.path.basename( - self._default_logging_logfile_ + logfile_basename = os.path.basename(self._default_logging_logfile_) + logger.debug( + "The user '%s' is not allowed to write to '%s'. " + "The log file will be stored in '~/.salt/'%s'.log'", + six.text_type(current_user), + six.text_type(logfile), + six.text_type(logfile_basename), ) - logger.debug("The user '%s' is not allowed to write to '%s'. " - "The log file will be stored in '~/.salt/'%s'.log'", - six.text_type(current_user), - six.text_type(logfile), - six.text_type(logfile_basename)) logfile = os.path.join( - user_salt_dir, '{0}.log'.format(logfile_basename) + user_salt_dir, "{0}.log".format(logfile_basename) ) # If we haven't changed the logfile path and it's not writeable, # salt will fail once we try to setup the logfile logging. # Log rotate options - log_rotate_max_bytes = self.config.get('log_rotate_max_bytes', 0) - log_rotate_backup_count = self.config.get('log_rotate_backup_count', 0) + log_rotate_max_bytes = self.config.get("log_rotate_max_bytes", 0) + log_rotate_backup_count = self.config.get("log_rotate_backup_count", 0) if not salt.utils.platform.is_windows(): # Not supported on platforms other than Windows. # Other platforms may use an external tool such as 'logrotate' @@ -835,10 +856,10 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): # Save the settings back to the configuration self.config[self._logfile_config_setting_name_] = logfile self.config[self._logfile_loglevel_config_setting_name_] = loglevel - self.config['log_fmt_logfile'] = log_file_fmt - self.config['log_datefmt_logfile'] = log_file_datefmt - self.config['log_rotate_max_bytes'] = log_rotate_max_bytes - self.config['log_rotate_backup_count'] = log_rotate_backup_count + self.config["log_fmt_logfile"] = log_file_fmt + self.config["log_datefmt_logfile"] = log_file_datefmt + self.config["log_rotate_max_bytes"] = log_rotate_max_bytes + self.config["log_rotate_backup_count"] = log_rotate_backup_count def setup_logfile_logger(self): if salt.utils.platform.is_windows() and self._setup_mp_logging_listener_: @@ -848,10 +869,10 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): logfile = self.config[self._logfile_config_setting_name_] loglevel = self.config[self._logfile_loglevel_config_setting_name_] - log_file_fmt = self.config['log_fmt_logfile'] - log_file_datefmt = self.config['log_datefmt_logfile'] - log_rotate_max_bytes = self.config['log_rotate_max_bytes'] - log_rotate_backup_count = self.config['log_rotate_backup_count'] + log_file_fmt = self.config["log_fmt_logfile"] + log_file_datefmt = self.config["log_datefmt_logfile"] + log_rotate_max_bytes = self.config["log_rotate_max_bytes"] + log_rotate_backup_count = self.config["log_rotate_backup_count"] log.setup_logfile_logger( logfile, @@ -859,9 +880,9 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): log_format=log_file_fmt, date_format=log_file_datefmt, max_bytes=log_rotate_max_bytes, - backup_count=log_rotate_backup_count + backup_count=log_rotate_backup_count, ) - for name, level in six.iteritems(self.config.get('log_granular_levels', {})): + for name, level in six.iteritems(self.config.get("log_granular_levels", {})): log.set_logger_level(name, level) def __setup_extended_logging(self): @@ -877,8 +898,7 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): def _setup_mp_logging_listener(self): if self._setup_mp_logging_listener_: log.setup_multiprocessing_logging_listener( - self.config, - self._get_mp_logging_listener_queue() + self.config, self._get_mp_logging_listener_queue() ) def _setup_mp_logging_client(self): @@ -897,9 +917,7 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): # This will allow log file rotation on Windows # since only one process can own the log file # for log file rotation to work. - log.setup_multiprocessing_logging( - self._get_mp_logging_listener_queue() - ) + log.setup_multiprocessing_logging(self._get_mp_logging_listener_queue()) # Remove the temp logger and any other configured loggers since # all of our logging is going through the multiprocessing # logging listener. @@ -910,32 +928,24 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): def __setup_console_logger_config(self): # Since we're not going to be a daemon, setup the console logger logfmt = self.config.get( - 'log_fmt_console', - self.config.get( - 'log_fmt', - config._DFLT_LOG_FMT_CONSOLE - ) + "log_fmt_console", self.config.get("log_fmt", config._DFLT_LOG_FMT_CONSOLE) ) - if self.config.get('log_datefmt_console', None) is None: + if self.config.get("log_datefmt_console", None) is None: # Remove it from config so it inherits from log_datefmt - self.config.pop('log_datefmt_console', None) + self.config.pop("log_datefmt_console", None) datefmt = self.config.get( - 'log_datefmt_console', - self.config.get( - 'log_datefmt', - '%Y-%m-%d %H:%M:%S' - ) + "log_datefmt_console", self.config.get("log_datefmt", "%Y-%m-%d %H:%M:%S") ) # Save the settings back to the configuration - self.config['log_fmt_console'] = logfmt - self.config['log_datefmt_console'] = datefmt + self.config["log_fmt_console"] = logfmt + self.config["log_datefmt_console"] = datefmt def __setup_console_logger(self): # If daemon is set force console logger to quiet - if getattr(self.options, 'daemon', False) is True: + if getattr(self.options, "daemon", False) is True: return if salt.utils.platform.is_windows() and self._setup_mp_logging_listener_: @@ -944,17 +954,17 @@ class LogLevelMixIn(six.with_metaclass(MixInMeta, object)): return # ensure that yaml stays valid with log output - if getattr(self.options, 'output', None) == 'yaml': - log_format = '# {0}'.format(self.config['log_fmt_console']) + if getattr(self.options, "output", None) == "yaml": + log_format = "# {0}".format(self.config["log_fmt_console"]) else: - log_format = self.config['log_fmt_console'] + log_format = self.config["log_fmt_console"] log.setup_console_logger( - self.config['log_level'], + self.config["log_level"], log_format=log_format, - date_format=self.config['log_datefmt_console'] + date_format=self.config["log_datefmt_console"], ) - for name, level in six.iteritems(self.config.get('log_granular_levels', {})): + for name, level in six.iteritems(self.config.get("log_granular_levels", {})): log.set_logger_level(name, level) @@ -963,8 +973,7 @@ class RunUserMixin(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): self.add_option( - '-u', '--user', - help='Specify user to run {0}.'.format(self.get_prog_name()) + "-u", "--user", help="Specify user to run {0}.".format(self.get_prog_name()) ) @@ -973,32 +982,36 @@ class DaemonMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): self.add_option( - '-d', '--daemon', + "-d", + "--daemon", default=False, - action='store_true', - help='Run the {0} as a daemon.'.format(self.get_prog_name()) + action="store_true", + help="Run the {0} as a daemon.".format(self.get_prog_name()), ) self.add_option( - '--pid-file', dest='pidfile', + "--pid-file", + dest="pidfile", default=os.path.join( - syspaths.PIDFILE_DIR, '{0}.pid'.format(self.get_prog_name()) + syspaths.PIDFILE_DIR, "{0}.pid".format(self.get_prog_name()) ), - help="Specify the location of the pidfile. Default: '%default'." + help="Specify the location of the pidfile. Default: '%default'.", ) def _mixin_before_exit(self): - if hasattr(self, 'config') and self.config.get('pidfile'): + if hasattr(self, "config") and self.config.get("pidfile"): # We've loaded and merged options into the configuration, it's safe # to query about the pidfile if self.check_pidfile(): try: - os.unlink(self.config['pidfile']) + os.unlink(self.config["pidfile"]) except OSError as err: # Log error only when running salt-master as a root user. # Otherwise this can be ignored, since salt-master is able to # overwrite the PIDfile on the next start. - err_msg = ('PIDfile could not be deleted: %s', - six.text_type(self.config['pidfile'])) + err_msg = ( + "PIDfile could not be deleted: %s", + six.text_type(self.config["pidfile"]), + ) if salt.utils.platform.is_windows(): user = salt.utils.win_functions.get_current_user() if salt.utils.win_functions.is_admin(user): @@ -1011,21 +1024,24 @@ class DaemonMixIn(six.with_metaclass(MixInMeta, object)): def set_pidfile(self): from salt.utils.process import set_pidfile - set_pidfile(self.config['pidfile'], self.config['user']) + + set_pidfile(self.config["pidfile"], self.config["user"]) def check_pidfile(self): - ''' + """ Report whether a pidfile exists - ''' + """ from salt.utils.process import check_pidfile - return check_pidfile(self.config['pidfile']) + + return check_pidfile(self.config["pidfile"]) def get_pidfile(self): - ''' + """ Return a pid contained in a pidfile - ''' + """ from salt.utils.process import get_pidfile - return get_pidfile(self.config['pidfile']) + + return get_pidfile(self.config["pidfile"]) def daemonize_if_required(self): if self.options.daemon: @@ -1041,24 +1057,33 @@ class DaemonMixIn(six.with_metaclass(MixInMeta, object)): self._setup_mp_logging_listener() def check_running(self): - ''' + """ Check if a pid file exists and if it is associated with a running process. - ''' + """ if self.check_pidfile(): pid = self.get_pidfile() if not salt.utils.platform.is_windows(): - if self.check_pidfile() and self.is_daemonized(pid) and os.getppid() != pid: + if ( + self.check_pidfile() + and self.is_daemonized(pid) + and os.getppid() != pid + ): return True else: # We have no os.getppid() on Windows. Use salt.utils.win_functions.get_parent_pid - if self.check_pidfile() and self.is_daemonized(pid) and salt.utils.win_functions.get_parent_pid() != pid: + if ( + self.check_pidfile() + and self.is_daemonized(pid) + and salt.utils.win_functions.get_parent_pid() != pid + ): return True return False def is_daemonized(self, pid): from salt.utils.process import os_is_running + return os_is_running(pid) # Common methods for scripts which can daemonize @@ -1076,11 +1101,11 @@ class DaemonMixIn(six.with_metaclass(MixInMeta, object)): def _handle_signals(self, signum, sigframe): # pylint: disable=unused-argument msg = self.__class__.__name__ if signum == signal.SIGINT: - msg += ' received a SIGINT.' + msg += " received a SIGINT." elif signum == signal.SIGTERM: - msg += ' received a SIGTERM.' - logging.getLogger(__name__).warning('%s Exiting.', msg) - self.shutdown(exitmsg='{0} Exited.'.format(msg)) + msg += " received a SIGTERM." + logging.getLogger(__name__).warning("%s Exiting.", msg) + self.shutdown(exitmsg="{0} Exited.".format(msg)) def shutdown(self, exitcode=0, exitmsg=None): self.exit(exitcode, exitmsg) @@ -1094,104 +1119,129 @@ class TargetOptionsMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): group = self.target_options_group = optparse.OptionGroup( - self, 'Target Options', 'Target selection options.' + self, "Target Options", "Target selection options." ) self.add_option_group(group) group.add_option( - '-H', '--hosts', + "-H", + "--hosts", default=False, - action='store_true', - dest='list_hosts', - help='List all known hosts to currently visible or other specified rosters' + action="store_true", + dest="list_hosts", + help="List all known hosts to currently visible or other specified rosters", ) group.add_option( - '-E', '--pcre', + "-E", + "--pcre", default=False, - action='store_true', - help=('Instead of using shell globs to evaluate the target ' - 'servers, use pcre regular expressions.') + action="store_true", + help=( + "Instead of using shell globs to evaluate the target " + "servers, use pcre regular expressions." + ), ) group.add_option( - '-L', '--list', + "-L", + "--list", default=False, - action='store_true', - help=('Instead of using shell globs to evaluate the target ' - 'servers, take a comma or whitespace delimited list of ' - 'servers.') + action="store_true", + help=( + "Instead of using shell globs to evaluate the target " + "servers, take a comma or whitespace delimited list of " + "servers." + ), ) group.add_option( - '-G', '--grain', + "-G", + "--grain", default=False, - action='store_true', - help=('Instead of using shell globs to evaluate the target ' - 'use a grain value to identify targets, the syntax ' - 'for the target is the grain key followed by a glob' - 'expression: "os:Arch*".') + action="store_true", + help=( + "Instead of using shell globs to evaluate the target " + "use a grain value to identify targets, the syntax " + "for the target is the grain key followed by a glob" + 'expression: "os:Arch*".' + ), ) group.add_option( - '-P', '--grain-pcre', + "-P", + "--grain-pcre", default=False, - action='store_true', - help=('Instead of using shell globs to evaluate the target ' - 'use a grain value to identify targets, the syntax ' - 'for the target is the grain key followed by a pcre ' - 'regular expression: "os:Arch.*".') + action="store_true", + help=( + "Instead of using shell globs to evaluate the target " + "use a grain value to identify targets, the syntax " + "for the target is the grain key followed by a pcre " + 'regular expression: "os:Arch.*".' + ), ) group.add_option( - '-N', '--nodegroup', + "-N", + "--nodegroup", default=False, - action='store_true', - help=('Instead of using shell globs to evaluate the target ' - 'use one of the predefined nodegroups to identify a ' - 'list of targets.') + action="store_true", + help=( + "Instead of using shell globs to evaluate the target " + "use one of the predefined nodegroups to identify a " + "list of targets." + ), ) group.add_option( - '-R', '--range', + "-R", + "--range", default=False, - action='store_true', - help=('Instead of using shell globs to evaluate the target ' - 'use a range expression to identify targets. ' - 'Range expressions look like %cluster.') + action="store_true", + help=( + "Instead of using shell globs to evaluate the target " + "use a range expression to identify targets. " + "Range expressions look like %cluster." + ), ) group = self.additional_target_options_group = optparse.OptionGroup( self, - 'Additional Target Options', - 'Additional options for minion targeting.' + "Additional Target Options", + "Additional options for minion targeting.", ) self.add_option_group(group) group.add_option( - '--delimiter', + "--delimiter", default=DEFAULT_TARGET_DELIM, - help=('Change the default delimiter for matching in multi-level ' - 'data structures. Default: \'%default\'.') + help=( + "Change the default delimiter for matching in multi-level " + "data structures. Default: '%default'." + ), ) self._create_process_functions() def _create_process_functions(self): for option in self.target_options_group.option_list: + def process(opt): if getattr(self.options, opt.dest): self.selected_target_option = opt.dest - funcname = 'process_{0}'.format(option.dest) + funcname = "process_{0}".format(option.dest) if not hasattr(self, funcname): setattr(self, funcname, partial(process, option)) def _mixin_after_parsed(self): group_options_selected = [ - option for option in self.target_options_group.option_list if - getattr(self.options, option.dest) is True + option + for option in self.target_options_group.option_list + if getattr(self.options, option.dest) is True ] if len(group_options_selected) > 1: self.error( - 'The options {0} are mutually exclusive. Please only choose ' - 'one of them'.format('/'.join( - [option.get_opt_string() - for option in group_options_selected])) + "The options {0} are mutually exclusive. Please only choose " + "one of them".format( + "/".join( + [option.get_opt_string() for option in group_options_selected] + ) + ) ) - self.config['selected_target_option'] = self.selected_target_option + self.config["selected_target_option"] = self.selected_target_option class ExtendedTargetOptionsMixIn(TargetOptionsMixIn): @@ -1199,64 +1249,77 @@ class ExtendedTargetOptionsMixIn(TargetOptionsMixIn): TargetOptionsMixIn._mixin_setup(self) group = self.target_options_group group.add_option( - '-C', '--compound', + "-C", + "--compound", default=False, - action='store_true', - help=('The compound target option allows for multiple target ' - 'types to be evaluated, allowing for greater granularity in ' - 'target matching. The compound target is space delimited, ' - 'targets other than globs are preceded with an identifier ' - 'matching the specific targets argument type: salt ' - '\'G@os:RedHat and webser* or E@database.*\'.') + action="store_true", + help=( + "The compound target option allows for multiple target " + "types to be evaluated, allowing for greater granularity in " + "target matching. The compound target is space delimited, " + "targets other than globs are preceded with an identifier " + "matching the specific targets argument type: salt " + "'G@os:RedHat and webser* or E@database.*'." + ), ) group.add_option( - '-I', '--pillar', + "-I", + "--pillar", default=False, - dest='pillar_target', - action='store_true', - help=('Instead of using shell globs to evaluate the target ' - 'use a pillar value to identify targets, the syntax ' - 'for the target is the pillar key followed by a glob ' - 'expression: "role:production*".') + dest="pillar_target", + action="store_true", + help=( + "Instead of using shell globs to evaluate the target " + "use a pillar value to identify targets, the syntax " + "for the target is the pillar key followed by a glob " + 'expression: "role:production*".' + ), ) group.add_option( - '-J', '--pillar-pcre', + "-J", + "--pillar-pcre", default=False, - action='store_true', - help=('Instead of using shell globs to evaluate the target ' - 'use a pillar value to identify targets, the syntax ' - 'for the target is the pillar key followed by a pcre ' - 'regular expression: "role:prod.*".') + action="store_true", + help=( + "Instead of using shell globs to evaluate the target " + "use a pillar value to identify targets, the syntax " + "for the target is the pillar key followed by a pcre " + 'regular expression: "role:prod.*".' + ), ) group.add_option( - '-S', '--ipcidr', + "-S", + "--ipcidr", default=False, - action='store_true', - help=('Match based on Subnet (CIDR notation) or IP address.') + action="store_true", + help=("Match based on Subnet (CIDR notation) or IP address."), ) self._create_process_functions() def process_pillar_target(self): if self.options.pillar_target: - self.selected_target_option = 'pillar' + self.selected_target_option = "pillar" class TimeoutMixIn(six.with_metaclass(MixInMeta, object)): _mixin_prio_ = 10 def _mixin_setup(self): - if not hasattr(self, 'default_timeout'): + if not hasattr(self, "default_timeout"): raise RuntimeError( - 'You need to define the \'default_timeout\' attribute ' - 'on {0}'.format(self.__class__.__name__) + "You need to define the 'default_timeout' attribute " + "on {0}".format(self.__class__.__name__) ) self.add_option( - '-t', '--timeout', + "-t", + "--timeout", type=int, default=self.default_timeout, - help=('Change the timeout, if applicable, for the running ' - 'command (in seconds). Default: %default.') + help=( + "Change the timeout, if applicable, for the running " + "command (in seconds). Default: %default." + ), ) @@ -1265,12 +1328,14 @@ class ArgsStdinMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): self.add_option( - '--args-stdin', + "--args-stdin", default=False, - dest='args_stdin', - action='store_true', - help=('Read additional options and/or arguments from stdin. ' - 'Each entry is newline separated.') + dest="args_stdin", + action="store_true", + help=( + "Read additional options and/or arguments from stdin. " + "Each entry is newline separated." + ), ) @@ -1279,10 +1344,7 @@ class ProxyIdMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): self.add_option( - '--proxyid', - default=None, - dest='proxyid', - help=('Id for this proxy.') + "--proxyid", default=None, dest="proxyid", help=("Id for this proxy.") ) @@ -1291,20 +1353,24 @@ class ExecutorsMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): self.add_option( - '--module-executors', - dest='module_executors', + "--module-executors", + dest="module_executors", default=None, - metavar='EXECUTOR_LIST', - help=('Set an alternative list of executors to override the one ' - 'set in minion config.') + metavar="EXECUTOR_LIST", + help=( + "Set an alternative list of executors to override the one " + "set in minion config." + ), ) self.add_option( - '--executor-opts', - dest='executor_opts', + "--executor-opts", + dest="executor_opts", default=None, - metavar='EXECUTOR_OPTS', - help=('Set alternate executor options if supported by executor. ' - 'Options set by minion config are used by default.') + metavar="EXECUTOR_OPTS", + help=( + "Set alternate executor options if supported by executor. " + "Options set by minion config are used by default." + ), ) @@ -1313,10 +1379,10 @@ class CacheDirMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): self.add_option( - '--cachedir', - default='/var/cache/salt/', - dest='cachedir', - help=('Cache Directory') + "--cachedir", + default="/var/cache/salt/", + dest="cachedir", + help=("Cache Directory"), ) @@ -1329,76 +1395,89 @@ class OutputOptionsMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): group = self.output_options_group = optparse.OptionGroup( - self, 'Output Options', 'Configure your preferred output format.' + self, "Output Options", "Configure your preferred output format." ) self.add_option_group(group) group.add_option( - '--out', '--output', - dest='output', + "--out", + "--output", + dest="output", help=( - 'Print the output from the \'{0}\' command using the ' - 'specified outputter.'.format( - self.get_prog_name(), - ) - ) + "Print the output from the '{0}' command using the " + "specified outputter.".format(self.get_prog_name(),) + ), ) group.add_option( - '--out-indent', '--output-indent', - dest='output_indent', + "--out-indent", + "--output-indent", + dest="output_indent", default=None, type=int, - help=('Print the output indented by the provided value in spaces. ' - 'Negative values disables indentation. Only applicable in ' - 'outputters that support indentation.') + help=( + "Print the output indented by the provided value in spaces. " + "Negative values disables indentation. Only applicable in " + "outputters that support indentation." + ), ) group.add_option( - '--out-file', '--output-file', - dest='output_file', + "--out-file", + "--output-file", + dest="output_file", default=None, - help='Write the output to the specified file.' + help="Write the output to the specified file.", ) group.add_option( - '--out-file-append', '--output-file-append', - action='store_true', - dest='output_file_append', + "--out-file-append", + "--output-file-append", + action="store_true", + dest="output_file_append", default=False, - help='Append the output to the specified file.' + help="Append the output to the specified file.", ) group.add_option( - '--no-color', '--no-colour', + "--no-color", + "--no-colour", default=False, - action='store_true', - help='Disable all colored output.' + action="store_true", + help="Disable all colored output.", ) group.add_option( - '--force-color', '--force-colour', + "--force-color", + "--force-colour", default=False, - action='store_true', - help='Force colored output.' + action="store_true", + help="Force colored output.", ) group.add_option( - '--state-output', '--state_output', + "--state-output", + "--state_output", default=None, - help=('Override the configured state_output value for minion ' - 'output. One of \'full\', \'terse\', \'mixed\', \'changes\' or \'filter\'. ' - 'Default: \'%default\'.') + help=( + "Override the configured state_output value for minion " + "output. One of 'full', 'terse', 'mixed', 'changes' or 'filter'. " + "Default: '%default'." + ), ) group.add_option( - '--state-verbose', '--state_verbose', + "--state-verbose", + "--state_verbose", default=None, - help=('Override the configured state_verbose value for minion ' - 'output. Set to True or False. Default: %default.') + help=( + "Override the configured state_verbose value for minion " + "output. Set to True or False. Default: %default." + ), ) for option in self.output_options_group.option_list: + def process(opt): default = self.defaults.get(opt.dest) if getattr(self.options, opt.dest, default) is False: return self.selected_output_option = opt.dest - funcname = 'process_{0}'.format(option.dest) + funcname = "process_{0}".format(option.dest) if not hasattr(self, funcname): setattr(self, funcname, partial(process, option)) @@ -1406,42 +1485,49 @@ class OutputOptionsMixIn(six.with_metaclass(MixInMeta, object)): self.selected_output_option = self.options.output def process_output_file(self): - if self.options.output_file is not None and self.options.output_file_append is False: + if ( + self.options.output_file is not None + and self.options.output_file_append is False + ): if os.path.isfile(self.options.output_file): try: - with salt.utils.files.fopen(self.options.output_file, 'w'): + with salt.utils.files.fopen(self.options.output_file, "w"): # Make this a zero length filename instead of removing # it. This way we keep the file permissions. pass except (IOError, OSError) as exc: self.error( - '{0}: Access denied: {1}'.format( - self.options.output_file, - exc - ) + "{0}: Access denied: {1}".format(self.options.output_file, exc) ) def process_state_verbose(self): if self.options.state_verbose == "True" or self.options.state_verbose == "true": self.options.state_verbose = True - elif self.options.state_verbose == "False" or self.options.state_verbose == "false": + elif ( + self.options.state_verbose == "False" + or self.options.state_verbose == "false" + ): self.options.state_verbose = False def _mixin_after_parsed(self): group_options_selected = [ - option for option in self.output_options_group.option_list if ( - getattr(self.options, option.dest) and - (option.dest.endswith('_out') or option.dest == 'output')) + option + for option in self.output_options_group.option_list + if ( + getattr(self.options, option.dest) + and (option.dest.endswith("_out") or option.dest == "output") + ) ] if len(group_options_selected) > 1: self.error( - 'The options {0} are mutually exclusive. Please only choose ' - 'one of them'.format('/'.join([ - option.get_opt_string() for - option in group_options_selected - ])) + "The options {0} are mutually exclusive. Please only choose " + "one of them".format( + "/".join( + [option.get_opt_string() for option in group_options_selected] + ) + ) ) - self.config['selected_output_option'] = self.selected_output_option + self.config["selected_output_option"] = self.selected_output_option class ExecutionOptionsMixIn(six.with_metaclass(MixInMeta, object)): @@ -1450,117 +1536,124 @@ class ExecutionOptionsMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): group = self.execution_group = optparse.OptionGroup( self, - 'Execution Options', + "Execution Options", # Include description here as a string ) group.add_option( - '-L', '--location', - default=None, - help='Specify which region to connect to.' + "-L", "--location", default=None, help="Specify which region to connect to." ) group.add_option( - '-a', '--action', + "-a", + "--action", default=None, - help='Perform an action that may be specific to this cloud ' - 'provider. This argument requires one or more instance ' - 'names to be specified.' + help="Perform an action that may be specific to this cloud " + "provider. This argument requires one or more instance " + "names to be specified.", ) group.add_option( - '-f', '--function', + "-f", + "--function", nargs=2, default=None, - metavar='<FUNC-NAME> <PROVIDER>', - help='Perform a function that may be specific to this cloud ' - 'provider, that does not apply to an instance. This ' - 'argument requires a provider to be specified (i.e.: nova).' + metavar="<FUNC-NAME> <PROVIDER>", + help="Perform a function that may be specific to this cloud " + "provider, that does not apply to an instance. This " + "argument requires a provider to be specified (i.e.: nova).", ) group.add_option( - '-p', '--profile', + "-p", + "--profile", default=None, - help='Create an instance using the specified profile.' + help="Create an instance using the specified profile.", ) group.add_option( - '-m', '--map', + "-m", + "--map", default=None, - help='Specify a cloud map file to use for deployment. This option ' - 'may be used alone, or in conjunction with -Q, -F, -S or -d. ' - 'The map can also be filtered by a list of VM names.' + help="Specify a cloud map file to use for deployment. This option " + "may be used alone, or in conjunction with -Q, -F, -S or -d. " + "The map can also be filtered by a list of VM names.", ) group.add_option( - '-H', '--hard', + "-H", + "--hard", default=False, - action='store_true', - help='Delete all VMs that are not defined in the map file. ' - 'CAUTION!!! This operation can irrevocably destroy VMs! It ' - 'must be explicitly enabled in the cloud config file.' + action="store_true", + help="Delete all VMs that are not defined in the map file. " + "CAUTION!!! This operation can irrevocably destroy VMs! It " + "must be explicitly enabled in the cloud config file.", ) group.add_option( - '-d', '--destroy', + "-d", + "--destroy", default=False, - action='store_true', - help='Destroy the specified instance(s).' + action="store_true", + help="Destroy the specified instance(s).", ) group.add_option( - '--no-deploy', + "--no-deploy", default=True, - dest='deploy', - action='store_false', - help='Don\'t run a deploy script after instance creation.' + dest="deploy", + action="store_false", + help="Don't run a deploy script after instance creation.", ) group.add_option( - '-P', '--parallel', + "-P", + "--parallel", default=False, - action='store_true', - help='Build all of the specified instances in parallel.' + action="store_true", + help="Build all of the specified instances in parallel.", ) group.add_option( - '-u', '--update-bootstrap', + "-u", + "--update-bootstrap", default=False, - action='store_true', - help='Update salt-bootstrap to the latest stable bootstrap release.' + action="store_true", + help="Update salt-bootstrap to the latest stable bootstrap release.", ) group.add_option( - '-y', '--assume-yes', + "-y", + "--assume-yes", default=False, - action='store_true', - help='Default "yes" in answer to all confirmation questions.' + action="store_true", + help='Default "yes" in answer to all confirmation questions.', ) group.add_option( - '-k', '--keep-tmp', + "-k", + "--keep-tmp", default=False, - action='store_true', - help='Do not remove files from /tmp/ after deploy.sh finishes.' + action="store_true", + help="Do not remove files from /tmp/ after deploy.sh finishes.", ) group.add_option( - '--show-deploy-args', + "--show-deploy-args", default=False, - action='store_true', - help='Include the options used to deploy the minion in the data ' - 'returned.' + action="store_true", + help="Include the options used to deploy the minion in the data " + "returned.", ) group.add_option( - '--script-args', + "--script-args", default=None, - help='Script arguments to be fed to the bootstrap script when ' - 'deploying the VM.' + help="Script arguments to be fed to the bootstrap script when " + "deploying the VM.", ) group.add_option( - '-b', '--bootstrap', + "-b", + "--bootstrap", nargs=1, default=False, - metavar='<HOST> [MINION_ID] [OPTIONS...]', - help='Bootstrap an existing machine.' + metavar="<HOST> [MINION_ID] [OPTIONS...]", + help="Bootstrap an existing machine.", ) self.add_option_group(group) def process_function(self): if self.options.function: self.function_name, self.function_provider = self.options.function - if self.function_provider.startswith('-') or \ - '=' in self.function_provider: + if self.function_provider.startswith("-") or "=" in self.function_provider: self.error( - '--function expects two arguments: <function-name> ' - '<provider>' + "--function expects two arguments: <function-name> " "<provider>" ) @@ -1572,93 +1665,104 @@ class CloudQueriesMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): group = self.cloud_queries_group = optparse.OptionGroup( self, - 'Query Options', + "Query Options", # Include description here as a string ) group.add_option( - '-Q', '--query', + "-Q", + "--query", default=False, - action='store_true', - help=('Execute a query and return some information about the ' - 'nodes running on configured cloud providers.') + action="store_true", + help=( + "Execute a query and return some information about the " + "nodes running on configured cloud providers." + ), ) group.add_option( - '-F', '--full-query', + "-F", + "--full-query", default=False, - action='store_true', - help=('Execute a query and return all information about the ' - 'nodes running on configured cloud providers.') + action="store_true", + help=( + "Execute a query and return all information about the " + "nodes running on configured cloud providers." + ), ) group.add_option( - '-S', '--select-query', + "-S", + "--select-query", default=False, - action='store_true', - help=('Execute a query and return select information about ' - 'the nodes running on configured cloud providers.') + action="store_true", + help=( + "Execute a query and return select information about " + "the nodes running on configured cloud providers." + ), ) group.add_option( - '--list-providers', + "--list-providers", default=False, - action='store_true', - help='Display a list of configured providers.' + action="store_true", + help="Display a list of configured providers.", ) group.add_option( - '--list-profiles', + "--list-profiles", default=None, - action='store', - help='Display a list of configured profiles. Pass in a cloud ' - 'provider to view the provider\'s associated profiles, ' - 'such as digitalocean, or pass in "all" to list all the ' - 'configured profiles.' + action="store", + help="Display a list of configured profiles. Pass in a cloud " + "provider to view the provider's associated profiles, " + 'such as digitalocean, or pass in "all" to list all the ' + "configured profiles.", ) self.add_option_group(group) self._create_process_functions() def _create_process_functions(self): for option in self.cloud_queries_group.option_list: + def process(opt): if getattr(self.options, opt.dest): - query = 'list_nodes' - if opt.dest == 'full_query': - query += '_full' - elif opt.dest == 'select_query': - query += '_select' - elif opt.dest == 'list_providers': - query = 'list_providers' + query = "list_nodes" + if opt.dest == "full_query": + query += "_full" + elif opt.dest == "select_query": + query += "_select" + elif opt.dest == "list_providers": + query = "list_providers" if self.args: self.error( - '\'--list-providers\' does not accept any ' - 'arguments' + "'--list-providers' does not accept any " "arguments" ) - elif opt.dest == 'list_profiles': - query = 'list_profiles' + elif opt.dest == "list_profiles": + query = "list_profiles" option_dict = vars(self.options) - if option_dict.get('list_profiles') == '--list-providers': + if option_dict.get("list_profiles") == "--list-providers": self.error( - '\'--list-profiles\' does not accept ' - '\'--list-providers\' as an argument' + "'--list-profiles' does not accept " + "'--list-providers' as an argument" ) self.selected_query_option = query - funcname = 'process_{0}'.format(option.dest) + funcname = "process_{0}".format(option.dest) if not hasattr(self, funcname): setattr(self, funcname, partial(process, option)) def _mixin_after_parsed(self): group_options_selected = [ - option for option in self.cloud_queries_group.option_list if - getattr(self.options, option.dest) is not False and - getattr(self.options, option.dest) is not None + option + for option in self.cloud_queries_group.option_list + if getattr(self.options, option.dest) is not False + and getattr(self.options, option.dest) is not None ] if len(group_options_selected) > 1: self.error( - 'The options {0} are mutually exclusive. Please only choose ' - 'one of them'.format('/'.join([ - option.get_opt_string() for option in - group_options_selected - ])) + "The options {0} are mutually exclusive. Please only choose " + "one of them".format( + "/".join( + [option.get_opt_string() for option in group_options_selected] + ) + ) ) - self.config['selected_query_option'] = self.selected_query_option + self.config["selected_query_option"] = self.selected_query_option class CloudProvidersListsMixIn(six.with_metaclass(MixInMeta, object)): @@ -1667,48 +1771,54 @@ class CloudProvidersListsMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): group = self.providers_listings_group = optparse.OptionGroup( self, - 'Cloud Providers Listings', + "Cloud Providers Listings", # Include description here as a string ) group.add_option( - '--list-locations', + "--list-locations", default=None, - help=('Display a list of locations available in configured cloud ' - 'providers. Pass the cloud provider that available ' - 'locations are desired on, such as "linode", or pass "all" to ' - 'list locations for all configured cloud providers.') + help=( + "Display a list of locations available in configured cloud " + "providers. Pass the cloud provider that available " + 'locations are desired on, such as "linode", or pass "all" to ' + "list locations for all configured cloud providers." + ), ) group.add_option( - '--list-images', + "--list-images", default=None, - help=('Display a list of images available in configured cloud ' - 'providers. Pass the cloud provider that available images ' - 'are desired on, such as "linode", or pass "all" to list images ' - 'for all configured cloud providers.') + help=( + "Display a list of images available in configured cloud " + "providers. Pass the cloud provider that available images " + 'are desired on, such as "linode", or pass "all" to list images ' + "for all configured cloud providers." + ), ) group.add_option( - '--list-sizes', + "--list-sizes", default=None, - help=('Display a list of sizes available in configured cloud ' - 'providers. Pass the cloud provider that available sizes ' - 'are desired on, such as "AWS", or pass "all" to list sizes ' - 'for all configured cloud providers.') + help=( + "Display a list of sizes available in configured cloud " + "providers. Pass the cloud provider that available sizes " + 'are desired on, such as "AWS", or pass "all" to list sizes ' + "for all configured cloud providers." + ), ) self.add_option_group(group) def _mixin_after_parsed(self): list_options_selected = [ - option for option in self.providers_listings_group.option_list if - getattr(self.options, option.dest) is not None + option + for option in self.providers_listings_group.option_list + if getattr(self.options, option.dest) is not None ] if len(list_options_selected) > 1: self.error( - 'The options {0} are mutually exclusive. Please only choose ' - 'one of them'.format( - '/'.join([ - option.get_opt_string() for option in - list_options_selected - ]) + "The options {0} are mutually exclusive. Please only choose " + "one of them".format( + "/".join( + [option.get_opt_string() for option in list_options_selected] + ) ) ) @@ -1719,22 +1829,24 @@ class ProfilingPMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): group = self.profiling_group = optparse.OptionGroup( self, - 'Profiling support', + "Profiling support", # Include description here as a string ) group.add_option( - '--profiling-path', - dest='profiling_path', - default='/tmp/stats', - help=('Folder that will hold all stats generations path. Default: \'%default\'.') + "--profiling-path", + dest="profiling_path", + default="/tmp/stats", + help=( + "Folder that will hold all stats generations path. Default: '%default'." + ), ) group.add_option( - '--enable-profiling', - dest='profiling_enabled', + "--enable-profiling", + dest="profiling_enabled", default=False, - action='store_true', - help=('Enable generating profiling stats. See also: --profiling-path.') + action="store_true", + help=("Enable generating profiling stats. See also: --profiling-path."), ) self.add_option_group(group) @@ -1745,27 +1857,29 @@ class CloudCredentialsMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): group = self.cloud_credentials_group = optparse.OptionGroup( self, - 'Cloud Credentials', + "Cloud Credentials", # Include description here as a string ) group.add_option( - '--set-password', + "--set-password", default=None, nargs=2, - metavar='<USERNAME> <PROVIDER>', - help=('Configure password for a cloud provider and save it to the keyring. ' - 'PROVIDER can be specified with or without a driver, for example: ' - '"--set-password bob rackspace" or more specific ' - '"--set-password bob rackspace:openstack" ' - 'Deprecated.') + metavar="<USERNAME> <PROVIDER>", + help=( + "Configure password for a cloud provider and save it to the keyring. " + "PROVIDER can be specified with or without a driver, for example: " + '"--set-password bob rackspace" or more specific ' + '"--set-password bob rackspace:openstack" ' + "Deprecated." + ), ) self.add_option_group(group) def process_set_password(self): if self.options.set_password: raise RuntimeError( - 'This functionality is not supported; ' - 'please see the keyring module at http://docs.saltstack.com/en/latest/topics/sdb/' + "This functionality is not supported; " + "please see the keyring module at http://docs.saltstack.com/en/latest/topics/sdb/" ) @@ -1775,104 +1889,122 @@ class EAuthMixIn(six.with_metaclass(MixInMeta, object)): def _mixin_setup(self): group = self.eauth_group = optparse.OptionGroup( self, - 'External Authentication', + "External Authentication", # Include description here as a string ) group.add_option( - '-a', '--auth', '--eauth', '--external-auth', - default='', - dest='eauth', - help=('Specify an external authentication system to use.') + "-a", + "--auth", + "--eauth", + "--external-auth", + default="", + dest="eauth", + help=("Specify an external authentication system to use."), ) group.add_option( - '-T', '--make-token', + "-T", + "--make-token", default=False, - dest='mktoken', - action='store_true', - help=('Generate and save an authentication token for re-use. The ' - 'token is generated and made available for the period ' - 'defined in the Salt Master.') + dest="mktoken", + action="store_true", + help=( + "Generate and save an authentication token for re-use. The " + "token is generated and made available for the period " + "defined in the Salt Master." + ), ) group.add_option( - '--username', - dest='username', + "--username", + dest="username", nargs=1, - help=('Username for external authentication.') + help=("Username for external authentication."), ) group.add_option( - '--password', - dest='password', + "--password", + dest="password", nargs=1, - help=('Password for external authentication.') + help=("Password for external authentication."), ) self.add_option_group(group) -class MasterOptionParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ConfigDirMixIn, - MergeConfigMixIn, - LogLevelMixIn, - RunUserMixin, - DaemonMixIn, - SaltfileMixIn)): +class MasterOptionParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ConfigDirMixIn, + MergeConfigMixIn, + LogLevelMixIn, + RunUserMixin, + DaemonMixIn, + SaltfileMixIn, + ) +): - description = 'The Salt Master, used to control the Salt Minions' + description = "The Salt Master, used to control the Salt Minions" # ConfigDirMixIn config filename attribute - _config_filename_ = 'master' + _config_filename_ = "master" # LogLevelMixIn attributes - _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS['log_file'] + _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS["log_file"] _setup_mp_logging_listener_ = True def setup_config(self): return config.master_config(self.get_config_file_path()) -class MinionOptionParser(six.with_metaclass(OptionParserMeta, - MasterOptionParser)): # pylint: disable=no-init +class MinionOptionParser( + six.with_metaclass(OptionParserMeta, MasterOptionParser) +): # pylint: disable=no-init - description = ( - 'The Salt Minion, receives commands from a remote Salt Master' - ) + description = "The Salt Minion, receives commands from a remote Salt Master" # ConfigDirMixIn config filename attribute - _config_filename_ = 'minion' + _config_filename_ = "minion" # LogLevelMixIn attributes - _default_logging_logfile_ = config.DEFAULT_MINION_OPTS['log_file'] + _default_logging_logfile_ = config.DEFAULT_MINION_OPTS["log_file"] _setup_mp_logging_listener_ = True def setup_config(self): - opts = config.minion_config(self.get_config_file_path(), # pylint: disable=no-member - cache_minion_id=True, - ignore_config_errors=False) + opts = config.minion_config( + self.get_config_file_path(), # pylint: disable=no-member + cache_minion_id=True, + ignore_config_errors=False, + ) # Optimization: disable multiprocessing logging if running as a # daemon, without engines and without multiprocessing - if not opts.get('engines') and not opts.get('multiprocessing', True) \ - and self.options.daemon: # pylint: disable=no-member + if ( + not opts.get("engines") + and not opts.get("multiprocessing", True) + and self.options.daemon + ): # pylint: disable=no-member self._setup_mp_logging_listener_ = False return opts -class ProxyMinionOptionParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ProxyIdMixIn, - ConfigDirMixIn, - MergeConfigMixIn, - LogLevelMixIn, - RunUserMixin, - DaemonMixIn, - SaltfileMixIn)): # pylint: disable=no-init +class ProxyMinionOptionParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ProxyIdMixIn, + ConfigDirMixIn, + MergeConfigMixIn, + LogLevelMixIn, + RunUserMixin, + DaemonMixIn, + SaltfileMixIn, + ) +): # pylint: disable=no-init description = ( - 'The Salt Proxy Minion, connects to and controls devices not able to run a minion.\n' - 'Receives commands from a remote Salt Master.' + "The Salt Proxy Minion, connects to and controls devices not able to run a minion.\n" + "Receives commands from a remote Salt Master." ) # ConfigDirMixIn config filename attribute - _config_filename_ = 'proxy' + _config_filename_ = "proxy" # LogLevelMixIn attributes - _default_logging_logfile_ = config.DEFAULT_PROXY_MINION_OPTS['log_file'] + _default_logging_logfile_ = config.DEFAULT_PROXY_MINION_OPTS["log_file"] def setup_config(self): try: @@ -1880,69 +2012,79 @@ class ProxyMinionOptionParser(six.with_metaclass(OptionParserMeta, except AttributeError: minion_id = None - return config.proxy_config(self.get_config_file_path(), - cache_minion_id=False, - minion_id=minion_id) + return config.proxy_config( + self.get_config_file_path(), cache_minion_id=False, minion_id=minion_id + ) -class SyndicOptionParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ConfigDirMixIn, - MergeConfigMixIn, - LogLevelMixIn, - RunUserMixin, - DaemonMixIn, - SaltfileMixIn)): +class SyndicOptionParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ConfigDirMixIn, + MergeConfigMixIn, + LogLevelMixIn, + RunUserMixin, + DaemonMixIn, + SaltfileMixIn, + ) +): description = ( - 'The Salt Syndic daemon, a special Minion that passes through commands from a\n' - 'higher Master. Scale Salt to thousands of hosts or across many different networks.' + "The Salt Syndic daemon, a special Minion that passes through commands from a\n" + "higher Master. Scale Salt to thousands of hosts or across many different networks." ) # ConfigDirMixIn config filename attribute - _config_filename_ = 'master' + _config_filename_ = "master" # LogLevelMixIn attributes - _logfile_config_setting_name_ = 'syndic_log_file' - _default_logging_level_ = config.DEFAULT_MASTER_OPTS['log_level'] - _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS[_logfile_config_setting_name_] + _logfile_config_setting_name_ = "syndic_log_file" + _default_logging_level_ = config.DEFAULT_MASTER_OPTS["log_level"] + _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS[ + _logfile_config_setting_name_ + ] _setup_mp_logging_listener_ = True def setup_config(self): return config.syndic_config( - self.get_config_file_path(), - self.get_config_file_path('minion')) + self.get_config_file_path(), self.get_config_file_path("minion") + ) -class SaltCMDOptionParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ConfigDirMixIn, - MergeConfigMixIn, - TimeoutMixIn, - ExtendedTargetOptionsMixIn, - OutputOptionsMixIn, - LogLevelMixIn, - ExecutorsMixIn, - HardCrashMixin, - SaltfileMixIn, - ArgsStdinMixIn, - EAuthMixIn, - NoParseMixin)): +class SaltCMDOptionParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ConfigDirMixIn, + MergeConfigMixIn, + TimeoutMixIn, + ExtendedTargetOptionsMixIn, + OutputOptionsMixIn, + LogLevelMixIn, + ExecutorsMixIn, + HardCrashMixin, + SaltfileMixIn, + ArgsStdinMixIn, + EAuthMixIn, + NoParseMixin, + ) +): default_timeout = 5 description = ( - 'Salt allows for commands to be executed across a swath of remote systems in\n' - 'parallel, so they can be both controlled and queried with ease.' + "Salt allows for commands to be executed across a swath of remote systems in\n" + "parallel, so they can be both controlled and queried with ease." ) - usage = '%prog [options] \'<target>\' <function> [arguments]' + usage = "%prog [options] '<target>' <function> [arguments]" # ConfigDirMixIn config filename attribute - _config_filename_ = 'master' + _config_filename_ = "master" # LogLevelMixIn attributes - _default_logging_level_ = config.DEFAULT_MASTER_OPTS['log_level'] - _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS['log_file'] + _default_logging_level_ = config.DEFAULT_MASTER_OPTS["log_level"] + _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS["log_file"] try: os.getcwd() @@ -1951,233 +2093,258 @@ class SaltCMDOptionParser(six.with_metaclass(OptionParserMeta, def _mixin_setup(self): self.add_option( - '-s', '--static', + "-s", + "--static", default=False, - action='store_true', - help=('Return the data from minions as a group after they ' - 'all return.') + action="store_true", + help=("Return the data from minions as a group after they " "all return."), ) self.add_option( - '-p', '--progress', + "-p", + "--progress", default=False, - action='store_true', - help=('Display a progress graph. Requires "progressbar" python package.') + action="store_true", + help=('Display a progress graph. Requires "progressbar" python package.'), ) self.add_option( - '--failhard', + "--failhard", default=False, - action='store_true', - help=('Stop batch execution upon first "bad" return.') + action="store_true", + help=('Stop batch execution upon first "bad" return.'), ) self.add_option( - '--async', + "--async", default=False, - dest='async', - action='store_true', - help=('Run the salt command but don\'t wait for a reply.') + dest="async", + action="store_true", + help=("Run the salt command but don't wait for a reply."), ) self.add_option( - '--subset', + "--subset", default=0, type=int, - help=('Execute the routine on a random subset of the targeted ' - 'minions. The minions will be verified that they have the ' - 'named function before executing.') + help=( + "Execute the routine on a random subset of the targeted " + "minions. The minions will be verified that they have the " + "named function before executing." + ), ) self.add_option( - '-v', '--verbose', + "-v", + "--verbose", default=False, - action='store_true', - help=('Turn on command verbosity, display jid and active job ' - 'queries.') + action="store_true", + help=("Turn on command verbosity, display jid and active job " "queries."), ) self.add_option( - '--hide-timeout', - dest='show_timeout', + "--hide-timeout", + dest="show_timeout", default=True, - action='store_false', - help=('Hide minions that timeout.') + action="store_false", + help=("Hide minions that timeout."), ) self.add_option( - '--show-jid', + "--show-jid", default=False, - action='store_true', - help=('Display jid without the additional output of --verbose.') + action="store_true", + help=("Display jid without the additional output of --verbose."), ) self.add_option( - '-b', '--batch', - '--batch-size', - default='', - dest='batch', - help=('Execute the salt job in batch mode, pass either the number ' - 'of minions to batch at a time, or the percentage of ' - 'minions to have running.') + "-b", + "--batch", + "--batch-size", + default="", + dest="batch", + help=( + "Execute the salt job in batch mode, pass either the number " + "of minions to batch at a time, or the percentage of " + "minions to have running." + ), ) self.add_option( - '--batch-wait', + "--batch-wait", default=0, - dest='batch_wait', + dest="batch_wait", type=float, - help=('Wait the specified time in seconds after each job is done ' - 'before freeing the slot in the batch for the next one.') + help=( + "Wait the specified time in seconds after each job is done " + "before freeing the slot in the batch for the next one." + ), ) self.add_option( - '--batch-safe-limit', + "--batch-safe-limit", default=0, - dest='batch_safe_limit', + dest="batch_safe_limit", type=int, - help=('Execute the salt job in batch mode if the job would have ' - 'executed on more than this many minions.') + help=( + "Execute the salt job in batch mode if the job would have " + "executed on more than this many minions." + ), ) self.add_option( - '--batch-safe-size', + "--batch-safe-size", default=8, - dest='batch_safe_size', - help=('Batch size to use for batch jobs created by batch-safe-limit.') + dest="batch_safe_size", + help=("Batch size to use for batch jobs created by batch-safe-limit."), ) self.add_option( - '--return', - default='', - metavar='RETURNER', - help=('Set an alternative return method. By default salt will ' - 'send the return data from the command back to the master, ' - 'but the return data can be redirected into any number of ' - 'systems, databases or applications.') + "--return", + default="", + metavar="RETURNER", + help=( + "Set an alternative return method. By default salt will " + "send the return data from the command back to the master, " + "but the return data can be redirected into any number of " + "systems, databases or applications." + ), ) self.add_option( - '--return_config', - default='', - metavar='RETURNER_CONF', - help=('Set an alternative return method. By default salt will ' - 'send the return data from the command back to the master, ' - 'but the return data can be redirected into any number of ' - 'systems, databases or applications.') + "--return_config", + default="", + metavar="RETURNER_CONF", + help=( + "Set an alternative return method. By default salt will " + "send the return data from the command back to the master, " + "but the return data can be redirected into any number of " + "systems, databases or applications." + ), ) self.add_option( - '--return_kwargs', + "--return_kwargs", default={}, - metavar='RETURNER_KWARGS', - help=('Set any returner options at the command line.') + metavar="RETURNER_KWARGS", + help=("Set any returner options at the command line."), ) self.add_option( - '-d', '--doc', '--documentation', - dest='doc', + "-d", + "--doc", + "--documentation", + dest="doc", default=False, - action='store_true', - help=('Return the documentation for the specified module or for ' - 'all modules if none are specified.') + action="store_true", + help=( + "Return the documentation for the specified module or for " + "all modules if none are specified." + ), ) self.add_option( - '--args-separator', - dest='args_separator', - default=',', - help=('Set the special argument used as a delimiter between ' - 'command arguments of compound commands. This is useful ' - 'when one wants to pass commas as arguments to ' - 'some of the commands in a compound command.') + "--args-separator", + dest="args_separator", + default=",", + help=( + "Set the special argument used as a delimiter between " + "command arguments of compound commands. This is useful " + "when one wants to pass commas as arguments to " + "some of the commands in a compound command." + ), ) self.add_option( - '--summary', - dest='cli_summary', + "--summary", + dest="cli_summary", default=False, - action='store_true', - help=('Display summary information about a salt command.') + action="store_true", + help=("Display summary information about a salt command."), ) self.add_option( - '--metadata', - default='', - metavar='METADATA', - help=('Pass metadata into Salt, used to search jobs.') + "--metadata", + default="", + metavar="METADATA", + help=("Pass metadata into Salt, used to search jobs."), ) self.add_option( - '--output-diff', - dest='state_output_diff', - action='store_true', + "--output-diff", + dest="state_output_diff", + action="store_true", default=False, - help=('Report only those states that have changed.') + help=("Report only those states that have changed."), ) self.add_option( - '--config-dump', - dest='config_dump', - action='store_true', + "--config-dump", + dest="config_dump", + action="store_true", default=False, - help=('Dump the master configuration values') + help=("Dump the master configuration values"), ) self.add_option( - '--preview-target', - dest='preview_target', - action='store_true', + "--preview-target", + dest="preview_target", + action="store_true", default=False, - help=('Show the minions expected to match a target. Does not issue any command.') + help=( + "Show the minions expected to match a target. Does not issue any command." + ), ) def _mixin_after_parsed(self): - if len(self.args) <= 1 and not self.options.doc and not self.options.preview_target: + if ( + len(self.args) <= 1 + and not self.options.doc + and not self.options.preview_target + ): try: self.print_help() except Exception: # pylint: disable=broad-except # We get an argument that Python's optparser just can't deal # with. Perhaps stdout was redirected, or a file glob was # passed in. Regardless, we're in an unknown state here. - sys.stdout.write('Invalid options passed. Please try -h for ' - 'help.') # Try to warn if we can. + sys.stdout.write( + "Invalid options passed. Please try -h for " "help." + ) # Try to warn if we can. sys.exit(salt.defaults.exitcodes.EX_GENERIC) # Dump the master configuration file, exit normally at the end. if self.options.config_dump: cfg = config.master_config(self.get_config_file_path()) - sys.stdout.write( - salt.utils.yaml.safe_dump( - cfg, - default_flow_style=False) - ) + sys.stdout.write(salt.utils.yaml.safe_dump(cfg, default_flow_style=False)) sys.exit(salt.defaults.exitcodes.EX_OK) if self.options.preview_target: # Insert dummy arg which won't be used - self.args.append('not_a_valid_command') + self.args.append("not_a_valid_command") if self.options.doc: # Include the target if not self.args: - self.args.insert(0, '*') + self.args.insert(0, "*") if len(self.args) < 2: # Include the function - self.args.insert(1, 'sys.doc') - if self.args[1] != 'sys.doc': - self.args.insert(1, 'sys.doc') + self.args.insert(1, "sys.doc") + if self.args[1] != "sys.doc": + self.args.insert(1, "sys.doc") if len(self.args) > 3: - self.error('You can only get documentation for one method at one time.') + self.error("You can only get documentation for one method at one time.") if self.options.list: try: - if ',' in self.args[0]: - self.config['tgt'] = self.args[0].replace(' ', '').split(',') + if "," in self.args[0]: + self.config["tgt"] = self.args[0].replace(" ", "").split(",") else: - self.config['tgt'] = self.args[0].split() + self.config["tgt"] = self.args[0].split() except IndexError: - self.exit(42, '\nCannot execute command without defining a target.\n\n') + self.exit(42, "\nCannot execute command without defining a target.\n\n") else: try: - self.config['tgt'] = self.args[0] + self.config["tgt"] = self.args[0] except IndexError: - self.exit(42, '\nCannot execute command without defining a target.\n\n') + self.exit(42, "\nCannot execute command without defining a target.\n\n") # Detect compound command and set up the data for it if self.args: try: - if ',' in self.args[1]: - self.config['fun'] = self.args[1].split(',') - self.config['arg'] = [[]] + if "," in self.args[1]: + self.config["fun"] = self.args[1].split(",") + self.config["arg"] = [[]] cmd_index = 0 - if (self.args[2:].count(self.options.args_separator) == - len(self.config['fun']) - 1): + if ( + self.args[2:].count(self.options.args_separator) + == len(self.config["fun"]) - 1 + ): # new style parsing: standalone argument separator for arg in self.args[2:]: if arg == self.options.args_separator: cmd_index += 1 - self.config['arg'].append([]) + self.config["arg"].append([]) else: - self.config['arg'][cmd_index].append(arg) + self.config["arg"][cmd_index].append(arg) else: # old style parsing: argument separator can be inside args for arg in self.args[2:]: @@ -2185,81 +2352,93 @@ class SaltCMDOptionParser(six.with_metaclass(OptionParserMeta, sub_args = arg.split(self.options.args_separator) for sub_arg_index, sub_arg in enumerate(sub_args): if sub_arg: - self.config['arg'][cmd_index].append(sub_arg) + self.config["arg"][cmd_index].append(sub_arg) if sub_arg_index != len(sub_args) - 1: cmd_index += 1 - self.config['arg'].append([]) + self.config["arg"].append([]) else: - self.config['arg'][cmd_index].append(arg) - if len(self.config['fun']) > len(self.config['arg']): - self.exit(42, 'Cannot execute compound command without ' - 'defining all arguments.\n') - elif len(self.config['fun']) < len(self.config['arg']): - self.exit(42, 'Cannot execute compound command with more ' - 'arguments than commands.\n') + self.config["arg"][cmd_index].append(arg) + if len(self.config["fun"]) > len(self.config["arg"]): + self.exit( + 42, + "Cannot execute compound command without " + "defining all arguments.\n", + ) + elif len(self.config["fun"]) < len(self.config["arg"]): + self.exit( + 42, + "Cannot execute compound command with more " + "arguments than commands.\n", + ) # parse the args and kwargs before sending to the publish # interface - for i in range(len(self.config['arg'])): - self.config['arg'][i] = salt.utils.args.parse_input( - self.config['arg'][i], - no_parse=self.options.no_parse) + for i in range(len(self.config["arg"])): + self.config["arg"][i] = salt.utils.args.parse_input( + self.config["arg"][i], no_parse=self.options.no_parse + ) else: - self.config['fun'] = self.args[1] - self.config['arg'] = self.args[2:] + self.config["fun"] = self.args[1] + self.config["arg"] = self.args[2:] # parse the args and kwargs before sending to the publish # interface - self.config['arg'] = salt.utils.args.parse_input( - self.config['arg'], - no_parse=self.options.no_parse) + self.config["arg"] = salt.utils.args.parse_input( + self.config["arg"], no_parse=self.options.no_parse + ) except IndexError: - self.exit(42, '\nIncomplete options passed.\n\n') + self.exit(42, "\nIncomplete options passed.\n\n") def setup_config(self): return config.client_config(self.get_config_file_path()) -class SaltCPOptionParser(six.with_metaclass(OptionParserMeta, - OptionParser, - OutputOptionsMixIn, - ConfigDirMixIn, - MergeConfigMixIn, - TimeoutMixIn, - TargetOptionsMixIn, - LogLevelMixIn, - HardCrashMixin, - SaltfileMixIn)): +class SaltCPOptionParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + OutputOptionsMixIn, + ConfigDirMixIn, + MergeConfigMixIn, + TimeoutMixIn, + TargetOptionsMixIn, + LogLevelMixIn, + HardCrashMixin, + SaltfileMixIn, + ) +): description = ( - 'salt-cp is NOT intended to broadcast large files, it is intended to handle text\n' - 'files. salt-cp can be used to distribute configuration files.' + "salt-cp is NOT intended to broadcast large files, it is intended to handle text\n" + "files. salt-cp can be used to distribute configuration files." ) - usage = '%prog [options] \'<target>\' SOURCE DEST' + usage = "%prog [options] '<target>' SOURCE DEST" default_timeout = 5 # ConfigDirMixIn config filename attribute - _config_filename_ = 'master' + _config_filename_ = "master" # LogLevelMixIn attributes - _default_logging_level_ = config.DEFAULT_MASTER_OPTS['log_level'] - _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS['log_file'] + _default_logging_level_ = config.DEFAULT_MASTER_OPTS["log_level"] + _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS["log_file"] def _mixin_setup(self): - file_opts_group = optparse.OptionGroup(self, 'File Options') + file_opts_group = optparse.OptionGroup(self, "File Options") file_opts_group.add_option( - '-C', '--chunked', + "-C", + "--chunked", default=False, - dest='chunked', - action='store_true', - help='Use chunked files transfer. Supports big files, recursive ' - 'lookup and directories creation.' + dest="chunked", + action="store_true", + help="Use chunked files transfer. Supports big files, recursive " + "lookup and directories creation.", ) file_opts_group.add_option( - '-n', '--no-compression', + "-n", + "--no-compression", default=True, - dest='gzip', - action='store_false', - help='Disable gzip compression.' + dest="gzip", + action="store_false", + help="Disable gzip compression.", ) self.add_option_group(file_opts_group) @@ -2267,263 +2446,283 @@ class SaltCPOptionParser(six.with_metaclass(OptionParserMeta, # salt-cp needs arguments if len(self.args) <= 1: self.print_help() - self.error('Insufficient arguments') + self.error("Insufficient arguments") if self.options.list: - if ',' in self.args[0]: - self.config['tgt'] = self.args[0].split(',') + if "," in self.args[0]: + self.config["tgt"] = self.args[0].split(",") else: - self.config['tgt'] = self.args[0].split() + self.config["tgt"] = self.args[0].split() else: - self.config['tgt'] = self.args[0] - self.config['src'] = [os.path.realpath(x) for x in self.args[1:-1]] - self.config['dest'] = self.args[-1] + self.config["tgt"] = self.args[0] + self.config["src"] = [os.path.realpath(x) for x in self.args[1:-1]] + self.config["dest"] = self.args[-1] def setup_config(self): return config.master_config(self.get_config_file_path()) -class SaltKeyOptionParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ConfigDirMixIn, - MergeConfigMixIn, - LogLevelMixIn, - OutputOptionsMixIn, - RunUserMixin, - HardCrashMixin, - SaltfileMixIn, - EAuthMixIn)): +class SaltKeyOptionParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ConfigDirMixIn, + MergeConfigMixIn, + LogLevelMixIn, + OutputOptionsMixIn, + RunUserMixin, + HardCrashMixin, + SaltfileMixIn, + EAuthMixIn, + ) +): - description = 'salt-key is used to manage Salt authentication keys' + description = "salt-key is used to manage Salt authentication keys" # ConfigDirMixIn config filename attribute - _config_filename_ = 'master' + _config_filename_ = "master" # LogLevelMixIn attributes _skip_console_logging_config_ = True - _logfile_config_setting_name_ = 'key_logfile' - _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS[_logfile_config_setting_name_] + _logfile_config_setting_name_ = "key_logfile" + _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS[ + _logfile_config_setting_name_ + ] def _mixin_setup(self): - actions_group = optparse.OptionGroup(self, 'Actions') - actions_group.set_conflict_handler('resolve') + actions_group = optparse.OptionGroup(self, "Actions") + actions_group.set_conflict_handler("resolve") actions_group.add_option( - '-l', '--list', - default='', - metavar='ARG', - help=('List the public keys. The args ' - '\'pre\', \'un\', and \'unaccepted\' will list ' - 'unaccepted/unsigned keys. ' - '\'acc\' or \'accepted\' will list accepted/signed keys. ' - '\'rej\' or \'rejected\' will list rejected keys. ' - '\'den\' or \'denied\' will list denied keys. ' - 'Finally, \'all\' will list all keys.') + "-l", + "--list", + default="", + metavar="ARG", + help=( + "List the public keys. The args " + "'pre', 'un', and 'unaccepted' will list " + "unaccepted/unsigned keys. " + "'acc' or 'accepted' will list accepted/signed keys. " + "'rej' or 'rejected' will list rejected keys. " + "'den' or 'denied' will list denied keys. " + "Finally, 'all' will list all keys." + ), ) actions_group.add_option( - '-L', '--list-all', + "-L", + "--list-all", default=False, - action='store_true', - help='List all public keys. Deprecated: use "--list all".' + action="store_true", + help='List all public keys. Deprecated: use "--list all".', ) actions_group.add_option( - '-a', '--accept', - default='', - help='Accept the specified public key (use --include-rejected and ' - '--include-denied to match rejected and denied keys in ' - 'addition to pending keys). Globs are supported.', + "-a", + "--accept", + default="", + help="Accept the specified public key (use --include-rejected and " + "--include-denied to match rejected and denied keys in " + "addition to pending keys). Globs are supported.", ) actions_group.add_option( - '-A', '--accept-all', + "-A", + "--accept-all", default=False, - action='store_true', - help='Accept all pending keys.' + action="store_true", + help="Accept all pending keys.", ) actions_group.add_option( - '-r', '--reject', - default='', - help='Reject the specified public key. Use --include-accepted and ' - '--include-denied to match accepted and denied keys in ' - 'addition to pending keys. Globs are supported.' + "-r", + "--reject", + default="", + help="Reject the specified public key. Use --include-accepted and " + "--include-denied to match accepted and denied keys in " + "addition to pending keys. Globs are supported.", ) actions_group.add_option( - '-R', '--reject-all', + "-R", + "--reject-all", default=False, - action='store_true', - help='Reject all pending keys.' + action="store_true", + help="Reject all pending keys.", ) actions_group.add_option( - '--include-all', + "--include-all", default=False, - action='store_true', - help='Include rejected/accepted keys when accepting/rejecting. ' - 'Deprecated: use "--include-rejected" and "--include-accepted".' + action="store_true", + help="Include rejected/accepted keys when accepting/rejecting. " + 'Deprecated: use "--include-rejected" and "--include-accepted".', ) actions_group.add_option( - '--include-accepted', + "--include-accepted", default=False, - action='store_true', - help='Include accepted keys when rejecting.' + action="store_true", + help="Include accepted keys when rejecting.", ) actions_group.add_option( - '--include-rejected', + "--include-rejected", default=False, - action='store_true', - help='Include rejected keys when accepting.' + action="store_true", + help="Include rejected keys when accepting.", ) actions_group.add_option( - '--include-denied', + "--include-denied", default=False, - action='store_true', - help='Include denied keys when accepting/rejecting.' + action="store_true", + help="Include denied keys when accepting/rejecting.", ) actions_group.add_option( - '-p', '--print', - default='', - help='Print the specified public key.' + "-p", "--print", default="", help="Print the specified public key." ) actions_group.add_option( - '-P', '--print-all', + "-P", + "--print-all", default=False, - action='store_true', - help='Print all public keys.' + action="store_true", + help="Print all public keys.", ) actions_group.add_option( - '-d', '--delete', - default='', - help='Delete the specified key. Globs are supported.' + "-d", + "--delete", + default="", + help="Delete the specified key. Globs are supported.", ) actions_group.add_option( - '-D', '--delete-all', + "-D", + "--delete-all", default=False, - action='store_true', - help='Delete all keys.' + action="store_true", + help="Delete all keys.", ) actions_group.add_option( - '-f', '--finger', - default='', - help='Print the specified key\'s fingerprint.' + "-f", "--finger", default="", help="Print the specified key's fingerprint." ) actions_group.add_option( - '-F', '--finger-all', + "-F", + "--finger-all", default=False, - action='store_true', - help='Print all keys\' fingerprints.' + action="store_true", + help="Print all keys' fingerprints.", ) self.add_option_group(actions_group) self.add_option( - '-q', '--quiet', - default=False, - action='store_true', - help='Suppress output.' + "-q", "--quiet", default=False, action="store_true", help="Suppress output." ) self.add_option( - '-y', '--yes', + "-y", + "--yes", default=False, - action='store_true', - help='Answer "Yes" to all questions presented. Default: %default.' + action="store_true", + help='Answer "Yes" to all questions presented. Default: %default.', ) self.add_option( - '--rotate-aes-key', + "--rotate-aes-key", default=True, - help=('Setting this to False prevents the master from refreshing ' - 'the key session when keys are deleted or rejected, this ' - 'lowers the security of the key deletion/rejection operation. ' - 'Default: %default.') + help=( + "Setting this to False prevents the master from refreshing " + "the key session when keys are deleted or rejected, this " + "lowers the security of the key deletion/rejection operation. " + "Default: %default." + ), ) self.add_option( - '--preserve-minions', + "--preserve-minions", default=False, - help=('Setting this to True prevents the master from deleting ' - 'the minion cache when keys are deleted, this may have ' - 'security implications if compromised minions auth with ' - 'a previous deleted minion ID. ' - 'Default: %default.') + help=( + "Setting this to True prevents the master from deleting " + "the minion cache when keys are deleted, this may have " + "security implications if compromised minions auth with " + "a previous deleted minion ID. " + "Default: %default." + ), ) - key_options_group = optparse.OptionGroup( - self, 'Key Generation Options' - ) + key_options_group = optparse.OptionGroup(self, "Key Generation Options") self.add_option_group(key_options_group) key_options_group.add_option( - '--gen-keys', - default='', - help='Set a name to generate a keypair for use with salt.' + "--gen-keys", + default="", + help="Set a name to generate a keypair for use with salt.", ) key_options_group.add_option( - '--gen-keys-dir', - default='.', - help=('Set the directory to save the generated keypair, only ' - 'works with "gen_keys_dir" option. Default: \'%default\'.') + "--gen-keys-dir", + default=".", + help=( + "Set the directory to save the generated keypair, only " + "works with \"gen_keys_dir\" option. Default: '%default'." + ), ) key_options_group.add_option( - '--keysize', + "--keysize", default=2048, type=int, - help=('Set the keysize for the generated key, only works with ' - 'the "--gen-keys" option, the key size must be 2048 or ' - 'higher, otherwise it will be rounded up to 2048. ' - 'Default: %default.') + help=( + "Set the keysize for the generated key, only works with " + 'the "--gen-keys" option, the key size must be 2048 or ' + "higher, otherwise it will be rounded up to 2048. " + "Default: %default." + ), ) key_options_group.add_option( - '--gen-signature', + "--gen-signature", default=False, - action='store_true', - help=('Create a signature file of the masters public-key named ' - 'master_pubkey_signature. The signature can be send to a ' - 'minion in the masters auth-reply and enables the minion ' - 'to verify the masters public-key cryptographically. ' - 'This requires a new signing-key-pair which can be auto-created ' - 'with the --auto-create parameter.') + action="store_true", + help=( + "Create a signature file of the masters public-key named " + "master_pubkey_signature. The signature can be send to a " + "minion in the masters auth-reply and enables the minion " + "to verify the masters public-key cryptographically. " + "This requires a new signing-key-pair which can be auto-created " + "with the --auto-create parameter." + ), ) key_options_group.add_option( - '--priv', - default='', + "--priv", + default="", type=str, - help=('The private-key file to create a signature with.') + help=("The private-key file to create a signature with."), ) key_options_group.add_option( - '--signature-path', - default='', + "--signature-path", + default="", type=str, - help=('The path where the signature file should be written.') + help=("The path where the signature file should be written."), ) key_options_group.add_option( - '--pub', - default='', + "--pub", + default="", type=str, - help=('The public-key file to create a signature for.') + help=("The public-key file to create a signature for."), ) key_options_group.add_option( - '--auto-create', + "--auto-create", default=False, - action='store_true', - help=('Auto-create a signing key-pair if it does not yet exist.') + action="store_true", + help=("Auto-create a signing key-pair if it does not yet exist."), ) def process_config_dir(self): @@ -2538,6 +2737,7 @@ class SaltKeyOptionParser(six.with_metaclass(OptionParserMeta, os.makedirs(self.options.gen_keys_dir) self.options.config_dir = self.options.gen_keys_dir super(SaltKeyOptionParser, self).process_config_dir() + # Don't change its mixin priority! process_config_dir._mixin_prio_ = ConfigDirMixIn._mixin_prio_ @@ -2547,610 +2747,682 @@ class SaltKeyOptionParser(six.with_metaclass(OptionParserMeta, # Since we're generating the keys, some defaults can be assumed # or tweaked keys_config[self._logfile_config_setting_name_] = os.devnull - keys_config['pki_dir'] = self.options.gen_keys_dir + keys_config["pki_dir"] = self.options.gen_keys_dir return keys_config def process_rotate_aes_key(self): - if hasattr(self.options, 'rotate_aes_key') and isinstance(self.options.rotate_aes_key, six.string_types): - if self.options.rotate_aes_key.lower() == 'true': + if hasattr(self.options, "rotate_aes_key") and isinstance( + self.options.rotate_aes_key, six.string_types + ): + if self.options.rotate_aes_key.lower() == "true": self.options.rotate_aes_key = True - elif self.options.rotate_aes_key.lower() == 'false': + elif self.options.rotate_aes_key.lower() == "false": self.options.rotate_aes_key = False def process_preserve_minions(self): - if hasattr(self.options, 'preserve_minions') and isinstance(self.options.preserve_minions, six.string_types): - if self.options.preserve_minions.lower() == 'true': + if hasattr(self.options, "preserve_minions") and isinstance( + self.options.preserve_minions, six.string_types + ): + if self.options.preserve_minions.lower() == "true": self.options.preserve_minions = True - elif self.options.preserve_minions.lower() == 'false': + elif self.options.preserve_minions.lower() == "false": self.options.preserve_minions = False def process_list(self): # Filter accepted list arguments as soon as possible if not self.options.list: return - if not self.options.list.startswith(('acc', 'pre', 'un', 'rej', 'den', 'all')): + if not self.options.list.startswith(("acc", "pre", "un", "rej", "den", "all")): self.error( - '\'{0}\' is not a valid argument to \'--list\''.format( - self.options.list - ) + "'{0}' is not a valid argument to '--list'".format(self.options.list) ) def process_keysize(self): if self.options.keysize < 2048: - self.error('The minimum value for keysize is 2048') + self.error("The minimum value for keysize is 2048") elif self.options.keysize > 32768: - self.error('The maximum value for keysize is 32768') + self.error("The maximum value for keysize is 32768") def process_gen_keys_dir(self): # Schedule __create_keys_dir() to run if there's a value for # --create-keys-dir - self._mixin_after_parsed_funcs.append(self.__create_keys_dir) # pylint: disable=no-member + self._mixin_after_parsed_funcs.append( + self.__create_keys_dir + ) # pylint: disable=no-member def _mixin_after_parsed(self): # It was decided to always set this to info, since it really all is # info or error. - self.config['loglevel'] = 'info' + self.config["loglevel"] = "info" def __create_keys_dir(self): - if not os.path.isdir(self.config['gen_keys_dir']): - os.makedirs(self.config['gen_keys_dir']) + if not os.path.isdir(self.config["gen_keys_dir"]): + os.makedirs(self.config["gen_keys_dir"]) -class SaltCallOptionParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ProxyIdMixIn, - ConfigDirMixIn, - ExecutorsMixIn, - MergeConfigMixIn, - LogLevelMixIn, - OutputOptionsMixIn, - HardCrashMixin, - SaltfileMixIn, - ArgsStdinMixIn, - ProfilingPMixIn, - NoParseMixin, - CacheDirMixIn)): +class SaltCallOptionParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ProxyIdMixIn, + ConfigDirMixIn, + ExecutorsMixIn, + MergeConfigMixIn, + LogLevelMixIn, + OutputOptionsMixIn, + HardCrashMixin, + SaltfileMixIn, + ArgsStdinMixIn, + ProfilingPMixIn, + NoParseMixin, + CacheDirMixIn, + ) +): description = ( - 'salt-call is used to execute module functions locally on a Salt Minion' + "salt-call is used to execute module functions locally on a Salt Minion" ) - usage = '%prog [options] <function> [arguments]' + usage = "%prog [options] <function> [arguments]" # ConfigDirMixIn config filename attribute - _config_filename_ = 'minion' + _config_filename_ = "minion" # LogLevelMixIn attributes - _default_logging_level_ = config.DEFAULT_MINION_OPTS['log_level'] - _default_logging_logfile_ = config.DEFAULT_MINION_OPTS['log_file'] + _default_logging_level_ = config.DEFAULT_MINION_OPTS["log_level"] + _default_logging_logfile_ = config.DEFAULT_MINION_OPTS["log_file"] def _mixin_setup(self): self.add_option( - '-g', '--grains', - dest='grains_run', + "-g", + "--grains", + dest="grains_run", default=False, - action='store_true', - help='Return the information generated by the salt grains.' + action="store_true", + help="Return the information generated by the salt grains.", ) self.add_option( - '-m', '--module-dirs', + "-m", + "--module-dirs", default=[], - action='append', - help=('Specify an additional directory to pull modules from. ' - 'Multiple directories can be provided by passing ' - '`-m/--module-dirs` multiple times.') + action="append", + help=( + "Specify an additional directory to pull modules from. " + "Multiple directories can be provided by passing " + "`-m/--module-dirs` multiple times." + ), ) self.add_option( - '-d', '--doc', '--documentation', - dest='doc', + "-d", + "--doc", + "--documentation", + dest="doc", default=False, - action='store_true', - help=('Return the documentation for the specified module or for ' - 'all modules if none are specified.') + action="store_true", + help=( + "Return the documentation for the specified module or for " + "all modules if none are specified." + ), ) self.add_option( - '--master', - default='', - dest='master', - help=('Specify the master to use. The minion must be ' - 'authenticated with the master. If this option is omitted, ' - 'the master options from the minion config will be used. ' - 'If multi masters are set up the first listed master that ' - 'responds will be used.') + "--master", + default="", + dest="master", + help=( + "Specify the master to use. The minion must be " + "authenticated with the master. If this option is omitted, " + "the master options from the minion config will be used. " + "If multi masters are set up the first listed master that " + "responds will be used." + ), ) self.add_option( - '--return', - default='', - metavar='RETURNER', - help=('Set salt-call to pass the return data to one or many ' - 'returner interfaces.') + "--return", + default="", + metavar="RETURNER", + help=( + "Set salt-call to pass the return data to one or many " + "returner interfaces." + ), ) self.add_option( - '--local', + "--local", default=False, - action='store_true', - help='Run salt-call locally, as if there was no master running.' + action="store_true", + help="Run salt-call locally, as if there was no master running.", ) self.add_option( - '--file-root', + "--file-root", default=None, - help='Set this directory as the base file root.' + help="Set this directory as the base file root.", ) self.add_option( - '--pillar-root', + "--pillar-root", default=None, - help='Set this directory as the base pillar root.' + help="Set this directory as the base pillar root.", ) self.add_option( - '--states-dir', + "--states-dir", default=None, - help='Set this directory to search for additional states.' + help="Set this directory to search for additional states.", ) self.add_option( - '--retcode-passthrough', + "--retcode-passthrough", default=False, - action='store_true', - help=('Exit with the salt call retcode and not the salt binary ' - 'retcode.') + action="store_true", + help=( + "Exit with the salt call retcode and not the salt binary " "retcode." + ), ) self.add_option( - '--metadata', + "--metadata", default=False, - dest='print_metadata', - action='store_true', - help=('Print out the execution metadata as well as the return. ' - 'This will print out the outputter data, the return code, ' - 'etc.') + dest="print_metadata", + action="store_true", + help=( + "Print out the execution metadata as well as the return. " + "This will print out the outputter data, the return code, " + "etc." + ), ) self.add_option( - '--set-metadata', - dest='metadata', + "--set-metadata", + dest="metadata", default=None, - metavar='METADATA', - help=('Pass metadata into Salt, used to search jobs.') + metavar="METADATA", + help=("Pass metadata into Salt, used to search jobs."), ) self.add_option( - '--id', - default='', - dest='id', - help=('Specify the minion id to use. If this option is omitted, ' - 'the id option from the minion config will be used.') + "--id", + default="", + dest="id", + help=( + "Specify the minion id to use. If this option is omitted, " + "the id option from the minion config will be used." + ), ) self.add_option( - '--skip-grains', + "--skip-grains", default=False, - action='store_true', - help=('Do not load grains.') + action="store_true", + help=("Do not load grains."), ) self.add_option( - '--refresh-grains-cache', + "--refresh-grains-cache", default=False, - action='store_true', - help=('Force a refresh of the grains cache.') + action="store_true", + help=("Force a refresh of the grains cache."), ) self.add_option( - '-t', '--timeout', + "-t", + "--timeout", default=60, - dest='auth_timeout', + dest="auth_timeout", type=int, - help=('Change the timeout, if applicable, for the running ' - 'command. Default: %default.') + help=( + "Change the timeout, if applicable, for the running " + "command. Default: %default." + ), ) self.add_option( - '--output-diff', - dest='state_output_diff', - action='store_true', + "--output-diff", + dest="state_output_diff", + action="store_true", default=False, - help=('Report only those states that have changed.') + help=("Report only those states that have changed."), ) def _mixin_after_parsed(self): if not self.args and not self.options.grains_run and not self.options.doc: self.print_help() - self.error('Requires function, --grains or --doc') + self.error("Requires function, --grains or --doc") elif len(self.args) >= 1: if self.options.grains_run: - self.error('-g/--grains does not accept any arguments') + self.error("-g/--grains does not accept any arguments") if self.options.doc and len(self.args) > 1: - self.error('You can only get documentation for one method at one time') + self.error("You can only get documentation for one method at one time") - self.config['fun'] = self.args[0] - self.config['arg'] = self.args[1:] + self.config["fun"] = self.args[0] + self.config["arg"] = self.args[1:] def setup_config(self): if self.options.proxyid: - opts = config.proxy_config(self.get_config_file_path(configfile='proxy'), - cache_minion_id=True, - minion_id=self.options.proxyid) + opts = config.proxy_config( + self.get_config_file_path(configfile="proxy"), + cache_minion_id=True, + minion_id=self.options.proxyid, + ) else: - opts = config.minion_config(self.get_config_file_path(), - cache_minion_id=True) + opts = config.minion_config( + self.get_config_file_path(), cache_minion_id=True + ) return opts def process_module_dirs(self): for module_dir in self.options.module_dirs: # Provide some backwards compatibility with previous comma # delimited format - if ',' in module_dir: - self.config.setdefault('module_dirs', []).extend( - os.path.abspath(x) for x in module_dir.split(',')) + if "," in module_dir: + self.config.setdefault("module_dirs", []).extend( + os.path.abspath(x) for x in module_dir.split(",") + ) continue - self.config.setdefault('module_dirs', - []).append(os.path.abspath(module_dir)) + self.config.setdefault("module_dirs", []).append( + os.path.abspath(module_dir) + ) -class SaltRunOptionParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ConfigDirMixIn, - MergeConfigMixIn, - TimeoutMixIn, - LogLevelMixIn, - HardCrashMixin, - SaltfileMixIn, - OutputOptionsMixIn, - ArgsStdinMixIn, - ProfilingPMixIn, - EAuthMixIn, - NoParseMixin)): +class SaltRunOptionParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ConfigDirMixIn, + MergeConfigMixIn, + TimeoutMixIn, + LogLevelMixIn, + HardCrashMixin, + SaltfileMixIn, + OutputOptionsMixIn, + ArgsStdinMixIn, + ProfilingPMixIn, + EAuthMixIn, + NoParseMixin, + ) +): default_timeout = 1 description = ( - 'salt-run is the frontend command for executing Salt Runners.\n' - 'Salt Runners are modules used to execute convenience functions on the Salt Master' + "salt-run is the frontend command for executing Salt Runners.\n" + "Salt Runners are modules used to execute convenience functions on the Salt Master" ) - usage = '%prog [options] <function> [arguments]' + usage = "%prog [options] <function> [arguments]" # ConfigDirMixIn config filename attribute - _config_filename_ = 'master' + _config_filename_ = "master" # LogLevelMixIn attributes - _default_logging_level_ = config.DEFAULT_MASTER_OPTS['log_level'] - _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS['log_file'] + _default_logging_level_ = config.DEFAULT_MASTER_OPTS["log_level"] + _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS["log_file"] def _mixin_setup(self): self.add_option( - '-d', '--doc', '--documentation', - dest='doc', + "-d", + "--doc", + "--documentation", + dest="doc", default=False, - action='store_true', - help=('Display documentation for runners, pass a runner or ' - 'runner.function to see documentation on only that runner ' - 'or function.') + action="store_true", + help=( + "Display documentation for runners, pass a runner or " + "runner.function to see documentation on only that runner " + "or function." + ), ) self.add_option( - '--async', + "--async", default=False, - action='store_true', - help='Start the runner operation and immediately return control.' + action="store_true", + help="Start the runner operation and immediately return control.", ) self.add_option( - '--skip-grains', + "--skip-grains", default=False, - action='store_true', - help='Do not load grains.' + action="store_true", + help="Do not load grains.", ) group = self.output_options_group = optparse.OptionGroup( - self, 'Output Options', 'Configure your preferred output format.' + self, "Output Options", "Configure your preferred output format." ) self.add_option_group(group) group.add_option( - '--quiet', + "--quiet", default=False, - action='store_true', - help='Do not display the results of the run.' + action="store_true", + help="Do not display the results of the run.", ) def _mixin_after_parsed(self): if self.options.doc and len(self.args) > 1: - self.error('You can only get documentation for one method at one time') + self.error("You can only get documentation for one method at one time") if len(self.args) > 0: - self.config['fun'] = self.args[0] + self.config["fun"] = self.args[0] else: - self.config['fun'] = '' + self.config["fun"] = "" if len(self.args) > 1: - self.config['arg'] = self.args[1:] + self.config["arg"] = self.args[1:] else: - self.config['arg'] = [] + self.config["arg"] = [] def setup_config(self): return config.client_config(self.get_config_file_path()) -class SaltSSHOptionParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ConfigDirMixIn, - MergeConfigMixIn, - LogLevelMixIn, - TargetOptionsMixIn, - OutputOptionsMixIn, - SaltfileMixIn, - HardCrashMixin, - NoParseMixin)): +class SaltSSHOptionParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ConfigDirMixIn, + MergeConfigMixIn, + LogLevelMixIn, + TargetOptionsMixIn, + OutputOptionsMixIn, + SaltfileMixIn, + HardCrashMixin, + NoParseMixin, + ) +): - usage = '%prog [options] \'<target>\' <function> [arguments]' + usage = "%prog [options] '<target>' <function> [arguments]" # ConfigDirMixIn config filename attribute - _config_filename_ = 'master' + _config_filename_ = "master" # LogLevelMixIn attributes - _logfile_config_setting_name_ = 'ssh_log_file' - _default_logging_level_ = config.DEFAULT_MASTER_OPTS['log_level'] - _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS[_logfile_config_setting_name_] + _logfile_config_setting_name_ = "ssh_log_file" + _default_logging_level_ = config.DEFAULT_MASTER_OPTS["log_level"] + _default_logging_logfile_ = config.DEFAULT_MASTER_OPTS[ + _logfile_config_setting_name_ + ] def _mixin_setup(self): self.add_option( - '-r', '--raw', '--raw-shell', - dest='raw_shell', + "-r", + "--raw", + "--raw-shell", + dest="raw_shell", default=False, - action='store_true', - help=('Don\'t execute a salt routine on the targets, execute a ' - 'raw shell command.') + action="store_true", + help=( + "Don't execute a salt routine on the targets, execute a " + "raw shell command." + ), ) self.add_option( - '--roster', - dest='roster', - default='flat', - help=('Define which roster system to use, this defines if a ' - 'database backend, scanner, or custom roster system is ' - 'used. Default: \'flat\'.') + "--roster", + dest="roster", + default="flat", + help=( + "Define which roster system to use, this defines if a " + "database backend, scanner, or custom roster system is " + "used. Default: 'flat'." + ), ) self.add_option( - '--roster-file', - dest='roster_file', - default='', - help=('Define an alternative location for the default roster ' - 'file location. The default roster file is called roster ' - 'and is found in the same directory as the master config ' - 'file.') + "--roster-file", + dest="roster_file", + default="", + help=( + "Define an alternative location for the default roster " + "file location. The default roster file is called roster " + "and is found in the same directory as the master config " + "file." + ), ) self.add_option( - '--refresh', '--refresh-cache', - dest='refresh_cache', + "--refresh", + "--refresh-cache", + dest="refresh_cache", default=False, - action='store_true', - help=('Force a refresh of the master side data cache of the ' - 'target\'s data. This is needed if a target\'s grains have ' - 'been changed and the auto refresh timeframe has not been ' - 'reached.') + action="store_true", + help=( + "Force a refresh of the master side data cache of the " + "target's data. This is needed if a target's grains have " + "been changed and the auto refresh timeframe has not been " + "reached." + ), ) self.add_option( - '--max-procs', - dest='ssh_max_procs', + "--max-procs", + dest="ssh_max_procs", default=25, type=int, - help='Set the number of concurrent minions to communicate with. ' - 'This value defines how many processes are opened up at a ' - 'time to manage connections, the more running processes the ' - 'faster communication should be. Default: %default.' + help="Set the number of concurrent minions to communicate with. " + "This value defines how many processes are opened up at a " + "time to manage connections, the more running processes the " + "faster communication should be. Default: %default.", ) self.add_option( - '--extra-filerefs', - dest='extra_filerefs', + "--extra-filerefs", + dest="extra_filerefs", default=None, - help='Pass in extra files to include in the state tarball.' + help="Pass in extra files to include in the state tarball.", ) - self.add_option('--min-extra-modules', - dest='min_extra_mods', default=None, - help='One or comma-separated list of extra Python modules' - 'to be included into Minimal Salt.') self.add_option( - '--thin-extra-modules', - dest='thin_extra_mods', + "--min-extra-modules", + dest="min_extra_mods", default=None, - help='One or comma-separated list of extra Python modules' - 'to be included into Thin Salt.') - self.add_option( - '-v', '--verbose', - default=False, - action='store_true', - help='Turn on command verbosity, display jid.' + help="One or comma-separated list of extra Python modules" + "to be included into Minimal Salt.", ) self.add_option( - '-s', '--static', - default=False, - action='store_true', - help='Return the data from minions as a group after they all return.' - ) - self.add_option( - '-w', '--wipe', - default=False, - action='store_true', - dest='ssh_wipe', - help='Remove the deployment of the salt files when done executing.', - ) - self.add_option( - '-W', '--rand-thin-dir', - default=False, - action='store_true', - help=('Select a random temp dir to deploy on the remote system. ' - 'The dir will be cleaned after the execution.')) - self.add_option( - '-t', '--regen-thin', '--thin', - dest='regen_thin', - default=False, - action='store_true', - help=('Trigger a thin tarball regeneration. This is needed if ' - 'custom grains/modules/states have been added or updated.')) - self.add_option( - '--python2-bin', - default='python2', - help='Path to a python2 binary which has salt installed.' - ) - self.add_option( - '--python3-bin', - default='python3', - help='Path to a python3 binary which has salt installed.' - ) - self.add_option( - '--jid', + "--thin-extra-modules", + dest="thin_extra_mods", default=None, - help='Pass a JID to be used instead of generating one.' + help="One or comma-separated list of extra Python modules" + "to be included into Thin Salt.", + ) + self.add_option( + "-v", + "--verbose", + default=False, + action="store_true", + help="Turn on command verbosity, display jid.", + ) + self.add_option( + "-s", + "--static", + default=False, + action="store_true", + help="Return the data from minions as a group after they all return.", + ) + self.add_option( + "-w", + "--wipe", + default=False, + action="store_true", + dest="ssh_wipe", + help="Remove the deployment of the salt files when done executing.", + ) + self.add_option( + "-W", + "--rand-thin-dir", + default=False, + action="store_true", + help=( + "Select a random temp dir to deploy on the remote system. " + "The dir will be cleaned after the execution." + ), + ) + self.add_option( + "-t", + "--regen-thin", + "--thin", + dest="regen_thin", + default=False, + action="store_true", + help=( + "Trigger a thin tarball regeneration. This is needed if " + "custom grains/modules/states have been added or updated." + ), + ) + self.add_option( + "--python2-bin", + default="python2", + help="Path to a python2 binary which has salt installed.", + ) + self.add_option( + "--python3-bin", + default="python3", + help="Path to a python3 binary which has salt installed.", + ) + self.add_option( + "--jid", + default=None, + help="Pass a JID to be used instead of generating one.", + ) + + self.add_option( + "--pre-flight", + default=False, + action="store_true", + dest="ssh_run_pre_flight", + help="Run the defined ssh_pre_flight script in the roster", ) ssh_group = optparse.OptionGroup( - self, 'SSH Options', - 'Parameters for the SSH client.' + self, "SSH Options", "Parameters for the SSH client." ) ssh_group.add_option( - '--remote-port-forwards', - dest='ssh_remote_port_forwards', - help='Setup remote port forwarding using the same syntax as with ' - 'the -R parameter of ssh. A comma separated list of port ' - 'forwarding definitions will be translated into multiple ' - '-R parameters.' + "--remote-port-forwards", + dest="ssh_remote_port_forwards", + help="Setup remote port forwarding using the same syntax as with " + "the -R parameter of ssh. A comma separated list of port " + "forwarding definitions will be translated into multiple " + "-R parameters.", ) ssh_group.add_option( - '--ssh-option', - dest='ssh_options', - action='append', - help='Equivalent to the -o ssh command option. Passes options to ' - 'the SSH client in the format used in the client configuration file. ' - 'Can be used multiple times.' + "--ssh-option", + dest="ssh_options", + action="append", + help="Equivalent to the -o ssh command option. Passes options to " + "the SSH client in the format used in the client configuration file. " + "Can be used multiple times.", ) self.add_option_group(ssh_group) auth_group = optparse.OptionGroup( - self, 'Authentication Options', - 'Parameters affecting authentication.' + self, "Authentication Options", "Parameters affecting authentication." + ) + auth_group.add_option("--priv", dest="ssh_priv", help="Ssh private key file.") + auth_group.add_option( + "--priv-passwd", + dest="ssh_priv_passwd", + default="", + help="Passphrase for ssh private key file.", ) auth_group.add_option( - '--priv', - dest='ssh_priv', - help='Ssh private key file.' - ) - auth_group.add_option( - '--priv-passwd', - dest='ssh_priv_passwd', - default='', - help='Passphrase for ssh private key file.' - ) - auth_group.add_option( - '-i', - '--ignore-host-keys', - dest='ignore_host_keys', + "-i", + "--ignore-host-keys", + dest="ignore_host_keys", default=False, - action='store_true', - help='By default ssh host keys are honored and connections will ' - 'ask for approval. Use this option to disable ' - 'StrictHostKeyChecking.' + action="store_true", + help="By default ssh host keys are honored and connections will " + "ask for approval. Use this option to disable " + "StrictHostKeyChecking.", ) auth_group.add_option( - '--no-host-keys', - dest='no_host_keys', + "--no-host-keys", + dest="no_host_keys", default=False, - action='store_true', - help='Removes all host key checking functionality from SSH session.' + action="store_true", + help="Removes all host key checking functionality from SSH session.", ) auth_group.add_option( - '--user', - dest='ssh_user', - default='root', - help='Set the default user to attempt to use when ' - 'authenticating.' + "--user", + dest="ssh_user", + default="root", + help="Set the default user to attempt to use when " "authenticating.", ) auth_group.add_option( - '--passwd', - dest='ssh_passwd', - default='', - help='Set the default password to attempt to use when ' - 'authenticating.' + "--passwd", + dest="ssh_passwd", + default="", + help="Set the default password to attempt to use when " "authenticating.", ) auth_group.add_option( - '--askpass', - dest='ssh_askpass', + "--askpass", + dest="ssh_askpass", default=False, - action='store_true', - help='Interactively ask for the SSH password with no echo - avoids ' - 'password in process args and stored in history.' + action="store_true", + help="Interactively ask for the SSH password with no echo - avoids " + "password in process args and stored in history.", ) auth_group.add_option( - '--key-deploy', - dest='ssh_key_deploy', + "--key-deploy", + dest="ssh_key_deploy", default=False, - action='store_true', - help='Set this flag to attempt to deploy the authorized ssh key ' - 'with all minions. This combined with --passwd can make ' - 'initial deployment of keys very fast and easy.' + action="store_true", + help="Set this flag to attempt to deploy the authorized ssh key " + "with all minions. This combined with --passwd can make " + "initial deployment of keys very fast and easy.", ) auth_group.add_option( - '--identities-only', - dest='ssh_identities_only', + "--identities-only", + dest="ssh_identities_only", default=False, - action='store_true', - help='Use the only authentication identity files configured in the ' - 'ssh_config files. See IdentitiesOnly flag in man ssh_config.' + action="store_true", + help="Use the only authentication identity files configured in the " + "ssh_config files. See IdentitiesOnly flag in man ssh_config.", ) auth_group.add_option( - '--sudo', - dest='ssh_sudo', + "--sudo", + dest="ssh_sudo", default=False, - action='store_true', - help='Run command via sudo.' + action="store_true", + help="Run command via sudo.", ) auth_group.add_option( - '--update-roster', - dest='ssh_update_roster', + "--update-roster", + dest="ssh_update_roster", default=False, - action='store_true', - help='If hostname is not found in the roster, store the information' - 'into the default roster file (flat).' + action="store_true", + help="If hostname is not found in the roster, store the information" + "into the default roster file (flat).", ) self.add_option_group(auth_group) scan_group = optparse.OptionGroup( - self, 'Scan Roster Options', - 'Parameters affecting scan roster.' + self, "Scan Roster Options", "Parameters affecting scan roster." ) scan_group.add_option( - '--scan-ports', - default='22', - dest='ssh_scan_ports', - help='Comma-separated list of ports to scan in the scan roster.', + "--scan-ports", + default="22", + dest="ssh_scan_ports", + help="Comma-separated list of ports to scan in the scan roster.", ) scan_group.add_option( - '--scan-timeout', + "--scan-timeout", default=0.01, - dest='ssh_scan_timeout', - help='Scanning socket timeout for the scan roster.', + dest="ssh_scan_timeout", + help="Scanning socket timeout for the scan roster.", ) self.add_option_group(scan_group) def _mixin_after_parsed(self): if not self.args: self.print_help() - self.error('Insufficient arguments') + self.error("Insufficient arguments") if self.options.list: - if ',' in self.args[0]: - self.config['tgt'] = self.args[0].split(',') + if "," in self.args[0]: + self.config["tgt"] = self.args[0].split(",") else: - self.config['tgt'] = self.args[0].split() + self.config["tgt"] = self.args[0].split() else: - self.config['tgt'] = self.args[0] + self.config["tgt"] = self.args[0] - self.config['argv'] = self.args[1:] - if not self.config['argv'] or not self.config['tgt']: + self.config["argv"] = self.args[1:] + if not self.config["argv"] or not self.config["tgt"]: self.print_help() - self.error('Insufficient arguments') + self.error("Insufficient arguments") # Add back the --no-parse options so that shimmed/wrapped commands # handle the arguments correctly. if self.options.no_parse: - self.config['argv'].append( - '--no-parse=' + ','.join(self.options.no_parse)) + self.config["argv"].append("--no-parse=" + ",".join(self.options.no_parse)) if self.options.ssh_askpass: - self.options.ssh_passwd = getpass.getpass('Password: ') + self.options.ssh_passwd = getpass.getpass("Password: ") for group in self.option_groups: for option in group.option_list: - if option.dest == 'ssh_passwd': + if option.dest == "ssh_passwd": option.explicit = True break @@ -3160,60 +3432,66 @@ class SaltSSHOptionParser(six.with_metaclass(OptionParserMeta, def process_jid(self): if self.options.jid is not None: if not salt.utils.jid.is_jid(self.options.jid): - self.error('\'{0}\' is not a valid JID'.format(self.options.jid)) + self.error("'{0}' is not a valid JID".format(self.options.jid)) -class SaltCloudParser(six.with_metaclass(OptionParserMeta, - OptionParser, - LogLevelMixIn, - MergeConfigMixIn, - OutputOptionsMixIn, - ConfigDirMixIn, - CloudQueriesMixIn, - ExecutionOptionsMixIn, - CloudProvidersListsMixIn, - CloudCredentialsMixIn, - HardCrashMixin, - SaltfileMixIn)): +class SaltCloudParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + LogLevelMixIn, + MergeConfigMixIn, + OutputOptionsMixIn, + ConfigDirMixIn, + CloudQueriesMixIn, + ExecutionOptionsMixIn, + CloudProvidersListsMixIn, + CloudCredentialsMixIn, + HardCrashMixin, + SaltfileMixIn, + ) +): description = ( - 'Salt Cloud is the system used to provision virtual machines on various public\n' - 'clouds via a cleanly controlled profile and mapping system' + "Salt Cloud is the system used to provision virtual machines on various public\n" + "clouds via a cleanly controlled profile and mapping system" ) - usage = '%prog [options] <-m MAP | -p PROFILE> <NAME> [NAME2 ...]' + usage = "%prog [options] <-m MAP | -p PROFILE> <NAME> [NAME2 ...]" # ConfigDirMixIn attributes - _config_filename_ = 'cloud' + _config_filename_ = "cloud" # LogLevelMixIn attributes - _default_logging_level_ = config.DEFAULT_CLOUD_OPTS['log_level'] - _default_logging_logfile_ = config.DEFAULT_CLOUD_OPTS['log_file'] + _default_logging_level_ = config.DEFAULT_CLOUD_OPTS["log_level"] + _default_logging_logfile_ = config.DEFAULT_CLOUD_OPTS["log_file"] - def print_versions_report(self, file=sys.stdout): # pylint: disable=redefined-builtin - print('\n'.join(version.versions_report(include_salt_cloud=True)), - file=file) + def print_versions_report( + self, file=sys.stdout + ): # pylint: disable=redefined-builtin + print("\n".join(version.versions_report(include_salt_cloud=True)), file=file) self.exit(salt.defaults.exitcodes.EX_OK) def parse_args(self, args=None, values=None): try: # Late import in order not to break setup from salt.cloud import libcloudfuncs + libcloudfuncs.check_libcloud_version() except ImportError as exc: self.error(exc) return super(SaltCloudParser, self).parse_args(args, values) def _mixin_after_parsed(self): - if 'DUMP_SALT_CLOUD_CONFIG' in os.environ: + if "DUMP_SALT_CLOUD_CONFIG" in os.environ: import pprint - print('Salt Cloud configuration dump (INCLUDES SENSIBLE DATA):') + print("Salt Cloud configuration dump (INCLUDES SENSIBLE DATA):") pprint.pprint(self.config) self.exit(salt.defaults.exitcodes.EX_OK) if self.args: - self.config['names'] = self.args + self.config["names"] = self.args def setup_config(self): try: @@ -3222,74 +3500,89 @@ class SaltCloudParser(six.with_metaclass(OptionParserMeta, self.error(exc) -class SPMParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ConfigDirMixIn, - LogLevelMixIn, - MergeConfigMixIn, - SaltfileMixIn)): - ''' +class SPMParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ConfigDirMixIn, + LogLevelMixIn, + MergeConfigMixIn, + SaltfileMixIn, + ) +): + """ The CLI parser object used to fire up the Salt SPM system. - ''' - description = 'SPM is used to manage 3rd party formulas and other Salt components' + """ - usage = '%prog [options] <function> <argument>' + description = "SPM is used to manage 3rd party formulas and other Salt components" + + usage = "%prog [options] <function> <argument>" # ConfigDirMixIn config filename attribute - _config_filename_ = 'spm' + _config_filename_ = "spm" # LogLevelMixIn attributes - _logfile_config_setting_name_ = 'spm_logfile' + _logfile_config_setting_name_ = "spm_logfile" _default_logging_logfile_ = config.DEFAULT_SPM_OPTS[_logfile_config_setting_name_] def _mixin_setup(self): self.add_option( - '-y', '--assume-yes', + "-y", + "--assume-yes", default=False, - action='store_true', - help='Default "yes" in answer to all confirmation questions.' + action="store_true", + help='Default "yes" in answer to all confirmation questions.', ) self.add_option( - '-f', '--force', + "-f", + "--force", default=False, - action='store_true', - help='Default "yes" in answer to all confirmation questions.' + action="store_true", + help='Default "yes" in answer to all confirmation questions.', ) self.add_option( - '-v', '--verbose', + "-v", + "--verbose", default=False, - action='store_true', - help='Display more detailed information.' + action="store_true", + help="Display more detailed information.", ) def _mixin_after_parsed(self): # spm needs arguments if len(self.args) <= 1: - if not self.args or self.args[0] not in ('update_repo',): + if not self.args or self.args[0] not in ("update_repo",): self.print_help() - self.error('Insufficient arguments') + self.error("Insufficient arguments") def setup_config(self): return salt.config.spm_config(self.get_config_file_path()) -class SaltAPIParser(six.with_metaclass(OptionParserMeta, - OptionParser, - ConfigDirMixIn, - LogLevelMixIn, - DaemonMixIn, - MergeConfigMixIn)): - ''' +class SaltAPIParser( + six.with_metaclass( + OptionParserMeta, + OptionParser, + ConfigDirMixIn, + LogLevelMixIn, + DaemonMixIn, + MergeConfigMixIn, + ) +): + """ The CLI parser object used to fire up the Salt API system. - ''' + """ + description = ( - 'The Salt API system manages network API connectors for the Salt Master' + "The Salt API system manages network API connectors for the Salt Master" ) # ConfigDirMixIn config filename attribute - _config_filename_ = 'master' + _config_filename_ = "master" # LogLevelMixIn attributes - _logfile_config_setting_name_ = 'api_logfile' + _logfile_config_setting_name_ = "api_logfile" _default_logging_logfile_ = config.DEFAULT_API_OPTS[_logfile_config_setting_name_] def setup_config(self): - return salt.config.api_config(self.get_config_file_path()) # pylint: disable=no-member + return salt.config.api_config( + self.get_config_file_path() + ) # pylint: disable=no-member diff --git a/salt/utils/path.py b/salt/utils/path.py index 6cbbf3a08ee..7aefe941276 100644 --- a/salt/utils/path.py +++ b/salt/utils/path.py @@ -1,15 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Platform independent versions of some os/os.path functions. Gets around PY2's lack of support for reading NTFS links. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -try: - from collections.abc import Iterable -except ImportError: - from collections import Iterable + import errno import logging import os @@ -23,14 +20,24 @@ import salt.utils.args import salt.utils.platform import salt.utils.stringutils from salt.exceptions import CommandNotFoundError -from salt.utils.decorators.jinja import jinja_filter # Import 3rd-party libs from salt.ext import six +from salt.utils.decorators.jinja import jinja_filter + +try: + from collections.abc import Iterable +except ImportError: + # pylint: disable=no-name-in-module + from collections import Iterable + + # pylint: enable=no-name-in-module + try: import win32file from pywintypes import error as pywinerror + HAS_WIN32FILE = True except ImportError: HAS_WIN32FILE = False @@ -39,14 +46,14 @@ log = logging.getLogger(__name__) def islink(path): - ''' + """ Equivalent to os.path.islink() - ''' + """ if six.PY3 or not salt.utils.platform.is_windows(): return os.path.islink(path) if not HAS_WIN32FILE: - log.error('Cannot check if %s is a link, missing required modules', path) + log.error("Cannot check if %s is a link, missing required modules", path) if not _is_reparse_point(path): return False @@ -64,8 +71,8 @@ def islink(path): # http://msdn.microsoft.com/en-us/library/ff552012.aspx # parse the structure header to work out which type of reparse point this is - header_parser = struct.Struct('L') - ReparseTag, = header_parser.unpack(reparse_data[:header_parser.size]) + header_parser = struct.Struct("L") + (ReparseTag,) = header_parser.unpack(reparse_data[: header_parser.size]) # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511.aspx if not ReparseTag & 0xA000FFFF == 0xA000000C: return False @@ -74,49 +81,59 @@ def islink(path): def readlink(path): - ''' + """ Equivalent to os.readlink() - ''' + """ if six.PY3 or not salt.utils.platform.is_windows(): return os.readlink(path) if not HAS_WIN32FILE: - log.error('Cannot read %s, missing required modules', path) + log.error("Cannot read %s, missing required modules", path) reparse_data = _get_reparse_data(path) if not reparse_data: # Reproduce *NIX behavior when os.readlink is performed on a path that # is not a symbolic link. - raise OSError(errno.EINVAL, 'Invalid argument: \'{0}\''.format(path)) + raise OSError(errno.EINVAL, "Invalid argument: '{0}'".format(path)) # REPARSE_DATA_BUFFER structure - see # http://msdn.microsoft.com/en-us/library/ff552012.aspx # parse the structure header to work out which type of reparse point this is - header_parser = struct.Struct('L') - ReparseTag, = header_parser.unpack(reparse_data[:header_parser.size]) + header_parser = struct.Struct("L") + (ReparseTag,) = header_parser.unpack(reparse_data[: header_parser.size]) # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511.aspx if not ReparseTag & 0xA000FFFF == 0xA000000C: raise OSError( errno.EINVAL, - '{0} is not a symlink, but another type of reparse point ' - '(0x{0:X}).'.format(ReparseTag) + "{0} is not a symlink, but another type of reparse point " + "(0x{0:X}).".format(ReparseTag), ) # parse as a symlink reparse point structure (the structure for other # reparse points is different) - data_parser = struct.Struct('LHHHHHHL') - ReparseTag, ReparseDataLength, Reserved, SubstituteNameOffset, \ - SubstituteNameLength, PrintNameOffset, \ - PrintNameLength, Flags = data_parser.unpack(reparse_data[:data_parser.size]) + data_parser = struct.Struct("LHHHHHHL") + ( + ReparseTag, + ReparseDataLength, + Reserved, + SubstituteNameOffset, + SubstituteNameLength, + PrintNameOffset, + PrintNameLength, + Flags, + ) = data_parser.unpack(reparse_data[: data_parser.size]) path_buffer_offset = data_parser.size absolute_substitute_name_offset = path_buffer_offset + SubstituteNameOffset - target_bytes = reparse_data[absolute_substitute_name_offset:absolute_substitute_name_offset+SubstituteNameLength] - target = target_bytes.decode('UTF-16') + target_bytes = reparse_data[ + absolute_substitute_name_offset : absolute_substitute_name_offset + + SubstituteNameLength + ] + target = target_bytes.decode("UTF-16") - if target.startswith('\\??\\'): + if target.startswith("\\??\\"): target = target[4:] try: @@ -126,8 +143,8 @@ def readlink(path): # If target is on a UNC share, the decoded target will be in the format # "UNC\hostanme\sharename\additional\subdirs\under\share". So, in # these cases, return the target path in the proper UNC path format. - if target.startswith('UNC\\'): - return re.sub(r'^UNC\\+', r'\\\\', target) + if target.startswith("UNC\\"): + return re.sub(r"^UNC\\+", r"\\\\", target) # if file is not found (i.e. bad symlink), return it anyway like on *nix if exc.winerror == 2: return target @@ -137,9 +154,9 @@ def readlink(path): def _is_reparse_point(path): - ''' + """ Returns True if path is a reparse point; False otherwise. - ''' + """ result = win32file.GetFileAttributesW(path) if result == -1: @@ -149,14 +166,14 @@ def _is_reparse_point(path): def _get_reparse_data(path): - ''' + """ Retrieves the reparse point data structure for the given path. If the path is not a reparse point, None is returned. See http://msdn.microsoft.com/en-us/library/ff552012.aspx for details on the REPARSE_DATA_BUFFER structure returned. - ''' + """ # ensure paths are using the right slashes path = os.path.normpath(path) @@ -171,14 +188,15 @@ def _get_reparse_data(path): 1, # share with other readers None, # no inherit, default security descriptor 3, # OPEN_EXISTING - 0x00200000 | 0x02000000 # FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS + 0x00200000 + | 0x02000000, # FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS ) reparseData = win32file.DeviceIoControl( fileHandle, - 0x900a8, # FSCTL_GET_REPARSE_POINT + 0x900A8, # FSCTL_GET_REPARSE_POINT None, # in buffer - 16384 # out buffer size (MAXIMUM_REPARSE_DATA_BUFFER_SIZE) + 16384, # out buffer size (MAXIMUM_REPARSE_DATA_BUFFER_SIZE) ) finally: @@ -188,29 +206,29 @@ def _get_reparse_data(path): return reparseData -@jinja_filter('which') +@jinja_filter("which") def which(exe=None): - ''' + """ Python clone of /usr/bin/which - ''' + """ if not exe: - log.error('No executable was passed to be searched by salt.utils.path.which()') + log.error("No executable was passed to be searched by salt.utils.path.which()") return None ## define some utilities (we use closures here because our predecessor used them) def is_executable_common(path): - ''' + """ This returns truth if posixy semantics (which python simulates on windows) states that this is executable. - ''' + """ return os.path.isfile(path) and os.access(path, os.X_OK) def resolve(path): - ''' + """ This will take a path and recursively follow the link until we get to a real file. - ''' + """ while os.path.islink(path): res = os.readlink(path) @@ -224,27 +242,34 @@ def which(exe=None): # windows-only def has_executable_ext(path, ext_membership): - ''' + """ Extract the extension from the specified path, lowercase it so we can be insensitive, and then check it against the available exts. - ''' + """ p, ext = os.path.splitext(path) return ext.lower() in ext_membership ## prepare related variables from the environment - res = salt.utils.stringutils.to_unicode(os.environ.get('PATH', '')) + res = salt.utils.stringutils.to_unicode(os.environ.get("PATH", "")) system_path = res.split(os.pathsep) # add some reasonable defaults in case someone's PATH is busted if not salt.utils.platform.is_windows(): res = set(system_path) - extended_path = ['/sbin', '/bin', '/usr/sbin', '/usr/bin', '/usr/local/sbin', '/usr/local/bin'] + extended_path = [ + "/sbin", + "/bin", + "/usr/sbin", + "/usr/bin", + "/usr/local/sbin", + "/usr/local/bin", + ] system_path.extend([p for p in extended_path if p not in res]) ## now to define the semantics of what's considered executable on a given platform if salt.utils.platform.is_windows(): # executable semantics on windows requires us to search PATHEXT - res = salt.utils.stringutils.to_str(os.environ.get('PATHEXT', str('.EXE'))) + res = salt.utils.stringutils.to_str(os.environ.get("PATHEXT", str(".EXE"))) # generate two variables, one of them for O(n) searches (but ordered) # and another for O(1) searches. the previous guy was trying to use @@ -256,18 +281,20 @@ def which(exe=None): # check if our caller already specified a valid extension as then we don't need to match it _, ext = os.path.splitext(exe) if ext.lower() in res: - pathext = [''] + pathext = [""] is_executable = is_executable_common # The specified extension isn't valid, so we just assume it's part of the # filename and proceed to walk the pathext list else: - is_executable = lambda path, membership=res: is_executable_common(path) and has_executable_ext(path, membership) + is_executable = lambda path, membership=res: is_executable_common( + path + ) and has_executable_ext(path, membership) else: # in posix, there's no such thing as file extensions..only zuul - pathext = [''] + pathext = [""] # executable semantics are pretty simple on reasonable platforms... is_executable = is_executable_common @@ -294,16 +321,15 @@ def which(exe=None): ## if something was executable, we should've found it already... log.trace( - '\'%s\' could not be found in the following search path: \'%s\'', - exe, system_path + "'%s' could not be found in the following search path: '%s'", exe, system_path ) return None def which_bin(exes): - ''' + """ Scan over some possible executables and return the first one that is found - ''' + """ if not isinstance(exes, Iterable): return None for exe in exes: @@ -314,9 +340,9 @@ def which_bin(exes): return None -@jinja_filter('path_join') +@jinja_filter("path_join") def join(*parts, **kwargs): - ''' + """ This functions tries to solve some issues when joining multiple absolute paths on both *nix and windows platforms. @@ -325,7 +351,7 @@ def join(*parts, **kwargs): The "use_posixpath" kwarg can be be used to force joining using poxixpath, which is useful for Salt fileserver paths on Windows masters. - ''' + """ if six.PY3: new_parts = [] for part in parts: @@ -333,7 +359,7 @@ def join(*parts, **kwargs): parts = new_parts kwargs = salt.utils.args.clean_kwargs(**kwargs) - use_posixpath = kwargs.pop('use_posixpath', False) + use_posixpath = kwargs.pop("use_posixpath", False) if kwargs: salt.utils.args.invalid_kwargs(kwargs) @@ -346,7 +372,7 @@ def join(*parts, **kwargs): root = parts.pop(0) except IndexError: # No args passed to func - return '' + return "" root = salt.utils.stringutils.to_unicode(root) if not parts: @@ -358,36 +384,38 @@ def join(*parts, **kwargs): def check_or_die(command): - ''' + """ Simple convenience function for modules to use for gracefully blowing up if a required tool is not available in the system path. Lazily import `salt.modules.cmdmod` to avoid any sort of circular dependencies. - ''' + """ if command is None: - raise CommandNotFoundError('\'None\' is not a valid command.') + raise CommandNotFoundError("'None' is not a valid command.") if not which(command): - raise CommandNotFoundError('\'{0}\' is not in the path'.format(command)) + raise CommandNotFoundError("'{0}' is not in the path".format(command)) def sanitize_win_path(winpath): - ''' + """ Remove illegal path characters for windows - ''' - intab = '<>:|?*' + """ + intab = "<>:|?*" if isinstance(winpath, six.text_type): - winpath = winpath.translate(dict((ord(c), '_') for c in intab)) + winpath = winpath.translate(dict((ord(c), "_") for c in intab)) elif isinstance(winpath, six.string_types): - outtab = '_' * len(intab) - trantab = ''.maketrans(intab, outtab) if six.PY3 else string.maketrans(intab, outtab) # pylint: disable=no-member + outtab = "_" * len(intab) + trantab = ( + "".maketrans(intab, outtab) if six.PY3 else string.maketrans(intab, outtab) + ) # pylint: disable=no-member winpath = winpath.translate(trantab) return winpath def safe_path(path, allow_path=None): - r''' + r""" .. versionadded:: 2017.7.3 Checks that the path is safe for modification by Salt. For example, you @@ -407,13 +435,13 @@ def safe_path(path, allow_path=None): Returns: bool: True if safe, otherwise False - ''' + """ # Create regex definitions for directories that may be unsafe to modify - system_root = os.environ.get('SystemRoot', 'C:\\Windows') + system_root = os.environ.get("SystemRoot", "C:\\Windows") deny_paths = ( - r'[a-z]\:\\$', # C:\, D:\, etc - r'\\$', # \ - re.escape(system_root) # C:\Windows + r"[a-z]\:\\$", # C:\, D:\, etc + r"\\$", # \ + re.escape(system_root), # C:\Windows ) # Make allow_path a list @@ -444,10 +472,10 @@ def safe_path(path, allow_path=None): def os_walk(top, *args, **kwargs): - ''' + """ This is a helper than ensures that all paths returned from os.walk are unicode. - ''' + """ if six.PY2 and salt.utils.platform.is_windows(): top_query = top else: diff --git a/salt/utils/pbm.py b/salt/utils/pbm.py index 216f1987186..c18239ef734 100644 --- a/salt/utils/pbm.py +++ b/salt/utils/pbm.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Library for VMware Storage Policy management (via the pbm endpoint) This library is used to manage the various policies available in VMware @@ -36,20 +36,24 @@ version currently listed in PyPi, run the following: .. code-block:: bash pip install pyVmomi==5.5.0.2014.1.1 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs import salt.utils.vmware -from salt.exceptions import VMwareApiError, VMwareRuntimeError, \ - VMwareObjectRetrievalError - +from salt.exceptions import ( + VMwareApiError, + VMwareObjectRetrievalError, + VMwareRuntimeError, +) try: from pyVmomi import pbm, vim, vmodl # pylint: disable=no-name-in-module + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False @@ -60,32 +64,37 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if PyVmomi is installed. - ''' + """ if HAS_PYVMOMI: return True else: - return False, 'Missing dependency: The salt.utils.pbm module ' \ - 'requires the pyvmomi library' + return ( + False, + "Missing dependency: The salt.utils.pbm module " + "requires the pyvmomi library", + ) def get_profile_manager(service_instance): - ''' + """ Returns a profile manager service_instance Service instance to the host or vCenter - ''' + """ stub = salt.utils.vmware.get_new_service_instance_stub( - service_instance, ns='pbm/2.0', path='/pbm/sdk') - pbm_si = pbm.ServiceInstance('ServiceInstance', stub) + service_instance, ns="pbm/2.0", path="/pbm/sdk" + ) + pbm_si = pbm.ServiceInstance("ServiceInstance", stub) try: profile_manager = pbm_si.RetrieveContent().profileManager except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) @@ -96,21 +105,23 @@ def get_profile_manager(service_instance): def get_placement_solver(service_instance): - ''' + """ Returns a placement solver service_instance Service instance to the host or vCenter - ''' + """ stub = salt.utils.vmware.get_new_service_instance_stub( - service_instance, ns='pbm/2.0', path='/pbm/sdk') - pbm_si = pbm.ServiceInstance('ServiceInstance', stub) + service_instance, ns="pbm/2.0", path="/pbm/sdk" + ) + pbm_si = pbm.ServiceInstance("ServiceInstance", stub) try: profile_manager = pbm_si.RetrieveContent().placementSolver except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) @@ -121,20 +132,22 @@ def get_placement_solver(service_instance): def get_capability_definitions(profile_manager): - ''' + """ Returns a list of all capability definitions. profile_manager Reference to the profile manager. - ''' + """ res_type = pbm.profile.ResourceType( - resourceType=pbm.profile.ResourceTypeEnum.STORAGE) + resourceType=pbm.profile.ResourceTypeEnum.STORAGE + ) try: cap_categories = profile_manager.FetchCapabilityMetadata(res_type) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) @@ -148,7 +161,7 @@ def get_capability_definitions(profile_manager): def get_policies_by_id(profile_manager, policy_ids): - ''' + """ Returns a list of policies with the specified ids. profile_manager @@ -156,13 +169,14 @@ def get_policies_by_id(profile_manager, policy_ids): policy_ids List of policy ids to retrieve. - ''' + """ try: return profile_manager.RetrieveContent(policy_ids) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) @@ -171,9 +185,8 @@ def get_policies_by_id(profile_manager, policy_ids): raise VMwareRuntimeError(exc.msg) -def get_storage_policies(profile_manager, policy_names=None, - get_all_policies=False): - ''' +def get_storage_policies(profile_manager, policy_names=None, get_all_policies=False): + """ Returns a list of the storage policies, filtered by name. profile_manager @@ -186,26 +199,30 @@ def get_storage_policies(profile_manager, policy_names=None, get_all_policies Flag specifying to return all policies, regardless of the specified filter. - ''' + """ res_type = pbm.profile.ResourceType( - resourceType=pbm.profile.ResourceTypeEnum.STORAGE) + resourceType=pbm.profile.ResourceTypeEnum.STORAGE + ) try: policy_ids = profile_manager.QueryProfile(res_type) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise VMwareRuntimeError(exc.msg) - log.trace('policy_ids = %s', policy_ids) + log.trace("policy_ids = %s", policy_ids) # More policies are returned so we need to filter again - policies = [p for p in get_policies_by_id(profile_manager, policy_ids) - if p.resourceType.resourceType == - pbm.profile.ResourceTypeEnum.STORAGE] + policies = [ + p + for p in get_policies_by_id(profile_manager, policy_ids) + if p.resourceType.resourceType == pbm.profile.ResourceTypeEnum.STORAGE + ] if get_all_policies: return policies if not policy_names: @@ -214,7 +231,7 @@ def get_storage_policies(profile_manager, policy_names=None, def create_storage_policy(profile_manager, policy_spec): - ''' + """ Creates a storage policy. profile_manager @@ -222,13 +239,14 @@ def create_storage_policy(profile_manager, policy_spec): policy_spec Policy update spec. - ''' + """ try: profile_manager.Create(policy_spec) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) @@ -238,7 +256,7 @@ def create_storage_policy(profile_manager, policy_spec): def update_storage_policy(profile_manager, policy, policy_spec): - ''' + """ Updates a storage policy. profile_manager @@ -249,13 +267,14 @@ def update_storage_policy(profile_manager, policy, policy_spec): policy_spec Policy update spec. - ''' + """ try: profile_manager.Update(policy.profileId, policy_spec) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) @@ -265,7 +284,7 @@ def update_storage_policy(profile_manager, policy, policy_spec): def get_default_storage_policy_of_datastore(profile_manager, datastore): - ''' + """ Returns the default storage policy reference assigned to a datastore. profile_manager @@ -273,17 +292,17 @@ def get_default_storage_policy_of_datastore(profile_manager, datastore): datastore Reference to the datastore. - ''' + """ # Retrieve all datastores visible - hub = pbm.placement.PlacementHub( - hubId=datastore._moId, hubType='Datastore') - log.trace('placement_hub = %s', hub) + hub = pbm.placement.PlacementHub(hubId=datastore._moId, hubType="Datastore") + log.trace("placement_hub = %s", hub) try: policy_id = profile_manager.QueryDefaultRequirementProfile(hub) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) @@ -292,14 +311,14 @@ def get_default_storage_policy_of_datastore(profile_manager, datastore): raise VMwareRuntimeError(exc.msg) policy_refs = get_policies_by_id(profile_manager, [policy_id]) if not policy_refs: - raise VMwareObjectRetrievalError('Storage policy with id \'{0}\' was ' - 'not found'.format(policy_id)) + raise VMwareObjectRetrievalError( + "Storage policy with id '{0}' was " "not found".format(policy_id) + ) return policy_refs[0] -def assign_default_storage_policy_to_datastore(profile_manager, policy, - datastore): - ''' +def assign_default_storage_policy_to_datastore(profile_manager, policy, datastore): + """ Assigns a storage policy as the default policy to a datastore. profile_manager @@ -310,17 +329,20 @@ def assign_default_storage_policy_to_datastore(profile_manager, policy, datastore Reference to the datastore. - ''' + """ placement_hub = pbm.placement.PlacementHub( - hubId=datastore._moId, hubType='Datastore') - log.trace('placement_hub = %s', placement_hub) + hubId=datastore._moId, hubType="Datastore" + ) + log.trace("placement_hub = %s", placement_hub) try: - profile_manager.AssignDefaultRequirementProfile(policy.profileId, - [placement_hub]) + profile_manager.AssignDefaultRequirementProfile( + policy.profileId, [placement_hub] + ) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) diff --git a/salt/utils/pkg/__init__.py b/salt/utils/pkg/__init__.py index b0e42b3f93e..bc4aabeef89 100644 --- a/salt/utils/pkg/__init__.py +++ b/salt/utils/pkg/__init__.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Common functions for managing package refreshes during states -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import logging import os @@ -18,41 +19,41 @@ log = logging.getLogger(__name__) def rtag(opts): - ''' + """ Return the rtag file location. This file is used to ensure that we don't refresh more than once (unless explicitly configured to do so). - ''' - return os.path.join(opts['cachedir'], 'pkg_refresh') + """ + return os.path.join(opts["cachedir"], "pkg_refresh") def clear_rtag(opts): - ''' + """ Remove the rtag file - ''' + """ try: os.remove(rtag(opts)) except OSError as exc: if exc.errno != errno.ENOENT: # Using __str__() here to get the fully-formatted error message # (error number, error message, path) - log.warning('Encountered error removing rtag: %s', exc.__str__()) + log.warning("Encountered error removing rtag: %s", exc.__str__()) def write_rtag(opts): - ''' + """ Write the rtag file - ''' + """ rtag_file = rtag(opts) if not os.path.exists(rtag_file): try: - with salt.utils.files.fopen(rtag_file, 'w+'): + with salt.utils.files.fopen(rtag_file, "w+"): pass except OSError as exc: - log.warning('Encountered error writing rtag: %s', exc.__str__()) + log.warning("Encountered error writing rtag: %s", exc.__str__()) def check_refresh(opts, refresh=None): - ''' + """ Check whether or not a refresh is necessary Returns: @@ -60,36 +61,38 @@ def check_refresh(opts, refresh=None): - True if refresh evaluates as True - False if refresh is False - A boolean if refresh is not False and the rtag file exists - ''' + """ return bool( - salt.utils.data.is_true(refresh) or - (os.path.isfile(rtag(opts)) and refresh is not False) + salt.utils.data.is_true(refresh) + or (os.path.isfile(rtag(opts)) and refresh is not False) ) def split_comparison(version): - match = re.match(r'^(<=>|!=|>=|<=|>>|<<|<>|>|<|=)?\s?([^<>=]+)$', version) + match = re.match(r"^(<=>|!=|>=|<=|>>|<<|<>|>|<|=)?\s?([^<>=]+)$", version) if match: - comparison = match.group(1) or '' + comparison = match.group(1) or "" version = match.group(2) else: - comparison = '' + comparison = "" return comparison, version def match_version(desired, available, cmp_func=None, ignore_epoch=False): - ''' + """ Returns the first version of the list of available versions which matches the desired version comparison expression, or None if no match is found. - ''' + """ oper, version = split_comparison(desired) if not oper: - oper = '==' + oper = "==" for candidate in available: - if salt.utils.versions.compare(ver1=candidate, - oper=oper, - ver2=version, - cmp_func=cmp_func, - ignore_epoch=ignore_epoch): + if salt.utils.versions.compare( + ver1=candidate, + oper=oper, + ver2=version, + cmp_func=cmp_func, + ignore_epoch=ignore_epoch, + ): return candidate return None diff --git a/salt/utils/pkg/deb.py b/salt/utils/pkg/deb.py index 8233d6618f3..e3f0fc4f8ab 100644 --- a/salt/utils/pkg/deb.py +++ b/salt/utils/pkg/deb.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Common functions for working with deb packages -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -12,10 +12,10 @@ from salt.ext.six.moves import range # pylint: disable=redefined-builtin def combine_comments(comments): - ''' + """ Given a list of comments, or a comment submitted as a string, return a single line of text containing all of the comments. - ''' + """ if isinstance(comments, list): for idx in range(len(comments)): if not isinstance(comments[idx], six.string_types): @@ -25,16 +25,15 @@ def combine_comments(comments): comments = [six.text_type(comments)] else: comments = [comments] - return ' '.join(comments).strip() + return " ".join(comments).strip() def strip_uri(repo): - ''' + """ Remove the trailing slash from the URI in a repo definition - ''' + """ splits = repo.split() for idx in range(len(splits)): - if any(splits[idx].startswith(x) - for x in ('http://', 'https://', 'ftp://')): - splits[idx] = splits[idx].rstrip('/') - return ' '.join(splits) + if any(splits[idx].startswith(x) for x in ("http://", "https://", "ftp://")): + splits[idx] = splits[idx].rstrip("/") + return " ".join(splits) diff --git a/salt/utils/pkg/rpm.py b/salt/utils/pkg/rpm.py index bc5eb30eda0..2ee2bac4e5a 100644 --- a/salt/utils/pkg/rpm.py +++ b/salt/utils/pkg/rpm.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" Common functions for working with RPM packages -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import collections import datetime import logging -import subprocess import platform -import salt.utils.stringutils +import subprocess + import salt.utils.path +import salt.utils.stringutils # Import 3rd-party libs from salt.ext import six @@ -19,86 +21,98 @@ from salt.ext import six log = logging.getLogger(__name__) # These arches compiled from the rpmUtils.arch python module source -ARCHES_64 = ('x86_64', 'athlon', 'amd64', 'ia32e', 'ia64', 'geode') -ARCHES_32 = ('i386', 'i486', 'i586', 'i686') -ARCHES_PPC = ('ppc', 'ppc64', 'ppc64le', 'ppc64iseries', 'ppc64pseries') -ARCHES_S390 = ('s390', 's390x') -ARCHES_SPARC = ( - 'sparc', 'sparcv8', 'sparcv9', 'sparcv9v', 'sparc64', 'sparc64v' -) +ARCHES_64 = ("x86_64", "athlon", "amd64", "ia32e", "ia64", "geode") +ARCHES_32 = ("i386", "i486", "i586", "i686") +ARCHES_PPC = ("ppc", "ppc64", "ppc64le", "ppc64iseries", "ppc64pseries") +ARCHES_S390 = ("s390", "s390x") +ARCHES_SPARC = ("sparc", "sparcv8", "sparcv9", "sparcv9v", "sparc64", "sparc64v") ARCHES_ALPHA = ( - 'alpha', 'alphaev4', 'alphaev45', 'alphaev5', 'alphaev56', - 'alphapca56', 'alphaev6', 'alphaev67', 'alphaev68', 'alphaev7' + "alpha", + "alphaev4", + "alphaev45", + "alphaev5", + "alphaev56", + "alphapca56", + "alphaev6", + "alphaev67", + "alphaev68", + "alphaev7", ) -ARCHES_ARM = ('armv5tel', 'armv5tejl', 'armv6l', 'armv7l') -ARCHES_SH = ('sh3', 'sh4', 'sh4a') +ARCHES_ARM = ("armv5tel", "armv5tejl", "armv6l", "armv7l") +ARCHES_SH = ("sh3", "sh4", "sh4a") -ARCHES = ARCHES_64 + ARCHES_32 + ARCHES_PPC + ARCHES_S390 + \ - ARCHES_ALPHA + ARCHES_ARM + ARCHES_SH +ARCHES = ( + ARCHES_64 + + ARCHES_32 + + ARCHES_PPC + + ARCHES_S390 + + ARCHES_ALPHA + + ARCHES_ARM + + ARCHES_SH +) # EPOCHNUM can't be used until RHEL5 is EOL as it is not present -QUERYFORMAT = '%{NAME}_|-%{EPOCH}_|-%{VERSION}_|-%{RELEASE}_|-%{ARCH}_|-%{REPOID}_|-%{INSTALLTIME}' +QUERYFORMAT = "%{NAME}_|-%{EPOCH}_|-%{VERSION}_|-%{RELEASE}_|-%{ARCH}_|-%{REPOID}_|-%{INSTALLTIME}" def get_osarch(): - ''' + """ Get the os architecture using rpm --eval - ''' - if salt.utils.path.which('rpm'): + """ + if salt.utils.path.which("rpm"): ret = subprocess.Popen( 'rpm --eval "%{_host_cpu}"', shell=True, close_fds=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate()[0] + stderr=subprocess.PIPE, + ).communicate()[0] else: - ret = ''.join([x for x in platform.uname()[-2:] if x][-1:]) + ret = "".join([x for x in platform.uname()[-2:] if x][-1:]) - return salt.utils.stringutils.to_str(ret).strip() or 'unknown' + return salt.utils.stringutils.to_str(ret).strip() or "unknown" def check_32(arch, osarch=None): - ''' + """ Returns True if both the OS arch and the passed arch are 32-bit - ''' + """ if osarch is None: osarch = get_osarch() return all(x in ARCHES_32 for x in (osarch, arch)) def pkginfo(name, version, arch, repoid, install_date=None, install_date_time_t=None): - ''' + """ Build and return a pkginfo namedtuple - ''' + """ pkginfo_tuple = collections.namedtuple( - 'PkgInfo', - ('name', 'version', 'arch', 'repoid', 'install_date', - 'install_date_time_t') + "PkgInfo", + ("name", "version", "arch", "repoid", "install_date", "install_date_time_t"), ) - return pkginfo_tuple(name, version, arch, repoid, install_date, - install_date_time_t) + return pkginfo_tuple(name, version, arch, repoid, install_date, install_date_time_t) def resolve_name(name, arch, osarch=None): - ''' + """ Resolve the package name and arch into a unique name referred to by salt. For example, on a 64-bit OS, a 32-bit package will be pkgname.i386. - ''' + """ if osarch is None: osarch = get_osarch() - if not check_32(arch, osarch) and arch not in (osarch, 'noarch'): - name += '.{0}'.format(arch) + if not check_32(arch, osarch) and arch not in (osarch, "noarch"): + name += ".{0}".format(arch) return name def parse_pkginfo(line, osarch=None): - ''' + """ A small helper to parse an rpm/repoquery command's output. Returns a pkginfo namedtuple. - ''' + """ try: - name, epoch, version, release, arch, repoid, install_time = line.split('_|-') + name, epoch, version, release, arch, repoid, install_time = line.split("_|-") # Handle unpack errors (should never happen with the queryformat we are # using, but can't hurt to be careful). except ValueError: @@ -106,12 +120,14 @@ def parse_pkginfo(line, osarch=None): name = resolve_name(name, arch, osarch) if release: - version += '-{0}'.format(release) - if epoch not in ('(none)', '0'): - version = ':'.join((epoch, version)) + version += "-{0}".format(release) + if epoch not in ("(none)", "0"): + version = ":".join((epoch, version)) - if install_time not in ('(none)', '0'): - install_date = datetime.datetime.utcfromtimestamp(int(install_time)).isoformat() + "Z" + if install_time not in ("(none)", "0"): + install_date = ( + datetime.datetime.utcfromtimestamp(int(install_time)).isoformat() + "Z" + ) install_date_time_t = int(install_time) else: install_date = None @@ -121,11 +137,11 @@ def parse_pkginfo(line, osarch=None): def combine_comments(comments): - ''' + """ Given a list of comments, strings, a single comment or a single string, return a single string of text containing all of the comments, prepending the '#' and joining with newlines as necessary. - ''' + """ if not isinstance(comments, list): comments = [comments] ret = [] @@ -133,12 +149,12 @@ def combine_comments(comments): if not isinstance(comment, six.string_types): comment = str(comment) # Normalize for any spaces (or lack thereof) after the # - ret.append('# {0}\n'.format(comment.lstrip('#').lstrip())) - return ''.join(ret) + ret.append("# {0}\n".format(comment.lstrip("#").lstrip())) + return "".join(ret) def version_to_evr(verstring): - ''' + """ Split the package version string into epoch, version and release. Return this as tuple. @@ -148,25 +164,25 @@ def version_to_evr(verstring): "2:1.0-1.2" => ('2', '1.0', '1.2) "1.0" => ('0', '1.0', '') "" => ('0', '', '') - ''' - if verstring in [None, '']: - return '0', '', '' + """ + if verstring in [None, ""]: + return "0", "", "" - idx_e = verstring.find(':') + idx_e = verstring.find(":") if idx_e != -1: try: epoch = six.text_type(int(verstring[:idx_e])) except ValueError: # look, garbage in the epoch field, how fun, kill it - epoch = '0' # this is our fallback, deal + epoch = "0" # this is our fallback, deal else: - epoch = '0' - idx_r = verstring.find('-') + epoch = "0" + idx_r = verstring.find("-") if idx_r != -1: - version = verstring[idx_e + 1:idx_r] - release = verstring[idx_r + 1:] + version = verstring[idx_e + 1 : idx_r] + release = verstring[idx_r + 1 :] else: - version = verstring[idx_e + 1:] - release = '' + version = verstring[idx_e + 1 :] + release = "" return epoch, version, release diff --git a/salt/utils/pkg/win.py b/salt/utils/pkg/win.py index 38f0c1adeb4..2fe589cfdb2 100644 --- a/salt/utils/pkg/win.py +++ b/salt/utils/pkg/win.py @@ -12,7 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -r''' +r""" Collect information about software installed on Windows OS ================ @@ -23,7 +23,7 @@ Collect information about software installed on Windows OS :platform: windows Known Issue: install_date may not match Control Panel\Programs\Programs and Features -''' +""" # Note although this code will work with Python 2.7, win32api does not @@ -34,18 +34,22 @@ Known Issue: install_date may not match Control Panel\Programs\Programs and Feat # Import _future_ python libs first & before any other code # pylint: disable=incompatible-py3-code from __future__ import absolute_import, print_function, unicode_literals -__version__ = '0.1' -# Import Standard libs -import sys -import re -import platform + +import collections +import datetime import locale import logging import os.path -import datetime +import platform +import re + +# Import Standard libs +import sys import time -import collections from functools import cmp_to_key + +__version__ = "0.1" + # Import third party libs try: from salt.ext import six @@ -61,15 +65,15 @@ try: import winerror except ImportError: - if __name__ == '__main__': - raise ImportError('Please install pywin32/pypiwin32') + if __name__ == "__main__": + raise ImportError("Please install pywin32/pypiwin32") else: raise -if __name__ == '__main__': +if __name__ == "__main__": LOG_CONSOLE = logging.StreamHandler() - LOG_CONSOLE.setFormatter(logging.Formatter('[%(levelname)s]: %(message)s')) + LOG_CONSOLE.setFormatter(logging.Formatter("[%(levelname)s]: %(message)s")) log = logging.getLogger(__name__) log.addHandler(LOG_CONSOLE) log.setLevel(logging.DEBUG) @@ -90,32 +94,37 @@ except ImportError: # pylint: disable=too-many-instance-attributes + class RegSoftwareInfo(object): - ''' + """ Retrieve Registry data on a single installed software item or component. Attribute: None :codeauthor: Damon Atkins <https://github.com/damon-atkins> - ''' + """ # Variables shared by all instances - __guid_pattern = re.compile(r'^\{(\w{8})-(\w{4})-(\w{4})-(\w\w)(\w\w)-(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)\}$') - __squid_pattern = re.compile(r'^(\w{8})(\w{4})(\w{4})(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)$') - __version_pattern = re.compile(r'\d+\.\d+\.\d+[\w.-]*|\d+\.\d+[\w.-]*') + __guid_pattern = re.compile( + r"^\{(\w{8})-(\w{4})-(\w{4})-(\w\w)(\w\w)-(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)\}$" + ) + __squid_pattern = re.compile( + r"^(\w{8})(\w{4})(\w{4})(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)$" + ) + __version_pattern = re.compile(r"\d+\.\d+\.\d+[\w.-]*|\d+\.\d+[\w.-]*") __upgrade_codes = {} __upgrade_code_have_scan = {} __reg_types = { - 'str': (win32con.REG_EXPAND_SZ, win32con.REG_SZ), - 'list': (win32con.REG_MULTI_SZ), - 'int': (win32con.REG_DWORD, win32con.REG_DWORD_BIG_ENDIAN, win32con.REG_QWORD), - 'bytes': (win32con.REG_BINARY) + "str": (win32con.REG_EXPAND_SZ, win32con.REG_SZ), + "list": (win32con.REG_MULTI_SZ), + "int": (win32con.REG_DWORD, win32con.REG_DWORD_BIG_ENDIAN, win32con.REG_QWORD), + "bytes": (win32con.REG_BINARY), } # Search 64bit, on 64bit platform, on 32bit its ignored - if platform.architecture()[0] == '32bit': + if platform.architecture()[0] == "32bit": # Handle Python 32bit on 64&32 bit platform and Python 64bit if win32process.IsWow64Process(): # pylint: disable=no-member # 32bit python on a 64bit platform @@ -127,7 +136,7 @@ class RegSoftwareInfo(object): __use_32bit_lookup = {True: win32con.KEY_WOW64_32KEY, False: 0} def __init__(self, key_guid, sid=None, use_32bit=False): - ''' + """ Initialise against a software item or component. All software has a unique "Identifer" within the registry. This can be free @@ -140,11 +149,11 @@ class RegSoftwareInfo(object): use_32bit (bool): Regisrty location of the Identifer. ``True`` 32 bit registry only meaning fully on 64 bit OS. - ''' + """ self.__reg_key_guid = key_guid # also called IdentifyingNumber(wmic) - self.__squid = '' - self.__reg_products_path = '' - self.__reg_upgradecode_path = '' + self.__squid = "" + self.__reg_products_path = "" + self.__reg_upgradecode_path = "" self.__patch_list = None # If a valid GUID create the SQUID also. @@ -156,48 +165,62 @@ class RegSoftwareInfo(object): if sid: # User data seems to be more spreadout within the registry. - self.__reg_hive = 'HKEY_USERS' + self.__reg_hive = "HKEY_USERS" self.__reg_32bit = False # Force to False - self.__reg_32bit_access = 0 # HKEY_USERS does not have a 32bit and 64bit view - self.__reg_uninstall_path = ('{0}\\Software\\Microsoft\\Windows\\' - 'CurrentVersion\\Uninstall\\{1}').format(sid, key_guid) + self.__reg_32bit_access = ( + 0 # HKEY_USERS does not have a 32bit and 64bit view + ) + self.__reg_uninstall_path = ( + "{0}\\Software\\Microsoft\\Windows\\" "CurrentVersion\\Uninstall\\{1}" + ).format(sid, key_guid) if self.__squid: - self.__reg_products_path = \ - '{0}\\Software\\Classes\\Installer\\Products\\{1}'.format(sid, self.__squid) - self.__reg_upgradecode_path = \ - '{0}\\Software\\Microsoft\\Installer\\UpgradeCodes'.format(sid) - self.__reg_patches_path = \ - ('Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\' - '{0}\\Products\\{1}\\Patches').format(sid, self.__squid) + self.__reg_products_path = "{0}\\Software\\Classes\\Installer\\Products\\{1}".format( + sid, self.__squid + ) + self.__reg_upgradecode_path = "{0}\\Software\\Microsoft\\Installer\\UpgradeCodes".format( + sid + ) + self.__reg_patches_path = ( + "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\" + "{0}\\Products\\{1}\\Patches" + ).format(sid, self.__squid) else: - self.__reg_hive = 'HKEY_LOCAL_MACHINE' + self.__reg_hive = "HKEY_LOCAL_MACHINE" self.__reg_32bit = use_32bit self.__reg_32bit_access = self.__use_32bit_lookup[use_32bit] - self.__reg_uninstall_path = \ - 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{0}'.format(key_guid) + self.__reg_uninstall_path = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\{0}".format( + key_guid + ) if self.__squid: - self.__reg_products_path = \ - 'Software\\Classes\\Installer\\Products\\{0}'.format(self.__squid) - self.__reg_upgradecode_path = 'Software\\Classes\\Installer\\UpgradeCodes' - self.__reg_patches_path = \ - ('Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\' - 'S-1-5-18\\Products\\{0}\\Patches').format(self.__squid) + self.__reg_products_path = "Software\\Classes\\Installer\\Products\\{0}".format( + self.__squid + ) + self.__reg_upgradecode_path = ( + "Software\\Classes\\Installer\\UpgradeCodes" + ) + self.__reg_patches_path = ( + "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\" + "S-1-5-18\\Products\\{0}\\Patches" + ).format(self.__squid) # OpenKey is expensive, open in advance and keep it open. # This must exist try: # pylint: disable=no-member - self.__reg_uninstall_handle = \ - win32api.RegOpenKeyEx(getattr(win32con, self.__reg_hive), - self.__reg_uninstall_path, - 0, - win32con.KEY_READ | self.__reg_32bit_access) + self.__reg_uninstall_handle = win32api.RegOpenKeyEx( + getattr(win32con, self.__reg_hive), + self.__reg_uninstall_path, + 0, + win32con.KEY_READ | self.__reg_32bit_access, + ) except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: log.error( - 'Software/Component Not Found key_guid: \'%s\', ' - 'sid: \'%s\' , use_32bit: \'%s\'', - key_guid, sid, use_32bit + "Software/Component Not Found key_guid: '%s', " + "sid: '%s' , use_32bit: '%s'", + key_guid, + sid, + use_32bit, ) raise # This must exist or have no errors @@ -205,17 +228,20 @@ class RegSoftwareInfo(object): if self.__squid: try: # pylint: disable=no-member - self.__reg_products_handle = \ - win32api.RegOpenKeyEx(getattr(win32con, self.__reg_hive), - self.__reg_products_path, - 0, - win32con.KEY_READ | self.__reg_32bit_access) + self.__reg_products_handle = win32api.RegOpenKeyEx( + getattr(win32con, self.__reg_hive), + self.__reg_products_path, + 0, + win32con.KEY_READ | self.__reg_32bit_access, + ) except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: log.debug( - 'Software/Component Not Found in Products section of registry ' - 'key_guid: \'%s\', sid: \'%s\', use_32bit: \'%s\'', - key_guid, sid, use_32bit + "Software/Component Not Found in Products section of registry " + "key_guid: '%s', sid: '%s', use_32bit: '%s'", + key_guid, + sid, + use_32bit, ) self.__squid = None # mark it as not a SQUID else: @@ -223,17 +249,19 @@ class RegSoftwareInfo(object): self.__mod_time1970 = 0 # pylint: disable=no-member - mod_win_time = win32api.RegQueryInfoKeyW(self.__reg_uninstall_handle).get('LastWriteTime', None) + mod_win_time = win32api.RegQueryInfoKeyW(self.__reg_uninstall_handle).get( + "LastWriteTime", None + ) # pylint: enable=no-member if mod_win_time: # at some stage __int__() was removed from pywintypes.datetime to return secs since 1970 - if hasattr(mod_win_time, 'utctimetuple'): + if hasattr(mod_win_time, "utctimetuple"): self.__mod_time1970 = time.mktime(mod_win_time.utctimetuple()) - elif hasattr(mod_win_time, '__int__'): + elif hasattr(mod_win_time, "__int__"): self.__mod_time1970 = int(mod_win_time) def __squid_to_guid(self, squid): - ''' + """ Squished GUID (SQUID) to GUID. A SQUID is a Squished/Compressed version of a GUID to use up less space @@ -244,25 +272,32 @@ class RegSoftwareInfo(object): Returns: str: the GUID if a valid SQUID provided. - ''' + """ if not squid: - return '' + return "" squid_match = self.__squid_pattern.match(squid) - guid = '' + guid = "" if squid_match is not None: - guid = '{' +\ - squid_match.group(1)[::-1]+'-' +\ - squid_match.group(2)[::-1]+'-' +\ - squid_match.group(3)[::-1]+'-' +\ - squid_match.group(4)[::-1]+squid_match.group(5)[::-1] + '-' + guid = ( + "{" + + squid_match.group(1)[::-1] + + "-" + + squid_match.group(2)[::-1] + + "-" + + squid_match.group(3)[::-1] + + "-" + + squid_match.group(4)[::-1] + + squid_match.group(5)[::-1] + + "-" + ) for index in range(6, 12): guid += squid_match.group(index)[::-1] - guid += '}' + guid += "}" return guid @staticmethod def __one_equals_true(value): - ''' + """ Test for ``1`` as a number or a string and return ``True`` if it is. Args: @@ -270,18 +305,20 @@ class RegSoftwareInfo(object): Returns: bool: ``True`` if 1 otherwise ``False``. - ''' + """ if isinstance(value, six.integer_types) and value == 1: return True - elif (isinstance(value, six.string_types) and - re.match(r'\d+', value, flags=re.IGNORECASE + re.UNICODE) is not None and - six.text_type(value) == '1'): + elif ( + isinstance(value, six.string_types) + and re.match(r"\d+", value, flags=re.IGNORECASE + re.UNICODE) is not None + and six.text_type(value) == "1" + ): return True return False @staticmethod def __reg_query_value(handle, value_name): - ''' + """ Calls RegQueryValueEx If PY2 ensure unicode string and expand REG_EXPAND_SZ before returning @@ -293,12 +330,18 @@ class RegSoftwareInfo(object): Returns: tuple: type, value - ''' + """ # item_value, item_type = win32api.RegQueryValueEx(self.__reg_uninstall_handle, value_name) - item_value, item_type = win32api.RegQueryValueEx(handle, value_name) # pylint: disable=no-member - if six.PY2 and isinstance(item_value, six.string_types) and not isinstance(item_value, six.text_type): + item_value, item_type = win32api.RegQueryValueEx( + handle, value_name + ) # pylint: disable=no-member + if ( + six.PY2 + and isinstance(item_value, six.string_types) + and not isinstance(item_value, six.text_type) + ): try: - item_value = six.text_type(item_value, encoding='mbcs') + item_value = six.text_type(item_value, encoding="mbcs") except UnicodeError: pass if item_type == win32con.REG_EXPAND_SZ: @@ -309,7 +352,7 @@ class RegSoftwareInfo(object): @property def install_time(self): - ''' + """ Return the install time, or provide an estimate of install time. Installers or even self upgrading software must/should update the date @@ -319,12 +362,13 @@ class RegSoftwareInfo(object): Returns: int: Seconds since 1970 UTC. - ''' + """ time1970 = self.__mod_time1970 # time of last resort try: # pylint: disable=no-member - date_string, item_type = \ - win32api.RegQueryValueEx(self.__reg_uninstall_handle, 'InstallDate') + date_string, item_type = win32api.RegQueryValueEx( + self.__reg_uninstall_handle, "InstallDate" + ) except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: return time1970 # i.e. use time of last resort @@ -341,7 +385,7 @@ class RegSoftwareInfo(object): return time1970 def get_install_value(self, value_name, wanted_type=None): - ''' + """ For the uninstall section of the registry return the name value. Args: @@ -353,9 +397,11 @@ class RegSoftwareInfo(object): Returns: value: Value requested or None if not found. - ''' + """ try: - item_value, item_type = self.__reg_query_value(self.__reg_uninstall_handle, value_name) + item_value, item_type = self.__reg_query_value( + self.__reg_uninstall_handle, value_name + ) except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: # Not Found @@ -368,7 +414,7 @@ class RegSoftwareInfo(object): return item_value def is_install_true(self, key): - ''' + """ For the uninstall section check if name value is ``1``. Args: @@ -376,11 +422,11 @@ class RegSoftwareInfo(object): Returns: bool: ``True`` if ``1`` otherwise ``False``. - ''' + """ return self.__one_equals_true(self.get_install_value(key)) def get_product_value(self, value_name, wanted_type=None): - ''' + """ For the product section of the registry return the name value. Args: @@ -392,7 +438,7 @@ class RegSoftwareInfo(object): Returns: value: Value requested or ``None`` if not found. - ''' + """ if not self.__reg_products_handle: return None subkey, search_value_name = os.path.split(value_name) @@ -400,15 +446,19 @@ class RegSoftwareInfo(object): if subkey: handle = win32api.RegOpenKeyEx( # pylint: disable=no-member - self.__reg_products_handle, - subkey, - 0, - win32con.KEY_READ | self.__reg_32bit_access) - item_value, item_type = self.__reg_query_value(handle, search_value_name) + self.__reg_products_handle, + subkey, + 0, + win32con.KEY_READ | self.__reg_32bit_access, + ) + item_value, item_type = self.__reg_query_value( + handle, search_value_name + ) win32api.RegCloseKey(handle) # pylint: disable=no-member else: - item_value, item_type = \ - win32api.RegQueryValueEx(self.__reg_products_handle, value_name) # pylint: disable=no-member + item_value, item_type = win32api.RegQueryValueEx( + self.__reg_products_handle, value_name + ) # pylint: disable=no-member except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: # Not Found @@ -421,77 +471,97 @@ class RegSoftwareInfo(object): @property def upgrade_code(self): - ''' + """ For installers which follow the Microsoft Installer standard, returns the ``Upgrade code``. Returns: value (str): ``Upgrade code`` GUID for installed software. - ''' + """ if not self.__squid: # Must have a valid squid for an upgrade code to exist - return '' + return "" # GUID/SQUID are unique, so it does not matter if they are 32bit or # 64bit or user install so all items are cached into a single dict - have_scan_key = '{0}\\{1}\\{2}'.format(self.__reg_hive, self.__reg_upgradecode_path, self.__reg_32bit) + have_scan_key = "{0}\\{1}\\{2}".format( + self.__reg_hive, self.__reg_upgradecode_path, self.__reg_32bit + ) if not self.__upgrade_codes or self.__reg_key_guid not in self.__upgrade_codes: # Read in the upgrade codes in this section of the registry. try: - uc_handle = win32api.RegOpenKeyEx(getattr(win32con, self.__reg_hive), # pylint: disable=no-member - self.__reg_upgradecode_path, - 0, - win32con.KEY_READ | self.__reg_32bit_access) + uc_handle = win32api.RegOpenKeyEx( + getattr(win32con, self.__reg_hive), # pylint: disable=no-member + self.__reg_upgradecode_path, + 0, + win32con.KEY_READ | self.__reg_32bit_access, + ) except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: # Not Found log.warning( - 'Not Found %s\\%s 32bit %s', + "Not Found %s\\%s 32bit %s", self.__reg_hive, self.__reg_upgradecode_path, - self.__reg_32bit + self.__reg_32bit, ) - return '' + return "" raise - squid_upgrade_code_all, _, _, suc_pytime = zip(*win32api.RegEnumKeyEx(uc_handle)) # pylint: disable=no-member + squid_upgrade_code_all, _, _, suc_pytime = zip( + *win32api.RegEnumKeyEx(uc_handle) + ) # pylint: disable=no-member # Check if we have already scanned these upgrade codes before, and also # check if they have been updated in the registry since last time we scanned. - if (have_scan_key in self.__upgrade_code_have_scan and - self.__upgrade_code_have_scan[have_scan_key] == (squid_upgrade_code_all, suc_pytime)): - log.debug('Scan skipped for upgrade codes, no changes (%s)', have_scan_key) - return '' # we have scanned this before and no new changes. + if have_scan_key in self.__upgrade_code_have_scan and self.__upgrade_code_have_scan[ + have_scan_key + ] == ( + squid_upgrade_code_all, + suc_pytime, + ): + log.debug( + "Scan skipped for upgrade codes, no changes (%s)", have_scan_key + ) + return "" # we have scanned this before and no new changes. # Go into each squid upgrade code and find all the related product codes. - log.debug('Scan for upgrade codes (%s) for product codes', have_scan_key) + log.debug("Scan for upgrade codes (%s) for product codes", have_scan_key) for upgrade_code_squid in squid_upgrade_code_all: upgrade_code_guid = self.__squid_to_guid(upgrade_code_squid) - pc_handle = win32api.RegOpenKeyEx(uc_handle, # pylint: disable=no-member - upgrade_code_squid, - 0, - win32con.KEY_READ | self.__reg_32bit_access) - _, pc_val_count, _ = win32api.RegQueryInfoKey(pc_handle) # pylint: disable=no-member + pc_handle = win32api.RegOpenKeyEx( + uc_handle, # pylint: disable=no-member + upgrade_code_squid, + 0, + win32con.KEY_READ | self.__reg_32bit_access, + ) + _, pc_val_count, _ = win32api.RegQueryInfoKey( + pc_handle + ) # pylint: disable=no-member for item_index in range(pc_val_count): - product_code_guid = \ - self.__squid_to_guid(win32api.RegEnumValue(pc_handle, item_index)[0]) # pylint: disable=no-member + product_code_guid = self.__squid_to_guid( + win32api.RegEnumValue(pc_handle, item_index)[0] + ) # pylint: disable=no-member if product_code_guid: self.__upgrade_codes[product_code_guid] = upgrade_code_guid win32api.RegCloseKey(pc_handle) # pylint: disable=no-member win32api.RegCloseKey(uc_handle) # pylint: disable=no-member - self.__upgrade_code_have_scan[have_scan_key] = (squid_upgrade_code_all, suc_pytime) + self.__upgrade_code_have_scan[have_scan_key] = ( + squid_upgrade_code_all, + suc_pytime, + ) - return self.__upgrade_codes.get(self.__reg_key_guid, '') + return self.__upgrade_codes.get(self.__reg_key_guid, "") @property def list_patches(self): - ''' + """ For installers which follow the Microsoft Installer standard, returns a list of patches applied. Returns: value (list): Long name of the patch. - ''' + """ if not self.__squid: # Must have a valid squid for an upgrade code to exist return [] @@ -499,49 +569,65 @@ class RegSoftwareInfo(object): if self.__patch_list is None: # Read in the upgrade codes in this section of the reg. try: - pat_all_handle = win32api.RegOpenKeyEx(getattr(win32con, self.__reg_hive), # pylint: disable=no-member - self.__reg_patches_path, - 0, - win32con.KEY_READ | self.__reg_32bit_access) + pat_all_handle = win32api.RegOpenKeyEx( + getattr(win32con, self.__reg_hive), # pylint: disable=no-member + self.__reg_patches_path, + 0, + win32con.KEY_READ | self.__reg_32bit_access, + ) except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: # Not Found log.warning( - 'Not Found %s\\%s 32bit %s', + "Not Found %s\\%s 32bit %s", self.__reg_hive, self.__reg_patches_path, - self.__reg_32bit + self.__reg_32bit, ) return [] raise - pc_sub_key_cnt, _, _ = win32api.RegQueryInfoKey(pat_all_handle) # pylint: disable=no-member + pc_sub_key_cnt, _, _ = win32api.RegQueryInfoKey( + pat_all_handle + ) # pylint: disable=no-member if not pc_sub_key_cnt: return [] - squid_patch_all, _, _, _ = zip(*win32api.RegEnumKeyEx(pat_all_handle)) # pylint: disable=no-member + squid_patch_all, _, _, _ = zip( + *win32api.RegEnumKeyEx(pat_all_handle) + ) # pylint: disable=no-member ret = [] # Scan the patches for the DisplayName of active patches. for patch_squid in squid_patch_all: try: patch_squid_handle = win32api.RegOpenKeyEx( # pylint: disable=no-member - pat_all_handle, - patch_squid, - 0, - win32con.KEY_READ | self.__reg_32bit_access) - patch_display_name, patch_display_name_type = \ - self.__reg_query_value(patch_squid_handle, 'DisplayName') - patch_state, patch_state_type = self.__reg_query_value(patch_squid_handle, 'State') - if (patch_state_type != win32con.REG_DWORD or - not isinstance(patch_state_type, six.integer_types) or - patch_state != 1 or # 1 is Active, 2 is Superseded/Obsolute - patch_display_name_type != win32con.REG_SZ): + pat_all_handle, + patch_squid, + 0, + win32con.KEY_READ | self.__reg_32bit_access, + ) + ( + patch_display_name, + patch_display_name_type, + ) = self.__reg_query_value(patch_squid_handle, "DisplayName") + patch_state, patch_state_type = self.__reg_query_value( + patch_squid_handle, "State" + ) + if ( + patch_state_type != win32con.REG_DWORD + or not isinstance(patch_state_type, six.integer_types) + or patch_state != 1 + or patch_display_name_type # 1 is Active, 2 is Superseded/Obsolute + != win32con.REG_SZ + ): continue - win32api.RegCloseKey(patch_squid_handle) # pylint: disable=no-member + win32api.RegCloseKey( + patch_squid_handle + ) # pylint: disable=no-member ret.append(patch_display_name) except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: - log.debug('skipped patch, not found %s', patch_squid) + log.debug("skipped patch, not found %s", patch_squid) continue raise @@ -549,62 +635,62 @@ class RegSoftwareInfo(object): @property def registry_path_text(self): - ''' + """ Returns the uninstall path this object is associated with. Returns: str: <hive>\\<uninstall registry entry> - ''' - return '{0}\\{1}'.format(self.__reg_hive, self.__reg_uninstall_path) + """ + return "{0}\\{1}".format(self.__reg_hive, self.__reg_uninstall_path) @property def registry_path(self): - ''' + """ Returns the uninstall path this object is associated with. Returns: tuple: hive, uninstall registry entry path. - ''' + """ return (self.__reg_hive, self.__reg_uninstall_path) @property def guid(self): - ''' + """ Return GUID or Key. Returns: str: GUID or Key - ''' + """ return self.__reg_key_guid @property def squid(self): - ''' + """ Return SQUID of the GUID if a valid GUID. Returns: str: GUID - ''' + """ return self.__squid @property def package_code(self): - ''' + """ Return package code of the software. Returns: str: GUID - ''' - return self.__squid_to_guid(self.get_product_value('PackageCode')) + """ + return self.__squid_to_guid(self.get_product_value("PackageCode")) @property def version_binary(self): - ''' + """ Return version number which is stored in binary format. Returns: str: <major 0-255>.<minior 0-255>.<build 0-65535> or None if not found - ''' + """ # Under MSI 'Version' is a 'REG_DWORD' which then sets other registry # values like DisplayVersion to x.x.x to the same value. # However not everyone plays by the rules, so we need to check first. @@ -612,39 +698,44 @@ class RegSoftwareInfo(object): # Some installs set 'Version' to REG_SZ (string) which is not # the MSI standard try: - item_value, item_type = self.__reg_query_value(self.__reg_uninstall_handle, 'version') + item_value, item_type = self.__reg_query_value( + self.__reg_uninstall_handle, "version" + ) except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: # Not Found - return '', '' + return "", "" - version_binary_text = '' - version_src = '' + version_binary_text = "" + version_src = "" if item_value: if item_type == win32con.REG_DWORD: if isinstance(item_value, six.integer_types): version_binary_raw = item_value if version_binary_raw: # Major.Minor.Build - version_binary_text = '{0}.{1}.{2}'.format( - version_binary_raw >> 24 & 0xff, - version_binary_raw >> 16 & 0xff, - version_binary_raw & 0xffff) - version_src = 'binary-version' + version_binary_text = "{0}.{1}.{2}".format( + version_binary_raw >> 24 & 0xFF, + version_binary_raw >> 16 & 0xFF, + version_binary_raw & 0xFFFF, + ) + version_src = "binary-version" - elif (item_type == win32con.REG_SZ and - isinstance(item_value, six.string_types) and - self.__version_pattern.match(item_value) is not None): + elif ( + item_type == win32con.REG_SZ + and isinstance(item_value, six.string_types) + and self.__version_pattern.match(item_value) is not None + ): # Hey, version should be a int/REG_DWORD, an installer has set # it to a string - version_binary_text = item_value.strip(' ') - version_src = 'binary-version (string)' + version_binary_text = item_value.strip(" ") + version_src = "binary-version (string)" return (version_binary_text, version_src) class WinSoftware(object): - ''' + """ Point in time snapshot of the software and components installed on a system. @@ -652,33 +743,34 @@ class WinSoftware(object): None :codeauthor: Damon Atkins <https://github.com/damon-atkins> - ''' - __sid_pattern = re.compile(r'^S-\d-\d-\d+$|^S-\d-\d-\d+-\d+-\d+-\d+-\d+$') - __whitespace_pattern = re.compile(r'^\s*$', flags=re.UNICODE) + """ + + __sid_pattern = re.compile(r"^S-\d-\d-\d+$|^S-\d-\d-\d+-\d+-\d+-\d+-\d+$") + __whitespace_pattern = re.compile(r"^\s*$", flags=re.UNICODE) # items we copy out of the uninstall section of the registry without further processing __uninstall_search_list = [ - ('url', 'str', ['URLInfoAbout', 'HelpLink', 'MoreInfoUrl', 'UrlUpdateInfo']), - ('size', 'int', ['Size', 'EstimatedSize']), - ('win_comments', 'str', ['Comments']), - ('win_release_type', 'str', ['ReleaseType']), - ('win_product_id', 'str', ['ProductID']), - ('win_product_codes', 'str', ['ProductCodes']), - ('win_package_refs', 'str', ['PackageRefs']), - ('win_install_location', 'str', ['InstallLocation']), - ('win_install_src_dir', 'str', ['InstallSource']), - ('win_parent_pkg_uid', 'str', ['ParentKeyName']), - ('win_parent_name', 'str', ['ParentDisplayName']) - ] + ("url", "str", ["URLInfoAbout", "HelpLink", "MoreInfoUrl", "UrlUpdateInfo"]), + ("size", "int", ["Size", "EstimatedSize"]), + ("win_comments", "str", ["Comments"]), + ("win_release_type", "str", ["ReleaseType"]), + ("win_product_id", "str", ["ProductID"]), + ("win_product_codes", "str", ["ProductCodes"]), + ("win_package_refs", "str", ["PackageRefs"]), + ("win_install_location", "str", ["InstallLocation"]), + ("win_install_src_dir", "str", ["InstallSource"]), + ("win_parent_pkg_uid", "str", ["ParentKeyName"]), + ("win_parent_name", "str", ["ParentDisplayName"]), + ] # items we copy out of the products section of the registry without further processing __products_search_list = [ - ('win_advertise_flags', 'int', ['AdvertiseFlags']), - ('win_redeployment_flags', 'int', ['DeploymentFlags']), - ('win_instance_type', 'int', ['InstanceType']), - ('win_package_name', 'str', ['SourceList\\PackageName']) - ] + ("win_advertise_flags", "int", ["AdvertiseFlags"]), + ("win_redeployment_flags", "int", ["DeploymentFlags"]), + ("win_instance_type", "int", ["InstanceType"]), + ("win_package_name", "str", ["SourceList\\PackageName"]), + ] def __init__(self, version_only=False, user_pkgs=False, pkg_obj=None): - ''' + """ Point in time snapshot of the software and components installed on a system. @@ -689,7 +781,7 @@ class WinSoftware(object): If None (default) return default package naming standard and use default version capture methods (``DisplayVersion`` then ``Version``, otherwise ``0.0.0.0``) - ''' + """ self.__pkg_obj = pkg_obj # must be set before calling get_software_details self.__version_only = version_only self.__reg_software = {} @@ -699,36 +791,36 @@ class WinSoftware(object): @property def data(self): - ''' + """ Returns the raw data Returns: dict: contents of the dict are dependant on the parameters passed when the class was initiated. - ''' + """ return self.__reg_software @property def version_only(self): - ''' + """ Returns True if class initiated with ``version_only=True`` Returns: bool: The value of ``version_only`` - ''' + """ return self.__version_only def __len__(self): - ''' + """ Returns total number of software/components installed. Returns: int: total number of software/components installed. - ''' + """ return self.__pkg_cnt def __getitem__(self, pkg_id): - ''' + """ Returns information on a package. Args: @@ -736,28 +828,28 @@ class WinSoftware(object): Returns: dict or list: List if ``version_only`` is ``True`` otherwise dict - ''' + """ if pkg_id in self.__reg_software: return self.__reg_software[pkg_id] else: raise KeyError(pkg_id) def __iter__(self): - ''' + """ Standard interation class initialisation over package information. - ''' + """ if self.__iter_list is not None: - raise RuntimeError('Can only perform one iter at a time') + raise RuntimeError("Can only perform one iter at a time") self.__iter_list = collections.deque(sorted(self.__reg_software.keys())) return self def __next__(self): - ''' + """ Returns next Package Id. Returns: str: Package Id - ''' + """ try: return self.__iter_list.popleft() except IndexError: @@ -765,16 +857,16 @@ class WinSoftware(object): raise StopIteration def next(self): - ''' + """ Returns next Package Id. Returns: str: Package Id - ''' + """ return self.__next__() def get(self, pkg_id, default_value=None): - ''' + """ Returns information on a package. Args: @@ -783,25 +875,25 @@ class WinSoftware(object): Returns: dict or list: List if ``version_only`` is ``True`` otherwise dict - ''' + """ return self.__reg_software.get(pkg_id, default_value) @staticmethod def __oldest_to_latest_version(ver1, ver2): - ''' + """ Used for sorting version numbers oldest to latest - ''' + """ return 1 if LooseVersion(ver1) > LooseVersion(ver2) else -1 @staticmethod def __latest_to_oldest_version(ver1, ver2): - ''' + """ Used for sorting version numbers, latest to oldest - ''' + """ return 1 if LooseVersion(ver1) < LooseVersion(ver2) else -1 def pkg_version_list(self, pkg_id): - ''' + """ Returns information on a package. Args: @@ -809,7 +901,7 @@ class WinSoftware(object): Returns: list: List of version numbers installed. - ''' + """ pkg_data = self.__reg_software.get(pkg_id, None) if not pkg_data: return [] @@ -819,11 +911,13 @@ class WinSoftware(object): return pkg_data # already sorted oldest to newest # Must be a dict or OrderDict, and contain full details - installed_versions = list(pkg_data.get('version').keys()) - return sorted(installed_versions, key=cmp_to_key(self.__oldest_to_latest_version)) + installed_versions = list(pkg_data.get("version").keys()) + return sorted( + installed_versions, key=cmp_to_key(self.__oldest_to_latest_version) + ) def pkg_version_latest(self, pkg_id): - ''' + """ Returns a package latest version installed out of all the versions currently installed. @@ -832,11 +926,11 @@ class WinSoftware(object): Returns: str: Latest/Newest version number installed. - ''' + """ return self.pkg_version_list(pkg_id)[-1] def pkg_version_oldest(self, pkg_id): - ''' + """ Returns a package oldest version installed out of all the versions currently installed. @@ -845,12 +939,12 @@ class WinSoftware(object): Returns: str: Oldest version number installed. - ''' + """ return self.pkg_version_list(pkg_id)[0] @staticmethod def __sid_to_username(sid): - ''' + """ Provided with a valid Windows Security Identifier (SID) and returns a Username Args: @@ -858,18 +952,22 @@ class WinSoftware(object): Returns: str: Username in the format of username@realm or username@computer. - ''' - if sid is None or sid == '': - return '' + """ + if sid is None or sid == "": + return "" try: sid_bin = win32security.GetBinarySid(sid) # pylint: disable=no-member except pywintypes.error as exc: # pylint: disable=no-member raise ValueError( - 'pkg: Software owned by {0} is not valid: [{1}] {2}'.format(sid, exc.winerror, exc.strerror) + "pkg: Software owned by {0} is not valid: [{1}] {2}".format( + sid, exc.winerror, exc.strerror ) + ) try: - name, domain, _account_type = win32security.LookupAccountSid(None, sid_bin) # pylint: disable=no-member - user_name = '{0}\\{1}'.format(domain, name) + name, domain, _account_type = win32security.LookupAccountSid( + None, sid_bin + ) # pylint: disable=no-member + user_name = "{0}\\{1}".format(domain, name) except pywintypes.error as exc: # pylint: disable=no-member # if user does not exist... # winerror.ERROR_NONE_MAPPED = No mapping between account names and @@ -880,29 +978,34 @@ class WinSoftware(object): return sid else: raise ValueError( - 'Failed looking up sid \'{0}\' username: [{1}] {2}'.format(sid, exc.winerror, exc.strerror) - ) + "Failed looking up sid '{0}' username: [{1}] {2}".format( + sid, exc.winerror, exc.strerror + ) + ) try: user_principal = win32security.TranslateName( # pylint: disable=no-member - user_name, - win32api.NameSamCompatible, # pylint: disable=no-member - win32api.NameUserPrincipal) # pylint: disable=no-member + user_name, + win32api.NameSamCompatible, # pylint: disable=no-member + win32api.NameUserPrincipal, + ) # pylint: disable=no-member except pywintypes.error as exc: # pylint: disable=no-member # winerror.ERROR_NO_SUCH_DOMAIN The specified domain either does not exist # or could not be contacted, computer may not be part of a domain also # winerror.ERROR_INVALID_DOMAINNAME The format of the specified domain name is # invalid. e.g. S-1-5-19 which is a local account # winerror.ERROR_NONE_MAPPED No mapping between account names and security IDs was done. - if exc.winerror in (winerror.ERROR_NO_SUCH_DOMAIN, - winerror.ERROR_INVALID_DOMAINNAME, - winerror.ERROR_NONE_MAPPED): - return '{0}@{1}'.format(name.lower(), domain.lower()) + if exc.winerror in ( + winerror.ERROR_NO_SUCH_DOMAIN, + winerror.ERROR_INVALID_DOMAINNAME, + winerror.ERROR_NONE_MAPPED, + ): + return "{0}@{1}".format(name.lower(), domain.lower()) else: raise return user_principal def __software_to_pkg_id(self, publisher, name, is_component, is_32bit): - ''' + """ Determine the Package ID of a software/component using the software/component ``publisher``, ``name``, whether its a software or a component, and if its 32bit or 64bit archiecture. @@ -915,42 +1018,44 @@ class WinSoftware(object): Returns: str: Package Id - ''' + """ if publisher: # remove , and lowercase as , are used as list separators - pub_lc = publisher.replace(',', '').lower() + pub_lc = publisher.replace(",", "").lower() else: # remove , and lowercase - pub_lc = 'NoValue' # Capitals/Special Value + pub_lc = "NoValue" # Capitals/Special Value if name: - name_lc = name.replace(',', '').lower() + name_lc = name.replace(",", "").lower() # remove , OR we do the URL Encode on chars we do not want e.g. \\ and , else: - name_lc = 'NoValue' # Capitals/Special Value + name_lc = "NoValue" # Capitals/Special Value if is_component: - soft_type = 'comp' + soft_type = "comp" else: - soft_type = 'soft' + soft_type = "soft" if is_32bit: - soft_type += '32' # Tag only the 32bit only + soft_type += "32" # Tag only the 32bit only - default_pkg_id = pub_lc+'\\\\'+name_lc+'\\\\'+soft_type + default_pkg_id = pub_lc + "\\\\" + name_lc + "\\\\" + soft_type # Check to see if class was initialise with pkg_obj with a method called # to_pkg_id, and if so use it for the naming standard instead of the default - if self.__pkg_obj and hasattr(self.__pkg_obj, 'to_pkg_id'): + if self.__pkg_obj and hasattr(self.__pkg_obj, "to_pkg_id"): pkg_id = self.__pkg_obj.to_pkg_id(publisher, name, is_component, is_32bit) if pkg_id: return pkg_id return default_pkg_id - def __version_capture_slp(self, pkg_id, version_binary, version_display, display_name): - ''' + def __version_capture_slp( + self, pkg_id, version_binary, version_display, display_name + ): + """ This returns the version and where the version string came from, based on instructions under ``version_capture``, if ``version_capture`` is missing, it defaults to value of display-version. @@ -963,75 +1068,101 @@ class WinSoftware(object): Returns: str: Package Id - ''' - if self.__pkg_obj and hasattr(self.__pkg_obj, 'version_capture'): - version_str, src, version_user_str = \ - self.__pkg_obj.version_capture(pkg_id, version_binary, version_display, display_name) - if src != 'use-default' and version_str and src: + """ + if self.__pkg_obj and hasattr(self.__pkg_obj, "version_capture"): + version_str, src, version_user_str = self.__pkg_obj.version_capture( + pkg_id, version_binary, version_display, display_name + ) + if src != "use-default" and version_str and src: return version_str, src, version_user_str - elif src != 'use-default': + elif src != "use-default": raise ValueError( - 'version capture within object \'{0}\' failed ' - 'for pkg id: \'{1}\' it returned \'{2}\' \'{3}\' ' - '\'{4}\''.format(six.text_type(self.__pkg_obj), pkg_id, version_str, src, version_user_str) + "version capture within object '{0}' failed " + "for pkg id: '{1}' it returned '{2}' '{3}' " + "'{4}'".format( + six.text_type(self.__pkg_obj), + pkg_id, + version_str, + src, + version_user_str, ) + ) # If self.__pkg_obj.version_capture() not defined defaults to using # version_display and if not valid then use version_binary, and as a last # result provide the version 0.0.0.0.0 to indicate version string was not determined. - if version_display and re.match(r'\d+', version_display, flags=re.IGNORECASE + re.UNICODE) is not None: + if ( + version_display + and re.match(r"\d+", version_display, flags=re.IGNORECASE + re.UNICODE) + is not None + ): version_str = version_display - src = 'display-version' - elif version_binary and re.match(r'\d+', version_binary, flags=re.IGNORECASE + re.UNICODE) is not None: + src = "display-version" + elif ( + version_binary + and re.match(r"\d+", version_binary, flags=re.IGNORECASE + re.UNICODE) + is not None + ): version_str = version_binary - src = 'version-binary' + src = "version-binary" else: - src = 'none' - version_str = '0.0.0.0.0' + src = "none" + version_str = "0.0.0.0.0" # return version str, src of the version, "user" interpretation of the version # which by default is version_str return version_str, src, version_str def __collect_software_info(self, sid, key_software, use_32bit): - ''' + """ Update data with the next software found - ''' + """ reg_soft_info = RegSoftwareInfo(key_software, sid, use_32bit) # Check if the registry entry is a valid. # a) Cannot manage software without at least a display name - display_name = reg_soft_info.get_install_value('DisplayName', wanted_type='str') + display_name = reg_soft_info.get_install_value("DisplayName", wanted_type="str") if display_name is None or self.__whitespace_pattern.match(display_name): return # b) make sure its not an 'Hotfix', 'Update Rollup', 'Security Update', 'ServicePack' # General this is software which pre dates Windows 10 - default_value = reg_soft_info.get_install_value('', wanted_type='str') - release_type = reg_soft_info.get_install_value('ReleaseType', wanted_type='str') + default_value = reg_soft_info.get_install_value("", wanted_type="str") + release_type = reg_soft_info.get_install_value("ReleaseType", wanted_type="str") - if (re.match(r'^{.*\}\.KB\d{6,}$', key_software, flags=re.IGNORECASE + re.UNICODE) is not None or - (default_value and default_value.startswith(('KB', 'kb', 'Kb'))) or - (release_type and release_type in ('Hotfix', 'Update Rollup', 'Security Update', 'ServicePack'))): - log.debug('skipping hotfix/update/service pack %s', key_software) + if ( + re.match( + r"^{.*\}\.KB\d{6,}$", key_software, flags=re.IGNORECASE + re.UNICODE + ) + is not None + or (default_value and default_value.startswith(("KB", "kb", "Kb"))) + or ( + release_type + and release_type + in ("Hotfix", "Update Rollup", "Security Update", "ServicePack") + ) + ): + log.debug("skipping hotfix/update/service pack %s", key_software) return # if NoRemove exists we would expect their to be no UninstallString - uninstall_no_remove = reg_soft_info.is_install_true('NoRemove') - uninstall_string = reg_soft_info.get_install_value('UninstallString') - uninstall_quiet_string = reg_soft_info.get_install_value('QuietUninstallString') - uninstall_modify_path = reg_soft_info.get_install_value('ModifyPath') - windows_installer = reg_soft_info.is_install_true('WindowsInstaller') - system_component = reg_soft_info.is_install_true('SystemComponent') - publisher = reg_soft_info.get_install_value('Publisher', wanted_type='str') + uninstall_no_remove = reg_soft_info.is_install_true("NoRemove") + uninstall_string = reg_soft_info.get_install_value("UninstallString") + uninstall_quiet_string = reg_soft_info.get_install_value("QuietUninstallString") + uninstall_modify_path = reg_soft_info.get_install_value("ModifyPath") + windows_installer = reg_soft_info.is_install_true("WindowsInstaller") + system_component = reg_soft_info.is_install_true("SystemComponent") + publisher = reg_soft_info.get_install_value("Publisher", wanted_type="str") # UninstallString is optional if the installer is "windows installer"/MSI # However for it to appear in Control-Panel -> Program and Features -> Uninstall or change a program # the UninstallString needs to be set or ModifyPath set - if (uninstall_string is None and - uninstall_quiet_string is None and - uninstall_modify_path is None and - (not windows_installer)): + if ( + uninstall_string is None + and uninstall_quiet_string is None + and uninstall_modify_path is None + and (not windows_installer) + ): return # Question: If uninstall string is not set and windows_installer should we set it @@ -1043,19 +1174,26 @@ class WinSoftware(object): username = None # We now have a valid software install or a system component - pkg_id = self.__software_to_pkg_id(publisher, display_name, system_component, use_32bit) + pkg_id = self.__software_to_pkg_id( + publisher, display_name, system_component, use_32bit + ) version_binary, version_src = reg_soft_info.version_binary - version_display = reg_soft_info.get_install_value('DisplayVersion', wanted_type='str') + version_display = reg_soft_info.get_install_value( + "DisplayVersion", wanted_type="str" + ) # version_capture is what the slp defines, the result overrides. Question: maybe it should error if it fails? - (version_text, version_src, user_version) = \ - self.__version_capture_slp(pkg_id, version_binary, version_display, display_name) + (version_text, version_src, user_version) = self.__version_capture_slp( + pkg_id, version_binary, version_display, display_name + ) if not user_version: user_version = version_text # log.trace('%s\\%s ver:%s src:%s', username or 'SYSTEM', pkg_id, version_text, version_src) if username: - dict_key = '{};{}'.format(username, pkg_id) # Use ; as its not a valid hostnmae char + dict_key = "{};{}".format( + username, pkg_id + ) # Use ; as its not a valid hostnmae char else: dict_key = pkg_id @@ -1078,9 +1216,11 @@ class WinSoftware(object): # This code is here as it can happen, especially if the # package id provided by pkg_obj is simple. log.debug( - 'Found extra entries for \'%s\' with same version ' - '\'%s\', skipping entry \'%s\'', - dict_key, version_text, key_software + "Found extra entries for '%s' with same version " + "'%s', skipping entry '%s'", + dict_key, + version_text, + key_software, ) else: self.__reg_software[dict_key] = [version_text] @@ -1094,106 +1234,137 @@ class WinSoftware(object): if sid: # HKEY_USERS has no 32bit and 64bit view like HKEY_LOCAL_MACHINE - data.update({'arch': 'unknown'}) + data.update({"arch": "unknown"}) else: - arch_str = 'x86' if use_32bit else 'x64' - if 'arch' in data: - if data['arch'] != arch_str: - data['arch'] = 'many' + arch_str = "x86" if use_32bit else "x64" + if "arch" in data: + if data["arch"] != arch_str: + data["arch"] = "many" else: - data.update({'arch': arch_str}) + data.update({"arch": arch_str}) if publisher: - if 'vendor' in data: - if data['vendor'].lower() != publisher.lower(): - data['vendor'] = 'many' + if "vendor" in data: + if data["vendor"].lower() != publisher.lower(): + data["vendor"] = "many" else: - data['vendor'] = publisher + data["vendor"] = publisher - if 'win_system_component' in data: - if data['win_system_component'] != system_component: - data['win_system_component'] = None + if "win_system_component" in data: + if data["win_system_component"] != system_component: + data["win_system_component"] = None else: - data['win_system_component'] = system_component + data["win_system_component"] = system_component - data.update({'win_version_src': version_src}) + data.update({"win_version_src": version_src}) - data.setdefault('version', {}) - if version_text in data['version']: - if 'win_install_count' in data['version'][version_text]: - data['version'][version_text]['win_install_count'] += 1 + data.setdefault("version", {}) + if version_text in data["version"]: + if "win_install_count" in data["version"][version_text]: + data["version"][version_text]["win_install_count"] += 1 else: # This is only defined when we have the same item already - data['version'][version_text]['win_install_count'] = 2 + data["version"][version_text]["win_install_count"] = 2 else: - data['version'][version_text] = OrderedDict() + data["version"][version_text] = OrderedDict() - version_data = data['version'][version_text] - version_data.update({'win_display_name': display_name}) + version_data = data["version"][version_text] + version_data.update({"win_display_name": display_name}) if uninstall_string: - version_data.update({'win_uninstall_cmd': uninstall_string}) + version_data.update({"win_uninstall_cmd": uninstall_string}) if uninstall_quiet_string: - version_data.update({'win_uninstall_quiet_cmd': uninstall_quiet_string}) + version_data.update({"win_uninstall_quiet_cmd": uninstall_quiet_string}) if uninstall_no_remove: - version_data.update({'win_uninstall_no_remove': uninstall_no_remove}) + version_data.update({"win_uninstall_no_remove": uninstall_no_remove}) - version_data.update({'win_product_code': key_software}) + version_data.update({"win_product_code": key_software}) if version_display: - version_data.update({'win_version_display': version_display}) + version_data.update({"win_version_display": version_display}) if version_binary: - version_data.update({'win_version_binary': version_binary}) + version_data.update({"win_version_binary": version_binary}) if user_version: - version_data.update({'win_version_user': user_version}) + version_data.update({"win_version_user": user_version}) # Determine Installer Product # 'NSIS:Language' # 'Inno Setup: Setup Version' - if (windows_installer or - (uninstall_string and - re.search(r'MsiExec.exe\s|MsiExec\s', uninstall_string, flags=re.IGNORECASE + re.UNICODE))): - version_data.update({'win_installer_type': 'winmsi'}) - elif (re.match(r'InstallShield_', key_software, re.IGNORECASE) is not None or - (uninstall_string and ( - re.search(r'InstallShield', uninstall_string, flags=re.IGNORECASE + re.UNICODE) is not None or - re.search(r'isuninst\.exe.*\.isu', uninstall_string, flags=re.IGNORECASE + re.UNICODE) is not None) - ) - ): - version_data.update({'win_installer_type': 'installshield'}) - elif (key_software.endswith('_is1') and - reg_soft_info.get_install_value('Inno Setup: Setup Version', wanted_type='str')): - version_data.update({'win_installer_type': 'inno'}) - elif (uninstall_string and - re.search(r'.*\\uninstall.exe|.*\\uninst.exe', uninstall_string, flags=re.IGNORECASE + re.UNICODE)): - version_data.update({'win_installer_type': 'nsis'}) + if windows_installer or ( + uninstall_string + and re.search( + r"MsiExec.exe\s|MsiExec\s", + uninstall_string, + flags=re.IGNORECASE + re.UNICODE, + ) + ): + version_data.update({"win_installer_type": "winmsi"}) + elif re.match(r"InstallShield_", key_software, re.IGNORECASE) is not None or ( + uninstall_string + and ( + re.search( + r"InstallShield", uninstall_string, flags=re.IGNORECASE + re.UNICODE + ) + is not None + or re.search( + r"isuninst\.exe.*\.isu", + uninstall_string, + flags=re.IGNORECASE + re.UNICODE, + ) + is not None + ) + ): + version_data.update({"win_installer_type": "installshield"}) + elif key_software.endswith("_is1") and reg_soft_info.get_install_value( + "Inno Setup: Setup Version", wanted_type="str" + ): + version_data.update({"win_installer_type": "inno"}) + elif uninstall_string and re.search( + r".*\\uninstall.exe|.*\\uninst.exe", + uninstall_string, + flags=re.IGNORECASE + re.UNICODE, + ): + version_data.update({"win_installer_type": "nsis"}) else: - version_data.update({'win_installer_type': 'unknown'}) + version_data.update({"win_installer_type": "unknown"}) # Update dict with information retrieved so far for detail results to be return # Do not add fields which are blank. - language_number = reg_soft_info.get_install_value('Language') - if isinstance(language_number, six.integer_types) and language_number in locale.windows_locale: - version_data.update({'win_language': locale.windows_locale[language_number]}) + language_number = reg_soft_info.get_install_value("Language") + if ( + isinstance(language_number, six.integer_types) + and language_number in locale.windows_locale + ): + version_data.update( + {"win_language": locale.windows_locale[language_number]} + ) package_code = reg_soft_info.package_code if package_code: - version_data.update({'win_package_code': package_code}) + version_data.update({"win_package_code": package_code}) upgrade_code = reg_soft_info.upgrade_code if upgrade_code: - version_data.update({'win_upgrade_code': upgrade_code}) + version_data.update({"win_upgrade_code": upgrade_code}) - is_minor_upgrade = reg_soft_info.is_install_true('IsMinorUpgrade') + is_minor_upgrade = reg_soft_info.is_install_true("IsMinorUpgrade") if is_minor_upgrade: - version_data.update({'win_is_minor_upgrade': is_minor_upgrade}) + version_data.update({"win_is_minor_upgrade": is_minor_upgrade}) install_time = reg_soft_info.install_time if install_time: - version_data.update({'install_date': datetime.datetime.fromtimestamp(install_time).isoformat()}) - version_data.update({'install_date_time_t': int(install_time)}) + version_data.update( + { + "install_date": datetime.datetime.fromtimestamp( + install_time + ).isoformat() + } + ) + version_data.update({"install_date_time_t": int(install_time)}) for infokey, infotype, regfield_list in self.__uninstall_search_list: for regfield in regfield_list: - strvalue = reg_soft_info.get_install_value(regfield, wanted_type=infotype) + strvalue = reg_soft_info.get_install_value( + regfield, wanted_type=infotype + ) if strvalue: version_data.update({infokey: strvalue}) break @@ -1206,20 +1377,20 @@ class WinSoftware(object): break patch_list = reg_soft_info.list_patches if patch_list: - version_data.update({'win_patches': patch_list}) + version_data.update({"win_patches": patch_list}) def __get_software_details(self, user_pkgs): - ''' + """ This searches the uninstall keys in the registry to find a match in the sub keys, it will return a dict with the display name as the key and the version as the value .. sectionauthor:: Damon Atkins <https://github.com/damon-atkins> .. versionadded:: Carbon - ''' + """ # FUNCTION MAIN CODE # # Search 64bit, on 64bit platform, on 32bit its ignored. - if platform.architecture()[0] == '32bit': + if platform.architecture()[0] == "32bit": # Handle Python 32bit on 64&32 bit platform and Python 64bit if win32process.IsWow64Process(): # pylint: disable=no-member # 32bit python on a 64bit platform @@ -1237,14 +1408,17 @@ class WinSoftware(object): # Process software installed for the machine i.e. all users. for arch_flag in arch_list: - key_search = 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall' - log.debug('SYSTEM processing 32bit:%s', arch_flag) + key_search = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall" + log.debug("SYSTEM processing 32bit:%s", arch_flag) handle = win32api.RegOpenKeyEx( # pylint: disable=no-member - win32con.HKEY_LOCAL_MACHINE, - key_search, - 0, - win32con.KEY_READ | use_32bit_lookup[arch_flag]) - reg_key_all, _, _, _ = zip(*win32api.RegEnumKeyEx(handle)) # pylint: disable=no-member + win32con.HKEY_LOCAL_MACHINE, + key_search, + 0, + win32con.KEY_READ | use_32bit_lookup[arch_flag], + ) + reg_key_all, _, _, _ = zip( + *win32api.RegEnumKeyEx(handle) + ) # pylint: disable=no-member win32api.RegCloseKey(handle) # pylint: disable=no-member for reg_key in reg_key_all: self.__collect_software_info(None, reg_key, arch_flag) @@ -1254,36 +1428,42 @@ class WinSoftware(object): # Process software installed under all USERs, this adds significate processing time. # There is not 32/64 bit registry redirection under user tree. - log.debug('Processing user software... please wait') + log.debug("Processing user software... please wait") handle_sid = win32api.RegOpenKeyEx( # pylint: disable=no-member - win32con.HKEY_USERS, - '', - 0, - win32con.KEY_READ) + win32con.HKEY_USERS, "", 0, win32con.KEY_READ + ) sid_all = [] - for index in range(win32api.RegQueryInfoKey(handle_sid)[0]): # pylint: disable=no-member - sid_all.append(win32api.RegEnumKey(handle_sid, index)) # pylint: disable=no-member + for index in range( + win32api.RegQueryInfoKey(handle_sid)[0] + ): # pylint: disable=no-member + sid_all.append( + win32api.RegEnumKey(handle_sid, index) + ) # pylint: disable=no-member for sid in sid_all: - if self.__sid_pattern.match(sid) is not None: # S-1-5-18 needs to be ignored? - user_uninstall_path = '{0}\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall'.format(sid) + if ( + self.__sid_pattern.match(sid) is not None + ): # S-1-5-18 needs to be ignored? + user_uninstall_path = "{0}\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall".format( + sid + ) try: handle = win32api.RegOpenKeyEx( # pylint: disable=no-member - handle_sid, - user_uninstall_path, - 0, - win32con.KEY_READ) + handle_sid, user_uninstall_path, 0, win32con.KEY_READ + ) except pywintypes.error as exc: # pylint: disable=no-member if exc.winerror == winerror.ERROR_FILE_NOT_FOUND: # Not Found Uninstall under SID - log.debug('Not Found %s', user_uninstall_path) + log.debug("Not Found %s", user_uninstall_path) continue else: raise try: - reg_key_all, _, _, _ = zip(*win32api.RegEnumKeyEx(handle)) # pylint: disable=no-member + reg_key_all, _, _, _ = zip( + *win32api.RegEnumKeyEx(handle) + ) # pylint: disable=no-member except ValueError: - log.debug('No Entries Found %s', user_uninstall_path) + log.debug("No Entries Found %s", user_uninstall_path) reg_key_all = [] win32api.RegCloseKey(handle) # pylint: disable=no-member for reg_key in reg_key_all: @@ -1293,33 +1473,39 @@ class WinSoftware(object): def __main(): - '''This module can also be run directly for testing + """This module can also be run directly for testing Args: detail|list : Provide ``detail`` or version ``list``. system|system+user: System installed and System and User installs. - ''' + """ if len(sys.argv) < 3: - sys.stderr.write('usage: {0} <detail|list> <system|system+user>\n'.format(sys.argv[0])) + sys.stderr.write( + "usage: {0} <detail|list> <system|system+user>\n".format(sys.argv[0]) + ) sys.exit(64) user_pkgs = False version_only = False - if six.text_type(sys.argv[1]) == 'list': + if six.text_type(sys.argv[1]) == "list": version_only = True - if six.text_type(sys.argv[2]) == 'system+user': + if six.text_type(sys.argv[2]) == "system+user": user_pkgs = True import salt.utils.json import timeit def run(): - ''' + """ Main run code, when this module is run directly - ''' + """ pkg_list = WinSoftware(user_pkgs=user_pkgs, version_only=version_only) - print(salt.utils.json.dumps(pkg_list.data, sort_keys=True, indent=4)) # pylint: disable=superfluous-parens - print('Total: {}'.format(len(pkg_list))) # pylint: disable=superfluous-parens + print( + salt.utils.json.dumps(pkg_list.data, sort_keys=True, indent=4) + ) # pylint: disable=superfluous-parens + print("Total: {}".format(len(pkg_list))) # pylint: disable=superfluous-parens - print('Time Taken: {}'.format(timeit.timeit(run, number=1))) # pylint: disable=superfluous-parens + print( + "Time Taken: {}".format(timeit.timeit(run, number=1)) + ) # pylint: disable=superfluous-parens -if __name__ == '__main__': +if __name__ == "__main__": __main() diff --git a/salt/utils/platform.py b/salt/utils/platform.py index 2f4c4d4974f..0fb220175ff 100644 --- a/salt/utils/platform.py +++ b/salt/utils/platform.py @@ -1,14 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Functions for identifying which platform a machine is -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import subprocess import sys - import warnings + +# Import Salt libs +from salt.utils.decorators import memoize as real_memoize + # linux_distribution deprecated in py3.7 try: from platform import linux_distribution as _deprecated_linux_distribution @@ -17,31 +21,31 @@ try: with warnings.catch_warnings(): warnings.simplefilter("ignore") return _deprecated_linux_distribution(**kwargs) + + except ImportError: from distro import linux_distribution -# Import Salt libs -from salt.utils.decorators import memoize as real_memoize - @real_memoize def is_windows(): - ''' + """ Simple function to return if a host is Windows or not - ''' - return sys.platform.startswith('win') + """ + return sys.platform.startswith("win") @real_memoize def is_proxy(): - ''' + """ Return True if this minion is a proxy minion. Leverages the fact that is_linux() and is_windows both return False for proxies. TODO: Need to extend this for proxies that might run on other Unices - ''' + """ import __main__ as main + # This is a hack. If a proxy minion is started by other # means, e.g. a custom script that creates the minion objects # then this will fail. @@ -52,7 +56,7 @@ def is_proxy(): # # Add '--proxyid' in sys.argv so that salt-call --proxyid # is seen as a proxy minion - if 'proxy' in main.__file__ or '--proxyid' in sys.argv: + if "proxy" in main.__file__ or "--proxyid" in sys.argv: ret = True except AttributeError: pass @@ -61,58 +65,58 @@ def is_proxy(): @real_memoize def is_linux(): - ''' + """ Simple function to return if a host is Linux or not. Note for a proxy minion, we need to return something else - ''' - return sys.platform.startswith('linux') + """ + return sys.platform.startswith("linux") @real_memoize def is_darwin(): - ''' + """ Simple function to return if a host is Darwin (macOS) or not - ''' - return sys.platform.startswith('darwin') + """ + return sys.platform.startswith("darwin") @real_memoize def is_sunos(): - ''' + """ Simple function to return if host is SunOS or not - ''' - return sys.platform.startswith('sunos') + """ + return sys.platform.startswith("sunos") @real_memoize def is_smartos(): - ''' + """ Simple function to return if host is SmartOS (Illumos) or not - ''' + """ if not is_sunos(): return False else: - return os.uname()[3].startswith('joyent_') + return os.uname()[3].startswith("joyent_") @real_memoize def is_smartos_globalzone(): - ''' + """ Function to return if host is SmartOS (Illumos) global zone or not - ''' + """ if not is_smartos(): return False else: - cmd = ['zonename'] + cmd = ["zonename"] try: zonename = subprocess.Popen( - cmd, shell=False, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) except OSError: return False if zonename.returncode: return False - if zonename.stdout.read().strip() == 'global': + if zonename.stdout.read().strip() == "global": return True return False @@ -120,22 +124,22 @@ def is_smartos_globalzone(): @real_memoize def is_smartos_zone(): - ''' + """ Function to return if host is SmartOS (Illumos) and not the gz - ''' + """ if not is_smartos(): return False else: - cmd = ['zonename'] + cmd = ["zonename"] try: zonename = subprocess.Popen( - cmd, shell=False, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) except OSError: return False if zonename.returncode: return False - if zonename.stdout.read().strip() == 'global': + if zonename.stdout.read().strip() == "global": return False return True @@ -143,41 +147,42 @@ def is_smartos_zone(): @real_memoize def is_freebsd(): - ''' + """ Simple function to return if host is FreeBSD or not - ''' - return sys.platform.startswith('freebsd') + """ + return sys.platform.startswith("freebsd") @real_memoize def is_netbsd(): - ''' + """ Simple function to return if host is NetBSD or not - ''' - return sys.platform.startswith('netbsd') + """ + return sys.platform.startswith("netbsd") @real_memoize def is_openbsd(): - ''' + """ Simple function to return if host is OpenBSD or not - ''' - return sys.platform.startswith('openbsd') + """ + return sys.platform.startswith("openbsd") @real_memoize def is_aix(): - ''' + """ Simple function to return if host is AIX or not - ''' - return sys.platform.startswith('aix') + """ + return sys.platform.startswith("aix") @real_memoize def is_fedora(): - ''' + """ Simple function to return if host is Fedora or not - ''' - (osname, osrelease, oscodename) = \ - [x.strip('"').strip("'") for x in linux_distribution()] - return osname == 'Fedora' + """ + (osname, osrelease, oscodename) = [ + x.strip('"').strip("'") for x in linux_distribution() + ] + return osname == "Fedora" diff --git a/salt/utils/powershell.py b/salt/utils/powershell.py index 7b7639132e4..f279435a19b 100644 --- a/salt/utils/powershell.py +++ b/salt/utils/powershell.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Common functions for working with powershell .. note:: The PSModulePath environment variable should be set to the default @@ -7,9 +7,10 @@ Common functions for working with powershell powershell. If not set, then Salt will attempt to use some default paths. If Salt can't find your modules, ensure that the PSModulePath is set and pointing to all locations of your Powershell modules. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os @@ -19,7 +20,7 @@ log = logging.getLogger(__name__) def module_exists(name): - ''' + """ Check if a module exists on the system. Use this utility instead of attempting to import the module with powershell. @@ -39,12 +40,12 @@ def module_exists(name): import salt.utils.powershell exists = salt.utils.powershell.module_exists('ServerManager') - ''' + """ return name in get_modules() def get_modules(): - ''' + """ Get a list of the PowerShell modules which are potentially available to be imported. The intent is to mimic the functionality of ``Get-Module -ListAvailable | Select-Object -Expand Name``, without the delay of loading @@ -59,9 +60,9 @@ def get_modules(): import salt.utils.powershell modules = salt.utils.powershell.get_modules() - ''' + """ ret = list() - valid_extensions = ('.psd1', '.psm1', '.cdxml', '.xaml', '.dll') + valid_extensions = (".psd1", ".psm1", ".cdxml", ".xaml", ".dll") # need to create an info function to get PS information including version # __salt__ is not available from salt.utils... need to create a salt.util # for the registry to avoid loading powershell to get the version @@ -71,30 +72,31 @@ def get_modules(): # ps_version = info()['version_raw'] root_paths = [] - home_dir = os.environ.get('HOME', os.environ.get('HOMEPATH')) - system_dir = '{0}\\System32'.format(os.environ.get('WINDIR', 'C:\\Windows')) - program_files = os.environ.get('ProgramFiles', 'C:\\Program Files') + home_dir = os.environ.get("HOME", os.environ.get("HOMEPATH")) + system_dir = "{0}\\System32".format(os.environ.get("WINDIR", "C:\\Windows")) + program_files = os.environ.get("ProgramFiles", "C:\\Program Files") default_paths = [ - '{0}/.local/share/powershell/Modules'.format(home_dir), + "{0}/.local/share/powershell/Modules".format(home_dir), # Once version is available, these can be enabled # '/opt/microsoft/powershell/{0}/Modules'.format(ps_version), # '/usr/local/microsoft/powershell/{0}/Modules'.format(ps_version), - '/usr/local/share/powershell/Modules', - '{0}\\WindowsPowerShell\\v1.0\\Modules\\'.format(system_dir), - '{0}\\WindowsPowerShell\\Modules'.format(program_files)] - default_paths = ';'.join(default_paths) + "/usr/local/share/powershell/Modules", + "{0}\\WindowsPowerShell\\v1.0\\Modules\\".format(system_dir), + "{0}\\WindowsPowerShell\\Modules".format(program_files), + ] + default_paths = ";".join(default_paths) - ps_module_path = os.environ.get('PSModulePath', default_paths) + ps_module_path = os.environ.get("PSModulePath", default_paths) # Check if defaults exist, add them if they do - ps_module_path = ps_module_path.split(';') + ps_module_path = ps_module_path.split(";") for item in ps_module_path: if os.path.exists(item): root_paths.append(item) # Did we find any, if not log the error and return if not root_paths: - log.error('Default paths not found') + log.error("Default paths not found") return ret for root_path in root_paths: @@ -116,8 +118,7 @@ def get_modules(): # Stop recursion once we find a match, and use # the capitalization from the directory name. - if dir_name not in ret and \ - base_name.lower() == dir_name.lower(): + if dir_name not in ret and base_name.lower() == dir_name.lower(): del sub_dirs[:] ret.append(dir_name) diff --git a/salt/utils/preseed.py b/salt/utils/preseed.py index 9ec5ee7f528..0a780ecf050 100644 --- a/salt/utils/preseed.py +++ b/salt/utils/preseed.py @@ -1,25 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" Utilities for managing Debian preseed .. versionadded:: Beryllium -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import shlex + import salt.utils.files import salt.utils.stringutils import salt.utils.yaml def mksls(src, dst=None): - ''' + """ Convert a preseed file to an SLS file - ''' + """ ps_opts = {} - with salt.utils.files.fopen(src, 'r') as fh_: + with salt.utils.files.fopen(src, "r") as fh_: for line in fh_: line = salt.utils.stringutils.to_unicode(line) - if line.startswith('#'): + if line.startswith("#"): continue if not line.strip(): continue @@ -27,54 +29,54 @@ def mksls(src, dst=None): comps = shlex.split(line) if comps[0] not in ps_opts.keys(): ps_opts[comps[0]] = {} - cmds = comps[1].split('/') + cmds = comps[1].split("/") pointer = ps_opts[comps[0]] for cmd in cmds: pointer = pointer.setdefault(cmd, {}) - pointer['type'] = comps[2] + pointer["type"] = comps[2] if len(comps) > 3: - pointer['argument'] = comps[3] + pointer["argument"] = comps[3] sls = {} # Set language # ( This looks like it maps to something else ) - sls[ps_opts['d-i']['languagechooser']['language-name-fb']['argument']] = { - 'locale': ['system'] - } + sls[ps_opts["d-i"]["languagechooser"]["language-name-fb"]["argument"]] = { + "locale": ["system"] + } # Set keyboard # ( This looks like it maps to something else ) - sls[ps_opts['d-i']['kbd-chooser']['method']['argument']] = { - 'keyboard': ['system'] - } + sls[ps_opts["d-i"]["kbd-chooser"]["method"]["argument"]] = {"keyboard": ["system"]} # Set timezone - timezone = ps_opts['d-i']['time']['zone']['argument'] - sls[timezone] = {'timezone': ['system']} - if ps_opts['d-i']['tzconfig']['gmt']['argument'] == 'true': - sls[timezone]['timezone'].append('utc') + timezone = ps_opts["d-i"]["time"]["zone"]["argument"] + sls[timezone] = {"timezone": ["system"]} + if ps_opts["d-i"]["tzconfig"]["gmt"]["argument"] == "true": + sls[timezone]["timezone"].append("utc") # Set network - if 'netcfg' in ps_opts['d-i'].keys(): - iface = ps_opts['d-i']['netcfg']['choose_interface']['argument'] + if "netcfg" in ps_opts["d-i"].keys(): + iface = ps_opts["d-i"]["netcfg"]["choose_interface"]["argument"] sls[iface] = {} - sls[iface]['enabled'] = True - if ps_opts['d-i']['netcfg']['confirm_static'] == 'true': - sls[iface]['proto'] = 'static' - elif ps_opts['d-i']['netcfg']['disable_dhcp'] == 'false': - sls[iface]['proto'] = 'dhcp' - sls[iface]['netmask'] = ps_opts['d-i']['netcfg']['get_netmask']['argument'] - sls[iface]['domain'] = ps_opts['d-i']['netcfg']['get_domain']['argument'] - sls[iface]['gateway'] = ps_opts['d-i']['netcfg']['get_gateway']['argument'] - sls[iface]['hostname'] = ps_opts['d-i']['netcfg']['get_hostname']['argument'] - sls[iface]['ipaddress'] = ps_opts['d-i']['netcfg']['get_ipaddress']['argument'] - sls[iface]['nameservers'] = ps_opts['d-i']['netcfg']['get_nameservers']['argument'] + sls[iface]["enabled"] = True + if ps_opts["d-i"]["netcfg"]["confirm_static"] == "true": + sls[iface]["proto"] = "static" + elif ps_opts["d-i"]["netcfg"]["disable_dhcp"] == "false": + sls[iface]["proto"] = "dhcp" + sls[iface]["netmask"] = ps_opts["d-i"]["netcfg"]["get_netmask"]["argument"] + sls[iface]["domain"] = ps_opts["d-i"]["netcfg"]["get_domain"]["argument"] + sls[iface]["gateway"] = ps_opts["d-i"]["netcfg"]["get_gateway"]["argument"] + sls[iface]["hostname"] = ps_opts["d-i"]["netcfg"]["get_hostname"]["argument"] + sls[iface]["ipaddress"] = ps_opts["d-i"]["netcfg"]["get_ipaddress"]["argument"] + sls[iface]["nameservers"] = ps_opts["d-i"]["netcfg"]["get_nameservers"][ + "argument" + ] if dst is not None: - with salt.utils.files.fopen(dst, 'w') as fh_: + with salt.utils.files.fopen(dst, "w") as fh_: salt.utils.yaml.safe_dump(sls, fh_, default_flow_style=False) else: return salt.utils.yaml.safe_dump(sls, default_flow_style=False) diff --git a/salt/utils/process.py b/salt/utils/process.py index 9626ac0cb2f..aca58dc574f 100644 --- a/salt/utils/process.py +++ b/salt/utils/process.py @@ -1,41 +1,41 @@ # -*- coding: utf-8 -*- -''' +""" Functions for daemonizing and otherwise modifying running processes -''' +""" # Import python libs -from __future__ import absolute_import, with_statement, print_function, unicode_literals -import copy -import io -import os -import sys -import time -import errno -import types -import signal -import logging -import functools -import threading +from __future__ import absolute_import, print_function, unicode_literals, with_statement + import contextlib -import subprocess +import copy +import errno +import functools +import io +import logging import multiprocessing import multiprocessing.util +import os +import signal import socket +import subprocess +import sys +import threading +import time +import types # Import salt libs import salt.defaults.exitcodes +import salt.log.setup import salt.utils.files import salt.utils.path import salt.utils.platform -import salt.log.setup -import salt.defaults.exitcodes import salt.utils.versions -from salt.log.mixins import NewStyleClassMixIn # Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import queue, range # pylint: disable=import-error,redefined-builtin +from salt.ext.six.moves import queue, range from salt.ext.tornado import gen +from salt.log.mixins import NewStyleClassMixIn log = logging.getLogger(__name__) @@ -43,31 +43,34 @@ log = logging.getLogger(__name__) HAS_PSUTIL = False try: import psutil + HAS_PSUTIL = True except ImportError: pass try: import setproctitle + HAS_SETPROCTITLE = True except ImportError: HAS_SETPROCTITLE = False def appendproctitle(name): - ''' + """ Append "name" to the current process title - ''' + """ if HAS_SETPROCTITLE: - setproctitle.setproctitle(setproctitle.getproctitle() + ' ' + name) + setproctitle.setproctitle(setproctitle.getproctitle() + " " + name) def daemonize(redirect_out=True): - ''' + """ Daemonize a process - ''' + """ # Avoid circular import import salt.utils.crypt + try: pid = os.fork() if pid > 0: @@ -75,11 +78,11 @@ def daemonize(redirect_out=True): salt.utils.crypt.reinit_crypto() os._exit(salt.defaults.exitcodes.EX_OK) except OSError as exc: - log.error('fork #1 failed: %s (%s)', exc.errno, exc) + log.error("fork #1 failed: %s (%s)", exc.errno, exc) sys.exit(salt.defaults.exitcodes.EX_GENERIC) # decouple from parent environment - os.chdir('/') + os.chdir("/") # noinspection PyArgumentList os.setsid() os.umask(0o022) # pylint: disable=blacklisted-function @@ -91,7 +94,7 @@ def daemonize(redirect_out=True): salt.utils.crypt.reinit_crypto() sys.exit(salt.defaults.exitcodes.EX_OK) except OSError as exc: - log.error('fork #2 failed: %s (%s)', exc.errno, exc) + log.error("fork #2 failed: %s (%s)", exc.errno, exc) sys.exit(salt.defaults.exitcodes.EX_GENERIC) salt.utils.crypt.reinit_crypto() @@ -101,7 +104,7 @@ def daemonize(redirect_out=True): # not cleanly redirected and the parent process dies when the # multiprocessing process attempts to access stdout or err. if redirect_out: - with salt.utils.files.fopen('/dev/null', 'r+') as dev_null: + with salt.utils.files.fopen("/dev/null", "r+") as dev_null: # Redirect python stdin/out/err # and the os stdin/out/err which can be different dup2(dev_null, sys.stdin) @@ -113,18 +116,18 @@ def daemonize(redirect_out=True): def dup2(file1, file2): - ''' + """ Duplicate file descriptor fd to fd2, closing the latter first if necessary. This method is similar to os.dup2 but ignores streams that do not have a supported fileno method. - ''' + """ if isinstance(file1, int): fno1 = file1 else: try: fno1 = file1.fileno() except io.UnsupportedOperation: - log.warn('Unsupported operation on file: %r', file1) + log.warn("Unsupported operation on file: %r", file1) return if isinstance(file2, int): fno2 = file2 @@ -132,95 +135,95 @@ def dup2(file1, file2): try: fno2 = file2.fileno() except io.UnsupportedOperation: - log.warn('Unsupported operation on file: %r', file2) + log.warn("Unsupported operation on file: %r", file2) return os.dup2(fno1, fno2) def daemonize_if(opts): - ''' + """ Daemonize a module function process if multiprocessing is True and the process is not being called by salt-call - ''' - if 'salt-call' in sys.argv[0]: + """ + if "salt-call" in sys.argv[0]: return - if not opts.get('multiprocessing', True): + if not opts.get("multiprocessing", True): return - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): return daemonize(False) def systemd_notify_call(action): - process = subprocess.Popen(['systemd-notify', action], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process = subprocess.Popen( + ["systemd-notify", action], stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) process.communicate() status = process.poll() return status == 0 def notify_systemd(): - ''' + """ Notify systemd that this process has started - ''' + """ try: import systemd.daemon # pylint: disable=no-name-in-module except ImportError: - if salt.utils.path.which('systemd-notify') \ - and systemd_notify_call('--booted'): + if salt.utils.path.which("systemd-notify") and systemd_notify_call("--booted"): # Notify systemd synchronously - notify_socket = os.getenv('NOTIFY_SOCKET') + notify_socket = os.getenv("NOTIFY_SOCKET") if notify_socket: # Handle abstract namespace socket - if notify_socket.startswith('@'): - notify_socket = '\0{0}'.format(notify_socket[1:]) + if notify_socket.startswith("@"): + notify_socket = "\0{0}".format(notify_socket[1:]) try: sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) sock.connect(notify_socket) - sock.sendall('READY=1'.encode()) + sock.sendall("READY=1".encode()) sock.close() except socket.error: - return systemd_notify_call('--ready') + return systemd_notify_call("--ready") return True return False if systemd.daemon.booted(): try: - return systemd.daemon.notify('READY=1') + return systemd.daemon.notify("READY=1") except SystemError: # Daemon was not started by systemd pass def set_pidfile(pidfile, user): - ''' + """ Save the pidfile - ''' + """ pdir = os.path.dirname(pidfile) if not os.path.isdir(pdir) and pdir: os.makedirs(pdir) try: - with salt.utils.files.fopen(pidfile, 'w+') as ofile: + with salt.utils.files.fopen(pidfile, "w+") as ofile: ofile.write(str(os.getpid())) # future lint: disable=blacklisted-function except IOError: pass - log.debug('Created pidfile: %s', pidfile) + log.debug("Created pidfile: %s", pidfile) if salt.utils.platform.is_windows(): return True import pwd # after confirming not running Windows - #import grp + + # import grp try: pwnam = pwd.getpwnam(user) uid = pwnam[2] gid = pwnam[3] - #groups = [g.gr_gid for g in grp.getgrall() if user in g.gr_mem] + # groups = [g.gr_gid for g in grp.getgrall() if user in g.gr_mem] except (KeyError, IndexError): sys.stderr.write( - 'Failed to set the pid to user: {0}. The user is not ' - 'available.\n'.format( - user - ) + "Failed to set the pid to user: {0}. The user is not " + "available.\n".format(user) ) sys.exit(salt.defaults.exitcodes.EX_NOUSER) @@ -231,28 +234,26 @@ def set_pidfile(pidfile, user): try: os.chown(pidfile, uid, gid) except OSError as err: - msg = ( - 'Failed to set the ownership of PID file {0} to user {1}.'.format( - pidfile, user - ) + msg = "Failed to set the ownership of PID file {0} to user {1}.".format( + pidfile, user ) - log.debug('%s Traceback follows:', msg, exc_info=True) - sys.stderr.write('{0}\n'.format(msg)) + log.debug("%s Traceback follows:", msg, exc_info=True) + sys.stderr.write("{0}\n".format(msg)) sys.exit(err.errno) - log.debug('Chowned pidfile: %s to user: %s', pidfile, user) + log.debug("Chowned pidfile: %s to user: %s", pidfile, user) def check_pidfile(pidfile): - ''' + """ Determine if a pidfile has been written out - ''' + """ return os.path.isfile(pidfile) def get_pidfile(pidfile): - ''' + """ Return the pid from a pidfile as an integer - ''' + """ try: with salt.utils.files.fopen(pidfile) as pdf: pid = pdf.read().strip() @@ -262,9 +263,9 @@ def get_pidfile(pidfile): def clean_proc(proc, wait_for_kill=10): - ''' + """ Generic method for cleaning up multiprocessing procs - ''' + """ # NoneType and other fun stuff need not apply if not proc: return @@ -275,7 +276,7 @@ def clean_proc(proc, wait_for_kill=10): waited += 1 time.sleep(0.1) if proc.is_alive() and (waited >= wait_for_kill): - log.error('Process did not die with terminate(): %s', proc.pid) + log.error("Process did not die with terminate(): %s", proc.pid) os.kill(proc.pid, signal.SIGKILL) except (AssertionError, AttributeError): # Catch AssertionError when the proc is evaluated inside the child @@ -285,9 +286,9 @@ def clean_proc(proc, wait_for_kill=10): def os_is_running(pid): - ''' + """ Use OS facilities to determine if a process is running - ''' + """ if isinstance(pid, six.string_types): pid = int(pid) if HAS_PSUTIL: @@ -301,7 +302,7 @@ def os_is_running(pid): class ThreadPool(object): - ''' + """ This is a very VERY basic threadpool implementation This was made instead of using multiprocessing ThreadPool because we want to set max queue size and we want to daemonize threads (neither @@ -312,10 +313,9 @@ class ThreadPool(object): TODO: if this is found to be more generally useful it would be nice to pull in the majority of code from upstream or from http://bit.ly/1wTeJtM - ''' - def __init__(self, - num_threads=None, - queue_size=0): + """ + + def __init__(self, num_threads=None, queue_size=0): # if no count passed, default to number of CPUs if num_threads is None: num_threads = multiprocessing.cpu_count() @@ -363,8 +363,10 @@ class ThreadPool(object): continue try: log.debug( - 'ThreadPool executing func: %s with args=%s kwargs=%s', - func, args, kwargs + "ThreadPool executing func: %s with args=%s kwargs=%s", + func, + args, + kwargs, ) func(*args, **kwargs) except Exception as err: # pylint: disable=broad-except @@ -372,9 +374,10 @@ class ThreadPool(object): class ProcessManager(object): - ''' + """ A class which will manage processes that should be running - ''' + """ + def __init__(self, name=None, wait_for_kill=1): # pid -> {tgt: foo, Process: object, args: args, kwargs: kwargs} self._process_map = {} @@ -391,11 +394,11 @@ class ProcessManager(object): self._restart_processes = True def add_process(self, tgt, args=None, kwargs=None, name=None): - ''' + """ Create a processes and args + kwargs This will deterimine if it is a Process class, otherwise it assumes it is a function - ''' + """ if args is None: args = [] @@ -406,46 +409,48 @@ class ProcessManager(object): # Need to ensure that 'log_queue' and 'log_queue_level' is # correctly transferred to processes that inherit from # 'Process'. - if type(Process) is type(tgt) and ( - issubclass(tgt, Process)): + if type(Process) is type(tgt) and (issubclass(tgt, Process)): need_log_queue = True else: need_log_queue = False if need_log_queue: - if 'log_queue' not in kwargs: - if hasattr(self, 'log_queue'): - kwargs['log_queue'] = self.log_queue + if "log_queue" not in kwargs: + if hasattr(self, "log_queue"): + kwargs["log_queue"] = self.log_queue else: - kwargs['log_queue'] = ( - salt.log.setup.get_multiprocessing_logging_queue() - ) - if 'log_queue_level' not in kwargs: - if hasattr(self, 'log_queue_level'): - kwargs['log_queue_level'] = self.log_queue_level + kwargs[ + "log_queue" + ] = salt.log.setup.get_multiprocessing_logging_queue() + if "log_queue_level" not in kwargs: + if hasattr(self, "log_queue_level"): + kwargs["log_queue_level"] = self.log_queue_level else: - kwargs['log_queue_level'] = ( - salt.log.setup.get_multiprocessing_logging_level() - ) + kwargs[ + "log_queue_level" + ] = salt.log.setup.get_multiprocessing_logging_level() # create a nicer name for the debug log if name is None: if isinstance(tgt, types.FunctionType): - name = '{0}.{1}'.format( - tgt.__module__, - tgt.__name__, - ) + name = "{0}.{1}".format(tgt.__module__, tgt.__name__,) else: - name = '{0}{1}.{2}'.format( + name = "{0}{1}.{2}".format( tgt.__module__, - '.{0}'.format(tgt.__class__) if six.text_type(tgt.__class__) != "<type 'type'>" else '', + ".{0}".format(tgt.__class__) + if six.text_type(tgt.__class__) != "<type 'type'>" + else "", tgt.__name__, ) - if type(multiprocessing.Process) is type(tgt) and issubclass(tgt, multiprocessing.Process): + if type(multiprocessing.Process) is type(tgt) and issubclass( + tgt, multiprocessing.Process + ): process = tgt(*args, **kwargs) else: - process = multiprocessing.Process(target=tgt, args=args, kwargs=kwargs, name=name) + process = multiprocessing.Process( + target=tgt, args=args, kwargs=kwargs, name=name + ) if isinstance(process, SignalHandlingProcess): with default_signals(signal.SIGINT, signal.SIGTERM): @@ -453,30 +458,34 @@ class ProcessManager(object): else: process.start() log.debug("Started '%s' with pid %s", name, process.pid) - self._process_map[process.pid] = {'tgt': tgt, - 'args': args, - 'kwargs': kwargs, - 'Process': process} + self._process_map[process.pid] = { + "tgt": tgt, + "args": args, + "kwargs": kwargs, + "Process": process, + } return process def restart_process(self, pid): - ''' + """ Create new process (assuming this one is dead), then remove the old one - ''' + """ if self._restart_processes is False: return log.info( - 'Process %s (%s) died with exit status %s, restarting...', - self._process_map[pid]['tgt'], + "Process %s (%s) died with exit status %s, restarting...", + self._process_map[pid]["tgt"], pid, - self._process_map[pid]['Process'].exitcode + self._process_map[pid]["Process"].exitcode, ) # don't block, the process is already dead - self._process_map[pid]['Process'].join(1) + self._process_map[pid]["Process"].join(1) - self.add_process(self._process_map[pid]['tgt'], - self._process_map[pid]['args'], - self._process_map[pid]['kwargs']) + self.add_process( + self._process_map[pid]["tgt"], + self._process_map[pid]["args"], + self._process_map[pid]["kwargs"], + ) del self._process_map[pid] @@ -484,8 +493,10 @@ class ProcessManager(object): self._restart_processes = False def send_signal_to_processes(self, signal_): - if (salt.utils.platform.is_windows() and - signal_ in (signal.SIGTERM, signal.SIGINT)): + if salt.utils.platform.is_windows() and signal_ in ( + signal.SIGTERM, + signal.SIGINT, + ): # On Windows, the subprocesses automatically have their signal # handlers invoked. If you send one of these signals while the # signal handler is running, it will kill the process where it @@ -510,10 +521,10 @@ class ProcessManager(object): @gen.coroutine def run(self, asynchronous=False): - ''' + """ Load and start all available api modules - ''' - log.debug('Process Manager starting!') + """ + log.debug("Process Manager starting!") appendproctitle(self.name) # make sure to kill the subprocesses if the parent is killed @@ -525,7 +536,7 @@ class ProcessManager(object): signal.signal(signal.SIGINT, self.kill_children) while True: - log.trace('Process manager iteration') + log.trace("Process manager iteration") try: # in case someone died while we were waiting... self.check_children() @@ -549,19 +560,19 @@ class ProcessManager(object): break def check_children(self): - ''' + """ Check the children once - ''' + """ if self._restart_processes is True: for pid, mapping in six.iteritems(self._process_map): - if not mapping['Process'].is_alive(): - log.trace('Process restart of %s', pid) + if not mapping["Process"].is_alive(): + log.trace("Process restart of %s", pid) self.restart_process(pid) def kill_children(self, *args, **kwargs): - ''' + """ Kill all of the children - ''' + """ # first lets reset signal handlers to default one to prevent running this twice signal.signal(signal.SIGTERM, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN) @@ -576,25 +587,26 @@ class ProcessManager(object): else: return if salt.utils.platform.is_windows(): - if multiprocessing.current_process().name != 'MainProcess': + if multiprocessing.current_process().name != "MainProcess": # Since the main process will kill subprocesses by tree, # no need to do anything in the subprocesses. # Sometimes, when both a subprocess and the main process # call 'taskkill', it will leave a 'taskkill' zombie process. # We want to avoid this. return - with salt.utils.files.fopen(os.devnull, 'wb') as devnull: + with salt.utils.files.fopen(os.devnull, "wb") as devnull: for pid, p_map in six.iteritems(self._process_map): # On Windows, we need to explicitly terminate sub-processes # because the processes don't have a sigterm handler. subprocess.call( - ['taskkill', '/F', '/T', '/PID', six.text_type(pid)], - stdout=devnull, stderr=devnull - ) - p_map['Process'].terminate() + ["taskkill", "/F", "/T", "/PID", six.text_type(pid)], + stdout=devnull, + stderr=devnull, + ) + p_map["Process"].terminate() else: for pid, p_map in six.iteritems(self._process_map.copy()): - log.trace('Terminating pid %s: %s', pid, p_map['Process']) + log.trace("Terminating pid %s: %s", pid, p_map["Process"]) if args: # escalate the signal to the process try: @@ -602,11 +614,11 @@ class ProcessManager(object): except OSError: pass try: - p_map['Process'].terminate() + p_map["Process"].terminate() except OSError as exc: if exc.errno not in (errno.ESRCH, errno.EACCES): raise - if not p_map['Process'].is_alive(): + if not p_map["Process"].is_alive(): try: del self._process_map[pid] except KeyError: @@ -615,13 +627,13 @@ class ProcessManager(object): end_time = time.time() + self.wait_for_kill # when to die - log.trace('Waiting to kill process manager children') + log.trace("Waiting to kill process manager children") while self._process_map and time.time() < end_time: for pid, p_map in six.iteritems(self._process_map.copy()): - log.trace('Joining pid %s: %s', pid, p_map['Process']) - p_map['Process'].join(0) + log.trace("Joining pid %s: %s", pid, p_map["Process"]) + p_map["Process"].join(0) - if not p_map['Process'].is_alive(): + if not p_map["Process"].is_alive(): # The process is no longer alive, remove it from the process map dictionary try: del self._process_map[pid] @@ -634,7 +646,7 @@ class ProcessManager(object): while kill_iterations >= 0: kill_iterations -= 1 for pid, p_map in six.iteritems(self._process_map.copy()): - if not p_map['Process'].is_alive(): + if not p_map["Process"].is_alive(): # The process is no longer alive, remove it from the process map dictionary try: del self._process_map[pid] @@ -642,13 +654,13 @@ class ProcessManager(object): # This is a race condition if a signal was passed to all children pass continue - log.trace('Killing pid %s: %s', pid, p_map['Process']) + log.trace("Killing pid %s: %s", pid, p_map["Process"]) try: os.kill(pid, signal.SIGKILL) except OSError as exc: log.exception(exc) # in case the process has since decided to die, os.kill returns OSError - if not p_map['Process'].is_alive(): + if not p_map["Process"].is_alive(): # The process is no longer alive, remove it from the process map dictionary try: del self._process_map[pid] @@ -658,37 +670,42 @@ class ProcessManager(object): if self._process_map: # Some processes disrespected the KILL signal!!!! - available_retries = kwargs.get('retry', 3) + available_retries = kwargs.get("retry", 3) if available_retries >= 0: log.info( - 'Some processes failed to respect the KILL signal: %s', - '; '.join( - 'Process: {0} (Pid: {1})'.format(v['Process'], k) for # pylint: disable=str-format-in-logging - (k, v) in self._process_map.items() - ) + "Some processes failed to respect the KILL signal: %s", + "; ".join( + "Process: {0} (Pid: {1})".format(v["Process"], k) + for ( # pylint: disable=str-format-in-logging + k, + v, + ) in self._process_map.items() + ), ) - log.info('kill_children retries left: %s', available_retries) - kwargs['retry'] = available_retries - 1 + log.info("kill_children retries left: %s", available_retries) + kwargs["retry"] = available_retries - 1 return self.kill_children(*args, **kwargs) else: log.warning( - 'Failed to kill the following processes: %s', - '; '.join( - 'Process: {0} (Pid: {1})'.format(v['Process'], k) for # pylint: disable=str-format-in-logging - (k, v) in self._process_map.items() - ) + "Failed to kill the following processes: %s", + "; ".join( + "Process: {0} (Pid: {1})".format(v["Process"], k) + for ( # pylint: disable=str-format-in-logging + k, + v, + ) in self._process_map.items() + ), ) log.warning( - 'Salt will either fail to terminate now or leave some ' - 'zombie processes behind' + "Salt will either fail to terminate now or leave some " + "zombie processes behind" ) class Process(multiprocessing.Process, NewStyleClassMixIn): - def __init__(self, *args, **kwargs): - log_queue = kwargs.pop('log_queue', None) - log_queue_level = kwargs.pop('log_queue_level', None) + log_queue = kwargs.pop("log_queue", None) + log_queue_level = kwargs.pop("log_queue_level", None) super(Process, self).__init__(*args, **kwargs) if salt.utils.platform.is_windows(): # On Windows, subclasses should call super if they define @@ -723,12 +740,12 @@ class Process(multiprocessing.Process, NewStyleClassMixIn): # # We use setattr here to fool pylint not to complain that we're # overriding run from the subclass here - setattr(self, 'run', self.__decorate_run(self.run)) + setattr(self, "run", self.__decorate_run(self.run)) # __setstate__ and __getstate__ are only used on Windows. def __setstate__(self, state): - args = state['args'] - kwargs = state['kwargs'] + args = state["args"] + kwargs = state["kwargs"] # This will invoke __init__ of the most derived class. self.__init__(*args, **kwargs) self._after_fork_methods = self._after_fork_methods @@ -737,21 +754,21 @@ class Process(multiprocessing.Process, NewStyleClassMixIn): def __getstate__(self): args = self._args_for_getstate kwargs = self._kwargs_for_getstate - if 'log_queue' not in kwargs: - kwargs['log_queue'] = self.log_queue - if 'log_queue_level' not in kwargs: - kwargs['log_queue_level'] = self.log_queue_level - return {'args': args, - 'kwargs': kwargs, - '_after_fork_methods': self._after_fork_methods, - '_finalize_methods': self._finalize_methods, - } + if "log_queue" not in kwargs: + kwargs["log_queue"] = self.log_queue + if "log_queue_level" not in kwargs: + kwargs["log_queue_level"] = self.log_queue_level + return { + "args": args, + "kwargs": kwargs, + "_after_fork_methods": self._after_fork_methods, + "_finalize_methods": self._finalize_methods, + } def _setup_process_logging(self): salt.log.setup.setup_multiprocessing_logging(self.log_queue) def __decorate_run(self, run_func): - @functools.wraps(run_func) def wrapped_run_func(): for method, args, kwargs in self._after_fork_methods: @@ -763,8 +780,11 @@ class Process(multiprocessing.Process, NewStyleClassMixIn): six.reraise(*sys.exc_info()) except Exception as exc: # pylint: disable=broad-except log.error( - 'An un-handled exception from the multiprocessing process ' - '\'%s\' was caught:\n', self.name, exc_info=True) + "An un-handled exception from the multiprocessing process " + "'%s' was caught:\n", + self.name, + exc_info=True, + ) # Re-raise the exception. multiprocessing.Process will write it to # sys.stderr and set the proper exitcode and we have already logged # it above. @@ -777,19 +797,17 @@ class Process(multiprocessing.Process, NewStyleClassMixIn): class MultiprocessingProcess(Process): - ''' + """ This class exists for backwards compatibility and to properly deprecate it. - ''' + """ def __init__(self, *args, **kwargs): salt.utils.versions.warn_until_date( - '20220101', - 'Please stop using \'{name}.MultiprocessingProcess\' and instead use ' - '\'{name}.Process\'. \'{name}.MultiprocessingProcess\' will go away ' - 'after {{date}}.'.format( - name=__name__ - ), - stacklevel=3 + "20220101", + "Please stop using '{name}.MultiprocessingProcess' and instead use " + "'{name}.Process'. '{name}.MultiprocessingProcess' will go away " + "after {{date}}.".format(name=__name__), + stacklevel=3, ) super(MultiprocessingProcess, self).__init__(*args, **kwargs) @@ -813,32 +831,34 @@ class SignalHandlingProcess(Process): self._signal_handled.set() signal.signal(signal.SIGTERM, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN) - msg = '{0} received a '.format(self.__class__.__name__) + msg = "{0} received a ".format(self.__class__.__name__) if signum == signal.SIGINT: - msg += 'SIGINT' + msg += "SIGINT" elif signum == signal.SIGTERM: - msg += 'SIGTERM' - msg += '. Exiting' + msg += "SIGTERM" + msg += ". Exiting" log.debug(msg) if HAS_PSUTIL: try: process = psutil.Process(os.getpid()) - if hasattr(process, 'children'): + if hasattr(process, "children"): for child in process.children(recursive=True): try: if child.is_running(): child.terminate() except psutil.NoSuchProcess: log.warn( - 'Unable to kill child of process %d, it does ' - 'not exist. My pid is %d', - self.pid, os.getpid() + "Unable to kill child of process %d, it does " + "not exist. My pid is %d", + self.pid, + os.getpid(), ) except psutil.NoSuchProcess: log.warn( - 'Unable to kill children of process %d, it does not exist.' - 'My pid is %d', - self.pid, os.getpid() + "Unable to kill children of process %d, it does not exist." + "My pid is %d", + self.pid, + os.getpid(), ) sys.exit(salt.defaults.exitcodes.EX_OK) @@ -848,19 +868,17 @@ class SignalHandlingProcess(Process): class SignalHandlingMultiprocessingProcess(SignalHandlingProcess): - ''' + """ This class exists for backwards compatibility and to properly deprecate it. - ''' + """ def __init__(self, *args, **kwargs): salt.utils.versions.warn_until_date( - '20220101', - 'Please stop using \'{name}.SignalHandlingMultiprocessingProcess\' and instead use ' - '\'{name}.SignalHandlingProcess\'. \'{name}.SignalHandlingMultiprocessingProcess\' ' - 'will go away after {{date}}.'.format( - name=__name__ - ), - stacklevel=3 + "20220101", + "Please stop using '{name}.SignalHandlingMultiprocessingProcess' and instead use " + "'{name}.SignalHandlingProcess'. '{name}.SignalHandlingMultiprocessingProcess' " + "will go away after {{date}}.".format(name=__name__), + stacklevel=3, ) super(SignalHandlingMultiprocessingProcess, self).__init__(*args, **kwargs) @@ -876,10 +894,7 @@ def default_signals(*signals): # This happens when a netapi module attempts to run a function # using wheel_async, because the process trying to register signals # will not be the main PID. - log.trace( - 'Failed to register signal for signum %d: %s', - signum, exc - ) + log.trace("Failed to register signal for signum %d: %s", signum, exc) else: old_signals[signum] = saved_signal @@ -894,7 +909,6 @@ def default_signals(*signals): class SubprocessList(object): - def __init__(self, processes=None, lock=None): if processes is None: self.processes = [] @@ -908,7 +922,7 @@ class SubprocessList(object): def add(self, proc): with self.lock: self.processes.append(proc) - log.debug('Subprocess %s added', proc.name) + log.debug("Subprocess %s added", proc.name) def cleanup(self): with self.lock: @@ -917,4 +931,4 @@ class SubprocessList(object): continue proc.join() self.processes.remove(proc) - log.debug('Subprocess %s cleaned up', proc.name) + log.debug("Subprocess %s cleaned up", proc.name) diff --git a/salt/utils/profile.py b/salt/utils/profile.py index 2a4e650f2a7..e11fda59c9a 100644 --- a/salt/utils/profile.py +++ b/salt/utils/profile.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Decorator and functions to profile Salt using cProfile -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -22,28 +22,33 @@ log = logging.getLogger(__name__) try: import cProfile + HAS_CPROFILE = True except ImportError: HAS_CPROFILE = False def profile_func(filename=None): - ''' + """ Decorator for adding profiling to a nested function in Salt - ''' + """ + def proffunc(fun): def profiled_func(*args, **kwargs): - logging.info('Profiling function %s', fun.__name__) + logging.info("Profiling function %s", fun.__name__) try: profiler = cProfile.Profile() retval = profiler.runcall(fun, *args, **kwargs) - profiler.dump_stats((filename or '{0}_func.profile' - .format(fun.__name__))) + profiler.dump_stats( + (filename or "{0}_func.profile".format(fun.__name__)) + ) except IOError: - logging.exception('Could not open profile file %s', filename) + logging.exception("Could not open profile file %s", filename) return retval + return profiled_func + return proffunc @@ -54,11 +59,11 @@ def activate_profile(test=True): pr = cProfile.Profile() pr.enable() else: - log.error('cProfile is not available on your platform') + log.error("cProfile is not available on your platform") return pr -def output_profile(pr, stats_path='/tmp/stats', stop=False, id_=None): +def output_profile(pr, stats_path="/tmp/stats", stop=False, id_=None): if pr is not None and HAS_CPROFILE: try: pr.disable() @@ -67,36 +72,38 @@ def output_profile(pr, stats_path='/tmp/stats', stop=False, id_=None): date = datetime.datetime.now().isoformat() if id_ is None: id_ = salt.utils.hashutils.random_hash(size=32) - ficp = os.path.join(stats_path, '{0}.{1}.pstats'.format(id_, date)) - fico = os.path.join(stats_path, '{0}.{1}.dot'.format(id_, date)) - ficn = os.path.join(stats_path, '{0}.{1}.stats'.format(id_, date)) + ficp = os.path.join(stats_path, "{0}.{1}.pstats".format(id_, date)) + fico = os.path.join(stats_path, "{0}.{1}.dot".format(id_, date)) + ficn = os.path.join(stats_path, "{0}.{1}.stats".format(id_, date)) if not os.path.exists(ficp): pr.dump_stats(ficp) - with salt.utils.files.fopen(ficn, 'w') as fic: - pstats.Stats(pr, stream=fic).sort_stats('cumulative') - log.info('PROFILING: %s generated', ficp) - log.info('PROFILING (cumulative): %s generated', ficn) - pyprof = salt.utils.path.which('pyprof2calltree') - cmd = [pyprof, '-i', ficp, '-o', fico] + with salt.utils.files.fopen(ficn, "w") as fic: + pstats.Stats(pr, stream=fic).sort_stats("cumulative") + log.info("PROFILING: %s generated", ficp) + log.info("PROFILING (cumulative): %s generated", ficn) + pyprof = salt.utils.path.which("pyprof2calltree") + cmd = [pyprof, "-i", ficp, "-o", fico] if pyprof: failed = False try: pro = subprocess.Popen( - cmd, shell=False, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) except OSError: failed = True if pro.returncode: failed = True if failed: - log.error('PROFILING (dot problem') + log.error("PROFILING (dot problem") else: - log.info('PROFILING (dot): %s generated', fico) - log.trace('pyprof2calltree output:') - log.trace(salt.utils.stringutils.to_str(pro.stdout.read()).strip() + - salt.utils.stringutils.to_str(pro.stderr.read()).strip()) + log.info("PROFILING (dot): %s generated", fico) + log.trace("pyprof2calltree output:") + log.trace( + salt.utils.stringutils.to_str(pro.stdout.read()).strip() + + salt.utils.stringutils.to_str(pro.stderr.read()).strip() + ) else: - log.info('You can run %s for additional stats.', cmd) + log.info("You can run %s for additional stats.", cmd) finally: if not stop: pr.enable() diff --git a/salt/utils/psutil_compat.py b/salt/utils/psutil_compat.py index 1c3e30d6b87..5e794876377 100644 --- a/salt/utils/psutil_compat.py +++ b/salt/utils/psutil_compat.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Version agnostic psutil hack to fully support both old (<2.0) and new (>=2.0) psutil versions. @@ -8,17 +8,17 @@ The old <1.0 psutil API is dropped in psutil 3.0 Should be removed once support for psutil <2.0 is dropped. (eg RHEL 6) Built off of http://grodola.blogspot.com/2014/01/psutil-20-porting.html -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt libs -from salt.ext import six - # No exception handling, as we want ImportError if psutil doesn't exist import psutil # pylint: disable=3rd-party-module-not-gated +# Import Salt libs +from salt.ext import six + if psutil.version_info >= (2, 0): from psutil import * # pylint: disable=wildcard-import,unused-wildcard-import,3rd-party-module-not-gated else: @@ -26,7 +26,7 @@ else: # Psuedo "from psutil import *" _globals = globals() for attr in psutil.__all__: - _temp = __import__('psutil', globals(), locals(), [attr], -1 if six.PY2 else 0) + _temp = __import__("psutil", globals(), locals(), [attr], -1 if six.PY2 else 0) try: _globals[attr] = getattr(_temp, attr) except AttributeError: @@ -36,6 +36,7 @@ else: # pylint: disable=unused-import,3rd-party-module-not-gated from psutil import disk_partitions from psutil import disk_usage + # pylint: enable=unused-import,3rd-party-module-not-gated # Alias new module functions @@ -50,8 +51,9 @@ else: try: users = psutil.get_users except AttributeError: - users = lambda: (_ for _ in ()).throw(NotImplementedError('Your ' - 'psutil version is too old')) + users = lambda: (_ for _ in ()).throw( + NotImplementedError("Your " "psutil version is too old") + ) # Deprecated in 1.0.1, but not mentioned in blog post if psutil.version_info < (1, 0, 1): @@ -79,9 +81,9 @@ else: return self.get_nice() def rlimit(self, *args, **kwargs): - ''' + """ set_rlimit and get_limit were not introduced until psutil v1.1.0 - ''' + """ if psutil.version_info >= (1, 1, 0): if args or kwargs: return self.set_rlimit(*args, **kwargs) @@ -89,6 +91,7 @@ else: return self.get_rlimit() else: pass + # pylint: enable=arguments-differ # Alias renamed Process functions @@ -108,7 +111,6 @@ else: "open_files": "get_open_files", "threads": "get_threads", "cwd": "getcwd", - } for new, old in six.iteritems(_PROCESS_FUNCTION_MAP): diff --git a/salt/utils/pushover.py b/salt/utils/pushover.py index a3abf4b330d..08274ffd0fb 100644 --- a/salt/utils/pushover.py +++ b/salt/utils/pushover.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Library for interacting with Slack API .. versionadded:: 2016.3.0 @@ -14,30 +14,35 @@ Library for interacting with Slack API slack: api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + +import salt.ext.six.moves.http_client + # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext.six.moves.urllib.parse import urljoin as _urljoin from salt.ext.six.moves.urllib.parse import urlencode as _urlencode -import salt.ext.six.moves.http_client +from salt.ext.six.moves.urllib.parse import urljoin as _urljoin from salt.version import __version__ + # pylint: enable=import-error,no-name-in-module log = logging.getLogger(__name__) -def query(function, - token=None, - api_version='1', - method='POST', - header_dict=None, - data=None, - query_params=None, - opts=None): - ''' +def query( + function, + token=None, + api_version="1", + method="POST", + header_dict=None, + data=None, + query_params=None, + opts=None, +): + """ PushOver object method function to construct and execute on the API URL. :param token: The PushOver api key. @@ -46,36 +51,26 @@ def query(function, :param method: The HTTP method, e.g. GET or POST. :param data: The data to be sent for POST method. :return: The json response from the API call or False. - ''' + """ - ret = {'message': '', - 'res': True} + ret = {"message": "", "res": True} pushover_functions = { - 'message': { - 'request': 'messages.json', - 'response': 'status', - }, - 'validate_user': { - 'request': 'users/validate.json', - 'response': 'status', - }, - 'validate_sound': { - 'request': 'sounds.json', - 'response': 'status', - }, + "message": {"request": "messages.json", "response": "status"}, + "validate_user": {"request": "users/validate.json", "response": "status"}, + "validate_sound": {"request": "sounds.json", "response": "status"}, } - api_url = 'https://api.pushover.net' - base_url = _urljoin(api_url, api_version + '/') - path = pushover_functions.get(function).get('request') + api_url = "https://api.pushover.net" + base_url = _urljoin(api_url, api_version + "/") + path = pushover_functions.get(function).get("request") url = _urljoin(base_url, path, False) if not query_params: query_params = {} decode = True - if method == 'DELETE': + if method == "DELETE": decode = False result = salt.utils.http.query( @@ -85,7 +80,7 @@ def query(function, data=data, header_dict=header_dict, decode=decode, - decode_type='json', + decode_type="json", text=True, status=True, cookies=True, @@ -93,91 +88,82 @@ def query(function, opts=opts, ) - if result.get('status', None) == salt.ext.six.moves.http_client.OK: - response = pushover_functions.get(function).get('response') + if result.get("status", None) == salt.ext.six.moves.http_client.OK: + response = pushover_functions.get(function).get("response") if response in result and result[response] == 0: - ret['res'] = False - ret['message'] = result + ret["res"] = False + ret["message"] = result return ret else: try: - if 'response' in result and result[response] == 0: - ret['res'] = False - ret['message'] = result + if "response" in result and result[response] == 0: + ret["res"] = False + ret["message"] = result except ValueError: - ret['res'] = False - ret['message'] = result + ret["res"] = False + ret["message"] = result return ret -def validate_sound(sound, - token): - ''' +def validate_sound(sound, token): + """ Send a message to a Pushover user or group. :param sound: The sound that we want to verify :param token: The PushOver token. - ''' - ret = { - 'message': 'Sound is invalid', - 'res': False - } + """ + ret = {"message": "Sound is invalid", "res": False} parameters = dict() - parameters['token'] = token + parameters["token"] = token - response = query(function='validate_sound', - method='GET', - query_params=parameters) + response = query(function="validate_sound", method="GET", query_params=parameters) - if response['res']: - if 'message' in response: - _message = response.get('message', '') - if 'status' in _message: - if _message.get('dict', {}).get('status', '') == 1: - sounds = _message.get('dict', {}).get('sounds', '') + if response["res"]: + if "message" in response: + _message = response.get("message", "") + if "status" in _message: + if _message.get("dict", {}).get("status", "") == 1: + sounds = _message.get("dict", {}).get("sounds", "") if sound in sounds: - ret['message'] = 'Valid sound {0}.'.format(sound) - ret['res'] = True + ret["message"] = "Valid sound {0}.".format(sound) + ret["res"] = True else: - ret['message'] = 'Warning: {0} not a valid sound.'.format(sound) - ret['res'] = False + ret["message"] = "Warning: {0} not a valid sound.".format(sound) + ret["res"] = False else: - ret['message'] = ''.join(_message.get('dict', {}).get('errors')) + ret["message"] = "".join(_message.get("dict", {}).get("errors")) return ret -def validate_user(user, - device, - token): - ''' +def validate_user(user, device, token): + """ Send a message to a Pushover user or group. :param user: The user or group name, either will work. :param device: The device for the user. :param token: The PushOver token. - ''' - res = { - 'message': 'User key is invalid', - 'result': False - } + """ + res = {"message": "User key is invalid", "result": False} parameters = dict() - parameters['user'] = user - parameters['token'] = token + parameters["user"] = user + parameters["token"] = token if device: - parameters['device'] = device + parameters["device"] = device - response = query(function='validate_user', - method='POST', - header_dict={'Content-Type': 'application/x-www-form-urlencoded'}, - data=_urlencode(parameters)) + response = query( + function="validate_user", + method="POST", + header_dict={"Content-Type": "application/x-www-form-urlencoded"}, + data=_urlencode(parameters), + ) - if response['res']: - if 'message' in response: - _message = response.get('message', '') - if 'status' in _message: - if _message.get('dict', {}).get('status', None) == 1: - res['result'] = True - res['message'] = 'User key is valid.' + if response["res"]: + if "message" in response: + _message = response.get("message", "") + if "status" in _message: + if _message.get("dict", {}).get("status", None) == 1: + res["result"] = True + res["message"] = "User key is valid." else: - res['result'] = False - res['message'] = ''.join(_message.get('dict', {}).get('errors')) + res["result"] = False + res["message"] = "".join(_message.get("dict", {}).get("errors")) return res diff --git a/salt/utils/pycrypto.py b/salt/utils/pycrypto.py index 9c97909bbef..d5de5574330 100644 --- a/salt/utils/pycrypto.py +++ b/salt/utils/pycrypto.py @@ -1,14 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" Use pycrypto to generate random passwords on the fly. -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals + import logging +import random import re import string -import random + +# Import salt libs +import salt.utils.stringutils +from salt.exceptions import CommandExecutionError, SaltInvocationError +from salt.ext import six # Import 3rd-party libs try: @@ -27,25 +33,22 @@ try: # Windows does not have the crypt module # consider using passlib.hash instead import crypt + HAS_CRYPT = True except ImportError: HAS_CRYPT = False -# Import salt libs -import salt.utils.stringutils -from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.ext import six log = logging.getLogger(__name__) def secure_password(length=20, use_random=True): - ''' + """ Generate a secure password. - ''' + """ try: length = int(length) - pw = '' + pw = "" while len(pw) < length: if HAS_RANDOM and use_random: while True: @@ -55,32 +58,28 @@ def secure_password(length=20, use_random=True): except UnicodeDecodeError: continue pw += re.sub( - salt.utils.stringutils.to_str(r'[\W_]'), + salt.utils.stringutils.to_str(r"[\W_]"), str(), # future lint: disable=blacklisted-function - char + char, ) else: pw += random.SystemRandom().choice(string.ascii_letters + string.digits) return pw except Exception as exc: # pylint: disable=broad-except - log.exception('Failed to generate secure passsword') + log.exception("Failed to generate secure passsword") raise CommandExecutionError(six.text_type(exc)) -def gen_hash(crypt_salt=None, password=None, algorithm='sha512'): - ''' +def gen_hash(crypt_salt=None, password=None, algorithm="sha512"): + """ Generate /etc/shadow hash - ''' + """ if not HAS_CRYPT: - raise SaltInvocationError('No crypt module for windows') + raise SaltInvocationError("No crypt module for windows") - hash_algorithms = dict( - md5='$1$', blowfish='$2a$', sha256='$5$', sha512='$6$' - ) + hash_algorithms = dict(md5="$1$", blowfish="$2a$", sha256="$5$", sha512="$6$") if algorithm not in hash_algorithms: - raise SaltInvocationError( - 'Algorithm \'{0}\' is not supported'.format(algorithm) - ) + raise SaltInvocationError("Algorithm '{0}' is not supported".format(algorithm)) if password is None: password = secure_password() diff --git a/salt/utils/pydsl.py b/salt/utils/pydsl.py index e7417c83641..8e37a18ead5 100644 --- a/salt/utils/pydsl.py +++ b/salt/utils/pydsl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :maintainer: Jack Kuan <kjkuan@gmail.com> :maturity: new :platform: all @@ -58,7 +58,7 @@ Example of a ``cmd`` state calling a python function:: state('hello').cmd.call(hello, 'pydsl!') -''' +""" # Implementation note: # - There's a bit of terminology mix-up here: @@ -85,17 +85,19 @@ Example of a ``cmd`` state calling a python function:: # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from uuid import uuid4 as _uuid -# Import salt libs -from salt.utils.odict import OrderedDict -from salt.state import HighState +from uuid import uuid4 as _uuid # Import 3rd-party libs from salt.ext import six +from salt.state import HighState +# Import salt libs +from salt.utils.odict import OrderedDict -REQUISITES = set('listen require watch prereq use listen_in require_in watch_in prereq_in use_in onchanges onfail'.split()) +REQUISITES = set( + "listen require watch prereq use listen_in require_in watch_in prereq_in use_in onchanges onfail".split() +) class PyDslError(Exception): @@ -111,7 +113,6 @@ SLS_MATCHES = None class Sls(object): - def __init__(self, sls, saltenv, rendered_sls): self.name = sls self.saltenv = saltenv @@ -124,7 +125,7 @@ class Sls(object): self.rendered_sls = rendered_sls # a set of names of rendered sls modules if not HighState.get_active(): - raise PyDslError('PyDSL only works with a running high state!') + raise PyDslError("PyDSL only works with a running high state!") @classmethod def get_all_decls(cls): @@ -138,13 +139,13 @@ class Sls(object): self.options.update(options) def include(self, *sls_names, **kws): - if 'env' in kws: + if "env" in kws: # "env" is not supported; Use "saltenv". - kws.pop('env') + kws.pop("env") - saltenv = kws.get('saltenv', self.saltenv) + saltenv = kws.get("saltenv", self.saltenv) - if kws.get('delayed', False): + if kws.get("delayed", False): for incl in sls_names: self.includes.append((saltenv, incl)) return @@ -158,24 +159,26 @@ class Sls(object): highstate = self.included_highstate slsmods = [] # a list of pydsl sls modules rendered. for sls in sls_names: - r_env = '{0}:{1}'.format(saltenv, sls) + r_env = "{0}:{1}".format(saltenv, sls) if r_env not in self.rendered_sls: - self.rendered_sls.add(sls) # needed in case the starting sls uses the pydsl renderer. + self.rendered_sls.add( + sls + ) # needed in case the starting sls uses the pydsl renderer. histates, errors = HIGHSTATE.render_state( sls, saltenv, self.rendered_sls, SLS_MATCHES ) HIGHSTATE.merge_included_states(highstate, histates, errors) if errors: - raise PyDslError('\n'.join(errors)) + raise PyDslError("\n".join(errors)) HIGHSTATE.clean_duplicate_extends(highstate) - state_id = '_slsmod_{0}'.format(sls) + state_id = "_slsmod_{0}".format(sls) if state_id not in highstate: slsmods.append(None) else: - for arg in highstate[state_id]['stateconf']: - if isinstance(arg, dict) and next(iter(arg)) == 'slsmod': - slsmods.append(arg['slsmod']) + for arg in highstate[state_id]["stateconf"]: + if isinstance(arg, dict) and next(iter(arg)) == "slsmod": + slsmods.append(arg["slsmod"]) break if not slsmods: @@ -184,9 +187,7 @@ class Sls(object): def extend(self, *state_funcs): if self.options.ordered or self.last_func(): - raise PyDslError( - 'Cannot extend() after the ordered option was turned on!' - ) + raise PyDslError("Cannot extend() after the ordered option was turned on!") for f in state_funcs: state_id = f.mod._state_id self.extends.append(self.get_all_decls().pop(state_id)) @@ -199,7 +200,7 @@ class Sls(object): def state(self, id=None): if not id: - id = '.{0}'.format(_uuid()) + id = ".{0}".format(_uuid()) # adds a leading dot to make use of stateconf's namespace feature. try: return self.get_all_decls()[id] @@ -220,17 +221,17 @@ class Sls(object): # containing the DSL statements. This is to prevent the module # from being GC'ed, so that objects defined in it will be # available while salt is executing the states. - slsmod_id = '_slsmod_' + self.name + slsmod_id = "_slsmod_" + self.name self.state(slsmod_id).stateconf.set(slsmod=slsmod) del self.get_all_decls()[slsmod_id] highstate = OrderedDict() if self.includes: - highstate['include'] = [{t[0]: t[1]} for t in self.includes] + highstate["include"] = [{t[0]: t[1]} for t in self.includes] if self.extends: - highstate['extend'] = extend = OrderedDict() + highstate["extend"] = extend = OrderedDict() for ext in self.extends: - extend[ext._id] = ext._repr(context='extend') + extend[ext._id] = ext._repr(context="extend") for decl in self.decls: highstate[decl._id] = decl._repr() @@ -240,19 +241,19 @@ class Sls(object): highstate, self.included_highstate, errors ) if errors: - raise PyDslError('\n'.join(errors)) + raise PyDslError("\n".join(errors)) return highstate def load_highstate(self, highstate): for sid, decl in six.iteritems(highstate): s = self.state(sid) for modname, args in six.iteritems(decl): - if '.' in modname: - modname, funcname = modname.rsplit('.', 1) + if "." in modname: + modname, funcname = modname.rsplit(".", 1) else: - funcname = next(( - x for x in args if isinstance(x, six.string_types) - )) + funcname = next( + (x for x in args if isinstance(x, six.string_types)) + ) args.remove(funcname) mod = getattr(s, modname) named_args = {} @@ -264,7 +265,6 @@ class Sls(object): class StateDeclaration(object): - def __init__(self, id): self._id = id self._mods = [] @@ -294,10 +294,12 @@ class StateDeclaration(object): last_func = sls.last_func() if last_func and self._mods[-1]._func is not last_func: raise PyDslError( - 'Cannot run state({0}: {1}) that is required by a runtime ' - 'state({2}: {3}), at compile time.'.format( - self._mods[-1]._name, self._id, - last_func.mod, last_func.mod._state_id + "Cannot run state({0}: {1}) that is required by a runtime " + "state({2}: {3}), at compile time.".format( + self._mods[-1]._name, + self._id, + last_func.mod, + last_func.mod._state_id, ) ) sls.get_all_decls().pop(self._id) @@ -309,32 +311,33 @@ class StateDeclaration(object): except ValueError: pass - result = HighState.get_active().state.functions['state.high']( + result = HighState.get_active().state.functions["state.high"]( {self._id: self._repr()} ) if not isinstance(result, dict): # A list is an error raise PyDslError( - 'An error occurred while running highstate: {0}'.format( - '; '.join(result) + "An error occurred while running highstate: {0}".format( + "; ".join(result) ) ) - result = sorted(six.iteritems(result), key=lambda t: t[1]['__run_num__']) + result = sorted(six.iteritems(result), key=lambda t: t[1]["__run_num__"]) if check: for k, v in result: - if not v['result']: + if not v["result"]: import pprint + raise PyDslError( - 'Failed executing low state at compile time:\n{0}' - .format(pprint.pformat({k: v})) + "Failed executing low state at compile time:\n{0}".format( + pprint.pformat({k: v}) + ) ) return result class StateModule(object): - def __init__(self, name, parent_decl): self._state_id = parent_decl self._name = name @@ -348,8 +351,11 @@ class StateModule(object): if name not in REQUISITES: if self._func.name: raise PyDslError( - ('Multiple state functions({0}) not allowed in a ' - 'state module({1})!').format(name, self._name)) + ( + "Multiple state functions({0}) not allowed in a " + "state module({1})!" + ).format(name, self._name) + ) self._func.name = name return self._func return getattr(self._func, name) @@ -378,11 +384,11 @@ def _generate_requsite_method(t): for mod_ref in six.iteritems(kws): self.reference(t, *mod_ref) return self + return req class StateFunction(object): - def __init__(self, name, parent_mod): self.mod = parent_mod self.name = name @@ -410,10 +416,11 @@ class StateFunction(object): return self def _repr(self, context=None): - if not self.name and context != 'extend': - raise PyDslError('No state function specified for module: ' - '{0}'.format(self.mod._name)) - if not self.name and context == 'extend': + if not self.name and context != "extend": + raise PyDslError( + "No state function specified for module: " "{0}".format(self.mod._name) + ) + if not self.name and context == "extend": return self.args return [self.name] + self.args @@ -421,8 +428,11 @@ class StateFunction(object): args = list(args) if args: first = args[0] - if self.mod._name == 'cmd' and \ - self.name in ('call', 'wait_call') and callable(first): + if ( + self.mod._name == "cmd" + and self.name in ("call", "wait_call") + and callable(first) + ): args[0] = first.__name__ kws = dict(func=first, args=args[1:], kws=kws) @@ -441,9 +451,7 @@ class StateFunction(object): ref = mod._state_id elif not (mod and ref): raise PyDslError( - 'Invalid a requisite reference declaration! {0}: {1}'.format( - mod, ref - ) + "Invalid a requisite reference declaration! {0}: {1}".format(mod, ref) ) self.args.append({req_type: [{six.text_type(mod): six.text_type(ref)}]}) diff --git a/salt/utils/pyobjects.py b/salt/utils/pyobjects.py index 9db9b8342a1..2b717af394e 100644 --- a/salt/utils/pyobjects.py +++ b/salt/utils/pyobjects.py @@ -1,19 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" :maintainer: Evan Borgstrom <evan@borgstrom.ca> Pythonic object interface to creating state data, see the pyobjects renderer for more documentation. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import inspect import logging +from salt.ext import six from salt.utils.odict import OrderedDict from salt.utils.schema import Prepareable -from salt.ext import six -REQUISITES = ('listen', 'onchanges', 'onfail', 'require', 'watch', 'use', 'listen_in', 'onchanges_in', 'onfail_in', 'require_in', 'watch_in', 'use_in') +REQUISITES = ( + "listen", + "onchanges", + "onfail", + "require", + "watch", + "use", + "listen_in", + "onchanges_in", + "onfail_in", + "require_in", + "watch_in", + "use_in", +) log = logging.getLogger(__name__) @@ -31,9 +45,10 @@ class InvalidFunction(StateException): class Registry(object): - ''' + """ The StateRegistry holds all of the states that have been created. - ''' + """ + states = OrderedDict() requisites = [] includes = [] @@ -56,19 +71,17 @@ class Registry(object): @classmethod def salt_data(cls): - states = OrderedDict([ - (id_, states_) - for id_, states_ in six.iteritems(cls.states) - ]) + states = OrderedDict( + [(id_, states_) for id_, states_ in six.iteritems(cls.states)] + ) if cls.includes: - states['include'] = cls.includes + states["include"] = cls.includes if cls.extends: - states['extend'] = OrderedDict([ - (id_, states_) - for id_, states_ in six.iteritems(cls.extends) - ]) + states["extend"] = OrderedDict( + [(id_, states_) for id_, states_ in six.iteritems(cls.extends)] + ) cls.empty() @@ -87,9 +100,8 @@ class Registry(object): if id_ in attr: if state.full_func in attr[id_]: raise DuplicateState( - "A state with id '\'{0}\'', type '\'{1}\'' exists".format( - id_, - state.full_func + "A state with id ''{0}'', type ''{1}'' exists".format( + id_, state.full_func ) ) else: @@ -149,7 +161,7 @@ class StateRequisite(object): class StateFactory(object): - ''' + """ The StateFactory is used to generate new States through a natural syntax It is used by initializing it with the name of the salt module:: @@ -162,7 +174,8 @@ class StateFactory(object): File.managed('/path/', owner='root', group='root') The kwargs are passed through to the State object - ''' + """ + def __init__(self, module, valid_funcs=None): self.module = module if valid_funcs is None: @@ -171,37 +184,32 @@ class StateFactory(object): def __getattr__(self, func): if len(self.valid_funcs) > 0 and func not in self.valid_funcs: - raise InvalidFunction('The function \'{0}\' does not exist in the ' - 'StateFactory for \'{1}\''.format( - func, - self.module - )) + raise InvalidFunction( + "The function '{0}' does not exist in the " + "StateFactory for '{1}'".format(func, self.module) + ) def make_state(id_, **kwargs): - return State( - id_, - self.module, - func, - **kwargs - ) + return State(id_, self.module, func, **kwargs) + return make_state - def __call__(self, id_, requisite='require'): - ''' + def __call__(self, id_, requisite="require"): + """ When an object is called it is being used as a requisite - ''' + """ # return the correct data structure for the requisite return StateRequisite(requisite, self.module, id_) class State(object): - ''' + """ This represents a single item in the state tree The id_ is the id of the state, the func is the full name of the salt state (i.e. file.managed). All the keyword args you pass in become the properties of your state. - ''' + """ def __init__(self, id_, module, func, **kwargs): self.id_ = id_ @@ -225,7 +233,7 @@ class State(object): else: Registry.add(self.id_, self) - self.requisite = StateRequisite('require', self.module, self.id_) + self.requisite = StateRequisite("require", self.module, self.id_) @property def attrs(self): @@ -243,10 +251,7 @@ class State(object): # build our attrs from kwargs. we sort the kwargs by key so that we # have consistent ordering for tests - return [ - {k: kwargs[k]} - for k in sorted(six.iterkeys(kwargs)) - ] + return [{k: kwargs[k]} for k in sorted(six.iterkeys(kwargs))] @property def full_func(self): @@ -256,9 +261,7 @@ class State(object): return "{0!s} = {1!s}:{2!s}".format(self.id_, self.full_func, self.attrs) def __call__(self): - return { - self.full_func: self.attrs - } + return {self.full_func: self.attrs} def __enter__(self): Registry.push_requisite(self.requisite) @@ -268,14 +271,15 @@ class State(object): class SaltObject(object): - ''' + """ Object based interface to the functions in __salt__ .. code-block:: python :linenos: Salt = SaltObject(__salt__) Salt.cmd.run(bar) - ''' + """ + def __init__(self, salt): self._salt = salt @@ -283,17 +287,19 @@ class SaltObject(object): class __wrapper__(object): def __getattr__(wself, func): # pylint: disable=E0213 try: - return self._salt['{0}.{1}'.format(mod, func)] + return self._salt["{0}.{1}".format(mod, func)] except KeyError: raise AttributeError + return __wrapper__() class MapMeta(six.with_metaclass(Prepareable, type)): - ''' + """ This is the metaclass for our Map class, used for building data maps based off of grain data. - ''' + """ + @classmethod def __prepare__(metacls, name, bases): return OrderedDict() @@ -313,7 +319,7 @@ class MapMeta(six.with_metaclass(Prepareable, type)): # find all of our filters for item in cls.__ordered_attrs__: - if item[0] == '_': + if item[0] == "_": continue filt = cls.__dict__[item] @@ -323,26 +329,26 @@ class MapMeta(six.with_metaclass(Prepareable, type)): continue # which grain are we filtering on - grain = getattr(filt, '__grain__', 'os_family') + grain = getattr(filt, "__grain__", "os_family") grain_targets.add(grain) # does the object pointed to have a __match__ attribute? # if so use it, otherwise use the name of the object # this is so that you can match complex values, which the python # class name syntax does not allow - match = getattr(filt, '__match__', item) + match = getattr(filt, "__match__", item) match_attrs = {} for name in filt.__dict__: - if name[0] != '_': + if name[0] != "_": match_attrs[name] = filt.__dict__[name] match_info.append((grain, match, match_attrs)) # Reorder based on priority try: - if not hasattr(cls.priority, '__iter__'): - log.error('pyobjects: priority must be an iterable') + if not hasattr(cls.priority, "__iter__"): + log.error("pyobjects: priority must be an iterable") else: new_match_info = [] for grain in cls.priority: @@ -368,15 +374,15 @@ class MapMeta(six.with_metaclass(Prepareable, type)): # Check for matches and update the attrs dict accordingly attrs = {} if match_info: - grain_vals = Map.__salt__['grains.item'](*grain_targets) + grain_vals = Map.__salt__["grains.item"](*grain_targets) for grain, match, match_attrs in match_info: if grain not in grain_vals: continue if grain_vals[grain] == match: attrs.update(match_attrs) - if hasattr(cls, 'merge'): - pillar = Map.__salt__['pillar.get'](cls.merge) + if hasattr(cls, "merge"): + pillar = Map.__salt__["pillar.get"](cls.merge) if pillar: attrs.update(pillar) @@ -390,7 +396,4 @@ def need_salt(*a, **k): class Map(six.with_metaclass(MapMeta, object)): # pylint: disable=W0232 - __salt__ = { - 'grains.filter_by': need_salt, - 'pillar.get': need_salt - } + __salt__ = {"grains.filter_by": need_salt, "pillar.get": need_salt} diff --git a/salt/utils/reactor.py b/salt/utils/reactor.py index 6c7305515dc..4fc654fbf17 100644 --- a/salt/utils/reactor.py +++ b/salt/utils/reactor.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" Functions which implement running reactor jobs -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import fnmatch import glob import logging # Import salt libs import salt.client +import salt.defaults.exitcodes import salt.runner import salt.state import salt.utils.args @@ -22,38 +24,33 @@ import salt.utils.files import salt.utils.process import salt.utils.yaml import salt.wheel -import salt.defaults.exitcodes # Import 3rd-party libs from salt.ext import six log = logging.getLogger(__name__) -REACTOR_INTERNAL_KEYWORDS = frozenset([ - '__id__', - '__sls__', - 'name', - 'order', - 'fun', - 'state', -]) +REACTOR_INTERNAL_KEYWORDS = frozenset( + ["__id__", "__sls__", "name", "order", "fun", "state"] +) class Reactor(salt.utils.process.SignalHandlingProcess, salt.state.Compiler): - ''' + """ Read in the reactor configuration variable and compare it to events processed on the master. The reactor has the capability to execute pre-programmed executions as reactions to events - ''' + """ + aliases = { - 'cmd': 'local', + "cmd": "local", } def __init__(self, opts, **kwargs): super(Reactor, self).__init__(**kwargs) local_minion_opts = opts.copy() - local_minion_opts['file_client'] = 'local' + local_minion_opts["file_client"] = "local" self.minion = salt.minion.MasterMinion(local_minion_opts) salt.state.Compiler.__init__(self, opts, self.minion.rend) @@ -64,41 +61,43 @@ class Reactor(salt.utils.process.SignalHandlingProcess, salt.state.Compiler): # non-Windows platforms. def __setstate__(self, state): Reactor.__init__( - self, state['opts'], - log_queue=state['log_queue'], - log_queue_level=state['log_queue_level'] + self, + state["opts"], + log_queue=state["log_queue"], + log_queue_level=state["log_queue_level"], ) def __getstate__(self): return { - 'opts': self.opts, - 'log_queue': self.log_queue, - 'log_queue_level': self.log_queue_level + "opts": self.opts, + "log_queue": self.log_queue, + "log_queue_level": self.log_queue_level, } def render_reaction(self, glob_ref, tag, data): - ''' + """ Execute the render system against a single reaction file and return the data structure - ''' + """ react = {} - if glob_ref.startswith('salt://'): - glob_ref = self.minion.functions['cp.cache_file'](glob_ref) or '' + if glob_ref.startswith("salt://"): + glob_ref = self.minion.functions["cp.cache_file"](glob_ref) or "" globbed_ref = glob.glob(glob_ref) if not globbed_ref: - log.error('Can not render SLS %s for tag %s. File missing or not found.', glob_ref, tag) + log.error( + "Can not render SLS %s for tag %s. File missing or not found.", + glob_ref, + tag, + ) for fn_ in globbed_ref: try: - res = self.render_template( - fn_, - tag=tag, - data=data) + res = self.render_template(fn_, tag=tag, data=data) # for #20841, inject the sls name here since verify_high() # assumes it exists in case there are any errors for name in res: - res[name]['__sls__'] = fn_ + res[name]["__sls__"] = fn_ react.update(res) except Exception: # pylint: disable=broad-except @@ -106,22 +105,24 @@ class Reactor(salt.utils.process.SignalHandlingProcess, salt.state.Compiler): return react def list_reactors(self, tag): - ''' + """ Take in the tag from an event and return a list of the reactors to process - ''' - log.debug('Gathering reactors for tag %s', tag) + """ + log.debug("Gathering reactors for tag %s", tag) reactors = [] - if isinstance(self.opts['reactor'], six.string_types): + if isinstance(self.opts["reactor"], six.string_types): try: - with salt.utils.files.fopen(self.opts['reactor']) as fp_: + with salt.utils.files.fopen(self.opts["reactor"]) as fp_: react_map = salt.utils.yaml.safe_load(fp_) except (OSError, IOError): - log.error('Failed to read reactor map: "%s"', self.opts['reactor']) + log.error('Failed to read reactor map: "%s"', self.opts["reactor"]) except Exception: # pylint: disable=broad-except - log.error('Failed to parse YAML in reactor map: "%s"', self.opts['reactor']) + log.error( + 'Failed to parse YAML in reactor map: "%s"', self.opts["reactor"] + ) else: - react_map = self.opts['reactor'] + react_map = self.opts["reactor"] for ropt in react_map: if not isinstance(ropt, dict): continue @@ -137,67 +138,66 @@ class Reactor(salt.utils.process.SignalHandlingProcess, salt.state.Compiler): return reactors def list_all(self): - ''' + """ Return a list of the reactors - ''' - if isinstance(self.minion.opts['reactor'], six.string_types): - log.debug('Reading reactors from yaml %s', self.opts['reactor']) + """ + if isinstance(self.minion.opts["reactor"], six.string_types): + log.debug("Reading reactors from yaml %s", self.opts["reactor"]) try: - with salt.utils.files.fopen(self.opts['reactor']) as fp_: + with salt.utils.files.fopen(self.opts["reactor"]) as fp_: react_map = salt.utils.yaml.safe_load(fp_) except (OSError, IOError): - log.error('Failed to read reactor map: "%s"', self.opts['reactor']) + log.error('Failed to read reactor map: "%s"', self.opts["reactor"]) except Exception: # pylint: disable=broad-except log.error( - 'Failed to parse YAML in reactor map: "%s"', - self.opts['reactor'] + 'Failed to parse YAML in reactor map: "%s"', self.opts["reactor"] ) else: - log.debug('Not reading reactors from yaml') - react_map = self.minion.opts['reactor'] + log.debug("Not reading reactors from yaml") + react_map = self.minion.opts["reactor"] return react_map def add_reactor(self, tag, reaction): - ''' + """ Add a reactor - ''' + """ reactors = self.list_all() for reactor in reactors: _tag = next(six.iterkeys(reactor)) if _tag == tag: - return {'status': False, 'comment': 'Reactor already exists.'} + return {"status": False, "comment": "Reactor already exists."} - self.minion.opts['reactor'].append({tag: reaction}) - return {'status': True, 'comment': 'Reactor added.'} + self.minion.opts["reactor"].append({tag: reaction}) + return {"status": True, "comment": "Reactor added."} def delete_reactor(self, tag): - ''' + """ Delete a reactor - ''' + """ reactors = self.list_all() for reactor in reactors: _tag = next(six.iterkeys(reactor)) if _tag == tag: - self.minion.opts['reactor'].remove(reactor) - return {'status': True, 'comment': 'Reactor deleted.'} + self.minion.opts["reactor"].remove(reactor) + return {"status": True, "comment": "Reactor deleted."} - return {'status': False, 'comment': 'Reactor does not exists.'} + return {"status": False, "comment": "Reactor does not exists."} def resolve_aliases(self, chunks): - ''' + """ Preserve backward compatibility by rewriting the 'state' key in the low chunks if it is using a legacy type. - ''' + """ for idx, _ in enumerate(chunks): - new_state = self.aliases.get(chunks[idx]['state']) + new_state = self.aliases.get(chunks[idx]["state"]) if new_state is not None: - chunks[idx]['state'] = new_state + chunks[idx]["state"] = new_state def reactions(self, tag, data, reactors): - ''' + """ Render a list of reactor files and returns a reaction struct - ''' - log.debug('Compiling reactions for tag %s', tag) + """ + log.debug("Compiling reactions for tag %s", tag) high = {} chunks = [] try: @@ -207,108 +207,119 @@ class Reactor(salt.utils.process.SignalHandlingProcess, salt.state.Compiler): errors = self.verify_high(high) if errors: log.error( - 'Unable to render reactions for event %s due to ' - 'errors (%s) in one or more of the sls files (%s)', - tag, errors, reactors + "Unable to render reactions for event %s due to " + "errors (%s) in one or more of the sls files (%s)", + tag, + errors, + reactors, ) return [] # We'll return nothing since there was an error chunks = self.order_chunks(self.compile_high_data(high)) except Exception as exc: # pylint: disable=broad-except - log.exception('Exception encountered while compiling reactions') + log.exception("Exception encountered while compiling reactions") self.resolve_aliases(chunks) return chunks def call_reactions(self, chunks): - ''' + """ Execute the reaction state - ''' + """ for chunk in chunks: self.wrap.run(chunk) def run(self): - ''' + """ Enter into the server loop - ''' + """ salt.utils.process.appendproctitle(self.__class__.__name__) # instantiate some classes inside our new process with salt.utils.event.get_event( - self.opts['__role'], - self.opts['sock_dir'], - self.opts['transport'], - opts=self.opts, - listen=True) as event: + self.opts["__role"], + self.opts["sock_dir"], + self.opts["transport"], + opts=self.opts, + listen=True, + ) as event: self.wrap = ReactWrap(self.opts) for data in event.iter_events(full=True): # skip all events fired by ourselves - if data['data'].get('user') == self.wrap.event_user: + if data["data"].get("user") == self.wrap.event_user: continue - if data['tag'].endswith('salt/reactors/manage/add'): - _data = data['data'] - res = self.add_reactor(_data['event'], _data['reactors']) - event.fire_event({'reactors': self.list_all(), - 'result': res}, - 'salt/reactors/manage/add-complete') - elif data['tag'].endswith('salt/reactors/manage/delete'): - _data = data['data'] - res = self.delete_reactor(_data['event']) - event.fire_event({'reactors': self.list_all(), - 'result': res}, - 'salt/reactors/manage/delete-complete') - elif data['tag'].endswith('salt/reactors/manage/list'): - event.fire_event({'reactors': self.list_all()}, - 'salt/reactors/manage/list-results') + if data["tag"].endswith("salt/reactors/manage/add"): + _data = data["data"] + res = self.add_reactor(_data["event"], _data["reactors"]) + event.fire_event( + {"reactors": self.list_all(), "result": res}, + "salt/reactors/manage/add-complete", + ) + elif data["tag"].endswith("salt/reactors/manage/delete"): + _data = data["data"] + res = self.delete_reactor(_data["event"]) + event.fire_event( + {"reactors": self.list_all(), "result": res}, + "salt/reactors/manage/delete-complete", + ) + elif data["tag"].endswith("salt/reactors/manage/list"): + event.fire_event( + {"reactors": self.list_all()}, + "salt/reactors/manage/list-results", + ) else: - reactors = self.list_reactors(data['tag']) + reactors = self.list_reactors(data["tag"]) if not reactors: continue - chunks = self.reactions(data['tag'], data['data'], reactors) + chunks = self.reactions(data["tag"], data["data"], reactors) if chunks: try: self.call_reactions(chunks) except SystemExit: - log.warning('Exit ignored by reactor') + log.warning("Exit ignored by reactor") class ReactWrap(object): - ''' + """ Wrapper that executes low data for the Reactor System - ''' + """ + # class-wide cache of clients client_cache = None - event_user = 'Reactor' + event_user = "Reactor" reaction_class = { - 'local': salt.client.LocalClient, - 'runner': salt.runner.RunnerClient, - 'wheel': salt.wheel.Wheel, - 'caller': salt.client.Caller, + "local": salt.client.LocalClient, + "runner": salt.runner.RunnerClient, + "wheel": salt.wheel.Wheel, + "caller": salt.client.Caller, } def __init__(self, opts): self.opts = opts if ReactWrap.client_cache is None: - ReactWrap.client_cache = salt.utils.cache.CacheDict(opts['reactor_refresh_interval']) + ReactWrap.client_cache = salt.utils.cache.CacheDict( + opts["reactor_refresh_interval"] + ) self.pool = salt.utils.process.ThreadPool( - self.opts['reactor_worker_threads'], # number of workers for runner/wheel - queue_size=self.opts['reactor_worker_hwm'] # queue size for those workers + self.opts["reactor_worker_threads"], # number of workers for runner/wheel + queue_size=self.opts["reactor_worker_hwm"], # queue size for those workers ) def populate_client_cache(self, low): - ''' + """ Populate the client cache with an instance of the specified type - ''' - reaction_type = low['state'] + """ + reaction_type = low["state"] if reaction_type not in self.client_cache: - log.debug('Reactor is populating %s client cache', reaction_type) - if reaction_type in ('runner', 'wheel'): + log.debug("Reactor is populating %s client cache", reaction_type) + if reaction_type in ("runner", "wheel"): # Reaction types that run locally on the master want the full # opts passed. - self.client_cache[reaction_type] = \ - self.reaction_class[reaction_type](self.opts) + self.client_cache[reaction_type] = self.reaction_class[reaction_type]( + self.opts + ) # The len() function will cause the module functions to load if # they aren't already loaded. We want to load them so that the # spawned threads don't need to load them. Loading in the @@ -319,98 +330,104 @@ class ReactWrap(object): else: # Reactions which use remote pubs only need the conf file when # instantiating a client instance. - self.client_cache[reaction_type] = \ - self.reaction_class[reaction_type](self.opts['conf_file']) + self.client_cache[reaction_type] = self.reaction_class[reaction_type]( + self.opts["conf_file"] + ) def run(self, low): - ''' + """ Execute a reaction by invoking the proper wrapper func - ''' + """ self.populate_client_cache(low) try: - l_fun = getattr(self, low['state']) + l_fun = getattr(self, low["state"]) except AttributeError: - log.error( - 'ReactWrap is missing a wrapper function for \'%s\'', - low['state'] - ) + log.error("ReactWrap is missing a wrapper function for '%s'", low["state"]) try: wrap_call = salt.utils.args.format_call(l_fun, low) - args = wrap_call.get('args', ()) - kwargs = wrap_call.get('kwargs', {}) + args = wrap_call.get("args", ()) + kwargs = wrap_call.get("kwargs", {}) # TODO: Setting user doesn't seem to work for actual remote pubs - if low['state'] in ('runner', 'wheel'): + if low["state"] in ("runner", "wheel"): # Update called function's low data with event user to # segregate events fired by reactor and avoid reaction loops - kwargs['__user__'] = self.event_user + kwargs["__user__"] = self.event_user # Replace ``state`` kwarg which comes from high data compiler. # It breaks some runner functions and seems unnecessary. - kwargs['__state__'] = kwargs.pop('state') + kwargs["__state__"] = kwargs.pop("state") # NOTE: if any additional keys are added here, they will also # need to be added to filter_kwargs() - if 'args' in kwargs: + if "args" in kwargs: # New configuration - reactor_args = kwargs.pop('args') - for item in ('arg', 'kwarg'): + reactor_args = kwargs.pop("args") + for item in ("arg", "kwarg"): if item in low: log.warning( - 'Reactor \'%s\' is ignoring \'%s\' param %s due to ' - 'presence of \'args\' param. Check the Reactor System ' - 'documentation for the correct argument format.', - low['__id__'], item, low[item] + "Reactor '%s' is ignoring '%s' param %s due to " + "presence of 'args' param. Check the Reactor System " + "documentation for the correct argument format.", + low["__id__"], + item, + low[item], ) - if low['state'] == 'caller' \ - and isinstance(reactor_args, list) \ - and not salt.utils.data.is_dictlist(reactor_args): + if ( + low["state"] == "caller" + and isinstance(reactor_args, list) + and not salt.utils.data.is_dictlist(reactor_args) + ): # Legacy 'caller' reactors were already using the 'args' # param, but only supported a list of positional arguments. # If low['args'] is a list but is *not* a dictlist, then # this is actually using the legacy configuration. So, put # the reactor args into kwarg['arg'] so that the wrapper # interprets them as positional args. - kwargs['arg'] = reactor_args - kwargs['kwarg'] = {} + kwargs["arg"] = reactor_args + kwargs["kwarg"] = {} else: - kwargs['arg'] = () - kwargs['kwarg'] = reactor_args - if not isinstance(kwargs['kwarg'], dict): - kwargs['kwarg'] = salt.utils.data.repack_dictlist(kwargs['kwarg']) - if not kwargs['kwarg']: + kwargs["arg"] = () + kwargs["kwarg"] = reactor_args + if not isinstance(kwargs["kwarg"], dict): + kwargs["kwarg"] = salt.utils.data.repack_dictlist(kwargs["kwarg"]) + if not kwargs["kwarg"]: log.error( - 'Reactor \'%s\' failed to execute %s \'%s\': ' - 'Incorrect argument format, check the Reactor System ' - 'documentation for the correct format.', - low['__id__'], low['state'], low['fun'] + "Reactor '%s' failed to execute %s '%s': " + "Incorrect argument format, check the Reactor System " + "documentation for the correct format.", + low["__id__"], + low["state"], + low["fun"], ) return else: # Legacy configuration react_call = {} - if low['state'] in ('runner', 'wheel'): - if 'arg' not in kwargs or 'kwarg' not in kwargs: + if low["state"] in ("runner", "wheel"): + if "arg" not in kwargs or "kwarg" not in kwargs: # Runner/wheel execute on the master, so we can use # format_call to get the functions args/kwargs - react_fun = self.client_cache[low['state']].functions.get(low['fun']) + react_fun = self.client_cache[low["state"]].functions.get( + low["fun"] + ) if react_fun is None: log.error( - 'Reactor \'%s\' failed to execute %s \'%s\': ' - 'function not available', - low['__id__'], low['state'], low['fun'] + "Reactor '%s' failed to execute %s '%s': " + "function not available", + low["__id__"], + low["state"], + low["fun"], ) return react_call = salt.utils.args.format_call( - react_fun, - low, - expected_extra_kws=REACTOR_INTERNAL_KEYWORDS + react_fun, low, expected_extra_kws=REACTOR_INTERNAL_KEYWORDS ) - if 'arg' not in kwargs: - kwargs['arg'] = react_call.get('args', ()) - if 'kwarg' not in kwargs: - kwargs['kwarg'] = react_call.get('kwargs', {}) + if "arg" not in kwargs: + kwargs["arg"] = react_call.get("args", ()) + if "kwarg" not in kwargs: + kwargs["kwarg"] = react_call.get("kwargs", {}) # Execute the wrapper with the proper args/kwargs. kwargs['arg'] # and kwargs['kwarg'] contain the positional and keyword arguments @@ -418,35 +435,36 @@ class ReactWrap(object): # desired runner/wheel/remote-exec/etc. function. l_fun(*args, **kwargs) except SystemExit: - log.warning( - 'Reactor \'%s\' attempted to exit. Ignored.', low['__id__'] - ) + log.warning("Reactor '%s' attempted to exit. Ignored.", low["__id__"]) except Exception: # pylint: disable=broad-except log.error( - 'Reactor \'%s\' failed to execute %s \'%s\'', - low['__id__'], low['state'], low['fun'], exc_info=True + "Reactor '%s' failed to execute %s '%s'", + low["__id__"], + low["state"], + low["fun"], + exc_info=True, ) def runner(self, fun, **kwargs): - ''' + """ Wrap RunnerClient for executing :ref:`runner modules <all-salt.runners>` - ''' - self.pool.fire_async(self.client_cache['runner'].low, args=(fun, kwargs)) + """ + self.pool.fire_async(self.client_cache["runner"].low, args=(fun, kwargs)) def wheel(self, fun, **kwargs): - ''' + """ Wrap Wheel to enable executing :ref:`wheel modules <all-salt.wheel>` - ''' - self.pool.fire_async(self.client_cache['wheel'].low, args=(fun, kwargs)) + """ + self.pool.fire_async(self.client_cache["wheel"].low, args=(fun, kwargs)) def local(self, fun, tgt, **kwargs): - ''' + """ Wrap LocalClient for running :ref:`execution modules <all-salt.modules>` - ''' - self.client_cache['local'].cmd_async(tgt, fun, **kwargs) + """ + self.client_cache["local"].cmd_async(tgt, fun, **kwargs) def caller(self, fun, **kwargs): - ''' + """ Wrap LocalCaller to execute remote exec functions locally on the Minion - ''' - self.client_cache['caller'].cmd(fun, *kwargs['arg'], **kwargs['kwarg']) + """ + self.client_cache["caller"].cmd(fun, *kwargs["arg"], **kwargs["kwarg"]) diff --git a/salt/utils/reclass.py b/salt/utils/reclass.py index e3c0768d66e..f78a449955c 100644 --- a/salt/utils/reclass.py +++ b/salt/utils/reclass.py @@ -1,32 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" Common utility functions for the reclass adapters http://reclass.pantsfullofunix.net -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import os + # Import python libs import sys -import os def prepend_reclass_source_path(opts): - source_path = opts.get('reclass_source_path') + source_path = opts.get("reclass_source_path") if source_path: source_path = os.path.abspath(os.path.expanduser(source_path)) sys.path.insert(0, source_path) def filter_out_source_path_option(opts): - if 'reclass_source_path' in opts: - del opts['reclass_source_path'] + if "reclass_source_path" in opts: + del opts["reclass_source_path"] # no return required, object was passed by reference def set_inventory_base_uri_default(config, opts): - if 'inventory_base_uri' in opts: + if "inventory_base_uri" in opts: return - base_roots = config.get('file_roots', {}).get('base', []) + base_roots = config.get("file_roots", {}).get("base", []) if len(base_roots) > 0: - opts['inventory_base_uri'] = base_roots[0] + opts["inventory_base_uri"] = base_roots[0] diff --git a/salt/utils/roster_matcher.py b/salt/utils/roster_matcher.py index 1ba8e5bacf8..ee4f0d42eb4 100644 --- a/salt/utils/roster_matcher.py +++ b/salt/utils/roster_matcher.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Roster matching by various criteria (glob, pcre, etc) -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -11,62 +11,64 @@ import functools import logging import re +# Import Salt libs +from salt.ext import six + # Try to import range from https://github.com/ytoolshed/range HAS_RANGE = False try: import seco.range + HAS_RANGE = True except ImportError: pass # pylint: enable=import-error -# Import Salt libs -from salt.ext import six - log = logging.getLogger(__name__) -def targets(conditioned_raw, tgt, tgt_type, ipv='ipv4'): +def targets(conditioned_raw, tgt, tgt_type, ipv="ipv4"): rmatcher = RosterMatcher(conditioned_raw, tgt, tgt_type, ipv) return rmatcher.targets() def _tgt_set(tgt): - ''' + """ Return the tgt as a set of literal names - ''' + """ try: # A comma-delimited string - return set(tgt.split(',')) + return set(tgt.split(",")) except AttributeError: # Assume tgt is already a non-string iterable. return set(tgt) class RosterMatcher(object): - ''' + """ Matcher for the roster data structure - ''' - def __init__(self, raw, tgt, tgt_type, ipv='ipv4'): + """ + + def __init__(self, raw, tgt, tgt_type, ipv="ipv4"): self.tgt = tgt self.tgt_type = tgt_type self.raw = raw self.ipv = ipv def targets(self): - ''' + """ Execute the correct tgt_type routine and return - ''' + """ try: - return getattr(self, 'ret_{0}_minions'.format(self.tgt_type))() + return getattr(self, "ret_{0}_minions".format(self.tgt_type))() except AttributeError: return {} def _ret_minions(self, filter_): - ''' + """ Filter minions by a generic filter. - ''' + """ minions = {} for minion in filter_(self.raw): data = self.get_data(minion) @@ -75,54 +77,54 @@ class RosterMatcher(object): return minions def ret_glob_minions(self): - ''' + """ Return minions that match via glob - ''' + """ fnfilter = functools.partial(fnmatch.filter, pat=self.tgt) return self._ret_minions(fnfilter) def ret_pcre_minions(self): - ''' + """ Return minions that match via pcre - ''' + """ tgt = re.compile(self.tgt) refilter = functools.partial(filter, tgt.match) return self._ret_minions(refilter) def ret_list_minions(self): - ''' + """ Return minions that match via list - ''' + """ tgt = _tgt_set(self.tgt) return self._ret_minions(tgt.intersection) def ret_nodegroup_minions(self): - ''' + """ Return minions which match the special list-only groups defined by ssh_list_nodegroups - ''' - nodegroup = __opts__.get('ssh_list_nodegroups', {}).get(self.tgt, []) + """ + nodegroup = __opts__.get("ssh_list_nodegroups", {}).get(self.tgt, []) nodegroup = _tgt_set(nodegroup) return self._ret_minions(nodegroup.intersection) def ret_range_minions(self): - ''' + """ Return minions that are returned by a range query - ''' + """ if HAS_RANGE is False: raise RuntimeError("Python lib 'seco.range' is not available") minions = {} - range_hosts = _convert_range_to_list(self.tgt, __opts__['range_server']) + range_hosts = _convert_range_to_list(self.tgt, __opts__["range_server"]) return self._ret_minions(range_hosts.__contains__) def get_data(self, minion): - ''' + """ Return the configured ip - ''' - ret = copy.deepcopy(__opts__.get('roster_defaults', {})) + """ + ret = copy.deepcopy(__opts__.get("roster_defaults", {})) if isinstance(self.raw[minion], six.string_types): - ret.update({'host': self.raw[minion]}) + ret.update({"host": self.raw[minion]}) return ret elif isinstance(self.raw[minion], dict): ret.update(self.raw[minion]) @@ -131,12 +133,12 @@ class RosterMatcher(object): def _convert_range_to_list(tgt, range_server): - ''' + """ convert a seco.range range into a list target - ''' + """ r = seco.range.Range(range_server) try: return r.expand(tgt) except seco.range.RangeException as err: - log.error('Range server exception: %s', err) + log.error("Range server exception: %s", err) return [] diff --git a/salt/utils/rsax931.py b/salt/utils/rsax931.py index 1ee72f49552..e3c68f8c882 100644 --- a/salt/utils/rsax931.py +++ b/salt/utils/rsax931.py @@ -1,22 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Create and verify ANSI X9.31 RSA signatures using OpenSSL libcrypto -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import glob -import sys import os +import sys + +# Import 3rd-party libs +from ctypes import c_char_p, c_int, c_void_p, cdll, create_string_buffer, pointer +from ctypes.util import find_library # Import Salt libs import salt.utils.platform import salt.utils.stringutils -# Import 3rd-party libs -from ctypes import cdll, c_char_p, c_int, c_void_p, pointer, create_string_buffer -from ctypes.util import find_library - # Constants taken from openssl-1.1.0c/include/openssl/crypto.h OPENSSL_INIT_ADD_ALL_CIPHERS = 0x00000004 OPENSSL_INIT_ADD_ALL_DIGESTS = 0x00000008 @@ -24,18 +25,20 @@ OPENSSL_INIT_NO_LOAD_CONFIG = 0x00000080 def _load_libcrypto(): - ''' + """ Load OpenSSL libcrypto - ''' - if sys.platform.startswith('win'): + """ + if sys.platform.startswith("win"): # cdll.LoadLibrary on windows requires an 'str' argument - return cdll.LoadLibrary(str('libeay32')) # future lint: disable=blacklisted-function - elif getattr(sys, 'frozen', False) and salt.utils.platform.is_smartos(): - return cdll.LoadLibrary(glob.glob(os.path.join( - os.path.dirname(sys.executable), - 'libcrypto.so*'))[0]) + return cdll.LoadLibrary( + str("libeay32") + ) # future lint: disable=blacklisted-function + elif getattr(sys, "frozen", False) and salt.utils.platform.is_smartos(): + return cdll.LoadLibrary( + glob.glob(os.path.join(os.path.dirname(sys.executable), "libcrypto.so*"))[0] + ) else: - lib = find_library('crypto') + lib = find_library("crypto") if not lib and salt.utils.platform.is_sunos(): # Solaris-like distribution that use pkgsrc have # libraries in a non standard location. @@ -43,25 +46,27 @@ def _load_libcrypto(): # This could be /opt/tools/lib (Global Zone) # or /opt/local/lib (non-Global Zone), thus the # two checks below - lib = glob.glob('/opt/local/lib/libcrypto.so*') + glob.glob('/opt/tools/lib/libcrypto.so*') + lib = glob.glob("/opt/local/lib/libcrypto.so*") + glob.glob( + "/opt/tools/lib/libcrypto.so*" + ) lib = lib[0] if len(lib) > 0 else None if not lib and salt.utils.platform.is_aix(): - if os.path.isdir('/opt/salt/lib'): + if os.path.isdir("/opt/salt/lib"): # preference for Salt installed fileset - lib = glob.glob('/opt/salt/lib/libcrypto.so*') + lib = glob.glob("/opt/salt/lib/libcrypto.so*") lib = lib[0] if len(lib) > 0 else None else: - lib = glob.glob('/opt/freeware/lib/libcrypto.so*') + lib = glob.glob("/opt/freeware/lib/libcrypto.so*") lib = lib[0] if len(lib) > 0 else None if lib: return cdll.LoadLibrary(lib) - raise OSError('Cannot locate OpenSSL libcrypto') + raise OSError("Cannot locate OpenSSL libcrypto") def _init_libcrypto(): - ''' + """ Set up libcrypto argtypes and initialize the library - ''' + """ libcrypto = _load_libcrypto() try: @@ -78,16 +83,32 @@ def _init_libcrypto(): libcrypto.RSA_new.argtypes = () libcrypto.RSA_new.restype = c_void_p - libcrypto.RSA_free.argtypes = (c_void_p, ) - libcrypto.RSA_size.argtype = (c_void_p) + libcrypto.RSA_free.argtypes = (c_void_p,) + libcrypto.RSA_size.argtype = c_void_p libcrypto.BIO_new_mem_buf.argtypes = (c_char_p, c_int) libcrypto.BIO_new_mem_buf.restype = c_void_p - libcrypto.BIO_free.argtypes = (c_void_p, ) - libcrypto.PEM_read_bio_RSAPrivateKey.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p) + libcrypto.BIO_free.argtypes = (c_void_p,) + libcrypto.PEM_read_bio_RSAPrivateKey.argtypes = ( + c_void_p, + c_void_p, + c_void_p, + c_void_p, + ) libcrypto.PEM_read_bio_RSAPrivateKey.restype = c_void_p - libcrypto.PEM_read_bio_RSA_PUBKEY.argtypes = (c_void_p, c_void_p, c_void_p, c_void_p) + libcrypto.PEM_read_bio_RSA_PUBKEY.argtypes = ( + c_void_p, + c_void_p, + c_void_p, + c_void_p, + ) libcrypto.PEM_read_bio_RSA_PUBKEY.restype = c_void_p - libcrypto.RSA_private_encrypt.argtypes = (c_int, c_char_p, c_char_p, c_void_p, c_int) + libcrypto.RSA_private_encrypt.argtypes = ( + c_int, + c_char_p, + c_char_p, + c_void_p, + c_int, + ) libcrypto.RSA_public_decrypt.argtypes = (c_int, c_char_p, c_char_p, c_void_p, c_int) return libcrypto @@ -100,80 +121,92 @@ RSA_X931_PADDING = 5 class RSAX931Signer(object): - ''' + """ Create ANSI X9.31 RSA signatures using OpenSSL libcrypto - ''' + """ + def __init__(self, keydata): - ''' + """ Init an RSAX931Signer instance :param str keydata: The RSA private key in PEM format - ''' - keydata = salt.utils.stringutils.to_bytes(keydata, 'ascii') + """ + keydata = salt.utils.stringutils.to_bytes(keydata, "ascii") self._bio = libcrypto.BIO_new_mem_buf(keydata, len(keydata)) self._rsa = c_void_p(libcrypto.RSA_new()) - if not libcrypto.PEM_read_bio_RSAPrivateKey(self._bio, pointer(self._rsa), None, None): - raise ValueError('invalid RSA private key') + if not libcrypto.PEM_read_bio_RSAPrivateKey( + self._bio, pointer(self._rsa), None, None + ): + raise ValueError("invalid RSA private key") # pylint: disable=W1701 def __del__(self): libcrypto.BIO_free(self._bio) libcrypto.RSA_free(self._rsa) + # pylint: enable=W1701 def sign(self, msg): - ''' + """ Sign a message (digest) using the private key :param str msg: The message (digest) to sign :rtype: str :return: The signature, or an empty string if the encryption failed - ''' + """ # Allocate a buffer large enough for the signature. Freed by ctypes. buf = create_string_buffer(libcrypto.RSA_size(self._rsa)) msg = salt.utils.stringutils.to_bytes(msg) - size = libcrypto.RSA_private_encrypt(len(msg), msg, buf, self._rsa, RSA_X931_PADDING) + size = libcrypto.RSA_private_encrypt( + len(msg), msg, buf, self._rsa, RSA_X931_PADDING + ) if size < 0: - raise ValueError('Unable to encrypt message') + raise ValueError("Unable to encrypt message") return buf[0:size] class RSAX931Verifier(object): - ''' + """ Verify ANSI X9.31 RSA signatures using OpenSSL libcrypto - ''' + """ + def __init__(self, pubdata): - ''' + """ Init an RSAX931Verifier instance :param str pubdata: The RSA public key in PEM format - ''' - pubdata = salt.utils.stringutils.to_bytes(pubdata, 'ascii') - pubdata = pubdata.replace(b'RSA ', b'') + """ + pubdata = salt.utils.stringutils.to_bytes(pubdata, "ascii") + pubdata = pubdata.replace(b"RSA ", b"") self._bio = libcrypto.BIO_new_mem_buf(pubdata, len(pubdata)) self._rsa = c_void_p(libcrypto.RSA_new()) - if not libcrypto.PEM_read_bio_RSA_PUBKEY(self._bio, pointer(self._rsa), None, None): - raise ValueError('invalid RSA public key') + if not libcrypto.PEM_read_bio_RSA_PUBKEY( + self._bio, pointer(self._rsa), None, None + ): + raise ValueError("invalid RSA public key") # pylint: disable=W1701 def __del__(self): libcrypto.BIO_free(self._bio) libcrypto.RSA_free(self._rsa) + # pylint: enable=W1701 def verify(self, signed): - ''' + """ Recover the message (digest) from the signature using the public key :param str signed: The signature created with the private key :rtype: str :return: The message (digest) recovered from the signature, or an empty string if the decryption failed - ''' + """ # Allocate a buffer large enough for the signature. Freed by ctypes. buf = create_string_buffer(libcrypto.RSA_size(self._rsa)) signed = salt.utils.stringutils.to_bytes(signed) - size = libcrypto.RSA_public_decrypt(len(signed), signed, buf, self._rsa, RSA_X931_PADDING) + size = libcrypto.RSA_public_decrypt( + len(signed), signed, buf, self._rsa, RSA_X931_PADDING + ) if size < 0: - raise ValueError('Unable to decrypt message') + raise ValueError("Unable to decrypt message") return buf[0:size] diff --git a/salt/utils/s3.py b/salt/utils/s3.py index b9c702b2e9e..bfbf7191768 100644 --- a/salt/utils/s3.py +++ b/salt/utils/s3.py @@ -1,21 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Connection library for Amazon S3 :depends: requests -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging -# Import 3rd-party libs -try: - import requests - HAS_REQUESTS = True # pylint: disable=W0612 -except ImportError: - HAS_REQUESTS = False # pylint: disable=W0612 - # Import Salt libs import salt.utils.aws import salt.utils.files @@ -23,19 +16,45 @@ import salt.utils.hashutils import salt.utils.xmlutil as xml from salt._compat import ElementTree as ET from salt.exceptions import CommandExecutionError -from salt.ext.six.moves.urllib.parse import quote as _quote # pylint: disable=import-error,no-name-in-module from salt.ext import six +from salt.ext.six.moves.urllib.parse import quote as _quote + +# Import 3rd-party libs +try: + import requests + + HAS_REQUESTS = True # pylint: disable=W0612 +except ImportError: + HAS_REQUESTS = False # pylint: disable=W0612 + log = logging.getLogger(__name__) -def query(key, keyid, method='GET', params=None, headers=None, - requesturl=None, return_url=False, bucket=None, service_url=None, - path='', return_bin=False, action=None, local_file=None, - verify_ssl=True, full_headers=False, kms_keyid=None, - location=None, role_arn=None, chunk_size=16384, path_style=False, - https_enable=True): - ''' +def query( + key, + keyid, + method="GET", + params=None, + headers=None, + requesturl=None, + return_url=False, + bucket=None, + service_url=None, + path="", + return_bin=False, + action=None, + local_file=None, + verify_ssl=True, + full_headers=False, + kms_keyid=None, + location=None, + role_arn=None, + chunk_size=16384, + path_style=False, + https_enable=True, +): + """ Perform a query against an S3-like API. This function requires that a secret key and the id for that key are passed in. For instance: @@ -82,9 +101,9 @@ def query(key, keyid, method='GET', params=None, headers=None, If region is not specified, an attempt to fetch the region from EC2 IAM metadata service will be made. Failing that, default is us-east-1 - ''' + """ if not HAS_REQUESTS: - log.error('There was an error: requests is required for s3 access') + log.error("There was an error: requests is required for s3 access") if not headers: headers = {} @@ -93,15 +112,15 @@ def query(key, keyid, method='GET', params=None, headers=None, params = {} if not service_url: - service_url = 's3.amazonaws.com' + service_url = "s3.amazonaws.com" if not bucket or path_style: endpoint = service_url else: - endpoint = '{0}.{1}'.format(bucket, service_url) + endpoint = "{0}.{1}".format(bucket, service_url) if path_style and bucket: - path = '{0}/{1}'.format(bucket, path) + path = "{0}/{1}".format(bucket, path) # Try grabbing the credentials from the EC2 instance IAM metadata if available if not key: @@ -110,75 +129,85 @@ def query(key, keyid, method='GET', params=None, headers=None, if not keyid: keyid = salt.utils.aws.IROLE_CODE - if kms_keyid is not None and method in ('PUT', 'POST'): - headers['x-amz-server-side-encryption'] = 'aws:kms' - headers['x-amz-server-side-encryption-aws-kms-key-id'] = kms_keyid + if kms_keyid is not None and method in ("PUT", "POST"): + headers["x-amz-server-side-encryption"] = "aws:kms" + headers["x-amz-server-side-encryption-aws-kms-key-id"] = kms_keyid if not location: location = salt.utils.aws.get_location() - data = '' + data = "" fh = None payload_hash = None - if method == 'PUT': + if method == "PUT": if local_file: - payload_hash = salt.utils.hashutils.get_hash(local_file, form='sha256') + payload_hash = salt.utils.hashutils.get_hash(local_file, form="sha256") if path is None: - path = '' + path = "" path = _quote(path) if not requesturl: - requesturl = (('https' if https_enable else 'http')+'://{0}/{1}').format(endpoint, path) + requesturl = (("https" if https_enable else "http") + "://{0}/{1}").format( + endpoint, path + ) headers, requesturl = salt.utils.aws.sig4( method, endpoint, params, data=data, - uri='/{0}'.format(path), - prov_dict={'id': keyid, 'key': key}, + uri="/{0}".format(path), + prov_dict={"id": keyid, "key": key}, role_arn=role_arn, location=location, - product='s3', + product="s3", requesturl=requesturl, headers=headers, payload_hash=payload_hash, ) - log.debug('S3 Request: %s', requesturl) - log.debug('S3 Headers::') - log.debug(' Authorization: %s', headers['Authorization']) + log.debug("S3 Request: %s", requesturl) + log.debug("S3 Headers::") + log.debug(" Authorization: %s", headers["Authorization"]) if not data: data = None try: - if method == 'PUT': + if method == "PUT": if local_file: - fh = salt.utils.files.fopen(local_file, 'rb') # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + fh = salt.utils.files.fopen(local_file, "rb") + # pylint: enable=resource-leakage data = fh.read() # pylint: disable=resource-leakage - result = requests.request(method, - requesturl, - headers=headers, - data=data, - verify=verify_ssl, - stream=True, - timeout=300) - elif method == 'GET' and local_file and not return_bin: - result = requests.request(method, - requesturl, - headers=headers, - data=data, - verify=verify_ssl, - stream=True, - timeout=300) + result = requests.request( + method, + requesturl, + headers=headers, + data=data, + verify=verify_ssl, + stream=True, + timeout=300, + ) + elif method == "GET" and local_file and not return_bin: + result = requests.request( + method, + requesturl, + headers=headers, + data=data, + verify=verify_ssl, + stream=True, + timeout=300, + ) else: - result = requests.request(method, - requesturl, - headers=headers, - data=data, - verify=verify_ssl, - timeout=300) + result = requests.request( + method, + requesturl, + headers=headers, + data=data, + verify=verify_ssl, + timeout=300, + ) finally: if fh is not None: fh.close() @@ -187,71 +216,80 @@ def query(key, keyid, method='GET', params=None, headers=None, err_msg = None if result.status_code >= 400: # On error the S3 API response should contain error message - err_text = result.content or 'Unknown error' - log.debug(' Response content: %s', err_text) + err_text = result.content or "Unknown error" + log.debug(" Response content: %s", err_text) # Try to get err info from response xml try: err_data = xml.to_dict(ET.fromstring(err_text)) - err_code = err_data['Code'] - err_msg = err_data['Message'] + err_code = err_data["Code"] + err_msg = err_data["Message"] except (KeyError, ET.ParseError) as err: log.debug( - 'Failed to parse s3 err response. %s: %s', - type(err).__name__, err + "Failed to parse s3 err response. %s: %s", type(err).__name__, err ) - err_code = 'http-{0}'.format(result.status_code) + err_code = "http-{0}".format(result.status_code) err_msg = err_text - log.debug('S3 Response Status Code: %s', result.status_code) + log.debug("S3 Response Status Code: %s", result.status_code) - if method == 'PUT': + if method == "PUT": if result.status_code != 200: if local_file: raise CommandExecutionError( - 'Failed to upload from {0} to {1}. {2}: {3}'.format( - local_file, path, err_code, err_msg)) + "Failed to upload from {0} to {1}. {2}: {3}".format( + local_file, path, err_code, err_msg + ) + ) raise CommandExecutionError( - 'Failed to create bucket {0}. {1}: {2}'.format( - bucket, err_code, err_msg)) + "Failed to create bucket {0}. {1}: {2}".format( + bucket, err_code, err_msg + ) + ) if local_file: - log.debug('Uploaded from %s to %s', local_file, path) + log.debug("Uploaded from %s to %s", local_file, path) else: - log.debug('Created bucket %s', bucket) + log.debug("Created bucket %s", bucket) return - if method == 'DELETE': - if not six.text_type(result.status_code).startswith('2'): + if method == "DELETE": + if not six.text_type(result.status_code).startswith("2"): if path: raise CommandExecutionError( - 'Failed to delete {0} from bucket {1}. {2}: {3}'.format( - path, bucket, err_code, err_msg)) + "Failed to delete {0} from bucket {1}. {2}: {3}".format( + path, bucket, err_code, err_msg + ) + ) raise CommandExecutionError( - 'Failed to delete bucket {0}. {1}: {2}'.format( - bucket, err_code, err_msg)) + "Failed to delete bucket {0}. {1}: {2}".format( + bucket, err_code, err_msg + ) + ) if path: - log.debug('Deleted %s from bucket %s', path, bucket) + log.debug("Deleted %s from bucket %s", path, bucket) else: - log.debug('Deleted bucket %s', bucket) + log.debug("Deleted bucket %s", bucket) return # This can be used to save a binary object to disk - if local_file and method == 'GET': + if local_file and method == "GET": if result.status_code < 200 or result.status_code >= 300: raise CommandExecutionError( - 'Failed to get file. {0}: {1}'.format(err_code, err_msg)) + "Failed to get file. {0}: {1}".format(err_code, err_msg) + ) - log.debug('Saving to local file: %s', local_file) - with salt.utils.files.fopen(local_file, 'wb') as out: + log.debug("Saving to local file: %s", local_file) + with salt.utils.files.fopen(local_file, "wb") as out: for chunk in result.iter_content(chunk_size=chunk_size): out.write(chunk) - return 'Saved to local file: {0}'.format(local_file) + return "Saved to local file: {0}".format(local_file) if result.status_code < 200 or result.status_code >= 300: raise CommandExecutionError( - 'Failed s3 operation. {0}: {1}'.format(err_code, err_msg)) + "Failed s3 operation. {0}: {1}".format(err_code, err_msg) + ) # This can be used to return a binary object wholesale if return_bin: @@ -269,11 +307,11 @@ def query(key, keyid, method='GET', params=None, headers=None, else: if result.status_code != requests.codes.ok: return - ret = {'headers': []} + ret = {"headers": []} if full_headers: - ret['headers'] = dict(result.headers) + ret["headers"] = dict(result.headers) else: for header in result.headers: - ret['headers'].append(header.strip()) + ret["headers"].append(header.strip()) return ret diff --git a/salt/utils/saltclass.py b/salt/utils/saltclass.py index e1dfd8bb10f..0cb0b94c7e2 100644 --- a/salt/utils/saltclass.py +++ b/salt/utils/saltclass.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import re -import logging -from jinja2 import FileSystemLoader, Environment # Import Salt libs import salt.utils.path import salt.utils.yaml +from jinja2 import Environment, FileSystemLoader # Import 3rd-party libs from salt.ext import six @@ -19,13 +20,15 @@ log = logging.getLogger(__name__) # Renders jinja from a template file def render_jinja(_file, salt_data): j_env = Environment(loader=FileSystemLoader(os.path.dirname(_file))) - j_env.globals.update({ - '__opts__': salt_data['__opts__'], - '__salt__': salt_data['__salt__'], - '__grains__': salt_data['__grains__'], - '__pillar__': salt_data['__pillar__'], - 'minion_id': salt_data['minion_id'], - }) + j_env.globals.update( + { + "__opts__": salt_data["__opts__"], + "__salt__": salt_data["__salt__"], + "__grains__": salt_data["__grains__"], + "__pillar__": salt_data["__pillar__"], + "minion_id": salt_data["minion_id"], + } + ) j_render = j_env.get_template(os.path.basename(_file)).render() return j_render @@ -38,20 +41,19 @@ def render_yaml(_file, salt_data): # Returns a dict from a class yaml definition def get_class(_class, salt_data): l_files = [] - saltclass_path = salt_data['path'] + saltclass_path = salt_data["path"] - straight = os.path.join(saltclass_path, - 'classes', - '{0}.yml'.format(_class)) - sub_straight = os.path.join(saltclass_path, - 'classes', - '{0}.yml'.format(_class.replace('.', os.sep))) - sub_init = os.path.join(saltclass_path, - 'classes', - _class.replace('.', os.sep), - 'init.yml') + straight = os.path.join(saltclass_path, "classes", "{0}.yml".format(_class)) + sub_straight = os.path.join( + saltclass_path, "classes", "{0}.yml".format(_class.replace(".", os.sep)) + ) + sub_init = os.path.join( + saltclass_path, "classes", _class.replace(".", os.sep), "init.yml" + ) - for root, dirs, files in salt.utils.path.os_walk(os.path.join(saltclass_path, 'classes'), followlinks=True): + for root, dirs, files in salt.utils.path.os_walk( + os.path.join(saltclass_path, "classes"), followlinks=True + ): for l_file in files: l_files.append(os.path.join(root, l_file)) @@ -64,16 +66,16 @@ def get_class(_class, salt_data): if sub_init in l_files: return render_yaml(sub_init, salt_data) - log.warning('%s: Class definition not found', _class) + log.warning("%s: Class definition not found", _class) return {} # Return environment def get_env_from_dict(exp_dict_list): - environment = '' + environment = "" for s_class in exp_dict_list: - if 'environment' in s_class: - environment = s_class['environment'] + if "environment" in s_class: + environment = s_class["environment"] return environment @@ -85,7 +87,7 @@ def dict_merge(a, b, path=None): for key in b: if key in a: if isinstance(a[key], list) and isinstance(b[key], list): - if b[key][0] == '^': + if b[key][0] == "^": b[key].pop(0) a[key] = b[key] else: @@ -126,7 +128,7 @@ def dict_search_and_replace(d, old, new, expanded): # Retrieve original value from ${xx:yy:zz} to be expanded def find_value_to_expand(x, v): a = x - for i in v[2:-1].split(':'): + for i in v[2:-1].split(":"): if a is None: return v if i in a: @@ -138,15 +140,15 @@ def find_value_to_expand(x, v): # Look for regexes and expand them def find_and_process_re(_str, v, k, b, expanded): - vre = re.finditer(r'(^|.)\$\{.*?\}', _str) + vre = re.finditer(r"(^|.)\$\{.*?\}", _str) if vre: for re_v in vre: re_str = str(re_v.group()) - if re_str.startswith('\\'): - v_new = _str.replace(re_str, re_str.lstrip('\\')) + if re_str.startswith("\\"): + v_new = _str.replace(re_str, re_str.lstrip("\\")) b = dict_search_and_replace(b, _str, v_new, expanded) expanded.append(k) - elif not re_str.startswith('$'): + elif not re_str.startswith("$"): v_expanded = find_value_to_expand(b, re_str[1:]) v_new = _str.replace(re_str[1:], v_expanded) b = dict_search_and_replace(b, _str, v_new, expanded) @@ -187,14 +189,12 @@ def expand_variables(a, b, expanded, path=None): return b -def expand_classes_in_order(minion_dict, - salt_data, - seen_classes, - expanded_classes, - classes_to_expand): +def expand_classes_in_order( + minion_dict, salt_data, seen_classes, expanded_classes, classes_to_expand +): # Get classes to expand from minion dictionary - if not classes_to_expand and 'classes' in minion_dict: - classes_to_expand = minion_dict['classes'] + if not classes_to_expand and "classes" in minion_dict: + classes_to_expand = minion_dict["classes"] # Now loop on list to recursively expand them for klass in classes_to_expand: @@ -206,25 +206,29 @@ def expand_classes_in_order(minion_dict, expanded_classes[klass] = {} # Merge newly found pillars into existing ones - new_pillars = expanded_classes[klass].get('pillars', {}) + new_pillars = expanded_classes[klass].get("pillars", {}) if new_pillars: - dict_merge(salt_data['__pillar__'], new_pillars) + dict_merge(salt_data["__pillar__"], new_pillars) # Now replace class element in classes_to_expand by expansion - if expanded_classes[klass].get('classes'): + if expanded_classes[klass].get("classes"): l_id = classes_to_expand.index(klass) - classes_to_expand[l_id:l_id] = expanded_classes[klass]['classes'] - expand_classes_in_order(minion_dict, - salt_data, - seen_classes, - expanded_classes, - classes_to_expand) + classes_to_expand[l_id:l_id] = expanded_classes[klass]["classes"] + expand_classes_in_order( + minion_dict, + salt_data, + seen_classes, + expanded_classes, + classes_to_expand, + ) else: - expand_classes_in_order(minion_dict, - salt_data, - seen_classes, - expanded_classes, - classes_to_expand) + expand_classes_in_order( + minion_dict, + salt_data, + seen_classes, + expanded_classes, + classes_to_expand, + ) # We may have duplicates here and we want to remove them tmp = [] @@ -242,18 +246,21 @@ def expand_classes_in_order(minion_dict, ord_expanded_classes.append(expanded_classes[ord_klass]) # And be smart and sort out states list # Address the corner case where states is empty in a class definition - if 'states' in expanded_classes[ord_klass] and expanded_classes[ord_klass]['states'] is None: - expanded_classes[ord_klass]['states'] = {} + if ( + "states" in expanded_classes[ord_klass] + and expanded_classes[ord_klass]["states"] is None + ): + expanded_classes[ord_klass]["states"] = {} - if 'states' in expanded_classes[ord_klass]: - ord_expanded_states.extend(expanded_classes[ord_klass]['states']) + if "states" in expanded_classes[ord_klass]: + ord_expanded_states.extend(expanded_classes[ord_klass]["states"]) # Add our minion dict as final element but check if we have states to process - if 'states' in minion_dict and minion_dict['states'] is None: - minion_dict['states'] = [] + if "states" in minion_dict and minion_dict["states"] is None: + minion_dict["states"] = [] - if 'states' in minion_dict: - ord_expanded_states.extend(minion_dict['states']) + if "states" in minion_dict: + ord_expanded_states.extend(minion_dict["states"]) ord_expanded_classes.append(minion_dict) @@ -261,12 +268,14 @@ def expand_classes_in_order(minion_dict, def expanded_dict_from_minion(minion_id, salt_data): - _file = '' - saltclass_path = salt_data['path'] + _file = "" + saltclass_path = salt_data["path"] # Start - for root, dirs, files in salt.utils.path.os_walk(os.path.join(saltclass_path, 'nodes'), followlinks=True): + for root, dirs, files in salt.utils.path.os_walk( + os.path.join(saltclass_path, "nodes"), followlinks=True + ): for minion_file in files: - if minion_file == '{0}.yml'.format(minion_id): + if minion_file == "{0}.yml".format(minion_id): _file = os.path.join(root, minion_file) # Load the minion_id definition if existing, else an empty dict @@ -274,23 +283,23 @@ def expanded_dict_from_minion(minion_id, salt_data): if _file: node_dict[minion_id] = render_yaml(_file, salt_data) else: - log.warning('%s: Node definition not found', minion_id) + log.warning("%s: Node definition not found", minion_id) node_dict[minion_id] = {} # Merge newly found pillars into existing ones - dict_merge(salt_data['__pillar__'], node_dict[minion_id].get('pillars', {})) + dict_merge(salt_data["__pillar__"], node_dict[minion_id].get("pillars", {})) # Get 2 ordered lists: # expanded_classes: A list of all the dicts # classes_list: List of all the classes expanded_classes, classes_list, states_list = expand_classes_in_order( - node_dict[minion_id], - salt_data, [], {}, []) + node_dict[minion_id], salt_data, [], {}, [] + ) # Here merge the pillars together pillars_dict = {} for exp_dict in expanded_classes: - if 'pillars' in exp_dict: + if "pillars" in exp_dict: dict_merge(pillars_dict, exp_dict) return expanded_classes, pillars_dict, classes_list, states_list @@ -302,28 +311,30 @@ def get_pillars(minion_id, salt_data): # pillars_dict: dict containing merged pillars in order # classes_list: All classes processed in order # states_list: All states listed in order - (expanded_classes, - pillars_dict, - classes_list, - states_list) = expanded_dict_from_minion(minion_id, salt_data) + ( + expanded_classes, + pillars_dict, + classes_list, + states_list, + ) = expanded_dict_from_minion(minion_id, salt_data) # Retrieve environment environment = get_env_from_dict(expanded_classes) # Expand ${} variables in merged dict # pillars key shouldn't exist if we haven't found any minion_id ref - if 'pillars' in pillars_dict: - pillars_dict_expanded = expand_variables(pillars_dict['pillars'], {}, []) + if "pillars" in pillars_dict: + pillars_dict_expanded = expand_variables(pillars_dict["pillars"], {}, []) else: pillars_dict_expanded = expand_variables({}, {}, []) # Build the final pillars dict pillars_dict = {} - pillars_dict['__saltclass__'] = {} - pillars_dict['__saltclass__']['states'] = states_list - pillars_dict['__saltclass__']['classes'] = classes_list - pillars_dict['__saltclass__']['environment'] = environment - pillars_dict['__saltclass__']['nodename'] = minion_id + pillars_dict["__saltclass__"] = {} + pillars_dict["__saltclass__"]["states"] = states_list + pillars_dict["__saltclass__"]["classes"] = classes_list + pillars_dict["__saltclass__"]["environment"] = environment + pillars_dict["__saltclass__"]["nodename"] = minion_id pillars_dict.update(pillars_dict_expanded) return pillars_dict @@ -335,10 +346,12 @@ def get_tops(minion_id, salt_data): # pillars_dict: dict containing merged pillars in order # classes_list: All classes processed in order # states_list: All states listed in order - (expanded_classes, - pillars_dict, - classes_list, - states_list) = expanded_dict_from_minion(minion_id, salt_data) + ( + expanded_classes, + pillars_dict, + classes_list, + states_list, + ) = expanded_dict_from_minion(minion_id, salt_data) # Retrieve environment environment = get_env_from_dict(expanded_classes) diff --git a/salt/utils/sanitizers.py b/salt/utils/sanitizers.py index 826949d6425..6dab0e44873 100644 --- a/salt/utils/sanitizers.py +++ b/salt/utils/sanitizers.py @@ -16,25 +16,27 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import re -import os.path + import fnmatch +import os.path +import re + +import salt.utils.stringutils +from salt.exceptions import CommandExecutionError # Import Salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError -import salt.utils.stringutils class InputSanitizer(object): @staticmethod def trim(value): - ''' + """ Raise an exception if value is empty. Otherwise strip it down. :param value: :return: - ''' - value = (value or '').strip() + """ + value = (value or "").strip() if not value: raise CommandExecutionError("Empty value during sanitation") @@ -42,23 +44,25 @@ class InputSanitizer(object): @staticmethod def filename(value): - ''' + """ Remove everything that would affect paths in the filename :param value: :return: - ''' - return re.sub('[^a-zA-Z0-9.-_ ]', '', os.path.basename(InputSanitizer.trim(value))) + """ + return re.sub( + "[^a-zA-Z0-9.-_ ]", "", os.path.basename(InputSanitizer.trim(value)) + ) @staticmethod def hostname(value): - ''' + """ Clean value for RFC1123. :param value: :return: - ''' - return re.sub(r'[^a-zA-Z0-9.-]', '', InputSanitizer.trim(value)).strip('.') + """ + return re.sub(r"[^a-zA-Z0-9.-]", "", InputSanitizer.trim(value)).strip(".") id = hostname @@ -67,7 +71,7 @@ clean = InputSanitizer() def mask_args_value(data, mask): - ''' + """ Mask a line in the data, which matches "mask". This can be used for cases where values in your roster file may contain @@ -80,16 +84,20 @@ def mask_args_value(data, mask): :param mask: Mask that matches a single line :return: - ''' + """ if not mask: return data out = [] for line in data.split(os.linesep): - if fnmatch.fnmatch(line.strip(), mask) and ':' in line: - key, value = line.split(':', 1) - out.append('{}: {}'.format(salt.utils.stringutils.to_unicode(key.strip()), '** hidden **')) + if fnmatch.fnmatch(line.strip(), mask) and ":" in line: + key, value = line.split(":", 1) + out.append( + "{}: {}".format( + salt.utils.stringutils.to_unicode(key.strip()), "** hidden **" + ) + ) else: out.append(line) - return '\n'.join(out) + return "\n".join(out) diff --git a/salt/utils/schedule.py b/salt/utils/schedule.py index 474af442a1c..5b771293d81 100644 --- a/salt/utils/schedule.py +++ b/salt/utils/schedule.py @@ -1,32 +1,39 @@ # -*- coding: utf-8 -*- # See doc/topics/jobs/index.rst -''' +""" Scheduling routines are located here. To activate the scheduler make the ``schedule`` option available to the master or minion configurations (master config file or for the minion via config or pillar). Detailed tutorial about scheduling jobs can be found :ref:`here <scheduling-jobs>`. -''' +""" # Import python libs -from __future__ import absolute_import, with_statement, print_function, unicode_literals -import os -import sys -import time +from __future__ import absolute_import, print_function, unicode_literals, with_statement + import copy -import signal import datetime -import itertools -import threading -import logging import errno +import itertools +import logging +import os import random +import signal +import sys +import threading +import time import weakref # Import Salt libs import salt.config +import salt.defaults.exitcodes +import salt.exceptions +import salt.loader +import salt.minion +import salt.payload +import salt.syspaths import salt.utils.args import salt.utils.error import salt.utils.event @@ -39,24 +46,16 @@ import salt.utils.process import salt.utils.stringutils import salt.utils.user import salt.utils.yaml -import salt.loader -import salt.minion -import salt.payload -import salt.syspaths -import salt.exceptions -import salt.defaults.exitcodes -from salt.utils.odict import OrderedDict - -from salt.exceptions import ( - SaltInvocationError -) +from salt.exceptions import SaltInvocationError # Import 3rd-party libs from salt.ext import six +from salt.utils.odict import OrderedDict # pylint: disable=import-error try: import dateutil.parser as dateutil_parser + _WHEN_SUPPORTED = True _RANGE_SUPPORTED = True except ImportError: @@ -65,6 +64,7 @@ except ImportError: try: import croniter + _CRON_SUPPORTED = True except ImportError: _CRON_SUPPORTED = False @@ -74,63 +74,78 @@ log = logging.getLogger(__name__) class Schedule(object): - ''' + """ Create a Schedule object, pass in the opts and the functions dict to use - ''' + """ + instance = None - def __new__(cls, opts, functions, - returners=None, - intervals=None, - cleanup=None, - proxy=None, - standalone=False, - new_instance=False, - utils=None): - ''' + def __new__( + cls, + opts, + functions, + returners=None, + intervals=None, + cleanup=None, + proxy=None, + standalone=False, + new_instance=False, + utils=None, + ): + """ Only create one instance of Schedule - ''' + """ if cls.instance is None or new_instance is True: - log.debug('Initializing new Schedule') + log.debug("Initializing new Schedule") # we need to make a local variable for this, as we are going to store # it in a WeakValueDictionary-- which will remove the item if no one # references it-- this forces a reference while we return to the caller instance = object.__new__(cls) - instance.__singleton_init__(opts, functions, - returners=returners, - intervals=intervals, - cleanup=cleanup, - proxy=proxy, - standalone=standalone, - utils=utils) + instance.__singleton_init__( + opts, + functions, + returners=returners, + intervals=intervals, + cleanup=cleanup, + proxy=proxy, + standalone=standalone, + utils=utils, + ) if new_instance is True: return instance cls.instance = instance else: - log.debug('Re-using Schedule') + log.debug("Re-using Schedule") return cls.instance # has to remain empty for singletons, since __init__ will *always* be called - def __init__(self, opts, functions, - returners=None, - intervals=None, - cleanup=None, - proxy=None, - standalone=False, - new_instance=False, - utils=None): + def __init__( + self, + opts, + functions, + returners=None, + intervals=None, + cleanup=None, + proxy=None, + standalone=False, + new_instance=False, + utils=None, + ): pass # an init for the singleton instance to call - def __singleton_init__(self, opts, - functions, - returners=None, - intervals=None, - cleanup=None, - proxy=None, - standalone=False, - utils=None, - _subprocess_list=None): + def __singleton_init__( + self, + opts, + functions, + returners=None, + intervals=None, + cleanup=None, + proxy=None, + standalone=False, + utils=None, + _subprocess_list=None, + ): self.opts = opts self.proxy = proxy self.functions = functions @@ -145,12 +160,12 @@ class Schedule(object): else: self.intervals = {} if not self.standalone: - if hasattr(returners, '__getitem__'): + if hasattr(returners, "__getitem__"): self.returners = returners else: self.returners = returners.loader.gen_functions() - self.time_offset = self.functions.get('timezone.get_offset', lambda: '0000')() - self.schedule_returner = self.option('schedule_returner') + self.time_offset = self.functions.get("timezone.get_offset", lambda: "0000")() + self.schedule_returner = self.option("schedule_returner") # Keep track of the lowest loop interval needed in this variable self.loop_interval = six.MAXSIZE if not self.standalone: @@ -167,30 +182,29 @@ class Schedule(object): return self.opts, self.functions, self.returners, self.intervals, None def option(self, opt): - ''' + """ Return options merged from config and pillar - ''' - if 'config.merge' in self.functions: - return self.functions['config.merge'](opt, {}, omit_master=True) + """ + if "config.merge" in self.functions: + return self.functions["config.merge"](opt, {}, omit_master=True) return self.opts.get(opt, {}) - def _get_schedule(self, - include_opts=True, - include_pillar=True, - remove_hidden=False): - ''' + def _get_schedule( + self, include_opts=True, include_pillar=True, remove_hidden=False + ): + """ Return the schedule data structure - ''' + """ schedule = {} if include_pillar: - pillar_schedule = self.opts.get('pillar', {}).get('schedule', {}) + pillar_schedule = self.opts.get("pillar", {}).get("schedule", {}) if not isinstance(pillar_schedule, dict): - raise ValueError('Schedule must be of type dict.') + raise ValueError("Schedule must be of type dict.") schedule.update(pillar_schedule) if include_opts: - opts_schedule = self.opts.get('schedule', {}) + opts_schedule = self.opts.get("schedule", {}) if not isinstance(opts_schedule, dict): - raise ValueError('Schedule must be of type dict.') + raise ValueError("Schedule must be of type dict.") schedule.update(opts_schedule) if remove_hidden: @@ -198,14 +212,14 @@ class Schedule(object): for job in _schedule: if isinstance(_schedule[job], dict): for item in _schedule[job]: - if item.startswith('_'): + if item.startswith("_"): del schedule[job][item] return schedule def _check_max_running(self, func, data, opts, now): - ''' + """ Return the schedule data structure - ''' + """ # Check to see if there are other jobs with this # signature running. If there are more than maxrunning # jobs present then don't start another. @@ -214,91 +228,100 @@ class Schedule(object): # dict we treat it like it was there and is True # Check if we're able to run - if 'run' not in data or not data['run']: + if "run" not in data or not data["run"]: return data - if 'jid_include' not in data or data['jid_include']: + if "jid_include" not in data or data["jid_include"]: jobcount = 0 - if self.opts['__role'] == 'master': + if self.opts["__role"] == "master": current_jobs = salt.utils.master.get_running_jobs(self.opts) else: current_jobs = salt.utils.minion.running(self.opts) for job in current_jobs: - if 'schedule' in job: + if "schedule" in job: log.debug( - 'schedule.handle_func: Checking job against fun ' - '%s: %s', func, job + "schedule.handle_func: Checking job against fun " "%s: %s", + func, + job, ) - if data['name'] == job['schedule'] \ - and salt.utils.process.os_is_running(job['pid']): + if data["name"] == job[ + "schedule" + ] and salt.utils.process.os_is_running(job["pid"]): jobcount += 1 log.debug( - 'schedule.handle_func: Incrementing jobcount, ' - 'now %s, maxrunning is %s', - jobcount, data['maxrunning'] + "schedule.handle_func: Incrementing jobcount, " + "now %s, maxrunning is %s", + jobcount, + data["maxrunning"], ) - if jobcount >= data['maxrunning']: + if jobcount >= data["maxrunning"]: log.debug( - 'schedule.handle_func: The scheduled job ' - '%s was not started, %s already running', - data['name'], data['maxrunning'] + "schedule.handle_func: The scheduled job " + "%s was not started, %s already running", + data["name"], + data["maxrunning"], ) - data['_skip_reason'] = 'maxrunning' - data['_skipped'] = True - data['_skipped_time'] = now - data['run'] = False + data["_skip_reason"] = "maxrunning" + data["_skipped"] = True + data["_skipped_time"] = now + data["run"] = False return data return data def persist(self): - ''' + """ Persist the modified schedule into <<configdir>>/<<default_include>>/_schedule.conf - ''' - config_dir = self.opts.get('conf_dir', None) - if config_dir is None and 'conf_file' in self.opts: - config_dir = os.path.dirname(self.opts['conf_file']) + """ + config_dir = self.opts.get("conf_dir", None) + if config_dir is None and "conf_file" in self.opts: + config_dir = os.path.dirname(self.opts["conf_file"]) if config_dir is None: config_dir = salt.syspaths.CONFIG_DIR minion_d_dir = os.path.join( config_dir, - os.path.dirname(self.opts.get('default_include', - salt.config.DEFAULT_MINION_OPTS['default_include']))) + os.path.dirname( + self.opts.get( + "default_include", + salt.config.DEFAULT_MINION_OPTS["default_include"], + ) + ), + ) if not os.path.isdir(minion_d_dir): os.makedirs(minion_d_dir) - schedule_conf = os.path.join(minion_d_dir, '_schedule.conf') - log.debug('Persisting schedule') - schedule_data = self._get_schedule(include_pillar=False, - remove_hidden=True) + schedule_conf = os.path.join(minion_d_dir, "_schedule.conf") + log.debug("Persisting schedule") + schedule_data = self._get_schedule(include_pillar=False, remove_hidden=True) try: - with salt.utils.files.fopen(schedule_conf, 'wb+') as fp_: + with salt.utils.files.fopen(schedule_conf, "wb+") as fp_: fp_.write( salt.utils.stringutils.to_bytes( - salt.utils.yaml.safe_dump( - {'schedule': schedule_data} - ) + salt.utils.yaml.safe_dump({"schedule": schedule_data}) ) ) except (IOError, OSError): - log.error('Failed to persist the updated schedule', - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Failed to persist the updated schedule", + exc_info_on_loglevel=logging.DEBUG, + ) def delete_job(self, name, persist=True): - ''' + """ Deletes a job from the scheduler. Ignore jobs from pillar - ''' + """ # ensure job exists, then delete it - if name in self.opts['schedule']: - del self.opts['schedule'][name] + if name in self.opts["schedule"]: + del self.opts["schedule"][name] elif name in self._get_schedule(include_opts=False): log.warning("Cannot delete job %s, it's in the pillar!", name) # Fire the complete event back along with updated list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_delete_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "schedule": self._get_schedule()}, + tag="/salt/minion/minion_schedule_delete_complete", + ) # remove from self.intervals if name in self.intervals: @@ -308,32 +331,33 @@ class Schedule(object): self.persist() def reset(self): - ''' + """ Reset the scheduler to defaults - ''' + """ self.skip_function = None self.skip_during_range = None self.enabled = True self.splay = None - self.opts['schedule'] = {} + self.opts["schedule"] = {} def delete_job_prefix(self, name, persist=True): - ''' + """ Deletes a job from the scheduler. Ignores jobs from pillar - ''' + """ # ensure job exists, then delete it - for job in list(self.opts['schedule'].keys()): + for job in list(self.opts["schedule"].keys()): if job.startswith(name): - del self.opts['schedule'][job] + del self.opts["schedule"][job] for job in self._get_schedule(include_opts=False): if job.startswith(name): log.warning("Cannot delete job %s, it's in the pillar!", job) # Fire the complete event back along with updated list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_delete_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "schedule": self._get_schedule()}, + tag="/salt/minion/minion_schedule_delete_complete", + ) # remove from self.intervals for job in list(self.intervals.keys()): @@ -344,376 +368,403 @@ class Schedule(object): self.persist() def add_job(self, data, persist=True): - ''' + """ Adds a new job to the scheduler. The format is the same as required in the configuration file. See the docs on how YAML is interpreted into python data-structures to make sure, you pass correct dictionaries. - ''' + """ # we don't do any checking here besides making sure its a dict. # eval() already does for us and raises errors accordingly if not isinstance(data, dict): - raise ValueError('Scheduled jobs have to be of type dict.') + raise ValueError("Scheduled jobs have to be of type dict.") if not len(data) == 1: - raise ValueError('You can only schedule one new job at a time.') + raise ValueError("You can only schedule one new job at a time.") # if enabled is not included in the job, # assume job is enabled. for job in data: - if 'enabled' not in data[job]: - data[job]['enabled'] = True + if "enabled" not in data[job]: + data[job]["enabled"] = True new_job = next(six.iterkeys(data)) if new_job in self._get_schedule(include_opts=False): log.warning("Cannot update job %s, it's in the pillar!", new_job) - elif new_job in self.opts['schedule']: - log.info('Updating job settings for scheduled job: %s', new_job) - self.opts['schedule'].update(data) + elif new_job in self.opts["schedule"]: + log.info("Updating job settings for scheduled job: %s", new_job) + self.opts["schedule"].update(data) else: - log.info('Added new job %s to scheduler', new_job) - self.opts['schedule'].update(data) + log.info("Added new job %s to scheduler", new_job) + self.opts["schedule"].update(data) # Fire the complete event back along with updated list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_add_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "schedule": self._get_schedule()}, + tag="/salt/minion/minion_schedule_add_complete", + ) if persist: self.persist() def enable_job(self, name, persist=True): - ''' + """ Enable a job in the scheduler. Ignores jobs from pillar - ''' + """ # ensure job exists, then enable it - if name in self.opts['schedule']: - self.opts['schedule'][name]['enabled'] = True - log.info('Enabling job %s in scheduler', name) + if name in self.opts["schedule"]: + self.opts["schedule"][name]["enabled"] = True + log.info("Enabling job %s in scheduler", name) elif name in self._get_schedule(include_opts=False): log.warning("Cannot modify job %s, it's in the pillar!", name) # Fire the complete event back along with updated list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_enabled_job_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "schedule": self._get_schedule()}, + tag="/salt/minion/minion_schedule_enabled_job_complete", + ) if persist: self.persist() def disable_job(self, name, persist=True): - ''' + """ Disable a job in the scheduler. Ignores jobs from pillar - ''' + """ # ensure job exists, then disable it - if name in self.opts['schedule']: - self.opts['schedule'][name]['enabled'] = False - log.info('Disabling job %s in scheduler', name) + if name in self.opts["schedule"]: + self.opts["schedule"][name]["enabled"] = False + log.info("Disabling job %s in scheduler", name) elif name in self._get_schedule(include_opts=False): log.warning("Cannot modify job %s, it's in the pillar!", name) - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: # Fire the complete event back along with updated list of schedule - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_disabled_job_complete') + evt.fire_event( + {"complete": True, "schedule": self._get_schedule()}, + tag="/salt/minion/minion_schedule_disabled_job_complete", + ) if persist: self.persist() def modify_job(self, name, schedule, persist=True): - ''' + """ Modify a job in the scheduler. Ignores jobs from pillar - ''' + """ # ensure job exists, then replace it - if name in self.opts['schedule']: + if name in self.opts["schedule"]: self.delete_job(name, persist) elif name in self._get_schedule(include_opts=False): log.warning("Cannot modify job %s, it's in the pillar!", name) return - self.opts['schedule'][name] = schedule + self.opts["schedule"][name] = schedule if persist: self.persist() def run_job(self, name): - ''' + """ Run a schedule job now - ''' + """ data = self._get_schedule().get(name, {}) - if 'function' in data: - func = data['function'] - elif 'func' in data: - func = data['func'] - elif 'fun' in data: - func = data['fun'] + if "function" in data: + func = data["function"] + elif "func" in data: + func = data["func"] + elif "fun" in data: + func = data["fun"] else: func = None if func not in self.functions: - log.info( - 'Invalid function: %s in scheduled job %s.', - func, name - ) + log.info("Invalid function: %s in scheduled job %s.", func, name) - if 'name' not in data: - data['name'] = name + if "name" not in data: + data["name"] = name # Assume run should be True until we check max_running - if 'run' not in data: - data['run'] = True + if "run" not in data: + data["run"] = True if not self.standalone: - data = self._check_max_running(func, - data, - self.opts, - datetime.datetime.now()) + data = self._check_max_running( + func, data, self.opts, datetime.datetime.now() + ) # Grab run, assume True - if data.get('run'): - log.info('Running Job: %s', name) + if data.get("run"): + log.info("Running Job: %s", name) self._run_job(func, data) def enable_schedule(self, persist=True): - ''' + """ Enable the scheduler. - ''' - self.opts['schedule']['enabled'] = True + """ + self.opts["schedule"]["enabled"] = True # Fire the complete event back along with updated list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_enabled_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "schedule": self._get_schedule()}, + tag="/salt/minion/minion_schedule_enabled_complete", + ) if persist: self.persist() def disable_schedule(self, persist=True): - ''' + """ Disable the scheduler. - ''' - self.opts['schedule']['enabled'] = False + """ + self.opts["schedule"]["enabled"] = False # Fire the complete event back along with updated list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_disabled_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "schedule": self._get_schedule()}, + tag="/salt/minion/minion_schedule_disabled_complete", + ) if persist: self.persist() def reload(self, schedule): - ''' + """ Reload the schedule from saved schedule file. - ''' + """ # Remove all jobs from self.intervals self.intervals = {} - if 'schedule' in schedule: - schedule = schedule['schedule'] - self.opts.setdefault('schedule', {}).update(schedule) + if "schedule" in schedule: + schedule = schedule["schedule"] + self.opts.setdefault("schedule", {}).update(schedule) def list(self, where): - ''' + """ List the current schedule items - ''' - if where == 'pillar': + """ + if where == "pillar": schedule = self._get_schedule(include_opts=False) - elif where == 'opts': + elif where == "opts": schedule = self._get_schedule(include_pillar=False) else: schedule = self._get_schedule() # Fire the complete event back along with the list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, 'schedule': schedule}, - tag='/salt/minion/minion_schedule_list_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "schedule": schedule}, + tag="/salt/minion/minion_schedule_list_complete", + ) def save_schedule(self): - ''' + """ Save the current schedule - ''' + """ self.persist() # Fire the complete event back along with the list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True}, - tag='/salt/minion/minion_schedule_saved') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event({"complete": True}, tag="/salt/minion/minion_schedule_saved") def postpone_job(self, name, data): - ''' + """ Postpone a job in the scheduler. Ignores jobs from pillar - ''' - time = data['time'] - new_time = data['new_time'] - time_fmt = data.get('time_fmt', '%Y-%m-%dT%H:%M:%S') + """ + time = data["time"] + new_time = data["new_time"] + time_fmt = data.get("time_fmt", "%Y-%m-%dT%H:%M:%S") # ensure job exists, then disable it - if name in self.opts['schedule']: - if 'skip_explicit' not in self.opts['schedule'][name]: - self.opts['schedule'][name]['skip_explicit'] = [] - self.opts['schedule'][name]['skip_explicit'].append({'time': time, - 'time_fmt': time_fmt}) + if name in self.opts["schedule"]: + if "skip_explicit" not in self.opts["schedule"][name]: + self.opts["schedule"][name]["skip_explicit"] = [] + self.opts["schedule"][name]["skip_explicit"].append( + {"time": time, "time_fmt": time_fmt} + ) - if 'run_explicit' not in self.opts['schedule'][name]: - self.opts['schedule'][name]['run_explicit'] = [] - self.opts['schedule'][name]['run_explicit'].append({'time': new_time, - 'time_fmt': time_fmt}) + if "run_explicit" not in self.opts["schedule"][name]: + self.opts["schedule"][name]["run_explicit"] = [] + self.opts["schedule"][name]["run_explicit"].append( + {"time": new_time, "time_fmt": time_fmt} + ) elif name in self._get_schedule(include_opts=False): log.warning("Cannot modify job %s, it's in the pillar!", name) # Fire the complete event back along with updated list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_postpone_job_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "schedule": self._get_schedule()}, + tag="/salt/minion/minion_schedule_postpone_job_complete", + ) def skip_job(self, name, data): - ''' + """ Skip a job at a specific time in the scheduler. Ignores jobs from pillar - ''' - time = data['time'] - time_fmt = data.get('time_fmt', '%Y-%m-%dT%H:%M:%S') + """ + time = data["time"] + time_fmt = data.get("time_fmt", "%Y-%m-%dT%H:%M:%S") # ensure job exists, then disable it - if name in self.opts['schedule']: - if 'skip_explicit' not in self.opts['schedule'][name]: - self.opts['schedule'][name]['skip_explicit'] = [] - self.opts['schedule'][name]['skip_explicit'].append({'time': time, - 'time_fmt': time_fmt}) + if name in self.opts["schedule"]: + if "skip_explicit" not in self.opts["schedule"][name]: + self.opts["schedule"][name]["skip_explicit"] = [] + self.opts["schedule"][name]["skip_explicit"].append( + {"time": time, "time_fmt": time_fmt} + ) elif name in self._get_schedule(include_opts=False): log.warning("Cannot modify job %s, it's in the pillar!", name) # Fire the complete event back along with updated list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, - 'schedule': self._get_schedule()}, - tag='/salt/minion/minion_schedule_skip_job_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "schedule": self._get_schedule()}, + tag="/salt/minion/minion_schedule_skip_job_complete", + ) - def get_next_fire_time(self, name, fmt='%Y-%m-%dT%H:%M:%S'): - ''' + def get_next_fire_time(self, name, fmt="%Y-%m-%dT%H:%M:%S"): + """ Return the next fire time for the specified job - ''' + """ schedule = self._get_schedule() _next_fire_time = None if schedule: - _next_fire_time = schedule.get(name, {}).get('_next_fire_time', None) + _next_fire_time = schedule.get(name, {}).get("_next_fire_time", None) if _next_fire_time: _next_fire_time = _next_fire_time.strftime(fmt) # Fire the complete event back along with updated list of schedule - with salt.utils.event.get_event('minion', opts=self.opts, listen=False) as evt: - evt.fire_event({'complete': True, 'next_fire_time': _next_fire_time}, - tag='/salt/minion/minion_schedule_next_fire_time_complete') + with salt.utils.event.get_event("minion", opts=self.opts, listen=False) as evt: + evt.fire_event( + {"complete": True, "next_fire_time": _next_fire_time}, + tag="/salt/minion/minion_schedule_next_fire_time_complete", + ) def job_status(self, name): - ''' + """ Return the specified schedule item - ''' + """ schedule = self._get_schedule() return schedule.get(name, {}) def handle_func(self, multiprocessing_enabled, func, data): - ''' + """ Execute this method in a multiprocess or thread - ''' - if salt.utils.platform.is_windows() \ - or self.opts.get('transport') == 'zeromq': + """ + if salt.utils.platform.is_windows() or self.opts.get("transport") == "zeromq": # Since function references can't be pickled and pickling # is required when spawning new processes on Windows, regenerate # the functions and returners. # This also needed for ZeroMQ transport to reset all functions # context data that could keep paretns connections. ZeroMQ will # hang on polling parents connections from the child process. - if self.opts['__role'] == 'master': + if self.opts["__role"] == "master": self.functions = salt.loader.runner(self.opts, utils=self.utils) else: - self.functions = salt.loader.minion_mods(self.opts, proxy=self.proxy, utils=self.utils) - self.returners = salt.loader.returners(self.opts, self.functions, proxy=self.proxy) - ret = {'id': self.opts.get('id', 'master'), - 'fun': func, - 'fun_args': [], - 'schedule': data['name'], - 'jid': salt.utils.jid.gen_jid(self.opts)} + self.functions = salt.loader.minion_mods( + self.opts, proxy=self.proxy, utils=self.utils + ) + self.returners = salt.loader.returners( + self.opts, self.functions, proxy=self.proxy + ) + ret = { + "id": self.opts.get("id", "master"), + "fun": func, + "fun_args": [], + "schedule": data["name"], + "jid": salt.utils.jid.gen_jid(self.opts), + } - if 'metadata' in data: - if isinstance(data['metadata'], dict): - ret['metadata'] = data['metadata'] - ret['metadata']['_TOS'] = self.time_offset - ret['metadata']['_TS'] = time.ctime() - ret['metadata']['_TT'] = time.strftime('%Y %B %d %a %H %m', time.gmtime()) + if "metadata" in data: + if isinstance(data["metadata"], dict): + ret["metadata"] = data["metadata"] + ret["metadata"]["_TOS"] = self.time_offset + ret["metadata"]["_TS"] = time.ctime() + ret["metadata"]["_TT"] = time.strftime( + "%Y %B %d %a %H %m", time.gmtime() + ) else: - log.warning('schedule: The metadata parameter must be ' - 'specified as a dictionary. Ignoring.') + log.warning( + "schedule: The metadata parameter must be " + "specified as a dictionary. Ignoring." + ) if multiprocessing_enabled: # We just want to modify the process name if we're on a different process - salt.utils.process.appendproctitle('{0} {1}'.format(self.__class__.__name__, ret['jid'])) - data_returner = data.get('returner', None) + salt.utils.process.appendproctitle( + "{0} {1}".format(self.__class__.__name__, ret["jid"]) + ) + data_returner = data.get("returner", None) if not self.standalone: proc_fn = os.path.join( - salt.minion.get_proc_dir(self.opts['cachedir']), - ret['jid'] + salt.minion.get_proc_dir(self.opts["cachedir"]), ret["jid"] ) # TODO: Make it readable! Splt to funcs, remove nested try-except-finally sections. try: minion_blackout_violation = False - if self.opts.get('pillar', {}).get('minion_blackout', False): - whitelist = self.opts.get('pillar', {}).get('minion_blackout_whitelist', []) + if self.opts.get("pillar", {}).get("minion_blackout", False): + whitelist = self.opts.get("pillar", {}).get( + "minion_blackout_whitelist", [] + ) # this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist - if func != 'saltutil.refresh_pillar' and func not in whitelist: + if func != "saltutil.refresh_pillar" and func not in whitelist: minion_blackout_violation = True - elif self.opts.get('grains', {}).get('minion_blackout', False): - whitelist = self.opts.get('grains', {}).get('minion_blackout_whitelist', []) - if func != 'saltutil.refresh_pillar' and func not in whitelist: + elif self.opts.get("grains", {}).get("minion_blackout", False): + whitelist = self.opts.get("grains", {}).get( + "minion_blackout_whitelist", [] + ) + if func != "saltutil.refresh_pillar" and func not in whitelist: minion_blackout_violation = True if minion_blackout_violation: - raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' ' - 'to False in pillar or grains to resume operations. Only ' - 'saltutil.refresh_pillar allowed in blackout mode.') + raise SaltInvocationError( + "Minion in blackout mode. Set 'minion_blackout' " + "to False in pillar or grains to resume operations. Only " + "saltutil.refresh_pillar allowed in blackout mode." + ) - ret['pid'] = os.getpid() + ret["pid"] = os.getpid() if not self.standalone: - if 'jid_include' not in data or data['jid_include']: + if "jid_include" not in data or data["jid_include"]: log.debug( - 'schedule.handle_func: adding this job to the ' - 'jobcache with data %s', ret + "schedule.handle_func: adding this job to the " + "jobcache with data %s", + ret, ) # write this to /var/cache/salt/minion/proc - with salt.utils.files.fopen(proc_fn, 'w+b') as fp_: + with salt.utils.files.fopen(proc_fn, "w+b") as fp_: fp_.write(salt.payload.Serial(self.opts).dumps(ret)) args = tuple() - if 'args' in data: - args = data['args'] - ret['fun_args'].extend(data['args']) + if "args" in data: + args = data["args"] + ret["fun_args"].extend(data["args"]) kwargs = {} - if 'kwargs' in data: - kwargs = data['kwargs'] - ret['fun_args'].append(copy.deepcopy(kwargs)) + if "kwargs" in data: + kwargs = data["kwargs"] + ret["fun_args"].append(copy.deepcopy(kwargs)) if func not in self.functions: - ret['return'] = self.functions.missing_fun_string(func) + ret["return"] = self.functions.missing_fun_string(func) salt.utils.error.raise_error( - message=self.functions.missing_fun_string(func)) + message=self.functions.missing_fun_string(func) + ) # if the func support **kwargs, lets pack in the pub data we have # TODO: pack the *same* pub data as a minion? @@ -721,31 +772,31 @@ class Schedule(object): if argspec.keywords: # this function accepts **kwargs, pack in the publish data for key, val in six.iteritems(ret): - if key is not 'kwargs': - kwargs['__pub_{0}'.format(key)] = copy.deepcopy(val) + if key is not "kwargs": + kwargs["__pub_{0}".format(key)] = copy.deepcopy(val) # Only include these when running runner modules - if self.opts['__role'] == 'master': + if self.opts["__role"] == "master": jid = salt.utils.jid.gen_jid(self.opts) - tag = salt.utils.event.tagify(jid, prefix='salt/scheduler/') + tag = salt.utils.event.tagify(jid, prefix="salt/scheduler/") namespaced_event = salt.utils.event.NamespacedEvent( salt.utils.event.get_event( - self.opts['__role'], - self.opts['sock_dir'], - self.opts['transport'], + self.opts["__role"], + self.opts["sock_dir"], + self.opts["transport"], opts=self.opts, listen=False, ), tag, - print_func=None + print_func=None, ) func_globals = { - '__jid__': jid, - '__user__': salt.utils.user.get_user(), - '__tag__': tag, - '__jid_event__': weakref.proxy(namespaced_event), + "__jid__": jid, + "__user__": salt.utils.user.get_user(), + "__tag__": tag, + "__jid_event__": weakref.proxy(namespaced_event), } self_functions = copy.copy(self.functions) salt.utils.lazy.verify_fun(self_functions, func) @@ -755,31 +806,31 @@ class Schedule(object): completed_funcs = [] for mod_name in six.iterkeys(self_functions): - if '.' not in mod_name: + if "." not in mod_name: continue - mod, _ = mod_name.split('.', 1) + mod, _ = mod_name.split(".", 1) if mod in completed_funcs: continue completed_funcs.append(mod) for global_key, value in six.iteritems(func_globals): self.functions[mod_name].__globals__[global_key] = value - self.functions.pack['__context__']['retcode'] = 0 + self.functions.pack["__context__"]["retcode"] = 0 - ret['return'] = self.functions[func](*args, **kwargs) + ret["return"] = self.functions[func](*args, **kwargs) if not self.standalone: # runners do not provide retcode - if 'retcode' in self.functions.pack['__context__']: - ret['retcode'] = self.functions.pack['__context__']['retcode'] + if "retcode" in self.functions.pack["__context__"]: + ret["retcode"] = self.functions.pack["__context__"]["retcode"] - ret['success'] = True + ret["success"] = True if data_returner or self.schedule_returner: - if 'return_config' in data: - ret['ret_config'] = data['return_config'] - if 'return_kwargs' in data: - ret['ret_kwargs'] = data['return_kwargs'] + if "return_config" in data: + ret["ret_config"] = data["return_config"] + if "return_kwargs" in data: + ret["ret_kwargs"] = data["return_kwargs"] rets = [] for returner in [data_returner, self.schedule_returner]: if isinstance(returner, six.string_types): @@ -788,64 +839,68 @@ class Schedule(object): rets.extend(returner) # simple de-duplication with order retained for returner in OrderedDict.fromkeys(rets): - ret_str = '{0}.returner'.format(returner) + ret_str = "{0}.returner".format(returner) if ret_str in self.returners: self.returners[ret_str](ret) else: log.info( - 'Job %s using invalid returner: %s. Ignoring.', - func, returner + "Job %s using invalid returner: %s. Ignoring.", + func, + returner, ) except Exception: # pylint: disable=broad-except - log.exception('Unhandled exception running %s', ret['fun']) + log.exception("Unhandled exception running %s", ret["fun"]) # Although catch-all exception handlers are bad, the exception here # is to let the exception bubble up to the top of the thread context, # where the thread will die silently, which is worse. - if 'return' not in ret: - ret['return'] = "Unhandled exception running {0}".format(ret['fun']) - ret['success'] = False - ret['retcode'] = 254 + if "return" not in ret: + ret["return"] = "Unhandled exception running {0}".format(ret["fun"]) + ret["success"] = False + ret["retcode"] = 254 finally: # Only attempt to return data to the master if the scheduled job is running # on a master itself or a minion. - if '__role' in self.opts and self.opts['__role'] in ('master', 'minion'): + if "__role" in self.opts and self.opts["__role"] in ("master", "minion"): # The 'return_job' option is enabled by default even if not set - if 'return_job' in data and not data['return_job']: + if "return_job" in data and not data["return_job"]: pass else: # Send back to master so the job is included in the job list mret = ret.copy() # No returners defined, so we're only sending back to the master if not data_returner and not self.schedule_returner: - mret['jid'] = 'req' - if data.get('return_job') == 'nocache': + mret["jid"] = "req" + if data.get("return_job") == "nocache": # overwrite 'req' to signal to master that # this job shouldn't be stored - mret['jid'] = 'nocache' - load = {'cmd': '_return', 'id': self.opts['id']} + mret["jid"] = "nocache" + load = {"cmd": "_return", "id": self.opts["id"]} for key, value in six.iteritems(mret): load[key] = value - if '__role' in self.opts and self.opts['__role'] == 'minion': - event = salt.utils.event.get_event('minion', - opts=self.opts, - listen=False) - elif '__role' in self.opts and self.opts['__role'] == 'master': - event = salt.utils.event.get_master_event(self.opts, - self.opts['sock_dir']) + if "__role" in self.opts and self.opts["__role"] == "minion": + event = salt.utils.event.get_event( + "minion", opts=self.opts, listen=False + ) + elif "__role" in self.opts and self.opts["__role"] == "master": + event = salt.utils.event.get_master_event( + self.opts, self.opts["sock_dir"] + ) try: - event.fire_event(load, '__schedule_return') + event.fire_event(load, "__schedule_return") except Exception as exc: # pylint: disable=broad-except - log.exception('Unhandled exception firing __schedule_return event') + log.exception( + "Unhandled exception firing __schedule_return event" + ) finally: event.destroy() - if self.opts['__role'] == 'master': + if self.opts["__role"] == "master": namespaced_event.destroy() if not self.standalone: - log.debug('schedule.handle_func: Removing %s', proc_fn) + log.debug("schedule.handle_func: Removing %s", proc_fn) try: os.unlink(proc_fn) @@ -865,125 +920,138 @@ class Schedule(object): sys.exit(salt.defaults.exitcodes.EX_GENERIC) def eval(self, now=None): - ''' + """ Evaluate and execute the schedule :param datetime now: Override current time with a datetime object instance`` - ''' + """ - log.trace('==== evaluating schedule now %s =====', now) + log.trace("==== evaluating schedule now %s =====", now) - loop_interval = self.opts['loop_interval'] + loop_interval = self.opts["loop_interval"] if not isinstance(loop_interval, datetime.timedelta): loop_interval = datetime.timedelta(seconds=loop_interval) def _splay(splaytime): - ''' + """ Calculate splaytime - ''' + """ splay_ = None if isinstance(splaytime, dict): - if splaytime['end'] >= splaytime['start']: - splay_ = random.randint(splaytime['start'], - splaytime['end']) + if splaytime["end"] >= splaytime["start"]: + splay_ = random.randint(splaytime["start"], splaytime["end"]) else: - log.error('schedule.handle_func: Invalid Splay, ' - 'end must be larger than start. Ignoring splay.') + log.error( + "schedule.handle_func: Invalid Splay, " + "end must be larger than start. Ignoring splay." + ) else: splay_ = random.randint(1, splaytime) return splay_ def _handle_time_elements(data): - ''' + """ Handle schedule item with time elements seconds, minutes, hours, days - ''' - if '_seconds' not in data: - interval = int(data.get('seconds', 0)) - interval += int(data.get('minutes', 0)) * 60 - interval += int(data.get('hours', 0)) * 3600 - interval += int(data.get('days', 0)) * 86400 + """ + if "_seconds" not in data: + interval = int(data.get("seconds", 0)) + interval += int(data.get("minutes", 0)) * 60 + interval += int(data.get("hours", 0)) * 3600 + interval += int(data.get("days", 0)) * 86400 - data['_seconds'] = interval + data["_seconds"] = interval - if not data['_next_fire_time']: - data['_next_fire_time'] = now + datetime.timedelta(seconds=data['_seconds']) + if not data["_next_fire_time"]: + data["_next_fire_time"] = now + datetime.timedelta( + seconds=data["_seconds"] + ) if interval < self.loop_interval: self.loop_interval = interval - data['_next_scheduled_fire_time'] = now + datetime.timedelta(seconds=data['_seconds']) + data["_next_scheduled_fire_time"] = now + datetime.timedelta( + seconds=data["_seconds"] + ) def _handle_once(data, loop_interval): - ''' + """ Handle schedule item with once - ''' - if data['_next_fire_time']: - if data['_next_fire_time'] < now - loop_interval or \ - data['_next_fire_time'] > now and \ - not data['_splay']: - data['_continue'] = True + """ + if data["_next_fire_time"]: + if ( + data["_next_fire_time"] < now - loop_interval + or data["_next_fire_time"] > now + and not data["_splay"] + ): + data["_continue"] = True - if not data['_next_fire_time'] and \ - not data['_splay']: - once = data['once'] + if not data["_next_fire_time"] and not data["_splay"]: + once = data["once"] if not isinstance(once, datetime.datetime): - once_fmt = data.get('once_fmt', '%Y-%m-%dT%H:%M:%S') + once_fmt = data.get("once_fmt", "%Y-%m-%dT%H:%M:%S") try: - once = datetime.datetime.strptime(data['once'], - once_fmt) + once = datetime.datetime.strptime(data["once"], once_fmt) except (TypeError, ValueError): - data['_error'] = ('Date string could not ' - 'be parsed: {0}, {1}. ' - 'Ignoring job {2}.'.format( - data['once'], - once_fmt, - data['name'])) - log.error(data['_error']) + data["_error"] = ( + "Date string could not " + "be parsed: {0}, {1}. " + "Ignoring job {2}.".format( + data["once"], once_fmt, data["name"] + ) + ) + log.error(data["_error"]) return - data['_next_fire_time'] = once - data['_next_scheduled_fire_time'] = once + data["_next_fire_time"] = once + data["_next_scheduled_fire_time"] = once # If _next_fire_time is less than now, continue if once < now - loop_interval: - data['_continue'] = True + data["_continue"] = True def _handle_when(data, loop_interval): - ''' + """ Handle schedule item with when - ''' + """ if not _WHEN_SUPPORTED: - data['_error'] = ('Missing python-dateutil. ' - 'Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = "Missing python-dateutil. " "Ignoring job {0}.".format( + data["name"] + ) + log.error(data["_error"]) return - if not isinstance(data['when'], list): - _when_data = [data['when']] + if not isinstance(data["when"], list): + _when_data = [data["when"]] else: - _when_data = data['when'] + _when_data = data["when"] _when = [] for i in _when_data: - if ('pillar' in self.opts and 'whens' in self.opts['pillar'] and - i in self.opts['pillar']['whens']): - if not isinstance(self.opts['pillar']['whens'], - dict): - data['_error'] = ('Pillar item "whens" ' - 'must be a dict. ' - 'Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + if ( + "pillar" in self.opts + and "whens" in self.opts["pillar"] + and i in self.opts["pillar"]["whens"] + ): + if not isinstance(self.opts["pillar"]["whens"], dict): + data["_error"] = ( + 'Pillar item "whens" ' + "must be a dict. " + "Ignoring job {0}.".format(data["name"]) + ) + log.error(data["_error"]) return - when_ = self.opts['pillar']['whens'][i] - elif ('whens' in self.opts['grains'] and - i in self.opts['grains']['whens']): - if not isinstance(self.opts['grains']['whens'], - dict): - data['_error'] = ('Grain "whens" must be a dict. ' - 'Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + when_ = self.opts["pillar"]["whens"][i] + elif ( + "whens" in self.opts["grains"] and i in self.opts["grains"]["whens"] + ): + if not isinstance(self.opts["grains"]["whens"], dict): + data["_error"] = ( + 'Grain "whens" must be a dict. ' + "Ignoring job {0}.".format(data["name"]) + ) + log.error(data["_error"]) return - when_ = self.opts['grains']['whens'][i] + when_ = self.opts["grains"]["whens"][i] else: when_ = i @@ -991,15 +1059,17 @@ class Schedule(object): try: when_ = dateutil_parser.parse(when_) except ValueError: - data['_error'] = ('Invalid date string {0}. ' - 'Ignoring job {1}.'.format(i, data['name'])) - log.error(data['_error']) + data["_error"] = ( + "Invalid date string {0}. " + "Ignoring job {1}.".format(i, data["name"]) + ) + log.error(data["_error"]) return _when.append(when_) - if data['_splay']: - _when.append(data['_splay']) + if data["_splay"]: + _when.append(data["_splay"]) # Sort the list of "whens" from earlier to later schedules _when.sort() @@ -1017,75 +1087,84 @@ class Schedule(object): # last scheduled time in the past. when = _when[0] - if when < now - loop_interval and \ - not data.get('_run', False) and \ - not run and \ - not data['_splay']: - data['_next_fire_time'] = None - data['_continue'] = True + if ( + when < now - loop_interval + and not data.get("_run", False) + and not run + and not data["_splay"] + ): + data["_next_fire_time"] = None + data["_continue"] = True return - if '_run' not in data: + if "_run" not in data: # Prevent run of jobs from the past - data['_run'] = bool(when >= now - loop_interval) + data["_run"] = bool(when >= now - loop_interval) - if not data['_next_fire_time']: - data['_next_fire_time'] = when + if not data["_next_fire_time"]: + data["_next_fire_time"] = when - data['_next_scheduled_fire_time'] = when + data["_next_scheduled_fire_time"] = when - if data['_next_fire_time'] < when and \ - not run and \ - not data['_run']: - data['_next_fire_time'] = when - data['_run'] = True + if data["_next_fire_time"] < when and not run and not data["_run"]: + data["_next_fire_time"] = when + data["_run"] = True - elif not data.get('_run', False): - data['_next_fire_time'] = None - data['_continue'] = True + elif not data.get("_run", False): + data["_next_fire_time"] = None + data["_continue"] = True def _handle_cron(data, loop_interval): - ''' + """ Handle schedule item with cron - ''' + """ if not _CRON_SUPPORTED: - data['_error'] = ('Missing python-croniter. ' - 'Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = "Missing python-croniter. " "Ignoring job {0}.".format( + data["name"] + ) + log.error(data["_error"]) return - if data['_next_fire_time'] is None: + if data["_next_fire_time"] is None: # Get next time frame for a "cron" job if it has been never # executed before or already executed in the past. try: - data['_next_fire_time'] = croniter.croniter(data['cron'], now).get_next(datetime.datetime) - data['_next_scheduled_fire_time'] = croniter.croniter(data['cron'], now).get_next(datetime.datetime) + data["_next_fire_time"] = croniter.croniter( + data["cron"], now + ).get_next(datetime.datetime) + data["_next_scheduled_fire_time"] = croniter.croniter( + data["cron"], now + ).get_next(datetime.datetime) except (ValueError, KeyError): - data['_error'] = ('Invalid cron string. ' - 'Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = "Invalid cron string. " "Ignoring job {0}.".format( + data["name"] + ) + log.error(data["_error"]) return # If next job run is scheduled more than 1 minute ahead and # configured loop interval is longer than that, we should # shorten it to get our job executed closer to the beginning # of desired time. - interval = (now - data['_next_fire_time']).total_seconds() + interval = (now - data["_next_fire_time"]).total_seconds() if interval >= 60 and interval < self.loop_interval: self.loop_interval = interval def _handle_run_explicit(data, loop_interval): - ''' + """ Handle schedule item with run_explicit - ''' + """ _run_explicit = [] - for _run_time in data['run_explicit']: + for _run_time in data["run_explicit"]: if isinstance(_run_time, datetime.datetime): _run_explicit.append(_run_time) else: - _run_explicit.append(datetime.datetime.strptime(_run_time['time'], - _run_time['time_fmt'])) - data['run'] = False + _run_explicit.append( + datetime.datetime.strptime( + _run_time["time"], _run_time["time_fmt"] + ) + ) + data["run"] = False # Copy the list so we can loop through it for i in copy.deepcopy(_run_explicit): @@ -1095,22 +1174,25 @@ class Schedule(object): if _run_explicit: if _run_explicit[0] <= now < _run_explicit[0] + loop_interval: - data['run'] = True - data['_next_fire_time'] = _run_explicit[0] + data["run"] = True + data["_next_fire_time"] = _run_explicit[0] def _handle_skip_explicit(data, loop_interval): - ''' + """ Handle schedule item with skip_explicit - ''' - data['run'] = False + """ + data["run"] = False _skip_explicit = [] - for _skip_time in data['skip_explicit']: + for _skip_time in data["skip_explicit"]: if isinstance(_skip_time, datetime.datetime): _skip_explicit.append(_skip_time) else: - _skip_explicit.append(datetime.datetime.strptime(_skip_time['time'], - _skip_time['time_fmt'])) + _skip_explicit.append( + datetime.datetime.strptime( + _skip_time["time"], _skip_time["time_fmt"] + ) + ) # Copy the list so we can loop through it for i in copy.deepcopy(_skip_explicit): @@ -1120,220 +1202,231 @@ class Schedule(object): if _skip_explicit: if _skip_explicit[0] <= now <= (_skip_explicit[0] + loop_interval): if self.skip_function: - data['run'] = True - data['func'] = self.skip_function + data["run"] = True + data["func"] = self.skip_function else: - data['_skip_reason'] = 'skip_explicit' - data['_skipped_time'] = now - data['_skipped'] = True - data['run'] = False + data["_skip_reason"] = "skip_explicit" + data["_skipped_time"] = now + data["_skipped"] = True + data["run"] = False else: - data['run'] = True + data["run"] = True def _handle_skip_during_range(data, loop_interval): - ''' + """ Handle schedule item with skip_explicit - ''' + """ if not _RANGE_SUPPORTED: - data['_error'] = ('Missing python-dateutil. ' - 'Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = "Missing python-dateutil. " "Ignoring job {0}.".format( + data["name"] + ) + log.error(data["_error"]) return - if not isinstance(data['skip_during_range'], dict): - data['_error'] = ('schedule.handle_func: Invalid, range ' - 'must be specified as a dictionary. ' - 'Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + if not isinstance(data["skip_during_range"], dict): + data["_error"] = ( + "schedule.handle_func: Invalid, range " + "must be specified as a dictionary. " + "Ignoring job {0}.".format(data["name"]) + ) + log.error(data["_error"]) return - start = data['skip_during_range']['start'] - end = data['skip_during_range']['end'] + start = data["skip_during_range"]["start"] + end = data["skip_during_range"]["end"] if not isinstance(start, datetime.datetime): try: start = dateutil_parser.parse(start) except ValueError: - data['_error'] = ('Invalid date string for start in ' - 'skip_during_range. Ignoring ' - 'job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = ( + "Invalid date string for start in " + "skip_during_range. Ignoring " + "job {0}.".format(data["name"]) + ) + log.error(data["_error"]) return if not isinstance(end, datetime.datetime): try: end = dateutil_parser.parse(end) except ValueError: - data['_error'] = ('Invalid date string for end in ' - 'skip_during_range. Ignoring ' - 'job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = ( + "Invalid date string for end in " + "skip_during_range. Ignoring " + "job {0}.".format(data["name"]) + ) + log.error(data["_error"]) return # Check to see if we should run the job immediately # after the skip_during_range is over - if 'run_after_skip_range' in data and \ - data['run_after_skip_range']: - if 'run_explicit' not in data: - data['run_explicit'] = [] + if "run_after_skip_range" in data and data["run_after_skip_range"]: + if "run_explicit" not in data: + data["run_explicit"] = [] # Add a run_explicit for immediately after the # skip_during_range ends - _run_immediate = (end + loop_interval).strftime('%Y-%m-%dT%H:%M:%S') - if _run_immediate not in data['run_explicit']: - data['run_explicit'].append({'time': _run_immediate, - 'time_fmt': '%Y-%m-%dT%H:%M:%S'}) + _run_immediate = (end + loop_interval).strftime("%Y-%m-%dT%H:%M:%S") + if _run_immediate not in data["run_explicit"]: + data["run_explicit"].append( + {"time": _run_immediate, "time_fmt": "%Y-%m-%dT%H:%M:%S"} + ) if end > start: if start <= now <= end: if self.skip_function: - data['run'] = True - data['func'] = self.skip_function + data["run"] = True + data["func"] = self.skip_function else: - data['_skip_reason'] = 'in_skip_range' - data['_skipped_time'] = now - data['_skipped'] = True - data['run'] = False + data["_skip_reason"] = "in_skip_range" + data["_skipped_time"] = now + data["_skipped"] = True + data["run"] = False else: - data['run'] = True + data["run"] = True else: - data['_error'] = ('schedule.handle_func: Invalid ' - 'range, end must be larger than ' - 'start. Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = ( + "schedule.handle_func: Invalid " + "range, end must be larger than " + "start. Ignoring job {0}.".format(data["name"]) + ) + log.error(data["_error"]) def _handle_range(data): - ''' + """ Handle schedule item with skip_explicit - ''' + """ if not _RANGE_SUPPORTED: - data['_error'] = ('Missing python-dateutil. ' - 'Ignoring job {0}'.format(data['name'])) - log.error(data['_error']) + data["_error"] = "Missing python-dateutil. " "Ignoring job {0}".format( + data["name"] + ) + log.error(data["_error"]) return - if not isinstance(data['range'], dict): - data['_error'] = ('schedule.handle_func: Invalid, range ' - 'must be specified as a dictionary.' - 'Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + if not isinstance(data["range"], dict): + data["_error"] = ( + "schedule.handle_func: Invalid, range " + "must be specified as a dictionary." + "Ignoring job {0}.".format(data["name"]) + ) + log.error(data["_error"]) return - start = data['range']['start'] - end = data['range']['end'] + start = data["range"]["start"] + end = data["range"]["end"] if not isinstance(start, datetime.datetime): try: start = dateutil_parser.parse(start) except ValueError: - data['_error'] = ('Invalid date string for start. ' - 'Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = ( + "Invalid date string for start. " + "Ignoring job {0}.".format(data["name"]) + ) + log.error(data["_error"]) return if not isinstance(end, datetime.datetime): try: end = dateutil_parser.parse(end) except ValueError: - data['_error'] = ('Invalid date string for end.' - ' Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = ( + "Invalid date string for end." + " Ignoring job {0}.".format(data["name"]) + ) + log.error(data["_error"]) return if end > start: - if 'invert' in data['range'] and data['range']['invert']: + if "invert" in data["range"] and data["range"]["invert"]: if now <= start or now >= end: - data['run'] = True + data["run"] = True else: - data['_skip_reason'] = 'in_skip_range' - data['run'] = False + data["_skip_reason"] = "in_skip_range" + data["run"] = False else: if start <= now <= end: - data['run'] = True + data["run"] = True else: if self.skip_function: - data['run'] = True - data['func'] = self.skip_function + data["run"] = True + data["func"] = self.skip_function else: - data['_skip_reason'] = 'not_in_range' - data['run'] = False + data["_skip_reason"] = "not_in_range" + data["run"] = False else: - data['_error'] = ('schedule.handle_func: Invalid ' - 'range, end must be larger ' - 'than start. Ignoring job {0}.'.format(data['name'])) - log.error(data['_error']) + data["_error"] = ( + "schedule.handle_func: Invalid " + "range, end must be larger " + "than start. Ignoring job {0}.".format(data["name"]) + ) + log.error(data["_error"]) def _handle_after(data): - ''' + """ Handle schedule item with after - ''' + """ if not _WHEN_SUPPORTED: - data['_error'] = ('Missing python-dateutil. ' - 'Ignoring job {0}'.format(data['name'])) - log.error(data['_error']) + data["_error"] = "Missing python-dateutil. " "Ignoring job {0}".format( + data["name"] + ) + log.error(data["_error"]) return - after = data['after'] + after = data["after"] if not isinstance(after, datetime.datetime): after = dateutil_parser.parse(after) if after >= now: - log.debug( - 'After time has not passed skipping job: %s.', - data['name'] - ) - data['_skip_reason'] = 'after_not_passed' - data['_skipped_time'] = now - data['_skipped'] = True - data['run'] = False + log.debug("After time has not passed skipping job: %s.", data["name"]) + data["_skip_reason"] = "after_not_passed" + data["_skipped_time"] = now + data["_skipped"] = True + data["run"] = False else: - data['run'] = True + data["run"] = True def _handle_until(data): - ''' + """ Handle schedule item with until - ''' + """ if not _WHEN_SUPPORTED: - data['_error'] = ('Missing python-dateutil. ' - 'Ignoring job {0}'.format(data['name'])) - log.error(data['_error']) + data["_error"] = "Missing python-dateutil. " "Ignoring job {0}".format( + data["name"] + ) + log.error(data["_error"]) return - until = data['until'] + until = data["until"] if not isinstance(until, datetime.datetime): until = dateutil_parser.parse(until) if until <= now: - log.debug( - 'Until time has passed skipping job: %s.', - data['name'] - ) - data['_skip_reason'] = 'until_passed' - data['_skipped_time'] = now - data['_skipped'] = True - data['run'] = False + log.debug("Until time has passed skipping job: %s.", data["name"]) + data["_skip_reason"] = "until_passed" + data["_skipped_time"] = now + data["_skipped"] = True + data["run"] = False else: - data['run'] = True + data["run"] = True def _chop_ms(dt): - ''' + """ Remove the microseconds from a datetime object - ''' + """ return dt - datetime.timedelta(microseconds=dt.microsecond) schedule = self._get_schedule() if not isinstance(schedule, dict): - raise ValueError('Schedule must be of type dict.') - if 'skip_function' in schedule: - self.skip_function = schedule['skip_function'] - if 'skip_during_range' in schedule: - self.skip_during_range = schedule['skip_during_range'] - if 'enabled' in schedule: - self.enabled = schedule['enabled'] - if 'splay' in schedule: - self.splay = schedule['splay'] + raise ValueError("Schedule must be of type dict.") + if "skip_function" in schedule: + self.skip_function = schedule["skip_function"] + if "skip_during_range" in schedule: + self.skip_during_range = schedule["skip_during_range"] + if "enabled" in schedule: + self.enabled = schedule["enabled"] + if "splay" in schedule: + self.splay = schedule["splay"] - _hidden = ['enabled', - 'skip_function', - 'skip_during_range', - 'splay'] + _hidden = ["enabled", "skip_function", "skip_during_range", "splay"] for job, data in six.iteritems(schedule): # Skip anything that is a global setting @@ -1341,51 +1434,53 @@ class Schedule(object): continue # Clear these out between runs - for item in ['_continue', - '_error', - '_enabled', - '_skipped', - '_skip_reason', - '_skipped_time']: + for item in [ + "_continue", + "_error", + "_enabled", + "_skipped", + "_skip_reason", + "_skipped_time", + ]: if item in data: del data[item] run = False - if 'name' in data: - job_name = data['name'] + if "name" in data: + job_name = data["name"] else: - job_name = data['name'] = job + job_name = data["name"] = job if not isinstance(data, dict): log.error( 'Scheduled job "%s" should have a dict value, not %s', - job_name, type(data) + job_name, + type(data), ) continue - if 'function' in data: - func = data['function'] - elif 'func' in data: - func = data['func'] - elif 'fun' in data: - func = data['fun'] + if "function" in data: + func = data["function"] + elif "func" in data: + func = data["func"] + elif "fun" in data: + func = data["fun"] else: func = None if func not in self.functions: - log.info( - 'Invalid function: %s in scheduled job %s.', - func, job_name - ) + log.info("Invalid function: %s in scheduled job %s.", func, job_name) - if '_next_fire_time' not in data: - data['_next_fire_time'] = None + if "_next_fire_time" not in data: + data["_next_fire_time"] = None - if '_splay' not in data: - data['_splay'] = None + if "_splay" not in data: + data["_splay"] = None - if 'run_on_start' in data and \ - data['run_on_start'] and \ - '_run_on_start' not in data: - data['_run_on_start'] = True + if ( + "run_on_start" in data + and data["run_on_start"] + and "_run_on_start" not in data + ): + data["_run_on_start"] = True if not now: now = datetime.datetime.now() @@ -1394,8 +1489,8 @@ class Schedule(object): # combinations. schedule_keys = set(data.keys()) - time_elements = ('seconds', 'minutes', 'hours', 'days') - scheduling_elements = ('when', 'cron', 'once') + time_elements = ("seconds", "minutes", "hours", "days") + scheduling_elements = ("when", "cron", "once") invalid_sched_combos = [ set(i) for i in itertools.combinations(scheduling_elements, 2) @@ -1404,228 +1499,257 @@ class Schedule(object): if any(i <= schedule_keys for i in invalid_sched_combos): log.error( 'Unable to use "%s" options together. Ignoring.', - '", "'.join(scheduling_elements) + '", "'.join(scheduling_elements), ) continue invalid_time_combos = [] for item in scheduling_elements: all_items = itertools.chain([item], time_elements) - invalid_time_combos.append( - set(itertools.combinations(all_items, 2))) + invalid_time_combos.append(set(itertools.combinations(all_items, 2))) if any(set(x) <= schedule_keys for x in invalid_time_combos): log.error( 'Unable to use "%s" with "%s" options. Ignoring', '", "'.join(time_elements), - '", "'.join(scheduling_elements) + '", "'.join(scheduling_elements), ) continue - if 'run_explicit' in data: + if "run_explicit" in data: _handle_run_explicit(data, loop_interval) - run = data['run'] + run = data["run"] if True in [True for item in time_elements if item in data]: _handle_time_elements(data) - elif 'once' in data: + elif "once" in data: _handle_once(data, loop_interval) - elif 'when' in data: + elif "when" in data: _handle_when(data, loop_interval) - elif 'cron' in data: + elif "cron" in data: _handle_cron(data, loop_interval) else: continue # Something told us to continue, so we continue - if '_continue' in data and data['_continue']: + if "_continue" in data and data["_continue"]: continue # An error occurred so we bail out - if '_error' in data and data['_error']: + if "_error" in data and data["_error"]: continue - seconds = int((_chop_ms(data['_next_fire_time']) - _chop_ms(now)).total_seconds()) + seconds = int( + (_chop_ms(data["_next_fire_time"]) - _chop_ms(now)).total_seconds() + ) # If there is no job specific splay available, # grab the global which defaults to None. - if 'splay' not in data: - data['splay'] = self.splay + if "splay" not in data: + data["splay"] = self.splay - if 'splay' in data and data['splay']: + if "splay" in data and data["splay"]: # Got "splay" configured, make decision to run a job based on that - if not data['_splay']: + if not data["_splay"]: # Try to add "splay" time only if next job fire time is # still in the future. We should trigger job run # immediately otherwise. - splay = _splay(data['splay']) - if now < data['_next_fire_time'] + datetime.timedelta(seconds=splay): - log.debug('schedule.handle_func: Adding splay of ' - '%s seconds to next run.', splay) - data['_splay'] = data['_next_fire_time'] + datetime.timedelta(seconds=splay) - if 'when' in data: - data['_run'] = True + splay = _splay(data["splay"]) + if now < data["_next_fire_time"] + datetime.timedelta( + seconds=splay + ): + log.debug( + "schedule.handle_func: Adding splay of " + "%s seconds to next run.", + splay, + ) + data["_splay"] = data["_next_fire_time"] + datetime.timedelta( + seconds=splay + ) + if "when" in data: + data["_run"] = True else: run = True - if data['_splay']: + if data["_splay"]: # The "splay" configuration has been already processed, just use it - seconds = (data['_splay'] - now).total_seconds() - if 'when' in data: - data['_next_fire_time'] = data['_splay'] + seconds = (data["_splay"] - now).total_seconds() + if "when" in data: + data["_next_fire_time"] = data["_splay"] - if '_seconds' in data: + if "_seconds" in data: if seconds <= 0: run = True - elif 'when' in data and data['_run']: - if data['_next_fire_time'] <= now <= (data['_next_fire_time'] + loop_interval): - data['_run'] = False + elif "when" in data and data["_run"]: + if ( + data["_next_fire_time"] + <= now + <= (data["_next_fire_time"] + loop_interval) + ): + data["_run"] = False run = True - elif 'cron' in data: + elif "cron" in data: # Reset next scheduled time because it is in the past now, # and we should trigger the job run, then wait for the next one. if seconds <= 0: - data['_next_fire_time'] = None + data["_next_fire_time"] = None run = True - elif 'once' in data: - if data['_next_fire_time'] <= now <= (data['_next_fire_time'] + loop_interval): + elif "once" in data: + if ( + data["_next_fire_time"] + <= now + <= (data["_next_fire_time"] + loop_interval) + ): run = True elif seconds == 0: run = True - if '_run_on_start' in data and data['_run_on_start']: + if "_run_on_start" in data and data["_run_on_start"]: run = True - data['_run_on_start'] = False + data["_run_on_start"] = False elif run: - if 'range' in data: + if "range" in data: _handle_range(data) # An error occurred so we bail out - if '_error' in data and data['_error']: + if "_error" in data and data["_error"]: continue - run = data['run'] + run = data["run"] # Override the functiton if passed back - if 'func' in data: - func = data['func'] + if "func" in data: + func = data["func"] # If there is no job specific skip_during_range available, # grab the global which defaults to None. - if 'skip_during_range' not in data and self.skip_during_range: - data['skip_during_range'] = self.skip_during_range + if "skip_during_range" not in data and self.skip_during_range: + data["skip_during_range"] = self.skip_during_range - if 'skip_during_range' in data and data['skip_during_range']: + if "skip_during_range" in data and data["skip_during_range"]: _handle_skip_during_range(data, loop_interval) # An error occurred so we bail out - if '_error' in data and data['_error']: + if "_error" in data and data["_error"]: continue - run = data['run'] + run = data["run"] # Override the functiton if passed back - if 'func' in data: - func = data['func'] + if "func" in data: + func = data["func"] - if 'skip_explicit' in data: + if "skip_explicit" in data: _handle_skip_explicit(data, loop_interval) # An error occurred so we bail out - if '_error' in data and data['_error']: + if "_error" in data and data["_error"]: continue - run = data['run'] + run = data["run"] # Override the functiton if passed back - if 'func' in data: - func = data['func'] + if "func" in data: + func = data["func"] - if 'until' in data: + if "until" in data: _handle_until(data) # An error occurred so we bail out - if '_error' in data and data['_error']: + if "_error" in data and data["_error"]: continue - run = data['run'] + run = data["run"] - if 'after' in data: + if "after" in data: _handle_after(data) # An error occurred so we bail out - if '_error' in data and data['_error']: + if "_error" in data and data["_error"]: continue - run = data['run'] + run = data["run"] # If the job item has continue, then we set run to False # so the job does not run but we still get the important # information calculated, eg. _next_fire_time - if '_continue' in data and data['_continue']: + if "_continue" in data and data["_continue"]: run = False # If globally disabled or job # is diabled skip the job - if not self.enabled or not data.get('enabled', True): - log.trace('Job: %s is disabled', job_name) - data['_skip_reason'] = 'disabled' - data['_skipped_time'] = now - data['_skipped'] = True + if not self.enabled or not data.get("enabled", True): + log.trace("Job: %s is disabled", job_name) + data["_skip_reason"] = "disabled" + data["_skipped_time"] = now + data["_skipped"] = True run = False - miss_msg = '' + miss_msg = "" if seconds < 0: - miss_msg = ' (runtime missed ' \ - 'by {0} seconds)'.format(abs(seconds)) + miss_msg = " (runtime missed " "by {0} seconds)".format(abs(seconds)) try: if run: - if 'jid_include' not in data or data['jid_include']: - data['jid_include'] = True - log.debug('schedule: Job %s was scheduled with jid_include, ' - 'adding to cache (jid_include defaults to True)', - job_name) - if 'maxrunning' in data: - log.debug('schedule: Job %s was scheduled with a max ' - 'number of %s', job_name, data['maxrunning']) + if "jid_include" not in data or data["jid_include"]: + data["jid_include"] = True + log.debug( + "schedule: Job %s was scheduled with jid_include, " + "adding to cache (jid_include defaults to True)", + job_name, + ) + if "maxrunning" in data: + log.debug( + "schedule: Job %s was scheduled with a max " + "number of %s", + job_name, + data["maxrunning"], + ) else: - log.info('schedule: maxrunning parameter was not specified for ' - 'job %s, defaulting to 1.', job_name) - data['maxrunning'] = 1 + log.info( + "schedule: maxrunning parameter was not specified for " + "job %s, defaulting to 1.", + job_name, + ) + data["maxrunning"] = 1 if not self.standalone: - data['run'] = run - data = self._check_max_running(func, - data, - self.opts, - now) - run = data['run'] + data["run"] = run + data = self._check_max_running(func, data, self.opts, now) + run = data["run"] # Check run again, just in case _check_max_running # set run to False if run: - log.info('Running scheduled job: %s%s', job_name, miss_msg) + log.info("Running scheduled job: %s%s", job_name, miss_msg) self._run_job(func, data) finally: # Only set _last_run if the job ran if run: - data['_last_run'] = now - data['_splay'] = None - if '_seconds' in data: + data["_last_run"] = now + data["_splay"] = None + if "_seconds" in data: if self.standalone: - data['_next_fire_time'] = now + datetime.timedelta(seconds=data['_seconds']) - elif '_skipped' in data and data['_skipped']: - data['_next_fire_time'] = now + datetime.timedelta(seconds=data['_seconds']) + data["_next_fire_time"] = now + datetime.timedelta( + seconds=data["_seconds"] + ) + elif "_skipped" in data and data["_skipped"]: + data["_next_fire_time"] = now + datetime.timedelta( + seconds=data["_seconds"] + ) elif run: - data['_next_fire_time'] = now + datetime.timedelta(seconds=data['_seconds']) + data["_next_fire_time"] = now + datetime.timedelta( + seconds=data["_seconds"] + ) def _run_job(self, func, data): - job_dry_run = data.get('dry_run', False) + job_dry_run = data.get("dry_run", False) if job_dry_run: - log.debug('Job %s has \'dry_run\' set to True. Not running it.', data['name']) + log.debug("Job %s has 'dry_run' set to True. Not running it.", data["name"]) return - multiprocessing_enabled = self.opts.get('multiprocessing', True) - run_schedule_jobs_in_background = self.opts.get('run_schedule_jobs_in_background', True) + multiprocessing_enabled = self.opts.get("multiprocessing", True) + run_schedule_jobs_in_background = self.opts.get( + "run_schedule_jobs_in_background", True + ) if run_schedule_jobs_in_background is False: # Explicitly pass False for multiprocessing_enabled @@ -1651,16 +1775,21 @@ class Schedule(object): if multiprocessing_enabled: with salt.utils.process.default_signals(signal.SIGINT, signal.SIGTERM): - proc = thread_cls(target=self.handle_func, args=(multiprocessing_enabled, func, data)) + proc = thread_cls( + target=self.handle_func, + args=(multiprocessing_enabled, func, data), + ) # Reset current signals before starting the process in # order not to inherit the current signal handlers proc.start() - proc.name = '{}-Schedule-{}'.format(proc.name, data['name']) + proc.name = "{}-Schedule-{}".format(proc.name, data["name"]) self._subprocess_list.add(proc) else: - proc = thread_cls(target=self.handle_func, args=(multiprocessing_enabled, func, data)) + proc = thread_cls( + target=self.handle_func, args=(multiprocessing_enabled, func, data) + ) proc.start() - proc.name = '{}-Schedule-{}'.format(proc.name, data['name']) + proc.name = "{}-Schedule-{}".format(proc.name, data["name"]) self._subprocess_list.add(proc) finally: if multiprocessing_enabled and salt.utils.platform.is_windows(): @@ -1675,14 +1804,14 @@ class Schedule(object): def clean_proc_dir(opts): - ''' + """ Loop through jid files in the minion proc directory (default /var/cache/salt/minion/proc) and remove any that refer to processes that no longer exist - ''' + """ - for basefilename in os.listdir(salt.minion.get_proc_dir(opts['cachedir'])): - fn_ = os.path.join(salt.minion.get_proc_dir(opts['cachedir']), basefilename) - with salt.utils.files.fopen(fn_, 'rb') as fp_: + for basefilename in os.listdir(salt.minion.get_proc_dir(opts["cachedir"])): + fn_ = os.path.join(salt.minion.get_proc_dir(opts["cachedir"]), basefilename) + with salt.utils.files.fopen(fn_, "rb") as fp_: job = None try: job = salt.payload.Serial(opts).load(fp_) @@ -1697,14 +1826,14 @@ def clean_proc_dir(opts): except OSError: continue log.debug( - 'schedule.clean_proc_dir: checking job %s for process ' - 'existence', job + "schedule.clean_proc_dir: checking job %s for process " "existence", job ) - if job is not None and 'pid' in job: - if salt.utils.process.os_is_running(job['pid']): + if job is not None and "pid" in job: + if salt.utils.process.os_is_running(job["pid"]): log.debug( - 'schedule.clean_proc_dir: Cleaning proc dir, pid %s ' - 'still exists.', job['pid'] + "schedule.clean_proc_dir: Cleaning proc dir, pid %s " + "still exists.", + job["pid"], ) else: # Windows cannot delete an open file diff --git a/salt/utils/schema.py b/salt/utils/schema.py index 9372c617d18..e707f097165 100644 --- a/salt/utils/schema.py +++ b/salt/utils/schema.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) :codeauthor: Alexandru Bleotu (alexandru.bleotu@morganstanley.com) @@ -318,34 +318,38 @@ ], "additionalProperties": false } -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import sys -import inspect -import textwrap + import functools +import inspect +import sys +import textwrap # Import salt libs import salt.utils.args -#import salt.utils.yaml -from salt.utils.odict import OrderedDict # Import 3rd-party libs from salt.ext import six -BASE_SCHEMA_URL = 'https://non-existing.saltstack.com/schemas' +# import salt.utils.yaml +from salt.utils.odict import OrderedDict + +BASE_SCHEMA_URL = "https://non-existing.saltstack.com/schemas" RENDER_COMMENT_YAML_MAX_LINE_LENGTH = 80 class Prepareable(type): - ''' + """ Preserve attributes order for python 2.x - ''' + """ + # This code was taken from # https://github.com/aromanovich/jsl/blob/master/jsl/_compat/prepareable.py # which in turn was taken from https://gist.github.com/DasIch/5562625 with minor fixes if not six.PY3: + def __new__(mcs, name, bases, attributes): try: constructor = attributes["__new__"] @@ -361,11 +365,15 @@ class Prepareable(type): defining_frame = sys._getframe(1) for constant in reversed(defining_frame.f_code.co_consts): if inspect.iscode(constant) and constant.co_name == name: - def get_index(attribute_name, _names=constant.co_names): # pylint: disable=cell-var-from-loop + + def get_index( + attribute_name, _names=constant.co_names + ): # pylint: disable=cell-var-from-loop try: return _names.index(attribute_name) except ValueError: return 0 + break else: return constructor(mcs, name, bases, attributes) @@ -376,15 +384,16 @@ class Prepareable(type): for key, value in by_appearance: namespace[key] = value return constructor(mcs, name, bases, namespace) + attributes["__new__"] = functools.wraps(constructor)(preparing_constructor) return type.__new__(mcs, name, bases, attributes) class NullSentinel(object): - ''' + """ A class which instance represents a null value. Allows specifying fields with a default value of null. - ''' + """ def __bool__(self): return False @@ -393,15 +402,15 @@ class NullSentinel(object): Null = NullSentinel() -''' +""" A special value that can be used to set the default value of a field to null. -''' +""" # make sure nobody creates another Null value def _failing_new(*args, **kwargs): - raise TypeError('Can\'t create another NullSentinel instance') + raise TypeError("Can't create another NullSentinel instance") NullSentinel.__new__ = staticmethod(_failing_new) @@ -409,16 +418,15 @@ del _failing_new class SchemaMeta(six.with_metaclass(Prepareable, type)): - @classmethod def __prepare__(mcs, name, bases): return OrderedDict() def __new__(mcs, name, bases, attrs): # Mark the instance as a configuration document/section - attrs['__config__'] = True - attrs['__flatten__'] = False - attrs['__config_name__'] = None + attrs["__config__"] = True + attrs["__flatten__"] = False + attrs["__config_name__"] = None # Let's record the configuration items/sections items = {} @@ -426,39 +434,39 @@ class SchemaMeta(six.with_metaclass(Prepareable, type)): order = [] # items from parent classes for base in reversed(bases): - if hasattr(base, '_items'): + if hasattr(base, "_items"): items.update(base._items) - if hasattr(base, '_sections'): + if hasattr(base, "_sections"): sections.update(base._sections) - if hasattr(base, '_order'): + if hasattr(base, "_order"): order.extend(base._order) # Iterate through attrs to discover items/config sections for key, value in six.iteritems(attrs): entry_name = None - if not hasattr(value, '__item__') and not hasattr(value, '__config__'): + if not hasattr(value, "__item__") and not hasattr(value, "__config__"): continue - if hasattr(value, '__item__'): + if hasattr(value, "__item__"): # the value is an item instance - if hasattr(value, 'title') and value.title is None: + if hasattr(value, "title") and value.title is None: # It's an item instance without a title, make the title # its name value.title = key entry_name = value.__item_name__ or key items[entry_name] = value - if hasattr(value, '__config__'): + if hasattr(value, "__config__"): entry_name = value.__config_name__ or key sections[entry_name] = value order.append(entry_name) - attrs['_order'] = order - attrs['_items'] = items - attrs['_sections'] = sections + attrs["_order"] = order + attrs["_items"] = items + attrs["_sections"] = sections return type.__new__(mcs, name, bases, attrs) def __call__(cls, flatten=False, allow_additional_items=False, **kwargs): instance = object.__new__(cls) - instance.__config_name__ = kwargs.pop('name', None) + instance.__config_name__ = kwargs.pop("name", None) if flatten is True: # This configuration block is to be treated as a part of the # configuration for which it was defined as an attribute, not as @@ -474,30 +482,31 @@ class SchemaMeta(six.with_metaclass(Prepareable, type)): class BaseSchemaItemMeta(six.with_metaclass(Prepareable, type)): - ''' + """ Config item metaclass to "tag" the class as a configuration item - ''' + """ + @classmethod def __prepare__(mcs, name, bases): return OrderedDict() def __new__(mcs, name, bases, attrs): # Register the class as an item class - attrs['__item__'] = True - attrs['__item_name__'] = None + attrs["__item__"] = True + attrs["__item_name__"] = None # Instantiate an empty list to store the config item attribute names attributes = [] for base in reversed(bases): try: - base_attributes = getattr(base, '_attributes', []) + base_attributes = getattr(base, "_attributes", []) if base_attributes: attributes.extend(base_attributes) # Extend the attributes with the base argspec argument names # but skip "self" for argname in salt.utils.args.get_function_argspec(base.__init__).args: - if argname == 'self' or argname in attributes: + if argname == "self" or argname in attributes: continue - if argname == 'name': + if argname == "name": continue attributes.append(argname) except TypeError: @@ -505,7 +514,7 @@ class BaseSchemaItemMeta(six.with_metaclass(Prepareable, type)): # triggers a TypeError when we're trying to find out its # argspec continue - attrs['_attributes'] = attributes + attrs["_attributes"] = attributes return type.__new__(mcs, name, bases, attrs) def __call__(cls, *args, **kwargs): @@ -513,13 +522,13 @@ class BaseSchemaItemMeta(six.with_metaclass(Prepareable, type)): instance = object.__new__(cls) if args: raise RuntimeError( - 'Please pass all arguments as named arguments. Un-named ' - 'arguments are not supported' + "Please pass all arguments as named arguments. Un-named " + "arguments are not supported" ) for key in kwargs.copy(): # Store the kwarg keys as the instance attributes for the # serialization step - if key == 'name': + if key == "name": # This is the item name to override the class attribute name instance.__item_name__ = kwargs.pop(key) continue @@ -529,9 +538,12 @@ class BaseSchemaItemMeta(six.with_metaclass(Prepareable, type)): instance.__init__(*args, **kwargs) # Validate the instance after initialization for base in reversed(inspect.getmro(cls)): - validate_attributes = getattr(base, '__validate_attributes__', None) + validate_attributes = getattr(base, "__validate_attributes__", None) if validate_attributes: - if instance.__validate_attributes__.__func__.__code__ is not validate_attributes.__code__: + if ( + instance.__validate_attributes__.__func__.__code__ + is not validate_attributes.__code__ + ): # The method was overridden, run base.__validate_attributes__ function base.__validate_attributes__(instance) # Finally, run the instance __validate_attributes__ function @@ -541,9 +553,9 @@ class BaseSchemaItemMeta(six.with_metaclass(Prepareable, type)): class Schema(six.with_metaclass(SchemaMeta, object)): - ''' + """ Configuration definition class - ''' + """ # Define some class level attributes to make PyLint happier title = None @@ -558,21 +570,21 @@ class Schema(six.with_metaclass(SchemaMeta, object)): serialized = OrderedDict() if id_ is not None: # This is meant as a configuration section, sub json schema - serialized['id'] = '{0}/{1}.json#'.format(BASE_SCHEMA_URL, id_) + serialized["id"] = "{0}/{1}.json#".format(BASE_SCHEMA_URL, id_) else: # Main configuration block, json schema - serialized['$schema'] = 'http://json-schema.org/draft-04/schema#' + serialized["$schema"] = "http://json-schema.org/draft-04/schema#" if cls.title is not None: - serialized['title'] = cls.title + serialized["title"] = cls.title if cls.description is not None: if cls.description == cls.__doc__: - serialized['description'] = textwrap.dedent(cls.description).strip() + serialized["description"] = textwrap.dedent(cls.description).strip() else: - serialized['description'] = cls.description + serialized["description"] = cls.description required = [] ordering = [] - serialized['type'] = 'object' + serialized["type"] = "object" properties = OrderedDict() cls.after_items_update = [] for name in cls._order: # pylint: disable=E1133 @@ -580,16 +592,18 @@ class Schema(six.with_metaclass(SchemaMeta, object)): item_name = None if name in cls._sections: # pylint: disable=E1135 section = cls._sections[name] - serialized_section = section.serialize(None if section.__flatten__ is True else name) + serialized_section = section.serialize( + None if section.__flatten__ is True else name + ) if section.__flatten__ is True: # Flatten the configuration section into the parent # configuration - properties.update(serialized_section['properties']) - if 'x-ordering' in serialized_section: - ordering.extend(serialized_section['x-ordering']) - if 'required' in serialized_section: - required.extend(serialized_section['required']) - if hasattr(section, 'after_items_update'): + properties.update(serialized_section["properties"]) + if "x-ordering" in serialized_section: + ordering.extend(serialized_section["x-ordering"]) + if "required" in serialized_section: + required.extend(serialized_section["required"]) + if hasattr(section, "after_items_update"): cls.after_items_update.extend(section.after_items_update) skip_order = True else: @@ -621,7 +635,7 @@ class Schema(six.with_metaclass(SchemaMeta, object)): ordering.append(name) if properties: - serialized['properties'] = properties + serialized["properties"] = properties # Update the serialized object with any items to include after properties. # Do not overwrite properties already existing in the serialized dict. @@ -640,47 +654,47 @@ class Schema(six.with_metaclass(SchemaMeta, object)): if required: # Only include required if not empty - serialized['required'] = required + serialized["required"] = required if ordering: # Only include ordering if not empty - serialized['x-ordering'] = ordering - serialized['additionalProperties'] = cls.__allow_additional_items__ + serialized["x-ordering"] = ordering + serialized["additionalProperties"] = cls.__allow_additional_items__ return serialized @classmethod def defaults(cls): serialized = cls.serialize() defaults = {} - for name, details in serialized['properties'].items(): - if 'default' in details: - defaults[name] = details['default'] + for name, details in serialized["properties"].items(): + if "default" in details: + defaults[name] = details["default"] continue - if 'properties' in details: - for sname, sdetails in details['properties'].items(): - if 'default' in sdetails: - defaults.setdefault(name, {})[sname] = sdetails['default'] + if "properties" in details: + for sname, sdetails in details["properties"].items(): + if "default" in sdetails: + defaults.setdefault(name, {})[sname] = sdetails["default"] continue return defaults @classmethod def as_requirements_item(cls): serialized_schema = cls.serialize() - required = serialized_schema.get('required', []) - for name in serialized_schema['properties']: + required = serialized_schema.get("required", []) + for name in serialized_schema["properties"]: if name not in required: required.append(name) return RequirementsItem(requirements=required) - #@classmethod - #def render_as_rst(cls): + # @classmethod + # def render_as_rst(cls): # ''' # Render the configuration block as a restructured text string # ''' # # TODO: Implement RST rendering # raise NotImplementedError - #@classmethod - #def render_as_yaml(cls): + # @classmethod + # def render_as_yaml(cls): # ''' # Render the configuration block as a parseable YAML string including comments # ''' @@ -689,11 +703,11 @@ class Schema(six.with_metaclass(SchemaMeta, object)): class SchemaItem(six.with_metaclass(BaseSchemaItemMeta, object)): - ''' + """ Base configuration items class. All configurations must subclass it - ''' + """ # Define some class level attributes to make PyLint happier __type__ = None @@ -706,15 +720,15 @@ class SchemaItem(six.with_metaclass(BaseSchemaItemMeta, object)): required = False def __init__(self, required=None, **extra): - ''' + """ :param required: If the configuration item is required. Defaults to ``False``. - ''' + """ if required is not None: self.required = required self.extra = extra def __validate_attributes__(self): - ''' + """ Run any validation check you need the instance attributes. ATTENTION: @@ -722,18 +736,16 @@ class SchemaItem(six.with_metaclass(BaseSchemaItemMeta, object)): Don't call the parent class when overriding this method because it will just duplicate the executions. This class'es metaclass will take care of that. - ''' + """ if self.required not in (True, False): - raise RuntimeError( - '\'required\' can only be True/False' - ) + raise RuntimeError("'required' can only be True/False") def _get_argname_value(self, argname): - ''' + """ Return the argname value looking up on all possible attributes - ''' + """ # Let's see if there's a private function to get the value - argvalue = getattr(self, '__get_{0}__'.format(argname), None) + argvalue = getattr(self, "__get_{0}__".format(argname), None) if argvalue is not None and callable(argvalue): argvalue = argvalue() # pylint: disable=not-callable if argvalue is None: @@ -741,25 +753,25 @@ class SchemaItem(six.with_metaclass(BaseSchemaItemMeta, object)): argvalue = getattr(self, argname, None) if argvalue is None: # Let's see if it's defined as a private class variable - argvalue = getattr(self, '__{0}__'.format(argname), None) + argvalue = getattr(self, "__{0}__".format(argname), None) if argvalue is None: # Let's look for it in the extra dictionary argvalue = self.extra.get(argname, None) return argvalue def serialize(self): - ''' + """ Return a serializable form of the config instance - ''' + """ raise NotImplementedError class BaseSchemaItem(SchemaItem): - ''' + """ Base configuration items class. All configurations must subclass it - ''' + """ # Let's define description as a class attribute, this will allow a custom configuration # item to do something like: @@ -776,8 +788,16 @@ class BaseSchemaItem(SchemaItem): enum = None enumNames = None - def __init__(self, title=None, description=None, default=None, enum=None, enumNames=None, **kwargs): - ''' + def __init__( + self, + title=None, + description=None, + default=None, + enum=None, + enumNames=None, + **kwargs + ): + """ :param required: If the configuration item is required. Defaults to ``False``. :param title: @@ -789,7 +809,7 @@ class BaseSchemaItem(SchemaItem): to set the default value to null). :param enum: A list(list, tuple, set) of valid choices. - ''' + """ if title is not None: self.title = title if description is not None: @@ -806,31 +826,31 @@ class BaseSchemaItem(SchemaItem): if self.enum is not None: if not isinstance(self.enum, (list, tuple, set)): raise RuntimeError( - 'Only the \'list\', \'tuple\' and \'set\' python types can be used ' - 'to define \'enum\'' + "Only the 'list', 'tuple' and 'set' python types can be used " + "to define 'enum'" ) if not isinstance(self.enum, list): self.enum = list(self.enum) if self.enumNames is not None: if not isinstance(self.enumNames, (list, tuple, set)): raise RuntimeError( - 'Only the \'list\', \'tuple\' and \'set\' python types can be used ' - 'to define \'enumNames\'' + "Only the 'list', 'tuple' and 'set' python types can be used " + "to define 'enumNames'" ) if len(self.enum) != len(self.enumNames): raise RuntimeError( - 'The size of \'enumNames\' must match the size of \'enum\'' + "The size of 'enumNames' must match the size of 'enum'" ) if not isinstance(self.enumNames, list): self.enumNames = list(self.enumNames) def serialize(self): - ''' + """ Return a serializable form of the config instance - ''' - serialized = {'type': self.__type__} + """ + serialized = {"type": self.__type__} for argname in self._attributes: - if argname == 'required': + if argname == "required": # This is handled elsewhere continue argvalue = self._get_argname_value(argname) @@ -839,7 +859,10 @@ class BaseSchemaItem(SchemaItem): argvalue = None # None values are not meant to be included in the # serialization, since this is not None... - if self.__serialize_attr_aliases__ and argname in self.__serialize_attr_aliases__: + if ( + self.__serialize_attr_aliases__ + and argname in self.__serialize_attr_aliases__ + ): argname = self.__serialize_attr_aliases__[argname] serialized[argname] = argvalue return serialized @@ -850,14 +873,14 @@ class BaseSchemaItem(SchemaItem): return textwrap.dedent(self.description).strip() return self.description - #def render_as_rst(self, name): + # def render_as_rst(self, name): # ''' # Render the configuration item as a restructured text string # ''' # # TODO: Implement YAML rendering # raise NotImplementedError - #def render_as_yaml(self, name): + # def render_as_yaml(self, name): # ''' # Render the configuration item as a parseable YAML string including comments # ''' @@ -884,37 +907,36 @@ class BaseSchemaItem(SchemaItem): class NullItem(BaseSchemaItem): - __type__ = 'null' + __type__ = "null" class BooleanItem(BaseSchemaItem): - __type__ = 'boolean' + __type__ = "boolean" class StringItem(BaseSchemaItem): - ''' + """ A string configuration field - ''' + """ - __type__ = 'string' + __type__ = "string" - __serialize_attr_aliases__ = { - 'min_length': 'minLength', - 'max_length': 'maxLength' - } + __serialize_attr_aliases__ = {"min_length": "minLength", "max_length": "maxLength"} format = None pattern = None min_length = None max_length = None - def __init__(self, - format=None, # pylint: disable=redefined-builtin - pattern=None, - min_length=None, - max_length=None, - **kwargs): - ''' + def __init__( + self, + format=None, # pylint: disable=redefined-builtin + pattern=None, + min_length=None, + max_length=None, + **kwargs + ): + """ :param required: If the configuration item is required. Defaults to ``False``. :param title: @@ -934,7 +956,7 @@ class StringItem(BaseSchemaItem): The minimum length :param max_length: The maximum length - ''' + """ if format is not None: # pylint: disable=redefined-builtin self.format = format if pattern is not None: @@ -951,75 +973,82 @@ class StringItem(BaseSchemaItem): class EMailItem(StringItem): - ''' + """ An internet email address, see `RFC 5322, section 3.4.1`__. .. __: http://tools.ietf.org/html/rfc5322 - ''' - __format__ = 'email' + """ + + __format__ = "email" class IPv4Item(StringItem): - ''' + """ An IPv4 address configuration field, according to dotted-quad ABNF syntax as defined in `RFC 2673, section 3.2`__. .. __: http://tools.ietf.org/html/rfc2673 - ''' - __format__ = 'ipv4' + """ + + __format__ = "ipv4" class IPv6Item(StringItem): - ''' + """ An IPv6 address configuration field, as defined in `RFC 2373, section 2.2`__. .. __: http://tools.ietf.org/html/rfc2373 - ''' - __format__ = 'ipv6' + """ + + __format__ = "ipv6" class HostnameItem(StringItem): - ''' + """ An Internet host name configuration field, see `RFC 1034, section 3.1`__. .. __: http://tools.ietf.org/html/rfc1034 - ''' - __format__ = 'hostname' + """ + + __format__ = "hostname" class DateTimeItem(StringItem): - ''' + """ An ISO 8601 formatted date-time configuration field, as defined by `RFC 3339, section 5.6`__. .. __: http://tools.ietf.org/html/rfc3339 - ''' - __format__ = 'date-time' + """ + + __format__ = "date-time" class UriItem(StringItem): - ''' + """ A universal resource identifier (URI) configuration field, according to `RFC3986`__. .. __: http://tools.ietf.org/html/rfc3986 - ''' - __format__ = 'uri' + """ + + __format__ = "uri" class SecretItem(StringItem): - ''' + """ A string configuration field containing a secret, for example, passwords, API keys, etc - ''' - __format__ = 'secret' + """ + + __format__ = "secret" class NumberItem(BaseSchemaItem): - __type__ = 'number' + __type__ = "number" __serialize_attr_aliases__ = { - 'multiple_of': 'multipleOf', - 'exclusive_minimum': 'exclusiveMinimum', - 'exclusive_maximum': 'exclusiveMaximum', + "multiple_of": "multipleOf", + "exclusive_minimum": "exclusiveMinimum", + "exclusive_maximum": "exclusiveMaximum", } multiple_of = None @@ -1028,14 +1057,16 @@ class NumberItem(BaseSchemaItem): maximum = None exclusive_maximum = None - def __init__(self, - multiple_of=None, - minimum=None, - exclusive_minimum=None, - maximum=None, - exclusive_maximum=None, - **kwargs): - ''' + def __init__( + self, + multiple_of=None, + minimum=None, + exclusive_minimum=None, + maximum=None, + exclusive_maximum=None, + **kwargs + ): + """ :param required: If the configuration item is required. Defaults to ``False``. :param title: @@ -1057,7 +1088,7 @@ class NumberItem(BaseSchemaItem): The maximum allowed value :param exclusive_maximum: Whether a value is allowed to be exactly equal to the maximum - ''' + """ if multiple_of is not None: self.multiple_of = multiple_of if minimum is not None: @@ -1072,17 +1103,17 @@ class NumberItem(BaseSchemaItem): class IntegerItem(NumberItem): - __type__ = 'integer' + __type__ = "integer" class ArrayItem(BaseSchemaItem): - __type__ = 'array' + __type__ = "array" __serialize_attr_aliases__ = { - 'min_items': 'minItems', - 'max_items': 'maxItems', - 'unique_items': 'uniqueItems', - 'additional_items': 'additionalItems' + "min_items": "minItems", + "max_items": "maxItems", + "unique_items": "uniqueItems", + "additional_items": "additionalItems", } items = None @@ -1091,14 +1122,16 @@ class ArrayItem(BaseSchemaItem): unique_items = None additional_items = None - def __init__(self, - items=None, - min_items=None, - max_items=None, - unique_items=None, - additional_items=None, - **kwargs): - ''' + def __init__( + self, + items=None, + min_items=None, + max_items=None, + unique_items=None, + additional_items=None, + **kwargs + ): + """ :param required: If the configuration item is required. Defaults to ``False``. :param title: @@ -1126,7 +1159,7 @@ class ArrayItem(BaseSchemaItem): the number of fields in ``items``, then the additional items are described by the :class:`.BaseField` passed using this argument. :type additional_items: bool or :class:`.BaseSchemaItem` - ''' + """ if items is not None: self.items = items if min_items is not None: @@ -1141,23 +1174,21 @@ class ArrayItem(BaseSchemaItem): def __validate_attributes__(self): if not self.items and not self.additional_items: - raise RuntimeError( - 'One of items or additional_items must be passed.' - ) + raise RuntimeError("One of items or additional_items must be passed.") if self.items is not None: if isinstance(self.items, (list, tuple)): for item in self.items: if not isinstance(item, (Schema, SchemaItem)): raise RuntimeError( - 'All items passed in the item argument tuple/list must be ' - 'a subclass of Schema, SchemaItem or BaseSchemaItem, ' - 'not {0}'.format(type(item)) + "All items passed in the item argument tuple/list must be " + "a subclass of Schema, SchemaItem or BaseSchemaItem, " + "not {0}".format(type(item)) ) elif not isinstance(self.items, (Schema, SchemaItem)): raise RuntimeError( - 'The items argument passed must be a subclass of ' - 'Schema, SchemaItem or BaseSchemaItem, not ' - '{0}'.format(type(self.items)) + "The items argument passed must be a subclass of " + "Schema, SchemaItem or BaseSchemaItem, not " + "{0}".format(type(self.items)) ) def __get_items__(self): @@ -1174,13 +1205,13 @@ class ArrayItem(BaseSchemaItem): class DictItem(BaseSchemaItem): - __type__ = 'object' + __type__ = "object" __serialize_attr_aliases__ = { - 'min_properties': 'minProperties', - 'max_properties': 'maxProperties', - 'pattern_properties': 'patternProperties', - 'additional_properties': 'additionalProperties' + "min_properties": "minProperties", + "max_properties": "maxProperties", + "pattern_properties": "patternProperties", + "additional_properties": "additionalProperties", } properties = None @@ -1189,14 +1220,16 @@ class DictItem(BaseSchemaItem): min_properties = None max_properties = None - def __init__(self, - properties=None, - pattern_properties=None, - additional_properties=None, - min_properties=None, - max_properties=None, - **kwargs): - ''' + def __init__( + self, + properties=None, + pattern_properties=None, + additional_properties=None, + min_properties=None, + max_properties=None, + **kwargs + ): + """ :param required: If the configuration item is required. Defaults to ``False``. :type required: @@ -1230,7 +1263,7 @@ class DictItem(BaseSchemaItem): :param max_properties: A maximum number of properties :type max_properties: int - ''' + """ if properties is not None: self.properties = properties if pattern_properties is not None: @@ -1244,42 +1277,46 @@ class DictItem(BaseSchemaItem): super(DictItem, self).__init__(**kwargs) def __validate_attributes__(self): - if not self.properties and not self.pattern_properties and not self.additional_properties: + if ( + not self.properties + and not self.pattern_properties + and not self.additional_properties + ): raise RuntimeError( - 'One of properties, pattern_properties or additional_properties must be passed' + "One of properties, pattern_properties or additional_properties must be passed" ) if self.properties is not None: if not isinstance(self.properties, (Schema, dict)): raise RuntimeError( - 'The passed properties must be passed as a dict or ' - ' a Schema not \'{0}\''.format(type(self.properties)) + "The passed properties must be passed as a dict or " + " a Schema not '{0}'".format(type(self.properties)) ) if not isinstance(self.properties, Schema): for key, prop in self.properties.items(): if not isinstance(prop, (Schema, SchemaItem)): raise RuntimeError( - 'The passed property who\'s key is \'{0}\' must be of type ' - 'Schema, SchemaItem or BaseSchemaItem, not ' - '\'{1}\''.format(key, type(prop)) + "The passed property who's key is '{0}' must be of type " + "Schema, SchemaItem or BaseSchemaItem, not " + "'{1}'".format(key, type(prop)) ) if self.pattern_properties is not None: if not isinstance(self.pattern_properties, dict): raise RuntimeError( - 'The passed pattern_properties must be passed as a dict ' - 'not \'{0}\''.format(type(self.pattern_properties)) + "The passed pattern_properties must be passed as a dict " + "not '{0}'".format(type(self.pattern_properties)) ) for key, prop in self.pattern_properties.items(): if not isinstance(prop, (Schema, SchemaItem)): raise RuntimeError( - 'The passed pattern_property who\'s key is \'{0}\' must ' - 'be of type Schema, SchemaItem or BaseSchemaItem, ' - 'not \'{1}\''.format(key, type(prop)) + "The passed pattern_property who's key is '{0}' must " + "be of type Schema, SchemaItem or BaseSchemaItem, " + "not '{1}'".format(key, type(prop)) ) if self.additional_properties is not None: if not isinstance(self.additional_properties, (bool, Schema, SchemaItem)): raise RuntimeError( - 'The passed additional_properties must be of type bool, ' - 'Schema, SchemaItem or BaseSchemaItem, not \'{0}\''.format( + "The passed additional_properties must be of type bool, " + "Schema, SchemaItem or BaseSchemaItem, not '{0}'".format( type(self.pattern_properties) ) ) @@ -1288,7 +1325,7 @@ class DictItem(BaseSchemaItem): if self.properties is None: return if isinstance(self.properties, Schema): - return self.properties.serialize()['properties'] + return self.properties.serialize()["properties"] properties = OrderedDict() for key, prop in self.properties.items(): properties[key] = prop.serialize() @@ -1319,19 +1356,19 @@ class DictItem(BaseSchemaItem): if self.properties is not None: if isinstance(self.properties, Schema): serialized = self.properties.serialize() - if 'required' in serialized: - required.extend(serialized['required']) + if "required" in serialized: + required.extend(serialized["required"]) else: for key, prop in self.properties.items(): if prop.required: required.append(key) if required: - result['required'] = required + result["required"] = required return result class RequirementsItem(SchemaItem): - __type__ = 'object' + __type__ = "object" requirements = None @@ -1342,13 +1379,11 @@ class RequirementsItem(SchemaItem): def __validate_attributes__(self): if self.requirements is None: - raise RuntimeError( - 'The passed requirements must not be empty' - ) + raise RuntimeError("The passed requirements must not be empty") if not isinstance(self.requirements, (SchemaItem, list, tuple, set)): raise RuntimeError( - 'The passed requirements must be passed as a list, tuple, ' - 'set SchemaItem or BaseSchemaItem, not \'{0}\''.format(self.requirements) + "The passed requirements must be passed as a list, tuple, " + "set SchemaItem or BaseSchemaItem, not '{0}'".format(self.requirements) ) if not isinstance(self.requirements, SchemaItem): @@ -1358,8 +1393,8 @@ class RequirementsItem(SchemaItem): for idx, item in enumerate(self.requirements): if not isinstance(item, (six.string_types, SchemaItem)): raise RuntimeError( - 'The passed requirement at the {0} index must be of type ' - 'str or SchemaItem, not \'{1}\''.format(idx, type(item)) + "The passed requirement at the {0} index must be of type " + "str or SchemaItem, not '{1}'".format(idx, type(item)) ) def serialize(self): @@ -1372,12 +1407,12 @@ class RequirementsItem(SchemaItem): requirements.append(requirement.serialize()) continue requirements.append(requirement) - return {'required': requirements} + return {"required": requirements} class OneOfItem(SchemaItem): - __type__ = 'oneOf' + __type__ = "oneOf" items = None @@ -1388,20 +1423,18 @@ class OneOfItem(SchemaItem): def __validate_attributes__(self): if not self.items: - raise RuntimeError( - 'The passed items must not be empty' - ) + raise RuntimeError("The passed items must not be empty") if not isinstance(self.items, (list, tuple)): raise RuntimeError( - 'The passed items must be passed as a list/tuple not ' - '\'{0}\''.format(type(self.items)) + "The passed items must be passed as a list/tuple not " + "'{0}'".format(type(self.items)) ) for idx, item in enumerate(self.items): if not isinstance(item, (Schema, SchemaItem)): raise RuntimeError( - 'The passed item at the {0} index must be of type ' - 'Schema, SchemaItem or BaseSchemaItem, not ' - '\'{1}\''.format(idx, type(item)) + "The passed item at the {0} index must be of type " + "Schema, SchemaItem or BaseSchemaItem, not " + "'{1}'".format(idx, type(item)) ) if not isinstance(self.items, list): self.items = list(self.items) @@ -1416,17 +1449,17 @@ class OneOfItem(SchemaItem): class AnyOfItem(OneOfItem): - __type__ = 'anyOf' + __type__ = "anyOf" class AllOfItem(OneOfItem): - __type__ = 'allOf' + __type__ = "allOf" class NotItem(SchemaItem): - __type__ = 'not' + __type__ = "not" item = None @@ -1437,13 +1470,11 @@ class NotItem(SchemaItem): def __validate_attributes__(self): if not self.item: - raise RuntimeError( - 'An item must be passed' - ) + raise RuntimeError("An item must be passed") if not isinstance(self.item, (Schema, SchemaItem)): raise RuntimeError( - 'The passed item be of type Schema, SchemaItem or ' - 'BaseSchemaItem, not \'{0}\''.format(type(self.item)) + "The passed item be of type Schema, SchemaItem or " + "BaseSchemaItem, not '{0}'".format(type(self.item)) ) def serialize(self): @@ -1454,15 +1485,17 @@ class NotItem(SchemaItem): class PortItem(IntegerItem): minimum = 0 # yes, 0 is a valid port number maximum = 65535 + + # <---- Custom Preconfigured Configs --------------------------------------------------------------------------------- class ComplexSchemaItem(BaseSchemaItem): - ''' + """ .. versionadded:: 2016.11.0 Complex Schema Item - ''' + """ # This attribute is populated by the metaclass, but pylint fails to see it # and assumes it's not an iterable @@ -1471,24 +1504,27 @@ class ComplexSchemaItem(BaseSchemaItem): def __init__(self, definition_name=None, required=None): super(ComplexSchemaItem, self).__init__(required=required) - self.__type__ = 'object' - self._definition_name = definition_name if definition_name else \ - self.__class__.__name__ + self.__type__ = "object" + self._definition_name = ( + definition_name if definition_name else self.__class__.__name__ + ) # Schema attributes might have been added as class attributes so we # and they must be added to the _attributes attr self._add_missing_schema_attributes() def _add_missing_schema_attributes(self): - ''' + """ Adds any missed schema attributes to the _attributes list The attributes can be class attributes and they won't be included in the _attributes list automatically - ''' - for attr in [attr for attr in dir(self) if not attr.startswith('__')]: + """ + for attr in [attr for attr in dir(self) if not attr.startswith("__")]: attr_val = getattr(self, attr) - if isinstance(getattr(self, attr), SchemaItem) and \ - attr not in self._attributes: + if ( + isinstance(getattr(self, attr), SchemaItem) + and attr not in self._attributes + ): self._attributes.append(attr) @@ -1497,19 +1533,19 @@ class ComplexSchemaItem(BaseSchemaItem): return self._definition_name def serialize(self): - ''' + """ The serialization of the complex item is a pointer to the item definition - ''' - return {'$ref': '#/definitions/{0}'.format(self.definition_name)} + """ + return {"$ref": "#/definitions/{0}".format(self.definition_name)} def get_definition(self): - '''Returns the definition of the complex item''' + """Returns the definition of the complex item""" serialized = super(ComplexSchemaItem, self).serialize() # Adjust entries in the serialization - del serialized['definition_name'] - serialized['title'] = self.definition_name + del serialized["definition_name"] + serialized["title"] = self.definition_name properties = {} required_attr_names = [] @@ -1520,26 +1556,29 @@ class ComplexSchemaItem(BaseSchemaItem): # Remove the attribute entry added by the base serialization del serialized[attr_name] properties[attr_name] = attr.serialize() - properties[attr_name]['type'] = attr.__type__ + properties[attr_name]["type"] = attr.__type__ if attr.required: required_attr_names.append(attr_name) - if serialized.get('properties') is None: - serialized['properties'] = {} - serialized['properties'].update(properties) + if serialized.get("properties") is None: + serialized["properties"] = {} + serialized["properties"].update(properties) # Assign the required array if required_attr_names: - serialized['required'] = required_attr_names + serialized["required"] = required_attr_names return serialized def get_complex_attrs(self): - '''Returns a dictionary of the complex attributes''' - return [getattr(self, attr_name) for attr_name in self._attributes if - isinstance(getattr(self, attr_name), ComplexSchemaItem)] + """Returns a dictionary of the complex attributes""" + return [ + getattr(self, attr_name) + for attr_name in self._attributes + if isinstance(getattr(self, attr_name), ComplexSchemaItem) + ] class DefinitionsSchema(Schema): - ''' + """ .. versionadded:: 2016.11.0 JSON schema class that supports ComplexSchemaItem objects by adding @@ -1547,7 +1586,7 @@ class DefinitionsSchema(Schema): All references to ComplexSchemaItems are built using schema inline dereferencing. - ''' + """ @classmethod def serialize(cls, id_=None): @@ -1576,15 +1615,15 @@ class DefinitionsSchema(Schema): elif isinstance(item, DictItem): if item.properties: aux_items.extend(item.properties.values()) - if item.additional_properties and \ - isinstance(item.additional_properties, SchemaItem): + if item.additional_properties and isinstance( + item.additional_properties, SchemaItem + ): aux_items.append(item.additional_properties) definitions = OrderedDict() for config in complex_items: if isinstance(config, ComplexSchemaItem): - definitions[config.definition_name] = \ - config.get_definition() - serialized['definitions'] = definitions + definitions[config.definition_name] = config.get_definition() + serialized["definitions"] = definitions return serialized diff --git a/salt/utils/sdb.py b/salt/utils/sdb.py index 98c13ee142f..0b762cc0236 100644 --- a/salt/utils/sdb.py +++ b/salt/utils/sdb.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Basic functions for accessing the SDB interface For configuration options, see the docs for specific sdb modules. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -17,108 +17,110 @@ from salt.ext.six.moves import range def sdb_get(uri, opts, utils=None): - ''' + """ Get a value from a db, using a uri in the form of ``sdb://<profile>/<key>``. If the uri provided does not start with ``sdb://``, then it will be returned as-is. - ''' - if not isinstance(uri, string_types) or not uri.startswith('sdb://'): + """ + if not isinstance(uri, string_types) or not uri.startswith("sdb://"): return uri if utils is None: utils = salt.loader.utils(opts) - sdlen = len('sdb://') - indx = uri.find('/', sdlen) + sdlen = len("sdb://") + indx = uri.find("/", sdlen) - if (indx == -1) or len(uri[(indx+1):]) == 0: + if (indx == -1) or len(uri[(indx + 1) :]) == 0: return uri profile = opts.get(uri[sdlen:indx], {}) if not profile: - profile = opts.get('pillar', {}).get(uri[sdlen:indx], {}) - if 'driver' not in profile: + profile = opts.get("pillar", {}).get(uri[sdlen:indx], {}) + if "driver" not in profile: return uri - fun = '{0}.get'.format(profile['driver']) - query = uri[indx+1:] + fun = "{0}.get".format(profile["driver"]) + query = uri[indx + 1 :] loaded_db = salt.loader.sdb(opts, fun, utils=utils) return loaded_db[fun](query, profile=profile) def sdb_set(uri, value, opts, utils=None): - ''' + """ Set a value in a db, using a uri in the form of ``sdb://<profile>/<key>``. If the uri provided does not start with ``sdb://`` or the value is not successfully set, return ``False``. - ''' - if not isinstance(uri, string_types) or not uri.startswith('sdb://'): + """ + if not isinstance(uri, string_types) or not uri.startswith("sdb://"): return False if utils is None: utils = salt.loader.utils(opts) - sdlen = len('sdb://') - indx = uri.find('/', sdlen) + sdlen = len("sdb://") + indx = uri.find("/", sdlen) - if (indx == -1) or len(uri[(indx+1):]) == 0: + if (indx == -1) or len(uri[(indx + 1) :]) == 0: return False profile = opts.get(uri[sdlen:indx], {}) if not profile: - profile = opts.get('pillar', {}).get(uri[sdlen:indx], {}) - if 'driver' not in profile: + profile = opts.get("pillar", {}).get(uri[sdlen:indx], {}) + if "driver" not in profile: return False - fun = '{0}.set'.format(profile['driver']) - query = uri[indx+1:] + fun = "{0}.set".format(profile["driver"]) + query = uri[indx + 1 :] loaded_db = salt.loader.sdb(opts, fun, utils=utils) return loaded_db[fun](query, value, profile=profile) def sdb_delete(uri, opts, utils=None): - ''' + """ Delete a value from a db, using a uri in the form of ``sdb://<profile>/<key>``. If the uri provided does not start with ``sdb://`` or the value is not successfully deleted, return ``False``. - ''' - if not isinstance(uri, string_types) or not uri.startswith('sdb://'): + """ + if not isinstance(uri, string_types) or not uri.startswith("sdb://"): return False if utils is None: utils = salt.loader.utils(opts) - sdlen = len('sdb://') - indx = uri.find('/', sdlen) + sdlen = len("sdb://") + indx = uri.find("/", sdlen) - if (indx == -1) or len(uri[(indx+1):]) == 0: + if (indx == -1) or len(uri[(indx + 1) :]) == 0: return False profile = opts.get(uri[sdlen:indx], {}) if not profile: - profile = opts.get('pillar', {}).get(uri[sdlen:indx], {}) - if 'driver' not in profile: + profile = opts.get("pillar", {}).get(uri[sdlen:indx], {}) + if "driver" not in profile: return False - fun = '{0}.delete'.format(profile['driver']) - query = uri[indx+1:] + fun = "{0}.delete".format(profile["driver"]) + query = uri[indx + 1 :] loaded_db = salt.loader.sdb(opts, fun, utils=utils) return loaded_db[fun](query, profile=profile) -def sdb_get_or_set_hash(uri, - opts, - length=8, - chars='abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)', - utils=None): - ''' +def sdb_get_or_set_hash( + uri, + opts, + length=8, + chars="abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)", + utils=None, +): + """ Check if value exists in sdb. If it does, return, otherwise generate a random string and store it. This can be used for storing secrets in a centralized place. - ''' - if not isinstance(uri, string_types) or not uri.startswith('sdb://'): + """ + if not isinstance(uri, string_types) or not uri.startswith("sdb://"): return False if utils is None: @@ -127,7 +129,7 @@ def sdb_get_or_set_hash(uri, ret = sdb_get(uri, opts, utils=utils) if ret is None: - val = ''.join([random.SystemRandom().choice(chars) for _ in range(length)]) + val = "".join([random.SystemRandom().choice(chars) for _ in range(length)]) sdb_set(uri, val, opts, utils) return ret or val diff --git a/salt/utils/slack.py b/salt/utils/slack.py index d84a4233d66..84392746199 100644 --- a/salt/utils/slack.py +++ b/salt/utils/slack.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Library for interacting with Slack API .. versionadded:: 2016.3.0 @@ -14,29 +14,34 @@ Library for interacting with Slack API slack: api_key: peWcBiMOS9HrZG15peWcBiMOS9HrZG15 -''' +""" from __future__ import absolute_import, print_function, unicode_literals import logging + +import salt.ext.six.moves.http_client + +# pylint: enable=import-error,no-name-in-module +import salt.utils.http + # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext.six.moves.urllib.parse import urljoin as _urljoin -import salt.ext.six.moves.http_client from salt.version import __version__ -# pylint: enable=import-error,no-name-in-module -import salt.utils.http log = logging.getLogger(__name__) -def query(function, - api_key=None, - args=None, - method='GET', - header_dict=None, - data=None, - opts=None): - ''' +def query( + function, + api_key=None, + args=None, + method="GET", + header_dict=None, + data=None, + opts=None, +): + """ Slack object method function to construct and execute on the API URL. :param api_key: The Slack api key. @@ -44,52 +49,43 @@ def query(function, :param method: The HTTP method, e.g. GET or POST. :param data: The data to be sent for POST method. :return: The json response from the API call or False. - ''' + """ - ret = {'message': '', - 'res': True} + ret = {"message": "", "res": True} slack_functions = { - 'rooms': { - 'request': 'channels.list', - 'response': 'channels', - }, - 'users': { - 'request': 'users.list', - 'response': 'members', - }, - 'message': { - 'request': 'chat.postMessage', - 'response': 'channel', - }, + "rooms": {"request": "channels.list", "response": "channels"}, + "users": {"request": "users.list", "response": "members"}, + "message": {"request": "chat.postMessage", "response": "channel"}, } if not api_key: - api_key = __salt__['config.get']('slack.api_key') or \ - __salt__['config.get']('slack:api_key') + api_key = __salt__["config.get"]("slack.api_key") or __salt__["config.get"]( + "slack:api_key" + ) if not api_key: - log.error('No Slack api key found.') - ret['message'] = 'No Slack api key found.' - ret['res'] = False + log.error("No Slack api key found.") + ret["message"] = "No Slack api key found." + ret["res"] = False return ret - api_url = 'https://slack.com' - base_url = _urljoin(api_url, '/api/') - path = slack_functions.get(function).get('request') + api_url = "https://slack.com" + base_url = _urljoin(api_url, "/api/") + path = slack_functions.get(function).get("request") url = _urljoin(base_url, path, False) if not isinstance(args, dict): query_params = {} else: query_params = args.copy() - query_params['token'] = api_key + query_params["token"] = api_key if header_dict is None: header_dict = {} - if method != 'POST': - header_dict['Accept'] = 'application/json' + if method != "POST": + header_dict["Accept"] = "application/json" result = salt.utils.http.query( url, @@ -99,33 +95,33 @@ def query(function, decode=True, status=True, header_dict=header_dict, - opts=opts + opts=opts, ) - if result.get('status', None) == salt.ext.six.moves.http_client.OK: - _result = result['dict'] - response = slack_functions.get(function).get('response') - if 'error' in _result: - ret['message'] = _result['error'] - ret['res'] = False + if result.get("status", None) == salt.ext.six.moves.http_client.OK: + _result = result["dict"] + response = slack_functions.get(function).get("response") + if "error" in _result: + ret["message"] = _result["error"] + ret["res"] = False return ret - ret['message'] = _result.get(response) + ret["message"] = _result.get(response) return ret - elif result.get('status', None) == salt.ext.six.moves.http_client.NO_CONTENT: + elif result.get("status", None) == salt.ext.six.moves.http_client.NO_CONTENT: return True else: log.debug(url) log.debug(query_params) log.debug(data) log.debug(result) - if 'dict' in result: - _result = result['dict'] - if 'error' in _result: - ret['message'] = result['error'] - ret['res'] = False + if "dict" in result: + _result = result["dict"] + if "error" in _result: + ret["message"] = result["error"] + ret["res"] = False return ret - ret['message'] = _result.get(response) + ret["message"] = _result.get(response) else: - ret['message'] = 'invalid_auth' - ret['res'] = False + ret["message"] = "invalid_auth" + ret["res"] = False return ret diff --git a/salt/utils/smb.py b/salt/utils/smb.py index 0d70860e1c8..86e83c4c129 100644 --- a/salt/utils/smb.py +++ b/salt/utils/smb.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Utility functions for SMB connections :depends: impacket -''' +""" from __future__ import absolute_import, print_function, unicode_literals + +import logging import socket import uuid @@ -13,15 +15,16 @@ import uuid import salt.utils.files import salt.utils.stringutils import salt.utils.versions -import logging +from salt.exceptions import MissingSmb log = logging.getLogger(__name__) -from salt.exceptions import MissingSmb + try: import impacket.smbconnection from impacket.smbconnection import SessionError as smbSessionError from impacket.smb3 import SessionError as smb3SessionError + HAS_IMPACKET = True except ImportError: HAS_IMPACKET = False @@ -31,26 +34,37 @@ try: from smbprotocol.session import Session from smbprotocol.tree import TreeConnect from smbprotocol.open import ( - Open, ImpersonationLevel, FilePipePrinterAccessMask, FileAttributes, - CreateDisposition, CreateOptions, ShareAccess, DirectoryAccessMask, - FileInformationClass + Open, + ImpersonationLevel, + FilePipePrinterAccessMask, + FileAttributes, + CreateDisposition, + CreateOptions, + ShareAccess, + DirectoryAccessMask, + FileInformationClass, ) from smbprotocol.create_contexts import ( CreateContextName, - SMB2CreateContextRequest, SMB2CreateQueryMaximalAccessRequest + SMB2CreateContextRequest, + SMB2CreateQueryMaximalAccessRequest, ) from smbprotocol.security_descriptor import ( - AccessAllowedAce, AccessMask, AclPacket, SDControl, SIDPacket, - SMB2CreateSDBuffer + AccessAllowedAce, + AccessMask, + AclPacket, + SDControl, + SIDPacket, + SMB2CreateSDBuffer, ) - logging.getLogger('smbprotocol').setLevel(logging.WARNING) + + logging.getLogger("smbprotocol").setLevel(logging.WARNING) HAS_SMBPROTOCOL = True except ImportError: HAS_SMBPROTOCOL = False class SMBProto(object): - def __init__(self, server, username, password, port=445): connection_id = uuid.uuid4() addr = socket.gethostbyname(server) @@ -70,48 +84,47 @@ class SMBProto(object): return self.session.connection def tree_connect(self, share): - if share.endswith('$'): - share = r'\\{}\{}'.format(self.server, share) + if share.endswith("$"): + share = r"\\{}\{}".format(self.server, share) tree = TreeConnect(self.session, share) tree.connect() return tree @staticmethod def normalize_filename(file): - return file.lstrip('\\') + return file.lstrip("\\") @classmethod def open_file(cls, tree, file): file = cls.normalize_filename(file) # ensure file is created, get maximal access, and set everybody read access max_req = SMB2CreateContextRequest() - max_req['buffer_name'] = CreateContextName.SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST - max_req['buffer_data'] = SMB2CreateQueryMaximalAccessRequest() + max_req[ + "buffer_name" + ] = CreateContextName.SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST + max_req["buffer_data"] = SMB2CreateQueryMaximalAccessRequest() # create security buffer that sets the ACL for everyone to have read access everyone_sid = SIDPacket() everyone_sid.from_string("S-1-1-0") ace = AccessAllowedAce() - ace['mask'] = AccessMask.GENERIC_ALL - ace['sid'] = everyone_sid + ace["mask"] = AccessMask.GENERIC_ALL + ace["sid"] = everyone_sid acl = AclPacket() - acl['aces'] = [ace] + acl["aces"] = [ace] sec_desc = SMB2CreateSDBuffer() - sec_desc['control'].set_flag(SDControl.SELF_RELATIVE) + sec_desc["control"].set_flag(SDControl.SELF_RELATIVE) sec_desc.set_dacl(acl) sd_buffer = SMB2CreateContextRequest() - sd_buffer['buffer_name'] = CreateContextName.SMB2_CREATE_SD_BUFFER - sd_buffer['buffer_data'] = sec_desc + sd_buffer["buffer_name"] = CreateContextName.SMB2_CREATE_SD_BUFFER + sd_buffer["buffer_data"] = sec_desc - create_contexts = [ - max_req, - sd_buffer - ] + create_contexts = [max_req, sd_buffer] file_open = Open(tree, file) open_info = file_open.create( ImpersonationLevel.Impersonation, - FilePipePrinterAccessMask.GENERIC_READ | - FilePipePrinterAccessMask.GENERIC_WRITE, + FilePipePrinterAccessMask.GENERIC_READ + | FilePipePrinterAccessMask.GENERIC_WRITE, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OVERWRITE_IF, @@ -130,94 +143,99 @@ class SMBProto(object): FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN_IF, - CreateOptions.FILE_DIRECTORY_FILE + CreateOptions.FILE_DIRECTORY_FILE, ) return dir_open class StrHandle(object): - ''' + """ Fakes a file handle, so that raw strings may be uploaded instead of having to write files first. Used by put_str() - ''' + """ + def __init__(self, content): - ''' + """ Init - ''' + """ self.content = content self.finished = False def string(self, writesize=None): - ''' + """ Looks like a file handle - ''' + """ if not self.finished: self.finished = True return self.content - return '' + return "" -def _get_conn_impacket(host=None, username=None, password=None, client_name=None, port=445): +def _get_conn_impacket( + host=None, username=None, password=None, client_name=None, port=445 +): conn = impacket.smbconnection.SMBConnection( - remoteName=host, - remoteHost=host, - myName=client_name, + remoteName=host, remoteHost=host, myName=client_name, ) conn.login(user=username, password=password) return conn -def _get_conn_smbprotocol(host='', username='', password='', client_name='', port=445): +def _get_conn_smbprotocol(host="", username="", password="", client_name="", port=445): conn = SMBProto(host, username, password, port) conn.connect() return conn -def get_conn(host='', username=None, password=None, port=445): - ''' +def get_conn(host="", username=None, password=None, port=445): + """ Get an SMB connection - ''' + """ if HAS_IMPACKET and not HAS_SMBPROTOCOL: salt.utils.versions.warn_until( - 'Sodium', - 'Support of impacket has been depricated and will be ' - 'removed in Sodium. Please install smbprotocol instead.' + "Sodium", + "Support of impacket has been depricated and will be " + "removed in Sodium. Please install smbprotocol instead.", ) if HAS_SMBPROTOCOL: - log.info('Get connection smbprotocol') + log.info("Get connection smbprotocol") return _get_conn_smbprotocol(host, username, password, port=port) elif HAS_IMPACKET: - log.info('Get connection impacket') + log.info("Get connection impacket") return _get_conn_impacket(host, username, password, port=port) return False -def _mkdirs_impacket(path, share='C$', conn=None, host=None, username=None, password=None): - ''' +def _mkdirs_impacket( + path, share="C$", conn=None, host=None, username=None, password=None +): + """ Recursively create a directory structure on an SMB share Paths should be passed in with forward-slash delimiters, and should not start with a forward-slash. - ''' + """ if conn is None: conn = get_conn(host, username, password) if conn is False: return False - comps = path.split('/') + comps = path.split("/") pos = 1 for comp in comps: - cwd = '\\'.join(comps[0:pos]) + cwd = "\\".join(comps[0:pos]) try: conn.listPath(share, cwd) except (smbSessionError, smb3SessionError): - log.exception('Encountered error running conn.listPath') + log.exception("Encountered error running conn.listPath") conn.createDirectory(share, cwd) pos += 1 -def _mkdirs_smbprotocol(path, share='C$', conn=None, host=None, username=None, password=None): +def _mkdirs_smbprotocol( + path, share="C$", conn=None, host=None, username=None, password=None +): if conn is None: conn = get_conn(host, username, password) @@ -225,34 +243,42 @@ def _mkdirs_smbprotocol(path, share='C$', conn=None, host=None, username=None, p return False tree = conn.tree_connect(share) - comps = path.split('/') + comps = path.split("/") pos = 1 for comp in comps: - cwd = '\\'.join(comps[0:pos]) + cwd = "\\".join(comps[0:pos]) dir_open = conn.open_directory(tree, cwd, create=True) compound_messages = [ - dir_open.query_directory("*", - FileInformationClass.FILE_NAMES_INFORMATION, - send=False), - dir_open.close(False, send=False) + dir_open.query_directory( + "*", FileInformationClass.FILE_NAMES_INFORMATION, send=False + ), + dir_open.close(False, send=False), ] - requests = conn.session.connection.send_compound([x[0] for x in compound_messages], - conn.session.session_id, - tree.tree_connect_id) + requests = conn.session.connection.send_compound( + [x[0] for x in compound_messages], + conn.session.session_id, + tree.tree_connect_id, + ) for i, request in enumerate(requests): response = compound_messages[i][1](request) pos += 1 -def mkdirs(path, share='C$', conn=None, host=None, username=None, password=None): +def mkdirs(path, share="C$", conn=None, host=None, username=None, password=None): if HAS_SMBPROTOCOL: - return _mkdirs_smbprotocol(path, share, conn=conn, host=host, username=username, password=password) + return _mkdirs_smbprotocol( + path, share, conn=conn, host=host, username=username, password=password + ) elif HAS_IMPACKET: - return _mkdirs_impacket(path, share, conn=conn, host=host, username=username, password=password) + return _mkdirs_impacket( + path, share, conn=conn, host=host, username=username, password=password + ) raise MissingSmb("SMB library required (impacket or smbprotocol)") -def _put_str_impacket(content, path, share='C$', conn=None, host=None, username=None, password=None): +def _put_str_impacket( + content, path, share="C$", conn=None, host=None, username=None, password=None +): if conn is None: conn = get_conn(host, username, password) @@ -264,8 +290,8 @@ def _put_str_impacket(content, path, share='C$', conn=None, host=None, username= def _put_str_smbprotocol( - content, path, share='C$', conn=None, host=None, username=None, - password=None): + content, path, share="C$", conn=None, host=None, username=None, password=None +): if conn is None: conn = get_conn(host, username, password) if conn is False: @@ -278,25 +304,40 @@ def _put_str_smbprotocol( file_open.close() -def put_str(content, path, share='C$', conn=None, host=None, username=None, password=None): - ''' +def put_str( + content, path, share="C$", conn=None, host=None, username=None, password=None +): + """ Wrapper around impacket.smbconnection.putFile() that allows a string to be uploaded, without first writing it as a local file - ''' + """ if HAS_SMBPROTOCOL: return _put_str_smbprotocol( - content, path, share, conn=conn, host=host, - username=username, password=password + content, + path, + share, + conn=conn, + host=host, + username=username, + password=password, ) elif HAS_IMPACKET: return _put_str_impacket( - content, path, share, conn=conn, host=host, username=username, password=password + content, + path, + share, + conn=conn, + host=host, + username=username, + password=password, ) raise MissingSmb("SMB library required (impacket or smbprotocol)") -def _put_file_impacket(local_path, path, share='C$', conn=None, host=None, username=None, password=None): - ''' +def _put_file_impacket( + local_path, path, share="C$", conn=None, host=None, username=None, password=None +): + """ Wrapper around impacket.smbconnection.putFile() that allows a file to be uploaded @@ -305,23 +346,30 @@ def _put_file_impacket(local_path, path, share='C$', conn=None, host=None, usern import salt.utils.smb smb_conn = salt.utils.smb.get_conn('10.0.0.45', 'vagrant', 'vagrant') salt.utils.smb.put_file('/root/test.pdf', 'temp\\myfiles\\test1.pdf', conn=smb_conn) - ''' + """ if conn is None: conn = get_conn(host, username, password) if conn is False: return False - if hasattr(local_path, 'read'): + if hasattr(local_path, "read"): conn.putFile(share, path, local_path) return - with salt.utils.files.fopen(local_path, 'rb') as fh_: + with salt.utils.files.fopen(local_path, "rb") as fh_: conn.putFile(share, path, fh_.read) def _put_file_smbprotocol( - local_path, path, share='C$', conn=None, host=None, username=None, - password=None, chunk_size=1024 * 1024): + local_path, + path, + share="C$", + conn=None, + host=None, + username=None, + password=None, + chunk_size=1024 * 1024, +): if conn is None: conn = get_conn(host, username, password) if conn is False: @@ -329,7 +377,7 @@ def _put_file_smbprotocol( tree = conn.tree_connect(share) file_open = conn.open_file(tree, path) - with salt.utils.files.fopen(local_path, 'rb') as fh_: + with salt.utils.files.fopen(local_path, "rb") as fh_: try: position = 0 while True: @@ -342,8 +390,10 @@ def _put_file_smbprotocol( file_open.close(False) -def put_file(local_path, path, share='C$', conn=None, host=None, username=None, password=None): - ''' +def put_file( + local_path, path, share="C$", conn=None, host=None, username=None, password=None +): + """ Wrapper around impacket.smbconnection.putFile() that allows a file to be uploaded @@ -352,21 +402,33 @@ def put_file(local_path, path, share='C$', conn=None, host=None, username=None, import salt.utils.smb smb_conn = salt.utils.smb.get_conn('10.0.0.45', 'vagrant', 'vagrant') salt.utils.smb.put_file('/root/test.pdf', 'temp\\myfiles\\test1.pdf', conn=smb_conn) - ''' + """ if HAS_SMBPROTOCOL: return _put_file_smbprotocol( - local_path, path, share, conn=conn, host=host, username=username, - password=password + local_path, + path, + share, + conn=conn, + host=host, + username=username, + password=password, ) elif HAS_IMPACKET: return _put_file_impacket( - local_path, path, share, conn=conn, host=host, username=username, - password=password + local_path, + path, + share, + conn=conn, + host=host, + username=username, + password=password, ) raise MissingSmb("SMB library required (impacket or smbprotocol)") -def _delete_file_impacket(path, share='C$', conn=None, host=None, username=None, password=None): +def _delete_file_impacket( + path, share="C$", conn=None, host=None, username=None, password=None +): if conn is None: conn = get_conn(host, username, password) if conn is False: @@ -374,7 +436,9 @@ def _delete_file_impacket(path, share='C$', conn=None, host=None, username=None, conn.deleteFile(share, path) -def _delete_file_smbprotocol(path, share='C$', conn=None, host=None, username=None, password=None): +def _delete_file_smbprotocol( + path, share="C$", conn=None, host=None, username=None, password=None +): if conn is None: conn = get_conn(host, username, password) if conn is False: @@ -384,20 +448,21 @@ def _delete_file_smbprotocol(path, share='C$', conn=None, host=None, username=No delete_msgs = [ file_open.create( ImpersonationLevel.Impersonation, - FilePipePrinterAccessMask.GENERIC_READ | - FilePipePrinterAccessMask.DELETE, + FilePipePrinterAccessMask.GENERIC_READ | FilePipePrinterAccessMask.DELETE, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN, - CreateOptions.FILE_NON_DIRECTORY_FILE | - CreateOptions.FILE_DELETE_ON_CLOSE, - send=False + CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_DELETE_ON_CLOSE, + send=False, ), - file_open.close(False, send=False) + file_open.close(False, send=False), ] - requests = conn.connection.send_compound([x[0] for x in delete_msgs], - conn.session.session_id, - tree.tree_connect_id, related=True) + requests = conn.connection.send_compound( + [x[0] for x in delete_msgs], + conn.session.session_id, + tree.tree_connect_id, + related=True, + ) responses = [] for i, request in enumerate(requests): # A SMBResponseException will be raised if something went wrong @@ -405,15 +470,21 @@ def _delete_file_smbprotocol(path, share='C$', conn=None, host=None, username=No responses.append(response) -def delete_file(path, share='C$', conn=None, host=None, username=None, password=None): +def delete_file(path, share="C$", conn=None, host=None, username=None, password=None): if HAS_SMBPROTOCOL: - return _delete_file_smbprotocol(path, share, conn=conn, host=host, username=username, password=password) + return _delete_file_smbprotocol( + path, share, conn=conn, host=host, username=username, password=password + ) elif HAS_IMPACKET: - return _delete_file_impacket(path, share, conn=conn, host=host, username=username, password=password) + return _delete_file_impacket( + path, share, conn=conn, host=host, username=username, password=password + ) raise MissingSmb("SMB library required (impacket or smbprotocol)") -def _delete_directory_impacket(path, share='C$', conn=None, host=None, username=None, password=None): +def _delete_directory_impacket( + path, share="C$", conn=None, host=None, username=None, password=None +): if conn is None: conn = get_conn(host, username, password) if conn is False: @@ -421,7 +492,9 @@ def _delete_directory_impacket(path, share='C$', conn=None, host=None, username= conn.deleteDirectory(share, path) -def _delete_directory_smbprotocol(path, share='C$', conn=None, host=None, username=None, password=None): +def _delete_directory_smbprotocol( + path, share="C$", conn=None, host=None, username=None, password=None +): if conn is None: conn = get_conn(host, username, password) if conn is False: @@ -437,22 +510,25 @@ def _delete_directory_smbprotocol(path, share='C$', conn=None, host=None, userna FileAttributes.FILE_ATTRIBUTE_DIRECTORY, 0, CreateDisposition.FILE_OPEN, - CreateOptions.FILE_DIRECTORY_FILE | - CreateOptions.FILE_DELETE_ON_CLOSE, - send=False + CreateOptions.FILE_DIRECTORY_FILE | CreateOptions.FILE_DELETE_ON_CLOSE, + send=False, ), - dir_open.close(False, send=False) + dir_open.close(False, send=False), ] - delete_reqs = conn.connection.send_compound([x[0] for x in delete_msgs], - sid=conn.session.session_id, - tid=tree.tree_connect_id, - related=True) + delete_reqs = conn.connection.send_compound( + [x[0] for x in delete_msgs], + sid=conn.session.session_id, + tid=tree.tree_connect_id, + related=True, + ) for i, request in enumerate(delete_reqs): # A SMBResponseException will be raised if something went wrong response = delete_msgs[i][1](request) -def delete_directory(path, share='C$', conn=None, host=None, username=None, password=None): +def delete_directory( + path, share="C$", conn=None, host=None, username=None, password=None +): if HAS_SMBPROTOCOL: return _delete_directory_smbprotocol( path, share, conn=conn, host=host, username=username, password=password diff --git a/salt/utils/smtp.py b/salt/utils/smtp.py index a26275ebf28..0ecce978b77 100644 --- a/salt/utils/smtp.py +++ b/salt/utils/smtp.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Return salt data via email The following fields can be set in the minion conf file: @@ -26,12 +26,13 @@ There are a few things to keep in mind: * The field gpgowner specifies a user's ~/.gpg directory. This must contain a gpg public key matching the address the mail is sent to. If left unset, no encryption will be used. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import os -import logging import smtplib from email.utils import formatdate @@ -39,6 +40,7 @@ from salt.ext import six try: import gnupg + HAS_GNUPG = True except ImportError: HAS_GNUPG = False @@ -48,80 +50,70 @@ log = logging.getLogger(__name__) def send(kwargs, opts): - ''' + """ Send an email with the data - ''' + """ opt_keys = ( - 'smtp.to', - 'smtp.from', - 'smtp.host', - 'smtp.port', - 'smtp.tls', - 'smtp.username', - 'smtp.password', - 'smtp.subject', - 'smtp.gpgowner', - 'smtp.content', + "smtp.to", + "smtp.from", + "smtp.host", + "smtp.port", + "smtp.tls", + "smtp.username", + "smtp.password", + "smtp.subject", + "smtp.gpgowner", + "smtp.content", ) config = {} for key in opt_keys: - config[key] = opts.get(key, '') + config[key] = opts.get(key, "") config.update(kwargs) - if not config['smtp.port']: - config['smtp.port'] = 25 + if not config["smtp.port"]: + config["smtp.port"] = 25 - log.debug('SMTP port has been set to %s', config['smtp.port']) - log.debug("smtp_return: Subject is '%s'", config['smtp.subject']) + log.debug("SMTP port has been set to %s", config["smtp.port"]) + log.debug("smtp_return: Subject is '%s'", config["smtp.subject"]) - if HAS_GNUPG and config['smtp.gpgowner']: + if HAS_GNUPG and config["smtp.gpgowner"]: gpg = gnupg.GPG( - gnupghome=os.path.expanduser( - '~{0}/.gnupg'.format(config['smtp.gpgowner']) - ), - options=['--trust-model always'] + gnupghome=os.path.expanduser("~{0}/.gnupg".format(config["smtp.gpgowner"])), + options=["--trust-model always"], ) - encrypted_data = gpg.encrypt(config['smtp.content'], config['smtp.to']) + encrypted_data = gpg.encrypt(config["smtp.content"], config["smtp.to"]) if encrypted_data.ok: - log.debug('smtp_return: Encryption successful') - config['smtp.content'] = six.text_type(encrypted_data) + log.debug("smtp_return: Encryption successful") + config["smtp.content"] = six.text_type(encrypted_data) else: - log.error( - 'SMTP: Encryption failed, only an error message will be sent' - ) - config['smtp.content'] = ( - 'Encryption failed, the return data was not sent.' - '\r\n\r\n{0}\r\n{1}' + log.error("SMTP: Encryption failed, only an error message will be sent") + config["smtp.content"] = ( + "Encryption failed, the return data was not sent." "\r\n\r\n{0}\r\n{1}" ).format(encrypted_data.status, encrypted_data.stderr) message = ( - 'From: {0}\r\n' - 'To: {1}\r\n' - 'Date: {2}\r\n' - 'Subject: {3}\r\n' - '\r\n' - '{4}' + "From: {0}\r\n" "To: {1}\r\n" "Date: {2}\r\n" "Subject: {3}\r\n" "\r\n" "{4}" ).format( - config['smtp.from'], - config['smtp.to'], + config["smtp.from"], + config["smtp.to"], formatdate(localtime=True), - config['smtp.subject'], - config['smtp.content'], + config["smtp.subject"], + config["smtp.content"], ) - log.debug('smtp_return: Connecting to the server...') - server = smtplib.SMTP(config['smtp.host'], int(config['smtp.port'])) + log.debug("smtp_return: Connecting to the server...") + server = smtplib.SMTP(config["smtp.host"], int(config["smtp.port"])) - if config['smtp.tls'] is True: + if config["smtp.tls"] is True: server.starttls() - log.debug('smtp_return: TLS enabled') + log.debug("smtp_return: TLS enabled") - if config['smtp.username'] and config['smtp.password']: - server.login(config['smtp.username'], config['smtp.password']) - log.debug('smtp_return: Authenticated') + if config["smtp.username"] and config["smtp.password"]: + server.login(config["smtp.username"], config["smtp.password"]) + log.debug("smtp_return: Authenticated") - server.sendmail(config['smtp.from'], config['smtp.to'], message) - log.debug('smtp_return: Message sent.') + server.sendmail(config["smtp.from"], config["smtp.to"], message) + log.debug("smtp_return: Message sent.") server.quit() diff --git a/salt/utils/ssdp.py b/salt/utils/ssdp.py index 26b757b7388..84926cbb283 100644 --- a/salt/utils/ssdp.py +++ b/salt/utils/ssdp.py @@ -15,33 +15,36 @@ # See the License for the specific language governing permissions and # limitations under the License. -''' +""" Salt Service Discovery Protocol. JSON-based service discovery protocol, used by minions to find running Master. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + +import copy import datetime -import time import logging import random import socket -import copy +import time from collections import OrderedDict import salt.utils.json import salt.utils.stringutils _json = salt.utils.json.import_json() -if not hasattr(_json, 'dumps'): +if not hasattr(_json, "dumps"): _json = None try: import asyncio + asyncio.ported = False except ImportError: try: # Python 2 doesn't have asyncio import trollius as asyncio + asyncio.ported = True except ImportError: asyncio = None @@ -56,45 +59,46 @@ class TimeStampException(Exception): class SSDPBase(object): - ''' + """ Salt Service Discovery Protocol. - ''' + """ + log = logging.getLogger(__name__) # Fields - SIGNATURE = 'signature' - ANSWER = 'answer' - PORT = 'port' - LISTEN_IP = 'listen_ip' - TIMEOUT = 'timeout' + SIGNATURE = "signature" + ANSWER = "answer" + PORT = "port" + LISTEN_IP = "listen_ip" + TIMEOUT = "timeout" # Default values DEFAULTS = { - SIGNATURE: '__salt_master_service', + SIGNATURE: "__salt_master_service", PORT: 4520, - LISTEN_IP: '0.0.0.0', + LISTEN_IP: "0.0.0.0", TIMEOUT: 3, ANSWER: {}, } @staticmethod def _is_available(): - ''' + """ Return True if the USSDP dependencies are satisfied. :return: - ''' + """ return bool(asyncio and _json) @staticmethod def get_self_ip(): - ''' + """ Find out localhost outside IP. :return: - ''' + """ sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: - sck.connect(('1.255.255.255', 1)) # Does not needs to be reachable + sck.connect(("1.255.255.255", 1)) # Does not needs to be reachable ip_addr = sck.getsockname()[0] except Exception: # pylint: disable=broad-except ip_addr = socket.gethostbyname(socket.gethostname()) @@ -104,16 +108,16 @@ class SSDPBase(object): class SSDPFactory(SSDPBase): - ''' + """ Socket protocol factory. - ''' + """ def __init__(self, **config): - ''' + """ Initialize :param config: - ''' + """ for attr in (self.SIGNATURE, self.ANSWER): setattr(self, attr, config.get(attr, self.DEFAULTS[attr])) self.disable_hidden = False @@ -121,25 +125,25 @@ class SSDPFactory(SSDPBase): self.my_ip = socket.gethostbyname(socket.gethostname()) def __call__(self, *args, **kwargs): - ''' + """ Return instance on Factory call. :param args: :param kwargs: :return: - ''' + """ return self def connection_made(self, transport): - ''' + """ On connection. :param transport: :return: - ''' + """ self.transport = transport def _sendto(self, data, addr=None, attempts=10): - ''' + """ On multi-master environments, running on the same machine, transport sending to the destination can be allowed only at once. Since every machine will immediately respond, high chance to @@ -149,127 +153,157 @@ class SSDPFactory(SSDPBase): :param data: :param addr: :return: - ''' + """ tries = 0 slp_time = lambda: 0.5 / random.randint(10, 30) slp = slp_time() while tries < attempts: try: self.transport.sendto(data, addr=addr) - self.log.debug('Sent successfully') + self.log.debug("Sent successfully") return except AttributeError as ex: - self.log.debug('Permission error: %s', ex) + self.log.debug("Permission error: %s", ex) time.sleep(slp) tries += 1 slp += slp_time() def datagram_received(self, data, addr): - ''' + """ On datagram receive. :param data: :param addr: :return: - ''' + """ message = salt.utils.stringutils.to_unicode(data) if message.startswith(self.signature): try: - timestamp = float(message[len(self.signature):]) + timestamp = float(message[len(self.signature) :]) except (TypeError, ValueError): self.log.debug( - 'Received invalid timestamp in package from %s:%s', - *addr + "Received invalid timestamp in package from %s:%s", *addr ) if self.disable_hidden: - self._sendto('{0}:E:{1}'.format(self.signature, 'Invalid timestamp'), addr) + self._sendto( + "{0}:E:{1}".format(self.signature, "Invalid timestamp"), addr + ) return - if datetime.datetime.fromtimestamp(timestamp) < (datetime.datetime.now() - datetime.timedelta(seconds=20)): + if datetime.datetime.fromtimestamp(timestamp) < ( + datetime.datetime.now() - datetime.timedelta(seconds=20) + ): if self.disable_hidden: - self._sendto('{0}:E:{1}'.format(self.signature, 'Timestamp is too old'), addr) - self.log.debug('Received outdated package from %s:%s', *addr) + self._sendto( + "{0}:E:{1}".format(self.signature, "Timestamp is too old"), addr + ) + self.log.debug("Received outdated package from %s:%s", *addr) return self.log.debug('Received "%s" from %s:%s', message, *addr) self._sendto( - salt.utils.stringutils.to_bytes(str('{0}:@:{1}').format( # future lint: disable=blacklisted-function - self.signature, - salt.utils.json.dumps(self.answer, _json_module=_json) - )), - addr + salt.utils.stringutils.to_bytes( + str( + "{0}:@:{1}" + ).format( # future lint: disable=blacklisted-function + self.signature, + salt.utils.json.dumps(self.answer, _json_module=_json), + ) + ), + addr, ) else: if self.disable_hidden: self._sendto( salt.utils.stringutils.to_bytes( - '{0}:E:{1}'.format(self.signature, 'Invalid packet signature'), - addr + "{0}:E:{1}".format(self.signature, "Invalid packet signature"), + addr, ) ) - self.log.debug('Received bad signature from %s:%s', *addr) + self.log.debug("Received bad signature from %s:%s", *addr) class SSDPDiscoveryServer(SSDPBase): - ''' + """ Discovery service publisher. - ''' + """ + @staticmethod def is_available(): - ''' + """ Return availability of the Server. :return: - ''' + """ return SSDPBase._is_available() def __init__(self, **config): - ''' + """ Initialize. :param config: - ''' + """ self._config = copy.deepcopy(config) if self.ANSWER not in self._config: self._config[self.ANSWER] = {} - self._config[self.ANSWER].update({'master': self.get_self_ip()}) + self._config[self.ANSWER].update({"master": self.get_self_ip()}) @staticmethod - def create_datagram_endpoint(loop, protocol_factory, local_addr=None, remote_addr=None, family=0, proto=0, flags=0): - ''' + def create_datagram_endpoint( + loop, + protocol_factory, + local_addr=None, + remote_addr=None, + family=0, + proto=0, + flags=0, + ): + """ Create datagram connection. Based on code from Python 3.5 version, this method is used only in Python 2.7+ versions, since Trollius library did not ported UDP packets broadcast. - ''' + """ if not (local_addr or remote_addr): if not family: - raise ValueError('unexpected address family') + raise ValueError("unexpected address family") addr_pairs_info = (((family, proto), (None, None)),) else: addr_infos = OrderedDict() for idx, addr in ((0, local_addr), (1, remote_addr)): if addr is not None: - assert isinstance(addr, tuple) and len(addr) == 2, '2-tuple is expected' - infos = yield asyncio.coroutines.From(loop.getaddrinfo( - *addr, family=family, type=socket.SOCK_DGRAM, proto=proto, flags=flags)) + assert ( + isinstance(addr, tuple) and len(addr) == 2 + ), "2-tuple is expected" + infos = yield asyncio.coroutines.From( + loop.getaddrinfo( + *addr, + family=family, + type=socket.SOCK_DGRAM, + proto=proto, + flags=flags + ) + ) if not infos: - raise socket.error('getaddrinfo() returned empty list') + raise socket.error("getaddrinfo() returned empty list") for fam, _, pro, _, address in infos: key = (fam, pro) if key not in addr_infos: addr_infos[key] = [None, None] addr_infos[key][idx] = address addr_pairs_info = [ - (key, addr_pair) for key, addr_pair in addr_infos.items() - if not ((local_addr and addr_pair[0] is None) or - (remote_addr and addr_pair[1] is None))] + (key, addr_pair) + for key, addr_pair in addr_infos.items() + if not ( + (local_addr and addr_pair[0] is None) + or (remote_addr and addr_pair[1] is None) + ) + ] if not addr_pairs_info: - raise ValueError('can not get address information') + raise ValueError("can not get address information") exceptions = [] - for ((family, proto), - (local_address, remote_address)) in addr_pairs_info: + for ((family, proto), (local_address, remote_address)) in addr_pairs_info: sock = r_addr = None try: sock = socket.socket(family=family, type=socket.SOCK_DGRAM, proto=proto) @@ -279,7 +313,9 @@ class SSDPDiscoveryServer(SSDPBase): if local_addr: sock.bind(local_address) if remote_addr: - yield asyncio.coroutines.From(loop.sock_connect(sock, remote_address)) + yield asyncio.coroutines.From( + loop.sock_connect(sock, remote_address) + ) r_addr = remote_address except socket.error as exc: if sock is not None: @@ -305,69 +341,81 @@ class SSDPDiscoveryServer(SSDPBase): raise asyncio.coroutines.Return(transport, protocol) def run(self): - ''' + """ Run server. :return: - ''' + """ listen_ip = self._config.get(self.LISTEN_IP, self.DEFAULTS[self.LISTEN_IP]) port = self._config.get(self.PORT, self.DEFAULTS[self.PORT]) - self.log.info('Starting service discovery listener on udp://%s:%s', listen_ip, port) + self.log.info( + "Starting service discovery listener on udp://%s:%s", listen_ip, port + ) loop = asyncio.get_event_loop() protocol = SSDPFactory(answer=self._config[self.ANSWER]) if asyncio.ported: transport, protocol = loop.run_until_complete( - SSDPDiscoveryServer.create_datagram_endpoint(loop, protocol, local_addr=(listen_ip, port))) + SSDPDiscoveryServer.create_datagram_endpoint( + loop, protocol, local_addr=(listen_ip, port) + ) + ) else: transport, protocol = loop.run_until_complete( - loop.create_datagram_endpoint(protocol, local_addr=(listen_ip, port), allow_broadcast=True)) + loop.create_datagram_endpoint( + protocol, local_addr=(listen_ip, port), allow_broadcast=True + ) + ) try: loop.run_forever() finally: - self.log.info('Stopping service discovery listener.') + self.log.info("Stopping service discovery listener.") transport.close() loop.close() class SSDPDiscoveryClient(SSDPBase): - ''' + """ Class to discover Salt Master via UDP broadcast. - ''' + """ + @staticmethod def is_available(): - ''' + """ Return availability of the Client :return: - ''' + """ return SSDPBase._is_available() def __init__(self, **config): - ''' + """ Initialize - ''' + """ self._config = config self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - self._socket.settimeout(self._config.get(self.TIMEOUT, self.DEFAULTS[self.TIMEOUT])) + self._socket.settimeout( + self._config.get(self.TIMEOUT, self.DEFAULTS[self.TIMEOUT]) + ) for attr in [self.SIGNATURE, self.TIMEOUT, self.PORT]: setattr(self, attr, self._config.get(attr, self.DEFAULTS[attr])) def _query(self): - ''' + """ Query the broadcast for defined services. :return: - ''' + """ query = salt.utils.stringutils.to_bytes( - "{}{}".format(self.signature, time.time())) - self._socket.sendto(query, ('<broadcast>', self.port)) + "{}{}".format(self.signature, time.time()) + ) + self._socket.sendto(query, ("<broadcast>", self.port)) return query def _collect_masters_map(self, response): - ''' + """ Collect masters map from the network. :return: - ''' + """ while True: try: data, addr = self._socket.recvfrom(0x400) @@ -378,45 +426,54 @@ class SSDPDiscoveryClient(SSDPBase): else: break except Exception as err: # pylint: disable=broad-except - self.log.error('Discovery master collection failure: %s', err) + self.log.error("Discovery master collection failure: %s", err) break def discover(self): - ''' + """ Gather the information of currently declared servers. :return: - ''' + """ response = {} masters = {} self.log.info("Looking for a server discovery") self._query() self._collect_masters_map(response) if not response: - msg = 'No master has been discovered.' + msg = "No master has been discovered." self.log.info(msg) else: for addr, descriptions in response.items(): - for data in descriptions: # Several masters can run at the same machine. + for ( + data + ) in descriptions: # Several masters can run at the same machine. msg = salt.utils.stringutils.to_unicode(data) if msg.startswith(self.signature): msg = msg.split(self.signature)[-1] self.log.debug( "Service announcement at '%s:%s'. Response: '%s'", - addr[0], addr[1], msg + addr[0], + addr[1], + msg, ) - if ':E:' in msg: - err = msg.split(':E:')[-1] + if ":E:" in msg: + err = msg.split(":E:")[-1] self.log.error( - 'Error response from the service publisher at %s: %s', - addr, err + "Error response from the service publisher at %s: %s", + addr, + err, ) if "timestamp" in err: - self.log.error('Publisher sent shifted timestamp from %s', addr) + self.log.error( + "Publisher sent shifted timestamp from %s", addr + ) else: if addr not in masters: masters[addr] = [] masters[addr].append( - salt.utils.json.loads(msg.split(':@:')[-1], _json_module=_json) + salt.utils.json.loads( + msg.split(":@:")[-1], _json_module=_json + ) ) return masters diff --git a/salt/utils/ssh.py b/salt/utils/ssh.py index 665feb84033..6f2cd231bad 100644 --- a/salt/utils/ssh.py +++ b/salt/utils/ssh.py @@ -2,6 +2,7 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import re # Import salt libs @@ -14,17 +15,17 @@ def key_is_encrypted(key): # NOTE: this is a temporary workaround until we can get salt/modules/ssh.py # working on Windows. try: - with salt.utils.files.fopen(key, 'r') as fp_: + with salt.utils.files.fopen(key, "r") as fp_: key_data = salt.utils.stringutils.to_unicode(fp_.read()) except (IOError, OSError) as exc: # Raise a CommandExecutionError salt.utils.files.process_read_exception(exc, key) - is_private_key = re.search(r'BEGIN (?:\w+\s)*PRIVATE KEY', key_data) - is_encrypted = 'ENCRYPTED' in key_data + is_private_key = re.search(r"BEGIN (?:\w+\s)*PRIVATE KEY", key_data) + is_encrypted = "ENCRYPTED" in key_data del key_data if not is_private_key: - raise CommandExecutionError('{0} is not a private key'.format(key)) + raise CommandExecutionError("{0} is not a private key".format(key)) return is_encrypted diff --git a/salt/utils/state.py b/salt/utils/state.py index 371f393a4ac..da759b8a5ac 100644 --- a/salt/utils/state.py +++ b/salt/utils/state.py @@ -1,47 +1,46 @@ # -*- coding: utf-8 -*- -''' +""" Utility functions for state functions .. versionadded:: 2018.3.0 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import copy +import salt.state +from salt.exceptions import CommandExecutionError + # Import Salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError -import salt.state _empty = object() def gen_tag(low): - ''' + """ Generate the running dict tag string from the low data structure - ''' - return '{0[state]}_|-{0[__id__]}_|-{0[name]}_|-{0[fun]}'.format(low) + """ + return "{0[state]}_|-{0[__id__]}_|-{0[name]}_|-{0[fun]}".format(low) def search_onfail_requisites(sid, highstate): - ''' + """ For a particular low chunk, search relevant onfail related states - ''' + """ onfails = [] - if '_|-' in sid: + if "_|-" in sid: st = salt.state.split_low_tag(sid) else: - st = {'__id__': sid} + st = {"__id__": sid} for fstate, fchunks in six.iteritems(highstate): - if fstate == st['__id__']: + if fstate == st["__id__"]: continue else: for mod_, fchunk in six.iteritems(fchunks): - if ( - not isinstance(mod_, six.string_types) or - mod_.startswith('__') - ): + if not isinstance(mod_, six.string_types) or mod_.startswith("__"): continue else: if not isinstance(fchunk, list): @@ -54,8 +53,7 @@ def search_onfail_requisites(sid, highstate): for fdata in fchunk: if not isinstance(fdata, dict): continue - onfail_handled = (fdata.get('onfail_stop', True) - is False) + onfail_handled = fdata.get("onfail_stop", True) is False if onfail_handled: break if not onfail_handled: @@ -64,13 +62,13 @@ def search_onfail_requisites(sid, highstate): if not isinstance(fdata, dict): continue for knob, fvalue in six.iteritems(fdata): - if knob != 'onfail': + if knob != "onfail": continue for freqs in fvalue: for fmod, fid in six.iteritems(freqs): if not ( - fid == st['__id__'] and - fmod == st.get('state', fmod) + fid == st["__id__"] + and fmod == st.get("state", fmod) ): continue onfails.append((fstate, mod_, fchunk)) @@ -78,7 +76,7 @@ def search_onfail_requisites(sid, highstate): def check_onfail_requisites(state_id, state_result, running, highstate): - ''' + """ When a state fail and is part of a highstate, check if there is onfail requisites. When we find onfail requisites, we will consider the state failed @@ -90,27 +88,24 @@ def check_onfail_requisites(state_id, state_result, running, highstate): False: if one on those handler failed None: if the state does not have onfail requisites - ''' + """ nret = None - if ( - state_id and state_result and - highstate and isinstance(highstate, dict) - ): + if state_id and state_result and highstate and isinstance(highstate, dict): onfails = search_onfail_requisites(state_id, highstate) if onfails: for handler in onfails: fstate, mod_, fchunk = handler for rstateid, rstate in six.iteritems(running): - if '_|-' in rstateid: + if "_|-" in rstateid: st = salt.state.split_low_tag(rstateid) # in case of simple state, try to guess else: - id_ = rstate.get('__id__', rstateid) + id_ = rstate.get("__id__", rstateid) if not id_: - raise ValueError('no state id') - st = {'__id__': id_, 'state': mod_} - if mod_ == st['state'] and fstate == st['__id__']: - ofresult = rstate.get('result', _empty) + raise ValueError("no state id") + st = {"__id__": id_, "state": mod_} + if mod_ == st["state"] and fstate == st["__id__"]: + ofresult = rstate.get("result", _empty) if ofresult in [False, True]: nret = ofresult if ofresult is False: @@ -124,10 +119,10 @@ def check_onfail_requisites(state_id, state_result, running, highstate): def check_result(running, recurse=False, highstate=None): - ''' + """ Check the total return value of the run and determine if the running dict has any issues - ''' + """ if not isinstance(running, dict): return False @@ -143,18 +138,18 @@ def check_result(running, recurse=False, highstate=None): if not recurse and not isinstance(state_result, expected_type): ret = False if ret and isinstance(state_result, dict): - result = state_result.get('result', _empty) + result = state_result.get("result", _empty) if result is False: ret = False # only override return value if we are not already failed elif result is _empty and isinstance(state_result, dict) and ret: - ret = check_result( - state_result, recurse=True, highstate=highstate) + ret = check_result(state_result, recurse=True, highstate=highstate) # if we detect a fail, check for onfail requisites if not ret: # ret can be None in case of no onfail reqs, recast it to bool - ret = bool(check_onfail_requisites(state_id, state_result, - running, highstate)) + ret = bool( + check_onfail_requisites(state_id, state_result, running, highstate) + ) # return as soon as we got a failure if not ret: break @@ -162,7 +157,7 @@ def check_result(running, recurse=False, highstate=None): def merge_subreturn(original_return, sub_return, subkey=None): - ''' + """ Update an existing state return (`original_return`) in place with another state return (`sub_return`), i.e. for a subresource. @@ -188,58 +183,58 @@ def merge_subreturn(original_return, sub_return, subkey=None): __utils__['state.merge_subreturn'](ret, _ret) if _ret['result'] is False: return ret - ''' + """ if not subkey: - subkey = sub_return['name'] + subkey = sub_return["name"] - if sub_return['result'] is False: + if sub_return["result"] is False: # True or None stay the same - original_return['result'] = sub_return['result'] + original_return["result"] = sub_return["result"] - sub_comment = sub_return['comment'] + sub_comment = sub_return["comment"] if not isinstance(sub_comment, list): sub_comment = [sub_comment] - original_return.setdefault('comment', []) - if isinstance(original_return['comment'], list): - original_return['comment'].extend(sub_comment) + original_return.setdefault("comment", []) + if isinstance(original_return["comment"], list): + original_return["comment"].extend(sub_comment) else: - if original_return['comment']: + if original_return["comment"]: # Skip for empty original comments - original_return['comment'] += '\n' - original_return['comment'] += '\n'.join(sub_comment) + original_return["comment"] += "\n" + original_return["comment"] += "\n".join(sub_comment) - if sub_return['changes']: # changes always exists - original_return.setdefault('changes', {}) - original_return['changes'][subkey] = sub_return['changes'] + if sub_return["changes"]: # changes always exists + original_return.setdefault("changes", {}) + original_return["changes"][subkey] = sub_return["changes"] return original_return def get_sls_opts(opts, **kwargs): - ''' + """ Return a copy of the opts for use, optionally load a local config on top - ''' + """ opts = copy.deepcopy(opts) - if 'localconfig' in kwargs: - return salt.config.minion_config(kwargs['localconfig'], defaults=opts) + if "localconfig" in kwargs: + return salt.config.minion_config(kwargs["localconfig"], defaults=opts) - if 'saltenv' in kwargs: - saltenv = kwargs['saltenv'] + if "saltenv" in kwargs: + saltenv = kwargs["saltenv"] if saltenv is not None: if not isinstance(saltenv, six.string_types): saltenv = six.text_type(saltenv) - if opts['lock_saltenv'] and saltenv != opts['saltenv']: + if opts["lock_saltenv"] and saltenv != opts["saltenv"]: raise CommandExecutionError( - 'lock_saltenv is enabled, saltenv cannot be changed' + "lock_saltenv is enabled, saltenv cannot be changed" ) - opts['saltenv'] = kwargs['saltenv'] + opts["saltenv"] = kwargs["saltenv"] - if 'pillarenv' in kwargs or opts.get('pillarenv_from_saltenv', False): - pillarenv = kwargs.get('pillarenv') or kwargs.get('saltenv') + if "pillarenv" in kwargs or opts.get("pillarenv_from_saltenv", False): + pillarenv = kwargs.get("pillarenv") or kwargs.get("saltenv") if pillarenv is not None and not isinstance(pillarenv, six.string_types): - opts['pillarenv'] = six.text_type(pillarenv) + opts["pillarenv"] = six.text_type(pillarenv) else: - opts['pillarenv'] = pillarenv + opts["pillarenv"] = pillarenv return opts diff --git a/salt/utils/stringio.py b/salt/utils/stringio.py index 902e9e8dc4a..ae70220d944 100644 --- a/salt/utils/stringio.py +++ b/salt/utils/stringio.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Functions for StringIO objects -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -13,10 +13,12 @@ from salt.ext import six if six.PY2: import StringIO import cStringIO + readable_types = (StringIO.StringIO, cStringIO.InputType) writable_types = (StringIO.StringIO, cStringIO.OutputType) else: import io + readable_types = (io.StringIO,) writable_types = (io.StringIO,) diff --git a/salt/utils/stringutils.py b/salt/utils/stringutils.py index dcd3beec5c7..961d0ddcde0 100644 --- a/salt/utils/stringutils.py +++ b/salt/utils/stringutils.py @@ -1,45 +1,46 @@ # -*- coding: utf-8 -*- -''' +""" Functions for manipulating or otherwise processing strings -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import difflib import errno import fnmatch import logging import os -import shlex import re +import shlex import time import unicodedata -# Import Salt libs -from salt.utils.decorators.jinja import jinja_filter - # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=redefined-builtin +# Import Salt libs +from salt.utils.decorators.jinja import jinja_filter + log = logging.getLogger(__name__) -@jinja_filter('to_bytes') -def to_bytes(s, encoding=None, errors='strict'): - ''' +@jinja_filter("to_bytes") +def to_bytes(s, encoding=None, errors="strict"): + """ Given bytes, bytearray, str, or unicode (python 2), return bytes (str for python 2) - ''' + """ if encoding is None: # Try utf-8 first, and fall back to detected encoding - encoding = ('utf-8', __salt_system_encoding__) + encoding = ("utf-8", __salt_system_encoding__) if not isinstance(encoding, (tuple, list)): encoding = (encoding,) if not encoding: - raise ValueError('encoding cannot be empty') + raise ValueError("encoding cannot be empty") exc = None if six.PY3: @@ -58,29 +59,30 @@ def to_bytes(s, encoding=None, errors='strict'): # raised, otherwise we would have already returned (or raised some # other exception). raise exc # pylint: disable=raising-bad-type - raise TypeError('expected str, bytes, or bytearray not {}'.format(type(s))) + raise TypeError("expected str, bytes, or bytearray not {}".format(type(s))) else: return to_str(s, encoding, errors) -def to_str(s, encoding=None, errors='strict', normalize=False): - ''' +def to_str(s, encoding=None, errors="strict", normalize=False): + """ Given str, bytes, bytearray, or unicode (py2), return str - ''' + """ + def _normalize(s): try: - return unicodedata.normalize('NFC', s) if normalize else s + return unicodedata.normalize("NFC", s) if normalize else s except TypeError: return s if encoding is None: # Try utf-8 first, and fall back to detected encoding - encoding = ('utf-8', __salt_system_encoding__) + encoding = ("utf-8", __salt_system_encoding__) if not isinstance(encoding, (tuple, list)): encoding = (encoding,) if not encoding: - raise ValueError('encoding cannot be empty') + raise ValueError("encoding cannot be empty") # This shouldn't be six.string_types because if we're on PY2 and we already # have a string, we should just return it. @@ -100,11 +102,12 @@ def to_str(s, encoding=None, errors='strict', normalize=False): # raised, otherwise we would have already returned (or raised some # other exception). raise exc # pylint: disable=raising-bad-type - raise TypeError('expected str, bytes, or bytearray not {}'.format(type(s))) + raise TypeError("expected str, bytes, or bytearray not {}".format(type(s))) else: if isinstance(s, bytearray): return str(s) # future lint: disable=blacklisted-function - if isinstance(s, unicode): # pylint: disable=incompatible-py3-code,undefined-variable + # pylint: disable=incompatible-py3-code,undefined-variable + if isinstance(s, unicode): for enc in encoding: try: return _normalize(s).encode(enc, errors) @@ -115,24 +118,26 @@ def to_str(s, encoding=None, errors='strict', normalize=False): # raised, otherwise we would have already returned (or raised some # other exception). raise exc # pylint: disable=raising-bad-type - raise TypeError('expected str, bytes, or bytearray not {}'.format(type(s))) + # pylint: enable=incompatible-py3-code,undefined-variable + raise TypeError("expected str, bytes, or bytearray not {}".format(type(s))) -def to_unicode(s, encoding=None, errors='strict', normalize=False): - ''' +def to_unicode(s, encoding=None, errors="strict", normalize=False): + """ Given str or unicode, return unicode (str for python 3) - ''' + """ + def _normalize(s): - return unicodedata.normalize('NFC', s) if normalize else s + return unicodedata.normalize("NFC", s) if normalize else s if encoding is None: # Try utf-8 first, and fall back to detected encoding - encoding = ('utf-8', __salt_system_encoding__) + encoding = ("utf-8", __salt_system_encoding__) if not isinstance(encoding, (tuple, list)): encoding = (encoding,) if not encoding: - raise ValueError('encoding cannot be empty') + raise ValueError("encoding cannot be empty") exc = None if six.PY3: @@ -140,12 +145,13 @@ def to_unicode(s, encoding=None, errors='strict', normalize=False): return _normalize(s) elif isinstance(s, (bytes, bytearray)): return _normalize(to_str(s, encoding, errors)) - raise TypeError('expected str, bytes, or bytearray not {}'.format(type(s))) + raise TypeError("expected str, bytes, or bytearray not {}".format(type(s))) else: # This needs to be str and not six.string_types, since if the string is # already a unicode type, it does not need to be decoded (and doing so # will raise an exception). - if isinstance(s, unicode): # pylint: disable=incompatible-py3-code,undefined-variable + # pylint: disable=incompatible-py3-code + if isinstance(s, unicode): # pylint: disable=E0602 return _normalize(s) elif isinstance(s, (str, bytearray)): for enc in encoding: @@ -158,18 +164,19 @@ def to_unicode(s, encoding=None, errors='strict', normalize=False): # raised, otherwise we would have already returned (or raised some # other exception). raise exc # pylint: disable=raising-bad-type - raise TypeError('expected str, bytes, or bytearray not {}'.format(type(s))) + # pylint: enable=incompatible-py3-code + raise TypeError("expected str, bytes, or bytearray not {}".format(type(s))) -@jinja_filter('str_to_num') -@jinja_filter('to_num') +@jinja_filter("str_to_num") +@jinja_filter("to_num") def to_num(text): - ''' + """ Convert a string to a number. Returns an integer if the string represents an integer, a floating point number if the string is a real number, or the string unchanged otherwise. - ''' + """ try: return int(text) except ValueError: @@ -180,41 +187,43 @@ def to_num(text): def to_none(text): - ''' + """ Convert a string to None if the string is empty or contains only spaces. - ''' + """ if six.text_type(text).strip(): return text return None def is_quoted(value): - ''' + """ Return a single or double quote, if a string is wrapped in extra quotes. Otherwise return an empty string. - ''' - ret = '' - if isinstance(value, six.string_types) \ - and value[0] == value[-1] \ - and value.startswith(('\'', '"')): + """ + ret = "" + if ( + isinstance(value, six.string_types) + and value[0] == value[-1] + and value.startswith(("'", '"')) + ): ret = value[0] return ret def dequote(value): - ''' + """ Remove extra quotes around a string. - ''' + """ if is_quoted(value): return value[1:-1] return value -@jinja_filter('is_hex') +@jinja_filter("is_hex") def is_hex(value): - ''' + """ Returns True if value is a hexidecimal string, otherwise returns False - ''' + """ try: int(value, 16) return True @@ -223,33 +232,37 @@ def is_hex(value): def is_binary(data): - ''' + """ Detects if the passed string of data is binary or text - ''' + """ if not data or not isinstance(data, (six.string_types, six.binary_type)): return False if isinstance(data, six.binary_type): - if b'\0' in data: + if b"\0" in data: return True - elif str('\0') in data: + elif str("\0") in data: return True - text_characters = ''.join([chr(x) for x in range(32, 127)] + list('\n\r\t\b')) + text_characters = "".join([chr(x) for x in range(32, 127)] + list("\n\r\t\b")) # Get the non-text characters (map each character to itself then use the # 'remove' option to get rid of the text characters.) if six.PY3: if isinstance(data, six.binary_type): import salt.utils.data + nontext = data.translate(None, salt.utils.data.encode(text_characters)) else: - trans = ''.maketrans('', '', text_characters) + trans = "".maketrans("", "", text_characters) nontext = data.translate(trans) else: if isinstance(data, six.text_type): trans_args = ({ord(x): None for x in text_characters},) else: - trans_args = (None, str(text_characters)) # future lint: blacklisted-function + trans_args = ( + None, + str(text_characters), + ) # future lint: blacklisted-function nontext = data.translate(*trans_args) # If more than 30% non-text characters, then @@ -259,39 +272,39 @@ def is_binary(data): return False -@jinja_filter('random_str') +@jinja_filter("random_str") def random(size=32): key = os.urandom(size) - return to_unicode(base64.b64encode(key).replace(b'\n', b'')[:size]) + return to_unicode(base64.b64encode(key).replace(b"\n", b"")[:size]) -@jinja_filter('contains_whitespace') +@jinja_filter("contains_whitespace") def contains_whitespace(text): - ''' + """ Returns True if there are any whitespace characters in the string - ''' + """ return any(x.isspace() for x in text) def human_to_bytes(size): - ''' + """ Given a human-readable byte string (e.g. 2G, 30M), return the number of bytes. Will return 0 if the argument has unexpected form. .. versionadded:: 2018.3.0 - ''' + """ sbytes = size[:-1] unit = size[-1] if sbytes.isdigit(): sbytes = int(sbytes) - if unit == 'P': + if unit == "P": sbytes *= 1125899906842624 - elif unit == 'T': + elif unit == "T": sbytes *= 1099511627776 - elif unit == 'G': + elif unit == "G": sbytes *= 1073741824 - elif unit == 'M': + elif unit == "M": sbytes *= 1048576 else: sbytes = 0 @@ -329,27 +342,28 @@ def build_whitespace_split_regex(text): >>> ''' + def __build_parts(text): lexer = shlex.shlex(text) lexer.whitespace_split = True - lexer.commenters = '' + lexer.commenters = "" if r"'\"" in text: - lexer.quotes = '' - elif '\'' in text: + lexer.quotes = "" + elif "'" in text: lexer.quotes = '"' elif '"' in text: - lexer.quotes = '\'' + lexer.quotes = "'" return list(lexer) - regex = r'' + regex = r"" for line in text.splitlines(): parts = [re.escape(s) for s in __build_parts(line)] - regex += r'(?:[\s]+)?{0}(?:[\s]+)?'.format(r'(?:[\s]+)?'.join(parts)) - return r'(?m)^{0}$'.format(regex) + regex += r"(?:[\s]+)?{0}(?:[\s]+)?".format(r"(?:[\s]+)?".join(parts)) + return r"(?m)^{0}$".format(regex) def expr_match(line, expr): - ''' + """ Checks whether or not the passed value matches the specified expression. Tries to match expr first as a glob using fnmatch.fnmatch(), and then tries to match expr as a regular expression. Originally designed to match minion @@ -362,23 +376,23 @@ def expr_match(line, expr): >>> fnmatch.fnmatch('foo', 'foo') True - ''' + """ try: if fnmatch.fnmatch(line, expr): return True try: - if re.match(r'\A{0}\Z'.format(expr), line): + if re.match(r"\A{0}\Z".format(expr), line): return True except re.error: pass except TypeError: - log.exception('Value %r or expression %r is not a string', line, expr) + log.exception("Value %r or expression %r is not a string", line, expr) return False -@jinja_filter('check_whitelist_blacklist') +@jinja_filter("check_whitelist_blacklist") def check_whitelist_blacklist(value, whitelist=None, blacklist=None): - ''' + """ Check a whitelist and/or blacklist to see if the value matches it. value @@ -398,14 +412,14 @@ def check_whitelist_blacklist(value, whitelist=None, blacklist=None): in the blacklist will be examined first. If the value is not found in the blacklist, then the whitelist is checked. If the value isn't found in the whitelist, the function returns ``False``. - ''' + """ # Normalize the input so that we have a list if blacklist: if isinstance(blacklist, six.string_types): blacklist = [blacklist] - if not hasattr(blacklist, '__iter__'): + if not hasattr(blacklist, "__iter__"): raise TypeError( - 'Expecting iterable blacklist, but got {0} ({1})'.format( + "Expecting iterable blacklist, but got {0} ({1})".format( type(blacklist).__name__, blacklist ) ) @@ -415,9 +429,9 @@ def check_whitelist_blacklist(value, whitelist=None, blacklist=None): if whitelist: if isinstance(whitelist, six.string_types): whitelist = [whitelist] - if not hasattr(whitelist, '__iter__'): + if not hasattr(whitelist, "__iter__"): raise TypeError( - 'Expecting iterable whitelist, but got {0} ({1})'.format( + "Expecting iterable whitelist, but got {0} ({1})".format( type(whitelist).__name__, whitelist ) ) @@ -442,7 +456,7 @@ def check_whitelist_blacklist(value, whitelist=None, blacklist=None): def check_include_exclude(path_str, include_pat=None, exclude_pat=None): - ''' + """ Check for glob or regexp patterns for include_pat and exclude_pat in the 'path_str' string and return True/False conditions as follows. - Default: return 'True' if no include_pat or exclude_pat patterns are @@ -451,32 +465,20 @@ def check_include_exclude(path_str, include_pat=None, exclude_pat=None): passes the include_pat test or fails exclude_pat test respectively - If both include_pat and exclude_pat are supplied: return 'True' if include_pat matches AND exclude_pat does not match - ''' + """ ret = True # -- default true # Before pattern match, check if it is regexp (E@'') or glob(default) if include_pat: - if re.match('E@', include_pat): - retchk_include = True if re.search( - include_pat[2:], - path_str - ) else False + if re.match("E@", include_pat): + retchk_include = True if re.search(include_pat[2:], path_str) else False else: - retchk_include = True if fnmatch.fnmatch( - path_str, - include_pat - ) else False + retchk_include = True if fnmatch.fnmatch(path_str, include_pat) else False if exclude_pat: - if re.match('E@', exclude_pat): - retchk_exclude = False if re.search( - exclude_pat[2:], - path_str - ) else True + if re.match("E@", exclude_pat): + retchk_exclude = False if re.search(exclude_pat[2:], path_str) else True else: - retchk_exclude = False if fnmatch.fnmatch( - path_str, - exclude_pat - ) else True + retchk_exclude = False if fnmatch.fnmatch(path_str, exclude_pat) else True # Now apply include/exclude conditions if include_pat and not exclude_pat: @@ -492,24 +494,22 @@ def check_include_exclude(path_str, include_pat=None, exclude_pat=None): def print_cli(msg, retries=10, step=0.01): - ''' + """ Wrapper around print() that suppresses tracebacks on broken pipes (i.e. when salt output is piped to less and less is stopped prematurely). - ''' + """ while retries: try: try: print(msg) except UnicodeEncodeError: - print(msg.encode('utf-8')) + print(msg.encode("utf-8")) except IOError as exc: err = "{0}".format(exc) if exc.errno != errno.EPIPE: if ( - ("temporarily unavailable" in err or - exc.errno in (errno.EAGAIN,)) and - retries - ): + "temporarily unavailable" in err or exc.errno in (errno.EAGAIN,) + ) and retries: time.sleep(step) retries -= 1 continue @@ -519,11 +519,11 @@ def print_cli(msg, retries=10, step=0.01): def get_context(template, line, num_lines=5, marker=None): - ''' + """ Returns debugging context around a line in a given string Returns:: string - ''' + """ template_lines = template.splitlines() num_template_lines = len(template_lines) @@ -539,60 +539,64 @@ def get_context(template, line, num_lines=5, marker=None): buf = [] if context_start > 0: - buf.append('[...]') + buf.append("[...]") error_line_in_context += 1 buf.extend(template_lines[context_start:context_end]) if context_end < num_template_lines: - buf.append('[...]') + buf.append("[...]") if marker: buf[error_line_in_context] += marker - return '---\n{0}\n---'.format('\n'.join(buf)) + return "---\n{0}\n---".format("\n".join(buf)) def get_diff(a, b, *args, **kwargs): - ''' + """ Perform diff on two iterables containing lines from two files, and return the diff as as string. Lines are normalized to str types to avoid issues with unicode on PY2. - ''' - encoding = ('utf-8', 'latin-1', __salt_system_encoding__) + """ + encoding = ("utf-8", "latin-1", __salt_system_encoding__) # Late import to avoid circular import import salt.utils.data - return ''.join( + + return "".join( difflib.unified_diff( salt.utils.data.decode_list(a, encoding=encoding), salt.utils.data.decode_list(b, encoding=encoding), - *args, **kwargs + *args, + **kwargs ) ) -@jinja_filter('to_snake_case') +@jinja_filter("to_snake_case") def camel_to_snake_case(camel_input): - ''' + """ Converts camelCase (or CamelCase) to snake_case. From https://codereview.stackexchange.com/questions/185966/functions-to-convert-camelcase-strings-to-snake-case :param str camel_input: The camelcase or CamelCase string to convert to snake_case :return str - ''' + """ res = camel_input[0].lower() for i, letter in enumerate(camel_input[1:], 1): if letter.isupper(): - if camel_input[i-1].islower() or (i != len(camel_input)-1 and camel_input[i+1].islower()): - res += '_' + if camel_input[i - 1].islower() or ( + i != len(camel_input) - 1 and camel_input[i + 1].islower() + ): + res += "_" res += letter.lower() return res -@jinja_filter('to_camelcase') +@jinja_filter("to_camelcase") def snake_to_camel_case(snake_input, uppercamel=False): - ''' + """ Converts snake_case to camelCase (or CamelCase if uppercamel is ``True``). Inspired by https://codereview.stackexchange.com/questions/85311/transform-snake-case-to-camelcase @@ -600,8 +604,8 @@ def snake_to_camel_case(snake_input, uppercamel=False): :param bool uppercamel: Whether or not to convert to CamelCase instead :return str - ''' - words = snake_input.split('_') + """ + words = snake_input.split("_") if uppercamel: words[0] = words[0].capitalize() - return words[0] + ''.join(word.capitalize() for word in words[1:]) + return words[0] + "".join(word.capitalize() for word in words[1:]) diff --git a/salt/utils/systemd.py b/salt/utils/systemd.py index 060bc1e3fbd..e830d36ed4c 100644 --- a/salt/utils/systemd.py +++ b/salt/utils/systemd.py @@ -1,41 +1,43 @@ # -*- coding: utf-8 -*- -''' +""" Contains systemd related help files -''' +""" # import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import re import subprocess +import salt.utils.stringutils + # Import Salt libs from salt.exceptions import SaltInvocationError -import salt.utils.stringutils log = logging.getLogger(__name__) def booted(context=None): - ''' + """ Return True if the system was booted with systemd, False otherwise. If the loader context dict ``__context__`` is passed, this function will set the ``salt.utils.systemd.booted`` key to represent if systemd is running and keep the logic below from needing to be run again during the same salt run. - ''' - contextkey = 'salt.utils.systemd.booted' + """ + contextkey = "salt.utils.systemd.booted" if isinstance(context, dict): # Can't put this if block on the same line as the above if block, # because it willl break the elif below. if contextkey in context: return context[contextkey] elif context is not None: - raise SaltInvocationError('context must be a dictionary if passed') + raise SaltInvocationError("context must be a dictionary if passed") try: # This check does the same as sd_booted() from libsystemd-daemon: # http://www.freedesktop.org/software/systemd/man/sd_booted.html - ret = bool(os.stat('/run/systemd/system')) + ret = bool(os.stat("/run/systemd/system")) except OSError: ret = False @@ -48,29 +50,32 @@ def booted(context=None): def version(context=None): - ''' + """ Attempts to run systemctl --version. Returns None if unable to determine version. - ''' - contextkey = 'salt.utils.systemd.version' + """ + contextkey = "salt.utils.systemd.version" if isinstance(context, dict): # Can't put this if block on the same line as the above if block, # because it will break the elif below. if contextkey in context: return context[contextkey] elif context is not None: - raise SaltInvocationError('context must be a dictionary if passed') + raise SaltInvocationError("context must be a dictionary if passed") stdout = subprocess.Popen( - ['systemctl', '--version'], + ["systemctl", "--version"], close_fds=True, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0] + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ).communicate()[0] outstr = salt.utils.stringutils.to_str(stdout) try: - ret = int(re.search(r'\w+ ([0-9]+)', outstr.splitlines()[0]).group(1)) + ret = int(re.search(r"\w+ ([0-9]+)", outstr.splitlines()[0]).group(1)) except (AttributeError, IndexError, ValueError): log.error( - 'Unable to determine systemd version from systemctl ' - '--version, output follows:\n%s', outstr + "Unable to determine systemd version from systemctl " + "--version, output follows:\n%s", + outstr, ) return None else: @@ -82,10 +87,10 @@ def version(context=None): def has_scope(context=None): - ''' + """ Scopes were introduced in systemd 205, this function returns a boolean which is true when the minion is systemd-booted and running systemd>=205. - ''' + """ if not booted(context): return False _sd_version = version(context) diff --git a/salt/utils/templates.py b/salt/utils/templates.py index 98092a9d796..f6e3f1f99a4 100644 --- a/salt/utils/templates.py +++ b/salt/utils/templates.py @@ -1,61 +1,62 @@ # -*- coding: utf-8 -*- -''' +""" Template render systems -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import codecs -import os import logging +import os +import sys import tempfile import traceback -import sys # Import 3rd-party libs import jinja2 import jinja2.ext -from salt.ext import six - -if sys.version_info[:2] >= (3, 5): - import importlib.machinery # pylint: disable=no-name-in-module,import-error - import importlib.util # pylint: disable=no-name-in-module,import-error - USE_IMPORTLIB = True -else: - import imp - USE_IMPORTLIB = False # Import Salt libs import salt.utils.data import salt.utils.dateutils -import salt.utils.http import salt.utils.files -import salt.utils.platform -import salt.utils.yamlencoding import salt.utils.hashutils -import salt.utils.stringutils -from salt.exceptions import ( - SaltRenderError, CommandExecutionError, SaltInvocationError -) +import salt.utils.http import salt.utils.jinja import salt.utils.network -from salt.utils.odict import OrderedDict -from salt.utils.decorators.jinja import JinjaFilter, JinjaTest, JinjaGlobal +import salt.utils.platform +import salt.utils.stringutils +import salt.utils.yamlencoding from salt import __path__ as saltpath +from salt.exceptions import CommandExecutionError, SaltInvocationError, SaltRenderError +from salt.ext import six +from salt.utils.decorators.jinja import JinjaFilter, JinjaGlobal, JinjaTest +from salt.utils.odict import OrderedDict + +if sys.version_info[:2] >= (3, 5): + import importlib.machinery # pylint: disable=no-name-in-module,import-error + import importlib.util # pylint: disable=no-name-in-module,import-error + + USE_IMPORTLIB = True +else: + import imp + + USE_IMPORTLIB = False + log = logging.getLogger(__name__) -TEMPLATE_DIRNAME = os.path.join(saltpath[0], 'templates') +TEMPLATE_DIRNAME = os.path.join(saltpath[0], "templates") # FIXME: also in salt/template.py -SLS_ENCODING = 'utf-8' # this one has no BOM. +SLS_ENCODING = "utf-8" # this one has no BOM. SLS_ENCODER = codecs.getencoder(SLS_ENCODING) class AliasedLoader(object): - ''' + """ Light wrapper around the LazyLoader to redirect 'cmd.run' calls to 'cmd.shell', for easy use of shellisms during templating calls @@ -64,7 +65,7 @@ class AliasedLoader(object): Non-dotted aliases ('cmd') must resolve to a dictionary of function aliases for that module (e.g. {'run': 'shell'}) - ''' + """ def __init__(self, wrapped): self.wrapped = wrapped @@ -80,13 +81,14 @@ class AliasedLoader(object): class AliasedModule(object): - ''' + """ Light wrapper around module objects returned by the LazyLoader's getattr for the purposes of `salt.cmd.run()` syntax in templates Allows for aliasing specific functions, such as `run` to `shell` for easy use of shellisms during templating calls - ''' + """ + def __init__(self, wrapped, aliases): self.aliases = aliases self.wrapped = wrapped @@ -96,49 +98,45 @@ class AliasedModule(object): def wrap_tmpl_func(render_str): - - def render_tmpl(tmplsrc, - from_str=False, - to_str=False, - context=None, - tmplpath=None, - **kws): + def render_tmpl( + tmplsrc, from_str=False, to_str=False, context=None, tmplpath=None, **kws + ): if context is None: context = {} # Alias cmd.run to cmd.shell to make python_shell=True the default for # templated calls - if 'salt' in kws: - kws['salt'] = AliasedLoader(kws['salt']) + if "salt" in kws: + kws["salt"] = AliasedLoader(kws["salt"]) # We want explicit context to overwrite the **kws kws.update(context) context = kws - assert 'opts' in context - assert 'saltenv' in context + assert "opts" in context + assert "saltenv" in context - if 'sls' in context: - slspath = context['sls'].replace('.', '/') + if "sls" in context: + slspath = context["sls"].replace(".", "/") if tmplpath is not None: - context['tplpath'] = tmplpath - if not tmplpath.lower().replace('\\', '/').endswith('/init.sls'): + context["tplpath"] = tmplpath + if not tmplpath.lower().replace("\\", "/").endswith("/init.sls"): slspath = os.path.dirname(slspath) - template = tmplpath.replace('\\', '/') - i = template.rfind(slspath.replace('.', '/')) + template = tmplpath.replace("\\", "/") + i = template.rfind(slspath.replace(".", "/")) if i != -1: template = template[i:] - tpldir = os.path.dirname(template).replace('\\', '/') + tpldir = os.path.dirname(template).replace("\\", "/") tpldata = { - 'tplfile': template, - 'tpldir': '.' if tpldir == '' else tpldir, - 'tpldot': tpldir.replace('/', '.'), + "tplfile": template, + "tpldir": "." if tpldir == "" else tpldir, + "tpldot": tpldir.replace("/", "."), } context.update(tpldata) - context['slsdotpath'] = slspath.replace('/', '.') - context['slscolonpath'] = slspath.replace('/', ':') - context['sls_path'] = slspath.replace('/', '_') - context['slspath'] = slspath + context["slsdotpath"] = slspath.replace("/", ".") + context["slscolonpath"] = slspath.replace("/", ":") + context["sls_path"] = slspath.replace("/", "_") + context["slspath"] = slspath if isinstance(tmplsrc, six.string_types): if from_str: @@ -147,19 +145,17 @@ def wrap_tmpl_func(render_str): try: if tmplpath is not None: tmplsrc = os.path.join(tmplpath, tmplsrc) - with codecs.open(tmplsrc, 'r', SLS_ENCODING) as _tmplsrc: + with codecs.open(tmplsrc, "r", SLS_ENCODING) as _tmplsrc: tmplstr = _tmplsrc.read() - except (UnicodeDecodeError, - ValueError, - OSError, - IOError) as exc: + except (UnicodeDecodeError, ValueError, OSError, IOError) as exc: if salt.utils.files.is_binary(tmplsrc): # Template is a bin file, return the raw file return dict(result=True, data=tmplsrc) log.error( - 'Exception occurred while reading file %s: %s', - tmplsrc, exc, - exc_info_on_loglevel=logging.DEBUG + "Exception occurred while reading file %s: %s", + tmplsrc, + exc, + exc_info_on_loglevel=logging.DEBUG, ) six.reraise(*sys.exc_info()) else: # assume tmplsrc is file-like. @@ -169,7 +165,9 @@ def wrap_tmpl_func(render_str): output = render_str(tmplstr, context, tmplpath) if salt.utils.platform.is_windows(): newline = False - if salt.utils.stringutils.to_unicode(output, encoding=SLS_ENCODING).endswith(('\n', os.linesep)): + if salt.utils.stringutils.to_unicode( + output, encoding=SLS_ENCODING + ).endswith(("\n", os.linesep)): newline = True # Write out with Windows newlines output = os.linesep.join(output.splitlines()) @@ -177,16 +175,20 @@ def wrap_tmpl_func(render_str): output += os.linesep except SaltRenderError as exc: - log.exception('Rendering exception occurred') - #return dict(result=False, data=six.text_type(exc)) + log.exception("Rendering exception occurred") + # return dict(result=False, data=six.text_type(exc)) raise except Exception: # pylint: disable=broad-except return dict(result=False, data=traceback.format_exc()) else: if to_str: # then render as string return dict(result=True, data=output) - with tempfile.NamedTemporaryFile('wb', delete=False, prefix=salt.utils.files.TEMPFILE_PREFIX) as outf: - outf.write(salt.utils.stringutils.to_bytes(output, encoding=SLS_ENCODING)) + with tempfile.NamedTemporaryFile( + "wb", delete=False, prefix=salt.utils.files.TEMPFILE_PREFIX + ) as outf: + outf.write( + salt.utils.stringutils.to_bytes(output, encoding=SLS_ENCODING) + ) # Note: If nothing is replaced or added by the rendering # function, then the contents of the output file will # be exactly the same as the input. @@ -197,36 +199,35 @@ def wrap_tmpl_func(render_str): def _get_jinja_error_slug(tb_data): - ''' + """ Return the line number where the template error was found - ''' + """ try: return [ x - for x in tb_data if x[2] in ('top-level template code', - 'template', - '<module>') + for x in tb_data + if x[2] in ("top-level template code", "template", "<module>") ][-1] except IndexError: pass def _get_jinja_error_message(tb_data): - ''' + """ Return an understandable message from jinja error output - ''' + """ try: line = _get_jinja_error_slug(tb_data) - return '{0}({1}):\n{3}'.format(*line) + return "{0}({1}):\n{3}".format(*line) except IndexError: pass return None def _get_jinja_error_line(tb_data): - ''' + """ Return the line number where the template error was found - ''' + """ try: return _get_jinja_error_slug(tb_data)[1] except IndexError: @@ -235,15 +236,15 @@ def _get_jinja_error_line(tb_data): def _get_jinja_error(trace, context=None): - ''' + """ Return the error line and error message output from a stacktrace. If we are in a macro, also output inside the message the exact location of the error in the macro - ''' + """ if not context: context = {} - out = '' + out = "" error = _get_jinja_error_slug(trace) line = _get_jinja_error_line(trace) msg = _get_jinja_error_message(trace) @@ -253,20 +254,14 @@ def _get_jinja_error(trace, context=None): # resolve the filename add_log = False template_path = None - if 'sls' not in context: - if ( - (error[0] != '<unknown>') - and os.path.exists(error[0]) - ): + if "sls" not in context: + if (error[0] != "<unknown>") and os.path.exists(error[0]): template_path = error[0] add_log = True else: # the offender error is not from the called sls - filen = context['sls'].replace('.', '/') - if ( - not error[0].endswith(filen) - and os.path.exists(error[0]) - ): + filen = context["sls"].replace(".", "/") + if not error[0].endswith(filen) and os.path.exists(error[0]): add_log = True template_path = error[0] # if we add a log, format explicitly the exception here @@ -274,22 +269,21 @@ def _get_jinja_error(trace, context=None): # error log place at the beginning if add_log: if template_path: - out = '\n{0}\n'.format(msg.splitlines()[0]) + out = "\n{0}\n".format(msg.splitlines()[0]) with salt.utils.files.fopen(template_path) as fp_: template_contents = salt.utils.stringutils.to_unicode(fp_.read()) out += salt.utils.stringutils.get_context( - template_contents, - line, - marker=' <======================') + template_contents, line, marker=" <======================" + ) else: - out = '\n{0}\n'.format(msg) + out = "\n{0}\n".format(msg) line = 0 return line, out def render_jinja_tmpl(tmplstr, context, tmplpath=None): - opts = context['opts'] - saltenv = context['saltenv'] + opts = context["opts"] + saltenv = context["saltenv"] loader = None newline = False @@ -299,27 +293,29 @@ def render_jinja_tmpl(tmplstr, context, tmplpath=None): if tmplstr.endswith(os.linesep): newline = os.linesep - elif tmplstr.endswith('\n'): - newline = '\n' + elif tmplstr.endswith("\n"): + newline = "\n" if not saltenv: if tmplpath: loader = jinja2.FileSystemLoader(os.path.dirname(tmplpath)) else: - loader = salt.utils.jinja.SaltCacheLoader(opts, saltenv, pillar_rend=context.get('_pillar_rend', False)) + loader = salt.utils.jinja.SaltCacheLoader( + opts, saltenv, pillar_rend=context.get("_pillar_rend", False) + ) - env_args = {'extensions': [], 'loader': loader} + env_args = {"extensions": [], "loader": loader} - if hasattr(jinja2.ext, 'with_'): - env_args['extensions'].append('jinja2.ext.with_') - if hasattr(jinja2.ext, 'do'): - env_args['extensions'].append('jinja2.ext.do') - if hasattr(jinja2.ext, 'loopcontrols'): - env_args['extensions'].append('jinja2.ext.loopcontrols') - env_args['extensions'].append(salt.utils.jinja.SerializerExtension) + if hasattr(jinja2.ext, "with_"): + env_args["extensions"].append("jinja2.ext.with_") + if hasattr(jinja2.ext, "do"): + env_args["extensions"].append("jinja2.ext.do") + if hasattr(jinja2.ext, "loopcontrols"): + env_args["extensions"].append("jinja2.ext.loopcontrols") + env_args["extensions"].append(salt.utils.jinja.SerializerExtension) - opt_jinja_env = opts.get('jinja_env', {}) - opt_jinja_sls_env = opts.get('jinja_sls_env', {}) + opt_jinja_env = opts.get("jinja_env", {}) + opt_jinja_sls_env = opts.get("jinja_sls_env", {}) opt_jinja_env = opt_jinja_env if isinstance(opt_jinja_env, dict) else {} opt_jinja_sls_env = opt_jinja_sls_env if isinstance(opt_jinja_sls_env, dict) else {} @@ -328,50 +324,53 @@ def render_jinja_tmpl(tmplstr, context, tmplpath=None): # trim_blocks removes newlines around Jinja blocks # lstrip_blocks strips tabs and spaces from the beginning of # line to the start of a block. - if opts.get('jinja_trim_blocks', False): - log.debug('Jinja2 trim_blocks is enabled') - log.warning('jinja_trim_blocks is deprecated and will be removed in a future release, please use jinja_env and/or jinja_sls_env instead') - opt_jinja_env['trim_blocks'] = True - opt_jinja_sls_env['trim_blocks'] = True - if opts.get('jinja_lstrip_blocks', False): - log.debug('Jinja2 lstrip_blocks is enabled') - log.warning('jinja_lstrip_blocks is deprecated and will be removed in a future release, please use jinja_env and/or jinja_sls_env instead') - opt_jinja_env['lstrip_blocks'] = True - opt_jinja_sls_env['lstrip_blocks'] = True + if opts.get("jinja_trim_blocks", False): + log.debug("Jinja2 trim_blocks is enabled") + log.warning( + "jinja_trim_blocks is deprecated and will be removed in a future release, please use jinja_env and/or jinja_sls_env instead" + ) + opt_jinja_env["trim_blocks"] = True + opt_jinja_sls_env["trim_blocks"] = True + if opts.get("jinja_lstrip_blocks", False): + log.debug("Jinja2 lstrip_blocks is enabled") + log.warning( + "jinja_lstrip_blocks is deprecated and will be removed in a future release, please use jinja_env and/or jinja_sls_env instead" + ) + opt_jinja_env["lstrip_blocks"] = True + opt_jinja_sls_env["lstrip_blocks"] = True def opt_jinja_env_helper(opts, optname): for k, v in six.iteritems(opts): k = k.lower() if hasattr(jinja2.defaults, k.upper()): - log.debug('Jinja2 environment %s was set to %s by %s', k, v, optname) + log.debug("Jinja2 environment %s was set to %s by %s", k, v, optname) env_args[k] = v else: - log.warning('Jinja2 environment %s is not recognized', k) + log.warning("Jinja2 environment %s is not recognized", k) - if 'sls' in context and context['sls'] != '': - opt_jinja_env_helper(opt_jinja_sls_env, 'jinja_sls_env') + if "sls" in context and context["sls"] != "": + opt_jinja_env_helper(opt_jinja_sls_env, "jinja_sls_env") else: - opt_jinja_env_helper(opt_jinja_env, 'jinja_env') + opt_jinja_env_helper(opt_jinja_env, "jinja_env") - if opts.get('allow_undefined', False): + if opts.get("allow_undefined", False): jinja_env = jinja2.Environment(**env_args) else: - jinja_env = jinja2.Environment(undefined=jinja2.StrictUndefined, - **env_args) + jinja_env = jinja2.Environment(undefined=jinja2.StrictUndefined, **env_args) - tojson_filter = jinja_env.filters.get('tojson') + tojson_filter = jinja_env.filters.get("tojson") jinja_env.tests.update(JinjaTest.salt_jinja_tests) jinja_env.filters.update(JinjaFilter.salt_jinja_filters) if tojson_filter is not None: # Use the existing tojson filter, if present (jinja2 >= 2.9) - jinja_env.filters['tojson'] = tojson_filter + jinja_env.filters["tojson"] = tojson_filter jinja_env.globals.update(JinjaGlobal.salt_jinja_globals) # globals - jinja_env.globals['odict'] = OrderedDict - jinja_env.globals['show_full_context'] = salt.utils.jinja.show_full_context + jinja_env.globals["odict"] = OrderedDict + jinja_env.globals["show_full_context"] = salt.utils.jinja.show_full_context - jinja_env.tests['list'] = salt.utils.data.is_list + jinja_env.tests["list"] = salt.utils.data.is_list decoded_context = {} for key, value in six.iteritems(context): @@ -380,7 +379,9 @@ def render_jinja_tmpl(tmplstr, context, tmplpath=None): continue try: - decoded_context[key] = salt.utils.stringutils.to_unicode(value, encoding=SLS_ENCODING) + decoded_context[key] = salt.utils.stringutils.to_unicode( + value, encoding=SLS_ENCODING + ) except UnicodeDecodeError as ex: log.debug( "Failed to decode using default encoding (%s), trying system encoding", @@ -395,52 +396,49 @@ def render_jinja_tmpl(tmplstr, context, tmplpath=None): except jinja2.exceptions.UndefinedError as exc: trace = traceback.extract_tb(sys.exc_info()[2]) out = _get_jinja_error(trace, context=decoded_context)[1] - tmplstr = '' + tmplstr = "" # Don't include the line number, since it is misreported # https://github.com/mitsuhiko/jinja2/issues/276 - raise SaltRenderError( - 'Jinja variable {0}{1}'.format( - exc, out), - buf=tmplstr) - except (jinja2.exceptions.TemplateRuntimeError, - jinja2.exceptions.TemplateSyntaxError) as exc: + raise SaltRenderError("Jinja variable {0}{1}".format(exc, out), buf=tmplstr) + except ( + jinja2.exceptions.TemplateRuntimeError, + jinja2.exceptions.TemplateSyntaxError, + ) as exc: trace = traceback.extract_tb(sys.exc_info()[2]) line, out = _get_jinja_error(trace, context=decoded_context) if not line: - tmplstr = '' + tmplstr = "" raise SaltRenderError( - 'Jinja syntax error: {0}{1}'.format(exc, out), - line, - tmplstr) + "Jinja syntax error: {0}{1}".format(exc, out), line, tmplstr + ) except (SaltInvocationError, CommandExecutionError) as exc: trace = traceback.extract_tb(sys.exc_info()[2]) line, out = _get_jinja_error(trace, context=decoded_context) if not line: - tmplstr = '' + tmplstr = "" raise SaltRenderError( - 'Problem running salt function in Jinja template: {0}{1}'.format( - exc, out), + "Problem running salt function in Jinja template: {0}{1}".format(exc, out), line, - tmplstr) + tmplstr, + ) except Exception as exc: # pylint: disable=broad-except tracestr = traceback.format_exc() trace = traceback.extract_tb(sys.exc_info()[2]) line, out = _get_jinja_error(trace, context=decoded_context) if not line: - tmplstr = '' + tmplstr = "" else: - tmplstr += '\n{0}'.format(tracestr) - log.debug('Jinja Error') - log.debug('Exception:', exc_info=True) - log.debug('Out: %s', out) - log.debug('Line: %s', line) - log.debug('TmplStr: %s', tmplstr) - log.debug('TraceStr: %s', tracestr) + tmplstr += "\n{0}".format(tracestr) + log.debug("Jinja Error") + log.debug("Exception:", exc_info=True) + log.debug("Out: %s", out) + log.debug("Line: %s", line) + log.debug("TmplStr: %s", tmplstr) + log.debug("TraceStr: %s", tracestr) - raise SaltRenderError('Jinja error: {0}{1}'.format(exc, out), - line, - tmplstr, - trace=tracestr) + raise SaltRenderError( + "Jinja error: {0}{1}".format(exc, out), line, tmplstr, trace=tracestr + ) # Workaround a bug in Jinja that removes the final newline # (https://github.com/mitsuhiko/jinja2/issues/75) @@ -456,24 +454,24 @@ def render_mako_tmpl(tmplstr, context, tmplpath=None): from mako.template import Template # pylint: disable=no-name-in-module from salt.utils.mako import SaltMakoTemplateLookup - saltenv = context['saltenv'] + saltenv = context["saltenv"] lookup = None if not saltenv: if tmplpath: # i.e., the template is from a file outside the state tree from mako.lookup import TemplateLookup # pylint: disable=no-name-in-module + lookup = TemplateLookup(directories=[os.path.dirname(tmplpath)]) else: lookup = SaltMakoTemplateLookup( - context['opts'], - saltenv, - pillar_rend=context.get('_pillar_rend', False)) + context["opts"], saltenv, pillar_rend=context.get("_pillar_rend", False) + ) try: return Template( tmplstr, strict_undefined=True, - uri=context['sls'].replace('.', '/') if 'sls' in context else None, - lookup=lookup + uri=context["sls"].replace(".", "/") if "sls" in context else None, + lookup=lookup, ).render(**context) except Exception: # pylint: disable=broad-except raise SaltRenderError(mako.exceptions.text_error_template().render()) @@ -481,11 +479,12 @@ def render_mako_tmpl(tmplstr, context, tmplpath=None): def render_wempy_tmpl(tmplstr, context, tmplpath=None): from wemplate.wemplate import TemplateParser as Template + return Template(tmplstr).render(**context) def render_genshi_tmpl(tmplstr, context, tmplpath=None): - ''' + """ Render a Genshi template. A method should be passed in as part of the context. If no method is passed in, xml is assumed. Valid methods are: @@ -500,25 +499,28 @@ def render_genshi_tmpl(tmplstr, context, tmplpath=None): Note that the ``text`` method will call ``NewTextTemplate``. If ``oldtext`` is desired, it must be called explicitly - ''' - method = context.get('method', 'xml') - if method == 'text' or method == 'newtext': + """ + method = context.get("method", "xml") + if method == "text" or method == "newtext": from genshi.template import NewTextTemplate # pylint: disable=no-name-in-module + tmpl = NewTextTemplate(tmplstr) - elif method == 'oldtext': + elif method == "oldtext": from genshi.template import OldTextTemplate # pylint: disable=no-name-in-module + tmpl = OldTextTemplate(tmplstr) else: from genshi.template import MarkupTemplate # pylint: disable=no-name-in-module + tmpl = MarkupTemplate(tmplstr) return tmpl.generate(**context).render(method) def render_cheetah_tmpl(tmplstr, context, tmplpath=None): - ''' + """ Render a Cheetah template. - ''' + """ from Cheetah.Template import Template # Compile the template and render it into the class @@ -536,27 +538,33 @@ def render_cheetah_tmpl(tmplstr, context, tmplpath=None): # This should call .__str() res = str(data) else: - raise SaltRenderError('Unknown type {!s} for Cheetah template while trying to render.'.format(type(tmplstr))) + raise SaltRenderError( + "Unknown type {!s} for Cheetah template while trying to render.".format( + type(tmplstr) + ) + ) # Now we can decode it to the correct encoding return salt.utils.data.decode(res) + + # pylint: enable=3rd-party-module-not-gated def py(sfn, string=False, **kwargs): # pylint: disable=C0103 - ''' + """ Render a template from a python source file Returns:: {'result': bool, 'data': <Error data or rendered file path>} - ''' + """ if not os.path.isfile(sfn): return {} base_fname = os.path.basename(sfn) - name = base_fname.split('.')[0] + name = base_fname.split(".")[0] if USE_IMPORTLIB: # pylint: disable=no-member @@ -572,11 +580,11 @@ def py(sfn, string=False, **kwargs): # pylint: disable=C0103 mod = imp.load_source(name, sfn) # File templates need these set as __var__ - if '__env__' not in kwargs and 'saltenv' in kwargs: - setattr(mod, '__env__', kwargs['saltenv']) - builtins = ['salt', 'grains', 'pillar', 'opts'] + if "__env__" not in kwargs and "saltenv" in kwargs: + setattr(mod, "__env__", kwargs["saltenv"]) + builtins = ["salt", "grains", "pillar", "opts"] for builtin in builtins: - arg = '__{0}__'.format(builtin) + arg = "__{0}__".format(builtin) setattr(mod, arg, kwargs[builtin]) for kwarg in kwargs: @@ -585,17 +593,14 @@ def py(sfn, string=False, **kwargs): # pylint: disable=C0103 try: data = mod.run() if string: - return {'result': True, - 'data': data} + return {"result": True, "data": data} tgt = salt.utils.files.mkstemp() - with salt.utils.files.fopen(tgt, 'w+') as target: + with salt.utils.files.fopen(tgt, "w+") as target: target.write(salt.utils.stringutils.to_str(data)) - return {'result': True, - 'data': tgt} + return {"result": True, "data": tgt} except Exception: # pylint: disable=broad-except trb = traceback.format_exc() - return {'result': False, - 'data': trb} + return {"result": False, "data": trb} JINJA = wrap_tmpl_func(render_jinja_tmpl) @@ -605,10 +610,10 @@ GENSHI = wrap_tmpl_func(render_genshi_tmpl) CHEETAH = wrap_tmpl_func(render_cheetah_tmpl) TEMPLATE_REGISTRY = { - 'jinja': JINJA, - 'mako': MAKO, - 'py': py, - 'wempy': WEMPY, - 'genshi': GENSHI, - 'cheetah': CHEETAH, + "jinja": JINJA, + "mako": MAKO, + "py": py, + "wempy": WEMPY, + "genshi": GENSHI, + "cheetah": CHEETAH, } diff --git a/salt/utils/textformat.py b/salt/utils/textformat.py index 7b2fc6cd2e7..cb4eea819c0 100644 --- a/salt/utils/textformat.py +++ b/salt/utils/textformat.py @@ -1,114 +1,106 @@ # -*- coding: utf-8 -*- -''' +""" ANSI escape code utilities, see http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import 3rd-party libs from salt.ext import six -graph_prefix = '\x1b[' -graph_suffix = 'm' +graph_prefix = "\x1b[" +graph_suffix = "m" codes = { - 'reset': '0', - - 'bold': '1', - 'faint': '2', - 'italic': '3', - 'underline': '4', - 'blink': '5', - 'slow_blink': '5', - 'fast_blink': '6', - 'inverse': '7', - 'conceal': '8', - 'strike': '9', - - 'primary_font': '10', - 'reset_font': '10', - 'font_0': '10', - 'font_1': '11', - 'font_2': '12', - 'font_3': '13', - 'font_4': '14', - 'font_5': '15', - 'font_6': '16', - 'font_7': '17', - 'font_8': '18', - 'font_9': '19', - 'fraktur': '20', - - 'double_underline': '21', - 'end_bold': '21', - 'normal_intensity': '22', - 'end_italic': '23', - 'end_fraktur': '23', - 'end_underline': '24', # single or double - 'end_blink': '25', - 'end_inverse': '27', - 'end_conceal': '28', - 'end_strike': '29', - - 'black': '30', - 'red': '31', - 'green': '32', - 'yellow': '33', - 'blue': '34', - 'magenta': '35', - 'cyan': '36', - 'white': '37', - 'extended': '38', - 'default': '39', - - 'fg_black': '30', - 'fg_red': '31', - 'fg_green': '32', - 'fg_yellow': '33', - 'fg_blue': '34', - 'fg_magenta': '35', - 'fg_cyan': '36', - 'fg_white': '37', - 'fg_extended': '38', - 'fg_default': '39', - - 'bg_black': '40', - 'bg_red': '41', - 'bg_green': '42', - 'bg_yellow': '44', - 'bg_blue': '44', - 'bg_magenta': '45', - 'bg_cyan': '46', - 'bg_white': '47', - 'bg_extended': '48', - 'bg_default': '49', - - 'frame': '51', - 'encircle': '52', - 'overline': '53', - 'end_frame': '54', - 'end_encircle': '54', - 'end_overline': '55', - - 'ideogram_underline': '60', - 'right_line': '60', - 'ideogram_double_underline': '61', - 'right_double_line': '61', - 'ideogram_overline': '62', - 'left_line': '62', - 'ideogram_double_overline': '63', - 'left_double_line': '63', - 'ideogram_stress': '64', - 'reset_ideogram': '65' + "reset": "0", + "bold": "1", + "faint": "2", + "italic": "3", + "underline": "4", + "blink": "5", + "slow_blink": "5", + "fast_blink": "6", + "inverse": "7", + "conceal": "8", + "strike": "9", + "primary_font": "10", + "reset_font": "10", + "font_0": "10", + "font_1": "11", + "font_2": "12", + "font_3": "13", + "font_4": "14", + "font_5": "15", + "font_6": "16", + "font_7": "17", + "font_8": "18", + "font_9": "19", + "fraktur": "20", + "double_underline": "21", + "end_bold": "21", + "normal_intensity": "22", + "end_italic": "23", + "end_fraktur": "23", + "end_underline": "24", # single or double + "end_blink": "25", + "end_inverse": "27", + "end_conceal": "28", + "end_strike": "29", + "black": "30", + "red": "31", + "green": "32", + "yellow": "33", + "blue": "34", + "magenta": "35", + "cyan": "36", + "white": "37", + "extended": "38", + "default": "39", + "fg_black": "30", + "fg_red": "31", + "fg_green": "32", + "fg_yellow": "33", + "fg_blue": "34", + "fg_magenta": "35", + "fg_cyan": "36", + "fg_white": "37", + "fg_extended": "38", + "fg_default": "39", + "bg_black": "40", + "bg_red": "41", + "bg_green": "42", + "bg_yellow": "44", + "bg_blue": "44", + "bg_magenta": "45", + "bg_cyan": "46", + "bg_white": "47", + "bg_extended": "48", + "bg_default": "49", + "frame": "51", + "encircle": "52", + "overline": "53", + "end_frame": "54", + "end_encircle": "54", + "end_overline": "55", + "ideogram_underline": "60", + "right_line": "60", + "ideogram_double_underline": "61", + "right_double_line": "61", + "ideogram_overline": "62", + "left_line": "62", + "ideogram_double_overline": "63", + "left_double_line": "63", + "ideogram_stress": "64", + "reset_ideogram": "65", } class TextFormat(object): - ''' + """ ANSI Select Graphic Rendition (SGR) code escape sequence. - ''' + """ def __init__(self, *attrs, **kwargs): - ''' + """ :param attrs: are the attribute names of any format codes in `codes` :param kwargs: may contain @@ -141,11 +133,13 @@ class TextFormat(object): print( '{0}Can you read this?{1}' ).format(magenta_on_green, TextFormat('reset')) - ''' - self.codes = [codes[attr.lower()] for attr in attrs if isinstance(attr, six.string_types)] + """ + self.codes = [ + codes[attr.lower()] for attr in attrs if isinstance(attr, six.string_types) + ] - if kwargs.get('reset', True): - self.codes[:0] = [codes['reset']] + if kwargs.get("reset", True): + self.codes[:0] = [codes["reset"]] def qualify_int(i): if isinstance(i, int): @@ -155,24 +149,24 @@ class TextFormat(object): if isinstance(t, (list, tuple)) and len(t) == 3: return qualify_int(t[0]), qualify_int(t[1]), qualify_int(t[2]) - if kwargs.get('x', None) is not None: - self.codes.extend((codes['extended'], '5', qualify_int(kwargs['x']))) - elif kwargs.get('rgb', None) is not None: - self.codes.extend((codes['extended'], '2')) - self.codes.extend(*qualify_triple_int(kwargs['rgb'])) + if kwargs.get("x", None) is not None: + self.codes.extend((codes["extended"], "5", qualify_int(kwargs["x"]))) + elif kwargs.get("rgb", None) is not None: + self.codes.extend((codes["extended"], "2")) + self.codes.extend(*qualify_triple_int(kwargs["rgb"])) - if kwargs.get('bg_x', None) is not None: - self.codes.extend((codes['extended'], '5', qualify_int(kwargs['bg_x']))) - elif kwargs.get('bg_rgb', None) is not None: - self.codes.extend((codes['extended'], '2')) - self.codes.extend(*qualify_triple_int(kwargs['bg_rgb'])) + if kwargs.get("bg_x", None) is not None: + self.codes.extend((codes["extended"], "5", qualify_int(kwargs["bg_x"]))) + elif kwargs.get("bg_rgb", None) is not None: + self.codes.extend((codes["extended"], "2")) + self.codes.extend(*qualify_triple_int(kwargs["bg_rgb"])) - self.sequence = '%s%s%s' % (graph_prefix, # pylint: disable=E1321 - ';'.join(self.codes), - graph_suffix) + # pylint: disable=string-substitution-usage-error + self.sequence = "%s%s%s" % (graph_prefix, ";".join(self.codes), graph_suffix,) + # pylint: enable=string-substitution-usage-error def __call__(self, text, reset=True): - ''' + """ Format :param text: by prefixing `self.sequence` and suffixing the reset sequence if :param reset: is `True`. @@ -182,9 +176,9 @@ class TextFormat(object): green_blink_text = TextFormat('blink', 'green') 'The answer is: {0}'.format(green_blink_text(42)) - ''' - end = TextFormat('reset') if reset else '' - return '%s%s%s' % (self.sequence, text, end) # pylint: disable=E1321 + """ + end = TextFormat("reset") if reset else "" + return "%s%s%s" % (self.sequence, text, end) # pylint: disable=E1321 def __str__(self): return self.sequence diff --git a/salt/utils/thin.py b/salt/utils/thin.py index 505e747e72a..de9299442a9 100644 --- a/salt/utils/thin.py +++ b/salt/utils/thin.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Generate the salt thin tarball from the installed python files -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -18,10 +18,20 @@ import zipfile # Import third party libs import jinja2 -import yaml import msgpack + +# Import salt libs +import salt +import salt.exceptions import salt.ext.six as _six import salt.ext.tornado as tornado +import salt.utils.files +import salt.utils.hashutils +import salt.utils.json +import salt.utils.path +import salt.utils.stringutils +import salt.version +import yaml try: import zlib @@ -67,15 +77,6 @@ except ImportError: ssl_match_hostname = None # pylint: enable=import-error,no-name-in-module -# Import salt libs -import salt -import salt.utils.files -import salt.utils.hashutils -import salt.utils.json -import salt.utils.path -import salt.utils.stringutils -import salt.exceptions -import salt.version if _six.PY2: import concurrent @@ -87,7 +88,7 @@ log = logging.getLogger(__name__) def _get_salt_call(*dirs, **namespaces): - ''' + """ Return salt-call source, based on configuration. This will include additional namespaces for another versions of Salt, if needed (e.g. older interpreters etc). @@ -95,8 +96,8 @@ def _get_salt_call(*dirs, **namespaces): :dirs: List of directories to include in the system path :namespaces: Dictionary of namespace :return: - ''' - template = '''# -*- coding: utf-8 -*- + """ + template = """# -*- coding: utf-8 -*- import os import sys @@ -123,58 +124,57 @@ for base in syspaths: if __name__ == '__main__': from salt.scripts import salt_call salt_call() -''' +""" - for tgt, cnt in [('%dirs%', dirs), ('%namespaces%', namespaces)]: + for tgt, cnt in [("%dirs%", dirs), ("%namespaces%", namespaces)]: template = template.replace(tgt, salt.utils.json.dumps(cnt)) return salt.utils.stringutils.to_bytes(template) def thin_path(cachedir): - ''' + """ Return the path to the thin tarball - ''' - return os.path.join(cachedir, 'thin', 'thin.tgz') + """ + return os.path.join(cachedir, "thin", "thin.tgz") def _is_shareable(mod): - ''' + """ Return True if module is share-able between major Python versions. :param mod: :return: - ''' + """ # This list is subject to change - shareable = ['salt', 'jinja2', - 'msgpack', 'certifi'] + shareable = ["salt", "jinja2", "msgpack", "certifi"] return os.path.basename(mod) in shareable def _add_dependency(container, obj): - ''' + """ Add a dependency to the top list. :param obj: :param is_file: :return: - ''' - if os.path.basename(obj.__file__).split('.')[0] == '__init__': + """ + if os.path.basename(obj.__file__).split(".")[0] == "__init__": container.append(os.path.dirname(obj.__file__)) else: - container.append(obj.__file__.replace('.pyc', '.py')) + container.append(obj.__file__.replace(".pyc", ".py")) def gte(): - ''' + """ This function is called externally from the alternative Python interpreter from within _get_tops function. :param extra_mods: :param so_mods: :return: - ''' + """ extra = salt.utils.json.loads(sys.argv[1]) tops = get_tops(**extra) @@ -182,109 +182,135 @@ def gte(): def get_ext_tops(config): - ''' + """ Get top directories for the dependencies, based on external configuration. :return: - ''' + """ config = copy.deepcopy(config) alternatives = {} - required = ['jinja2', 'yaml', 'tornado', 'msgpack'] + required = ["jinja2", "yaml", "tornado", "msgpack"] tops = [] for ns, cfg in salt.ext.six.iteritems(config or {}): alternatives[ns] = cfg - locked_py_version = cfg.get('py-version') + locked_py_version = cfg.get("py-version") err_msg = None if not locked_py_version: - err_msg = 'Alternative Salt library: missing specific locked Python version' + err_msg = "Alternative Salt library: missing specific locked Python version" elif not isinstance(locked_py_version, (tuple, list)): - err_msg = ('Alternative Salt library: specific locked Python version ' - 'should be a list of major/minor version') + err_msg = ( + "Alternative Salt library: specific locked Python version " + "should be a list of major/minor version" + ) if err_msg: raise salt.exceptions.SaltSystemExit(err_msg) - if cfg.get('dependencies') == 'inherit': + if cfg.get("dependencies") == "inherit": # TODO: implement inheritance of the modules from _here_ - raise NotImplementedError('This feature is not yet implemented') + raise NotImplementedError("This feature is not yet implemented") else: - for dep in cfg.get('dependencies'): - mod = cfg['dependencies'][dep] or '' + for dep in cfg.get("dependencies"): + mod = cfg["dependencies"][dep] or "" if not mod: - log.warning('Module %s has missing configuration', dep) + log.warning("Module %s has missing configuration", dep) continue - elif mod.endswith('.py') and not os.path.isfile(mod): - log.warning('Module %s configured with not a file or does not exist: %s', dep, mod) + elif mod.endswith(".py") and not os.path.isfile(mod): + log.warning( + "Module %s configured with not a file or does not exist: %s", + dep, + mod, + ) continue - elif not mod.endswith('.py') and not os.path.isfile(os.path.join(mod, '__init__.py')): - log.warning('Module %s is not a Python importable module with %s', dep, mod) + elif not mod.endswith(".py") and not os.path.isfile( + os.path.join(mod, "__init__.py") + ): + log.warning( + "Module %s is not a Python importable module with %s", dep, mod + ) continue tops.append(mod) if dep in required: required.pop(required.index(dep)) - required = ', '.join(required) + required = ", ".join(required) if required: - msg = 'Missing dependencies for the alternative version' \ - ' in the external configuration: {}'.format(required) + msg = ( + "Missing dependencies for the alternative version" + " in the external configuration: {}".format(required) + ) log.error(msg) raise salt.exceptions.SaltSystemExit(msg) - alternatives[ns]['dependencies'] = tops + alternatives[ns]["dependencies"] = tops return alternatives def _get_ext_namespaces(config): - ''' + """ Get namespaces from the existing configuration. :param config: :return: - ''' + """ namespaces = {} if not config: return namespaces for ns in config: - constraint_version = tuple(config[ns].get('py-version', [])) + constraint_version = tuple(config[ns].get("py-version", [])) if not constraint_version: - raise salt.exceptions.SaltSystemExit("An alternative version is configured, but not defined " - "to what Python's major/minor version it should be constrained.") + raise salt.exceptions.SaltSystemExit( + "An alternative version is configured, but not defined " + "to what Python's major/minor version it should be constrained." + ) else: namespaces[ns] = constraint_version return namespaces -def get_tops(extra_mods='', so_mods=''): - ''' +def get_tops(extra_mods="", so_mods=""): + """ Get top directories for the dependencies, based on Python interpreter. :param extra_mods: :param so_mods: :return: - ''' + """ tops = [] - for mod in [salt, jinja2, yaml, tornado, msgpack, certifi, singledispatch, concurrent, - singledispatch_helpers, ssl_match_hostname, markupsafe, backports_abc]: + for mod in [ + salt, + jinja2, + yaml, + tornado, + msgpack, + certifi, + singledispatch, + concurrent, + singledispatch_helpers, + ssl_match_hostname, + markupsafe, + backports_abc, + ]: if mod: log.debug('Adding module to the tops: "%s"', mod.__name__) _add_dependency(tops, mod) - for mod in [m for m in extra_mods.split(',') if m]: + for mod in [m for m in extra_mods.split(",") if m]: if mod not in locals() and mod not in globals(): try: locals()[mod] = __import__(mod) moddir, modname = os.path.split(locals()[mod].__file__) base, _ = os.path.splitext(modname) - if base == '__init__': + if base == "__init__": tops.append(moddir) else: - tops.append(os.path.join(moddir, base + '.py')) + tops.append(os.path.join(moddir, base + ".py")) except ImportError as err: log.exception(err) log.error('Unable to import extra-module "%s"', mod) - for mod in [m for m in so_mods.split(',') if m]: + for mod in [m for m in so_mods.split(",") if m]: try: locals()[mod] = __import__(mod) tops.append(locals()[mod].__file__) @@ -296,7 +322,7 @@ def get_tops(extra_mods='', so_mods=''): def _get_supported_py_config(tops, extended_cfg): - ''' + """ Based on the Salt SSH configuration, create a YAML configuration for the supported Python interpreter versions. This is then written into the thin.tgz archive and then verified by salt.client.ssh.ssh_py_shim.get_executable() @@ -304,42 +330,51 @@ def _get_supported_py_config(tops, extended_cfg): Note: Minimum default of 2.x versions is 2.7 and 3.x is 3.0, unless specified in namespaces. :return: - ''' + """ pymap = [] for py_ver, tops in _six.iteritems(copy.deepcopy(tops)): py_ver = int(py_ver) if py_ver == 2: - pymap.append('py2:2:7') + pymap.append("py2:2:7") elif py_ver == 3: - pymap.append('py3:3:0') + pymap.append("py3:3:0") for ns, cfg in _six.iteritems(copy.deepcopy(extended_cfg) or {}): - pymap.append('{}:{}:{}'.format(ns, *cfg.get('py-version'))) - pymap.append('') + pymap.append("{}:{}:{}".format(ns, *cfg.get("py-version"))) + pymap.append("") return salt.utils.stringutils.to_bytes(os.linesep.join(pymap)) def _get_thintar_prefix(tarname): - ''' + """ Make sure thintar temporary name is concurrent and secure. :param tarname: name of the chosen tarball :return: prefixed tarname - ''' + """ tfd, tmp_tarname = tempfile.mkstemp( dir=os.path.dirname(tarname), prefix=".thin-", - suffix=os.path.splitext(tarname)[1]) + suffix=os.path.splitext(tarname)[1], + ) os.close(tfd) return tmp_tarname -def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', - python2_bin='python2', python3_bin='python3', absonly=True, - compress='gzip', extended_cfg=None): - ''' +def gen_thin( + cachedir, + extra_mods="", + overwrite=False, + so_mods="", + python2_bin="python2", + python3_bin="python3", + absonly=True, + compress="gzip", + extended_cfg=None, +): + """ Generate the salt-thin tarball and print the location of the tarball Optional additional mods to include (e.g. mako) can be supplied as a comma delimited string. Permits forcing an overwrite of the output file as well. @@ -352,26 +387,31 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', salt-run thin.generate mako salt-run thin.generate mako,wempy 1 salt-run thin.generate overwrite=1 - ''' + """ if sys.version_info < (2, 6): - raise salt.exceptions.SaltSystemExit('The minimum required python version to run salt-ssh is "2.6".') - if compress not in ['gzip', 'zip']: - log.warning('Unknown compression type: "%s". Falling back to "gzip" compression.', compress) - compress = 'gzip' + raise salt.exceptions.SaltSystemExit( + 'The minimum required python version to run salt-ssh is "2.6".' + ) + if compress not in ["gzip", "zip"]: + log.warning( + 'Unknown compression type: "%s". Falling back to "gzip" compression.', + compress, + ) + compress = "gzip" - thindir = os.path.join(cachedir, 'thin') + thindir = os.path.join(cachedir, "thin") if not os.path.isdir(thindir): os.makedirs(thindir) - thintar = os.path.join(thindir, 'thin.' + (compress == 'gzip' and 'tgz' or 'zip')) - thinver = os.path.join(thindir, 'version') - pythinver = os.path.join(thindir, '.thin-gen-py-version') - salt_call = os.path.join(thindir, 'salt-call') - pymap_cfg = os.path.join(thindir, 'supported-versions') - code_checksum = os.path.join(thindir, 'code-checksum') + thintar = os.path.join(thindir, "thin." + (compress == "gzip" and "tgz" or "zip")) + thinver = os.path.join(thindir, "version") + pythinver = os.path.join(thindir, ".thin-gen-py-version") + salt_call = os.path.join(thindir, "salt-call") + pymap_cfg = os.path.join(thindir, "supported-versions") + code_checksum = os.path.join(thindir, "code-checksum") digest_collector = salt.utils.hashutils.DigestCollector() - with salt.utils.files.fopen(salt_call, 'wb') as fp_: - fp_.write(_get_salt_call('pyall', **_get_ext_namespaces(extended_cfg))) + with salt.utils.files.fopen(salt_call, "wb") as fp_: + fp_.write(_get_salt_call("pyall", **_get_ext_namespaces(extended_cfg))) if os.path.isfile(thintar): if not overwrite: @@ -380,19 +420,21 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', overwrite = fh_.read() != salt.version.__version__ if overwrite is False and os.path.isfile(pythinver): with salt.utils.files.fopen(pythinver) as fh_: - overwrite = fh_.read() != str(sys.version_info[0]) # future lint: disable=blacklisted-function + overwrite = fh_.read() != str( + sys.version_info[0] + ) # future lint: disable=blacklisted-function else: overwrite = True if overwrite: try: - log.debug('Removing %s archive file', thintar) + log.debug("Removing %s archive file", thintar) os.remove(thintar) except OSError as exc: - log.error('Error while removing %s file: %s', thintar, exc) + log.error("Error while removing %s file: %s", thintar, exc) if os.path.exists(thintar): raise salt.exceptions.SaltSystemExit( - 'Unable to remove {} file. See logs for details.'.format( + "Unable to remove {} file. See logs for details.".format( thintar ) ) @@ -401,24 +443,36 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', if _six.PY3: # Let's check for the minimum python 2 version requirement, 2.6 if not salt.utils.path.which(python2_bin): - log.debug('%s binary does not exist. Will not detect Python 2 version', python2_bin) + log.debug( + "%s binary does not exist. Will not detect Python 2 version", + python2_bin, + ) else: - py_shell_cmd = "{} -c 'import sys;sys.stdout.write(\"%s.%s\\n\" % sys.version_info[:2]);'".format(python2_bin) + py_shell_cmd = "{} -c 'import sys;sys.stdout.write(\"%s.%s\\n\" % sys.version_info[:2]);'".format( + python2_bin + ) cmd = subprocess.Popen(py_shell_cmd, stdout=subprocess.PIPE, shell=True) stdout, _ = cmd.communicate() if cmd.returncode == 0: - py2_version = tuple(int(n) for n in stdout.decode('utf-8').strip().split('.')) + py2_version = tuple( + int(n) for n in stdout.decode("utf-8").strip().split(".") + ) if py2_version < (2, 6): raise salt.exceptions.SaltSystemExit( 'The minimum required python version to run salt-ssh is "2.6".' 'The version reported by "{0}" is "{1}". Please try "salt-ssh ' - '--python2-bin=<path-to-python-2.6-binary-or-higher>".'.format(python2_bin, stdout.strip())) + '--python2-bin=<path-to-python-2.6-binary-or-higher>".'.format( + python2_bin, stdout.strip() + ) + ) else: - log.debug('Unable to detect %s version', python2_bin) + log.debug("Unable to detect %s version", python2_bin) log.debug(stdout) - tops_failure_msg = 'Failed %s tops for Python binary %s.' - python_check_msg = '%s binary does not exist. Will not attempt to generate tops for Python %s' + tops_failure_msg = "Failed %s tops for Python binary %s." + python_check_msg = ( + "%s binary does not exist. Will not attempt to generate tops for Python %s" + ) tops_py_version_mapping = {} tops = get_tops(extra_mods=extra_mods, so_mods=so_mods) tops_py_version_mapping[sys.version_info.major] = tops @@ -427,52 +481,68 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', if _six.PY2 and sys.version_info.major == 2: # Get python 3 tops if not salt.utils.path.which(python3_bin): - log.debug(python_check_msg, python3_bin, '3') + log.debug(python_check_msg, python3_bin, "3") else: py_shell_cmd = "{0} -c 'import salt.utils.thin as t;print(t.gte())' '{1}'".format( - python3_bin, salt.utils.json.dumps({'extra_mods': extra_mods, 'so_mods': so_mods})) - cmd = subprocess.Popen(py_shell_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + python3_bin, + salt.utils.json.dumps({"extra_mods": extra_mods, "so_mods": so_mods}), + ) + cmd = subprocess.Popen( + py_shell_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True + ) stdout, stderr = cmd.communicate() if cmd.returncode == 0: try: tops = salt.utils.json.loads(stdout) - tops_py_version_mapping['3'] = tops + tops_py_version_mapping["3"] = tops except ValueError as err: - log.error(tops_failure_msg, 'parsing', python3_bin) + log.error(tops_failure_msg, "parsing", python3_bin) log.exception(err) else: - log.debug(tops_failure_msg, 'collecting', python3_bin) + log.debug(tops_failure_msg, "collecting", python3_bin) log.debug(stderr) # Collect tops, alternative to 3.x version if _six.PY3 and sys.version_info.major == 3: # Get python 2 tops if not salt.utils.path.which(python2_bin): - log.debug(python_check_msg, python2_bin, '2') + log.debug(python_check_msg, python2_bin, "2") else: py_shell_cmd = "{0} -c 'import salt.utils.thin as t;print(t.gte())' '{1}'".format( - python2_bin, salt.utils.json.dumps({'extra_mods': extra_mods, 'so_mods': so_mods})) - cmd = subprocess.Popen(py_shell_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + python2_bin, + salt.utils.json.dumps({"extra_mods": extra_mods, "so_mods": so_mods}), + ) + cmd = subprocess.Popen( + py_shell_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True + ) stdout, stderr = cmd.communicate() if cmd.returncode == 0: try: - tops = salt.utils.json.loads(stdout.decode('utf-8')) - tops_py_version_mapping['2'] = tops + tops = salt.utils.json.loads(stdout.decode("utf-8")) + tops_py_version_mapping["2"] = tops except ValueError as err: - log.error(tops_failure_msg, 'parsing', python2_bin) + log.error(tops_failure_msg, "parsing", python2_bin) log.exception(err) else: - log.debug(tops_failure_msg, 'collecting', python2_bin) + log.debug(tops_failure_msg, "collecting", python2_bin) log.debug(stderr) - with salt.utils.files.fopen(pymap_cfg, 'wb') as fp_: - fp_.write(_get_supported_py_config(tops=tops_py_version_mapping, extended_cfg=extended_cfg)) + with salt.utils.files.fopen(pymap_cfg, "wb") as fp_: + fp_.write( + _get_supported_py_config( + tops=tops_py_version_mapping, extended_cfg=extended_cfg + ) + ) tmp_thintar = _get_thintar_prefix(thintar) - if compress == 'gzip': - tfp = tarfile.open(tmp_thintar, 'w:gz', dereference=True) - elif compress == 'zip': - tfp = zipfile.ZipFile(tmp_thintar, 'w', compression=zlib and zipfile.ZIP_DEFLATED or zipfile.ZIP_STORED) + if compress == "gzip": + tfp = tarfile.open(tmp_thintar, "w:gz", dereference=True) + elif compress == "zip": + tfp = zipfile.ZipFile( + tmp_thintar, + "w", + compression=zlib and zipfile.ZIP_DEFLATED or zipfile.ZIP_STORED, + ) tfp.add = tfp.write try: # cwd may not exist if it was removed but salt was run from it start_dir = os.getcwd() @@ -481,7 +551,7 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', tempdir = None # Pack default data - log.debug('Packing default libraries based on current Salt version') + log.debug("Packing default libraries based on current Salt version") for py_ver, tops in _six.iteritems(tops_py_version_mapping): for top in tops: if absonly and not os.path.isabs(top): @@ -498,7 +568,7 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', top = os.path.join(tempdir, base) os.chdir(tempdir) - site_pkg_dir = _is_shareable(base) and 'pyall' or 'py{}'.format(py_ver) + site_pkg_dir = _is_shareable(base) and "pyall" or "py{}".format(py_ver) log.debug('Packing "%s" to "%s" destination', base, site_pkg_dir) if not os.path.isdir(top): @@ -508,16 +578,18 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', continue for root, dirs, files in salt.utils.path.os_walk(base, followlinks=True): for name in files: - if not name.endswith(('.pyc', '.pyo')): + if not name.endswith((".pyc", ".pyo")): digest_collector.add(os.path.join(root, name)) arcname = os.path.join(site_pkg_dir, root, name) - if hasattr(tfp, 'getinfo'): + if hasattr(tfp, "getinfo"): try: # This is a little slow but there's no clear way to detect duplicates tfp.getinfo(os.path.join(site_pkg_dir, root, name)) arcname = None except KeyError: - log.debug('ZIP: Unable to add "%s" with "getinfo"', arcname) + log.debug( + 'ZIP: Unable to add "%s" with "getinfo"', arcname + ) if arcname: tfp.add(os.path.join(root, name), arcname=arcname) @@ -527,15 +599,22 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', # Pack alternative data if extended_cfg: - log.debug('Packing libraries based on alternative Salt versions') + log.debug("Packing libraries based on alternative Salt versions") for ns, cfg in _six.iteritems(get_ext_tops(extended_cfg)): - tops = [cfg.get('path')] + cfg.get('dependencies') - py_ver_major, py_ver_minor = cfg.get('py-version') + tops = [cfg.get("path")] + cfg.get("dependencies") + py_ver_major, py_ver_minor = cfg.get("py-version") for top in tops: base, top_dirname = os.path.basename(top), os.path.dirname(top) os.chdir(top_dirname) - site_pkg_dir = _is_shareable(base) and 'pyall' or 'py{0}'.format(py_ver_major) - log.debug('Packing alternative "%s" to "%s/%s" destination', base, ns, site_pkg_dir) + site_pkg_dir = ( + _is_shareable(base) and "pyall" or "py{0}".format(py_ver_major) + ) + log.debug( + 'Packing alternative "%s" to "%s/%s" destination', + base, + ns, + site_pkg_dir, + ) if not os.path.isdir(top): # top is a single file module if os.path.exists(os.path.join(top_dirname, base)): @@ -543,28 +622,38 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', continue for root, dirs, files in salt.utils.path.os_walk(base, followlinks=True): for name in files: - if not name.endswith(('.pyc', '.pyo')): + if not name.endswith((".pyc", ".pyo")): digest_collector.add(os.path.join(root, name)) arcname = os.path.join(ns, site_pkg_dir, root, name) - if hasattr(tfp, 'getinfo'): + if hasattr(tfp, "getinfo"): try: tfp.getinfo(os.path.join(site_pkg_dir, root, name)) arcname = None except KeyError: - log.debug('ZIP: Unable to add "%s" with "getinfo"', arcname) + log.debug( + 'ZIP: Unable to add "%s" with "getinfo"', arcname + ) if arcname: tfp.add(os.path.join(root, name), arcname=arcname) os.chdir(thindir) - with salt.utils.files.fopen(thinver, 'w+') as fp_: + with salt.utils.files.fopen(thinver, "w+") as fp_: fp_.write(salt.version.__version__) - with salt.utils.files.fopen(pythinver, 'w+') as fp_: - fp_.write(str(sys.version_info.major)) # future lint: disable=blacklisted-function - with salt.utils.files.fopen(code_checksum, 'w+') as fp_: + with salt.utils.files.fopen(pythinver, "w+") as fp_: + fp_.write( + str(sys.version_info.major) + ) # future lint: disable=blacklisted-function + with salt.utils.files.fopen(code_checksum, "w+") as fp_: fp_.write(digest_collector.digest()) os.chdir(os.path.dirname(thinver)) - for fname in ['version', '.thin-gen-py-version', 'salt-call', 'supported-versions', 'code-checksum']: + for fname in [ + "version", + ".thin-gen-py-version", + "salt-call", + "supported-versions", + "code-checksum", + ]: tfp.add(fname) if start_dir: @@ -576,14 +665,14 @@ def gen_thin(cachedir, extra_mods='', overwrite=False, so_mods='', return thintar -def thin_sum(cachedir, form='sha1'): - ''' +def thin_sum(cachedir, form="sha1"): + """ Return the checksum of the current thin tarball - ''' + """ thintar = gen_thin(cachedir) - code_checksum_path = os.path.join(cachedir, 'thin', 'code-checksum') + code_checksum_path = os.path.join(cachedir, "thin", "code-checksum") if os.path.isfile(code_checksum_path): - with salt.utils.files.fopen(code_checksum_path, 'r') as fh: + with salt.utils.files.fopen(code_checksum_path, "r") as fh: code_checksum = "'{0}'".format(fh.read().strip()) else: code_checksum = "'0'" @@ -591,9 +680,15 @@ def thin_sum(cachedir, form='sha1'): return code_checksum, salt.utils.hashutils.get_hash(thintar, form) -def gen_min(cachedir, extra_mods='', overwrite=False, so_mods='', - python2_bin='python2', python3_bin='python3'): - ''' +def gen_min( + cachedir, + extra_mods="", + overwrite=False, + so_mods="", + python2_bin="python2", + python3_bin="python3", +): + """ Generate the salt-min tarball and print the location of the tarball Optional additional mods to include (e.g. mako) can be supplied as a comma delimited string. Permits forcing an overwrite of the output file as well. @@ -606,15 +701,15 @@ def gen_min(cachedir, extra_mods='', overwrite=False, so_mods='', salt-run min.generate mako salt-run min.generate mako,wempy 1 salt-run min.generate overwrite=1 - ''' - mindir = os.path.join(cachedir, 'min') + """ + mindir = os.path.join(cachedir, "min") if not os.path.isdir(mindir): os.makedirs(mindir) - mintar = os.path.join(mindir, 'min.tgz') - minver = os.path.join(mindir, 'version') - pyminver = os.path.join(mindir, '.min-gen-py-version') - salt_call = os.path.join(mindir, 'salt-call') - with salt.utils.files.fopen(salt_call, 'wb') as fp_: + mintar = os.path.join(mindir, "min.tgz") + minver = os.path.join(mindir, "version") + pyminver = os.path.join(mindir, ".min-gen-py-version") + salt_call = os.path.join(mindir, "salt-call") + with salt.utils.files.fopen(salt_call, "wb") as fp_: fp_.write(_get_salt_call()) if os.path.isfile(mintar): if not overwrite: @@ -623,7 +718,9 @@ def gen_min(cachedir, extra_mods='', overwrite=False, so_mods='', overwrite = fh_.read() != salt.version.__version__ if overwrite is False and os.path.isfile(pyminver): with salt.utils.files.fopen(pyminver) as fh_: - overwrite = fh_.read() != str(sys.version_info[0]) # future lint: disable=blacklisted-function + overwrite = fh_.read() != str( + sys.version_info[0] + ) # future lint: disable=blacklisted-function else: overwrite = True @@ -637,20 +734,23 @@ def gen_min(cachedir, extra_mods='', overwrite=False, so_mods='', if _six.PY3: # Let's check for the minimum python 2 version requirement, 2.6 py_shell_cmd = ( - python2_bin + ' -c \'from __future__ import print_function; import sys; ' + python2_bin + " -c 'from __future__ import print_function; import sys; " 'print("{0}.{1}".format(*(sys.version_info[:2])));\'' ) cmd = subprocess.Popen(py_shell_cmd, stdout=subprocess.PIPE, shell=True) stdout, _ = cmd.communicate() if cmd.returncode == 0: - py2_version = tuple(int(n) for n in stdout.decode('utf-8').strip().split('.')) + py2_version = tuple( + int(n) for n in stdout.decode("utf-8").strip().split(".") + ) if py2_version < (2, 6): # Bail! raise salt.exceptions.SaltSystemExit( 'The minimum required python version to run salt-ssh is "2.6".' 'The version reported by "{0}" is "{1}". Please try "salt-ssh ' - '--python2-bin=<path-to-python-2.6-binary-or-higher>".'.format(python2_bin, - stdout.strip()) + '--python2-bin=<path-to-python-2.6-binary-or-higher>".'.format( + python2_bin, stdout.strip() + ) ) elif sys.version_info < (2, 6): # Bail! Though, how did we reached this far in the first place. @@ -661,45 +761,53 @@ def gen_min(cachedir, extra_mods='', overwrite=False, so_mods='', tops_py_version_mapping = {} tops = get_tops(extra_mods=extra_mods, so_mods=so_mods) if _six.PY2: - tops_py_version_mapping['2'] = tops + tops_py_version_mapping["2"] = tops else: - tops_py_version_mapping['3'] = tops + tops_py_version_mapping["3"] = tops # TODO: Consider putting known py2 and py3 compatible libs in its own sharable directory. # This would reduce the min size. if _six.PY2 and sys.version_info[0] == 2: # Get python 3 tops py_shell_cmd = ( - python3_bin + ' -c \'import sys; import json; import salt.utils.thin; ' - 'print(json.dumps(salt.utils.thin.get_tops(**(json.loads(sys.argv[1]))), ensure_ascii=False)); exit(0);\' ' - '\'{0}\''.format(salt.utils.json.dumps({'extra_mods': extra_mods, 'so_mods': so_mods})) + python3_bin + " -c 'import sys; import json; import salt.utils.thin; " + "print(json.dumps(salt.utils.thin.get_tops(**(json.loads(sys.argv[1]))), ensure_ascii=False)); exit(0);' " + "'{0}'".format( + salt.utils.json.dumps({"extra_mods": extra_mods, "so_mods": so_mods}) + ) + ) + cmd = subprocess.Popen( + py_shell_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True ) - cmd = subprocess.Popen(py_shell_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) stdout, stderr = cmd.communicate() if cmd.returncode == 0: try: tops = salt.utils.json.loads(stdout) - tops_py_version_mapping['3'] = tops + tops_py_version_mapping["3"] = tops except ValueError: pass if _six.PY3 and sys.version_info[0] == 3: # Get python 2 tops py_shell_cmd = ( - python2_bin + ' -c \'from __future__ import print_function; ' - 'import sys; import json; import salt.utils.thin; ' - 'print(json.dumps(salt.utils.thin.get_tops(**(json.loads(sys.argv[1]))), ensure_ascii=False)); exit(0);\' ' - '\'{0}\''.format(salt.utils.json.dumps({'extra_mods': extra_mods, 'so_mods': so_mods})) + python2_bin + " -c 'from __future__ import print_function; " + "import sys; import json; import salt.utils.thin; " + "print(json.dumps(salt.utils.thin.get_tops(**(json.loads(sys.argv[1]))), ensure_ascii=False)); exit(0);' " + "'{0}'".format( + salt.utils.json.dumps({"extra_mods": extra_mods, "so_mods": so_mods}) + ) + ) + cmd = subprocess.Popen( + py_shell_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True ) - cmd = subprocess.Popen(py_shell_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) stdout, stderr = cmd.communicate() if cmd.returncode == 0: try: - tops = salt.utils.json.loads(stdout.decode('utf-8')) - tops_py_version_mapping['2'] = tops + tops = salt.utils.json.loads(stdout.decode("utf-8")) + tops_py_version_mapping["2"] = tops except ValueError: pass - tfp = tarfile.open(mintar, 'w:gz', dereference=True) + tfp = tarfile.open(mintar, "w:gz", dereference=True) try: # cwd may not exist if it was removed but salt was run from it start_dir = os.getcwd() except OSError: @@ -708,124 +816,124 @@ def gen_min(cachedir, extra_mods='', overwrite=False, so_mods='', # This is the absolute minimum set of files required to run salt-call min_files = ( - 'salt/__init__.py', - 'salt/utils', - 'salt/utils/__init__.py', - 'salt/utils/atomicfile.py', - 'salt/utils/validate', - 'salt/utils/validate/__init__.py', - 'salt/utils/validate/path.py', - 'salt/utils/decorators', - 'salt/utils/decorators/__init__.py', - 'salt/utils/cache.py', - 'salt/utils/xdg.py', - 'salt/utils/odict.py', - 'salt/utils/minions.py', - 'salt/utils/dicttrim.py', - 'salt/utils/sdb.py', - 'salt/utils/migrations.py', - 'salt/utils/files.py', - 'salt/utils/parsers.py', - 'salt/utils/locales.py', - 'salt/utils/lazy.py', - 'salt/utils/s3.py', - 'salt/utils/dictupdate.py', - 'salt/utils/verify.py', - 'salt/utils/args.py', - 'salt/utils/kinds.py', - 'salt/utils/xmlutil.py', - 'salt/utils/debug.py', - 'salt/utils/jid.py', - 'salt/utils/openstack', - 'salt/utils/openstack/__init__.py', - 'salt/utils/openstack/swift.py', - 'salt/utils/asynchronous.py', - 'salt/utils/process.py', - 'salt/utils/jinja.py', - 'salt/utils/rsax931.py', - 'salt/utils/context.py', - 'salt/utils/minion.py', - 'salt/utils/error.py', - 'salt/utils/aws.py', - 'salt/utils/timed_subprocess.py', - 'salt/utils/zeromq.py', - 'salt/utils/schedule.py', - 'salt/utils/url.py', - 'salt/utils/yamlencoding.py', - 'salt/utils/network.py', - 'salt/utils/http.py', - 'salt/utils/gzip_util.py', - 'salt/utils/vt.py', - 'salt/utils/templates.py', - 'salt/utils/aggregation.py', - 'salt/utils/yaml.py', - 'salt/utils/yamldumper.py', - 'salt/utils/yamlloader.py', - 'salt/utils/event.py', - 'salt/utils/state.py', - 'salt/serializers', - 'salt/serializers/__init__.py', - 'salt/serializers/yamlex.py', - 'salt/template.py', - 'salt/_compat.py', - 'salt/loader.py', - 'salt/client', - 'salt/client/__init__.py', - 'salt/ext', - 'salt/ext/__init__.py', - 'salt/ext/six.py', - 'salt/ext/ipaddress.py', - 'salt/version.py', - 'salt/syspaths.py', - 'salt/defaults', - 'salt/defaults/__init__.py', - 'salt/defaults/exitcodes.py', - 'salt/renderers', - 'salt/renderers/__init__.py', - 'salt/renderers/jinja.py', - 'salt/renderers/yaml.py', - 'salt/modules', - 'salt/modules/__init__.py', - 'salt/modules/test.py', - 'salt/modules/selinux.py', - 'salt/modules/cmdmod.py', - 'salt/modules/saltutil.py', - 'salt/minion.py', - 'salt/pillar', - 'salt/pillar/__init__.py', - 'salt/utils/textformat.py', - 'salt/log', - 'salt/log/__init__.py', - 'salt/log/handlers', - 'salt/log/handlers/__init__.py', - 'salt/log/mixins.py', - 'salt/log/setup.py', - 'salt/cli', - 'salt/cli/__init__.py', - 'salt/cli/caller.py', - 'salt/cli/daemons.py', - 'salt/cli/salt.py', - 'salt/cli/call.py', - 'salt/fileserver', - 'salt/fileserver/__init__.py', - 'salt/transport', - 'salt/transport/__init__.py', - 'salt/transport/client.py', - 'salt/exceptions.py', - 'salt/grains', - 'salt/grains/__init__.py', - 'salt/grains/extra.py', - 'salt/scripts.py', - 'salt/state.py', - 'salt/fileclient.py', - 'salt/crypt.py', - 'salt/config.py', - 'salt/beacons', - 'salt/beacons/__init__.py', - 'salt/payload.py', - 'salt/output', - 'salt/output/__init__.py', - 'salt/output/nested.py', + "salt/__init__.py", + "salt/utils", + "salt/utils/__init__.py", + "salt/utils/atomicfile.py", + "salt/utils/validate", + "salt/utils/validate/__init__.py", + "salt/utils/validate/path.py", + "salt/utils/decorators", + "salt/utils/decorators/__init__.py", + "salt/utils/cache.py", + "salt/utils/xdg.py", + "salt/utils/odict.py", + "salt/utils/minions.py", + "salt/utils/dicttrim.py", + "salt/utils/sdb.py", + "salt/utils/migrations.py", + "salt/utils/files.py", + "salt/utils/parsers.py", + "salt/utils/locales.py", + "salt/utils/lazy.py", + "salt/utils/s3.py", + "salt/utils/dictupdate.py", + "salt/utils/verify.py", + "salt/utils/args.py", + "salt/utils/kinds.py", + "salt/utils/xmlutil.py", + "salt/utils/debug.py", + "salt/utils/jid.py", + "salt/utils/openstack", + "salt/utils/openstack/__init__.py", + "salt/utils/openstack/swift.py", + "salt/utils/asynchronous.py", + "salt/utils/process.py", + "salt/utils/jinja.py", + "salt/utils/rsax931.py", + "salt/utils/context.py", + "salt/utils/minion.py", + "salt/utils/error.py", + "salt/utils/aws.py", + "salt/utils/timed_subprocess.py", + "salt/utils/zeromq.py", + "salt/utils/schedule.py", + "salt/utils/url.py", + "salt/utils/yamlencoding.py", + "salt/utils/network.py", + "salt/utils/http.py", + "salt/utils/gzip_util.py", + "salt/utils/vt.py", + "salt/utils/templates.py", + "salt/utils/aggregation.py", + "salt/utils/yaml.py", + "salt/utils/yamldumper.py", + "salt/utils/yamlloader.py", + "salt/utils/event.py", + "salt/utils/state.py", + "salt/serializers", + "salt/serializers/__init__.py", + "salt/serializers/yamlex.py", + "salt/template.py", + "salt/_compat.py", + "salt/loader.py", + "salt/client", + "salt/client/__init__.py", + "salt/ext", + "salt/ext/__init__.py", + "salt/ext/six.py", + "salt/ext/ipaddress.py", + "salt/version.py", + "salt/syspaths.py", + "salt/defaults", + "salt/defaults/__init__.py", + "salt/defaults/exitcodes.py", + "salt/renderers", + "salt/renderers/__init__.py", + "salt/renderers/jinja.py", + "salt/renderers/yaml.py", + "salt/modules", + "salt/modules/__init__.py", + "salt/modules/test.py", + "salt/modules/selinux.py", + "salt/modules/cmdmod.py", + "salt/modules/saltutil.py", + "salt/minion.py", + "salt/pillar", + "salt/pillar/__init__.py", + "salt/utils/textformat.py", + "salt/log", + "salt/log/__init__.py", + "salt/log/handlers", + "salt/log/handlers/__init__.py", + "salt/log/mixins.py", + "salt/log/setup.py", + "salt/cli", + "salt/cli/__init__.py", + "salt/cli/caller.py", + "salt/cli/daemons.py", + "salt/cli/salt.py", + "salt/cli/call.py", + "salt/fileserver", + "salt/fileserver/__init__.py", + "salt/transport", + "salt/transport/__init__.py", + "salt/transport/client.py", + "salt/exceptions.py", + "salt/grains", + "salt/grains/__init__.py", + "salt/grains/extra.py", + "salt/scripts.py", + "salt/state.py", + "salt/fileclient.py", + "salt/crypt.py", + "salt/config.py", + "salt/beacons", + "salt/beacons/__init__.py", + "salt/payload.py", + "salt/output", + "salt/output/__init__.py", + "salt/output/nested.py", ) for py_ver, tops in _six.iteritems(tops_py_version_mapping): @@ -843,38 +951,43 @@ def gen_min(cachedir, extra_mods='', overwrite=False, so_mods='', os.chdir(tempdir) if not os.path.isdir(top): # top is a single file module - tfp.add(base, arcname=os.path.join('py{0}'.format(py_ver), base)) + tfp.add(base, arcname=os.path.join("py{0}".format(py_ver), base)) continue for root, dirs, files in salt.utils.path.os_walk(base, followlinks=True): for name in files: - if name.endswith(('.pyc', '.pyo')): + if name.endswith((".pyc", ".pyo")): continue - if root.startswith('salt') and os.path.join(root, name) not in min_files: + if ( + root.startswith("salt") + and os.path.join(root, name) not in min_files + ): continue - tfp.add(os.path.join(root, name), - arcname=os.path.join('py{0}'.format(py_ver), root, name)) + tfp.add( + os.path.join(root, name), + arcname=os.path.join("py{0}".format(py_ver), root, name), + ) if tempdir is not None: shutil.rmtree(tempdir) tempdir = None os.chdir(mindir) - tfp.add('salt-call') - with salt.utils.files.fopen(minver, 'w+') as fp_: + tfp.add("salt-call") + with salt.utils.files.fopen(minver, "w+") as fp_: fp_.write(salt.version.__version__) - with salt.utils.files.fopen(pyminver, 'w+') as fp_: + with salt.utils.files.fopen(pyminver, "w+") as fp_: fp_.write(str(sys.version_info[0])) # future lint: disable=blacklisted-function os.chdir(os.path.dirname(minver)) - tfp.add('version') - tfp.add('.min-gen-py-version') + tfp.add("version") + tfp.add(".min-gen-py-version") if start_dir: os.chdir(start_dir) tfp.close() return mintar -def min_sum(cachedir, form='sha1'): - ''' +def min_sum(cachedir, form="sha1"): + """ Return the checksum of the current thin tarball - ''' + """ mintar = gen_min(cachedir) return salt.utils.hashutils.get_hash(mintar, form) diff --git a/salt/utils/timed_subprocess.py b/salt/utils/timed_subprocess.py index c59c26c1038..5c4ac35ac38 100644 --- a/salt/utils/timed_subprocess.py +++ b/salt/utils/timed_subprocess.py @@ -1,54 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" For running command line executables with a timeout -''' +""" from __future__ import absolute_import, print_function, unicode_literals import shlex import subprocess import threading + import salt.exceptions import salt.utils.data from salt.ext import six class TimedProc(object): - ''' + """ Create a TimedProc object, calls subprocess.Popen with passed args and **kwargs - ''' + """ + def __init__(self, args, **kwargs): - self.wait = not kwargs.pop('bg', False) - self.stdin = kwargs.pop('stdin', None) - self.with_communicate = kwargs.pop('with_communicate', self.wait) - self.timeout = kwargs.pop('timeout', None) - self.stdin_raw_newlines = kwargs.pop('stdin_raw_newlines', False) + self.wait = not kwargs.pop("bg", False) + self.stdin = kwargs.pop("stdin", None) + self.with_communicate = kwargs.pop("with_communicate", self.wait) + self.timeout = kwargs.pop("timeout", None) + self.stdin_raw_newlines = kwargs.pop("stdin_raw_newlines", False) # If you're not willing to wait for the process # you can't define any stdin, stdout or stderr if not self.wait: - self.stdin = kwargs['stdin'] = None + self.stdin = kwargs["stdin"] = None self.with_communicate = False elif self.stdin is not None: if not self.stdin_raw_newlines: # Translate a newline submitted as '\n' on the CLI to an actual # newline character. - self.stdin = salt.utils.stringutils.to_bytes(self.stdin.replace('\\n', '\n')) - kwargs['stdin'] = subprocess.PIPE + self.stdin = salt.utils.stringutils.to_bytes( + self.stdin.replace("\\n", "\n") + ) + kwargs["stdin"] = subprocess.PIPE if not self.with_communicate: - self.stdout = kwargs['stdout'] = None - self.stderr = kwargs['stderr'] = None + self.stdout = kwargs["stdout"] = None + self.stderr = kwargs["stderr"] = None if self.timeout and not isinstance(self.timeout, (int, float)): - raise salt.exceptions.TimedProcTimeoutError('Error: timeout {0} must be a number'.format(self.timeout)) - if kwargs.get('shell', False): + raise salt.exceptions.TimedProcTimeoutError( + "Error: timeout {0} must be a number".format(self.timeout) + ) + if kwargs.get("shell", False): args = salt.utils.data.decode(args, to_str=True) try: self.process = subprocess.Popen(args, **kwargs) except (AttributeError, TypeError): - if not kwargs.get('shell', False): + if not kwargs.get("shell", False): if not isinstance(args, (list, tuple)): try: args = shlex.split(args) @@ -66,24 +72,25 @@ class TimedProc(object): # Handle corner case where someone does a 'cmd.run 3' args = six.text_type(args) # Ensure that environment variables are strings - for key, val in six.iteritems(kwargs.get('env', {})): + for key, val in six.iteritems(kwargs.get("env", {})): if not isinstance(val, six.string_types): - kwargs['env'][key] = six.text_type(val) + kwargs["env"][key] = six.text_type(val) if not isinstance(key, six.string_types): - kwargs['env'][six.text_type(key)] = kwargs['env'].pop(key) - if six.PY2 and 'env' in kwargs: + kwargs["env"][six.text_type(key)] = kwargs["env"].pop(key) + if six.PY2 and "env" in kwargs: # Ensure no unicode in custom env dict, as it can cause # problems with subprocess. - kwargs['env'] = salt.utils.data.encode_dict(kwargs['env']) + kwargs["env"] = salt.utils.data.encode_dict(kwargs["env"]) args = salt.utils.data.decode(args) self.process = subprocess.Popen(args, **kwargs) self.command = args def run(self): - ''' + """ wait for subprocess to terminate and return subprocess' return code. If timeout is reached, throw TimedProcTimeoutError - ''' + """ + def receive(): if self.with_communicate: self.stdout, self.stderr = self.process.communicate(input=self.stdin) @@ -103,11 +110,11 @@ class TimedProc(object): def terminate(): if rt.isAlive(): self.process.terminate() + threading.Timer(10, terminate).start() raise salt.exceptions.TimedProcTimeoutError( - '{0} : Timed out after {1} seconds'.format( - self.command, - six.text_type(self.timeout), + "{0} : Timed out after {1} seconds".format( + self.command, six.text_type(self.timeout), ) ) return self.process.returncode diff --git a/salt/utils/timeout.py b/salt/utils/timeout.py index 5217a77ae79..2b472f41100 100644 --- a/salt/utils/timeout.py +++ b/salt/utils/timeout.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals + import logging import time @@ -10,7 +11,7 @@ BLUR_FACTOR = 0.95 def wait_for(func, timeout=10, step=1, default=None, func_args=(), func_kwargs=None): - ''' + """ Call `func` at regular intervals and Waits until the given function returns a truthy value within the given timeout and returns that value. @@ -27,7 +28,7 @@ def wait_for(func, timeout=10, step=1, default=None, func_args=(), func_kwargs=N @param func_kwargs: **kwargs for `func` @type func_kwargs: dict @return: `default` or result of `func` - ''' + """ if func_kwargs is None: func_kwargs = dict() max_time = time.time() + timeout diff --git a/salt/utils/timeutil.py b/salt/utils/timeutil.py index 74a8226926f..90f9261ad65 100644 --- a/salt/utils/timeutil.py +++ b/salt/utils/timeutil.py @@ -1,34 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Functions various time manipulations. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python import logging import time -from datetime import datetime -from datetime import timedelta -log = logging.getLogger(__name__) +from datetime import datetime, timedelta # Import Salt modules from salt.ext import six +log = logging.getLogger(__name__) + def get_timestamp_at(time_in=None, time_at=None): - ''' + """ Computes the timestamp for a future event that may occur in ``time_in`` time or at ``time_at``. - ''' + """ if time_in: if isinstance(time_in, int): hours = 0 minutes = time_in else: - time_in = time_in.replace('h', ':') - time_in = time_in.replace('m', '') + time_in = time_in.replace("h", ":") + time_in = time_in.replace("m", "") try: - hours, minutes = time_in.split(':') + hours, minutes = time_in.split(":") except ValueError: hours = 0 minutes = time_in @@ -40,12 +40,12 @@ def get_timestamp_at(time_in=None, time_at=None): time_at = time_now + dt return time.mktime(time_at.timetuple()) elif time_at: - log.debug('Predicted at specified as {}'.format(time_at)) + log.debug("Predicted at specified as {}".format(time_at)) if isinstance(time_at, (six.integer_types, float)): # then it's a timestamp return time_at else: - fmts = ('%H%M', '%Hh%M', '%I%p', '%I:%M%p', '%I:%M %p') + fmts = ("%H%M", "%Hh%M", "%I%p", "%I:%M%p", "%I:%M %p") # Support different formats for the timestamp # The current formats accepted are the following: # @@ -54,22 +54,23 @@ def get_timestamp_at(time_in=None, time_at=None): # - 1:20am (and 1:20am - with or without space) for fmt in fmts: try: - log.debug('Trying to match %s', fmt) + log.debug("Trying to match %s", fmt) dt = datetime.strptime(time_at, fmt) return time.mktime(dt.timetuple()) except ValueError: - log.debug('Did not match %s, continue searching', fmt) + log.debug("Did not match %s, continue searching", fmt) continue - msg = '{pat} does not match any of the accepted formats: {fmts}'.format(pat=time_at, - fmts=', '.join(fmts)) + msg = "{pat} does not match any of the accepted formats: {fmts}".format( + pat=time_at, fmts=", ".join(fmts) + ) log.error(msg) raise ValueError(msg) -def get_time_at(time_in=None, time_at=None, out_fmt='%Y-%m-%dT%H:%M:%S'): - ''' +def get_time_at(time_in=None, time_at=None, out_fmt="%Y-%m-%dT%H:%M:%S"): + """ Return the time in human readable format for a future event that may occur in ``time_in`` time, or at ``time_at``. - ''' + """ dt = get_timestamp_at(time_in=time_in, time_at=time_at) return time.strftime(out_fmt, time.localtime(dt)) diff --git a/salt/utils/url.py b/salt/utils/url.py index 7804a34ff6f..631f672b929 100644 --- a/salt/utils/url.py +++ b/salt/utils/url.py @@ -1,36 +1,41 @@ # -*- coding: utf-8 -*- -''' +""" URL utils -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import re import sys -# Import salt libs -from salt.ext.six.moves.urllib.parse import urlparse, urlunparse # pylint: disable=import-error,no-name-in-module import salt.utils.data import salt.utils.path import salt.utils.platform import salt.utils.versions +# Import salt libs +from salt.ext.six.moves.urllib.parse import ( # pylint: disable=import-error,no-name-in-module + urlparse, + urlunparse, +) + def parse(url): - ''' + """ Parse a salt:// URL; return the path and a possible saltenv query. - ''' - if not url.startswith('salt://'): + """ + if not url.startswith("salt://"): return url, None # urlparse will split on valid filename chars such as '?' and '&' - resource = url.split('salt://', 1)[-1] + resource = url.split("salt://", 1)[-1] - if '?env=' in resource: + if "?env=" in resource: # "env" is not supported; Use "saltenv". - path, saltenv = resource.split('?env=', 1)[0], None - elif '?saltenv=' in resource: - path, saltenv = resource.split('?saltenv=', 1) + path, saltenv = resource.split("?env=", 1)[0], None + elif "?saltenv=" in resource: + path, saltenv = resource.split("?saltenv=", 1) else: path, saltenv = resource, None @@ -41,80 +46,80 @@ def parse(url): def create(path, saltenv=None): - ''' + """ join `path` and `saltenv` into a 'salt://' URL. - ''' + """ if salt.utils.platform.is_windows(): path = salt.utils.path.sanitize_win_path(path) path = salt.utils.data.decode(path) - query = 'saltenv={0}'.format(saltenv) if saltenv else '' - url = salt.utils.data.decode(urlunparse(('file', '', path, '', query, ''))) - return 'salt://{0}'.format(url[len('file:///'):]) + query = "saltenv={0}".format(saltenv) if saltenv else "" + url = salt.utils.data.decode(urlunparse(("file", "", path, "", query, ""))) + return "salt://{0}".format(url[len("file:///") :]) def is_escaped(url): - ''' + """ test whether `url` is escaped with `|` - ''' + """ scheme = urlparse(url).scheme if not scheme: - return url.startswith('|') - elif scheme == 'salt': + return url.startswith("|") + elif scheme == "salt": path, saltenv = parse(url) - if salt.utils.platform.is_windows() and '|' in url: - return path.startswith('_') + if salt.utils.platform.is_windows() and "|" in url: + return path.startswith("_") else: - return path.startswith('|') + return path.startswith("|") else: return False def escape(url): - ''' + """ add escape character `|` to `url` - ''' + """ if salt.utils.platform.is_windows(): return url scheme = urlparse(url).scheme if not scheme: - if url.startswith('|'): + if url.startswith("|"): return url else: - return '|{0}'.format(url) - elif scheme == 'salt': + return "|{0}".format(url) + elif scheme == "salt": path, saltenv = parse(url) - if path.startswith('|'): + if path.startswith("|"): return create(path, saltenv) else: - return create('|{0}'.format(path), saltenv) + return create("|{0}".format(path), saltenv) else: return url def unescape(url): - ''' + """ remove escape character `|` from `url` - ''' + """ scheme = urlparse(url).scheme if not scheme: - return url.lstrip('|') - elif scheme == 'salt': + return url.lstrip("|") + elif scheme == "salt": path, saltenv = parse(url) - if salt.utils.platform.is_windows() and '|' in url: - return create(path.lstrip('_'), saltenv) + if salt.utils.platform.is_windows() and "|" in url: + return create(path.lstrip("_"), saltenv) else: - return create(path.lstrip('|'), saltenv) + return create(path.lstrip("|"), saltenv) else: return url def add_env(url, saltenv): - ''' + """ append `saltenv` to `url` as a query parameter to a 'salt://' url - ''' - if not url.startswith('salt://'): + """ + if not url.startswith("salt://"): return url path, senv = parse(url) @@ -122,10 +127,10 @@ def add_env(url, saltenv): def split_env(url): - ''' + """ remove the saltenv query parameter from a 'salt://' url - ''' - if not url.startswith('salt://'): + """ + if not url.startswith("salt://"): return url, None path, senv = parse(url) @@ -133,60 +138,50 @@ def split_env(url): def validate(url, protos): - ''' + """ Return true if the passed URL scheme is in the list of accepted protos - ''' + """ if urlparse(url).scheme in protos: return True return False def strip_proto(url): - ''' + """ Return a copy of the string with the protocol designation stripped, if one was present. - ''' - return re.sub('^[^:/]+://', '', url) + """ + return re.sub("^[^:/]+://", "", url) -def add_http_basic_auth(url, - user=None, - password=None, - https_only=False): - ''' +def add_http_basic_auth(url, user=None, password=None, https_only=False): + """ Return a string with http basic auth incorporated into it - ''' + """ if user is None and password is None: return url else: urltuple = urlparse(url) - if https_only and urltuple.scheme != 'https': - raise ValueError('Basic Auth only supported for HTTPS') + if https_only and urltuple.scheme != "https": + raise ValueError("Basic Auth only supported for HTTPS") if password is None: - netloc = '{0}@{1}'.format( - user, - urltuple.netloc - ) + netloc = "{0}@{1}".format(user, urltuple.netloc) urltuple = urltuple._replace(netloc=netloc) return urlunparse(urltuple) else: - netloc = '{0}:{1}@{2}'.format( - user, - password, - urltuple.netloc - ) + netloc = "{0}:{1}@{2}".format(user, password, urltuple.netloc) urltuple = urltuple._replace(netloc=netloc) return urlunparse(urltuple) def redact_http_basic_auth(output): - ''' + """ Remove HTTP user and password - ''' + """ # We can't use re.compile because re.compile(someregex).sub() doesn't # support flags even in Python 2.7. - url_re = '(https?)://.*@' - redacted = r'\1://<redacted>@' + url_re = "(https?)://.*@" + redacted = r"\1://<redacted>@" if sys.version_info >= (2, 7): # re.sub() supports flags as of 2.7, use this to do a case-insensitive # match. diff --git a/salt/utils/user.py b/salt/utils/user.py index b5d143d5005..5b3d0270048 100644 --- a/salt/utils/user.py +++ b/salt/utils/user.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" Functions for querying and modifying a user account and the groups to which it belongs. -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -18,32 +18,36 @@ import salt.utils.path import salt.utils.platform import salt.utils.stringutils from salt.exceptions import CommandExecutionError -from salt.utils.decorators.jinja import jinja_filter # Import 3rd-party libs from salt.ext import six +from salt.utils.decorators.jinja import jinja_filter # Conditional imports try: import pwd + HAS_PWD = True except ImportError: HAS_PWD = False try: import grp + HAS_GRP = True except ImportError: HAS_GRP = False try: import pysss + HAS_PYSSS = True except ImportError: HAS_PYSSS = False try: import salt.utils.win_functions + HAS_WIN_FUNCTIONS = True except ImportError: HAS_WIN_FUNCTIONS = False @@ -52,26 +56,27 @@ log = logging.getLogger(__name__) def get_user(): - ''' + """ Get the current user - ''' + """ if HAS_PWD: ret = pwd.getpwuid(os.geteuid()).pw_name elif HAS_WIN_FUNCTIONS and salt.utils.win_functions.HAS_WIN32: ret = salt.utils.win_functions.get_current_user() else: raise CommandExecutionError( - 'Required external library (pwd or win32api) not installed') + "Required external library (pwd or win32api) not installed" + ) return salt.utils.stringutils.to_unicode(ret) -@jinja_filter('get_uid') +@jinja_filter("get_uid") def get_uid(user=None): - ''' + """ Get the uid for a given user name. If no user given, the current euid will be returned. If the user does not exist, None will be returned. On systems which do not support pwd or os.geteuid, None will be returned. - ''' + """ if not HAS_PWD: return None elif user is None: @@ -87,13 +92,14 @@ def get_uid(user=None): def _win_user_token_is_admin(user_token): - ''' + """ Using the win32 api, determine if the user with token 'user_token' has administrator rights. See MSDN entry here: http://msdn.microsoft.com/en-us/library/aa376389(VS.85).aspx - ''' + """ + class SID_IDENTIFIER_AUTHORITY(ctypes.Structure): _fields_ = [ ("byte0", ctypes.c_byte), @@ -103,27 +109,39 @@ def _win_user_token_is_admin(user_token): ("byte4", ctypes.c_byte), ("byte5", ctypes.c_byte), ] + nt_authority = SID_IDENTIFIER_AUTHORITY() nt_authority.byte5 = 5 SECURITY_BUILTIN_DOMAIN_RID = 0x20 DOMAIN_ALIAS_RID_ADMINS = 0x220 administrators_group = ctypes.c_void_p() - if ctypes.windll.advapi32.AllocateAndInitializeSid( + if ( + ctypes.windll.advapi32.AllocateAndInitializeSid( ctypes.byref(nt_authority), 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, - ctypes.byref(administrators_group)) == 0: + 0, + 0, + 0, + 0, + 0, + 0, + ctypes.byref(administrators_group), + ) + == 0 + ): raise Exception("AllocateAndInitializeSid failed") try: is_admin = ctypes.wintypes.BOOL() - if ctypes.windll.advapi32.CheckTokenMembership( - user_token, - administrators_group, - ctypes.byref(is_admin)) == 0: + if ( + ctypes.windll.advapi32.CheckTokenMembership( + user_token, administrators_group, ctypes.byref(is_admin) + ) + == 0 + ): raise Exception("CheckTokenMembership failed") return is_admin.value != 0 @@ -132,36 +150,36 @@ def _win_user_token_is_admin(user_token): def _win_current_user_is_admin(): - ''' + """ ctypes.windll.shell32.IsUserAnAdmin() is intentionally avoided due to this function being deprecated. - ''' + """ return _win_user_token_is_admin(0) def get_specific_user(): - ''' + """ Get a user name for publishing. If you find the user is "root" attempt to be more specific - ''' + """ user = get_user() if salt.utils.platform.is_windows(): if _win_current_user_is_admin(): - return 'sudo_{0}'.format(user) + return "sudo_{0}".format(user) else: - env_vars = ('SUDO_USER',) - if user == 'root': + env_vars = ("SUDO_USER",) + if user == "root": for evar in env_vars: if evar in os.environ: - return 'sudo_{0}'.format(os.environ[evar]) + return "sudo_{0}".format(os.environ[evar]) return user def chugid(runas, group=None): - ''' + """ Change the current process to belong to the specified user (and the groups to which it belongs) - ''' + """ uinfo = pwd.getpwnam(runas) supgroups = [] supgroups_seen = set() @@ -171,9 +189,7 @@ def chugid(runas, group=None): target_pw_gid = grp.getgrnam(group).gr_gid except KeyError as err: raise CommandExecutionError( - 'Failed to fetch the GID for {0}. Error: {1}'.format( - group, err - ) + "Failed to fetch the GID for {0}. Error: {1}".format(group, err) ) else: target_pw_gid = uinfo.pw_gid @@ -188,13 +204,13 @@ def chugid(runas, group=None): # the supplemental groups for a running process. On FreeBSD # this does not appear to be strictly true. group_list = get_group_dict(runas, include_default=True) - if sys.platform == 'darwin': - group_list = dict((k, v) for k, v in six.iteritems(group_list) - if not k.startswith('_')) + if sys.platform == "darwin": + group_list = dict( + (k, v) for k, v in six.iteritems(group_list) if not k.startswith("_") + ) for group_name in group_list: gid = group_list[group_name] - if (gid not in supgroups_seen - and not supgroups_seen.add(gid)): + if gid not in supgroups_seen and not supgroups_seen.add(gid): supgroups.append(gid) if os.getgid() != target_pw_gid: @@ -202,7 +218,7 @@ def chugid(runas, group=None): os.setgid(target_pw_gid) except OSError as err: raise CommandExecutionError( - 'Failed to change from gid {0} to {1}. Error: {2}'.format( + "Failed to change from gid {0} to {1}. Error: {2}".format( os.getgid(), target_pw_gid, err ) ) @@ -213,7 +229,7 @@ def chugid(runas, group=None): os.setgroups(supgroups) except OSError as err: raise CommandExecutionError( - 'Failed to set supplemental groups to {0}. Error: {1}'.format( + "Failed to set supplemental groups to {0}. Error: {1}".format( supgroups, err ) ) @@ -223,17 +239,17 @@ def chugid(runas, group=None): os.setuid(uinfo.pw_uid) except OSError as err: raise CommandExecutionError( - 'Failed to change from uid {0} to {1}. Error: {2}'.format( + "Failed to change from uid {0} to {1}. Error: {2}".format( os.getuid(), uinfo.pw_uid, err ) ) def chugid_and_umask(runas, umask, group=None): - ''' + """ Helper method for for subprocess.Popen to initialise uid/gid and umask for the new process. - ''' + """ set_runas = False set_grp = False @@ -258,37 +274,37 @@ def chugid_and_umask(runas, umask, group=None): def get_default_group(user): - ''' + """ Returns the specified user's default group. If the user doesn't exist, a KeyError will be raised. - ''' - return grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name \ - if HAS_GRP and HAS_PWD \ - else None + """ + return ( + grp.getgrgid(pwd.getpwnam(user).pw_gid).gr_name if HAS_GRP and HAS_PWD else None + ) def get_group_list(user, include_default=True): - ''' + """ Returns a list of all of the system group names of which the user is a member. - ''' + """ if HAS_GRP is False or HAS_PWD is False: return [] group_names = None ugroups = set() - if hasattr(os, 'getgrouplist'): + if hasattr(os, "getgrouplist"): # Try os.getgrouplist, available in python >= 3.3 - log.trace('Trying os.getgrouplist for \'%s\'', user) + log.trace("Trying os.getgrouplist for '%s'", user) try: group_names = [ - grp.getgrgid(grpid).gr_name for grpid in - os.getgrouplist(user, pwd.getpwnam(user).pw_gid) + grp.getgrgid(grpid).gr_name + for grpid in os.getgrouplist(user, pwd.getpwnam(user).pw_gid) ] except Exception: # pylint: disable=broad-except pass elif HAS_PYSSS: # Try pysss.getgrouplist - log.trace('Trying pysss.getgrouplist for \'%s\'', user) + log.trace("Trying pysss.getgrouplist for '%s'", user) try: group_names = list(pysss.getgrouplist(user)) except Exception: # pylint: disable=broad-except @@ -298,7 +314,7 @@ def get_group_list(user, include_default=True): # Fall back to generic code # Include the user's default group to match behavior of # os.getgrouplist() and pysss.getgrouplist() - log.trace('Trying generic group list for \'%s\'', user) + log.trace("Trying generic group list for '%s'", user) group_names = [g.gr_name for g in grp.getgrall() if user in g.gr_mem] try: default_group = get_default_group(user) @@ -322,16 +338,16 @@ def get_group_list(user, include_default=True): except KeyError: # If for some reason the user does not have a default group pass - log.trace('Group list for user \'%s\': %s', user, sorted(ugroups)) + log.trace("Group list for user '%s': %s", user, sorted(ugroups)) return sorted(ugroups) def get_group_dict(user=None, include_default=True): - ''' + """ Returns a dict of all of the system groups as keys, and group ids as values, of which the user is a member. E.g.: {'staff': 501, 'sudo': 27} - ''' + """ if HAS_GRP is False or HAS_PWD is False: return {} group_dict = {} @@ -342,26 +358,24 @@ def get_group_dict(user=None, include_default=True): def get_gid_list(user, include_default=True): - ''' + """ Returns a list of all of the system group IDs of which the user is a member. - ''' + """ if HAS_GRP is False or HAS_PWD is False: return [] gid_list = list( - six.itervalues( - get_group_dict(user, include_default=include_default) - ) + six.itervalues(get_group_dict(user, include_default=include_default)) ) return sorted(set(gid_list)) def get_gid(group=None): - ''' + """ Get the gid for a given group name. If no group given, the current egid will be returned. If the group does not exist, None will be returned. On systems which do not support grp or os.getegid it will return None. - ''' + """ if not HAS_GRP: return None if group is None: diff --git a/salt/utils/validate/__init__.py b/salt/utils/validate/__init__.py index da8023c053d..f6bd3e7180c 100644 --- a/salt/utils/validate/__init__.py +++ b/salt/utils/validate/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" The salt.utils.validate package contains routines for validating components and values. -''' +""" diff --git a/salt/utils/validate/net.py b/salt/utils/validate/net.py index f07506dd0a5..fd49d43edb3 100644 --- a/salt/utils/validate/net.py +++ b/salt/utils/validate/net.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Various network validation utilities -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs @@ -13,39 +13,42 @@ import salt.utils.platform # Import 3rd-party libs from salt.ext.six import string_types + if salt.utils.platform.is_windows(): from salt.ext import win_inet_pton # pylint: disable=unused-import def mac(addr): - ''' + """ Validates a mac address - ''' - valid = re.compile(r''' + """ + valid = re.compile( + r""" (^([0-9A-F]{1,2}[-]){5}([0-9A-F]{1,2})$ |^([0-9A-F]{1,2}[:]){5}([0-9A-F]{1,2})$ |^([0-9A-F]{1,2}[.]){5}([0-9A-F]{1,2})$) - ''', - re.VERBOSE | re.IGNORECASE) + """, + re.VERBOSE | re.IGNORECASE, + ) return valid.match(addr) is not None def __ip_addr(addr, address_family=socket.AF_INET): - ''' + """ Returns True if the IP address (and optional subnet) are valid, otherwise returns False. - ''' - mask_max = '32' + """ + mask_max = "32" if address_family == socket.AF_INET6: - mask_max = '128' + mask_max = "128" try: - if '/' not in addr: - addr = '{addr}/{mask_max}'.format(addr=addr, mask_max=mask_max) + if "/" not in addr: + addr = "{addr}/{mask_max}".format(addr=addr, mask_max=mask_max) except TypeError: return False - ip, mask = addr.rsplit('/', 1) + ip, mask = addr.rsplit("/", 1) # Verify that IP address is valid try: @@ -66,37 +69,37 @@ def __ip_addr(addr, address_family=socket.AF_INET): def ipv4_addr(addr): - ''' + """ Returns True if the IPv4 address (and optional subnet) are valid, otherwise returns False. - ''' + """ return __ip_addr(addr, socket.AF_INET) def ipv6_addr(addr): - ''' + """ Returns True if the IPv6 address (and optional subnet) are valid, otherwise returns False. - ''' + """ return __ip_addr(addr, socket.AF_INET6) def ip_addr(addr): - ''' + """ Returns True if the IPv4 or IPv6 address (and optional subnet) are valid, otherwise returns False. - ''' + """ return ipv4_addr(addr) or ipv6_addr(addr) def netmask(mask): - ''' + """ Returns True if the value passed is a valid netmask, otherwise return False - ''' + """ if not isinstance(mask, string_types): return False - octets = mask.split('.') + octets = mask.split(".") if not len(octets) == 4: return False diff --git a/salt/utils/validate/path.py b/salt/utils/validate/path.py index 6dc3ee8cb1f..9c32600b952 100644 --- a/salt/utils/validate/path.py +++ b/salt/utils/validate/path.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,7 +7,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~ Several path related validators -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -15,14 +15,14 @@ import os def is_writeable(path, check_parent=False): - ''' + """ Check if a given path is writeable by the current user. :param path: The path to check :param check_parent: If the path to check does not exist, check for the ability to write to the parent directory instead :returns: True or False - ''' + """ if os.access(path, os.F_OK) and os.access(path, os.W_OK): # The path exists and is writeable @@ -51,12 +51,12 @@ def is_writeable(path, check_parent=False): def is_readable(path): - ''' + """ Check if a given path is readable by the current user. :param path: The path to check :returns: True or False - ''' + """ if os.access(path, os.F_OK) and os.access(path, os.R_OK): # The path exists and is readable @@ -67,11 +67,11 @@ def is_readable(path): def is_executable(path): - ''' + """ Check if a given path is executable by the current user. :param path: The path to check :returns: True or False - ''' + """ return os.access(path, os.X_OK) diff --git a/salt/utils/validate/user.py b/salt/utils/validate/user.py index 7d59deae95b..3255a30a543 100644 --- a/salt/utils/validate/user.py +++ b/salt/utils/validate/user.py @@ -1,23 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" Various user validation utilities -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import re -import logging + from salt.ext import six log = logging.getLogger(__name__) -VALID_USERNAME = re.compile(r'[a-z_][a-z0-9_-]*[$]?', re.IGNORECASE) +VALID_USERNAME = re.compile(r"[a-z_][a-z0-9_-]*[$]?", re.IGNORECASE) def valid_username(user): - ''' + """ Validates a username based on the guidelines in `useradd(8)` - ''' + """ if not isinstance(user, six.string_types): return False diff --git a/salt/utils/value.py b/salt/utils/value.py index cf564d7aa6c..9e48169f516 100644 --- a/salt/utils/value.py +++ b/salt/utils/value.py @@ -1,18 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Utility functions used for values. .. versionadded:: 2018.3.0 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def xor(*variables): - ''' + """ XOR definition for multiple variables - ''' + """ sum_ = False for value in variables: sum_ = sum_ ^ bool(value) diff --git a/salt/utils/vault.py b/salt/utils/vault.py index f385a4a915a..54aea182a93 100644 --- a/salt/utils/vault.py +++ b/salt/utils/vault.py @@ -1,18 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" :maintainer: SaltStack :maturity: new :platform: all Utilities supporting modules for Hashicorp Vault. Configuration instructions are documented in the execution module docs. -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import base64 import logging -import requests +import requests import salt.crypt import salt.exceptions import salt.utils.versions @@ -37,127 +38,142 @@ def __virtual__(): # pylint: disable=expected-2-blank-lines-found-0 def _get_token_and_url_from_master(): - ''' + """ Get a token with correct policies for the minion, and the url to the Vault service - ''' - minion_id = __grains__['id'] - pki_dir = __opts__['pki_dir'] + """ + minion_id = __grains__["id"] + pki_dir = __opts__["pki_dir"] # When rendering pillars, the module executes on the master, but the token # should be issued for the minion, so that the correct policies are applied - if __opts__.get('__role', 'minion') == 'minion': - private_key = '{0}/minion.pem'.format(pki_dir) - log.debug('Running on minion, signing token request with key %s', - private_key) - signature = base64.b64encode(salt.crypt.sign_message( - private_key, - minion_id - )) - result = __salt__['publish.runner']( - 'vault.generate_token', - arg=[minion_id, signature] + if __opts__.get("__role", "minion") == "minion": + private_key = "{0}/minion.pem".format(pki_dir) + log.debug("Running on minion, signing token request with key %s", private_key) + signature = base64.b64encode(salt.crypt.sign_message(private_key, minion_id)) + result = __salt__["publish.runner"]( + "vault.generate_token", arg=[minion_id, signature] ) else: - private_key = '{0}/master.pem'.format(pki_dir) - log.debug('Running on master, signing token request for %s with key %s', - minion_id, private_key) - signature = base64.b64encode(salt.crypt.sign_message( + private_key = "{0}/master.pem".format(pki_dir) + log.debug( + "Running on master, signing token request for %s with key %s", + minion_id, private_key, - minion_id - )) - result = __salt__['saltutil.runner']( - 'vault.generate_token', + ) + signature = base64.b64encode(salt.crypt.sign_message(private_key, minion_id)) + result = __salt__["saltutil.runner"]( + "vault.generate_token", minion_id=minion_id, signature=signature, - impersonated_by_master=True + impersonated_by_master=True, ) if not result: - log.error('Failed to get token from master! No result returned - ' - 'is the peer publish configuration correct?') + log.error( + "Failed to get token from master! No result returned - " + "is the peer publish configuration correct?" + ) raise salt.exceptions.CommandExecutionError(result) if not isinstance(result, dict): - log.error('Failed to get token from master! ' - 'Response is not a dict: %s', result) + log.error( + "Failed to get token from master! " "Response is not a dict: %s", result + ) raise salt.exceptions.CommandExecutionError(result) - if 'error' in result: - log.error('Failed to get token from master! ' - 'An error was returned: %s', result['error']) + if "error" in result: + log.error( + "Failed to get token from master! " "An error was returned: %s", + result["error"], + ) raise salt.exceptions.CommandExecutionError(result) return { - 'url': result['url'], - 'token': result['token'], - 'verify': result.get('verify', None), + "url": result["url"], + "token": result["token"], + "verify": result.get("verify", None), } def get_vault_connection(): - ''' + """ Get the connection details for calling Vault, from local configuration if it exists, or from the master otherwise - ''' + """ + def _use_local_config(): - log.debug('Using Vault connection details from local config') + log.debug("Using Vault connection details from local config") try: - if __opts__['vault']['auth']['method'] == 'approle': - verify = __opts__['vault'].get('verify', None) + if __opts__["vault"]["auth"]["method"] == "approle": + verify = __opts__["vault"].get("verify", None) if _selftoken_expired(): - log.debug('Vault token expired. Recreating one') + log.debug("Vault token expired. Recreating one") # Requesting a short ttl token - url = '{0}/v1/auth/approle/login'.format(__opts__['vault']['url']) - payload = {'role_id': __opts__['vault']['auth']['role_id']} - if 'secret_id' in __opts__['vault']['auth']: - payload['secret_id'] = __opts__['vault']['auth']['secret_id'] + url = "{0}/v1/auth/approle/login".format(__opts__["vault"]["url"]) + payload = {"role_id": __opts__["vault"]["auth"]["role_id"]} + if "secret_id" in __opts__["vault"]["auth"]: + payload["secret_id"] = __opts__["vault"]["auth"]["secret_id"] response = requests.post(url, json=payload, verify=verify) if response.status_code != 200: - errmsg = 'An error occured while getting a token from approle' + errmsg = "An error occured while getting a token from approle" raise salt.exceptions.CommandExecutionError(errmsg) - __opts__['vault']['auth']['token'] = response.json()['auth']['client_token'] - if __opts__['vault']['auth']['method'] == 'wrapped_token': - verify = __opts__['vault'].get('verify', None) + __opts__["vault"]["auth"]["token"] = response.json()["auth"][ + "client_token" + ] + if __opts__["vault"]["auth"]["method"] == "wrapped_token": + verify = __opts__["vault"].get("verify", None) if _wrapped_token_valid(): - url = '{0}/v1/sys/wrapping/unwrap'.format(__opts__['vault']['url']) - headers = {'X-Vault-Token': __opts__['vault']['auth']['token']} + url = "{0}/v1/sys/wrapping/unwrap".format(__opts__["vault"]["url"]) + headers = {"X-Vault-Token": __opts__["vault"]["auth"]["token"]} response = requests.post(url, headers=headers, verify=verify) if response.status_code != 200: - errmsg = 'An error occured while unwrapping vault token' + errmsg = "An error occured while unwrapping vault token" raise salt.exceptions.CommandExecutionError(errmsg) - __opts__['vault']['auth']['token'] = response.json()['auth']['client_token'] + __opts__["vault"]["auth"]["token"] = response.json()["auth"][ + "client_token" + ] return { - 'url': __opts__['vault']['url'], - 'token': __opts__['vault']['auth']['token'], - 'verify': __opts__['vault'].get('verify', None) + "url": __opts__["vault"]["url"], + "token": __opts__["vault"]["auth"]["token"], + "verify": __opts__["vault"].get("verify", None), } except KeyError as err: - errmsg = 'Minion has "vault" config section, but could not find key "{0}" within'.format(err.message) + errmsg = 'Minion has "vault" config section, but could not find key "{0}" within'.format( + err.message + ) raise salt.exceptions.CommandExecutionError(errmsg) - if 'vault' in __opts__ and __opts__.get('__role', 'minion') == 'master': - if 'id' in __grains__: - log.debug('Contacting master for Vault connection details') + if "vault" in __opts__ and __opts__.get("__role", "minion") == "master": + if "id" in __grains__: + log.debug("Contacting master for Vault connection details") return _get_token_and_url_from_master() else: return _use_local_config() - elif any((__opts__['local'], __opts__['file_client'] == 'local', __opts__['master_type'] == 'disable')): + elif any( + ( + __opts__["local"], + __opts__["file_client"] == "local", + __opts__["master_type"] == "disable", + ) + ): return _use_local_config() else: - log.debug('Contacting master for Vault connection details') + log.debug("Contacting master for Vault connection details") return _get_token_and_url_from_master() -def make_request(method, resource, token=None, vault_url=None, get_token_url=False, **args): - ''' +def make_request( + method, resource, token=None, vault_url=None, get_token_url=False, **args +): + """ Make a request to Vault - ''' + """ if not token or not vault_url: connection = get_vault_connection() - token, vault_url = connection['token'], connection['url'] - if 'verify' not in args: - args['verify'] = connection['verify'] + token, vault_url = connection["token"], connection["url"] + if "verify" not in args: + args["verify"] = connection["verify"] url = "{0}/{1}".format(vault_url, resource) - headers = {'X-Vault-Token': token, 'Content-Type': 'application/json'} + headers = {"X-Vault-Token": token, "Content-Type": "application/json"} response = requests.request(method, url, headers=headers, **args) if get_token_url: @@ -167,40 +183,133 @@ def make_request(method, resource, token=None, vault_url=None, get_token_url=Fal def _selftoken_expired(): - ''' + """ Validate the current token exists and is still valid - ''' + """ try: - verify = __opts__['vault'].get('verify', None) - url = '{0}/v1/auth/token/lookup-self'.format(__opts__['vault']['url']) - if 'token' not in __opts__['vault']['auth']: + verify = __opts__["vault"].get("verify", None) + url = "{0}/v1/auth/token/lookup-self".format(__opts__["vault"]["url"]) + if "token" not in __opts__["vault"]["auth"]: return True - headers = {'X-Vault-Token': __opts__['vault']['auth']['token']} + headers = {"X-Vault-Token": __opts__["vault"]["auth"]["token"]} response = requests.get(url, headers=headers, verify=verify) if response.status_code != 200: return True return False except Exception as e: # pylint: disable=broad-except raise salt.exceptions.CommandExecutionError( - 'Error while looking up self token : {0}'.format(e) + "Error while looking up self token : {0}".format(e) ) def _wrapped_token_valid(): - ''' + """ Validate the wrapped token exists and is still valid - ''' + """ try: - verify = __opts__['vault'].get('verify', None) - url = '{0}/v1/sys/wrapping/lookup'.format(__opts__['vault']['url']) - if 'token' not in __opts__['vault']['auth']: + verify = __opts__["vault"].get("verify", None) + url = "{0}/v1/sys/wrapping/lookup".format(__opts__["vault"]["url"]) + if "token" not in __opts__["vault"]["auth"]: return False - headers = {'X-Vault-Token': __opts__['vault']['auth']['token']} + headers = {"X-Vault-Token": __opts__["vault"]["auth"]["token"]} response = requests.post(url, headers=headers, verify=verify) if response.status_code != 200: return False return True except Exception as e: # pylint: disable=broad-except raise salt.exceptions.CommandExecutionError( - 'Error while looking up wrapped token : {0}'.format(e) + "Error while looking up wrapped token : {0}".format(e) ) + + +def is_v2(path): + """ + Determines if a given secret path is kv version 1 or 2 + CLI Example: + .. code-block:: bash + salt '*' vault.is_v2 "secret/my/secret" + """ + ret = {"v2": False, "data": path, "metadata": path, "delete": path, "type": None} + path_metadata = _get_secret_path_metadata(path) + if not path_metadata: + # metadata lookup failed. Simply return not v2 + return ret + ret["type"] = path_metadata.get("type", "kv") + if ret["type"] == "kv" and path_metadata.get("options", {}).get("version", "1") in [ + "2" + ]: + ret["v2"] = True + ret["data"] = _v2_the_path(path, path_metadata.get("path", path)) + ret["metadata"] = _v2_the_path( + path, path_metadata.get("path", path), "metadata" + ) + ret["destroy"] = _v2_the_path(path, path_metadata.get("path", path), "destroy") + return ret + + +def _v2_the_path(path, pfilter, ptype="data"): + """ + Given a path, a filter, and a path type, properly inject 'data' or 'metadata' into the path + CLI Example: + .. code-block:: python + _v2_the_path('dev/secrets/fu/bar', 'dev/secrets', 'data') => 'dev/secrets/data/fu/bar' + """ + possible_types = ["data", "metadata", "destroy"] + assert ptype in possible_types + msg = "Path {} already contains {} in the right place - saltstack duct tape?".format( + path, ptype + ) + + path = path.rstrip("/").lstrip("/") + pfilter = pfilter.rstrip("/").lstrip("/") + + together = pfilter + "/" + ptype + + otype = possible_types[0] if possible_types[0] != ptype else possible_types[1] + other = pfilter + "/" + otype + if path.startswith(other): + path = path.replace(other, together, 1) + msg = 'Path is a "{}" type but "{}" type requested - Flipping: {}'.format( + otype, ptype, path + ) + elif not path.startswith(together): + msg = "Converting path to v2 {} => {}".format( + path, path.replace(pfilter, together, 1) + ) + path = path.replace(pfilter, together, 1) + + log.debug(msg) + return path + + +def _get_secret_path_metadata(path): + """ + Given a path query vault to determine where the mount point is, it's type and version + CLI Example: + .. code-block:: python + _get_secret_path_metadata('dev/secrets/fu/bar') + """ + ckey = "vault_secret_path_metadata" + if ckey not in __context__: + __context__[ckey] = {} + + ret = None + if path.startswith(tuple(__context__[ckey].keys())): + log.debug("Found cached metadata for %s", path) + ret = next(v for k, v in __context__[ckey].items() if path.startswith(k)) + else: + log.debug("Fetching metadata for %s", path) + try: + url = "v1/sys/internal/ui/mounts/{0}".format(path) + response = make_request("GET", url) + if response.ok: + response.raise_for_status() + if response.json().get("data", False): + log.debug("Got metadata for %s", path) + ret = response.json()["data"] + __context__[ckey][path] = ret + else: + raise response.json() + except Exception as err: # pylint: disable=broad-except + log.error("Failed to list secrets! %s: %s", type(err).__name__, err) + return ret diff --git a/salt/utils/verify.py b/salt/utils/verify.py index 57f6bb371fc..6de117be4df 100644 --- a/salt/utils/verify.py +++ b/salt/utils/verify.py @@ -1,19 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" A few checks to make sure the environment is sane -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Original Author: Jeff Schroeder <jeffschroeder@computer.org> +import errno +import logging # Import python libs import os import re -import sys -import stat -import errno import socket -import logging +import stat +import sys + +import salt.defaults.exitcodes +import salt.utils.files +import salt.utils.path +import salt.utils.platform +import salt.utils.user +from salt.exceptions import CommandExecutionError, SaltClientError, SaltSystemExit + +# Import salt libs +from salt.log import is_console_configured +from salt.log.setup import LOG_LEVELS + +# Original Author: Jeff Schroeder <jeffschroeder@computer.org> + # Import third party libs try: @@ -21,27 +34,17 @@ try: except ImportError: import resource -# Import salt libs -from salt.log import is_console_configured -from salt.log.setup import LOG_LEVELS -from salt.exceptions import SaltClientError, SaltSystemExit, \ - CommandExecutionError -import salt.defaults.exitcodes -import salt.utils.files -import salt.utils.path -import salt.utils.platform -import salt.utils.user log = logging.getLogger(__name__) -ROOT_DIR = 'c:\\salt' if salt.utils.platform.is_windows() else '/' -DEFAULT_SCHEMES = ['tcp://', 'udp://', 'file://'] +ROOT_DIR = "c:\\salt" if salt.utils.platform.is_windows() else "/" +DEFAULT_SCHEMES = ["tcp://", "udp://", "file://"] def zmq_version(): - ''' + """ ZeroMQ python bindings >= 2.1.9 are required - ''' + """ try: import zmq except Exception: # pylint: disable=broad-except @@ -50,7 +53,7 @@ def zmq_version(): ver = zmq.__version__ # The last matched group can be None if the version # is something like 3.1 and that will work properly - match = re.match(r'^(\d+)\.(\d+)(?:\.(\d+))?', ver) + match = re.match(r"^(\d+)\.(\d+)(?:\.(\d+))?", ver) # Fallthrough and hope for the best if not match: @@ -75,7 +78,7 @@ def zmq_version(): if major == 2 and minor == 1: # zmq 2.1dev could be built against a newer libzmq if "dev" in ver and not point: - msg = 'Using dev zmq module, please report unexpected results' + msg = "Using dev zmq module, please report unexpected results" if is_console_configured(): log.warning(msg) else: @@ -87,23 +90,25 @@ def zmq_version(): return True # If all else fails, gracefully croak and warn the user - log.critical('ZeroMQ python bindings >= 2.1.9 are required') - if 'salt-master' in sys.argv[0]: - msg = ('The Salt Master is unstable using a ZeroMQ version ' - 'lower than 2.1.11 and requires this fix: http://lists.zeromq.' - 'org/pipermail/zeromq-dev/2011-June/012094.html') + log.critical("ZeroMQ python bindings >= 2.1.9 are required") + if "salt-master" in sys.argv[0]: + msg = ( + "The Salt Master is unstable using a ZeroMQ version " + "lower than 2.1.11 and requires this fix: http://lists.zeromq." + "org/pipermail/zeromq-dev/2011-June/012094.html" + ) if is_console_configured(): log.critical(msg) else: - sys.stderr.write('CRITICAL {0}\n'.format(msg)) + sys.stderr.write("CRITICAL {0}\n".format(msg)) return False def lookup_family(hostname): - ''' + """ Lookup a hostname and determine its address family. The first address returned will be AF_INET6 if the system is IPv6-enabled, and AF_INET otherwise. - ''' + """ # If lookups fail, fall back to AF_INET sockets (and v4 addresses). fallback = socket.AF_INET try: @@ -119,9 +124,9 @@ def lookup_family(hostname): def verify_socket(interface, pub_port, ret_port): - ''' + """ Attempt to bind to the sockets to verify that they are available - ''' + """ addr_family = lookup_family(interface) for port in pub_port, ret_port: @@ -130,16 +135,16 @@ def verify_socket(interface, pub_port, ret_port): sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((interface, int(port))) except Exception as exc: # pylint: disable=broad-except - msg = 'Unable to bind socket {0}:{1}'.format(interface, port) + msg = "Unable to bind socket {0}:{1}".format(interface, port) if exc.args: - msg = '{0}, error: {1}'.format(msg, str(exc)) + msg = "{0}, error: {1}".format(msg, str(exc)) else: - msg = '{0}, this might not be a problem.'.format(msg) - msg += '; Is there another salt-master running?' + msg = "{0}, this might not be a problem.".format(msg) + msg += "; Is there another salt-master running?" if is_console_configured(): log.warning(msg) else: - sys.stderr.write('WARNING: {0}\n'.format(msg)) + sys.stderr.write("WARNING: {0}\n".format(msg)) return False finally: sock.close() @@ -161,27 +166,30 @@ def verify_logs_filter(files): def verify_log_files(files, user): - ''' + """ Verify the log files exist and are owned by the named user. Filenames that begin with tcp:// and udp:// will be filtered out. Filenames that begin with file:// are handled correctly - ''' + """ return verify_files(verify_logs_filter(files), user) def verify_files(files, user): - ''' + """ Verify that the named files exist and are owned by the named user - ''' + """ if salt.utils.platform.is_windows(): return True import pwd # after confirming not running Windows + try: pwnam = pwd.getpwnam(user) uid = pwnam[2] except KeyError: - err = ('Failed to prepare the Salt environment for user ' - '{0}. The user is not available.\n').format(user) + err = ( + "Failed to prepare the Salt environment for user " + "{0}. The user is not available.\n" + ).format(user) sys.stderr.write(err) sys.exit(salt.defaults.exitcodes.EX_NOUSER) @@ -195,16 +203,18 @@ def verify_files(files, user): if err.errno != errno.EEXIST: raise if not os.path.isfile(fn_): - with salt.utils.files.fopen(fn_, 'w'): + with salt.utils.files.fopen(fn_, "w"): pass except IOError as err: if os.path.isfile(dirname): - msg = 'Failed to create path {0}, is {1} a file?'.format(fn_, dirname) + msg = "Failed to create path {0}, is {1} a file?".format(fn_, dirname) raise SaltSystemExit(msg=msg) if err.errno != errno.EACCES: raise - msg = 'No permissions to access "{0}", are you running as the correct user?'.format(fn_) + msg = 'No permissions to access "{0}", are you running as the correct user?'.format( + fn_ + ) raise SaltSystemExit(msg=msg) except OSError as err: # pylint: disable=duplicate-except @@ -221,22 +231,18 @@ def verify_files(files, user): def verify_env( - dirs, - user, - permissive=False, - pki_dir='', - skip_extra=False, - root_dir=ROOT_DIR): - ''' + dirs, user, permissive=False, pki_dir="", skip_extra=False, root_dir=ROOT_DIR +): + """ Verify that the named directories are in place and that the environment can shake the salt - ''' + """ if salt.utils.platform.is_windows(): - return win_verify_env(root_dir, - dirs, - permissive=permissive, - skip_extra=skip_extra) + return win_verify_env( + root_dir, dirs, permissive=permissive, skip_extra=skip_extra + ) import pwd # after confirming not running Windows + try: pwnam = pwd.getpwnam(user) uid = pwnam[2] @@ -244,8 +250,10 @@ def verify_env( groups = salt.utils.user.get_gid_list(user, include_default=False) except KeyError: - err = ('Failed to prepare the Salt environment for user ' - '{0}. The user is not available.\n').format(user) + err = ( + "Failed to prepare the Salt environment for user " + "{0}. The user is not available.\n" + ).format(user) sys.stderr.write(err) sys.exit(salt.defaults.exitcodes.EX_NOUSER) for dir_ in dirs: @@ -275,13 +283,13 @@ def verify_env( else: # chown the file for the new user os.chown(dir_, uid, gid) - for subdir in [a for a in os.listdir(dir_) if 'jobs' not in a]: + for subdir in [a for a in os.listdir(dir_) if "jobs" not in a]: fsubdir = os.path.join(dir_, subdir) - if '{0}jobs'.format(os.path.sep) in fsubdir: + if "{0}jobs".format(os.path.sep) in fsubdir: continue for root, dirs, files in salt.utils.path.os_walk(fsubdir): for name in files: - if name.startswith('.'): + if name.startswith("."): continue path = os.path.join(root, name) try: @@ -331,19 +339,22 @@ def verify_env( def check_user(user): - ''' + """ Check user and assign process uid/gid. - ''' + """ if salt.utils.platform.is_windows(): return True if user == salt.utils.user.get_user(): return True import pwd # after confirming not running Windows + try: pwuser = pwd.getpwnam(user) try: - if hasattr(os, 'initgroups'): - os.initgroups(user, pwuser.pw_gid) # pylint: disable=minimum-python-version + if hasattr(os, "initgroups"): + os.initgroups( + user, pwuser.pw_gid + ) # pylint: disable=minimum-python-version else: os.setgroups(salt.utils.user.get_gid_list(user, include_default=False)) os.setgid(pwuser.pw_gid) @@ -351,13 +362,13 @@ def check_user(user): # We could just reset the whole environment but let's just override # the variables we can get from pwuser - if 'HOME' in os.environ: - os.environ['HOME'] = pwuser.pw_dir + if "HOME" in os.environ: + os.environ["HOME"] = pwuser.pw_dir - if 'SHELL' in os.environ: - os.environ['SHELL'] = pwuser.pw_shell + if "SHELL" in os.environ: + os.environ["SHELL"] = pwuser.pw_shell - for envvar in ('USER', 'LOGNAME'): + for envvar in ("USER", "LOGNAME"): if envvar in os.environ: os.environ[envvar] = pwuser.pw_name @@ -380,7 +391,7 @@ def check_user(user): def list_path_traversal(path): - ''' + """ Returns a full list of directories leading up to, and including, a path. So list_path_traversal('/path/to/salt') would return: @@ -390,10 +401,10 @@ def list_path_traversal(path): This routine has been tested on Windows systems as well. list_path_traversal('c:\\path\\to\\salt') on Windows would return: ['c:\\', 'c:\\path', 'c:\\path\\to', 'c:\\path\\to\\salt'] - ''' + """ out = [path] (head, tail) = os.path.split(path) - if tail == '': + if tail == "": # paths with trailing separators will return an empty string out = [head] (head, tail) = os.path.split(head) @@ -404,26 +415,26 @@ def list_path_traversal(path): return out -def check_path_traversal(path, user='root', skip_perm_errors=False): - ''' +def check_path_traversal(path, user="root", skip_perm_errors=False): + """ Walk from the root up to a directory and verify that the current user has access to read each directory. This is used for making sure a user can read all parent directories of the minion's key before trying to go and generate a new key and raising an IOError - ''' + """ for tpath in list_path_traversal(path): if not os.access(tpath, os.R_OK): - msg = 'Could not access {0}.'.format(tpath) + msg = "Could not access {0}.".format(tpath) if not os.path.exists(tpath): - msg += ' Path does not exist.' + msg += " Path does not exist." else: current_user = salt.utils.user.get_user() # Make the error message more intelligent based on how # the user invokes salt-call or whatever other script. if user != current_user: - msg += ' Try running as user {0}.'.format(user) + msg += " Try running as user {0}.".format(user) else: - msg += ' Please give {0} read permissions.'.format(user) + msg += " Please give {0} read permissions.".format(user) # We don't need to bail on config file permission errors # if the CLI @@ -436,11 +447,11 @@ def check_path_traversal(path, user='root', skip_perm_errors=False): def check_max_open_files(opts): - ''' + """ Check the number of max allowed open files and adjust if needed - ''' - mof_c = opts.get('max_open_files', 100000) - if sys.platform.startswith('win'): + """ + mof_c = opts.get("max_open_files", 100000) + if sys.platform.startswith("win"): # Check the Windows API for more detail on this # http://msdn.microsoft.com/en-us/library/xt874334(v=vs.71).aspx # and the python binding http://timgolden.me.uk/pywin32-docs/win32file.html @@ -448,13 +459,10 @@ def check_max_open_files(opts): else: mof_s, mof_h = resource.getrlimit(resource.RLIMIT_NOFILE) - accepted_keys_dir = os.path.join(opts.get('pki_dir'), 'minions') + accepted_keys_dir = os.path.join(opts.get("pki_dir"), "minions") accepted_count = len(os.listdir(accepted_keys_dir)) - log.debug( - 'This salt-master instance has accepted %s minion keys.', - accepted_count - ) + log.debug("This salt-master instance has accepted %s minion keys.", accepted_count) level = logging.INFO @@ -467,15 +475,13 @@ def check_max_open_files(opts): return msg = ( - 'The number of accepted minion keys({0}) should be lower than 1/4 ' - 'of the max open files soft setting({1}). '.format( - accepted_count, mof_s - ) + "The number of accepted minion keys({0}) should be lower than 1/4 " + "of the max open files soft setting({1}). ".format(accepted_count, mof_s) ) if accepted_count >= mof_s: # This should never occur, it might have already crashed - msg += 'salt-master will crash pretty soon! ' + msg += "salt-master will crash pretty soon! " level = logging.CRITICAL elif (accepted_count * 2) >= mof_s: # This is way too low, CRITICAL @@ -487,22 +493,24 @@ def check_max_open_files(opts): level = logging.INFO if mof_c < mof_h: - msg += ('According to the system\'s hard limit, there\'s still a ' - 'margin of {0} to raise the salt\'s max_open_files ' - 'setting. ').format(mof_h - mof_c) + msg += ( + "According to the system's hard limit, there's still a " + "margin of {0} to raise the salt's max_open_files " + "setting. " + ).format(mof_h - mof_c) - msg += 'Please consider raising this value.' + msg += "Please consider raising this value." log.log(level=level, msg=msg) def clean_path(root, path, subdir=False): - ''' + """ Accepts the root the path needs to be under and verifies that the path is under said root. Pass in subdir=True if the path can result in a subdirectory of the root instead of having to reside directly in the root - ''' + """ if not os.path.isabs(root): - return '' + return "" if not os.path.isabs(path): path = os.path.join(root, path) path = os.path.normpath(path) @@ -512,36 +520,28 @@ def clean_path(root, path, subdir=False): else: if os.path.dirname(path) == os.path.normpath(root): return path - return '' + return "" def valid_id(opts, id_): - ''' + """ Returns if the passed id is valid - ''' + """ try: - if any(x in id_ for x in ('/', '\\', str('\0'))): + if any(x in id_ for x in ("/", "\\", str("\0"))): return False - return bool(clean_path(opts['pki_dir'], id_)) + return bool(clean_path(opts["pki_dir"], id_)) except (AttributeError, KeyError, TypeError, UnicodeDecodeError): return False def safe_py_code(code): - ''' + """ Check a string to see if it has any potentially unsafe routines which could be executed via python, this routine is used to improve the safety of modules suct as virtualenv - ''' - bads = ( - 'import', - ';', - 'subprocess', - 'eval', - 'open', - 'file', - 'exec', - 'input') + """ + bads = ("import", ";", "subprocess", "eval", "open", "file", "exec", "input") for bad in bads: if code.count(bad): return False @@ -549,25 +549,22 @@ def safe_py_code(code): def verify_log(opts): - ''' + """ If an insecre logging configuration is found, show a warning - ''' - level = LOG_LEVELS.get(str(opts.get('log_level')).lower(), logging.NOTSET) + """ + level = LOG_LEVELS.get(str(opts.get("log_level")).lower(), logging.NOTSET) if level < logging.INFO: - log.warning('Insecure logging configuration detected! Sensitive data may be logged.') + log.warning( + "Insecure logging configuration detected! Sensitive data may be logged." + ) -def win_verify_env( - path, - dirs, - permissive=False, - pki_dir='', - skip_extra=False): - ''' +def win_verify_env(path, dirs, permissive=False, pki_dir="", skip_extra=False): + """ Verify that the named directories are in place and that the environment can shake the salt - ''' + """ import salt.utils.win_functions import salt.utils.win_dacl import salt.utils.path @@ -579,11 +576,11 @@ def win_verify_env( # be unsafe. In some instances the test suite uses # `C:\Windows\Temp\salt-tests-tmpdir\rootdir` as the file_roots. So, we need # to consider anything in `C:\Windows\Temp` to be safe - system_root = os.environ.get('SystemRoot', r'C:\Windows') - allow_path = '\\'.join([system_root, 'TEMP']) + system_root = os.environ.get("SystemRoot", r"C:\Windows") + allow_path = "\\".join([system_root, "TEMP"]) if not salt.utils.path.safe_path(path=path, allow_path=allow_path): raise CommandExecutionError( - '`file_roots` set to a possibly unsafe location: {0}'.format(path) + "`file_roots` set to a possibly unsafe location: {0}".format(path) ) # Create the root path directory if missing @@ -596,7 +593,7 @@ def win_verify_env( try: # Make the Administrators group owner # Use the SID to be locale agnostic - salt.utils.win_dacl.set_owner(path, 'S-1-5-32-544') + salt.utils.win_dacl.set_owner(path, "S-1-5-32-544") except CommandExecutionError: msg = 'Unable to securely set the owner of "{0}".'.format(path) @@ -612,21 +609,26 @@ def win_verify_env( # Add aces to the dacl, use the GUID (locale non-specific) # Administrators Group - dacl.add_ace('S-1-5-32-544', 'grant', 'full_control', - 'this_folder_subfolders_files') + dacl.add_ace( + "S-1-5-32-544", + "grant", + "full_control", + "this_folder_subfolders_files", + ) # System - dacl.add_ace('S-1-5-18', 'grant', 'full_control', - 'this_folder_subfolders_files') + dacl.add_ace( + "S-1-5-18", "grant", "full_control", "this_folder_subfolders_files" + ) # Owner - dacl.add_ace('S-1-3-4', 'grant', 'full_control', - 'this_folder_subfolders_files') + dacl.add_ace( + "S-1-3-4", "grant", "full_control", "this_folder_subfolders_files" + ) # Save the dacl to the object dacl.save(path, True) except CommandExecutionError: - msg = 'Unable to securely set the permissions of ' \ - '"{0}".'.format(path) + msg = "Unable to securely set the permissions of " '"{0}".'.format(path) if is_console_configured(): log.critical(msg) else: @@ -648,7 +650,7 @@ def win_verify_env( if dir_ == pki_dir: try: # Make Administrators group the owner - salt.utils.win_dacl.set_owner(path, 'S-1-5-32-544') + salt.utils.win_dacl.set_owner(path, "S-1-5-32-544") # Give Admins, System and Owner permissions # Get a clean dacl by not passing an obj_name @@ -656,14 +658,20 @@ def win_verify_env( # Add aces to the dacl, use the GUID (locale non-specific) # Administrators Group - dacl.add_ace('S-1-5-32-544', 'grant', 'full_control', - 'this_folder_subfolders_files') + dacl.add_ace( + "S-1-5-32-544", + "grant", + "full_control", + "this_folder_subfolders_files", + ) # System - dacl.add_ace('S-1-5-18', 'grant', 'full_control', - 'this_folder_subfolders_files') + dacl.add_ace( + "S-1-5-18", "grant", "full_control", "this_folder_subfolders_files" + ) # Owner - dacl.add_ace('S-1-3-4', 'grant', 'full_control', - 'this_folder_subfolders_files') + dacl.add_ace( + "S-1-3-4", "grant", "full_control", "this_folder_subfolders_files" + ) # Save the dacl to the object dacl.save(dir_, True) diff --git a/salt/utils/versions.py b/salt/utils/versions.py index 1a7fd1ee77f..8335dac91d9 100644 --- a/salt/utils/versions.py +++ b/salt/utils/versions.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. @@ -9,21 +9,22 @@ Version parsing based on distutils.version which works under python 3 because on python 3 you can no longer compare strings against integers. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import contextlib +import datetime +import inspect import logging import numbers import sys import warnings -import datetime -import inspect -import contextlib + # pylint: disable=blacklisted-module,no-name-in-module -from distutils.version import StrictVersion as _StrictVersion from distutils.version import LooseVersion as _LooseVersion -# pylint: enable=blacklisted-module,no-name-in-module +from distutils.version import StrictVersion as _StrictVersion # Import Salt libs import salt.version @@ -31,6 +32,9 @@ import salt.version # Import 3rd-party libs from salt.ext import six +# pylint: enable=blacklisted-module,no-name-in-module + + log = logging.getLogger(__name__) @@ -45,16 +49,18 @@ class StrictVersion(_StrictVersion): class LooseVersion(_LooseVersion): - def parse(self, vstring): _LooseVersion.parse(self, vstring) if six.PY3: # Convert every part of the version to string in order to be able to compare self._str_version = [ - six.text_type(vp).zfill(8) if isinstance(vp, int) else vp for vp in self.version] + six.text_type(vp).zfill(8) if isinstance(vp, int) else vp + for vp in self.version + ] if six.PY3: + def _cmp(self, other): if isinstance(other, six.string_types): other = LooseVersion(other) @@ -79,13 +85,11 @@ class LooseVersion(_LooseVersion): def _format_warning(message, category, filename, lineno, line=None): - ''' + """ Replacement for warnings.formatwarning that disables the echoing of the 'line' parameter. - ''' - return '{}:{}: {}: {}\n'.format( - filename, lineno, category.__name__, message - ) + """ + return "{}:{}: {}: {}\n".format(filename, lineno, category.__name__, message) @contextlib.contextmanager @@ -100,13 +104,15 @@ def _patched_format_warning(): yield -def warn_until(version, - message, - category=DeprecationWarning, - stacklevel=None, - _version_info_=None, - _dont_call_warnings=False): - ''' +def warn_until( + version, + message, + category=DeprecationWarning, + stacklevel=None, + _version_info_=None, + _dont_call_warnings=False, +): + """ Helper function to raise a warning, by default, a ``DeprecationWarning``, until the provided ``version``, after which, a ``RuntimeError`` will be raised to remind the developers to remove the warning because the @@ -127,13 +133,13 @@ def warn_until(version, functionality until the actual error is to be issued. When we're only after the salt version checks to raise a ``RuntimeError``. - ''' - if not isinstance(version, (tuple, - six.string_types, - salt.version.SaltStackVersion)): + """ + if not isinstance( + version, (tuple, six.string_types, salt.version.SaltStackVersion) + ): raise RuntimeError( - 'The \'version\' argument should be passed as a tuple, string or ' - 'an instance of \'salt.version.SaltStackVersion\'.' + "The 'version' argument should be passed as a tuple, string or " + "an instance of 'salt.version.SaltStackVersion'." ) elif isinstance(version, tuple): version = salt.version.SaltStackVersion(*version) @@ -152,14 +158,14 @@ def warn_until(version, if _version_ >= version: caller = inspect.getframeinfo(sys._getframe(stacklevel - 1)) raise RuntimeError( - 'The warning triggered on filename \'{filename}\', line number ' - '{lineno}, is supposed to be shown until version ' - '{until_version} is released. Current version is now ' - '{salt_version}. Please remove the warning.'.format( + "The warning triggered on filename '{filename}', line number " + "{lineno}, is supposed to be shown until version " + "{until_version} is released. Current version is now " + "{salt_version}. Please remove the warning.".format( filename=caller.filename, lineno=caller.lineno, until_version=version.formatted_version, - salt_version=_version_.formatted_version + salt_version=_version_.formatted_version, ), ) @@ -168,17 +174,19 @@ def warn_until(version, warnings.warn( message.format(version=version.formatted_version), category, - stacklevel=stacklevel + stacklevel=stacklevel, ) -def warn_until_date(date, - message, - category=DeprecationWarning, - stacklevel=None, - _current_date=None, - _dont_call_warnings=False): - ''' +def warn_until_date( + date, + message, + category=DeprecationWarning, + stacklevel=None, + _current_date=None, + _dont_call_warnings=False, +): + """ Helper function to raise a warning, by default, a ``DeprecationWarning``, until the provided ``date``, after which, a ``RuntimeError`` will be raised to remind the developers to remove the warning because the @@ -194,13 +202,13 @@ def warn_until_date(date, functionality until the actual error is to be issued. When we're only after the date checks to raise a ``RuntimeError``. - ''' - _strptime_fmt = '%Y%m%d' + """ + _strptime_fmt = "%Y%m%d" if not isinstance(date, (six.string_types, datetime.date, datetime.datetime)): raise RuntimeError( - 'The \'date\' argument should be passed as a \'datetime.date()\' or ' - '\'datetime.datetime()\' objects or as string parserable by ' - '\'datetime.datetime.strptime()\' with the following format \'{}\'.'.format( + "The 'date' argument should be passed as a 'datetime.date()' or " + "'datetime.datetime()' objects or as string parserable by " + "'datetime.datetime.strptime()' with the following format '{}'.".format( _strptime_fmt ) ) @@ -219,14 +227,11 @@ def warn_until_date(date, if today >= date: caller = inspect.getframeinfo(sys._getframe(stacklevel - 1)) raise RuntimeError( - '{message} This warning(now exception) triggered on ' - 'filename \'{filename}\', line number {lineno}, is ' - 'supposed to be shown until {date}. Today is {today}. ' - 'Please remove the warning.'.format( - message=message.format( - date=date.isoformat(), - today=today.isoformat() - ), + "{message} This warning(now exception) triggered on " + "filename '{filename}', line number {lineno}, is " + "supposed to be shown until {date}. Today is {today}. " + "Please remove the warning.".format( + message=message.format(date=date.isoformat(), today=today.isoformat()), filename=caller.filename, lineno=caller.lineno, date=date.isoformat(), @@ -237,22 +242,21 @@ def warn_until_date(date, if _dont_call_warnings is False: with _patched_format_warning(): warnings.warn( - message.format( - date=date.isoformat(), - today=today.isoformat() - ), + message.format(date=date.isoformat(), today=today.isoformat()), category, - stacklevel=stacklevel + stacklevel=stacklevel, ) -def kwargs_warn_until(kwargs, - version, - category=DeprecationWarning, - stacklevel=None, - _version_info_=None, - _dont_call_warnings=False): - ''' +def kwargs_warn_until( + kwargs, + version, + category=DeprecationWarning, + stacklevel=None, + _version_info_=None, + _dont_call_warnings=False, +): + """ Helper function to raise a warning (by default, a ``DeprecationWarning``) when unhandled keyword arguments are passed to function, until the provided ``version_info``, after which, a ``RuntimeError`` will be raised @@ -279,13 +283,13 @@ def kwargs_warn_until(kwargs, functionality until the actual error is to be issued. When we're only after the salt version checks to raise a ``RuntimeError``. - ''' - if not isinstance(version, (tuple, - six.string_types, - salt.version.SaltStackVersion)): + """ + if not isinstance( + version, (tuple, six.string_types, salt.version.SaltStackVersion) + ): raise RuntimeError( - 'The \'version\' argument should be passed as a tuple, string or ' - 'an instance of \'salt.version.SaltStackVersion\'.' + "The 'version' argument should be passed as a tuple, string or " + "an instance of 'salt.version.SaltStackVersion'." ) elif isinstance(version, tuple): version = salt.version.SaltStackVersion(*version) @@ -303,29 +307,31 @@ def kwargs_warn_until(kwargs, _version_ = salt.version.SaltStackVersion(*_version_info_) if kwargs or _version_.info >= version.info: - arg_names = ', '.join('\'{0}\''.format(key) for key in kwargs) + arg_names = ", ".join("'{0}'".format(key) for key in kwargs) warn_until( version, - message='The following parameter(s) have been deprecated and ' - 'will be removed in \'{0}\': {1}.'.format(version.string, - arg_names), + message="The following parameter(s) have been deprecated and " + "will be removed in '{0}': {1}.".format(version.string, arg_names), category=category, stacklevel=stacklevel, _version_info_=_version_.info, - _dont_call_warnings=_dont_call_warnings + _dont_call_warnings=_dont_call_warnings, ) def version_cmp(pkg1, pkg2, ignore_epoch=False): - ''' + """ Compares two version strings using salt.utils.versions.LooseVersion. This is a fallback for providers which don't have a version comparison utility built into them. Return -1 if version1 < version2, 0 if version1 == version2, and 1 if version1 > version2. Return None if there was a problem making the comparison. - ''' - normalize = lambda x: six.text_type(x).split(':', 1)[-1] \ - if ignore_epoch else six.text_type(x) + """ + normalize = ( + lambda x: six.text_type(x).split(":", 1)[-1] + if ignore_epoch + else six.text_type(x) + ) pkg1 = normalize(pkg1) pkg2 = normalize(pkg2) @@ -342,15 +348,14 @@ def version_cmp(pkg1, pkg2, ignore_epoch=False): return None -def compare(ver1='', oper='==', ver2='', cmp_func=None, ignore_epoch=False): - ''' +def compare(ver1="", oper="==", ver2="", cmp_func=None, ignore_epoch=False): + """ Compares two version numbers. Accepts a custom function to perform the cmp-style version comparison, otherwise uses version_cmp(). - ''' - cmp_map = {'<': (-1,), '<=': (-1, 0), '==': (0,), - '>=': (0, 1), '>': (1,)} - if oper not in ('!=',) and oper not in cmp_map: - log.error('Invalid operator \'%s\' for version comparison', oper) + """ + cmp_map = {"<": (-1,), "<=": (-1, 0), "==": (0,), ">=": (0, 1), ">": (1,)} + if oper not in ("!=",) and oper not in cmp_map: + log.error("Invalid operator '%s' for version comparison", oper) return False if cmp_func is None: @@ -362,12 +367,11 @@ def compare(ver1='', oper='==', ver2='', cmp_func=None, ignore_epoch=False): # Check if integer/long if not isinstance(cmp_result, numbers.Integral): - log.error('The version comparison function did not return an ' - 'integer/long.') + log.error("The version comparison function did not return an " "integer/long.") return False - if oper == '!=': - return cmp_result not in cmp_map['=='] + if oper == "!=": + return cmp_result not in cmp_map["=="] else: # Gracefully handle cmp_result not in (-1, 0, 1). if cmp_result < -1: @@ -378,12 +382,10 @@ def compare(ver1='', oper='==', ver2='', cmp_func=None, ignore_epoch=False): return cmp_result in cmp_map[oper] -def check_boto_reqs(boto_ver=None, - boto3_ver=None, - botocore_ver=None, - check_boto=True, - check_boto3=True): - ''' +def check_boto_reqs( + boto_ver=None, boto3_ver=None, botocore_ver=None, check_boto=True, check_boto3=True +): + """ Checks for the version of various required boto libs in one central location. Most boto states and modules rely on a single version of the boto, boto3, or botocore libs. However, some require newer versions of any of these dependencies. This function allows @@ -409,26 +411,28 @@ def check_boto_reqs(boto_ver=None, Boolean defining whether or not to check for boto3 (and therefore botocore) deps. This defaults to ``True`` as most boto modules/states rely on boto3/botocore, but some do not. - ''' + """ if check_boto is True: try: # Late import so we can only load these for this function import boto + has_boto = True except ImportError: has_boto = False if boto_ver is None: - boto_ver = '2.0.0' + boto_ver = "2.0.0" if not has_boto or version_cmp(boto.__version__, boto_ver) == -1: - return False, 'A minimum version of boto {0} is required.'.format(boto_ver) + return False, "A minimum version of boto {0} is required.".format(boto_ver) if check_boto3 is True: try: # Late import so we can only load these for this function import boto3 import botocore + has_boto3 = True except ImportError: has_boto3 = False @@ -436,13 +440,19 @@ def check_boto_reqs(boto_ver=None, # boto_s3_bucket module requires boto3 1.2.6 and botocore 1.3.23 for # idempotent ACL operations via the fix in https://github.com/boto/boto3/issues/390 if boto3_ver is None: - boto3_ver = '1.2.6' + boto3_ver = "1.2.6" if botocore_ver is None: - botocore_ver = '1.3.23' + botocore_ver = "1.3.23" if not has_boto3 or version_cmp(boto3.__version__, boto3_ver) == -1: - return False, 'A minimum version of boto3 {0} is required.'.format(boto3_ver) + return ( + False, + "A minimum version of boto3 {0} is required.".format(boto3_ver), + ) elif version_cmp(botocore.__version__, botocore_ver) == -1: - return False, 'A minimum version of botocore {0} is required'.format(botocore_ver) + return ( + False, + "A minimum version of botocore {0} is required".format(botocore_ver), + ) return True diff --git a/salt/utils/virt.py b/salt/utils/virt.py index e52dc340843..85ee6dbe2d0 100644 --- a/salt/utils/virt.py +++ b/salt/utils/virt.py @@ -1,22 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" This module contains routines shared by the virt system. -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import hashlib +import logging + # Import python libs import os import re import time -import logging -import hashlib - -# pylint: disable=E0611 -from salt.ext.six.moves.urllib.parse import urlparse -from salt.ext.six.moves.urllib import request # Import salt libs import salt.utils.files +from salt.ext.six.moves.urllib import request + +# pylint: disable=E0611 +from salt.ext.six.moves.urllib.parse import urlparse log = logging.getLogger(__name__) @@ -31,11 +32,11 @@ def download_remote(url, dir): try: rand = hashlib.md5(os.urandom(32)).hexdigest() - remote_filename = urlparse(url).path.split('/')[-1] - full_directory = \ - os.path.join(dir, "{}-{}".format(rand, remote_filename)) - with salt.utils.files.fopen(full_directory, 'wb') as file,\ - request.urlopen(url) as response: + remote_filename = urlparse(url).path.split("/")[-1] + full_directory = os.path.join(dir, "{}-{}".format(rand, remote_filename)) + with salt.utils.files.fopen(full_directory, "wb") as file, request.urlopen( + url + ) as response: file.write(response.rease()) return full_directory @@ -51,7 +52,7 @@ def check_remote(cmdline_path): :param cmdline_path: The path to the initrd image or the kernel """ - regex = re.compile('^(ht|f)tps?\\b') + regex = re.compile("^(ht|f)tps?\\b") if regex.match(urlparse(cmdline_path).scheme): return True @@ -60,33 +61,36 @@ def check_remote(cmdline_path): class VirtKey(object): - ''' + """ Used to manage key signing requests. - ''' + """ + def __init__(self, hyper, id_, opts): self.opts = opts self.hyper = hyper self.id = id_ - path = os.path.join(self.opts['pki_dir'], 'virtkeys', hyper) + path = os.path.join(self.opts["pki_dir"], "virtkeys", hyper) if not os.path.isdir(path): os.makedirs(path) self.path = os.path.join(path, id_) def accept(self, pub): - ''' + """ Accept the provided key - ''' + """ try: - with salt.utils.files.fopen(self.path, 'r') as fp_: + with salt.utils.files.fopen(self.path, "r") as fp_: expiry = int(fp_.read()) except (OSError, IOError): log.error( - 'Request to sign key for minion \'%s\' on hyper \'%s\' ' - 'denied: no authorization', self.id, self.hyper + "Request to sign key for minion '%s' on hyper '%s' " + "denied: no authorization", + self.id, + self.hyper, ) return False except ValueError: - log.error('Invalid expiry data in %s', self.path) + log.error("Invalid expiry data in %s", self.path) return False # Limit acceptance window to 10 minutes @@ -94,30 +98,32 @@ class VirtKey(object): if (time.time() - expiry) > 600: log.warning( 'Request to sign key for minion "%s" on hyper "%s" denied: ' - 'authorization expired', self.id, self.hyper + "authorization expired", + self.id, + self.hyper, ) return False - pubfn = os.path.join(self.opts['pki_dir'], - 'minions', - self.id) - with salt.utils.files.fopen(pubfn, 'w+') as fp_: + pubfn = os.path.join(self.opts["pki_dir"], "minions", self.id) + with salt.utils.files.fopen(pubfn, "w+") as fp_: fp_.write(pub) self.void() return True def authorize(self): - ''' + """ Prepare the master to expect a signing request - ''' - with salt.utils.files.fopen(self.path, 'w+') as fp_: - fp_.write(str(int(time.time()))) # future lint: disable=blacklisted-function + """ + with salt.utils.files.fopen(self.path, "w+") as fp_: + fp_.write( + str(int(time.time())) + ) # future lint: disable=blacklisted-function return True def void(self): - ''' + """ Invalidate any existing authorization - ''' + """ try: os.unlink(self.path) return True diff --git a/salt/utils/virtualbox.py b/salt/utils/virtualbox.py index e6b111417dd..da6f7a65b4b 100644 --- a/salt/utils/virtualbox.py +++ b/salt/utils/virtualbox.py @@ -1,30 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Utilities to help make requests to virtualbox The virtualbox SDK reference can be found at http://download.virtualbox.org/virtualbox/SDKRef.pdf This code assumes vboxapi.py from VirtualBox distribution being in PYTHONPATH, or installed system-wide -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import re import time +import salt.ext.six as six + # Import salt libs import salt.utils.compat import salt.utils.data -from salt.utils.timeout import wait_for -import salt.ext.six as six +# Import 3rd-party libs +from salt.ext.six.moves import range +from salt.utils.timeout import wait_for log = logging.getLogger(__name__) -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import range # Import virtualbox libs HAS_LIBS = False @@ -35,107 +36,170 @@ try: except ImportError: VirtualBoxManager = None - log.trace('Couldn\'t import VirtualBox API') + log.trace("Couldn't import VirtualBox API") _virtualboxManager = None -''' +""" Attributes we expect to have when converting an XPCOM object to a dict -''' +""" XPCOM_ATTRIBUTES = { - 'IMachine': [ - 'id', - 'name', - 'accessible', - 'description', - 'groups', - 'memorySize', - 'OSTypeId', - 'state', + "IMachine": [ + "id", + "name", + "accessible", + "description", + "groups", + "memorySize", + "OSTypeId", + "state", + ], + "INetworkAdapter": [ + "adapterType", + "slot", + "enabled", + "MACAddress", + "bridgedInterface", + "hostOnlyInterface", + "internalNetwork", + "NATNetwork", + "genericDriver", + "cableConnected", + "lineSpeed", + "lineSpeed", ], - 'INetworkAdapter': [ - 'adapterType', - 'slot', - 'enabled', - 'MACAddress', - 'bridgedInterface', - 'hostOnlyInterface', - 'internalNetwork', - 'NATNetwork', - 'genericDriver', - 'cableConnected', - 'lineSpeed', - 'lineSpeed', - ] } -UNKNOWN_MACHINE_STATE = ('Unknown', 'This state is unknown to us. Might be new?') +UNKNOWN_MACHINE_STATE = ("Unknown", "This state is unknown to us. Might be new?") MACHINE_STATE_LIST = [ - ('Null', 'Null value (never used by the API)'), - ('PoweredOff', 'The machine is not running and has no saved execution state; ' - 'it has either never been started or been shut down successfully.'), - ('Saved', 'The machine is not currently running, but the execution state of the machine has been ' - 'saved to an external file when it was running, from where it can be resumed.'), - ('Teleported', 'The machine was teleported to a different host (or process) and then powered off. ' - 'Take care when powering it on again may corrupt resources it shares with the teleportation ' - 'target (e.g. disk and network).'), - ('Aborted', 'The process running the machine has terminated abnormally. This may indicate a ' - 'crash of the VM process in host execution context, or the VM process has been terminated ' - 'externally.'), - ('Running', 'The machine is currently being executed.'), - ('Paused', 'Execution of the machine has been paused.'), - ('Stuck', 'Execution of the machine has reached the \'Guru Meditation\' condition. This indicates a ' - 'severe error in the hypervisor itself.'), - ('Teleporting', 'The machine is about to be teleported to a different host or process. It is possible ' - 'to pause a machine in this state, but it will go to the TeleportingPausedVM state and it ' - 'will not be possible to resume it again unless the teleportation fails.'), - ('LiveSnapshotting', 'A live snapshot is being taken. The machine is running normally, but some ' - 'of the runtime configuration options are inaccessible. ' - 'Also, if paused while in this state it will transition to OnlineSnapshotting ' - 'and it will not be resume the execution until the snapshot operation has completed.'), - ('Starting', 'Machine is being started after powering it on from a zero execution state.'), - ('Stopping', 'Machine is being normally stopped powering it off, or after the guest OS has initiated ' - 'a shutdown sequence.'), - ('Saving', 'Machine is saving its execution state to a file.'), - ('Restoring', 'Execution state of the machine is being restored from a file after powering it on from ' - 'the saved execution state.'), - ('TeleportingPausedVM', 'The machine is being teleported to another host or process, but it is not ' - 'running. This is the paused variant of the Teleporting state.'), - ('TeleportingIn', 'Teleporting the machine state in from another host or process.'), - ('FaultTolerantSyncing', 'The machine is being synced with a fault tolerant VM running else-where.'), - ('DeletingSnapshotOnline', 'Like DeletingSnapshot , but the merging of media is ongoing in the ' - 'background while the machine is running.'), - ('DeletingSnapshotPaused', 'Like DeletingSnapshotOnline , but the machine was paused when ' - 'the merging of differencing media was started.'), - ('OnlineSnapshotting', 'Like LiveSnapshotting , but the machine was paused when the merging ' - 'of differencing media was started.'), - ('RestoringSnapshot', 'A machine snapshot is being restored; this typically does not take long.'), - ('DeletingSnapshot', 'A machine snapshot is being deleted; this can take a long time since this may ' - 'require merging differencing media. This value indicates that the machine is not running ' - 'while the snapshot is being deleted.'), - ('SettingUp', 'Lengthy setup operation is in progress.'), - ('Snapshotting', 'Taking an (offline) snapshot.'), - ('FirstOnline', 'Pseudo-state: first online state (for use in relational expressions).'), - ('LastOnline', 'Pseudo-state: last online state (for use in relational expressions).'), - ('FirstTransient', 'Pseudo-state: first transient state (for use in relational expressions).'), - ('LastTransient', 'Pseudo-state: last transient state (for use in relational expressions).'), + ("Null", "Null value (never used by the API)"), + ( + "PoweredOff", + "The machine is not running and has no saved execution state; " + "it has either never been started or been shut down successfully.", + ), + ( + "Saved", + "The machine is not currently running, but the execution state of the machine has been " + "saved to an external file when it was running, from where it can be resumed.", + ), + ( + "Teleported", + "The machine was teleported to a different host (or process) and then powered off. " + "Take care when powering it on again may corrupt resources it shares with the teleportation " + "target (e.g. disk and network).", + ), + ( + "Aborted", + "The process running the machine has terminated abnormally. This may indicate a " + "crash of the VM process in host execution context, or the VM process has been terminated " + "externally.", + ), + ("Running", "The machine is currently being executed."), + ("Paused", "Execution of the machine has been paused."), + ( + "Stuck", + "Execution of the machine has reached the 'Guru Meditation' condition. This indicates a " + "severe error in the hypervisor itself.", + ), + ( + "Teleporting", + "The machine is about to be teleported to a different host or process. It is possible " + "to pause a machine in this state, but it will go to the TeleportingPausedVM state and it " + "will not be possible to resume it again unless the teleportation fails.", + ), + ( + "LiveSnapshotting", + "A live snapshot is being taken. The machine is running normally, but some " + "of the runtime configuration options are inaccessible. " + "Also, if paused while in this state it will transition to OnlineSnapshotting " + "and it will not be resume the execution until the snapshot operation has completed.", + ), + ( + "Starting", + "Machine is being started after powering it on from a zero execution state.", + ), + ( + "Stopping", + "Machine is being normally stopped powering it off, or after the guest OS has initiated " + "a shutdown sequence.", + ), + ("Saving", "Machine is saving its execution state to a file."), + ( + "Restoring", + "Execution state of the machine is being restored from a file after powering it on from " + "the saved execution state.", + ), + ( + "TeleportingPausedVM", + "The machine is being teleported to another host or process, but it is not " + "running. This is the paused variant of the Teleporting state.", + ), + ("TeleportingIn", "Teleporting the machine state in from another host or process."), + ( + "FaultTolerantSyncing", + "The machine is being synced with a fault tolerant VM running else-where.", + ), + ( + "DeletingSnapshotOnline", + "Like DeletingSnapshot , but the merging of media is ongoing in the " + "background while the machine is running.", + ), + ( + "DeletingSnapshotPaused", + "Like DeletingSnapshotOnline , but the machine was paused when " + "the merging of differencing media was started.", + ), + ( + "OnlineSnapshotting", + "Like LiveSnapshotting , but the machine was paused when the merging " + "of differencing media was started.", + ), + ( + "RestoringSnapshot", + "A machine snapshot is being restored; this typically does not take long.", + ), + ( + "DeletingSnapshot", + "A machine snapshot is being deleted; this can take a long time since this may " + "require merging differencing media. This value indicates that the machine is not running " + "while the snapshot is being deleted.", + ), + ("SettingUp", "Lengthy setup operation is in progress."), + ("Snapshotting", "Taking an (offline) snapshot."), + ( + "FirstOnline", + "Pseudo-state: first online state (for use in relational expressions).", + ), + ( + "LastOnline", + "Pseudo-state: last online state (for use in relational expressions).", + ), + ( + "FirstTransient", + "Pseudo-state: first transient state (for use in relational expressions).", + ), + ( + "LastTransient", + "Pseudo-state: last transient state (for use in relational expressions).", + ), ] MACHINE_STATES = dict(MACHINE_STATE_LIST) -''' +""" Dict of states { <number>: ( <name>, <description> ) } -''' +""" MACHINE_STATES_ENUM = dict(enumerate(MACHINE_STATE_LIST)) def vb_get_manager(): - ''' + """ Creates a 'singleton' manager to communicate with a local virtualbox hypervisor. @return: @rtype: VirtualBoxManager - ''' + """ global _virtualboxManager if _virtualboxManager is None and HAS_LIBS: salt.utils.compat.reload(vboxapi) @@ -145,11 +209,11 @@ def vb_get_manager(): def vb_get_box(): - ''' + """ Needed for certain operations in the SDK e.g creating sessions @return: @rtype: IVirtualBox - ''' + """ vb_get_manager() try: @@ -162,24 +226,24 @@ def vb_get_box(): def vb_get_max_network_slots(): - ''' + """ Max number of slots any machine can have @return: @rtype: number - ''' + """ sysprops = vb_get_box().systemProperties totals = [ sysprops.getMaxNetworkAdapters(adapter_type) for adapter_type in [ 1, # PIIX3 A PIIX3 (PCI IDE ISA Xcelerator) chipset. - 2 # ICH9 A ICH9 (I/O Controller Hub) chipset - ] + 2, # ICH9 A ICH9 (I/O Controller Hub) chipset ] + ] return sum(totals) def vb_get_network_adapters(machine_name=None, machine=None): - ''' + """ A valid machine_name or a machine is needed to make this work! @param machine_name: @@ -188,7 +252,7 @@ def vb_get_network_adapters(machine_name=None, machine=None): @type machine: IMachine @return: INetorkAdapter's converted to dicts @rtype: [dict] - ''' + """ if machine_name: machine = vb_get_box().findMachine(machine_name) @@ -198,9 +262,9 @@ def vb_get_network_adapters(machine_name=None, machine=None): try: inetwork_adapter = machine.getNetworkAdapter(i) network_adapter = vb_xpcom_to_attribute_dict( - inetwork_adapter, 'INetworkAdapter' + inetwork_adapter, "INetworkAdapter" ) - network_adapter['properties'] = inetwork_adapter.getProperties('') + network_adapter["properties"] = inetwork_adapter.getProperties("") network_adapters.append(network_adapter) except Exception: # pylint: disable=broad-except pass @@ -208,8 +272,10 @@ def vb_get_network_adapters(machine_name=None, machine=None): return network_adapters -def vb_wait_for_network_address(timeout, step=None, machine_name=None, machine=None, wait_for_pattern=None): - ''' +def vb_wait_for_network_address( + timeout, step=None, machine_name=None, machine=None, wait_for_pattern=None +): + """ Wait until a machine has a network address to return or quit after the timeout @param timeout: in seconds @@ -225,30 +291,38 @@ def vb_wait_for_network_address(timeout, step=None, machine_name=None, machine=N @type machine: str @return: @rtype: list - ''' + """ kwargs = { - 'machine_name': machine_name, - 'machine': machine, - 'wait_for_pattern': wait_for_pattern + "machine_name": machine_name, + "machine": machine, + "wait_for_pattern": wait_for_pattern, } - return wait_for(vb_get_network_addresses, timeout=timeout, step=step, default=[], func_kwargs=kwargs) + return wait_for( + vb_get_network_addresses, + timeout=timeout, + step=step, + default=[], + func_kwargs=kwargs, + ) -def _check_session_state(xp_session, expected_state='Unlocked'): - ''' +def _check_session_state(xp_session, expected_state="Unlocked"): + """ @param xp_session: @type xp_session: ISession from the Virtualbox API @param expected_state: The constant descriptor according to the docs @type expected_state: str @return: @rtype: bool - ''' - state_value = getattr(_virtualboxManager.constants, 'SessionState_' + expected_state) + """ + state_value = getattr( + _virtualboxManager.constants, "SessionState_" + expected_state + ) return xp_session.state == state_value -def vb_wait_for_session_state(xp_session, state='Unlocked', timeout=10, step=None): - ''' +def vb_wait_for_session_state(xp_session, state="Unlocked", timeout=10, step=None): + """ Waits until a session state has been reached, checking at regular intervals. @param xp_session: @@ -261,13 +335,15 @@ def vb_wait_for_session_state(xp_session, state='Unlocked', timeout=10, step=Non @type step: int | float @return: Did we reach the state? @rtype: bool - ''' + """ args = (xp_session, state) - wait_for(_check_session_state, timeout=timeout, step=step, default=False, func_args=args) + wait_for( + _check_session_state, timeout=timeout, step=step, default=False, func_args=args + ) def vb_get_network_addresses(machine_name=None, machine=None, wait_for_pattern=None): - ''' + """ TODO distinguish between private and public addresses A valid machine_name or a machine is needed to make this work! @@ -286,7 +362,7 @@ def vb_get_network_addresses(machine_name=None, machine=None, wait_for_pattern=N @type machine: IMachine @return: All the IPv4 addresses we could get @rtype: str[] - ''' + """ if machine_name: machine = vb_get_box().findMachine(machine_name) @@ -296,15 +372,15 @@ def vb_get_network_addresses(machine_name=None, machine=None, wait_for_pattern=N log.debug("got power on:") - #wait on an arbitrary named property - #for instance use a dhcp client script to set a property via VBoxControl guestproperty set dhcp_done 1 + # wait on an arbitrary named property + # for instance use a dhcp client script to set a property via VBoxControl guestproperty set dhcp_done 1 if wait_for_pattern and not machine.getGuestPropertyValue(wait_for_pattern): log.debug("waiting for pattern:%s:", wait_for_pattern) return None - _total_slots = machine.getGuestPropertyValue('/VirtualBox/GuestInfo/Net/Count') + _total_slots = machine.getGuestPropertyValue("/VirtualBox/GuestInfo/Net/Count") - #upon dhcp the net count drops to 0 and it takes some seconds for it to be set again + # upon dhcp the net count drops to 0 and it takes some seconds for it to be set again if not _total_slots: log.debug("waiting for net count:%s:", wait_for_pattern) return None @@ -313,7 +389,9 @@ def vb_get_network_addresses(machine_name=None, machine=None, wait_for_pattern=N total_slots = int(_total_slots) for i in range(total_slots): try: - address = machine.getGuestPropertyValue('/VirtualBox/GuestInfo/Net/{0}/V4/IP'.format(i)) + address = machine.getGuestPropertyValue( + "/VirtualBox/GuestInfo/Net/{0}/V4/IP".format(i) + ) if address: ip_addresses.append(address) except Exception as e: # pylint: disable=broad-except @@ -327,23 +405,23 @@ def vb_get_network_addresses(machine_name=None, machine=None, wait_for_pattern=N def vb_list_machines(**kwargs): - ''' + """ Which machines does the hypervisor have @param kwargs: Passed to vb_xpcom_to_attribute_dict to filter the attributes @type kwargs: dict @return: Untreated dicts of the machines known to the hypervisor @rtype: [{}] - ''' + """ manager = vb_get_manager() - machines = manager.getArray(vb_get_box(), 'machines') + machines = manager.getArray(vb_get_box(), "machines") return [ - vb_xpcom_to_attribute_dict(machine, 'IMachine', **kwargs) + vb_xpcom_to_attribute_dict(machine, "IMachine", **kwargs) for machine in machines - ] + ] def vb_create_machine(name=None): - ''' + """ Creates a machine on the virtualbox hypervisor TODO pass more params to customize machine creation @@ -351,31 +429,21 @@ def vb_create_machine(name=None): @type name: str @return: Representation of the created machine @rtype: dict - ''' + """ vbox = vb_get_box() - log.info('Create virtualbox machine %s ', name) + log.info("Create virtualbox machine %s ", name) groups = None - os_type_id = 'Other' + os_type_id = "Other" new_machine = vbox.createMachine( - None, # Settings file - name, - groups, - os_type_id, - None # flags + None, name, groups, os_type_id, None # Settings file # flags ) vbox.registerMachine(new_machine) - log.info('Finished creating %s', name) - return vb_xpcom_to_attribute_dict(new_machine, 'IMachine') + log.info("Finished creating %s", name) + return vb_xpcom_to_attribute_dict(new_machine, "IMachine") -def vb_clone_vm( - name=None, - clone_from=None, - clone_mode=0, - timeout=10000, - **kwargs -): - ''' +def vb_clone_vm(name=None, clone_from=None, clone_mode=0, timeout=10000, **kwargs): + """ Tells virtualbox to create a VM by cloning from an existing one @param name: Name for the new VM @@ -385,38 +453,32 @@ def vb_clone_vm( @param timeout: maximum time in milliseconds to wait or -1 to wait indefinitely @type timeout: int @return dict of resulting VM - ''' + """ vbox = vb_get_box() - log.info('Clone virtualbox machine %s from %s', name, clone_from) + log.info("Clone virtualbox machine %s from %s", name, clone_from) source_machine = vbox.findMachine(clone_from) groups = None - os_type_id = 'Other' + os_type_id = "Other" new_machine = vbox.createMachine( - None, # Settings file - name, - groups, - os_type_id, - None # flags + None, name, groups, os_type_id, None # Settings file # flags ) progress = source_machine.cloneTo( - new_machine, - clone_mode, # CloneMode - None # CloneOptions : None = Full? + new_machine, clone_mode, None # CloneMode # CloneOptions : None = Full? ) progress.waitForCompletion(timeout) - log.info('Finished cloning %s from %s', name, clone_from) + log.info("Finished cloning %s from %s", name, clone_from) vbox.registerMachine(new_machine) - return vb_xpcom_to_attribute_dict(new_machine, 'IMachine') + return vb_xpcom_to_attribute_dict(new_machine, "IMachine") def _start_machine(machine, session): - ''' + """ Helper to try and start machines @param machine: @@ -425,16 +487,16 @@ def _start_machine(machine, session): @type session: ISession @return: @rtype: IProgress or None - ''' + """ try: - return machine.launchVMProcess(session, '', '') + return machine.launchVMProcess(session, "", "") except Exception as e: # pylint: disable=broad-except log.debug(e.message, exc_info=True) return None def vb_start_vm(name=None, timeout=10000, **kwargs): - ''' + """ Tells Virtualbox to start up a VM. Blocking function! @@ -443,7 +505,7 @@ def vb_start_vm(name=None, timeout=10000, **kwargs): @param timeout: Maximum time in milliseconds to wait or -1 to wait indefinitely @type timeout: int @return untreated dict of started VM - ''' + """ # Time tracking start_time = time.time() timeout_in_seconds = timeout / 1000 @@ -453,13 +515,15 @@ def vb_start_vm(name=None, timeout=10000, **kwargs): machine = vbox.findMachine(name) session = _virtualboxManager.getSessionObject(vbox) - log.info('Starting machine %s in state %s', name, vb_machinestate_to_str(machine.state)) + log.info( + "Starting machine %s in state %s", name, vb_machinestate_to_str(machine.state) + ) try: # Keep trying to start a machine args = (machine, session) progress = wait_for(_start_machine, timeout=timeout_in_seconds, func_args=args) if not progress: - progress = machine.launchVMProcess(session, '', '') + progress = machine.launchVMProcess(session, "", "") # We already waited for stuff, don't push it time_left = max_time - time.time() @@ -470,13 +534,13 @@ def vb_start_vm(name=None, timeout=10000, **kwargs): # The session state should best be unlocked otherwise subsequent calls might cause problems time_left = max_time - time.time() vb_wait_for_session_state(session, timeout=time_left) - log.info('Started machine %s', name) + log.info("Started machine %s", name) - return vb_xpcom_to_attribute_dict(machine, 'IMachine') + return vb_xpcom_to_attribute_dict(machine, "IMachine") def vb_stop_vm(name=None, timeout=10000, **kwargs): - ''' + """ Tells Virtualbox to stop a VM. This is a blocking function! @@ -485,10 +549,10 @@ def vb_stop_vm(name=None, timeout=10000, **kwargs): @param timeout: Maximum time in milliseconds to wait or -1 to wait indefinitely @type timeout: int @return untreated dict of stopped VM - ''' + """ vbox = vb_get_box() machine = vbox.findMachine(name) - log.info('Stopping machine %s', name) + log.info("Stopping machine %s", name) session = _virtualboxManager.openMachineSession(machine) try: console = session.console @@ -497,33 +561,36 @@ def vb_stop_vm(name=None, timeout=10000, **kwargs): finally: _virtualboxManager.closeMachineSession(session) vb_wait_for_session_state(session) - log.info('Stopped machine %s is now %s', name, vb_machinestate_to_str(machine.state)) - return vb_xpcom_to_attribute_dict(machine, 'IMachine') + log.info( + "Stopped machine %s is now %s", name, vb_machinestate_to_str(machine.state) + ) + return vb_xpcom_to_attribute_dict(machine, "IMachine") def vb_destroy_machine(name=None, timeout=10000): - ''' + """ Attempts to get rid of a machine and all its files from the hypervisor @param name: @type name: str @param timeout int timeout in milliseconds - ''' + """ vbox = vb_get_box() - log.info('Destroying machine %s', name) + log.info("Destroying machine %s", name) machine = vbox.findMachine(name) files = machine.unregister(2) progress = machine.deleteConfig(files) progress.waitForCompletion(timeout) - log.info('Finished destroying machine %s', name) + log.info("Finished destroying machine %s", name) -def vb_xpcom_to_attribute_dict(xpcom, - interface_name=None, - attributes=None, - excluded_attributes=None, - extra_attributes=None - ): - ''' +def vb_xpcom_to_attribute_dict( + xpcom, + interface_name=None, + attributes=None, + excluded_attributes=None, + extra_attributes=None, +): + """ Attempts to build a dict from an XPCOM object. Attributes that don't exist in the object return an empty string. @@ -545,13 +612,18 @@ def vb_xpcom_to_attribute_dict(xpcom, @type extra_attributes: attribute_list @return: @rtype: dict - ''' + """ # Check the interface if interface_name: - m = re.search(r'XPCOM.+implementing {0}'.format(interface_name), six.text_type(xpcom)) + m = re.search( + r"XPCOM.+implementing {0}".format(interface_name), six.text_type(xpcom) + ) if not m: # TODO maybe raise error here? - log.warning('Interface %s is unknown and cannot be converted to dict', interface_name) + log.warning( + "Interface %s is unknown and cannot be converted to dict", + interface_name, + ) return dict() interface_attributes = set(attributes or XPCOM_ATTRIBUTES.get(interface_name, [])) @@ -567,14 +639,14 @@ def vb_xpcom_to_attribute_dict(xpcom, attribute_class = attribute[1] value = (attribute_name, getattr(xpcom, attribute_name, attribute_class())) else: - value = (attribute, getattr(xpcom, attribute, '')) + value = (attribute, getattr(xpcom, attribute, "")) attribute_tuples.append(value) return dict(attribute_tuples) def treat_machine_dict(machine): - ''' + """ Make machine presentable for outside world. !!!Modifies the input machine!!! @@ -583,55 +655,57 @@ def treat_machine_dict(machine): @type machine: dict @return: the modified input machine @rtype: dict - ''' - machine.update({ - 'id': machine.get('id', ''), - 'image': machine.get('image', ''), - 'size': '{0} MB'.format(machine.get('memorySize', 0)), - 'state': machine_get_machinestate_str(machine), - 'private_ips': [], - 'public_ips': [], - }) + """ + machine.update( + { + "id": machine.get("id", ""), + "image": machine.get("image", ""), + "size": "{0} MB".format(machine.get("memorySize", 0)), + "state": machine_get_machinestate_str(machine), + "private_ips": [], + "public_ips": [], + } + ) # Replaced keys - if 'memorySize' in machine: - del machine['memorySize'] + if "memorySize" in machine: + del machine["memorySize"] return machine def vb_machinestate_to_str(machinestate): - ''' + """ Put a name to the state @param machinestate: from the machine state enum from XPCOM @type machinestate: int @return: @rtype: str - ''' + """ return vb_machinestate_to_tuple(machinestate)[0] def vb_machinestate_to_description(machinestate): - ''' + """ Describe the given state @param machinestate: from the machine state enum from XPCOM @type machinestate: int | str @return: @rtype: str - ''' + """ return vb_machinestate_to_tuple(machinestate)[1] def vb_machinestate_to_tuple(machinestate): - ''' + """ @param machinestate: @type machinestate: int | str @return: @rtype: tuple(<name>, <description>) - ''' + """ if isinstance(machinestate, int): ret = MACHINE_STATES_ENUM.get(machinestate, UNKNOWN_MACHINE_STATE) elif isinstance(machinestate, six.string_types): @@ -642,21 +716,21 @@ def vb_machinestate_to_tuple(machinestate): def machine_get_machinestate_tuple(machinedict): - return vb_machinestate_to_tuple(machinedict.get('state')) + return vb_machinestate_to_tuple(machinedict.get("state")) def machine_get_machinestate_str(machinedict): - return vb_machinestate_to_str(machinedict.get('state')) + return vb_machinestate_to_str(machinedict.get("state")) def vb_machine_exists(name): - ''' + """ Checks in with the hypervisor to see if the machine with the given name is known @param name: @type name: @return: @rtype: - ''' + """ try: vbox = vb_get_box() vbox.findMachine(name) @@ -664,18 +738,18 @@ def vb_machine_exists(name): except Exception as e: # pylint: disable=broad-except if isinstance(e.message, six.string_types): message = e.message - elif hasattr(e, 'msg') and isinstance(getattr(e, 'msg'), six.string_types): - message = getattr(e, 'msg') + elif hasattr(e, "msg") and isinstance(getattr(e, "msg"), six.string_types): + message = getattr(e, "msg") else: - message = '' - if 0 > message.find('Could not find a registered machine named'): + message = "" + if 0 > message.find("Could not find a registered machine named"): log.error(message) return False def vb_get_machine(name, **kwargs): - ''' + """ Attempts to fetch a machine from Virtualbox and convert it to a dict @param name: The unique name of the machine @@ -684,7 +758,7 @@ def vb_get_machine(name, **kwargs): @type kwargs: @return: @rtype: dict - ''' + """ vbox = vb_get_box() machine = vbox.findMachine(name) - return vb_xpcom_to_attribute_dict(machine, 'IMachine', **kwargs) + return vb_xpcom_to_attribute_dict(machine, "IMachine", **kwargs) diff --git a/salt/utils/vmware.py b/salt/utils/vmware.py index 0187b4e4332..26353701ecb 100644 --- a/salt/utils/vmware.py +++ b/salt/utils/vmware.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection library for VMware .. versionadded:: 2015.8.2 @@ -71,17 +71,19 @@ server by running the following command: If the connection was successful, ESXCLI was successfully installed on your system. You should see output related to the ESXi host's syslog configuration. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import atexit import errno import logging -import time -import requests -import sys import ssl +import sys +import time + +import requests # Import Salt Libs import salt.exceptions @@ -93,11 +95,12 @@ import salt.utils.stringutils # Import Third Party Libs from salt.ext import six from salt.ext.six.moves.http_client import BadStatusLine # pylint: disable=E0611 + # pylint: disable=no-name-in-module try: - from pyVim.connect import GetSi, SmartConnect, Disconnect, GetStub, \ - SoapStubAdapter + from pyVim.connect import GetSi, SmartConnect, Disconnect, GetStub, SoapStubAdapter from pyVmomi import vim, vmodl, VmomiSupport + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False @@ -105,6 +108,7 @@ except ImportError: try: from com.vmware.vapi.std.errors_client import Unauthenticated from vmware.vapi.vsphere.client import create_vsphere_client + HAS_VSPHERE_SDK = True except ImportError: @@ -113,6 +117,7 @@ except ImportError: try: import gssapi import base64 + HAS_GSSAPI = True except ImportError: HAS_GSSAPI = False @@ -123,17 +128,19 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if PyVmomi is installed. - ''' + """ if HAS_PYVMOMI: return True - return False, 'Missing dependency: The salt.utils.vmware module requires pyVmomi.' + return False, "Missing dependency: The salt.utils.vmware module requires pyVmomi." -def esxcli(host, user, pwd, cmd, protocol=None, port=None, esxi_host=None, credstore=None): - ''' +def esxcli( + host, user, pwd, cmd, protocol=None, port=None, esxi_host=None, credstore=None +): + """ Shell out and call the specified esxcli commmand, parse the result and return something sane. @@ -147,50 +154,49 @@ def esxcli(host, user, pwd, cmd, protocol=None, port=None, esxi_host=None, creds :param credstore: Optional path to the credential store file :return: Dictionary - ''' + """ - esx_cmd = salt.utils.path.which('esxcli') + esx_cmd = salt.utils.path.which("esxcli") if not esx_cmd: - log.error('Missing dependency: The salt.utils.vmware.esxcli function requires ESXCLI.') + log.error( + "Missing dependency: The salt.utils.vmware.esxcli function requires ESXCLI." + ) return False # Set default port and protocol if none are provided. if port is None: port = 443 if protocol is None: - protocol = 'https' + protocol = "https" if credstore: - esx_cmd += ' --credstore \'{0}\''.format(credstore) + esx_cmd += " --credstore '{0}'".format(credstore) if not esxi_host: # Then we are connecting directly to an ESXi server, # 'host' points at that server, and esxi_host is a reference to the # ESXi instance we are manipulating - esx_cmd += ' -s {0} -u {1} -p \'{2}\' ' \ - '--protocol={3} --portnumber={4} {5}'.format(host, - user, - pwd, - protocol, - port, - cmd) + esx_cmd += ( + " -s {0} -u {1} -p '{2}' " + "--protocol={3} --portnumber={4} {5}".format( + host, user, pwd, protocol, port, cmd + ) + ) else: - esx_cmd += ' -s {0} -h {1} -u {2} -p \'{3}\' ' \ - '--protocol={4} --portnumber={5} {6}'.format(host, - esxi_host, - user, - pwd, - protocol, - port, - cmd) + esx_cmd += ( + " -s {0} -h {1} -u {2} -p '{3}' " + "--protocol={4} --portnumber={5} {6}".format( + host, esxi_host, user, pwd, protocol, port, cmd + ) + ) - ret = salt.modules.cmdmod.run_all(esx_cmd, output_loglevel='quiet') + ret = salt.modules.cmdmod.run_all(esx_cmd, output_loglevel="quiet") return ret def get_vsphere_client(server, username, password, session=None): - ''' + """ Internal helper method to create an instance of the vSphere API client. Please provide username and password to authenticate. @@ -208,7 +214,7 @@ def get_vsphere_client(server, username, password, session=None): Vsphere Client instance :rtype: :class:`vmware.vapi.vmc.client.VsphereClient` - ''' + """ if not session: # Create an https session to be used for a vSphere client session = requests.session() @@ -216,48 +222,56 @@ def get_vsphere_client(server, username, password, session=None): session.verify = False client = None try: - client = create_vsphere_client(server=server, - username=username, - password=password, - session=session) + client = create_vsphere_client( + server=server, username=username, password=password, session=session + ) except Unauthenticated as err: log.trace(err) return client -def _get_service_instance(host, username, password, protocol, - port, mechanism, principal, domain): - ''' +def _get_service_instance( + host, username, password, protocol, port, mechanism, principal, domain +): + """ Internal method to authenticate with a vCenter server or ESX/ESXi host and return the service instance object. - ''' - log.trace('Retrieving new service instance') + """ + log.trace("Retrieving new service instance") token = None - if mechanism == 'userpass': + if mechanism == "userpass": if username is None: raise salt.exceptions.CommandExecutionError( - 'Login mechanism userpass was specified but the mandatory ' - 'parameter \'username\' is missing') + "Login mechanism userpass was specified but the mandatory " + "parameter 'username' is missing" + ) if password is None: raise salt.exceptions.CommandExecutionError( - 'Login mechanism userpass was specified but the mandatory ' - 'parameter \'password\' is missing') - elif mechanism == 'sspi': + "Login mechanism userpass was specified but the mandatory " + "parameter 'password' is missing" + ) + elif mechanism == "sspi": if principal is not None and domain is not None: try: token = get_gssapi_token(principal, host, domain) except Exception as exc: # pylint: disable=broad-except raise salt.exceptions.VMwareConnectionError(six.text_type(exc)) else: - err_msg = 'Login mechanism \'{0}\' was specified but the' \ - ' mandatory parameters are missing'.format(mechanism) + err_msg = ( + "Login mechanism '{0}' was specified but the" + " mandatory parameters are missing".format(mechanism) + ) raise salt.exceptions.CommandExecutionError(err_msg) else: raise salt.exceptions.CommandExecutionError( - 'Unsupported mechanism: \'{0}\''.format(mechanism)) + "Unsupported mechanism: '{0}'".format(mechanism) + ) try: - log.trace('Connecting using the \'%s\' mechanism, with username \'%s\'', - mechanism, username) + log.trace( + "Connecting using the '%s' mechanism, with username '%s'", + mechanism, + username, + ) service_instance = SmartConnect( host=host, user=username, @@ -265,38 +279,51 @@ def _get_service_instance(host, username, password, protocol, protocol=protocol, port=port, b64token=token, - mechanism=mechanism) + mechanism=mechanism, + ) except TypeError as exc: - if 'unexpected keyword argument' in exc.message: - log.error('Initial connect to the VMware endpoint failed with %s', exc.message) - log.error('This may mean that a version of PyVmomi EARLIER than 6.0.0.2016.6 is installed.') - log.error('We recommend updating to that version or later.') + if "unexpected keyword argument" in exc.message: + log.error( + "Initial connect to the VMware endpoint failed with %s", exc.message + ) + log.error( + "This may mean that a version of PyVmomi EARLIER than 6.0.0.2016.6 is installed." + ) + log.error("We recommend updating to that version or later.") 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 \'{0}\'. ' \ - 'Please check the debug log for more information.'.format(host) + default_msg = ( + "Could not connect to host '{0}'. " + "Please check the debug log for more information.".format(host) + ) try: - if (isinstance(exc, vim.fault.HostConnectFault) and - '[SSL: CERTIFICATE_VERIFY_FAILED]' in exc.msg) or \ - '[SSL: CERTIFICATE_VERIFY_FAILED]' in six.text_type(exc): + if ( + isinstance(exc, vim.fault.HostConnectFault) + and "[SSL: CERTIFICATE_VERIFY_FAILED]" in exc.msg + ) or "[SSL: CERTIFICATE_VERIFY_FAILED]" in six.text_type(exc): service_instance = SmartConnect( host=host, user=username, pwd=password, protocol=protocol, port=port, - sslContext=getattr(ssl, '_create_unverified_context', getattr(ssl, '_create_stdlib_context'))(), + sslContext=getattr( + ssl, + "_create_unverified_context", + getattr(ssl, "_create_stdlib_context"), + )(), b64token=token, - mechanism=mechanism) + mechanism=mechanism, + ) else: log.exception(exc) - err_msg = exc.msg if hasattr(exc, 'msg') else default_msg + err_msg = exc.msg if hasattr(exc, "msg") else default_msg raise salt.exceptions.VMwareConnectionError(err_msg) except Exception as exc: # pylint: disable=broad-except # pyVmomi's SmartConnect() actually raises Exception in some cases. - if 'certificate verify failed' in six.text_type(exc): + if "certificate verify failed" in six.text_type(exc): context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) context.verify_mode = ssl.CERT_NONE try: @@ -308,16 +335,16 @@ def _get_service_instance(host, username, password, protocol, port=port, sslContext=context, b64token=token, - mechanism=mechanism + mechanism=mechanism, ) except Exception as exc: # pylint: disable=broad-except log.exception(exc) - err_msg = exc.msg if hasattr(exc, 'msg') else six.text_type(exc) + err_msg = exc.msg if hasattr(exc, "msg") else six.text_type(exc) raise salt.exceptions.VMwareConnectionError( - 'Could not connect to host \'{0}\': ' - '{1}'.format(host, err_msg)) + "Could not connect to host '{0}': " "{1}".format(host, err_msg) + ) else: - err_msg = exc.msg if hasattr(exc, 'msg') else default_msg + 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) @@ -325,7 +352,7 @@ def _get_service_instance(host, username, password, protocol, def get_customizationspec_ref(si, customization_spec_name): - ''' + """ Get a reference to a VMware customization spec for the purposes of customizing a clone si @@ -334,13 +361,15 @@ def get_customizationspec_ref(si, customization_spec_name): customization_spec_name Name of the customization spec - ''' - customization_spec_name = si.content.customizationSpecManager.GetCustomizationSpec(name=customization_spec_name) + """ + customization_spec_name = si.content.customizationSpecManager.GetCustomizationSpec( + name=customization_spec_name + ) return customization_spec_name def get_mor_using_container_view(si, obj_type, obj_name): - ''' + """ Get reference to an object of specified object type and name si @@ -352,19 +381,28 @@ def get_mor_using_container_view(si, obj_type, obj_name): obj_name Name of the object - ''' + """ inventory = get_inventory(si) - container = inventory.viewManager.CreateContainerView(inventory.rootFolder, [obj_type], True) + container = inventory.viewManager.CreateContainerView( + inventory.rootFolder, [obj_type], True + ) for item in container.view: if item.name == obj_name: return item return None -def get_service_instance(host, username=None, password=None, protocol=None, - port=None, mechanism='userpass', principal=None, - domain=None): - ''' +def get_service_instance( + host, + username=None, + password=None, + protocol=None, + port=None, + mechanism="userpass", + principal=None, + domain=None, +): + """ Authenticate with a vCenter server or ESX/ESXi host and return the service instance object. host @@ -395,19 +433,19 @@ def get_service_instance(host, username=None, password=None, protocol=None, domain Kerberos user domain. Required if mechanism is ``sspi`` - ''' + """ if protocol is None: - protocol = 'https' + protocol = "https" if port is None: port = 443 service_instance = GetSi() if service_instance: stub = GetStub() - if (salt.utils.platform.is_proxy() or - (hasattr(stub, 'host') and - stub.host != ':'.join([host, six.text_type(port)]))): + if salt.utils.platform.is_proxy() or ( + hasattr(stub, "host") and stub.host != ":".join([host, six.text_type(port)]) + ): # Proxies will fork and mess up the cached service instance. # If this is a proxy or we are connecting to a different host # invalidate the service instance to avoid a potential memory leak @@ -418,35 +456,25 @@ def get_service_instance(host, username=None, password=None, protocol=None, return service_instance if not service_instance: - service_instance = _get_service_instance(host, - username, - password, - protocol, - port, - mechanism, - principal, - domain) + service_instance = _get_service_instance( + host, username, password, protocol, port, mechanism, principal, domain + ) # Test if data can actually be retrieved or connection has gone stale - log.trace('Checking connection is still authenticated') + log.trace("Checking connection is still authenticated") try: service_instance.CurrentTime() except vim.fault.NotAuthenticated: - log.trace('Session no longer authenticating. Reconnecting') + log.trace("Session no longer authenticating. Reconnecting") Disconnect(service_instance) - service_instance = _get_service_instance(host, - username, - password, - protocol, - port, - mechanism, - principal, - domain) + service_instance = _get_service_instance( + host, username, password, protocol, port, mechanism, principal, domain + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -457,9 +485,8 @@ def get_service_instance(host, username=None, password=None, protocol=None, return service_instance -def get_new_service_instance_stub(service_instance, path, ns=None, - version=None): - ''' +def get_new_service_instance_stub(service_instance, path, ns=None, version=None): + """ Returns a stub that points to a different path, created from an existing connection. @@ -476,7 +503,7 @@ def get_new_service_instance_stub(service_instance, path, ns=None, version Version of the new stub. Default value is None. - ''' + """ # For python 2.7.9 and later, the default SSL context has more strict # connection handshaking rule. We may need turn off the hostname checking # and the client side cert verification. @@ -487,21 +514,18 @@ def get_new_service_instance_stub(service_instance, path, ns=None, context.verify_mode = ssl.CERT_NONE stub = service_instance._stub - hostname = stub.host.split(':')[0] + hostname = stub.host.split(":")[0] session_cookie = stub.cookie.split('"')[1] - VmomiSupport.GetRequestContext()['vcSessionCookie'] = session_cookie - new_stub = SoapStubAdapter(host=hostname, - ns=ns, - path=path, - version=version, - poolSize=0, - sslContext=context) + VmomiSupport.GetRequestContext()["vcSessionCookie"] = session_cookie + new_stub = SoapStubAdapter( + host=hostname, ns=ns, path=path, version=version, poolSize=0, sslContext=context + ) new_stub.cookie = stub.cookie return new_stub -def get_service_instance_from_managed_object(mo_ref, name='<unnamed>'): - ''' +def get_service_instance_from_managed_object(mo_ref, name="<unnamed>"): + """ Retrieves the service instance from a managed object. me_ref @@ -509,30 +533,30 @@ def get_service_instance_from_managed_object(mo_ref, name='<unnamed>'): name Name of managed object. This field is optional. - ''' + """ if not name: name = mo_ref.name - log.trace('[%s] Retrieving service instance from managed object', name) - si = vim.ServiceInstance('ServiceInstance') + log.trace("[%s] Retrieving service instance from managed object", name) + si = vim.ServiceInstance("ServiceInstance") si._stub = mo_ref._stub return si def disconnect(service_instance): - ''' + """ Function that disconnects from the vCenter server or ESXi host service_instance The Service Instance from which to obtain managed object references. - ''' - log.trace('Disconnecting') + """ + log.trace("Disconnecting") try: Disconnect(service_instance) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -542,51 +566,52 @@ def disconnect(service_instance): def is_connection_to_a_vcenter(service_instance): - ''' + """ Function that returns True if the connection is made to a vCenter Server and False if the connection is made to an ESXi host service_instance The Service Instance from which to obtain managed object references. - ''' + """ try: api_type = service_instance.content.about.apiType except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - log.trace('api_type = %s', api_type) - if api_type == 'VirtualCenter': + log.trace("api_type = %s", api_type) + if api_type == "VirtualCenter": return True - elif api_type == 'HostAgent': + elif api_type == "HostAgent": return False else: raise salt.exceptions.VMwareApiError( - 'Unexpected api type \'{0}\' . Supported types: ' - '\'VirtualCenter/HostAgent\''.format(api_type)) + "Unexpected api type '{0}' . Supported types: " + "'VirtualCenter/HostAgent'".format(api_type) + ) def get_service_info(service_instance): - ''' + """ Returns information of the vCenter or ESXi host service_instance The Service Instance from which to obtain managed object references. - ''' + """ try: return service_instance.content.about except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -596,17 +621,19 @@ def get_service_info(service_instance): def _get_dvs(service_instance, dvs_name): - ''' + """ Return a reference to a Distributed Virtual Switch object. :param service_instance: PyVmomi service instance :param dvs_name: Name of DVS to return :return: A PyVmomi DVS object - ''' + """ switches = list_dvs(service_instance) if dvs_name in switches: inventory = get_inventory(service_instance) - container = inventory.viewManager.CreateContainerView(inventory.rootFolder, [vim.DistributedVirtualSwitch], True) + container = inventory.viewManager.CreateContainerView( + inventory.rootFolder, [vim.DistributedVirtualSwitch], True + ) for item in container.view: if item.name == dvs_name: return item @@ -615,35 +642,35 @@ def _get_dvs(service_instance, dvs_name): def _get_pnics(host_reference): - ''' + """ Helper function that returns a list of PhysicalNics and their information. - ''' + """ return host_reference.config.network.pnic def _get_vnics(host_reference): - ''' + """ Helper function that returns a list of VirtualNics and their information. - ''' + """ return host_reference.config.network.vnic def _get_vnic_manager(host_reference): - ''' + """ Helper function that returns a list of Virtual NicManagers and their information. - ''' + """ return host_reference.configManager.virtualNicManager def _get_dvs_portgroup(dvs, portgroup_name): - ''' + """ Return a portgroup object corresponding to the portgroup name on the dvs :param dvs: DVS object :param portgroup_name: Name of portgroup to return :return: Portgroup object - ''' + """ for portgroup in dvs.portgroup: if portgroup.name == portgroup_name: return portgroup @@ -652,13 +679,13 @@ def _get_dvs_portgroup(dvs, portgroup_name): def _get_dvs_uplink_portgroup(dvs, portgroup_name): - ''' + """ Return a portgroup object corresponding to the portgroup name on the dvs :param dvs: DVS object :param portgroup_name: Name of portgroup to return :return: Portgroup object - ''' + """ for portgroup in dvs.portgroup: if portgroup.name == portgroup_name: return portgroup @@ -667,7 +694,7 @@ def _get_dvs_uplink_portgroup(dvs, portgroup_name): def get_gssapi_token(principal, host, domain): - ''' + """ Get the gssapi token for Kerberos connection principal @@ -676,13 +703,13 @@ def get_gssapi_token(principal, host, domain): Host url where we would like to authenticate domain Kerberos user domain - ''' + """ if not HAS_GSSAPI: - raise ImportError('The gssapi library is not imported.') + raise ImportError("The gssapi library is not imported.") - service = '{0}/{1}@{2}'.format(principal, host, domain) - log.debug('Retrieving gsspi token for service %s', service) + service = "{0}/{1}@{2}".format(principal, host, domain) + log.debug("Retrieving gsspi token for service %s", service) service_name = gssapi.Name(service, gssapi.C_NT_USER_NAME) ctx = gssapi.InitContext(service_name) in_token = None @@ -696,100 +723,126 @@ def get_gssapi_token(principal, host, domain): break if not in_token: raise salt.exceptions.CommandExecutionError( - 'Can\'t receive token, no response from server') + "Can't receive token, no response from server" + ) raise salt.exceptions.CommandExecutionError( - 'Context established, but didn\'t receive token') + "Context established, but didn't receive token" + ) def get_hardware_grains(service_instance): - ''' + """ Return hardware info for standard minion grains if the service_instance is a HostAgent type service_instance The service instance object to get hardware info for .. versionadded:: 2016.11.0 - ''' + """ hw_grain_data = {} - if get_inventory(service_instance).about.apiType == 'HostAgent': - view = service_instance.content.viewManager.CreateContainerView(service_instance.RetrieveContent().rootFolder, - [vim.HostSystem], True) + if get_inventory(service_instance).about.apiType == "HostAgent": + view = service_instance.content.viewManager.CreateContainerView( + service_instance.RetrieveContent().rootFolder, [vim.HostSystem], True + ) if view and view.view: - hw_grain_data['manufacturer'] = view.view[0].hardware.systemInfo.vendor - hw_grain_data['productname'] = view.view[0].hardware.systemInfo.model + hw_grain_data["manufacturer"] = view.view[0].hardware.systemInfo.vendor + hw_grain_data["productname"] = view.view[0].hardware.systemInfo.model for _data in view.view[0].hardware.systemInfo.otherIdentifyingInfo: - if _data.identifierType.key == 'ServiceTag': - hw_grain_data['serialnumber'] = _data.identifierValue + if _data.identifierType.key == "ServiceTag": + hw_grain_data["serialnumber"] = _data.identifierValue - hw_grain_data['osfullname'] = view.view[0].summary.config.product.fullName - hw_grain_data['osmanufacturer'] = view.view[0].summary.config.product.vendor - hw_grain_data['osrelease'] = view.view[0].summary.config.product.version - hw_grain_data['osbuild'] = view.view[0].summary.config.product.build - hw_grain_data['os_family'] = view.view[0].summary.config.product.name - hw_grain_data['os'] = view.view[0].summary.config.product.name - hw_grain_data['mem_total'] = view.view[0].hardware.memorySize /1024/1024 - hw_grain_data['biosversion'] = view.view[0].hardware.biosInfo.biosVersion - hw_grain_data['biosreleasedate'] = view.view[0].hardware.biosInfo.releaseDate.date().strftime('%m/%d/%Y') - hw_grain_data['cpu_model'] = view.view[0].hardware.cpuPkg[0].description - hw_grain_data['kernel'] = view.view[0].summary.config.product.productLineId - hw_grain_data['num_cpu_sockets'] = view.view[0].hardware.cpuInfo.numCpuPackages - hw_grain_data['num_cpu_cores'] = view.view[0].hardware.cpuInfo.numCpuCores - hw_grain_data['num_cpus'] = hw_grain_data['num_cpu_sockets'] * hw_grain_data['num_cpu_cores'] - hw_grain_data['ip_interfaces'] = {} - hw_grain_data['ip4_interfaces'] = {} - hw_grain_data['ip6_interfaces'] = {} - hw_grain_data['hwaddr_interfaces'] = {} + hw_grain_data["osfullname"] = view.view[0].summary.config.product.fullName + hw_grain_data["osmanufacturer"] = view.view[0].summary.config.product.vendor + hw_grain_data["osrelease"] = view.view[0].summary.config.product.version + hw_grain_data["osbuild"] = view.view[0].summary.config.product.build + hw_grain_data["os_family"] = view.view[0].summary.config.product.name + hw_grain_data["os"] = view.view[0].summary.config.product.name + hw_grain_data["mem_total"] = view.view[0].hardware.memorySize / 1024 / 1024 + hw_grain_data["biosversion"] = view.view[0].hardware.biosInfo.biosVersion + hw_grain_data["biosreleasedate"] = ( + view.view[0].hardware.biosInfo.releaseDate.date().strftime("%m/%d/%Y") + ) + hw_grain_data["cpu_model"] = view.view[0].hardware.cpuPkg[0].description + hw_grain_data["kernel"] = view.view[0].summary.config.product.productLineId + hw_grain_data["num_cpu_sockets"] = view.view[ + 0 + ].hardware.cpuInfo.numCpuPackages + hw_grain_data["num_cpu_cores"] = view.view[0].hardware.cpuInfo.numCpuCores + hw_grain_data["num_cpus"] = ( + hw_grain_data["num_cpu_sockets"] * hw_grain_data["num_cpu_cores"] + ) + hw_grain_data["ip_interfaces"] = {} + hw_grain_data["ip4_interfaces"] = {} + hw_grain_data["ip6_interfaces"] = {} + hw_grain_data["hwaddr_interfaces"] = {} for _vnic in view.view[0].configManager.networkSystem.networkConfig.vnic: - hw_grain_data['ip_interfaces'][_vnic.device] = [] - hw_grain_data['ip4_interfaces'][_vnic.device] = [] - hw_grain_data['ip6_interfaces'][_vnic.device] = [] + hw_grain_data["ip_interfaces"][_vnic.device] = [] + hw_grain_data["ip4_interfaces"][_vnic.device] = [] + hw_grain_data["ip6_interfaces"][_vnic.device] = [] - hw_grain_data['ip_interfaces'][_vnic.device].append(_vnic.spec.ip.ipAddress) - hw_grain_data['ip4_interfaces'][_vnic.device].append(_vnic.spec.ip.ipAddress) + hw_grain_data["ip_interfaces"][_vnic.device].append( + _vnic.spec.ip.ipAddress + ) + hw_grain_data["ip4_interfaces"][_vnic.device].append( + _vnic.spec.ip.ipAddress + ) if _vnic.spec.ip.ipV6Config: - hw_grain_data['ip6_interfaces'][_vnic.device].append(_vnic.spec.ip.ipV6Config.ipV6Address) - hw_grain_data['hwaddr_interfaces'][_vnic.device] = _vnic.spec.mac - hw_grain_data['host'] = view.view[0].configManager.networkSystem.dnsConfig.hostName - hw_grain_data['domain'] = view.view[0].configManager.networkSystem.dnsConfig.domainName - hw_grain_data['fqdn'] = '{0}{1}{2}'.format( + hw_grain_data["ip6_interfaces"][_vnic.device].append( + _vnic.spec.ip.ipV6Config.ipV6Address + ) + hw_grain_data["hwaddr_interfaces"][_vnic.device] = _vnic.spec.mac + hw_grain_data["host"] = view.view[ + 0 + ].configManager.networkSystem.dnsConfig.hostName + hw_grain_data["domain"] = view.view[ + 0 + ].configManager.networkSystem.dnsConfig.domainName + hw_grain_data["fqdn"] = "{0}{1}{2}".format( view.view[0].configManager.networkSystem.dnsConfig.hostName, - ('.' if view.view[0].configManager.networkSystem.dnsConfig.domainName else ''), - view.view[0].configManager.networkSystem.dnsConfig.domainName) + ( + "." + if view.view[0].configManager.networkSystem.dnsConfig.domainName + else "" + ), + view.view[0].configManager.networkSystem.dnsConfig.domainName, + ) for _pnic in view.view[0].configManager.networkSystem.networkInfo.pnic: - hw_grain_data['hwaddr_interfaces'][_pnic.device] = _pnic.mac + hw_grain_data["hwaddr_interfaces"][_pnic.device] = _pnic.mac - hw_grain_data['timezone'] = view.view[0].configManager.dateTimeSystem.dateTimeInfo.timeZone.name + hw_grain_data["timezone"] = view.view[ + 0 + ].configManager.dateTimeSystem.dateTimeInfo.timeZone.name view = None return hw_grain_data def get_inventory(service_instance): - ''' + """ Return the inventory of a Service Instance Object. service_instance The Service Instance Object for which to obtain inventory. - ''' + """ return service_instance.RetrieveContent() def get_root_folder(service_instance): - ''' + """ Returns the root folder of a vCenter. service_instance The Service Instance Object for which to obtain the root folder. - ''' + """ try: - log.trace('Retrieving root folder') + log.trace("Retrieving root folder") return service_instance.RetrieveContent().rootFolder except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -798,10 +851,15 @@ def get_root_folder(service_instance): raise salt.exceptions.VMwareRuntimeError(exc.msg) -def get_content(service_instance, obj_type, property_list=None, - container_ref=None, traversal_spec=None, - local_properties=False): - ''' +def get_content( + service_instance, + obj_type, + property_list=None, + container_ref=None, + traversal_spec=None, + local_properties=False, +): + """ Returns the content of the specified type of object for a Service Instance. For more information, please see: @@ -828,7 +886,7 @@ def get_content(service_instance, obj_type, property_list=None, local_properties Flag specifying whether the properties to be retrieved are local to the container. If that is the case, the traversal spec needs to be None. - ''' + """ # Start at the rootFolder if container starting point not specified if not container_ref: container_ref = get_root_folder(service_instance) @@ -843,12 +901,14 @@ def get_content(service_instance, obj_type, property_list=None, # get everything using a container view try: obj_ref = service_instance.content.viewManager.CreateContainerView( - container_ref, [obj_type], True) + container_ref, [obj_type], True + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " + "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -859,41 +919,41 @@ def get_content(service_instance, obj_type, property_list=None, # Create 'Traverse All' traversal spec to determine the path for # collection traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - name='traverseEntities', - path='view', + name="traverseEntities", + path="view", skip=False, - type=vim.view.ContainerView + type=vim.view.ContainerView, ) # Create property spec to determine properties to be retrieved property_spec = vmodl.query.PropertyCollector.PropertySpec( - type=obj_type, - all=True if not property_list else False, - pathSet=property_list + type=obj_type, all=True if not property_list else False, pathSet=property_list ) # Create object spec to navigate content obj_spec = vmodl.query.PropertyCollector.ObjectSpec( obj=obj_ref, skip=True if not local_properties else False, - selectSet=[traversal_spec] if not local_properties else None + selectSet=[traversal_spec] if not local_properties else None, ) # Create a filter spec and specify object, property spec in it filter_spec = vmodl.query.PropertyCollector.FilterSpec( objectSet=[obj_spec], propSet=[property_spec], - reportMissingObjectsInResults=False + reportMissingObjectsInResults=False, ) # Retrieve the contents try: - content = service_instance.content.propertyCollector.RetrieveContents([filter_spec]) + content = service_instance.content.propertyCollector.RetrieveContents( + [filter_spec] + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -908,8 +968,9 @@ def get_content(service_instance, obj_type, property_list=None, except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " + "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -920,8 +981,14 @@ def get_content(service_instance, obj_type, property_list=None, return content -def get_mor_by_property(service_instance, object_type, property_value, property_name='name', container_ref=None): - ''' +def get_mor_by_property( + service_instance, + object_type, + property_value, + property_name="name", + container_ref=None, +): + """ Returns the first managed object reference having the specified property value. service_instance @@ -940,22 +1007,32 @@ def get_mor_by_property(service_instance, object_type, property_value, property_ An optional reference to the managed object to search under. Can either be an object of type Folder, Datacenter, ComputeResource, Resource Pool or HostSystem. If not specified, default behaviour is to search under the inventory rootFolder. - ''' + """ # Get list of all managed object references with specified property - object_list = get_mors_with_properties(service_instance, object_type, property_list=[property_name], container_ref=container_ref) + object_list = get_mors_with_properties( + service_instance, + object_type, + property_list=[property_name], + container_ref=container_ref, + ) for obj in object_list: - obj_id = six.text_type(obj.get('object', '')).strip('\'"') + obj_id = six.text_type(obj.get("object", "")).strip("'\"") if obj[property_name] == property_value or property_value == obj_id: - return obj['object'] + return obj["object"] return None -def get_mors_with_properties(service_instance, object_type, property_list=None, - container_ref=None, traversal_spec=None, - local_properties=False): - ''' +def get_mors_with_properties( + service_instance, + object_type, + property_list=None, + container_ref=None, + traversal_spec=None, + local_properties=False, +): + """ Returns a list containing properties and managed object references for the managed object. service_instance @@ -979,13 +1056,15 @@ def get_mors_with_properties(service_instance, object_type, property_list=None, local_properties Flag specigying whether the properties to be retrieved are local to the container. If that is the case, the traversal spec needs to be None. - ''' + """ # Get all the content content_args = [service_instance, object_type] - content_kwargs = {'property_list': property_list, - 'container_ref': container_ref, - 'traversal_spec': traversal_spec, - 'local_properties': local_properties} + content_kwargs = { + "property_list": property_list, + "container_ref": container_ref, + "traversal_spec": traversal_spec, + "local_properties": local_properties, + } try: content = get_content(*content_args, **content_kwargs) except BadStatusLine: @@ -1000,14 +1079,14 @@ def get_mors_with_properties(service_instance, object_type, property_list=None, properties = {} for prop in obj.propSet: properties[prop.name] = prop.val - properties['object'] = obj.obj + properties["object"] = obj.obj object_list.append(properties) - log.trace('Retrieved %s objects', len(object_list)) + log.trace("Retrieved %s objects", len(object_list)) return object_list def get_properties_of_managed_object(mo_ref, properties): - ''' + """ Returns specific properties of a managed object, retrieved in an optimally. @@ -1016,88 +1095,96 @@ def get_properties_of_managed_object(mo_ref, properties): properties List of properties of the managed object to retrieve. - ''' + """ service_instance = get_service_instance_from_managed_object(mo_ref) - log.trace('Retrieving name of %s', type(mo_ref).__name__) + log.trace("Retrieving name of %s", type(mo_ref).__name__) try: - items = get_mors_with_properties(service_instance, - type(mo_ref), - container_ref=mo_ref, - property_list=['name'], - local_properties=True) - mo_name = items[0]['name'] + items = get_mors_with_properties( + service_instance, + type(mo_ref), + container_ref=mo_ref, + property_list=["name"], + local_properties=True, + ) + mo_name = items[0]["name"] except vmodl.query.InvalidProperty: - mo_name = '<unnamed>' - log.trace('Retrieving properties \'%s\' of %s \'%s\'', - properties, type(mo_ref).__name__, mo_name) - items = get_mors_with_properties(service_instance, - type(mo_ref), - container_ref=mo_ref, - property_list=properties, - local_properties=True) + mo_name = "<unnamed>" + log.trace( + "Retrieving properties '%s' of %s '%s'", + properties, + type(mo_ref).__name__, + mo_name, + ) + items = get_mors_with_properties( + service_instance, + type(mo_ref), + container_ref=mo_ref, + property_list=properties, + local_properties=True, + ) if not items: raise salt.exceptions.VMwareApiError( - 'Properties of managed object \'{0}\' weren\'t ' - 'retrieved'.format(mo_name)) + "Properties of managed object '{0}' weren't " "retrieved".format(mo_name) + ) return items[0] def get_managed_object_name(mo_ref): - ''' + """ Returns the name of a managed object. If the name wasn't found, it returns None. mo_ref The managed object reference. - ''' - props = get_properties_of_managed_object(mo_ref, ['name']) - return props.get('name') + """ + props = get_properties_of_managed_object(mo_ref, ["name"]) + return props.get("name") def get_network_adapter_type(adapter_type): - ''' + """ Return the network adapter type. adpater_type The adapter type from which to obtain the network adapter type. - ''' - if adapter_type == 'vmxnet': + """ + if adapter_type == "vmxnet": return vim.vm.device.VirtualVmxnet() - elif adapter_type == 'vmxnet2': + elif adapter_type == "vmxnet2": return vim.vm.device.VirtualVmxnet2() - elif adapter_type == 'vmxnet3': + elif adapter_type == "vmxnet3": return vim.vm.device.VirtualVmxnet3() - elif adapter_type == 'e1000': + elif adapter_type == "e1000": return vim.vm.device.VirtualE1000() - elif adapter_type == 'e1000e': + elif adapter_type == "e1000e": return vim.vm.device.VirtualE1000e() - raise ValueError('An unknown network adapter object type name.') + raise ValueError("An unknown network adapter object type name.") def get_network_adapter_object_type(adapter_object): - ''' + """ Returns the network adapter type. adapter_object The adapter object from which to obtain the network adapter type. - ''' + """ if isinstance(adapter_object, vim.vm.device.VirtualVmxnet2): - return 'vmxnet2' + return "vmxnet2" if isinstance(adapter_object, vim.vm.device.VirtualVmxnet3): - return 'vmxnet3' + return "vmxnet3" if isinstance(adapter_object, vim.vm.device.VirtualVmxnet): - return 'vmxnet' + return "vmxnet" if isinstance(adapter_object, vim.vm.device.VirtualE1000e): - return 'e1000e' + return "e1000e" if isinstance(adapter_object, vim.vm.device.VirtualE1000): - return 'e1000' + return "e1000" - raise ValueError('An unknown network adapter object type.') + raise ValueError("An unknown network adapter object type.") def get_dvss(dc_ref, dvs_names=None, get_all_dvss=False): - ''' + """ Returns distributed virtual switches (DVSs) in a datacenter. dc_ref @@ -1108,59 +1195,66 @@ def get_dvss(dc_ref, dvs_names=None, get_all_dvss=False): get_all_dvss Return all DVSs in the datacenter. Default is False. - ''' + """ dc_name = get_managed_object_name(dc_ref) log.trace( - 'Retrieving DVSs in datacenter \'%s\', dvs_names=\'%s\', get_all_dvss=%s', + "Retrieving DVSs in datacenter '%s', dvs_names='%s', get_all_dvss=%s", dc_name, - ','.join(dvs_names) if dvs_names else None, - get_all_dvss + ",".join(dvs_names) if dvs_names else None, + get_all_dvss, ) - properties = ['name'] + properties = ["name"] traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='networkFolder', + path="networkFolder", skip=True, type=vim.Datacenter, - selectSet=[vmodl.query.PropertyCollector.TraversalSpec( - path='childEntity', - skip=False, - type=vim.Folder)]) + selectSet=[ + vmodl.query.PropertyCollector.TraversalSpec( + path="childEntity", skip=False, type=vim.Folder + ) + ], + ) service_instance = get_service_instance_from_managed_object(dc_ref) - items = [i['object'] for i in - get_mors_with_properties(service_instance, - vim.DistributedVirtualSwitch, - container_ref=dc_ref, - property_list=properties, - traversal_spec=traversal_spec) - if get_all_dvss or (dvs_names and i['name'] in dvs_names)] + items = [ + i["object"] + for i in get_mors_with_properties( + service_instance, + vim.DistributedVirtualSwitch, + container_ref=dc_ref, + property_list=properties, + traversal_spec=traversal_spec, + ) + if get_all_dvss or (dvs_names and i["name"] in dvs_names) + ] return items def get_network_folder(dc_ref): - ''' + """ Retrieves the network folder of a datacenter - ''' + """ dc_name = get_managed_object_name(dc_ref) - log.trace('Retrieving network folder in datacenter \'%s\'', dc_name) + log.trace("Retrieving network folder in datacenter '%s'", dc_name) service_instance = get_service_instance_from_managed_object(dc_ref) traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='networkFolder', - skip=False, - type=vim.Datacenter) - entries = get_mors_with_properties(service_instance, - vim.Folder, - container_ref=dc_ref, - property_list=['name'], - traversal_spec=traversal_spec) + path="networkFolder", skip=False, type=vim.Datacenter + ) + entries = get_mors_with_properties( + service_instance, + vim.Folder, + container_ref=dc_ref, + property_list=["name"], + traversal_spec=traversal_spec, + ) if not entries: raise salt.exceptions.VMwareObjectRetrievalError( - 'Network folder in datacenter \'{0}\' wasn\'t retrieved' - ''.format(dc_name)) - return entries[0]['object'] + "Network folder in datacenter '{0}' wasn't retrieved" "".format(dc_name) + ) + return entries[0]["object"] def create_dvs(dc_ref, dvs_name, dvs_create_spec=None): - ''' + """ Creates a distributed virtual switches (DVS) in a datacenter. Returns the reference to the newly created distributed virtual switch. @@ -1173,9 +1267,9 @@ def create_dvs(dc_ref, dvs_name, dvs_create_spec=None): dvs_create_spec The DVS spec (vim.DVSCreateSpec) to use when creating the DVS. Default is None. - ''' + """ dc_name = get_managed_object_name(dc_ref) - log.trace('Creating DVS \'%s\' in datacenter \'%s\'', dvs_name, dc_name) + log.trace("Creating DVS '%s' in datacenter '%s'", dvs_name, dc_name) if not dvs_create_spec: dvs_create_spec = vim.DVSCreateSpec() if not dvs_create_spec.configSpec: @@ -1187,8 +1281,8 @@ def create_dvs(dc_ref, dvs_name, dvs_create_spec=None): except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1199,7 +1293,7 @@ def create_dvs(dc_ref, dvs_name, dvs_create_spec=None): def update_dvs(dvs_ref, dvs_config_spec): - ''' + """ Updates a distributed virtual switch with the config_spec. dvs_ref @@ -1208,16 +1302,16 @@ def update_dvs(dvs_ref, dvs_config_spec): dvs_config_spec The updated config spec (vim.VMwareDVSConfigSpec) to be applied to the DVS. - ''' + """ dvs_name = get_managed_object_name(dvs_ref) - log.trace('Updating dvs \'%s\'', dvs_name) + log.trace("Updating dvs '%s'", dvs_name) try: task = dvs_ref.ReconfigureDvs_Task(dvs_config_spec) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1228,7 +1322,7 @@ def update_dvs(dvs_ref, dvs_config_spec): def set_dvs_network_resource_management_enabled(dvs_ref, enabled): - ''' + """ Sets whether NIOC is enabled on a DVS. dvs_ref @@ -1236,17 +1330,20 @@ def set_dvs_network_resource_management_enabled(dvs_ref, enabled): enabled Flag specifying whether NIOC is enabled. - ''' + """ dvs_name = get_managed_object_name(dvs_ref) - log.trace('Setting network resource management enable to %s on ' - 'dvs \'%s\'', enabled, dvs_name) + log.trace( + "Setting network resource management enable to %s on " "dvs '%s'", + enabled, + dvs_name, + ) try: dvs_ref.EnableNetworkResourceManagement(enable=enabled) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1255,9 +1352,8 @@ def set_dvs_network_resource_management_enabled(dvs_ref, enabled): raise salt.exceptions.VMwareRuntimeError(exc.msg) -def get_dvportgroups(parent_ref, portgroup_names=None, - get_all_portgroups=False): - ''' +def get_dvportgroups(parent_ref, portgroup_names=None, get_all_portgroups=False): + """ Returns distributed virtual porgroups (dvportgroups). The parent object can be either a datacenter or a dvs. @@ -1269,78 +1365,86 @@ def get_dvportgroups(parent_ref, portgroup_names=None, get_all_portgroups Return all portgroups in the parent. Default is False. - ''' - if not (isinstance(parent_ref, - (vim.Datacenter, vim.DistributedVirtualSwitch))): + """ + if not isinstance(parent_ref, (vim.Datacenter, vim.DistributedVirtualSwitch)): raise salt.exceptions.ArgumentValueError( - 'Parent has to be either a datacenter, ' - 'or a distributed virtual switch') + "Parent has to be either a datacenter, " "or a distributed virtual switch" + ) parent_name = get_managed_object_name(parent_ref) - log.trace('Retrieving portgroup in %s \'%s\', portgroups_names=\'%s\', ' - 'get_all_portgroups=%s', - type(parent_ref).__name__, - parent_name, - ','.join(portgroup_names) if portgroup_names else None, - get_all_portgroups) - properties = ['name'] + log.trace( + "Retrieving portgroup in %s '%s', portgroups_names='%s', " + "get_all_portgroups=%s", + type(parent_ref).__name__, + parent_name, + ",".join(portgroup_names) if portgroup_names else None, + get_all_portgroups, + ) + properties = ["name"] if isinstance(parent_ref, vim.Datacenter): traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='networkFolder', + path="networkFolder", skip=True, type=vim.Datacenter, - selectSet=[vmodl.query.PropertyCollector.TraversalSpec( - path='childEntity', - skip=False, - type=vim.Folder)]) + selectSet=[ + vmodl.query.PropertyCollector.TraversalSpec( + path="childEntity", skip=False, type=vim.Folder + ) + ], + ) else: # parent is distributed virtual switch traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='portgroup', - skip=False, - type=vim.DistributedVirtualSwitch) + path="portgroup", skip=False, type=vim.DistributedVirtualSwitch + ) service_instance = get_service_instance_from_managed_object(parent_ref) - items = [i['object'] for i in - get_mors_with_properties(service_instance, - vim.DistributedVirtualPortgroup, - container_ref=parent_ref, - property_list=properties, - traversal_spec=traversal_spec) - if get_all_portgroups or - (portgroup_names and i['name'] in portgroup_names)] + items = [ + i["object"] + for i in get_mors_with_properties( + service_instance, + vim.DistributedVirtualPortgroup, + container_ref=parent_ref, + property_list=properties, + traversal_spec=traversal_spec, + ) + if get_all_portgroups or (portgroup_names and i["name"] in portgroup_names) + ] return items def get_uplink_dvportgroup(dvs_ref): - ''' + """ Returns the uplink distributed virtual portgroup of a distributed virtual switch (dvs) dvs_ref The dvs reference - ''' + """ dvs_name = get_managed_object_name(dvs_ref) - log.trace('Retrieving uplink portgroup of dvs \'%s\'', dvs_name) + log.trace("Retrieving uplink portgroup of dvs '%s'", dvs_name) traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='portgroup', - skip=False, - type=vim.DistributedVirtualSwitch) + path="portgroup", skip=False, type=vim.DistributedVirtualSwitch + ) service_instance = get_service_instance_from_managed_object(dvs_ref) - items = [entry['object'] for entry in - get_mors_with_properties(service_instance, - vim.DistributedVirtualPortgroup, - container_ref=dvs_ref, - property_list=['tag'], - traversal_spec=traversal_spec) - if entry['tag'] and - [t for t in entry['tag'] if t.key == 'SYSTEM/DVS.UPLINKPG']] + items = [ + entry["object"] + for entry in get_mors_with_properties( + service_instance, + vim.DistributedVirtualPortgroup, + container_ref=dvs_ref, + property_list=["tag"], + traversal_spec=traversal_spec, + ) + if entry["tag"] and [t for t in entry["tag"] if t.key == "SYSTEM/DVS.UPLINKPG"] + ] if not items: raise salt.exceptions.VMwareObjectRetrievalError( - 'Uplink portgroup of DVS \'{0}\' wasn\'t found'.format(dvs_name)) + "Uplink portgroup of DVS '{0}' wasn't found".format(dvs_name) + ) return items[0] def create_dvportgroup(dvs_ref, spec): - ''' + """ Creates a distributed virtual portgroup on a distributed virtual switch (dvs) @@ -1349,17 +1453,17 @@ def create_dvportgroup(dvs_ref, spec): spec Portgroup spec (vim.DVPortgroupConfigSpec) - ''' + """ dvs_name = get_managed_object_name(dvs_ref) - log.trace('Adding portgroup %s to dvs \'%s\'', spec.name, dvs_name) - log.trace('spec = %s', spec) + log.trace("Adding portgroup %s to dvs '%s'", spec.name, dvs_name) + log.trace("spec = %s", spec) try: task = dvs_ref.CreateDVPortgroup_Task(spec) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1370,7 +1474,7 @@ def create_dvportgroup(dvs_ref, spec): def update_dvportgroup(portgroup_ref, spec): - ''' + """ Updates a distributed virtual portgroup portgroup_ref @@ -1378,16 +1482,16 @@ def update_dvportgroup(portgroup_ref, spec): spec Portgroup spec (vim.DVPortgroupConfigSpec) - ''' + """ pg_name = get_managed_object_name(portgroup_ref) - log.trace('Updating portgrouo %s', pg_name) + log.trace("Updating portgrouo %s", pg_name) try: task = portgroup_ref.ReconfigureDVPortgroup_Task(spec) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1398,21 +1502,21 @@ def update_dvportgroup(portgroup_ref, spec): def remove_dvportgroup(portgroup_ref): - ''' + """ Removes a distributed virtual portgroup portgroup_ref The portgroup reference - ''' + """ pg_name = get_managed_object_name(portgroup_ref) - log.trace('Removing portgroup %s', pg_name) + log.trace("Removing portgroup %s", pg_name) try: task = portgroup_ref.Destroy_Task() except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1423,7 +1527,7 @@ def remove_dvportgroup(portgroup_ref): def get_networks(parent_ref, network_names=None, get_all_networks=False): - ''' + """ Returns networks of standard switches. The parent object can be a datacenter. @@ -1436,41 +1540,46 @@ def get_networks(parent_ref, network_names=None, get_all_networks=False): get_all_networks Boolean indicates whether to return all networks in the parent. Default is False. - ''' + """ if not isinstance(parent_ref, vim.Datacenter): - raise salt.exceptions.ArgumentValueError( - 'Parent has to be a datacenter.') + raise salt.exceptions.ArgumentValueError("Parent has to be a datacenter.") parent_name = get_managed_object_name(parent_ref) - log.trace('Retrieving network from %s \'%s\', network_names=\'%s\', ' - 'get_all_networks=%s', - type(parent_ref).__name__, - parent_name, - ','.join(network_names) if network_names else None, - get_all_networks) - properties = ['name'] + log.trace( + "Retrieving network from %s '%s', network_names='%s', " "get_all_networks=%s", + type(parent_ref).__name__, + parent_name, + ",".join(network_names) if network_names else None, + get_all_networks, + ) + properties = ["name"] service_instance = get_service_instance_from_managed_object(parent_ref) traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='networkFolder', + path="networkFolder", skip=True, type=vim.Datacenter, - selectSet=[vmodl.query.PropertyCollector.TraversalSpec( - path='childEntity', - skip=False, - type=vim.Folder)]) - items = [i['object'] for i in - get_mors_with_properties(service_instance, - vim.Network, - container_ref=parent_ref, - property_list=properties, - traversal_spec=traversal_spec) - if get_all_networks or - (network_names and i['name'] in network_names)] + selectSet=[ + vmodl.query.PropertyCollector.TraversalSpec( + path="childEntity", skip=False, type=vim.Folder + ) + ], + ) + items = [ + i["object"] + for i in get_mors_with_properties( + service_instance, + vim.Network, + container_ref=parent_ref, + property_list=properties, + traversal_spec=traversal_spec, + ) + if get_all_networks or (network_names and i["name"] in network_names) + ] return items def list_objects(service_instance, vim_object, properties=None): - ''' + """ Returns a simple list of objects from a given service instance. service_instance @@ -1482,33 +1591,33 @@ def list_objects(service_instance, vim_object, properties=None): properties An optional list of object properties used to return reference results. If not provided, defaults to ``name``. - ''' + """ if properties is None: - properties = ['name'] + properties = ["name"] items = [] item_list = get_mors_with_properties(service_instance, vim_object, properties) for item in item_list: - items.append(item['name']) + items.append(item["name"]) return items def get_license_manager(service_instance): - ''' + """ Returns the license manager. service_instance The Service Instance Object from which to obrain the license manager. - ''' + """ - log.debug('Retrieving license manager') + log.debug("Retrieving license manager") try: lic_manager = service_instance.content.licenseManager except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1519,22 +1628,23 @@ def get_license_manager(service_instance): def get_license_assignment_manager(service_instance): - ''' + """ Returns the license assignment manager. service_instance The Service Instance Object from which to obrain the license manager. - ''' + """ - log.debug('Retrieving license assignment manager') + log.debug("Retrieving license assignment manager") try: - lic_assignment_manager = \ - service_instance.content.licenseManager.licenseAssignmentManager + lic_assignment_manager = ( + service_instance.content.licenseManager.licenseAssignmentManager + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1543,12 +1653,13 @@ def get_license_assignment_manager(service_instance): raise salt.exceptions.VMwareRuntimeError(exc.msg) if not lic_assignment_manager: raise salt.exceptions.VMwareObjectRetrievalError( - 'License assignment manager was not retrieved') + "License assignment manager was not retrieved" + ) return lic_assignment_manager def get_licenses(service_instance, license_manager=None): - ''' + """ Returns the licenses on a specific instance. service_instance @@ -1557,18 +1668,18 @@ def get_licenses(service_instance, license_manager=None): license_manager The License Manager object of the service instance. If not provided it will be retrieved. - ''' + """ if not license_manager: license_manager = get_license_manager(service_instance) - log.debug('Retrieving licenses') + log.debug("Retrieving licenses") try: return license_manager.licenses except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1578,7 +1689,7 @@ def get_licenses(service_instance, license_manager=None): def add_license(service_instance, key, description, license_manager=None): - ''' + """ Adds a license. service_instance @@ -1593,20 +1704,20 @@ def add_license(service_instance, key, description, license_manager=None): license_manager The License Manager object of the service instance. If not provided it will be retrieved. - ''' + """ if not license_manager: license_manager = get_license_manager(service_instance) label = vim.KeyValue() - label.key = 'VpxClientLicenseLabel' + label.key = "VpxClientLicenseLabel" label.value = description - log.debug('Adding license \'%s\'', description) + log.debug("Adding license '%s'", description) try: vmware_license = license_manager.AddLicense(key, [label]) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1616,9 +1727,10 @@ def add_license(service_instance, key, description, license_manager=None): return vmware_license -def get_assigned_licenses(service_instance, entity_ref=None, entity_name=None, - license_assignment_manager=None): - ''' +def get_assigned_licenses( + service_instance, entity_ref=None, entity_name=None, license_assignment_manager=None +): + """ Returns the licenses assigned to an entity. If entity ref is not provided, then entity_name is assumed to be the vcenter. This is later checked if the entity name is provided. @@ -1639,27 +1751,27 @@ def get_assigned_licenses(service_instance, entity_ref=None, entity_name=None, The LicenseAssignmentManager object of the service instance. If not provided it will be retrieved. Default is None. - ''' + """ if not license_assignment_manager: - license_assignment_manager = \ - get_license_assignment_manager(service_instance) + license_assignment_manager = get_license_assignment_manager(service_instance) if not entity_name: - raise salt.exceptions.ArgumentValueError('No entity_name passed') + raise salt.exceptions.ArgumentValueError("No entity_name passed") # If entity_ref is not defined, then interested in the vcenter entity_id = None - entity_type = 'moid' + entity_type = "moid" check_name = False if not entity_ref: if entity_name: check_name = True - entity_type = 'uuid' + entity_type = "uuid" try: entity_id = service_instance.content.about.instanceUuid except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " + "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1669,15 +1781,14 @@ def get_assigned_licenses(service_instance, entity_ref=None, entity_name=None, else: entity_id = entity_ref._moId - log.trace('Retrieving licenses assigned to \'%s\'', entity_name) + log.trace("Retrieving licenses assigned to '%s'", entity_name) try: - assignments = \ - license_assignment_manager.QueryAssignedLicenses(entity_id) + assignments = license_assignment_manager.QueryAssignedLicenses(entity_id) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1685,26 +1796,37 @@ def get_assigned_licenses(service_instance, entity_ref=None, entity_name=None, log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - if entity_type == 'uuid' and len(assignments) > 1: - log.trace('Unexpectectedly retrieved more than one' - ' VCenter license assignment.') + if entity_type == "uuid" and len(assignments) > 1: + log.trace( + "Unexpectectedly retrieved more than one" " VCenter license assignment." + ) raise salt.exceptions.VMwareObjectRetrievalError( - 'Unexpected return. Expect only a single assignment') + "Unexpected return. Expect only a single assignment" + ) if check_name: if entity_name != assignments[0].entityDisplayName: - log.trace('Getting license info for wrong vcenter: %s != %s', - entity_name, assignments[0].entityDisplayName) + log.trace( + "Getting license info for wrong vcenter: %s != %s", + entity_name, + assignments[0].entityDisplayName, + ) raise salt.exceptions.VMwareObjectRetrievalError( - 'Got license assignment info for a different vcenter') + "Got license assignment info for a different vcenter" + ) return [a.assignedLicense for a in assignments] -def assign_license(service_instance, license_key, license_name, - entity_ref=None, entity_name=None, - license_assignment_manager=None): - ''' +def assign_license( + service_instance, + license_key, + license_name, + entity_ref=None, + entity_name=None, + license_assignment_manager=None, +): + """ Assigns a license to an entity. service_instance @@ -1729,10 +1851,9 @@ def assign_license(service_instance, license_key, license_name, The LicenseAssignmentManager object of the service instance. If not provided it will be retrieved Default is None. - ''' + """ if not license_assignment_manager: - license_assignment_manager = \ - get_license_assignment_manager(service_instance) + license_assignment_manager = get_license_assignment_manager(service_instance) entity_id = None if not entity_ref: @@ -1742,29 +1863,29 @@ def assign_license(service_instance, license_key, license_name, except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " + "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: raise salt.exceptions.VMwareRuntimeError(exc.msg) if not entity_name: - entity_name = 'vCenter' + entity_name = "vCenter" else: # e.g. vsan cluster or host entity_id = entity_ref._moId - log.trace('Assigning license to \'%s\'', entity_name) + log.trace("Assigning license to '%s'", entity_name) try: vmware_license = license_assignment_manager.UpdateAssignedLicense( - entity_id, - license_key, - license_name) + entity_id, license_key, license_name + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1775,18 +1896,17 @@ def assign_license(service_instance, license_key, license_name, def list_datacenters(service_instance): - ''' + """ Returns a list of datacenters associated with a given service instance. service_instance The Service Instance Object from which to obtain datacenters. - ''' + """ return list_objects(service_instance, vim.Datacenter) -def get_datacenters(service_instance, datacenter_names=None, - get_all_datacenters=False): - ''' +def get_datacenters(service_instance, datacenter_names=None, get_all_datacenters=False): + """ Returns all datacenters in a vCenter. service_instance @@ -1798,18 +1918,19 @@ def get_datacenters(service_instance, datacenter_names=None, get_all_datacenters Flag specifying whether to retrieve all datacenters. Default value is None. - ''' - items = [i['object'] for i in - get_mors_with_properties(service_instance, - vim.Datacenter, - property_list=['name']) - if get_all_datacenters or - (datacenter_names and i['name'] in datacenter_names)] + """ + items = [ + i["object"] + for i in get_mors_with_properties( + service_instance, vim.Datacenter, property_list=["name"] + ) + if get_all_datacenters or (datacenter_names and i["name"] in datacenter_names) + ] return items def get_datacenter(service_instance, datacenter_name): - ''' + """ Returns a vim.Datacenter managed object. service_instance @@ -1817,17 +1938,17 @@ def get_datacenter(service_instance, datacenter_name): datacenter_name The datacenter name - ''' - items = get_datacenters(service_instance, - datacenter_names=[datacenter_name]) + """ + items = get_datacenters(service_instance, datacenter_names=[datacenter_name]) if not items: raise salt.exceptions.VMwareObjectRetrievalError( - 'Datacenter \'{0}\' was not found'.format(datacenter_name)) + "Datacenter '{0}' was not found".format(datacenter_name) + ) return items[0] def create_datacenter(service_instance, datacenter_name): - ''' + """ Creates a datacenter. .. versionadded:: 2017.7.0 @@ -1837,16 +1958,16 @@ def create_datacenter(service_instance, datacenter_name): datacenter_name The datacenter name - ''' + """ root_folder = get_root_folder(service_instance) - log.trace('Creating datacenter \'%s\'', datacenter_name) + log.trace("Creating datacenter '%s'", datacenter_name) try: dc_obj = root_folder.CreateDatacenter(datacenter_name) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1857,7 +1978,7 @@ def create_datacenter(service_instance, datacenter_name): def get_cluster(dc_ref, cluster): - ''' + """ Returns a cluster in a datacenter. dc_ref @@ -1865,35 +1986,41 @@ def get_cluster(dc_ref, cluster): cluster The cluster to be retrieved - ''' + """ dc_name = get_managed_object_name(dc_ref) - log.trace('Retrieving cluster \'%s\' from datacenter \'%s\'', - cluster, dc_name) + log.trace("Retrieving cluster '%s' from datacenter '%s'", cluster, dc_name) si = get_service_instance_from_managed_object(dc_ref, name=dc_name) traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='hostFolder', + path="hostFolder", skip=True, type=vim.Datacenter, - selectSet=[vmodl.query.PropertyCollector.TraversalSpec( - path='childEntity', - skip=False, - type=vim.Folder)]) - items = [i['object'] for i in - get_mors_with_properties(si, - vim.ClusterComputeResource, - container_ref=dc_ref, - property_list=['name'], - traversal_spec=traversal_spec) - if i['name'] == cluster] + selectSet=[ + vmodl.query.PropertyCollector.TraversalSpec( + path="childEntity", skip=False, type=vim.Folder + ) + ], + ) + items = [ + i["object"] + for i in get_mors_with_properties( + si, + vim.ClusterComputeResource, + container_ref=dc_ref, + property_list=["name"], + traversal_spec=traversal_spec, + ) + if i["name"] == cluster + ] if not items: raise salt.exceptions.VMwareObjectRetrievalError( - 'Cluster \'{0}\' was not found in datacenter ' - '\'{1}\''. format(cluster, dc_name)) + "Cluster '{0}' was not found in datacenter " + "'{1}'".format(cluster, dc_name) + ) return items[0] def create_cluster(dc_ref, cluster_name, cluster_spec): - ''' + """ Creates a cluster in a datacenter. dc_ref @@ -1905,17 +2032,16 @@ def create_cluster(dc_ref, cluster_name, cluster_spec): cluster_spec The cluster spec (vim.ClusterConfigSpecEx). Defaults to None. - ''' + """ dc_name = get_managed_object_name(dc_ref) - log.trace('Creating cluster \'%s\' in datacenter \'%s\'', - cluster_name, dc_name) + log.trace("Creating cluster '%s' in datacenter '%s'", cluster_name, dc_name) try: dc_ref.hostFolder.CreateClusterEx(cluster_name, cluster_spec) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -1925,7 +2051,7 @@ def create_cluster(dc_ref, cluster_name, cluster_spec): def update_cluster(cluster_ref, cluster_spec): - ''' + """ Updates a cluster in a datacenter. cluster_ref @@ -1934,58 +2060,59 @@ def update_cluster(cluster_ref, cluster_spec): cluster_spec The cluster spec (vim.ClusterConfigSpecEx). Defaults to None. - ''' + """ cluster_name = get_managed_object_name(cluster_ref) - log.trace('Updating cluster \'%s\'', cluster_name) + log.trace("Updating cluster '%s'", cluster_name) try: - task = cluster_ref.ReconfigureComputeResource_Task(cluster_spec, - modify=True) + task = cluster_ref.ReconfigureComputeResource_Task(cluster_spec, modify=True) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - wait_for_task(task, cluster_name, 'ClusterUpdateTask') + wait_for_task(task, cluster_name, "ClusterUpdateTask") def list_clusters(service_instance): - ''' + """ Returns a list of clusters associated with a given service instance. service_instance The Service Instance Object from which to obtain clusters. - ''' + """ return list_objects(service_instance, vim.ClusterComputeResource) def list_datastore_clusters(service_instance): - ''' + """ Returns a list of datastore clusters associated with a given service instance. service_instance The Service Instance Object from which to obtain datastore clusters. - ''' + """ return list_objects(service_instance, vim.StoragePod) def list_datastores(service_instance): - ''' + """ Returns a list of datastores associated with a given service instance. service_instance The Service Instance Object from which to obtain datastores. - ''' + """ return list_objects(service_instance, vim.Datastore) -def get_datastore_files(service_instance, directory, datastores, container_object, browser_spec): - ''' +def get_datastore_files( + service_instance, directory, datastores, container_object, browser_spec +): + """ Get the files with a given browser specification from the datastore. service_instance @@ -2005,19 +2132,24 @@ def get_datastore_files(service_instance, directory, datastores, container_objec return list of vim.host.DatastoreBrowser.SearchResults objects - ''' + """ files = [] - datastore_objects = get_datastores(service_instance, container_object, datastore_names=datastores) + datastore_objects = get_datastores( + service_instance, container_object, datastore_names=datastores + ) for datobj in datastore_objects: try: - task = datobj.browser.SearchDatastore_Task(datastorePath='[{}] {}'.format(datobj.name, directory), - searchSpec=browser_spec) + task = datobj.browser.SearchDatastore_Task( + datastorePath="[{}] {}".format(datobj.name, directory), + searchSpec=browser_spec, + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " + "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -2025,15 +2157,24 @@ def get_datastore_files(service_instance, directory, datastores, container_objec log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) try: - files.append(salt.utils.vmware.wait_for_task(task, directory, 'query virtual machine files')) + files.append( + salt.utils.vmware.wait_for_task( + task, directory, "query virtual machine files" + ) + ) except salt.exceptions.VMwareFileNotFoundError: pass return files -def get_datastores(service_instance, reference, datastore_names=None, - backing_disk_ids=None, get_all_datastores=False): - ''' +def get_datastores( + service_instance, + reference, + datastore_names=None, + backing_disk_ids=None, + get_all_datastores=False, +): + """ Returns a list of vim.Datastore objects representing the datastores visible from a VMware object, filtered by their names, or the backing disk cannonical name or scsi_addresses @@ -2055,114 +2196,133 @@ def get_datastores(service_instance, reference, datastore_names=None, get_all_datastores Specifies whether to retrieve all disks in the host. Default value is False. - ''' + """ obj_name = get_managed_object_name(reference) if get_all_datastores: - log.trace('Retrieving all datastores visible to \'%s\'', obj_name) + log.trace("Retrieving all datastores visible to '%s'", obj_name) else: - log.trace('Retrieving datastores visible to \'%s\': names = (%s); ' - 'backing disk ids = (%s)', - obj_name, datastore_names, backing_disk_ids) + log.trace( + "Retrieving datastores visible to '%s': names = (%s); " + "backing disk ids = (%s)", + obj_name, + datastore_names, + backing_disk_ids, + ) if backing_disk_ids and not isinstance(reference, vim.HostSystem): raise salt.exceptions.ArgumentValueError( - 'Unsupported reference type \'{0}\' when backing disk filter ' - 'is set'.format(reference.__class__.__name__)) + "Unsupported reference type '{0}' when backing disk filter " + "is set".format(reference.__class__.__name__) + ) if (not get_all_datastores) and backing_disk_ids: # At this point we know the reference is a vim.HostSystem - log.trace('Filtering datastores with backing disk ids: %s', - backing_disk_ids) - storage_system = get_storage_system(service_instance, reference, - obj_name) + log.trace("Filtering datastores with backing disk ids: %s", backing_disk_ids) + storage_system = get_storage_system(service_instance, reference, obj_name) props = salt.utils.vmware.get_properties_of_managed_object( - storage_system, ['fileSystemVolumeInfo.mountInfo']) - mount_infos = props.get('fileSystemVolumeInfo.mountInfo', []) + storage_system, ["fileSystemVolumeInfo.mountInfo"] + ) + mount_infos = props.get("fileSystemVolumeInfo.mountInfo", []) disk_datastores = [] # Non vmfs volumes aren't backed by a disk - for vol in [i.volume for i in mount_infos if - isinstance(i.volume, vim.HostVmfsVolume)]: + for vol in [ + i.volume for i in mount_infos if isinstance(i.volume, vim.HostVmfsVolume) + ]: if not [e for e in vol.extent if e.diskName in backing_disk_ids]: # Skip volume if it doesn't contain an extent with a # canonical name of interest continue - log.trace('Found datastore \'%s\' for disk id(s) \'%s\'', - vol.name, [e.diskName for e in vol.extent]) + log.trace( + "Found datastore '%s' for disk id(s) '%s'", + vol.name, + [e.diskName for e in vol.extent], + ) disk_datastores.append(vol.name) - log.trace('Datastore found for disk filter: %s', disk_datastores) + log.trace("Datastore found for disk filter: %s", disk_datastores) if datastore_names: datastore_names.extend(disk_datastores) else: datastore_names = disk_datastores if (not get_all_datastores) and (not datastore_names): - log.trace('No datastore to be filtered after retrieving the datastores ' - 'backed by the disk id(s) \'%s\'', backing_disk_ids) + log.trace( + "No datastore to be filtered after retrieving the datastores " + "backed by the disk id(s) '%s'", + backing_disk_ids, + ) return [] - log.trace('datastore_names = %s', datastore_names) + log.trace("datastore_names = %s", datastore_names) # Use the default traversal spec if isinstance(reference, vim.HostSystem): # Create a different traversal spec for hosts because it looks like the # default doesn't retrieve the datastores traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - name='host_datastore_traversal', - path='datastore', + name="host_datastore_traversal", + path="datastore", skip=False, - type=vim.HostSystem) + type=vim.HostSystem, + ) elif isinstance(reference, vim.ClusterComputeResource): # Traversal spec for clusters traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - name='cluster_datastore_traversal', - path='datastore', + name="cluster_datastore_traversal", + path="datastore", skip=False, - type=vim.ClusterComputeResource) + type=vim.ClusterComputeResource, + ) elif isinstance(reference, vim.Datacenter): # Traversal spec for datacenter traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - name='datacenter_datastore_traversal', - path='datastore', + name="datacenter_datastore_traversal", + path="datastore", skip=False, - type=vim.Datacenter) + type=vim.Datacenter, + ) elif isinstance(reference, vim.StoragePod): # Traversal spec for datastore clusters traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - name='datastore_cluster_traversal', - path='childEntity', + name="datastore_cluster_traversal", + path="childEntity", skip=False, - type=vim.StoragePod) - elif isinstance(reference, vim.Folder) and \ - get_managed_object_name(reference) == 'Datacenters': + type=vim.StoragePod, + ) + elif ( + isinstance(reference, vim.Folder) + and get_managed_object_name(reference) == "Datacenters" + ): # Traversal of root folder (doesn't support multiple levels of Folders) traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='childEntity', + path="childEntity", selectSet=[ vmodl.query.PropertyCollector.TraversalSpec( - path='datastore', - skip=False, - type=vim.Datacenter)], + path="datastore", skip=False, type=vim.Datacenter + ) + ], skip=False, - type=vim.Folder) + type=vim.Folder, + ) else: raise salt.exceptions.ArgumentValueError( - 'Unsupported reference type \'{0}\'' - ''.format(reference.__class__.__name__)) + "Unsupported reference type '{0}'" "".format(reference.__class__.__name__) + ) - items = get_mors_with_properties(service_instance, - object_type=vim.Datastore, - property_list=['name'], - container_ref=reference, - traversal_spec=traversal_spec) - log.trace('Retrieved %s datastores', len(items)) - items = [i for i in items if get_all_datastores or i['name'] in - datastore_names] - log.trace('Filtered datastores: %s', [i['name'] for i in items]) - return [i['object'] for i in items] + items = get_mors_with_properties( + service_instance, + object_type=vim.Datastore, + property_list=["name"], + container_ref=reference, + traversal_spec=traversal_spec, + ) + log.trace("Retrieved %s datastores", len(items)) + items = [i for i in items if get_all_datastores or i["name"] in datastore_names] + log.trace("Filtered datastores: %s", [i["name"] for i in items]) + return [i["object"] for i in items] def rename_datastore(datastore_ref, new_datastore_name): - ''' + """ Renames a datastore datastore_ref @@ -2170,7 +2330,7 @@ def rename_datastore(datastore_ref, new_datastore_name): new_datastore_name New datastore name - ''' + """ ds_name = get_managed_object_name(datastore_ref) log.trace("Renaming datastore '%s' to '%s'", ds_name, new_datastore_name) try: @@ -2178,8 +2338,8 @@ def rename_datastore(datastore_ref, new_datastore_name): except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -2189,120 +2349,128 @@ def rename_datastore(datastore_ref, new_datastore_name): def get_storage_system(service_instance, host_ref, hostname=None): - ''' + """ Returns a host's storage system - ''' + """ if not hostname: hostname = get_managed_object_name(host_ref) traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='configManager.storageSystem', - type=vim.HostSystem, - skip=False) - objs = get_mors_with_properties(service_instance, - vim.HostStorageSystem, - property_list=['systemFile'], - container_ref=host_ref, - traversal_spec=traversal_spec) + path="configManager.storageSystem", type=vim.HostSystem, skip=False + ) + objs = get_mors_with_properties( + service_instance, + vim.HostStorageSystem, + property_list=["systemFile"], + container_ref=host_ref, + traversal_spec=traversal_spec, + ) if not objs: raise salt.exceptions.VMwareObjectRetrievalError( - 'Host\'s \'{0}\' storage system was not retrieved' - ''.format(hostname)) - log.trace('[%s] Retrieved storage system', hostname) - return objs[0]['object'] + "Host's '{0}' storage system was not retrieved" "".format(hostname) + ) + log.trace("[%s] Retrieved storage system", hostname) + return objs[0]["object"] def _get_partition_info(storage_system, device_path): - ''' + """ Returns partition informations for a device path, of type vim.HostDiskPartitionInfo - ''' + """ try: - partition_infos = \ - storage_system.RetrieveDiskPartitionInfo( - devicePath=[device_path]) + partition_infos = storage_system.RetrieveDiskPartitionInfo( + devicePath=[device_path] + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - log.trace('partition_info = %s', partition_infos[0]) + log.trace("partition_info = %s", partition_infos[0]) return partition_infos[0] -def _get_new_computed_partition_spec(storage_system, - device_path, - partition_info): - ''' +def _get_new_computed_partition_spec(storage_system, device_path, partition_info): + """ Computes the new disk partition info when adding a new vmfs partition that uses up the remainder of the disk; returns a tuple (new_partition_number, vim.HostDiskPartitionSpec - ''' - log.trace('Adding a partition at the end of the disk and getting the new ' - 'computed partition spec') + """ + log.trace( + "Adding a partition at the end of the disk and getting the new " + "computed partition spec" + ) # TODO implement support for multiple partitions # We support adding a partition add the end of the disk with partitions - free_partitions = [p for p in partition_info.layout.partition - if p.type == 'none'] + free_partitions = [p for p in partition_info.layout.partition if p.type == "none"] if not free_partitions: raise salt.exceptions.VMwareObjectNotFoundError( - 'Free partition was not found on device \'{0}\'' - ''.format(partition_info.deviceName)) + "Free partition was not found on device '{0}'" + "".format(partition_info.deviceName) + ) free_partition = free_partitions[0] # Create a layout object that copies the existing one layout = vim.HostDiskPartitionLayout( - total=partition_info.layout.total, - partition=partition_info.layout.partition) + total=partition_info.layout.total, partition=partition_info.layout.partition + ) # Create a partition with the free space on the disk # Change the free partition type to vmfs - free_partition.type = 'vmfs' + free_partition.type = "vmfs" try: computed_partition_info = storage_system.ComputeDiskPartitionInfo( devicePath=device_path, partitionFormat=vim.HostDiskPartitionInfoPartitionFormat.gpt, - layout=layout) + layout=layout, + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - log.trace('computed partition info = {0}', computed_partition_info) - log.trace('Retrieving new partition number') - partition_numbers = [p.partition for p in - computed_partition_info.layout.partition - if (p.start.block == free_partition.start.block or - # XXX If the entire disk is free (i.e. the free - # disk partition starts at block 0) the newily - # created partition is created from block 1 - (free_partition.start.block == 0 and - p.start.block == 1)) and - p.end.block == free_partition.end.block and - p.type == 'vmfs'] + log.trace("computed partition info = {0}", computed_partition_info) + log.trace("Retrieving new partition number") + partition_numbers = [ + p.partition + for p in computed_partition_info.layout.partition + if ( + p.start.block == free_partition.start.block + or + # XXX If the entire disk is free (i.e. the free + # disk partition starts at block 0) the newily + # created partition is created from block 1 + (free_partition.start.block == 0 and p.start.block == 1) + ) + and p.end.block == free_partition.end.block + and p.type == "vmfs" + ] if not partition_numbers: raise salt.exceptions.VMwareNotFoundError( - 'New partition was not found in computed partitions of device ' - '\'{0}\''.format(partition_info.deviceName)) - log.trace('new partition number = %s', partition_numbers[0]) + "New partition was not found in computed partitions of device " + "'{0}'".format(partition_info.deviceName) + ) + log.trace("new partition number = %s", partition_numbers[0]) return (partition_numbers[0], computed_partition_info.spec) -def create_vmfs_datastore(host_ref, datastore_name, disk_ref, - vmfs_major_version, storage_system=None): - ''' +def create_vmfs_datastore( + host_ref, datastore_name, disk_ref, vmfs_major_version, storage_system=None +): + """ Creates a VMFS datastore from a disk_id host_ref @@ -2316,54 +2484,57 @@ def create_vmfs_datastore(host_ref, datastore_name, disk_ref, vmfs_major_version VMFS major version to use - ''' + """ # TODO Support variable sized partitions hostname = get_managed_object_name(host_ref) disk_id = disk_ref.canonicalName - log.debug('Creating datastore \'%s\' on host \'%s\', scsi disk \'%s\', ' - 'vmfs v%s', datastore_name, hostname, disk_id, vmfs_major_version) + log.debug( + "Creating datastore '%s' on host '%s', scsi disk '%s', " "vmfs v%s", + datastore_name, + hostname, + disk_id, + vmfs_major_version, + ) if not storage_system: si = get_service_instance_from_managed_object(host_ref, name=hostname) storage_system = get_storage_system(si, host_ref, hostname) target_disk = disk_ref - partition_info = _get_partition_info(storage_system, - target_disk.devicePath) - log.trace('partition_info = %s', partition_info) + partition_info = _get_partition_info(storage_system, target_disk.devicePath) + log.trace("partition_info = %s", partition_info) new_partition_number, partition_spec = _get_new_computed_partition_spec( - storage_system, - target_disk.devicePath, - partition_info + storage_system, target_disk.devicePath, partition_info ) spec = vim.VmfsDatastoreCreateSpec( vmfs=vim.HostVmfsSpec( majorVersion=vmfs_major_version, volumeName=datastore_name, extent=vim.HostScsiDiskPartition( - diskName=disk_id, - partition=new_partition_number)), + diskName=disk_id, partition=new_partition_number + ), + ), diskUuid=target_disk.uuid, - partition=partition_spec) + partition=partition_spec, + ) try: - ds_ref = \ - host_ref.configManager.datastoreSystem.CreateVmfsDatastore(spec) + ds_ref = host_ref.configManager.datastoreSystem.CreateVmfsDatastore(spec) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - log.debug('Created datastore \'%s\' on host \'%s\'', datastore_name, hostname) + log.debug("Created datastore '%s' on host '%s'", datastore_name, hostname) return ds_ref def get_host_datastore_system(host_ref, hostname=None): - ''' + """ Returns a host's datastore system host_ref @@ -2371,30 +2542,31 @@ def get_host_datastore_system(host_ref, hostname=None): hostname Name of the host. This argument is optional. - ''' + """ if not hostname: hostname = get_managed_object_name(host_ref) service_instance = get_service_instance_from_managed_object(host_ref) traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='configManager.datastoreSystem', - type=vim.HostSystem, - skip=False) - objs = get_mors_with_properties(service_instance, - vim.HostDatastoreSystem, - property_list=['datastore'], - container_ref=host_ref, - traversal_spec=traversal_spec) + path="configManager.datastoreSystem", type=vim.HostSystem, skip=False + ) + objs = get_mors_with_properties( + service_instance, + vim.HostDatastoreSystem, + property_list=["datastore"], + container_ref=host_ref, + traversal_spec=traversal_spec, + ) if not objs: raise salt.exceptions.VMwareObjectRetrievalError( - 'Host\'s \'{0}\' datastore system was not retrieved' - ''.format(hostname)) - log.trace('[%s] Retrieved datastore system', hostname) - return objs[0]['object'] + "Host's '{0}' datastore system was not retrieved" "".format(hostname) + ) + log.trace("[%s] Retrieved datastore system", hostname) + return objs[0]["object"] def remove_datastore(service_instance, datastore_ref): - ''' + """ Creates a VMFS datastore from a disk_id service_instance @@ -2402,38 +2574,42 @@ def remove_datastore(service_instance, datastore_ref): datastore_ref The reference to the datastore to remove - ''' - ds_props = get_properties_of_managed_object( - datastore_ref, ['host', 'info', 'name']) - ds_name = ds_props['name'] - log.debug('Removing datastore \'%s\'', ds_name) - ds_hosts = ds_props.get('host') + """ + ds_props = get_properties_of_managed_object(datastore_ref, ["host", "info", "name"]) + ds_name = ds_props["name"] + log.debug("Removing datastore '%s'", ds_name) + ds_hosts = ds_props.get("host") if not ds_hosts: raise salt.exceptions.VMwareApiError( - 'Datastore \'{0}\' can\'t be removed. No ' - 'attached hosts found'.format(ds_name)) + "Datastore '{0}' can't be removed. No " + "attached hosts found".format(ds_name) + ) hostname = get_managed_object_name(ds_hosts[0].key) - host_ds_system = get_host_datastore_system(ds_hosts[0].key, - hostname=hostname) + host_ds_system = get_host_datastore_system(ds_hosts[0].key, hostname=hostname) try: host_ds_system.RemoveDatastore(datastore_ref) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - log.trace('[%s] Removed datastore \'%s\'', hostname, ds_name) + log.trace("[%s] Removed datastore '%s'", hostname, ds_name) -def get_hosts(service_instance, datacenter_name=None, host_names=None, - cluster_name=None, get_all_hosts=False): - ''' +def get_hosts( + service_instance, + datacenter_name=None, + host_names=None, + cluster_name=None, + get_all_hosts=False, +): + """ Returns a list of vim.HostSystem objects representing ESXi hosts in a vcenter filtered by their names and/or datacenter, cluster membership. @@ -2453,11 +2629,12 @@ def get_hosts(service_instance, datacenter_name=None, host_names=None, get_all_hosts Specifies whether to retrieve all hosts in the container. Default value is False. - ''' - properties = ['name'] + """ + properties = ["name"] if cluster_name and not datacenter_name: raise salt.exceptions.ArgumentValueError( - 'Must specify the datacenter when specifying the cluster') + "Must specify the datacenter when specifying the cluster" + ) if not host_names: host_names = [] if not datacenter_name: @@ -2468,40 +2645,41 @@ def get_hosts(service_instance, datacenter_name=None, host_names=None, if cluster_name: # Retrieval to test if cluster exists. Cluster existence only makes # sense if the datacenter has been specified - properties.append('parent') + properties.append("parent") # Search for the objects - hosts = get_mors_with_properties(service_instance, - vim.HostSystem, - container_ref=start_point, - property_list=properties) - log.trace('Retrieved hosts: %s', [h['name'] for h in hosts]) + hosts = get_mors_with_properties( + service_instance, + vim.HostSystem, + container_ref=start_point, + property_list=properties, + ) + log.trace("Retrieved hosts: %s", [h["name"] for h in hosts]) filtered_hosts = [] for h in hosts: # Complex conditions checking if a host should be added to the # filtered list (either due to its name and/or cluster membership) if cluster_name: - if not isinstance(h['parent'], vim.ClusterComputeResource): + if not isinstance(h["parent"], vim.ClusterComputeResource): continue - parent_name = get_managed_object_name(h['parent']) + parent_name = get_managed_object_name(h["parent"]) if parent_name != cluster_name: continue if get_all_hosts: - filtered_hosts.append(h['object']) + filtered_hosts.append(h["object"]) continue - if h['name'] in host_names: - filtered_hosts.append(h['object']) + if h["name"] in host_names: + filtered_hosts.append(h["object"]) return filtered_hosts -def _get_scsi_address_to_lun_key_map(service_instance, - host_ref, - storage_system=None, - hostname=None): - ''' +def _get_scsi_address_to_lun_key_map( + service_instance, host_ref, storage_system=None, hostname=None +): + """ Returns a map between the scsi addresses and the keys of all luns on an ESXi host. map[<scsi_address>] = <lun key> @@ -2518,19 +2696,18 @@ def _get_scsi_address_to_lun_key_map(service_instance, hostname Name of the host. Default is None. - ''' + """ if not hostname: hostname = get_managed_object_name(host_ref) if not storage_system: - storage_system = get_storage_system(service_instance, host_ref, - hostname) + storage_system = get_storage_system(service_instance, host_ref, hostname) try: device_info = storage_system.storageDeviceInfo except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -2539,29 +2716,30 @@ def _get_scsi_address_to_lun_key_map(service_instance, raise salt.exceptions.VMwareRuntimeError(exc.msg) if not device_info: raise salt.exceptions.VMwareObjectRetrievalError( - 'Host\'s \'{0}\' storage device ' - 'info was not retrieved'.format(hostname)) + "Host's '{0}' storage device " "info was not retrieved".format(hostname) + ) multipath_info = device_info.multipathInfo if not multipath_info: raise salt.exceptions.VMwareObjectRetrievalError( - 'Host\'s \'{0}\' multipath info was not retrieved' - ''.format(hostname)) + "Host's '{0}' multipath info was not retrieved" "".format(hostname) + ) if multipath_info.lun is None: raise salt.exceptions.VMwareObjectRetrievalError( - 'No luns were retrieved from host \'{0}\''.format(hostname)) + "No luns were retrieved from host '{0}'".format(hostname) + ) lun_key_by_scsi_addr = {} for l in multipath_info.lun: # The vmware scsi_address may have multiple comma separated values # The first one is the actual scsi address - lun_key_by_scsi_addr.update({p.name.split(',')[0]: l.lun - for p in l.path}) - log.trace('Scsi address to lun id map on host \'%s\': %s', - hostname, lun_key_by_scsi_addr) + lun_key_by_scsi_addr.update({p.name.split(",")[0]: l.lun for p in l.path}) + log.trace( + "Scsi address to lun id map on host '%s': %s", hostname, lun_key_by_scsi_addr + ) return lun_key_by_scsi_addr def get_all_luns(host_ref, storage_system=None, hostname=None): - ''' + """ Returns a list of all vim.HostScsiDisk objects in a disk host_ref @@ -2573,7 +2751,7 @@ def get_all_luns(host_ref, storage_system=None, hostname=None): hostname Name of the host. This argument is optional. - ''' + """ if not hostname: hostname = get_managed_object_name(host_ref) if not storage_system: @@ -2581,15 +2759,15 @@ def get_all_luns(host_ref, storage_system=None, hostname=None): storage_system = get_storage_system(si, host_ref, hostname) if not storage_system: raise salt.exceptions.VMwareObjectRetrievalError( - 'Host\'s \'{0}\' storage system was not retrieved' - ''.format(hostname)) + "Host's '{0}' storage system was not retrieved" "".format(hostname) + ) try: device_info = storage_system.storageDeviceInfo except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -2598,20 +2776,23 @@ def get_all_luns(host_ref, storage_system=None, hostname=None): raise salt.exceptions.VMwareRuntimeError(exc.msg) if not device_info: raise salt.exceptions.VMwareObjectRetrievalError( - 'Host\'s \'{0}\' storage device info was not retrieved' - ''.format(hostname)) + "Host's '{0}' storage device info was not retrieved" "".format(hostname) + ) scsi_luns = device_info.scsiLun if scsi_luns: - log.trace('Retrieved scsi luns in host \'%s\': %s', - hostname, [l.canonicalName for l in scsi_luns]) + log.trace( + "Retrieved scsi luns in host '%s': %s", + hostname, + [l.canonicalName for l in scsi_luns], + ) return scsi_luns - log.trace('Retrieved no scsi_luns in host \'%s\'', hostname) + log.trace("Retrieved no scsi_luns in host '%s'", hostname) return [] def get_scsi_address_to_lun_map(host_ref, storage_system=None, hostname=None): - ''' + """ Returns a map of all vim.ScsiLun objects on a ESXi host keyed by their scsi address @@ -2624,24 +2805,26 @@ def get_scsi_address_to_lun_map(host_ref, storage_system=None, hostname=None): hostname Name of the host. This argument is optional. - ''' + """ if not hostname: hostname = get_managed_object_name(host_ref) si = get_service_instance_from_managed_object(host_ref, name=hostname) if not storage_system: storage_system = get_storage_system(si, host_ref, hostname) - lun_ids_to_scsi_addr_map = \ - _get_scsi_address_to_lun_key_map(si, host_ref, storage_system, - hostname) - luns_to_key_map = {d.key: d for d in - get_all_luns(host_ref, storage_system, hostname)} - return {scsi_addr: luns_to_key_map[lun_key] for scsi_addr, lun_key in - six.iteritems(lun_ids_to_scsi_addr_map)} + lun_ids_to_scsi_addr_map = _get_scsi_address_to_lun_key_map( + si, host_ref, storage_system, hostname + ) + luns_to_key_map = { + d.key: d for d in get_all_luns(host_ref, storage_system, hostname) + } + return { + scsi_addr: luns_to_key_map[lun_key] + for scsi_addr, lun_key in six.iteritems(lun_ids_to_scsi_addr_map) + } -def get_disks(host_ref, disk_ids=None, scsi_addresses=None, - get_all_disks=False): - ''' +def get_disks(host_ref, disk_ids=None, scsi_addresses=None, get_all_disks=False): + """ Returns a list of vim.HostScsiDisk objects representing disks in a ESXi host, filtered by their cannonical names and scsi_addresses @@ -2660,13 +2843,17 @@ def get_disks(host_ref, disk_ids=None, scsi_addresses=None, get_all_disks Specifies whether to retrieve all disks in the host. Default value is False. - ''' + """ hostname = get_managed_object_name(host_ref) if get_all_disks: - log.trace('Retrieving all disks in host \'%s\'', hostname) + log.trace("Retrieving all disks in host '%s'", hostname) else: - log.trace('Retrieving disks in host \'%s\': ids = (%s); scsi ' - 'addresses = (%s)', hostname, disk_ids, scsi_addresses) + log.trace( + "Retrieving disks in host '%s': ids = (%s); scsi " "addresses = (%s)", + hostname, + disk_ids, + scsi_addresses, + ) if not (disk_ids or scsi_addresses): return [] si = get_service_instance_from_managed_object(host_ref, name=hostname) @@ -2674,29 +2861,41 @@ def get_disks(host_ref, disk_ids=None, scsi_addresses=None, disk_keys = [] if scsi_addresses: # convert the scsi addresses to disk keys - lun_key_by_scsi_addr = _get_scsi_address_to_lun_key_map(si, host_ref, - storage_system, - hostname) - disk_keys = [key for scsi_addr, key - in six.iteritems(lun_key_by_scsi_addr) - if scsi_addr in scsi_addresses] - log.trace('disk_keys based on scsi_addresses = %s', disk_keys) + lun_key_by_scsi_addr = _get_scsi_address_to_lun_key_map( + si, host_ref, storage_system, hostname + ) + disk_keys = [ + key + for scsi_addr, key in six.iteritems(lun_key_by_scsi_addr) + if scsi_addr in scsi_addresses + ] + log.trace("disk_keys based on scsi_addresses = %s", disk_keys) scsi_luns = get_all_luns(host_ref, storage_system) - scsi_disks = [disk for disk in scsi_luns - if isinstance(disk, vim.HostScsiDisk) and ( - get_all_disks or - # Filter by canonical name - (disk_ids and (disk.canonicalName in disk_ids)) or - # Filter by disk keys from scsi addresses - (disk.key in disk_keys))] - log.trace('Retrieved disks in host \'%s\': %s', - hostname, [d.canonicalName for d in scsi_disks]) + scsi_disks = [ + disk + for disk in scsi_luns + if isinstance(disk, vim.HostScsiDisk) + and ( + get_all_disks + or + # Filter by canonical name + (disk_ids and (disk.canonicalName in disk_ids)) + or + # Filter by disk keys from scsi addresses + (disk.key in disk_keys) + ) + ] + log.trace( + "Retrieved disks in host '%s': %s", + hostname, + [d.canonicalName for d in scsi_disks], + ) return scsi_disks def get_disk_partition_info(host_ref, disk_id, storage_system=None): - ''' + """ Returns all partitions on a disk host_ref @@ -2707,42 +2906,49 @@ def get_disk_partition_info(host_ref, disk_id, storage_system=None): storage_system The ESXi host's storage system. Default is None. - ''' + """ hostname = get_managed_object_name(host_ref) service_instance = get_service_instance_from_managed_object(host_ref) if not storage_system: - storage_system = get_storage_system(service_instance, host_ref, - hostname) + storage_system = get_storage_system(service_instance, host_ref, hostname) - props = get_properties_of_managed_object(storage_system, - ['storageDeviceInfo.scsiLun']) - if not props.get('storageDeviceInfo.scsiLun'): - raise salt.exceptions.VMwareObjectRetrievalError( - 'No devices were retrieved in host \'{0}\''.format(hostname)) - log.trace( - '[%s] Retrieved %s devices: %s', - hostname, - len(props['storageDeviceInfo.scsiLun']), - ', '.join([l.canonicalName - for l in props['storageDeviceInfo.scsiLun']]) + props = get_properties_of_managed_object( + storage_system, ["storageDeviceInfo.scsiLun"] ) - disks = [l for l in props['storageDeviceInfo.scsiLun'] - if isinstance(l, vim.HostScsiDisk) and - l.canonicalName == disk_id] + if not props.get("storageDeviceInfo.scsiLun"): + raise salt.exceptions.VMwareObjectRetrievalError( + "No devices were retrieved in host '{0}'".format(hostname) + ) + log.trace( + "[%s] Retrieved %s devices: %s", + hostname, + len(props["storageDeviceInfo.scsiLun"]), + ", ".join([l.canonicalName for l in props["storageDeviceInfo.scsiLun"]]), + ) + disks = [ + l + for l in props["storageDeviceInfo.scsiLun"] + if isinstance(l, vim.HostScsiDisk) and l.canonicalName == disk_id + ] if not disks: raise salt.exceptions.VMwareObjectRetrievalError( - 'Disk \'{0}\' was not found in host \'{1}\'' - ''.format(disk_id, hostname)) - log.trace('[%s] device_path = %s', hostname, disks[0].devicePath) + "Disk '{0}' was not found in host '{1}'" "".format(disk_id, hostname) + ) + log.trace("[%s] device_path = %s", hostname, disks[0].devicePath) partition_info = _get_partition_info(storage_system, disks[0].devicePath) - log.trace('[%s] Retrieved %s partition(s) on disk \'%s\'', - hostname, len(partition_info.spec.partition), disk_id) + log.trace( + "[%s] Retrieved %s partition(s) on disk '%s'", + hostname, + len(partition_info.spec.partition), + disk_id, + ) return partition_info -def erase_disk_partitions(service_instance, host_ref, disk_id, - hostname=None, storage_system=None): - ''' +def erase_disk_partitions( + service_instance, host_ref, disk_id, hostname=None, storage_system=None +): + """ Erases all partitions on a disk in a vcenter filtered by their names and/or datacenter, cluster membership @@ -2761,61 +2967,66 @@ def erase_disk_partitions(service_instance, host_ref, disk_id, storage_system The ESXi host's storage system. Default is None. - ''' + """ if not hostname: hostname = get_managed_object_name(host_ref) if not storage_system: - storage_system = get_storage_system(service_instance, host_ref, - hostname) + storage_system = get_storage_system(service_instance, host_ref, hostname) traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='configManager.storageSystem', - type=vim.HostSystem, - skip=False) - results = get_mors_with_properties(service_instance, - vim.HostStorageSystem, - ['storageDeviceInfo.scsiLun'], - container_ref=host_ref, - traversal_spec=traversal_spec) + path="configManager.storageSystem", type=vim.HostSystem, skip=False + ) + results = get_mors_with_properties( + service_instance, + vim.HostStorageSystem, + ["storageDeviceInfo.scsiLun"], + container_ref=host_ref, + traversal_spec=traversal_spec, + ) if not results: raise salt.exceptions.VMwareObjectRetrievalError( - 'Host\'s \'{0}\' devices were not retrieved'.format(hostname)) + "Host's '{0}' devices were not retrieved".format(hostname) + ) log.trace( - '[%s] Retrieved %s devices: %s', + "[%s] Retrieved %s devices: %s", hostname, - len(results[0].get('storageDeviceInfo.scsiLun', [])), - ', '.join([l.canonicalName for l in - results[0].get('storageDeviceInfo.scsiLun', [])]) + len(results[0].get("storageDeviceInfo.scsiLun", [])), + ", ".join( + [l.canonicalName for l in results[0].get("storageDeviceInfo.scsiLun", [])] + ), ) - disks = [l for l in results[0].get('storageDeviceInfo.scsiLun', []) - if isinstance(l, vim.HostScsiDisk) and - l.canonicalName == disk_id] + disks = [ + l + for l in results[0].get("storageDeviceInfo.scsiLun", []) + if isinstance(l, vim.HostScsiDisk) and l.canonicalName == disk_id + ] if not disks: raise salt.exceptions.VMwareObjectRetrievalError( - 'Disk \'{0}\' was not found in host \'{1}\'' - ''.format(disk_id, hostname)) - log.trace('[%s] device_path = %s', hostname, disks[0].devicePath) + "Disk '{0}' was not found in host '{1}'" "".format(disk_id, hostname) + ) + log.trace("[%s] device_path = %s", hostname, disks[0].devicePath) # Erase the partitions by setting an empty partition spec try: - storage_system.UpdateDiskPartitions(disks[0].devicePath, - vim.HostDiskPartitionSpec()) + storage_system.UpdateDiskPartitions( + disks[0].devicePath, vim.HostDiskPartitionSpec() + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - log.trace('[%s] Erased partitions on disk \'%s\'', hostname, disk_id) + log.trace("[%s] Erased partitions on disk '%s'", hostname, disk_id) def get_diskgroups(host_ref, cache_disk_ids=None, get_all_disk_groups=False): - ''' + """ Returns a list of vim.VsanHostDiskMapping objects representing disks in a ESXi host, filtered by their cannonical names. @@ -2832,13 +3043,16 @@ def get_diskgroups(host_ref, cache_disk_ids=None, get_all_disk_groups=False): get_all_disk_groups Specifies whether to retrieve all disks groups in the host. Default value is False. - ''' + """ hostname = get_managed_object_name(host_ref) if get_all_disk_groups: - log.trace('Retrieving all disk groups on host \'%s\'', hostname) + log.trace("Retrieving all disk groups on host '%s'", hostname) else: - log.trace('Retrieving disk groups from host \'%s\', with cache disk ' - 'ids : (%s)', hostname, cache_disk_ids) + log.trace( + "Retrieving disk groups from host '%s', with cache disk " "ids : (%s)", + hostname, + cache_disk_ids, + ) if not cache_disk_ids: return [] try: @@ -2846,8 +3060,8 @@ def get_diskgroups(host_ref, cache_disk_ids=None, get_all_disk_groups=False): except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -2856,47 +3070,52 @@ def get_diskgroups(host_ref, cache_disk_ids=None, get_all_disk_groups=False): raise salt.exceptions.VMwareRuntimeError(exc.msg) if not vsan_host_config: raise salt.exceptions.VMwareObjectRetrievalError( - 'No host config found on host \'{0}\''.format(hostname)) + "No host config found on host '{0}'".format(hostname) + ) vsan_storage_info = vsan_host_config.storageInfo if not vsan_storage_info: raise salt.exceptions.VMwareObjectRetrievalError( - 'No vsan storage info found on host \'{0}\''.format(hostname)) + "No vsan storage info found on host '{0}'".format(hostname) + ) vsan_disk_mappings = vsan_storage_info.diskMapping if not vsan_disk_mappings: return [] - disk_groups = [dm for dm in vsan_disk_mappings if - (get_all_disk_groups or - (dm.ssd.canonicalName in cache_disk_ids))] + disk_groups = [ + dm + for dm in vsan_disk_mappings + if (get_all_disk_groups or (dm.ssd.canonicalName in cache_disk_ids)) + ] log.trace( - 'Retrieved disk groups on host \'%s\', with cache disk ids : %s', - hostname, [d.ssd.canonicalName for d in disk_groups] + "Retrieved disk groups on host '%s', with cache disk ids : %s", + hostname, + [d.ssd.canonicalName for d in disk_groups], ) return disk_groups def _check_disks_in_diskgroup(disk_group, cache_disk_id, capacity_disk_ids): - ''' + """ Checks that the disks in a disk group are as expected and raises CheckError exceptions if the check fails - ''' + """ if not disk_group.ssd.canonicalName == cache_disk_id: raise salt.exceptions.ArgumentValueError( - 'Incorrect diskgroup cache disk; got id: \'{0}\'; expected id: ' - '\'{1}\''.format(disk_group.ssd.canonicalName, cache_disk_id)) + "Incorrect diskgroup cache disk; got id: '{0}'; expected id: " + "'{1}'".format(disk_group.ssd.canonicalName, cache_disk_id) + ) non_ssd_disks = [d.canonicalName for d in disk_group.nonSsd] if sorted(non_ssd_disks) != sorted(capacity_disk_ids): raise salt.exceptions.ArgumentValueError( - 'Incorrect capacity disks; got ids: \'{0}\'; expected ids: \'{1}\'' - ''.format(sorted(non_ssd_disks), - sorted(capacity_disk_ids))) - log.trace('Checked disks in diskgroup with cache disk id \'%s\'', - cache_disk_id) + "Incorrect capacity disks; got ids: '{0}'; expected ids: '{1}'" + "".format(sorted(non_ssd_disks), sorted(capacity_disk_ids)) + ) + log.trace("Checked disks in diskgroup with cache disk id '%s'", cache_disk_id) return True # TODO Support host caches on multiple datastores def get_host_cache(host_ref, host_cache_manager=None): - ''' + """ Returns a vim.HostScsiDisk if the host cache is configured on the specified host, other wise returns None @@ -2908,37 +3127,42 @@ def get_host_cache(host_ref, host_cache_manager=None): The vim.HostCacheConfigurationManager object representing the cache configuration manager on the specified host. Default is None. If None, it will be retrieved in the method - ''' + """ hostname = get_managed_object_name(host_ref) service_instance = get_service_instance_from_managed_object(host_ref) - log.trace('Retrieving the host cache on host \'%s\'', hostname) + log.trace("Retrieving the host cache on host '%s'", hostname) if not host_cache_manager: traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='configManager.cacheConfigurationManager', + path="configManager.cacheConfigurationManager", type=vim.HostSystem, - skip=False) - results = get_mors_with_properties(service_instance, - vim.HostCacheConfigurationManager, - ['cacheConfigurationInfo'], - container_ref=host_ref, - traversal_spec=traversal_spec) - if not results or not results[0].get('cacheConfigurationInfo'): - log.trace('Host \'%s\' has no host cache', hostname) + skip=False, + ) + results = get_mors_with_properties( + service_instance, + vim.HostCacheConfigurationManager, + ["cacheConfigurationInfo"], + container_ref=host_ref, + traversal_spec=traversal_spec, + ) + if not results or not results[0].get("cacheConfigurationInfo"): + log.trace("Host '%s' has no host cache", hostname) return None - return results[0]['cacheConfigurationInfo'][0] + return results[0]["cacheConfigurationInfo"][0] else: - results = get_properties_of_managed_object(host_cache_manager, - ['cacheConfigurationInfo']) + results = get_properties_of_managed_object( + host_cache_manager, ["cacheConfigurationInfo"] + ) if not results: - log.trace('Host \'%s\' has no host cache', hostname) + log.trace("Host '%s' has no host cache", hostname) return None - return results['cacheConfigurationInfo'][0] + return results["cacheConfigurationInfo"][0] # TODO Support host caches on multiple datastores -def configure_host_cache(host_ref, datastore_ref, swap_size_MiB, - host_cache_manager=None): - ''' +def configure_host_cache( + host_ref, datastore_ref, swap_size_MiB, host_cache_manager=None +): + """ Configures the host cahe of the specified host host_ref @@ -2956,53 +3180,63 @@ def configure_host_cache(host_ref, datastore_ref, swap_size_MiB, The vim.HostCacheConfigurationManager object representing the cache configuration manager on the specified host. Default is None. If None, it will be retrieved in the method - ''' + """ hostname = get_managed_object_name(host_ref) if not host_cache_manager: props = get_properties_of_managed_object( - host_ref, ['configManager.cacheConfigurationManager']) - if not props.get('configManager.cacheConfigurationManager'): + host_ref, ["configManager.cacheConfigurationManager"] + ) + if not props.get("configManager.cacheConfigurationManager"): raise salt.exceptions.VMwareObjectRetrievalError( - 'Host \'{0}\' has no host cache'.format(hostname)) - host_cache_manager = props['configManager.cacheConfigurationManager'] - log.trace('Configuring the host cache on host \'%s\', datastore \'%s\', ' - 'swap size=%s MiB', hostname, datastore_ref.name, swap_size_MiB) + "Host '{0}' has no host cache".format(hostname) + ) + host_cache_manager = props["configManager.cacheConfigurationManager"] + log.trace( + "Configuring the host cache on host '%s', datastore '%s', " "swap size=%s MiB", + hostname, + datastore_ref.name, + swap_size_MiB, + ) spec = vim.HostCacheConfigurationSpec( - datastore=datastore_ref, - swapSize=swap_size_MiB) - log.trace('host_cache_spec=%s', spec) + datastore=datastore_ref, swapSize=swap_size_MiB + ) + log.trace("host_cache_spec=%s", spec) try: task = host_cache_manager.ConfigureHostCache_Task(spec) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - wait_for_task(task, hostname, 'HostCacheConfigurationTask') - log.trace('Configured host cache on host \'%s\'', hostname) + wait_for_task(task, hostname, "HostCacheConfigurationTask") + log.trace("Configured host cache on host '%s'", hostname) return True def list_hosts(service_instance): - ''' + """ Returns a list of hosts associated with a given service instance. service_instance The Service Instance Object from which to obtain hosts. - ''' + """ return list_objects(service_instance, vim.HostSystem) -def get_resource_pools(service_instance, resource_pool_names, datacenter_name=None, - get_all_resource_pools=False): - ''' +def get_resource_pools( + service_instance, + resource_pool_names, + datacenter_name=None, + get_all_resource_pools=False, +): + """ Retrieves resource pool objects service_instance @@ -3019,9 +3253,9 @@ def get_resource_pools(service_instance, resource_pool_names, datacenter_name=No return Resourcepool managed object reference - ''' + """ - properties = ['name'] + properties = ["name"] if not resource_pool_names: resource_pool_names = [] if datacenter_name: @@ -3029,96 +3263,100 @@ def get_resource_pools(service_instance, resource_pool_names, datacenter_name=No else: container_ref = get_root_folder(service_instance) - resource_pools = get_mors_with_properties(service_instance, - vim.ResourcePool, - container_ref=container_ref, - property_list=properties) + resource_pools = get_mors_with_properties( + service_instance, + vim.ResourcePool, + container_ref=container_ref, + property_list=properties, + ) selected_pools = [] for pool in resource_pools: - if get_all_resource_pools or (pool['name'] in resource_pool_names): - selected_pools.append(pool['object']) + if get_all_resource_pools or (pool["name"] in resource_pool_names): + selected_pools.append(pool["object"]) if not selected_pools: raise salt.exceptions.VMwareObjectRetrievalError( - 'The resource pools with properties ' - 'names={} get_all={} could not be found'.format(selected_pools, - get_all_resource_pools)) + "The resource pools with properties " + "names={} get_all={} could not be found".format( + selected_pools, get_all_resource_pools + ) + ) return selected_pools def list_resourcepools(service_instance): - ''' + """ Returns a list of resource pools associated with a given service instance. service_instance The Service Instance Object from which to obtain resource pools. - ''' + """ return list_objects(service_instance, vim.ResourcePool) def list_networks(service_instance): - ''' + """ Returns a list of networks associated with a given service instance. service_instance The Service Instance Object from which to obtain networks. - ''' + """ return list_objects(service_instance, vim.Network) def list_vms(service_instance): - ''' + """ Returns a list of VMs associated with a given service instance. service_instance The Service Instance Object from which to obtain VMs. - ''' + """ return list_objects(service_instance, vim.VirtualMachine) def list_folders(service_instance): - ''' + """ Returns a list of folders associated with a given service instance. service_instance The Service Instance Object from which to obtain folders. - ''' + """ return list_objects(service_instance, vim.Folder) def list_dvs(service_instance): - ''' + """ Returns a list of distributed virtual switches associated with a given service instance. service_instance The Service Instance Object from which to obtain distributed virtual switches. - ''' + """ return list_objects(service_instance, vim.DistributedVirtualSwitch) def list_vapps(service_instance): - ''' + """ Returns a list of vApps associated with a given service instance. service_instance The Service Instance Object from which to obtain vApps. - ''' + """ return list_objects(service_instance, vim.VirtualApp) def list_portgroups(service_instance): - ''' + """ Returns a list of distributed virtual portgroups associated with a given service instance. service_instance The Service Instance Object from which to obtain distributed virtual switches. - ''' + """ return list_objects(service_instance, vim.dvs.DistributedVirtualPortgroup) -def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='debug'): - ''' +def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level="debug"): + """ Waits for a task to be completed. task @@ -3138,17 +3376,17 @@ def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='de log_level The level at which to log task information. Default is ``debug``, but ``info`` is also supported. - ''' + """ time_counter = 0 start_time = time.time() - log.trace('task = %s, task_type = %s', task, task.__class__.__name__) + log.trace("task = %s, task_type = %s", task, task.__class__.__name__) try: task_info = task.info except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.FileNotFound as exc: log.exception(exc) raise salt.exceptions.VMwareFileNotFoundError(exc.msg) @@ -3158,11 +3396,12 @@ def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='de except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - while task_info.state == 'running' or task_info.state == 'queued': + while task_info.state == "running" or task_info.state == "queued": if time_counter % sleep_seconds == 0: - msg = '[ {0} ] Waiting for {1} task to finish [{2} s]'.format( - instance_name, task_type, time_counter) - if log_level == 'info': + msg = "[ {0} ] Waiting for {1} task to finish [{2} s]".format( + instance_name, task_type, time_counter + ) + if log_level == "info": log.info(msg) else: log.debug(msg) @@ -3173,8 +3412,9 @@ def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='de except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " + "{}".format(exc.privilegeId) + ) except vim.fault.FileNotFound as exc: log.exception(exc) raise salt.exceptions.VMwareFileNotFoundError(exc.msg) @@ -3184,10 +3424,11 @@ def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='de except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - if task_info.state == 'success': - msg = '[ {0} ] Successfully completed {1} task in {2} seconds'.format( - instance_name, task_type, time_counter) - if log_level == 'info': + if task_info.state == "success": + msg = "[ {0} ] Successfully completed {1} task in {2} seconds".format( + instance_name, task_type, time_counter + ) + if log_level == "info": log.info(msg) else: log.debug(msg) @@ -3200,8 +3441,9 @@ def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='de except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " + "{}".format(exc.privilegeId) + ) except vim.fault.FileNotFound as exc: log.exception(exc) raise salt.exceptions.VMwareFileNotFoundError(exc.msg) @@ -3215,14 +3457,21 @@ def wait_for_task(task, instance_name, task_type, sleep_seconds=1, log_level='de log.exception(exc) exc_message = exc.msg if exc.faultMessage: - exc_message = '{0} ({1})'.format(exc_message, - exc.faultMessage[0].message) + exc_message = "{0} ({1})".format( + exc_message, exc.faultMessage[0].message + ) raise salt.exceptions.VMwareApiError(exc_message) -def get_vm_by_property(service_instance, name, datacenter=None, vm_properties=None, - traversal_spec=None, parent_ref=None): - ''' +def get_vm_by_property( + service_instance, + name, + datacenter=None, + vm_properties=None, + traversal_spec=None, + parent_ref=None, +): + """ Get virtual machine properties based on the traversal specs and properties list, returns Virtual Machine object with properties. @@ -3243,41 +3492,52 @@ def get_vm_by_property(service_instance, name, datacenter=None, vm_properties=No parent_ref Container Reference object for searching under a given object. - ''' + """ if datacenter and not parent_ref: parent_ref = salt.utils.vmware.get_datacenter(service_instance, datacenter) if not vm_properties: - vm_properties = ['name', - 'config.hardware.device', - 'summary.storage.committed', - 'summary.storage.uncommitted', - 'summary.storage.unshared', - 'layoutEx.file', - 'config.guestFullName', - 'config.guestId', - 'guest.net', - 'config.hardware.memoryMB', - 'config.hardware.numCPU', - 'config.files.vmPathName', - 'summary.runtime.powerState', - 'guest.toolsStatus'] - vm_list = salt.utils.vmware.get_mors_with_properties(service_instance, - vim.VirtualMachine, - vm_properties, - container_ref=parent_ref, - traversal_spec=traversal_spec) - vm_formatted = [vm for vm in vm_list if vm['name'] == name] + vm_properties = [ + "name", + "config.hardware.device", + "summary.storage.committed", + "summary.storage.uncommitted", + "summary.storage.unshared", + "layoutEx.file", + "config.guestFullName", + "config.guestId", + "guest.net", + "config.hardware.memoryMB", + "config.hardware.numCPU", + "config.files.vmPathName", + "summary.runtime.powerState", + "guest.toolsStatus", + ] + vm_list = salt.utils.vmware.get_mors_with_properties( + service_instance, + vim.VirtualMachine, + vm_properties, + container_ref=parent_ref, + traversal_spec=traversal_spec, + ) + vm_formatted = [vm for vm in vm_list if vm["name"] == name] if not vm_formatted: - raise salt.exceptions.VMwareObjectRetrievalError('The virtual machine was not found.') + raise salt.exceptions.VMwareObjectRetrievalError( + "The virtual machine was not found." + ) elif len(vm_formatted) > 1: - raise salt.exceptions.VMwareMultipleObjectsError(' '.join([ - 'Multiple virtual machines were found with the' - 'same name, please specify a container.'])) + raise salt.exceptions.VMwareMultipleObjectsError( + " ".join( + [ + "Multiple virtual machines were found with the" + "same name, please specify a container." + ] + ) + ) return vm_formatted[0] def get_folder(service_instance, datacenter, placement, base_vm_name=None): - ''' + """ Returns a Folder Object service_instance @@ -3291,36 +3551,53 @@ def get_folder(service_instance, datacenter, placement, base_vm_name=None): base_vm_name Existing virtual machine name (for cloning) - ''' - log.trace('Retrieving folder information') + """ + log.trace("Retrieving folder information") if base_vm_name: - vm_object = get_vm_by_property(service_instance, base_vm_name, vm_properties=['name']) - vm_props = salt.utils.vmware.get_properties_of_managed_object(vm_object, properties=['parent']) - if 'parent' in vm_props: - folder_object = vm_props['parent'] + vm_object = get_vm_by_property( + service_instance, base_vm_name, vm_properties=["name"] + ) + vm_props = salt.utils.vmware.get_properties_of_managed_object( + vm_object, properties=["parent"] + ) + if "parent" in vm_props: + folder_object = vm_props["parent"] else: - raise salt.exceptions.VMwareObjectRetrievalError(' '.join([ - 'The virtual machine parent', - 'object is not defined'])) - elif 'folder' in placement: - folder_objects = salt.utils.vmware.get_folders(service_instance, [placement['folder']], datacenter) + raise salt.exceptions.VMwareObjectRetrievalError( + " ".join(["The virtual machine parent", "object is not defined"]) + ) + elif "folder" in placement: + folder_objects = salt.utils.vmware.get_folders( + service_instance, [placement["folder"]], datacenter + ) if len(folder_objects) > 1: - raise salt.exceptions.VMwareMultipleObjectsError(' '.join([ - 'Multiple instances are available of the', - 'specified folder {0}'.format(placement['folder'])])) + raise salt.exceptions.VMwareMultipleObjectsError( + " ".join( + [ + "Multiple instances are available of the", + "specified folder {0}".format(placement["folder"]), + ] + ) + ) folder_object = folder_objects[0] elif datacenter: - datacenter_object = salt.utils.vmware.get_datacenter(service_instance, datacenter) - dc_props = salt.utils.vmware.get_properties_of_managed_object(datacenter_object, properties=['vmFolder']) - if 'vmFolder' in dc_props: - folder_object = dc_props['vmFolder'] + datacenter_object = salt.utils.vmware.get_datacenter( + service_instance, datacenter + ) + dc_props = salt.utils.vmware.get_properties_of_managed_object( + datacenter_object, properties=["vmFolder"] + ) + if "vmFolder" in dc_props: + folder_object = dc_props["vmFolder"] else: - raise salt.exceptions.VMwareObjectRetrievalError('The datacenter vm folder object is not defined') + raise salt.exceptions.VMwareObjectRetrievalError( + "The datacenter vm folder object is not defined" + ) return folder_object def get_placement(service_instance, datacenter, placement=None): - ''' + """ To create a virtual machine a resource pool needs to be supplied, we would like to use the strictest as possible. datacenter @@ -3331,99 +3608,121 @@ def get_placement(service_instance, datacenter, placement=None): return Resource pool, cluster and host object if any applies - ''' - log.trace('Retrieving placement information') + """ + log.trace("Retrieving placement information") resourcepool_object, placement_object = None, None - if 'host' in placement: - host_objects = get_hosts(service_instance, datacenter_name=datacenter, host_names=[placement['host']]) + if "host" in placement: + host_objects = get_hosts( + service_instance, datacenter_name=datacenter, host_names=[placement["host"]] + ) if not host_objects: - raise salt.exceptions.VMwareObjectRetrievalError(' '.join([ - 'The specified host', - '{0} cannot be found.'.format(placement['host'])])) + raise salt.exceptions.VMwareObjectRetrievalError( + " ".join( + [ + "The specified host", + "{0} cannot be found.".format(placement["host"]), + ] + ) + ) try: - host_props = \ - get_properties_of_managed_object(host_objects[0], - properties=['resourcePool']) - resourcepool_object = host_props['resourcePool'] + host_props = get_properties_of_managed_object( + host_objects[0], properties=["resourcePool"] + ) + resourcepool_object = host_props["resourcePool"] except vmodl.query.InvalidProperty: traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='parent', + path="parent", skip=True, type=vim.HostSystem, - selectSet=[vmodl.query.PropertyCollector.TraversalSpec( - path='resourcePool', - skip=False, - type=vim.ClusterComputeResource)]) - resourcepools = get_mors_with_properties(service_instance, - vim.ResourcePool, - container_ref=host_objects[0], - property_list=['name'], - traversal_spec=traversal_spec) + selectSet=[ + vmodl.query.PropertyCollector.TraversalSpec( + path="resourcePool", skip=False, type=vim.ClusterComputeResource + ) + ], + ) + resourcepools = get_mors_with_properties( + service_instance, + vim.ResourcePool, + container_ref=host_objects[0], + property_list=["name"], + traversal_spec=traversal_spec, + ) if resourcepools: - resourcepool_object = resourcepools[0]['object'] + resourcepool_object = resourcepools[0]["object"] else: raise salt.exceptions.VMwareObjectRetrievalError( - 'The resource pool of host {0} cannot be found.'.format(placement['host'])) + "The resource pool of host {0} cannot be found.".format( + placement["host"] + ) + ) placement_object = host_objects[0] - elif 'resourcepool' in placement: - resourcepool_objects = get_resource_pools(service_instance, - [placement['resourcepool']], - datacenter_name=datacenter) + elif "resourcepool" in placement: + resourcepool_objects = get_resource_pools( + service_instance, [placement["resourcepool"]], datacenter_name=datacenter + ) if len(resourcepool_objects) > 1: - raise salt.exceptions.VMwareMultipleObjectsError(' '.join([ - 'Multiple instances are available of the', - 'specified host {}.'.format(placement['host'])])) + raise salt.exceptions.VMwareMultipleObjectsError( + " ".join( + [ + "Multiple instances are available of the", + "specified host {}.".format(placement["host"]), + ] + ) + ) resourcepool_object = resourcepool_objects[0] - res_props = get_properties_of_managed_object(resourcepool_object, - properties=['parent']) - if 'parent' in res_props: - placement_object = res_props['parent'] + res_props = get_properties_of_managed_object( + resourcepool_object, properties=["parent"] + ) + if "parent" in res_props: + placement_object = res_props["parent"] else: - raise salt.exceptions.VMwareObjectRetrievalError(' '.join([ - 'The resource pool\'s parent', - 'object is not defined'])) - elif 'cluster' in placement: + raise salt.exceptions.VMwareObjectRetrievalError( + " ".join(["The resource pool's parent", "object is not defined"]) + ) + elif "cluster" in placement: datacenter_object = get_datacenter(service_instance, datacenter) - cluster_object = get_cluster(datacenter_object, placement['cluster']) - clus_props = get_properties_of_managed_object(cluster_object, - properties=['resourcePool']) - if 'resourcePool' in clus_props: - resourcepool_object = clus_props['resourcePool'] + cluster_object = get_cluster(datacenter_object, placement["cluster"]) + clus_props = get_properties_of_managed_object( + cluster_object, properties=["resourcePool"] + ) + if "resourcePool" in clus_props: + resourcepool_object = clus_props["resourcePool"] else: - raise salt.exceptions.VMwareObjectRetrievalError(' '.join([ - 'The cluster\'s resource pool', - 'object is not defined'])) + raise salt.exceptions.VMwareObjectRetrievalError( + " ".join(["The cluster's resource pool", "object is not defined"]) + ) placement_object = cluster_object else: # We are checking the schema for this object, this exception should never be raised - raise salt.exceptions.VMwareObjectRetrievalError(' '.join([ - 'Placement is not defined.'])) + raise salt.exceptions.VMwareObjectRetrievalError( + " ".join(["Placement is not defined."]) + ) return (resourcepool_object, placement_object) def convert_to_kb(unit, size): - ''' + """ Converts the given size to KB based on the unit, returns a long integer. unit Unit of the size eg. GB; Note: to VMware a GB is the same as GiB = 1024MiB size Number which represents the size - ''' - if unit.lower() == 'gb': + """ + if unit.lower() == "gb": # vCenter needs long value target_size = int(size * 1024 * 1024) - elif unit.lower() == 'mb': + elif unit.lower() == "mb": target_size = int(size * 1024) - elif unit.lower() == 'kb': + elif unit.lower() == "kb": target_size = int(size) else: - raise salt.exceptions.ArgumentValueError('The unit is not specified') - return {'size': target_size, 'unit': 'KB'} + raise salt.exceptions.ArgumentValueError("The unit is not specified") + return {"size": target_size, "unit": "KB"} -def power_cycle_vm(virtual_machine, action='on'): - ''' +def power_cycle_vm(virtual_machine, action="on"): + """ Powers on/off a virtual machine specified by its name. virtual_machine @@ -3431,31 +3730,33 @@ def power_cycle_vm(virtual_machine, action='on'): action Operation option to power on/off the machine - ''' - if action == 'on': + """ + if action == "on": try: task = virtual_machine.PowerOn() - task_name = 'power on' + task_name = "power on" except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " + "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - elif action == 'off': + elif action == "off": try: task = virtual_machine.PowerOff() - task_name = 'power off' + task_name = "power off" except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " + "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -3463,18 +3764,25 @@ def power_cycle_vm(virtual_machine, action='on'): log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) else: - raise salt.exceptions.ArgumentValueError('The given action is not supported') + raise salt.exceptions.ArgumentValueError("The given action is not supported") try: wait_for_task(task, get_managed_object_name(virtual_machine), task_name) except salt.exceptions.VMwareFileNotFoundError as exc: - raise salt.exceptions.VMwarePowerOnError(' '.join([ - 'An error occurred during power', - 'operation, a file was not found: {0}'.format(exc)])) + raise salt.exceptions.VMwarePowerOnError( + " ".join( + [ + "An error occurred during power", + "operation, a file was not found: {0}".format(exc), + ] + ) + ) return virtual_machine -def create_vm(vm_name, vm_config_spec, folder_object, resourcepool_object, host_object=None): - ''' +def create_vm( + vm_name, vm_config_spec, folder_object, resourcepool_object, host_object=None +): + """ Creates virtual machine from config spec vm_name @@ -3494,32 +3802,31 @@ def create_vm(vm_name, vm_config_spec, folder_object, resourcepool_object, host_ return Virtual Machine managed object reference - ''' + """ try: if host_object and isinstance(host_object, vim.HostSystem): - task = folder_object.CreateVM_Task(vm_config_spec, - pool=resourcepool_object, - host=host_object) + task = folder_object.CreateVM_Task( + vm_config_spec, pool=resourcepool_object, host=host_object + ) else: - task = folder_object.CreateVM_Task(vm_config_spec, - pool=resourcepool_object) + task = folder_object.CreateVM_Task(vm_config_spec, pool=resourcepool_object) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - vm_object = wait_for_task(task, vm_name, 'CreateVM Task', 10, 'info') + vm_object = wait_for_task(task, vm_name, "CreateVM Task", 10, "info") return vm_object def register_vm(datacenter, name, vmx_path, resourcepool_object, host_object=None): - ''' + """ Registers a virtual machine to the inventory with the given vmx file, on success it returns the vim.VirtualMachine managed object reference @@ -3537,22 +3844,25 @@ def register_vm(datacenter, name, vmx_path, resourcepool_object, host_object=Non host Placement host of the virtual machine, vim.HostSystem object - ''' + """ try: if host_object: - task = datacenter.vmFolder.RegisterVM_Task(path=vmx_path, name=name, - asTemplate=False, - host=host_object, - pool=resourcepool_object) + task = datacenter.vmFolder.RegisterVM_Task( + path=vmx_path, + name=name, + asTemplate=False, + host=host_object, + pool=resourcepool_object, + ) else: - task = datacenter.vmFolder.RegisterVM_Task(path=vmx_path, name=name, - asTemplate=False, - pool=resourcepool_object) + task = datacenter.vmFolder.RegisterVM_Task( + path=vmx_path, name=name, asTemplate=False, pool=resourcepool_object + ) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) @@ -3560,16 +3870,17 @@ def register_vm(datacenter, name, vmx_path, resourcepool_object, host_object=Non log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) try: - vm_ref = wait_for_task(task, name, 'RegisterVM Task') + vm_ref = wait_for_task(task, name, "RegisterVM Task") except salt.exceptions.VMwareFileNotFoundError as exc: raise salt.exceptions.VMwareVmRegisterError( - 'An error occurred during registration operation, the ' - 'configuration file was not found: {0}'.format(exc)) + "An error occurred during registration operation, the " + "configuration file was not found: {0}".format(exc) + ) return vm_ref def update_vm(vm_ref, vm_config_spec): - ''' + """ Updates the virtual machine configuration with the given object vm_ref @@ -3577,67 +3888,67 @@ def update_vm(vm_ref, vm_config_spec): vm_config_spec Virtual machine config spec object to update - ''' + """ vm_name = get_managed_object_name(vm_ref) - log.trace('Updating vm \'%s\'', vm_name) + log.trace("Updating vm '%s'", vm_name) try: task = vm_ref.ReconfigVM_Task(vm_config_spec) except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - vm_ref = wait_for_task(task, vm_name, 'ReconfigureVM Task') + vm_ref = wait_for_task(task, vm_name, "ReconfigureVM Task") return vm_ref def delete_vm(vm_ref): - ''' + """ Destroys the virtual machine vm_ref Managed object reference of a virtual machine object - ''' + """ vm_name = get_managed_object_name(vm_ref) - log.trace('Destroying vm \'%s\'', vm_name) + log.trace("Destroying vm '%s'", vm_name) try: task = vm_ref.Destroy_Task() except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise salt.exceptions.VMwareRuntimeError(exc.msg) - wait_for_task(task, vm_name, 'Destroy Task') + wait_for_task(task, vm_name, "Destroy Task") def unregister_vm(vm_ref): - ''' + """ Destroys the virtual machine vm_ref Managed object reference of a virtual machine object - ''' + """ vm_name = get_managed_object_name(vm_ref) - log.trace('Destroying vm \'%s\'', vm_name) + log.trace("Destroying vm '%s'", vm_name) try: vm_ref.UnregisterVM() except vim.fault.NoPermission as exc: log.exception(exc) raise salt.exceptions.VMwareApiError( - 'Not enough permissions. Required privilege: ' - '{}'.format(exc.privilegeId)) + "Not enough permissions. Required privilege: " "{}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: raise salt.exceptions.VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: diff --git a/salt/utils/vsan.py b/salt/utils/vsan.py index c6aac91f364..85a144ee510 100644 --- a/salt/utils/vsan.py +++ b/salt/utils/vsan.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Connection library for VMware vSAN endpoint This library used the vSAN extension of the VMware SDK @@ -40,22 +40,28 @@ version currently listed in PyPi, run the following: The 5.5.0.2014.1.1 is a known stable version that this original VMware utils file was developed against. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import sys + import logging import ssl +import sys + +import salt.utils.vmware +from salt.exceptions import ( + VMwareApiError, + VMwareObjectRetrievalError, + VMwareRuntimeError, +) # Import Salt Libs from salt.ext import six -from salt.exceptions import VMwareApiError, VMwareRuntimeError, \ - VMwareObjectRetrievalError -import salt.utils.vmware try: from pyVmomi import vim, vmodl # pylint: disable=no-name-in-module + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False @@ -63,6 +69,7 @@ except ImportError: try: from salt.ext.vsan import vsanapiutils + HAS_PYVSAN = True except ImportError: HAS_PYVSAN = False @@ -72,54 +79,58 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if PyVmomi is installed. - ''' + """ if HAS_PYVSAN and HAS_PYVMOMI: return True else: - return False, 'Missing dependency: The salt.utils.vsan module ' \ - 'requires pyvmomi and the pyvsan extension library' + return ( + False, + "Missing dependency: The salt.utils.vsan module " + "requires pyvmomi and the pyvsan extension library", + ) def vsan_supported(service_instance): - ''' + """ Returns whether vsan is supported on the vCenter: api version needs to be 6 or higher service_instance Service instance to the host or vCenter - ''' + """ try: api_version = service_instance.content.about.apiVersion except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise VMwareRuntimeError(exc.msg) - if int(api_version.split('.')[0]) < 6: + if int(api_version.split(".")[0]) < 6: return False return True def get_vsan_cluster_config_system(service_instance): - ''' + """ Returns a vim.cluster.VsanVcClusterConfigSystem object service_instance Service instance to the host or vCenter - ''' + """ - #TODO Replace when better connection mechanism is available + # TODO Replace when better connection mechanism is available - #For python 2.7.9 and later, the defaul SSL conext has more strict - #connection handshaking rule. We may need turn of the hostname checking - #and client side cert verification + # For python 2.7.9 and later, the defaul SSL conext has more strict + # connection handshaking rule. We may need turn of the hostname checking + # and client side cert verification context = None if sys.version_info[:3] > (2, 7, 8): context = ssl.create_default_context() @@ -128,22 +139,22 @@ def get_vsan_cluster_config_system(service_instance): stub = service_instance._stub vc_mos = vsanapiutils.GetVsanVcMos(stub, context=context) - return vc_mos['vsan-cluster-config-system'] + return vc_mos["vsan-cluster-config-system"] def get_vsan_disk_management_system(service_instance): - ''' + """ Returns a vim.VimClusterVsanVcDiskManagementSystem object service_instance Service instance to the host or vCenter - ''' + """ - #TODO Replace when better connection mechanism is available + # TODO Replace when better connection mechanism is available - #For python 2.7.9 and later, the defaul SSL conext has more strict - #connection handshaking rule. We may need turn of the hostname checking - #and client side cert verification + # For python 2.7.9 and later, the defaul SSL conext has more strict + # connection handshaking rule. We may need turn of the hostname checking + # and client side cert verification context = None if sys.version_info[:3] > (2, 7, 8): context = ssl.create_default_context() @@ -152,11 +163,11 @@ def get_vsan_disk_management_system(service_instance): stub = service_instance._stub vc_mos = vsanapiutils.GetVsanVcMos(stub, context=context) - return vc_mos['vsan-disk-management-system'] + return vc_mos["vsan-disk-management-system"] def get_host_vsan_system(service_instance, host_ref, hostname=None): - ''' + """ Returns a host's vsan system service_instance @@ -167,26 +178,31 @@ def get_host_vsan_system(service_instance, host_ref, hostname=None): hostname Name of ESXi host. Default value is None. - ''' + """ if not hostname: hostname = salt.utils.vmware.get_managed_object_name(host_ref) traversal_spec = vmodl.query.PropertyCollector.TraversalSpec( - path='configManager.vsanSystem', - type=vim.HostSystem, - skip=False) + path="configManager.vsanSystem", type=vim.HostSystem, skip=False + ) objs = salt.utils.vmware.get_mors_with_properties( - service_instance, vim.HostVsanSystem, property_list=['config.enabled'], - container_ref=host_ref, traversal_spec=traversal_spec) + service_instance, + vim.HostVsanSystem, + property_list=["config.enabled"], + container_ref=host_ref, + traversal_spec=traversal_spec, + ) if not objs: - raise VMwareObjectRetrievalError('Host\'s \'{0}\' VSAN system was ' - 'not retrieved'.format(hostname)) - log.trace('[%s] Retrieved VSAN system', hostname) - return objs[0]['object'] + raise VMwareObjectRetrievalError( + "Host's '{0}' VSAN system was " "not retrieved".format(hostname) + ) + log.trace("[%s] Retrieved VSAN system", hostname) + return objs[0]["object"] -def create_diskgroup(service_instance, vsan_disk_mgmt_system, - host_ref, cache_disk, capacity_disks): - ''' +def create_diskgroup( + service_instance, vsan_disk_mgmt_system, host_ref, cache_disk, capacity_disks +): + """ Creates a disk group service_instance @@ -207,34 +223,35 @@ def create_diskgroup(service_instance, vsan_disk_mgmt_system, List of vim.HostScsiDisk objects representing of disks to be used as capacity disks. Can be either ssd or non-ssd. There must be a minimum of 1 capacity disk in the list. - ''' + """ hostname = salt.utils.vmware.get_managed_object_name(host_ref) cache_disk_id = cache_disk.canonicalName log.debug( - 'Creating a new disk group with cache disk \'%s\' on host \'%s\'', - cache_disk_id, hostname + "Creating a new disk group with cache disk '%s' on host '%s'", + cache_disk_id, + hostname, ) - log.trace('capacity_disk_ids = %s', [c.canonicalName for c in capacity_disks]) + log.trace("capacity_disk_ids = %s", [c.canonicalName for c in capacity_disks]) spec = vim.VimVsanHostDiskMappingCreationSpec() spec.cacheDisks = [cache_disk] spec.capacityDisks = capacity_disks # All capacity disks must be either ssd or non-ssd (mixed disks are not # supported) - spec.creationType = 'allFlash' if getattr(capacity_disks[0], 'ssd') \ - else 'hybrid' + spec.creationType = "allFlash" if getattr(capacity_disks[0], "ssd") else "hybrid" spec.host = host_ref try: task = vsan_disk_mgmt_system.InitializeDiskMappings(spec) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) except vmodl.fault.MethodNotFound as exc: log.exception(exc) - raise VMwareRuntimeError('Method \'{0}\' not found'.format(exc.method)) + raise VMwareRuntimeError("Method '{0}' not found".format(exc.method)) except vmodl.RuntimeFault as exc: log.exception(exc) raise VMwareRuntimeError(exc.msg) @@ -242,9 +259,10 @@ def create_diskgroup(service_instance, vsan_disk_mgmt_system, return True -def add_capacity_to_diskgroup(service_instance, vsan_disk_mgmt_system, - host_ref, diskgroup, new_capacity_disks): - ''' +def add_capacity_to_diskgroup( + service_instance, vsan_disk_mgmt_system, host_ref, diskgroup, new_capacity_disks +): + """ Adds capacity disk(s) to a disk group. service_instance @@ -266,17 +284,17 @@ def add_capacity_to_diskgroup(service_instance, vsan_disk_mgmt_system, List of vim.HostScsiDisk objects representing the disks to be added as capacity disks. Can be either ssd or non-ssd. There must be a minimum of 1 new capacity disk in the list. - ''' + """ hostname = salt.utils.vmware.get_managed_object_name(host_ref) cache_disk = diskgroup.ssd cache_disk_id = cache_disk.canonicalName log.debug( - 'Adding capacity to disk group with cache disk \'%s\' on host \'%s\'', - cache_disk_id, hostname + "Adding capacity to disk group with cache disk '%s' on host '%s'", + cache_disk_id, + hostname, ) log.trace( - 'new_capacity_disk_ids = %s', - [c.canonicalName for c in new_capacity_disks] + "new_capacity_disk_ids = %s", [c.canonicalName for c in new_capacity_disks] ) spec = vim.VimVsanHostDiskMappingCreationSpec() spec.cacheDisks = [cache_disk] @@ -284,32 +302,39 @@ def add_capacity_to_diskgroup(service_instance, vsan_disk_mgmt_system, # All new capacity disks must be either ssd or non-ssd (mixed disks are not # supported); also they need to match the type of the existing capacity # disks; we assume disks are already validated - spec.creationType = 'allFlash' if getattr(new_capacity_disks[0], 'ssd') \ - else 'hybrid' + spec.creationType = ( + "allFlash" if getattr(new_capacity_disks[0], "ssd") else "hybrid" + ) spec.host = host_ref try: task = vsan_disk_mgmt_system.InitializeDiskMappings(spec) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) except vmodl.fault.MethodNotFound as exc: log.exception(exc) - raise VMwareRuntimeError('Method \'{0}\' not found'.format(exc.method)) + raise VMwareRuntimeError("Method '{0}' not found".format(exc.method)) except vmodl.RuntimeFault as exc: raise VMwareRuntimeError(exc.msg) _wait_for_tasks([task], service_instance) return True -def remove_capacity_from_diskgroup(service_instance, host_ref, diskgroup, - capacity_disks, data_evacuation=True, - hostname=None, - host_vsan_system=None): - ''' +def remove_capacity_from_diskgroup( + service_instance, + host_ref, + diskgroup, + capacity_disks, + data_evacuation=True, + hostname=None, + host_vsan_system=None, +): + """ Removes capacity disk(s) from a disk group. service_instance @@ -339,50 +364,59 @@ def remove_capacity_from_diskgroup(service_instance, host_ref, diskgroup, host_vsan_system ESXi host's VSAN system. Default value is None. - ''' + """ if not hostname: hostname = salt.utils.vmware.get_managed_object_name(host_ref) cache_disk = diskgroup.ssd cache_disk_id = cache_disk.canonicalName log.debug( - 'Removing capacity from disk group with cache disk \'%s\' on host \'%s\'', - cache_disk_id, hostname + "Removing capacity from disk group with cache disk '%s' on host '%s'", + cache_disk_id, + hostname, ) - log.trace('capacity_disk_ids = %s', - [c.canonicalName for c in capacity_disks]) + log.trace("capacity_disk_ids = %s", [c.canonicalName for c in capacity_disks]) if not host_vsan_system: - host_vsan_system = get_host_vsan_system(service_instance, - host_ref, hostname) + host_vsan_system = get_host_vsan_system(service_instance, host_ref, hostname) # Set to evacuate all data before removing the disks maint_spec = vim.HostMaintenanceSpec() maint_spec.vsanMode = vim.VsanHostDecommissionMode() if data_evacuation: - maint_spec.vsanMode.objectAction = \ - vim.VsanHostDecommissionModeObjectAction.evacuateAllData + maint_spec.vsanMode.objectAction = ( + vim.VsanHostDecommissionModeObjectAction.evacuateAllData + ) else: - maint_spec.vsanMode.objectAction = \ - vim.VsanHostDecommissionModeObjectAction.noAction + maint_spec.vsanMode.objectAction = ( + vim.VsanHostDecommissionModeObjectAction.noAction + ) try: - task = host_vsan_system.RemoveDisk_Task(disk=capacity_disks, - maintenanceSpec=maint_spec) + task = host_vsan_system.RemoveDisk_Task( + disk=capacity_disks, maintenanceSpec=maint_spec + ) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise VMwareRuntimeError(exc.msg) - salt.utils.vmware.wait_for_task(task, hostname, 'remove_capacity') + salt.utils.vmware.wait_for_task(task, hostname, "remove_capacity") return True -def remove_diskgroup(service_instance, host_ref, diskgroup, hostname=None, - host_vsan_system=None, erase_disk_partitions=False, - data_accessibility=True): - ''' +def remove_diskgroup( + service_instance, + host_ref, + diskgroup, + hostname=None, + host_vsan_system=None, + erase_disk_partitions=False, + data_accessibility=True, +): + """ Removes a disk group. service_instance @@ -403,63 +437,67 @@ def remove_diskgroup(service_instance, host_ref, diskgroup, hostname=None, data_accessibility Specifies whether to ensure data accessibility. Default value is True. - ''' + """ if not hostname: hostname = salt.utils.vmware.get_managed_object_name(host_ref) cache_disk_id = diskgroup.ssd.canonicalName - log.debug('Removing disk group with cache disk \'%s\' on ' - 'host \'%s\'', cache_disk_id, hostname) + log.debug( + "Removing disk group with cache disk '%s' on " "host '%s'", + cache_disk_id, + hostname, + ) if not host_vsan_system: - host_vsan_system = get_host_vsan_system( - service_instance, host_ref, hostname) + host_vsan_system = get_host_vsan_system(service_instance, host_ref, hostname) # Set to evacuate all data before removing the disks maint_spec = vim.HostMaintenanceSpec() maint_spec.vsanMode = vim.VsanHostDecommissionMode() object_action = vim.VsanHostDecommissionModeObjectAction if data_accessibility: - maint_spec.vsanMode.objectAction = \ - object_action.ensureObjectAccessibility + maint_spec.vsanMode.objectAction = object_action.ensureObjectAccessibility else: maint_spec.vsanMode.objectAction = object_action.noAction try: task = host_vsan_system.RemoveDiskMapping_Task( - mapping=[diskgroup], maintenanceSpec=maint_spec) + mapping=[diskgroup], maintenanceSpec=maint_spec + ) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise VMwareRuntimeError(exc.msg) - salt.utils.vmware.wait_for_task(task, hostname, 'remove_diskgroup') - log.debug('Removed disk group with cache disk \'%s\' on host \'%s\'', - cache_disk_id, hostname) + salt.utils.vmware.wait_for_task(task, hostname, "remove_diskgroup") + log.debug( + "Removed disk group with cache disk '%s' on host '%s'", cache_disk_id, hostname + ) return True def get_cluster_vsan_info(cluster_ref): - ''' + """ Returns the extended cluster vsan configuration object (vim.VsanConfigInfoEx). cluster_ref Reference to the cluster - ''' + """ cluster_name = salt.utils.vmware.get_managed_object_name(cluster_ref) - log.trace('Retrieving cluster vsan info of cluster \'%s\'', cluster_name) - si = salt.utils.vmware.get_service_instance_from_managed_object( - cluster_ref) + log.trace("Retrieving cluster vsan info of cluster '%s'", cluster_name) + si = salt.utils.vmware.get_service_instance_from_managed_object(cluster_ref) vsan_cl_conf_sys = get_vsan_cluster_config_system(si) try: return vsan_cl_conf_sys.VsanClusterGetConfig(cluster_ref) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) @@ -469,7 +507,7 @@ def get_cluster_vsan_info(cluster_ref): def reconfigure_cluster_vsan(cluster_ref, cluster_vsan_spec): - ''' + """ Reconfigures the VSAN system of a cluster. cluster_ref @@ -477,20 +515,18 @@ def reconfigure_cluster_vsan(cluster_ref, cluster_vsan_spec): cluster_vsan_spec Cluster VSAN reconfigure spec (vim.vsan.ReconfigSpec). - ''' + """ cluster_name = salt.utils.vmware.get_managed_object_name(cluster_ref) - log.trace('Reconfiguring vsan on cluster \'%s\': %s', - cluster_name, cluster_vsan_spec) - si = salt.utils.vmware.get_service_instance_from_managed_object( - cluster_ref) + log.trace("Reconfiguring vsan on cluster '%s': %s", cluster_name, cluster_vsan_spec) + si = salt.utils.vmware.get_service_instance_from_managed_object(cluster_ref) vsan_cl_conf_sys = salt.utils.vsan.get_vsan_cluster_config_system(si) try: - task = vsan_cl_conf_sys.VsanClusterReconfig(cluster_ref, - cluster_vsan_spec) + task = vsan_cl_conf_sys.VsanClusterReconfig(cluster_ref, cluster_vsan_spec) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) @@ -501,22 +537,25 @@ def reconfigure_cluster_vsan(cluster_ref, cluster_vsan_spec): def _wait_for_tasks(tasks, service_instance): - ''' + """ Wait for tasks created via the VSAN API - ''' - log.trace('Waiting for vsan tasks: {0}', - ', '.join([six.text_type(t) for t in tasks])) + """ + log.trace( + "Waiting for vsan tasks: {0}", ", ".join([six.text_type(t) for t in tasks]) + ) try: vsanapiutils.WaitForTasks(tasks, service_instance) except vim.fault.NoPermission as exc: log.exception(exc) - raise VMwareApiError('Not enough permissions. Required privilege: ' - '{0}'.format(exc.privilegeId)) + raise VMwareApiError( + "Not enough permissions. Required privilege: " "{0}".format(exc.privilegeId) + ) except vim.fault.VimFault as exc: log.exception(exc) raise VMwareApiError(exc.msg) except vmodl.RuntimeFault as exc: log.exception(exc) raise VMwareRuntimeError(exc.msg) - log.trace('Tasks %s finished successfully', - ', '.join([six.text_type(t) for t in tasks])) + log.trace( + "Tasks %s finished successfully", ", ".join([six.text_type(t) for t in tasks]) + ) diff --git a/salt/utils/vt.py b/salt/utils/vt.py index d4cd2dd7237..446f31f0ff0 100644 --- a/salt/utils/vt.py +++ b/salt/utils/vt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -18,22 +18,30 @@ .. __: https://github.com/python-mirror/python/blob/3.3/Lib/pty.py .. __: https://github.com/pexpect/pexpect -''' +""" from __future__ import absolute_import, print_function, unicode_literals +import errno +import logging + # Import python libs import os +import select +import signal import sys import time -import errno -import signal -import select -import logging + +# Import salt libs +import salt.utils.crypt +import salt.utils.data +import salt.utils.stringutils # Import salt libs from salt.ext import six +from salt.ext.six import string_types +from salt.log.setup import LOG_LEVELS -mswindows = (sys.platform == "win32") +mswindows = sys.platform == "win32" try: # pylint: disable=F0401,W0611 @@ -43,6 +51,7 @@ try: import win32api import win32con import win32process + # pylint: enable=F0401,W0611 except ImportError: import pty @@ -51,20 +60,14 @@ except ImportError: import termios import resource -# Import salt libs -import salt.utils.crypt -import salt.utils.data -import salt.utils.stringutils -from salt.ext.six import string_types -from salt.log.setup import LOG_LEVELS log = logging.getLogger(__name__) class TerminalException(Exception): - ''' + """ Terminal specific exception - ''' + """ # ----- Cleanup Running Instances -------------------------------------------> @@ -76,10 +79,10 @@ _ACTIVE = [] def _cleanup(): - ''' + """ Make sure that any terminal processes still running when __del__ was called to the waited and cleaned up. - ''' + """ for inst in _ACTIVE[:]: res = inst.isalive() if res is not True: @@ -89,37 +92,38 @@ def _cleanup(): # This can happen if two threads create a new Terminal instance # It's harmless that it was already removed, so ignore. pass + + # <---- Cleanup Running Instances -------------------------------------------- class Terminal(object): - ''' + """ I'm a virtual terminal - ''' - def __init__(self, - args=None, - executable=None, - shell=False, - cwd=None, - env=None, - preexec_fn=None, + """ - # Terminal Size - rows=None, - cols=None, - - # Logging options - log_stdin=None, - log_stdin_level='debug', - log_stdout=None, - log_stdout_level='debug', - log_stderr=None, - log_stderr_level='debug', - - # sys.stdXYZ streaming options - stream_stdout=None, - stream_stderr=None, - ): + def __init__( + self, + args=None, + executable=None, + shell=False, + cwd=None, + env=None, + preexec_fn=None, + # Terminal Size + rows=None, + cols=None, + # Logging options + log_stdin=None, + log_stdin_level="debug", + log_stdout=None, + log_stdout_level="debug", + log_stderr=None, + log_stderr_level="debug", + # sys.stdXYZ streaming options + stream_stdout=None, + stream_stderr=None, + ): # Let's avoid Zombies!!! _cleanup() @@ -164,7 +168,7 @@ class Terminal(object): self.signalstatus = None # status returned by os.waitpid self.status = None - self.__irix_hack = 'irix' in sys.platform.lower() + self.__irix_hack = "irix" in sys.platform.lower() # <---- Internally Set Attributes ------------------------------------ # ----- Direct Streaming Setup --------------------------------------> @@ -173,18 +177,20 @@ class Terminal(object): elif stream_stdout is False: self.stream_stdout = None elif stream_stdout is not None: - if not hasattr(stream_stdout, 'write') or \ - not hasattr(stream_stdout, 'flush') or \ - not hasattr(stream_stdout, 'close'): + if ( + not hasattr(stream_stdout, "write") + or not hasattr(stream_stdout, "flush") + or not hasattr(stream_stdout, "close") + ): raise TerminalException( - '\'stream_stdout\' needs to have at least 3 methods, ' - '\'write()\', \'flush()\' and \'close()\'.' + "'stream_stdout' needs to have at least 3 methods, " + "'write()', 'flush()' and 'close()'." ) self.stream_stdout = stream_stdout else: raise TerminalException( - 'Don\'t know how to handle \'{0}\' as the VT\'s ' - '\'stream_stdout\' parameter.'.format(stream_stdout) + "Don't know how to handle '{0}' as the VT's " + "'stream_stdout' parameter.".format(stream_stdout) ) if stream_stderr is True: @@ -192,18 +198,20 @@ class Terminal(object): elif stream_stderr is False: self.stream_stderr = None elif stream_stderr is not None: - if not hasattr(stream_stderr, 'write') or \ - not hasattr(stream_stderr, 'flush') or \ - not hasattr(stream_stderr, 'close'): + if ( + not hasattr(stream_stderr, "write") + or not hasattr(stream_stderr, "flush") + or not hasattr(stream_stderr, "close") + ): raise TerminalException( - '\'stream_stderr\' needs to have at least 3 methods, ' - '\'write()\', \'flush()\' and \'close()\'.' + "'stream_stderr' needs to have at least 3 methods, " + "'write()', 'flush()' and 'close()'." ) self.stream_stderr = stream_stderr else: raise TerminalException( - 'Don\'t know how to handle \'{0}\' as the VT\'s ' - '\'stream_stderr\' parameter.'.format(stream_stderr) + "Don't know how to handle '{0}' as the VT's " + "'stream_stderr' parameter.".format(stream_stderr) ) # <---- Direct Streaming Setup --------------------------------------- @@ -214,23 +222,25 @@ class Terminal(object): # A lot can go wrong, so that's why we're catching the most general # exception type log.warning( - 'Failed to spawn the VT: %s', err, - exc_info_on_loglevel=logging.DEBUG - ) - raise TerminalException( - 'Failed to spawn the VT. Error: {0}'.format(err) + "Failed to spawn the VT: %s", err, exc_info_on_loglevel=logging.DEBUG ) + raise TerminalException("Failed to spawn the VT. Error: {0}".format(err)) log.debug( - 'Child Forked! PID: %s STDOUT_FD: %s STDERR_FD: %s', - self.pid, self.child_fd, self.child_fde + "Child Forked! PID: %s STDOUT_FD: %s STDERR_FD: %s", + self.pid, + self.child_fd, + self.child_fde, ) - terminal_command = ' '.join(self.args) - if 'decode("base64")' in terminal_command or 'base64.b64decode(' in terminal_command: - log.debug('VT: Salt-SSH SHIM Terminal Command executed. Logged to TRACE') - log.trace('Terminal Command: %s', terminal_command) + terminal_command = " ".join(self.args) + if ( + 'decode("base64")' in terminal_command + or "base64.b64decode(" in terminal_command + ): + log.debug("VT: Salt-SSH SHIM Terminal Command executed. Logged to TRACE") + log.trace("Terminal Command: %s", terminal_command) else: - log.debug('Terminal Command: %s', terminal_command) + log.debug("Terminal Command: %s", terminal_command) # <---- Spawn our terminal ------------------------------------------- # ----- Setup Logging -----------------------------------------------> @@ -238,15 +248,13 @@ class Terminal(object): self.stdin_logger_level = LOG_LEVELS.get(log_stdin_level, log_stdin_level) if log_stdin is True: self.stdin_logger = logging.getLogger( - '{0}.{1}.PID-{2}.STDIN'.format( + "{0}.{1}.PID-{2}.STDIN".format( __name__, self.__class__.__name__, self.pid ) ) elif log_stdin is not None: if not isinstance(log_stdin, logging.Logger): - raise RuntimeError( - '\'log_stdin\' needs to subclass `logging.Logger`' - ) + raise RuntimeError("'log_stdin' needs to subclass `logging.Logger`") self.stdin_logger = log_stdin else: self.stdin_logger = None @@ -254,15 +262,13 @@ class Terminal(object): self.stdout_logger_level = LOG_LEVELS.get(log_stdout_level, log_stdout_level) if log_stdout is True: self.stdout_logger = logging.getLogger( - '{0}.{1}.PID-{2}.STDOUT'.format( + "{0}.{1}.PID-{2}.STDOUT".format( __name__, self.__class__.__name__, self.pid ) ) elif log_stdout is not None: if not isinstance(log_stdout, logging.Logger): - raise RuntimeError( - '\'log_stdout\' needs to subclass `logging.Logger`' - ) + raise RuntimeError("'log_stdout' needs to subclass `logging.Logger`") self.stdout_logger = log_stdout else: self.stdout_logger = None @@ -270,15 +276,13 @@ class Terminal(object): self.stderr_logger_level = LOG_LEVELS.get(log_stderr_level, log_stderr_level) if log_stderr is True: self.stderr_logger = logging.getLogger( - '{0}.{1}.PID-{2}.STDERR'.format( + "{0}.{1}.PID-{2}.STDERR".format( __name__, self.__class__.__name__, self.pid ) ) elif log_stderr is not None: if not isinstance(log_stderr, logging.Logger): - raise RuntimeError( - '\'log_stderr\' needs to subclass `logging.Logger`' - ) + raise RuntimeError("'log_stderr' needs to subclass `logging.Logger`") self.stderr_logger = log_stderr else: self.stderr_logger = None @@ -286,24 +290,24 @@ class Terminal(object): # ----- Common Public API -----------------------------------------------> def send(self, data): - ''' + """ Send data to the terminal. You are responsible to send any required line feeds. - ''' + """ return self._send(data) def sendline(self, data, linesep=os.linesep): - ''' + """ Send the provided data to the terminal appending a line feed. - ''' - return self.send('{0}{1}'.format(data, linesep)) + """ + return self.send("{0}{1}".format(data, linesep)) def recv(self, maxsize=None): - ''' + """ Receive data from the terminal as a (``stdout``, ``stderr``) tuple. If any of those is ``None`` we can no longer communicate with the terminal's child process. - ''' + """ if maxsize is None: maxsize = 1024 elif maxsize < 1: @@ -311,12 +315,12 @@ class Terminal(object): return self._recv(maxsize) def close(self, terminate=True, kill=False): - ''' + """ Close the communication with the terminal's child. If ``terminate`` is ``True`` then additionally try to terminate the terminal, and if ``kill`` is also ``True``, kill the terminal if terminating it was not enough. - ''' + """ if not self.closed: if self.child_fd is not None: os.close(self.child_fd) @@ -327,7 +331,7 @@ class Terminal(object): time.sleep(0.1) if terminate: if not self.terminate(kill): - raise TerminalException('Failed to terminate child process.') + raise TerminalException("Failed to terminate child process.") self.closed = True @property @@ -341,7 +345,8 @@ class Terminal(object): if data is None or not data: return # PTY's always return \r\n as the line feeds - return data.replace('\r\n', os.linesep) + return data.replace("\r\n", os.linesep) + # <---- Common Internal API ---------------------------------------------- # ----- Context Manager Methods -----------------------------------------> @@ -353,9 +358,10 @@ class Terminal(object): # Wait for the process to terminate, to avoid zombies. if self.isalive(): self.wait() + # <---- Context Manager Methods ------------------------------------------ -# ----- Platform Specific Methods -------------------------------------------> + # ----- Platform Specific Methods -------------------------------------------> if mswindows: # ----- Windows Methods ---------------------------------------------> def _execute(self): @@ -371,9 +377,9 @@ class Terminal(object): raise NotImplementedError def send_signal(self, sig): - ''' + """ Send a signal to the process - ''' + """ # pylint: disable=E1101 if sig == signal.SIGTERM: self.terminate() @@ -382,13 +388,13 @@ class Terminal(object): elif sig == signal.CTRL_BREAK_EVENT: os.kill(self.pid, signal.CTRL_BREAK_EVENT) else: - raise ValueError('Unsupported signal: {0}'.format(sig)) + raise ValueError("Unsupported signal: {0}".format(sig)) # pylint: enable=E1101 def terminate(self, force=False): - ''' + """ Terminates the process - ''' + """ try: win32api.TerminateProcess(self._handle, 1) except OSError: @@ -415,9 +421,9 @@ class Terminal(object): args = [] if self.shell and self.args: - self.args = ['/bin/sh', '-c', ' '.join(args)] + self.args = ["/bin/sh", "-c", " ".join(args)] elif self.shell: - self.args = ['/bin/sh'] + self.args = ["/bin/sh"] else: self.args = args @@ -442,8 +448,9 @@ class Terminal(object): self.setwinsize(self.rows, self.cols) except IOError as err: log.warning( - 'Failed to set the VT terminal size: %s', - err, exc_info_on_loglevel=logging.DEBUG + "Failed to set the VT terminal size: %s", + err, + exc_info_on_loglevel=logging.DEBUG, ) # Do not allow child to inherit open file descriptors from @@ -470,23 +477,23 @@ class Terminal(object): self.terminated = False def __fork_ptys(self): - ''' + """ Fork the PTY The major difference from the python source is that we separate the stdout from stderr output. - ''' + """ stdout_parent_fd, stdout_child_fd = pty.openpty() if stdout_parent_fd < 0 or stdout_child_fd < 0: - raise TerminalException('Failed to open a TTY for stdout') + raise TerminalException("Failed to open a TTY for stdout") stderr_parent_fd, stderr_child_fd = pty.openpty() if stderr_parent_fd < 0 or stderr_child_fd < 0: - raise TerminalException('Failed to open a TTY for stderr') + raise TerminalException("Failed to open a TTY for stderr") pid = os.fork() if pid < pty.CHILD: - raise TerminalException('Failed to fork') + raise TerminalException("Failed to fork") elif pid == pty.CHILD: # Child. # Close parent FDs @@ -499,7 +506,7 @@ class Terminal(object): # Disconnect from controlling tty. Harmless if not already # connected try: - tty_fd = os.open('/dev/tty', os.O_RDWR | os.O_NOCTTY) + tty_fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) if tty_fd >= 0: os.close(tty_fd) # which exception, shouldn't we catch explicitly .. ? @@ -513,12 +520,12 @@ class Terminal(object): # Verify we are disconnected from controlling tty # by attempting to open it again. try: - tty_fd = os.open('/dev/tty', os.O_RDWR | os.O_NOCTTY) + tty_fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) if tty_fd >= 0: os.close(tty_fd) raise TerminalException( - 'Failed to disconnect from controlling tty. It is ' - 'still possible to open /dev/tty.' + "Failed to disconnect from controlling tty. It is " + "still possible to open /dev/tty." ) # which exception, shouldn't we catch explicitly .. ? except Exception: # pylint: disable=broad-except @@ -529,18 +536,18 @@ class Terminal(object): tty_fd = os.open(child_name, os.O_RDWR) if tty_fd < 0: raise TerminalException( - 'Could not open child pty, {0}'.format(child_name) + "Could not open child pty, {0}".format(child_name) ) else: os.close(tty_fd) # Verify we now have a controlling tty. - if os.name != 'posix': + if os.name != "posix": # Only do this check in not BSD-like operating systems. BSD-like operating systems breaks at this point - tty_fd = os.open('/dev/tty', os.O_WRONLY) + tty_fd = os.open("/dev/tty", os.O_WRONLY) if tty_fd < 0: raise TerminalException( - 'Could not open controlling tty, /dev/tty' + "Could not open controlling tty, /dev/tty" ) else: os.close(tty_fd) @@ -570,7 +577,9 @@ class Terminal(object): if self.stdin_logger: self.stdin_logger.log(self.stdin_logger_level, data) if six.PY3: - written = os.write(self.child_fd, data.encode(__salt_system_encoding__)) + written = os.write( + self.child_fd, data.encode(__salt_system_encoding__) + ) else: written = os.write(self.child_fd, data) except OSError as why: @@ -594,7 +603,7 @@ class Terminal(object): rlist, _, _ = select.select(rfds, [], [], 0) if not rlist: self.flag_eof_stdout = self.flag_eof_stderr = True - log.debug('End of file(EOL). Brain-dead platform.') + log.debug("End of file(EOL). Brain-dead platform.") return None, None elif self.__irix_hack: # Irix takes a long time before it realizes a child was @@ -605,11 +614,11 @@ class Terminal(object): rlist, _, _ = select.select(rfds, [], [], 2) if not rlist: self.flag_eof_stdout = self.flag_eof_stderr = True - log.debug('End of file(EOL). Slow platform.') + log.debug("End of file(EOL). Slow platform.") return None, None - stderr = '' - stdout = '' + stderr = "" + stdout = "" # ----- Store FD Flags ------------------------------------------> if self.child_fd: @@ -620,11 +629,9 @@ class Terminal(object): # ----- Non blocking Reads --------------------------------------> if self.child_fd: - fcntl.fcntl(self.child_fd, - fcntl.F_SETFL, fd_flags | os.O_NONBLOCK) + fcntl.fcntl(self.child_fd, fcntl.F_SETFL, fd_flags | os.O_NONBLOCK) if self.child_fde: - fcntl.fcntl(self.child_fde, - fcntl.F_SETFL, fde_flags | os.O_NONBLOCK) + fcntl.fcntl(self.child_fde, fcntl.F_SETFL, fde_flags | os.O_NONBLOCK) # <---- Non blocking Reads --------------------------------------- # ----- Check for any incoming data -----------------------------> @@ -635,7 +642,7 @@ class Terminal(object): if not rlist: if not self.isalive(): self.flag_eof_stdout = self.flag_eof_stderr = True - log.debug('End of file(EOL). Very slow platform.') + log.debug("End of file(EOL). Very slow platform.") return None, None # <---- Nothing to Process!? ------------------------------------- @@ -659,9 +666,11 @@ class Terminal(object): if self.stderr_logger: stripped = stderr.rstrip() if stripped.startswith(os.linesep): - stripped = stripped[len(os.linesep):] + stripped = stripped[len(os.linesep) :] if stripped: - self.stderr_logger.log(self.stderr_logger_level, stripped) + self.stderr_logger.log( + self.stderr_logger_level, stripped + ) except OSError: os.close(self.child_fde) self.child_fde = None @@ -686,15 +695,19 @@ class Terminal(object): stdout = None else: if self.stream_stdout: - self.stream_stdout.write(salt.utils.stringutils.to_str(stdout)) + self.stream_stdout.write( + salt.utils.stringutils.to_str(stdout) + ) self.stream_stdout.flush() if self.stdout_logger: stripped = stdout.rstrip() if stripped.startswith(os.linesep): - stripped = stripped[len(os.linesep):] + stripped = stripped[len(os.linesep) :] if stripped: - self.stdout_logger.log(self.stdout_logger_level, stripped) + self.stdout_logger.log( + self.stdout_logger_level, stripped + ) except OSError: os.close(self.child_fd) self.child_fd = None @@ -708,36 +721,37 @@ class Terminal(object): def __detect_parent_terminal_size(self): try: - TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912) - packed = struct.pack(b'HHHH', 0, 0, 0, 0) + TIOCGWINSZ = getattr(termios, "TIOCGWINSZ", 1074295912) + packed = struct.pack(b"HHHH", 0, 0, 0, 0) ioctl = fcntl.ioctl(sys.stdin.fileno(), TIOCGWINSZ, packed) - return struct.unpack(b'HHHH', ioctl)[0:2] + return struct.unpack(b"HHHH", ioctl)[0:2] except IOError: # Return a default value of 24x80 return 24, 80 + # <---- Internal API ------------------------------------------------- # ----- Public API --------------------------------------------------> def getwinsize(self): - ''' + """ This returns the terminal window size of the child tty. The return value is a tuple of (rows, cols). Thank you for the shortcut PEXPECT - ''' + """ if self.child_fd is None: raise TerminalException( - 'Can\'t check the size of the terminal since we\'re not ' - 'connected to the child process.' + "Can't check the size of the terminal since we're not " + "connected to the child process." ) - TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912) - packed = struct.pack(b'HHHH', 0, 0, 0, 0) + TIOCGWINSZ = getattr(termios, "TIOCGWINSZ", 1074295912) + packed = struct.pack(b"HHHH", 0, 0, 0, 0) ioctl = fcntl.ioctl(self.child_fd, TIOCGWINSZ, packed) - return struct.unpack(b'HHHH', ioctl)[0:2] + return struct.unpack(b"HHHH", ioctl)[0:2] def setwinsize(self, rows, cols): - ''' + """ This sets the terminal window size of the child tty. This will cause a SIGWINCH signal to be sent to the child. This does not change the physical window size. It changes the size reported to @@ -745,7 +759,7 @@ class Terminal(object): respond to the SIGWINCH signal. Thank you for the shortcut PEXPECT - ''' + """ # Check for buggy platforms. Some Python versions on some platforms # (notably OSF1 Alpha and RedHat 7.1) truncate the value for # termios.TIOCSWINSZ. It is not clear why this happens. @@ -755,32 +769,34 @@ class Terminal(object): # Newer versions of Linux have totally different values for # TIOCSWINSZ. # Note that this fix is a hack. - TIOCSWINSZ = getattr(termios, 'TIOCSWINSZ', -2146929561) + TIOCSWINSZ = getattr(termios, "TIOCSWINSZ", -2146929561) if TIOCSWINSZ == 2148037735: # Same bits, but with sign. TIOCSWINSZ = -2146929561 # Note, assume ws_xpixel and ws_ypixel are zero. - packed = struct.pack(b'HHHH', rows, cols, 0, 0) + packed = struct.pack(b"HHHH", rows, cols, 0, 0) fcntl.ioctl(self.child_fd, TIOCSWINSZ, packed) - def isalive(self, - _waitpid=os.waitpid, - _wnohang=os.WNOHANG, - _wifexited=os.WIFEXITED, - _wexitstatus=os.WEXITSTATUS, - _wifsignaled=os.WIFSIGNALED, - _wifstopped=os.WIFSTOPPED, - _wtermsig=os.WTERMSIG, - _os_error=os.error, - _errno_echild=errno.ECHILD, - _terminal_exception=TerminalException): - ''' + def isalive( + self, + _waitpid=os.waitpid, + _wnohang=os.WNOHANG, + _wifexited=os.WIFEXITED, + _wexitstatus=os.WEXITSTATUS, + _wifsignaled=os.WIFSIGNALED, + _wifstopped=os.WIFSTOPPED, + _wtermsig=os.WTERMSIG, + _os_error=os.error, + _errno_echild=errno.ECHILD, + _terminal_exception=TerminalException, + ): + """ This tests if the child process is running or not. This is non-blocking. If the child was terminated then this will read the exitstatus or signalstatus of the child. This returns True if the child process appears to be running or False if not. It can take literally SECONDS for Solaris to return the right status. - ''' + """ if self.terminated: return False @@ -801,8 +817,8 @@ class Terminal(object): if err.errno == _errno_echild: raise _terminal_exception( 'isalive() encountered condition where "terminated" ' - 'is 0, but there was no child process. Did someone ' - 'else call waitpid() on our process?' + "is 0, but there was no child process. Did someone " + "else call waitpid() on our process?" ) else: six.reraise(*sys.exc_info()) @@ -819,9 +835,9 @@ class Terminal(object): # This should never happen... if exc.errno == _errno_echild: raise _terminal_exception( - 'isalive() encountered condition that should ' - 'never happen. There was no child process. Did ' - 'someone else call waitpid() on our process?' + "isalive() encountered condition that should " + "never happen. There was no child process. Did " + "someone else call waitpid() on our process?" ) else: six.reraise(*sys.exc_info()) @@ -849,19 +865,19 @@ class Terminal(object): self.terminated = True elif _wifstopped(status): raise _terminal_exception( - 'isalive() encountered condition where child process is ' - 'stopped. This is not supported. Is some other process ' - 'attempting job control with our child pid?' + "isalive() encountered condition where child process is " + "stopped. This is not supported. Is some other process " + "attempting job control with our child pid?" ) return False def terminate(self, force=False): - ''' + """ This forces a child process to terminate. It starts nicely with SIGHUP and SIGINT. If "force" is True then moves onto SIGKILL. This returns True if the child was terminated. This returns False if the child could not be terminated. - ''' + """ if not self.closed: self.close(terminate=False) @@ -900,11 +916,11 @@ class Terminal(object): return False def wait(self): - ''' + """ This waits until the child exits internally consuming any remaining output from the child, thus, no blocking forever because the child has unread data. - ''' + """ if self.isalive(): while self.isalive(): stdout, stderr = self.recv() @@ -913,20 +929,21 @@ class Terminal(object): if stderr is None: break else: - raise TerminalException('Cannot wait for dead child process.') + raise TerminalException("Cannot wait for dead child process.") return self.exitstatus def send_signal(self, sig): - ''' + """ Send a signal to the process - ''' + """ os.kill(self.pid, sig) def kill(self): - ''' + """ Kill the process with SIGKILL - ''' + """ self.send_signal(signal.SIGKILL) + # <---- Public API --------------------------------------------------- # <---- Linux Methods ---------------------------------------------------- @@ -946,6 +963,9 @@ class Terminal(object): if self.isalive() and _ACTIVE is not None: # Child is still running, keep us alive until we can wait on it. _ACTIVE.append(self) + # pylint: enable=W1701 # <---- Cleanup!!! ------------------------------------------------------- + + # <---- Platform Specific Methods -------------------------------------------- diff --git a/salt/utils/vt_helper.py b/salt/utils/vt_helper.py index da1fb236489..ff7210bc7b0 100644 --- a/salt/utils/vt_helper.py +++ b/salt/utils/vt_helper.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" salt.utils.vt_helper ~~~~~~~~~~~~~~~~~~~~ @@ -8,7 +8,7 @@ This module provides the SSHConnection to expose an SSH connection object allowing users to programmatically execute commands on a remote server using Salt VT. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -19,26 +19,29 @@ import re # Import salt's Libs from salt.utils.vt import Terminal, TerminalException -SSH_PASSWORD_PROMPT_RE = re.compile(r'(?:.*)[Pp]assword(?: for .*)?:', re.M) -KEY_VALID_RE = re.compile(r'.*\(yes\/no\).*') +SSH_PASSWORD_PROMPT_RE = re.compile(r"(?:.*)[Pp]assword(?: for .*)?:", re.M) +KEY_VALID_RE = re.compile(r".*\(yes\/no\).*") log = logging.getLogger(__name__) class SSHConnection(object): - ''' + """ SSH Connection to a remote server. - ''' - def __init__(self, - username='salt', - password='password', - host='localhost', - key_accept=False, - prompt=r'(Cmd)', - passwd_retries=3, - linesep=os.linesep, - ssh_args=''): - ''' + """ + + def __init__( + self, + username="salt", + password="password", + host="localhost", + key_accept=False, + prompt=r"(Cmd)", + passwd_retries=3, + linesep=os.linesep, + ssh_args="", + ): + """ Establishes a connection to the remote server. The format for parameters is: @@ -60,16 +63,17 @@ class SSHConnection(object): commands to the server. Defaults to os.linesep. ssh_args (string): Extra ssh args to use with ssh. Example: '-o PubkeyAuthentication=no' - ''' + """ self.conn = Terminal( - 'ssh {0} -l {1} {2}'.format(ssh_args, username, host), + "ssh {0} -l {1} {2}".format(ssh_args, username, host), shell=True, log_stdout=True, - log_stdout_level='trace', + log_stdout_level="trace", log_stderr=True, - log_stderr_level='trace', + log_stderr_level="trace", stream_stdout=False, - stream_stderr=False) + stream_stderr=False, + ) sent_passwd = 0 self.prompt_re = re.compile(prompt) @@ -80,39 +84,40 @@ class SSHConnection(object): if stdout and SSH_PASSWORD_PROMPT_RE.search(stdout): if not password: - log.error('Failure while authentication.') + log.error("Failure while authentication.") raise TerminalException( - 'Permission denied, no authentication information') + "Permission denied, no authentication information" + ) if sent_passwd < passwd_retries: self.conn.sendline(password, self.linesep) sent_passwd += 1 continue else: # asking for a password, and we can't seem to send it - raise TerminalException('Password authentication failed') + raise TerminalException("Password authentication failed") elif stdout and KEY_VALID_RE.search(stdout): # Connecting to this server for the first time # and need to accept key if key_accept: - log.info('Adding %s to known_hosts', host) - self.conn.sendline('yes') + log.info("Adding %s to known_hosts", host) + self.conn.sendline("yes") continue else: - self.conn.sendline('no') + self.conn.sendline("no") elif stdout and self.prompt_re.search(stdout): # Auth success! # We now have a prompt break def sendline(self, cmd): - ''' + """ Send this command to the server and return a tuple of the output and the stderr. The format for parameters is: cmd (string): The command to send to the sever. - ''' + """ self.conn.sendline(cmd, self.linesep) # saw_prompt = False @@ -124,16 +129,16 @@ class SSHConnection(object): if stdout: ret_stdout.append(stdout) if stderr: - log.debug('Error while executing command.') + log.debug("Error while executing command.") ret_stderr.append(stderr) if stdout and self.prompt_re.search(stdout): break - return ''.join(ret_stdout), ''.join(ret_stderr) + return "".join(ret_stdout), "".join(ret_stderr) def close_connection(self): - ''' + """ Close the server connection - ''' + """ self.conn.close(terminate=True, kill=True) diff --git a/salt/utils/win_dacl.py b/salt/utils/win_dacl.py index 037830bd3d9..8eaa5593d0a 100644 --- a/salt/utils/win_dacl.py +++ b/salt/utils/win_dacl.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" ============ Windows DACL ============ @@ -126,49 +126,53 @@ should match what you see when you look at the properties for an object. - this_key_subkeys: Applies to this key and all subkeys - subkeys_only: Applies to all subkeys beneath this object -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt libs -from salt.exceptions import CommandExecutionError, SaltInvocationError import salt.utils.platform import salt.utils.win_functions +# Import Salt libs +from salt.exceptions import CommandExecutionError, SaltInvocationError +from salt.ext import six + # Import 3rd-party libs from salt.ext.six.moves import range -from salt.ext import six + HAS_WIN32 = False try: import win32security import win32con import win32api import pywintypes + HAS_WIN32 = True except ImportError: pass log = logging.getLogger(__name__) -__virtualname__ = 'dacl' +__virtualname__ = "dacl" def __virtual__(): - ''' + """ Only load if Win32 Libraries are installed - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'win_dacl: Requires Windows' + return False, "win_dacl: Requires Windows" if not HAS_WIN32: - return False, 'win_dacl: Requires pywin32' + return False, "win_dacl: Requires pywin32" return __virtualname__ def flags(instantiated=True): - ''' + """ Helper function for instantiating a Flags object Args: @@ -180,176 +184,177 @@ def flags(instantiated=True): Returns: object: An instance of the Flags object or its definition - ''' + """ if not HAS_WIN32: return class Flags(object): - ''' + """ Object containing all the flags for dealing with Windows permissions - ''' + """ + # Flag Dicts ace_perms = { - 'file': { - 'basic': { - 0x1f01ff: 'Full control', - 0x1301bf: 'Modify', - 0x1201bf: 'Read & execute with write', - 0x1200a9: 'Read & execute', - 0x120089: 'Read', - 0x100116: 'Write', - 'full_control': 0x1f01ff, - 'modify': 0x1301bf, - 'read_execute': 0x1200a9, - 'read': 0x120089, - 'write': 0x100116, + "file": { + "basic": { + 0x1F01FF: "Full control", + 0x1301BF: "Modify", + 0x1201BF: "Read & execute with write", + 0x1200A9: "Read & execute", + 0x120089: "Read", + 0x100116: "Write", + "full_control": 0x1F01FF, + "modify": 0x1301BF, + "read_execute": 0x1200A9, + "read": 0x120089, + "write": 0x100116, }, - 'advanced': { + "advanced": { # Advanced - 0x0001: 'List folder / read data', - 0x0002: 'Create files / write data', - 0x0004: 'Create folders / append data', - 0x0008: 'Read extended attributes', - 0x0010: 'Write extended attributes', - 0x0020: 'Traverse folder / execute file', - 0x0040: 'Delete subfolders and files', - 0x0080: 'Read attributes', - 0x0100: 'Write attributes', - 0x10000: 'Delete', - 0x20000: 'Read permissions', - 0x40000: 'Change permissions', - 0x80000: 'Take ownership', + 0x0001: "List folder / read data", + 0x0002: "Create files / write data", + 0x0004: "Create folders / append data", + 0x0008: "Read extended attributes", + 0x0010: "Write extended attributes", + 0x0020: "Traverse folder / execute file", + 0x0040: "Delete subfolders and files", + 0x0080: "Read attributes", + 0x0100: "Write attributes", + 0x10000: "Delete", + 0x20000: "Read permissions", + 0x40000: "Change permissions", + 0x80000: "Take ownership", # 0x100000: 'SYNCHRONIZE', # This is in all of them - 'list_folder': 0x0001, - 'read_data': 0x0001, - 'create_files': 0x0002, - 'write_data': 0x0002, - 'create_folders': 0x0004, - 'append_data': 0x0004, - 'read_ea': 0x0008, - 'write_ea': 0x0010, - 'traverse_folder': 0x0020, - 'execute_file': 0x0020, - 'delete_subfolders_files': 0x0040, - 'read_attributes': 0x0080, - 'write_attributes': 0x0100, - 'delete': 0x10000, - 'read_permissions': 0x20000, - 'change_permissions': 0x40000, - 'take_ownership': 0x80000, + "list_folder": 0x0001, + "read_data": 0x0001, + "create_files": 0x0002, + "write_data": 0x0002, + "create_folders": 0x0004, + "append_data": 0x0004, + "read_ea": 0x0008, + "write_ea": 0x0010, + "traverse_folder": 0x0020, + "execute_file": 0x0020, + "delete_subfolders_files": 0x0040, + "read_attributes": 0x0080, + "write_attributes": 0x0100, + "delete": 0x10000, + "read_permissions": 0x20000, + "change_permissions": 0x40000, + "take_ownership": 0x80000, }, }, - 'registry': { - 'basic': { - 0xf003f: 'Full Control', - 0x20019: 'Read', - 0x20006: 'Write', + "registry": { + "basic": { + 0xF003F: "Full Control", + 0x20019: "Read", + 0x20006: "Write", # Generic Values (These sometimes get hit) - 0x10000000: 'Full Control', - 0x20000000: 'Execute', - 0x40000000: 'Write', - 0xffffffff80000000: 'Read', - 'full_control': 0xf003f, - 'read': 0x20019, - 'write': 0x20006, + 0x10000000: "Full Control", + 0x20000000: "Execute", + 0x40000000: "Write", + 0xFFFFFFFF80000000: "Read", + "full_control": 0xF003F, + "read": 0x20019, + "write": 0x20006, }, - 'advanced': { + "advanced": { # Advanced - 0x0001: 'Query Value', - 0x0002: 'Set Value', - 0x0004: 'Create Subkey', - 0x0008: 'Enumerate Subkeys', - 0x0010: 'Notify', - 0x0020: 'Create Link', - 0x10000: 'Delete', - 0x20000: 'Read Control', - 0x40000: 'Write DAC', - 0x80000: 'Write Owner', - 'query_value': 0x0001, - 'set_value': 0x0002, - 'create_subkey': 0x0004, - 'enum_subkeys': 0x0008, - 'notify': 0x0010, - 'create_link': 0x0020, - 'delete': 0x10000, - 'read_control': 0x20000, - 'write_dac': 0x40000, - 'write_owner': 0x80000, + 0x0001: "Query Value", + 0x0002: "Set Value", + 0x0004: "Create Subkey", + 0x0008: "Enumerate Subkeys", + 0x0010: "Notify", + 0x0020: "Create Link", + 0x10000: "Delete", + 0x20000: "Read Control", + 0x40000: "Write DAC", + 0x80000: "Write Owner", + "query_value": 0x0001, + "set_value": 0x0002, + "create_subkey": 0x0004, + "enum_subkeys": 0x0008, + "notify": 0x0010, + "create_link": 0x0020, + "delete": 0x10000, + "read_control": 0x20000, + "write_dac": 0x40000, + "write_owner": 0x80000, }, }, - 'share': { - 'basic': { - 0x1f01ff: 'Full control', - 0x1301bf: 'Change', - 0x1200a9: 'Read', - 'full_control': 0x1f01ff, - 'change': 0x1301bf, - 'read': 0x1200a9, + "share": { + "basic": { + 0x1F01FF: "Full control", + 0x1301BF: "Change", + 0x1200A9: "Read", + "full_control": 0x1F01FF, + "change": 0x1301BF, + "read": 0x1200A9, }, - 'advanced': {}, # No 'advanced' for shares, needed for lookup + "advanced": {}, # No 'advanced' for shares, needed for lookup }, - 'printer': { - 'basic': { - 0x20008: 'Print', - 0xf000c: 'Manage this printer', - 0xf0030: 'Manage documents', - 'print': 0x20008, - 'manage_printer': 0xf000c, - 'manage_documents': 0xf0030, + "printer": { + "basic": { + 0x20008: "Print", + 0xF000C: "Manage this printer", + 0xF0030: "Manage documents", + "print": 0x20008, + "manage_printer": 0xF000C, + "manage_documents": 0xF0030, }, - 'advanced': { + "advanced": { # Advanced - 0x10004: 'Manage this printer', - 0x0008: 'Print', - 0x20000: 'Read permissions', - 0x40000: 'Change permissions', - 0x80000: 'Take ownership', - 'manage_printer': 0x10004, - 'print': 0x0008, - 'read_permissions': 0x20000, - 'change_permissions': 0x40000, - 'take_ownership': 0x80000, + 0x10004: "Manage this printer", + 0x0008: "Print", + 0x20000: "Read permissions", + 0x40000: "Change permissions", + 0x80000: "Take ownership", + "manage_printer": 0x10004, + "print": 0x0008, + "read_permissions": 0x20000, + "change_permissions": 0x40000, + "take_ownership": 0x80000, }, }, - 'service': { - 'basic': { - 0xf01ff: 'Full Control', - 0x2008f: 'Read & Write', - 0x2018d: 'Read', - 0x20002: 'Write', - 'full_control': 0xf01ff, - 'read_write': 0x2008f, - 'read': 0x2018d, - 'write': 0x20002, + "service": { + "basic": { + 0xF01FF: "Full Control", + 0x2008F: "Read & Write", + 0x2018D: "Read", + 0x20002: "Write", + "full_control": 0xF01FF, + "read_write": 0x2008F, + "read": 0x2018D, + "write": 0x20002, }, - 'advanced': { - 0x0001: 'Query Config', - 0x0002: 'Change Config', - 0x0004: 'Query Status', - 0x0008: 'Enumerate Dependents', - 0x0010: 'Start', - 0x0020: 'Stop', - 0x0040: 'Pause/Resume', - 0x0080: 'Interrogate', - 0x0100: 'User-Defined Control', + "advanced": { + 0x0001: "Query Config", + 0x0002: "Change Config", + 0x0004: "Query Status", + 0x0008: "Enumerate Dependents", + 0x0010: "Start", + 0x0020: "Stop", + 0x0040: "Pause/Resume", + 0x0080: "Interrogate", + 0x0100: "User-Defined Control", # 0x10000: 'Delete', # Not visible in the GUI - 0x20000: 'Read Permissions', - 0x40000: 'Change Permissions', - 0x80000: 'Change Owner', - 'query_config': 0x0001, - 'change_config': 0x0002, - 'query_status': 0x0004, - 'enum_dependents': 0x0008, - 'start': 0x0010, - 'stop': 0x0020, - 'pause_resume': 0x0040, - 'interrogate': 0x0080, - 'user_defined': 0x0100, - 'read_permissions': 0x20000, - 'change_permissions': 0x40000, - 'change_owner': 0x80000, + 0x20000: "Read Permissions", + 0x40000: "Change Permissions", + 0x80000: "Change Owner", + "query_config": 0x0001, + "change_config": 0x0002, + "query_status": 0x0004, + "enum_dependents": 0x0008, + "start": 0x0010, + "stop": 0x0020, + "pause_resume": 0x0040, + "interrogate": 0x0080, + "user_defined": 0x0100, + "read_permissions": 0x20000, + "change_permissions": 0x40000, + "change_owner": 0x80000, }, - } + }, } # These denote inheritance @@ -363,80 +368,80 @@ def flags(instantiated=True): # 0x0004 : win32security.NO_PROPAGATE_INHERIT_ACE # 0x0008 : win32security.INHERIT_ONLY_ACE ace_prop = { - 'file': { + "file": { # for report - 0x0000: 'Not Inherited (file)', - 0x0001: 'This folder and files', - 0x0002: 'This folder and subfolders', - 0x0003: 'This folder, subfolders and files', - 0x0006: 'This folder only', - 0x0009: 'Files only', - 0x000a: 'Subfolders only', - 0x000b: 'Subfolders and files only', - 0x0010: 'Inherited (file)', + 0x0000: "Not Inherited (file)", + 0x0001: "This folder and files", + 0x0002: "This folder and subfolders", + 0x0003: "This folder, subfolders and files", + 0x0006: "This folder only", + 0x0009: "Files only", + 0x000A: "Subfolders only", + 0x000B: "Subfolders and files only", + 0x0010: "Inherited (file)", # for setting - 'this_folder_only': 0x0006, - 'this_folder_subfolders_files': 0x0003, - 'this_folder_subfolders': 0x0002, - 'this_folder_files': 0x0001, - 'subfolders_files': 0x000b, - 'subfolders_only': 0x000a, - 'files_only': 0x0009, + "this_folder_only": 0x0006, + "this_folder_subfolders_files": 0x0003, + "this_folder_subfolders": 0x0002, + "this_folder_files": 0x0001, + "subfolders_files": 0x000B, + "subfolders_only": 0x000A, + "files_only": 0x0009, }, - 'registry': { - 0x0000: 'Not Inherited', - 0x0002: 'This key and subkeys', - 0x0006: 'This key only', - 0x000a: 'Subkeys only', - 0x0010: 'Inherited', - 'this_key_only': 0x0006, - 'this_key_subkeys': 0x0002, - 'subkeys_only': 0x000a, + "registry": { + 0x0000: "Not Inherited", + 0x0002: "This key and subkeys", + 0x0006: "This key only", + 0x000A: "Subkeys only", + 0x0010: "Inherited", + "this_key_only": 0x0006, + "this_key_subkeys": 0x0002, + "subkeys_only": 0x000A, }, - 'registry32': { - 0x0000: 'Not Inherited', - 0x0002: 'This key and subkeys', - 0x0006: 'This key only', - 0x000a: 'Subkeys only', - 0x0010: 'Inherited', - 'this_key_only': 0x0006, - 'this_key_subkeys': 0x0002, - 'subkeys_only': 0x000a, + "registry32": { + 0x0000: "Not Inherited", + 0x0002: "This key and subkeys", + 0x0006: "This key only", + 0x000A: "Subkeys only", + 0x0010: "Inherited", + "this_key_only": 0x0006, + "this_key_subkeys": 0x0002, + "subkeys_only": 0x000A, }, } ace_type = { - 'grant': win32security.ACCESS_ALLOWED_ACE_TYPE, - 'deny': win32security.ACCESS_DENIED_ACE_TYPE, - win32security.ACCESS_ALLOWED_ACE_TYPE: 'grant', - win32security.ACCESS_DENIED_ACE_TYPE: 'deny', + "grant": win32security.ACCESS_ALLOWED_ACE_TYPE, + "deny": win32security.ACCESS_DENIED_ACE_TYPE, + win32security.ACCESS_ALLOWED_ACE_TYPE: "grant", + win32security.ACCESS_DENIED_ACE_TYPE: "deny", } element = { - 'dacl': win32security.DACL_SECURITY_INFORMATION, - 'group': win32security.GROUP_SECURITY_INFORMATION, - 'owner': win32security.OWNER_SECURITY_INFORMATION, + "dacl": win32security.DACL_SECURITY_INFORMATION, + "group": win32security.GROUP_SECURITY_INFORMATION, + "owner": win32security.OWNER_SECURITY_INFORMATION, } inheritance = { - 'protected': win32security.PROTECTED_DACL_SECURITY_INFORMATION, - 'unprotected': win32security.UNPROTECTED_DACL_SECURITY_INFORMATION, + "protected": win32security.PROTECTED_DACL_SECURITY_INFORMATION, + "unprotected": win32security.UNPROTECTED_DACL_SECURITY_INFORMATION, } obj_type = { - 'file': win32security.SE_FILE_OBJECT, - 'service': win32security.SE_SERVICE, - 'printer': win32security.SE_PRINTER, - 'registry': win32security.SE_REGISTRY_KEY, - 'registry32': win32security.SE_REGISTRY_WOW64_32KEY, - 'share': win32security.SE_LMSHARE, + "file": win32security.SE_FILE_OBJECT, + "service": win32security.SE_SERVICE, + "printer": win32security.SE_PRINTER, + "registry": win32security.SE_REGISTRY_KEY, + "registry32": win32security.SE_REGISTRY_WOW64_32KEY, + "share": win32security.SE_LMSHARE, } return Flags() if instantiated else Flags -def dacl(obj_name=None, obj_type='file'): - ''' +def dacl(obj_name=None, obj_type="file"): + """ Helper function for instantiating a Dacl class. Args: @@ -450,17 +455,18 @@ def dacl(obj_name=None, obj_type='file'): Returns: object: An instantiated Dacl object - ''' + """ if not HAS_WIN32: return class Dacl(flags(False)): - ''' + """ DACL Object - ''' - def __init__(self, obj_name=None, obj_type='file'): - ''' + """ + + def __init__(self, obj_name=None, obj_type="file"): + """ Either load the DACL from the passed object or create an empty DACL. If `obj_name` is not passed, an empty DACL is created. @@ -485,26 +491,28 @@ def dacl(obj_name=None, obj_type='file'): # Load the DACL of the named object dacl = Dacl(obj_name, obj_type) - ''' + """ # Validate obj_type if obj_type.lower() not in self.obj_type: raise SaltInvocationError( - 'Invalid "obj_type" passed: {0}'.format(obj_type)) + 'Invalid "obj_type" passed: {0}'.format(obj_type) + ) self.dacl_type = obj_type.lower() if obj_name is None: self.dacl = win32security.ACL() else: - if 'registry' in self.dacl_type: + if "registry" in self.dacl_type: obj_name = self.get_reg_name(obj_name) try: sd = win32security.GetNamedSecurityInfo( - obj_name, self.obj_type[self.dacl_type], self.element['dacl']) + obj_name, self.obj_type[self.dacl_type], self.element["dacl"] + ) except pywintypes.error as exc: - if 'The system cannot find' in exc.strerror: - msg = 'System cannot find {0}'.format(obj_name) + if "The system cannot find" in exc.strerror: + msg = "System cannot find {0}".format(obj_name) log.exception(msg) raise CommandExecutionError(msg) raise @@ -514,7 +522,7 @@ def dacl(obj_name=None, obj_type='file'): self.dacl = win32security.ACL() def get_reg_name(self, obj_name): - ''' + """ Take the obj_name and convert the hive to a valid registry hive. Args: @@ -550,43 +558,44 @@ def dacl(obj_name=None, obj_type='file'): valid_key = dacl.get_reg_name('HKLM\\SOFTWARE\\salt') # Returns: MACHINE\\SOFTWARE\\salt - ''' + """ # Make sure the hive is correct # Should be MACHINE, USERS, CURRENT_USER, or CLASSES_ROOT hives = { # MACHINE - 'HKEY_LOCAL_MACHINE': 'MACHINE', - 'MACHINE': 'MACHINE', - 'HKLM': 'MACHINE', + "HKEY_LOCAL_MACHINE": "MACHINE", + "MACHINE": "MACHINE", + "HKLM": "MACHINE", # USERS - 'HKEY_USERS': 'USERS', - 'USERS': 'USERS', - 'HKU': 'USERS', + "HKEY_USERS": "USERS", + "USERS": "USERS", + "HKU": "USERS", # CURRENT_USER - 'HKEY_CURRENT_USER': 'CURRENT_USER', - 'CURRENT_USER': 'CURRENT_USER', - 'HKCU': 'CURRENT_USER', + "HKEY_CURRENT_USER": "CURRENT_USER", + "CURRENT_USER": "CURRENT_USER", + "HKCU": "CURRENT_USER", # CLASSES ROOT - 'HKEY_CLASSES_ROOT': 'CLASSES_ROOT', - 'CLASSES_ROOT': 'CLASSES_ROOT', - 'HKCR': 'CLASSES_ROOT', + "HKEY_CLASSES_ROOT": "CLASSES_ROOT", + "CLASSES_ROOT": "CLASSES_ROOT", + "HKCR": "CLASSES_ROOT", } - reg = obj_name.split('\\') + reg = obj_name.split("\\") passed_hive = reg.pop(0) try: valid_hive = hives[passed_hive.upper()] except KeyError: - log.exception('Invalid Registry Hive: %s', passed_hive) + log.exception("Invalid Registry Hive: %s", passed_hive) raise CommandExecutionError( - 'Invalid Registry Hive: {0}'.format(passed_hive)) + "Invalid Registry Hive: {0}".format(passed_hive) + ) reg.insert(0, valid_hive) - return r'\\'.join(reg) + return r"\\".join(reg) def add_ace(self, principal, access_mode, permissions, applies_to): - ''' + """ Add an ACE to the DACL Args: @@ -616,62 +625,66 @@ def dacl(obj_name=None, obj_type='file'): dacl = Dacl(obj_type=obj_type) dacl.add_ace(sid, access_mode, permission, applies_to) dacl.save(obj_name, protected) - ''' + """ sid = get_sid(principal) if self.dacl is None: - raise SaltInvocationError( - 'You must load the DACL before adding an ACE') + raise SaltInvocationError("You must load the DACL before adding an ACE") # Get the permission flag perm_flag = 0 if isinstance(permissions, six.string_types): try: - perm_flag = self.ace_perms[self.dacl_type]['basic'][permissions] + perm_flag = self.ace_perms[self.dacl_type]["basic"][permissions] except KeyError as exc: - msg = 'Invalid permission specified: {0}'.format(permissions) + msg = "Invalid permission specified: {0}".format(permissions) log.exception(msg) raise CommandExecutionError(msg, exc) else: try: for perm in permissions: - perm_flag |= self.ace_perms[self.dacl_type]['advanced'][perm] + perm_flag |= self.ace_perms[self.dacl_type]["advanced"][perm] except KeyError as exc: - msg = 'Invalid permission specified: {0}'.format(perm) + msg = "Invalid permission specified: {0}".format(perm) log.exception(msg) raise CommandExecutionError(msg, exc) - if access_mode.lower() not in ['grant', 'deny']: - raise SaltInvocationError('Invalid Access Mode: {0}'.format(access_mode)) + if access_mode.lower() not in ["grant", "deny"]: + raise SaltInvocationError( + "Invalid Access Mode: {0}".format(access_mode) + ) # Add ACE to the DACL # Grant or Deny try: - if access_mode.lower() == 'grant': + if access_mode.lower() == "grant": self.dacl.AddAccessAllowedAceEx( win32security.ACL_REVISION_DS, # Some types don't support propagation # May need to use 0x0000 instead of None self.ace_prop.get(self.dacl_type, {}).get(applies_to), perm_flag, - sid) - elif access_mode.lower() == 'deny': + sid, + ) + elif access_mode.lower() == "deny": self.dacl.AddAccessDeniedAceEx( win32security.ACL_REVISION_DS, self.ace_prop.get(self.dacl_type, {}).get(applies_to), perm_flag, - sid) + sid, + ) else: - log.exception('Invalid access mode: %s', access_mode) + log.exception("Invalid access mode: %s", access_mode) raise SaltInvocationError( - 'Invalid access mode: {0}'.format(access_mode)) + "Invalid access mode: {0}".format(access_mode) + ) except Exception as exc: # pylint: disable=broad-except - return False, 'Error: {0}'.format(exc) + return False, "Error: {0}".format(exc) return True def order_acl(self): - ''' + """ Put the ACEs in the ACL in the proper order. This is necessary because the add_ace function puts ACEs at the end of the list without regard for order. This will cause the following Windows @@ -701,7 +714,7 @@ def dacl(obj_name=None, obj_type='file'): dacl.add_ace(sid, access_mode, applies_to, permission) dacl.order_acl() dacl.save(obj_name, protected) - ''' + """ new_dacl = Dacl() deny_dacl = Dacl() deny_obj_dacl = Dacl() @@ -714,59 +727,45 @@ def dacl(obj_name=None, obj_type='file'): if ace[0][1] & win32security.INHERITED_ACE == 0: if ace[0][0] == win32security.ACCESS_DENIED_ACE_TYPE: deny_dacl.dacl.AddAccessDeniedAceEx( - win32security.ACL_REVISION_DS, - ace[0][1], - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace[0][1], ace[1], ace[2] + ) elif ace[0][0] == win32security.ACCESS_DENIED_OBJECT_ACE_TYPE: deny_obj_dacl.dacl.AddAccessDeniedAceEx( - win32security.ACL_REVISION_DS, - ace[0][1], - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace[0][1], ace[1], ace[2] + ) elif ace[0][0] == win32security.ACCESS_ALLOWED_ACE_TYPE: allow_dacl.dacl.AddAccessAllowedAceEx( - win32security.ACL_REVISION_DS, - ace[0][1], - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace[0][1], ace[1], ace[2] + ) elif ace[0][0] == win32security.ACCESS_ALLOWED_OBJECT_ACE_TYPE: allow_obj_dacl.dacl.AddAccessAllowedAceEx( - win32security.ACL_REVISION_DS, - ace[0][1], - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace[0][1], ace[1], ace[2] + ) # Load Inherited ACEs last for i in range(0, self.dacl.GetAceCount()): ace = self.dacl.GetAce(i) - if ace[0][1] & win32security.INHERITED_ACE == \ - win32security.INHERITED_ACE: + if ( + ace[0][1] & win32security.INHERITED_ACE + == win32security.INHERITED_ACE + ): ace_prop = ace[0][1] ^ win32security.INHERITED_ACE if ace[0][0] == win32security.ACCESS_DENIED_ACE_TYPE: deny_dacl.dacl.AddAccessDeniedAceEx( - win32security.ACL_REVISION_DS, - ace_prop, - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace_prop, ace[1], ace[2] + ) elif ace[0][0] == win32security.ACCESS_DENIED_OBJECT_ACE_TYPE: deny_obj_dacl.dacl.AddAccessDeniedAceEx( - win32security.ACL_REVISION_DS, - ace_prop, - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace_prop, ace[1], ace[2] + ) elif ace[0][0] == win32security.ACCESS_ALLOWED_ACE_TYPE: allow_dacl.dacl.AddAccessAllowedAceEx( - win32security.ACL_REVISION_DS, - ace_prop, - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace_prop, ace[1], ace[2] + ) elif ace[0][0] == win32security.ACCESS_ALLOWED_OBJECT_ACE_TYPE: allow_obj_dacl.dacl.AddAccessAllowedAceEx( - win32security.ACL_REVISION_DS, - ace_prop, - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace_prop, ace[1], ace[2] + ) # Combine ACEs in the proper order # Deny, Deny Object, Allow, Allow Object @@ -774,10 +773,8 @@ def dacl(obj_name=None, obj_type='file'): for i in range(0, deny_dacl.dacl.GetAceCount()): ace = deny_dacl.dacl.GetAce(i) new_dacl.dacl.AddAccessDeniedAceEx( - win32security.ACL_REVISION_DS, - ace[0][1], - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace[0][1], ace[1], ace[2] + ) # Deny Object for i in range(0, deny_obj_dacl.dacl.GetAceCount()): @@ -786,16 +783,15 @@ def dacl(obj_name=None, obj_type='file'): win32security.ACL_REVISION_DS, ace[0][1] ^ win32security.INHERITED_ACE, ace[1], - ace[2]) + ace[2], + ) # Allow for i in range(0, allow_dacl.dacl.GetAceCount()): ace = allow_dacl.dacl.GetAce(i) new_dacl.dacl.AddAccessAllowedAceEx( - win32security.ACL_REVISION_DS, - ace[0][1], - ace[1], - ace[2]) + win32security.ACL_REVISION_DS, ace[0][1], ace[1], ace[2] + ) # Allow Object for i in range(0, allow_obj_dacl.dacl.GetAceCount()): @@ -804,13 +800,14 @@ def dacl(obj_name=None, obj_type='file'): win32security.ACL_REVISION_DS, ace[0][1] ^ win32security.INHERITED_ACE, ace[1], - ace[2]) + ace[2], + ) # Set the new dacl self.dacl = new_dacl.dacl def get_ace(self, principal): - ''' + """ Get the ACE for a specific principal. Args: @@ -828,7 +825,7 @@ def dacl(obj_name=None, obj_type='file'): dacl = Dacl(obj_type=obj_type) dacl.get_ace() - ''' + """ principal = get_name(principal) aces = self.list_aces() @@ -841,7 +838,7 @@ def dacl(obj_name=None, obj_type='file'): return ret def list_aces(self): - ''' + """ List all Entries in the dacl. Returns: @@ -853,9 +850,8 @@ def dacl(obj_name=None, obj_type='file'): dacl = Dacl('C:\\Temp') dacl.list_aces() - ''' - ret = {'Inherited': {}, - 'Not Inherited': {}} + """ + ret = {"Inherited": {}, "Not Inherited": {}} # loop through each ACE in the DACL for i in range(0, self.dacl.GetAceCount()): @@ -866,22 +862,20 @@ def dacl(obj_name=None, obj_type='file'): if user in ret[inheritance]: ret[inheritance][user][a_type] = { - 'applies to': a_prop, - 'permissions': a_perms, + "applies to": a_prop, + "permissions": a_perms, } else: ret[inheritance][user] = { - a_type: { - 'applies to': a_prop, - 'permissions': a_perms, - }} + a_type: {"applies to": a_prop, "permissions": a_perms} + } return ret def _ace_to_dict(self, ace): - ''' + """ Helper function for creating the ACE return dictionary - ''' + """ # Get the principal from the sid (object sid) sid = win32security.ConvertSidToStringSid(ace[2]) try: @@ -896,10 +890,10 @@ def dacl(obj_name=None, obj_type='file'): inherited = ace[0][1] & win32security.INHERITED_ACE == 16 # Ace Propagation - ace_prop = 'NA' + ace_prop = "NA" # Get the ace propagation properties - if self.dacl_type in ['file', 'registry', 'registry32']: + if self.dacl_type in ["file", "registry", "registry32"]: ace_prop = ace[0][1] @@ -911,37 +905,40 @@ def dacl(obj_name=None, obj_type='file'): try: ace_prop = self.ace_prop[self.dacl_type][ace_prop] except KeyError: - ace_prop = 'Unknown propagation' + ace_prop = "Unknown propagation" # Get the object type - obj_type = 'registry' if self.dacl_type == 'registry32' \ - else self.dacl_type + obj_type = "registry" if self.dacl_type == "registry32" else self.dacl_type # Get the ace permissions # Check basic permissions first - ace_perms = self.ace_perms[obj_type]['basic'].get(ace[1], []) + ace_perms = self.ace_perms[obj_type]["basic"].get(ace[1], []) # If it didn't find basic perms, check advanced permissions if not ace_perms: ace_perms = [] - for perm in self.ace_perms[obj_type]['advanced']: + for perm in self.ace_perms[obj_type]["advanced"]: # Don't match against the string perms if isinstance(perm, six.string_types): continue if ace[1] & perm == perm: - ace_perms.append( - self.ace_perms[obj_type]['advanced'][perm]) + ace_perms.append(self.ace_perms[obj_type]["advanced"][perm]) ace_perms.sort() # If still nothing, it must be undefined if not ace_perms: - ace_perms = ['Undefined Permission: {0}'.format(ace[1])] + ace_perms = ["Undefined Permission: {0}".format(ace[1])] - return principal, ace_type, ace_prop, ace_perms, \ - 'Inherited' if inherited else 'Not Inherited' + return ( + principal, + ace_type, + ace_prop, + ace_perms, + "Inherited" if inherited else "Not Inherited", + ) - def rm_ace(self, principal, ace_type='all'): - ''' + def rm_ace(self, principal, ace_type="all"): + """ Remove a specific ACE from the DACL. Args: @@ -967,7 +964,7 @@ def dacl(obj_name=None, obj_type='file'): dacl = Dacl(obj_name='C:\\temp', obj_type='file') dacl.rm_ace('Users') dacl.save(obj_name='C:\\temp') - ''' + """ sid = get_sid(principal) offset = 0 ret = [] @@ -979,19 +976,21 @@ def dacl(obj_name=None, obj_type='file'): inherited = ace[0][1] & win32security.INHERITED_ACE == 16 if ace[2] == sid and not inherited: - if self.ace_type[ace[0][0]] == ace_type.lower() or \ - ace_type == 'all': + if ( + self.ace_type[ace[0][0]] == ace_type.lower() + or ace_type == "all" + ): self.dacl.DeleteAce(i - offset) ret.append(self._ace_to_dict(ace)) offset += 1 if not ret: - ret = ['ACE not found for {0}'.format(principal)] + ret = ["ACE not found for {0}".format(principal)] return ret def save(self, obj_name, protected=None): - ''' + """ Save the DACL Args: @@ -1017,16 +1016,16 @@ def dacl(obj_name=None, obj_type='file'): dacl = Dacl(obj_type='file') dacl.save('C:\\Temp', True) - ''' - sec_info = self.element['dacl'] + """ + sec_info = self.element["dacl"] if protected is not None: if protected: - sec_info = sec_info | self.inheritance['protected'] + sec_info = sec_info | self.inheritance["protected"] else: - sec_info = sec_info | self.inheritance['unprotected'] + sec_info = sec_info | self.inheritance["unprotected"] - if self.dacl_type in ['registry', 'registry32']: + if self.dacl_type in ["registry", "registry32"]: obj_name = self.get_reg_name(obj_name) try: @@ -1034,11 +1033,15 @@ def dacl(obj_name=None, obj_type='file'): obj_name, self.obj_type[self.dacl_type], sec_info, - None, None, self.dacl, None) + None, + None, + self.dacl, + None, + ) except pywintypes.error as exc: raise CommandExecutionError( - 'Failed to set permissions: {0}'.format(obj_name), - exc.strerror) + "Failed to set permissions: {0}".format(obj_name), exc.strerror + ) return True @@ -1046,7 +1049,7 @@ def dacl(obj_name=None, obj_type='file'): def get_sid(principal): - ''' + """ Converts a username to a sid, or verifies a sid. Required for working with the DACL. @@ -1067,10 +1070,10 @@ def get_sid(principal): # Verify that the sid is valid salt.utils.win_dacl.get_sid('S-1-5-32-544') - ''' + """ # If None is passed, use the Universal Well-known SID "Null SID" if principal is None: - principal = 'NULL SID' + principal = "NULL SID" # Test if the user passed a sid or a name try: @@ -1082,9 +1085,8 @@ def get_sid(principal): try: sid = win32security.ConvertStringSidToSid(sid) except pywintypes.error: - log.exception('Invalid user/group or sid: %s', principal) - raise CommandExecutionError( - 'Invalid user/group or sid: {0}'.format(principal)) + log.exception("Invalid user/group or sid: %s", principal) + raise CommandExecutionError("Invalid user/group or sid: {0}".format(principal)) except TypeError: raise CommandExecutionError @@ -1092,7 +1094,7 @@ def get_sid(principal): def get_sid_string(principal): - ''' + """ Converts a PySID object to a string SID. Args: @@ -1112,10 +1114,10 @@ def get_sid_string(principal): # Get the string version of the SID salt.utils.win_dacl.get_sid_string(py_sid) - ''' + """ # If None is passed, use the Universal Well-known SID "Null SID" if principal is None: - principal = 'NULL SID' + principal = "NULL SID" try: return win32security.ConvertSidToStringSid(principal) @@ -1126,12 +1128,12 @@ def get_sid_string(principal): try: return win32security.ConvertSidToStringSid(principal) except pywintypes.error: - log.exception('Invalid principal %s', principal) - raise CommandExecutionError('Invalid principal {0}'.format(principal)) + log.exception("Invalid principal %s", principal) + raise CommandExecutionError("Invalid principal {0}".format(principal)) def get_name(principal): - ''' + """ Gets the name from the specified principal. Args: @@ -1153,14 +1155,14 @@ def get_name(principal): salt.utils.win_dacl.get_name('S-1-5-32-544') salt.utils.win_dacl.get_name('adminisTrators') - ''' + """ # If this is a PySID object, use it if isinstance(principal, pywintypes.SIDType): sid_obj = principal else: # If None is passed, use the Universal Well-known SID for "Null SID" if principal is None: - principal = 'S-1-0-0' + principal = "S-1-0-0" # Try Converting String SID to SID Object first as it's least expensive try: sid_obj = win32security.ConvertStringSidToSid(principal) @@ -1181,14 +1183,14 @@ def get_name(principal): except (pywintypes.error, TypeError) as exc: message = 'Error resolving "{0}"'.format(principal) if type(exc) == pywintypes.error: - win_error = win32api.FormatMessage(exc.winerror).rstrip('\n') - message = '{0}: {1}'.format(message, win_error) + win_error = win32api.FormatMessage(exc.winerror).rstrip("\n") + message = "{0}: {1}".format(message, win_error) log.exception(message) raise CommandExecutionError(message, exc) -def get_owner(obj_name, obj_type='file'): - r''' +def get_owner(obj_name, obj_type="file"): + r""" Gets the owner of the passed object Args: @@ -1235,7 +1237,7 @@ def get_owner(obj_name, obj_type='file'): .. code-block:: python salt.utils.win_dacl.get_owner('c:\\file') - ''' + """ # Not all filesystems mountable within windows have SecurityDescriptors. # For instance, some mounted SAMBA shares, or VirtualBox shared folders. If # we can't load a file descriptor for the file, we default to "None" @@ -1244,35 +1246,36 @@ def get_owner(obj_name, obj_type='file'): try: obj_type_flag = flags().obj_type[obj_type.lower()] except KeyError: - raise SaltInvocationError( - 'Invalid "obj_type" passed: {0}'.format(obj_type)) + raise SaltInvocationError('Invalid "obj_type" passed: {0}'.format(obj_type)) - if obj_type in ['registry', 'registry32']: + if obj_type in ["registry", "registry32"]: obj_name = dacl().get_reg_name(obj_name) try: security_descriptor = win32security.GetNamedSecurityInfo( - obj_name, obj_type_flag, win32security.OWNER_SECURITY_INFORMATION) + obj_name, obj_type_flag, win32security.OWNER_SECURITY_INFORMATION + ) owner_sid = security_descriptor.GetSecurityDescriptorOwner() except MemoryError: # Generic Memory Error (Windows Server 2003+) - owner_sid = 'S-1-0-0' + owner_sid = "S-1-0-0" except pywintypes.error as exc: # Incorrect function error (Windows Server 2008+) if exc.winerror == 1 or exc.winerror == 50: - owner_sid = 'S-1-0-0' + owner_sid = "S-1-0-0" else: - log.exception('Failed to get the owner: %s', obj_name) + log.exception("Failed to get the owner: %s", obj_name) raise CommandExecutionError( - 'Failed to get owner: {0}'.format(obj_name), exc.strerror) + "Failed to get owner: {0}".format(obj_name), exc.strerror + ) return get_name(owner_sid) -def get_primary_group(obj_name, obj_type='file'): - r''' +def get_primary_group(obj_name, obj_type="file"): + r""" Gets the primary group of the passed object Args: @@ -1318,7 +1321,7 @@ def get_primary_group(obj_name, obj_type='file'): .. code-block:: python salt.utils.win_dacl.get_primary_group('c:\\file') - ''' + """ # Not all filesystems mountable within windows have SecurityDescriptors. # For instance, some mounted SAMBA shares, or VirtualBox shared folders. If # we can't load a file descriptor for the file, we default to "Everyone" @@ -1328,37 +1331,37 @@ def get_primary_group(obj_name, obj_type='file'): try: obj_type_flag = flags().obj_type[obj_type.lower()] except KeyError: - raise SaltInvocationError( - 'Invalid "obj_type" passed: {0}'.format(obj_type)) + raise SaltInvocationError('Invalid "obj_type" passed: {0}'.format(obj_type)) - if 'registry' in obj_type.lower(): + if "registry" in obj_type.lower(): obj_name = dacl().get_reg_name(obj_name) - log.debug('Name converted to: %s', obj_name) + log.debug("Name converted to: %s", obj_name) try: security_descriptor = win32security.GetNamedSecurityInfo( - obj_name, obj_type_flag, win32security.GROUP_SECURITY_INFORMATION) + obj_name, obj_type_flag, win32security.GROUP_SECURITY_INFORMATION + ) primary_group_gid = security_descriptor.GetSecurityDescriptorGroup() except MemoryError: # Generic Memory Error (Windows Server 2003+) - primary_group_gid = 'S-1-0-0' + primary_group_gid = "S-1-0-0" except pywintypes.error as exc: # Incorrect function error (Windows Server 2008+) if exc.winerror == 1 or exc.winerror == 50: - primary_group_gid = 'S-1-0-0' + primary_group_gid = "S-1-0-0" else: - log.exception('Failed to get the primary group: %s', obj_name) + log.exception("Failed to get the primary group: %s", obj_name) raise CommandExecutionError( - 'Failed to get primary group: {0}'.format(obj_name), - exc.strerror) + "Failed to get primary group: {0}".format(obj_name), exc.strerror + ) return get_name(win32security.ConvertSidToStringSid(primary_group_gid)) -def set_owner(obj_name, principal, obj_type='file'): - ''' +def set_owner(obj_name, principal, obj_type="file"): + """ Set the owner of an object. This can be a file, folder, registry key, printer, service, etc... @@ -1386,17 +1389,16 @@ def set_owner(obj_name, principal, obj_type='file'): .. code-block:: python salt.utils.win_dacl.set_owner('C:\\MyDirectory', 'jsnuffy', 'file') - ''' + """ sid = get_sid(principal) obj_flags = flags() # Validate obj_type if obj_type.lower() not in obj_flags.obj_type: - raise SaltInvocationError( - 'Invalid "obj_type" passed: {0}'.format(obj_type)) + raise SaltInvocationError('Invalid "obj_type" passed: {0}'.format(obj_type)) - if 'registry' in obj_type.lower(): + if "registry" in obj_type.lower(): obj_name = dacl().get_reg_name(obj_name) # To set the owner to something other than the logged in user requires @@ -1404,16 +1406,16 @@ def set_owner(obj_name, principal, obj_type='file'): # Enable them for the logged in user # Setup the privilege set new_privs = set() - luid = win32security.LookupPrivilegeValue('', 'SeTakeOwnershipPrivilege') + luid = win32security.LookupPrivilegeValue("", "SeTakeOwnershipPrivilege") new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED)) - luid = win32security.LookupPrivilegeValue('', 'SeRestorePrivilege') + luid = win32security.LookupPrivilegeValue("", "SeRestorePrivilege") new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED)) # Get the current token p_handle = win32api.GetCurrentProcess() t_handle = win32security.OpenProcessToken( - p_handle, - win32security.TOKEN_ALL_ACCESS | win32con.TOKEN_ADJUST_PRIVILEGES) + p_handle, win32security.TOKEN_ALL_ACCESS | win32con.TOKEN_ADJUST_PRIVILEGES + ) # Enable the privileges win32security.AdjustTokenPrivileges(t_handle, 0, new_privs) @@ -1423,19 +1425,23 @@ def set_owner(obj_name, principal, obj_type='file'): win32security.SetNamedSecurityInfo( obj_name, obj_flags.obj_type[obj_type.lower()], - obj_flags.element['owner'], + obj_flags.element["owner"], sid, - None, None, None) + None, + None, + None, + ) except pywintypes.error as exc: - log.exception('Failed to make %s the owner: %s', principal, exc) + log.exception("Failed to make %s the owner: %s", principal, exc) raise CommandExecutionError( - 'Failed to set owner: {0}'.format(obj_name), exc.strerror) + "Failed to set owner: {0}".format(obj_name), exc.strerror + ) return True -def set_primary_group(obj_name, principal, obj_type='file'): - ''' +def set_primary_group(obj_name, principal, obj_type="file"): + """ Set the primary group of an object. This can be a file, folder, registry key, printer, service, etc... @@ -1463,11 +1469,11 @@ def set_primary_group(obj_name, principal, obj_type='file'): .. code-block:: python salt.utils.win_dacl.set_primary_group('C:\\MyDirectory', 'Administrators', 'file') - ''' + """ # Windows has the concept of a group called 'None'. It is the default group # for all Objects. If the user passes None, assume 'None' if principal is None: - principal = 'None' + principal = "None" gid = get_sid(principal) @@ -1475,10 +1481,9 @@ def set_primary_group(obj_name, principal, obj_type='file'): # Validate obj_type if obj_type.lower() not in obj_flags.obj_type: - raise SaltInvocationError( - 'Invalid "obj_type" passed: {0}'.format(obj_type)) + raise SaltInvocationError('Invalid "obj_type" passed: {0}'.format(obj_type)) - if 'registry' in obj_type.lower(): + if "registry" in obj_type.lower(): obj_name = dacl().get_reg_name(obj_name) # To set the owner to something other than the logged in user requires @@ -1486,16 +1491,16 @@ def set_primary_group(obj_name, principal, obj_type='file'): # Enable them for the logged in user # Setup the privilege set new_privs = set() - luid = win32security.LookupPrivilegeValue('', 'SeTakeOwnershipPrivilege') + luid = win32security.LookupPrivilegeValue("", "SeTakeOwnershipPrivilege") new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED)) - luid = win32security.LookupPrivilegeValue('', 'SeRestorePrivilege') + luid = win32security.LookupPrivilegeValue("", "SeRestorePrivilege") new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED)) # Get the current token p_handle = win32api.GetCurrentProcess() t_handle = win32security.OpenProcessToken( - p_handle, - win32security.TOKEN_ALL_ACCESS | win32con.TOKEN_ADJUST_PRIVILEGES) + p_handle, win32security.TOKEN_ALL_ACCESS | win32con.TOKEN_ADJUST_PRIVILEGES + ) # Enable the privileges win32security.AdjustTokenPrivileges(t_handle, 0, new_privs) @@ -1505,25 +1510,32 @@ def set_primary_group(obj_name, principal, obj_type='file'): win32security.SetNamedSecurityInfo( obj_name, obj_flags.obj_type[obj_type.lower()], - obj_flags.element['group'], - None, gid, None, None) + obj_flags.element["group"], + None, + gid, + None, + None, + ) except pywintypes.error as exc: - log.exception('Failed to make %s the primary group: %s', principal, exc) + log.exception("Failed to make %s the primary group: %s", principal, exc) raise CommandExecutionError( - 'Failed to set primary group: {0}'.format(obj_name), exc.strerror) + "Failed to set primary group: {0}".format(obj_name), exc.strerror + ) return True -def set_permissions(obj_name, - principal, - permissions, - access_mode='grant', - applies_to=None, - obj_type='file', - reset_perms=False, - protected=None): - ''' +def set_permissions( + obj_name, + principal, + permissions, + access_mode="grant", + applies_to=None, + obj_type="file", + reset_perms=False, + protected=None, +): + """ Set the permissions of an object. This can be a file, folder, registry key, printer, service, etc... @@ -1575,13 +1587,13 @@ def set_permissions(obj_name, salt.utils.win_dacl.set_permissions( 'C:\\Temp', 'jsnuffy', 'full_control', 'grant') - ''' + """ # Set up applies_to defaults used by registry and file types if applies_to is None: - if 'registry' in obj_type.lower(): - applies_to = 'this_key_subkeys' - elif obj_type.lower() == 'file': - applies_to = 'this_folder_subfolders_files' + if "registry" in obj_type.lower(): + applies_to = "this_key_subkeys" + elif obj_type.lower() == "file": + applies_to = "this_folder_subfolders_files" # If you don't pass `obj_name` it will create a blank DACL # Otherwise, it will grab the existing DACL and add to it @@ -1600,11 +1612,8 @@ def set_permissions(obj_name, return True -def rm_permissions(obj_name, - principal, - ace_type='all', - obj_type='file'): - r''' +def rm_permissions(obj_name, principal, ace_type="all", obj_type="file"): + r""" Remove a user's ACE from an object. This can be a file, folder, registry key, printer, service, etc... @@ -1640,7 +1649,7 @@ def rm_permissions(obj_name, # Remove all ACEs for jsnuffy from C:\Temp salt.utils.win_dacl.rm_permissions('C:\\Temp', 'jsnuffy') - ''' + """ obj_dacl = dacl(obj_name, obj_type) obj_dacl.rm_ace(principal, ace_type) @@ -1649,8 +1658,8 @@ def rm_permissions(obj_name, return True -def get_permissions(obj_name, principal=None, obj_type='file'): - ''' +def get_permissions(obj_name, principal=None, obj_type="file"): + """ Get the permissions for the passed object Args: @@ -1674,7 +1683,7 @@ def get_permissions(obj_name, principal=None, obj_type='file'): .. code-block:: python salt.utils.win_dacl.get_permissions('C:\\Temp') - ''' + """ obj_dacl = dacl(obj_name, obj_type) if principal is None: @@ -1683,13 +1692,10 @@ def get_permissions(obj_name, principal=None, obj_type='file'): return obj_dacl.get_ace(principal) -def has_permission(obj_name, - principal, - permission, - access_mode='grant', - obj_type='file', - exact=True): - r''' +def has_permission( + obj_name, principal, permission, access_mode="grant", obj_type="file", exact=True +): + r""" Check if the object has a permission Args: @@ -1732,11 +1738,12 @@ def has_permission(obj_name, # Does Joe have Full Control of C:\Temp salt.utils.win_dacl.has_permission( 'C:\\Temp', 'joe', 'full_control', 'grant') - ''' + """ # Validate access_mode - if access_mode.lower() not in ['grant', 'deny']: + if access_mode.lower() not in ["grant", "deny"]: raise SaltInvocationError( - 'Invalid "access_mode" passed: {0}'.format(access_mode)) + 'Invalid "access_mode" passed: {0}'.format(access_mode) + ) access_mode = access_mode.lower() # Get the DACL @@ -1748,12 +1755,12 @@ def has_permission(obj_name, sid = get_sid(principal) # Get the passed permission flag, check basic first - chk_flag = obj_dacl.ace_perms[obj_type]['basic'].get( + chk_flag = obj_dacl.ace_perms[obj_type]["basic"].get( permission.lower(), - obj_dacl.ace_perms[obj_type]['advanced'].get(permission.lower(), False)) + obj_dacl.ace_perms[obj_type]["advanced"].get(permission.lower(), False), + ) if not chk_flag: - raise SaltInvocationError( - 'Invalid "permission" passed: {0}'.format(permission)) + raise SaltInvocationError('Invalid "permission" passed: {0}'.format(permission)) # Check each ace for sid and type cur_flag = None @@ -1774,8 +1781,8 @@ def has_permission(obj_name, return cur_flag & chk_flag == chk_flag -def set_inheritance(obj_name, enabled, obj_type='file', clear=False): - ''' +def set_inheritance(obj_name, enabled, obj_type="file", clear=False): + """ Enable or disable an objects inheritance. Args: @@ -1806,10 +1813,11 @@ def set_inheritance(obj_name, enabled, obj_type='file', clear=False): .. code-block:: python salt.utils.win_dacl.set_inheritance('C:\\Temp', False) - ''' - if obj_type not in ['file', 'registry', 'registry32']: + """ + if obj_type not in ["file", "registry", "registry32"]: raise SaltInvocationError( - 'obj_type called with incorrect parameter: {0}'.format(obj_name)) + "obj_type called with incorrect parameter: {0}".format(obj_name) + ) if clear: obj_dacl = dacl(obj_type=obj_type) @@ -1819,8 +1827,8 @@ def set_inheritance(obj_name, enabled, obj_type='file', clear=False): return obj_dacl.save(obj_name, not enabled) -def get_inheritance(obj_name, obj_type='file'): - ''' +def get_inheritance(obj_name, obj_type="file"): + """ Get an object's inheritance. Args: @@ -1850,7 +1858,7 @@ def get_inheritance(obj_name, obj_type='file'): .. code-block:: python salt.utils.win_dacl.get_inheritance('HKLM\\SOFTWARE\\salt', 'registry') - ''' + """ obj_dacl = dacl(obj_name=obj_name, obj_type=obj_type) inherited = win32security.INHERITED_ACE @@ -1862,14 +1870,16 @@ def get_inheritance(obj_name, obj_type='file'): return False -def copy_security(source, - target, - obj_type='file', - copy_owner=True, - copy_group=True, - copy_dacl=True, - copy_sacl=True): - r''' +def copy_security( + source, + target, + obj_type="file", + copy_owner=True, + copy_group=True, + copy_dacl=True, + copy_sacl=True, +): + r""" Copy the security descriptor of the Source to the Target. You can specify a specific portion of the security descriptor to copy using one of the `copy_*` parameters. @@ -1957,20 +1967,19 @@ def copy_security(source, target='HKLM\\SOFTWARE\\salt\\test_target', obj_type='registry', copy_owner=False) - ''' + """ obj_dacl = dacl(obj_type=obj_type) - if 'registry' in obj_type.lower(): + if "registry" in obj_type.lower(): source = obj_dacl.get_reg_name(source) - log.info('Source converted to: %s', source) + log.info("Source converted to: %s", source) target = obj_dacl.get_reg_name(target) - log.info('Target converted to: %s', target) + log.info("Target converted to: %s", target) # Set flags try: obj_type_flag = flags().obj_type[obj_type.lower()] except KeyError: - raise SaltInvocationError( - 'Invalid "obj_type" passed: {0}'.format(obj_type)) + raise SaltInvocationError('Invalid "obj_type" passed: {0}'.format(obj_type)) security_flags = 0 if copy_owner: @@ -1984,33 +1993,32 @@ def copy_security(source, if not security_flags: raise SaltInvocationError( - 'One of copy_owner, copy_group, copy_dacl, or copy_sacl must be ' - 'True') + "One of copy_owner, copy_group, copy_dacl, or copy_sacl must be " "True" + ) # To set the owner to something other than the logged in user requires # SE_TAKE_OWNERSHIP_NAME and SE_RESTORE_NAME privileges # Enable them for the logged in user # Setup the privilege set new_privs = set() - luid = win32security.LookupPrivilegeValue('', 'SeTakeOwnershipPrivilege') + luid = win32security.LookupPrivilegeValue("", "SeTakeOwnershipPrivilege") new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED)) - luid = win32security.LookupPrivilegeValue('', 'SeRestorePrivilege') + luid = win32security.LookupPrivilegeValue("", "SeRestorePrivilege") new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED)) - luid = win32security.LookupPrivilegeValue('', 'SeSecurityPrivilege') + luid = win32security.LookupPrivilegeValue("", "SeSecurityPrivilege") new_privs.add((luid, win32con.SE_PRIVILEGE_ENABLED)) # Get the current token p_handle = win32api.GetCurrentProcess() t_handle = win32security.OpenProcessToken( - p_handle, - win32security.TOKEN_ALL_ACCESS | win32con.TOKEN_ADJUST_PRIVILEGES) + p_handle, win32security.TOKEN_ALL_ACCESS | win32con.TOKEN_ADJUST_PRIVILEGES + ) # Enable the privileges win32security.AdjustTokenPrivileges(t_handle, 0, new_privs) # Load object Security Info from the Source - sec = win32security.GetNamedSecurityInfo( - source, obj_type_flag, security_flags) + sec = win32security.GetNamedSecurityInfo(source, obj_type_flag, security_flags) # The following return None if the corresponding flag is not set sd_sid = sec.GetSecurityDescriptorOwner() @@ -2021,17 +2029,18 @@ def copy_security(source, # Set Security info on the target try: win32security.SetNamedSecurityInfo( - target, obj_type_flag, security_flags, sd_sid, sd_gid, sd_dacl, - sd_sacl) + target, obj_type_flag, security_flags, sd_sid, sd_gid, sd_dacl, sd_sacl + ) except pywintypes.error as exc: raise CommandExecutionError( - 'Failed to set security info: {0}'.format(exc.strerror)) + "Failed to set security info: {0}".format(exc.strerror) + ) return True def _check_perms(obj_name, obj_type, new_perms, cur_perms, access_mode, ret): - ''' + """ Helper function used by ``check_perms`` for checking and setting Grant and Deny permissions. @@ -2060,57 +2069,62 @@ def _check_perms(obj_name, obj_type, new_perms, cur_perms, access_mode, ret): Returns: dict: A dictionary of return data as expected by the state system - ''' + """ access_mode = access_mode.lower() changes = {} for user in new_perms: - applies_to_text = '' + applies_to_text = "" # Check that user exists: try: user_name = get_name(principal=user) except CommandExecutionError: - ret['comment'].append( + ret["comment"].append( '{0} Perms: User "{1}" missing from Target System' - ''.format(access_mode.capitalize(), user)) + "".format(access_mode.capitalize(), user) + ) continue # Get the proper applies_to text - if 'applies_to' in new_perms[user]: - applies_to = new_perms[user]['applies_to'] - at_flag = flags().ace_prop['file'][applies_to] - applies_to_text = flags().ace_prop['file'][at_flag] + if "applies_to" in new_perms[user]: + applies_to = new_perms[user]["applies_to"] + at_flag = flags().ace_prop["file"][applies_to] + applies_to_text = flags().ace_prop["file"][at_flag] else: applies_to = None - if user_name not in cur_perms['Not Inherited']: + if user_name not in cur_perms["Not Inherited"]: if user not in changes: changes[user] = {} - changes[user][access_mode] = new_perms[user]['perms'] + changes[user][access_mode] = new_perms[user]["perms"] if applies_to: - changes[user]['applies_to'] = applies_to + changes[user]["applies_to"] = applies_to else: # Check Perms for basic perms - if isinstance(new_perms[user]['perms'], six.string_types): - if not has_permission(obj_name=obj_name, - principal=user_name, - permission=new_perms[user]['perms'], - access_mode=access_mode, - obj_type=obj_type, - exact=False): + if isinstance(new_perms[user]["perms"], six.string_types): + if not has_permission( + obj_name=obj_name, + principal=user_name, + permission=new_perms[user]["perms"], + access_mode=access_mode, + obj_type=obj_type, + exact=False, + ): if user not in changes: changes[user] = {} - changes[user][access_mode] = new_perms[user]['perms'] + changes[user][access_mode] = new_perms[user]["perms"] # Check Perms for advanced perms else: - for perm in new_perms[user]['perms']: - if not has_permission(obj_name=obj_name, - principal=user_name, - permission=perm, - access_mode=access_mode, - obj_type=obj_type, - exact=False): + for perm in new_perms[user]["perms"]: + if not has_permission( + obj_name=obj_name, + principal=user_name, + permission=perm, + access_mode=access_mode, + obj_type=obj_type, + exact=False, + ): if user not in changes: changes[user] = {access_mode: []} changes[user][access_mode].append(perm) @@ -2118,63 +2132,97 @@ def _check_perms(obj_name, obj_type, new_perms, cur_perms, access_mode, ret): # Check if applies_to was passed if applies_to: # Is there a deny/grant permission set - if access_mode in cur_perms['Not Inherited'][user_name]: + if access_mode in cur_perms["Not Inherited"][user_name]: # If the applies to settings are different, use the new one - if not cur_perms['Not Inherited'][user_name][access_mode]['applies to'] == applies_to_text: + if ( + not cur_perms["Not Inherited"][user_name][access_mode][ + "applies to" + ] + == applies_to_text + ): if user not in changes: changes[user] = {} - changes[user]['applies_to'] = applies_to + changes[user]["applies_to"] = applies_to if changes: - if 'perms' not in ret['changes']: - ret['changes']['perms'] = {} + if "perms" not in ret["changes"]: + ret["changes"]["perms"] = {} for user in changes: user_name = get_name(principal=user) - if __opts__['test'] is True: - if user not in ret['changes']['perms']: - ret['changes']['perms'][user] = {} - ret['changes']['perms'][user][access_mode] = changes[user][access_mode] + if __opts__["test"] is True: + if user not in ret["changes"]["perms"]: + ret["changes"]["perms"][user] = {} + ret["changes"]["perms"][user][access_mode] = changes[user][access_mode] else: # Get applies_to applies_to = None - if 'applies_to' not in changes[user]: + if "applies_to" not in changes[user]: # Get current "applies to" settings from the file - if user_name in cur_perms['Not Inherited'] and \ - access_mode in cur_perms['Not Inherited'][user_name]: + if ( + user_name in cur_perms["Not Inherited"] + and access_mode in cur_perms["Not Inherited"][user_name] + ): for flag in flags().ace_prop[obj_type]: - if flags().ace_prop[obj_type][flag] == cur_perms['Not Inherited'][user_name][access_mode]['applies to']: + if ( + flags().ace_prop[obj_type][flag] + == cur_perms["Not Inherited"][user_name][access_mode][ + "applies to" + ] + ): at_flag = flag for flag1 in flags().ace_prop[obj_type]: - if salt.utils.win_dacl.flags().ace_prop[obj_type][flag1] == at_flag: + if ( + salt.utils.win_dacl.flags().ace_prop[obj_type][ + flag1 + ] + == at_flag + ): applies_to = flag1 if not applies_to: - if obj_type.lower() in ['registry', 'registry32']: - applies_to = 'this_key_subkeys' + if obj_type.lower() in ["registry", "registry32"]: + applies_to = "this_key_subkeys" else: - applies_to = 'this_folder_subfolders_files' + applies_to = "this_folder_subfolders_files" else: - applies_to = changes[user]['applies_to'] + applies_to = changes[user]["applies_to"] perms = [] if access_mode not in changes[user]: # Get current perms # Check for basic perms - for perm in cur_perms['Not Inherited'][user_name][access_mode]['permissions']: - for flag in flags().ace_perms[obj_type]['basic']: - if flags().ace_perms[obj_type]['basic'][flag] == perm: + for perm in cur_perms["Not Inherited"][user_name][access_mode][ + "permissions" + ]: + for flag in flags().ace_perms[obj_type]["basic"]: + if flags().ace_perms[obj_type]["basic"][flag] == perm: perm_flag = flag - for flag1 in flags().ace_perms[obj_type]['basic']: - if flags().ace_perms[obj_type]['basic'][flag1] == perm_flag: + for flag1 in flags().ace_perms[obj_type]["basic"]: + if ( + flags().ace_perms[obj_type]["basic"][flag1] + == perm_flag + ): perms = flag1 # Make a list of advanced perms if not perms: - for perm in cur_perms['Not Inherited'][user_name][access_mode]['permissions']: - for flag in flags().ace_perms[obj_type]['advanced']: - if flags().ace_perms[obj_type]['advanced'][flag] == perm: + for perm in cur_perms["Not Inherited"][user_name][access_mode][ + "permissions" + ]: + for flag in flags().ace_perms[obj_type]["advanced"]: + if ( + flags().ace_perms[obj_type]["advanced"][flag] + == perm + ): perm_flag = flag - for flag1 in flags().ace_perms[obj_type]['advanced']: - if flags().ace_perms[obj_type]['advanced'][flag1] == perm_flag: + for flag1 in flags().ace_perms[obj_type][ + "advanced" + ]: + if ( + flags().ace_perms[obj_type]["advanced"][ + flag1 + ] + == perm_flag + ): perms.append(flag1) else: perms = changes[user][access_mode] @@ -2186,28 +2234,36 @@ def _check_perms(obj_name, obj_type, new_perms, cur_perms, access_mode, ret): permissions=perms, access_mode=access_mode, applies_to=applies_to, - obj_type=obj_type) - if user not in ret['changes']['perms']: - ret['changes']['perms'][user] = {} - ret['changes']['perms'][user][access_mode] = changes[user][access_mode] + obj_type=obj_type, + ) + if user not in ret["changes"]["perms"]: + ret["changes"]["perms"][user] = {} + ret["changes"]["perms"][user][access_mode] = changes[user][ + access_mode + ] except CommandExecutionError as exc: - ret['result'] = False - ret['comment'].append( + ret["result"] = False + ret["comment"].append( 'Failed to change {0} permissions for "{1}" to {2}\n' - 'Error: {3}'.format(access_mode, user, changes[user], exc.strerror)) + "Error: {3}".format( + access_mode, user, changes[user], exc.strerror + ) + ) return ret -def check_perms(obj_name, - obj_type='file', - ret=None, - owner=None, - grant_perms=None, - deny_perms=None, - inheritance=True, - reset=False): - ''' +def check_perms( + obj_name, + obj_type="file", + ret=None, + owner=None, + grant_perms=None, + deny_perms=None, + inheritance=True, + reset=False, +): + """ Check owner and permissions for the passed directory. This function checks the permissions and sets them, returning the changes made. @@ -2279,143 +2335,151 @@ def check_perms(obj_name, 'applies_to': 'files_only' } }) - ''' + """ # Validate obj_type if obj_type.lower() not in flags().obj_type: - raise SaltInvocationError( - 'Invalid "obj_type" passed: {0}'.format(obj_type)) + raise SaltInvocationError('Invalid "obj_type" passed: {0}'.format(obj_type)) obj_type = obj_type.lower() if not ret: - ret = {'name': obj_name, - 'changes': {}, - 'comment': [], - 'result': True} - orig_comment = '' + ret = {"name": obj_name, "changes": {}, "comment": [], "result": True} + orig_comment = "" else: - orig_comment = ret['comment'] - ret['comment'] = [] + orig_comment = ret["comment"] + ret["comment"] = [] # Check owner if owner: owner = get_name(principal=owner) current_owner = get_owner(obj_name=obj_name, obj_type=obj_type) if owner != current_owner: - if __opts__['test'] is True: - ret['changes']['owner'] = owner + if __opts__["test"] is True: + ret["changes"]["owner"] = owner else: try: - set_owner(obj_name=obj_name, - principal=owner, - obj_type=obj_type) - log.debug('Owner set to {0}'.format(owner)) - ret['changes']['owner'] = owner + set_owner(obj_name=obj_name, principal=owner, obj_type=obj_type) + log.debug("Owner set to {0}".format(owner)) + ret["changes"]["owner"] = owner except CommandExecutionError: - ret['result'] = False - ret['comment'].append( - 'Failed to change owner to "{0}"'.format(owner)) + ret["result"] = False + ret["comment"].append( + 'Failed to change owner to "{0}"'.format(owner) + ) # Check inheritance if inheritance is not None: - if not inheritance == get_inheritance(obj_name=obj_name, - obj_type=obj_type): - if __opts__['test'] is True: - ret['changes']['inheritance'] = inheritance + if not inheritance == get_inheritance(obj_name=obj_name, obj_type=obj_type): + if __opts__["test"] is True: + ret["changes"]["inheritance"] = inheritance else: try: set_inheritance( - obj_name=obj_name, - enabled=inheritance, - obj_type=obj_type) - log.debug('%s inheritance', 'Enabling' if inheritance else 'Disabling') - ret['changes']['inheritance'] = inheritance + obj_name=obj_name, enabled=inheritance, obj_type=obj_type + ) + log.debug( + "%s inheritance", "Enabling" if inheritance else "Disabling" + ) + ret["changes"]["inheritance"] = inheritance except CommandExecutionError: - ret['result'] = False - ret['comment'].append( + ret["result"] = False + ret["comment"].append( 'Failed to set inheritance for "{0}" to {1}' - ''.format(obj_name, inheritance)) + "".format(obj_name, inheritance) + ) # Check permissions - log.debug('Getting current permissions for {0}'.format(obj_name)) + log.debug("Getting current permissions for {0}".format(obj_name)) cur_perms = get_permissions(obj_name=obj_name, obj_type=obj_type) # Verify Deny Permissions if deny_perms is not None: - ret = _check_perms(obj_name=obj_name, - obj_type=obj_type, - new_perms=deny_perms, - cur_perms=cur_perms, - access_mode='deny', - ret=ret) + ret = _check_perms( + obj_name=obj_name, + obj_type=obj_type, + new_perms=deny_perms, + cur_perms=cur_perms, + access_mode="deny", + ret=ret, + ) # Verify Grant Permissions if grant_perms is not None: - ret = _check_perms(obj_name=obj_name, - obj_type=obj_type, - new_perms=grant_perms, - cur_perms=cur_perms, - access_mode='grant', - ret=ret) + ret = _check_perms( + obj_name=obj_name, + obj_type=obj_type, + new_perms=grant_perms, + cur_perms=cur_perms, + access_mode="grant", + ret=ret, + ) # Check reset # If reset=True, which users will be removed as a result if reset: - log.debug('Resetting permissions for {0}'.format(obj_name)) + log.debug("Resetting permissions for {0}".format(obj_name)) cur_perms = get_permissions(obj_name=obj_name, obj_type=obj_type) - for user_name in cur_perms['Not Inherited']: + for user_name in cur_perms["Not Inherited"]: # case insensitive dictionary search - if grant_perms is not None and \ - user_name.lower() not in set(k.lower() for k in grant_perms): - if 'grant' in cur_perms['Not Inherited'][user_name]: - if __opts__['test'] is True: - if 'remove_perms' not in ret['changes']: - ret['changes']['remove_perms'] = {} - ret['changes']['remove_perms'].update( - {user_name: cur_perms['Not Inherited'][user_name]}) + if grant_perms is not None and user_name.lower() not in set( + k.lower() for k in grant_perms + ): + if "grant" in cur_perms["Not Inherited"][user_name]: + if __opts__["test"] is True: + if "remove_perms" not in ret["changes"]: + ret["changes"]["remove_perms"] = {} + ret["changes"]["remove_perms"].update( + {user_name: cur_perms["Not Inherited"][user_name]} + ) else: - if 'remove_perms' not in ret['changes']: - ret['changes']['remove_perms'] = {} + if "remove_perms" not in ret["changes"]: + ret["changes"]["remove_perms"] = {} rm_permissions( obj_name=obj_name, principal=user_name, - ace_type='grant', - obj_type=obj_type) - ret['changes']['remove_perms'].update( - {user_name: cur_perms['Not Inherited'][user_name]}) + ace_type="grant", + obj_type=obj_type, + ) + ret["changes"]["remove_perms"].update( + {user_name: cur_perms["Not Inherited"][user_name]} + ) # case insensitive dictionary search - if deny_perms is not None and \ - user_name.lower() not in set(k.lower() for k in deny_perms): - if 'deny' in cur_perms['Not Inherited'][user_name]: - if __opts__['test'] is True: - if 'remove_perms' not in ret['changes']: - ret['changes']['remove_perms'] = {} - ret['changes']['remove_perms'].update( - {user_name: cur_perms['Not Inherited'][user_name]}) + if deny_perms is not None and user_name.lower() not in set( + k.lower() for k in deny_perms + ): + if "deny" in cur_perms["Not Inherited"][user_name]: + if __opts__["test"] is True: + if "remove_perms" not in ret["changes"]: + ret["changes"]["remove_perms"] = {} + ret["changes"]["remove_perms"].update( + {user_name: cur_perms["Not Inherited"][user_name]} + ) else: - if 'remove_perms' not in ret['changes']: - ret['changes']['remove_perms'] = {} + if "remove_perms" not in ret["changes"]: + ret["changes"]["remove_perms"] = {} rm_permissions( obj_name=obj_name, principal=user_name, - ace_type='deny', - obj_type=obj_type) - ret['changes']['remove_perms'].update( - {user_name: cur_perms['Not Inherited'][user_name]}) + ace_type="deny", + obj_type=obj_type, + ) + ret["changes"]["remove_perms"].update( + {user_name: cur_perms["Not Inherited"][user_name]} + ) # Re-add the Original Comment if defined if isinstance(orig_comment, six.string_types): if orig_comment: - ret['comment'].insert(0, orig_comment) + ret["comment"].insert(0, orig_comment) else: if orig_comment: - ret['comment'] = orig_comment.extend(ret['comment']) + ret["comment"] = orig_comment.extend(ret["comment"]) - ret['comment'] = '\n'.join(ret['comment']) + ret["comment"] = "\n".join(ret["comment"]) # Set result for test = True - if __opts__['test'] and (ret['changes']): - ret['result'] = None + if __opts__["test"] and (ret["changes"]): + ret["result"] = None return ret @@ -2428,50 +2492,64 @@ def _set_perms(obj_dacl, obj_type, new_perms, cur_perms, access_mode): try: user_name = get_name(user) except CommandExecutionError: - log.debug('%s Perms: User "%s" missing from Target System', - access_mode.capitalize(), user) + log.debug( + '%s Perms: User "%s" missing from Target System', + access_mode.capitalize(), + user, + ) continue # Get applies_to applies_to = None # Propagation only applies to file and registry object types - if obj_type in ['file', 'registry', 'registry32']: - if 'applies_to' not in new_perms[user]: + if obj_type in ["file", "registry", "registry32"]: + if "applies_to" not in new_perms[user]: # Get current "applies to" settings from the object - if user_name in cur_perms['Not Inherited'] and \ - 'deny' in cur_perms['Not Inherited'][user_name]: + if ( + user_name in cur_perms["Not Inherited"] + and "deny" in cur_perms["Not Inherited"][user_name] + ): for flag in flags().ace_prop[obj_type]: - if flags().ace_prop[obj_type][flag] == cur_perms['Not Inherited'][user_name]['deny']['applies to']: + if ( + flags().ace_prop[obj_type][flag] + == cur_perms["Not Inherited"][user_name]["deny"][ + "applies to" + ] + ): at_flag = flag for flag1 in flags().ace_prop[obj_type]: if flags().ace_prop[obj_type][flag1] == at_flag: applies_to = flag1 if not applies_to: # Propagation only applies to file and registry object types - if obj_type == 'file': - applies_to = 'this_folder_subfolders_files' - elif 'registry' in obj_type: - applies_to = 'this_key_subkeys' + if obj_type == "file": + applies_to = "this_folder_subfolders_files" + elif "registry" in obj_type: + applies_to = "this_key_subkeys" else: - applies_to = new_perms[user]['applies_to'] + applies_to = new_perms[user]["applies_to"] # Set permissions - if obj_dacl.add_ace(principal=user, - access_mode=access_mode, - permissions=new_perms[user]['perms'], - applies_to=applies_to): + if obj_dacl.add_ace( + principal=user, + access_mode=access_mode, + permissions=new_perms[user]["perms"], + applies_to=applies_to, + ): ret[user] = new_perms[user] return ret -def set_perms(obj_name, - obj_type='file', - grant_perms=None, - deny_perms=None, - inheritance=True, - reset=False): - ''' +def set_perms( + obj_name, + obj_type="file", + grant_perms=None, + deny_perms=None, + inheritance=True, + reset=False, +): + """ Set permissions for the given path .. versionadded:: 2019.2.0 @@ -2561,7 +2639,7 @@ def set_perms(obj_name, 'applies_to': 'this_folder_only' } }" - ''' + """ ret = {} if reset: @@ -2580,19 +2658,23 @@ def set_perms(obj_name, # Set 'deny' perms if any if deny_perms is not None: - ret['deny'] = _set_perms(obj_dacl=obj_dacl, - obj_type=obj_type, - new_perms=deny_perms, - cur_perms=cur_perms, - access_mode='deny') + ret["deny"] = _set_perms( + obj_dacl=obj_dacl, + obj_type=obj_type, + new_perms=deny_perms, + cur_perms=cur_perms, + access_mode="deny", + ) # Set 'grant' perms if any if grant_perms is not None: - ret['grant'] = _set_perms(obj_dacl=obj_dacl, - obj_type=obj_type, - new_perms=grant_perms, - cur_perms=cur_perms, - access_mode='grant') + ret["grant"] = _set_perms( + obj_dacl=obj_dacl, + obj_type=obj_type, + new_perms=grant_perms, + cur_perms=cur_perms, + access_mode="grant", + ) # Order the ACL obj_dacl.order_acl() diff --git a/salt/utils/win_functions.py b/salt/utils/win_functions.py index a0dce2d6f16..053ecedb0d3 100644 --- a/salt/utils/win_functions.py +++ b/salt/utils/win_functions.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Various functions to be used by windows during start up and to monkey patch missing functions in other modules -''' +""" from __future__ import absolute_import, print_function, unicode_literals + +import ctypes import platform import re -import ctypes # Import Salt Libs from salt.exceptions import CommandExecutionError @@ -20,6 +21,7 @@ try: import win32net import win32security from win32con import HWND_BROADCAST, WM_SETTINGCHANGE, SMTO_ABORTIFHUNG + HAS_WIN32 = True except ImportError: HAS_WIN32 = False @@ -28,28 +30,28 @@ except ImportError: # Although utils are often directly imported, it is also possible to use the # loader. def __virtual__(): - ''' + """ Only load if Win32 Libraries are installed - ''' + """ if not HAS_WIN32: - return False, 'This utility requires pywin32' + return False, "This utility requires pywin32" - return 'win_functions' + return "win_functions" def get_parent_pid(): - ''' + """ This is a monkey patch for os.getppid. Used in: - salt.utils.parsers Returns: int: The parent process id - ''' + """ return psutil.Process().ppid() def is_admin(name): - ''' + """ Is the passed user a member of the Administrators group Args: @@ -58,18 +60,18 @@ def is_admin(name): Returns: bool: True if user is a member of the Administrators group, False otherwise - ''' + """ groups = get_user_groups(name, True) for group in groups: - if group in ('S-1-5-32-544', 'S-1-5-18'): + if group in ("S-1-5-32-544", "S-1-5-18"): return True return False def get_user_groups(name, sid=False): - ''' + """ Get the groups to which a user belongs Args: @@ -79,11 +81,11 @@ def get_user_groups(name, sid=False): Returns: list: A list of group names or sids - ''' + """ groups = [] - if name.upper() == 'SYSTEM': + if name.upper() == "SYSTEM": # 'win32net.NetUserGetLocalGroups' will fail if you pass in 'SYSTEM'. - groups = ['SYSTEM'] + groups = ["SYSTEM"] else: try: groups = win32net.NetUserGetLocalGroups(None, name) @@ -107,7 +109,7 @@ def get_user_groups(name, sid=False): def get_sid_from_name(name): - ''' + """ This is a tool for getting a sid from a name. The name can be any object. Usually a user or a group @@ -116,22 +118,21 @@ def get_sid_from_name(name): Returns: str: The corresponding SID - ''' + """ # If None is passed, use the Universal Well-known SID "Null SID" if name is None: - name = 'NULL SID' + name = "NULL SID" try: sid = win32security.LookupAccountName(None, name)[0] except pywintypes.error as exc: - raise CommandExecutionError( - 'User {0} not found: {1}'.format(name, exc)) + raise CommandExecutionError("User {0} not found: {1}".format(name, exc)) return win32security.ConvertSidToStringSid(sid) def get_current_user(with_domain=True): - ''' + """ Gets the user executing the process Args: @@ -142,22 +143,21 @@ def get_current_user(with_domain=True): Returns: str: The user name - ''' + """ try: user_name = win32api.GetUserNameEx(win32api.NameSamCompatible) - if user_name[-1] == '$': + if user_name[-1] == "$": # Make the system account easier to identify. # Fetch sid so as to handle other language than english test_user = win32api.GetUserName() - if test_user == 'SYSTEM': - user_name = 'SYSTEM' - elif get_sid_from_name(test_user) == 'S-1-5-18': - user_name = 'SYSTEM' + if test_user == "SYSTEM": + user_name = "SYSTEM" + elif get_sid_from_name(test_user) == "S-1-5-18": + user_name = "SYSTEM" elif not with_domain: user_name = win32api.GetUserName() except pywintypes.error as exc: - raise CommandExecutionError( - 'Failed to get current user: {0}'.format(exc)) + raise CommandExecutionError("Failed to get current user: {0}".format(exc)) if not user_name: return False @@ -166,7 +166,7 @@ def get_current_user(with_domain=True): def get_sam_name(username): - r''' + r""" Gets the SAM name for a user. It basically prefixes a username without a backslash with the computer name. If the user does not exist, a SAM compatible name will be returned using the local hostname as the domain. @@ -174,26 +174,25 @@ def get_sam_name(username): i.e. salt.utils.get_same_name('Administrator') would return 'DOMAIN.COM\Administrator' .. note:: Long computer names are truncated to 15 characters - ''' + """ try: sid_obj = win32security.LookupAccountName(None, username)[0] except pywintypes.error: - return '\\'.join([platform.node()[:15].upper(), username]) + return "\\".join([platform.node()[:15].upper(), username]) username, domain, _ = win32security.LookupAccountSid(None, sid_obj) - return '\\'.join([domain, username]) + return "\\".join([domain, username]) def enable_ctrl_logoff_handler(): if HAS_WIN32: ctrl_logoff_event = 5 win32api.SetConsoleCtrlHandler( - lambda event: True if event == ctrl_logoff_event else False, - 1 + lambda event: True if event == ctrl_logoff_event else False, 1 ) def escape_argument(arg, escape=True): - ''' + """ Escape the argument for the cmd.exe shell. See http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx @@ -210,9 +209,9 @@ def escape_argument(arg, escape=True): Returns: str: an escaped string suitable to be passed as a program argument to the cmd.exe shell - ''' + """ if not arg or re.search(r'(["\s])', arg): - arg = '"' + arg.replace('"', r'\"') + '"' + arg = '"' + arg.replace('"', r"\"") + '"' if not escape: return arg @@ -220,7 +219,7 @@ def escape_argument(arg, escape=True): def escape_for_cmd_exe(arg): - ''' + """ Escape an argument string to be suitable to be passed to cmd.exe on Windows @@ -237,9 +236,11 @@ def escape_for_cmd_exe(arg): Returns: str: an escaped string suitable to be passed as a program argument to cmd.exe - ''' + """ meta_chars = '()%!^"<>&|' - meta_re = re.compile('(' + '|'.join(re.escape(char) for char in list(meta_chars)) + ')') + meta_re = re.compile( + "(" + "|".join(re.escape(char) for char in list(meta_chars)) + ")" + ) meta_map = {char: "^{0}".format(char) for char in meta_chars} def escape_meta_chars(m): @@ -249,8 +250,8 @@ def escape_for_cmd_exe(arg): return meta_re.sub(escape_meta_chars, arg) -def broadcast_setting_change(message='Environment'): - ''' +def broadcast_setting_change(message="Environment"): + """ Send a WM_SETTINGCHANGE Broadcast to all Windows Args: @@ -290,7 +291,7 @@ def broadcast_setting_change(message='Environment'): import salt.utils.win_functions salt.utils.win_functions.broadcast_setting_change('Environment') - ''' + """ # Listen for messages sent by this would involve working with the # SetWindowLong function. This can be accessed via win32gui or through # ctypes. You can find examples on how to do this by searching for @@ -311,15 +312,21 @@ def broadcast_setting_change(message='Environment'): # new_function = WndProcType # old_function = user32.SetWindowLongW(window_handle, win32con.GWL_WNDPROC, new_function) broadcast_message = ctypes.create_unicode_buffer(message) - user32 = ctypes.WinDLL('user32', use_last_error=True) - result = user32.SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, - broadcast_message, SMTO_ABORTIFHUNG, - 5000, 0) + user32 = ctypes.WinDLL("user32", use_last_error=True) + result = user32.SendMessageTimeoutW( + HWND_BROADCAST, + WM_SETTINGCHANGE, + 0, + broadcast_message, + SMTO_ABORTIFHUNG, + 5000, + 0, + ) return result == 1 def guid_to_squid(guid): - ''' + """ Converts a GUID to a compressed guid (SQUID) Each Guid has 5 parts separated by '-'. For the first three each one will be @@ -339,10 +346,12 @@ def guid_to_squid(guid): Returns: str: A valid compressed GUID (SQUID) - ''' - guid_pattern = re.compile(r'^\{(\w{8})-(\w{4})-(\w{4})-(\w\w)(\w\w)-(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)\}$') + """ + guid_pattern = re.compile( + r"^\{(\w{8})-(\w{4})-(\w{4})-(\w\w)(\w\w)-(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)\}$" + ) guid_match = guid_pattern.match(guid) - squid = '' + squid = "" if guid_match is not None: for index in range(1, 12): squid += guid_match.group(index)[::-1] @@ -350,7 +359,7 @@ def guid_to_squid(guid): def squid_to_guid(squid): - ''' + """ Converts a compressed GUID (SQUID) back into a GUID Args: @@ -359,17 +368,26 @@ def squid_to_guid(squid): Returns: str: A valid GUID - ''' - squid_pattern = re.compile(r'^(\w{8})(\w{4})(\w{4})(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)$') + """ + squid_pattern = re.compile( + r"^(\w{8})(\w{4})(\w{4})(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)$" + ) squid_match = squid_pattern.match(squid) - guid = '' + guid = "" if squid_match is not None: - guid = '{' + \ - squid_match.group(1)[::-1]+'-' + \ - squid_match.group(2)[::-1]+'-' + \ - squid_match.group(3)[::-1]+'-' + \ - squid_match.group(4)[::-1]+squid_match.group(5)[::-1] + '-' + guid = ( + "{" + + squid_match.group(1)[::-1] + + "-" + + squid_match.group(2)[::-1] + + "-" + + squid_match.group(3)[::-1] + + "-" + + squid_match.group(4)[::-1] + + squid_match.group(5)[::-1] + + "-" + ) for index in range(6, 12): guid += squid_match.group(index)[::-1] - guid += '}' + guid += "}" return guid diff --git a/salt/utils/win_lgpo_auditpol.py b/salt/utils/win_lgpo_auditpol.py index b729728b8d5..e6474e2568c 100644 --- a/salt/utils/win_lgpo_auditpol.py +++ b/salt/utils/win_lgpo_auditpol.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" A salt util for modifying the audit policies on the machine. This util is used by the ``win_auditpol`` and ``win_lgpo`` modules. @@ -58,9 +58,10 @@ Usage: # Set the state of the "Credential Validation" setting to No Auditing salt.utils.win_lgpo_auditpol.set_setting(name='Credential Validation', value='No Auditing') -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import re import tempfile @@ -75,38 +76,42 @@ from salt.exceptions import CommandExecutionError from salt.ext.six.moves import zip log = logging.getLogger(__name__) -__virtualname__ = 'auditpol' +__virtualname__ = "auditpol" -categories = ['Account Logon', - 'Account Management', - 'Detailed Tracking', - 'DS Access', - 'Logon/Logoff', - 'Object Access', - 'Policy Change', - 'Privilege Use', - 'System'] +categories = [ + "Account Logon", + "Account Management", + "Detailed Tracking", + "DS Access", + "Logon/Logoff", + "Object Access", + "Policy Change", + "Privilege Use", + "System", +] -settings = {'No Auditing': '/success:disable /failure:disable', - 'Success': '/success:enable /failure:disable', - 'Failure': '/success:disable /failure:enable', - 'Success and Failure': '/success:enable /failure:enable'} +settings = { + "No Auditing": "/success:disable /failure:disable", + "Success": "/success:enable /failure:disable", + "Failure": "/success:disable /failure:enable", + "Success and Failure": "/success:enable /failure:enable", +} # Although utils are often directly imported, it is also possible to use the # loader. def __virtual__(): - ''' + """ Only load if on a Windows system - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'This utility only available on Windows' + return False, "This utility only available on Windows" return __virtualname__ def _auditpol_cmd(cmd): - ''' + """ Helper function for running the auditpol command Args: @@ -117,19 +122,18 @@ def _auditpol_cmd(cmd): Raises: CommandExecutionError: If the command encounters an error - ''' - ret = salt.modules.cmdmod.run_all(cmd='auditpol {0}'.format(cmd), - python_shell=True) - if ret['retcode'] == 0: - return ret['stdout'].splitlines() + """ + ret = salt.modules.cmdmod.run_all(cmd="auditpol {0}".format(cmd), python_shell=True) + if ret["retcode"] == 0: + return ret["stdout"].splitlines() - msg = 'Error executing auditpol command: {0}\n'.format(cmd) - msg += '\n'.join(ret['stdout']) + msg = "Error executing auditpol command: {0}\n".format(cmd) + msg += "\n".join(ret["stdout"]) raise CommandExecutionError(msg) -def get_settings(category='All'): - ''' +def get_settings(category="All"): + """ Get the current configuration for all audit settings specified in the category @@ -171,10 +175,10 @@ def get_settings(category='All'): # Get the current state of all audit settings in the "Account Logon" # category salt.utils.win_lgpo_auditpol.get_settings(category="Account Logon") - ''' + """ # Parameter validation - if category.lower() in ['all', '*']: - category = '*' + if category.lower() in ["all", "*"]: + category = "*" elif category.lower() not in [x.lower() for x in categories]: raise KeyError('Invalid category: "{0}"'.format(category)) @@ -184,13 +188,13 @@ def get_settings(category='All'): ret = {} # Skip the first 2 lines for line in results[3:]: - if ' ' in line.strip(): - ret.update(dict(list(zip(*[iter(re.split(r"\s{2,}", line.strip()))]*2)))) + if " " in line.strip(): + ret.update(dict(list(zip(*[iter(re.split(r"\s{2,}", line.strip()))] * 2)))) return ret def get_setting(name): - ''' + """ Get the current configuration for the named audit setting Args: @@ -211,23 +215,23 @@ def get_setting(name): # Get current state of the "Credential Validation" setting salt.utils.win_lgpo_auditpol.get_setting(name='Credential Validation') - ''' - current_settings = get_settings(category='All') + """ + current_settings = get_settings(category="All") for setting in current_settings: if name.lower() == setting.lower(): return current_settings[setting] - raise KeyError('Invalid name: {0}'.format(name)) + raise KeyError("Invalid name: {0}".format(name)) def _get_valid_names(): - if 'auditpol.valid_names' not in __context__: - settings = get_settings(category='All') - __context__['auditpol.valid_names'] = [k.lower() for k in settings] - return __context__['auditpol.valid_names'] + if "auditpol.valid_names" not in __context__: + settings = get_settings(category="All") + __context__["auditpol.valid_names"] = [k.lower() for k in settings] + return __context__["auditpol.valid_names"] def set_setting(name, value): - ''' + """ Set the configuration for the named audit setting Args: @@ -264,16 +268,16 @@ def set_setting(name, value): # Set the state of the "Credential Validation" setting to No Auditing salt.utils.win_lgpo_auditpol.set_setting(name='Credential Validation', value='No Auditing') - ''' + """ # Input validation if name.lower() not in _get_valid_names(): - raise KeyError('Invalid name: {0}'.format(name)) + raise KeyError("Invalid name: {0}".format(name)) for setting in settings: if value.lower() == setting.lower(): cmd = '/set /subcategory:"{0}" {1}'.format(name, settings[setting]) break else: - raise KeyError('Invalid setting value: {0}'.format(value)) + raise KeyError("Invalid setting value: {0}".format(value)) _auditpol_cmd(cmd) @@ -281,7 +285,7 @@ def set_setting(name, value): def get_auditpol_dump(): - ''' + """ Gets the contents of an auditpol /backup. Used by the LGPO module to get fieldnames and GUIDs for Advanced Audit policies. @@ -295,13 +299,13 @@ def get_auditpol_dump(): import salt.utils.win_lgpo_auditpol dump = salt.utils.win_lgpo_auditpol.get_auditpol_dump() - ''' + """ # Just get a temporary file name # NamedTemporaryFile will delete the file it creates by default on Windows - with tempfile.NamedTemporaryFile(suffix='.csv') as tmp_file: + with tempfile.NamedTemporaryFile(suffix=".csv") as tmp_file: csv_file = tmp_file.name - cmd = '/backup /file:{0}'.format(csv_file) + cmd = "/backup /file:{0}".format(csv_file) _auditpol_cmd(cmd) with salt.utils.files.fopen(csv_file) as fp: diff --git a/salt/utils/win_lgpo_netsh.py b/salt/utils/win_lgpo_netsh.py index 30cfb1afda8..722375bc396 100644 --- a/salt/utils/win_lgpo_netsh.py +++ b/salt/utils/win_lgpo_netsh.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" A salt util for modifying firewall settings. .. versionadded:: 2018.3.4 @@ -65,15 +65,16 @@ Usage: inbound='blockinbound', outbound='allowoutbound', store='lgpo') -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -from textwrap import dedent +from __future__ import absolute_import, print_function, unicode_literals + import logging import os import re import socket import tempfile +from textwrap import dedent import salt.modules.cmdmod from salt.exceptions import CommandExecutionError @@ -81,23 +82,23 @@ from salt.ext.six.moves import zip log = logging.getLogger(__name__) __hostname__ = socket.gethostname() -__virtualname__ = 'netsh' +__virtualname__ = "netsh" # Although utils are often directly imported, it is also possible to use the # loader. def __virtual__(): - ''' + """ Only load if on a Windows system - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'This utility only available on Windows' + return False, "This utility only available on Windows" return __virtualname__ def _netsh_file(content): - ''' + """ helper function to get the results of ``netsh -f content.txt`` Running ``netsh`` will drop you into a ``netsh`` prompt where you can issue @@ -113,40 +114,49 @@ def _netsh_file(content): Returns: str: The text returned by the netsh command - ''' - with tempfile.NamedTemporaryFile(mode='w', - prefix='salt-', - suffix='.netsh', - delete=False) as fp: + """ + with tempfile.NamedTemporaryFile( + mode="w", prefix="salt-", suffix=".netsh", delete=False + ) as fp: fp.write(content) try: - log.debug('{0}:\n{1}'.format(fp.name, content)) - return salt.modules.cmdmod.run('netsh -f {0}'.format(fp.name), python_shell=True) + log.debug("{0}:\n{1}".format(fp.name, content)) + return salt.modules.cmdmod.run( + "netsh -f {0}".format(fp.name), python_shell=True + ) finally: os.remove(fp.name) def _netsh_command(command, store): - if store.lower() not in ('local', 'lgpo'): - raise ValueError('Incorrect store: {0}'.format(store)) + if store.lower() not in ("local", "lgpo"): + raise ValueError("Incorrect store: {0}".format(store)) # set the store for local or lgpo - if store.lower() == 'local': - netsh_script = dedent('''\ + if store.lower() == "local": + netsh_script = dedent( + """\ advfirewall - set store local + set store local {0} - '''.format(command)) + """.format( + command + ) + ) else: - netsh_script = dedent('''\ + netsh_script = dedent( + """\ advfirewall set store gpo = {0} {1} - '''.format(__hostname__, command)) + """.format( + __hostname__, command + ) + ) return _netsh_file(content=netsh_script).splitlines() -def get_settings(profile, section, store='local'): - ''' +def get_settings(profile, section, store="local"): + """ Get the firewall property from the specified profile in the specified store as returned by ``netsh advfirewall``. @@ -183,15 +193,15 @@ def get_settings(profile, section, store='local'): Raises: CommandExecutionError: If an error occurs ValueError: If the parameters are incorrect - ''' + """ # validate input - if profile.lower() not in ('domain', 'public', 'private'): - raise ValueError('Incorrect profile: {0}'.format(profile)) - if section.lower() not in ('state', 'firewallpolicy', 'settings', 'logging'): - raise ValueError('Incorrect section: {0}'.format(section)) - if store.lower() not in ('local', 'lgpo'): - raise ValueError('Incorrect store: {0}'.format(store)) - command = 'show {0}profile {1}'.format(profile, section) + if profile.lower() not in ("domain", "public", "private"): + raise ValueError("Incorrect profile: {0}".format(profile)) + if section.lower() not in ("state", "firewallpolicy", "settings", "logging"): + raise ValueError("Incorrect section: {0}".format(section)) + if store.lower() not in ("local", "lgpo"): + raise ValueError("Incorrect store: {0}".format(store)) + command = "show {0}profile {1}".format(profile, section) # run it results = _netsh_command(command=command, store=store) # sample output: @@ -205,27 +215,27 @@ def get_settings(profile, section, store='local'): # if it's less than 3 lines it failed if len(results) < 3: - raise CommandExecutionError('Invalid results: {0}'.format(results)) + raise CommandExecutionError("Invalid results: {0}".format(results)) ret = {} # Skip the first 2 lines. Add everything else to a dictionary for line in results[3:]: - ret.update(dict(list(zip(*[iter(re.split(r"\s{2,}", line))]*2)))) + ret.update(dict(list(zip(*[iter(re.split(r"\s{2,}", line))] * 2)))) # Remove spaces from the values so that `Not Configured` is detected # correctly for item in ret: - ret[item] = ret[item].replace(' ', '') + ret[item] = ret[item].replace(" ", "") # special handling for firewallpolicy - if section == 'firewallpolicy': - inbound, outbound = ret['Firewall Policy'].split(',') - return {'Inbound': inbound, 'Outbound': outbound} + if section == "firewallpolicy": + inbound, outbound = ret["Firewall Policy"].split(",") + return {"Inbound": inbound, "Outbound": outbound} return ret -def get_all_settings(profile, store='local'): - ''' +def get_all_settings(profile, store="local"): + """ Gets all the properties for the specified profile in the specified store Args: @@ -248,17 +258,17 @@ def get_all_settings(profile, store='local'): Returns: dict: A dictionary containing the specified settings - ''' + """ ret = dict() - ret.update(get_settings(profile=profile, section='state', store=store)) - ret.update(get_settings(profile=profile, section='firewallpolicy', store=store)) - ret.update(get_settings(profile=profile, section='settings', store=store)) - ret.update(get_settings(profile=profile, section='logging', store=store)) + ret.update(get_settings(profile=profile, section="state", store=store)) + ret.update(get_settings(profile=profile, section="firewallpolicy", store=store)) + ret.update(get_settings(profile=profile, section="settings", store=store)) + ret.update(get_settings(profile=profile, section="logging", store=store)) return ret -def get_all_profiles(store='local'): - ''' +def get_all_profiles(store="local"): + """ Gets all properties for all profiles in the specified store Args: @@ -274,19 +284,16 @@ def get_all_profiles(store='local'): Returns: dict: A dictionary containing the specified settings for each profile - ''' + """ return { - 'Domain Profile': get_all_settings(profile='domain', store=store), - 'Private Profile': get_all_settings(profile='private', store=store), - 'Public Profile': get_all_settings(profile='public', store=store) + "Domain Profile": get_all_settings(profile="domain", store=store), + "Private Profile": get_all_settings(profile="private", store=store), + "Public Profile": get_all_settings(profile="public", store=store), } -def set_firewall_settings(profile, - inbound=None, - outbound=None, - store='local'): - ''' +def set_firewall_settings(profile, inbound=None, outbound=None, store="local"): + """ Set the firewall inbound/outbound settings for the specified profile and store @@ -335,47 +342,50 @@ def set_firewall_settings(profile, Raises: CommandExecutionError: If an error occurs ValueError: If the parameters are incorrect - ''' + """ # Input validation - if profile.lower() not in ('domain', 'public', 'private'): - raise ValueError('Incorrect profile: {0}'.format(profile)) - if inbound and inbound.lower() not in ('blockinbound', - 'blockinboundalways', - 'allowinbound', - 'notconfigured'): - raise ValueError('Incorrect inbound value: {0}'.format(inbound)) - if outbound and outbound.lower() not in ('allowoutbound', - 'blockoutbound', - 'notconfigured'): - raise ValueError('Incorrect outbound value: {0}'.format(outbound)) + if profile.lower() not in ("domain", "public", "private"): + raise ValueError("Incorrect profile: {0}".format(profile)) + if inbound and inbound.lower() not in ( + "blockinbound", + "blockinboundalways", + "allowinbound", + "notconfigured", + ): + raise ValueError("Incorrect inbound value: {0}".format(inbound)) + if outbound and outbound.lower() not in ( + "allowoutbound", + "blockoutbound", + "notconfigured", + ): + raise ValueError("Incorrect outbound value: {0}".format(outbound)) if not inbound and not outbound: - raise ValueError('Must set inbound or outbound') + raise ValueError("Must set inbound or outbound") # You have to specify inbound and outbound setting at the same time # If you're only specifying one, you have to get the current setting for the # other if not inbound or not outbound: - ret = get_settings(profile=profile, - section='firewallpolicy', - store=store) + ret = get_settings(profile=profile, section="firewallpolicy", store=store) if not inbound: - inbound = ret['Inbound'] + inbound = ret["Inbound"] if not outbound: - outbound = ret['Outbound'] + outbound = ret["Outbound"] - command = 'set {0}profile firewallpolicy {1},{2}' \ - ''.format(profile, inbound, outbound) + command = "set {0}profile firewallpolicy {1},{2}" "".format( + profile, inbound, outbound + ) results = _netsh_command(command=command, store=store) if results: - raise CommandExecutionError('An error occurred: {0}'.format(results)) + raise CommandExecutionError("An error occurred: {0}".format(results)) return True -def set_logging_settings(profile, setting, value, store='local'): - ''' +def set_logging_settings(profile, setting, value, store="local"): + """ Configure logging settings for the Windows firewall. Args: @@ -436,42 +446,44 @@ def set_logging_settings(profile, setting, value, store='local'): Raises: CommandExecutionError: If an error occurs ValueError: If the parameters are incorrect - ''' + """ # Input validation - if profile.lower() not in ('domain', 'public', 'private'): - raise ValueError('Incorrect profile: {0}'.format(profile)) - if setting.lower() not in ('allowedconnections', - 'droppedconnections', - 'filename', - 'maxfilesize'): - raise ValueError('Incorrect setting: {0}'.format(setting)) - if setting.lower() in ('allowedconnections', 'droppedconnections'): - if value.lower() not in ('enable', 'disable', 'notconfigured'): - raise ValueError('Incorrect value: {0}'.format(value)) + if profile.lower() not in ("domain", "public", "private"): + raise ValueError("Incorrect profile: {0}".format(profile)) + if setting.lower() not in ( + "allowedconnections", + "droppedconnections", + "filename", + "maxfilesize", + ): + raise ValueError("Incorrect setting: {0}".format(setting)) + if setting.lower() in ("allowedconnections", "droppedconnections"): + if value.lower() not in ("enable", "disable", "notconfigured"): + raise ValueError("Incorrect value: {0}".format(value)) # TODO: Consider adding something like the following to validate filename # https://stackoverflow.com/questions/9532499/check-whether-a-path-is-valid-in-python-without-creating-a-file-at-the-paths-ta - if setting.lower() == 'maxfilesize': - if value.lower() != 'notconfigured': + if setting.lower() == "maxfilesize": + if value.lower() != "notconfigured": # Must be a number between 1 and 32767 try: int(value) except ValueError: - raise ValueError('Incorrect value: {0}'.format(value)) + raise ValueError("Incorrect value: {0}".format(value)) if not 1 <= int(value) <= 32767: - raise ValueError('Incorrect value: {0}'.format(value)) + raise ValueError("Incorrect value: {0}".format(value)) # Run the command - command = 'set {0}profile logging {1} {2}'.format(profile, setting, value) + command = "set {0}profile logging {1} {2}".format(profile, setting, value) results = _netsh_command(command=command, store=store) # A successful run should return an empty list if results: - raise CommandExecutionError('An error occurred: {0}'.format(results)) + raise CommandExecutionError("An error occurred: {0}".format(results)) return True -def set_settings(profile, setting, value, store='local'): - ''' +def set_settings(profile, setting, value, store="local"): + """ Configure firewall settings. Args: @@ -514,32 +526,34 @@ def set_settings(profile, setting, value, store='local'): Raises: CommandExecutionError: If an error occurs ValueError: If the parameters are incorrect - ''' + """ # Input validation - if profile.lower() not in ('domain', 'public', 'private'): - raise ValueError('Incorrect profile: {0}'.format(profile)) - if setting.lower() not in ('localfirewallrules', - 'localconsecrules', - 'inboundusernotification', - 'remotemanagement', - 'unicastresponsetomulticast'): - raise ValueError('Incorrect setting: {0}'.format(setting)) - if value.lower() not in ('enable', 'disable', 'notconfigured'): - raise ValueError('Incorrect value: {0}'.format(value)) + if profile.lower() not in ("domain", "public", "private"): + raise ValueError("Incorrect profile: {0}".format(profile)) + if setting.lower() not in ( + "localfirewallrules", + "localconsecrules", + "inboundusernotification", + "remotemanagement", + "unicastresponsetomulticast", + ): + raise ValueError("Incorrect setting: {0}".format(setting)) + if value.lower() not in ("enable", "disable", "notconfigured"): + raise ValueError("Incorrect value: {0}".format(value)) # Run the command - command = 'set {0}profile settings {1} {2}'.format(profile, setting, value) + command = "set {0}profile settings {1} {2}".format(profile, setting, value) results = _netsh_command(command=command, store=store) # A successful run should return an empty list if results: - raise CommandExecutionError('An error occurred: {0}'.format(results)) + raise CommandExecutionError("An error occurred: {0}".format(results)) return True -def set_state(profile, state, store='local'): - ''' +def set_state(profile, state, store="local"): + """ Configure the firewall state. Args: @@ -573,19 +587,19 @@ def set_state(profile, state, store='local'): Raises: CommandExecutionError: If an error occurs ValueError: If the parameters are incorrect - ''' + """ # Input validation - if profile.lower() not in ('domain', 'public', 'private'): - raise ValueError('Incorrect profile: {0}'.format(profile)) - if state.lower() not in ('on', 'off', 'notconfigured'): - raise ValueError('Incorrect state: {0}'.format(state)) + if profile.lower() not in ("domain", "public", "private"): + raise ValueError("Incorrect profile: {0}".format(profile)) + if state.lower() not in ("on", "off", "notconfigured"): + raise ValueError("Incorrect state: {0}".format(state)) # Run the command - command = 'set {0}profile state {1}'.format(profile, state) + command = "set {0}profile state {1}".format(profile, state) results = _netsh_command(command=command, store=store) # A successful run should return an empty list if results: - raise CommandExecutionError('An error occurred: {0}'.format(results)) + raise CommandExecutionError("An error occurred: {0}".format(results)) return True diff --git a/salt/utils/win_network.py b/salt/utils/win_network.py index 26c0e5ba90d..1259dbecbda 100644 --- a/salt/utils/win_network.py +++ b/salt/utils/win_network.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" This salt util uses WMI to gather network information on Windows 7 and .NET 4.0+ on newer systems. The reason for this is that calls to WMI tend to be slower. Especially if the @@ -18,26 +18,27 @@ depending on the the version of Windows this is run on. Once support for Windows 7 is dropped we can remove the WMI stuff and just use .NET. :depends: - pythonnet - wmi -''' +""" # https://docs.microsoft.com/en-us/dotnet/api/system.net.networkinformation.networkinterface.getallnetworkinterfaces?view=netframework-4.7.2 # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import ipaddress import platform -# Import Salt libs -from salt.utils.versions import StrictVersion - # Import 3rd party libs # I don't understand why I need this import, but the linter fails without it from salt.ext.six.moves import range -IS_WINDOWS = platform.system() == 'Windows' +# Import Salt libs +from salt.utils.versions import StrictVersion -__virtualname__ = 'win_network' +IS_WINDOWS = platform.system() == "Windows" + +__virtualname__ = "win_network" if IS_WINDOWS: - USE_WMI = StrictVersion(platform.version()) < StrictVersion('6.2') + USE_WMI = StrictVersion(platform.version()) < StrictVersion("6.2") if USE_WMI: import wmi import salt.utils.winapi @@ -46,63 +47,66 @@ if IS_WINDOWS: from System.Net import NetworkInformation enum_adapter_types = { - 1: 'Unknown', - 6: 'Ethernet', - 9: 'TokenRing', - 15: 'FDDI', - 20: 'BasicISDN', - 21: 'PrimaryISDN', - 23: 'PPP', - 24: 'Loopback', - 26: 'Ethernet3Megabit', - 28: 'Slip', - 37: 'ATM', - 48: 'GenericModem', - 53: 'TAPAdapter', # Not in MSDN Defined enumeration - 62: 'FastEthernetT', - 63: 'ISDN', - 69: 'FastEthernetFx', - 71: 'Wireless802.11', - 94: 'AsymmetricDSL', - 95: 'RateAdaptDSL', - 96: 'SymmetricDSL', - 97: 'VeryHighSpeedDSL', - 114: 'IPOverATM', - 117: 'GigabitEthernet', - 131: 'Tunnel', - 143: 'MultiRateSymmetricDSL', - 144: 'HighPerformanceSerialBus', - 237: 'WMAN', - 243: 'WWANPP', - 244: 'WWANPP2'} + 1: "Unknown", + 6: "Ethernet", + 9: "TokenRing", + 15: "FDDI", + 20: "BasicISDN", + 21: "PrimaryISDN", + 23: "PPP", + 24: "Loopback", + 26: "Ethernet3Megabit", + 28: "Slip", + 37: "ATM", + 48: "GenericModem", + 53: "TAPAdapter", # Not in MSDN Defined enumeration + 62: "FastEthernetT", + 63: "ISDN", + 69: "FastEthernetFx", + 71: "Wireless802.11", + 94: "AsymmetricDSL", + 95: "RateAdaptDSL", + 96: "SymmetricDSL", + 97: "VeryHighSpeedDSL", + 114: "IPOverATM", + 117: "GigabitEthernet", + 131: "Tunnel", + 143: "MultiRateSymmetricDSL", + 144: "HighPerformanceSerialBus", + 237: "WMAN", + 243: "WWANPP", + 244: "WWANPP2", +} enum_operational_status = { - 1: 'Up', - 2: 'Down', - 3: 'Testing', - 4: 'Unknown', - 5: 'Dormant', - 6: 'NotPresent', - 7: 'LayerDown'} + 1: "Up", + 2: "Down", + 3: "Testing", + 4: "Unknown", + 5: "Dormant", + 6: "NotPresent", + 7: "LayerDown", +} enum_prefix_suffix = { - 0: 'Other', - 1: 'Manual', - 2: 'WellKnown', - 3: 'DHCP', - 4: 'Router', - 5: 'Random'} + 0: "Other", + 1: "Manual", + 2: "WellKnown", + 3: "DHCP", + 4: "Router", + 5: "Random", +} af_inet = 2 af_inet6 = 23 def __virtual__(): - ''' + """ Only load if windows - ''' + """ if not IS_WINDOWS: - return False, 'This utility will only run on Windows' + return False, "This utility will only run on Windows" return __virtualname__ @@ -114,52 +118,58 @@ def _get_base_properties(i_face): except KeyError: i_face_type = i_face.Description return { - 'alias': i_face.Name, - 'description': i_face.Description, - 'id': i_face.Id, - 'receive_only': i_face.IsReceiveOnly, - 'type': i_face_type, - 'status': enum_operational_status[i_face.OperationalStatus], - 'physical_address': ':'.join(raw_mac[i:i+2] for i in range(0, 12, 2))} + "alias": i_face.Name, + "description": i_face.Description, + "id": i_face.Id, + "receive_only": i_face.IsReceiveOnly, + "type": i_face_type, + "status": enum_operational_status[i_face.OperationalStatus], + "physical_address": ":".join(raw_mac[i : i + 2] for i in range(0, 12, 2)), + } def _get_ip_base_properties(i_face): ip_properties = i_face.GetIPProperties() - return {'dns_suffix': ip_properties.DnsSuffix, - 'dns_enabled': ip_properties.IsDnsEnabled, - 'dynamic_dns_enabled': ip_properties.IsDynamicDnsEnabled} + return { + "dns_suffix": ip_properties.DnsSuffix, + "dns_enabled": ip_properties.IsDnsEnabled, + "dynamic_dns_enabled": ip_properties.IsDynamicDnsEnabled, + } def _get_ip_unicast_info(i_face): ip_properties = i_face.GetIPProperties() int_dict = {} if ip_properties.UnicastAddresses.Count > 0: - names = {af_inet: 'ip_addresses', - af_inet6: 'ipv6_addresses'} + names = {af_inet: "ip_addresses", af_inet6: "ipv6_addresses"} for addrs in ip_properties.UnicastAddresses: if addrs.Address.AddressFamily == af_inet: ip = addrs.Address.ToString() mask = addrs.IPv4Mask.ToString() - net = ipaddress.IPv4Network(ip + '/' + mask, False) + net = ipaddress.IPv4Network(ip + "/" + mask, False) ip_info = { - 'address': ip, - 'netmask': mask, - 'broadcast': net.broadcast_address.compressed, - 'loopback': addrs.Address.Loopback.ToString()} + "address": ip, + "netmask": mask, + "broadcast": net.broadcast_address.compressed, + "loopback": addrs.Address.Loopback.ToString(), + } else: ip_info = { - 'address': addrs.Address.ToString().split('%')[0], + "address": addrs.Address.ToString().split("%")[0], # ScopeID is a suffix affixed to the end of an IPv6 # address it denotes the adapter. This is different from # ScopeLevel. Need to figure out how to get ScopeLevel # for feature parity with Linux - 'interface_index': int(addrs.Address.ScopeId)} - ip_info.update({ - 'prefix_length': addrs.PrefixLength, - 'prefix_origin': enum_prefix_suffix[addrs.PrefixOrigin], - 'suffix_origin': enum_prefix_suffix[addrs.SuffixOrigin]}) - int_dict.setdefault( - names[addrs.Address.AddressFamily], []).append(ip_info) + "interface_index": int(addrs.Address.ScopeId), + } + ip_info.update( + { + "prefix_length": addrs.PrefixLength, + "prefix_origin": enum_prefix_suffix[addrs.PrefixOrigin], + "suffix_origin": enum_prefix_suffix[addrs.SuffixOrigin], + } + ) + int_dict.setdefault(names[addrs.Address.AddressFamily], []).append(ip_info) return int_dict @@ -167,12 +177,11 @@ def _get_ip_gateway_info(i_face): ip_properties = i_face.GetIPProperties() int_dict = {} if ip_properties.GatewayAddresses.Count > 0: - names = {af_inet: 'ip_gateways', - af_inet6: 'ipv6_gateways'} + names = {af_inet: "ip_gateways", af_inet6: "ipv6_gateways"} for addrs in ip_properties.GatewayAddresses: - int_dict.setdefault( - names[addrs.Address.AddressFamily], - []).append(addrs.Address.ToString().split('%')[0]) + int_dict.setdefault(names[addrs.Address.AddressFamily], []).append( + addrs.Address.ToString().split("%")[0] + ) return int_dict @@ -180,12 +189,11 @@ def _get_ip_dns_info(i_face): ip_properties = i_face.GetIPProperties() int_dict = {} if ip_properties.DnsAddresses.Count > 0: - names = {af_inet: 'ip_dns', - af_inet6: 'ipv6_dns'} + names = {af_inet: "ip_dns", af_inet6: "ipv6_dns"} for addrs in ip_properties.DnsAddresses: - int_dict.setdefault( - names[addrs.AddressFamily], - []).append(addrs.ToString().split('%')[0]) + int_dict.setdefault(names[addrs.AddressFamily], []).append( + addrs.ToString().split("%")[0] + ) return int_dict @@ -193,12 +201,11 @@ def _get_ip_multicast_info(i_face): ip_properties = i_face.GetIPProperties() int_dict = {} if ip_properties.MulticastAddresses.Count > 0: - names = {af_inet: 'ip_multicast', - af_inet6: 'ipv6_multicast'} + names = {af_inet: "ip_multicast", af_inet6: "ipv6_multicast"} for addrs in ip_properties.MulticastAddresses: - int_dict.setdefault( - names[addrs.Address.AddressFamily], - []).append(addrs.Address.ToString().split('%')[0]) + int_dict.setdefault(names[addrs.Address.AddressFamily], []).append( + addrs.Address.ToString().split("%")[0] + ) return int_dict @@ -206,12 +213,11 @@ def _get_ip_anycast_info(i_face): ip_properties = i_face.GetIPProperties() int_dict = {} if ip_properties.AnycastAddresses.Count > 0: - names = {af_inet: 'ip_anycast', - af_inet6: 'ipv6_anycast'} + names = {af_inet: "ip_anycast", af_inet6: "ipv6_anycast"} for addrs in ip_properties.AnycastAddresses: - int_dict.setdefault( - names[addrs.Address.AddressFamily], - []).append(addrs.Address.ToString()) + int_dict.setdefault(names[addrs.Address.AddressFamily], []).append( + addrs.Address.ToString() + ) return int_dict @@ -220,58 +226,62 @@ def _get_ip_wins_info(i_face): int_dict = {} if ip_properties.WinsServersAddresses.Count > 0: for addrs in ip_properties.WinsServersAddresses: - int_dict.setdefault( - 'ip_wins', []).append(addrs.ToString()) + int_dict.setdefault("ip_wins", []).append(addrs.ToString()) return int_dict def _get_network_interfaces(): - clr.AddReference('System.Net') + clr.AddReference("System.Net") return NetworkInformation.NetworkInterface.GetAllNetworkInterfaces() def get_interface_info_dot_net_formatted(): - ''' + """ Returns data gathered via ``get_interface_info_dot_net`` and returns the info in the same manner as ``get_interface_info_wmi`` Returns: dict: A dictionary of information about all interfaces on the system - ''' + """ # Massage the data returned by dotnet to mirror that returned by wmi interfaces = get_interface_info_dot_net() i_faces = {} for i_face in interfaces: - if interfaces[i_face]['status'] == 'Up': - name = interfaces[i_face]['description'] - i_faces.setdefault(name, {}).update({ - 'hwaddr': interfaces[i_face]['physical_address'], - 'up': True}) - for ip in interfaces[i_face].get('ip_addresses', []): - i_faces[name].setdefault('inet', []).append({ - 'address': ip['address'], - 'broadcast': ip['broadcast'], - 'netmask': ip['netmask'], - 'gateway': interfaces[i_face].get('ip_gateways', [''])[0], - 'label': name}) - for ip in interfaces[i_face].get('ipv6_addresses', []): - i_faces[name].setdefault('inet6', []).append({ - 'address': ip['address'], - 'gateway': interfaces[i_face].get('ipv6_gateways', [''])[0], - # Add prefix length - }) + if interfaces[i_face]["status"] == "Up": + name = interfaces[i_face]["description"] + i_faces.setdefault(name, {}).update( + {"hwaddr": interfaces[i_face]["physical_address"], "up": True} + ) + for ip in interfaces[i_face].get("ip_addresses", []): + i_faces[name].setdefault("inet", []).append( + { + "address": ip["address"], + "broadcast": ip["broadcast"], + "netmask": ip["netmask"], + "gateway": interfaces[i_face].get("ip_gateways", [""])[0], + "label": name, + } + ) + for ip in interfaces[i_face].get("ipv6_addresses", []): + i_faces[name].setdefault("inet6", []).append( + { + "address": ip["address"], + "gateway": interfaces[i_face].get("ipv6_gateways", [""])[0], + # Add prefix length + } + ) return i_faces def get_interface_info_dot_net(): - ''' + """ Uses .NET 4.0+ to gather Network Interface information. Should only run on Windows systems newer than Windows 7/Server 2008R2 Returns: dict: A dictionary of information about all interfaces on the system - ''' + """ interfaces = {} for i_face in _get_network_interfaces(): temp_dict = _get_base_properties(i_face) @@ -288,64 +298,67 @@ def get_interface_info_dot_net(): def get_interface_info_wmi(): - ''' + """ Uses WMI to gather Network Interface information. Should only run on Windows 7/2008 R2 and lower systems. This code was pulled from the ``win_interfaces`` function in ``salt.utils.network`` unchanged. Returns: dict: A dictionary of information about all interfaces on the system - ''' + """ with salt.utils.winapi.Com(): c = wmi.WMI() i_faces = {} for i_face in c.Win32_NetworkAdapterConfiguration(IPEnabled=1): i_faces[i_face.Description] = {} if i_face.MACAddress: - i_faces[i_face.Description]['hwaddr'] = i_face.MACAddress + i_faces[i_face.Description]["hwaddr"] = i_face.MACAddress if i_face.IPEnabled: - i_faces[i_face.Description]['up'] = True + i_faces[i_face.Description]["up"] = True for ip in i_face.IPAddress: - if '.' in ip: - if 'inet' not in i_faces[i_face.Description]: - i_faces[i_face.Description]['inet'] = [] - item = {'address': ip, - 'label': i_face.Description} + if "." in ip: + if "inet" not in i_faces[i_face.Description]: + i_faces[i_face.Description]["inet"] = [] + item = {"address": ip, "label": i_face.Description} if i_face.DefaultIPGateway: - broadcast = next((i for i in i_face.DefaultIPGateway if '.' in i), '') + broadcast = next( + (i for i in i_face.DefaultIPGateway if "." in i), "" + ) if broadcast: - item['broadcast'] = broadcast + item["broadcast"] = broadcast if i_face.IPSubnet: - netmask = next((i for i in i_face.IPSubnet if '.' in i), '') + netmask = next((i for i in i_face.IPSubnet if "." in i), "") if netmask: - item['netmask'] = netmask - i_faces[i_face.Description]['inet'].append(item) - if ':' in ip: - if 'inet6' not in i_faces[i_face.Description]: - i_faces[i_face.Description]['inet6'] = [] - item = {'address': ip} + item["netmask"] = netmask + i_faces[i_face.Description]["inet"].append(item) + if ":" in ip: + if "inet6" not in i_faces[i_face.Description]: + i_faces[i_face.Description]["inet6"] = [] + item = {"address": ip} if i_face.DefaultIPGateway: - broadcast = next((i for i in i_face.DefaultIPGateway if ':' in i), '') + broadcast = next( + (i for i in i_face.DefaultIPGateway if ":" in i), "" + ) if broadcast: - item['broadcast'] = broadcast + item["broadcast"] = broadcast if i_face.IPSubnet: - netmask = next((i for i in i_face.IPSubnet if ':' in i), '') + netmask = next((i for i in i_face.IPSubnet if ":" in i), "") if netmask: - item['netmask'] = netmask - i_faces[i_face.Description]['inet6'].append(item) + item["netmask"] = netmask + i_faces[i_face.Description]["inet6"].append(item) else: - i_faces[i_face.Description]['up'] = False + i_faces[i_face.Description]["up"] = False return i_faces def get_interface_info(): - ''' + """ This function will return network interface information for the system and will use the best method to retrieve that information. Windows 7/2008R2 and below will use WMI. Newer systems will use .NET. Returns: dict: A dictionary of information about the Network interfaces - ''' + """ # On Windows 7 machines, use WMI as dotnet 4.0 is not available by default if USE_WMI: return get_interface_info_wmi() diff --git a/salt/utils/win_osinfo.py b/salt/utils/win_osinfo.py index ad3f2bf8b03..7e4d7e97e24 100644 --- a/salt/utils/win_osinfo.py +++ b/salt/utils/win_osinfo.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Get Version information from Windows -''' +""" # http://stackoverflow.com/questions/32300004/python-ctypes-getting-0-with-getversionex-function from __future__ import absolute_import, print_function, unicode_literals # Import Third Party Libs import ctypes + HAS_WIN32 = True try: from ctypes.wintypes import BYTE, WORD, DWORD, WCHAR @@ -16,40 +17,44 @@ except (ImportError, ValueError): HAS_WIN32 = False if HAS_WIN32: - kernel32 = ctypes.WinDLL(str('kernel32'), # future lint: disable=blacklisted-function - use_last_error=True) + kernel32 = ctypes.WinDLL( + str("kernel32"), # future lint: disable=blacklisted-function + use_last_error=True, + ) # Although utils are often directly imported, it is also possible to use the # loader. def __virtual__(): - ''' + """ Only load if Win32 Libraries are installed - ''' + """ if not HAS_WIN32: - return False, 'This utility requires pywin32' + return False, "This utility requires pywin32" - return 'win_osinfo' + return "win_osinfo" def os_version_info_ex(): - ''' + """ Helper function to return the results of the GetVersionExW Windows API call. It is a ctypes Structure that contains Windows OS Version information. Returns: class: An instance of a class containing version info - ''' + """ if not HAS_WIN32: return class OSVersionInfo(ctypes.Structure): - _fields_ = (('dwOSVersionInfoSize', DWORD), - ('dwMajorVersion', DWORD), - ('dwMinorVersion', DWORD), - ('dwBuildNumber', DWORD), - ('dwPlatformId', DWORD), - ('szCSDVersion', WCHAR * 128)) + _fields_ = ( + ("dwOSVersionInfoSize", DWORD), + ("dwMajorVersion", DWORD), + ("dwMinorVersion", DWORD), + ("dwBuildNumber", DWORD), + ("dwPlatformId", DWORD), + ("szCSDVersion", WCHAR * 128), + ) def __init__(self, *args, **kwds): super(OSVersionInfo, self).__init__(*args, **kwds) @@ -57,31 +62,35 @@ def os_version_info_ex(): kernel32.GetVersionExW(ctypes.byref(self)) class OSVersionInfoEx(OSVersionInfo): - _fields_ = (('wServicePackMajor', WORD), - ('wServicePackMinor', WORD), - ('wSuiteMask', WORD), - ('wProductType', BYTE), - ('wReserved', BYTE)) + _fields_ = ( + ("wServicePackMajor", WORD), + ("wServicePackMinor", WORD), + ("wSuiteMask", WORD), + ("wProductType", BYTE), + ("wReserved", BYTE), + ) return OSVersionInfoEx() def get_os_version_info(): info = os_version_info_ex() - ret = {'MajorVersion': info.dwMajorVersion, - 'MinorVersion': info.dwMinorVersion, - 'BuildNumber': info.dwBuildNumber, - 'PlatformID': info.dwPlatformId, - 'ServicePackMajor': info.wServicePackMajor, - 'ServicePackMinor': info.wServicePackMinor, - 'SuiteMask': info.wSuiteMask, - 'ProductType': info.wProductType} + ret = { + "MajorVersion": info.dwMajorVersion, + "MinorVersion": info.dwMinorVersion, + "BuildNumber": info.dwBuildNumber, + "PlatformID": info.dwPlatformId, + "ServicePackMajor": info.wServicePackMajor, + "ServicePackMinor": info.wServicePackMinor, + "SuiteMask": info.wSuiteMask, + "ProductType": info.wProductType, + } return ret def get_join_info(): - ''' + """ Gets information about the domain/workgroup. This will tell you if the system is joined to a domain or a workgroup @@ -89,11 +98,12 @@ def get_join_info(): Returns: dict: A dictionary containing the domain/workgroup and its status - ''' + """ info = win32net.NetGetJoinInformation() - status = {win32netcon.NetSetupUnknown: 'Unknown', - win32netcon.NetSetupUnjoined: 'Unjoined', - win32netcon.NetSetupWorkgroupName: 'Workgroup', - win32netcon.NetSetupDomainName: 'Domain'} - return {'Domain': info[0], - 'DomainType': status[info[1]]} + status = { + win32netcon.NetSetupUnknown: "Unknown", + win32netcon.NetSetupUnjoined: "Unjoined", + win32netcon.NetSetupWorkgroupName: "Workgroup", + win32netcon.NetSetupDomainName: "Domain", + } + return {"Domain": info[0], "DomainType": status[info[1]]} diff --git a/salt/utils/win_pdh.py b/salt/utils/win_pdh.py index 9921ee72da5..da2901938e3 100644 --- a/salt/utils/win_pdh.py +++ b/salt/utils/win_pdh.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -r''' +r""" Salt Util for getting system information with the Performance Data Helper (pdh). Counter information is gathered from current activity or log files. @@ -31,50 +31,55 @@ Usage: # Get all counters for the Processor object salt.utils.win_pdh.get_all_counters('Processor') -''' -# https://www.cac.cornell.edu/wiki/index.php?title=Performance_Data_Helper_in_Python_with_win32pdh +""" + # https://docs.microsoft.com/en-us/windows/desktop/perfctrs/using-the-pdh-functions-to-consume-counter-data # Import python libs from __future__ import absolute_import, unicode_literals + +# https://www.cac.cornell.edu/wiki/index.php?title=Performance_Data_Helper_in_Python_with_win32pdh import logging import time -# Import 3rd party libs -try: - import pywintypes - import win32pdh - HAS_WINDOWS_MODULES = True -except ImportError: - HAS_WINDOWS_MODULES = False - # Import salt libs import salt.utils.platform from salt.exceptions import CommandExecutionError +# Import 3rd party libs +try: + import pywintypes + import win32pdh + + HAS_WINDOWS_MODULES = True +except ImportError: + HAS_WINDOWS_MODULES = False + + log = logging.getLogger(__file__) # Define the virtual name -__virtualname__ = 'pdh' +__virtualname__ = "pdh" def __virtual__(): - ''' + """ Only works on Windows systems with the PyWin32 - ''' + """ if not salt.utils.platform.is_windows(): - return False, 'salt.utils.win_pdh: Requires Windows' + return False, "salt.utils.win_pdh: Requires Windows" if not HAS_WINDOWS_MODULES: - return False, 'salt.utils.win_pdh: Missing required modules' + return False, "salt.utils.win_pdh: Missing required modules" return __virtualname__ class Counter(object): - ''' + """ Counter object Has enumerations and functions for working with counters - ''' + """ + # The dwType field from GetCounterInfo returns the following, or'ed. # These come from WinPerf.h PERF_SIZE_DWORD = 0x00000000 @@ -130,7 +135,7 @@ class Counter(object): PERF_DISPLAY_NO_SHOW = 0x40000000 # value is not displayed def build_counter(obj, instance, instance_index, counter): - r''' + r""" Makes a fully resolved counter path. Counter names are formatted like this: @@ -161,12 +166,13 @@ class Counter(object): Raises: CommandExecutionError: If the path is invalid - ''' + """ path = win32pdh.MakeCounterPath( - (None, obj, instance, None, instance_index, counter), 0) + (None, obj, instance, None, instance_index, counter), 0 + ) if win32pdh.ValidatePath(path) is 0: return Counter(path, obj, instance, instance_index, counter) - raise CommandExecutionError('Invalid counter specified: {0}'.format(path)) + raise CommandExecutionError("Invalid counter specified: {0}".format(path)) build_counter = staticmethod(build_counter) @@ -181,62 +187,63 @@ class Counter(object): self.type = None def add_to_query(self, query): - ''' + """ Add the current path to the query Args: query (obj): The handle to the query to add the counter - ''' + """ self.handle = win32pdh.AddCounter(query, self.path) def get_info(self): - ''' + """ Get information about the counter .. note:: GetCounterInfo sometimes crashes in the wrapper code. Fewer crashes if this is called after sampling data. - ''' + """ if not self.info: ci = win32pdh.GetCounterInfo(self.handle, 0) self.info = { - 'type': ci[0], - 'version': ci[1], - 'scale': ci[2], - 'default_scale': ci[3], - 'user_data': ci[4], - 'query_user_data': ci[5], - 'full_path': ci[6], - 'machine_name': ci[7][0], - 'object_name': ci[7][1], - 'instance_name': ci[7][2], - 'parent_instance': ci[7][3], - 'instance_index': ci[7][4], - 'counter_name': ci[7][5], - 'explain_text': ci[8] + "type": ci[0], + "version": ci[1], + "scale": ci[2], + "default_scale": ci[3], + "user_data": ci[4], + "query_user_data": ci[5], + "full_path": ci[6], + "machine_name": ci[7][0], + "object_name": ci[7][1], + "instance_name": ci[7][2], + "parent_instance": ci[7][3], + "instance_index": ci[7][4], + "counter_name": ci[7][5], + "explain_text": ci[8], } return self.info def value(self): - ''' + """ Return the counter value Returns: long: The counter value - ''' + """ (counter_type, value) = win32pdh.GetFormattedCounterValue( - self.handle, win32pdh.PDH_FMT_DOUBLE) + self.handle, win32pdh.PDH_FMT_DOUBLE + ) self.type = counter_type return value def type_string(self): - ''' + """ Returns the names of the flags that are set in the Type field It can be used to format the counter. - ''' - type = self.get_info()['type'] + """ + type = self.get_info()["type"] type_list = [] for member in dir(self): if member.startswith("PERF_"): @@ -250,17 +257,17 @@ class Counter(object): def list_objects(): - ''' + """ Get a list of available counter objects on the system Returns: list: A list of counter objects - ''' + """ return sorted(win32pdh.EnumObjects(None, None, -1, 0)) def list_counters(obj): - ''' + """ Get a list of counters available for the object Args: @@ -270,12 +277,12 @@ def list_counters(obj): Returns: list: A list of counters available to the passed object - ''' + """ return win32pdh.EnumObjectItems(None, None, obj, -1, 0)[0] def list_instances(obj): - ''' + """ Get a list of instances available for the object Args: @@ -285,12 +292,12 @@ def list_instances(obj): Returns: list: A list of instances available to the passed object - ''' + """ return win32pdh.EnumObjectItems(None, None, obj, -1, 0)[1] def build_counter_list(counter_list): - r''' + r""" Create a list of Counter objects to be used in the pdh query Args: @@ -319,7 +326,7 @@ def build_counter_list(counter_list): Returns: list: A list of Counter objects - ''' + """ counters = [] index = 0 for obj, instance, counter_name in counter_list: @@ -335,7 +342,7 @@ def build_counter_list(counter_list): def get_all_counters(obj, instance_list=None): - ''' + """ Get the values for all counters available to a Counter object Args: @@ -350,7 +357,7 @@ def get_all_counters(obj, instance_list=None): .. note:: ``_Total`` is returned as ``*`` - ''' + """ counters, instances_avail = win32pdh.EnumObjectItems(None, None, obj, -1, 0) if instance_list is None: @@ -362,7 +369,7 @@ def get_all_counters(obj, instance_list=None): counter_list = [] for counter in counters: for instance in instance_list: - instance = '*' if instance.lower() == '_total' else instance + instance = "*" if instance.lower() == "_total" else instance counter_list.append((obj, instance, counter)) else: # pylint: disable=useless-else-on-loop counter_list.append((obj, None, counter)) @@ -371,7 +378,7 @@ def get_all_counters(obj, instance_list=None): def get_counters(counter_list): - ''' + """ Get the values for the passes list of counters Args: @@ -380,9 +387,9 @@ def get_counters(counter_list): Returns: dict: A dictionary of counters and their values - ''' + """ if not isinstance(counter_list, list): - raise CommandExecutionError('counter_list must be a list of tuples') + raise CommandExecutionError("counter_list must be a list of tuples") try: # Start a Query instances @@ -407,7 +414,7 @@ def get_counters(counter_list): try: ret.update({counter.path: counter.value()}) except pywintypes.error as exc: - if exc.strerror == 'No data to return.': + if exc.strerror == "No data to return.": # Some counters are not active and will throw an error if # there is no data to return continue @@ -421,7 +428,7 @@ def get_counters(counter_list): def get_counter(obj, instance, counter): - ''' + """ Get the value of a single counter Args: @@ -440,5 +447,5 @@ def get_counter(obj, instance, counter): counter (str): The name of the counter. Get a list of counters using the ``list_counters`` function - ''' + """ return get_counters([(obj, instance, counter)]) diff --git a/salt/utils/win_reg.py b/salt/utils/win_reg.py index ec889997c42..a291c4fd7a3 100644 --- a/salt/utils/win_reg.py +++ b/salt/utils/win_reg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Manage the Windows registry ----- @@ -25,13 +25,20 @@ Values/Entries are name/data pairs. There can be many values in a key. The pairs. :depends: - PyWin32 -''' +""" # When production windows installer is using Python 3, Python 2 code can be removed from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import python libs import sys -import logging + +# Import Salt libs +import salt.utils.platform +import salt.utils.stringutils +from salt.exceptions import CommandExecutionError +from salt.ext import six from salt.ext.six.moves import range # pylint: disable=W0622,import-error # Import third party libs @@ -39,111 +46,108 @@ try: import win32gui import win32api import win32con + HAS_WINDOWS_MODULES = True except ImportError: HAS_WINDOWS_MODULES = False -# Import Salt libs -import salt.utils.platform -import salt.utils.stringutils -from salt.exceptions import CommandExecutionError -from salt.ext import six PY2 = sys.version_info[0] == 2 log = logging.getLogger(__name__) # Define the module's virtual name -__virtualname__ = 'reg' +__virtualname__ = "reg" def __virtual__(): - ''' + """ Only works on Windows systems with the PyWin32 - ''' + """ if not salt.utils.platform.is_windows(): - return (False, 'reg execution module failed to load: ' - 'The module will only run on Windows systems') + return ( + False, + "reg execution module failed to load: " + "The module will only run on Windows systems", + ) if not HAS_WINDOWS_MODULES: - return (False, 'reg execution module failed to load: ' - 'One of the following libraries did not load: ' - 'win32gui, win32con, win32api') + return ( + False, + "reg execution module failed to load: " + "One of the following libraries did not load: " + "win32gui, win32con, win32api", + ) return __virtualname__ def _to_mbcs(vdata): - ''' + """ Converts unicode to to current users character encoding. Use this for values returned by reg functions - ''' - return salt.utils.stringutils.to_unicode(vdata, 'mbcs') + """ + return salt.utils.stringutils.to_unicode(vdata, "mbcs") def _to_unicode(vdata): - ''' + """ Converts from current users character encoding to unicode. Use this for parameters being pass to reg functions - ''' + """ # None does not convert to Unicode if vdata is None: return None if isinstance(vdata, int): vdata = str(vdata) - return salt.utils.stringutils.to_unicode(vdata, 'utf-8') + return salt.utils.stringutils.to_unicode(vdata, "utf-8") class Registry(object): # pylint: disable=R0903 - ''' + """ This was put in a class to delay usage until this module is actually used This class contains all the lookup dicts for working with the registry - ''' + """ + def __init__(self): self.hkeys = { - 'HKEY_CURRENT_CONFIG': win32con.HKEY_CURRENT_CONFIG, - 'HKEY_CLASSES_ROOT': win32con.HKEY_CLASSES_ROOT, - 'HKEY_CURRENT_USER': win32con.HKEY_CURRENT_USER, - 'HKEY_LOCAL_MACHINE': win32con.HKEY_LOCAL_MACHINE, - 'HKEY_USERS': win32con.HKEY_USERS, - 'HKCC': win32con.HKEY_CURRENT_CONFIG, - 'HKCR': win32con.HKEY_CLASSES_ROOT, - 'HKCU': win32con.HKEY_CURRENT_USER, - 'HKLM': win32con.HKEY_LOCAL_MACHINE, - 'HKU': win32con.HKEY_USERS, + "HKEY_CURRENT_CONFIG": win32con.HKEY_CURRENT_CONFIG, + "HKEY_CLASSES_ROOT": win32con.HKEY_CLASSES_ROOT, + "HKEY_CURRENT_USER": win32con.HKEY_CURRENT_USER, + "HKEY_LOCAL_MACHINE": win32con.HKEY_LOCAL_MACHINE, + "HKEY_USERS": win32con.HKEY_USERS, + "HKCC": win32con.HKEY_CURRENT_CONFIG, + "HKCR": win32con.HKEY_CLASSES_ROOT, + "HKCU": win32con.HKEY_CURRENT_USER, + "HKLM": win32con.HKEY_LOCAL_MACHINE, + "HKU": win32con.HKEY_USERS, } self.vtype = { - 'REG_BINARY': win32con.REG_BINARY, - 'REG_DWORD': win32con.REG_DWORD, - 'REG_EXPAND_SZ': win32con.REG_EXPAND_SZ, - 'REG_MULTI_SZ': win32con.REG_MULTI_SZ, - 'REG_SZ': win32con.REG_SZ, - 'REG_QWORD': win32con.REG_QWORD - } - self.opttype = { - 'REG_OPTION_NON_VOLATILE': 0, - 'REG_OPTION_VOLATILE': 1 + "REG_BINARY": win32con.REG_BINARY, + "REG_DWORD": win32con.REG_DWORD, + "REG_EXPAND_SZ": win32con.REG_EXPAND_SZ, + "REG_MULTI_SZ": win32con.REG_MULTI_SZ, + "REG_SZ": win32con.REG_SZ, + "REG_QWORD": win32con.REG_QWORD, } + self.opttype = {"REG_OPTION_NON_VOLATILE": 0, "REG_OPTION_VOLATILE": 1} # Return Unicode due to from __future__ import unicode_literals self.vtype_reverse = { - win32con.REG_BINARY: 'REG_BINARY', - win32con.REG_DWORD: 'REG_DWORD', - win32con.REG_EXPAND_SZ: 'REG_EXPAND_SZ', - win32con.REG_MULTI_SZ: 'REG_MULTI_SZ', - win32con.REG_SZ: 'REG_SZ', - win32con.REG_QWORD: 'REG_QWORD' - } - self.opttype_reverse = { - 0: 'REG_OPTION_NON_VOLATILE', - 1: 'REG_OPTION_VOLATILE' + win32con.REG_BINARY: "REG_BINARY", + win32con.REG_DWORD: "REG_DWORD", + win32con.REG_EXPAND_SZ: "REG_EXPAND_SZ", + win32con.REG_MULTI_SZ: "REG_MULTI_SZ", + win32con.REG_SZ: "REG_SZ", + win32con.REG_QWORD: "REG_QWORD", } + self.opttype_reverse = {0: "REG_OPTION_NON_VOLATILE", 1: "REG_OPTION_VOLATILE"} # delete_key_recursive uses this to check the subkey contains enough \ # as we do not want to remove all or most of the registry self.subkey_slash_check = { - win32con.HKEY_CURRENT_USER: 0, - win32con.HKEY_LOCAL_MACHINE: 1, - win32con.HKEY_USERS: 1, + win32con.HKEY_CURRENT_USER: 0, + win32con.HKEY_LOCAL_MACHINE: 1, + win32con.HKEY_USERS: 1, win32con.HKEY_CURRENT_CONFIG: 1, - win32con.HKEY_CLASSES_ROOT: 1 + win32con.HKEY_CLASSES_ROOT: 1, } self.registry_32 = { @@ -155,13 +159,13 @@ class Registry(object): # pylint: disable=R0903 try: return self.hkeys[k] except KeyError: - msg = 'No hkey named \'{0}. Try one of {1}\'' - hkeys = ', '.join(self.hkeys) + msg = "No hkey named '{0}. Try one of {1}'" + hkeys = ", ".join(self.hkeys) raise CommandExecutionError(msg.format(k, hkeys)) def key_exists(hive, key, use_32bit_registry=False): - ''' + """ Check that the key is found in the registry. This refers to keys and not value/data pairs. To check value/data pairs, use ``value_exists`` @@ -182,7 +186,7 @@ def key_exists(hive, key, use_32bit_registry=False): import salt.utils.win_reg winreg.key_exists(hive='HKLM', key='SOFTWARE\\Microsoft') - ''' + """ local_hive = _to_unicode(hive) local_key = _to_unicode(key) @@ -190,7 +194,7 @@ def key_exists(hive, key, use_32bit_registry=False): try: hkey = registry.hkeys[local_hive] except KeyError: - raise CommandExecutionError('Invalid Hive: {0}'.format(local_hive)) + raise CommandExecutionError("Invalid Hive: {0}".format(local_hive)) access_mask = registry.registry_32[use_32bit_registry] handle = None @@ -207,7 +211,7 @@ def key_exists(hive, key, use_32bit_registry=False): def value_exists(hive, key, vname, use_32bit_registry=False): - ''' + """ Check that the value/data pair is found in the registry. .. versionadded:: 2018.3.4 @@ -233,7 +237,7 @@ def value_exists(hive, key, vname, use_32bit_registry=False): winreg.value_exists(hive='HKLM', key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', vname='CommonFilesDir') - ''' + """ local_hive = _to_unicode(hive) local_key = _to_unicode(key) local_vname = _to_unicode(vname) @@ -242,7 +246,7 @@ def value_exists(hive, key, vname, use_32bit_registry=False): try: hkey = registry.hkeys[local_hive] except KeyError: - raise CommandExecutionError('Invalid Hive: {0}'.format(local_hive)) + raise CommandExecutionError("Invalid Hive: {0}".format(local_hive)) access_mask = registry.registry_32[use_32bit_registry] try: @@ -270,7 +274,7 @@ def value_exists(hive, key, vname, use_32bit_registry=False): def broadcast_change(): - ''' + """ Refresh the windows environment. .. note:: @@ -286,16 +290,21 @@ def broadcast_change(): import salt.utils.win_reg winreg.broadcast_change() - ''' + """ # https://msdn.microsoft.com/en-us/library/windows/desktop/ms644952(v=vs.85).aspx _, res = win32gui.SendMessageTimeout( - win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 0, - win32con.SMTO_ABORTIFHUNG, 5000) + win32con.HWND_BROADCAST, + win32con.WM_SETTINGCHANGE, + 0, + 0, + win32con.SMTO_ABORTIFHUNG, + 5000, + ) return not bool(res) def list_keys(hive, key=None, use_32bit_registry=False): - ''' + """ Enumerates the subkeys in a registry key or hive. Args: @@ -326,7 +335,7 @@ def list_keys(hive, key=None, use_32bit_registry=False): import salt.utils.win_reg winreg.list_keys(hive='HKLM', key='SOFTWARE\\Microsoft') - ''' + """ local_hive = _to_unicode(hive) local_key = _to_unicode(key) @@ -335,7 +344,7 @@ def list_keys(hive, key=None, use_32bit_registry=False): try: hkey = registry.hkeys[local_hive] except KeyError: - raise CommandExecutionError('Invalid Hive: {0}'.format(local_hive)) + raise CommandExecutionError("Invalid Hive: {0}".format(local_hive)) access_mask = registry.registry_32[use_32bit_registry] subkeys = [] @@ -352,8 +361,8 @@ def list_keys(hive, key=None, use_32bit_registry=False): except win32api.error as exc: if exc.winerror == 2: - log.debug(r'Cannot find key: %s\%s', hive, key, exc_info=True) - return False, r'Cannot find key: {0}\{1}'.format(hive, key) + log.debug(r"Cannot find key: %s\%s", hive, key, exc_info=True) + return False, r"Cannot find key: {0}\{1}".format(hive, key) raise finally: @@ -364,7 +373,7 @@ def list_keys(hive, key=None, use_32bit_registry=False): def list_values(hive, key=None, use_32bit_registry=False): - ''' + """ Enumerates the values in a registry key or hive. .. note:: @@ -399,7 +408,7 @@ def list_values(hive, key=None, use_32bit_registry=False): import salt.utils.win_reg winreg.list_values(hive='HKLM', key='SYSTEM\\CurrentControlSet\\Services\\Tcpip') - ''' + """ local_hive = _to_unicode(hive) local_key = _to_unicode(key) @@ -407,7 +416,7 @@ def list_values(hive, key=None, use_32bit_registry=False): try: hkey = registry.hkeys[local_hive] except KeyError: - raise CommandExecutionError('Invalid Hive: {0}'.format(local_hive)) + raise CommandExecutionError("Invalid Hive: {0}".format(local_hive)) access_mask = registry.registry_32[use_32bit_registry] handle = None values = list() @@ -421,24 +430,26 @@ def list_values(hive, key=None, use_32bit_registry=False): if not vname: vname = "(Default)" - value = {'hive': local_hive, - 'key': local_key, - 'vname': _to_mbcs(vname), - 'vtype': registry.vtype_reverse[vtype], - 'success': True} + value = { + "hive": local_hive, + "key": local_key, + "vname": _to_mbcs(vname), + "vtype": registry.vtype_reverse[vtype], + "success": True, + } # Only convert text types to unicode if vtype == win32con.REG_MULTI_SZ: - value['vdata'] = [_to_mbcs(i) for i in vdata] + value["vdata"] = [_to_mbcs(i) for i in vdata] elif vtype in [win32con.REG_SZ, win32con.REG_EXPAND_SZ]: - value['vdata'] = _to_mbcs(vdata) + value["vdata"] = _to_mbcs(vdata) else: - value['vdata'] = vdata + value["vdata"] = vdata values.append(value) except win32api.error as exc: if exc.winerror == 2: - log.debug(r'Cannot find key: %s\%s', hive, key) - return False, r'Cannot find key: {0}\{1}'.format(hive, key) + log.debug(r"Cannot find key: %s\%s", hive, key) + return False, r"Cannot find key: {0}\{1}".format(hive, key) raise finally: @@ -448,7 +459,7 @@ def list_values(hive, key=None, use_32bit_registry=False): def read_value(hive, key, vname=None, use_32bit_registry=False): - r''' + r""" Reads a registry value entry or the default value for a key. To read the default value, don't pass ``vname`` @@ -503,7 +514,7 @@ def read_value(hive, key, vname=None, use_32bit_registry=False): import salt.utils.win_reg winreg.read_value(hive='HKLM', key='SOFTWARE\\Salt') - ''' + """ # If no name is passed, the default value of the key will be returned # The value name is Default @@ -512,20 +523,22 @@ def read_value(hive, key, vname=None, use_32bit_registry=False): local_key = _to_unicode(key) local_vname = _to_unicode(vname) - ret = {'hive': local_hive, - 'key': local_key, - 'vname': local_vname, - 'vdata': None, - 'success': True} + ret = { + "hive": local_hive, + "key": local_key, + "vname": local_vname, + "vdata": None, + "success": True, + } if not vname: - ret['vname'] = '(Default)' + ret["vname"] = "(Default)" registry = Registry() try: hkey = registry.hkeys[local_hive] except KeyError: - raise CommandExecutionError('Invalid Hive: {0}'.format(local_hive)) + raise CommandExecutionError("Invalid Hive: {0}".format(local_hive)) access_mask = registry.registry_32[use_32bit_registry] try: @@ -533,50 +546,53 @@ def read_value(hive, key, vname=None, use_32bit_registry=False): try: # RegQueryValueEx returns and accepts unicode data vdata, vtype = win32api.RegQueryValueEx(handle, local_vname) - if vdata or vdata in [0, '', []]: + if vdata or vdata in [0, "", []]: # Only convert text types to unicode - ret['vtype'] = registry.vtype_reverse[vtype] + ret["vtype"] = registry.vtype_reverse[vtype] if vtype == win32con.REG_MULTI_SZ: - ret['vdata'] = [_to_mbcs(i) for i in vdata] + ret["vdata"] = [_to_mbcs(i) for i in vdata] elif vtype in [win32con.REG_SZ, win32con.REG_EXPAND_SZ]: - ret['vdata'] = _to_mbcs(vdata) + ret["vdata"] = _to_mbcs(vdata) else: - ret['vdata'] = vdata + ret["vdata"] = vdata else: - ret['comment'] = 'Empty Value' + ret["comment"] = "Empty Value" except win32api.error as exc: if exc.winerror == 2 and vname is None: - ret['vdata'] = ('(value not set)') - ret['vtype'] = 'REG_SZ' + ret["vdata"] = "(value not set)" + ret["vtype"] = "REG_SZ" elif exc.winerror == 2: - msg = 'Cannot find {0} in {1}\\{2}' \ - ''.format(local_vname, local_hive, local_key) + msg = "Cannot find {0} in {1}\\{2}" "".format( + local_vname, local_hive, local_key + ) log.trace(exc) log.trace(msg) - ret['comment'] = msg - ret['success'] = False + ret["comment"] = msg + ret["success"] = False else: raise except win32api.error as exc: if exc.winerror == 2: - msg = 'Cannot find key: {0}\\{1}'.format(local_hive, local_key) + msg = "Cannot find key: {0}\\{1}".format(local_hive, local_key) log.trace(exc) log.trace(msg) - ret['comment'] = msg - ret['success'] = False + ret["comment"] = msg + ret["success"] = False else: raise return ret -def set_value(hive, - key, - vname=None, - vdata=None, - vtype='REG_SZ', - use_32bit_registry=False, - volatile=False): - ''' +def set_value( + hive, + key, + vname=None, + vdata=None, + vtype="REG_SZ", + use_32bit_registry=False, + volatile=False, +): + """ Sets a value in the registry. If ``vname`` is passed, it will be the value for that value name, otherwise it will be the default value for the specified key @@ -696,7 +712,7 @@ def set_value(hive, import salt.utils.win_reg winreg.set_value(hive='HKLM', key='SOFTWARE\\Salt', vname='list_data', vdata=['Salt', 'is', 'great'], vtype='REG_MULTI_SZ') - ''' + """ local_hive = _to_unicode(hive) local_key = _to_unicode(key) local_vname = _to_unicode(vname) @@ -706,23 +722,25 @@ def set_value(hive, try: hkey = registry.hkeys[local_hive] except KeyError: - raise CommandExecutionError('Invalid Hive: {0}'.format(local_hive)) + raise CommandExecutionError("Invalid Hive: {0}".format(local_hive)) vtype_value = registry.vtype[local_vtype] access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS local_vdata = cast_vdata(vdata=vdata, vtype=local_vtype) if volatile: - create_options = registry.opttype['REG_OPTION_VOLATILE'] + create_options = registry.opttype["REG_OPTION_VOLATILE"] else: - create_options = registry.opttype['REG_OPTION_NON_VOLATILE'] + create_options = registry.opttype["REG_OPTION_NON_VOLATILE"] handle = None try: - handle, result = win32api.RegCreateKeyEx(hkey, local_key, access_mask, - Options=create_options) - msg = 'Created new key: %s\\%s' if result == 1 else \ - 'Opened existing key: %s\\%s' + handle, result = win32api.RegCreateKeyEx( + hkey, local_key, access_mask, Options=create_options + ) + msg = ( + "Created new key: %s\\%s" if result == 1 else "Opened existing key: %s\\%s" + ) log.debug(msg, local_hive, local_key) try: @@ -731,16 +749,19 @@ def set_value(hive, broadcast_change() return True except TypeError as exc: - log.exception('"vdata" does not match the expected data type.\n%s', - exc) + log.exception('"vdata" does not match the expected data type.\n%s', exc) return False except (SystemError, ValueError) as exc: - log.exception('Encountered error setting registry value.\n%s', exc) + log.exception("Encountered error setting registry value.\n%s", exc) return False except win32api.error as exc: - log.exception('Error creating/opening key: %s\\%s\n%s', local_hive, - local_key, exc.winerror) + log.exception( + "Error creating/opening key: %s\\%s\n%s", + local_hive, + local_key, + exc.winerror, + ) return False finally: @@ -748,8 +769,8 @@ def set_value(hive, win32api.RegCloseKey(handle) -def cast_vdata(vdata=None, vtype='REG_SZ'): - ''' +def cast_vdata(vdata=None, vtype="REG_SZ"): + """ Cast the ``vdata` value to the appropriate data type for the registry type specified in ``vtype`` @@ -778,7 +799,7 @@ def cast_vdata(vdata=None, vtype='REG_SZ'): import salt.utils.win_reg winreg.cast_vdata(vdata='This is the string', vtype='REG_SZ') - ''' + """ # Check data type and cast to expected type # int will automatically become long on 64bit numbers # https://www.python.org/dev/peps/pep-0237/ @@ -793,21 +814,23 @@ def cast_vdata(vdata=None, vtype='REG_SZ'): elif vtype_value == win32con.REG_BINARY: if isinstance(vdata, six.text_type): # Unicode data must be encoded - return vdata.encode('utf-8') + return vdata.encode("utf-8") return vdata # Make sure REG_MULTI_SZ is a list of strings elif vtype_value == win32con.REG_MULTI_SZ: return [_to_unicode(i) for i in vdata] # Make sure REG_QWORD is a 64 bit integer elif vtype_value == win32con.REG_QWORD: - return int(vdata) if six.PY3 else long(vdata) # pylint: disable=undefined-variable,incompatible-py3-code + # pylint: disable=undefined-variable,incompatible-py3-code + return int(vdata) if six.PY3 else long(vdata) + # pylint: enable=undefined-variable,incompatible-py3-code # Everything else is int else: return int(vdata) def delete_key_recursive(hive, key, use_32bit_registry=False): - ''' + """ .. versionadded:: 2015.5.4 Delete a registry key to include all subkeys and value/data pairs. @@ -843,7 +866,7 @@ def delete_key_recursive(hive, key, use_32bit_registry=False): import salt.utils.win_reg winreg.delete_key_recursive(hive='HKLM', key='SOFTWARE\\DeleteMe') - ''' + """ local_hive = _to_unicode(hive) local_key = _to_unicode(key) @@ -853,7 +876,7 @@ def delete_key_recursive(hive, key, use_32bit_registry=False): try: hkey = registry.hkeys[local_hive] except KeyError: - raise CommandExecutionError('Invalid Hive: {0}'.format(local_hive)) + raise CommandExecutionError("Invalid Hive: {0}".format(local_hive)) key_path = local_key access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS @@ -861,16 +884,15 @@ def delete_key_recursive(hive, key, use_32bit_registry=False): log.debug('"%s\\%s" not found', hive, key) return False - if (len(key) > 1) and (key.count('\\', 1) < registry.subkey_slash_check[hkey]): - log.error( - '"%s\\%s" is too close to root, not safe to remove', hive, key) + if (len(key) > 1) and (key.count("\\", 1) < registry.subkey_slash_check[hkey]): + log.error('"%s\\%s" is too close to root, not safe to remove', hive, key) return False # Functions for traversing the registry tree def _subkeys(_key): - ''' + """ Enumerate keys - ''' + """ i = 0 while True: try: @@ -881,12 +903,12 @@ def delete_key_recursive(hive, key, use_32bit_registry=False): break def _traverse_registry_tree(_hkey, _keypath, _ret, _access_mask): - ''' + """ Traverse the registry tree i.e. dive into the tree - ''' + """ _key = win32api.RegOpenKeyEx(_hkey, _keypath, 0, _access_mask) for subkeyname in _subkeys(_key): - subkeypath = '{0}\\{1}'.format(_keypath, subkeyname) + subkeypath = "{0}\\{1}".format(_keypath, subkeyname) _ret = _traverse_registry_tree(_hkey, subkeypath, _ret, access_mask) _ret.append(subkeypath) return _ret @@ -897,8 +919,7 @@ def delete_key_recursive(hive, key, use_32bit_registry=False): # Add the top level key last, all subkeys must be deleted first key_list.append(key_path) - ret = {'Deleted': [], - 'Failed': []} + ret = {"Deleted": [], "Failed": []} # Delete all sub_keys for sub_key_path in key_list: @@ -906,14 +927,16 @@ def delete_key_recursive(hive, key, use_32bit_registry=False): try: key_handle = win32api.RegOpenKeyEx(hkey, sub_key_path, 0, access_mask) try: - win32api.RegDeleteKey(key_handle, '') - ret['Deleted'].append(r'{0}\{1}'.format(hive, sub_key_path)) + win32api.RegDeleteKey(key_handle, "") + ret["Deleted"].append(r"{0}\{1}".format(hive, sub_key_path)) except WindowsError as exc: # pylint: disable=undefined-variable log.error(exc, exc_info=True) - ret['Failed'].append(r'{0}\{1} {2}'.format(hive, sub_key_path, exc)) + ret["Failed"].append(r"{0}\{1} {2}".format(hive, sub_key_path, exc)) except win32api.error as exc: log.error(exc, exc_info=True) - ret['Failed'].append(r'{0}\{1} {2}'.format(hive, sub_key_path, exc.strerror)) + ret["Failed"].append( + r"{0}\{1} {2}".format(hive, sub_key_path, exc.strerror) + ) finally: if key_handle: win32api.CloseHandle(key_handle) @@ -924,7 +947,7 @@ def delete_key_recursive(hive, key, use_32bit_registry=False): def delete_value(hive, key, vname=None, use_32bit_registry=False): - ''' + """ Delete a registry value entry or the default value for a key. Args: @@ -958,7 +981,7 @@ def delete_value(hive, key, vname=None, use_32bit_registry=False): import salt.utils.win_reg winreg.delete_value(hive='HKLM', key='SOFTWARE\\SaltTest', vname='version') - ''' + """ local_hive = _to_unicode(hive) local_key = _to_unicode(key) local_vname = _to_unicode(vname) @@ -967,7 +990,7 @@ def delete_value(hive, key, vname=None, use_32bit_registry=False): try: hkey = registry.hkeys[local_hive] except KeyError: - raise CommandExecutionError('Invalid Hive: {0}'.format(local_hive)) + raise CommandExecutionError("Invalid Hive: {0}".format(local_hive)) access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS handle = None diff --git a/salt/utils/win_runas.py b/salt/utils/win_runas.py index 62bccdac457..8cc70c80c95 100644 --- a/salt/utils/win_runas.py +++ b/salt/utils/win_runas.py @@ -1,17 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" Run processes as a different user in Windows -''' +""" from __future__ import absolute_import, unicode_literals # Import Python Libraries import ctypes -import os import logging +import os + +# Import Salt Libs +from salt.exceptions import CommandExecutionError # Import Third Party Libs try: import psutil + HAS_PSUTIL = True except ImportError: HAS_PSUTIL = False @@ -27,12 +31,11 @@ try: import msvcrt import salt.platform.win import pywintypes + HAS_WIN32 = True except ImportError: HAS_WIN32 = False -# Import Salt Libs -from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) @@ -40,46 +43,43 @@ log = logging.getLogger(__name__) # Although utils are often directly imported, it is also possible to use the # loader. def __virtual__(): - ''' + """ Only load if Win32 Libraries are installed - ''' + """ if not HAS_WIN32 or not HAS_PSUTIL: - return False, 'This utility requires pywin32 and psutil' + return False, "This utility requires pywin32 and psutil" - return 'win_runas' + return "win_runas" def split_username(username): # TODO: Is there a windows api for this? - domain = '.' - if '@' in username: - username, domain = username.split('@') - if '\\' in username: - domain, username = username.split('\\') + domain = "." + if "@" in username: + username, domain = username.split("@") + if "\\" in username: + domain, username = username.split("\\") return username, domain def runas(cmdLine, username, password=None, cwd=None): - ''' + """ Run a command as another user. If the process is running as an admin or system account this method does not require a password. Other non privileged accounts need to provide a password for the user to runas. Commands are run in with the highest level privileges possible for the account provided. - ''' + """ # Validate the domain and sid exist for the username username, domain = split_username(username) try: _, domain, _ = win32security.LookupAccountName(domain, username) except pywintypes.error as exc: - message = win32api.FormatMessage(exc.winerror).rstrip('\n') + message = win32api.FormatMessage(exc.winerror).rstrip("\n") raise CommandExecutionError(message) # Elevate the token from the current process - access = ( - win32security.TOKEN_QUERY | - win32security.TOKEN_ADJUST_PRIVILEGES - ) + access = win32security.TOKEN_QUERY | win32security.TOKEN_ADJUST_PRIVILEGES th = win32security.OpenProcessToken(win32api.GetCurrentProcess(), access) salt.platform.win.elevate_token(th) @@ -88,9 +88,7 @@ def runas(cmdLine, username, password=None, cwd=None): # accounts have this permission by default. try: impersonation_token = salt.platform.win.impersonate_sid( - salt.platform.win.SYSTEM_SID, - session_id=0, - privs=['SeTcbPrivilege'], + salt.platform.win.SYSTEM_SID, session_id=0, privs=["SeTcbPrivilege"], ) except WindowsError: # pylint: disable=undefined-variable log.debug("Unable to impersonate SYSTEM user") @@ -103,13 +101,13 @@ def runas(cmdLine, username, password=None, cwd=None): log.debug("No impersonation token, using unprivileged runas") return runas_unpriv(cmdLine, username, password, cwd) - if domain == 'NT AUTHORITY': + if domain == "NT AUTHORITY": # Logon as a system level account, SYSTEM, LOCAL SERVICE, or NETWORK # SERVICE. user_token = win32security.LogonUser( username, domain, - '', + "", win32con.LOGON32_LOGON_SERVICE, win32con.LOGON32_PROVIDER_DEFAULT, ) @@ -132,8 +130,7 @@ def runas(cmdLine, username, password=None, cwd=None): ) if elevation_type > 1: user_token = win32security.GetTokenInformation( - user_token, - win32security.TokenLinkedToken + user_token, win32security.TokenLinkedToken ) # Elevate the user token @@ -157,9 +154,9 @@ def runas(cmdLine, username, password=None, cwd=None): # Run the process without showing a window. creationflags = ( - win32process.CREATE_NO_WINDOW | - win32process.CREATE_NEW_CONSOLE | - win32process.CREATE_SUSPENDED + win32process.CREATE_NO_WINDOW + | win32process.CREATE_NEW_CONSOLE + | win32process.CREATE_SUSPENDED ) startup_info = salt.platform.win.STARTUPINFO( @@ -196,26 +193,29 @@ def runas(cmdLine, username, password=None, cwd=None): salt.platform.win.kernel32.CloseHandle(stdout_write.handle) salt.platform.win.kernel32.CloseHandle(stderr_write.handle) - ret = {'pid': dwProcessId} + ret = {"pid": dwProcessId} # Resume the process psutil.Process(dwProcessId).resume() # Wait for the process to exit and get its return code. - if win32event.WaitForSingleObject(hProcess, win32event.INFINITE) == win32con.WAIT_OBJECT_0: + if ( + win32event.WaitForSingleObject(hProcess, win32event.INFINITE) + == win32con.WAIT_OBJECT_0 + ): exitcode = win32process.GetExitCodeProcess(hProcess) - ret['retcode'] = exitcode + ret["retcode"] = exitcode # Read standard out fd_out = msvcrt.open_osfhandle(stdout_read.handle, os.O_RDONLY | os.O_TEXT) - with os.fdopen(fd_out, 'r') as f_out: + with os.fdopen(fd_out, "r") as f_out: stdout = f_out.read() - ret['stdout'] = stdout + ret["stdout"] = stdout # Read standard error fd_err = msvcrt.open_osfhandle(stderr_read.handle, os.O_RDONLY | os.O_TEXT) - with os.fdopen(fd_err, 'r') as f_err: + with os.fdopen(fd_err, "r") as f_err: stderr = f_err.read() - ret['stderr'] = stderr + ret["stderr"] = stderr finally: if hProcess is not None: salt.platform.win.kernel32.CloseHandle(hProcess) @@ -229,15 +229,15 @@ def runas(cmdLine, username, password=None, cwd=None): def runas_unpriv(cmd, username, password, cwd=None): - ''' + """ Runas that works for non-priviledged users - ''' + """ # Validate the domain and sid exist for the username username, domain = split_username(username) try: _, domain, _ = win32security.LookupAccountName(domain, username) except pywintypes.error as exc: - message = win32api.FormatMessage(exc.winerror).rstrip('\n') + message = win32api.FormatMessage(exc.winerror).rstrip("\n") raise CommandExecutionError(message) # Create a pipe to set as stdout in the child. The write handle needs to be @@ -250,9 +250,7 @@ def runas_unpriv(cmd, username, password, cwd=None): ) # Create inheritable copy of the stdin - stdin = salt.platform.win.kernel32.GetStdHandle( - salt.platform.win.STD_INPUT_HANDLE, - ) + stdin = salt.platform.win.kernel32.GetStdHandle(salt.platform.win.STD_INPUT_HANDLE,) dupin = salt.platform.win.DuplicateHandle(srchandle=stdin, inherit=True) # Get startup info structure @@ -266,13 +264,14 @@ def runas_unpriv(cmd, username, password, cwd=None): try: # Run command and return process info structure process_info = salt.platform.win.CreateProcessWithLogonW( - username=username, - domain=domain, - password=password, - logonflags=salt.platform.win.LOGON_WITH_PROFILE, - commandline=cmd, - startupinfo=startup_info, - currentdirectory=cwd) + username=username, + domain=domain, + password=password, + logonflags=salt.platform.win.LOGON_WITH_PROFILE, + commandline=cmd, + startupinfo=startup_info, + currentdirectory=cwd, + ) salt.platform.win.kernel32.CloseHandle(process_info.hThread) finally: salt.platform.win.kernel32.CloseHandle(dupin) @@ -280,25 +279,30 @@ def runas_unpriv(cmd, username, password, cwd=None): salt.platform.win.kernel32.CloseHandle(errwrite) # Initialize ret and set first element - ret = {'pid': process_info.dwProcessId} + ret = {"pid": process_info.dwProcessId} # Get Standard Out fd_out = msvcrt.open_osfhandle(c2pread, os.O_RDONLY | os.O_TEXT) - with os.fdopen(fd_out, 'r') as f_out: - ret['stdout'] = f_out.read() + with os.fdopen(fd_out, "r") as f_out: + ret["stdout"] = f_out.read() # Get Standard Error fd_err = msvcrt.open_osfhandle(errread, os.O_RDONLY | os.O_TEXT) - with os.fdopen(fd_err, 'r') as f_err: - ret['stderr'] = f_err.read() + with os.fdopen(fd_err, "r") as f_err: + ret["stderr"] = f_err.read() # Get Return Code - if salt.platform.win.kernel32.WaitForSingleObject(process_info.hProcess, win32event.INFINITE) == \ - win32con.WAIT_OBJECT_0: + if ( + salt.platform.win.kernel32.WaitForSingleObject( + process_info.hProcess, win32event.INFINITE + ) + == win32con.WAIT_OBJECT_0 + ): exitcode = salt.platform.win.wintypes.DWORD() - salt.platform.win.kernel32.GetExitCodeProcess(process_info.hProcess, - ctypes.byref(exitcode)) - ret['retcode'] = exitcode.value + salt.platform.win.kernel32.GetExitCodeProcess( + process_info.hProcess, ctypes.byref(exitcode) + ) + ret["retcode"] = exitcode.value # Close handle to process salt.platform.win.kernel32.CloseHandle(process_info.hProcess) diff --git a/salt/utils/win_update.py b/salt/utils/win_update.py index 79af60741f5..7d3ee4b98eb 100644 --- a/salt/utils/win_update.py +++ b/salt/utils/win_update.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Classes for working with Windows Update Agent -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import subprocess @@ -11,33 +12,34 @@ import subprocess import salt.utils.args import salt.utils.data import salt.utils.winapi +from salt.exceptions import CommandExecutionError from salt.ext import six from salt.ext.six.moves import range -from salt.exceptions import CommandExecutionError # Import 3rd-party libs try: import win32com.client import pywintypes + HAS_PYWIN32 = True except ImportError: HAS_PYWIN32 = False log = logging.getLogger(__name__) -__virtualname__ = 'win_update' +__virtualname__ = "win_update" def __virtual__(): if not salt.utils.platform.is_windows(): - return False, 'win_update: Not available on Windows' + return False, "win_update: Not available on Windows" if not HAS_PYWIN32: - return False, 'win_update: Missing pywin32' + return False, "win_update: Missing pywin32" return __virtualname__ class Updates(object): - ''' + """ Wrapper around the 'Microsoft.Update.UpdateColl' instance Adds the list and summary functions. For use by the WindowUpdateAgent class. @@ -64,25 +66,26 @@ class Updates(object): # Return a summary of the contents of the updates collection updates.summary() - ''' + """ - update_types = {1: 'Software', - 2: 'Driver'} + update_types = {1: "Software", 2: "Driver"} - reboot_behavior = {0: 'Never Requires Reboot', - 1: 'Always Requires Reboot', - 2: 'Can Require Reboot'} + reboot_behavior = { + 0: "Never Requires Reboot", + 1: "Always Requires Reboot", + 2: "Can Require Reboot", + } def __init__(self): - ''' + """ Initialize the updates collection. Can be accessed via ``Updates.updates`` - ''' + """ with salt.utils.winapi.Com(): - self.updates = win32com.client.Dispatch('Microsoft.Update.UpdateColl') + self.updates = win32com.client.Dispatch("Microsoft.Update.UpdateColl") def count(self): - ''' + """ Return how many records are in the Microsoft Update Collection Returns: @@ -95,11 +98,11 @@ class Updates(object): import salt.utils.win_update updates = salt.utils.win_update.Updates() updates.count() - ''' + """ return self.updates.Count def list(self): - ''' + """ Create a dictionary with the details for the updates in the collection. Returns: @@ -133,41 +136,40 @@ class Updates(object): import salt.utils.win_update updates = salt.utils.win_update.Updates() updates.list() - ''' + """ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa386099(v=vs.85).aspx if self.count() == 0: - return 'Nothing to return' + return "Nothing to return" - log.debug('Building a detailed report of the results.') + log.debug("Building a detailed report of the results.") # Build a dictionary containing details for each update results = {} for update in self.updates: results[update.Identity.UpdateID] = { - 'guid': update.Identity.UpdateID, - 'Title': six.text_type(update.Title), - 'Type': self.update_types[update.Type], - 'Description': update.Description, - 'Downloaded': bool(update.IsDownloaded), - 'Installed': bool(update.IsInstalled), - 'Mandatory': bool(update.IsMandatory), - 'EULAAccepted': bool(update.EulaAccepted), - 'NeedsReboot': bool(update.RebootRequired), - 'Severity': six.text_type(update.MsrcSeverity), - 'UserInput': - bool(update.InstallationBehavior.CanRequestUserInput), - 'RebootBehavior': - self.reboot_behavior[ - update.InstallationBehavior.RebootBehavior], - 'KBs': ['KB' + item for item in update.KBArticleIDs], - 'Categories': [item.Name for item in update.Categories] + "guid": update.Identity.UpdateID, + "Title": six.text_type(update.Title), + "Type": self.update_types[update.Type], + "Description": update.Description, + "Downloaded": bool(update.IsDownloaded), + "Installed": bool(update.IsInstalled), + "Mandatory": bool(update.IsMandatory), + "EULAAccepted": bool(update.EulaAccepted), + "NeedsReboot": bool(update.RebootRequired), + "Severity": six.text_type(update.MsrcSeverity), + "UserInput": bool(update.InstallationBehavior.CanRequestUserInput), + "RebootBehavior": self.reboot_behavior[ + update.InstallationBehavior.RebootBehavior + ], + "KBs": ["KB" + item for item in update.KBArticleIDs], + "Categories": [item.Name for item in update.Categories], } return results def summary(self): - ''' + """ Create a dictionary with a summary of the updates in the collection. Returns: @@ -191,112 +193,119 @@ class Updates(object): import salt.utils.win_update updates = salt.utils.win_update.Updates() updates.summary() - ''' + """ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa386099(v=vs.85).aspx if self.count() == 0: - return 'Nothing to return' + return "Nothing to return" # Build a dictionary containing a summary of updates available - results = {'Total': 0, - 'Available': 0, - 'Downloaded': 0, - 'Installed': 0, - 'Categories': {}, - 'Severity': {}} + results = { + "Total": 0, + "Available": 0, + "Downloaded": 0, + "Installed": 0, + "Categories": {}, + "Severity": {}, + } for update in self.updates: # Count the total number of updates available - results['Total'] += 1 + results["Total"] += 1 # Updates available for download - if not salt.utils.data.is_true(update.IsDownloaded) \ - and not salt.utils.data.is_true(update.IsInstalled): - results['Available'] += 1 + if not salt.utils.data.is_true( + update.IsDownloaded + ) and not salt.utils.data.is_true(update.IsInstalled): + results["Available"] += 1 # Updates downloaded awaiting install - if salt.utils.data.is_true(update.IsDownloaded) \ - and not salt.utils.data.is_true(update.IsInstalled): - results['Downloaded'] += 1 + if salt.utils.data.is_true( + update.IsDownloaded + ) and not salt.utils.data.is_true(update.IsInstalled): + results["Downloaded"] += 1 # Updates installed if salt.utils.data.is_true(update.IsInstalled): - results['Installed'] += 1 + results["Installed"] += 1 # Add Categories and increment total for each one # The sum will be more than the total because each update can have # multiple categories for category in update.Categories: - if category.Name in results['Categories']: - results['Categories'][category.Name] += 1 + if category.Name in results["Categories"]: + results["Categories"][category.Name] += 1 else: - results['Categories'][category.Name] = 1 + results["Categories"][category.Name] = 1 # Add Severity Summary if update.MsrcSeverity: - if update.MsrcSeverity in results['Severity']: - results['Severity'][update.MsrcSeverity] += 1 + if update.MsrcSeverity in results["Severity"]: + results["Severity"][update.MsrcSeverity] += 1 else: - results['Severity'][update.MsrcSeverity] = 1 + results["Severity"][update.MsrcSeverity] = 1 return results class WindowsUpdateAgent(object): - ''' + """ Class for working with the Windows update agent - ''' + """ + # Error codes found at the following site: # https://msdn.microsoft.com/en-us/library/windows/desktop/hh968413(v=vs.85).aspx # https://technet.microsoft.com/en-us/library/cc720442(v=ws.10).aspx - fail_codes = {-2145107924: 'WinHTTP Send/Receive failed: 0x8024402C', - -2145124300: 'Download failed: 0x80240034', - -2145124302: 'Invalid search criteria: 0x80240032', - -2145124305: 'Cancelled by policy: 0x8024002F', - -2145124307: 'Missing source: 0x8024002D', - -2145124308: 'Missing source: 0x8024002C', - -2145124312: 'Uninstall not allowed: 0x80240028', - -2145124315: 'Prevented by policy: 0x80240025', - -2145124316: 'No Updates: 0x80240024', - -2145124322: 'Service being shutdown: 0x8024001E', - -2145124325: 'Self Update in Progress: 0x8024001B', - -2145124327: 'Exclusive Install Conflict: 0x80240019', - -2145124330: 'Install not allowed: 0x80240016', - -2145124333: 'Duplicate item: 0x80240013', - -2145124341: 'Operation cancelled: 0x8024000B', - -2145124343: 'Operation in progress: 0x80240009', - -2145124284: 'Access Denied: 0x8024044', - -2145124283: 'Unsupported search scope: 0x80240045', - -2147024891: 'Access is denied: 0x80070005', - -2149843018: 'Setup in progress: 0x8024004A', - -4292599787: 'Install still pending: 0x00242015', - -4292607992: 'Already downloaded: 0x00240008', - -4292607993: 'Already uninstalled: 0x00240007', - -4292607994: 'Already installed: 0x00240006', - -4292607995: 'Reboot required: 0x00240005'} + fail_codes = { + -2145107924: "WinHTTP Send/Receive failed: 0x8024402C", + -2145124300: "Download failed: 0x80240034", + -2145124302: "Invalid search criteria: 0x80240032", + -2145124305: "Cancelled by policy: 0x8024002F", + -2145124307: "Missing source: 0x8024002D", + -2145124308: "Missing source: 0x8024002C", + -2145124312: "Uninstall not allowed: 0x80240028", + -2145124315: "Prevented by policy: 0x80240025", + -2145124316: "No Updates: 0x80240024", + -2145124322: "Service being shutdown: 0x8024001E", + -2145124325: "Self Update in Progress: 0x8024001B", + -2145124327: "Exclusive Install Conflict: 0x80240019", + -2145124330: "Install not allowed: 0x80240016", + -2145124333: "Duplicate item: 0x80240013", + -2145124341: "Operation cancelled: 0x8024000B", + -2145124343: "Operation in progress: 0x80240009", + -2145124284: "Access Denied: 0x8024044", + -2145124283: "Unsupported search scope: 0x80240045", + -2147024891: "Access is denied: 0x80070005", + -2149843018: "Setup in progress: 0x8024004A", + -4292599787: "Install still pending: 0x00242015", + -4292607992: "Already downloaded: 0x00240008", + -4292607993: "Already uninstalled: 0x00240007", + -4292607994: "Already installed: 0x00240006", + -4292607995: "Reboot required: 0x00240005", + } def __init__(self): - ''' + """ Initialize the session and load all updates into the ``_updates`` collection. This collection is used by the other class functions instead of querying Windows update (expensive). Need to look at the possibility of loading this into ``__context__`` - ''' + """ # Initialize the PyCom system with salt.utils.winapi.Com(): # Create a session with the Windows Update Agent - self._session = win32com.client.Dispatch('Microsoft.Update.Session') + self._session = win32com.client.Dispatch("Microsoft.Update.Session") # Create Collection for Updates - self._updates = win32com.client.Dispatch('Microsoft.Update.UpdateColl') + self._updates = win32com.client.Dispatch("Microsoft.Update.UpdateColl") self.refresh() def updates(self): - ''' + """ Get the contents of ``_updates`` (all updates) and puts them in an Updates class to expose the list and summary functions. @@ -315,7 +324,7 @@ class WindowsUpdateAgent(object): # To get a summary updates.summary() - ''' + """ updates = Updates() found = updates.updates @@ -325,7 +334,7 @@ class WindowsUpdateAgent(object): return updates def refresh(self): - ''' + """ Refresh the contents of the ``_updates`` collection. This gets all updates in the Windows Update system and loads them into the collection. This is the part that is slow. @@ -337,44 +346,45 @@ class WindowsUpdateAgent(object): import salt.utils.win_update wua = salt.utils.win_update.WindowsUpdateAgent() wua.refresh() - ''' + """ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa386526(v=vs.85).aspx - search_string = 'Type=\'Software\' or ' \ - 'Type=\'Driver\'' + search_string = "Type='Software' or " "Type='Driver'" # Create searcher object searcher = self._session.CreateUpdateSearcher() - self._session.ClientApplicationID = 'Salt: Load Updates' + self._session.ClientApplicationID = "Salt: Load Updates" # Load all updates into the updates collection try: results = searcher.Search(search_string) if results.Updates.Count == 0: - log.debug('No Updates found for:\n\t\t%s', search_string) - return 'No Updates found: {0}'.format(search_string) + log.debug("No Updates found for:\n\t\t%s", search_string) + return "No Updates found: {0}".format(search_string) except pywintypes.com_error as error: # Something happened, raise an error hr, msg, exc, arg = error.args # pylint: disable=W0633 try: failure_code = self.fail_codes[exc[5]] except KeyError: - failure_code = 'Unknown Failure: {0}'.format(error) + failure_code = "Unknown Failure: {0}".format(error) - log.error('Search Failed: %s\n\t\t%s', failure_code, search_string) + log.error("Search Failed: %s\n\t\t%s", failure_code, search_string) raise CommandExecutionError(failure_code) self._updates = results.Updates - def available(self, - skip_hidden=True, - skip_installed=True, - skip_mandatory=False, - skip_reboot=False, - software=True, - drivers=True, - categories=None, - severities=None): - ''' + def available( + self, + skip_hidden=True, + skip_installed=True, + skip_mandatory=False, + skip_reboot=False, + software=True, + drivers=True, + categories=None, + severities=None, + ): + """ Gets a list of all updates available on the system that match the passed criteria. @@ -441,7 +451,7 @@ class WindowsUpdateAgent(object): # Get a list of Critical updates updates = wua.available(categories=['Critical Updates']) updates.list() - ''' + """ # https://msdn.microsoft.com/en-us/library/windows/desktop/aa386099(v=vs.85).aspx updates = Updates() found = updates.updates @@ -457,8 +467,10 @@ class WindowsUpdateAgent(object): if salt.utils.data.is_true(update.IsMandatory) and skip_mandatory: continue - if salt.utils.data.is_true( - update.InstallationBehavior.RebootBehavior) and skip_reboot: + if ( + salt.utils.data.is_true(update.InstallationBehavior.RebootBehavior) + and skip_reboot + ): continue if not software and update.Type == 1: @@ -484,7 +496,7 @@ class WindowsUpdateAgent(object): return updates def search(self, search_string): - ''' + """ Search for either a single update or a specific list of updates. GUIDs are searched first, then KB numbers, and finally Titles. @@ -513,7 +525,7 @@ class WindowsUpdateAgent(object): # search for a list of updates and show their details updates = wua.search(['KB3195432', '12345678-abcd-1234-abcd-1234567890ab']) updates.list() - ''' + """ updates = Updates() found = updates.updates @@ -533,7 +545,7 @@ class WindowsUpdateAgent(object): continue # Search by KB - if find in ['KB' + item for item in update.KBArticleIDs]: + if find in ["KB" + item for item in update.KBArticleIDs]: found.Add(update) continue @@ -550,7 +562,7 @@ class WindowsUpdateAgent(object): return updates def download(self, updates): - ''' + """ Download the updates passed in the updates collection. Load the updates collection using ``search`` or ``available`` @@ -572,47 +584,44 @@ class WindowsUpdateAgent(object): # Download KB3195454 updates = wua.search('KB3195454') results = wua.download(updates) - ''' + """ # Check for empty list if updates.count() == 0: - ret = {'Success': False, - 'Updates': 'Nothing to download'} + ret = {"Success": False, "Updates": "Nothing to download"} return ret # Initialize the downloader object and list collection downloader = self._session.CreateUpdateDownloader() - self._session.ClientApplicationID = 'Salt: Download Update' + self._session.ClientApplicationID = "Salt: Download Update" with salt.utils.winapi.Com(): - download_list = win32com.client.Dispatch('Microsoft.Update.UpdateColl') + download_list = win32com.client.Dispatch("Microsoft.Update.UpdateColl") - ret = {'Updates': {}} + ret = {"Updates": {}} # Check for updates that aren't already downloaded for update in updates.updates: # Define uid to keep the lines shorter uid = update.Identity.UpdateID - ret['Updates'][uid] = {} - ret['Updates'][uid]['Title'] = update.Title - ret['Updates'][uid]['AlreadyDownloaded'] = \ - bool(update.IsDownloaded) + ret["Updates"][uid] = {} + ret["Updates"][uid]["Title"] = update.Title + ret["Updates"][uid]["AlreadyDownloaded"] = bool(update.IsDownloaded) # Accept EULA if not salt.utils.data.is_true(update.EulaAccepted): - log.debug('Accepting EULA: %s', update.Title) + log.debug("Accepting EULA: %s", update.Title) update.AcceptEula() # pylint: disable=W0104 # Update already downloaded if not salt.utils.data.is_true(update.IsDownloaded): - log.debug('To Be Downloaded: %s', uid) - log.debug('\tTitle: %s', update.Title) + log.debug("To Be Downloaded: %s", uid) + log.debug("\tTitle: %s", update.Title) download_list.Add(update) # Check the download list if download_list.Count == 0: - ret = {'Success': True, - 'Updates': 'Nothing to download'} + ret = {"Success": True, "Updates": "Nothing to download"} return ret # Send the list to the downloader @@ -620,7 +629,7 @@ class WindowsUpdateAgent(object): # Download the list try: - log.debug('Downloading Updates') + log.debug("Downloading Updates") result = downloader.Download() except pywintypes.com_error as error: # Something happened, raise an error @@ -628,41 +637,44 @@ class WindowsUpdateAgent(object): try: failure_code = self.fail_codes[exc[5]] except KeyError: - failure_code = 'Unknown Failure: {0}'.format(error) + failure_code = "Unknown Failure: {0}".format(error) - log.error('Download Failed: %s', failure_code) + log.error("Download Failed: %s", failure_code) raise CommandExecutionError(failure_code) # Lookup dictionary - result_code = {0: 'Download Not Started', - 1: 'Download In Progress', - 2: 'Download Succeeded', - 3: 'Download Succeeded With Errors', - 4: 'Download Failed', - 5: 'Download Aborted'} + result_code = { + 0: "Download Not Started", + 1: "Download In Progress", + 2: "Download Succeeded", + 3: "Download Succeeded With Errors", + 4: "Download Failed", + 5: "Download Aborted", + } - log.debug('Download Complete') + log.debug("Download Complete") log.debug(result_code[result.ResultCode]) - ret['Message'] = result_code[result.ResultCode] + ret["Message"] = result_code[result.ResultCode] # Was the download successful? if result.ResultCode in [2, 3]: - log.debug('Downloaded Successfully') - ret['Success'] = True + log.debug("Downloaded Successfully") + ret["Success"] = True else: - log.debug('Download Failed') - ret['Success'] = False + log.debug("Download Failed") + ret["Success"] = False # Report results for each update for i in range(download_list.Count): uid = download_list.Item(i).Identity.UpdateID - ret['Updates'][uid]['Result'] = \ - result_code[result.GetUpdateResult(i).ResultCode] + ret["Updates"][uid]["Result"] = result_code[ + result.GetUpdateResult(i).ResultCode + ] return ret def install(self, updates): - ''' + """ Install the updates passed in the updates collection. Load the updates collection using the ``search`` or ``available`` functions. If the updates need to be downloaded, use the ``download`` function. @@ -686,39 +698,37 @@ class WindowsUpdateAgent(object): updates = wua.search('KB3195454') results = wua.download(updates) results = wua.install(updates) - ''' + """ # Check for empty list if updates.count() == 0: - ret = {'Success': False, - 'Updates': 'Nothing to install'} + ret = {"Success": False, "Updates": "Nothing to install"} return ret installer = self._session.CreateUpdateInstaller() - self._session.ClientApplicationID = 'Salt: Install Update' + self._session.ClientApplicationID = "Salt: Install Update" with salt.utils.winapi.Com(): - install_list = win32com.client.Dispatch('Microsoft.Update.UpdateColl') + install_list = win32com.client.Dispatch("Microsoft.Update.UpdateColl") - ret = {'Updates': {}} + ret = {"Updates": {}} # Check for updates that aren't already installed for update in updates.updates: # Define uid to keep the lines shorter uid = update.Identity.UpdateID - ret['Updates'][uid] = {} - ret['Updates'][uid]['Title'] = update.Title - ret['Updates'][uid]['AlreadyInstalled'] = bool(update.IsInstalled) + ret["Updates"][uid] = {} + ret["Updates"][uid]["Title"] = update.Title + ret["Updates"][uid]["AlreadyInstalled"] = bool(update.IsInstalled) # Make sure the update has actually been installed if not salt.utils.data.is_true(update.IsInstalled): - log.debug('To Be Installed: %s', uid) - log.debug('\tTitle: %s', update.Title) + log.debug("To Be Installed: %s", uid) + log.debug("\tTitle: %s", update.Title) install_list.Add(update) # Check the install list if install_list.Count == 0: - ret = {'Success': True, - 'Updates': 'Nothing to install'} + ret = {"Success": True, "Updates": "Nothing to install"} return ret # Send the list to the installer @@ -726,7 +736,7 @@ class WindowsUpdateAgent(object): # Install the list try: - log.debug('Installing Updates') + log.debug("Installing Updates") result = installer.Install() except pywintypes.com_error as error: @@ -735,45 +745,47 @@ class WindowsUpdateAgent(object): try: failure_code = self.fail_codes[exc[5]] except KeyError: - failure_code = 'Unknown Failure: {0}'.format(error) + failure_code = "Unknown Failure: {0}".format(error) - log.error('Install Failed: %s', failure_code) + log.error("Install Failed: %s", failure_code) raise CommandExecutionError(failure_code) # Lookup dictionary - result_code = {0: 'Installation Not Started', - 1: 'Installation In Progress', - 2: 'Installation Succeeded', - 3: 'Installation Succeeded With Errors', - 4: 'Installation Failed', - 5: 'Installation Aborted'} + result_code = { + 0: "Installation Not Started", + 1: "Installation In Progress", + 2: "Installation Succeeded", + 3: "Installation Succeeded With Errors", + 4: "Installation Failed", + 5: "Installation Aborted", + } - log.debug('Install Complete') + log.debug("Install Complete") log.debug(result_code[result.ResultCode]) - ret['Message'] = result_code[result.ResultCode] + ret["Message"] = result_code[result.ResultCode] if result.ResultCode in [2, 3]: - ret['Success'] = True - ret['NeedsReboot'] = result.RebootRequired - log.debug('NeedsReboot: %s', result.RebootRequired) + ret["Success"] = True + ret["NeedsReboot"] = result.RebootRequired + log.debug("NeedsReboot: %s", result.RebootRequired) else: - log.debug('Install Failed') - ret['Success'] = False + log.debug("Install Failed") + ret["Success"] = False - reboot = {0: 'Never Reboot', - 1: 'Always Reboot', - 2: 'Poss Reboot'} + reboot = {0: "Never Reboot", 1: "Always Reboot", 2: "Poss Reboot"} for i in range(install_list.Count): uid = install_list.Item(i).Identity.UpdateID - ret['Updates'][uid]['Result'] = \ - result_code[result.GetUpdateResult(i).ResultCode] - ret['Updates'][uid]['RebootBehavior'] = \ - reboot[install_list.Item(i).InstallationBehavior.RebootBehavior] + ret["Updates"][uid]["Result"] = result_code[ + result.GetUpdateResult(i).ResultCode + ] + ret["Updates"][uid]["RebootBehavior"] = reboot[ + install_list.Item(i).InstallationBehavior.RebootBehavior + ] return ret def uninstall(self, updates): - ''' + """ Uninstall the updates passed in the updates collection. Load the updates collection using the ``search`` or ``available`` functions. @@ -801,7 +813,7 @@ class WindowsUpdateAgent(object): # uninstall KB3195454 updates = wua.search('KB3195454') results = wua.uninstall(updates) - ''' + """ # This doesn't work with the WUA API since Windows 10. It always returns # "0x80240028 # Uninstall not allowed". The full message is: "The update # could not be uninstalled because the request did not originate from a @@ -809,37 +821,34 @@ class WindowsUpdateAgent(object): # Check for empty list if updates.count() == 0: - ret = {'Success': False, - 'Updates': 'Nothing to uninstall'} + ret = {"Success": False, "Updates": "Nothing to uninstall"} return ret installer = self._session.CreateUpdateInstaller() - self._session.ClientApplicationID = 'Salt: Install Update' + self._session.ClientApplicationID = "Salt: Install Update" with salt.utils.winapi.Com(): - uninstall_list = win32com.client.Dispatch('Microsoft.Update.UpdateColl') + uninstall_list = win32com.client.Dispatch("Microsoft.Update.UpdateColl") - ret = {'Updates': {}} + ret = {"Updates": {}} # Check for updates that aren't already installed for update in updates.updates: # Define uid to keep the lines shorter uid = update.Identity.UpdateID - ret['Updates'][uid] = {} - ret['Updates'][uid]['Title'] = update.Title - ret['Updates'][uid]['AlreadyUninstalled'] = \ - not bool(update.IsInstalled) + ret["Updates"][uid] = {} + ret["Updates"][uid]["Title"] = update.Title + ret["Updates"][uid]["AlreadyUninstalled"] = not bool(update.IsInstalled) # Make sure the update has actually been Uninstalled if salt.utils.data.is_true(update.IsInstalled): - log.debug('To Be Uninstalled: %s', uid) - log.debug('\tTitle: %s', update.Title) + log.debug("To Be Uninstalled: %s", uid) + log.debug("\tTitle: %s", update.Title) uninstall_list.Add(update) # Check the install list if uninstall_list.Count == 0: - ret = {'Success': False, - 'Updates': 'Nothing to uninstall'} + ret = {"Success": False, "Updates": "Nothing to uninstall"} return ret # Send the list to the installer @@ -847,7 +856,7 @@ class WindowsUpdateAgent(object): # Uninstall the list try: - log.debug('Uninstalling Updates') + log.debug("Uninstalling Updates") result = installer.Uninstall() except pywintypes.com_error as error: @@ -856,11 +865,11 @@ class WindowsUpdateAgent(object): try: failure_code = self.fail_codes[exc[5]] except KeyError: - failure_code = 'Unknown Failure: {0}'.format(error) + failure_code = "Unknown Failure: {0}".format(error) # If "Uninstall Not Allowed" error, try using DISM if exc[5] == -2145124312: - log.debug('Uninstall Failed with WUA, attempting with DISM') + log.debug("Uninstall Failed with WUA, attempting with DISM") try: # Go through each update... @@ -870,49 +879,50 @@ class WindowsUpdateAgent(object): for kb in item.KBArticleIDs: # Get the list of packages - cmd = ['dism', '/Online', '/Get-Packages'] + cmd = ["dism", "/Online", "/Get-Packages"] pkg_list = self._run(cmd)[0].splitlines() # Find the KB in the pkg_list for item in pkg_list: # Uninstall if found - if 'kb' + kb in item.lower(): - pkg = item.split(' : ')[1] + if "kb" + kb in item.lower(): + pkg = item.split(" : ")[1] - ret['DismPackage'] = pkg + ret["DismPackage"] = pkg - cmd = ['dism', - '/Online', - '/Remove-Package', - '/PackageName:{0}'.format(pkg), - '/Quiet', - '/NoRestart'] + cmd = [ + "dism", + "/Online", + "/Remove-Package", + "/PackageName:{0}".format(pkg), + "/Quiet", + "/NoRestart", + ] self._run(cmd) except CommandExecutionError as exc: - log.debug('Uninstall using DISM failed') - log.debug('Command: %s', ' '.join(cmd)) - log.debug('Error: %s', exc) + log.debug("Uninstall using DISM failed") + log.debug("Command: %s", " ".join(cmd)) + log.debug("Error: %s", exc) raise CommandExecutionError( - 'Uninstall using DISM failed: {0}'.format(exc)) + "Uninstall using DISM failed: {0}".format(exc) + ) # DISM Uninstall Completed Successfully - log.debug('Uninstall Completed using DISM') + log.debug("Uninstall Completed using DISM") # Populate the return dictionary - ret['Success'] = True - ret['Message'] = 'Uninstalled using DISM' - ret['NeedsReboot'] = needs_reboot() - log.debug('NeedsReboot: %s', ret['NeedsReboot']) + ret["Success"] = True + ret["Message"] = "Uninstalled using DISM" + ret["NeedsReboot"] = needs_reboot() + log.debug("NeedsReboot: %s", ret["NeedsReboot"]) # Refresh the Updates Table self.refresh() - reboot = {0: 'Never Reboot', - 1: 'Always Reboot', - 2: 'Poss Reboot'} + reboot = {0: "Never Reboot", 1: "Always Reboot", 2: "Poss Reboot"} # Check the status of each update for update in self._updates: @@ -920,54 +930,57 @@ class WindowsUpdateAgent(object): for item in uninstall_list: if item.Identity.UpdateID == uid: if not update.IsInstalled: - ret['Updates'][uid]['Result'] = \ - 'Uninstallation Succeeded' + ret["Updates"][uid][ + "Result" + ] = "Uninstallation Succeeded" else: - ret['Updates'][uid]['Result'] = \ - 'Uninstallation Failed' - ret['Updates'][uid]['RebootBehavior'] = \ - reboot[update.InstallationBehavior.RebootBehavior] + ret["Updates"][uid]["Result"] = "Uninstallation Failed" + ret["Updates"][uid]["RebootBehavior"] = reboot[ + update.InstallationBehavior.RebootBehavior + ] return ret # Found a differenct exception, Raise error - log.error('Uninstall Failed: %s', failure_code) + log.error("Uninstall Failed: %s", failure_code) raise CommandExecutionError(failure_code) # Lookup dictionary - result_code = {0: 'Uninstallation Not Started', - 1: 'Uninstallation In Progress', - 2: 'Uninstallation Succeeded', - 3: 'Uninstallation Succeeded With Errors', - 4: 'Uninstallation Failed', - 5: 'Uninstallation Aborted'} + result_code = { + 0: "Uninstallation Not Started", + 1: "Uninstallation In Progress", + 2: "Uninstallation Succeeded", + 3: "Uninstallation Succeeded With Errors", + 4: "Uninstallation Failed", + 5: "Uninstallation Aborted", + } - log.debug('Uninstall Complete') + log.debug("Uninstall Complete") log.debug(result_code[result.ResultCode]) - ret['Message'] = result_code[result.ResultCode] + ret["Message"] = result_code[result.ResultCode] if result.ResultCode in [2, 3]: - ret['Success'] = True - ret['NeedsReboot'] = result.RebootRequired - log.debug('NeedsReboot: %s', result.RebootRequired) + ret["Success"] = True + ret["NeedsReboot"] = result.RebootRequired + log.debug("NeedsReboot: %s", result.RebootRequired) else: - log.debug('Uninstall Failed') - ret['Success'] = False + log.debug("Uninstall Failed") + ret["Success"] = False - reboot = {0: 'Never Reboot', - 1: 'Always Reboot', - 2: 'Poss Reboot'} + reboot = {0: "Never Reboot", 1: "Always Reboot", 2: "Poss Reboot"} for i in range(uninstall_list.Count): uid = uninstall_list.Item(i).Identity.UpdateID - ret['Updates'][uid]['Result'] = \ - result_code[result.GetUpdateResult(i).ResultCode] - ret['Updates'][uid]['RebootBehavior'] = reboot[ - uninstall_list.Item(i).InstallationBehavior.RebootBehavior] + ret["Updates"][uid]["Result"] = result_code[ + result.GetUpdateResult(i).ResultCode + ] + ret["Updates"][uid]["RebootBehavior"] = reboot[ + uninstall_list.Item(i).InstallationBehavior.RebootBehavior + ] return ret def _run(self, cmd): - ''' + """ Internal function for running commands. Used by the uninstall function. Args: @@ -975,7 +988,7 @@ class WindowsUpdateAgent(object): Returns: str: The stdout of the command - ''' + """ if isinstance(cmd, six.string_types): cmd = salt.utils.args.shlex_split(cmd) @@ -983,20 +996,18 @@ class WindowsUpdateAgent(object): try: log.debug(cmd) p = subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) return p.communicate() except (OSError, IOError) as exc: - log.debug('Command Failed: %s', ' '.join(cmd)) - log.debug('Error: %s', exc) + log.debug("Command Failed: %s", " ".join(cmd)) + log.debug("Error: %s", exc) raise CommandExecutionError(exc) def needs_reboot(): - ''' + """ Determines if the system needs to be rebooted. Returns: @@ -1011,9 +1022,9 @@ def needs_reboot(): salt.utils.win_update.needs_reboot() - ''' + """ # Initialize the PyCom system with salt.utils.winapi.Com(): # Create an AutoUpdate object - obj_sys = win32com.client.Dispatch('Microsoft.Update.SystemInfo') + obj_sys = win32com.client.Dispatch("Microsoft.Update.SystemInfo") return salt.utils.data.is_true(obj_sys.RebootRequired) diff --git a/salt/utils/winapi.py b/salt/utils/winapi.py index b05dc307ab3..8abdd633dd5 100644 --- a/salt/utils/winapi.py +++ b/salt/utils/winapi.py @@ -8,6 +8,7 @@ import threading try: import pythoncom + HAS_LIBS = True except ImportError: HAS_LIBS = False @@ -16,9 +17,9 @@ log = logging.getLogger(__name__) def __virtual__(): - ''' + """ Only load if required libraries exist - ''' + """ if not HAS_LIBS: return False else: @@ -30,14 +31,14 @@ class Com(object): self.need_com_init = not self._is_main_thread() def _is_main_thread(self): - return threading.current_thread().name == 'MainThread' + return threading.current_thread().name == "MainThread" def __enter__(self): if self.need_com_init: - log.debug('Initializing COM library') + log.debug("Initializing COM library") pythoncom.CoInitialize() def __exit__(self, exc_type, exc_value, traceback): if self.need_com_init: - log.debug('Uninitializing COM library') + log.debug("Uninitializing COM library") pythoncom.CoUninitialize() diff --git a/salt/utils/xdg.py b/salt/utils/xdg.py index 89fb08ff302..7c7a2d9574a 100644 --- a/salt/utils/xdg.py +++ b/salt/utils/xdg.py @@ -1,15 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" Create an XDG function to get the config dir -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import os def xdg_config_dir(): - ''' + """ Check xdg locations for config files - ''' - xdg_config = os.getenv('XDG_CONFIG_HOME', os.path.expanduser('~/.config')) - xdg_config_directory = os.path.join(xdg_config, 'salt') + """ + xdg_config = os.getenv("XDG_CONFIG_HOME", os.path.expanduser("~/.config")) + xdg_config_directory = os.path.join(xdg_config, "salt") return xdg_config_directory diff --git a/salt/utils/xmlutil.py b/salt/utils/xmlutil.py index 6d8d74fd3fa..9295afe7dff 100644 --- a/salt/utils/xmlutil.py +++ b/salt/utils/xmlutil.py @@ -1,30 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" Various XML utilities -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals def _conv_name(x): - ''' + """ If this XML tree has an xmlns attribute, then etree will add it to the beginning of the tag, like: "{http://path}tag". - ''' - if '}' in x: - comps = x.split('}') + """ + if "}" in x: + comps = x.split("}") name = comps[1] return name return x def _to_dict(xmltree): - ''' + """ Converts an XML ElementTree to a dictionary that only contains items. This is the default behavior in version 2017.7. This will default to prevent unexpected parsing issues on modules dependant on this. - ''' + """ # If this object has no children, the for..loop below will return nothing # for it, so just return a single dict representing it. if len(xmltree.getchildren()) < 1: @@ -51,9 +51,9 @@ def _to_dict(xmltree): def _to_full_dict(xmltree): - ''' + """ Returns the full XML dictionary including attributes. - ''' + """ xmldict = {} for attrName, attrValue in xmltree.attrib.items(): @@ -87,14 +87,14 @@ def _to_full_dict(xmltree): def to_dict(xmltree, attr=False): - ''' + """ Convert an XML tree into a dict. The tree that is passed in must be an ElementTree object. Args: xmltree: An ElementTree object. attr: If true, attributes will be parsed. If false, they will be ignored. - ''' + """ if attr: return _to_full_dict(xmltree) else: diff --git a/salt/utils/yaml.py b/salt/utils/yaml.py index 7fbfbe6035c..cd878b147a2 100644 --- a/salt/utils/yaml.py +++ b/salt/utils/yaml.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Convenience module that provides our custom loader and dumper in a single module -''' +""" +# pylint: disable=wildcard-import,unused-wildcard-import,unused-import from __future__ import absolute_import, print_function, unicode_literals -# pylint: disable=wildcard-import,unused-wildcard-import,unused-import -from yaml import YAMLError, parser, scanner from salt.utils.yamldumper import * from salt.utils.yamlloader import * +from yaml import YAMLError, parser, scanner + +# pylint: enable=wildcard-import,unused-wildcard-import,unused-import diff --git a/salt/utils/yamldumper.py b/salt/utils/yamldumper.py index a756564e635..9304c03fbdd 100644 --- a/salt/utils/yamldumper.py +++ b/salt/utils/yamldumper.py @@ -1,13 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" salt.utils.yamldumper ~~~~~~~~~~~~~~~~~~~~~ -''' +""" # pylint: disable=W0232 # class has no __init__ method from __future__ import absolute_import, print_function, unicode_literals + +import collections + +import salt.utils.context +import yaml # pylint: disable=blacklisted-import +from salt.utils.odict import OrderedDict + try: from yaml import CDumper as Dumper from yaml import CSafeDumper as SafeDumper @@ -15,43 +22,45 @@ except ImportError: from yaml import Dumper from yaml import SafeDumper -import yaml # pylint: disable=blacklisted-import -import collections -import salt.utils.context -from salt.utils.odict import OrderedDict -__all__ = ['OrderedDumper', 'SafeOrderedDumper', 'IndentedSafeOrderedDumper', - 'get_dumper', 'dump', 'safe_dump'] +__all__ = [ + "OrderedDumper", + "SafeOrderedDumper", + "IndentedSafeOrderedDumper", + "get_dumper", + "dump", + "safe_dump", +] class IndentMixin(Dumper): - ''' + """ Mixin that improves YAML dumped list readability by indenting them by two spaces, instead of being flush with the key they are under. - ''' + """ def increase_indent(self, flow=False, indentless=False): return super(IndentMixin, self).increase_indent(flow, False) class OrderedDumper(Dumper): - ''' + """ A YAML dumper that represents python OrderedDict as simple YAML map. - ''' + """ class SafeOrderedDumper(SafeDumper): - ''' + """ A YAML safe dumper that represents python OrderedDict as simple YAML map. - ''' + """ class IndentedSafeOrderedDumper(IndentMixin, SafeOrderedDumper): - ''' + """ A YAML safe dumper that represents python OrderedDict as simple YAML map, and also indents lists by two spaces. - ''' + """ def represent_ordereddict(dumper, data): @@ -59,7 +68,7 @@ def represent_ordereddict(dumper, data): def represent_undefined(dumper, data): - return dumper.represent_scalar(u'tag:yaml.org,2002:null', u'NULL') + return dumper.represent_scalar("tag:yaml.org,2002:null", "NULL") OrderedDumper.add_representer(OrderedDict, represent_ordereddict) @@ -67,58 +76,56 @@ SafeOrderedDumper.add_representer(OrderedDict, represent_ordereddict) SafeOrderedDumper.add_representer(None, represent_undefined) OrderedDumper.add_representer( - collections.defaultdict, - yaml.representer.SafeRepresenter.represent_dict + collections.defaultdict, yaml.representer.SafeRepresenter.represent_dict ) SafeOrderedDumper.add_representer( - collections.defaultdict, - yaml.representer.SafeRepresenter.represent_dict + collections.defaultdict, yaml.representer.SafeRepresenter.represent_dict ) OrderedDumper.add_representer( salt.utils.context.NamespacedDictWrapper, - yaml.representer.SafeRepresenter.represent_dict + yaml.representer.SafeRepresenter.represent_dict, ) SafeOrderedDumper.add_representer( salt.utils.context.NamespacedDictWrapper, - yaml.representer.SafeRepresenter.represent_dict + yaml.representer.SafeRepresenter.represent_dict, ) OrderedDumper.add_representer( - 'tag:yaml.org,2002:timestamp', - OrderedDumper.represent_scalar) + "tag:yaml.org,2002:timestamp", OrderedDumper.represent_scalar +) SafeOrderedDumper.add_representer( - 'tag:yaml.org,2002:timestamp', - SafeOrderedDumper.represent_scalar) + "tag:yaml.org,2002:timestamp", SafeOrderedDumper.represent_scalar +) def get_dumper(dumper_name): return { - 'OrderedDumper': OrderedDumper, - 'SafeOrderedDumper': SafeOrderedDumper, - 'IndentedSafeOrderedDumper': IndentedSafeOrderedDumper, + "OrderedDumper": OrderedDumper, + "SafeOrderedDumper": SafeOrderedDumper, + "IndentedSafeOrderedDumper": IndentedSafeOrderedDumper, }.get(dumper_name) def dump(data, stream=None, **kwargs): - ''' + """ .. versionadded:: 2018.3.0 Helper that wraps yaml.dump and ensures that we encode unicode strings unless explicitly told not to. - ''' - if 'allow_unicode' not in kwargs: - kwargs['allow_unicode'] = True - kwargs.setdefault('default_flow_style', None) + """ + if "allow_unicode" not in kwargs: + kwargs["allow_unicode"] = True + kwargs.setdefault("default_flow_style", None) return yaml.dump(data, stream, **kwargs) def safe_dump(data, stream=None, **kwargs): - ''' + """ Use a custom dumper to ensure that defaultdict and OrderedDict are represented properly. Ensure that unicode strings are encoded unless explicitly told not to. - ''' - if 'allow_unicode' not in kwargs: - kwargs['allow_unicode'] = True - kwargs.setdefault('default_flow_style', None) + """ + if "allow_unicode" not in kwargs: + kwargs["allow_unicode"] = True + kwargs.setdefault("default_flow_style", None) return yaml.dump(data, stream, Dumper=SafeOrderedDumper, **kwargs) diff --git a/salt/utils/yamlencoding.py b/salt/utils/yamlencoding.py index 3d0a7186e9f..5249d3103bf 100644 --- a/salt/utils/yamlencoding.py +++ b/salt/utils/yamlencoding.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Functions for adding yaml encoding to the jinja context -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import io # Import 3rd-party libs @@ -17,11 +18,11 @@ from salt.utils.decorators.jinja import jinja_filter @jinja_filter() def yaml_dquote(text): - ''' + """ Make text into a double-quoted YAML string with correct escaping for special characters. Includes the opening and closing double quote characters. - ''' + """ with io.StringIO() as ostream: yemitter = yaml.emitter.Emitter(ostream, width=six.MAXSIZE) yemitter.write_double_quoted(six.text_type(text)) @@ -30,11 +31,11 @@ def yaml_dquote(text): @jinja_filter() def yaml_squote(text): - ''' + """ Make text into a single-quoted YAML string with correct escaping for special characters. Includes the opening and closing single quote characters. - ''' + """ with io.StringIO() as ostream: yemitter = yaml.emitter.Emitter(ostream, width=six.MAXSIZE) yemitter.write_single_quoted(six.text_type(text)) @@ -43,10 +44,10 @@ def yaml_squote(text): @jinja_filter() def yaml_encode(data): - ''' + """ A simple YAML encode that can take a single-element datatype and return a string representation. - ''' + """ yrepr = yaml.representer.SafeRepresenter() ynode = yrepr.represent_data(data) if not isinstance(ynode, yaml.ScalarNode): @@ -55,7 +56,7 @@ def yaml_encode(data): " failed for {0}".format(type(data)) ) - tag = ynode.tag.rsplit(':', 1)[-1] + tag = ynode.tag.rsplit(":", 1)[-1] ret = ynode.value if tag == "str": diff --git a/salt/utils/yamlloader.py b/salt/utils/yamlloader.py index 0516647d1f6..e9d80fc4ad6 100644 --- a/salt/utils/yamlloader.py +++ b/salt/utils/yamlloader.py @@ -1,15 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Custom YAML loading in Salt -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import warnings +import salt.utils.stringutils import yaml # pylint: disable=blacklisted-import -from yaml.nodes import MappingNode, SequenceNode from yaml.constructor import ConstructorError +from yaml.nodes import MappingNode, SequenceNode + try: yaml.Loader = yaml.CLoader yaml.Dumper = yaml.CDumper @@ -18,47 +21,40 @@ try: except Exception: # pylint: disable=broad-except pass -import salt.utils.stringutils - -__all__ = ['SaltYamlSafeLoader', 'load', 'safe_load'] +__all__ = ["SaltYamlSafeLoader", "load", "safe_load"] class DuplicateKeyWarning(RuntimeWarning): - ''' + """ Warned when duplicate keys exist - ''' + """ -warnings.simplefilter('always', category=DuplicateKeyWarning) +warnings.simplefilter("always", category=DuplicateKeyWarning) # with code integrated from https://gist.github.com/844388 class SaltYamlSafeLoader(yaml.SafeLoader): - ''' + """ Create a custom YAML loader that uses the custom constructor. This allows for the YAML loading defaults to be manipulated based on needs within salt to make things like sls file more intuitive. - ''' + """ + def __init__(self, stream, dictclass=dict): super(SaltYamlSafeLoader, self).__init__(stream) if dictclass is not dict: # then assume ordered dict and use it for both !map and !omap + self.add_constructor("tag:yaml.org,2002:map", type(self).construct_yaml_map) self.add_constructor( - 'tag:yaml.org,2002:map', - type(self).construct_yaml_map) - self.add_constructor( - 'tag:yaml.org,2002:omap', - type(self).construct_yaml_map) + "tag:yaml.org,2002:omap", type(self).construct_yaml_map + ) + self.add_constructor("tag:yaml.org,2002:str", type(self).construct_yaml_str) self.add_constructor( - 'tag:yaml.org,2002:str', - type(self).construct_yaml_str) - self.add_constructor( - 'tag:yaml.org,2002:python/unicode', - type(self).construct_unicode) - self.add_constructor( - 'tag:yaml.org,2002:timestamp', - type(self).construct_scalar) + "tag:yaml.org,2002:python/unicode", type(self).construct_unicode + ) + self.add_constructor("tag:yaml.org,2002:timestamp", type(self).construct_scalar) self.dictclass = dictclass def construct_yaml_map(self, node): @@ -71,19 +67,20 @@ class SaltYamlSafeLoader(yaml.SafeLoader): return node.value def construct_mapping(self, node, deep=False): - ''' + """ Build the mapping for YAML - ''' + """ if not isinstance(node, MappingNode): raise ConstructorError( None, None, - 'expected a mapping node, but found {0}'.format(node.id), - node.start_mark) + "expected a mapping node, but found {0}".format(node.id), + node.start_mark, + ) self.flatten_mapping(node) - context = 'while constructing a mapping' + context = "while constructing a mapping" mapping = self.dictclass() for key_node, value_node in node.value: key = self.construct_object(key_node, deep=deep) @@ -94,31 +91,33 @@ class SaltYamlSafeLoader(yaml.SafeLoader): context, node.start_mark, "found unacceptable key {0}".format(key_node.value), - key_node.start_mark) + key_node.start_mark, + ) value = self.construct_object(value_node, deep=deep) if key in mapping: raise ConstructorError( context, node.start_mark, "found conflicting ID '{0}'".format(key), - key_node.start_mark) + key_node.start_mark, + ) mapping[key] = value return mapping def construct_scalar(self, node): - ''' + """ Verify integers and pass them in correctly is they are declared as octal - ''' - if node.tag == 'tag:yaml.org,2002:int': - if node.value == '0': + """ + if node.tag == "tag:yaml.org,2002:int": + if node.value == "0": pass - elif node.value.startswith('0') and not node.value.startswith(('0b', '0x')): - node.value = node.value.lstrip('0') + elif node.value.startswith("0") and not node.value.startswith(("0b", "0x")): + node.value = node.value.lstrip("0") # If value was all zeros, node.value would have been reduced to # an empty string. Change it to '0'. - if node.value == '': - node.value = '0' + if node.value == "": + node.value = "0" return super(SaltYamlSafeLoader, self).construct_scalar(node) def construct_yaml_str(self, node): @@ -131,7 +130,7 @@ class SaltYamlSafeLoader(yaml.SafeLoader): while index < len(node.value): key_node, value_node = node.value[index] - if key_node.tag == 'tag:yaml.org,2002:merge': + if key_node.tag == "tag:yaml.org,2002:merge": del node.value[index] if isinstance(value_node, MappingNode): self.flatten_mapping(value_node) @@ -140,22 +139,30 @@ class SaltYamlSafeLoader(yaml.SafeLoader): submerge = [] for subnode in value_node.value: if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping for merging, but found {0}".format(subnode.id), - subnode.start_mark) + raise ConstructorError( + "while constructing a mapping", + node.start_mark, + "expected a mapping for merging, but found {0}".format( + subnode.id + ), + subnode.start_mark, + ) self.flatten_mapping(subnode) submerge.append(subnode.value) submerge.reverse() for value in submerge: merge.extend(value) else: - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping or list of mappings for merging, but found {0}".format(value_node.id), - value_node.start_mark) - elif key_node.tag == 'tag:yaml.org,2002:value': - key_node.tag = 'tag:yaml.org,2002:str' + raise ConstructorError( + "while constructing a mapping", + node.start_mark, + "expected a mapping or list of mappings for merging, but found {0}".format( + value_node.id + ), + value_node.start_mark, + ) + elif key_node.tag == "tag:yaml.org,2002:value": + key_node.tag = "tag:yaml.org,2002:str" index += 1 else: index += 1 @@ -172,9 +179,9 @@ def load(stream, Loader=SaltYamlSafeLoader): def safe_load(stream, Loader=SaltYamlSafeLoader): - ''' + """ .. versionadded:: 2018.3.0 Helper function which automagically uses our custom loader. - ''' + """ return yaml.load(stream, Loader=Loader) diff --git a/salt/utils/yamlloader_old.py b/salt/utils/yamlloader_old.py index 3c7e25aed16..f231784606c 100644 --- a/salt/utils/yamlloader_old.py +++ b/salt/utils/yamlloader_old.py @@ -1,62 +1,59 @@ # -*- coding: utf-8 -*- -''' +""" Custom YAML loading in Salt -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import re import warnings +import salt.utils.stringutils import yaml # pylint: disable=blacklisted-import -from yaml.nodes import MappingNode, SequenceNode from yaml.constructor import ConstructorError +from yaml.nodes import MappingNode, SequenceNode + try: yaml.Loader = yaml.CLoader yaml.Dumper = yaml.CDumper except Exception: # pylint: disable=broad-except pass -import salt.utils.stringutils -__all__ = ['SaltYamlSafeLoader', 'load', 'safe_load'] +__all__ = ["SaltYamlSafeLoader", "load", "safe_load"] class DuplicateKeyWarning(RuntimeWarning): - ''' + """ Warned when duplicate keys exist - ''' + """ -warnings.simplefilter('always', category=DuplicateKeyWarning) +warnings.simplefilter("always", category=DuplicateKeyWarning) # with code integrated from https://gist.github.com/844388 class SaltYamlSafeLoader(yaml.SafeLoader): - ''' + """ Create a custom YAML loader that uses the custom constructor. This allows for the YAML loading defaults to be manipulated based on needs within salt to make things like sls file more intuitive. - ''' + """ + def __init__(self, stream, dictclass=dict): super(SaltYamlSafeLoader, self).__init__(stream) if dictclass is not dict: # then assume ordered dict and use it for both !map and !omap + self.add_constructor("tag:yaml.org,2002:map", type(self).construct_yaml_map) self.add_constructor( - 'tag:yaml.org,2002:map', - type(self).construct_yaml_map) - self.add_constructor( - 'tag:yaml.org,2002:omap', - type(self).construct_yaml_map) + "tag:yaml.org,2002:omap", type(self).construct_yaml_map + ) + self.add_constructor("tag:yaml.org,2002:str", type(self).construct_yaml_str) self.add_constructor( - 'tag:yaml.org,2002:str', - type(self).construct_yaml_str) - self.add_constructor( - 'tag:yaml.org,2002:python/unicode', - type(self).construct_unicode) - self.add_constructor( - 'tag:yaml.org,2002:timestamp', - type(self).construct_scalar) + "tag:yaml.org,2002:python/unicode", type(self).construct_unicode + ) + self.add_constructor("tag:yaml.org,2002:timestamp", type(self).construct_scalar) self.dictclass = dictclass def construct_yaml_map(self, node): @@ -69,19 +66,20 @@ class SaltYamlSafeLoader(yaml.SafeLoader): return node.value def construct_mapping(self, node, deep=False): - ''' + """ Build the mapping for YAML - ''' + """ if not isinstance(node, MappingNode): raise ConstructorError( None, None, - 'expected a mapping node, but found {0}'.format(node.id), - node.start_mark) + "expected a mapping node, but found {0}".format(node.id), + node.start_mark, + ) self.flatten_mapping(node) - context = 'while constructing a mapping' + context = "while constructing a mapping" mapping = self.dictclass() for key_node, value_node in node.value: key = self.construct_object(key_node, deep=deep) @@ -92,32 +90,34 @@ class SaltYamlSafeLoader(yaml.SafeLoader): context, node.start_mark, "found unacceptable key {0}".format(key_node.value), - key_node.start_mark) + key_node.start_mark, + ) value = self.construct_object(value_node, deep=deep) if key in mapping: raise ConstructorError( context, node.start_mark, "found conflicting ID '{0}'".format(key), - key_node.start_mark) + key_node.start_mark, + ) mapping[key] = value return mapping def construct_scalar(self, node): - ''' + """ Verify integers and pass them in correctly is they are declared as octal - ''' - if node.tag == 'tag:yaml.org,2002:int': - if node.value == '0': + """ + if node.tag == "tag:yaml.org,2002:int": + if node.value == "0": pass - elif node.value.startswith('0') and not node.value.startswith(('0b', '0x')): - node.value = node.value.lstrip('0') + elif node.value.startswith("0") and not node.value.startswith(("0b", "0x")): + node.value = node.value.lstrip("0") # If value was all zeros, node.value would have been reduced to # an empty string. Change it to '0'. - if node.value == '': - node.value = '0' - elif node.tag == 'tag:yaml.org,2002:str': + if node.value == "": + node.value = "0" + elif node.tag == "tag:yaml.org,2002:str": # If any string comes in as a quoted unicode literal, eval it into # the proper unicode string type. if re.match(r'^u([\'"]).+\1$', node.value, flags=re.IGNORECASE): @@ -129,9 +129,9 @@ class SaltYamlSafeLoader(yaml.SafeLoader): return salt.utils.stringutils.to_unicode(value) def fetch_plain(self): - ''' + """ Handle unicode literal strings which appear inline in the YAML - ''' + """ orig_line = self.line orig_column = self.column orig_pointer = self.pointer @@ -146,7 +146,7 @@ class SaltYamlSafeLoader(yaml.SafeLoader): self.line = orig_line self.column = orig_column self.pointer = orig_pointer - if self.peek(0) == 'u': + if self.peek(0) == "u": # Might be a unicode literal string, check for 2nd char and # call the appropriate fetch func if it's a quote quote_char = self.peek(1) @@ -155,7 +155,7 @@ class SaltYamlSafeLoader(yaml.SafeLoader): # pointer by 1 self.column += 1 self.pointer += 1 - if quote_char == '\'': + if quote_char == "'": return self.fetch_single() else: return self.fetch_double() @@ -175,7 +175,7 @@ class SaltYamlSafeLoader(yaml.SafeLoader): while index < len(node.value): key_node, value_node = node.value[index] - if key_node.tag == 'tag:yaml.org,2002:merge': + if key_node.tag == "tag:yaml.org,2002:merge": del node.value[index] if isinstance(value_node, MappingNode): self.flatten_mapping(value_node) @@ -184,22 +184,30 @@ class SaltYamlSafeLoader(yaml.SafeLoader): submerge = [] for subnode in value_node.value: if not isinstance(subnode, MappingNode): - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping for merging, but found {0}".format(subnode.id), - subnode.start_mark) + raise ConstructorError( + "while constructing a mapping", + node.start_mark, + "expected a mapping for merging, but found {0}".format( + subnode.id + ), + subnode.start_mark, + ) self.flatten_mapping(subnode) submerge.append(subnode.value) submerge.reverse() for value in submerge: merge.extend(value) else: - raise ConstructorError("while constructing a mapping", - node.start_mark, - "expected a mapping or list of mappings for merging, but found {0}".format(value_node.id), - value_node.start_mark) - elif key_node.tag == 'tag:yaml.org,2002:value': - key_node.tag = 'tag:yaml.org,2002:str' + raise ConstructorError( + "while constructing a mapping", + node.start_mark, + "expected a mapping or list of mappings for merging, but found {0}".format( + value_node.id + ), + value_node.start_mark, + ) + elif key_node.tag == "tag:yaml.org,2002:value": + key_node.tag = "tag:yaml.org,2002:str" index += 1 else: index += 1 @@ -216,9 +224,9 @@ def load(stream, Loader=SaltYamlSafeLoader): def safe_load(stream, Loader=SaltYamlSafeLoader): - ''' + """ .. versionadded:: 2018.3.0 Helper function which automagically uses our custom loader. - ''' + """ return yaml.load(stream, Loader=Loader) diff --git a/salt/utils/yast.py b/salt/utils/yast.py index 621bb7fda01..5ef6a2050f3 100644 --- a/salt/utils/yast.py +++ b/salt/utils/yast.py @@ -1,25 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" Utilities for managing YAST .. versionadded:: Beryllium -''' +""" from __future__ import absolute_import, print_function, unicode_literals -from salt._compat import ElementTree as ET -import salt.utils.xmlutil as xml + import salt.utils.files +import salt.utils.xmlutil as xml import salt.utils.yaml +from salt._compat import ElementTree as ET def mksls(src, dst=None): - ''' + """ Convert an AutoYAST file to an SLS file - ''' - with salt.utils.files.fopen(src, 'r') as fh_: + """ + with salt.utils.files.fopen(src, "r") as fh_: ps_opts = xml.to_dict(ET.fromstring(fh_.read())) if dst is not None: - with salt.utils.files.fopen(dst, 'w') as fh_: + with salt.utils.files.fopen(dst, "w") as fh_: salt.utils.yaml.safe_dump(ps_opts, fh_, default_flow_style=False) else: return salt.utils.yaml.safe_dump(ps_opts, default_flow_style=False) diff --git a/salt/utils/zeromq.py b/salt/utils/zeromq.py index b53258f550d..b84f828324b 100644 --- a/salt/utils/zeromq.py +++ b/salt/utils/zeromq.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" ZMQ-specific functions -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import salt.ext.tornado - import logging + +import salt.ext.tornado import salt.ext.tornado.ioloop -from salt.exceptions import SaltSystemExit from salt._compat import ipaddress +from salt.exceptions import SaltSystemExit log = logging.getLogger(__name__) @@ -18,7 +18,7 @@ try: import zmq except ImportError: zmq = None - log.debug('ZMQ module is not found') + log.debug("ZMQ module is not found") ZMQDefaultLoop = None ZMQ_VERSION_INFO = (-1, -1, -1) @@ -26,18 +26,21 @@ LIBZMQ_VERSION_INFO = (-1, -1, -1) try: if zmq: - ZMQ_VERSION_INFO = tuple([int(v_el) for v_el in zmq.__version__.split('.')]) - LIBZMQ_VERSION_INFO = tuple([int(v_el) for v_el in zmq.zmq_version().split('.')]) + ZMQ_VERSION_INFO = tuple([int(v_el) for v_el in zmq.__version__.split(".")]) + LIBZMQ_VERSION_INFO = tuple( + [int(v_el) for v_el in zmq.zmq_version().split(".")] + ) if ZMQ_VERSION_INFO[0] > 16: # 17.0.x+ deprecates zmq's ioloops ZMQDefaultLoop = salt.ext.tornado.ioloop.IOLoop except Exception: # pylint: disable=broad-except - log.exception('Error while getting LibZMQ/PyZMQ library version') + log.exception("Error while getting LibZMQ/PyZMQ library version") if ZMQDefaultLoop is None: try: import zmq.eventloop.ioloop + # Support for ZeroMQ 13.x - if not hasattr(zmq.eventloop.ioloop, 'ZMQIOLoop'): + if not hasattr(zmq.eventloop.ioloop, "ZMQIOLoop"): zmq.eventloop.ioloop.ZMQIOLoop = zmq.eventloop.ioloop.IOLoop if salt.ext.tornado.version_info < (5,): ZMQDefaultLoop = zmq.eventloop.ioloop.ZMQIOLoop @@ -48,11 +51,11 @@ if ZMQDefaultLoop is None: def install_zmq(): - ''' + """ While pyzmq 17 no longer needs any special integration for tornado, older version still need one. :return: - ''' + """ # The zmq module is mocked in Sphinx, so when we build the docs # ZMQ_VERSION_INFO ends up being an empty tuple. Using a tuple comparison # instead of checking the first element of ZMQ_VERSION_INFO will prevent an @@ -67,26 +70,24 @@ def check_ipc_path_max_len(uri): # Linux, and 103 characters on BSD-based systems. if zmq is None: return - ipc_path_max_len = getattr(zmq, 'IPC_PATH_MAX_LEN', 103) + ipc_path_max_len = getattr(zmq, "IPC_PATH_MAX_LEN", 103) if ipc_path_max_len and len(uri) > ipc_path_max_len: raise SaltSystemExit( - 'The socket path is longer than allowed by OS. ' - '\'{0}\' is longer than {1} characters. ' - 'Either try to reduce the length of this setting\'s ' - 'path or switch to TCP; in the configuration file, ' - 'set "ipc_mode: tcp".'.format( - uri, ipc_path_max_len - ) + "The socket path is longer than allowed by OS. " + "'{0}' is longer than {1} characters. " + "Either try to reduce the length of this setting's " + "path or switch to TCP; in the configuration file, " + 'set "ipc_mode: tcp".'.format(uri, ipc_path_max_len) ) def ip_bracket(addr): - ''' + """ Ensure IP addresses are URI-compatible - specifically, add brackets around IPv6 literals if they are not already present. - ''' + """ addr = str(addr) - addr = addr.lstrip('[') - addr = addr.rstrip(']') + addr = addr.lstrip("[") + addr = addr.rstrip("]") addr = ipaddress.ip_address(addr) - return ('[{}]' if addr.version == 6 else '{}').format(addr) + return ("[{}]" if addr.version == 6 else "{}").format(addr) diff --git a/salt/utils/zfs.py b/salt/utils/zfs.py index 628d2448fd5..99733f1547f 100644 --- a/salt/utils/zfs.py +++ b/salt/utils/zfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Utility functions for zfs These functions are for dealing with type conversion and basic execution @@ -11,118 +11,125 @@ These functions are for dealing with type conversion and basic execution .. versionadded:: 2018.3.1 -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import logging +import math import os import re -import math -import logging from numbers import Number -# Import salt libs -from salt.utils.decorators import memoize as real_memoize -from salt.utils.odict import OrderedDict -from salt.utils.stringutils import to_num as str_to_num import salt.modules.cmdmod # Import 3rd-party libs from salt.ext.six.moves import zip +# Import salt libs +from salt.utils.decorators import memoize as real_memoize +from salt.utils.odict import OrderedDict +from salt.utils.stringutils import to_num as str_to_num + # Size conversion data -re_zfs_size = re.compile(r'^(\d+|\d+(?=\d*)\.\d+)([KkMmGgTtPpEe][Bb]?)$') -zfs_size = ['K', 'M', 'G', 'T', 'P', 'E'] +re_zfs_size = re.compile(r"^(\d+|\d+(?=\d*)\.\d+)([KkMmGgTtPpEe][Bb]?)$") +zfs_size = ["K", "M", "G", "T", "P", "E"] log = logging.getLogger(__name__) def _check_retcode(cmd): - ''' + """ Simple internal wrapper for cmdmod.retcode - ''' - return salt.modules.cmdmod.retcode(cmd, output_loglevel='quiet', ignore_retcode=True) == 0 + """ + return ( + salt.modules.cmdmod.retcode(cmd, output_loglevel="quiet", ignore_retcode=True) + == 0 + ) def _exec(**kwargs): - ''' + """ Simple internal wrapper for cmdmod.run - ''' - if 'ignore_retcode' not in kwargs: - kwargs['ignore_retcode'] = True - if 'output_loglevel' not in kwargs: - kwargs['output_loglevel'] = 'quiet' + """ + if "ignore_retcode" not in kwargs: + kwargs["ignore_retcode"] = True + if "output_loglevel" not in kwargs: + kwargs["output_loglevel"] = "quiet" return salt.modules.cmdmod.run_all(**kwargs) -def _merge_last(values, merge_after, merge_with=' '): - ''' +def _merge_last(values, merge_after, merge_with=" "): + """ Merge values all values after X into the last value - ''' + """ if len(values) > merge_after: - values = values[0:(merge_after-1)] + [merge_with.join(values[(merge_after-1):])] + values = values[0 : (merge_after - 1)] + [ + merge_with.join(values[(merge_after - 1) :]) + ] return values def _property_normalize_name(name): - ''' + """ Normalizes property names - ''' - if '@' in name: - name = name[:name.index('@')+1] + """ + if "@" in name: + name = name[: name.index("@") + 1] return name def _property_detect_type(name, values): - ''' + """ Detect the datatype of a property - ''' - value_type = 'str' - if values.startswith('on | off'): - value_type = 'bool' - elif values.startswith('yes | no'): - value_type = 'bool_alt' - elif values in ['<size>', '<size> | none']: - value_type = 'size' - elif values in ['<count>', '<count> | none', '<guid>']: - value_type = 'numeric' - elif name in ['sharenfs', 'sharesmb', 'canmount']: - value_type = 'bool' - elif name in ['version', 'copies']: - value_type = 'numeric' + """ + value_type = "str" + if values.startswith("on | off"): + value_type = "bool" + elif values.startswith("yes | no"): + value_type = "bool_alt" + elif values in ["<size>", "<size> | none"]: + value_type = "size" + elif values in ["<count>", "<count> | none", "<guid>"]: + value_type = "numeric" + elif name in ["sharenfs", "sharesmb", "canmount"]: + value_type = "bool" + elif name in ["version", "copies"]: + value_type = "numeric" return value_type def _property_create_dict(header, data): - ''' + """ Create a property dict - ''' + """ prop = dict(zip(header, _merge_last(data, len(header)))) - prop['name'] = _property_normalize_name(prop['property']) - prop['type'] = _property_detect_type(prop['name'], prop['values']) - prop['edit'] = from_bool(prop['edit']) - if 'inherit' in prop: - prop['inherit'] = from_bool(prop['inherit']) - del prop['property'] + prop["name"] = _property_normalize_name(prop["property"]) + prop["type"] = _property_detect_type(prop["name"], prop["values"]) + prop["edit"] = from_bool(prop["edit"]) + if "inherit" in prop: + prop["inherit"] = from_bool(prop["inherit"]) + del prop["property"] return prop def _property_parse_cmd(cmd, alias=None): - ''' + """ Parse output of zpool/zfs get command - ''' + """ if not alias: alias = {} properties = {} # NOTE: append get to command - if cmd[-3:] != 'get': - cmd += ' get' + if cmd[-3:] != "get": + cmd += " get" # NOTE: parse output prop_hdr = [] - for prop_data in _exec(cmd=cmd)['stderr'].split('\n'): + for prop_data in _exec(cmd=cmd)["stderr"].split("\n"): # NOTE: make the line data more manageable prop_data = prop_data.lower().split() @@ -130,74 +137,81 @@ def _property_parse_cmd(cmd, alias=None): if not prop_data: continue # NOTE: parse header - elif prop_data[0] == 'property': + elif prop_data[0] == "property": prop_hdr = prop_data continue # NOTE: skip lines after data - elif not prop_hdr or prop_data[1] not in ['no', 'yes']: + elif not prop_hdr or prop_data[1] not in ["no", "yes"]: continue # NOTE: create property dict prop = _property_create_dict(prop_hdr, prop_data) # NOTE: add property to dict - properties[prop['name']] = prop - if prop['name'] in alias: - properties[alias[prop['name']]] = prop + properties[prop["name"]] = prop + if prop["name"] in alias: + properties[alias[prop["name"]]] = prop # NOTE: cleanup some duplicate data - del prop['name'] + del prop["name"] return properties -def _auto(direction, name, value, source='auto', convert_to_human=True): - ''' +def _auto(direction, name, value, source="auto", convert_to_human=True): + """ Internal magic for from_auto and to_auto - ''' + """ # NOTE: check direction - if direction not in ['to', 'from']: + if direction not in ["to", "from"]: return value # NOTE: collect property data props = property_data_zpool() - if source == 'zfs': + if source == "zfs": props = property_data_zfs() - elif source == 'auto': + elif source == "auto": props.update(property_data_zfs()) # NOTE: figure out the conversion type - value_type = props[name]['type'] if name in props else 'str' + value_type = props[name]["type"] if name in props else "str" # NOTE: convert - if value_type == 'size' and direction == 'to': - return globals()['{}_{}'.format(direction, value_type)](value, convert_to_human) + if value_type == "size" and direction == "to": + return globals()["{}_{}".format(direction, value_type)](value, convert_to_human) - return globals()['{}_{}'.format(direction, value_type)](value) + return globals()["{}_{}".format(direction, value_type)](value) @real_memoize def _zfs_cmd(): - ''' + """ Return the path of the zfs binary if present - ''' + """ # Get the path to the zfs binary. - return salt.utils.path.which('zfs') + return salt.utils.path.which("zfs") @real_memoize def _zpool_cmd(): - ''' + """ Return the path of the zpool binary if present - ''' + """ # Get the path to the zfs binary. - return salt.utils.path.which('zpool') + return salt.utils.path.which("zpool") -def _command(source, command, flags=None, opts=None, - property_name=None, property_value=None, - filesystem_properties=None, pool_properties=None, - target=None): - ''' +def _command( + source, + command, + flags=None, + opts=None, + property_name=None, + property_value=None, + filesystem_properties=None, + pool_properties=None, + target=None, +): + """ Build and properly escape a zfs command .. note:: @@ -206,9 +220,9 @@ def _command(source, command, flags=None, opts=None, to_auto(from_auto('input_here')), you do not need to do so your self first. - ''' + """ # NOTE: start with the zfs binary and command - cmd = [_zpool_cmd() if source == 'zpool' else _zfs_cmd(), command] + cmd = [_zpool_cmd() if source == "zpool" else _zfs_cmd(), command] # NOTE: append flags if we have any if flags is None: @@ -232,22 +246,36 @@ def _command(source, command, flags=None, opts=None, if filesystem_properties is None: filesystem_properties = {} for fsopt in sorted(filesystem_properties): - cmd.append('-O' if source == 'zpool' else '-o') - cmd.append('{key}={val}'.format( - key=fsopt, - val=to_auto(fsopt, filesystem_properties[fsopt], source='zfs', convert_to_human=False), - )) + cmd.append("-O" if source == "zpool" else "-o") + cmd.append( + "{key}={val}".format( + key=fsopt, + val=to_auto( + fsopt, + filesystem_properties[fsopt], + source="zfs", + convert_to_human=False, + ), + ) + ) # NOTE: append pool properties (really just options with a key/value) # we pass through 'sorted' to guarantee the same order if pool_properties is None: pool_properties = {} for fsopt in sorted(pool_properties): - cmd.append('-o') - cmd.append('{key}={val}'.format( - key=fsopt, - val=to_auto(fsopt, pool_properties[fsopt], source='zpool', convert_to_human=False), - )) + cmd.append("-o") + cmd.append( + "{key}={val}".format( + key=fsopt, + val=to_auto( + fsopt, + pool_properties[fsopt], + source="zpool", + convert_to_human=False, + ), + ) + ) # NOTE: append property and value # the set command takes a key=value pair, we need to support this @@ -258,10 +286,12 @@ def _command(source, command, flags=None, opts=None, if not isinstance(property_value, list): property_value = [property_value] for key, val in zip(property_name, property_value): - cmd.append('{key}={val}'.format( - key=key, - val=to_auto(key, val, source=source, convert_to_human=False), - )) + cmd.append( + "{key}={val}".format( + key=key, + val=to_auto(key, val, source=source, convert_to_human=False), + ) + ) else: cmd.append(property_name) @@ -276,49 +306,49 @@ def _command(source, command, flags=None, opts=None, continue cmd.append(to_str(tgt)) - return ' '.join(cmd) + return " ".join(cmd) def is_supported(): - ''' + """ Check the system for ZFS support - ''' + """ # Check for supported platforms # NOTE: ZFS on Windows is in development # NOTE: ZFS on NetBSD is in development on_supported_platform = False if salt.utils.platform.is_sunos(): on_supported_platform = True - elif salt.utils.platform.is_freebsd() and _check_retcode('kldstat -q -m zfs'): + elif salt.utils.platform.is_freebsd() and _check_retcode("kldstat -q -m zfs"): on_supported_platform = True - elif salt.utils.platform.is_linux() and os.path.exists('/sys/module/zfs'): + elif salt.utils.platform.is_linux() and os.path.exists("/sys/module/zfs"): on_supported_platform = True - elif salt.utils.platform.is_linux() and salt.utils.path.which('zfs-fuse'): + elif salt.utils.platform.is_linux() and salt.utils.path.which("zfs-fuse"): on_supported_platform = True - elif salt.utils.platform.is_darwin() and \ - os.path.exists('/Library/Extensions/zfs.kext') and \ - os.path.exists('/dev/zfs'): + elif ( + salt.utils.platform.is_darwin() + and os.path.exists("/Library/Extensions/zfs.kext") + and os.path.exists("/dev/zfs") + ): on_supported_platform = True # Additional check for the zpool command - return (salt.utils.path.which('zpool') and on_supported_platform) is True + return (salt.utils.path.which("zpool") and on_supported_platform) is True @real_memoize def has_feature_flags(): - ''' + """ Check if zpool-features is available - ''' + """ # get man location - man = salt.utils.path.which('man') - return _check_retcode('{man} zpool-features'.format( - man=man - )) if man else False + man = salt.utils.path.which("man") + return _check_retcode("{man} zpool-features".format(man=man)) if man else False @real_memoize def property_data_zpool(): - ''' + """ Return a dict of zpool properties .. note:: @@ -334,39 +364,47 @@ def property_data_zpool(): data that is hardcoded. There is no better way to get this information aside from reading the code. - ''' + """ # NOTE: man page also mentions a few short forms - property_data = _property_parse_cmd(_zpool_cmd(), { - 'allocated': 'alloc', - 'autoexpand': 'expand', - 'autoreplace': 'replace', - 'listsnapshots': 'listsnaps', - 'fragmentation': 'frag', - }) + property_data = _property_parse_cmd( + _zpool_cmd(), + { + "allocated": "alloc", + "autoexpand": "expand", + "autoreplace": "replace", + "listsnapshots": "listsnaps", + "fragmentation": "frag", + }, + ) # NOTE: zpool status/iostat has a few extra fields zpool_size_extra = [ - 'capacity-alloc', 'capacity-free', - 'operations-read', 'operations-write', - 'bandwith-read', 'bandwith-write', - 'read', 'write', + "capacity-alloc", + "capacity-free", + "operations-read", + "operations-write", + "bandwith-read", + "bandwith-write", + "read", + "write", ] zpool_numeric_extra = [ - 'cksum', 'cap', + "cksum", + "cap", ] for prop in zpool_size_extra: property_data[prop] = { - 'edit': False, - 'type': 'size', - 'values': '<size>', + "edit": False, + "type": "size", + "values": "<size>", } for prop in zpool_numeric_extra: property_data[prop] = { - 'edit': False, - 'type': 'numeric', - 'values': '<count>', + "edit": False, + "type": "numeric", + "values": "<count>", } return property_data @@ -374,7 +412,7 @@ def property_data_zpool(): @real_memoize def property_data_zfs(): - ''' + """ Return a dict of zfs properties .. note:: @@ -391,26 +429,29 @@ def property_data_zfs(): data that is hardcoded. There is no better way to get this information aside from reading the code. - ''' - return _property_parse_cmd(_zfs_cmd(), { - 'available': 'avail', - 'logicalreferenced': 'lrefer.', - 'logicalused': 'lused.', - 'referenced': 'refer', - 'volblocksize': 'volblock', - 'compression': 'compress', - 'readonly': 'rdonly', - 'recordsize': 'recsize', - 'refreservation': 'refreserv', - 'reservation': 'reserv', - }) + """ + return _property_parse_cmd( + _zfs_cmd(), + { + "available": "avail", + "logicalreferenced": "lrefer.", + "logicalused": "lused.", + "referenced": "refer", + "volblocksize": "volblock", + "compression": "compress", + "readonly": "rdonly", + "recordsize": "recsize", + "refreservation": "refreserv", + "reservation": "reserv", + }, + ) def from_numeric(value): - ''' + """ Convert zfs numeric to python int - ''' - if value == 'none': + """ + if value == "none": value = None elif value: value = str_to_num(value) @@ -418,66 +459,66 @@ def from_numeric(value): def to_numeric(value): - ''' + """ Convert python int to zfs numeric - ''' + """ value = from_numeric(value) if value is None: - value = 'none' + value = "none" return value def from_bool(value): - ''' + """ Convert zfs bool to python bool - ''' - if value in ['on', 'yes']: + """ + if value in ["on", "yes"]: value = True - elif value in ['off', 'no']: + elif value in ["off", "no"]: value = False - elif value == 'none': + elif value == "none": value = None return value def from_bool_alt(value): - ''' + """ Convert zfs bool_alt to python bool - ''' + """ return from_bool(value) def to_bool(value): - ''' + """ Convert python bool to zfs on/off bool - ''' + """ value = from_bool(value) if isinstance(value, bool): - value = 'on' if value else 'off' + value = "on" if value else "off" elif value is None: - value = 'none' + value = "none" return value def to_bool_alt(value): - ''' + """ Convert python to zfs yes/no value - ''' + """ value = from_bool_alt(value) if isinstance(value, bool): - value = 'yes' if value else 'no' + value = "yes" if value else "no" elif value is None: - value = 'none' + value = "none" return value def from_size(value): - ''' + """ Convert zfs size (human readable) to python int (bytes) - ''' + """ match_size = re_zfs_size.match(str(value)) if match_size: v_unit = match_size.group(2).upper()[0] @@ -493,14 +534,14 @@ def from_size(value): def to_size(value, convert_to_human=True): - ''' + """ Convert python int (bytes) to zfs size NOTE: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/pyzfs/common/util.py#114 - ''' + """ value = from_size(value) if value is None: - value = 'none' + value = "none" if isinstance(value, Number) and value > 1024 and convert_to_human: v_power = int(math.floor(math.log(value, 1024))) @@ -510,16 +551,10 @@ def to_size(value, convert_to_human=True): # see libzfs implementation linked above v_size_float = float(value) / v_multiplier if v_size_float == int(v_size_float): - value = "{:.0f}{}".format( - v_size_float, - zfs_size[v_power-1], - ) + value = "{:.0f}{}".format(v_size_float, zfs_size[v_power - 1],) else: for v_precision in ["{:.2f}{}", "{:.1f}{}", "{:.0f}{}"]: - v_size = v_precision.format( - v_size_float, - zfs_size[v_power-1], - ) + v_size = v_precision.format(v_size_float, zfs_size[v_power - 1],) if len(v_size) <= 5: value = v_size break @@ -528,10 +563,10 @@ def to_size(value, convert_to_human=True): def from_str(value): - ''' + """ Decode zfs safe string (used for name, path, ...) - ''' - if value == 'none': + """ + if value == "none": value = None if value: value = str(value) @@ -543,56 +578,56 @@ def from_str(value): def to_str(value): - ''' + """ Encode zfs safe string (used for name, path, ...) - ''' + """ value = from_str(value) if value: value = value.replace('"', '\\"') - if ' ' in value: + if " " in value: value = '"' + value + '"' elif value is None: - value = 'none' + value = "none" return value -def from_auto(name, value, source='auto'): - ''' +def from_auto(name, value, source="auto"): + """ Convert zfs value to python value - ''' - return _auto('from', name, value, source) + """ + return _auto("from", name, value, source) -def to_auto(name, value, source='auto', convert_to_human=True): - ''' +def to_auto(name, value, source="auto", convert_to_human=True): + """ Convert python value to zfs value - ''' - return _auto('to', name, value, source, convert_to_human) + """ + return _auto("to", name, value, source, convert_to_human) -def from_auto_dict(values, source='auto'): - ''' +def from_auto_dict(values, source="auto"): + """ Pass an entire dictionary to from_auto .. note:: The key will be passed as the name - ''' + """ for name, value in values.items(): values[name] = from_auto(name, value, source) return values -def to_auto_dict(values, source='auto', convert_to_human=True): - ''' +def to_auto_dict(values, source="auto", convert_to_human=True): + """ Pass an entire dictionary to to_auto .. note:: The key will be passed as the name - ''' + """ for name, value in values.items(): values[name] = to_auto(name, value, source, convert_to_human) @@ -600,29 +635,36 @@ def to_auto_dict(values, source='auto', convert_to_human=True): def is_snapshot(name): - ''' + """ Check if name is a valid snapshot name - ''' - return from_str(name).count('@') == 1 + """ + return from_str(name).count("@") == 1 def is_bookmark(name): - ''' + """ Check if name is a valid bookmark name - ''' - return from_str(name).count('#') == 1 + """ + return from_str(name).count("#") == 1 def is_dataset(name): - ''' + """ Check if name is a valid filesystem or volume name - ''' + """ return not is_snapshot(name) and not is_bookmark(name) -def zfs_command(command, flags=None, opts=None, property_name=None, property_value=None, - filesystem_properties=None, target=None): - ''' +def zfs_command( + command, + flags=None, + opts=None, + property_name=None, + property_value=None, + filesystem_properties=None, + target=None, +): + """ Build and properly escape a zfs command .. note:: @@ -631,9 +673,9 @@ def zfs_command(command, flags=None, opts=None, property_name=None, property_val to_auto(from_auto('input_here')), you do not need to do so your self first. - ''' + """ return _command( - 'zfs', + "zfs", command=command, flags=flags, opts=opts, @@ -645,9 +687,17 @@ def zfs_command(command, flags=None, opts=None, property_name=None, property_val ) -def zpool_command(command, flags=None, opts=None, property_name=None, property_value=None, - filesystem_properties=None, pool_properties=None, target=None): - ''' +def zpool_command( + command, + flags=None, + opts=None, + property_name=None, + property_value=None, + filesystem_properties=None, + pool_properties=None, + target=None, +): + """ Build and properly escape a zpool command .. note:: @@ -656,9 +706,9 @@ def zpool_command(command, flags=None, opts=None, property_name=None, property_v to_auto(from_auto('input_here')), you do not need to do so your self first. - ''' + """ return _command( - 'zpool', + "zpool", command=command, flags=flags, opts=opts, @@ -671,7 +721,7 @@ def zpool_command(command, flags=None, opts=None, property_name=None, property_v def parse_command_result(res, label=None): - ''' + """ Parse the result of a zpool/zfs command .. note:: @@ -684,28 +734,29 @@ def parse_command_result(res, label=None): We simple check those and return a OrderedDict were we set label = True|False and error = error_messages - ''' + """ ret = OrderedDict() if label: - ret[label] = res['retcode'] == 0 + ret[label] = res["retcode"] == 0 - if res['retcode'] != 0: - ret['error'] = [] - for error in res['stderr'].splitlines(): - if error.lower().startswith('usage:'): + if res["retcode"] != 0: + ret["error"] = [] + for error in res["stderr"].splitlines(): + if error.lower().startswith("usage:"): break if error.lower().startswith("use '-f'"): - error = error.replace('-f', 'force=True') + error = error.replace("-f", "force=True") if error.lower().startswith("use '-r'"): - error = error.replace('-r', 'recursive=True') - ret['error'].append(error) + error = error.replace("-r", "recursive=True") + ret["error"].append(error) - if ret['error']: - ret['error'] = "\n".join(ret['error']) + if ret["error"]: + ret["error"] = "\n".join(ret["error"]) else: - del ret['error'] + del ret["error"] return ret + # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 diff --git a/salt/version.py b/salt/version.py index 863ce1bf4b8..fb775b05949 100644 --- a/salt/version.py +++ b/salt/version.py @@ -1,15 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" Set up the version of Salt -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import platform import re import sys -import platform import warnings +# pylint: disable=invalid-name,redefined-builtin +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import map + # linux_distribution deprecated in py3.7 try: from platform import linux_distribution as _deprecated_linux_distribution @@ -18,13 +24,11 @@ try: with warnings.catch_warnings(): warnings.simplefilter("ignore") return _deprecated_linux_distribution(**kwargs) + + except ImportError: from distro import linux_distribution -# pylint: disable=invalid-name,redefined-builtin -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import map # Don't rely on external packages in this module since it's used at install time if sys.version_info[0] == 3: @@ -59,26 +63,36 @@ VERSION_LIMIT = MAX_SIZE - 200 class SaltStackVersion(object): - ''' + """ Handle SaltStack versions class. Knows how to parse ``git describe`` output, knows about release candidates and also supports version comparison. - ''' + """ - __slots__ = ('name', 'major', 'minor', 'bugfix', 'mbugfix', 'pre_type', 'pre_num', 'noc', 'sha') + __slots__ = ( + "name", + "major", + "minor", + "bugfix", + "mbugfix", + "pre_type", + "pre_num", + "noc", + "sha", + ) - git_sha_regex = r'(?P<sha>g?[a-f0-9]{7,40})' + git_sha_regex = r"(?P<sha>g?[a-f0-9]{7,40})" git_describe_regex = re.compile( - r'(?:[^\d]+)?(?P<major>[\d]{1,4})' - r'(?:\.(?P<minor>[\d]{1,2}))?' - r'(?:\.(?P<bugfix>[\d]{0,2}))?' - r'(?:\.(?P<mbugfix>[\d]{0,2}))?' - r'(?:(?P<pre_type>rc|a|b|alpha|beta|nb)(?P<pre_num>[\d]{1}))?' - r'(?:(?:.*)-(?P<noc>(?:[\d]+|n/a))-' + git_sha_regex + r')?' + r"(?:[^\d]+)?(?P<major>[\d]{1,4})" + r"(?:\.(?P<minor>[\d]{1,2}))?" + r"(?:\.(?P<bugfix>[\d]{0,2}))?" + r"(?:\.(?P<mbugfix>[\d]{0,2}))?" + r"(?:(?P<pre_type>rc|a|b|alpha|beta|nb)(?P<pre_num>[\d]{1}))?" + r"(?:(?:.*)-(?P<noc>(?:[\d]+|n/a))-" + git_sha_regex + r")?" ) - git_sha_regex = r'^' + git_sha_regex + git_sha_regex = r"^" + git_sha_regex if six.PY2: git_sha_regex = git_sha_regex.decode(__salt_system_encoding__) @@ -94,27 +108,25 @@ class SaltStackVersion(object): NAMES = { # Let's keep at least 3 version names uncommented counting from the # latest release so we can map deprecation warnings to versions. - - # pylint: disable=E8203 # ----- Please refrain from fixing PEP-8 E203 and E265 -----> # The idea is to keep this readable. # ----------------------------------------------------------- - 'Hydrogen' : (2014, 1), - 'Helium' : (2014, 7), - 'Lithium' : (2015, 5), - 'Beryllium' : (2015, 8), - 'Boron' : (2016, 3), - 'Carbon' : (2016, 11), - 'Nitrogen' : (2017, 7), - 'Oxygen' : (2018, 3), - 'Fluorine' : (2019, 2), - 'Neon' : (3000,), - 'Sodium' : (MAX_SIZE - 98, 0), - 'Magnesium' : (MAX_SIZE - 97, 0), - 'Aluminium' : (MAX_SIZE - 96, 0), - 'Silicon' : (MAX_SIZE - 95, 0), - 'Phosphorus' : (MAX_SIZE - 94, 0), + "Hydrogen": (2014, 1), + "Helium": (2014, 7), + "Lithium": (2015, 5), + "Beryllium": (2015, 8), + "Boron": (2016, 3), + "Carbon": (2016, 11), + "Nitrogen": (2017, 7), + "Oxygen": (2018, 3), + "Fluorine": (2019, 2), + "Neon": (3000,), + "Sodium": (MAX_SIZE - 98, 0), + "Magnesium": (MAX_SIZE - 97, 0), + "Aluminium": (MAX_SIZE - 96, 0), + "Silicon": (MAX_SIZE - 95, 0), + "Phosphorus": (MAX_SIZE - 94, 0), # pylint: disable=E8265 #'Sulfur' : (MAX_SIZE - 93, 0), #'Chlorine' : (MAX_SIZE - 92, 0), @@ -218,15 +230,17 @@ class SaltStackVersion(object): VNAMES = dict((v, k) for (k, v) in iter(NAMES.items())) RMATCH = dict((v[:2], k) for (k, v) in iter(NAMES.items())) - def __init__(self, # pylint: disable=C0103 - major, - minor=None, - bugfix=None, - mbugfix=0, - pre_type=None, - pre_num=None, - noc=0, - sha=None): + def __init__( + self, # pylint: disable=C0103 + major, + minor=None, + bugfix=None, + mbugfix=0, + pre_type=None, + pre_num=None, + noc=0, + sha=None, + ): if isinstance(major, string_types): major = int(major) @@ -241,7 +255,10 @@ class SaltStackVersion(object): if bugfix is None and not self.new_version(major=major): bugfix = 0 elif isinstance(bugfix, string_types): - bugfix = int(bugfix) + if not bugfix: + bugfix = None + else: + bugfix = int(bugfix) if mbugfix is None: mbugfix = 0 @@ -249,7 +266,7 @@ class SaltStackVersion(object): mbugfix = int(mbugfix) if pre_type is None: - pre_type = '' + pre_type = "" if pre_num is None: pre_num = 0 elif isinstance(pre_num, string_types): @@ -257,7 +274,7 @@ class SaltStackVersion(object): if noc is None: noc = 0 - elif isinstance(noc, string_types) and noc == 'n/a': + elif isinstance(noc, string_types) and noc == "n/a": noc = -1 elif isinstance(noc, string_types): noc = int(noc) @@ -269,42 +286,50 @@ class SaltStackVersion(object): self.pre_type = pre_type self.pre_num = pre_num self.name = self.VNAMES.get((major, minor), None) + if self.new_version(major): + self.name = self.VNAMES.get((major,), None) self.noc = noc self.sha = sha def new_version(self, major): - ''' + """ determine if using new versioning scheme - ''' + """ return bool(int(major) >= 3000 and int(major) < VERSION_LIMIT) @classmethod def parse(cls, version_string): if version_string.lower() in cls.LNAMES: return cls.from_name(version_string) - vstr = version_string.decode() if isinstance(version_string, bytes) else version_string + vstr = ( + version_string.decode() + if isinstance(version_string, bytes) + else version_string + ) match = cls.git_describe_regex.match(vstr) if not match: raise ValueError( - 'Unable to parse version string: \'{0}\''.format(version_string) + "Unable to parse version string: '{0}'".format(version_string) ) return cls(*match.groups()) @classmethod def from_name(cls, name): if name.lower() not in cls.LNAMES: - raise ValueError( - 'Named version \'{0}\' is not known'.format(name) - ) + raise ValueError("Named version '{0}' is not known".format(name)) return cls(*cls.LNAMES[name.lower()]) @classmethod def from_last_named_version(cls): return cls.from_name( cls.VNAMES[ - max([version_info for version_info in - cls.VNAMES if - version_info[0] < (VERSION_LIMIT)]) + max( + [ + version_info + for version_info in cls.VNAMES + if version_info[0] < (VERSION_LIMIT) + ] + ) ] ) @@ -312,9 +337,13 @@ class SaltStackVersion(object): def next_release(cls): return cls.from_name( cls.VNAMES[ - min([version_info for version_info in - cls.VNAMES if - version_info > cls.from_last_named_version().info]) + min( + [ + version_info + for version_info in cls.VNAMES + if version_info > cls.from_last_named_version().info + ] + ) ] ) @@ -329,9 +358,7 @@ class SaltStackVersion(object): if self.minor: info.append(self.minor) else: - info.extend([self.minor, - self.bugfix, - self.mbugfix]) + info.extend([self.minor, self.bugfix, self.mbugfix]) return info @property @@ -341,48 +368,57 @@ class SaltStackVersion(object): @property def pre_info(self): info = self.min_info() - info.extend([self.pre_type, - self.pre_num]) + info.extend([self.pre_type, self.pre_num]) return tuple(info) @property def noc_info(self): info = self.min_info() - info.extend([self.pre_type, - self.pre_num, - self.noc]) + info.extend([self.pre_type, self.pre_num, self.noc]) return tuple(info) @property def full_info(self): info = self.min_info() - info.extend([self.pre_type, - self.pre_num, - self.noc, - self.sha]) + info.extend([self.pre_type, self.pre_num, self.noc, self.sha]) + return tuple(info) + + @property + def full_info_all_versions(self): + """ + Return the full info regardless + of which versioning scheme we + are using. + """ + info = [ + self.major, + self.minor, + self.bugfix, + self.mbugfix, + self.pre_type, + self.pre_num, + self.noc, + self.sha, + ] return tuple(info) @property def string(self): if self.new_version(self.major): - version_string = '{0}'.format(self.major) + version_string = "{0}".format(self.major) if self.minor: - version_string = '{0}.{1}'.format(self.major, self.minor) + version_string = "{0}.{1}".format(self.major, self.minor) else: - version_string = '{0}.{1}.{2}'.format( - self.major, - self.minor, - self.bugfix - ) + version_string = "{0}.{1}.{2}".format(self.major, self.minor, self.bugfix) if self.mbugfix: - version_string += '.{0}'.format(self.mbugfix) + version_string += ".{0}".format(self.mbugfix) if self.pre_type: - version_string += '{0}{1}'.format(self.pre_type, self.pre_num) + version_string += "{0}{1}".format(self.pre_type, self.pre_num) if self.noc and self.sha: noc = self.noc if noc < 0: - noc = 'n/a' - version_string += '-{0}-{1}'.format(noc, self.sha) + noc = "n/a" + version_string += "-{0}-{1}".format(noc, self.sha) return version_string @property @@ -390,14 +426,14 @@ class SaltStackVersion(object): if self.name and self.major > 10000: version_string = self.name if self.sse: - version_string += ' Enterprise' - version_string += ' (Unreleased)' + version_string += " Enterprise" + version_string += " (Unreleased)" return version_string version_string = self.string if self.sse: - version_string += ' Enterprise' + version_string += " Enterprise" if (self.major, self.minor) in self.RMATCH: - version_string += ' ({0})'.format(self.RMATCH[(self.major, self.minor)]) + version_string += " ({0})".format(self.RMATCH[(self.major, self.minor)]) return version_string @property @@ -421,9 +457,7 @@ class SaltStackVersion(object): other = SaltStackVersion(*other) else: raise ValueError( - 'Cannot instantiate Version from type \'{0}\''.format( - type(other) - ) + "Cannot instantiate Version from type '{0}'".format(type(other)) ) pre_type = self.pre_index @@ -444,11 +478,11 @@ class SaltStackVersion(object): if self.pre_type and not other.pre_type: # We have pre-release information, the other side doesn't - other_noc_info[other_pre_type] = 'zzzzz' + other_noc_info[other_pre_type] = "zzzzz" if not self.pre_type and other.pre_type: # The other side has pre-release information, we don't - noc_info[pre_type] = 'zzzzz' + noc_info[pre_type] = "zzzzz" return method(tuple(noc_info), tuple(other_noc_info)) @@ -473,25 +507,25 @@ class SaltStackVersion(object): def __repr__(self): parts = [] if self.name: - parts.append('name=\'{0}\''.format(self.name)) - parts.extend([ - 'major={0}'.format(self.major), - 'minor={0}'.format(self.minor), - 'bugfix={0}'.format(self.bugfix) - ]) + parts.append("name='{0}'".format(self.name)) + parts.extend(["major={0}".format(self.major), "minor={0}".format(self.minor)]) + + if self.new_version(self.major): + if not self.minor: + parts.remove("".join([x for x in parts if re.search("^minor*", x)])) + else: + parts.extend(["bugfix={0}".format(self.bugfix)]) + if self.mbugfix: - parts.append('minor-bugfix={0}'.format(self.mbugfix)) + parts.append("minor-bugfix={0}".format(self.mbugfix)) if self.pre_type: - parts.append('{0}={1}'.format(self.pre_type, self.pre_num)) + parts.append("{0}={1}".format(self.pre_type, self.pre_num)) noc = self.noc if noc == -1: - noc = 'n/a' + noc = "n/a" if noc and self.sha: - parts.extend([ - 'noc={0}'.format(noc), - 'sha={0}'.format(self.sha) - ]) - return '<{0} {1}>'.format(self.__class__.__name__, ' '.join(parts)) + parts.extend(["noc={0}".format(noc), "sha={0}".format(self.sha)]) + return "<{0} {1}>".format(self.__class__.__name__, " ".join(parts)) # ----- Hardcoded Salt Codename Version Information -----------------------------------------------------------------> @@ -509,31 +543,37 @@ def __discover_version(saltstack_version): import os import subprocess - if 'SETUP_DIRNAME' in globals(): + if "SETUP_DIRNAME" in globals(): # This is from the exec() call in Salt's setup.py cwd = SETUP_DIRNAME # pylint: disable=E0602 - if not os.path.exists(os.path.join(cwd, '.git')): + if not os.path.exists(os.path.join(cwd, ".git")): # This is not a Salt git checkout!!! Don't even try to parse... return saltstack_version else: cwd = os.path.abspath(os.path.dirname(__file__)) - if not os.path.exists(os.path.join(os.path.dirname(cwd), '.git')): + if not os.path.exists(os.path.join(os.path.dirname(cwd), ".git")): # This is not a Salt git checkout!!! Don't even try to parse... return saltstack_version try: - kwargs = dict( - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - cwd=cwd - ) + kwargs = dict(stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd) - if not sys.platform.startswith('win'): + if not sys.platform.startswith("win"): # Let's not import `salt.utils` for the above check - kwargs['close_fds'] = True + kwargs["close_fds"] = True process = subprocess.Popen( - ['git', 'describe', '--tags', '--first-parent', '--match', 'v[0-9]*', '--always'], **kwargs) + [ + "git", + "describe", + "--tags", + "--first-parent", + "--match", + "v[0-9]*", + "--always", + ], + **kwargs + ) out, err = process.communicate() @@ -541,7 +581,9 @@ def __discover_version(saltstack_version): # The git version running this might not support --first-parent # Revert to old command process = subprocess.Popen( - ['git', 'describe', '--tags', '--match', 'v[0-9]*', '--always'], **kwargs) + ["git", "describe", "--tags", "--match", "v[0-9]*", "--always"], + **kwargs + ) out, err = process.communicate() if six.PY3: out = out.decode() @@ -570,13 +612,14 @@ def __discover_version(saltstack_version): def __get_version(saltstack_version): - ''' + """ If we can get a version provided at installation time or from Git, use that instead, otherwise we carry on. - ''' + """ try: # Try to import the version information provided at install time from salt._version import __saltstack_version__ # pylint: disable=E0611,F0401 + return __saltstack_version__ except ImportError: return __discover_version(saltstack_version) @@ -596,48 +639,46 @@ __version__ = __saltstack_version__.string def salt_information(): - ''' + """ Report version of salt. - ''' - yield 'Salt', __version__ + """ + yield "Salt", __version__ def dependency_information(include_salt_cloud=False): - ''' + """ Report versions of library dependencies. - ''' + """ libs = [ - ('Python', None, sys.version.rsplit('\n')[0].strip()), - ('Jinja2', 'jinja2', '__version__'), - ('M2Crypto', 'M2Crypto', 'version'), - ('msgpack-python', 'msgpack', 'version'), - ('msgpack-pure', 'msgpack_pure', 'version'), - ('pycrypto', 'Crypto', '__version__'), - ('pycryptodome', 'Cryptodome', 'version_info'), - ('PyYAML', 'yaml', '__version__'), - ('PyZMQ', 'zmq', '__version__'), - ('ZMQ', 'zmq', 'zmq_version'), - ('Mako', 'mako', '__version__'), - ('Tornado', 'tornado', 'version'), - ('timelib', 'timelib', 'version'), - ('dateutil', 'dateutil', '__version__'), - ('pygit2', 'pygit2', '__version__'), - ('libgit2', 'pygit2', 'LIBGIT2_VERSION'), - ('smmap', 'smmap', '__version__'), - ('cffi', 'cffi', '__version__'), - ('pycparser', 'pycparser', '__version__'), - ('gitdb', 'gitdb', '__version__'), - ('gitpython', 'git', '__version__'), - ('python-gnupg', 'gnupg', '__version__'), - ('mysql-python', 'MySQLdb', '__version__'), - ('cherrypy', 'cherrypy', '__version__'), - ('docker-py', 'docker', '__version__'), + ("Python", None, sys.version.rsplit("\n")[0].strip()), + ("Jinja2", "jinja2", "__version__"), + ("M2Crypto", "M2Crypto", "version"), + ("msgpack-python", "msgpack", "version"), + ("msgpack-pure", "msgpack_pure", "version"), + ("pycrypto", "Crypto", "__version__"), + ("pycryptodome", "Cryptodome", "version_info"), + ("PyYAML", "yaml", "__version__"), + ("PyZMQ", "zmq", "__version__"), + ("ZMQ", "zmq", "zmq_version"), + ("Mako", "mako", "__version__"), + ("Tornado", "tornado", "version"), + ("timelib", "timelib", "version"), + ("dateutil", "dateutil", "__version__"), + ("pygit2", "pygit2", "__version__"), + ("libgit2", "pygit2", "LIBGIT2_VERSION"), + ("smmap", "smmap", "__version__"), + ("cffi", "cffi", "__version__"), + ("pycparser", "pycparser", "__version__"), + ("gitdb", "gitdb", "__version__"), + ("gitpython", "git", "__version__"), + ("python-gnupg", "gnupg", "__version__"), + ("mysql-python", "MySQLdb", "__version__"), + ("cherrypy", "cherrypy", "__version__"), + ("docker-py", "docker", "__version__"), ] if include_salt_cloud: - libs.append( - ('Apache Libcloud', 'libcloud', '__version__'), - ) + libs.append(("Apache Libcloud", "libcloud", "__version__"),) for name, imp, attr in libs: if imp is None: @@ -649,35 +690,36 @@ def dependency_information(include_salt_cloud=False): if callable(version): version = version() if isinstance(version, (tuple, list)): - version = '.'.join(map(str, version)) + version = ".".join(map(str, version)) yield name, version except Exception: # pylint: disable=broad-except yield name, None def system_information(): - ''' + """ Report system versions. - ''' + """ + def system_version(): - ''' + """ Return host system version. - ''' + """ lin_ver = linux_distribution() mac_ver = platform.mac_ver() win_ver = platform.win32_ver() if lin_ver[0]: - return ' '.join(lin_ver) + return " ".join(lin_ver) elif mac_ver[0]: - if isinstance(mac_ver[1], (tuple, list)) and ''.join(mac_ver[1]): - return ' '.join([mac_ver[0], '.'.join(mac_ver[1]), mac_ver[2]]) + if isinstance(mac_ver[1], (tuple, list)) and "".join(mac_ver[1]): + return " ".join([mac_ver[0], ".".join(mac_ver[1]), mac_ver[2]]) else: - return ' '.join([mac_ver[0], mac_ver[2]]) + return " ".join([mac_ver[0], mac_ver[2]]) elif win_ver[0]: - return ' '.join(win_ver) + return " ".join(win_ver) else: - return '' + return "" if platform.win32_ver()[0]: # Get the version and release info based on the Windows Operating @@ -688,46 +730,46 @@ def system_information(): # Get the product name from the registry hkey = win32con.HKEY_LOCAL_MACHINE - key = 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion' - value_name = 'ProductName' + key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" + value_name = "ProductName" reg_handle = win32api.RegOpenKey(hkey, key) # Returns a tuple of (product_name, value_type) product_name, _ = win32api.RegQueryValueEx(reg_handle, value_name) - version = 'Unknown' - release = '' - if 'Server' in product_name: - for item in product_name.split(' '): + version = "Unknown" + release = "" + if "Server" in product_name: + for item in product_name.split(" "): # If it's all digits, then it's version - if re.match(r'\d+', item): + if re.match(r"\d+", item): version = item # If it starts with R and then numbers, it's the release # ie: R2 - if re.match(r'^R\d+$', item): + if re.match(r"^R\d+$", item): release = item - release = '{0}Server{1}'.format(version, release) + release = "{0}Server{1}".format(version, release) else: - for item in product_name.split(' '): + for item in product_name.split(" "): # If it's a number, decimal number, Thin or Vista, then it's the # version - if re.match(r'^(\d+(\.\d+)?)|Thin|Vista$', item): + if re.match(r"^(\d+(\.\d+)?)|Thin|Vista$", item): version = item release = version _, ver, service_pack, extra = platform.win32_ver() - version = ' '.join([release, ver, service_pack, extra]) + version = " ".join([release, ver, service_pack, extra]) else: version = system_version() release = platform.release() system = [ - ('system', platform.system()), - ('dist', ' '.join(linux_distribution(full_distribution_name=False))), - ('release', release), - ('machine', platform.machine()), - ('version', version), - ('locale', __salt_system_encoding__), + ("system", platform.system()), + ("dist", " ".join(linux_distribution(full_distribution_name=False))), + ("release", release), + ("machine", platform.machine()), + ("version", version), + ("locale", __salt_system_encoding__), ] for name, attr in system: @@ -736,84 +778,46 @@ def system_information(): def versions_information(include_salt_cloud=False): - ''' + """ Report the versions of dependent software. - ''' + """ salt_info = list(salt_information()) lib_info = list(dependency_information(include_salt_cloud)) sys_info = list(system_information()) - return {'Salt Version': dict(salt_info), - 'Dependency Versions': dict(lib_info), - 'System Versions': dict(sys_info)} + return { + "Salt Version": dict(salt_info), + "Dependency Versions": dict(lib_info), + "System Versions": dict(sys_info), + } def versions_report(include_salt_cloud=False): - ''' + """ Yield each version properly formatted for console output. - ''' + """ ver_info = versions_information(include_salt_cloud) - not_installed = 'Not Installed' + not_installed = "Not Installed" ns_pad = len(not_installed) - lib_pad = max(len(name) for name in ver_info['Dependency Versions']) - sys_pad = max(len(name) for name in ver_info['System Versions']) + lib_pad = max(len(name) for name in ver_info["Dependency Versions"]) + sys_pad = max(len(name) for name in ver_info["System Versions"]) padding = max(lib_pad, sys_pad, ns_pad) + 1 - fmt = '{0:>{pad}}: {1}' + fmt = "{0:>{pad}}: {1}" info = [] - for ver_type in ('Salt Version', 'Dependency Versions', 'System Versions'): - info.append('{0}:'.format(ver_type)) + for ver_type in ("Salt Version", "Dependency Versions", "System Versions"): + info.append("{0}:".format(ver_type)) # List dependencies in alphabetical, case insensitive order for name in sorted(ver_info[ver_type], key=lambda x: x.lower()): - ver = fmt.format(name, - ver_info[ver_type][name] or not_installed, - pad=padding) + ver = fmt.format( + name, ver_info[ver_type][name] or not_installed, pad=padding + ) info.append(ver) - info.append(' ') + info.append(" ") for line in info: yield line -def msi_conformant_version(): - ''' - An msi installer uninstalls/replaces a lower "internal version" of itself. - "internal version" is ivMAJOR.ivMINOR.ivBUILD with max values 255.255.65535. - Using the build nr allows continuous integration of the installer. - "Display version" is indipendent and free format: Year.Month.Bugfix as in Salt 2016.11.3. - Calculation of the internal version fields: - ivMAJOR = 'short year' (2 digits). - ivMINOR = 20*(month-1) + Bugfix - Combine Month and Bugfix to free ivBUILD for the build number - This limits Bugfix < 20. - The msi automatically replaces only 19 bugfixes of a month, one must uninstall manually. - ivBUILD = git commit count (noc) - noc for tags is 0, representing the final word, translates to the highest build number (65535). - - Examples: - git checkout Display version Internal version Remark - develop 2016.11.0-742 16.200.742 The develop branch has bugfix 0 - 2016.11 2016.11.2-78 16.202.78 - 2016.11 2016.11.9-88 16.209.88 - 2018.8 2018.3.2-1306 18.42.1306 - v2016.11.0 2016.11.0 16.200.65535 Tags have noc 0 - v2016.11.2 2016.11.2 16.202.65535 - - ''' - short_year = int(six.text_type(__saltstack_version__.major)[2:]) - month = __saltstack_version__.minor - bugfix = __saltstack_version__.bugfix - if bugfix > 19: - bugfix = 19 - noc = __saltstack_version__.noc - if noc == 0: - noc = 65535 - return '{}.{}.{}'.format(short_year, 20*(month-1)+bugfix, noc) - - -if __name__ == '__main__': - if len(sys.argv) == 2 and sys.argv[1] == 'msi': - # Building the msi requires an msi-conformant version - print(msi_conformant_version()) - else: - print(__version__) +if __name__ == "__main__": + print(__version__) diff --git a/salt/wheel/__init__.py b/salt/wheel/__init__.py index 7eeac71678c..9029b6edf2e 100644 --- a/salt/wheel/__init__.py +++ b/salt/wheel/__init__.py @@ -1,27 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Modules used to control the master itself -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import collections # Import salt libs import salt.client.mixins import salt.config import salt.loader +import salt.transport.client import salt.utils.error import salt.utils.zeromq -import salt.transport.client # Import 3rd-party libs from salt.ext import six -class WheelClient(salt.client.mixins.SyncClientMixin, - salt.client.mixins.AsyncClientMixin, object): - ''' +class WheelClient( + salt.client.mixins.SyncClientMixin, salt.client.mixins.AsyncClientMixin, object +): + """ An interface to Salt's wheel modules :ref:`Wheel modules <all-salt.wheel>` interact with various parts of the @@ -40,9 +42,10 @@ class WheelClient(salt.client.mixins.SyncClientMixin, import salt.wheel opts = salt.config.master_config('/etc/salt/master') wheel = salt.wheel.WheelClient(opts) - ''' - client = 'wheel' - tag_prefix = 'wheel' + """ + + client = "wheel" + tag_prefix = "wheel" def __init__(self, opts=None): self.opts = opts @@ -51,40 +54,44 @@ class WheelClient(salt.client.mixins.SyncClientMixin, # TODO: remove/deprecate def call_func(self, fun, **kwargs): - ''' + """ Backwards compatibility - ''' - return self.low(fun, kwargs, print_event=kwargs.get('print_event', True), full_return=kwargs.get('full_return', False)) + """ + return self.low( + fun, + kwargs, + print_event=kwargs.get("print_event", True), + full_return=kwargs.get("full_return", False), + ) # TODO: Inconsistent with runner client-- the runner client's master_call gives # an asynchronous return, unlike this def master_call(self, **kwargs): - ''' + """ Execute a wheel function through the master network interface (eauth). - ''' + """ load = kwargs - load['cmd'] = 'wheel' - interface = self.opts['interface'] - if interface == '0.0.0.0': - interface = '127.0.0.1' - master_uri = 'tcp://{}:{}'.format( + load["cmd"] = "wheel" + interface = self.opts["interface"] + if interface == "0.0.0.0": + interface = "127.0.0.1" + master_uri = "tcp://{}:{}".format( salt.utils.zeromq.ip_bracket(interface), - six.text_type(self.opts['ret_port']) + six.text_type(self.opts["ret_port"]), ) - with salt.transport.client.ReqChannel.factory(self.opts, - crypt='clear', - master_uri=master_uri, - usage='master_call') as channel: + with salt.transport.client.ReqChannel.factory( + self.opts, crypt="clear", master_uri=master_uri, usage="master_call" + ) as channel: ret = channel.send(load) if isinstance(ret, collections.Mapping): - if 'error' in ret: - salt.utils.error.raise_error(**ret['error']) + if "error" in ret: + salt.utils.error.raise_error(**ret["error"]) return ret def cmd_sync(self, low, timeout=None, full_return=False): - ''' + """ Execute a wheel function synchronously; eauth is respected This function requires that :conf_master:`external_auth` is configured @@ -100,13 +107,13 @@ class WheelClient(salt.client.mixins.SyncClientMixin, 'password': 'saltdev', }) {'minions': {'jerry': '5d:f6:79:43:5e:d4:42:3f:57:b8:45:a8:7e:a4:6e:ca'}} - ''' + """ return self.master_call(**low) # TODO: Inconsistent with runner client-- that one uses the master_call function # and runs within the master daemon. Need to pick one... def cmd_async(self, low): - ''' + """ Execute a function asynchronously; eauth is respected This function requires that :conf_master:`external_auth` is configured @@ -122,25 +129,30 @@ class WheelClient(salt.client.mixins.SyncClientMixin, 'password': 'saltdev', }) {'jid': '20131219224744416681', 'tag': 'salt/wheel/20131219224744416681'} - ''' - fun = low.pop('fun') + """ + fun = low.pop("fun") return self.asynchronous(fun, low) - def cmd(self, fun, arg=None, pub_data=None, kwarg=None, print_event=True, full_return=False): # pylint: disable=useless-super-delegation - ''' + def cmd( + self, + fun, + arg=None, + pub_data=None, + kwarg=None, + print_event=True, + full_return=False, + ): # pylint: disable=useless-super-delegation + """ Execute a function .. code-block:: python >>> 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(WheelClient, self).cmd( + fun, arg, pub_data, kwarg, print_event, full_return + ) Wheel = WheelClient # for backward-compat diff --git a/salt/wheel/config.py b/salt/wheel/config.py index a8a93c53e56..8e75ed3e0be 100644 --- a/salt/wheel/config.py +++ b/salt/wheel/config.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Manage the master configuration file -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import python libs @@ -20,32 +20,32 @@ log = logging.getLogger(__name__) def values(): - ''' + """ Return the raw values of the config file - ''' - data = salt.config.master_config(__opts__['conf_file']) + """ + data = salt.config.master_config(__opts__["conf_file"]) return data def apply(key, value): - ''' + """ Set a single key .. note:: This will strip comments from your config file - ''' - path = __opts__['conf_file'] + """ + path = __opts__["conf_file"] if os.path.isdir(path): - path = os.path.join(path, 'master') + path = os.path.join(path, "master") data = values() data[key] = value - with salt.utils.files.fopen(path, 'w+') as fp_: + with salt.utils.files.fopen(path, "w+") as fp_: salt.utils.yaml.safe_dump(data, default_flow_style=False) def update_config(file_name, yaml_contents): - ''' + """ Update master config with ``yaml_contents``. @@ -70,21 +70,22 @@ def update_config(file_name, yaml_contents): 'client': 'wheel', 'eauth': 'pam', } - ''' - file_name = '{0}{1}'.format(file_name, '.conf') - dir_path = os.path.join(__opts__['config_dir'], - os.path.dirname(__opts__['default_include'])) + """ + file_name = "{0}{1}".format(file_name, ".conf") + dir_path = os.path.join( + __opts__["config_dir"], os.path.dirname(__opts__["default_include"]) + ) try: yaml_out = salt.utils.yaml.safe_dump(yaml_contents, default_flow_style=False) if not os.path.exists(dir_path): - log.debug('Creating directory %s', dir_path) + log.debug("Creating directory %s", dir_path) os.makedirs(dir_path, 0o755) file_path = os.path.join(dir_path, file_name) - with salt.utils.files.fopen(file_path, 'w') as fp_: + with salt.utils.files.fopen(file_path, "w") as fp_: fp_.write(yaml_out) - return 'Wrote {0}'.format(file_name) + return "Wrote {0}".format(file_name) except (IOError, OSError, salt.utils.yaml.YAMLError, ValueError) as err: return six.text_type(err) diff --git a/salt/wheel/error.py b/salt/wheel/error.py index 868f1db0456..76f2f77f57a 100644 --- a/salt/wheel/error.py +++ b/salt/wheel/error.py @@ -1,19 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Error generator to enable integration testing of salt wheel error handling -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import python libs - - # Import salt libs import salt.utils.error +# Import python libs -def error(name=None, message=''): - ''' + +def error(name=None, message=""): + """ If name is None Then return empty dict Otherwise raise an exception with __name__ from name, message from message @@ -24,7 +23,7 @@ def error(name=None, message=''): salt-wheel error salt-wheel error.error name="Exception" message="This is an error." - ''' + """ ret = {} if name is not None: salt.utils.error.raise_error(name=name, message=message) diff --git a/salt/wheel/file_roots.py b/salt/wheel/file_roots.py index 02cc8c5b327..a89b98d760e 100644 --- a/salt/wheel/file_roots.py +++ b/salt/wheel/file_roots.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Read in files from the file_root and save files to the file root -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import salt libs @@ -15,34 +16,34 @@ import salt.utils.path from salt.ext import six -def find(path, saltenv='base'): - ''' +def find(path, saltenv="base"): + """ Return a dict of the files located with the given path and environment - ''' + """ # Return a list of paths + text or bin ret = [] - if saltenv not in __opts__['file_roots']: + if saltenv not in __opts__["file_roots"]: return ret - for root in __opts__['file_roots'][saltenv]: + for root in __opts__["file_roots"][saltenv]: full = os.path.join(root, path) if os.path.isfile(full): # Add it to the dict - with salt.utils.files.fopen(full, 'rb') as fp_: + with salt.utils.files.fopen(full, "rb") as fp_: if salt.utils.files.is_text(fp_): - ret.append({full: 'txt'}) + ret.append({full: "txt"}) else: - ret.append({full: 'bin'}) + ret.append({full: "bin"}) return ret -def list_env(saltenv='base'): - ''' +def list_env(saltenv="base"): + """ Return all of the file paths found in an environment - ''' + """ ret = {} - if saltenv not in __opts__['file_roots']: + if saltenv not in __opts__["file_roots"]: return ret - for f_root in __opts__['file_roots'][saltenv]: + for f_root in __opts__["file_roots"][saltenv]: ret[f_root] = {} for root, dirs, files in salt.utils.path.os_walk(f_root): sub = ret[f_root] @@ -61,56 +62,56 @@ def list_env(saltenv='base'): for dir_ in dirs: sub[dir_] = {} for fn_ in files: - sub[fn_] = 'f' + sub[fn_] = "f" return ret def list_roots(): - ''' + """ Return all of the files names in all available environments - ''' + """ ret = {} - for saltenv in __opts__['file_roots']: + for saltenv in __opts__["file_roots"]: ret[saltenv] = [] ret[saltenv].append(list_env(saltenv)) return ret -def read(path, saltenv='base'): - ''' +def read(path, saltenv="base"): + """ Read the contents of a text file, if the file is binary then - ''' + """ # Return a dict of paths + content ret = [] files = find(path, saltenv) for fn_ in files: full = next(six.iterkeys(fn_)) form = fn_[full] - if form == 'txt': - with salt.utils.files.fopen(full, 'rb') as fp_: - ret.append( - {full: salt.utils.stringutils.to_unicode(fp_.read())} - ) + if form == "txt": + with salt.utils.files.fopen(full, "rb") as fp_: + ret.append({full: salt.utils.stringutils.to_unicode(fp_.read())}) return ret -def write(data, path, saltenv='base', index=0): - ''' +def write(data, path, saltenv="base", index=0): + """ Write the named file, by default the first file found is written, but the index of the file can be specified to write to a lower priority file root - ''' - if saltenv not in __opts__['file_roots']: - return 'Named environment {0} is not present'.format(saltenv) - if len(__opts__['file_roots'][saltenv]) <= index: - return 'Specified index {0} in environment {1} is not present'.format( - index, saltenv) + """ + if saltenv not in __opts__["file_roots"]: + return "Named environment {0} is not present".format(saltenv) + if len(__opts__["file_roots"][saltenv]) <= index: + return "Specified index {0} in environment {1} 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) - dest = os.path.join(__opts__['file_roots'][saltenv][index], path) + return ( + "The path passed in {0} is not relative to the environment " "{1}" + ).format(path, saltenv) + dest = os.path.join(__opts__["file_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_: + 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 {0}".format(dest) diff --git a/salt/wheel/key.py b/salt/wheel/key.py index 8ebd47f8988..7eed3ddcb18 100644 --- a/salt/wheel/key.py +++ b/salt/wheel/key.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Wheel system wrapper for the Salt key system to be used in interactions with the Salt Master programmatically. @@ -25,33 +25,34 @@ be called from a Python interpreter. The wheel key functions can also be called via a ``salt`` command at the CLI using the :mod:`saltutil execution module <salt.modules.saltutil>`. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import hashlib import logging +import os -# Import salt libs -from salt.key import get_key import salt.crypt import salt.utils.crypt import salt.utils.files import salt.utils.platform + +# Import salt libs +from salt.key import get_key from salt.utils.sanitizers import clean - __func_alias__ = { - 'list_': 'list', - 'key_str': 'print', + "list_": "list", + "key_str": "print", } log = logging.getLogger(__name__) def list_(match): - ''' + """ List all the keys under a named status. Returns a dictionary. match @@ -64,13 +65,13 @@ def list_(match): >>> wheel.cmd('key.list', ['accepted']) {'minions': ['minion1', 'minion2', 'minion3']} - ''' + """ skey = get_key(__opts__) return skey.list_status(match) def list_all(): - ''' + """ List all the keys. Returns a dictionary containing lists of the minions in each salt-key category, including ``minions``, ``minions_rejected``, ``minions_denied``, etc. Returns a dictionary. @@ -81,21 +82,21 @@ def list_all(): {'local': ['master.pem', 'master.pub'], 'minions_rejected': [], 'minions_denied': [], 'minions_pre': [], 'minions': ['minion1', 'minion2', 'minion3']} - ''' + """ skey = get_key(__opts__) return skey.all_keys() def name_match(match): - ''' + """ List all the keys based on a glob match - ''' + """ skey = get_key(__opts__) return skey.name_match(match) def accept(match, include_rejected=False, include_denied=False): - ''' + """ Accept keys based on a glob match. Returns a dictionary. match @@ -113,13 +114,15 @@ def accept(match, include_rejected=False, include_denied=False): >>> wheel.cmd('key.accept', ['minion1']) {'minions': ['minion1']} - ''' + """ skey = get_key(__opts__) - return skey.accept(match, include_rejected=include_rejected, include_denied=include_denied) + return skey.accept( + match, include_rejected=include_rejected, include_denied=include_denied + ) def accept_dict(match, include_rejected=False, include_denied=False): - ''' + """ Accept keys based on a dict of keys. Returns a dictionary. match @@ -151,15 +154,17 @@ def accept_dict(match, include_rejected=False, include_denied=False): ], }) {'minions': ['jerry', 'stuart', 'bob']} - ''' + """ skey = get_key(__opts__) - return skey.accept(match_dict=match, - include_rejected=include_rejected, - include_denied=include_denied) + return skey.accept( + match_dict=match, + include_rejected=include_rejected, + include_denied=include_denied, + ) def delete(match): - ''' + """ Delete keys based on a glob match. Returns a dictionary. match @@ -169,13 +174,13 @@ def delete(match): >>> wheel.cmd_async({'fun': 'key.delete', 'match': 'minion1'}) {'jid': '20160826201244808521', 'tag': 'salt/wheel/20160826201244808521'} - ''' + """ skey = get_key(__opts__) return skey.delete_key(match) def delete_dict(match): - ''' + """ Delete keys based on a dict of keys. Returns a dictionary. match @@ -190,15 +195,15 @@ def delete_dict(match): 'stuart', 'bob', ], - }) + }}) {'jid': '20160826201244808521', 'tag': 'salt/wheel/20160826201244808521'} - ''' + """ skey = get_key(__opts__) return skey.delete_key(match_dict=match) def reject(match, include_accepted=False, include_denied=False): - ''' + """ Reject keys based on a glob match. Returns a dictionary. match @@ -216,13 +221,15 @@ def reject(match, include_accepted=False, include_denied=False): >>> wheel.cmd_async({'fun': 'key.reject', 'match': 'minion1'}) {'jid': '20160826201244808521', 'tag': 'salt/wheel/20160826201244808521'} - ''' + """ skey = get_key(__opts__) - return skey.reject(match, include_accepted=include_accepted, include_denied=include_denied) + return skey.reject( + match, include_accepted=include_accepted, include_denied=include_denied + ) def reject_dict(match, include_accepted=False, include_denied=False): - ''' + """ Reject keys based on a dict of keys. Returns a dictionary. match @@ -249,17 +256,19 @@ def reject_dict(match, include_accepted=False, include_denied=False): 'stuart', 'bob', ], - }) + }}) {'jid': '20160826201244808521', 'tag': 'salt/wheel/20160826201244808521'} - ''' + """ skey = get_key(__opts__) - return skey.reject(match_dict=match, - include_accepted=include_accepted, - include_denied=include_denied) + return skey.reject( + match_dict=match, + include_accepted=include_accepted, + include_denied=include_denied, + ) def key_str(match): - r''' + r""" Return information about the key. Returns a dictionary. match @@ -271,13 +280,31 @@ def key_str(match): {'minions': {'minion1': '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0B ... TWugEQpPt\niQIDAQAB\n-----END PUBLIC KEY-----'}} - ''' + """ skey = get_key(__opts__) return skey.key_str(match) +def master_key_str(): + r""" + Returns master's public key. Returns a dictionary + + .. code-block:: python + + >>> wheel.cmd('key.master_key_str') + {'local': {'master.pub': '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0B + ... + TWugEQpPt\niQIDAQAB\n-----END PUBLIC KEY-----'}} + """ + keyname = "master.pub" + path_to_pubkey = os.path.join(__opts__["pki_dir"], keyname) + with salt.utils.files.fopen(path_to_pubkey, "r") as fp_: + keyvalue = salt.utils.stringutils.to_unicode(fp_.read()) + return {"local": {keyname: keyvalue}} + + def finger(match, hash_type=None): - ''' + """ Return the matching key fingerprints. Returns a dictionary. match @@ -291,16 +318,16 @@ def finger(match, hash_type=None): >>> wheel.cmd('key.finger', ['minion1']) {'minions': {'minion1': '5d:f6:79:43:5e:d4:42:3f:57:b8:45:a8:7e:a4:6e:ca'}} - ''' + """ if hash_type is None: - hash_type = __opts__['hash_type'] + hash_type = __opts__["hash_type"] skey = get_key(__opts__) return skey.finger(match, hash_type) def finger_master(hash_type=None): - ''' + """ Return the fingerprint of the master's public key hash_type @@ -310,18 +337,19 @@ def finger_master(hash_type=None): >>> wheel.cmd('key.finger_master') {'local': {'master.pub': '5d:f6:79:43:5e:d4:42:3f:57:b8:45:a8:7e:a4:6e:ca'}} - ''' - keyname = 'master.pub' + """ + keyname = "master.pub" if hash_type is None: - hash_type = __opts__['hash_type'] + hash_type = __opts__["hash_type"] fingerprint = salt.utils.crypt.pem_finger( - os.path.join(__opts__['pki_dir'], keyname), sum_type=hash_type) - return {'local': {keyname: fingerprint}} + os.path.join(__opts__["pki_dir"], keyname), sum_type=hash_type + ) + return {"local": {keyname: fingerprint}} def gen(id_=None, keysize=2048): - r''' + r""" Generate a key pair. No keys are stored on the master. A key pair is returned as a dict containing pub and priv keys. Returns a dictionary containing the the ``pub`` and ``priv`` keys with their generated values. @@ -347,19 +375,18 @@ def gen(id_=None, keysize=2048): QH3/W74X1+WTBlx4R2KGLYBiH+bCCFEQ/Zvcu4Xp4bIOPtRKozEQ==\n -----END RSA PRIVATE KEY-----'} - ''' + """ if id_ is None: id_ = hashlib.sha512(os.urandom(32)).hexdigest() else: id_ = clean.filename(id_) - ret = {'priv': '', - 'pub': ''} - priv = salt.crypt.gen_keys(__opts__['pki_dir'], id_, keysize) - pub = '{0}.pub'.format(priv[:priv.rindex('.')]) + ret = {"priv": "", "pub": ""} + priv = salt.crypt.gen_keys(__opts__["pki_dir"], id_, keysize) + pub = "{0}.pub".format(priv[: priv.rindex(".")]) with salt.utils.files.fopen(priv) as fp_: - ret['priv'] = salt.utils.stringutils.to_unicode(fp_.read()) + ret["priv"] = salt.utils.stringutils.to_unicode(fp_.read()) with salt.utils.files.fopen(pub) as fp_: - ret['pub'] = salt.utils.stringutils.to_unicode(fp_.read()) + ret["pub"] = salt.utils.stringutils.to_unicode(fp_.read()) # The priv key is given the Read-Only attribute. The causes `os.remove` to # fail in Windows. @@ -372,7 +399,7 @@ def gen(id_=None, keysize=2048): def gen_accept(id_, keysize=2048, force=False): - r''' + r""" Generate a key pair then accept the public key. This function returns the key pair in a dict, only the public key is preserved on the master. Returns a dictionary. @@ -410,28 +437,28 @@ def gen_accept(id_, keysize=2048, force=False): >>> wheel.cmd('key.list', ['accepted']) {'minions': ['foo', 'minion1', 'minion2', 'minion3']} - ''' + """ id_ = clean.id(id_) ret = gen(id_, keysize) - acc_path = os.path.join(__opts__['pki_dir'], 'minions', id_) + acc_path = os.path.join(__opts__["pki_dir"], "minions", id_) if os.path.isfile(acc_path) and not force: return {} - with salt.utils.files.fopen(acc_path, 'w+') as fp_: - fp_.write(salt.utils.stringutils.to_str(ret['pub'])) + with salt.utils.files.fopen(acc_path, "w+") as fp_: + fp_.write(salt.utils.stringutils.to_str(ret["pub"])) return ret def gen_keys(keydir=None, keyname=None, keysize=None, user=None): - ''' + """ Generate minion RSA public keypair - ''' + """ skey = get_key(__opts__) return skey.gen_keys(keydir, keyname, keysize, user) def gen_signature(priv, pub, signature_path, auto_create=False, keysize=None): - ''' + """ Generate master public-key-signature - ''' + """ skey = get_key(__opts__) return skey.gen_keys_signature(priv, pub, signature_path, auto_create, keysize) diff --git a/salt/wheel/minions.py b/salt/wheel/minions.py index a8c1d409341..c33019429d0 100644 --- a/salt/wheel/minions.py +++ b/salt/wheel/minions.py @@ -1,24 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" Wheel system wrapper for connected minions -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt libs -from salt.utils.cache import CacheCli import salt.config import salt.utils.minions +# Import Salt libs +from salt.utils.cache import CacheCli + def connected(): - ''' + """ List all connected minions on a salt-master - ''' - opts = salt.config.master_config(__opts__['conf_file']) + """ + opts = salt.config.master_config(__opts__["conf_file"]) - if opts.get('con_cache'): + if opts.get("con_cache"): cache_cli = CacheCli(opts) minions = cache_cli.get_cached() else: diff --git a/salt/wheel/pillar_roots.py b/salt/wheel/pillar_roots.py index bb24f92f7ba..2c242ef3a7e 100644 --- a/salt/wheel/pillar_roots.py +++ b/salt/wheel/pillar_roots.py @@ -1,11 +1,12 @@ # -*- 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 @@ -16,34 +17,34 @@ import salt.utils.path from salt.ext import six -def find(path, saltenv='base'): - ''' +def find(path, saltenv="base"): + """ Return a dict of the files located with the given path and environment - ''' + """ # Return a list of paths + text or bin ret = [] - if saltenv not in __opts__['pillar_roots']: + if saltenv not in __opts__["pillar_roots"]: return ret - for root in __opts__['pillar_roots'][saltenv]: + for root in __opts__["pillar_roots"][saltenv]: full = os.path.join(root, path) if os.path.isfile(full): # Add it to the dict - with salt.utils.files.fopen(full, 'rb') as fp_: + with salt.utils.files.fopen(full, "rb") as fp_: if salt.utils.files.is_text(fp_): - ret.append({full: 'txt'}) + ret.append({full: "txt"}) else: - ret.append({full: 'bin'}) + ret.append({full: "bin"}) return ret -def list_env(saltenv='base'): - ''' +def list_env(saltenv="base"): + """ Return all of the file paths found in an environment - ''' + """ ret = {} - if saltenv not in __opts__['pillar_roots']: + if saltenv not in __opts__["pillar_roots"]: return ret - for f_root in __opts__['pillar_roots'][saltenv]: + for f_root in __opts__["pillar_roots"][saltenv]: ret[f_root] = {} for root, dirs, files in salt.utils.path.os_walk(f_root): sub = ret[f_root] @@ -62,56 +63,56 @@ def list_env(saltenv='base'): for dir_ in dirs: sub[dir_] = {} for fn_ in files: - sub[fn_] = 'f' + sub[fn_] = "f" return ret def list_roots(): - ''' + """ Return all of the files names in all available environments - ''' + """ ret = {} - for saltenv in __opts__['pillar_roots']: + for saltenv in __opts__["pillar_roots"]: ret[saltenv] = [] ret[saltenv].append(list_env(saltenv)) return ret -def read(path, saltenv='base'): - ''' +def read(path, saltenv="base"): + """ Read the contents of a text file, if the file is binary then - ''' + """ # Return a dict of paths + content ret = [] files = find(path, saltenv) for fn_ in files: full = next(six.iterkeys(fn_)) form = fn_[full] - if form == 'txt': - with salt.utils.files.fopen(full, 'rb') as fp_: - ret.append( - {full: salt.utils.stringutils.to_unicode(fp_.read())} - ) + if form == "txt": + with salt.utils.files.fopen(full, "rb") as fp_: + ret.append({full: salt.utils.stringutils.to_unicode(fp_.read())}) return ret -def write(data, path, saltenv='base', index=0): - ''' +def write(data, path, saltenv="base", index=0): + """ Write the named file, by default the first file found is written, but the 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) - if len(__opts__['pillar_roots'][saltenv]) <= index: - return 'Specified index {0} in environment {1} is not present'.format( - index, saltenv) + """ + if saltenv not in __opts__["pillar_roots"]: + return "Named environment {0} is not present".format(saltenv) + if len(__opts__["pillar_roots"][saltenv]) <= index: + return "Specified index {0} in environment {1} 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) - dest = os.path.join(__opts__['pillar_roots'][saltenv][index], path) + return ( + "The path passed in {0} is not relative to the environment " "{1}" + ).format(path, saltenv) + 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_: + 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 {0}".format(dest) diff --git a/scripts/salt b/scripts/salt index 1a6d6b4d0c6..eb4cce9a2da 100755 --- a/scripts/salt +++ b/scripts/salt @@ -1,10 +1,9 @@ #!/usr/bin/env python -''' +""" Publish commands to the salt system from the command line on the master. -''' +""" from salt.scripts import salt_main - -if __name__ == '__main__': +if __name__ == "__main__": salt_main() diff --git a/scripts/salt-api b/scripts/salt-api index 6f028bacb61..01b02cd1ff3 100755 --- a/scripts/salt-api +++ b/scripts/salt-api @@ -3,6 +3,5 @@ # Import salt libs from salt.scripts import salt_api - -if __name__ == '__main__': +if __name__ == "__main__": salt_api() diff --git a/scripts/salt-call b/scripts/salt-call index 5b8a8f39355..282964bc1cc 100755 --- a/scripts/salt-call +++ b/scripts/salt-call @@ -1,11 +1,10 @@ #!/usr/bin/env python -''' +""" Directly call a salt command in the modules, does not require a running salt minion to run. -''' +""" from salt.scripts import salt_call - -if __name__ == '__main__': +if __name__ == "__main__": salt_call() diff --git a/scripts/salt-cloud b/scripts/salt-cloud index e977b68179d..cc5d9dbb879 100755 --- a/scripts/salt-cloud +++ b/scripts/salt-cloud @@ -1,10 +1,9 @@ #!/usr/bin/env python -''' +""" Publish commands to the salt system from the command line on the master. -''' +""" from salt.scripts import salt_cloud - -if __name__ == '__main__': +if __name__ == "__main__": salt_cloud() diff --git a/scripts/salt-cp b/scripts/salt-cp index 3da660d137b..ab56628ed58 100755 --- a/scripts/salt-cp +++ b/scripts/salt-cp @@ -1,10 +1,9 @@ #!/usr/bin/env python -''' +""" Publish commands to the salt system from the command line on the master. -''' +""" from salt.scripts import salt_cp - -if __name__ == '__main__': +if __name__ == "__main__": salt_cp() diff --git a/scripts/salt-extend b/scripts/salt-extend index 5385a7b6a9f..bc0603ecb5f 100755 --- a/scripts/salt-extend +++ b/scripts/salt-extend @@ -1,5 +1,5 @@ #!/usr/bin/env python -''' +""" Quickstart for creating an/or extending the functionality of your SaltStack installation usage: salt-extend [-h] [--extension EXTENSION] @@ -18,42 +18,43 @@ Quickstart for creating an/or extending the functionality of your SaltStack inst Short description of what the module does. --no-merge Don't merge the module into the salt directory, keep in a temp location -''' +""" import logging -from salt.scripts import salt_extend import sys +from salt.scripts import salt_extend + PY26 = sys.version_info[0] == 2 and sys.version_info[1] == 6 def _parse_args_argparse(): import argparse - parser = argparse.ArgumentParser(description="Quickly boilerplate an extension to SaltStack") + parser = argparse.ArgumentParser( + description="Quickly boilerplate an extension to SaltStack" + ) parser.add_argument( - "--extension", - "-e", - help="Extension type, e.g. 'module', 'state'.") + "--extension", "-e", help="Extension type, e.g. 'module', 'state'." + ) parser.add_argument( "--salt-directory", "-o", - help="Directory where your salt installation is kept (defaults to .).") + help="Directory where your salt installation is kept (defaults to .).", + ) + parser.add_argument("--name", "-n", help="Module name.") parser.add_argument( - "--name", - "-n", - help="Module name.") - parser.add_argument( - "--description", - "-d", - help="Short description of what the module does.") + "--description", "-d", help="Short description of what the module does." + ) parser.add_argument( "--no-merge", help="Don't merge the module into the salt directory, keep in a temp location", - action="store_true") + action="store_true", + ) parser.add_argument( "--debug", help="Display detailed logs whilst applying templates", - action="store_true") + action="store_true", + ) return parser.parse_args() @@ -62,33 +63,31 @@ def _parse_args_optparse(): parser = OptionParser(usage="Quickly boilerplate an extension to SaltStack") parser.add_option( - "--extension", - "-e", - help="Extension type, e.g. 'module', 'state'.") + "--extension", "-e", help="Extension type, e.g. 'module', 'state'." + ) parser.add_option( "--salt-directory", "-o", - help="Directory where your salt installation is kept (defaults to .).") + help="Directory where your salt installation is kept (defaults to .).", + ) + parser.add_option("--name", "-n", help="Module name.") parser.add_option( - "--name", - "-n", - help="Module name.") - parser.add_option( - "--description", - "-d", - help="Short description of what the module does.") + "--description", "-d", help="Short description of what the module does." + ) parser.add_option( "--no-merge", help="Don't merge the module into the salt directory, keep in a temp location", - action="store_true") + action="store_true", + ) parser.add_option( "--debug", help="Display detailed logs whilst applying templates", - action="store_true") + action="store_true", + ) return parser.parse_args() -if __name__ == '__main__': +if __name__ == "__main__": if PY26: (args, _) = _parse_args_optparse() else: @@ -100,4 +99,5 @@ if __name__ == '__main__': name=args.name, description=args.description, salt_dir=args.salt_directory, - merge=not args.no_merge) + merge=not args.no_merge, + ) diff --git a/scripts/salt-key b/scripts/salt-key index cb26dfd5a69..bc61f77dfdf 100755 --- a/scripts/salt-key +++ b/scripts/salt-key @@ -1,10 +1,9 @@ #!/usr/bin/env python -''' +""" Manage the authentication keys with salt-key -''' +""" from salt.scripts import salt_key - -if __name__ == '__main__': +if __name__ == "__main__": salt_key() diff --git a/scripts/salt-master b/scripts/salt-master index b4323bd5326..6bcdc55324d 100755 --- a/scripts/salt-master +++ b/scripts/salt-master @@ -1,13 +1,12 @@ #!/usr/bin/env python -''' +""" Start the salt-master -''' +""" import salt.utils.platform from salt.scripts import salt_master - -if __name__ == '__main__': +if __name__ == "__main__": if salt.utils.platform.is_windows(): # Since this file does not have a '.py' extension, when running on # Windows, spawning any addional processes will fail due to Python @@ -16,7 +15,8 @@ if __name__ == '__main__': # spawned process to load this 'module' and proceed. import os.path import py_compile - cfile = os.path.splitext(__file__)[0] + '.pyc' + + cfile = os.path.splitext(__file__)[0] + ".pyc" if not os.path.exists(cfile): py_compile.compile(__file__, cfile) salt_master() diff --git a/scripts/salt-minion b/scripts/salt-minion index 74a9fa1d676..1e4ecdf66e9 100755 --- a/scripts/salt-minion +++ b/scripts/salt-minion @@ -1,14 +1,14 @@ #!/usr/bin/env python -''' +""" This script is used to kick off a salt minion daemon -''' +""" + +from multiprocessing import freeze_support import salt.utils.platform from salt.scripts import salt_minion -from multiprocessing import freeze_support - -if __name__ == '__main__': +if __name__ == "__main__": if salt.utils.platform.is_windows(): # Since this file does not have a '.py' extension, when running on # Windows, spawning any addional processes will fail due to Python @@ -17,7 +17,8 @@ if __name__ == '__main__': # spawned process to load this 'module' and proceed. import os.path import py_compile - cfile = os.path.splitext(__file__)[0] + '.pyc' + + cfile = os.path.splitext(__file__)[0] + ".pyc" if not os.path.exists(cfile): py_compile.compile(__file__, cfile) # This handles the bootstrapping code that is included with frozen diff --git a/scripts/salt-proxy b/scripts/salt-proxy index e921e6590bd..a8cf27f041c 100755 --- a/scripts/salt-proxy +++ b/scripts/salt-proxy @@ -1,16 +1,17 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -''' +""" This script is used to kick off a salt proxy minion daemon -''' +""" from __future__ import absolute_import -import salt.utils.platform -from salt.scripts import salt_proxy + from multiprocessing import freeze_support +import salt.utils.platform +from salt.scripts import salt_proxy -if __name__ == '__main__': +if __name__ == "__main__": if salt.utils.platform.is_windows(): # Since this file does not have a '.py' extension, when running on # Windows, spawning any addional processes will fail due to Python @@ -19,7 +20,8 @@ if __name__ == '__main__': # spawned process to load this 'module' and proceed. import os.path import py_compile - cfile = os.path.splitext(__file__)[0] + '.pyc' + + cfile = os.path.splitext(__file__)[0] + ".pyc" if not os.path.exists(cfile): py_compile.compile(__file__, cfile) # This handles the bootstrapping code that is included with frozen diff --git a/scripts/salt-run b/scripts/salt-run index 2b2d86554c3..82686823a54 100755 --- a/scripts/salt-run +++ b/scripts/salt-run @@ -1,10 +1,9 @@ #!/usr/bin/env python -''' +""" Execute a salt convenience routine -''' +""" from salt.scripts import salt_run - -if __name__ == '__main__': +if __name__ == "__main__": salt_run() diff --git a/scripts/salt-ssh b/scripts/salt-ssh index 4f21baa5f40..e6dbc54d1fc 100755 --- a/scripts/salt-ssh +++ b/scripts/salt-ssh @@ -1,10 +1,9 @@ #!/usr/bin/env python -''' +""" Execute the salt ssh system -''' +""" from salt.scripts import salt_ssh - -if __name__ == '__main__': +if __name__ == "__main__": salt_ssh() diff --git a/scripts/salt-syndic b/scripts/salt-syndic index d6faf2df63c..413ae28915d 100755 --- a/scripts/salt-syndic +++ b/scripts/salt-syndic @@ -1,10 +1,9 @@ #!/usr/bin/env python -''' +""" This script is used to kick off a salt syndic daemon -''' +""" from salt.scripts import salt_syndic - -if __name__ == '__main__': +if __name__ == "__main__": salt_syndic() diff --git a/scripts/salt-unity b/scripts/salt-unity index b0a2d315979..88d8f09b0e3 100755 --- a/scripts/salt-unity +++ b/scripts/salt-unity @@ -2,6 +2,5 @@ from salt.scripts import salt_unity - -if __name__ == '__main__': +if __name__ == "__main__": salt_unity() diff --git a/scripts/spm b/scripts/spm index 027864ee112..216fe664f81 100755 --- a/scripts/spm +++ b/scripts/spm @@ -1,12 +1,11 @@ #!/usr/bin/env python -''' +""" Publish commands to the salt system from the command line on the master. .. versionadded:: 2015.8.0 -''' +""" from salt.scripts import salt_spm - -if __name__ == '__main__': +if __name__ == "__main__": salt_spm() diff --git a/scripts/suse/yum/plugins/yumnotify.py b/scripts/suse/yum/plugins/yumnotify.py index dd2485c8866..f715bb66976 100644 --- a/scripts/suse/yum/plugins/yumnotify.py +++ b/scripts/suse/yum/plugins/yumnotify.py @@ -3,15 +3,16 @@ # # Author: Bo Maryniuk <bo@suse.de> -from yum.plugins import TYPE_CORE -from yum import config -import os import hashlib +import os + +from yum import config +from yum.plugins import TYPE_CORE CK_PATH = "/var/cache/salt/minion/rpmdb.cookie" RPM_PATH = "/var/lib/rpm/Packages" -requires_api_version = '2.5' +requires_api_version = "2.5" plugin_type = TYPE_CORE @@ -50,6 +51,8 @@ def posttrans_hook(conduit): :return: """ # Integrate Yum with Salt - if 'SALT_RUNNING' not in os.environ: - with open(CK_PATH, 'w') as ck_fh: - ck_fh.write('{chksum} {mtime}\n'.format(chksum=_get_checksum(), mtime=_get_mtime())) + if "SALT_RUNNING" not in os.environ: + with open(CK_PATH, "w") as ck_fh: + ck_fh.write( + "{chksum} {mtime}\n".format(chksum=_get_checksum(), mtime=_get_mtime()) + ) diff --git a/scripts/suse/zypper/plugins/commit/zyppnotify b/scripts/suse/zypper/plugins/commit/zyppnotify index b64badb1197..51ac02254e7 100755 --- a/scripts/suse/zypper/plugins/commit/zyppnotify +++ b/scripts/suse/zypper/plugins/commit/zyppnotify @@ -5,9 +5,9 @@ # # Author: Bo Maryniuk <bo@suse.de> -import sys -import os import hashlib +import os +import sys from zypp_plugin import Plugin @@ -16,25 +16,28 @@ class DriftDetector(Plugin): """ Return diff of the installed packages outside the Salt. """ + def __init__(self): Plugin.__init__(self) self.ck_path = "/var/cache/salt/minion/rpmdb.cookie" self.rpm_path = "/var/lib/rpm/Packages" def _get_mtime(self): - ''' + """ Get the modified time of the RPM Database. Returns: Unix ticks - ''' - return os.path.exists(self.rpm_path) and int(os.path.getmtime(self.rpm_path)) or 0 + """ + return ( + os.path.exists(self.rpm_path) and int(os.path.getmtime(self.rpm_path)) or 0 + ) def _get_checksum(self): - ''' + """ Get the checksum of the RPM Database. Returns: hexdigest - ''' + """ digest = hashlib.sha256() with open(self.rpm_path, "rb") as rpm_db_fh: while True: @@ -49,9 +52,13 @@ class DriftDetector(Plugin): """ Hook when plugin closes Zypper's transaction. """ - if 'SALT_RUNNING' not in os.environ: - with open(self.ck_path, 'w') as ck_fh: - ck_fh.write('{chksum} {mtime}\n'.format(chksum=self._get_checksum(), mtime=self._get_mtime())) + if "SALT_RUNNING" not in os.environ: + with open(self.ck_path, "w") as ck_fh: + ck_fh.write( + "{chksum} {mtime}\n".format( + chksum=self._get_checksum(), mtime=self._get_mtime() + ) + ) self.ack() diff --git a/setup.py b/setup.py index cde28884835..c77695e27c4 100755 --- a/setup.py +++ b/setup.py @@ -1,8 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -''' +""" The setup script for salt -''' +""" # pylint: disable=file-perms,ungrouped-imports,wrong-import-order,wrong-import-position,repr-flag-used-in-string # pylint: disable=3rd-party-local-module-not-gated,resource-leakage,blacklisted-module @@ -11,43 +11,54 @@ The setup script for salt # For Python 2.5. A no-op on 2.6 and above. from __future__ import absolute_import, print_function, with_statement -import os -import sys +import distutils.dist import glob import inspect import operator +import os import platform +import sys +from ctypes.util import find_library +from datetime import datetime +from distutils import log +from distutils.cmd import Command +from distutils.command.build import build +from distutils.command.clean import clean +from distutils.command.install_lib import install_lib +from distutils.errors import DistutilsArgError +from distutils.version import LooseVersion # pylint: disable=blacklisted-module + +# pylint: disable=E0611 +import setuptools +from setuptools import setup +from setuptools.command.develop import develop +from setuptools.command.egg_info import egg_info +from setuptools.command.install import install +from setuptools.command.sdist import sdist + try: from urllib2 import urlopen except ImportError: from urllib.request import urlopen # pylint: disable=no-name-in-module -from datetime import datetime -# pylint: disable=E0611 -import setuptools -import distutils.dist -from distutils import log -from distutils.cmd import Command -from distutils.errors import DistutilsArgError -from distutils.command.build import build -from distutils.command.clean import clean -from distutils.command.install_lib import install_lib -from distutils.version import LooseVersion # pylint: disable=blacklisted-module -from ctypes.util import find_library -from setuptools import setup -from setuptools.command.develop import develop -from setuptools.command.install import install -from setuptools.command.sdist import sdist -from setuptools.command.egg_info import egg_info + + +try: + from wheel.bdist_wheel import bdist_wheel + + HAS_BDIST_WHEEL = True +except ImportError: + HAS_BDIST_WHEEL = False # pylint: enable=E0611 try: import zmq + HAS_ZMQ = True except ImportError: HAS_ZMQ = False try: - DATE = datetime.utcfromtimestamp(int(os.environ['SOURCE_DATE_EPOCH'])) + DATE = datetime.utcfromtimestamp(int(os.environ["SOURCE_DATE_EPOCH"])) except (KeyError, ValueError): DATE = datetime.utcnow() @@ -59,7 +70,7 @@ except NameError: # Let's work around that SETUP_DIRNAME = os.path.dirname(sys.argv[0]) -if SETUP_DIRNAME != '': +if SETUP_DIRNAME != "": os.chdir(SETUP_DIRNAME) SETUP_DIRNAME = os.path.abspath(SETUP_DIRNAME) @@ -67,20 +78,22 @@ SETUP_DIRNAME = os.path.abspath(SETUP_DIRNAME) BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION = os.environ.get( # The user can provide a different bootstrap-script version. # ATTENTION: A tag for that version MUST exist - 'BOOTSTRAP_SCRIPT_VERSION', + "BOOTSTRAP_SCRIPT_VERSION", # If no bootstrap-script version was provided from the environment, let's # provide the one we define. - 'v2014.06.21' + "v2014.06.21", ) # Store a reference to the executing platform -IS_OSX_PLATFORM = sys.platform.startswith('darwin') -IS_WINDOWS_PLATFORM = sys.platform.startswith('win') +IS_OSX_PLATFORM = sys.platform.startswith("darwin") +IS_WINDOWS_PLATFORM = sys.platform.startswith("win") if IS_WINDOWS_PLATFORM or IS_OSX_PLATFORM: IS_SMARTOS_PLATFORM = False else: # os.uname() not available on Windows. - IS_SMARTOS_PLATFORM = os.uname()[0] == 'SunOS' and os.uname()[3].startswith('joyent_') + IS_SMARTOS_PLATFORM = os.uname()[0] == "SunOS" and os.uname()[3].startswith( + "joyent_" + ) # Store a reference whether if we're running under Python 3 and above IS_PY3 = sys.version_info > (3,) @@ -89,42 +102,56 @@ try: # Add the esky bdist target if the module is available # may require additional modules depending on platform from esky import bdist_esky + # bbfreeze chosen for its tight integration with distutils import bbfreeze + HAS_ESKY = True except ImportError: HAS_ESKY = False -SALT_VERSION = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', 'version.py') -SALT_VERSION_HARDCODED = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', '_version.py') -SALT_SYSPATHS_HARDCODED = os.path.join(os.path.abspath(SETUP_DIRNAME), 'salt', '_syspaths.py') -SALT_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'base.txt') -SALT_CRYPTO_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'crypto.txt') -SALT_ZEROMQ_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), 'requirements', 'zeromq.txt') -SALT_LONG_DESCRIPTION_FILE = os.path.join(os.path.abspath(SETUP_DIRNAME), 'README.rst') +SALT_VERSION = os.path.join(os.path.abspath(SETUP_DIRNAME), "salt", "version.py") +SALT_VERSION_HARDCODED = os.path.join( + os.path.abspath(SETUP_DIRNAME), "salt", "_version.py" +) +SALT_SYSPATHS_HARDCODED = os.path.join( + os.path.abspath(SETUP_DIRNAME), "salt", "_syspaths.py" +) +SALT_REQS = os.path.join(os.path.abspath(SETUP_DIRNAME), "requirements", "base.txt") +SALT_CRYPTO_REQS = os.path.join( + os.path.abspath(SETUP_DIRNAME), "requirements", "crypto.txt" +) +SALT_ZEROMQ_REQS = os.path.join( + os.path.abspath(SETUP_DIRNAME), "requirements", "zeromq.txt" +) +SALT_LONG_DESCRIPTION_FILE = os.path.join(os.path.abspath(SETUP_DIRNAME), "README.rst") SALT_OSX_REQS = [ - os.path.join(os.path.abspath(SETUP_DIRNAME), 'pkg', 'osx', 'req.txt'), - os.path.join(os.path.abspath(SETUP_DIRNAME), 'pkg', 'osx', 'req_ext.txt') + os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "osx", "req.txt"), + os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "osx", "req_ext.txt"), + os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "osx", "req_pyobjc.txt"), ] SALT_WINDOWS_REQS = [ - os.path.join(os.path.abspath(SETUP_DIRNAME), 'pkg', 'windows', 'req.txt'), - os.path.join(os.path.abspath(SETUP_DIRNAME), 'pkg', 'windows', 'req_win.txt') + os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "windows", "req.txt"), + os.path.join(os.path.abspath(SETUP_DIRNAME), "pkg", "windows", "req_win.txt"), ] # Salt SSH Packaging Detection -PACKAGED_FOR_SALT_SSH_FILE = os.path.join(os.path.abspath(SETUP_DIRNAME), '.salt-ssh-package') +PACKAGED_FOR_SALT_SSH_FILE = os.path.join( + os.path.abspath(SETUP_DIRNAME), ".salt-ssh-package" +) PACKAGED_FOR_SALT_SSH = os.path.isfile(PACKAGED_FOR_SALT_SSH_FILE) # pylint: disable=W0122 -exec(compile(open(SALT_VERSION).read(), SALT_VERSION, 'exec')) +exec(compile(open(SALT_VERSION).read(), SALT_VERSION, "exec")) # pylint: enable=W0122 # ----- Helper Functions --------------------------------------------------------------------------------------------> + def _parse_op(op): - ''' + """ >>> _parse_op('>') 'gt' >>> _parse_op('>=') @@ -141,41 +168,41 @@ def _parse_op(op): 'eq' >>> _parse_op(' <= ') 'le' - ''' + """ op = op.strip() - if '>' in op: - if '=' in op: - return 'ge' + if ">" in op: + if "=" in op: + return "ge" else: - return 'gt' - elif '<' in op: - if '=' in op: - return 'le' + return "gt" + elif "<" in op: + if "=" in op: + return "le" else: - return 'lt' - elif '!' in op: - return 'ne' + return "lt" + elif "!" in op: + return "ne" else: - return 'eq' + return "eq" def _parse_ver(ver): - ''' + """ >>> _parse_ver("'3.4' # pyzmq 17.1.0 stopped building wheels for python3.4") '3.4' >>> _parse_ver('"3.4"') '3.4' >>> _parse_ver('"2.6.17"') '2.6.17' - ''' - if '#' in ver: - ver, _ = ver.split('#', 1) + """ + if "#" in ver: + ver, _ = ver.split("#", 1) ver = ver.strip() - return ver.strip('\'').strip('"') + return ver.strip("'").strip('"') def _check_ver(pyver, op, wanted): - ''' + """ >>> _check_ver('2.7.15', 'gt', '2.7') True >>> _check_ver('2.7.15', 'gt', '2.7.15') @@ -184,7 +211,7 @@ def _check_ver(pyver, op, wanted): True >>> _check_ver('2.7.15', 'eq', '2.7.15') True - ''' + """ pyver = distutils.version.LooseVersion(pyver) wanted = distutils.version.LooseVersion(wanted) if IS_PY3: @@ -192,7 +219,7 @@ def _check_ver(pyver, op, wanted): pyver = str(pyver) if not isinstance(wanted, str): wanted = str(wanted) - return getattr(operator, '__{}__'.format(op))(pyver, wanted) + return getattr(operator, "__{}__".format(op))(pyver, wanted) def _parse_requirements_file(requirements_file): @@ -200,63 +227,76 @@ def _parse_requirements_file(requirements_file): with open(requirements_file) as rfh: for line in rfh.readlines(): line = line.strip() - if not line or line.startswith(('#', '-r')): + if not line or line.startswith(("#", "-r")): continue if IS_WINDOWS_PLATFORM: - if 'libcloud' in line: + if "libcloud" in line: continue - if IS_PY3 and 'futures' in line.lower(): + if IS_PY3 and "futures" in line.lower(): # Python 3 already has futures, installing it will only break # the current python installation whenever futures is imported continue try: - pkg, pyverspec = line.rsplit(';', 1) + pkg, pyverspec = line.rsplit(";", 1) except ValueError: - pkg, pyverspec = line, '' + pkg, pyverspec = line, "" pyverspec = pyverspec.strip() - if pyverspec and (not pkg.startswith('pycrypto') or pkg.startswith('pycryptodome')): - _, op, ver = pyverspec.split(' ', 2) - if not _check_ver(platform.python_version(), _parse_op(op), _parse_ver(ver)): + if pyverspec and ( + not pkg.startswith("pycrypto") or pkg.startswith("pycryptodome") + ): + _, op, ver = pyverspec.split(" ", 2) + if not _check_ver( + platform.python_version(), _parse_op(op), _parse_ver(ver) + ): continue parsed_requirements.append(pkg) return parsed_requirements + + # <---- Helper Functions --------------------------------------------------------------------------------------------- # ----- Custom Distutils/Setuptools Commands ------------------------------------------------------------------------> class WriteSaltVersion(Command): - description = 'Write salt\'s hardcoded version file' + description = "Write salt's hardcoded version file" user_options = [] def initialize_options(self): - ''' + """ Abstract method that is required to be overwritten - ''' + """ def finalize_options(self): - ''' + """ Abstract method that is required to be overwritten - ''' + """ def run(self): - if not os.path.exists(SALT_VERSION_HARDCODED) or self.distribution.with_salt_version: + if ( + not os.path.exists(SALT_VERSION_HARDCODED) + or self.distribution.with_salt_version + ): # Write the version file - if getattr(self.distribution, 'salt_version_hardcoded_path', None) is None: - print('This command is not meant to be called on it\'s own') + if getattr(self.distribution, "salt_version_hardcoded_path", None) is None: + print("This command is not meant to be called on it's own") exit(1) if not self.distribution.with_salt_version: - salt_version = __saltstack_version__ # pylint: disable=undefined-variable + salt_version = ( + __saltstack_version__ # pylint: disable=undefined-variable + ) else: from salt.version import SaltStackVersion - salt_version = SaltStackVersion.parse(self.distribution.with_salt_version) + + salt_version = SaltStackVersion.parse( + self.distribution.with_salt_version + ) # pylint: disable=E0602 - open(self.distribution.salt_version_hardcoded_path, 'w').write( + open(self.distribution.salt_version_hardcoded_path, "w").write( INSTALL_VERSION_TEMPLATE.format( - date=DATE, - full_version_info=salt_version.full_info + date=DATE, full_version_info=salt_version.full_info_all_versions ) ) # pylint: enable=E0602 @@ -264,7 +304,7 @@ class WriteSaltVersion(Command): class GenerateSaltSyspaths(Command): - description = 'Generate salt\'s hardcoded syspaths file' + description = "Generate salt's hardcoded syspaths file" def initialize_options(self): pass @@ -274,12 +314,12 @@ class GenerateSaltSyspaths(Command): def run(self): # Write the syspaths file - if getattr(self.distribution, 'salt_syspaths_hardcoded_path', None) is None: - print('This command is not meant to be called on it\'s own') + if getattr(self.distribution, "salt_syspaths_hardcoded_path", None) is None: + print("This command is not meant to be called on it's own") exit(1) # Write the system paths file - open(self.distribution.salt_syspaths_hardcoded_path, 'w').write( + open(self.distribution.salt_syspaths_hardcoded_path, "w").write( INSTALL_SYSPATHS_TEMPLATE.format( date=DATE, root_dir=self.distribution.salt_root_dir, @@ -305,55 +345,66 @@ class GenerateSaltSyspaths(Command): class WriteSaltSshPackagingFile(Command): - description = 'Write salt\'s ssh packaging file' + description = "Write salt's ssh packaging file" user_options = [] def initialize_options(self): - ''' + """ Abstract method that is required to be overwritten - ''' + """ def finalize_options(self): - ''' + """ Abstract method that is required to be overwritten - ''' + """ def run(self): if not os.path.exists(PACKAGED_FOR_SALT_SSH_FILE): # Write the salt-ssh packaging file - if getattr(self.distribution, 'salt_ssh_packaging_file', None) is None: - print('This command is not meant to be called on it\'s own') + if getattr(self.distribution, "salt_ssh_packaging_file", None) is None: + print("This command is not meant to be called on it's own") exit(1) # pylint: disable=E0602 - open(self.distribution.salt_ssh_packaging_file, 'w').write('Packaged for Salt-SSH\n') + open(self.distribution.salt_ssh_packaging_file, "w").write( + "Packaged for Salt-SSH\n" + ) # pylint: enable=E0602 class Develop(develop): user_options = develop.user_options + [ - ('write-salt-version', None, - 'Generate Salt\'s _version.py file which allows proper version ' - 'reporting. This defaults to False on develop/editable setups. ' - 'If WRITE_SALT_VERSION is found in the environment this flag is ' - 'switched to True.'), - ('generate-salt-syspaths', None, - 'Generate Salt\'s _syspaths.py file which allows tweaking some ' - 'common paths that salt uses. This defaults to False on ' - 'develop/editable setups. If GENERATE_SALT_SYSPATHS is found in ' - 'the environment this flag is switched to True.'), - ('mimic-salt-install', None, - 'Mimmic the install command when running the develop command. ' - 'This will generate salt\'s _version.py and _syspaths.py files. ' - 'Generate Salt\'s _syspaths.py file which allows tweaking some ' - 'This defaults to False on develop/editable setups. ' - 'If MIMIC_INSTALL is found in the environment this flag is ' - 'switched to True.') + ( + "write-salt-version", + None, + "Generate Salt's _version.py file which allows proper version " + "reporting. This defaults to False on develop/editable setups. " + "If WRITE_SALT_VERSION is found in the environment this flag is " + "switched to True.", + ), + ( + "generate-salt-syspaths", + None, + "Generate Salt's _syspaths.py file which allows tweaking some " + "common paths that salt uses. This defaults to False on " + "develop/editable setups. If GENERATE_SALT_SYSPATHS is found in " + "the environment this flag is switched to True.", + ), + ( + "mimic-salt-install", + None, + "Mimmic the install command when running the develop command. " + "This will generate salt's _version.py and _syspaths.py files. " + "Generate Salt's _syspaths.py file which allows tweaking some " + "This defaults to False on develop/editable setups. " + "If MIMIC_INSTALL is found in the environment this flag is " + "switched to True.", + ), ] boolean_options = develop.boolean_options + [ - 'write-salt-version', - 'generate-salt-syspaths', - 'mimic-salt-install' + "write-salt-version", + "generate-salt-syspaths", + "mimic-salt-install", ] def initialize_options(self): @@ -364,11 +415,11 @@ class Develop(develop): def finalize_options(self): develop.finalize_options(self) - if 'WRITE_SALT_VERSION' in os.environ: + if "WRITE_SALT_VERSION" in os.environ: self.write_salt_version = True - if 'GENERATE_SALT_SYSPATHS' in os.environ: + if "GENERATE_SALT_SYSPATHS" in os.environ: self.generate_salt_syspaths = True - if 'MIMIC_SALT_INSTALL' in os.environ: + if "MIMIC_SALT_INSTALL" in os.environ: self.mimic_salt_install = True if self.mimic_salt_install: @@ -379,17 +430,17 @@ class Develop(develop): if IS_WINDOWS_PLATFORM: # Download the required DLLs self.distribution.salt_download_windows_dlls = True - self.run_command('download-windows-dlls') + self.run_command("download-windows-dlls") self.distribution.salt_download_windows_dlls = None if self.write_salt_version is True: self.distribution.running_salt_install = True self.distribution.salt_version_hardcoded_path = SALT_VERSION_HARDCODED - self.run_command('write_salt_version') + self.run_command("write_salt_version") if self.generate_salt_syspaths: self.distribution.salt_syspaths_hardcoded_path = SALT_SYSPATHS_HARDCODED - self.run_command('generate_salt_syspaths') + self.run_command("generate_salt_syspaths") # Resume normal execution develop.run(self) @@ -397,7 +448,7 @@ class Develop(develop): class DownloadWindowsDlls(Command): - description = 'Download required DLL\'s for windows' + description = "Download required DLL's for windows" def initialize_options(self): pass @@ -406,40 +457,49 @@ class DownloadWindowsDlls(Command): pass def run(self): - if getattr(self.distribution, 'salt_download_windows_dlls', None) is None: - print('This command is not meant to be called on it\'s own') + if getattr(self.distribution, "salt_download_windows_dlls", None) is None: + print("This command is not meant to be called on it's own") exit(1) import pip + # pip has moved many things to `_internal` starting with pip 10 - if LooseVersion(pip.__version__) < LooseVersion('10.0'): - from pip.utils.logging import indent_log # pylint: disable=no-name-in-module + if LooseVersion(pip.__version__) < LooseVersion("10.0"): + # pylint: disable=no-name-in-module + from pip.utils.logging import indent_log + + # pylint: enable=no-name-in-module else: - from pip._internal.utils.logging import indent_log # pylint: disable=no-name-in-module + from pip._internal.utils.logging import ( + indent_log, + ) # pylint: disable=no-name-in-module platform_bits, _ = platform.architecture() - url = 'https://repo.saltstack.com/windows/dependencies/{bits}/{fname}.dll' - dest = os.path.join(os.path.dirname(sys.executable), '{fname}.dll') + url = "https://repo.saltstack.com/windows/dependencies/{bits}/{fname}.dll" + dest = os.path.join(os.path.dirname(sys.executable), "{fname}.dll") with indent_log(): - for fname in ('libeay32', 'ssleay32', 'msvcr120'): + for fname in ("libeay32", "ssleay32", "msvcr120"): # See if the library is already on the system if find_library(fname): continue furl = url.format(bits=platform_bits[:2], fname=fname) fdest = dest.format(fname=fname) if not os.path.exists(fdest): - log.info('Downloading {0}.dll to {1} from {2}'.format(fname, fdest, furl)) + log.info( + "Downloading {0}.dll to {1} from {2}".format(fname, fdest, furl) + ) try: import requests from contextlib import closing + with closing(requests.get(furl, stream=True)) as req: if req.status_code == 200: - with open(fdest, 'wb') as wfh: + with open(fdest, "wb") as wfh: for chunk in req.iter_content(chunk_size=4096): if chunk: # filter out keep-alive new chunks wfh.write(chunk) wfh.flush() else: log.error( - 'Failed to download {0}.dll to {1} from {2}'.format( + "Failed to download {0}.dll to {1} from {2}".format( fname, fdest, furl ) ) @@ -447,7 +507,7 @@ class DownloadWindowsDlls(Command): req = urlopen(furl) if req.getcode() == 200: - with open(fdest, 'wb') as wfh: + with open(fdest, "wb") as wfh: if IS_PY3: while True: chunk = req.read(4096) @@ -464,32 +524,31 @@ class DownloadWindowsDlls(Command): wfh.flush() else: log.error( - 'Failed to download {0}.dll to {1} from {2}'.format( + "Failed to download {0}.dll to {1} from {2}".format( fname, fdest, furl ) ) class Sdist(sdist): - def make_release_tree(self, base_dir, files): if self.distribution.ssh_packaging: self.distribution.salt_ssh_packaging_file = PACKAGED_FOR_SALT_SSH_FILE - self.run_command('write_salt_ssh_packaging_file') + self.run_command("write_salt_ssh_packaging_file") self.filelist.files.append(os.path.basename(PACKAGED_FOR_SALT_SSH_FILE)) if not IS_PY3 and not isinstance(base_dir, str): # Work around some bad code in distutils which logs unicode paths # against a str format string. - base_dir = base_dir.encode('utf-8') + base_dir = base_dir.encode("utf-8") sdist.make_release_tree(self, base_dir, files) # Let's generate salt/_version.py to include in the sdist tarball self.distribution.running_salt_sdist = True self.distribution.salt_version_hardcoded_path = os.path.join( - base_dir, 'salt', '_version.py' + base_dir, "salt", "_version.py" ) - self.run_command('write_salt_version') + self.run_command("write_salt_version") def make_distribution(self): sdist.make_distribution(self) @@ -499,15 +558,15 @@ class Sdist(sdist): class CloudSdist(Sdist): # pylint: disable=too-many-ancestors user_options = Sdist.user_options + [ - ('download-bootstrap-script', None, - 'Download the latest stable bootstrap-salt.sh script. This ' - 'can also be triggered by having `DOWNLOAD_BOOTSTRAP_SCRIPT=1` as an ' - 'environment variable.') - - ] - boolean_options = Sdist.boolean_options + [ - 'download-bootstrap-script' + ( + "download-bootstrap-script", + None, + "Download the latest stable bootstrap-salt.sh script. This " + "can also be triggered by having `DOWNLOAD_BOOTSTRAP_SCRIPT=1` as an " + "environment variable.", + ) ] + boolean_options = Sdist.boolean_options + ["download-bootstrap-script"] def initialize_options(self): Sdist.initialize_options(self) @@ -516,53 +575,45 @@ class CloudSdist(Sdist): # pylint: disable=too-many-ancestors def finalize_options(self): Sdist.finalize_options(self) - if 'SKIP_BOOTSTRAP_DOWNLOAD' in os.environ: - log('Please stop using \'SKIP_BOOTSTRAP_DOWNLOAD\' and use ' # pylint: disable=not-callable - '\'DOWNLOAD_BOOTSTRAP_SCRIPT\' instead') - - if 'DOWNLOAD_BOOTSTRAP_SCRIPT' in os.environ: - download_bootstrap_script = os.environ.get( - 'DOWNLOAD_BOOTSTRAP_SCRIPT', '0' + if "SKIP_BOOTSTRAP_DOWNLOAD" in os.environ: + # pylint: disable=not-callable + log( + "Please stop using 'SKIP_BOOTSTRAP_DOWNLOAD' and use " + "'DOWNLOAD_BOOTSTRAP_SCRIPT' instead" ) - self.download_bootstrap_script = download_bootstrap_script == '1' + # pylint: enable=not-callable + + if "DOWNLOAD_BOOTSTRAP_SCRIPT" in os.environ: + download_bootstrap_script = os.environ.get("DOWNLOAD_BOOTSTRAP_SCRIPT", "0") + self.download_bootstrap_script = download_bootstrap_script == "1" def run(self): if self.download_bootstrap_script is True: # Let's update the bootstrap-script to the version defined to be # distributed. See BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION above. url = ( - 'https://github.com/saltstack/salt-bootstrap/raw/{0}' - '/bootstrap-salt.sh'.format( - BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION - ) + "https://github.com/saltstack/salt-bootstrap/raw/{0}" + "/bootstrap-salt.sh".format(BOOTSTRAP_SCRIPT_DISTRIBUTED_VERSION) ) deploy_path = os.path.join( - SETUP_DIRNAME, - 'salt', - 'cloud', - 'deploy', - 'bootstrap-salt.sh' + SETUP_DIRNAME, "salt", "cloud", "deploy", "bootstrap-salt.sh" ) log.info( - 'Updating bootstrap-salt.sh.' - '\n\tSource: {0}' - '\n\tDestination: {1}'.format( - url, - deploy_path - ) + "Updating bootstrap-salt.sh." + "\n\tSource: {0}" + "\n\tDestination: {1}".format(url, deploy_path) ) try: import requests + req = requests.get(url) if req.status_code == 200: script_contents = req.text.encode(req.encoding) else: log.error( - 'Failed to update the bootstrap-salt.sh script. HTTP ' - 'Error code: {0}'.format( - req.status_code - ) + "Failed to update the bootstrap-salt.sh script. HTTP " + "Error code: {0}".format(req.status_code) ) except ImportError: req = urlopen(url) @@ -571,18 +622,14 @@ class CloudSdist(Sdist): # pylint: disable=too-many-ancestors script_contents = req.read() else: log.error( - 'Failed to update the bootstrap-salt.sh script. HTTP ' - 'Error code: {0}'.format( - req.getcode() - ) + "Failed to update the bootstrap-salt.sh script. HTTP " + "Error code: {0}".format(req.getcode()) ) try: - with open(deploy_path, 'w') as fp_: + with open(deploy_path, "w") as fp_: fp_.write(script_contents) except (OSError, IOError) as err: - log.error( - 'Failed to write the updated script: {0}'.format(err) - ) + log.error("Failed to write the updated script: {0}".format(err)) # Let's the rest of the build command Sdist.run(self) @@ -591,7 +638,7 @@ class CloudSdist(Sdist): # pylint: disable=too-many-ancestors # We only need to ship the scripts which are supposed to be installed dist_scripts = self.distribution.scripts for script in self.filelist.files[:]: - if not script.startswith('scripts/'): + if not script.startswith("scripts/"): continue if script not in dist_scripts: self.filelist.files.remove(script) @@ -599,33 +646,36 @@ class CloudSdist(Sdist): # pylint: disable=too-many-ancestors class TestCommand(Command): - description = 'Run tests' + description = "Run tests" user_options = [ - ('runtests-opts=', 'R', 'Command line options to pass to runtests.py') + ("runtests-opts=", "R", "Command line options to pass to runtests.py") ] def initialize_options(self): self.runtests_opts = None def finalize_options(self): - ''' + """ Abstract method that is required to be overwritten - ''' + """ def run(self): from subprocess import Popen - self.run_command('build') - build_cmd = self.get_finalized_command('build_ext') - runner = os.path.abspath('tests/runtests.py') - test_cmd = sys.executable + ' {0}'.format(runner) - if self.runtests_opts: - test_cmd += ' {0}'.format(self.runtests_opts) - print('running test') + self.run_command("build") + build_cmd = self.get_finalized_command("build_ext") + runner = os.path.abspath("tests/runtests.py") + test_cmd = sys.executable + " {0}".format(runner) + if self.runtests_opts: + test_cmd += " {0}".format(self.runtests_opts) + + print("running test") test_process = Popen( - test_cmd, shell=True, - stdout=sys.stdout, stderr=sys.stderr, - cwd=build_cmd.build_lib + test_cmd, + shell=True, + stdout=sys.stdout, + stderr=sys.stderr, + cwd=build_cmd.build_lib, ) test_process.communicate() sys.exit(test_process.returncode) @@ -635,23 +685,31 @@ class Clean(clean): def run(self): clean.run(self) # Let's clean compiled *.py[c,o] - for subdir in ('salt', 'tests', 'doc'): + for subdir in ("salt", "tests", "doc"): root = os.path.join(os.path.dirname(__file__), subdir) for dirname, _, _ in os.walk(root): - for to_remove_filename in glob.glob('{0}/*.py[oc]'.format(dirname)): + for to_remove_filename in glob.glob("{0}/*.py[oc]".format(dirname)): os.remove(to_remove_filename) -INSTALL_VERSION_TEMPLATE = '''\ +if HAS_BDIST_WHEEL: + + class BDistWheel(bdist_wheel): + def finalize_options(self): + bdist_wheel.finalize_options(self) + self.distribution.build_wheel = True + + +INSTALL_VERSION_TEMPLATE = """\ # This file was auto-generated by salt's setup from salt.version import SaltStackVersion __saltstack_version__ = SaltStackVersion{full_version_info!r} -''' +""" -INSTALL_SYSPATHS_TEMPLATE = '''\ +INSTALL_SYSPATHS_TEMPLATE = """\ # This file was auto-generated by salt's setup on \ {date:%A, %d %B %Y @ %H:%m:%S UTC}. @@ -672,32 +730,37 @@ SPM_FORMULA_PATH = {spm_formula_path!r} SPM_PILLAR_PATH = {spm_pillar_path!r} SPM_REACTOR_PATH = {spm_reactor_path!r} HOME_DIR = {home_dir!r} -''' +""" class Build(build): def run(self): # Run build.run function build.run(self) - if getattr(self.distribution, 'with_salt_version', False): - # Write the hardcoded salt version module salt/_version.py - self.distribution.salt_version_hardcoded_path = os.path.join( - self.build_lib, 'salt', '_version.py' - ) - self.run_command('write_salt_version') + salt_build_ver_file = os.path.join(self.build_lib, "salt", "_version.py") - if getattr(self.distribution, 'running_salt_install', False): + if getattr(self.distribution, "with_salt_version", False): + # Write the hardcoded salt version module salt/_version.py + self.distribution.salt_version_hardcoded_path = salt_build_ver_file + self.run_command("write_salt_version") + + if getattr(self.distribution, "build_wheel", False): + # we are building a wheel package. need to include _version.py + self.distribution.salt_version_hardcoded_path = salt_build_ver_file + self.run_command("write_salt_version") + + if getattr(self.distribution, "running_salt_install", False): # If our install attribute is present and set to True, we'll go # ahead and write our install time python modules. # Write the hardcoded salt version module salt/_version.py - self.run_command('write_salt_version') + self.run_command("write_salt_version") # Write the system paths file self.distribution.salt_syspaths_hardcoded_path = os.path.join( - self.build_lib, 'salt', '_syspaths.py' + self.build_lib, "salt", "_syspaths.py" ) - self.run_command('generate_salt_syspaths') + self.run_command("generate_salt_syspaths") class Install(install): @@ -708,10 +771,10 @@ class Install(install): install.finalize_options(self) def run(self): - if LooseVersion(setuptools.__version__) < LooseVersion('9.1'): + if LooseVersion(setuptools.__version__) < LooseVersion("9.1"): sys.stderr.write( - '\n\nInstalling Salt requires setuptools >= 9.1\n' - 'Available setuptools version is {}\n\n'.format(setuptools.__version__) + "\n\nInstalling Salt requires setuptools >= 9.1\n" + "Available setuptools version is {}\n\n".format(setuptools.__version__) ) sys.stderr.flush() sys.exit(1) @@ -720,13 +783,19 @@ class Install(install): # _version.py in the build command self.distribution.running_salt_install = True self.distribution.salt_version_hardcoded_path = os.path.join( - self.build_lib, 'salt', '_version.py' + self.build_lib, "salt", "_version.py" ) if IS_WINDOWS_PLATFORM: # Download the required DLLs self.distribution.salt_download_windows_dlls = True - self.run_command('download-windows-dlls') + self.run_command("download-windows-dlls") self.distribution.salt_download_windows_dlls = None + # need to ensure _version.py is created in build dir before install + if not os.path.exists(os.path.join(self.build_lib)): + if not self.skip_build: + self.run_command("build") + else: + self.run_command("write_salt_version") # Run install.run install.run(self) @@ -766,9 +835,9 @@ class Install(install): class InstallLib(install_lib): def run(self): executables = [ - 'salt/templates/git/ssh-id-wrapper', - 'salt/templates/lxc/salt_tarball', - ] + "salt/templates/git/ssh-id-wrapper", + "salt/templates/lxc/salt_tarball", + ] install_lib.run(self) # input and outputs match 1-1 @@ -783,6 +852,8 @@ class InstallLib(install_lib): for idx in chmod: filename = out[idx] os.chmod(filename, 0o755) + + # <---- Custom Distutils/Setuptools Commands ------------------------------------------------------------------------- @@ -790,7 +861,7 @@ class InstallLib(install_lib): # We use this to override the package name in case --ssh-packaging is passed to # setup.py or the special .salt-ssh-package is found class SaltDistribution(distutils.dist.Distribution): - ''' + """ Just so it's completely clear Under windows, the following scripts should be installed: @@ -814,45 +885,69 @@ class SaltDistribution(distutils.dist.Distribution): * salt-run Under *nix, all scripts should be installed - ''' - global_options = distutils.dist.Distribution.global_options + [ - ('ssh-packaging', None, 'Run in SSH packaging mode'), - ('salt-transport=', None, 'The transport to prepare salt for. Currently, the only choice ' - 'is \'zeromq\'. This may be expanded in the future. Defaults to ' - '\'zeromq\'', 'zeromq')] + [ - ('with-salt-version=', None, 'Set a fixed version for Salt instead calculating it'), - # Salt's Paths Configuration Settings - ('salt-root-dir=', None, - 'Salt\'s pre-configured root directory'), - ('salt-share-dir=', None, - 'Salt\'s pre-configured share directory'), - ('salt-config-dir=', None, - 'Salt\'s pre-configured configuration directory'), - ('salt-cache-dir=', None, - 'Salt\'s pre-configured cache directory'), - ('salt-sock-dir=', None, - 'Salt\'s pre-configured socket directory'), - ('salt-srv-root-dir=', None, - 'Salt\'s pre-configured service directory'), - ('salt-base-file-roots-dir=', None, - 'Salt\'s pre-configured file roots directory'), - ('salt-base-pillar-roots-dir=', None, - 'Salt\'s pre-configured pillar roots directory'), - ('salt-base-master-roots-dir=', None, - 'Salt\'s pre-configured master roots directory'), - ('salt-logs-dir=', None, - 'Salt\'s pre-configured logs directory'), - ('salt-pidfile-dir=', None, - 'Salt\'s pre-configured pidfiles directory'), - ('salt-spm-formula-dir=', None, - 'Salt\'s pre-configured SPM formulas directory'), - ('salt-spm-pillar-dir=', None, - 'Salt\'s pre-configured SPM pillar directory'), - ('salt-spm-reactor-dir=', None, - 'Salt\'s pre-configured SPM reactor directory'), - ('salt-home-dir=', None, - 'Salt\'s pre-configured user home directory'), - ] + """ + + global_options = ( + distutils.dist.Distribution.global_options + + [ + ("ssh-packaging", None, "Run in SSH packaging mode"), + ( + "salt-transport=", + None, + "The transport to prepare salt for. Currently, the only choice " + "is 'zeromq'. This may be expanded in the future. Defaults to " + "'zeromq'", + "zeromq", + ), + ] + + [ + ( + "with-salt-version=", + None, + "Set a fixed version for Salt instead calculating it", + ), + # Salt's Paths Configuration Settings + ("salt-root-dir=", None, "Salt's pre-configured root directory"), + ("salt-share-dir=", None, "Salt's pre-configured share directory"), + ("salt-config-dir=", None, "Salt's pre-configured configuration directory"), + ("salt-cache-dir=", None, "Salt's pre-configured cache directory"), + ("salt-sock-dir=", None, "Salt's pre-configured socket directory"), + ("salt-srv-root-dir=", None, "Salt's pre-configured service directory"), + ( + "salt-base-file-roots-dir=", + None, + "Salt's pre-configured file roots directory", + ), + ( + "salt-base-pillar-roots-dir=", + None, + "Salt's pre-configured pillar roots directory", + ), + ( + "salt-base-master-roots-dir=", + None, + "Salt's pre-configured master roots directory", + ), + ("salt-logs-dir=", None, "Salt's pre-configured logs directory"), + ("salt-pidfile-dir=", None, "Salt's pre-configured pidfiles directory"), + ( + "salt-spm-formula-dir=", + None, + "Salt's pre-configured SPM formulas directory", + ), + ( + "salt-spm-pillar-dir=", + None, + "Salt's pre-configured SPM pillar directory", + ), + ( + "salt-spm-reactor-dir=", + None, + "Salt's pre-configured SPM reactor directory", + ), + ("salt-home-dir=", None, "Salt's pre-configured user home directory"), + ] + ) def __init__(self, attrs=None): distutils.dist.Distribution.__init__(self, attrs) @@ -882,34 +977,39 @@ class SaltDistribution(distutils.dist.Distribution): # Salt version self.with_salt_version = None - self.name = 'salt-ssh' if PACKAGED_FOR_SALT_SSH else 'salt' + self.name = "salt-ssh" if PACKAGED_FOR_SALT_SSH else "salt" self.salt_version = __version__ # pylint: disable=undefined-variable - self.description = 'Portable, distributed, remote execution and configuration management system' + self.description = "Portable, distributed, remote execution and configuration management system" kwargs = {} if IS_PY3: - kwargs['encoding'] = 'utf-8' + kwargs["encoding"] = "utf-8" with open(SALT_LONG_DESCRIPTION_FILE, **kwargs) as f: self.long_description = f.read() - self.long_description_content_type = 'text/x-rst' - self.author = 'Thomas S Hatch' - self.author_email = 'thatch45@gmail.com' - self.url = 'http://saltstack.org' - self.cmdclass.update({'test': TestCommand, - 'clean': Clean, - 'build': Build, - 'sdist': Sdist, - 'install': Install, - 'develop': Develop, - 'write_salt_version': WriteSaltVersion, - 'generate_salt_syspaths': GenerateSaltSyspaths, - 'write_salt_ssh_packaging_file': WriteSaltSshPackagingFile}) + self.long_description_content_type = "text/x-rst" + self.author = "Thomas S Hatch" + self.author_email = "thatch45@gmail.com" + self.url = "http://saltstack.org" + self.cmdclass.update( + { + "test": TestCommand, + "clean": Clean, + "build": Build, + "sdist": Sdist, + "install": Install, + "develop": Develop, + "write_salt_version": WriteSaltVersion, + "generate_salt_syspaths": GenerateSaltSyspaths, + "write_salt_ssh_packaging_file": WriteSaltSshPackagingFile, + } + ) if not IS_WINDOWS_PLATFORM: - self.cmdclass.update({'sdist': CloudSdist, - 'install_lib': InstallLib}) + self.cmdclass.update({"sdist": CloudSdist, "install_lib": InstallLib}) if IS_WINDOWS_PLATFORM: - self.cmdclass.update({'download-windows-dlls': DownloadWindowsDlls}) + self.cmdclass.update({"download-windows-dlls": DownloadWindowsDlls}) + if HAS_BDIST_WHEEL: + self.cmdclass["bdist_wheel"] = BDistWheel - self.license = 'Apache Software License 2.0' + self.license = "Apache Software License 2.0" self.packages = self.discover_packages() self.zip_safe = False @@ -920,15 +1020,15 @@ class SaltDistribution(distutils.dist.Distribution): def update_metadata(self): for attrname in dir(self): - if attrname.startswith('__'): + if attrname.startswith("__"): continue attrvalue = getattr(self, attrname, None) if attrvalue == 0: continue - if attrname == 'salt_version': - attrname = 'version' - if hasattr(self.metadata, 'set_{0}'.format(attrname)): - getattr(self.metadata, 'set_{0}'.format(attrname))(attrvalue) + if attrname == "salt_version": + attrname = "version" + if hasattr(self.metadata, "set_{0}".format(attrname)): + getattr(self.metadata, "set_{0}".format(attrname))(attrvalue) elif hasattr(self.metadata, attrname): try: setattr(self.metadata, attrname, attrvalue) @@ -937,91 +1037,107 @@ class SaltDistribution(distutils.dist.Distribution): def discover_packages(self): modules = [] - for root, _, files in os.walk(os.path.join(SETUP_DIRNAME, 'salt')): - if '__init__.py' not in files: + for root, _, files in os.walk(os.path.join(SETUP_DIRNAME, "salt")): + if "__init__.py" not in files: continue - modules.append(os.path.relpath(root, SETUP_DIRNAME).replace(os.sep, '.')) + modules.append(os.path.relpath(root, SETUP_DIRNAME).replace(os.sep, ".")) return modules # ----- Static Data --------------------------------------------------------------------------------------------> @property def _property_classifiers(self): - return ['Programming Language :: Python', - 'Programming Language :: Cython', - 'Programming Language :: Python :: 2.6', - 'Programming Language :: Python :: 2.7', - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'Intended Audience :: Information Technology', - 'Intended Audience :: System Administrators', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: POSIX :: Linux', - 'Topic :: System :: Clustering', - 'Topic :: System :: Distributed Computing'] + return [ + "Programming Language :: Python", + "Programming Language :: Cython", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: Apache Software License", + "Operating System :: POSIX :: Linux", + "Topic :: System :: Clustering", + "Topic :: System :: Distributed Computing", + ] @property def _property_dependency_links(self): - return ['https://github.com/saltstack/salt-testing/tarball/develop#egg=SaltTesting'] + return [ + "https://github.com/saltstack/salt-testing/tarball/develop#egg=SaltTesting" + ] @property def _property_tests_require(self): - return ['SaltTesting'] + return ["SaltTesting"] + # <---- Static Data ---------------------------------------------------------------------------------------------- # ----- Dynamic Data --------------------------------------------------------------------------------------------> @property def _property_package_data(self): - package_data = {'salt.templates': ['rh_ip/*.jinja', - 'debian_ip/*.jinja', - 'virt/*.jinja', - 'git/*', - 'lxc/*', - ]} + package_data = { + "salt.templates": [ + "rh_ip/*.jinja", + "debian_ip/*.jinja", + "virt/*.jinja", + "git/*", + "lxc/*", + ] + } if not IS_WINDOWS_PLATFORM: - package_data['salt.cloud'] = ['deploy/*.sh'] + package_data["salt.cloud"] = ["deploy/*.sh"] if not self.ssh_packaging and not PACKAGED_FOR_SALT_SSH: - package_data['salt.daemons.flo'] = ['*.flo'] + package_data["salt.daemons.flo"] = ["*.flo"] return package_data @property def _property_data_files(self): # Data files common to all scenarios data_files = [ - ('share/man/man1', ['doc/man/salt-call.1', 'doc/man/salt-run.1']), - ('share/man/man7', ['doc/man/salt.7']) + ("share/man/man1", ["doc/man/salt-call.1", "doc/man/salt-run.1"]), + ("share/man/man7", ["doc/man/salt.7"]), ] if self.ssh_packaging or PACKAGED_FOR_SALT_SSH: - data_files[0][1].append('doc/man/salt-ssh.1') + data_files[0][1].append("doc/man/salt-ssh.1") if IS_WINDOWS_PLATFORM: return data_files - data_files[0][1].append('doc/man/salt-cloud.1') + data_files[0][1].append("doc/man/salt-cloud.1") return data_files if IS_WINDOWS_PLATFORM: - data_files[0][1].extend(['doc/man/salt-cp.1', - 'doc/man/salt-key.1', - 'doc/man/salt-minion.1', - 'doc/man/salt-syndic.1', - 'doc/man/salt-unity.1', - 'doc/man/spm.1']) + data_files[0][1].extend( + [ + "doc/man/salt-cp.1", + "doc/man/salt-key.1", + "doc/man/salt-minion.1", + "doc/man/salt-syndic.1", + "doc/man/salt-unity.1", + "doc/man/spm.1", + ] + ) return data_files # *nix, so, we need all man pages - data_files[0][1].extend(['doc/man/salt-api.1', - 'doc/man/salt-cloud.1', - 'doc/man/salt-cp.1', - 'doc/man/salt-key.1', - 'doc/man/salt-master.1', - 'doc/man/salt-minion.1', - 'doc/man/salt-proxy.1', - 'doc/man/spm.1', - 'doc/man/salt.1', - 'doc/man/salt-ssh.1', - 'doc/man/salt-syndic.1', - 'doc/man/salt-unity.1']) + data_files[0][1].extend( + [ + "doc/man/salt-api.1", + "doc/man/salt-cloud.1", + "doc/man/salt-cp.1", + "doc/man/salt-key.1", + "doc/man/salt-master.1", + "doc/man/salt-minion.1", + "doc/man/salt-proxy.1", + "doc/man/spm.1", + "doc/man/salt.1", + "doc/man/salt-ssh.1", + "doc/man/salt-syndic.1", + "doc/man/salt-unity.1", + ] + ) return data_files @property @@ -1041,7 +1157,7 @@ class SaltDistribution(distutils.dist.Distribution): install_requires = _parse_requirements_file(SALT_REQS) - if self.salt_transport == 'zeromq': + if self.salt_transport == "zeromq": install_requires += _parse_requirements_file(SALT_CRYPTO_REQS) install_requires += _parse_requirements_file(SALT_ZEROMQ_REQS) return install_requires @@ -1049,83 +1165,105 @@ class SaltDistribution(distutils.dist.Distribution): @property def _property_scripts(self): # Scripts common to all scenarios - scripts = ['scripts/salt-call', 'scripts/salt-run'] + scripts = ["scripts/salt-call", "scripts/salt-run"] if self.ssh_packaging or PACKAGED_FOR_SALT_SSH: - scripts.append('scripts/salt-ssh') + scripts.append("scripts/salt-ssh") if IS_WINDOWS_PLATFORM: return scripts - scripts.extend(['scripts/salt-cloud', 'scripts/spm']) + scripts.extend(["scripts/salt-cloud", "scripts/spm"]) return scripts if IS_WINDOWS_PLATFORM: - scripts.extend(['scripts/salt-cp', - 'scripts/salt-key', - 'scripts/salt-minion', - 'scripts/salt-syndic', - 'scripts/salt-unity', - 'scripts/spm']) + scripts.extend( + [ + "scripts/salt-cp", + "scripts/salt-key", + "scripts/salt-minion", + "scripts/salt-syndic", + "scripts/salt-unity", + "scripts/spm", + ] + ) return scripts # *nix, so, we need all scripts - scripts.extend(['scripts/salt', - 'scripts/salt-api', - 'scripts/salt-cloud', - 'scripts/salt-cp', - 'scripts/salt-key', - 'scripts/salt-master', - 'scripts/salt-minion', - 'scripts/salt-proxy', - 'scripts/salt-ssh', - 'scripts/salt-syndic', - 'scripts/salt-unity', - 'scripts/spm']) + scripts.extend( + [ + "scripts/salt", + "scripts/salt-api", + "scripts/salt-cloud", + "scripts/salt-cp", + "scripts/salt-key", + "scripts/salt-master", + "scripts/salt-minion", + "scripts/salt-proxy", + "scripts/salt-ssh", + "scripts/salt-syndic", + "scripts/salt-unity", + "scripts/spm", + ] + ) return scripts @property def _property_entry_points(self): # console scripts common to all scenarios - scripts = ['salt-call = salt.scripts:salt_call', - 'salt-run = salt.scripts:salt_run'] + scripts = [ + "salt-call = salt.scripts:salt_call", + "salt-run = salt.scripts:salt_run", + ] if self.ssh_packaging or PACKAGED_FOR_SALT_SSH: - scripts.append('salt-ssh = salt.scripts:salt_ssh') + scripts.append("salt-ssh = salt.scripts:salt_ssh") if IS_WINDOWS_PLATFORM: - return {'console_scripts': scripts} - scripts.append('salt-cloud = salt.scripts:salt_cloud') - return {'console_scripts': scripts} + return {"console_scripts": scripts} + scripts.append("salt-cloud = salt.scripts:salt_cloud") + return {"console_scripts": scripts} if IS_WINDOWS_PLATFORM: - scripts.extend(['salt-cp = salt.scripts:salt_cp', - 'salt-key = salt.scripts:salt_key', - 'salt-minion = salt.scripts:salt_minion', - 'salt-syndic = salt.scripts:salt_syndic', - 'salt-unity = salt.scripts:salt_unity', - 'spm = salt.scripts:salt_spm']) - return {'console_scripts': scripts} + scripts.extend( + [ + "salt-cp = salt.scripts:salt_cp", + "salt-key = salt.scripts:salt_key", + "salt-minion = salt.scripts:salt_minion", + "salt-syndic = salt.scripts:salt_syndic", + "salt-unity = salt.scripts:salt_unity", + "spm = salt.scripts:salt_spm", + ] + ) + return {"console_scripts": scripts} # *nix, so, we need all scripts - scripts.extend(['salt = salt.scripts:salt_main', - 'salt-api = salt.scripts:salt_api', - 'salt-cloud = salt.scripts:salt_cloud', - 'salt-cp = salt.scripts:salt_cp', - 'salt-key = salt.scripts:salt_key', - 'salt-master = salt.scripts:salt_master', - 'salt-minion = salt.scripts:salt_minion', - 'salt-ssh = salt.scripts:salt_ssh', - 'salt-syndic = salt.scripts:salt_syndic', - 'salt-unity = salt.scripts:salt_unity', - 'spm = salt.scripts:salt_spm']) - return {'console_scripts': scripts} + scripts.extend( + [ + "salt = salt.scripts:salt_main", + "salt-api = salt.scripts:salt_api", + "salt-cloud = salt.scripts:salt_cloud", + "salt-cp = salt.scripts:salt_cp", + "salt-key = salt.scripts:salt_key", + "salt-master = salt.scripts:salt_master", + "salt-minion = salt.scripts:salt_minion", + "salt-ssh = salt.scripts:salt_ssh", + "salt-syndic = salt.scripts:salt_syndic", + "salt-unity = salt.scripts:salt_unity", + "spm = salt.scripts:salt_spm", + ] + ) + return {"console_scripts": scripts} + # <---- Dynamic Data --------------------------------------------------------------------------------------------- # ----- Esky Setup ----------------------------------------------------------------------------------------------> def setup_esky(self): - opt_dict = self.get_option_dict('bdist_esky') - opt_dict['freezer_module'] = ('setup script', 'bbfreeze') - opt_dict['freezer_options'] = ('setup script', {'includes': self.get_esky_freezer_includes()}) + opt_dict = self.get_option_dict("bdist_esky") + opt_dict["freezer_module"] = ("setup script", "bbfreeze") + opt_dict["freezer_options"] = ( + "setup script", + {"includes": self.get_esky_freezer_includes()}, + ) @property def _property_freezer_options(self): - return {'includes': self.get_esky_freezer_includes()} + return {"includes": self.get_esky_freezer_includes()} def get_esky_freezer_includes(self): # Sometimes the auto module traversal doesn't find everything, so we @@ -1134,87 +1272,87 @@ class SaltDistribution(distutils.dist.Distribution): # Specifying includes that don't exist doesn't appear to cause a freezing # error. freezer_includes = [ - 'zmq.core.*', - 'zmq.utils.*', - 'ast', - 'csv', - 'difflib', - 'distutils', - 'distutils.version', - 'numbers', - 'json', - 'M2Crypto', - 'Cookie', - 'asyncore', - 'fileinput', - 'sqlite3', - 'email', - 'email.mime.*', - 'requests', - 'sqlite3', + "zmq.core.*", + "zmq.utils.*", + "ast", + "csv", + "difflib", + "distutils", + "distutils.version", + "numbers", + "json", + "M2Crypto", + "Cookie", + "asyncore", + "fileinput", + "sqlite3", + "email", + "email.mime.*", + "requests", + "sqlite3", ] - if HAS_ZMQ and hasattr(zmq, 'pyzmq_version_info'): + if HAS_ZMQ and hasattr(zmq, "pyzmq_version_info"): if HAS_ZMQ and zmq.pyzmq_version_info() >= (0, 14): # We're freezing, and when freezing ZMQ needs to be installed, so this # works fine - if 'zmq.core.*' in freezer_includes: + if "zmq.core.*" in freezer_includes: # For PyZMQ >= 0.14, freezing does not need 'zmq.core.*' - freezer_includes.remove('zmq.core.*') + freezer_includes.remove("zmq.core.*") if IS_WINDOWS_PLATFORM: - freezer_includes.extend([ - 'imp', - 'win32api', - 'win32file', - 'win32con', - 'win32com', - 'win32net', - 'win32netcon', - 'win32gui', - 'win32security', - 'ntsecuritycon', - 'pywintypes', - 'pythoncom', - '_winreg', - 'wmi', - 'site', - 'psutil', - 'pytz', - ]) + freezer_includes.extend( + [ + "imp", + "win32api", + "win32file", + "win32con", + "win32com", + "win32net", + "win32netcon", + "win32gui", + "win32security", + "ntsecuritycon", + "pywintypes", + "pythoncom", + "_winreg", + "wmi", + "site", + "psutil", + "pytz", + ] + ) elif IS_SMARTOS_PLATFORM: # we have them as requirements in pkg/smartos/esky/requirements.txt # all these should be safe to force include - freezer_includes.extend([ - 'cherrypy', - 'python-dateutil', - 'pyghmi', - 'croniter', - 'mako', - 'gnupg', - ]) - elif sys.platform.startswith('linux'): - freezer_includes.append('spwd') + freezer_includes.extend( + ["cherrypy", "python-dateutil", "pyghmi", "croniter", "mako", "gnupg"] + ) + elif sys.platform.startswith("linux"): + freezer_includes.append("spwd") try: import yum # pylint: disable=unused-variable - freezer_includes.append('yum') + + freezer_includes.append("yum") except ImportError: pass - elif sys.platform.startswith('sunos'): + elif sys.platform.startswith("sunos"): # (The sledgehammer approach) # Just try to include everything # (This may be a better way to generate freezer_includes generally) try: from bbfreeze.modulegraph.modulegraph import ModuleGraph + mgraph = ModuleGraph(sys.path[:]) - for arg in glob.glob('salt/modules/*.py'): + for arg in glob.glob("salt/modules/*.py"): mgraph.run_script(arg) for mod in mgraph.flatten(): - if type(mod).__name__ != 'Script' and mod.filename: + if type(mod).__name__ != "Script" and mod.filename: freezer_includes.append(str(os.path.basename(mod.identifier))) except ImportError: pass return freezer_includes + # <---- Esky Setup ----------------------------------------------------------------------------------------------- # ----- Overridden Methods --------------------------------------------------------------------------------------> @@ -1225,17 +1363,15 @@ class SaltDistribution(distutils.dist.Distribution): self.ssh_packaging = 1 if self.ssh_packaging: - self.metadata.name = 'salt-ssh' - self.salt_transport = 'ssh' + self.metadata.name = "salt-ssh" + self.salt_transport = "ssh" elif self.salt_transport is None: - self.salt_transport = 'zeromq' + self.salt_transport = "zeromq" - if self.salt_transport not in ('zeromq', 'both', 'ssh', 'none'): + if self.salt_transport not in ("zeromq", "both", "ssh", "none"): raise DistutilsArgError( - 'The value of --salt-transport needs be \'zeromq\', ' - '\'both\', \'ssh\', or \'none\' not \'{0}\''.format( - self.salt_transport - ) + "The value of --salt-transport needs be 'zeromq', " + "'both', 'ssh', or 'none' not '{0}'".format(self.salt_transport) ) # Setup our property functions after class initialization and @@ -1243,16 +1379,18 @@ class SaltDistribution(distutils.dist.Distribution): # ATTENTION: This should be the last step before returning the args or # some of the requirements won't be correctly set for funcname in dir(self): - if not funcname.startswith('_property_'): + if not funcname.startswith("_property_"): continue - property_name = funcname.split('_property_', 1)[-1] + property_name = funcname.split("_property_", 1)[-1] setattr(self, property_name, getattr(self, funcname)) return args + # <---- Overridden Methods --------------------------------------------------------------------------------------- + # <---- Custom Distribution Class ------------------------------------------------------------------------------------ -if __name__ == '__main__': +if __name__ == "__main__": setup(distclass=SaltDistribution) diff --git a/tests/buildpackage.py b/tests/buildpackage.py index 90cf9157fc7..6b85b961dae 100644 --- a/tests/buildpackage.py +++ b/tests/buildpackage.py @@ -11,6 +11,7 @@ # pylint: disable=file-perms,resource-leakage from __future__ import absolute_import, print_function + import errno import glob import logging @@ -19,26 +20,26 @@ import re import shutil import subprocess import sys -from optparse import OptionParser, OptionGroup +from optparse import OptionGroup, OptionParser logging.QUIET = 0 logging.GARBAGE = 1 logging.TRACE = 5 -logging.addLevelName(logging.QUIET, 'QUIET') -logging.addLevelName(logging.TRACE, 'TRACE') -logging.addLevelName(logging.GARBAGE, 'GARBAGE') +logging.addLevelName(logging.QUIET, "QUIET") +logging.addLevelName(logging.TRACE, "TRACE") +logging.addLevelName(logging.GARBAGE, "GARBAGE") LOG_LEVELS = { - 'all': logging.NOTSET, - 'debug': logging.DEBUG, - 'error': logging.ERROR, - 'critical': logging.CRITICAL, - 'garbage': logging.GARBAGE, - 'info': logging.INFO, - 'quiet': logging.QUIET, - 'trace': logging.TRACE, - 'warning': logging.WARNING, + "all": logging.NOTSET, + "debug": logging.DEBUG, + "error": logging.ERROR, + "critical": logging.CRITICAL, + "garbage": logging.GARBAGE, + "info": logging.INFO, + "quiet": logging.QUIET, + "trace": logging.TRACE, + "warning": logging.WARNING, } log = logging.getLogger(__name__) @@ -47,60 +48,67 @@ log = logging.getLogger(__name__) def _abort(msgs): - ''' + """ Unrecoverable error, pull the plug - ''' + """ if not isinstance(msgs, list): msgs = [msgs] for msg in msgs: log.error(msg) - sys.stderr.write(msg + '\n\n') - sys.stderr.write('Build failed. See log file for further details.\n') + sys.stderr.write(msg + "\n\n") + sys.stderr.write("Build failed. See log file for further details.\n") sys.exit(1) # HELPER FUNCTIONS + def _init(): - ''' + """ Parse CLI options. - ''' + """ parser = OptionParser() - parser.add_option('--platform', - dest='platform', - help='Platform (\'os\' grain)') - parser.add_option('--log-level', - dest='log_level', - default='warning', - help='Control verbosity of logging. Default: %default') + parser.add_option("--platform", dest="platform", help="Platform ('os' grain)") + parser.add_option( + "--log-level", + dest="log_level", + default="warning", + help="Control verbosity of logging. Default: %default", + ) # All arguments dealing with file paths (except for platform-specific ones # like those for SPEC files) should be placed in this group so that # relative paths are properly expanded. - path_group = OptionGroup(parser, 'File/Directory Options') - path_group.add_option('--source-dir', - default='/testing', - help='Source directory. Must be a git checkout. ' - '(default: %default)') - path_group.add_option('--build-dir', - default='/tmp/salt-buildpackage', - help='Build root, will be removed if it exists ' - 'prior to running script. (default: %default)') - path_group.add_option('--artifact-dir', - default='/tmp/salt-packages', - help='Location where build artifacts should be ' - 'placed for Jenkins to retrieve them ' - '(default: %default)') + path_group = OptionGroup(parser, "File/Directory Options") + path_group.add_option( + "--source-dir", + default="/testing", + help="Source directory. Must be a git checkout. " "(default: %default)", + ) + path_group.add_option( + "--build-dir", + default="/tmp/salt-buildpackage", + help="Build root, will be removed if it exists " + "prior to running script. (default: %default)", + ) + path_group.add_option( + "--artifact-dir", + default="/tmp/salt-packages", + help="Location where build artifacts should be " + "placed for Jenkins to retrieve them " + "(default: %default)", + ) parser.add_option_group(path_group) # This group should also consist of nothing but file paths, which will be # normalized below. - rpm_group = OptionGroup(parser, 'RPM-specific File/Directory Options') - rpm_group.add_option('--spec', - dest='spec_file', - default='/tmp/salt.spec', - help='Spec file to use as a template to build RPM. ' - '(default: %default)') + rpm_group = OptionGroup(parser, "RPM-specific File/Directory Options") + rpm_group.add_option( + "--spec", + dest="spec_file", + default="/tmp/salt.spec", + help="Spec file to use as a template to build RPM. " "(default: %default)", + ) parser.add_option_group(rpm_group) opts = parser.parse_args()[0] @@ -121,38 +129,47 @@ def _init(): # Sanity checks problems = [] if not opts.platform: - problems.append('Platform (\'os\' grain) required') + problems.append("Platform ('os' grain) required") if not os.path.isdir(opts.source_dir): - problems.append('Source directory {0} not found' - .format(opts.source_dir)) + problems.append("Source directory {0} not found".format(opts.source_dir)) try: shutil.rmtree(opts.build_dir) except OSError as exc: if exc.errno not in (errno.ENOENT, errno.ENOTDIR): - problems.append('Unable to remove pre-existing destination ' - 'directory {0}: {1}'.format(opts.build_dir, exc)) + problems.append( + "Unable to remove pre-existing destination " + "directory {0}: {1}".format(opts.build_dir, exc) + ) finally: try: os.makedirs(opts.build_dir) except OSError as exc: - problems.append('Unable to create destination directory {0}: {1}' - .format(opts.build_dir, exc)) + problems.append( + "Unable to create destination directory {0}: {1}".format( + opts.build_dir, exc + ) + ) try: shutil.rmtree(opts.artifact_dir) except OSError as exc: if exc.errno not in (errno.ENOENT, errno.ENOTDIR): - problems.append('Unable to remove pre-existing artifact directory ' - '{0}: {1}'.format(opts.artifact_dir, exc)) + problems.append( + "Unable to remove pre-existing artifact directory " + "{0}: {1}".format(opts.artifact_dir, exc) + ) finally: try: os.makedirs(opts.artifact_dir) except OSError as exc: - problems.append('Unable to create artifact directory {0}: {1}' - .format(opts.artifact_dir, exc)) + problems.append( + "Unable to create artifact directory {0}: {1}".format( + opts.artifact_dir, exc + ) + ) # Create log file in the artifact dir so it is sent back to master if the # job fails - opts.log_file = os.path.join(opts.artifact_dir, 'salt-buildpackage.log') + opts.log_file = os.path.join(opts.artifact_dir, "salt-buildpackage.log") if problems: _abort(problems) @@ -161,9 +178,9 @@ def _init(): def _move(src, dst): - ''' + """ Wrapper around shutil.move() - ''' + """ try: os.remove(os.path.join(dst, os.path.basename(src))) except OSError as exc: @@ -177,75 +194,72 @@ def _move(src, dst): def _run_command(args): - log.info('Running command: {0}'.format(args)) - proc = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + log.info("Running command: {0}".format(args)) + proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() if stdout: - log.debug('Command output: \n{0}'.format(stdout)) + log.debug("Command output: \n{0}".format(stdout)) if stderr: log.error(stderr) - log.info('Return code: {0}'.format(proc.returncode)) + log.info("Return code: {0}".format(proc.returncode)) return stdout, stderr, proc.returncode -def _make_sdist(opts, python_bin='python'): +def _make_sdist(opts, python_bin="python"): os.chdir(opts.source_dir) - stdout, stderr, rcode = _run_command([python_bin, 'setup.py', 'sdist']) + stdout, stderr, rcode = _run_command([python_bin, "setup.py", "sdist"]) if rcode == 0: # Find the sdist with the most recently-modified metadata sdist_path = max( - glob.iglob(os.path.join(opts.source_dir, 'dist', 'salt-*.tar.gz')), - key=os.path.getctime + glob.iglob(os.path.join(opts.source_dir, "dist", "salt-*.tar.gz")), + key=os.path.getctime, ) - log.info('sdist is located at {0}'.format(sdist_path)) + log.info("sdist is located at {0}".format(sdist_path)) return sdist_path else: - _abort('Failed to create sdist') + _abort("Failed to create sdist") # BUILDER FUNCTIONS def build_centos(opts): - ''' + """ Build an RPM - ''' - log.info('Building CentOS RPM') - log.info('Detecting major release') + """ + log.info("Building CentOS RPM") + log.info("Detecting major release") try: - with open('/etc/redhat-release', 'r') as fp_: + with open("/etc/redhat-release", "r") as fp_: redhat_release = fp_.read().strip() - major_release = int(redhat_release.split()[2].split('.')[0]) + major_release = int(redhat_release.split()[2].split(".")[0]) except (ValueError, IndexError): - _abort('Unable to determine major release from /etc/redhat-release ' - 'contents: \'{0}\''.format(redhat_release)) + _abort( + "Unable to determine major release from /etc/redhat-release " + "contents: '{0}'".format(redhat_release) + ) except IOError as exc: - _abort('{0}'.format(exc)) + _abort("{0}".format(exc)) - log.info('major_release: {0}'.format(major_release)) + log.info("major_release: {0}".format(major_release)) - define_opts = [ - '--define', - '_topdir {0}'.format(os.path.join(opts.build_dir)) - ] - build_reqs = ['rpm-build'] + define_opts = ["--define", "_topdir {0}".format(os.path.join(opts.build_dir))] + build_reqs = ["rpm-build"] if major_release == 5: - python_bin = 'python26' - define_opts.extend(['--define', 'dist .el5']) - if os.path.exists('/etc/yum.repos.d/saltstack.repo'): - build_reqs.extend(['--enablerepo=saltstack']) - build_reqs.extend(['python26-devel']) + python_bin = "python26" + define_opts.extend(["--define", "dist .el5"]) + if os.path.exists("/etc/yum.repos.d/saltstack.repo"): + build_reqs.extend(["--enablerepo=saltstack"]) + build_reqs.extend(["python26-devel"]) elif major_release == 6: - build_reqs.extend(['python-devel']) + build_reqs.extend(["python-devel"]) elif major_release == 7: - build_reqs.extend(['python-devel', 'systemd-units']) + build_reqs.extend(["python-devel", "systemd-units"]) else: - _abort('Unsupported major release: {0}'.format(major_release)) + _abort("Unsupported major release: {0}".format(major_release)) # Install build deps - _run_command(['yum', '-y', 'install'] + build_reqs) + _run_command(["yum", "-y", "install"] + build_reqs) # Make the sdist try: @@ -256,77 +270,83 @@ def build_centos(opts): # Example tarball names: # - Git checkout: salt-2014.7.0rc1-1584-g666602e.tar.gz # - Tagged release: salt-2014.7.0.tar.gz - tarball_re = re.compile(r'^salt-([^-]+)(?:-(\d+)-(g[0-9a-f]+))?\.tar\.gz$') + tarball_re = re.compile(r"^salt-([^-]+)(?:-(\d+)-(g[0-9a-f]+))?\.tar\.gz$") try: base, offset, oid = tarball_re.match(os.path.basename(sdist)).groups() except AttributeError: - _abort('Unable to extract version info from sdist filename \'{0}\'' - .format(sdist)) + _abort("Unable to extract version info from sdist filename '{0}'".format(sdist)) if offset is None: salt_pkgver = salt_srcver = base else: - salt_pkgver = '.'.join((base, offset, oid)) - salt_srcver = '-'.join((base, offset, oid)) + salt_pkgver = ".".join((base, offset, oid)) + salt_srcver = "-".join((base, offset, oid)) - log.info('salt_pkgver: {0}'.format(salt_pkgver)) - log.info('salt_srcver: {0}'.format(salt_srcver)) + log.info("salt_pkgver: {0}".format(salt_pkgver)) + log.info("salt_srcver: {0}".format(salt_srcver)) # Setup build environment - for build_dir in 'BUILD BUILDROOT RPMS SOURCES SPECS SRPMS'.split(): + for build_dir in "BUILD BUILDROOT RPMS SOURCES SPECS SRPMS".split(): path = os.path.join(opts.build_dir, build_dir) try: os.makedirs(path) except OSError: pass if not os.path.isdir(path): - _abort('Unable to make directory: {0}'.format(path)) + _abort("Unable to make directory: {0}".format(path)) # Get sources into place - build_sources_path = os.path.join(opts.build_dir, 'SOURCES') - rpm_sources_path = os.path.join(opts.source_dir, 'pkg', 'rpm') + build_sources_path = os.path.join(opts.build_dir, "SOURCES") + rpm_sources_path = os.path.join(opts.source_dir, "pkg", "rpm") _move(sdist, build_sources_path) - for src in ('salt-master', 'salt-syndic', 'salt-minion', 'salt-api', - 'salt-master.service', 'salt-syndic.service', - 'salt-minion.service', 'salt-api.service', - 'README.fedora', 'logrotate.salt', 'salt.bash'): + for src in ( + "salt-master", + "salt-syndic", + "salt-minion", + "salt-api", + "salt-master.service", + "salt-syndic.service", + "salt-minion.service", + "salt-api.service", + "README.fedora", + "logrotate.salt", + "salt.bash", + ): shutil.copy(os.path.join(rpm_sources_path, src), build_sources_path) # Prepare SPEC file - spec_path = os.path.join(opts.build_dir, 'SPECS', 'salt.spec') - with open(opts.spec_file, 'r') as spec: + spec_path = os.path.join(opts.build_dir, "SPECS", "salt.spec") + with open(opts.spec_file, "r") as spec: spec_lines = spec.read().splitlines() - with open(spec_path, 'w') as fp_: + with open(spec_path, "w") as fp_: for line in spec_lines: - if line.startswith('%global srcver '): - line = '%global srcver {0}'.format(salt_srcver) - elif line.startswith('Version: '): - line = 'Version: {0}'.format(salt_pkgver) - fp_.write(line + '\n') + if line.startswith("%global srcver "): + line = "%global srcver {0}".format(salt_srcver) + elif line.startswith("Version: "): + line = "Version: {0}".format(salt_pkgver) + fp_.write(line + "\n") # Do the thing - cmd = ['rpmbuild', '-ba'] + cmd = ["rpmbuild", "-ba"] cmd.extend(define_opts) cmd.append(spec_path) stdout, stderr, rcode = _run_command(cmd) if rcode != 0: - _abort('Build failed.') + _abort("Build failed.") packages = glob.glob( os.path.join( opts.build_dir, - 'RPMS', - 'noarch', - 'salt-*{0}*.noarch.rpm'.format(salt_pkgver) + "RPMS", + "noarch", + "salt-*{0}*.noarch.rpm".format(salt_pkgver), ) ) packages.extend( glob.glob( os.path.join( - opts.build_dir, - 'SRPMS', - 'salt-{0}*.src.rpm'.format(salt_pkgver) + opts.build_dir, "SRPMS", "salt-{0}*.src.rpm".format(salt_pkgver) ) ) ) @@ -335,39 +355,46 @@ def build_centos(opts): # MAIN -if __name__ == '__main__': +if __name__ == "__main__": opts = _init() - print('Starting {0} build. Progress will be logged to {1}.' - .format(opts.platform, opts.log_file)) + print( + "Starting {0} build. Progress will be logged to {1}.".format( + opts.platform, opts.log_file + ) + ) # Setup logging - log_format = '%(asctime)s.%(msecs)03d %(levelname)s: %(message)s' - log_datefmt = '%H:%M:%S' - log_level = LOG_LEVELS[opts.log_level] \ - if opts.log_level in LOG_LEVELS \ - else LOG_LEVELS['warning'] - logging.basicConfig(filename=opts.log_file, - format=log_format, - datefmt=log_datefmt, - level=LOG_LEVELS[opts.log_level]) + log_format = "%(asctime)s.%(msecs)03d %(levelname)s: %(message)s" + log_datefmt = "%H:%M:%S" + log_level = ( + LOG_LEVELS[opts.log_level] + if opts.log_level in LOG_LEVELS + else LOG_LEVELS["warning"] + ) + logging.basicConfig( + filename=opts.log_file, + format=log_format, + datefmt=log_datefmt, + level=LOG_LEVELS[opts.log_level], + ) if opts.log_level not in LOG_LEVELS: - log.error('Invalid log level \'{0}\', falling back to \'warning\'' - .format(opts.log_level)) + log.error( + "Invalid log level '{0}', falling back to 'warning'".format(opts.log_level) + ) # Build for the specified platform if not opts.platform: - _abort('Platform required') - elif opts.platform.lower() == 'centos': + _abort("Platform required") + elif opts.platform.lower() == "centos": artifacts = build_centos(opts) else: - _abort('Unsupported platform \'{0}\''.format(opts.platform)) + _abort("Unsupported platform '{0}'".format(opts.platform)) - msg = ('Build complete. Artifacts will be stored in {0}' - .format(opts.artifact_dir)) + msg = "Build complete. Artifacts will be stored in {0}".format(opts.artifact_dir) log.info(msg) print(msg) # pylint: disable=C0325 for artifact in artifacts: shutil.copy(artifact, opts.artifact_dir) - log.info('Copied {0} to artifact directory'.format(artifact)) - log.info('Done!') + log.info("Copied {0} to artifact directory".format(artifact)) + log.info("Done!") diff --git a/tests/committer_parser.py b/tests/committer_parser.py index 1a7fbbd36f4..5633d1c3944 100644 --- a/tests/committer_parser.py +++ b/tests/committer_parser.py @@ -6,34 +6,39 @@ # Simple script to parse the output of 'git log' and generate some statistics. # May leverage GitHub API in the future # -''' +""" To use this commit parser script pipe git log into the stdin: git log | committer_parser.py -c - -''' +""" # pylint: disable=resource-leakage # Import python libs -from __future__ import absolute_import -from __future__ import print_function -import sys +from __future__ import absolute_import, print_function + +import datetime +import email.utils import getopt import re -import email.utils -import datetime +import sys class Usage(Exception): def __init__(self, msg): # pylint: disable=W0231 - self.msg = 'committer_parser.py [-c | --contributor-detail] - |' \ - ' <logfilename>\n' - self.msg += ' : Parse commit log from git and print number of ' \ - 'commits and unique committers\n' - self.msg += ' : by month. Accepts a filename or reads from stdin.\n' - self.msg += ' : -c | --contributor-detail generates output by ' \ - 'contributor, by month, in a tab-separated table\n' + self.msg = ( + "committer_parser.py [-c | --contributor-detail] - |" " <logfilename>\n" + ) + self.msg += ( + " : Parse commit log from git and print number of " + "commits and unique committers\n" + ) + self.msg += " : by month. Accepts a filename or reads from stdin.\n" + self.msg += ( + " : -c | --contributor-detail generates output by " + "contributor, by month, in a tab-separated table\n" + ) if msg: - self.msg += '\n' + self.msg += "\n" self.msg += msg @@ -43,42 +48,42 @@ def parse_date(datestr): def parse_gitlog(filename=None): - ''' + """ Parse out the gitlog cli data - ''' + """ results = {} commits = {} commits_by_contributor = {} - if not filename or filename == '-': + if not filename or filename == "-": fh = sys.stdin else: - fh = open(filename, 'r+') + fh = open(filename, "r+") try: commitcount = 0 for line in fh.readlines(): line = line.rstrip() - if line.startswith('commit '): + if line.startswith("commit "): new_commit = True commitcount += 1 continue - if line.startswith('Author:'): - author = re.match(r'Author:\s+(.*)\s+<(.*)>', line) + if line.startswith("Author:"): + author = re.match(r"Author:\s+(.*)\s+<(.*)>", line) if author: email = author.group(2) continue - if line.startswith('Date:'): + if line.startswith("Date:"): - isodate = re.match(r'Date:\s+(.*)', line) + isodate = re.match(r"Date:\s+(.*)", line) d = parse_date(isodate.group(1)) continue if len(line) < 2 and new_commit: new_commit = False - key = '{0}-{1}'.format(d.year, str(d.month).zfill(2)) + key = "{0}-{1}".format(d.year, str(d.month).zfill(2)) if key not in results: results[key] = [] @@ -105,30 +110,30 @@ def parse_gitlog(filename=None): def counts_by_contributor(commits_by_contributor, results): - output = '' + output = "" dates = sorted(results.keys()) for d in dates: - output += '\t{0}'.format(d) + output += "\t{0}".format(d) - output += '\n' + output += "\n" for email in sorted(commits_by_contributor.keys()): - output += '\'{0}'.format(email) + output += "'{0}".format(email) for d in dates: if d in commits_by_contributor[email]: - output += '\t{0}'.format(commits_by_contributor[email][d]) + output += "\t{0}".format(commits_by_contributor[email][d]) else: - output += '\t' - output += '\n' + output += "\t" + output += "\n" return output def count_results(results, commits): - result_str = '' - print('Date\tContributors\tCommits') + result_str = "" + print("Date\tContributors\tCommits") for k in sorted(results.keys()): - result_str += '{0}\t{1}\t{2}'.format(k, len(results[k]), commits[k]) - result_str += '\n' + result_str += "{0}\t{1}\t{2}".format(k, len(results[k]), commits[k]) + result_str += "\n" return result_str @@ -137,9 +142,11 @@ def main(argv=None): argv = sys.argv try: try: - opts, args = getopt.getopt(argv[1:], 'hc', ['help', 'contributor-detail']) + opts, args = getopt.getopt(argv[1:], "hc", ["help", "contributor-detail"]) if len(args) < 1: - raise Usage('committer_parser.py needs a filename or \'-\' to read from stdin') + raise Usage( + "committer_parser.py needs a filename or '-' to read from stdin" + ) except getopt.error as msg: raise Usage(msg) except Usage as err: @@ -147,13 +154,13 @@ def main(argv=None): return 2 if len(opts) > 0: - if '-h' in opts[0] or '--help' in opts[0]: + if "-h" in opts[0] or "--help" in opts[0]: return 0 data, counts, commits_by_contributor = parse_gitlog(filename=args[0]) if len(opts) > 0: - if '-c' or '--contributor-detail': + if "-c" or "--contributor-detail": print(counts_by_contributor(commits_by_contributor, data)) else: print(count_results(data, counts)) diff --git a/tests/conftest.py b/tests/conftest.py index c22f64d5a42..430473d192b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,29 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.conftest ~~~~~~~~~~~~~~ Prepare py.test for our test suite -''' +""" # pylint: disable=wrong-import-order,wrong-import-position,3rd-party-local-module-not-gated # pylint: disable=redefined-outer-name,invalid-name # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import fnmatch +import logging import os -import sys -import stat import pprint import shutil import socket -import fnmatch -import logging +import stat +import sys import tempfile import textwrap from contextlib import contextmanager +import _pytest.logging +import _pytest.skipping + +# Import 3rd-party libs +import psutil + +# Import pytest libs +import pytest +import salt.config + +# Import salt libs +import salt.loader +import salt.log.mixins +import salt.log.setup +import salt.utils.files +import salt.utils.path +import salt.utils.platform +import salt.utils.win_functions +from _pytest.mark.evaluate import MarkEvaluator + +# Import Pytest Salt libs +from pytestsalt.utils import cli_scripts +from salt.ext import six +from salt.serializers import yaml +from salt.utils.immutabletypes import freeze + +# Import test libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.sminion import create_sminion + TESTS_DIR = os.path.dirname(os.path.normpath(os.path.abspath(__file__))) CODE_DIR = os.path.dirname(TESTS_DIR) @@ -35,71 +66,48 @@ if CODE_DIR in sys.path: sys.path.remove(CODE_DIR) sys.path.insert(0, CODE_DIR) -# Import test libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.sminion import create_sminion - -# Import pytest libs -import pytest -import _pytest.logging -import _pytest.skipping -from _pytest.mark.evaluate import MarkEvaluator - -# Import 3rd-party libs -import psutil -from salt.ext import six - -# Import salt libs -import salt.loader -import salt.config -import salt.utils.files -import salt.utils.path -import salt.log.setup -import salt.log.mixins -import salt.utils.platform -import salt.utils.win_functions -from salt.serializers import yaml -from salt.utils.immutabletypes import freeze - -# Import Pytest Salt libs -from pytestsalt.utils import cli_scripts # Coverage -if 'COVERAGE_PROCESS_START' in os.environ: +if "COVERAGE_PROCESS_START" in os.environ: MAYBE_RUN_COVERAGE = True - COVERAGERC_FILE = os.environ['COVERAGE_PROCESS_START'] + COVERAGERC_FILE = os.environ["COVERAGE_PROCESS_START"] else: - COVERAGERC_FILE = os.path.join(CODE_DIR, '.coveragerc') - MAYBE_RUN_COVERAGE = sys.argv[0].endswith('pytest.py') or '_COVERAGE_RCFILE' in os.environ + COVERAGERC_FILE = os.path.join(CODE_DIR, ".coveragerc") + MAYBE_RUN_COVERAGE = ( + sys.argv[0].endswith("pytest.py") or "_COVERAGE_RCFILE" in os.environ + ) if MAYBE_RUN_COVERAGE: # Flag coverage to track suprocesses by pointing it to the right .coveragerc file - os.environ[str('COVERAGE_PROCESS_START')] = str(COVERAGERC_FILE) + os.environ[str("COVERAGE_PROCESS_START")] = str(COVERAGERC_FILE) # Define the pytest plugins we rely on -pytest_plugins = ['tempdir', 'helpers_namespace', 'salt-runtests-bridge'] +pytest_plugins = ["tempdir", "helpers_namespace", "salt-runtests-bridge"] # Define where not to collect tests from -collect_ignore = ['setup.py'] +collect_ignore = ["setup.py"] # Patch PyTest logging handlers -class LogCaptureHandler(salt.log.mixins.ExcInfoOnLogLevelFormatMixIn, - _pytest.logging.LogCaptureHandler): - ''' +class LogCaptureHandler( + salt.log.mixins.ExcInfoOnLogLevelFormatMixIn, _pytest.logging.LogCaptureHandler +): + """ Subclassing PyTest's LogCaptureHandler in order to add the exc_info_on_loglevel functionality. - ''' + """ _pytest.logging.LogCaptureHandler = LogCaptureHandler -class LiveLoggingStreamHandler(salt.log.mixins.ExcInfoOnLogLevelFormatMixIn, - _pytest.logging._LiveLoggingStreamHandler): - ''' +class LiveLoggingStreamHandler( + salt.log.mixins.ExcInfoOnLogLevelFormatMixIn, + _pytest.logging._LiveLoggingStreamHandler, +): + """ Subclassing PyTest's LiveLoggingStreamHandler in order to add the exc_info_on_loglevel functionality. - ''' + """ _pytest.logging._LiveLoggingStreamHandler = LiveLoggingStreamHandler @@ -112,7 +120,7 @@ for handler in logging.root.handlers[:]: # Reset the root logger to its default level(because salt changed it) logging.root.setLevel(logging.WARNING) -log = logging.getLogger('salt.testsuite') +log = logging.getLogger("salt.testsuite") # ----- PyTest Tempdir Plugin Hooks ---------------------------------------------------------------------------------> @@ -121,147 +129,158 @@ def pytest_tempdir_temproot(): # Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long # for unix sockets: ``error: AF_UNIX path too long`` # Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR} - if not sys.platform.startswith('darwin'): - tempdir = os.environ.get('TMPDIR') or tempfile.gettempdir() + if not sys.platform.startswith("darwin"): + tempdir = os.environ.get("TMPDIR") or tempfile.gettempdir() else: - tempdir = '/tmp' + tempdir = "/tmp" return os.path.abspath(os.path.realpath(tempdir)) def pytest_tempdir_basename(): - ''' + """ Return the temporary directory basename for the salt test suite. - ''' - return 'salt-tests-tmpdir' + """ + return "salt-tests-tmpdir" + + # <---- PyTest Tempdir Plugin Hooks ---------------------------------------------------------------------------------- # ----- CLI Options Setup -------------------------------------------------------------------------------------------> def pytest_addoption(parser): - ''' + """ register argparse-style options and ini-style config values. - ''' + """ parser.addoption( - '--sysinfo', + "--sysinfo", default=False, - action='store_true', - help='Print some system information.' + action="store_true", + help="Print some system information.", ) parser.addoption( - '--transport', - default='zeromq', - choices=('zeromq', 'tcp'), - help=('Select which transport to run the integration tests with, ' - 'zeromq or tcp. Default: %default') + "--transport", + default="zeromq", + choices=("zeromq", "tcp"), + help=( + "Select which transport to run the integration tests with, " + "zeromq or tcp. Default: %default" + ), ) - test_selection_group = parser.getgroup('Tests Selection') + test_selection_group = parser.getgroup("Tests Selection") test_selection_group.addoption( - '--ssh', - '--ssh-tests', - dest='ssh', - action='store_true', + "--ssh", + "--ssh-tests", + dest="ssh", + action="store_true", default=False, - help='Run salt-ssh tests. These tests will spin up a temporary ' - 'SSH server on your machine. In certain environments, this ' - 'may be insecure! Default: False' + help="Run salt-ssh tests. These tests will spin up a temporary " + "SSH server on your machine. In certain environments, this " + "may be insecure! Default: False", ) test_selection_group.addoption( - '--proxy', - '--proxy-tests', - dest='proxy', - action='store_true', + "--proxy", + "--proxy-tests", + dest="proxy", + action="store_true", default=False, - help='Run proxy tests' + help="Run proxy tests", ) test_selection_group.addoption( - '--run-destructive', - action='store_true', + "--run-destructive", + action="store_true", default=False, - help='Run destructive tests. These tests can include adding ' - 'or removing users from your system for example. ' - 'Default: False' + help="Run destructive tests. These tests can include adding " + "or removing users from your system for example. " + "Default: False", ) test_selection_group.addoption( - '--run-expensive', - action='store_true', + "--run-expensive", + action="store_true", default=False, - help='Run expensive tests. These tests usually involve costs ' - 'like for example bootstrapping a cloud VM. ' - 'Default: False' + help="Run expensive tests. These tests usually involve costs " + "like for example bootstrapping a cloud VM. " + "Default: False", ) - output_options_group = parser.getgroup('Output Options') + output_options_group = parser.getgroup("Output Options") output_options_group.addoption( - '--output-columns', + "--output-columns", default=80, type=int, - help='Number of maximum columns to use on the output' + help="Number of maximum columns to use on the output", ) output_options_group.addoption( - '--no-colors', - '--no-colours', + "--no-colors", + "--no-colours", default=False, - action='store_true', - help='Disable colour printing.' + action="store_true", + help="Disable colour printing.", ) # ----- Test Groups ---------------------------------------------------------------------------------------------> # This will allow running the tests in chunks test_selection_group.addoption( - '--test-group-count', dest='test-group-count', type=int, - help='The number of groups to split the tests into' + "--test-group-count", + dest="test-group-count", + type=int, + help="The number of groups to split the tests into", ) test_selection_group.addoption( - '--test-group', dest='test-group', type=int, - help='The group of tests that should be executed' + "--test-group", + dest="test-group", + type=int, + help="The group of tests that should be executed", ) # <---- Test Groups ---------------------------------------------------------------------------------------------- + + # <---- CLI Options Setup -------------------------------------------------------------------------------------------- # ----- Register Markers --------------------------------------------------------------------------------------------> @pytest.mark.trylast def pytest_configure(config): - ''' + """ called after command line options have been parsed and all plugins and initial conftest files been loaded. - ''' + """ for dirname in os.listdir(CODE_DIR): if not os.path.isdir(dirname): continue - if dirname != 'tests': - config.addinivalue_line('norecursedirs', os.path.join(CODE_DIR, dirname)) + if dirname != "tests": + config.addinivalue_line("norecursedirs", os.path.join(CODE_DIR, dirname)) - config.addinivalue_line('norecursedirs', os.path.join(CODE_DIR, 'templates')) - config.addinivalue_line('norecursedirs', os.path.join(CODE_DIR, 'tests/kitchen')) - config.addinivalue_line('norecursedirs', os.path.join(CODE_DIR, 'tests/support')) + config.addinivalue_line("norecursedirs", os.path.join(CODE_DIR, "templates")) + config.addinivalue_line("norecursedirs", os.path.join(CODE_DIR, "tests/kitchen")) + config.addinivalue_line("norecursedirs", os.path.join(CODE_DIR, "tests/support")) # Expose the markers we use to pytest CLI config.addinivalue_line( - 'markers', - 'destructive_test: Run destructive tests. These tests can include adding ' - 'or removing users from your system for example.' + "markers", + "destructive_test: Run destructive tests. These tests can include adding " + "or removing users from your system for example.", ) config.addinivalue_line( - 'markers', - 'skip_if_not_root: Skip if the current user is not `root`.' + "markers", "skip_if_not_root: Skip if the current user is not `root`." ) config.addinivalue_line( - 'markers', - 'skip_if_binaries_missing(*binaries, check_all=False, message=None): Skip if ' - 'any of the passed binaries are not found in path. If \'check_all\' is ' - '\'True\', then all binaries must be found.' + "markers", + "skip_if_binaries_missing(*binaries, check_all=False, message=None): Skip if " + "any of the passed binaries are not found in path. If 'check_all' is " + "'True', then all binaries must be found.", ) config.addinivalue_line( - 'markers', - 'requires_network(only_local_network=False): Skip if no networking is set up. ' - 'If \'only_local_network\' is \'True\', only the local network is checked.' + "markers", + "requires_network(only_local_network=False): Skip if no networking is set up. " + "If 'only_local_network' is 'True', only the local network is checked.", ) config.addinivalue_line( - 'markers', - 'requires_salt_modules(*required_module_names): Skip if at least one module is not available. ' + "markers", + "requires_salt_modules(*required_module_names): Skip if at least one module is not available. ", ) # Make sure the test suite "knows" this is a pytest test run RUNTIME_VARS.PYTEST_SESSION = True + + # <---- Register Markers --------------------------------------------------------------------------------------------- @@ -271,10 +290,12 @@ def set_max_open_files_limits(min_soft=3072, min_hard=4096): # Get current limits if salt.utils.platform.is_windows(): import win32file + prev_hard = win32file._getmaxstdio() prev_soft = 512 else: import resource + prev_soft, prev_hard = resource.getrlimit(resource.RLIMIT_NOFILE) # Check minimum required limits @@ -294,12 +315,12 @@ def set_max_open_files_limits(min_soft=3072, min_hard=4096): # Increase limits if set_limits: log.debug( - ' * Max open files settings is too low (soft: %s, hard: %s) for running the tests. ' - 'Trying to raise the limits to soft: %s, hard: %s', + " * Max open files settings is too low (soft: %s, hard: %s) for running the tests. " + "Trying to raise the limits to soft: %s, hard: %s", prev_soft, prev_hard, soft, - hard + hard, ) try: if salt.utils.platform.is_windows(): @@ -309,8 +330,8 @@ def set_max_open_files_limits(min_soft=3072, min_hard=4096): resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard)) except Exception as err: # pylint: disable=broad-except log.error( - 'Failed to raise the max open files settings -> %s. Please issue the following command ' - 'on your console: \'ulimit -u %s\'', + "Failed to raise the max open files settings -> %s. Please issue the following command " + "on your console: 'ulimit -u %s'", err, soft, ) @@ -320,23 +341,25 @@ def set_max_open_files_limits(min_soft=3072, min_hard=4096): def pytest_report_header(): soft, hard = set_max_open_files_limits() - return 'max open files; soft: {}; hard: {}'.format(soft, hard) + return "max open files; soft: {}; hard: {}".format(soft, hard) def pytest_runtest_logstart(nodeid): - ''' + """ implements the runtest_setup/call/teardown protocol for the given test item, including capturing exceptions and calling reporting hooks. - ''' - log.debug('>>>>> START >>>>> %s', nodeid) + """ + log.debug(">>>>> START >>>>> %s", nodeid) def pytest_runtest_logfinish(nodeid): - ''' + """ called after ``pytest_runtest_call`` - ''' - log.debug('<<<<< END <<<<<<< %s', nodeid) + """ + log.debug("<<<<< END <<<<<<< %s", nodeid) + + # <---- PyTest Tweaks ------------------------------------------------------------------------------------------------ @@ -354,76 +377,88 @@ def _has_unittest_attr(item, attr): @pytest.hookimpl(tryfirst=True) def pytest_runtest_setup(item): - ''' + """ Fixtures injection based on markers or test skips based on CLI arguments - ''' - destructive_tests_marker = item.get_closest_marker('destructive_test') - if destructive_tests_marker is not None or _has_unittest_attr(item, '__destructive_test__'): - if item.config.getoption('--run-destructive') is False: + """ + destructive_tests_marker = item.get_closest_marker("destructive_test") + if destructive_tests_marker is not None or _has_unittest_attr( + item, "__destructive_test__" + ): + if item.config.getoption("--run-destructive") is False: item._skipped_by_mark = True - pytest.skip('Destructive tests are disabled') - os.environ[str('DESTRUCTIVE_TESTS')] = str(item.config.getoption('--run-destructive')) + pytest.skip("Destructive tests are disabled") + os.environ[str("DESTRUCTIVE_TESTS")] = str( + item.config.getoption("--run-destructive") + ) - expensive_tests_marker = item.get_closest_marker('expensive_test') - if expensive_tests_marker is not None or _has_unittest_attr(item, '__expensive_test__'): - if item.config.getoption('--run-expensive') is False: + expensive_tests_marker = item.get_closest_marker("expensive_test") + if expensive_tests_marker is not None or _has_unittest_attr( + item, "__expensive_test__" + ): + if item.config.getoption("--run-expensive") is False: item._skipped_by_mark = True - pytest.skip('Expensive tests are disabled') - os.environ[str('EXPENSIVE_TESTS')] = str(item.config.getoption('--run-expensive')) + pytest.skip("Expensive tests are disabled") + os.environ[str("EXPENSIVE_TESTS")] = str(item.config.getoption("--run-expensive")) - skip_if_not_root_marker = item.get_closest_marker('skip_if_not_root') - if skip_if_not_root_marker is not None or _has_unittest_attr(item, '__skip_if_not_root__'): - if not sys.platform.startswith('win'): + skip_if_not_root_marker = item.get_closest_marker("skip_if_not_root") + if skip_if_not_root_marker is not None or _has_unittest_attr( + item, "__skip_if_not_root__" + ): + if not sys.platform.startswith("win"): if os.getuid() != 0: item._skipped_by_mark = True - pytest.skip('You must be logged in as root to run this test') + pytest.skip("You must be logged in as root to run this test") else: current_user = salt.utils.win_functions.get_current_user() - if current_user != 'SYSTEM': + if current_user != "SYSTEM": if not salt.utils.win_functions.is_admin(current_user): item._skipped_by_mark = True - pytest.skip('You must be logged in as an Administrator to run this test') + pytest.skip( + "You must be logged in as an Administrator to run this test" + ) - skip_if_binaries_missing_marker = item.get_closest_marker('skip_if_binaries_missing') + skip_if_binaries_missing_marker = item.get_closest_marker( + "skip_if_binaries_missing" + ) if skip_if_binaries_missing_marker is not None: binaries = skip_if_binaries_missing_marker.args if len(binaries) == 1: if isinstance(binaries[0], (list, tuple, set, frozenset)): binaries = binaries[0] - check_all = skip_if_binaries_missing_marker.kwargs.get('check_all', False) - message = skip_if_binaries_missing_marker.kwargs.get('message', None) + check_all = skip_if_binaries_missing_marker.kwargs.get("check_all", False) + message = skip_if_binaries_missing_marker.kwargs.get("message", None) if check_all: for binary in binaries: if salt.utils.path.which(binary) is None: item._skipped_by_mark = True pytest.skip( '{0}The "{1}" binary was not found'.format( - message and '{0}. '.format(message) or '', - binary + message and "{0}. ".format(message) or "", binary ) ) elif salt.utils.path.which_bin(binaries) is None: item._skipped_by_mark = True pytest.skip( - '{0}None of the following binaries was found: {1}'.format( - message and '{0}. '.format(message) or '', - ', '.join(binaries) + "{0}None of the following binaries was found: {1}".format( + message and "{0}. ".format(message) or "", ", ".join(binaries) ) ) - requires_network_marker = item.get_closest_marker('requires_network') + requires_network_marker = item.get_closest_marker("requires_network") if requires_network_marker is not None: - only_local_network = requires_network_marker.kwargs.get('only_local_network', False) + only_local_network = requires_network_marker.kwargs.get( + "only_local_network", False + ) has_local_network = False # First lets try if we have a local network. Inspired in verify_socket try: pubsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) retsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) pubsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - pubsock.bind(('', 18000)) + pubsock.bind(("", 18000)) pubsock.close() retsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - retsock.bind(('', 18001)) + retsock.bind(("", 18001)) retsock.close() has_local_network = True except socket.error: @@ -431,13 +466,11 @@ def pytest_runtest_setup(item): try: pubsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) retsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - pubsock.setsockopt( - socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 - ) - pubsock.bind(('', 18000)) + pubsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + pubsock.bind(("", 18000)) pubsock.close() retsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - retsock.bind(('', 18001)) + retsock.bind(("", 18001)) retsock.close() has_local_network = True except socket.error: @@ -449,14 +482,23 @@ def pytest_runtest_setup(item): # Since we're only supposed to check local network, and no # local network was detected, skip the test item._skipped_by_mark = True - pytest.skip('No local network was detected') + pytest.skip("No local network was detected") # We are using the google.com DNS records as numerical IPs to avoid # DNS lookups which could greatly slow down this check - for addr in ('173.194.41.198', '173.194.41.199', '173.194.41.200', - '173.194.41.201', '173.194.41.206', '173.194.41.192', - '173.194.41.193', '173.194.41.194', '173.194.41.195', - '173.194.41.196', '173.194.41.197'): + for addr in ( + "173.194.41.198", + "173.194.41.199", + "173.194.41.200", + "173.194.41.201", + "173.194.41.206", + "173.194.41.192", + "173.194.41.193", + "173.194.41.194", + "173.194.41.195", + "173.194.41.196", + "173.194.41.197", + ): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(0.25) @@ -469,12 +511,14 @@ def pytest_runtest_setup(item): continue else: item._skipped_by_mark = True - pytest.skip('No internet network connection was detected') + pytest.skip("No internet network connection was detected") - requires_salt_modules_marker = item.get_closest_marker('requires_salt_modules') + requires_salt_modules_marker = item.get_closest_marker("requires_salt_modules") if requires_salt_modules_marker is not None: required_salt_modules = requires_salt_modules_marker.args - if len(required_salt_modules) == 1 and isinstance(required_salt_modules[0], (list, tuple, set)): + if len(required_salt_modules) == 1 and isinstance( + required_salt_modules[0], (list, tuple, set) + ): required_salt_modules = required_salt_modules[0] required_salt_modules = set(required_salt_modules) sminion = create_sminion() @@ -493,8 +537,8 @@ def pytest_runtest_setup(item): for required_module_name in required_salt_modules: search_name = required_module_name - if '.' not in search_name: - search_name += '.*' + if "." not in search_name: + search_name += ".*" if not fnmatch.filter(available_modules, search_name): not_available_modules.add(required_module_name) cached_not_available_modules.add(required_module_name) @@ -502,29 +546,41 @@ def pytest_runtest_setup(item): if not_available_modules: item._skipped_by_mark = True if len(not_available_modules) == 1: - pytest.skip('Salt module \'{}\' is not available'.format(*not_available_modules)) - pytest.skip('Salt modules not available: {}'.format(', '.join(not_available_modules))) + pytest.skip( + "Salt module '{}' is not available".format(*not_available_modules) + ) + pytest.skip( + "Salt modules not available: {}".format( + ", ".join(not_available_modules) + ) + ) + + # <---- Test Setup --------------------------------------------------------------------------------------------------- # ----- Test Groups Selection ---------------------------------------------------------------------------------------> def get_group_size(total_items, total_groups): - ''' + """ Return the group size. - ''' + """ return int(total_items / total_groups) def get_group(items, group_count, group_size, group_id): - ''' + """ Get the items from the passed in group based on group size. - ''' + """ start = group_size * (group_id - 1) end = start + group_size total_items = len(items) if start >= total_items: - pytest.fail("Invalid test-group argument. start({})>=total_items({})".format(start, total_items)) + pytest.fail( + "Invalid test-group argument. start({})>=total_items({})".format( + start, total_items + ) + ) elif start < 0: pytest.fail("Invalid test-group argument. Start({})<0".format(start)) @@ -542,8 +598,8 @@ def pytest_collection_modifyitems(config, items): # Let PyTest or other plugins handle the initial collection yield - group_count = config.getoption('test-group-count') - group_id = config.getoption('test-group') + group_count = config.getoption("test-group-count") + group_id = config.getoption("test-group") if not group_count or not group_id: # We're not selection tests using groups, don't do any filtering @@ -556,14 +612,13 @@ def pytest_collection_modifyitems(config, items): # Replace all items in the list items[:] = tests_in_group - terminal_reporter = config.pluginmanager.get_plugin('terminalreporter') + terminal_reporter = config.pluginmanager.get_plugin("terminalreporter") terminal_reporter.write( - 'Running test group #{0} ({1} tests)\n'.format( - group_id, - len(items) - ), - yellow=True + "Running test group #{0} ({1} tests)\n".format(group_id, len(items)), + yellow=True, ) + + # <---- Test Groups Selection ---------------------------------------------------------------------------------------- @@ -578,9 +633,9 @@ if six.PY2: # Helper for mock_open: # Retrieve lines from read_data via a generator so that separate calls to # readline, read, and readlines are properly interleaved - data_as_list = ['{0}\n'.format(l) for l in read_data.split('\n')] + data_as_list = ["{0}\n".format(l) for l in read_data.split("\n")] - if data_as_list[-1] == '\n': + if data_as_list[-1] == "\n": # If the last line ended in a newline, the list comprehension will have an # extra entry that's just a newline. Remove this. data_as_list = data_as_list[:-1] @@ -594,7 +649,7 @@ if six.PY2: yield line @pytest.helpers.mock.register - def mock_open(mock=None, read_data=''): + def mock_open(mock=None, read_data=""): """ A helper function to create a mock to replace the use of `open`. It works for `open` called directly or used as a context manager. @@ -606,7 +661,7 @@ if six.PY2: `read_data` is a string for the `read` methoddline`, and `readlines` of the file handle to return. This is an empty string by default. """ - _mock = pytest.importorskip('mock', minversion='2.0.0') + _mock = pytest.importorskip("mock", minversion="2.0.0") def _readlines_side_effect(*args, **kwargs): if handle.readlines.return_value is not None: @@ -616,7 +671,7 @@ if six.PY2: def _read_side_effect(*args, **kwargs): if handle.read.return_value is not None: return handle.read.return_value - return ''.join(_data) + return "".join(_data) def _readline_side_effect(): if handle.readline.return_value is not None: @@ -630,7 +685,7 @@ if six.PY2: file_spec = file # pylint: disable=undefined-variable if mock is None: - mock = _mock.MagicMock(name='open', spec=open) + mock = _mock.MagicMock(name="open", spec=open) handle = _mock.MagicMock(spec=file_spec) handle.__enter__.return_value = handle @@ -648,10 +703,13 @@ if six.PY2: mock.return_value = handle return mock + + else: + @pytest.helpers.mock.register - def mock_open(mock=None, read_data=''): - _mock = pytest.importorskip('mock', minversion='2.0.0') + def mock_open(mock=None, read_data=""): + _mock = pytest.importorskip("mock", minversion="2.0.0") return _mock.mock_open(mock=mock, read_data=read_data) @@ -678,7 +736,7 @@ def temp_file(name, contents=None, directory=None, strip_first_newline=True): file_directory = os.path.dirname(file_path) if contents is not None: if contents: - if contents.startswith('\n') and strip_first_newline: + if contents.startswith("\n") and strip_first_newline: contents = contents[1:] file_contents = textwrap.dedent(contents) else: @@ -688,7 +746,7 @@ def temp_file(name, contents=None, directory=None, strip_first_newline=True): if not os.path.isdir(file_directory): os.makedirs(file_directory) if contents is not None: - with salt.utils.files.fopen(file_path, 'w') as wfh: + with salt.utils.files.fopen(file_path, "w") as wfh: wfh.write(file_contents) yield file_path @@ -702,305 +760,316 @@ def temp_file(name, contents=None, directory=None, strip_first_newline=True): @pytest.helpers.register -def temp_state_file(name, contents, saltenv='base', strip_first_newline=True): +def temp_state_file(name, contents, saltenv="base", strip_first_newline=True): - if saltenv == 'base': + if saltenv == "base": directory = RUNTIME_VARS.TMP_STATE_TREE - elif saltenv == 'prod': + elif saltenv == "prod": directory = RUNTIME_VARS.TMP_PRODENV_STATE_TREE else: - raise RuntimeError('"saltenv" can only be "base" or "prod", not "{}"'.format(saltenv)) - return temp_file(name, contents, directory=directory, strip_first_newline=strip_first_newline) + raise RuntimeError( + '"saltenv" can only be "base" or "prod", not "{}"'.format(saltenv) + ) + return temp_file( + name, contents, directory=directory, strip_first_newline=strip_first_newline + ) + + # <---- Pytest Helpers ----------------------------------------------------------------------------------------------- # ----- Fixtures Overrides ------------------------------------------------------------------------------------------> # ----- Generate CLI Scripts ----------------------------------------------------------------------------------------> -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def cli_master_script_name(): - ''' + """ Return the CLI script basename - ''' - return 'cli_salt_master.py' + """ + return "cli_salt_master.py" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def cli_minion_script_name(): - ''' + """ Return the CLI script basename - ''' - return 'cli_salt_minion.py' + """ + return "cli_salt_minion.py" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def cli_salt_script_name(): - ''' + """ Return the CLI script basename - ''' - return 'cli_salt.py' + """ + return "cli_salt.py" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def cli_run_script_name(): - ''' + """ Return the CLI script basename - ''' - return 'cli_salt_run.py' + """ + return "cli_salt_run.py" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def cli_key_script_name(): - ''' + """ Return the CLI script basename - ''' - return 'cli_salt_key.py' + """ + return "cli_salt_key.py" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def cli_call_script_name(): - ''' + """ Return the CLI script basename - ''' - return 'cli_salt_call.py' + """ + return "cli_salt_call.py" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def cli_syndic_script_name(): - ''' + """ Return the CLI script basename - ''' - return 'cli_salt_syndic.py' + """ + return "cli_salt_syndic.py" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def cli_ssh_script_name(): - ''' + """ Return the CLI script basename - ''' - return 'cli_salt_ssh.py' + """ + return "cli_salt_ssh.py" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def cli_proxy_script_name(): - ''' + """ Return the CLI script basename - ''' - return 'cli_salt_proxy.py' + """ + return "cli_salt_proxy.py" -@pytest.fixture(scope='session') -def cli_bin_dir(tempdir, - request, - python_executable_path, - cli_master_script_name, - cli_minion_script_name, - cli_salt_script_name, - cli_call_script_name, - cli_key_script_name, - cli_run_script_name, - cli_ssh_script_name, - cli_syndic_script_name, - cli_proxy_script_name): - ''' +@pytest.fixture(scope="session") +def cli_bin_dir( + tempdir, + request, + python_executable_path, + cli_master_script_name, + cli_minion_script_name, + cli_salt_script_name, + cli_call_script_name, + cli_key_script_name, + cli_run_script_name, + cli_ssh_script_name, + cli_syndic_script_name, + cli_proxy_script_name, +): + """ Return the path to the CLI script directory to use - ''' - tmp_cli_scripts_dir = tempdir.join('cli-scrips-bin') + """ + tmp_cli_scripts_dir = tempdir.join("cli-scrips-bin") # Make sure we re-write the scripts every time we start the tests shutil.rmtree(tmp_cli_scripts_dir.strpath, ignore_errors=True) tmp_cli_scripts_dir.ensure(dir=True) cli_bin_dir_path = tmp_cli_scripts_dir.strpath # Now that we have the CLI directory created, lets generate the required CLI scripts to run salt's test suite - for script_name in (cli_master_script_name, - cli_minion_script_name, - cli_call_script_name, - cli_key_script_name, - cli_run_script_name, - cli_salt_script_name, - cli_ssh_script_name, - cli_syndic_script_name, - cli_proxy_script_name): - original_script_name = os.path.splitext(script_name)[0].split('cli_')[-1].replace('_', '-') + for script_name in ( + cli_master_script_name, + cli_minion_script_name, + cli_call_script_name, + cli_key_script_name, + cli_run_script_name, + cli_salt_script_name, + cli_ssh_script_name, + cli_syndic_script_name, + cli_proxy_script_name, + ): + original_script_name = ( + os.path.splitext(script_name)[0].split("cli_")[-1].replace("_", "-") + ) cli_scripts.generate_script( bin_dir=cli_bin_dir_path, script_name=original_script_name, executable=sys.executable, code_dir=CODE_DIR, - inject_sitecustomize=MAYBE_RUN_COVERAGE + inject_sitecustomize=MAYBE_RUN_COVERAGE, ) # Return the CLI bin dir value return cli_bin_dir_path + + # <---- Generate CLI Scripts ----------------------------------------------------------------------------------------- # ----- Salt Configuration ------------------------------------------------------------------------------------------> -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_master_of_masters_id(): - ''' + """ Returns the master of masters id - ''' - return 'syndic_master' + """ + return "syndic_master" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_master_id(): - ''' + """ Returns the session scoped master id - ''' - return 'master' + """ + return "master" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_minion_id(): - ''' + """ Returns the session scoped minion id - ''' - return 'minion' + """ + return "minion" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_secondary_minion_id(): - ''' + """ Returns the session scoped secondary minion id - ''' - return 'sub_minion' + """ + return "sub_minion" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_syndic_id(): - ''' + """ Returns the session scoped syndic id - ''' - return 'syndic' + """ + return "syndic" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_proxy_id(): - ''' + """ Returns the session scoped proxy id - ''' - return 'proxytest' + """ + return "proxytest" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def salt_fail_hard(): - ''' + """ Return the salt fail hard value - ''' + """ return True -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_master_default_options(request, session_root_dir): - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'master')) as rfh: + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, "master")) as rfh: opts = yaml.deserialize(rfh.read()) - tests_known_hosts_file = session_root_dir.join('salt_ssh_known_hosts').strpath - with salt.utils.files.fopen(tests_known_hosts_file, 'w') as known_hosts: - known_hosts.write('') + tests_known_hosts_file = session_root_dir.join("salt_ssh_known_hosts").strpath + with salt.utils.files.fopen(tests_known_hosts_file, "w") as known_hosts: + known_hosts.write("") - opts['known_hosts_file'] = tests_known_hosts_file - opts['syndic_master'] = 'localhost' - opts['transport'] = request.config.getoption('--transport') + opts["known_hosts_file"] = tests_known_hosts_file + opts["syndic_master"] = "localhost" + opts["transport"] = request.config.getoption("--transport") # Config settings to test `event_return` - if 'returner_dirs' not in opts: - opts['returner_dirs'] = [] - opts['returner_dirs'].append(os.path.join(RUNTIME_VARS.FILES, 'returners')) - opts['event_return'] = 'runtests_noop' + if "returner_dirs" not in opts: + opts["returner_dirs"] = [] + opts["returner_dirs"].append(os.path.join(RUNTIME_VARS.FILES, "returners")) + opts["event_return"] = "runtests_noop" return opts -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_master_config_overrides(session_root_dir): ext_pillar = [] if salt.utils.platform.is_windows(): ext_pillar.append( - {'cmd_yaml': 'type {0}'.format(os.path.join(RUNTIME_VARS.FILES, 'ext.yaml'))} + { + "cmd_yaml": "type {0}".format( + os.path.join(RUNTIME_VARS.FILES, "ext.yaml") + ) + } ) else: ext_pillar.append( - {'cmd_yaml': 'cat {0}'.format(os.path.join(RUNTIME_VARS.FILES, 'ext.yaml'))} + {"cmd_yaml": "cat {0}".format(os.path.join(RUNTIME_VARS.FILES, "ext.yaml"))} ) ext_pillar.append( { - 'file_tree': { - 'root_dir': os.path.join(RUNTIME_VARS.PILLAR_DIR, 'base', 'file_tree'), - 'follow_dir_links': False, - 'keep_newline': True + "file_tree": { + "root_dir": os.path.join(RUNTIME_VARS.PILLAR_DIR, "base", "file_tree"), + "follow_dir_links": False, + "keep_newline": True, } } ) # We need to copy the extension modules into the new master root_dir or # it will be prefixed by it - extension_modules_path = session_root_dir.join('extension_modules').strpath + extension_modules_path = session_root_dir.join("extension_modules").strpath if not os.path.exists(extension_modules_path): shutil.copytree( - os.path.join( - RUNTIME_VARS.FILES, 'extension_modules' - ), - extension_modules_path + os.path.join(RUNTIME_VARS.FILES, "extension_modules"), + extension_modules_path, ) # Copy the autosign_file to the new master root_dir - autosign_file_path = session_root_dir.join('autosign_file').strpath + autosign_file_path = session_root_dir.join("autosign_file").strpath shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'autosign_file'), - autosign_file_path + os.path.join(RUNTIME_VARS.FILES, "autosign_file"), autosign_file_path ) # all read, only owner write - autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + autosign_file_permissions = ( + stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + ) os.chmod(autosign_file_path, autosign_file_permissions) - pytest_stop_sending_events_file = session_root_dir.join('pytest_stop_sending_events_file').strpath - with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: - wfh.write('') + pytest_stop_sending_events_file = session_root_dir.join( + "pytest_stop_sending_events_file" + ).strpath + with salt.utils.files.fopen(pytest_stop_sending_events_file, "w") as wfh: + wfh.write("") return { - 'pillar_opts': True, - 'ext_pillar': ext_pillar, - 'extension_modules': extension_modules_path, - 'file_roots': { - 'base': [ - os.path.join(RUNTIME_VARS.FILES, 'file', 'base'), - ], + "pillar_opts": True, + "ext_pillar": ext_pillar, + "extension_modules": extension_modules_path, + "file_roots": { + "base": [os.path.join(RUNTIME_VARS.FILES, "file", "base")], # Alternate root to test __env__ choices - 'prod': [ - os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), - ] + "prod": [os.path.join(RUNTIME_VARS.FILES, "file", "prod")], }, - 'pillar_roots': { - 'base': [ - os.path.join(RUNTIME_VARS.FILES, 'pillar', 'base'), - ] - }, - 'reactor': [ + "pillar_roots": {"base": [os.path.join(RUNTIME_VARS.FILES, "pillar", "base")]}, + "reactor": [ { - 'salt/minion/*/start': [ - os.path.join(RUNTIME_VARS.FILES, 'reactor-sync-minion.sls') + "salt/minion/*/start": [ + os.path.join(RUNTIME_VARS.FILES, "reactor-sync-minion.sls") ], }, { - 'salt/test/reactor': [ - os.path.join(RUNTIME_VARS.FILES, 'reactor-test.sls') + "salt/test/reactor": [ + os.path.join(RUNTIME_VARS.FILES, "reactor-test.sls") ], - } + }, ], - 'pytest_stop_sending_events_file': pytest_stop_sending_events_file + "pytest_stop_sending_events_file": pytest_stop_sending_events_file, } -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_minion_default_options(request, tempdir): - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'minion')) as rfh: + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, "minion")) as rfh: opts = yaml.deserialize(rfh.read()) - opts['hosts.file'] = tempdir.join('hosts').strpath - opts['aliases.file'] = tempdir.join('aliases').strpath - opts['transport'] = request.config.getoption('--transport') + opts["hosts.file"] = tempdir.join("hosts").strpath + opts["aliases.file"] = tempdir.join("aliases").strpath + opts["transport"] = request.config.getoption("--transport") return opts @@ -1016,21 +1085,23 @@ def _get_virtualenv_binary_path(): real_prefix = sys.real_prefix # The above attribute exists, this is a virtualenv if salt.utils.platform.is_windows(): - virtualenv_binary = os.path.join(real_prefix, 'Scripts', 'virtualenv.exe') + virtualenv_binary = os.path.join( + real_prefix, "Scripts", "virtualenv.exe" + ) else: # We need to remove the virtualenv from PATH or we'll get the virtualenv binary # from within the virtualenv, we don't want that - path = os.environ.get('PATH') + path = os.environ.get("PATH") if path is not None: path_items = path.split(os.pathsep) for item in path_items[:]: if item.startswith(sys.base_prefix): path_items.remove(item) - os.environ['PATH'] = os.pathsep.join(path_items) - virtualenv_binary = salt.utils.path.which('virtualenv') + os.environ["PATH"] = os.pathsep.join(path_items) + virtualenv_binary = salt.utils.path.which("virtualenv") if path is not None: # Restore previous environ PATH - os.environ['PATH'] = path + os.environ["PATH"] = path if not virtualenv_binary.startswith(real_prefix): virtualenv_binary = None if virtualenv_binary and not os.path.exists(virtualenv_binary): @@ -1043,204 +1114,218 @@ def _get_virtualenv_binary_path(): return virtualenv_binary -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_minion_config_overrides(): opts = { - 'file_roots': { - 'base': [ - os.path.join(RUNTIME_VARS.FILES, 'file', 'base'), - ], + "file_roots": { + "base": [os.path.join(RUNTIME_VARS.FILES, "file", "base")], # Alternate root to test __env__ choices - 'prod': [ - os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), - ] - }, - 'pillar_roots': { - 'base': [ - os.path.join(RUNTIME_VARS.FILES, 'pillar', 'base'), - ] + "prod": [os.path.join(RUNTIME_VARS.FILES, "file", "prod")], }, + "pillar_roots": {"base": [os.path.join(RUNTIME_VARS.FILES, "pillar", "base")]}, } virtualenv_binary = _get_virtualenv_binary_path() if virtualenv_binary: - opts['venv_bin'] = virtualenv_binary + opts["venv_bin"] = virtualenv_binary return opts -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_secondary_minion_default_options(request, tempdir): - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'sub_minion')) as rfh: + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.CONF_DIR, "sub_minion") + ) as rfh: opts = yaml.deserialize(rfh.read()) - opts['hosts.file'] = tempdir.join('hosts').strpath - opts['aliases.file'] = tempdir.join('aliases').strpath - opts['transport'] = request.config.getoption('--transport') + opts["hosts.file"] = tempdir.join("hosts").strpath + opts["aliases.file"] = tempdir.join("aliases").strpath + opts["transport"] = request.config.getoption("--transport") return opts -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_seconary_minion_config_overrides(): opts = {} virtualenv_binary = _get_virtualenv_binary_path() if virtualenv_binary: - opts['venv_bin'] = virtualenv_binary + opts["venv_bin"] = virtualenv_binary return opts -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_master_of_masters_default_options(request, tempdir): - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic_master')) as rfh: + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.CONF_DIR, "syndic_master") + ) as rfh: opts = yaml.deserialize(rfh.read()) - opts['hosts.file'] = tempdir.join('hosts').strpath - opts['aliases.file'] = tempdir.join('aliases').strpath - opts['transport'] = request.config.getoption('--transport') + opts["hosts.file"] = tempdir.join("hosts").strpath + opts["aliases.file"] = tempdir.join("aliases").strpath + opts["transport"] = request.config.getoption("--transport") return opts -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_master_of_masters_config_overrides(session_master_of_masters_root_dir): if salt.utils.platform.is_windows(): - ext_pillar = {'cmd_yaml': 'type {0}'.format(os.path.join(RUNTIME_VARS.FILES, 'ext.yaml'))} + ext_pillar = { + "cmd_yaml": "type {0}".format(os.path.join(RUNTIME_VARS.FILES, "ext.yaml")) + } else: - ext_pillar = {'cmd_yaml': 'cat {0}'.format(os.path.join(RUNTIME_VARS.FILES, 'ext.yaml'))} + ext_pillar = { + "cmd_yaml": "cat {0}".format(os.path.join(RUNTIME_VARS.FILES, "ext.yaml")) + } # We need to copy the extension modules into the new master root_dir or # it will be prefixed by it - extension_modules_path = session_master_of_masters_root_dir.join('extension_modules').strpath + extension_modules_path = session_master_of_masters_root_dir.join( + "extension_modules" + ).strpath if not os.path.exists(extension_modules_path): shutil.copytree( - os.path.join( - RUNTIME_VARS.FILES, 'extension_modules' - ), - extension_modules_path + os.path.join(RUNTIME_VARS.FILES, "extension_modules"), + extension_modules_path, ) # Copy the autosign_file to the new master root_dir - autosign_file_path = session_master_of_masters_root_dir.join('autosign_file').strpath + autosign_file_path = session_master_of_masters_root_dir.join( + "autosign_file" + ).strpath shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'autosign_file'), - autosign_file_path + os.path.join(RUNTIME_VARS.FILES, "autosign_file"), autosign_file_path ) # all read, only owner write - autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + autosign_file_permissions = ( + stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + ) os.chmod(autosign_file_path, autosign_file_permissions) - pytest_stop_sending_events_file = session_master_of_masters_root_dir.join('pytest_stop_sending_events_file').strpath - with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: - wfh.write('') + pytest_stop_sending_events_file = session_master_of_masters_root_dir.join( + "pytest_stop_sending_events_file" + ).strpath + with salt.utils.files.fopen(pytest_stop_sending_events_file, "w") as wfh: + wfh.write("") return { - 'ext_pillar': [ext_pillar], - 'extension_modules': extension_modules_path, - 'file_roots': { - 'base': [ - os.path.join(RUNTIME_VARS.FILES, 'file', 'base'), - ], + "ext_pillar": [ext_pillar], + "extension_modules": extension_modules_path, + "file_roots": { + "base": [os.path.join(RUNTIME_VARS.FILES, "file", "base")], # Alternate root to test __env__ choices - 'prod': [ - os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), - ] + "prod": [os.path.join(RUNTIME_VARS.FILES, "file", "prod")], }, - 'pillar_roots': { - 'base': [ - os.path.join(RUNTIME_VARS.FILES, 'pillar', 'base'), - ] - }, - 'pytest_stop_sending_events_file': pytest_stop_sending_events_file + "pillar_roots": {"base": [os.path.join(RUNTIME_VARS.FILES, "pillar", "base")]}, + "pytest_stop_sending_events_file": pytest_stop_sending_events_file, } -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_syndic_master_default_options(request, tempdir): - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic_master')) as rfh: + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.CONF_DIR, "syndic_master") + ) as rfh: opts = yaml.deserialize(rfh.read()) - opts['hosts.file'] = tempdir.join('hosts').strpath - opts['aliases.file'] = tempdir.join('aliases').strpath - opts['transport'] = request.config.getoption('--transport') + opts["hosts.file"] = tempdir.join("hosts").strpath + opts["aliases.file"] = tempdir.join("aliases").strpath + opts["transport"] = request.config.getoption("--transport") return opts -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_syndic_default_options(request, tempdir): - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic')) as rfh: + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, "syndic")) as rfh: opts = yaml.deserialize(rfh.read()) - opts['hosts.file'] = tempdir.join('hosts').strpath - opts['aliases.file'] = tempdir.join('aliases').strpath - opts['transport'] = request.config.getoption('--transport') + opts["hosts.file"] = tempdir.join("hosts").strpath + opts["aliases.file"] = tempdir.join("aliases").strpath + opts["transport"] = request.config.getoption("--transport") return opts -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_proxy_default_options(request, tempdir): - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'proxy')) as rfh: + with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, "proxy")) as rfh: opts = yaml.deserialize(rfh.read()) - opts['hosts.file'] = tempdir.join('hosts').strpath - opts['aliases.file'] = tempdir.join('aliases').strpath - opts['transport'] = request.config.getoption('--transport') + opts["hosts.file"] = tempdir.join("hosts").strpath + opts["aliases.file"] = tempdir.join("aliases").strpath + opts["transport"] = request.config.getoption("--transport") return opts -@pytest.fixture(scope='session', autouse=True) -def bridge_pytest_and_runtests(reap_stray_processes, - session_root_dir, - session_conf_dir, - session_secondary_conf_dir, - session_syndic_conf_dir, - session_master_of_masters_conf_dir, - session_base_env_pillar_tree_root_dir, - session_base_env_state_tree_root_dir, - session_prod_env_state_tree_root_dir, - session_master_config, - session_minion_config, - session_secondary_minion_config, - session_master_of_masters_config, - session_syndic_config): +@pytest.fixture(scope="session", autouse=True) +def bridge_pytest_and_runtests( + reap_stray_processes, + session_root_dir, + session_conf_dir, + session_secondary_conf_dir, + session_syndic_conf_dir, + session_master_of_masters_conf_dir, + session_base_env_pillar_tree_root_dir, + session_base_env_state_tree_root_dir, + session_prod_env_state_tree_root_dir, + session_master_config, + session_minion_config, + session_secondary_minion_config, + session_master_of_masters_config, + session_syndic_config, +): # Make sure unittest2 classes know their paths RUNTIME_VARS.TMP_ROOT_DIR = session_root_dir.realpath().strpath RUNTIME_VARS.TMP_CONF_DIR = session_conf_dir.realpath().strpath RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR = session_secondary_conf_dir.realpath().strpath - RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR = session_master_of_masters_conf_dir.realpath().strpath + RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR = ( + session_master_of_masters_conf_dir.realpath().strpath + ) RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR = session_syndic_conf_dir.realpath().strpath - RUNTIME_VARS.TMP_PILLAR_TREE = session_base_env_pillar_tree_root_dir.realpath().strpath - RUNTIME_VARS.TMP_STATE_TREE = session_base_env_state_tree_root_dir.realpath().strpath - RUNTIME_VARS.TMP_PRODENV_STATE_TREE = session_prod_env_state_tree_root_dir.realpath().strpath + RUNTIME_VARS.TMP_PILLAR_TREE = ( + session_base_env_pillar_tree_root_dir.realpath().strpath + ) + RUNTIME_VARS.TMP_STATE_TREE = ( + session_base_env_state_tree_root_dir.realpath().strpath + ) + RUNTIME_VARS.TMP_PRODENV_STATE_TREE = ( + session_prod_env_state_tree_root_dir.realpath().strpath + ) # Make sure unittest2 uses the pytest generated configuration - RUNTIME_VARS.RUNTIME_CONFIGS['master'] = freeze(session_master_config) - RUNTIME_VARS.RUNTIME_CONFIGS['minion'] = freeze(session_minion_config) - RUNTIME_VARS.RUNTIME_CONFIGS['sub_minion'] = freeze(session_secondary_minion_config) - RUNTIME_VARS.RUNTIME_CONFIGS['syndic_master'] = freeze(session_master_of_masters_config) - RUNTIME_VARS.RUNTIME_CONFIGS['syndic'] = freeze(session_syndic_config) - RUNTIME_VARS.RUNTIME_CONFIGS['client_config'] = freeze( - salt.config.client_config(session_conf_dir.join('master').strpath) + RUNTIME_VARS.RUNTIME_CONFIGS["master"] = freeze(session_master_config) + RUNTIME_VARS.RUNTIME_CONFIGS["minion"] = freeze(session_minion_config) + RUNTIME_VARS.RUNTIME_CONFIGS["sub_minion"] = freeze(session_secondary_minion_config) + RUNTIME_VARS.RUNTIME_CONFIGS["syndic_master"] = freeze( + session_master_of_masters_config + ) + RUNTIME_VARS.RUNTIME_CONFIGS["syndic"] = freeze(session_syndic_config) + RUNTIME_VARS.RUNTIME_CONFIGS["client_config"] = freeze( + salt.config.client_config(session_conf_dir.join("master").strpath) ) # Copy configuration files and directories which are not automatically generated for entry in os.listdir(RUNTIME_VARS.CONF_DIR): - if entry in ('master', 'minion', 'sub_minion', 'syndic', 'syndic_master', 'proxy'): + if entry in ( + "master", + "minion", + "sub_minion", + "syndic", + "syndic_master", + "proxy", + ): # These have runtime computed values and are handled by pytest-salt fixtures continue entry_path = os.path.join(RUNTIME_VARS.CONF_DIR, entry) if os.path.isfile(entry_path): - shutil.copy( - entry_path, - os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry) - ) + shutil.copy(entry_path, os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry)) elif os.path.isdir(entry_path): - shutil.copytree( - entry_path, - os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry) - ) + shutil.copytree(entry_path, os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry)) + + # <---- Salt Configuration ------------------------------------------------------------------------------------------- # <---- Fixtures Overrides ------------------------------------------------------------------------------------------- @@ -1253,8 +1338,8 @@ class GrainsMarkEvaluator(MarkEvaluator): item_globals = super(GrainsMarkEvaluator, self)._getglobals() if GrainsMarkEvaluator._cached_grains is None: sminion = create_sminion() - GrainsMarkEvaluator._cached_grains = sminion.opts['grains'].copy() - item_globals['grains'] = GrainsMarkEvaluator._cached_grains.copy() + GrainsMarkEvaluator._cached_grains = sminion.opts["grains"].copy() + item_globals["grains"] = GrainsMarkEvaluator._cached_grains.copy() return item_globals @@ -1264,26 +1349,26 @@ _pytest.skipping.MarkEvaluator = GrainsMarkEvaluator # ----- Custom Fixtures ---------------------------------------------------------------------------------------------> -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def reap_stray_processes(): # Run tests yield children = psutil.Process(os.getpid()).children(recursive=True) if not children: - log.info('No astray processes found') + log.info("No astray processes found") return def on_terminate(proc): - log.debug('Process %s terminated with exit code %s', proc, proc.returncode) + log.debug("Process %s terminated with exit code %s", proc, proc.returncode) if children: # Reverse the order, sublings first, parents after children.reverse() log.warning( - 'Test suite left %d astray processes running. Killing those processes:\n%s', + "Test suite left %d astray processes running. Killing those processes:\n%s", len(children), - pprint.pformat(children) + pprint.pformat(children), ) _, alive = psutil.wait_procs(children, timeout=3, callback=on_terminate) @@ -1294,11 +1379,17 @@ def reap_stray_processes(): if alive: # Give up for child in alive: - log.warning('Process %s survived SIGKILL, giving up:\n%s', child, pprint.pformat(child.as_dict())) + log.warning( + "Process %s survived SIGKILL, giving up:\n%s", + child, + pprint.pformat(child.as_dict()), + ) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def grains(request): sminion = create_sminion() - return sminion.opts['grains'].copy() + return sminion.opts["grains"].copy() + + # <---- Custom Fixtures ---------------------------------------------------------------------------------------------- diff --git a/tests/consist.py b/tests/consist.py index 6b3890ac2ec..7fa33fe5522 100644 --- a/tests/consist.py +++ b/tests/consist.py @@ -3,10 +3,11 @@ # Import Python libs from __future__ import absolute_import, print_function -import subprocess + import hashlib -import pprint import optparse +import pprint +import subprocess # Import Salt libs import salt.utils.color @@ -20,53 +21,57 @@ colors = salt.utils.color.get_colors() def parse(): - ''' + """ Parse command line options - ''' + """ parser = optparse.OptionParser() - parser.add_option('-r', - '--runs', - dest='runs', - default=10, - type=int, - help='Specify the number of times to run the consistency check') - parser.add_option('-c', - '--command', - dest='command', - default='state.show_highstate', - help='The command to execute') + parser.add_option( + "-r", + "--runs", + dest="runs", + default=10, + type=int, + help="Specify the number of times to run the consistency check", + ) + parser.add_option( + "-c", + "--command", + dest="command", + default="state.show_highstate", + help="The command to execute", + ) options, args = parser.parse_args() return options.__dict__ def run(command): - ''' + """ Execute a single command and check the returns - ''' - cmd = r'salt \* {0} --yaml-out -t 500 > high'.format(command) + """ + cmd = r"salt \* {0} --yaml-out -t 500 > high".format(command) subprocess.call(cmd, shell=True) - with salt.utils.files.fopen('high') as fp_: + with salt.utils.files.fopen("high") as fp_: data = salt.utils.yaml.safe_load(fp_) hashes = set() for key, val in six.iteritems(data): has = hashlib.md5(str(val)).hexdigest() if has not in hashes: - print('{0}:'.format(has)) + print("{0}:".format(has)) pprint.pprint(val) hashes.add(has) if len(hashes) > 1: - print('{0}Command: {1} gave inconsistent returns{2}'.format( - colors['LIGHT_RED'], - command, - colors['ENDC'] - )) + print( + "{0}Command: {1} gave inconsistent returns{2}".format( + colors["LIGHT_RED"], command, colors["ENDC"] + ) + ) -if __name__ == '__main__': +if __name__ == "__main__": opts = parse() - for _ in opts['runs']: - for command in opts['command'].split(','): - print('-' * 30) - print('Running command {0}'.format(command)) + for _ in opts["runs"]: + for command in opts["command"].split(","): + print("-" * 30) + print("Running command {0}".format(command)) run(command) diff --git a/tests/eventlisten.py b/tests/eventlisten.py index 65bbeed94d1..e2e4b66b11a 100644 --- a/tests/eventlisten.py +++ b/tests/eventlisten.py @@ -1,18 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" Use this script to dump the event data out to the terminal. It needs to know what the sock_dir is. This script is a generic tool to test event output -''' +""" # Import Python libs from __future__ import absolute_import, print_function + import optparse +import os import pprint import time -import os # Import Salt libs import salt.utils.event @@ -22,45 +23,51 @@ from salt.ext import six def parse(): - ''' + """ Parse the script command line inputs - ''' + """ parser = optparse.OptionParser() parser.add_option( - '-s', - '--sock-dir', - dest='sock_dir', - default='/var/run/salt', - help=('Statically define the directory holding the salt unix ' - 'sockets for communication') + "-s", + "--sock-dir", + dest="sock_dir", + default="/var/run/salt", + help=( + "Statically define the directory holding the salt unix " + "sockets for communication" + ), ) parser.add_option( - '-n', - '--node', - dest='node', - default='master', - help=('State if this listener will attach to a master or a ' - 'minion daemon, pass "master" or "minion"') + "-n", + "--node", + dest="node", + default="master", + help=( + "State if this listener will attach to a master or a " + 'minion daemon, pass "master" or "minion"' + ), ) parser.add_option( - '-f', - '--func_count', - default='', - help=('Return a count of the number of minions which have ' - 'replied to a job with a given func.') + "-f", + "--func_count", + default="", + help=( + "Return a count of the number of minions which have " + "replied to a job with a given func." + ), ) parser.add_option( - '-i', - '--id', - default='', - help=('If connecting to a live master or minion, pass in the id') + "-i", + "--id", + default="", + help=("If connecting to a live master or minion, pass in the id"), ) parser.add_option( - '-t', - '--transport', - default='zeromq', - help=('Transport to use. (Default: \'zeromq\'') + "-t", + "--transport", + default="zeromq", + help=("Transport to use. (Default: 'zeromq'"), ) options, args = parser.parse_args() @@ -71,46 +78,50 @@ def parse(): if v is not None: opts[k] = v - opts['sock_dir'] = os.path.join(opts['sock_dir'], opts['node']) + opts["sock_dir"] = os.path.join(opts["sock_dir"], opts["node"]) - if 'minion' in options.node: + if "minion" in options.node: if args: - opts['id'] = args[0] + opts["id"] = args[0] return opts if options.id: - opts['id'] = options.id + opts["id"] = options.id else: - opts['id'] = options.node + opts["id"] = options.node return opts def check_access_and_print_warning(sock_dir): - ''' + """ Check if this user is able to access the socket directory and print a warning if not - ''' - if (os.access(sock_dir, os.R_OK) and - os.access(sock_dir, os.W_OK) and - os.access(sock_dir, os.X_OK)): + """ + if ( + os.access(sock_dir, os.R_OK) + and os.access(sock_dir, os.W_OK) + and os.access(sock_dir, os.X_OK) + ): return else: - print('WARNING: Events will not be reported' - ' (not able to access {0})'.format(sock_dir)) + print( + "WARNING: Events will not be reported" + " (not able to access {0})".format(sock_dir) + ) def listen(opts): - ''' + """ Attach to the pub socket and grab messages - ''' + """ event = salt.utils.event.get_event( - opts['node'], - sock_dir=opts['sock_dir'], - transport=opts['transport'], + opts["node"], + sock_dir=opts["sock_dir"], + transport=opts["transport"], opts=opts, - listen=True + listen=True, ) - check_access_and_print_warning(opts['sock_dir']) + check_access_and_print_warning(opts["sock_dir"]) print(event.puburi) jid_counter = 0 found_minions = [] @@ -118,23 +129,30 @@ def listen(opts): ret = event.get_event(full=True) if ret is None: continue - if opts['func_count']: - data = ret.get('data', False) + if opts["func_count"]: + data = ret.get("data", False) if data: - if 'id' in six.iterkeys(data) and data.get('id', False) not in found_minions: - if data['fun'] == opts['func_count']: + if ( + "id" in six.iterkeys(data) + and data.get("id", False) not in found_minions + ): + if data["fun"] == opts["func_count"]: jid_counter += 1 - found_minions.append(data['id']) - print('Reply received from [{0}]. Total replies now: [{1}].'.format(ret['data']['id'], jid_counter)) + found_minions.append(data["id"]) + print( + "Reply received from [{0}]. Total replies now: [{1}].".format( + ret["data"]["id"], jid_counter + ) + ) continue else: - print('Event fired at {0}'.format(time.asctime())) - print('*' * 25) - print('Tag: {0}'.format(ret['tag'])) - print('Data:') - pprint.pprint(ret['data']) + print("Event fired at {0}".format(time.asctime())) + print("*" * 25) + print("Tag: {0}".format(ret["tag"])) + print("Data:") + pprint.pprint(ret["data"]) -if __name__ == '__main__': +if __name__ == "__main__": opts = parse() listen(opts) diff --git a/tests/filename_map.yml b/tests/filename_map.yml index a3b3b708db2..0279af9312f 100644 --- a/tests/filename_map.yml +++ b/tests/filename_map.yml @@ -17,7 +17,7 @@ salt/modules/(mac_user|useradd|pw_user|solaris_user|win_useradd)\.py: salt/modules/(aix_group|groupadd|mac_group|pw_group|solaris_group|win_groupadd)\.py: - unit.states.test_group -salt/modules/(debian_service|freebsdservice|gentoo_service|launchctl_service|mac_service|netbsdservice|openbsdrcctl_service|openbsdservice|rh_service|runit|service|smf_service|systemd_service|upstart_service|win_service)\.py: +salt/modules/(debian_service|freebsdservice|gentoo_service|launchctl_service|mac_service|netbsdservice|openbsdrcctl_service|openbsdservice|rh_service|runit|linux_service|smf_service|systemd_service|upstart_service|win_service)\.py: - unit.states.test_service - integration.states.test_service diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index 6b87bfe0e83..dbdc1af0a49 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -1,55 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Set up the Salt integration test suite -''' +""" # Import Python libs from __future__ import absolute_import, print_function -import os -import re -import sys -import copy -import time -import stat -import errno -import signal -import shutil -import pprint + import atexit -import socket +import copy +import errno import logging +import multiprocessing +import os +import pprint +import re +import shutil +import signal +import socket +import stat +import subprocess +import sys import tempfile import threading -import subprocess -import multiprocessing +import time from datetime import datetime, timedelta -try: - import pwd -except ImportError: - pass - -# Import salt tests support dirs -from tests.support.paths import * # pylint: disable=wildcard-import -from tests.support.processes import * # pylint: disable=wildcard-import -from tests.support.unit import TestCase -from tests.support.case import ShellTestCase -from tests.support.parser import PNUM, print_header, SaltTestcaseParser -from tests.support.helpers import requires_sshd_server, RedirectStdStreams -from tests.support.cli_scripts import ScriptPathMixin -from tests.support.mixins import CheckShellBinaryNameAndVersionMixin, ShellCaseCommonTestsMixin -from tests.support.mixins import AdaptedConfigurationTestCaseMixin, SaltClientTestCaseMixin -from tests.support.mixins import SaltMinionEventAssertsMixin, SaltReturnAssertsMixin -from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt import salt.config +import salt.log.setup as salt_log_setup import salt.master import salt.minion -import salt.runner import salt.output -import salt.version +import salt.runner import salt.utils.color import salt.utils.files import salt.utils.msgpack @@ -58,21 +42,44 @@ import salt.utils.platform import salt.utils.process import salt.utils.stringutils import salt.utils.yaml -import salt.log.setup as salt_log_setup -from salt.utils.verify import verify_env -from salt.utils.immutabletypes import freeze +import salt.version from salt.exceptions import SaltClientError # Import 3rd-party libs from salt.ext import six +from salt.utils.immutabletypes import freeze +from salt.utils.verify import verify_env +from tests.support.case import ShellTestCase +from tests.support.cli_scripts import ScriptPathMixin +from tests.support.helpers import RedirectStdStreams, requires_sshd_server +from tests.support.mixins import ( + AdaptedConfigurationTestCaseMixin, + CheckShellBinaryNameAndVersionMixin, + SaltClientTestCaseMixin, + SaltMinionEventAssertsMixin, + SaltReturnAssertsMixin, + ShellCaseCommonTestsMixin, +) +from tests.support.parser import PNUM, SaltTestcaseParser, print_header +from tests.support.paths import * # pylint: disable=wildcard-import +from tests.support.processes import * # pylint: disable=wildcard-import + +# Import salt tests support libs +from tests.support.processes import SaltMaster, SaltMinion, SaltSyndic +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase + +try: + import pwd +except ImportError: + pass + try: import salt.ext.six.moves.socketserver as socketserver # pylint: disable=no-name-in-module except ImportError: import socketserver -# Import salt tests support libs -from tests.support.processes import SaltMaster, SaltMinion, SaltSyndic log = logging.getLogger(__name__) @@ -80,12 +87,12 @@ _RUNTESTS_PORTS = {} def get_unused_localhost_port(): - ''' + """ Return a random unused port on localhost - ''' + """ usock = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM) usock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - usock.bind(('127.0.0.1', 0)) + usock.bind(("127.0.0.1", 0)) port = usock.getsockname()[1] if port in (54505, 54506, 64505, 64506, 64507, 64508, 64510, 64511, 64520, 64521): # These ports are hardcoded in the test configuration @@ -93,9 +100,9 @@ def get_unused_localhost_port(): usock.close() return port - DARWIN = True if sys.platform.startswith('darwin') else False - BSD = True if 'bsd' in sys.platform else False - AIX = True if sys.platform.startswith('aix') else False + DARWIN = True if sys.platform.startswith("darwin") else False + BSD = True if "bsd" in sys.platform else False + AIX = True if sys.platform.startswith("aix") else False if (AIX or DARWIN) and port in _RUNTESTS_PORTS: port = get_unused_localhost_port() @@ -135,14 +142,14 @@ class ThreadedSocketServer(ThreadingMixIn, socketserver.TCPServer, object): super(ThreadedSocketServer, self).server_activate() def server_close(self): - if hasattr(self, 'shutting_down'): + if hasattr(self, "shutting_down"): self.shutting_down.set() super(ThreadedSocketServer, self).server_close() class SocketServerRequestHandler(socketserver.StreamRequestHandler): def handle(self): - unpacker = salt.utils.msgpack.Unpacker(encoding='utf-8') + unpacker = salt.utils.msgpack.Unpacker(encoding="utf-8") while not self.server.shutting_down.is_set(): try: wire_bytes = self.request.recv(1024) @@ -170,48 +177,49 @@ class SocketServerRequestHandler(socketserver.StreamRequestHandler): class TestDaemonStartFailed(Exception): - ''' + """ Simple exception to signal that a test daemon failed to start - ''' + """ class TestDaemon(object): - ''' + """ Set up the master and minion daemons, and run related cases - ''' + """ + MINIONS_CONNECT_TIMEOUT = MINIONS_SYNC_TIMEOUT = 600 def __init__(self, parser): self.parser = parser - self.colors = salt.utils.color.get_colors(self.parser.options.no_colors is False) + self.colors = salt.utils.color.get_colors( + self.parser.options.no_colors is False + ) if salt.utils.platform.is_windows(): # There's no shell color support on windows... for key in self.colors: - self.colors[key] = '' + self.colors[key] = "" def __enter__(self): - ''' + """ Start a master and minion - ''' + """ # Setup the multiprocessing logging queue listener - salt_log_setup.setup_multiprocessing_logging_listener( - self.master_opts - ) + salt_log_setup.setup_multiprocessing_logging_listener(self.master_opts) # Set up PATH to mockbin self._enter_mockbin() - self.minion_targets = set(['minion', 'sub_minion']) + self.minion_targets = set(["minion", "sub_minion"]) - if self.parser.options.transport == 'zeromq': + if self.parser.options.transport == "zeromq": self.start_zeromq_daemons() - elif self.parser.options.transport == 'tcp': + elif self.parser.options.transport == "tcp": self.start_tcp_daemons() self.pre_setup_minions() self.setup_minions() - if getattr(self.parser.options, 'ssh', False): + if getattr(self.parser.options, "ssh", False): self.prep_ssh() self.wait_for_minions(time.time(), self.MINIONS_CONNECT_TIMEOUT) @@ -219,35 +227,39 @@ class TestDaemon(object): if self.parser.options.sysinfo: try: print_header( - '~~~~~~~ Versions Report ', inline=True, - width=getattr(self.parser.options, 'output_columns', PNUM) + "~~~~~~~ Versions Report ", + inline=True, + width=getattr(self.parser.options, "output_columns", PNUM), ) except TypeError: - print_header('~~~~~~~ Versions Report ', inline=True) + print_header("~~~~~~~ Versions Report ", inline=True) - print('\n'.join(salt.version.versions_report())) + print("\n".join(salt.version.versions_report())) try: print_header( - '~~~~~~~ Minion Grains Information ', inline=True, - width=getattr(self.parser.options, 'output_columns', PNUM) + "~~~~~~~ Minion Grains Information ", + inline=True, + width=getattr(self.parser.options, "output_columns", PNUM), ) except TypeError: - print_header('~~~~~~~ Minion Grains Information ', inline=True) + print_header("~~~~~~~ Minion Grains Information ", inline=True) - grains = self.client.cmd('minion', 'grains.items') + grains = self.client.cmd("minion", "grains.items") minion_opts = self.minion_opts.copy() - minion_opts['color'] = self.parser.options.no_colors is False - salt.output.display_output(grains, 'grains', minion_opts) + minion_opts["color"] = self.parser.options.no_colors is False + salt.output.display_output(grains, "grains", minion_opts) try: print_header( - '=', sep='=', inline=True, - width=getattr(self.parser.options, 'output_columns', PNUM) + "=", + sep="=", + inline=True, + width=getattr(self.parser.options, "output_columns", PNUM), ) except TypeError: - print_header('', sep='=', inline=True) + print_header("", sep="=", inline=True) try: return self @@ -255,238 +267,280 @@ class TestDaemon(object): self.post_setup_minions() def start_zeromq_daemons(self): - ''' + """ Fire up the daemons used for zeromq tests - ''' - self.log_server = ThreadedSocketServer(('localhost', SALT_LOG_PORT), SocketServerRequestHandler) + """ + self.log_server = ThreadedSocketServer( + ("localhost", SALT_LOG_PORT), SocketServerRequestHandler + ) self.log_server_process = threading.Thread(target=self.log_server.serve_forever) self.log_server_process.start() try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting salt-master ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting salt-master ... {ENDC}".format(**self.colors) ) sys.stdout.flush() self.master_process = start_daemon( - daemon_name='salt-master', - daemon_id=self.master_opts['id'], - daemon_log_prefix='salt-master/{}'.format(self.master_opts['id']), - daemon_cli_script_name='master', + daemon_name="salt-master", + daemon_id=self.master_opts["id"], + daemon_log_prefix="salt-master/{}".format(self.master_opts["id"]), + daemon_cli_script_name="master", daemon_config=self.master_opts, daemon_config_dir=RUNTIME_VARS.TMP_CONF_DIR, daemon_class=SaltMaster, bin_dir_path=SCRIPT_DIR, fail_hard=True, event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting salt-master ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting salt-master ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting salt-master ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting salt-master ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting salt-minion ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting salt-minion ... {ENDC}".format(**self.colors) ) sys.stdout.flush() self.minion_process = start_daemon( - daemon_name='salt-minion', - daemon_id=self.master_opts['id'], - daemon_log_prefix='salt-minion/{}'.format(self.minion_opts['id']), - daemon_cli_script_name='minion', + daemon_name="salt-minion", + daemon_id=self.master_opts["id"], + daemon_log_prefix="salt-minion/{}".format(self.minion_opts["id"]), + daemon_cli_script_name="minion", daemon_config=self.minion_opts, daemon_config_dir=RUNTIME_VARS.TMP_CONF_DIR, daemon_class=SaltMinion, bin_dir_path=SCRIPT_DIR, fail_hard=True, event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting salt-minion ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting salt-minion ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting salt-minion ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting salt-minion ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting sub salt-minion ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting sub salt-minion ... {ENDC}".format( + **self.colors + ) ) sys.stdout.flush() self.sub_minion_process = start_daemon( - daemon_name='sub salt-minion', - daemon_id=self.master_opts['id'], - daemon_log_prefix='sub-salt-minion/{}'.format(self.sub_minion_opts['id']), - daemon_cli_script_name='minion', + daemon_name="sub salt-minion", + daemon_id=self.master_opts["id"], + daemon_log_prefix="sub-salt-minion/{}".format( + self.sub_minion_opts["id"] + ), + daemon_cli_script_name="minion", daemon_config=self.sub_minion_opts, daemon_config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, daemon_class=SaltMinion, bin_dir_path=SCRIPT_DIR, fail_hard=True, event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting sub salt-minion ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting sub salt-minion ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting sub salt-minion ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting sub salt-minion ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting syndic salt-master ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting syndic salt-master ... {ENDC}".format( + **self.colors + ) ) sys.stdout.flush() self.prep_syndic() self.smaster_process = start_daemon( - daemon_name='salt-smaster', - daemon_id=self.syndic_master_opts['id'], - daemon_log_prefix='salt-smaster/{}'.format(self.syndic_master_opts['id']), - daemon_cli_script_name='master', + daemon_name="salt-smaster", + daemon_id=self.syndic_master_opts["id"], + daemon_log_prefix="salt-smaster/{}".format( + self.syndic_master_opts["id"] + ), + daemon_cli_script_name="master", daemon_config=self.syndic_master_opts, daemon_config_dir=RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, daemon_class=SaltMaster, bin_dir_path=SCRIPT_DIR, fail_hard=True, event_listener_config_dir=RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting syndic salt-master ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting syndic salt-master ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting syndic salt-master ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting syndic salt-master ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting salt-syndic ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting salt-syndic ... {ENDC}".format(**self.colors) ) sys.stdout.flush() self.syndic_process = start_daemon( - daemon_name='salt-syndic', - daemon_id=self.syndic_opts['id'], - daemon_log_prefix='salt-syndic/{}'.format(self.syndic_opts['id']), - daemon_cli_script_name='syndic', + daemon_name="salt-syndic", + daemon_id=self.syndic_opts["id"], + daemon_log_prefix="salt-syndic/{}".format(self.syndic_opts["id"]), + daemon_cli_script_name="syndic", daemon_config=self.syndic_opts, daemon_config_dir=RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, daemon_class=SaltSyndic, bin_dir_path=SCRIPT_DIR, fail_hard=True, event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting salt-syndic ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting salt-syndic ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting salt-syndic ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting salt-syndic ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() if self.parser.options.proxy: - self.minion_targets.add(self.proxy_opts['id']) + self.minion_targets.add(self.proxy_opts["id"]) try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting salt-proxy ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting salt-proxy ... {ENDC}".format( + **self.colors + ) ) sys.stdout.flush() self.proxy_process = start_daemon( - daemon_name='salt-proxy', - daemon_id=self.proxy_opts['id'], - daemon_log_prefix='salt-proxy/{}'.format(self.proxy_opts['id']), - daemon_cli_script_name='proxy', + daemon_name="salt-proxy", + daemon_id=self.proxy_opts["id"], + daemon_log_prefix="salt-proxy/{}".format(self.proxy_opts["id"]), + daemon_cli_script_name="proxy", daemon_config=self.proxy_opts, daemon_config_dir=RUNTIME_VARS.TMP_CONF_DIR, daemon_class=SaltProxy, bin_dir_path=SCRIPT_DIR, fail_hard=True, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting salt-proxy ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting salt-proxy ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting salt-proxy ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting salt-proxy ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() @@ -494,177 +548,222 @@ class TestDaemon(object): start_tcp_daemons = start_zeromq_daemons def prep_syndic(self): - ''' + """ Create a roster file for salt's syndic - ''' - roster_path = os.path.join(FILES, 'conf/_ssh/roster') + """ + roster_path = os.path.join(FILES, "conf/_ssh/roster") shutil.copy(roster_path, RUNTIME_VARS.TMP_CONF_DIR) shutil.copy(roster_path, RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR) def prep_ssh(self): - ''' + """ Generate keys and start an ssh daemon on an alternate port - ''' + """ sys.stdout.write( - ' * {LIGHT_GREEN}Starting {0} ... {ENDC}'.format( - 'SSH server', - **self.colors + " * {LIGHT_GREEN}Starting {0} ... {ENDC}".format( + "SSH server", **self.colors ) ) - keygen = salt.utils.path.which('ssh-keygen') - sshd = salt.utils.path.which('sshd') + keygen = salt.utils.path.which("ssh-keygen") + sshd = salt.utils.path.which("sshd") if not (keygen and sshd): - print('WARNING: Could not initialize SSH subsystem. Tests for salt-ssh may break!') + print( + "WARNING: Could not initialize SSH subsystem. Tests for salt-ssh may break!" + ) return if not os.path.exists(RUNTIME_VARS.TMP_CONF_DIR): os.makedirs(RUNTIME_VARS.TMP_CONF_DIR) # Generate client key - pub_key_test_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test.pub') - priv_key_test_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test') + pub_key_test_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test.pub") + priv_key_test_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test") if os.path.exists(pub_key_test_file): os.remove(pub_key_test_file) if os.path.exists(priv_key_test_file): os.remove(priv_key_test_file) keygen_process = subprocess.Popen( - [keygen, '-t', - 'ecdsa', - '-b', - '521', - '-C', - '"$(whoami)@$(hostname)-$(date -I)"', - '-f', - 'key_test', - '-P', - ''], + [ + keygen, + "-t", + "ecdsa", + "-b", + "521", + "-C", + '"$(whoami)@$(hostname)-$(date -I)"', + "-f", + "key_test", + "-P", + "", + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - cwd=RUNTIME_VARS.TMP_CONF_DIR + cwd=RUNTIME_VARS.TMP_CONF_DIR, ) _, keygen_err = keygen_process.communicate() if keygen_err: - print('ssh-keygen had errors: {0}'.format(salt.utils.stringutils.to_str(keygen_err))) - sshd_config_path = os.path.join(FILES, 'conf/_ssh/sshd_config') + print( + "ssh-keygen had errors: {0}".format( + salt.utils.stringutils.to_str(keygen_err) + ) + ) + sshd_config_path = os.path.join(FILES, "conf/_ssh/sshd_config") shutil.copy(sshd_config_path, RUNTIME_VARS.TMP_CONF_DIR) - auth_key_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test.pub') + auth_key_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test.pub") # Generate server key - server_key_dir = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'server') + server_key_dir = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "server") if not os.path.exists(server_key_dir): os.makedirs(server_key_dir) - server_dsa_priv_key_file = os.path.join(server_key_dir, 'ssh_host_dsa_key') - server_dsa_pub_key_file = os.path.join(server_key_dir, 'ssh_host_dsa_key.pub') - server_ecdsa_priv_key_file = os.path.join(server_key_dir, 'ssh_host_ecdsa_key') - server_ecdsa_pub_key_file = os.path.join(server_key_dir, 'ssh_host_ecdsa_key.pub') - server_ed25519_priv_key_file = os.path.join(server_key_dir, 'ssh_host_ed25519_key') - server_ed25519_pub_key_file = os.path.join(server_key_dir, 'ssh_host.ed25519_key.pub') + server_dsa_priv_key_file = os.path.join(server_key_dir, "ssh_host_dsa_key") + server_dsa_pub_key_file = os.path.join(server_key_dir, "ssh_host_dsa_key.pub") + server_ecdsa_priv_key_file = os.path.join(server_key_dir, "ssh_host_ecdsa_key") + server_ecdsa_pub_key_file = os.path.join( + server_key_dir, "ssh_host_ecdsa_key.pub" + ) + server_ed25519_priv_key_file = os.path.join( + server_key_dir, "ssh_host_ed25519_key" + ) + server_ed25519_pub_key_file = os.path.join( + server_key_dir, "ssh_host.ed25519_key.pub" + ) - for server_key_file in (server_dsa_priv_key_file, - server_dsa_pub_key_file, - server_ecdsa_priv_key_file, - server_ecdsa_pub_key_file, - server_ed25519_priv_key_file, - server_ed25519_pub_key_file): + for server_key_file in ( + server_dsa_priv_key_file, + server_dsa_pub_key_file, + server_ecdsa_priv_key_file, + server_ecdsa_pub_key_file, + server_ed25519_priv_key_file, + server_ed25519_pub_key_file, + ): if os.path.exists(server_key_file): os.remove(server_key_file) keygen_process_dsa = subprocess.Popen( - [keygen, '-t', - 'dsa', - '-b', - '1024', - '-C', - '"$(whoami)@$(hostname)-$(date -I)"', - '-f', - 'ssh_host_dsa_key', - '-P', - ''], + [ + keygen, + "-t", + "dsa", + "-b", + "1024", + "-C", + '"$(whoami)@$(hostname)-$(date -I)"', + "-f", + "ssh_host_dsa_key", + "-P", + "", + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - cwd=server_key_dir + cwd=server_key_dir, ) _, keygen_dsa_err = keygen_process_dsa.communicate() if keygen_dsa_err: - print('ssh-keygen had errors: {0}'.format(salt.utils.stringutils.to_str(keygen_dsa_err))) + print( + "ssh-keygen had errors: {0}".format( + salt.utils.stringutils.to_str(keygen_dsa_err) + ) + ) keygen_process_ecdsa = subprocess.Popen( - [keygen, '-t', - 'ecdsa', - '-b', - '521', - '-C', - '"$(whoami)@$(hostname)-$(date -I)"', - '-f', - 'ssh_host_ecdsa_key', - '-P', - ''], + [ + keygen, + "-t", + "ecdsa", + "-b", + "521", + "-C", + '"$(whoami)@$(hostname)-$(date -I)"', + "-f", + "ssh_host_ecdsa_key", + "-P", + "", + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - cwd=server_key_dir + cwd=server_key_dir, ) _, keygen_escda_err = keygen_process_ecdsa.communicate() if keygen_escda_err: - print('ssh-keygen had errors: {0}'.format(salt.utils.stringutils.to_str(keygen_escda_err))) + print( + "ssh-keygen had errors: {0}".format( + salt.utils.stringutils.to_str(keygen_escda_err) + ) + ) keygen_process_ed25519 = subprocess.Popen( - [keygen, '-t', - 'ed25519', - '-b', - '521', - '-C', - '"$(whoami)@$(hostname)-$(date -I)"', - '-f', - 'ssh_host_ed25519_key', - '-P', - ''], + [ + keygen, + "-t", + "ed25519", + "-b", + "521", + "-C", + '"$(whoami)@$(hostname)-$(date -I)"', + "-f", + "ssh_host_ed25519_key", + "-P", + "", + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - cwd=server_key_dir + cwd=server_key_dir, ) _, keygen_ed25519_err = keygen_process_ed25519.communicate() if keygen_ed25519_err: - print('ssh-keygen had errors: {0}'.format(salt.utils.stringutils.to_str(keygen_ed25519_err))) + print( + "ssh-keygen had errors: {0}".format( + salt.utils.stringutils.to_str(keygen_ed25519_err) + ) + ) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'sshd_config'), 'a') as ssh_config: - ssh_config.write('AuthorizedKeysFile {0}\n'.format(auth_key_file)) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "sshd_config"), "a" + ) as ssh_config: + ssh_config.write("AuthorizedKeysFile {0}\n".format(auth_key_file)) if not keygen_dsa_err: - ssh_config.write('HostKey {0}\n'.format(server_dsa_priv_key_file)) + ssh_config.write("HostKey {0}\n".format(server_dsa_priv_key_file)) if not keygen_escda_err: - ssh_config.write('HostKey {0}\n'.format(server_ecdsa_priv_key_file)) + ssh_config.write("HostKey {0}\n".format(server_ecdsa_priv_key_file)) if not keygen_ed25519_err: - ssh_config.write('HostKey {0}\n'.format(server_ed25519_priv_key_file)) + ssh_config.write("HostKey {0}\n".format(server_ed25519_priv_key_file)) - self.sshd_pidfile = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'sshd.pid') + self.sshd_pidfile = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "sshd.pid") self.sshd_process = subprocess.Popen( - [sshd, '-f', 'sshd_config', '-o', 'PidFile={0}'.format(self.sshd_pidfile)], + [sshd, "-f", "sshd_config", "-o", "PidFile={0}".format(self.sshd_pidfile)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, - cwd=RUNTIME_VARS.TMP_CONF_DIR + cwd=RUNTIME_VARS.TMP_CONF_DIR, ) _, sshd_err = self.sshd_process.communicate() if sshd_err: - print('sshd had errors on startup: {0}'.format(salt.utils.stringutils.to_str(sshd_err))) - else: - os.environ['SSH_DAEMON_RUNNING'] = 'True' - self.prep_syndic() - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'roster'), 'a') as roster: - roster.write(' user: {0}\n'.format(RUNTIME_VARS.RUNNING_TESTS_USER)) - roster.write(' priv: {0}/{1}'.format(RUNTIME_VARS.TMP_CONF_DIR, 'key_test')) - sys.stdout.write( - ' {LIGHT_GREEN}STARTED!\n{ENDC}'.format( - **self.colors + print( + "sshd had errors on startup: {0}".format( + salt.utils.stringutils.to_str(sshd_err) + ) ) - ) + else: + os.environ["SSH_DAEMON_RUNNING"] = "True" + self.prep_syndic() + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "roster"), "a" + ) as roster: + roster.write(" user: {0}\n".format(RUNTIME_VARS.RUNNING_TESTS_USER)) + roster.write( + " priv: {0}/{1}\n".format(RUNTIME_VARS.TMP_CONF_DIR, "key_test") + ) + if salt.utils.platform.is_darwin(): + roster.write(" set_path: $PATH:/usr/local/bin/\n") + sys.stdout.write(" {LIGHT_GREEN}STARTED!\n{ENDC}".format(**self.colors)) @classmethod def config(cls, role): - ''' + """ Return a configuration for a master/minion/syndic. Currently these roles are: @@ -674,7 +773,7 @@ class TestDaemon(object): * syndic_master * sub_minion * proxy - ''' + """ return RUNTIME_VARS.RUNTIME_CONFIGS[role] @classmethod @@ -683,22 +782,22 @@ class TestDaemon(object): @property def client(self): - ''' + """ Return a local client which will be used for example to ping and sync the test minions. This client is defined as a class attribute because its creation needs to be deferred to a latter stage. If created it on `__enter__` like it previously was, it would not receive the master events. - ''' - if 'runtime_client' not in RUNTIME_VARS.RUNTIME_CONFIGS: - RUNTIME_VARS.RUNTIME_CONFIGS['runtime_client'] = salt.client.get_local_client( - mopts=self.master_opts - ) - return RUNTIME_VARS.RUNTIME_CONFIGS['runtime_client'] + """ + if "runtime_client" not in RUNTIME_VARS.RUNTIME_CONFIGS: + RUNTIME_VARS.RUNTIME_CONFIGS[ + "runtime_client" + ] = salt.client.get_local_client(mopts=self.master_opts) + return RUNTIME_VARS.RUNTIME_CONFIGS["runtime_client"] @classmethod - def transplant_configs(cls, transport='zeromq'): + def transplant_configs(cls, transport="zeromq"): if os.path.isdir(RUNTIME_VARS.TMP): shutil.rmtree(RUNTIME_VARS.TMP) os.makedirs(RUNTIME_VARS.TMP) @@ -708,35 +807,47 @@ class TestDaemon(object): os.makedirs(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR) os.makedirs(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR) - print(' * Transplanting configuration files to \'{0}\''.format(RUNTIME_VARS.TMP_CONF_DIR)) - tests_known_hosts_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'salt_ssh_known_hosts') - with salt.utils.files.fopen(tests_known_hosts_file, 'w') as known_hosts: - known_hosts.write('') + print( + " * Transplanting configuration files to '{0}'".format( + RUNTIME_VARS.TMP_CONF_DIR + ) + ) + tests_known_hosts_file = os.path.join( + RUNTIME_VARS.TMP_CONF_DIR, "salt_ssh_known_hosts" + ) + with salt.utils.files.fopen(tests_known_hosts_file, "w") as known_hosts: + known_hosts.write("") # This master connects to syndic_master via a syndic - master_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'master')) - master_opts['known_hosts_file'] = tests_known_hosts_file - master_opts['cachedir'] = 'cache' - master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - master_opts['root_dir'] = os.path.join(TMP_ROOT_DIR) - master_opts['pki_dir'] = 'pki' - master_opts['syndic_master'] = 'localhost' - pytest_stop_sending_events_file = os.path.join(TMP_ROOT_DIR, 'pytest_stop_sending_events_file_master') - with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: - wfh.write('') - master_opts['pytest_stop_sending_events_file'] = pytest_stop_sending_events_file + master_opts = salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "master") + ) + master_opts["known_hosts_file"] = tests_known_hosts_file + master_opts["cachedir"] = "cache" + master_opts["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + master_opts["root_dir"] = os.path.join(TMP_ROOT_DIR) + master_opts["pki_dir"] = "pki" + master_opts["syndic_master"] = "localhost" + pytest_stop_sending_events_file = os.path.join( + TMP_ROOT_DIR, "pytest_stop_sending_events_file_master" + ) + with salt.utils.files.fopen(pytest_stop_sending_events_file, "w") as wfh: + wfh.write("") + master_opts["pytest_stop_sending_events_file"] = pytest_stop_sending_events_file file_tree = { - 'root_dir': os.path.join(FILES, 'pillar', 'base', 'file_tree'), - 'follow_dir_links': False, - 'keep_newline': True, + "root_dir": os.path.join(FILES, "pillar", "base", "file_tree"), + "follow_dir_links": False, + "keep_newline": True, } - master_opts['ext_pillar'].append({'file_tree': file_tree}) + master_opts["ext_pillar"].append({"file_tree": file_tree}) # Config settings to test `event_return` - if 'returner_dirs' not in master_opts: - master_opts['returner_dirs'] = [] - master_opts['returner_dirs'].append(os.path.join(RUNTIME_VARS.FILES, 'returners')) - master_opts['event_return'] = 'runtests_noop' + if "returner_dirs" not in master_opts: + master_opts["returner_dirs"] = [] + master_opts["returner_dirs"].append( + os.path.join(RUNTIME_VARS.FILES, "returners") + ) + master_opts["event_return"] = "runtests_noop" # Under windows we can't seem to properly create a virtualenv off of another # virtualenv, we can on linux but we will still point to the virtualenv binary @@ -745,21 +856,23 @@ class TestDaemon(object): real_prefix = sys.real_prefix # The above attribute exists, this is a virtualenv if salt.utils.platform.is_windows(): - virtualenv_binary = os.path.join(real_prefix, 'Scripts', 'virtualenv.exe') + virtualenv_binary = os.path.join( + real_prefix, "Scripts", "virtualenv.exe" + ) else: # We need to remove the virtualenv from PATH or we'll get the virtualenv binary # from within the virtualenv, we don't want that - path = os.environ.get('PATH') + path = os.environ.get("PATH") if path is not None: path_items = path.split(os.pathsep) for item in path_items[:]: if item.startswith(sys.base_prefix): path_items.remove(item) - os.environ['PATH'] = os.pathsep.join(path_items) - virtualenv_binary = salt.utils.path.which('virtualenv') + os.environ["PATH"] = os.pathsep.join(path_items) + virtualenv_binary = salt.utils.path.which("virtualenv") if path is not None: # Restore previous environ PATH - os.environ['PATH'] = path + os.environ["PATH"] = path if not virtualenv_binary.startswith(real_prefix): virtualenv_binary = None if virtualenv_binary and not os.path.exists(virtualenv_binary): @@ -770,160 +883,180 @@ class TestDaemon(object): virtualenv_binary = None # This minion connects to master - minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'minion')) - minion_opts['cachedir'] = 'cache' - minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - minion_opts['root_dir'] = os.path.join(TMP_ROOT_DIR) - minion_opts['pki_dir'] = 'pki' - minion_opts['hosts.file'] = os.path.join(TMP_ROOT_DIR, 'hosts') - minion_opts['aliases.file'] = os.path.join(TMP_ROOT_DIR, 'aliases') + minion_opts = salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "minion") + ) + minion_opts["cachedir"] = "cache" + minion_opts["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + minion_opts["root_dir"] = os.path.join(TMP_ROOT_DIR) + minion_opts["pki_dir"] = "pki" + minion_opts["hosts.file"] = os.path.join(TMP_ROOT_DIR, "hosts") + minion_opts["aliases.file"] = os.path.join(TMP_ROOT_DIR, "aliases") if virtualenv_binary: - minion_opts['venv_bin'] = virtualenv_binary + minion_opts["venv_bin"] = virtualenv_binary # This sub_minion also connects to master - sub_minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'sub_minion')) - sub_minion_opts['cachedir'] = 'cache' - sub_minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - sub_minion_opts['root_dir'] = os.path.join(TMP, 'rootdir-sub-minion') - sub_minion_opts['pki_dir'] = 'pki' - sub_minion_opts['hosts.file'] = os.path.join(TMP_ROOT_DIR, 'hosts') - sub_minion_opts['aliases.file'] = os.path.join(TMP_ROOT_DIR, 'aliases') + sub_minion_opts = salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "sub_minion") + ) + sub_minion_opts["cachedir"] = "cache" + sub_minion_opts["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + sub_minion_opts["root_dir"] = os.path.join(TMP, "rootdir-sub-minion") + sub_minion_opts["pki_dir"] = "pki" + sub_minion_opts["hosts.file"] = os.path.join(TMP_ROOT_DIR, "hosts") + sub_minion_opts["aliases.file"] = os.path.join(TMP_ROOT_DIR, "aliases") if virtualenv_binary: - sub_minion_opts['venv_bin'] = virtualenv_binary + sub_minion_opts["venv_bin"] = virtualenv_binary # This is the master of masters - syndic_master_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic_master')) - syndic_master_opts['cachedir'] = 'cache' - syndic_master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - syndic_master_opts['root_dir'] = os.path.join(TMP, 'rootdir-syndic-master') - syndic_master_opts['pki_dir'] = 'pki' - pytest_stop_sending_events_file = os.path.join(TMP_ROOT_DIR, 'pytest_stop_sending_events_file_syndic_master') - with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: - wfh.write('') - syndic_master_opts['pytest_stop_sending_events_file'] = pytest_stop_sending_events_file + syndic_master_opts = salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "syndic_master") + ) + syndic_master_opts["cachedir"] = "cache" + syndic_master_opts["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + syndic_master_opts["root_dir"] = os.path.join(TMP, "rootdir-syndic-master") + syndic_master_opts["pki_dir"] = "pki" + pytest_stop_sending_events_file = os.path.join( + TMP_ROOT_DIR, "pytest_stop_sending_events_file_syndic_master" + ) + with salt.utils.files.fopen(pytest_stop_sending_events_file, "w") as wfh: + wfh.write("") + syndic_master_opts[ + "pytest_stop_sending_events_file" + ] = pytest_stop_sending_events_file # This is the syndic for master # Let's start with a copy of the syndic master configuration syndic_opts = copy.deepcopy(syndic_master_opts) # Let's update with the syndic configuration - syndic_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic'))) - syndic_opts['cachedir'] = 'cache' - syndic_opts['root_dir'] = os.path.join(TMP_ROOT_DIR) + syndic_opts.update( + salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, "syndic")) + ) + syndic_opts["cachedir"] = "cache" + syndic_opts["root_dir"] = os.path.join(TMP_ROOT_DIR) # This is the syndic for master # Let's start with a copy of the syndic master configuration syndic_opts = copy.deepcopy(syndic_master_opts) # Let's update with the syndic configuration - syndic_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic'))) - syndic_opts['cachedir'] = os.path.join(TMP, 'rootdir', 'cache') - syndic_opts['config_dir'] = RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR + syndic_opts.update( + salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, "syndic")) + ) + syndic_opts["cachedir"] = os.path.join(TMP, "rootdir", "cache") + syndic_opts["config_dir"] = RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR # This proxy connects to master - proxy_opts = salt.config._read_conf_file(os.path.join(CONF_DIR, 'proxy')) - proxy_opts['cachedir'] = 'cache' + proxy_opts = salt.config._read_conf_file(os.path.join(CONF_DIR, "proxy")) + proxy_opts["cachedir"] = "cache" # proxy_opts['user'] = running_tests_user - proxy_opts['root_dir'] = os.path.join(TMP, 'rootdir-proxy') - proxy_opts['pki_dir'] = 'pki' - proxy_opts['hosts.file'] = os.path.join(TMP, 'rootdir-proxy', 'hosts') - proxy_opts['aliases.file'] = os.path.join(TMP, 'rootdir-proxy', 'aliases') + proxy_opts["root_dir"] = os.path.join(TMP, "rootdir-proxy") + proxy_opts["pki_dir"] = "pki" + proxy_opts["hosts.file"] = os.path.join(TMP, "rootdir-proxy", "hosts") + proxy_opts["aliases.file"] = os.path.join(TMP, "rootdir-proxy", "aliases") - if transport == 'tcp': - master_opts['transport'] = 'tcp' - minion_opts['transport'] = 'tcp' - sub_minion_opts['transport'] = 'tcp' - syndic_master_opts['transport'] = 'tcp' - proxy_opts['transport'] = 'tcp' + if transport == "tcp": + master_opts["transport"] = "tcp" + minion_opts["transport"] = "tcp" + sub_minion_opts["transport"] = "tcp" + syndic_master_opts["transport"] = "tcp" + proxy_opts["transport"] = "tcp" # This is the syndic for master # Let's start with a copy of the syndic master configuration syndic_opts = copy.deepcopy(master_opts) # Let's update with the syndic configuration - syndic_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'syndic'))) - syndic_opts['cachedir'] = os.path.join(TMP, 'rootdir', 'cache') - syndic_opts['config_dir'] = RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR + syndic_opts.update( + salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, "syndic")) + ) + syndic_opts["cachedir"] = os.path.join(TMP, "rootdir", "cache") + syndic_opts["config_dir"] = RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR # Set up config options that require internal data - master_opts['pillar_roots'] = syndic_master_opts['pillar_roots'] = { - 'base': [ + master_opts["pillar_roots"] = syndic_master_opts["pillar_roots"] = { + "base": [ RUNTIME_VARS.TMP_PILLAR_TREE, - os.path.join(FILES, 'pillar', 'base'), + os.path.join(FILES, "pillar", "base"), ] } - minion_opts['pillar_roots'] = { - 'base': [ + minion_opts["pillar_roots"] = { + "base": [ RUNTIME_VARS.TMP_PILLAR_TREE, - os.path.join(FILES, 'pillar', 'base'), + os.path.join(FILES, "pillar", "base"), ] } - master_opts['file_roots'] = syndic_master_opts['file_roots'] = { - 'base': [ + master_opts["file_roots"] = syndic_master_opts["file_roots"] = { + "base": [ # Let's support runtime created files that can be used like: # salt://my-temp-file.txt RUNTIME_VARS.TMP_STATE_TREE, - os.path.join(FILES, 'file', 'base'), + os.path.join(FILES, "file", "base"), ], # Alternate root to test __env__ choices - 'prod': [ - os.path.join(FILES, 'file', 'prod'), - RUNTIME_VARS.TMP_PRODENV_STATE_TREE - ] + "prod": [ + os.path.join(FILES, "file", "prod"), + RUNTIME_VARS.TMP_PRODENV_STATE_TREE, + ], } - minion_opts['file_roots'] = { - 'base': [ + minion_opts["file_roots"] = { + "base": [ # Let's support runtime created files that can be used like: # salt://my-temp-file.txt RUNTIME_VARS.TMP_STATE_TREE, - os.path.join(FILES, 'file', 'base'), + os.path.join(FILES, "file", "base"), ], # Alternate root to test __env__ choices - 'prod': [ - os.path.join(FILES, 'file', 'prod'), - RUNTIME_VARS.TMP_PRODENV_STATE_TREE - ] + "prod": [ + os.path.join(FILES, "file", "prod"), + RUNTIME_VARS.TMP_PRODENV_STATE_TREE, + ], } - master_opts.setdefault('reactor', []).append( - { - 'salt/minion/*/start': [ - os.path.join(FILES, 'reactor-sync-minion.sls') - ], - } + master_opts.setdefault("reactor", []).append( + {"salt/minion/*/start": [os.path.join(FILES, "reactor-sync-minion.sls")]} ) for opts_dict in (master_opts, syndic_master_opts): - if 'ext_pillar' not in opts_dict: - opts_dict['ext_pillar'] = [] + if "ext_pillar" not in opts_dict: + opts_dict["ext_pillar"] = [] if salt.utils.platform.is_windows(): - opts_dict['ext_pillar'].append( - {'cmd_yaml': 'type {0}'.format(os.path.join(FILES, 'ext.yaml'))}) + opts_dict["ext_pillar"].append( + {"cmd_yaml": "type {0}".format(os.path.join(FILES, "ext.yaml"))} + ) else: - opts_dict['ext_pillar'].append( - {'cmd_yaml': 'cat {0}'.format(os.path.join(FILES, 'ext.yaml'))}) + opts_dict["ext_pillar"].append( + {"cmd_yaml": "cat {0}".format(os.path.join(FILES, "ext.yaml"))} + ) # all read, only owner write - autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + autosign_file_permissions = ( + stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + ) for opts_dict in (master_opts, syndic_master_opts): # We need to copy the extension modules into the new master root_dir or # it will be prefixed by it - new_extension_modules_path = os.path.join(opts_dict['root_dir'], 'extension_modules') + new_extension_modules_path = os.path.join( + opts_dict["root_dir"], "extension_modules" + ) if not os.path.exists(new_extension_modules_path): shutil.copytree( - os.path.join( - INTEGRATION_TEST_DIR, 'files', 'extension_modules' - ), - new_extension_modules_path + os.path.join(INTEGRATION_TEST_DIR, "files", "extension_modules"), + new_extension_modules_path, ) - opts_dict['extension_modules'] = os.path.join(opts_dict['root_dir'], 'extension_modules') + opts_dict["extension_modules"] = os.path.join( + opts_dict["root_dir"], "extension_modules" + ) # Copy the autosign_file to the new master root_dir - new_autosign_file_path = os.path.join(opts_dict['root_dir'], 'autosign_file') + new_autosign_file_path = os.path.join( + opts_dict["root_dir"], "autosign_file" + ) shutil.copyfile( - os.path.join(INTEGRATION_TEST_DIR, 'files', 'autosign_file'), - new_autosign_file_path + os.path.join(INTEGRATION_TEST_DIR, "files", "autosign_file"), + new_autosign_file_path, ) os.chmod(new_autosign_file_path, autosign_file_permissions) # Point the config values to the correct temporary paths - for name in ('hosts', 'aliases'): - optname = '{0}.file'.format(name) + for name in ("hosts", "aliases"): + optname = "{0}.file".format(name) optname_path = os.path.join(TMP, name) master_opts[optname] = optname_path minion_opts[optname] = optname_path @@ -932,123 +1065,175 @@ class TestDaemon(object): syndic_master_opts[optname] = optname_path proxy_opts[optname] = optname_path - master_opts['runtests_conn_check_port'] = get_unused_localhost_port() - minion_opts['runtests_conn_check_port'] = get_unused_localhost_port() - sub_minion_opts['runtests_conn_check_port'] = get_unused_localhost_port() - syndic_opts['runtests_conn_check_port'] = get_unused_localhost_port() - syndic_master_opts['runtests_conn_check_port'] = get_unused_localhost_port() - proxy_opts['runtests_conn_check_port'] = get_unused_localhost_port() + master_opts["runtests_conn_check_port"] = get_unused_localhost_port() + minion_opts["runtests_conn_check_port"] = get_unused_localhost_port() + sub_minion_opts["runtests_conn_check_port"] = get_unused_localhost_port() + syndic_opts["runtests_conn_check_port"] = get_unused_localhost_port() + syndic_master_opts["runtests_conn_check_port"] = get_unused_localhost_port() + proxy_opts["runtests_conn_check_port"] = get_unused_localhost_port() - for conf in (master_opts, minion_opts, sub_minion_opts, syndic_opts, syndic_master_opts, proxy_opts): - if 'engines' not in conf: - conf['engines'] = [] - conf['engines'].append({'salt_runtests': {}}) + for conf in ( + master_opts, + minion_opts, + sub_minion_opts, + syndic_opts, + syndic_master_opts, + proxy_opts, + ): + if "engines" not in conf: + conf["engines"] = [] + conf["engines"].append({"salt_runtests": {}}) - if 'engines_dirs' not in conf: - conf['engines_dirs'] = [] + if "engines_dirs" not in conf: + conf["engines_dirs"] = [] - conf['engines_dirs'].insert(0, ENGINES_DIR) + conf["engines_dirs"].insert(0, ENGINES_DIR) - if 'log_handlers_dirs' not in conf: - conf['log_handlers_dirs'] = [] - conf['log_handlers_dirs'].insert(0, LOG_HANDLERS_DIR) - conf['runtests_log_port'] = SALT_LOG_PORT - conf['runtests_log_level'] = os.environ.get('TESTS_MIN_LOG_LEVEL_NAME') or 'debug' + if "log_handlers_dirs" not in conf: + conf["log_handlers_dirs"] = [] + conf["log_handlers_dirs"].insert(0, LOG_HANDLERS_DIR) + conf["runtests_log_port"] = SALT_LOG_PORT + conf["runtests_log_level"] = ( + os.environ.get("TESTS_MIN_LOG_LEVEL_NAME") or "debug" + ) # ----- Transcribe Configuration ----------------------------------------------------------------------------> for entry in os.listdir(RUNTIME_VARS.CONF_DIR): - if entry in ('master', 'minion', 'sub_minion', 'syndic', 'syndic_master', 'proxy'): + if entry in ( + "master", + "minion", + "sub_minion", + "syndic", + "syndic_master", + "proxy", + ): # These have runtime computed values and will be handled # differently continue entry_path = os.path.join(RUNTIME_VARS.CONF_DIR, entry) if os.path.isfile(entry_path): - shutil.copy( - entry_path, - os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry) - ) + shutil.copy(entry_path, os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry)) elif os.path.isdir(entry_path): shutil.copytree( - entry_path, - os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry) + entry_path, os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry) ) - for entry in ('master', 'minion', 'sub_minion', 'syndic', 'syndic_master', 'proxy'): - computed_config = copy.deepcopy(locals()['{0}_opts'.format(entry)]) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry), 'w') as fp_: - salt.utils.yaml.safe_dump(computed_config, fp_, default_flow_style=False) + for entry in ( + "master", + "minion", + "sub_minion", + "syndic", + "syndic_master", + "proxy", + ): + computed_config = copy.deepcopy(locals()["{0}_opts".format(entry)]) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, entry), "w" + ) as fp_: + salt.utils.yaml.safe_dump( + computed_config, fp_, default_flow_style=False + ) sub_minion_computed_config = copy.deepcopy(sub_minion_opts) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, 'minion'), 'w') as wfh: - salt.utils.yaml.safe_dump(sub_minion_computed_config, wfh, default_flow_style=False) - shutil.copyfile(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master'), os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, 'master')) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, "minion"), "w" + ) as wfh: + salt.utils.yaml.safe_dump( + sub_minion_computed_config, wfh, default_flow_style=False + ) + shutil.copyfile( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master"), + os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, "master"), + ) syndic_master_computed_config = copy.deepcopy(syndic_master_opts) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, 'master'), 'w') as wfh: - salt.utils.yaml.safe_dump(syndic_master_computed_config, wfh, default_flow_style=False) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, "master"), "w" + ) as wfh: + salt.utils.yaml.safe_dump( + syndic_master_computed_config, wfh, default_flow_style=False + ) syndic_computed_config = copy.deepcopy(syndic_opts) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, 'minion'), 'w') as wfh: - salt.utils.yaml.safe_dump(syndic_computed_config, wfh, default_flow_style=False) - shutil.copyfile(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master'), os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, 'master')) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, "minion"), "w" + ) as wfh: + salt.utils.yaml.safe_dump( + syndic_computed_config, wfh, default_flow_style=False + ) + shutil.copyfile( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master"), + os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, "master"), + ) # <---- Transcribe Configuration ----------------------------------------------------------------------------- # ----- Verify Environment ----------------------------------------------------------------------------------> - master_opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master')) - minion_opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) - syndic_opts = salt.config.syndic_config( - os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, 'master'), - os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, 'minion'), + master_opts = salt.config.master_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master") + ) + minion_opts = salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion") + ) + syndic_opts = salt.config.syndic_config( + os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, "master"), + os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, "minion"), + ) + sub_minion_opts = salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, "minion") + ) + syndic_master_opts = salt.config.master_config( + os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, "master") + ) + proxy_opts = salt.config.proxy_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "proxy") ) - sub_minion_opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, 'minion')) - syndic_master_opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, 'master')) - proxy_opts = salt.config.proxy_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'proxy')) - RUNTIME_VARS.RUNTIME_CONFIGS['master'] = freeze(master_opts) - RUNTIME_VARS.RUNTIME_CONFIGS['minion'] = freeze(minion_opts) - RUNTIME_VARS.RUNTIME_CONFIGS['syndic'] = freeze(syndic_opts) - RUNTIME_VARS.RUNTIME_CONFIGS['sub_minion'] = freeze(sub_minion_opts) - RUNTIME_VARS.RUNTIME_CONFIGS['syndic_master'] = freeze(syndic_master_opts) - RUNTIME_VARS.RUNTIME_CONFIGS['proxy'] = freeze(proxy_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["master"] = freeze(master_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["minion"] = freeze(minion_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["syndic"] = freeze(syndic_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["sub_minion"] = freeze(sub_minion_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["syndic_master"] = freeze(syndic_master_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["proxy"] = freeze(proxy_opts) - verify_env([os.path.join(master_opts['pki_dir'], 'minions'), - os.path.join(master_opts['pki_dir'], 'minions_pre'), - os.path.join(master_opts['pki_dir'], 'minions_rejected'), - os.path.join(master_opts['pki_dir'], 'minions_denied'), - os.path.join(master_opts['cachedir'], 'jobs'), - os.path.join(master_opts['root_dir'], 'cache', 'tokens'), - os.path.join(syndic_master_opts['pki_dir'], 'minions'), - os.path.join(syndic_master_opts['pki_dir'], 'minions_pre'), - os.path.join(syndic_master_opts['pki_dir'], 'minions_rejected'), - os.path.join(syndic_master_opts['cachedir'], 'jobs'), - os.path.join(syndic_master_opts['root_dir'], 'cache', 'tokens'), - os.path.join(master_opts['pki_dir'], 'accepted'), - os.path.join(master_opts['pki_dir'], 'rejected'), - os.path.join(master_opts['pki_dir'], 'pending'), - os.path.join(syndic_master_opts['pki_dir'], 'accepted'), - os.path.join(syndic_master_opts['pki_dir'], 'rejected'), - os.path.join(syndic_master_opts['pki_dir'], 'pending'), - - os.path.join(minion_opts['pki_dir'], 'accepted'), - os.path.join(minion_opts['pki_dir'], 'rejected'), - os.path.join(minion_opts['pki_dir'], 'pending'), - os.path.join(sub_minion_opts['pki_dir'], 'accepted'), - os.path.join(sub_minion_opts['pki_dir'], 'rejected'), - os.path.join(sub_minion_opts['pki_dir'], 'pending'), - os.path.dirname(master_opts['log_file']), - minion_opts['extension_modules'], - sub_minion_opts['extension_modules'], - sub_minion_opts['pki_dir'], - master_opts['sock_dir'], - syndic_master_opts['sock_dir'], - sub_minion_opts['sock_dir'], - minion_opts['sock_dir'], - RUNTIME_VARS.TMP_STATE_TREE, - RUNTIME_VARS.TMP_PILLAR_TREE, - RUNTIME_VARS.TMP_PRODENV_STATE_TREE, - TMP, - ], - RUNTIME_VARS.RUNNING_TESTS_USER, - root_dir=master_opts['root_dir'], - ) + verify_env( + [ + os.path.join(master_opts["pki_dir"], "minions"), + os.path.join(master_opts["pki_dir"], "minions_pre"), + os.path.join(master_opts["pki_dir"], "minions_rejected"), + os.path.join(master_opts["pki_dir"], "minions_denied"), + os.path.join(master_opts["cachedir"], "jobs"), + os.path.join(master_opts["root_dir"], "cache", "tokens"), + os.path.join(syndic_master_opts["pki_dir"], "minions"), + os.path.join(syndic_master_opts["pki_dir"], "minions_pre"), + os.path.join(syndic_master_opts["pki_dir"], "minions_rejected"), + os.path.join(syndic_master_opts["cachedir"], "jobs"), + os.path.join(syndic_master_opts["root_dir"], "cache", "tokens"), + os.path.join(master_opts["pki_dir"], "accepted"), + os.path.join(master_opts["pki_dir"], "rejected"), + os.path.join(master_opts["pki_dir"], "pending"), + os.path.join(syndic_master_opts["pki_dir"], "accepted"), + os.path.join(syndic_master_opts["pki_dir"], "rejected"), + os.path.join(syndic_master_opts["pki_dir"], "pending"), + os.path.join(minion_opts["pki_dir"], "accepted"), + os.path.join(minion_opts["pki_dir"], "rejected"), + os.path.join(minion_opts["pki_dir"], "pending"), + os.path.join(sub_minion_opts["pki_dir"], "accepted"), + os.path.join(sub_minion_opts["pki_dir"], "rejected"), + os.path.join(sub_minion_opts["pki_dir"], "pending"), + os.path.dirname(master_opts["log_file"]), + minion_opts["extension_modules"], + sub_minion_opts["extension_modules"], + sub_minion_opts["pki_dir"], + master_opts["sock_dir"], + syndic_master_opts["sock_dir"], + sub_minion_opts["sock_dir"], + minion_opts["sock_dir"], + RUNTIME_VARS.TMP_STATE_TREE, + RUNTIME_VARS.TMP_PILLAR_TREE, + RUNTIME_VARS.TMP_PRODENV_STATE_TREE, + TMP, + ], + RUNTIME_VARS.RUNNING_TESTS_USER, + root_dir=master_opts["root_dir"], + ) cls.master_opts = master_opts cls.minion_opts = minion_opts @@ -1060,33 +1245,33 @@ class TestDaemon(object): # <---- Verify Environment ----------------------------------------------------------------------------------- def __exit__(self, type, value, traceback): - ''' + """ Kill the minion and master processes - ''' + """ try: - if hasattr(self.sub_minion_process, 'terminate'): + if hasattr(self.sub_minion_process, "terminate"): self.sub_minion_process.terminate() else: - log.error('self.sub_minion_process can\'t be terminate.') + log.error("self.sub_minion_process can't be terminate.") except AttributeError: pass try: - if hasattr(self.minion_process, 'terminate'): + if hasattr(self.minion_process, "terminate"): self.minion_process.terminate() else: - log.error('self.minion_process can\'t be terminate.') + log.error("self.minion_process can't be terminate.") except AttributeError: pass - if hasattr(self, 'proxy_process'): + if hasattr(self, "proxy_process"): self.proxy_process.terminate() try: - if hasattr(self.master_process, 'terminate'): + if hasattr(self.master_process, "terminate"): self.master_process.terminate() else: - log.error('self.master_process can\'t be terminate.') + log.error("self.master_process can't be terminate.") except AttributeError: pass @@ -1109,29 +1294,29 @@ class TestDaemon(object): self.log_server_process.join() def pre_setup_minions(self): - ''' + """ Subclass this method for additional minion setups. - ''' + """ def setup_minions(self): - ''' + """ Minions setup routines - ''' + """ def post_setup_minions(self): - ''' + """ Subclass this method to execute code after the minions have been setup - ''' + """ def _enter_mockbin(self): - path = os.environ.get('PATH', '') + path = os.environ.get("PATH", "") path_items = path.split(os.pathsep) if MOCKBIN not in path_items: path_items.insert(0, MOCKBIN) - os.environ['PATH'] = os.pathsep.join(path_items) + os.environ["PATH"] = os.pathsep.join(path_items) def _exit_ssh(self): - if hasattr(self, 'sshd_process'): + if hasattr(self, "sshd_process"): try: self.sshd_process.kill() except OSError as exc: @@ -1145,32 +1330,37 @@ class TestDaemon(object): raise def _exit_mockbin(self): - path = os.environ.get('PATH', '') + path = os.environ.get("PATH", "") path_items = path.split(os.pathsep) try: path_items.remove(MOCKBIN) except ValueError: pass - os.environ['PATH'] = os.pathsep.join(path_items) + os.environ["PATH"] = os.pathsep.join(path_items) @classmethod def clean(cls): - ''' + """ Clean out the tmp files - ''' + """ + def remove_readonly(func, path, excinfo): if os.path.exists(path): # Give full permissions to owner os.chmod(path, stat.S_IRWXU) func(path) - for dirname in (TMP, RUNTIME_VARS.TMP_STATE_TREE, - RUNTIME_VARS.TMP_PILLAR_TREE, RUNTIME_VARS.TMP_PRODENV_STATE_TREE): + for dirname in ( + TMP, + RUNTIME_VARS.TMP_STATE_TREE, + RUNTIME_VARS.TMP_PILLAR_TREE, + RUNTIME_VARS.TMP_PRODENV_STATE_TREE, + ): if os.path.isdir(dirname): try: shutil.rmtree(six.text_type(dirname), onerror=remove_readonly) except Exception: # pylint: disable=broad-except - log.exception('Failed to remove directory: %s', dirname) + log.exception("Failed to remove directory: %s", dirname) def wait_for_jid(self, targets, jid, timeout=120): time.sleep(1) # Allow some time for minions to accept jobs @@ -1180,8 +1370,8 @@ class TestDaemon(object): while now <= expire: running = self.__client_job_running(targets, jid) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) if not running and job_finished is False: @@ -1194,9 +1384,9 @@ class TestDaemon(object): if job_finished is False: sys.stdout.write( - ' * {LIGHT_YELLOW}[Quit in {0}]{ENDC} Waiting for {1}'.format( - '{0}'.format(expire - now).rsplit('.', 1)[0], - ', '.join(running), + " * {LIGHT_YELLOW}[Quit in {0}]{ENDC} Waiting for {1}".format( + "{0}".format(expire - now).rsplit(".", 1)[0], + ", ".join(running), **self.colors ) ) @@ -1205,73 +1395,66 @@ class TestDaemon(object): now = datetime.now() else: # pylint: disable=W0120 sys.stdout.write( - '\n {LIGHT_RED}*{ENDC} ERROR: Failed to get information ' - 'back\n'.format(**self.colors) + "\n {LIGHT_RED}*{ENDC} ERROR: Failed to get information " + "back\n".format(**self.colors) ) sys.stdout.flush() return False def __client_job_running(self, targets, jid): - running = self.client.cmd( - list(targets), 'saltutil.running', tgt_type='list' - ) - return [ - k for (k, v) in six.iteritems(running) if v and v[0]['jid'] == jid - ] + running = self.client.cmd(list(targets), "saltutil.running", tgt_type="list") + return [k for (k, v) in six.iteritems(running) if v and v[0]["jid"] == jid] def sync_minion_modules_(self, modules_kind, targets, timeout=None): if not timeout: timeout = 120 # Let's sync all connected minions print( - ' {LIGHT_BLUE}*{ENDC} Syncing minion\'s {1} ' - '(saltutil.sync_{1})'.format( - ', '.join(targets), - modules_kind, - **self.colors + " {LIGHT_BLUE}*{ENDC} Syncing minion's {1} " + "(saltutil.sync_{1})".format( + ", ".join(targets), modules_kind, **self.colors ) ) syncing = set(targets) jid_info = self.client.run_job( - list(targets), 'saltutil.sync_{0}'.format(modules_kind), - tgt_type='list', + list(targets), + "saltutil.sync_{0}".format(modules_kind), + tgt_type="list", timeout=999999999999999, ) - if self.wait_for_jid(targets, jid_info['jid'], timeout) is False: + if self.wait_for_jid(targets, jid_info["jid"], timeout) is False: print( - ' {LIGHT_RED}*{ENDC} WARNING: Minions failed to sync {0}. ' - 'Tests requiring these {0} WILL fail'.format( - modules_kind, **self.colors) + " {LIGHT_RED}*{ENDC} WARNING: Minions failed to sync {0}. " + "Tests requiring these {0} WILL fail".format( + modules_kind, **self.colors + ) ) raise SystemExit() while syncing: - rdata = self.client.get_full_returns(jid_info['jid'], syncing, 1) + rdata = self.client.get_full_returns(jid_info["jid"], syncing, 1) if rdata: for name, output in six.iteritems(rdata): - if not output['ret']: + if not output["ret"]: # Already synced!? syncing.remove(name) continue - if isinstance(output['ret'], six.string_types): + if isinstance(output["ret"], six.string_types): # An errors has occurred print( - ' {LIGHT_RED}*{ENDC} {0} Failed to sync {2}: ' - '{1}'.format( - name, output['ret'], - modules_kind, - **self.colors) + " {LIGHT_RED}*{ENDC} {0} Failed to sync {2}: " + "{1}".format( + name, output["ret"], modules_kind, **self.colors + ) ) return False print( - ' {LIGHT_GREEN}*{ENDC} Synced {0} {2}: ' - '{1}'.format( - name, - ', '.join(output['ret']), - modules_kind, **self.colors + " {LIGHT_GREEN}*{ENDC} Synced {0} {2}: " + "{1}".format( + name, ", ".join(output["ret"]), modules_kind, **self.colors ) ) # Synced! @@ -1279,35 +1462,35 @@ class TestDaemon(object): syncing.remove(name) except KeyError: print( - ' {LIGHT_RED}*{ENDC} {0} already synced??? ' - '{1}'.format(name, output, **self.colors) + " {LIGHT_RED}*{ENDC} {0} already synced??? " + "{1}".format(name, output, **self.colors) ) return True def sync_minion_states(self, targets, timeout=None): - salt.utils.process.appendproctitle('SyncMinionStates') - self.sync_minion_modules_('states', targets, timeout=timeout) + salt.utils.process.appendproctitle("SyncMinionStates") + self.sync_minion_modules_("states", targets, timeout=timeout) def sync_minion_modules(self, targets, timeout=None): - salt.utils.process.appendproctitle('SyncMinionModules') - self.sync_minion_modules_('modules', targets, timeout=timeout) + salt.utils.process.appendproctitle("SyncMinionModules") + self.sync_minion_modules_("modules", targets, timeout=timeout) def sync_minion_grains(self, targets, timeout=None): - salt.utils.process.appendproctitle('SyncMinionGrains') - self.sync_minion_modules_('grains', targets, timeout=timeout) + salt.utils.process.appendproctitle("SyncMinionGrains") + self.sync_minion_modules_("grains", targets, timeout=timeout) def wait_for_minions(self, start, timeout, sleep=5): - ''' + """ Ensure all minions and masters (including sub-masters) are connected. - ''' + """ while True: try: - ret = self.client.run_job('*', 'test.ping') + ret = self.client.run_job("*", "test.ping") except salt.exceptions.SaltClientError: ret = None - if ret and 'minions' not in ret: + if ret and "minions" not in ret: continue - if ret and sorted(ret['minions']) == sorted(self.minion_targets): + if ret and sorted(ret["minions"]) == sorted(self.minion_targets): break if time.time() - start >= timeout: raise RuntimeError("Ping Minions Failed") diff --git a/tests/integration/cli/test_batch.py b/tests/integration/cli/test_batch.py index 6a609798444..5c35b8f171f 100644 --- a/tests/integration/cli/test_batch.py +++ b/tests/integration/cli/test_batch.py @@ -1,60 +1,57 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs +import pytest +import salt.utils.platform from tests.support.case import ShellCase -# Import Salt libs -import salt.utils.platform - +@pytest.mark.windows_whitelisted class BatchTest(ShellCase): - ''' + """ Integration tests for the salt.cli.batch module - ''' + """ + if salt.utils.platform.is_windows(): run_timeout = 180 else: run_timeout = 30 def test_batch_run(self): - ''' + """ Tests executing a simple batch command to help catch regressions - ''' - ret = 'Executing run on [{0}]'.format(repr('sub_minion')) + """ + ret = "Executing run on [{0}]".format(repr("sub_minion")) cmd = self.run_salt( - '"*minion" test.echo "batch testing" -b 50%', - timeout=self.run_timeout, + '"*minion" test.echo "batch testing" -b 50%', timeout=self.run_timeout, ) self.assertIn(ret, cmd) def test_batch_run_number(self): - ''' + """ Tests executing a simple batch command using a number division instead of a percentage with full batch CLI call. - ''' - ret = "Executing run on [{0}, {1}]".format(repr('minion'), repr('sub_minion')) + """ + ret = "Executing run on [{0}, {1}]".format(repr("minion"), repr("sub_minion")) cmd = self.run_salt( - '"*minion" test.ping --batch-size 2', - timeout=self.run_timeout, + '"*minion" test.ping --batch-size 2', timeout=self.run_timeout, ) self.assertIn(ret, cmd) def test_batch_run_grains_targeting(self): - ''' + """ Tests executing a batch command using a percentage divisor as well as grains targeting. - ''' - os_grain = '' - sub_min_ret = "Executing run on [{0}]".format(repr('sub_minion')) - min_ret = "Executing run on [{0}]".format(repr('minion')) + """ + os_grain = "" + sub_min_ret = "Executing run on [{0}]".format(repr("sub_minion")) + min_ret = "Executing run on [{0}]".format(repr("minion")) - for item in self.run_salt('minion grains.get os'): - if item != 'minion:': + for item in self.run_salt("minion grains.get os"): + if item != "minion:": os_grain = item os_grain = os_grain.strip() @@ -66,9 +63,9 @@ class BatchTest(ShellCase): self.assertIn(min_ret, cmd) def test_batch_exit_code(self): - ''' + """ Test that a failed state returns a non-zero exit code in batch mode - ''' + """ cmd = self.run_salt( ' "*" state.single test.fail_without_changes name=test_me -b 25%', with_retcode=True, @@ -76,14 +73,14 @@ class BatchTest(ShellCase): ) self.assertEqual(cmd[-1], 2) -# Test for failhard + batch. The best possible solution here was to do something like that: -# assertRaises(StopIteration) -# But it's impossible due to nature of the tests execution via fork() + # Test for failhard + batch. The best possible solution here was to do something like that: + # assertRaises(StopIteration) + # But it's impossible due to nature of the tests execution via fork() def test_batch_module_stopping_after_error(self): - ''' + """ Test that a failed command stops the batch run - ''' + """ minions_list = [] retcode = None @@ -97,9 +94,9 @@ class BatchTest(ShellCase): # Parsing the output. Idea is to fetch number on minions and retcode of the execution. # retcode var could be overwritten in case of broken failhard but number of minions check should still fail. for line in cmd: - if line.startswith('Executing run on'): + if line.startswith("Executing run on"): minions_list.append(line) - if line.startswith('retcode'): + if line.startswith("retcode"): retcode = line[-1] # We expect to have only one minion to be run self.assertEqual(1, len(minions_list)) @@ -109,9 +106,9 @@ class BatchTest(ShellCase): self.assertNotEqual(0, retcode) def test_batch_state_stopping_after_error(self): - ''' + """ Test that a failed state stops the batch run - ''' + """ minions_list = [] retcode = None @@ -125,9 +122,9 @@ class BatchTest(ShellCase): # Parsing the output. Idea is to fetch number on minions and retcode of the execution. # retcode var could be overwritten in case of broken failhard but number of minions check should still fail. for line in cmd: - if line.startswith('Executing run on'): + if line.startswith("Executing run on"): minions_list.append(line) - if line.startswith('retcode'): + if line.startswith("retcode"): retcode = line[-1] # We expect to have only one minion to be run self.assertEqual(1, len(minions_list)) diff --git a/tests/integration/cli/test_custom_module.py b/tests/integration/cli/test_custom_module.py index ee18b0777cf..16f2275a835 100644 --- a/tests/integration/cli/test_custom_module.py +++ b/tests/integration/cli/test_custom_module.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Daniel Mizyrycki (mzdaniel@glidelink.net) @@ -33,48 +33,48 @@ This test can be run in a small test suite with: $ python tests/runtests.py -C --ssh -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals -# Import test Libs +import pytest from tests.support.case import SSHCase +@pytest.mark.windows_whitelisted class SSHCustomModuleTest(SSHCase): - ''' + """ Test sls with custom module functionality using ssh - ''' + """ def test_ssh_regular_module(self): - ''' + """ Test regular module work using SSHCase environment - ''' - expected = 'hello' - cmd = self.run_function('test.echo', arg=['hello']) + """ + expected = "hello" + cmd = self.run_function("test.echo", arg=["hello"]) self.assertEqual(expected, cmd) def test_ssh_custom_module(self): - ''' + """ Test custom module work using SSHCase environment - ''' - expected = 'hello'[::-1] - cmd = self.run_function('test.recho', arg=['hello']) + """ + expected = "hello"[::-1] + cmd = self.run_function("test.recho", arg=["hello"]) self.assertEqual(expected, cmd) def test_ssh_sls_with_custom_module(self): - ''' + """ Test sls with custom module work using SSHCase environment - ''' + """ expected = { - "module_|-regular-module_|-test.echo_|-run": 'hello', - "module_|-custom-module_|-test.recho_|-run": 'olleh'} - cmd = self.run_function('state.sls', arg=['custom_module']) + "module_|-regular-module_|-test.echo_|-run": "hello", + "module_|-custom-module_|-test.recho_|-run": "olleh", + } + cmd = self.run_function("state.sls", arg=["custom_module"]) for key in cmd: if not isinstance(cmd, dict) or not isinstance(cmd[key], dict): - raise AssertionError('{0} is not a proper state return' - .format(cmd)) - elif not cmd[key]['result']: - raise AssertionError(cmd[key]['comment']) - cmd_ret = cmd[key]['changes'].get('ret', None) + raise AssertionError("{0} is not a proper state return".format(cmd)) + elif not cmd[key]["result"]: + raise AssertionError(cmd[key]["comment"]) + cmd_ret = cmd[key]["changes"].get("ret", None) self.assertEqual(cmd_ret, expected[key]) diff --git a/tests/integration/cli/test_grains.py b/tests/integration/cli/test_grains.py index fc81500307c..da82a6f93f2 100644 --- a/tests/integration/cli/test_grains.py +++ b/tests/integration/cli/test_grains.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Daniel Mizyrycki (mzdaniel@glidelink.net) @@ -11,87 +11,90 @@ $ salt-ssh localhost grains.get id localhost: localhost -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Libs +import pytest import salt.utils.files - -# Import Salt Testing Libs from tests.support.case import ShellCase, SSHCase from tests.support.helpers import flaky +@pytest.mark.windows_whitelisted class GrainsTargetingTest(ShellCase): - ''' + """ Integration tests for targeting with grains. - ''' + """ def test_grains_targeting_os_running(self): - ''' + """ Tests running "salt -G 'os:<system-os>' test.ping and minions both return True - ''' - test_ret = ['sub_minion:', ' True', 'minion:', ' True'] + """ + test_ret = ["sub_minion:", " True", "minion:", " True"] - os_grain = '' - for item in self.run_salt('minion grains.get os'): - if item != 'minion:': + os_grain = "" + for item in self.run_salt("minion grains.get os"): + if item != "minion:": os_grain = item.strip() ret = self.run_salt('-G "os:{0}" test.ping'.format(os_grain)) self.assertEqual(sorted(ret), sorted(test_ret)) def test_grains_targeting_minion_id_running(self): - ''' + """ Tests return of each running test minion targeting with minion id grain - ''' + """ minion = self.run_salt('-G "id:minion" test.ping') - self.assertEqual(sorted(minion), sorted(['minion:', ' True'])) + self.assertEqual(sorted(minion), sorted(["minion:", " True"])) sub_minion = self.run_salt('-G "id:sub_minion" test.ping') - self.assertEqual(sorted(sub_minion), sorted(['sub_minion:', ' True'])) + self.assertEqual(sorted(sub_minion), sorted(["sub_minion:", " True"])) @flaky def test_grains_targeting_disconnected(self): - ''' + """ Tests return of minion using grains targeting on a disconnected minion. - ''' - test_ret = 'Minion did not return. [No response]' + """ + test_ret = "Minion did not return. [No response]" # Create a minion key, but do not start the "fake" minion. This mimics a # disconnected minion. - key_file = os.path.join(self.master_opts['pki_dir'], 'minions', 'disconnected') - with salt.utils.files.fopen(key_file, 'a'): + key_file = os.path.join(self.master_opts["pki_dir"], "minions", "disconnected") + with salt.utils.files.fopen(key_file, "a"): pass import logging + log = logging.getLogger(__name__) # ping disconnected minion and ensure it times out and returns with correct message try: if salt.utils.platform.is_windows(): cmd_str = '-t 1 -G "id:disconnected" test.ping' else: - cmd_str = '-t 1 -G \'id:disconnected\' test.ping' - ret = '' - for item in self.run_salt('-t 1 -G "id:disconnected" test.ping', timeout=40): - if item != 'disconnected:': + cmd_str = "-t 1 -G 'id:disconnected' test.ping" + ret = "" + for item in self.run_salt( + '-t 1 -G "id:disconnected" test.ping', timeout=40 + ): + if item != "disconnected:": ret = item.strip() assert ret == test_ret finally: os.unlink(key_file) +@pytest.mark.windows_whitelisted class SSHGrainsTest(SSHCase): - ''' + """ Test salt-ssh grains functionality Depend on proper environment set by SSHCase class - ''' + """ def test_grains_id(self): - ''' + """ Test salt-ssh grains id work for localhost. - ''' - cmd = self.run_function('grains.get', ['id']) - self.assertEqual(cmd, 'localhost') + """ + cmd = self.run_function("grains.get", ["id"]) + self.assertEqual(cmd, "localhost") diff --git a/tests/integration/client/test_kwarg.py b/tests/integration/client/test_kwarg.py index 7b8022eae1b..23ed3041cd5 100644 --- a/tests/integration/client/test_kwarg.py +++ b/tests/integration/client/test_kwarg.py @@ -1,132 +1,112 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest +import salt.utils.platform +from salt.ext import six from tests.support.case import ModuleCase -# Import 3rd-party libs -from salt.ext import six -import salt.utils.platform - +@pytest.mark.windows_whitelisted class StdTest(ModuleCase): - ''' + """ Test standard client calls - ''' + """ + def setUp(self): self.TIMEOUT = 600 if salt.utils.platform.is_windows() else 10 def test_cli(self): - ''' + """ Test cli function - ''' + """ cmd_iter = self.client.cmd_cli( - 'minion', - 'test.arg', - ['foo', 'bar', 'baz'], - kwarg={'qux': 'quux'} - ) + "minion", "test.arg", ["foo", "bar", "baz"], kwarg={"qux": "quux"} + ) for ret in cmd_iter: - data = ret['minion']['ret'] - self.assertEqual(data['args'], ['foo', 'bar', 'baz']) - self.assertEqual(data['kwargs']['qux'], 'quux') + data = ret["minion"]["ret"] + self.assertEqual(data["args"], ["foo", "bar", "baz"]) + self.assertEqual(data["kwargs"]["qux"], "quux") def test_iter(self): - ''' + """ test cmd_iter - ''' + """ cmd_iter = self.client.cmd_iter( - 'minion', - 'test.arg', - ['foo', 'bar', 'baz'], - kwarg={'qux': 'quux'} - ) + "minion", "test.arg", ["foo", "bar", "baz"], kwarg={"qux": "quux"} + ) for ret in cmd_iter: - data = ret['minion']['ret'] - self.assertEqual(data['args'], ['foo', 'bar', 'baz']) - self.assertEqual(data['kwargs']['qux'], 'quux') + data = ret["minion"]["ret"] + self.assertEqual(data["args"], ["foo", "bar", "baz"]) + self.assertEqual(data["kwargs"]["qux"], "quux") def test_iter_no_block(self): - ''' + """ test cmd_iter_no_block - ''' + """ cmd_iter = self.client.cmd_iter_no_block( - 'minion', - 'test.arg', - ['foo', 'bar', 'baz'], - kwarg={'qux': 'quux'} - ) + "minion", "test.arg", ["foo", "bar", "baz"], kwarg={"qux": "quux"} + ) for ret in cmd_iter: if ret is None: continue - data = ret['minion']['ret'] - self.assertEqual(data['args'], ['foo', 'bar', 'baz']) - self.assertEqual(data['kwargs']['qux'], 'quux') + data = ret["minion"]["ret"] + self.assertEqual(data["args"], ["foo", "bar", "baz"]) + self.assertEqual(data["kwargs"]["qux"], "quux") def test_full_returns(self): - ''' + """ test cmd_iter - ''' + """ ret = self.client.cmd_full_return( - 'minion', - 'test.arg', - ['foo', 'bar', 'baz'], - timeout=self.TIMEOUT, - kwarg={'qux': 'quux'} - ) - data = ret['minion']['ret'] - self.assertEqual(data['args'], ['foo', 'bar', 'baz']) - self.assertEqual(data['kwargs']['qux'], 'quux') + "minion", + "test.arg", + ["foo", "bar", "baz"], + timeout=self.TIMEOUT, + kwarg={"qux": "quux"}, + ) + data = ret["minion"]["ret"] + self.assertEqual(data["args"], ["foo", "bar", "baz"]) + self.assertEqual(data["kwargs"]["qux"], "quux") def test_kwarg_type(self): - ''' + """ Test that kwargs end up on the client as the same type - ''' + """ terrible_yaml_string = 'foo: ""\n# \'' ret = self.client.cmd_full_return( - 'minion', - 'test.arg_type', - ['a', 1], - kwarg={ - 'outer': {'a': terrible_yaml_string}, - 'inner': 'value' - }, + "minion", + "test.arg_type", + ["a", 1], + kwarg={"outer": {"a": terrible_yaml_string}, "inner": "value"}, timeout=self.TIMEOUT, ) - data = ret['minion']['ret'] - self.assertIn(six.text_type.__name__, data['args'][0]) - self.assertIn('int', data['args'][1]) - self.assertIn('dict', data['kwargs']['outer']) - self.assertIn(six.text_type.__name__, data['kwargs']['inner']) + data = ret["minion"]["ret"] + self.assertIn(six.text_type.__name__, data["args"][0]) + self.assertIn("int", data["args"][1]) + self.assertIn("dict", data["kwargs"]["outer"]) + self.assertIn(six.text_type.__name__, data["kwargs"]["inner"]) def test_full_return_kwarg(self): ret = self.client.cmd( - 'minion', 'test.ping', full_return=True, timeout=self.TIMEOUT, + "minion", "test.ping", full_return=True, timeout=self.TIMEOUT, ) for mid, data in ret.items(): - self.assertIn('retcode', data) + self.assertIn("retcode", data) def test_cmd_arg_kwarg_parsing(self): - ret = self.client.cmd('minion', 'test.arg_clean', - arg=[ - 'foo', - 'bar=off', - 'baz={qux: 123}' - ], - kwarg={ - 'quux': 'Quux', - }, + ret = self.client.cmd( + "minion", + "test.arg_clean", + arg=["foo", "bar=off", "baz={qux: 123}"], + kwarg={"quux": "Quux"}, timeout=self.TIMEOUT, ) - self.assertEqual(ret['minion'], { - 'args': ['foo'], - 'kwargs': { - 'bar': False, - 'baz': { - 'qux': 123, - }, - 'quux': 'Quux', + self.assertEqual( + ret["minion"], + { + "args": ["foo"], + "kwargs": {"bar": False, "baz": {"qux": 123}, "quux": "Quux"}, }, - }) + ) diff --git a/tests/integration/client/test_runner.py b/tests/integration/client/test_runner.py index 1db701a1d13..fc81531f38c 100644 --- a/tests/integration/client/test_runner.py +++ b/tests/integration/client/test_runner.py @@ -1,67 +1,63 @@ # coding: utf-8 -# Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mixins import AdaptedConfigurationTestCaseMixin - -# Import Salt libs +import pytest import salt.runner +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.unit import TestCase +@pytest.mark.windows_whitelisted class RunnerModuleTest(TestCase, AdaptedConfigurationTestCaseMixin): # This is really an integration test since it needs a salt-master running eauth_creds = { - 'username': 'saltdev_auto', - 'password': 'saltdev', - 'eauth': 'auto', + "username": "saltdev_auto", + "password": "saltdev", + "eauth": "auto", } def setUp(self): - ''' + """ Configure an eauth user to test with - ''' - self.runner = salt.runner.RunnerClient(self.get_config('client_config')) + """ + self.runner = salt.runner.RunnerClient(self.get_config("client_config")) def test_eauth(self): - ''' + """ Test executing master_call with lowdata The choice of using error.error for this is arbitrary and should be changed to some mocked function that is more testing friendly. - ''' + """ low = { - 'client': 'runner', - 'fun': 'error.error', + "client": "runner", + "fun": "error.error", } low.update(self.eauth_creds) self.runner.master_call(**low) def test_token(self): - ''' + """ Test executing master_call with lowdata The choice of using error.error for this is arbitrary and should be changed to some mocked function that is more testing friendly. - ''' + """ import salt.auth - auth = salt.auth.LoadAuth(self.get_config('client_config')) + auth = salt.auth.LoadAuth(self.get_config("client_config")) token = auth.mk_token(self.eauth_creds) - self.runner.master_call(**{ - 'client': 'runner', - 'fun': 'error.error', - 'token': token['token'], - }) + self.runner.master_call( + **{"client": "runner", "fun": "error.error", "token": token["token"]} + ) def test_cmd_sync(self): low = { - 'client': 'runner', - 'fun': 'error.error', + "client": "runner", + "fun": "error.error", } low.update(self.eauth_creds) @@ -69,8 +65,8 @@ class RunnerModuleTest(TestCase, AdaptedConfigurationTestCaseMixin): def test_cmd_async(self): low = { - 'client': 'runner', - 'fun': 'error.error', + "client": "runner", + "fun": "error.error", } low.update(self.eauth_creds) @@ -78,69 +74,64 @@ class RunnerModuleTest(TestCase, AdaptedConfigurationTestCaseMixin): def test_cmd_sync_w_arg(self): low = { - 'fun': 'test.arg', - 'foo': 'Foo!', - 'bar': 'Bar!', + "fun": "test.arg", + "foo": "Foo!", + "bar": "Bar!", } low.update(self.eauth_creds) ret = self.runner.cmd_sync(low) - self.assertEqual(ret['kwargs']['foo'], 'Foo!') - self.assertEqual(ret['kwargs']['bar'], 'Bar!') + self.assertEqual(ret["kwargs"]["foo"], "Foo!") + self.assertEqual(ret["kwargs"]["bar"], "Bar!") def test_wildcard_auth(self): low = { - 'username': 'the_s0und_of_t3ch', - 'password': 'willrockyou', - 'eauth': 'auto', - 'fun': 'test.arg', - 'foo': 'Foo!', - 'bar': 'Bar!', + "username": "the_s0und_of_t3ch", + "password": "willrockyou", + "eauth": "auto", + "fun": "test.arg", + "foo": "Foo!", + "bar": "Bar!", } self.runner.cmd_sync(low) def test_full_return_kwarg(self): - low = {'fun': 'test.arg'} + low = {"fun": "test.arg"} low.update(self.eauth_creds) ret = self.runner.cmd_sync(low, full_return=True) - self.assertIn('success', ret['data']) + self.assertIn("success", ret["data"]) def test_cmd_sync_arg_kwarg_parsing(self): low = { - 'client': 'runner', - 'fun': 'test.arg', - 'arg': [ - 'foo', - 'bar=off', - 'baz={qux: 123}' - ], - 'kwarg': { - 'quux': 'Quux', - }, - 'quuz': 'on', + "client": "runner", + "fun": "test.arg", + "arg": ["foo", "bar=off", "baz={qux: 123}"], + "kwarg": {"quux": "Quux"}, + "quuz": "on", } low.update(self.eauth_creds) ret = self.runner.cmd_sync(low) - self.assertEqual(ret, { - 'args': ['foo'], - 'kwargs': { - 'bar': False, - 'baz': { - 'qux': 123, + self.assertEqual( + ret, + { + "args": ["foo"], + "kwargs": { + "bar": False, + "baz": {"qux": 123}, + "quux": "Quux", + "quuz": "on", }, - 'quux': 'Quux', - 'quuz': 'on', }, - }) + ) def test_invalid_kwargs_are_ignored(self): low = { - 'client': 'runner', - 'fun': 'test.metasyntactic', - 'thiskwargisbad': 'justpretendimnothere', + "client": "runner", + "fun": "test.metasyntactic", + "thiskwargisbad": "justpretendimnothere", } low.update(self.eauth_creds) ret = self.runner.cmd_sync(low) - self.assertEqual(ret[0], 'foo') + self.assertEqual(ret[0], "foo") diff --git a/tests/integration/client/test_standard.py b/tests/integration/client/test_standard.py index 8118036a2ed..4b3e05577b2 100644 --- a/tests/integration/client/test_standard.py +++ b/tests/integration/client/test_standard.py @@ -1,153 +1,121 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing libs -from tests.support.case import ModuleCase - -# Import salt libs +import pytest import salt.utils.files import salt.utils.platform +from tests.support.case import ModuleCase +@pytest.mark.windows_whitelisted class StdTest(ModuleCase): - ''' + """ Test standard client calls - ''' + """ + def setUp(self): self.TIMEOUT = 600 if salt.utils.platform.is_windows() else 10 def test_cli(self): - ''' + """ Test cli function - ''' - cmd_iter = self.client.cmd_cli( - 'minion', - 'test.ping', - timeout=20, - ) + """ + cmd_iter = self.client.cmd_cli("minion", "test.ping", timeout=20,) for ret in cmd_iter: - self.assertTrue(ret['minion']) + self.assertTrue(ret["minion"]) # make sure that the iter waits for long running jobs too - cmd_iter = self.client.cmd_cli( - 'minion', - 'test.sleep', - [6], - timeout=20, - ) + cmd_iter = self.client.cmd_cli("minion", "test.sleep", [6], timeout=20,) num_ret = 0 for ret in cmd_iter: num_ret += 1 - self.assertTrue(ret['minion']) + self.assertTrue(ret["minion"]) assert num_ret > 0 # ping a minion that doesn't exist, to make sure that it doesn't hang forever # create fake minion - key_file = os.path.join(self.master_opts['pki_dir'], 'minions', 'footest') + key_file = os.path.join(self.master_opts["pki_dir"], "minions", "footest") # touch the file - with salt.utils.files.fopen(key_file, 'a'): + with salt.utils.files.fopen(key_file, "a"): pass # ping that minion and ensure it times out try: - cmd_iter = self.client.cmd_cli( - 'footest', - 'test.ping', - timeout=20, - ) + cmd_iter = self.client.cmd_cli("footest", "test.ping", timeout=20,) num_ret = 0 for ret in cmd_iter: num_ret += 1 - self.assertTrue(ret['minion']) + self.assertTrue(ret["minion"]) assert num_ret == 0 finally: os.unlink(key_file) def test_iter(self): - ''' + """ test cmd_iter - ''' - cmd_iter = self.client.cmd_iter( - 'minion', - 'test.ping', - ) + """ + cmd_iter = self.client.cmd_iter("minion", "test.ping",) for ret in cmd_iter: - self.assertTrue(ret['minion']) + self.assertTrue(ret["minion"]) def test_iter_no_block(self): - ''' + """ test cmd_iter_no_block - ''' - cmd_iter = self.client.cmd_iter_no_block( - 'minion', - 'test.ping', - ) + """ + cmd_iter = self.client.cmd_iter_no_block("minion", "test.ping",) for ret in cmd_iter: if ret is None: continue - self.assertTrue(ret['minion']) + self.assertTrue(ret["minion"]) def test_batch(self): - ''' + """ test cmd_batch - ''' - cmd_batch = self.client.cmd_batch( - 'minion', - 'test.ping', - ) + """ + cmd_batch = self.client.cmd_batch("minion", "test.ping",) for ret in cmd_batch: - self.assertTrue(ret['minion']) + self.assertTrue(ret["minion"]) def test_batch_raw(self): - ''' + """ test cmd_batch with raw option - ''' - cmd_batch = self.client.cmd_batch( - 'minion', - 'test.ping', - raw=True, - ) + """ + cmd_batch = self.client.cmd_batch("minion", "test.ping", raw=True,) for ret in cmd_batch: - self.assertTrue(ret['data']['success']) + self.assertTrue(ret["data"]["success"]) def test_full_returns(self): - ''' + """ test cmd_iter - ''' - ret = self.client.cmd_full_return( - 'minion', - 'test.ping', - timeout=20, - ) - self.assertIn('minion', ret) - self.assertEqual({'ret': True, 'success': True}, ret['minion']) + """ + ret = self.client.cmd_full_return("minion", "test.ping", timeout=20,) + self.assertIn("minion", ret) + self.assertEqual({"ret": True, "success": True}, ret["minion"]) def test_disconnected_return(self): - ''' + """ Test return/messaging on a disconnected minion - ''' - test_ret = {'ret': 'Minion did not return. [No response]', 'out': 'no_return'} + """ + test_ret = {"ret": "Minion did not return. [No response]", "out": "no_return"} # Create a minion key, but do not start the "fake" minion. This mimics # a disconnected minion. - key_file = os.path.join(self.master_opts['pki_dir'], 'minions', 'disconnected') - with salt.utils.files.fopen(key_file, 'a'): + key_file = os.path.join(self.master_opts["pki_dir"], "minions", "disconnected") + with salt.utils.files.fopen(key_file, "a"): pass # ping disconnected minion and ensure it times out and returns with correct message try: cmd_iter = self.client.cmd_cli( - 'disconnected', - 'test.ping', - show_timeout=True + "disconnected", "test.ping", show_timeout=True ) num_ret = 0 for ret in cmd_iter: num_ret += 1 - self.assertEqual(ret['disconnected']['ret'], test_ret['ret']) - self.assertEqual(ret['disconnected']['out'], test_ret['out']) + self.assertEqual(ret["disconnected"]["ret"], test_ret["ret"]) + self.assertEqual(ret["disconnected"]["out"], test_ret["out"]) # Ensure that we entered the loop above self.assertEqual(num_ret, 1) @@ -156,32 +124,23 @@ class StdTest(ModuleCase): os.unlink(key_file) def test_missing_minion_list(self): - ''' + """ test cmd with missing minion in nodegroup - ''' + """ ret = self.client.cmd( - 'minion,ghostminion', - 'test.ping', - tgt_type='list', - timeout=self.TIMEOUT - ) - self.assertIn('minion', ret) - self.assertIn('ghostminion', ret) - self.assertEqual(True, ret['minion']) - self.assertEqual(u'Minion did not return. [No response]', - ret['ghostminion']) + "minion,ghostminion", "test.ping", tgt_type="list", timeout=self.TIMEOUT + ) + self.assertIn("minion", ret) + self.assertIn("ghostminion", ret) + self.assertEqual(True, ret["minion"]) + self.assertEqual("Minion did not return. [No response]", ret["ghostminion"]) def test_missing_minion_nodegroup(self): - ''' + """ test cmd with missing minion in nodegroup - ''' - ret = self.client.cmd( - 'missing_minion', - 'test.ping', - tgt_type='nodegroup' - ) - self.assertIn('minion', ret) - self.assertIn('ghostminion', ret) - self.assertEqual(True, ret['minion']) - self.assertEqual(u'Minion did not return. [No response]', - ret['ghostminion']) + """ + ret = self.client.cmd("missing_minion", "test.ping", tgt_type="nodegroup") + self.assertIn("minion", ret) + self.assertIn("ghostminion", ret) + self.assertEqual(True, ret["minion"]) + self.assertEqual("Minion did not return. [No response]", ret["ghostminion"]) diff --git a/tests/integration/client/test_syndic.py b/tests/integration/client/test_syndic.py index 93980211232..5d9f485e027 100644 --- a/tests/integration/client/test_syndic.py +++ b/tests/integration/client/test_syndic.py @@ -1,30 +1,25 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import SyndicCase +@pytest.mark.windows_whitelisted class TestSyndic(SyndicCase): - ''' + """ Validate the syndic interface by testing the test module - ''' + """ + def test_ping(self): - ''' + """ test.ping - ''' - self.assertTrue(self.run_function('test.ping')) + """ + self.assertTrue(self.run_function("test.ping")) def test_fib(self): - ''' + """ test.fib - ''' - self.assertEqual( - self.run_function( - 'test.fib', - ['20'], - )[0], - 6765 - ) + """ + self.assertEqual(self.run_function("test.fib", ["20"],)[0], 6765) diff --git a/tests/integration/cloud/clouds/test_digitalocean.py b/tests/integration/cloud/clouds/test_digitalocean.py index f2a243cef7c..8837177afe9 100644 --- a/tests/integration/cloud/clouds/test_digitalocean.py +++ b/tests/integration/cloud/clouds/test_digitalocean.py @@ -1,119 +1,125 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for DigitalOcean APIv2 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import hashlib -# Import Salt Testing Libs -from tests.integration.cloud.helpers.cloud_test_base import CloudTest, TIMEOUT +import salt.crypt # Import Salt Libs import salt.ext.six as six -from salt.ext.six.moves import range -import salt.crypt import salt.utils.stringutils +from salt.ext.six.moves import range + +# Import Salt Testing Libs +from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest class DigitalOceanTest(CloudTest): - ''' + """ Integration tests for the DigitalOcean cloud provider in Salt-Cloud - ''' - PROVIDER = 'digitalocean' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('personal_access_token', 'ssh_key_file', 'ssh_key_name') + """ + + PROVIDER = "digitalocean" + REQUIRED_PROVIDER_CONFIG_ITEMS = ( + "personal_access_token", + "ssh_key_file", + "ssh_key_name", + ) def test_list_images(self): - ''' + """ Tests the return of running the --list-images command for digitalocean - ''' - image_list = self.run_cloud('--list-images {0}'.format(self.PROVIDER)) - self.assertIn( - '14.04.5 x64', - [i.strip() for i in image_list] - ) + """ + image_list = self.run_cloud("--list-images {0}".format(self.PROVIDER)) + self.assertIn("14.04.5 x64", [i.strip() for i in image_list]) def test_list_locations(self): - ''' + """ Tests the return of running the --list-locations command for digitalocean - ''' - _list_locations = self.run_cloud('--list-locations {0}'.format(self.PROVIDER)) - self.assertIn( - 'San Francisco 2', - [i.strip() for i in _list_locations] - ) + """ + _list_locations = self.run_cloud("--list-locations {0}".format(self.PROVIDER)) + self.assertIn("San Francisco 2", [i.strip() for i in _list_locations]) def test_list_sizes(self): - ''' + """ Tests the return of running the --list-sizes command for digitalocean - ''' - _list_sizes = self.run_cloud('--list-sizes {0}'.format(self.PROVIDER)) - self.assertIn( - '16gb', - [i.strip() for i in _list_sizes] - ) + """ + _list_sizes = self.run_cloud("--list-sizes {0}".format(self.PROVIDER)) + self.assertIn("16gb", [i.strip() for i in _list_sizes]) def test_key_management(self): - ''' + """ Test key management - ''' - do_key_name = self.instance_name + '-key' + """ + do_key_name = self.instance_name + "-key" # generate key and fingerprint if salt.crypt.HAS_M2: rsa_key = salt.crypt.RSA.gen_key(4096, 65537, lambda: None) - pub = six.b('ssh-rsa {}'.format( - base64.b64encode( - six.b('\x00\x00\x00\x07ssh-rsa{}{}'.format(*rsa_key.pub())) + pub = six.b( + "ssh-rsa {}".format( + base64.b64encode( + six.b("\x00\x00\x00\x07ssh-rsa{}{}".format(*rsa_key.pub())) + ) ) - )) + ) else: ssh_key = salt.crypt.RSA.generate(4096) pub = ssh_key.publickey().exportKey("OpenSSH") pub = salt.utils.stringutils.to_str(pub) - key_hex = hashlib.md5(base64.b64decode(pub.strip().split()[1].encode())).hexdigest() - finger_print = ':'.join([key_hex[x:x+2] for x in range(0, len(key_hex), 2)]) + key_hex = hashlib.md5( + base64.b64decode(pub.strip().split()[1].encode()) + ).hexdigest() + finger_print = ":".join([key_hex[x : x + 2] for x in range(0, len(key_hex), 2)]) try: - _key = self.run_cloud('-f create_key {0} name="{1}" public_key="{2}"'.format(self.PROVIDER, - do_key_name, pub)) + _key = self.run_cloud( + '-f create_key {0} name="{1}" public_key="{2}"'.format( + self.PROVIDER, do_key_name, pub + ) + ) # Upload public key - self.assertIn( - finger_print, - [i.strip() for i in _key] - ) + self.assertIn(finger_print, [i.strip() for i in _key]) # List all keys - list_keypairs = self.run_cloud('-f list_keypairs {0}'.format(self.PROVIDER)) + list_keypairs = self.run_cloud("-f list_keypairs {0}".format(self.PROVIDER)) - self.assertIn( - finger_print, - [i.strip() for i in list_keypairs] - ) + self.assertIn(finger_print, [i.strip() for i in list_keypairs]) # List key - show_keypair = self.run_cloud('-f show_keypair {0} keyname={1}'.format(self.PROVIDER, do_key_name)) - self.assertIn( - finger_print, - [i.strip() for i in show_keypair] + show_keypair = self.run_cloud( + "-f show_keypair {0} keyname={1}".format(self.PROVIDER, do_key_name) ) + self.assertIn(finger_print, [i.strip() for i in show_keypair]) except AssertionError: # Delete the public key if the above assertions fail - self.run_cloud('-f remove_key {0} id={1}'.format(self.PROVIDER, finger_print)) + self.run_cloud( + "-f remove_key {0} id={1}".format(self.PROVIDER, finger_print) + ) raise finally: # Delete public key - self.assertTrue(self.run_cloud('-f remove_key {0} id={1}'.format(self.PROVIDER, finger_print))) + self.assertTrue( + self.run_cloud( + "-f remove_key {0} id={1}".format(self.PROVIDER, finger_print) + ) + ) def test_instance(self): - ''' + """ Test creating an instance on DigitalOcean - ''' + """ # check if instance with salt installed returned - ret_str = self.run_cloud('-p digitalocean-test {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_str = self.run_cloud( + "-p digitalocean-test {0}".format(self.instance_name), timeout=TIMEOUT + ) self.assertInstanceExists(ret_str) self.assertDestroyInstance() diff --git a/tests/integration/cloud/clouds/test_dimensiondata.py b/tests/integration/cloud/clouds/test_dimensiondata.py index 3b8f6f8fe5c..86e466d78a5 100644 --- a/tests/integration/cloud/clouds/test_dimensiondata.py +++ b/tests/integration/cloud/clouds/test_dimensiondata.py @@ -1,58 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the Dimension Data cloud provider -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs -from tests.integration.cloud.helpers.cloud_test_base import CloudTest, TIMEOUT +from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest class DimensionDataTest(CloudTest): - ''' + """ Integration tests for the Dimension Data cloud provider in Salt-Cloud - ''' - PROVIDER = 'dimensiondata' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('key', 'region', 'user_id') + """ + + PROVIDER = "dimensiondata" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("key", "region", "user_id") def test_list_images(self): - ''' + """ Tests the return of running the --list-images command for the dimensiondata cloud provider - ''' - image_list = self.run_cloud('--list-images {0}'.format(self.PROVIDER)) - self.assertIn( - 'Ubuntu 14.04 2 CPU', - [i.strip() for i in image_list] - ) + """ + image_list = self.run_cloud("--list-images {0}".format(self.PROVIDER)) + self.assertIn("Ubuntu 14.04 2 CPU", [i.strip() for i in image_list]) def test_list_locations(self): - ''' + """ Tests the return of running the --list-locations command for the dimensiondata cloud provider - ''' - _list_locations = self.run_cloud('--list-locations {0}'.format(self.PROVIDER)) + """ + _list_locations = self.run_cloud("--list-locations {0}".format(self.PROVIDER)) self.assertIn( - 'Australia - Melbourne MCP2', - [i.strip() for i in _list_locations] + "Australia - Melbourne MCP2", [i.strip() for i in _list_locations] ) def test_list_sizes(self): - ''' + """ Tests the return of running the --list-sizes command for the dimensiondata cloud provider - ''' - _list_sizes = self.run_cloud('--list-sizes {0}'.format(self.PROVIDER)) - self.assertIn( - 'default', - [i.strip() for i in _list_sizes] - ) + """ + _list_sizes = self.run_cloud("--list-sizes {0}".format(self.PROVIDER)) + self.assertIn("default", [i.strip() for i in _list_sizes]) def test_instance(self): - ''' + """ Test creating an instance on Dimension Data's cloud - ''' + """ # check if instance with salt installed returned - ret_val = self.run_cloud('-p dimensiondata-test {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_val = self.run_cloud( + "-p dimensiondata-test {0}".format(self.instance_name), timeout=TIMEOUT + ) self.assertInstanceExists(ret_val) self.assertDestroyInstance() diff --git a/tests/integration/cloud/clouds/test_ec2.py b/tests/integration/cloud/clouds/test_ec2.py index c7b81f31b40..b5e8e591e02 100644 --- a/tests/integration/cloud/clouds/test_ec2.py +++ b/tests/integration/cloud/clouds/test_ec2.py @@ -1,25 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import os -import yaml # Import Salt Libs import salt.utils.cloud import salt.utils.files import salt.utils.yaml +import yaml + +# Create the cloud instance name to be used throughout the tests +from tests.integration.cloud.helpers.cloud_test_base import CloudTest +from tests.support import win_installer # Import Salt Testing Libs from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf -from tests.support import win_installer - -# Create the cloud instance name to be used throughout the tests -from tests.integration.cloud.helpers.cloud_test_base import CloudTest HAS_WINRM = salt.utils.cloud.HAS_WINRM and salt.utils.cloud.HAS_SMB # THis test needs a longer timeout than other cloud tests @@ -27,11 +28,12 @@ TIMEOUT = 1200 class EC2Test(CloudTest): - ''' + """ Integration tests for the EC2 cloud provider in Salt-Cloud - ''' - PROVIDER = 'ec2' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('id', 'key', 'keyname', 'private_key', 'location') + """ + + PROVIDER = "ec2" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("id", "key", "keyname", "private_key", "location") @staticmethod def __fetch_installer(): @@ -45,64 +47,66 @@ class EC2Test(CloudTest): # If the installer wasn't found in the previous steps, download the latest Windows installer executable name = win_installer.latest_installer_name() path = os.path.join(RUNTIME_VARS.FILES, name) - with salt.utils.files.fopen(path, 'wb') as fp: + with salt.utils.files.fopen(path, "wb") as fp: win_installer.download_and_verify(fp, name) return name @property def installer(self): - ''' + """ Make sure the testing environment has a Windows installer executable. - ''' - if not hasattr(self, '_installer'): + """ + if not hasattr(self, "_installer"): self._installer = self.__fetch_installer() return self._installer def setUp(self): - ''' + """ Sets up the test requirements - ''' - group_or_subnet = self.provider_config.get('securitygroup') + """ + group_or_subnet = self.provider_config.get("securitygroup") if not group_or_subnet: - group_or_subnet = self.provider_config.get('subnetid') + group_or_subnet = self.provider_config.get("subnetid") if not group_or_subnet: - self.skipTest('securitygroup or subnetid missing for {} config'.format(self.PROVIDER)) + self.skipTest( + "securitygroup or subnetid missing for {} config".format(self.PROVIDER) + ) super(EC2Test, self).setUp() def override_profile_config(self, name, data): - conf_path = os.path.join(self.config_dir, 'cloud.profiles.d', 'ec2.conf') - with salt.utils.files.fopen(conf_path, 'r') as fp: + conf_path = os.path.join(self.config_dir, "cloud.profiles.d", "ec2.conf") + with salt.utils.files.fopen(conf_path, "r") as fp: conf = yaml.safe_load(fp) conf[name].update(data) - with salt.utils.files.fopen(conf_path, 'w') as fp: + with salt.utils.files.fopen(conf_path, "w") as fp: salt.utils.yaml.safe_dump(conf, fp) def copy_file(self, name): - ''' + """ Copy a file from tests/integration/files to a test's temporary configuration directory. The path to the file which is created will be returned. - ''' + """ src = os.path.join(RUNTIME_VARS.FILES, name) dst = os.path.join(self.config_dir, name) - with salt.utils.files.fopen(src, 'rb') as sfp: - with salt.utils.files.fopen(dst, 'wb') as dfp: + with salt.utils.files.fopen(src, "rb") as sfp: + with salt.utils.files.fopen(dst, "wb") as dfp: dfp.write(sfp.read()) return dst def _test_instance(self, profile, debug): - ''' + """ Tests creating and deleting an instance on EC2 (classic) - ''' + """ # create the instance - cmd = ['-p', profile] + cmd = ["-p", profile] if debug: - cmd.extend(['-l', 'debug']) + cmd.extend(["-l", "debug"]) cmd.append(self.instance_name) - ret_val = self.run_cloud(' '.join(cmd), timeout=TIMEOUT) + ret_val = self.run_cloud(" ".join(cmd), timeout=TIMEOUT) # check if instance returned with salt installed self.assertInstanceExists(ret_val) @@ -110,96 +114,103 @@ class EC2Test(CloudTest): self.assertDestroyInstance() def test_instance_rename(self): - ''' + """ Tests creating and renaming an instance on EC2 (classic) - ''' + """ # create the instance - ret_val = self.run_cloud('-p ec2-test {0} --no-deploy'.format(self.instance_name), timeout=TIMEOUT) + ret_val = self.run_cloud( + "-p ec2-test {0} --no-deploy".format(self.instance_name), timeout=TIMEOUT + ) # check if instance returned self.assertInstanceExists(ret_val) - changed_name = self.instance_name + '-changed' + changed_name = self.instance_name + "-changed" rename_result = self.run_cloud( - '-a rename {0} newname={1} --assume-yes'.format(self.instance_name, changed_name), timeout=TIMEOUT) - self.assertFalse(self._instance_exists(), 'Instance wasn\'t renamed: |\n{}'.format(rename_result)) + "-a rename {0} newname={1} --assume-yes".format( + self.instance_name, changed_name + ), + timeout=TIMEOUT, + ) + self.assertFalse( + self._instance_exists(), + "Instance wasn't renamed: |\n{}".format(rename_result), + ) self.assertInstanceExists(instance_name=changed_name) self.assertDestroyInstance(changed_name) def test_instance(self): - ''' + """ Tests creating and deleting an instance on EC2 (classic) - ''' - self._test_instance('ec2-test', debug=False) + """ + self._test_instance("ec2-test", debug=False) def test_win2012r2_psexec(self): - ''' + """ Tests creating and deleting a Windows 2012r2instance on EC2 using psexec (classic) - ''' + """ # TODO: psexec calls hang and the test fails by timing out. The same # same calls succeed when run outside of the test environment. # FIXME? Does this override need to be undone at the end of the test? self.override_profile_config( - 'ec2-win2012r2-test', + "ec2-win2012r2-test", { - 'use_winrm': False, - 'userdata_file': self.copy_file('windows-firewall-winexe.ps1'), - 'win_installer': self.copy_file(self.installer), + "use_winrm": False, + "userdata_file": self.copy_file("windows-firewall-winexe.ps1"), + "win_installer": self.copy_file(self.installer), }, ) - self._test_instance('ec2-win2012r2-test', debug=True) + self._test_instance("ec2-win2012r2-test", debug=True) - @skipIf(not HAS_WINRM, 'Skip when winrm dependencies are missing') + @skipIf(not HAS_WINRM, "Skip when winrm dependencies are missing") def test_win2012r2_winrm(self): - ''' + """ Tests creating and deleting a Windows 2012r2 instance on EC2 using winrm (classic) - ''' + """ self.override_profile_config( - 'ec2-win2012r2-test', + "ec2-win2012r2-test", { - 'userdata_file': self.copy_file('windows-firewall.ps1'), - 'win_installer': self.copy_file(self.installer), - 'winrm_ssl_verify': False, - 'use_winrm': True, - } - + "userdata_file": self.copy_file("windows-firewall.ps1"), + "win_installer": self.copy_file(self.installer), + "winrm_ssl_verify": False, + "use_winrm": True, + }, ) - self._test_instance('ec2-win2012r2-test', debug=True) + self._test_instance("ec2-win2012r2-test", debug=True) def test_win2016_psexec(self): - ''' + """ Tests creating and deleting a Windows 2016 instance on EC2 using winrm (classic) - ''' + """ # TODO: winexe calls hang and the test fails by timing out. The # same calls succeed when run outside of the test environment. self.override_profile_config( - 'ec2-win2016-test', + "ec2-win2016-test", { - 'use_winrm': False, - 'userdata_file': self.copy_file('windows-firewall-winexe.ps1'), - 'win_installer': self.copy_file(self.installer), + "use_winrm": False, + "userdata_file": self.copy_file("windows-firewall-winexe.ps1"), + "win_installer": self.copy_file(self.installer), }, ) - self._test_instance('ec2-win2016-test', debug=True) + self._test_instance("ec2-win2016-test", debug=True) - @skipIf(not HAS_WINRM, 'Skip when winrm dependencies are missing') + @skipIf(not HAS_WINRM, "Skip when winrm dependencies are missing") def test_win2016_winrm(self): - ''' + """ Tests creating and deleting a Windows 2016 instance on EC2 using winrm (classic) - ''' + """ self.override_profile_config( - 'ec2-win2016-test', + "ec2-win2016-test", { - 'userdata_file': self.copy_file('windows-firewall.ps1'), - 'win_installer': self.copy_file(self.installer), - 'winrm_ssl_verify': False, - 'use_winrm': True, - } - + "userdata_file": self.copy_file("windows-firewall.ps1"), + "win_installer": self.copy_file(self.installer), + "winrm_ssl_verify": False, + "use_winrm": True, + }, ) - self._test_instance('ec2-win2016-test', debug=True) + self._test_instance("ec2-win2016-test", debug=True) diff --git a/tests/integration/cloud/clouds/test_gce.py b/tests/integration/cloud/clouds/test_gce.py index d447f873330..f28506a010e 100644 --- a/tests/integration/cloud/clouds/test_gce.py +++ b/tests/integration/cloud/clouds/test_gce.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> :codeauthor: Tomas Sirny <tsirny@gmail.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -12,31 +12,40 @@ from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest class GCETest(CloudTest): - ''' + """ Integration tests for the GCE cloud provider in Salt-Cloud - ''' - PROVIDER = 'gce' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('project', 'service_account_email_address', 'service_account_private_key') + """ + + PROVIDER = "gce" + REQUIRED_PROVIDER_CONFIG_ITEMS = ( + "project", + "service_account_email_address", + "service_account_private_key", + ) def test_instance(self): - ''' + """ Tests creating and deleting an instance on GCE - ''' + """ # create the instance - ret_str = self.run_cloud('-p gce-test {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_str = self.run_cloud( + "-p gce-test {0}".format(self.instance_name), timeout=TIMEOUT + ) # check if instance returned with salt installed self.assertInstanceExists(ret_str) self.assertDestroyInstance() def test_instance_extra(self): - ''' + """ Tests creating and deleting an instance on GCE - ''' + """ # create the instance - ret_str = self.run_cloud('-p gce-test-extra {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_str = self.run_cloud( + "-p gce-test-extra {0}".format(self.instance_name), timeout=TIMEOUT + ) # check if instance returned with salt installed self.assertInstanceExists(ret_str) diff --git a/tests/integration/cloud/clouds/test_gogrid.py b/tests/integration/cloud/clouds/test_gogrid.py index f767d596705..cebe3743f25 100644 --- a/tests/integration/cloud/clouds/test_gogrid.py +++ b/tests/integration/cloud/clouds/test_gogrid.py @@ -1,32 +1,35 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +# Create the cloud instance name to be used throughout the tests +from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest + # Import Salt Testing Libs from tests.support.unit import skipIf -# Create the cloud instance name to be used throughout the tests -from tests.integration.cloud.helpers.cloud_test_base import CloudTest, TIMEOUT - -@skipIf(True, 'waiting on bug report fixes from #13365') +@skipIf(True, "waiting on bug report fixes from #13365") class GoGridTest(CloudTest): - ''' + """ Integration tests for the GoGrid cloud provider in Salt-Cloud - ''' - PROVIDER = 'gogrid' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('apikey', 'sharedsecret') + """ + + PROVIDER = "gogrid" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("apikey", "sharedsecret") def test_instance(self): - ''' + """ Test creating an instance on GoGrid - ''' + """ # check if instance with salt installed returned - ret_str = self.run_cloud('-p gogrid-test {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_str = self.run_cloud( + "-p gogrid-test {0}".format(self.instance_name), timeout=TIMEOUT + ) self.assertInstanceExists(ret_str) self.assertDestroyInstance() diff --git a/tests/integration/cloud/clouds/test_linode.py b/tests/integration/cloud/clouds/test_linode.py index f1483aa43fd..a33393a0db0 100644 --- a/tests/integration/cloud/clouds/test_linode.py +++ b/tests/integration/cloud/clouds/test_linode.py @@ -1,30 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals - # Create the cloud instance name to be used throughout the tests -from tests.integration.cloud.helpers.cloud_test_base import CloudTest, TIMEOUT +from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest class LinodeTest(CloudTest): - ''' + """ Integration tests for the Linode cloud provider in Salt-Cloud - ''' + """ - PROVIDER = 'linode' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('apikey', 'password') + PROVIDER = "linode" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("apikey", "password") def test_instance(self): - ''' + """ Test creating an instance on Linode - ''' + """ # check if instance with salt installed returned - ret_str = self.run_cloud('-p linode-test {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_str = self.run_cloud( + "-p linode-test {0}".format(self.instance_name), timeout=TIMEOUT + ) self.assertInstanceExists(ret_str) self.assertDestroyInstance() diff --git a/tests/integration/cloud/clouds/test_msazure.py b/tests/integration/cloud/clouds/test_msazure.py index 0b8f21da352..f7e5b43193e 100644 --- a/tests/integration/cloud/clouds/test_msazure.py +++ b/tests/integration/cloud/clouds/test_msazure.py @@ -1,40 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging +# Import Salt Libs +from salt.utils.versions import LooseVersion + # Import Salt Testing Libs from tests.integration.cloud.helpers.cloud_test_base import CloudTest from tests.support.unit import skipIf -# Import Salt Libs -from salt.utils.versions import LooseVersion - try: import azure # pylint: disable=unused-import + HAS_AZURE = True except ImportError: HAS_AZURE = False -if HAS_AZURE and not hasattr(azure, '__version__'): +if HAS_AZURE and not hasattr(azure, "__version__"): import azure.common log = logging.getLogger(__name__) TIMEOUT = 1000 -REQUIRED_AZURE = '1.1.0' +REQUIRED_AZURE = "1.1.0" def __has_required_azure(): - ''' + """ Returns True/False if the required version of the Azure SDK is installed. - ''' + """ if HAS_AZURE: - if hasattr(azure, '__version__'): + if hasattr(azure, "__version__"): version = LooseVersion(azure.__version__) else: version = LooseVersion(azure.common.__version__) @@ -43,20 +45,26 @@ def __has_required_azure(): return False -@skipIf(not HAS_AZURE, 'These tests require the Azure Python SDK to be installed.') -@skipIf(not __has_required_azure(), 'The Azure Python SDK must be >= {}.'.format(REQUIRED_AZURE)) +@skipIf(not HAS_AZURE, "These tests require the Azure Python SDK to be installed.") +@skipIf( + not __has_required_azure(), + "The Azure Python SDK must be >= {}.".format(REQUIRED_AZURE), +) class AzureTest(CloudTest): - ''' + """ Integration tests for the Azure cloud provider in Salt-Cloud - ''' - PROVIDER = 'azurearm' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('subscription_id',) + """ + + PROVIDER = "azurearm" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("subscription_id",) def test_instance(self): - ''' + """ Test creating an instance on Azure - ''' + """ # check if instance with salt installed returned - ret_val = self.run_cloud('-p azure-test {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_val = self.run_cloud( + "-p azure-test {0}".format(self.instance_name), timeout=TIMEOUT + ) self.assertInstanceExists(ret_val) self.assertDestroyInstance(timeout=TIMEOUT) diff --git a/tests/integration/cloud/clouds/test_oneandone.py b/tests/integration/cloud/clouds/test_oneandone.py index bd7779b5d36..36d1e93029d 100644 --- a/tests/integration/cloud/clouds/test_oneandone.py +++ b/tests/integration/cloud/clouds/test_oneandone.py @@ -1,47 +1,48 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Amel Ajdinovic <amel@stackpointcloud.com>` -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs -from tests.integration.cloud.helpers.cloud_test_base import CloudTest, TIMEOUT +from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest from tests.support.unit import skipIf # Import Third-Party Libs try: from oneandone.client import OneAndOneService # pylint: disable=unused-import + HAS_ONEANDONE = True except ImportError: HAS_ONEANDONE = False -@skipIf(HAS_ONEANDONE is False, 'salt-cloud requires >= 1and1 1.2.0') +@skipIf(HAS_ONEANDONE is False, "salt-cloud requires >= 1and1 1.2.0") class OneAndOneTest(CloudTest): - ''' + """ Integration tests for the 1and1 cloud provider - ''' - PROVIDER = 'oneandone' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('api_token',) + """ + + PROVIDER = "oneandone" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("api_token",) def test_list_images(self): - ''' + """ Tests the return of running the --list-images command for 1and1 - ''' - image_list = self.run_cloud('--list-images {0}'.format(self.PROVIDER_NAME)) - self.assertIn( - 'coreOSimage', - [i.strip() for i in image_list] - ) + """ + image_list = self.run_cloud("--list-images {0}".format(self.PROVIDER_NAME)) + self.assertIn("coreOSimage", [i.strip() for i in image_list]) def test_instance(self): - ''' + """ Test creating an instance on 1and1 - ''' + """ # check if instance with salt installed returned - ret_str = self.run_cloud('-p oneandone-test {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_str = self.run_cloud( + "-p oneandone-test {0}".format(self.instance_name), timeout=TIMEOUT + ) self.assertInstanceExists(ret_str) self.assertDestroyInstance() diff --git a/tests/integration/cloud/clouds/test_openstack.py b/tests/integration/cloud/clouds/test_openstack.py index b2b4e9ff163..cb6c018fbcc 100644 --- a/tests/integration/cloud/clouds/test_openstack.py +++ b/tests/integration/cloud/clouds/test_openstack.py @@ -1,27 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the Openstack Cloud Provider -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest -from tests.support.mixins import SaltReturnAssertsMixin +import logging # Import Salt Libs from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf + log = logging.getLogger(__name__) try: import keystoneclient # pylint: disable=import-error,unused-import from libcloud.common.openstack_identity import OpenStackIdentity_3_0_Connection from libcloud.common.openstack_identity import OpenStackIdentityTokenScope + HAS_KEYSTONE = True except ImportError: HAS_KEYSTONE = False @@ -37,152 +39,194 @@ except ImportError: @skipIf( not HAS_KEYSTONE, - 'Please install keystoneclient and a keystone server before running' - 'openstack integration tests.' + "Please install keystoneclient and a keystone server before running" + "openstack integration tests.", ) class OpenstackTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the keystone state - ''' - endpoint = 'http://localhost:35357/v2.0' - token = 'administrator' + """ + + endpoint = "http://localhost:35357/v2.0" + token = "administrator" @destructiveTest def test_aaa_setup_keystone_endpoint(self): - ret = self.run_state('keystone.service_present', - name='keystone', - description='OpenStack Identity', - service_type='identity', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-keystone_|-keystone_|-service_present']['result']) + ret = self.run_state( + "keystone.service_present", + name="keystone", + description="OpenStack Identity", + service_type="identity", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue( + ret["keystone_|-keystone_|-keystone_|-service_present"]["result"] + ) - ret = self.run_state('keystone.endpoint_present', - name='keystone', - region='RegionOne', - publicurl='http://localhost:5000/v2.0', - internalurl='http://localhost:5000/v2.0', - adminurl='http://localhost:35357/v2.0', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-keystone_|-keystone_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="keystone", + region="RegionOne", + publicurl="http://localhost:5000/v2.0", + internalurl="http://localhost:5000/v2.0", + adminurl="http://localhost:35357/v2.0", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue( + ret["keystone_|-keystone_|-keystone_|-endpoint_present"]["result"] + ) - ret = self.run_state('keystone.tenant_present', - name='admin', - description='Admin Project', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-admin_|-admin_|-tenant_present']['result']) + ret = self.run_state( + "keystone.tenant_present", + name="admin", + description="Admin Project", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-admin_|-admin_|-tenant_present"]["result"]) - ret = self.run_state('keystone.tenant_present', - name='demo', - description='Demo Project', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-demo_|-demo_|-tenant_present']['result']) + ret = self.run_state( + "keystone.tenant_present", + name="demo", + description="Demo Project", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-demo_|-demo_|-tenant_present"]["result"]) - ret = self.run_state('keystone.role_present', - name='admin', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-admin_|-admin_|-role_present']['result']) + ret = self.run_state( + "keystone.role_present", + name="admin", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-admin_|-admin_|-role_present"]["result"]) - ret = self.run_state('keystone.role_present', - name='user', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-user_|-user_|-role_present']['result']) + ret = self.run_state( + "keystone.role_present", + name="user", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-user_|-user_|-role_present"]["result"]) - ret = self.run_state('keystone.user_present', - name='admin', - email='admin@example.com', - password='adminpass', - tenant='admin', - roles={'admin': ['admin']}, - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-admin_|-admin_|-user_present']['result']) + ret = self.run_state( + "keystone.user_present", + name="admin", + email="admin@example.com", + password="adminpass", + tenant="admin", + roles={"admin": ["admin"]}, + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-admin_|-admin_|-user_present"]["result"]) - ret = self.run_state('keystone.user_present', - name='demo', - email='demo@example.com', - password='demopass', - tenant='demo', - roles={'demo': ['user']}, - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-demo_|-demo_|-user_present']['result']) + ret = self.run_state( + "keystone.user_present", + name="demo", + email="demo@example.com", + password="demopass", + tenant="demo", + roles={"demo": ["user"]}, + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-demo_|-demo_|-user_present"]["result"]) @destructiveTest def test_zzz_teardown_keystone_endpoint(self): - ret = self.run_state('keystone.user_absent', - name='admin', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-admin_|-admin_|-user_absent']['result']) + ret = self.run_state( + "keystone.user_absent", + name="admin", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-admin_|-admin_|-user_absent"]["result"]) - ret = self.run_state('keystone.user_absent', - name='demo', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-demo_|-demo_|-user_absent']['result']) + ret = self.run_state( + "keystone.user_absent", + name="demo", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-demo_|-demo_|-user_absent"]["result"]) - ret = self.run_state('keystone.role_absent', - name='admin', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-admin_|-admin_|-role_absent']['result']) + ret = self.run_state( + "keystone.role_absent", + name="admin", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-admin_|-admin_|-role_absent"]["result"]) - ret = self.run_state('keystone.role_absent', - name='user', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-user_|-user_|-role_absent']['result']) + ret = self.run_state( + "keystone.role_absent", + name="user", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-user_|-user_|-role_absent"]["result"]) - ret = self.run_state('keystone.tenant_absent', - name='admin', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-admin_|-admin_|-tenant_absent']['result']) + ret = self.run_state( + "keystone.tenant_absent", + name="admin", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-admin_|-admin_|-tenant_absent"]["result"]) - ret = self.run_state('keystone.tenant_absent', - name='demo', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-demo_|-demo_|-tenant_absent']['result']) + ret = self.run_state( + "keystone.tenant_absent", + name="demo", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-demo_|-demo_|-tenant_absent"]["result"]) - ret = self.run_state('keystone.service_absent', - name='keystone', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-keystone_|-keystone_|-service_absent']['result']) + ret = self.run_state( + "keystone.service_absent", + name="keystone", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue( + ret["keystone_|-keystone_|-keystone_|-service_absent"]["result"] + ) @destructiveTest def test_libcloud_auth_v3(self): - driver = OpenStackIdentity_3_0_Connection(auth_url='http://localhost:5000', - user_id='admin', - key='adminpass', - token_scope=OpenStackIdentityTokenScope.PROJECT, - domain_name='Default', - tenant_name='admin') + driver = OpenStackIdentity_3_0_Connection( + auth_url="http://localhost:5000", + user_id="admin", + key="adminpass", + token_scope=OpenStackIdentityTokenScope.PROJECT, + domain_name="Default", + tenant_name="admin", + ) driver.authenticate() self.assertTrue(driver.auth_token) -@skipIf(not HAS_SHADE, 'openstack driver requires `shade`') +@skipIf(not HAS_SHADE, "openstack driver requires `shade`") class RackspaceTest(CloudTest): - ''' + """ Integration tests for the Rackspace cloud provider using the Openstack driver - ''' - PROVIDER = 'openstack' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('auth', 'cloud', 'region_name') + """ + + PROVIDER = "openstack" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("auth", "cloud", "region_name") def test_instance(self): - ''' + """ Test creating an instance on rackspace with the openstack driver - ''' + """ # check if instance with salt installed returned - ret_val = self.run_cloud('-p rackspace-test {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_val = self.run_cloud( + "-p rackspace-test {0}".format(self.instance_name), timeout=TIMEOUT + ) self.assertInstanceExists(ret_val) self.assertDestroyInstance() diff --git a/tests/integration/cloud/clouds/test_profitbricks.py b/tests/integration/cloud/clouds/test_profitbricks.py index 9bd635164cd..8be5d9a4562 100644 --- a/tests/integration/cloud/clouds/test_profitbricks.py +++ b/tests/integration/cloud/clouds/test_profitbricks.py @@ -1,154 +1,130 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Ethan Devenport <ethand@stackpointcloud.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import skipIf - # Import Third-Party Libs from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest +# Import Salt Testing Libs +from tests.support.unit import skipIf + try: # pylint: disable=unused-import from profitbricks.client import ProfitBricksService + HAS_PROFITBRICKS = True except ImportError: HAS_PROFITBRICKS = False -@skipIf(HAS_PROFITBRICKS is False, 'salt-cloud requires >= profitbricks 4.1.0') +@skipIf(HAS_PROFITBRICKS is False, "salt-cloud requires >= profitbricks 4.1.0") class ProfitBricksTest(CloudTest): - ''' + """ Integration tests for the ProfitBricks cloud provider - ''' - PROVIDER = 'profitbricks' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('username', 'password', 'datacenter_id') + """ + + PROVIDER = "profitbricks" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("username", "password", "datacenter_id") def setUp(self): super(ProfitBricksTest, self).setUp() - username = self.provider_config.get('username') - password = self.provider_config.get('password') + username = self.provider_config.get("username") + password = self.provider_config.get("password") # A default username and password must be hard-coded as defaults as per issue #46265 # If they are 'foo' and 'bar' it is the same as not being set - self.skipTest('Conf items are missing that must be provided to run these tests: username, password' - '\nCheck tests/integration/files/conf/cloud.providers.d/{0}.conf'.format(self.PROVIDER)) + self.skipTest( + "Conf items are missing that must be provided to run these tests: username, password" + "\nCheck tests/integration/files/conf/cloud.providers.d/{0}.conf".format( + self.PROVIDER + ) + ) def test_list_images(self): - ''' + """ Tests the return of running the --list-images command for ProfitBricks - ''' - list_images = self.run_cloud('--list-images {0}'.format(self.PROVIDER)) + """ + list_images = self.run_cloud("--list-images {0}".format(self.PROVIDER)) self.assertIn( - 'Ubuntu-16.04-LTS-server-2017-10-01', - [i.strip() for i in list_images] + "Ubuntu-16.04-LTS-server-2017-10-01", [i.strip() for i in list_images] ) def test_list_image_alias(self): - ''' + """ Tests the return of running the -f list_images command for ProfitBricks - ''' - cmd = '-f list_images {0}'.format(self.PROVIDER) + """ + cmd = "-f list_images {0}".format(self.PROVIDER) list_images = self.run_cloud(cmd) - self.assertIn( - '- ubuntu:latest', - [i.strip() for i in list_images] - ) + self.assertIn("- ubuntu:latest", [i.strip() for i in list_images]) def test_list_sizes(self): - ''' + """ Tests the return of running the --list_sizes command for ProfitBricks - ''' - list_sizes = self.run_cloud('--list-sizes {0}'.format(self.PROVIDER)) - self.assertIn( - 'Micro Instance:', - [i.strip() for i in list_sizes] - ) + """ + list_sizes = self.run_cloud("--list-sizes {0}".format(self.PROVIDER)) + self.assertIn("Micro Instance:", [i.strip() for i in list_sizes]) def test_list_datacenters(self): - ''' + """ Tests the return of running the -f list_datacenters command for ProfitBricks - ''' - cmd = '-f list_datacenters {0}'.format(self.PROVIDER) + """ + cmd = "-f list_datacenters {0}".format(self.PROVIDER) list_datacenters = self.run_cloud(cmd) self.assertIn( - self.provider_config['datacenter_id'], - [i.strip() for i in list_datacenters] + self.provider_config["datacenter_id"], [i.strip() for i in list_datacenters] ) def test_list_nodes(self): - ''' + """ Tests the return of running the -f list_nodes command for ProfitBricks - ''' - list_nodes = self.run_cloud('-f list_nodes {0}'.format(self.PROVIDER)) - self.assertIn( - 'state:', - [i.strip() for i in list_nodes] - ) + """ + list_nodes = self.run_cloud("-f list_nodes {0}".format(self.PROVIDER)) + self.assertIn("state:", [i.strip() for i in list_nodes]) - self.assertIn( - 'name:', - [i.strip() for i in list_nodes] - ) + self.assertIn("name:", [i.strip() for i in list_nodes]) def test_list_nodes_full(self): - ''' + """ Tests the return of running the -f list_nodes_full command for ProfitBricks - ''' - cmd = '-f list_nodes_full {0}'.format(self.PROVIDER) + """ + cmd = "-f list_nodes_full {0}".format(self.PROVIDER) list_nodes = self.run_cloud(cmd) - self.assertIn( - 'state:', - [i.strip() for i in list_nodes] - ) + self.assertIn("state:", [i.strip() for i in list_nodes]) - self.assertIn( - 'name:', - [i.strip() for i in list_nodes] - ) + self.assertIn("name:", [i.strip() for i in list_nodes]) def test_list_location(self): - ''' + """ Tests the return of running the --list-locations command for ProfitBricks - ''' - cmd = '--list-locations {0}'.format(self.PROVIDER) + """ + cmd = "--list-locations {0}".format(self.PROVIDER) list_locations = self.run_cloud(cmd) - self.assertIn( - 'de/fkb', - [i.strip() for i in list_locations] - ) + self.assertIn("de/fkb", [i.strip() for i in list_locations]) - self.assertIn( - 'de/fra', - [i.strip() for i in list_locations] - ) + self.assertIn("de/fra", [i.strip() for i in list_locations]) - self.assertIn( - 'us/las', - [i.strip() for i in list_locations] - ) + self.assertIn("us/las", [i.strip() for i in list_locations]) - self.assertIn( - 'us/ewr', - [i.strip() for i in list_locations] - ) + self.assertIn("us/ewr", [i.strip() for i in list_locations]) def test_instance(self): - ''' + """ Test creating an instance on ProfitBricks - ''' + """ # check if instance with salt installed returned - ret_str = self.run_cloud('-p profitbricks-test {0}'.format(self.instance_name), timeout=TIMEOUT) + ret_str = self.run_cloud( + "-p profitbricks-test {0}".format(self.instance_name), timeout=TIMEOUT + ) self.assertInstanceExists(ret_str) self.assertDestroyInstance() diff --git a/tests/integration/cloud/clouds/test_tencentcloud.py b/tests/integration/cloud/clouds/test_tencentcloud.py index 103be9ae883..ea1bbf4b884 100644 --- a/tests/integration/cloud/clouds/test_tencentcloud.py +++ b/tests/integration/cloud/clouds/test_tencentcloud.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Li Kexian <doyenli@tencent.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt Libs @@ -12,89 +13,92 @@ from salt.config import cloud_providers_config # Import Salt Testing Libs from tests.support.case import ShellCase -from tests.support.runtests import RUNTIME_VARS from tests.support.helpers import expensiveTest, generate_random_name - +from tests.support.runtests import RUNTIME_VARS # Create the cloud instance name to be used throughout the tests -INSTANCE_NAME = generate_random_name('CLOUD-TEST-') -PROVIDER_NAME = 'tencentcloud' +INSTANCE_NAME = generate_random_name("CLOUD-TEST-") +PROVIDER_NAME = "tencentcloud" @expensiveTest class TencentCloudTest(ShellCase): - ''' + """ Integration tests for the Tencent Cloud cloud provider in Salt-Cloud - ''' + """ def setUp(self): - ''' + """ Sets up the test requirements - ''' + """ super(TencentCloudTest, self).setUp() # check if appropriate cloud provider and profile files are present - profile_str = 'tencentcloud-config' - providers = self.run_cloud('--list-providers') + profile_str = "tencentcloud-config" + providers = self.run_cloud("--list-providers") - if profile_str + ':' not in providers: + if profile_str + ":" not in providers: self.skipTest( - 'Configuration file for {0} was not found. Check {0}.conf files ' - 'in tests/integration/files/conf/cloud.*.d/ to run these tests.' - .format(PROVIDER_NAME) + "Configuration file for {0} was not found. Check {0}.conf files " + "in tests/integration/files/conf/cloud.*.d/ to run these tests.".format( + PROVIDER_NAME + ) ) # check if personal access token, ssh_key_file, and ssh_key_names are present config = cloud_providers_config( os.path.join( - RUNTIME_VARS.FILES, - 'conf', - 'cloud.providers.d', - PROVIDER_NAME + '.conf' + RUNTIME_VARS.FILES, "conf", "cloud.providers.d", PROVIDER_NAME + ".conf" ) ) - tid = config[profile_str][PROVIDER_NAME]['id'] - key = config[profile_str][PROVIDER_NAME]['key'] - if tid == '' or key == '': + tid = config[profile_str][PROVIDER_NAME]["id"] + key = config[profile_str][PROVIDER_NAME]["key"] + if tid == "" or key == "": self.skipTest( - 'An api id and key must be provided to run these tests. Check ' - 'tests/integration/files/conf/cloud.providers.d/{0}.conf'.format( + "An api id and key must be provided to run these tests. Check " + "tests/integration/files/conf/cloud.providers.d/{0}.conf".format( PROVIDER_NAME ) ) def test_instance(self): - ''' + """ Test creating an instance on Tencent Cloud - ''' + """ # check if instance with salt installed returned try: self.assertIn( INSTANCE_NAME, - [i.strip() for i in self.run_cloud( - '-p tencentcloud-test {0}'.format(INSTANCE_NAME), timeout=500)] + [ + i.strip() + for i in self.run_cloud( + "-p tencentcloud-test {0}".format(INSTANCE_NAME), timeout=500 + ) + ], ) except AssertionError: - self.run_cloud( - '-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500) + self.run_cloud("-d {0} --assume-yes".format(INSTANCE_NAME), timeout=500) raise # delete the instance self.assertIn( - INSTANCE_NAME + ':', - [i.strip() for i in self.run_cloud( - '-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)] + INSTANCE_NAME + ":", + [ + i.strip() + for i in self.run_cloud( + "-d {0} --assume-yes".format(INSTANCE_NAME), timeout=500 + ) + ], ) def tearDown(self): - ''' + """ Clean up after tests - ''' - query = self.run_cloud('--query') - ret_str = ' {0}:'.format(INSTANCE_NAME) + """ + query = self.run_cloud("--query") + ret_str = " {0}:".format(INSTANCE_NAME) # if test instance is still present, delete it if ret_str in query: - self.run_cloud( - '-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500) + self.run_cloud("-d {0} --assume-yes".format(INSTANCE_NAME), timeout=500) diff --git a/tests/integration/cloud/clouds/test_virtualbox.py b/tests/integration/cloud/clouds/test_virtualbox.py index 462225a9236..87fcd8f4d7b 100644 --- a/tests/integration/cloud/clouds/test_virtualbox.py +++ b/tests/integration/cloud/clouds/test_virtualbox.py @@ -4,40 +4,46 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os + import logging +import os import socket -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.runtests import RUNTIME_VARS -from tests.integration.cloud.helpers.virtualbox import (VirtualboxTestCase, - VirtualboxCloudTestCase, - CONFIG_NAME, - PROVIDER_NAME, - PROFILE_NAME, - BASE_BOX_NAME, - INSTANCE_NAME, - BOOTABLE_BASE_BOX_NAME, - DEPLOY_PROFILE_NAME) +from salt.config import cloud_providers_config, vm_profiles_config # Import Salt Libs from salt.ext import six from salt.ext.six.moves import range -from salt.config import cloud_providers_config, vm_profiles_config -from salt.utils.virtualbox import (vb_xpcom_to_attribute_dict, - vb_clone_vm, - vb_destroy_machine, - vb_create_machine, - vb_get_box, - vb_machine_exists, - XPCOM_ATTRIBUTES, - vb_start_vm, - vb_stop_vm, - vb_get_network_addresses, - vb_wait_for_network_address, - machine_get_machinestate_str, - HAS_LIBS) +from salt.utils.virtualbox import ( + HAS_LIBS, + XPCOM_ATTRIBUTES, + machine_get_machinestate_str, + vb_clone_vm, + vb_create_machine, + vb_destroy_machine, + vb_get_box, + vb_get_network_addresses, + vb_machine_exists, + vb_start_vm, + vb_stop_vm, + vb_wait_for_network_address, + vb_xpcom_to_attribute_dict, +) +from tests.integration.cloud.helpers.virtualbox import ( + BASE_BOX_NAME, + BOOTABLE_BASE_BOX_NAME, + CONFIG_NAME, + DEPLOY_PROFILE_NAME, + INSTANCE_NAME, + PROFILE_NAME, + PROVIDER_NAME, + VirtualboxCloudTestCase, + VirtualboxTestCase, +) +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf log = logging.getLogger(__name__) @@ -65,7 +71,9 @@ class VirtualboxProviderTest(VirtualboxCloudTestCase): @return: @rtype: dict """ - output = self.run_cloud('-d {0} --assume-yes --log-level=debug'.format(machine_name)) + output = self.run_cloud( + "-d {0} --assume-yes --log-level=debug".format(machine_name) + ) return output.get(CONFIG_NAME, {}).get(PROVIDER_NAME, {}) def setUp(self): @@ -75,47 +83,46 @@ class VirtualboxProviderTest(VirtualboxCloudTestCase): super(VirtualboxProviderTest, self).setUp() # check if appropriate cloud provider and profile files are present - profile_str = 'virtualbox-config' - providers = self.run_cloud('--list-providers') + profile_str = "virtualbox-config" + providers = self.run_cloud("--list-providers") log.debug("providers: %s", providers) if profile_str not in providers: self.skipTest( - 'Configuration file for {0} was not found. Check {0}.conf files ' - 'in tests/integration/files/conf/cloud.*.d/ to run these tests.'.format(PROVIDER_NAME) + "Configuration file for {0} was not found. Check {0}.conf files " + "in tests/integration/files/conf/cloud.*.d/ to run these tests.".format( + PROVIDER_NAME + ) ) # check if personal access token, ssh_key_file, and ssh_key_names are present config_path = os.path.join( - RUNTIME_VARS.FILES, - 'conf', - 'cloud.providers.d', - PROVIDER_NAME + '.conf' + RUNTIME_VARS.FILES, "conf", "cloud.providers.d", PROVIDER_NAME + ".conf" ) log.debug("config_path: %s", config_path) providers = cloud_providers_config(config_path) log.debug("config: %s", providers) config_path = os.path.join( - RUNTIME_VARS.FILES, - 'conf', - 'cloud.profiles.d', - PROVIDER_NAME + '.conf' + RUNTIME_VARS.FILES, "conf", "cloud.profiles.d", PROVIDER_NAME + ".conf" ) profiles = vm_profiles_config(config_path, providers) profile = profiles.get(PROFILE_NAME) if not profile: self.skipTest( - 'Profile {0} was not found. Check {1}.conf files ' - 'in tests/integration/files/conf/cloud.profiles.d/ to run these tests.'.format(PROFILE_NAME, - PROVIDER_NAME) + "Profile {0} was not found. Check {1}.conf files " + "in tests/integration/files/conf/cloud.profiles.d/ to run these tests.".format( + PROFILE_NAME, PROVIDER_NAME + ) ) base_box_name = profile.get("clonefrom") if base_box_name != BASE_BOX_NAME: self.skipTest( - 'Profile {0} does not have a base box to clone from. Check {1}.conf files ' - 'in tests/integration/files/conf/cloud.profiles.d/ to run these tests.' - 'And add a "clone_from: {2}" to the profile'.format(PROFILE_NAME, PROVIDER_NAME, BASE_BOX_NAME) + "Profile {0} does not have a base box to clone from. Check {1}.conf files " + "in tests/integration/files/conf/cloud.profiles.d/ to run these tests." + 'And add a "clone_from: {2}" to the profile'.format( + PROFILE_NAME, PROVIDER_NAME, BASE_BOX_NAME + ) ) @classmethod @@ -130,14 +137,16 @@ class VirtualboxProviderTest(VirtualboxCloudTestCase): """ Simply create a machine and make sure it was created """ - machines = self.run_cloud('-p {0} {1} --log-level=debug'.format(PROFILE_NAME, INSTANCE_NAME)) + machines = self.run_cloud( + "-p {0} {1} --log-level=debug".format(PROFILE_NAME, INSTANCE_NAME) + ) self.assertIn(INSTANCE_NAME, machines.keys()) def test_cloud_list(self): """ List all machines in virtualbox and make sure the requested attributes are included """ - machines = self.run_cloud_function('list_nodes') + machines = self.run_cloud_function("list_nodes") expected_attributes = MINIMAL_MACHINE_ATTRIBUTES names = machines.keys() @@ -154,13 +163,15 @@ class VirtualboxProviderTest(VirtualboxCloudTestCase): """ List all machines and make sure full information in included """ - machines = self.run_cloud_function('list_nodes_full') + machines = self.run_cloud_function("list_nodes_full") expected_minimal_attribute_count = len(MINIMAL_MACHINE_ATTRIBUTES) names = machines.keys() self.assertGreaterEqual(len(names), 1, "No machines found") for name, machine in six.iteritems(machines): - self.assertGreaterEqual(len(machine.keys()), expected_minimal_attribute_count) + self.assertGreaterEqual( + len(machine.keys()), expected_minimal_attribute_count + ) self.assertIn(BASE_BOX_NAME, names) @@ -168,7 +179,7 @@ class VirtualboxProviderTest(VirtualboxCloudTestCase): """ List selected attributes of all machines """ - machines = self.run_cloud_function('list_nodes_select') + machines = self.run_cloud_function("list_nodes_select") # TODO find out how to get query.selection from the "cloud" config expected_attributes = ["id"] @@ -194,10 +205,8 @@ class VirtualboxProviderTest(VirtualboxCloudTestCase): self.assertIn(INSTANCE_NAME, ret.keys()) def test_function_show_instance(self): - kw_function_args = { - "image": BASE_BOX_NAME - } - machines = self.run_cloud_function('show_image', kw_function_args, timeout=30) + kw_function_args = {"image": BASE_BOX_NAME} + machines = self.run_cloud_function("show_image", kw_function_args, timeout=30) expected_minimal_attribute_count = len(MINIMAL_MACHINE_ATTRIBUTES) self.assertIn(BASE_BOX_NAME, machines) machine = machines[BASE_BOX_NAME] @@ -211,13 +220,15 @@ class VirtualboxProviderTest(VirtualboxCloudTestCase): vb_destroy_machine(INSTANCE_NAME) -@skipIf(HAS_LIBS and vb_machine_exists(BOOTABLE_BASE_BOX_NAME) is False, - "Bootable VM '{0}' not found. Cannot run tests.".format(BOOTABLE_BASE_BOX_NAME) - ) +@skipIf( + HAS_LIBS and vb_machine_exists(BOOTABLE_BASE_BOX_NAME) is False, + "Bootable VM '{0}' not found. Cannot run tests.".format(BOOTABLE_BASE_BOX_NAME), +) class VirtualboxProviderHeavyTests(VirtualboxCloudTestCase): """ Tests that include actually booting a machine and doing operations on it that might be lengthy. """ + def assertIsIpAddress(self, ip_str): """ Is it either a IPv4 or IPv6 address @@ -240,46 +251,45 @@ class VirtualboxProviderHeavyTests(VirtualboxCloudTestCase): """ # check if appropriate cloud provider and profile files are present provider_str = CONFIG_NAME - providers = self.run_cloud('--list-providers') + providers = self.run_cloud("--list-providers") log.debug("providers: %s", providers) if provider_str not in providers: self.skipTest( - 'Configuration file for {0} was not found. Check {0}.conf files ' - 'in tests/integration/files/conf/cloud.*.d/ to run these tests.'.format(PROVIDER_NAME) + "Configuration file for {0} was not found. Check {0}.conf files " + "in tests/integration/files/conf/cloud.*.d/ to run these tests.".format( + PROVIDER_NAME + ) ) # check if personal access token, ssh_key_file, and ssh_key_names are present config_path = os.path.join( - RUNTIME_VARS.FILES, - 'conf', - 'cloud.providers.d', - PROVIDER_NAME + '.conf' + RUNTIME_VARS.FILES, "conf", "cloud.providers.d", PROVIDER_NAME + ".conf" ) log.debug("config_path: %s", config_path) providers = cloud_providers_config(config_path) log.debug("config: %s", providers) config_path = os.path.join( - RUNTIME_VARS.FILES, - 'conf', - 'cloud.profiles.d', - PROVIDER_NAME + '.conf' + RUNTIME_VARS.FILES, "conf", "cloud.profiles.d", PROVIDER_NAME + ".conf" ) profiles = vm_profiles_config(config_path, providers) profile = profiles.get(DEPLOY_PROFILE_NAME) if not profile: self.skipTest( - 'Profile {0} was not found. Check {1}.conf files ' - 'in tests/integration/files/conf/cloud.profiles.d/ to run these tests.'.format(DEPLOY_PROFILE_NAME, - PROVIDER_NAME) + "Profile {0} was not found. Check {1}.conf files " + "in tests/integration/files/conf/cloud.profiles.d/ to run these tests.".format( + DEPLOY_PROFILE_NAME, PROVIDER_NAME + ) ) base_box_name = profile.get("clonefrom") if base_box_name != BOOTABLE_BASE_BOX_NAME: self.skipTest( - 'Profile {0} does not have a base box to clone from. Check {1}.conf files ' - 'in tests/integration/files/conf/cloud.profiles.d/ to run these tests.' - 'And add a "clone_from: {2}" to the profile'.format(PROFILE_NAME, PROVIDER_NAME, BOOTABLE_BASE_BOX_NAME) + "Profile {0} does not have a base box to clone from. Check {1}.conf files " + "in tests/integration/files/conf/cloud.profiles.d/ to run these tests." + 'And add a "clone_from: {2}" to the profile'.format( + PROFILE_NAME, PROVIDER_NAME, BOOTABLE_BASE_BOX_NAME + ) ) def tearDown(self): @@ -296,7 +306,9 @@ class VirtualboxProviderHeavyTests(VirtualboxCloudTestCase): log.warning("Possibly dirty state after exception", exc_info=True) def test_deploy(self): - machines = self.run_cloud('-p {0} {1} --log-level=debug'.format(DEPLOY_PROFILE_NAME, INSTANCE_NAME)) + machines = self.run_cloud( + "-p {0} {1} --log-level=debug".format(DEPLOY_PROFILE_NAME, INSTANCE_NAME) + ) self.assertIn(INSTANCE_NAME, machines.keys()) machine = machines[INSTANCE_NAME] self.assertIn("deployed", machine) @@ -334,7 +346,9 @@ class VirtualboxProviderHeavyTests(VirtualboxCloudTestCase): # Machine is up again vb_start_vm(BOOTABLE_BASE_BOX_NAME) - ip_addresses = vb_wait_for_network_address(20, machine_name=BOOTABLE_BASE_BOX_NAME) + ip_addresses = vb_wait_for_network_address( + 20, machine_name=BOOTABLE_BASE_BOX_NAME + ) network_count = len(ip_addresses) self.assertGreater(network_count, 0) @@ -342,14 +356,13 @@ class VirtualboxProviderHeavyTests(VirtualboxCloudTestCase): self.assertIsIpAddress(ip_address) -@skipIf(HAS_LIBS is False, 'The \'vboxapi\' library is not available') +@skipIf(HAS_LIBS is False, "The 'vboxapi' library is not available") class BaseVirtualboxTests(TestCase): def test_get_manager(self): self.assertIsNotNone(vb_get_box()) class CreationDestructionVirtualboxTests(VirtualboxTestCase): - def test_vm_creation_and_destruction(self): vm_name = BASE_BOX_NAME vb_create_machine(vm_name) @@ -373,10 +386,7 @@ class CloneVirtualboxTests(VirtualboxTestCase): def test_create_machine(self): vb_name = "NewTestMachine" - machine = vb_clone_vm( - name=vb_name, - clone_from=self.name - ) + machine = vb_clone_vm(name=vb_name, clone_from=self.name) self.assertEqual(machine.get("name"), vb_name) self.assertMachineExists(vb_name) @@ -384,9 +394,10 @@ class CloneVirtualboxTests(VirtualboxTestCase): self.assertMachineDoesNotExist(vb_name) -@skipIf(HAS_LIBS and vb_machine_exists(BOOTABLE_BASE_BOX_NAME) is False, - "Bootable VM '{0}' not found. Cannot run tests.".format(BOOTABLE_BASE_BOX_NAME) - ) +@skipIf( + HAS_LIBS and vb_machine_exists(BOOTABLE_BASE_BOX_NAME) is False, + "Bootable VM '{0}' not found. Cannot run tests.".format(BOOTABLE_BASE_BOX_NAME), +) class BootVirtualboxTests(VirtualboxTestCase): def test_start_stop(self): for i in range(2): @@ -401,9 +412,10 @@ class XpcomConversionTests(TestCase): @classmethod def _mock_xpcom_object(cls, interface_name=None, attributes=None): class XPCOM(object): - def __str__(self): - return "<XPCOM component '<unknown>' (implementing {0})>".format(interface_name) + return "<XPCOM component '<unknown>' (implementing {0})>".format( + interface_name + ) o = XPCOM() @@ -433,11 +445,7 @@ class XpcomConversionTests(TestCase): def test_override_attributes(self): - expected_dict = { - "herp": "derp", - "lol": "rofl", - "something": 12345 - } + expected_dict = {"herp": "derp", "lol": "rofl", "something": 12345} xpc = XpcomConversionTests._mock_xpcom_object(attributes=expected_dict) @@ -450,15 +458,17 @@ class XpcomConversionTests(TestCase): expected_extras = { "extra": "extra", } - expected_machine = dict([(attribute, attribute) for attribute in XPCOM_ATTRIBUTES[interface]]) + expected_machine = dict( + [(attribute, attribute) for attribute in XPCOM_ATTRIBUTES[interface]] + ) expected_machine.update(expected_extras) - imachine = XpcomConversionTests._mock_xpcom_object(interface, attributes=expected_machine) + imachine = XpcomConversionTests._mock_xpcom_object( + interface, attributes=expected_machine + ) ret = vb_xpcom_to_attribute_dict( - imachine, - interface_name=interface, - extra_attributes=expected_extras.keys() + imachine, interface_name=interface, extra_attributes=expected_extras.keys() ) self.assertDictEqual(ret, expected_machine) @@ -467,19 +477,17 @@ class XpcomConversionTests(TestCase): self.assertIn(key, ret_keys) def test_extra_nonexistent_attributes(self): - expected_extra_dict = { - "nonexistent": "" - } + expected_extra_dict = {"nonexistent": ""} xpcom = XpcomConversionTests._mock_xpcom_object() - ret = vb_xpcom_to_attribute_dict(xpcom, extra_attributes=expected_extra_dict.keys()) + ret = vb_xpcom_to_attribute_dict( + xpcom, extra_attributes=expected_extra_dict.keys() + ) self.assertDictEqual(ret, expected_extra_dict) def test_extra_nonexistent_attribute_with_default(self): expected_extras = [("nonexistent", list)] - expected_extra_dict = { - "nonexistent": [] - } + expected_extra_dict = {"nonexistent": []} xpcom = XpcomConversionTests._mock_xpcom_object() ret = vb_xpcom_to_attribute_dict(xpcom, extra_attributes=expected_extras) diff --git a/tests/integration/cloud/clouds/test_vmware.py b/tests/integration/cloud/clouds/test_vmware.py index 0f14f2bb4de..a22c79950ca 100644 --- a/tests/integration/cloud/clouds/test_vmware.py +++ b/tests/integration/cloud/clouds/test_vmware.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Megan Wilhite <mwilhite@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -14,45 +14,60 @@ from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest class VMWareTest(CloudTest): - ''' + """ Integration tests for the vmware cloud provider in Salt-Cloud - ''' - PROVIDER = 'vmware' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('password', 'user', 'url') + """ + + PROVIDER = "vmware" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("password", "user", "url") def test_instance(self): - ''' + """ Tests creating and deleting an instance on vmware and installing salt - ''' + """ # create the instance - disk_datastore = self.config['vmware-test']['devices']['disk']['Hard disk 2']['datastore'] + disk_datastore = self.config["vmware-test"]["devices"]["disk"]["Hard disk 2"][ + "datastore" + ] - ret_val = self.run_cloud('-p vmware-test {0}'.format(self.instance_name), timeout=TIMEOUT) - disk_datastore_str = ' [{0}] {1}/Hard disk 2-flat.vmdk'.format(disk_datastore, - self.instance_name) + ret_val = self.run_cloud( + "-p vmware-test {0}".format(self.instance_name), timeout=TIMEOUT + ) + disk_datastore_str = " [{0}] {1}/Hard disk 2-flat.vmdk".format( + disk_datastore, self.instance_name + ) # check if instance returned with salt installed self.assertInstanceExists(ret_val) - self.assertIn(disk_datastore_str, ret_val, - msg='Hard Disk 2 did not use the Datastore {0} '.format(disk_datastore)) + self.assertIn( + disk_datastore_str, + ret_val, + msg="Hard Disk 2 did not use the Datastore {0} ".format(disk_datastore), + ) self.assertDestroyInstance() def test_snapshot(self): - ''' + """ Tests creating snapshot and creating vm with --no-deploy - ''' + """ # create the instance - ret_val = self.run_cloud('-p vmware-test {0} --no-deploy'.format(self.instance_name), - timeout=TIMEOUT) + ret_val = self.run_cloud( + "-p vmware-test {0} --no-deploy".format(self.instance_name), timeout=TIMEOUT + ) # check if instance returned with salt installed self.assertInstanceExists(ret_val) - create_snapshot = self.run_cloud('-a create_snapshot {0} \ - snapshot_name=\'Test Cloud\' \ - memdump=True -y'.format(self.instance_name), timeout=TIMEOUT) - s_ret_str = 'Snapshot created successfully' + create_snapshot = self.run_cloud( + "-a create_snapshot {0} \ + snapshot_name='Test Cloud' \ + memdump=True -y".format( + self.instance_name + ), + timeout=TIMEOUT, + ) + s_ret_str = "Snapshot created successfully" self.assertIn(s_ret_str, six.text_type(create_snapshot)) diff --git a/tests/integration/cloud/clouds/test_vultrpy.py b/tests/integration/cloud/clouds/test_vultrpy.py index 3b364645bcc..fd3a9a4736e 100644 --- a/tests/integration/cloud/clouds/test_vultrpy.py +++ b/tests/integration/cloud/clouds/test_vultrpy.py @@ -1,51 +1,49 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for Vultr -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import time # Import Salt Testing Libs -from tests.integration.cloud.helpers.cloud_test_base import CloudTest, TIMEOUT +from tests.integration.cloud.helpers.cloud_test_base import TIMEOUT, CloudTest from tests.support.unit import skipIf class VultrTest(CloudTest): - ''' + """ Integration tests for the Vultr cloud provider in Salt-Cloud - ''' - PROVIDER = 'vultr' - REQUIRED_PROVIDER_CONFIG_ITEMS = ('api_key', 'ssh_key_file', 'ssh_key_name') + """ + + PROVIDER = "vultr" + REQUIRED_PROVIDER_CONFIG_ITEMS = ("api_key", "ssh_key_file", "ssh_key_name") def test_list_images(self): - ''' + """ Tests the return of running the --list-images command for Vultr - ''' - image_list = self.run_cloud('--list-images {0}'.format(self.PROVIDER)) + """ + image_list = self.run_cloud("--list-images {0}".format(self.PROVIDER)) - self.assertIn( - 'Debian 10 x64 (buster)', - [i.strip() for i in image_list] - ) + self.assertIn("Debian 10 x64 (buster)", [i.strip() for i in image_list]) def test_list_locations(self): - ''' + """ Tests the return of running the --list-locations command for Vultr - ''' - location_list = self.run_cloud('--list-locations {0}'.format(self.PROVIDER)) - self.assertIn( - 'New Jersey', - [i.strip() for i in location_list] - ) + """ + location_list = self.run_cloud("--list-locations {0}".format(self.PROVIDER)) + self.assertIn("New Jersey", [i.strip() for i in location_list]) def test_list_sizes(self): - ''' + """ Tests the return of running the --list-sizes command for Vultr - ''' - size_list = self.run_cloud('--list-sizes {0}'.format(self.PROVIDER)) - self.assertIn('2048 MB RAM,64 GB SSD,2.00 TB BW', [i.strip() for i in size_list]) + """ + size_list = self.run_cloud("--list-sizes {0}".format(self.PROVIDER)) + self.assertIn( + "2048 MB RAM,64 GB SSD,2.00 TB BW", [i.strip() for i in size_list] + ) # Commented for now, Vultr driver does not yet support key management # def test_key_management(self): @@ -87,13 +85,15 @@ class VultrTest(CloudTest): # # Delete public key # self.assertTrue(self.run_cloud('-f remove_key {0} id={1}'.format(self.PROVIDER, finger_print))) - @skipIf(True, 'Skipped temporarily') + @skipIf(True, "Skipped temporarily") def test_instance(self): - ''' + """ Test creating an instance on Vultr - ''' + """ # check if instance with salt installed returned - ret_val = self.run_cloud('-p vultr-test {0}'.format(self.instance_name), timeout=TIMEOUT + 300) + ret_val = self.run_cloud( + "-p vultr-test {0}".format(self.instance_name), timeout=TIMEOUT + 300 + ) self.assertInstanceExists(ret_val) # Vultr won't let us delete an instance less than 5 minutes old. diff --git a/tests/integration/cloud/helpers/__init__.py b/tests/integration/cloud/helpers/__init__.py index 5ff16a4e63b..3505c84b075 100644 --- a/tests/integration/cloud/helpers/__init__.py +++ b/tests/integration/cloud/helpers/__init__.py @@ -1,15 +1,16 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals + import random import string + from salt.ext.six.moves import range def random_name(size=6): - ''' + """ Generates a random cloud instance name - ''' - return 'CLOUD-TEST-' + ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(size) + """ + return "CLOUD-TEST-" + "".join( + random.choice(string.ascii_uppercase + string.digits) for x in range(size) ) diff --git a/tests/integration/cloud/helpers/cloud_test_base.py b/tests/integration/cloud/helpers/cloud_test_base.py index 9fd3b18da12..09414765ee3 100644 --- a/tests/integration/cloud/helpers/cloud_test_base.py +++ b/tests/integration/cloud/helpers/cloud_test_base.py @@ -1,26 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the Openstack Cloud Provider -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from time import sleep + import logging import os import shutil - -# Import Salt Testing libs -from tests.support.case import ShellCase -from tests.support.helpers import generate_random_name, expensiveTest -from tests.support.paths import FILES -from tests.support.runtests import RUNTIME_VARS +from time import sleep # Import Salt Libs from salt.config import cloud_config, cloud_providers_config from salt.ext.six.moves import range from salt.utils.yaml import safe_load +# Import Salt Testing libs +from tests.support.case import ShellCase +from tests.support.helpers import expensiveTest, generate_random_name +from tests.support.paths import FILES +from tests.support.runtests import RUNTIME_VARS + TIMEOUT = 500 log = logging.getLogger(__name__) @@ -28,33 +29,37 @@ log = logging.getLogger(__name__) @expensiveTest class CloudTest(ShellCase): - PROVIDER = '' + PROVIDER = "" REQUIRED_PROVIDER_CONFIG_ITEMS = tuple() - TMP_PROVIDER_DIR = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'cloud.providers.d') + TMP_PROVIDER_DIR = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "cloud.providers.d") __RE_RUN_DELAY = 30 __RE_TRIES = 12 @staticmethod def clean_cloud_dir(tmp_dir): - ''' + """ Clean the cloud.providers.d tmp directory - ''' + """ # make sure old provider configs are deleted for i in os.listdir(tmp_dir): os.remove(os.path.join(tmp_dir, i)) def query_instances(self): - ''' + """ Standardize the data returned from a salt-cloud --query - ''' - return set(x.strip(': ') for x in self.run_cloud('--query') if x.lstrip().lower().startswith('cloud-test-')) + """ + return set( + x.strip(": ") + for x in self.run_cloud("--query") + if x.lstrip().lower().startswith("cloud-test-") + ) def _instance_exists(self, instance_name=None, query=None): - ''' + """ :param instance_name: The name of the instance to check for in salt-cloud. For example this is may used when a test temporarily renames an instance :param query: The result of a salt-cloud --query run outside of this function - ''' + """ if not instance_name: instance_name = self.instance_name if not query: @@ -63,38 +68,47 @@ class CloudTest(ShellCase): log.debug('Checking for "{}" in {}'.format(instance_name, query)) if isinstance(query, set): return instance_name in query - return any(instance_name == q.strip(': ') for q in query) + return any(instance_name == q.strip(": ") for q in query) def assertInstanceExists(self, creation_ret=None, instance_name=None): - ''' + """ :param instance_name: Override the checked instance name, otherwise the class default will be used. :param creation_ret: The return value from the run_cloud() function that created the instance - ''' + """ if not instance_name: instance_name = self.instance_name # If it exists but doesn't show up in the creation_ret, there was probably an error during creation if creation_ret: - self.assertIn(instance_name, [i.strip(': ') for i in creation_ret], - 'An error occured during instance creation: |\n\t{}\n\t|'.format( - '\n\t'.join(creation_ret) - )) + self.assertIn( + instance_name, + [i.strip(": ") for i in creation_ret], + "An error occured during instance creation: |\n\t{}\n\t|".format( + "\n\t".join(creation_ret) + ), + ) else: # Verify that the instance exists via query query = self.query_instances() for tries in range(self.__RE_TRIES): if self._instance_exists(instance_name, query): log.debug( - 'Instance "{}" reported after {} seconds'.format(instance_name, tries * self.__RE_RUN_DELAY)) + 'Instance "{}" reported after {} seconds'.format( + instance_name, tries * self.__RE_RUN_DELAY + ) + ) break else: sleep(self.__RE_RUN_DELAY) query = self.query_instances() # Assert that the last query was successful - self.assertTrue(self._instance_exists(instance_name, query), - 'Instance "{}" was not created successfully: {}'.format(self.instance_name, - ', '.join(query))) + self.assertTrue( + self._instance_exists(instance_name, query), + 'Instance "{}" was not created successfully: {}'.format( + self.instance_name, ", ".join(query) + ), + ) log.debug('Instance exists and was created: "{}"'.format(instance_name)) @@ -104,25 +118,27 @@ class CloudTest(ShellCase): if not instance_name: instance_name = self.instance_name log.debug('Deleting instance "{}"'.format(instance_name)) - delete_str = self.run_cloud('-d {0} --assume-yes --out=yaml'.format(instance_name), timeout=timeout) + delete_str = self.run_cloud( + "-d {0} --assume-yes --out=yaml".format(instance_name), timeout=timeout + ) if delete_str: - delete = safe_load('\n'.join(delete_str)) + delete = safe_load("\n".join(delete_str)) self.assertIn(self.profile_str, delete) self.assertIn(self.PROVIDER, delete[self.profile_str]) self.assertIn(instance_name, delete[self.profile_str][self.PROVIDER]) delete_status = delete[self.profile_str][self.PROVIDER][instance_name] if isinstance(delete_status, str): - self.assertEqual(delete_status, 'True') + self.assertEqual(delete_status, "True") return elif isinstance(delete_status, dict): - current_state = delete_status.get('currentState') + current_state = delete_status.get("currentState") if current_state: - if current_state.get('ACTION'): - self.assertIn('.delete', current_state.get('ACTION')) + if current_state.get("ACTION"): + self.assertIn(".delete", current_state.get("ACTION")) return else: - self.assertEqual(current_state.get('name'), 'shutting-down') + self.assertEqual(current_state.get("name"), "shutting-down") return # It's not clear from the delete string that deletion was successful, ask salt-cloud after a delay query = self.query_instances() @@ -130,70 +146,72 @@ class CloudTest(ShellCase): for tries in range(6): if self._instance_exists(query=query): sleep(30) - log.debug('Instance "{}" still found in query after {} tries: {}' - .format(instance_name, tries, query)) + log.debug( + 'Instance "{}" still found in query after {} tries: {}'.format( + instance_name, tries, query + ) + ) query = self.query_instances() # The last query should have been successful self.assertNotIn(instance_name, self.query_instances()) @property def instance_name(self): - if not hasattr(self, '_instance_name'): + if not hasattr(self, "_instance_name"): # Create the cloud instance name to be used throughout the tests - subclass = self.__class__.__name__.strip('Test') + subclass = self.__class__.__name__.strip("Test") # Use the first three letters of the subclass, fill with '-' if too short - self._instance_name = generate_random_name('cloud-test-{:-<3}-'.format(subclass[:3])).lower() + self._instance_name = generate_random_name( + "cloud-test-{:-<3}-".format(subclass[:3]) + ).lower() return self._instance_name @property def providers(self): - if not hasattr(self, '_providers'): - self._providers = self.run_cloud('--list-providers') + if not hasattr(self, "_providers"): + self._providers = self.run_cloud("--list-providers") return self._providers @property def provider_config(self): - if not hasattr(self, '_provider_config'): + if not hasattr(self, "_provider_config"): self._provider_config = cloud_providers_config( os.path.join( - self.config_dir, - 'cloud.providers.d', - self.PROVIDER + '.conf' + self.config_dir, "cloud.providers.d", self.PROVIDER + ".conf" ) ) return self._provider_config[self.profile_str][self.PROVIDER] @property def config(self): - if not hasattr(self, '_config'): + if not hasattr(self, "_config"): self._config = cloud_config( os.path.join( - self.config_dir, - 'cloud.profiles.d', - self.PROVIDER + '.conf' + self.config_dir, "cloud.profiles.d", self.PROVIDER + ".conf" ) ) return self._config @property def profile_str(self): - return self.PROVIDER + '-config' + return self.PROVIDER + "-config" def setUp(self): - ''' + """ Sets up the test requirements. In child classes, define PROVIDER and REQUIRED_CONFIG_ITEMS or this will fail - ''' + """ super(CloudTest, self).setUp() if not self.PROVIDER: - self.fail('A PROVIDER must be defined for this test') + self.fail("A PROVIDER must be defined for this test") # check if appropriate cloud provider and profile files are present - if self.profile_str + ':' not in self.providers: + if self.profile_str + ":" not in self.providers: self.skipTest( - 'Configuration file for {0} was not found. Check {0}.conf files ' - 'in tests/integration/files/conf/cloud.*.d/ to run these tests.' - .format(self.PROVIDER) + "Configuration file for {0} was not found. Check {0}.conf files " + "in tests/integration/files/conf/cloud.*.d/ to run these tests.".format( + self.PROVIDER + ) ) missing_conf_item = [] @@ -202,28 +220,39 @@ class CloudTest(ShellCase): missing_conf_item.append(att) if missing_conf_item: - self.skipTest('Conf items are missing that must be provided to run these tests: {}' - .format(', '.join(missing_conf_item)) + - '\nCheck tests/integration/files/conf/cloud.providers.d/{0}.conf'.format(self.PROVIDER)) + self.skipTest( + "Conf items are missing that must be provided to run these tests: {}".format( + ", ".join(missing_conf_item) + ) + + "\nCheck tests/integration/files/conf/cloud.providers.d/{0}.conf".format( + self.PROVIDER + ) + ) def _alt_names(self): - ''' + """ Check for an instances created alongside this test's instance that weren't cleaned up - ''' + """ query = self.query_instances() instances = set() for q in query: # Verify but this is a new name and not a shutting down ec2 instance - if q.startswith(self.instance_name) and not q.split('-')[-1].startswith('DEL'): + if q.startswith(self.instance_name) and not q.split("-")[-1].startswith( + "DEL" + ): instances.add(q) - log.debug('Adding "{}" to the set of instances that needs to be deleted'.format(q)) + log.debug( + 'Adding "{}" to the set of instances that needs to be deleted'.format( + q + ) + ) return instances def _ensure_deletion(self, instance_name=None): - ''' + """ Make sure that the instance absolutely gets deleted, but fail the test if it happens in the tearDown :return True if an instance was deleted, False if no instance was deleted; and a message - ''' + """ destroyed = False if not instance_name: instance_name = self.instance_name @@ -232,10 +261,18 @@ class CloudTest(ShellCase): for tries in range(3): try: self.assertDestroyInstance(instance_name) - return False, 'The instance "{}" was deleted during the tearDown, not the test.'.format( - instance_name) + return ( + False, + 'The instance "{}" was deleted during the tearDown, not the test.'.format( + instance_name + ), + ) except AssertionError as e: - log.error('Failed to delete instance "{}". Tries: {}\n{}'.format(instance_name, tries, str(e))) + log.error( + 'Failed to delete instance "{}". Tries: {}\n{}'.format( + instance_name, tries, str(e) + ) + ) if not self._instance_exists(): destroyed = True break @@ -244,17 +281,27 @@ class CloudTest(ShellCase): if not destroyed: # Destroying instances in the tearDown is a contingency, not the way things should work by default. - return False, 'The Instance "{}" was not deleted after multiple attempts'.format(instance_name) + return ( + False, + 'The Instance "{}" was not deleted after multiple attempts'.format( + instance_name + ), + ) - return True, 'The instance "{}" cleaned up properly after the test'.format(instance_name) + return ( + True, + 'The instance "{}" cleaned up properly after the test'.format( + instance_name + ), + ) def tearDown(self): - ''' + """ Clean up after tests, If the instance still exists for any reason, delete it. Instances should be destroyed before the tearDown, assertDestroyInstance() should be called exactly one time in a test for each instance created. This is a failSafe and something went wrong if the tearDown is where an instance is destroyed. - ''' + """ success = True fail_messages = [] alt_names = self._alt_names() @@ -263,9 +310,15 @@ class CloudTest(ShellCase): if not alt_destroyed: success = False fail_messages.append(alt_destroy_message) - log.error('Failed to destroy instance "{}": {}'.format(instance, alt_destroy_message)) - self.assertTrue(success, '\n'.join(fail_messages)) - self.assertFalse(alt_names, 'Cleanup should happen in the test, not the TearDown') + log.error( + 'Failed to destroy instance "{}": {}'.format( + instance, alt_destroy_message + ) + ) + self.assertTrue(success, "\n".join(fail_messages)) + self.assertFalse( + alt_names, "Cleanup should happen in the test, not the TearDown" + ) @classmethod def tearDownClass(cls): @@ -277,6 +330,10 @@ class CloudTest(ShellCase): cls.clean_cloud_dir(cls.TMP_PROVIDER_DIR) # add the provider config for only the cloud we are testing - provider_file = cls.PROVIDER + '.conf' - shutil.copyfile(os.path.join(os.path.join(FILES, 'conf', 'cloud.providers.d'), provider_file), - os.path.join(os.path.join(cls.TMP_PROVIDER_DIR, provider_file))) + provider_file = cls.PROVIDER + ".conf" + shutil.copyfile( + os.path.join( + os.path.join(FILES, "conf", "cloud.providers.d"), provider_file + ), + os.path.join(os.path.join(cls.TMP_PROVIDER_DIR, provider_file)), + ) diff --git a/tests/integration/cloud/helpers/virtualbox.py b/tests/integration/cloud/helpers/virtualbox.py index 668f15d82fb..9603b531b37 100644 --- a/tests/integration/cloud/helpers/virtualbox.py +++ b/tests/integration/cloud/helpers/virtualbox.py @@ -2,19 +2,21 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os +import salt.utils.json +import salt.utils.virtualbox + # Import Salt Testing libs import tests.integration.cloud.helpers -from tests.support.case import ShellCase -from tests.support.unit import TestCase, skipIf -from tests.support.runtests import RUNTIME_VARS # Import Salt libs from salt.ext import six -import salt.utils.json -import salt.utils.virtualbox +from tests.support.case import ShellCase +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf # Create the cloud instance name to be used throughout the tests INSTANCE_NAME = tests.integration.cloud.helpers.random_name() @@ -30,7 +32,7 @@ BOOTABLE_BASE_BOX_NAME = "SaltMiniBuntuTest" log = logging.getLogger(__name__) -@skipIf(salt.utils.virtualbox.HAS_LIBS is False, 'virtualbox has to be installed') +@skipIf(salt.utils.virtualbox.HAS_LIBS is False, "virtualbox has to be installed") class VirtualboxTestCase(TestCase): def setUp(self): self.vbox = salt.utils.virtualbox.vb_get_box() @@ -45,10 +47,18 @@ class VirtualboxTestCase(TestCase): self.fail(e.message) def assertMachineDoesNotExist(self, name): - self.assertRaisesRegex(Exception, "Could not find a registered machine", self.vbox.findMachine, name) + self.assertRaisesRegex( + Exception, + "Could not find a registered machine", + self.vbox.findMachine, + name, + ) -@skipIf(salt.utils.virtualbox.HAS_LIBS is False, 'salt-cloud requires virtualbox to be installed') +@skipIf( + salt.utils.virtualbox.HAS_LIBS is False, + "salt-cloud requires virtualbox to be installed", +) class VirtualboxCloudTestCase(ShellCase): def run_cloud(self, arg_str, catch_stderr=False, timeout=None): """ @@ -57,18 +67,18 @@ class VirtualboxCloudTestCase(ShellCase): @return: @rtype: dict """ - config_path = os.path.join(RUNTIME_VARS.FILES, 'conf') - arg_str = '--out=json -c {0} {1}'.format(config_path, arg_str) + config_path = os.path.join(RUNTIME_VARS.FILES, "conf") + arg_str = "--out=json -c {0} {1}".format(config_path, arg_str) # arg_str = "{0} --log-level=error".format(arg_str) log.debug("running salt-cloud with %s", arg_str) - output = self.run_script('salt-cloud', arg_str, catch_stderr, timeout=timeout) + output = self.run_script("salt-cloud", arg_str, catch_stderr, timeout=timeout) # Sometimes tuples are returned??? if isinstance(output, tuple) and len(output) == 2: output = output[0] # Attempt to clean json output before fix of https://github.com/saltstack/salt/issues/27629 - valid_initial_chars = ['{', '[', '"'] + valid_initial_chars = ["{", "[", '"'] for line in output[:]: if len(line) == 0 or (line[0] not in valid_initial_chars): output.pop(0) @@ -77,7 +87,7 @@ class VirtualboxCloudTestCase(ShellCase): if len(output) is 0: return dict() else: - return salt.utils.json.loads(''.join(output)) + return salt.utils.json.loads("".join(output)) def run_cloud_function(self, function, kw_function_args=None, **kwargs): """ @@ -98,9 +108,11 @@ class VirtualboxCloudTestCase(ShellCase): args = [ "{0}='{1}'".format(key, value) for key, value in six.iteritems(kw_function_args) - ] + ] - output = self.run_cloud("-f {0} {1} {2}".format(function, CONFIG_NAME, " ".join(args)), **kwargs) + output = self.run_cloud( + "-f {0} {1} {2}".format(function, CONFIG_NAME, " ".join(args)), **kwargs + ) return output.get(CONFIG_NAME, {}).get(PROVIDER_NAME, {}) def run_cloud_action(self, action, instance_name, **kwargs): @@ -115,5 +127,7 @@ class VirtualboxCloudTestCase(ShellCase): @rtype: dict """ - output = self.run_cloud("-a {0} {1} --assume-yes".format(action, instance_name), **kwargs) + output = self.run_cloud( + "-a {0} {1} --assume-yes".format(action, instance_name), **kwargs + ) return output.get(CONFIG_NAME, {}).get(PROVIDER_NAME, {}) diff --git a/tests/integration/cloud/test_cloud.py b/tests/integration/cloud/test_cloud.py index aacaf5ece85..9bdc52c9ac1 100644 --- a/tests/integration/cloud/test_cloud.py +++ b/tests/integration/cloud/test_cloud.py @@ -1,45 +1,45 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for functions located in the salt.cloud.__init__.py file. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +# Import Salt libs +import salt.cloud + # Import Salt Testing libs from tests.integration.cloud.helpers.cloud_test_base import CloudTest from tests.support.helpers import expensiveTest -# Import Salt libs -import salt.cloud - class CloudClientTestCase(CloudTest): - ''' + """ Integration tests for the CloudClient class. Uses DigitalOcean as a salt-cloud provider. - ''' - PROVIDER = 'digitalocean' + """ + + PROVIDER = "digitalocean" REQUIRED_PROVIDER_CONFIG_ITEMS = tuple() - IMAGE_NAME = '14.04.5 x64' + IMAGE_NAME = "14.04.5 x64" @expensiveTest def setUp(self): # Use a --list-images salt-cloud call to see if the DigitalOcean provider is # configured correctly before running any tests. - images = self.run_cloud('--list-images {0}'.format(self.PROVIDER)) + images = self.run_cloud("--list-images {0}".format(self.PROVIDER)) if self.image_name not in [i.strip() for i in images]: self.skipTest( - 'Image \'{0}\' was not found in image search. Is the {1} provider ' - 'configured correctly for this test?'.format( - self.PROVIDER, - self.image_name + "Image '{0}' was not found in image search. Is the {1} provider " + "configured correctly for this test?".format( + self.PROVIDER, self.image_name ) ) def test_cloud_client_create_and_delete(self): - ''' + """ Tests that a VM is created successfully when calling salt.cloud.CloudClient.create(), which does not require a profile configuration. @@ -47,7 +47,7 @@ class CloudClientTestCase(CloudTest): to remove the VM after creating it. This test was created as a regression check against Issue #41971. - ''' + """ cloud_client = salt.cloud.CloudClient(self.config_file) # Create the VM using salt.cloud.CloudClient.create() instead of calling salt-cloud @@ -55,7 +55,9 @@ class CloudClientTestCase(CloudTest): provider=self.PROVIDER, names=[self.instance_name], image=self.IMAGE_NAME, - location='sfo1', size='512mb', vm_size='512mb' + location="sfo1", + size="512mb", + vm_size="512mb", ) # Check that the VM was created correctly diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index ff53d8d7601..853139d225b 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" tests.integration.conftest ~~~~~~~~~~~~~~~~~~~~~~~~~~ Integration tests PyTest configuration/fixtures -''' +""" # pylint: disable=unused-argument,redefined-outer-name # Import Python libs from __future__ import absolute_import, unicode_literals + import logging from collections import OrderedDict @@ -19,26 +20,35 @@ import pytest log = logging.getLogger(__name__) -@pytest.fixture(scope='package', autouse=True) -def default_session_daemons(request, - log_server, - session_salt_master, - session_salt_minion, - session_secondary_salt_minion, - ): +@pytest.fixture(scope="package", autouse=True) +def default_session_daemons( + request, + log_server, + session_salt_master, + session_salt_minion, + session_secondary_salt_minion, +): - request.session.stats_processes.update(OrderedDict(( - ('Salt Master', psutil.Process(session_salt_master.pid)), - ('Salt Minion', psutil.Process(session_salt_minion.pid)), - ('Salt Sub Minion', psutil.Process(session_secondary_salt_minion.pid)), - )).items()) + request.session.stats_processes.update( + OrderedDict( + ( + ("Salt Master", psutil.Process(session_salt_master.pid)), + ("Salt Minion", psutil.Process(session_salt_minion.pid)), + ("Salt Sub Minion", psutil.Process(session_secondary_salt_minion.pid)), + ) + ).items() + ) # Run tests yield # Stop daemons now(they would be stopped at the end of the test run session - for daemon in (session_secondary_salt_minion, session_salt_minion, session_salt_master): + for daemon in ( + session_secondary_salt_minion, + session_salt_minion, + session_salt_master, + ): try: daemon.terminate() except Exception as exc: # pylint: disable=broad-except - log.warning('Failed to terminate daemon: %s', daemon.__class__.__name__) + log.warning("Failed to terminate daemon: %s", daemon.__class__.__name__) diff --git a/tests/integration/daemons/test_masterapi.py b/tests/integration/daemons/test_masterapi.py index ca1638ab80f..351faec6923 100644 --- a/tests/integration/daemons/test_masterapi.py +++ b/tests/integration/daemons/test_masterapi.py @@ -2,61 +2,67 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import stat -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ShellCase - -# Import 3rd-party libs - # Import Salt libs import salt.utils.files import salt.utils.stringutils +from tests.support.case import ShellCase + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS + +# Import 3rd-party libs class AutosignGrainsTest(ShellCase): - ''' + """ Test autosigning minions based on grain values. - ''' + """ def setUp(self): # all read, only owner write - self.autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + self.autosign_file_permissions = ( + stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + ) if RUNTIME_VARS.PYTEST_SESSION: - self.autosign_file_path = os.path.join(RUNTIME_VARS.TMP, 'autosign_file') + self.autosign_file_path = os.path.join(RUNTIME_VARS.TMP, "autosign_file") else: - self.autosign_file_path = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'autosign_file') + self.autosign_file_path = os.path.join( + RUNTIME_VARS.TMP, "rootdir", "autosign_file" + ) shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'autosign_grains', 'autosign_file'), - self.autosign_file_path + os.path.join(RUNTIME_VARS.FILES, "autosign_grains", "autosign_file"), + self.autosign_file_path, ) os.chmod(self.autosign_file_path, self.autosign_file_permissions) - self.run_key('-d minion -y') - self.run_call('test.ping -l quiet') # get minon to try to authenticate itself again + self.run_key("-d minion -y") + self.run_call( + "test.ping -l quiet" + ) # get minon to try to authenticate itself again - if 'minion' in self.run_key('-l acc'): + if "minion" in self.run_key("-l acc"): self.tearDown() - self.skipTest('Could not deauthorize minion') - if 'minion' not in self.run_key('-l un'): + self.skipTest("Could not deauthorize minion") + if "minion" not in self.run_key("-l un"): self.tearDown() - self.skipTest('minion did not try to reauthenticate itself') + self.skipTest("minion did not try to reauthenticate itself") - self.autosign_grains_dir = os.path.join(self.master_opts['autosign_grains_dir']) + self.autosign_grains_dir = os.path.join(self.master_opts["autosign_grains_dir"]) if not os.path.isdir(self.autosign_grains_dir): os.makedirs(self.autosign_grains_dir) def tearDown(self): shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'autosign_file'), - self.autosign_file_path + os.path.join(RUNTIME_VARS.FILES, "autosign_file"), self.autosign_file_path ) os.chmod(self.autosign_file_path, self.autosign_file_permissions) - self.run_call('test.ping -l quiet') # get minon to authenticate itself again + self.run_call("test.ping -l quiet") # get minon to authenticate itself again try: if os.path.isdir(self.autosign_grains_dir): @@ -65,20 +71,24 @@ class AutosignGrainsTest(ShellCase): pass def test_autosign_grains_accept(self): - grain_file_path = os.path.join(self.autosign_grains_dir, 'test_grain') - with salt.utils.files.fopen(grain_file_path, 'w') as f: - f.write(salt.utils.stringutils.to_str('#invalid_value\ncheese')) + grain_file_path = os.path.join(self.autosign_grains_dir, "test_grain") + with salt.utils.files.fopen(grain_file_path, "w") as f: + f.write(salt.utils.stringutils.to_str("#invalid_value\ncheese")) os.chmod(grain_file_path, self.autosign_file_permissions) - self.run_call('test.ping -l quiet') # get minon to try to authenticate itself again - self.assertIn('minion', self.run_key('-l acc')) + self.run_call( + "test.ping -l quiet" + ) # get minon to try to authenticate itself again + self.assertIn("minion", self.run_key("-l acc")) def test_autosign_grains_fail(self): - grain_file_path = os.path.join(self.autosign_grains_dir, 'test_grain') - with salt.utils.files.fopen(grain_file_path, 'w') as f: - f.write(salt.utils.stringutils.to_str('#cheese\ninvalid_value')) + grain_file_path = os.path.join(self.autosign_grains_dir, "test_grain") + with salt.utils.files.fopen(grain_file_path, "w") as f: + f.write(salt.utils.stringutils.to_str("#cheese\ninvalid_value")) os.chmod(grain_file_path, self.autosign_file_permissions) - self.run_call('test.ping -l quiet') # get minon to try to authenticate itself again - self.assertNotIn('minion', self.run_key('-l acc')) - self.assertIn('minion', self.run_key('-l un')) + self.run_call( + "test.ping -l quiet" + ) # get minon to try to authenticate itself again + self.assertNotIn("minion", self.run_key("-l acc")) + self.assertIn("minion", self.run_key("-l un")) diff --git a/tests/integration/doc/test_man.py b/tests/integration/doc/test_man.py index 1803bb15121..c7058e94aad 100644 --- a/tests/integration/doc/test_man.py +++ b/tests/integration/doc/test_man.py @@ -1,88 +1,57 @@ # -*- coding: utf-8 -*- -''' +""" Tests for existence of manpages -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os import pprint import shutil # Import Salt libs import salt.utils.platform +from tests.support.case import ModuleCase # Import Salt Testing libs from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase from tests.support.unit import skipIf -@skipIf(salt.utils.platform.is_windows(), 'minion is windows') -@skipIf(salt.utils.platform.is_aix(), 'minion is AIX') +@skipIf(salt.utils.platform.is_windows(), "minion is windows") +@skipIf(salt.utils.platform.is_aix(), "minion is AIX") class ManTest(ModuleCase): - @classmethod def setUpClass(cls): - cls.rootdir = os.path.join(RUNTIME_VARS.TMP, 'mantest') + cls.rootdir = os.path.join(RUNTIME_VARS.TMP, "mantest") # Map filenames to search strings which should be in the manpage cls.manpages = { - 'salt-cp.1': [ - 'salt-cp Documentation', - 'copies files from the master', + "salt-cp.1": ["salt-cp Documentation", "copies files from the master"], + "salt-cloud.1": [ + "Salt Cloud Command", + "Provision virtual machines in the cloud", ], - 'salt-cloud.1': [ - 'Salt Cloud Command', - 'Provision virtual machines in the cloud', + "salt-call.1": ["salt-call Documentation", "run module functions locally"], + "salt-api.1": [ + "salt-api Command", + "Start interfaces used to remotely connect", ], - 'salt-call.1': [ - 'salt-call Documentation', - 'run module functions locally', + "salt-unity.1": ["salt-unity Command", "unified invocation wrapper"], + "salt-syndic.1": ["salt-syndic Documentation", "Salt syndic daemon"], + "salt-ssh.1": ["salt-ssh Documentation", "executed using only SSH"], + "salt-run.1": ["salt-run Documentation", "frontend command for executing"], + "salt-proxy.1": ["salt-proxy Documentation", "proxies these commands"], + "salt-minion.1": ["salt-minion Documentation", "Salt minion daemon"], + "salt-master.1": ["salt-master Documentation", "Salt master daemon"], + "salt-key.1": [ + "salt-key Documentation", + "management of Salt server public keys", ], - 'salt-api.1': [ - 'salt-api Command', - 'Start interfaces used to remotely connect', - ], - 'salt-unity.1': [ - 'salt-unity Command', - 'unified invocation wrapper', - ], - 'salt-syndic.1': [ - 'salt-syndic Documentation', - 'Salt syndic daemon', - ], - 'salt-ssh.1': [ - 'salt-ssh Documentation', - 'executed using only SSH', - ], - 'salt-run.1': [ - 'salt-run Documentation', - 'frontend command for executing', - ], - 'salt-proxy.1': [ - 'salt-proxy Documentation', - 'proxies these commands', - ], - 'salt-minion.1': [ - 'salt-minion Documentation', - 'Salt minion daemon', - ], - 'salt-master.1': [ - 'salt-master Documentation', - 'Salt master daemon', - ], - 'salt-key.1': [ - 'salt-key Documentation', - 'management of Salt server public keys', - ], - 'salt.1': [ - 'allows for commands to be executed', - ], - 'salt.7': [ - 'Salt Documentation', - ], - 'spm.1': [ - 'Salt Package Manager Command', - 'command for managing Salt packages', + "salt.1": ["allows for commands to be executed"], + "salt.7": ["Salt Documentation"], + "spm.1": [ + "Salt Package Manager Command", + "command for managing Salt packages", ], } @@ -93,25 +62,25 @@ class ManTest(ModuleCase): def setUp(self): self.addCleanup(shutil.rmtree, self.rootdir, ignore_errors=True) if not os.path.exists(self.rootdir): - ret = self.run_function('mantest.install', [self.rootdir]) + ret = self.run_function("mantest.install", [self.rootdir]) if not isinstance(ret, dict): self.fail( - 'The \'mantest.install\' command did not return the excepted dictionary. Output:\n{}'.format( + "The 'mantest.install' command did not return the excepted dictionary. Output:\n{}".format( ret ) ) - if ret['retcode'] != 0: + if ret["retcode"] != 0: self.fail( - 'Failed to install. Full return dictionary:\n{}'.format( + "Failed to install. Full return dictionary:\n{}".format( pprint.pformat(ret) ) ) def test_man(self): - ''' + """ Make sure that man pages are installed - ''' - ret = self.run_function('mantest.search', [self.manpages, self.rootdir]) + """ + ret = self.run_function("mantest.search", [self.manpages, self.rootdir]) # The above function returns True if successful and an exception (which # will manifest in the return as a stringified exception) if # unsuccessful. Therefore, a simple assertTrue is not sufficient. diff --git a/tests/integration/externalapi/test_venafiapi.py b/tests/integration/externalapi/test_venafiapi.py index 9c2bb8e4d2a..0965d704d57 100644 --- a/tests/integration/externalapi/test_venafiapi.py +++ b/tests/integration/externalapi/test_venafiapi.py @@ -1,26 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the salt-run command -''' +""" # Import Python libs from __future__ import absolute_import + import functools import random import string +import tempfile +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.x509.oid import NameOID +from salt.ext.six import text_type +from salt.ext.six.moves import range # Import Salt Testing libs from tests.support.case import ShellCase -from salt.ext.six.moves import range -from salt.ext.six import text_type -from cryptography import x509 -from cryptography.hazmat.backends import default_backend -from cryptography.x509.oid import NameOID -from cryptography.hazmat.primitives import serialization -import tempfile -def _random_name(prefix=''): +def _random_name(prefix=""): ret = prefix for _ in range(8): ret += random.choice(string.ascii_lowercase) @@ -28,60 +29,61 @@ def _random_name(prefix=''): def with_random_name(func): - ''' + """ generate a randomized name for a container - ''' + """ @functools.wraps(func) def wrapper(self, *args, **kwargs): - name = _random_name(prefix='salt_') - return func(self, _random_name(prefix='salt-test-'), *args, **kwargs) + name = _random_name(prefix="salt_") + return func(self, _random_name(prefix="salt-test-"), *args, **kwargs) return wrapper class VenafiTest(ShellCase): - ''' + """ Test the venafi runner - ''' + """ @with_random_name def test_request(self, name): - cn = '{0}.example.com'.format(name) + cn = "{0}.example.com".format(name) # Provide python27 compatibility if not isinstance(cn, text_type): cn = cn.decode() - ret = self.run_run_plus(fun='venafi.request', - minion_id=cn, - dns_name=cn, - key_password='secretPassword', - zone='fake') - cert_output = ret['return'][0] - assert cert_output is not None, 'venafi_certificate not found in `output_value`' + ret = self.run_run_plus( + fun="venafi.request", + minion_id=cn, + dns_name=cn, + key_password="secretPassword", + zone="fake", + ) + cert_output = ret["return"][0] + assert cert_output is not None, "venafi_certificate not found in `output_value`" cert = x509.load_pem_x509_certificate(cert_output.encode(), default_backend()) assert isinstance(cert, x509.Certificate) assert cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME) == [ - x509.NameAttribute( - NameOID.COMMON_NAME, cn - ) + x509.NameAttribute(NameOID.COMMON_NAME, cn) ] - pkey_output = ret['return'][1] - assert pkey_output is not None, 'venafi_private key not found in output_value' + pkey_output = ret["return"][1] + assert pkey_output is not None, "venafi_private key not found in output_value" - pkey = serialization.load_pem_private_key(pkey_output.encode(), password=b'secretPassword', - backend=default_backend()) + pkey = serialization.load_pem_private_key( + pkey_output.encode(), password=b"secretPassword", backend=default_backend() + ) pkey_public_key_pem = pkey.public_key().public_bytes( encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo + format=serialization.PublicFormat.SubjectPublicKeyInfo, ) cert_public_key_pem = cert.public_key().public_bytes( encoding=serialization.Encoding.PEM, - format=serialization.PublicFormat.SubjectPublicKeyInfo + format=serialization.PublicFormat.SubjectPublicKeyInfo, ) assert pkey_public_key_pem == cert_public_key_pem @@ -121,7 +123,7 @@ xlAKgaU6i03jOm5+sww5L2YVMi1eeBN+kx7o94ogpRemC/EUidvl1PUJ6+e7an9V -----END CERTIFICATE REQUEST----- """ - with tempfile.NamedTemporaryFile('w+') as f: + with tempfile.NamedTemporaryFile("w+") as f: f.write(csr_pem) f.flush() csr_path = f.name @@ -131,17 +133,18 @@ xlAKgaU6i03jOm5+sww5L2YVMi1eeBN+kx7o94ogpRemC/EUidvl1PUJ6+e7an9V if not isinstance(cn, text_type): cn = cn.decode() - ret = self.run_run_plus(fun='venafi.request', - minion_id=cn, - csr_path=csr_path, - zone='fake') - cert_output = ret['return'][0] - assert cert_output is not None, 'venafi_certificate not found in `output_value`' + ret = self.run_run_plus( + fun="venafi.request", minion_id=cn, csr_path=csr_path, zone="fake" + ) + cert_output = ret["return"][0] + assert ( + cert_output is not None + ), "venafi_certificate not found in `output_value`" - cert = x509.load_pem_x509_certificate(cert_output.encode(), default_backend()) + cert = x509.load_pem_x509_certificate( + cert_output.encode(), default_backend() + ) assert isinstance(cert, x509.Certificate) assert cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME) == [ - x509.NameAttribute( - NameOID.COMMON_NAME, cn - ) + x509.NameAttribute(NameOID.COMMON_NAME, cn) ] diff --git a/tests/integration/files/conf/syndic b/tests/integration/files/conf/syndic index 3d465162e4c..c8525481b66 100644 --- a/tests/integration/files/conf/syndic +++ b/tests/integration/files/conf/syndic @@ -8,3 +8,4 @@ syndic_pidfile: run/syndic.pid tcp_pub_port: 64510 tcp_pull_port: 64511 sock_dir: syndic_sock +zmq_filtering: True diff --git a/tests/integration/files/conf/syndic_master b/tests/integration/files/conf/syndic_master index 2778a359176..e7efda11aec 100644 --- a/tests/integration/files/conf/syndic_master +++ b/tests/integration/files/conf/syndic_master @@ -27,3 +27,5 @@ order_masters: True # enable using ssh minions and regular minions enable_ssh_minions: True ignore_host_keys: True + +zmq_filtering: True diff --git a/tests/integration/files/engines/runtests_engine.py b/tests/integration/files/engines/runtests_engine.py index c34cbeb1564..33976a9df0e 100644 --- a/tests/integration/files/engines/runtests_engine.py +++ b/tests/integration/files/engines/runtests_engine.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) :copyright: Copyright 2015 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. @@ -10,35 +10,34 @@ Simple salt engine which will setup a socket to accept connections allowing us to know when a daemon is up and running -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys + import errno -import socket import logging +import os +import socket +import sys + +import salt.utils.asynchronous # Import salt libs import salt.utils.event -import salt.utils.asynchronous # Import 3rd-party libs -from salt.ext.tornado import gen -from salt.ext.tornado import ioloop -from salt.ext.tornado import netutil -from salt.ext.tornado import iostream +from salt.ext.tornado import gen, ioloop, iostream, netutil log = logging.getLogger(__name__) -__virtualname__ = 'salt_runtests' +__virtualname__ = "salt_runtests" def __virtual__(): - if __opts__['__role'] != 'master': + if __opts__["__role"] != "master": return False - return 'runtests_conn_check_port' in __opts__ # pylint: disable=undefined-variable + return "runtests_conn_check_port" in __opts__ # pylint: disable=undefined-variable def start(): @@ -50,7 +49,7 @@ class PyTestEngine(object): def __init__(self, opts): self.opts = opts self.sock = None - self.stop_sending_events_file = opts.get('pytest_stop_sending_events_file') + self.stop_sending_events_file = opts.get("pytest_stop_sending_events_file") def start(self): self.io_loop = ioloop.IOLoop() @@ -60,33 +59,39 @@ class PyTestEngine(object): @gen.coroutine def _start(self): - port = int(self.opts['runtests_conn_check_port']) - log.info('Starting Pytest Engine(role=%s, id=%s) on port %s', self.opts['__role'], self.opts['id'], port) + port = int(self.opts["runtests_conn_check_port"]) + log.info( + "Starting Pytest Engine(role=%s, id=%s) on port %s", + self.opts["__role"], + self.opts["id"], + port, + ) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.setblocking(0) # bind the socket to localhost on the config provided port - self.sock.bind(('localhost', port)) + self.sock.bind(("localhost", port)) # become a server socket self.sock.listen(5) with salt.utils.asynchronous.current_ioloop(self.io_loop): netutil.add_accept_handler( - self.sock, - self.handle_connection, + self.sock, self.handle_connection, ) - if self.opts['__role'] == 'master': + if self.opts["__role"] == "master": yield self.fire_master_started_event() def handle_connection(self, connection, address): - log.warning('Accepted connection from %s. Role: %s', address, self.opts['__role']) + log.warning( + "Accepted connection from %s. Role: %s", address, self.opts["__role"] + ) # We just need to know that the daemon running the engine is alive... try: connection.shutdown(socket.SHUT_RDWR) # pylint: disable=no-member connection.close() except socket.error as exc: - if not sys.platform.startswith('darwin'): + if not sys.platform.startswith("darwin"): raise try: if exc.errno != errno.ENOTCONN: @@ -97,17 +102,29 @@ class PyTestEngine(object): @gen.coroutine def fire_master_started_event(self): - log.info('Firing salt-%s started event...', self.opts['__role']) - start_event_tag = 'salt/{}/{}/start'.format(self.opts['__role'], self.opts['id']) - log.info('Firing salt-%s started event. Tag: %s', self.opts['__role'], start_event_tag) - load = {'id': self.opts['id'], 'tag': start_event_tag, 'data': {}} + log.info("Firing salt-%s started event...", self.opts["__role"]) + start_event_tag = "salt/{}/{}/start".format( + self.opts["__role"], self.opts["id"] + ) + log.info( + "Firing salt-%s started event. Tag: %s", + self.opts["__role"], + start_event_tag, + ) + load = {"id": self.opts["id"], "tag": start_event_tag, "data": {}} # One minute should be more than enough to fire these events every second in order # for pytest-salt to pickup that the master is running - with salt.utils.event.get_master_event(self.opts, self.opts['sock_dir'], listen=False) as event_bus: + with salt.utils.event.get_master_event( + self.opts, self.opts["sock_dir"], listen=False + ) as event_bus: timeout = 30 while True: - if self.stop_sending_events_file and not os.path.exists(self.stop_sending_events_file): - log.info('The stop sending events file "marker" is done. Stop sending events...') + if self.stop_sending_events_file and not os.path.exists( + self.stop_sending_events_file + ): + log.info( + 'The stop sending events file "marker" is done. Stop sending events...' + ) break timeout -= 1 try: diff --git a/tests/integration/files/extension_modules/pillar/ext_pillar_opts.py b/tests/integration/files/extension_modules/pillar/ext_pillar_opts.py index d1dc454902b..8d4aefbed1f 100644 --- a/tests/integration/files/extension_modules/pillar/ext_pillar_opts.py +++ b/tests/integration/files/extension_modules/pillar/ext_pillar_opts.py @@ -1,25 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" External pillar module for testing the contents of __opts__ as seen by external pillar modules. Returns a hash of the name of the pillar module as defined in _virtual__ with the value __opts__ -''' +""" # Import python libs from __future__ import absolute_import + import logging # Set up logging log = logging.getLogger(__name__) # DRY up the name we use -MY_NAME = 'ext_pillar_opts' +MY_NAME = "ext_pillar_opts" def __virtual__(): - log.debug('Loaded external pillar %s as %s', __name__, MY_NAME) + log.debug("Loaded external pillar %s as %s", __name__, MY_NAME) return True diff --git a/tests/integration/files/extension_modules/tops/master_tops_test.py b/tests/integration/files/extension_modules/tops/master_tops_test.py index 27cafec4079..f26b9954b46 100644 --- a/tests/integration/files/extension_modules/tops/master_tops_test.py +++ b/tests/integration/files/extension_modules/tops/master_tops_test.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -import logging +import logging log = logging.getLogger(__name__) def __virtual__(): - log.info('master tops test loaded') - return 'master_tops_test' + log.info("master tops test loaded") + return "master_tops_test" def top(**kwargs): - log.info('master_tops_test') - return {'base': ['master_tops_test']} + log.info("master_tops_test") + return {"base": ["master_tops_test"]} diff --git a/tests/integration/files/file/base/HelloWorld.ps1 b/tests/integration/files/file/base/HelloWorld.ps1 new file mode 100644 index 00000000000..b0cfbac932a --- /dev/null +++ b/tests/integration/files/file/base/HelloWorld.ps1 @@ -0,0 +1,16 @@ +Configuration HelloWorld { + + # Import the module that contains the File resource. + Import-DscResource -ModuleName PsDesiredStateConfiguration + + # The Node statement specifies which targets to compile MOF files for, when this configuration is executed. + Node $AllNodes.NodeName { + + # The File resource can ensure the state of files, or copy them from a source to a destination with persistent updates. + File HelloWorld { + DestinationPath = "C:\Temp\HelloWorld.txt" + Ensure = "Present" + Contents = "Hello World from DSC!" + } + } +} diff --git a/tests/integration/files/file/base/HelloWorld.psd1 b/tests/integration/files/file/base/HelloWorld.psd1 new file mode 100644 index 00000000000..3acc3c9906b --- /dev/null +++ b/tests/integration/files/file/base/HelloWorld.psd1 @@ -0,0 +1,9 @@ +@{ + AllNodes = @( + @{ + NodeName = 'localhost' + PSDscAllowPlainTextPassword = $true + PSDscAllowDomainUser = $true + } + ) +} diff --git a/tests/integration/files/file/base/_executors/arg.py b/tests/integration/files/file/base/_executors/arg.py index dfe09cb7b83..8465f1b81df 100644 --- a/tests/integration/files/file/base/_executors/arg.py +++ b/tests/integration/files/file/base/_executors/arg.py @@ -7,4 +7,4 @@ def __virtual__(): def execute(*args, **kwargs): # we use the dunder to assert the loader is provided minionmods - return __salt__['test.arg']('test.arg fired') + return __salt__["test.arg"]("test.arg fired") diff --git a/tests/integration/files/file/base/_grains/custom_grain2.py b/tests/integration/files/file/base/_grains/custom_grain2.py index 8d96666d7e0..bbf7ca5cefa 100644 --- a/tests/integration/files/file/base/_grains/custom_grain2.py +++ b/tests/integration/files/file/base/_grains/custom_grain2.py @@ -3,5 +3,5 @@ def myfunction(): grains = {} - grains['a_custom'] = {'k2': 'v2'} + grains["a_custom"] = {"k2": "v2"} return grains diff --git a/tests/integration/files/file/base/_grains/custom_grains.py b/tests/integration/files/file/base/_grains/custom_grains.py index f41c4dd6d8c..e015e086933 100644 --- a/tests/integration/files/file/base/_grains/custom_grains.py +++ b/tests/integration/files/file/base/_grains/custom_grains.py @@ -2,4 +2,4 @@ def test(grains): - return {'custom_grain_test': 'itworked' if 'os' in grains else 'itdidntwork'} + return {"custom_grain_test": "itworked" if "os" in grains else "itdidntwork"} diff --git a/tests/integration/files/file/base/_grains/matcher_grain.py b/tests/integration/files/file/base/_grains/matcher_grain.py index f7d6c8f76ff..ef4f8c887c2 100644 --- a/tests/integration/files/file/base/_grains/matcher_grain.py +++ b/tests/integration/files/file/base/_grains/matcher_grain.py @@ -3,5 +3,5 @@ def myfunction(): grains = {} - grains['match'] = 'maker' + grains["match"] = "maker" return grains diff --git a/tests/integration/files/file/base/_modules/mantest.py b/tests/integration/files/file/base/_modules/mantest.py index 16a9d5e5d2a..49fba3576da 100644 --- a/tests/integration/files/file/base/_modules/mantest.py +++ b/tests/integration/files/file/base/_modules/mantest.py @@ -1,12 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Helpers for testing man pages -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import sys -import logging # Import Salt libs import salt.utils.files @@ -23,13 +24,14 @@ log = logging.getLogger(__name__) def install(rootdir): if not os.path.exists(rootdir): os.makedirs(rootdir) - return __salt__['cmd.run_all']( + return __salt__["cmd.run_all"]( [ sys.executable, - os.path.join(RUNTIME_VARS.CODE_DIR, 'setup.py'), - 'install', '--root={0}'.format(rootdir) + os.path.join(RUNTIME_VARS.CODE_DIR, "setup.py"), + "install", + "--root={0}".format(rootdir), ], - redirect_stderr=True + redirect_stderr=True, ) @@ -49,9 +51,8 @@ def search(manpages, rootdir): if manpage_fns: raise CommandExecutionError( - 'The following manpages were not found under {0}: {1}'.format( - rootdir, - ', '.join(sorted(manpage_fns)) + "The following manpages were not found under {0}: {1}".format( + rootdir, ", ".join(sorted(manpage_fns)) ) ) @@ -63,20 +64,18 @@ def search(manpages, rootdir): for search_string in manpages[manpage]: if search_string not in contents: failed.setdefault(manpage, []).append( - 'No match for search string \'{0}\' found in {1}'.format( + "No match for search string '{0}' found in {1}".format( search_string, manpage_paths[manpage] ) ) # Check for correct install dir - path = '/man{0}/'.format(manpage.rsplit('.', 1)[-1]) + path = "/man{0}/".format(manpage.rsplit(".", 1)[-1]) if path not in manpage_paths[manpage]: failed.setdefault(manpage, []).append( - '{0} not found in manpage path {1}'.format( - path, manpage_paths[manpage] - ) + "{0} not found in manpage path {1}".format(path, manpage_paths[manpage]) ) if failed: - raise CommandExecutionError('One or more manpages failed', info=failed) + raise CommandExecutionError("One or more manpages failed", info=failed) return True diff --git a/tests/integration/files/file/base/_modules/override_test.py b/tests/integration/files/file/base/_modules/override_test.py index 6d979195754..e688608de71 100644 --- a/tests/integration/files/file/base/_modules/override_test.py +++ b/tests/integration/files/file/base/_modules/override_test.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Module for running arbitrary tests -''' +""" from __future__ import absolute_import -__virtualname__ = 'test' +__virtualname__ = "test" def __virtual__(): @@ -12,7 +12,7 @@ def __virtual__(): def recho(text): - ''' + """ Return a reversed string CLI Example: @@ -20,5 +20,5 @@ def recho(text): .. code-block:: bash salt '*' test.recho 'foo bar baz quo qux' - ''' + """ return text[::-1] diff --git a/tests/integration/files/file/base/_modules/runtests_decorators.py b/tests/integration/files/file/base/_modules/runtests_decorators.py index 182830e6f16..164f8f457da 100644 --- a/tests/integration/files/file/base/_modules/runtests_decorators.py +++ b/tests/integration/files/file/base/_modules/runtests_decorators.py @@ -2,6 +2,7 @@ # Import Python libs from __future__ import absolute_import + import os import time @@ -9,19 +10,19 @@ import time import salt.utils.decorators from tests.support.runtests import RUNTIME_VARS -EXIT_CODE_SH = os.path.join(RUNTIME_VARS.BASE_FILES, 'exit_code.sh') -EXIT_CODE_CMD = os.path.join(RUNTIME_VARS.BASE_FILES, 'exit_code.cmd') +EXIT_CODE_SH = os.path.join(RUNTIME_VARS.BASE_FILES, "exit_code.sh") +EXIT_CODE_CMD = os.path.join(RUNTIME_VARS.BASE_FILES, "exit_code.cmd") def _exit_code(code): - if os.name == 'nt': - return 'cmd /c {0} {1}'.format(EXIT_CODE_CMD, code) + if os.name == "nt": + return "cmd /c {0} {1}".format(EXIT_CODE_CMD, code) else: - return '/usr/bin/env sh {0} {1}'.format(EXIT_CODE_SH, code) + return "/usr/bin/env sh {0} {1}".format(EXIT_CODE_SH, code) def _fallbackfunc(): - return False, 'fallback' + return False, "fallback" def working_function(): @@ -38,29 +39,26 @@ def booldependsFalse(): return True -@salt.utils.decorators.depends('time') +@salt.utils.decorators.depends("time") def depends(): - ret = {'ret': True, - 'time': time.time()} + ret = {"ret": True, "time": time.time()} return ret -@salt.utils.decorators.depends('time123') +@salt.utils.decorators.depends("time123") def missing_depends(): return True -@salt.utils.decorators.depends('time', fallback_function=_fallbackfunc) +@salt.utils.decorators.depends("time", fallback_function=_fallbackfunc) def depends_will_not_fallback(): - ret = {'ret': True, - 'time': time.time()} + ret = {"ret": True, "time": time.time()} return ret -@salt.utils.decorators.depends('time123', fallback_function=_fallbackfunc) +@salt.utils.decorators.depends("time123", fallback_function=_fallbackfunc) def missing_depends_will_fallback(): - ret = {'ret': True, - 'time': time.time()} + ret = {"ret": True, "time": time.time()} return ret @@ -95,17 +93,17 @@ def command_failure_nonzero_retcode_false(): # The 'depends_versioned.py'-module has __version__ = '1.8' -@salt.utils.decorators.depends('depends_versioned', version='1.0') +@salt.utils.decorators.depends("depends_versioned", version="1.0") def version_depends_false(): return True -@salt.utils.decorators.depends('depends_versioned', version='2.0') +@salt.utils.decorators.depends("depends_versioned", version="2.0") def version_depends_true(): return True # The 'depends_versionless.py'-module does not have a `__version__`-string -@salt.utils.decorators.depends('depends_versionless', version='0.2') +@salt.utils.decorators.depends("depends_versionless", version="0.2") def version_depends_versionless_true(): return True diff --git a/tests/integration/files/file/base/_modules/runtests_helpers.py b/tests/integration/files/file/base/_modules/runtests_helpers.py index 4672c2e6995..b60ab8574aa 100644 --- a/tests/integration/files/file/base/_modules/runtests_helpers.py +++ b/tests/integration/files/file/base/_modules/runtests_helpers.py @@ -1,18 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) runtests_helpers.py ~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs from __future__ import absolute_import + +import fnmatch +import logging import os import re import sys -import fnmatch import tempfile # Import salt libs @@ -30,16 +32,21 @@ except ImportError: # Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long # for unix sockets: ``error: AF_UNIX path too long`` # Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR} - os.environ.get('TMPDIR', tempfile.gettempdir()) if not salt.utils.platform.is_darwin() else '/tmp' + os.environ.get("TMPDIR", tempfile.gettempdir()) + if not salt.utils.platform.is_darwin() + else "/tmp" ) # This tempdir path is defined on tests.integration.__init__ - TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir') + TMP = os.path.join(SYS_TMP_DIR, "salt-tests-tmpdir") class RUNTIME_VARS(object): TMP = TMP SYS_TMP_DIR = SYS_TMP_DIR +log = logging.getLogger(__name__) + + def get_salt_temp_dir(): return RUNTIME_VARS.TMP @@ -53,61 +60,61 @@ def get_sys_temp_dir_for_path(*path): def get_invalid_docs(): - ''' + """ Outputs the functions which do not have valid CLI example, or are missing a docstring. - ''' + """ allow_failure = ( - 'cmd.win_runas', - 'cp.recv', - 'cp.recv_chunked', - 'glance.warn_until', - 'ipset.long_range', - 'libcloud_compute.get_driver', - 'libcloud_dns.get_driver', - 'libcloud_loadbalancer.get_driver', - 'libcloud_storage.get_driver', - 'log.critical', - 'log.debug', - 'log.error', - 'log.exception', - 'log.info', - 'log.warning', - 'lowpkg.bin_pkg_info', - 'lxc.run_cmd', - 'mantest.install', - 'mantest.search', - 'nspawn.restart', - 'nspawn.stop', - 'pkg.expand_repo_def', - 'pip.iteritems', - 'pip.parse_version', - 'peeringdb.clean_kwargs', - 'runtests_decorators.depends', - 'runtests_decorators.depends_will_fallback', - 'runtests_decorators.missing_depends', - 'runtests_decorators.missing_depends_will_fallback', - 'state.apply', - 'status.list2cmdline', - 'swift.head', - 'test.rand_str', - 'travisci.parse_qs', - 'vsphere.clean_kwargs', - 'vsphere.disconnect', - 'vsphere.get_service_instance_via_proxy', - 'vsphere.gets_service_instance_via_proxy', - 'vsphere.supports_proxies', - 'vsphere.test_vcenter_connection', - 'vsphere.wraps', + "cmd.win_runas", + "cp.recv", + "cp.recv_chunked", + "glance.warn_until", + "ipset.long_range", + "libcloud_compute.get_driver", + "libcloud_dns.get_driver", + "libcloud_loadbalancer.get_driver", + "libcloud_storage.get_driver", + "log.critical", + "log.debug", + "log.error", + "log.exception", + "log.info", + "log.warning", + "lowpkg.bin_pkg_info", + "lxc.run_cmd", + "mantest.install", + "mantest.search", + "nspawn.restart", + "nspawn.stop", + "pkg.expand_repo_def", + "pip.iteritems", + "pip.parse_version", + "peeringdb.clean_kwargs", + "runtests_decorators.depends", + "runtests_decorators.depends_will_fallback", + "runtests_decorators.missing_depends", + "runtests_decorators.missing_depends_will_fallback", + "state.apply", + "status.list2cmdline", + "swift.head", + "test.rand_str", + "travisci.parse_qs", + "vsphere.clean_kwargs", + "vsphere.disconnect", + "vsphere.get_service_instance_via_proxy", + "vsphere.gets_service_instance_via_proxy", + "vsphere.supports_proxies", + "vsphere.test_vcenter_connection", + "vsphere.wraps", ) allow_failure_glob = ( - 'runtests_decorators.*', - 'runtests_helpers.*', - 'vsphere.*', + "runtests_decorators.*", + "runtests_helpers.*", + "vsphere.*", ) nodoc = set() noexample = set() - for fun, docstring in six.iteritems(__salt__['sys.doc']()): + for fun, docstring in six.iteritems(__salt__["sys.doc"]()): if fun in allow_failure: continue else: @@ -121,62 +128,83 @@ def get_invalid_docs(): continue if not isinstance(docstring, six.string_types): nodoc.add(fun) - elif isinstance(docstring, dict) and not re.search(r'([E|e]xample(?:s)?)+(?:.*):?', docstring): + elif isinstance(docstring, dict) and not re.search( + r"([E|e]xample(?:s)?)+(?:.*):?", docstring + ): noexample.add(fun) - return {'missing_docstring': sorted(nodoc), - 'missing_cli_example': sorted(noexample)} + return { + "missing_docstring": sorted(nodoc), + "missing_cli_example": sorted(noexample), + } def modules_available(*names): - ''' + """ Returns a list of modules not available. Empty list if modules are all available - ''' + """ not_found = [] for name in names: - if '.' not in name: - name = name + '.*' + if "." not in name: + name = name + ".*" if not fnmatch.filter(list(__salt__), name): not_found.append(name) return not_found def nonzero_retcode_return_true(): - ''' + """ Sets a nonzero retcode before returning. Designed to test orchestration. - ''' - __context__['retcode'] = 1 + """ + __context__["retcode"] = 1 return True def nonzero_retcode_return_false(): - ''' + """ Sets a nonzero retcode before returning. Designed to test orchestration. - ''' - __context__['retcode'] = 1 + """ + __context__["retcode"] = 1 return False def fail_function(*args, **kwargs): # pylint: disable=unused-argument - ''' + """ Return False no matter what is passed to it - ''' + """ return False def get_python_executable(): - ''' + """ Return the path to the python executable. This is particularly important when running the test suite within a virtualenv, while trying to create virtualenvs on windows. - ''' + """ try: if salt.utils.platform.is_windows(): - python_binary = os.path.join(sys.real_prefix, os.path.basename(sys.executable)) + python_binary = os.path.join( + sys.real_prefix, os.path.basename(sys.executable) + ) else: - python_binary = os.path.join(sys.real_prefix, 'bin', os.path.basename(sys.executable)) + python_binary = os.path.join( + sys.real_prefix, "bin", os.path.basename(sys.executable) + ) + if not os.path.exists(python_binary): + if not python_binary[-1].isdigit(): + versioned_python_binary = "{}{}".format( + python_binary, *sys.version_info + ) + log.info( + "Python binary could not be found at %s. Trying %s", + python_binary, + versioned_python_binary, + ) + if os.path.exists(versioned_python_binary): + python_binary = versioned_python_binary if not os.path.exists(python_binary): + log.warning("Python binary could not be found at %s", python_binary) python_binary = None except AttributeError: # We're not running inside a virtualenv diff --git a/tests/integration/files/file/base/_modules/salttest.py b/tests/integration/files/file/base/_modules/salttest.py index 9f5542f52a0..5cb0b1aab97 100644 --- a/tests/integration/files/file/base/_modules/salttest.py +++ b/tests/integration/files/file/base/_modules/salttest.py @@ -1,19 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" Module for running arbitrary tests -''' +""" # Import Python libs from __future__ import absolute_import def jinja_error(): - ''' + """ CLI Example: .. code-block:: bash salt '*' salttest.jinja_error - ''' - raise Exception('hehehe') + """ + raise Exception("hehehe") diff --git a/tests/integration/files/file/base/_renderers/issue55124.py b/tests/integration/files/file/base/_renderers/issue55124.py new file mode 100644 index 00000000000..ec7c94cf9c5 --- /dev/null +++ b/tests/integration/files/file/base/_renderers/issue55124.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +""" +Renderer to test argline handling in jinja renderer + +See: https://github.com/saltstack/salt/issues/55124 +""" + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import salt libs +import salt.utils.stringio + + +def render(gpg_data, saltenv="base", sls="", argline="", **kwargs): + """ + Renderer which returns the text value of the SLS file, instead of a + StringIO object. + """ + if salt.utils.stringio.is_readable(gpg_data): + return gpg_data.getvalue() + else: + return gpg_data diff --git a/tests/integration/files/file/base/_runners/runtests_helpers.py b/tests/integration/files/file/base/_runners/runtests_helpers.py index df816a92a54..3c4cf3cfdd8 100644 --- a/tests/integration/files/file/base/_runners/runtests_helpers.py +++ b/tests/integration/files/file/base/_runners/runtests_helpers.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Runner functions for integration tests -''' +""" # Import python libs from __future__ import absolute_import def failure(): - __context__['retcode'] = 1 + __context__["retcode"] = 1 return False diff --git a/tests/integration/files/file/base/_states/salttest.py b/tests/integration/files/file/base/_states/salttest.py index b857e6dc88d..766d76a9ced 100644 --- a/tests/integration/files/file/base/_states/salttest.py +++ b/tests/integration/files/file/base/_states/salttest.py @@ -3,10 +3,11 @@ from __future__ import absolute_import -__docformat__ = 'restructuredtext en' +__docformat__ = "restructuredtext en" def hello(name): return "hello " + name + # vim:set et sts=4 ts=4 tw=80: diff --git a/tests/integration/files/file/base/_wheel/runtests_helpers.py b/tests/integration/files/file/base/_wheel/runtests_helpers.py index ec18383cfce..3fc7c47a3fd 100644 --- a/tests/integration/files/file/base/_wheel/runtests_helpers.py +++ b/tests/integration/files/file/base/_wheel/runtests_helpers.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Wheel functions for integration tests -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals def failure(): - __context__['retcode'] = 1 + __context__["retcode"] = 1 return False diff --git a/tests/integration/files/file/base/issue-55124.sls b/tests/integration/files/file/base/issue-55124.sls new file mode 100644 index 00000000000..aadcd84aaa2 --- /dev/null +++ b/tests/integration/files/file/base/issue-55124.sls @@ -0,0 +1,5 @@ +#!issue55124|jinja -s|yaml + +'Who am I?': + cmd.run: + - name: echo {{ salt.cmd.run('whoami') }} diff --git a/tests/integration/files/file/base/mysql/update_query.sql b/tests/integration/files/file/base/mysql/update_query.sql index 34cee2dab14..9a0bd8b65b8 100644 --- a/tests/integration/files/file/base/mysql/update_query.sql +++ b/tests/integration/files/file/base/mysql/update_query.sql @@ -1,3 +1,9 @@ -CREATE TABLE test_update (a INT); -insert into test_update values (1); +/* +multiline +comment +*/ +CREATE TABLE test_update (a INT); # end of line comment +# example comment +insert into test_update values (1); -- ending comment +-- another comment type update test_update set a=2 where a=1; diff --git a/tests/integration/files/file/base/script.py b/tests/integration/files/file/base/script.py index ea1c887150d..0dbde454780 100644 --- a/tests/integration/files/file/base/script.py +++ b/tests/integration/files/file/base/script.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import + import sys -print(' '.join(sys.argv[1:])) +print(" ".join(sys.argv[1:])) diff --git a/tests/integration/files/log_handlers/runtests_log_handler.py b/tests/integration/files/log_handlers/runtests_log_handler.py index da1446ee514..e0ba2e1196f 100644 --- a/tests/integration/files/log_handlers/runtests_log_handler.py +++ b/tests/integration/files/log_handlers/runtests_log_handler.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) :copyright: Copyright 2016 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. @@ -9,44 +9,49 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Salt External Logging Handler -''' +""" # Import python libs from __future__ import absolute_import + import errno -import socket import logging +import socket import threading from multiprocessing import Queue +import salt.log.setup + # Import Salt libs import salt.utils.msgpack from salt.ext import six from salt.utils.platform import is_darwin -import salt.log.setup log = logging.getLogger(__name__) -__virtualname__ = 'runtests_log_handler' +__virtualname__ = "runtests_log_handler" def __virtual__(): - if 'runtests_log_port' not in __opts__: + if "runtests_log_port" not in __opts__: return False, "'runtests_log_port' not in options" if six.PY3: - return False, "runtests external logging handler is temporarily disabled for Python 3 tests" + return ( + False, + "runtests external logging handler is temporarily disabled for Python 3 tests", + ) return True def setup_handlers(): - port = __opts__['runtests_log_port'] + port = __opts__["runtests_log_port"] sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: - sock.connect(('localhost', port)) + sock.connect(("localhost", port)) except socket.error as exc: if exc.errno == errno.ECONNREFUSED: - log.warning('Failed to connect to log server') + log.warning("Failed to connect to log server") return finally: try: @@ -65,7 +70,9 @@ def setup_handlers(): queue_size = 10000000 queue = Queue(queue_size) handler = salt.log.setup.QueueHandler(queue) - level = salt.log.setup.LOG_LEVELS[(__opts__.get('runtests_log_level') or 'error').lower()] + level = salt.log.setup.LOG_LEVELS[ + (__opts__.get("runtests_log_level") or "error").lower() + ] handler.setLevel(level) process_queue_thread = threading.Thread(target=process_queue, args=(port, queue)) process_queue_thread.daemon = True @@ -77,12 +84,12 @@ def process_queue(port, queue): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: - sock.connect(('localhost', port)) + sock.connect(("localhost", port)) except socket.error as exc: if exc.errno == errno.ECONNREFUSED: sock.shutdown(socket.SHUT_RDWR) sock.close() - log.warning('Failed to connect to log server') + log.warning("Failed to connect to log server") return while True: @@ -93,28 +100,19 @@ def process_queue(port, queue): break # Just log everything, filtering will happen on the main process # logging handlers - sock.sendall(salt.utils.msgpack.dumps(record.__dict__, encoding='utf-8')) + sock.sendall(salt.utils.msgpack.dumps(record.__dict__, encoding="utf-8")) except (IOError, EOFError, KeyboardInterrupt, SystemExit): + if hasattr(exc, "errno") and exc.errno != errno.EPIPE: + log.exception(exc) try: sock.shutdown(socket.SHUT_RDWR) sock.close() except (OSError, socket.error): pass break - except socket.error as exc: - if exc.errno == errno.EPIPE: - # Broken pipe - try: - sock.shutdown(socket.SHUT_RDWR) - sock.close() - except (OSError, socket.error): - pass - break - log.exception(exc) except Exception as exc: # pylint: disable=broad-except log.warning( - 'An exception occurred in the pytest salt logging ' - 'queue thread: %s', + "An exception occurred in the pytest salt logging " "queue thread: %s", exc, - exc_info_on_loglevel=logging.DEBUG + exc_info_on_loglevel=logging.DEBUG, ) diff --git a/tests/integration/files/returners/noop_returner.py b/tests/integration/files/returners/noop_returner.py index 2e33af39676..4f09ca9e9cb 100644 --- a/tests/integration/files/returners/noop_returner.py +++ b/tests/integration/files/returners/noop_returner.py @@ -1,22 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" noop_returner ~~~~~~~~~~~~~ A returner that does nothing which is used to test the salt-master `event_return` functionality -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.jid - log = logging.getLogger(__name__) -__virtualname__ = 'runtests_noop' +__virtualname__ = "runtests_noop" def __virtual__(): @@ -24,15 +24,15 @@ def __virtual__(): def event_return(events): - log.debug('NOOP_RETURN.event_return - Events: %s', events) + log.debug("NOOP_RETURN.event_return - Events: %s", events) def returner(ret): - log.debug('NOOP_RETURN.returner - Ret: %s', ret) + log.debug("NOOP_RETURN.returner - Ret: %s", ret) def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - ''' + """ Do any work necessary to prepare a JID, including sending a custom id - ''' + """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/tests/integration/grains/test_core.py b/tests/integration/grains/test_core.py index e449005a3ae..77251386b42 100644 --- a/tests/integration/grains/test_core.py +++ b/tests/integration/grains/test_core.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Test the core grains -''' +""" -# Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest +import salt.loader +import salt.utils.platform from tests.support.case import ModuleCase from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf -# Import Salt libs -import salt.loader -import salt.utils.platform if salt.utils.platform.is_windows(): try: import salt.modules.reg @@ -25,59 +23,52 @@ def _freebsd_or_openbsd(): return salt.utils.platform.is_freebsd() or salt.utils.platform.is_openbsd() +@pytest.mark.windows_whitelisted class TestGrainsCore(ModuleCase): - ''' + """ Test the core grains grains - ''' + """ - @skipIf(not _freebsd_or_openbsd(), 'Only run on FreeBSD or OpenBSD') + @skipIf(not _freebsd_or_openbsd(), "Only run on FreeBSD or OpenBSD") def test_freebsd_openbsd_mem_total(self): - ''' + """ test grains['mem_total'] - ''' - physmem = self.run_function('sysctl.get', ['hw.physmem']) + """ + physmem = self.run_function("sysctl.get", ["hw.physmem"]) self.assertEqual( - self.run_function('grains.items')['mem_total'], - int(physmem) // 1048576 + self.run_function("grains.items")["mem_total"], int(physmem) // 1048576 ) - @skipIf(not salt.utils.platform.is_openbsd(), 'Only run on OpenBSD') + @skipIf(not salt.utils.platform.is_openbsd(), "Only run on OpenBSD") def test_openbsd_swap_total(self): - ''' + """ test grains['swap_total'] - ''' - swapmem = self.run_function('cmd.run', ['swapctl -sk']).split(' ')[1] + """ + swapmem = self.run_function("cmd.run", ["swapctl -sk"]).split(" ")[1] self.assertEqual( - self.run_function('grains.items')['swap_total'], - int(swapmem) // 1048576 + self.run_function("grains.items")["swap_total"], int(swapmem) // 1048576 ) +@pytest.mark.windows_whitelisted class TestGrainsReg(ModuleCase, LoaderModuleMockMixin): - ''' + """ Test the core windows grains - ''' + """ def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() - utils = salt.loader.utils(opts, whitelist=['reg']) - return { - salt.modules.reg: { - '__opts__': opts, - '__utils__': utils, - } - } + utils = salt.loader.utils(opts, whitelist=["reg"]) + return {salt.modules.reg: {"__opts__": opts, "__utils__": utils}} - @skipIf(not salt.utils.platform.is_windows(), 'Only run on Windows') + @skipIf(not salt.utils.platform.is_windows(), "Only run on Windows") def test_win_cpu_model(self): - ''' + """ test grains['cpu_model'] - ''' + """ cpu_model_text = salt.modules.reg.read_value( - 'HKEY_LOCAL_MACHINE', - 'HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0', - 'ProcessorNameString').get('vdata') - self.assertEqual( - self.run_function('grains.items')['cpu_model'], - cpu_model_text - ) + "HKEY_LOCAL_MACHINE", + "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + "ProcessorNameString", + ).get("vdata") + self.assertEqual(self.run_function("grains.items")["cpu_model"], cpu_model_text) diff --git a/tests/integration/grains/test_custom.py b/tests/integration/grains/test_custom.py index f9c534d812f..ea068e439ea 100644 --- a/tests/integration/grains/test_custom.py +++ b/tests/integration/grains/test_custom.py @@ -1,22 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" Test the core grains -''' +""" -# Import python libs from __future__ import absolute_import, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ModuleCase +@pytest.mark.windows_whitelisted class TestGrainsCore(ModuleCase): - ''' + """ Test the core grains grains - ''' + """ def test_grains_passed_to_custom_grain(self): - ''' + """ test if current grains are passed to grains module functions that have a grains argument - ''' - self.assertEqual(self.run_function('grains.get', ['custom_grain_test']), 'itworked') + """ + self.assertEqual( + self.run_function("grains.get", ["custom_grain_test"]), "itworked" + ) diff --git a/tests/integration/loader/test_ext_grains.py b/tests/integration/loader/test_ext_grains.py index 405013042fe..bf886928e7d 100644 --- a/tests/integration/loader/test_ext_grains.py +++ b/tests/integration/loader/test_ext_grains.py @@ -1,75 +1,82 @@ # -*- coding: utf-8 -*- -''' +""" integration.loader.ext_grains ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Test Salt's loader regarding external grains -''' +""" -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import time -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf - -# Import salt libs +import pytest import salt.config import salt.loader +from tests.support.case import ModuleCase +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf +@pytest.mark.windows_whitelisted class LoaderGrainsTest(ModuleCase): - ''' + """ Test the loader standard behavior with external grains - ''' + """ - #def setUp(self): + # def setUp(self): # self.opts = minion_config(None) # self.opts['disable_modules'] = ['pillar'] # self.opts['grains'] = grains(self.opts) def test_grains_overwrite(self): # Force a grains sync - self.run_function('saltutil.sync_grains') + self.run_function("saltutil.sync_grains") # To avoid a race condition on Windows, we need to make sure the # `test_custom_grain2.py` file is present in the _grains directory # before trying to get the grains. This test may execute before the # minion has finished syncing down the files it needs. - module = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache', 'files', - 'base', '_grains', 'test_custom_grain2.py') + module = os.path.join( + RUNTIME_VARS.TMP, + "rootdir", + "cache", + "files", + "base", + "_grains", + "test_custom_grain2.py", + ) tries = 0 while not os.path.exists(module): tries += 1 if tries > 60: break time.sleep(1) - grains = self.run_function('grains.items') + grains = self.run_function("grains.items") # Check that custom grains are overwritten - self.assertEqual({'k2': 'v2'}, grains['a_custom']) + self.assertEqual({"k2": "v2"}, grains["a_custom"]) @skipIf(True, "needs a way to reload minion after config change") +@pytest.mark.windows_whitelisted class LoaderGrainsMergeTest(ModuleCase): - ''' + """ Test the loader deep merge behavior with external grains - ''' + """ def setUp(self): # XXX: This seems like it should become a unit test instead self.opts = salt.config.minion_config(None) - self.opts['grains_deep_merge'] = True - self.assertTrue(self.opts['grains_deep_merge']) - self.opts['disable_modules'] = ['pillar'] + self.opts["grains_deep_merge"] = True + self.assertTrue(self.opts["grains_deep_merge"]) + self.opts["disable_modules"] = ["pillar"] __grains__ = salt.loader.grains(self.opts) def test_grains_merge(self): - __grain__ = self.run_function('grains.item', ['a_custom']) + __grain__ = self.run_function("grains.item", ["a_custom"]) # Check that the grain is present - self.assertIn('a_custom', __grain__) + self.assertIn("a_custom", __grain__) # Check that the grains are merged - self.assertEqual({'k1': 'v1', 'k2': 'v2'}, __grain__['a_custom']) + self.assertEqual({"k1": "v1", "k2": "v2"}, __grain__["a_custom"]) diff --git a/tests/integration/loader/test_ext_modules.py b/tests/integration/loader/test_ext_modules.py index 053666bc2e2..3eb1931a8c0 100644 --- a/tests/integration/loader/test_ext_modules.py +++ b/tests/integration/loader/test_ext_modules.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,30 +7,37 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Test Salt's loader regarding external overrides -''' +""" -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import time -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS +import pytest from tests.support.case import ModuleCase +from tests.support.runtests import RUNTIME_VARS +@pytest.mark.windows_whitelisted class LoaderOverridesTest(ModuleCase): - def setUp(self): - self.run_function('saltutil.sync_modules') + self.run_function("saltutil.sync_modules") def test_overridden_internal(self): # To avoid a race condition on Windows, we need to make sure the # `override_test.py` file is present in the _modules directory before # trying to list all functions. This test may execute before the # minion has finished syncing down the files it needs. - module = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache', 'files', - 'base', '_modules', 'override_test.py') + module = os.path.join( + RUNTIME_VARS.TMP, + "rootdir", + "cache", + "files", + "base", + "_modules", + "override_test.py", + ) tries = 0 while not os.path.exists(module): tries += 1 @@ -38,20 +45,20 @@ class LoaderOverridesTest(ModuleCase): break time.sleep(1) - funcs = self.run_function('sys.list_functions') + funcs = self.run_function("sys.list_functions") # We placed a test module under _modules. # The previous functions should also still exist. - self.assertIn('test.ping', funcs) + self.assertIn("test.ping", funcs) # A non existing function should, of course, not exist - self.assertNotIn('brain.left_hemisphere', funcs) + self.assertNotIn("brain.left_hemisphere", funcs) # There should be a new function for the test module, recho - self.assertIn('test.recho', funcs) + self.assertIn("test.recho", funcs) - text = 'foo bar baz quo qux' + text = "foo bar baz quo qux" self.assertEqual( - self.run_function('test.echo', arg=[text])[::-1], - self.run_function('test.recho', arg=[text]), + self.run_function("test.echo", arg=[text])[::-1], + self.run_function("test.recho", arg=[text]), ) diff --git a/tests/integration/logging/handlers/test_logstash_mod.py b/tests/integration/logging/handlers/test_logstash_mod.py index d73b37ec59d..ceab9aeecb4 100644 --- a/tests/integration/logging/handlers/test_logstash_mod.py +++ b/tests/integration/logging/handlers/test_logstash_mod.py @@ -5,9 +5,9 @@ import errno import logging import socket import time -import zmq import salt.utils.stringutils +import zmq from salt.log.handlers.logstash_mod import DatagramLogstashHandler, ZMQLogstashHander from tests.support.helpers import get_unused_localhost_port from tests.support.unit import TestCase @@ -42,8 +42,10 @@ class DatagramLogstashHandlerTest(TestCase): received_log, addr = self.test_server.recvfrom(12) self.assertEqual(received_log, salt.utils.stringutils.to_bytes(the_log)) except socket.timeout: - self.fail("Log message was not received.\n" - "Check either pickling failed (and message was not send) or some other error occurred") + self.fail( + "Log message was not received.\n" + "Check either pickling failed (and message was not send) or some other error occurred" + ) # At the moment of writing this test the `functional` suite is not yet complete @@ -88,5 +90,8 @@ class ZMQLogstashHanderTest(TestCase): continue raise - self.assertEqual(received_log, salt.utils.stringutils.to_bytes(the_log), - "Check either pickling failed (and message was not send) or some other error occurred") + self.assertEqual( + received_log, + salt.utils.stringutils.to_bytes(the_log), + "Check either pickling failed (and message was not send) or some other error occurred", + ) diff --git a/tests/integration/logging/test_jid_logging.py b/tests/integration/logging/test_jid_logging.py index 5fe6e84f793..b1d91ec4443 100644 --- a/tests/integration/logging/test_jid_logging.py +++ b/tests/integration/logging/test_jid_logging.py @@ -3,33 +3,36 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import TstSuiteLoggingHandler, flaky - import logging + import salt.ext.six as six +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import TstSuiteLoggingHandler, flaky +from tests.support.unit import skipIf -@skipIf(six.PY3, 'Runtest Log Hander Disabled for PY3, #41836') + +@skipIf(six.PY3, "Runtest Log Hander Disabled for PY3, #41836") class LoggingJIDsTest(ModuleCase): - ''' + """ Validate that JIDs appear in LOGs - ''' + """ + def setUp(self): - ''' + """ Set up - ''' - log_format = '[%(levelname)-8s] %(jid)s %(message)s' - self.handler = TstSuiteLoggingHandler(format=log_format, - level=logging.DEBUG) + """ + log_format = "[%(levelname)-8s] %(jid)s %(message)s" + self.handler = TstSuiteLoggingHandler(format=log_format, level=logging.DEBUG) @flaky def test_jid_in_logs(self): - ''' + """ Test JID in log_format - ''' + """ with self.handler: - self.run_function('test.ping') - assert any('JID' in s for s in self.handler.messages) is True, 'JID not found in log messages' + self.run_function("test.ping") + assert ( + any("JID" in s for s in self.handler.messages) is True + ), "JID not found in log messages" diff --git a/tests/integration/master/test_event_return.py b/tests/integration/master/test_event_return.py index 24045ac7b8b..dfaa334c1be 100644 --- a/tests/integration/master/test_event_return.py +++ b/tests/integration/master/test_event_return.py @@ -1,26 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" tests.integration.master.test_event_return ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This test module is meant to cover the issue being fixed by: https://github.com/saltstack/salt/pull/54731 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import time + import logging +import os import shutil import subprocess - -# Import Salt Testing libs -from tests.support.case import TestCase -from tests.support.cli_scripts import ScriptPathMixin -from tests.support.helpers import get_unused_localhost_port -from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.processes import terminate_process +import time # Import 3rd-party libs import pytest @@ -29,29 +23,37 @@ import pytest import salt.ext.six as six from salt.utils.nb_popen import NonBlockingPopen +# Import Salt Testing libs +from tests.support.case import TestCase +from tests.support.cli_scripts import ScriptPathMixin +from tests.support.helpers import get_unused_localhost_port +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.processes import terminate_process + log = logging.getLogger(__name__) class TestEventReturn(AdaptedConfigurationTestCaseMixin, ScriptPathMixin, TestCase): - @classmethod def setUpClass(cls): overrides = { - 'publish_port': get_unused_localhost_port(), - 'ret_port': get_unused_localhost_port(), - 'tcp_master_pub_port': get_unused_localhost_port(), - 'tcp_master_pull_port': get_unused_localhost_port(), - 'tcp_master_publish_pull': get_unused_localhost_port(), - 'tcp_master_workers': get_unused_localhost_port(), - 'runtests_conn_check_port': get_unused_localhost_port(), - 'runtests_log_port': get_unused_localhost_port() + "publish_port": get_unused_localhost_port(), + "ret_port": get_unused_localhost_port(), + "tcp_master_pub_port": get_unused_localhost_port(), + "tcp_master_pull_port": get_unused_localhost_port(), + "tcp_master_publish_pull": get_unused_localhost_port(), + "tcp_master_workers": get_unused_localhost_port(), + "runtests_conn_check_port": get_unused_localhost_port(), + "runtests_log_port": get_unused_localhost_port(), } - overrides['pytest_engine_port'] = overrides['runtests_conn_check_port'] - temp_config = AdaptedConfigurationTestCaseMixin.get_temp_config('master', **overrides) - cls.root_dir = temp_config['root_dir'] - cls.config_dir = os.path.dirname(temp_config['conf_file']) - if temp_config['transport'] == 'tcp': - pytest.skip('Test only applicable to the ZMQ transport') + overrides["pytest_engine_port"] = overrides["runtests_conn_check_port"] + temp_config = AdaptedConfigurationTestCaseMixin.get_temp_config( + "master", **overrides + ) + cls.root_dir = temp_config["root_dir"] + cls.config_dir = os.path.dirname(temp_config["conf_file"]) + if temp_config["transport"] == "tcp": + pytest.skip("Test only applicable to the ZMQ transport") @classmethod def tearDownClass(cls): @@ -60,25 +62,19 @@ class TestEventReturn(AdaptedConfigurationTestCaseMixin, ScriptPathMixin, TestCa def test_master_startup(self): proc = NonBlockingPopen( - [ - self.get_script_path('master'), - '-c', - self.config_dir, - '-l', - 'info' - ], + [self.get_script_path("master"), "-c", self.config_dir, "-l", "info"], stdout=subprocess.PIPE, - stderr=subprocess.STDOUT + stderr=subprocess.STDOUT, ) - out = six.b('') - err = six.b('') + out = six.b("") + err = six.b("") # Testing this should never be longer than 1 minute max_time = time.time() + 60 try: while True: if time.time() > max_time: - assert False, 'Max timeout ocurred' + assert False, "Max timeout ocurred" time.sleep(0.5) _out = proc.recv() _err = proc.recv_err() @@ -87,13 +83,17 @@ class TestEventReturn(AdaptedConfigurationTestCaseMixin, ScriptPathMixin, TestCa if _err: err += _err - if six.b('DeprecationWarning: object() takes no parameters') in out: - self.fail('\'DeprecationWarning: object() takes no parameters\' was seen in output') + if six.b("DeprecationWarning: object() takes no parameters") in out: + self.fail( + "'DeprecationWarning: object() takes no parameters' was seen in output" + ) - if six.b('TypeError: object() takes no parameters') in out: - self.fail('\'TypeError: object() takes no parameters\' was seen in output') + if six.b("TypeError: object() takes no parameters") in out: + self.fail( + "'TypeError: object() takes no parameters' was seen in output" + ) - if six.b('Setting up the master communication server') in out: + if six.b("Setting up the master communication server") in out: # We got past the place we need, stop the process break diff --git a/tests/integration/minion/test_blackout.py b/tests/integration/minion/test_blackout.py index 2a8218cfac0..52530410b51 100644 --- a/tests/integration/minion/test_blackout.py +++ b/tests/integration/minion/test_blackout.py @@ -1,34 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" Tests for minion blackout -''' +""" -# Import Python libs from __future__ import absolute_import -import os -import time -import logging -import textwrap -# Import Salt Testing libs +import logging +import os +import textwrap +import time + +import pytest +import salt.utils.files from tests.support.case import ModuleCase from tests.support.runtests import RUNTIME_VARS -# Import Salt libs -import salt.utils.files - log = logging.getLogger(__name__) +@pytest.mark.windows_whitelisted class MinionBlackoutTestCase(ModuleCase): - ''' + """ Test minion blackout functionality - ''' + """ @classmethod def setUpClass(cls): - cls.top_pillar = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls') - cls.blackout_pillar = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'blackout.sls') + cls.top_pillar = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls") + cls.blackout_pillar = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "blackout.sls") @classmethod def tearDownClass(cls): @@ -40,24 +39,28 @@ class MinionBlackoutTestCase(ModuleCase): del cls.blackout_pillar def setUp(self): - with salt.utils.files.fopen(self.top_pillar, 'w') as wfh: - wfh.write(textwrap.dedent('''\ + with salt.utils.files.fopen(self.top_pillar, "w") as wfh: + wfh.write( + textwrap.dedent( + """\ base: '*': - blackout - ''')) - with salt.utils.files.fopen(self.blackout_pillar, 'w') as wfh: - wfh.write('minion_blackout: False') + """ + ) + ) + with salt.utils.files.fopen(self.blackout_pillar, "w") as wfh: + wfh.write("minion_blackout: False") self.addCleanup(self.cleanup_blackout_pillar) def tearDown(self): self.end_blackout() # Be sure to also refresh the sub_minion pillar - self.run_function('saltutil.refresh_pillar', minion_tgt='sub_minion') + self.run_function("saltutil.refresh_pillar", minion_tgt="sub_minion") timeout = 120 - if not self.wait_for_blackout(end=True, tgt='sub_minion', timeout=timeout): + if not self.wait_for_blackout(end=True, tgt="sub_minion", timeout=timeout): raise Exception( - 'Minion did not exit blackout mode after {} seconds'.format(timeout) + "Minion did not exit blackout mode after {} seconds".format(timeout) ) self.wait_for_all_jobs() @@ -67,102 +70,110 @@ class MinionBlackoutTestCase(ModuleCase): if os.path.exists(self.blackout_pillar): os.unlink(self.blackout_pillar) - def begin_blackout(self, blackout_data='minion_blackout: True'): - ''' + def begin_blackout(self, blackout_data="minion_blackout: True"): + """ setup minion blackout mode - ''' - log.info('Entering minion blackout...') + """ + log.info("Entering minion blackout...") self.wait_for_all_jobs() - with salt.utils.files.fopen(self.blackout_pillar, 'w') as wfh: + with salt.utils.files.fopen(self.blackout_pillar, "w") as wfh: wfh.write(blackout_data) - ret = self.run_function('saltutil.refresh_pillar', timeout=30) + ret = self.run_function("saltutil.refresh_pillar", timeout=30) timeout = 120 if not self.wait_for_blackout(timeout=timeout): raise Exception( - 'Minion did not enter blackout mode after {} seconds'.format(timeout) + "Minion did not enter blackout mode after {} seconds".format(timeout) ) - log.info('Entered minion blackout.') + log.info("Entered minion blackout.") - def wait_for_blackout(self, end=False, tgt='minion', timeout=120, sleep=.3): - ''' + def wait_for_blackout(self, end=False, tgt="minion", timeout=120, sleep=0.3): + """ Wait for blackout mode to start or end. - ''' + """ start = time.time() while time.time() - start <= timeout: ret = self.run_function( - 'pillar.get', minion_tgt=tgt, arg=['minion_blackout'], timeout=30, + "pillar.get", minion_tgt=tgt, arg=["minion_blackout"], timeout=30, ) if end: - if str(ret).find('Minion in blackout mode') == -1: + if str(ret).find("Minion in blackout mode") == -1: return True else: - if str(ret).find('Minion in blackout mode') != -1: + if str(ret).find("Minion in blackout mode") != -1: return True time.sleep(sleep) return False def end_blackout(self): - ''' + """ takedown minion blackout mode - ''' - log.info('Exiting minion blackout...') - with salt.utils.files.fopen(self.blackout_pillar, 'w') as wfh: - wfh.write('minion_blackout: False\n') - self.run_function('saltutil.refresh_pillar') + """ + log.info("Exiting minion blackout...") + with salt.utils.files.fopen(self.blackout_pillar, "w") as wfh: + wfh.write("minion_blackout: False\n") + self.run_function("saltutil.refresh_pillar") timeout = 120 if not self.wait_for_blackout(end=True, timeout=timeout): raise Exception( - 'Minion did not exit blackout mode after {} seconds'.format(timeout) + "Minion did not exit blackout mode after {} seconds".format(timeout) ) self.wait_for_all_jobs() - log.info('Exited minion blackout.') + log.info("Exited minion blackout.") def test_blackout(self): - ''' + """ Test that basic minion blackout functionality works - ''' + """ try: self.begin_blackout() - blackout_ret = self.run_function('test.ping') - self.assertIn('Minion in blackout mode.', blackout_ret) + blackout_ret = self.run_function("test.ping") + self.assertIn("Minion in blackout mode.", blackout_ret) finally: self.end_blackout() - ret = self.run_function('test.ping') + ret = self.run_function("test.ping") self.assertEqual(ret, True) def test_blackout_whitelist(self): - ''' + """ Test that minion blackout whitelist works - ''' - self.begin_blackout(textwrap.dedent('''\ + """ + self.begin_blackout( + textwrap.dedent( + """\ minion_blackout: True minion_blackout_whitelist: - test.ping - test.fib - ''')) + """ + ) + ) - ping_ret = self.run_function('test.ping') + ping_ret = self.run_function("test.ping") self.assertEqual(ping_ret, True) - fib_ret = self.run_function('test.fib', [7]) + fib_ret = self.run_function("test.fib", [7]) self.assertTrue(isinstance(fib_ret, list)) self.assertEqual(fib_ret[0], 13) def test_blackout_nonwhitelist(self): - ''' + """ Test that minion refuses to run non-whitelisted functions during blackout whitelist - ''' - self.begin_blackout(textwrap.dedent('''\ + """ + self.begin_blackout( + textwrap.dedent( + """\ minion_blackout: True minion_blackout_whitelist: - test.ping - test.fib - ''')) + """ + ) + ) - state_ret = self.run_function('state.apply') - self.assertIn('Minion in blackout mode.', state_ret) + state_ret = self.run_function("state.apply") + self.assertIn("Minion in blackout mode.", state_ret) - cloud_ret = self.run_function('cloud.query', ['list_nodes_full']) - self.assertIn('Minion in blackout mode.', cloud_ret) + cloud_ret = self.run_function("cloud.query", ["list_nodes_full"]) + self.assertIn("Minion in blackout mode.", cloud_ret) diff --git a/tests/integration/minion/test_executor.py b/tests/integration/minion/test_executor.py index 93e9c57ffdb..80485381d2a 100644 --- a/tests/integration/minion/test_executor.py +++ b/tests/integration/minion/test_executor.py @@ -12,13 +12,12 @@ log = logging.getLogger(__name__) class ExecutorTest(ModuleCase, ShellCase): - def setup(self): - self.run_function('saltutil.sync_all') + self.run_function("saltutil.sync_all") def test_executor(self): - ''' + """ test that dunders are set - ''' - data = self.run_call('test.arg --module-executors=arg') - self.assertIn('test.arg fired', "".join(data)) + """ + data = self.run_call("test.arg --module-executors=arg") + self.assertIn("test.arg fired", "".join(data)) diff --git a/tests/integration/minion/test_pillar.py b/tests/integration/minion/test_pillar.py index 6e362397d36..db106805822 100644 --- a/tests/integration/minion/test_pillar.py +++ b/tests/integration/minion/test_pillar.py @@ -1,36 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Erik Johnson <erik@saltstack.com> -''' +""" -# Import Python libs from __future__ import absolute_import + import copy import errno import logging import os import shutil -import textwrap import subprocess +import textwrap -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import requires_system_grains, dedent -from tests.support.runtests import RUNTIME_VARS - -# Import salt libs +import pytest +import salt.pillar as pillar import salt.utils.files import salt.utils.path import salt.utils.stringutils import salt.utils.yaml -import salt.pillar as pillar +from tests.support.case import ModuleCase +from tests.support.helpers import dedent, requires_system_grains +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf log = logging.getLogger(__name__) -TEST_KEY = '''\ +TEST_KEY = """\ -----BEGIN PGP PRIVATE KEY BLOCK----- lQOYBFiKrcYBCADAj92+fz20uKxxH0ffMwcryGG9IogkiUi2QrNYilB4hwrY5Qt7 @@ -88,9 +85,9 @@ DvDAstzLZ9dIcmr+OmcRQykKOKXlhEl3HnR5CyuPrA8hdVup4oeVwdkJhfJFKLLb jc0cCHsEqJNZ2AB+1uEl3tcH0tyAFJa33F0znSonP17SS1Ff9sgHYBVLUg== =06Tz -----END PGP PRIVATE KEY BLOCK----- -''' +""" -GPG_PILLAR_YAML = '''\ +GPG_PILLAR_YAML = """\ secrets: vault: foo: | @@ -135,96 +132,97 @@ secrets: 4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0= =LrCQ -----END PGP MESSAGE----- -''' +""" GPG_PILLAR_ENCRYPTED = { - 'secrets': { - 'vault': { - 'foo': '-----BEGIN PGP MESSAGE-----\n' - '\n' - 'hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th\n' - 'W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74\n' - 'ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7\n' - '+KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb\n' - 'VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73\n' - 'zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06\n' - 'KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh\n' - 'iFndxegN9w==\n' - '=bAuo\n' - '-----END PGP MESSAGE-----\n', - 'bar': 'this was unencrypted already', - 'baz': '-----BEGIN PGP MESSAGE-----\n' - '\n' - 'hQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz\n' - 'gLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf\n' - '9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7\n' - 'Izwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2\n' - 'q+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V\n' - 'kJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl\n' - 'JkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY\n' - '1OZi\n' - '=7epf\n' - '-----END PGP MESSAGE-----\n', - 'qux': [ - 'foo', - 'bar', - '-----BEGIN PGP MESSAGE-----\n' - '\n' - 'hQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS\n' - 'ksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI\n' - 'gR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA\n' - 'YrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF\n' - 'f7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE\n' - 'uZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd\n' - '4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=\n' - '=LrCQ\n' - '-----END PGP MESSAGE-----\n' + "secrets": { + "vault": { + "foo": "-----BEGIN PGP MESSAGE-----\n" + "\n" + "hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th\n" + "W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74\n" + "ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7\n" + "+KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb\n" + "VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73\n" + "zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06\n" + "KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh\n" + "iFndxegN9w==\n" + "=bAuo\n" + "-----END PGP MESSAGE-----\n", + "bar": "this was unencrypted already", + "baz": "-----BEGIN PGP MESSAGE-----\n" + "\n" + "hQEMAw2B674HRhwSAQf+Ne+IfsP2IcPDrUWct8sTJrga47jQvlPCmO+7zJjOVcqz\n" + "gLjUKvMajrbI/jorBWxyAbF+5E7WdG9WHHVnuoywsyTB9rbmzuPqYCJCe+ZVyqWf\n" + "9qgJ+oUjcvYIFmH3h7H68ldqbxaAUkAOQbTRHdr253wwaTIC91ZeX0SCj64HfTg7\n" + "Izwk383CRWonEktXJpientApQFSUWNeLUWagEr/YPNFA3vzpPF5/Ia9X8/z/6oO2\n" + "q+D5W5mVsns3i2HHbg2A8Y+pm4TWnH6mTSh/gdxPqssi9qIrzGQ6H1tEoFFOEq1V\n" + "kJBe0izlfudqMq62XswzuRB4CYT5Iqw1c97T+1RqENJCASG0Wz8AGhinTdlU5iQl\n" + "JkLKqBxcBz4L70LYWyHhYwYROJWjHgKAywX5T67ftq0wi8APuZl9olnOkwSK+wrY\n" + "1OZi\n" + "=7epf\n" + "-----END PGP MESSAGE-----\n", + "qux": [ + "foo", + "bar", + "-----BEGIN PGP MESSAGE-----\n" + "\n" + "hQEMAw2B674HRhwSAQgAg1YCmokrweoOI1c9HO0BLamWBaFPTMblOaTo0WJLZoTS\n" + "ksbQ3OJAMkrkn3BnnM/djJc5C7vNs86ZfSJ+pvE8Sp1Rhtuxh25EKMqGOn/SBedI\n" + "gR6N5vGUNiIpG5Tf3DuYAMNFDUqw8uY0MyDJI+ZW3o3xrMUABzTH0ew+Piz85FDA\n" + "YrVgwZfqyL+9OQuu6T66jOIdwQNRX2NPFZqvon8liZUPus5VzD8E5cAL9OPxQ3sF\n" + "f7/zE91YIXUTimrv3L7eCgU1dSxKhhfvA2bEUi+AskMWFXFuETYVrIhFJAKnkFmE\n" + "uZx+O9R9hADW3hM5hWHKH9/CRtb0/cC84I9oCWIQPdI+AaPtICxtsD2N8Q98hhhd\n" + "4M7I0sLZhV+4ZJqzpUsOnSpaGyfh1Zy/1d3ijJi99/l+uVHuvmMllsNmgR+ZTj0=\n" + "=LrCQ\n" + "-----END PGP MESSAGE-----\n", ], }, }, } GPG_PILLAR_DECRYPTED = { - 'secrets': { - 'vault': { - 'foo': 'supersecret', - 'bar': 'this was unencrypted already', - 'baz': 'rosebud', - 'qux': ['foo', 'bar', 'baz'], + "secrets": { + "vault": { + "foo": "supersecret", + "bar": "this was unencrypted already", + "baz": "rosebud", + "qux": ["foo", "bar", "baz"], }, }, } class _CommonBase(ModuleCase): - @classmethod def setUpClass(cls): - cls.pillar_base = os.path.join(RUNTIME_VARS.TMP, 'test-decrypt-pillar', 'pillar') - cls.top_sls = os.path.join(cls.pillar_base, 'top.sls') - cls.gpg_sls = os.path.join(cls.pillar_base, 'gpg.sls') + cls.pillar_base = os.path.join( + RUNTIME_VARS.TMP, "test-decrypt-pillar", "pillar" + ) + cls.top_sls = os.path.join(cls.pillar_base, "top.sls") + cls.gpg_sls = os.path.join(cls.pillar_base, "gpg.sls") cls.default_opts = { - 'cachedir': os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache'), - 'optimization_order': [0, 1, 2], - 'extension_modules': os.path.join(RUNTIME_VARS.TMP, - 'test-decrypt-pillar', - 'extmods'), - 'pillar_roots': {'base': [cls.pillar_base]}, - 'ext_pillar_first': False, - 'ext_pillar': [], - 'decrypt_pillar_default': 'gpg', - 'decrypt_pillar_delimiter': ':', - 'decrypt_pillar_renderers': ['gpg'], + "cachedir": os.path.join(RUNTIME_VARS.TMP, "rootdir", "cache"), + "optimization_order": [0, 1, 2], + "extension_modules": os.path.join( + RUNTIME_VARS.TMP, "test-decrypt-pillar", "extmods" + ), + "pillar_roots": {"base": [cls.pillar_base]}, + "ext_pillar_first": False, + "ext_pillar": [], + "decrypt_pillar_default": "gpg", + "decrypt_pillar_delimiter": ":", + "decrypt_pillar_renderers": ["gpg"], } cls.additional_opts = ( - 'conf_file', - 'file_roots', - 'state_top', - 'renderer', - 'renderer_whitelist', - 'renderer_blacklist', + "conf_file", + "file_roots", + "state_top", + "renderer", + "renderer_whitelist", + "renderer_blacklist", ) - cls.gpg_homedir = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'gpgkeys') + cls.gpg_homedir = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "gpgkeys") def _build_opts(self, opts): ret = copy.deepcopy(self.default_opts) @@ -234,66 +232,82 @@ class _CommonBase(ModuleCase): return ret +@pytest.mark.windows_whitelisted class BasePillarTest(_CommonBase): - ''' + """ Tests for pillar decryption - ''' + """ + @classmethod def setUpClass(cls): super(BasePillarTest, cls).setUpClass() os.makedirs(cls.pillar_base) - with salt.utils.files.fopen(cls.top_sls, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + with salt.utils.files.fopen(cls.top_sls, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ base: 'N@mins not L@minion': - ng1 'N@missing_minion': - ng2 - ''')) + """ + ) + ) - with salt.utils.files.fopen(os.path.join(cls.pillar_base, 'ng1.sls'), 'w') as fp_: - fp_.write('pillar_from_nodegroup: True') + with salt.utils.files.fopen( + os.path.join(cls.pillar_base, "ng1.sls"), "w" + ) as fp_: + fp_.write("pillar_from_nodegroup: True") - with salt.utils.files.fopen(os.path.join(cls.pillar_base, 'ng2.sls'), 'w') as fp_: - fp_.write('pillar_from_nodegroup_with_ghost: True') + with salt.utils.files.fopen( + os.path.join(cls.pillar_base, "ng2.sls"), "w" + ) as fp_: + fp_.write("pillar_from_nodegroup_with_ghost: True") @classmethod def tearDownClass(cls): shutil.rmtree(cls.pillar_base) def test_pillar_top_compound_match(self, grains=None): - ''' + """ Test that a compound match topfile that refers to a nodegroup via N@ works as expected. - ''' + """ if not grains: grains = {} - grains['os'] = 'Fedora' - nodegroup_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + grains["os"] = "Fedora" + nodegroup_opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ nodegroups: min: minion sub_min: sub_minion mins: N@min or N@sub_min missing_minion: L@minion,ghostminion - ''')) + """ + ) + ) opts = self._build_opts(nodegroup_opts) - pillar_obj = pillar.Pillar(opts, grains, 'minion', 'base') + pillar_obj = pillar.Pillar(opts, grains, "minion", "base") ret = pillar_obj.compile_pillar() - self.assertEqual(ret.get('pillar_from_nodegroup_with_ghost'), True) - self.assertEqual(ret.get('pillar_from_nodegroup'), None) + self.assertEqual(ret.get("pillar_from_nodegroup_with_ghost"), True) + self.assertEqual(ret.get("pillar_from_nodegroup"), None) - sub_pillar_obj = pillar.Pillar(opts, grains, 'sub_minion', 'base') + sub_pillar_obj = pillar.Pillar(opts, grains, "sub_minion", "base") sub_ret = sub_pillar_obj.compile_pillar() - self.assertEqual(sub_ret.get('pillar_from_nodegroup_with_ghost'), None) - self.assertEqual(sub_ret.get('pillar_from_nodegroup'), True) + self.assertEqual(sub_ret.get("pillar_from_nodegroup_with_ghost"), None) + self.assertEqual(sub_ret.get("pillar_from_nodegroup"), True) -@skipIf(not salt.utils.path.which('gpg'), 'GPG is not installed') +@skipIf(not salt.utils.path.which("gpg"), "GPG is not installed") +@pytest.mark.windows_whitelisted class DecryptGPGPillarTest(_CommonBase): - ''' + """ Tests for pillar decryption - ''' + """ + maxDiff = None @classmethod @@ -306,48 +320,55 @@ class DecryptGPGPillarTest(_CommonBase): raise else: cls.created_gpg_homedir = True - cmd_prefix = ['gpg', '--homedir', cls.gpg_homedir] + cmd_prefix = ["gpg", "--homedir", cls.gpg_homedir] - cmd = cmd_prefix + ['--list-keys'] - log.debug('Instantiating gpg keyring using: %s', cmd) - output = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - shell=False).communicate()[0] - log.debug('Result:\n%s', output) + cmd = cmd_prefix + ["--list-keys"] + log.debug("Instantiating gpg keyring using: %s", cmd) + output = subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False + ).communicate()[0] + log.debug("Result:\n%s", output) - cmd = cmd_prefix + ['--import', '--allow-secret-key-import'] - log.debug('Importing keypair using: %s', cmd) - output = subprocess.Popen(cmd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - shell=False).communicate(input=salt.utils.stringutils.to_bytes(TEST_KEY))[0] - log.debug('Result:\n%s', output) + cmd = cmd_prefix + ["--import", "--allow-secret-key-import"] + log.debug("Importing keypair using: %s", cmd) + output = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=False, + ).communicate(input=salt.utils.stringutils.to_bytes(TEST_KEY))[0] + log.debug("Result:\n%s", output) os.makedirs(cls.pillar_base) - with salt.utils.files.fopen(cls.top_sls, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + with salt.utils.files.fopen(cls.top_sls, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ base: '*': - gpg - ''')) - with salt.utils.files.fopen(cls.gpg_sls, 'w') as fp_: + """ + ) + ) + with salt.utils.files.fopen(cls.gpg_sls, "w") as fp_: fp_.write(GPG_PILLAR_YAML) @classmethod def tearDownClass(cls): - cmd = ['gpg-connect-agent', '--homedir', cls.gpg_homedir] + cmd = ["gpg-connect-agent", "--homedir", cls.gpg_homedir] try: - log.debug('Killing gpg-agent using: %s', cmd) - output = subprocess.Popen(cmd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - shell=False).communicate(input=b'KILLAGENT')[0] - log.debug('Result:\n%s', output) + log.debug("Killing gpg-agent using: %s", cmd) + output = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=False, + ).communicate(input=b"KILLAGENT")[0] + log.debug("Result:\n%s", output) except OSError: - log.debug('No need to kill: old gnupg doesn\'t start the agent.') + log.debug("No need to kill: old gnupg doesn't start the agent.") if cls.created_gpg_homedir: try: @@ -360,140 +381,174 @@ class DecryptGPGPillarTest(_CommonBase): @requires_system_grains def test_decrypt_pillar_default_renderer(self, grains=None): - ''' + """ Test recursive decryption of secrets:vault as well as the fallback to default decryption renderer. - ''' - decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + decrypt_pillar_opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ decrypt_pillar: - 'secrets:vault' - ''')) + """ + ) + ) opts = self._build_opts(decrypt_pillar_opts) - pillar_obj = pillar.Pillar(opts, grains, 'test', 'base') + pillar_obj = pillar.Pillar(opts, grains, "test", "base") ret = pillar_obj.compile_pillar() self.assertEqual(ret, GPG_PILLAR_DECRYPTED) @requires_system_grains def test_decrypt_pillar_alternate_delimiter(self, grains=None): - ''' + """ Test recursive decryption of secrets:vault using a pipe instead of a colon as the nesting delimiter. - ''' - decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + decrypt_pillar_opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ decrypt_pillar_delimiter: '|' decrypt_pillar: - 'secrets|vault' - ''')) + """ + ) + ) opts = self._build_opts(decrypt_pillar_opts) - pillar_obj = pillar.Pillar(opts, grains, 'test', 'base') + pillar_obj = pillar.Pillar(opts, grains, "test", "base") ret = pillar_obj.compile_pillar() self.assertEqual(ret, GPG_PILLAR_DECRYPTED) @requires_system_grains def test_decrypt_pillar_deeper_nesting(self, grains=None): - ''' + """ Test recursive decryption, only with a more deeply-nested target. This should leave the other keys in secrets:vault encrypted. - ''' - decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + decrypt_pillar_opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ decrypt_pillar: - 'secrets:vault:qux' - ''')) + """ + ) + ) opts = self._build_opts(decrypt_pillar_opts) - pillar_obj = pillar.Pillar(opts, grains, 'test', 'base') + pillar_obj = pillar.Pillar(opts, grains, "test", "base") ret = pillar_obj.compile_pillar() expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED) - expected['secrets']['vault']['qux'][-1] = \ - GPG_PILLAR_DECRYPTED['secrets']['vault']['qux'][-1] + expected["secrets"]["vault"]["qux"][-1] = GPG_PILLAR_DECRYPTED["secrets"][ + "vault" + ]["qux"][-1] self.assertEqual(ret, expected) @requires_system_grains def test_decrypt_pillar_explicit_renderer(self, grains=None): - ''' + """ Test recursive decryption of secrets:vault, with the renderer explicitly defined, overriding the default. Setting the default to a nonexistant renderer so we can be sure that the override happened. - ''' - decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + decrypt_pillar_opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ decrypt_pillar_default: asdf decrypt_pillar_renderers: - asdf - gpg decrypt_pillar: - 'secrets:vault': gpg - ''')) + """ + ) + ) opts = self._build_opts(decrypt_pillar_opts) - pillar_obj = pillar.Pillar(opts, grains, 'test', 'base') + pillar_obj = pillar.Pillar(opts, grains, "test", "base") ret = pillar_obj.compile_pillar() self.assertEqual(ret, GPG_PILLAR_DECRYPTED) @requires_system_grains def test_decrypt_pillar_missing_renderer(self, grains=None): - ''' + """ Test decryption using a missing renderer. It should fail, leaving the encrypted keys intact, and add an error to the pillar dictionary. - ''' - decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + decrypt_pillar_opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ decrypt_pillar_default: asdf decrypt_pillar_renderers: - asdf decrypt_pillar: - 'secrets:vault' - ''')) + """ + ) + ) opts = self._build_opts(decrypt_pillar_opts) - pillar_obj = pillar.Pillar(opts, grains, 'test', 'base') + pillar_obj = pillar.Pillar(opts, grains, "test", "base") ret = pillar_obj.compile_pillar() expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED) - expected['_errors'] = [ - 'Failed to decrypt pillar key \'secrets:vault\': Decryption ' - 'renderer \'asdf\' is not available' + expected["_errors"] = [ + "Failed to decrypt pillar key 'secrets:vault': Decryption " + "renderer 'asdf' is not available" ] - self.assertEqual(ret['_errors'], expected['_errors']) - self.assertEqual(ret['secrets']['vault']['foo'], - expected['secrets']['vault']['foo']) - self.assertEqual(ret['secrets']['vault']['bar'], - expected['secrets']['vault']['bar']) - self.assertEqual(ret['secrets']['vault']['baz'], - expected['secrets']['vault']['baz']) - self.assertEqual(ret['secrets']['vault']['qux'], - expected['secrets']['vault']['qux']) + self.assertEqual(ret["_errors"], expected["_errors"]) + self.assertEqual( + ret["secrets"]["vault"]["foo"], expected["secrets"]["vault"]["foo"] + ) + self.assertEqual( + ret["secrets"]["vault"]["bar"], expected["secrets"]["vault"]["bar"] + ) + self.assertEqual( + ret["secrets"]["vault"]["baz"], expected["secrets"]["vault"]["baz"] + ) + self.assertEqual( + ret["secrets"]["vault"]["qux"], expected["secrets"]["vault"]["qux"] + ) @requires_system_grains def test_decrypt_pillar_invalid_renderer(self, grains=None): - ''' + """ Test decryption using a renderer which is not permitted. It should fail, leaving the encrypted keys intact, and add an error to the pillar dictionary. - ''' - decrypt_pillar_opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + decrypt_pillar_opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ decrypt_pillar_default: foo decrypt_pillar_renderers: - foo - bar decrypt_pillar: - 'secrets:vault': gpg - ''')) + """ + ) + ) opts = self._build_opts(decrypt_pillar_opts) - pillar_obj = pillar.Pillar(opts, grains, 'test', 'base') + pillar_obj = pillar.Pillar(opts, grains, "test", "base") ret = pillar_obj.compile_pillar() expected = copy.deepcopy(GPG_PILLAR_ENCRYPTED) - expected['_errors'] = [ - 'Failed to decrypt pillar key \'secrets:vault\': \'gpg\' is ' - 'not a valid decryption renderer. Valid choices are: foo, bar' + expected["_errors"] = [ + "Failed to decrypt pillar key 'secrets:vault': 'gpg' is " + "not a valid decryption renderer. Valid choices are: foo, bar" ] - self.assertEqual(ret['_errors'], expected['_errors']) - self.assertEqual(ret['secrets']['vault']['foo'], - expected['secrets']['vault']['foo']) - self.assertEqual(ret['secrets']['vault']['bar'], - expected['secrets']['vault']['bar']) - self.assertEqual(ret['secrets']['vault']['baz'], - expected['secrets']['vault']['baz']) - self.assertEqual(ret['secrets']['vault']['qux'], - expected['secrets']['vault']['qux']) + self.assertEqual(ret["_errors"], expected["_errors"]) + self.assertEqual( + ret["secrets"]["vault"]["foo"], expected["secrets"]["vault"]["foo"] + ) + self.assertEqual( + ret["secrets"]["vault"]["bar"], expected["secrets"]["vault"]["bar"] + ) + self.assertEqual( + ret["secrets"]["vault"]["baz"], expected["secrets"]["vault"]["baz"] + ) + self.assertEqual( + ret["secrets"]["vault"]["qux"], expected["secrets"]["vault"]["qux"] + ) +@pytest.mark.windows_whitelisted class RefreshPillarTest(ModuleCase): - ''' + """ These tests validate the behavior defined in the documentation: https://docs.saltstack.com/en/latest/topics/pillar/#in-memory-pillar-data-vs-on-demand-pillar-data @@ -501,158 +556,168 @@ class RefreshPillarTest(ModuleCase): These tests also serve as a regression test for: https://github.com/saltstack/salt/issues/54941 - ''' + """ def cleanup_pillars(self, top_path, pillar_path): os.remove(top_path) os.remove(pillar_path) - self.run_function('saltutil.refresh_pillar', arg=(True,)) + self.run_function("saltutil.refresh_pillar", arg=(True,)) def create_pillar(self, key): - ''' + """ Utility method to create a pillar for the minion and a value of true, this method also removes and cleans up the pillar at the end of the test. - ''' - top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls') - pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'test_pillar.sls') - with salt.utils.files.fopen(top_path, 'w') as fd: - fd.write(dedent(''' + """ + top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls") + pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "test_pillar.sls") + with salt.utils.files.fopen(top_path, "w") as fd: + fd.write( + dedent( + """ base: 'minion': - test_pillar - ''')) - with salt.utils.files.fopen(pillar_path, 'w') as fd: - fd.write(dedent(''' + """ + ) + ) + with salt.utils.files.fopen(pillar_path, "w") as fd: + fd.write( + dedent( + """ {}: true - '''.format(key))) + """.format( + key + ) + ) + ) self.addCleanup(self.cleanup_pillars, top_path, pillar_path) def test_pillar_refresh_pillar_raw(self): - ''' + """ Validate the minion's pillar.raw call behavior for new pillars - ''' - key = 'issue-54941-raw' + """ + key = "issue-54941-raw" # We do not expect to see the pillar beacuse it does not exist yet - val = self.run_function('pillar.raw', arg=(key,)) + val = self.run_function("pillar.raw", arg=(key,)) assert val == {} self.create_pillar(key) # The pillar exists now but raw reads it from in-memory pillars - val = self.run_function('pillar.raw', arg=(key,)) + val = self.run_function("pillar.raw", arg=(key,)) assert val == {} # Calling refresh_pillar to update in-memory pillars - ret = self.run_function('saltutil.refresh_pillar', arg=(True,)) + ret = self.run_function("saltutil.refresh_pillar", arg=(True,)) # The pillar can now be read from in-memory pillars - val = self.run_function('pillar.raw', arg=(key,)) + val = self.run_function("pillar.raw", arg=(key,)) assert val is True, repr(val) def test_pillar_refresh_pillar_get(self): - ''' + """ Validate the minion's pillar.get call behavior for new pillars - ''' - key = 'issue-54941-get' + """ + key = "issue-54941-get" # We do not expect to see the pillar beacuse it does not exist yet - val = self.run_function('pillar.get', arg=(key,)) - assert val == '' - top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls') - pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'test_pillar.sls') + val = self.run_function("pillar.get", arg=(key,)) + assert val == "" + top_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls") + pillar_path = os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "test_pillar.sls") self.create_pillar(key) # The pillar exists now but get reads it from in-memory pillars, no # refresh happens - val = self.run_function('pillar.get', arg=(key,)) - assert val == '' + val = self.run_function("pillar.get", arg=(key,)) + assert val == "" # Calling refresh_pillar to update in-memory pillars - ret = self.run_function('saltutil.refresh_pillar', arg=(True,)) + ret = self.run_function("saltutil.refresh_pillar", arg=(True,)) assert ret is True # The pillar can now be read from in-memory pillars - val = self.run_function('pillar.get', arg=(key,)) + val = self.run_function("pillar.get", arg=(key,)) assert val is True, repr(val) def test_pillar_refresh_pillar_item(self): - ''' + """ Validate the minion's pillar.item call behavior for new pillars - ''' - key = 'issue-54941-item' + """ + key = "issue-54941-item" # We do not expect to see the pillar beacuse it does not exist yet - val = self.run_function('pillar.item', arg=(key,)) + val = self.run_function("pillar.item", arg=(key,)) assert key in val - assert val[key] == '' + assert val[key] == "" self.create_pillar(key) # The pillar exists now but get reads it from in-memory pillars, no # refresh happens - val = self.run_function('pillar.item', arg=(key,)) + val = self.run_function("pillar.item", arg=(key,)) assert key in val - assert val[key] == '' + assert val[key] == "" # Calling refresh_pillar to update in-memory pillars - ret = self.run_function('saltutil.refresh_pillar', arg=(True,)) + ret = self.run_function("saltutil.refresh_pillar", arg=(True,)) assert ret is True # The pillar can now be read from in-memory pillars - val = self.run_function('pillar.item', arg=(key,)) + val = self.run_function("pillar.item", arg=(key,)) assert key in val assert val[key] is True def test_pillar_refresh_pillar_items(self): - ''' + """ Validate the minion's pillar.item call behavior for new pillars - ''' - key = 'issue-54941-items' + """ + key = "issue-54941-items" # We do not expect to see the pillar beacuse it does not exist yet - val = self.run_function('pillar.items') + val = self.run_function("pillar.items") assert key not in val self.create_pillar(key) # A pillar.items call sees the pillar right away because a # refresh_pillar event is fired. - val = self.run_function('pillar.items') + val = self.run_function("pillar.items") assert key in val assert val[key] is True def test_pillar_refresh_pillar_ping(self): - ''' + """ Validate the minion's test.ping does not update pillars See: https://github.com/saltstack/salt/issues/54941 - ''' - key = 'issue-54941-ping' + """ + key = "issue-54941-ping" # We do not expect to see the pillar beacuse it does not exist yet - val = self.run_function('pillar.item', arg=(key,)) + val = self.run_function("pillar.item", arg=(key,)) assert key in val - assert val[key] == '' + assert val[key] == "" self.create_pillar(key) - val = self.run_function('test.ping') + val = self.run_function("test.ping") assert val is True # The pillar exists now but get reads it from in-memory pillars, no # refresh happens - val = self.run_function('pillar.item', arg=(key,)) + val = self.run_function("pillar.item", arg=(key,)) assert key in val - assert val[key] == '' + assert val[key] == "" # Calling refresh_pillar to update in-memory pillars - ret = self.run_function('saltutil.refresh_pillar', arg=(True,)) + ret = self.run_function("saltutil.refresh_pillar", arg=(True,)) assert ret is True # The pillar can now be read from in-memory pillars - val = self.run_function('pillar.item', arg=(key,)) + val = self.run_function("pillar.item", arg=(key,)) assert key in val assert val[key] is True diff --git a/tests/integration/minion/test_timeout.py b/tests/integration/minion/test_timeout.py index 8cc3b0fb496..a9edcbada4d 100644 --- a/tests/integration/minion/test_timeout.py +++ b/tests/integration/minion/test_timeout.py @@ -1,42 +1,48 @@ # -*- coding: utf-8 -*- -''' +""" Tests for various minion timeouts -''' +""" -# Import Python libs from __future__ import absolute_import + import os import sys +import pytest import salt.utils.platform - -# Import Salt Testing libs from tests.support.case import ShellCase +@pytest.mark.windows_whitelisted class MinionTimeoutTestCase(ShellCase): - ''' + """ Test minion timing functions - ''' + """ + def test_long_running_job(self): - ''' + """ Test that we will wait longer than the job timeout for a minion to return. - ''' + """ # Launch the command sleep_length = 30 if salt.utils.platform.is_windows(): - popen_kwargs = {'env': dict(os.environ, PYTHONPATH=';'.join(sys.path))} + popen_kwargs = {"env": dict(os.environ, PYTHONPATH=";".join(sys.path))} else: popen_kwargs = None ret = self.run_salt( - 'minion test.sleep {0}'.format(sleep_length), + "minion test.sleep {0}".format(sleep_length), timeout=90, catch_stderr=True, popen_kwargs=popen_kwargs, ) - self.assertTrue(isinstance(ret[0], list), 'Return is not a list. Minion' - ' may have returned error: {0}'.format(ret)) - self.assertEqual(len(ret[0]), 2, 'Standard out wrong length {}'.format(ret)) - self.assertTrue('True' in ret[0][1], 'Minion did not return True after ' - '{0} seconds. ret={1}'.format(sleep_length, ret)) + self.assertTrue( + isinstance(ret[0], list), + "Return is not a list. Minion" " may have returned error: {0}".format(ret), + ) + self.assertEqual(len(ret[0]), 2, "Standard out wrong length {}".format(ret)) + self.assertTrue( + "True" in ret[0][1], + "Minion did not return True after " + "{0} seconds. ret={1}".format(sleep_length, ret), + ) diff --git a/tests/integration/modules/test_aliases.py b/tests/integration/modules/test_aliases.py index a9fa9929e6b..80abf3ebf6e 100644 --- a/tests/integration/modules/test_aliases.py +++ b/tests/integration/modules/test_aliases.py @@ -1,72 +1,52 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ModuleCase +@pytest.mark.windows_whitelisted class AliasesTest(ModuleCase): - ''' + """ Validate aliases module - ''' + """ + def test_set_target(self): - ''' + """ aliases.set_target and aliases.get_target - ''' - set_ret = self.run_function( - 'aliases.set_target', - alias='fred', - target='bob') + """ + set_ret = self.run_function("aliases.set_target", alias="fred", target="bob") self.assertTrue(set_ret) - tgt_ret = self.run_function( - 'aliases.get_target', - alias='fred') - self.assertEqual(tgt_ret, 'bob') + tgt_ret = self.run_function("aliases.get_target", alias="fred") + self.assertEqual(tgt_ret, "bob") def test_has_target(self): - ''' + """ aliases.set_target and aliases.has_target - ''' - set_ret = self.run_function( - 'aliases.set_target', - alias='fred', - target='bob') + """ + set_ret = self.run_function("aliases.set_target", alias="fred", target="bob") self.assertTrue(set_ret) - tgt_ret = self.run_function( - 'aliases.has_target', - alias='fred', - target='bob') + tgt_ret = self.run_function("aliases.has_target", alias="fred", target="bob") self.assertTrue(tgt_ret) def test_list_aliases(self): - ''' + """ aliases.list_aliases - ''' - set_ret = self.run_function( - 'aliases.set_target', - alias='fred', - target='bob') + """ + set_ret = self.run_function("aliases.set_target", alias="fred", target="bob") self.assertTrue(set_ret) - tgt_ret = self.run_function( - 'aliases.list_aliases') + tgt_ret = self.run_function("aliases.list_aliases") self.assertIsInstance(tgt_ret, dict) - self.assertIn('fred', tgt_ret) + self.assertIn("fred", tgt_ret) def test_rm_alias(self): - ''' + """ aliases.rm_alias - ''' - set_ret = self.run_function( - 'aliases.set_target', - alias='frank', - target='greg') + """ + set_ret = self.run_function("aliases.set_target", alias="frank", target="greg") self.assertTrue(set_ret) - self.run_function( - 'aliases.rm_alias', - alias='frank') - tgt_ret = self.run_function( - 'aliases.list_aliases') + self.run_function("aliases.rm_alias", alias="frank") + tgt_ret = self.run_function("aliases.list_aliases") self.assertIsInstance(tgt_ret, dict) - self.assertNotIn('alias=frank', tgt_ret) + self.assertNotIn("alias=frank", tgt_ret) diff --git a/tests/integration/modules/test_archive.py b/tests/integration/modules/test_archive.py index 56f603ea432..2bbc783159a 100644 --- a/tests/integration/modules/test_archive.py +++ b/tests/integration/modules/test_archive.py @@ -1,59 +1,65 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the archive state -''' -# Import python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import textwrap -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest - -# Import salt libs +import pytest import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils - -# Import 3rd party libs from salt.ext import six +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + try: - import zipfile # pylint: disable=W0611 + import zipfile # pylint: disable=unused-import + HAS_ZIPFILE = True except ImportError: HAS_ZIPFILE = False @destructiveTest +@pytest.mark.windows_whitelisted class ArchiveTest(ModuleCase): - ''' + """ Validate the archive module - ''' - # Base path used for test artifacts - base_path = os.path.join(RUNTIME_VARS.TMP, 'modules', 'archive') + """ + + @classmethod + def setUpClass(cls): + # Base path used for test artifacts + cls.base_path = os.path.join(RUNTIME_VARS.TMP, "modules", "archive") + + @classmethod + def tearDownClass(cls): + cls.base_path = None def _set_artifact_paths(self, arch_fmt): - ''' + """ Define the paths for the source, archive, and destination files :param str arch_fmt: The archive format used in the test - ''' - self.src = os.path.join(self.base_path, '{0}_src_dir'.format(arch_fmt)) - self.src_file = os.path.join(self.src, 'file') - self.arch = os.path.join(self.base_path, 'archive.{0}'.format(arch_fmt)) - self.dst = os.path.join(self.base_path, '{0}_dst_dir'.format(arch_fmt)) + """ + self.src = os.path.join(self.base_path, "{0}_src_dir".format(arch_fmt)) + self.src_file = os.path.join(self.src, "file") + self.arch = os.path.join(self.base_path, "archive.{0}".format(arch_fmt)) + self.dst = os.path.join(self.base_path, "{0}_dst_dir".format(arch_fmt)) def _set_up(self, arch_fmt, unicode_filename=False): - ''' + """ Create source file tree and destination directory :param str arch_fmt: The archive format used in the test - ''' + """ self._set_artifact_paths(arch_fmt) # Remove the artifacts if any present @@ -64,15 +70,18 @@ class ArchiveTest(ModuleCase): # Create source os.makedirs(self.src) if unicode_filename: - filename = 'file®' + filename = "file®" else: - filename = 'file' - with salt.utils.files.fopen(os.path.join(self.src, filename), 'wb') as theorem: + filename = "file" + with salt.utils.files.fopen(os.path.join(self.src, filename), "wb") as theorem: if six.PY3 and salt.utils.platform.is_windows(): - encoding = 'utf-8' + encoding = "utf-8" else: encoding = None - theorem.write(salt.utils.stringutils.to_bytes(textwrap.dedent('''\ + theorem.write( + salt.utils.stringutils.to_bytes( + textwrap.dedent( + """\ Compression theorem of computational complexity theory: Given a Gödel numbering $φ$ of the computable functions and a @@ -89,15 +98,19 @@ class ArchiveTest(ModuleCase): and $\\mathrm C(φ_i) ⊊ \\mathrm{C}(φ_{f(i)})$. - '''), encoding=encoding)) + """ + ), + encoding=encoding, + ) + ) # Create destination os.makedirs(self.dst) def _tear_down(self): - ''' + """ Remove source file tree, archive, and destination file tree - ''' + """ for f in (self.src, self.arch, self.dst): if os.path.exists(f): if os.path.isdir(f): @@ -110,29 +123,30 @@ class ArchiveTest(ModuleCase): del self.src_file def _assert_artifacts_in_ret(self, ret, file_only=False, unix_sep=False): - ''' + """ Assert that the artifact source files are printed in the source command output - ''' + """ def normdir(path): normdir = os.path.normcase(os.path.abspath(path)) if salt.utils.platform.is_windows(): # Remove the drive portion of path - if len(normdir) >= 2 and normdir[1] == ':': - normdir = normdir.split(':', 1)[1] + if len(normdir) >= 2 and normdir[1] == ":": + normdir = normdir.split(":", 1)[1] normdir = normdir.lstrip(os.path.sep) # Unzipped paths might have unix line endings if unix_sep: - normdir = normdir.replace(os.path.sep, '/') + normdir = normdir.replace(os.path.sep, "/") return normdir # Try to find source directory and file in output lines dir_in_ret = None file_in_ret = None for line in ret: - if normdir(self.src) in os.path.normcase(line) \ - and not normdir(self.src_file) in os.path.normcase(line): + if normdir(self.src) in os.path.normcase(line) and not normdir( + self.src_file + ) in os.path.normcase(line): dir_in_ret = True if normdir(self.src_file) in os.path.normcase(line): file_in_ret = True @@ -143,193 +157,193 @@ class ArchiveTest(ModuleCase): self.assertTrue(dir_in_ret) self.assertTrue(file_in_ret) - @skipIf(not salt.utils.path.which('tar'), 'Cannot find tar executable') + @skipIf(not salt.utils.path.which("tar"), "Cannot find tar executable") def test_tar_pack(self): - ''' + """ Validate using the tar function to create archives - ''' - self._set_up(arch_fmt='tar') + """ + self._set_up(arch_fmt="tar") # Test create archive - ret = self.run_function('archive.tar', ['-cvf', self.arch], sources=self.src) + ret = self.run_function("archive.tar", ["-cvf", self.arch], sources=self.src) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) self._tear_down() - @skipIf(not salt.utils.path.which('tar'), 'Cannot find tar executable') + @skipIf(not salt.utils.path.which("tar"), "Cannot find tar executable") def test_tar_unpack(self): - ''' + """ Validate using the tar function to extract archives - ''' - self._set_up(arch_fmt='tar') - self.run_function('archive.tar', ['-cvf', self.arch], sources=self.src) + """ + self._set_up(arch_fmt="tar") + self.run_function("archive.tar", ["-cvf", self.arch], sources=self.src) # Test extract archive - ret = self.run_function('archive.tar', ['-xvf', self.arch], dest=self.dst) + ret = self.run_function("archive.tar", ["-xvf", self.arch], dest=self.dst) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) self._tear_down() - @skipIf(not salt.utils.path.which('tar'), 'Cannot find tar executable') + @skipIf(not salt.utils.path.which("tar"), "Cannot find tar executable") def test_tar_pack_unicode(self): - ''' + """ Validate using the tar function to create archives - ''' - self._set_up(arch_fmt='tar', unicode_filename=True) + """ + self._set_up(arch_fmt="tar", unicode_filename=True) # Test create archive - ret = self.run_function('archive.tar', ['-cvf', self.arch], sources=self.src) + ret = self.run_function("archive.tar", ["-cvf", self.arch], sources=self.src) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) self._tear_down() - @skipIf(not salt.utils.path.which('tar'), 'Cannot find tar executable') + @skipIf(not salt.utils.path.which("tar"), "Cannot find tar executable") def test_tar_unpack_unicode(self): - ''' + """ Validate using the tar function to extract archives - ''' - self._set_up(arch_fmt='tar', unicode_filename=True) - self.run_function('archive.tar', ['-cvf', self.arch], sources=self.src) + """ + self._set_up(arch_fmt="tar", unicode_filename=True) + self.run_function("archive.tar", ["-cvf", self.arch], sources=self.src) # Test extract archive - ret = self.run_function('archive.tar', ['-xvf', self.arch], dest=self.dst) + ret = self.run_function("archive.tar", ["-xvf", self.arch], dest=self.dst) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) self._tear_down() - @skipIf(not salt.utils.path.which('tar'), 'Cannot find tar executable') + @skipIf(not salt.utils.path.which("tar"), "Cannot find tar executable") def test_tar_list_unicode(self): - ''' + """ Validate using the tar function to extract archives - ''' - self._set_up(arch_fmt='tar', unicode_filename=True) - self.run_function('archive.tar', ['-cvf', self.arch], sources=self.src) + """ + self._set_up(arch_fmt="tar", unicode_filename=True) + self.run_function("archive.tar", ["-cvf", self.arch], sources=self.src) # Test list archive - ret = self.run_function('archive.list', name=self.arch) + ret = self.run_function("archive.list", name=self.arch) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) self._tear_down() - @skipIf(not salt.utils.path.which('gzip'), 'Cannot find gzip executable') + @skipIf(not salt.utils.path.which("gzip"), "Cannot find gzip executable") def test_gzip(self): - ''' + """ Validate using the gzip function - ''' - self._set_up(arch_fmt='gz') + """ + self._set_up(arch_fmt="gz") # Test create archive - ret = self.run_function('archive.gzip', [self.src_file], options='-v') + ret = self.run_function("archive.gzip", [self.src_file], options="-v") self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret, file_only=True) self._tear_down() - @skipIf(not salt.utils.path.which('gzip'), 'Cannot find gzip executable') - @skipIf(not salt.utils.path.which('gunzip'), 'Cannot find gunzip executable') + @skipIf(not salt.utils.path.which("gzip"), "Cannot find gzip executable") + @skipIf(not salt.utils.path.which("gunzip"), "Cannot find gunzip executable") def test_gunzip(self): - ''' + """ Validate using the gunzip function - ''' - self._set_up(arch_fmt='gz') - self.run_function('archive.gzip', [self.src_file], options='-v') + """ + self._set_up(arch_fmt="gz") + self.run_function("archive.gzip", [self.src_file], options="-v") # Test extract archive - ret = self.run_function('archive.gunzip', [self.src_file + '.gz'], options='-v') + ret = self.run_function("archive.gunzip", [self.src_file + ".gz"], options="-v") self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret, file_only=True) self._tear_down() - @skipIf(not salt.utils.path.which('zip'), 'Cannot find zip executable') + @skipIf(not salt.utils.path.which("zip"), "Cannot find zip executable") def test_cmd_zip(self): - ''' + """ Validate using the cmd_zip function - ''' - self._set_up(arch_fmt='zip') + """ + self._set_up(arch_fmt="zip") # Test create archive - ret = self.run_function('archive.cmd_zip', [self.arch, self.src]) + ret = self.run_function("archive.cmd_zip", [self.arch, self.src]) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) self._tear_down() - @skipIf(not salt.utils.path.which('zip'), 'Cannot find zip executable') - @skipIf(not salt.utils.path.which('unzip'), 'Cannot find unzip executable') + @skipIf(not salt.utils.path.which("zip"), "Cannot find zip executable") + @skipIf(not salt.utils.path.which("unzip"), "Cannot find unzip executable") def test_cmd_unzip(self): - ''' + """ Validate using the cmd_unzip function - ''' - self._set_up(arch_fmt='zip') - self.run_function('archive.cmd_zip', [self.arch, self.src]) + """ + self._set_up(arch_fmt="zip") + self.run_function("archive.cmd_zip", [self.arch, self.src]) # Test create archive - ret = self.run_function('archive.cmd_unzip', [self.arch, self.dst]) + ret = self.run_function("archive.cmd_unzip", [self.arch, self.dst]) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) self._tear_down() - @skipIf(not HAS_ZIPFILE, 'Cannot find zipfile python module') + @skipIf(not HAS_ZIPFILE, "Cannot find zipfile python module") def test_zip(self): - ''' + """ Validate using the zip function - ''' - self._set_up(arch_fmt='zip') + """ + self._set_up(arch_fmt="zip") # Test create archive - ret = self.run_function('archive.zip', [self.arch, self.src]) + ret = self.run_function("archive.zip", [self.arch, self.src]) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) self._tear_down() - @skipIf(not HAS_ZIPFILE, 'Cannot find zipfile python module') + @skipIf(not HAS_ZIPFILE, "Cannot find zipfile python module") def test_unzip(self): - ''' + """ Validate using the unzip function - ''' - self._set_up(arch_fmt='zip') - self.run_function('archive.zip', [self.arch, self.src]) + """ + self._set_up(arch_fmt="zip") + self.run_function("archive.zip", [self.arch, self.src]) # Test create archive - ret = self.run_function('archive.unzip', [self.arch, self.dst]) + ret = self.run_function("archive.unzip", [self.arch, self.dst]) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret, unix_sep=False) self._tear_down() - @skipIf(not salt.utils.path.which('rar'), 'Cannot find rar executable') + @skipIf(not salt.utils.path.which("rar"), "Cannot find rar executable") def test_rar(self): - ''' + """ Validate using the rar function - ''' - self._set_up(arch_fmt='rar') + """ + self._set_up(arch_fmt="rar") # Test create archive - ret = self.run_function('archive.rar', [self.arch, self.src]) + ret = self.run_function("archive.rar", [self.arch, self.src]) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) self._tear_down() - @skipIf(not salt.utils.path.which('rar'), 'Cannot find rar executable') - @skipIf(not salt.utils.path.which('unrar'), 'Cannot find unrar executable') + @skipIf(not salt.utils.path.which("rar"), "Cannot find rar executable") + @skipIf(not salt.utils.path.which("unrar"), "Cannot find unrar executable") def test_unrar(self): - ''' + """ Validate using the unrar function - ''' - self._set_up(arch_fmt='rar') - self.run_function('archive.rar', [self.arch, self.src]) + """ + self._set_up(arch_fmt="rar") + self.run_function("archive.rar", [self.arch, self.src]) # Test create archive - ret = self.run_function('archive.unrar', [self.arch, self.dst]) + ret = self.run_function("archive.unrar", [self.arch, self.dst]) self.assertTrue(isinstance(ret, list), six.text_type(ret)) self._assert_artifacts_in_ret(ret) diff --git a/tests/integration/modules/test_beacons.py b/tests/integration/modules/test_beacons.py index ae15133a37e..cec13cc8132 100644 --- a/tests/integration/modules/test_beacons.py +++ b/tests/integration/modules/test_beacons.py @@ -1,306 +1,339 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Justin Anderson <janderson@saltstack.com> -''' +""" -# Python Libs from __future__ import absolute_import, print_function, unicode_literals + import os -# Salt Libs +import pytest from salt.exceptions import CommandExecutionError - -# Salttesting libs -from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf +@pytest.mark.windows_whitelisted class BeaconsAddDeleteTest(ModuleCase): - ''' + """ Tests the add and delete functions - ''' + """ + def setUp(self): self.minion_conf_d_dir = os.path.join( - RUNTIME_VARS.TMP_CONF_DIR, - os.path.dirname(self.minion_opts['default_include'])) + RUNTIME_VARS.TMP_CONF_DIR, + os.path.dirname(self.minion_opts["default_include"]), + ) if not os.path.isdir(self.minion_conf_d_dir): os.makedirs(self.minion_conf_d_dir) - self.beacons_config_file_path = os.path.join(self.minion_conf_d_dir, 'beacons.conf') + self.beacons_config_file_path = os.path.join( + self.minion_conf_d_dir, "beacons.conf" + ) def tearDown(self): if os.path.isfile(self.beacons_config_file_path): os.unlink(self.beacons_config_file_path) # Reset beacons - self.run_function('beacons.reset', f_timeout=300) + self.run_function("beacons.reset", f_timeout=300) def test_add_and_delete(self): - ''' + """ Test adding and deleting a beacon - ''' + """ _add = self.run_function( - 'beacons.add', - ['ps', [{'processes': {'apache2': 'stopped'}}]], - f_timeout=300 + "beacons.add", + ["ps", [{"processes": {"apache2": "stopped"}}]], + f_timeout=300, ) - self.assertTrue(_add['result']) + self.assertTrue(_add["result"]) # save added beacon - _save = self.run_function('beacons.save', f_timeout=300) - self.assertTrue(_save['result']) + _save = self.run_function("beacons.save", f_timeout=300) + self.assertTrue(_save["result"]) # delete the beacon - _delete = self.run_function('beacons.delete', ['ps'], f_timeout=300) - self.assertTrue(_delete['result']) + _delete = self.run_function("beacons.delete", ["ps"], f_timeout=300) + self.assertTrue(_delete["result"]) # save the results - self.run_function('beacons.save', f_timeout=300) + self.run_function("beacons.save", f_timeout=300) def test_add_and_delete_beacon_module(self): - ''' + """ Test adding and deleting a beacon - ''' - _add = self.run_function('beacons.add', ['watch_apache', [{'processes': {'apache2': 'stopped'}}, {'beacon_module': 'ps'}]]) - self.assertTrue(_add['result']) + """ + _add = self.run_function( + "beacons.add", + [ + "watch_apache", + [{"processes": {"apache2": "stopped"}}, {"beacon_module": "ps"}], + ], + ) + self.assertTrue(_add["result"]) # save added beacon - _save = self.run_function('beacons.save') - self.assertTrue(_save['result']) + _save = self.run_function("beacons.save") + self.assertTrue(_save["result"]) # delete the beacon - _delete = self.run_function('beacons.delete', ['watch_apache']) - self.assertTrue(_delete['result']) + _delete = self.run_function("beacons.delete", ["watch_apache"]) + self.assertTrue(_delete["result"]) # save the results - self.run_function('beacons.save') + self.run_function("beacons.save") +@pytest.mark.windows_whitelisted class BeaconsTest(ModuleCase): - ''' + """ Tests the beacons execution module - ''' + """ + beacons_config_file_path = minion_conf_d_dir = None @classmethod def tearDownClass(cls): - if cls.beacons_config_file_path and os.path.isfile(cls.beacons_config_file_path): + if cls.beacons_config_file_path and os.path.isfile( + cls.beacons_config_file_path + ): os.unlink(cls.beacons_config_file_path) def setUp(self): if self.minion_conf_d_dir is None: self.minion_conf_d_dir = os.path.join( - RUNTIME_VARS.TMP_CONF_DIR, - os.path.dirname(self.minion_opts['default_include'])) + RUNTIME_VARS.TMP_CONF_DIR, + os.path.dirname(self.minion_opts["default_include"]), + ) if not os.path.isdir(self.minion_conf_d_dir): os.makedirs(self.minion_conf_d_dir) - self.__class__.beacons_config_file_path = os.path.join(self.minion_conf_d_dir, 'beacons.conf') + self.__class__.beacons_config_file_path = os.path.join( + self.minion_conf_d_dir, "beacons.conf" + ) try: # Add beacon to disable - self.run_function('beacons.add', - ['ps', [{'processes': {'apache2': 'stopped'}}]], - f_timeout=300) - self.run_function('beacons.save', f_timeout=300) + self.run_function( + "beacons.add", + ["ps", [{"processes": {"apache2": "stopped"}}]], + f_timeout=300, + ) + self.run_function("beacons.save", f_timeout=300) except CommandExecutionError: - self.skipTest('Unable to add beacon') + self.skipTest("Unable to add beacon") def tearDown(self): # delete added beacon - self.run_function('beacons.delete', ['ps'], f_timeout=300) - self.run_function('beacons.save', f_timeout=300) + self.run_function("beacons.delete", ["ps"], f_timeout=300) + self.run_function("beacons.save", f_timeout=300) # Reset beacons - self.run_function('beacons.reset', f_timeout=300) + self.run_function("beacons.reset", f_timeout=300) def test_disable(self): - ''' + """ Test disabling beacons - ''' + """ # assert beacon exists - _list = self.run_function('beacons.list', - return_yaml=False, - f_timeout=300) - self.assertIn('ps', _list) + _list = self.run_function("beacons.list", return_yaml=False, f_timeout=300) + self.assertIn("ps", _list) - ret = self.run_function('beacons.disable', f_timeout=300) - self.assertTrue(ret['result']) + ret = self.run_function("beacons.disable", f_timeout=300) + self.assertTrue(ret["result"]) # assert beacons are disabled - _list = self.run_function('beacons.list', - return_yaml=False, - f_timeout=300) - self.assertFalse(_list['enabled']) + _list = self.run_function("beacons.list", return_yaml=False, f_timeout=300) + self.assertFalse(_list["enabled"]) # disable added beacon - ret = self.run_function('beacons.disable_beacon', ['ps'], f_timeout=300) - self.assertTrue(ret['result']) + ret = self.run_function("beacons.disable_beacon", ["ps"], f_timeout=300) + self.assertTrue(ret["result"]) # assert beacon ps is disabled - _list = self.run_function('beacons.list', - return_yaml=False, - f_timeout=300) - for bdict in _list['ps']: - if 'enabled' in bdict: - self.assertFalse(bdict['enabled']) + _list = self.run_function("beacons.list", return_yaml=False, f_timeout=300) + for bdict in _list["ps"]: + if "enabled" in bdict: + self.assertFalse(bdict["enabled"]) break def test_enable(self): - ''' + """ Test enabling beacons - ''' + """ # assert beacon exists - _list = self.run_function('beacons.list', - return_yaml=False, - f_timeout=300) - self.assertIn('ps', _list) + _list = self.run_function("beacons.list", return_yaml=False, f_timeout=300) + self.assertIn("ps", _list) # enable beacons on minion - ret = self.run_function('beacons.enable', f_timeout=300) - self.assertTrue(ret['result']) + ret = self.run_function("beacons.enable", f_timeout=300) + self.assertTrue(ret["result"]) # assert beacons are enabled - _list = self.run_function('beacons.list', - return_yaml=False, - f_timeout=300) - self.assertTrue(_list['enabled']) + _list = self.run_function("beacons.list", return_yaml=False, f_timeout=300) + self.assertTrue(_list["enabled"]) - @skipIf(True, 'Skip until https://github.com/saltstack/salt/issues/31516 ' - 'problems are resolved.') + @skipIf( + True, + "Skip until https://github.com/saltstack/salt/issues/31516 " + "problems are resolved.", + ) def test_enabled_beacons(self): - ''' + """ Test enabled specific beacon - ''' + """ # enable added beacon - ret = self.run_function('beacons.enable_beacon', ['ps'], f_timeout=300) - self.assertTrue(ret['result']) + ret = self.run_function("beacons.enable_beacon", ["ps"], f_timeout=300) + self.assertTrue(ret["result"]) # assert beacon ps is enabled - _list = self.run_function('beacons.list', - return_yaml=False, - f_timeout=300) - self.assertTrue(_list['ps']['enabled']) + _list = self.run_function("beacons.list", return_yaml=False, f_timeout=300) + self.assertTrue(_list["ps"]["enabled"]) def test_list(self): - ''' + """ Test listing the beacons - ''' + """ # list beacons - ret = self.run_function('beacons.list', - return_yaml=False, - f_timeout=300) - if 'enabled' in ret: - self.assertEqual(ret, {'ps': [{'processes': {'apache2': 'stopped'}}], 'enabled': True}) + ret = self.run_function("beacons.list", return_yaml=False, f_timeout=300) + if "enabled" in ret: + self.assertEqual( + ret, {"ps": [{"processes": {"apache2": "stopped"}}], "enabled": True} + ) else: - self.assertEqual(ret, {'ps': [{'processes': {'apache2': 'stopped'}}]}) + self.assertEqual(ret, {"ps": [{"processes": {"apache2": "stopped"}}]}) def test_list_available(self): - ''' + """ Test listing the beacons - ''' + """ # list beacons - ret = self.run_function('beacons.list_available', - return_yaml=False, - f_timeout=300) + ret = self.run_function( + "beacons.list_available", return_yaml=False, f_timeout=300 + ) self.assertTrue(ret) class BeaconsWithBeaconTypeTest(ModuleCase): - ''' + """ Tests the beacons execution module - ''' + """ + beacons_config_file_path = minion_conf_d_dir = None @classmethod def tearDownClass(cls): - if cls.beacons_config_file_path and os.path.isfile(cls.beacons_config_file_path): + if cls.beacons_config_file_path and os.path.isfile( + cls.beacons_config_file_path + ): os.unlink(cls.beacons_config_file_path) def setUp(self): if self.minion_conf_d_dir is None: self.minion_conf_d_dir = os.path.join( - RUNTIME_VARS.TMP_CONF_DIR, - os.path.dirname(self.minion_opts['default_include'])) + RUNTIME_VARS.TMP_CONF_DIR, + os.path.dirname(self.minion_opts["default_include"]), + ) if not os.path.isdir(self.minion_conf_d_dir): os.makedirs(self.minion_conf_d_dir) - self.__class__.beacons_config_file_path = os.path.join(self.minion_conf_d_dir, 'beacons.conf') + self.__class__.beacons_config_file_path = os.path.join( + self.minion_conf_d_dir, "beacons.conf" + ) try: # Add beacon to disable - self.run_function('beacons.add', ['watch_apache', [{'processes': {'apache2': 'stopped'}}, {'beacon_module': 'ps'}]]) - self.run_function('beacons.save') + self.run_function( + "beacons.add", + [ + "watch_apache", + [{"processes": {"apache2": "stopped"}}, {"beacon_module": "ps"}], + ], + ) + self.run_function("beacons.save") except CommandExecutionError: - self.skipTest('Unable to add beacon') + self.skipTest("Unable to add beacon") def tearDown(self): # delete added beacon - self.run_function('beacons.delete', ['watch_apache']) - self.run_function('beacons.save') + self.run_function("beacons.delete", ["watch_apache"]) + self.run_function("beacons.save") def test_disable(self): - ''' + """ Test disabling beacons - ''' + """ # assert beacon exists - _list = self.run_function('beacons.list', return_yaml=False) - self.assertIn('watch_apache', _list) + _list = self.run_function("beacons.list", return_yaml=False) + self.assertIn("watch_apache", _list) - ret = self.run_function('beacons.disable') - self.assertTrue(ret['result']) + ret = self.run_function("beacons.disable") + self.assertTrue(ret["result"]) # assert beacons are disabled - _list = self.run_function('beacons.list', return_yaml=False) - self.assertFalse(_list['enabled']) + _list = self.run_function("beacons.list", return_yaml=False) + self.assertFalse(_list["enabled"]) # disable added beacon - ret = self.run_function('beacons.disable_beacon', ['watch_apache']) - self.assertTrue(ret['result']) + ret = self.run_function("beacons.disable_beacon", ["watch_apache"]) + self.assertTrue(ret["result"]) # assert beacon ps is disabled - _list = self.run_function('beacons.list', return_yaml=False) - for bdict in _list['watch_apache']: - if 'enabled' in bdict: - self.assertFalse(bdict['enabled']) + _list = self.run_function("beacons.list", return_yaml=False) + for bdict in _list["watch_apache"]: + if "enabled" in bdict: + self.assertFalse(bdict["enabled"]) break def test_enable(self): - ''' + """ Test enabling beacons - ''' + """ # assert beacon exists - _list = self.run_function('beacons.list', return_yaml=False) - self.assertIn('watch_apache', _list) + _list = self.run_function("beacons.list", return_yaml=False) + self.assertIn("watch_apache", _list) # enable beacons on minion - ret = self.run_function('beacons.enable') - self.assertTrue(ret['result']) + ret = self.run_function("beacons.enable") + self.assertTrue(ret["result"]) # assert beacons are enabled - _list = self.run_function('beacons.list', return_yaml=False) - self.assertTrue(_list['enabled']) + _list = self.run_function("beacons.list", return_yaml=False) + self.assertTrue(_list["enabled"]) - @skipIf(True, 'Skip until https://github.com/saltstack/salt/issues/31516 problems are resolved.') + @skipIf( + True, + "Skip until https://github.com/saltstack/salt/issues/31516 problems are resolved.", + ) def test_enabled_beacons(self): - ''' + """ Test enabled specific beacon - ''' + """ # enable added beacon - ret = self.run_function('beacons.enable_beacon', ['watch_apache']) - self.assertTrue(ret['result']) + ret = self.run_function("beacons.enable_beacon", ["watch_apache"]) + self.assertTrue(ret["result"]) # assert beacon ps is enabled - _list = self.run_function('beacons.list', return_yaml=False) - self.assertTrue(_list['watch_apache']['enabled']) + _list = self.run_function("beacons.list", return_yaml=False) + self.assertTrue(_list["watch_apache"]["enabled"]) def test_list(self): - ''' + """ Test lising the beacons - ''' + """ # list beacons - ret = self.run_function('beacons.list', return_yaml=False) - _expected = {'watch_apache': [{'processes': {'apache2': 'stopped'}}, - {'beacon_module': 'ps'}]} - _enabled_expected = {'watch_apache': [{'processes': {'apache2': 'stopped'}}, {'beacon_module': 'ps'}], - 'enabled': True} - if 'enabled' in ret: + ret = self.run_function("beacons.list", return_yaml=False) + _expected = { + "watch_apache": [ + {"processes": {"apache2": "stopped"}}, + {"beacon_module": "ps"}, + ] + } + _enabled_expected = { + "watch_apache": [ + {"processes": {"apache2": "stopped"}}, + {"beacon_module": "ps"}, + ], + "enabled": True, + } + if "enabled" in ret: self.assertEqual(ret, _enabled_expected) else: self.assertEqual(ret, _expected) diff --git a/tests/integration/modules/test_boto_iam.py b/tests/integration/modules/test_boto_iam.py index 842bbfff543..340aab4e335 100644 --- a/tests/integration/modules/test_boto_iam.py +++ b/tests/integration/modules/test_boto_iam.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Validate the boto_iam module -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -13,6 +13,7 @@ from tests.support.unit import skipIf # Import 3rd-party libs try: import boto + NO_BOTO_MODULE = False except ImportError: NO_BOTO_MODULE = True @@ -20,18 +21,19 @@ except ImportError: @skipIf( NO_BOTO_MODULE, - 'Please install the boto library before running boto integration tests.' + "Please install the boto library before running boto integration tests.", ) class BotoIAMTest(ModuleCase): - def setUp(self): try: boto.connect_iam() except boto.exception.NoAuthHandlerFound: - self.skipTest('Please setup boto AWS credentials before running boto integration tests.') + self.skipTest( + "Please setup boto AWS credentials before running boto integration tests." + ) def test_get_account_id(self): - ret = self.run_function('boto_iam.get_account_id') + ret = self.run_function("boto_iam.get_account_id") # The AWS account ID is a 12-digit number. # http://docs.aws.amazon.com/general/latest/gr/acct-identifiers.html - self.assertRegex(ret, r'^\d{12}$') + self.assertRegex(ret, r"^\d{12}$") diff --git a/tests/integration/modules/test_boto_sns.py b/tests/integration/modules/test_boto_sns.py index 0899c3547a0..4a997c9d906 100644 --- a/tests/integration/modules/test_boto_sns.py +++ b/tests/integration/modules/test_boto_sns.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Validate the boto_sns module -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import re # Import Salt Testing libs @@ -14,6 +15,7 @@ from tests.support.unit import skipIf # Import 3rd-party libs try: import boto + NO_BOTO_MODULE = False except ImportError: NO_BOTO_MODULE = True @@ -21,64 +23,65 @@ except ImportError: @skipIf( NO_BOTO_MODULE, - 'Please install the boto library before running boto integration tests.' + "Please install the boto library before running boto integration tests.", ) class BotoSNSTest(ModuleCase): - def setUp(self): try: boto.connect_iam() except boto.exception.NoAuthHandlerFound: - self.skipTest('Please setup boto AWS credentials before running boto integration tests.') + self.skipTest( + "Please setup boto AWS credentials before running boto integration tests." + ) # The name of the topic you want to create. # Constraints: Topic names must be made up of only uppercase and # lowercase ASCII letters, numbers, underscores, and hyphens, # and must be between 1 and 256 characters long. # http://docs.aws.amazon.com/sns/latest/api/API_CreateTopic.html - self.topic_name = re.sub(r'[^a-zA-Z_-]', '_', self.id())[0:256] + self.topic_name = re.sub(r"[^a-zA-Z_-]", "_", self.id())[0:256] self.topic_names = [self.topic_name] - self.run_function('boto_sns.delete', name=self.topic_name) + self.run_function("boto_sns.delete", name=self.topic_name) def tearDown(self): for topic in self.topic_names: - self.run_function('boto_sns.delete', name=topic) + self.run_function("boto_sns.delete", name=topic) def test_exists_non_existing(self): - ret = self.run_function('boto_sns.exists', ['nonexistent']) + ret = self.run_function("boto_sns.exists", ["nonexistent"]) self.assertSaltModuleFalseReturn(ret) def test_exists_existing(self): - self.run_function('boto_sns.create', [self.topic_name]) - ret = self.run_function('boto_sns.exists', [self.topic_name]) + self.run_function("boto_sns.create", [self.topic_name]) + ret = self.run_function("boto_sns.exists", [self.topic_name]) self.assertSaltModuleTrueReturn(ret) def test_create(self): - ret = self.run_function('boto_sns.create', [self.topic_name]) + ret = self.run_function("boto_sns.create", [self.topic_name]) self.assertSaltModuleTrueReturn(ret) - ret = self.run_function('boto_sns.get_all_topics') + ret = self.run_function("boto_sns.get_all_topics") self.assertIn(self.topic_name, list(ret.keys())) self.assertIn(self._get_arn(self.topic_name), list(ret.values())) def test_delete_non_existing(self): - ret = self.run_function('boto_sns.delete', [self.topic_name]) + ret = self.run_function("boto_sns.delete", [self.topic_name]) self.assertSaltModuleTrueReturn(ret) def test_delete_existing(self): - self.run_function('boto_sns.create', [self.topic_name]) - ret = self.run_function('boto_sns.delete', [self.topic_name]) + self.run_function("boto_sns.create", [self.topic_name]) + ret = self.run_function("boto_sns.delete", [self.topic_name]) self.assertSaltModuleTrueReturn(ret) - ret = self.run_function('boto_sns.get_all_topics') + ret = self.run_function("boto_sns.get_all_topics") self.assertNotIn(self.topic_name, list(ret.keys())) self.assertNotIn(self._get_arn(self.topic_name), list(ret.values())) def test_get_all_topics(self): - self.topic_names.append(self.topic_name + '-2') + self.topic_names.append(self.topic_name + "-2") for topic in self.topic_names: - self.run_function('boto_sns.create', [topic]) + self.run_function("boto_sns.create", [topic]) - ret = self.run_function('boto_sns.get_all_topics') + ret = self.run_function("boto_sns.get_all_topics") for topic in self.topic_names: self.assertIn(topic, list(ret.keys())) @@ -86,29 +89,28 @@ class BotoSNSTest(ModuleCase): def test_subscribe_and_get_all_subscriptions_by_topic(self): topic_name = self.topic_name - ret = self.run_function('boto_sns.create', [topic_name]) + ret = self.run_function("boto_sns.create", [topic_name]) ret = self.run_function( - 'boto_sns.subscribe', - [topic_name, 'https', 'https://www.example.com/sns/endpoint'] + "boto_sns.subscribe", + [topic_name, "https", "https://www.example.com/sns/endpoint"], ) self.assertSaltModuleTrueReturn(ret) - ret = self.run_function('boto_sns.get_all_subscriptions_by_topic', - [topic_name]) - self.assertDictContainsSubset({ - 'Protocol': 'https', - 'Endpoint': 'https://www.example.com/sns/endpoint' - }, ret[0]) + ret = self.run_function("boto_sns.get_all_subscriptions_by_topic", [topic_name]) + self.assertDictContainsSubset( + {"Protocol": "https", "Endpoint": "https://www.example.com/sns/endpoint"}, + ret[0], + ) def _get_arn(self, name): - return 'arn:aws:sns:us-east-1:{0}:{1}'.format(self.account_id, name) + return "arn:aws:sns:us-east-1:{0}:{1}".format(self.account_id, name) @property def account_id(self): - if not hasattr(self, '_account_id'): - account_id = self.run_function('boto_iam.get_account_id') - setattr(self, '_account_id', account_id) + if not hasattr(self, "_account_id"): + account_id = self.run_function("boto_iam.get_account_id") + setattr(self, "_account_id", account_id) return self._account_id def assertSaltModuleTrueReturn(self, ret): diff --git a/tests/integration/modules/test_cmdmod.py b/tests/integration/modules/test_cmdmod.py index 4981634c3d6..ca832bfd4a2 100644 --- a/tests/integration/modules/test_cmdmod.py +++ b/tests/integration/modules/test_cmdmod.py @@ -1,14 +1,17 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals -from contextlib import contextmanager + import os import sys import tempfile import textwrap +from contextlib import contextmanager -# Import Salt Testing libs +import pytest +import salt.utils.path +import salt.utils.platform +from salt.ext import six from tests.support.case import ModuleCase from tests.support.helpers import ( destructiveTest, @@ -18,311 +21,344 @@ from tests.support.helpers import ( from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf -# Import salt libs -import salt.utils.path -import salt.utils.platform - -# Import 3rd-party libs -from salt.ext import six - -AVAILABLE_PYTHON_EXECUTABLE = salt.utils.path.which_bin([ - 'python', - 'python2', - 'python2.6', - 'python2.7' - -]) +AVAILABLE_PYTHON_EXECUTABLE = salt.utils.path.which_bin( + ["python", "python2", "python2.6", "python2.7"] +) +@pytest.mark.windows_whitelisted class CMDModuleTest(ModuleCase): - ''' + """ Validate the cmd module - ''' + """ + def setUp(self): - self.runas_usr = 'nobody' + self.runas_usr = "nobody" if salt.utils.platform.is_darwin(): - self.runas_usr = 'macsalttest' + self.runas_usr = "macsalttest" @contextmanager def _ensure_user_exists(self, name): - if name in self.run_function('user.info', [name]).values(): + if name in self.run_function("user.info", [name]).values(): # User already exists; don't touch yield else: # Need to create user for test - self.run_function('user.add', [name]) + self.run_function("user.add", [name]) try: yield finally: - self.run_function('user.delete', [name], remove=True) + self.run_function("user.delete", [name], remove=True) def test_run(self): - ''' + """ cmd.run - ''' - shell = os.environ.get('SHELL') + """ + shell = os.environ.get("SHELL") if shell is None: # Failed to get the SHELL var, don't run - self.skipTest('Unable to get the SHELL environment variable') + self.skipTest("Unable to get the SHELL environment variable") - self.assertTrue(self.run_function('cmd.run', ['echo $SHELL'])) + self.assertTrue(self.run_function("cmd.run", ["echo $SHELL"])) self.assertEqual( - self.run_function('cmd.run', - ['echo $SHELL', - 'shell={0}'.format(shell)], - python_shell=True).rstrip(), shell) - self.assertEqual(self.run_function('cmd.run', - ['ls / | grep etc'], - python_shell=True), 'etc') - self.assertEqual(self.run_function('cmd.run', - ['echo {{grains.id}} | awk "{print $1}"'], - template='jinja', - python_shell=True), 'minion') - self.assertEqual(self.run_function('cmd.run', - ['grep f'], - stdin='one\ntwo\nthree\nfour\nfive\n'), 'four\nfive') - self.assertEqual(self.run_function('cmd.run', - ['echo "a=b" | sed -e s/=/:/g'], - python_shell=True), 'a:b') + self.run_function( + "cmd.run", ["echo $SHELL", "shell={0}".format(shell)], python_shell=True + ).rstrip(), + shell, + ) + self.assertEqual( + self.run_function("cmd.run", ["ls / | grep etc"], python_shell=True), "etc" + ) + self.assertEqual( + self.run_function( + "cmd.run", + ['echo {{grains.id}} | awk "{print $1}"'], + template="jinja", + python_shell=True, + ), + "minion", + ) + self.assertEqual( + self.run_function( + "cmd.run", ["grep f"], stdin="one\ntwo\nthree\nfour\nfive\n" + ), + "four\nfive", + ) + self.assertEqual( + self.run_function( + "cmd.run", ['echo "a=b" | sed -e s/=/:/g'], python_shell=True + ), + "a:b", + ) def test_stdout(self): - ''' + """ cmd.run_stdout - ''' - self.assertEqual(self.run_function('cmd.run_stdout', - ['echo "cheese"']).rstrip(), - 'cheese' if not salt.utils.platform.is_windows() else '"cheese"') + """ + self.assertEqual( + self.run_function("cmd.run_stdout", ['echo "cheese"']).rstrip(), + "cheese" if not salt.utils.platform.is_windows() else '"cheese"', + ) def test_stderr(self): - ''' + """ cmd.run_stderr - ''' - if sys.platform.startswith(('freebsd', 'openbsd')): - shell = '/bin/sh' + """ + if sys.platform.startswith(("freebsd", "openbsd")): + shell = "/bin/sh" else: - shell = '/bin/bash' + shell = "/bin/bash" - self.assertEqual(self.run_function('cmd.run_stderr', - ['echo "cheese" 1>&2', - 'shell={0}'.format(shell)], python_shell=True - ).rstrip(), - 'cheese' if not salt.utils.platform.is_windows() else '"cheese"') + self.assertEqual( + self.run_function( + "cmd.run_stderr", + ['echo "cheese" 1>&2', "shell={0}".format(shell)], + python_shell=True, + ).rstrip(), + "cheese" if not salt.utils.platform.is_windows() else '"cheese"', + ) def test_run_all(self): - ''' + """ cmd.run_all - ''' - if sys.platform.startswith(('freebsd', 'openbsd')): - shell = '/bin/sh' + """ + if sys.platform.startswith(("freebsd", "openbsd")): + shell = "/bin/sh" else: - shell = '/bin/bash' + shell = "/bin/bash" - ret = self.run_function('cmd.run_all', ['echo "cheese" 1>&2', - 'shell={0}'.format(shell)], python_shell=True) - self.assertTrue('pid' in ret) - self.assertTrue('retcode' in ret) - self.assertTrue('stdout' in ret) - self.assertTrue('stderr' in ret) - self.assertTrue(isinstance(ret.get('pid'), int)) - self.assertTrue(isinstance(ret.get('retcode'), int)) - self.assertTrue(isinstance(ret.get('stdout'), six.string_types)) - self.assertTrue(isinstance(ret.get('stderr'), six.string_types)) - self.assertEqual(ret.get('stderr').rstrip(), 'cheese' if not salt.utils.platform.is_windows() else '"cheese"') + ret = self.run_function( + "cmd.run_all", + ['echo "cheese" 1>&2', "shell={0}".format(shell)], + python_shell=True, + ) + self.assertTrue("pid" in ret) + self.assertTrue("retcode" in ret) + self.assertTrue("stdout" in ret) + self.assertTrue("stderr" in ret) + self.assertTrue(isinstance(ret.get("pid"), int)) + self.assertTrue(isinstance(ret.get("retcode"), int)) + self.assertTrue(isinstance(ret.get("stdout"), six.string_types)) + self.assertTrue(isinstance(ret.get("stderr"), six.string_types)) + self.assertEqual( + ret.get("stderr").rstrip(), + "cheese" if not salt.utils.platform.is_windows() else '"cheese"', + ) def test_retcode(self): - ''' + """ cmd.retcode - ''' - self.assertEqual(self.run_function('cmd.retcode', ['exit 0'], python_shell=True), 0) - self.assertEqual(self.run_function('cmd.retcode', ['exit 1'], python_shell=True), 1) + """ + self.assertEqual( + self.run_function("cmd.retcode", ["exit 0"], python_shell=True), 0 + ) + self.assertEqual( + self.run_function("cmd.retcode", ["exit 1"], python_shell=True), 1 + ) def test_run_all_with_success_retcodes(self): - ''' + """ cmd.run with success_retcodes - ''' - ret = self.run_function('cmd.run_all', - ['exit 42'], - success_retcodes=[42], - python_shell=True) + """ + ret = self.run_function( + "cmd.run_all", ["exit 42"], success_retcodes=[42], python_shell=True + ) - self.assertTrue('retcode' in ret) - self.assertEqual(ret.get('retcode'), 0) + self.assertTrue("retcode" in ret) + self.assertEqual(ret.get("retcode"), 0) def test_retcode_with_success_retcodes(self): - ''' + """ cmd.run with success_retcodes - ''' - ret = self.run_function('cmd.retcode', - ['exit 42'], - success_retcodes=[42], - python_shell=True) + """ + ret = self.run_function( + "cmd.retcode", ["exit 42"], success_retcodes=[42], python_shell=True + ) self.assertEqual(ret, 0) def test_blacklist_glob(self): - ''' + """ cmd_blacklist_glob - ''' - self.assertEqual(self.run_function('cmd.run', - ['bad_command --foo']).rstrip(), - 'ERROR: The shell command "bad_command --foo" is not permitted') + """ + self.assertEqual( + self.run_function("cmd.run", ["bad_command --foo"]).rstrip(), + 'ERROR: The shell command "bad_command --foo" is not permitted', + ) def test_script(self): - ''' + """ cmd.script - ''' - args = 'saltines crackers biscuits=yes' - script = 'salt://script.py' - ret = self.run_function('cmd.script', [script, args]) - self.assertEqual(ret['stdout'], args) + """ + args = "saltines crackers biscuits=yes" + script = "salt://script.py" + ret = self.run_function("cmd.script", [script, args]) + self.assertEqual(ret["stdout"], args) def test_script_retcode(self): - ''' + """ cmd.script_retcode - ''' - script = 'salt://script.py' - ret = self.run_function('cmd.script_retcode', [script]) + """ + script = "salt://script.py" + ret = self.run_function("cmd.script_retcode", [script]) self.assertEqual(ret, 0) def test_script_cwd(self): - ''' + """ cmd.script with cwd - ''' + """ tmp_cwd = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - args = 'saltines crackers biscuits=yes' - script = 'salt://script.py' - ret = self.run_function('cmd.script', [script, args], cwd=tmp_cwd) - self.assertEqual(ret['stdout'], args) + args = "saltines crackers biscuits=yes" + script = "salt://script.py" + ret = self.run_function("cmd.script", [script, args], cwd=tmp_cwd) + self.assertEqual(ret["stdout"], args) def test_script_cwd_with_space(self): - ''' + """ cmd.script with cwd - ''' - tmp_cwd = "{0}{1}test 2".format(tempfile.mkdtemp(dir=RUNTIME_VARS.TMP), os.path.sep) + """ + tmp_cwd = "{0}{1}test 2".format( + tempfile.mkdtemp(dir=RUNTIME_VARS.TMP), os.path.sep + ) os.mkdir(tmp_cwd) - args = 'saltines crackers biscuits=yes' - script = 'salt://script.py' - ret = self.run_function('cmd.script', [script, args], cwd=tmp_cwd) - self.assertEqual(ret['stdout'], args) + args = "saltines crackers biscuits=yes" + script = "salt://script.py" + ret = self.run_function("cmd.script", [script, args], cwd=tmp_cwd) + self.assertEqual(ret["stdout"], args) @destructiveTest def test_tty(self): - ''' + """ cmd.tty - ''' - for tty in ('tty0', 'pts3'): - if os.path.exists(os.path.join('/dev', tty)): - ret = self.run_function('cmd.tty', [tty, 'apply salt liberally']) - self.assertTrue('Success' in ret) + """ + for tty in ("tty0", "pts3"): + if os.path.exists(os.path.join("/dev", tty)): + ret = self.run_function("cmd.tty", [tty, "apply salt liberally"]) + self.assertTrue("Success" in ret) - @skip_if_binaries_missing(['which']) + @skip_if_binaries_missing(["which"]) def test_which(self): - ''' + """ cmd.which - ''' - self.assertEqual(self.run_function('cmd.which', ['cat']).rstrip(), - self.run_function('cmd.run', ['which cat']).rstrip()) + """ + self.assertEqual( + self.run_function("cmd.which", ["cat"]).rstrip(), + self.run_function("cmd.run", ["which cat"]).rstrip(), + ) - @skip_if_binaries_missing(['which']) + @skip_if_binaries_missing(["which"]) def test_which_bin(self): - ''' + """ cmd.which_bin - ''' - cmds = ['pip3', 'pip2', 'pip', 'pip-python'] - ret = self.run_function('cmd.which_bin', [cmds]) + """ + cmds = ["pip3", "pip2", "pip", "pip-python"] + ret = self.run_function("cmd.which_bin", [cmds]) self.assertTrue(os.path.split(ret)[1] in cmds) def test_has_exec(self): - ''' + """ cmd.has_exec - ''' - self.assertTrue(self.run_function('cmd.has_exec', - [AVAILABLE_PYTHON_EXECUTABLE])) - self.assertFalse(self.run_function('cmd.has_exec', - ['alllfsdfnwieulrrh9123857ygf'])) + """ + self.assertTrue( + self.run_function("cmd.has_exec", [AVAILABLE_PYTHON_EXECUTABLE]) + ) + self.assertFalse( + self.run_function("cmd.has_exec", ["alllfsdfnwieulrrh9123857ygf"]) + ) def test_exec_code(self): - ''' + """ cmd.exec_code - ''' - code = textwrap.dedent('''\ + """ + code = textwrap.dedent( + """\ import sys - sys.stdout.write('cheese')''') - self.assertEqual(self.run_function('cmd.exec_code', - [AVAILABLE_PYTHON_EXECUTABLE, - code]).rstrip(), - 'cheese') + sys.stdout.write('cheese')""" + ) + self.assertEqual( + self.run_function( + "cmd.exec_code", [AVAILABLE_PYTHON_EXECUTABLE, code] + ).rstrip(), + "cheese", + ) def test_exec_code_with_single_arg(self): - ''' + """ cmd.exec_code - ''' - code = textwrap.dedent('''\ + """ + code = textwrap.dedent( + """\ import sys - sys.stdout.write(sys.argv[1])''') - arg = 'cheese' - self.assertEqual(self.run_function('cmd.exec_code', - [AVAILABLE_PYTHON_EXECUTABLE, - code], - args=arg).rstrip(), - arg) + sys.stdout.write(sys.argv[1])""" + ) + arg = "cheese" + self.assertEqual( + self.run_function( + "cmd.exec_code", [AVAILABLE_PYTHON_EXECUTABLE, code], args=arg + ).rstrip(), + arg, + ) def test_exec_code_with_multiple_args(self): - ''' + """ cmd.exec_code - ''' - code = textwrap.dedent('''\ + """ + code = textwrap.dedent( + """\ import sys - sys.stdout.write(sys.argv[1])''') - arg = 'cheese' - self.assertEqual(self.run_function('cmd.exec_code', - [AVAILABLE_PYTHON_EXECUTABLE, - code], - args=[arg, 'test']).rstrip(), - arg) + sys.stdout.write(sys.argv[1])""" + ) + arg = "cheese" + self.assertEqual( + self.run_function( + "cmd.exec_code", [AVAILABLE_PYTHON_EXECUTABLE, code], args=[arg, "test"] + ).rstrip(), + arg, + ) def test_quotes(self): - ''' + """ cmd.run with quoted command - ''' - cmd = '''echo 'SELECT * FROM foo WHERE bar="baz"' ''' + """ + cmd = """echo 'SELECT * FROM foo WHERE bar="baz"' """ expected_result = 'SELECT * FROM foo WHERE bar="baz"' if salt.utils.platform.is_windows(): - expected_result = '\'SELECT * FROM foo WHERE bar="baz"\'' - result = self.run_function('cmd.run_stdout', [cmd]).strip() + expected_result = "'SELECT * FROM foo WHERE bar=\"baz\"'" + result = self.run_function("cmd.run_stdout", [cmd]).strip() self.assertEqual(result, expected_result) @skip_if_not_root - @skipIf(salt.utils.platform.is_windows(), 'skip windows, requires password') + @skipIf(salt.utils.platform.is_windows(), "skip windows, requires password") def test_quotes_runas(self): - ''' + """ cmd.run with quoted command - ''' - cmd = '''echo 'SELECT * FROM foo WHERE bar="baz"' ''' + """ + cmd = """echo 'SELECT * FROM foo WHERE bar="baz"' """ expected_result = 'SELECT * FROM foo WHERE bar="baz"' runas = RUNTIME_VARS.RUNNING_TESTS_USER - result = self.run_function('cmd.run_stdout', [cmd], - runas=runas).strip() + result = self.run_function("cmd.run_stdout", [cmd], runas=runas).strip() self.assertEqual(result, expected_result) @destructiveTest @skip_if_not_root - @skipIf(salt.utils.platform.is_windows(), 'skip windows, uses unix commands') + @skipIf(salt.utils.platform.is_windows(), "skip windows, uses unix commands") def test_avoid_injecting_shell_code_as_root(self): - ''' + """ cmd.run should execute the whole command as the "runas" user, not running substitutions as root. - ''' - cmd = 'echo $(id -u)' + """ + cmd = "echo $(id -u)" - root_id = self.run_function('cmd.run_stdout', [cmd]) - runas_root_id = self.run_function('cmd.run_stdout', [cmd], runas=RUNTIME_VARS.RUNNING_TESTS_USER) + root_id = self.run_function("cmd.run_stdout", [cmd]) + runas_root_id = self.run_function( + "cmd.run_stdout", [cmd], runas=RUNTIME_VARS.RUNNING_TESTS_USER + ) with self._ensure_user_exists(self.runas_usr): - user_id = self.run_function('cmd.run_stdout', [cmd], runas=self.runas_usr) + user_id = self.run_function("cmd.run_stdout", [cmd], runas=self.runas_usr) self.assertNotEqual(user_id, root_id) self.assertNotEqual(user_id, runas_root_id) @@ -330,33 +366,39 @@ class CMDModuleTest(ModuleCase): @destructiveTest @skip_if_not_root - @skipIf(salt.utils.platform.is_windows(), 'skip windows, uses unix commands') + @skipIf(salt.utils.platform.is_windows(), "skip windows, uses unix commands") def test_cwd_runas(self): - ''' + """ cmd.run should be able to change working directory correctly, whether or not runas is in use. - ''' - cmd = 'pwd' + """ + cmd = "pwd" tmp_cwd = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) os.chmod(tmp_cwd, 0o711) - cwd_normal = self.run_function('cmd.run_stdout', [cmd], cwd=tmp_cwd).rstrip('\n') + cwd_normal = self.run_function("cmd.run_stdout", [cmd], cwd=tmp_cwd).rstrip( + "\n" + ) self.assertEqual(tmp_cwd, cwd_normal) with self._ensure_user_exists(self.runas_usr): - cwd_runas = self.run_function('cmd.run_stdout', [cmd], cwd=tmp_cwd, runas=self.runas_usr).rstrip('\n') + cwd_runas = self.run_function( + "cmd.run_stdout", [cmd], cwd=tmp_cwd, runas=self.runas_usr + ).rstrip("\n") self.assertEqual(tmp_cwd, cwd_runas) @destructiveTest @skip_if_not_root - @skipIf(not salt.utils.platform.is_darwin(), 'applicable to MacOS only') + @skipIf(not salt.utils.platform.is_darwin(), "applicable to MacOS only") def test_runas_env(self): - ''' + """ cmd.run should be able to change working directory correctly, whether or not runas is in use. - ''' + """ with self._ensure_user_exists(self.runas_usr): - user_path = self.run_function('cmd.run_stdout', ['printf %s "$PATH"'], runas=self.runas_usr) + user_path = self.run_function( + "cmd.run_stdout", ['printf %s "$PATH"'], runas=self.runas_usr + ) # XXX: Not sure of a better way. Environment starts out with # /bin:/usr/bin and should be populated by path helper and the bash # profile. @@ -364,9 +406,9 @@ class CMDModuleTest(ModuleCase): @destructiveTest @skip_if_not_root - @skipIf(not salt.utils.platform.is_darwin(), 'applicable to MacOS only') + @skipIf(not salt.utils.platform.is_darwin(), "applicable to MacOS only") def test_runas_complex_command_bad_cwd(self): - ''' + """ cmd.run should not accidentally run parts of a complex command when given a cwd which cannot be used by the user the command is run as. @@ -375,130 +417,121 @@ class CMDModuleTest(ModuleCase): have execute permission for. To an extent, this test is testing that buggy behaviour, but its purpose is to ensure that the greater bug of running commands after failing to cd does not occur. - ''' + """ tmp_cwd = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) os.chmod(tmp_cwd, 0o700) with self._ensure_user_exists(self.runas_usr): - cmd_result = self.run_function('cmd.run_all', ['pwd; pwd; : $(echo "You have failed the test" >&2)'], cwd=tmp_cwd, runas=self.runas_usr) + cmd_result = self.run_function( + "cmd.run_all", + ['pwd; pwd; : $(echo "You have failed the test" >&2)'], + cwd=tmp_cwd, + runas=self.runas_usr, + ) - self.assertEqual("", cmd_result['stdout']) - self.assertNotIn("You have failed the test", cmd_result['stderr']) - self.assertNotEqual(0, cmd_result['retcode']) + self.assertEqual("", cmd_result["stdout"]) + self.assertNotIn("You have failed the test", cmd_result["stderr"]) + self.assertNotEqual(0, cmd_result["retcode"]) - @skipIf(salt.utils.platform.is_windows(), 'minion is windows') + @skipIf(salt.utils.platform.is_windows(), "minion is windows") @skip_if_not_root @destructiveTest def test_runas(self): - ''' + """ Ensure that the env is the runas user's - ''' + """ with self._ensure_user_exists(self.runas_usr): - out = self.run_function('cmd.run', ['env'], runas=self.runas_usr).splitlines() - self.assertIn('USER={0}'.format(self.runas_usr), out) + out = self.run_function( + "cmd.run", ["env"], runas=self.runas_usr + ).splitlines() + self.assertIn("USER={0}".format(self.runas_usr), out) - @skipIf(not salt.utils.path.which_bin('sleep'), 'sleep cmd not installed') + @skipIf(not salt.utils.path.which_bin("sleep"), "sleep cmd not installed") def test_timeout(self): - ''' + """ cmd.run trigger timeout - ''' - out = self.run_function('cmd.run', - ['sleep 2 && echo hello'], - f_timeout=1, - python_shell=True) - self.assertTrue('Timed out' in out) + """ + out = self.run_function( + "cmd.run", ["sleep 2 && echo hello"], f_timeout=1, python_shell=True + ) + self.assertTrue("Timed out" in out) - @skipIf(not salt.utils.path.which_bin('sleep'), 'sleep cmd not installed') + @skipIf(not salt.utils.path.which_bin("sleep"), "sleep cmd not installed") def test_timeout_success(self): - ''' + """ cmd.run sufficient timeout to succeed - ''' - out = self.run_function('cmd.run', - ['sleep 1 && echo hello'], - f_timeout=2, - python_shell=True) - self.assertEqual(out, 'hello') + """ + out = self.run_function( + "cmd.run", ["sleep 1 && echo hello"], f_timeout=2, python_shell=True + ) + self.assertEqual(out, "hello") def test_hide_output(self): - ''' + """ Test the hide_output argument - ''' - ls_command = ['ls', '/'] \ - if not salt.utils.platform.is_windows() \ - else ['dir', 'c:\\'] + """ + ls_command = ( + ["ls", "/"] if not salt.utils.platform.is_windows() else ["dir", "c:\\"] + ) - error_command = ['thiscommanddoesnotexist'] + error_command = ["thiscommanddoesnotexist"] # cmd.run - out = self.run_function( - 'cmd.run', - ls_command, - hide_output=True) - self.assertEqual(out, '') + out = self.run_function("cmd.run", ls_command, hide_output=True) + self.assertEqual(out, "") # cmd.shell - out = self.run_function( - 'cmd.shell', - ls_command, - hide_output=True) - self.assertEqual(out, '') + out = self.run_function("cmd.shell", ls_command, hide_output=True) + self.assertEqual(out, "") # cmd.run_stdout - out = self.run_function( - 'cmd.run_stdout', - ls_command, - hide_output=True) - self.assertEqual(out, '') + out = self.run_function("cmd.run_stdout", ls_command, hide_output=True) + self.assertEqual(out, "") # cmd.run_stderr - out = self.run_function( - 'cmd.shell', - error_command, - hide_output=True) - self.assertEqual(out, '') + out = self.run_function("cmd.shell", error_command, hide_output=True) + self.assertEqual(out, "") # cmd.run_all (command should have produced stdout) - out = self.run_function( - 'cmd.run_all', - ls_command, - hide_output=True) - self.assertEqual(out['stdout'], '') - self.assertEqual(out['stderr'], '') + out = self.run_function("cmd.run_all", ls_command, hide_output=True) + self.assertEqual(out["stdout"], "") + self.assertEqual(out["stderr"], "") # cmd.run_all (command should have produced stderr) - out = self.run_function( - 'cmd.run_all', - error_command, - hide_output=True) - self.assertEqual(out['stdout'], '') - self.assertEqual(out['stderr'], '') + out = self.run_function("cmd.run_all", error_command, hide_output=True) + self.assertEqual(out["stdout"], "") + self.assertEqual(out["stderr"], "") def test_cmd_run_whoami(self): - ''' + """ test return of whoami - ''' - cmd = self.run_function('cmd.run', ['whoami']) + """ + cmd = self.run_function("cmd.run", ["whoami"]) if salt.utils.platform.is_windows(): - self.assertIn('administrator', cmd) + self.assertIn("administrator", cmd) else: - self.assertEqual('root', cmd) + self.assertEqual("root", cmd) - @skipIf(not salt.utils.platform.is_windows(), 'minion is not windows') + @skipIf(not salt.utils.platform.is_windows(), "minion is not windows") def test_windows_env_handling(self): - ''' + """ Ensure that nt.environ is used properly with cmd.run* - ''' - out = self.run_function('cmd.run', ['set'], env={"abc": "123", "ABC": "456"}).splitlines() - self.assertIn('abc=123', out) - self.assertIn('ABC=456', out) + """ + out = self.run_function( + "cmd.run", ["set"], env={"abc": "123", "ABC": "456"} + ).splitlines() + self.assertIn("abc=123", out) + self.assertIn("ABC=456", out) - @skipIf(not salt.utils.platform.is_windows(), 'minion is not windows') + @skipIf(not salt.utils.platform.is_windows(), "minion is not windows") def test_windows_powershell_script_args(self): - ''' + """ Ensure that powershell processes inline script in args - ''' - val = 'i like cheese' - args = '-SecureString (ConvertTo-SecureString -String "{0}" -AsPlainText -Force) -ErrorAction Stop'.format(val) - script = 'salt://issue-56195/test.ps1' - ret = self.run_function('cmd.script', [script], args=args, shell='powershell') - self.assertEqual(ret['stdout'], val) + """ + val = "i like cheese" + args = '-SecureString (ConvertTo-SecureString -String "{0}" -AsPlainText -Force) -ErrorAction Stop'.format( + val + ) + script = "salt://issue-56195/test.ps1" + ret = self.run_function("cmd.script", [script], args=args, shell="powershell") + self.assertEqual(ret["stdout"], val) diff --git a/tests/integration/modules/test_config.py b/tests/integration/modules/test_config.py index cbc582a5282..3fc5b30021b 100644 --- a/tests/integration/modules/test_config.py +++ b/tests/integration/modules/test_config.py @@ -1,107 +1,76 @@ # -*- coding: utf-8 -*- -''' +""" Validate the config system -''' -# Import Python libs +""" from __future__ import absolute_import -# Import Salt Testing libs +import pytest from tests.support.case import ModuleCase +@pytest.mark.windows_whitelisted class ConfigTest(ModuleCase): - ''' + """ Test config routines - ''' + """ + def test_valid_file_proto(self): - ''' + """ test config.valid_file_proto - ''' - self.assertTrue( - self.run_function('config.valid_fileproto', ['salt://'])) - self.assertTrue( - self.run_function('config.valid_fileproto', ['file://'])) - self.assertTrue( - self.run_function('config.valid_fileproto', ['http://'])) - self.assertTrue( - self.run_function('config.valid_fileproto', ['https://'])) - self.assertTrue( - self.run_function('config.valid_fileproto', ['ftp://'])) - self.assertTrue( - self.run_function('config.valid_fileproto', ['s3://'])) - self.assertTrue( - self.run_function('config.valid_fileproto', ['swift://'])) - self.assertFalse( - self.run_function('config.valid_fileproto', ['cheese://'])) + """ + self.assertTrue(self.run_function("config.valid_fileproto", ["salt://"])) + self.assertTrue(self.run_function("config.valid_fileproto", ["file://"])) + self.assertTrue(self.run_function("config.valid_fileproto", ["http://"])) + self.assertTrue(self.run_function("config.valid_fileproto", ["https://"])) + self.assertTrue(self.run_function("config.valid_fileproto", ["ftp://"])) + self.assertTrue(self.run_function("config.valid_fileproto", ["s3://"])) + self.assertTrue(self.run_function("config.valid_fileproto", ["swift://"])) + self.assertFalse(self.run_function("config.valid_fileproto", ["cheese://"])) def test_backup_mode(self): - ''' + """ test config.backup_mode - ''' - self.assertEqual( - self.run_function('config.backup_mode', ['minion']), 'minion') + """ + self.assertEqual(self.run_function("config.backup_mode", ["minion"]), "minion") def test_manage_mode(self): - ''' + """ test config.manage_mode - ''' + """ # This function is generally only used with cross calls, the yaml # interpreter is breaking it for remote calls # The correct standard is the four digit form. - self.assertEqual( - self.run_function('config.manage_mode', ['"775"']), '0775') - self.assertEqual( - self.run_function('config.manage_mode', ['"1775"']), '1775') - self.assertEqual( - self.run_function('config.manage_mode', ['"0775"']), '0775') - self.assertEqual( - self.run_function('config.manage_mode', ['"01775"']), '1775') - self.assertEqual( - self.run_function('config.manage_mode', ['"0"']), '0000') - self.assertEqual( - self.run_function('config.manage_mode', ['775']), '0775') - self.assertEqual( - self.run_function('config.manage_mode', ['1775']), '1775') - self.assertEqual( - self.run_function('config.manage_mode', ['0']), '0000') + self.assertEqual(self.run_function("config.manage_mode", ['"775"']), "0775") + self.assertEqual(self.run_function("config.manage_mode", ['"1775"']), "1775") + self.assertEqual(self.run_function("config.manage_mode", ['"0775"']), "0775") + self.assertEqual(self.run_function("config.manage_mode", ['"01775"']), "1775") + self.assertEqual(self.run_function("config.manage_mode", ['"0"']), "0000") + self.assertEqual(self.run_function("config.manage_mode", ["775"]), "0775") + self.assertEqual(self.run_function("config.manage_mode", ["1775"]), "1775") + self.assertEqual(self.run_function("config.manage_mode", ["0"]), "0000") def test_option(self): - ''' + """ test config.option - ''' + """ # Minion opt self.assertEqual( - self.run_function( - 'config.option', - ['master_port']), - self.get_config('minion')['master_port']) + self.run_function("config.option", ["master_port"]), + self.get_config("minion")["master_port"], + ) # pillar conf opt - self.assertEqual( - self.run_function( - 'config.option', - ['ext_spam']), - 'eggs') + self.assertEqual(self.run_function("config.option", ["ext_spam"]), "eggs") def test_get(self): - ''' + """ Test option.get - ''' + """ # Check pillar get - self.assertEqual( - self.run_function( - 'config.get', - ['level1:level2']), - 'foo') + self.assertEqual(self.run_function("config.get", ["level1:level2"]), "foo") # Check master config self.assertEqual( - self.run_function( - 'config.get', - ['config_opt:layer2']), - 'kosher') + self.run_function("config.get", ["config_opt:layer2"]), "kosher" + ) # Check minion config - self.assertEqual( - self.run_function( - 'config.get', - ['config_test:spam']), - 'eggs') + self.assertEqual(self.run_function("config.get", ["config_test:spam"]), "eggs") diff --git a/tests/integration/modules/test_cp.py b/tests/integration/modules/test_cp.py index 91b644f5df9..81255b94e2e 100644 --- a/tests/integration/modules/test_cp.py +++ b/tests/integration/modules/test_cp.py @@ -1,548 +1,441 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys -import uuid + import hashlib import logging -import psutil +import os import shutil import signal import tempfile import textwrap import time +import uuid -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.helpers import ( - get_unused_localhost_port, - skip_if_not_root, - with_tempfile) -from tests.support.unit import skipIf -from tests.support.runtests import RUNTIME_VARS - -# Import 3rd party libs +import psutil +import pytest import salt.ext.six as six - -# Import salt libs import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils +from tests.support.case import ModuleCase +from tests.support.helpers import ( + get_unused_localhost_port, + skip_if_not_root, + with_tempfile, +) +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf log = logging.getLogger(__name__) -SSL3_SUPPORT = sys.version_info >= (2, 7, 9) - - +@pytest.mark.windows_whitelisted class CPModuleTest(ModuleCase): - ''' + """ Validate the cp module - ''' + """ + def run_function(self, *args, **kwargs): # pylint: disable=arguments-differ - ''' + """ Ensure that results are decoded TODO: maybe move this behavior to ModuleCase itself? - ''' + """ return salt.utils.data.decode( super(CPModuleTest, self).run_function(*args, **kwargs) ) @with_tempfile() def test_get_file(self, tgt): - ''' + """ cp.get_file - ''' - self.run_function( - 'cp.get_file', - [ - 'salt://grail/scene33', - tgt, - ]) - with salt.utils.files.fopen(tgt, 'r') as scene: + """ + self.run_function("cp.get_file", ["salt://grail/scene33", tgt]) + with salt.utils.files.fopen(tgt, "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) def test_get_file_to_dir(self): - ''' + """ cp.get_file - ''' - tgt = os.path.join(RUNTIME_VARS.TMP, '') - self.run_function( - 'cp.get_file', - [ - 'salt://grail/scene33', - tgt, - ]) - with salt.utils.files.fopen(tgt + 'scene33', 'r') as scene: + """ + tgt = os.path.join(RUNTIME_VARS.TMP, "") + self.run_function("cp.get_file", ["salt://grail/scene33", tgt]) + with salt.utils.files.fopen(tgt + "scene33", "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) @with_tempfile() - @skipIf(salt.utils.platform.is_windows() and six.PY3, 'This test hangs on Windows on Py3') + @skipIf( + salt.utils.platform.is_windows() and six.PY3, + "This test hangs on Windows on Py3", + ) def test_get_file_templated_paths(self, tgt): - ''' + """ cp.get_file - ''' + """ self.run_function( - 'cp.get_file', + "cp.get_file", [ - 'salt://{{grains.test_grain}}', - tgt.replace('cheese', '{{grains.test_grain}}') + "salt://{{grains.test_grain}}", + tgt.replace("cheese", "{{grains.test_grain}}"), ], - template='jinja' + template="jinja", ) - with salt.utils.files.fopen(tgt, 'r') as cheese: + with salt.utils.files.fopen(tgt, "r") as cheese: data = salt.utils.stringutils.to_unicode(cheese.read()) - self.assertIn('Gromit', data) - self.assertNotIn('bacon', data) + self.assertIn("Gromit", data) + self.assertNotIn("bacon", data) @with_tempfile() def test_get_file_gzipped(self, tgt): - ''' + """ cp.get_file - ''' - src = os.path.join(RUNTIME_VARS.FILES, 'file', 'base', 'file.big') - with salt.utils.files.fopen(src, 'rb') as fp_: + """ + src = os.path.join(RUNTIME_VARS.FILES, "file", "base", "file.big") + with salt.utils.files.fopen(src, "rb") as fp_: hash_str = hashlib.md5(fp_.read()).hexdigest() - self.run_function( - 'cp.get_file', - [ - 'salt://file.big', - tgt, - ], - gzip=5 - ) - with salt.utils.files.fopen(tgt, 'rb') as scene: + self.run_function("cp.get_file", ["salt://file.big", tgt], gzip=5) + with salt.utils.files.fopen(tgt, "rb") as scene: data = scene.read() self.assertEqual(hash_str, hashlib.md5(data).hexdigest()) data = salt.utils.stringutils.to_unicode(data) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) def test_get_file_makedirs(self): - ''' + """ cp.get_file - ''' - tgt = os.path.join(RUNTIME_VARS.TMP, 'make', 'dirs', 'scene33') - self.run_function( - 'cp.get_file', - [ - 'salt://grail/scene33', - tgt, - ], - makedirs=True + """ + tgt = os.path.join(RUNTIME_VARS.TMP, "make", "dirs", "scene33") + self.run_function("cp.get_file", ["salt://grail/scene33", tgt], makedirs=True) + self.addCleanup( + shutil.rmtree, os.path.join(RUNTIME_VARS.TMP, "make"), ignore_errors=True ) - self.addCleanup(shutil.rmtree, os.path.join(RUNTIME_VARS.TMP, 'make'), ignore_errors=True) - with salt.utils.files.fopen(tgt, 'r') as scene: + with salt.utils.files.fopen(tgt, "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) @with_tempfile() def test_get_template(self, tgt): - ''' + """ cp.get_template - ''' + """ self.run_function( - 'cp.get_template', - ['salt://grail/scene33', tgt], - spam='bacon') - with salt.utils.files.fopen(tgt, 'r') as scene: + "cp.get_template", ["salt://grail/scene33", tgt], spam="bacon" + ) + with salt.utils.files.fopen(tgt, "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('bacon', data) - self.assertNotIn('spam', data) + self.assertIn("bacon", data) + self.assertNotIn("spam", data) def test_get_dir(self): - ''' + """ cp.get_dir - ''' - tgt = os.path.join(RUNTIME_VARS.TMP, 'many') - self.run_function( - 'cp.get_dir', - [ - 'salt://grail', - tgt - ]) - self.assertIn('grail', os.listdir(tgt)) - self.assertIn('36', os.listdir(os.path.join(tgt, 'grail'))) - self.assertIn('empty', os.listdir(os.path.join(tgt, 'grail'))) - self.assertIn('scene', os.listdir(os.path.join(tgt, 'grail', '36'))) + """ + tgt = os.path.join(RUNTIME_VARS.TMP, "many") + self.run_function("cp.get_dir", ["salt://grail", tgt]) + self.assertIn("grail", os.listdir(tgt)) + self.assertIn("36", os.listdir(os.path.join(tgt, "grail"))) + self.assertIn("empty", os.listdir(os.path.join(tgt, "grail"))) + self.assertIn("scene", os.listdir(os.path.join(tgt, "grail", "36"))) def test_get_dir_templated_paths(self): - ''' + """ cp.get_dir - ''' - tgt = os.path.join(RUNTIME_VARS.TMP, 'many') + """ + tgt = os.path.join(RUNTIME_VARS.TMP, "many") self.run_function( - 'cp.get_dir', - [ - 'salt://{{grains.script}}', - tgt.replace('many', '{{grains.alot}}') - ] + "cp.get_dir", + ["salt://{{grains.script}}", tgt.replace("many", "{{grains.alot}}")], ) - self.assertIn('grail', os.listdir(tgt)) - self.assertIn('36', os.listdir(os.path.join(tgt, 'grail'))) - self.assertIn('empty', os.listdir(os.path.join(tgt, 'grail'))) - self.assertIn('scene', os.listdir(os.path.join(tgt, 'grail', '36'))) + self.assertIn("grail", os.listdir(tgt)) + self.assertIn("36", os.listdir(os.path.join(tgt, "grail"))) + self.assertIn("empty", os.listdir(os.path.join(tgt, "grail"))) + self.assertIn("scene", os.listdir(os.path.join(tgt, "grail", "36"))) # cp.get_url tests @with_tempfile() def test_get_url(self, tgt): - ''' + """ cp.get_url with salt:// source given - ''' - self.run_function( - 'cp.get_url', - [ - 'salt://grail/scene33', - tgt, - ]) - with salt.utils.files.fopen(tgt, 'r') as scene: + """ + self.run_function("cp.get_url", ["salt://grail/scene33", tgt]) + with salt.utils.files.fopen(tgt, "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) def test_get_url_makedirs(self): - ''' + """ cp.get_url - ''' - tgt = os.path.join(RUNTIME_VARS.TMP, 'make', 'dirs', 'scene33') - self.run_function( - 'cp.get_url', - [ - 'salt://grail/scene33', - tgt, - ], - makedirs=True - ) - self.addCleanup(shutil.rmtree, os.path.join(RUNTIME_VARS.TMP, 'make'), ignore_errors=True) - with salt.utils.files.fopen(tgt, 'r') as scene: + """ + tgt = os.path.join(RUNTIME_VARS.TMP, "make", "dirs", "scene33") + self.run_function("cp.get_url", ["salt://grail/scene33", tgt], makedirs=True) + self.addCleanup( + shutil.rmtree, os.path.join(RUNTIME_VARS.TMP, "make"), ignore_errors=True + ) + with salt.utils.files.fopen(tgt, "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) def test_get_url_dest_empty(self): - ''' + """ cp.get_url with salt:// source given and destination omitted. - ''' - ret = self.run_function( - 'cp.get_url', - [ - 'salt://grail/scene33', - ]) - with salt.utils.files.fopen(ret, 'r') as scene: + """ + ret = self.run_function("cp.get_url", ["salt://grail/scene33"]) + with salt.utils.files.fopen(ret, "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) def test_get_url_no_dest(self): - ''' + """ cp.get_url with salt:// source given and destination set as None - ''' + """ tgt = None - ret = self.run_function( - 'cp.get_url', - [ - 'salt://grail/scene33', - tgt, - ]) - self.assertIn('KNIGHT: They\'re nervous, sire.', ret) + ret = self.run_function("cp.get_url", ["salt://grail/scene33", tgt]) + self.assertIn("KNIGHT: They're nervous, sire.", ret) def test_get_url_nonexistent_source(self): - ''' + """ cp.get_url with nonexistent salt:// source given - ''' + """ tgt = None - ret = self.run_function( - 'cp.get_url', - [ - 'salt://grail/nonexistent_scene', - tgt, - ]) + ret = self.run_function("cp.get_url", ["salt://grail/nonexistent_scene", tgt]) self.assertEqual(ret, False) def test_get_url_to_dir(self): - ''' + """ cp.get_url with salt:// source - ''' - tgt = os.path.join(RUNTIME_VARS.TMP, '') - self.run_function( - 'cp.get_url', - [ - 'salt://grail/scene33', - tgt, - ]) - with salt.utils.files.fopen(tgt + 'scene33', 'r') as scene: + """ + tgt = os.path.join(RUNTIME_VARS.TMP, "") + self.run_function("cp.get_url", ["salt://grail/scene33", tgt]) + with salt.utils.files.fopen(tgt + "scene33", "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) - @skipIf(not SSL3_SUPPORT, 'Requires python with SSL3 support') - @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2') + @skipIf( + salt.utils.platform.is_darwin() and six.PY2, "This test hangs on OS X on Py2" + ) @with_tempfile() def test_get_url_https(self, tgt): - ''' + """ cp.get_url with https:// source given - ''' - self.run_function( - 'cp.get_url', - [ - 'https://repo.saltstack.com/index.html', - tgt, - ]) - with salt.utils.files.fopen(tgt, 'r') as instructions: + """ + self.run_function("cp.get_url", ["https://repo.saltstack.com/index.html", tgt]) + with salt.utils.files.fopen(tgt, "r") as instructions: data = salt.utils.stringutils.to_unicode(instructions.read()) - self.assertIn('Bootstrap', data) - self.assertIn('Debian', data) - self.assertIn('Windows', data) - self.assertNotIn('AYBABTU', data) + self.assertIn("Bootstrap", data) + self.assertIn("Debian", data) + self.assertIn("Windows", data) + self.assertNotIn("AYBABTU", data) - @skipIf(not SSL3_SUPPORT, 'Requires python with SSL3 support') - @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2') + @skipIf( + salt.utils.platform.is_darwin() and six.PY2, "This test hangs on OS X on Py2" + ) def test_get_url_https_dest_empty(self): - ''' + """ cp.get_url with https:// source given and destination omitted. - ''' - ret = self.run_function( - 'cp.get_url', - [ - 'https://repo.saltstack.com/index.html', - ]) + """ + ret = self.run_function("cp.get_url", ["https://repo.saltstack.com/index.html"]) - with salt.utils.files.fopen(ret, 'r') as instructions: + with salt.utils.files.fopen(ret, "r") as instructions: data = salt.utils.stringutils.to_unicode(instructions.read()) - self.assertIn('Bootstrap', data) - self.assertIn('Debian', data) - self.assertIn('Windows', data) - self.assertNotIn('AYBABTU', data) + self.assertIn("Bootstrap", data) + self.assertIn("Debian", data) + self.assertIn("Windows", data) + self.assertNotIn("AYBABTU", data) - @skipIf(not SSL3_SUPPORT, 'Requires python with SSL3 support') - @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2') + @skipIf( + salt.utils.platform.is_darwin() and six.PY2, "This test hangs on OS X on Py2" + ) def test_get_url_https_no_dest(self): - ''' + """ cp.get_url with https:// source given and destination set as None - ''' + """ timeout = 500 start = time.time() sleep = 5 tgt = None while time.time() - start <= timeout: ret = self.run_function( - 'cp.get_url', - [ - 'https://repo.saltstack.com/index.html', - tgt, - ]) - if ret.find('HTTP 599') == -1: + "cp.get_url", ["https://repo.saltstack.com/index.html", tgt] + ) + if ret.find("HTTP 599") == -1: break time.sleep(sleep) - if ret.find('HTTP 599') != -1: - raise Exception( - 'https://repo.saltstack.com/index.html returned 599 error' - ) - self.assertIn('Bootstrap', ret) - self.assertIn('Debian', ret) - self.assertIn('Windows', ret) - self.assertNotIn('AYBABTU', ret) + if ret.find("HTTP 599") != -1: + raise Exception("https://repo.saltstack.com/index.html returned 599 error") + self.assertIn("Bootstrap", ret) + self.assertIn("Debian", ret) + self.assertIn("Windows", ret) + self.assertNotIn("AYBABTU", ret) def test_get_url_file(self): - ''' + """ cp.get_url with file:// source given - ''' - tgt = '' - src = os.path.join('file://', RUNTIME_VARS.FILES, 'file', 'base', 'file.big') - ret = self.run_function( - 'cp.get_url', - [ - src, - tgt, - ]) - with salt.utils.files.fopen(ret, 'r') as scene: + """ + tgt = "" + src = os.path.join("file://", RUNTIME_VARS.FILES, "file", "base", "file.big") + ret = self.run_function("cp.get_url", [src, tgt]) + with salt.utils.files.fopen(ret, "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) def test_get_url_file_no_dest(self): - ''' + """ cp.get_url with file:// source given and destination set as None - ''' + """ tgt = None - src = os.path.join('file://', RUNTIME_VARS.FILES, 'file', 'base', 'file.big') - ret = self.run_function( - 'cp.get_url', - [ - src, - tgt, - ]) - self.assertIn('KNIGHT: They\'re nervous, sire.', ret) - self.assertNotIn('bacon', ret) + src = os.path.join("file://", RUNTIME_VARS.FILES, "file", "base", "file.big") + ret = self.run_function("cp.get_url", [src, tgt]) + self.assertIn("KNIGHT: They're nervous, sire.", ret) + self.assertNotIn("bacon", ret) @with_tempfile() def test_get_url_ftp(self, tgt): - ''' + """ cp.get_url with https:// source given - ''' + """ self.run_function( - 'cp.get_url', + "cp.get_url", [ - 'ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/12.0-RELEASE/MANIFEST', + "ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/12.0-RELEASE/MANIFEST", tgt, - ]) - with salt.utils.files.fopen(tgt, 'r') as instructions: + ], + ) + with salt.utils.files.fopen(tgt, "r") as instructions: data = salt.utils.stringutils.to_unicode(instructions.read()) - self.assertIn('Base system', data) + self.assertIn("Base system", data) # cp.get_file_str tests def test_get_file_str_salt(self): - ''' + """ cp.get_file_str with salt:// source given - ''' - src = 'salt://grail/scene33' - ret = self.run_function( - 'cp.get_file_str', - [ - src, - ]) - self.assertIn('KNIGHT: They\'re nervous, sire.', ret) + """ + src = "salt://grail/scene33" + ret = self.run_function("cp.get_file_str", [src]) + self.assertIn("KNIGHT: They're nervous, sire.", ret) def test_get_file_str_nonexistent_source(self): - ''' + """ cp.get_file_str with nonexistent salt:// source given - ''' - src = 'salt://grail/nonexistent_scene' - ret = self.run_function( - 'cp.get_file_str', - [ - src, - ]) + """ + src = "salt://grail/nonexistent_scene" + ret = self.run_function("cp.get_file_str", [src]) self.assertEqual(ret, False) - @skipIf(not SSL3_SUPPORT, 'Requires python with SSL3 support') - @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2') + @skipIf( + salt.utils.platform.is_darwin() and six.PY2, "This test hangs on OS X on Py2" + ) def test_get_file_str_https(self): - ''' + """ cp.get_file_str with https:// source given - ''' - src = 'https://repo.saltstack.com/index.html' - ret = self.run_function( - 'cp.get_file_str', - [ - src, - ]) - self.assertIn('Bootstrap', ret) - self.assertIn('Debian', ret) - self.assertIn('Windows', ret) - self.assertNotIn('AYBABTU', ret) + """ + src = "https://repo.saltstack.com/index.html" + ret = self.run_function("cp.get_file_str", [src]) + self.assertIn("Bootstrap", ret) + self.assertIn("Debian", ret) + self.assertIn("Windows", ret) + self.assertNotIn("AYBABTU", ret) def test_get_file_str_local(self): - ''' + """ cp.get_file_str with file:// source given - ''' - src = os.path.join('file://', RUNTIME_VARS.FILES, 'file', 'base', 'file.big') - ret = self.run_function( - 'cp.get_file_str', - [ - src, - ]) - self.assertIn('KNIGHT: They\'re nervous, sire.', ret) - self.assertNotIn('bacon', ret) + """ + src = os.path.join("file://", RUNTIME_VARS.FILES, "file", "base", "file.big") + ret = self.run_function("cp.get_file_str", [src]) + self.assertIn("KNIGHT: They're nervous, sire.", ret) + self.assertNotIn("bacon", ret) # caching tests def test_cache_file(self): - ''' + """ cp.cache_file - ''' - ret = self.run_function( - 'cp.cache_file', - [ - 'salt://grail/scene33', - ]) - with salt.utils.files.fopen(ret, 'r') as scene: + """ + ret = self.run_function("cp.cache_file", ["salt://grail/scene33"]) + with salt.utils.files.fopen(ret, "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('KNIGHT: They\'re nervous, sire.', data) - self.assertNotIn('bacon', data) + self.assertIn("KNIGHT: They're nervous, sire.", data) + self.assertNotIn("bacon", data) def test_cache_files(self): - ''' + """ cp.cache_files - ''' + """ ret = self.run_function( - 'cp.cache_files', - [ - ['salt://grail/scene33', 'salt://grail/36/scene'], - ]) + "cp.cache_files", [["salt://grail/scene33", "salt://grail/36/scene"]] + ) for path in ret: - with salt.utils.files.fopen(path, 'r') as scene: + with salt.utils.files.fopen(path, "r") as scene: data = salt.utils.stringutils.to_unicode(scene.read()) - self.assertIn('ARTHUR:', data) - self.assertNotIn('bacon', data) + self.assertIn("ARTHUR:", data) + self.assertNotIn("bacon", data) @with_tempfile() def test_cache_master(self, tgt): - ''' + """ cp.cache_master - ''' - ret = self.run_function( - 'cp.cache_master', - [tgt], - ) + """ + ret = self.run_function("cp.cache_master", [tgt],) for path in ret: self.assertTrue(os.path.exists(path)) def test_cache_local_file(self): - ''' + """ cp.cache_local_file - ''' - src = os.path.join(RUNTIME_VARS.TMP, 'random') - with salt.utils.files.fopen(src, 'w+') as fn_: - fn_.write(salt.utils.stringutils.to_str('foo')) - ret = self.run_function( - 'cp.cache_local_file', - [src]) - with salt.utils.files.fopen(ret, 'r') as cp_: - self.assertEqual( - salt.utils.stringutils.to_unicode(cp_.read()), - 'foo' - ) + """ + src = os.path.join(RUNTIME_VARS.TMP, "random") + with salt.utils.files.fopen(src, "w+") as fn_: + fn_.write(salt.utils.stringutils.to_str("foo")) + ret = self.run_function("cp.cache_local_file", [src]) + with salt.utils.files.fopen(ret, "r") as cp_: + self.assertEqual(salt.utils.stringutils.to_unicode(cp_.read()), "foo") - @skipIf(not salt.utils.path.which('nginx'), 'nginx not installed') + @skipIf(not salt.utils.path.which("nginx"), "nginx not installed") @skip_if_not_root def test_cache_remote_file(self): - ''' + """ cp.cache_file - ''' + """ nginx_port = get_unused_localhost_port() - url_prefix = 'http://localhost:{0}/'.format(nginx_port) + url_prefix = "http://localhost:{0}/".format(nginx_port) temp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, temp_dir, ignore_errors=True) - nginx_root_dir = os.path.join(temp_dir, 'root') - nginx_conf_dir = os.path.join(temp_dir, 'conf') - nginx_conf = os.path.join(nginx_conf_dir, 'nginx.conf') - nginx_pidfile = os.path.join(nginx_conf_dir, 'nginx.pid') - file_contents = 'Hello world!' + nginx_root_dir = os.path.join(temp_dir, "root") + nginx_conf_dir = os.path.join(temp_dir, "conf") + nginx_conf = os.path.join(nginx_conf_dir, "nginx.conf") + nginx_pidfile = os.path.join(nginx_conf_dir, "nginx.pid") + file_contents = "Hello world!" for dirname in (nginx_root_dir, nginx_conf_dir): os.makedirs(dirname) # Write the temp file - with salt.utils.files.fopen(os.path.join(nginx_root_dir, 'actual_file'), 'w') as fp_: + with salt.utils.files.fopen( + os.path.join(nginx_root_dir, "actual_file"), "w" + ) as fp_: fp_.write(salt.utils.stringutils.to_str(file_contents)) # Write the nginx config - with salt.utils.files.fopen(nginx_conf, 'w') as fp_: - fp_.write(textwrap.dedent(salt.utils.stringutils.to_str( - '''\ + with salt.utils.files.fopen(nginx_conf, "w") as fp_: + fp_.write( + textwrap.dedent( + salt.utils.stringutils.to_str( + """\ user root; worker_processes 1; error_log {nginx_conf_dir}/server_error.log; @@ -572,52 +465,46 @@ class CPModuleTest(ModuleCase): return 302 /actual_file; }} }} - }}'''.format(**locals()) - ))) + }}""".format( + **locals() + ) + ) + ) + ) - self.run_function( - 'cmd.run', - [['nginx', '-c', nginx_conf]], - python_shell=False - ) + self.run_function("cmd.run", [["nginx", "-c", nginx_conf]], python_shell=False) with salt.utils.files.fopen(nginx_pidfile) as fp_: nginx_pid = int(fp_.read().strip()) nginx_proc = psutil.Process(pid=nginx_pid) self.addCleanup(nginx_proc.send_signal, signal.SIGQUIT) - for code in ('', '301', '302'): - url = url_prefix + (code or 'actual_file') - log.debug('attempting to cache %s', url) - ret = self.run_function('cp.cache_file', [url]) + for code in ("", "301", "302"): + url = url_prefix + (code or "actual_file") + log.debug("attempting to cache %s", url) + ret = self.run_function("cp.cache_file", [url]) self.assertTrue(ret) with salt.utils.files.fopen(ret) as fp_: cached_contents = salt.utils.stringutils.to_unicode(fp_.read()) self.assertEqual(cached_contents, file_contents) def test_list_states(self): - ''' + """ cp.list_states - ''' - ret = self.run_function( - 'cp.list_states', - ) - self.assertIn('core', ret) - self.assertIn('top', ret) + """ + ret = self.run_function("cp.list_states",) + self.assertIn("core", ret) + self.assertIn("top", ret) def test_list_minion(self): - ''' + """ cp.list_minion - ''' - self.run_function( - 'cp.cache_file', - [ - 'salt://grail/scene33', - ]) - ret = self.run_function('cp.list_minion') + """ + self.run_function("cp.cache_file", ["salt://grail/scene33"]) + ret = self.run_function("cp.list_minion") found = False - search = 'grail/scene33' + search = "grail/scene33" if salt.utils.platform.is_windows(): - search = r'grail\scene33' + search = r"grail\scene33" for path in ret: if search in path: found = True @@ -625,90 +512,72 @@ class CPModuleTest(ModuleCase): self.assertTrue(found) def test_is_cached(self): - ''' + """ cp.is_cached - ''' - self.run_function( - 'cp.cache_file', - [ - 'salt://grail/scene33', - ]) - ret1 = self.run_function( - 'cp.is_cached', - [ - 'salt://grail/scene33', - ]) + """ + self.run_function("cp.cache_file", ["salt://grail/scene33"]) + ret1 = self.run_function("cp.is_cached", ["salt://grail/scene33"]) self.assertTrue(ret1) - ret2 = self.run_function( - 'cp.is_cached', - [ - 'salt://fasldkgj/poicxzbn', - ]) + ret2 = self.run_function("cp.is_cached", ["salt://fasldkgj/poicxzbn"]) self.assertFalse(ret2) def test_hash_file(self): - ''' + """ cp.hash_file - ''' - sha256_hash = self.run_function( - 'cp.hash_file', - [ - 'salt://grail/scene33', - ]) - path = self.run_function( - 'cp.cache_file', - [ - 'salt://grail/scene33', - ]) - with salt.utils.files.fopen(path, 'rb') as fn_: + """ + sha256_hash = self.run_function("cp.hash_file", ["salt://grail/scene33"]) + path = self.run_function("cp.cache_file", ["salt://grail/scene33"]) + with salt.utils.files.fopen(path, "rb") as fn_: data = fn_.read() - self.assertEqual( - sha256_hash['hsum'], hashlib.sha256(data).hexdigest()) + self.assertEqual(sha256_hash["hsum"], hashlib.sha256(data).hexdigest()) @with_tempfile() def test_get_file_from_env_predefined(self, tgt): - ''' + """ cp.get_file - ''' - tgt = os.path.join(RUNTIME_VARS.TMP, 'cheese') + """ + tgt = os.path.join(RUNTIME_VARS.TMP, "cheese") try: - self.run_function('cp.get_file', ['salt://cheese', tgt]) - with salt.utils.files.fopen(tgt, 'r') as cheese: + self.run_function("cp.get_file", ["salt://cheese", tgt]) + with salt.utils.files.fopen(tgt, "r") as cheese: data = salt.utils.stringutils.to_unicode(cheese.read()) - self.assertIn('Gromit', data) - self.assertNotIn('Comte', data) + self.assertIn("Gromit", data) + self.assertNotIn("Comte", data) finally: os.unlink(tgt) @with_tempfile() def test_get_file_from_env_in_url(self, tgt): - tgt = os.path.join(RUNTIME_VARS.TMP, 'cheese') + tgt = os.path.join(RUNTIME_VARS.TMP, "cheese") try: - self.run_function('cp.get_file', ['salt://cheese?saltenv=prod', tgt]) - with salt.utils.files.fopen(tgt, 'r') as cheese: + self.run_function("cp.get_file", ["salt://cheese?saltenv=prod", tgt]) + with salt.utils.files.fopen(tgt, "r") as cheese: data = salt.utils.stringutils.to_unicode(cheese.read()) - self.assertIn('Gromit', data) - self.assertIn('Comte', data) + self.assertIn("Gromit", data) + self.assertIn("Comte", data) finally: os.unlink(tgt) def test_push(self): log_to_xfer = os.path.join(RUNTIME_VARS.TMP, uuid.uuid4().hex) - open(log_to_xfer, 'w').close() # pylint: disable=resource-leakage + open(log_to_xfer, "w").close() # pylint: disable=resource-leakage try: - self.run_function('cp.push', [log_to_xfer]) + self.run_function("cp.push", [log_to_xfer]) tgt_cache_file = os.path.join( - RUNTIME_VARS.TMP, - 'master-minion-root', - 'cache', - 'minions', - 'minion', - 'files', - RUNTIME_VARS.TMP, - log_to_xfer) - self.assertTrue(os.path.isfile(tgt_cache_file), 'File was not cached on the master') + RUNTIME_VARS.TMP, + "master-minion-root", + "cache", + "minions", + "minion", + "files", + RUNTIME_VARS.TMP, + log_to_xfer, + ) + self.assertTrue( + os.path.isfile(tgt_cache_file), "File was not cached on the master" + ) finally: os.unlink(tgt_cache_file) def test_envs(self): - self.assertEqual(sorted(self.run_function('cp.envs')), sorted(['base', 'prod'])) + self.assertEqual(sorted(self.run_function("cp.envs")), sorted(["base", "prod"])) diff --git a/tests/integration/modules/test_data.py b/tests/integration/modules/test_data.py index 416982cea71..5e630176bdc 100644 --- a/tests/integration/modules/test_data.py +++ b/tests/integration/modules/test_data.py @@ -1,45 +1,48 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ModuleCase +@pytest.mark.windows_whitelisted class DataModuleTest(ModuleCase): - ''' + """ Validate the data module - ''' + """ + def setUp(self): - self.run_function('data.clear') - self.addCleanup(self.run_function, 'data.clear') + self.run_function("data.clear") + self.addCleanup(self.run_function, "data.clear") def test_load_dump(self): - ''' + """ data.load data.dump - ''' - self.assertTrue(self.run_function('data.dump', ['{"foo": "bar"}'])) - self.assertEqual(self.run_function('data.load'), {'foo': 'bar'}) + """ + self.assertTrue(self.run_function("data.dump", ['{"foo": "bar"}'])) + self.assertEqual(self.run_function("data.load"), {"foo": "bar"}) def test_get_update(self): - ''' + """ data.get data.update - ''' - self.assertTrue(self.run_function('data.update', ['spam', 'eggs'])) - self.assertEqual(self.run_function('data.get', ['spam']), 'eggs') + """ + self.assertTrue(self.run_function("data.update", ["spam", "eggs"])) + self.assertEqual(self.run_function("data.get", ["spam"]), "eggs") - self.assertTrue(self.run_function('data.update', ['unladen', 'swallow'])) - self.assertEqual(self.run_function('data.get', [["spam", "unladen"]]), ['eggs', 'swallow']) + self.assertTrue(self.run_function("data.update", ["unladen", "swallow"])) + self.assertEqual( + self.run_function("data.get", [["spam", "unladen"]]), ["eggs", "swallow"] + ) def test_cas_update(self): - ''' + """ data.update data.cas data.get - ''' - self.assertTrue(self.run_function('data.update', ['spam', 'eggs'])) - self.assertTrue(self.run_function('data.cas', ['spam', 'green', 'eggs'])) - self.assertEqual(self.run_function('data.get', ['spam']), 'green') + """ + self.assertTrue(self.run_function("data.update", ["spam", "eggs"])) + self.assertTrue(self.run_function("data.cas", ["spam", "green", "eggs"])) + self.assertEqual(self.run_function("data.get", ["spam"]), "green") diff --git a/tests/integration/modules/test_decorators.py b/tests/integration/modules/test_decorators.py index aa233f9f582..49e66fbd335 100644 --- a/tests/integration/modules/test_decorators.py +++ b/tests/integration/modules/test_decorators.py @@ -1,108 +1,103 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ModuleCase +@pytest.mark.windows_whitelisted class DecoratorTest(ModuleCase): def test_module(self): - self.assertTrue( - self.run_function( - 'runtests_decorators.working_function' - ) - ) + self.assertTrue(self.run_function("runtests_decorators.working_function")) def test_depends(self): - ret = self.run_function('runtests_decorators.depends') + ret = self.run_function("runtests_decorators.depends") self.assertTrue(isinstance(ret, dict)) - self.assertTrue(ret['ret']) - self.assertTrue(isinstance(ret['time'], float)) + self.assertTrue(ret["ret"]) + self.assertTrue(isinstance(ret["time"], float)) def test_missing_depends(self): self.assertEqual( - {'runtests_decorators.missing_depends_will_fallback': None, - 'runtests_decorators.missing_depends': "'runtests_decorators.missing_depends' is not available."}, - self.run_function('runtests_decorators.missing_depends' - ) - ) + { + "runtests_decorators.missing_depends_will_fallback": None, + "runtests_decorators.missing_depends": "'runtests_decorators.missing_depends' is not available.", + }, + self.run_function("runtests_decorators.missing_depends"), + ) def test_bool_depends(self): # test True - self.assertTrue( - self.run_function( - 'runtests_decorators.booldependsTrue' - ) - ) + self.assertTrue(self.run_function("runtests_decorators.booldependsTrue")) # test False self.assertIn( - 'is not available', - self.run_function('runtests_decorators.booldependsFalse' - ) - ) + "is not available", + self.run_function("runtests_decorators.booldependsFalse"), + ) def test_depends_will_not_fallback(self): - ret = self.run_function('runtests_decorators.depends_will_not_fallback') + ret = self.run_function("runtests_decorators.depends_will_not_fallback") self.assertTrue(isinstance(ret, dict)) - self.assertTrue(ret['ret']) - self.assertTrue(isinstance(ret['time'], float)) + self.assertTrue(ret["ret"]) + self.assertTrue(isinstance(ret["time"], float)) def test_missing_depends_will_fallback(self): self.assertListEqual( - [False, 'fallback'], - self.run_function( - 'runtests_decorators.missing_depends_will_fallback' - ) - ) + [False, "fallback"], + self.run_function("runtests_decorators.missing_depends_will_fallback"), + ) def test_command_success_retcode(self): - ret = self.run_function('runtests_decorators.command_success_retcode') + ret = self.run_function("runtests_decorators.command_success_retcode") self.assertIs(ret, True) def test_command_failure_retcode(self): - ret = self.run_function('runtests_decorators.command_failure_retcode') + ret = self.run_function("runtests_decorators.command_failure_retcode") self.assertEqual( - ret, - "'runtests_decorators.command_failure_retcode' is not available." + ret, "'runtests_decorators.command_failure_retcode' is not available." ) def test_command_success_nonzero_retcode_true(self): - ret = self.run_function('runtests_decorators.command_success_nonzero_retcode_true') + ret = self.run_function( + "runtests_decorators.command_success_nonzero_retcode_true" + ) self.assertIs(ret, True) def test_command_failure_nonzero_retcode_true(self): - ret = self.run_function('runtests_decorators.command_failure_nonzero_retcode_true') + ret = self.run_function( + "runtests_decorators.command_failure_nonzero_retcode_true" + ) self.assertEqual( ret, - "'runtests_decorators.command_failure_nonzero_retcode_true' is not available." + "'runtests_decorators.command_failure_nonzero_retcode_true' is not available.", ) def test_command_success_nonzero_retcode_false(self): - ret = self.run_function('runtests_decorators.command_success_nonzero_retcode_false') + ret = self.run_function( + "runtests_decorators.command_success_nonzero_retcode_false" + ) self.assertIs(ret, True) def test_command_failure_nonzero_retcode_false(self): - ret = self.run_function('runtests_decorators.command_failure_nonzero_retcode_false') + ret = self.run_function( + "runtests_decorators.command_failure_nonzero_retcode_false" + ) self.assertEqual( ret, - "'runtests_decorators.command_failure_nonzero_retcode_false' is not available." + "'runtests_decorators.command_failure_nonzero_retcode_false' is not available.", ) def test_versioned_depend_insufficient(self): self.assertIn( - 'is not available', - self.run_function('runtests_decorators.version_depends_false') + "is not available", + self.run_function("runtests_decorators.version_depends_false"), ) def test_versioned_depend_sufficient(self): - self.assertTrue( - self.run_function('runtests_decorators.version_depends_true') - ) + self.assertTrue(self.run_function("runtests_decorators.version_depends_true")) def test_versioned_depend_versionless(self): self.assertTrue( - self.run_function('runtests_decorators.version_depends_versionless_true') + self.run_function("runtests_decorators.version_depends_versionless_true") ) diff --git a/tests/integration/modules/test_disk.py b/tests/integration/modules/test_disk.py index b9e3de8f2ba..a05c9b562f1 100644 --- a/tests/integration/modules/test_disk.py +++ b/tests/integration/modules/test_disk.py @@ -1,88 +1,88 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest - -# Import Salt libs +import pytest import salt.utils.platform - -# Import 3rd-party libs from salt.ext import six +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.unit import skipIf @destructiveTest -@skipIf(salt.utils.platform.is_darwin(), 'No mtab on Darwin') -@skipIf(salt.utils.platform.is_freebsd(), 'No mtab on FreeBSD') -@skipIf(salt.utils.platform.is_windows(), 'No mtab on Windows') +@pytest.mark.windows_whitelisted +@skipIf(salt.utils.platform.is_darwin(), "No mtab on Darwin") +@skipIf(salt.utils.platform.is_freebsd(), "No mtab on FreeBSD") +@skipIf(salt.utils.platform.is_windows(), "No mtab on Windows") class DiskModuleVirtualizationTest(ModuleCase): - ''' + """ Test to make sure we return a clean result under Docker. Refs #8976 This is factored into its own class so that we can have some certainty that setUp() and tearDown() are run. - ''' + """ + def setUp(self): # Make /etc/mtab unreadable - if os.path.isfile('/etc/mtab'): - shutil.move('/etc/mtab', '/tmp/mtab') + if os.path.isfile("/etc/mtab"): + shutil.move("/etc/mtab", "/tmp/mtab") def test_no_mtab(self): - ret = self.run_function('disk.usage') + ret = self.run_function("disk.usage") self.assertDictEqual(ret, {}) def tearDown(self): - if os.path.isfile('/tmp/mtab'): - shutil.move('/tmp/mtab', '/etc/mtab') + if os.path.isfile("/tmp/mtab"): + shutil.move("/tmp/mtab", "/etc/mtab") +@pytest.mark.windows_whitelisted class DiskModuleTest(ModuleCase): - ''' + """ Validate the disk module - ''' + """ + def test_usage(self): - ''' + """ disk.usage - ''' - ret = self.run_function('disk.usage') + """ + ret = self.run_function("disk.usage") self.assertTrue(isinstance(ret, dict)) if not isinstance(ret, dict): return if salt.utils.platform.is_darwin(): for key, val in six.iteritems(ret): - self.assertTrue('filesystem' in val) - self.assertTrue('512-blocks' in val) - self.assertTrue('used' in val) - self.assertTrue('available' in val) - self.assertTrue('capacity' in val) - self.assertTrue('iused' in val) - self.assertTrue('ifree' in val) - self.assertTrue('%iused' in val) + self.assertTrue("filesystem" in val) + self.assertTrue("512-blocks" in val) + self.assertTrue("used" in val) + self.assertTrue("available" in val) + self.assertTrue("capacity" in val) + self.assertTrue("iused" in val) + self.assertTrue("ifree" in val) + self.assertTrue("%iused" in val) else: for key, val in six.iteritems(ret): - self.assertTrue('filesystem' in val) - self.assertTrue('1K-blocks' in val) - self.assertTrue('used' in val) - self.assertTrue('available' in val) - self.assertTrue('capacity' in val) + self.assertTrue("filesystem" in val) + self.assertTrue("1K-blocks" in val) + self.assertTrue("used" in val) + self.assertTrue("available" in val) + self.assertTrue("capacity" in val) - @skipIf(salt.utils.platform.is_windows(), 'inode info not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "inode info not available on Windows") def test_inodeusage(self): - ''' + """ disk.inodeusage - ''' - ret = self.run_function('disk.inodeusage') + """ + ret = self.run_function("disk.inodeusage") self.assertTrue(isinstance(ret, dict)) if not isinstance(ret, dict): return for key, val in six.iteritems(ret): - self.assertTrue('inodes' in val) - self.assertTrue('used' in val) - self.assertTrue('free' in val) - self.assertTrue('use' in val) - self.assertTrue('filesystem' in val) + self.assertTrue("inodes" in val) + self.assertTrue("used" in val) + self.assertTrue("free" in val) + self.assertTrue("use" in val) + self.assertTrue("filesystem" in val) diff --git a/tests/integration/modules/test_dockermod.py b/tests/integration/modules/test_dockermod.py index c8288777b8b..24f32733fb6 100644 --- a/tests/integration/modules/test_dockermod.py +++ b/tests/integration/modules/test_dockermod.py @@ -1,28 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the docker_container states -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import random import string import sys -# Import Salt Testing Libs -from tests.support.unit import skipIf -from tests.support.case import ModuleCase -from tests.support.helpers import destructiveTest -from tests.support.mixins import SaltReturnAssertsMixin - # Import Salt Libs import salt.utils.path # Import 3rd-party libs from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.mixins import SaltReturnAssertsMixin + +# Import Salt Testing Libs +from tests.support.unit import skipIf -def _random_name(prefix=''): +def _random_name(prefix=""): ret = prefix for _ in range(8): ret += random.choice(string.ascii_lowercase) @@ -30,58 +31,58 @@ def _random_name(prefix=''): @destructiveTest -@skipIf(not salt.utils.path.which('dockerd'), 'Docker not installed') +@skipIf(not salt.utils.path.which("dockerd"), "Docker not installed") class DockerCallTestCase(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Test docker_container states - ''' + """ def setUp(self): - ''' + """ setup docker.call tests - ''' + """ # Create temp dir - self.random_name = _random_name(prefix='salt_test_') + self.random_name = _random_name(prefix="salt_test_") self.image_tag = sys.version_info[0] - self.run_state('docker_image.present', - tag=self.image_tag, - name='python') - self.run_state('docker_container.running', - name=self.random_name, - image='python:{0}'.format(self.image_tag), - entrypoint='tail -f /dev/null') + self.run_state("docker_image.present", tag=self.image_tag, name="python") + self.run_state( + "docker_container.running", + name=self.random_name, + image="python:{0}".format(self.image_tag), + entrypoint="tail -f /dev/null", + ) def tearDown(self): - ''' + """ teardown docker.call tests - ''' - self.run_state('docker_container.absent', - name=self.random_name, - force=True) - self.run_state('docker_image.absent', - images=['python:{0}'.format(self.image_tag)], - force=True) - delattr(self, 'random_name') - delattr(self, 'image_tag') + """ + self.run_state("docker_container.absent", name=self.random_name, force=True) + self.run_state( + "docker_image.absent", + images=["python:{0}".format(self.image_tag)], + force=True, + ) + delattr(self, "random_name") + delattr(self, "image_tag") def test_docker_call(self): - ''' + """ check that docker.call works, and works with a container not running as root - ''' - ret = self.run_function('docker.call', [self.random_name, 'test.ping']) + """ + ret = self.run_function("docker.call", [self.random_name, "test.ping"]) assert ret is True def test_docker_sls(self): - ''' + """ check that docker.sls works, and works with a container not running as root - ''' - ret = self.run_function('docker.apply', [self.random_name, 'core']) + """ + ret = self.run_function("docker.apply", [self.random_name, "core"]) self.assertSaltTrueReturn(ret) def test_docker_highstate(self): - ''' + """ check that docker.highstate works, and works with a container not running as root - ''' - ret = self.run_function('docker.apply', [self.random_name]) + """ + ret = self.run_function("docker.apply", [self.random_name]) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/modules/test_event.py b/tests/integration/modules/test_event.py index c3baefe2beb..e8040c046f3 100644 --- a/tests/integration/modules/test_event.py +++ b/tests/integration/modules/test_event.py @@ -1,55 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.modules.event ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import python libs from __future__ import absolute_import -import time -import threading -# Import Salt Testing libs +import threading +import time + +import pytest +import salt.utils.event as event +from salt.ext.six.moves.queue import Empty, Queue from tests.support.case import ModuleCase -# Import salt libs -import salt.utils.event as event - -# Import 3rd-party libs -from salt.ext.six.moves.queue import Queue, Empty # pylint: disable=import-error,no-name-in-module - +@pytest.mark.windows_whitelisted class EventModuleTest(ModuleCase): def __test_event_fire_master(self): events = Queue() def get_event(events): - me = event.MasterEvent(self.master_opts['sock_dir'], listen=True) - events.put_nowait( - me.get_event(wait=10, tag='salttest', full=False) - ) + me = event.MasterEvent(self.master_opts["sock_dir"], listen=True) + events.put_nowait(me.get_event(wait=10, tag="salttest", full=False)) threading.Thread(target=get_event, args=(events,)).start() - time.sleep(1) # Allow multiprocessing.Process to start + time.sleep(1) # Allow multiprocessing.Process to start ret = self.run_function( - 'event.fire_master', - ['event.fire_master: just test it!!!!', 'salttest'] + "event.fire_master", ["event.fire_master: just test it!!!!", "salttest"] ) self.assertTrue(ret) eventfired = events.get(block=True, timeout=10) self.assertIsNotNone(eventfired) - self.assertIn( - 'event.fire_master: just test it!!!!', eventfired['data'] - ) + self.assertIn("event.fire_master: just test it!!!!", eventfired["data"]) ret = self.run_function( - 'event.fire_master', - ['event.fire_master: just test it!!!!', 'salttest-miss'] + "event.fire_master", + ["event.fire_master: just test it!!!!", "salttest-miss"], ) self.assertTrue(ret) @@ -61,24 +53,22 @@ class EventModuleTest(ModuleCase): def get_event(events): me = event.MinionEvent(self.minion_opts, listen=True) - events.put_nowait( - me.get_event(wait=10, tag='salttest', full=False) - ) + events.put_nowait(me.get_event(wait=10, tag="salttest", full=False)) threading.Thread(target=get_event, args=(events,)).start() - time.sleep(1) # Allow multiprocessing.Process to start + time.sleep(1) # Allow multiprocessing.Process to start ret = self.run_function( - 'event.fire', ['event.fire: just test it!!!!', 'salttest'] + "event.fire", ["event.fire: just test it!!!!", "salttest"] ) self.assertTrue(ret) eventfired = events.get(block=True, timeout=10) self.assertIsNotNone(eventfired) - self.assertIn('event.fire: just test it!!!!', eventfired) + self.assertIn("event.fire: just test it!!!!", eventfired) ret = self.run_function( - 'event.fire', ['event.fire: just test it!!!!', 'salttest-miss'] + "event.fire", ["event.fire: just test it!!!!", "salttest-miss"] ) self.assertTrue(ret) @@ -90,26 +80,26 @@ class EventModuleTest(ModuleCase): def get_event(events): me = event.MinionEvent(self.sub_minion_opts, listen=True) - events.put_nowait( - me.get_event(wait=10, tag='salttest', full=False) - ) + events.put_nowait(me.get_event(wait=10, tag="salttest", full=False)) threading.Thread(target=get_event, args=(events,)).start() - time.sleep(1) # Allow multiprocessing.Process to start + time.sleep(1) # Allow multiprocessing.Process to start ret = self.run_function( - 'event.fire', ['event.fire: just test it!!!!', 'salttest'], - minion_tgt='sub_minion' + "event.fire", + ["event.fire: just test it!!!!", "salttest"], + minion_tgt="sub_minion", ) self.assertTrue(ret) eventfired = events.get(block=True, timeout=10) self.assertIsNotNone(eventfired) - self.assertIn('event.fire: just test it!!!!', eventfired) + self.assertIn("event.fire: just test it!!!!", eventfired) ret = self.run_function( - 'event.fire', ['event.fire: just test it!!!!', 'salttest-miss'], - minion_tgt='sub_minion' + "event.fire", + ["event.fire: just test it!!!!", "salttest-miss"], + minion_tgt="sub_minion", ) self.assertTrue(ret) diff --git a/tests/integration/modules/test_file.py b/tests/integration/modules/test_file.py index aac9272c8c2..a3f61b2b68f 100644 --- a/tests/integration/modules/test_file.py +++ b/tests/integration/modules/test_file.py @@ -1,12 +1,20 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + import getpass import os import shutil import sys +import pytest +import salt.utils.files +import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import requires_system_grains +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + # Posix only try: import grp @@ -20,46 +28,39 @@ try: except ImportError: pass -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf - -# Import salt libs -import salt.utils.files -import salt.utils.platform - def symlink(source, link_name): - ''' + """ Handle symlinks on Windows with Python < 3.2 - ''' + """ if salt.utils.platform.is_windows(): win32file.CreateSymbolicLink(link_name, source) else: os.symlink(source, link_name) +@pytest.mark.windows_whitelisted class FileModuleTest(ModuleCase): - ''' + """ Validate the file module - ''' + """ + def setUp(self): - self.myfile = os.path.join(RUNTIME_VARS.TMP, 'myfile') - with salt.utils.files.fopen(self.myfile, 'w+') as fp: - fp.write(salt.utils.stringutils.to_str('Hello' + os.linesep)) - self.mydir = os.path.join(RUNTIME_VARS.TMP, 'mydir/isawesome') + self.myfile = os.path.join(RUNTIME_VARS.TMP, "myfile") + with salt.utils.files.fopen(self.myfile, "w+") as fp: + fp.write(salt.utils.stringutils.to_str("Hello" + os.linesep)) + self.mydir = os.path.join(RUNTIME_VARS.TMP, "mydir/isawesome") if not os.path.isdir(self.mydir): # left behind... Don't fail because of this! os.makedirs(self.mydir) - self.mysymlink = os.path.join(RUNTIME_VARS.TMP, 'mysymlink') + self.mysymlink = os.path.join(RUNTIME_VARS.TMP, "mysymlink") if os.path.islink(self.mysymlink) or os.path.isfile(self.mysymlink): os.remove(self.mysymlink) symlink(self.myfile, self.mysymlink) - self.mybadsymlink = os.path.join(RUNTIME_VARS.TMP, 'mybadsymlink') + self.mybadsymlink = os.path.join(RUNTIME_VARS.TMP, "mybadsymlink") if os.path.islink(self.mybadsymlink) or os.path.isfile(self.mybadsymlink): os.remove(self.mybadsymlink) - symlink('/nonexistentpath', self.mybadsymlink) + symlink("/nonexistentpath", self.mybadsymlink) super(FileModuleTest, self).setUp() def tearDown(self): @@ -72,148 +73,195 @@ class FileModuleTest(ModuleCase): shutil.rmtree(self.mydir, ignore_errors=True) super(FileModuleTest, self).tearDown() - @skipIf(salt.utils.platform.is_windows(), 'No chgrp on Windows') + @skipIf(salt.utils.platform.is_windows(), "No security context on Windows") + @requires_system_grains + def test_get_selinux_context(self, grains): + if grains.get("selinux", {}).get("enabled", False): + NEW_CONTEXT = "system_u:object_r:system_conf_t:s0" + self.run_function( + "file.set_selinux_context", arg=[self.myfile, *(NEW_CONTEXT.split(":"))] + ) + ret_file = self.run_function("file.get_selinux_context", arg=[self.myfile]) + self.assertEqual(ret_file, NEW_CONTEXT) + + # Issue #56557. Ensure that the context of the directory + # containing one file is the context of the directory itself, and + # not the context of the first file in the directory. + self.run_function( + "file.set_selinux_context", arg=[self.mydir, *(NEW_CONTEXT.split(":"))] + ) + ret_dir = self.run_function("file.get_selinux_context", arg=[self.mydir]) + self.assertEqual(ret_dir, NEW_CONTEXT) + ret_updir = self.run_function( + "file.get_selinux_context", + arg=[os.path.abspath(os.path.join(self.mydir, ".."))], + ) + self.assertNotEqual(ret_updir, NEW_CONTEXT) + else: + ret_file = self.run_function("file.get_selinux_context", arg=[self.myfile]) + self.assertIn("No selinux context information is available", ret_file) + + @skipIf(salt.utils.platform.is_windows(), "No security context on Windows") + @requires_system_grains + def test_set_selinux_context(self, grains): + if not grains.get("selinux", {}).get("enabled", False): + self.skipTest("selinux not available") + + FILE_CONTEXT = "system_u:object_r:system_conf_t:s0" + ret_file = self.run_function( + "file.set_selinux_context", arg=[self.myfile, *(FILE_CONTEXT.split(":"))] + ) + self.assertEqual(ret_file, FILE_CONTEXT) + + DIR_CONTEXT = "system_u:object_r:user_home_t:s0" + ret_dir = self.run_function( + "file.set_selinux_context", arg=[self.mydir, *(DIR_CONTEXT.split(":"))] + ) + self.assertEqual(ret_dir, DIR_CONTEXT) + + @skipIf(salt.utils.platform.is_windows(), "No chgrp on Windows") def test_chown(self): user = getpass.getuser() - if sys.platform == 'darwin': - group = 'staff' - elif sys.platform.startswith(('linux', 'freebsd', 'openbsd')): + if sys.platform == "darwin": + group = "staff" + elif sys.platform.startswith(("linux", "freebsd", "openbsd")): group = grp.getgrgid(pwd.getpwuid(os.getuid()).pw_gid).gr_name - ret = self.run_function('file.chown', arg=[self.myfile, user, group]) + ret = self.run_function("file.chown", arg=[self.myfile, user, group]) self.assertIsNone(ret) fstat = os.stat(self.myfile) self.assertEqual(fstat.st_uid, os.getuid()) self.assertEqual(fstat.st_gid, grp.getgrnam(group).gr_gid) - @skipIf(salt.utils.platform.is_windows(), 'No chgrp on Windows') + @skipIf(salt.utils.platform.is_windows(), "No chgrp on Windows") def test_chown_no_user(self): - user = 'notanyuseriknow' + user = "notanyuseriknow" group = grp.getgrgid(pwd.getpwuid(os.getuid()).pw_gid).gr_name - ret = self.run_function('file.chown', arg=[self.myfile, user, group]) - self.assertIn('not exist', ret) + ret = self.run_function("file.chown", arg=[self.myfile, user, group]) + self.assertIn("not exist", ret) - @skipIf(salt.utils.platform.is_windows(), 'No chgrp on Windows') + @skipIf(salt.utils.platform.is_windows(), "No chgrp on Windows") def test_chown_no_user_no_group(self): - user = 'notanyuseriknow' - group = 'notanygroupyoushoulduse' - ret = self.run_function('file.chown', arg=[self.myfile, user, group]) - self.assertIn('Group does not exist', ret) - self.assertIn('User does not exist', ret) + user = "notanyuseriknow" + group = "notanygroupyoushoulduse" + ret = self.run_function("file.chown", arg=[self.myfile, user, group]) + self.assertIn("Group does not exist", ret) + self.assertIn("User does not exist", ret) - @skipIf(salt.utils.platform.is_windows(), 'No chgrp on Windows') + @skipIf(salt.utils.platform.is_windows(), "No chgrp on Windows") def test_chown_no_path(self): user = getpass.getuser() - if sys.platform == 'darwin': - group = 'staff' - elif sys.platform.startswith(('linux', 'freebsd', 'openbsd')): + if sys.platform == "darwin": + group = "staff" + elif sys.platform.startswith(("linux", "freebsd", "openbsd")): group = grp.getgrgid(pwd.getpwuid(os.getuid()).pw_gid).gr_name - ret = self.run_function('file.chown', - arg=['/tmp/nosuchfile', user, group]) - self.assertIn('File not found', ret) + ret = self.run_function("file.chown", arg=["/tmp/nosuchfile", user, group]) + self.assertIn("File not found", ret) - @skipIf(salt.utils.platform.is_windows(), 'No chgrp on Windows') + @skipIf(salt.utils.platform.is_windows(), "No chgrp on Windows") def test_chown_noop(self): - user = '' - group = '' - ret = self.run_function('file.chown', arg=[self.myfile, user, group]) + user = "" + group = "" + ret = self.run_function("file.chown", arg=[self.myfile, user, group]) self.assertIsNone(ret) fstat = os.stat(self.myfile) self.assertEqual(fstat.st_uid, os.getuid()) self.assertEqual(fstat.st_gid, os.getgid()) - @skipIf(salt.utils.platform.is_windows(), 'No chgrp on Windows') + @skipIf(salt.utils.platform.is_windows(), "No chgrp on Windows") def test_chgrp(self): - if sys.platform == 'darwin': - group = 'everyone' - elif sys.platform.startswith(('linux', 'freebsd', 'openbsd')): + if sys.platform == "darwin": + group = "everyone" + elif sys.platform.startswith(("linux", "freebsd", "openbsd")): group = grp.getgrgid(pwd.getpwuid(os.getuid()).pw_gid).gr_name - ret = self.run_function('file.chgrp', arg=[self.myfile, group]) + ret = self.run_function("file.chgrp", arg=[self.myfile, group]) self.assertIsNone(ret) fstat = os.stat(self.myfile) self.assertEqual(fstat.st_gid, grp.getgrnam(group).gr_gid) - @skipIf(salt.utils.platform.is_windows(), 'No chgrp on Windows') + @skipIf(salt.utils.platform.is_windows(), "No chgrp on Windows") def test_chgrp_failure(self): - group = 'thisgroupdoesntexist' - ret = self.run_function('file.chgrp', arg=[self.myfile, group]) - self.assertIn('not exist', ret) + group = "thisgroupdoesntexist" + ret = self.run_function("file.chgrp", arg=[self.myfile, group]) + self.assertIn("not exist", ret) def test_patch(self): - if not self.run_function('cmd.has_exec', ['patch']): - self.skipTest('patch is not installed') + if not self.run_function("cmd.has_exec", ["patch"]): + self.skipTest("patch is not installed") - src_patch = os.path.join( - RUNTIME_VARS.FILES, 'file', 'base', 'hello.patch') - src_file = os.path.join(RUNTIME_VARS.TMP, 'src.txt') - with salt.utils.files.fopen(src_file, 'w+') as fp: - fp.write(salt.utils.stringutils.to_str('Hello\n')) + src_patch = os.path.join(RUNTIME_VARS.FILES, "file", "base", "hello.patch") + src_file = os.path.join(RUNTIME_VARS.TMP, "src.txt") + with salt.utils.files.fopen(src_file, "w+") as fp: + fp.write(salt.utils.stringutils.to_str("Hello\n")) # dry-run should not modify src_file - ret = self.minion_run('file.patch', src_file, src_patch, dry_run=True) - assert ret['retcode'] == 0, repr(ret) + ret = self.minion_run("file.patch", src_file, src_patch, dry_run=True) + assert ret["retcode"] == 0, repr(ret) with salt.utils.files.fopen(src_file) as fp: - self.assertEqual( - salt.utils.stringutils.to_unicode(fp.read()), 'Hello\n') + self.assertEqual(salt.utils.stringutils.to_unicode(fp.read()), "Hello\n") - ret = self.minion_run('file.patch', src_file, src_patch) - assert ret['retcode'] == 0, repr(ret) + ret = self.minion_run("file.patch", src_file, src_patch) + assert ret["retcode"] == 0, repr(ret) with salt.utils.files.fopen(src_file) as fp: self.assertEqual( - salt.utils.stringutils.to_unicode(fp.read()), 'Hello world\n') + salt.utils.stringutils.to_unicode(fp.read()), "Hello world\n" + ) def test_remove_file(self): - ret = self.run_function('file.remove', arg=[self.myfile]) + ret = self.run_function("file.remove", arg=[self.myfile]) self.assertTrue(ret) def test_remove_dir(self): - ret = self.run_function('file.remove', arg=[self.mydir]) + ret = self.run_function("file.remove", arg=[self.mydir]) self.assertTrue(ret) def test_remove_symlink(self): - ret = self.run_function('file.remove', arg=[self.mysymlink]) + ret = self.run_function("file.remove", arg=[self.mysymlink]) self.assertTrue(ret) def test_remove_broken_symlink(self): - ret = self.run_function('file.remove', arg=[self.mybadsymlink]) + ret = self.run_function("file.remove", arg=[self.mybadsymlink]) self.assertTrue(ret) def test_cannot_remove(self): - ret = self.run_function('file.remove', arg=['tty']) + ret = self.run_function("file.remove", arg=["tty"]) self.assertEqual( - 'ERROR executing \'file.remove\': File path must be absolute: tty', ret + "ERROR executing 'file.remove': File path must be absolute: tty", ret ) def test_source_list_for_single_file_returns_unchanged(self): - ret = self.run_function('file.source_list', ['salt://http/httpd.conf', - 'filehash', 'base']) - self.assertEqual(list(ret), ['salt://http/httpd.conf', 'filehash']) + ret = self.run_function( + "file.source_list", ["salt://http/httpd.conf", "filehash", "base"] + ) + self.assertEqual(list(ret), ["salt://http/httpd.conf", "filehash"]) def test_source_list_for_single_local_file_slash_returns_unchanged(self): - ret = self.run_function('file.source_list', [self.myfile, - 'filehash', 'base']) - self.assertEqual(list(ret), [self.myfile, 'filehash']) + ret = self.run_function("file.source_list", [self.myfile, "filehash", "base"]) + self.assertEqual(list(ret), [self.myfile, "filehash"]) def test_source_list_for_single_local_file_proto_returns_unchanged(self): - ret = self.run_function('file.source_list', ['file://' + self.myfile, - 'filehash', 'base']) - self.assertEqual(list(ret), ['file://' + self.myfile, 'filehash']) + ret = self.run_function( + "file.source_list", ["file://" + self.myfile, "filehash", "base"] + ) + self.assertEqual(list(ret), ["file://" + self.myfile, "filehash"]) def test_file_line_changes_format(self): - ''' + """ Test file.line changes output formatting. Issue #41474 - ''' - ret = self.minion_run('file.line', self.myfile, 'Goodbye', - mode='insert', after='Hello') - self.assertIn('Hello' + os.linesep + '+Goodbye', ret) + """ + ret = self.minion_run( + "file.line", self.myfile, "Goodbye", mode="insert", after="Hello" + ) + self.assertIn("Hello" + os.linesep + "+Goodbye", ret) def test_file_line_content(self): - self.minion_run('file.line', self.myfile, 'Goodbye', - mode='insert', after='Hello') - with salt.utils.files.fopen(self.myfile, 'r') as fp: + self.minion_run( + "file.line", self.myfile, "Goodbye", mode="insert", after="Hello" + ) + with salt.utils.files.fopen(self.myfile, "r") as fp: content = fp.read() - self.assertEqual(content, 'Hello' + os.linesep + 'Goodbye' + os.linesep) + self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep) def test_file_line_duplicate_insert_after(self): """ @@ -221,13 +269,14 @@ class FileModuleTest(ModuleCase): Issue #50254 """ - with salt.utils.files.fopen(self.myfile, 'a') as fp: - fp.write(salt.utils.stringutils.to_str('Goodbye' + os.linesep)) - self.minion_run('file.line', self.myfile, 'Goodbye', - mode='insert', after='Hello') - with salt.utils.files.fopen(self.myfile, 'r') as fp: + with salt.utils.files.fopen(self.myfile, "a") as fp: + fp.write(salt.utils.stringutils.to_str("Goodbye" + os.linesep)) + self.minion_run( + "file.line", self.myfile, "Goodbye", mode="insert", after="Hello" + ) + with salt.utils.files.fopen(self.myfile, "r") as fp: content = fp.read() - self.assertEqual(content, 'Hello' + os.linesep + 'Goodbye' + os.linesep) + self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep) def test_file_line_duplicate_insert_before(self): """ @@ -235,13 +284,14 @@ class FileModuleTest(ModuleCase): Issue #50254 """ - with salt.utils.files.fopen(self.myfile, 'a') as fp: - fp.write(salt.utils.stringutils.to_str('Goodbye' + os.linesep)) - self.minion_run('file.line', self.myfile, 'Hello', - mode='insert', before='Goodbye') - with salt.utils.files.fopen(self.myfile, 'r') as fp: + with salt.utils.files.fopen(self.myfile, "a") as fp: + fp.write(salt.utils.stringutils.to_str("Goodbye" + os.linesep)) + self.minion_run( + "file.line", self.myfile, "Hello", mode="insert", before="Goodbye" + ) + with salt.utils.files.fopen(self.myfile, "r") as fp: content = fp.read() - self.assertEqual(content, 'Hello' + os.linesep + 'Goodbye' + os.linesep) + self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep) def test_file_line_duplicate_ensure_after(self): """ @@ -249,13 +299,14 @@ class FileModuleTest(ModuleCase): Issue #50254 """ - with salt.utils.files.fopen(self.myfile, 'a') as fp: - fp.write(salt.utils.stringutils.to_str('Goodbye' + os.linesep)) - self.minion_run('file.line', self.myfile, 'Goodbye', - mode='ensure', after='Hello') - with salt.utils.files.fopen(self.myfile, 'r') as fp: + with salt.utils.files.fopen(self.myfile, "a") as fp: + fp.write(salt.utils.stringutils.to_str("Goodbye" + os.linesep)) + self.minion_run( + "file.line", self.myfile, "Goodbye", mode="ensure", after="Hello" + ) + with salt.utils.files.fopen(self.myfile, "r") as fp: content = fp.read() - self.assertEqual(content, 'Hello' + os.linesep + 'Goodbye' + os.linesep) + self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep) def test_file_line_duplicate_ensure_before(self): """ @@ -263,10 +314,11 @@ class FileModuleTest(ModuleCase): Issue #50254 """ - with salt.utils.files.fopen(self.myfile, 'a') as fp: - fp.write(salt.utils.stringutils.to_str('Goodbye' + os.linesep)) - self.minion_run('file.line', self.myfile, 'Hello', - mode='ensure', before='Goodbye') - with salt.utils.files.fopen(self.myfile, 'r') as fp: + with salt.utils.files.fopen(self.myfile, "a") as fp: + fp.write(salt.utils.stringutils.to_str("Goodbye" + os.linesep)) + self.minion_run( + "file.line", self.myfile, "Hello", mode="ensure", before="Goodbye" + ) + with salt.utils.files.fopen(self.myfile, "r") as fp: content = fp.read() - self.assertEqual(content, 'Hello' + os.linesep + 'Goodbye' + os.linesep) + self.assertEqual(content, "Hello" + os.linesep + "Goodbye" + os.linesep) diff --git a/tests/integration/modules/test_gem.py b/tests/integration/modules/test_gem.py index 88603cabf05..073f90813b3 100644 --- a/tests/integration/modules/test_gem.py +++ b/tests/integration/modules/test_gem.py @@ -1,144 +1,147 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for Ruby Gem module -''' +""" -# Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest - -# Import salt libs +import pytest import salt.utils.path - -# Import 3rd-party libs from salt.ext.tornado.httpclient import HTTPClient +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.unit import skipIf def check_status(): - ''' + """ Check the status of the rubygems source - ''' + """ try: - return HTTPClient().fetch('https://rubygems.org').code == 200 + return HTTPClient().fetch("https://rubygems.org").code == 200 except Exception: # pylint: disable=broad-except return False @destructiveTest -@skipIf(not salt.utils.path.which('gem'), 'Gem is not available') +@skipIf(not salt.utils.path.which("gem"), "Gem is not available") +@pytest.mark.windows_whitelisted class GemModuleTest(ModuleCase): - ''' + """ Validate gem module - ''' + """ def setUp(self): if check_status() is False: - self.skipTest('External resource \'https://rubygems.org\' is not available') + self.skipTest("External resource 'https://rubygems.org' is not available") - self.GEM = 'tidy' - self.GEM_VER = '1.1.2' - self.OLD_GEM = 'brass' - self.OLD_VERSION = '1.0.0' - self.NEW_VERSION = '1.2.1' + self.GEM = "tidy" + self.GEM_VER = "1.1.2" + self.OLD_GEM = "brass" + self.OLD_VERSION = "1.0.0" + self.NEW_VERSION = "1.2.1" self.GEM_LIST = [self.GEM, self.OLD_GEM] - for name in ('GEM', 'GEM_VER', 'OLD_GEM', 'OLD_VERSION', 'NEW_VERSION', 'GEM_LIST'): + for name in ( + "GEM", + "GEM_VER", + "OLD_GEM", + "OLD_VERSION", + "NEW_VERSION", + "GEM_LIST", + ): self.addCleanup(delattr, self, name) def uninstall_gem(): # Remove gem if it is already installed - if self.run_function('gem.list', [self.GEM]): - self.run_function('gem.uninstall', [self.GEM]) + if self.run_function("gem.list", [self.GEM]): + self.run_function("gem.uninstall", [self.GEM]) self.addCleanup(uninstall_gem) def test_install_uninstall(self): - ''' + """ gem.install gem.uninstall - ''' - self.run_function('gem.install', [self.GEM]) - gem_list = self.run_function('gem.list', [self.GEM]) + """ + self.run_function("gem.install", [self.GEM]) + gem_list = self.run_function("gem.list", [self.GEM]) self.assertIn(self.GEM, gem_list) - self.run_function('gem.uninstall', [self.GEM]) - self.assertFalse(self.run_function('gem.list', [self.GEM])) + self.run_function("gem.uninstall", [self.GEM]) + self.assertFalse(self.run_function("gem.list", [self.GEM])) def test_install_version(self): - ''' + """ gem.install rake version=11.1.2 - ''' - self.run_function('gem.install', [self.GEM], version=self.GEM_VER) - gem_list = self.run_function('gem.list', [self.GEM]) + """ + self.run_function("gem.install", [self.GEM], version=self.GEM_VER) + gem_list = self.run_function("gem.list", [self.GEM]) self.assertIn(self.GEM, gem_list) self.assertIn(self.GEM_VER, gem_list[self.GEM]) - self.run_function('gem.uninstall', [self.GEM]) - self.assertFalse(self.run_function('gem.list', [self.GEM])) + self.run_function("gem.uninstall", [self.GEM]) + self.assertFalse(self.run_function("gem.list", [self.GEM])) def test_list(self): - ''' + """ gem.list - ''' - self.run_function('gem.install', [' '.join(self.GEM_LIST)]) + """ + self.run_function("gem.install", [" ".join(self.GEM_LIST)]) - all_ret = self.run_function('gem.list') + all_ret = self.run_function("gem.list") for gem in self.GEM_LIST: self.assertIn(gem, all_ret) - single_ret = self.run_function('gem.list', [self.GEM]) + single_ret = self.run_function("gem.list", [self.GEM]) self.assertIn(self.GEM, single_ret) - self.run_function('gem.uninstall', [' '.join(self.GEM_LIST)]) + self.run_function("gem.uninstall", [" ".join(self.GEM_LIST)]) def test_list_upgrades(self): - ''' + """ gem.list_upgrades - ''' + """ # install outdated gem - self.run_function('gem.install', [self.OLD_GEM], version=self.OLD_VERSION) + self.run_function("gem.install", [self.OLD_GEM], version=self.OLD_VERSION) - ret = self.run_function('gem.list_upgrades') + ret = self.run_function("gem.list_upgrades") self.assertIn(self.OLD_GEM, ret) - self.run_function('gem.uninstall', [self.OLD_GEM]) + self.run_function("gem.uninstall", [self.OLD_GEM]) def test_sources_add_remove(self): - ''' + """ gem.sources_add gem.sources_remove - ''' - source = 'http://production.cf.rubygems.org' + """ + source = "http://production.cf.rubygems.org" - self.run_function('gem.sources_add', [source]) - sources_list = self.run_function('gem.sources_list') + self.run_function("gem.sources_add", [source]) + sources_list = self.run_function("gem.sources_list") self.assertIn(source, sources_list) - self.run_function('gem.sources_remove', [source]) - sources_list = self.run_function('gem.sources_list') + self.run_function("gem.sources_remove", [source]) + sources_list = self.run_function("gem.sources_list") self.assertNotIn(source, sources_list) def test_update(self): - ''' + """ gem.update - ''' - self.run_function('gem.install', [self.OLD_GEM], version=self.OLD_VERSION) - gem_list = self.run_function('gem.list', [self.OLD_GEM]) + """ + self.run_function("gem.install", [self.OLD_GEM], version=self.OLD_VERSION) + gem_list = self.run_function("gem.list", [self.OLD_GEM]) self.assertEqual({self.OLD_GEM: [self.OLD_VERSION]}, gem_list) - self.run_function('gem.update', [self.OLD_GEM]) - gem_list = self.run_function('gem.list', [self.OLD_GEM]) + self.run_function("gem.update", [self.OLD_GEM]) + gem_list = self.run_function("gem.list", [self.OLD_GEM]) self.assertEqual({self.OLD_GEM: [self.NEW_VERSION, self.OLD_VERSION]}, gem_list) - self.run_function('gem.uninstall', [self.OLD_GEM]) - self.assertFalse(self.run_function('gem.list', [self.OLD_GEM])) + self.run_function("gem.uninstall", [self.OLD_GEM]) + self.assertFalse(self.run_function("gem.list", [self.OLD_GEM])) def test_update_system(self): - ''' + """ gem.update_system - ''' - ret = self.run_function('gem.update_system') + """ + ret = self.run_function("gem.update_system") self.assertTrue(ret) diff --git a/tests/integration/modules/test_gentoolkitmod.py b/tests/integration/modules/test_gentoolkitmod.py index 55b3dde9f83..920c4bc7ad0 100644 --- a/tests/integration/modules/test_gentoolkitmod.py +++ b/tests/integration/modules/test_gentoolkitmod.py @@ -1,22 +1,22 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ModuleCase +@pytest.mark.windows_whitelisted class GentoolkitModuleTest(ModuleCase): def setUp(self): - ''' + """ Set up test environment - ''' + """ super(GentoolkitModuleTest, self).setUp() - ret_grain = self.run_function('grains.item', ['os']) - if ret_grain['os'] not in 'Gentoo': - self.skipTest('For Gentoo only') + ret_grain = self.run_function("grains.item", ["os"]) + if ret_grain["os"] not in "Gentoo": + self.skipTest("For Gentoo only") def test_revdep_rebuild_true(self): - ret = self.run_function('gentoolkit.revdep_rebuild') + ret = self.run_function("gentoolkit.revdep_rebuild") self.assertTrue(ret) diff --git a/tests/integration/modules/test_git.py b/tests/integration/modules/test_git.py index fa9512cc0e9..bc4df37ea85 100644 --- a/tests/integration/modules/test_git.py +++ b/tests/integration/modules/test_git.py @@ -1,15 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Tests for git execution module NOTE: These tests may modify the global git config, and have been marked as destructive as a result. If no values are set for user.name or user.email in the user's global .gitconfig, then these tests will set one. -''' +""" -# Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -from contextlib import closing + import errno import logging import os @@ -18,21 +17,18 @@ import shutil import subprocess import tarfile import tempfile +from contextlib import closing -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import skip_if_binaries_missing - -# Import salt libs +import pytest import salt.utils.data import salt.utils.files import salt.utils.platform -from salt.utils.versions import LooseVersion - -# Import 3rd-party libs from salt.ext import six +from salt.utils.versions import LooseVersion +from tests.support.case import ModuleCase +from tests.support.helpers import skip_if_binaries_missing +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf log = logging.getLogger(__name__) @@ -40,29 +36,30 @@ log = logging.getLogger(__name__) def _git_version(): try: git_version = subprocess.Popen( - ['git', '--version'], + ["git", "--version"], shell=False, close_fds=False if salt.utils.platform.is_windows() else True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate()[0] + stderr=subprocess.PIPE, + ).communicate()[0] except OSError: return False if not git_version: - log.debug('Git not installed') + log.debug("Git not installed") return False git_version = git_version.strip().split()[-1] if six.PY3: git_version = git_version.decode(__salt_system_encoding__) - log.debug('Detected git version: %s', git_version) + log.debug("Detected git version: %s", git_version) return LooseVersion(git_version) def _worktrees_supported(): - ''' + """ Check if the git version is 2.5.0 or later - ''' + """ try: - return _git_version() >= LooseVersion('2.5.0') + return _git_version() >= LooseVersion("2.5.0") except AttributeError: return False @@ -76,142 +73,161 @@ def _makedirs(path): raise -@skip_if_binaries_missing('git') +@skip_if_binaries_missing("git") +@pytest.mark.windows_whitelisted class GitModuleTest(ModuleCase): - def setUp(self): super(GitModuleTest, self).setUp() self.orig_cwd = os.getcwd() self.addCleanup(os.chdir, self.orig_cwd) self.repo = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.repo, ignore_errors=True) - self.files = ('foo', 'bar', 'baz', 'питон') - self.dirs = ('', 'qux') - self.branches = ('master', 'iamanewbranch') - self.tags = ('git_testing',) + self.files = ("foo", "bar", "baz", "питон") + self.dirs = ("", "qux") + self.branches = ("master", "iamanewbranch") + self.tags = ("git_testing",) for dirname in self.dirs: dir_path = os.path.join(self.repo, dirname) _makedirs(dir_path) for filename in self.files: - with salt.utils.files.fopen(os.path.join(dir_path, filename), 'wb') as fp_: + with salt.utils.files.fopen( + os.path.join(dir_path, filename), "wb" + ) as fp_: fp_.write( - 'This is a test file named {0}.'.format(filename).encode('utf-8') + "This is a test file named {0}.".format(filename).encode( + "utf-8" + ) ) # Navigate to the root of the repo to init, stage, and commit os.chdir(self.repo) # Initialize a new git repository - subprocess.check_call(['git', 'init', '--quiet', self.repo]) + subprocess.check_call(["git", "init", "--quiet", self.repo]) # Set user.name and user.email config attributes if not present - for key, value in (('user.name', 'Jenkins'), - ('user.email', 'qa@saltstack.com')): + for key, value in ( + ("user.name", "Jenkins"), + ("user.email", "qa@saltstack.com"), + ): # Check if key is missing keycheck = subprocess.Popen( - ['git', 'config', '--get', '--global', key], + ["git", "config", "--get", "--global", key], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + stderr=subprocess.PIPE, + ) if keycheck.wait() != 0: # Set the key if it is not present - subprocess.check_call( - ['git', 'config', '--global', key, value]) + subprocess.check_call(["git", "config", "--global", key, value]) - subprocess.check_call(['git', 'add', '.']) + subprocess.check_call(["git", "add", "."]) subprocess.check_call( - ['git', 'commit', '--quiet', '--message', 'Initial commit'] + ["git", "commit", "--quiet", "--message", "Initial commit"] ) # Add a tag - subprocess.check_call( - ['git', 'tag', '-a', self.tags[0], '-m', 'Add tag'] - ) + subprocess.check_call(["git", "tag", "-a", self.tags[0], "-m", "Add tag"]) # Checkout a second branch - subprocess.check_call( - ['git', 'checkout', '--quiet', '-b', self.branches[1]] - ) + subprocess.check_call(["git", "checkout", "--quiet", "-b", self.branches[1]]) # Add a line to the file - with salt.utils.files.fopen(self.files[0], 'a') as fp_: - fp_.write(salt.utils.stringutils.to_str('Added a line\n')) + with salt.utils.files.fopen(self.files[0], "a") as fp_: + fp_.write(salt.utils.stringutils.to_str("Added a line\n")) # Commit the updated file subprocess.check_call( - ['git', 'commit', '--quiet', - '--message', 'Added a line to ' + self.files[0], self.files[0]] + [ + "git", + "commit", + "--quiet", + "--message", + "Added a line to " + self.files[0], + self.files[0], + ] ) # Switch back to master - subprocess.check_call(['git', 'checkout', '--quiet', 'master']) + subprocess.check_call(["git", "checkout", "--quiet", "master"]) # Go back to original cwd os.chdir(self.orig_cwd) def run_function(self, *args, **kwargs): # pylint: disable=arguments-differ - ''' + """ Ensure that results are decoded TODO: maybe move this behavior to ModuleCase itself? - ''' + """ return salt.utils.data.decode( super(GitModuleTest, self).run_function(*args, **kwargs) ) def tearDown(self): - for key in ('orig_cwd', 'repo', 'files', 'dirs', 'branches', 'tags'): + for key in ("orig_cwd", "repo", "files", "dirs", "branches", "tags"): delattr(self, key) super(GitModuleTest, self).tearDown() def test_add_dir(self): - ''' + """ Test git.add with a directory - ''' - newdir = 'quux' + """ + newdir = "quux" # Change to the repo dir newdir_path = os.path.join(self.repo, newdir) _makedirs(newdir_path) files = [os.path.join(newdir_path, x) for x in self.files] files_relpath = [os.path.join(newdir, x) for x in self.files] for path in files: - with salt.utils.files.fopen(path, 'wb') as fp_: + with salt.utils.files.fopen(path, "wb") as fp_: fp_.write( - 'This is a test file with relative path {0}.\n'.format(path).encode('utf-8') + "This is a test file with relative path {0}.\n".format(path).encode( + "utf-8" + ) ) - ret = self.run_function('git.add', [self.repo, newdir]) - res = '\n'.join(sorted(['add \'{0}\''.format(x) for x in files_relpath])) + ret = self.run_function("git.add", [self.repo, newdir]) + res = "\n".join(sorted(["add '{0}'".format(x) for x in files_relpath])) if salt.utils.platform.is_windows(): - res = res.replace('\\', '/') + res = res.replace("\\", "/") self.assertEqual(ret, res) def test_add_file(self): - ''' + """ Test git.add with a file - ''' - filename = 'quux' + """ + filename = "quux" file_path = os.path.join(self.repo, filename) - with salt.utils.files.fopen(file_path, 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str( - 'This is a test file named {0}.\n'.format(filename) - )) - ret = self.run_function('git.add', [self.repo, filename]) - self.assertEqual(ret, 'add \'{0}\''.format(filename)) + with salt.utils.files.fopen(file_path, "w") as fp_: + fp_.write( + salt.utils.stringutils.to_str( + "This is a test file named {0}.\n".format(filename) + ) + ) + ret = self.run_function("git.add", [self.repo, filename]) + self.assertEqual(ret, "add '{0}'".format(filename)) def test_archive(self): - ''' + """ Test git.archive - ''' - tar_archive = os.path.join(RUNTIME_VARS.TMP, 'test_archive.tar.gz') + """ + tar_archive = os.path.join(RUNTIME_VARS.TMP, "test_archive.tar.gz") try: self.assertTrue( self.run_function( - 'git.archive', - [self.repo, tar_archive], - prefix='foo/' + "git.archive", [self.repo, tar_archive], prefix="foo/" ) ) self.assertTrue(tarfile.is_tarfile(tar_archive)) - self.run_function('cmd.run', ['cp ' + tar_archive + ' /root/']) - with closing(tarfile.open(tar_archive, 'r')) as tar_obj: + self.run_function("cmd.run", ["cp " + tar_archive + " /root/"]) + with closing(tarfile.open(tar_archive, "r")) as tar_obj: self.assertEqual( sorted(salt.utils.data.decode(tar_obj.getnames())), - sorted([ - 'foo', 'foo/bar', 'foo/baz', 'foo/foo', 'foo/питон', - 'foo/qux', 'foo/qux/bar', 'foo/qux/baz', 'foo/qux/foo', - 'foo/qux/питон' - ]) + sorted( + [ + "foo", + "foo/bar", + "foo/baz", + "foo/foo", + "foo/питон", + "foo/qux", + "foo/qux/bar", + "foo/qux/baz", + "foo/qux/foo", + "foo/qux/питон", + ] + ), ) finally: try: @@ -220,24 +236,24 @@ class GitModuleTest(ModuleCase): pass def test_archive_subdir(self): - ''' + """ Test git.archive on a subdir, giving only a partial copy of the repo in the resulting archive - ''' - tar_archive = os.path.join(RUNTIME_VARS.TMP, 'test_archive.tar.gz') + """ + tar_archive = os.path.join(RUNTIME_VARS.TMP, "test_archive.tar.gz") try: self.assertTrue( self.run_function( - 'git.archive', - [os.path.join(self.repo, 'qux'), tar_archive], - prefix='foo/' + "git.archive", + [os.path.join(self.repo, "qux"), tar_archive], + prefix="foo/", ) ) self.assertTrue(tarfile.is_tarfile(tar_archive)) - with closing(tarfile.open(tar_archive, 'r')) as tar_obj: + with closing(tarfile.open(tar_archive, "r")) as tar_obj: self.assertEqual( sorted(salt.utils.data.decode(tar_obj.getnames())), - sorted(['foo', 'foo/bar', 'foo/baz', 'foo/foo', 'foo/питон']) + sorted(["foo", "foo/bar", "foo/baz", "foo/foo", "foo/питон"]), ) finally: try: @@ -246,375 +262,309 @@ class GitModuleTest(ModuleCase): pass def test_branch(self): - ''' + """ Test creating, renaming, and deleting a branch using git.branch - ''' - renamed_branch = 'ihavebeenrenamed' - self.assertTrue( - self.run_function('git.branch', [self.repo, self.branches[1]]) - ) + """ + renamed_branch = "ihavebeenrenamed" + self.assertTrue(self.run_function("git.branch", [self.repo, self.branches[1]])) self.assertTrue( self.run_function( - 'git.branch', - [self.repo, renamed_branch], - opts='-m ' + self.branches[1] + "git.branch", [self.repo, renamed_branch], opts="-m " + self.branches[1] ) ) self.assertTrue( - self.run_function( - 'git.branch', - [self.repo, renamed_branch], - opts='-D' - ) + self.run_function("git.branch", [self.repo, renamed_branch], opts="-D") ) def test_checkout(self): - ''' + """ Test checking out a new branch and then checking out master again - ''' - new_branch = 'iamanothernewbranch' + """ + new_branch = "iamanothernewbranch" self.assertEqual( self.run_function( - 'git.checkout', - [self.repo, 'HEAD'], - opts='-b ' + new_branch + "git.checkout", [self.repo, "HEAD"], opts="-b " + new_branch ), - 'Switched to a new branch \'' + new_branch + '\'' + "Switched to a new branch '" + new_branch + "'", ) self.assertTrue( - 'Switched to branch \'master\'' in - self.run_function('git.checkout', [self.repo, 'master']), + "Switched to branch 'master'" + in self.run_function("git.checkout", [self.repo, "master"]), ) def test_checkout_no_rev(self): - ''' + """ Test git.checkout without a rev, both with -b in opts and without - ''' - new_branch = 'iamanothernewbranch' + """ + new_branch = "iamanothernewbranch" self.assertEqual( self.run_function( - 'git.checkout', [self.repo], rev=None, opts='-b ' + new_branch + "git.checkout", [self.repo], rev=None, opts="-b " + new_branch ), - 'Switched to a new branch \'' + new_branch + '\'' + "Switched to a new branch '" + new_branch + "'", ) self.assertTrue( - '\'rev\' argument is required unless -b or -B in opts' in - self.run_function('git.checkout', [self.repo]) + "'rev' argument is required unless -b or -B in opts" + in self.run_function("git.checkout", [self.repo]) ) def test_clone(self): - ''' + """ Test cloning an existing repo - ''' + """ clone_parent_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - self.assertTrue( - self.run_function('git.clone', [clone_parent_dir, self.repo]) - ) + self.assertTrue(self.run_function("git.clone", [clone_parent_dir, self.repo])) # Cleanup after yourself shutil.rmtree(clone_parent_dir, True) def test_clone_with_alternate_name(self): - ''' + """ Test cloning an existing repo with an alternate name for the repo dir - ''' + """ clone_parent_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) clone_name = os.path.basename(self.repo) # Change to newly-created temp dir self.assertTrue( self.run_function( - 'git.clone', - [clone_parent_dir, self.repo], - name=clone_name + "git.clone", [clone_parent_dir, self.repo], name=clone_name ) ) # Cleanup after yourself shutil.rmtree(clone_parent_dir, True) def test_commit(self): - ''' + """ Test git.commit two ways: 1) First using git.add, then git.commit 2) Using git.commit with the 'filename' argument to skip staging - ''' - filename = 'foo' - commit_re_prefix = r'^\[master [0-9a-f]+\] ' + """ + filename = "foo" + commit_re_prefix = r"^\[master [0-9a-f]+\] " # Add a line - with salt.utils.files.fopen(os.path.join(self.repo, filename), 'a') as fp_: - fp_.write('Added a line\n') + with salt.utils.files.fopen(os.path.join(self.repo, filename), "a") as fp_: + fp_.write("Added a line\n") # Stage the file - self.run_function('git.add', [self.repo, filename]) + self.run_function("git.add", [self.repo, filename]) # Commit the staged file - commit_msg = 'Add a line to ' + filename - ret = self.run_function('git.commit', [self.repo, commit_msg]) + commit_msg = "Add a line to " + filename + ret = self.run_function("git.commit", [self.repo, commit_msg]) # Make sure the expected line is in the output self.assertTrue(bool(re.search(commit_re_prefix + commit_msg, ret))) # Add another line - with salt.utils.files.fopen(os.path.join(self.repo, filename), 'a') as fp_: - fp_.write('Added another line\n') + with salt.utils.files.fopen(os.path.join(self.repo, filename), "a") as fp_: + fp_.write("Added another line\n") # Commit the second file without staging - commit_msg = 'Add another line to ' + filename + commit_msg = "Add another line to " + filename ret = self.run_function( - 'git.commit', - [self.repo, commit_msg], - filename=filename + "git.commit", [self.repo, commit_msg], filename=filename ) self.assertTrue(bool(re.search(commit_re_prefix + commit_msg, ret))) def test_config(self): - ''' + """ Test setting, getting, and unsetting config values WARNING: This test will modify and completely remove a config section 'foo', both in the repo created in setUp() and in the user's global .gitconfig. - ''' + """ + def _clear_config(): cmds = ( - ['git', 'config', '--remove-section', 'foo'], - ['git', 'config', '--global', '--remove-section', 'foo'] + ["git", "config", "--remove-section", "foo"], + ["git", "config", "--global", "--remove-section", "foo"], ) for cmd in cmds: - with salt.utils.files.fopen(os.devnull, 'w') as devnull: + with salt.utils.files.fopen(os.devnull, "w") as devnull: try: subprocess.check_call(cmd, stderr=devnull) except subprocess.CalledProcessError: pass - cfg_local = { - 'foo.single': ['foo'], - 'foo.multi': ['foo', 'bar', 'baz'] - } - cfg_global = { - 'foo.single': ['abc'], - 'foo.multi': ['abc', 'def', 'ghi'] - } + cfg_local = {"foo.single": ["foo"], "foo.multi": ["foo", "bar", "baz"]} + cfg_global = {"foo.single": ["abc"], "foo.multi": ["abc", "def", "ghi"]} _clear_config() try: - log.debug( - 'Try to specify both single and multivar (should raise error)' - ) + log.debug("Try to specify both single and multivar (should raise error)") self.assertTrue( - 'Only one of \'value\' and \'multivar\' is permitted' in - self.run_function( - 'git.config_set', - ['foo.single'], - value=cfg_local['foo.single'][0], - multivar=cfg_local['foo.multi'], - cwd=self.repo + "Only one of 'value' and 'multivar' is permitted" + in self.run_function( + "git.config_set", + ["foo.single"], + value=cfg_local["foo.single"][0], + multivar=cfg_local["foo.multi"], + cwd=self.repo, ) ) log.debug( - 'Try to set single local value without cwd (should raise ' - 'error)' + "Try to set single local value without cwd (should raise " "error)" ) self.assertTrue( - '\'cwd\' argument required unless global=True' in - self.run_function( - 'git.config_set', - ['foo.single'], - value=cfg_local['foo.single'][0], + "'cwd' argument required unless global=True" + in self.run_function( + "git.config_set", ["foo.single"], value=cfg_local["foo.single"][0], ) ) - log.debug('Set single local value') + log.debug("Set single local value") self.assertEqual( self.run_function( - 'git.config_set', - ['foo.single'], - value=cfg_local['foo.single'][0], - cwd=self.repo - ), - cfg_local['foo.single'] - ) - log.debug('Set single global value') - self.assertEqual( - self.run_function( - 'git.config_set', - ['foo.single'], - value=cfg_global['foo.single'][0], - **{'global': True} - ), - cfg_global['foo.single'] - ) - log.debug('Set local multivar') - self.assertEqual( - self.run_function( - 'git.config_set', - ['foo.multi'], - multivar=cfg_local['foo.multi'], - cwd=self.repo - ), - cfg_local['foo.multi'] - ) - log.debug('Set global multivar') - self.assertEqual( - self.run_function( - 'git.config_set', - ['foo.multi'], - multivar=cfg_global['foo.multi'], - **{'global': True} - ), - cfg_global['foo.multi'] - ) - log.debug('Get single local value') - self.assertEqual( - self.run_function( - 'git.config_get', - ['foo.single'], - cwd=self.repo - ), - cfg_local['foo.single'][0] - ) - log.debug('Get single value from local multivar') - self.assertEqual( - self.run_function( - 'git.config_get', - ['foo.multi'], - cwd=self.repo - ), - cfg_local['foo.multi'][-1] - ) - log.debug('Get all values from multivar (includes globals)') - self.assertEqual( - self.run_function( - 'git.config_get', - ['foo.multi'], + "git.config_set", + ["foo.single"], + value=cfg_local["foo.single"][0], cwd=self.repo, - **{'all': True} ), - cfg_local['foo.multi'] + cfg_local["foo.single"], ) - log.debug('Get single global value') + log.debug("Set single global value") self.assertEqual( self.run_function( - 'git.config_get', - ['foo.single'], - **{'global': True} + "git.config_set", + ["foo.single"], + value=cfg_global["foo.single"][0], + **{"global": True} ), - cfg_global['foo.single'][0] + cfg_global["foo.single"], ) - log.debug('Get single value from global multivar') + log.debug("Set local multivar") self.assertEqual( self.run_function( - 'git.config_get', - ['foo.multi'], - **{'global': True} - ), - cfg_global['foo.multi'][-1] - ) - log.debug('Get all values from global multivar') - self.assertEqual( - self.run_function( - 'git.config_get', - ['foo.multi'], - **{'all': True, 'global': True} - ), - cfg_global['foo.multi'] - ) - log.debug('Get all local keys/values using regex') - self.assertEqual( - self.run_function( - 'git.config_get_regexp', - ['foo.(single|multi)'], - cwd=self.repo - ), - cfg_local - ) - log.debug('Get all global keys/values using regex') - self.assertEqual( - self.run_function( - 'git.config_get_regexp', - ['foo.(single|multi)'], + "git.config_set", + ["foo.multi"], + multivar=cfg_local["foo.multi"], cwd=self.repo, - **{'global': True} ), - cfg_global + cfg_local["foo.multi"], ) - log.debug('Get just the local foo.multi values containing \'a\'') + log.debug("Set global multivar") self.assertEqual( self.run_function( - 'git.config_get_regexp', - ['foo.multi'], - value_regex='a', - cwd=self.repo + "git.config_set", + ["foo.multi"], + multivar=cfg_global["foo.multi"], + **{"global": True} ), - {'foo.multi': [x for x in cfg_local['foo.multi'] if 'a' in x]} + cfg_global["foo.multi"], ) - log.debug('Get just the global foo.multi values containing \'a\'') + log.debug("Get single local value") + self.assertEqual( + self.run_function("git.config_get", ["foo.single"], cwd=self.repo), + cfg_local["foo.single"][0], + ) + log.debug("Get single value from local multivar") + self.assertEqual( + self.run_function("git.config_get", ["foo.multi"], cwd=self.repo), + cfg_local["foo.multi"][-1], + ) + log.debug("Get all values from multivar (includes globals)") self.assertEqual( self.run_function( - 'git.config_get_regexp', - ['foo.multi'], - value_regex='a', + "git.config_get", ["foo.multi"], cwd=self.repo, **{"all": True} + ), + cfg_local["foo.multi"], + ) + log.debug("Get single global value") + self.assertEqual( + self.run_function("git.config_get", ["foo.single"], **{"global": True}), + cfg_global["foo.single"][0], + ) + log.debug("Get single value from global multivar") + self.assertEqual( + self.run_function("git.config_get", ["foo.multi"], **{"global": True}), + cfg_global["foo.multi"][-1], + ) + log.debug("Get all values from global multivar") + self.assertEqual( + self.run_function( + "git.config_get", ["foo.multi"], **{"all": True, "global": True} + ), + cfg_global["foo.multi"], + ) + log.debug("Get all local keys/values using regex") + self.assertEqual( + self.run_function( + "git.config_get_regexp", ["foo.(single|multi)"], cwd=self.repo + ), + cfg_local, + ) + log.debug("Get all global keys/values using regex") + self.assertEqual( + self.run_function( + "git.config_get_regexp", + ["foo.(single|multi)"], cwd=self.repo, - **{'global': True} + **{"global": True} ), - {'foo.multi': [x for x in cfg_global['foo.multi'] if 'a' in x]} + cfg_global, + ) + log.debug("Get just the local foo.multi values containing 'a'") + self.assertEqual( + self.run_function( + "git.config_get_regexp", + ["foo.multi"], + value_regex="a", + cwd=self.repo, + ), + {"foo.multi": [x for x in cfg_local["foo.multi"] if "a" in x]}, + ) + log.debug("Get just the global foo.multi values containing 'a'") + self.assertEqual( + self.run_function( + "git.config_get_regexp", + ["foo.multi"], + value_regex="a", + cwd=self.repo, + **{"global": True} + ), + {"foo.multi": [x for x in cfg_global["foo.multi"] if "a" in x]}, ) # TODO: More robust unset testing, try to trigger all the # exceptions raised. - log.debug('Unset a single local value') + log.debug("Unset a single local value") + self.assertTrue( + self.run_function("git.config_unset", ["foo.single"], cwd=self.repo,) + ) + log.debug("Unset an entire local multivar") self.assertTrue( self.run_function( - 'git.config_unset', - ['foo.single'], - cwd=self.repo, + "git.config_unset", ["foo.multi"], cwd=self.repo, **{"all": True} ) ) - log.debug('Unset an entire local multivar') + log.debug("Unset a single global value") self.assertTrue( self.run_function( - 'git.config_unset', - ['foo.multi'], - cwd=self.repo, - **{'all': True} + "git.config_unset", ["foo.single"], **{"global": True} ) ) - log.debug('Unset a single global value') + log.debug("Unset an entire local multivar") self.assertTrue( self.run_function( - 'git.config_unset', - ['foo.single'], - **{'global': True} - ) - ) - log.debug('Unset an entire local multivar') - self.assertTrue( - self.run_function( - 'git.config_unset', - ['foo.multi'], - **{'all': True, 'global': True} + "git.config_unset", ["foo.multi"], **{"all": True, "global": True} ) ) finally: _clear_config() def test_current_branch(self): - ''' + """ Test git.current_branch - ''' - self.assertEqual( - self.run_function('git.current_branch', [self.repo]), - 'master' - ) + """ + self.assertEqual(self.run_function("git.current_branch", [self.repo]), "master") def test_describe(self): - ''' + """ Test git.describe - ''' - self.assertEqual( - self.run_function('git.describe', [self.repo]), - self.tags[0] - ) + """ + self.assertEqual(self.run_function("git.describe", [self.repo]), self.tags[0]) # Test for git.fetch would be unreliable on Jenkins, skipping for now # The test should go into test_remotes when ready def test_init(self): - ''' + """ Use git.init to init a new repo - ''' + """ new_repo = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # `tempfile.mkdtemp` gets the path to the Temp directory using @@ -625,78 +575,70 @@ class GitModuleTest(ModuleCase): # the path returned by `tempfile.mkdtemp` and the results of `git.init` # exactly. if salt.utils.platform.is_windows(): - new_repo = new_repo.replace('\\', '/') + new_repo = new_repo.replace("\\", "/") # Get the name of the temp directory tmp_dir = os.path.basename(new_repo) # Get git output - git_ret = self.run_function('git.init', [new_repo]).lower() + git_ret = self.run_function("git.init", [new_repo]).lower() - self.assertIn( - 'Initialized empty Git repository in'.lower(), git_ret) + self.assertIn("Initialized empty Git repository in".lower(), git_ret) self.assertIn(tmp_dir, git_ret) else: self.assertEqual( - self.run_function('git.init', [new_repo]).lower(), - 'Initialized empty Git repository in {0}/.git/'.format(new_repo).lower() + self.run_function("git.init", [new_repo]).lower(), + "Initialized empty Git repository in {0}/.git/".format( + new_repo + ).lower(), ) shutil.rmtree(new_repo) def test_list_branches(self): - ''' + """ Test git.list_branches - ''' + """ self.assertEqual( - self.run_function('git.list_branches', [self.repo]), - sorted(self.branches) + self.run_function("git.list_branches", [self.repo]), sorted(self.branches) ) def test_list_tags(self): - ''' + """ Test git.list_tags - ''' + """ self.assertEqual( - self.run_function('git.list_tags', [self.repo]), - sorted(self.tags) + self.run_function("git.list_tags", [self.repo]), sorted(self.tags) ) # Test for git.ls_remote will need to wait for now, while I think of how to # properly mock it. def test_merge(self): - ''' + """ Test git.merge # TODO: Test more than just a fast-forward merge - ''' + """ # Merge the second branch into the current branch - ret = self.run_function( - 'git.merge', - [self.repo], - rev=self.branches[1] - ) + ret = self.run_function("git.merge", [self.repo], rev=self.branches[1]) # Merge should be a fast-forward - self.assertTrue('Fast-forward' in ret.splitlines()) + self.assertTrue("Fast-forward" in ret.splitlines()) def test_merge_base_and_tree(self): - ''' + """ Test git.merge_base, git.merge_tree and git.revision TODO: Test all of the arguments - ''' + """ # Get the SHA1 of current HEAD - head_rev = self.run_function('git.revision', [self.repo], rev='HEAD') + head_rev = self.run_function("git.revision", [self.repo], rev="HEAD") # Make sure revision is a 40-char string self.assertTrue(len(head_rev) == 40) # Get the second branch's SHA1 second_rev = self.run_function( - 'git.revision', - [self.repo], - rev=self.branches[1], - timeout=120 + "git.revision", [self.repo], rev=self.branches[1], timeout=120 ) # Make sure revision is a 40-char string self.assertTrue(len(second_rev) == 40) @@ -705,55 +647,50 @@ class GitModuleTest(ModuleCase): # HEAD. self.assertEqual( self.run_function( - 'git.merge_base', - [self.repo], - refs=','.join((head_rev, second_rev)) + "git.merge_base", [self.repo], refs=",".join((head_rev, second_rev)) ), - head_rev + head_rev, ) # There should be no conflict here, so the return should be an empty # string. ret = self.run_function( - 'git.merge_tree', - [self.repo, head_rev, second_rev] + "git.merge_tree", [self.repo, head_rev, second_rev] ).splitlines() - self.assertTrue(len([x for x in ret if x.startswith('@@')]) == 1) + self.assertTrue(len([x for x in ret if x.startswith("@@")]) == 1) # Test for git.pull would be unreliable on Jenkins, skipping for now # Test for git.push would be unreliable on Jenkins, skipping for now def test_rebase(self): - ''' + """ Test git.rebase - ''' + """ # Make a change to a different file than the one modifed in setUp file_path = os.path.join(self.repo, self.files[1]) - with salt.utils.files.fopen(file_path, 'a') as fp_: - fp_.write('Added a line\n') + with salt.utils.files.fopen(file_path, "a") as fp_: + fp_.write("Added a line\n") # Commit the change self.assertTrue( - 'ERROR' not in self.run_function( - 'git.commit', - [self.repo, 'Added a line to ' + self.files[1]], - filename=self.files[1] + "ERROR" + not in self.run_function( + "git.commit", + [self.repo, "Added a line to " + self.files[1]], + filename=self.files[1], ) ) # Switch to the second branch self.assertTrue( - 'ERROR' not in self.run_function( - 'git.checkout', - [self.repo], - rev=self.branches[1] - ) + "ERROR" + not in self.run_function("git.checkout", [self.repo], rev=self.branches[1]) ) # Perform the rebase. The commit should show a comment about # self.files[0] being modified, as that is the file that was modified # in the second branch in the setUp function self.assertEqual( - self.run_function('git.rebase', [self.repo]), - 'First, rewinding head to replay your work on top of it...\n' - 'Applying: Added a line to ' + self.files[0] + self.run_function("git.rebase", [self.repo]), + "First, rewinding head to replay your work on top of it...\n" + "Applying: Added a line to " + self.files[0], ) # Test for git.remote_get is in test_remotes @@ -761,213 +698,184 @@ class GitModuleTest(ModuleCase): # Test for git.remote_set is in test_remotes def test_remotes(self): - ''' + """ Test setting a remote (git.remote_set), and getting a remote (git.remote_get and git.remotes) TODO: Properly mock fetching a remote (git.fetch), and build out more robust testing that confirms that the https auth bits work. - ''' + """ remotes = { - 'first': {'fetch': '/dev/null', 'push': '/dev/null'}, - 'second': {'fetch': '/dev/null', 'push': '/dev/stdout'} + "first": {"fetch": "/dev/null", "push": "/dev/null"}, + "second": {"fetch": "/dev/null", "push": "/dev/stdout"}, } self.assertEqual( self.run_function( - 'git.remote_set', - [self.repo, remotes['first']['fetch']], - remote='first' + "git.remote_set", [self.repo, remotes["first"]["fetch"]], remote="first" ), - remotes['first'] + remotes["first"], ) self.assertEqual( self.run_function( - 'git.remote_set', - [self.repo, remotes['second']['fetch']], - remote='second', - push_url=remotes['second']['push'] + "git.remote_set", + [self.repo, remotes["second"]["fetch"]], + remote="second", + push_url=remotes["second"]["push"], ), - remotes['second'] - ) - self.assertEqual( - self.run_function('git.remotes', [self.repo]), - remotes + remotes["second"], ) + self.assertEqual(self.run_function("git.remotes", [self.repo]), remotes) def test_reset(self): - ''' + """ Test git.reset TODO: Test more than just a hard reset - ''' + """ # Switch to the second branch self.assertTrue( - 'ERROR' not in self.run_function( - 'git.checkout', - [self.repo], - rev=self.branches[1] - ) + "ERROR" + not in self.run_function("git.checkout", [self.repo], rev=self.branches[1]) ) # Back up one commit. We should now be at the same revision as master - self.run_function( - 'git.reset', - [self.repo], - opts='--hard HEAD~1' - ) + self.run_function("git.reset", [self.repo], opts="--hard HEAD~1") # Get the SHA1 of current HEAD (remember, we're on the second branch) - head_rev = self.run_function('git.revision', [self.repo], rev='HEAD') + head_rev = self.run_function("git.revision", [self.repo], rev="HEAD") # Make sure revision is a 40-char string self.assertTrue(len(head_rev) == 40) # Get the master branch's SHA1 - master_rev = self.run_function( - 'git.revision', - [self.repo], - rev='master' - ) + master_rev = self.run_function("git.revision", [self.repo], rev="master") # Make sure revision is a 40-char string self.assertTrue(len(master_rev) == 40) # The two revisions should be the same self.assertEqual(head_rev, master_rev) def test_rev_parse(self): - ''' + """ Test git.rev_parse - ''' + """ # Using --abbrev-ref on HEAD will give us the current branch self.assertEqual( self.run_function( - 'git.rev_parse', [self.repo, 'HEAD'], opts='--abbrev-ref' + "git.rev_parse", [self.repo, "HEAD"], opts="--abbrev-ref" ), - 'master' + "master", ) # Test for git.revision happens in test_merge_base def test_rm(self): - ''' + """ Test git.rm - ''' + """ single_file = self.files[0] entire_dir = self.dirs[1] # Remove a single file self.assertEqual( - self.run_function('git.rm', [self.repo, single_file]), - 'rm \'' + single_file + '\'' + self.run_function("git.rm", [self.repo, single_file]), + "rm '" + single_file + "'", ) # Remove an entire dir - expected = '\n'.join( - sorted(['rm \'' + os.path.join(entire_dir, x) + '\'' - for x in self.files]) + expected = "\n".join( + sorted(["rm '" + os.path.join(entire_dir, x) + "'" for x in self.files]) ) if salt.utils.platform.is_windows(): - expected = expected.replace('\\', '/') + expected = expected.replace("\\", "/") self.assertEqual( - self.run_function('git.rm', [self.repo, entire_dir], opts='-r'), - expected + self.run_function("git.rm", [self.repo, entire_dir], opts="-r"), expected ) def test_stash(self): - ''' + """ Test git.stash # TODO: test more stash actions - ''' + """ file_path = os.path.join(self.repo, self.files[0]) - with salt.utils.files.fopen(file_path, 'a') as fp_: - fp_.write('Temp change to be stashed') - self.assertTrue( - 'ERROR' not in self.run_function('git.stash', [self.repo]) - ) + with salt.utils.files.fopen(file_path, "a") as fp_: + fp_.write("Temp change to be stashed") + self.assertTrue("ERROR" not in self.run_function("git.stash", [self.repo])) # List stashes - ret = self.run_function('git.stash', [self.repo], action='list') - self.assertTrue('ERROR' not in ret) + ret = self.run_function("git.stash", [self.repo], action="list") + self.assertTrue("ERROR" not in ret) self.assertTrue(len(ret.splitlines()) == 1) # Apply the stash self.assertTrue( - 'ERROR' not in self.run_function( - 'git.stash', - [self.repo], - action='apply', - opts='stash@{0}' + "ERROR" + not in self.run_function( + "git.stash", [self.repo], action="apply", opts="stash@{0}" ) ) # Drop the stash self.assertTrue( - 'ERROR' not in self.run_function( - 'git.stash', - [self.repo], - action='drop', - opts='stash@{0}' + "ERROR" + not in self.run_function( + "git.stash", [self.repo], action="drop", opts="stash@{0}" ) ) def test_status(self): - ''' + """ Test git.status - ''' + """ changes = { - 'modified': ['foo'], - 'new': ['thisisdefinitelyanewfile'], - 'deleted': ['bar'], - 'untracked': ['thisisalsoanewfile'] + "modified": ["foo"], + "new": ["thisisdefinitelyanewfile"], + "deleted": ["bar"], + "untracked": ["thisisalsoanewfile"], } - for filename in changes['modified']: - with salt.utils.files.fopen(os.path.join(self.repo, filename), 'a') as fp_: - fp_.write('Added a line\n') - for filename in changes['new']: - with salt.utils.files.fopen(os.path.join(self.repo, filename), 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str( - 'This is a new file named {0}.'.format(filename) - )) + for filename in changes["modified"]: + with salt.utils.files.fopen(os.path.join(self.repo, filename), "a") as fp_: + fp_.write("Added a line\n") + for filename in changes["new"]: + with salt.utils.files.fopen(os.path.join(self.repo, filename), "w") as fp_: + fp_.write( + salt.utils.stringutils.to_str( + "This is a new file named {0}.".format(filename) + ) + ) # Stage the new file so it shows up as a 'new' file self.assertTrue( - 'ERROR' not in self.run_function( - 'git.add', - [self.repo, filename] - ) + "ERROR" not in self.run_function("git.add", [self.repo, filename]) ) - for filename in changes['deleted']: - self.run_function('git.rm', [self.repo, filename]) - for filename in changes['untracked']: - with salt.utils.files.fopen(os.path.join(self.repo, filename), 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str( - 'This is a new file named {0}.'.format(filename) - )) - self.assertEqual( - self.run_function('git.status', [self.repo]), - changes - ) + for filename in changes["deleted"]: + self.run_function("git.rm", [self.repo, filename]) + for filename in changes["untracked"]: + with salt.utils.files.fopen(os.path.join(self.repo, filename), "w") as fp_: + fp_.write( + salt.utils.stringutils.to_str( + "This is a new file named {0}.".format(filename) + ) + ) + self.assertEqual(self.run_function("git.status", [self.repo]), changes) # TODO: Add git.submodule test def test_symbolic_ref(self): - ''' + """ Test git.symbolic_ref - ''' + """ self.assertEqual( - self.run_function( - 'git.symbolic_ref', - [self.repo, 'HEAD'], - opts='--quiet' - ), - 'refs/heads/master' + self.run_function("git.symbolic_ref", [self.repo, "HEAD"], opts="--quiet"), + "refs/heads/master", ) - @skipIf(not _worktrees_supported(), - 'Git 2.5 or newer required for worktree support') + @skipIf( + not _worktrees_supported(), "Git 2.5 or newer required for worktree support" + ) def test_worktree_add_rm(self): - ''' + """ This tests git.worktree_add, git.is_worktree, git.worktree_rm, and git.worktree_prune. Tests for 'git worktree list' are covered in tests.unit.modules.git_test. - ''' + """ # We don't need to enclose this comparison in a try/except, since the # decorator would skip this test if git is not installed and we'd never # get here in the first place. - if _git_version() >= LooseVersion('2.6.0'): - worktree_add_prefix = 'Preparing ' + if _git_version() >= LooseVersion("2.6.0"): + worktree_add_prefix = "Preparing " else: - worktree_add_prefix = 'Enter ' + worktree_add_prefix = "Enter " worktree_path = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) worktree_basename = os.path.basename(worktree_path) @@ -976,43 +884,36 @@ class GitModuleTest(ModuleCase): # Even though this is Windows, git commands return a unix style path if salt.utils.platform.is_windows(): - worktree_path = worktree_path.replace('\\', '/') - worktree_path2 = worktree_path2.replace('\\', '/') + worktree_path = worktree_path.replace("\\", "/") + worktree_path2 = worktree_path2.replace("\\", "/") # Add the worktrees - ret = self.run_function( - 'git.worktree_add', [self.repo, worktree_path], - ) + ret = self.run_function("git.worktree_add", [self.repo, worktree_path],) self.assertTrue(worktree_add_prefix in ret) self.assertTrue(worktree_basename in ret) - ret = self.run_function( - 'git.worktree_add', [self.repo, worktree_path2] - ) + ret = self.run_function("git.worktree_add", [self.repo, worktree_path2]) self.assertTrue(worktree_add_prefix in ret) self.assertTrue(worktree_basename2 in ret) # Check if this new path is a worktree - self.assertTrue(self.run_function('git.is_worktree', [worktree_path])) + self.assertTrue(self.run_function("git.is_worktree", [worktree_path])) # Check if the main repo is a worktree - self.assertFalse(self.run_function('git.is_worktree', [self.repo])) + self.assertFalse(self.run_function("git.is_worktree", [self.repo])) # Check if a non-repo directory is a worktree empty_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - self.assertFalse(self.run_function('git.is_worktree', [empty_dir])) + self.assertFalse(self.run_function("git.is_worktree", [empty_dir])) shutil.rmtree(empty_dir) # Remove the first worktree - self.assertTrue(self.run_function('git.worktree_rm', [worktree_path])) + self.assertTrue(self.run_function("git.worktree_rm", [worktree_path])) # Prune the worktrees prune_message = ( - 'Removing worktrees/{0}: gitdir file points to non-existent ' - 'location'.format(worktree_basename) + "Removing worktrees/{0}: gitdir file points to non-existent " + "location".format(worktree_basename) ) # Test dry run output. It should match the same output we get when we # actually prune the worktrees. - result = self.run_function('git.worktree_prune', - [self.repo], - dry_run=True) + result = self.run_function("git.worktree_prune", [self.repo], dry_run=True) self.assertEqual(result, prune_message) # Test pruning for real, and make sure the output is the same self.assertEqual( - self.run_function('git.worktree_prune', [self.repo]), - prune_message + self.run_function("git.worktree_prune", [self.repo]), prune_message ) diff --git a/tests/integration/modules/test_grains.py b/tests/integration/modules/test_grains.py index c15a1e8c98c..7d9a8367f75 100644 --- a/tests/integration/modules/test_grains.py +++ b/tests/integration/modules/test_grains.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Test the grains module -''' +""" -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os -import time import pprint +import time -# Import Salt libs +import pytest from salt.ext.six.moves import range - -# Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.helpers import flaky from tests.support.unit import skipIf @@ -21,207 +19,212 @@ from tests.support.unit import skipIf log = logging.getLogger(__name__) +@pytest.mark.windows_whitelisted class TestModulesGrains(ModuleCase): - ''' + """ Test the grains module - ''' + """ + def test_items(self): - ''' + """ grains.items - ''' + """ opts = self.minion_opts self.assertEqual( - self.run_function('grains.items')['test_grain'], - opts['grains']['test_grain'] + self.run_function("grains.items")["test_grain"], + opts["grains"]["test_grain"], ) def test_item(self): - ''' + """ grains.item - ''' + """ opts = self.minion_opts self.assertEqual( - self.run_function('grains.item', ['test_grain'])['test_grain'], - opts['grains']['test_grain'] + self.run_function("grains.item", ["test_grain"])["test_grain"], + opts["grains"]["test_grain"], ) def test_ls(self): - ''' + """ grains.ls - ''' + """ check_for = ( - 'cpu_flags', - 'cpu_model', - 'cpuarch', - 'domain', - 'fqdn', - 'fqdns', - 'gid', - 'groupname', - 'host', - 'kernel', - 'kernelrelease', - 'kernelversion', - 'localhost', - 'mem_total', - 'num_cpus', - 'os', - 'os_family', - 'path', - 'pid', - 'ps', - 'pythonpath', - 'pythonversion', - 'saltpath', - 'saltversion', - 'uid', - 'username', - 'virtual', + "cpu_flags", + "cpu_model", + "cpuarch", + "domain", + "fqdn", + "fqdns", + "gid", + "groupname", + "host", + "kernel", + "kernelrelease", + "kernelversion", + "localhost", + "mem_total", + "num_cpus", + "os", + "os_family", + "path", + "pid", + "ps", + "pythonpath", + "pythonversion", + "saltpath", + "saltversion", + "uid", + "username", + "virtual", ) - lsgrains = self.run_function('grains.ls') - os = self.run_function('grains.get', ['os']) + lsgrains = self.run_function("grains.ls") + os = self.run_function("grains.get", ["os"]) for grain in check_for: - if os == 'Windows' and grain in ['cpu_flags', 'gid', 'groupname', 'uid']: + if os == "Windows" and grain in ["cpu_flags", "gid", "groupname", "uid"]: continue self.assertTrue(grain in lsgrains) - @skipIf(os.environ.get('TRAVIS_PYTHON_VERSION', None) is not None, - 'Travis environment can\'t keep up with salt refresh') + @skipIf( + os.environ.get("TRAVIS_PYTHON_VERSION", None) is not None, + "Travis environment can't keep up with salt refresh", + ) def test_set_val(self): - ''' + """ test grains.set_val - ''' + """ self.assertEqual( - self.run_function( - 'grains.setval', - ['setgrain', 'grainval']), - {'setgrain': 'grainval'}) + self.run_function("grains.setval", ["setgrain", "grainval"]), + {"setgrain": "grainval"}, + ) time.sleep(5) - ret = self.run_function('grains.item', ['setgrain']) + ret = self.run_function("grains.item", ["setgrain"]) if not ret: # Sleep longer, sometimes test systems get bogged down time.sleep(20) - ret = self.run_function('grains.item', ['setgrain']) + ret = self.run_function("grains.item", ["setgrain"]) self.assertTrue(ret) def test_get(self): - ''' + """ test grains.get - ''' - self.assertEqual( - self.run_function( - 'grains.get', - ['level1:level2']), - 'foo') + """ + self.assertEqual(self.run_function("grains.get", ["level1:level2"]), "foo") def test_get_core_grains(self): - ''' + """ test to ensure some core grains are returned - ''' - grains = ('os', 'os_family', 'osmajorrelease', 'osrelease', 'osfullname', 'id') - os = self.run_function('grains.get', ['os']) + """ + grains = ("os", "os_family", "osmajorrelease", "osrelease", "osfullname", "id") + os = self.run_function("grains.get", ["os"]) for grain in grains: - get_grain = self.run_function('grains.get', [grain]) - log.debug('Value of \'%s\' grain: \'%s\'', grain, get_grain) - if os == 'Arch' and grain in ['osmajorrelease']: - self.assertEqual(get_grain, '') + get_grain = self.run_function("grains.get", [grain]) + log.debug("Value of '%s' grain: '%s'", grain, get_grain) + if os == "Arch" and grain in ["osmajorrelease"]: + self.assertEqual(get_grain, "") continue - if os == 'Windows' and grain in ['osmajorrelease']: - self.assertEqual(get_grain, '') + if os == "Windows" and grain in ["osmajorrelease"]: + self.assertEqual(get_grain, "") continue self.assertTrue(get_grain) def test_get_grains_int(self): - ''' + """ test to ensure int grains are returned as integers - ''' - grains = ['num_cpus', 'mem_total', 'num_gpus', 'uid'] - os = self.run_function('grains.get', ['os']) + """ + grains = ["num_cpus", "mem_total", "num_gpus", "uid"] + os = self.run_function("grains.get", ["os"]) for grain in grains: - get_grain = self.run_function('grains.get', [grain]) - if os == 'Windows' and grain in ['uid']: - self.assertEqual(get_grain, '') + get_grain = self.run_function("grains.get", [grain]) + if os == "Windows" and grain in ["uid"]: + self.assertEqual(get_grain, "") continue self.assertIsInstance( - get_grain, int, msg='grain: {0} is not an int or empty'.format(grain)) + get_grain, int, msg="grain: {0} is not an int or empty".format(grain) + ) +@pytest.mark.windows_whitelisted class GrainsAppendTestCase(ModuleCase): - ''' + """ Tests written specifically for the grains.append function. - ''' - GRAIN_KEY = 'salttesting-grain-key' - GRAIN_VAL = 'my-grain-val' + """ + + GRAIN_KEY = "salttesting-grain-key" + GRAIN_VAL = "my-grain-val" def setUp(self): # Start off with an empty list - self.run_function('grains.setval', [self.GRAIN_KEY, []]) + self.run_function("grains.setval", [self.GRAIN_KEY, []]) if not self.wait_for_grain(self.GRAIN_KEY, []): - raise Exception('Failed to set grain') + raise Exception("Failed to set grain") self.addCleanup(self.cleanup_grain) def cleanup_grain(self): - self.run_function('grains.setval', [self.GRAIN_KEY, []]) + self.run_function("grains.setval", [self.GRAIN_KEY, []]) if not self.wait_for_grain(self.GRAIN_KEY, []): - raise Exception('Failed to set grain') + raise Exception("Failed to set grain") def test_grains_append(self): - ''' + """ Tests the return of a simple grains.append call. - ''' - ret = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + """ + ret = self.run_function("grains.append", [self.GRAIN_KEY, self.GRAIN_VAL]) self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL]) def test_grains_append_val_already_present(self): - ''' + """ Tests the return of a grains.append call when the value is already present in the grains list. - ''' - msg = 'The val {0} was already in the list ' \ - 'salttesting-grain-key'.format(self.GRAIN_VAL) + """ + msg = "The val {0} was already in the list " "salttesting-grain-key".format( + self.GRAIN_VAL + ) # First, make sure the test grain is present - ret = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + ret = self.run_function("grains.append", [self.GRAIN_KEY, self.GRAIN_VAL]) self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL]) # Now try to append again - ret = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + ret = self.run_function("grains.append", [self.GRAIN_KEY, self.GRAIN_VAL]) self.assertTrue(self.wait_for_grain(self.GRAIN_KEY, [self.GRAIN_VAL])) if not ret or isinstance(ret, dict): # Sleep for a bit, sometimes the second "append" runs too quickly time.sleep(5) - ret = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + ret = self.run_function("grains.append", [self.GRAIN_KEY, self.GRAIN_VAL]) assert msg == ret @flaky def test_grains_append_val_is_list(self): - ''' + """ Tests the return of a grains.append call when val is passed in as a list. - ''' + """ # Start off with an empty list, don't know if the flaky decorator runs the setUp function or not... - self.run_function('grains.setval', [self.GRAIN_KEY, []]) - second_grain = self.GRAIN_VAL + '-2' - ret = self.run_function('grains.append', [self.GRAIN_KEY, [self.GRAIN_VAL, second_grain]]) + self.run_function("grains.setval", [self.GRAIN_KEY, []]) + second_grain = self.GRAIN_VAL + "-2" + ret = self.run_function( + "grains.append", [self.GRAIN_KEY, [self.GRAIN_VAL, second_grain]] + ) self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL, second_grain]) def test_grains_append_call_twice(self): - ''' + """ Tests the return of a grains.append call when the value is already present but also ensure the grain is not listed twice. - ''' + """ # First, add the test grain. - append_1 = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + append_1 = self.run_function("grains.append", [self.GRAIN_KEY, self.GRAIN_VAL]) # Call the function again, which results in a string message, as tested in # test_grains_append_val_already_present above. - append_2 = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + append_2 = self.run_function("grains.append", [self.GRAIN_KEY, self.GRAIN_VAL]) # Now make sure the grain doesn't show up twice. - grains = self.run_function('grains.items') + grains = self.run_function("grains.items") count = 0 for grain in grains: if grain == self.GRAIN_KEY: @@ -231,39 +234,41 @@ class GrainsAppendTestCase(ModuleCase): self.assertEqual( count, 1, - msg='Count did not match({}!=1) while looking for key \'{}\'.\nFirst append return:\n{}\nSecond append return:\n{}'.format( + msg="Count did not match({}!=1) while looking for key '{}'.\nFirst append return:\n{}\nSecond append return:\n{}".format( count, self.GRAIN_KEY, pprint.pformat(append_1), - pprint.pformat(append_2) - ) + pprint.pformat(append_2), + ), ) - def wait_for_grain(self, key, val, timeout=60, sleep=.3): + def wait_for_grain(self, key, val, timeout=60, sleep=0.3): start = time.time() while time.time() - start <= timeout: - ret = self.run_function('grains.get', [key]) + ret = self.run_function("grains.get", [key]) if ret == val: return True time.sleep(sleep) return False def test_grains_remove_add(self): - second_grain = self.GRAIN_VAL + '-2' - ret = self.run_function('grains.get', [self.GRAIN_KEY]) + second_grain = self.GRAIN_VAL + "-2" + ret = self.run_function("grains.get", [self.GRAIN_KEY]) self.assertEqual(ret, []) for i in range(10): - ret = self.run_function('grains.setval', [self.GRAIN_KEY, []]) + ret = self.run_function("grains.setval", [self.GRAIN_KEY, []]) self.assertEqual(ret[self.GRAIN_KEY], []) self.wait_for_grain(self.GRAIN_KEY, []) - ret = self.run_function('grains.append', [self.GRAIN_KEY, self.GRAIN_VAL]) + ret = self.run_function("grains.append", [self.GRAIN_KEY, self.GRAIN_VAL]) self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL]) self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL]) - ret = self.run_function('grains.setval', [self.GRAIN_KEY, []]) + ret = self.run_function("grains.setval", [self.GRAIN_KEY, []]) self.wait_for_grain(self.GRAIN_KEY, []) self.assertTrue(self.wait_for_grain(self.GRAIN_KEY, [])) - ret = self.run_function('grains.append', [self.GRAIN_KEY, [self.GRAIN_VAL, second_grain]]) + ret = self.run_function( + "grains.append", [self.GRAIN_KEY, [self.GRAIN_VAL, second_grain]] + ) self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL, second_grain]) self.assertEqual(ret[self.GRAIN_KEY], [self.GRAIN_VAL, second_grain]) diff --git a/tests/integration/modules/test_groupadd.py b/tests/integration/modules/test_groupadd.py index 8f30eccebf7..bc44e3fed08 100644 --- a/tests/integration/modules/test_groupadd.py +++ b/tests/integration/modules/test_groupadd.py @@ -1,21 +1,19 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + import random import string -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.helpers import destructiveTest, skip_if_not_root -from tests.support.unit import skipIf - -# Import Salt libs -from salt.ext import six -from salt.ext.six.moves import range +import pytest import salt.utils.files import salt.utils.platform import salt.utils.stringutils +from salt.ext import six +from salt.ext.six.moves import range +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root +from tests.support.unit import skipIf if not salt.utils.platform.is_windows(): import grp @@ -23,58 +21,56 @@ if not salt.utils.platform.is_windows(): @skip_if_not_root @destructiveTest +@pytest.mark.windows_whitelisted class GroupModuleTest(ModuleCase): - ''' + """ Validate the linux group system module - ''' + """ def setUp(self): - ''' + """ Get current settings - ''' + """ super(GroupModuleTest, self).setUp() self._user = self.__random_string() self._user1 = self.__random_string() self._no_user = self.__random_string() self._group = self.__random_string() self._no_group = self.__random_string() - self.os_grain = self.run_function('grains.item', ['kernel']) - self._gid = 64989 if 'Windows' not in self.os_grain['kernel'] else None - self._new_gid = 64998 if 'Windows' not in self.os_grain['kernel'] else None - if self.os_grain['kernel'] not in ('Linux', 'Windows'): + self.os_grain = self.run_function("grains.item", ["kernel"]) + self._gid = 64989 if "Windows" not in self.os_grain["kernel"] else None + self._new_gid = 64998 if "Windows" not in self.os_grain["kernel"] else None + if self.os_grain["kernel"] not in ("Linux", "Windows"): self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **self.os_grain - ) + "Test not applicable to '{kernel}' kernel".format(**self.os_grain) ) def tearDown(self): - ''' + """ Reset to original settings - ''' - self.run_function('user.delete', [self._user]) - self.run_function('user.delete', [self._user1]) - self.run_function('group.delete', [self._group]) + """ + self.run_function("user.delete", [self._user]) + self.run_function("user.delete", [self._user1]) + self.run_function("group.delete", [self._group]) def __random_string(self, size=6): - ''' + """ Generates a random names - ''' - return 'tg-' + ''.join( - random.choice(string.ascii_lowercase + string.digits) - for x in range(size) + """ + return "tg-" + "".join( + random.choice(string.ascii_lowercase + string.digits) for x in range(size) ) def __get_system_group_gid_range(self): - ''' + """ Returns (SYS_GID_MIN, SYS_GID_MAX) - ''' + """ try: login_defs = {} - with salt.utils.files.fopen('/etc/login.defs') as defs_fd: + with salt.utils.files.fopen("/etc/login.defs") as defs_fd: for line in defs_fd: line = salt.utils.stringutils.to_unicode(line).strip() - if line.startswith('#'): + if line.startswith("#"): continue try: key, val = line.split() @@ -83,25 +79,23 @@ class GroupModuleTest(ModuleCase): else: login_defs[key] = val except OSError: - login_defs = {'SYS_GID_MIN': 101, - 'SYS_GID_MAX': 999} + login_defs = {"SYS_GID_MIN": 101, "SYS_GID_MAX": 999} - gid_min = login_defs.get('SYS_GID_MIN', 101) - gid_max = login_defs.get('SYS_GID_MAX', - int(login_defs.get('GID_MIN', 1000)) - 1) + gid_min = login_defs.get("SYS_GID_MIN", 101) + gid_max = login_defs.get( + "SYS_GID_MAX", int(login_defs.get("GID_MIN", 1000)) - 1 + ) return int(gid_min), int(gid_max) def __get_free_system_gid(self): - ''' + """ Find a free system gid - ''' + """ gid_min, gid_max = self.__get_system_group_gid_range() - busy_gids = [x.gr_gid - for x in grp.getgrall() - if gid_min <= x.gr_gid <= gid_max] + busy_gids = [x.gr_gid for x in grp.getgrall() if gid_min <= x.gr_gid <= gid_max] # find free system gid for gid in range(gid_min, gid_max + 1): @@ -110,140 +104,142 @@ class GroupModuleTest(ModuleCase): @destructiveTest def test_add(self): - ''' + """ Test the add group function - ''' + """ # add a new group - self.assertTrue(self.run_function('group.add', [self._group], gid=self._gid)) - group_info = self.run_function('group.info', [self._group]) - self.assertEqual(group_info['gid'], self._gid) - self.assertEqual(group_info['name'], self._group) + self.assertTrue(self.run_function("group.add", [self._group], gid=self._gid)) + group_info = self.run_function("group.info", [self._group]) + self.assertEqual(group_info["gid"], self._gid) + self.assertEqual(group_info["name"], self._group) # try adding the group again - self.assertFalse(self.run_function('group.add', [self._group], gid=self._gid)) + self.assertFalse(self.run_function("group.add", [self._group], gid=self._gid)) @destructiveTest - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows") def test_add_system_group(self): - ''' + """ Test the add group function with system=True - ''' + """ gid_min, gid_max = self.__get_system_group_gid_range() # add a new system group - self.assertTrue(self.run_function('group.add', - [self._group, None, True])) - group_info = self.run_function('group.info', [self._group]) - self.assertEqual(group_info['name'], self._group) - self.assertTrue(gid_min <= group_info['gid'] <= gid_max) + self.assertTrue(self.run_function("group.add", [self._group, None, True])) + group_info = self.run_function("group.info", [self._group]) + self.assertEqual(group_info["name"], self._group) + self.assertTrue(gid_min <= group_info["gid"] <= gid_max) # try adding the group again - self.assertFalse(self.run_function('group.add', - [self._group])) + self.assertFalse(self.run_function("group.add", [self._group])) @destructiveTest - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows") def test_add_system_group_gid(self): - ''' + """ Test the add group function with system=True and a specific gid - ''' + """ gid = self.__get_free_system_gid() # add a new system group - self.assertTrue(self.run_function('group.add', - [self._group, gid, True])) - group_info = self.run_function('group.info', [self._group]) - self.assertEqual(group_info['name'], self._group) - self.assertEqual(group_info['gid'], gid) + self.assertTrue(self.run_function("group.add", [self._group, gid, True])) + group_info = self.run_function("group.info", [self._group]) + self.assertEqual(group_info["name"], self._group) + self.assertEqual(group_info["gid"], gid) # try adding the group again - self.assertFalse(self.run_function('group.add', - [self._group, gid])) + self.assertFalse(self.run_function("group.add", [self._group, gid])) @destructiveTest def test_delete(self): - ''' + """ Test the delete group function - ''' - self.assertTrue(self.run_function('group.add', [self._group])) + """ + self.assertTrue(self.run_function("group.add", [self._group])) # correct functionality - self.assertTrue(self.run_function('group.delete', [self._group])) + self.assertTrue(self.run_function("group.delete", [self._group])) # group does not exist - self.assertFalse(self.run_function('group.delete', [self._no_group])) + self.assertFalse(self.run_function("group.delete", [self._no_group])) def test_info(self): - ''' + """ Test the info group function - ''' - self.run_function('group.add', [self._group], gid=self._gid) - self.run_function('user.add', [self._user]) - self.run_function('group.adduser', [self._group, self._user]) - group_info = self.run_function('group.info', [self._group]) + """ + self.run_function("group.add", [self._group], gid=self._gid) + self.run_function("user.add", [self._user]) + self.run_function("group.adduser", [self._group, self._user]) + group_info = self.run_function("group.info", [self._group]) - self.assertEqual(group_info['name'], self._group) - self.assertEqual(group_info['gid'], self._gid) - self.assertIn(self._user, str(group_info['members'])) + self.assertEqual(group_info["name"], self._group) + self.assertEqual(group_info["gid"], self._gid) + self.assertIn(self._user, str(group_info["members"])) - @skipIf(salt.utils.platform.is_windows(), 'gid test skipped on windows') + @skipIf(salt.utils.platform.is_windows(), "gid test skipped on windows") def test_chgid(self): - ''' + """ Test the change gid function - ''' - self.run_function('group.add', [self._group], gid=self._gid) - self.assertTrue(self.run_function('group.chgid', [self._group, self._new_gid])) - group_info = self.run_function('group.info', [self._group]) - self.assertEqual(group_info['gid'], self._new_gid) + """ + self.run_function("group.add", [self._group], gid=self._gid) + self.assertTrue(self.run_function("group.chgid", [self._group, self._new_gid])) + group_info = self.run_function("group.info", [self._group]) + self.assertEqual(group_info["gid"], self._new_gid) def test_adduser(self): - ''' + """ Test the add user to group function - ''' - self.run_function('group.add', [self._group], gid=self._gid) - self.run_function('user.add', [self._user]) - self.assertTrue(self.run_function('group.adduser', [self._group, self._user])) - group_info = self.run_function('group.info', [self._group]) - self.assertIn(self._user, str(group_info['members'])) + """ + self.run_function("group.add", [self._group], gid=self._gid) + self.run_function("user.add", [self._user]) + self.assertTrue(self.run_function("group.adduser", [self._group, self._user])) + group_info = self.run_function("group.info", [self._group]) + self.assertIn(self._user, str(group_info["members"])) # try add a non existing user - self.assertFalse(self.run_function('group.adduser', [self._group, self._no_user])) + self.assertFalse( + self.run_function("group.adduser", [self._group, self._no_user]) + ) # try add a user to non existing group - self.assertFalse(self.run_function('group.adduser', [self._no_group, self._user])) + self.assertFalse( + self.run_function("group.adduser", [self._no_group, self._user]) + ) # try add a non existing user to a non existing group - self.assertFalse(self.run_function('group.adduser', [self._no_group, self._no_user])) + self.assertFalse( + self.run_function("group.adduser", [self._no_group, self._no_user]) + ) def test_deluser(self): - ''' + """ Test the delete user from group function - ''' - self.run_function('group.add', [self._group], gid=self._gid) - self.run_function('user.add', [self._user]) - self.run_function('group.adduser', [self._group, self._user]) - self.assertTrue(self.run_function('group.deluser', [self._group, self._user])) - group_info = self.run_function('group.info', [self._group]) - self.assertNotIn(self._user, str(group_info['members'])) + """ + self.run_function("group.add", [self._group], gid=self._gid) + self.run_function("user.add", [self._user]) + self.run_function("group.adduser", [self._group, self._user]) + self.assertTrue(self.run_function("group.deluser", [self._group, self._user])) + group_info = self.run_function("group.info", [self._group]) + self.assertNotIn(self._user, str(group_info["members"])) def test_members(self): - ''' + """ Test the members function - ''' - self.run_function('group.add', [self._group], gid=self._gid) - self.run_function('user.add', [self._user]) - self.run_function('user.add', [self._user1]) - m = '{0},{1}'.format(self._user, self._user1) - ret = self.run_function('group.members', [self._group, m]) + """ + self.run_function("group.add", [self._group], gid=self._gid) + self.run_function("user.add", [self._user]) + self.run_function("user.add", [self._user1]) + m = "{0},{1}".format(self._user, self._user1) + ret = self.run_function("group.members", [self._group, m]) self.assertTrue(ret) - group_info = self.run_function('group.info', [self._group]) - self.assertIn(self._user, str(group_info['members'])) - self.assertIn(self._user1, str(group_info['members'])) + group_info = self.run_function("group.info", [self._group]) + self.assertIn(self._user, str(group_info["members"])) + self.assertIn(self._user1, str(group_info["members"])) def test_getent(self): - ''' + """ Test the getent function - ''' - self.run_function('group.add', [self._group], gid=self._gid) - self.run_function('user.add', [self._user]) - self.run_function('group.adduser', [self._group, self._user]) - ginfo = self.run_function('user.getent') + """ + self.run_function("group.add", [self._group], gid=self._gid) + self.run_function("user.add", [self._user]) + self.run_function("group.adduser", [self._group, self._user]) + ginfo = self.run_function("user.getent") self.assertIn(self._group, six.text_type(ginfo)) self.assertIn(self._user, six.text_type(ginfo)) self.assertNotIn(self._no_group, six.text_type(ginfo)) diff --git a/tests/integration/modules/test_hosts.py b/tests/integration/modules/test_hosts.py index fd30b8fa963..77238002a68 100644 --- a/tests/integration/modules/test_hosts.py +++ b/tests/integration/modules/test_hosts.py @@ -1,206 +1,165 @@ # -*- coding: utf-8 -*- -''' +""" Test the hosts module -''' -# Import python libs +""" from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import shutil -import logging -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase - -# Import Salt libs +import pytest import salt.utils.files import salt.utils.stringutils +from tests.support.case import ModuleCase +from tests.support.runtests import RUNTIME_VARS log = logging.getLogger(__name__) +@pytest.mark.windows_whitelisted class HostsModuleTest(ModuleCase): - ''' + """ Test the hosts module - ''' + """ maxDiff = None @classmethod def setUpClass(cls): - cls.hosts_file = os.path.join(RUNTIME_VARS.TMP, 'hosts') + cls.hosts_file = os.path.join(RUNTIME_VARS.TMP, "hosts") def __clear_hosts(self): - ''' + """ Delete the tmp hosts file - ''' + """ if os.path.isfile(self.hosts_file): os.remove(self.hosts_file) def setUp(self): - shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'hosts'), self.hosts_file) + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, "hosts"), self.hosts_file) self.addCleanup(self.__clear_hosts) def test_list_hosts(self): - ''' + """ hosts.list_hosts - ''' - hosts = self.run_function('hosts.list_hosts') + """ + hosts = self.run_function("hosts.list_hosts") self.assertEqual(len(hosts), 10) - self.assertEqual(hosts['::1'], ['ip6-localhost', 'ip6-loopback']) - self.assertEqual(hosts['127.0.0.1'], ['localhost', 'myname']) + self.assertEqual(hosts["::1"], {"aliases": ["ip6-localhost", "ip6-loopback"]}) + self.assertEqual(hosts["127.0.0.1"], {"aliases": ["localhost", "myname"]}) def test_list_hosts_nofile(self): - ''' + """ hosts.list_hosts without a hosts file - ''' + """ if os.path.isfile(self.hosts_file): os.remove(self.hosts_file) - hosts = self.run_function('hosts.list_hosts') + hosts = self.run_function("hosts.list_hosts") self.assertEqual(hosts, {}) def test_get_ip(self): - ''' + """ hosts.get_ip - ''' - self.assertEqual( - self.run_function('hosts.get_ip', ['myname']), '127.0.0.1' - ) - self.assertEqual(self.run_function('hosts.get_ip', ['othername']), '') + """ + self.assertEqual(self.run_function("hosts.get_ip", ["myname"]), "127.0.0.1") + self.assertEqual(self.run_function("hosts.get_ip", ["othername"]), "") self.__clear_hosts() - self.assertEqual(self.run_function('hosts.get_ip', ['othername']), '') + self.assertEqual(self.run_function("hosts.get_ip", ["othername"]), "") def test_get_alias(self): - ''' + """ hosts.get_alias - ''' + """ self.assertEqual( - self.run_function('hosts.get_alias', ['127.0.0.1']), - ['localhost', 'myname'] - ) - self.assertEqual( - self.run_function('hosts.get_alias', ['127.0.0.2']), - [] + self.run_function("hosts.get_alias", ["127.0.0.1"]), ["localhost", "myname"] ) + self.assertEqual(self.run_function("hosts.get_alias", ["127.0.0.2"]), []) self.__clear_hosts() - self.assertEqual( - self.run_function('hosts.get_alias', ['127.0.0.1']), - [] - ) + self.assertEqual(self.run_function("hosts.get_alias", ["127.0.0.1"]), []) def test_has_pair(self): - ''' + """ hosts.has_pair - ''' - self.assertTrue( - self.run_function('hosts.has_pair', ['127.0.0.1', 'myname']) - ) + """ + self.assertTrue(self.run_function("hosts.has_pair", ["127.0.0.1", "myname"])) self.assertFalse( - self.run_function('hosts.has_pair', ['127.0.0.1', 'othername']) + self.run_function("hosts.has_pair", ["127.0.0.1", "othername"]) ) def test_set_host(self): - ''' + """ hosts.set_hosts - ''' - self.assertTrue( - self.run_function('hosts.set_host', ['192.168.1.123', 'newip']) - ) - self.assertTrue( - self.run_function('hosts.has_pair', ['192.168.1.123', 'newip']) - ) - self.assertTrue( - self.run_function('hosts.set_host', ['127.0.0.1', 'localhost']) - ) - self.assertEqual(len(self.run_function('hosts.list_hosts')), 11) + """ + self.assertTrue(self.run_function("hosts.set_host", ["192.168.1.123", "newip"])) + self.assertTrue(self.run_function("hosts.has_pair", ["192.168.1.123", "newip"])) + self.assertTrue(self.run_function("hosts.set_host", ["127.0.0.1", "localhost"])) + self.assertEqual(len(self.run_function("hosts.list_hosts")), 11) self.assertFalse( - self.run_function('hosts.has_pair', ['127.0.0.1', 'myname']), - 'should remove second entry' + self.run_function("hosts.has_pair", ["127.0.0.1", "myname"]), + "should remove second entry", ) def test_add_host(self): - ''' + """ hosts.add_host - ''' + """ + self.assertTrue(self.run_function("hosts.add_host", ["192.168.1.123", "newip"])) + self.assertTrue(self.run_function("hosts.has_pair", ["192.168.1.123", "newip"])) + self.assertEqual(len(self.run_function("hosts.list_hosts")), 11) self.assertTrue( - self.run_function('hosts.add_host', ['192.168.1.123', 'newip']) + self.run_function("hosts.add_host", ["127.0.0.1", "othernameip"]) ) - self.assertTrue( - self.run_function('hosts.has_pair', ['192.168.1.123', 'newip']) - ) - self.assertEqual(len(self.run_function('hosts.list_hosts')), 11) - self.assertTrue( - self.run_function('hosts.add_host', ['127.0.0.1', 'othernameip']) - ) - self.assertEqual(len(self.run_function('hosts.list_hosts')), 11) + self.assertEqual(len(self.run_function("hosts.list_hosts")), 11) def test_rm_host(self): - self.assertTrue( - self.run_function('hosts.has_pair', ['127.0.0.1', 'myname']) - ) - self.assertTrue( - self.run_function('hosts.rm_host', ['127.0.0.1', 'myname']) - ) - self.assertFalse( - self.run_function('hosts.has_pair', ['127.0.0.1', 'myname']) - ) - self.assertTrue( - self.run_function('hosts.rm_host', ['127.0.0.1', 'unknown']) - ) + self.assertTrue(self.run_function("hosts.has_pair", ["127.0.0.1", "myname"])) + self.assertTrue(self.run_function("hosts.rm_host", ["127.0.0.1", "myname"])) + self.assertFalse(self.run_function("hosts.has_pair", ["127.0.0.1", "myname"])) + self.assertTrue(self.run_function("hosts.rm_host", ["127.0.0.1", "unknown"])) def test_add_host_formatting(self): - ''' + """ Ensure that hosts.add_host isn't adding duplicates and that it's formatting the output correctly - ''' + """ # instead of using the 'clean' hosts file we're going to # use an empty one so we can prove the syntax of the entries # being added by the hosts module self.__clear_hosts() - with salt.utils.files.fopen(self.hosts_file, 'w'): + with salt.utils.files.fopen(self.hosts_file, "w"): pass self.assertTrue( - self.run_function( - 'hosts.add_host', ['192.168.1.3', 'host3.fqdn.com'] - ) + self.run_function("hosts.add_host", ["192.168.1.3", "host3.fqdn.com"]) ) self.assertTrue( - self.run_function( - 'hosts.add_host', ['192.168.1.1', 'host1.fqdn.com'] - ) + self.run_function("hosts.add_host", ["192.168.1.1", "host1.fqdn.com"]) + ) + self.assertTrue(self.run_function("hosts.add_host", ["192.168.1.1", "host1"])) + self.assertTrue( + self.run_function("hosts.add_host", ["192.168.1.2", "host2.fqdn.com"]) + ) + self.assertTrue(self.run_function("hosts.add_host", ["192.168.1.2", "host2"])) + self.assertTrue( + self.run_function("hosts.add_host", ["192.168.1.2", "oldhost2"]) ) self.assertTrue( - self.run_function('hosts.add_host', ['192.168.1.1', 'host1']) + self.run_function("hosts.add_host", ["192.168.1.2", "host2-reorder"]) ) self.assertTrue( - self.run_function( - 'hosts.add_host', ['192.168.1.2', 'host2.fqdn.com'] - ) - ) - self.assertTrue( - self.run_function('hosts.add_host', ['192.168.1.2', 'host2']) - ) - self.assertTrue( - self.run_function('hosts.add_host', ['192.168.1.2', 'oldhost2']) - ) - self.assertTrue( - self.run_function( - 'hosts.add_host', ['192.168.1.2', 'host2-reorder'] - ) - ) - self.assertTrue( - self.run_function( - 'hosts.add_host', ['192.168.1.1', 'host1-reorder'] - ) + self.run_function("hosts.add_host", ["192.168.1.1", "host1-reorder"]) ) # now read the lines and ensure they're formatted correctly - with salt.utils.files.fopen(self.hosts_file, 'r') as fp_: + with salt.utils.files.fopen(self.hosts_file, "r") as fp_: lines = salt.utils.stringutils.to_unicode(fp_.read()).splitlines() - self.assertEqual(lines, [ - '192.168.1.3\t\thost3.fqdn.com', - '192.168.1.1\t\thost1.fqdn.com host1 host1-reorder', - '192.168.1.2\t\thost2.fqdn.com host2 oldhost2 host2-reorder', - ]) + self.assertEqual( + lines, + [ + "192.168.1.3\t\thost3.fqdn.com", + "192.168.1.1\t\thost1.fqdn.com host1 host1-reorder", + "192.168.1.2\t\thost2.fqdn.com host2 oldhost2 host2-reorder", + ], + ) diff --git a/tests/integration/modules/test_jinja.py b/tests/integration/modules/test_jinja.py index 832c0785e7c..20ccb246520 100644 --- a/tests/integration/modules/test_jinja.py +++ b/tests/integration/modules/test_jinja.py @@ -1,72 +1,80 @@ # -*- coding: utf-8 -*- -''' +""" Test the jinja module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.helpers import requires_system_grains +import salt.utils.files # Import Salt libs import salt.utils.json -import salt.utils.files import salt.utils.yaml +from tests.support.case import ModuleCase +from tests.support.helpers import requires_system_grains + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS class TestModulesJinja(ModuleCase): - ''' + """ Test the jinja map module - ''' + """ def _path(self, name, absolute=False): - path = os.path.join('modules', 'jinja', name) + path = os.path.join("modules", "jinja", name) if absolute: return os.path.join(RUNTIME_VARS.BASE_FILES, path) else: return path def test_import_json(self): - json_file = 'osarchmap.json' - ret = self.run_function('jinja.import_json', [self._path(json_file)]) + json_file = "osarchmap.json" + ret = self.run_function("jinja.import_json", [self._path(json_file)]) with salt.utils.files.fopen(self._path(json_file, absolute=True)) as fh_: self.assertDictEqual(salt.utils.json.load(fh_), ret) def test_import_yaml(self): - yaml_file = 'defaults.yaml' - ret = self.run_function('jinja.import_yaml', [self._path(yaml_file)]) + yaml_file = "defaults.yaml" + ret = self.run_function("jinja.import_yaml", [self._path(yaml_file)]) with salt.utils.files.fopen(self._path(yaml_file, absolute=True)) as fh_: self.assertDictEqual(salt.utils.yaml.safe_load(fh_), ret) @requires_system_grains def test_load_map(self, grains): - ret = self.run_function('jinja.load_map', [self._path('map.jinja'), 'template']) + ret = self.run_function("jinja.load_map", [self._path("map.jinja"), "template"]) - with salt.utils.files.fopen(self._path('defaults.yaml', absolute=True)) as fh_: + with salt.utils.files.fopen(self._path("defaults.yaml", absolute=True)) as fh_: defaults = salt.utils.yaml.safe_load(fh_) - with salt.utils.files.fopen(self._path('osarchmap.json', absolute=True)) as fh_: + with salt.utils.files.fopen(self._path("osarchmap.json", absolute=True)) as fh_: osarchmap = salt.utils.json.load(fh_) - with salt.utils.files.fopen(self._path('osfamilymap.yaml', absolute=True)) as fh_: + with salt.utils.files.fopen( + self._path("osfamilymap.yaml", absolute=True) + ) as fh_: osfamilymap = salt.utils.yaml.safe_load(fh_) - with salt.utils.files.fopen(self._path('osmap.yaml', absolute=True)) as fh_: + with salt.utils.files.fopen(self._path("osmap.yaml", absolute=True)) as fh_: osmap = salt.utils.yaml.safe_load(fh_) - with salt.utils.files.fopen(self._path('osfingermap.yaml', absolute=True)) as fh_: + with salt.utils.files.fopen( + self._path("osfingermap.yaml", absolute=True) + ) as fh_: osfingermap = salt.utils.yaml.safe_load(fh_) - self.assertEqual(ret.get('arch'), osarchmap.get(grains['osarch'], {}).get('arch')) self.assertEqual( - ret.get('config'), - osfingermap.get( - grains['osfinger'], {} - ).get('config', osmap.get( - grains['os'], {} - ).get('config', osfamilymap.get( - grains['os_family'], {} - ).get('config', defaults.get( - 'template' - ).get('config')))) + ret.get("arch"), osarchmap.get(grains["osarch"], {}).get("arch") + ) + self.assertEqual( + ret.get("config"), + osfingermap.get(grains["osfinger"], {}).get( + "config", + osmap.get(grains["os"], {}).get( + "config", + osfamilymap.get(grains["os_family"], {}).get( + "config", defaults.get("template").get("config") + ), + ), + ), ) diff --git a/tests/integration/modules/test_key.py b/tests/integration/modules/test_key.py index 569afb61123..d8359e452cd 100644 --- a/tests/integration/modules/test_key.py +++ b/tests/integration/modules/test_key.py @@ -1,26 +1,27 @@ # -*- coding: utf-8 -*- -# Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import re -# Import Salt Testing libs +import pytest from tests.support.case import ModuleCase +@pytest.mark.windows_whitelisted class KeyModuleTest(ModuleCase): def test_key_finger(self): - ''' + """ test key.finger to ensure we receive a valid fingerprint - ''' - out = self.run_function('key.finger') + """ + out = self.run_function("key.finger") match = re.match("([0-9a-z]{2}:){15,}[0-9a-z]{2}$", out) self.assertTrue(match) def test_key_finger_master(self): - ''' + """ test key.finger_master to ensure we receive a valid fingerprint - ''' - out = self.run_function('key.finger_master') + """ + out = self.run_function("key.finger_master") match = re.match("([0-9a-z]{2}:){15,}[0-9a-z]{2}$", out) self.assertTrue(match) diff --git a/tests/integration/modules/test_linux_acl.py b/tests/integration/modules/test_linux_acl.py index bb7f2af1603..cfec1777308 100644 --- a/tests/integration/modules/test_linux_acl.py +++ b/tests/integration/modules/test_linux_acl.py @@ -1,47 +1,47 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.helpers import skip_if_binaries_missing - -# Import salt libs +import pytest import salt.utils.files import salt.utils.user +from tests.support.case import ModuleCase +from tests.support.helpers import skip_if_binaries_missing +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.runtests import RUNTIME_VARS # Acl package should be installed to test linux_acl module -@skip_if_binaries_missing(['getfacl']) +@skip_if_binaries_missing(["getfacl"]) # Doesn't work. Why? # @requires_salt_modules('acl') # @requires_salt_modules('linux_acl') +@pytest.mark.windows_whitelisted class LinuxAclModuleTest(ModuleCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Validate the linux_acl module - ''' + """ + def setUp(self): # Blindly copied from tests.integration.modules.file; Refactoring? - self.myfile = os.path.join(RUNTIME_VARS.TMP, 'myfile') - with salt.utils.files.fopen(self.myfile, 'w+') as fp: - fp.write('Hello\n') - self.mydir = os.path.join(RUNTIME_VARS.TMP, 'mydir/isawesome') + self.myfile = os.path.join(RUNTIME_VARS.TMP, "myfile") + with salt.utils.files.fopen(self.myfile, "w+") as fp: + fp.write("Hello\n") + self.mydir = os.path.join(RUNTIME_VARS.TMP, "mydir/isawesome") if not os.path.isdir(self.mydir): # left behind... Don't fail because of this! os.makedirs(self.mydir) - self.mysymlink = os.path.join(RUNTIME_VARS.TMP, 'mysymlink') + self.mysymlink = os.path.join(RUNTIME_VARS.TMP, "mysymlink") if os.path.islink(self.mysymlink): os.remove(self.mysymlink) os.symlink(self.myfile, self.mysymlink) - self.mybadsymlink = os.path.join(RUNTIME_VARS.TMP, 'mybadsymlink') + self.mybadsymlink = os.path.join(RUNTIME_VARS.TMP, "mybadsymlink") if os.path.islink(self.mybadsymlink): os.remove(self.mybadsymlink) - os.symlink('/nonexistentpath', self.mybadsymlink) + os.symlink("/nonexistentpath", self.mybadsymlink) super(LinuxAclModuleTest, self).setUp() def tearDown(self): @@ -55,17 +55,54 @@ class LinuxAclModuleTest(ModuleCase, AdaptedConfigurationTestCaseMixin): super(LinuxAclModuleTest, self).tearDown() def test_version(self): - self.assertRegex(self.run_function('acl.version'), r'\d+\.\d+\.\d+') + self.assertRegex(self.run_function("acl.version"), r"\d+\.\d+\.\d+") def test_getfacl_w_single_file_without_acl(self): - ret = self.run_function('acl.getfacl', arg=[self.myfile]) + ret = self.run_function("acl.getfacl", arg=[self.myfile]) user = salt.utils.user.get_user() group = salt.utils.user.get_default_group(user) self.maxDiff = None self.assertEqual( ret, - {self.myfile: {'other': [{'': {'octal': 4, 'permissions': {'read': True, 'write': False, 'execute': False}}}], - 'user': [{user: {'octal': 6, 'permissions': {'read': True, 'write': True, 'execute': False}}}], - 'group': [{group: {'octal': 4, 'permissions': {'read': True, 'write': False, 'execute': False}}}], - 'comment': {'owner': user, 'group': group, 'file': self.myfile}}} + { + self.myfile: { + "other": [ + { + "": { + "octal": 4, + "permissions": { + "read": True, + "write": False, + "execute": False, + }, + } + } + ], + "user": [ + { + user: { + "octal": 6, + "permissions": { + "read": True, + "write": True, + "execute": False, + }, + } + } + ], + "group": [ + { + group: { + "octal": 4, + "permissions": { + "read": True, + "write": False, + "execute": False, + }, + } + } + ], + "comment": {"owner": user, "group": group, "file": self.myfile}, + } + }, ) diff --git a/tests/integration/modules/test_linux_service.py b/tests/integration/modules/test_linux_service.py new file mode 100644 index 00000000000..b61d4a56ade --- /dev/null +++ b/tests/integration/modules/test_linux_service.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, print_function, unicode_literals + +import pytest +import salt.utils.path +import salt.utils.platform +import salt.utils.systemd +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, flaky +from tests.support.unit import skipIf + + +@destructiveTest +@pytest.mark.windows_whitelisted +class ServiceModuleTest(ModuleCase): + """ + Module testing the service module + """ + + def setUp(self): + self.service_name = "cron" + cmd_name = "crontab" + os_family = self.run_function("grains.get", ["os_family"]) + os_release = self.run_function("grains.get", ["osrelease"]) + if os_family == "RedHat": + if os_release[0] == "7": + self.skipTest( + "Disabled on CentOS 7 until we can fix SSH connection issues." + ) + self.service_name = "crond" + elif os_family == "Arch": + self.service_name = "sshd" + cmd_name = "systemctl" + elif os_family == "NILinuxRT": + self.service_name = "syslog" + cmd_name = "syslog-ng" + elif os_family == "MacOS": + self.service_name = "org.ntp.ntpd" + if int(os_release.split(".")[1]) >= 13: + self.service_name = "com.apple.AirPlayXPCHelper" + elif salt.utils.platform.is_windows(): + self.service_name = "Spooler" + + self.pre_srv_status = self.run_function("service.status", [self.service_name]) + self.pre_srv_enabled = ( + True + if self.service_name in self.run_function("service.get_enabled") + else False + ) + + if ( + salt.utils.path.which(cmd_name) is None + and not salt.utils.platform.is_windows() + ): + self.skipTest("{0} is not installed".format(cmd_name)) + + def tearDown(self): + post_srv_status = self.run_function("service.status", [self.service_name]) + post_srv_enabled = ( + True + if self.service_name in self.run_function("service.get_enabled") + else False + ) + + if post_srv_status != self.pre_srv_status: + if self.pre_srv_status: + self.run_function("service.enable", [self.service_name]) + else: + self.run_function("service.disable", [self.service_name]) + + if post_srv_enabled != self.pre_srv_enabled: + if self.pre_srv_enabled: + self.run_function("service.enable", [self.service_name]) + else: + self.run_function("service.disable", [self.service_name]) + del self.service_name + + @flaky + def test_service_status_running(self): + """ + test service.status execution module + when service is running + """ + self.run_function("service.start", [self.service_name]) + check_service = self.run_function("service.status", [self.service_name]) + self.assertTrue(check_service) + + def test_service_status_dead(self): + """ + test service.status execution module + when service is dead + """ + self.run_function("service.stop", [self.service_name]) + check_service = self.run_function("service.status", [self.service_name]) + self.assertFalse(check_service) + + def test_service_restart(self): + """ + test service.restart + """ + self.assertTrue(self.run_function("service.restart", [self.service_name])) + + def test_service_enable(self): + """ + test service.get_enabled and service.enable module + """ + # disable service before test + self.assertTrue(self.run_function("service.disable", [self.service_name])) + + self.assertTrue(self.run_function("service.enable", [self.service_name])) + self.assertIn(self.service_name, self.run_function("service.get_enabled")) + + def test_service_disable(self): + """ + test service.get_disabled and service.disable module + """ + # enable service before test + self.assertTrue(self.run_function("service.enable", [self.service_name])) + + self.assertTrue(self.run_function("service.disable", [self.service_name])) + if salt.utils.platform.is_darwin(): + self.assertTrue(self.run_function("service.disabled", [self.service_name])) + else: + self.assertIn(self.service_name, self.run_function("service.get_disabled")) + + def test_service_disable_doesnot_exist(self): + """ + test service.get_disabled and service.disable module + when service name does not exist + """ + # enable service before test + srv_name = "doesnotexist" + enable = self.run_function("service.enable", [srv_name]) + systemd = salt.utils.systemd.booted() + + # check service was not enabled + try: + self.assertFalse(enable) + except AssertionError: + self.assertIn("ERROR", enable) + + # check service was not disabled + if ( + tuple( + self.run_function("grains.item", ["osrelease_info"])["osrelease_info"] + ) + == (14, 0o4) + and not systemd + ): + # currently upstart does not have a mechanism to report if disabling a service fails if does not exist + self.assertTrue(self.run_function("service.disable", [srv_name])) + elif ( + self.run_function("grains.item", ["os"])["os"] == "Debian" + and self.run_function("grains.item", ["osmajorrelease"])["osmajorrelease"] + < 9 + and systemd + ): + # currently disabling a service via systemd that does not exist + # on Debian 8 results in a True return code + self.assertTrue(self.run_function("service.disable", [srv_name])) + else: + try: + disable = self.run_function("service.disable", [srv_name]) + self.assertFalse(disable) + except AssertionError: + self.assertTrue("error" in disable.lower()) + + if salt.utils.platform.is_darwin(): + self.assertFalse(self.run_function("service.disabled", [srv_name])) + else: + self.assertNotIn(srv_name, self.run_function("service.get_disabled")) + + @skipIf(not salt.utils.platform.is_windows(), "Windows Only Test") + def test_service_get_service_name(self): + """ + test service.get_service_name + """ + ret = self.run_function("service.get_service_name") + self.assertIn(self.service_name, ret.values()) diff --git a/tests/integration/modules/test_linux_shadow.py b/tests/integration/modules/test_linux_shadow.py new file mode 100644 index 00000000000..1f49db39a12 --- /dev/null +++ b/tests/integration/modules/test_linux_shadow.py @@ -0,0 +1,259 @@ +# -*- coding: utf-8 -*- +""" +integration tests for shadow linux +""" + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +import os +import random +import string + +import salt.modules.linux_shadow as shadow + +# Import Salt libs +import salt.utils.files +import salt.utils.platform +from salt.ext.six.moves import range + +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, flaky, skip_if_not_root +from tests.support.unit import skipIf + + +@skip_if_not_root +@skipIf(not salt.utils.platform.is_linux(), "These tests can only be run on linux") +class ShadowModuleTest(ModuleCase): + """ + Validate the linux shadow system module + """ + + def setUp(self): + """ + Get current settings + """ + self._password = self.run_function("shadow.gen_password", ["Password1234"]) + if "ERROR" in self._password: + self.fail("Failed to generate password: {0}".format(self._password)) + super(ShadowModuleTest, self).setUp() + os_grain = self.run_function("grains.item", ["kernel"]) + if os_grain["kernel"] not in "Linux": + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) + self._test_user = self.__random_string() + self._no_user = self.__random_string() + self._password = shadow.gen_password("Password1234") + + def __random_string(self, size=6): + """ + Generates a random username + """ + return "tu-" + "".join( + random.choice(string.ascii_lowercase + string.digits) for x in range(size) + ) + + @destructiveTest + def test_info(self): + """ + Test shadow.info + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + + # Correct Functionality + ret = self.run_function("shadow.info", [self._test_user]) + self.assertEqual(ret["name"], self._test_user) + + # User does not exist + ret = self.run_function("shadow.info", [self._no_user]) + self.assertEqual(ret["name"], "") + + @destructiveTest + def test_del_password(self): + """ + Test shadow.del_password + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + + # Correct Functionality + self.assertTrue(self.run_function("shadow.del_password", [self._test_user])) + self.assertEqual( + self.run_function("shadow.info", [self._test_user])["passwd"], "" + ) + + # User does not exist + self.assertFalse(self.run_function("shadow.del_password", [self._no_user])) + + @destructiveTest + def test_set_password(self): + """ + Test shadow.set_password + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + + # Correct Functionality + self.assertTrue( + self.run_function("shadow.set_password", [self._test_user, self._password]) + ) + + # User does not exist + self.assertFalse( + self.run_function("shadow.set_password", [self._no_user, self._password]) + ) + + @destructiveTest + def test_set_inactdays(self): + """ + Test shadow.set_inactdays + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + + # Correct Functionality + self.assertTrue( + self.run_function("shadow.set_inactdays", [self._test_user, 12]) + ) + + # User does not exist (set_inactdays return None is user does not exist) + self.assertFalse(self.run_function("shadow.set_inactdays", [self._no_user, 12])) + + @destructiveTest + def test_set_maxdays(self): + """ + Test shadow.set_maxdays + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + + # Correct Functionality + self.assertTrue(self.run_function("shadow.set_maxdays", [self._test_user, 12])) + + # User does not exist (set_inactdays return None is user does not exist) + self.assertFalse(self.run_function("shadow.set_maxdays", [self._no_user, 12])) + + @destructiveTest + def test_set_mindays(self): + """ + Test shadow.set_mindays + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + + # Correct Functionality + self.assertTrue(self.run_function("shadow.set_mindays", [self._test_user, 12])) + + # User does not exist (set_inactdays return None is user does not exist) + self.assertFalse(self.run_function("shadow.set_mindays", [self._no_user, 12])) + + @flaky + @destructiveTest + def test_lock_password(self): + """ + Test shadow.lock_password + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + self.run_function("shadow.set_password", [self._test_user, self._password]) + + # Correct Functionality + self.assertTrue(self.run_function("shadow.lock_password", [self._test_user])) + + # User does not exist (set_inactdays return None is user does not exist) + self.assertFalse(self.run_function("shadow.lock_password", [self._no_user])) + + @destructiveTest + def test_unlock_password(self): + """ + Test shadow.lock_password + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + self.run_function("shadow.set_password", [self._test_user, self._password]) + + # Correct Functionality + self.assertTrue(self.run_function("shadow.unlock_password", [self._test_user])) + + # User does not exist (set_inactdays return None is user does not exist) + self.assertFalse(self.run_function("shadow.unlock_password", [self._no_user])) + + @destructiveTest + def test_set_warndays(self): + """ + Test shadow.set_warndays + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + + # Correct Functionality + self.assertTrue(self.run_function("shadow.set_warndays", [self._test_user, 12])) + + # User does not exist (set_inactdays return None is user does not exist) + self.assertFalse(self.run_function("shadow.set_warndays", [self._no_user, 12])) + + @destructiveTest + def test_set_date(self): + """ + Test shadow.set_date + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + + # Correct Functionality + self.assertTrue( + self.run_function("shadow.set_date", [self._test_user, "2016-08-19"]) + ) + + # User does not exist (set_inactdays return None is user does not exist) + self.assertFalse( + self.run_function("shadow.set_date", [self._no_user, "2016-08-19"]) + ) + + @destructiveTest + def test_set_expire(self): + """ + Test shadow.set_exipre + """ + self.addCleanup(self.run_function, "user.delete", [self._test_user]) + self.run_function("user.add", [self._test_user]) + + # Correct Functionality + self.assertTrue( + self.run_function("shadow.set_expire", [self._test_user, "2016-08-25"]) + ) + + # User does not exist (set_inactdays return None is user does not exist) + self.assertFalse( + self.run_function("shadow.set_expire", [self._no_user, "2016-08-25"]) + ) + + @destructiveTest + def test_set_del_root_password(self): + """ + Test set/del password for root + """ + # saving shadow file + if not os.access("/etc/shadow", os.R_OK | os.W_OK): + self.skipTest("Could not save initial state of /etc/shadow") + + def restore_shadow_file(contents): + # restore shadow file + with salt.utils.files.fopen("/etc/shadow", "w") as wfh: + wfh.write(contents) + + with salt.utils.files.fopen("/etc/shadow", "r") as rfh: + contents = rfh.read() + self.addCleanup(restore_shadow_file, contents) + + # set root password + self.assertTrue( + self.run_function("shadow.set_password", ["root", self._password]) + ) + self.assertEqual( + self.run_function("shadow.info", ["root"])["passwd"], self._password + ) + # delete root password + self.assertTrue(self.run_function("shadow.del_password", ["root"])) + self.assertEqual(self.run_function("shadow.info", ["root"])["passwd"], "") diff --git a/tests/integration/modules/test_localemod.py b/tests/integration/modules/test_localemod.py index 50dccdb03a4..d4f77745658 100644 --- a/tests/integration/modules/test_localemod.py +++ b/tests/integration/modules/test_localemod.py @@ -1,60 +1,57 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import ( - requires_salt_modules, - destructiveTest, -) - -# Import Salt libs +import pytest import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, requires_salt_modules +from tests.support.unit import skipIf def _find_new_locale(current_locale): - for locale in ['en_US.UTF-8', 'de_DE.UTF-8', 'fr_FR.UTF-8']: + for locale in ["en_US.UTF-8", "de_DE.UTF-8", "fr_FR.UTF-8"]: if locale != current_locale: return locale -@skipIf(salt.utils.platform.is_windows(), 'minion is windows') -@skipIf(salt.utils.platform.is_darwin(), 'locale method is not supported on mac') -@requires_salt_modules('locale') +@skipIf(salt.utils.platform.is_windows(), "minion is windows") +@skipIf(salt.utils.platform.is_darwin(), "locale method is not supported on mac") +@requires_salt_modules("locale") +@pytest.mark.windows_whitelisted class LocaleModuleTest(ModuleCase): def test_get_locale(self): - locale = self.run_function('locale.get_locale') - self.assertNotIn('Unsupported platform!', locale) + locale = self.run_function("locale.get_locale") + self.assertNotIn("Unsupported platform!", locale) @destructiveTest def test_gen_locale(self): # Make sure charmaps are available on test system before attempting # call gen_locale. We log this error to the user in the function, but # we don't want to fail this test if this is missing on the test system. - char_maps = self.run_function('cmd.run_all', ['locale -m']) - if char_maps['stdout'] == '': - self.skipTest('locale charmaps not available. Skipping test.') + char_maps = self.run_function("cmd.run_all", ["locale -m"]) + if char_maps["stdout"] == "": + self.skipTest("locale charmaps not available. Skipping test.") - if char_maps['retcode'] and char_maps['stderr']: - self.skipTest('{0}. Cannot generate locale. Skipping test.'.format( - char_maps['stderr']) + if char_maps["retcode"] and char_maps["stderr"]: + self.skipTest( + "{0}. Cannot generate locale. Skipping test.".format( + char_maps["stderr"] + ) ) - locale = self.run_function('locale.get_locale') + locale = self.run_function("locale.get_locale") new_locale = _find_new_locale(locale) - ret = self.run_function('locale.gen_locale', [new_locale]) + ret = self.run_function("locale.gen_locale", [new_locale]) self.assertTrue(ret) @destructiveTest def test_set_locale(self): - original_locale = self.run_function('locale.get_locale') + original_locale = self.run_function("locale.get_locale") locale_to_set = _find_new_locale(original_locale) - self.run_function('locale.gen_locale', [locale_to_set]) - ret = self.run_function('locale.set_locale', [locale_to_set]) - new_locale = self.run_function('locale.get_locale') + self.run_function("locale.gen_locale", [locale_to_set]) + ret = self.run_function("locale.set_locale", [locale_to_set]) + new_locale = self.run_function("locale.get_locale") self.assertTrue(ret) self.assertEqual(locale_to_set, new_locale) - self.run_function('locale.set_locale', [original_locale]) + self.run_function("locale.set_locale", [original_locale]) diff --git a/tests/integration/modules/test_lxc.py b/tests/integration/modules/test_lxc.py index 253dc310fa3..0e3ff917859 100644 --- a/tests/integration/modules/test_lxc.py +++ b/tests/integration/modules/test_lxc.py @@ -1,102 +1,107 @@ # -*- coding: utf-8 -*- -''' +""" Test the lxc module -''' +""" -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest +from salt.ext import six from tests.support.case import ModuleCase -from tests.support.helpers import ( - skip_if_not_root, - skip_if_binaries_missing -) +from tests.support.helpers import skip_if_binaries_missing, skip_if_not_root from tests.support.unit import skipIf -# Import 3rd-party libs -from salt.ext import six - -@skipIf(True, - 'Needs rewrite to be more distro agnostic. Also, the tearDown ' - 'function destroys ALL containers on the box, which is BAD.') +@skipIf( + True, + "Needs rewrite to be more distro agnostic. Also, the tearDown " + "function destroys ALL containers on the box, which is BAD.", +) @skip_if_not_root -@skip_if_binaries_missing('lxc-start', message='LXC is not installed or minimal version not met') +@skip_if_binaries_missing( + "lxc-start", message="LXC is not installed or minimal version not met" +) +@pytest.mark.windows_whitelisted class LXCModuleTest(ModuleCase): - ''' + """ Test the lxc module - ''' - prefix = '_salttesting' + """ + + prefix = "_salttesting" def setUp(self): - os = self.run_function('grains.item', - ['os', 'oscodename', 'osarch']) + os = self.run_function("grains.item", ["os", "oscodename", "osarch"]) - p = {'download': - {'dist': os['os'].lower(), - 'arch': os['osarch'].lower(), - 'template': 'download', - 'release': os['oscodename'].lower()}, - 'sshd': {'template': 'sshd'}} - self.run_function('grains.setval', ['lxc.profile', p]) + p = { + "download": { + "dist": os["os"].lower(), + "arch": os["osarch"].lower(), + "template": "download", + "release": os["oscodename"].lower(), + }, + "sshd": {"template": "sshd"}, + } + self.run_function("grains.setval", ["lxc.profile", p]) def tearDown(self): - ''' + """ Clean up any LXCs created. - ''' - r = self.run_function('lxc.list') + """ + r = self.run_function("lxc.list") for k, v in six.iteritems(r): for x in v: if x.startswith(self.prefix): - self.run_function('lxc.destroy', [x]) + self.run_function("lxc.destroy", [x]) def test_create_destroy(self): - ''' + """ Test basic create/destroy of an LXC. - ''' + """ - r = self.run_function('lxc.create', [self.prefix], - template='sshd') - self.assertEqual(r, {'state': {'new': 'stopped', 'old': None}, - 'result': True}) - self.assertTrue(self.run_function('lxc.exists', [self.prefix])) - r = self.run_function('lxc.destroy', [self.prefix]) - self.assertEqual(r, {'state': None, 'change': True}) - self.assertFalse(self.run_function('lxc.exists', [self.prefix])) + r = self.run_function("lxc.create", [self.prefix], template="sshd") + self.assertEqual(r, {"state": {"new": "stopped", "old": None}, "result": True}) + self.assertTrue(self.run_function("lxc.exists", [self.prefix])) + r = self.run_function("lxc.destroy", [self.prefix]) + self.assertEqual(r, {"state": None, "change": True}) + self.assertFalse(self.run_function("lxc.exists", [self.prefix])) def test_init(self): - ''' + """ Test basic init functionality. - ''' + """ - r = self.run_function('lxc.init', [self.prefix], - profile='sshd', seed=False) - self.assertTrue(r.get('created', False)) - self.assertTrue(self.run_function('lxc.exists', [self.prefix])) + r = self.run_function("lxc.init", [self.prefix], profile="sshd", seed=False) + self.assertTrue(r.get("created", False)) + self.assertTrue(self.run_function("lxc.exists", [self.prefix])) def test_macvlan(self): - ''' + """ Regression test for macvlan nic profile. - ''' + """ - p = {"macvlan": {"eth0": { - "macvlan.mode": "bridge", - "link": "eth0", - "type": "macvlan"}}} + p = { + "macvlan": { + "eth0": {"macvlan.mode": "bridge", "link": "eth0", "type": "macvlan"} + } + } - self.run_function('grains.setval', ['lxc.nic', p]) + self.run_function("grains.setval", ["lxc.nic", p]) - self.run_function('lxc.init', [self.prefix], - profile='sshd', nic='macvlan', - seed=False, start=False) + self.run_function( + "lxc.init", + [self.prefix], + profile="sshd", + nic="macvlan", + seed=False, + start=False, + ) - f = '/var/lib/lxc/{0}/config'.format(self.prefix) - conf = self.run_function('lxc.read_conf', [f]) + f = "/var/lib/lxc/{0}/config".format(self.prefix) + conf = self.run_function("lxc.read_conf", [f]) # Due to a segfault in lxc-destroy caused by invalid configs, # truncate the config. - self.run_function('cmd.run', ['truncate -s 0 {0}'.format(f)]) + self.run_function("cmd.run", ["truncate -s 0 {0}".format(f)]) - self.assertEqual(conf.get('lxc.network.type'), 'macvlan') + self.assertEqual(conf.get("lxc.network.type"), "macvlan") diff --git a/tests/integration/modules/test_mac_assistive.py b/tests/integration/modules/test_mac_assistive.py index 79df8016411..b9468aba6d8 100644 --- a/tests/integration/modules/test_mac_assistive.py +++ b/tests/integration/modules/test_mac_assistive.py @@ -1,115 +1,91 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest, skip_if_not_root -OSA_SCRIPT = '/usr/bin/osascript' +OSA_SCRIPT = "/usr/bin/osascript" @destructiveTest @skip_if_not_root class MacAssistiveTest(ModuleCase): - ''' + """ Integration tests for the mac_assistive module. - ''' + """ def setUp(self): - ''' + """ Sets up test requirements - ''' - os_grain = self.run_function('grains.item', ['kernel']) - if os_grain['kernel'] not in 'Darwin': - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) + """ + os_grain = self.run_function("grains.item", ["kernel"]) + if os_grain["kernel"] not in "Darwin": + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) # Let's install a bundle to use in tests - self.run_function('assistive.install', [OSA_SCRIPT, True]) + self.run_function("assistive.install", [OSA_SCRIPT, True]) def tearDown(self): - ''' + """ Clean up after tests - ''' + """ # Delete any bundles that were installed - osa_script = self.run_function('assistive.installed', [OSA_SCRIPT]) + osa_script = self.run_function("assistive.installed", [OSA_SCRIPT]) if osa_script: - self.run_function('assistive.remove', [OSA_SCRIPT]) + self.run_function("assistive.remove", [OSA_SCRIPT]) - smile_bundle = 'com.smileonmymac.textexpander' - smile_bundle_present = self.run_function('assistive.installed', [smile_bundle]) + smile_bundle = "com.smileonmymac.textexpander" + smile_bundle_present = self.run_function("assistive.installed", [smile_bundle]) if smile_bundle_present: - self.run_function('assistive.remove', [smile_bundle]) + self.run_function("assistive.remove", [smile_bundle]) def test_install_and_remove(self): - ''' + """ Tests installing and removing a bundled ID or command to use assistive access. - ''' - new_bundle = 'com.smileonmymac.textexpander' - self.assertTrue( - self.run_function('assistive.install', [new_bundle]) - ) - self.assertTrue( - self.run_function('assistive.remove', [new_bundle]) - ) + """ + new_bundle = "com.smileonmymac.textexpander" + self.assertTrue(self.run_function("assistive.install", [new_bundle])) + self.assertTrue(self.run_function("assistive.remove", [new_bundle])) def test_installed(self): - ''' + """ Tests the True and False return of assistive.installed. - ''' + """ # OSA script should have been installed in setUp function - self.assertTrue( - self.run_function('assistive.installed', [OSA_SCRIPT]) - ) + self.assertTrue(self.run_function("assistive.installed", [OSA_SCRIPT])) # Clean up install - self.run_function('assistive.remove', [OSA_SCRIPT]) + self.run_function("assistive.remove", [OSA_SCRIPT]) # Installed should now return False - self.assertFalse( - self.run_function('assistive.installed', [OSA_SCRIPT]) - ) + self.assertFalse(self.run_function("assistive.installed", [OSA_SCRIPT])) def test_enable(self): - ''' + """ Tests setting the enabled status of a bundled ID or command. - ''' + """ # OSA script should have been installed and enabled in setUp function # Now let's disable it, which should return True. - self.assertTrue( - self.run_function('assistive.enable', [OSA_SCRIPT, False]) - ) + self.assertTrue(self.run_function("assistive.enable", [OSA_SCRIPT, False])) # Double check the script was disabled, as intended. - self.assertFalse( - self.run_function('assistive.enabled', [OSA_SCRIPT]) - ) + self.assertFalse(self.run_function("assistive.enabled", [OSA_SCRIPT])) # Now re-enable - self.assertTrue( - self.run_function('assistive.enable', [OSA_SCRIPT]) - ) + self.assertTrue(self.run_function("assistive.enable", [OSA_SCRIPT])) # Double check the script was enabled, as intended. - self.assertTrue( - self.run_function('assistive.enabled', [OSA_SCRIPT]) - ) + self.assertTrue(self.run_function("assistive.enabled", [OSA_SCRIPT])) def test_enabled(self): - ''' + """ Tests if a bundled ID or command is listed in assistive access returns True. - ''' + """ # OSA script should have been installed in setUp function, which sets # enabled to True by default. - self.assertTrue( - self.run_function('assistive.enabled', [OSA_SCRIPT]) - ) + self.assertTrue(self.run_function("assistive.enabled", [OSA_SCRIPT])) # Disable OSA Script - self.run_function('assistive.enable', [OSA_SCRIPT, False]) + self.run_function("assistive.enable", [OSA_SCRIPT, False]) # Assert against new disabled status - self.assertFalse( - self.run_function('assistive.enabled', [OSA_SCRIPT]) - ) + self.assertFalse(self.run_function("assistive.enabled", [OSA_SCRIPT])) diff --git a/tests/integration/modules/test_mac_brew_pkg.py b/tests/integration/modules/test_mac_brew_pkg.py index 5b5362b306e..ba534322b58 100644 --- a/tests/integration/modules/test_mac_brew_pkg.py +++ b/tests/integration/modules/test_mac_brew_pkg.py @@ -1,15 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, skip_if_not_root +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.utils.path @@ -19,124 +14,148 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six +# Import Salt Testing Libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root +from tests.support.unit import skipIf + # Brew doesn't support local package installation - So, let's # Grab some small packages available online for brew -ADD_PKG = 'algol68g' -DEL_PKG = 'acme' +ADD_PKG = "algol68g" +DEL_PKG = "acme" @destructiveTest @skip_if_not_root -@skipIf(not salt.utils.platform.is_darwin(), 'Test only applies to macOS') -@skipIf(not salt.utils.path.which('brew'), 'This test requires the brew binary') +@skipIf(not salt.utils.platform.is_darwin(), "Test only applies to macOS") +@skipIf(not salt.utils.path.which("brew"), "This test requires the brew binary") class BrewModuleTest(ModuleCase): - ''' + """ Integration tests for the brew module - ''' + """ + def test_brew_install(self): - ''' + """ Tests the installation of packages - ''' + """ try: - self.run_function('pkg.install', [ADD_PKG]) - pkg_list = self.run_function('pkg.list_pkgs') + self.run_function("pkg.install", [ADD_PKG]) + pkg_list = self.run_function("pkg.list_pkgs") try: self.assertIn(ADD_PKG, pkg_list) except AssertionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise except CommandExecutionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise def test_remove(self): - ''' + """ Tests the removal of packages - ''' + """ try: # Install a package to delete - If unsuccessful, skip the test - self.run_function('pkg.install', [DEL_PKG]) - pkg_list = self.run_function('pkg.list_pkgs') + self.run_function("pkg.install", [DEL_PKG]) + pkg_list = self.run_function("pkg.list_pkgs") if DEL_PKG not in pkg_list: - self.run_function('pkg.install', [DEL_PKG]) - self.skipTest('Failed to install a package to delete') + self.run_function("pkg.install", [DEL_PKG]) + self.skipTest("Failed to install a package to delete") # Now remove the installed package - self.run_function('pkg.remove', [DEL_PKG]) - del_list = self.run_function('pkg.list_pkgs') + self.run_function("pkg.remove", [DEL_PKG]) + del_list = self.run_function("pkg.list_pkgs") self.assertNotIn(DEL_PKG, del_list) except CommandExecutionError: - self.run_function('pkg.remove', [DEL_PKG]) + self.run_function("pkg.remove", [DEL_PKG]) raise def test_version(self): - ''' + """ Test pkg.version for mac. Installs a package and then checks we can get a version for the installed package. - ''' + """ try: - self.run_function('pkg.install', [ADD_PKG]) - pkg_list = self.run_function('pkg.list_pkgs') - version = self.run_function('pkg.version', [ADD_PKG]) + self.run_function("pkg.install", [ADD_PKG]) + pkg_list = self.run_function("pkg.list_pkgs") + version = self.run_function("pkg.version", [ADD_PKG]) try: - self.assertTrue(version, - msg=('version: {0} is empty,\ - or other issue is present'.format(version))) - self.assertIn(ADD_PKG, pkg_list, - msg=('package: {0} is not in\ - the list of installed packages: {1}' - .format(ADD_PKG, pkg_list))) - #make sure the version is accurate and is listed in the pkg_list - self.assertIn(version, six.text_type(pkg_list[ADD_PKG]), - msg=('The {0} version: {1} is \ - not listed in the pkg_list: {2}' - .format(ADD_PKG, version, pkg_list[ADD_PKG]))) + self.assertTrue( + version, + msg=( + "version: {0} is empty,\ + or other issue is present".format( + version + ) + ), + ) + self.assertIn( + ADD_PKG, + pkg_list, + msg=( + "package: {0} is not in\ + the list of installed packages: {1}".format( + ADD_PKG, pkg_list + ) + ), + ) + # make sure the version is accurate and is listed in the pkg_list + self.assertIn( + version, + six.text_type(pkg_list[ADD_PKG]), + msg=( + "The {0} version: {1} is \ + not listed in the pkg_list: {2}".format( + ADD_PKG, version, pkg_list[ADD_PKG] + ) + ), + ) except AssertionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise except CommandExecutionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise def test_latest_version(self): - ''' + """ Test pkg.latest_version: - get the latest version available - install the package - get the latest version available - check that the latest version is empty after installing it - ''' + """ try: - self.run_function('pkg.remove', [ADD_PKG]) - uninstalled_latest = self.run_function('pkg.latest_version', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) + uninstalled_latest = self.run_function("pkg.latest_version", [ADD_PKG]) - self.run_function('pkg.install', [ADD_PKG]) - installed_latest = self.run_function('pkg.latest_version', [ADD_PKG]) - version = self.run_function('pkg.version', [ADD_PKG]) + self.run_function("pkg.install", [ADD_PKG]) + installed_latest = self.run_function("pkg.latest_version", [ADD_PKG]) + version = self.run_function("pkg.version", [ADD_PKG]) try: self.assertTrue(isinstance(uninstalled_latest, six.string_types)) self.assertEqual(installed_latest, version) except AssertionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise except CommandExecutionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise def test_refresh_db(self): - ''' + """ Integration test to ensure pkg.refresh_db works with brew - ''' - refresh_brew = self.run_function('pkg.refresh_db') + """ + refresh_brew = self.run_function("pkg.refresh_db") self.assertTrue(refresh_brew) def test_list_upgrades(self): - ''' + """ Test pkg.list_upgrades: data is in the form {'name1': 'version1', 'name2': 'version2', ... } - ''' + """ try: - upgrades = self.run_function('pkg.list_upgrades') + upgrades = self.run_function("pkg.list_upgrades") try: self.assertTrue(isinstance(upgrades, dict)) if upgrades: @@ -144,40 +163,40 @@ class BrewModuleTest(ModuleCase): self.assertTrue(isinstance(name, six.string_types)) self.assertTrue(isinstance(upgrades[name], six.string_types)) except AssertionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise except CommandExecutionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise def test_info_installed(self): - ''' + """ Test pkg.info_installed: info returned has certain fields used by mac_brew.latest_version - ''' + """ try: - self.run_function('pkg.install', [ADD_PKG]) - info = self.run_function('pkg.info_installed', [ADD_PKG]) + self.run_function("pkg.install", [ADD_PKG]) + info = self.run_function("pkg.info_installed", [ADD_PKG]) try: self.assertTrue(ADD_PKG in info) - self.assertTrue('versions' in info[ADD_PKG]) - self.assertTrue('revision' in info[ADD_PKG]) - self.assertTrue('stable' in info[ADD_PKG]['versions']) + self.assertTrue("versions" in info[ADD_PKG]) + self.assertTrue("revision" in info[ADD_PKG]) + self.assertTrue("stable" in info[ADD_PKG]["versions"]) except AssertionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise except CommandExecutionError: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) raise def tearDown(self): - ''' + """ Clean up after tests - ''' - pkg_list = self.run_function('pkg.list_pkgs') + """ + pkg_list = self.run_function("pkg.list_pkgs") # Remove any installed packages if ADD_PKG in pkg_list: - self.run_function('pkg.remove', [ADD_PKG]) + self.run_function("pkg.remove", [ADD_PKG]) if DEL_PKG in pkg_list: - self.run_function('pkg.remove', [DEL_PKG]) + self.run_function("pkg.remove", [DEL_PKG]) diff --git a/tests/integration/modules/test_mac_desktop.py b/tests/integration/modules/test_mac_desktop.py index 749dfd869a6..f4978acae93 100644 --- a/tests/integration/modules/test_mac_desktop.py +++ b/tests/integration/modules/test_mac_desktop.py @@ -1,81 +1,72 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the mac_desktop execution module. -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +# Import 3rd-party libs +from salt.ext import six # Import Salt Testing Libs from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest, skip_if_not_root -# Import 3rd-party libs -from salt.ext import six - @destructiveTest @skip_if_not_root class MacDesktopTestCase(ModuleCase): - ''' + """ Integration tests for the mac_desktop module. - ''' + """ def setUp(self): - ''' + """ Sets up test requirements. - ''' - os_grain = self.run_function('grains.item', ['kernel']) - if os_grain['kernel'] not in 'Darwin': - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) + """ + os_grain = self.run_function("grains.item", ["kernel"]) + if os_grain["kernel"] not in "Darwin": + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) def test_get_output_volume(self): - ''' + """ Tests the return of get_output_volume. - ''' - ret = self.run_function('desktop.get_output_volume') + """ + ret = self.run_function("desktop.get_output_volume") self.assertIsNotNone(ret) def test_set_output_volume(self): - ''' + """ Tests the return of set_output_volume. - ''' - current_vol = self.run_function('desktop.get_output_volume') + """ + current_vol = self.run_function("desktop.get_output_volume") to_set = 10 if current_vol == six.text_type(to_set): to_set += 2 - new_vol = self.run_function('desktop.set_output_volume', - [six.text_type(to_set)]) - check_vol = self.run_function('desktop.get_output_volume') + new_vol = self.run_function( + "desktop.set_output_volume", [six.text_type(to_set)] + ) + check_vol = self.run_function("desktop.get_output_volume") self.assertEqual(new_vol, check_vol) # Set volume back to what it was before - self.run_function('desktop.set_output_volume', [current_vol]) + self.run_function("desktop.set_output_volume", [current_vol]) def test_screensaver(self): - ''' + """ Tests the return of the screensaver function. - ''' - self.assertTrue( - self.run_function('desktop.screensaver') - ) + """ + self.assertTrue(self.run_function("desktop.screensaver")) def test_lock(self): - ''' + """ Tests the return of the lock function. - ''' - self.assertTrue( - self.run_function('desktop.lock') - ) + """ + self.assertTrue(self.run_function("desktop.lock")) def test_say(self): - ''' + """ Tests the return of the say function. - ''' - self.assertTrue( - self.run_function('desktop.say', ['hello', 'world']) - ) + """ + self.assertTrue(self.run_function("desktop.say", ["hello", "world"])) diff --git a/tests/integration/modules/test_mac_group.py b/tests/integration/modules/test_mac_group.py index d49e172b700..2c966f724a1 100644 --- a/tests/integration/modules/test_mac_group.py +++ b/tests/integration/modules/test_mac_group.py @@ -1,32 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import random import string +# Import Salt Libs +from salt.exceptions import CommandExecutionError +from salt.ext import six + +# Import 3rd-party libs +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin + # Import Salt Testing Libs from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest, skip_if_not_root -# Import Salt Libs -from salt.exceptions import CommandExecutionError - -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -from salt.ext import six - def __random_string(size=6): - ''' + """ Generates a random username - ''' - return 'RS-' + ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(size) + """ + return "RS-" + "".join( + random.choice(string.ascii_uppercase + string.digits) for x in range(size) ) @@ -41,148 +41,151 @@ REP_USER_GROUP = __random_string() @destructiveTest @skip_if_not_root class MacGroupModuleTest(ModuleCase): - ''' + """ Integration tests for the mac_group module - ''' + """ def setUp(self): - ''' + """ Sets up test requirements - ''' - os_grain = self.run_function('grains.item', ['kernel']) - if os_grain['kernel'] not in 'Darwin': - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) + """ + os_grain = self.run_function("grains.item", ["kernel"]) + if os_grain["kernel"] not in "Darwin": + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) def test_mac_group_add(self): - ''' + """ Tests the add group function - ''' + """ try: - self.run_function('group.add', [ADD_GROUP, 3456]) - group_info = self.run_function('group.info', [ADD_GROUP]) - self.assertEqual(group_info['name'], ADD_GROUP) + self.run_function("group.add", [ADD_GROUP, 3456]) + group_info = self.run_function("group.info", [ADD_GROUP]) + self.assertEqual(group_info["name"], ADD_GROUP) except CommandExecutionError: - self.run_function('group.delete', [ADD_GROUP]) + self.run_function("group.delete", [ADD_GROUP]) raise def test_mac_group_delete(self): - ''' + """ Tests the delete group function - ''' + """ # Create a group to delete - If unsuccessful, skip the test - if self.run_function('group.add', [DEL_GROUP, 4567]) is not True: - self.run_function('group.delete', [DEL_GROUP]) - self.skipTest('Failed to create a group to delete') + if self.run_function("group.add", [DEL_GROUP, 4567]) is not True: + self.run_function("group.delete", [DEL_GROUP]) + self.skipTest("Failed to create a group to delete") # Now try to delete the added group - ret = self.run_function('group.delete', [DEL_GROUP]) + ret = self.run_function("group.delete", [DEL_GROUP]) self.assertTrue(ret) def test_mac_group_chgid(self): - ''' + """ Tests changing the group id - ''' + """ # Create a group to delete - If unsuccessful, skip the test - if self.run_function('group.add', [CHANGE_GROUP, 5678]) is not True: - self.run_function('group.delete', [CHANGE_GROUP]) - self.skipTest('Failed to create a group to manipulate') + if self.run_function("group.add", [CHANGE_GROUP, 5678]) is not True: + self.run_function("group.delete", [CHANGE_GROUP]) + self.skipTest("Failed to create a group to manipulate") try: - self.run_function('group.chgid', [CHANGE_GROUP, 6789]) - group_info = self.run_function('group.info', [CHANGE_GROUP]) - self.assertEqual(group_info['gid'], 6789) + self.run_function("group.chgid", [CHANGE_GROUP, 6789]) + group_info = self.run_function("group.info", [CHANGE_GROUP]) + self.assertEqual(group_info["gid"], 6789) except AssertionError: - self.run_function('group.delete', [CHANGE_GROUP]) + self.run_function("group.delete", [CHANGE_GROUP]) raise def test_mac_adduser(self): - ''' + """ Tests adding user to the group - ''' + """ # Create a group to use for test - If unsuccessful, skip the test - if self.run_function('group.add', [ADD_GROUP, 5678]) is not True: - self.run_function('group.delete', [ADD_GROUP]) - self.skipTest('Failed to create a group to manipulate') + if self.run_function("group.add", [ADD_GROUP, 5678]) is not True: + self.run_function("group.delete", [ADD_GROUP]) + self.skipTest("Failed to create a group to manipulate") try: - self.run_function('group.adduser', [ADD_GROUP, ADD_USER]) - group_info = self.run_function('group.info', [ADD_GROUP]) - self.assertEqual(ADD_USER, ''.join(group_info['members'])) + self.run_function("group.adduser", [ADD_GROUP, ADD_USER]) + group_info = self.run_function("group.info", [ADD_GROUP]) + self.assertEqual(ADD_USER, "".join(group_info["members"])) except AssertionError: - self.run_function('group.delete', [ADD_GROUP]) + self.run_function("group.delete", [ADD_GROUP]) raise def test_mac_deluser(self): - ''' + """ Test deleting user from a group - ''' + """ # Create a group to use for test - If unsuccessful, skip the test - if self.run_function('group.add', [ADD_GROUP, 5678]) and \ - self.run_function('group.adduser', [ADD_GROUP, ADD_USER]) is not True: - self.run_function('group.delete', [ADD_GROUP]) - self.skipTest('Failed to create a group to manipulate') + if ( + self.run_function("group.add", [ADD_GROUP, 5678]) + and self.run_function("group.adduser", [ADD_GROUP, ADD_USER]) is not True + ): + self.run_function("group.delete", [ADD_GROUP]) + self.skipTest("Failed to create a group to manipulate") - delusr = self.run_function('group.deluser', [ADD_GROUP, ADD_USER]) + delusr = self.run_function("group.deluser", [ADD_GROUP, ADD_USER]) self.assertTrue(delusr) - group_info = self.run_function('group.info', [ADD_GROUP]) - self.assertNotIn(ADD_USER, ''.join(group_info['members'])) + group_info = self.run_function("group.info", [ADD_GROUP]) + self.assertNotIn(ADD_USER, "".join(group_info["members"])) def test_mac_members(self): - ''' + """ Test replacing members of a group - ''' - if self.run_function('group.add', [ADD_GROUP, 5678]) and \ - self.run_function('group.adduser', [ADD_GROUP, ADD_USER]) is not True: - self.run_function('group.delete', [ADD_GROUP]) - self.skipTest('Failed to create the {0} group or add user {1} to group ' - 'to manipulate'.format(ADD_GROUP, - ADD_USER)) + """ + if ( + self.run_function("group.add", [ADD_GROUP, 5678]) + and self.run_function("group.adduser", [ADD_GROUP, ADD_USER]) is not True + ): + self.run_function("group.delete", [ADD_GROUP]) + self.skipTest( + "Failed to create the {0} group or add user {1} to group " + "to manipulate".format(ADD_GROUP, ADD_USER) + ) - rep_group_mem = self.run_function('group.members', - [ADD_GROUP, REP_USER_GROUP]) + rep_group_mem = self.run_function("group.members", [ADD_GROUP, REP_USER_GROUP]) self.assertTrue(rep_group_mem) # ensure new user is added to group and previous user is removed - group_info = self.run_function('group.info', [ADD_GROUP]) - self.assertIn(REP_USER_GROUP, six.text_type(group_info['members'])) - self.assertNotIn(ADD_USER, six.text_type(group_info['members'])) + group_info = self.run_function("group.info", [ADD_GROUP]) + self.assertIn(REP_USER_GROUP, six.text_type(group_info["members"])) + self.assertNotIn(ADD_USER, six.text_type(group_info["members"])) def test_mac_getent(self): - ''' + """ Test returning info on all groups - ''' - if self.run_function('group.add', [ADD_GROUP, 5678]) and \ - self.run_function('group.adduser', [ADD_GROUP, ADD_USER])is not True: - self.run_function('group.delete', [ADD_GROUP]) - self.skipTest('Failed to create the {0} group or add user {1} to group ' - 'to manipulate'.format(ADD_GROUP, - ADD_USER)) + """ + if ( + self.run_function("group.add", [ADD_GROUP, 5678]) + and self.run_function("group.adduser", [ADD_GROUP, ADD_USER]) is not True + ): + self.run_function("group.delete", [ADD_GROUP]) + self.skipTest( + "Failed to create the {0} group or add user {1} to group " + "to manipulate".format(ADD_GROUP, ADD_USER) + ) - getinfo = self.run_function('group.getent') + getinfo = self.run_function("group.getent") self.assertTrue(getinfo) self.assertIn(ADD_GROUP, six.text_type(getinfo)) self.assertIn(ADD_USER, six.text_type(getinfo)) def tearDown(self): - ''' + """ Clean up after tests - ''' + """ # Delete ADD_GROUP - add_info = self.run_function('group.info', [ADD_GROUP]) + add_info = self.run_function("group.info", [ADD_GROUP]) if add_info: - self.run_function('group.delete', [ADD_GROUP]) + self.run_function("group.delete", [ADD_GROUP]) # Delete DEL_GROUP if something failed - del_info = self.run_function('group.info', [DEL_GROUP]) + del_info = self.run_function("group.info", [DEL_GROUP]) if del_info: - self.run_function('group.delete', [DEL_GROUP]) + self.run_function("group.delete", [DEL_GROUP]) # Delete CHANGE_GROUP - change_info = self.run_function('group.info', [CHANGE_GROUP]) + change_info = self.run_function("group.info", [CHANGE_GROUP]) if change_info: - self.run_function('group.delete', [CHANGE_GROUP]) + self.run_function("group.delete", [CHANGE_GROUP]) diff --git a/tests/integration/modules/test_mac_keychain.py b/tests/integration/modules/test_mac_keychain.py index 2f16901e20a..3f1320168fb 100644 --- a/tests/integration/modules/test_mac_keychain.py +++ b/tests/integration/modules/test_mac_keychain.py @@ -1,16 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Validate the mac-keychain module -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.case import ModuleCase -from tests.support.runtests import RUNTIME_VARS -from tests.support.helpers import destructiveTest, skip_if_not_root +import os # Import Salt Libs from salt.exceptions import CommandExecutionError @@ -18,106 +14,106 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six +# Import Salt Testing Libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root +from tests.support.runtests import RUNTIME_VARS + @destructiveTest @skip_if_not_root class MacKeychainModuleTest(ModuleCase): - ''' + """ Integration tests for the mac_keychain module - ''' + """ @classmethod def setUpClass(cls): cls.cert = os.path.join( - RUNTIME_VARS.FILES, - 'file', - 'base', - 'certs', - 'salttest.p12' + RUNTIME_VARS.FILES, "file", "base", "certs", "salttest.p12" ) - cls.cert_alias = 'Salt Test' - cls.passwd = 'salttest' + cls.cert_alias = "Salt Test" + cls.passwd = "salttest" def setUp(self): - ''' + """ Sets up the test requirements - ''' - os_grain = self.run_function('grains.item', ['kernel']) + """ + os_grain = self.run_function("grains.item", ["kernel"]) # Must be running on a mac - if os_grain['kernel'] not in 'Darwin': - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) + if os_grain["kernel"] not in "Darwin": + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) def tearDown(self): - ''' + """ Clean up after tests - ''' + """ # Remove the salttest cert, if left over. - certs_list = self.run_function('keychain.list_certs') + certs_list = self.run_function("keychain.list_certs") if self.cert_alias in certs_list: - self.run_function('keychain.uninstall', [self.cert_alias]) + self.run_function("keychain.uninstall", [self.cert_alias]) def test_mac_keychain_install(self): - ''' + """ Tests that attempts to install a certificate - ''' - install_cert = self.run_function('keychain.install', [self.cert, self.passwd]) + """ + install_cert = self.run_function("keychain.install", [self.cert, self.passwd]) self.assertTrue(install_cert) # check to ensure the cert was installed - certs_list = self.run_function('keychain.list_certs') + certs_list = self.run_function("keychain.list_certs") self.assertIn(self.cert_alias, certs_list) def test_mac_keychain_uninstall(self): - ''' + """ Tests that attempts to uninstall a certificate - ''' - self.run_function('keychain.install', [self.cert, self.passwd]) - certs_list = self.run_function('keychain.list_certs') + """ + self.run_function("keychain.install", [self.cert, self.passwd]) + certs_list = self.run_function("keychain.list_certs") if self.cert_alias not in certs_list: - self.run_function('keychain.uninstall', [self.cert_alias]) - self.skipTest('Failed to install keychain') + self.run_function("keychain.uninstall", [self.cert_alias]) + self.skipTest("Failed to install keychain") # uninstall cert - self.run_function('keychain.uninstall', [self.cert_alias]) - certs_list = self.run_function('keychain.list_certs') + self.run_function("keychain.uninstall", [self.cert_alias]) + certs_list = self.run_function("keychain.list_certs") # check to ensure the cert was uninstalled try: self.assertNotIn(self.cert_alias, six.text_type(certs_list)) except CommandExecutionError: - self.run_function('keychain.uninstall', [self.cert_alias]) + self.run_function("keychain.uninstall", [self.cert_alias]) def test_mac_keychain_get_friendly_name(self): - ''' + """ Test that attempts to get friendly name of a cert - ''' - self.run_function('keychain.install', [self.cert, self.passwd]) - certs_list = self.run_function('keychain.list_certs') + """ + self.run_function("keychain.install", [self.cert, self.passwd]) + certs_list = self.run_function("keychain.list_certs") if self.cert_alias not in certs_list: - self.run_function('keychain.uninstall', [self.cert_alias]) - self.skipTest('Failed to install keychain') + self.run_function("keychain.uninstall", [self.cert_alias]) + self.skipTest("Failed to install keychain") - get_name = self.run_function('keychain.get_friendly_name', [self.cert, self.passwd]) + get_name = self.run_function( + "keychain.get_friendly_name", [self.cert, self.passwd] + ) self.assertEqual(get_name, self.cert_alias) def test_mac_keychain_get_default_keychain(self): - ''' + """ Test that attempts to get the default keychain - ''' - salt_get_keychain = self.run_function('keychain.get_default_keychain') - sys_get_keychain = self.run_function('cmd.run', - ['security default-keychain -d user']) + """ + salt_get_keychain = self.run_function("keychain.get_default_keychain") + sys_get_keychain = self.run_function( + "cmd.run", ["security default-keychain -d user"] + ) self.assertEqual(salt_get_keychain, sys_get_keychain) def test_mac_keychain_list_certs(self): - ''' + """ Test that attempts to list certs - ''' - cert_default = 'com.apple.systemdefault' - certs = self.run_function('keychain.list_certs') + """ + cert_default = "com.apple.systemdefault" + certs = self.run_function("keychain.list_certs") self.assertIn(cert_default, certs) diff --git a/tests/integration/modules/test_mac_pkgutil.py b/tests/integration/modules/test_mac_pkgutil.py index d5502c30e69..2a23c3188f0 100644 --- a/tests/integration/modules/test_mac_pkgutil.py +++ b/tests/integration/modules/test_mac_pkgutil.py @@ -1,97 +1,99 @@ # -*- coding: utf-8 -*- -''' +""" integration tests for mac_pkgutil -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.helpers import destructiveTest, skip_if_not_root +import os # Import Salt libs import salt.utils.path import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root -TEST_PKG_URL = 'https://distfiles.macports.org/MacPorts/MacPorts-2.3.4-10.11-ElCapitan.pkg' -TEST_PKG_NAME = 'org.macports.MacPorts' +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS + +TEST_PKG_URL = ( + "https://distfiles.macports.org/MacPorts/MacPorts-2.3.4-10.11-ElCapitan.pkg" +) +TEST_PKG_NAME = "org.macports.MacPorts" @skip_if_not_root class MacPkgutilModuleTest(ModuleCase): - ''' + """ Validate the mac_pkgutil module - ''' + """ @classmethod def setUpClass(cls): - cls.test_pkg = os.path.join(RUNTIME_VARS.TMP, 'MacPorts-2.3.4-10.11-ElCapitan.pkg') + cls.test_pkg = os.path.join( + RUNTIME_VARS.TMP, "MacPorts-2.3.4-10.11-ElCapitan.pkg" + ) def setUp(self): - ''' + """ Get current settings - ''' + """ if not salt.utils.platform.is_darwin(): - self.skipTest('Test only available on macOS') + self.skipTest("Test only available on macOS") - if not salt.utils.path.which('pkgutil'): - self.skipTest('Test requires pkgutil binary') + if not salt.utils.path.which("pkgutil"): + self.skipTest("Test requires pkgutil binary") - os_release = self.run_function('grains.get', ['osrelease']) - self.pkg_name = 'com.apple.pkg.BaseSystemResources' - if int(os_release.split('.')[1]) >= 13 and salt.utils.platform.is_darwin(): - self.pkg_name = 'com.apple.pkg.iTunesX' + os_release = self.run_function("grains.get", ["osrelease"]) + self.pkg_name = "com.apple.pkg.BaseSystemResources" + if int(os_release.split(".")[1]) >= 13 and salt.utils.platform.is_darwin(): + self.pkg_name = "com.apple.pkg.iTunesX" def tearDown(self): - ''' + """ Reset to original settings - ''' - self.run_function('pkgutil.forget', [TEST_PKG_NAME]) - self.run_function('file.remove', ['/opt/local']) + """ + self.run_function("pkgutil.forget", [TEST_PKG_NAME]) + self.run_function("file.remove", ["/opt/local"]) def test_list(self): - ''' + """ Test pkgutil.list - ''' - self.assertIsInstance(self.run_function('pkgutil.list'), list) - self.assertIn(self.pkg_name, - self.run_function('pkgutil.list')) + """ + self.assertIsInstance(self.run_function("pkgutil.list"), list) + self.assertIn(self.pkg_name, self.run_function("pkgutil.list")) def test_is_installed(self): - ''' + """ Test pkgutil.is_installed - ''' + """ # Test Package is installed - self.assertTrue( - self.run_function('pkgutil.is_installed', - [self.pkg_name])) + self.assertTrue(self.run_function("pkgutil.is_installed", [self.pkg_name])) # Test Package is not installed - self.assertFalse( - self.run_function('pkgutil.is_installed', ['spongebob'])) + self.assertFalse(self.run_function("pkgutil.is_installed", ["spongebob"])) @destructiveTest def test_install_forget(self): - ''' + """ Test pkgutil.install Test pkgutil.forget - ''' + """ # Test if installed - self.assertFalse( - self.run_function('pkgutil.is_installed', [TEST_PKG_NAME])) + self.assertFalse(self.run_function("pkgutil.is_installed", [TEST_PKG_NAME])) # Download the package - self.run_function('cp.get_url', [TEST_PKG_URL, self.test_pkg]) + self.run_function("cp.get_url", [TEST_PKG_URL, self.test_pkg]) # Test install self.assertTrue( - self.run_function('pkgutil.install', [self.test_pkg, TEST_PKG_NAME])) + self.run_function("pkgutil.install", [self.test_pkg, TEST_PKG_NAME]) + ) self.assertIn( - 'Unsupported scheme', - self.run_function('pkgutil.install', ['ftp://test', 'spongebob'])) + "Unsupported scheme", + self.run_function("pkgutil.install", ["ftp://test", "spongebob"]), + ) # Test forget - self.assertTrue(self.run_function('pkgutil.forget', [TEST_PKG_NAME])) + self.assertTrue(self.run_function("pkgutil.forget", [TEST_PKG_NAME])) diff --git a/tests/integration/modules/test_mac_portspkg.py b/tests/integration/modules/test_mac_portspkg.py index fb0f1f43158..35be0c44c6e 100644 --- a/tests/integration/modules/test_mac_portspkg.py +++ b/tests/integration/modules/test_mac_portspkg.py @@ -1,116 +1,116 @@ # -*- coding: utf-8 -*- -''' +""" integration tests for mac_ports -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.helpers import destructiveTest, skip_if_not_root +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.path import salt.utils.platform +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root + @skip_if_not_root class MacPortsModuleTest(ModuleCase): - ''' + """ Validate the mac_ports module - ''' + """ + AGREE_INSTALLED = False def setUp(self): - ''' + """ Get current settings - ''' + """ if not salt.utils.platform.is_darwin(): - self.skipTest('Test only available on macOS') + self.skipTest("Test only available on macOS") - if not salt.utils.path.which('port'): - self.skipTest('Test requires port binary') + if not salt.utils.path.which("port"): + self.skipTest("Test requires port binary") - self.AGREE_INSTALLED = 'agree' in self.run_function('pkg.list_pkgs') - self.run_function('pkg.refresh_db') + self.AGREE_INSTALLED = "agree" in self.run_function("pkg.list_pkgs") + self.run_function("pkg.refresh_db") def tearDown(self): - ''' + """ Reset to original settings - ''' + """ if not self.AGREE_INSTALLED: - self.run_function('pkg.remove', ['agree']) + self.run_function("pkg.remove", ["agree"]) @destructiveTest def test_list_pkgs(self): - ''' + """ Test pkg.list_pkgs - ''' - self.run_function('pkg.install', ['agree']) - self.assertIsInstance(self.run_function('pkg.list_pkgs'), dict) - self.assertIn('agree', self.run_function('pkg.list_pkgs')) + """ + self.run_function("pkg.install", ["agree"]) + self.assertIsInstance(self.run_function("pkg.list_pkgs"), dict) + self.assertIn("agree", self.run_function("pkg.list_pkgs")) @destructiveTest def test_latest_version(self): - ''' + """ Test pkg.latest_version - ''' - self.run_function('pkg.install', ['agree']) - result = self.run_function('pkg.latest_version', - ['agree'], - refresh=False) + """ + self.run_function("pkg.install", ["agree"]) + result = self.run_function("pkg.latest_version", ["agree"], refresh=False) self.assertIsInstance(result, dict) - self.assertIn('agree', result) + self.assertIn("agree", result) @destructiveTest def test_remove(self): - ''' + """ Test pkg.remove - ''' - self.run_function('pkg.install', ['agree']) - removed = self.run_function('pkg.remove', ['agree']) + """ + self.run_function("pkg.install", ["agree"]) + removed = self.run_function("pkg.remove", ["agree"]) self.assertIsInstance(removed, dict) - self.assertIn('agree', removed) + self.assertIn("agree", removed) @destructiveTest def test_install(self): - ''' + """ Test pkg.install - ''' - self.run_function('pkg.remove', ['agree']) - installed = self.run_function('pkg.install', ['agree']) + """ + self.run_function("pkg.remove", ["agree"]) + installed = self.run_function("pkg.install", ["agree"]) self.assertIsInstance(installed, dict) - self.assertIn('agree', installed) + self.assertIn("agree", installed) def test_list_upgrades(self): - ''' + """ Test pkg.list_upgrades - ''' + """ self.assertIsInstance( - self.run_function('pkg.list_upgrades', refresh=False), dict) + self.run_function("pkg.list_upgrades", refresh=False), dict + ) @destructiveTest def test_upgrade_available(self): - ''' + """ Test pkg.upgrade_available - ''' - self.run_function('pkg.install', ['agree']) - self.assertFalse(self.run_function('pkg.upgrade_available', - ['agree'], - refresh=False)) + """ + self.run_function("pkg.install", ["agree"]) + self.assertFalse( + self.run_function("pkg.upgrade_available", ["agree"], refresh=False) + ) def test_refresh_db(self): - ''' + """ Test pkg.refresh_db - ''' - self.assertTrue(self.run_function('pkg.refresh_db')) + """ + self.assertTrue(self.run_function("pkg.refresh_db")) @destructiveTest def test_upgrade(self): - ''' + """ Test pkg.upgrade - ''' - results = self.run_function('pkg.upgrade', refresh=False) + """ + results = self.run_function("pkg.upgrade", refresh=False) self.assertIsInstance(results, dict) - self.assertTrue(results['result']) + self.assertTrue(results["result"]) diff --git a/tests/integration/modules/test_mac_power.py b/tests/integration/modules/test_mac_power.py index 8c170f4756b..2ba3f91673d 100644 --- a/tests/integration/modules/test_mac_power.py +++ b/tests/integration/modules/test_mac_power.py @@ -1,336 +1,358 @@ # -*- coding: utf-8 -*- -''' +""" integration tests for mac_power -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, skip_if_not_root, flaky +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.path import salt.utils.platform +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, flaky, skip_if_not_root +from tests.support.unit import skipIf + @skip_if_not_root @flaky(attempts=10) -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('systemsetup'), '\'systemsetup\' binary not found in $PATH') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf( + not salt.utils.path.which("systemsetup"), "'systemsetup' binary not found in $PATH" +) class MacPowerModuleTest(ModuleCase): - ''' + """ Validate the mac_power module - ''' + """ + def setUp(self): - ''' + """ Get current settings - ''' + """ # Get current settings - self.COMPUTER_SLEEP = self.run_function('power.get_computer_sleep') - self.DISPLAY_SLEEP = self.run_function('power.get_display_sleep') - self.HARD_DISK_SLEEP = self.run_function('power.get_harddisk_sleep') + self.COMPUTER_SLEEP = self.run_function("power.get_computer_sleep") + self.DISPLAY_SLEEP = self.run_function("power.get_display_sleep") + self.HARD_DISK_SLEEP = self.run_function("power.get_harddisk_sleep") def tearDown(self): - ''' + """ Reset to original settings - ''' - self.run_function('power.set_computer_sleep', [self.COMPUTER_SLEEP]) - self.run_function('power.set_display_sleep', [self.DISPLAY_SLEEP]) - self.run_function('power.set_harddisk_sleep', [self.HARD_DISK_SLEEP]) + """ + self.run_function("power.set_computer_sleep", [self.COMPUTER_SLEEP]) + self.run_function("power.set_display_sleep", [self.DISPLAY_SLEEP]) + self.run_function("power.set_harddisk_sleep", [self.HARD_DISK_SLEEP]) @destructiveTest def test_computer_sleep(self): - ''' + """ Test power.get_computer_sleep Test power.set_computer_sleep - ''' + """ # Normal Functionality - self.assertTrue(self.run_function('power.set_computer_sleep', [90])) + self.assertTrue(self.run_function("power.set_computer_sleep", [90])) self.assertEqual( - self.run_function('power.get_computer_sleep'), 'after 90 minutes') - self.assertTrue(self.run_function('power.set_computer_sleep', ['Off'])) - self.assertEqual(self.run_function('power.get_computer_sleep'), 'Never') + self.run_function("power.get_computer_sleep"), "after 90 minutes" + ) + self.assertTrue(self.run_function("power.set_computer_sleep", ["Off"])) + self.assertEqual(self.run_function("power.get_computer_sleep"), "Never") # Test invalid input self.assertIn( - 'Invalid String Value for Minutes', - self.run_function('power.set_computer_sleep', ['spongebob'])) + "Invalid String Value for Minutes", + self.run_function("power.set_computer_sleep", ["spongebob"]), + ) self.assertIn( - 'Invalid Integer Value for Minutes', - self.run_function('power.set_computer_sleep', [0])) + "Invalid Integer Value for Minutes", + self.run_function("power.set_computer_sleep", [0]), + ) self.assertIn( - 'Invalid Integer Value for Minutes', - self.run_function('power.set_computer_sleep', [181])) + "Invalid Integer Value for Minutes", + self.run_function("power.set_computer_sleep", [181]), + ) self.assertIn( - 'Invalid Boolean Value for Minutes', - self.run_function('power.set_computer_sleep', [True])) + "Invalid Boolean Value for Minutes", + self.run_function("power.set_computer_sleep", [True]), + ) @destructiveTest def test_display_sleep(self): - ''' + """ Test power.get_display_sleep Test power.set_display_sleep - ''' + """ # Normal Functionality - self.assertTrue(self.run_function('power.set_display_sleep', [90])) + self.assertTrue(self.run_function("power.set_display_sleep", [90])) self.assertEqual( - self.run_function('power.get_display_sleep'), 'after 90 minutes') - self.assertTrue(self.run_function('power.set_display_sleep', ['Off'])) - self.assertEqual(self.run_function('power.get_display_sleep'), 'Never') + self.run_function("power.get_display_sleep"), "after 90 minutes" + ) + self.assertTrue(self.run_function("power.set_display_sleep", ["Off"])) + self.assertEqual(self.run_function("power.get_display_sleep"), "Never") # Test invalid input self.assertIn( - 'Invalid String Value for Minutes', - self.run_function('power.set_display_sleep', ['spongebob'])) + "Invalid String Value for Minutes", + self.run_function("power.set_display_sleep", ["spongebob"]), + ) self.assertIn( - 'Invalid Integer Value for Minutes', - self.run_function('power.set_display_sleep', [0])) + "Invalid Integer Value for Minutes", + self.run_function("power.set_display_sleep", [0]), + ) self.assertIn( - 'Invalid Integer Value for Minutes', - self.run_function('power.set_display_sleep', [181])) + "Invalid Integer Value for Minutes", + self.run_function("power.set_display_sleep", [181]), + ) self.assertIn( - 'Invalid Boolean Value for Minutes', - self.run_function('power.set_display_sleep', [True])) + "Invalid Boolean Value for Minutes", + self.run_function("power.set_display_sleep", [True]), + ) @destructiveTest def test_harddisk_sleep(self): - ''' + """ Test power.get_harddisk_sleep Test power.set_harddisk_sleep - ''' + """ # Normal Functionality - self.assertTrue(self.run_function('power.set_harddisk_sleep', [90])) + self.assertTrue(self.run_function("power.set_harddisk_sleep", [90])) self.assertEqual( - self.run_function('power.get_harddisk_sleep'), 'after 90 minutes') - self.assertTrue(self.run_function('power.set_harddisk_sleep', ['Off'])) - self.assertEqual(self.run_function('power.get_harddisk_sleep'), 'Never') + self.run_function("power.get_harddisk_sleep"), "after 90 minutes" + ) + self.assertTrue(self.run_function("power.set_harddisk_sleep", ["Off"])) + self.assertEqual(self.run_function("power.get_harddisk_sleep"), "Never") # Test invalid input self.assertIn( - 'Invalid String Value for Minutes', - self.run_function('power.set_harddisk_sleep', ['spongebob'])) + "Invalid String Value for Minutes", + self.run_function("power.set_harddisk_sleep", ["spongebob"]), + ) self.assertIn( - 'Invalid Integer Value for Minutes', - self.run_function('power.set_harddisk_sleep', [0])) + "Invalid Integer Value for Minutes", + self.run_function("power.set_harddisk_sleep", [0]), + ) self.assertIn( - 'Invalid Integer Value for Minutes', - self.run_function('power.set_harddisk_sleep', [181])) + "Invalid Integer Value for Minutes", + self.run_function("power.set_harddisk_sleep", [181]), + ) self.assertIn( - 'Invalid Boolean Value for Minutes', - self.run_function('power.set_harddisk_sleep', [True])) + "Invalid Boolean Value for Minutes", + self.run_function("power.set_harddisk_sleep", [True]), + ) def test_restart_freeze(self): - ''' + """ Test power.get_restart_freeze Test power.set_restart_freeze - ''' + """ # Normal Functionality - self.assertTrue(self.run_function('power.set_restart_freeze', ['on'])) - self.assertTrue(self.run_function('power.get_restart_freeze')) + self.assertTrue(self.run_function("power.set_restart_freeze", ["on"])) + self.assertTrue(self.run_function("power.get_restart_freeze")) # This will return False because mac fails to actually make the change - self.assertFalse( - self.run_function('power.set_restart_freeze', ['off'])) + self.assertFalse(self.run_function("power.set_restart_freeze", ["off"])) # Even setting to off returns true, it actually is never set # This is an apple bug - self.assertTrue(self.run_function('power.get_restart_freeze')) + self.assertTrue(self.run_function("power.get_restart_freeze")) @skip_if_not_root @flaky(attempts=10) -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('systemsetup'), '\'systemsetup\' binary not found in $PATH') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf( + not salt.utils.path.which("systemsetup"), "'systemsetup' binary not found in $PATH" +) class MacPowerModuleTestSleepOnPowerButton(ModuleCase): - ''' + """ Test power.get_sleep_on_power_button Test power.set_sleep_on_power_button - ''' + """ + SLEEP_ON_BUTTON = None def setUp(self): - ''' + """ Check if function is available Get existing value - ''' + """ # Is the function available - ret = self.run_function('power.get_sleep_on_power_button') + ret = self.run_function("power.get_sleep_on_power_button") if isinstance(ret, bool): - self.SLEEP_ON_BUTTON = self.run_function( - 'power.get_sleep_on_power_button') + self.SLEEP_ON_BUTTON = self.run_function("power.get_sleep_on_power_button") def tearDown(self): - ''' + """ Reset to original value - ''' + """ if self.SLEEP_ON_BUTTON is not None: - self.run_function( - 'power.set_sleep_on_power_button', [self.SLEEP_ON_BUTTON]) + self.run_function("power.set_sleep_on_power_button", [self.SLEEP_ON_BUTTON]) def test_sleep_on_power_button(self): - ''' + """ Test power.get_sleep_on_power_button Test power.set_sleep_on_power_button - ''' + """ # If available on this system, test it if self.SLEEP_ON_BUTTON is None: # Check for not available - ret = self.run_function('power.get_sleep_on_power_button') - self.assertIn('Error', ret) + ret = self.run_function("power.get_sleep_on_power_button") + self.assertIn("Error", ret) else: self.assertTrue( - self.run_function('power.set_sleep_on_power_button', ['on'])) + self.run_function("power.set_sleep_on_power_button", ["on"]) + ) + self.assertTrue(self.run_function("power.get_sleep_on_power_button")) self.assertTrue( - self.run_function('power.get_sleep_on_power_button')) - self.assertTrue( - self.run_function('power.set_sleep_on_power_button', ['off'])) - self.assertFalse( - self.run_function('power.get_sleep_on_power_button')) + self.run_function("power.set_sleep_on_power_button", ["off"]) + ) + self.assertFalse(self.run_function("power.get_sleep_on_power_button")) @skip_if_not_root @flaky(attempts=10) -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('systemsetup'), '\'systemsetup\' binary not found in $PATH') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf( + not salt.utils.path.which("systemsetup"), "'systemsetup' binary not found in $PATH" +) class MacPowerModuleTestRestartPowerFailure(ModuleCase): - ''' + """ Test power.get_restart_power_failure Test power.set_restart_power_failure - ''' + """ + RESTART_POWER = None def setUp(self): - ''' + """ Check if function is available Get existing value - ''' + """ # Is the function available - ret = self.run_function('power.get_restart_power_failure') + ret = self.run_function("power.get_restart_power_failure") if isinstance(ret, bool): self.RESTART_POWER = ret def tearDown(self): - ''' + """ Reset to original value - ''' + """ if self.RESTART_POWER is not None: - self.run_function( - 'power.set_sleep_on_power_button', [self.SLEEP_ON_BUTTON]) + self.run_function("power.set_sleep_on_power_button", [self.SLEEP_ON_BUTTON]) def test_restart_power_failure(self): - ''' + """ Test power.get_restart_power_failure Test power.set_restart_power_failure - ''' + """ # If available on this system, test it if self.RESTART_POWER is None: # Check for not available - ret = self.run_function('power.get_restart_power_failure') - self.assertIn('Error', ret) + ret = self.run_function("power.get_restart_power_failure") + self.assertIn("Error", ret) else: self.assertTrue( - self.run_function('power.set_restart_power_failure', ['on'])) + self.run_function("power.set_restart_power_failure", ["on"]) + ) + self.assertTrue(self.run_function("power.get_restart_power_failure")) self.assertTrue( - self.run_function('power.get_restart_power_failure')) - self.assertTrue( - self.run_function('power.set_restart_power_failure', ['off'])) - self.assertFalse( - self.run_function('power.get_restart_power_failure')) + self.run_function("power.set_restart_power_failure", ["off"]) + ) + self.assertFalse(self.run_function("power.get_restart_power_failure")) @skip_if_not_root @flaky(attempts=10) -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('systemsetup'), '\'systemsetup\' binary not found in $PATH') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf( + not salt.utils.path.which("systemsetup"), "'systemsetup' binary not found in $PATH" +) class MacPowerModuleTestWakeOnNet(ModuleCase): - ''' + """ Test power.get_wake_on_network Test power.set_wake_on_network - ''' + """ + WAKE_ON_NET = None def setUp(self): - ''' + """ Check if function is available Get existing value - ''' + """ # Is the function available - ret = self.run_function('power.get_wake_on_network') + ret = self.run_function("power.get_wake_on_network") if isinstance(ret, bool): self.WAKE_ON_NET = ret def tearDown(self): - ''' + """ Reset to original value - ''' + """ if self.WAKE_ON_NET is not None: - self.run_function('power.set_wake_on_network', [self.WAKE_ON_NET]) + self.run_function("power.set_wake_on_network", [self.WAKE_ON_NET]) def test_wake_on_network(self): - ''' + """ Test power.get_wake_on_network Test power.set_wake_on_network - ''' + """ # If available on this system, test it if self.WAKE_ON_NET is None: # Check for not available - ret = self.run_function('power.get_wake_on_network') - self.assertIn('Error', ret) + ret = self.run_function("power.get_wake_on_network") + self.assertIn("Error", ret) else: - self.assertTrue( - self.run_function('power.set_wake_on_network', ['on'])) - self.assertTrue(self.run_function('power.get_wake_on_network')) - self.assertTrue( - self.run_function('power.set_wake_on_network', ['off'])) - self.assertFalse(self.run_function('power.get_wake_on_network')) + self.assertTrue(self.run_function("power.set_wake_on_network", ["on"])) + self.assertTrue(self.run_function("power.get_wake_on_network")) + self.assertTrue(self.run_function("power.set_wake_on_network", ["off"])) + self.assertFalse(self.run_function("power.get_wake_on_network")) @skip_if_not_root @flaky(attempts=10) -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('systemsetup'), '\'systemsetup\' binary not found in $PATH') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf( + not salt.utils.path.which("systemsetup"), "'systemsetup' binary not found in $PATH" +) class MacPowerModuleTestWakeOnModem(ModuleCase): - ''' + """ Test power.get_wake_on_modem Test power.set_wake_on_modem - ''' + """ + WAKE_ON_MODEM = None def setUp(self): - ''' + """ Check if function is available Get existing value - ''' + """ # Is the function available - ret = self.run_function('power.get_wake_on_modem') + ret = self.run_function("power.get_wake_on_modem") if isinstance(ret, bool): self.WAKE_ON_MODEM = ret def tearDown(self): - ''' + """ Reset to original value - ''' + """ if self.WAKE_ON_MODEM is not None: - self.run_function('power.set_wake_on_modem', [self.WAKE_ON_MODEM]) + self.run_function("power.set_wake_on_modem", [self.WAKE_ON_MODEM]) def test_wake_on_modem(self): - ''' + """ Test power.get_wake_on_modem Test power.set_wake_on_modem - ''' + """ # If available on this system, test it if self.WAKE_ON_MODEM is None: # Check for not available - ret = self.run_function('power.get_wake_on_modem') - self.assertIn('Error', ret) + ret = self.run_function("power.get_wake_on_modem") + self.assertIn("Error", ret) else: - self.assertTrue( - self.run_function('power.set_wake_on_modem', ['on'])) - self.assertTrue(self.run_function('power.get_wake_on_modem')) - self.assertTrue( - self.run_function('power.set_wake_on_modem', ['off'])) - self.assertFalse(self.run_function('power.get_wake_on_modem')) + self.assertTrue(self.run_function("power.set_wake_on_modem", ["on"])) + self.assertTrue(self.run_function("power.get_wake_on_modem")) + self.assertTrue(self.run_function("power.set_wake_on_modem", ["off"])) + self.assertFalse(self.run_function("power.get_wake_on_modem")) diff --git a/tests/integration/modules/test_mac_service.py b/tests/integration/modules/test_mac_service.py index 41cc4b05a02..de3c4997478 100644 --- a/tests/integration/modules/test_mac_service.py +++ b/tests/integration/modules/test_mac_service.py @@ -1,224 +1,218 @@ # -*- coding: utf-8 -*- -''' +""" integration tests for mac_service -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, skip_if_not_root +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.path import salt.utils.platform +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root +from tests.support.unit import skipIf -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('launchctl'), 'Test requires launchctl binary') -@skipIf(not salt.utils.path.which('plutil'), 'Test requires plutil binary') + +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf(not salt.utils.path.which("launchctl"), "Test requires launchctl binary") +@skipIf(not salt.utils.path.which("plutil"), "Test requires plutil binary") @skip_if_not_root class MacServiceModuleTest(ModuleCase): - ''' + """ Validate the mac_service module - ''' - SERVICE_NAME = 'com.apple.apsd' + """ + + SERVICE_NAME = "com.apple.apsd" SERVICE_ENABLED = False def setUp(self): - ''' + """ Get current state of the test service - ''' - self.SERVICE_ENABLED = self.run_function('service.enabled', - [self.SERVICE_NAME]) + """ + self.SERVICE_ENABLED = self.run_function("service.enabled", [self.SERVICE_NAME]) def tearDown(self): - ''' + """ Reset the test service to the original state - ''' + """ if self.SERVICE_ENABLED: - self.run_function('service.start', [self.SERVICE_NAME]) - self.run_function('service.enable', [self.SERVICE_NAME]) + self.run_function("service.start", [self.SERVICE_NAME]) + self.run_function("service.enable", [self.SERVICE_NAME]) else: - self.run_function('service.stop', [self.SERVICE_NAME]) - self.run_function('service.disable', [self.SERVICE_NAME]) + self.run_function("service.stop", [self.SERVICE_NAME]) + self.run_function("service.disable", [self.SERVICE_NAME]) def test_show(self): - ''' + """ Test service.show - ''' + """ # Existing Service - service_info = self.run_function('service.show', [self.SERVICE_NAME]) + service_info = self.run_function("service.show", [self.SERVICE_NAME]) self.assertIsInstance(service_info, dict) - self.assertEqual(service_info['plist']['Label'], self.SERVICE_NAME) + self.assertEqual(service_info["plist"]["Label"], self.SERVICE_NAME) # Missing Service self.assertIn( - 'Service not found', - self.run_function('service.show', ['spongebob'])) + "Service not found", self.run_function("service.show", ["spongebob"]) + ) def test_launchctl(self): - ''' + """ Test service.launchctl - ''' + """ # Expected Functionality self.assertTrue( - self.run_function('service.launchctl', - ['error', 'bootstrap', 64])) + self.run_function("service.launchctl", ["error", "bootstrap", 64]) + ) self.assertEqual( - self.run_function('service.launchctl', - ['error', 'bootstrap', 64], - return_stdout=True), - '64: unknown error code') + self.run_function( + "service.launchctl", ["error", "bootstrap", 64], return_stdout=True + ), + "64: unknown error code", + ) # Raise an error self.assertIn( - 'Failed to error service', - self.run_function('service.launchctl', ['error', 'bootstrap'])) + "Failed to error service", + self.run_function("service.launchctl", ["error", "bootstrap"]), + ) def test_list(self): - ''' + """ Test service.list - ''' + """ # Expected Functionality - self.assertIn('PID', self.run_function('service.list')) + self.assertIn("PID", self.run_function("service.list")) self.assertIn( - '{', - self.run_function('service.list', ['com.apple.coreservicesd'])) + "{", self.run_function("service.list", ["com.apple.coreservicesd"]) + ) # Service not found self.assertIn( - 'Service not found', - self.run_function('service.list', ['spongebob'])) + "Service not found", self.run_function("service.list", ["spongebob"]) + ) @destructiveTest def test_enable(self): - ''' + """ Test service.enable - ''' - self.assertTrue( - self.run_function('service.enable', [self.SERVICE_NAME])) + """ + self.assertTrue(self.run_function("service.enable", [self.SERVICE_NAME])) self.assertIn( - 'Service not found', - self.run_function('service.enable', ['spongebob'])) + "Service not found", self.run_function("service.enable", ["spongebob"]) + ) @destructiveTest def test_disable(self): - ''' + """ Test service.disable - ''' - self.assertTrue( - self.run_function('service.disable', [self.SERVICE_NAME])) + """ + self.assertTrue(self.run_function("service.disable", [self.SERVICE_NAME])) self.assertIn( - 'Service not found', - self.run_function('service.disable', ['spongebob'])) + "Service not found", self.run_function("service.disable", ["spongebob"]) + ) @destructiveTest def test_start(self): - ''' + """ Test service.start Test service.stop Test service.status - ''' - self.assertTrue(self.run_function('service.start', [self.SERVICE_NAME])) + """ + self.assertTrue(self.run_function("service.start", [self.SERVICE_NAME])) self.assertIn( - 'Service not found', - self.run_function('service.start', ['spongebob'])) + "Service not found", self.run_function("service.start", ["spongebob"]) + ) @destructiveTest def test_stop(self): - ''' + """ Test service.stop - ''' - self.assertTrue(self.run_function('service.stop', [self.SERVICE_NAME])) + """ + self.assertTrue(self.run_function("service.stop", [self.SERVICE_NAME])) self.assertIn( - 'Service not found', - self.run_function('service.stop', ['spongebob'])) + "Service not found", self.run_function("service.stop", ["spongebob"]) + ) @destructiveTest def test_status(self): - ''' + """ Test service.status - ''' + """ # A running service - self.assertTrue(self.run_function('service.start', [self.SERVICE_NAME])) + self.assertTrue(self.run_function("service.start", [self.SERVICE_NAME])) self.assertTrue( - self.run_function('service.status', [self.SERVICE_NAME]).isdigit()) + self.run_function("service.status", [self.SERVICE_NAME]).isdigit() + ) # A stopped service - self.assertTrue(self.run_function('service.stop', [self.SERVICE_NAME])) - self.assertEqual( - '', - self.run_function('service.status', [self.SERVICE_NAME])) + self.assertTrue(self.run_function("service.stop", [self.SERVICE_NAME])) + self.assertEqual("", self.run_function("service.status", [self.SERVICE_NAME])) # Service not found - self.assertEqual('', self.run_function('service.status', ['spongebob'])) + self.assertEqual("", self.run_function("service.status", ["spongebob"])) def test_available(self): - ''' + """ Test service.available - ''' - self.assertTrue( - self.run_function('service.available', [self.SERVICE_NAME])) - self.assertFalse(self.run_function('service.available', ['spongebob'])) + """ + self.assertTrue(self.run_function("service.available", [self.SERVICE_NAME])) + self.assertFalse(self.run_function("service.available", ["spongebob"])) def test_missing(self): - ''' + """ Test service.missing - ''' - self.assertFalse(self.run_function('service.missing', [self.SERVICE_NAME])) - self.assertTrue(self.run_function('service.missing', ['spongebob'])) + """ + self.assertFalse(self.run_function("service.missing", [self.SERVICE_NAME])) + self.assertTrue(self.run_function("service.missing", ["spongebob"])) @destructiveTest def test_enabled(self): - ''' + """ Test service.enabled - ''' - self.assertTrue(self.run_function('service.start', [self.SERVICE_NAME])) - self.assertTrue( - self.run_function('service.enabled', [self.SERVICE_NAME])) + """ + self.assertTrue(self.run_function("service.start", [self.SERVICE_NAME])) + self.assertTrue(self.run_function("service.enabled", [self.SERVICE_NAME])) - self.assertTrue(self.run_function('service.stop', [self.SERVICE_NAME])) - self.assertFalse( - self.run_function('service.enabled', [self.SERVICE_NAME])) + self.assertTrue(self.run_function("service.stop", [self.SERVICE_NAME])) + self.assertFalse(self.run_function("service.enabled", [self.SERVICE_NAME])) - self.assertFalse(self.run_function('service.enabled', ['spongebob'])) + self.assertFalse(self.run_function("service.enabled", ["spongebob"])) @destructiveTest def test_disabled(self): - ''' + """ Test service.disabled - ''' - SERVICE_NAME = 'com.apple.nfsd' - self.assertTrue(self.run_function('service.start', [SERVICE_NAME])) - self.assertFalse( - self.run_function('service.disabled', [SERVICE_NAME])) + """ + SERVICE_NAME = "com.apple.nfsd" + self.assertTrue(self.run_function("service.start", [SERVICE_NAME])) + self.assertFalse(self.run_function("service.disabled", [SERVICE_NAME])) - self.assertTrue(self.run_function('service.disable', [SERVICE_NAME])) - self.assertTrue( - self.run_function('service.disabled', [SERVICE_NAME])) - self.assertTrue(self.run_function('service.enable', [SERVICE_NAME])) + self.assertTrue(self.run_function("service.disable", [SERVICE_NAME])) + self.assertTrue(self.run_function("service.disabled", [SERVICE_NAME])) + self.assertTrue(self.run_function("service.enable", [SERVICE_NAME])) - self.assertFalse(self.run_function('service.disabled', ['spongebob'])) + self.assertFalse(self.run_function("service.disabled", ["spongebob"])) def test_get_all(self): - ''' + """ Test service.get_all - ''' - services = self.run_function('service.get_all') + """ + services = self.run_function("service.get_all") self.assertIsInstance(services, list) self.assertIn(self.SERVICE_NAME, services) def test_get_enabled(self): - ''' + """ Test service.get_enabled - ''' - services = self.run_function('service.get_enabled') + """ + services = self.run_function("service.get_enabled") self.assertIsInstance(services, list) - self.assertIn('com.apple.coreservicesd', services) + self.assertIn("com.apple.coreservicesd", services) diff --git a/tests/integration/modules/test_mac_shadow.py b/tests/integration/modules/test_mac_shadow.py index 396c6852560..fa991c5c623 100644 --- a/tests/integration/modules/test_mac_shadow.py +++ b/tests/integration/modules/test_mac_shadow.py @@ -1,32 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" integration tests for mac_shadow -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import datetime import random import string -# Import Salt Testing libs -from tests.support.unit import skipIf -from tests.support.case import ModuleCase -from tests.support.helpers import destructiveTest, skip_if_not_root - # Import Salt libs import salt.utils.path import salt.utils.platform from salt.ext.six.moves import range +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root + +# Import Salt Testing libs +from tests.support.unit import skipIf def __random_string(size=6): - ''' + """ Generates a random username - ''' - return 'RS-' + ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(size) + """ + return "RS-" + "".join( + random.choice(string.ascii_uppercase + string.digits) for x in range(size) ) @@ -35,186 +35,200 @@ NO_USER = __random_string() @skip_if_not_root -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('dscl'), '\'dscl\' binary not found in $PATH') -@skipIf(not salt.utils.path.which('pwpolicy'), '\'pwpolicy\' binary not found in $PATH') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf(not salt.utils.path.which("dscl"), "'dscl' binary not found in $PATH") +@skipIf(not salt.utils.path.which("pwpolicy"), "'pwpolicy' binary not found in $PATH") class MacShadowModuleTest(ModuleCase): - ''' + """ Validate the mac_shadow module - ''' + """ def setUp(self): - ''' + """ Get current settings - ''' - self.run_function('user.add', [TEST_USER]) + """ + self.run_function("user.add", [TEST_USER]) def tearDown(self): - ''' + """ Reset to original settings - ''' - self.run_function('user.delete', [TEST_USER]) + """ + self.run_function("user.delete", [TEST_USER]) def test_info(self): - ''' + """ Test shadow.info - ''' + """ # Correct Functionality - ret = self.run_function('shadow.info', [TEST_USER]) - self.assertEqual(ret['name'], TEST_USER) + ret = self.run_function("shadow.info", [TEST_USER]) + self.assertEqual(ret["name"], TEST_USER) # User does not exist - ret = self.run_function('shadow.info', [NO_USER]) - self.assertEqual(ret['name'], '') + ret = self.run_function("shadow.info", [NO_USER]) + self.assertEqual(ret["name"], "") @destructiveTest def test_get_account_created(self): - ''' + """ Test shadow.get_account_created - ''' + """ # Correct Functionality - text_date = self.run_function('shadow.get_account_created', [TEST_USER]) - self.assertNotEqual(text_date, 'Invalid Timestamp') - obj_date = datetime.datetime.strptime(text_date, '%Y-%m-%d %H:%M:%S') + text_date = self.run_function("shadow.get_account_created", [TEST_USER]) + self.assertNotEqual(text_date, "Invalid Timestamp") + obj_date = datetime.datetime.strptime(text_date, "%Y-%m-%d %H:%M:%S") self.assertIsInstance(obj_date, datetime.date) # User does not exist self.assertEqual( - self.run_function('shadow.get_account_created', [NO_USER]), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.get_account_created", [NO_USER]), + "ERROR: User not found: {0}".format(NO_USER), + ) @destructiveTest def test_get_last_change(self): - ''' + """ Test shadow.get_last_change - ''' + """ # Correct Functionality - text_date = self.run_function('shadow.get_last_change', [TEST_USER]) - self.assertNotEqual(text_date, 'Invalid Timestamp') - obj_date = datetime.datetime.strptime(text_date, '%Y-%m-%d %H:%M:%S') + text_date = self.run_function("shadow.get_last_change", [TEST_USER]) + self.assertNotEqual(text_date, "Invalid Timestamp") + obj_date = datetime.datetime.strptime(text_date, "%Y-%m-%d %H:%M:%S") self.assertIsInstance(obj_date, datetime.date) # User does not exist self.assertEqual( - self.run_function('shadow.get_last_change', [NO_USER]), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.get_last_change", [NO_USER]), + "ERROR: User not found: {0}".format(NO_USER), + ) @destructiveTest def test_get_login_failed_last(self): - ''' + """ Test shadow.get_login_failed_last - ''' + """ # Correct Functionality - text_date = self.run_function('shadow.get_login_failed_last', [TEST_USER]) - self.assertNotEqual(text_date, 'Invalid Timestamp') - obj_date = datetime.datetime.strptime(text_date, '%Y-%m-%d %H:%M:%S') + text_date = self.run_function("shadow.get_login_failed_last", [TEST_USER]) + self.assertNotEqual(text_date, "Invalid Timestamp") + obj_date = datetime.datetime.strptime(text_date, "%Y-%m-%d %H:%M:%S") self.assertIsInstance(obj_date, datetime.date) # User does not exist self.assertEqual( - self.run_function('shadow.get_login_failed_last', [NO_USER]), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.get_login_failed_last", [NO_USER]), + "ERROR: User not found: {0}".format(NO_USER), + ) @destructiveTest def test_get_login_failed_count(self): - ''' + """ Test shadow.get_login_failed_count - ''' + """ # Correct Functionality self.assertEqual( - self.run_function('shadow.get_login_failed_count', [TEST_USER]), - '0') + self.run_function("shadow.get_login_failed_count", [TEST_USER]), "0" + ) # User does not exist self.assertEqual( - self.run_function('shadow.get_login_failed_count', [NO_USER]), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.get_login_failed_count", [NO_USER]), + "ERROR: User not found: {0}".format(NO_USER), + ) @destructiveTest def test_get_set_maxdays(self): - ''' + """ Test shadow.get_maxdays Test shadow.set_maxdays - ''' + """ # Correct Functionality - self.assertTrue( - self.run_function('shadow.set_maxdays', [TEST_USER, 20])) - self.assertEqual( - self.run_function('shadow.get_maxdays', [TEST_USER]), 20) + self.assertTrue(self.run_function("shadow.set_maxdays", [TEST_USER, 20])) + self.assertEqual(self.run_function("shadow.get_maxdays", [TEST_USER]), 20) # User does not exist self.assertEqual( - self.run_function('shadow.set_maxdays', [NO_USER, 7]), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.set_maxdays", [NO_USER, 7]), + "ERROR: User not found: {0}".format(NO_USER), + ) self.assertEqual( - self.run_function('shadow.get_maxdays', [NO_USER]), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.get_maxdays", [NO_USER]), + "ERROR: User not found: {0}".format(NO_USER), + ) @destructiveTest def test_get_set_change(self): - ''' + """ Test shadow.get_change Test shadow.set_change - ''' + """ # Correct Functionality self.assertTrue( - self.run_function('shadow.set_change', [TEST_USER, '02/11/2011'])) - self.assertEqual(self.run_function('shadow.get_change', [TEST_USER]), - '02/11/2011') + self.run_function("shadow.set_change", [TEST_USER, "02/11/2011"]) + ) + self.assertEqual( + self.run_function("shadow.get_change", [TEST_USER]), "02/11/2011" + ) # User does not exist self.assertEqual( - self.run_function('shadow.set_change', [NO_USER, '02/11/2012']), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.set_change", [NO_USER, "02/11/2012"]), + "ERROR: User not found: {0}".format(NO_USER), + ) self.assertEqual( - self.run_function('shadow.get_change', [NO_USER]), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.get_change", [NO_USER]), + "ERROR: User not found: {0}".format(NO_USER), + ) @destructiveTest def test_get_set_expire(self): - ''' + """ Test shadow.get_expire Test shadow.set_expire - ''' + """ # Correct Functionality self.assertTrue( - self.run_function('shadow.set_expire', [TEST_USER, '02/11/2011'])) + self.run_function("shadow.set_expire", [TEST_USER, "02/11/2011"]) + ) self.assertEqual( - self.run_function('shadow.get_expire', [TEST_USER]), '02/11/2011') + self.run_function("shadow.get_expire", [TEST_USER]), "02/11/2011" + ) # User does not exist self.assertEqual( - self.run_function('shadow.set_expire', [NO_USER, '02/11/2012']), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.set_expire", [NO_USER, "02/11/2012"]), + "ERROR: User not found: {0}".format(NO_USER), + ) self.assertEqual( - self.run_function('shadow.get_expire', [NO_USER]), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.get_expire", [NO_USER]), + "ERROR: User not found: {0}".format(NO_USER), + ) @destructiveTest def test_del_password(self): - ''' + """ Test shadow.del_password - ''' + """ # Correct Functionality - self.assertTrue(self.run_function('shadow.del_password', [TEST_USER])) - self.assertEqual( - self.run_function('shadow.info', [TEST_USER])['passwd'], '*') + self.assertTrue(self.run_function("shadow.del_password", [TEST_USER])) + self.assertEqual(self.run_function("shadow.info", [TEST_USER])["passwd"], "*") # User does not exist self.assertEqual( - self.run_function('shadow.del_password', [NO_USER]), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.del_password", [NO_USER]), + "ERROR: User not found: {0}".format(NO_USER), + ) @destructiveTest def test_set_password(self): - ''' + """ Test shadow.set_password - ''' + """ # Correct Functionality self.assertTrue( - self.run_function('shadow.set_password', [TEST_USER, 'Pa$$W0rd'])) + self.run_function("shadow.set_password", [TEST_USER, "Pa$$W0rd"]) + ) # User does not exist self.assertEqual( - self.run_function('shadow.set_password', [NO_USER, 'P@SSw0rd']), - 'ERROR: User not found: {0}'.format(NO_USER)) + self.run_function("shadow.set_password", [NO_USER, "P@SSw0rd"]), + "ERROR: User not found: {0}".format(NO_USER), + ) diff --git a/tests/integration/modules/test_mac_softwareupdate.py b/tests/integration/modules/test_mac_softwareupdate.py index e249409dcff..974f5a27960 100644 --- a/tests/integration/modules/test_mac_softwareupdate.py +++ b/tests/integration/modules/test_mac_softwareupdate.py @@ -1,180 +1,171 @@ # -*- coding: utf-8 -*- -''' +""" integration tests for mac_softwareupdate -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.unit import skipIf -from tests.support.case import ModuleCase -from tests.support.helpers import destructiveTest, skip_if_not_root +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.path import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root + +# Import Salt Testing libs +from tests.support.unit import skipIf @skip_if_not_root -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('softwareupdate'), '\'softwareupdate\' binary not found in $PATH') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf( + not salt.utils.path.which("softwareupdate"), + "'softwareupdate' binary not found in $PATH", +) class MacSoftwareUpdateModuleTest(ModuleCase): - ''' + """ Validate the mac_softwareupdate module - ''' + """ + IGNORED_LIST = [] SCHEDULE = False - CATALOG = '' + CATALOG = "" def setUp(self): - ''' + """ Get current settings - ''' - self.IGNORED_LIST = self.run_function('softwareupdate.list_ignored') - self.SCHEDULE = self.run_function('softwareupdate.schedule') - self.CATALOG = self.run_function('softwareupdate.get_catalog') + """ + self.IGNORED_LIST = self.run_function("softwareupdate.list_ignored") + self.SCHEDULE = self.run_function("softwareupdate.schedule") + self.CATALOG = self.run_function("softwareupdate.get_catalog") super(MacSoftwareUpdateModuleTest, self).setUp() def tearDown(self): - ''' + """ Reset to original settings - ''' + """ if self.IGNORED_LIST: for item in self.IGNORED_LIST: - self.run_function('softwareupdate.ignore', [item]) + self.run_function("softwareupdate.ignore", [item]) else: - self.run_function('softwareupdate.reset_ignored') + self.run_function("softwareupdate.reset_ignored") - self.run_function('softwareupdate.schedule', [self.SCHEDULE]) + self.run_function("softwareupdate.schedule", [self.SCHEDULE]) - if self.CATALOG == 'Default': - self.run_function('softwareupdate.reset_catalog') + if self.CATALOG == "Default": + self.run_function("softwareupdate.reset_catalog") else: - self.run_function('softwareupdate.set_catalog', [self.CATALOG]) + self.run_function("softwareupdate.set_catalog", [self.CATALOG]) super(MacSoftwareUpdateModuleTest, self).tearDown() def test_list_available(self): - ''' + """ Test softwareupdate.list_available - ''' + """ # Can't predict what will be returned, so can only test that the return # is the correct type, dict - self.assertIsInstance( - self.run_function('softwareupdate.list_available'), dict) + self.assertIsInstance(self.run_function("softwareupdate.list_available"), dict) @destructiveTest def test_ignore(self): - ''' + """ Test softwareupdate.ignore Test softwareupdate.list_ignored Test softwareupdate.reset_ignored - ''' + """ # Test reset_ignored - self.assertTrue(self.run_function('softwareupdate.reset_ignored')) - self.assertEqual(self.run_function('softwareupdate.list_ignored'), []) + self.assertTrue(self.run_function("softwareupdate.reset_ignored")) + self.assertEqual(self.run_function("softwareupdate.list_ignored"), []) # Test ignore - self.assertTrue( - self.run_function('softwareupdate.ignore', ['spongebob'])) - self.assertTrue( - self.run_function('softwareupdate.ignore', ['squidward'])) + self.assertTrue(self.run_function("softwareupdate.ignore", ["spongebob"])) + self.assertTrue(self.run_function("softwareupdate.ignore", ["squidward"])) # Test list_ignored and verify ignore - self.assertIn( - 'spongebob', - self.run_function('softwareupdate.list_ignored')) - self.assertIn( - 'squidward', - self.run_function('softwareupdate.list_ignored')) + self.assertIn("spongebob", self.run_function("softwareupdate.list_ignored")) + self.assertIn("squidward", self.run_function("softwareupdate.list_ignored")) @destructiveTest def test_schedule(self): - ''' + """ Test softwareupdate.schedule_enable Test softwareupdate.schedule_enabled - ''' + """ # Test enable - self.assertTrue( - self.run_function('softwareupdate.schedule_enable', [True])) - self.assertTrue(self.run_function('softwareupdate.schedule_enabled')) + self.assertTrue(self.run_function("softwareupdate.schedule_enable", [True])) + self.assertTrue(self.run_function("softwareupdate.schedule_enabled")) # Test disable in case it was already enabled - self.assertTrue( - self.run_function('softwareupdate.schedule_enable', [False])) - self.assertFalse(self.run_function('softwareupdate.schedule_enabled')) + self.assertTrue(self.run_function("softwareupdate.schedule_enable", [False])) + self.assertFalse(self.run_function("softwareupdate.schedule_enabled")) @destructiveTest def test_update(self): - ''' + """ Test softwareupdate.update_all Test softwareupdate.update Test softwareupdate.update_available Need to know the names of updates that are available to properly test the update functions... - ''' + """ # There's no way to know what the dictionary will contain, so all we can # check is that the return is a dictionary - self.assertIsInstance( - self.run_function('softwareupdate.update_all'), dict) + self.assertIsInstance(self.run_function("softwareupdate.update_all"), dict) # Test update_available self.assertFalse( - self.run_function('softwareupdate.update_available', ['spongebob'])) + self.run_function("softwareupdate.update_available", ["spongebob"]) + ) # Test update not available self.assertIn( - 'Update not available', - self.run_function('softwareupdate.update', ['spongebob'])) + "Update not available", + self.run_function("softwareupdate.update", ["spongebob"]), + ) def test_list_downloads(self): - ''' + """ Test softwareupdate.list_downloads - ''' - self.assertIsInstance( - self.run_function('softwareupdate.list_downloads'), list) + """ + self.assertIsInstance(self.run_function("softwareupdate.list_downloads"), list) @destructiveTest def test_download(self): - ''' + """ Test softwareupdate.download Need to know the names of updates that are available to properly test the download function - ''' + """ # Test update not available self.assertIn( - 'Update not available', - self.run_function('softwareupdate.download', ['spongebob'])) + "Update not available", + self.run_function("softwareupdate.download", ["spongebob"]), + ) @destructiveTest def test_download_all(self): - ''' + """ Test softwareupdate.download_all - ''' - self.assertIsInstance( - self.run_function('softwareupdate.download_all'), list) + """ + self.assertIsInstance(self.run_function("softwareupdate.download_all"), list) @destructiveTest def test_get_set_reset_catalog(self): - ''' + """ Test softwareupdate.download_all - ''' + """ # Reset the catalog - self.assertTrue(self.run_function('softwareupdate.reset_catalog')) - self.assertEqual(self.run_function('softwareupdate.get_catalog'), - 'Default') + self.assertTrue(self.run_function("softwareupdate.reset_catalog")) + self.assertEqual(self.run_function("softwareupdate.get_catalog"), "Default") # Test setting and getting the catalog - self.assertTrue( - self.run_function('softwareupdate.set_catalog', ['spongebob'])) - self.assertEqual( - self.run_function('softwareupdate.get_catalog'), 'spongebob') + self.assertTrue(self.run_function("softwareupdate.set_catalog", ["spongebob"])) + self.assertEqual(self.run_function("softwareupdate.get_catalog"), "spongebob") # Test reset the catalog - self.assertTrue(self.run_function('softwareupdate.reset_catalog')) - self.assertEqual(self.run_function('softwareupdate.get_catalog'), - 'Default') + self.assertTrue(self.run_function("softwareupdate.reset_catalog")) + self.assertEqual(self.run_function("softwareupdate.get_catalog"), "Default") diff --git a/tests/integration/modules/test_mac_sysctl.py b/tests/integration/modules/test_mac_sysctl.py index 86b69efd047..cabb1f476ab 100644 --- a/tests/integration/modules/test_mac_sysctl.py +++ b/tests/integration/modules/test_mac_sysctl.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import os import random @@ -17,32 +18,28 @@ from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest, skip_if_not_root # Module Variables -ASSIGN_CMD = 'net.inet.icmp.icmplim' -CONFIG = '/etc/sysctl.conf' +ASSIGN_CMD = "net.inet.icmp.icmplim" +CONFIG = "/etc/sysctl.conf" @destructiveTest @skip_if_not_root class DarwinSysctlModuleTest(ModuleCase): - ''' + """ Integration tests for the darwin_sysctl module - ''' + """ def setUp(self): - ''' + """ Sets up the test requirements - ''' + """ super(DarwinSysctlModuleTest, self).setUp() - os_grain = self.run_function('grains.item', ['kernel']) - if os_grain['kernel'] not in 'Darwin': - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) + os_grain = self.run_function("grains.item", ["kernel"]) + if os_grain["kernel"] not in "Darwin": + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) # Data needed for cleanup self.has_conf = False - self.val = self.run_function('sysctl.get', [ASSIGN_CMD]) + self.val = self.run_function("sysctl.get", [ASSIGN_CMD]) # If sysctl file is present, make a copy # Remove original file so we can replace it with test files @@ -51,39 +48,39 @@ class DarwinSysctlModuleTest(ModuleCase): try: self.conf = self.__copy_sysctl() except CommandExecutionError: - msg = 'Could not copy file: {0}' + msg = "Could not copy file: {0}" raise CommandExecutionError(msg.format(CONFIG)) os.remove(CONFIG) def test_assign(self): - ''' + """ Tests assigning a single sysctl parameter - ''' + """ try: rand = random.randint(0, 500) while rand == self.val: rand = random.randint(0, 500) - self.run_function('sysctl.assign', [ASSIGN_CMD, rand]) - info = int(self.run_function('sysctl.get', [ASSIGN_CMD])) + self.run_function("sysctl.assign", [ASSIGN_CMD, rand]) + info = int(self.run_function("sysctl.get", [ASSIGN_CMD])) try: self.assertEqual(rand, info) except AssertionError: - self.run_function('sysctl.assign', [ASSIGN_CMD, self.val]) + self.run_function("sysctl.assign", [ASSIGN_CMD, self.val]) raise except CommandExecutionError: - self.run_function('sysctl.assign', [ASSIGN_CMD, self.val]) + self.run_function("sysctl.assign", [ASSIGN_CMD, self.val]) raise def test_persist_new_file(self): - ''' + """ Tests assigning a sysctl value to a system without a sysctl.conf file - ''' + """ # Always start with a clean/known sysctl.conf state if os.path.isfile(CONFIG): os.remove(CONFIG) try: - self.run_function('sysctl.persist', [ASSIGN_CMD, 10]) - line = '{0}={1}'.format(ASSIGN_CMD, 10) + self.run_function("sysctl.persist", [ASSIGN_CMD, 10]) + line = "{0}={1}".format(ASSIGN_CMD, 10) found = self.__check_string(CONFIG, line) self.assertTrue(found) except CommandExecutionError: @@ -91,24 +88,24 @@ class DarwinSysctlModuleTest(ModuleCase): raise def test_persist_already_set(self): - ''' + """ Tests assigning a sysctl value that is already set in sysctl.conf file - ''' + """ # Always start with a clean/known sysctl.conf state if os.path.isfile(CONFIG): os.remove(CONFIG) try: - self.run_function('sysctl.persist', [ASSIGN_CMD, 50]) - ret = self.run_function('sysctl.persist', [ASSIGN_CMD, 50]) - self.assertEqual(ret, 'Already set') + self.run_function("sysctl.persist", [ASSIGN_CMD, 50]) + ret = self.run_function("sysctl.persist", [ASSIGN_CMD, 50]) + self.assertEqual(ret, "Already set") except CommandExecutionError: os.remove(CONFIG) raise def test_persist_apply_change(self): - ''' + """ Tests assigning a sysctl value and applying the change to system - ''' + """ # Always start with a clean/known sysctl.conf state if os.path.isfile(CONFIG): os.remove(CONFIG) @@ -116,40 +113,38 @@ class DarwinSysctlModuleTest(ModuleCase): rand = random.randint(0, 500) while rand == self.val: rand = random.randint(0, 500) - self.run_function('sysctl.persist', - [ASSIGN_CMD, rand], - apply_change=True) - info = int(self.run_function('sysctl.get', [ASSIGN_CMD])) + self.run_function("sysctl.persist", [ASSIGN_CMD, rand], apply_change=True) + info = int(self.run_function("sysctl.get", [ASSIGN_CMD])) self.assertEqual(info, rand) except CommandExecutionError: os.remove(CONFIG) raise def __copy_sysctl(self): - ''' + """ Copies an existing sysconf file and returns temp file path. Copied file will be restored in tearDown - ''' + """ # Create new temporary file path and open needed files temp_path = salt.utils.files.mkstemp() - with salt.utils.files.fopen(CONFIG, 'r') as org_conf: - with salt.utils.files.fopen(temp_path, 'w') as temp_sysconf: + with salt.utils.files.fopen(CONFIG, "r") as org_conf: + with salt.utils.files.fopen(temp_path, "w") as temp_sysconf: # write sysctl lines to temp file for line in org_conf: temp_sysconf.write(line) return temp_path def __restore_sysctl(self): - ''' + """ Restores the original sysctl.conf file from temporary copy - ''' + """ # If sysctl testing file exists, delete it if os.path.isfile(CONFIG): os.remove(CONFIG) # write temp lines to sysctl file to restore - with salt.utils.files.fopen(self.conf, 'r') as temp_sysctl: - with salt.utils.files.fopen(CONFIG, 'w') as sysctl: + with salt.utils.files.fopen(self.conf, "r") as temp_sysctl: + with salt.utils.files.fopen(CONFIG, "w") as sysctl: for line in temp_sysctl: sysctl.write(line) @@ -157,22 +152,22 @@ class DarwinSysctlModuleTest(ModuleCase): os.remove(self.conf) def __check_string(self, conf_file, to_find): - ''' + """ Returns True if given line is present in file - ''' - with salt.utils.files.fopen(conf_file, 'r') as f_in: + """ + with salt.utils.files.fopen(conf_file, "r") as f_in: for line in f_in: if to_find in salt.utils.stringutils.to_unicode(line): return True return False def tearDown(self): - ''' + """ Clean up after tests - ''' - ret = self.run_function('sysctl.get', [ASSIGN_CMD]) + """ + ret = self.run_function("sysctl.get", [ASSIGN_CMD]) if ret != self.val: - self.run_function('sysctl.assign', [ASSIGN_CMD, self.val]) + self.run_function("sysctl.assign", [ASSIGN_CMD, self.val]) if self.has_conf is True: # restore original sysctl file diff --git a/tests/integration/modules/test_mac_system.py b/tests/integration/modules/test_mac_system.py index 914b94efd4a..ae2e66fdebe 100644 --- a/tests/integration/modules/test_mac_system.py +++ b/tests/integration/modules/test_mac_system.py @@ -1,35 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" integration tests for mac_system -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import logging import random import string -import logging - -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, skip_if_not_root, flaky # Import salt libs import salt.utils.path import salt.utils.platform from salt.ext.six.moves import range +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, flaky, skip_if_not_root +from tests.support.unit import skipIf log = logging.getLogger(__name__) def __random_string(size=6): - ''' + """ Generates a random username - ''' - return 'RS-' + ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(size) + """ + return "RS-" + "".join( + random.choice(string.ascii_uppercase + string.digits) for x in range(size) ) @@ -39,229 +38,239 @@ SET_SUBNET_NAME = __random_string() @skip_if_not_root @flaky(attempts=10) -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('systemsetup'), '\'systemsetup\' binary not found in $PATH') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf( + not salt.utils.path.which("systemsetup"), "'systemsetup' binary not found in $PATH" +) class MacSystemModuleTest(ModuleCase): - ''' + """ Validate the mac_system module - ''' + """ + ATRUN_ENABLED = False REMOTE_LOGIN_ENABLED = False REMOTE_EVENTS_ENABLED = False - SUBNET_NAME = '' + SUBNET_NAME = "" KEYBOARD_DISABLED = False def setUp(self): - ''' + """ Get current settings - ''' - self.ATRUN_ENABLED = self.run_function('service.enabled', ['com.apple.atrun']) - self.REMOTE_LOGIN_ENABLED = self.run_function('system.get_remote_login') - self.REMOTE_EVENTS_ENABLED = self.run_function('system.get_remote_events') - self.SUBNET_NAME = self.run_function('system.get_subnet_name') - self.KEYBOARD_DISABLED = self.run_function('system.get_disable_keyboard_on_lock') + """ + self.ATRUN_ENABLED = self.run_function("service.enabled", ["com.apple.atrun"]) + self.REMOTE_LOGIN_ENABLED = self.run_function("system.get_remote_login") + self.REMOTE_EVENTS_ENABLED = self.run_function("system.get_remote_events") + self.SUBNET_NAME = self.run_function("system.get_subnet_name") + self.KEYBOARD_DISABLED = self.run_function( + "system.get_disable_keyboard_on_lock" + ) def tearDown(self): - ''' + """ Reset to original settings - ''' + """ if not self.ATRUN_ENABLED: - atrun = '/System/Library/LaunchDaemons/com.apple.atrun.plist' - self.run_function('service.stop', [atrun]) + atrun = "/System/Library/LaunchDaemons/com.apple.atrun.plist" + self.run_function("service.stop", [atrun]) - self.run_function('system.set_remote_login', [self.REMOTE_LOGIN_ENABLED]) - self.run_function('system.set_remote_events', [self.REMOTE_EVENTS_ENABLED]) - self.run_function('system.set_subnet_name', [self.SUBNET_NAME]) - self.run_function('system.set_disable_keyboard_on_lock', - [self.KEYBOARD_DISABLED]) + self.run_function("system.set_remote_login", [self.REMOTE_LOGIN_ENABLED]) + self.run_function("system.set_remote_events", [self.REMOTE_EVENTS_ENABLED]) + self.run_function("system.set_subnet_name", [self.SUBNET_NAME]) + self.run_function( + "system.set_disable_keyboard_on_lock", [self.KEYBOARD_DISABLED] + ) @destructiveTest def test_get_set_remote_login(self): - ''' + """ Test system.get_remote_login Test system.set_remote_login - ''' + """ # Normal Functionality - self.assertTrue(self.run_function('system.set_remote_login', [True])) - self.assertTrue(self.run_function('system.get_remote_login')) - self.assertTrue(self.run_function('system.set_remote_login', [False])) - self.assertFalse(self.run_function('system.get_remote_login')) + self.assertTrue(self.run_function("system.set_remote_login", [True])) + self.assertTrue(self.run_function("system.get_remote_login")) + self.assertTrue(self.run_function("system.set_remote_login", [False])) + self.assertFalse(self.run_function("system.get_remote_login")) # Test valid input - self.assertTrue(self.run_function('system.set_remote_login', [True])) - self.assertTrue(self.run_function('system.set_remote_login', [False])) - self.assertTrue(self.run_function('system.set_remote_login', ['yes'])) - self.assertTrue(self.run_function('system.set_remote_login', ['no'])) - self.assertTrue(self.run_function('system.set_remote_login', ['On'])) - self.assertTrue(self.run_function('system.set_remote_login', ['Off'])) - self.assertTrue(self.run_function('system.set_remote_login', [1])) - self.assertTrue(self.run_function('system.set_remote_login', [0])) + self.assertTrue(self.run_function("system.set_remote_login", [True])) + self.assertTrue(self.run_function("system.set_remote_login", [False])) + self.assertTrue(self.run_function("system.set_remote_login", ["yes"])) + self.assertTrue(self.run_function("system.set_remote_login", ["no"])) + self.assertTrue(self.run_function("system.set_remote_login", ["On"])) + self.assertTrue(self.run_function("system.set_remote_login", ["Off"])) + self.assertTrue(self.run_function("system.set_remote_login", [1])) + self.assertTrue(self.run_function("system.set_remote_login", [0])) # Test invalid input self.assertIn( - 'Invalid String Value for Enabled', - self.run_function('system.set_remote_login', ['spongebob'])) + "Invalid String Value for Enabled", + self.run_function("system.set_remote_login", ["spongebob"]), + ) @destructiveTest def test_get_set_remote_events(self): - ''' + """ Test system.get_remote_events Test system.set_remote_events - ''' + """ # Normal Functionality - self.assertTrue(self.run_function('system.set_remote_events', [True])) - self.assertTrue(self.run_function('system.get_remote_events')) - self.assertTrue(self.run_function('system.set_remote_events', [False])) - self.assertFalse(self.run_function('system.get_remote_events')) + self.assertTrue(self.run_function("system.set_remote_events", [True])) + self.assertTrue(self.run_function("system.get_remote_events")) + self.assertTrue(self.run_function("system.set_remote_events", [False])) + self.assertFalse(self.run_function("system.get_remote_events")) # Test valid input - self.assertTrue(self.run_function('system.set_remote_events', [True])) - self.assertTrue(self.run_function('system.set_remote_events', [False])) - self.assertTrue(self.run_function('system.set_remote_events', ['yes'])) - self.assertTrue(self.run_function('system.set_remote_events', ['no'])) - self.assertTrue(self.run_function('system.set_remote_events', ['On'])) - self.assertTrue(self.run_function('system.set_remote_events', ['Off'])) - self.assertTrue(self.run_function('system.set_remote_events', [1])) - self.assertTrue(self.run_function('system.set_remote_events', [0])) + self.assertTrue(self.run_function("system.set_remote_events", [True])) + self.assertTrue(self.run_function("system.set_remote_events", [False])) + self.assertTrue(self.run_function("system.set_remote_events", ["yes"])) + self.assertTrue(self.run_function("system.set_remote_events", ["no"])) + self.assertTrue(self.run_function("system.set_remote_events", ["On"])) + self.assertTrue(self.run_function("system.set_remote_events", ["Off"])) + self.assertTrue(self.run_function("system.set_remote_events", [1])) + self.assertTrue(self.run_function("system.set_remote_events", [0])) # Test invalid input self.assertIn( - 'Invalid String Value for Enabled', - self.run_function('system.set_remote_events', ['spongebob'])) + "Invalid String Value for Enabled", + self.run_function("system.set_remote_events", ["spongebob"]), + ) @destructiveTest def test_get_set_subnet_name(self): - ''' + """ Test system.get_subnet_name Test system.set_subnet_name - ''' - self.assertTrue( - self.run_function('system.set_subnet_name', [SET_SUBNET_NAME])) - self.assertEqual( - self.run_function('system.get_subnet_name'), - SET_SUBNET_NAME) + """ + self.assertTrue(self.run_function("system.set_subnet_name", [SET_SUBNET_NAME])) + self.assertEqual(self.run_function("system.get_subnet_name"), SET_SUBNET_NAME) def test_get_list_startup_disk(self): - ''' + """ Test system.get_startup_disk Test system.list_startup_disks Don't know how to test system.set_startup_disk as there's usually only one startup disk available on a system - ''' + """ # Test list and get - ret = self.run_function('system.list_startup_disks') + ret = self.run_function("system.list_startup_disks") self.assertIsInstance(ret, list) - self.assertIn(self.run_function('system.get_startup_disk'), ret) + self.assertIn(self.run_function("system.get_startup_disk"), ret) # Test passing set a bad disk self.assertIn( - 'Invalid value passed for path.', - self.run_function('system.set_startup_disk', ['spongebob'])) + "Invalid value passed for path.", + self.run_function("system.set_startup_disk", ["spongebob"]), + ) - @skipIf(True, 'Skip this test until mac fixes it.') + @skipIf(True, "Skip this test until mac fixes it.") def test_get_set_restart_delay(self): - ''' + """ Test system.get_restart_delay Test system.set_restart_delay system.set_restart_delay does not work due to an apple bug, see docs may need to disable this test as we can't control the delay value - ''' + """ # Normal Functionality - self.assertTrue(self.run_function('system.set_restart_delay', [90])) - self.assertEqual( - self.run_function('system.get_restart_delay'), - '90 seconds') + self.assertTrue(self.run_function("system.set_restart_delay", [90])) + self.assertEqual(self.run_function("system.get_restart_delay"), "90 seconds") # Pass set bad value for seconds self.assertIn( - 'Invalid value passed for seconds.', - self.run_function('system.set_restart_delay', [70])) + "Invalid value passed for seconds.", + self.run_function("system.set_restart_delay", [70]), + ) def test_get_set_disable_keyboard_on_lock(self): - ''' + """ Test system.get_disable_keyboard_on_lock Test system.set_disable_keyboard_on_lock - ''' + """ # Normal Functionality self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', [True])) - self.assertTrue( - self.run_function('system.get_disable_keyboard_on_lock')) + self.run_function("system.set_disable_keyboard_on_lock", [True]) + ) + self.assertTrue(self.run_function("system.get_disable_keyboard_on_lock")) self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', [False])) - self.assertFalse( - self.run_function('system.get_disable_keyboard_on_lock')) + self.run_function("system.set_disable_keyboard_on_lock", [False]) + ) + self.assertFalse(self.run_function("system.get_disable_keyboard_on_lock")) # Test valid input self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', [True])) + self.run_function("system.set_disable_keyboard_on_lock", [True]) + ) self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', [False])) + self.run_function("system.set_disable_keyboard_on_lock", [False]) + ) self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', ['yes'])) + self.run_function("system.set_disable_keyboard_on_lock", ["yes"]) + ) self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', ['no'])) + self.run_function("system.set_disable_keyboard_on_lock", ["no"]) + ) self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', ['On'])) + self.run_function("system.set_disable_keyboard_on_lock", ["On"]) + ) self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', ['Off'])) - self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', [1])) - self.assertTrue( - self.run_function('system.set_disable_keyboard_on_lock', [0])) + self.run_function("system.set_disable_keyboard_on_lock", ["Off"]) + ) + self.assertTrue(self.run_function("system.set_disable_keyboard_on_lock", [1])) + self.assertTrue(self.run_function("system.set_disable_keyboard_on_lock", [0])) # Test invalid input self.assertIn( - 'Invalid String Value for Enabled', - self.run_function('system.set_disable_keyboard_on_lock', - ['spongebob'])) + "Invalid String Value for Enabled", + self.run_function("system.set_disable_keyboard_on_lock", ["spongebob"]), + ) - @skipIf(True, 'Skip this test until mac fixes it.') + @skipIf(True, "Skip this test until mac fixes it.") def test_get_set_boot_arch(self): - ''' + """ Test system.get_boot_arch Test system.set_boot_arch system.set_boot_arch does not work due to an apple bug, see docs may need to disable this test as we can't set the boot architecture - ''' + """ # Normal Functionality - self.assertTrue(self.run_function('system.set_boot_arch', ['i386'])) - self.assertEqual(self.run_function('system.get_boot_arch'), 'i386') - self.assertTrue(self.run_function('system.set_boot_arch', ['default'])) - self.assertEqual(self.run_function('system.get_boot_arch'), 'default') + self.assertTrue(self.run_function("system.set_boot_arch", ["i386"])) + self.assertEqual(self.run_function("system.get_boot_arch"), "i386") + self.assertTrue(self.run_function("system.set_boot_arch", ["default"])) + self.assertEqual(self.run_function("system.get_boot_arch"), "default") # Test invalid input self.assertIn( - 'Invalid value passed for arch', - self.run_function('system.set_boot_arch', ['spongebob'])) + "Invalid value passed for arch", + self.run_function("system.set_boot_arch", ["spongebob"]), + ) @skip_if_not_root -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") class MacSystemComputerNameTest(ModuleCase): - def setUp(self): - self.COMPUTER_NAME = self.run_function('system.get_computer_name') + self.COMPUTER_NAME = self.run_function("system.get_computer_name") self.wait_for_all_jobs() def tearDown(self): - self.run_function('system.set_computer_name', [self.COMPUTER_NAME]) + self.run_function("system.set_computer_name", [self.COMPUTER_NAME]) self.wait_for_all_jobs() # A similar test used to be skipped on py3 due to 'hanging', if we see # something similar again we may want to skip this gain until we # investigate - #@skipIf(salt.utils.platform.is_darwin() and six.PY3, 'This test hangs on OS X on Py3. Skipping until #53566 is merged.') + # @skipIf(salt.utils.platform.is_darwin() and six.PY3, 'This test hangs on OS X on Py3. Skipping until #53566 is merged.') @destructiveTest def test_get_set_computer_name(self): - ''' + """ Test system.get_computer_name Test system.set_computer_name - ''' + """ log.debug("Set name is %s", SET_COMPUTER_NAME) self.assertTrue( - self.run_function('system.set_computer_name', [SET_COMPUTER_NAME])) + self.run_function("system.set_computer_name", [SET_COMPUTER_NAME]) + ) self.assertEqual( - self.run_function('system.get_computer_name'), - SET_COMPUTER_NAME) + self.run_function("system.get_computer_name"), SET_COMPUTER_NAME + ) diff --git a/tests/integration/modules/test_mac_timezone.py b/tests/integration/modules/test_mac_timezone.py index 7375c4d7107..fbe37b87c27 100644 --- a/tests/integration/modules/test_mac_timezone.py +++ b/tests/integration/modules/test_mac_timezone.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for mac_timezone If using parallels, make sure Time sync is turned off. Otherwise, parallels will @@ -8,16 +8,12 @@ Time sync do the following: - Go to actions -> configure - Select options at the top and 'More Options' on the left - Set time to 'Do not sync' -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import datetime +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, skip_if_not_root, flaky +import datetime # Import Salt libs import salt.utils.path @@ -26,193 +22,213 @@ import salt.utils.platform # Import 3rd Party libs from salt.ext import six +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, flaky, skip_if_not_root +from tests.support.unit import skipIf + @skip_if_not_root @flaky -@skipIf(not salt.utils.platform.is_darwin(), 'Test only available on macOS') -@skipIf(not salt.utils.path.which('systemsetup'), '\'systemsetup\' binary not found in $PATH') +@skipIf(not salt.utils.platform.is_darwin(), "Test only available on macOS") +@skipIf( + not salt.utils.path.which("systemsetup"), "'systemsetup' binary not found in $PATH" +) class MacTimezoneModuleTest(ModuleCase): - ''' + """ Validate the mac_timezone module - ''' + """ + USE_NETWORK_TIME = False - TIME_SERVER = 'time.apple.com' - TIME_ZONE = '' - CURRENT_DATE = '' - CURRENT_TIME = '' + TIME_SERVER = "time.apple.com" + TIME_ZONE = "" + CURRENT_DATE = "" + CURRENT_TIME = "" def setUp(self): - ''' + """ Get current settings - ''' - self.USE_NETWORK_TIME = self.run_function('timezone.get_using_network_time') - self.TIME_SERVER = self.run_function('timezone.get_time_server') - self.TIME_ZONE = self.run_function('timezone.get_zone') - self.CURRENT_DATE = self.run_function('timezone.get_date') - self.CURRENT_TIME = self.run_function('timezone.get_time') + """ + self.USE_NETWORK_TIME = self.run_function("timezone.get_using_network_time") + self.TIME_SERVER = self.run_function("timezone.get_time_server") + self.TIME_ZONE = self.run_function("timezone.get_zone") + self.CURRENT_DATE = self.run_function("timezone.get_date") + self.CURRENT_TIME = self.run_function("timezone.get_time") - self.run_function('timezone.set_using_network_time', [False]) - self.run_function('timezone.set_zone', ['America/Denver']) + self.run_function("timezone.set_using_network_time", [False]) + self.run_function("timezone.set_zone", ["America/Denver"]) def tearDown(self): - ''' + """ Reset to original settings - ''' - self.run_function('timezone.set_time_server', [self.TIME_SERVER]) - self.run_function('timezone.set_using_network_time', - [self.USE_NETWORK_TIME]) - self.run_function('timezone.set_zone', [self.TIME_ZONE]) + """ + self.run_function("timezone.set_time_server", [self.TIME_SERVER]) + self.run_function("timezone.set_using_network_time", [self.USE_NETWORK_TIME]) + self.run_function("timezone.set_zone", [self.TIME_ZONE]) if not self.USE_NETWORK_TIME: - self.run_function('timezone.set_date', [self.CURRENT_DATE]) - self.run_function('timezone.set_time', [self.CURRENT_TIME]) + self.run_function("timezone.set_date", [self.CURRENT_DATE]) + self.run_function("timezone.set_time", [self.CURRENT_TIME]) - @skipIf(True, 'Skip until we can figure out why modifying the system clock causes ZMQ errors') + @skipIf( + True, + "Skip until we can figure out why modifying the system clock causes ZMQ errors", + ) @destructiveTest def test_get_set_date(self): - ''' + """ Test timezone.get_date Test timezone.set_date - ''' + """ # Correct Functionality - self.assertTrue(self.run_function('timezone.set_date', ['2/20/2011'])) - self.assertEqual(self.run_function('timezone.get_date'), '2/20/2011') + self.assertTrue(self.run_function("timezone.set_date", ["2/20/2011"])) + self.assertEqual(self.run_function("timezone.get_date"), "2/20/2011") # Test bad date format self.assertEqual( - self.run_function('timezone.set_date', ['13/12/2014']), - 'ERROR executing \'timezone.set_date\': ' - 'Invalid Date/Time Format: 13/12/2014' + self.run_function("timezone.set_date", ["13/12/2014"]), + "ERROR executing 'timezone.set_date': " + "Invalid Date/Time Format: 13/12/2014", ) def test_get_time(self): - ''' + """ Test timezone.get_time - ''' - text_time = self.run_function('timezone.get_time') - self.assertNotEqual(text_time, 'Invalid Timestamp') - obj_date = datetime.datetime.strptime(text_time, '%H:%M:%S') + """ + text_time = self.run_function("timezone.get_time") + self.assertNotEqual(text_time, "Invalid Timestamp") + obj_date = datetime.datetime.strptime(text_time, "%H:%M:%S") self.assertIsInstance(obj_date, datetime.date) - @skipIf(True, 'Skip until we can figure out why modifying the system clock causes ZMQ errors') + @skipIf( + True, + "Skip until we can figure out why modifying the system clock causes ZMQ errors", + ) @destructiveTest def test_set_time(self): - ''' + """ Test timezone.set_time - ''' + """ # Correct Functionality - self.assertTrue(self.run_function('timezone.set_time', ['3:14'])) + self.assertTrue(self.run_function("timezone.set_time", ["3:14"])) # Test bad time format self.assertEqual( - self.run_function('timezone.set_time', ['3:71']), - 'ERROR executing \'timezone.set_time\': ' - 'Invalid Date/Time Format: 3:71') + self.run_function("timezone.set_time", ["3:71"]), + "ERROR executing 'timezone.set_time': " "Invalid Date/Time Format: 3:71", + ) - @skipIf(True, 'Skip until we can figure out why modifying the system clock causes ZMQ errors') + @skipIf( + True, + "Skip until we can figure out why modifying the system clock causes ZMQ errors", + ) @destructiveTest def test_get_set_zone(self): - ''' + """ Test timezone.get_zone Test timezone.set_zone - ''' + """ # Correct Functionality - self.assertTrue( - self.run_function('timezone.set_zone', ['Pacific/Wake'])) - self.assertEqual( - self.run_function('timezone.get_zone'), 'Pacific/Wake') + self.assertTrue(self.run_function("timezone.set_zone", ["Pacific/Wake"])) + self.assertEqual(self.run_function("timezone.get_zone"), "Pacific/Wake") # Test bad time zone self.assertEqual( - self.run_function('timezone.set_zone', ['spongebob']), - 'ERROR executing \'timezone.set_zone\': ' - 'Invalid Timezone: spongebob') + self.run_function("timezone.set_zone", ["spongebob"]), + "ERROR executing 'timezone.set_zone': " "Invalid Timezone: spongebob", + ) - @skipIf(True, 'Skip until we can figure out why modifying the system clock causes ZMQ errors') + @skipIf( + True, + "Skip until we can figure out why modifying the system clock causes ZMQ errors", + ) @destructiveTest def test_get_offset(self): - ''' + """ Test timezone.get_offset - ''' - self.assertTrue( - self.run_function('timezone.set_zone', ['Pacific/Wake'])) - self.assertIsInstance(self.run_function('timezone.get_offset'), - six.string_types) - self.assertEqual(self.run_function('timezone.get_offset'), '+1200') + """ + self.assertTrue(self.run_function("timezone.set_zone", ["Pacific/Wake"])) + self.assertIsInstance( + self.run_function("timezone.get_offset"), six.string_types + ) + self.assertEqual(self.run_function("timezone.get_offset"), "+1200") - self.assertTrue( - self.run_function('timezone.set_zone', ['America/Los_Angeles'])) - self.assertIsInstance(self.run_function('timezone.get_offset'), - six.string_types) - self.assertEqual(self.run_function('timezone.get_offset'), '-0700') + self.assertTrue(self.run_function("timezone.set_zone", ["America/Los_Angeles"])) + self.assertIsInstance( + self.run_function("timezone.get_offset"), six.string_types + ) + self.assertEqual(self.run_function("timezone.get_offset"), "-0700") - @skipIf(True, 'Skip until we can figure out why modifying the system clock causes ZMQ errors') + @skipIf( + True, + "Skip until we can figure out why modifying the system clock causes ZMQ errors", + ) @destructiveTest def test_get_set_zonecode(self): - ''' + """ Test timezone.get_zonecode Test timezone.set_zonecode - ''' - self.assertTrue( - self.run_function('timezone.set_zone', ['America/Los_Angeles'])) - self.assertIsInstance(self.run_function('timezone.get_zonecode'), - six.string_types) - self.assertEqual(self.run_function('timezone.get_zonecode'), 'PDT') + """ + self.assertTrue(self.run_function("timezone.set_zone", ["America/Los_Angeles"])) + self.assertIsInstance( + self.run_function("timezone.get_zonecode"), six.string_types + ) + self.assertEqual(self.run_function("timezone.get_zonecode"), "PDT") - self.assertTrue( - self.run_function('timezone.set_zone', ['Pacific/Wake'])) - self.assertIsInstance(self.run_function('timezone.get_zonecode'), - six.string_types) - self.assertEqual(self.run_function('timezone.get_zonecode'), 'WAKT') + self.assertTrue(self.run_function("timezone.set_zone", ["Pacific/Wake"])) + self.assertIsInstance( + self.run_function("timezone.get_zonecode"), six.string_types + ) + self.assertEqual(self.run_function("timezone.get_zonecode"), "WAKT") def test_list_zones(self): - ''' + """ Test timezone.list_zones - ''' - zones = self.run_function('timezone.list_zones') - self.assertIsInstance(self.run_function('timezone.list_zones'), list) - self.assertIn( - 'America/Denver', - self.run_function('timezone.list_zones')) - self.assertIn( - 'America/Los_Angeles', - self.run_function('timezone.list_zones')) + """ + zones = self.run_function("timezone.list_zones") + self.assertIsInstance(self.run_function("timezone.list_zones"), list) + self.assertIn("America/Denver", self.run_function("timezone.list_zones")) + self.assertIn("America/Los_Angeles", self.run_function("timezone.list_zones")) - @skipIf(True, 'Skip until we can figure out why modifying the system clock causes ZMQ errors') + @skipIf( + True, + "Skip until we can figure out why modifying the system clock causes ZMQ errors", + ) @destructiveTest def test_zone_compare(self): - ''' + """ Test timezone.zone_compare - ''' - self.assertTrue( - self.run_function('timezone.set_zone', ['America/Denver'])) - self.assertTrue( - self.run_function('timezone.zone_compare', ['America/Denver'])) - self.assertFalse( - self.run_function('timezone.zone_compare', ['Pacific/Wake'])) + """ + self.assertTrue(self.run_function("timezone.set_zone", ["America/Denver"])) + self.assertTrue(self.run_function("timezone.zone_compare", ["America/Denver"])) + self.assertFalse(self.run_function("timezone.zone_compare", ["Pacific/Wake"])) - @skipIf(True, 'Skip until we can figure out why modifying the system clock causes ZMQ errors') + @skipIf( + True, + "Skip until we can figure out why modifying the system clock causes ZMQ errors", + ) @destructiveTest def test_get_set_using_network_time(self): - ''' + """ Test timezone.get_using_network_time Test timezone.set_using_network_time - ''' - self.assertTrue( - self.run_function('timezone.set_using_network_time', [True])) - self.assertTrue(self.run_function('timezone.get_using_network_time')) + """ + self.assertTrue(self.run_function("timezone.set_using_network_time", [True])) + self.assertTrue(self.run_function("timezone.get_using_network_time")) - self.assertTrue( - self.run_function('timezone.set_using_network_time', [False])) - self.assertFalse(self.run_function('timezone.get_using_network_time')) + self.assertTrue(self.run_function("timezone.set_using_network_time", [False])) + self.assertFalse(self.run_function("timezone.get_using_network_time")) - @skipIf(True, 'Skip until we can figure out why modifying the system clock causes ZMQ errors') + @skipIf( + True, + "Skip until we can figure out why modifying the system clock causes ZMQ errors", + ) @destructiveTest def test_get_set_time_server(self): - ''' + """ Test timezone.get_time_server Test timezone.set_time_server - ''' + """ self.assertTrue( - self.run_function('timezone.set_time_server', ['spongebob.com'])) - self.assertEqual( - self.run_function('timezone.get_time_server'), 'spongebob.com') + self.run_function("timezone.set_time_server", ["spongebob.com"]) + ) + self.assertEqual(self.run_function("timezone.get_time_server"), "spongebob.com") diff --git a/tests/integration/modules/test_mac_user.py b/tests/integration/modules/test_mac_user.py index 22f63437956..e41c25e2053 100644 --- a/tests/integration/modules/test_mac_user.py +++ b/tests/integration/modules/test_mac_user.py @@ -1,35 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import os import random import string -import os + +import salt.ext.six as six +import salt.ext.six as six + +# Import Salt Libs +import salt.utils.files +from salt.exceptions import CommandExecutionError + +# Import 3rd-party libs +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin # Import Salt Testing Libs from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest, skip_if_not_root -# Import Salt Libs -import salt.utils.files -from salt.exceptions import CommandExecutionError -import salt.ext.six as six - -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -import salt.ext.six as six - def __random_string(size=6): - ''' + """ Generates a random username - ''' - return 'RS-' + ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(size) + """ + return "RS-" + "".join( + random.choice(string.ascii_uppercase + string.digits) for x in range(size) ) @@ -43,212 +44,213 @@ CHANGE_USER = __random_string() @destructiveTest @skip_if_not_root class MacUserModuleTest(ModuleCase): - ''' + """ Integration tests for the mac_user module - ''' + """ def setUp(self): - ''' + """ Sets up test requirements - ''' + """ super(MacUserModuleTest, self).setUp() - os_grain = self.run_function('grains.item', ['kernel']) - if os_grain['kernel'] not in 'Darwin': - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) + os_grain = self.run_function("grains.item", ["kernel"]) + if os_grain["kernel"] not in "Darwin": + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) def test_mac_user_add(self): - ''' + """ Tests the add function - ''' + """ try: - self.run_function('user.add', [ADD_USER]) - user_info = self.run_function('user.info', [ADD_USER]) - self.assertEqual(ADD_USER, user_info['name']) + self.run_function("user.add", [ADD_USER]) + user_info = self.run_function("user.info", [ADD_USER]) + self.assertEqual(ADD_USER, user_info["name"]) except CommandExecutionError: - self.run_function('user.delete', [ADD_USER]) + self.run_function("user.delete", [ADD_USER]) raise def test_mac_user_delete(self): - ''' + """ Tests the delete function - ''' + """ # Create a user to delete - If unsuccessful, skip the test - if self.run_function('user.add', [DEL_USER]) is not True: - self.run_function('user.delete', [DEL_USER]) - self.skipTest('Failed to create a user to delete') + if self.run_function("user.add", [DEL_USER]) is not True: + self.run_function("user.delete", [DEL_USER]) + self.skipTest("Failed to create a user to delete") # Now try to delete the added user - ret = self.run_function('user.delete', [DEL_USER]) + ret = self.run_function("user.delete", [DEL_USER]) self.assertTrue(ret) def test_mac_user_primary_group(self): - ''' + """ Tests the primary_group function - ''' + """ # Create a user to test primary group function - if self.run_function('user.add', [PRIMARY_GROUP_USER]) is not True: - self.run_function('user.delete', [PRIMARY_GROUP_USER]) - self.skipTest('Failed to create a user') + if self.run_function("user.add", [PRIMARY_GROUP_USER]) is not True: + self.run_function("user.delete", [PRIMARY_GROUP_USER]) + self.skipTest("Failed to create a user") try: # Test mac_user.primary_group - primary_group = self.run_function('user.primary_group', [PRIMARY_GROUP_USER]) - uid_info = self.run_function('user.info', [PRIMARY_GROUP_USER]) - self.assertIn(primary_group, uid_info['groups']) + primary_group = self.run_function( + "user.primary_group", [PRIMARY_GROUP_USER] + ) + uid_info = self.run_function("user.info", [PRIMARY_GROUP_USER]) + self.assertIn(primary_group, uid_info["groups"]) except AssertionError: - self.run_function('user.delete', [PRIMARY_GROUP_USER]) + self.run_function("user.delete", [PRIMARY_GROUP_USER]) raise def test_mac_user_changes(self): - ''' + """ Tests mac_user functions that change user properties - ''' + """ # Create a user to manipulate - if unsuccessful, skip the test - if self.run_function('user.add', [CHANGE_USER]) is not True: - self.run_function('user.delete', [CHANGE_USER]) - self.skipTest('Failed to create a user') + if self.run_function("user.add", [CHANGE_USER]) is not True: + self.run_function("user.delete", [CHANGE_USER]) + self.skipTest("Failed to create a user") try: # Test mac_user.chuid - self.run_function('user.chuid', [CHANGE_USER, 4376]) - uid_info = self.run_function('user.info', [CHANGE_USER]) - self.assertEqual(uid_info['uid'], 4376) + self.run_function("user.chuid", [CHANGE_USER, 4376]) + uid_info = self.run_function("user.info", [CHANGE_USER]) + self.assertEqual(uid_info["uid"], 4376) # Test mac_user.chgid - self.run_function('user.chgid', [CHANGE_USER, 4376]) - gid_info = self.run_function('user.info', [CHANGE_USER]) - self.assertEqual(gid_info['gid'], 4376) + self.run_function("user.chgid", [CHANGE_USER, 4376]) + gid_info = self.run_function("user.info", [CHANGE_USER]) + self.assertEqual(gid_info["gid"], 4376) # Test mac.user.chshell - self.run_function('user.chshell', [CHANGE_USER, '/bin/zsh']) - shell_info = self.run_function('user.info', [CHANGE_USER]) - self.assertEqual(shell_info['shell'], '/bin/zsh') + self.run_function("user.chshell", [CHANGE_USER, "/bin/zsh"]) + shell_info = self.run_function("user.info", [CHANGE_USER]) + self.assertEqual(shell_info["shell"], "/bin/zsh") # Test mac_user.chhome - self.run_function('user.chhome', [CHANGE_USER, '/Users/foo']) - home_info = self.run_function('user.info', [CHANGE_USER]) - self.assertEqual(home_info['home'], '/Users/foo') + self.run_function("user.chhome", [CHANGE_USER, "/Users/foo"]) + home_info = self.run_function("user.info", [CHANGE_USER]) + self.assertEqual(home_info["home"], "/Users/foo") # Test mac_user.chfullname - self.run_function('user.chfullname', [CHANGE_USER, 'Foo Bar']) - fullname_info = self.run_function('user.info', [CHANGE_USER]) - self.assertEqual(fullname_info['fullname'], 'Foo Bar') + self.run_function("user.chfullname", [CHANGE_USER, "Foo Bar"]) + fullname_info = self.run_function("user.info", [CHANGE_USER]) + self.assertEqual(fullname_info["fullname"], "Foo Bar") # Test mac_user.chgroups - self.run_function('user.chgroups', [CHANGE_USER, 'wheel']) - groups_info = self.run_function('user.info', [CHANGE_USER]) - self.assertEqual(groups_info['groups'], ['wheel']) + self.run_function("user.chgroups", [CHANGE_USER, "wheel"]) + groups_info = self.run_function("user.info", [CHANGE_USER]) + self.assertEqual(groups_info["groups"], ["wheel"]) except AssertionError: - self.run_function('user.delete', [CHANGE_USER]) + self.run_function("user.delete", [CHANGE_USER]) raise def test_mac_user_enable_auto_login(self): - ''' + """ Tests mac_user functions that enable auto login - ''' + """ # Make sure auto login is disabled before we start - if self.run_function('user.get_auto_login'): - self.skipTest('Auto login already enabled') + if self.run_function("user.get_auto_login"): + self.skipTest("Auto login already enabled") try: # Does enable return True self.assertTrue( - self.run_function('user.enable_auto_login', - ['Spongebob', 'Squarepants'])) + self.run_function( + "user.enable_auto_login", ["Spongebob", "Squarepants"] + ) + ) # Did it set the user entry in the plist file - self.assertEqual( - self.run_function('user.get_auto_login'), - 'Spongebob') + self.assertEqual(self.run_function("user.get_auto_login"), "Spongebob") # Did it generate the `/etc/kcpassword` file - self.assertTrue(os.path.exists('/etc/kcpassword')) + self.assertTrue(os.path.exists("/etc/kcpassword")) # Are the contents of the file correct if six.PY2: - test_data = b'.\xf8\'B\xa0\xd9\xad\x8b\xcd\xcdl' + test_data = b".\xf8'B\xa0\xd9\xad\x8b\xcd\xcdl" else: - test_data = b".\xc3\xb8'B\xc2\xa0\xc3\x99\xc2\xad\xc2\x8b\xc3\x8d\xc3\x8dl" - with salt.utils.files.fopen('/etc/kcpassword', 'r' if six.PY2 else 'rb') as f: + test_data = ( + b".\xc3\xb8'B\xc2\xa0\xc3\x99\xc2\xad\xc2\x8b\xc3\x8d\xc3\x8dl" + ) + with salt.utils.files.fopen( + "/etc/kcpassword", "r" if six.PY2 else "rb" + ) as f: file_data = f.read() self.assertEqual(test_data, file_data) # Does disable return True - self.assertTrue(self.run_function('user.disable_auto_login')) + self.assertTrue(self.run_function("user.disable_auto_login")) # Does it remove the user entry in the plist file - self.assertFalse(self.run_function('user.get_auto_login')) + self.assertFalse(self.run_function("user.get_auto_login")) # Is the `/etc/kcpassword` file removed - self.assertFalse(os.path.exists('/etc/kcpassword')) + self.assertFalse(os.path.exists("/etc/kcpassword")) finally: # Make sure auto_login is disabled - self.assertTrue(self.run_function('user.disable_auto_login')) + self.assertTrue(self.run_function("user.disable_auto_login")) # Make sure autologin is disabled - if self.run_function('user.get_auto_login'): - raise Exception('Failed to disable auto login') + if self.run_function("user.get_auto_login"): + raise Exception("Failed to disable auto login") def test_mac_user_disable_auto_login(self): - ''' + """ Tests mac_user functions that disable auto login - ''' + """ # Make sure auto login is enabled before we start # Is there an existing setting - if self.run_function('user.get_auto_login'): - self.skipTest('Auto login already enabled') + if self.run_function("user.get_auto_login"): + self.skipTest("Auto login already enabled") try: # Enable auto login for the test - self.run_function('user.enable_auto_login', - ['Spongebob', 'Squarepants']) + self.run_function("user.enable_auto_login", ["Spongebob", "Squarepants"]) # Make sure auto login got set up - if not self.run_function('user.get_auto_login') == 'Spongebob': - raise Exception('Failed to enable auto login') + if not self.run_function("user.get_auto_login") == "Spongebob": + raise Exception("Failed to enable auto login") # Does disable return True - self.assertTrue(self.run_function('user.disable_auto_login')) + self.assertTrue(self.run_function("user.disable_auto_login")) # Does it remove the user entry in the plist file - self.assertFalse(self.run_function('user.get_auto_login')) + self.assertFalse(self.run_function("user.get_auto_login")) # Is the `/etc/kcpassword` file removed - self.assertFalse(os.path.exists('/etc/kcpassword')) + self.assertFalse(os.path.exists("/etc/kcpassword")) finally: # Make sure auto login is disabled - self.assertTrue(self.run_function('user.disable_auto_login')) + self.assertTrue(self.run_function("user.disable_auto_login")) # Make sure auto login is disabled - if self.run_function('user.get_auto_login'): - raise Exception('Failed to disable auto login') + if self.run_function("user.get_auto_login"): + raise Exception("Failed to disable auto login") def tearDown(self): - ''' + """ Clean up after tests - ''' + """ # Delete ADD_USER - add_info = self.run_function('user.info', [ADD_USER]) + add_info = self.run_function("user.info", [ADD_USER]) if add_info: - self.run_function('user.delete', [ADD_USER]) + self.run_function("user.delete", [ADD_USER]) # Delete DEL_USER if something failed - del_info = self.run_function('user.info', [DEL_USER]) + del_info = self.run_function("user.info", [DEL_USER]) if del_info: - self.run_function('user.delete', [DEL_USER]) + self.run_function("user.delete", [DEL_USER]) # Delete CHANGE_USER - change_info = self.run_function('user.info', [CHANGE_USER]) + change_info = self.run_function("user.info", [CHANGE_USER]) if change_info: - self.run_function('user.delete', [CHANGE_USER]) + self.run_function("user.delete", [CHANGE_USER]) diff --git a/tests/integration/modules/test_mac_xattr.py b/tests/integration/modules/test_mac_xattr.py index 22d2771f1cd..d0eecc5bd83 100644 --- a/tests/integration/modules/test_mac_xattr.py +++ b/tests/integration/modules/test_mac_xattr.py @@ -1,179 +1,199 @@ # -*- coding: utf-8 -*- -''' +""" integration tests for mac_xattr -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase +import os # Import Salt libs import salt.utils.path import salt.utils.platform +from tests.support.case import ModuleCase + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS class MacXattrModuleTest(ModuleCase): - ''' + """ Validate the mac_xattr module - ''' + """ @classmethod def setUpClass(cls): - cls.test_file = os.path.join(RUNTIME_VARS.TMP, 'xattr_test_file.txt') - cls.no_file = os.path.join(RUNTIME_VARS.TMP, 'xattr_no_file.txt') + cls.test_file = os.path.join(RUNTIME_VARS.TMP, "xattr_test_file.txt") + cls.no_file = os.path.join(RUNTIME_VARS.TMP, "xattr_no_file.txt") def setUp(self): - ''' + """ Create test file for testing extended attributes - ''' + """ if not salt.utils.platform.is_darwin(): - self.skipTest('Test only available on macOS') + self.skipTest("Test only available on macOS") - if not salt.utils.path.which('xattr'): - self.skipTest('Test requires xattr binary') + if not salt.utils.path.which("xattr"): + self.skipTest("Test requires xattr binary") - self.run_function('file.touch', [self.test_file]) + self.run_function("file.touch", [self.test_file]) def tearDown(self): - ''' + """ Clean up test file - ''' + """ if os.path.exists(self.test_file): os.remove(self.test_file) def test_list_no_xattr(self): - ''' + """ Make sure there are no attributes - ''' + """ # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [self.test_file])) + self.assertTrue(self.run_function("xattr.clear", [self.test_file])) # Test no attributes - self.assertEqual(self.run_function('xattr.list', [self.test_file]), {}) + self.assertEqual(self.run_function("xattr.list", [self.test_file]), {}) # Test file not found - self.assertEqual(self.run_function('xattr.list', [self.no_file]), - 'ERROR: File not found: {0}'.format(self.no_file)) + self.assertEqual( + self.run_function("xattr.list", [self.no_file]), + "ERROR: File not found: {0}".format(self.no_file), + ) def test_write(self): - ''' + """ Write an attribute - ''' + """ # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [self.test_file])) + self.assertTrue(self.run_function("xattr.clear", [self.test_file])) # Write some attributes self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'spongebob', 'squarepants'])) + self.run_function( + "xattr.write", [self.test_file, "spongebob", "squarepants"] + ) + ) self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'squidward', 'plankton'])) + self.run_function("xattr.write", [self.test_file, "squidward", "plankton"]) + ) self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'crabby', 'patty'])) + self.run_function("xattr.write", [self.test_file, "crabby", "patty"]) + ) # Test that they were actually added self.assertEqual( - self.run_function('xattr.list', [self.test_file]), - {'spongebob': 'squarepants', - 'squidward': 'plankton', - 'crabby': 'patty'}) + self.run_function("xattr.list", [self.test_file]), + {"spongebob": "squarepants", "squidward": "plankton", "crabby": "patty"}, + ) # Test file not found self.assertEqual( - self.run_function('xattr.write', [self.no_file, 'patrick', 'jellyfish']), - 'ERROR: File not found: {0}'.format(self.no_file)) + self.run_function("xattr.write", [self.no_file, "patrick", "jellyfish"]), + "ERROR: File not found: {0}".format(self.no_file), + ) def test_read(self): - ''' + """ Test xattr.read - ''' + """ # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [self.test_file])) + self.assertTrue(self.run_function("xattr.clear", [self.test_file])) # Write an attribute self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'spongebob', 'squarepants'])) + self.run_function( + "xattr.write", [self.test_file, "spongebob", "squarepants"] + ) + ) # Read the attribute self.assertEqual( - self.run_function('xattr.read', [self.test_file, 'spongebob']), - 'squarepants') + self.run_function("xattr.read", [self.test_file, "spongebob"]), + "squarepants", + ) # Test file not found self.assertEqual( - self.run_function('xattr.read', [self.no_file, 'spongebob']), - 'ERROR: File not found: {0}'.format(self.no_file)) + self.run_function("xattr.read", [self.no_file, "spongebob"]), + "ERROR: File not found: {0}".format(self.no_file), + ) # Test attribute not found self.assertEqual( - self.run_function('xattr.read', [self.test_file, 'patrick']), - 'ERROR: Attribute not found: patrick') + self.run_function("xattr.read", [self.test_file, "patrick"]), + "ERROR: Attribute not found: patrick", + ) def test_delete(self): - ''' + """ Test xattr.delete - ''' + """ # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [self.test_file])) + self.assertTrue(self.run_function("xattr.clear", [self.test_file])) # Write some attributes self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'spongebob', 'squarepants'])) + self.run_function( + "xattr.write", [self.test_file, "spongebob", "squarepants"] + ) + ) self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'squidward', 'plankton'])) + self.run_function("xattr.write", [self.test_file, "squidward", "plankton"]) + ) self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'crabby', 'patty'])) + self.run_function("xattr.write", [self.test_file, "crabby", "patty"]) + ) # Delete an attribute self.assertTrue( - self.run_function('xattr.delete', [self.test_file, 'squidward'])) + self.run_function("xattr.delete", [self.test_file, "squidward"]) + ) # Make sure it was actually deleted self.assertEqual( - self.run_function('xattr.list', [self.test_file]), - {'spongebob': 'squarepants', 'crabby': 'patty'}) + self.run_function("xattr.list", [self.test_file]), + {"spongebob": "squarepants", "crabby": "patty"}, + ) # Test file not found self.assertEqual( - self.run_function('xattr.delete', [self.no_file, 'spongebob']), - 'ERROR: File not found: {0}'.format(self.no_file)) + self.run_function("xattr.delete", [self.no_file, "spongebob"]), + "ERROR: File not found: {0}".format(self.no_file), + ) # Test attribute not found self.assertEqual( - self.run_function('xattr.delete', [self.test_file, 'patrick']), - 'ERROR: Attribute not found: patrick') + self.run_function("xattr.delete", [self.test_file, "patrick"]), + "ERROR: Attribute not found: patrick", + ) def test_clear(self): - ''' + """ Test xattr.clear - ''' + """ # Clear existing attributes - self.assertTrue(self.run_function('xattr.clear', [self.test_file])) + self.assertTrue(self.run_function("xattr.clear", [self.test_file])) # Write some attributes self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'spongebob', 'squarepants'])) + self.run_function( + "xattr.write", [self.test_file, "spongebob", "squarepants"] + ) + ) self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'squidward', 'plankton'])) + self.run_function("xattr.write", [self.test_file, "squidward", "plankton"]) + ) self.assertTrue( - self.run_function('xattr.write', - [self.test_file, 'crabby', 'patty'])) + self.run_function("xattr.write", [self.test_file, "crabby", "patty"]) + ) # Test Clear - self.assertTrue(self.run_function('xattr.clear', [self.test_file])) + self.assertTrue(self.run_function("xattr.clear", [self.test_file])) # Test file not found - self.assertEqual(self.run_function('xattr.clear', [self.no_file]), - 'ERROR: File not found: {0}'.format(self.no_file)) + self.assertEqual( + self.run_function("xattr.clear", [self.no_file]), + "ERROR: File not found: {0}".format(self.no_file), + ) diff --git a/tests/integration/modules/test_macdefaults.py b/tests/integration/modules/test_macdefaults.py index a320f9c7f20..0ace89d8961 100644 --- a/tests/integration/modules/test_macdefaults.py +++ b/tests/integration/modules/test_macdefaults.py @@ -1,51 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" Validate the mac-defaults module -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest, skip_if_not_root -DEFAULT_DOMAIN = 'com.apple.AppleMultitouchMouse' -DEFAULT_KEY = 'MouseHorizontalScroll' -DEFAULT_VALUE = '0' +DEFAULT_DOMAIN = "com.apple.AppleMultitouchMouse" +DEFAULT_KEY = "MouseHorizontalScroll" +DEFAULT_VALUE = "0" @destructiveTest @skip_if_not_root class MacDefaultsModuleTest(ModuleCase): - ''' + """ Integration tests for the mac_default module - ''' + """ + def setUp(self): - ''' + """ Sets up the test requirements - ''' - os_grain = self.run_function('grains.item', ['kernel']) + """ + os_grain = self.run_function("grains.item", ["kernel"]) # Must be running on a mac - if os_grain['kernel'] not in 'Darwin': - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) + if os_grain["kernel"] not in "Darwin": + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) def test_macdefaults_write_read(self): - ''' + """ Tests that writes and reads macdefaults - ''' - write_domain = self.run_function('macdefaults.write', - [DEFAULT_DOMAIN, - DEFAULT_KEY, - DEFAULT_VALUE]) + """ + write_domain = self.run_function( + "macdefaults.write", [DEFAULT_DOMAIN, DEFAULT_KEY, DEFAULT_VALUE] + ) self.assertTrue(write_domain) - read_domain = self.run_function('macdefaults.read', - [DEFAULT_DOMAIN, - DEFAULT_KEY]) + read_domain = self.run_function( + "macdefaults.read", [DEFAULT_DOMAIN, DEFAULT_KEY] + ) self.assertTrue(read_domain) self.assertEqual(read_domain, DEFAULT_VALUE) diff --git a/tests/integration/modules/test_mine.py b/tests/integration/modules/test_mine.py index ac8f27970eb..f66571d1985 100644 --- a/tests/integration/modules/test_mine.py +++ b/tests/integration/modules/test_mine.py @@ -1,203 +1,179 @@ # -*- coding: utf-8 -*- -''' +""" Test the salt mine system -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals -import time -import pprint -# Import Salt Testing libs +import pprint +import time + +import pytest +import salt.utils.platform from tests.support.case import ModuleCase, ShellCase from tests.support.runtests import RUNTIME_VARS -# Import Salt libs -import salt.utils.platform - +@pytest.mark.windows_whitelisted class MineTest(ModuleCase, ShellCase): - ''' + """ Test the mine system - ''' + """ + def setUp(self): - self.tgt = r'\*' + self.tgt = r"\*" if salt.utils.platform.is_windows(): - self.tgt = '*' + self.tgt = "*" self.wait_for_all_jobs() def test_get(self): - ''' + """ test mine.get and mine.update - ''' - assert self.run_function('mine.update', minion_tgt='minion') - assert self.run_function('mine.update', minion_tgt='sub_minion') + """ + assert self.run_function("mine.update", minion_tgt="minion") + assert self.run_function("mine.update", minion_tgt="sub_minion") # Since the minion has mine_functions defined in its configuration, # mine.update will return True - self.assertTrue( - self.run_function( - 'mine.get', - ['minion', 'test.ping'] - ) - ) + self.assertTrue(self.run_function("mine.get", ["minion", "test.ping"])) def test_get_allow_tgt(self): - ''' + """ test mine.get and mine.update using allow_tgt - ''' - assert self.run_function('mine.update', minion_tgt='minion') - assert self.run_function('mine.update', minion_tgt='sub_minion') + """ + assert self.run_function("mine.update", minion_tgt="minion") + assert self.run_function("mine.update", minion_tgt="sub_minion") # sub_minion should be able to view test.arg data - sub_min_ret = self.run_call('mine.get {0} test.arg'.format(self.tgt), config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR) + sub_min_ret = self.run_call( + "mine.get {0} test.arg".format(self.tgt), + config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, + ) assert " - isn't" in sub_min_ret # minion should not be able to view test.arg data - min_ret = self.run_call('mine.get {0} test.arg'.format(self.tgt)) + min_ret = self.run_call("mine.get {0} test.arg".format(self.tgt)) assert " - isn't" not in min_ret def test_send_allow_tgt(self): - ''' + """ test mine.send with allow_tgt set - ''' - mine_name = 'test_this' - for minion in ['sub_minion', 'minion']: - assert self.run_function('mine.send', [mine_name, - 'mine_function=test.arg_clean', 'one'], allow_tgt='sub_minion', - minion_tgt=minion) - min_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name)) - sub_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name), - config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR) + """ + mine_name = "test_this" + for minion in ["sub_minion", "minion"]: + assert self.run_function( + "mine.send", + [mine_name, "mine_function=test.arg_clean", "one"], + allow_tgt="sub_minion", + minion_tgt=minion, + ) + min_ret = self.run_call("mine.get {0} {1}".format(self.tgt, mine_name)) + sub_ret = self.run_call( + "mine.get {0} {1}".format(self.tgt, mine_name), + config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, + ) # ensure we did get the mine_name mine function for sub_minion - assert ' - one' in sub_ret + assert " - one" in sub_ret # ensure we did not get the mine_name mine function for minion - assert ' - one' not in min_ret + assert " - one" not in min_ret def test_send_allow_tgt_compound(self): - ''' + """ test mine.send with allow_tgt set and using compound targeting - ''' - mine_name = 'test_this_comp' - for minion in ['sub_minion', 'minion']: - assert self.run_function('mine.send', [mine_name, - 'mine_function=test.arg_clean', 'one'], - allow_tgt='L@minion,sub_minion', - allow_tgt_type='compound', - minion_tgt=minion) - min_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name)) - sub_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name), - config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR) + """ + mine_name = "test_this_comp" + for minion in ["sub_minion", "minion"]: + assert self.run_function( + "mine.send", + [mine_name, "mine_function=test.arg_clean", "one"], + allow_tgt="L@minion,sub_minion", + allow_tgt_type="compound", + minion_tgt=minion, + ) + min_ret = self.run_call("mine.get {0} {1}".format(self.tgt, mine_name)) + sub_ret = self.run_call( + "mine.get {0} {1}".format(self.tgt, mine_name), + config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, + ) # ensure we get the mine_name mine function for both minions for ret in [min_ret, sub_ret]: - assert ' - one' in ret + assert " - one" in ret def test_send_allow_tgt_doesnotexist(self): - ''' + """ test mine.send with allow_tgt set when the minion defined in allow_tgt does not exist - ''' - mine_name = 'mine_doesnotexist' - for minion in ['sub_minion', 'minion']: - assert self.run_function('mine.send', [mine_name, - 'mine_function=test.arg_clean', 'one'], allow_tgt='doesnotexist', - minion_tgt=minion) - min_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name)) - sub_ret = self.run_call('mine.get {0} {1}'.format(self.tgt, mine_name), - config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR) + """ + mine_name = "mine_doesnotexist" + for minion in ["sub_minion", "minion"]: + assert self.run_function( + "mine.send", + [mine_name, "mine_function=test.arg_clean", "one"], + allow_tgt="doesnotexist", + minion_tgt=minion, + ) + min_ret = self.run_call("mine.get {0} {1}".format(self.tgt, mine_name)) + sub_ret = self.run_call( + "mine.get {0} {1}".format(self.tgt, mine_name), + config_dir=RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, + ) # ensure we did not get the mine_name mine function for both minions for ret in [sub_ret, min_ret]: - assert ' - one' not in ret + assert " - one" not in ret def test_send(self): - ''' + """ test mine.send - ''' - self.assertFalse( - self.run_function( - 'mine.send', - ['foo.__spam_and_cheese'] - ) + """ + self.assertFalse(self.run_function("mine.send", ["foo.__spam_and_cheese"])) + self.assertTrue( + self.run_function("mine.send", ["grains.items"], minion_tgt="minion",) ) self.assertTrue( - self.run_function( - 'mine.send', - ['grains.items'], - minion_tgt='minion', - ) - ) - self.assertTrue( - self.run_function( - 'mine.send', - ['grains.items'], - minion_tgt='sub_minion', - ) + self.run_function("mine.send", ["grains.items"], minion_tgt="sub_minion",) ) + ret = self.run_function("mine.get", ["sub_minion", "grains.items"]) + self.assertEqual(ret["sub_minion"]["id"], "sub_minion") ret = self.run_function( - 'mine.get', - ['sub_minion', 'grains.items'] + "mine.get", ["minion", "grains.items"], minion_tgt="sub_minion" ) - self.assertEqual(ret['sub_minion']['id'], 'sub_minion') - ret = self.run_function( - 'mine.get', - ['minion', 'grains.items'], - minion_tgt='sub_minion' - ) - self.assertEqual(ret['minion']['id'], 'minion') + self.assertEqual(ret["minion"]["id"], "minion") def test_mine_flush(self): - ''' + """ Test mine.flush - ''' + """ # TODO The calls to sleep were added in an attempt to make this tests # less flaky. If we still see it fail we need to look for a more robust # solution. - for minion_id in ('minion', 'sub_minion'): + for minion_id in ("minion", "sub_minion"): self.assertTrue( - self.run_function( - 'mine.send', - ['grains.items'], - minion_tgt=minion_id - ) + self.run_function("mine.send", ["grains.items"], minion_tgt=minion_id) ) time.sleep(1) - for minion_id in ('minion', 'sub_minion'): + for minion_id in ("minion", "sub_minion"): ret = self.run_function( - 'mine.get', - [minion_id, 'grains.items'], - minion_tgt=minion_id + "mine.get", [minion_id, "grains.items"], minion_tgt=minion_id ) - self.assertEqual(ret[minion_id]['id'], minion_id) + self.assertEqual(ret[minion_id]["id"], minion_id) time.sleep(1) - self.assertTrue( - self.run_function( - 'mine.flush', - minion_tgt='minion' - ) - ) + self.assertTrue(self.run_function("mine.flush", minion_tgt="minion")) time.sleep(1) - ret_flushed = self.run_function( - 'mine.get', - ['*', 'grains.items'] - ) - self.assertEqual(ret_flushed.get('minion', None), None) - self.assertEqual(ret_flushed['sub_minion']['id'], 'sub_minion') + ret_flushed = self.run_function("mine.get", ["*", "grains.items"]) + self.assertEqual(ret_flushed.get("minion", None), None) + self.assertEqual(ret_flushed["sub_minion"]["id"], "sub_minion") def test_mine_delete(self): - ''' + """ Test mine.delete - ''' + """ self.assertTrue( - self.run_function( - 'mine.send', - ['grains.items'], - minion_tgt='minion' - ) + self.run_function("mine.send", ["grains.items"], minion_tgt="minion") ) - self.wait_for_all_jobs(minions=('minion',)) + self.wait_for_all_jobs(minions=("minion",)) attempts = 10 ret_grains = None @@ -206,11 +182,9 @@ class MineTest(ModuleCase, ShellCase): break # Smoke testing that grains should now exist in the mine ret_grains = self.run_function( - 'mine.get', - ['minion', 'grains.items'], - minion_tgt='minion' + "mine.get", ["minion", "grains.items"], minion_tgt="minion" ) - if ret_grains and 'minion' in ret_grains: + if ret_grains and "minion" in ret_grains: break if attempts: @@ -221,87 +195,63 @@ class MineTest(ModuleCase, ShellCase): continue self.fail( - '\'minion\' was not found as a key of the \'mine.get\' \'grains.items\' call. Full return: {}'.format( + "'minion' was not found as a key of the 'mine.get' 'grains.items' call. Full return: {}".format( pprint.pformat(ret_grains) ) ) self.assertEqual( - ret_grains['minion']['id'], 'minion', - msg='{} != minion, full return payload: {}'.format( - ret_grains['minion']['id'], - pprint.pformat(ret_grains) - ) + ret_grains["minion"]["id"], + "minion", + msg="{} != minion, full return payload: {}".format( + ret_grains["minion"]["id"], pprint.pformat(ret_grains) + ), ) self.assertTrue( self.run_function( - 'mine.send', - ['test.arg', 'foo=bar', 'fnord=roscivs'], - minion_tgt='minion' + "mine.send", + ["test.arg", "foo=bar", "fnord=roscivs"], + minion_tgt="minion", ) ) - self.wait_for_all_jobs(minions=('minion',)) - ret_args = self.run_function( - 'mine.get', - ['minion', 'test.arg'] - ) + self.wait_for_all_jobs(minions=("minion",)) + ret_args = self.run_function("mine.get", ["minion", "test.arg"]) expected = { - 'minion': { - 'args': [], - 'kwargs': { - 'fnord': 'roscivs', - 'foo': 'bar', - }, - }, + "minion": {"args": [], "kwargs": {"fnord": "roscivs", "foo": "bar"}}, } # Smoke testing that test.arg exists in the mine self.assertDictEqual(ret_args, expected) self.assertTrue( - self.run_function( - 'mine.send', - ['test.echo', 'foo'], - minion_tgt='minion' - ) + self.run_function("mine.send", ["test.echo", "foo"], minion_tgt="minion") ) - self.wait_for_all_jobs(minions=('minion',)) + self.wait_for_all_jobs(minions=("minion",)) ret_echo = self.run_function( - 'mine.get', - ['minion', 'test.echo'], - minion_tgt='minion' + "mine.get", ["minion", "test.echo"], minion_tgt="minion" ) # Smoke testing that we were also able to set test.echo in the mine - self.assertEqual(ret_echo['minion'], 'foo') + self.assertEqual(ret_echo["minion"], "foo") self.assertTrue( - self.run_function( - 'mine.delete', - ['test.arg'], - minion_tgt='minion' - ) + self.run_function("mine.delete", ["test.arg"], minion_tgt="minion") ) - self.wait_for_all_jobs(minions=('minion',)) + self.wait_for_all_jobs(minions=("minion",)) ret_arg_deleted = self.run_function( - 'mine.get', - ['minion', 'test.arg'], - minion_tgt='minion' + "mine.get", ["minion", "test.arg"], minion_tgt="minion" ) # Now comes the real test - did we obliterate test.arg from the mine? # We could assert this a different way, but there shouldn't be any # other tests that are setting this mine value, so this should # definitely avoid any race conditions. self.assertFalse( - ret_arg_deleted.get('minion', {}) - .get('kwargs', {}) - .get('fnord', None) == 'roscivs', + ret_arg_deleted.get("minion", {}).get("kwargs", {}).get("fnord", None) + == "roscivs", '{} contained "fnord":"roscivs", which should be gone'.format( ret_arg_deleted, - ) + ), ) ret_echo_stays = self.run_function( - 'mine.get', - ['minion', 'test.echo'], - minion_tgt='minion' + "mine.get", ["minion", "test.echo"], minion_tgt="minion" ) # Of course, one more health check - we want targeted removal. # This isn't horseshoes or hand grenades - test.arg should go away # but test.echo should still be available. - self.assertEqual(ret_echo_stays['minion'], 'foo') + self.assertEqual(ret_echo_stays["minion"], "foo") diff --git a/tests/integration/modules/test_mysql.py b/tests/integration/modules/test_mysql.py index 7edb77cb940..4b81bbcc62b 100644 --- a/tests/integration/modules/test_mysql.py +++ b/tests/integration/modules/test_mysql.py @@ -1,22 +1,18 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt Testing libs +import pytest +import salt.utils.path +from salt.ext import six +from salt.ext.six.moves import range +from salt.modules import mysql as mysqlmod from tests.support.case import ModuleCase -from tests.support.unit import skipIf from tests.support.helpers import destructiveTest from tests.support.mixins import SaltReturnAssertsMixin - -# Import salt libs -import salt.utils.path -from salt.modules import mysql as mysqlmod - -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from tests.support.unit import skipIf log = logging.getLogger(__name__) @@ -26,197 +22,172 @@ try: except Exception: # pylint: disable=broad-except NO_MYSQL = True -if not salt.utils.path.which('mysqladmin'): +if not salt.utils.path.which("mysqladmin"): NO_MYSQL = True @skipIf( NO_MYSQL, - 'Please install MySQL bindings and a MySQL Server before running' - 'MySQL integration tests.' + "Please install MySQL bindings and a MySQL Server before running" + "MySQL integration tests.", ) +@pytest.mark.windows_whitelisted class MysqlModuleDbTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Module testing database creation on a real MySQL Server. - ''' + """ - user = 'root' - password = 'poney' + user = "root" + password = "poney" @destructiveTest def setUp(self): - ''' + """ Test presence of MySQL server, enforce a root password - ''' + """ super(MysqlModuleDbTest, self).setUp() NO_MYSQL_SERVER = True # now ensure we know the mysql root password # one of theses two at least should work ret1 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' flush-privileges password "' + + self.password + + '"', ) ret2 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' --password="' - + self.password - + '" flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' --password="' + + self.password + + '" flush-privileges password "' + + self.password + + '"', ) key, value = ret2.popitem() - if value['result']: + if value["result"]: NO_MYSQL_SERVER = False else: - self.skipTest('No MySQL Server running, or no root access on it.') + self.skipTest("No MySQL Server running, or no root access on it.") - def _db_creation_loop(self, - db_name, - returning_name, - test_conn=False, - **kwargs): - ''' + def _db_creation_loop(self, db_name, returning_name, test_conn=False, **kwargs): + """ Used in db testCase, create, check exists, check in list and removes. - ''' - ret = self.run_function( - 'mysql.db_create', - name=db_name, - **kwargs - ) + """ + ret = self.run_function("mysql.db_create", name=db_name, **kwargs) self.assertEqual( - True, - ret, - 'Problem while creating db for db name: \'{0}\''.format(db_name) + True, ret, "Problem while creating db for db name: '{0}'".format(db_name) ) # test db exists - ret = self.run_function( - 'mysql.db_exists', - name=db_name, - **kwargs - ) + ret = self.run_function("mysql.db_exists", name=db_name, **kwargs) self.assertEqual( True, ret, - 'Problem while testing db exists for db name: \'{0}\''.format(db_name) + "Problem while testing db exists for db name: '{0}'".format(db_name), ) # List db names to ensure db is created with the right utf8 string - ret = self.run_function( - 'mysql.db_list', - **kwargs - ) + ret = self.run_function("mysql.db_list", **kwargs) if not isinstance(ret, list): raise AssertionError( - ('Unexpected query result while retrieving databases list' - ' \'{0}\' for \'{1}\' test').format( - ret, - db_name - ) - ) + ( + "Unexpected query result while retrieving databases list" + " '{0}' for '{1}' test" + ).format(ret, db_name) + ) self.assertIn( returning_name, ret, - ('Problem while testing presence of db name in db lists' - ' for db name: \'{0}\' in list \'{1}\'').format( - db_name, - ret - )) + ( + "Problem while testing presence of db name in db lists" + " for db name: '{0}' in list '{1}'" + ).format(db_name, ret), + ) if test_conn: # test connections on database with root user ret = self.run_function( - 'mysql.query', - database=db_name, - query='SELECT 1', - **kwargs + "mysql.query", database=db_name, query="SELECT 1", **kwargs ) - if not isinstance(ret, dict) or 'results' not in ret: + if not isinstance(ret, dict) or "results" not in ret: raise AssertionError( - ('Unexpected result while testing connection' - ' on database : {0}').format( - repr(db_name) - ) + ( + "Unexpected result while testing connection" + " on database : {0}" + ).format(repr(db_name)) ) - self.assertEqual([['1']], ret['results']) + self.assertEqual([["1"]], ret["results"]) # Now remove database - ret = self.run_function( - 'mysql.db_remove', - name=db_name, - **kwargs - ) + ret = self.run_function("mysql.db_remove", name=db_name, **kwargs) self.assertEqual( - True, - ret, - 'Problem while removing db for db name: \'{0}\''.format(db_name) + True, ret, "Problem while removing db for db name: '{0}'".format(db_name) ) @destructiveTest def test_database_creation_level1(self): - ''' + """ Create database, test presence, then drop db. All theses with complex names. - ''' + """ # name with space - db_name = 'foo 1' - self._db_creation_loop(db_name=db_name, - returning_name=db_name, - test_conn=True, - connection_user=self.user, - connection_pass=self.password + db_name = "foo 1" + self._db_creation_loop( + db_name=db_name, + returning_name=db_name, + test_conn=True, + connection_user=self.user, + connection_pass=self.password, ) # ``````` # create # also with character_set and collate only ret = self.run_function( - 'mysql.db_create', - name='foo`2', - character_set='utf8', - collate='utf8_general_ci', - connection_user=self.user, - connection_pass=self.password + "mysql.db_create", + name="foo`2", + character_set="utf8", + collate="utf8_general_ci", + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(True, ret) # test db exists ret = self.run_function( - 'mysql.db_exists', - name='foo`2', - connection_user=self.user, - connection_pass=self.password + "mysql.db_exists", + name="foo`2", + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(True, ret) # redoing the same should fail # even with other character sets or collations ret = self.run_function( - 'mysql.db_create', - name='foo`2', - character_set='utf8', - collate='utf8_general_ci', - connection_user=self.user, - connection_pass=self.password + "mysql.db_create", + name="foo`2", + character_set="utf8", + collate="utf8_general_ci", + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(False, ret) # redoing the same should fail ret = self.run_function( - 'mysql.db_create', - name='foo`2', - character_set='utf8', - collate='utf8_general_ci', - connection_user=self.user, - connection_pass=self.password + "mysql.db_create", + name="foo`2", + character_set="utf8", + collate="utf8_general_ci", + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(False, ret) # Now remove database ret = self.run_function( - 'mysql.db_remove', - name='foo`2', - connection_user=self.user, - connection_pass=self.password + "mysql.db_remove", + name="foo`2", + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(True, ret) @@ -224,792 +195,886 @@ class MysqlModuleDbTest(ModuleCase, SaltReturnAssertsMixin): # create # also with character_set only db_name = "foo'3" - self._db_creation_loop(db_name=db_name, - returning_name=db_name, - test_conn=True, - character_set='utf8', - connection_user=self.user, - connection_pass=self.password + self._db_creation_loop( + db_name=db_name, + returning_name=db_name, + test_conn=True, + character_set="utf8", + connection_user=self.user, + connection_pass=self.password, ) # """""""" # also with collate only db_name = 'foo"4' - self._db_creation_loop(db_name=db_name, - returning_name=db_name, - test_conn=True, - collate='utf8_general_ci', - connection_user=self.user, - connection_pass=self.password + self._db_creation_loop( + db_name=db_name, + returning_name=db_name, + test_conn=True, + collate="utf8_general_ci", + connection_user=self.user, + connection_pass=self.password, ) # fuzzy db_name = '<foo` --"5>' - self._db_creation_loop(db_name=db_name, - returning_name=db_name, - test_conn=True, - connection_user=self.user, - connection_pass=self.password + self._db_creation_loop( + db_name=db_name, + returning_name=db_name, + test_conn=True, + connection_user=self.user, + connection_pass=self.password, ) @destructiveTest def test_mysql_dbname_character_percent(self): - ''' + """ Play with the '%' character problems This character should be escaped in the form '%%' on queries, but only when theses queries have arguments. It is also a special character in LIKE SQL queries. Finally it is used to indicate query arguments. - ''' + """ db_name1 = "foo%1_" db_name2 = "foo%12" ret = self.run_function( - 'mysql.db_create', - name=db_name1, - character_set='utf8', - collate='utf8_general_ci', - connection_user=self.user, - connection_pass=self.password + "mysql.db_create", + name=db_name1, + character_set="utf8", + collate="utf8_general_ci", + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(True, ret) ret = self.run_function( - 'mysql.db_create', - name=db_name2, - connection_user=self.user, - connection_pass=self.password + "mysql.db_create", + name=db_name2, + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(True, ret) ret = self.run_function( - 'mysql.db_remove', - name=db_name1, - connection_user=self.user, - connection_pass=self.password - ) - self.assertEqual(True, ret) - ret = self.run_function( - 'mysql.db_exists', + "mysql.db_remove", name=db_name1, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, + ) + self.assertEqual(True, ret) + ret = self.run_function( + "mysql.db_exists", + name=db_name1, + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(False, ret) ret = self.run_function( - 'mysql.db_exists', + "mysql.db_exists", name=db_name2, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) self.assertEqual(True, ret) ret = self.run_function( - 'mysql.db_remove', - name=db_name2, - connection_user=self.user, - connection_pass=self.password + "mysql.db_remove", + name=db_name2, + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(True, ret) @destructiveTest def test_database_creation_utf8(self): - ''' + """ Test support of utf8 in database names - ''' + """ # Simple accents : using utf8 string - db_name_unicode = u'notam\xe9rican' + db_name_unicode = "notam\xe9rican" # same as 'notamérican' because of file encoding # but ensure it on this test - db_name_utf8 = 'notam\xc3\xa9rican' + db_name_utf8 = "notam\xc3\xa9rican" # FIXME: MySQLdb problems on conn strings containing # utf-8 on user name of db name prevent conn test - self._db_creation_loop(db_name=db_name_utf8, - returning_name=db_name_utf8, - test_conn=False, - connection_user=self.user, - connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + self._db_creation_loop( + db_name=db_name_utf8, + returning_name=db_name_utf8, + test_conn=False, + connection_user=self.user, + connection_pass=self.password, + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) # test unicode entry will also return utf8 name - self._db_creation_loop(db_name=db_name_unicode, - returning_name=db_name_utf8, - test_conn=False, - connection_user=self.user, - connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + self._db_creation_loop( + db_name=db_name_unicode, + returning_name=db_name_utf8, + test_conn=False, + connection_user=self.user, + connection_pass=self.password, + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) # Using more complex unicode characters: - db_name_unicode = u'\u6a19\u6e96\u8a9e' + db_name_unicode = "\u6a19\u6e96\u8a9e" # same as '標準語' because of file encoding # but ensure it on this test - db_name_utf8 = '\xe6\xa8\x99\xe6\xba\x96\xe8\xaa\x9e' - self._db_creation_loop(db_name=db_name_utf8, - returning_name=db_name_utf8, - test_conn=False, - connection_user=self.user, - connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + db_name_utf8 = "\xe6\xa8\x99\xe6\xba\x96\xe8\xaa\x9e" + self._db_creation_loop( + db_name=db_name_utf8, + returning_name=db_name_utf8, + test_conn=False, + connection_user=self.user, + connection_pass=self.password, + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) # test unicode entry will also return utf8 name - self._db_creation_loop(db_name=db_name_unicode, - returning_name=db_name_utf8, - test_conn=False, - connection_user=self.user, - connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + self._db_creation_loop( + db_name=db_name_unicode, + returning_name=db_name_utf8, + test_conn=False, + connection_user=self.user, + connection_pass=self.password, + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) @destructiveTest def test_database_maintenance(self): - ''' + """ Test maintenance operations on a created database - ''' - dbname = u"foo%'-- `\"'" + """ + dbname = "foo%'-- `\"'" # create database # but first silently try to remove it # in case of previous tests failures ret = self.run_function( - 'mysql.db_remove', - name=dbname, - connection_user=self.user, - connection_pass=self.password + "mysql.db_remove", + name=dbname, + connection_user=self.user, + connection_pass=self.password, ) ret = self.run_function( - 'mysql.db_create', - name=dbname, - character_set='utf8', - collate='utf8_general_ci', - connection_user=self.user, - connection_pass=self.password + "mysql.db_create", + name=dbname, + character_set="utf8", + collate="utf8_general_ci", + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(True, ret) # test db exists ret = self.run_function( - 'mysql.db_exists', - name=dbname, - connection_user=self.user, - connection_pass=self.password + "mysql.db_exists", + name=dbname, + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(True, ret) # Create 3 tables - tablenames = {'A%table "`1': 'MYISAM', - 'B%table \'`2': 'InnoDB', - 'Ctable --`3': 'MEMORY' - } + tablenames = { + 'A%table "`1': "MYISAM", + "B%table '`2": "InnoDB", + "Ctable --`3": "MEMORY", + } for tablename, engine in sorted(six.iteritems(tablenames)): # prepare queries - create_query = ('CREATE TABLE {tblname} (' - ' id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,' - ' data VARCHAR(100)) ENGINE={engine};'.format( - tblname=mysqlmod.quote_identifier(tablename), - engine=engine, - )) - insert_query = ('INSERT INTO {tblname} (data)' - ' VALUES '.format( - tblname=mysqlmod.quote_identifier(tablename) - )) - delete_query = ('DELETE from {tblname}' - ' order by rand() limit 50;'.format( - tblname=mysqlmod.quote_identifier(tablename) - )) + create_query = ( + "CREATE TABLE {tblname} (" + " id INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + " data VARCHAR(100)) ENGINE={engine};".format( + tblname=mysqlmod.quote_identifier(tablename), engine=engine, + ) + ) + insert_query = "INSERT INTO {tblname} (data)" " VALUES ".format( + tblname=mysqlmod.quote_identifier(tablename) + ) + delete_query = "DELETE from {tblname}" " order by rand() limit 50;".format( + tblname=mysqlmod.quote_identifier(tablename) + ) for x in range(100): - insert_query += "('foo"+six.text_type(x)+"')," + insert_query += "('foo" + six.text_type(x) + "')," insert_query += "('bar');" # populate database - log.info('Adding table \'%s\'', tablename) + log.info("Adding table '%s'", tablename) ret = self.run_function( - 'mysql.query', - database=dbname, - query=create_query, - connection_user=self.user, - connection_pass=self.password + "mysql.query", + database=dbname, + query=create_query, + connection_user=self.user, + connection_pass=self.password, ) - if not isinstance(ret, dict) or 'rows affected' not in ret: + if not isinstance(ret, dict) or "rows affected" not in ret: raise AssertionError( - ('Unexpected query result while populating test table' - ' \'{0}\' : \'{1}\'').format( - tablename, - ret, + ( + "Unexpected query result while populating test table" + " '{0}' : '{1}'" + ).format( + tablename, ret, ) ) - self.assertEqual(ret['rows affected'], 0) - log.info('Populating table \'%s\'', tablename) + self.assertEqual(ret["rows affected"], 0) + log.info("Populating table '%s'", tablename) ret = self.run_function( - 'mysql.query', - database=dbname, - query=insert_query, - connection_user=self.user, - connection_pass=self.password + "mysql.query", + database=dbname, + query=insert_query, + connection_user=self.user, + connection_pass=self.password, ) - if not isinstance(ret, dict) or 'rows affected' not in ret: + if not isinstance(ret, dict) or "rows affected" not in ret: raise AssertionError( - ('Unexpected query result while populating test table' - ' \'{0}\' : \'{1}\'').format( - tablename, - ret, + ( + "Unexpected query result while populating test table" + " '{0}' : '{1}'" + ).format( + tablename, ret, ) ) - self.assertEqual(ret['rows affected'], 101) - log.info('Removing some rows on table\'%s\'', tablename) + self.assertEqual(ret["rows affected"], 101) + log.info("Removing some rows on table'%s'", tablename) ret = self.run_function( - 'mysql.query', - database=dbname, - query=delete_query, - connection_user=self.user, - connection_pass=self.password + "mysql.query", + database=dbname, + query=delete_query, + connection_user=self.user, + connection_pass=self.password, ) - if not isinstance(ret, dict) or 'rows affected' not in ret: + if not isinstance(ret, dict) or "rows affected" not in ret: raise AssertionError( - ('Unexpected query result while removing rows on test table' - ' \'{0}\' : \'{1}\'').format( - tablename, - ret, + ( + "Unexpected query result while removing rows on test table" + " '{0}' : '{1}'" + ).format( + tablename, ret, ) ) - self.assertEqual(ret['rows affected'], 50) + self.assertEqual(ret["rows affected"], 50) # test check/repair/opimize on 1 table tablename = 'A%table "`1' ret = self.run_function( - 'mysql.db_check', - name=dbname, - table=tablename, - connection_user=self.user, - connection_pass=self.password + "mysql.db_check", + name=dbname, + table=tablename, + connection_user=self.user, + connection_pass=self.password, ) # Note that returned result does not quote_identifier of table and db - self.assertEqual(ret, [{'Table': dbname+'.'+tablename, - 'Msg_text': 'OK', - 'Msg_type': 'status', - 'Op': 'check'}] + self.assertEqual( + ret, + [ + { + "Table": dbname + "." + tablename, + "Msg_text": "OK", + "Msg_type": "status", + "Op": "check", + } + ], ) ret = self.run_function( - 'mysql.db_repair', - name=dbname, - table=tablename, - connection_user=self.user, - connection_pass=self.password + "mysql.db_repair", + name=dbname, + table=tablename, + connection_user=self.user, + connection_pass=self.password, ) # Note that returned result does not quote_identifier of table and db - self.assertEqual(ret, [{'Table': dbname+'.'+tablename, - 'Msg_text': 'OK', - 'Msg_type': 'status', - 'Op': 'repair'}] + self.assertEqual( + ret, + [ + { + "Table": dbname + "." + tablename, + "Msg_text": "OK", + "Msg_type": "status", + "Op": "repair", + } + ], ) ret = self.run_function( - 'mysql.db_optimize', - name=dbname, - table=tablename, - connection_user=self.user, - connection_pass=self.password + "mysql.db_optimize", + name=dbname, + table=tablename, + connection_user=self.user, + connection_pass=self.password, ) # Note that returned result does not quote_identifier of table and db - self.assertEqual(ret, [{'Table': dbname+'.'+tablename, - 'Msg_text': 'OK', - 'Msg_type': 'status', - 'Op': 'optimize'}] + self.assertEqual( + ret, + [ + { + "Table": dbname + "." + tablename, + "Msg_text": "OK", + "Msg_type": "status", + "Op": "optimize", + } + ], ) # test check/repair/opimize on all tables ret = self.run_function( - 'mysql.db_check', - name=dbname, - connection_user=self.user, - connection_pass=self.password + "mysql.db_check", + name=dbname, + connection_user=self.user, + connection_pass=self.password, ) expected = [] for tablename, engine in sorted(six.iteritems(tablenames)): - if engine is 'MEMORY': - expected.append([{ - 'Table': dbname+'.'+tablename, - 'Msg_text': ("The storage engine for the table doesn't" - " support check"), - 'Msg_type': 'note', - 'Op': 'check' - }]) + if engine is "MEMORY": + expected.append( + [ + { + "Table": dbname + "." + tablename, + "Msg_text": ( + "The storage engine for the table doesn't" + " support check" + ), + "Msg_type": "note", + "Op": "check", + } + ] + ) else: - expected.append([{ - 'Table': dbname+'.'+tablename, - 'Msg_text': 'OK', - 'Msg_type': 'status', - 'Op': 'check' - }]) + expected.append( + [ + { + "Table": dbname + "." + tablename, + "Msg_text": "OK", + "Msg_type": "status", + "Op": "check", + } + ] + ) self.assertEqual(ret, expected) ret = self.run_function( - 'mysql.db_repair', - name=dbname, - connection_user=self.user, - connection_pass=self.password + "mysql.db_repair", + name=dbname, + connection_user=self.user, + connection_pass=self.password, ) expected = [] for tablename, engine in sorted(six.iteritems(tablenames)): - if engine is 'MYISAM': - expected.append([{ - 'Table': dbname+'.'+tablename, - 'Msg_text': 'OK', - 'Msg_type': 'status', - 'Op': 'repair' - }]) + if engine is "MYISAM": + expected.append( + [ + { + "Table": dbname + "." + tablename, + "Msg_text": "OK", + "Msg_type": "status", + "Op": "repair", + } + ] + ) else: - expected.append([{ - 'Table': dbname+'.'+tablename, - 'Msg_text': ("The storage engine for the table doesn't" - " support repair"), - 'Msg_type': 'note', - 'Op': 'repair' - }]) + expected.append( + [ + { + "Table": dbname + "." + tablename, + "Msg_text": ( + "The storage engine for the table doesn't" + " support repair" + ), + "Msg_type": "note", + "Op": "repair", + } + ] + ) self.assertEqual(ret, expected) ret = self.run_function( - 'mysql.db_optimize', - name=dbname, - connection_user=self.user, - connection_pass=self.password + "mysql.db_optimize", + name=dbname, + connection_user=self.user, + connection_pass=self.password, ) expected = [] for tablename, engine in sorted(six.iteritems(tablenames)): - if engine is 'MYISAM': - expected.append([{ - 'Table': dbname+'.'+tablename, - 'Msg_text': 'OK', - 'Msg_type': 'status', - 'Op': 'optimize' - }]) - elif engine is 'InnoDB': - expected.append([{ - 'Table': dbname+'.'+tablename, - 'Msg_text': ("Table does not support optimize, " - "doing recreate + analyze instead"), - 'Msg_type': 'note', - 'Op': 'optimize' - }, - { - 'Table': dbname+'.'+tablename, - 'Msg_text': 'OK', - 'Msg_type': 'status', - 'Op': 'optimize' - }]) - elif engine is 'MEMORY': - expected.append([{ - 'Table': dbname+'.'+tablename, - 'Msg_text': ("The storage engine for the table doesn't" - " support optimize"), - 'Msg_type': 'note', - 'Op': 'optimize' - }]) + if engine is "MYISAM": + expected.append( + [ + { + "Table": dbname + "." + tablename, + "Msg_text": "OK", + "Msg_type": "status", + "Op": "optimize", + } + ] + ) + elif engine is "InnoDB": + expected.append( + [ + { + "Table": dbname + "." + tablename, + "Msg_text": ( + "Table does not support optimize, " + "doing recreate + analyze instead" + ), + "Msg_type": "note", + "Op": "optimize", + }, + { + "Table": dbname + "." + tablename, + "Msg_text": "OK", + "Msg_type": "status", + "Op": "optimize", + }, + ] + ) + elif engine is "MEMORY": + expected.append( + [ + { + "Table": dbname + "." + tablename, + "Msg_text": ( + "The storage engine for the table doesn't" + " support optimize" + ), + "Msg_type": "note", + "Op": "optimize", + } + ] + ) self.assertEqual(ret, expected) # Teardown, remove database ret = self.run_function( - 'mysql.db_remove', - name=dbname, - connection_user=self.user, - connection_pass=self.password + "mysql.db_remove", + name=dbname, + connection_user=self.user, + connection_pass=self.password, ) self.assertEqual(True, ret) @skipIf( NO_MYSQL, - 'Please install MySQL bindings and a MySQL Server before running' - 'MySQL integration tests.' + "Please install MySQL bindings and a MySQL Server before running" + "MySQL integration tests.", ) +@pytest.mark.windows_whitelisted class MysqlModuleUserTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ User Creation and connection tests - ''' + """ - user = 'root' - password = 'poney' + user = "root" + password = "poney" @destructiveTest def setUp(self): - ''' + """ Test presence of MySQL server, enforce a root password - ''' + """ super(MysqlModuleUserTest, self).setUp() NO_MYSQL_SERVER = True # now ensure we know the mysql root password # one of theses two at least should work ret1 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' flush-privileges password "' + + self.password + + '"', ) ret2 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' --password="' - + self.password - + '" flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' --password="' + + self.password + + '" flush-privileges password "' + + self.password + + '"', ) key, value = ret2.popitem() - if value['result']: + if value["result"]: NO_MYSQL_SERVER = False else: - self.skipTest('No MySQL Server running, or no root access on it.') + self.skipTest("No MySQL Server running, or no root access on it.") - def _userCreationLoop(self, - uname, - host, - password=None, - new_password=None, - new_password_hash=None, - **kwargs): - ''' + def _userCreationLoop( + self, + uname, + host, + password=None, + new_password=None, + new_password_hash=None, + **kwargs + ): + """ Perform some tests around creation of the given user - ''' + """ # First silently remove it, in case of - ret = self.run_function( - 'mysql.user_remove', - user=uname, - host=host, - **kwargs - ) + ret = self.run_function("mysql.user_remove", user=uname, host=host, **kwargs) # creation ret = self.run_function( - 'mysql.user_create', - user=uname, - host=host, - password=password, - **kwargs + "mysql.user_create", user=uname, host=host, password=password, **kwargs + ) + self.assertEqual( + True, + ret, + ("Calling user_create on" " user '{0}' did not return True: {1}").format( + uname, repr(ret) + ), ) - self.assertEqual(True, ret, ('Calling user_create on' - ' user \'{0}\' did not return True: {1}').format( - uname, - repr(ret) - )) # double creation failure ret = self.run_function( - 'mysql.user_create', - user=uname, - host=host, - password=password, - **kwargs + "mysql.user_create", user=uname, host=host, password=password, **kwargs + ) + self.assertEqual( + False, + ret, + ( + "Calling user_create a second time on" + " user '{0}' did not return False: {1}" + ).format(uname, repr(ret)), ) - self.assertEqual(False, ret, ('Calling user_create a second time on' - ' user \'{0}\' did not return False: {1}').format( - uname, - repr(ret) - )) # Alter password if new_password is not None or new_password_hash is not None: ret = self.run_function( - 'mysql.user_chpass', + "mysql.user_chpass", user=uname, host=host, password=new_password, password_hash=new_password_hash, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, + ) + self.assertEqual( + True, + ret, + ( + "Calling user_chpass on" " user '{0}' did not return True: {1}" + ).format(uname, repr(ret)), ) - self.assertEqual(True, ret, ('Calling user_chpass on' - ' user \'{0}\' did not return True: {1}').format( - uname, - repr(ret) - )) def _chck_userinfo(self, user, host, check_user, check_hash): - ''' + """ Internal routine to check user_info returned results - ''' + """ ret = self.run_function( - 'mysql.user_info', + "mysql.user_info", user=user, host=host, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) if not isinstance(ret, dict): raise AssertionError( - 'Unexpected result while retrieving user_info for ' - '\'{0}\''.format(user) + "Unexpected result while retrieving user_info for " "'{0}'".format(user) ) - self.assertEqual(ret['Host'], host) - self.assertEqual(ret['Password'], check_hash) - self.assertEqual(ret['User'], check_user) + self.assertEqual(ret["Host"], host) + self.assertEqual(ret["Password"], check_hash) + self.assertEqual(ret["User"], check_user) def _chk_remove_user(self, user, host, **kwargs): - ''' + """ Internal routine to check user_remove - ''' - ret = self.run_function( - 'mysql.user_remove', - user=user, - host=host, - **kwargs + """ + ret = self.run_function("mysql.user_remove", user=user, host=host, **kwargs) + self.assertEqual( + True, + ret, + ( + "Assertion failed while removing user" " '{0}' on host '{1}': {2}" + ).format(user, host, repr(ret)), ) - self.assertEqual(True, ret, ('Assertion failed while removing user' - ' \'{0}\' on host \'{1}\': {2}').format( - user, - host, - repr(ret) - )) @destructiveTest def test_user_management(self): - ''' + """ Test various users creation settings - ''' + """ # Create users with rights on this database # and rights on other databases user1 = "user '1" - user1_pwd = 'pwd`\'"1b' - user1_pwd_hash = '*4DF33B3B12E43384677050A818327877FAB2F4BA' + user1_pwd = "pwd`'\"1b" + user1_pwd_hash = "*4DF33B3B12E43384677050A818327877FAB2F4BA" # this is : user "2'標 - user2 = 'user "2\'\xe6\xa8\x99' - user2_pwd = 'user "2\'\xe6\xa8\x99b' - user2_pwd_hash = '*3A38A7B94B024B983687BB9B44FB60B7AA38FE61' + user2 = "user \"2'\xe6\xa8\x99" + user2_pwd = "user \"2'\xe6\xa8\x99b" + user2_pwd_hash = "*3A38A7B94B024B983687BB9B44FB60B7AA38FE61" user3 = 'user "3;,?:@=&/' user3_pwd = 'user "3;,?:@=&/' - user3_pwd_hash = '*AA3B1D4105A45D381C23A5C221C47EA349E1FD7D' + user3_pwd_hash = "*AA3B1D4105A45D381C23A5C221C47EA349E1FD7D" # this is : user ":=;4標 in unicode instead of utf-8 # if unicode char is counted as 1 char we hit the max user # size (16) - user4 = u'user":;,?:@=&/4\u6a19' + user4 = 'user":;,?:@=&/4\u6a19' user4_utf8 = 'user":;,?:@=&/4\xe6\xa8\x99' user4_pwd = 'user "4;,?:@=&/' - user4_pwd_hash = '*FC8EF8DBF27628E4E113359F8E7478D5CF3DD57C' - user5 = u'user ``"5' + user4_pwd_hash = "*FC8EF8DBF27628E4E113359F8E7478D5CF3DD57C" + user5 = 'user ``"5' user5_utf8 = 'user ``"5' # this is 標標標\ - user5_pwd = '\xe6\xa8\x99\xe6\xa8\x99\\' + user5_pwd = "\xe6\xa8\x99\xe6\xa8\x99\\" # this is password('標標\\') - user5_pwd_hash = '*3752E65CDD8751AF8D889C62CFFC6C998B12C376' - user6 = u'user %--"6' + user5_pwd_hash = "*3752E65CDD8751AF8D889C62CFFC6C998B12C376" + user6 = 'user %--"6' user6_utf8 = 'user %--"6' # this is : --'"% SIX標b - user6_pwd_u = u' --\'"% SIX\u6a19b' - user6_pwd_utf8 = ' --\'"% SIX\xe6\xa8\x99b' + user6_pwd_u = " --'\"% SIX\u6a19b" + user6_pwd_utf8 = " --'\"% SIX\xe6\xa8\x99b" # this is password(' --\'"% SIX標b') - user6_pwd_hash = '*90AE800593E2D407CD9E28CCAFBE42D17EEA5369' + user6_pwd_hash = "*90AE800593E2D407CD9E28CCAFBE42D17EEA5369" self._userCreationLoop( uname=user1, - host='localhost', - password='pwd`\'"1', - new_password='pwd`\'"1b', + host="localhost", + password="pwd`'\"1", + new_password="pwd`'\"1b", connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) # Now check for results ret = self.run_function( - 'mysql.user_exists', + "mysql.user_exists", user=user1, - host='localhost', + host="localhost", password=user1_pwd, password_hash=None, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self.assertEqual(True, ret, ('Testing final user \'{0}\' on host \'{1}\'' - ' existence failed').format(user1, 'localhost') + self.assertEqual( + True, + ret, + ("Testing final user '{0}' on host '{1}'" " existence failed").format( + user1, "localhost" + ), ) self._userCreationLoop( uname=user2, - host='localhost', + host="localhost", password=None, # this is his name hash : user "2'標 - password_hash='*EEF6F854748ACF841226BB1C2422BEC70AE7F1FF', + password_hash="*EEF6F854748ACF841226BB1C2422BEC70AE7F1FF", # and this is the same with a 'b' added new_password_hash=user2_pwd_hash, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) # user2 can connect from other places with other password self._userCreationLoop( uname=user2, - host='10.0.0.1', + host="10.0.0.1", allow_passwordless=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) self._userCreationLoop( uname=user2, - host='10.0.0.2', + host="10.0.0.2", allow_passwordless=True, unix_socket=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) # Now check for results ret = self.run_function( - 'mysql.user_exists', + "mysql.user_exists", user=user2, - host='localhost', + host="localhost", password=None, password_hash=user2_pwd_hash, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self.assertEqual(True, ret, ('Testing final user \'{0}\' on host \'{1}\'' - ' failed').format(user2, 'localhost') + self.assertEqual( + True, + ret, + ("Testing final user '{0}' on host '{1}'" " failed").format( + user2, "localhost" + ), ) ret = self.run_function( - 'mysql.user_exists', + "mysql.user_exists", user=user2, - host='10.0.0.1', + host="10.0.0.1", allow_passwordless=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self.assertEqual(True, ret, ('Testing final user \'{0}\' on host \'{1}\'' - ' without password failed').format(user2, '10.0.0.1') + self.assertEqual( + True, + ret, + ( + "Testing final user '{0}' on host '{1}'" " without password failed" + ).format(user2, "10.0.0.1"), ) ret = self.run_function( - 'mysql.user_exists', + "mysql.user_exists", user=user2, - host='10.0.0.2', + host="10.0.0.2", allow_passwordless=True, unix_socket=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self.assertEqual(True, ret, ('Testing final user \'{0}\' on host \'{1}\'' - ' without password failed').format(user2, '10.0.0.2') + self.assertEqual( + True, + ret, + ( + "Testing final user '{0}' on host '{1}'" " without password failed" + ).format(user2, "10.0.0.2"), ) # Empty password is not passwordless (or is it a bug?) self._userCreationLoop( uname=user3, - host='localhost', - password='', + host="localhost", + password="", connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) # user 3 on another host with a password self._userCreationLoop( uname=user3, - host='%', - password='foo', + host="%", + password="foo", new_password=user3_pwd, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) # Now check for results ret = self.run_function( - 'mysql.user_exists', + "mysql.user_exists", user=user3, - host='localhost', - password='', + host="localhost", + password="", connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) - self.assertEqual(True, ret, ('Testing final user \'{0}\' on host \'{1}\'' - ' without empty password failed').format(user3, 'localhost') + self.assertEqual( + True, + ret, + ( + "Testing final user '{0}' on host '{1}'" + " without empty password failed" + ).format(user3, "localhost"), ) ret = self.run_function( - 'mysql.user_exists', + "mysql.user_exists", user=user3, - host='%', + host="%", password=user3_pwd, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) - self.assertEqual(True, ret, ('Testing final user \'{0}\' on host \'{1}\'' - ' with password failed').format(user3, '%') + self.assertEqual( + True, + ret, + ("Testing final user '{0}' on host '{1}'" " with password failed").format( + user3, "%" + ), ) # check unicode name, and password > password_hash self._userCreationLoop( uname=user4, - host='%', + host="%", password=user4_pwd, # this is password('foo') - password_hash='*F3A2A51A9B0F2BE2468926B4132313728C250DBF', + password_hash="*F3A2A51A9B0F2BE2468926B4132313728C250DBF", connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) # Now check for results ret = self.run_function( - 'mysql.user_exists', + "mysql.user_exists", user=user4_utf8, - host='%', + host="%", password=user4_pwd, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self.assertEqual(True, ret, ('Testing final user \'{0}\' on host \'{1}\'' - ' with password take from password and not password_hash' - ' failed').format(user4_utf8, '%') + self.assertEqual( + True, + ret, + ( + "Testing final user '{0}' on host '{1}'" + " with password take from password and not password_hash" + " failed" + ).format(user4_utf8, "%"), ) self._userCreationLoop( uname=user5, - host='localhost', - password='\xe6\xa8\x99\xe6\xa8\x99', + host="localhost", + password="\xe6\xa8\x99\xe6\xa8\x99", new_password=user5_pwd, unix_socket=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) ret = self.run_function( - 'mysql.user_exists', + "mysql.user_exists", user=user5_utf8, - host='localhost', + host="localhost", password=user5_pwd, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self.assertEqual(True, ret, ('Testing final user \'{0}\' on host \'{1}\'' - ' with utf8 password failed').format(user5_utf8, 'localhost') + self.assertEqual( + True, + ret, + ( + "Testing final user '{0}' on host '{1}'" " with utf8 password failed" + ).format(user5_utf8, "localhost"), ) # for this one we give password in unicode and check it in utf-8 self._userCreationLoop( uname=user6, - host='10.0.0.1', - password=' foobar', + host="10.0.0.1", + password=" foobar", new_password=user6_pwd_u, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) # Now check for results ret = self.run_function( - 'mysql.user_exists', + "mysql.user_exists", user=user6_utf8, - host='10.0.0.1', + host="10.0.0.1", password=user6_pwd_utf8, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self.assertEqual(True, ret, ('Testing final user \'{0}\' on host \'{1}\'' - ' with unicode password failed').format(user6_utf8, '10.0.0.1') + self.assertEqual( + True, + ret, + ( + "Testing final user '{0}' on host '{1}'" " with unicode password failed" + ).format(user6_utf8, "10.0.0.1"), ) # Final result should be: # mysql> select Host, User, Password from user where user like 'user%'; @@ -1026,95 +1091,81 @@ class MysqlModuleUserTest(ModuleCase, SaltReturnAssertsMixin): # | user ``"5 | localhost | *3752E65CD(...)FC6C998B12C376 | # | user":;,?:@=&/4標 | % | *FC8EF8DBF(...)7478D5CF3DD57C | # +--------------------+-----------+-------------------------------+ - self._chck_userinfo(user=user2, - host='10.0.0.1', - check_user=user2, - check_hash='' + self._chck_userinfo( + user=user2, host="10.0.0.1", check_user=user2, check_hash="" ) - self._chck_userinfo(user=user2, - host='10.0.0.2', - check_user=user2, - check_hash='' + self._chck_userinfo( + user=user2, host="10.0.0.2", check_user=user2, check_hash="" ) - self._chck_userinfo(user=user2, - host='localhost', - check_user=user2, - check_hash=user2_pwd_hash + self._chck_userinfo( + user=user2, host="localhost", check_user=user2, check_hash=user2_pwd_hash ) - self._chck_userinfo(user=user3, - host='%', - check_user=user3, - check_hash=user3_pwd_hash + self._chck_userinfo( + user=user3, host="%", check_user=user3, check_hash=user3_pwd_hash ) - self._chck_userinfo(user=user3, - host='localhost', - check_user=user3, - check_hash='' + self._chck_userinfo( + user=user3, host="localhost", check_user=user3, check_hash="" ) - self._chck_userinfo(user=user4, - host='%', - check_user=user4_utf8, - check_hash=user4_pwd_hash + self._chck_userinfo( + user=user4, host="%", check_user=user4_utf8, check_hash=user4_pwd_hash ) - self._chck_userinfo(user=user6, - host='10.0.0.1', - check_user=user6_utf8, - check_hash=user6_pwd_hash + self._chck_userinfo( + user=user6, + host="10.0.0.1", + check_user=user6_utf8, + check_hash=user6_pwd_hash, ) - self._chck_userinfo(user=user1, - host='localhost', - check_user=user1, - check_hash=user1_pwd_hash + self._chck_userinfo( + user=user1, host="localhost", check_user=user1, check_hash=user1_pwd_hash ) - self._chck_userinfo(user=user5, - host='localhost', - check_user=user5_utf8, - check_hash=user5_pwd_hash + self._chck_userinfo( + user=user5, + host="localhost", + check_user=user5_utf8, + check_hash=user5_pwd_hash, ) # check user_list function ret = self.run_function( - 'mysql.user_list', + "mysql.user_list", connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self.assertIn({'Host': 'localhost', 'User': user1}, ret) - self.assertIn({'Host': 'localhost', 'User': user2}, ret) - self.assertIn({'Host': '10.0.0.1', 'User': user2}, ret) - self.assertIn({'Host': '10.0.0.2', 'User': user2}, ret) - self.assertIn({'Host': '%', 'User': user3}, ret) - self.assertIn({'Host': 'localhost', 'User': user3}, ret) - self.assertIn({'Host': '%', 'User': user4_utf8}, ret) - self.assertIn({'Host': 'localhost', 'User': user5_utf8}, ret) - self.assertIn({'Host': '10.0.0.1', 'User': user6_utf8}, ret) + self.assertIn({"Host": "localhost", "User": user1}, ret) + self.assertIn({"Host": "localhost", "User": user2}, ret) + self.assertIn({"Host": "10.0.0.1", "User": user2}, ret) + self.assertIn({"Host": "10.0.0.2", "User": user2}, ret) + self.assertIn({"Host": "%", "User": user3}, ret) + self.assertIn({"Host": "localhost", "User": user3}, ret) + self.assertIn({"Host": "%", "User": user4_utf8}, ret) + self.assertIn({"Host": "localhost", "User": user5_utf8}, ret) + self.assertIn({"Host": "10.0.0.1", "User": user6_utf8}, ret) # And finally, test connections on MySQL with theses users ret = self.run_function( - 'mysql.query', - database='information_schema', - query='SELECT 1', + "mysql.query", + database="information_schema", + query="SELECT 1", connection_user=user1, - connection_pass='pwd`\'"1b', - connection_host='localhost' + connection_pass="pwd`'\"1b", + connection_host="localhost", ) - if not isinstance(ret, dict) or 'results' not in ret: + if not isinstance(ret, dict) or "results" not in ret: raise AssertionError( - ('Unexpected result while testing connection' - ' with user \'{0}\': {1}').format( - user1, - repr(ret) - ) + ( + "Unexpected result while testing connection" " with user '{0}': {1}" + ).format(user1, repr(ret)) ) - self.assertEqual([['1']], ret['results']) + self.assertEqual([["1"]], ret["results"]) # FIXME: still failing, but works by hand... # mysql --user="user \"2'標" --password="user \"2'標b" information_schema # Seems to be a python-mysql library problem with user names containing # utf8 characters # @see https://github.com/farcepest/MySQLdb1/issues/40 - #import urllib - #ret = self.run_function( + # import urllib + # ret = self.run_function( # 'mysql.query', # database='information_schema', # query='SELECT 1', @@ -1123,8 +1174,8 @@ class MysqlModuleUserTest(ModuleCase, SaltReturnAssertsMixin): # connection_host='localhost', # connection_charset='utf8', # saltenv={"LC_ALL": "en_US.utf8"} - #) - #if not isinstance(ret, dict) or 'results' not in ret: + # ) + # if not isinstance(ret, dict) or 'results' not in ret: # raise AssertionError( # ('Unexpected result while testing connection' # ' with user \'{0}\': {1}').format( @@ -1132,26 +1183,24 @@ class MysqlModuleUserTest(ModuleCase, SaltReturnAssertsMixin): # repr(ret) # ) # ) - #self.assertEqual([['1']], ret['results']) + # self.assertEqual([['1']], ret['results']) ret = self.run_function( - 'mysql.query', - database='information_schema', - query='SELECT 1', + "mysql.query", + database="information_schema", + query="SELECT 1", connection_user=user3, - connection_pass='', - connection_host='localhost', + connection_pass="", + connection_host="localhost", ) - if not isinstance(ret, dict) or 'results' not in ret: + if not isinstance(ret, dict) or "results" not in ret: raise AssertionError( - ('Unexpected result while testing connection' - ' with user \'{0}\': {1}').format( - user3, - repr(ret) - ) + ( + "Unexpected result while testing connection" " with user '{0}': {1}" + ).format(user3, repr(ret)) ) - self.assertEqual([['1']], ret['results']) + self.assertEqual([["1"]], ret["results"]) # FIXME: Failing - #ret = self.run_function( + # ret = self.run_function( # 'mysql.query', # database='information_schema', # query='SELECT 1', @@ -1160,8 +1209,8 @@ class MysqlModuleUserTest(ModuleCase, SaltReturnAssertsMixin): # connection_host='localhost', # connection_charset='utf8', # saltenv={"LC_ALL": "en_US.utf8"} - #) - #if not isinstance(ret, dict) or 'results' not in ret: + # ) + # if not isinstance(ret, dict) or 'results' not in ret: # raise AssertionError( # ('Unexpected result while testing connection' # ' with user \'{0}\': {1}').format( @@ -1169,288 +1218,278 @@ class MysqlModuleUserTest(ModuleCase, SaltReturnAssertsMixin): # repr(ret) # ) # ) - #self.assertEqual([['1']], ret['results']) + # self.assertEqual([['1']], ret['results']) ret = self.run_function( - 'mysql.query', - database='information_schema', - query='SELECT 1', + "mysql.query", + database="information_schema", + query="SELECT 1", connection_user=user5_utf8, connection_pass=user5_pwd, - connection_host='localhost', - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_host="localhost", + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - if not isinstance(ret, dict) or 'results' not in ret: + if not isinstance(ret, dict) or "results" not in ret: raise AssertionError( - ('Unexpected result while testing connection' - ' with user \'{0}\': {1}').format( - user5_utf8, - repr(ret) - ) + ( + "Unexpected result while testing connection" " with user '{0}': {1}" + ).format(user5_utf8, repr(ret)) ) - self.assertEqual([['1']], ret['results']) + self.assertEqual([["1"]], ret["results"]) # Teardown by deleting with user_remove - self._chk_remove_user(user=user2, - host='10.0.0.1', - connection_user=self.user, - connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + self._chk_remove_user( + user=user2, + host="10.0.0.1", + connection_user=self.user, + connection_pass=self.password, + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self._chk_remove_user(user=user2, - host='10.0.0.2', - connection_user=self.user, - connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + self._chk_remove_user( + user=user2, + host="10.0.0.2", + connection_user=self.user, + connection_pass=self.password, + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self._chk_remove_user(user=user2, - host='localhost', - connection_user=self.user, - connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + self._chk_remove_user( + user=user2, + host="localhost", + connection_user=self.user, + connection_pass=self.password, + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self._chk_remove_user(user=user3, - host='%', - connection_user=self.user, - connection_pass=self.password, + self._chk_remove_user( + user=user3, + host="%", + connection_user=self.user, + connection_pass=self.password, ) - self._chk_remove_user(user=user3, - host='localhost', - connection_user=self.user, - connection_pass=self.password, + self._chk_remove_user( + user=user3, + host="localhost", + connection_user=self.user, + connection_pass=self.password, ) - self._chk_remove_user(user=user4, - host='%', - connection_user=self.user, - connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + self._chk_remove_user( + user=user4, + host="%", + connection_user=self.user, + connection_pass=self.password, + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self._chk_remove_user(user=user6, - host='10.0.0.1', - connection_user=self.user, - connection_pass=self.password, + self._chk_remove_user( + user=user6, + host="10.0.0.1", + connection_user=self.user, + connection_pass=self.password, ) - self._chk_remove_user(user=user1, - host='localhost', - connection_user=self.user, - connection_pass=self.password, + self._chk_remove_user( + user=user1, + host="localhost", + connection_user=self.user, + connection_pass=self.password, ) - self._chk_remove_user(user=user5, - host='localhost', - connection_user=self.user, - connection_pass=self.password, + self._chk_remove_user( + user=user5, + host="localhost", + connection_user=self.user, + connection_pass=self.password, ) # Final verification of the cleanup ret = self.run_function( - 'mysql.user_list', + "mysql.user_list", connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - self.assertNotIn({'Host': 'localhost', 'User': user1}, ret) - self.assertNotIn({'Host': 'localhost', 'User': user2}, ret) - self.assertNotIn({'Host': '10.0.0.1', 'User': user2}, ret) - self.assertNotIn({'Host': '10.0.0.2', 'User': user2}, ret) - self.assertNotIn({'Host': '%', 'User': user3}, ret) - self.assertNotIn({'Host': 'localhost', 'User': user3}, ret) - self.assertNotIn({'Host': '%', 'User': user4_utf8}, ret) - self.assertNotIn({'Host': 'localhost', 'User': user5_utf8}, ret) - self.assertNotIn({'Host': '10.0.0.1', 'User': user6_utf8}, ret) + self.assertNotIn({"Host": "localhost", "User": user1}, ret) + self.assertNotIn({"Host": "localhost", "User": user2}, ret) + self.assertNotIn({"Host": "10.0.0.1", "User": user2}, ret) + self.assertNotIn({"Host": "10.0.0.2", "User": user2}, ret) + self.assertNotIn({"Host": "%", "User": user3}, ret) + self.assertNotIn({"Host": "localhost", "User": user3}, ret) + self.assertNotIn({"Host": "%", "User": user4_utf8}, ret) + self.assertNotIn({"Host": "localhost", "User": user5_utf8}, ret) + self.assertNotIn({"Host": "10.0.0.1", "User": user6_utf8}, ret) @skipIf( NO_MYSQL, - 'Please install MySQL bindings and a MySQL Server before running' - 'MySQL integration tests.' + "Please install MySQL bindings and a MySQL Server before running" + "MySQL integration tests.", ) +@pytest.mark.windows_whitelisted class MysqlModuleUserGrantTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ User Creation and connection tests - ''' + """ - user = 'root' - password = 'poney' + user = "root" + password = "poney" # yep, theses are valid MySQL db names # very special chars are _ % and . - testdb1 = 'tes.t\'"saltdb' - testdb2 = 't_st `(:=salt%b)' - testdb3 = 'test `(:=salteeb)' - test_file_query_db = 'test_query' - table1 = 'foo' - table2 = "foo `\'%_bar" + testdb1 = "tes.t'\"saltdb" + testdb2 = "t_st `(:=salt%b)" + testdb3 = "test `(:=salteeb)" + test_file_query_db = "test_query" + table1 = "foo" + table2 = "foo `'%_bar" users = { - 'user1': { - 'name': 'foo', - 'pwd': 'bar', - }, - 'user2': { - 'name': 'user ";--,?:&/\\', - 'pwd': '";--(),?:@=&/\\', - }, + "user1": {"name": "foo", "pwd": "bar"}, + "user2": {"name": 'user ";--,?:&/\\', "pwd": '";--(),?:@=&/\\'}, # this is : passwd 標標 - 'user3': { - 'name': 'user( @ )=foobar', - 'pwd': '\xe6\xa8\x99\xe6\xa8\x99', - }, + "user3": {"name": "user( @ )=foobar", "pwd": "\xe6\xa8\x99\xe6\xa8\x99"}, # this is : user/password containing 標標 - 'user4': { - 'name': 'user \xe6\xa8\x99', - 'pwd': '\xe6\xa8\x99\xe6\xa8\x99', - }, + "user4": {"name": "user \xe6\xa8\x99", "pwd": "\xe6\xa8\x99\xe6\xa8\x99"}, } @destructiveTest def setUp(self): - ''' + """ Test presence of MySQL server, enforce a root password, create users - ''' + """ super(MysqlModuleUserGrantTest, self).setUp() NO_MYSQL_SERVER = True # now ensure we know the mysql root password # one of theses two at least should work ret1 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' flush-privileges password "' + + self.password + + '"', ) ret2 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' --password="' - + self.password - + '" flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' --password="' + + self.password + + '" flush-privileges password "' + + self.password + + '"', ) key, value = ret2.popitem() - if value['result']: + if value["result"]: NO_MYSQL_SERVER = False else: - self.skipTest('No MySQL Server running, or no root access on it.') + self.skipTest("No MySQL Server running, or no root access on it.") # Create some users and a test db for user, userdef in six.iteritems(self.users): - self._userCreation(uname=userdef['name'], password=userdef['pwd']) + self._userCreation(uname=userdef["name"], password=userdef["pwd"]) self.run_function( - 'mysql.db_create', + "mysql.db_create", name=self.testdb1, connection_user=self.user, connection_pass=self.password, ) self.run_function( - 'mysql.db_create', + "mysql.db_create", name=self.testdb2, connection_user=self.user, connection_pass=self.password, ) - create_query = ('CREATE TABLE {tblname} (' - ' id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,' - ' data VARCHAR(100)) ENGINE={engine};'.format( - tblname=mysqlmod.quote_identifier(self.table1), - engine='MYISAM', - )) - log.info('Adding table \'%s\'', self.table1) - self.run_function( - 'mysql.query', - database=self.testdb2, - query=create_query, - connection_user=self.user, - connection_pass=self.password + create_query = ( + "CREATE TABLE {tblname} (" + " id INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + " data VARCHAR(100)) ENGINE={engine};".format( + tblname=mysqlmod.quote_identifier(self.table1), engine="MYISAM", + ) ) - create_query = ('CREATE TABLE {tblname} (' - ' id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,' - ' data VARCHAR(100)) ENGINE={engine};'.format( - tblname=mysqlmod.quote_identifier(self.table2), - engine='MYISAM', - )) - log.info('Adding table \'%s\'', self.table2) + log.info("Adding table '%s'", self.table1) self.run_function( - 'mysql.query', + "mysql.query", database=self.testdb2, query=create_query, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, + ) + create_query = ( + "CREATE TABLE {tblname} (" + " id INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + " data VARCHAR(100)) ENGINE={engine};".format( + tblname=mysqlmod.quote_identifier(self.table2), engine="MYISAM", + ) + ) + log.info("Adding table '%s'", self.table2) + self.run_function( + "mysql.query", + database=self.testdb2, + query=create_query, + connection_user=self.user, + connection_pass=self.password, ) @destructiveTest def tearDown(self): - ''' + """ Removes created users and db - ''' + """ for user, userdef in six.iteritems(self.users): - self._userRemoval(uname=userdef['name'], password=userdef['pwd']) + self._userRemoval(uname=userdef["name"], password=userdef["pwd"]) self.run_function( - 'mysql.db_remove', + "mysql.db_remove", name=self.testdb1, connection_user=self.user, connection_pass=self.password, ) self.run_function( - 'mysql.db_remove', + "mysql.db_remove", name=self.testdb2, connection_user=self.user, connection_pass=self.password, ) self.run_function( - 'mysql.db_remove', + "mysql.db_remove", name=self.test_file_query_db, connection_user=self.user, connection_pass=self.password, ) - def _userCreation(self, - uname, - password=None): - ''' + def _userCreation(self, uname, password=None): + """ Create a test user - ''' + """ self.run_function( - 'mysql.user_create', + "mysql.user_create", user=uname, - host='localhost', + host="localhost", password=password, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - def _userRemoval(self, - uname, - password=None): - ''' + def _userRemoval(self, uname, password=None): + """ Removes a test user - ''' + """ self.run_function( - 'mysql.user_remove', + "mysql.user_remove", user=uname, - host='localhost', + host="localhost", connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - def _addGrantRoutine(self, - grant, - user, - db, - grant_option=False, - escape=True, - **kwargs): - ''' + def _addGrantRoutine( + self, grant, user, db, grant_option=False, escape=True, **kwargs + ): + """ Perform some tests around creation of the given grants - ''' + """ ret = self.run_function( - 'mysql.grant_add', + "mysql.grant_add", grant=grant, database=db, user=user, @@ -1458,14 +1497,16 @@ class MysqlModuleUserGrantTest(ModuleCase, SaltReturnAssertsMixin): escape=escape, **kwargs ) - self.assertEqual(True, ret, ('Calling grant_add on' - ' user \'{0}\' and grants \'{1}\' did not return True: {2}').format( - user, - grant, - repr(ret) - )) + self.assertEqual( + True, + ret, + ( + "Calling grant_add on" + " user '{0}' and grants '{1}' did not return True: {2}" + ).format(user, grant, repr(ret)), + ) ret = self.run_function( - 'mysql.grant_exists', + "mysql.grant_exists", grant=grant, database=db, user=user, @@ -1473,284 +1514,307 @@ class MysqlModuleUserGrantTest(ModuleCase, SaltReturnAssertsMixin): escape=escape, **kwargs ) - self.assertEqual(True, ret, ('Calling grant_exists on' - ' user \'{0}\' and grants \'{1}\' did not return True: {2}').format( - user, - grant, - repr(ret) - )) + self.assertEqual( + True, + ret, + ( + "Calling grant_exists on" + " user '{0}' and grants '{1}' did not return True: {2}" + ).format(user, grant, repr(ret)), + ) @destructiveTest def testGrants(self): - ''' + """ Test user grant methods - ''' + """ self._addGrantRoutine( - grant='SELECT, INSERT,UPDATE, CREATE', - user=self.users['user1']['name'], - db=self.testdb1 + '.*', + grant="SELECT, INSERT,UPDATE, CREATE", + user=self.users["user1"]["name"], + db=self.testdb1 + ".*", grant_option=True, escape=True, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) self._addGrantRoutine( - grant='INSERT, SELECT', - user=self.users['user1']['name'], - db=self.testdb2 + '.' + self.table1, + grant="INSERT, SELECT", + user=self.users["user1"]["name"], + db=self.testdb2 + "." + self.table1, grant_option=True, escape=True, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) self._addGrantRoutine( - grant=' SELECT, UPDATE,DELETE, CREATE TEMPORARY TABLES', - user=self.users['user2']['name'], - db=self.testdb1 + '.*', + grant=" SELECT, UPDATE,DELETE, CREATE TEMPORARY TABLES", + user=self.users["user2"]["name"], + db=self.testdb1 + ".*", grant_option=True, escape=True, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) self._addGrantRoutine( - grant='select, ALTER,CREATE TEMPORARY TABLES, EXECUTE ', - user=self.users['user3']['name'], - db=self.testdb1 + '.*', + grant="select, ALTER,CREATE TEMPORARY TABLES, EXECUTE ", + user=self.users["user3"]["name"], + db=self.testdb1 + ".*", grant_option=True, escape=True, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) self._addGrantRoutine( - grant='SELECT, INSERT', - user=self.users['user4']['name'], - db=self.testdb2 + '.' + self.table2, + grant="SELECT, INSERT", + user=self.users["user4"]["name"], + db=self.testdb2 + "." + self.table2, grant_option=False, escape=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) self._addGrantRoutine( - grant='CREATE', - user=self.users['user4']['name'], - db=self.testdb2 + '.*', + grant="CREATE", + user=self.users["user4"]["name"], + db=self.testdb2 + ".*", grant_option=False, escape=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) self._addGrantRoutine( - grant='SELECT, INSERT', - user=self.users['user4']['name'], - db=self.testdb2 + '.' + self.table1, + grant="SELECT, INSERT", + user=self.users["user4"]["name"], + db=self.testdb2 + "." + self.table1, grant_option=False, escape=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) # '' is valid for anonymous users self._addGrantRoutine( - grant='DELETE', - user='', - db=self.testdb3 + '.*', + grant="DELETE", + user="", + db=self.testdb3 + ".*", grant_option=False, escape=True, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, ) # Check result for users ret = self.run_function( - 'mysql.user_grants', - user=self.users['user1']['name'], - host='localhost', - connection_user=self.user, - connection_pass=self.password - ) - self.assertEqual(ret, [ - "GRANT USAGE ON *.* TO 'foo'@'localhost'", - ('GRANT SELECT, INSERT, UPDATE, CREATE ON ' - '`tes.t\'"saltdb`.* TO \'foo\'@\'localhost\' WITH GRANT OPTION'), - ("GRANT SELECT, INSERT ON `t_st ``(:=salt%b)`.`foo`" - " TO 'foo'@'localhost' WITH GRANT OPTION") - ]) - - ret = self.run_function( - 'mysql.user_grants', - user=self.users['user2']['name'], - host='localhost', - connection_user=self.user, - connection_pass=self.password - ) - self.assertEqual(ret, [ - 'GRANT USAGE ON *.* TO \'user ";--,?:&/\\\'@\'localhost\'', - ('GRANT SELECT, UPDATE, DELETE, CREATE TEMPORARY TABLES ON `tes.t\'' - '"saltdb`.* TO \'user ";--,?:&/\\\'@\'localhost\'' - ' WITH GRANT OPTION') - ]) - - ret = self.run_function( - 'mysql.user_grants', - user=self.users['user3']['name'], - host='localhost', - connection_user=self.user, - connection_pass=self.password - ) - self.assertEqual(ret, [ - "GRANT USAGE ON *.* TO 'user( @ )=foobar'@'localhost'", - ('GRANT SELECT, ALTER, CREATE TEMPORARY TABLES, EXECUTE ON ' - '`tes.t\'"saltdb`.* TO \'user( @ )=foobar\'@\'localhost\' ' - 'WITH GRANT OPTION') - ]) - - ret = self.run_function( - 'mysql.user_grants', - user=self.users['user4']['name'], - host='localhost', + "mysql.user_grants", + user=self.users["user1"]["name"], + host="localhost", connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' ) - self.assertEqual(ret, [ - "GRANT USAGE ON *.* TO 'user \xe6\xa8\x99'@'localhost'", - (r"GRANT CREATE ON `t\_st ``(:=salt\%b)`.* TO " - "'user \xe6\xa8\x99'@'localhost'"), - ("GRANT SELECT, INSERT ON `t_st ``(:=salt%b)`.`foo ``'%_bar` TO " - "'user \xe6\xa8\x99'@'localhost'"), - ("GRANT SELECT, INSERT ON `t_st ``(:=salt%b)`.`foo` TO " - "'user \xe6\xa8\x99'@'localhost'"), - ]) + self.assertEqual( + ret, + [ + "GRANT USAGE ON *.* TO 'foo'@'localhost'", + ( + "GRANT SELECT, INSERT, UPDATE, CREATE ON " + "`tes.t'\"saltdb`.* TO 'foo'@'localhost' WITH GRANT OPTION" + ), + ( + "GRANT SELECT, INSERT ON `t_st ``(:=salt%b)`.`foo`" + " TO 'foo'@'localhost' WITH GRANT OPTION" + ), + ], + ) ret = self.run_function( - 'mysql.user_grants', - user='', - host='localhost', + "mysql.user_grants", + user=self.users["user2"]["name"], + host="localhost", connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, + ) + self.assertEqual( + ret, + [ + "GRANT USAGE ON *.* TO 'user \";--,?:&/\\'@'localhost'", + ( + "GRANT SELECT, UPDATE, DELETE, CREATE TEMPORARY TABLES ON `tes.t'" + "\"saltdb`.* TO 'user \";--,?:&/\\'@'localhost'" + " WITH GRANT OPTION" + ), + ], + ) + + ret = self.run_function( + "mysql.user_grants", + user=self.users["user3"]["name"], + host="localhost", + connection_user=self.user, + connection_pass=self.password, + ) + self.assertEqual( + ret, + [ + "GRANT USAGE ON *.* TO 'user( @ )=foobar'@'localhost'", + ( + "GRANT SELECT, ALTER, CREATE TEMPORARY TABLES, EXECUTE ON " + "`tes.t'\"saltdb`.* TO 'user( @ )=foobar'@'localhost' " + "WITH GRANT OPTION" + ), + ], + ) + + ret = self.run_function( + "mysql.user_grants", + user=self.users["user4"]["name"], + host="localhost", + connection_user=self.user, + connection_pass=self.password, + connection_charset="utf8", + ) + self.assertEqual( + ret, + [ + "GRANT USAGE ON *.* TO 'user \xe6\xa8\x99'@'localhost'", + ( + r"GRANT CREATE ON `t\_st ``(:=salt\%b)`.* TO " + "'user \xe6\xa8\x99'@'localhost'" + ), + ( + "GRANT SELECT, INSERT ON `t_st ``(:=salt%b)`.`foo ``'%_bar` TO " + "'user \xe6\xa8\x99'@'localhost'" + ), + ( + "GRANT SELECT, INSERT ON `t_st ``(:=salt%b)`.`foo` TO " + "'user \xe6\xa8\x99'@'localhost'" + ), + ], + ) + + ret = self.run_function( + "mysql.user_grants", + user="", + host="localhost", + connection_user=self.user, + connection_pass=self.password, + ) + self.assertEqual( + ret, + [ + "GRANT USAGE ON *.* TO ''@'localhost'", + "GRANT DELETE ON `test ``(:=salteeb)`.* TO ''@'localhost'", + ], ) - self.assertEqual(ret, [ - "GRANT USAGE ON *.* TO ''@'localhost'", - "GRANT DELETE ON `test ``(:=salteeb)`.* TO ''@'localhost'" - ]) @skipIf( NO_MYSQL, - 'Please install MySQL bindings and a MySQL Server before running' - 'MySQL integration tests.' + "Please install MySQL bindings and a MySQL Server before running" + "MySQL integration tests.", ) +@pytest.mark.windows_whitelisted class MysqlModuleFileQueryTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Test file query module - ''' + """ - user = 'root' - password = 'poney' - testdb = 'test_file_query' + user = "root" + password = "poney" + testdb = "test_file_query" @destructiveTest def setUp(self): - ''' + """ Test presence of MySQL server, enforce a root password, create users - ''' + """ super(MysqlModuleFileQueryTest, self).setUp() NO_MYSQL_SERVER = True # now ensure we know the mysql root password # one of theses two at least should work ret1 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' flush-privileges password "' + + self.password + + '"', ) ret2 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' --password="' - + self.password - + '" flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' --password="' + + self.password + + '" flush-privileges password "' + + self.password + + '"', ) key, value = ret2.popitem() - if value['result']: + if value["result"]: NO_MYSQL_SERVER = False else: - self.skipTest('No MySQL Server running, or no root access on it.') + self.skipTest("No MySQL Server running, or no root access on it.") # Create some users and a test db self.run_function( - 'mysql.db_create', + "mysql.db_create", name=self.testdb, connection_user=self.user, connection_pass=self.password, - connection_db='mysql', + connection_db="mysql", ) @destructiveTest def tearDown(self): - ''' + """ Removes created users and db - ''' + """ self.run_function( - 'mysql.db_remove', + "mysql.db_remove", name=self.testdb, connection_user=self.user, connection_pass=self.password, - connection_db='mysql', - ) + connection_db="mysql", + ) @destructiveTest def test_update_file_query(self): - ''' + """ Test query without any output - ''' + """ ret = self.run_function( - 'mysql.file_query', - database=self.testdb, - file_name='salt://mysql/update_query.sql', - character_set='utf8', - collate='utf8_general_ci', - connection_user=self.user, - connection_pass=self.password + "mysql.file_query", + database=self.testdb, + file_name="salt://mysql/update_query.sql", + character_set="utf8", + collate="utf8_general_ci", + connection_user=self.user, + connection_pass=self.password, ) - self.assertTrue('query time' in ret) - ret.pop('query time') - self.assertEqual(ret, {'rows affected': 2}) + self.assertTrue("query time" in ret) + ret.pop("query time") + self.assertEqual(ret, {"rows affected": 2}) @destructiveTest def test_select_file_query(self): - ''' + """ Test query with table output - ''' + """ ret = self.run_function( - 'mysql.file_query', - database=self.testdb, - file_name='salt://mysql/select_query.sql', - character_set='utf8', - collate='utf8_general_ci', - connection_user=self.user, - connection_pass=self.password + "mysql.file_query", + database=self.testdb, + file_name="salt://mysql/select_query.sql", + character_set="utf8", + collate="utf8_general_ci", + connection_user=self.user, + connection_pass=self.password, ) expected = { - 'rows affected': 5, - 'rows returned': 4, - 'results': [ - [ - ['2'], - ['3'], - ['4'], - ['5'] - ] - ], - 'columns': [ - ['a'] - ], + "rows affected": 5, + "rows returned": 4, + "results": [[["2"], ["3"], ["4"], ["5"]]], + "columns": [["a"]], } - self.assertTrue('query time' in ret) - ret.pop('query time') + self.assertTrue("query time" in ret) + ret.pop("query time") self.assertEqual(ret, expected) diff --git a/tests/integration/modules/test_network.py b/tests/integration/modules/test_network.py index 09176b317a3..309fcde6867 100644 --- a/tests/integration/modules/test_network.py +++ b/tests/integration/modules/test_network.py @@ -1,60 +1,62 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import -# Import Salt Testing libs +import pytest +import salt.utils.path +import salt.utils.platform from tests.support.case import ModuleCase from tests.support.unit import skipIf -# Import Salt Libs -import salt.utils.path -import salt.utils.platform - -URL = 'google-public-dns-a.google.com' +URL = "google-public-dns-a.google.com" +@pytest.mark.windows_whitelisted class NetworkTest(ModuleCase): - ''' + """ Validate network module - ''' + """ + def test_network_ping(self): - ''' + """ network.ping - ''' - ret = self.run_function('network.ping', [URL]) - exp_out = ['ping', URL, 'ms', 'time'] + """ + ret = self.run_function("network.ping", [URL]) + exp_out = ["ping", URL, "ms", "time"] for out in exp_out: self.assertIn(out, ret.lower()) - @skipIf(salt.utils.platform.is_darwin(), 'not supported on macosx') + @skipIf(salt.utils.platform.is_darwin(), "not supported on macosx") def test_network_netstat(self): - ''' + """ network.netstat - ''' - ret = self.run_function('network.netstat') - exp_out = ['proto', 'local-address'] + """ + ret = self.run_function("network.netstat") + exp_out = ["proto", "local-address"] for val in ret: for out in exp_out: self.assertIn(out, val) def test_network_traceroute(self): - ''' + """ network.traceroute - ''' - if not salt.utils.path.which('traceroute') and not salt.utils.platform.is_windows(): - self.skipTest('traceroute not installed') - ret = self.run_function('network.traceroute', [URL]) - exp_out = ['hostname', 'ip'] + """ + if ( + not salt.utils.path.which("traceroute") + and not salt.utils.platform.is_windows() + ): + self.skipTest("traceroute not installed") + ret = self.run_function("network.traceroute", [URL]) + exp_out = ["hostname", "ip"] for out in exp_out: self.assertIn(out, exp_out) - @skipIf(not salt.utils.platform.is_windows(), 'windows only test') + @skipIf(not salt.utils.platform.is_windows(), "windows only test") def test_network_nslookup(self): - ''' + """ network.nslookup - ''' - ret = self.run_function('network.nslookup', [URL]) - exp_out = ['Server', 'Address'] + """ + ret = self.run_function("network.nslookup", [URL]) + exp_out = ["Server", "Address"] for out in exp_out: self.assertIn(out, exp_out) diff --git a/tests/integration/modules/test_nilrt_ip.py b/tests/integration/modules/test_nilrt_ip.py index 69ba675d22c..4014550931b 100644 --- a/tests/integration/modules/test_nilrt_ip.py +++ b/tests/integration/modules/test_nilrt_ip.py @@ -1,111 +1,137 @@ # -*- coding: utf-8 -*- -''' +""" integration tests for nilirt_ip -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import time -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, skip_if_not_root +import time # Import Salt libs import salt.utils.platform +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root +from tests.support.unit import skipIf + @skip_if_not_root -@skipIf(not salt.utils.platform.is_linux(), 'These tests can only be run on linux') +@skipIf(not salt.utils.platform.is_linux(), "These tests can only be run on linux") class Nilrt_ipModuleTest(ModuleCase): - ''' + """ Validate the nilrt_ip module - ''' + """ @classmethod def setUpClass(cls): cls.initialState = {} def setUp(self): - ''' + """ Get current settings - ''' + """ # save files from var/lib/connman* - os_grain = self.run_function('grains.item', ['os_family']) - if os_grain['os_family'] != 'NILinuxRT': - self.skipTest('Tests applicable only to NILinuxRT') + os_grain = self.run_function("grains.item", ["os_family"]) + if os_grain["os_family"] != "NILinuxRT": + self.skipTest("Tests applicable only to NILinuxRT") super(Nilrt_ipModuleTest, self).setUp() - self.run_function('file.copy', ['/var/lib/connman', '/tmp/connman', 'recurse=True', 'remove_existing=True']) + self.run_function( + "file.copy", + [ + "/var/lib/connman", + "/tmp/connman", + "recurse=True", + "remove_existing=True", + ], + ) def tearDown(self): - ''' + """ Reset to original settings - ''' + """ # restore files - self.run_function('file.copy', ['/tmp/connman', '/var/lib/connman', 'recurse=True', 'remove_existing=True']) + self.run_function( + "file.copy", + [ + "/tmp/connman", + "/var/lib/connman", + "recurse=True", + "remove_existing=True", + ], + ) # restart connman - self.run_function('service.restart', ['connman']) + self.run_function("service.restart", ["connman"]) time.sleep(10) # wait 10 seconds for connman to be fully loaded interfaces = self.__interfaces() for interface in interfaces: - self.run_function('ip.up', [interface]) + self.run_function("ip.up", [interface]) def __connected(self, interface): - return interface['up'] + return interface["up"] def __interfaces(self): interfaceList = [] - for iface in self.run_function('ip.get_interfaces_details')['interfaces']: - interfaceList.append(iface['connectionid']) + for iface in self.run_function("ip.get_interfaces_details")["interfaces"]: + interfaceList.append(iface["connectionid"]) return interfaceList @destructiveTest def test_down(self): interfaces = self.__interfaces() for interface in interfaces: - result = self.run_function('ip.down', [interface]) + result = self.run_function("ip.down", [interface]) self.assertTrue(result) - info = self.run_function('ip.get_interfaces_details') - for interface in info['interfaces']: + info = self.run_function("ip.get_interfaces_details") + for interface in info["interfaces"]: self.assertFalse(self.__connected(interface)) @destructiveTest def test_up(self): interfaces = self.__interfaces() - #first down all interfaces + # first down all interfaces for interface in interfaces: - self.run_function('ip.down', [interface]) + self.run_function("ip.down", [interface]) # up interfaces for interface in interfaces: - result = self.run_function('ip.up', [interface]) + result = self.run_function("ip.up", [interface]) self.assertTrue(result) - info = self.run_function('ip.get_interfaces_details') - for interface in info['interfaces']: + info = self.run_function("ip.get_interfaces_details") + for interface in info["interfaces"]: self.assertTrue(self.__connected(interface)) @destructiveTest def test_set_dhcp_linklocal_all(self): interfaces = self.__interfaces() for interface in interfaces: - result = self.run_function('ip.set_dhcp_linklocal_all', [interface]) + result = self.run_function("ip.set_dhcp_linklocal_all", [interface]) self.assertTrue(result) - info = self.run_function('ip.get_interfaces_details') - for interface in info['interfaces']: - self.assertEqual(interface['ipv4']['requestmode'], 'dhcp_linklocal') + info = self.run_function("ip.get_interfaces_details") + for interface in info["interfaces"]: + self.assertEqual(interface["ipv4"]["requestmode"], "dhcp_linklocal") @destructiveTest def test_static_all(self): interfaces = self.__interfaces() for interface in interfaces: - result = self.run_function('ip.set_static_all', [interface, '192.168.10.4', '255.255.255.0', '192.168.10.1', '8.8.4.4 8.8.8.8']) + result = self.run_function( + "ip.set_static_all", + [ + interface, + "192.168.10.4", + "255.255.255.0", + "192.168.10.1", + "8.8.4.4 8.8.8.8", + ], + ) self.assertTrue(result) - info = self.run_function('ip.get_interfaces_details') - for interface in info['interfaces']: - self.assertIn('8.8.4.4', interface['ipv4']['dns']) - self.assertIn('8.8.8.8', interface['ipv4']['dns']) - self.assertEqual(interface['ipv4']['requestmode'], 'static') - self.assertEqual(interface['ipv4']['address'], '192.168.10.4') - self.assertEqual(interface['ipv4']['netmask'], '255.255.255.0') - self.assertEqual(interface['ipv4']['gateway'], '192.168.10.1') + info = self.run_function("ip.get_interfaces_details") + for interface in info["interfaces"]: + self.assertIn("8.8.4.4", interface["ipv4"]["dns"]) + self.assertIn("8.8.8.8", interface["ipv4"]["dns"]) + self.assertEqual(interface["ipv4"]["requestmode"], "static") + self.assertEqual(interface["ipv4"]["address"], "192.168.10.4") + self.assertEqual(interface["ipv4"]["netmask"], "255.255.255.0") + self.assertEqual(interface["ipv4"]["gateway"], "192.168.10.1") diff --git a/tests/integration/modules/test_pillar.py b/tests/integration/modules/test_pillar.py index f23d3d519b3..2fe981f2dbe 100644 --- a/tests/integration/modules/test_pillar.py +++ b/tests/integration/modules/test_pillar.py @@ -1,79 +1,76 @@ # -*- coding: utf-8 -*- -# Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS +import pytest from tests.support.case import ModuleCase +from tests.support.runtests import RUNTIME_VARS +@pytest.mark.windows_whitelisted class PillarModuleTest(ModuleCase): - ''' + """ Validate the pillar module - ''' + """ + def test_data(self): - ''' + """ pillar.data - ''' - grains = self.run_function('grains.items') - pillar = self.run_function('pillar.data') - self.assertEqual(pillar['os'], grains['os']) - self.assertEqual(pillar['monty'], 'python') - if grains['os'] == 'Fedora': - self.assertEqual(pillar['class'], 'redhat') + """ + grains = self.run_function("grains.items") + pillar = self.run_function("pillar.data") + self.assertEqual(pillar["os"], grains["os"]) + self.assertEqual(pillar["monty"], "python") + if grains["os"] == "Fedora": + self.assertEqual(pillar["class"], "redhat") else: - self.assertEqual(pillar['class'], 'other') + self.assertEqual(pillar["class"], "other") def test_issue_5449_report_actual_file_roots_in_pillar(self): - ''' + """ pillar['master']['file_roots'] is overwritten by the master in order to use the fileclient interface to read the pillar files. We should restore the actual file_roots when we send the pillar back to the minion. - ''' + """ self.assertIn( RUNTIME_VARS.TMP_STATE_TREE, - self.run_function('pillar.data')['master']['file_roots']['base'] + self.run_function("pillar.data")["master"]["file_roots"]["base"], ) def test_ext_cmd_yaml(self): - ''' + """ pillar.data for ext_pillar cmd.yaml - ''' - self.assertEqual( - self.run_function('pillar.data')['ext_spam'], 'eggs' - ) + """ + self.assertEqual(self.run_function("pillar.data")["ext_spam"], "eggs") def test_issue_5951_actual_file_roots_in_opts(self): self.assertIn( RUNTIME_VARS.TMP_STATE_TREE, - self.run_function('pillar.data')['ext_pillar_opts']['file_roots']['base'] + self.run_function("pillar.data")["ext_pillar_opts"]["file_roots"]["base"], ) def test_pillar_items(self): - ''' + """ Test to ensure we get expected output from pillar.items - ''' - get_items = self.run_function('pillar.items') - self.assertDictContainsSubset({'monty': 'python'}, get_items) + """ + get_items = self.run_function("pillar.items") + self.assertDictContainsSubset({"monty": "python"}, get_items) self.assertDictContainsSubset( - {'knights': ['Lancelot', 'Galahad', 'Bedevere', 'Robin']}, - get_items) + {"knights": ["Lancelot", "Galahad", "Bedevere", "Robin"]}, get_items + ) def test_pillar_command_line(self): - ''' + """ Test to ensure when using pillar override on command line works - ''' + """ # test when pillar is overwriting previous pillar - overwrite = self.run_function('pillar.items', pillar={"monty": - "overwrite"}) - self.assertDictContainsSubset({'monty': 'overwrite'}, overwrite) + overwrite = self.run_function("pillar.items", pillar={"monty": "overwrite"}) + self.assertDictContainsSubset({"monty": "overwrite"}, overwrite) # test when using additional pillar - additional = self.run_function('pillar.items', pillar={"new": - "additional"}) + additional = self.run_function("pillar.items", pillar={"new": "additional"}) - self.assertDictContainsSubset({'new': 'additional'}, additional) + self.assertDictContainsSubset({"new": "additional"}, additional) diff --git a/tests/integration/modules/test_pip.py b/tests/integration/modules/test_pip.py index de0f307531f..4d4236bbbdf 100644 --- a/tests/integration/modules/test_pip.py +++ b/tests/integration/modules/test_pip.py @@ -1,53 +1,53 @@ # -*- coding: utf-8 -*- -''' +""" tests.integration.modules.pip ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os -import re -import sys import pprint +import re import shutil +import sys import tempfile -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import patched_environ - -# Import salt libs +import pytest import salt.utils.files import salt.utils.path import salt.utils.platform from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.case import ModuleCase +from tests.support.helpers import patched_environ +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf -@skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') +@skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, "virtualenv not installed" +) +@pytest.mark.windows_whitelisted class PipModuleTest(ModuleCase): - def setUp(self): super(PipModuleTest, self).setUp() self.venv_test_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Remove the venv test directory self.addCleanup(shutil.rmtree, self.venv_test_dir, ignore_errors=True) - self.venv_dir = os.path.join(self.venv_test_dir, 'venv') - self.pip_temp = os.path.join(self.venv_test_dir, '.pip-temp') + self.venv_dir = os.path.join(self.venv_test_dir, "venv") + self.pip_temp = os.path.join(self.venv_test_dir, ".pip-temp") if not os.path.isdir(self.pip_temp): os.makedirs(self.pip_temp) self.patched_environ = patched_environ( - PIP_SOURCE_DIR='', - PIP_BUILD_DIR='', - __cleanup__=[k for k in os.environ if k.startswith('PIP_')] + PIP_SOURCE_DIR="", + PIP_BUILD_DIR="", + __cleanup__=[k for k in os.environ if k.startswith("PIP_")], ) self.patched_environ.__enter__() self.addCleanup(self.patched_environ.__exit__) def _create_virtualenv(self, path): - ''' + """ The reason why the virtualenv creation is proxied by this function is mostly because under windows, we can't seem to properly create a virtualenv off of another virtualenv(we can on linux) and also because, we really don't want to @@ -55,58 +55,59 @@ class PipModuleTest(ModuleCase): from the original python. Also, one windows, we must also point to the virtualenv binary outside the existing virtualenv because it will fail otherwise - ''' + """ try: if salt.utils.platform.is_windows(): python = os.path.join(sys.real_prefix, os.path.basename(sys.executable)) else: python_binary_names = [ - 'python{}.{}'.format(*sys.version_info), - 'python{}'.format(*sys.version_info), - 'python' + "python{}.{}".format(*sys.version_info), + "python{}".format(*sys.version_info), + "python", ] for binary_name in python_binary_names: - python = os.path.join(sys.real_prefix, 'bin', binary_name) + python = os.path.join(sys.real_prefix, "bin", binary_name) if os.path.exists(python): break else: self.fail( - 'Couldn\'t find a python binary name under \'{}\' matching: {}'.format( - os.path.join(sys.real_prefix, 'bin'), - python_binary_names + "Couldn't find a python binary name under '{}' matching: {}".format( + os.path.join(sys.real_prefix, "bin"), python_binary_names ) ) # We're running off a virtualenv, and we don't want to create a virtualenv off of # a virtualenv - kwargs = {'python': python} + kwargs = {"python": python} except AttributeError: # We're running off of the system python kwargs = {} - self.run_function('virtualenv.create', [path], **kwargs) + self.run_function("virtualenv.create", [path], **kwargs) def _check_download_error(self, ret): - ''' + """ Checks to see if a download error looks transitory - ''' - return any(w in ret for w in ['URLError', 'Download error']) + """ + return any(w in ret for w in ["URLError", "Download error"]) - def pip_successful_install(self, target, expect=('irc3-plugins-test', 'pep8',)): - ''' + def pip_successful_install(self, target, expect=("irc3-plugins-test", "pep8",)): + """ isolate regex for extracting `successful install` message from pip - ''' + """ expect = set(expect) - expect_str = '|'.join(expect) + expect_str = "|".join(expect) success = re.search( - r'^.*Successfully installed\s([^\n]+)(?:Clean.*)?', - target, - re.M | re.S) + r"^.*Successfully installed\s([^\n]+)(?:Clean.*)?", target, re.M | re.S + ) - success_for = re.findall( - r'({0})(?:-(?:[\d\.-]))?'.format(expect_str), - success.groups()[0] - ) if success else [] + success_for = ( + re.findall( + r"({0})(?:-(?:[\d\.-]))?".format(expect_str), success.groups()[0] + ) + if success + else [] + ) return expect.issubset(set(success_for)) @@ -115,30 +116,30 @@ class PipModuleTest(ModuleCase): self._create_virtualenv(self.venv_dir) # Let's remove the pip binary - pip_bin = os.path.join(self.venv_dir, 'bin', 'pip') - site_dir = self.run_function('virtualenv.get_distribution_path', [self.venv_dir, 'pip']) + pip_bin = os.path.join(self.venv_dir, "bin", "pip") + site_dir = self.run_function( + "virtualenv.get_distribution_path", [self.venv_dir, "pip"] + ) if salt.utils.platform.is_windows(): - pip_bin = os.path.join(self.venv_dir, 'Scripts', 'pip.exe') - site_dir = os.path.join(self.venv_dir, 'lib', 'site-packages') + pip_bin = os.path.join(self.venv_dir, "Scripts", "pip.exe") + site_dir = os.path.join(self.venv_dir, "lib", "site-packages") if not os.path.isfile(pip_bin): - self.skipTest( - 'Failed to find the pip binary to the test virtualenv' - ) + self.skipTest("Failed to find the pip binary to the test virtualenv") os.remove(pip_bin) # Also remove the pip dir from site-packages # This is needed now that we're using python -m pip instead of the # pip binary directly. python -m pip will still work even if the # pip binary is missing - shutil.rmtree(os.path.join(site_dir, 'pip')) + shutil.rmtree(os.path.join(site_dir, "pip")) # Let's run a pip depending functions - for func in ('pip.freeze', 'pip.list'): + for func in ("pip.freeze", "pip.list"): ret = self.run_function(func, bin_env=self.venv_dir) self.assertIn( - 'Command required for \'{0}\' not found: ' - 'Could not find a `pip` binary'.format(func), - ret + "Command required for '{0}' not found: " + "Could not find a `pip` binary".format(func), + ret, ) def test_requirements_as_list_of_chains__cwd_set__absolute_file_path(self): @@ -146,40 +147,43 @@ class PipModuleTest(ModuleCase): # Create a requirements file that depends on another one. - req1_filename = os.path.join(self.venv_dir, 'requirements1.txt') - req1b_filename = os.path.join(self.venv_dir, 'requirements1b.txt') - req2_filename = os.path.join(self.venv_dir, 'requirements2.txt') - req2b_filename = os.path.join(self.venv_dir, 'requirements2b.txt') + req1_filename = os.path.join(self.venv_dir, "requirements1.txt") + req1b_filename = os.path.join(self.venv_dir, "requirements1b.txt") + req2_filename = os.path.join(self.venv_dir, "requirements2.txt") + req2b_filename = os.path.join(self.venv_dir, "requirements2b.txt") - with salt.utils.files.fopen(req1_filename, 'w') as f: - f.write('-r requirements1b.txt\n') - with salt.utils.files.fopen(req1b_filename, 'w') as f: - f.write('irc3-plugins-test\n') - with salt.utils.files.fopen(req2_filename, 'w') as f: - f.write('-r requirements2b.txt\n') - with salt.utils.files.fopen(req2b_filename, 'w') as f: - f.write('pep8\n') + with salt.utils.files.fopen(req1_filename, "w") as f: + f.write("-r requirements1b.txt\n") + with salt.utils.files.fopen(req1b_filename, "w") as f: + f.write("irc3-plugins-test\n") + with salt.utils.files.fopen(req2_filename, "w") as f: + f.write("-r requirements2b.txt\n") + with salt.utils.files.fopen(req2b_filename, "w") as f: + f.write("pep8\n") requirements_list = [req1_filename, req2_filename] ret = self.run_function( - 'pip.install', requirements=requirements_list, - bin_env=self.venv_dir, cwd=self.venv_dir + "pip.install", + requirements=requirements_list, + bin_env=self.venv_dir, + cwd=self.venv_dir, ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - self.assertEqual(ret['retcode'], 0) - found = self.pip_successful_install(ret['stdout']) + self.assertEqual(ret["retcode"], 0) + found = self.pip_successful_install(ret["stdout"]) self.assertTrue(found) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) @@ -188,74 +192,76 @@ class PipModuleTest(ModuleCase): # Create a requirements file that depends on another one. - req1_filename = os.path.join(self.venv_dir, 'requirements1.txt') - req1b_filename = os.path.join(self.venv_dir, 'requirements1b.txt') - req2_filename = os.path.join(self.venv_dir, 'requirements2.txt') - req2b_filename = os.path.join(self.venv_dir, 'requirements2b.txt') + req1_filename = os.path.join(self.venv_dir, "requirements1.txt") + req1b_filename = os.path.join(self.venv_dir, "requirements1b.txt") + req2_filename = os.path.join(self.venv_dir, "requirements2.txt") + req2b_filename = os.path.join(self.venv_dir, "requirements2b.txt") - with salt.utils.files.fopen(req1_filename, 'w') as f: - f.write('-r requirements1b.txt\n') - with salt.utils.files.fopen(req1b_filename, 'w') as f: - f.write('irc3-plugins-test\n') - with salt.utils.files.fopen(req2_filename, 'w') as f: - f.write('-r requirements2b.txt\n') - with salt.utils.files.fopen(req2b_filename, 'w') as f: - f.write('pep8\n') + with salt.utils.files.fopen(req1_filename, "w") as f: + f.write("-r requirements1b.txt\n") + with salt.utils.files.fopen(req1b_filename, "w") as f: + f.write("irc3-plugins-test\n") + with salt.utils.files.fopen(req2_filename, "w") as f: + f.write("-r requirements2b.txt\n") + with salt.utils.files.fopen(req2b_filename, "w") as f: + f.write("pep8\n") requirements_list = [req1_filename, req2_filename] ret = self.run_function( - 'pip.install', requirements=requirements_list, bin_env=self.venv_dir + "pip.install", requirements=requirements_list, bin_env=self.venv_dir ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - self.assertEqual(ret['retcode'], 0) - found = self.pip_successful_install(ret['stdout']) + self.assertEqual(ret["retcode"], 0) + found = self.pip_successful_install(ret["stdout"]) self.assertTrue(found) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) def test_requirements_as_list__absolute_file_path(self): self._create_virtualenv(self.venv_dir) - req1_filename = os.path.join(self.venv_dir, 'requirements.txt') - req2_filename = os.path.join(self.venv_dir, 'requirements2.txt') + req1_filename = os.path.join(self.venv_dir, "requirements.txt") + req2_filename = os.path.join(self.venv_dir, "requirements2.txt") - with salt.utils.files.fopen(req1_filename, 'w') as f: - f.write('irc3-plugins-test\n') - with salt.utils.files.fopen(req2_filename, 'w') as f: - f.write('pep8\n') + with salt.utils.files.fopen(req1_filename, "w") as f: + f.write("irc3-plugins-test\n") + with salt.utils.files.fopen(req2_filename, "w") as f: + f.write("pep8\n") requirements_list = [req1_filename, req2_filename] ret = self.run_function( - 'pip.install', requirements=requirements_list, bin_env=self.venv_dir + "pip.install", requirements=requirements_list, bin_env=self.venv_dir ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - self.assertEqual(ret['retcode'], 0) - found = self.pip_successful_install(ret['stdout']) + self.assertEqual(ret["retcode"], 0) + found = self.pip_successful_install(ret["stdout"]) self.assertTrue(found) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) @@ -264,39 +270,42 @@ class PipModuleTest(ModuleCase): # Create a requirements file that depends on another one. - req1_filename = 'requirements.txt' - req2_filename = 'requirements2.txt' + req1_filename = "requirements.txt" + req2_filename = "requirements2.txt" req_cwd = self.venv_dir req1_filepath = os.path.join(req_cwd, req1_filename) req2_filepath = os.path.join(req_cwd, req2_filename) - with salt.utils.files.fopen(req1_filepath, 'w') as f: - f.write('irc3-plugins-test\n') - with salt.utils.files.fopen(req2_filepath, 'w') as f: - f.write('pep8\n') + with salt.utils.files.fopen(req1_filepath, "w") as f: + f.write("irc3-plugins-test\n") + with salt.utils.files.fopen(req2_filepath, "w") as f: + f.write("pep8\n") requirements_list = [req1_filename, req2_filename] ret = self.run_function( - 'pip.install', requirements=requirements_list, - bin_env=self.venv_dir, cwd=req_cwd + "pip.install", + requirements=requirements_list, + bin_env=self.venv_dir, + cwd=req_cwd, ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - self.assertEqual(ret['retcode'], 0) - found = self.pip_successful_install(ret['stdout']) + self.assertEqual(ret["retcode"], 0) + found = self.pip_successful_install(ret["stdout"]) self.assertTrue(found) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) @@ -305,30 +314,31 @@ class PipModuleTest(ModuleCase): # Create a requirements file that depends on another one. - req1_filename = os.path.join(self.venv_dir, 'requirements.txt') - req2_filename = os.path.join(self.venv_dir, 'requirements2.txt') + req1_filename = os.path.join(self.venv_dir, "requirements.txt") + req2_filename = os.path.join(self.venv_dir, "requirements2.txt") - with salt.utils.files.fopen(req1_filename, 'w') as f: - f.write('-r requirements2.txt') - with salt.utils.files.fopen(req2_filename, 'w') as f: - f.write('pep8') + with salt.utils.files.fopen(req1_filename, "w") as f: + f.write("-r requirements2.txt") + with salt.utils.files.fopen(req2_filename, "w") as f: + f.write("pep8") ret = self.run_function( - 'pip.install', requirements=req1_filename, bin_env=self.venv_dir + "pip.install", requirements=req1_filename, bin_env=self.venv_dir ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - self.assertEqual(ret['retcode'], 0) - self.assertIn('installed pep8', ret['stdout']) + self.assertEqual(ret["retcode"], 0) + self.assertIn("installed pep8", ret["stdout"]) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) @@ -336,36 +346,39 @@ class PipModuleTest(ModuleCase): self._create_virtualenv(self.venv_dir) # Create a requirements file that depends on another one. - req_basepath = (self.venv_dir) + req_basepath = self.venv_dir - req1_filename = 'requirements.txt' - req2_filename = 'requirements2.txt' + req1_filename = "requirements.txt" + req2_filename = "requirements2.txt" req1_file = os.path.join(self.venv_dir, req1_filename) req2_file = os.path.join(self.venv_dir, req2_filename) - with salt.utils.files.fopen(req1_file, 'w') as f: - f.write('-r requirements2.txt') - with salt.utils.files.fopen(req2_file, 'w') as f: - f.write('pep8') + with salt.utils.files.fopen(req1_file, "w") as f: + f.write("-r requirements2.txt") + with salt.utils.files.fopen(req2_file, "w") as f: + f.write("pep8") ret = self.run_function( - 'pip.install', requirements=req1_filename, cwd=req_basepath, - bin_env=self.venv_dir + "pip.install", + requirements=req1_filename, + cwd=req_basepath, + bin_env=self.venv_dir, ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - self.assertEqual(ret['retcode'], 0) - self.assertIn('installed pep8', ret['stdout']) + self.assertEqual(ret["retcode"], 0) + self.assertIn("installed pep8", ret["stdout"]) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) @@ -373,222 +386,229 @@ class PipModuleTest(ModuleCase): self._create_virtualenv(self.venv_dir) # Create a requirements file that depends on another one. - req1_filename = os.path.join(self.venv_dir, 'requirements.txt') - req2_filename = os.path.join(self.venv_dir, 'requirements2.txt') - with salt.utils.files.fopen(req1_filename, 'w') as f: - f.write('-r requirements2.txt') - with salt.utils.files.fopen(req2_filename, 'w') as f: - f.write('pep8') + req1_filename = os.path.join(self.venv_dir, "requirements.txt") + req2_filename = os.path.join(self.venv_dir, "requirements2.txt") + with salt.utils.files.fopen(req1_filename, "w") as f: + f.write("-r requirements2.txt") + with salt.utils.files.fopen(req2_filename, "w") as f: + f.write("pep8") ret = self.run_function( - 'pip.install', requirements=req1_filename, bin_env=self.venv_dir, timeout=300) + "pip.install", + requirements=req1_filename, + bin_env=self.venv_dir, + timeout=300, + ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - if self._check_download_error(ret['stdout']): - self.skipTest('Test skipped due to pip download error') - self.assertEqual(ret['retcode'], 0) - self.assertIn('installed pep8', ret['stdout']) + if self._check_download_error(ret["stdout"]): + self.skipTest("Test skipped due to pip download error") + self.assertEqual(ret["retcode"], 0) + self.assertIn("installed pep8", ret["stdout"]) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) def test_pip_uninstall(self): # Let's create the testing virtualenv self._create_virtualenv(self.venv_dir) - ret = self.run_function('pip.install', ['pep8'], bin_env=self.venv_dir) + ret = self.run_function("pip.install", ["pep8"], bin_env=self.venv_dir) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) - ) - - try: - if self._check_download_error(ret['stdout']): - self.skipTest('Test skipped due to pip download error') - self.assertEqual(ret['retcode'], 0) - self.assertIn('installed pep8', ret['stdout']) - except KeyError as exc: - self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret ) ) - ret = self.run_function( - 'pip.uninstall', ['pep8'], bin_env=self.venv_dir - ) + + try: + if self._check_download_error(ret["stdout"]): + self.skipTest("Test skipped due to pip download error") + self.assertEqual(ret["retcode"], 0) + self.assertIn("installed pep8", ret["stdout"]) + except KeyError as exc: + self.fail( + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) + ) + ) + ret = self.run_function("pip.uninstall", ["pep8"], bin_env=self.venv_dir) if not isinstance(ret, dict): self.fail( - 'The \'pip.uninstall\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.uninstall' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - self.assertEqual(ret['retcode'], 0) - self.assertIn('uninstalled pep8', ret['stdout']) + self.assertEqual(ret["retcode"], 0) + self.assertIn("uninstalled pep8", ret["stdout"]) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) def test_pip_install_upgrade(self): # Create the testing virtualenv self._create_virtualenv(self.venv_dir) - ret = self.run_function( - 'pip.install', ['pep8==1.3.4'], bin_env=self.venv_dir - ) + ret = self.run_function("pip.install", ["pep8==1.3.4"], bin_env=self.venv_dir) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - if self._check_download_error(ret['stdout']): - self.skipTest('Test skipped due to pip download error') - self.assertEqual(ret['retcode'], 0) - self.assertIn('installed pep8', ret['stdout']) + if self._check_download_error(ret["stdout"]): + self.skipTest("Test skipped due to pip download error") + self.assertEqual(ret["retcode"], 0) + self.assertIn("installed pep8", ret["stdout"]) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) ret = self.run_function( - 'pip.install', - ['pep8'], - bin_env=self.venv_dir, - upgrade=True + "pip.install", ["pep8"], bin_env=self.venv_dir, upgrade=True ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) - ) - - try: - if self._check_download_error(ret['stdout']): - self.skipTest('Test skipped due to pip download error') - self.assertEqual(ret['retcode'], 0) - self.assertIn('installed pep8', ret['stdout']) - except KeyError as exc: - self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret ) ) - ret = self.run_function( - 'pip.uninstall', ['pep8'], bin_env=self.venv_dir - ) + try: + if self._check_download_error(ret["stdout"]): + self.skipTest("Test skipped due to pip download error") + self.assertEqual(ret["retcode"], 0) + self.assertIn("installed pep8", ret["stdout"]) + except KeyError as exc: + self.fail( + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) + ) + ) + + ret = self.run_function("pip.uninstall", ["pep8"], bin_env=self.venv_dir) if not isinstance(ret, dict): self.fail( - 'The \'pip.uninstall\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.uninstall' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - self.assertEqual(ret['retcode'], 0) - self.assertIn('uninstalled pep8', ret['stdout']) + self.assertEqual(ret["retcode"], 0) + self.assertIn("uninstalled pep8", ret["stdout"]) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) def test_pip_install_multiple_editables(self): editables = [ - 'git+https://github.com/jek/blinker.git#egg=Blinker', - 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting' + "git+https://github.com/jek/blinker.git#egg=Blinker", + "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting", ] # Create the testing virtualenv self._create_virtualenv(self.venv_dir) ret = self.run_function( - 'pip.install', [], - editable='{0}'.format(','.join(editables)), - bin_env=self.venv_dir + "pip.install", + [], + editable="{0}".format(",".join(editables)), + bin_env=self.venv_dir, ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - if self._check_download_error(ret['stdout']): - self.skipTest('Test skipped due to pip download error') - self.assertEqual(ret['retcode'], 0) - self.assertIn( - 'Successfully installed Blinker SaltTesting', ret['stdout'] - ) + if self._check_download_error(ret["stdout"]): + self.skipTest("Test skipped due to pip download error") + self.assertEqual(ret["retcode"], 0) + self.assertIn("Successfully installed Blinker SaltTesting", ret["stdout"]) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) def test_pip_install_multiple_editables_and_pkgs(self): editables = [ - 'git+https://github.com/jek/blinker.git#egg=Blinker', - 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting' + "git+https://github.com/jek/blinker.git#egg=Blinker", + "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting", ] # Create the testing virtualenv self._create_virtualenv(self.venv_dir) ret = self.run_function( - 'pip.install', ['pep8'], - editable='{0}'.format(','.join(editables)), - bin_env=self.venv_dir + "pip.install", + ["pep8"], + editable="{0}".format(",".join(editables)), + bin_env=self.venv_dir, ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) try: - if self._check_download_error(ret['stdout']): - self.skipTest('Test skipped due to pip download error') - self.assertEqual(ret['retcode'], 0) - for package in ('Blinker', 'SaltTesting', 'pep8'): + if self._check_download_error(ret["stdout"]): + self.skipTest("Test skipped due to pip download error") + self.assertEqual(ret["retcode"], 0) + for package in ("Blinker", "SaltTesting", "pep8"): self.assertRegex( - ret['stdout'], - r'(?:.*)(Successfully installed)(?:.*)({0})(?:.*)'.format(package) + ret["stdout"], + r"(?:.*)(Successfully installed)(?:.*)({0})(?:.*)".format(package), ) except KeyError as exc: self.fail( - 'The returned dictionary is missing an expected key. Error: \'{}\'. Dictionary: {}'.format( - exc, - pprint.pformat(ret) + "The returned dictionary is missing an expected key. Error: '{}'. Dictionary: {}".format( + exc, pprint.pformat(ret) ) ) - @skipIf(not os.path.isfile('pip3'), 'test where pip3 is installed') - @skipIf(salt.utils.platform.is_windows(), 'test specific for linux usage of /bin/python') + @skipIf(not os.path.isfile("pip3"), "test where pip3 is installed") + @skipIf( + salt.utils.platform.is_windows(), "test specific for linux usage of /bin/python" + ) def test_system_pip3(self): - self.run_function('pip.install', pkgs=['lazyimport==0.0.1'], bin_env='/bin/pip3') - ret1 = self.run_function('cmd.run', '/bin/pip3 freeze | grep lazyimport') - self.run_function('pip.uninstall', pkgs=['lazyimport'], bin_env='/bin/pip3') - ret2 = self.run_function('cmd.run', '/bin/pip3 freeze | grep lazyimport') - assert 'lazyimport==0.0.1' in ret1 - assert ret2 == '' + self.run_function( + "pip.install", pkgs=["lazyimport==0.0.1"], bin_env="/bin/pip3" + ) + ret1 = self.run_function("cmd.run", "/bin/pip3 freeze | grep lazyimport") + self.run_function("pip.uninstall", pkgs=["lazyimport"], bin_env="/bin/pip3") + ret2 = self.run_function("cmd.run", "/bin/pip3 freeze | grep lazyimport") + assert "lazyimport==0.0.1" in ret1 + assert ret2 == "" diff --git a/tests/integration/modules/test_pkg.py b/tests/integration/modules/test_pkg.py index e8374db2c09..6013f5e041b 100644 --- a/tests/integration/modules/test_pkg.py +++ b/tests/integration/modules/test_pkg.py @@ -1,120 +1,128 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import pprint -# Import Salt Testing libs +import pytest +import salt.utils.path +import salt.utils.pkg +import salt.utils.platform from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin from tests.support.helpers import ( destructiveTest, requires_network, requires_salt_modules, requires_salt_states, requires_system_grains, - skip_if_not_root) + skip_if_not_root, +) +from tests.support.mixins import SaltReturnAssertsMixin from tests.support.unit import skipIf -# Import Salt libs -import salt.utils.path -import salt.utils.pkg -import salt.utils.platform - +@pytest.mark.windows_whitelisted class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the pkg module - ''' + """ @classmethod @requires_system_grains def setUpClass(cls, grains): # pylint: disable=arguments-differ cls.ctx = {} - cls.pkg = 'figlet' + cls.pkg = "figlet" if salt.utils.platform.is_windows(): - cls.pkg = 'putty' - elif grains['os_family'] == 'RedHat': - cls.pkg = 'units' + cls.pkg = "putty" + elif grains["os_family"] == "RedHat": + cls.pkg = "units" @skip_if_not_root - @requires_salt_modules('pkg.refresh_db') + @requires_salt_modules("pkg.refresh_db") def setUp(self): - if 'refresh' not in self.ctx: - self.run_function('pkg.refresh_db') - self.ctx['refresh'] = True + if "refresh" not in self.ctx: + self.run_function("pkg.refresh_db") + self.ctx["refresh"] = True - @requires_salt_modules('pkg.list_pkgs') + @requires_salt_modules("pkg.list_pkgs") def test_list(self): - ''' + """ verify that packages are installed - ''' - ret = self.run_function('pkg.list_pkgs') + """ + ret = self.run_function("pkg.list_pkgs") self.assertNotEqual(len(ret.keys()), 0) - @requires_salt_modules('pkg.version_cmp') + @requires_salt_modules("pkg.version_cmp") @requires_system_grains def test_version_cmp(self, grains): - ''' + """ test package version comparison on supported platforms - ''' - func = 'pkg.version_cmp' - if grains['os_family'] == 'Debian': - lt = ['0.2.4-0ubuntu1', '0.2.4.1-0ubuntu1'] - eq = ['0.2.4-0ubuntu1', '0.2.4-0ubuntu1'] - gt = ['0.2.4.1-0ubuntu1', '0.2.4-0ubuntu1'] - elif grains['os_family'] == 'Suse': - lt = ['2.3.0-1', '2.3.1-15.1'] - eq = ['2.3.1-15.1', '2.3.1-15.1'] - gt = ['2.3.2-15.1', '2.3.1-15.1'] + """ + func = "pkg.version_cmp" + if grains["os_family"] == "Debian": + lt = ["0.2.4-0ubuntu1", "0.2.4.1-0ubuntu1"] + eq = ["0.2.4-0ubuntu1", "0.2.4-0ubuntu1"] + gt = ["0.2.4.1-0ubuntu1", "0.2.4-0ubuntu1"] + elif grains["os_family"] == "Suse": + lt = ["2.3.0-1", "2.3.1-15.1"] + eq = ["2.3.1-15.1", "2.3.1-15.1"] + gt = ["2.3.2-15.1", "2.3.1-15.1"] else: - lt = ['2.3.0', '2.3.1'] - eq = ['2.3.1', '2.3.1'] - gt = ['2.3.2', '2.3.1'] + lt = ["2.3.0", "2.3.1"] + eq = ["2.3.1", "2.3.1"] + gt = ["2.3.2", "2.3.1"] self.assertEqual(self.run_function(func, lt), -1) self.assertEqual(self.run_function(func, eq), 0) self.assertEqual(self.run_function(func, gt), 1) @destructiveTest - @requires_salt_modules('pkg.mod_repo', 'pkg.del_repo', 'pkg.get_repo') + @requires_salt_modules("pkg.mod_repo", "pkg.del_repo", "pkg.get_repo") @requires_network() @requires_system_grains def test_mod_del_repo(self, grains): - ''' + """ test modifying and deleting a software repository - ''' + """ repo = None try: - if grains['os'] == 'Ubuntu': - repo = 'ppa:otto-kesselgulasch/gimp-edge' - uri = 'http://ppa.launchpad.net/otto-kesselgulasch/gimp-edge/ubuntu' - ret = self.run_function('pkg.mod_repo', [repo, 'comps=main']) + if grains["os"] == "Ubuntu": + repo = "ppa:otto-kesselgulasch/gimp-edge" + uri = "http://ppa.launchpad.net/otto-kesselgulasch/gimp-edge/ubuntu" + ret = self.run_function("pkg.mod_repo", [repo, "comps=main"]) self.assertNotEqual(ret, {}) - ret = self.run_function('pkg.get_repo', [repo]) + ret = self.run_function("pkg.get_repo", [repo]) - self.assertIsInstance(ret, dict, - 'The \'pkg.get_repo\' command did not return the excepted dictionary. ' - 'Output:\n{}'.format(ret)) - self.assertEqual( - ret['uri'], - uri, - msg='The URI did not match. Full return:\n{}'.format( - pprint.pformat(ret) - ) + self.assertIsInstance( + ret, + dict, + "The 'pkg.get_repo' command did not return the excepted dictionary. " + "Output:\n{}".format(ret), + ) + self.assertEqual( + ret["uri"], + uri, + msg="The URI did not match. Full return:\n{}".format( + pprint.pformat(ret) + ), + ) + elif grains["os_family"] == "RedHat": + repo = "saltstack" + name = "SaltStack repo for RHEL/CentOS {0}".format( + grains["osmajorrelease"] + ) + baseurl = "http://repo.saltstack.com/yum/redhat/{0}/x86_64/latest/".format( + grains["osmajorrelease"] + ) + gpgkey = "https://repo.saltstack.com/yum/rhel{0}/SALTSTACK-GPG-KEY.pub".format( + grains["osmajorrelease"] ) - elif grains['os_family'] == 'RedHat': - repo = 'saltstack' - name = 'SaltStack repo for RHEL/CentOS {0}'.format(grains['osmajorrelease']) - baseurl = 'http://repo.saltstack.com/yum/redhat/{0}/x86_64/latest/'.format(grains['osmajorrelease']) - gpgkey = 'https://repo.saltstack.com/yum/rhel{0}/SALTSTACK-GPG-KEY.pub'.format( - grains['osmajorrelease']) gpgcheck = 1 enabled = 1 ret = self.run_function( - 'pkg.mod_repo', + "pkg.mod_repo", [repo], name=name, baseurl=baseurl, @@ -127,38 +135,41 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): self.assertNotEqual(ret, {}) repo_info = ret[next(iter(ret))] self.assertIn(repo, repo_info) - self.assertEqual(repo_info[repo]['baseurl'], baseurl) - ret = self.run_function('pkg.get_repo', [repo]) - self.assertEqual(ret['baseurl'], baseurl) + self.assertEqual(repo_info[repo]["baseurl"], baseurl) + ret = self.run_function("pkg.get_repo", [repo]) + self.assertEqual(ret["baseurl"], baseurl) finally: if repo is not None: - self.run_function('pkg.del_repo', [repo]) + self.run_function("pkg.del_repo", [repo]) def test_mod_del_repo_multiline_values(self): - ''' + """ test modifying and deleting a software repository defined with multiline values - ''' - os_grain = self.run_function('grains.item', ['os'])['os'] + """ + os_grain = self.run_function("grains.item", ["os"])["os"] repo = None try: - if os_grain in ['CentOS', 'RedHat']: - my_baseurl = 'http://my.fake.repo/foo/bar/\n http://my.fake.repo.alt/foo/bar/' - expected_get_repo_baseurl = 'http://my.fake.repo/foo/bar/\nhttp://my.fake.repo.alt/foo/bar/' - major_release = int( - self.run_function( - 'grains.item', - ['osmajorrelease'] - )['osmajorrelease'] + if os_grain in ["CentOS", "RedHat"]: + my_baseurl = ( + "http://my.fake.repo/foo/bar/\n http://my.fake.repo.alt/foo/bar/" ) - repo = 'fakerepo' - name = 'Fake repo for RHEL/CentOS/SUSE' + expected_get_repo_baseurl = ( + "http://my.fake.repo/foo/bar/\nhttp://my.fake.repo.alt/foo/bar/" + ) + major_release = int( + self.run_function("grains.item", ["osmajorrelease"])[ + "osmajorrelease" + ] + ) + repo = "fakerepo" + name = "Fake repo for RHEL/CentOS/SUSE" baseurl = my_baseurl - gpgkey = 'https://my.fake.repo/foo/bar/MY-GPG-KEY.pub' - failovermethod = 'priority' + gpgkey = "https://my.fake.repo/foo/bar/MY-GPG-KEY.pub" + failovermethod = "priority" gpgcheck = 1 enabled = 1 ret = self.run_function( - 'pkg.mod_repo', + "pkg.mod_repo", [repo], name=name, baseurl=baseurl, @@ -172,50 +183,50 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): self.assertNotEqual(ret, {}) repo_info = ret[next(iter(ret))] self.assertIn(repo, repo_info) - self.assertEqual(repo_info[repo]['baseurl'], my_baseurl) - ret = self.run_function('pkg.get_repo', [repo]) - self.assertEqual(ret['baseurl'], expected_get_repo_baseurl) - self.run_function('pkg.mod_repo', [repo]) - ret = self.run_function('pkg.get_repo', [repo]) - self.assertEqual(ret['baseurl'], expected_get_repo_baseurl) + self.assertEqual(repo_info[repo]["baseurl"], my_baseurl) + ret = self.run_function("pkg.get_repo", [repo]) + self.assertEqual(ret["baseurl"], expected_get_repo_baseurl) + self.run_function("pkg.mod_repo", [repo]) + ret = self.run_function("pkg.get_repo", [repo]) + self.assertEqual(ret["baseurl"], expected_get_repo_baseurl) finally: if repo is not None: - self.run_function('pkg.del_repo', [repo]) + self.run_function("pkg.del_repo", [repo]) - @requires_salt_modules('pkg.owner') + @requires_salt_modules("pkg.owner") def test_owner(self): - ''' + """ test finding the package owning a file - ''' - func = 'pkg.owner' - ret = self.run_function(func, ['/bin/ls']) + """ + func = "pkg.owner" + ret = self.run_function(func, ["/bin/ls"]) self.assertNotEqual(len(ret), 0) # Similar to pkg.owner, but for FreeBSD's pkgng - @requires_salt_modules('pkg.which') + @requires_salt_modules("pkg.which") def test_which(self): - ''' + """ test finding the package owning a file - ''' - func = 'pkg.which' - ret = self.run_function(func, ['/usr/local/bin/salt-call']) + """ + func = "pkg.which" + ret = self.run_function(func, ["/usr/local/bin/salt-call"]) self.assertNotEqual(len(ret), 0) @destructiveTest - @requires_salt_modules('pkg.version', 'pkg.install', 'pkg.remove') + @requires_salt_modules("pkg.version", "pkg.install", "pkg.remove") @requires_network() def test_install_remove(self): - ''' + """ successfully install and uninstall a package - ''' - version = self.run_function('pkg.version', [self.pkg]) + """ + version = self.run_function("pkg.version", [self.pkg]) def test_install(): - install_ret = self.run_function('pkg.install', [self.pkg]) + install_ret = self.run_function("pkg.install", [self.pkg]) self.assertIn(self.pkg, install_ret) def test_remove(): - remove_ret = self.run_function('pkg.remove', [self.pkg]) + remove_ret = self.run_function("pkg.remove", [self.pkg]) self.assertIn(self.pkg, remove_ret) if version and isinstance(version, dict): @@ -229,21 +240,32 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): test_remove() @destructiveTest - @requires_salt_modules('pkg.hold', 'pkg.unhold', 'pkg.install', 'pkg.version', 'pkg.remove', 'pkg.list_pkgs') - @requires_salt_states('pkg.installed') + @requires_salt_modules( + "pkg.hold", + "pkg.unhold", + "pkg.install", + "pkg.version", + "pkg.remove", + "pkg.list_pkgs", + ) + @requires_salt_states("pkg.installed") @requires_network() @requires_system_grains def test_hold_unhold(self, grains): - ''' + """ test holding and unholding a package - ''' + """ versionlock_pkg = None - if grains['os_family'] == 'RedHat': - pkgs = {p for p in self.run_function('pkg.list_pkgs') if '-versionlock' in p} + if grains["os_family"] == "RedHat": + pkgs = { + p for p in self.run_function("pkg.list_pkgs") if "-versionlock" in p + } if not pkgs: - self.skipTest('No versionlock package found in repositories') + self.skipTest("No versionlock package found in repositories") for versionlock_pkg in pkgs: - ret = self.run_state('pkg.installed', name=versionlock_pkg, refresh=False) + ret = self.run_state( + "pkg.installed", name=versionlock_pkg, refresh=False + ) # Exit loop if a versionlock package installed correctly try: self.assertSaltTrueReturn(ret) @@ -251,35 +273,35 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): except AssertionError: pass else: - self.fail('Could not install versionlock package from {}'.format(pkgs)) + self.fail("Could not install versionlock package from {}".format(pkgs)) - self.run_function('pkg.install', [self.pkg]) + self.run_function("pkg.install", [self.pkg]) try: - hold_ret = self.run_function('pkg.hold', [self.pkg]) - if versionlock_pkg and '-versionlock is not installed' in str(hold_ret): - self.skipTest('{} `{}` is installed'.format(hold_ret, versionlock_pkg)) + hold_ret = self.run_function("pkg.hold", [self.pkg]) + if versionlock_pkg and "-versionlock is not installed" in str(hold_ret): + self.skipTest("{} `{}` is installed".format(hold_ret, versionlock_pkg)) self.assertIn(self.pkg, hold_ret) - self.assertTrue(hold_ret[self.pkg]['result']) + self.assertTrue(hold_ret[self.pkg]["result"]) - unhold_ret = self.run_function('pkg.unhold', [self.pkg]) + unhold_ret = self.run_function("pkg.unhold", [self.pkg]) self.assertIn(self.pkg, unhold_ret) - self.assertTrue(unhold_ret[self.pkg]['result']) - self.run_function('pkg.remove', [self.pkg]) + self.assertTrue(unhold_ret[self.pkg]["result"]) + self.run_function("pkg.remove", [self.pkg]) finally: if versionlock_pkg: - ret = self.run_state('pkg.removed', name=versionlock_pkg) + ret = self.run_state("pkg.removed", name=versionlock_pkg) self.assertSaltTrueReturn(ret) @destructiveTest - @requires_salt_modules('pkg.refresh_db') + @requires_salt_modules("pkg.refresh_db") @requires_network() @requires_system_grains def test_refresh_db(self, grains): - ''' + """ test refreshing the package database - ''' - func = 'pkg.refresh_db' + """ + func = "pkg.refresh_db" rtag = salt.utils.pkg.rtag(self.minion_opts) salt.utils.pkg.write_rtag(self.minion_opts) @@ -287,42 +309,46 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): ret = self.run_function(func) if not isinstance(ret, dict): - self.skipTest('Upstream repo did not return coherent results: {}'.format(ret)) + self.skipTest( + "Upstream repo did not return coherent results: {}".format(ret) + ) - if grains['os_family'] == 'RedHat': + if grains["os_family"] == "RedHat": self.assertIn(ret, (True, None)) - elif grains['os_family'] == 'Suse': + elif grains["os_family"] == "Suse": if not isinstance(ret, dict): - self.skipTest('Upstream repo did not return coherent results. Skipping test.') + self.skipTest( + "Upstream repo did not return coherent results. Skipping test." + ) self.assertNotEqual(ret, {}) for source, state in ret.items(): self.assertIn(state, (True, False, None)) self.assertFalse(os.path.isfile(rtag)) - @requires_salt_modules('pkg.info_installed') + @requires_salt_modules("pkg.info_installed") @requires_system_grains def test_pkg_info(self, grains): - ''' + """ Test returning useful information on Ubuntu systems. - ''' - func = 'pkg.info_installed' + """ + func = "pkg.info_installed" - if grains['os_family'] == 'Debian': - ret = self.run_function(func, ['bash', 'dpkg']) + if grains["os_family"] == "Debian": + ret = self.run_function(func, ["bash", "dpkg"]) keys = ret.keys() - self.assertIn('bash', keys) - self.assertIn('dpkg', keys) - elif grains['os_family'] == 'RedHat': - ret = self.run_function(func, ['rpm', 'bash']) + self.assertIn("bash", keys) + self.assertIn("dpkg", keys) + elif grains["os_family"] == "RedHat": + ret = self.run_function(func, ["rpm", "bash"]) keys = ret.keys() - self.assertIn('rpm', keys) - self.assertIn('bash', keys) - elif grains['os_family'] == 'Suse': - ret = self.run_function(func, ['less', 'zypper']) + self.assertIn("rpm", keys) + self.assertIn("bash", keys) + elif grains["os_family"] == "Suse": + ret = self.run_function(func, ["less", "zypper"]) keys = ret.keys() - self.assertIn('less', keys) - self.assertIn('zypper', keys) + self.assertIn("less", keys) + self.assertIn("zypper", keys) else: ret = self.run_function(func, [self.pkg]) keys = ret.keys() @@ -330,28 +356,34 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): @destructiveTest @requires_network() - @requires_salt_modules('pkg.refresh_db', 'pkg.upgrade', 'pkg.install', 'pkg.list_repo_pkgs', 'pkg.list_upgrades') + @requires_salt_modules( + "pkg.refresh_db", + "pkg.upgrade", + "pkg.install", + "pkg.list_repo_pkgs", + "pkg.list_upgrades", + ) @requires_system_grains def test_pkg_upgrade_has_pending_upgrades(self, grains): - ''' + """ Test running a system upgrade when there are packages that need upgrading - ''' - if grains['os'] == 'Arch': - self.skipTest('Arch moved to Python 3.8 and we\'re not ready for it yet') + """ + if grains["os"] == "Arch": + self.skipTest("Arch moved to Python 3.8 and we're not ready for it yet") - func = 'pkg.upgrade' + func = "pkg.upgrade" # First make sure that an up-to-date copy of the package db is available - self.run_function('pkg.refresh_db') + self.run_function("pkg.refresh_db") - if grains['os_family'] == 'Suse': + if grains["os_family"] == "Suse": # This test assumes that there are multiple possible versions of a # package available. That makes it brittle if you pick just one # target, as changes in the available packages will break the test. # Therefore, we'll choose from several packages to make sure we get # one that is suitable for this test. - packages = ('hwinfo', 'avrdude', 'diffoscope', 'vim') - available = self.run_function('pkg.list_repo_pkgs', packages) + packages = ("hwinfo", "avrdude", "diffoscope", "vim") + available = self.run_function("pkg.list_repo_pkgs", packages) for package in packages: try: @@ -369,15 +401,15 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): # used to get an overview of the available packages. We should # try to find packages with few dependencies and small download # sizes, to keep this test from taking longer than necessary. - self.fail('No suitable package found for this test') + self.fail("No suitable package found for this test") # Make sure we have the 2nd-oldest available version installed - ret = self.run_function('pkg.install', [target], version=old) + ret = self.run_function("pkg.install", [target], version=old) if not isinstance(ret, dict): - if ret.startswith('ERROR'): + if ret.startswith("ERROR"): self.skipTest( - 'Could not install older {0} to complete ' - 'test.'.format(target) + "Could not install older {0} to complete " + "test.".format(target) ) # Run a system upgrade, which should catch the fact that the @@ -385,53 +417,68 @@ class PkgModuleTest(ModuleCase, SaltReturnAssertsMixin): ret = self.run_function(func) # The changes dictionary should not be empty. - if 'changes' in ret: - self.assertIn(target, ret['changes']) + if "changes" in ret: + self.assertIn(target, ret["changes"]) else: self.assertIn(target, ret) else: - ret = self.run_function('pkg.list_upgrades') - if ret == '' or ret == {}: - self.skipTest('No updates available for this machine. Skipping pkg.upgrade test.') + ret = self.run_function("pkg.list_upgrades") + if ret == "" or ret == {}: + self.skipTest( + "No updates available for this machine. Skipping pkg.upgrade test." + ) else: args = [] - if grains['os_family'] == 'Debian': - args = ['dist_upgrade=True'] + if grains["os_family"] == "Debian": + args = ["dist_upgrade=True"] ret = self.run_function(func, args) self.assertNotEqual(ret, {}) @destructiveTest - @skipIf(salt.utils.platform.is_darwin(), 'The jenkins user is equivalent to root on mac, causing the test to be unrunnable') - @requires_salt_modules('pkg.remove', 'pkg.latest_version') - @requires_salt_states('pkg.removed') + @skipIf( + salt.utils.platform.is_darwin(), + "The jenkins user is equivalent to root on mac, causing the test to be unrunnable", + ) + @requires_salt_modules("pkg.remove", "pkg.latest_version") + @requires_salt_states("pkg.removed") @requires_system_grains def test_pkg_latest_version(self, grains): - ''' + """ Check that pkg.latest_version returns the latest version of the uninstalled package. The package is not installed. Only the package version is checked. - ''' - self.run_state('pkg.removed', name=self.pkg) + """ + self.run_state("pkg.removed", name=self.pkg) cmd_pkg = [] - if grains['os_family'] == 'RedHat': - cmd_pkg = self.run_function('cmd.run', ['yum list {0}'.format(self.pkg)]) + if grains["os_family"] == "RedHat": + cmd_pkg = self.run_function("cmd.run", ["yum list {0}".format(self.pkg)]) elif salt.utils.platform.is_windows(): - cmd_pkg = self.run_function('pkg.list_available', [self.pkg]) - elif grains['os_family'] == 'Debian': - cmd_pkg = self.run_function('cmd.run', ['apt list {0}'.format(self.pkg)]) - elif grains['os_family'] == 'Arch': - cmd_pkg = self.run_function('cmd.run', ['pacman -Si {0}'.format(self.pkg)]) - elif grains['os_family'] == 'FreeBSD': - cmd_pkg = self.run_function('cmd.run', ['pkg search -S name -qQ version -e {0}'.format(self.pkg)]) - elif grains['os_family'] == 'Suse': - cmd_pkg = self.run_function('cmd.run', ['zypper info {0}'.format(self.pkg)]) - elif grains['os_family'] == 'MacOS': - brew_bin = salt.utils.path.which('brew') - mac_user = self.run_function('file.get_user', [brew_bin]) - if mac_user == 'root': - self.skipTest('brew cannot run as root, try a user in {}'.format(os.listdir('/Users/'))) - cmd_pkg = self.run_function('cmd.run', ['brew info {0}'.format(self.pkg)], run_as=mac_user) + cmd_pkg = self.run_function("pkg.list_available", [self.pkg]) + elif grains["os_family"] == "Debian": + cmd_pkg = self.run_function("cmd.run", ["apt list {0}".format(self.pkg)]) + elif grains["os_family"] == "Arch": + cmd_pkg = self.run_function("cmd.run", ["pacman -Si {0}".format(self.pkg)]) + elif grains["os_family"] == "FreeBSD": + cmd_pkg = self.run_function( + "cmd.run", ["pkg search -S name -qQ version -e {0}".format(self.pkg)] + ) + elif grains["os_family"] == "Suse": + cmd_pkg = self.run_function("cmd.run", ["zypper info {0}".format(self.pkg)]) + elif grains["os_family"] == "MacOS": + brew_bin = salt.utils.path.which("brew") + mac_user = self.run_function("file.get_user", [brew_bin]) + if mac_user == "root": + self.skipTest( + "brew cannot run as root, try a user in {}".format( + os.listdir("/Users/") + ) + ) + cmd_pkg = self.run_function( + "cmd.run", ["brew info {0}".format(self.pkg)], run_as=mac_user + ) else: - self.skipTest('TODO: test not configured for {}'.format(grains['os_family'])) - pkg_latest = self.run_function('pkg.latest_version', [self.pkg]) + self.skipTest( + "TODO: test not configured for {}".format(grains["os_family"]) + ) + pkg_latest = self.run_function("pkg.latest_version", [self.pkg]) self.assertIn(pkg_latest, cmd_pkg) diff --git a/tests/integration/modules/test_publish.py b/tests/integration/modules/test_publish.py index 84fe52a6b6f..14c68c6a2a4 100644 --- a/tests/integration/modules/test_publish.py +++ b/tests/integration/modules/test_publish.py @@ -1,155 +1,143 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin +@pytest.mark.windows_whitelisted class PublishModuleTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the publish module - ''' + """ + def test_publish(self): - ''' + """ publish.publish - ''' + """ ret = self.run_function( - 'publish.publish', - ['minion', 'test.ping'], - f_timeout=50 + "publish.publish", ["minion", "test.ping"], f_timeout=50 ) - self.assertEqual(ret, {'minion': True}) + self.assertEqual(ret, {"minion": True}) ret = self.run_function( - 'publish.publish', - ['minion', 'test.kwarg'], - f_arg='cheese=spam', - f_timeout=50 + "publish.publish", + ["minion", "test.kwarg"], + f_arg="cheese=spam", + f_timeout=50, ) - ret = ret['minion'] + ret = ret["minion"] check_true = ( - 'cheese', - '__pub_arg', - '__pub_fun', - '__pub_id', - '__pub_jid', - '__pub_ret', - '__pub_tgt', - '__pub_tgt_type', + "cheese", + "__pub_arg", + "__pub_fun", + "__pub_id", + "__pub_jid", + "__pub_ret", + "__pub_tgt", + "__pub_tgt_type", ) for name in check_true: if name not in ret: print(name) self.assertTrue(name in ret) - self.assertEqual(ret['cheese'], 'spam') - self.assertEqual(ret['__pub_arg'], [{'cheese': 'spam'}]) - self.assertEqual(ret['__pub_id'], 'minion') - self.assertEqual(ret['__pub_fun'], 'test.kwarg') + self.assertEqual(ret["cheese"], "spam") + self.assertEqual(ret["__pub_arg"], [{"cheese": "spam"}]) + self.assertEqual(ret["__pub_id"], "minion") + self.assertEqual(ret["__pub_fun"], "test.kwarg") def test_publish_yaml_args(self): - ''' + """ test publish.publish yaml args formatting - ''' + """ ret = self.run_function( - 'publish.publish', - ['minion', 'test.ping'], - f_timeout=50 + "publish.publish", ["minion", "test.ping"], f_timeout=50 ) - self.assertEqual(ret, {'minion': True}) + self.assertEqual(ret, {"minion": True}) - test_args_list = ['saltines, si', 'crackers, nein', 'cheese, indeed'] - test_args = '["{args[0]}", "{args[1]}", "{args[2]}"]'.format(args=test_args_list) - ret = self.run_function( - 'publish.publish', - ['minion', 'test.arg', test_args], - f_timeout=50 + test_args_list = ["saltines, si", "crackers, nein", "cheese, indeed"] + test_args = '["{args[0]}", "{args[1]}", "{args[2]}"]'.format( + args=test_args_list ) - ret = ret['minion'] + ret = self.run_function( + "publish.publish", ["minion", "test.arg", test_args], f_timeout=50 + ) + ret = ret["minion"] check_true = ( - '__pub_arg', - '__pub_fun', - '__pub_id', - '__pub_jid', - '__pub_ret', - '__pub_tgt', - '__pub_tgt_type', + "__pub_arg", + "__pub_fun", + "__pub_id", + "__pub_jid", + "__pub_ret", + "__pub_tgt", + "__pub_tgt_type", ) for name in check_true: - if name not in ret['kwargs']: + if name not in ret["kwargs"]: print(name) - self.assertTrue(name in ret['kwargs']) + self.assertTrue(name in ret["kwargs"]) - self.assertEqual(ret['args'], test_args_list) - self.assertEqual(ret['kwargs']['__pub_id'], 'minion') - self.assertEqual(ret['kwargs']['__pub_fun'], 'test.arg') + self.assertEqual(ret["args"], test_args_list) + self.assertEqual(ret["kwargs"]["__pub_id"], "minion") + self.assertEqual(ret["kwargs"]["__pub_fun"], "test.arg") def test_full_data(self): - ''' + """ publish.full_data - ''' + """ ret = self.run_function( - 'publish.full_data', - ['minion', 'test.fib', 20], - f_timeout=50 + "publish.full_data", ["minion", "test.fib", 20], f_timeout=50 ) self.assertTrue(ret) - self.assertEqual(ret['minion']['ret'][0], 6765) + self.assertEqual(ret["minion"]["ret"][0], 6765) def test_kwarg(self): - ''' + """ Verify that the pub data is making it to the minion functions - ''' + """ ret = self.run_function( - 'publish.full_data', - ['minion', 'test.kwarg'], - f_arg='cheese=spam', - f_timeout=50 + "publish.full_data", + ["minion", "test.kwarg"], + f_arg="cheese=spam", + f_timeout=50, ) - ret = ret['minion']['ret'] + ret = ret["minion"]["ret"] check_true = ( - 'cheese', - '__pub_arg', - '__pub_fun', - '__pub_id', - '__pub_jid', - '__pub_ret', - '__pub_tgt', - '__pub_tgt_type', + "cheese", + "__pub_arg", + "__pub_fun", + "__pub_id", + "__pub_jid", + "__pub_ret", + "__pub_tgt", + "__pub_tgt_type", ) for name in check_true: if name not in ret: print(name) self.assertTrue(name in ret) - self.assertEqual(ret['cheese'], 'spam') - self.assertEqual(ret['__pub_arg'], [{'cheese': 'spam'}]) - self.assertEqual(ret['__pub_id'], 'minion') - self.assertEqual(ret['__pub_fun'], 'test.kwarg') + self.assertEqual(ret["cheese"], "spam") + self.assertEqual(ret["__pub_arg"], [{"cheese": "spam"}]) + self.assertEqual(ret["__pub_id"], "minion") + self.assertEqual(ret["__pub_fun"], "test.kwarg") ret = self.run_function( - 'publish.full_data', - ['minion', 'test.kwarg'], - cheese='spam', - f_timeout=50 - ) - self.assertIn( - 'The following keyword arguments are not valid', ret + "publish.full_data", ["minion", "test.kwarg"], cheese="spam", f_timeout=50 ) + self.assertIn("The following keyword arguments are not valid", ret) def test_reject_minion(self): - ''' + """ Test bad authentication - ''' + """ ret = self.run_function( - 'publish.publish', - ['minion', 'cmd.run', ['echo foo']], - f_timeout=50 + "publish.publish", ["minion", "cmd.run", ["echo foo"]], f_timeout=50 ) self.assertEqual(ret, {}) diff --git a/tests/integration/modules/test_pw_user.py b/tests/integration/modules/test_pw_user.py index d3a2d52561c..7e70dec37cf 100644 --- a/tests/integration/modules/test_pw_user.py +++ b/tests/integration/modules/test_pw_user.py @@ -1,39 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.modules.pw_user ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' -# Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import string -import random +""" +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import random +import string + +import pytest +from salt.ext.six.moves import range from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest, skip_if_not_root -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin - +@pytest.mark.skip_unless_on_freebsd class PwUserModuleTest(ModuleCase): - def setUp(self): super(PwUserModuleTest, self).setUp() - os_grain = self.run_function('grains.item', ['kernel']) - if os_grain['kernel'] != 'FreeBSD': - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) + os_grain = self.run_function("grains.item", ["kernel"]) + if os_grain["kernel"] != "FreeBSD": + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) def __random_string(self, size=6): - return ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(size) + return "".join( + random.choice(string.ascii_uppercase + string.digits) for x in range(size) ) @destructiveTest @@ -42,37 +35,37 @@ class PwUserModuleTest(ModuleCase): # Let's create a user, which usually creates the group matching the # name uname = self.__random_string() - if self.run_function('user.add', [uname]) is not True: + if self.run_function("user.add", [uname]) is not True: # Skip because creating is not what we're testing here - self.run_function('user.delete', [uname, True, True]) - self.skipTest('Failed to create user') + self.run_function("user.delete", [uname, True, True]) + self.skipTest("Failed to create user") try: - uinfo = self.run_function('user.info', [uname]) - self.assertIn(uname, uinfo['groups']) + uinfo = self.run_function("user.info", [uname]) + self.assertIn(uname, uinfo["groups"]) # This uid is available, store it - uid = uinfo['uid'] + uid = uinfo["uid"] - self.run_function('user.delete', [uname, True, True]) + self.run_function("user.delete", [uname, True, True]) # Now, a weird group id gname = self.__random_string() - if self.run_function('group.add', [gname]) is not True: - self.run_function('group.delete', [gname, True, True]) - self.skipTest('Failed to create group') + if self.run_function("group.add", [gname]) is not True: + self.run_function("group.delete", [gname, True, True]) + self.skipTest("Failed to create group") - ginfo = self.run_function('group.info', [gname]) + ginfo = self.run_function("group.info", [gname]) # And create the user with that gid - if self.run_function('user.add', [uname, uid, ginfo['gid']]) is False: + if self.run_function("user.add", [uname, uid, ginfo["gid"]]) is False: # Skip because creating is not what we're testing here - self.run_function('user.delete', [uname, True, True]) - self.skipTest('Failed to create user') + self.run_function("user.delete", [uname, True, True]) + self.skipTest("Failed to create user") - uinfo = self.run_function('user.info', [uname]) - self.assertIn(gname, uinfo['groups']) + uinfo = self.run_function("user.info", [uname]) + self.assertIn(gname, uinfo["groups"]) except AssertionError: - self.run_function('user.delete', [uname, True, True]) + self.run_function("user.delete", [uname, True, True]) raise diff --git a/tests/integration/modules/test_rabbitmq.py b/tests/integration/modules/test_rabbitmq.py index 2950f908a4b..0d25a72fbc0 100644 --- a/tests/integration/modules/test_rabbitmq.py +++ b/tests/integration/modules/test_rabbitmq.py @@ -1,24 +1,25 @@ # -*- coding: utf-8 -*- -# Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ModuleCase from tests.support.helpers import requires_salt_modules, skip_if_not_root @skip_if_not_root -@requires_salt_modules('rabbitmq') +@requires_salt_modules("rabbitmq") +@pytest.mark.windows_whitelisted class RabbitModuleTest(ModuleCase): - ''' + """ Validates the rabbitmqctl functions. To run these tests, you will need to be able to access the rabbitmqctl commands. - ''' + """ + def test_user_exists(self): - ''' + """ Find out whether a user exists. - ''' - ret = self.run_function('rabbitmq.user_exists', ['null_user']) + """ + ret = self.run_function("rabbitmq.user_exists", ["null_user"]) self.assertEqual(ret, False) diff --git a/tests/integration/modules/test_saltcheck.py b/tests/integration/modules/test_saltcheck.py index ef00d13e6e3..51de1c5d218 100644 --- a/tests/integration/modules/test_saltcheck.py +++ b/tests/integration/modules/test_saltcheck.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Test the saltcheck module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,56 +10,76 @@ from tests.support.case import ModuleCase class SaltcheckModuleTest(ModuleCase): - ''' + """ Test the saltcheck module - ''' + """ + def test_saltcheck_run(self): - ''' + """ saltcheck.run_test - ''' - saltcheck_test = {"module_and_function": "test.echo", - "assertion": "assertEqual", - "expected_return": "This works!", - "args": ["This works!"]} - ret = self.run_function('saltcheck.run_test', test=saltcheck_test) - self.assertDictContainsSubset({'status': 'Pass'}, ret) + """ + saltcheck_test = { + "module_and_function": "test.echo", + "assertion": "assertEqual", + "expected_return": "This works!", + "args": ["This works!"], + } + ret = self.run_function("saltcheck.run_test", test=saltcheck_test) + self.assertDictContainsSubset({"status": "Pass"}, ret) def test_saltcheck_state(self): - ''' + """ saltcheck.run_state_tests - ''' - saltcheck_test = 'validate-saltcheck' - ret = self.run_function('saltcheck.run_state_tests', [saltcheck_test]) - self.assertDictContainsSubset({'status': 'Pass'}, ret[0]['validate-saltcheck']['echo_test_hello']) + """ + saltcheck_test = "validate-saltcheck" + ret = self.run_function("saltcheck.run_state_tests", [saltcheck_test]) + self.assertDictContainsSubset( + {"status": "Pass"}, ret[0]["validate-saltcheck"]["echo_test_hello"] + ) def test_topfile_validation(self): - ''' + """ saltcheck.run_highstate_tests - ''' - expected_top_states = self.run_function('state.show_top').get('base', []) - expected_top_states.append('TEST RESULTS') - ret = self.run_function('saltcheck.run_highstate_tests') + """ + expected_top_states = self.run_function("state.show_top").get("base", []) + expected_top_states.append("TEST RESULTS") + ret = self.run_function("saltcheck.run_highstate_tests") for top_state_dict in ret: self.assertIn(list(top_state_dict)[0], expected_top_states) def test_saltcheck_checkall(self): - ''' + """ Validate saltcheck.run_state_tests check_all for the default saltenv of base. validate-saltcheck state hosts a saltcheck-tests directory with 2 .tst files. By running check_all=True, both files should be found and show passed results. - ''' - saltcheck_test = 'validate-saltcheck' - ret = self.run_function('saltcheck.run_state_tests', [saltcheck_test], check_all=True) - self.assertDictContainsSubset({'status': 'Pass'}, ret[0]['validate-saltcheck']['echo_test_hello']) - self.assertDictContainsSubset({'status': 'Pass'}, ret[0]['validate-saltcheck']['check_all_validate']) + """ + saltcheck_test = "validate-saltcheck" + ret = self.run_function( + "saltcheck.run_state_tests", [saltcheck_test], check_all=True + ) + self.assertDictContainsSubset( + {"status": "Pass"}, ret[0]["validate-saltcheck"]["echo_test_hello"] + ) + self.assertDictContainsSubset( + {"status": "Pass"}, ret[0]["validate-saltcheck"]["check_all_validate"] + ) def test_saltcheck_checkall_saltenv(self): - ''' + """ Validate saltcheck.run_state_tests check_all for the prod saltenv validate-saltcheck state hosts a saltcheck-tests directory with 2 .tst files. By running check_all=True, both files should be found and show passed results. - ''' - saltcheck_test = 'validate-saltcheck' - ret = self.run_function('saltcheck.run_state_tests', [saltcheck_test], saltenv='prod', check_all=True) - self.assertDictContainsSubset({'status': 'Pass'}, ret[0]['validate-saltcheck']['echo_test_prod_env']) - self.assertDictContainsSubset({'status': 'Pass'}, ret[0]['validate-saltcheck']['check_all_validate_prod']) + """ + saltcheck_test = "validate-saltcheck" + ret = self.run_function( + "saltcheck.run_state_tests", + [saltcheck_test], + saltenv="prod", + check_all=True, + ) + self.assertDictContainsSubset( + {"status": "Pass"}, ret[0]["validate-saltcheck"]["echo_test_prod_env"] + ) + self.assertDictContainsSubset( + {"status": "Pass"}, ret[0]["validate-saltcheck"]["check_all_validate_prod"] + ) diff --git a/tests/integration/modules/test_saltutil.py b/tests/integration/modules/test_saltutil.py index 396d132a7c4..25020a28835 100644 --- a/tests/integration/modules/test_saltutil.py +++ b/tests/integration/modules/test_saltutil.py @@ -1,240 +1,275 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the saltutil module. -''' +""" -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os -import time import textwrap +import time -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.helpers import flaky -from tests.support.unit import skipIf - -# Import Salt Libs +import pytest import salt.utils.files import salt.utils.stringutils +from tests.support.case import ModuleCase +from tests.support.helpers import flaky +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf +@pytest.mark.windows_whitelisted class SaltUtilModuleTest(ModuleCase): - ''' + """ Testcase for the saltutil execution module - ''' + """ def setUp(self): - self.run_function('saltutil.refresh_pillar') + self.run_function("saltutil.refresh_pillar") # Tests for the wheel function def test_wheel_just_function(self): - ''' + """ Tests using the saltutil.wheel function when passing only a function. - ''' + """ # Wait for the pillar refresh to kick in, so that grains are ready to go time.sleep(3) - ret = self.run_function('saltutil.wheel', ['minions.connected']) - self.assertIn('minion', ret['return']) - self.assertIn('sub_minion', ret['return']) + ret = self.run_function("saltutil.wheel", ["minions.connected"]) + self.assertIn("minion", ret["return"]) + self.assertIn("sub_minion", ret["return"]) def test_wheel_with_arg(self): - ''' + """ Tests using the saltutil.wheel function when passing a function and an arg. - ''' - ret = self.run_function('saltutil.wheel', ['key.list', 'minion']) - self.assertEqual(ret['return'], {}) + """ + ret = self.run_function("saltutil.wheel", ["key.list", "minion"]) + self.assertEqual(ret["return"], {}) def test_wheel_no_arg_raise_error(self): - ''' + """ Tests using the saltutil.wheel function when passing a function that requires an arg, but one isn't supplied. - ''' - self.assertRaises(TypeError, 'saltutil.wheel', ['key.list']) + """ + self.assertRaises(TypeError, "saltutil.wheel", ["key.list"]) def test_wheel_with_kwarg(self): - ''' + """ Tests using the saltutil.wheel function when passing a function and a kwarg. This function just generates a key pair, but doesn't do anything with it. We just need this for testing purposes. - ''' - ret = self.run_function('saltutil.wheel', ['key.gen'], keysize=1024) - self.assertIn('pub', ret['return']) - self.assertIn('priv', ret['return']) + """ + ret = self.run_function("saltutil.wheel", ["key.gen"], keysize=1024) + self.assertIn("pub", ret["return"]) + self.assertIn("priv", ret["return"]) +@pytest.mark.windows_whitelisted class SyncGrainsTest(ModuleCase): def test_sync_grains(self): - ret = self.run_function('saltutil.sync_grains') + ret = self.run_function("saltutil.sync_grains") self.assertEqual(ret, []) +@pytest.mark.windows_whitelisted class SaltUtilSyncModuleTest(ModuleCase): - ''' + """ Testcase for the saltutil sync execution module - ''' + """ def setUp(self): - whitelist = {'modules': [], } - self.run_function('saltutil.sync_all', extmod_whitelist=whitelist) + whitelist = { + "modules": [], + } + self.run_function("saltutil.sync_all", extmod_whitelist=whitelist) def tearDown(self): - self.run_function('saltutil.sync_all') + self.run_function("saltutil.sync_all") def test_sync_all(self): - ''' + """ Test syncing all ModuleCase - ''' - expected_return = {'engines': [], - 'clouds': [], - 'grains': [], - 'beacons': [], - 'utils': [], - 'returners': [], - 'modules': [ - 'modules.depends_versioned', - 'modules.depends_versionless', - 'modules.mantest', - 'modules.override_test', - 'modules.runtests_decorators', - 'modules.runtests_helpers', - 'modules.salttest'], - 'renderers': [], - 'log_handlers': [], - 'matchers': [], - 'states': [], - 'sdb': [], - 'proxymodules': [], - 'executors': [], - 'output': [], - 'thorium': [], - 'serializers': []} - ret = self.run_function('saltutil.sync_all') + """ + expected_return = { + "engines": [], + "clouds": [], + "grains": [], + "beacons": [], + "utils": [], + "returners": [], + "modules": [ + "modules.depends_versioned", + "modules.depends_versionless", + "modules.mantest", + "modules.override_test", + "modules.runtests_decorators", + "modules.runtests_helpers", + "modules.salttest", + ], + "renderers": [], + "log_handlers": [], + "matchers": [], + "states": [], + "sdb": [], + "proxymodules": [], + "executors": [], + "output": [], + "thorium": [], + "serializers": [], + } + ret = self.run_function("saltutil.sync_all") self.assertEqual(ret, expected_return) def test_sync_all_whitelist(self): - ''' + """ Test syncing all ModuleCase with whitelist - ''' - expected_return = {'engines': [], - 'clouds': [], - 'grains': [], - 'beacons': [], - 'utils': [], - 'returners': [], - 'modules': ['modules.salttest'], - 'renderers': [], - 'log_handlers': [], - 'matchers': [], - 'states': [], - 'sdb': [], - 'proxymodules': [], - 'executors': [], - 'output': [], - 'thorium': [], - 'serializers': []} - ret = self.run_function('saltutil.sync_all', extmod_whitelist={'modules': ['salttest']}) + """ + expected_return = { + "engines": [], + "clouds": [], + "grains": [], + "beacons": [], + "utils": [], + "returners": [], + "modules": ["modules.salttest"], + "renderers": [], + "log_handlers": [], + "matchers": [], + "states": [], + "sdb": [], + "proxymodules": [], + "executors": [], + "output": [], + "thorium": [], + "serializers": [], + } + ret = self.run_function( + "saltutil.sync_all", extmod_whitelist={"modules": ["salttest"]} + ) self.assertEqual(ret, expected_return) def test_sync_all_blacklist(self): - ''' + """ Test syncing all ModuleCase with blacklist - ''' - expected_return = {'engines': [], - 'clouds': [], - 'grains': [], - 'beacons': [], - 'utils': [], - 'returners': [], - 'modules': ['modules.mantest', - 'modules.override_test', - 'modules.runtests_helpers', - 'modules.salttest'], - 'renderers': [], - 'log_handlers': [], - 'matchers': [], - 'states': [], - 'sdb': [], - 'proxymodules': [], - 'executors': [], - 'output': [], - 'thorium': [], - 'serializers': []} - ret = self.run_function('saltutil.sync_all', extmod_blacklist={'modules': ['runtests_decorators', 'depends_versioned', 'depends_versionless']}) + """ + expected_return = { + "engines": [], + "clouds": [], + "grains": [], + "beacons": [], + "utils": [], + "returners": [], + "modules": [ + "modules.mantest", + "modules.override_test", + "modules.runtests_helpers", + "modules.salttest", + ], + "renderers": [], + "log_handlers": [], + "matchers": [], + "states": [], + "sdb": [], + "proxymodules": [], + "executors": [], + "output": [], + "thorium": [], + "serializers": [], + } + ret = self.run_function( + "saltutil.sync_all", + extmod_blacklist={ + "modules": [ + "runtests_decorators", + "depends_versioned", + "depends_versionless", + ] + }, + ) self.assertEqual(ret, expected_return) def test_sync_all_blacklist_and_whitelist(self): - ''' + """ Test syncing all ModuleCase with whitelist and blacklist - ''' - expected_return = {'engines': [], - 'clouds': [], - 'grains': [], - 'beacons': [], - 'utils': [], - 'returners': [], - 'executors': [], - 'modules': [], - 'renderers': [], - 'log_handlers': [], - 'matchers': [], - 'states': [], - 'sdb': [], - 'proxymodules': [], - 'output': [], - 'thorium': [], - 'serializers': []} - ret = self.run_function('saltutil.sync_all', extmod_whitelist={'modules': ['runtests_decorators']}, - extmod_blacklist={'modules': ['runtests_decorators']}) + """ + expected_return = { + "engines": [], + "clouds": [], + "grains": [], + "beacons": [], + "utils": [], + "returners": [], + "executors": [], + "modules": [], + "renderers": [], + "log_handlers": [], + "matchers": [], + "states": [], + "sdb": [], + "proxymodules": [], + "output": [], + "thorium": [], + "serializers": [], + } + ret = self.run_function( + "saltutil.sync_all", + extmod_whitelist={"modules": ["runtests_decorators"]}, + extmod_blacklist={"modules": ["runtests_decorators"]}, + ) self.assertEqual(ret, expected_return) -@skipIf(True, 'Pillar refresh test is flaky. Skipping for now.') +@skipIf(True, "Pillar refresh test is flaky. Skipping for now.") +@pytest.mark.windows_whitelisted class SaltUtilSyncPillarTest(ModuleCase): - ''' + """ Testcase for the saltutil sync pillar module - ''' + """ @flaky def test_pillar_refresh(self): - ''' + """ test pillar refresh module - ''' - pillar_key = 'itworked' + """ + pillar_key = "itworked" - pre_pillar = self.run_function('pillar.raw') - self.assertNotIn(pillar_key, pre_pillar.get(pillar_key, 'didnotwork')) + pre_pillar = self.run_function("pillar.raw") + self.assertNotIn(pillar_key, pre_pillar.get(pillar_key, "didnotwork")) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'add_pillar.sls'), 'w') as fp: - fp.write(salt.utils.stringutils.to_str( - '{0}: itworked'.format(pillar_key) - )) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "add_pillar.sls"), "w" + ) as fp: + fp.write(salt.utils.stringutils.to_str("{0}: itworked".format(pillar_key))) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls'), 'w') as fp: - fp.write(textwrap.dedent('''\ + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls"), "w" + ) as fp: + fp.write( + textwrap.dedent( + """\ base: '*': - add_pillar - ''')) + """ + ) + ) - self.run_function('saltutil.refresh_pillar') + self.run_function("saltutil.refresh_pillar") pillar = False timeout = time.time() + 30 while not pillar: - post_pillar = self.run_function('pillar.raw') + post_pillar = self.run_function("pillar.raw") try: - self.assertIn(pillar_key, post_pillar.get(pillar_key, 'didnotwork')) + self.assertIn(pillar_key, post_pillar.get(pillar_key, "didnotwork")) pillar = True except AssertionError: if time.time() > timeout: - self.assertIn(pillar_key, post_pillar.get(pillar_key, 'didnotwork')) + self.assertIn(pillar_key, post_pillar.get(pillar_key, "didnotwork")) continue - post_pillar = self.run_function('pillar.raw') - self.assertIn(pillar_key, post_pillar.get(pillar_key, 'didnotwork')) + post_pillar = self.run_function("pillar.raw") + self.assertIn(pillar_key, post_pillar.get(pillar_key, "didnotwork")) def tearDown(self): for filename in os.listdir(RUNTIME_VARS.TMP_PILLAR_TREE): diff --git a/tests/integration/modules/test_service.py b/tests/integration/modules/test_service.py deleted file mode 100644 index e2cc16343ea..00000000000 --- a/tests/integration/modules/test_service.py +++ /dev/null @@ -1,158 +0,0 @@ -# -*- coding: utf-8 -*- - -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals - -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.helpers import destructiveTest, flaky -from tests.support.unit import skipIf - -# Import Salt libs -import salt.utils.path -import salt.utils.platform -import salt.utils.systemd - - -@destructiveTest -class ServiceModuleTest(ModuleCase): - ''' - Module testing the service module - ''' - def setUp(self): - self.service_name = 'cron' - cmd_name = 'crontab' - os_family = self.run_function('grains.get', ['os_family']) - os_release = self.run_function('grains.get', ['osrelease']) - if os_family == 'RedHat': - if os_release[0] == '7': - self.skipTest('Disabled on CentOS 7 until we can fix SSH connection issues.') - self.service_name = 'crond' - elif os_family == 'Arch': - self.service_name = 'sshd' - cmd_name = 'systemctl' - elif os_family == 'NILinuxRT': - self.service_name = 'syslog' - cmd_name = 'syslog-ng' - elif os_family == 'MacOS': - self.service_name = 'org.ntp.ntpd' - if int(os_release.split('.')[1]) >= 13: - self.service_name = 'com.apple.AirPlayXPCHelper' - elif salt.utils.platform.is_windows(): - self.service_name = 'Spooler' - - self.pre_srv_status = self.run_function('service.status', [self.service_name]) - self.pre_srv_enabled = True if self.service_name in self.run_function('service.get_enabled') else False - - if salt.utils.path.which(cmd_name) is None and not salt.utils.platform.is_windows(): - self.skipTest('{0} is not installed'.format(cmd_name)) - - def tearDown(self): - post_srv_status = self.run_function('service.status', [self.service_name]) - post_srv_enabled = True if self.service_name in self.run_function('service.get_enabled') else False - - if post_srv_status != self.pre_srv_status: - if self.pre_srv_status: - self.run_function('service.enable', [self.service_name]) - else: - self.run_function('service.disable', [self.service_name]) - - if post_srv_enabled != self.pre_srv_enabled: - if self.pre_srv_enabled: - self.run_function('service.enable', [self.service_name]) - else: - self.run_function('service.disable', [self.service_name]) - del self.service_name - - @flaky - def test_service_status_running(self): - ''' - test service.status execution module - when service is running - ''' - self.run_function('service.start', [self.service_name]) - check_service = self.run_function('service.status', [self.service_name]) - self.assertTrue(check_service) - - def test_service_status_dead(self): - ''' - test service.status execution module - when service is dead - ''' - self.run_function('service.stop', [self.service_name]) - check_service = self.run_function('service.status', [self.service_name]) - self.assertFalse(check_service) - - def test_service_restart(self): - ''' - test service.restart - ''' - self.assertTrue(self.run_function('service.restart', [self.service_name])) - - def test_service_enable(self): - ''' - test service.get_enabled and service.enable module - ''' - # disable service before test - self.assertTrue(self.run_function('service.disable', [self.service_name])) - - self.assertTrue(self.run_function('service.enable', [self.service_name])) - self.assertIn(self.service_name, self.run_function('service.get_enabled')) - - def test_service_disable(self): - ''' - test service.get_disabled and service.disable module - ''' - # enable service before test - self.assertTrue(self.run_function('service.enable', [self.service_name])) - - self.assertTrue(self.run_function('service.disable', [self.service_name])) - if salt.utils.platform.is_darwin(): - self.assertTrue(self.run_function('service.disabled', [self.service_name])) - else: - self.assertIn(self.service_name, self.run_function('service.get_disabled')) - - def test_service_disable_doesnot_exist(self): - ''' - test service.get_disabled and service.disable module - when service name does not exist - ''' - # enable service before test - srv_name = 'doesnotexist' - enable = self.run_function('service.enable', [srv_name]) - systemd = salt.utils.systemd.booted() - - # check service was not enabled - try: - self.assertFalse(enable) - except AssertionError: - self.assertIn('ERROR', enable) - - # check service was not disabled - if tuple(self.run_function('grains.item', ['osrelease_info'])['osrelease_info']) == (14, 0o4) and not systemd: - # currently upstart does not have a mechanism to report if disabling a service fails if does not exist - self.assertTrue(self.run_function('service.disable', [srv_name])) - elif self.run_function('grains.item', ['os'])['os'] == 'Debian' and \ - self.run_function('grains.item', ['osmajorrelease'])['osmajorrelease'] < 9 and systemd: - # currently disabling a service via systemd that does not exist - # on Debian 8 results in a True return code - self.assertTrue(self.run_function('service.disable', [srv_name])) - else: - try: - disable = self.run_function('service.disable', [srv_name]) - self.assertFalse(disable) - except AssertionError: - self.assertTrue('error' in disable.lower()) - - if salt.utils.platform.is_darwin(): - self.assertFalse(self.run_function('service.disabled', [srv_name])) - else: - self.assertNotIn(srv_name, self.run_function('service.get_disabled')) - - @skipIf(not salt.utils.platform.is_windows(), 'Windows Only Test') - def test_service_get_service_name(self): - ''' - test service.get_service_name - ''' - ret = self.run_function('service.get_service_name') - self.assertIn(self.service_name, ret.values()) diff --git a/tests/integration/modules/test_shadow.py b/tests/integration/modules/test_shadow.py deleted file mode 100644 index e443388199d..00000000000 --- a/tests/integration/modules/test_shadow.py +++ /dev/null @@ -1,243 +0,0 @@ -# -*- coding: utf-8 -*- -''' -integration tests for shadow linux -''' - -# Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import random -import string -import os - -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, flaky, skip_if_not_root - -# Import Salt libs -import salt.utils.files -import salt.utils.platform -import salt.modules.shadow -from salt.ext.six.moves import range - - -@skip_if_not_root -@skipIf(not salt.utils.platform.is_linux(), 'These tests can only be run on linux') -class ShadowModuleTest(ModuleCase): - ''' - Validate the linux shadow system module - ''' - - def setUp(self): - ''' - Get current settings - ''' - self._password = self.run_function('shadow.gen_password', ['Password1234']) - if 'ERROR' in self._password: - self.fail('Failed to generate password: {0}'.format(self._password)) - super(ShadowModuleTest, self).setUp() - os_grain = self.run_function('grains.item', ['kernel']) - if os_grain['kernel'] not in 'Linux': - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) - self._test_user = self.__random_string() - self._no_user = self.__random_string() - self._password = salt.modules.shadow.gen_password('Password1234') - - def __random_string(self, size=6): - ''' - Generates a random username - ''' - return 'tu-' + ''.join( - random.choice(string.ascii_lowercase + string.digits) - for x in range(size) - ) - - @destructiveTest - def test_info(self): - ''' - Test shadow.info - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - - # Correct Functionality - ret = self.run_function('shadow.info', [self._test_user]) - self.assertEqual(ret['name'], self._test_user) - - # User does not exist - ret = self.run_function('shadow.info', [self._no_user]) - self.assertEqual(ret['name'], '') - - @destructiveTest - def test_del_password(self): - ''' - Test shadow.del_password - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.del_password', [self._test_user])) - self.assertEqual( - self.run_function('shadow.info', [self._test_user])['passwd'], '') - - # User does not exist - self.assertFalse(self.run_function('shadow.del_password', [self._no_user])) - - @destructiveTest - def test_set_password(self): - ''' - Test shadow.set_password - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.set_password', [self._test_user, self._password])) - - # User does not exist - self.assertFalse(self.run_function('shadow.set_password', [self._no_user, self._password])) - - @destructiveTest - def test_set_inactdays(self): - ''' - Test shadow.set_inactdays - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.set_inactdays', [self._test_user, 12])) - - # User does not exist (set_inactdays return None is user does not exist) - self.assertFalse(self.run_function('shadow.set_inactdays', [self._no_user, 12])) - - @destructiveTest - def test_set_maxdays(self): - ''' - Test shadow.set_maxdays - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.set_maxdays', [self._test_user, 12])) - - # User does not exist (set_inactdays return None is user does not exist) - self.assertFalse(self.run_function('shadow.set_maxdays', [self._no_user, 12])) - - @destructiveTest - def test_set_mindays(self): - ''' - Test shadow.set_mindays - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.set_mindays', [self._test_user, 12])) - - # User does not exist (set_inactdays return None is user does not exist) - self.assertFalse(self.run_function('shadow.set_mindays', [self._no_user, 12])) - - @flaky - @destructiveTest - def test_lock_password(self): - ''' - Test shadow.lock_password - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - self.run_function('shadow.set_password', [self._test_user, self._password]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.lock_password', [self._test_user])) - - # User does not exist (set_inactdays return None is user does not exist) - self.assertFalse(self.run_function('shadow.lock_password', [self._no_user])) - - @destructiveTest - def test_unlock_password(self): - ''' - Test shadow.lock_password - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - self.run_function('shadow.set_password', [self._test_user, self._password]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.unlock_password', [self._test_user])) - - # User does not exist (set_inactdays return None is user does not exist) - self.assertFalse(self.run_function('shadow.unlock_password', [self._no_user])) - - @destructiveTest - def test_set_warndays(self): - ''' - Test shadow.set_warndays - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.set_warndays', [self._test_user, 12])) - - # User does not exist (set_inactdays return None is user does not exist) - self.assertFalse(self.run_function('shadow.set_warndays', [self._no_user, 12])) - - @destructiveTest - def test_set_date(self): - ''' - Test shadow.set_date - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.set_date', [self._test_user, '2016-08-19'])) - - # User does not exist (set_inactdays return None is user does not exist) - self.assertFalse(self.run_function('shadow.set_date', [self._no_user, '2016-08-19'])) - - @destructiveTest - def test_set_expire(self): - ''' - Test shadow.set_exipre - ''' - self.addCleanup(self.run_function, 'user.delete', [self._test_user]) - self.run_function('user.add', [self._test_user]) - - # Correct Functionality - self.assertTrue(self.run_function('shadow.set_expire', [self._test_user, '2016-08-25'])) - - # User does not exist (set_inactdays return None is user does not exist) - self.assertFalse(self.run_function('shadow.set_expire', [self._no_user, '2016-08-25'])) - - @destructiveTest - def test_set_del_root_password(self): - ''' - Test set/del password for root - ''' - # saving shadow file - if not os.access("/etc/shadow", os.R_OK | os.W_OK): - self.skipTest('Could not save initial state of /etc/shadow') - - def restore_shadow_file(contents): - # restore shadow file - with salt.utils.files.fopen('/etc/shadow', 'w') as wfh: - wfh.write(contents) - - with salt.utils.files.fopen('/etc/shadow', 'r') as rfh: - contents = rfh.read() - self.addCleanup(restore_shadow_file, contents) - - # set root password - self.assertTrue(self.run_function('shadow.set_password', ['root', self._password])) - self.assertEqual(self.run_function('shadow.info', ['root'])['passwd'], self._password) - # delete root password - self.assertTrue(self.run_function('shadow.del_password', ['root'])) - self.assertEqual(self.run_function('shadow.info', ['root'])['passwd'], '') diff --git a/tests/integration/modules/test_ssh.py b/tests/integration/modules/test_ssh.py index d59726787c8..1e90a073761 100644 --- a/tests/integration/modules/test_ssh.py +++ b/tests/integration/modules/test_ssh.py @@ -1,108 +1,106 @@ # -*- coding: utf-8 -*- -''' +""" Test the ssh module -''' -# Import python libs -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals + import os import shutil -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.helpers import skip_if_binaries_missing - -# Import salt libs +import pytest import salt.utils.files import salt.utils.platform - -# Import 3rd-party libs from salt.ext.tornado.httpclient import HTTPClient +from tests.support.case import ModuleCase +from tests.support.helpers import skip_if_binaries_missing +from tests.support.runtests import RUNTIME_VARS -GITHUB_FINGERPRINT = '9d:38:5b:83:a9:17:52:92:56:1a:5e:c4:d4:81:8e:0a:ca:51:a2:64:f1:74:20:11:2e:f8:8a:c3:a1:39:49:8f' +GITHUB_FINGERPRINT = "9d:38:5b:83:a9:17:52:92:56:1a:5e:c4:d4:81:8e:0a:ca:51:a2:64:f1:74:20:11:2e:f8:8a:c3:a1:39:49:8f" def check_status(): - ''' + """ Check the status of Github for remote operations - ''' + """ try: - return HTTPClient().fetch('http://github.com').code == 200 + return HTTPClient().fetch("http://github.com").code == 200 except Exception: # pylint: disable=broad-except return False -@skip_if_binaries_missing(['ssh', 'ssh-keygen'], check_all=True) +@skip_if_binaries_missing(["ssh", "ssh-keygen"], check_all=True) +@pytest.mark.windows_whitelisted class SSHModuleTest(ModuleCase): - ''' + """ Test the ssh module - ''' + """ + @classmethod def setUpClass(cls): - cls.subsalt_dir = os.path.join(RUNTIME_VARS.TMP, 'subsalt') - cls.authorized_keys = os.path.join(cls.subsalt_dir, 'authorized_keys') - cls.known_hosts = os.path.join(cls.subsalt_dir, 'known_hosts') + cls.subsalt_dir = os.path.join(RUNTIME_VARS.TMP, "subsalt") + cls.authorized_keys = os.path.join(cls.subsalt_dir, "authorized_keys") + cls.known_hosts = os.path.join(cls.subsalt_dir, "known_hosts") def setUp(self): - ''' + """ Set up the ssh module tests - ''' + """ if not check_status(): - self.skipTest('External source, github.com is down') + self.skipTest("External source, github.com is down") super(SSHModuleTest, self).setUp() if not os.path.isdir(self.subsalt_dir): os.makedirs(self.subsalt_dir) - ssh_raw_path = os.path.join(RUNTIME_VARS.FILES, 'ssh', 'raw') + ssh_raw_path = os.path.join(RUNTIME_VARS.FILES, "ssh", "raw") with salt.utils.files.fopen(ssh_raw_path) as fd: self.key = fd.read().strip() def tearDown(self): - ''' + """ Tear down the ssh module tests - ''' + """ if os.path.isdir(self.subsalt_dir): shutil.rmtree(self.subsalt_dir) super(SSHModuleTest, self).tearDown() del self.key def test_auth_keys(self): - ''' + """ test ssh.auth_keys - ''' + """ shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'ssh', 'authorized_keys'), - self.authorized_keys) - user = 'root' + os.path.join(RUNTIME_VARS.FILES, "ssh", "authorized_keys"), + self.authorized_keys, + ) + user = "root" if salt.utils.platform.is_windows(): - user = 'Administrator' - ret = self.run_function('ssh.auth_keys', [user, self.authorized_keys]) + user = "Administrator" + ret = self.run_function("ssh.auth_keys", [user, self.authorized_keys]) self.assertEqual(len(list(ret.items())), 1) # exactly one key is found key_data = list(ret.items())[0][1] try: - self.assertEqual(key_data['comment'], 'github.com') - self.assertEqual(key_data['enc'], 'ssh-rsa') + self.assertEqual(key_data["comment"], "github.com") + self.assertEqual(key_data["enc"], "ssh-rsa") self.assertEqual( - key_data['options'], ['command="/usr/local/lib/ssh-helper"'] + key_data["options"], ['command="/usr/local/lib/ssh-helper"'] ) - self.assertEqual(key_data['fingerprint'], GITHUB_FINGERPRINT) + self.assertEqual(key_data["fingerprint"], GITHUB_FINGERPRINT) except AssertionError as exc: raise AssertionError( - 'AssertionError: {0}. Function returned: {1}'.format( - exc, ret - ) + "AssertionError: {0}. Function returned: {1}".format(exc, ret) ) def test_bad_enctype(self): - ''' + """ test to make sure that bad key encoding types don't generate an invalid key entry in authorized_keys - ''' + """ shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'ssh', 'authorized_badkeys'), - self.authorized_keys) - ret = self.run_function('ssh.auth_keys', ['root', self.authorized_keys]) + os.path.join(RUNTIME_VARS.FILES, "ssh", "authorized_badkeys"), + self.authorized_keys, + ) + ret = self.run_function("ssh.auth_keys", ["root", self.authorized_keys]) # The authorized_badkeys file contains a key with an invalid ssh key # encoding (dsa-sha2-nistp256 instead of ecdsa-sha2-nistp256) @@ -112,142 +110,139 @@ class SSHModuleTest(ModuleCase): self.assertEqual(len(list(ret.items())), 0) # Zero keys found def test_get_known_host_entries(self): - ''' + """ Check that known host information is returned from ~/.ssh/config - ''' + """ shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts'), - self.known_hosts) - arg = ['root', 'github.com'] - kwargs = {'config': self.known_hosts} - ret = self.run_function('ssh.get_known_host_entries', arg, **kwargs)[0] + os.path.join(RUNTIME_VARS.FILES, "ssh", "known_hosts"), self.known_hosts + ) + arg = ["root", "github.com"] + kwargs = {"config": self.known_hosts} + ret = self.run_function("ssh.get_known_host_entries", arg, **kwargs)[0] try: - self.assertEqual(ret['enc'], 'ssh-rsa') - self.assertEqual(ret['key'], self.key) - self.assertEqual(ret['fingerprint'], GITHUB_FINGERPRINT) + self.assertEqual(ret["enc"], "ssh-rsa") + self.assertEqual(ret["key"], self.key) + self.assertEqual(ret["fingerprint"], GITHUB_FINGERPRINT) except AssertionError as exc: raise AssertionError( - 'AssertionError: {0}. Function returned: {1}'.format( - exc, ret - ) + "AssertionError: {0}. Function returned: {1}".format(exc, ret) ) def test_recv_known_host_entries(self): - ''' + """ Check that known host information is returned from remote host - ''' - ret = self.run_function('ssh.recv_known_host_entries', ['github.com']) + """ + ret = self.run_function("ssh.recv_known_host_entries", ["github.com"]) try: self.assertNotEqual(ret, None) - self.assertEqual(ret[0]['enc'], 'ssh-rsa') - self.assertEqual(ret[0]['key'], self.key) - self.assertEqual(ret[0]['fingerprint'], GITHUB_FINGERPRINT) + self.assertEqual(ret[0]["enc"], "ssh-rsa") + self.assertEqual(ret[0]["key"], self.key) + self.assertEqual(ret[0]["fingerprint"], GITHUB_FINGERPRINT) except AssertionError as exc: raise AssertionError( - 'AssertionError: {0}. Function returned: {1}'.format( - exc, ret - ) + "AssertionError: {0}. Function returned: {1}".format(exc, ret) ) def test_check_known_host_add(self): - ''' + """ Check known hosts by its fingerprint. File needs to be updated - ''' - arg = ['root', 'github.com'] - kwargs = {'fingerprint': GITHUB_FINGERPRINT, 'config': self.known_hosts} - ret = self.run_function('ssh.check_known_host', arg, **kwargs) - self.assertEqual(ret, 'add') + """ + arg = ["root", "github.com"] + kwargs = {"fingerprint": GITHUB_FINGERPRINT, "config": self.known_hosts} + ret = self.run_function("ssh.check_known_host", arg, **kwargs) + self.assertEqual(ret, "add") def test_check_known_host_update(self): - ''' + """ ssh.check_known_host update verification - ''' + """ shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts'), - self.known_hosts) - arg = ['root', 'github.com'] - kwargs = {'config': self.known_hosts} + os.path.join(RUNTIME_VARS.FILES, "ssh", "known_hosts"), self.known_hosts + ) + arg = ["root", "github.com"] + kwargs = {"config": self.known_hosts} # wrong fingerprint - ret = self.run_function('ssh.check_known_host', arg, - **dict(kwargs, fingerprint='aa:bb:cc:dd')) - self.assertEqual(ret, 'update') + ret = self.run_function( + "ssh.check_known_host", arg, **dict(kwargs, fingerprint="aa:bb:cc:dd") + ) + self.assertEqual(ret, "update") # wrong keyfile - ret = self.run_function('ssh.check_known_host', arg, - **dict(kwargs, key='YQ==')) - self.assertEqual(ret, 'update') + ret = self.run_function("ssh.check_known_host", arg, **dict(kwargs, key="YQ==")) + self.assertEqual(ret, "update") def test_check_known_host_exists(self): - ''' + """ Verify check_known_host_exists - ''' + """ shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts'), - self.known_hosts) - arg = ['root', 'github.com'] - kwargs = {'config': self.known_hosts} + os.path.join(RUNTIME_VARS.FILES, "ssh", "known_hosts"), self.known_hosts + ) + arg = ["root", "github.com"] + kwargs = {"config": self.known_hosts} # wrong fingerprint - ret = self.run_function('ssh.check_known_host', arg, - **dict(kwargs, fingerprint=GITHUB_FINGERPRINT)) - self.assertEqual(ret, 'exists') + ret = self.run_function( + "ssh.check_known_host", arg, **dict(kwargs, fingerprint=GITHUB_FINGERPRINT) + ) + self.assertEqual(ret, "exists") # wrong keyfile - ret = self.run_function('ssh.check_known_host', arg, - **dict(kwargs, key=self.key)) - self.assertEqual(ret, 'exists') + ret = self.run_function( + "ssh.check_known_host", arg, **dict(kwargs, key=self.key) + ) + self.assertEqual(ret, "exists") def test_rm_known_host(self): - ''' + """ ssh.rm_known_host - ''' + """ shutil.copyfile( - os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts'), - self.known_hosts) - arg = ['root', 'github.com'] - kwargs = {'config': self.known_hosts, 'key': self.key} + os.path.join(RUNTIME_VARS.FILES, "ssh", "known_hosts"), self.known_hosts + ) + arg = ["root", "github.com"] + kwargs = {"config": self.known_hosts, "key": self.key} # before removal - ret = self.run_function('ssh.check_known_host', arg, **kwargs) - self.assertEqual(ret, 'exists') + ret = self.run_function("ssh.check_known_host", arg, **kwargs) + self.assertEqual(ret, "exists") # remove - self.run_function('ssh.rm_known_host', arg, config=self.known_hosts) + self.run_function("ssh.rm_known_host", arg, config=self.known_hosts) # after removal - ret = self.run_function('ssh.check_known_host', arg, **kwargs) - self.assertEqual(ret, 'add') + ret = self.run_function("ssh.check_known_host", arg, **kwargs) + self.assertEqual(ret, "add") def test_set_known_host(self): - ''' + """ ssh.set_known_host - ''' + """ # add item - ret = self.run_function('ssh.set_known_host', ['root', 'github.com'], - config=self.known_hosts) + ret = self.run_function( + "ssh.set_known_host", ["root", "github.com"], config=self.known_hosts + ) try: - self.assertEqual(ret['status'], 'updated') - self.assertEqual(ret['old'], None) - self.assertEqual(ret['new'][0]['fingerprint'], GITHUB_FINGERPRINT) + self.assertEqual(ret["status"], "updated") + self.assertEqual(ret["old"], None) + self.assertEqual(ret["new"][0]["fingerprint"], GITHUB_FINGERPRINT) except AssertionError as exc: raise AssertionError( - 'AssertionError: {0}. Function returned: {1}'.format( - exc, ret - ) + "AssertionError: {0}. Function returned: {1}".format(exc, ret) ) # check that item does exist - ret = self.run_function('ssh.get_known_host_entries', ['root', 'github.com'], - config=self.known_hosts)[0] + ret = self.run_function( + "ssh.get_known_host_entries", + ["root", "github.com"], + config=self.known_hosts, + )[0] try: - self.assertEqual(ret['fingerprint'], GITHUB_FINGERPRINT) + self.assertEqual(ret["fingerprint"], GITHUB_FINGERPRINT) except AssertionError as exc: raise AssertionError( - 'AssertionError: {0}. Function returned: {1}'.format( - exc, ret - ) + "AssertionError: {0}. Function returned: {1}".format(exc, ret) ) # add the same item once again - ret = self.run_function('ssh.set_known_host', ['root', 'github.com'], - config=self.known_hosts) + ret = self.run_function( + "ssh.set_known_host", ["root", "github.com"], config=self.known_hosts + ) try: - self.assertEqual(ret['status'], 'exists') + self.assertEqual(ret["status"], "exists") except AssertionError as exc: raise AssertionError( - 'AssertionError: {0}. Function returned: {1}'.format( - exc, ret - ) + "AssertionError: {0}. Function returned: {1}".format(exc, ret) ) diff --git a/tests/integration/modules/test_state.py b/tests/integration/modules/test_state.py index 81b3b677b91..80517726ee1 100644 --- a/tests/integration/modules/test_state.py +++ b/tests/integration/modules/test_state.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import shutil @@ -11,23 +11,19 @@ import textwrap import threading import time -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.helpers import with_tempdir -from tests.support.unit import skipIf -from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt libs +import pytest import salt.utils.atomicfile import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils -from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES - -# Import 3rd-party libs from salt.ext import six +from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.case import ModuleCase +from tests.support.helpers import with_tempdir +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf log = logging.getLogger(__name__) @@ -36,24 +32,24 @@ DEFAULT_ENDING = salt.utils.stringutils.to_bytes(os.linesep) def trim_line_end(line): - ''' + """ Remove CRLF or LF from the end of line. - ''' - if line[-2:] == salt.utils.stringutils.to_bytes('\r\n'): + """ + if line[-2:] == salt.utils.stringutils.to_bytes("\r\n"): return line[:-2] - elif line[-1:] == salt.utils.stringutils.to_bytes('\n'): + elif line[-1:] == salt.utils.stringutils.to_bytes("\n"): return line[:-1] raise Exception("Invalid line ending") def reline(source, dest, force=False, ending=DEFAULT_ENDING): - ''' + """ Normalize the line endings of a file. - ''' + """ fp, tmp = tempfile.mkstemp() os.close(fp) - with salt.utils.files.fopen(tmp, 'wb') as tmp_fd: - with salt.utils.files.fopen(source, 'rb') as fd: + with salt.utils.files.fopen(tmp, "wb") as tmp_fd: + with salt.utils.files.fopen(source, "rb") as fd: lines = fd.readlines() for line in lines: line_noend = trim_line_end(line) @@ -63,233 +59,239 @@ def reline(source, dest, force=False, ending=DEFAULT_ENDING): os.rename(tmp, dest) +@pytest.mark.windows_whitelisted class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the state module - ''' + """ maxDiff = None @classmethod def setUpClass(cls): def _reline(path, ending=DEFAULT_ENDING): - ''' + """ Normalize the line endings of a file. - ''' - with salt.utils.files.fopen(path, 'rb') as fhr: + """ + with salt.utils.files.fopen(path, "rb") as fhr: lines = fhr.read().splitlines() - with salt.utils.atomicfile.atomic_open(path, 'wb') as fhw: + with salt.utils.atomicfile.atomic_open(path, "wb") as fhw: for line in lines: fhw.write(line + ending) - destpath = os.path.join(RUNTIME_VARS.BASE_FILES, 'testappend', 'firstif') - destpath = os.path.join(RUNTIME_VARS.BASE_FILES, 'testappend', 'secondif') + destpath = os.path.join(RUNTIME_VARS.BASE_FILES, "testappend", "firstif") + destpath = os.path.join(RUNTIME_VARS.BASE_FILES, "testappend", "secondif") _reline(destpath) cls.TIMEOUT = 600 if salt.utils.platform.is_windows() else 10 def test_show_highstate(self): - ''' + """ state.show_highstate - ''' - high = self.run_function('state.show_highstate') - destpath = os.path.join(RUNTIME_VARS.TMP, 'testfile') + """ + high = self.run_function("state.show_highstate") + destpath = os.path.join(RUNTIME_VARS.TMP, "testfile") self.assertTrue(isinstance(high, dict)) self.assertTrue(destpath in high) - self.assertEqual(high[destpath]['__env__'], 'base') + self.assertEqual(high[destpath]["__env__"], "base") def test_show_lowstate(self): - ''' + """ state.show_lowstate - ''' - low = self.run_function('state.show_lowstate') + """ + low = self.run_function("state.show_lowstate") self.assertTrue(isinstance(low, list)) self.assertTrue(isinstance(low[0], dict)) def test_show_states(self): - ''' + """ state.show_states - ''' - states = self.run_function('state.show_states') + """ + states = self.run_function("state.show_states") self.assertTrue(isinstance(states, list)) self.assertTrue(isinstance(states[0], six.string_types)) - states = self.run_function('state.show_states', sorted=False) + states = self.run_function("state.show_states", sorted=False) self.assertTrue(isinstance(states, list)) self.assertTrue(isinstance(states[0], six.string_types)) def test_show_states_missing_sls(self): - ''' + """ Test state.show_states with a sls file defined in a top file is missing - ''' - topfile = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'top.sls') - with salt.utils.files.fopen(topfile, 'w') as top_file: - top_file.write(textwrap.dedent('''\ + """ + topfile = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, "top.sls") + with salt.utils.files.fopen(topfile, "w") as top_file: + top_file.write( + textwrap.dedent( + """\ base: '*': - doesnotexist - ''')) - states = self.run_function('state.show_states') + """ + ) + ) + states = self.run_function("state.show_states") assert isinstance(states, list) assert states == ["No matching sls found for 'doesnotexist' in env 'base'"] def test_catch_recurse(self): - ''' + """ state.show_sls used to catch a recursive ref - ''' - err = self.run_function('state.sls', mods='recurse_fail') - self.assertIn('recursive', err[0]) + """ + err = self.run_function("state.sls", mods="recurse_fail") + self.assertIn("recursive", err[0]) def test_no_recurse(self): - ''' + """ verify that a sls structure is NOT a recursive ref - ''' - sls = self.run_function('state.show_sls', mods='recurse_ok') - self.assertIn('snmpd', sls) + """ + sls = self.run_function("state.show_sls", mods="recurse_ok") + self.assertIn("snmpd", sls) def test_no_recurse_two(self): - ''' + """ verify that a sls structure is NOT a recursive ref - ''' - sls = self.run_function('state.show_sls', mods='recurse_ok_two') - self.assertIn('/etc/nagios/nrpe.cfg', sls) + """ + sls = self.run_function("state.show_sls", mods="recurse_ok_two") + self.assertIn("/etc/nagios/nrpe.cfg", sls) def test_running_dictionary_consistency(self): - ''' + """ Test the structure of the running dictionary so we don't change it without deprecating/documenting the change - ''' + """ running_dict_fields = [ - '__id__', - '__run_num__', - '__sls__', - 'changes', - 'comment', - 'duration', - 'name', - 'result', - 'start_time', + "__id__", + "__run_num__", + "__sls__", + "changes", + "comment", + "duration", + "name", + "result", + "start_time", ] - sls = self.run_function('state.single', - fun='test.succeed_with_changes', - name='gndn') + sls = self.run_function( + "state.single", fun="test.succeed_with_changes", name="gndn" + ) for state, ret in sls.items(): for field in running_dict_fields: self.assertIn(field, ret) def test_running_dictionary_key_sls(self): - ''' + """ Ensure the __sls__ key is either null or a string - ''' - sls1 = self.run_function('state.single', - fun='test.succeed_with_changes', - name='gndn') + """ + sls1 = self.run_function( + "state.single", fun="test.succeed_with_changes", name="gndn" + ) - sls2 = self.run_function('state.sls', mods='gndn') + sls2 = self.run_function("state.sls", mods="gndn") for state, ret in sls1.items(): - self.assertTrue(isinstance(ret['__sls__'], type(None))) + self.assertTrue(isinstance(ret["__sls__"], type(None))) for state, ret in sls2.items(): - self.assertTrue(isinstance(ret['__sls__'], six.string_types)) + self.assertTrue(isinstance(ret["__sls__"], six.string_types)) def _remove_request_cache_file(self): - ''' + """ remove minion state request file - ''' - cache_file = os.path.join(self.get_config('minion')['cachedir'], 'req_state.p') + """ + cache_file = os.path.join(self.get_config("minion")["cachedir"], "req_state.p") if os.path.exists(cache_file): os.remove(cache_file) def test_request(self): - ''' + """ verify sending a state request to the minion(s) - ''' + """ self._remove_request_cache_file() - ret = self.run_function('state.request', mods='modules.state.requested') - result = ret['cmd_|-count_root_dir_contents_|-ls -a / | wc -l_|-run']['result'] + ret = self.run_function("state.request", mods="modules.state.requested") + result = ret["cmd_|-count_root_dir_contents_|-ls -a / | wc -l_|-run"]["result"] self.assertEqual(result, None) def test_check_request(self): - ''' + """ verify checking a state request sent to the minion(s) - ''' + """ self._remove_request_cache_file() - self.run_function('state.request', mods='modules.state.requested') - ret = self.run_function('state.check_request') - result = ret['default']['test_run']['cmd_|-count_root_dir_contents_|-ls -a / | wc -l_|-run']['result'] + self.run_function("state.request", mods="modules.state.requested") + ret = self.run_function("state.check_request") + result = ret["default"]["test_run"][ + "cmd_|-count_root_dir_contents_|-ls -a / | wc -l_|-run" + ]["result"] self.assertEqual(result, None) def test_clear_request(self): - ''' + """ verify clearing a state request sent to the minion(s) - ''' + """ self._remove_request_cache_file() - self.run_function('state.request', mods='modules.state.requested') - ret = self.run_function('state.clear_request') + self.run_function("state.request", mods="modules.state.requested") + ret = self.run_function("state.clear_request") self.assertTrue(ret) def test_run_request_succeeded(self): - ''' + """ verify running a state request sent to the minion(s) - ''' + """ self._remove_request_cache_file() if salt.utils.platform.is_windows(): - self.run_function('state.request', mods='modules.state.requested_win') + self.run_function("state.request", mods="modules.state.requested_win") else: - self.run_function('state.request', mods='modules.state.requested') + self.run_function("state.request", mods="modules.state.requested") - ret = self.run_function('state.run_request') + ret = self.run_function("state.run_request") if salt.utils.platform.is_windows(): - key = 'cmd_|-count_root_dir_contents_|-Get-ChildItem C:\\\\ | Measure-Object | %{$_.Count}_|-run' + key = "cmd_|-count_root_dir_contents_|-Get-ChildItem C:\\\\ | Measure-Object | %{$_.Count}_|-run" else: - key = 'cmd_|-count_root_dir_contents_|-ls -a / | wc -l_|-run' + key = "cmd_|-count_root_dir_contents_|-ls -a / | wc -l_|-run" - result = ret[key]['result'] + result = ret[key]["result"] self.assertTrue(result) def test_run_request_failed_no_request_staged(self): - ''' + """ verify not running a state request sent to the minion(s) - ''' + """ self._remove_request_cache_file() - self.run_function('state.request', mods='modules.state.requested') - self.run_function('state.clear_request') - ret = self.run_function('state.run_request') + self.run_function("state.request", mods="modules.state.requested") + self.run_function("state.clear_request") + ret = self.run_function("state.run_request") self.assertEqual(ret, {}) @with_tempdir() def test_issue_1896_file_append_source(self, base_dir): - ''' + """ Verify that we can append a file's contents - ''' - testfile = os.path.join(base_dir, 'test.append') + """ + testfile = os.path.join(base_dir, "test.append") - ret = self.run_state('file.touch', name=testfile) + ret = self.run_state("file.touch", name=testfile) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'file.append', - name=testfile, - source='salt://testappend/firstif') + "file.append", name=testfile, source="salt://testappend/firstif" + ) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'file.append', - name=testfile, - source='salt://testappend/secondif') + "file.append", name=testfile, source="salt://testappend/secondif" + ) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(testfile, 'r') as fp_: + with salt.utils.files.fopen(testfile, "r") as fp_: testfile_contents = salt.utils.stringutils.to_unicode(fp_.read()) - contents = textwrap.dedent('''\ + contents = textwrap.dedent( + """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) @@ -299,7 +301,8 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): if [ -f /etc/bash_completion ] && ! shopt -oq posix; then . /etc/bash_completion fi - ''') + """ + ) if salt.utils.platform.is_windows(): new_contents = contents.splitlines() @@ -309,23 +312,21 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): self.assertMultiLineEqual(contents, testfile_contents) ret = self.run_state( - 'file.append', - name=testfile, - source='salt://testappend/secondif') + "file.append", name=testfile, source="salt://testappend/secondif" + ) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'file.append', - name=testfile, - source='salt://testappend/firstif') + "file.append", name=testfile, source="salt://testappend/firstif" + ) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(testfile, 'r') as fp_: + with salt.utils.files.fopen(testfile, "r") as fp_: testfile_contents = salt.utils.stringutils.to_unicode(fp_.read()) self.assertMultiLineEqual(contents, testfile_contents) def test_issue_1876_syntax_error(self): - ''' + """ verify that we catch the following syntax error:: /tmp/salttest/issue-1876: @@ -337,18 +338,19 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): file.append: - text: foo - ''' - testfile = os.path.join(RUNTIME_VARS.TMP, 'issue-1876') + """ + testfile = os.path.join(RUNTIME_VARS.TMP, "issue-1876") - sls = self.run_function('state.sls', mods='issue-1876') + sls = self.run_function("state.sls", mods="issue-1876") self.assertIn( - 'ID \'{0}\' in SLS \'issue-1876\' contains multiple state ' - 'declarations of the same type'.format(testfile), - sls + "ID '{0}' in SLS 'issue-1876' contains multiple state " + "declarations of the same type".format(testfile), + sls, ) def test_issue_1879_too_simple_contains_check(self): - expected = textwrap.dedent('''\ + expected = textwrap.dedent( + """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) @@ -357,56 +359,49 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): if [ -f /etc/bash_completion ] && ! shopt -oq posix; then . /etc/bash_completion fi - ''') + """ + ) if salt.utils.platform.is_windows(): new_contents = expected.splitlines() expected = os.linesep.join(new_contents) expected += os.linesep - testfile = os.path.join(RUNTIME_VARS.TMP, 'issue-1879') + testfile = os.path.join(RUNTIME_VARS.TMP, "issue-1879") # Delete if exiting if os.path.isfile(testfile): os.unlink(testfile) # Create the file - ret = self.run_function('state.sls', mods='issue-1879', timeout=120) + ret = self.run_function("state.sls", mods="issue-1879", timeout=120) self.assertSaltTrueReturn(ret) # The first append - ret = self.run_function( - 'state.sls', mods='issue-1879.step-1', timeout=120 - ) + ret = self.run_function("state.sls", mods="issue-1879.step-1", timeout=120) self.assertSaltTrueReturn(ret) # The second append - ret = self.run_function( - 'state.sls', mods='issue-1879.step-2', timeout=120 - ) + ret = self.run_function("state.sls", mods="issue-1879.step-2", timeout=120) self.assertSaltTrueReturn(ret) # Does it match? try: - with salt.utils.files.fopen(testfile, 'r') as fp_: + with salt.utils.files.fopen(testfile, "r") as fp_: contents = salt.utils.stringutils.to_unicode(fp_.read()) self.assertMultiLineEqual(expected, contents) # Make sure we don't re-append existing text - ret = self.run_function( - 'state.sls', mods='issue-1879.step-1', timeout=120 - ) + ret = self.run_function("state.sls", mods="issue-1879.step-1", timeout=120) self.assertSaltTrueReturn(ret) - ret = self.run_function( - 'state.sls', mods='issue-1879.step-2', timeout=120 - ) + ret = self.run_function("state.sls", mods="issue-1879.step-2", timeout=120) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(testfile, 'r') as fp_: + with salt.utils.files.fopen(testfile, "r") as fp_: contents = salt.utils.stringutils.to_unicode(fp_.read()) self.assertMultiLineEqual(expected, contents) except Exception: # pylint: disable=broad-except if os.path.exists(testfile): - shutil.copy(testfile, testfile + '.bak') + shutil.copy(testfile, testfile + ".bak") raise finally: if os.path.exists(testfile): @@ -416,36 +411,36 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True) pillar = {} - for path in ('include-test', 'to-include-test', 'exclude-test'): + for path in ("include-test", "to-include-test", "exclude-test"): pillar[path] = os.path.join(tempdir, path) - ret = self.run_function('state.sls', mods='include-test', pillar=pillar) + ret = self.run_function("state.sls", mods="include-test", pillar=pillar) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isfile(pillar['include-test'])) - self.assertTrue(os.path.isfile(pillar['to-include-test'])) - self.assertFalse(os.path.isfile(pillar['exclude-test'])) + self.assertTrue(os.path.isfile(pillar["include-test"])) + self.assertTrue(os.path.isfile(pillar["to-include-test"])) + self.assertFalse(os.path.isfile(pillar["exclude-test"])) def test_exclude(self): tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, tempdir, ignore_errors=True) pillar = {} - for path in ('include-test', 'exclude-test', 'to-include-test'): + for path in ("include-test", "exclude-test", "to-include-test"): pillar[path] = os.path.join(tempdir, path) - ret = self.run_function('state.sls', mods='exclude-test', pillar=pillar) + ret = self.run_function("state.sls", mods="exclude-test", pillar=pillar) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isfile(pillar['include-test'])) - self.assertTrue(os.path.isfile(pillar['exclude-test'])) - self.assertFalse(os.path.isfile(pillar['to-include-test'])) + self.assertTrue(os.path.isfile(pillar["include-test"])) + self.assertTrue(os.path.isfile(pillar["exclude-test"])) + self.assertFalse(os.path.isfile(pillar["to-include-test"])) - @skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') + @skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, + "virtualenv not installed", + ) def test_issue_2068_template_str(self): - venv_dir = os.path.join( - RUNTIME_VARS.TMP, 'issue-2068-template-str' - ) + venv_dir = os.path.join(RUNTIME_VARS.TMP, "issue-2068-template-str") try: ret = self.run_function( - 'state.sls', mods='issue-2068-template-str-no-dot', - timeout=120 + "state.sls", mods="issue-2068-template-str-no-dot", timeout=120 ) self.assertSaltTrueReturn(ret) finally: @@ -456,25 +451,24 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): # with state.sls works, so should using state.template_str template_path = os.path.join( os.path.dirname(os.path.dirname(__file__)), - 'files', 'file', 'base', 'issue-2068-template-str-no-dot.sls' + "files", + "file", + "base", + "issue-2068-template-str-no-dot.sls", ) - with salt.utils.files.fopen(template_path, 'r') as fp_: + with salt.utils.files.fopen(template_path, "r") as fp_: template = salt.utils.stringutils.to_unicode(fp_.read()) - ret = self.run_function( - 'state.template_str', [template], timeout=120 - ) + ret = self.run_function("state.template_str", [template], timeout=120) self.assertSaltTrueReturn(ret) # Now using state.template - ret = self.run_function( - 'state.template', [template_path], timeout=120 - ) + ret = self.run_function("state.template", [template_path], timeout=120) self.assertSaltTrueReturn(ret) # Now the problematic #2068 including dot's ret = self.run_function( - 'state.sls', mods='issue-2068-template-str', timeout=120 + "state.sls", mods="issue-2068-template-str", timeout=120 ) self.assertSaltTrueReturn(ret) @@ -482,24 +476,24 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): # with state.sls works, so should using state.template_str template_path = os.path.join( os.path.dirname(os.path.dirname(__file__)), - 'files', 'file', 'base', 'issue-2068-template-str.sls' + "files", + "file", + "base", + "issue-2068-template-str.sls", ) - with salt.utils.files.fopen(template_path, 'r') as fp_: + with salt.utils.files.fopen(template_path, "r") as fp_: template = salt.utils.stringutils.to_unicode(fp_.read()) - ret = self.run_function( - 'state.template_str', [template], timeout=120 - ) + ret = self.run_function("state.template_str", [template], timeout=120) self.assertSaltTrueReturn(ret) # Now using state.template - ret = self.run_function( - 'state.template', [template_path], timeout=120 - ) + ret = self.run_function("state.template", [template_path], timeout=120) self.assertSaltTrueReturn(ret) def test_template_invalid_items(self): - TEMPLATE = textwrap.dedent('''\ + TEMPLATE = textwrap.dedent( + """\ {0}: - issue-2068-template-str @@ -507,753 +501,803 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): file: - managed - source: salt://testfile - ''') - for item in ('include', 'exclude', 'extends'): - ret = self.run_function( - 'state.template_str', [TEMPLATE.format(item)] - ) + """ + ) + for item in ("include", "exclude", "extends"): + ret = self.run_function("state.template_str", [TEMPLATE.format(item)]) self.assertTrue(isinstance(ret, list)) self.assertNotEqual(ret, []) self.assertEqual( - ['The \'{0}\' declaration found on \'<template-str>\' is ' - 'invalid when rendering single templates'.format(item)], - ret + [ + "The '{0}' declaration found on '<template-str>' is " + "invalid when rendering single templates".format(item) + ], + ret, ) def test_pydsl(self): - ''' + """ Test the basics of the pydsl - ''' - ret = self.run_function('state.sls', mods='pydsl-1') + """ + ret = self.run_function("state.sls", mods="pydsl-1") self.assertSaltTrueReturn(ret) def test_issues_7905_and_8174_sls_syntax_error(self): - ''' + """ Call sls file with yaml syntax error. Ensure theses errors are detected and presented to the user without stack traces. - ''' - ret = self.run_function('state.sls', mods='syntax.badlist') - self.assertEqual(ret, [ - 'State \'A\' in SLS \'syntax.badlist\' is not formed as a list' - ]) - ret = self.run_function('state.sls', mods='syntax.badlist2') - self.assertEqual(ret, [ - 'State \'C\' in SLS \'syntax.badlist2\' is not formed as a list' - ]) + """ + ret = self.run_function("state.sls", mods="syntax.badlist") + self.assertEqual( + ret, ["State 'A' in SLS 'syntax.badlist' is not formed as a list"] + ) + ret = self.run_function("state.sls", mods="syntax.badlist2") + self.assertEqual( + ret, ["State 'C' in SLS 'syntax.badlist2' is not formed as a list"] + ) def test_requisites_mixed_require_prereq_use(self): - ''' + """ Call sls file containing several requisites. - ''' + """ expected_simple_result = { - 'cmd_|-A_|-echo A_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo A" run', - 'result': True, - 'changes': True}, - 'cmd_|-B_|-echo B_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo B" run', - 'result': True, - 'changes': True}, - 'cmd_|-C_|-echo C_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo C" run', - 'result': True, - 'changes': True} + "cmd_|-A_|-echo A_|-run": { + "__run_num__": 2, + "comment": 'Command "echo A" run', + "result": True, + "changes": True, + }, + "cmd_|-B_|-echo B_|-run": { + "__run_num__": 1, + "comment": 'Command "echo B" run', + "result": True, + "changes": True, + }, + "cmd_|-C_|-echo C_|-run": { + "__run_num__": 0, + "comment": 'Command "echo C" run', + "result": True, + "changes": True, + }, } expected_result = { - 'cmd_|-A_|-echo A fifth_|-run': { - '__run_num__': 4, - 'comment': 'Command "echo A fifth" run', - 'result': True, - 'changes': True}, - 'cmd_|-B_|-echo B third_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo B third" run', - 'result': True, - 'changes': True}, - 'cmd_|-C_|-echo C second_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo C second" run', - 'result': True, - 'changes': True}, - 'cmd_|-D_|-echo D first_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo D first" run', - 'result': True, - 'changes': True}, - 'cmd_|-E_|-echo E fourth_|-run': { - '__run_num__': 3, - 'comment': 'Command "echo E fourth" run', - 'result': True, - 'changes': True} + "cmd_|-A_|-echo A fifth_|-run": { + "__run_num__": 4, + "comment": 'Command "echo A fifth" run', + "result": True, + "changes": True, + }, + "cmd_|-B_|-echo B third_|-run": { + "__run_num__": 2, + "comment": 'Command "echo B third" run', + "result": True, + "changes": True, + }, + "cmd_|-C_|-echo C second_|-run": { + "__run_num__": 1, + "comment": 'Command "echo C second" run', + "result": True, + "changes": True, + }, + "cmd_|-D_|-echo D first_|-run": { + "__run_num__": 0, + "comment": 'Command "echo D first" run', + "result": True, + "changes": True, + }, + "cmd_|-E_|-echo E fourth_|-run": { + "__run_num__": 3, + "comment": 'Command "echo E fourth" run', + "result": True, + "changes": True, + }, } expected_req_use_result = { - 'cmd_|-A_|-echo A_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo A" run', - 'result': True, - 'changes': True}, - 'cmd_|-B_|-echo B_|-run': { - '__run_num__': 4, - 'comment': 'Command "echo B" run', - 'result': True, - 'changes': True}, - 'cmd_|-C_|-echo C_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo C" run', - 'result': True, - 'changes': True}, - 'cmd_|-D_|-echo D_|-run': { - '__run_num__': 5, - 'comment': 'Command "echo D" run', - 'result': True, - 'changes': True}, - 'cmd_|-E_|-echo E_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo E" run', - 'result': True, - 'changes': True}, - 'cmd_|-F_|-echo F_|-run': { - '__run_num__': 3, - 'comment': 'Command "echo F" run', - 'result': True, - 'changes': True} + "cmd_|-A_|-echo A_|-run": { + "__run_num__": 1, + "comment": 'Command "echo A" run', + "result": True, + "changes": True, + }, + "cmd_|-B_|-echo B_|-run": { + "__run_num__": 4, + "comment": 'Command "echo B" run', + "result": True, + "changes": True, + }, + "cmd_|-C_|-echo C_|-run": { + "__run_num__": 0, + "comment": 'Command "echo C" run', + "result": True, + "changes": True, + }, + "cmd_|-D_|-echo D_|-run": { + "__run_num__": 5, + "comment": 'Command "echo D" run', + "result": True, + "changes": True, + }, + "cmd_|-E_|-echo E_|-run": { + "__run_num__": 2, + "comment": 'Command "echo E" run', + "result": True, + "changes": True, + }, + "cmd_|-F_|-echo F_|-run": { + "__run_num__": 3, + "comment": 'Command "echo F" run', + "result": True, + "changes": True, + }, } - ret = self.run_function('state.sls', mods='requisites.mixed_simple') + ret = self.run_function("state.sls", mods="requisites.mixed_simple") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) self.assertEqual(expected_simple_result, result) # test Traceback recursion prereq+require #8785 # TODO: this is actually failing badly - #ret = self.run_function('state.sls', mods='requisites.prereq_require_recursion_error2') - #self.assertEqual( + # ret = self.run_function('state.sls', mods='requisites.prereq_require_recursion_error2') + # self.assertEqual( # ret, # ['A recursive requisite was found, SLS "requisites.prereq_require_recursion_error2" ID "B" ID "A"'] - #) + # ) # test Infinite recursion prereq+require #8785 v2 # TODO: this is actually failing badly - #ret = self.run_function('state.sls', mods='requisites.prereq_require_recursion_error3') - #self.assertEqual( + # ret = self.run_function('state.sls', mods='requisites.prereq_require_recursion_error3') + # self.assertEqual( # ret, # ['A recursive requisite was found, SLS "requisites.prereq_require_recursion_error2" ID "B" ID "A"'] - #) + # ) # test Infinite recursion prereq+require #8785 v3 # TODO: this is actually failing badly, and expected result is maybe not a recursion - #ret = self.run_function('state.sls', mods='requisites.prereq_require_recursion_error4') - #self.assertEqual( + # ret = self.run_function('state.sls', mods='requisites.prereq_require_recursion_error4') + # self.assertEqual( # ret, # ['A recursive requisite was found, SLS "requisites.prereq_require_recursion_error2" ID "B" ID "A"'] - #) + # ) # undetected infinite loopS prevents this test from running... # TODO: this is actually failing badly - #ret = self.run_function('state.sls', mods='requisites.mixed_complex1') - #result = self.normalize_ret(ret) - #self.assertEqual(expected_result, result) + # ret = self.run_function('state.sls', mods='requisites.mixed_complex1') + # result = self.normalize_ret(ret) + # self.assertEqual(expected_result, result) def test_watch_in(self): - ''' + """ test watch_in requisite when there is a success - ''' - ret = self.run_function('state.sls', mods='requisites.watch_in') - changes = 'test_|-return_changes_|-return_changes_|-succeed_with_changes' - watch = 'test_|-watch_states_|-watch_states_|-succeed_without_changes' + """ + ret = self.run_function("state.sls", mods="requisites.watch_in") + changes = "test_|-return_changes_|-return_changes_|-succeed_with_changes" + watch = "test_|-watch_states_|-watch_states_|-succeed_without_changes" - self.assertEqual(ret[changes]['__run_num__'], 0) - self.assertEqual(ret[watch]['__run_num__'], 2) + self.assertEqual(ret[changes]["__run_num__"], 0) + self.assertEqual(ret[watch]["__run_num__"], 2) - self.assertEqual('Watch statement fired.', ret[watch]['comment']) - self.assertEqual('Something pretended to change', - ret[changes]['changes']['testing']['new']) + self.assertEqual("Watch statement fired.", ret[watch]["comment"]) + self.assertEqual( + "Something pretended to change", ret[changes]["changes"]["testing"]["new"] + ) def test_watch_in_failure(self): - ''' + """ test watch_in requisite when there is a failure - ''' - ret = self.run_function('state.sls', mods='requisites.watch_in_failure') - fail = 'test_|-return_changes_|-return_changes_|-fail_with_changes' - watch = 'test_|-watch_states_|-watch_states_|-succeed_without_changes' + """ + ret = self.run_function("state.sls", mods="requisites.watch_in_failure") + fail = "test_|-return_changes_|-return_changes_|-fail_with_changes" + watch = "test_|-watch_states_|-watch_states_|-succeed_without_changes" - self.assertEqual(False, ret[fail]['result']) - self.assertEqual('One or more requisite failed: requisites.watch_in_failure.return_changes', - ret[watch]['comment']) + self.assertEqual(False, ret[fail]["result"]) + self.assertEqual( + "One or more requisite failed: requisites.watch_in_failure.return_changes", + ret[watch]["comment"], + ) def normalize_ret(self, ret): - ''' + """ Normalize the return to the format that we'll use for result checking - ''' + """ result = {} for item, descr in six.iteritems(ret): result[item] = { - '__run_num__': descr['__run_num__'], - 'comment': descr['comment'], - 'result': descr['result'], - 'changes': descr['changes'] != {} # whether there where any changes + "__run_num__": descr["__run_num__"], + "comment": descr["comment"], + "result": descr["result"], + "changes": descr["changes"] != {}, # whether there where any changes } return result def test_requisites_require_ordering_and_errors(self): - ''' + """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. - ''' + """ expected_result = { - 'cmd_|-A_|-echo A fifth_|-run': { - '__run_num__': 4, - 'comment': 'Command "echo A fifth" run', - 'result': True, - 'changes': True, + "cmd_|-A_|-echo A fifth_|-run": { + "__run_num__": 4, + "comment": 'Command "echo A fifth" run', + "result": True, + "changes": True, }, - 'cmd_|-B_|-echo B second_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo B second" run', - 'result': True, - 'changes': True, + "cmd_|-B_|-echo B second_|-run": { + "__run_num__": 1, + "comment": 'Command "echo B second" run', + "result": True, + "changes": True, }, - 'cmd_|-C_|-echo C third_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo C third" run', - 'result': True, - 'changes': True, + "cmd_|-C_|-echo C third_|-run": { + "__run_num__": 2, + "comment": 'Command "echo C third" run', + "result": True, + "changes": True, }, - 'cmd_|-D_|-echo D first_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo D first" run', - 'result': True, - 'changes': True, + "cmd_|-D_|-echo D first_|-run": { + "__run_num__": 0, + "comment": 'Command "echo D first" run', + "result": True, + "changes": True, }, - 'cmd_|-E_|-echo E fourth_|-run': { - '__run_num__': 3, - 'comment': 'Command "echo E fourth" run', - 'result': True, - 'changes': True, + "cmd_|-E_|-echo E fourth_|-run": { + "__run_num__": 3, + "comment": 'Command "echo E fourth" run', + "result": True, + "changes": True, }, - 'cmd_|-F_|-echo F_|-run': { - '__run_num__': 5, - 'comment': 'The following requisites were not found:\n' - + ' require:\n' - + ' foobar: A\n', - 'result': False, - 'changes': False, + "cmd_|-F_|-echo F_|-run": { + "__run_num__": 5, + "comment": "The following requisites were not found:\n" + + " require:\n" + + " foobar: A\n", + "result": False, + "changes": False, }, - 'cmd_|-G_|-echo G_|-run': { - '__run_num__': 6, - 'comment': 'The following requisites were not found:\n' - + ' require:\n' - + ' cmd: Z\n', - 'result': False, - 'changes': False, + "cmd_|-G_|-echo G_|-run": { + "__run_num__": 6, + "comment": "The following requisites were not found:\n" + + " require:\n" + + " cmd: Z\n", + "result": False, + "changes": False, + }, + "cmd_|-H_|-echo H_|-run": { + "__run_num__": 7, + "comment": "The following requisites were not found:\n" + + " require:\n" + + " cmd: Z\n", + "result": False, + "changes": False, }, - 'cmd_|-H_|-echo H_|-run': { - '__run_num__': 7, - 'comment': 'The following requisites were not found:\n' - + ' require:\n' - + ' cmd: Z\n', - 'result': False, - 'changes': False, - } } - ret = self.run_function('state.sls', mods='requisites.require') + ret = self.run_function("state.sls", mods="requisites.require") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) self.assertEqual(expected_result, result) - ret = self.run_function('state.sls', mods='requisites.require_error1') - self.assertEqual(ret, [ - "Cannot extend ID 'W' in 'base:requisites.require_error1'. It is not part of the high state.\nThis is likely due to a missing include statement or an incorrectly typed ID.\nEnsure that a state with an ID of 'W' is available\nin environment 'base' and to SLS 'requisites.require_error1'" - ]) + ret = self.run_function("state.sls", mods="requisites.require_error1") + self.assertEqual( + ret, + [ + "Cannot extend ID 'W' in 'base:requisites.require_error1'. It is not part of the high state.\nThis is likely due to a missing include statement or an incorrectly typed ID.\nEnsure that a state with an ID of 'W' is available\nin environment 'base' and to SLS 'requisites.require_error1'" + ], + ) # issue #8235 # FIXME: Why is require enforcing list syntax while require_in does not? # And why preventing it? # Currently this state fails, should return C/B/A result = {} - ret = self.run_function('state.sls', mods='requisites.require_simple_nolist') - self.assertEqual(ret, [ - 'The require statement in state \'B\' in SLS ' - + '\'requisites.require_simple_nolist\' needs to be formed as a list' - ]) + ret = self.run_function("state.sls", mods="requisites.require_simple_nolist") + self.assertEqual( + ret, + [ + "The require statement in state 'B' in SLS " + + "'requisites.require_simple_nolist' needs to be formed as a list" + ], + ) # commented until a fix is made for issue #8772 # TODO: this test actually fails - #ret = self.run_function('state.sls', mods='requisites.require_error2') - #self.assertEqual(ret, [ + # ret = self.run_function('state.sls', mods='requisites.require_error2') + # self.assertEqual(ret, [ # 'Cannot extend state foobar for ID A in "base:requisites.require_error2".' # + ' It is not part of the high state.' - #]) + # ]) - ret = self.run_function('state.sls', mods='requisites.require_recursion_error1') + ret = self.run_function("state.sls", mods="requisites.require_recursion_error1") self.assertEqual( ret, - ['A recursive requisite was found, SLS "requisites.require_recursion_error1" ID "B" ID "A"'] + [ + 'A recursive requisite was found, SLS "requisites.require_recursion_error1" ID "B" ID "A"' + ], ) def test_requisites_require_any(self): - ''' + """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. - ''' + """ expected_result = { - 'cmd_|-A_|-echo A_|-run': { - '__run_num__': 3, - 'comment': 'Command "echo A" run', - 'result': True, - 'changes': True, + "cmd_|-A_|-echo A_|-run": { + "__run_num__": 3, + "comment": 'Command "echo A" run', + "result": True, + "changes": True, }, - 'cmd_|-B_|-echo B_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo B" run', - 'result': True, - 'changes': True, + "cmd_|-B_|-echo B_|-run": { + "__run_num__": 0, + "comment": 'Command "echo B" run', + "result": True, + "changes": True, }, - 'cmd_|-C_|-$(which false)_|-run': { - '__run_num__': 1, - 'comment': 'Command "$(which false)" run', - 'result': False, - 'changes': True, + "cmd_|-C_|-$(which false)_|-run": { + "__run_num__": 1, + "comment": 'Command "$(which false)" run', + "result": False, + "changes": True, }, - 'cmd_|-D_|-echo D_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo D" run', - 'result': True, - 'changes': True, + "cmd_|-D_|-echo D_|-run": { + "__run_num__": 2, + "comment": 'Command "echo D" run', + "result": True, + "changes": True, }, } - ret = self.run_function('state.sls', mods='requisites.require_any') + ret = self.run_function("state.sls", mods="requisites.require_any") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) self.assertEqual(expected_result, result) def test_requisites_require_any_fail(self): - ''' + """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. - ''' - ret = self.run_function('state.sls', mods='requisites.require_any_fail') + """ + ret = self.run_function("state.sls", mods="requisites.require_any_fail") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) - self.assertIn('One or more requisite failed', - result['cmd_|-D_|-echo D_|-run']['comment']) + self.assertIn( + "One or more requisite failed", result["cmd_|-D_|-echo D_|-run"]["comment"] + ) def test_requisites_watch_any(self): - ''' + """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. - ''' + """ if salt.utils.platform.is_windows(): - cmd_true = 'exit' - cmd_false = 'exit /B 1' + cmd_true = "exit" + cmd_false = "exit /B 1" else: - cmd_true = 'true' - cmd_false = 'false' + cmd_true = "true" + cmd_false = "false" expected_result = { - 'cmd_|-A_|-{0}_|-wait'.format(cmd_true): { - '__run_num__': 4, - 'comment': 'Command "{0}" run'.format(cmd_true), - 'result': True, - 'changes': True, + "cmd_|-A_|-{0}_|-wait".format(cmd_true): { + "__run_num__": 4, + "comment": 'Command "{0}" run'.format(cmd_true), + "result": True, + "changes": True, }, - 'cmd_|-B_|-{0}_|-run'.format(cmd_true): { - '__run_num__': 0, - 'comment': 'Command "{0}" run'.format(cmd_true), - 'result': True, - 'changes': True, + "cmd_|-B_|-{0}_|-run".format(cmd_true): { + "__run_num__": 0, + "comment": 'Command "{0}" run'.format(cmd_true), + "result": True, + "changes": True, }, - 'cmd_|-C_|-{0}_|-run'.format(cmd_false): { - '__run_num__': 1, - 'comment': 'Command "{0}" run'.format(cmd_false), - 'result': False, - 'changes': True, + "cmd_|-C_|-{0}_|-run".format(cmd_false): { + "__run_num__": 1, + "comment": 'Command "{0}" run'.format(cmd_false), + "result": False, + "changes": True, }, - 'cmd_|-D_|-{0}_|-run'.format(cmd_true): { - '__run_num__': 2, - 'comment': 'Command "{0}" run'.format(cmd_true), - 'result': True, - 'changes': True, + "cmd_|-D_|-{0}_|-run".format(cmd_true): { + "__run_num__": 2, + "comment": 'Command "{0}" run'.format(cmd_true), + "result": True, + "changes": True, }, - 'cmd_|-E_|-{0}_|-wait'.format(cmd_true): { - '__run_num__': 9, - 'comment': 'Command "{0}" run'.format(cmd_true), - 'result': True, - 'changes': True, + "cmd_|-E_|-{0}_|-wait".format(cmd_true): { + "__run_num__": 9, + "comment": 'Command "{0}" run'.format(cmd_true), + "result": True, + "changes": True, }, - 'cmd_|-F_|-{0}_|-run'.format(cmd_true): { - '__run_num__': 5, - 'comment': 'Command "{0}" run'.format(cmd_true), - 'result': True, - 'changes': True, + "cmd_|-F_|-{0}_|-run".format(cmd_true): { + "__run_num__": 5, + "comment": 'Command "{0}" run'.format(cmd_true), + "result": True, + "changes": True, }, - 'cmd_|-G_|-{0}_|-run'.format(cmd_false): { - '__run_num__': 6, - 'comment': 'Command "{0}" run'.format(cmd_false), - 'result': False, - 'changes': True, + "cmd_|-G_|-{0}_|-run".format(cmd_false): { + "__run_num__": 6, + "comment": 'Command "{0}" run'.format(cmd_false), + "result": False, + "changes": True, }, - 'cmd_|-H_|-{0}_|-run'.format(cmd_false): { - '__run_num__': 7, - 'comment': 'Command "{0}" run'.format(cmd_false), - 'result': False, - 'changes': True, + "cmd_|-H_|-{0}_|-run".format(cmd_false): { + "__run_num__": 7, + "comment": 'Command "{0}" run'.format(cmd_false), + "result": False, + "changes": True, }, } - ret = self.run_function('state.sls', mods='requisites.watch_any') + ret = self.run_function("state.sls", mods="requisites.watch_any") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) self.assertEqual(expected_result, result) def test_requisites_watch_any_fail(self): - ''' + """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. - ''' - ret = self.run_function('state.sls', mods='requisites.watch_any_fail') + """ + ret = self.run_function("state.sls", mods="requisites.watch_any_fail") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) - self.assertIn('One or more requisite failed', - result['cmd_|-A_|-true_|-wait']['comment']) + self.assertIn( + "One or more requisite failed", result["cmd_|-A_|-true_|-wait"]["comment"] + ) def test_requisites_onchanges_any(self): - ''' + """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. - ''' + """ expected_result = { 'cmd_|-another_changing_state_|-echo "Changed!"_|-run': { - '__run_num__': 1, - 'changes': True, - 'comment': 'Command "echo "Changed!"" run', - 'result': True + "__run_num__": 1, + "changes": True, + "comment": 'Command "echo "Changed!"" run', + "result": True, }, 'cmd_|-changing_state_|-echo "Changed!"_|-run': { - '__run_num__': 0, - 'changes': True, - 'comment': 'Command "echo "Changed!"" run', - 'result': True + "__run_num__": 0, + "changes": True, + "comment": 'Command "echo "Changed!"" run', + "result": True, }, 'cmd_|-test_one_changing_states_|-echo "Success!"_|-run': { - '__run_num__': 4, - 'changes': True, - 'comment': 'Command "echo "Success!"" run', - 'result': True + "__run_num__": 4, + "changes": True, + "comment": 'Command "echo "Success!"" run', + "result": True, }, 'cmd_|-test_two_non_changing_states_|-echo "Should not run"_|-run': { - '__run_num__': 5, - 'changes': False, - 'comment': 'State was not run because none of the onchanges reqs changed', - 'result': True + "__run_num__": 5, + "changes": False, + "comment": "State was not run because none of the onchanges reqs changed", + "result": True, }, - 'pip_|-another_non_changing_state_|-mock_|-installed': { - '__run_num__': 3, - 'changes': False, - 'comment': 'Python package mock was already installed\nAll specified packages are already installed', - 'result': True + "pip_|-another_non_changing_state_|-mock_|-installed": { + "__run_num__": 3, + "changes": False, + "comment": "Python package mock was already installed\nAll specified packages are already installed", + "result": True, + }, + "pip_|-non_changing_state_|-mock_|-installed": { + "__run_num__": 2, + "changes": False, + "comment": "Python package mock was already installed\nAll specified packages are already installed", + "result": True, }, - 'pip_|-non_changing_state_|-mock_|-installed': { - '__run_num__': 2, - 'changes': False, - 'comment': 'Python package mock was already installed\nAll specified packages are already installed', - 'result': True - } } - ret = self.run_function('state.sls', mods='requisites.onchanges_any') + ret = self.run_function("state.sls", mods="requisites.onchanges_any") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) self.assertEqual(expected_result, result) def test_requisites_onfail_any(self): - ''' + """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. - ''' + """ expected_result = { - 'cmd_|-a_|-exit 0_|-run': { - '__run_num__': 0, - 'changes': True, - 'comment': 'Command "exit 0" run', - 'result': True + "cmd_|-a_|-exit 0_|-run": { + "__run_num__": 0, + "changes": True, + "comment": 'Command "exit 0" run', + "result": True, }, - 'cmd_|-b_|-exit 1_|-run': { - '__run_num__': 1, - 'changes': True, - 'comment': 'Command "exit 1" run', - 'result': False + "cmd_|-b_|-exit 1_|-run": { + "__run_num__": 1, + "changes": True, + "comment": 'Command "exit 1" run', + "result": False, }, - 'cmd_|-c_|-exit 0_|-run': { - '__run_num__': 2, - 'changes': True, - 'comment': 'Command "exit 0" run', - 'result': True + "cmd_|-c_|-exit 0_|-run": { + "__run_num__": 2, + "changes": True, + "comment": 'Command "exit 0" run', + "result": True, }, - 'cmd_|-d_|-echo itworked_|-run': { - '__run_num__': 3, - 'changes': True, - 'comment': 'Command "echo itworked" run', - 'result': True}, - 'cmd_|-e_|-exit 0_|-run': { - '__run_num__': 4, - 'changes': True, - 'comment': 'Command "exit 0" run', - 'result': True + "cmd_|-d_|-echo itworked_|-run": { + "__run_num__": 3, + "changes": True, + "comment": 'Command "echo itworked" run', + "result": True, }, - 'cmd_|-f_|-exit 0_|-run': { - '__run_num__': 5, - 'changes': True, - 'comment': 'Command "exit 0" run', - 'result': True + "cmd_|-e_|-exit 0_|-run": { + "__run_num__": 4, + "changes": True, + "comment": 'Command "exit 0" run', + "result": True, }, - 'cmd_|-g_|-exit 0_|-run': { - '__run_num__': 6, - 'changes': True, - 'comment': 'Command "exit 0" run', - 'result': True + "cmd_|-f_|-exit 0_|-run": { + "__run_num__": 5, + "changes": True, + "comment": 'Command "exit 0" run', + "result": True, + }, + "cmd_|-g_|-exit 0_|-run": { + "__run_num__": 6, + "changes": True, + "comment": 'Command "exit 0" run', + "result": True, + }, + "cmd_|-h_|-echo itworked_|-run": { + "__run_num__": 7, + "changes": False, + "comment": "State was not run because onfail req did not change", + "result": True, }, - 'cmd_|-h_|-echo itworked_|-run': { - '__run_num__': 7, - 'changes': False, - 'comment': 'State was not run because onfail req did not change', - 'result': True - } } - ret = self.run_function('state.sls', mods='requisites.onfail_any') + ret = self.run_function("state.sls", mods="requisites.onfail_any") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) self.assertEqual(expected_result, result) def test_requisites_full_sls(self): - ''' + """ Teste the sls special command in requisites - ''' + """ expected_result = { - 'cmd_|-A_|-echo A_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo A" run', - 'result': True, - 'changes': True}, - 'cmd_|-B_|-echo B_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo B" run', - 'result': True, - 'changes': True}, - 'cmd_|-C_|-echo C_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo C" run', - 'result': True, - 'changes': True}, + "cmd_|-A_|-echo A_|-run": { + "__run_num__": 2, + "comment": 'Command "echo A" run', + "result": True, + "changes": True, + }, + "cmd_|-B_|-echo B_|-run": { + "__run_num__": 0, + "comment": 'Command "echo B" run', + "result": True, + "changes": True, + }, + "cmd_|-C_|-echo C_|-run": { + "__run_num__": 1, + "comment": 'Command "echo C" run', + "result": True, + "changes": True, + }, } - ret = self.run_function('state.sls', mods='requisites.fullsls_require') + ret = self.run_function("state.sls", mods="requisites.fullsls_require") self.assertReturnNonEmptySaltType(ret) result = self.normalize_ret(ret) self.assertEqual(expected_result, result) # issue #8233: traceback on prereq sls # TODO: not done - #ret = self.run_function('state.sls', mods='requisites.fullsls_prereq') - #self.assertEqual(['sls command can only be used with require requisite'], ret) + # ret = self.run_function('state.sls', mods='requisites.fullsls_prereq') + # self.assertEqual(['sls command can only be used with require requisite'], ret) def test_requisites_require_no_state_module(self): - ''' + """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. - ''' + """ expected_result = { - 'cmd_|-A_|-echo A fifth_|-run': { - '__run_num__': 4, - 'comment': 'Command "echo A fifth" run', - 'result': True, - 'changes': True, + "cmd_|-A_|-echo A fifth_|-run": { + "__run_num__": 4, + "comment": 'Command "echo A fifth" run', + "result": True, + "changes": True, }, - 'cmd_|-B_|-echo B second_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo B second" run', - 'result': True, - 'changes': True, + "cmd_|-B_|-echo B second_|-run": { + "__run_num__": 1, + "comment": 'Command "echo B second" run', + "result": True, + "changes": True, }, - 'cmd_|-C_|-echo C third_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo C third" run', - 'result': True, - 'changes': True, + "cmd_|-C_|-echo C third_|-run": { + "__run_num__": 2, + "comment": 'Command "echo C third" run', + "result": True, + "changes": True, }, - 'cmd_|-D_|-echo D first_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo D first" run', - 'result': True, - 'changes': True, + "cmd_|-D_|-echo D first_|-run": { + "__run_num__": 0, + "comment": 'Command "echo D first" run', + "result": True, + "changes": True, }, - 'cmd_|-E_|-echo E fourth_|-run': { - '__run_num__': 3, - 'comment': 'Command "echo E fourth" run', - 'result': True, - 'changes': True, + "cmd_|-E_|-echo E fourth_|-run": { + "__run_num__": 3, + "comment": 'Command "echo E fourth" run', + "result": True, + "changes": True, }, - 'cmd_|-G_|-echo G_|-run': { - '__run_num__': 5, - 'comment': 'The following requisites were not found:\n' - + ' require:\n' - + ' id: Z\n', - 'result': False, - 'changes': False, + "cmd_|-G_|-echo G_|-run": { + "__run_num__": 5, + "comment": "The following requisites were not found:\n" + + " require:\n" + + " id: Z\n", + "result": False, + "changes": False, + }, + "cmd_|-H_|-echo H_|-run": { + "__run_num__": 6, + "comment": "The following requisites were not found:\n" + + " require:\n" + + " id: Z\n", + "result": False, + "changes": False, }, - 'cmd_|-H_|-echo H_|-run': { - '__run_num__': 6, - 'comment': 'The following requisites were not found:\n' - + ' require:\n' - + ' id: Z\n', - 'result': False, - 'changes': False, - } } - ret = self.run_function('state.sls', mods='requisites.require_no_state_module') + ret = self.run_function("state.sls", mods="requisites.require_no_state_module") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) self.assertEqual(expected_result, result) def test_requisites_prereq_simple_ordering_and_errors(self): - ''' + """ Call sls file containing several prereq_in and prereq. Ensure that some of them are failing and that the order is right. - ''' + """ expected_result_simple = { - 'cmd_|-A_|-echo A third_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo A third" run', - 'result': True, - 'changes': True}, - 'cmd_|-B_|-echo B first_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo B first" run', - 'result': True, - 'changes': True}, - 'cmd_|-C_|-echo C second_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo C second" run', - 'result': True, - 'changes': True}, - 'cmd_|-I_|-echo I_|-run': { - '__run_num__': 3, - 'comment': 'The following requisites were not found:\n' - + ' prereq:\n' - + ' cmd: Z\n', - 'result': False, - 'changes': False}, - 'cmd_|-J_|-echo J_|-run': { - '__run_num__': 4, - 'comment': 'The following requisites were not found:\n' - + ' prereq:\n' - + ' foobar: A\n', - 'result': False, - 'changes': False} + "cmd_|-A_|-echo A third_|-run": { + "__run_num__": 2, + "comment": 'Command "echo A third" run', + "result": True, + "changes": True, + }, + "cmd_|-B_|-echo B first_|-run": { + "__run_num__": 0, + "comment": 'Command "echo B first" run', + "result": True, + "changes": True, + }, + "cmd_|-C_|-echo C second_|-run": { + "__run_num__": 1, + "comment": 'Command "echo C second" run', + "result": True, + "changes": True, + }, + "cmd_|-I_|-echo I_|-run": { + "__run_num__": 3, + "comment": "The following requisites were not found:\n" + + " prereq:\n" + + " cmd: Z\n", + "result": False, + "changes": False, + }, + "cmd_|-J_|-echo J_|-run": { + "__run_num__": 4, + "comment": "The following requisites were not found:\n" + + " prereq:\n" + + " foobar: A\n", + "result": False, + "changes": False, + }, } expected_result_simple_no_state_module = { - 'cmd_|-A_|-echo A third_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo A third" run', - 'result': True, - 'changes': True}, - 'cmd_|-B_|-echo B first_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo B first" run', - 'result': True, - 'changes': True}, - 'cmd_|-C_|-echo C second_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo C second" run', - 'result': True, - 'changes': True}, - 'cmd_|-I_|-echo I_|-run': { - '__run_num__': 3, - 'comment': 'The following requisites were not found:\n' - + ' prereq:\n' - + ' id: Z\n', - 'result': False, - 'changes': False} + "cmd_|-A_|-echo A third_|-run": { + "__run_num__": 2, + "comment": 'Command "echo A third" run', + "result": True, + "changes": True, + }, + "cmd_|-B_|-echo B first_|-run": { + "__run_num__": 0, + "comment": 'Command "echo B first" run', + "result": True, + "changes": True, + }, + "cmd_|-C_|-echo C second_|-run": { + "__run_num__": 1, + "comment": 'Command "echo C second" run', + "result": True, + "changes": True, + }, + "cmd_|-I_|-echo I_|-run": { + "__run_num__": 3, + "comment": "The following requisites were not found:\n" + + " prereq:\n" + + " id: Z\n", + "result": False, + "changes": False, + }, } expected_result_simple2 = { - 'cmd_|-A_|-echo A_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo A" run', - 'result': True, - 'changes': True}, - 'cmd_|-B_|-echo B_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo B" run', - 'result': True, - 'changes': True}, - 'cmd_|-C_|-echo C_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo C" run', - 'result': True, - 'changes': True}, - 'cmd_|-D_|-echo D_|-run': { - '__run_num__': 3, - 'comment': 'Command "echo D" run', - 'result': True, - 'changes': True}, - 'cmd_|-E_|-echo E_|-run': { - '__run_num__': 4, - 'comment': 'Command "echo E" run', - 'result': True, - 'changes': True} + "cmd_|-A_|-echo A_|-run": { + "__run_num__": 1, + "comment": 'Command "echo A" run', + "result": True, + "changes": True, + }, + "cmd_|-B_|-echo B_|-run": { + "__run_num__": 2, + "comment": 'Command "echo B" run', + "result": True, + "changes": True, + }, + "cmd_|-C_|-echo C_|-run": { + "__run_num__": 0, + "comment": 'Command "echo C" run', + "result": True, + "changes": True, + }, + "cmd_|-D_|-echo D_|-run": { + "__run_num__": 3, + "comment": 'Command "echo D" run', + "result": True, + "changes": True, + }, + "cmd_|-E_|-echo E_|-run": { + "__run_num__": 4, + "comment": 'Command "echo E" run', + "result": True, + "changes": True, + }, } expected_result_simple3 = { - 'cmd_|-A_|-echo A first_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo A first" run', - 'result': True, - 'changes': True, + "cmd_|-A_|-echo A first_|-run": { + "__run_num__": 0, + "comment": 'Command "echo A first" run', + "result": True, + "changes": True, }, - 'cmd_|-B_|-echo B second_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo B second" run', - 'result': True, - 'changes': True, + "cmd_|-B_|-echo B second_|-run": { + "__run_num__": 1, + "comment": 'Command "echo B second" run', + "result": True, + "changes": True, + }, + "cmd_|-C_|-echo C third_|-wait": { + "__run_num__": 2, + "comment": "", + "result": True, + "changes": False, }, - 'cmd_|-C_|-echo C third_|-wait': { - '__run_num__': 2, - 'comment': '', - 'result': True, - 'changes': False, - } } expected_result_complex = { - 'cmd_|-A_|-echo A fourth_|-run': { - '__run_num__': 3, - 'comment': 'Command "echo A fourth" run', - 'result': True, - 'changes': True}, - 'cmd_|-B_|-echo B first_|-run': { - '__run_num__': 0, - 'comment': 'Command "echo B first" run', - 'result': True, - 'changes': True}, - 'cmd_|-C_|-echo C second_|-run': { - '__run_num__': 1, - 'comment': 'Command "echo C second" run', - 'result': True, - 'changes': True}, - 'cmd_|-D_|-echo D third_|-run': { - '__run_num__': 2, - 'comment': 'Command "echo D third" run', - 'result': True, - 'changes': True}, + "cmd_|-A_|-echo A fourth_|-run": { + "__run_num__": 3, + "comment": 'Command "echo A fourth" run', + "result": True, + "changes": True, + }, + "cmd_|-B_|-echo B first_|-run": { + "__run_num__": 0, + "comment": 'Command "echo B first" run', + "result": True, + "changes": True, + }, + "cmd_|-C_|-echo C second_|-run": { + "__run_num__": 1, + "comment": 'Command "echo C second" run', + "result": True, + "changes": True, + }, + "cmd_|-D_|-echo D third_|-run": { + "__run_num__": 2, + "comment": 'Command "echo D third" run', + "result": True, + "changes": True, + }, } - ret = self.run_function('state.sls', mods='requisites.prereq_simple') + ret = self.run_function("state.sls", mods="requisites.prereq_simple") self.assertReturnNonEmptySaltType(ret) result = self.normalize_ret(ret) self.assertEqual(expected_result_simple, result) @@ -1262,259 +1306,237 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): # TODO: issue #8235, prereq ignored when not used in list syntax # Currently fails badly with : # TypeError encountered executing state.sls: string indices must be integers, not str. - #expected_result_simple.pop('cmd_|-I_|-echo I_|-run') - #expected_result_simple.pop('cmd_|-J_|-echo J_|-run') - #ret = self.run_function('state.sls', mods='requisites.prereq_simple_nolist') - #result = self.normalize_ret(ret) - #self.assertEqual(expected_result_simple, result) + # expected_result_simple.pop('cmd_|-I_|-echo I_|-run') + # expected_result_simple.pop('cmd_|-J_|-echo J_|-run') + # ret = self.run_function('state.sls', mods='requisites.prereq_simple_nolist') + # result = self.normalize_ret(ret) + # self.assertEqual(expected_result_simple, result) - ret = self.run_function('state.sls', mods='requisites.prereq_simple2') + ret = self.run_function("state.sls", mods="requisites.prereq_simple2") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) self.assertEqual(expected_result_simple2, result) - ret = self.run_function('state.sls', mods='requisites.prereq_simple3') + ret = self.run_function("state.sls", mods="requisites.prereq_simple3") result = self.normalize_ret(ret) self.assertReturnNonEmptySaltType(ret) self.assertEqual(expected_result_simple3, result) - #ret = self.run_function('state.sls', mods='requisites.prereq_error_nolist') - #self.assertEqual( + # ret = self.run_function('state.sls', mods='requisites.prereq_error_nolist') + # self.assertEqual( # ret, # ['Cannot extend ID Z in "base:requisites.prereq_error_nolist".' # + ' It is not part of the high state.'] - #) + # ) - ret = self.run_function('state.sls', mods='requisites.prereq_compile_error1') + ret = self.run_function("state.sls", mods="requisites.prereq_compile_error1") self.assertReturnNonEmptySaltType(ret) self.assertEqual( - ret['cmd_|-B_|-echo B_|-run']['comment'], - 'The following requisites were not found:\n' - + ' prereq:\n' - + ' foobar: A\n' + ret["cmd_|-B_|-echo B_|-run"]["comment"], + "The following requisites were not found:\n" + + " prereq:\n" + + " foobar: A\n", ) - ret = self.run_function('state.sls', mods='requisites.prereq_compile_error2') + ret = self.run_function("state.sls", mods="requisites.prereq_compile_error2") self.assertReturnNonEmptySaltType(ret) self.assertEqual( - ret['cmd_|-B_|-echo B_|-run']['comment'], - 'The following requisites were not found:\n' - + ' prereq:\n' - + ' foobar: C\n' + ret["cmd_|-B_|-echo B_|-run"]["comment"], + "The following requisites were not found:\n" + + " prereq:\n" + + " foobar: C\n", ) - ret = self.run_function('state.sls', mods='requisites.prereq_complex') + ret = self.run_function("state.sls", mods="requisites.prereq_complex") result = self.normalize_ret(ret) self.assertEqual(expected_result_complex, result) # issue #8210 : prereq recursion undetected # TODO: this test fails - #ret = self.run_function('state.sls', mods='requisites.prereq_recursion_error') - #self.assertEqual( + # ret = self.run_function('state.sls', mods='requisites.prereq_recursion_error') + # self.assertEqual( # ret, # ['A recursive requisite was found, SLS "requisites.prereq_recursion_error" ID "B" ID "A"'] - #) + # ) - ret = self.run_function('state.sls', mods='requisites.prereq_simple_no_state_module') + ret = self.run_function( + "state.sls", mods="requisites.prereq_simple_no_state_module" + ) result = self.normalize_ret(ret) self.assertEqual(expected_result_simple_no_state_module, result) def test_infinite_recursion_sls_prereq(self): - ret = self.run_function('state.sls', mods='requisites.prereq_sls_infinite_recursion') + ret = self.run_function( + "state.sls", mods="requisites.prereq_sls_infinite_recursion" + ) self.assertSaltTrueReturn(ret) def test_requisites_use(self): - ''' + """ Call sls file containing several use_in and use. - ''' + """ # TODO issue #8235 & #8774 some examples are still commented in the test file - ret = self.run_function('state.sls', mods='requisites.use') + ret = self.run_function("state.sls", mods="requisites.use") self.assertReturnNonEmptySaltType(ret) for item, descr in six.iteritems(ret): - self.assertEqual(descr['comment'], 'onlyif condition is false') + self.assertEqual(descr["comment"], "onlyif condition is false") # TODO: issue #8802 : use recursions undetected # issue is closed as use does not actually inherit requisites # if chain-use is added after #8774 resolution theses tests would maybe become useful - #ret = self.run_function('state.sls', mods='requisites.use_recursion') - #self.assertEqual(ret, [ + # ret = self.run_function('state.sls', mods='requisites.use_recursion') + # self.assertEqual(ret, [ # 'A recursive requisite was found, SLS "requisites.use_recursion"' # + ' ID "B" ID "A"' - #]) + # ]) - #ret = self.run_function('state.sls', mods='requisites.use_recursion2') - #self.assertEqual(ret, [ + # ret = self.run_function('state.sls', mods='requisites.use_recursion2') + # self.assertEqual(ret, [ # 'A recursive requisite was found, SLS "requisites.use_recursion2"' # + ' ID "C" ID "A"' - #]) + # ]) - #ret = self.run_function('state.sls', mods='requisites.use_auto_recursion') - #self.assertEqual(ret, [ + # ret = self.run_function('state.sls', mods='requisites.use_auto_recursion') + # self.assertEqual(ret, [ # 'A recursive requisite was found, SLS "requisites.use_recursion"' # + ' ID "A" ID "A"' - #]) + # ]) def test_requisites_use_no_state_module(self): - ''' + """ Call sls file containing several use_in and use. - ''' - ret = self.run_function('state.sls', mods='requisites.use_no_state_module') + """ + ret = self.run_function("state.sls", mods="requisites.use_no_state_module") self.assertReturnNonEmptySaltType(ret) for item, descr in six.iteritems(ret): - self.assertEqual(descr['comment'], 'onlyif condition is false') + self.assertEqual(descr["comment"], "onlyif condition is false") def test_onlyif_req(self): ret = self.run_function( - 'state.single', - fun='test.succeed_with_changes', - name='onlyif test', - onlyif=[ - {} - ], - )['test_|-onlyif test_|-onlyif test_|-succeed_with_changes'] - self.assertTrue(ret['result']) - self.assertEqual(ret['comment'], 'Success!') + "state.single", + fun="test.succeed_with_changes", + name="onlyif test", + onlyif=[{}], + )["test_|-onlyif test_|-onlyif test_|-succeed_with_changes"] + self.assertTrue(ret["result"]) + self.assertEqual(ret["comment"], "Success!") ret = self.run_function( - 'state.single', - fun='test.fail_with_changes', - name='onlyif test', - onlyif=[ - {'fun': 'test.false'}, - ], - )['test_|-onlyif test_|-onlyif test_|-fail_with_changes'] - self.assertTrue(ret['result']) - self.assertFalse(ret['changes']) - self.assertEqual(ret['comment'], 'onlyif condition is false') + "state.single", + fun="test.fail_with_changes", + name="onlyif test", + onlyif=[{"fun": "test.false"}], + )["test_|-onlyif test_|-onlyif test_|-fail_with_changes"] + self.assertTrue(ret["result"]) + self.assertFalse(ret["changes"]) + self.assertEqual(ret["comment"], "onlyif condition is false") ret = self.run_function( - 'state.single', - fun='test.fail_with_changes', - name='onlyif test', - onlyif=[ - {'fun': 'test.true'}, - ], - )['test_|-onlyif test_|-onlyif test_|-fail_with_changes'] - self.assertFalse(ret['result']) - self.assertTrue(ret['changes']) - self.assertEqual(ret['comment'], 'Failure!') + "state.single", + fun="test.fail_with_changes", + name="onlyif test", + onlyif=[{"fun": "test.true"}], + )["test_|-onlyif test_|-onlyif test_|-fail_with_changes"] + self.assertFalse(ret["result"]) + self.assertTrue(ret["changes"]) + self.assertEqual(ret["comment"], "Failure!") ret = self.run_function( - 'state.single', - fun='test.succeed_without_changes', - name='onlyif test', - onlyif=[ - {'fun': 'test.true'}, - ], - )['test_|-onlyif test_|-onlyif test_|-succeed_without_changes'] - self.assertTrue(ret['result']) - self.assertFalse(ret['changes']) - self.assertEqual(ret['comment'], 'Success!') + "state.single", + fun="test.succeed_without_changes", + name="onlyif test", + onlyif=[{"fun": "test.true"}], + )["test_|-onlyif test_|-onlyif test_|-succeed_without_changes"] + self.assertTrue(ret["result"]) + self.assertFalse(ret["changes"]) + self.assertEqual(ret["comment"], "Success!") def test_onlyif_req_retcode(self): ret = self.run_function( - 'state.single', - fun='test.succeed_with_changes', - name='onlyif test', - onlyif=[ - {'fun': 'test.retcode'}, - ], - )['test_|-onlyif test_|-onlyif test_|-succeed_with_changes'] - self.assertTrue(ret['result']) - self.assertFalse(ret['changes']) - self.assertEqual(ret['comment'], 'onlyif condition is false') + "state.single", + fun="test.succeed_with_changes", + name="onlyif test", + onlyif=[{"fun": "test.retcode"}], + )["test_|-onlyif test_|-onlyif test_|-succeed_with_changes"] + self.assertTrue(ret["result"]) + self.assertFalse(ret["changes"]) + self.assertEqual(ret["comment"], "onlyif condition is false") ret = self.run_function( - 'state.single', - fun='test.succeed_with_changes', - name='onlyif test', - onlyif=[ - {'fun': 'test.retcode', 'code': 0}, - ], - )['test_|-onlyif test_|-onlyif test_|-succeed_with_changes'] - self.assertTrue(ret['result']) - self.assertTrue(ret['changes']) - self.assertEqual(ret['comment'], 'Success!') + "state.single", + fun="test.succeed_with_changes", + name="onlyif test", + onlyif=[{"fun": "test.retcode", "code": 0}], + )["test_|-onlyif test_|-onlyif test_|-succeed_with_changes"] + self.assertTrue(ret["result"]) + self.assertTrue(ret["changes"]) + self.assertEqual(ret["comment"], "Success!") def test_unless_req(self): ret = self.run_function( - 'state.single', - fun='test.succeed_with_changes', - name='unless test', - unless=[ - {} - ], - )['test_|-unless test_|-unless test_|-succeed_with_changes'] - self.assertTrue(ret['result']) - self.assertEqual(ret['comment'], 'Success!') + "state.single", + fun="test.succeed_with_changes", + name="unless test", + unless=[{}], + )["test_|-unless test_|-unless test_|-succeed_with_changes"] + self.assertTrue(ret["result"]) + self.assertEqual(ret["comment"], "Success!") ret = self.run_function( - 'state.single', - fun='test.fail_with_changes', - name='unless test', - unless=[ - {'fun': 'test.true'}, - ], - )['test_|-unless test_|-unless test_|-fail_with_changes'] - self.assertTrue(ret['result']) - self.assertFalse(ret['changes']) - self.assertEqual(ret['comment'], 'unless condition is true') + "state.single", + fun="test.fail_with_changes", + name="unless test", + unless=[{"fun": "test.true"}], + )["test_|-unless test_|-unless test_|-fail_with_changes"] + self.assertTrue(ret["result"]) + self.assertFalse(ret["changes"]) + self.assertEqual(ret["comment"], "unless condition is true") ret = self.run_function( - 'state.single', - fun='test.fail_with_changes', - name='unless test', - unless=[ - {'fun': 'test.false'}, - ], - )['test_|-unless test_|-unless test_|-fail_with_changes'] - self.assertFalse(ret['result']) - self.assertTrue(ret['changes']) - self.assertEqual(ret['comment'], 'Failure!') + "state.single", + fun="test.fail_with_changes", + name="unless test", + unless=[{"fun": "test.false"}], + )["test_|-unless test_|-unless test_|-fail_with_changes"] + self.assertFalse(ret["result"]) + self.assertTrue(ret["changes"]) + self.assertEqual(ret["comment"], "Failure!") ret = self.run_function( - 'state.single', - fun='test.succeed_without_changes', - name='unless test', - unless=[ - {'fun': 'test.false'}, - ], - )['test_|-unless test_|-unless test_|-succeed_without_changes'] - self.assertTrue(ret['result']) - self.assertFalse(ret['changes']) - self.assertEqual(ret['comment'], 'Success!') + "state.single", + fun="test.succeed_without_changes", + name="unless test", + unless=[{"fun": "test.false"}], + )["test_|-unless test_|-unless test_|-succeed_without_changes"] + self.assertTrue(ret["result"]) + self.assertFalse(ret["changes"]) + self.assertEqual(ret["comment"], "Success!") def test_unless_req_retcode(self): ret = self.run_function( - 'state.single', - fun='test.succeed_with_changes', - name='unless test', - unless=[ - {'fun': 'test.retcode'}, - ], - )['test_|-unless test_|-unless test_|-succeed_with_changes'] - self.assertTrue(ret['result']) - self.assertTrue(ret['changes']) - self.assertEqual(ret['comment'], 'Success!') + "state.single", + fun="test.succeed_with_changes", + name="unless test", + unless=[{"fun": "test.retcode"}], + )["test_|-unless test_|-unless test_|-succeed_with_changes"] + self.assertTrue(ret["result"]) + self.assertTrue(ret["changes"]) + self.assertEqual(ret["comment"], "Success!") ret = self.run_function( - 'state.single', - fun='test.succeed_with_changes', - name='unless test', - unless=[ - {'fun': 'test.retcode', 'code': 0}, - ], - )['test_|-unless test_|-unless test_|-succeed_with_changes'] - self.assertTrue(ret['result']) - self.assertFalse(ret['changes']) - self.assertEqual(ret['comment'], 'unless condition is true') + "state.single", + fun="test.succeed_with_changes", + name="unless test", + unless=[{"fun": "test.retcode", "code": 0}], + )["test_|-unless test_|-unless test_|-succeed_with_changes"] + self.assertTrue(ret["result"]) + self.assertFalse(ret["changes"]) + self.assertEqual(ret["comment"], "unless condition is true") def test_get_file_from_env_in_top_match(self): - tgt = os.path.join(RUNTIME_VARS.TMP, 'prod-cheese-file') + tgt = os.path.join(RUNTIME_VARS.TMP, "prod-cheese-file") try: - ret = self.run_function( - 'state.highstate', minion_tgt='sub_minion' - ) + ret = self.run_function("state.highstate", minion_tgt="sub_minion") self.assertSaltTrueReturn(ret) self.assertTrue(os.path.isfile(tgt)) - with salt.utils.files.fopen(tgt, 'r') as cheese: + with salt.utils.files.fopen(tgt, "r") as cheese: data = salt.utils.stringutils.to_unicode(cheese.read()) - self.assertIn('Gromit', data) - self.assertIn('Comte', data) + self.assertIn("Gromit", data) + self.assertIn("Comte", data) finally: if os.path.islink(tgt): os.unlink(tgt) @@ -1522,233 +1544,274 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): # onchanges tests def test_onchanges_requisite(self): - ''' + """ Tests a simple state using the onchanges requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.onchanges_simple') + state_run = self.run_function("state.sls", mods="requisites.onchanges_simple") # First, test the result of the state run when changes are expected to happen - test_data = state_run['cmd_|-test_changing_state_|-echo "Success!"_|-run']['comment'] + test_data = state_run['cmd_|-test_changing_state_|-echo "Success!"_|-run'][ + "comment" + ] expected_result = 'Command "echo "Success!"" run' self.assertIn(expected_result, test_data) # Then, test the result of the state run when changes are not expected to happen - test_data = state_run['cmd_|-test_non_changing_state_|-echo "Should not run"_|-run']['comment'] - expected_result = 'State was not run because none of the onchanges reqs changed' + test_data = state_run[ + 'cmd_|-test_non_changing_state_|-echo "Should not run"_|-run' + ]["comment"] + expected_result = "State was not run because none of the onchanges reqs changed" self.assertIn(expected_result, test_data) def test_onchanges_requisite_multiple(self): - ''' + """ Tests a simple state using the onchanges requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', - mods='requisites.onchanges_multiple') + state_run = self.run_function("state.sls", mods="requisites.onchanges_multiple") # First, test the result of the state run when two changes are expected to happen - test_data = state_run['cmd_|-test_two_changing_states_|-echo "Success!"_|-run']['comment'] + test_data = state_run['cmd_|-test_two_changing_states_|-echo "Success!"_|-run'][ + "comment" + ] expected_result = 'Command "echo "Success!"" run' self.assertIn(expected_result, test_data) # Then, test the result of the state run when two changes are not expected to happen - test_data = state_run['cmd_|-test_two_non_changing_states_|-echo "Should not run"_|-run']['comment'] - expected_result = 'State was not run because none of the onchanges reqs changed' + test_data = state_run[ + 'cmd_|-test_two_non_changing_states_|-echo "Should not run"_|-run' + ]["comment"] + expected_result = "State was not run because none of the onchanges reqs changed" self.assertIn(expected_result, test_data) # Finally, test the result of the state run when only one of the onchanges requisites changes. - test_data = state_run['cmd_|-test_one_changing_state_|-echo "Success!"_|-run']['comment'] + test_data = state_run['cmd_|-test_one_changing_state_|-echo "Success!"_|-run'][ + "comment" + ] expected_result = 'Command "echo "Success!"" run' self.assertIn(expected_result, test_data) def test_onchanges_in_requisite(self): - ''' + """ Tests a simple state using the onchanges_in requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.onchanges_in_simple') + state_run = self.run_function( + "state.sls", mods="requisites.onchanges_in_simple" + ) # First, test the result of the state run of when changes are expected to happen - test_data = state_run['cmd_|-test_changes_expected_|-echo "Success!"_|-run']['comment'] + test_data = state_run['cmd_|-test_changes_expected_|-echo "Success!"_|-run'][ + "comment" + ] expected_result = 'Command "echo "Success!"" run' self.assertIn(expected_result, test_data) # Then, test the result of the state run when changes are not expected to happen - test_data = state_run['cmd_|-test_changes_not_expected_|-echo "Should not run"_|-run']['comment'] - expected_result = 'State was not run because none of the onchanges reqs changed' + test_data = state_run[ + 'cmd_|-test_changes_not_expected_|-echo "Should not run"_|-run' + ]["comment"] + expected_result = "State was not run because none of the onchanges reqs changed" self.assertIn(expected_result, test_data) def test_onchanges_requisite_no_state_module(self): - ''' + """ Tests a simple state using the onchanges requisite without state modules - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.onchanges_simple_no_state_module') - test_data = state_run['cmd_|-test_changing_state_|-echo "Success!"_|-run']['comment'] + state_run = self.run_function( + "state.sls", mods="requisites.onchanges_simple_no_state_module" + ) + test_data = state_run['cmd_|-test_changing_state_|-echo "Success!"_|-run'][ + "comment" + ] expected_result = 'Command "echo "Success!"" run' self.assertIn(expected_result, test_data) def test_onchanges_requisite_with_duration(self): - ''' + """ Tests a simple state using the onchanges requisite the state will not run but results will include duration - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.onchanges_simple') + state_run = self.run_function("state.sls", mods="requisites.onchanges_simple") # Then, test the result of the state run when changes are not expected to happen # and ensure duration is included in the results - test_data = state_run['cmd_|-test_non_changing_state_|-echo "Should not run"_|-run'] - self.assertIn('duration', test_data) + test_data = state_run[ + 'cmd_|-test_non_changing_state_|-echo "Should not run"_|-run' + ] + self.assertIn("duration", test_data) # onfail tests def test_onfail_requisite(self): - ''' + """ Tests a simple state using the onfail requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.onfail_simple') + state_run = self.run_function("state.sls", mods="requisites.onfail_simple") # First, test the result of the state run when a failure is expected to happen - test_data = state_run['cmd_|-test_failing_state_|-echo "Success!"_|-run']['comment'] + test_data = state_run['cmd_|-test_failing_state_|-echo "Success!"_|-run'][ + "comment" + ] expected_result = 'Command "echo "Success!"" run' self.assertIn(expected_result, test_data) # Then, test the result of the state run when a failure is not expected to happen - test_data = state_run['cmd_|-test_non_failing_state_|-echo "Should not run"_|-run']['comment'] - expected_result = 'State was not run because onfail req did not change' + test_data = state_run[ + 'cmd_|-test_non_failing_state_|-echo "Should not run"_|-run' + ]["comment"] + expected_result = "State was not run because onfail req did not change" self.assertIn(expected_result, test_data) def test_multiple_onfail_requisite(self): - ''' + """ test to ensure state is run even if only one of the onfails fails. This is a test for the issue: https://github.com/saltstack/salt/issues/22370 - ''' + """ - state_run = self.run_function('state.sls', - mods='requisites.onfail_multiple', - timeout=self.TIMEOUT) + state_run = self.run_function( + "state.sls", mods="requisites.onfail_multiple", timeout=self.TIMEOUT + ) - retcode = state_run['cmd_|-c_|-echo itworked_|-run']['changes']['retcode'] + retcode = state_run["cmd_|-c_|-echo itworked_|-run"]["changes"]["retcode"] self.assertEqual(retcode, 0) - stdout = state_run['cmd_|-c_|-echo itworked_|-run']['changes']['stdout'] - self.assertEqual(stdout, 'itworked') + stdout = state_run["cmd_|-c_|-echo itworked_|-run"]["changes"]["stdout"] + self.assertEqual(stdout, "itworked") def test_onfail_in_requisite(self): - ''' + """ Tests a simple state using the onfail_in requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.onfail_in_simple') + state_run = self.run_function("state.sls", mods="requisites.onfail_in_simple") # First, test the result of the state run when a failure is expected to happen - test_data = state_run['cmd_|-test_failing_state_|-echo "Success!"_|-run']['comment'] + test_data = state_run['cmd_|-test_failing_state_|-echo "Success!"_|-run'][ + "comment" + ] expected_result = 'Command "echo "Success!"" run' self.assertIn(expected_result, test_data) # Then, test the result of the state run when a failure is not expected to happen - test_data = state_run['cmd_|-test_non_failing_state_|-echo "Should not run"_|-run']['comment'] - expected_result = 'State was not run because onfail req did not change' + test_data = state_run[ + 'cmd_|-test_non_failing_state_|-echo "Should not run"_|-run' + ]["comment"] + expected_result = "State was not run because onfail req did not change" self.assertIn(expected_result, test_data) def test_onfail_requisite_no_state_module(self): - ''' + """ Tests a simple state using the onfail requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.onfail_simple_no_state_module') + state_run = self.run_function( + "state.sls", mods="requisites.onfail_simple_no_state_module" + ) # First, test the result of the state run when a failure is expected to happen - test_data = state_run['cmd_|-test_failing_state_|-echo "Success!"_|-run']['comment'] + test_data = state_run['cmd_|-test_failing_state_|-echo "Success!"_|-run'][ + "comment" + ] expected_result = 'Command "echo "Success!"" run' self.assertIn(expected_result, test_data) # Then, test the result of the state run when a failure is not expected to happen - test_data = state_run['cmd_|-test_non_failing_state_|-echo "Should not run"_|-run']['comment'] - expected_result = 'State was not run because onfail req did not change' + test_data = state_run[ + 'cmd_|-test_non_failing_state_|-echo "Should not run"_|-run' + ]["comment"] + expected_result = "State was not run because onfail req did not change" self.assertIn(expected_result, test_data) def test_onfail_requisite_with_duration(self): - ''' + """ Tests a simple state using the onfail requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.onfail_simple') + state_run = self.run_function("state.sls", mods="requisites.onfail_simple") # Then, test the result of the state run when a failure is not expected to happen - test_data = state_run['cmd_|-test_non_failing_state_|-echo "Should not run"_|-run'] - self.assertIn('duration', test_data) + test_data = state_run[ + 'cmd_|-test_non_failing_state_|-echo "Should not run"_|-run' + ] + self.assertIn("duration", test_data) def test_multiple_onfail_requisite_with_required(self): - ''' + """ test to ensure multiple states are run when specified as onfails for a single state. This is a test for the issue: https://github.com/saltstack/salt/issues/46552 - ''' + """ - state_run = self.run_function('state.sls', mods='requisites.onfail_multiple_required') + state_run = self.run_function( + "state.sls", mods="requisites.onfail_multiple_required" + ) - retcode = state_run['cmd_|-b_|-echo b_|-run']['changes']['retcode'] + retcode = state_run["cmd_|-b_|-echo b_|-run"]["changes"]["retcode"] self.assertEqual(retcode, 0) - retcode = state_run['cmd_|-c_|-echo c_|-run']['changes']['retcode'] + retcode = state_run["cmd_|-c_|-echo c_|-run"]["changes"]["retcode"] self.assertEqual(retcode, 0) - retcode = state_run['cmd_|-d_|-echo d_|-run']['changes']['retcode'] + retcode = state_run["cmd_|-d_|-echo d_|-run"]["changes"]["retcode"] self.assertEqual(retcode, 0) - stdout = state_run['cmd_|-b_|-echo b_|-run']['changes']['stdout'] - self.assertEqual(stdout, 'b') + stdout = state_run["cmd_|-b_|-echo b_|-run"]["changes"]["stdout"] + self.assertEqual(stdout, "b") - stdout = state_run['cmd_|-c_|-echo c_|-run']['changes']['stdout'] - self.assertEqual(stdout, 'c') + stdout = state_run["cmd_|-c_|-echo c_|-run"]["changes"]["stdout"] + self.assertEqual(stdout, "c") - stdout = state_run['cmd_|-d_|-echo d_|-run']['changes']['stdout'] - self.assertEqual(stdout, 'd') + stdout = state_run["cmd_|-d_|-echo d_|-run"]["changes"]["stdout"] + self.assertEqual(stdout, "d") def test_multiple_onfail_requisite_with_required_no_run(self): - ''' + """ test to ensure multiple states are not run when specified as onfails for a single state which fails. This is a test for the issue: https://github.com/saltstack/salt/issues/46552 - ''' + """ - state_run = self.run_function('state.sls', mods='requisites.onfail_multiple_required_no_run') + state_run = self.run_function( + "state.sls", mods="requisites.onfail_multiple_required_no_run" + ) - expected = 'State was not run because onfail req did not change' + expected = "State was not run because onfail req did not change" - stdout = state_run['cmd_|-b_|-echo b_|-run']['comment'] + stdout = state_run["cmd_|-b_|-echo b_|-run"]["comment"] self.assertEqual(stdout, expected) - stdout = state_run['cmd_|-c_|-echo c_|-run']['comment'] + stdout = state_run["cmd_|-c_|-echo c_|-run"]["comment"] self.assertEqual(stdout, expected) - stdout = state_run['cmd_|-d_|-echo d_|-run']['comment'] + stdout = state_run["cmd_|-d_|-echo d_|-run"]["comment"] self.assertEqual(stdout, expected) # listen tests def test_listen_requisite(self): - ''' + """ Tests a simple state using the listen requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.listen_simple') + state_run = self.run_function("state.sls", mods="requisites.listen_simple") # First, test the result of the state run when a listener is expected to trigger listener_state = 'cmd_|-listener_test_listening_change_state_|-echo "Listening State"_|-mod_watch' @@ -1759,12 +1822,12 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): self.assertNotIn(absent_state, state_run) def test_listen_in_requisite(self): - ''' + """ Tests a simple state using the listen_in requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.listen_in_simple') + state_run = self.run_function("state.sls", mods="requisites.listen_in_simple") # First, test the result of the state run when a listener is expected to trigger listener_state = 'cmd_|-listener_test_listening_change_state_|-echo "Listening State"_|-mod_watch' @@ -1775,24 +1838,24 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): self.assertNotIn(absent_state, state_run) def test_listen_in_requisite_resolution(self): - ''' + """ Verify listen_in requisite lookups use ID declaration to check for changes - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.listen_in_simple') + state_run = self.run_function("state.sls", mods="requisites.listen_in_simple") # Test the result of the state run when a listener is expected to trigger listener_state = 'cmd_|-listener_test_listen_in_resolution_|-echo "Successful listen_in resolution"_|-mod_watch' self.assertIn(listener_state, state_run) def test_listen_requisite_resolution(self): - ''' + """ Verify listen requisite lookups use ID declaration to check for changes - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.listen_simple') + state_run = self.run_function("state.sls", mods="requisites.listen_simple") # Both listeners are expected to trigger listener_state = 'cmd_|-listener_test_listening_resolution_one_|-echo "Successful listen resolution"_|-mod_watch' @@ -1802,12 +1865,14 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): self.assertIn(listener_state, state_run) def test_listen_requisite_no_state_module(self): - ''' + """ Tests a simple state using the listen requisite - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.listen_simple_no_state_module') + state_run = self.run_function( + "state.sls", mods="requisites.listen_simple_no_state_module" + ) # First, test the result of the state run when a listener is expected to trigger listener_state = 'cmd_|-listener_test_listening_change_state_|-echo "Listening State"_|-mod_watch' self.assertIn(listener_state, state_run) @@ -1817,138 +1882,128 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): self.assertNotIn(absent_state, state_run) def test_listen_in_requisite_resolution_names(self): - ''' + """ Verify listen_in requisite lookups use ID declaration to check for changes and resolves magic names state variable - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', mods='requisites.listen_in_names') - self.assertIn('test_|-listener_service_|-nginx_|-mod_watch', state_run) - self.assertIn('test_|-listener_service_|-crond_|-mod_watch', state_run) + state_run = self.run_function("state.sls", mods="requisites.listen_in_names") + self.assertIn("test_|-listener_service_|-nginx_|-mod_watch", state_run) + self.assertIn("test_|-listener_service_|-crond_|-mod_watch", state_run) def test_listen_requisite_resolution_names(self): - ''' + """ Verify listen requisite lookups use ID declaration to check for changes and resolves magic names state variable - ''' + """ # Only run the state once and keep the return data - state_run = self.run_function('state.sls', - mods='requisites.listen_names', - timeout=self.TIMEOUT) - self.assertIn('test_|-listener_service_|-nginx_|-mod_watch', state_run) - self.assertIn('test_|-listener_service_|-crond_|-mod_watch', state_run) + state_run = self.run_function( + "state.sls", mods="requisites.listen_names", timeout=self.TIMEOUT + ) + self.assertIn("test_|-listener_service_|-nginx_|-mod_watch", state_run) + self.assertIn("test_|-listener_service_|-crond_|-mod_watch", state_run) def test_issue_30820_requisite_in_match_by_name(self): - ''' + """ This tests the case where a requisite_in matches by name instead of ID See https://github.com/saltstack/salt/issues/30820 for more info - ''' + """ state_run = self.run_function( - 'state.sls', - mods='requisites.requisite_in_match_by_name' + "state.sls", mods="requisites.requisite_in_match_by_name" ) - bar_state = 'cmd_|-bar state_|-echo bar_|-wait' + bar_state = "cmd_|-bar state_|-echo bar_|-wait" self.assertIn(bar_state, state_run) - self.assertEqual(state_run[bar_state]['comment'], - 'Command "echo bar" run') + self.assertEqual(state_run[bar_state]["comment"], 'Command "echo bar" run') def test_retry_option_defaults(self): - ''' + """ test the retry option on a simple state with defaults ensure comment is as expected ensure state duration is greater than default retry_interval (30 seconds) - ''' - state_run = self.run_function( - 'state.sls', - mods='retry.retry_defaults' + """ + state_run = self.run_function("state.sls", mods="retry.retry_defaults") + retry_state = "file_|-file_test_|-/path/to/a/non-existent/file.txt_|-exists" + expected_comment = ( + 'Attempt 1: Returned a result of "False", with the following ' + 'comment: "Specified path /path/to/a/non-existent/file.txt does not exist"\n' + "Specified path /path/to/a/non-existent/file.txt does not exist" ) - retry_state = 'file_|-file_test_|-/path/to/a/non-existent/file.txt_|-exists' - expected_comment = ('Attempt 1: Returned a result of "False", with the following ' - 'comment: "Specified path /path/to/a/non-existent/file.txt does not exist"\n' - 'Specified path /path/to/a/non-existent/file.txt does not exist') - self.assertEqual(state_run[retry_state]['comment'], expected_comment) - self.assertTrue(state_run[retry_state]['duration'] > 30) - self.assertEqual(state_run[retry_state]['result'], False) + self.assertEqual(state_run[retry_state]["comment"], expected_comment) + self.assertTrue(state_run[retry_state]["duration"] > 30) + self.assertEqual(state_run[retry_state]["result"], False) def test_retry_option_custom(self): - ''' + """ test the retry option on a simple state with custom retry values ensure comment is as expected ensure state duration is greater than custom defined interval * (retries - 1) - ''' - state_run = self.run_function( - 'state.sls', - mods='retry.retry_custom' + """ + state_run = self.run_function("state.sls", mods="retry.retry_custom") + retry_state = "file_|-file_test_|-/path/to/a/non-existent/file.txt_|-exists" + expected_comment = ( + 'Attempt 1: Returned a result of "False", with the following ' + 'comment: "Specified path /path/to/a/non-existent/file.txt does not exist"\n' + 'Attempt 2: Returned a result of "False", with the following comment: "Specified' + ' path /path/to/a/non-existent/file.txt does not exist"\nAttempt 3: Returned' + ' a result of "False", with the following comment: "Specified path' + ' /path/to/a/non-existent/file.txt does not exist"\nAttempt 4: Returned a' + ' result of "False", with the following comment: "Specified path' + ' /path/to/a/non-existent/file.txt does not exist"\nSpecified path' + " /path/to/a/non-existent/file.txt does not exist" ) - retry_state = 'file_|-file_test_|-/path/to/a/non-existent/file.txt_|-exists' - expected_comment = ('Attempt 1: Returned a result of "False", with the following ' - 'comment: "Specified path /path/to/a/non-existent/file.txt does not exist"\n' - 'Attempt 2: Returned a result of "False", with the following comment: "Specified' - ' path /path/to/a/non-existent/file.txt does not exist"\nAttempt 3: Returned' - ' a result of "False", with the following comment: "Specified path' - ' /path/to/a/non-existent/file.txt does not exist"\nAttempt 4: Returned a' - ' result of "False", with the following comment: "Specified path' - ' /path/to/a/non-existent/file.txt does not exist"\nSpecified path' - ' /path/to/a/non-existent/file.txt does not exist') - self.assertEqual(state_run[retry_state]['comment'], expected_comment) - self.assertTrue(state_run[retry_state]['duration'] > 40) - self.assertEqual(state_run[retry_state]['result'], False) + self.assertEqual(state_run[retry_state]["comment"], expected_comment) + self.assertTrue(state_run[retry_state]["duration"] > 40) + self.assertEqual(state_run[retry_state]["result"], False) def test_retry_option_success(self): - ''' + """ test a state with the retry option that should return True immedietly (i.e. no retries) - ''' - testfile = os.path.join(RUNTIME_VARS.TMP, 'retry_file_option_success') - state_run = self.run_function( - 'state.sls', - mods='retry.retry_success' - ) + """ + testfile = os.path.join(RUNTIME_VARS.TMP, "retry_file_option_success") + state_run = self.run_function("state.sls", mods="retry.retry_success") os.unlink(testfile) - retry_state = 'file_|-file_test_|-{0}_|-exists'.format(testfile) - self.assertNotIn('Attempt', state_run[retry_state]['comment']) + retry_state = "file_|-file_test_|-{0}_|-exists".format(testfile) + self.assertNotIn("Attempt", state_run[retry_state]["comment"]) def run_create(self, testfile): - ''' + """ helper function to wait 30 seconds and then create the temp retry file - ''' + """ # Wait for the requisite stae 'file_test_a' to complete before creating # test_file while True: - if os.path.exists(testfile + '_a'): + if os.path.exists(testfile + "_a"): break time.sleep(1) time.sleep(30) - with salt.utils.files.fopen(testfile, 'a'): + with salt.utils.files.fopen(testfile, "a"): pass def test_retry_option_eventual_success(self): - ''' + """ test a state with the retry option that should return True after at least 4 retry attmempt but never run 15 attempts - ''' - testfile = os.path.join(RUNTIME_VARS.TMP, 'retry_file_eventual_success') - assert not os.path.exists(testfile + '_a') + """ + testfile = os.path.join(RUNTIME_VARS.TMP, "retry_file_eventual_success") + assert not os.path.exists(testfile + "_a") assert not os.path.exists(testfile) create_thread = threading.Thread(target=self.run_create, args=(testfile,)) create_thread.start() - state_run = self.run_function( - 'state.sls', - mods='retry.retry_success2' - ) - retry_state = 'file_|-file_test_b_|-{0}_|-exists'.format(testfile) - self.assertIn('Attempt 1:', state_run[retry_state]['comment']) - self.assertIn('Attempt 2:', state_run[retry_state]['comment']) - self.assertIn('Attempt 3:', state_run[retry_state]['comment']) - self.assertIn('Attempt 4:', state_run[retry_state]['comment']) - self.assertNotIn('Attempt 15:', state_run[retry_state]['comment']) - self.assertEqual(state_run[retry_state]['result'], True) + state_run = self.run_function("state.sls", mods="retry.retry_success2") + retry_state = "file_|-file_test_b_|-{0}_|-exists".format(testfile) + self.assertIn("Attempt 1:", state_run[retry_state]["comment"]) + self.assertIn("Attempt 2:", state_run[retry_state]["comment"]) + self.assertIn("Attempt 3:", state_run[retry_state]["comment"]) + self.assertIn("Attempt 4:", state_run[retry_state]["comment"]) + self.assertNotIn("Attempt 15:", state_run[retry_state]["comment"]) + self.assertEqual(state_run[retry_state]["result"], True) def test_issue_38683_require_order_failhard_combination(self): - ''' + """ This tests the case where require, order, and failhard are all used together in a state definition. Previously, the order option, which used in tandem with require and failhard, would cause the state @@ -1956,19 +2011,18 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): compiler. With the logic error resolved, this test should now pass. See https://github.com/saltstack/salt/issues/38683 for more information. - ''' + """ state_run = self.run_function( - 'state.sls', - mods='requisites.require_order_failhard_combo' + "state.sls", mods="requisites.require_order_failhard_combo" ) - state_id = 'test_|-b_|-b_|-fail_with_changes' + state_id = "test_|-b_|-b_|-fail_with_changes" self.assertIn(state_id, state_run) - self.assertEqual(state_run[state_id]['comment'], 'Failure!') - self.assertFalse(state_run[state_id]['result']) + self.assertEqual(state_run[state_id]["comment"], "Failure!") + self.assertFalse(state_run[state_id]["result"]) def test_issue_46762_prereqs_on_a_state_with_unfulfilled_requirements(self): - ''' + """ This tests the case where state C requires state A, which fails. State C is a pre-required state for State B. Since state A fails, state C will not run because the requisite failed, @@ -1976,45 +2030,41 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): See https://github.com/saltstack/salt/issues/46762 for more information. - ''' - state_run = self.run_function( - 'state.sls', - mods='issue-46762' + """ + state_run = self.run_function("state.sls", mods="issue-46762") + + state_id = "test_|-a_|-a_|-fail_without_changes" + self.assertIn(state_id, state_run) + self.assertEqual(state_run[state_id]["comment"], "Failure!") + self.assertFalse(state_run[state_id]["result"]) + + state_id = "test_|-b_|-b_|-nop" + self.assertIn(state_id, state_run) + self.assertEqual( + state_run[state_id]["comment"], + "One or more requisite failed: issue-46762.c", ) + self.assertFalse(state_run[state_id]["result"]) - state_id = 'test_|-a_|-a_|-fail_without_changes' + state_id = "test_|-c_|-c_|-nop" self.assertIn(state_id, state_run) - self.assertEqual(state_run[state_id]['comment'], - 'Failure!') - self.assertFalse(state_run[state_id]['result']) - - state_id = 'test_|-b_|-b_|-nop' - self.assertIn(state_id, state_run) - self.assertEqual(state_run[state_id]['comment'], - 'One or more requisite failed: issue-46762.c') - self.assertFalse(state_run[state_id]['result']) - - state_id = 'test_|-c_|-c_|-nop' - self.assertIn(state_id, state_run) - self.assertEqual(state_run[state_id]['comment'], - 'One or more requisite failed: issue-46762.a') - self.assertFalse(state_run[state_id]['result']) + self.assertEqual( + state_run[state_id]["comment"], + "One or more requisite failed: issue-46762.a", + ) + self.assertFalse(state_run[state_id]["result"]) def test_state_nonbase_environment(self): - ''' + """ test state.sls with saltenv using a nonbase environment with a salt source - ''' - filename = os.path.join(RUNTIME_VARS.TMP, 'nonbase_env') + """ + filename = os.path.join(RUNTIME_VARS.TMP, "nonbase_env") try: - ret = self.run_function( - 'state.sls', - mods='non-base-env', - saltenv='prod' - ) + ret = self.run_function("state.sls", mods="non-base-env", saltenv="prod") ret = ret[next(iter(ret))] - assert ret['result'] - assert ret['comment'] == 'File {0} updated'.format(filename) + assert ret["result"] + assert ret["comment"] == "File {0} updated".format(filename) assert os.path.isfile(filename) finally: try: @@ -2022,10 +2072,15 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): except OSError: pass - @skipIf(sys.platform.startswith('win'), 'Skipped until parallel states can be fixed on Windows') - @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2') + @skipIf( + sys.platform.startswith("win"), + "Skipped until parallel states can be fixed on Windows", + ) + @skipIf( + salt.utils.platform.is_darwin() and six.PY2, "This test hangs on OS X on Py2" + ) def test_parallel_state_with_long_tag(self): - ''' + """ This tests the case where the state being executed has a long ID dec or name and states are being run in parallel. The filenames used for the parallel state cache were previously based on the tag for each chunk, @@ -2036,199 +2091,235 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): failures. See https://github.com/saltstack/salt/issues/49738 for more info. - ''' - short_command = 'helloworld' + """ + short_command = "helloworld" long_command = short_command * 25 ret = self.run_function( - 'state.sls', - mods='issue-49738', - pillar={'short_command': short_command, - 'long_command': long_command} + "state.sls", + mods="issue-49738", + pillar={"short_command": short_command, "long_command": long_command}, ) - comments = sorted([x['comment'] for x in six.itervalues(ret)]) - expected = sorted(['Command "{0}" run'.format(x) - for x in (short_command, long_command)]) - assert comments == expected, '{0} != {1}'.format(comments, expected) + comments = sorted([x["comment"] for x in six.itervalues(ret)]) + expected = sorted( + ['Command "{0}" run'.format(x) for x in (short_command, long_command)] + ) + assert comments == expected, "{0} != {1}".format(comments, expected) def _add_runtime_pillar(self, pillar): - ''' + """ helper class to add pillar data at runtime - ''' + """ import salt.utils.yaml - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, - 'pillar.sls'), 'w') as fp: + + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "pillar.sls"), "w" + ) as fp: salt.utils.yaml.safe_dump(pillar, fp) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls'), 'w') as fp: - fp.write(textwrap.dedent('''\ + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls"), "w" + ) as fp: + fp.write( + textwrap.dedent( + """\ base: '*': - pillar - ''')) + """ + ) + ) - self.run_function('saltutil.refresh_pillar') - self.run_function('test.sleep', [5]) + self.run_function("saltutil.refresh_pillar") + self.run_function("test.sleep", [5]) def test_state_sls_id_test(self): - ''' + """ test state.sls_id when test is set to true in pillar data - ''' - self._add_runtime_pillar(pillar={'test': True}) - testfile = os.path.join(RUNTIME_VARS.TMP, 'testfile') - comment = 'The file {0} is set to be changed\nNote: No changes made, actual changes may\nbe different due to other states.'.format(testfile) - ret = self.run_function('state.sls', ['core']) + """ + self._add_runtime_pillar(pillar={"test": True}) + testfile = os.path.join(RUNTIME_VARS.TMP, "testfile") + comment = "The file {0} is set to be changed\nNote: No changes made, actual changes may\nbe different due to other states.".format( + testfile + ) + ret = self.run_function("state.sls", ["core"]) for key, val in ret.items(): - self.assertEqual(val['comment'], comment) - self.assertEqual(val['changes'], {'newfile': testfile}) + self.assertEqual(val["comment"], comment) + self.assertEqual(val["changes"], {"newfile": testfile}) def test_state_sls_id_test_state_test_post_run(self): - ''' + """ test state.sls_id when test is set to true post the state already being run previously - ''' - file_name = os.path.join(RUNTIME_VARS.TMP, 'testfile') - ret = self.run_function('state.sls', ['core']) + """ + file_name = os.path.join(RUNTIME_VARS.TMP, "testfile") + ret = self.run_function("state.sls", ["core"]) for key, val in ret.items(): - self.assertEqual(val['comment'], - 'File {0} updated'.format(file_name)) - self.assertEqual(val['changes']['diff'], 'New file') + self.assertEqual(val["comment"], "File {0} updated".format(file_name)) + self.assertEqual(val["changes"]["diff"], "New file") - self._add_runtime_pillar(pillar={'test': True}) - ret = self.run_function('state.sls', ['core']) + self._add_runtime_pillar(pillar={"test": True}) + ret = self.run_function("state.sls", ["core"]) for key, val in ret.items(): self.assertEqual( - val['comment'], - 'The file {0} is in the correct state'.format(file_name)) - self.assertEqual(val['changes'], {}) + val["comment"], "The file {0} is in the correct state".format(file_name) + ) + self.assertEqual(val["changes"], {}) def test_state_sls_id_test_true(self): - ''' + """ test state.sls_id when test=True is passed as arg - ''' - file_name = os.path.join(RUNTIME_VARS.TMP, 'testfile') - ret = self.run_function('state.sls', ['core'], test=True) + """ + file_name = os.path.join(RUNTIME_VARS.TMP, "testfile") + ret = self.run_function("state.sls", ["core"], test=True) for key, val in ret.items(): self.assertEqual( - val['comment'], - 'The file {0} is set to be changed\nNote: No changes made, actual changes may\nbe different due to other states.'.format(file_name)) - self.assertEqual(val['changes'], {'newfile': file_name}) + val["comment"], + "The file {0} is set to be changed\nNote: No changes made, actual changes may\nbe different due to other states.".format( + file_name + ), + ) + self.assertEqual(val["changes"], {"newfile": file_name}) def test_state_sls_id_test_true_post_run(self): - ''' + """ test state.sls_id when test is set to true as an arg post the state already being run previously - ''' - file_name = os.path.join(RUNTIME_VARS.TMP, 'testfile') - ret = self.run_function('state.sls', ['core']) + """ + file_name = os.path.join(RUNTIME_VARS.TMP, "testfile") + ret = self.run_function("state.sls", ["core"]) for key, val in ret.items(): - self.assertEqual(val['comment'], - 'File {0} updated'.format(file_name)) - self.assertEqual(val['changes']['diff'], 'New file') + self.assertEqual(val["comment"], "File {0} updated".format(file_name)) + self.assertEqual(val["changes"]["diff"], "New file") - ret = self.run_function('state.sls', ['core'], test=True) + ret = self.run_function("state.sls", ["core"], test=True) for key, val in ret.items(): self.assertEqual( - val['comment'], - 'The file {0} is in the correct state'.format(file_name)) - self.assertEqual(val['changes'], {}) + val["comment"], "The file {0} is in the correct state".format(file_name) + ) + self.assertEqual(val["changes"], {}) def test_state_sls_id_test_false_pillar_true(self): - ''' + """ test state.sls_id when test is set to false as an arg and minion_state_test is set to True. Should return test=False. - ''' - file_name = os.path.join(RUNTIME_VARS.TMP, 'testfile') - self._add_runtime_pillar(pillar={'test': True}) - ret = self.run_function('state.sls', ['core'], test=False) + """ + file_name = os.path.join(RUNTIME_VARS.TMP, "testfile") + self._add_runtime_pillar(pillar={"test": True}) + ret = self.run_function("state.sls", ["core"], test=False) for key, val in ret.items(): - self.assertEqual(val['comment'], - 'File {0} updated'.format(file_name)) - self.assertEqual(val['changes']['diff'], 'New file') + self.assertEqual(val["comment"], "File {0} updated".format(file_name)) + self.assertEqual(val["changes"]["diff"], "New file") - @skipIf(six.PY3 and salt.utils.platform.is_darwin(), 'Test is broken on macosx and PY3') + @skipIf( + six.PY3 and salt.utils.platform.is_darwin(), "Test is broken on macosx and PY3" + ) def test_issue_30161_unless_and_onlyif_together(self): - ''' + """ test cmd.run using multiple unless options where the first cmd in the list will pass, but the second will fail. This tests the fix for issue #35384. (The fix is in PR #35545.) - ''' - sls = self.run_function('state.sls', mods='issue-30161') + """ + sls = self.run_function("state.sls", mods="issue-30161") self.assertSaltTrueReturn(sls) # We must assert against the comment here to make sure the comment reads that the # command "echo "hello"" was run. This ensures that we made it to the last unless # command in the state. If the comment reads "unless condition is true", or similar, # then the unless state run bailed out after the first unless command succeeded, # which is the bug we're regression testing for. - _expected = {'file_|-unless_false_onlyif_false_|-{0}{1}test.txt_|-managed'.format(RUNTIME_VARS.TMP, os.path.sep): - {'comment': 'onlyif condition is false\nunless condition is false', - 'name': '{0}{1}test.txt'.format(RUNTIME_VARS.TMP, os.path.sep), - 'skip_watch': True, - 'changes': {}, - 'result': True}, - 'file_|-unless_false_onlyif_true_|-{0}{1}test.txt_|-managed'.format(RUNTIME_VARS.TMP, os.path.sep): - {'comment': 'Empty file', - 'pchanges': {}, - 'name': '{0}{1}test.txt'.format(RUNTIME_VARS.TMP, os.path.sep), - 'start_time': '18:10:20.341753', - 'result': True, - 'changes': {'new': 'file {0}{1}test.txt created'.format(RUNTIME_VARS.TMP, os.path.sep)}}, - 'file_|-unless_true_onlyif_false_|-{0}{1}test.txt_|-managed'.format(RUNTIME_VARS.TMP, os.path.sep): - {'comment': 'onlyif condition is false\nunless condition is true', - 'name': '{0}{1}test.txt'.format(RUNTIME_VARS.TMP, os.path.sep), - 'start_time': '18:10:22.936446', - 'skip_watch': True, - 'changes': {}, - 'result': True}, - 'file_|-unless_true_onlyif_true_|-{0}{1}test.txt_|-managed'.format(RUNTIME_VARS.TMP, os.path.sep): - {'comment': 'onlyif condition is true\nunless condition is true', - 'name': '{0}{1}test.txt'.format(RUNTIME_VARS.TMP, os.path.sep), - 'skip_watch': True, - 'changes': {}, - 'result': True}} + _expected = { + "file_|-unless_false_onlyif_false_|-{0}{1}test.txt_|-managed".format( + RUNTIME_VARS.TMP, os.path.sep + ): { + "comment": "onlyif condition is false\nunless condition is false", + "name": "{0}{1}test.txt".format(RUNTIME_VARS.TMP, os.path.sep), + "skip_watch": True, + "changes": {}, + "result": True, + }, + "file_|-unless_false_onlyif_true_|-{0}{1}test.txt_|-managed".format( + RUNTIME_VARS.TMP, os.path.sep + ): { + "comment": "Empty file", + "pchanges": {}, + "name": "{0}{1}test.txt".format(RUNTIME_VARS.TMP, os.path.sep), + "start_time": "18:10:20.341753", + "result": True, + "changes": { + "new": "file {0}{1}test.txt created".format( + RUNTIME_VARS.TMP, os.path.sep + ) + }, + }, + "file_|-unless_true_onlyif_false_|-{0}{1}test.txt_|-managed".format( + RUNTIME_VARS.TMP, os.path.sep + ): { + "comment": "onlyif condition is false\nunless condition is true", + "name": "{0}{1}test.txt".format(RUNTIME_VARS.TMP, os.path.sep), + "start_time": "18:10:22.936446", + "skip_watch": True, + "changes": {}, + "result": True, + }, + "file_|-unless_true_onlyif_true_|-{0}{1}test.txt_|-managed".format( + RUNTIME_VARS.TMP, os.path.sep + ): { + "comment": "onlyif condition is true\nunless condition is true", + "name": "{0}{1}test.txt".format(RUNTIME_VARS.TMP, os.path.sep), + "skip_watch": True, + "changes": {}, + "result": True, + }, + } for id in _expected: - self.assertEqual(sls[id]['comment'], _expected[id]['comment']) + self.assertEqual(sls[id]["comment"], _expected[id]["comment"]) - @skipIf(six.PY3 and salt.utils.platform.is_darwin(), 'Test is broken on macosx and PY3') + @skipIf( + six.PY3 and salt.utils.platform.is_darwin(), "Test is broken on macosx and PY3" + ) def test_state_sls_unicode_characters(self): - ''' + """ test state.sls when state file contains non-ascii characters - ''' - ret = self.run_function('state.sls', ['issue-46672']) - log.debug('== ret %s ==', type(ret)) + """ + ret = self.run_function("state.sls", ["issue-46672"]) + log.debug("== ret %s ==", type(ret)) _expected = "cmd_|-echo1_|-echo 'This is Æ test!'_|-run" self.assertIn(_expected, ret) - @skipIf(six.PY3 and salt.utils.platform.is_darwin(), 'Test is broken on macosx and PY3') + @skipIf( + six.PY3 and salt.utils.platform.is_darwin(), "Test is broken on macosx and PY3" + ) def test_state_sls_unicode_characters_cmd_output(self): - ''' + """ test the output from running and echo command with non-ascii characters. - ''' - ret = self.run_function('state.sls', ['issue-46672-a'], timeout=60) + """ + ret = self.run_function("state.sls", ["issue-46672-a"], timeout=60) key = list(ret.keys())[0] - log.debug('== ret %s ==', type(ret)) - _expected = 'This is Æ test!' + log.debug("== ret %s ==", type(ret)) + _expected = "This is Æ test!" if salt.utils.platform.is_windows(): # Windows cmd.exe will mangle the output using cmd's codepage. if six.PY2: _expected = "'This is A+ test!'" else: _expected = "'This is ’ test!'" - self.assertEqual(_expected, ret[key]['changes']['stdout']) + self.assertEqual(_expected, ret[key]["changes"]["stdout"]) def tearDown(self): - rm_files = [os.path.join(RUNTIME_VARS.TMP, 'nonbase_env'), - os.path.join(RUNTIME_VARS.TMP, 'testfile'), - os.path.join(RUNTIME_VARS.TMP, 'test.txt'), - os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'top.sls')] + rm_files = [ + os.path.join(RUNTIME_VARS.TMP, "nonbase_env"), + os.path.join(RUNTIME_VARS.TMP, "testfile"), + os.path.join(RUNTIME_VARS.TMP, "test.txt"), + os.path.join(RUNTIME_VARS.TMP_STATE_TREE, "top.sls"), + ] for file_ in rm_files: if os.path.isfile(file_): @@ -2237,45 +2328,40 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): # remove old pillar data for filename in os.listdir(RUNTIME_VARS.TMP_PILLAR_TREE): os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, filename)) - self.run_function('saltutil.refresh_pillar') - self.run_function('test.sleep', [5]) + self.run_function("saltutil.refresh_pillar") + self.run_function("test.sleep", [5]) def test_state_sls_integer_name(self): - ''' + """ This tests the case where the state file is named only with integers - ''' - state_run = self.run_function( - 'state.sls', - mods='12345' - ) + """ + state_run = self.run_function("state.sls", mods="12345") - state_id = 'test_|-always-passes_|-always-passes_|-succeed_without_changes' + state_id = "test_|-always-passes_|-always-passes_|-succeed_without_changes" self.assertIn(state_id, state_run) - self.assertEqual(state_run[state_id]['comment'], - 'Success!') - self.assertTrue(state_run[state_id]['result']) + self.assertEqual(state_run[state_id]["comment"], "Success!") + self.assertTrue(state_run[state_id]["result"]) def test_state_sls_lazyloader_allows_recursion(self): - ''' + """ This tests that referencing dunders like __salt__ work context: https://github.com/saltstack/salt/pull/51499 - ''' - state_run = self.run_function('state.sls', mods='issue-51499') + """ + state_run = self.run_function("state.sls", mods="issue-51499") - state_id = 'test_|-always-passes_|-foo_|-succeed_without_changes' + state_id = "test_|-always-passes_|-foo_|-succeed_without_changes" self.assertIn(state_id, state_run) - self.assertEqual(state_run[state_id]['comment'], - 'Success!') - self.assertTrue(state_run[state_id]['result']) + self.assertEqual(state_run[state_id]["comment"], "Success!") + self.assertTrue(state_run[state_id]["result"]) def test_issue_56131(self): - module_path = os.path.join(RUNTIME_VARS.CODE_DIR, 'pip.py') + module_path = os.path.join(RUNTIME_VARS.CODE_DIR, "pip.py") if six.PY3: - modulec_path = os.path.join(RUNTIME_VARS.CODE_DIR, '__pycache__', 'pip.pyc') + modulec_path = os.path.join(RUNTIME_VARS.CODE_DIR, "__pycache__", "pip.pyc") else: - modulec_path = os.path.join(RUNTIME_VARS.CODE_DIR, 'pip.pyc') - unzip_path = os.path.join(RUNTIME_VARS.TMP, 'issue-56131.txt') + modulec_path = os.path.join(RUNTIME_VARS.CODE_DIR, "pip.pyc") + unzip_path = os.path.join(RUNTIME_VARS.TMP, "issue-56131.txt") def clean_paths(paths): for path in paths: @@ -2284,10 +2370,26 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin): except OSError: log.warn("Path not found: %s", path) - with salt.utils.files.fopen(module_path, 'w') as fp: + with salt.utils.files.fopen(module_path, "w") as fp: fp.write('raise ImportError("No module named pip")') self.addCleanup(clean_paths, [unzip_path, module_path, modulec_path]) assert not os.path.exists(unzip_path) - state_run = self.run_function('state.sls', mods='issue-56131', pillar={'unzip_to': RUNTIME_VARS.TMP}, timeout=30) + state_run = self.run_function( + "state.sls", + mods="issue-56131", + pillar={"unzip_to": RUNTIME_VARS.TMP}, + timeout=30, + ) assert state_run is not False assert os.path.exists(unzip_path) + + def test_jinja_renderer_argline(self): + """ + This is a test case for https://github.com/saltstack/salt/issues/55124 + + Renderer for this is in tests/integration/files/file/base/_renderers/issue55124.py + """ + result = self.run_function("state.sls", mods="issue-55124") + assert isinstance(result, dict), result + result = result[next(iter(result))] + assert result["result"], result diff --git a/tests/integration/modules/test_state_jinja_filters.py b/tests/integration/modules/test_state_jinja_filters.py index 83a7b4fef68..54b9a42da82 100644 --- a/tests/integration/modules/test_state_jinja_filters.py +++ b/tests/integration/modules/test_state_jinja_filters.py @@ -9,6 +9,6 @@ from tests.support.jinja_filters import JinjaFiltersTest class StateModuleJinjaFiltersTest(ModuleCase, JinjaFiltersTest): - ''' + """ testing Jinja filters are available via state system - ''' + """ diff --git a/tests/integration/modules/test_status.py b/tests/integration/modules/test_status.py index 18452863079..371eb1adf15 100644 --- a/tests/integration/modules/test_status.py +++ b/tests/integration/modules/test_status.py @@ -1,79 +1,79 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import random -# Import Salt Testing libs +import pytest +import salt.utils.platform +from salt.ext import six from tests.support.case import ModuleCase from tests.support.helpers import flaky from tests.support.unit import skipIf -# Import Salt libs -from salt.ext import six -import salt.utils.platform - +@pytest.mark.windows_whitelisted class StatusModuleTest(ModuleCase): - ''' + """ Test the status module - ''' - @skipIf(salt.utils.platform.is_windows(), 'minion is windows') + """ + + @skipIf(salt.utils.platform.is_windows(), "minion is windows") @flaky def test_status_pid(self): - ''' + """ status.pid - ''' - status_pid = self.run_function('status.pid', ['salt']) + """ + status_pid = self.run_function("status.pid", ["salt"]) grab_pids = status_pid.split()[:10] random_pid = random.choice(grab_pids) - grep_salt = self.run_function('cmd.run', ['pgrep -f salt']) + grep_salt = self.run_function("cmd.run", ["pgrep -f salt"]) self.assertIn(random_pid, grep_salt) - @skipIf(not salt.utils.platform.is_windows(), 'windows only test') + @skipIf(not salt.utils.platform.is_windows(), "windows only test") def test_status_cpuload(self): - ''' + """ status.cpuload - ''' - ret = self.run_function('status.cpuload') + """ + ret = self.run_function("status.cpuload") self.assertTrue(isinstance(ret, float)) - @skipIf(not salt.utils.platform.is_windows(), 'windows only test') + @skipIf(not salt.utils.platform.is_windows(), "windows only test") def test_status_saltmem(self): - ''' + """ status.saltmem - ''' - ret = self.run_function('status.saltmem') + """ + ret = self.run_function("status.saltmem") self.assertTrue(isinstance(ret, int)) def test_status_diskusage(self): - ''' + """ status.diskusage - ''' - ret = self.run_function('status.diskusage') + """ + ret = self.run_function("status.diskusage") if salt.utils.platform.is_darwin(): - self.assertIn('not yet supported on this platform', ret) + self.assertIn("not yet supported on this platform", ret) elif salt.utils.platform.is_windows(): - self.assertTrue(isinstance(ret['percent'], float)) + self.assertTrue(isinstance(ret["percent"], float)) else: - self.assertIn('total', str(ret)) - self.assertIn('available', str(ret)) + self.assertIn("total", str(ret)) + self.assertIn("available", str(ret)) def test_status_procs(self): - ''' + """ status.procs - ''' - ret = self.run_function('status.procs') + """ + ret = self.run_function("status.procs") for x, y in six.iteritems(ret): - self.assertIn('cmd', y) + self.assertIn("cmd", y) def test_status_uptime(self): - ''' + """ status.uptime - ''' - ret = self.run_function('status.uptime') + """ + ret = self.run_function("status.uptime") if salt.utils.platform.is_windows(): self.assertTrue(isinstance(ret, float)) else: - self.assertTrue(isinstance(ret['days'], int)) + self.assertTrue(isinstance(ret["days"], int)) diff --git a/tests/integration/modules/test_supervisord.py b/tests/integration/modules/test_supervisord.py index 0c215469f30..245b92c9e3d 100644 --- a/tests/integration/modules/test_supervisord.py +++ b/tests/integration/modules/test_supervisord.py @@ -1,71 +1,78 @@ # -*- coding: utf-8 -*- # Import python -from __future__ import absolute_import, unicode_literals, print_function -import os -import time -import subprocess +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf +import os +import subprocess +import time # Import salt libs import salt.utils.path -from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES # Import 3rd-party libs from salt.ext import six +from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.case import ModuleCase + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf -@skipIf(six.PY3, 'supervisor does not work under python 3') -@skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') -@skipIf(salt.utils.path.which('supervisorctl') is None, 'supervisord not installed') +@skipIf(six.PY3, "supervisor does not work under python 3") +@skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, "virtualenv not installed" +) +@skipIf(salt.utils.path.which("supervisorctl") is None, "supervisord not installed") class SupervisordModuleTest(ModuleCase): - ''' + """ Validates the supervisorctl functions. - ''' + """ + def setUp(self): super(SupervisordModuleTest, self).setUp() - self.venv_test_dir = os.path.join(RUNTIME_VARS.TMP, 'supervisortests') - self.venv_dir = os.path.join(self.venv_test_dir, 'venv') - self.supervisor_sock = os.path.join(self.venv_dir, 'supervisor.sock') + self.venv_test_dir = os.path.join(RUNTIME_VARS.TMP, "supervisortests") + self.venv_dir = os.path.join(self.venv_test_dir, "venv") + self.supervisor_sock = os.path.join(self.venv_dir, "supervisor.sock") if not os.path.exists(self.venv_dir): os.makedirs(self.venv_test_dir) - self.run_function('virtualenv.create', [self.venv_dir]) + self.run_function("virtualenv.create", [self.venv_dir]) self.run_function( - 'pip.install', [], pkgs='supervisor', bin_env=self.venv_dir) + "pip.install", [], pkgs="supervisor", bin_env=self.venv_dir + ) - self.supervisord = os.path.join(self.venv_dir, 'bin', 'supervisord') + self.supervisord = os.path.join(self.venv_dir, "bin", "supervisord") if not os.path.exists(self.supervisord): - self.skipTest('Failed to install supervisor in test virtualenv') - self.supervisor_conf = os.path.join(self.venv_dir, 'supervisor.conf') + self.skipTest("Failed to install supervisor in test virtualenv") + self.supervisor_conf = os.path.join(self.venv_dir, "supervisor.conf") def start_supervisord(self, autostart=True): self.run_state( - 'file.managed', name=self.supervisor_conf, - source='salt://supervisor.conf', template='jinja', + "file.managed", + name=self.supervisor_conf, + source="salt://supervisor.conf", + template="jinja", context={ - 'supervisor_sock': self.supervisor_sock, - 'virtual_env': self.venv_dir, - 'autostart': autostart - } + "supervisor_sock": self.supervisor_sock, + "virtual_env": self.venv_dir, + "autostart": autostart, + }, ) if not os.path.exists(self.supervisor_conf): - self.skipTest('failed to create supervisor config file') + self.skipTest("failed to create supervisor config file") self.supervisor_proc = subprocess.Popen( - [self.supervisord, '-c', self.supervisor_conf] + [self.supervisord, "-c", self.supervisor_conf] ) if self.supervisor_proc.poll() is not None: - self.skipTest('failed to start supervisord') + self.skipTest("failed to start supervisord") timeout = 10 while not os.path.exists(self.supervisor_sock): if timeout == 0: self.skipTest( - 'supervisor socket not found - failed to start supervisord' + "supervisor socket not found - failed to start supervisord" ) break else: @@ -73,11 +80,13 @@ class SupervisordModuleTest(ModuleCase): timeout -= 1 def tearDown(self): - if hasattr(self, 'supervisor_proc') and \ - self.supervisor_proc.poll() is not None: + if hasattr(self, "supervisor_proc") and self.supervisor_proc.poll() is not None: self.run_function( - 'supervisord.custom', ['shutdown'], - conf_file=self.supervisor_conf, bin_env=self.venv_dir) + "supervisord.custom", + ["shutdown"], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) self.supervisor_proc.wait() del self.venv_dir del self.venv_test_dir @@ -86,150 +95,191 @@ class SupervisordModuleTest(ModuleCase): del self.supervisor_conf def test_start_all(self): - ''' + """ Start all services when they are not running. - ''' + """ self.start_supervisord(autostart=False) ret = self.run_function( - 'supervisord.start', [], conf_file=self.supervisor_conf, - bin_env=self.venv_dir) - self.assertIn('sleep_service: started', ret) - self.assertIn('sleep_service2: started', ret) + "supervisord.start", + [], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertIn("sleep_service: started", ret) + self.assertIn("sleep_service2: started", ret) def test_start_all_already_running(self): - ''' + """ Start all services when they are running. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_function( - 'supervisord.start', [], conf_file=self.supervisor_conf, - bin_env=self.venv_dir + "supervisord.start", + [], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, ) - self.assertEqual(ret, '') + self.assertEqual(ret, "") def test_start_one(self): - ''' + """ Start a specific service that is not running. - ''' + """ self.start_supervisord(autostart=False) ret = self.run_function( - 'supervisord.start', ['sleep_service'], - conf_file=self.supervisor_conf, bin_env=self.venv_dir) - self.assertEqual(ret, 'sleep_service: started') + "supervisord.start", + ["sleep_service"], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertEqual(ret, "sleep_service: started") def test_start_one_already_running(self): - ''' + """ Try to start a specific service that is running. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_function( - 'supervisord.start', ['sleep_service'], - conf_file=self.supervisor_conf, bin_env=self.venv_dir) - self.assertEqual(ret, 'sleep_service: ERROR (already started)') + "supervisord.start", + ["sleep_service"], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertEqual(ret, "sleep_service: ERROR (already started)") def test_restart_all(self): - ''' + """ Restart all services when they are running. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_function( - 'supervisord.restart', [], conf_file=self.supervisor_conf, - bin_env=self.venv_dir) - self.assertIn('sleep_service: stopped', ret) - self.assertIn('sleep_service2: stopped', ret) - self.assertIn('sleep_service: started', ret) - self.assertIn('sleep_service2: started', ret) + "supervisord.restart", + [], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertIn("sleep_service: stopped", ret) + self.assertIn("sleep_service2: stopped", ret) + self.assertIn("sleep_service: started", ret) + self.assertIn("sleep_service2: started", ret) def test_restart_all_not_running(self): - ''' + """ Restart all services when they are not running. - ''' + """ self.start_supervisord(autostart=False) ret = self.run_function( - 'supervisord.restart', [], conf_file=self.supervisor_conf, - bin_env=self.venv_dir) + "supervisord.restart", + [], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) # These 2 services might return in different orders so test separately - self.assertIn('sleep_service: started', ret) - self.assertIn('sleep_service2: started', ret) + self.assertIn("sleep_service: started", ret) + self.assertIn("sleep_service2: started", ret) def test_restart_one(self): - ''' + """ Restart a specific service that is running. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_function( - 'supervisord.restart', ['sleep_service'], - conf_file=self.supervisor_conf, bin_env=self.venv_dir) - self.assertEqual(ret, 'sleep_service: stopped\nsleep_service: started') + "supervisord.restart", + ["sleep_service"], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertEqual(ret, "sleep_service: stopped\nsleep_service: started") def test_restart_one_not_running(self): - ''' + """ Restart a specific service that is not running. - ''' + """ self.start_supervisord(autostart=False) ret = self.run_function( - 'supervisord.restart', ['sleep_service'], - conf_file=self.supervisor_conf, bin_env=self.venv_dir) - self.assertIn('sleep_service: ERROR (not running)', ret) - self.assertIn('sleep_service: started', ret) + "supervisord.restart", + ["sleep_service"], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertIn("sleep_service: ERROR (not running)", ret) + self.assertIn("sleep_service: started", ret) def test_stop_all(self): - ''' + """ Stop all services when they are running. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_function( - 'supervisord.stop', [], conf_file=self.supervisor_conf, - bin_env=self.venv_dir) - self.assertIn('sleep_service: stopped', ret) - self.assertIn('sleep_service2: stopped', ret) + "supervisord.stop", + [], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertIn("sleep_service: stopped", ret) + self.assertIn("sleep_service2: stopped", ret) def test_stop_all_not_running(self): - ''' + """ Stop all services when they are not running. - ''' + """ self.start_supervisord(autostart=False) ret = self.run_function( - 'supervisord.stop', [], conf_file=self.supervisor_conf, - bin_env=self.venv_dir) - self.assertEqual(ret, '') + "supervisord.stop", + [], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertEqual(ret, "") def test_stop_one(self): - ''' + """ Stop a specific service that is running. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_function( - 'supervisord.stop', ['sleep_service'], - conf_file=self.supervisor_conf, bin_env=self.venv_dir) - self.assertEqual(ret, 'sleep_service: stopped') + "supervisord.stop", + ["sleep_service"], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertEqual(ret, "sleep_service: stopped") def test_stop_one_not_running(self): - ''' + """ Stop a specific service that is not running. - ''' + """ self.start_supervisord(autostart=False) ret = self.run_function( - 'supervisord.stop', ['sleep_service'], - conf_file=self.supervisor_conf, bin_env=self.venv_dir) - self.assertEqual(ret, 'sleep_service: ERROR (not running)') + "supervisord.stop", + ["sleep_service"], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertEqual(ret, "sleep_service: ERROR (not running)") def test_status_all(self): - ''' + """ Status for all services - ''' + """ self.start_supervisord(autostart=True) ret = self.run_function( - 'supervisord.status', [], conf_file=self.supervisor_conf, - bin_env=self.venv_dir) - self.assertEqual(sorted(ret), ['sleep_service', 'sleep_service2']) + "supervisord.status", + [], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) + self.assertEqual(sorted(ret), ["sleep_service", "sleep_service2"]) def test_status_one(self): - ''' + """ Status for a specific service. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_function( - 'supervisord.status', ['sleep_service'], - conf_file=self.supervisor_conf, bin_env=self.venv_dir) + "supervisord.status", + ["sleep_service"], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) self.assertTrue(ret) diff --git a/tests/integration/modules/test_sysctl.py b/tests/integration/modules/test_sysctl.py index bec70da6f96..3d5b7945d59 100644 --- a/tests/integration/modules/test_sysctl.py +++ b/tests/integration/modules/test_sysctl.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import sys # Import Salt Testing libs @@ -12,40 +13,38 @@ from tests.support.unit import skipIf class SysctlModuleTest(ModuleCase): def setUp(self): super(SysctlModuleTest, self).setUp() - ret = self.run_function('cmd.has_exec', ['sysctl']) + ret = self.run_function("cmd.has_exec", ["sysctl"]) if not ret: - self.skipTest('sysctl not found') + self.skipTest("sysctl not found") def test_show(self): - ret = self.run_function('sysctl.show') - self.assertIsInstance(ret, dict, 'sysctl.show return wrong type') - self.assertGreater(len(ret), 10, 'sysctl.show return few data') + ret = self.run_function("sysctl.show") + self.assertIsInstance(ret, dict, "sysctl.show return wrong type") + self.assertGreater(len(ret), 10, "sysctl.show return few data") - @skipIf(not sys.platform.startswith('linux'), 'Linux specific') + @skipIf(not sys.platform.startswith("linux"), "Linux specific") def test_show_linux(self): - ret = self.run_function('sysctl.show') - self.assertIn('kernel.ostype', ret, 'kernel.ostype absent') + ret = self.run_function("sysctl.show") + self.assertIn("kernel.ostype", ret, "kernel.ostype absent") - @skipIf(not sys.platform.startswith('freebsd'), 'FreeBSD specific') + @skipIf(not sys.platform.startswith("freebsd"), "FreeBSD specific") def test_show_freebsd(self): - ret = self.run_function('sysctl.show') - self.assertIn('vm.vmtotal', ret, 'Multiline variable absent') - self.assertGreater(len(ret.get('vm.vmtotal').splitlines()), - 1, - 'Multiline value was parsed wrong') + ret = self.run_function("sysctl.show") + self.assertIn("vm.vmtotal", ret, "Multiline variable absent") + self.assertGreater( + len(ret.get("vm.vmtotal").splitlines()), + 1, + "Multiline value was parsed wrong", + ) - @skipIf(not sys.platform.startswith('openbsd'), 'OpenBSD specific') + @skipIf(not sys.platform.startswith("openbsd"), "OpenBSD specific") def test_show_openbsd(self): - ret = self.run_function('sysctl.show') - self.assertIn('kern.ostype', ret, 'kern.ostype absent') - self.assertEqual( - ret.get('kern.ostype'), 'OpenBSD', 'Incorrect kern.ostype' - ) + ret = self.run_function("sysctl.show") + self.assertIn("kern.ostype", ret, "kern.ostype absent") + self.assertEqual(ret.get("kern.ostype"), "OpenBSD", "Incorrect kern.ostype") - @skipIf(not sys.platform.startswith('darwin'), 'Darwin (macOS) specific') + @skipIf(not sys.platform.startswith("darwin"), "Darwin (macOS) specific") def test_show_darwin(self): - ret = self.run_function('sysctl.show') - self.assertIn('kern.ostype', ret, 'kern.ostype absent') - self.assertEqual( - ret.get('kern.ostype'), 'Darwin', 'Incorrect kern.ostype' - ) + ret = self.run_function("sysctl.show") + self.assertIn("kern.ostype", ret, "kern.ostype absent") + self.assertEqual(ret.get("kern.ostype"), "Darwin", "Incorrect kern.ostype") diff --git a/tests/integration/modules/test_sysmod.py b/tests/integration/modules/test_sysmod.py index 38398dbbfca..dbf2c1166a9 100644 --- a/tests/integration/modules/test_sysmod.py +++ b/tests/integration/modules/test_sysmod.py @@ -1,32 +1,33 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import ModuleCase +import pytest from salt.ext import six +from tests.support.case import ModuleCase +@pytest.mark.windows_whitelisted class SysModuleTest(ModuleCase): - ''' + """ Validate the sys module - ''' + """ + def test_valid_docs(self): - ''' + """ Make sure no functions are exposed that don't have valid docstrings - ''' - ret = self.run_function('runtests_helpers.get_invalid_docs') - if ret == {'missing_docstring': [], 'missing_cli_example': []}: + """ + ret = self.run_function("runtests_helpers.get_invalid_docs") + if ret == {"missing_docstring": [], "missing_cli_example": []}: return if isinstance(ret, six.string_types): self.fail(ret) self.fail( - 'There are some functions which do not have a docstring or do not ' - 'have an example:\nNo docstring:\n{0}\nNo example:\n{1}\n'.format( - '\n'.join([' - {0}'.format(f) for f in ret['missing_docstring']]), - '\n'.join([' - {0}'.format(f) for f in ret['missing_cli_example']]), + "There are some functions which do not have a docstring or do not " + "have an example:\nNo docstring:\n{0}\nNo example:\n{1}\n".format( + "\n".join([" - {0}".format(f) for f in ret["missing_docstring"]]), + "\n".join([" - {0}".format(f) for f in ret["missing_cli_example"]]), ) ) diff --git a/tests/integration/modules/test_sysrc.py b/tests/integration/modules/test_sysrc.py index 1b4799e903e..32c2bbeaa4b 100644 --- a/tests/integration/modules/test_sysrc.py +++ b/tests/integration/modules/test_sysrc.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import sys # Import Salt Testing libs @@ -13,32 +14,52 @@ from tests.support.unit import skipIf class SysrcModuleTest(ModuleCase): def setUp(self): super(SysrcModuleTest, self).setUp() - ret = self.run_function('cmd.has_exec', ['sysrc']) + ret = self.run_function("cmd.has_exec", ["sysrc"]) if not ret: - self.skipTest('sysrc not found') + self.skipTest("sysrc not found") - @skipIf(not sys.platform.startswith('freebsd'), 'FreeBSD specific') + @skipIf(not sys.platform.startswith("freebsd"), "FreeBSD specific") def test_show(self): - ret = self.run_function('sysrc.get') - self.assertIsInstance(ret, dict, 'sysrc.get returned wrong type, expecting dictionary') - self.assertIn('/etc/rc.conf', ret, 'sysrc.get should have an rc.conf key in it.') + ret = self.run_function("sysrc.get") + self.assertIsInstance( + ret, dict, "sysrc.get returned wrong type, expecting dictionary" + ) + self.assertIn( + "/etc/rc.conf", ret, "sysrc.get should have an rc.conf key in it." + ) - @skipIf(not sys.platform.startswith('freebsd'), 'FreeBSD specific') + @skipIf(not sys.platform.startswith("freebsd"), "FreeBSD specific") @destructiveTest def test_set(self): - ret = self.run_function('sysrc.set', ['test_var', '1']) - self.assertIsInstance(ret, dict, 'sysrc.get returned wrong type, expecting dictionary') - self.assertIn('/etc/rc.conf', ret, 'sysrc.set should have an rc.conf key in it.') - self.assertIn('1', ret['/etc/rc.conf']['test_var'], 'sysrc.set should return the value it set.') - ret = self.run_function('sysrc.remove', ['test_var']) - self.assertEqual('test_var removed', ret) + ret = self.run_function("sysrc.set", ["test_var", "1"]) + self.assertIsInstance( + ret, dict, "sysrc.get returned wrong type, expecting dictionary" + ) + self.assertIn( + "/etc/rc.conf", ret, "sysrc.set should have an rc.conf key in it." + ) + self.assertIn( + "1", + ret["/etc/rc.conf"]["test_var"], + "sysrc.set should return the value it set.", + ) + ret = self.run_function("sysrc.remove", ["test_var"]) + self.assertEqual("test_var removed", ret) - @skipIf(not sys.platform.startswith('freebsd'), 'FreeBSD specific') + @skipIf(not sys.platform.startswith("freebsd"), "FreeBSD specific") @destructiveTest def test_set_bool(self): - ret = self.run_function('sysrc.set', ['test_var', True]) - self.assertIsInstance(ret, dict, 'sysrc.get returned wrong type, expecting dictionary') - self.assertIn('/etc/rc.conf', ret, 'sysrc.set should have an rc.conf key in it.') - self.assertIn('YES', ret['/etc/rc.conf']['test_var'], 'sysrc.set should return the value it set.') - ret = self.run_function('sysrc.remove', ['test_var']) - self.assertEqual('test_var removed', ret) + ret = self.run_function("sysrc.set", ["test_var", True]) + self.assertIsInstance( + ret, dict, "sysrc.get returned wrong type, expecting dictionary" + ) + self.assertIn( + "/etc/rc.conf", ret, "sysrc.set should have an rc.conf key in it." + ) + self.assertIn( + "YES", + ret["/etc/rc.conf"]["test_var"], + "sysrc.set should return the value it set.", + ) + ret = self.run_function("sysrc.remove", ["test_var"]) + self.assertEqual("test_var removed", ret) diff --git a/tests/integration/modules/test_system.py b/tests/integration/modules/test_system.py index 5442e22cd0e..6fa6e625a88 100644 --- a/tests/integration/modules/test_system.py +++ b/tests/integration/modules/test_system.py @@ -1,37 +1,40 @@ # -*- coding: utf-8 -*- -# Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import datetime import logging import os import signal import subprocess -import time import textwrap +import time -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf, SkipTest -from tests.support.helpers import destructiveTest, skip_if_not_root, flaky, requires_system_grains - -# Import Salt libs +import pytest +import salt.states.file import salt.utils.files import salt.utils.path import salt.utils.platform -import salt.states.file -from salt.ext.six.moves import range from salt.ext import six +from salt.ext.six.moves import range +from tests.support.case import ModuleCase +from tests.support.helpers import ( + destructiveTest, + flaky, + requires_system_grains, + skip_if_not_root, +) +from tests.support.unit import SkipTest, skipIf log = logging.getLogger(__name__) -@skipIf(not salt.utils.platform.is_linux(), - 'These tests can only be run on linux') +@skipIf(not salt.utils.platform.is_linux(), "These tests can only be run on linux") +@pytest.mark.windows_whitelisted class SystemModuleTest(ModuleCase): - ''' + """ Validate the date/time functions in the system module - ''' + """ _hwclock_has_compare_ = None _systemd_timesyncd_available_ = None @@ -39,27 +42,25 @@ class SystemModuleTest(ModuleCase): @classmethod @requires_system_grains def setUpClass(cls, grains): # pylint: disable=arguments-differ - if grains['kernel'] != 'Linux': - raise SkipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **grains - ) - ) + if grains["kernel"] != "Linux": + raise SkipTest("Test not applicable to '{kernel}' kernel".format(**grains)) cls.fmt_str = "%Y-%m-%d %H:%M:%S" cls._orig_time = None cls._machine_info = True @classmethod def tearDownClass(cls): - for name in ('fmt_str', '_orig_time', '_machine_info'): + for name in ("fmt_str", "_orig_time", "_machine_info"): delattr(cls, name) def setUp(self): super(SystemModuleTest, self).setUp() if self._systemd_timesyncd_available_ is None: - SystemModuleTest._systemd_timesyncd_available_ = self.run_function('service.available', ['systemd-timesyncd']) + SystemModuleTest._systemd_timesyncd_available_ = self.run_function( + "service.available", ["systemd-timesyncd"] + ) if self._systemd_timesyncd_available_: - self.run_function('service.stop', ['systemd-timesyncd']) + self.run_function("service.stop", ["systemd-timesyncd"]) def tearDown(self): if self._orig_time is not None: @@ -69,7 +70,7 @@ class SystemModuleTest(ModuleCase): self._restore_machine_info() self._machine_info = True if self._systemd_timesyncd_available_: - self.run_function('service.start', ['systemd-timesyncd']) + self.run_function("service.start", ["systemd-timesyncd"]) def _save_time(self): self._orig_time = datetime.datetime.utcnow() @@ -77,38 +78,38 @@ class SystemModuleTest(ModuleCase): def _set_time(self, new_time, offset=None): t = new_time.timetuple()[:6] t += (offset,) - return self.run_function('system.set_system_date_time', t) + return self.run_function("system.set_system_date_time", t) def _restore_time(self): result = self._set_time(self._orig_time, "+0000") self.assertTrue(result, msg="Unable to restore time properly") def _same_times(self, t1, t2, seconds_diff=30): - ''' + """ Helper function to check if two datetime objects are close enough to the same time. - ''' + """ return abs(t1 - t2) < datetime.timedelta(seconds=seconds_diff) def _hwclock_has_compare(self): - ''' + """ Some builds of hwclock don't include the `--compare` function needed to test hw/sw clock synchronization. Returns false on systems where it's not present so that we can skip the comparison portion of the test. - ''' + """ if self._hwclock_has_compare_ is None: - res = self.run_function('cmd.run_all', cmd='hwclock -h') + res = self.run_function("cmd.run_all", cmd="hwclock -h") SystemModuleTest._hwclock_has_compare_ = ( - res['retcode'] == 0 and res['stdout'].find('--compare') > 0 + res["retcode"] == 0 and res["stdout"].find("--compare") > 0 ) return self._hwclock_has_compare_ def _test_hwclock_sync(self): - ''' + """ Check that hw and sw clocks are sync'd. - ''' - if not self.run_function('system.has_settable_hwclock'): + """ + if not self.run_function("system.has_settable_hwclock"): return None if not self._hwclock_has_compare(): return None @@ -117,7 +118,7 @@ class SystemModuleTest(ModuleCase): pass def _alrm_handler(sig, frame): - log.warning('hwclock --compare failed to produce output after 3 seconds') + log.warning("hwclock --compare failed to produce output after 3 seconds") raise CompareTimeout for _ in range(2): @@ -125,12 +126,16 @@ class SystemModuleTest(ModuleCase): orig_handler = signal.signal(signal.SIGALRM, _alrm_handler) signal.alarm(3) rpipeFd, wpipeFd = os.pipe() - log.debug('Comparing hwclock to sys clock') + log.debug("Comparing hwclock to sys clock") with os.fdopen(rpipeFd, "r") as rpipe: with os.fdopen(wpipeFd, "w") as wpipe: with salt.utils.files.fopen(os.devnull, "r") as nulFd: - p = subprocess.Popen(args=['hwclock', '--compare'], - stdin=nulFd, stdout=wpipeFd, stderr=subprocess.PIPE) + p = subprocess.Popen( + args=["hwclock", "--compare"], + stdin=nulFd, + stdout=wpipeFd, + stderr=subprocess.PIPE, + ) p.communicate() # read header @@ -147,8 +152,13 @@ class SystemModuleTest(ModuleCase): swTime = float(timeComp[1]) diff = abs(hwTime - swTime) - self.assertTrue(diff <= 2.0, - msg=("hwclock difference too big: " + six.text_type(timeCompStr))) + self.assertTrue( + diff <= 2.0, + msg=( + "hwclock difference too big: " + + six.text_type(timeCompStr) + ), + ) break except CompareTimeout: p.terminate() @@ -156,77 +166,75 @@ class SystemModuleTest(ModuleCase): signal.alarm(0) signal.signal(signal.SIGALRM, orig_handler) else: - log.error('Failed to check hwclock sync') + log.error("Failed to check hwclock sync") def _save_machine_info(self): - if os.path.isfile('/etc/machine-info'): - with salt.utils.files.fopen('/etc/machine-info', 'r') as mach_info: + if os.path.isfile("/etc/machine-info"): + with salt.utils.files.fopen("/etc/machine-info", "r") as mach_info: self._machine_info = mach_info.read() else: self._machine_info = False def _restore_machine_info(self): if self._machine_info is not False: - with salt.utils.files.fopen('/etc/machine-info', 'w') as mach_info: + with salt.utils.files.fopen("/etc/machine-info", "w") as mach_info: mach_info.write(self._machine_info) else: - self.run_function('file.remove', ['/etc/machine-info']) + self.run_function("file.remove", ["/etc/machine-info"]) def test_get_system_date_time(self): - ''' + """ Test we are able to get the correct time - ''' + """ t1 = datetime.datetime.now() - res = self.run_function('system.get_system_date_time') + res = self.run_function("system.get_system_date_time") t2 = datetime.datetime.strptime(res, self.fmt_str) - msg = ("Difference in times is too large. Now: {0} Fake: {1}" - .format(t1, t2)) + msg = "Difference in times is too large. Now: {0} Fake: {1}".format(t1, t2) self.assertTrue(self._same_times(t1, t2, seconds_diff=2), msg=msg) def test_get_system_date_time_utc(self): - ''' + """ Test we are able to get the correct time with utc - ''' + """ t1 = datetime.datetime.utcnow() - res = self.run_function('system.get_system_date_time', - utc_offset="+0000") + res = self.run_function("system.get_system_date_time", utc_offset="+0000") t2 = datetime.datetime.strptime(res, self.fmt_str) - msg = ("Difference in times is too large. Now: {0} Fake: {1}" - .format(t1, t2)) + msg = "Difference in times is too large. Now: {0} Fake: {1}".format(t1, t2) self.assertTrue(self._same_times(t1, t2, seconds_diff=2), msg=msg) @destructiveTest @skip_if_not_root def test_set_system_date_time(self): - ''' + """ Test changing the system clock. We are only able to set it up to a resolution of a second so this test may appear to run in negative time. - ''' + """ self._save_time() cmp_time = datetime.datetime.now() - datetime.timedelta(days=7) result = self._set_time(cmp_time) time_now = datetime.datetime.now() - msg = ("Difference in times is too large. Now: {0} Fake: {1}" - .format(time_now, cmp_time)) - self.assertTrue(result and self._same_times(time_now, cmp_time), - msg=msg) + msg = "Difference in times is too large. Now: {0} Fake: {1}".format( + time_now, cmp_time + ) + self.assertTrue(result and self._same_times(time_now, cmp_time), msg=msg) self._test_hwclock_sync() @destructiveTest @skip_if_not_root def test_set_system_date_time_utc(self): - ''' + """ Test changing the system clock. We are only able to set it up to a resolution of a second so this test may appear to run in negative time. - ''' + """ self._save_time() cmp_time = datetime.datetime.utcnow() - datetime.timedelta(days=7) result = self._set_time(cmp_time, offset="+0000") time_now = datetime.datetime.utcnow() - msg = ("Difference in times is too large. Now: {0} Fake: {1}" - .format(time_now, cmp_time)) + msg = "Difference in times is too large. Now: {0} Fake: {1}".format( + time_now, cmp_time + ) self.assertTrue(result) self.assertTrue(self._same_times(time_now, cmp_time), msg=msg) self._test_hwclock_sync() @@ -234,19 +242,20 @@ class SystemModuleTest(ModuleCase): @destructiveTest @skip_if_not_root def test_set_system_date_time_utcoffset_east(self): - ''' + """ Test changing the system clock. We are only able to set it up to a resolution of a second so this test may appear to run in negative time. - ''' + """ self._save_time() cmp_time = datetime.datetime.utcnow() - datetime.timedelta(days=7) # 25200 seconds = 7 hours time_to_set = cmp_time - datetime.timedelta(seconds=25200) - result = self._set_time(time_to_set, offset='-0700') + result = self._set_time(time_to_set, offset="-0700") time_now = datetime.datetime.utcnow() - msg = ("Difference in times is too large. Now: {0} Fake: {1}" - .format(time_now, cmp_time)) + msg = "Difference in times is too large. Now: {0} Fake: {1}".format( + time_now, cmp_time + ) self.assertTrue(result) self.assertTrue(self._same_times(time_now, cmp_time), msg=msg) self._test_hwclock_sync() @@ -254,19 +263,20 @@ class SystemModuleTest(ModuleCase): @destructiveTest @skip_if_not_root def test_set_system_date_time_utcoffset_west(self): - ''' + """ Test changing the system clock. We are only able to set it up to a resolution of a second so this test may appear to run in negative time. - ''' + """ self._save_time() cmp_time = datetime.datetime.utcnow() - datetime.timedelta(days=7) # 7200 seconds = 2 hours time_to_set = cmp_time + datetime.timedelta(seconds=7200) - result = self._set_time(time_to_set, offset='+0200') + result = self._set_time(time_to_set, offset="+0200") time_now = datetime.datetime.utcnow() - msg = ("Difference in times is too large. Now: {0} Fake: {1}" - .format(time_now, cmp_time)) + msg = "Difference in times is too large. Now: {0} Fake: {1}".format( + time_now, cmp_time + ) self.assertTrue(result) self.assertTrue(self._same_times(time_now, cmp_time), msg=msg) self._test_hwclock_sync() @@ -275,17 +285,18 @@ class SystemModuleTest(ModuleCase): @destructiveTest @skip_if_not_root def test_set_system_time(self): - ''' + """ Test setting the system time without adjusting the date. - ''' + """ cmp_time = datetime.datetime.now().replace(hour=10, minute=5, second=0) self._save_time() - result = self.run_function('system.set_system_time', ["10:05:00"]) + result = self.run_function("system.set_system_time", ["10:05:00"]) time_now = datetime.datetime.now() - msg = ("Difference in times is too large. Now: {0} Fake: {1}" - .format(time_now, cmp_time)) + msg = "Difference in times is too large. Now: {0} Fake: {1}".format( + time_now, cmp_time + ) self.assertTrue(result) self.assertTrue(self._same_times(time_now, cmp_time), msg=msg) @@ -294,20 +305,20 @@ class SystemModuleTest(ModuleCase): @destructiveTest @skip_if_not_root def test_set_system_date(self): - ''' + """ Test setting the system date without adjusting the time. - ''' + """ cmp_time = datetime.datetime.now() - datetime.timedelta(days=7) self._save_time() result = self.run_function( - 'system.set_system_date', - [cmp_time.strftime('%Y-%m-%d')] + "system.set_system_date", [cmp_time.strftime("%Y-%m-%d")] ) time_now = datetime.datetime.now() - msg = ("Difference in times is too large. Now: {0} Fake: {1}" - .format(time_now, cmp_time)) + msg = "Difference in times is too large. Now: {0} Fake: {1}".format( + time_now, cmp_time + ) self.assertTrue(result) self.assertTrue(self._same_times(time_now, cmp_time), msg=msg) @@ -315,33 +326,33 @@ class SystemModuleTest(ModuleCase): @skip_if_not_root def test_get_computer_desc(self): - ''' + """ Test getting the system hostname - ''' - res = self.run_function('system.get_computer_desc') + """ + res = self.run_function("system.get_computer_desc") - hostname_cmd = salt.utils.path.which('hostnamectl') + hostname_cmd = salt.utils.path.which("hostnamectl") if hostname_cmd: - desc = self.run_function('cmd.run', ["hostnamectl status --pretty"]) + desc = self.run_function("cmd.run", ["hostnamectl status --pretty"]) self.assertEqual(res, desc) else: - if not os.path.isfile('/etc/machine-info'): + if not os.path.isfile("/etc/machine-info"): self.assertFalse(res) else: - with salt.utils.files.fopen('/etc/machine-info', 'r') as mach_info: + with salt.utils.files.fopen("/etc/machine-info", "r") as mach_info: data = mach_info.read() - self.assertIn(res, data.decode('string_escape')) + self.assertIn(res, data.decode("string_escape")) @destructiveTest @skip_if_not_root def test_set_computer_desc(self): - ''' + """ Test setting the computer description - ''' + """ self._save_machine_info() desc = "test" - ret = self.run_function('system.set_computer_desc', [desc]) - computer_desc = self.run_function('system.get_computer_desc') + ret = self.run_function("system.set_computer_desc", [desc]) + computer_desc = self.run_function("system.get_computer_desc") self.assertTrue(ret) self.assertIn(desc, computer_desc) @@ -349,20 +360,22 @@ class SystemModuleTest(ModuleCase): @destructiveTest @skip_if_not_root def test_set_computer_desc_multiline(self): - ''' + """ Test setting the computer description with a multiline string with tabs and double-quotes. - ''' + """ self._save_machine_info() - desc = textwrap.dedent('''\ + desc = textwrap.dedent( + '''\ 'First Line \tSecond Line: 'single-quoted string' - \t\tThird Line: "double-quoted string with unicode: питон"''') - ret = self.run_function('system.set_computer_desc', [desc]) + \t\tThird Line: "double-quoted string with unicode: питон"''' + ) + ret = self.run_function("system.set_computer_desc", [desc]) # self.run_function returns the serialized return, we need to convert # back to unicode to compare to desc. in the assertIn below. computer_desc = salt.utils.stringutils.to_unicode( - self.run_function('system.get_computer_desc') + self.run_function("system.get_computer_desc") ) self.assertTrue(ret) @@ -370,133 +383,135 @@ class SystemModuleTest(ModuleCase): @skip_if_not_root def test_has_hwclock(self): - ''' + """ Verify platform has a settable hardware clock, if possible. - ''' - if self.run_function('grains.get', ['os_family']) == 'NILinuxRT': - self.assertTrue(self.run_function('system._has_settable_hwclock')) + """ + if self.run_function("grains.get", ["os_family"]) == "NILinuxRT": + self.assertTrue(self.run_function("system._has_settable_hwclock")) self.assertTrue(self._hwclock_has_compare()) -@skipIf(not salt.utils.platform.is_windows(), - 'These tests can only be run on windows') +@skipIf(not salt.utils.platform.is_windows(), "These tests can only be run on windows") +@pytest.mark.windows_whitelisted class WinSystemModuleTest(ModuleCase): - ''' + """ Validate the date/time functions in the win_system module - ''' + """ + @classmethod def setUpClass(cls): - if subprocess.call('net stop w32time', shell=True) != 0: - log.error('Failed to stop w32time service') + if subprocess.call("net stop w32time", shell=True) != 0: + log.error("Failed to stop w32time service") @classmethod def tearDownClass(cls): - if subprocess.call('net start w32time', shell=True) != 0: - log.error('Failed to start w32time service') - if subprocess.call('w32tm /resync', shell=True) != 0: + if subprocess.call("net start w32time", shell=True) != 0: + log.error("Failed to start w32time service") + if subprocess.call("w32tm /resync", shell=True) != 0: log.error("Re-syncing time failed") def test_get_computer_name(self): - ''' + """ Test getting the computer name - ''' - ret = self.run_function('system.get_computer_name') + """ + ret = self.run_function("system.get_computer_name") self.assertTrue(isinstance(ret, six.text_type)) import socket + name = socket.gethostname() self.assertEqual(name, ret) @destructiveTest def test_set_computer_desc(self): - ''' + """ Test setting the computer description - ''' - current_desc = self.run_function('system.get_computer_desc') - desc = 'test description' + """ + current_desc = self.run_function("system.get_computer_desc") + desc = "test description" try: - set_desc = self.run_function('system.set_computer_desc', [desc]) + set_desc = self.run_function("system.set_computer_desc", [desc]) self.assertTrue(set_desc) - get_desc = self.run_function('system.get_computer_desc') - self.assertEqual(set_desc['Computer Description'], get_desc) + get_desc = self.run_function("system.get_computer_desc") + self.assertEqual(set_desc["Computer Description"], get_desc) finally: - self.run_function('system.set_computer_desc', [current_desc]) + self.run_function("system.set_computer_desc", [current_desc]) - @skipIf(True, 'WAR ROOM 7/29/2019, unit test?') + @skipIf(True, "WAR ROOM 7/29/2019, unit test?") def test_get_system_time(self): - ''' + """ Test getting the system time - ''' + """ time_now = datetime.datetime.now() # We have to do some datetime fu to account for the possibility that the # system time will be obtained just before the minutes increment - ret = self.run_function('system.get_system_time', timeout=300) + ret = self.run_function("system.get_system_time", timeout=300) # Split out time and AM/PM - sys_time, meridian = ret.split(' ') - h, m, s = sys_time.split(':') + sys_time, meridian = ret.split(" ") + h, m, s = sys_time.split(":") # Get the current time # Use the system time to generate a datetime object for the system time # with the same date time_sys = time_now.replace(hour=int(h), minute=int(m), second=int(s)) # get_system_time returns a non 24 hour time # Lets make it 24 hour time - if meridian == 'PM': + if meridian == "PM": time_sys = time_sys + datetime.timedelta(hours=12) diff = time_sys - time_now # Timeouts are set to 300 seconds. We're adding a 30 second buffer self.assertTrue(diff.seconds < 330) - @skipIf(True, 'WAR ROOM 7/18/2019, unit test?') + @skipIf(True, "WAR ROOM 7/18/2019, unit test?") @destructiveTest def test_set_system_time(self): - ''' + """ Test setting the system time .. note:: In order for this test to pass, time sync must be disabled for the VM in the hypervisor - ''' - self.run_function('service.stop', ['w32time']) + """ + self.run_function("service.stop", ["w32time"]) try: - current_time = datetime.datetime.now().strftime('%H:%M:%S') - test_time = '10:55' - self.run_function('system.set_system_time', [test_time + ' AM']) - time.sleep(.25) - new_time = datetime.datetime.now().strftime('%H:%M') + current_time = datetime.datetime.now().strftime("%H:%M:%S") + test_time = "10:55" + self.run_function("system.set_system_time", [test_time + " AM"]) + time.sleep(0.25) + new_time = datetime.datetime.now().strftime("%H:%M") self.assertEqual(new_time, test_time) finally: - self.run_function('system.set_system_time', [current_time]) - self.run_function('service.start', ['w32time']) + self.run_function("system.set_system_time", [current_time]) + self.run_function("service.start", ["w32time"]) def test_get_system_date(self): - ''' + """ Test getting system date - ''' - ret = self.run_function('system.get_system_date') + """ + ret = self.run_function("system.get_system_date") date = datetime.datetime.now().strftime("%m/%d/%Y") self.assertEqual(date, ret) - @skipIf(True, 'WAR ROOM 7/18/2019, unit test?') + @skipIf(True, "WAR ROOM 7/18/2019, unit test?") @destructiveTest def test_set_system_date(self): - ''' + """ Test setting system date .. note:: In order for this test to pass, time sync must be disabled for the VM in the hypervisor - ''' - self.run_function('service.stop', ['w32time']) + """ + self.run_function("service.stop", ["w32time"]) try: # If the test still fails, the hypervisor may be maintaining time # sync - current_date = datetime.datetime.now().strftime('%Y/%m/%d') - self.run_function('system.set_system_date', ['03/25/2018']) - new_date = datetime.datetime.now().strftime('%Y/%m/%d') - self.assertEqual(new_date, '2018/03/25') + current_date = datetime.datetime.now().strftime("%Y/%m/%d") + self.run_function("system.set_system_date", ["03/25/2018"]) + new_date = datetime.datetime.now().strftime("%Y/%m/%d") + self.assertEqual(new_date, "2018/03/25") finally: - self.run_function('system.set_system_date', [current_date]) - self.run_function('service.start', ['w32time']) + self.run_function("system.set_system_date", [current_date]) + self.run_function("service.start", ["w32time"]) diff --git a/tests/integration/modules/test_test.py b/tests/integration/modules/test_test.py index 5b6bcc05aea..7ed82946f19 100644 --- a/tests/integration/modules/test_test.py +++ b/tests/integration/modules/test_test.py @@ -1,95 +1,75 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest +import salt.config +import salt.version from tests.support.case import ModuleCase from tests.support.mixins import AdaptedConfigurationTestCaseMixin -# Import salt libs -import salt.version -import salt.config - +@pytest.mark.windows_whitelisted class TestModuleTest(ModuleCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Validate the test module - ''' + """ + def test_ping(self): - ''' + """ test.ping - ''' - self.assertTrue(self.run_function('test.ping')) + """ + self.assertTrue(self.run_function("test.ping")) def test_echo(self): - ''' + """ test.echo - ''' - self.assertEqual(self.run_function('test.echo', ['text']), 'text') + """ + self.assertEqual(self.run_function("test.echo", ["text"]), "text") def test_version(self): - ''' + """ test.version - ''' - self.assertEqual(self.run_function('test.version'), - salt.version.__saltstack_version__.string) + """ + self.assertEqual( + self.run_function("test.version"), salt.version.__saltstack_version__.string + ) def test_conf_test(self): - ''' + """ test.conf_test - ''' - self.assertEqual(self.run_function('test.conf_test'), 'baz') + """ + self.assertEqual(self.run_function("test.conf_test"), "baz") def test_get_opts(self): - ''' + """ test.get_opts - ''' - opts = salt.config.minion_config( - self.get_config_file_path('minion') - ) + """ + opts = salt.config.minion_config(self.get_config_file_path("minion")) self.assertEqual( - self.run_function('test.get_opts')['cachedir'], - opts['cachedir'] + self.run_function("test.get_opts")["cachedir"], opts["cachedir"] ) def test_cross_test(self): - ''' + """ test.cross_test - ''' - self.assertTrue( - self.run_function( - 'test.cross_test', - ['test.ping'] - ) - ) + """ + self.assertTrue(self.run_function("test.cross_test", ["test.ping"])) def test_fib(self): - ''' + """ test.fib - ''' - self.assertEqual( - self.run_function( - 'test.fib', - ['20'], - )[0], - 6765 - ) + """ + self.assertEqual(self.run_function("test.fib", ["20"],)[0], 6765) def test_collatz(self): - ''' + """ test.collatz - ''' - self.assertEqual( - self.run_function( - 'test.collatz', - ['40'], - )[0][-1], - 2 - ) + """ + self.assertEqual(self.run_function("test.collatz", ["40"],)[0][-1], 2) def test_outputter(self): - ''' + """ test.outputter - ''' - self.assertEqual(self.run_function('test.outputter', ['text']), 'text') + """ + self.assertEqual(self.run_function("test.outputter", ["text"]), "text") diff --git a/tests/integration/modules/test_timezone.py b/tests/integration/modules/test_timezone.py index 02e0765bfb0..2e66ae5cada 100644 --- a/tests/integration/modules/test_timezone.py +++ b/tests/integration/modules/test_timezone.py @@ -1,96 +1,97 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for timezone module Linux and Solaris are supported -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -try: - import tzlocal # pylint: disable=unused-import - HAS_TZLOCAL = True -except ImportError: - HAS_TZLOCAL = False +# Import salt libs +import salt.utils.platform # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest from tests.support.unit import skipIf -# Import salt libs -import salt.utils.platform +try: + import tzlocal # pylint: disable=unused-import + + HAS_TZLOCAL = True +except ImportError: + HAS_TZLOCAL = False class TimezoneLinuxModuleTest(ModuleCase): def setUp(self): - ''' + """ Set up Linux test environment - ''' - ret_grain = self.run_function('grains.item', ['kernel']) - if 'Linux' not in ret_grain['kernel']: - self.skipTest('For Linux only') + """ + ret_grain = self.run_function("grains.item", ["kernel"]) + if "Linux" not in ret_grain["kernel"]: + self.skipTest("For Linux only") super(TimezoneLinuxModuleTest, self).setUp() def test_get_hwclock(self): - timescale = ['UTC', 'localtime'] - ret = self.run_function('timezone.get_hwclock') + timescale = ["UTC", "localtime"] + ret = self.run_function("timezone.get_hwclock") self.assertIn(ret, timescale) class TimezoneSolarisModuleTest(ModuleCase): def setUp(self): - ''' + """ Set up Solaris test environment - ''' - ret_grain = self.run_function('grains.item', ['os_family']) - if 'Solaris' not in ret_grain['os_family']: - self.skipTest('For Solaris only') + """ + ret_grain = self.run_function("grains.item", ["os_family"]) + if "Solaris" not in ret_grain["os_family"]: + self.skipTest("For Solaris only") super(TimezoneSolarisModuleTest, self).setUp() def test_get_hwclock(self): - timescale = ['UTC', 'localtime'] - ret = self.run_function('timezone.get_hwclock') + timescale = ["UTC", "localtime"] + ret = self.run_function("timezone.get_hwclock") self.assertIn(ret, timescale) -@skipIf(not salt.utils.platform.is_windows(), 'windows test only') +@skipIf(not salt.utils.platform.is_windows(), "windows test only") class TimezoneWindowsModuleTest(ModuleCase): def setUp(self): - self.pre = self.run_function('timezone.get_zone') + self.pre = self.run_function("timezone.get_zone") def tearDown(self): - post = self.run_function('timezone.get_zone') + post = self.run_function("timezone.get_zone") if self.pre != post: - self.run_function('timezone.set_zone', [self.pre]) + self.run_function("timezone.set_zone", [self.pre]) def test_get_hwclock(self): - timescale = ['UTC', 'localtime'] - ret = self.run_function('timezone.get_hwclock') + timescale = ["UTC", "localtime"] + ret = self.run_function("timezone.get_hwclock") self.assertIn(ret, timescale) @destructiveTest def test_get_zone(self): - ''' + """ test timezone.set_zone, get_zone and zone_compare - ''' + """ - zone = 'America/Inuvik' if not HAS_TZLOCAL else 'America/Denver' + zone = "America/Inuvik" if not HAS_TZLOCAL else "America/Denver" # first set the zone - assert self.run_function('timezone.set_zone', [zone]) + assert self.run_function("timezone.set_zone", [zone]) # check it set the correct zone - ret = self.run_function('timezone.get_zone') + ret = self.run_function("timezone.get_zone") assert zone in ret # compare zones - assert self.run_function('timezone.zone_compare', [zone]) + assert self.run_function("timezone.zone_compare", [zone]) def test_get_offset(self): - ''' + """ test timezone.get_offset - ''' - ret = self.run_function('timezone.get_offset') - self.assertIn('-', ret) + """ + ret = self.run_function("timezone.get_offset") + self.assertIn("-", ret) diff --git a/tests/integration/modules/test_useradd.py b/tests/integration/modules/test_useradd.py index e468110c55b..3ace63c822b 100644 --- a/tests/integration/modules/test_useradd.py +++ b/tests/integration/modules/test_useradd.py @@ -1,45 +1,36 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import string -import random -# Import Salt Testing libs +import random +import string + +import pytest +import salt.utils.platform +from salt.ext.six.moves import range from tests.support.case import ModuleCase -from tests.support.unit import skipIf from tests.support.helpers import ( destructiveTest, + requires_system_grains, skip_if_not_root, - requires_system_grains ) - -# Import Salt libs -import salt.utils.platform - -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from tests.support.unit import skipIf @destructiveTest -@skipIf(not salt.utils.platform.is_linux(), 'These tests can only be run on linux') +@skipIf(not salt.utils.platform.is_linux(), "These tests can only be run on linux") @skip_if_not_root +@pytest.mark.windows_whitelisted class UseraddModuleTestLinux(ModuleCase): - def setUp(self): super(UseraddModuleTestLinux, self).setUp() - os_grain = self.run_function('grains.item', ['kernel']) - if os_grain['kernel'] not in ('Linux', 'Darwin'): - self.skipTest( - 'Test not applicable to \'{kernel}\' kernel'.format( - **os_grain - ) - ) + os_grain = self.run_function("grains.item", ["kernel"]) + if os_grain["kernel"] not in ("Linux", "Darwin"): + self.skipTest("Test not applicable to '{kernel}' kernel".format(**os_grain)) def __random_string(self, size=6): - return 'RS-' + ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(size) + return "RS-" + "".join( + random.choice(string.ascii_uppercase + string.digits) for x in range(size) ) @requires_system_grains @@ -47,203 +38,207 @@ class UseraddModuleTestLinux(ModuleCase): # Let's create a user, which usually creates the group matching the # name uname = self.__random_string() - if self.run_function('user.add', [uname]) is not True: + if self.run_function("user.add", [uname]) is not True: # Skip because creating is not what we're testing here - self.run_function('user.delete', [uname, True, True]) - self.skipTest('Failed to create user') + self.run_function("user.delete", [uname, True, True]) + self.skipTest("Failed to create user") try: - uinfo = self.run_function('user.info', [uname]) - if grains['os_family'] in ('Suse',): - self.assertIn('users', uinfo['groups']) + uinfo = self.run_function("user.info", [uname]) + if grains["os_family"] in ("Suse",): + self.assertIn("users", uinfo["groups"]) else: - self.assertIn(uname, uinfo['groups']) + self.assertIn(uname, uinfo["groups"]) # This uid is available, store it - uid = uinfo['uid'] + uid = uinfo["uid"] - self.run_function('user.delete', [uname, True, True]) + self.run_function("user.delete", [uname, True, True]) # Now, a weird group id gname = self.__random_string() - if self.run_function('group.add', [gname]) is not True: - self.run_function('group.delete', [gname, True, True]) - self.skipTest('Failed to create group') + if self.run_function("group.add", [gname]) is not True: + self.run_function("group.delete", [gname, True, True]) + self.skipTest("Failed to create group") - ginfo = self.run_function('group.info', [gname]) + ginfo = self.run_function("group.info", [gname]) # And create the user with that gid - if self.run_function('user.add', [uname, uid, ginfo['gid']]) is False: + if self.run_function("user.add", [uname, uid, ginfo["gid"]]) is False: # Skip because creating is not what we're testing here - self.run_function('user.delete', [uname, True, True]) - self.skipTest('Failed to create user') + self.run_function("user.delete", [uname, True, True]) + self.skipTest("Failed to create user") - uinfo = self.run_function('user.info', [uname]) - self.assertIn(gname, uinfo['groups']) + uinfo = self.run_function("user.info", [uname]) + self.assertIn(gname, uinfo["groups"]) except AssertionError: - self.run_function('user.delete', [uname, True, True]) + self.run_function("user.delete", [uname, True, True]) raise def test_user_primary_group(self): - ''' + """ Tests the primary_group function - ''' - name = 'saltyuser' + """ + name = "saltyuser" # Create a user to test primary group function - if self.run_function('user.add', [name]) is not True: - self.run_function('user.delete', [name]) - self.skipTest('Failed to create a user') + if self.run_function("user.add", [name]) is not True: + self.run_function("user.delete", [name]) + self.skipTest("Failed to create a user") try: # Test useradd.primary_group - primary_group = self.run_function('user.primary_group', [name]) - uid_info = self.run_function('user.info', [name]) - self.assertIn(primary_group, uid_info['groups']) + primary_group = self.run_function("user.primary_group", [name]) + uid_info = self.run_function("user.info", [name]) + self.assertIn(primary_group, uid_info["groups"]) except Exception: # pylint: disable=broad-except - self.run_function('user.delete', [name]) + self.run_function("user.delete", [name]) raise @destructiveTest -@skipIf(not salt.utils.platform.is_windows(), 'These tests can only be run on Windows') +@skipIf(not salt.utils.platform.is_windows(), "These tests can only be run on Windows") @skip_if_not_root +@pytest.mark.windows_whitelisted class UseraddModuleTestWindows(ModuleCase): - def __random_string(self, size=6): - return 'RS-' + ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(size)) + return "RS-" + "".join( + random.choice(string.ascii_uppercase + string.digits) for x in range(size) + ) def setUp(self): self.user_name = self.__random_string() self.group_name = self.__random_string() def tearDown(self): - self.run_function('user.delete', [self.user_name, True, True]) - self.run_function('group.delete', [self.group_name]) + self.run_function("user.delete", [self.user_name, True, True]) + self.run_function("group.delete", [self.group_name]) def _add_user(self): - ''' + """ helper class to add user - ''' - if self.run_function('user.add', [self.user_name]) is False: + """ + if self.run_function("user.add", [self.user_name]) is False: # Skip because creating is not what we're testing here - self.skipTest('Failed to create user') + self.skipTest("Failed to create user") def _add_group(self): - ''' + """ helper class to add group - ''' - if self.run_function('group.add', [self.group_name]) is False: + """ + if self.run_function("group.add", [self.group_name]) is False: # Skip because creating is not what we're testing here - self.skipTest('Failed to create group') + self.skipTest("Failed to create group") def test_add_user(self): - ''' + """ Test adding a user - ''' + """ self._add_user() - user_list = self.run_function('user.list_users') + user_list = self.run_function("user.list_users") self.assertIn(self.user_name, user_list) def test_add_group(self): - ''' + """ Test adding a user - ''' + """ self._add_group() - group_list = self.run_function('group.list_groups') + group_list = self.run_function("group.list_groups") self.assertIn(self.group_name, group_list) def test_add_user_to_group(self): - ''' + """ Test adding a user to a group - ''' + """ self._add_group() # And create the user as a member of that group - self.run_function('user.add', [self.user_name], groups=self.group_name) - user_info = self.run_function('user.info', [self.user_name]) - self.assertIn(self.group_name, user_info['groups']) + self.run_function("user.add", [self.user_name], groups=self.group_name) + user_info = self.run_function("user.info", [self.user_name]) + self.assertIn(self.group_name, user_info["groups"]) def test_add_user_addgroup(self): - ''' + """ Test adding a user to a group with groupadd - ''' + """ self._add_group() self._add_user() - self.run_function('user.addgroup', [self.user_name, self.group_name]) - info = self.run_function('user.info', [self.user_name]) - self.assertEqual(info['groups'], [self.group_name]) + self.run_function("user.addgroup", [self.user_name, self.group_name]) + info = self.run_function("user.info", [self.user_name]) + self.assertEqual(info["groups"], [self.group_name]) def test_user_chhome(self): - ''' + """ Test changing a users home dir - ''' + """ self._add_user() - user_dir = r'c:\salt' - self.run_function('user.chhome', [self.user_name, user_dir]) - info = self.run_function('user.info', [self.user_name]) - self.assertEqual(info['home'], user_dir) + user_dir = r"c:\salt" + self.run_function("user.chhome", [self.user_name, user_dir]) + info = self.run_function("user.info", [self.user_name]) + self.assertEqual(info["home"], user_dir) def test_user_chprofile(self): - ''' + """ Test changing a users profile - ''' + """ self._add_user() - config = r'c:\salt\config' - self.run_function('user.chprofile', [self.user_name, config]) - info = self.run_function('user.info', [self.user_name]) - self.assertEqual(info['profile'], config) + config = r"c:\salt\config" + self.run_function("user.chprofile", [self.user_name, config]) + info = self.run_function("user.info", [self.user_name]) + self.assertEqual(info["profile"], config) def test_user_chfullname(self): - ''' + """ Test changing a users fullname - ''' + """ self._add_user() - name = 'Salt Test' - self.run_function('user.chfullname', [self.user_name, name]) - info = self.run_function('user.info', [self.user_name]) - self.assertEqual(info['fullname'], name) + name = "Salt Test" + self.run_function("user.chfullname", [self.user_name, name]) + info = self.run_function("user.info", [self.user_name]) + self.assertEqual(info["fullname"], name) def test_user_delete(self): - ''' + """ Test deleting a user - ''' + """ self._add_user() - self.assertTrue(self.run_function('user.info', [self.user_name])['active']) - self.run_function('user.delete', [self.user_name]) - self.assertEqual({}, self.run_function('user.info', [self.user_name])) + self.assertTrue(self.run_function("user.info", [self.user_name])["active"]) + self.run_function("user.delete", [self.user_name]) + self.assertEqual({}, self.run_function("user.info", [self.user_name])) def test_user_removegroup(self): - ''' + """ Test removing a group - ''' + """ self._add_user() self._add_group() - self.run_function('user.addgroup', [self.user_name, self.group_name]) - self.assertIn(self.group_name, self.run_function('user.list_groups', [self.user_name])) - self.run_function('user.removegroup', [self.user_name, self.group_name]) - self.assertNotIn(self.group_name, self.run_function('user.list_groups', [self.user_name])) + self.run_function("user.addgroup", [self.user_name, self.group_name]) + self.assertIn( + self.group_name, self.run_function("user.list_groups", [self.user_name]) + ) + self.run_function("user.removegroup", [self.user_name, self.group_name]) + self.assertNotIn( + self.group_name, self.run_function("user.list_groups", [self.user_name]) + ) def test_user_rename(self): - ''' + """ Test changing a users name - ''' + """ self._add_user() - name = 'newuser' - self.run_function('user.rename', [self.user_name, name]) - info = self.run_function('user.info', [name]) - self.assertTrue(info['active']) + name = "newuser" + self.run_function("user.rename", [self.user_name, name]) + info = self.run_function("user.info", [name]) + self.assertTrue(info["active"]) - #delete new user - self.run_function('user.delete', [name, True, True]) + # delete new user + self.run_function("user.delete", [name, True, True]) def test_user_setpassword(self): - ''' + """ Test setting a password - ''' + """ self._add_user() - passwd = 'sup3rs3cr3T!' - self.assertTrue(self.run_function('user.setpassword', [self.user_name, passwd])) + passwd = "sup3rs3cr3T!" + self.assertTrue(self.run_function("user.setpassword", [self.user_name, passwd])) diff --git a/tests/integration/modules/test_vault.py b/tests/integration/modules/test_vault.py index b98c25bd005..aef91ee63ff 100644 --- a/tests/integration/modules/test_vault.py +++ b/tests/integration/modules/test_vault.py @@ -1,100 +1,290 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the vault execution module -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import inspect -import time -# Import Salt Testing Libs -from tests.support.unit import skipIf -from tests.support.case import ModuleCase -from tests.support.helpers import destructiveTest, flaky -from tests.support.paths import FILES +import inspect +import logging +import time # Import Salt Libs import salt.utils.path +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.paths import FILES + +# Import Salt Testing Libs +from tests.support.unit import skipIf -import logging log = logging.getLogger(__name__) @destructiveTest -@skipIf(not salt.utils.path.which('dockerd'), 'Docker not installed') -@skipIf(not salt.utils.path.which('vault'), 'Vault not installed') +@skipIf(not salt.utils.path.which("dockerd"), "Docker not installed") +@skipIf(not salt.utils.path.which("vault"), "Vault not installed") class VaultTestCase(ModuleCase): - ''' + """ Test vault module - ''' + """ + count = 0 def setUp(self): - ''' + """ SetUp vault container - ''' + """ if self.count == 0: - config = '{"backend": {"file": {"path": "/vault/file"}}, "default_lease_ttl": "168h", "max_lease_ttl": "720h"}' - self.run_state('docker_image.present', name='vault', tag='0.9.6') + config = '{"backend": {"file": {"path": "/vault/file"}}, "default_lease_ttl": "168h", "max_lease_ttl": "720h", "disable_mlock": true}' + self.run_state("docker_image.present", name="vault", tag="0.9.6") self.run_state( - 'docker_container.running', - name='vault', - image='vault:0.9.6', - port_bindings='8200:8200', + "docker_container.running", + name="vault", + image="vault:0.9.6", + port_bindings="8200:8200", environment={ - 'VAULT_DEV_ROOT_TOKEN_ID': 'testsecret', - 'VAULT_LOCAL_CONFIG': config, + "VAULT_DEV_ROOT_TOKEN_ID": "testsecret", + "VAULT_LOCAL_CONFIG": config, }, - cap_add='IPC_LOCK', ) time.sleep(5) ret = self.run_function( - 'cmd.retcode', - cmd='/usr/local/bin/vault login token=testsecret', - env={'VAULT_ADDR': 'http://127.0.0.1:8200'}, + "cmd.retcode", + cmd="/usr/local/bin/vault login token=testsecret", + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, ) if ret != 0: - self.skipTest('unable to login to vault') + self.skipTest("unable to login to vault") ret = self.run_function( - 'cmd.retcode', - cmd='/usr/local/bin/vault policy write testpolicy {0}/vault.hcl'.format(FILES), - env={'VAULT_ADDR': 'http://127.0.0.1:8200'}, + "cmd.retcode", + cmd="/usr/local/bin/vault policy write testpolicy {0}/vault.hcl".format( + FILES + ), + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, ) if ret != 0: - self.skipTest('unable to assign policy to vault') + self.skipTest("unable to assign policy to vault") self.count += 1 def tearDown(self): - ''' + """ TearDown vault container - ''' + """ + def count_tests(funcobj): - return inspect.ismethod(funcobj) and funcobj.__name__.startswith('test_') + return inspect.ismethod(funcobj) and funcobj.__name__.startswith("test_") + numtests = len(inspect.getmembers(VaultTestCase, predicate=count_tests)) if self.count >= numtests: - self.run_state('docker_container.stopped', name='vault') - self.run_state('docker_container.absent', name='vault') - self.run_state('docker_image.absent', name='vault', force=True) + self.run_state("docker_container.stopped", name="vault") + self.run_state("docker_container.absent", name="vault") + self.run_state("docker_image.absent", name="vault", force=True) - @flaky def test_write_read_secret(self): - assert self.run_function('vault.write_secret', path='secret/my/secret', user='foo', password='bar') is True - assert self.run_function('vault.read_secret', arg=['secret/my/secret']) == {'password': 'bar', 'user': 'foo'} + write_return = self.run_function( + "vault.write_secret", path="secret/my/secret", user="foo", password="bar" + ) + self.assertEqual(write_return, True) + assert self.run_function("vault.read_secret", arg=["secret/my/secret"]) == { + "password": "bar", + "user": "foo", + } + assert ( + self.run_function("vault.read_secret", arg=["secret/my/secret", "user"]) + == "foo" + ) - @flaky def test_write_raw_read_secret(self): - assert self.run_function('vault.write_raw', - path='secret/my/secret', - raw={"user": "foo", "password": "bar"}) is True - assert self.run_function('vault.read_secret', arg=['secret/my/secret']) == {'password': 'bar', 'user': 'foo'} + assert ( + self.run_function( + "vault.write_raw", + path="secret/my/secret2", + raw={"user2": "foo2", "password2": "bar2"}, + ) + is True + ) + assert self.run_function("vault.read_secret", arg=["secret/my/secret2"]) == { + "password2": "bar2", + "user2": "foo2", + } - @flaky def test_delete_secret(self): - assert self.run_function('vault.write_secret', path='secret/my/secret', user='foo', password='bar') is True - assert self.run_function('vault.delete_secret', arg=['secret/my/secret']) is True + assert ( + self.run_function( + "vault.write_secret", + path="secret/my/secret", + user="foo", + password="bar", + ) + is True + ) + assert ( + self.run_function("vault.delete_secret", arg=["secret/my/secret"]) is True + ) - @flaky def test_list_secrets(self): - assert self.run_function('vault.write_secret', path='secret/my/secret', user='foo', password='bar') is True - assert self.run_function('vault.list_secrets', arg=['secret/my/']) == {'keys': ['secret']} + assert ( + self.run_function( + "vault.write_secret", + path="secret/my/secret", + user="foo", + password="bar", + ) + is True + ) + assert self.run_function("vault.list_secrets", arg=["secret/my/"]) == { + "keys": ["secret"] + } + + +@destructiveTest +@skipIf(not salt.utils.path.which("dockerd"), "Docker not installed") +@skipIf(not salt.utils.path.which("vault"), "Vault not installed") +class VaultTestCaseCurrent(ModuleCase): + """ + Test vault module against current vault + """ + + count = 0 + + def setUp(self): + """ + SetUp vault container + """ + if self.count == 0: + config = '{"backend": {"file": {"path": "/vault/file"}}, "default_lease_ttl": "168h", "max_lease_ttl": "720h", "disable_mlock": true}' + self.run_state("docker_image.present", name="vault", tag="1.3.1") + self.run_state( + "docker_container.running", + name="vault", + image="vault:1.3.1", + port_bindings="8200:8200", + environment={ + "VAULT_DEV_ROOT_TOKEN_ID": "testsecret", + "VAULT_LOCAL_CONFIG": config, + }, + ) + time.sleep(5) + ret = self.run_function( + "cmd.retcode", + cmd="/usr/local/bin/vault login token=testsecret", + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, + ) + if ret != 0: + self.skipTest("unable to login to vault") + ret = self.run_function( + "cmd.retcode", + cmd="/usr/local/bin/vault policy write testpolicy {0}/vault.hcl".format( + FILES + ), + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, + ) + if ret != 0: + self.skipTest("unable to assign policy to vault") + self.count += 1 + + def tearDown(self): + """ + TearDown vault container + """ + + def count_tests(funcobj): + return inspect.ismethod(funcobj) and funcobj.__name__.startswith("test_") + + numtests = len(inspect.getmembers(VaultTestCaseCurrent, predicate=count_tests)) + if self.count >= numtests: + self.run_state("docker_container.stopped", name="vault") + self.run_state("docker_container.absent", name="vault") + self.run_state("docker_image.absent", name="vault", force=True) + + def test_write_read_secret_kv2(self): + write_return = self.run_function( + "vault.write_secret", path="secret/my/secret", user="foo", password="bar" + ) + # write_secret output: + # {'created_time': '2020-01-12T23:09:34.571294241Z', 'destroyed': False, + # 'version': 1, 'deletion_time': ''} + expected_write = {"destroyed": False, "deletion_time": ""} + self.assertDictContainsSubset(expected_write, write_return) + + read_return = self.run_function( + "vault.read_secret", arg=["secret/my/secret"], metadata=True + ) + # read_secret output: + # {'data': {'password': 'bar', 'user': 'foo'}, + # 'metadata': {'created_time': '2020-01-12T23:07:18.829326918Z', 'destroyed': False, + # 'version': 1, 'deletion_time': ''}} + expected_read = {"data": {"password": "bar", "user": "foo"}} + self.assertDictContainsSubset(expected_read, read_return) + expected_read = {"password": "bar", "user": "foo"} + read_return = self.run_function("vault.read_secret", arg=["secret/my/secret"]) + self.assertDictContainsSubset(expected_read, read_return) + + read_return = self.run_function( + "vault.read_secret", arg=["secret/my/secret", "user"] + ) + self.assertEqual(read_return, "foo") + + def test_list_secrets_kv2(self): + write_return = self.run_function( + "vault.write_secret", path="secret/my/secret", user="foo", password="bar" + ) + expected_write = {"destroyed": False, "deletion_time": ""} + self.assertDictContainsSubset(expected_write, write_return) + + list_return = self.run_function("vault.list_secrets", arg=["secret/my/"]) + self.assertIn("secret", list_return["keys"]) + + def test_write_raw_read_secret_kv2(self): + write_return = self.run_function( + "vault.write_raw", + path="secret/my/secret2", + raw={"user2": "foo2", "password2": "bar2"}, + ) + expected_write = {"destroyed": False, "deletion_time": ""} + self.assertDictContainsSubset(expected_write, write_return) + + read_return = self.run_function( + "vault.read_secret", arg=["secret/my/secret2"], metadata=True + ) + expected_read = {"data": {"password2": "bar2", "user2": "foo2"}} + self.assertDictContainsSubset(expected_read, read_return) + + read_return = self.run_function("vault.read_secret", arg=["secret/my/secret2"]) + expected_read = {"password2": "bar2", "user2": "foo2"} + self.assertDictContainsSubset(expected_read, read_return) + + def test_delete_secret_kv2(self): + write_return = self.run_function( + "vault.write_secret", + path="secret/my/secret3", + user3="foo3", + password3="bar3", + ) + expected_write = {"destroyed": False, "deletion_time": ""} + self.assertDictContainsSubset(expected_write, write_return) + + delete_return = self.run_function( + "vault.delete_secret", arg=["secret/my/secret3"] + ) + self.assertEqual(delete_return, True) + + def test_destroy_secret_kv2(self): + write_return = self.run_function( + "vault.write_secret", + path="secret/my/secret4", + user3="foo4", + password4="bar4", + ) + expected_write = {"destroyed": False, "deletion_time": ""} + self.assertDictContainsSubset(expected_write, write_return) + + destroy_return = self.run_function( + "vault.destroy_secret", arg=["secret/my/secret4", "1"] + ) + self.assertEqual(destroy_return, True) + # self.assertIsNone(self.run_function('vault.read_secret', arg=['secret/my/secret4'])) + # list_return = self.run_function('vault.list_secrets', arg=['secret/my/']) + # self.assertNotIn('secret4', list_return['keys']) diff --git a/tests/integration/modules/test_virt.py b/tests/integration/modules/test_virt.py index 8474f557820..c1e6656c2b3 100644 --- a/tests/integration/modules/test_virt.py +++ b/tests/integration/modules/test_virt.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Validate the virt module -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -11,34 +11,34 @@ from tests.support.case import ModuleCase from tests.support.helpers import requires_salt_modules -@requires_salt_modules('virt.get_profiles') +@requires_salt_modules("virt.get_profiles") class VirtTest(ModuleCase): - ''' + """ Test virt routines - ''' + """ def test_default_kvm_profile(self): - ''' + """ Test virt.get_profiles with the KVM profile - ''' - profiles = self.run_function('virt.get_profiles', ['kvm']) - nicp = profiles['nic']['default'] - self.assertTrue(nicp[0].get('model', '') == 'virtio') - self.assertTrue(nicp[0].get('source', '') == 'br0') - diskp = profiles['disk']['default'] - self.assertTrue(diskp[0]['system'].get('model', '') == 'virtio') - self.assertTrue(diskp[0]['system'].get('format', '') == 'qcow2') - self.assertTrue(diskp[0]['system'].get('size', '') == '8192') + """ + profiles = self.run_function("virt.get_profiles", ["kvm"]) + nicp = profiles["nic"]["default"] + self.assertTrue(nicp[0].get("model", "") == "virtio") + self.assertTrue(nicp[0].get("source", "") == "br0") + diskp = profiles["disk"]["default"] + self.assertTrue(diskp[0]["system"].get("model", "") == "virtio") + self.assertTrue(diskp[0]["system"].get("format", "") == "qcow2") + self.assertTrue(diskp[0]["system"].get("size", "") == "8192") def test_default_esxi_profile(self): - ''' + """ Test virt.get_profiles with the ESX profile - ''' - profiles = self.run_function('virt.get_profiles', ['esxi']) - nicp = profiles['nic']['default'] - self.assertTrue(nicp[0].get('model', '') == 'e1000') - self.assertTrue(nicp[0].get('source', '') == 'DEFAULT') - diskp = profiles['disk']['default'] - self.assertTrue(diskp[0]['system'].get('model', '') == 'scsi') - self.assertTrue(diskp[0]['system'].get('format', '') == 'vmdk') - self.assertTrue(diskp[0]['system'].get('size', '') == '8192') + """ + profiles = self.run_function("virt.get_profiles", ["esxi"]) + nicp = profiles["nic"]["default"] + self.assertTrue(nicp[0].get("model", "") == "e1000") + self.assertTrue(nicp[0].get("source", "") == "DEFAULT") + diskp = profiles["disk"]["default"] + self.assertTrue(diskp[0]["system"].get("model", "") == "scsi") + self.assertTrue(diskp[0]["system"].get("format", "") == "vmdk") + self.assertTrue(diskp[0]["system"].get("size", "") == "8192") diff --git a/tests/integration/modules/test_virtualenv_mod.py b/tests/integration/modules/test_virtualenv_mod.py index 52bd3fc3f8b..8587f8c498b 100644 --- a/tests/integration/modules/test_virtualenv_mod.py +++ b/tests/integration/modules/test_virtualenv_mod.py @@ -2,57 +2,59 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os import tempfile -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf - # Import salt libs import salt.utils.path from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.case import ModuleCase + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf -@skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') +@skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, "virtualenv not installed" +) class VirtualenvModuleTest(ModuleCase): - ''' + """ Validate the virtualenv module - ''' + """ + def setUp(self): super(VirtualenvModuleTest, self).setUp() self.venv_test_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - self.venv_dir = os.path.join(self.venv_test_dir, 'venv') + self.venv_dir = os.path.join(self.venv_test_dir, "venv") def test_create_defaults(self): - ''' + """ virtualenv.managed - ''' - self.run_function('virtualenv.create', [self.venv_dir]) - pip_file = os.path.join(self.venv_dir, 'bin', 'pip') + """ + self.run_function("virtualenv.create", [self.venv_dir]) + pip_file = os.path.join(self.venv_dir, "bin", "pip") self.assertTrue(os.path.exists(pip_file)) def test_site_packages(self): - pip_bin = os.path.join(self.venv_dir, 'bin', 'pip') + pip_bin = os.path.join(self.venv_dir, "bin", "pip") self.run_function( - 'virtualenv.create', [self.venv_dir], system_site_packages=True + "virtualenv.create", [self.venv_dir], system_site_packages=True ) - with_site = self.run_function('pip.freeze', bin_env=pip_bin) - self.run_function('file.remove', [self.venv_dir]) - self.run_function('virtualenv.create', [self.venv_dir]) - without_site = self.run_function('pip.freeze', bin_env=pip_bin) + with_site = self.run_function("pip.freeze", bin_env=pip_bin) + self.run_function("file.remove", [self.venv_dir]) + self.run_function("virtualenv.create", [self.venv_dir]) + without_site = self.run_function("pip.freeze", bin_env=pip_bin) self.assertFalse(with_site == without_site) def test_clear(self): - pip_bin = os.path.join(self.venv_dir, 'bin', 'pip') - self.run_function('virtualenv.create', [self.venv_dir]) - self.run_function('pip.install', [], pkgs='pep8', bin_env=pip_bin) - self.run_function('virtualenv.create', [self.venv_dir], clear=True) - packages = self.run_function( - 'pip.list', prefix='pep8', bin_env=pip_bin - ) - self.assertFalse('pep8' in packages) + pip_bin = os.path.join(self.venv_dir, "bin", "pip") + self.run_function("virtualenv.create", [self.venv_dir]) + self.run_function("pip.install", [], pkgs="pep8", bin_env=pip_bin) + self.run_function("virtualenv.create", [self.venv_dir], clear=True) + packages = self.run_function("pip.list", prefix="pep8", bin_env=pip_bin) + self.assertFalse("pep8" in packages) def tearDown(self): - self.run_function('file.remove', [self.venv_test_dir]) + self.run_function("file.remove", [self.venv_test_dir]) diff --git a/tests/integration/modules/test_win_autoruns.py b/tests/integration/modules/test_win_autoruns.py index edac1b52350..2b245616a31 100644 --- a/tests/integration/modules/test_win_autoruns.py +++ b/tests/integration/modules/test_win_autoruns.py @@ -1,25 +1,24 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import -# Import Salt Testing libs +import pytest +import salt.utils.platform from tests.support.case import ModuleCase from tests.support.unit import skipIf -# Import Salt libs -import salt.utils.platform - -@skipIf(not salt.utils.platform.is_windows(), 'windows tests only') +@skipIf(not salt.utils.platform.is_windows(), "windows tests only") +@pytest.mark.windows_whitelisted class AutoRunsModuleTest(ModuleCase): - ''' + """ Test the autoruns module - ''' + """ + def test_win_autoruns_list(self): - ''' + """ test win_autoruns.list module - ''' - ret = self.run_function('autoruns.list') - self.assertIn('HKLM', str(ret)) + """ + ret = self.run_function("autoruns.list") + self.assertIn("HKLM", str(ret)) self.assertTrue(isinstance(ret, dict)) diff --git a/tests/integration/modules/test_win_dns_client.py b/tests/integration/modules/test_win_dns_client.py index 1893b5ec5d8..4d3da17985d 100644 --- a/tests/integration/modules/test_win_dns_client.py +++ b/tests/integration/modules/test_win_dns_client.py @@ -1,41 +1,44 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest - -# Import Salt libs +import pytest import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.unit import skipIf -@skipIf(not salt.utils.platform.is_windows(), 'windows test only') +@skipIf(not salt.utils.platform.is_windows(), "windows test only") +@pytest.mark.windows_whitelisted class WinDNSTest(ModuleCase): - ''' + """ Test for salt.modules.win_dns_client - ''' + """ + @destructiveTest def test_add_remove_dns(self): - ''' + """ Test add and removing a dns server - ''' + """ # Get a list of interfaces on the system - interfaces = self.run_function('network.interfaces_names') - skipIf(interfaces.count == 0, 'This test requires a network interface') + interfaces = self.run_function("network.interfaces_names") + skipIf(interfaces.count == 0, "This test requires a network interface") interface = interfaces[0] - dns = '8.8.8.8' + dns = "8.8.8.8" # add dns server - self.assertTrue(self.run_function('win_dns_client.add_dns', [dns, interface], index=42)) + self.assertTrue( + self.run_function("win_dns_client.add_dns", [dns, interface], index=42) + ) - srvs = self.run_function('win_dns_client.get_dns_servers', interface=interface) + srvs = self.run_function("win_dns_client.get_dns_servers", interface=interface) self.assertIn(dns, srvs) # remove dns server - self.assertTrue(self.run_function('win_dns_client.rm_dns', [dns], interface=interface)) + self.assertTrue( + self.run_function("win_dns_client.rm_dns", [dns], interface=interface) + ) - srvs = self.run_function('win_dns_client.get_dns_servers', interface=interface) + srvs = self.run_function("win_dns_client.get_dns_servers", interface=interface) self.assertNotIn(dns, srvs) diff --git a/tests/integration/modules/test_win_dsc.py b/tests/integration/modules/test_win_dsc.py new file mode 100644 index 00000000000..8430636e527 --- /dev/null +++ b/tests/integration/modules/test_win_dsc.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Import Python libs +from __future__ import absolute_import + +import os + +# Import Salt Libs +import salt.utils.files +import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + + +@skipIf(not salt.utils.platform.is_windows(), "Tests for only Windows") +class DscModuleTest(ModuleCase): + """ + Validate PowerShell DSC module + """ + + def setUp(self): + self.ps1file = os.path.join(RUNTIME_VARS.TMP, "HelloWorld.ps1") + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.FILES, "file", "base", "HelloWorld.ps1"), "rb" + ) as sfp: + with salt.utils.files.fopen(self.ps1file, "wb") as dfp: + dfp.write(sfp.read()) + self.psd1file = os.path.join(RUNTIME_VARS.TMP, "HelloWorld.psd1") + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.FILES, "file", "base", "HelloWorld.psd1"), "rb" + ) as sfp: + with salt.utils.files.fopen(self.psd1file, "wb") as dfp: + dfp.write(sfp.read()) + super(DscModuleTest, self).setUp() + + def tearDown(self): + if os.path.isfile(self.ps1file): + os.remove(self.ps1file) + if os.path.isfile(self.psd1file): + os.remove(self.psd1file) + super(DscModuleTest, self).tearDown() + + @destructiveTest + def test_compile_config(self): + ret = self.run_function( + "dsc.compile_config", + self.ps1file, + config_name="HelloWorld", + config_data=self.psd1file, + ) + self.assertTrue(ret["Exists"]) diff --git a/tests/integration/modules/test_win_firewall.py b/tests/integration/modules/test_win_firewall.py index 71e78c052f9..033ace1f274 100644 --- a/tests/integration/modules/test_win_firewall.py +++ b/tests/integration/modules/test_win_firewall.py @@ -1,110 +1,109 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest - -# Import Salt Libs +import pytest import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.unit import skipIf -@skipIf(not salt.utils.platform.is_windows(), 'Tests for only Windows') +@skipIf(not salt.utils.platform.is_windows(), "Tests for only Windows") +@pytest.mark.windows_whitelisted class FirewallTest(ModuleCase): - ''' + """ Validate windows firewall module - ''' + """ + def _pre_firewall_status(self, pre_run): - post_run = self.run_function('firewall.get_config') - network = ['Domain', 'Public', 'Private'] + post_run = self.run_function("firewall.get_config") + network = ["Domain", "Public", "Private"] # compare the status of the firewall before and after test # and re-enable or disable depending on status before test run for net in network: if post_run[net] != pre_run[net]: if pre_run[net]: - self.assertTrue(self.run_function('firewall.enable', profile=net)) + self.assertTrue(self.run_function("firewall.enable", profile=net)) else: - self.assertTrue(self.run_function('firewall.disable', profile=net)) + self.assertTrue(self.run_function("firewall.disable", profile=net)) @destructiveTest def test_firewall_get_config(self): - ''' + """ test firewall.get_config - ''' - pre_run = self.run_function('firewall.get_config') + """ + pre_run = self.run_function("firewall.get_config") # ensure all networks are enabled then test status - self.assertTrue(self.run_function('firewall.enable', profile='allprofiles')) - ret = self.run_function('firewall.get_config') - network = ['Domain', 'Public', 'Private'] + self.assertTrue(self.run_function("firewall.enable", profile="allprofiles")) + ret = self.run_function("firewall.get_config") + network = ["Domain", "Public", "Private"] for net in network: self.assertTrue(ret[net]) self._pre_firewall_status(pre_run) @destructiveTest def test_firewall_disable(self): - ''' + """ test firewall.disable - ''' - pre_run = self.run_function('firewall.get_config') - network = 'Private' + """ + pre_run = self.run_function("firewall.get_config") + network = "Private" - ret = self.run_function('firewall.get_config')[network] + ret = self.run_function("firewall.get_config")[network] if not ret: - self.assertTrue(self.run_function('firewall.enable', profile=network)) + self.assertTrue(self.run_function("firewall.enable", profile=network)) - self.assertTrue(self.run_function('firewall.disable', profile=network)) - ret = self.run_function('firewall.get_config')[network] + self.assertTrue(self.run_function("firewall.disable", profile=network)) + ret = self.run_function("firewall.get_config")[network] self.assertFalse(ret) self._pre_firewall_status(pre_run) @destructiveTest def test_firewall_enable(self): - ''' + """ test firewall.enable - ''' - pre_run = self.run_function('firewall.get_config') - network = 'Private' + """ + pre_run = self.run_function("firewall.get_config") + network = "Private" - ret = self.run_function('firewall.get_config')[network] + ret = self.run_function("firewall.get_config")[network] if ret: - self.assertTrue(self.run_function('firewall.disable', profile=network)) + self.assertTrue(self.run_function("firewall.disable", profile=network)) - self.assertTrue(self.run_function('firewall.enable', profile=network)) - ret = self.run_function('firewall.get_config')[network] + self.assertTrue(self.run_function("firewall.enable", profile=network)) + ret = self.run_function("firewall.get_config")[network] self.assertTrue(ret) self._pre_firewall_status(pre_run) def test_firewall_get_rule(self): - ''' + """ test firewall.get_rule - ''' - rule = 'Remote Event Log Management (NP-In)' + """ + rule = "Remote Event Log Management (NP-In)" - ret = self.run_function('firewall.get_rule', [rule]) - checks = ['Private', 'LocalPort', 'RemotePort'] + ret = self.run_function("firewall.get_rule", [rule]) + checks = ["Private", "LocalPort", "RemotePort"] for check in checks: self.assertIn(check, ret[rule]) @destructiveTest def test_firewall_add_delete_rule(self): - ''' + """ test firewall.add_rule and delete_rule - ''' - rule = 'test rule' - port = '8080' + """ + rule = "test rule" + port = "8080" # test adding firewall rule - add_rule = self.run_function('firewall.add_rule', [rule, port]) - ret = self.run_function('firewall.get_rule', [rule]) + add_rule = self.run_function("firewall.add_rule", [rule, port]) + ret = self.run_function("firewall.get_rule", [rule]) self.assertIn(rule, ret[rule]) self.assertIn(port, ret[rule]) # test deleting firewall rule - self.assertTrue(self.run_function('firewall.delete_rule', [rule, port])) - ret = self.run_function('firewall.get_rule', [rule]) + self.assertTrue(self.run_function("firewall.delete_rule", [rule, port])) + ret = self.run_function("firewall.get_rule", [rule]) self.assertNotIn(rule, ret) self.assertNotIn(port, ret) - self.assertIn('No rules match the specified criteria.', ret) + self.assertIn("No rules match the specified criteria.", ret) diff --git a/tests/integration/modules/test_win_ip.py b/tests/integration/modules/test_win_ip.py index a9969172a0a..251b1bcc222 100644 --- a/tests/integration/modules/test_win_ip.py +++ b/tests/integration/modules/test_win_ip.py @@ -2,32 +2,34 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import re +# Import Salt libs +import salt.utils.platform + # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.unit import skipIf -# Import Salt libs -import salt.utils.platform - -@skipIf(not salt.utils.platform.is_windows(), 'windows test only') +@skipIf(not salt.utils.platform.is_windows(), "windows test only") class WinIPTest(ModuleCase): - ''' + """ Tests for salt.modules.win_ip - ''' + """ + def test_get_default_gateway(self): - ''' + """ Test getting default gateway - ''' - ip = re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$') - ret = self.run_function('ip.get_default_gateway') + """ + ip = re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") + ret = self.run_function("ip.get_default_gateway") assert ip.match(ret) def test_ip_is_enabled(self): - ''' + """ Test ip.is_enabled - ''' - assert self.run_function('ip.is_enabled', ['Ethernet']) - assert 'not found' in self.run_function('ip.is_enabled', ['doesnotexist']) + """ + assert self.run_function("ip.is_enabled", ["Ethernet"]) + assert "not found" in self.run_function("ip.is_enabled", ["doesnotexist"]) diff --git a/tests/integration/modules/test_win_lgpo.py b/tests/integration/modules/test_win_lgpo.py index d4b0d7f30db..162593463ac 100644 --- a/tests/integration/modules/test_win_lgpo.py +++ b/tests/integration/modules/test_win_lgpo.py @@ -2,40 +2,44 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import re + import io import logging - -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, generate_random_name -from tests.support.runtests import RUNTIME_VARS +import os +import re # Import Salt libs import salt.utils.files import salt.utils.platform import salt.utils.win_reg as reg +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, generate_random_name +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + log = logging.getLogger(__name__) -@skipIf(not salt.utils.platform.is_windows(), 'windows test only') +@skipIf(not salt.utils.platform.is_windows(), "windows test only") class WinLgpoTest(ModuleCase): - ''' + """ Tests for salt.modules.win_lgpo - ''' + """ + osrelease = None - def _testRegistryPolicy(self, - policy_name, - policy_config, - registry_value_hive, - registry_value_path, - registry_value_vname, - expected_value_data): - ''' + def _testRegistryPolicy( + self, + policy_name, + policy_config, + registry_value_hive, + registry_value_path, + registry_value_vname, + expected_value_data, + ): + """ Takes a registry based policy name and config and validates that the expected registry value exists and has the correct data @@ -51,28 +55,37 @@ class WinLgpoTest(ModuleCase): the registry value name expected_value_data the expected data that the value will contain - ''' - ret = self.run_function('lgpo.set_computer_policy', - (policy_name, policy_config)) + """ + ret = self.run_function( + "lgpo.set_computer_policy", (policy_name, policy_config) + ) self.assertTrue(ret) val = reg.read_value( hive=registry_value_hive, key=registry_value_path, - vname=registry_value_vname) - self.assertTrue(val['success'], msg='Failed to obtain the registry data for policy {0}'.format(policy_name)) - if val['success']: + vname=registry_value_vname, + ) + self.assertTrue( + val["success"], + msg="Failed to obtain the registry data for policy {0}".format(policy_name), + ) + if val["success"]: self.assertEqual( - val['vdata'], + val["vdata"], expected_value_data, - 'The registry value data {0} does not match the expected value {1} for policy {2}'.format( - val['vdata'], expected_value_data, policy_name)) + "The registry value data {0} does not match the expected value {1} for policy {2}".format( + val["vdata"], expected_value_data, policy_name + ), + ) - def _testSeceditPolicy(self, - policy_name, - policy_config, - expected_regexes, - cumulative_rights_assignments=True): - ''' + def _testSeceditPolicy( + self, + policy_name, + policy_config, + expected_regexes, + cumulative_rights_assignments=True, + ): + """ Takes a secedit policy name and config and validates that the expected output is returned from secedit @@ -82,34 +95,43 @@ class WinLgpoTest(ModuleCase): the configuration of the policy expected_regexes the expected regexes to be found in the secedit output file - ''' - ret = self.run_function('lgpo.set_computer_policy', - (policy_name, policy_config), - cumulative_rights_assignments=cumulative_rights_assignments) + """ + ret = self.run_function( + "lgpo.set_computer_policy", + (policy_name, policy_config), + cumulative_rights_assignments=cumulative_rights_assignments, + ) self.assertTrue(ret) - secedit_output_file = os.path.join(RUNTIME_VARS.TMP, generate_random_name('secedit-output-')) + secedit_output_file = os.path.join( + RUNTIME_VARS.TMP, generate_random_name("secedit-output-") + ) secedit_output = self.run_function( - 'cmd.run', - (), - cmd='secedit /export /cfg {0}'.format(secedit_output_file)) + "cmd.run", (), cmd="secedit /export /cfg {0}".format(secedit_output_file) + ) secedit_file_content = None if secedit_output: - with io.open(secedit_output_file, encoding='utf-16') as _reader: + with io.open(secedit_output_file, encoding="utf-16") as _reader: secedit_file_content = _reader.read() for expected_regex in expected_regexes: match = re.search( - expected_regex, - secedit_file_content, - re.IGNORECASE | re.MULTILINE) - self.assertIsNotNone(match, 'Failed validating policy "{0}" configuration, regex "{1}" not found in secedit output'.format(policy_name, expected_regex)) + expected_regex, secedit_file_content, re.IGNORECASE | re.MULTILINE + ) + self.assertIsNotNone( + match, + 'Failed validating policy "{0}" configuration, regex "{1}" not found in secedit output'.format( + policy_name, expected_regex + ), + ) - def _testAdmxPolicy(self, - policy_name, - policy_config, - expected_regexes, - assert_true=True, - policy_class='Machine'): - ''' + def _testAdmxPolicy( + self, + policy_name, + policy_config, + expected_regexes, + assert_true=True, + policy_class="Machine", + ): + """ Takes a ADMX policy name and config and validates that the expected output is returned from lgpo looking at the Registry.pol file @@ -123,49 +145,54 @@ class WinLgpoTest(ModuleCase): set to false if expecting the module run to fail policy_class the policy class this policy belongs to, either Machine or User - ''' - lgpo_function = 'set_computer_policy' - lgpo_class = '/m' - lgpo_folder = 'Machine' - if policy_class.lower() == 'user': - lgpo_function = 'set_user_policy' - lgpo_class = '/u' - lgpo_folder = 'User' + """ + lgpo_function = "set_computer_policy" + lgpo_class = "/m" + lgpo_folder = "Machine" + if policy_class.lower() == "user": + lgpo_function = "set_user_policy" + lgpo_class = "/u" + lgpo_folder = "User" - ret = self.run_function('lgpo.{0}'.format(lgpo_function), - (policy_name, policy_config)) - log.debug('lgpo set_computer_policy ret == %s', ret) - cmd = ['lgpo.exe', - '/parse', - lgpo_class, - r'c:\Windows\System32\GroupPolicy\{}\Registry.pol'.format(lgpo_folder)] + ret = self.run_function( + "lgpo.{0}".format(lgpo_function), (policy_name, policy_config) + ) + log.debug("lgpo set_computer_policy ret == %s", ret) + cmd = [ + "lgpo.exe", + "/parse", + lgpo_class, + r"c:\Windows\System32\GroupPolicy\{}\Registry.pol".format(lgpo_folder), + ] if assert_true: self.assertTrue(ret) - lgpo_output = self.run_function('cmd.run', (), cmd=' '.join(cmd)) + lgpo_output = self.run_function("cmd.run", (), cmd=" ".join(cmd)) # validate that the lgpo output doesn't say the format is invalid self.assertIsNone( - re.search(r'Invalid file format\.', lgpo_output, re.IGNORECASE), - msg='Failed validating Registry.pol file format') + re.search(r"Invalid file format\.", lgpo_output, re.IGNORECASE), + msg="Failed validating Registry.pol file format", + ) # validate that the regexes we expect are in the output for expected_regex in expected_regexes: match = re.search(expected_regex, lgpo_output, re.IGNORECASE) self.assertIsNotNone( match, msg='Failed validating policy "{0}" configuration, regex ' - '"{1}" not found in lgpo output:\n{2}' - ''.format(policy_name, expected_regex, lgpo_output)) + '"{1}" not found in lgpo output:\n{2}' + "".format(policy_name, expected_regex, lgpo_output), + ) else: # expecting it to fail self.assertNotEqual(ret, True) def runTest(self): - ''' + """ runTest method - ''' + """ @classmethod def setUpClass(cls): - ''' + """ class setup function, only runs once downloads and extracts the lgpo.exe tool into c:/windows/system32 @@ -173,538 +200,591 @@ class WinLgpoTest(ModuleCase): gets osrelease grain for tests that are only applicable to certain windows versions - ''' - osrelease_grains = cls().run_function('grains.item', ['osrelease']) - if 'osrelease' in osrelease_grains: - cls.osrelease = osrelease_grains['osrelease'] + """ + osrelease_grains = cls().run_function("grains.item", ["osrelease"]) + if "osrelease" in osrelease_grains: + cls.osrelease = osrelease_grains["osrelease"] else: - log.debug('Unable to get osrelease grain') - if not os.path.exists(r'c:\windows\system32\lgpo.exe'): - log.debug('lgpo.exe does not exist, attempting to download/extract') + log.debug("Unable to get osrelease grain") + if not os.path.exists(r"c:\windows\system32\lgpo.exe"): + log.debug("lgpo.exe does not exist, attempting to download/extract") ret = cls().run_function( - 'state.single', - ('archive.extracted', r'c:\windows\system32'), - source='https://download.microsoft.com/download/8/5/C/85C25433-A1B0-4FFA-9429-7E023E7DA8D8/LGPO.zip', - archive_format='zip', - source_hash='sha256=6ffb6416366652993c992280e29faea3507b5b5aa661c33ba1af31f48acea9c4', - enforce_toplevel=False) - log.debug('ret from archive.unzip == %s', ret) + "state.single", + ("archive.extracted", r"c:\windows\system32"), + source="https://download.microsoft.com/download/8/5/C/85C25433-A1B0-4FFA-9429-7E023E7DA8D8/LGPO.zip", + archive_format="zip", + source_hash="sha256=6ffb6416366652993c992280e29faea3507b5b5aa661c33ba1af31f48acea9c4", + enforce_toplevel=False, + ) + log.debug("ret from archive.unzip == %s", ret) @destructiveTest def test_set_user_policy_point_and_print_restrictions(self): - ''' + """ Test setting/unsetting/changing the PointAndPrint_Restrictions user policy - ''' + """ # Disable Point and Print Restrictions self._testAdmxPolicy( - r'Control Panel\Printers\Point and Print Restrictions', - 'Disabled', + r"Control Panel\Printers\Point and Print Restrictions", + "Disabled", [ - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*Restricted[\s]*DWORD:0', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*TrustedServers[\s]*DELETE', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*ServerList[\s]*DELETE', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*InForest[\s]*DELETE', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*NoWarningNoElevationOnInstall[\s]*DELETE', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*UpdatePromptSettings[\s]*DELETE', + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*Restricted[\s]*DWORD:0", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*TrustedServers[\s]*DELETE", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*ServerList[\s]*DELETE", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*InForest[\s]*DELETE", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*NoWarningNoElevationOnInstall[\s]*DELETE", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*UpdatePromptSettings[\s]*DELETE", ], - policy_class='User') + policy_class="User", + ) # Enable Point and Print Restrictions self._testAdmxPolicy( - r'Point and Print Restrictions', + r"Point and Print Restrictions", { - 'Users can only point and print to these servers': True, - 'Enter fully qualified server names separated by semicolons': 'fakeserver1;fakeserver2', - 'Users can only point and print to machines in their forest': True, - 'When installing drivers for a new connection': 'Show warning and elevation prompt', - 'When updating drivers for an existing connection': 'Show warning only', + "Users can only point and print to these servers": True, + "Enter fully qualified server names separated by semicolons": "fakeserver1;fakeserver2", + "Users can only point and print to machines in their forest": True, + "When installing drivers for a new connection": "Show warning and elevation prompt", + "When updating drivers for an existing connection": "Show warning only", }, [ - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*Restricted[\s]*DWORD:1', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*TrustedServers[\s]*DWORD:1', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*ServerList[\s]*SZ:fakeserver1;fakeserver2', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*InForest[\s]*DWORD:1', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*NoWarningNoElevationOnInstall[\s]*DWORD:0', - r'User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*UpdatePromptSettings[\s]*DWORD:1', + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*Restricted[\s]*DWORD:1", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*TrustedServers[\s]*DWORD:1", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*ServerList[\s]*SZ:fakeserver1;fakeserver2", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*InForest[\s]*DWORD:1", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*NoWarningNoElevationOnInstall[\s]*DWORD:0", + r"User[\s]*Software\\Policies\\Microsoft\\Windows NT\\Printers\\PointAndPrint[\s]*UpdatePromptSettings[\s]*DWORD:1", ], - policy_class='User') + policy_class="User", + ) # set Point and Print Restrictions to 'Not Configured' self._testAdmxPolicy( - r'Control Panel\Printers\Point and Print Restrictions', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\user\\registry.pol[\s]*; PARSING COMPLETED.'], - policy_class='User') + r"Control Panel\Printers\Point and Print Restrictions", + "Not Configured", + [ + r"; Source file: c:\\windows\\system32\\grouppolicy\\user\\registry.pol[\s]*; PARSING COMPLETED." + ], + policy_class="User", + ) @destructiveTest def test_set_computer_policy_NTP_Client(self): - ''' + """ Test setting/unsetting/changing NTP Client policies - ''' + """ # Disable Configure NTP Client self._testAdmxPolicy( - r'System\Windows Time Service\Time Providers\Configure Windows NTP Client', - 'Disabled', + r"System\Windows Time Service\Time Providers\Configure Windows NTP Client", + "Disabled", [ - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*NtpServer[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*Type[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*CrossSiteSyncFlags[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMinutes[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMaxTimes[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*SpecialPollInterval[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*EventLogFlags[\s]*DELETE' - ]) + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*NtpServer[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*Type[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*CrossSiteSyncFlags[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMinutes[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMaxTimes[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*SpecialPollInterval[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*EventLogFlags[\s]*DELETE", + ], + ) # Enable Configure NTP Client self._testAdmxPolicy( - r'System\Windows Time Service\Time Providers\Configure Windows NTP Client', + r"System\Windows Time Service\Time Providers\Configure Windows NTP Client", { - 'NtpServer': 'time.windows.com,0x9', - 'Type': 'NT5DS', - 'CrossSiteSyncFlags': 2, - 'ResolvePeerBackoffMinutes': 15, - 'ResolvePeerBackoffMaxTimes': 7, - 'W32TIME_SpecialPollInterval': 3600, - 'W32TIME_NtpClientEventLogFlags': 0 + "NtpServer": "time.windows.com,0x9", + "Type": "NT5DS", + "CrossSiteSyncFlags": 2, + "ResolvePeerBackoffMinutes": 15, + "ResolvePeerBackoffMaxTimes": 7, + "W32TIME_SpecialPollInterval": 3600, + "W32TIME_NtpClientEventLogFlags": 0, }, [ - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*NtpServer[\s]*SZ:time.windows.com,0x9', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*Type[\s]*SZ:NT5DS', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*CrossSiteSyncFlags[\s]*DWORD:2', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMinutes[\s]*DWORD:15', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMaxTimes[\s]*DWORD:7', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*SpecialPollInterval[\s]*DWORD:3600', - r'Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*EventLogFlags[\s]*DWORD:0', - ]) + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*NtpServer[\s]*SZ:time.windows.com,0x9", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\Parameters[\s]*Type[\s]*SZ:NT5DS", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*CrossSiteSyncFlags[\s]*DWORD:2", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMinutes[\s]*DWORD:15", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*ResolvePeerBackoffMaxTimes[\s]*DWORD:7", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*SpecialPollInterval[\s]*DWORD:3600", + r"Computer[\s]*Software\\Policies\\Microsoft\\W32time\\TimeProviders\\NtpClient[\s]*EventLogFlags[\s]*DWORD:0", + ], + ) # set Configure NTP Client to 'Not Configured' self._testAdmxPolicy( - r'System\Windows Time Service\Time Providers\Configure Windows NTP Client', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + r"System\Windows Time Service\Time Providers\Configure Windows NTP Client", + "Not Configured", + [ + r"; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED." + ], + ) @destructiveTest def test_set_computer_policy_RA_Unsolicit(self): - ''' + """ Test setting/unsetting/changing RA_Unsolicit policy - ''' + """ # Disable RA_Unsolicit - log.debug('Attempting to disable RA_Unsolicit') + log.debug("Attempting to disable RA_Unsolicit") self._testAdmxPolicy( - 'RA_Unsolicit', - 'Disabled', + "RA_Unsolicit", + "Disabled", [ - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:0', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DELETE', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*\*[\s]*DELETEALLVALUES', - ]) + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:0", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DELETE", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*\*[\s]*DELETEALLVALUES", + ], + ) # configure RA_Unsolicit - log.debug('Attempting to configure RA_Unsolicit') + log.debug("Attempting to configure RA_Unsolicit") self._testAdmxPolicy( - 'RA_Unsolicit', + "RA_Unsolicit", { - 'Configure Offer Remote Access': 'Enabled', - 'Permit remote control of this computer': 'Allow helpers to remotely control the computer', - 'Helpers': ['administrators', 'user1'] + "Configure Offer Remote Access": "Enabled", + "Permit remote control of this computer": "Allow helpers to remotely control the computer", + "Helpers": ["administrators", "user1"], }, [ - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1', - ]) + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1", + ], + ) # Not Configure RA_Unsolicit - log.debug('Attempting to set RA_Unsolicit to Not Configured') + log.debug("Attempting to set RA_Unsolicit to Not Configured") self._testAdmxPolicy( - 'RA_Unsolicit', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + "RA_Unsolicit", + "Not Configured", + [ + r"; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED." + ], + ) @destructiveTest def test_set_computer_policy_Pol_HardenedPaths(self): # Disable Pol_HardenedPaths - log.debug('Attempting to disable Pol_HardenedPaths') + log.debug("Attempting to disable Pol_HardenedPaths") self._testAdmxPolicy( - 'Pol_HardenedPaths', - 'Disabled', - [r'Computer[\s]*Software\\policies\\Microsoft\\Windows\\NetworkProvider\\HardenedPaths[\s]*\*[\s]*DELETEALLVALUES']) + "Pol_HardenedPaths", + "Disabled", + [ + r"Computer[\s]*Software\\policies\\Microsoft\\Windows\\NetworkProvider\\HardenedPaths[\s]*\*[\s]*DELETEALLVALUES" + ], + ) # Configure Pol_HardenedPaths - log.debug('Attempting to configure Pol_HardenedPaths') + log.debug("Attempting to configure Pol_HardenedPaths") self._testAdmxPolicy( - 'Pol_HardenedPaths', + "Pol_HardenedPaths", { - 'Hardened UNC Paths': { - r'\\*\NETLOGON': 'RequireMutualAuthentication=1, RequireIntegrity=1', - r'\\*\SYSVOL': 'RequireMutualAuthentication=1, RequireIntegrity=1' + "Hardened UNC Paths": { + r"\\*\NETLOGON": "RequireMutualAuthentication=1, RequireIntegrity=1", + r"\\*\SYSVOL": "RequireMutualAuthentication=1, RequireIntegrity=1", } }, [ - r'Computer[\s]*Software\\policies\\Microsoft\\Windows\\NetworkProvider\\HardenedPaths[\s]*\\\\\*\\NETLOGON[\s]*SZ:RequireMutualAuthentication=1, RequireIntegrity=1[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows\\NetworkProvider\\HardenedPaths[\s]*\\\\\*\\SYSVOL[\s]*SZ:RequireMutualAuthentication=1, RequireIntegrity=1[\s]*', - ]) + r"Computer[\s]*Software\\policies\\Microsoft\\Windows\\NetworkProvider\\HardenedPaths[\s]*\\\\\*\\NETLOGON[\s]*SZ:RequireMutualAuthentication=1, RequireIntegrity=1[\s]*", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows\\NetworkProvider\\HardenedPaths[\s]*\\\\\*\\SYSVOL[\s]*SZ:RequireMutualAuthentication=1, RequireIntegrity=1[\s]*", + ], + ) # Not Configure Pol_HardenedPaths - log.debug('Attempting to set Pol_HardenedPaths to Not Configured') + log.debug("Attempting to set Pol_HardenedPaths to Not Configured") self._testAdmxPolicy( - 'Pol_HardenedPaths', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + "Pol_HardenedPaths", + "Not Configured", + [ + r"; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED." + ], + ) @destructiveTest def test_set_computer_policy_WindowsUpdate(self): - ''' + """ Test setting/unsetting/changing WindowsUpdate policy - ''' + """ # Configure Automatic Updates has different options in different builds # and releases of Windows, so we'll get the elements and add them if # they are present. Newer elements will need to be added manually as # they are released by Microsoft result = self.run_function( - 'lgpo.get_policy_info', - ['Configure Automatic Updates'], - policy_class='machine' + "lgpo.get_policy_info", + ["Configure Automatic Updates"], + policy_class="machine", ) the_policy = {} the_policy_check_enabled = [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:0', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:0", ] the_policy_check_disabled = [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:1', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:1", ] - for item in result['policy_elements']: - if 'Configure automatic updating' in item['element_aliases']: - the_policy.update({ - 'Configure automatic updating': '4 - Auto download and schedule the install', - }) + for item in result["policy_elements"]: + if "Configure automatic updating" in item["element_aliases"]: + the_policy.update( + { + "Configure automatic updating": "4 - Auto download and schedule the install", + } + ) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DWORD:4', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DWORD:4", ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DELETE', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DELETE", ) - elif 'Install during automatic maintenance' in item['element_aliases']: - the_policy.update({ - 'Install during automatic maintenance': True, - }) + elif "Install during automatic maintenance" in item["element_aliases"]: + the_policy.update({"Install during automatic maintenance": True}) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DWORD:1\s*', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DWORD:1\s*", ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE", ) - elif 'Scheduled install day' in item['element_aliases']: - the_policy.update({ - 'Scheduled install day': '7 - Every Saturday', - }) + elif "Scheduled install day" in item["element_aliases"]: + the_policy.update({"Scheduled install day": "7 - Every Saturday"}) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DWORD:7', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DWORD:7", ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DELETE', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DELETE", ) - elif 'Scheduled install time' in item['element_aliases']: - the_policy.update({ - 'Scheduled install time': '17:00', - }) + elif "Scheduled install time" in item["element_aliases"]: + the_policy.update({"Scheduled install time": "17:00"}) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DWORD:17', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DWORD:17", ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DELETE', + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DELETE", + ) + elif ( + "Install updates for other Microsoft products" + in item["element_aliases"] + ): + the_policy.update( + {"Install updates for other Microsoft products": True} ) - elif 'Install updates for other Microsoft products' in item['element_aliases']: - the_policy.update({ - 'Install updates for other Microsoft products': True - }) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DWORD:1\s*' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DWORD:1\s*" ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DELETE' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DELETE" ) - elif 'AutoUpdateSchEveryWeek' in item['element_aliases']: - the_policy.update({ - 'AutoUpdateSchEveryWeek': True - }) + elif "AutoUpdateSchEveryWeek" in item["element_aliases"]: + the_policy.update({"AutoUpdateSchEveryWeek": True}) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallEveryWeek[\s]*DWORD:1\s*' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallEveryWeek[\s]*DWORD:1\s*" ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallEveryWeek[\s]*DELETE' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallEveryWeek[\s]*DELETE" ) - elif 'First week of the month' in item['element_aliases']: - the_policy.update({ - 'First week of the month': True - }) + elif "First week of the month" in item["element_aliases"]: + the_policy.update({"First week of the month": True}) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFirstWeek[\s]*DWORD:1\s*' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFirstWeek[\s]*DWORD:1\s*" ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFirstWeek[\s]*DELETE' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFirstWeek[\s]*DELETE" ) - elif 'Second week of the month' in item['element_aliases']: - the_policy.update({ - 'Second week of the month': True - }) + elif "Second week of the month" in item["element_aliases"]: + the_policy.update({"Second week of the month": True}) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallSecondWeek[\s]*DWORD:1\s*' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallSecondWeek[\s]*DWORD:1\s*" ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallSecondWeek[\s]*DELETE' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallSecondWeek[\s]*DELETE" ) - elif 'Third week of the month' in item['element_aliases']: - the_policy.update({ - 'Third week of the month': True - }) + elif "Third week of the month" in item["element_aliases"]: + the_policy.update({"Third week of the month": True}) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallThirdWeek[\s]*DWORD:1\s*' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallThirdWeek[\s]*DWORD:1\s*" ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallThirdWeek[\s]*DELETE' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallThirdWeek[\s]*DELETE" ) - elif 'Fourth week of the month' in item['element_aliases']: - the_policy.update({ - 'Fourth week of the month': True - }) + elif "Fourth week of the month" in item["element_aliases"]: + the_policy.update({"Fourth week of the month": True}) the_policy_check_enabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFourthWeek[\s]*DWORD:1\s*' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFourthWeek[\s]*DWORD:1\s*" ) the_policy_check_disabled.append( - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFourthWeek[\s]*DELETE' + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallFourthWeek[\s]*DELETE" ) # enable Automatic Updates self._testAdmxPolicy( - r'Windows Components\Windows Update\Configure Automatic Updates', + r"Windows Components\Windows Update\Configure Automatic Updates", the_policy, - the_policy_check_enabled) + the_policy_check_enabled, + ) # disable Configure Automatic Updates self._testAdmxPolicy( - r'Windows Components\Windows Update\Configure Automatic Updates', - 'Disabled', - the_policy_check_disabled) + r"Windows Components\Windows Update\Configure Automatic Updates", + "Disabled", + the_policy_check_disabled, + ) # set Configure Automatic Updates to 'Not Configured' self._testAdmxPolicy( - r'Windows Components\Windows Update\Configure Automatic Updates', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + r"Windows Components\Windows Update\Configure Automatic Updates", + "Not Configured", + [ + r"; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED." + ], + ) @destructiveTest def test_set_computer_policy_ClipboardRedirection(self): - ''' + """ Test setting/unsetting/changing ClipboardRedirection policy - ''' + """ # Enable/Disable/Not Configured "Do not allow Clipboard redirection" self._testAdmxPolicy( - r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', - 'Enabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:1']) + r"Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection", + "Enabled", + [ + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:1" + ], + ) self._testAdmxPolicy( - r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', - 'Disabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0']) + r"Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection", + "Disabled", + [ + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0" + ], + ) self._testAdmxPolicy( - r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + r"Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection", + "Not Configured", + [ + r"; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED." + ], + ) @destructiveTest def test_set_computer_policy_GuestAccountStatus(self): - ''' + """ Test setting/unsetting/changing GuestAccountStatus - ''' + """ # disable GuestAccountStatus self._testSeceditPolicy( - 'GuestAccountStatus', - 'Disabled', - [r'^EnableGuestAccount = 0']) + "GuestAccountStatus", "Disabled", [r"^EnableGuestAccount = 0"] + ) # enable GuestAccountStatus self._testSeceditPolicy( - 'GuestAccountStatus', - 'Enabled', - [r'^EnableGuestAccount = 1']) + "GuestAccountStatus", "Enabled", [r"^EnableGuestAccount = 1"] + ) @destructiveTest def test_set_computer_policy_PasswordComplexity(self): - ''' + """ Test setting/unsetting/changing PasswordComplexity - ''' + """ # disable PasswordComplexity self._testSeceditPolicy( - 'Password must meet complexity requirements', - 'Disabled', - [r'^PasswordComplexity = 0']) + "Password must meet complexity requirements", + "Disabled", + [r"^PasswordComplexity = 0"], + ) # enable PasswordComplexity self._testSeceditPolicy( - 'PasswordComplexity', - 'Enabled', - [r'^PasswordComplexity = 1']) + "PasswordComplexity", "Enabled", [r"^PasswordComplexity = 1"] + ) @destructiveTest def test_set_computer_policy_PasswordLen(self): - ''' + """ Test setting/unsetting/changing PasswordLength - ''' + """ # set Minimum password length self._testSeceditPolicy( - 'Minimum password length', - 10, - [r'^MinimumPasswordLength = 10']) + "Minimum password length", 10, [r"^MinimumPasswordLength = 10"] + ) # set MinimumPasswordLength = 0 - self._testSeceditPolicy( - 'MinPasswordLen', - 0, - [r'^MinimumPasswordLength = 0']) + self._testSeceditPolicy("MinPasswordLen", 0, [r"^MinimumPasswordLength = 0"]) @destructiveTest def test_set_computer_policy_SeNetworkLogonRight(self): - ''' + """ Test setting/unsetting/changing PasswordLength - ''' + """ # set SeNetworkLogonRight to only Administrators self._testSeceditPolicy( - 'Access this computer from the network', - ['Administrators'], - [r'^SeNetworkLogonRight = \*S-1-5-32-544'], - cumulative_rights_assignments=False) + "Access this computer from the network", + ["Administrators"], + [r"^SeNetworkLogonRight = \*S-1-5-32-544"], + cumulative_rights_assignments=False, + ) # set SeNetworkLogonRight back to the default self._testSeceditPolicy( - 'SeNetworkLogonRight', - ['Everyone', 'Administrators', 'Users', 'Backup Operators'], - [r'^SeNetworkLogonRight = \*S-1-1-0,\*S-1-5-32-544,\*S-1-5-32-545,\*S-1-5-32-551']) + "SeNetworkLogonRight", + ["Everyone", "Administrators", "Users", "Backup Operators"], + [ + r"^SeNetworkLogonRight = \*S-1-1-0,\*S-1-5-32-544,\*S-1-5-32-545,\*S-1-5-32-551" + ], + ) @destructiveTest def test_set_computer_policy_multipleAdmxPolicies(self): - ''' + """ Tests setting several ADMX policies in succession and validating the configuration w/lgop - ''' + """ # set one policy self._testAdmxPolicy( - r'Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection', - 'Disabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0']) + r"Windows Components\Remote Desktop Services\Remote Desktop Session Host\Device and Resource Redirection\Do not allow Clipboard redirection", + "Disabled", + [ + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0" + ], + ) # set another policy and make sure both this policy and the previous are okay self._testAdmxPolicy( - 'RA_Unsolicit', + "RA_Unsolicit", { - 'Configure Offer Remote Access': 'Enabled', - 'Permit remote control of this computer': 'Allow helpers to remotely control the computer', - 'Helpers': ['administrators', 'user1'] + "Configure Offer Remote Access": "Enabled", + "Permit remote control of this computer": "Allow helpers to remotely control the computer", + "Helpers": ["administrators", "user1"], }, [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1', - ]) + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1", + ], + ) # Configure Automatic Updates and validate everything is still okay self._testAdmxPolicy( - r'Windows Components\Windows Update\Configure Automatic Updates', - 'Disabled', + r"Windows Components\Windows Update\Configure Automatic Updates", + "Disabled", [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1', - r'Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:1', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DELETE' - ]) + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fDisableClip[\s]*DWORD:0", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*user1[\s]*SZ:user1[\s]*", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services\\RAUnsolicit[\s]*administrators[\s]*SZ:administrators[\s]*", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicited[\s]*DWORD:1", + r"Computer[\s]*Software\\policies\\Microsoft\\Windows NT\\Terminal Services[\s]*fAllowUnsolicitedFullControl[\s]*DWORD:1", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*NoAutoUpdate[\s]*DWORD:1", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AUOptions[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AutomaticMaintenanceEnabled[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallDay[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*ScheduledInstallTime[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate\\AU[\s]*AllowMUUpdateService[\s]*DELETE", + ], + ) @destructiveTest def test_set_computer_policy_DisableDomainCreds(self): - ''' + """ Tests Enable/Disable of DisableDomainCreds policy - ''' + """ self._testRegistryPolicy( - policy_name='DisableDomainCreds', - policy_config='Enabled', - registry_value_hive='HKEY_LOCAL_MACHINE', - registry_value_path='SYSTEM\\CurrentControlSet\\Control\\Lsa', - registry_value_vname='DisableDomainCreds', - expected_value_data=1) + policy_name="DisableDomainCreds", + policy_config="Enabled", + registry_value_hive="HKEY_LOCAL_MACHINE", + registry_value_path="SYSTEM\\CurrentControlSet\\Control\\Lsa", + registry_value_vname="DisableDomainCreds", + expected_value_data=1, + ) self._testRegistryPolicy( - policy_name='Network access: Do not allow storage of passwords and credentials for network authentication', - policy_config='Disabled', - registry_value_hive='HKEY_LOCAL_MACHINE', - registry_value_path='SYSTEM\\CurrentControlSet\\Control\\Lsa', - registry_value_vname='DisableDomainCreds', - expected_value_data=0) + policy_name="Network access: Do not allow storage of passwords and credentials for network authentication", + policy_config="Disabled", + registry_value_hive="HKEY_LOCAL_MACHINE", + registry_value_path="SYSTEM\\CurrentControlSet\\Control\\Lsa", + registry_value_vname="DisableDomainCreds", + expected_value_data=0, + ) @destructiveTest def test_set_computer_policy_ForceGuest(self): - ''' + """ Tests changing ForceGuest policy - ''' + """ self._testRegistryPolicy( - policy_name='ForceGuest', - policy_config='Guest only - local users authenticate as Guest', - registry_value_hive='HKEY_LOCAL_MACHINE', - registry_value_path='SYSTEM\\CurrentControlSet\\Control\\Lsa', - registry_value_vname='ForceGuest', - expected_value_data=1) + policy_name="ForceGuest", + policy_config="Guest only - local users authenticate as Guest", + registry_value_hive="HKEY_LOCAL_MACHINE", + registry_value_path="SYSTEM\\CurrentControlSet\\Control\\Lsa", + registry_value_vname="ForceGuest", + expected_value_data=1, + ) self._testRegistryPolicy( - policy_name='Network access: Sharing and security model for local accounts', - policy_config='Classic - local users authenticate as themselves', - registry_value_hive='HKEY_LOCAL_MACHINE', - registry_value_path='SYSTEM\\CurrentControlSet\\Control\\Lsa', - registry_value_vname='ForceGuest', - expected_value_data=0) + policy_name="Network access: Sharing and security model for local accounts", + policy_config="Classic - local users authenticate as themselves", + registry_value_hive="HKEY_LOCAL_MACHINE", + registry_value_path="SYSTEM\\CurrentControlSet\\Control\\Lsa", + registry_value_vname="ForceGuest", + expected_value_data=0, + ) @destructiveTest def test_set_computer_policy_DisableUXWUAccess(self): - ''' + """ Tests changing DisableUXWUAccess #50079 shows using the 'Remove access to use all Windows Update features' failed Policy only exists on 2016 - ''' - valid_osreleases = ['2016Server'] + """ + valid_osreleases = ["2016Server"] if self.osrelease not in valid_osreleases: - self.skipTest('DisableUXWUAccess policy is only applicable if the osrelease grain is {0}'.format(' or '.join(valid_osreleases))) + self.skipTest( + "DisableUXWUAccess policy is only applicable if the osrelease grain is {0}".format( + " or ".join(valid_osreleases) + ) + ) else: self._testAdmxPolicy( - r'DisableUXWUAccess', - 'Enabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetDisableUXWUAccess[\s]*DWORD:1']) + r"DisableUXWUAccess", + "Enabled", + [ + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetDisableUXWUAccess[\s]*DWORD:1" + ], + ) self._testAdmxPolicy( - r'Remove access to use all Windows Update features', - 'Disabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetDisableUXWUAccess[\s]*DWORD:0']) + r"Remove access to use all Windows Update features", + "Disabled", + [ + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetDisableUXWUAccess[\s]*DWORD:0" + ], + ) self._testAdmxPolicy( - r'Windows Components\Windows Update\Remove access to use all Windows Update features', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + r"Windows Components\Windows Update\Remove access to use all Windows Update features", + "Not Configured", + [ + r"; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED." + ], + ) @destructiveTest def test_set_computer_policy_Access_data_sources_across_domains(self): - ''' + """ Tests that a policy that has multiple names - ''' + """ self._testAdmxPolicy( - r'Access data sources across domains', - 'Enabled', - [], - assert_true=False) + r"Access data sources across domains", "Enabled", [], assert_true=False + ) self._testAdmxPolicy( - r'Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains', - {'Access data sources across domains': 'Prompt'}, - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DWORD:1']) + r"Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains", + {"Access data sources across domains": "Prompt"}, + [ + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DWORD:1" + ], + ) self._testAdmxPolicy( - r'Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains', - {'Access data sources across domains': 'Enable'}, - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DWORD:0']) + r"Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains", + {"Access data sources across domains": "Enable"}, + [ + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DWORD:0" + ], + ) self._testAdmxPolicy( - r'Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains', - 'Disabled', - [r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DELETE']) + r"Windows Components\Internet Explorer\Internet Control Panel\Security Page\Internet Zone\Access data sources across domains", + "Disabled", + [ + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Zones\\3[\s]*1406[\s]*DELETE" + ], + ) @destructiveTest def test_set_computer_policy_ActiveHours(self): - ''' + """ Test configuring the ActiveHours policy, #47784 Only applies to 2016Server # activehours.sls @@ -714,111 +794,136 @@ class WinLgpoTest(ModuleCase): 'ActiveHours': 'ActiveHoursStartTime': '8 AM' 'ActiveHoursEndTime': '7 PM' - ''' - valid_osreleases = ['2016Server'] + """ + valid_osreleases = ["2016Server"] if self.osrelease not in valid_osreleases: - self.skipTest('ActiveHours policy is only applicable if the osrelease grain is {0}'.format(' or '.join(valid_osreleases))) + self.skipTest( + "ActiveHours policy is only applicable if the osrelease grain is {0}".format( + " or ".join(valid_osreleases) + ) + ) else: self._testAdmxPolicy( - r'ActiveHours', - {'ActiveHoursStartTime': '8 AM', 'ActiveHoursEndTime': '7 PM'}, + r"ActiveHours", + {"ActiveHoursStartTime": "8 AM", "ActiveHoursEndTime": "7 PM"}, [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:1', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DWORD:8', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DWORD:19' - ]) + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:1", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DWORD:8", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DWORD:19", + ], + ) self._testAdmxPolicy( - r'ActiveHours', - {'ActiveHoursStartTime': '5 AM', 'ActiveHoursEndTime': '10 PM'}, + r"ActiveHours", + {"ActiveHoursStartTime": "5 AM", "ActiveHoursEndTime": "10 PM"}, [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:1', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DWORD:5', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DWORD:22' - ]) + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:1", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DWORD:5", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DWORD:22", + ], + ) self._testAdmxPolicy( - 'Turn off auto-restart for updates during active hours', - 'Disabled', + "Turn off auto-restart for updates during active hours", + "Disabled", [ - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:0', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DELETE', - r'Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DELETE' - ]) + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*SetActiveHours[\s]*DWORD:0", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursStart[\s]*DELETE", + r"Computer[\s]*Software\\Policies\\Microsoft\\Windows\\WindowsUpdate[\s]*ActiveHoursEnd[\s]*DELETE", + ], + ) self._testAdmxPolicy( - r'Windows Components\Windows Update\Turn off auto-restart for updates during active hours', - 'Not Configured', - [r'; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED.']) + r"Windows Components\Windows Update\Turn off auto-restart for updates during active hours", + "Not Configured", + [ + r"; Source file: c:\\windows\\system32\\grouppolicy\\machine\\registry.pol[\s]*; PARSING COMPLETED." + ], + ) @destructiveTest def test_set_computer_policy_AllowTelemetry(self): - ''' + """ Tests that a the AllowTelemetry policy is applied correctly and that it doesn't appear in subsequent group policy states as having changed - ''' - valid_osreleases = ['10', '2016Server', '2019Server'] + """ + valid_osreleases = ["10", "2016Server", "2019Server"] if self.osrelease not in valid_osreleases: - self.skipTest('Allow Telemetry policy is only applicable if the ' - 'osrelease grain is {0}'.format(' or '.join(valid_osreleases))) + self.skipTest( + "Allow Telemetry policy is only applicable if the " + "osrelease grain is {0}".format(" or ".join(valid_osreleases)) + ) else: self._testAdmxPolicy( - 'Allow Telemetry', - {'AllowTelemetry': '1 - Basic'}, - [r'Software\\Policies\\Microsoft\\Windows\\DataCollection[\s]*AllowTelemetry[\s]*DWORD:1'], - assert_true=True) + "Allow Telemetry", + {"AllowTelemetry": "1 - Basic"}, + [ + r"Software\\Policies\\Microsoft\\Windows\\DataCollection[\s]*AllowTelemetry[\s]*DWORD:1" + ], + assert_true=True, + ) # This policy does not exist on newer Windows builds result = self.run_function( - 'lgpo.get_policy_info', - ['Disable pre-release features or settings'], - policy_class='machine') - if result['policy_found']: + "lgpo.get_policy_info", + ["Disable pre-release features or settings"], + policy_class="machine", + ) + if result["policy_found"]: result = self.run_function( - 'state.single', - ['lgpo.set'], - name='state', + "state.single", + ["lgpo.set"], + name="state", computer_policy={ - 'Disable pre-release features or settings': 'Disabled' - } + "Disable pre-release features or settings": "Disabled" + }, ) - name = 'lgpo_|-state_|-state_|-set' + name = "lgpo_|-state_|-state_|-set" expected = { - 'new': { - 'Computer Configuration': { - 'Disable pre-release features or settings': 'Disabled'}}, - 'old': { - 'Computer Configuration': { - 'Disable pre-release features or settings': 'Not Configured'}}} - self.assertDictEqual(result[name]['changes'], expected) + "new": { + "Computer Configuration": { + "Disable pre-release features or settings": "Disabled" + } + }, + "old": { + "Computer Configuration": { + "Disable pre-release features or settings": "Not Configured" + } + }, + } + self.assertDictEqual(result[name]["changes"], expected) else: result = self.run_function( - 'lgpo.get_policy_info', - ['Manage preview builds'], - policy_class='machine' + "lgpo.get_policy_info", + ["Manage preview builds"], + policy_class="machine", ) - if result['policy_found']: + if result["policy_found"]: result = self.run_function( - 'state.single', - ['lgpo.set'], - name='state', - computer_policy={ - 'Manage preview builds': 'Disabled' - } + "state.single", + ["lgpo.set"], + name="state", + computer_policy={"Manage preview builds": "Disabled"}, ) - name = 'lgpo_|-state_|-state_|-set' + name = "lgpo_|-state_|-state_|-set" expected = { - 'new': { - 'Computer Configuration': { - 'Manage preview builds': 'Disabled'}}, - 'old': { - 'Computer Configuration': { - 'Manage preview builds': 'Not Configured'}}} - self.assertDictEqual(result[name]['changes'], expected) + "new": { + "Computer Configuration": { + "Manage preview builds": "Disabled" + } + }, + "old": { + "Computer Configuration": { + "Manage preview builds": "Not Configured" + } + }, + } + self.assertDictEqual(result[name]["changes"], expected) def tearDown(self): - ''' + """ tearDown method, runs after each test - ''' + """ self.run_state( - 'file.absent', - name='c:\\windows\\system32\\grouppolicy\\machine\\registry.pol') + "file.absent", + name="c:\\windows\\system32\\grouppolicy\\machine\\registry.pol", + ) self.run_state( - 'file.absent', - name='c:\\windows\\system32\\grouppolicy\\user\\registry.pol') + "file.absent", name="c:\\windows\\system32\\grouppolicy\\user\\registry.pol" + ) diff --git a/tests/integration/modules/test_win_ntp.py b/tests/integration/modules/test_win_ntp.py index a1410b55d36..a16dcd472ac 100644 --- a/tests/integration/modules/test_win_ntp.py +++ b/tests/integration/modules/test_win_ntp.py @@ -1,31 +1,30 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, flaky - -# Import Salt Libs +import pytest import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, flaky +from tests.support.unit import skipIf @flaky -@skipIf(not salt.utils.platform.is_windows(), 'Tests for only Windows') +@skipIf(not salt.utils.platform.is_windows(), "Tests for only Windows") +@pytest.mark.windows_whitelisted class NTPTest(ModuleCase): - ''' + """ Validate windows ntp module - ''' + """ + @destructiveTest def test_ntp_set_servers(self): - ''' + """ test ntp get and set servers - ''' - ntp_srv = 'pool.ntp.org' - set_srv = self.run_function('ntp.set_servers', [ntp_srv]) + """ + ntp_srv = "pool.ntp.org" + set_srv = self.run_function("ntp.set_servers", [ntp_srv]) self.assertTrue(set_srv) - get_srv = self.run_function('ntp.get_servers') + get_srv = self.run_function("ntp.get_servers") self.assertEqual(ntp_srv, get_srv[0]) diff --git a/tests/integration/modules/test_win_pkg.py b/tests/integration/modules/test_win_pkg.py index 00924684b0b..12d9dbc583d 100644 --- a/tests/integration/modules/test_win_pkg.py +++ b/tests/integration/modules/test_win_pkg.py @@ -1,34 +1,36 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import textwrap -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest -from tests.support.runtests import RUNTIME_VARS - -# Import Salt libs +import pytest import salt.utils.files import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf -@skipIf(not salt.utils.platform.is_windows(), 'windows test only') +@skipIf(not salt.utils.platform.is_windows(), "windows test only") +@pytest.mark.windows_whitelisted class WinPKGTest(ModuleCase): - ''' + """ Tests for salt.modules.win_pkg. There are already some pkg execution module tests in the the test integration.modules.test_pkg but this will be for specific windows software respository tests while using the win_pkg module. - ''' + """ + @classmethod def setUpClass(cls): - cls.repo_dir = os.path.join(RUNTIME_VARS.FILES, 'file', 'base', 'win', 'repo-ng') - cls.curl_sls_path = os.path.join(cls.repo_dir, 'curl.sls') + cls.repo_dir = os.path.join( + RUNTIME_VARS.FILES, "file", "base", "win", "repo-ng" + ) + cls.curl_sls_path = os.path.join(cls.repo_dir, "curl.sls") def tearDown(self): if os.path.isfile(self.curl_sls_path): @@ -36,16 +38,24 @@ class WinPKGTest(ModuleCase): @destructiveTest def test_adding_removing_pkg_sls(self): - ''' + """ Test add and removing a new pkg sls in the windows software repository - ''' + """ + def _check_pkg(pkgs, check_refresh, exists=True): - refresh = self.run_function('pkg.refresh_db') - self.assertEqual(check_refresh, refresh['total'], - msg='total returned {0}. Expected return {1}'.format(refresh['total'], check_refresh)) - repo_data = self.run_function('pkg.get_repo_data') - repo_cache = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache', 'files', 'base', 'win', 'repo-ng') + refresh = self.run_function("pkg.refresh_db") + self.assertEqual( + check_refresh, + refresh["total"], + msg="total returned {0}. Expected return {1}".format( + refresh["total"], check_refresh + ), + ) + repo_data = self.run_function("pkg.get_repo_data") + repo_cache = os.path.join( + RUNTIME_VARS.TMP, "rootdir", "cache", "files", "base", "win", "repo-ng" + ) for pkg in pkgs: if exists: assert pkg in str(repo_data), str(repo_data) @@ -54,17 +64,19 @@ class WinPKGTest(ModuleCase): for root, dirs, files in os.walk(repo_cache): if exists: - assert pkg + '.sls' in files + assert pkg + ".sls" in files else: - assert pkg + '.sls' not in files + assert pkg + ".sls" not in files - pkgs = ['putty', '7zip'] + pkgs = ["putty", "7zip"] # check putty and 7zip are in cache and repo query _check_pkg(pkgs, 2) # now add new sls - with salt.utils.files.fopen(self.curl_sls_path, 'w') as fp_: - fp_.write(textwrap.dedent(''' + with salt.utils.files.fopen(self.curl_sls_path, "w") as fp_: + fp_.write( + textwrap.dedent( + """ curl: '7.46.0': full_name: 'cURL' @@ -80,13 +92,15 @@ class WinPKGTest(ModuleCase): msiexec: True locale: en_US reboot: False - ''')) + """ + ) + ) # now check if curl is also in cache and repo query - pkgs.append('curl') + pkgs.append("curl") for pkg in pkgs: - self.assertIn(pkg + '.sls', os.listdir(self.repo_dir)) + self.assertIn(pkg + ".sls", os.listdir(self.repo_dir)) _check_pkg(pkgs, 3) # remove curl sls and check its not in cache and repo query os.remove(self.curl_sls_path) - _check_pkg(['curl'], 2, exists=False) + _check_pkg(["curl"], 2, exists=False) diff --git a/tests/integration/modules/test_win_servermanager.py b/tests/integration/modules/test_win_servermanager.py index c3290f80cd1..401645a6bd7 100644 --- a/tests/integration/modules/test_win_servermanager.py +++ b/tests/integration/modules/test_win_servermanager.py @@ -1,26 +1,25 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import -# Import Salt Testing libs +import pytest +import salt.utils.platform from tests.support.case import ModuleCase from tests.support.unit import skipIf -# Import Salt libs -import salt.utils.platform - -@skipIf(not salt.utils.platform.is_windows(), 'windows test only') +@skipIf(not salt.utils.platform.is_windows(), "windows test only") +@pytest.mark.windows_whitelisted class WinServermanagerTest(ModuleCase): - ''' + """ Test for salt.modules.win_servermanager - ''' + """ + def test_list_available(self): - ''' + """ Test list available features to install - ''' - cmd = self.run_function('win_servermanager.list_available') - self.assertIn('DNS', cmd) - self.assertIn('NetworkController', cmd) - self.assertIn('RemoteAccess', cmd) + """ + cmd = self.run_function("win_servermanager.list_available") + self.assertIn("DNS", cmd) + self.assertIn("NetworkController", cmd) + self.assertIn("RemoteAccess", cmd) diff --git a/tests/integration/netapi/rest_cherrypy/test_app.py b/tests/integration/netapi/rest_cherrypy/test_app.py index c54cc84e2b7..d8357a286cb 100644 --- a/tests/integration/netapi/rest_cherrypy/test_app.py +++ b/tests/integration/netapi/rest_cherrypy/test_app.py @@ -2,6 +2,7 @@ # Import python libs from __future__ import absolute_import + import os # Import salt libs @@ -10,331 +11,351 @@ import salt.utils.stringutils # Import test support libs import tests.support.cherrypy_testclasses as cptc -from tests.support.helpers import flaky # Import 3rd-party libs -from salt.ext.six.moves.urllib.parse import urlencode # pylint: disable=no-name-in-module,import-error +from salt.ext.six.moves.urllib.parse import ( # pylint: disable=no-name-in-module,import-error + urlencode, +) +from tests.support.helpers import flaky class TestAuth(cptc.BaseRestCherryPyTest): def test_get_root_noauth(self): - ''' + """ GET requests to the root URL should not require auth - ''' - request, response = self.request('/') - self.assertEqual(response.status, '200 OK') + """ + request, response = self.request("/") + self.assertEqual(response.status, "200 OK") def test_post_root_auth(self): - ''' + """ POST requests to the root URL redirect to login - ''' - request, response = self.request('/', method='POST', data={}) - self.assertEqual(response.status, '401 Unauthorized') + """ + request, response = self.request("/", method="POST", data={}) + self.assertEqual(response.status, "401 Unauthorized") def test_login_noauth(self): - ''' + """ GET requests to the login URL should not require auth - ''' - request, response = self.request('/login') - self.assertEqual(response.status, '200 OK') + """ + request, response = self.request("/login") + self.assertEqual(response.status, "200 OK") def test_webhook_auth(self): - ''' + """ Requests to the webhook URL require auth by default - ''' - request, response = self.request('/hook', method='POST', data={}) - self.assertEqual(response.status, '401 Unauthorized') + """ + request, response = self.request("/hook", method="POST", data={}) + self.assertEqual(response.status, "401 Unauthorized") class TestLogin(cptc.BaseRestCherryPyTest): - auth_creds = ( - ('username', 'saltdev'), - ('password', 'saltdev'), - ('eauth', 'auto')) + auth_creds = (("username", "saltdev"), ("password", "saltdev"), ("eauth", "auto")) def test_good_login(self): - ''' + """ Test logging in - ''' + """ body = urlencode(self.auth_creds) - request, response = self.request('/login', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '200 OK') + request, response = self.request( + "/login", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "200 OK") return response def test_bad_login(self): - ''' + """ Test logging in - ''' - body = urlencode({'totally': 'invalid_creds'}) - request, response = self.request('/login', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '401 Unauthorized') + """ + body = urlencode({"totally": "invalid_creds"}) + request, response = self.request( + "/login", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "401 Unauthorized") def test_logout(self): ret = self.test_good_login() - token = ret.headers['X-Auth-Token'] + token = ret.headers["X-Auth-Token"] body = urlencode({}) - request, response = self.request('/logout', method='POST', body=body, + request, response = self.request( + "/logout", + method="POST", + body=body, headers={ - 'content-type': 'application/x-www-form-urlencoded', - 'X-Auth-Token': token, - }) - self.assertEqual(response.status, '200 OK') + "content-type": "application/x-www-form-urlencoded", + "X-Auth-Token": token, + }, + ) + self.assertEqual(response.status, "200 OK") class TestRun(cptc.BaseRestCherryPyTest): auth_creds = ( - ('username', 'saltdev_auto'), - ('password', 'saltdev'), - ('eauth', 'auto')) + ("username", "saltdev_auto"), + ("password", "saltdev"), + ("eauth", "auto"), + ) low = ( - ('client', 'local'), - ('tgt', '*'), - ('fun', 'test.ping'), + ("client", "local"), + ("tgt", "*"), + ("fun", "test.ping"), ) def test_run_good_login(self): - ''' + """ Test the run URL with good auth credentials - ''' + """ cmd = dict(self.low, **dict(self.auth_creds)) body = urlencode(cmd) - request, response = self.request('/run', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '200 OK') + request, response = self.request( + "/run", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "200 OK") def test_run_bad_login(self): - ''' + """ Test the run URL with bad auth credentials - ''' - cmd = dict(self.low, **{'totally': 'invalid_creds'}) + """ + cmd = dict(self.low, **{"totally": "invalid_creds"}) body = urlencode(cmd) - request, response = self.request('/run', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '401 Unauthorized') + request, response = self.request( + "/run", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "401 Unauthorized") def test_run_empty_token(self): - ''' + """ Test the run URL with empty token - ''' - cmd = dict(self.low, **{'token': ''}) + """ + cmd = dict(self.low, **{"token": ""}) body = urlencode(cmd) - request, response = self.request('/run', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - assert response.status == '401 Unauthorized' + request, response = self.request( + "/run", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + assert response.status == "401 Unauthorized" def test_run_empty_token_upercase(self): - ''' + """ Test the run URL with empty token with upercase characters - ''' - cmd = dict(self.low, **{'ToKen': ''}) + """ + cmd = dict(self.low, **{"ToKen": ""}) body = urlencode(cmd) - request, response = self.request('/run', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - assert response.status == '401 Unauthorized' + request, response = self.request( + "/run", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + assert response.status == "401 Unauthorized" def test_run_wrong_token(self): - ''' + """ Test the run URL with incorrect token - ''' - cmd = dict(self.low, **{'token': 'bad'}) + """ + cmd = dict(self.low, **{"token": "bad"}) body = urlencode(cmd) - request, response = self.request('/run', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - assert response.status == '401 Unauthorized' + request, response = self.request( + "/run", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + assert response.status == "401 Unauthorized" def test_run_pathname_token(self): - ''' + """ Test the run URL with path that exists in token - ''' - cmd = dict(self.low, **{'token': os.path.join('etc', 'passwd')}) + """ + cmd = dict(self.low, **{"token": os.path.join("etc", "passwd")}) body = urlencode(cmd) - request, response = self.request('/run', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - assert response.status == '401 Unauthorized' + request, response = self.request( + "/run", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + assert response.status == "401 Unauthorized" def test_run_pathname_not_exists_token(self): - ''' + """ Test the run URL with path that does not exist in token - ''' - cmd = dict(self.low, **{'token': os.path.join('tmp', 'doesnotexist')}) + """ + cmd = dict(self.low, **{"token": os.path.join("tmp", "doesnotexist")}) body = urlencode(cmd) - request, response = self.request('/run', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - assert response.status == '401 Unauthorized' + request, response = self.request( + "/run", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + assert response.status == "401 Unauthorized" def test_run_extra_parameters(self): - ''' + """ Test the run URL with good auth credentials - ''' + """ cmd = dict(self.low, **dict(self.auth_creds)) - cmd['id_'] = 'someminionname' + cmd["id_"] = "someminionname" body = urlencode(cmd) - request, response = self.request('/run', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '200 OK') + request, response = self.request( + "/run", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "200 OK") class TestWebhookDisableAuth(cptc.BaseRestCherryPyTest): - def __get_opts__(self): return { - 'rest_cherrypy': { - 'port': 8000, - 'debug': True, - 'webhook_disable_auth': True, + "rest_cherrypy": { + "port": 8000, + "debug": True, + "webhook_disable_auth": True, }, } def test_webhook_noauth(self): - ''' + """ Auth can be disabled for requests to the webhook URL - ''' - body = urlencode({'foo': 'Foo!'}) - request, response = self.request('/hook', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '200 OK') + """ + body = urlencode({"foo": "Foo!"}) + request, response = self.request( + "/hook", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "200 OK") class TestArgKwarg(cptc.BaseRestCherryPyTest): - auth_creds = ( - ('username', 'saltdev'), - ('password', 'saltdev'), - ('eauth', 'auto')) + auth_creds = (("username", "saltdev"), ("password", "saltdev"), ("eauth", "auto")) low = ( - ('client', 'runner'), - ('fun', 'test.arg'), + ("client", "runner"), + ("fun", "test.arg"), # use singular form for arg and kwarg - ('arg', [1234]), - ('kwarg', {'ext_source': 'redis'}), + ("arg", [1234]), + ("kwarg", {"ext_source": "redis"}), ) def _token(self): - ''' + """ Return the token - ''' + """ body = urlencode(self.auth_creds) request, response = self.request( - '/login', - method='POST', + "/login", + method="POST", body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - } + headers={"content-type": "application/x-www-form-urlencoded"}, ) - return response.headers['X-Auth-Token'] + return response.headers["X-Auth-Token"] def test_accepts_arg_kwarg_keys(self): - ''' + """ Ensure that (singular) arg and kwarg keys (for passing parameters) are supported by runners. - ''' + """ cmd = dict(self.low) body = salt.utils.json.dumps(cmd) request, response = self.request( - '/', - method='POST', + "/", + method="POST", body=body, headers={ - 'content-type': 'application/json', - 'X-Auth-Token': self._token(), - 'Accept': 'application/json', - } + "content-type": "application/json", + "X-Auth-Token": self._token(), + "Accept": "application/json", + }, ) resp = salt.utils.json.loads(salt.utils.stringutils.to_str(response.body[0])) - self.assertEqual(resp['return'][0]['args'], [1234]) - self.assertEqual(resp['return'][0]['kwargs'], - {'ext_source': 'redis'}) + self.assertEqual(resp["return"][0]["args"], [1234]) + self.assertEqual(resp["return"][0]["kwargs"], {"ext_source": "redis"}) class TestJobs(cptc.BaseRestCherryPyTest): auth_creds = ( - ('username', 'saltdev_auto'), - ('password', 'saltdev'), - ('eauth', 'auto')) + ("username", "saltdev_auto"), + ("password", "saltdev"), + ("eauth", "auto"), + ) low = ( - ('client', 'local'), - ('tgt', '*'), - ('fun', 'test.ping'), + ("client", "local"), + ("tgt", "*"), + ("fun", "test.ping"), ) def _token(self): - ''' + """ Return the token - ''' + """ body = urlencode(self.auth_creds) request, response = self.request( - '/login', - method='POST', + "/login", + method="POST", body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - } + headers={"content-type": "application/x-www-form-urlencoded"}, ) - return response.headers['X-Auth-Token'] + return response.headers["X-Auth-Token"] def _add_job(self): - ''' + """ Helper function to add a job to the job cache - ''' + """ cmd = dict(self.low, **dict(self.auth_creds)) body = urlencode(cmd) - request, response = self.request('/run', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '200 OK') + request, response = self.request( + "/run", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "200 OK") @flaky def test_all_jobs(self): - ''' + """ test query to /jobs returns job data - ''' + """ self._add_job() - request, response = self.request('/jobs', method='GET', - headers={ - 'Accept': 'application/json', - 'X-Auth-Token': self._token(), - }) + request, response = self.request( + "/jobs", + method="GET", + headers={"Accept": "application/json", "X-Auth-Token": self._token()}, + ) resp = salt.utils.json.loads(salt.utils.stringutils.to_str(response.body[0])) - self.assertIn('test.ping', str(resp['return'])) - self.assertEqual(response.status, '200 OK') + self.assertIn("test.ping", str(resp["return"])) + self.assertEqual(response.status, "200 OK") diff --git a/tests/integration/netapi/rest_cherrypy/test_app_pam.py b/tests/integration/netapi/rest_cherrypy/test_app_pam.py index 9fda228c084..ac6dde89e66 100644 --- a/tests/integration/netapi/rest_cherrypy/test_app_pam.py +++ b/tests/integration/netapi/rest_cherrypy/test_app_pam.py @@ -1,136 +1,142 @@ # coding: utf-8 -''' +""" Integration Tests for restcherry salt-api with pam eauth -''' +""" # Import Python libs from __future__ import absolute_import -# Import test support libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, skip_if_not_root -import tests.support.cherrypy_testclasses as cptc - # Import Salt Libs import salt.utils.platform +import tests.support.cherrypy_testclasses as cptc # Import 3rd-party libs -from salt.ext.six.moves.urllib.parse import urlencode # pylint: disable=no-name-in-module,import-error +from salt.ext.six.moves.urllib.parse import ( # pylint: disable=no-name-in-module,import-error + urlencode, +) + +# Import test support libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root +from tests.support.unit import skipIf + if cptc.HAS_CHERRYPY: import cherrypy -USERA = 'saltdev' -USERA_PWD = 'saltdev' -HASHED_USERA_PWD = '$6$SALTsalt$ZZFD90fKFWq8AGmmX0L3uBtS9fXL62SrTk5zcnQ6EkD6zoiM3kB88G1Zvs0xm/gZ7WXJRs5nsTBybUvGSqZkT.' +USERA = "saltdev" +USERA_PWD = "saltdev" +HASHED_USERA_PWD = "$6$SALTsalt$ZZFD90fKFWq8AGmmX0L3uBtS9fXL62SrTk5zcnQ6EkD6zoiM3kB88G1Zvs0xm/gZ7WXJRs5nsTBybUvGSqZkT." -AUTH_CREDS = { - 'username': USERA, - 'password': USERA_PWD, - 'eauth': 'pam'} +AUTH_CREDS = {"username": USERA, "password": USERA_PWD, "eauth": "pam"} -@skipIf(cptc.HAS_CHERRYPY is False, 'CherryPy not installed') +@skipIf(cptc.HAS_CHERRYPY is False, "CherryPy not installed") class TestAuthPAM(cptc.BaseRestCherryPyTest, ModuleCase): - ''' + """ Test auth with pam using salt-api - ''' + """ @destructiveTest @skip_if_not_root def setUp(self): super(TestAuthPAM, self).setUp() try: - add_user = self.run_function('user.add', [USERA], createhome=False) + add_user = self.run_function("user.add", [USERA], createhome=False) add_pwd = self.run_function( - 'shadow.set_password', + "shadow.set_password", [ USERA, - USERA_PWD if salt.utils.platform.is_darwin() else HASHED_USERA_PWD - ] + USERA_PWD if salt.utils.platform.is_darwin() else HASHED_USERA_PWD, + ], ) self.assertTrue(add_user) self.assertTrue(add_pwd) - user_list = self.run_function('user.list_users') + user_list = self.run_function("user.list_users") self.assertIn(USERA, str(user_list)) except AssertionError: - self.run_function('user.delete', [USERA], remove=True) - self.skipTest( - 'Could not add user or password, skipping test' - ) + self.run_function("user.delete", [USERA], remove=True) + self.skipTest("Could not add user or password, skipping test") def test_bad_pwd_pam_chsh_service(self): - ''' + """ Test login while specifying chsh service with bad passwd This test ensures this PR is working correctly: https://github.com/saltstack/salt/pull/31826 - ''' + """ copyauth_creds = AUTH_CREDS.copy() - copyauth_creds['service'] = 'chsh' - copyauth_creds['password'] = 'wrong_password' + copyauth_creds["service"] = "chsh" + copyauth_creds["password"] = "wrong_password" body = urlencode(copyauth_creds) - request, response = self.request('/login', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '401 Unauthorized') + request, response = self.request( + "/login", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "401 Unauthorized") def test_bad_pwd_pam_login_service(self): - ''' + """ Test login while specifying login service with bad passwd This test ensures this PR is working correctly: https://github.com/saltstack/salt/pull/31826 - ''' + """ copyauth_creds = AUTH_CREDS.copy() - copyauth_creds['service'] = 'login' - copyauth_creds['password'] = 'wrong_password' + copyauth_creds["service"] = "login" + copyauth_creds["password"] = "wrong_password" body = urlencode(copyauth_creds) - request, response = self.request('/login', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '401 Unauthorized') + request, response = self.request( + "/login", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "401 Unauthorized") def test_good_pwd_pam_chsh_service(self): - ''' + """ Test login while specifying chsh service with good passwd This test ensures this PR is working correctly: https://github.com/saltstack/salt/pull/31826 - ''' + """ copyauth_creds = AUTH_CREDS.copy() - copyauth_creds['service'] = 'chsh' + copyauth_creds["service"] = "chsh" body = urlencode(copyauth_creds) - request, response = self.request('/login', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '200 OK') + request, response = self.request( + "/login", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "200 OK") def test_good_pwd_pam_login_service(self): - ''' + """ Test login while specifying login service with good passwd This test ensures this PR is working correctly: https://github.com/saltstack/salt/pull/31826 - ''' + """ copyauth_creds = AUTH_CREDS.copy() - copyauth_creds['service'] = 'login' + copyauth_creds["service"] = "login" body = urlencode(copyauth_creds) - request, response = self.request('/login', method='POST', body=body, - headers={ - 'content-type': 'application/x-www-form-urlencoded' - }) - self.assertEqual(response.status, '200 OK') + request, response = self.request( + "/login", + method="POST", + body=body, + headers={"content-type": "application/x-www-form-urlencoded"}, + ) + self.assertEqual(response.status, "200 OK") @destructiveTest @skip_if_not_root def tearDown(self): - ''' + """ Clean up after tests. Delete user - ''' + """ super(TestAuthPAM, self).tearDown() - user_list = self.run_function('user.list_users') + user_list = self.run_function("user.list_users") # Remove saltdev user if USERA in user_list: - self.run_function('user.delete', [USERA], remove=True) + self.run_function("user.delete", [USERA], remove=True) # need to exit cherypy engine cherrypy.engine.exit() diff --git a/tests/integration/netapi/rest_tornado/test_app.py b/tests/integration/netapi/rest_tornado/test_app.py index 10ec29f7faa..bf3901a5b77 100644 --- a/tests/integration/netapi/rest_tornado/test_app.py +++ b/tests/integration/netapi/rest_tornado/test_app.py @@ -2,48 +2,52 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import os -import time import threading +import time # Import Salt Libs import salt.utils.json import salt.utils.stringutils -from salt.netapi.rest_tornado import saltnado -from salt.utils.versions import StrictVersion - -# Import Salt Testing Libs -from tests.unit.netapi.test_rest_tornado import SaltnadoTestCase -from tests.support.helpers import flaky -from tests.support.unit import skipIf # Import 3rd-party libs from salt.ext import six -from salt.utils.zeromq import zmq, ZMQDefaultLoop as ZMQIOLoop +from salt.netapi.rest_tornado import saltnado +from salt.utils.versions import StrictVersion +from salt.utils.zeromq import ZMQDefaultLoop as ZMQIOLoop +from salt.utils.zeromq import zmq +from tests.support.helpers import flaky +from tests.support.unit import skipIf + +# Import Salt Testing Libs +from tests.unit.netapi.test_rest_tornado import SaltnadoTestCase + HAS_ZMQ_IOLOOP = bool(zmq) class _SaltnadoIntegrationTestCase(SaltnadoTestCase): # pylint: disable=abstract-method - @property def opts(self): - return self.get_config('client_config', from_scratch=True) + return self.get_config("client_config", from_scratch=True) @property def mod_opts(self): - return self.get_config('minion', from_scratch=True) + return self.get_config("minion", from_scratch=True) -@skipIf(HAS_ZMQ_IOLOOP is False, 'PyZMQ version must be >= 14.0.1 to run these tests.') -@skipIf(StrictVersion(zmq.__version__) < StrictVersion('14.0.1'), 'PyZMQ must be >= 14.0.1 to run these tests.') +@skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.") +@skipIf( + StrictVersion(zmq.__version__) < StrictVersion("14.0.1"), + "PyZMQ must be >= 14.0.1 to run these tests.", +) class TestSaltAPIHandler(_SaltnadoIntegrationTestCase): - def setUp(self): super(TestSaltAPIHandler, self).setUp() - os.environ['ASYNC_TEST_TIMEOUT'] = '300' + os.environ["ASYNC_TEST_TIMEOUT"] = "300" def get_app(self): - urls = [('/', saltnado.SaltAPIHandler)] + urls = [("/", saltnado.SaltAPIHandler)] application = self.build_tornado_app(urls) @@ -52,513 +56,549 @@ class TestSaltAPIHandler(_SaltnadoIntegrationTestCase): return application def test_root(self): - ''' + """ Test the root path which returns the list of clients we support - ''' - response = self.fetch('/', - connect_timeout=30, - request_timeout=30, - ) + """ + response = self.fetch("/", connect_timeout=30, request_timeout=30,) self.assertEqual(response.code, 200) response_obj = salt.utils.json.loads(response.body) - self.assertEqual(sorted(response_obj['clients']), - ['local', 'local_async', 'runner', 'runner_async']) - self.assertEqual(response_obj['return'], 'Welcome') + self.assertEqual( + sorted(response_obj["clients"]), + ["local", "local_async", "runner", "runner_async"], + ) + self.assertEqual(response_obj["return"], "Welcome") def test_post_no_auth(self): - ''' + """ Test post with no auth token, should 401 - ''' + """ # get a token for this test - low = [{'client': 'local', - 'tgt': '*', - 'fun': 'test.ping', - }] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json']}, - follow_redirects=False, - connect_timeout=30, - request_timeout=30, - ) + low = [{"client": "local", "tgt": "*", "fun": "test.ping"}] + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={"Content-Type": self.content_type_map["json"]}, + follow_redirects=False, + connect_timeout=30, + request_timeout=30, + ) self.assertEqual(response.code, 302) - self.assertEqual(response.headers['Location'], '/login') + self.assertEqual(response.headers["Location"], "/login") # Local client tests - @skipIf(True, 'to be re-enabled when #23623 is merged') + @skipIf(True, "to be re-enabled when #23623 is merged") def test_simple_local_post(self): - ''' + """ Test a basic API of / - ''' - low = [{'client': 'local', - 'tgt': '*', - 'fun': 'test.ping', - }] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - connect_timeout=30, - request_timeout=30, - ) + """ + low = [{"client": "local", "tgt": "*", "fun": "test.ping"}] + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + connect_timeout=30, + request_timeout=30, + ) response_obj = salt.utils.json.loads(response.body) - self.assertEqual(len(response_obj['return']), 1) + self.assertEqual(len(response_obj["return"]), 1) # If --proxy is set, it will cause an extra minion_id to be in the # response. Since there's not a great way to know if the test # runner's proxy minion is running, and we're not testing proxy # minions here anyway, just remove it from the response. - response_obj['return'][0].pop('proxytest', None) - self.assertEqual(response_obj['return'][0], {'minion': True, 'sub_minion': True}) + response_obj["return"][0].pop("proxytest", None) + self.assertEqual( + response_obj["return"][0], {"minion": True, "sub_minion": True} + ) def test_simple_local_post_no_tgt(self): - ''' + """ POST job with invalid tgt - ''' - low = [{'client': 'local', - 'tgt': 'minion_we_dont_have', - 'fun': 'test.ping', - }] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - connect_timeout=30, - request_timeout=30, - ) + """ + low = [{"client": "local", "tgt": "minion_we_dont_have", "fun": "test.ping"}] + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + connect_timeout=30, + request_timeout=30, + ) response_obj = salt.utils.json.loads(response.body) - self.assertEqual(response_obj['return'], ["No minions matched the target. No command was sent, no jid was assigned."]) + self.assertEqual( + response_obj["return"], + [ + "No minions matched the target. No command was sent, no jid was assigned." + ], + ) # local client request body test - @skipIf(True, 'Undetermined race condition in test. Temporarily disabled.') + @skipIf(True, "Undetermined race condition in test. Temporarily disabled.") def test_simple_local_post_only_dictionary_request(self): - ''' + """ Test a basic API of / - ''' - low = {'client': 'local', - 'tgt': '*', - 'fun': 'test.ping', - } - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - connect_timeout=30, - request_timeout=30, - ) + """ + low = { + "client": "local", + "tgt": "*", + "fun": "test.ping", + } + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + connect_timeout=30, + request_timeout=30, + ) response_obj = salt.utils.json.loads(response.body) - self.assertEqual(len(response_obj['return']), 1) + self.assertEqual(len(response_obj["return"]), 1) # If --proxy is set, it will cause an extra minion_id to be in the # response. Since there's not a great way to know if the test # runner's proxy minion is running, and we're not testing proxy # minions here anyway, just remove it from the response. - response_obj['return'][0].pop('proxytest', None) - self.assertEqual(response_obj['return'][0], {'minion': True, 'sub_minion': True}) + response_obj["return"][0].pop("proxytest", None) + self.assertEqual( + response_obj["return"][0], {"minion": True, "sub_minion": True} + ) def test_simple_local_post_invalid_request(self): - ''' + """ Test a basic API of / - ''' + """ low = ["invalid request"] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - connect_timeout=30, - request_timeout=30, - ) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + connect_timeout=30, + request_timeout=30, + ) self.assertEqual(response.code, 400) # local_async tests def test_simple_local_async_post(self): - low = [{'client': 'local_async', - 'tgt': '*', - 'fun': 'test.ping', - }] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - ) + low = [{"client": "local_async", "tgt": "*", "fun": "test.ping"}] + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + ) response_obj = salt.utils.json.loads(response.body) - ret = response_obj['return'] - ret[0]['minions'] = sorted(ret[0]['minions']) + ret = response_obj["return"] + ret[0]["minions"] = sorted(ret[0]["minions"]) try: # If --proxy is set, it will cause an extra minion_id to be in the # response. Since there's not a great way to know if the test # runner's proxy minion is running, and we're not testing proxy # minions here anyway, just remove it from the response. - ret[0]['minions'].remove('proxytest') + ret[0]["minions"].remove("proxytest") except ValueError: pass # TODO: verify pub function? Maybe look at how we test the publisher self.assertEqual(len(ret), 1) - self.assertIn('jid', ret[0]) - self.assertEqual(ret[0]['minions'], sorted(['minion', 'sub_minion'])) + self.assertIn("jid", ret[0]) + self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"])) def test_multi_local_async_post(self): - low = [{'client': 'local_async', - 'tgt': '*', - 'fun': 'test.ping', - }, - {'client': 'local_async', - 'tgt': '*', - 'fun': 'test.ping', - }] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - ) + low = [ + {"client": "local_async", "tgt": "*", "fun": "test.ping"}, + {"client": "local_async", "tgt": "*", "fun": "test.ping"}, + ] + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + ) response_obj = salt.utils.json.loads(response.body) - ret = response_obj['return'] - ret[0]['minions'] = sorted(ret[0]['minions']) - ret[1]['minions'] = sorted(ret[1]['minions']) + ret = response_obj["return"] + ret[0]["minions"] = sorted(ret[0]["minions"]) + ret[1]["minions"] = sorted(ret[1]["minions"]) try: # If --proxy is set, it will cause an extra minion_id to be in the # response. Since there's not a great way to know if the test # runner's proxy minion is running, and we're not testing proxy # minions here anyway, just remove it from the response. - ret[0]['minions'].remove('proxytest') - ret[1]['minions'].remove('proxytest') + ret[0]["minions"].remove("proxytest") + ret[1]["minions"].remove("proxytest") except ValueError: pass self.assertEqual(len(ret), 2) - self.assertIn('jid', ret[0]) - self.assertIn('jid', ret[1]) - self.assertEqual(ret[0]['minions'], sorted(['minion', 'sub_minion'])) - self.assertEqual(ret[1]['minions'], sorted(['minion', 'sub_minion'])) + self.assertIn("jid", ret[0]) + self.assertIn("jid", ret[1]) + self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"])) + self.assertEqual(ret[1]["minions"], sorted(["minion", "sub_minion"])) def test_multi_local_async_post_multitoken(self): - low = [{'client': 'local_async', - 'tgt': '*', - 'fun': 'test.ping', - }, - {'client': 'local_async', - 'tgt': '*', - 'fun': 'test.ping', - 'token': self.token['token'], # send a different (but still valid token) - }, - {'client': 'local_async', - 'tgt': '*', - 'fun': 'test.ping', - 'token': 'BAD_TOKEN', # send a bad token - }, - ] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - ) + low = [ + {"client": "local_async", "tgt": "*", "fun": "test.ping"}, + { + "client": "local_async", + "tgt": "*", + "fun": "test.ping", + "token": self.token[ + "token" + ], # send a different (but still valid token) + }, + { + "client": "local_async", + "tgt": "*", + "fun": "test.ping", + "token": "BAD_TOKEN", # send a bad token + }, + ] + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + ) response_obj = salt.utils.json.loads(response.body) - ret = response_obj['return'] - ret[0]['minions'] = sorted(ret[0]['minions']) - ret[1]['minions'] = sorted(ret[1]['minions']) + ret = response_obj["return"] + ret[0]["minions"] = sorted(ret[0]["minions"]) + ret[1]["minions"] = sorted(ret[1]["minions"]) try: # If --proxy is set, it will cause an extra minion_id to be in the # response. Since there's not a great way to know if the test # runner's proxy minion is running, and we're not testing proxy # minions here anyway, just remove it from the response. - ret[0]['minions'].remove('proxytest') - ret[1]['minions'].remove('proxytest') + ret[0]["minions"].remove("proxytest") + ret[1]["minions"].remove("proxytest") except ValueError: pass self.assertEqual(len(ret), 3) # make sure we got 3 responses - self.assertIn('jid', ret[0]) # the first 2 are regular returns - self.assertIn('jid', ret[1]) - self.assertIn('Failed to authenticate', ret[2]) # bad auth - self.assertEqual(ret[0]['minions'], sorted(['minion', 'sub_minion'])) - self.assertEqual(ret[1]['minions'], sorted(['minion', 'sub_minion'])) + self.assertIn("jid", ret[0]) # the first 2 are regular returns + self.assertIn("jid", ret[1]) + self.assertIn("Failed to authenticate", ret[2]) # bad auth + self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"])) + self.assertEqual(ret[1]["minions"], sorted(["minion", "sub_minion"])) def test_simple_local_async_post_no_tgt(self): - low = [{'client': 'local_async', - 'tgt': 'minion_we_dont_have', - 'fun': 'test.ping', - }] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - ) + low = [ + {"client": "local_async", "tgt": "minion_we_dont_have", "fun": "test.ping"} + ] + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + ) response_obj = salt.utils.json.loads(response.body) - self.assertEqual(response_obj['return'], [{}]) + self.assertEqual(response_obj["return"], [{}]) - @skipIf(True, 'Undetermined race condition in test. Temporarily disabled.') + @skipIf(True, "Undetermined race condition in test. Temporarily disabled.") def test_simple_local_post_only_dictionary_request_with_order_masters(self): - ''' + """ Test a basic API of / - ''' - low = {'client': 'local', - 'tgt': '*', - 'fun': 'test.ping', - } + """ + low = { + "client": "local", + "tgt": "*", + "fun": "test.ping", + } - self.application.opts['order_masters'] = True - self.application.opts['syndic_wait'] = 5 + self.application.opts["order_masters"] = True + self.application.opts["syndic_wait"] = 5 - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - connect_timeout=30, - request_timeout=30, - ) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + connect_timeout=30, + request_timeout=30, + ) response_obj = salt.utils.json.loads(response.body) - self.application.opts['order_masters'] = [] - self.application.opts['syndic_wait'] = 5 + self.application.opts["order_masters"] = [] + self.application.opts["syndic_wait"] = 5 # If --proxy is set, it will cause an extra minion_id to be in the # response. Since there's not a great way to know if the test runner's # proxy minion is running, and we're not testing proxy minions here # anyway, just remove it from the response. - response_obj[0]['return'].pop('proxytest', None) - self.assertEqual(response_obj['return'], [{'minion': True, 'sub_minion': True}]) + response_obj[0]["return"].pop("proxytest", None) + self.assertEqual(response_obj["return"], [{"minion": True, "sub_minion": True}]) # runner tests def test_simple_local_runner_post(self): - low = [{'client': 'runner', - 'fun': 'manage.up', - }] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - connect_timeout=30, - request_timeout=300, - ) + low = [{"client": "runner", "fun": "manage.up"}] + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + connect_timeout=30, + request_timeout=300, + ) response_obj = salt.utils.json.loads(response.body) - self.assertEqual(len(response_obj['return']), 1) + self.assertEqual(len(response_obj["return"]), 1) try: # If --proxy is set, it will cause an extra minion_id to be in the # response. Since there's not a great way to know if the test # runner's proxy minion is running, and we're not testing proxy # minions here anyway, just remove it from the response. - response_obj['return'][0].remove('proxytest') + response_obj["return"][0].remove("proxytest") except ValueError: pass - self.assertEqual(sorted(response_obj['return'][0]), sorted(['minion', 'sub_minion'])) + self.assertEqual( + sorted(response_obj["return"][0]), sorted(["minion", "sub_minion"]) + ) # runner_async tests def test_simple_local_runner_async_post(self): - low = [{'client': 'runner_async', - 'fun': 'manage.up', - }] - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - connect_timeout=10, - request_timeout=10, - ) + low = [{"client": "runner_async", "fun": "manage.up"}] + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + connect_timeout=10, + request_timeout=10, + ) response_obj = salt.utils.json.loads(response.body) - self.assertIn('return', response_obj) - self.assertEqual(1, len(response_obj['return'])) - self.assertIn('jid', response_obj['return'][0]) - self.assertIn('tag', response_obj['return'][0]) + self.assertIn("return", response_obj) + self.assertEqual(1, len(response_obj["return"])) + self.assertIn("jid", response_obj["return"][0]) + self.assertIn("tag", response_obj["return"][0]) @flaky -@skipIf(HAS_ZMQ_IOLOOP is False, 'PyZMQ version must be >= 14.0.1 to run these tests.') +@skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.") class TestMinionSaltAPIHandler(_SaltnadoIntegrationTestCase): def get_app(self): - urls = [(r"/minions/(.*)", saltnado.MinionSaltAPIHandler), - (r"/minions", saltnado.MinionSaltAPIHandler), - ] + urls = [ + (r"/minions/(.*)", saltnado.MinionSaltAPIHandler), + (r"/minions", saltnado.MinionSaltAPIHandler), + ] application = self.build_tornado_app(urls) application.event_listener = saltnado.EventListener({}, self.opts) return application - @skipIf(True, 'issue #34753') + @skipIf(True, "issue #34753") def test_get_no_mid(self): - response = self.fetch('/minions', - method='GET', - headers={saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - follow_redirects=False, - ) + response = self.fetch( + "/minions", + method="GET", + headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]}, + follow_redirects=False, + ) response_obj = salt.utils.json.loads(response.body) - self.assertEqual(len(response_obj['return']), 1) + self.assertEqual(len(response_obj["return"]), 1) # one per minion - self.assertEqual(len(response_obj['return'][0]), 2) + self.assertEqual(len(response_obj["return"][0]), 2) # check a single grain - for minion_id, grains in six.iteritems(response_obj['return'][0]): - self.assertEqual(minion_id, grains['id']) + for minion_id, grains in six.iteritems(response_obj["return"][0]): + self.assertEqual(minion_id, grains["id"]) - @skipIf(True, 'to be re-enabled when #23623 is merged') + @skipIf(True, "to be re-enabled when #23623 is merged") def test_get(self): - response = self.fetch('/minions/minion', - method='GET', - headers={saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - follow_redirects=False, - ) + response = self.fetch( + "/minions/minion", + method="GET", + headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]}, + follow_redirects=False, + ) response_obj = salt.utils.json.loads(response.body) - self.assertEqual(len(response_obj['return']), 1) - self.assertEqual(len(response_obj['return'][0]), 1) + self.assertEqual(len(response_obj["return"]), 1) + self.assertEqual(len(response_obj["return"][0]), 1) # check a single grain - self.assertEqual(response_obj['return'][0]['minion']['id'], 'minion') + self.assertEqual(response_obj["return"][0]["minion"]["id"], "minion") def test_post(self): - low = [{'tgt': '*minion', - 'fun': 'test.ping', - }] - response = self.fetch('/minions', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - ) + low = [{"tgt": "*minion", "fun": "test.ping"}] + response = self.fetch( + "/minions", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + ) response_obj = salt.utils.json.loads(response.body) - ret = response_obj['return'] - ret[0]['minions'] = sorted(ret[0]['minions']) + ret = response_obj["return"] + ret[0]["minions"] = sorted(ret[0]["minions"]) # TODO: verify pub function? Maybe look at how we test the publisher self.assertEqual(len(ret), 1) - self.assertIn('jid', ret[0]) - self.assertEqual(ret[0]['minions'], sorted(['minion', 'sub_minion'])) + self.assertIn("jid", ret[0]) + self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"])) def test_post_with_client(self): # get a token for this test - low = [{'client': 'local_async', - 'tgt': '*minion', - 'fun': 'test.ping', - }] - response = self.fetch('/minions', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - ) + low = [{"client": "local_async", "tgt": "*minion", "fun": "test.ping"}] + response = self.fetch( + "/minions", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + ) response_obj = salt.utils.json.loads(response.body) - ret = response_obj['return'] - ret[0]['minions'] = sorted(ret[0]['minions']) + ret = response_obj["return"] + ret[0]["minions"] = sorted(ret[0]["minions"]) # TODO: verify pub function? Maybe look at how we test the publisher self.assertEqual(len(ret), 1) - self.assertIn('jid', ret[0]) - self.assertEqual(ret[0]['minions'], sorted(['minion', 'sub_minion'])) + self.assertIn("jid", ret[0]) + self.assertEqual(ret[0]["minions"], sorted(["minion", "sub_minion"])) def test_post_with_incorrect_client(self): - ''' + """ The /minions endpoint is asynchronous only, so if you try something else make sure you get an error - ''' + """ # get a token for this test - low = [{'client': 'local', - 'tgt': '*', - 'fun': 'test.ping', - }] - response = self.fetch('/minions', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - ) + low = [{"client": "local", "tgt": "*", "fun": "test.ping"}] + response = self.fetch( + "/minions", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + ) self.assertEqual(response.code, 400) -@skipIf(HAS_ZMQ_IOLOOP is False, 'PyZMQ version must be >= 14.0.1 to run these tests.') +@skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.") class TestJobsSaltAPIHandler(_SaltnadoIntegrationTestCase): def get_app(self): - urls = [(r"/jobs/(.*)", saltnado.JobsSaltAPIHandler), - (r"/jobs", saltnado.JobsSaltAPIHandler), - ] + urls = [ + (r"/jobs/(.*)", saltnado.JobsSaltAPIHandler), + (r"/jobs", saltnado.JobsSaltAPIHandler), + ] application = self.build_tornado_app(urls) application.event_listener = saltnado.EventListener({}, self.opts) return application - @skipIf(True, 'to be re-enabled when #23623 is merged') + @skipIf(True, "to be re-enabled when #23623 is merged") def test_get(self): # test with no JID - self.http_client.fetch(self.get_url('/jobs'), - self.stop, - method='GET', - headers={saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - follow_redirects=False, - ) + self.http_client.fetch( + self.get_url("/jobs"), + self.stop, + method="GET", + headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]}, + follow_redirects=False, + ) response = self.wait(timeout=30) - response_obj = salt.utils.json.loads(response.body)['return'][0] + response_obj = salt.utils.json.loads(response.body)["return"][0] try: for jid, ret in six.iteritems(response_obj): - self.assertIn('Function', ret) - self.assertIn('Target', ret) - self.assertIn('Target-type', ret) - self.assertIn('User', ret) - self.assertIn('StartTime', ret) - self.assertIn('Arguments', ret) + self.assertIn("Function", ret) + self.assertIn("Target", ret) + self.assertIn("Target-type", ret) + self.assertIn("User", ret) + self.assertIn("StartTime", ret) + self.assertIn("Arguments", ret) except AttributeError as attribute_error: print(salt.utils.json.loads(response.body)) raise # test with a specific JID passed in jid = next(six.iterkeys(response_obj)) - self.http_client.fetch(self.get_url('/jobs/{0}'.format(jid)), - self.stop, - method='GET', - headers={saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - follow_redirects=False, - ) + self.http_client.fetch( + self.get_url("/jobs/{0}".format(jid)), + self.stop, + method="GET", + headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]}, + follow_redirects=False, + ) response = self.wait(timeout=30) - response_obj = salt.utils.json.loads(response.body)['return'][0] - self.assertIn('Function', response_obj) - self.assertIn('Target', response_obj) - self.assertIn('Target-type', response_obj) - self.assertIn('User', response_obj) - self.assertIn('StartTime', response_obj) - self.assertIn('Arguments', response_obj) - self.assertIn('Result', response_obj) + response_obj = salt.utils.json.loads(response.body)["return"][0] + self.assertIn("Function", response_obj) + self.assertIn("Target", response_obj) + self.assertIn("Target-type", response_obj) + self.assertIn("User", response_obj) + self.assertIn("StartTime", response_obj) + self.assertIn("Arguments", response_obj) + self.assertIn("Result", response_obj) # TODO: run all the same tests from the root handler, but for now since they are # the same code, we'll just sanity check -@skipIf(HAS_ZMQ_IOLOOP is False, 'PyZMQ version must be >= 14.0.1 to run these tests.') +@skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.") class TestRunSaltAPIHandler(_SaltnadoIntegrationTestCase): def get_app(self): - urls = [("/run", saltnado.RunSaltAPIHandler), - ] + urls = [ + ("/run", saltnado.RunSaltAPIHandler), + ] application = self.build_tornado_app(urls) application.event_listener = saltnado.EventListener({}, self.opts) return application - @skipIf(True, 'to be re-enabled when #23623 is merged') + @skipIf(True, "to be re-enabled when #23623 is merged") def test_get(self): - low = [{'client': 'local', - 'tgt': '*', - 'fun': 'test.ping', - }] - response = self.fetch('/run', - method='POST', - body=salt.utils.json.dumps(low), - headers={'Content-Type': self.content_type_map['json'], - saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - ) + low = [{"client": "local", "tgt": "*", "fun": "test.ping"}] + response = self.fetch( + "/run", + method="POST", + body=salt.utils.json.dumps(low), + headers={ + "Content-Type": self.content_type_map["json"], + saltnado.AUTH_TOKEN_HEADER: self.token["token"], + }, + ) response_obj = salt.utils.json.loads(response.body) - self.assertEqual(response_obj['return'], [{'minion': True, 'sub_minion': True}]) + self.assertEqual(response_obj["return"], [{"minion": True, "sub_minion": True}]) -@skipIf(HAS_ZMQ_IOLOOP is False, 'PyZMQ version must be >= 14.0.1 to run these tests.') +@skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.") class TestEventsSaltAPIHandler(_SaltnadoIntegrationTestCase): def get_app(self): - urls = [(r"/events", saltnado.EventsSaltAPIHandler), - ] + urls = [ + (r"/events", saltnado.EventsSaltAPIHandler), + ] application = self.build_tornado_app(urls) application.event_listener = saltnado.EventListener({}, self.opts) @@ -569,22 +609,22 @@ class TestEventsSaltAPIHandler(_SaltnadoIntegrationTestCase): def test_get(self): self.events_to_fire = 5 - response = self.fetch('/events', - headers={saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - streaming_callback=self.on_event, - ) + response = self.fetch( + "/events", + headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]}, + streaming_callback=self.on_event, + ) def _stop(self): self.stop() def on_event(self, event): if six.PY3: - event = event.decode('utf-8') + event = event.decode("utf-8") if self.events_to_fire > 0: - self.application.event_listener.event.fire_event({ - 'foo': 'bar', - 'baz': 'qux', - }, 'salt/netapi/test') + self.application.event_listener.event.fire_event( + {"foo": "bar", "baz": "qux"}, "salt/netapi/test" + ) self.events_to_fire -= 1 # once we've fired all the events, lets call it a day else: @@ -594,19 +634,19 @@ class TestEventsSaltAPIHandler(_SaltnadoIntegrationTestCase): event = event.strip() # if we got a retry, just continue - if event != 'retry: 400': + if event != "retry: 400": tag, data = event.splitlines() - self.assertTrue(tag.startswith('tag: ')) - self.assertTrue(data.startswith('data: ')) + self.assertTrue(tag.startswith("tag: ")) + self.assertTrue(data.startswith("data: ")) -@skipIf(HAS_ZMQ_IOLOOP is False, 'PyZMQ version must be >= 14.0.1 to run these tests.') +@skipIf(HAS_ZMQ_IOLOOP is False, "PyZMQ version must be >= 14.0.1 to run these tests.") class TestWebhookSaltAPIHandler(_SaltnadoIntegrationTestCase): - def get_app(self): - urls = [(r"/hook(/.*)?", saltnado.WebhookSaltAPIHandler), - ] + urls = [ + (r"/hook(/.*)?", saltnado.WebhookSaltAPIHandler), + ] application = self.build_tornado_app(urls) @@ -615,41 +655,48 @@ class TestWebhookSaltAPIHandler(_SaltnadoIntegrationTestCase): application.event_listener = saltnado.EventListener({}, self.opts) return application - @skipIf(True, 'Skipping until we can devote more resources to debugging this test.') + @skipIf(True, "Skipping until we can devote more resources to debugging this test.") def test_post(self): self._future_resolved = threading.Event() try: + def verify_event(future): - ''' + """ Notify the threading event that the future is resolved - ''' + """ self._future_resolved.set() - self._finished = False # TODO: remove after some cleanup of the event listener + self._finished = ( + False # TODO: remove after some cleanup of the event listener + ) # get an event future - future = self.application.event_listener.get_event(self, - tag='salt/netapi/hook', - callback=verify_event) + future = self.application.event_listener.get_event( + self, tag="salt/netapi/hook", callback=verify_event + ) # fire the event - response = self.fetch('/hook', - method='POST', - body='foo=bar', - headers={saltnado.AUTH_TOKEN_HEADER: self.token['token']}, - ) + response = self.fetch( + "/hook", + method="POST", + body="foo=bar", + headers={saltnado.AUTH_TOKEN_HEADER: self.token["token"]}, + ) response_obj = salt.utils.json.loads(response.body) - self.assertTrue(response_obj['success']) + self.assertTrue(response_obj["success"]) resolve_future_timeout = 60 self._future_resolved.wait(resolve_future_timeout) try: event = future.result() except Exception as exc: # pylint: disable=broad-except - self.fail('Failed to resolve future under {} secs: {}'.format(resolve_future_timeout, exc)) - self.assertEqual(event['tag'], 'salt/netapi/hook') - self.assertIn('headers', event['data']) + self.fail( + "Failed to resolve future under {} secs: {}".format( + resolve_future_timeout, exc + ) + ) + self.assertEqual(event["tag"], "salt/netapi/hook") + self.assertIn("headers", event["data"]) self.assertEqual( - event['data']['post'], - {'foo': salt.utils.stringutils.to_bytes('bar')} + event["data"]["post"], {"foo": salt.utils.stringutils.to_bytes("bar")} ) finally: self._future_resolved.clear() diff --git a/tests/integration/netapi/test_client.py b/tests/integration/netapi/test_client.py index 08030f31ecb..ac3abc2be3a 100644 --- a/tests/integration/netapi/test_client.py +++ b/tests/integration/netapi/test_client.py @@ -2,52 +2,52 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import time -# Import Salt Testing libs -from tests.support.paths import TMP_CONF_DIR, TMP -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch -from tests.support.case import SSHCase -from tests.support.helpers import ( - Webserver, - SaveRequestsPostHandler, - requires_sshd_server -) - # Import Salt libs import salt.config import salt.netapi - -from salt.exceptions import ( - EauthAuthenticationError +from salt.exceptions import EauthAuthenticationError +from tests.support.case import SSHCase +from tests.support.helpers import ( + SaveRequestsPostHandler, + Webserver, + requires_sshd_server, ) +from tests.support.mock import patch + +# Import Salt Testing libs +from tests.support.paths import TMP, TMP_CONF_DIR +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf log = logging.getLogger(__name__) class NetapiClientTest(TestCase): eauth_creds = { - 'username': 'saltdev_auto', - 'password': 'saltdev', - 'eauth': 'auto', + "username": "saltdev_auto", + "password": "saltdev", + "eauth": "auto", } def setUp(self): - ''' + """ Set up a NetapiClient instance - ''' - opts = salt.config.client_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master')) + """ + opts = salt.config.client_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master") + ) self.netapi = salt.netapi.NetapiClient(opts) def tearDown(self): del self.netapi def test_local(self): - low = {'client': 'local', 'tgt': '*', 'fun': 'test.ping', 'timeout': 300} + low = {"client": "local", "tgt": "*", "fun": "test.ping", "timeout": 300} low.update(self.eauth_creds) ret = self.netapi.run(low) @@ -55,106 +55,110 @@ class NetapiClientTest(TestCase): # response. Since there's not a great way to know if the test # runner's proxy minion is running, and we're not testing proxy # minions here anyway, just remove it from the response. - ret.pop('proxytest', None) - self.assertEqual(ret, {'minion': True, 'sub_minion': True}) + ret.pop("proxytest", None) + self.assertEqual(ret, {"minion": True, "sub_minion": True}) def test_local_batch(self): - low = {'client': 'local_batch', 'tgt': '*', 'fun': 'test.ping', 'timeout': 300} + low = {"client": "local_batch", "tgt": "*", "fun": "test.ping", "timeout": 300} low.update(self.eauth_creds) ret = self.netapi.run(low) rets = [] for _ret in ret: rets.append(_ret) - self.assertIn({'sub_minion': True}, rets) - self.assertIn({'minion': True}, rets) + self.assertIn({"sub_minion": True}, rets) + self.assertIn({"minion": True}, rets) def test_local_async(self): - low = {'client': 'local_async', 'tgt': '*', 'fun': 'test.ping'} + low = {"client": "local_async", "tgt": "*", "fun": "test.ping"} low.update(self.eauth_creds) ret = self.netapi.run(low) # Remove all the volatile values before doing the compare. - self.assertIn('jid', ret) - ret.pop('jid', None) - ret['minions'] = sorted(ret['minions']) + self.assertIn("jid", ret) + ret.pop("jid", None) + ret["minions"] = sorted(ret["minions"]) try: # If --proxy is set, it will cause an extra minion_id to be in the # response. Since there's not a great way to know if the test # runner's proxy minion is running, and we're not testing proxy # minions here anyway, just remove it from the response. - ret['minions'].remove('proxytest') + ret["minions"].remove("proxytest") except ValueError: pass - self.assertEqual(ret, {'minions': sorted(['minion', 'sub_minion'])}) + self.assertEqual(ret, {"minions": sorted(["minion", "sub_minion"])}) def test_local_unauthenticated(self): - low = {'client': 'local', 'tgt': '*', 'fun': 'test.ping'} + low = {"client": "local", "tgt": "*", "fun": "test.ping"} with self.assertRaises(EauthAuthenticationError) as excinfo: ret = self.netapi.run(low) def test_wheel(self): - low = {'client': 'wheel', 'fun': 'key.list_all'} + low = {"client": "wheel", "fun": "key.list_all"} low.update(self.eauth_creds) ret = self.netapi.run(low) # Remove all the volatile values before doing the compare. - self.assertIn('tag', ret) - ret.pop('tag') + self.assertIn("tag", ret) + ret.pop("tag") - data = ret.get('data', {}) - self.assertIn('jid', data) - data.pop('jid', None) + data = ret.get("data", {}) + self.assertIn("jid", data) + data.pop("jid", None) - self.assertIn('tag', data) - data.pop('tag', None) + self.assertIn("tag", data) + data.pop("tag", None) - ret.pop('_stamp', None) - data.pop('_stamp', None) + ret.pop("_stamp", None) + data.pop("_stamp", None) self.maxDiff = None - self.assertTrue(set(['master.pem', 'master.pub']).issubset(set(ret['data']['return']['local']))) + self.assertTrue( + set(["master.pem", "master.pub"]).issubset( + set(ret["data"]["return"]["local"]) + ) + ) def test_wheel_async(self): # Give this test a little breathing room time.sleep(3) - low = {'client': 'wheel_async', 'fun': 'key.list_all'} + low = {"client": "wheel_async", "fun": "key.list_all"} low.update(self.eauth_creds) ret = self.netapi.run(low) - self.assertIn('jid', ret) - self.assertIn('tag', ret) + self.assertIn("jid", ret) + self.assertIn("tag", ret) def test_wheel_unauthenticated(self): - low = {'client': 'wheel', 'tgt': '*', 'fun': 'test.ping'} + low = {"client": "wheel", "tgt": "*", "fun": "test.ping"} with self.assertRaises(EauthAuthenticationError) as excinfo: ret = self.netapi.run(low) - @skipIf(True, 'This is not testing anything. Skipping for now.') + @skipIf(True, "This is not testing anything. Skipping for now.") def test_runner(self): # TODO: fix race condition in init of event-- right now the event class # will finish init even if the underlying zmq socket hasn't connected yet # this is problematic for the runnerclient's master_call method if the # runner is quick - #low = {'client': 'runner', 'fun': 'cache.grains'} - low = {'client': 'runner', 'fun': 'test.sleep', 'arg': [2]} + # low = {'client': 'runner', 'fun': 'cache.grains'} + low = {"client": "runner", "fun": "test.sleep", "arg": [2]} low.update(self.eauth_creds) ret = self.netapi.run(low) - @skipIf(True, 'This is not testing anything. Skipping for now.') + @skipIf(True, "This is not testing anything. Skipping for now.") def test_runner_async(self): - low = {'client': 'runner', 'fun': 'cache.grains'} + low = {"client": "runner", "fun": "cache.grains"} low.update(self.eauth_creds) ret = self.netapi.run(low) def test_runner_unauthenticated(self): - low = {'client': 'runner', 'tgt': '*', 'fun': 'test.ping'} + low = {"client": "runner", "tgt": "*", "fun": "test.ping"} with self.assertRaises(EauthAuthenticationError) as excinfo: ret = self.netapi.run(low) @@ -163,23 +167,23 @@ class NetapiClientTest(TestCase): @requires_sshd_server class NetapiSSHClientTest(SSHCase): eauth_creds = { - 'username': 'saltdev_auto', - 'password': 'saltdev', - 'eauth': 'auto', + "username": "saltdev_auto", + "password": "saltdev", + "eauth": "auto", } def setUp(self): - ''' + """ Set up a NetapiClient instance - ''' - opts = salt.config.client_config(os.path.join(TMP_CONF_DIR, 'master')) + """ + opts = salt.config.client_config(os.path.join(TMP_CONF_DIR, "master")) self.netapi = salt.netapi.NetapiClient(opts) - self.priv_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test') + self.priv_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test") self.rosters = os.path.join(RUNTIME_VARS.TMP_CONF_DIR) # Initialize salt-ssh - self.run_function('test.ping') + self.run_function("test.ping") def tearDown(self): del self.netapi @@ -197,37 +201,36 @@ class NetapiSSHClientTest(SSHCase): del cls.post_webserver def test_ssh(self): - low = {'client': 'ssh', - 'tgt': 'localhost', - 'fun': 'test.ping', - 'ignore_host_keys': True, - 'roster_file': 'roster', - 'rosters': [self.rosters], - 'ssh_priv': self.priv_file} + low = { + "client": "ssh", + "tgt": "localhost", + "fun": "test.ping", + "ignore_host_keys": True, + "roster_file": "roster", + "rosters": [self.rosters], + "ssh_priv": self.priv_file, + } low.update(self.eauth_creds) ret = self.netapi.run(low) - self.assertIn('localhost', ret) - self.assertIn('return', ret['localhost']) - self.assertEqual(ret['localhost']['return'], True) - self.assertEqual(ret['localhost']['id'], 'localhost') - self.assertEqual(ret['localhost']['fun'], 'test.ping') + self.assertIn("localhost", ret) + self.assertIn("return", ret["localhost"]) + self.assertEqual(ret["localhost"]["return"], True) + self.assertEqual(ret["localhost"]["id"], "localhost") + self.assertEqual(ret["localhost"]["fun"], "test.ping") def test_ssh_unauthenticated(self): - low = {'client': 'ssh', 'tgt': 'localhost', 'fun': 'test.ping'} + low = {"client": "ssh", "tgt": "localhost", "fun": "test.ping"} with self.assertRaises(EauthAuthenticationError) as excinfo: ret = self.netapi.run(low) def test_ssh_unauthenticated_raw_shell_curl(self): - fun = '-o ProxyCommand curl {0}'.format(self.post_web_root) - low = {'client': 'ssh', - 'tgt': 'localhost', - 'fun': fun, - 'raw_shell': True} + fun = "-o ProxyCommand curl {0}".format(self.post_web_root) + low = {"client": "ssh", "tgt": "localhost", "fun": fun, "raw_shell": True} ret = None with self.assertRaises(EauthAuthenticationError) as excinfo: @@ -238,36 +241,29 @@ class NetapiSSHClientTest(SSHCase): def test_ssh_unauthenticated_raw_shell_touch(self): - badfile = os.path.join(TMP, 'badfile.txt') - fun = '-o ProxyCommand touch {0}'.format(badfile) - low = {'client': 'ssh', - 'tgt': 'localhost', - 'fun': fun, - 'raw_shell': True} + badfile = os.path.join(TMP, "badfile.txt") + fun = "-o ProxyCommand touch {0}".format(badfile) + low = {"client": "ssh", "tgt": "localhost", "fun": fun, "raw_shell": True} ret = None with self.assertRaises(EauthAuthenticationError) as excinfo: ret = self.netapi.run(low) self.assertEqual(ret, None) - self.assertFalse(os.path.exists('badfile.txt')) + self.assertFalse(os.path.exists("badfile.txt")) def test_ssh_authenticated_raw_shell_disabled(self): - badfile = os.path.join(TMP, 'badfile.txt') - fun = '-o ProxyCommand touch {0}'.format(badfile) - low = {'client': 'ssh', - 'tgt': 'localhost', - 'fun': fun, - 'raw_shell': True} + badfile = os.path.join(TMP, "badfile.txt") + fun = "-o ProxyCommand touch {0}".format(badfile) + low = {"client": "ssh", "tgt": "localhost", "fun": fun, "raw_shell": True} low.update(self.eauth_creds) ret = None - with patch.dict(self.netapi.opts, - {'netapi_allow_raw_shell': False}): + with patch.dict(self.netapi.opts, {"netapi_allow_raw_shell": False}): with self.assertRaises(EauthAuthenticationError) as excinfo: ret = self.netapi.run(low) self.assertEqual(ret, None) - self.assertFalse(os.path.exists('badfile.txt')) + self.assertFalse(os.path.exists("badfile.txt")) diff --git a/tests/integration/output/test_output.py b/tests/integration/output/test_output.py index 1c3342a0fa5..f02129444e5 100644 --- a/tests/integration/output/test_output.py +++ b/tests/integration/output/test_output.py @@ -1,114 +1,112 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Salt Libs from __future__ import absolute_import, print_function, unicode_literals + import os import traceback +# Import Salt libs +import salt.config +import salt.utils.yaml +from salt.ext import six +from salt.output import display_output + # Import Salt Testing Libs from tests.support.case import ShellCase from tests.support.mixins import RUNTIME_VARS -# Import Salt libs -import salt.config -import salt.utils.yaml -from salt.output import display_output -from salt.ext import six - class OutputReturnTest(ShellCase): - ''' + """ Integration tests to ensure outputters return their expected format. Tests against situations where the loader might not be returning the right outputter even though it was explicitly requested. - ''' + """ def test_output_json(self): - ''' + """ Tests the return of json-formatted data - ''' - ret = self.run_call('test.ping --out=json') - self.assertIn('{', ret) - self.assertIn('"local": true', ''.join(ret)) - self.assertIn('}', ''.join(ret)) + """ + ret = self.run_call("test.ping --out=json") + self.assertIn("{", ret) + self.assertIn('"local": true', "".join(ret)) + self.assertIn("}", "".join(ret)) def test_output_nested(self): - ''' + """ Tests the return of nested-formatted data - ''' - expected = ['local:', ' True'] - ret = self.run_call('test.ping --out=nested') + """ + expected = ["local:", " True"] + ret = self.run_call("test.ping --out=nested") self.assertEqual(ret, expected) def test_output_quiet(self): - ''' + """ Tests the return of an out=quiet query - ''' + """ expected = [] - ret = self.run_call('test.ping --out=quiet') + ret = self.run_call("test.ping --out=quiet") self.assertEqual(ret, expected) def test_output_pprint(self): - ''' + """ Tests the return of pprint-formatted data - ''' + """ expected = ["{u'local': True}"] if six.PY2 else ["{'local': True}"] - ret = self.run_call('test.ping --out=pprint') + ret = self.run_call("test.ping --out=pprint") self.assertEqual(ret, expected) def test_output_raw(self): - ''' + """ Tests the return of raw-formatted data - ''' + """ expected = ["{u'local': True}"] if six.PY2 else ["{'local': True}"] - ret = self.run_call('test.ping --out=raw') + ret = self.run_call("test.ping --out=raw") self.assertEqual(ret, expected) def test_output_txt(self): - ''' + """ Tests the return of txt-formatted data - ''' - expected = ['local: True'] - ret = self.run_call('test.ping --out=txt') + """ + expected = ["local: True"] + ret = self.run_call("test.ping --out=txt") self.assertEqual(ret, expected) def test_output_yaml(self): - ''' + """ Tests the return of yaml-formatted data - ''' - expected = ['local: true'] - ret = self.run_call('test.ping --out=yaml') + """ + expected = ["local: true"] + ret = self.run_call("test.ping --out=yaml") self.assertEqual(ret, expected) def test_output_yaml_namespaced_dict_wrapper(self): - ''' + """ Tests the ability to dump a NamespacedDictWrapper instance, as used in magic dunders like __grains__ and __pillar__ See https://github.com/saltstack/salt/issues/49269 - ''' - dumped_yaml = '\n'.join(self.run_call('grains.items --out=yaml')) + """ + dumped_yaml = "\n".join(self.run_call("grains.items --out=yaml")) loaded_yaml = salt.utils.yaml.safe_load(dumped_yaml) # We just want to check that the dumped YAML loades as a dict with a # single top-level key, we don't care about the real contents. assert isinstance(loaded_yaml, dict) - assert list(loaded_yaml) == ['local'] + assert list(loaded_yaml) == ["local"] def test_output_unicodebad(self): - ''' + """ Tests outputter reliability with utf8 - ''' - opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) - opts['output_file'] = os.path.join( - RUNTIME_VARS.TMP, - 'outputtest' + """ + opts = salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion") ) - data = {'foo': {'result': False, - 'aaa': 'azerzaeréééé', - 'comment': u'ééééàààà'}} + opts["output_file"] = os.path.join(RUNTIME_VARS.TMP, "outputtest") + data = {"foo": {"result": False, "aaa": "azerzaeréééé", "comment": "ééééàààà"}} try: # this should not raises UnicodeEncodeError display_output(data, opts=opts) @@ -116,28 +114,37 @@ class OutputReturnTest(ShellCase): # display trace in error message for debugging on jenkins trace = traceback.format_exc() sentinel = object() - old_max_diff = getattr(self, 'maxDiff', sentinel) + old_max_diff = getattr(self, "maxDiff", sentinel) try: self.maxDiff = None - self.assertEqual(trace, '') + self.assertEqual(trace, "") finally: if old_max_diff is sentinel: - delattr(self, 'maxDiff') + delattr(self, "maxDiff") else: self.maxDiff = old_max_diff def test_output_highstate(self): - ''' + """ Regression tests for the highstate outputter. Calls a basic state with various flags. Each comparison should be identical when successful. - ''' + """ # Test basic highstate output. No frills. - expected = ['minion:', ' ID: simple-ping', ' Function: module.run', - ' Name: test.ping', ' Result: True', - ' Comment: Module function test.ping executed', - ' Changes: ', ' ret:', ' True', - 'Summary for minion', 'Succeeded: 1 (changed=1)', 'Failed: 0', - 'Total states run: 1'] + expected = [ + "minion:", + " ID: simple-ping", + " Function: module.run", + " Name: test.ping", + " Result: True", + " Comment: Module function test.ping executed", + " Changes: ", + " ret:", + " True", + "Summary for minion", + "Succeeded: 1 (changed=1)", + "Failed: 0", + "Total states run: 1", + ] state_run = self.run_salt('"minion" state.sls simple-ping') for expected_item in expected: @@ -159,25 +166,27 @@ class OutputReturnTest(ShellCase): # Test highstate output when passing --static and --out=highstate. # See Issue #44556. - state_run = self.run_salt('"minion" state.sls simple-ping --static --out=highstate') + state_run = self.run_salt( + '"minion" state.sls simple-ping --static --out=highstate' + ) for expected_item in expected: self.assertIn(expected_item, state_run) def test_output_highstate_falls_back_nested(self): - ''' + """ Tests outputter when passing --out=highstate with a non-state call. This should fall back to "nested" output. - ''' - expected = ['minion:', ' True'] + """ + expected = ["minion:", " True"] ret = self.run_salt('"minion" test.ping --out=highstate') self.assertEqual(ret, expected) def test_static_simple(self): - ''' + """ Tests passing the --static option with a basic test.ping command. This should be the "nested" output. - ''' - expected = ['minion:', ' True'] + """ + expected = ["minion:", " True"] ret = self.run_salt('"minion" test.ping --static') self.assertEqual(ret, expected) diff --git a/tests/integration/pillar/test_git_pillar.py b/tests/integration/pillar/test_git_pillar.py index 2e549f3948e..8da514f23dd 100644 --- a/tests/integration/pillar/test_git_pillar.py +++ b/tests/integration/pillar/test_git_pillar.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for git_pillar The base classes for all of these tests are in tests/support/gitfs.py. @@ -62,41 +62,42 @@ workaround can be found in the below two links: https://github.com/git/git/commit/6bc0cb5 https://github.com/unbit/uwsgi/commit/ac1e354 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import random import string -# Import Salt Testing libs -from tests.support.gitfs import ( - USERNAME, - PASSWORD, - GitPillarSSHTestBase, - GitPillarHTTPTestBase, -) -from tests.support.helpers import ( - destructiveTest, - requires_system_grains, - skip_if_not_root -) -from tests.support.unit import skipIf - # Import Salt libs import salt.utils.path import salt.utils.platform -from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES as VIRTUALENV_NAMES from salt.ext.six.moves import range # pylint: disable=redefined-builtin +from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES as VIRTUALENV_NAMES from salt.utils.gitfs import ( - GITPYTHON_VERSION, GITPYTHON_MINVER, - PYGIT2_VERSION, - PYGIT2_MINVER, + GITPYTHON_VERSION, + LIBGIT2_MINVER, LIBGIT2_VERSION, - LIBGIT2_MINVER + PYGIT2_MINVER, + PYGIT2_VERSION, ) +# Import Salt Testing libs +from tests.support.gitfs import ( + PASSWORD, + USERNAME, + GitPillarHTTPTestBase, + GitPillarSSHTestBase, +) +from tests.support.helpers import ( + destructiveTest, + requires_system_grains, + skip_if_not_root, +) +from tests.support.unit import skipIf + # Check for requisite components try: HAS_GITPYTHON = GITPYTHON_VERSION >= GITPYTHON_MINVER @@ -104,19 +105,18 @@ except Exception: # pylint: disable=broad-except HAS_GITPYTHON = False try: - HAS_PYGIT2 = PYGIT2_VERSION >= PYGIT2_MINVER \ - and LIBGIT2_VERSION >= LIBGIT2_MINVER + HAS_PYGIT2 = PYGIT2_VERSION >= PYGIT2_MINVER and LIBGIT2_VERSION >= LIBGIT2_MINVER except Exception: # pylint: disable=broad-except HAS_PYGIT2 = False -HAS_SSHD = bool(salt.utils.path.which('sshd')) -HAS_NGINX = bool(salt.utils.path.which('nginx')) +HAS_SSHD = bool(salt.utils.path.which("sshd")) +HAS_NGINX = bool(salt.utils.path.which("nginx")) HAS_VIRTUALENV = bool(salt.utils.path.which_bin(VIRTUALENV_NAMES)) def _rand_key_name(length): - return 'id_rsa_{0}'.format( - ''.join(random.choice(string.ascii_letters) for _ in range(length)) + return "id_rsa_{0}".format( + "".join(random.choice(string.ascii_letters) for _ in range(length)) ) @@ -125,15 +125,17 @@ def _windows_or_mac(): class GitPythonMixin(object): - ''' + """ GitPython doesn't support anything fancy in terms of authentication options, so all of the tests for GitPython can be re-used via this mixin. - ''' + """ + def test_single_source(self): - ''' + """ Test using a single ext_pillar repo - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -142,25 +144,31 @@ class GitPythonMixin(object): ext_pillar: - git: - master {url} - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}} + { + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + }, ) def test_multiple_sources_master_dev_no_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the master branch followed by dev, and with pillar_merge_lists disabled. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -171,26 +179,32 @@ class GitPythonMixin(object): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual( ret, - {'branch': 'dev', - 'mylist': ['dev'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['dev'], - 'nested_dict': {'master': True, 'dev': True}}} + { + "branch": "dev", + "mylist": ["dev"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"master": True, "dev": True}, + }, + }, ) def test_multiple_sources_dev_master_no_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the dev branch followed by master, and with pillar_merge_lists disabled. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -201,26 +215,32 @@ class GitPythonMixin(object): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True, 'dev': True}}} + { + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["master"], + "nested_dict": {"master": True, "dev": True}, + }, + }, ) def test_multiple_sources_master_dev_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the master branch followed by dev, and with pillar_merge_lists enabled. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -231,26 +251,32 @@ class GitPythonMixin(object): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual( ret, - {'branch': 'dev', - 'mylist': ['master', 'dev'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['master', 'dev'], - 'nested_dict': {'master': True, 'dev': True}}} + { + "branch": "dev", + "mylist": ["master", "dev"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["master", "dev"], + "nested_dict": {"master": True, "dev": True}, + }, + }, ) def test_multiple_sources_dev_master_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the dev branch followed by master, and with pillar_merge_lists enabled. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -261,22 +287,28 @@ class GitPythonMixin(object): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'mylist': ['dev', 'master'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['dev', 'master'], - 'nested_dict': {'master': True, 'dev': True}}} + { + "branch": "master", + "mylist": ["dev", "master"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["dev", "master"], + "nested_dict": {"master": True, "dev": True}, + }, + }, ) def test_multiple_sources_with_pillarenv(self): - ''' + """ Test using pillarenv to restrict results to those from a single branch - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -287,23 +319,29 @@ class GitPythonMixin(object): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}} + { + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + }, ) def test_includes_enabled(self): - ''' + """ Test with git_pillar_includes enabled. The top_only branch references an SLS file from the master branch, so we should see the key from that SLS file (included_pillar) in the compiled pillar data. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -314,26 +352,32 @@ class GitPythonMixin(object): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}, - 'included_pillar': True} + { + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + "included_pillar": True, + }, ) def test_includes_disabled(self): - ''' + """ Test with git_pillar_includes enabled. The top_only branch references an SLS file from the master branch, but since includes are disabled it will not find the SLS file and the "included_pillar" key should not be present in the compiled pillar data. We should instead see an error message in the compiled data. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -345,27 +389,35 @@ class GitPythonMixin(object): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}, - '_errors': ["Specified SLS 'bar' in environment 'base' is not " - "available on the salt master"]} + { + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + "_errors": [ + "Specified SLS 'bar' in environment 'base' is not " + "available on the salt master" + ], + }, ) def test_includes_enabled_solves___env___with_mountpoint(self): - ''' + """ Test with git_pillar_includes enabled and using "__env__" as the branch name for the configured repositories. The "gitinfo" repository contains top.sls file with a local reference and also referencing external "nowhere.foo" which is provided by "webinfo" repository mounted as "nowhere". - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -378,26 +430,30 @@ class GitPythonMixin(object): - __env__ {url}: - name: webinfo - mountpoint: nowhere - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'motd': 'The force will be with you. Always.', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}} + { + "branch": "master", + "motd": "The force will be with you. Always.", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + }, ) def test_root_parameter(self): - ''' + """ Test root parameter - ''' - expected = { - 'from_subdir': True - } + """ + expected = {"from_subdir": True} - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -409,18 +465,18 @@ class GitPythonMixin(object): - root: subdir - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_mountpoint_parameter(self): - ''' + """ Test mountpoint parameter - ''' - expected = { - 'included_pillar': True - } + """ + expected = {"included_pillar": True} - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -432,18 +488,18 @@ class GitPythonMixin(object): - mountpoint: mounted - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_root_and_mountpoint_parameters(self): - ''' + """ Test root and mountpoint parameters - ''' - expected = { - 'from_subdir': True - } + """ + expected = {"from_subdir": True} - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -456,14 +512,16 @@ class GitPythonMixin(object): - root: subdir - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_all_saltenvs(self): - ''' + """ Test all_saltenvs parameter. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -476,22 +534,28 @@ class GitPythonMixin(object): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual( ret, - {'branch': 'dev', - 'motd': 'The force will be with you. Always.', - 'mylist': ['dev'], - 'mydict': {'dev': True, - 'nested_list': ['dev'], - 'nested_dict': {'dev': True}}} + { + "branch": "dev", + "motd": "The force will be with you. Always.", + "mylist": ["dev"], + "mydict": { + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"dev": True}, + }, + }, ) def test_all_saltenvs_base(self): - ''' + """ Test all_saltenvs parameter with base pillarenv. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: gitpython @@ -503,90 +567,134 @@ class GitPythonMixin(object): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'motd': 'The force will be with you. Always.', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}} + { + "branch": "master", + "motd": "The force will be with you. Always.", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + }, + ) + + def test_fallback(self): + """ + Test fallback parameter. + """ + ret = self.get_pillar( + """\ + file_ignore_regex: [] + file_ignore_glob: [] + git_pillar_provider: gitpython + cachedir: {cachedir} + extension_modules: {extmods} + pillarenv: nonexisting + ext_pillar: + - git: + - __env__ {url_extra_repo}: + - fallback: master + - __env__ {url}: + - mountpoint: nowhere + - fallback: dev + """ + ) + self.assertEqual( + ret, + { + "branch": "dev", + "motd": "The force will be with you. Always.", + "mylist": ["dev"], + "mydict": { + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"dev": True}, + }, + }, ) @destructiveTest -@skipIf(_windows_or_mac(), 'minion is windows or mac') +@skipIf(_windows_or_mac(), "minion is windows or mac") @skip_if_not_root -@skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required'.format(GITPYTHON_MINVER)) -@skipIf(not HAS_SSHD, 'sshd not present') +@skipIf(not HAS_GITPYTHON, "GitPython >= {0} required".format(GITPYTHON_MINVER)) +@skipIf(not HAS_SSHD, "sshd not present") class TestGitPythonSSH(GitPillarSSHTestBase, GitPythonMixin): - ''' + """ Test git_pillar with GitPython using SSH authentication - ''' + """ + id_rsa_nopass = _rand_key_name(8) id_rsa_withpass = _rand_key_name(8) username = USERNAME passphrase = PASSWORD -@skipIf(_windows_or_mac(), 'minion is windows or mac') +@skipIf(_windows_or_mac(), "minion is windows or mac") @skip_if_not_root -@skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required'.format(GITPYTHON_MINVER)) -@skipIf(not HAS_NGINX, 'nginx not present') -@skipIf(not HAS_VIRTUALENV, 'virtualenv not present') +@skipIf(not HAS_GITPYTHON, "GitPython >= {0} required".format(GITPYTHON_MINVER)) +@skipIf(not HAS_NGINX, "nginx not present") +@skipIf(not HAS_VIRTUALENV, "virtualenv not present") class TestGitPythonHTTP(GitPillarHTTPTestBase, GitPythonMixin): - ''' + """ Test git_pillar with GitPython using unauthenticated HTTP - ''' + """ -@skipIf(_windows_or_mac(), 'minion is windows or mac') +@skipIf(_windows_or_mac(), "minion is windows or mac") @skip_if_not_root -@skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required'.format(GITPYTHON_MINVER)) -@skipIf(not HAS_NGINX, 'nginx not present') -@skipIf(not HAS_VIRTUALENV, 'virtualenv not present') +@skipIf(not HAS_GITPYTHON, "GitPython >= {0} required".format(GITPYTHON_MINVER)) +@skipIf(not HAS_NGINX, "nginx not present") +@skipIf(not HAS_VIRTUALENV, "virtualenv not present") class TestGitPythonAuthenticatedHTTP(TestGitPythonHTTP, GitPythonMixin): - ''' + """ Test git_pillar with GitPython using authenticated HTTP - ''' + """ + username = USERNAME password = PASSWORD @classmethod def setUpClass(cls): - ''' + """ Create start the webserver - ''' + """ super(TestGitPythonAuthenticatedHTTP, cls).setUpClass() # Override the URL set up in the parent class to encode the # username/password into it. - cls.url = 'http://{username}:{password}@127.0.0.1:{port}/repo.git'.format( - username=cls.username, - password=cls.password, - port=cls.nginx_port) - cls.url_extra_repo = 'http://{username}:{password}@127.0.0.1:{port}/extra_repo.git'.format( - username=cls.username, - password=cls.password, - port=cls.nginx_port) - cls.ext_opts['url'] = cls.url - cls.ext_opts['url_extra_repo'] = cls.url_extra_repo - cls.ext_opts['username'] = cls.username - cls.ext_opts['password'] = cls.password + cls.url = "http://{username}:{password}@127.0.0.1:{port}/repo.git".format( + username=cls.username, password=cls.password, port=cls.nginx_port + ) + cls.url_extra_repo = "http://{username}:{password}@127.0.0.1:{port}/extra_repo.git".format( + username=cls.username, password=cls.password, port=cls.nginx_port + ) + cls.ext_opts["url"] = cls.url + cls.ext_opts["url_extra_repo"] = cls.url_extra_repo + cls.ext_opts["username"] = cls.username + cls.ext_opts["password"] = cls.password @destructiveTest -@skipIf(_windows_or_mac(), 'minion is windows or mac') +@skipIf(_windows_or_mac(), "minion is windows or mac") @skip_if_not_root -@skipIf(not HAS_PYGIT2, 'pygit2 >= {0} and libgit2 >= {1} required'.format(PYGIT2_MINVER, LIBGIT2_MINVER)) -@skipIf(not HAS_SSHD, 'sshd not present') +@skipIf( + not HAS_PYGIT2, + "pygit2 >= {0} and libgit2 >= {1} required".format(PYGIT2_MINVER, LIBGIT2_MINVER), +) +@skipIf(not HAS_SSHD, "sshd not present") class TestPygit2SSH(GitPillarSSHTestBase): - ''' + """ Test git_pillar with pygit2 using SSH authentication NOTE: Any tests added to this test class should have equivalent tests (if possible) in the TestGitPythonSSH class. - ''' + """ + id_rsa_nopass = _rand_key_name(8) id_rsa_withpass = _rand_key_name(8) username = USERNAME @@ -594,19 +702,22 @@ class TestPygit2SSH(GitPillarSSHTestBase): @requires_system_grains def test_single_source(self, grains): - ''' + """ Test using a single ext_pillar repo - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}} + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -617,11 +728,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): ext_pillar: - git: - master {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -632,15 +745,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - master {url}: - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -652,11 +767,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): ext_pillar: - git: - master {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -668,29 +785,33 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_withpass} - privkey: {privkey_withpass} - passphrase: {passphrase} - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_multiple_sources_master_dev_no_merge_lists(self, grains): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the master branch followed by dev, and with pillar_merge_lists disabled. - ''' + """ expected = { - 'branch': 'dev', - 'mylist': ['dev'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['dev'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "dev", + "mylist": ["dev"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"master": True, "dev": True}, + }, } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -703,11 +824,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -722,15 +845,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - dev {url}: - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -744,11 +869,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -765,29 +892,33 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_withpass} - privkey: {privkey_withpass} - passphrase: {passphrase} - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_multiple_sources_dev_master_no_merge_lists(self, grains): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the dev branch followed by master, and with pillar_merge_lists disabled. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["master"], + "nested_dict": {"master": True, "dev": True}, + }, } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -800,11 +931,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -819,15 +952,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - master {url}: - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -841,11 +976,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -862,29 +999,33 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_withpass} - privkey: {privkey_withpass} - passphrase: {passphrase} - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_multiple_sources_master_dev_merge_lists(self, grains): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the master branch followed by dev, and with pillar_merge_lists enabled. - ''' + """ expected = { - 'branch': 'dev', - 'mylist': ['master', 'dev'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['master', 'dev'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "dev", + "mylist": ["master", "dev"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["master", "dev"], + "nested_dict": {"master": True, "dev": True}, + }, } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -897,11 +1038,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -916,15 +1059,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - dev {url}: - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -938,11 +1083,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -959,29 +1106,33 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_withpass} - privkey: {privkey_withpass} - passphrase: {passphrase} - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_multiple_sources_dev_master_merge_lists(self, grains): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the dev branch followed by master, and with pillar_merge_lists enabled. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['dev', 'master'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['dev', 'master'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "master", + "mylist": ["dev", "master"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["dev", "master"], + "nested_dict": {"master": True, "dev": True}, + }, } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -994,11 +1145,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1013,15 +1166,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - master {url}: - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1035,11 +1190,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1056,24 +1213,28 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_withpass} - privkey: {privkey_withpass} - passphrase: {passphrase} - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_multiple_sources_with_pillarenv(self, grains): - ''' + """ Test using pillarenv to restrict results to those from a single branch - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}} + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1086,11 +1247,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1105,15 +1268,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - dev {url}: - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1127,11 +1292,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1148,27 +1315,31 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - passphrase: {passphrase} - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_includes_enabled(self, grains): - ''' + """ Test with git_pillar_includes enabled. The top_only branch references an SLS file from the master branch, so we should see the "included_pillar" key from that SLS file in the compiled pillar data. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}, - 'included_pillar': True + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + "included_pillar": True, } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1181,11 +1352,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1200,15 +1373,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - env: base - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1222,11 +1397,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1243,30 +1420,36 @@ class TestPygit2SSH(GitPillarSSHTestBase): - privkey: {privkey_withpass} - passphrase: {passphrase} - env: base - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_includes_disabled(self, grains): - ''' + """ Test with git_pillar_includes enabled. The top_only branch references an SLS file from the master branch, but since includes are disabled it will not find the SLS file and the "included_pillar" key should not be present in the compiled pillar data. We should instead see an error message in the compiled data. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}, - '_errors': ["Specified SLS 'bar' in environment 'base' is not " - "available on the salt master"] + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + "_errors": [ + "Specified SLS 'bar' in environment 'base' is not " + "available on the salt master" + ], } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1280,11 +1463,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1300,15 +1485,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - env: base - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1323,11 +1510,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1345,18 +1534,20 @@ class TestPygit2SSH(GitPillarSSHTestBase): - privkey: {privkey_withpass} - passphrase: {passphrase} - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_includes_enabled_solves___env___with_mountpoint(self): - ''' + """ Test with git_pillar_includes enabled and using "__env__" as the branch name for the configured repositories. The "gitinfo" repository contains top.sls file with a local reference and also referencing external "nowhere.foo" which is provided by "webinfo" repository mounted as "nowhere". - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1371,28 +1562,32 @@ class TestPygit2SSH(GitPillarSSHTestBase): - __env__ {url}: - name: webinfo - mountpoint: nowhere - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'motd': 'The force will be with you. Always.', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}} + { + "branch": "master", + "motd": "The force will be with you. Always.", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + }, ) @requires_system_grains def test_root_parameter(self, grains): - ''' + """ Test root parameter - ''' - expected = { - 'from_subdir': True - } + """ + expected = {"from_subdir": True} # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1406,11 +1601,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - root: subdir - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1426,15 +1623,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - env: base - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1449,11 +1648,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - root: subdir - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1471,20 +1672,20 @@ class TestPygit2SSH(GitPillarSSHTestBase): - privkey: {privkey_withpass} - passphrase: {passphrase} - env: base - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_mountpoint_parameter(self, grains): - ''' + """ Test mountpoint parameter - ''' - expected = { - 'included_pillar': True - } + """ + expected = {"included_pillar": True} # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1498,11 +1699,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - mountpoint: mounted - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1518,15 +1721,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - env: base - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1541,11 +1746,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - mountpoint: mounted - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1563,20 +1770,20 @@ class TestPygit2SSH(GitPillarSSHTestBase): - privkey: {privkey_withpass} - passphrase: {passphrase} - env: base - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_root_and_mountpoint_parameters(self, grains): - ''' + """ Test root and mountpoint parameters - ''' - expected = { - 'from_subdir': True - } + """ + expected = {"from_subdir": True} # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1591,11 +1798,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - root: subdir - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1612,15 +1821,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - env: base - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1636,11 +1847,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - root: subdir - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1659,25 +1872,29 @@ class TestPygit2SSH(GitPillarSSHTestBase): - privkey: {privkey_withpass} - passphrase: {passphrase} - env: base - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_all_saltenvs(self, grains): - ''' + """ Test all_saltenvs parameter. - ''' - expected = {'branch': 'dev', - 'motd': 'The force will be with you. Always.', - 'mylist': ['dev'], - 'mydict': {'dev': True, - 'nested_list': ['dev'], - 'nested_dict': {'dev': True} - } + """ + expected = { + "branch": "dev", + "motd": "The force will be with you. Always.", + "mylist": ["dev"], + "mydict": { + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"dev": True}, + }, } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1692,11 +1909,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1713,15 +1932,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - mountpoint: nowhere - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1737,11 +1958,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1760,25 +1983,29 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - passphrase: {passphrase} - ''') + """ + ) self.assertEqual(ret, expected) @requires_system_grains def test_all_saltenvs_base(self, grains): - ''' + """ Test all_saltenvs parameter. - ''' - expected = {'branch': 'master', - 'motd': 'The force will be with you. Always.', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True} - } + """ + expected = { + "branch": "master", + "motd": "The force will be with you. Always.", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, } # Test with passphraseless key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1792,11 +2019,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphraseless key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1812,15 +2041,17 @@ class TestPygit2SSH(GitPillarSSHTestBase): - mountpoint: nowhere - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - ''') + """ + ) self.assertEqual(ret, expected) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": # passphrase-protected currently does not work here return # Test with passphrase-protected key and global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1835,11 +2066,13 @@ class TestPygit2SSH(GitPillarSSHTestBase): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual(ret, expected) # Test with passphrase-protected key and per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1857,32 +2090,155 @@ class TestPygit2SSH(GitPillarSSHTestBase): - pubkey: {pubkey_nopass} - privkey: {privkey_nopass} - passphrase: {passphrase} - ''') + """ + ) + self.assertEqual(ret, expected) + + @requires_system_grains + def test_fallback(self, grains): + """ + Test fallback parameter. + """ + expected = { + "branch": "dev", + "motd": "The force will be with you. Always.", + "mylist": ["dev"], + "mydict": { + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"dev": True}, + }, + } + + # Test with passphraseless key and global credential options + ret = self.get_pillar( + """\ + file_ignore_regex: [] + file_ignore_glob: [] + git_pillar_provider: pygit2 + git_pillar_pubkey: {pubkey_nopass} + git_pillar_privkey: {privkey_nopass} + cachedir: {cachedir} + extension_modules: {extmods} + pillarenv: nonexisting + ext_pillar: + - git: + - __env__ {url_extra_repo}: + - fallback: master + - __env__ {url}: + - mountpoint: nowhere + - fallback: dev + """ + ) + self.assertEqual(ret, expected) + + # Test with passphraseless key and per-repo credential options + ret = self.get_pillar( + """\ + file_ignore_regex: [] + file_ignore_glob: [] + git_pillar_provider: pygit2 + cachedir: {cachedir} + extension_modules: {extmods} + pillarenv: nonexisting + ext_pillar: + - git: + - __env__ {url_extra_repo}: + - fallback: master + - pubkey: {pubkey_nopass} + - privkey: {privkey_nopass} + - __env__ {url}: + - mountpoint: nowhere + - fallback: dev + - pubkey: {pubkey_nopass} + - privkey: {privkey_nopass} + """ + ) + self.assertEqual(ret, expected) + + if grains["os_family"] == "Debian": + # passphrase-protected currently does not work here + return + + # Test with passphrase-protected key and global credential options + ret = self.get_pillar( + """\ + file_ignore_regex: [] + file_ignore_glob: [] + git_pillar_provider: pygit2 + git_pillar_pubkey: {pubkey_withpass} + git_pillar_privkey: {privkey_withpass} + git_pillar_passphrase: {passphrase} + cachedir: {cachedir} + extension_modules: {extmods} + pillarenv: nonexisting + ext_pillar: + - git: + - __env__ {url_extra_repo}: + - fallback: master + - __env__ {url}: + - mountpoint: nowhere + - fallback: dev + """ + ) + self.assertEqual(ret, expected) + + # Test with passphrase-protected key and per-repo credential options + ret = self.get_pillar( + """\ + file_ignore_regex: [] + file_ignore_glob: [] + git_pillar_provider: pygit2 + cachedir: {cachedir} + extension_modules: {extmods} + pillarenv: nonexisting + ext_pillar: + - git: + - __env__ {url_extra_repo}: + - fallback: master + - pubkey: {pubkey_nopass} + - privkey: {privkey_nopass} + - passphrase: {passphrase} + - __env__ {url}: + - mountpoint: nowhere + - fallback: dev + - pubkey: {pubkey_nopass} + - privkey: {privkey_nopass} + - passphrase: {passphrase} + """ + ) self.assertEqual(ret, expected) -@skipIf(_windows_or_mac(), 'minion is windows or mac') +@skipIf(_windows_or_mac(), "minion is windows or mac") @skip_if_not_root -@skipIf(not HAS_PYGIT2, 'pygit2 >= {0} and libgit2 >= {1} required'.format(PYGIT2_MINVER, LIBGIT2_MINVER)) -@skipIf(not HAS_NGINX, 'nginx not present') -@skipIf(not HAS_VIRTUALENV, 'virtualenv not present') +@skipIf( + not HAS_PYGIT2, + "pygit2 >= {0} and libgit2 >= {1} required".format(PYGIT2_MINVER, LIBGIT2_MINVER), +) +@skipIf(not HAS_NGINX, "nginx not present") +@skipIf(not HAS_VIRTUALENV, "virtualenv not present") class TestPygit2HTTP(GitPillarHTTPTestBase): - ''' + """ Test git_pillar with pygit2 using SSH authentication - ''' + """ + def test_single_source(self): - ''' + """ Test using a single ext_pillar repo - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}} + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, } - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1891,27 +2247,31 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): ext_pillar: - git: - master {url} - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_master_dev_no_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the master branch followed by dev, and with pillar_merge_lists disabled. - ''' + """ expected = { - 'branch': 'dev', - 'mylist': ['dev'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['dev'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "dev", + "mylist": ["dev"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"master": True, "dev": True}, + }, } - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1922,27 +2282,31 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_dev_master_no_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the dev branch followed by master, and with pillar_merge_lists disabled. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["master"], + "nested_dict": {"master": True, "dev": True}, + }, } - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1953,27 +2317,31 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_master_dev_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the master branch followed by dev, and with pillar_merge_lists enabled. - ''' + """ expected = { - 'branch': 'dev', - 'mylist': ['master', 'dev'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['master', 'dev'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "dev", + "mylist": ["master", "dev"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["master", "dev"], + "nested_dict": {"master": True, "dev": True}, + }, } - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -1984,27 +2352,31 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_dev_master_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the dev branch followed by master, and with pillar_merge_lists enabled. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['dev', 'master'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['dev', 'master'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "master", + "mylist": ["dev", "master"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["dev", "master"], + "nested_dict": {"master": True, "dev": True}, + }, } - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2015,22 +2387,26 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_with_pillarenv(self): - ''' + """ Test using pillarenv to restrict results to those from a single branch - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}} + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, } - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2041,25 +2417,29 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) def test_includes_enabled(self): - ''' + """ Test with git_pillar_includes enabled. The top_only branch references an SLS file from the master branch, so we should see the "included_pillar" key from that SLS file in the compiled pillar data. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}, - 'included_pillar': True + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + "included_pillar": True, } - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2070,28 +2450,34 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_includes_disabled(self): - ''' + """ Test with git_pillar_includes enabled. The top_only branch references an SLS file from the master branch, but since includes are disabled it will not find the SLS file and the "included_pillar" key should not be present in the compiled pillar data. We should instead see an error message in the compiled data. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}, - '_errors': ["Specified SLS 'bar' in environment 'base' is not " - "available on the salt master"] + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + "_errors": [ + "Specified SLS 'bar' in environment 'base' is not " + "available on the salt master" + ], } - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2103,18 +2489,20 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_includes_enabled_solves___env___with_mountpoint(self): - ''' + """ Test with git_pillar_includes enabled and using "__env__" as the branch name for the configured repositories. The "gitinfo" repository contains top.sls file with a local reference and also referencing external "nowhere.foo" which is provided by "webinfo" repository mounted as "nowhere". - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2127,26 +2515,30 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - __env__ {url}: - name: webinfo - mountpoint: nowhere - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'motd': 'The force will be with you. Always.', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}} + { + "branch": "master", + "motd": "The force will be with you. Always.", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + }, ) def test_root_parameter(self): - ''' + """ Test root parameter - ''' - expected = { - 'from_subdir': True - } + """ + expected = {"from_subdir": True} - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2158,18 +2550,18 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - root: subdir - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_mountpoint_parameter(self): - ''' + """ Test mountpoint parameter - ''' - expected = { - 'included_pillar': True - } + """ + expected = {"included_pillar": True} - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2181,18 +2573,18 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - mountpoint: mounted - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_root_and_mountpoint_parameters(self): - ''' + """ Test root and mountpoint parameters - ''' - expected = { - 'from_subdir': True - } + """ + expected = {"from_subdir": True} - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2205,14 +2597,16 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - root: subdir - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_all_saltenvs(self): - ''' + """ Test all_saltenvs parameter. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2225,22 +2619,28 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual( ret, - {'branch': 'dev', - 'motd': 'The force will be with you. Always.', - 'mylist': ['dev'], - 'mydict': {'dev': True, - 'nested_list': ['dev'], - 'nested_dict': {'dev': True}}} + { + "branch": "dev", + "motd": "The force will be with you. Always.", + "mylist": ["dev"], + "mydict": { + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"dev": True}, + }, + }, ) def test_all_saltenvs_base(self): - ''' + """ Test all_saltenvs parameter with base pillarenv. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2252,47 +2652,94 @@ class TestPygit2HTTP(GitPillarHTTPTestBase): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'motd': 'The force will be with you. Always.', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}} + { + "branch": "master", + "motd": "The force will be with you. Always.", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + }, + ) + + def test_fallback(self): + """ + Test fallback parameter. + """ + ret = self.get_pillar( + """\ + file_ignore_regex: [] + file_ignore_glob: [] + git_pillar_provider: pygit2 + cachedir: {cachedir} + extension_modules: {extmods} + pillarenv: nonexisting + ext_pillar: + - git: + - __env__ {url_extra_repo}: + - fallback: master + - __env__ {url}: + - mountpoint: nowhere + - fallback: dev + """ + ) + self.assertEqual( + ret, + { + "branch": "dev", + "motd": "The force will be with you. Always.", + "mylist": ["dev"], + "mydict": { + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"dev": True}, + }, + }, ) -@skipIf(_windows_or_mac(), 'minion is windows or mac') +@skipIf(_windows_or_mac(), "minion is windows or mac") @skip_if_not_root -@skipIf(not HAS_PYGIT2, 'pygit2 >= {0} and libgit2 >= {1} required'.format(PYGIT2_MINVER, LIBGIT2_MINVER)) -@skipIf(not HAS_NGINX, 'nginx not present') -@skipIf(not HAS_VIRTUALENV, 'virtualenv not present') +@skipIf( + not HAS_PYGIT2, + "pygit2 >= {0} and libgit2 >= {1} required".format(PYGIT2_MINVER, LIBGIT2_MINVER), +) +@skipIf(not HAS_NGINX, "nginx not present") +@skipIf(not HAS_VIRTUALENV, "virtualenv not present") class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - ''' + """ Test git_pillar with pygit2 using SSH authentication NOTE: Any tests added to this test class should have equivalent tests (if possible) in the TestGitPythonSSH class. - ''' + """ + user = USERNAME password = PASSWORD def test_single_source(self): - ''' + """ Test using a single ext_pillar repo - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}} + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, } # Test with global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2304,11 +2751,13 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): ext_pillar: - git: - master {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2320,28 +2769,32 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - user: {user} - password: {password} - insecure_auth: True - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_master_dev_no_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the master branch followed by dev, and with pillar_merge_lists disabled. - ''' + """ expected = { - 'branch': 'dev', - 'mylist': ['dev'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['dev'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "dev", + "mylist": ["dev"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"master": True, "dev": True}, + }, } # Test with global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2355,11 +2808,13 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2376,28 +2831,32 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - user: {user} - password: {password} - insecure_auth: True - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_dev_master_no_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the dev branch followed by master, and with pillar_merge_lists disabled. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["master"], + "nested_dict": {"master": True, "dev": True}, + }, } # Test with global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2411,11 +2870,13 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2432,28 +2893,32 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - user: {user} - password: {password} - insecure_auth: True - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_master_dev_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the master branch followed by dev, and with pillar_merge_lists enabled. - ''' + """ expected = { - 'branch': 'dev', - 'mylist': ['master', 'dev'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['master', 'dev'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "dev", + "mylist": ["master", "dev"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["master", "dev"], + "nested_dict": {"master": True, "dev": True}, + }, } # Test with global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2467,11 +2932,13 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2488,28 +2955,32 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - user: {user} - password: {password} - insecure_auth: True - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_dev_master_merge_lists(self): - ''' + """ Test using two ext_pillar dirs. Since all git_pillar repos are merged into a single dictionary, ordering matters. This tests with the dev branch followed by master, and with pillar_merge_lists enabled. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['dev', 'master'], - 'mydict': {'master': True, - 'dev': True, - 'nested_list': ['dev', 'master'], - 'nested_dict': {'master': True, 'dev': True}} + "branch": "master", + "mylist": ["dev", "master"], + "mydict": { + "master": True, + "dev": True, + "nested_list": ["dev", "master"], + "nested_dict": {"master": True, "dev": True}, + }, } # Test with global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2523,11 +2994,13 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - git: - dev {url} - master {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2544,23 +3017,27 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - user: {user} - password: {password} - insecure_auth: True - ''') + """ + ) self.assertEqual(ret, expected) def test_multiple_sources_with_pillarenv(self): - ''' + """ Test using pillarenv to restrict results to those from a single branch - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}} + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, } # Test with global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2574,11 +3051,13 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - git: - master {url} - dev {url} - ''') + """ + ) self.assertEqual(ret, expected) # Test with per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2595,26 +3074,30 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - user: {user} - password: {password} - insecure_auth: True - ''') + """ + ) self.assertEqual(ret, expected) def test_includes_enabled(self): - ''' + """ Test with git_pillar_includes enabled. The top_only branch references an SLS file from the master branch, so we should see the "included_pillar" key from that SLS file in the compiled pillar data. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}, - 'included_pillar': True + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + "included_pillar": True, } # Test with global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2628,18 +3111,18 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_mountpoint_parameter(self): - ''' + """ Test mountpoint parameter - ''' - expected = { - 'included_pillar': True - } + """ + expected = {"included_pillar": True} - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2654,18 +3137,18 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - mountpoint: mounted - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_root_parameter(self): - ''' + """ Test root parameter - ''' - expected = { - 'from_subdir': True - } + """ + expected = {"from_subdir": True} - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2680,11 +3163,13 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - root: subdir - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2702,29 +3187,35 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - password: {password} - insecure_auth: True - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_includes_disabled(self): - ''' + """ Test with git_pillar_includes enabled. The top_only branch references an SLS file from the master branch, but since includes are disabled it will not find the SLS file and the "included_pillar" key should not be present in the compiled pillar data. We should instead see an error message in the compiled data. - ''' + """ expected = { - 'branch': 'master', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}, - '_errors': ["Specified SLS 'bar' in environment 'base' is not " - "available on the salt master"] + "branch": "master", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + "_errors": [ + "Specified SLS 'bar' in environment 'base' is not " + "available on the salt master" + ], } # Test with global credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2739,11 +3230,13 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - master {url} - top_only {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) # Test with per-repo credential options - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2761,18 +3254,20 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - password: {password} - insecure_auth: True - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_includes_enabled_solves___env___with_mountpoint(self): - ''' + """ Test with git_pillar_includes enabled and using "__env__" as the branch name for the configured repositories. The "gitinfo" repository contains top.sls file with a local reference and also referencing external "nowhere.foo" which is provided by "webinfo" repository mounted as "nowhere". - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2794,26 +3289,30 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - user: {user} - password: {password} - insecure_auth: True - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'motd': 'The force will be with you. Always.', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}} + { + "branch": "master", + "motd": "The force will be with you. Always.", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + }, ) def test_root_and_mountpoint_parameters(self): - ''' + """ Test root and mountpoint parameters - ''' - expected = { - 'from_subdir': True - } + """ + expected = {"from_subdir": True} - ret = self.get_pillar('''\ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2829,14 +3328,16 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - root: subdir - top_mounted {url}: - env: base - ''') + """ + ) self.assertEqual(ret, expected) def test_all_saltenvs(self): - ''' + """ Test all_saltenvs parameter. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2852,22 +3353,28 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual( ret, - {'branch': 'dev', - 'motd': 'The force will be with you. Always.', - 'mylist': ['dev'], - 'mydict': {'dev': True, - 'nested_list': ['dev'], - 'nested_dict': {'dev': True}}} + { + "branch": "dev", + "motd": "The force will be with you. Always.", + "mylist": ["dev"], + "mydict": { + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"dev": True}, + }, + }, ) def test_all_saltenvs_base(self): - ''' + """ Test all_saltenvs parameter with base pillarenv. - ''' - ret = self.get_pillar('''\ + """ + ret = self.get_pillar( + """\ file_ignore_regex: [] file_ignore_glob: [] git_pillar_provider: pygit2 @@ -2882,13 +3389,56 @@ class TestPygit2AuthenticatedHTTP(GitPillarHTTPTestBase): - all_saltenvs: master - __env__ {url}: - mountpoint: nowhere - ''') + """ + ) self.assertEqual( ret, - {'branch': 'master', - 'motd': 'The force will be with you. Always.', - 'mylist': ['master'], - 'mydict': {'master': True, - 'nested_list': ['master'], - 'nested_dict': {'master': True}}} + { + "branch": "master", + "motd": "The force will be with you. Always.", + "mylist": ["master"], + "mydict": { + "master": True, + "nested_list": ["master"], + "nested_dict": {"master": True}, + }, + }, + ) + + def test_fallback(self): + """ + Test fallback parameter. + """ + ret = self.get_pillar( + """\ + file_ignore_regex: [] + file_ignore_glob: [] + git_pillar_provider: pygit2 + git_pillar_user: {user} + git_pillar_password: {password} + git_pillar_insecure_auth: True + cachedir: {cachedir} + extension_modules: {extmods} + pillarenv: nonexisting + ext_pillar: + - git: + - __env__ {url_extra_repo}: + - fallback: master + - __env__ {url}: + - mountpoint: nowhere + - fallback: dev + """ + ) + self.assertEqual( + ret, + { + "branch": "dev", + "motd": "The force will be with you. Always.", + "mylist": ["dev"], + "mydict": { + "dev": True, + "nested_list": ["dev"], + "nested_dict": {"dev": True}, + }, + }, ) diff --git a/tests/integration/pillar/test_pillar_include.py b/tests/integration/pillar/test_pillar_include.py index 1451aee13cd..ea0a0c27eef 100644 --- a/tests/integration/pillar/test_pillar_include.py +++ b/tests/integration/pillar/test_pillar_include.py @@ -1,30 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Pillar include tests -''' +""" from __future__ import absolute_import, unicode_literals from tests.support.case import ModuleCase class PillarIncludeTest(ModuleCase): - def test_pillar_include(self): - ''' + """ Test pillar include - ''' - ret = self.minion_run('pillar.items') - assert 'a' in ret['element'] - assert ret['element']['a'] == {'a': ['Entry A']} - assert 'b' in ret['element'] - assert ret['element']['b'] == {'b': ['Entry B']} + """ + ret = self.minion_run("pillar.items") + assert "a" in ret["element"] + assert ret["element"]["a"] == {"a": ["Entry A"]} + assert "b" in ret["element"] + assert ret["element"]["b"] == {"b": ["Entry B"]} def test_pillar_glob_include(self): - ''' + """ Test pillar include via glob pattern - ''' - ret = self.minion_run('pillar.items') - assert 'glob-a' in ret - assert ret['glob-a'] == ['Entry A'] - assert 'glob-b' in ret - assert ret['glob-b'] == ['Entry B'] + """ + ret = self.minion_run("pillar.items") + assert "glob-a" in ret + assert ret["glob-a"] == ["Entry A"] + assert "glob-b" in ret + assert ret["glob-b"] == ["Entry B"] diff --git a/tests/integration/proxy/conftest.py b/tests/integration/proxy/conftest.py index f840fac8fd1..d0e8077d4e7 100644 --- a/tests/integration/proxy/conftest.py +++ b/tests/integration/proxy/conftest.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" tests.integration.proxy.conftest ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Proxy related fixtures -''' +""" # pylint: disable=unused-argument,redefined-outer-name # Import Python libs from __future__ import absolute_import, unicode_literals -import os + import logging +import os # Import 3rd-party libs import psutil @@ -19,22 +20,23 @@ import pytest log = logging.getLogger(__name__) -@pytest.fixture(scope='package', autouse=True) -def session_salt_proxy(request, - session_salt_proxy, - session_proxy_id, - session_master_config): +@pytest.fixture(scope="package", autouse=True) +def session_salt_proxy( + request, session_salt_proxy, session_proxy_id, session_master_config +): - stats_key = ' Salt Proxy' + stats_key = " Salt Proxy" request.session.stats_processes[stats_key] = psutil.Process(session_salt_proxy.pid) yield session_salt_proxy # Terminate Proxy now, we want to cleanup its key before we move along session_salt_proxy.terminate() del request.session.stats_processes[stats_key] - proxy_key_file = os.path.join(session_master_config['pki_dir'], 'minions', session_proxy_id) - log.warning('KEY FILE: %s', proxy_key_file) + proxy_key_file = os.path.join( + session_master_config["pki_dir"], "minions", session_proxy_id + ) + log.warning("KEY FILE: %s", proxy_key_file) if os.path.exists(proxy_key_file): os.unlink(proxy_key_file) else: - log.warning('The proxy minion key was not found at %s', proxy_key_file) + log.warning("The proxy minion key was not found at %s", proxy_key_file) diff --git a/tests/integration/proxy/test_shell.py b/tests/integration/proxy/test_shell.py index 5fbde9a5781..1ad104e88ce 100644 --- a/tests/integration/proxy/test_shell.py +++ b/tests/integration/proxy/test_shell.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Test salt-call --proxyid commands tests.integration.proxy.test_shell ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import sys + import json import logging +import sys # Import Salt Libs import salt.ext.six as six @@ -23,53 +24,70 @@ log = logging.getLogger(__name__) class ProxyCallerSimpleTestCase(ShellCase): - ''' + """ Test salt-call --proxyid <proxyid> commands - ''' + """ + @staticmethod def _load_return(ret): try: - return json.loads('\n'.join(ret)) + return json.loads("\n".join(ret)) except ValueError: - log.warning('Failed to JSON decode: \'%s\'', ret) + log.warning("Failed to JSON decode: '%s'", ret) six.reraise(*sys.exc_info()) def test_can_it_ping(self): - ''' + """ Ensure the proxy can ping - ''' - ret = self._load_return(self.run_call('--proxyid proxytest --out=json test.ping')) - self.assertEqual(ret['local'], True) + """ + ret = self._load_return( + self.run_call("--proxyid proxytest --out=json test.ping") + ) + self.assertEqual(ret["local"], True) def test_list_pkgs(self): - ''' + """ Package test 1, really just tests that the virtual function capability is working OK. - ''' - ret = self._load_return(self.run_call('--proxyid proxytest --out=json pkg.list_pkgs')) - self.assertIn('coreutils', ret['local']) - self.assertIn('apache', ret['local']) - self.assertIn('redbull', ret['local']) + """ + ret = self._load_return( + self.run_call("--proxyid proxytest --out=json pkg.list_pkgs") + ) + self.assertIn("coreutils", ret["local"]) + self.assertIn("apache", ret["local"]) + self.assertIn("redbull", ret["local"]) def test_upgrade(self): - ret = self._load_return(self.run_call('--proxyid proxytest --out=json pkg.upgrade')) - self.assertEqual(ret['local']['coreutils']['new'], '2.0') - self.assertEqual(ret['local']['redbull']['new'], '1000.99') + ret = self._load_return( + self.run_call("--proxyid proxytest --out=json pkg.upgrade") + ) + self.assertEqual(ret["local"]["coreutils"]["new"], "2.0") + self.assertEqual(ret["local"]["redbull"]["new"], "1000.99") def test_service_list(self): - ret = self._load_return(self.run_call('--proxyid proxytest --out=json service.list')) - self.assertIn('ntp', ret['local']) + ret = self._load_return( + self.run_call("--proxyid proxytest --out=json service.list") + ) + self.assertIn("ntp", ret["local"]) def test_service_start(self): - ret = self._load_return(self.run_call('--proxyid proxytest --out=json service.start samba')) - ret = self._load_return(self.run_call('--proxyid proxytest --out=json service.status samba')) + ret = self._load_return( + self.run_call("--proxyid proxytest --out=json service.start samba") + ) + ret = self._load_return( + self.run_call("--proxyid proxytest --out=json service.status samba") + ) self.assertTrue(ret) def test_service_get_all(self): - ret = self._load_return(self.run_call('--proxyid proxytest --out=json service.get_all')) - self.assertIn('samba', ret['local']) + ret = self._load_return( + self.run_call("--proxyid proxytest --out=json service.get_all") + ) + self.assertIn("samba", ret["local"]) def test_grains_items(self): - ret = self._load_return(self.run_call('--proxyid proxytest --out=json grains.items')) - self.assertEqual(ret['local']['kernel'], 'proxy') - self.assertEqual(ret['local']['kernelrelease'], 'proxy') + ret = self._load_return( + self.run_call("--proxyid proxytest --out=json grains.items") + ) + self.assertEqual(ret["local"]["kernel"], "proxy") + self.assertEqual(ret["local"]["kernelrelease"], "proxy") diff --git a/tests/integration/proxy/test_simple.py b/tests/integration/proxy/test_simple.py index e965a279d0a..70fe75b6d5d 100644 --- a/tests/integration/proxy/test_simple.py +++ b/tests/integration/proxy/test_simple.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Simple Smoke Tests for Connected Proxy Minion -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -11,79 +11,80 @@ from tests.support.case import ModuleCase class ProxyMinionSimpleTestCase(ModuleCase): - ''' + """ Test proxy minion functionality - ''' + """ + def test_can_it_ping(self): - ''' + """ Ensure the proxy can ping - ''' - ret = self.run_function('test.ping', minion_tgt='proxytest') + """ + ret = self.run_function("test.ping", minion_tgt="proxytest") self.assertEqual(ret, True) def test_list_pkgs(self): - ''' + """ Package test 1, really just tests that the virtual function capability is working OK. - ''' - ret = self.run_function('pkg.list_pkgs', minion_tgt='proxytest') - self.assertIn('coreutils', ret) - self.assertIn('apache', ret) - self.assertIn('redbull', ret) + """ + ret = self.run_function("pkg.list_pkgs", minion_tgt="proxytest") + self.assertIn("coreutils", ret) + self.assertIn("apache", ret) + self.assertIn("redbull", ret) def test_install_pkgs(self): - ''' + """ Package test 2, really just tests that the virtual function capability is working OK. - ''' - ret = self.run_function('pkg.install', ['thispkg'], minion_tgt='proxytest') - self.assertEqual(ret['thispkg'], '1.0') + """ + ret = self.run_function("pkg.install", ["thispkg"], minion_tgt="proxytest") + self.assertEqual(ret["thispkg"], "1.0") - ret = self.run_function('pkg.list_pkgs', minion_tgt='proxytest') + ret = self.run_function("pkg.list_pkgs", minion_tgt="proxytest") - self.assertEqual(ret['apache'], '2.4') - self.assertEqual(ret['redbull'], '999.99') - self.assertEqual(ret['thispkg'], '1.0') + self.assertEqual(ret["apache"], "2.4") + self.assertEqual(ret["redbull"], "999.99") + self.assertEqual(ret["thispkg"], "1.0") def test_remove_pkgs(self): - ret = self.run_function('pkg.remove', ['apache'], minion_tgt='proxytest') - self.assertNotIn('apache', ret) + ret = self.run_function("pkg.remove", ["apache"], minion_tgt="proxytest") + self.assertNotIn("apache", ret) def test_upgrade(self): - ret = self.run_function('pkg.upgrade', minion_tgt='proxytest') - self.assertEqual(ret['coreutils']['new'], '2.0') - self.assertEqual(ret['redbull']['new'], '1000.99') + ret = self.run_function("pkg.upgrade", minion_tgt="proxytest") + self.assertEqual(ret["coreutils"]["new"], "2.0") + self.assertEqual(ret["redbull"]["new"], "1000.99") def test_service_list(self): - ret = self.run_function('service.list', minion_tgt='proxytest') - self.assertIn('ntp', ret) + ret = self.run_function("service.list", minion_tgt="proxytest") + self.assertIn("ntp", ret) def test_service_stop(self): - ret = self.run_function('service.stop', ['ntp'], minion_tgt='proxytest') - ret = self.run_function('service.status', ['ntp'], minion_tgt='proxytest') + ret = self.run_function("service.stop", ["ntp"], minion_tgt="proxytest") + ret = self.run_function("service.status", ["ntp"], minion_tgt="proxytest") self.assertFalse(ret) def test_service_start(self): - ret = self.run_function('service.start', ['samba'], minion_tgt='proxytest') - ret = self.run_function('service.status', ['samba'], minion_tgt='proxytest') + ret = self.run_function("service.start", ["samba"], minion_tgt="proxytest") + ret = self.run_function("service.status", ["samba"], minion_tgt="proxytest") self.assertTrue(ret) def test_service_get_all(self): - ret = self.run_function('service.get_all', minion_tgt='proxytest') + ret = self.run_function("service.get_all", minion_tgt="proxytest") self.assertTrue(ret) - self.assertIn('samba', ' '.join(ret)) + self.assertIn("samba", " ".join(ret)) def test_grains_items(self): - ret = self.run_function('grains.items', minion_tgt='proxytest') - self.assertEqual(ret['kernel'], 'proxy') - self.assertEqual(ret['kernelrelease'], 'proxy') + ret = self.run_function("grains.items", minion_tgt="proxytest") + self.assertEqual(ret["kernel"], "proxy") + self.assertEqual(ret["kernelrelease"], "proxy") def test_state_apply(self): - ret = self.run_function('state.apply', ['core'], minion_tgt='proxytest') + ret = self.run_function("state.apply", ["core"], minion_tgt="proxytest") for key, value in ret.items(): - self.assertTrue(value['result']) + self.assertTrue(value["result"]) def test_state_highstate(self): - ret = self.run_function('state.highstate', minion_tgt='proxytest') + ret = self.run_function("state.highstate", minion_tgt="proxytest") for key, value in ret.items(): - self.assertTrue(value['result']) + self.assertTrue(value["result"]) diff --git a/tests/integration/reactor/test_reactor.py b/tests/integration/reactor/test_reactor.py index 5120b43a122..efefe83f58f 100644 --- a/tests/integration/reactor/test_reactor.py +++ b/tests/integration/reactor/test_reactor.py @@ -1,36 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" integration.reactor.reactor ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Test Salt's reactor system -''' +""" -# Import Python libs from __future__ import absolute_import -# Import Salt testing libs +import pytest +import salt.utils.event from tests.support.case import ModuleCase from tests.support.mixins import SaltMinionEventAssertsMixin -# Import Salt libs -import salt.utils.event - +@pytest.mark.windows_whitelisted class ReactorTest(SaltMinionEventAssertsMixin, ModuleCase): - ''' + """ Test Salt's reactor system - ''' + """ def test_ping_reaction(self): - ''' + """ Fire an event on the master and ensure that it pings the minion - ''' + """ # Create event bus connection - e = salt.utils.event.get_event('minion', sock_dir=self.minion_opts['sock_dir'], opts=self.minion_opts) + e = salt.utils.event.get_event( + "minion", sock_dir=self.minion_opts["sock_dir"], opts=self.minion_opts + ) - e.fire_event({'a': 'b'}, '/test_event') + e.fire_event({"a": "b"}, "/test_event") - self.assertMinionEventReceived({'a': 'b'}, timeout=30) + self.assertMinionEventReceived({"a": "b"}, timeout=30) diff --git a/tests/integration/renderers/test_jinja.py b/tests/integration/renderers/test_jinja.py index 78cbed11977..4975552ba34 100644 --- a/tests/integration/renderers/test_jinja.py +++ b/tests/integration/renderers/test_jinja.py @@ -1,33 +1,35 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals + import os import salt.utils.files - from tests.support.case import ModuleCase, ShellCase from tests.support.helpers import with_tempdir class JinjaRendererTest(ModuleCase): - @with_tempdir() def test_issue_54765(self, tmpdir): - file_path = os.path.join(tmpdir, 'issue-54765') - ret = self.run_function('state.sls', mods='issue-54765', pillar={'file_path': file_path}) - key = 'file_|-issue-54765_|-{}_|-managed'.format(file_path) + file_path = os.path.join(tmpdir, "issue-54765") + ret = self.run_function( + "state.sls", mods="issue-54765", pillar={"file_path": file_path} + ) + key = "file_|-issue-54765_|-{}_|-managed".format(file_path) assert key in ret - assert ret[key]['result'] is True - with salt.utils.files.fopen(file_path, 'r') as fp: - assert fp.read().strip() == 'bar' + assert ret[key]["result"] is True + with salt.utils.files.fopen(file_path, "r") as fp: + assert fp.read().strip() == "bar" class JinjaRenderCallTest(ShellCase): - @with_tempdir() def test_issue_54765(self, tmpdir): - file_path = os.path.join(tmpdir, 'issue-54765') + file_path = os.path.join(tmpdir, "issue-54765") pillar_str = '\'{{"file_path": "{}"}}\''.format(file_path) - ret = self.run_call('state.apply issue-54765 pillar={}'.format(pillar_str), local=True) - assert ' Result: True' in ret - with salt.utils.files.fopen(file_path, 'r') as fp: - assert fp.read().strip() == 'bar' + ret = self.run_call( + "state.apply issue-54765 pillar={}".format(pillar_str), local=True + ) + assert " Result: True" in ret + with salt.utils.files.fopen(file_path, "r") as fp: + assert fp.read().strip() == "bar" diff --git a/tests/integration/renderers/test_pydsl.py b/tests/integration/renderers/test_pydsl.py index c62075de1c0..8e6c1ab86d8 100644 --- a/tests/integration/renderers/test_pydsl.py +++ b/tests/integration/renderers/test_pydsl.py @@ -1,45 +1,44 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import textwrap -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.helpers import destructiveTest - -# Import Salt libs +import pytest import salt.utils.files import salt.utils.platform import salt.utils.stringutils +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +@pytest.mark.windows_whitelisted class PyDSLRendererIncludeTestCase(ModuleCase): - def setUp(self): self.directory_created = False if salt.utils.platform.is_windows(): - if not os.path.isdir('\\tmp'): - os.mkdir('\\tmp') + if not os.path.isdir("\\tmp"): + os.mkdir("\\tmp") self.directory_created = True def tearDown(self): if salt.utils.platform.is_windows(): if self.directory_created: - shutil.rmtree('\\tmp') + shutil.rmtree("\\tmp") @destructiveTest def test_rendering_includes(self): - ''' + """ This test is currently hard-coded to /tmp to work-around a seeming inability to load custom modules inside the pydsl renderers. This is a FIXME. - ''' - self.run_function('state.sls', ['pydsl.aaa']) + """ + self.run_function("state.sls", ["pydsl.aaa"]) - expected = textwrap.dedent('''\ + expected = textwrap.dedent( + """\ X1 X2 X3 @@ -49,25 +48,28 @@ class PyDSLRendererIncludeTestCase(ModuleCase): hello red 1 hello green 2 hello blue 3 - ''') + """ + ) # Windows adds `linefeed` in addition to `newline`. There's also an # unexplainable space before the `linefeed`... if salt.utils.platform.is_windows(): - expected = 'X1 \r\n' \ - 'X2 \r\n' \ - 'X3 \r\n' \ - 'Y1 extended \r\n' \ - 'Y2 extended \r\n' \ - 'Y3 \r\n' \ - 'hello red 1 \r\n' \ - 'hello green 2 \r\n' \ - 'hello blue 3 \r\n' + expected = ( + "X1 \r\n" + "X2 \r\n" + "X3 \r\n" + "Y1 extended \r\n" + "Y2 extended \r\n" + "Y3 \r\n" + "hello red 1 \r\n" + "hello green 2 \r\n" + "hello blue 3 \r\n" + ) try: - with salt.utils.files.fopen('/tmp/output', 'r') as f: + with salt.utils.files.fopen("/tmp/output", "r") as f: ret = salt.utils.stringutils.to_unicode(f.read()) finally: - os.remove('/tmp/output') + os.remove("/tmp/output") self.assertEqual(sorted(ret), sorted(expected)) diff --git a/tests/integration/returners/test_librato_return.py b/tests/integration/returners/test_librato_return.py index 90fce66346f..5a35ad12e6f 100644 --- a/tests/integration/returners/test_librato_return.py +++ b/tests/integration/returners/test_librato_return.py @@ -1,16 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the librato returner -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt Testing libs -from tests.support.case import ShellCase - -# Import salt libs +import pytest from salt.returners import librato_return +from tests.support.case import ShellCase log = logging.getLogger(__name__) @@ -26,7 +24,7 @@ MOCK_RET_OBJ = { "duration": 3.645, "__run_num__": 193, "changes": {}, - "__id__": "test-return-state" + "__id__": "test-return-state", }, "test-return-state2": { "comment": "insertcommenthere", @@ -36,27 +34,28 @@ MOCK_RET_OBJ = { "duration": 3.645, "__run_num__": 194, "changes": {}, - "__id__": "test-return-state" - } + "__id__": "test-return-state", + }, }, "retcode": 2, "success": True, "fun": "state.highstate", "id": "Librato-Test", - "out": "highstate" + "out": "highstate", } +@pytest.mark.windows_whitelisted class libratoTest(ShellCase): - ''' + """ Test the librato returner - ''' + """ def test_count_runtimes(self): - ''' + """ Test the calculations - ''' - results = librato_return._calculate_runtimes(MOCK_RET_OBJ['return']) - self.assertEqual(results['num_failed_states'], 1) - self.assertEqual(results['num_passed_states'], 1) - self.assertEqual(results['runtime'], 7.29) + """ + results = librato_return._calculate_runtimes(MOCK_RET_OBJ["return"]) + self.assertEqual(results["num_failed_states"], 1) + self.assertEqual(results["num_passed_states"], 1) + self.assertEqual(results["runtime"], 7.29) diff --git a/tests/integration/returners/test_noop_return.py b/tests/integration/returners/test_noop_return.py index cf2dac65e11..227f3203e31 100644 --- a/tests/integration/returners/test_noop_return.py +++ b/tests/integration/returners/test_noop_return.py @@ -1,32 +1,35 @@ # -*- coding: utf-8 -*- -''' +""" tests.integration.returners.test_noop_return ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This test module is meant to cover the issue being fixed by: https://github.com/saltstack/salt/pull/54731 -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import TstSuiteLoggingHandler +import logging # Import 3rd-party tests import salt.ext.six as six +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import TstSuiteLoggingHandler +from tests.support.unit import skipIf log = logging.getLogger(__name__) -@skipIf(six.PY3, 'Runtest Log Hander Disabled for PY3, #41836') +@skipIf(six.PY3, "Runtest Log Hander Disabled for PY3, #41836") class TestEventReturn(ModuleCase): - def test_noop_return(self): - with TstSuiteLoggingHandler(format='%(message)s', level=logging.DEBUG) as handler: - self.run_function('test.ping') - assert any('NOOP_RETURN' in s for s in handler.messages) is True, 'NOOP_RETURN not found in log messages' + with TstSuiteLoggingHandler( + format="%(message)s", level=logging.DEBUG + ) as handler: + self.run_function("test.ping") + assert ( + any("NOOP_RETURN" in s for s in handler.messages) is True + ), "NOOP_RETURN not found in log messages" diff --git a/tests/integration/runners/test_cache.py b/tests/integration/runners/test_cache.py index dc16ccffd50..6fdd174076b 100644 --- a/tests/integration/runners/test_cache.py +++ b/tests/integration/runners/test_cache.py @@ -1,126 +1,112 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the salt-run command -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import Salt Testing libs from tests.support.case import ShellCase -import logging log = logging.getLogger(__name__) class ManageTest(ShellCase): - ''' + """ Test the manage runner - ''' + """ + def test_cache(self): - ''' + """ Store, list, fetch, then flush data - ''' + """ # Store the data ret = self.run_run_plus( - 'cache.store', - bank='cachetest/runner', - key='test_cache', - data='The time has come the walrus said', + "cache.store", + bank="cachetest/runner", + key="test_cache", + data="The time has come the walrus said", ) # Make sure we can see the new key - ret = self.run_run_plus('cache.list', bank='cachetest/runner') - self.assertIn('test_cache', ret['return']) + ret = self.run_run_plus("cache.list", bank="cachetest/runner") + self.assertIn("test_cache", ret["return"]) # Make sure we can see the new data - ret = self.run_run_plus('cache.fetch', bank='cachetest/runner', key='test_cache') - self.assertIn('The time has come the walrus said', ret['return']) + ret = self.run_run_plus( + "cache.fetch", bank="cachetest/runner", key="test_cache" + ) + self.assertIn("The time has come the walrus said", ret["return"]) # Make sure we can delete the data - ret = self.run_run_plus('cache.flush', bank='cachetest/runner', key='test_cache') - ret = self.run_run_plus('cache.list', bank='cachetest/runner') - self.assertNotIn('test_cache', ret['return']) + ret = self.run_run_plus( + "cache.flush", bank="cachetest/runner", key="test_cache" + ) + ret = self.run_run_plus("cache.list", bank="cachetest/runner") + self.assertNotIn("test_cache", ret["return"]) def test_cache_invalid(self): - ''' + """ Store, list, fetch, then flush data - ''' + """ # Store the data - ret = self.run_run_plus( - 'cache.store', - ) + ret = self.run_run_plus("cache.store",) # Make sure we can see the new key - expected = 'Passed invalid arguments:' - self.assertIn(expected, ret['return']) + expected = "Passed invalid arguments:" + self.assertIn(expected, ret["return"]) def test_grains(self): - ''' + """ Test cache.grains - ''' + """ # Store the data - ret = self.run_run_plus( - 'cache.grains', - tgt='minion' - ) + ret = self.run_run_plus("cache.grains", tgt="minion") - self.assertIn('minion', ret['return']) + self.assertIn("minion", ret["return"]) def test_pillar(self): - ''' + """ Test cache.pillar - ''' + """ # Store the data - ret = self.run_run_plus( - 'cache.pillar', - tgt='minion' - ) + ret = self.run_run_plus("cache.pillar", tgt="minion") - assert 'minion' in ret['return'] - assert 'sub_minion' not in ret['return'] + assert "minion" in ret["return"] + assert "sub_minion" not in ret["return"] def test_pillar_no_tgt(self): - ''' + """ Test cache.pillar when no tgt is supplied. This should return pillar data for all minions - ''' + """ # Store the data - ret = self.run_run_plus( - 'cache.pillar', - ) + ret = self.run_run_plus("cache.pillar",) - assert all(x in ret['return'] for x in ['minion', 'sub_minion']) + assert all(x in ret["return"] for x in ["minion", "sub_minion"]) def test_pillar_minion_noexist(self): - ''' + """ Test cache.pillar when the target does not exist - ''' - ret = self.run_run_plus( - 'cache.pillar', - tgt='doesnotexist' - ) + """ + ret = self.run_run_plus("cache.pillar", tgt="doesnotexist") - assert 'minion' not in ret['return'] - assert 'sub_minion' not in ret['return'] + assert "minion" not in ret["return"] + assert "sub_minion" not in ret["return"] def test_pillar_minion_tgt_type_pillar(self): - ''' + """ Test cache.pillar when the target exists and tgt_type is pillar - ''' - ret = self.run_run_plus( - 'cache.pillar', - tgt='monty:python', - tgt_type='pillar', - ) + """ + ret = self.run_run_plus("cache.pillar", tgt="monty:python", tgt_type="pillar",) - assert all(x in ret['return'] for x in ['minion', 'sub_minion']) + assert all(x in ret["return"] for x in ["minion", "sub_minion"]) def test_mine(self): - ''' + """ Test cache.mine - ''' + """ # Store the data - ret = self.run_run_plus( - 'cache.mine', - tgt='minion' - ) + ret = self.run_run_plus("cache.mine", tgt="minion") - self.assertIn('minion', ret['return']) + self.assertIn("minion", ret["return"]) diff --git a/tests/integration/runners/test_fileserver.py b/tests/integration/runners/test_fileserver.py index 86dfc94ad6f..7ae73ee1864 100644 --- a/tests/integration/runners/test_fileserver.py +++ b/tests/integration/runners/test_fileserver.py @@ -1,199 +1,203 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the fileserver runner -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import contextlib -# Import Salt Testing libs +import pytest +import salt.utils.platform from tests.support.case import ShellCase from tests.support.unit import skipIf -# Import Salt libs -import salt.utils.platform - +@pytest.mark.windows_whitelisted class FileserverTest(ShellCase): - ''' + """ Test the fileserver runner - ''' + """ + def test_dir_list(self): - ''' + """ fileserver.dir_list - ''' - ret = self.run_run_plus(fun='fileserver.dir_list') - self.assertIsInstance(ret['return'], list) - self.assertTrue('_modules' in ret['return']) + """ + ret = self.run_run_plus(fun="fileserver.dir_list") + self.assertIsInstance(ret["return"], list) + self.assertTrue("_modules" in ret["return"]) # Backend submitted as a string - ret = self.run_run_plus(fun='fileserver.dir_list', backend='roots') - self.assertIsInstance(ret['return'], list) - self.assertTrue('_modules' in ret['return']) + ret = self.run_run_plus(fun="fileserver.dir_list", backend="roots") + self.assertIsInstance(ret["return"], list) + self.assertTrue("_modules" in ret["return"]) # Backend submitted as a list - ret = self.run_run_plus(fun='fileserver.dir_list', backend=['roots']) - self.assertIsInstance(ret['return'], list) - self.assertTrue('_modules' in ret['return']) + ret = self.run_run_plus(fun="fileserver.dir_list", backend=["roots"]) + self.assertIsInstance(ret["return"], list) + self.assertTrue("_modules" in ret["return"]) def test_empty_dir_list(self): - ''' + """ fileserver.empty_dir_list - ''' - ret = self.run_run_plus(fun='fileserver.empty_dir_list') - self.assertIsInstance(ret['return'], list) - self.assertEqual(ret['return'], []) + """ + ret = self.run_run_plus(fun="fileserver.empty_dir_list") + self.assertIsInstance(ret["return"], list) + self.assertEqual(ret["return"], []) # Backend submitted as a string - ret = self.run_run_plus( - fun='fileserver.empty_dir_list', - backend='roots') - self.assertIsInstance(ret['return'], list) - self.assertEqual(ret['return'], []) + ret = self.run_run_plus(fun="fileserver.empty_dir_list", backend="roots") + self.assertIsInstance(ret["return"], list) + self.assertEqual(ret["return"], []) # Backend submitted as a list - ret = self.run_run_plus( - fun='fileserver.empty_dir_list', - backend=['roots']) - self.assertIsInstance(ret['return'], list) - self.assertEqual(ret['return'], []) + ret = self.run_run_plus(fun="fileserver.empty_dir_list", backend=["roots"]) + self.assertIsInstance(ret["return"], list) + self.assertEqual(ret["return"], []) def test_envs(self): - ''' + """ fileserver.envs - ''' - ret = self.run_run_plus(fun='fileserver.envs') - self.assertIsInstance(ret['return'], list) + """ + ret = self.run_run_plus(fun="fileserver.envs") + self.assertIsInstance(ret["return"], list) # Backend submitted as a string - ret = self.run_run_plus(fun='fileserver.envs', backend='roots') - self.assertIsInstance(ret['return'], list) + ret = self.run_run_plus(fun="fileserver.envs", backend="roots") + self.assertIsInstance(ret["return"], list) # Backend submitted as a list - ret = self.run_run_plus(fun='fileserver.envs', backend=['roots']) - self.assertIsInstance(ret['return'], list) + ret = self.run_run_plus(fun="fileserver.envs", backend=["roots"]) + self.assertIsInstance(ret["return"], list) def test_clear_file_list_cache(self): - ''' + """ fileserver.clear_file_list_cache If this test fails, then something may have changed in the test suite and we may have more than just "roots" configured in the fileserver backends. This assert will need to be updated accordingly. - ''' - saltenvs = sorted(self.run_run_plus(fun='fileserver.envs')['return']) + """ + saltenvs = sorted(self.run_run_plus(fun="fileserver.envs")["return"]) @contextlib.contextmanager def gen_cache(): - ''' + """ Create file_list cache so we have something to clear - ''' + """ for saltenv in saltenvs: - self.run_run_plus(fun='fileserver.file_list', saltenv=saltenv) + self.run_run_plus(fun="fileserver.file_list", saltenv=saltenv) yield # Test with no arguments with gen_cache(): - ret = self.run_run_plus(fun='fileserver.clear_file_list_cache') - ret['return']['roots'].sort() - self.assertEqual(ret['return'], {'roots': saltenvs}) + ret = self.run_run_plus(fun="fileserver.clear_file_list_cache") + ret["return"]["roots"].sort() + self.assertEqual(ret["return"], {"roots": saltenvs}) # Test with backend passed as string with gen_cache(): - ret = self.run_run_plus(fun='fileserver.clear_file_list_cache', - backend='roots') - ret['return']['roots'].sort() - self.assertEqual(ret['return'], {'roots': saltenvs}) + ret = self.run_run_plus( + fun="fileserver.clear_file_list_cache", backend="roots" + ) + ret["return"]["roots"].sort() + self.assertEqual(ret["return"], {"roots": saltenvs}) # Test with backend passed as list with gen_cache(): - ret = self.run_run_plus(fun='fileserver.clear_file_list_cache', - backend=['roots']) - ret['return']['roots'].sort() - self.assertEqual(ret['return'], {'roots': saltenvs}) + ret = self.run_run_plus( + fun="fileserver.clear_file_list_cache", backend=["roots"] + ) + ret["return"]["roots"].sort() + self.assertEqual(ret["return"], {"roots": saltenvs}) # Test with backend passed as string, but with a nonsense backend with gen_cache(): - ret = self.run_run_plus(fun='fileserver.clear_file_list_cache', - backend='notarealbackend') - self.assertEqual(ret['return'], {}) + ret = self.run_run_plus( + fun="fileserver.clear_file_list_cache", backend="notarealbackend" + ) + self.assertEqual(ret["return"], {}) # Test with saltenv passed as string with gen_cache(): - ret = self.run_run_plus(fun='fileserver.clear_file_list_cache', - saltenv='base') - self.assertEqual(ret['return'], {'roots': ['base']}) + ret = self.run_run_plus( + fun="fileserver.clear_file_list_cache", saltenv="base" + ) + self.assertEqual(ret["return"], {"roots": ["base"]}) # Test with saltenv passed as list with gen_cache(): - ret = self.run_run_plus(fun='fileserver.clear_file_list_cache', - saltenv=['base']) - self.assertEqual(ret['return'], {'roots': ['base']}) + ret = self.run_run_plus( + fun="fileserver.clear_file_list_cache", saltenv=["base"] + ) + self.assertEqual(ret["return"], {"roots": ["base"]}) # Test with saltenv passed as string, but with a nonsense saltenv with gen_cache(): - ret = self.run_run_plus(fun='fileserver.clear_file_list_cache', - saltenv='notarealsaltenv') - self.assertEqual(ret['return'], {}) + ret = self.run_run_plus( + fun="fileserver.clear_file_list_cache", saltenv="notarealsaltenv" + ) + self.assertEqual(ret["return"], {}) # Test with both backend and saltenv passed with gen_cache(): - ret = self.run_run_plus(fun='fileserver.clear_file_list_cache', - backend='roots', - saltenv='base') - self.assertEqual(ret['return'], {'roots': ['base']}) + ret = self.run_run_plus( + fun="fileserver.clear_file_list_cache", backend="roots", saltenv="base" + ) + self.assertEqual(ret["return"], {"roots": ["base"]}) def test_file_list(self): - ''' + """ fileserver.file_list - ''' - ret = self.run_run_plus(fun='fileserver.file_list') - self.assertIsInstance(ret['return'], list) - self.assertTrue('grail/scene33' in ret['return']) + """ + ret = self.run_run_plus(fun="fileserver.file_list") + self.assertIsInstance(ret["return"], list) + self.assertTrue("grail/scene33" in ret["return"]) # Backend submitted as a string - ret = self.run_run_plus(fun='fileserver.file_list', backend='roots') - self.assertIsInstance(ret['return'], list) - self.assertTrue('grail/scene33' in ret['return']) + ret = self.run_run_plus(fun="fileserver.file_list", backend="roots") + self.assertIsInstance(ret["return"], list) + self.assertTrue("grail/scene33" in ret["return"]) # Backend submitted as a list - ret = self.run_run_plus(fun='fileserver.file_list', backend=['roots']) - self.assertIsInstance(ret['return'], list) - self.assertTrue('grail/scene33' in ret['return']) + ret = self.run_run_plus(fun="fileserver.file_list", backend=["roots"]) + self.assertIsInstance(ret["return"], list) + self.assertTrue("grail/scene33" in ret["return"]) # Git doesn't handle symlinks in Windows. See the thread below: # http://stackoverflow.com/questions/5917249/git-symlinks-in-windows - @skipIf(salt.utils.platform.is_windows(), - 'Git for Windows does not preserve symbolic links when cloning') + @skipIf( + salt.utils.platform.is_windows(), + "Git for Windows does not preserve symbolic links when cloning", + ) def test_symlink_list(self): - ''' + """ fileserver.symlink_list - ''' - ret = self.run_run_plus(fun='fileserver.symlink_list') - self.assertIsInstance(ret['return'], dict) - self.assertTrue('dest_sym' in ret['return']) + """ + ret = self.run_run_plus(fun="fileserver.symlink_list") + self.assertIsInstance(ret["return"], dict) + self.assertTrue("dest_sym" in ret["return"]) # Backend submitted as a string - ret = self.run_run_plus(fun='fileserver.symlink_list', backend='roots') - self.assertIsInstance(ret['return'], dict) - self.assertTrue('dest_sym' in ret['return']) + ret = self.run_run_plus(fun="fileserver.symlink_list", backend="roots") + self.assertIsInstance(ret["return"], dict) + self.assertTrue("dest_sym" in ret["return"]) # Backend submitted as a list - ret = self.run_run_plus(fun='fileserver.symlink_list', backend=['roots']) - self.assertIsInstance(ret['return'], dict) - self.assertTrue('dest_sym' in ret['return']) + ret = self.run_run_plus(fun="fileserver.symlink_list", backend=["roots"]) + self.assertIsInstance(ret["return"], dict) + self.assertTrue("dest_sym" in ret["return"]) def test_update(self): - ''' + """ fileserver.update - ''' - ret = self.run_run_plus(fun='fileserver.update') - self.assertTrue(ret['return']) + """ + ret = self.run_run_plus(fun="fileserver.update") + self.assertTrue(ret["return"]) # Backend submitted as a string - ret = self.run_run_plus(fun='fileserver.update', backend='roots') - self.assertTrue(ret['return']) + ret = self.run_run_plus(fun="fileserver.update", backend="roots") + self.assertTrue(ret["return"]) # Backend submitted as a list - ret = self.run_run_plus(fun='fileserver.update', backend=['roots']) - self.assertTrue(ret['return']) + ret = self.run_run_plus(fun="fileserver.update", backend=["roots"]) + self.assertTrue(ret["return"]) diff --git a/tests/integration/runners/test_jobs.py b/tests/integration/runners/test_jobs.py index 7cabaadc122..e8a07f59544 100644 --- a/tests/integration/runners/test_jobs.py +++ b/tests/integration/runners/test_jobs.py @@ -1,59 +1,61 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the salt-run command -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ShellCase from tests.support.unit import skipIf +@pytest.mark.windows_whitelisted class ManageTest(ShellCase): - ''' + """ Test the manage runner - ''' + """ + def test_active(self): - ''' + """ jobs.active - ''' - ret = self.run_run_plus('jobs.active') - self.assertEqual(ret['return'], {}) - self.assertEqual(ret['out'], []) + """ + ret = self.run_run_plus("jobs.active") + self.assertEqual(ret["return"], {}) + self.assertEqual(ret["out"], []) def test_lookup_jid(self): - ''' + """ jobs.lookup_jid - ''' - ret = self.run_run_plus('jobs.lookup_jid', '23974239742394') - self.assertEqual(ret['return'], {}) - self.assertEqual(ret['out'], []) + """ + ret = self.run_run_plus("jobs.lookup_jid", "23974239742394") + self.assertEqual(ret["return"], {}) + self.assertEqual(ret["out"], []) def test_lookup_jid_invalid(self): - ''' + """ jobs.lookup_jid - ''' - ret = self.run_run_plus('jobs.lookup_jid') - expected = 'Passed invalid arguments:' - self.assertIn(expected, ret['return']) + """ + ret = self.run_run_plus("jobs.lookup_jid") + expected = "Passed invalid arguments:" + self.assertIn(expected, ret["return"]) - @skipIf(True, 'to be re-enabled when #23623 is merged') + @skipIf(True, "to be re-enabled when #23623 is merged") def test_list_jobs(self): - ''' + """ jobs.list_jobs - ''' - ret = self.run_run_plus('jobs.list_jobs') - self.assertIsInstance(ret['return'], dict) + """ + ret = self.run_run_plus("jobs.list_jobs") + self.assertIsInstance(ret["return"], dict) +@pytest.mark.windows_whitelisted class LocalCacheTargetTest(ShellCase): - ''' + """ Test that a job stored in the local_cache has target information - ''' + """ def test_target_info(self): - ''' + """ This is a test case for issue #48734 PR #43454 fixed an issue where "jobs.lookup_jid" was not working @@ -69,16 +71,18 @@ class LocalCacheTargetTest(ShellCase): "list" type instead of "glob". This is a regression test for fixing the local_cache behavior. - ''' - self.run_salt('minion test.echo target_info_test') - ret = self.run_run_plus('jobs.list_jobs') - for item in ret['return'].values(): - if item['Function'] == 'test.echo' and \ - item['Arguments'][0] == 'target_info_test': + """ + self.run_salt("minion test.echo target_info_test") + ret = self.run_run_plus("jobs.list_jobs") + for item in ret["return"].values(): + if ( + item["Function"] == "test.echo" + and item["Arguments"][0] == "target_info_test" + ): job_ret = item - tgt = job_ret['Target'] - tgt_type = job_ret['Target-type'] + tgt = job_ret["Target"] + tgt_type = job_ret["Target-type"] - assert tgt != 'unknown-target' - assert tgt in ['minion', 'sub_minion'] - assert tgt_type == 'glob' + assert tgt != "unknown-target" + assert tgt in ["minion", "sub_minion"] + assert tgt_type == "glob" diff --git a/tests/integration/runners/test_manage.py b/tests/integration/runners/test_manage.py index cdd3e02c6bf..c7521fe1594 100644 --- a/tests/integration/runners/test_manage.py +++ b/tests/integration/runners/test_manage.py @@ -1,34 +1,35 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the salt-run command -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ShellCase +@pytest.mark.windows_whitelisted class ManageTest(ShellCase): - ''' + """ Test the manage runner - ''' + """ + def test_up(self): - ''' + """ manage.up - ''' - ret = self.run_run_plus('manage.up', timeout=60) - self.assertIn('minion', ret['return']) - self.assertIn('sub_minion', ret['return']) - self.assertTrue(any('- minion' in out for out in ret['out'])) - self.assertTrue(any('- sub_minion' in out for out in ret['out'])) + """ + ret = self.run_run_plus("manage.up", timeout=60) + self.assertIn("minion", ret["return"]) + self.assertIn("sub_minion", ret["return"]) + self.assertTrue(any("- minion" in out for out in ret["out"])) + self.assertTrue(any("- sub_minion" in out for out in ret["out"])) def test_down(self): - ''' + """ manage.down - ''' - ret = self.run_run_plus('manage.down', timeout=60) - self.assertNotIn('minion', ret['return']) - self.assertNotIn('sub_minion', ret['return']) - self.assertNotIn('minion', ret['out']) - self.assertNotIn('sub_minion', ret['out']) + """ + ret = self.run_run_plus("manage.down", timeout=60) + self.assertNotIn("minion", ret["return"]) + self.assertNotIn("sub_minion", ret["return"]) + self.assertNotIn("minion", ret["out"]) + self.assertNotIn("sub_minion", ret["out"]) diff --git a/tests/integration/runners/test_nacl.py b/tests/integration/runners/test_nacl.py index 67f7fc301ad..24e9ddc1a0f 100644 --- a/tests/integration/runners/test_nacl.py +++ b/tests/integration/runners/test_nacl.py @@ -1,19 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the salt-run command -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals import logging -# Import Salt Testing libs +import pytest from tests.support.case import ShellCase from tests.support.unit import skipIf try: import libnacl.secret # pylint: disable=unused-import import libnacl.sealed # pylint: disable=unused-import + HAS_LIBNACL = True except (ImportError, OSError, AttributeError): HAS_LIBNACL = False @@ -21,170 +21,124 @@ except (ImportError, OSError, AttributeError): log = logging.getLogger(__name__) -@skipIf(not HAS_LIBNACL, 'skipping test_nacl, libnacl is unavailable') +@skipIf(not HAS_LIBNACL, "skipping test_nacl, libnacl is unavailable") +@pytest.mark.windows_whitelisted class NaclTest(ShellCase): - ''' + """ Test the nacl runner - ''' + """ + def test_keygen(self): - ''' + """ Test keygen - ''' + """ # Store the data - ret = self.run_run_plus( - 'nacl.keygen', - ) - self.assertIn('pk', ret['return']) - self.assertIn('sk', ret['return']) + ret = self.run_run_plus("nacl.keygen",) + self.assertIn("pk", ret["return"]) + self.assertIn("sk", ret["return"]) def test_enc(self): - ''' + """ Test keygen - ''' + """ # Store the data - ret = self.run_run_plus( - 'nacl.keygen', - ) - self.assertIn('pk', ret['return']) - self.assertIn('sk', ret['return']) - pk = ret['return']['pk'] - sk = ret['return']['sk'] + ret = self.run_run_plus("nacl.keygen",) + self.assertIn("pk", ret["return"]) + self.assertIn("sk", ret["return"]) + pk = ret["return"]["pk"] + sk = ret["return"]["sk"] - unencrypted_data = 'hello' + unencrypted_data = "hello" # Encrypt with pk - ret = self.run_run_plus( - 'nacl.enc', - data=unencrypted_data, - pk=pk, - ) - self.assertIn('return', ret) + ret = self.run_run_plus("nacl.enc", data=unencrypted_data, pk=pk,) + self.assertIn("return", ret) def test_enc_dec(self): - ''' + """ Store, list, fetch, then flush data - ''' + """ # Store the data - ret = self.run_run_plus( - 'nacl.keygen', - ) - self.assertIn('pk', ret['return']) - self.assertIn('sk', ret['return']) - pk = ret['return']['pk'] - sk = ret['return']['sk'] + ret = self.run_run_plus("nacl.keygen",) + self.assertIn("pk", ret["return"]) + self.assertIn("sk", ret["return"]) + pk = ret["return"]["pk"] + sk = ret["return"]["sk"] - unencrypted_data = b'hello' + unencrypted_data = b"hello" # Encrypt with pk - ret = self.run_run_plus( - 'nacl.enc', - data=unencrypted_data, - pk=pk, - ) - self.assertIn('return', ret) - encrypted_data = ret['return'] + ret = self.run_run_plus("nacl.enc", data=unencrypted_data, pk=pk,) + self.assertIn("return", ret) + encrypted_data = ret["return"] # Decrypt with sk - ret = self.run_run_plus( - 'nacl.dec', - data=encrypted_data, - sk=sk, - ) - self.assertIn('return', ret) - self.assertEqual(unencrypted_data, ret['return']) + ret = self.run_run_plus("nacl.dec", data=encrypted_data, sk=sk,) + self.assertIn("return", ret) + self.assertEqual(unencrypted_data, ret["return"]) def test_sealedbox_enc_dec(self): - ''' + """ Generate keys, encrypt, then decrypt. - ''' + """ # Store the data - ret = self.run_run_plus( - 'nacl.keygen', - ) - self.assertIn('pk', ret['return']) - self.assertIn('sk', ret['return']) - pk = ret['return']['pk'] - sk = ret['return']['sk'] + ret = self.run_run_plus("nacl.keygen",) + self.assertIn("pk", ret["return"]) + self.assertIn("sk", ret["return"]) + pk = ret["return"]["pk"] + sk = ret["return"]["sk"] - unencrypted_data = b'hello' + unencrypted_data = b"hello" # Encrypt with pk - ret = self.run_run_plus( - 'nacl.sealedbox_encrypt', - data=unencrypted_data, - pk=pk, - ) - encrypted_data = ret['return'] + ret = self.run_run_plus("nacl.sealedbox_encrypt", data=unencrypted_data, pk=pk,) + encrypted_data = ret["return"] # Decrypt with sk - ret = self.run_run_plus( - 'nacl.sealedbox_decrypt', - data=encrypted_data, - sk=sk, - ) - self.assertEqual(unencrypted_data, ret['return']) + ret = self.run_run_plus("nacl.sealedbox_decrypt", data=encrypted_data, sk=sk,) + self.assertEqual(unencrypted_data, ret["return"]) def test_secretbox_enc_dec(self): - ''' + """ Generate keys, encrypt, then decrypt. - ''' + """ # Store the data - ret = self.run_run_plus( - 'nacl.keygen', - ) - self.assertIn('pk', ret['return']) - self.assertIn('sk', ret['return']) - pk = ret['return']['pk'] - sk = ret['return']['sk'] + ret = self.run_run_plus("nacl.keygen",) + self.assertIn("pk", ret["return"]) + self.assertIn("sk", ret["return"]) + pk = ret["return"]["pk"] + sk = ret["return"]["sk"] - unencrypted_data = b'hello' + unencrypted_data = b"hello" # Encrypt with pk - ret = self.run_run_plus( - 'nacl.secretbox_encrypt', - data=unencrypted_data, - sk=sk, - ) - encrypted_data = ret['return'] + ret = self.run_run_plus("nacl.secretbox_encrypt", data=unencrypted_data, sk=sk,) + encrypted_data = ret["return"] # Decrypt with sk - ret = self.run_run_plus( - 'nacl.secretbox_decrypt', - data=encrypted_data, - sk=sk, - ) - self.assertEqual(unencrypted_data, ret['return']) + ret = self.run_run_plus("nacl.secretbox_decrypt", data=encrypted_data, sk=sk,) + self.assertEqual(unencrypted_data, ret["return"]) def test_enc_dec_no_pk_no_sk(self): - ''' + """ Store, list, fetch, then flush data - ''' + """ # Store the data - ret = self.run_run_plus( - 'nacl.keygen', - ) - self.assertIn('pk', ret['return']) - self.assertIn('sk', ret['return']) - pk = ret['return']['pk'] - sk = ret['return']['sk'] + ret = self.run_run_plus("nacl.keygen",) + self.assertIn("pk", ret["return"]) + self.assertIn("sk", ret["return"]) + pk = ret["return"]["pk"] + sk = ret["return"]["sk"] - unencrypted_data = b'hello' + unencrypted_data = b"hello" # Encrypt with pk - ret = self.run_run_plus( - 'nacl.enc', - data=unencrypted_data, - pk=None, - ) - self.assertIn('Exception: no pubkey or pk_file found', ret['return']) + ret = self.run_run_plus("nacl.enc", data=unencrypted_data, pk=None,) + self.assertIn("Exception: no pubkey or pk_file found", ret["return"]) - self.assertIn('return', ret) - encrypted_data = ret['return'] + self.assertIn("return", ret) + encrypted_data = ret["return"] # Decrypt with sk - ret = self.run_run_plus( - 'nacl.dec', - data=encrypted_data, - sk=None, - ) - self.assertIn('Exception: no key or sk_file found', ret['return']) + ret = self.run_run_plus("nacl.dec", data=encrypted_data, sk=None,) + self.assertIn("Exception: no key or sk_file found", ret["return"]) diff --git a/tests/integration/runners/test_runner_returns.py b/tests/integration/runners/test_runner_returns.py index 05efb4356e5..cbb75f64d25 100644 --- a/tests/integration/runners/test_runner_returns.py +++ b/tests/integration/runners/test_runner_returns.py @@ -1,37 +1,37 @@ # -*- coding: utf-8 -*- -''' +""" Tests for runner_returns -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import errno import os import socket import tempfile -# Import Salt Testing libs -from tests.support.case import ShellCase -from tests.support.runtests import RUNTIME_VARS - -# Import salt libs +import pytest import salt.payload import salt.utils.args import salt.utils.files import salt.utils.jid import salt.utils.yaml +from tests.support.case import ShellCase +from tests.support.runtests import RUNTIME_VARS +@pytest.mark.windows_whitelisted class RunnerReturnsTest(ShellCase): - ''' + """ Test the "runner_returns" feature - ''' + """ + def setUp(self): - ''' + """ Create the temp file and master.d directory - ''' - self.job_dir = os.path.join(self.master_opts['cachedir'], 'jobs') - self.hash_type = self.master_opts['hash_type'] - self.master_d_dir = os.path.join(self.config_dir, 'master.d') + """ + self.job_dir = os.path.join(self.master_opts["cachedir"], "jobs") + self.hash_type = self.master_opts["hash_type"] + self.master_d_dir = os.path.join(self.config_dir, "master.d") try: os.makedirs(self.master_d_dir) except OSError as exc: @@ -39,106 +39,103 @@ class RunnerReturnsTest(ShellCase): raise self.conf = tempfile.NamedTemporaryFile( - mode='w', - suffix='.conf', - dir=self.master_d_dir, - delete=False, + mode="w", suffix=".conf", dir=self.master_d_dir, delete=False, ) def tearDown(self): - ''' + """ Close the tempfile.NamedTemporaryFile object, cleaning it up - ''' + """ salt.utils.files.rm_rf(self.master_d_dir) # Force a reload of the configuration now that our temp config file has # been removed. - self.run_run_plus('test.arg', __reload_config=True) + self.run_run_plus("test.arg", __reload_config=True) @staticmethod def clean_return(data): - ''' + """ Remove kwargs and timestamp (things that are variable) so we have a stable value to assert - ''' + """ # Remove pub_kwargs - data['fun_args'][1] = \ - salt.utils.args.clean_kwargs(**data['fun_args'][1]) - data['return']['kwargs'] = \ - salt.utils.args.clean_kwargs(**data['return']['kwargs']) + data["fun_args"][1] = salt.utils.args.clean_kwargs(**data["fun_args"][1]) + data["return"]["kwargs"] = salt.utils.args.clean_kwargs( + **data["return"]["kwargs"] + ) # Pop off the timestamp (do not provide a 2nd argument, if the stamp is # missing we want to know!) - data.pop('_stamp') + data.pop("_stamp") def write_conf(self, data): - ''' + """ Dump the config dict to the conf file - ''' + """ self.conf.write(salt.utils.yaml.safe_dump(data, default_flow_style=False)) self.conf.flush() self.conf.close() def test_runner_returns_disabled(self): - ''' + """ Test with runner_returns enabled - ''' - self.write_conf({'runner_returns': False}) + """ + self.write_conf({"runner_returns": False}) ret = self.run_run_plus( - 'test.arg', - 'foo', - bar='hello world!', - __reload_config=True) + "test.arg", "foo", bar="hello world!", __reload_config=True + ) - jid = ret.get('jid') + jid = ret.get("jid") if jid is None: - raise Exception('jid missing from run_run_plus output') + raise Exception("jid missing from run_run_plus output") serialized_return = os.path.join( salt.utils.jid.jid_dir(jid, self.job_dir, self.hash_type), - 'master', - 'return.p', + "master", + "return.p", ) self.assertFalse(os.path.isfile(serialized_return)) def test_runner_returns_enabled(self): - ''' + """ Test with runner_returns enabled - ''' - self.write_conf({'runner_returns': True}) + """ + self.write_conf({"runner_returns": True}) ret = self.run_run_plus( - 'test.arg', - 'foo', - bar='hello world!', - __reload_config=True) + "test.arg", "foo", bar="hello world!", __reload_config=True + ) - jid = ret.get('jid') + jid = ret.get("jid") if jid is None: - raise Exception('jid missing from run_run_plus output') + raise Exception("jid missing from run_run_plus output") serialized_return = os.path.join( salt.utils.jid.jid_dir(jid, self.job_dir, self.hash_type), - 'master', - 'return.p', + "master", + "return.p", ) serial = salt.payload.Serial(self.master_opts) - with salt.utils.files.fopen(serialized_return, 'rb') as fp_: + with salt.utils.files.fopen(serialized_return, "rb") as fp_: deserialized = serial.loads(fp_.read()) - self.clean_return(deserialized['return']) + self.clean_return(deserialized["return"]) # Now we have something sane we can reliably compare in an assert. - if 'SUDO_USER' in os.environ: - user = 'sudo_{0}'.format(os.environ['SUDO_USER']) + if "SUDO_USER" in os.environ: + user = "sudo_{0}".format(os.environ["SUDO_USER"]) else: user = RUNTIME_VARS.RUNNING_TESTS_USER if salt.utils.platform.is_windows(): - user = 'sudo_{0}\\{1}'.format(socket.gethostname(), user) + user = "sudo_{0}\\{1}".format(socket.gethostname(), user) self.assertEqual( deserialized, - {'return': {'fun': 'runner.test.arg', - 'fun_args': ['foo', {'bar': 'hello world!'}], - 'jid': jid, - 'return': {'args': ['foo'], 'kwargs': {'bar': 'hello world!'}}, - 'success': True, - 'user': user}} + { + "return": { + "fun": "runner.test.arg", + "fun_args": ["foo", {"bar": "hello world!"}], + "jid": jid, + "return": {"args": ["foo"], "kwargs": {"bar": "hello world!"}}, + "success": True, + "user": user, + } + }, ) diff --git a/tests/integration/runners/test_salt.py b/tests/integration/runners/test_salt.py index 71683a8e250..7590114f4ce 100644 --- a/tests/integration/runners/test_salt.py +++ b/tests/integration/runners/test_salt.py @@ -1,35 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the salt runner .. versionadded:: 2016.11.0 -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ShellCase +@pytest.mark.windows_whitelisted class SaltRunnerTest(ShellCase): - ''' + """ Test the salt runner - ''' - def test_salt_cmd(self): - ''' - test return values of salt.cmd - ''' - ret = self.run_run_plus('salt.cmd', 'test.ping') - out_ret = ret.get('out')[0] - return_ret = ret.get('return') + """ - self.assertEqual(out_ret, 'True') + def test_salt_cmd(self): + """ + test return values of salt.cmd + """ + ret = self.run_run_plus("salt.cmd", "test.ping") + out_ret = ret.get("out")[0] + return_ret = ret.get("return") + + self.assertEqual(out_ret, "True") self.assertTrue(return_ret) def test_salt_cmd_invalid(self): - ''' + """ test return values of salt.cmd invalid parameters - ''' - ret = self.run_run_plus('salt.cmd') - expected = 'Passed invalid arguments:' - self.assertIn(expected, ret['return']) + """ + ret = self.run_run_plus("salt.cmd") + expected = "Passed invalid arguments:" + self.assertIn(expected, ret["return"]) diff --git a/tests/integration/runners/test_state.py b/tests/integration/runners/test_state.py index 4cdfac9d1f8..4bdf25e1d39 100644 --- a/tests/integration/runners/test_state.py +++ b/tests/integration/runners/test_state.py @@ -1,59 +1,61 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the state runner -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import errno import logging import os import shutil import signal import tempfile -import time import textwrap import threading - -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ShellCase -from tests.support.helpers import flaky, expensiveTest -from tests.support.mock import MagicMock, patch -from tests.support.unit import skipIf +import time # Import Salt Libs import salt.exceptions -import salt.utils.platform import salt.utils.event import salt.utils.files import salt.utils.json +import salt.utils.platform import salt.utils.stringutils import salt.utils.yaml # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import queue +from tests.support.case import ShellCase +from tests.support.helpers import expensiveTest, flaky +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf log = logging.getLogger(__name__) @flaky class StateRunnerTest(ShellCase): - ''' + """ Test the state runner. - ''' + """ + def add_to_queue(self, q, cmd): - ''' + """ helper method to add salt-run return data to a queue - ''' + """ ret = self.run_run(cmd) q.put(ret) q.task_done() def test_orchestrate_output(self): - ''' + """ Ensure the orchestrate runner outputs useful state data. In Issue #31330, the output only contains ['outputter:', ' highstate'], @@ -62,14 +64,16 @@ class StateRunnerTest(ShellCase): Also test against some sample "good" output that would be included in a correct orchestrate run. - ''' - ret_output = self.run_run('state.orchestrate orch.simple') - bad_out = ['outputter:', ' highstate'] - good_out = [' Function: salt.state', - ' Result: True', - 'Succeeded: 1 (changed=1)', - 'Failed: 0', - 'Total states run: 1'] + """ + ret_output = self.run_run("state.orchestrate orch.simple") + bad_out = ["outputter:", " highstate"] + good_out = [ + " Function: salt.state", + " Result: True", + "Succeeded: 1 (changed=1)", + "Failed: 0", + "Total states run: 1", + ] # First, check that we don't have the "bad" output that was displaying in # Issue #31330 where only the highstate outputter was listed @@ -80,106 +84,107 @@ class StateRunnerTest(ShellCase): assert item in ret_output def test_orchestrate_nested(self): - ''' + """ test salt-run state.orchestrate and failhard with nested orchestration - ''' - if os.path.exists('/tmp/ewu-2016-12-13'): - os.remove('/tmp/ewu-2016-12-13') + """ + if os.path.exists("/tmp/ewu-2016-12-13"): + os.remove("/tmp/ewu-2016-12-13") - _, code = self.run_run( - 'state.orchestrate nested-orch.outer', - with_retcode=True) + _, code = self.run_run("state.orchestrate nested-orch.outer", with_retcode=True) - assert os.path.exists('/tmp/ewu-2016-12-13') is False + assert os.path.exists("/tmp/ewu-2016-12-13") is False assert code != 0 def test_orchestrate_with_mine(self): - ''' + """ test salt-run state.orchestrate with mine.get call in sls - ''' + """ fail_time = time.time() + 120 self.run_run('mine.update "*"') - exp_ret = 'Succeeded: 1 (changed=1)' + exp_ret = "Succeeded: 1 (changed=1)" while True: - ret = self.run_run('state.orchestrate orch.mine') + ret = self.run_run("state.orchestrate orch.mine") try: assert exp_ret in ret break except AssertionError: if time.time() > fail_time: - self.fail('"{0}" was not found in the orchestration call'.format(exp_ret)) + self.fail( + '"{0}" was not found in the orchestration call'.format(exp_ret) + ) def test_orchestrate_state_and_function_failure(self): - ''' + """ Ensure that returns from failed minions are in the changes dict where they belong, so they can be programatically analyzed. See https://github.com/saltstack/salt/issues/43204 - ''' - self.run_run('saltutil.sync_modules') + """ + self.run_run("saltutil.sync_modules") ret = salt.utils.json.loads( - '\n'.join( - self.run_run('state.orchestrate orch.issue43204 --out=json') - ) + "\n".join(self.run_run("state.orchestrate orch.issue43204 --out=json")) ) # Drill down to the changes dict - state_ret = ret['data']['master']['salt_|-Step01_|-Step01_|-state']['changes'] - func_ret = ret['data']['master']['salt_|-Step02_|-runtests_helpers.nonzero_retcode_return_false_|-function']['changes'] + state_ret = ret["data"]["master"]["salt_|-Step01_|-Step01_|-state"]["changes"] + func_ret = ret["data"]["master"][ + "salt_|-Step02_|-runtests_helpers.nonzero_retcode_return_false_|-function" + ]["changes"] # Remove duration and start time from the results, since they would # vary with each run and that would make it impossible to test. - for item in ('duration', 'start_time'): - state_ret['ret']['minion']['test_|-test fail with changes_|-test fail with changes_|-fail_with_changes'].pop(item) + for item in ("duration", "start_time"): + state_ret["ret"]["minion"][ + "test_|-test fail with changes_|-test fail with changes_|-fail_with_changes" + ].pop(item) self.assertEqual( state_ret, { - 'out': 'highstate', - 'ret': { - 'minion': { - 'test_|-test fail with changes_|-test fail with changes_|-fail_with_changes': { - '__id__': 'test fail with changes', - '__run_num__': 0, - '__sls__': 'orch.issue43204.fail_with_changes', - 'changes': { - 'testing': { - 'new': 'Something pretended to change', - 'old': 'Unchanged' + "out": "highstate", + "ret": { + "minion": { + "test_|-test fail with changes_|-test fail with changes_|-fail_with_changes": { + "__id__": "test fail with changes", + "__run_num__": 0, + "__sls__": "orch.issue43204.fail_with_changes", + "changes": { + "testing": { + "new": "Something pretended to change", + "old": "Unchanged", } }, - 'comment': 'Failure!', - 'name': 'test fail with changes', - 'result': False, + "comment": "Failure!", + "name": "test fail with changes", + "result": False, } } - } - } + }, + }, ) - self.assertEqual( - func_ret, - {'out': 'highstate', 'ret': {'minion': False}} - ) + self.assertEqual(func_ret, {"out": "highstate", "ret": {"minion": False}}) def test_orchestrate_target_exists(self): - ''' + """ test orchestration when target exists while using multiple states - ''' - ret = self.run_run('state.orchestrate orch.target-exists') + """ + ret = self.run_run("state.orchestrate orch.target-exists") - first = [' ID: core', - ' Function: salt.state', - ' Result: True'] + first = [" ID: core", " Function: salt.state", " Result: True"] - second = [' ID: test-state', - ' Function: salt.state', - ' Result: True'] + second = [ + " ID: test-state", + " Function: salt.state", + " Result: True", + ] - third = [' ID: cmd.run', - ' Function: salt.function', - ' Result: True'] + third = [ + " ID: cmd.run", + " Function: salt.function", + " Result: True", + ] ret_out = [first, second, third] @@ -188,53 +193,58 @@ class StateRunnerTest(ShellCase): assert item in ret def test_orchestrate_retcode(self): - ''' + """ Test orchestration with nonzero retcode set in __context__ - ''' - self.run_run('saltutil.sync_runners') - self.run_run('saltutil.sync_wheel') - ret = '\n'.join(self.run_run('state.orchestrate orch.retcode')) + """ + self.run_run("saltutil.sync_runners") + self.run_run("saltutil.sync_wheel") + ret = "\n".join(self.run_run("state.orchestrate orch.retcode")) - for result in (' ID: test_runner_success\n' - ' Function: salt.runner\n' - ' Name: runtests_helpers.success\n' - ' Result: True', - - ' ID: test_runner_failure\n' - ' Function: salt.runner\n' - ' Name: runtests_helpers.failure\n' - ' Result: False', - - ' ID: test_wheel_success\n' - ' Function: salt.wheel\n' - ' Name: runtests_helpers.success\n' - ' Result: True', - - ' ID: test_wheel_failure\n' - ' Function: salt.wheel\n' - ' Name: runtests_helpers.failure\n' - ' Result: False'): + for result in ( + " ID: test_runner_success\n" + " Function: salt.runner\n" + " Name: runtests_helpers.success\n" + " Result: True", + " ID: test_runner_failure\n" + " Function: salt.runner\n" + " Name: runtests_helpers.failure\n" + " Result: False", + " ID: test_wheel_success\n" + " Function: salt.wheel\n" + " Name: runtests_helpers.success\n" + " Result: True", + " ID: test_wheel_failure\n" + " Function: salt.wheel\n" + " Name: runtests_helpers.failure\n" + " Result: False", + ): self.assertIn(result, ret) def test_orchestrate_target_doesnt_exist(self): - ''' + """ test orchestration when target doesn't exist while using multiple states - ''' - ret = self.run_run('state.orchestrate orch.target-doesnt-exists') + """ + ret = self.run_run("state.orchestrate orch.target-doesnt-exists") - first = ['No minions matched the target. No command was sent, no jid was assigned.', - ' ID: core', - ' Function: salt.state', - ' Result: False'] + first = [ + "No minions matched the target. No command was sent, no jid was assigned.", + " ID: core", + " Function: salt.state", + " Result: False", + ] - second = [' ID: test-state', - ' Function: salt.state', - ' Result: True'] + second = [ + " ID: test-state", + " Function: salt.state", + " Result: True", + ] - third = [' ID: cmd.run', - ' Function: salt.function', - ' Result: True'] + third = [ + " ID: cmd.run", + " Function: salt.function", + " Result: True", + ] ret_out = [first, second, third] @@ -243,24 +253,28 @@ class StateRunnerTest(ShellCase): assert item in ret def test_orchestrate_batch_with_failhard_error(self): - ''' + """ test orchestration properly stops with failhard and batch. - ''' - ret = self.run_run('state.orchestrate orch.batch --out=json -l critical') - ret_json = salt.utils.json.loads('\n'.join(ret)) - retcode = ret_json['retcode'] - result = ret_json['data']['master']['salt_|-call_fail_state_|-call_fail_state_|-state']['result'] - changes = ret_json['data']['master']['salt_|-call_fail_state_|-call_fail_state_|-state']['changes'] + """ + ret = self.run_run("state.orchestrate orch.batch --out=json -l critical") + ret_json = salt.utils.json.loads("\n".join(ret)) + retcode = ret_json["retcode"] + result = ret_json["data"]["master"][ + "salt_|-call_fail_state_|-call_fail_state_|-state" + ]["result"] + changes = ret_json["data"]["master"][ + "salt_|-call_fail_state_|-call_fail_state_|-state" + ]["changes"] # Looks like there is a platform differences in execution. # I see empty changes dict in MacOS for some reason. Maybe it's a bug? if changes: - changes_ret = changes['ret'] + changes_ret = changes["ret"] # Debug - print('Retcode: {}'.format(retcode)) - print('Changes: {}'.format(changes)) - print('Result: {}'.format(result)) + print("Retcode: {}".format(retcode)) + print("Changes: {}".format(changes)) + print("Result: {}".format(result)) assert retcode != 0 assert result is False @@ -269,72 +283,72 @@ class StateRunnerTest(ShellCase): assert len(changes_ret) == 1 def test_state_event(self): - ''' + """ test to ensure state.event runner returns correct data - ''' + """ q = queue.Queue(maxsize=0) - cmd = 'state.event salt/job/*/new count=1' + cmd = "state.event salt/job/*/new count=1" expect = '"minions": ["minion"]' server_thread = threading.Thread(target=self.add_to_queue, args=(q, cmd)) server_thread.setDaemon(True) server_thread.start() while q.empty(): - self.run_salt('minion test.ping --static') + self.run_salt("minion test.ping --static") out = q.get() assert expect in six.text_type(out) server_thread.join() def test_orchestrate_subset(self): - ''' + """ test orchestration state using subset - ''' - ret = self.run_run('state.orchestrate orch.subset', timeout=500) + """ + ret = self.run_run("state.orchestrate orch.subset", timeout=500) def count(thing, listobj): return sum([obj.strip() == thing for obj in listobj]) - assert count('ID: test subset', ret) == 1 - assert count('Succeeded: 1', ret) == 1 - assert count('Failed: 0', ret) == 1 + assert count("ID: test subset", ret) == 1 + assert count("Succeeded: 1", ret) == 1 + assert count("Failed: 0", ret) == 1 def test_orchestrate_salt_function_return_false_failure(self): - ''' + """ Ensure that functions that only return False in the return are flagged as failed when run as orchestrations. See https://github.com/saltstack/salt/issues/30367 - ''' - self.run_run('saltutil.sync_modules') + """ + self.run_run("saltutil.sync_modules") ret = salt.utils.json.loads( - '\n'.join( - self.run_run('state.orchestrate orch.issue30367 --out=json') - ) + "\n".join(self.run_run("state.orchestrate orch.issue30367 --out=json")) ) # Drill down to the changes dict - state_result = ret['data']['master']['salt_|-deploy_check_|-test.false_|-function']['result'] - func_ret = ret['data']['master']['salt_|-deploy_check_|-test.false_|-function']['changes'] + state_result = ret["data"]["master"][ + "salt_|-deploy_check_|-test.false_|-function" + ]["result"] + func_ret = ret["data"]["master"]["salt_|-deploy_check_|-test.false_|-function"][ + "changes" + ] assert state_result is False - self.assertEqual( - func_ret, - {'out': 'highstate', 'ret': {'minion': False}} - ) + self.assertEqual(func_ret, {"out": "highstate", "ret": {"minion": False}}) -@skipIf(salt.utils.platform.is_windows(), '*NIX-only test') +@skipIf(salt.utils.platform.is_windows(), "*NIX-only test") @flaky class OrchEventTest(ShellCase): - ''' + """ Tests for orchestration events - ''' + """ + def setUp(self): self.timeout = 60 - self.master_d_dir = os.path.join(self.config_dir, 'master.d') + self.master_d_dir = os.path.join(self.config_dir, "master.d") try: os.makedirs(self.master_d_dir) except OSError as exc: @@ -342,53 +356,56 @@ class OrchEventTest(ShellCase): raise self.conf = tempfile.NamedTemporaryFile( - mode='w', - suffix='.conf', - dir=self.master_d_dir, - delete=True, + mode="w", suffix=".conf", dir=self.master_d_dir, delete=True, ) self.base_env = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.base_env) self.addCleanup(self.conf.close) - for attr in ('timeout', 'master_d_dir', 'conf', 'base_env'): + for attr in ("timeout", "master_d_dir", "conf", "base_env"): self.addCleanup(delattr, self, attr) # Force a reload of the configuration now that our temp config file has # been removed. - self.addCleanup(self.run_run_plus, 'test.arg', __reload_config=True) + self.addCleanup(self.run_run_plus, "test.arg", __reload_config=True) def alarm_handler(self, signal, frame): - raise Exception('Timeout of {0} seconds reached'.format(self.timeout)) + raise Exception("Timeout of {0} seconds reached".format(self.timeout)) def write_conf(self, data): - ''' + """ Dump the config dict to the conf file - ''' + """ self.conf.write(salt.utils.yaml.safe_dump(data, default_flow_style=False)) self.conf.flush() @expensiveTest def test_jid_in_ret_event(self): - ''' + """ Test to confirm that the ret event for the orchestration contains the jid for the jobs spawned. - ''' - self.write_conf({ - 'fileserver_backend': ['roots'], - 'file_roots': { - 'base': [self.base_env], - }, - }) + """ + self.write_conf( + {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}} + ) - state_sls = os.path.join(self.base_env, 'test_state.sls') - with salt.utils.files.fopen(state_sls, 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str(textwrap.dedent(''' + state_sls = os.path.join(self.base_env, "test_state.sls") + with salt.utils.files.fopen(state_sls, "w") as fp_: + fp_.write( + salt.utils.stringutils.to_str( + textwrap.dedent( + """ date: cmd.run - '''))) + """ + ) + ) + ) - orch_sls = os.path.join(self.base_env, 'test_orch.sls') - with salt.utils.files.fopen(orch_sls, 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str(textwrap.dedent(''' + orch_sls = os.path.join(self.base_env, "test_orch.sls") + with salt.utils.files.fopen(orch_sls, "w") as fp_: + fp_.write( + salt.utils.stringutils.to_str( + textwrap.dedent( + """ date_cmd: salt.state: - tgt: minion @@ -404,21 +421,24 @@ class OrchEventTest(ShellCase): config.values: salt.wheel - '''))) + """ + ) + ) + ) listener = salt.utils.event.get_event( - 'master', - sock_dir=self.master_opts['sock_dir'], - transport=self.master_opts['transport'], - opts=self.master_opts) + "master", + sock_dir=self.master_opts["sock_dir"], + transport=self.master_opts["transport"], + opts=self.master_opts, + ) jid = self.run_run_plus( - 'state.orchestrate', - 'test_orch', - __reload_config=True).get('jid') + "state.orchestrate", "test_orch", __reload_config=True + ).get("jid") if jid is None: - raise Exception('jid missing from run_run_plus output') + raise Exception("jid missing from run_run_plus output") signal.signal(signal.SIGALRM, self.alarm_handler) signal.alarm(self.timeout) @@ -428,12 +448,12 @@ class OrchEventTest(ShellCase): if event is None: continue - if event['tag'] == 'salt/run/{0}/ret'.format(jid): + if event["tag"] == "salt/run/{0}/ret".format(jid): # Don't wrap this in a try/except. We want to know if the # data structure is different from what we expect! - ret = event['data']['return']['data']['master'] + ret = event["data"]["return"]["data"]["master"] for job in ret: - self.assertTrue('__jid__' in ret[job]) + self.assertTrue("__jid__" in ret[job]) break finally: del listener @@ -441,21 +461,20 @@ class OrchEventTest(ShellCase): @expensiveTest def test_parallel_orchestrations(self): - ''' + """ Test to confirm that the parallel state requisite works in orch we do this by running 10 test.sleep's of 10 seconds, and insure it only takes roughly 10s - ''' - self.write_conf({ - 'fileserver_backend': ['roots'], - 'file_roots': { - 'base': [self.base_env], - }, - }) + """ + self.write_conf( + {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}} + ) - orch_sls = os.path.join(self.base_env, 'test_par_orch.sls') + orch_sls = os.path.join(self.base_env, "test_par_orch.sls") - with salt.utils.files.fopen(orch_sls, 'w') as fp_: - fp_.write(textwrap.dedent(''' + with salt.utils.files.fopen(orch_sls, "w") as fp_: + fp_.write( + textwrap.dedent( + """ {% for count in range(1, 20) %} sleep {{ count }}: @@ -473,24 +492,26 @@ class OrchEventTest(ShellCase): - parallel: True - require: - module: sleep 1 - ''')) + """ + ) + ) - orch_sls = os.path.join(self.base_env, 'test_par_orch.sls') + orch_sls = os.path.join(self.base_env, "test_par_orch.sls") listener = salt.utils.event.get_event( - 'master', - sock_dir=self.master_opts['sock_dir'], - transport=self.master_opts['transport'], - opts=self.master_opts) + "master", + sock_dir=self.master_opts["sock_dir"], + transport=self.master_opts["transport"], + opts=self.master_opts, + ) start_time = time.time() jid = self.run_run_plus( - 'state.orchestrate', - 'test_par_orch', - __reload_config=True).get('jid') + "state.orchestrate", "test_par_orch", __reload_config=True + ).get("jid") if jid is None: - raise Exception('jid missing from run_run_plus output') + raise Exception("jid missing from run_run_plus output") signal.signal(signal.SIGALRM, self.alarm_handler) signal.alarm(self.timeout) @@ -504,15 +525,15 @@ class OrchEventTest(ShellCase): # if we receive the ret for this job before self.timeout (60), # the test is implicitly sucessful; if it were happening in serial it would be # atleast 110 seconds. - if event['tag'] == 'salt/run/{0}/ret'.format(jid): + if event["tag"] == "salt/run/{0}/ret".format(jid): received = True # Don't wrap this in a try/except. We want to know if the # data structure is different from what we expect! - ret = event['data']['return']['data']['master'] + ret = event["data"]["return"]["data"]["master"] for state in ret: data = ret[state] # we expect each duration to be greater than 10s - self.assertTrue(data['duration'] > 10000) + self.assertTrue(data["duration"] > 10000) break # self confirm that the total runtime is roughly 30s (left 10s for buffer) @@ -524,44 +545,45 @@ class OrchEventTest(ShellCase): @expensiveTest def test_orchestration_soft_kill(self): - ''' + """ Test to confirm that the parallel state requisite works in orch we do this by running 10 test.sleep's of 10 seconds, and insure it only takes roughly 10s - ''' - self.write_conf({ - 'fileserver_backend': ['roots'], - 'file_roots': { - 'base': [self.base_env], - }, - }) + """ + self.write_conf( + {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}} + ) - orch_sls = os.path.join(self.base_env, 'two_stage_orch_kill.sls') + orch_sls = os.path.join(self.base_env, "two_stage_orch_kill.sls") - with salt.utils.files.fopen(orch_sls, 'w') as fp_: - fp_.write(textwrap.dedent(''' + with salt.utils.files.fopen(orch_sls, "w") as fp_: + fp_.write( + textwrap.dedent( + """ stage_one: test.succeed_without_changes stage_two: test.fail_without_changes - ''')) + """ + ) + ) listener = salt.utils.event.get_event( - 'master', - sock_dir=self.master_opts['sock_dir'], - transport=self.master_opts['transport'], - opts=self.master_opts) + "master", + sock_dir=self.master_opts["sock_dir"], + transport=self.master_opts["transport"], + opts=self.master_opts, + ) - mock_jid = '20131219120000000000' - self.run_run('state.soft_kill {0} stage_two'.format(mock_jid)) - with patch('salt.utils.jid.gen_jid', MagicMock(return_value=mock_jid)): + mock_jid = "20131219120000000000" + self.run_run("state.soft_kill {0} stage_two".format(mock_jid)) + with patch("salt.utils.jid.gen_jid", MagicMock(return_value=mock_jid)): jid = self.run_run_plus( - 'state.orchestrate', - 'two_stage_orch_kill', - __reload_config=True).get('jid') + "state.orchestrate", "two_stage_orch_kill", __reload_config=True + ).get("jid") if jid is None: - raise Exception('jid missing from run_run_plus output') + raise Exception("jid missing from run_run_plus output") signal.signal(signal.SIGALRM, self.alarm_handler) signal.alarm(self.timeout) @@ -573,12 +595,14 @@ class OrchEventTest(ShellCase): continue # Ensure that stage_two of the state does not run - if event['tag'] == 'salt/run/{0}/ret'.format(jid): + if event["tag"] == "salt/run/{0}/ret".format(jid): received = True # Don't wrap this in a try/except. We want to know if the # data structure is different from what we expect! - ret = event['data']['return']['data']['master'] - self.assertNotIn('test_|-stage_two_|-stage_two_|-fail_without_changes', ret) + ret = event["data"]["return"]["data"]["master"] + self.assertNotIn( + "test_|-stage_two_|-stage_two_|-fail_without_changes", ret + ) break finally: @@ -587,65 +611,80 @@ class OrchEventTest(ShellCase): signal.alarm(0) def test_orchestration_with_pillar_dot_items(self): - ''' + """ Test to confirm when using a state file that includes other state file, if one of those state files includes pillar related functions that will not be pulling from the pillar cache that all the state files are available and the file_roots has been preserved. See issues #48277 and #46986. - ''' - self.write_conf({ - 'fileserver_backend': ['roots'], - 'file_roots': { - 'base': [self.base_env], - }, - }) + """ + self.write_conf( + {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}} + ) - orch_sls = os.path.join(self.base_env, 'main.sls') - with salt.utils.files.fopen(orch_sls, 'w') as fp_: - fp_.write(textwrap.dedent(''' + orch_sls = os.path.join(self.base_env, "main.sls") + with salt.utils.files.fopen(orch_sls, "w") as fp_: + fp_.write( + textwrap.dedent( + """ include: - one - two - three - ''')) + """ + ) + ) - orch_sls = os.path.join(self.base_env, 'one.sls') - with salt.utils.files.fopen(orch_sls, 'w') as fp_: - fp_.write(textwrap.dedent(''' + orch_sls = os.path.join(self.base_env, "one.sls") + with salt.utils.files.fopen(orch_sls, "w") as fp_: + fp_.write( + textwrap.dedent( + """ {%- set foo = salt['saltutil.runner']('pillar.show_pillar') %} placeholder_one: test.succeed_without_changes - ''')) + """ + ) + ) - orch_sls = os.path.join(self.base_env, 'two.sls') - with salt.utils.files.fopen(orch_sls, 'w') as fp_: - fp_.write(textwrap.dedent(''' + orch_sls = os.path.join(self.base_env, "two.sls") + with salt.utils.files.fopen(orch_sls, "w") as fp_: + fp_.write( + textwrap.dedent( + """ placeholder_two: test.succeed_without_changes - ''')) + """ + ) + ) - orch_sls = os.path.join(self.base_env, 'three.sls') - with salt.utils.files.fopen(orch_sls, 'w') as fp_: - fp_.write(textwrap.dedent(''' + orch_sls = os.path.join(self.base_env, "three.sls") + with salt.utils.files.fopen(orch_sls, "w") as fp_: + fp_.write( + textwrap.dedent( + """ placeholder_three: test.succeed_without_changes - ''')) + """ + ) + ) - orch_sls = os.path.join(self.base_env, 'main.sls') + orch_sls = os.path.join(self.base_env, "main.sls") listener = salt.utils.event.get_event( - 'master', - sock_dir=self.master_opts['sock_dir'], - transport=self.master_opts['transport'], - opts=self.master_opts) + "master", + sock_dir=self.master_opts["sock_dir"], + transport=self.master_opts["transport"], + opts=self.master_opts, + ) - jid = self.run_run_plus( - 'state.orchestrate', - 'main', - __reload_config=True).get('jid') + jid = self.run_run_plus("state.orchestrate", "main", __reload_config=True).get( + "jid" + ) if jid is None: - raise salt.exceptions.SaltInvocationError('jid missing from run_run_plus output') + raise salt.exceptions.SaltInvocationError( + "jid missing from run_run_plus output" + ) signal.signal(signal.SIGALRM, self.alarm_handler) signal.alarm(self.timeout) @@ -655,15 +694,15 @@ class OrchEventTest(ShellCase): event = listener.get_event(full=True) if event is None: continue - if event.get('tag', '') == 'salt/run/{0}/ret'.format(jid): + if event.get("tag", "") == "salt/run/{0}/ret".format(jid): received = True # Don't wrap this in a try/except. We want to know if the # data structure is different from what we expect! - ret = event['data']['return']['data']['master'] + ret = event["data"]["return"]["data"]["master"] for state in ret: data = ret[state] # Each state should be successful - self.assertEqual(data['comment'], 'Success!') + self.assertEqual(data["comment"], "Success!") break finally: self.assertTrue(received) @@ -671,20 +710,19 @@ class OrchEventTest(ShellCase): signal.alarm(0) def test_orchestration_onchanges_and_prereq(self): - ''' + """ Test to confirm that the parallel state requisite works in orch we do this by running 10 test.sleep's of 10 seconds, and insure it only takes roughly 10s - ''' - self.write_conf({ - 'fileserver_backend': ['roots'], - 'file_roots': { - 'base': [self.base_env], - }, - }) + """ + self.write_conf( + {"fileserver_backend": ["roots"], "file_roots": {"base": [self.base_env]}} + ) - orch_sls = os.path.join(self.base_env, 'orch.sls') - with salt.utils.files.fopen(orch_sls, 'w') as fp_: - fp_.write(textwrap.dedent(''' + orch_sls = os.path.join(self.base_env, "orch.sls") + with salt.utils.files.fopen(orch_sls, "w") as fp_: + fp_.write( + textwrap.dedent( + """ manage_a_file: salt.state: - tgt: minion @@ -704,44 +742,42 @@ class OrchEventTest(ShellCase): - name: test.ping - prereq: - salt: manage_a_file - ''')) + """ + ) + ) listener = salt.utils.event.get_event( - 'master', - sock_dir=self.master_opts['sock_dir'], - transport=self.master_opts['transport'], - opts=self.master_opts) + "master", + sock_dir=self.master_opts["sock_dir"], + transport=self.master_opts["transport"], + opts=self.master_opts, + ) try: jid1 = self.run_run_plus( - 'state.orchestrate', - 'orch', - test=True, - __reload_config=True).get('jid') + "state.orchestrate", "orch", test=True, __reload_config=True + ).get("jid") # Run for real to create the file - self.run_run_plus( - 'state.orchestrate', - 'orch', - __reload_config=True).get('jid') + self.run_run_plus("state.orchestrate", "orch", __reload_config=True).get( + "jid" + ) # Run again in test mode. Since there were no changes, the # requisites should not fire. jid2 = self.run_run_plus( - 'state.orchestrate', - 'orch', - test=True, - __reload_config=True).get('jid') + "state.orchestrate", "orch", test=True, __reload_config=True + ).get("jid") finally: try: - os.remove(os.path.join(RUNTIME_VARS.TMP, 'orch.req_test')) + os.remove(os.path.join(RUNTIME_VARS.TMP, "orch.req_test")) except OSError: pass assert jid1 is not None assert jid2 is not None - tags = {'salt/run/{0}/ret'.format(x): x for x in (jid1, jid2)} + tags = {"salt/run/{0}/ret".format(x): x for x in (jid1, jid2)} ret = {} signal.signal(signal.SIGALRM, self.alarm_handler) @@ -752,9 +788,9 @@ class OrchEventTest(ShellCase): if event is None: continue - if event['tag'] in tags: - ret[tags.pop(event['tag'])] = self.repack_state_returns( - event['data']['return']['data']['master'] + if event["tag"] in tags: + ret[tags.pop(event["tag"])] = self.repack_state_returns( + event["data"]["return"]["data"]["master"] ) if not tags: # If tags is empty, we've grabbed all the returns we @@ -764,24 +800,27 @@ class OrchEventTest(ShellCase): del listener signal.alarm(0) - for sls_id in ('manage_a_file', 'do_onchanges', 'do_prereq'): + for sls_id in ("manage_a_file", "do_onchanges", "do_prereq"): # The first time through, all three states should have a None # result, while the second time through, they should all have a # True result. - assert ret[jid1][sls_id]['result'] is None, \ - 'result of {0} ({1}) is not None'.format( - sls_id, - ret[jid1][sls_id]['result']) - assert ret[jid2][sls_id]['result'] is True, \ - 'result of {0} ({1}) is not True'.format( - sls_id, - ret[jid2][sls_id]['result']) + assert ( + ret[jid1][sls_id]["result"] is None + ), "result of {0} ({1}) is not None".format( + sls_id, ret[jid1][sls_id]["result"] + ) + assert ( + ret[jid2][sls_id]["result"] is True + ), "result of {0} ({1}) is not True".format( + sls_id, ret[jid2][sls_id]["result"] + ) # The file.managed state should have shown changes in the test mode # return data. - assert ret[jid1]['manage_a_file']['changes'] + assert ret[jid1]["manage_a_file"]["changes"] # After the file was created, running again in test mode should have # shown no changes. - assert not ret[jid2]['manage_a_file']['changes'], \ - ret[jid2]['manage_a_file']['changes'] + assert not ret[jid2]["manage_a_file"]["changes"], ret[jid2]["manage_a_file"][ + "changes" + ] diff --git a/tests/integration/scheduler/test_error.py b/tests/integration/scheduler/test_error.py index 1eac396150d..c103054a7c2 100644 --- a/tests/integration/scheduler/test_error.py +++ b/tests/integration/scheduler/test_error.py @@ -2,96 +2,87 @@ # Import Python libs from __future__ import absolute_import + import copy import logging import os import dateutil.parser as dateutil_parser +# Import Salt libs +import salt.utils.schedule +from salt.modules.test import ping + # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin from tests.support.mock import MagicMock, patch -from tests.support.unit import skipIf from tests.support.runtests import RUNTIME_VARS - -# Import Salt libs -import salt.utils.schedule - -from salt.modules.test import ping +from tests.support.unit import skipIf try: import croniter # pylint: disable=W0611 + HAS_CRONITER = True except ImportError: HAS_CRONITER = False log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') -SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, "schedule-unit-tests") +SOCK_DIR = os.path.join(ROOT_DIR, "test-socks") DEFAULT_CONFIG = salt.config.minion_config(None) -DEFAULT_CONFIG['conf_dir'] = ROOT_DIR -DEFAULT_CONFIG['root_dir'] = ROOT_DIR -DEFAULT_CONFIG['sock_dir'] = SOCK_DIR -DEFAULT_CONFIG['pki_dir'] = os.path.join(ROOT_DIR, 'pki') -DEFAULT_CONFIG['cachedir'] = os.path.join(ROOT_DIR, 'cache') +DEFAULT_CONFIG["conf_dir"] = ROOT_DIR +DEFAULT_CONFIG["root_dir"] = ROOT_DIR +DEFAULT_CONFIG["sock_dir"] = SOCK_DIR +DEFAULT_CONFIG["pki_dir"] = os.path.join(ROOT_DIR, "pki") +DEFAULT_CONFIG["cachedir"] = os.path.join(ROOT_DIR, "cache") class SchedulerErrorTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the pkg module - ''' - def setUp(self): - with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): - functions = {'test.ping': ping} - self.schedule = salt.utils.schedule.Schedule(copy.deepcopy(DEFAULT_CONFIG), functions, returners={}) - self.schedule.opts['loop_interval'] = 1 + """ - self.schedule.opts['grains']['whens'] = {'tea time': '11/29/2017 12:00pm'} + def setUp(self): + with patch("salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)): + functions = {"test.ping": ping} + self.schedule = salt.utils.schedule.Schedule( + copy.deepcopy(DEFAULT_CONFIG), functions, returners={} + ) + self.schedule.opts["loop_interval"] = 1 + + self.schedule.opts["grains"]["whens"] = {"tea time": "11/29/2017 12:00pm"} def tearDown(self): self.schedule.reset() - @skipIf(not HAS_CRONITER, 'Cannot find croniter python module') + @skipIf(not HAS_CRONITER, "Cannot find croniter python module") def test_eval_cron_invalid(self): - ''' + """ verify that scheduled job runs - ''' - job = { - 'schedule': { - 'job1': { - 'function': 'test.ping', - 'cron': '0 16 29 13 *' - } - } - } + """ + job = {"schedule": {"job1": {"function": "test.ping", "cron": "0 16 29 13 *"}}} # Add the job to the scheduler self.schedule.opts.update(job) - run_time = dateutil_parser.parse('11/29/2017 4:00pm') - with patch('croniter.croniter.get_next', MagicMock(return_value=run_time)): + run_time = dateutil_parser.parse("11/29/2017 4:00pm") + with patch("croniter.croniter.get_next", MagicMock(return_value=run_time)): self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') - self.assertEqual(ret['_error'], - 'Invalid cron string. Ignoring job job1.') + ret = self.schedule.job_status("job1") + self.assertEqual(ret["_error"], "Invalid cron string. Ignoring job job1.") def test_eval_when_invalid_date(self): - ''' + """ verify that scheduled job does not run and returns the right error - ''' - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + """ + run_time = dateutil_parser.parse("11/29/2017 4:00pm") job = { - 'schedule': { - 'job1': { - 'function': 'test.ping', - 'when': '13/29/2017 1:00pm', - } - } + "schedule": {"job1": {"function": "test.ping", "when": "13/29/2017 1:00pm"}} } # Add the job to the scheduler @@ -99,161 +90,156 @@ class SchedulerErrorTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate 1 second before the run time self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') - self.assertEqual(ret['_error'], - 'Invalid date string 13/29/2017 1:00pm. Ignoring job job1.') + ret = self.schedule.job_status("job1") + self.assertEqual( + ret["_error"], "Invalid date string 13/29/2017 1:00pm. Ignoring job job1." + ) def test_eval_whens_grain_not_dict(self): - ''' + """ verify that scheduled job does not run and returns the right error - ''' - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + """ + run_time = dateutil_parser.parse("11/29/2017 4:00pm") - job = { - 'schedule': { - 'job1': { - 'function': 'test.ping', - 'when': 'tea time', - } - } - } + job = {"schedule": {"job1": {"function": "test.ping", "when": "tea time"}}} - self.schedule.opts['grains']['whens'] = ['tea time'] + self.schedule.opts["grains"]["whens"] = ["tea time"] # Add the job to the scheduler self.schedule.opts.update(job) # Evaluate 1 second before the run time self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') - self.assertEqual(ret['_error'], - 'Grain "whens" must be a dict. Ignoring job job1.') + ret = self.schedule.job_status("job1") + self.assertEqual( + ret["_error"], 'Grain "whens" must be a dict. Ignoring job job1.' + ) def test_eval_once_invalid_datestring(self): - ''' + """ verify that scheduled job does not run and returns the right error - ''' + """ job = { - 'schedule': { - 'job1': { - 'function': 'test.ping', - 'once': '2017-13-13T13:00:00', + "schedule": { + "job1": {"function": "test.ping", "once": "2017-13-13T13:00:00"} } - } } - run_time = dateutil_parser.parse('12/13/2017 1:00pm') + run_time = dateutil_parser.parse("12/13/2017 1:00pm") # Add the job to the scheduler self.schedule.opts.update(job) # Evaluate 1 second at the run time self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') - _expected = ('Date string could not be parsed: ' - '2017-13-13T13:00:00, %Y-%m-%dT%H:%M:%S. ' - 'Ignoring job job1.') - self.assertEqual(ret['_error'], _expected) + ret = self.schedule.job_status("job1") + _expected = ( + "Date string could not be parsed: " + "2017-13-13T13:00:00, %Y-%m-%dT%H:%M:%S. " + "Ignoring job job1." + ) + self.assertEqual(ret["_error"], _expected) def test_eval_skip_during_range_invalid_date(self): - ''' + """ verify that scheduled job does not run and returns the right error - ''' + """ job = { - 'schedule': { - 'job1': { - 'function': 'test.ping', - 'hours': 1, - 'skip_during_range': {'start': '1:00pm', 'end': '25:00pm'} - + "schedule": { + "job1": { + "function": "test.ping", + "hours": 1, + "skip_during_range": {"start": "1:00pm", "end": "25:00pm"}, + } } - } } # Add the job to the scheduler self.schedule.opts.update(job) # eval at 3:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 3:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:00pm") self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') + ret = self.schedule.job_status("job1") # eval at 4:00pm to prime - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + run_time = dateutil_parser.parse("11/29/2017 4:00pm") self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') - _expected = ('Invalid date string for end in ' - 'skip_during_range. Ignoring ' - 'job job1.') - self.assertEqual(ret['_error'], _expected) + ret = self.schedule.job_status("job1") + _expected = ( + "Invalid date string for end in " "skip_during_range. Ignoring " "job job1." + ) + self.assertEqual(ret["_error"], _expected) def test_eval_skip_during_range_end_before_start(self): - ''' + """ verify that scheduled job does not run and returns the right error - ''' + """ job = { - 'schedule': { - 'job1': { - 'function': 'test.ping', - 'hours': 1, - 'skip_during_range': {'start': '1:00pm', 'end': '12:00pm'} - + "schedule": { + "job1": { + "function": "test.ping", + "hours": 1, + "skip_during_range": {"start": "1:00pm", "end": "12:00pm"}, + } } - } } # Add the job to the scheduler self.schedule.opts.update(job) # eval at 3:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 3:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:00pm") self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') + ret = self.schedule.job_status("job1") # eval at 4:00pm to prime - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + run_time = dateutil_parser.parse("11/29/2017 4:00pm") self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') - _expected = ('schedule.handle_func: Invalid ' - 'range, end must be larger than ' - 'start. Ignoring job job1.') - self.assertEqual(ret['_error'], _expected) + ret = self.schedule.job_status("job1") + _expected = ( + "schedule.handle_func: Invalid " + "range, end must be larger than " + "start. Ignoring job job1." + ) + self.assertEqual(ret["_error"], _expected) def test_eval_skip_during_range_not_dict(self): - ''' + """ verify that scheduled job does not run and returns the right error - ''' + """ job = { - 'schedule': { - 'job1': { - 'function': 'test.ping', - 'hours': 1, - 'skip_during_range': ['start', '1:00pm', 'end', '12:00pm'] - + "schedule": { + "job1": { + "function": "test.ping", + "hours": 1, + "skip_during_range": ["start", "1:00pm", "end", "12:00pm"], + } } - } } # Add the job to the scheduler self.schedule.opts.update(job) # eval at 3:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 3:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:00pm") self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') + ret = self.schedule.job_status("job1") # eval at 4:00pm to prime - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + run_time = dateutil_parser.parse("11/29/2017 4:00pm") self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') - _expected = ('schedule.handle_func: Invalid, ' - 'range must be specified as a ' - 'dictionary. Ignoring job job1.') - self.assertEqual(ret['_error'], _expected) + ret = self.schedule.job_status("job1") + _expected = ( + "schedule.handle_func: Invalid, " + "range must be specified as a " + "dictionary. Ignoring job job1." + ) + self.assertEqual(ret["_error"], _expected) diff --git a/tests/integration/scheduler/test_eval.py b/tests/integration/scheduler/test_eval.py index e4b41906a44..21b707732b7 100644 --- a/tests/integration/scheduler/test_eval.py +++ b/tests/integration/scheduler/test_eval.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import + import copy import datetime import logging @@ -9,75 +9,74 @@ import os import random import time +import pytest +import salt.utils.platform +import salt.utils.schedule +from salt.modules.test import ping +from tests.support.case import ModuleCase +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + try: import dateutil.parser as dateutil_parser + HAS_DATEUTIL_PARSER = True except ImportError: HAS_DATEUTIL_PARSER = False -import datetime - -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.mock import MagicMock, patch -from tests.support.unit import skipIf -from tests.support.runtests import RUNTIME_VARS - -# Import Salt libs -import salt.utils.schedule -import salt.utils.platform - -from salt.modules.test import ping try: - import croniter # pylint: disable=W0611 + import croniter # pylint: disable=unused-import + HAS_CRONITER = True except ImportError: HAS_CRONITER = False log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') -SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, "schedule-unit-tests") +SOCK_DIR = os.path.join(ROOT_DIR, "test-socks") DEFAULT_CONFIG = salt.config.minion_config(None) -DEFAULT_CONFIG['conf_dir'] = ROOT_DIR -DEFAULT_CONFIG['root_dir'] = ROOT_DIR -DEFAULT_CONFIG['sock_dir'] = SOCK_DIR -DEFAULT_CONFIG['pki_dir'] = os.path.join(ROOT_DIR, 'pki') -DEFAULT_CONFIG['cachedir'] = os.path.join(ROOT_DIR, 'cache') +DEFAULT_CONFIG["conf_dir"] = ROOT_DIR +DEFAULT_CONFIG["root_dir"] = ROOT_DIR +DEFAULT_CONFIG["sock_dir"] = SOCK_DIR +DEFAULT_CONFIG["pki_dir"] = os.path.join(ROOT_DIR, "pki") +DEFAULT_CONFIG["cachedir"] = os.path.join(ROOT_DIR, "cache") -@skipIf(HAS_DATEUTIL_PARSER is False, 'The \'dateutil.parser\' library is not available') +@skipIf(HAS_DATEUTIL_PARSER is False, "The 'dateutil.parser' library is not available") +@pytest.mark.windows_whitelisted class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the pkg module - ''' - def setUp(self): - with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): - functions = {'test.ping': ping} - self.schedule = salt.utils.schedule.Schedule(copy.deepcopy(DEFAULT_CONFIG), functions, returners={}) - self.schedule.opts['loop_interval'] = 1 + """ - self.schedule.opts['grains']['whens'] = {'tea time': '11/29/2017 12:00pm'} + def setUp(self): + with patch("salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)): + functions = {"test.ping": ping} + self.schedule = salt.utils.schedule.Schedule( + copy.deepcopy(DEFAULT_CONFIG), functions, returners={} + ) + self.schedule.opts["loop_interval"] = 1 + + self.schedule.opts["grains"]["whens"] = {"tea time": "11/29/2017 12:00pm"} def tearDown(self): self.schedule.reset() def test_eval(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval' + """ + job_name = "test_eval" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'when': '11/29/2017 4:00pm', + "schedule": { + job_name: {"function": "test.ping", "when": "11/29/2017 4:00pm"} } - } } - run_time2 = dateutil_parser.parse('11/29/2017 4:00pm') + run_time2 = dateutil_parser.parse("11/29/2017 4:00pm") run_time1 = run_time2 - datetime.timedelta(seconds=1) # Add the job to the scheduler @@ -86,34 +85,31 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate 1 second before the run time self.schedule.eval(now=run_time1) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) + self.assertNotIn("_last_run", ret) # Evaluate 1 second at the run time self.schedule.eval(now=run_time2) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time2) + self.assertEqual(ret["_last_run"], run_time2) def test_eval_multiple_whens(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval_multiple_whens' + """ + job_name = "test_eval_multiple_whens" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'when': [ - '11/29/2017 4:00pm', - '11/29/2017 5:00pm', - ], + "schedule": { + job_name: { + "function": "test.ping", + "when": ["11/29/2017 4:00pm", "11/29/2017 5:00pm"], + } } - } } if salt.utils.platform.is_darwin(): - job['schedule'][job_name]['dry_run'] = True + job["schedule"][job_name]["dry_run"] = True - run_time1 = dateutil_parser.parse('11/29/2017 4:00pm') - run_time2 = dateutil_parser.parse('11/29/2017 5:00pm') + run_time1 = dateutil_parser.parse("11/29/2017 4:00pm") + run_time2 = dateutil_parser.parse("11/29/2017 5:00pm") # Add the job to the scheduler self.schedule.opts.update(job) @@ -121,29 +117,22 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate run time1 self.schedule.eval(now=run_time1) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time1) + self.assertEqual(ret["_last_run"], run_time1) time.sleep(2) # Evaluate run time2 self.schedule.eval(now=run_time2) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time2) + self.assertEqual(ret["_last_run"], run_time2) def test_eval_whens(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval_whens' - job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'when': 'tea time', - } - } - } - run_time = dateutil_parser.parse('11/29/2017 12:00pm') + """ + job_name = "test_eval_whens" + job = {"schedule": {job_name: {"function": "test.ping", "when": "tea time"}}} + run_time = dateutil_parser.parse("11/29/2017 12:00pm") # Add the job to the scheduler self.schedule.opts.update(job) @@ -151,26 +140,23 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate run time1 self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_eval_loop_interval(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval_loop_interval' + """ + job_name = "test_eval_loop_interval" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'when': '11/29/2017 4:00pm', + "schedule": { + job_name: {"function": "test.ping", "when": "11/29/2017 4:00pm"} } - } } # 30 second loop interval LOOP_INTERVAL = random.randint(30, 59) - self.schedule.opts['loop_interval'] = LOOP_INTERVAL + self.schedule.opts["loop_interval"] = LOOP_INTERVAL - run_time2 = dateutil_parser.parse('11/29/2017 4:00pm') + run_time2 = dateutil_parser.parse("11/29/2017 4:00pm") # Add the job to the scheduler self.schedule.opts.update(job) @@ -179,33 +165,36 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): self.schedule.eval(now=run_time2 + datetime.timedelta(seconds=LOOP_INTERVAL)) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time2 + datetime.timedelta(seconds=LOOP_INTERVAL)) + self.assertEqual( + ret["_last_run"], run_time2 + datetime.timedelta(seconds=LOOP_INTERVAL) + ) def test_eval_multiple_whens_loop_interval(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval_multiple_whens_loop_interval' + """ + job_name = "test_eval_multiple_whens_loop_interval" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'when': [ - '11/29/2017 4:00pm', - '11/29/2017 5:00pm', - ], + "schedule": { + job_name: { + "function": "test.ping", + "when": ["11/29/2017 4:00pm", "11/29/2017 5:00pm"], + } } - } } if salt.utils.platform.is_darwin(): - job['schedule'][job_name]['dry_run'] = True + job["schedule"][job_name]["dry_run"] = True # 30 second loop interval LOOP_INTERVAL = random.randint(30, 59) - self.schedule.opts['loop_interval'] = LOOP_INTERVAL + self.schedule.opts["loop_interval"] = LOOP_INTERVAL - run_time1 = dateutil_parser.parse('11/29/2017 4:00pm') + datetime.timedelta(seconds=LOOP_INTERVAL) - run_time2 = dateutil_parser.parse('11/29/2017 5:00pm') + datetime.timedelta(seconds=LOOP_INTERVAL) + run_time1 = dateutil_parser.parse("11/29/2017 4:00pm") + datetime.timedelta( + seconds=LOOP_INTERVAL + ) + run_time2 = dateutil_parser.parse("11/29/2017 5:00pm") + datetime.timedelta( + seconds=LOOP_INTERVAL + ) # Add the job to the scheduler self.schedule.opts.update(job) @@ -213,58 +202,54 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate 1 second at the run time self.schedule.eval(now=run_time1) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time1) + self.assertEqual(ret["_last_run"], run_time1) time.sleep(2) # Evaluate 1 second at the run time self.schedule.eval(now=run_time2) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time2) + self.assertEqual(ret["_last_run"], run_time2) def test_eval_once(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_once' + """ + job_name = "test_once" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'once': '2017-12-13T13:00:00', + "schedule": { + job_name: {"function": "test.ping", "once": "2017-12-13T13:00:00"} } - } } - run_time = dateutil_parser.parse('12/13/2017 1:00pm') + run_time = dateutil_parser.parse("12/13/2017 1:00pm") # Add the job to the scheduler - self.schedule.opts['schedule'] = {} + self.schedule.opts["schedule"] = {} self.schedule.opts.update(job) # Evaluate 1 second at the run time self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_eval_once_loop_interval(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval_once_loop_interval' + """ + job_name = "test_eval_once_loop_interval" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'once': '2017-12-13T13:00:00', + "schedule": { + job_name: {"function": "test.ping", "once": "2017-12-13T13:00:00"} } - } } # Randomn second loop interval LOOP_INTERVAL = random.randint(0, 59) - self.schedule.opts['loop_interval'] = LOOP_INTERVAL + self.schedule.opts["loop_interval"] = LOOP_INTERVAL # Run the job at the right plus LOOP_INTERVAL - run_time = dateutil_parser.parse('12/13/2017 1:00pm') + datetime.timedelta(seconds=LOOP_INTERVAL) + run_time = dateutil_parser.parse("12/13/2017 1:00pm") + datetime.timedelta( + seconds=LOOP_INTERVAL + ) # Add the job to the scheduler self.schedule.opts.update(job) @@ -272,178 +257,165 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate at the run time self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) - @skipIf(not HAS_CRONITER, 'Cannot find croniter python module') + @skipIf(not HAS_CRONITER, "Cannot find croniter python module") def test_eval_cron(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval_cron' + """ + job_name = "test_eval_cron" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'cron': '0 16 29 11 *', - } - } + "schedule": {job_name: {"function": "test.ping", "cron": "0 16 29 11 *"}} } # Add the job to the scheduler self.schedule.opts.update(job) - run_time = dateutil_parser.parse('11/29/2017 4:00pm') - with patch('croniter.croniter.get_next', MagicMock(return_value=run_time)): + run_time = dateutil_parser.parse("11/29/2017 4:00pm") + with patch("croniter.croniter.get_next", MagicMock(return_value=run_time)): self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) - @skipIf(not HAS_CRONITER, 'Cannot find croniter python module') + @skipIf(not HAS_CRONITER, "Cannot find croniter python module") def test_eval_cron_loop_interval(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval_cron_loop_interval' + """ + job_name = "test_eval_cron_loop_interval" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'cron': '0 16 29 11 *', - } - } + "schedule": {job_name: {"function": "test.ping", "cron": "0 16 29 11 *"}} } # Randomn second loop interval LOOP_INTERVAL = random.randint(0, 59) - self.schedule.opts['loop_interval'] = LOOP_INTERVAL + self.schedule.opts["loop_interval"] = LOOP_INTERVAL # Add the job to the scheduler self.schedule.opts.update(job) - run_time = dateutil_parser.parse('11/29/2017 4:00pm') - with patch('croniter.croniter.get_next', MagicMock(return_value=run_time)): + run_time = dateutil_parser.parse("11/29/2017 4:00pm") + with patch("croniter.croniter.get_next", MagicMock(return_value=run_time)): self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_eval_until(self): - ''' + """ verify that scheduled job is skipped once the current time reaches the specified until time - ''' - job_name = 'test_eval_until' + """ + job_name = "test_eval_until" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'hours': '1', - 'until': '11/29/2017 5:00pm', + "schedule": { + job_name: { + "function": "test.ping", + "hours": "1", + "until": "11/29/2017 5:00pm", + } } - } } if salt.utils.platform.is_darwin(): - job['schedule'][job_name]['dry_run'] = True + job["schedule"][job_name]["dry_run"] = True # Add job to schedule - self.schedule.delete_job('test_eval_until') + self.schedule.delete_job("test_eval_until") self.schedule.opts.update(job) # eval at 2:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # eval at 3:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 3:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) time.sleep(2) # eval at 4:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + run_time = dateutil_parser.parse("11/29/2017 4:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) time.sleep(2) # eval at 5:00pm, will not run - run_time = dateutil_parser.parse('11/29/2017 5:00pm') + run_time = dateutil_parser.parse("11/29/2017 5:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_skip_reason'], 'until_passed') - self.assertEqual(ret['_skipped_time'], run_time) + self.assertEqual(ret["_skip_reason"], "until_passed") + self.assertEqual(ret["_skipped_time"], run_time) def test_eval_after(self): - ''' + """ verify that scheduled job is skipped until after the specified time has been reached. - ''' - job_name = 'test_eval_after' + """ + job_name = "test_eval_after" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'hours': '1', - 'after': '11/29/2017 5:00pm', + "schedule": { + job_name: { + "function": "test.ping", + "hours": "1", + "after": "11/29/2017 5:00pm", + } } - } } # Add job to schedule self.schedule.opts.update(job) # eval at 2:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # eval at 3:00pm, will not run. - run_time = dateutil_parser.parse('11/29/2017 3:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_skip_reason'], 'after_not_passed') - self.assertEqual(ret['_skipped_time'], run_time) + self.assertEqual(ret["_skip_reason"], "after_not_passed") + self.assertEqual(ret["_skipped_time"], run_time) # eval at 4:00pm, will not run. - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + run_time = dateutil_parser.parse("11/29/2017 4:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_skip_reason'], 'after_not_passed') - self.assertEqual(ret['_skipped_time'], run_time) + self.assertEqual(ret["_skip_reason"], "after_not_passed") + self.assertEqual(ret["_skipped_time"], run_time) # eval at 5:00pm, will not run - run_time = dateutil_parser.parse('11/29/2017 5:00pm') + run_time = dateutil_parser.parse("11/29/2017 5:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_skip_reason'], 'after_not_passed') - self.assertEqual(ret['_skipped_time'], run_time) + self.assertEqual(ret["_skip_reason"], "after_not_passed") + self.assertEqual(ret["_skipped_time"], run_time) # eval at 6:00pm, will run - run_time = dateutil_parser.parse('11/29/2017 6:00pm') + run_time = dateutil_parser.parse("11/29/2017 6:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_eval_enabled(self): - ''' + """ verify that scheduled job does not run - ''' - job_name = 'test_eval_enabled' + """ + job_name = "test_eval_enabled" job = { - 'schedule': { - 'enabled': True, - job_name: { - 'function': 'test.ping', - 'when': '11/29/2017 4:00pm', + "schedule": { + "enabled": True, + job_name: {"function": "test.ping", "when": "11/29/2017 4:00pm"}, } - } } - run_time1 = dateutil_parser.parse('11/29/2017 4:00pm') + run_time1 = dateutil_parser.parse("11/29/2017 4:00pm") # Add the job to the scheduler self.schedule.opts.update(job) @@ -451,25 +423,22 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate 1 second at the run time self.schedule.eval(now=run_time1) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time1) + self.assertEqual(ret["_last_run"], run_time1) def test_eval_enabled_key(self): - ''' + """ verify that scheduled job runs when the enabled key is in place https://github.com/saltstack/salt/issues/47695 - ''' - job_name = 'test_eval_enabled_key' + """ + job_name = "test_eval_enabled_key" job = { - 'schedule': { - 'enabled': True, - job_name: { - 'function': 'test.ping', - 'when': '11/29/2017 4:00pm', + "schedule": { + "enabled": True, + job_name: {"function": "test.ping", "when": "11/29/2017 4:00pm"}, } - } } - run_time2 = dateutil_parser.parse('11/29/2017 4:00pm') + run_time2 = dateutil_parser.parse("11/29/2017 4:00pm") run_time1 = run_time2 - datetime.timedelta(seconds=1) # Add the job to the scheduler @@ -477,29 +446,26 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate 1 second before the run time self.schedule.eval(now=run_time1) - ret = self.schedule.job_status('test_eval_enabled_key') - self.assertNotIn('_last_run', ret) + ret = self.schedule.job_status("test_eval_enabled_key") + self.assertNotIn("_last_run", ret) # Evaluate 1 second at the run time self.schedule.eval(now=run_time2) - ret = self.schedule.job_status('test_eval_enabled_key') - self.assertEqual(ret['_last_run'], run_time2) + ret = self.schedule.job_status("test_eval_enabled_key") + self.assertEqual(ret["_last_run"], run_time2) def test_eval_disabled(self): - ''' + """ verify that scheduled job does not run - ''' - job_name = 'test_eval_disabled' + """ + job_name = "test_eval_disabled" job = { - 'schedule': { - 'enabled': False, - job_name: { - 'function': 'test.ping', - 'when': '11/29/2017 4:00pm', + "schedule": { + "enabled": False, + job_name: {"function": "test.ping", "when": "11/29/2017 4:00pm"}, } - } } - run_time1 = dateutil_parser.parse('11/29/2017 4:00pm') + run_time1 = dateutil_parser.parse("11/29/2017 4:00pm") # Add the job to the scheduler self.schedule.opts.update(job) @@ -507,28 +473,28 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate 1 second at the run time self.schedule.eval(now=run_time1) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_skip_reason'], 'disabled') + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_skip_reason"], "disabled") # Ensure job data still matches - self.assertEqual(ret, job['schedule'][job_name]) + self.assertEqual(ret, job["schedule"][job_name]) def test_eval_global_disabled_job_enabled(self): - ''' + """ verify that scheduled job does not run - ''' - job_name = 'test_eval_global_disabled' + """ + job_name = "test_eval_global_disabled" job = { - 'schedule': { - 'enabled': False, - job_name: { - 'function': 'test.ping', - 'when': '11/29/2017 4:00pm', - 'enabled': True, + "schedule": { + "enabled": False, + job_name: { + "function": "test.ping", + "when": "11/29/2017 4:00pm", + "enabled": True, + }, } - } } - run_time1 = dateutil_parser.parse('11/29/2017 4:00pm') + run_time1 = dateutil_parser.parse("11/29/2017 4:00pm") # Add the job to the scheduler self.schedule.opts.update(job) @@ -536,446 +502,410 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # Evaluate 1 second at the run time self.schedule.eval(now=run_time1) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_skip_reason'], 'disabled') + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_skip_reason"], "disabled") # Ensure job is still enabled - self.assertEqual(ret['enabled'], True) + self.assertEqual(ret["enabled"], True) def test_eval_run_on_start(self): - ''' + """ verify that scheduled job is run when minion starts - ''' - job_name = 'test_eval_run_on_start' + """ + job_name = "test_eval_run_on_start" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'hours': '1', - 'run_on_start': True, + "schedule": { + job_name: {"function": "test.ping", "hours": "1", "run_on_start": True} } - } } # Add job to schedule self.schedule.opts.update(job) # eval at 2:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) # eval at 3:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 3:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) def test_eval_splay(self): - ''' + """ verify that scheduled job runs with splayed time - ''' - job_name = 'job_eval_splay' + """ + job_name = "job_eval_splay" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'seconds': '30', - 'splay': '10', + "schedule": { + job_name: {"function": "test.ping", "seconds": "30", "splay": "10"} } - } } # Add job to schedule self.schedule.opts.update(job) - with patch('random.randint', MagicMock(return_value=10)): + with patch("random.randint", MagicMock(return_value=10)): # eval at 2:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # eval at 2:00:40pm, will run. - run_time = dateutil_parser.parse('11/29/2017 2:00:40pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:40pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_eval_splay_range(self): - ''' + """ verify that scheduled job runs with splayed time - ''' - job_name = 'job_eval_splay_range' + """ + job_name = "job_eval_splay_range" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'seconds': '30', - 'splay': {'start': 5, 'end': 10}, + "schedule": { + job_name: { + "function": "test.ping", + "seconds": "30", + "splay": {"start": 5, "end": 10}, + } } - } } # Add job to schedule self.schedule.opts.update(job) - with patch('random.randint', MagicMock(return_value=10)): + with patch("random.randint", MagicMock(return_value=10)): # eval at 2:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # eval at 2:00:40pm, will run. - run_time = dateutil_parser.parse('11/29/2017 2:00:40pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:40pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_eval_splay_global(self): - ''' + """ verify that scheduled job runs with splayed time - ''' - job_name = 'job_eval_splay_global' + """ + job_name = "job_eval_splay_global" job = { - 'schedule': { - 'splay': {'start': 5, 'end': 10}, - job_name: { - 'function': 'test.ping', - 'seconds': '30', + "schedule": { + "splay": {"start": 5, "end": 10}, + job_name: {"function": "test.ping", "seconds": "30"}, } - } } # Add job to schedule self.schedule.opts.update(job) - with patch('random.randint', MagicMock(return_value=10)): + with patch("random.randint", MagicMock(return_value=10)): # eval at 2:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # eval at 2:00:40pm, will run. - run_time = dateutil_parser.parse('11/29/2017 2:00:40pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:40pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_eval_seconds(self): - ''' + """ verify that scheduled job run mutiple times with seconds - ''' - job_name = 'job_eval_seconds' - job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'seconds': '30', - } - } - } + """ + job_name = "job_eval_seconds" + job = {"schedule": {job_name: {"function": "test.ping", "seconds": "30"}}} if salt.utils.platform.is_darwin(): - job['schedule'][job_name]['dry_run'] = True + job["schedule"][job_name]["dry_run"] = True # Add job to schedule self.schedule.opts.update(job) # eval at 2:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") next_run_time = run_time + datetime.timedelta(seconds=30) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) # eval at 2:00:01pm, will not run. - run_time = dateutil_parser.parse('11/29/2017 2:00:01pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:01pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_next_fire_time"], next_run_time) # eval at 2:00:30pm, will run. - run_time = dateutil_parser.parse('11/29/2017 2:00:30pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:30pm") next_run_time = run_time + datetime.timedelta(seconds=30) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_last_run"], run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) time.sleep(2) # eval at 2:01:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 2:01:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:01:00pm") next_run_time = run_time + datetime.timedelta(seconds=30) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_last_run"], run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) time.sleep(2) # eval at 2:01:30pm, will run. - run_time = dateutil_parser.parse('11/29/2017 2:01:30pm') + run_time = dateutil_parser.parse("11/29/2017 2:01:30pm") next_run_time = run_time + datetime.timedelta(seconds=30) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_last_run"], run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) def test_eval_minutes(self): - ''' + """ verify that scheduled job run mutiple times with minutes - ''' - job_name = 'job_eval_minutes' - job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'minutes': '30', - } - } - } + """ + job_name = "job_eval_minutes" + job = {"schedule": {job_name: {"function": "test.ping", "minutes": "30"}}} if salt.utils.platform.is_darwin(): - job['schedule'][job_name]['dry_run'] = True + job["schedule"][job_name]["dry_run"] = True # Add job to schedule self.schedule.opts.update(job) # eval at 2:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") next_run_time = run_time + datetime.timedelta(minutes=30) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) # eval at 2:00:01pm, will not run. - run_time = dateutil_parser.parse('11/29/2017 2:00:01pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:01pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_next_fire_time"], next_run_time) # eval at 2:30:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 2:30:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:30:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) time.sleep(2) # eval at 3:00:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 3:00:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:00:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) time.sleep(2) # eval at 3:30:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 3:30:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:30:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_eval_hours(self): - ''' + """ verify that scheduled job run mutiple times with hours - ''' - job_name = 'job_eval_hours' - job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'hours': '2', - } - } - } + """ + job_name = "job_eval_hours" + job = {"schedule": {job_name: {"function": "test.ping", "hours": "2"}}} if salt.utils.platform.is_darwin(): - job['schedule'][job_name]['dry_run'] = True + job["schedule"][job_name]["dry_run"] = True # Add job to schedule self.schedule.opts.update(job) # eval at 2:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") next_run_time = run_time + datetime.timedelta(hours=2) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) # eval at 2:00:01pm, will not run. - run_time = dateutil_parser.parse('11/29/2017 2:00:01pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:01pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_next_fire_time"], next_run_time) # eval at 4:00:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 4:00:00pm') + run_time = dateutil_parser.parse("11/29/2017 4:00:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) time.sleep(2) # eval at 6:00:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 6:00:00pm') + run_time = dateutil_parser.parse("11/29/2017 6:00:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) time.sleep(2) # eval at 8:00:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 8:00:00pm') + run_time = dateutil_parser.parse("11/29/2017 8:00:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_eval_days(self): - ''' + """ verify that scheduled job run mutiple times with days - ''' - job_name = 'job_eval_days' + """ + job_name = "job_eval_days" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'days': '2', - 'dry_run': True + "schedule": { + job_name: {"function": "test.ping", "days": "2", "dry_run": True} } - } } if salt.utils.platform.is_darwin(): - job['schedule'][job_name]['dry_run'] = True + job["schedule"][job_name]["dry_run"] = True # Add job to schedule self.schedule.opts.update(job) # eval at 11/23/2017 2:00pm to prime, simulate minion start up. - run_time = dateutil_parser.parse('11/23/2017 2:00pm') + run_time = dateutil_parser.parse("11/23/2017 2:00pm") next_run_time = run_time + datetime.timedelta(days=2) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) # eval at 11/25/2017 2:00:00pm, will run. - run_time = dateutil_parser.parse('11/25/2017 2:00:00pm') + run_time = dateutil_parser.parse("11/25/2017 2:00:00pm") next_run_time = run_time + datetime.timedelta(days=2) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_last_run"], run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) # eval at 11/26/2017 2:00:00pm, will not run. - run_time = dateutil_parser.parse('11/26/2017 2:00:00pm') + run_time = dateutil_parser.parse("11/26/2017 2:00:00pm") last_run_time = run_time - datetime.timedelta(days=1) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], last_run_time) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_last_run"], last_run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) time.sleep(2) # eval at 11/27/2017 2:00:00pm, will run. - run_time = dateutil_parser.parse('11/27/2017 2:00:00pm') + run_time = dateutil_parser.parse("11/27/2017 2:00:00pm") next_run_time = run_time + datetime.timedelta(days=2) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_last_run"], run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) time.sleep(2) # eval at 11/28/2017 2:00:00pm, will not run. - run_time = dateutil_parser.parse('11/28/2017 2:00:00pm') + run_time = dateutil_parser.parse("11/28/2017 2:00:00pm") last_run_time = run_time - datetime.timedelta(days=1) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], last_run_time) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_last_run"], last_run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) time.sleep(2) # eval at 11/29/2017 2:00:00pm, will run. - run_time = dateutil_parser.parse('11/29/2017 2:00:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:00pm") next_run_time = run_time + datetime.timedelta(days=2) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) - self.assertEqual(ret['_next_fire_time'], next_run_time) + self.assertEqual(ret["_last_run"], run_time) + self.assertEqual(ret["_next_fire_time"], next_run_time) def test_eval_when_splay(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval_when_splay' + """ + job_name = "test_eval_when_splay" splay = 300 job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'when': '11/29/2017 4:00pm', - 'splay': splay + "schedule": { + job_name: { + "function": "test.ping", + "when": "11/29/2017 4:00pm", + "splay": splay, + } } - } } - run_time1 = dateutil_parser.parse('11/29/2017 4:00pm') + run_time1 = dateutil_parser.parse("11/29/2017 4:00pm") run_time2 = run_time1 + datetime.timedelta(seconds=splay) run_time3 = run_time2 + datetime.timedelta(seconds=1) # Add the job to the scheduler self.schedule.opts.update(job) - with patch('random.randint', MagicMock(return_value=splay)): + with patch("random.randint", MagicMock(return_value=splay)): # Evaluate to prime - run_time = dateutil_parser.parse('11/29/2017 3:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # Evaluate at expected runtime1, should not run self.schedule.eval(now=run_time1) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) + self.assertNotIn("_last_run", ret) # Evaluate at expected runtime2, should run self.schedule.eval(now=run_time2) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time2) + self.assertEqual(ret["_last_run"], run_time2) # Evaluate at expected runtime3, should not run # _next_fire_time should be None self.schedule.eval(now=run_time3) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time2) - self.assertEqual(ret['_next_fire_time'], None) + self.assertEqual(ret["_last_run"], run_time2) + self.assertEqual(ret["_next_fire_time"], None) def test_eval_when_splay_in_past(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_eval_when_splay_in_past' + """ + job_name = "test_eval_when_splay_in_past" splay = 300 job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'when': ['11/29/2017 6:00am'], - 'splay': splay + "schedule": { + job_name: { + "function": "test.ping", + "when": ["11/29/2017 6:00am"], + "splay": splay, + } } - } } - run_time1 = dateutil_parser.parse('11/29/2017 4:00pm') + run_time1 = dateutil_parser.parse("11/29/2017 4:00pm") # Add the job to the scheduler self.schedule.opts.update(job) # Evaluate to prime - run_time = dateutil_parser.parse('11/29/2017 3:00pm') + run_time = dateutil_parser.parse("11/29/2017 3:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) @@ -983,5 +913,5 @@ class SchedulerEvalTest(ModuleCase, SaltReturnAssertsMixin): # and _next_fire_time should be None self.schedule.eval(now=run_time1) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_next_fire_time'], None) + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_next_fire_time"], None) diff --git a/tests/integration/scheduler/test_helpers.py b/tests/integration/scheduler/test_helpers.py index e513e3bd9db..d3c8b8f8a11 100644 --- a/tests/integration/scheduler/test_helpers.py +++ b/tests/integration/scheduler/test_helpers.py @@ -1,66 +1,64 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import + import copy import logging import os -# Import Salt Testing libs +import pytest +import salt.utils.platform +import salt.utils.schedule +from salt.modules.test import ping from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin from tests.support.mock import MagicMock, patch from tests.support.runtests import RUNTIME_VARS -# Import Salt libs -import salt.utils.schedule -import salt.utils.platform - -from salt.modules.test import ping - log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') -SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, "schedule-unit-tests") +SOCK_DIR = os.path.join(ROOT_DIR, "test-socks") DEFAULT_CONFIG = salt.config.minion_config(None) -DEFAULT_CONFIG['conf_dir'] = ROOT_DIR -DEFAULT_CONFIG['root_dir'] = ROOT_DIR -DEFAULT_CONFIG['sock_dir'] = SOCK_DIR -DEFAULT_CONFIG['pki_dir'] = os.path.join(ROOT_DIR, 'pki') -DEFAULT_CONFIG['cachedir'] = os.path.join(ROOT_DIR, 'cache') +DEFAULT_CONFIG["conf_dir"] = ROOT_DIR +DEFAULT_CONFIG["root_dir"] = ROOT_DIR +DEFAULT_CONFIG["sock_dir"] = SOCK_DIR +DEFAULT_CONFIG["pki_dir"] = os.path.join(ROOT_DIR, "pki") +DEFAULT_CONFIG["cachedir"] = os.path.join(ROOT_DIR, "cache") +@pytest.mark.windows_whitelisted class SchedulerHelpersTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Test scheduler helper functions - ''' + """ + def setUp(self): - with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): - functions = {'test.ping': ping} - self.schedule = salt.utils.schedule.Schedule(copy.deepcopy(DEFAULT_CONFIG), functions, returners={}) - self.schedule.opts['loop_interval'] = 1 + with patch("salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)): + functions = {"test.ping": ping} + self.schedule = salt.utils.schedule.Schedule( + copy.deepcopy(DEFAULT_CONFIG), functions, returners={} + ) + self.schedule.opts["loop_interval"] = 1 def tearDown(self): self.schedule.reset() def test_get_schedule(self): - ''' + """ verify that the _get_schedule function works when remove_hidden is True and schedule data contains enabled key - ''' - job_name = 'test_get_schedule' + """ + job_name = "test_get_schedule" job = { - 'schedule': { - 'enabled': True, - job_name: { - 'function': 'test.ping', - 'seconds': 60 + "schedule": { + "enabled": True, + job_name: {"function": "test.ping", "seconds": 60}, } - } } # Add the job to the scheduler self.schedule.opts.update(job) ret = self.schedule._get_schedule(remove_hidden=True) - self.assertEqual(job['schedule'], ret) + self.assertEqual(job["schedule"], ret) diff --git a/tests/integration/scheduler/test_maxrunning.py b/tests/integration/scheduler/test_maxrunning.py index 38631237412..e09b3fa31d6 100644 --- a/tests/integration/scheduler/test_maxrunning.py +++ b/tests/integration/scheduler/test_maxrunning.py @@ -2,147 +2,163 @@ # Import Python libs from __future__ import absolute_import + import copy import logging import os import dateutil.parser as dateutil_parser +# Import Salt libs +import salt.utils.schedule +from salt.modules.test import ping + # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin from tests.support.mock import MagicMock, patch from tests.support.runtests import RUNTIME_VARS -# Import Salt libs -import salt.utils.schedule - -from salt.modules.test import ping - try: import croniter # pylint: disable=W0611 + HAS_CRONITER = True except ImportError: HAS_CRONITER = False log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') -SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, "schedule-unit-tests") +SOCK_DIR = os.path.join(ROOT_DIR, "test-socks") DEFAULT_CONFIG = salt.config.minion_config(None) -DEFAULT_CONFIG['conf_dir'] = ROOT_DIR -DEFAULT_CONFIG['root_dir'] = ROOT_DIR -DEFAULT_CONFIG['sock_dir'] = SOCK_DIR -DEFAULT_CONFIG['pki_dir'] = os.path.join(ROOT_DIR, 'pki') -DEFAULT_CONFIG['cachedir'] = os.path.join(ROOT_DIR, 'cache') +DEFAULT_CONFIG["conf_dir"] = ROOT_DIR +DEFAULT_CONFIG["root_dir"] = ROOT_DIR +DEFAULT_CONFIG["sock_dir"] = SOCK_DIR +DEFAULT_CONFIG["pki_dir"] = os.path.join(ROOT_DIR, "pki") +DEFAULT_CONFIG["cachedir"] = os.path.join(ROOT_DIR, "cache") class SchedulerMaxRunningTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the pkg module - ''' + """ + def setUp(self): - with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): - functions = {'test.ping': ping} - self.schedule = salt.utils.schedule.Schedule(copy.deepcopy(DEFAULT_CONFIG), functions, returners={}) - self.schedule.opts['loop_interval'] = 1 + with patch("salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)): + functions = {"test.ping": ping} + self.schedule = salt.utils.schedule.Schedule( + copy.deepcopy(DEFAULT_CONFIG), functions, returners={} + ) + self.schedule.opts["loop_interval"] = 1 def tearDown(self): self.schedule.reset() def test_maxrunning_minion(self): - ''' + """ verify that scheduled job runs - ''' - self.schedule.opts['__role'] = 'minion' + """ + self.schedule.opts["__role"] = "minion" job = { - 'schedule': { - 'maxrunning_minion': { - 'function': 'test.ping', - 'seconds': 10, - 'maxrunning': 1 + "schedule": { + "maxrunning_minion": { + "function": "test.ping", + "seconds": 10, + "maxrunning": 1, + } } - } } - job_data = {'function': 'test.ping', - 'run': True, - 'name': 'maxrunning_minion', - 'seconds': 10, - '_seconds': 10, - 'jid_include': True, - 'maxrunning': 1} + job_data = { + "function": "test.ping", + "run": True, + "name": "maxrunning_minion", + "seconds": 10, + "_seconds": 10, + "jid_include": True, + "maxrunning": 1, + } # Add the job to the scheduler self.schedule.opts.update(job) - running_data = [{'fun_args': [], - 'jid': '20181018165923360935', - 'schedule': 'maxrunning_minion', - 'pid': 15338, - 'fun': 'test.ping', - 'id': 'host'}] + running_data = [ + { + "fun_args": [], + "jid": "20181018165923360935", + "schedule": "maxrunning_minion", + "pid": 15338, + "fun": "test.ping", + "id": "host", + } + ] - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + run_time = dateutil_parser.parse("11/29/2017 4:00pm") - with patch('salt.utils.minion.running', - MagicMock(return_value=running_data)): - with patch('salt.utils.process.os_is_running', - MagicMock(return_value=True)): - ret = self.schedule._check_max_running('test.ping', - job_data, - self.schedule.opts, - now=run_time) - self.assertIn('_skip_reason', ret) - self.assertEqual('maxrunning', ret['_skip_reason']) - self.assertEqual(False, ret['run']) + with patch("salt.utils.minion.running", MagicMock(return_value=running_data)): + with patch( + "salt.utils.process.os_is_running", MagicMock(return_value=True) + ): + ret = self.schedule._check_max_running( + "test.ping", job_data, self.schedule.opts, now=run_time + ) + self.assertIn("_skip_reason", ret) + self.assertEqual("maxrunning", ret["_skip_reason"]) + self.assertEqual(False, ret["run"]) def test_maxrunning_master(self): - ''' + """ verify that scheduled job runs - ''' - self.schedule.opts['__role'] = 'master' + """ + self.schedule.opts["__role"] = "master" job = { - 'schedule': { - 'maxrunning_master': { - 'function': 'state.orch', - 'args': ['test.orch_test'], - 'minutes': 1, - 'maxrunning': 1 + "schedule": { + "maxrunning_master": { + "function": "state.orch", + "args": ["test.orch_test"], + "minutes": 1, + "maxrunning": 1, + } } - } } - job_data = {'function': 'state.orch', - 'fun_args': ['test.orch_test'], - 'run': True, - 'name': 'maxrunning_master', - 'minutes': 1, - 'jid_include': True, - 'maxrunning': 1} + job_data = { + "function": "state.orch", + "fun_args": ["test.orch_test"], + "run": True, + "name": "maxrunning_master", + "minutes": 1, + "jid_include": True, + "maxrunning": 1, + } # Add the job to the scheduler self.schedule.opts.update(job) - running_data = [{'fun_args': ['test.orch_test'], - 'jid': '20181018165923360935', - 'schedule': 'maxrunning_master', - 'pid': 15338, - 'fun': 'state.orch', - 'id': 'host'}] + running_data = [ + { + "fun_args": ["test.orch_test"], + "jid": "20181018165923360935", + "schedule": "maxrunning_master", + "pid": 15338, + "fun": "state.orch", + "id": "host", + } + ] - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + run_time = dateutil_parser.parse("11/29/2017 4:00pm") - with patch('salt.utils.master.get_running_jobs', - MagicMock(return_value=running_data)): - with patch('salt.utils.process.os_is_running', - MagicMock(return_value=True)): - ret = self.schedule._check_max_running('state.orch', - job_data, - self.schedule.opts, - now=run_time) - self.assertIn('_skip_reason', ret) - self.assertEqual('maxrunning', ret['_skip_reason']) - self.assertEqual(False, ret['run']) + with patch( + "salt.utils.master.get_running_jobs", MagicMock(return_value=running_data) + ): + with patch( + "salt.utils.process.os_is_running", MagicMock(return_value=True) + ): + ret = self.schedule._check_max_running( + "state.orch", job_data, self.schedule.opts, now=run_time + ) + self.assertIn("_skip_reason", ret) + self.assertEqual("maxrunning", ret["_skip_reason"]) + self.assertEqual(False, ret["run"]) diff --git a/tests/integration/scheduler/test_postpone.py b/tests/integration/scheduler/test_postpone.py index 5d466228b3a..063d0818000 100644 --- a/tests/integration/scheduler/test_postpone.py +++ b/tests/integration/scheduler/test_postpone.py @@ -1,65 +1,60 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import + import copy import datetime import logging import os import dateutil.parser as dateutil_parser - -# Import Salt Testing libs +import pytest +import salt.utils.schedule +from salt.modules.test import ping from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin from tests.support.mock import MagicMock, patch from tests.support.runtests import RUNTIME_VARS -# Import Salt libs -import salt.utils.schedule - -from salt.modules.test import ping - log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') -SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, "schedule-unit-tests") +SOCK_DIR = os.path.join(ROOT_DIR, "test-socks") DEFAULT_CONFIG = salt.config.minion_config(None) -DEFAULT_CONFIG['conf_dir'] = ROOT_DIR -DEFAULT_CONFIG['root_dir'] = ROOT_DIR -DEFAULT_CONFIG['sock_dir'] = SOCK_DIR -DEFAULT_CONFIG['pki_dir'] = os.path.join(ROOT_DIR, 'pki') -DEFAULT_CONFIG['cachedir'] = os.path.join(ROOT_DIR, 'cache') +DEFAULT_CONFIG["conf_dir"] = ROOT_DIR +DEFAULT_CONFIG["root_dir"] = ROOT_DIR +DEFAULT_CONFIG["sock_dir"] = SOCK_DIR +DEFAULT_CONFIG["pki_dir"] = os.path.join(ROOT_DIR, "pki") +DEFAULT_CONFIG["cachedir"] = os.path.join(ROOT_DIR, "cache") +@pytest.mark.windows_whitelisted class SchedulerPostponeTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the pkg module - ''' + """ + def setUp(self): - with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): - functions = {'test.ping': ping} - self.schedule = salt.utils.schedule.Schedule(copy.deepcopy(DEFAULT_CONFIG), functions, returners={}) - self.schedule.opts['loop_interval'] = 1 + with patch("salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)): + functions = {"test.ping": ping} + self.schedule = salt.utils.schedule.Schedule( + copy.deepcopy(DEFAULT_CONFIG), functions, returners={} + ) + self.schedule.opts["loop_interval"] = 1 def tearDown(self): self.schedule.reset() def test_postpone(self): - ''' + """ verify that scheduled job is postponed until the specified time. - ''' + """ job = { - 'schedule': { - 'job1': { - 'function': 'test.ping', - 'when': '11/29/2017 4pm', - } - } + "schedule": {"job1": {"function": "test.ping", "when": "11/29/2017 4pm"}} } # 11/29/2017 4pm - run_time = dateutil_parser.parse('11/29/2017 4:00pm') + run_time = dateutil_parser.parse("11/29/2017 4:00pm") # 5 minute delay delay = 300 @@ -68,19 +63,26 @@ class SchedulerPostponeTest(ModuleCase, SaltReturnAssertsMixin): self.schedule.opts.update(job) # Postpone the job by 5 minutes - self.schedule.postpone_job('job1', {'time': run_time.strftime('%Y-%m-%dT%H:%M:%S'), - 'new_time': (run_time + datetime.timedelta(seconds=delay)).strftime('%Y-%m-%dT%H:%M:%S')}) + self.schedule.postpone_job( + "job1", + { + "time": run_time.strftime("%Y-%m-%dT%H:%M:%S"), + "new_time": (run_time + datetime.timedelta(seconds=delay)).strftime( + "%Y-%m-%dT%H:%M:%S" + ), + }, + ) # Run at the original time self.schedule.eval(now=run_time) - ret = self.schedule.job_status('job1') - self.assertNotIn('_last_run', ret) + ret = self.schedule.job_status("job1") + self.assertNotIn("_last_run", ret) # Run 5 minutes later self.schedule.eval(now=run_time + datetime.timedelta(seconds=delay)) - ret = self.schedule.job_status('job1') - self.assertEqual(ret['_last_run'], run_time + datetime.timedelta(seconds=delay)) + ret = self.schedule.job_status("job1") + self.assertEqual(ret["_last_run"], run_time + datetime.timedelta(seconds=delay)) # Run 6 minutes later self.schedule.eval(now=run_time + datetime.timedelta(seconds=delay + 1)) - ret = self.schedule.job_status('job1') - self.assertEqual(ret['_last_run'], run_time + datetime.timedelta(seconds=delay)) + ret = self.schedule.job_status("job1") + self.assertEqual(ret["_last_run"], run_time + datetime.timedelta(seconds=delay)) diff --git a/tests/integration/scheduler/test_run_job.py b/tests/integration/scheduler/test_run_job.py index afdcc062ee3..ad58751d1d0 100644 --- a/tests/integration/scheduler/test_run_job.py +++ b/tests/integration/scheduler/test_run_job.py @@ -2,70 +2,69 @@ # Import Python libs from __future__ import absolute_import + import copy import logging import os +import salt.utils.platform + +# Import Salt libs +import salt.utils.schedule +from salt.modules.test import ping + # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin from tests.support.mock import MagicMock, patch from tests.support.runtests import RUNTIME_VARS -# Import Salt libs -import salt.utils.schedule -import salt.utils.platform - -from salt.modules.test import ping - try: import croniter # pylint: disable=W0611 + HAS_CRONITER = True except ImportError: HAS_CRONITER = False log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') -SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, "schedule-unit-tests") +SOCK_DIR = os.path.join(ROOT_DIR, "test-socks") DEFAULT_CONFIG = salt.config.minion_config(None) -DEFAULT_CONFIG['conf_dir'] = ROOT_DIR -DEFAULT_CONFIG['root_dir'] = ROOT_DIR -DEFAULT_CONFIG['sock_dir'] = SOCK_DIR -DEFAULT_CONFIG['pki_dir'] = os.path.join(ROOT_DIR, 'pki') -DEFAULT_CONFIG['cachedir'] = os.path.join(ROOT_DIR, 'cache') +DEFAULT_CONFIG["conf_dir"] = ROOT_DIR +DEFAULT_CONFIG["root_dir"] = ROOT_DIR +DEFAULT_CONFIG["sock_dir"] = SOCK_DIR +DEFAULT_CONFIG["pki_dir"] = os.path.join(ROOT_DIR, "pki") +DEFAULT_CONFIG["cachedir"] = os.path.join(ROOT_DIR, "cache") class SchedulerRunJobTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the pkg module - ''' + """ + def setUp(self): - with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): - functions = {'test.ping': ping} - self.schedule = salt.utils.schedule.Schedule(copy.deepcopy(DEFAULT_CONFIG), functions, returners={}) - self.schedule.opts['loop_interval'] = 1 + with patch("salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)): + functions = {"test.ping": ping} + self.schedule = salt.utils.schedule.Schedule( + copy.deepcopy(DEFAULT_CONFIG), functions, returners={} + ) + self.schedule.opts["loop_interval"] = 1 def tearDown(self): self.schedule.reset() def test_run_job(self): - ''' + """ verify that scheduled job runs - ''' - job_name = 'test_run_job' - job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - } - } - } + """ + job_name = "test_run_job" + job = {"schedule": {job_name: {"function": "test.ping"}}} # Add the job to the scheduler self.schedule.opts.update(job) # Run job self.schedule.run_job(job_name) ret = self.schedule.job_status(job_name) - expected = {'function': 'test.ping', 'run': True, 'name': 'test_run_job'} + expected = {"function": "test.ping", "run": True, "name": "test_run_job"} self.assertEqual(ret, expected) diff --git a/tests/integration/scheduler/test_skip.py b/tests/integration/scheduler/test_skip.py index 4bc23a6f458..4a864b1871f 100644 --- a/tests/integration/scheduler/test_skip.py +++ b/tests/integration/scheduler/test_skip.py @@ -1,157 +1,156 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import + import copy import logging import os import dateutil.parser as dateutil_parser - -# Import Salt Testing libs +import pytest +import salt.utils.schedule +from salt.modules.test import ping from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin from tests.support.mock import MagicMock, patch from tests.support.runtests import RUNTIME_VARS -# Import Salt libs -import salt.utils.schedule - -from salt.modules.test import ping - log = logging.getLogger(__name__) -ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') -SOCK_DIR = os.path.join(ROOT_DIR, 'test-socks') +ROOT_DIR = os.path.join(RUNTIME_VARS.TMP, "schedule-unit-tests") +SOCK_DIR = os.path.join(ROOT_DIR, "test-socks") DEFAULT_CONFIG = salt.config.minion_config(None) -DEFAULT_CONFIG['conf_dir'] = ROOT_DIR -DEFAULT_CONFIG['root_dir'] = ROOT_DIR -DEFAULT_CONFIG['sock_dir'] = SOCK_DIR -DEFAULT_CONFIG['pki_dir'] = os.path.join(ROOT_DIR, 'pki') -DEFAULT_CONFIG['cachedir'] = os.path.join(ROOT_DIR, 'cache') +DEFAULT_CONFIG["conf_dir"] = ROOT_DIR +DEFAULT_CONFIG["root_dir"] = ROOT_DIR +DEFAULT_CONFIG["sock_dir"] = SOCK_DIR +DEFAULT_CONFIG["pki_dir"] = os.path.join(ROOT_DIR, "pki") +DEFAULT_CONFIG["cachedir"] = os.path.join(ROOT_DIR, "cache") +@pytest.mark.windows_whitelisted class SchedulerSkipTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the pkg module - ''' + """ + def setUp(self): - with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): - functions = {'test.ping': ping} - self.schedule = salt.utils.schedule.Schedule(copy.deepcopy(DEFAULT_CONFIG), functions, returners={}) - self.schedule.opts['loop_interval'] = 1 + with patch("salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)): + functions = {"test.ping": ping} + self.schedule = salt.utils.schedule.Schedule( + copy.deepcopy(DEFAULT_CONFIG), functions, returners={} + ) + self.schedule.opts["loop_interval"] = 1 def tearDown(self): self.schedule.reset() def test_skip(self): - ''' + """ verify that scheduled job is skipped at the specified time - ''' - job_name = 'test_skip' + """ + job_name = "test_skip" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'when': ['11/29/2017 4pm', '11/29/2017 5pm'], + "schedule": { + job_name: { + "function": "test.ping", + "when": ["11/29/2017 4pm", "11/29/2017 5pm"], + } } - } } # Add job to schedule self.schedule.opts.update(job) - run_time = dateutil_parser.parse('11/29/2017 4:00pm') - self.schedule.skip_job(job_name, {'time': run_time.strftime('%Y-%m-%dT%H:%M:%S'), - 'time_fmt': '%Y-%m-%dT%H:%M:%S'}) + run_time = dateutil_parser.parse("11/29/2017 4:00pm") + self.schedule.skip_job( + job_name, + { + "time": run_time.strftime("%Y-%m-%dT%H:%M:%S"), + "time_fmt": "%Y-%m-%dT%H:%M:%S", + }, + ) # Run 11/29/2017 at 4pm self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_skip_reason'], 'skip_explicit') - self.assertEqual(ret['_skipped_time'], run_time) + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_skip_reason"], "skip_explicit") + self.assertEqual(ret["_skipped_time"], run_time) # Run 11/29/2017 at 5pm - run_time = dateutil_parser.parse('11/29/2017 5:00pm') + run_time = dateutil_parser.parse("11/29/2017 5:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_skip_during_range(self): - ''' + """ verify that scheduled job is skipped during the specified range - ''' - job_name = 'test_skip_during_range' + """ + job_name = "test_skip_during_range" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'hours': '1', - 'skip_during_range': { - 'start': '11/29/2017 2pm', - 'end': '11/29/2017 3pm' - }, + "schedule": { + job_name: { + "function": "test.ping", + "hours": "1", + "skip_during_range": { + "start": "11/29/2017 2pm", + "end": "11/29/2017 3pm", + }, + } } - } } # Add job to schedule self.schedule.opts.update(job) # eval at 1:30pm to prime. - run_time = dateutil_parser.parse('11/29/2017 1:30pm') + run_time = dateutil_parser.parse("11/29/2017 1:30pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # eval at 2:30pm, will not run during range. - run_time = dateutil_parser.parse('11/29/2017 2:30pm') + run_time = dateutil_parser.parse("11/29/2017 2:30pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_skip_reason'], 'in_skip_range') - self.assertEqual(ret['_skipped_time'], run_time) + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_skip_reason"], "in_skip_range") + self.assertEqual(ret["_skipped_time"], run_time) # eval at 3:30pm, will run. - run_time = dateutil_parser.parse('11/29/2017 3:30pm') + run_time = dateutil_parser.parse("11/29/2017 3:30pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_skip_during_range_invalid_datestring(self): - ''' + """ verify that scheduled job is not not and returns the right error string - ''' - run_time = dateutil_parser.parse('11/29/2017 2:30pm') + """ + run_time = dateutil_parser.parse("11/29/2017 2:30pm") - job_name1 = 'skip_during_range_invalid_datestring1' + job_name1 = "skip_during_range_invalid_datestring1" job1 = { - 'schedule': { - job_name1: { - 'function': 'test.ping', - 'hours': '1', - '_next_fire_time': run_time, - 'skip_during_range': { - 'start': '25pm', - 'end': '3pm' - }, + "schedule": { + job_name1: { + "function": "test.ping", + "hours": "1", + "_next_fire_time": run_time, + "skip_during_range": {"start": "25pm", "end": "3pm"}, + } } - } } - job_name2 = 'skip_during_range_invalid_datestring2' + job_name2 = "skip_during_range_invalid_datestring2" job2 = { - 'schedule': { - job_name2: { - 'function': 'test.ping', - 'hours': '1', - '_next_fire_time': run_time, - 'skip_during_range': { - 'start': '2pm', - 'end': '25pm' - }, + "schedule": { + job_name2: { + "function": "test.ping", + "hours": "1", + "_next_fire_time": run_time, + "skip_during_range": {"start": "2pm", "end": "25pm"}, + } } - } } # Add job1 to schedule @@ -162,13 +161,15 @@ class SchedulerSkipTest(ModuleCase, SaltReturnAssertsMixin): # Check the first job ret = self.schedule.job_status(job_name1) - _expected = ('Invalid date string for start in ' - 'skip_during_range. Ignoring ' - 'job {0}.').format(job_name1) - self.assertEqual(ret['_error'], _expected) + _expected = ( + "Invalid date string for start in " + "skip_during_range. Ignoring " + "job {0}." + ).format(job_name1) + self.assertEqual(ret["_error"], _expected) # Clear out schedule - self.schedule.opts['schedule'] = {} + self.schedule.opts["schedule"] = {} # Add job2 to schedule self.schedule.opts.update(job2) @@ -178,126 +179,121 @@ class SchedulerSkipTest(ModuleCase, SaltReturnAssertsMixin): # Check the second job ret = self.schedule.job_status(job_name2) - _expected = ('Invalid date string for end in ' - 'skip_during_range. Ignoring ' - 'job {0}.').format(job_name2) - self.assertEqual(ret['_error'], _expected) + _expected = ( + "Invalid date string for end in " "skip_during_range. Ignoring " "job {0}." + ).format(job_name2) + self.assertEqual(ret["_error"], _expected) def test_skip_during_range_global(self): - ''' + """ verify that scheduled job is skipped during the specified range - ''' - job_name = 'skip_during_range_global' + """ + job_name = "skip_during_range_global" job = { - 'schedule': { - 'skip_during_range': { - 'start': '11/29/2017 2:00pm', - 'end': '11/29/2017 3:00pm' - }, - job_name: { - 'function': 'test.ping', - 'hours': '1', + "schedule": { + "skip_during_range": { + "start": "11/29/2017 2:00pm", + "end": "11/29/2017 3:00pm", + }, + job_name: {"function": "test.ping", "hours": "1"}, } - } } # Add job to schedule self.schedule.opts.update(job) # eval at 1:30pm to prime. - run_time = dateutil_parser.parse('11/29/2017 1:30pm') + run_time = dateutil_parser.parse("11/29/2017 1:30pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # eval at 2:30pm, will not run during range. - run_time = dateutil_parser.parse('11/29/2017 2:30pm') + run_time = dateutil_parser.parse("11/29/2017 2:30pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_skip_reason'], 'in_skip_range') - self.assertEqual(ret['_skipped_time'], run_time) + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_skip_reason"], "in_skip_range") + self.assertEqual(ret["_skipped_time"], run_time) # eval at 3:30pm, will run. - run_time = dateutil_parser.parse('11/29/2017 3:30pm') + run_time = dateutil_parser.parse("11/29/2017 3:30pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_run_after_skip_range(self): - ''' + """ verify that scheduled job is skipped during the specified range - ''' - job_name = 'skip_run_after_skip_range' + """ + job_name = "skip_run_after_skip_range" job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'when': '11/29/2017 2:30pm', - 'run_after_skip_range': True, - 'skip_during_range': { - 'start': '11/29/2017 2pm', - 'end': '11/29/2017 3pm' - }, + "schedule": { + job_name: { + "function": "test.ping", + "when": "11/29/2017 2:30pm", + "run_after_skip_range": True, + "skip_during_range": { + "start": "11/29/2017 2pm", + "end": "11/29/2017 3pm", + }, + } } - } } # Add job to schedule self.schedule.opts.update(job) # eval at 2:30pm, will not run during range. - run_time = dateutil_parser.parse('11/29/2017 2:30pm') + run_time = dateutil_parser.parse("11/29/2017 2:30pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertNotIn('_last_run', ret) - self.assertEqual(ret['_skip_reason'], 'in_skip_range') - self.assertEqual(ret['_skipped_time'], run_time) + self.assertNotIn("_last_run", ret) + self.assertEqual(ret["_skip_reason"], "in_skip_range") + self.assertEqual(ret["_skipped_time"], run_time) # eval at 3:00:01pm, will run. - run_time = dateutil_parser.parse('11/29/2017 3:00:01pm') + run_time = dateutil_parser.parse("11/29/2017 3:00:01pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertEqual(ret['_last_run'], run_time) + self.assertEqual(ret["_last_run"], run_time) def test_run_seconds_skip(self): - ''' + """ verify that scheduled job is skipped during the specified range - ''' - job_name = 'run_seconds_skip' - job = { - 'schedule': { - job_name: { - 'function': 'test.ping', - 'seconds': '10', - } - } - } + """ + job_name = "run_seconds_skip" + job = {"schedule": {job_name: {"function": "test.ping", "seconds": "10"}}} # Add job to schedule self.schedule.opts.update(job) # eval at 2:00pm, to prime the scheduler - run_time = dateutil_parser.parse('11/29/2017 2:00pm') + run_time = dateutil_parser.parse("11/29/2017 2:00pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # eval at 2:00:10pm - run_time = dateutil_parser.parse('11/29/2017 2:00:10pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:10pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) # Skip at 2:00:20pm - run_time = dateutil_parser.parse('11/29/2017 2:00:20pm') - self.schedule.skip_job(job_name, {'time': run_time.strftime('%Y-%m-%dT%H:%M:%S'), - 'time_fmt': '%Y-%m-%dT%H:%M:%S'}) + run_time = dateutil_parser.parse("11/29/2017 2:00:20pm") + self.schedule.skip_job( + job_name, + { + "time": run_time.strftime("%Y-%m-%dT%H:%M:%S"), + "time_fmt": "%Y-%m-%dT%H:%M:%S", + }, + ) self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertIn('_next_fire_time', ret) - self.assertEqual(ret['_skip_reason'], 'skip_explicit') - self.assertEqual(ret['_skipped_time'], run_time) + self.assertIn("_next_fire_time", ret) + self.assertEqual(ret["_skip_reason"], "skip_explicit") + self.assertEqual(ret["_skipped_time"], run_time) # Run at 2:00:30pm - run_time = dateutil_parser.parse('11/29/2017 2:00:30pm') + run_time = dateutil_parser.parse("11/29/2017 2:00:30pm") self.schedule.eval(now=run_time) ret = self.schedule.job_status(job_name) - self.assertIn('_last_run', ret) + self.assertIn("_last_run", ret) diff --git a/tests/integration/sdb/test_env.py b/tests/integration/sdb/test_env.py index 9d03dc0763c..9491128fbf7 100644 --- a/tests/integration/sdb/test_env.py +++ b/tests/integration/sdb/test_env.py @@ -1,28 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Test case for env sdb module -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import os import textwrap -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import SaltReturnAssertsMixin - -# Import salt libs +import pytest import salt.utils.files +from tests.support.case import ModuleCase +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS +@pytest.mark.windows_whitelisted class EnvTestCase(ModuleCase, SaltReturnAssertsMixin): - def setUp(self): - self.state_name = 'test_sdb_env' - self.state_file_name = self.state_name + '.sls' - self.state_file_set_var = os.path.join(RUNTIME_VARS.BASE_FILES, self.state_file_name) - with salt.utils.files.fopen(self.state_file_set_var, 'w') as wfh: - wfh.write(textwrap.dedent('''\ + self.state_name = "test_sdb_env" + self.state_file_name = self.state_name + ".sls" + self.state_file_set_var = os.path.join( + RUNTIME_VARS.BASE_FILES, self.state_file_name + ) + with salt.utils.files.fopen(self.state_file_set_var, "w") as wfh: + wfh.write( + textwrap.dedent( + """\ set some env var: cmd.run: - name: echo {{ salt['sdb.set']('sdb://osenv/foo', 'bar') }} @@ -37,12 +40,14 @@ class EnvTestCase(ModuleCase, SaltReturnAssertsMixin): test.fail_with_changes: - name: foo {% endif %} - ''')) + """ + ) + ) def tearDown(self): os.remove(self.state_file_set_var) def test_env_module_sets_key(self): - state_key = 'test_|-always-changes-and-succeeds_|-foo_|-succeed_with_changes' - ret = self.run_function('state.sls', [self.state_name]) - self.assertTrue(ret[state_key]['result']) + state_key = "test_|-always-changes-and-succeeds_|-foo_|-succeed_with_changes" + ret = self.run_function("state.sls", [self.state_name]) + self.assertTrue(ret[state_key]["result"]) diff --git a/tests/integration/sdb/test_vault.py b/tests/integration/sdb/test_vault.py index 1a950b9a625..a2bf5f27074 100644 --- a/tests/integration/sdb/test_vault.py +++ b/tests/integration/sdb/test_vault.py @@ -1,55 +1,59 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the vault modules -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import inspect +import logging import time -# Import Salt Testing Libs -from tests.support.unit import skipIf +import salt.utils.path from tests.support.case import ModuleCase, ShellCase from tests.support.helpers import destructiveTest, flaky from tests.support.runtests import RUNTIME_VARS -# Import Salt Libs -import salt.utils.path +# Import Salt Testing Libs +from tests.support.unit import skipIf + +log = logging.getLogger(__name__) @destructiveTest -@skipIf(not salt.utils.path.which('dockerd'), 'Docker not installed') -@skipIf(not salt.utils.path.which('vault'), 'Vault not installed') +@skipIf(not salt.utils.path.which("dockerd"), "Docker not installed") +@skipIf(not salt.utils.path.which("vault"), "Vault not installed") class VaultTestCase(ModuleCase, ShellCase): - ''' + """ Test vault module - ''' + """ + count = 0 def setUp(self): - ''' + """ SetUp vault container - ''' + """ if VaultTestCase.count == 0: config = '{"backend": {"file": {"path": "/vault/file"}}, "default_lease_ttl": "168h", "max_lease_ttl": "720h"}' - self.run_state('docker_image.present', name='vault', tag='0.9.6') + self.run_state("docker_image.present", name="vault", tag="0.9.6") self.run_state( - 'docker_container.running', - name='vault', - image='vault:0.9.6', - port_bindings='8200:8200', + "docker_container.running", + name="vault", + image="vault:0.9.6", + port_bindings="8200:8200", environment={ - 'VAULT_DEV_ROOT_TOKEN_ID': 'testsecret', - 'VAULT_LOCAL_CONFIG': config, + "VAULT_DEV_ROOT_TOKEN_ID": "testsecret", + "VAULT_LOCAL_CONFIG": config, }, - cap_add='IPC_LOCK', + cap_add="IPC_LOCK", ) time.sleep(5) ret = self.run_function( - 'cmd.retcode', - cmd='/usr/local/bin/vault login token=testsecret', - env={'VAULT_ADDR': 'http://127.0.0.1:8200'}, + "cmd.retcode", + cmd="/usr/local/bin/vault login token=testsecret", + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, ) login_attempts = 1 # If the login failed, container might have stopped @@ -57,59 +61,201 @@ class VaultTestCase(ModuleCase, ShellCase): # skipping. while ret != 0: self.run_state( - 'docker_container.running', - name='vault', - image='vault:0.9.6', - port_bindings='8200:8200', + "docker_container.running", + name="vault", + image="vault:0.9.6", + port_bindings="8200:8200", environment={ - 'VAULT_DEV_ROOT_TOKEN_ID': 'testsecret', - 'VAULT_LOCAL_CONFIG': config, + "VAULT_DEV_ROOT_TOKEN_ID": "testsecret", + "VAULT_LOCAL_CONFIG": config, }, - cap_add='IPC_LOCK', + cap_add="IPC_LOCK", ) time.sleep(5) ret = self.run_function( - 'cmd.retcode', - cmd='/usr/local/bin/vault login token=testsecret', - env={'VAULT_ADDR': 'http://127.0.0.1:8200'}, + "cmd.retcode", + cmd="/usr/local/bin/vault login token=testsecret", + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, ) login_attempts += 1 if login_attempts >= 3: - self.skipTest('unable to login to vault') + self.skipTest("unable to login to vault") ret = self.run_function( - 'cmd.retcode', - cmd='/usr/local/bin/vault policy write testpolicy {0}/vault.hcl'.format(RUNTIME_VARS.FILES), - env={'VAULT_ADDR': 'http://127.0.0.1:8200'}, + "cmd.retcode", + cmd="/usr/local/bin/vault policy write testpolicy {0}/vault.hcl".format( + RUNTIME_VARS.FILES + ), + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, ) if ret != 0: - self.skipTest('unable to assign policy to vault') + self.skipTest("unable to assign policy to vault") VaultTestCase.count += 1 def tearDown(self): - ''' + """ TearDown vault container - ''' + """ + def count_tests(funcobj): - return inspect.ismethod(funcobj) or \ - inspect.isfunction(funcobj) and \ - funcobj.__name__.startswith('test_') + return ( + inspect.ismethod(funcobj) + or inspect.isfunction(funcobj) + and funcobj.__name__.startswith("test_") + ) + numtests = len(inspect.getmembers(VaultTestCase, predicate=count_tests)) if VaultTestCase.count >= numtests: - self.run_state('docker_container.stopped', name='vault') - self.run_state('docker_container.absent', name='vault') - self.run_state('docker_image.absent', name='vault', force=True) + self.run_state("docker_container.stopped", name="vault") + self.run_state("docker_container.absent", name="vault") + self.run_state("docker_image.absent", name="vault", force=True) @flaky def test_sdb(self): - assert self.run_function('sdb.set', uri='sdb://sdbvault/secret/test/test_sdb/foo', value='bar') is True - assert self.run_function('sdb.get', arg=['sdb://sdbvault/secret/test/test_sdb/foo']) == 'bar' + set_output = self.run_function( + "sdb.set", uri="sdb://sdbvault/secret/test/test_sdb/foo", value="bar" + ) + self.assertEqual(set_output, True) + get_output = self.run_function( + "sdb.get", arg=["sdb://sdbvault/secret/test/test_sdb/foo"] + ) + self.assertEqual(get_output, "bar") @flaky def test_sdb_runner(self): - assert self.run_run('sdb.set sdb://sdbvault/secret/test/test_sdb_runner/foo bar') == ['True'] - assert self.run_run('sdb.get sdb://sdbvault/secret/test/test_sdb_runner/foo') == ['bar'] + set_output = self.run_run( + "sdb.set sdb://sdbvault/secret/test/test_sdb_runner/foo bar" + ) + self.assertEqual(set_output, ["True"]) + get_output = self.run_run( + "sdb.get sdb://sdbvault/secret/test/test_sdb_runner/foo" + ) + self.assertEqual(get_output, ["bar"]) @flaky def test_config(self): - assert self.run_function('sdb.set', uri='sdb://sdbvault/secret/test/test_pillar_sdb/foo', value='bar') is True - assert self.run_function('config.get', arg=['test_vault_pillar_sdb']) == 'bar' + set_output = self.run_function( + "sdb.set", uri="sdb://sdbvault/secret/test/test_pillar_sdb/foo", value="bar" + ) + self.assertEqual(set_output, True) + get_output = self.run_function("config.get", arg=["test_vault_pillar_sdb"]) + self.assertEqual(get_output, "bar") + + +@destructiveTest +@skipIf(not salt.utils.path.which("dockerd"), "Docker not installed") +@skipIf(not salt.utils.path.which("vault"), "Vault not installed") +class VaultTestCaseCurrent(ModuleCase, ShellCase): + """ + Test vault module + """ + + count = 0 + + def setUp(self): + """ + SetUp vault container + """ + if self.count == 0: + config = '{"backend": {"file": {"path": "/vault/file"}}, "default_lease_ttl": "168h", "max_lease_ttl": "720h"}' + self.run_state("docker_image.present", name="vault", tag="1.3.1") + self.run_state( + "docker_container.running", + name="vault", + image="vault:1.3.1", + port_bindings="8200:8200", + environment={ + "VAULT_DEV_ROOT_TOKEN_ID": "testsecret", + "VAULT_LOCAL_CONFIG": config, + }, + cap_add="IPC_LOCK", + ) + time.sleep(5) + ret = self.run_function( + "cmd.retcode", + cmd="/usr/local/bin/vault login token=testsecret", + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, + ) + login_attempts = 1 + # If the login failed, container might have stopped + # attempt again, maximum of three times before + # skipping. + while ret != 0: + self.run_state( + "docker_container.running", + name="vault", + image="vault:1.3.1", + port_bindings="8200:8200", + environment={ + "VAULT_DEV_ROOT_TOKEN_ID": "testsecret", + "VAULT_LOCAL_CONFIG": config, + }, + cap_add="IPC_LOCK", + ) + time.sleep(5) + ret = self.run_function( + "cmd.retcode", + cmd="/usr/local/bin/vault login token=testsecret", + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, + ) + login_attempts += 1 + if login_attempts >= 3: + self.skipTest("unable to login to vault") + ret = self.run_function( + "cmd.retcode", + cmd="/usr/local/bin/vault policy write testpolicy {0}/vault.hcl".format( + RUNTIME_VARS.FILES + ), + env={"VAULT_ADDR": "http://127.0.0.1:8200"}, + ) + if ret != 0: + self.skipTest("unable to assign policy to vault") + self.count += 1 + + def tearDown(self): + """ + TearDown vault container + """ + + def count_tests(funcobj): + return ( + inspect.ismethod(funcobj) + or inspect.isfunction(funcobj) + and funcobj.__name__.startswith("test_") + ) + + numtests = len(inspect.getmembers(VaultTestCaseCurrent, predicate=count_tests)) + if self.count >= numtests: + self.run_state("docker_container.stopped", name="vault") + self.run_state("docker_container.absent", name="vault") + self.run_state("docker_image.absent", name="vault", force=True) + + @flaky + def test_sdb_kv2(self): + set_output = self.run_function( + "sdb.set", uri="sdb://sdbvault/secret/test/test_sdb/foo", value="bar" + ) + self.assertEqual(set_output, True) + get_output = self.run_function( + "sdb.get", arg=["sdb://sdbvault/secret/test/test_sdb/foo"] + ) + self.assertEqual(get_output, "bar") + + @flaky + def test_sdb_runner_kv2(self): + set_output = self.run_run( + "sdb.set sdb://sdbvault/secret/test/test_sdb_runner/foo bar" + ) + self.assertEqual(set_output, ["True"]) + get_output = self.run_run( + "sdb.get sdb://sdbvault/secret/test/test_sdb_runner/foo" + ) + self.assertEqual(get_output, ["bar"]) + + @flaky + def test_config_kv2(self): + set_output = self.run_function( + "sdb.set", uri="sdb://sdbvault/secret/test/test_pillar_sdb/foo", value="bar" + ) + self.assertEqual(set_output, True) + get_output = self.run_function("config.get", arg=["test_vault_pillar_sdb"]) + self.assertEqual(get_output, "bar") diff --git a/tests/integration/setup/__init__.py b/tests/integration/setup/__init__.py new file mode 100644 index 00000000000..40a96afc6ff --- /dev/null +++ b/tests/integration/setup/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/tests/integration/setup/test_bdist.py b/tests/integration/setup/test_bdist.py new file mode 100644 index 00000000000..05c3b3e9210 --- /dev/null +++ b/tests/integration/setup/test_bdist.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +""" +tests.integration.setup.test_bdist +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +""" + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals + +import os +import re + +# Import salt libs +import salt.utils.path +import salt.utils.platform +from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.case import ModuleCase +from tests.support.helpers import VirtualEnv, skip_if_not_root + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + + +@skip_if_not_root +@skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, "virtualenv not installed" +) +class BdistSetupTest(ModuleCase): + """ + Tests for building and installing bdist_wheel packages + """ + + def test_wheel_build(self): + """ + test building a bdist_wheel package + """ + # Let's create the testing virtualenv + with VirtualEnv() as venv: + ret = self.run_function( + "cmd.run", + [ + "{0} setup.py bdist_wheel --dist-dir={1}".format( + venv.venv_python, venv.venv_dir + ) + ], + cwd=RUNTIME_VARS.CODE_DIR, + ) + + for _file in os.listdir(venv.venv_dir): + if _file.endswith("whl"): + whl = os.path.join(venv.venv_dir, _file) + break + + ret = self.run_function("pip.install", pkgs=whl, bin_env=venv.venv_dir) + + # Let's ensure the version is correct + pip_ver = self.run_function("pip.list", bin_env=venv.venv_dir).get("salt") + whl_ver = [ + x for x in whl.split("/")[-1:][0].split("-") if re.search(r"^\d.\d*", x) + ][0] + assert pip_ver == whl_ver.replace("_", "-") diff --git a/tests/integration/setup/test_egg.py b/tests/integration/setup/test_egg.py new file mode 100644 index 00000000000..ed5b85a054e --- /dev/null +++ b/tests/integration/setup/test_egg.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +""" +tests.integration.setup.test_egg +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +""" + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals + +import os +import re +import shutil + +# Import salt libs +import salt.utils.path +import salt.utils.platform +from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.case import ModuleCase +from tests.support.helpers import VirtualEnv, destructiveTest + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + + +@destructiveTest +@skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, "virtualenv not installed" +) +class EggSetupTest(ModuleCase): + """ + Tests for building and installing egg packages + """ + + def setUp(self): + # ensure we have a clean build dir + self._clean_build() + + def _clean_build(self): + """ + helper method to clean the build dir + """ + dirs = [ + os.path.join(RUNTIME_VARS.CODE_DIR, "build"), + os.path.join(RUNTIME_VARS.CODE_DIR, "salt.egg-info"), + os.path.join(RUNTIME_VARS.CODE_DIR, "dist"), + ] + for _dir in dirs: + if os.path.exists(_dir): + shutil.rmtree(_dir) + + def test_egg_install(self): + """ + test installing an egg package + """ + # Let's create the testing virtualenv + with VirtualEnv() as venv: + ret = self.run_function( + "cmd.run", + [ + "{0} setup.py install --prefix={1}".format( + venv.venv_python, venv.venv_dir + ) + ], + cwd=RUNTIME_VARS.CODE_DIR, + ) + self._clean_build() + lib_dir = os.path.join(venv.venv_dir, "lib") + for _dir in os.listdir(lib_dir): + site_pkg = os.path.join(lib_dir, _dir, "site-packages") + for _file in os.listdir(site_pkg): + if _file.startswith("salt-"): + egg = os.path.join(venv.venv_dir, _file) + assert os.path.exists( + os.path.join(site_pkg, _file, "salt", "_version.py") + ) + break + + # Let's ensure the version is correct + pip_ver = self.run_function("pip.list", bin_env=venv.venv_dir).get("salt") + egg_ver = [ + x for x in egg.split("/")[-1:][0].split("-") if re.search(r"^\d.\d*", x) + ][0] + assert pip_ver == egg_ver.replace("_", "-") diff --git a/tests/integration/shell/test_arguments.py b/tests/integration/shell/test_arguments.py index be1646955d0..b17dc71c3d3 100644 --- a/tests/integration/shell/test_arguments.py +++ b/tests/integration/shell/test_arguments.py @@ -1,56 +1,51 @@ # -*- coding: utf-8 -*- -''' +""" Test Salt's argument parser -''' +""" -# Import Python libs from __future__ import absolute_import -# Import Salt Testing libs +import pytest +import salt.utils.args from tests.support.case import ModuleCase from tests.support.helpers import requires_salt_modules -# Import Salt libs -import salt.utils.args - -@requires_salt_modules('test.ping', 'test.arg') +@requires_salt_modules("test.ping", "test.arg") +@pytest.mark.windows_whitelisted class ArgumentTestCase(ModuleCase): def test_unsupported_kwarg(self): - ''' + """ Test passing a non-supported keyword argument. The relevant code that checks for invalid kwargs is located in salt/minion.py, within the 'load_args_and_kwargs' function. - ''' + """ self.assertIn( ("ERROR executing 'test.ping': The following keyword arguments"), - self.run_function('test.ping', foo='bar') + self.run_function("test.ping", foo="bar"), ) def test_kwarg_name_containing_dashes(self): - ''' + """ Tests the arg parser to ensure that kwargs with dashes in the arg name are properly identified as kwargs. If this fails, then the KWARG_REGEX variable in salt/utils/__init__.py needs to be fixed. - ''' + """ # We need to use parse_input here because run_function now requires # kwargs to be passed in as *actual* kwargs, and dashes are not valid # characters in Python kwargs. self.assertEqual( - self.run_function( - 'test.arg', salt.utils.args.parse_input(['foo-bar=baz']) - ).get('kwargs', {}).get('foo-bar'), - 'baz' + self.run_function("test.arg", salt.utils.args.parse_input(["foo-bar=baz"])) + .get("kwargs", {}) + .get("foo-bar"), + "baz", ) def test_argument_containing_pound_sign(self): - ''' + """ Tests the argument parsing to ensure that a CLI argument with a pound sign doesn't have the pound sign interpreted as a comment and removed. See https://github.com/saltstack/salt/issues/8585 for more info. - ''' - arg = 'foo bar #baz' - self.assertEqual( - self.run_function('test.echo', [arg]), - arg - ) + """ + arg = "foo bar #baz" + self.assertEqual(self.run_function("test.echo", [arg]), arg) diff --git a/tests/integration/shell/test_auth.py b/tests/integration/shell/test_auth.py index 05978943d03..807ba4df705 100644 --- a/tests/integration/shell/test_auth.py +++ b/tests/integration/shell/test_auth.py @@ -1,144 +1,152 @@ # -*- coding: utf-8 -*- -''' +""" tests.integration.shell.auth ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -try: - import pwd - import grp -except ImportError: - pwd, grp = None, None import random import string -# Import Salt Testing libs -from tests.support.unit import skipIf -from tests.support.case import ShellCase, ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin +import pytest +import salt.utils.platform +from salt.ext.six.moves import range +from salt.utils.pycrypto import gen_hash +from tests.support.case import ModuleCase, ShellCase from tests.support.helpers import ( destructiveTest, requires_salt_modules, requires_salt_states, skip_if_not_root, ) +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf -# Import Salt libs -import salt.utils.platform -from salt.utils.pycrypto import gen_hash +try: + import pwd + import grp +except ImportError: + pwd, grp = None, None -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin log = logging.getLogger(__name__) def gen_password(): - ''' + """ generate a password and hash it - ''' - password = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20)) - hashed_pwd = gen_hash('salt', password, 'sha512') + """ + password = "".join( + random.choice(string.ascii_letters + string.digits) for _ in range(20) + ) + hashed_pwd = gen_hash("salt", password, "sha512") return password, hashed_pwd -@requires_salt_states('user.absent', 'user.present') -@requires_salt_modules('shadow.set_password') +@requires_salt_states("user.absent", "user.present") +@requires_salt_modules("shadow.set_password") @skip_if_not_root -@skipIf(pwd is None or grp is None, 'No crypt module available') +@skipIf(pwd is None or grp is None, "No pwd or grp module available") @destructiveTest +@pytest.mark.windows_whitelisted class UserAuthTest(ModuleCase, SaltReturnAssertsMixin, ShellCase): - ''' + """ Test user auth mechanisms - ''' + """ - _call_binary_ = 'salt' - user = 'saltdev' + _call_binary_ = "salt" + user = "saltdev" def setUp(self): - ret = self.run_state('user.present', name=self.user, createhome=False) + ret = self.run_state("user.present", name=self.user, createhome=False) self.assertSaltTrueReturn(ret) def tearDown(self): - ret = self.run_state('user.absent', name=self.user) + ret = self.run_state("user.absent", name=self.user) self.assertSaltTrueReturn(ret) def test_pam_auth_valid_user(self): - ''' + """ test that pam auth mechanism works with a valid user - ''' + """ password, hashed_pwd = gen_password() # set user password set_pw_cmd = "shadow.set_password {0} '{1}'".format( - self.user, - password if salt.utils.platform.is_darwin() else hashed_pwd + self.user, password if salt.utils.platform.is_darwin() else hashed_pwd ) self.assertRunCall(set_pw_cmd) # test user auth against pam - cmd = ('-a pam "*" test.ping --username {0} --password {1}'.format(self.user, password)) + cmd = '-a pam "*" test.ping --username {0} --password {1}'.format( + self.user, password + ) resp = self.run_salt(cmd) - log.debug('resp = %s', resp) - self.assertIn('minion', [r.strip(': ') for r in resp]) + log.debug("resp = %s", resp) + self.assertIn("minion", [r.strip(": ") for r in resp]) def test_pam_auth_invalid_user(self): - ''' + """ test pam auth mechanism errors for an invalid user - ''' - cmd = ('-a pam "*" test.ping ' - '--username nouser --password {0}'.format('abcd1234')) + """ + cmd = '-a pam "*" test.ping ' "--username nouser --password {0}".format( + "abcd1234" + ) resp = self.run_salt(cmd) - self.assertIn('Authentication error occurred', ''.join(resp)) + self.assertIn("Authentication error occurred", "".join(resp)) -@requires_salt_states('group.absent', 'group.present', 'user.absent', 'user.present') -@requires_salt_modules('shadow.set_password', 'user.chgroups') +@requires_salt_states("group.absent", "group.present", "user.absent", "user.present") +@requires_salt_modules("shadow.set_password", "user.chgroups") @skip_if_not_root -@skipIf(pwd is None or grp is None, 'No crypt module available') +@skipIf(pwd is None or grp is None, "No crypt module available") @destructiveTest class GroupAuthTest(ModuleCase, SaltReturnAssertsMixin, ShellCase): - ''' + """ Test group auth mechanisms - ''' + """ - _call_binary_ = 'salt' + _call_binary_ = "salt" - user = 'saltadm' - group = 'saltops' + user = "saltadm" + group = "saltops" def setUp(self): - ret = self.run_state('group.present', name=self.group) + ret = self.run_state("group.present", name=self.group) self.assertSaltTrueReturn(ret) - ret = self.run_state('user.present', name=self.user, createhome=False, groups=[self.group]) + ret = self.run_state( + "user.present", name=self.user, createhome=False, groups=[self.group] + ) self.assertSaltTrueReturn(ret) - self.assertRunCall('user.chgroups {0} {1} True'.format(self.user, self.group), local=True) + self.assertRunCall( + "user.chgroups {0} {1} True".format(self.user, self.group), local=True + ) def tearDown(self): - ret0 = self.run_state('user.absent', name=self.user) - ret1 = self.run_state('group.absent', name=self.group) + ret0 = self.run_state("user.absent", name=self.user) + ret1 = self.run_state("group.absent", name=self.group) self.assertSaltTrueReturn(ret0) self.assertSaltTrueReturn(ret1) def test_pam_auth_valid_group(self): - ''' + """ test that pam auth mechanism works for a valid group - ''' + """ password, hashed_pwd = gen_password() # set user password set_pw_cmd = "shadow.set_password {0} '{1}'".format( - self.user, - password if salt.utils.platform.is_darwin() else hashed_pwd + self.user, password if salt.utils.platform.is_darwin() else hashed_pwd ) self.assertRunCall(set_pw_cmd) # test group auth against pam: saltadm is not configured in # external_auth, but saltops is and saldadm is a member of saltops - cmd = ('-a pam "*" test.ping --username {0} --password {1}'.format(self.user, password)) + cmd = '-a pam "*" test.ping --username {0} --password {1}'.format( + self.user, password + ) resp = self.run_salt(cmd) - self.assertIn('minion', [r.strip(': ') for r in resp]) + self.assertIn("minion", [r.strip(": ") for r in resp]) diff --git a/tests/integration/shell/test_call.py b/tests/integration/shell/test_call.py index 7e648385600..7a26d7f9a48 100644 --- a/tests/integration/shell/test_call.py +++ b/tests/integration/shell/test_call.py @@ -1,141 +1,143 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.shell.call ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' -# pylint: disable=invalid-name - -# Import python libs +""" from __future__ import absolute_import + import logging import os import re import shutil import sys -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ShellCase -from tests.support.unit import skipIf -from tests.support.mixins import ShellCaseCommonTestsMixin -from tests.support.helpers import flaky, with_tempfile -from tests.integration.utils import testprogram - -# Import salt libs +import pytest import salt.utils.files import salt.utils.json import salt.utils.platform import salt.utils.yaml from salt.ext import six +from tests.integration.utils import testprogram +from tests.support.case import ShellCase +from tests.support.helpers import flaky, with_tempfile +from tests.support.mixins import ShellCaseCommonTestsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf log = logging.getLogger(__name__) +@pytest.mark.windows_whitelisted class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin): - _call_binary_ = 'salt-call' + _call_binary_ = "salt-call" def test_default_output(self): - out = self.run_call('-l quiet test.fib 3') + out = self.run_call("-l quiet test.fib 3") - expect = ['local:', - ' - 2'] + expect = ["local:", " - 2"] self.assertEqual(expect, out[:-1]) def test_text_output(self): - out = self.run_call('-l quiet --out txt test.fib 3') + out = self.run_call("-l quiet --out txt test.fib 3") - expect = [ - 'local: (2' - ] + expect = ["local: (2"] - self.assertEqual(''.join(expect), ''.join(out).rsplit(",", 1)[0]) + self.assertEqual("".join(expect), "".join(out).rsplit(",", 1)[0]) def test_json_out_indent(self): - out = self.run_call('test.ping -l quiet --out=json --out-indent=-1') - self.assertIn('"local": true', ''.join(out)) + out = self.run_call("test.ping -l quiet --out=json --out-indent=-1") + self.assertIn('"local": true', "".join(out)) - out = self.run_call('test.ping -l quiet --out=json --out-indent=0') - self.assertIn('"local": true', ''.join(out)) + out = self.run_call("test.ping -l quiet --out=json --out-indent=0") + self.assertIn('"local": true', "".join(out)) - out = self.run_call('test.ping -l quiet --out=json --out-indent=1') - self.assertIn('"local": true', ''.join(out)) + out = self.run_call("test.ping -l quiet --out=json --out-indent=1") + self.assertIn('"local": true', "".join(out)) def test_local_sls_call(self): - fileroot = os.path.join(RUNTIME_VARS.FILES, 'file', 'base') - out = self.run_call('--file-root {0} state.sls saltcalllocal'.format(fileroot), local=True) - self.assertIn('Name: test.echo', ''.join(out)) - self.assertIn('Result: True', ''.join(out)) - self.assertIn('hello', ''.join(out)) - self.assertIn('Succeeded: 1', ''.join(out)) + fileroot = os.path.join(RUNTIME_VARS.FILES, "file", "base") + out = self.run_call( + "--file-root {0} state.sls saltcalllocal".format(fileroot), local=True + ) + self.assertIn("Name: test.echo", "".join(out)) + self.assertIn("Result: True", "".join(out)) + self.assertIn("hello", "".join(out)) + self.assertIn("Succeeded: 1", "".join(out)) @with_tempfile() def test_local_salt_call(self, name): - ''' + """ This tests to make sure that salt-call does not execute the function twice, see https://github.com/saltstack/salt/pull/49552 - ''' + """ + def _run_call(cmd): - cmd = '--out=json ' + cmd - return salt.utils.json.loads(''.join(self.run_call(cmd, local=True)))['local'] + cmd = "--out=json " + cmd + return salt.utils.json.loads("".join(self.run_call(cmd, local=True)))[ + "local" + ] ret = _run_call('state.single file.append name={0} text="foo"'.format(name)) ret = ret[next(iter(ret))] # Make sure we made changes - assert ret['changes'] + assert ret["changes"] # 2nd sanity check: make sure that "foo" only exists once in the file with salt.utils.files.fopen(name) as fp_: contents = fp_.read() - assert contents.count('foo') == 1, contents + assert contents.count("foo") == 1, contents - @skipIf(salt.utils.platform.is_windows() or salt.utils.platform.is_darwin(), 'This test requires a supported master') + @skipIf( + salt.utils.platform.is_windows() or salt.utils.platform.is_darwin(), + "This test requires a supported master", + ) def test_user_delete_kw_output(self): - ret = self.run_call('-l quiet -d user.delete') - assert 'salt \'*\' user.delete name remove=True force=True' in ''.join(ret) + ret = self.run_call("-l quiet -d user.delete") + assert "salt '*' user.delete name remove=True force=True" in "".join(ret) def test_salt_documentation_too_many_arguments(self): - ''' + """ Test to see if passing additional arguments shows an error - ''' - data = self.run_call('-d virtualenv.create /tmp/ve', catch_stderr=True) - self.assertIn('You can only get documentation for one method at one time', '\n'.join(data[1])) + """ + data = self.run_call("-d virtualenv.create /tmp/ve", catch_stderr=True) + self.assertIn( + "You can only get documentation for one method at one time", + "\n".join(data[1]), + ) def test_issue_6973_state_highstate_exit_code(self): - ''' + """ If there is no tops/master_tops or state file matches for this minion, salt-call should exit non-zero if invoked with option --retcode-passthrough - ''' - src = os.path.join(RUNTIME_VARS.BASE_FILES, 'top.sls') - dst = os.path.join(RUNTIME_VARS.BASE_FILES, 'top.sls.bak') + """ + src = os.path.join(RUNTIME_VARS.BASE_FILES, "top.sls") + dst = os.path.join(RUNTIME_VARS.BASE_FILES, "top.sls.bak") shutil.move(src, dst) - expected_comment = 'No states found for this minion' + expected_comment = "No states found for this minion" try: stdout, retcode = self.run_call( - '-l quiet --retcode-passthrough state.highstate', - with_retcode=True + "-l quiet --retcode-passthrough state.highstate", with_retcode=True ) finally: shutil.move(dst, src) - self.assertIn(expected_comment, ''.join(stdout)) + self.assertIn(expected_comment, "".join(stdout)) self.assertNotEqual(0, retcode) - @skipIf(sys.platform.startswith('win'), 'This test does not apply on Win') - @skipIf(True, 'to be re-enabled when #23623 is merged') + @skipIf(sys.platform.startswith("win"), "This test does not apply on Win") + @skipIf(True, "to be re-enabled when #23623 is merged") def test_return(self): self.run_call('cmd.run "echo returnTOmaster"') - jobs = [a for a in self.run_run('jobs.list_jobs')] + jobs = [a for a in self.run_run("jobs.list_jobs")] - self.assertTrue(True in ['returnTOmaster' in j for j in jobs]) + self.assertTrue(True in ["returnTOmaster" in j for j in jobs]) # lookback jid - first_match = [(i, j) - for i, j in enumerate(jobs) - if 'returnTOmaster' in j][0] + first_match = [(i, j) for i, j in enumerate(jobs) if "returnTOmaster" in j][0] jid, idx = None, first_match[0] while idx > 0: jid = re.match("([0-9]+):", jobs[idx]) @@ -145,47 +147,44 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin idx -= 1 assert idx > 0 assert jid - master_out = [ - a for a in self.run_run('jobs.lookup_jid {0}'.format(jid)) - ] - self.assertTrue(True in ['returnTOmaster' in a for a in master_out]) + master_out = [a for a in self.run_run("jobs.lookup_jid {0}".format(jid))] + self.assertTrue(True in ["returnTOmaster" in a for a in master_out]) - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows") def test_syslog_file_not_found(self): - ''' + """ test when log_file is set to a syslog file that does not exist - ''' + """ old_cwd = os.getcwd() - config_dir = os.path.join(RUNTIME_VARS.TMP, 'log_file_incorrect') + config_dir = os.path.join(RUNTIME_VARS.TMP, "log_file_incorrect") if not os.path.isdir(config_dir): os.makedirs(config_dir) os.chdir(config_dir) - with salt.utils.files.fopen(self.get_config_file_path('minion'), 'r') as fh_: + with salt.utils.files.fopen(self.get_config_file_path("minion"), "r") as fh_: minion_config = salt.utils.yaml.load(fh_.read()) - minion_config['log_file'] = 'file:///dev/doesnotexist' - with salt.utils.files.fopen(os.path.join(config_dir, 'minion'), 'w') as fh_: - fh_.write( - salt.utils.yaml.dump(minion_config, default_flow_style=False) - ) + minion_config["log_file"] = "file:///dev/doesnotexist" + with salt.utils.files.fopen(os.path.join(config_dir, "minion"), "w") as fh_: + fh_.write(salt.utils.yaml.dump(minion_config, default_flow_style=False)) ret = self.run_script( - 'salt-call', - '--config-dir {0} cmd.run "echo foo"'.format( - config_dir - ), + "salt-call", + '--config-dir {0} cmd.run "echo foo"'.format(config_dir), timeout=120, catch_stderr=True, - with_retcode=True + with_retcode=True, ) try: if sys.version_info >= (3, 5, 4): - self.assertIn('local:', ret[0]) - self.assertIn('[WARNING ] The log_file does not exist. Logging not setup correctly or syslog service not started.', ret[1]) + self.assertIn("local:", ret[0]) + self.assertIn( + "[WARNING ] The log_file does not exist. Logging not setup correctly or syslog service not started.", + ret[1], + ) self.assertEqual(ret[2], 0) else: self.assertIn( - 'Failed to setup the Syslog logging handler', '\n'.join(ret[1]) + "Failed to setup the Syslog logging handler", "\n".join(ret[1]) ) self.assertEqual(ret[2], 2) finally: @@ -193,33 +192,31 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin if os.path.isdir(config_dir): shutil.rmtree(config_dir) - @skipIf(True, 'This test is unreliable. Need to investigate why more deeply.') + @skipIf(True, "This test is unreliable. Need to investigate why more deeply.") @flaky def test_issue_15074_output_file_append(self): - output_file_append = os.path.join(RUNTIME_VARS.TMP, 'issue-15074') + output_file_append = os.path.join(RUNTIME_VARS.TMP, "issue-15074") try: # Let's create an initial output file with some data _ = self.run_script( - 'salt-call', - '-c {0} --output-file={1} test.versions'.format( - self.config_dir, - output_file_append + "salt-call", + "-c {0} --output-file={1} test.versions".format( + self.config_dir, output_file_append ), catch_stderr=True, - with_retcode=True + with_retcode=True, ) with salt.utils.files.fopen(output_file_append) as ofa: output = ofa.read() self.run_script( - 'salt-call', - '-c {0} --output-file={1} --output-file-append test.versions'.format( - self.config_dir, - output_file_append + "salt-call", + "-c {0} --output-file={1} --output-file-append test.versions".format( + self.config_dir, output_file_append ), catch_stderr=True, - with_retcode=True + with_retcode=True, ) with salt.utils.files.fopen(output_file_append) as ofa: self.assertEqual(ofa.read(), output + output) @@ -227,43 +224,41 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin if os.path.exists(output_file_append): os.unlink(output_file_append) - @skipIf(True, 'This test is unreliable. Need to investigate why more deeply.') + @skipIf(True, "This test is unreliable. Need to investigate why more deeply.") @flaky def test_issue_14979_output_file_permissions(self): - output_file = os.path.join(RUNTIME_VARS.TMP, 'issue-14979') + output_file = os.path.join(RUNTIME_VARS.TMP, "issue-14979") with salt.utils.files.set_umask(0o077): try: # Let's create an initial output file with some data self.run_script( - 'salt-call', - '-c {0} --output-file={1} -l trace -g'.format( - self.config_dir, - output_file + "salt-call", + "-c {0} --output-file={1} -l trace -g".format( + self.config_dir, output_file ), catch_stderr=True, - with_retcode=True + with_retcode=True, ) try: stat1 = os.stat(output_file) except OSError: - self.fail('Failed to generate output file, see log for details') + self.fail("Failed to generate output file, see log for details") # Let's change umask os.umask(0o777) # pylint: disable=blacklisted-function self.run_script( - 'salt-call', - '-c {0} --output-file={1} --output-file-append -g'.format( - self.config_dir, - output_file + "salt-call", + "-c {0} --output-file={1} --output-file-append -g".format( + self.config_dir, output_file ), catch_stderr=True, - with_retcode=True + with_retcode=True, ) try: stat2 = os.stat(output_file) except OSError: - self.fail('Failed to generate output file, see log for details') + self.fail("Failed to generate output file, see log for details") self.assertEqual(stat1.st_mode, stat2.st_mode) # Data was appeneded to file self.assertTrue(stat1.st_size < stat2.st_size) @@ -273,28 +268,25 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin # Not appending data self.run_script( - 'salt-call', - '-c {0} --output-file={1} -g'.format( - self.config_dir, - output_file - ), + "salt-call", + "-c {0} --output-file={1} -g".format(self.config_dir, output_file), catch_stderr=True, - with_retcode=True + with_retcode=True, ) try: stat3 = os.stat(output_file) except OSError: - self.fail('Failed to generate output file, see log for details') + self.fail("Failed to generate output file, see log for details") # Mode must have changed since we're creating a new log file self.assertNotEqual(stat1.st_mode, stat3.st_mode) finally: if os.path.exists(output_file): os.unlink(output_file) - @skipIf(sys.platform.startswith('win'), 'This test does not apply on Win') + @skipIf(sys.platform.startswith("win"), "This test does not apply on Win") def test_42116_cli_pillar_override(self): ret = self.run_call( - 'state.apply issue-42116-cli-pillar-override ' + "state.apply issue-42116-cli-pillar-override " 'pillar=\'{"myhost": "localhost"}\'' ) for line in ret: @@ -303,61 +295,68 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin # Successful test break else: - log.debug('salt-call output:\n\n%s', '\n'.join(ret)) - self.fail('CLI pillar override not found in pillar data') + log.debug("salt-call output:\n\n%s", "\n".join(ret)) + self.fail("CLI pillar override not found in pillar data") def test_pillar_items_masterless(self): - ''' + """ Test to ensure we get expected output from pillar.items with salt-call - ''' - get_items = self.run_call('pillar.items', local=True) - exp_out = [' - Lancelot', ' - Galahad', ' - Bedevere', - ' monty:', ' python'] + """ + get_items = self.run_call("pillar.items", local=True) + exp_out = [ + " - Lancelot", + " - Galahad", + " - Bedevere", + " monty:", + " python", + ] for out in exp_out: self.assertIn(out, get_items) def tearDown(self): - ''' + """ Teardown method to remove installed packages - ''' - user = '' - user_info = self.run_call(' grains.get username', local=True) - if user_info and isinstance(user_info, (list, tuple)) and isinstance(user_info[-1], six.string_types): + """ + user = "" + user_info = self.run_call(" grains.get username", local=True) + if ( + user_info + and isinstance(user_info, (list, tuple)) + and isinstance(user_info[-1], six.string_types) + ): user = user_info[-1].strip() super(CallTest, self).tearDown() def test_exit_status_unknown_argument(self): - ''' + """ Ensure correct exit status when an unknown argument is passed to salt-call. - ''' + """ call = testprogram.TestProgramSaltCall( - name='unknown_argument', - parent_dir=self._test_dir, + name="unknown_argument", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist call.setup() stdout, stderr, status = call.run( - args=['--unknown-argument'], - catch_stderr=True, - with_retcode=True, + args=["--unknown-argument"], catch_stderr=True, with_retcode=True, ) self.assert_exit_status( - status, 'EX_USAGE', - message='unknown argument', - stdout=stdout, stderr=stderr + status, "EX_USAGE", message="unknown argument", stdout=stdout, stderr=stderr ) def test_masterless_highstate(self): - ''' + """ test state.highstate in masterless mode - ''' - ret = self.run_call('state.highstate', local=True) + """ + ret = self.run_call("state.highstate", local=True) - destpath = os.path.join(RUNTIME_VARS.TMP, 'testfile') - exp_out = [' Function: file.managed', ' Result: True', - ' ID: {0}'.format(destpath)] + destpath = os.path.join(RUNTIME_VARS.TMP, "testfile") + exp_out = [ + " Function: file.managed", + " Result: True", + " ID: {0}".format(destpath), + ] for out in exp_out: self.assertIn(out, ret) @@ -365,23 +364,18 @@ class CallTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin self.assertTrue(os.path.exists(destpath)) def test_exit_status_correct_usage(self): - ''' + """ Ensure correct exit status when salt-call starts correctly. - ''' + """ call = testprogram.TestProgramSaltCall( - name='correct_usage', - parent_dir=self._test_dir, + name="correct_usage", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist call.setup() stdout, stderr, status = call.run( - args=['--local', 'test.true'], - catch_stderr=True, - with_retcode=True, + args=["--local", "test.true"], catch_stderr=True, with_retcode=True, ) self.assert_exit_status( - status, 'EX_OK', - message='correct usage', - stdout=stdout, stderr=stderr + status, "EX_OK", message="correct usage", stdout=stdout, stderr=stderr ) diff --git a/tests/integration/shell/test_cloud.py b/tests/integration/shell/test_cloud.py index ab9398a6dd9..5b3eaf8f450 100644 --- a/tests/integration/shell/test_cloud.py +++ b/tests/integration/shell/test_cloud.py @@ -1,70 +1,68 @@ # -*- coding: utf-8 -*- -''' +""" integration.cli_test ~~~~~~~~~~~~~~~~~~~~ CLI related unit testing :codeauthor: Pedro Algarvio (pedro@algarvio.me) -''' +""" # Import Python libs from __future__ import absolute_import, print_function -# Import salt testing libs -from tests.support.unit import skipIf +# Import 3rd-party libs +# pylint: disable=import-error +from salt.ext.six.moves import range # pylint: disable=redefined-builtin # Import salt libs from tests.support.case import ShellCase from tests.support.mixins import ShellCaseCommonTestsMixin -# Import 3rd-party libs -# pylint: disable=import-error -from salt.ext.six.moves import range # pylint: disable=redefined-builtin +# Import salt testing libs +from tests.support.unit import skipIf + try: import libcloud # pylint: disable=unused-import + HAS_LIBCLOUD = True except ImportError: HAS_LIBCLOUD = False # pylint: enable=import-error -@skipIf(HAS_LIBCLOUD is False, 'salt-cloud requires >= libcloud 0.11.4') -class SaltCloudCliTest(ShellCase, - ShellCaseCommonTestsMixin): +@skipIf(HAS_LIBCLOUD is False, "salt-cloud requires >= libcloud 0.11.4") +class SaltCloudCliTest(ShellCase, ShellCaseCommonTestsMixin): - _call_binary_ = 'salt-cloud' + _call_binary_ = "salt-cloud" def test_function_arguments(self): self.assertIn( - 'error: --function expects two arguments: ' - '<function-name> <provider>', - '\n'.join(self.run_cloud('--function show_image -h', catch_stderr=True)[1]) + "error: --function expects two arguments: " "<function-name> <provider>", + "\n".join(self.run_cloud("--function show_image -h", catch_stderr=True)[1]), ) def test_list_providers_accepts_no_arguments(self): self.assertIn( - 'error: \'--list-providers\' does not accept any ' - 'arguments', - '\n'.join(self.run_cloud('--list-providers ec2', catch_stderr=True)[1]) + "error: '--list-providers' does not accept any " "arguments", + "\n".join(self.run_cloud("--list-providers ec2", catch_stderr=True)[1]), ) def test_mutually_exclusive_query_options(self): - test_options = [ - '--query', '--full-query', '--select-query', '--list-providers' - ] + test_options = ["--query", "--full-query", "--select-query", "--list-providers"] while True: for idx in range(1, len(test_options)): self.assertIn( - 'error: The options {0}/{1} are mutually ' - 'exclusive. Please only choose one of them'.format( + "error: The options {0}/{1} are mutually " + "exclusive. Please only choose one of them".format( test_options[0], test_options[idx] ), - '\n'.join( + "\n".join( self.run_cloud( - '{0} {1}'.format(test_options[0], test_options[idx]), - catch_stderr=True)[1] - ) + "{0} {1}".format(test_options[0], test_options[idx]), + catch_stderr=True, + )[1] + ), ) # Remove the first option from the list test_options.pop(0) @@ -73,21 +71,20 @@ class SaltCloudCliTest(ShellCase, break def test_mutually_exclusive_list_options(self): - test_options = ['--list-locations', '--list-images', '--list-sizes'] + test_options = ["--list-locations", "--list-images", "--list-sizes"] while True: for idx in range(1, len(test_options)): output = self.run_cloud( - '{0} ec2 {1} ec2'.format( - test_options[0], test_options[idx] - ), catch_stderr=True + "{0} ec2 {1} ec2".format(test_options[0], test_options[idx]), + catch_stderr=True, ) try: self.assertIn( - 'error: The options {0}/{1} are mutually ' - 'exclusive. Please only choose one of them'.format( + "error: The options {0}/{1} are mutually " + "exclusive. Please only choose one of them".format( test_options[0], test_options[idx] ), - '\n'.join(output[1]) + "\n".join(output[1]), ) except AssertionError: print(output) diff --git a/tests/integration/shell/test_cp.py b/tests/integration/shell/test_cp.py index 60901bd4f87..8b1832f82dd 100644 --- a/tests/integration/shell/test_cp.py +++ b/tests/integration/shell/test_cp.py @@ -1,43 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.shell.cp ~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import python libs from __future__ import absolute_import + +import logging import os import pipes -import logging -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS +import pytest +import salt.utils.files +import salt.utils.platform +import salt.utils.yaml +from salt.ext import six from tests.support.case import ShellCase from tests.support.mixins import ShellCaseCommonTestsMixin - -# Import salt libs -import salt.utils.platform -import salt.utils.files -import salt.utils.yaml - -# Import 3rd-party libs -from salt.ext import six - +from tests.support.runtests import RUNTIME_VARS log = logging.getLogger(__name__) +@pytest.mark.windows_whitelisted class CopyTest(ShellCase, ShellCaseCommonTestsMixin): - _call_binary_ = 'salt-cp' + _call_binary_ = "salt-cp" def test_cp_testfile(self): - ''' + """ test salt-cp - ''' + """ minions = [] for line in self.run_salt('--out yaml "*" test.ping'): if not line: @@ -50,10 +46,13 @@ class CopyTest(ShellCase, ShellCaseCommonTestsMixin): testfile = os.path.abspath( os.path.join( os.path.dirname(os.path.dirname(__file__)), - 'files', 'file', 'base', 'testfile' + "files", + "file", + "base", + "testfile", ) ) - with salt.utils.files.fopen(testfile, 'r') as fh_: + with salt.utils.files.fopen(testfile, "r") as fh_: testfile_contents = fh_.read() def quote(arg): @@ -62,63 +61,58 @@ class CopyTest(ShellCase, ShellCaseCommonTestsMixin): return pipes.quote(arg) for idx, minion in enumerate(minions): - if 'localhost' in minion: + if "localhost" in minion: continue ret = self.run_salt( - '--out yaml {0} file.directory_exists {1}'.format( + "--out yaml {0} file.directory_exists {1}".format( quote(minion), RUNTIME_VARS.TMP ) ) - data = salt.utils.yaml.safe_load('\n'.join(ret)) + data = salt.utils.yaml.safe_load("\n".join(ret)) if data[minion] is False: ret = self.run_salt( - '--out yaml {0} file.makedirs {1}'.format( - quote(minion), - RUNTIME_VARS.TMP + "--out yaml {0} file.makedirs {1}".format( + quote(minion), RUNTIME_VARS.TMP ) ) - data = salt.utils.yaml.safe_load('\n'.join(ret)) + data = salt.utils.yaml.safe_load("\n".join(ret)) self.assertTrue(data[minion]) minion_testfile = os.path.join( - RUNTIME_VARS.TMP, 'cp_{0}_testfile'.format(idx) + RUNTIME_VARS.TMP, "cp_{0}_testfile".format(idx) ) - ret = self.run_cp('--out pprint {0} {1} {2}'.format( - quote(minion), - quote(testfile), - quote(minion_testfile), - )) + ret = self.run_cp( + "--out pprint {0} {1} {2}".format( + quote(minion), quote(testfile), quote(minion_testfile), + ) + ) - data = eval('\n'.join(ret), {}, {}) # pylint: disable=eval-used + data = eval("\n".join(ret), {}, {}) # pylint: disable=eval-used for part in six.itervalues(data): key = minion_testfile self.assertTrue(part[key]) ret = self.run_salt( - '--out yaml {0} file.file_exists {1}'.format( - quote(minion), - quote(minion_testfile) + "--out yaml {0} file.file_exists {1}".format( + quote(minion), quote(minion_testfile) ) ) - data = salt.utils.yaml.safe_load('\n'.join(ret)) + data = salt.utils.yaml.safe_load("\n".join(ret)) self.assertTrue(data[minion]) ret = self.run_salt( - '--out yaml {0} file.contains {1} {2}'.format( - quote(minion), - quote(minion_testfile), - quote(testfile_contents) + "--out yaml {0} file.contains {1} {2}".format( + quote(minion), quote(minion_testfile), quote(testfile_contents) ) ) - data = salt.utils.yaml.safe_load('\n'.join(ret)) + data = salt.utils.yaml.safe_load("\n".join(ret)) self.assertTrue(data[minion]) ret = self.run_salt( - '--out yaml {0} file.remove {1}'.format( - quote(minion), - quote(minion_testfile) + "--out yaml {0} file.remove {1}".format( + quote(minion), quote(minion_testfile) ) ) - data = salt.utils.yaml.safe_load('\n'.join(ret)) + data = salt.utils.yaml.safe_load("\n".join(ret)) self.assertTrue(data[minion]) diff --git a/tests/integration/shell/test_enabled.py b/tests/integration/shell/test_enabled.py index 40ff0240412..4d7f97a609d 100644 --- a/tests/integration/shell/test_enabled.py +++ b/tests/integration/shell/test_enabled.py @@ -1,105 +1,127 @@ # -*- coding: utf-8 -*- -# Import Python Libs from __future__ import absolute_import + import os import textwrap -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.runtests import RUNTIME_VARS - -# Import Salt Libs -import salt.utils.platform +import pytest import salt.utils.files +import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf +@pytest.mark.windows_whitelisted class EnabledTest(ModuleCase): - ''' + """ validate the use of shell processing for cmd.run on the salt command line and in templating - ''' - cmd = ("printf '%s\n' first second third | wc -l ; " - "export SALTY_VARIABLE='saltines' && echo $SALTY_VARIABLE ; " - "echo duh &> /dev/null") + """ - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + cmd = ( + "printf '%s\n' first second third | wc -l ; " + "export SALTY_VARIABLE='saltines' && echo $SALTY_VARIABLE ; " + "echo duh &> /dev/null" + ) + + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_shell_default_enabled(self): - ''' + """ ensure that python_shell defaults to True for cmd.run - ''' - enabled_ret = '3\nsaltines' # the result of running self.cmd in a shell - ret = self.run_function('cmd.run', [self.cmd]) + """ + enabled_ret = "3\nsaltines" # the result of running self.cmd in a shell + ret = self.run_function("cmd.run", [self.cmd]) self.assertEqual(ret.strip(), enabled_ret) - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_shell_disabled(self): - ''' + """ test shell disabled output for cmd.run - ''' - disabled_ret = ('first\nsecond\nthird\n|\nwc\n-l\n;\nexport\nSALTY_VARIABLE=saltines' - '\n&&\necho\n$SALTY_VARIABLE\n;\necho\nduh\n&>\n/dev/null') - ret = self.run_function('cmd.run', [self.cmd], python_shell=False) + """ + disabled_ret = ( + "first\nsecond\nthird\n|\nwc\n-l\n;\nexport\nSALTY_VARIABLE=saltines" + "\n&&\necho\n$SALTY_VARIABLE\n;\necho\nduh\n&>\n/dev/null" + ) + ret = self.run_function("cmd.run", [self.cmd], python_shell=False) self.assertEqual(ret, disabled_ret) - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_template_shell(self): - ''' + """ Test cmd.shell works correctly when using a template. Note: This test used to test that python_shell defaulted to True for templates in releases before 2017.7.0. The cmd.run --> cmd.shell aliasing was removed in 2017.7.0. Templates should now be using cmd.shell. - ''' - state_name = 'template_shell_enabled' - state_filename = state_name + '.sls' + """ + state_name = "template_shell_enabled" + state_filename = state_name + ".sls" state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) - enabled_ret = '3 saltines' # the result of running self.cmd in a shell - ret_key = 'test_|-shell_enabled_|-{0}_|-configurable_test_state'.format(enabled_ret) + enabled_ret = "3 saltines" # the result of running self.cmd in a shell + ret_key = "test_|-shell_enabled_|-{0}_|-configurable_test_state".format( + enabled_ret + ) try: - with salt.utils.files.fopen(state_file, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + with salt.utils.files.fopen(state_file, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ {{% set shell_enabled = salt['cmd.shell']("{0}").strip() %}} shell_enabled: test.configurable_test_state: - name: '{{{{ shell_enabled }}}}' - '''.format(self.cmd))) + """.format( + self.cmd + ) + ) + ) - ret = self.run_function('state.sls', [state_name]) - self.assertEqual(ret[ret_key]['name'], enabled_ret) + ret = self.run_function("state.sls", [state_name]) + self.assertEqual(ret[ret_key]["name"], enabled_ret) finally: os.remove(state_file) - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_template_default_disabled(self): - ''' + """ test shell disabled output for templates (python_shell=False is the default beginning with the 2017.7.0 release). - ''' - state_name = 'template_shell_disabled' - state_filename = state_name + '.sls' + """ + state_name = "template_shell_disabled" + state_filename = state_name + ".sls" state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) # the result of running self.cmd not in a shell - disabled_ret = ('first second third | wc -l ; export SALTY_VARIABLE=saltines ' - '&& echo $SALTY_VARIABLE ; echo duh &> /dev/null') - ret_key = 'test_|-shell_enabled_|-{0}_|-configurable_test_state'.format(disabled_ret) + disabled_ret = ( + "first second third | wc -l ; export SALTY_VARIABLE=saltines " + "&& echo $SALTY_VARIABLE ; echo duh &> /dev/null" + ) + ret_key = "test_|-shell_enabled_|-{0}_|-configurable_test_state".format( + disabled_ret + ) try: - with salt.utils.files.fopen(state_file, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + with salt.utils.files.fopen(state_file, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ {{% set shell_disabled = salt['cmd.run']("{0}") %}} shell_enabled: test.configurable_test_state: - name: '{{{{ shell_disabled }}}}' - '''.format(self.cmd))) + """.format( + self.cmd + ) + ) + ) - ret = self.run_function('state.sls', [state_name]) - self.assertEqual(ret[ret_key]['name'], disabled_ret) + ret = self.run_function("state.sls", [state_name]) + self.assertEqual(ret[ret_key]["name"], disabled_ret) finally: os.remove(state_file) diff --git a/tests/integration/shell/test_key.py b/tests/integration/shell/test_key.py index 1265b072c73..e33d99c3a2c 100644 --- a/tests/integration/shell/test_key.py +++ b/tests/integration/shell/test_key.py @@ -1,75 +1,76 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import tempfile import textwrap -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ShellCase -from tests.support.mixins import ShellCaseCommonTestsMixin -from tests.support.helpers import skip_if_not_root, destructiveTest - -# Import 3rd-party libs -from salt.ext import six - -# Import Salt libs +import pytest import salt.utils.files import salt.utils.platform import salt.utils.yaml +from salt.ext import six +from tests.support.case import ShellCase +from tests.support.helpers import destructiveTest, skip_if_not_root +from tests.support.mixins import ShellCaseCommonTestsMixin +from tests.support.runtests import RUNTIME_VARS -USERA = 'saltdev' -USERA_PWD = 'saltdev' -HASHED_USERA_PWD = '$6$SALTsalt$ZZFD90fKFWq8AGmmX0L3uBtS9fXL62SrTk5zcnQ6EkD6zoiM3kB88G1Zvs0xm/gZ7WXJRs5nsTBybUvGSqZkT.' +USERA = "saltdev" +USERA_PWD = "saltdev" +HASHED_USERA_PWD = "$6$SALTsalt$ZZFD90fKFWq8AGmmX0L3uBtS9fXL62SrTk5zcnQ6EkD6zoiM3kB88G1Zvs0xm/gZ7WXJRs5nsTBybUvGSqZkT." +@pytest.mark.windows_whitelisted class KeyTest(ShellCase, ShellCaseCommonTestsMixin): - ''' + """ Test salt-key script - ''' + """ - _call_binary_ = 'salt-key' + _call_binary_ = "salt-key" def _add_user(self): - ''' + """ helper method to add user - ''' + """ try: - add_user = self.run_call('user.add {0} createhome=False'.format(USERA)) - add_pwd = self.run_call('shadow.set_password {0} \'{1}\''.format(USERA, - USERA_PWD if salt.utils.platform.is_darwin() else HASHED_USERA_PWD)) + add_user = self.run_call("user.add {0} createhome=False".format(USERA)) + add_pwd = self.run_call( + "shadow.set_password {0} '{1}'".format( + USERA, + USERA_PWD if salt.utils.platform.is_darwin() else HASHED_USERA_PWD, + ) + ) self.assertTrue(add_user) self.assertTrue(add_pwd) - user_list = self.run_call('user.list_users') + user_list = self.run_call("user.list_users") self.assertIn(USERA, six.text_type(user_list)) except AssertionError: - self.run_call('user.delete {0} remove=True'.format(USERA)) - self.skipTest( - 'Could not add user or password, skipping test' - ) + self.run_call("user.delete {0} remove=True".format(USERA)) + self.skipTest("Could not add user or password, skipping test") def _remove_user(self): - ''' + """ helper method to remove user - ''' - user_list = self.run_call('user.list_users') + """ + user_list = self.run_call("user.list_users") for user in user_list: if USERA in user: - self.run_call('user.delete {0} remove=True'.format(USERA)) + self.run_call("user.delete {0} remove=True".format(USERA)) def test_remove_key(self): - ''' + """ test salt-key -d usage - ''' - min_name = 'minibar' - pki_dir = self.master_opts['pki_dir'] - key = os.path.join(pki_dir, 'minions', min_name) + """ + min_name = "minibar" + pki_dir = self.master_opts["pki_dir"] + key = os.path.join(pki_dir, "minions", min_name) - with salt.utils.files.fopen(key, 'w') as fp: - fp.write(textwrap.dedent('''\ + with salt.utils.files.fopen(key, "w") as fp: + fp.write( + textwrap.dedent( + """\ -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoqIZDtcQtqUNs0wC7qQz JwFhXAVNT5C8M8zhI+pFtF/63KoN5k1WwAqP2j3LquTG68WpxcBwLtKfd7FVA/Kr @@ -79,163 +80,186 @@ class KeyTest(ShellCase, ShellCaseCommonTestsMixin): mkzpmuHkyi2wV33A9pDfMgRHdln2CLX0KgfRGixUQhW1o+Kmfv2rq4sGwpCgLbTh NwIDAQAB -----END PUBLIC KEY----- - ''')) + """ + ) + ) - check_key = self.run_key('-p {0}'.format(min_name)) - self.assertIn('Accepted Keys:', check_key) - self.assertIn('minibar: -----BEGIN PUBLIC KEY-----', check_key) + check_key = self.run_key("-p {0}".format(min_name)) + self.assertIn("Accepted Keys:", check_key) + self.assertIn("minibar: -----BEGIN PUBLIC KEY-----", check_key) - remove_key = self.run_key('-d {0} -y'.format(min_name)) + remove_key = self.run_key("-d {0} -y".format(min_name)) - check_key = self.run_key('-p {0}'.format(min_name)) + check_key = self.run_key("-p {0}".format(min_name)) self.assertEqual([], check_key) def test_list_accepted_args(self): - ''' + """ test salt-key -l for accepted arguments - ''' - for key in ('acc', 'pre', 'den', 'un', 'rej'): + """ + for key in ("acc", "pre", "den", "un", "rej"): # These should not trigger any error - data = self.run_key('-l {0}'.format(key), catch_stderr=True) - self.assertNotIn('error:', '\n'.join(data[1])) - data = self.run_key('-l foo-{0}'.format(key), catch_stderr=True) - self.assertIn('error:', '\n'.join(data[1])) + data = self.run_key("-l {0}".format(key), catch_stderr=True) + self.assertNotIn("error:", "\n".join(data[1])) + data = self.run_key("-l foo-{0}".format(key), catch_stderr=True) + self.assertIn("error:", "\n".join(data[1])) def test_list_all(self): - ''' + """ test salt-key -L - ''' - data = self.run_key('-L') + """ + data = self.run_key("-L") expect = None - if self.master_opts['transport'] in ('zeromq', 'tcp'): + if self.master_opts["transport"] in ("zeromq", "tcp"): expect = [ - 'Accepted Keys:', - 'minion', - 'sub_minion', - 'Denied Keys:', - 'Unaccepted Keys:', - 'Rejected Keys:' + "Accepted Keys:", + "minion", + "sub_minion", + "Denied Keys:", + "Unaccepted Keys:", + "Rejected Keys:", ] self.assertEqual(data, expect) def test_list_json_out(self): - ''' + """ test salt-key -L --json-out - ''' - data = self.run_key('-L --out json') + """ + data = self.run_key("-L --out json") ret = {} try: import salt.utils.json - ret = salt.utils.json.loads('\n'.join(data)) + + ret = salt.utils.json.loads("\n".join(data)) except ValueError: pass expect = None - if self.master_opts['transport'] in ('zeromq', 'tcp'): - expect = {'minions_rejected': [], - 'minions_denied': [], - 'minions_pre': [], - 'minions': ['minion', 'sub_minion']} + if self.master_opts["transport"] in ("zeromq", "tcp"): + expect = { + "minions_rejected": [], + "minions_denied": [], + "minions_pre": [], + "minions": ["minion", "sub_minion"], + } self.assertEqual(ret, expect) def test_list_yaml_out(self): - ''' + """ test salt-key -L --yaml-out - ''' - data = self.run_key('-L --out yaml') + """ + data = self.run_key("-L --out yaml") ret = {} try: import salt.utils.yaml - ret = salt.utils.yaml.safe_load('\n'.join(data)) + + ret = salt.utils.yaml.safe_load("\n".join(data)) except Exception: # pylint: disable=broad-except pass expect = [] - if self.master_opts['transport'] in ('zeromq', 'tcp'): - expect = {'minions_rejected': [], - 'minions_denied': [], - 'minions_pre': [], - 'minions': ['minion', 'sub_minion']} + if self.master_opts["transport"] in ("zeromq", "tcp"): + expect = { + "minions_rejected": [], + "minions_denied": [], + "minions_pre": [], + "minions": ["minion", "sub_minion"], + } self.assertEqual(ret, expect) def test_list_raw_out(self): - ''' + """ test salt-key -L --raw-out - ''' - data = self.run_key('-L --out raw') + """ + data = self.run_key("-L --out raw") self.assertEqual(len(data), 1) ret = {} try: import ast + ret = ast.literal_eval(data[0]) except ValueError: pass expect = None - if self.master_opts['transport'] in ('zeromq', 'tcp'): - expect = {'minions_rejected': [], - 'minions_denied': [], - 'minions_pre': [], - 'minions': ['minion', 'sub_minion']} + if self.master_opts["transport"] in ("zeromq", "tcp"): + expect = { + "minions_rejected": [], + "minions_denied": [], + "minions_pre": [], + "minions": ["minion", "sub_minion"], + } self.assertEqual(ret, expect) def test_list_acc(self): - ''' + """ test salt-key -l - ''' - data = self.run_key('-l acc') - expect = ['Accepted Keys:', 'minion', 'sub_minion'] + """ + data = self.run_key("-l acc") + expect = ["Accepted Keys:", "minion", "sub_minion"] self.assertEqual(data, expect) @skip_if_not_root @destructiveTest def test_list_acc_eauth(self): - ''' + """ test salt-key -l with eauth - ''' + """ self._add_user() - data = self.run_key('-l acc --eauth pam --username {0} --password {1}'.format(USERA, USERA_PWD)) - expect = ['Accepted Keys:', 'minion', 'sub_minion'] + data = self.run_key( + "-l acc --eauth pam --username {0} --password {1}".format(USERA, USERA_PWD) + ) + expect = ["Accepted Keys:", "minion", "sub_minion"] self.assertEqual(data, expect) self._remove_user() @skip_if_not_root @destructiveTest def test_list_acc_eauth_bad_creds(self): - ''' + """ test salt-key -l with eauth and bad creds - ''' + """ self._add_user() - data = self.run_key('-l acc --eauth pam --username {0} --password wrongpassword'.format(USERA)) - expect = ['Authentication failure of type "eauth" occurred for user {0}.'.format(USERA)] + data = self.run_key( + "-l acc --eauth pam --username {0} --password wrongpassword".format(USERA) + ) + expect = [ + 'Authentication failure of type "eauth" occurred for user {0}.'.format( + USERA + ) + ] self.assertEqual(data, expect) self._remove_user() def test_list_acc_wrong_eauth(self): - ''' + """ test salt-key -l with wrong eauth - ''' - data = self.run_key('-l acc --eauth wrongeauth --username {0} --password {1}'.format(USERA, USERA_PWD)) + """ + data = self.run_key( + "-l acc --eauth wrongeauth --username {0} --password {1}".format( + USERA, USERA_PWD + ) + ) expect = r"^The specified external authentication system \"wrongeauth\" is not available\tAvailable eauth types: auto, .*" self.assertRegex("\t".join(data), expect) def test_list_un(self): - ''' + """ test salt-key -l - ''' - data = self.run_key('-l un') - expect = ['Unaccepted Keys:'] + """ + data = self.run_key("-l un") + expect = ["Unaccepted Keys:"] self.assertEqual(data, expect) def test_keys_generation(self): tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - arg_str = '--gen-keys minibar --gen-keys-dir {0}'.format(tempdir) + arg_str = "--gen-keys minibar --gen-keys-dir {0}".format(tempdir) self.run_key(arg_str) try: key_names = None - if self.master_opts['transport'] in ('zeromq', 'tcp'): - key_names = ('minibar.pub', 'minibar.pem') + if self.master_opts["transport"] in ("zeromq", "tcp"): + key_names = ("minibar.pub", "minibar.pem") for fname in key_names: self.assertTrue(os.path.isfile(os.path.join(tempdir, fname))) finally: @@ -246,21 +270,16 @@ class KeyTest(ShellCase, ShellCaseCommonTestsMixin): def test_keys_generation_keysize_minmax(self): tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - arg_str = '--gen-keys minion --gen-keys-dir {0}'.format(tempdir) + arg_str = "--gen-keys minion --gen-keys-dir {0}".format(tempdir) try: - data, error = self.run_key( - arg_str + ' --keysize=1024', catch_stderr=True - ) + data, error = self.run_key(arg_str + " --keysize=1024", catch_stderr=True) self.assertIn( - 'error: The minimum value for keysize is 2048', '\n'.join(error) + "error: The minimum value for keysize is 2048", "\n".join(error) ) - data, error = self.run_key( - arg_str + ' --keysize=32769', catch_stderr=True - ) + data, error = self.run_key(arg_str + " --keysize=32769", catch_stderr=True) self.assertIn( - 'error: The maximum value for keysize is 32768', - '\n'.join(error) + "error: The maximum value for keysize is 32768", "\n".join(error) ) finally: shutil.rmtree(tempdir) diff --git a/tests/integration/shell/test_master.py b/tests/integration/shell/test_master.py index 0d8224b3f7f..72b9e5f9d9d 100644 --- a/tests/integration/shell/test_master.py +++ b/tests/integration/shell/test_master.py @@ -1,51 +1,50 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.shell.master ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import python libs from __future__ import absolute_import -# Import salt test libs +import pytest import tests.integration.utils +from tests.integration.utils import testprogram from tests.support.case import ShellCase from tests.support.mixins import ShellCaseCommonTestsMixin from tests.support.unit import skipIf -from tests.integration.utils import testprogram -@skipIf(True, 'This test file should be in an isolated test space.') +@skipIf(True, "This test file should be in an isolated test space.") +@pytest.mark.windows_whitelisted class MasterTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin): - _call_binary_ = 'salt-master' + _call_binary_ = "salt-master" def test_exit_status_unknown_user(self): - ''' + """ Ensure correct exit status when the master is configured to run as an unknown user. - ''' + """ master = testprogram.TestDaemonSaltMaster( - name='unknown_user', - configs={'master': {'map': {'user': 'some_unknown_user_xyz'}}}, + name="unknown_user", + configs={"master": {"map": {"user": "some_unknown_user_xyz"}}}, parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist master.setup() stdout, stderr, status = master.run( - args=['-d'], - catch_stderr=True, - with_retcode=True, + args=["-d"], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_NOUSER', - message='unknown user not on system', + status, + "EX_NOUSER", + message="unknown user not on system", stdout=stdout, - stderr=tests.integration.utils.decode_byte_list(stderr) + stderr=tests.integration.utils.decode_byte_list(stderr), ) finally: # Although the start-up should fail, call shutdown() to set the @@ -54,27 +53,25 @@ class MasterTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix master.shutdown() def test_exit_status_unknown_argument(self): - ''' + """ Ensure correct exit status when an unknown argument is passed to salt-master. - ''' + """ master = testprogram.TestDaemonSaltMaster( - name='unknown_argument', - parent_dir=self._test_dir, + name="unknown_argument", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist master.setup() stdout, stderr, status = master.run( - args=['-d', '--unknown-argument'], - catch_stderr=True, - with_retcode=True, + args=["-d", "--unknown-argument"], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_USAGE', - message='unknown argument', + status, + "EX_USAGE", + message="unknown argument", stdout=stdout, - stderr=tests.integration.utils.decode_byte_list(stderr) + stderr=tests.integration.utils.decode_byte_list(stderr), ) finally: # Although the start-up should fail, call shutdown() to set the @@ -83,27 +80,25 @@ class MasterTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix master.shutdown() def test_exit_status_correct_usage(self): - ''' + """ Ensure correct exit status when salt-master starts correctly. - ''' + """ master = testprogram.TestDaemonSaltMaster( - name='correct_usage', - parent_dir=self._test_dir, + name="correct_usage", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist master.setup() stdout, stderr, status = master.run( - args=['-d'], - catch_stderr=True, - with_retcode=True, + args=["-d"], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_OK', - message='correct usage', + status, + "EX_OK", + message="correct usage", stdout=stdout, - stderr=tests.integration.utils.decode_byte_list(stderr) + stderr=tests.integration.utils.decode_byte_list(stderr), ) finally: master.shutdown(wait_for_orphans=3) diff --git a/tests/integration/shell/test_master_tops.py b/tests/integration/shell/test_master_tops.py index e0439e8a2ed..43dac77a280 100644 --- a/tests/integration/shell/test_master_tops.py +++ b/tests/integration/shell/test_master_tops.py @@ -1,24 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" tests.integration.shell.master_tops ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest from tests.support.case import ShellCase +@pytest.mark.windows_whitelisted class MasterTopsTest(ShellCase): - _call_binary_ = 'salt' + _call_binary_ = "salt" def test_custom_tops_gets_utilized(self): - resp = self.run_call( - 'state.show_top' - ) - self.assertTrue( - any('master_tops_test' in _x for _x in resp) - ) + resp = self.run_call("state.show_top") + self.assertTrue(any("master_tops_test" in _x for _x in resp)) diff --git a/tests/integration/shell/test_matcher.py b/tests/integration/shell/test_matcher.py index 57601d4e191..60bebb718a6 100644 --- a/tests/integration/shell/test_matcher.py +++ b/tests/integration/shell/test_matcher.py @@ -1,360 +1,352 @@ # -*- coding: utf-8 -*- -# pylint: disable=invalid-name -# Import python libs from __future__ import absolute_import + import time -# Import Salt Testing libs +import pytest +import salt.utils.files +import salt.utils.yaml from tests.support.case import ShellCase -from tests.support.helpers import flaky, dedent +from tests.support.helpers import dedent, flaky from tests.support.mixins import ShellCaseCommonTestsMixin from tests.support.unit import skipIf -# Import salt libs -import salt.utils.files -import salt.utils.yaml - def minion_in_returns(minion, lines): - return bool([True for line in lines if line == '{0}:'.format(minion)]) + return bool([True for line in lines if line == "{0}:".format(minion)]) +@pytest.mark.windows_whitelisted class MatchTest(ShellCase, ShellCaseCommonTestsMixin): - ''' + """ Test salt matchers - ''' + """ - _call_binary_ = 'salt' + _call_binary_ = "salt" def test_list(self): - ''' + """ test salt -L matcher - ''' - data = self.run_salt('-L minion test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertNotIn('sub_minion', data) - data = self.run_salt('-L minion,sub_minion test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + """ + data = self.run_salt("-L minion test.ping") + data = "\n".join(data) + self.assertIn("minion", data) + self.assertNotIn("sub_minion", data) + data = self.run_salt("-L minion,sub_minion test.ping") + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) - # compound matcher tests: 11 + # compound matcher tests: 12 def test_compound_min_with_grain(self): - ''' + """ test salt compound matcher - ''' + """ data = self.run_salt('-C "min* and G@test_grain:cheese" test.ping') - assert minion_in_returns('minion', data) is True - assert minion_in_returns('sub_minion', data) is False + assert minion_in_returns("minion", data) is True + assert minion_in_returns("sub_minion", data) is False def test_compound_and_not_grain(self): data = self.run_salt('-C "min* and not G@test_grain:foo" test.ping') - assert minion_in_returns('minion', data) is True - assert minion_in_returns('sub_minion', data) is False + assert minion_in_returns("minion", data) is True + assert minion_in_returns("sub_minion", data) is False def test_compound_not_grain(self): data = self.run_salt('-C "min* not G@test_grain:foo" test.ping') - assert minion_in_returns('minion', data) is True - assert minion_in_returns('sub_minion', data) is False + assert minion_in_returns("minion", data) is True + assert minion_in_returns("sub_minion", data) is False def test_compound_pcre_grain_and_grain(self): - match = 'P@test_grain:^cheese$ and * and G@test_grain:cheese' + match = "P@test_grain:^cheese$ and * and G@test_grain:cheese" data = self.run_salt('-t 1 -C "{0}" test.ping'.format(match)) - assert minion_in_returns('minion', data) is True - assert minion_in_returns('sub_minion', data) is False + assert minion_in_returns("minion", data) is True + assert minion_in_returns("sub_minion", data) is False def test_compound_list_and_pcre_minion(self): - match = 'L@sub_minion and E@.*' + match = "L@sub_minion and E@.*" data = self.run_salt('-t 1 -C "{0}" test.ping'.format(match)) - assert minion_in_returns('sub_minion', data) is True - assert minion_in_returns('minion', data) is False + assert minion_in_returns("sub_minion", data) is True + assert minion_in_returns("minion", data) is False def test_compound_not_sub_minion(self): data = self.run_salt('-C "not sub_minion" test.ping') - assert minion_in_returns('minion', data) is True - assert minion_in_returns('sub_minion', data) is False + assert minion_in_returns("minion", data) is True + assert minion_in_returns("sub_minion", data) is False def test_compound_all_and_not_grains(self): data = self.run_salt('-C "* and ( not G@test_grain:cheese )" test.ping') - assert minion_in_returns('minion', data) is False - assert minion_in_returns('sub_minion', data) is True + assert minion_in_returns("minion", data) is False + assert minion_in_returns("sub_minion", data) is True def test_compound_grain_regex(self): data = self.run_salt('-C "G%@planets%merc*" test.ping') - assert minion_in_returns('minion', data) is True - assert minion_in_returns('sub_minion', data) is False + assert minion_in_returns("minion", data) is True + assert minion_in_returns("sub_minion", data) is False - def test_coumpound_pcre_grain_regex(self): + def test_compound_pcre_grain_regex(self): data = self.run_salt('-C "P%@planets%^(mercury|saturn)$" test.ping') - assert minion_in_returns('minion', data) is True - assert minion_in_returns('sub_minion', data) is True + assert minion_in_returns("minion", data) is True + assert minion_in_returns("sub_minion", data) is True - @skipIf(True, 'This test is unreliable. Need to investigate why more deeply.') + @skipIf(True, "This test is unreliable. Need to investigate why more deeply.") @flaky def test_compound_pillar(self): data = self.run_salt("-C 'I%@companions%three%sarah*' test.ping") - assert minion_in_returns('minion', data) is True - assert minion_in_returns('sub_minion', data) is True + assert minion_in_returns("minion", data) is True + assert minion_in_returns("sub_minion", data) is True - @skipIf(True, 'This test is unreliable. Need to investigate why more deeply.') + @skipIf(True, "This test is unreliable. Need to investigate why more deeply.") @flaky - def test_coumpound_pillar_pcre(self): + def test_compound_pillar_pcre(self): data = self.run_salt("-C 'J%@knights%^(Lancelot|Galahad)$' test.ping") - self.assertTrue(minion_in_returns('minion', data)) - self.assertTrue(minion_in_returns('sub_minion', data)) - # The multiline nodegroup tests are failing in develop. - # This needs to be fixed for Fluorine. @skipIf wasn't used, because - # the rest of the assertions above pass just fine, so we don't want - # to bypass the whole test. - # time.sleep(2) - # data = self.run_salt("-C 'N@multiline_nodegroup' test.ping") - # self.assertTrue(minion_in_returns('minion', data)) - # self.assertTrue(minion_in_returns('sub_minion', data)) - # time.sleep(2) - # data = self.run_salt("-C 'N@multiline_nodegroup not sub_minion' test.ping") - # self.assertTrue(minion_in_returns('minion', data)) - # self.assertFalse(minion_in_returns('sub_minion', data)) - # data = self.run_salt("-C 'N@multiline_nodegroup not @fakenodegroup not sub_minion' test.ping") - # self.assertTrue(minion_in_returns('minion', data)) - # self.assertFalse(minion_in_returns('sub_minion', data)) + self.assertTrue(minion_in_returns("minion", data)) + self.assertTrue(minion_in_returns("sub_minion", data)) + + def test_compound_nodegroup(self): + data = self.run_salt('-C "N@multiline_nodegroup" test.ping') + self.assertTrue(minion_in_returns("minion", data)) + self.assertTrue(minion_in_returns("sub_minion", data)) + data = self.run_salt('-C "N@multiline_nodegroup not sub_minion" test.ping') + self.assertTrue(minion_in_returns("minion", data)) + self.assertFalse(minion_in_returns("sub_minion", data)) + data = self.run_salt( + '-C "N@multiline_nodegroup not @fakenodegroup not sub_minion" test.ping' + ) + self.assertTrue(minion_in_returns("minion", data)) + self.assertFalse(minion_in_returns("sub_minion", data)) def test_nodegroup(self): - ''' + """ test salt nodegroup matcher - ''' - data = self.run_salt('-N min test.ping') - self.assertTrue(minion_in_returns('minion', data)) - self.assertFalse(minion_in_returns('sub_minion', data)) + """ + data = self.run_salt("-N min test.ping") + self.assertTrue(minion_in_returns("minion", data)) + self.assertFalse(minion_in_returns("sub_minion", data)) time.sleep(2) - data = self.run_salt('-N sub_min test.ping') - self.assertFalse(minion_in_returns('minion', data)) - self.assertTrue(minion_in_returns('sub_minion', data)) + data = self.run_salt("-N sub_min test.ping") + self.assertFalse(minion_in_returns("minion", data)) + self.assertTrue(minion_in_returns("sub_minion", data)) time.sleep(2) - data = self.run_salt('-N mins test.ping') - self.assertTrue(minion_in_returns('minion', data)) - self.assertTrue(minion_in_returns('sub_minion', data)) + data = self.run_salt("-N mins test.ping") + self.assertTrue(minion_in_returns("minion", data)) + self.assertTrue(minion_in_returns("sub_minion", data)) time.sleep(2) - data = self.run_salt('-N unknown_nodegroup test.ping') - self.assertFalse(minion_in_returns('minion', data)) - self.assertFalse(minion_in_returns('sub_minion', data)) + data = self.run_salt("-N unknown_nodegroup test.ping") + self.assertFalse(minion_in_returns("minion", data)) + self.assertFalse(minion_in_returns("sub_minion", data)) time.sleep(2) - data = self.run_salt('-N redundant_minions test.ping') - self.assertTrue(minion_in_returns('minion', data)) - self.assertTrue(minion_in_returns('sub_minion', data)) + data = self.run_salt("-N redundant_minions test.ping") + self.assertTrue(minion_in_returns("minion", data)) + self.assertTrue(minion_in_returns("sub_minion", data)) time.sleep(2) - data = '\n'.join(self.run_salt('-N nodegroup_loop_a test.ping')) - self.assertIn('No minions matched', data) + data = "\n".join(self.run_salt("-N nodegroup_loop_a test.ping")) + self.assertIn("No minions matched", data) time.sleep(2) data = self.run_salt("-N multiline_nodegroup test.ping") - self.assertTrue(minion_in_returns('minion', data)) - self.assertTrue(minion_in_returns('sub_minion', data)) + self.assertTrue(minion_in_returns("minion", data)) + self.assertTrue(minion_in_returns("sub_minion", data)) def test_nodegroup_list(self): - data = self.run_salt('-N list_group test.ping') - self.assertTrue(minion_in_returns('minion', data)) - self.assertTrue(minion_in_returns('sub_minion', data)) + data = self.run_salt("-N list_group test.ping") + self.assertTrue(minion_in_returns("minion", data)) + self.assertTrue(minion_in_returns("sub_minion", data)) - data = self.run_salt('-N list_group2 test.ping') - self.assertTrue(minion_in_returns('minion', data)) - self.assertTrue(minion_in_returns('sub_minion', data)) + data = self.run_salt("-N list_group2 test.ping") + self.assertTrue(minion_in_returns("minion", data)) + self.assertTrue(minion_in_returns("sub_minion", data)) - data = self.run_salt('-N one_list_group test.ping') - self.assertTrue(minion_in_returns('minion', data)) - self.assertFalse(minion_in_returns('sub_minion', data)) + data = self.run_salt("-N one_list_group test.ping") + self.assertTrue(minion_in_returns("minion", data)) + self.assertFalse(minion_in_returns("sub_minion", data)) - data = self.run_salt('-N one_minion_list test.ping') - self.assertTrue(minion_in_returns('minion', data)) - self.assertFalse(minion_in_returns('sub_minion', data)) + data = self.run_salt("-N one_minion_list test.ping") + self.assertTrue(minion_in_returns("minion", data)) + self.assertFalse(minion_in_returns("sub_minion", data)) def test_glob(self): - ''' + """ test salt glob matcher - ''' - data = self.run_salt('minion test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertNotIn('sub_minion', data) + """ + data = self.run_salt("minion test.ping") + data = "\n".join(data) + self.assertIn("minion", data) + self.assertNotIn("sub_minion", data) data = self.run_salt('"*" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) def test_regex(self): - ''' + """ test salt regex matcher - ''' + """ data = self.run_salt('-E "^minion$" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertNotIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertNotIn("sub_minion", data) data = self.run_salt('-E ".*" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) def test_grain(self): - ''' + """ test salt grain matcher - ''' + """ # Sync grains self.run_salt('-t1 "*" saltutil.sync_grains') # First-level grain (string value) data = self.run_salt('-t 1 -G "test_grain:cheese" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertNotIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertNotIn("sub_minion", data) data = self.run_salt('-G "test_grain:spam" test.ping') - data = '\n'.join(data) - self.assertIn('sub_minion', data) - self.assertNotIn('minion', data.replace('sub_minion', 'stub')) + data = "\n".join(data) + self.assertIn("sub_minion", data) + self.assertNotIn("minion", data.replace("sub_minion", "stub")) # Custom grain data = self.run_salt('-t 1 -G "match:maker" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) # First-level grain (list member) data = self.run_salt('-t 1 -G "planets:earth" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertNotIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertNotIn("sub_minion", data) data = self.run_salt('-G "planets:saturn" test.ping') - data = '\n'.join(data) - self.assertIn('sub_minion', data) - self.assertNotIn('minion', data.replace('sub_minion', 'stub')) + data = "\n".join(data) + self.assertIn("sub_minion", data) + self.assertNotIn("minion", data.replace("sub_minion", "stub")) data = self.run_salt('-G "planets:pluto" test.ping') expect = None - if self.master_opts['transport'] in ('zeromq', 'tcp'): + if self.master_opts["transport"] in ("zeromq", "tcp"): expect = ( - 'No minions matched the target. ' - 'No command was sent, no jid was ' - 'assigned.' + "No minions matched the target. " + "No command was sent, no jid was " + "assigned." ) - self.assertEqual( - ''.join(data), - expect - ) + self.assertEqual("".join(data), expect) # Nested grain (string value) data = self.run_salt('-t 1 -G "level1:level2:foo" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertNotIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertNotIn("sub_minion", data) data = self.run_salt('-G "level1:level2:bar" test.ping') - data = '\n'.join(data) - self.assertIn('sub_minion', data) - self.assertNotIn('minion', data.replace('sub_minion', 'stub')) + data = "\n".join(data) + self.assertIn("sub_minion", data) + self.assertNotIn("minion", data.replace("sub_minion", "stub")) # Nested grain (list member) data = self.run_salt('-t 1 -G "companions:one:ian" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertNotIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertNotIn("sub_minion", data) data = self.run_salt('-G "companions:two:jamie" test.ping') - data = '\n'.join(data) - self.assertIn('sub_minion', data) - self.assertNotIn('minion', data.replace('sub_minion', 'stub')) + data = "\n".join(data) + self.assertIn("sub_minion", data) + self.assertNotIn("minion", data.replace("sub_minion", "stub")) # Test for issue: https://github.com/saltstack/salt/issues/19651 data = self.run_salt('-G "companions:*:susan" test.ping') - data = '\n'.join(data) - self.assertIn('minion:', data) - self.assertNotIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion:", data) + self.assertNotIn("sub_minion", data) # Test to ensure wildcard at end works correctly data = self.run_salt('-G "companions:one:*" test.ping') - data = '\n'.join(data) - self.assertIn('minion:', data) - self.assertNotIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion:", data) + self.assertNotIn("sub_minion", data) # Test to ensure multiple wildcards works correctly data = self.run_salt('-G "companions:*:*" test.ping') - data = '\n'.join(data) - self.assertIn('minion:', data) - self.assertIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion:", data) + self.assertIn("sub_minion", data) def test_regrain(self): - ''' + """ test salt grain matcher - ''' - data = self.run_salt( - '-t 1 --grain-pcre "test_grain:^cheese$" test.ping' - ) - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertNotIn('sub_minion', data) + """ + data = self.run_salt('-t 1 --grain-pcre "test_grain:^cheese$" test.ping') + data = "\n".join(data) + self.assertIn("minion", data) + self.assertNotIn("sub_minion", data) data = self.run_salt('--grain-pcre "test_grain:.*am$" test.ping') - data = '\n'.join(data) - self.assertIn('sub_minion', data) - self.assertNotIn('minion', data.replace('sub_minion', 'stub')) + data = "\n".join(data) + self.assertIn("sub_minion", data) + self.assertNotIn("minion", data.replace("sub_minion", "stub")) def test_pillar(self): - ''' + """ test pillar matcher - ''' + """ # First-level pillar (string value) data = self.run_salt('-I "monty:python" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) # First-level pillar (string value, only in sub_minion) data = self.run_salt('-I "sub:sub_minion" test.ping') - data = '\n'.join(data) - self.assertIn('sub_minion', data) - self.assertNotIn('minion', data.replace('sub_minion', 'stub')) + data = "\n".join(data) + self.assertIn("sub_minion", data) + self.assertNotIn("minion", data.replace("sub_minion", "stub")) # First-level pillar (list member) data = self.run_salt('-I "knights:Bedevere" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) # Nested pillar (string value) data = self.run_salt('-I "level1:level2:foo" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) # Nested pillar (list member) data = self.run_salt('-I "companions:three:sarah jane" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) def test_repillar(self): - ''' + """ test salt pillar PCRE matcher - ''' + """ data = self.run_salt('-J "monty:^(python|hall)$" test.ping') - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) data = self.run_salt('--pillar-pcre "knights:^(Robin|Lancelot)$" test.ping') - data = '\n'.join(data) - self.assertIn('sub_minion', data) - self.assertIn('minion', data.replace('sub_minion', 'stub')) + data = "\n".join(data) + self.assertIn("sub_minion", data) + self.assertIn("minion", data.replace("sub_minion", "stub")) def test_ipcidr(self): subnets_data = self.run_salt('--out yaml "*" network.subnets') - yaml_data = salt.utils.yaml.safe_load('\n'.join(subnets_data)) + yaml_data = salt.utils.yaml.safe_load("\n".join(subnets_data)) # We're just after the first defined subnet from 'minion' - subnet = yaml_data['minion'][0] + subnet = yaml_data["minion"][0] - data = self.run_salt('-S {0} test.ping'.format(subnet)) - data = '\n'.join(data) - self.assertIn('minion', data) - self.assertIn('sub_minion', data) + data = self.run_salt("-S {0} test.ping".format(subnet)) + data = "\n".join(data) + self.assertIn("minion", data) + self.assertIn("sub_minion", data) def test_static(self): - ''' + """ test salt static call - ''' - data = self.run_salt('minion test.ping --static') - data = '\n'.join(data) - self.assertIn('minion', data) + """ + data = self.run_salt("minion test.ping --static") + data = "\n".join(data) + self.assertIn("minion", data) def test_salt_documentation(self): - ''' + """ Test to see if we're supporting --doc - ''' - expect_to_find = 'test.ping:' + """ + expect_to_find = "test.ping:" stdout, stderr = self.run_salt('-d "*" test', catch_stderr=True) - error_msg = dedent(''' + error_msg = dedent( + """ Failed to find \'{expected}\' in output {sep} @@ -364,15 +356,23 @@ class MatchTest(ShellCase, ShellCaseCommonTestsMixin): --- STDERR ----- {stderr} {sep} - '''.format(sep='-' * 80, - expected=expect_to_find, - stdout='\n'.join(stdout).strip(), - stderr='\n'.join(stderr).strip())) + """.format( + sep="-" * 80, + expected=expect_to_find, + stdout="\n".join(stdout).strip(), + stderr="\n".join(stderr).strip(), + ) + ) self.assertIn(expect_to_find, stdout, msg=error_msg) def test_salt_documentation_too_many_arguments(self): - ''' + """ Test to see if passing additional arguments shows an error - ''' - data = self.run_salt('-d minion salt ldap.search "filter=ou=People"', catch_stderr=True) - self.assertIn('You can only get documentation for one method at one time', '\n'.join(data[1])) + """ + data = self.run_salt( + '-d minion salt ldap.search "filter=ou=People"', catch_stderr=True + ) + self.assertIn( + "You can only get documentation for one method at one time", + "\n".join(data[1]), + ) diff --git a/tests/integration/shell/test_minion.py b/tests/integration/shell/test_minion.py index 47277c4ac18..62b24a7ef7c 100644 --- a/tests/integration/shell/test_minion.py +++ b/tests/integration/shell/test_minion.py @@ -1,91 +1,85 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.shell.minion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import python libs from __future__ import absolute_import + import getpass -import os -import sys -import platform import logging +import os +import platform +import sys -# Import Salt Testing libs -import tests.integration.utils -from tests.support.case import ShellCase -from tests.support.unit import skipIf -from tests.support.mixins import ShellCaseCommonTestsMixin -from tests.integration.utils import testprogram -from tests.support.runtests import RUNTIME_VARS - -# Import 3rd-party libs -from salt.ext import six - -# Import salt libs +import pytest import salt.utils.files -import salt.utils.yaml import salt.utils.platform +import salt.utils.yaml +import tests.integration.utils +from salt.ext import six +from tests.integration.utils import testprogram +from tests.support.case import ShellCase +from tests.support.mixins import ShellCaseCommonTestsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf log = logging.getLogger(__name__) DEBUG = True +@pytest.mark.windows_whitelisted class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin): - ''' + """ Various integration tests for the salt-minion executable. - ''' + """ - _call_binary_ = 'salt-minion' + _call_binary_ = "salt-minion" _test_minions = ( - 'minion', - 'subminion', + "minion", + "subminion", ) def _run_initscript( - self, - init_script, - minions, - minion_running, - action, - exitstatus=None, - message='' + self, init_script, minions, minion_running, action, exitstatus=None, message="" ): - ''' + """ Wrapper that runs the initscript for the configured minions and verifies the results. - ''' + """ user = getpass.getuser() ret = init_script.run( [action], catch_stderr=True, with_retcode=True, env={ - 'SALTMINION_CONFIGS': '\n'.join([ - '{0} {1}'.format(user, minion.abs_path(minion.config_dir)) for minion in minions - ]), + "SALTMINION_CONFIGS": "\n".join( + [ + "{0} {1}".format(user, minion.abs_path(minion.config_dir)) + for minion in minions + ] + ), }, timeout=90, ) for line in ret[0]: - log.debug('script: salt-minion: stdout: {0}'.format(line)) + log.debug("script: salt-minion: stdout: {0}".format(line)) for line in ret[1]: - log.debug('script: salt-minion: stderr: {0}'.format(line)) - log.debug('exit status: {0}'.format(ret[2])) + log.debug("script: salt-minion: stderr: {0}".format(line)) + log.debug("exit status: {0}".format(ret[2])) if six.PY3: - std_out = b'\nSTDOUT:'.join(ret[0]) - std_err = b'\nSTDERR:'.join(ret[1]) + std_out = b"\nSTDOUT:".join(ret[0]) + std_err = b"\nSTDERR:".join(ret[1]) else: - std_out = '\nSTDOUT:'.join(ret[0]) - std_err = '\nSTDERR:'.join(ret[1]) + std_out = "\nSTDOUT:".join(ret[0]) + std_err = "\nSTDERR:".join(ret[1]) # Check minion state for minion in minions: @@ -98,7 +92,7 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix ["stopped", "running"][minion_running], std_out, std_err, - ) + ), ) if exitstatus is not None: @@ -106,34 +100,29 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix ret[2], exitstatus, 'script action "{0}" {1} exited {2}, must be {3}\nSTDOUT:{4}\nSTDERR:{5}'.format( - action, - message, - ret[2], - exitstatus, - std_out, - std_err, - ) + action, message, ret[2], exitstatus, std_out, std_err, + ), ) return ret def _initscript_setup(self, minions): - '''Re-usable setup for running salt-minion tests''' + """Re-usable setup for running salt-minion tests""" _minions = [] for mname in minions: - pid_file = 'salt-{0}.pid'.format(mname) + pid_file = "salt-{0}.pid".format(mname) minion = testprogram.TestDaemonSaltMinion( name=mname, - root_dir='init_script', - config_dir=os.path.join('etc', mname), + root_dir="init_script", + config_dir=os.path.join("etc", mname), parent_dir=self._test_dir, pid_file=pid_file, configs={ - 'minion': { - 'map': { - 'pidfile': os.path.join('var', 'run', pid_file), - 'sock_dir': os.path.join('var', 'run', 'salt', mname), - 'log_file': os.path.join('var', 'log', 'salt', mname), + "minion": { + "map": { + "pidfile": os.path.join("var", "run", pid_file), + "sock_dir": os.path.join("var", "run", "salt", mname), + "log_file": os.path.join("var", "log", "salt", mname), }, }, }, @@ -143,45 +132,48 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix _minions.append(minion) # Need salt-call, salt-minion for wrapper script - salt_call = testprogram.TestProgramSaltCall(root_dir='init_script', parent_dir=self._test_dir) + salt_call = testprogram.TestProgramSaltCall( + root_dir="init_script", parent_dir=self._test_dir + ) # Ensure that run-time files are generated salt_call.setup() sysconf_dir = os.path.dirname(_minions[0].abs_path(_minions[0].config_dir)) cmd_env = { - 'PATH': ':'.join([salt_call.abs_path(salt_call.script_dir), os.getenv('PATH')]), - 'SALTMINION_DEBUG': '1' if DEBUG else '', - 'SALTMINION_PYTHON': sys.executable, - 'SALTMINION_SYSCONFDIR': sysconf_dir, - 'SALTMINION_BINDIR': _minions[0].abs_path(_minions[0].script_dir), + "PATH": ":".join( + [salt_call.abs_path(salt_call.script_dir), os.getenv("PATH")] + ), + "SALTMINION_DEBUG": "1" if DEBUG else "", + "SALTMINION_PYTHON": sys.executable, + "SALTMINION_SYSCONFDIR": sysconf_dir, + "SALTMINION_BINDIR": _minions[0].abs_path(_minions[0].script_dir), } - default_dir = os.path.join(sysconf_dir, 'default') + default_dir = os.path.join(sysconf_dir, "default") if not os.path.exists(default_dir): os.makedirs(default_dir) - with salt.utils.files.fopen(os.path.join(default_dir, 'salt'), 'w') as defaults: + with salt.utils.files.fopen(os.path.join(default_dir, "salt"), "w") as defaults: # Test suites is quite slow - extend the timeout - defaults.write( - 'TIMEOUT=60\n' - 'TICK=1\n' - ) + defaults.write("TIMEOUT=60\n" "TICK=1\n") init_script = testprogram.TestProgram( - name='init:salt-minion', - program=os.path.join(RUNTIME_VARS.CODE_DIR, 'pkg', 'rpm', 'salt-minion'), + name="init:salt-minion", + program=os.path.join(RUNTIME_VARS.CODE_DIR, "pkg", "rpm", "salt-minion"), env=cmd_env, ) return _minions, salt_call, init_script - @skipIf(True, 'Disabled. Test suite hanging') + @skipIf(True, "Disabled. Test suite hanging") def test_linux_initscript(self): - ''' + """ Various tests of the init script to verify that it properly controls a salt minion. - ''' + """ pform = platform.uname()[0].lower() - if pform not in ('linux',): - self.skipTest('salt-minion init script is unavailable on {1}'.format(platform)) + if pform not in ("linux",): + self.skipTest( + "salt-minion init script is unavailable on {1}".format(platform) + ) minions, _, init_script = self._initscript_setup(self._test_minions) @@ -193,20 +185,36 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix # I take visual readability with aligned columns over strict PEP8 # (bad-whitespace) Exactly one space required after comma # pylint: disable=C0326 - ret = self._run_initscript(init_script, minions[:1], False, 'bogusaction', 2) - ret = self._run_initscript(init_script, minions[:1], False, 'reload', 3) # Not implemented - ret = self._run_initscript(init_script, minions[:1], False, 'stop', 0, 'when not running') - ret = self._run_initscript(init_script, minions[:1], False, 'status', 3, 'when not running') - ret = self._run_initscript(init_script, minions[:1], False, 'condrestart', 7, 'when not running') - ret = self._run_initscript(init_script, minions[:1], False, 'try-restart', 7, 'when not running') - ret = self._run_initscript(init_script, minions, True, 'start', 0, 'when not running') + ret = self._run_initscript( + init_script, minions[:1], False, "bogusaction", 2 + ) + ret = self._run_initscript( + init_script, minions[:1], False, "reload", 3 + ) # Not implemented + ret = self._run_initscript( + init_script, minions[:1], False, "stop", 0, "when not running" + ) + ret = self._run_initscript( + init_script, minions[:1], False, "status", 3, "when not running" + ) + ret = self._run_initscript( + init_script, minions[:1], False, "condrestart", 7, "when not running" + ) + ret = self._run_initscript( + init_script, minions[:1], False, "try-restart", 7, "when not running" + ) + ret = self._run_initscript( + init_script, minions, True, "start", 0, "when not running" + ) - ret = self._run_initscript(init_script, minions, True, 'status', 0, 'when running') + ret = self._run_initscript( + init_script, minions, True, "status", 0, "when running" + ) # Verify that PIDs match mpids = {} for line in ret[0]: segs = line.decode(__salt_system_encoding__).split() - minfo = segs[0].split(':') + minfo = segs[0].split(":") mpids[minfo[-1]] = int(segs[-1]) if segs[-1].isdigit() else None for minion in minions: self.assertEqual( @@ -216,45 +224,52 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix minion.abs_path(minion.pid_path), minion.daemon_pid, mpids[minion.name], - ) + ), ) - ret = self._run_initscript(init_script, minions, True, 'start', 0, 'when running') - ret = self._run_initscript(init_script, minions, True, 'condrestart', 0, 'when running') - ret = self._run_initscript(init_script, minions, True, 'try-restart', 0, 'when running') - ret = self._run_initscript(init_script, minions, False, 'stop', 0, 'when running') + ret = self._run_initscript( + init_script, minions, True, "start", 0, "when running" + ) + ret = self._run_initscript( + init_script, minions, True, "condrestart", 0, "when running" + ) + ret = self._run_initscript( + init_script, minions, True, "try-restart", 0, "when running" + ) + ret = self._run_initscript( + init_script, minions, False, "stop", 0, "when running" + ) finally: # Ensure that minions are shutdown for minion in minions: minion.shutdown() - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_exit_status_unknown_user(self): - ''' + """ Ensure correct exit status when the minion is configured to run as an unknown user. Skipped on windows because daemonization not supported - ''' + """ minion = testprogram.TestDaemonSaltMinion( - name='unknown_user', - configs={'minion': {'map': {'user': 'some_unknown_user_xyz'}}}, + name="unknown_user", + configs={"minion": {"map": {"user": "some_unknown_user_xyz"}}}, parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist minion.setup() stdout, stderr, status = minion.run( - args=['-d'], - catch_stderr=True, - with_retcode=True, + args=["-d"], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_NOUSER', - message='unknown user not on system', + status, + "EX_NOUSER", + message="unknown user not on system", stdout=stdout, - stderr=tests.integration.utils.decode_byte_list(stderr) + stderr=tests.integration.utils.decode_byte_list(stderr), ) finally: # Although the start-up should fail, call shutdown() to set the @@ -262,29 +277,27 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix # cause timeout exceptions and respective traceback minion.shutdown() -# @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + # @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') def test_exit_status_unknown_argument(self): - ''' + """ Ensure correct exit status when an unknown argument is passed to salt-minion. - ''' + """ minion = testprogram.TestDaemonSaltMinion( - name='unknown_argument', - parent_dir=self._test_dir, + name="unknown_argument", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist minion.setup() stdout, stderr, status = minion.run( - args=['-d', '--unknown-argument'], - catch_stderr=True, - with_retcode=True, + args=["-d", "--unknown-argument"], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_USAGE', - message='unknown argument', + status, + "EX_USAGE", + message="unknown argument", stdout=stdout, - stderr=tests.integration.utils.decode_byte_list(stderr) + stderr=tests.integration.utils.decode_byte_list(stderr), ) finally: # Although the start-up should fail, call shutdown() to set the @@ -292,28 +305,23 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix # cause timeout exceptions and respective traceback minion.shutdown() - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_exit_status_correct_usage(self): - ''' + """ Ensure correct exit status when salt-minion starts correctly. Skipped on windows because daemonization not supported - ''' + """ minion = testprogram.TestDaemonSaltMinion( - name='correct_usage', - parent_dir=self._test_dir, + name="correct_usage", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist minion.setup() stdout, stderr, status = minion.run( - args=['-d'], - catch_stderr=True, - with_retcode=True, + args=["-d"], catch_stderr=True, with_retcode=True, ) self.assert_exit_status( - status, 'EX_OK', - message='correct usage', - stdout=stdout, stderr=stderr + status, "EX_OK", message="correct usage", stdout=stdout, stderr=stderr ) minion.shutdown(wait_for_orphans=3) diff --git a/tests/integration/shell/test_proxy.py b/tests/integration/shell/test_proxy.py index b36d220ab50..3e43d973af2 100644 --- a/tests/integration/shell/test_proxy.py +++ b/tests/integration/shell/test_proxy.py @@ -1,46 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Thayne Harbaugh (tharbaug@adobe.com) tests.integration.shell.proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -from tests.support.unit import skipIf - -# Import salt tests libs +import pytest +import salt.utils.platform import tests.integration.utils from tests.integration.utils import testprogram - -import salt.utils.platform +from tests.support.unit import skipIf log = logging.getLogger(__name__) +@pytest.mark.windows_whitelisted class ProxyTest(testprogram.TestProgramCase): - ''' + """ Various integration tests for the salt-proxy executable. - ''' + """ def test_exit_status_no_proxyid(self): - ''' + """ Ensure correct exit status when --proxyid argument is missing. - ''' + """ proxy = testprogram.TestDaemonSaltProxy( - name='proxy-no_proxyid', - parent_dir=self._test_dir, + name="proxy-no_proxyid", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist proxy.setup() # Needed due to verbatim_args=True - args = ['--config-dir', proxy.abs_path(proxy.config_dir)] + args = ["--config-dir", proxy.abs_path(proxy.config_dir)] if not salt.utils.platform.is_windows(): - args.append('-d') + args.append("-d") stdout, stderr, status = proxy.run( args=args, # verbatim_args prevents --proxyid from being added automatically @@ -53,10 +51,11 @@ class ProxyTest(testprogram.TestProgramCase): ) try: self.assert_exit_status( - status, 'EX_USAGE', - message='no --proxyid specified', + status, + "EX_USAGE", + message="no --proxyid specified", stdout=stdout, - stderr=tests.integration.utils.decode_byte_list(stderr) + stderr=tests.integration.utils.decode_byte_list(stderr), ) finally: # Although the start-up should fail, call shutdown() to set the @@ -66,31 +65,32 @@ class ProxyTest(testprogram.TestProgramCase): # Hangs on Windows. You can add a timeout to the proxy.run command, but then # it just times out. - @skipIf(salt.utils.platform.is_windows(), 'Test hangs on Windows') + @skipIf(salt.utils.platform.is_windows(), "Test hangs on Windows") def test_exit_status_unknown_user(self): - ''' + """ Ensure correct exit status when the proxy is configured to run as an unknown user. - ''' + """ proxy = testprogram.TestDaemonSaltProxy( - name='proxy-unknown_user', - config_base={'user': 'some_unknown_user_xyz'}, + name="proxy-unknown_user", + config_base={"user": "some_unknown_user_xyz"}, parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist proxy.setup() stdout, stderr, status = proxy.run( - args=['-d'] if not salt.utils.platform.is_windows() else [], + args=["-d"] if not salt.utils.platform.is_windows() else [], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_NOUSER', - message='unknown user not on system', + status, + "EX_NOUSER", + message="unknown user not on system", stdout=stdout, - stderr=tests.integration.utils.decode_byte_list(stderr) + stderr=tests.integration.utils.decode_byte_list(stderr), ) finally: # Although the start-up should fail, call shutdown() to set the @@ -99,30 +99,29 @@ class ProxyTest(testprogram.TestProgramCase): proxy.shutdown() def test_exit_status_unknown_argument(self): - ''' + """ Ensure correct exit status when an unknown argument is passed to salt-proxy. - ''' + """ proxy = testprogram.TestDaemonSaltProxy( - name='proxy-unknown_argument', - parent_dir=self._test_dir, + name="proxy-unknown_argument", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist proxy.setup() - args = ['--unknown-argument'] + args = ["--unknown-argument"] if not salt.utils.platform.is_windows(): - args.append('-b') + args.append("-b") stdout, stderr, status = proxy.run( - args=args, - catch_stderr=True, - with_retcode=True, + args=args, catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_USAGE', - message='unknown argument', - stdout=stdout, stderr=stderr + status, + "EX_USAGE", + message="unknown argument", + stdout=stdout, + stderr=stderr, ) finally: # Although the start-up should fail, call shutdown() to set the @@ -132,30 +131,30 @@ class ProxyTest(testprogram.TestProgramCase): # Hangs on Windows. You can add a timeout to the proxy.run command, but then # it just times out. - @skipIf(salt.utils.platform.is_windows(), 'Test hangs on Windows') + @skipIf(salt.utils.platform.is_windows(), "Test hangs on Windows") def test_exit_status_correct_usage(self): - ''' + """ Ensure correct exit status when salt-proxy starts correctly. Skip on Windows because daemonization not supported - ''' + """ proxy = testprogram.TestDaemonSaltProxy( - name='proxy-correct_usage', - parent_dir=self._test_dir, + name="proxy-correct_usage", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist proxy.setup() stdout, stderr, status = proxy.run( - args=['-d'] if not salt.utils.platform.is_windows() else [], + args=["-d"] if not salt.utils.platform.is_windows() else [], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_OK', - message='correct usage', + status, + "EX_OK", + message="correct usage", stdout=stdout, - stderr=tests.integration.utils.decode_byte_list(stderr) + stderr=tests.integration.utils.decode_byte_list(stderr), ) finally: proxy.shutdown(wait_for_orphans=3) diff --git a/tests/integration/shell/test_runner.py b/tests/integration/shell/test_runner.py index a18500f4307..cb0b74df3e4 100644 --- a/tests/integration/shell/test_runner.py +++ b/tests/integration/shell/test_runner.py @@ -1,169 +1,178 @@ # -*- coding: utf-8 -*- -# pylint: disable=invalid-name -''' +""" Tests for the salt-run command -''' +""" -# Import Python libs from __future__ import absolute_import -# Import Salt Testing libs -from tests.integration.utils import testprogram -from tests.support.case import ShellCase -from tests.support.mixins import ShellCaseCommonTestsMixin -from tests.support.helpers import skip_if_not_root - -# Import Salt libs +import pytest import salt.utils.files import salt.utils.platform import salt.utils.yaml +from tests.integration.utils import testprogram +from tests.support.case import ShellCase +from tests.support.helpers import skip_if_not_root +from tests.support.mixins import ShellCaseCommonTestsMixin + +USERA = "saltdev" +USERA_PWD = "saltdev" +HASHED_USERA_PWD = "$6$SALTsalt$ZZFD90fKFWq8AGmmX0L3uBtS9fXL62SrTk5zcnQ6EkD6zoiM3kB88G1Zvs0xm/gZ7WXJRs5nsTBybUvGSqZkT." -USERA = 'saltdev' -USERA_PWD = 'saltdev' -HASHED_USERA_PWD = '$6$SALTsalt$ZZFD90fKFWq8AGmmX0L3uBtS9fXL62SrTk5zcnQ6EkD6zoiM3kB88G1Zvs0xm/gZ7WXJRs5nsTBybUvGSqZkT.' - - +@pytest.mark.windows_whitelisted class RunTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin): - ''' + """ Test the salt-run command - ''' + """ - _call_binary_ = 'salt-run' + _call_binary_ = "salt-run" def _add_user(self): - ''' + """ helper method to add user - ''' + """ try: - add_user = self.run_call('user.add {0} createhome=False'.format(USERA)) - add_pwd = self.run_call('shadow.set_password {0} \'{1}\''.format(USERA, - USERA_PWD if salt.utils.platform.is_darwin() else HASHED_USERA_PWD)) + add_user = self.run_call("user.add {0} createhome=False".format(USERA)) + add_pwd = self.run_call( + "shadow.set_password {0} '{1}'".format( + USERA, + USERA_PWD if salt.utils.platform.is_darwin() else HASHED_USERA_PWD, + ) + ) self.assertTrue(add_user) self.assertTrue(add_pwd) - user_list = self.run_call('user.list_users') + user_list = self.run_call("user.list_users") self.assertIn(USERA, str(user_list)) except AssertionError: - self.run_call('user.delete {0} remove=True'.format(USERA)) - self.skipTest( - 'Could not add user or password, skipping test' - ) + self.run_call("user.delete {0} remove=True".format(USERA)) + self.skipTest("Could not add user or password, skipping test") def _remove_user(self): - ''' + """ helper method to remove user - ''' - user_list = self.run_call('user.list_users') + """ + user_list = self.run_call("user.list_users") for user in user_list: if USERA in user: - self.run_call('user.delete {0} remove=True'.format(USERA)) + self.run_call("user.delete {0} remove=True".format(USERA)) def test_in_docs(self): - ''' + """ test the salt-run docs system - ''' - data = self.run_run('-d') - data = '\n'.join(data) - self.assertIn('jobs.active:', data) - self.assertIn('jobs.list_jobs:', data) - self.assertIn('jobs.lookup_jid:', data) - self.assertIn('manage.down:', data) - self.assertIn('manage.up:', data) - self.assertIn('network.wol:', data) - self.assertIn('network.wollist:', data) + """ + data = self.run_run("-d") + data = "\n".join(data) + self.assertIn("jobs.active:", data) + self.assertIn("jobs.list_jobs:", data) + self.assertIn("jobs.lookup_jid:", data) + self.assertIn("manage.down:", data) + self.assertIn("manage.up:", data) + self.assertIn("network.wol:", data) + self.assertIn("network.wollist:", data) def test_notin_docs(self): - ''' + """ Verify that hidden methods are not in run docs - ''' - data = self.run_run('-d') - data = '\n'.join(data) - self.assertNotIn('jobs.SaltException:', data) + """ + data = self.run_run("-d") + data = "\n".join(data) + self.assertNotIn("jobs.SaltException:", data) def test_salt_documentation_too_many_arguments(self): - ''' + """ Test to see if passing additional arguments shows an error - ''' - data = self.run_run('-d virt.list foo', catch_stderr=True) - self.assertIn('You can only get documentation for one method at one time', '\n'.join(data[1])) + """ + data = self.run_run("-d virt.list foo", catch_stderr=True) + self.assertIn( + "You can only get documentation for one method at one time", + "\n".join(data[1]), + ) def test_exit_status_unknown_argument(self): - ''' + """ Ensure correct exit status when an unknown argument is passed to salt-run. - ''' + """ runner = testprogram.TestProgramSaltRun( - name='run-unknown_argument', - parent_dir=self._test_dir, + name="run-unknown_argument", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist runner.setup() stdout, stderr, status = runner.run( - args=['--unknown-argument'], - catch_stderr=True, - with_retcode=True, + args=["--unknown-argument"], catch_stderr=True, with_retcode=True, ) self.assert_exit_status( - status, 'EX_USAGE', - message='unknown argument', - stdout=stdout, stderr=stderr + status, "EX_USAGE", message="unknown argument", stdout=stdout, stderr=stderr ) # runner.shutdown() should be unnecessary since the start-up should fail def test_exit_status_correct_usage(self): - ''' + """ Ensure correct exit status when salt-run starts correctly. - ''' + """ runner = testprogram.TestProgramSaltRun( - name='run-correct_usage', - parent_dir=self._test_dir, + name="run-correct_usage", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist runner.setup() - stdout, stderr, status = runner.run( - catch_stderr=True, - with_retcode=True, - ) + stdout, stderr, status = runner.run(catch_stderr=True, with_retcode=True,) self.assert_exit_status( - status, 'EX_OK', - message='correct usage', - stdout=stdout, stderr=stderr + status, "EX_OK", message="correct usage", stdout=stdout, stderr=stderr ) @skip_if_not_root def test_salt_run_with_eauth_all_args(self): - ''' + """ test salt-run with eauth tests all eauth args - ''' - args = ['--auth', '--eauth', '--external-auth', '-a'] + """ + args = ["--auth", "--eauth", "--external-auth", "-a"] self._add_user() for arg in args: - run_cmd = self.run_run('{0} pam --username {1} --password {2}\ - test.arg arg kwarg=kwarg1'.format(arg, USERA, USERA_PWD)) - expect = ['args:', ' - arg', 'kwargs:', ' ----------', ' kwarg:', ' kwarg1'] + run_cmd = self.run_run( + "{0} pam --username {1} --password {2}\ + test.arg arg kwarg=kwarg1".format( + arg, USERA, USERA_PWD + ) + ) + expect = [ + "args:", + " - arg", + "kwargs:", + " ----------", + " kwarg:", + " kwarg1", + ] self.assertEqual(expect, run_cmd) self._remove_user() @skip_if_not_root def test_salt_run_with_eauth_bad_passwd(self): - ''' + """ test salt-run with eauth and bad password - ''' + """ self._add_user() - run_cmd = self.run_run('-a pam --username {0} --password wrongpassword\ - test.arg arg kwarg=kwarg1'.format(USERA)) + run_cmd = self.run_run( + "-a pam --username {0} --password wrongpassword\ + test.arg arg kwarg=kwarg1".format( + USERA + ) + ) expect = ['Authentication failure of type "eauth" occurred for user saltdev.'] self.assertEqual(expect, run_cmd) self._remove_user() def test_salt_run_with_wrong_eauth(self): - ''' + """ test salt-run with wrong eauth parameter - ''' - run_cmd = self.run_run('-a wrongeauth --username {0} --password {1}\ - test.arg arg kwarg=kwarg1'.format(USERA, USERA_PWD)) + """ + run_cmd = self.run_run( + "-a wrongeauth --username {0} --password {1}\ + test.arg arg kwarg=kwarg1".format( + USERA, USERA_PWD + ) + ) expect = r"^The specified external authentication system \"wrongeauth\" is not available\tAvailable eauth types: auto, .*" self.assertRegex("\t".join(run_cmd), expect) diff --git a/tests/integration/shell/test_saltcli.py b/tests/integration/shell/test_saltcli.py index 46144e4e8ce..f40e57413cc 100644 --- a/tests/integration/shell/test_saltcli.py +++ b/tests/integration/shell/test_saltcli.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Thayne Harbaugh (tharbaug@adobe.com) tests.integration.shell.saltcli @@ -7,81 +7,72 @@ :NOTE: this was named ``saltcli`` rather than ``salt`` because ``salt`` conflates in the python importer with the expected ``salt`` namespace and breaks imports. -''' +""" -# Import python libs from __future__ import absolute_import + import logging import os -# Import Salt libs +import pytest import salt.defaults.exitcodes import salt.utils.files import salt.utils.path - -# Import Salt Testing libs -from tests.support.case import ShellCase from tests.integration.utils import testprogram +from tests.support.case import ShellCase log = logging.getLogger(__name__) +@pytest.mark.windows_whitelisted class SaltTest(testprogram.TestProgramCase): - ''' + """ Various integration tests for the salt executable. - ''' + """ # pylint: disable=invalid-name def test_exit_status_unknown_argument(self): - ''' + """ Ensure correct exit status when an unknown argument is passed to salt-run. - ''' + """ runner = testprogram.TestProgramSalt( - name='run-unknown_argument', - parent_dir=self._test_dir, + name="run-unknown_argument", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist runner.setup() stdout, stderr, status = runner.run( - args=['--unknown-argument'], - catch_stderr=True, - with_retcode=True, + args=["--unknown-argument"], catch_stderr=True, with_retcode=True, ) self.assert_exit_status( - status, 'EX_USAGE', - message='unknown argument', - stdout=stdout, stderr=stderr + status, "EX_USAGE", message="unknown argument", stdout=stdout, stderr=stderr ) # runner.shutdown() should be unnecessary since the start-up should fail def test_exit_status_correct_usage(self): - ''' + """ Ensure correct exit status when salt-run starts correctly. - ''' + """ runner = testprogram.TestProgramSalt( - name='run-correct_usage', - parent_dir=self._test_dir, + name="run-correct_usage", parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist runner.setup() stdout, stderr, status = runner.run( - args=['*', '-h'], - catch_stderr=True, - with_retcode=True, + args=["*", "-h"], catch_stderr=True, with_retcode=True, ) self.assert_exit_status( - status, 'EX_OK', - message='correct usage', - stdout=stdout, stderr=stderr + status, "EX_OK", message="correct usage", stdout=stdout, stderr=stderr ) +@pytest.mark.windows_whitelisted class RetcodeTestCase(ShellCase): - ''' + """ Tests to ensure that we set non-zero retcodes when execution fails - ''' + """ + # Hard-coding these instead of substituting values from # salt.defaults.exitcodes will give us a heads-up in the event that someone # tries to do something daft like change these values. @@ -90,35 +81,35 @@ class RetcodeTestCase(ShellCase): state_failure = salt.defaults.exitcodes.EX_STATE_FAILURE def _salt(self, command): - cmdline = 'minion ' + command + cmdline = "minion " + command return self.run_salt(cmdline, with_retcode=True)[1] def _salt_call(self, command, retcode_passthrough=False): - cmdline = '--retcode-passthrough ' if retcode_passthrough else '' + cmdline = "--retcode-passthrough " if retcode_passthrough else "" cmdline += command return self.run_call(cmdline, with_retcode=True)[1] def _test_error(self, salt_call=False): - ''' + """ Tests retcode when various error conditions are triggered - ''' + """ _run = self._salt_call if salt_call else self._salt - retcode = _run('test.raise_exception TypeError') + retcode = _run("test.raise_exception TypeError") assert retcode == self.error_status, retcode - retcode = _run('test.raise_exception salt.exceptions.CommandNotFoundError') + retcode = _run("test.raise_exception salt.exceptions.CommandNotFoundError") assert retcode == self.error_status, retcode - retcode = _run('test.raise_exception salt.exceptions.CommandExecutionError') + retcode = _run("test.raise_exception salt.exceptions.CommandExecutionError") assert retcode == self.error_status, retcode - retcode = _run('test.raise_exception salt.exceptions.SaltInvocationError') + retcode = _run("test.raise_exception salt.exceptions.SaltInvocationError") assert retcode == self.error_status, retcode retcode = _run( - 'test.raise_exception ' - 'OSError 2 "No such file or directory" /tmp/foo.txt') + "test.raise_exception " 'OSError 2 "No such file or directory" /tmp/foo.txt' + ) assert retcode == self.error_status, retcode retcode = _run('test.echo "{foo: bar, result: False}"') @@ -128,76 +119,78 @@ class RetcodeTestCase(ShellCase): assert retcode == self.error_status, retcode def test_zero_exit_code(self): - ''' + """ Test that a zero exit code is set when there are no errors and there is no explicit False result set in the return data. - ''' - retcode = self._salt('test.ping') + """ + retcode = self._salt("test.ping") assert retcode == 0, retcode - retcode = self._salt_call('test.ping') + retcode = self._salt_call("test.ping") assert retcode == 0, retcode def test_context_retcode(self): - ''' + """ Test that a nonzero retcode set in the context dunder will cause the salt CLI to set a nonzero retcode. - ''' + """ # test.retcode will set the retcode in the context dunder - retcode = self._salt('test.retcode 0') + retcode = self._salt("test.retcode 0") assert retcode == 0, retcode - retcode = self._salt('test.retcode 42') + retcode = self._salt("test.retcode 42") assert retcode == self.error_status, retcode # Test salt-call, making sure to also confirm the behavior of # retcode_passthrough. - retcode = self._salt_call('test.retcode 0') + retcode = self._salt_call("test.retcode 0") assert retcode == 0, retcode - retcode = self._salt_call('test.retcode 42') + retcode = self._salt_call("test.retcode 42") assert retcode == self.error_status, retcode - retcode = self._salt_call('test.retcode 42', retcode_passthrough=True) + retcode = self._salt_call("test.retcode 42", retcode_passthrough=True) assert retcode == 42, retcode # Test a state run that exits with one or more failures - retcode = self._salt_call('state.single test.fail_without_changes foo') + retcode = self._salt_call("state.single test.fail_without_changes foo") assert retcode == self.error_status, retcode retcode = self._salt_call( - 'state.single test.fail_without_changes foo', - retcode_passthrough=True) + "state.single test.fail_without_changes foo", retcode_passthrough=True + ) assert retcode == self.state_failure, retcode # Test a state compiler error - retcode = self._salt_call('state.apply thisslsfiledoesnotexist') + retcode = self._salt_call("state.apply thisslsfiledoesnotexist") assert retcode == self.error_status, retcode retcode = self._salt_call( - 'state.apply thisslsfiledoesnotexist', - retcode_passthrough=True) + "state.apply thisslsfiledoesnotexist", retcode_passthrough=True + ) assert retcode == self.state_compiler_error, retcode def test_salt_error(self): - ''' + """ Test that we return the expected retcode when a minion function raises an exception. - ''' + """ self._test_error() self._test_error(salt_call=True) def test_missing_minion(self): - ''' + """ Test that a minion which doesn't respond results in a nonzeo exit code - ''' - good = salt.utils.path.join(self.master_opts['pki_dir'], 'minions', 'minion') - bad = salt.utils.path.join(self.master_opts['pki_dir'], 'minions', 'minion2') + """ + good = salt.utils.path.join(self.master_opts["pki_dir"], "minions", "minion") + bad = salt.utils.path.join(self.master_opts["pki_dir"], "minions", "minion2") try: # Copy the key - with salt.utils.files.fopen(good, 'rb') as fhr, \ - salt.utils.files.fopen(bad, 'wb') as fhw: + with salt.utils.files.fopen(good, "rb") as fhr, salt.utils.files.fopen( + bad, "wb" + ) as fhw: fhw.write(fhr.read()) retcode = self.run_script( - 'salt', - '-c {0} -t 5 minion2 test.ping'.format(self.config_dir), + "salt", + "-c {0} -t 5 minion2 test.ping".format(self.config_dir), with_retcode=True, - timeout=60)[1] + timeout=60, + )[1] assert retcode == salt.defaults.exitcodes.EX_GENERIC, retcode finally: # Now get rid of it @@ -206,6 +199,5 @@ class RetcodeTestCase(ShellCase): except OSError as exc: if exc.errno != os.errno.ENOENT: log.error( - 'Failed to remove %s, this may affect other tests: %s', - bad, exc + "Failed to remove %s, this may affect other tests: %s", bad, exc ) diff --git a/tests/integration/shell/test_spm.py b/tests/integration/shell/test_spm.py index 0cd8a4275f5..e4e2887cb27 100644 --- a/tests/integration/shell/test_spm.py +++ b/tests/integration/shell/test_spm.py @@ -1,76 +1,74 @@ # -*- coding: utf-8 -*- -# Import python libs from __future__ import absolute_import + import os -# Import Salt Testing libs +import pytest from tests.support.case import ShellCase, SPMCase +@pytest.mark.windows_whitelisted class SPMTest(ShellCase, SPMCase): - ''' + """ Test spm script - ''' + """ def test_spm_help(self): - ''' + """ test --help argument for spm - ''' - expected_args = ['--version', '--assume-yes', '--help'] - output = self.run_spm('--help') + """ + expected_args = ["--version", "--assume-yes", "--help"] + output = self.run_spm("--help") for arg in expected_args: - self.assertIn(arg, ''.join(output)) + self.assertIn(arg, "".join(output)) def test_spm_bad_arg(self): - ''' + """ test correct output when bad argument passed - ''' - expected_args = ['--version', '--assume-yes', '--help'] - output = self.run_spm('doesnotexist') + """ + expected_args = ["--version", "--assume-yes", "--help"] + output = self.run_spm("doesnotexist") for arg in expected_args: - self.assertIn(arg, ''.join(output)) + self.assertIn(arg, "".join(output)) def test_spm_assume_yes(self): - ''' + """ test spm install with -y arg - ''' + """ config = self._spm_config(assume_yes=False) self._spm_build_files(config) - spm_file = os.path.join(config['spm_build_dir'], - 'apache-201506-2.spm') + spm_file = os.path.join(config["spm_build_dir"], "apache-201506-2.spm") - build = self.run_spm('build {0} -c {1}'.format(self.formula_dir, - self._tmp_spm)) + build = self.run_spm("build {0} -c {1}".format(self.formula_dir, self._tmp_spm)) - install = self.run_spm('install {0} -c {1} -y'.format(spm_file, - self._tmp_spm)) + install = self.run_spm("install {0} -c {1} -y".format(spm_file, self._tmp_spm)) - self.assertTrue(os.path.exists(os.path.join(config['formula_path'], - 'apache', 'apache.sls'))) + self.assertTrue( + os.path.exists(os.path.join(config["formula_path"], "apache", "apache.sls")) + ) def test_spm_force(self): - ''' + """ test spm install with -f arg - ''' + """ config = self._spm_config(assume_yes=False) self._spm_build_files(config) - spm_file = os.path.join(config['spm_build_dir'], - 'apache-201506-2.spm') + spm_file = os.path.join(config["spm_build_dir"], "apache-201506-2.spm") - build = self.run_spm('build {0} -c {1}'.format(self.formula_dir, - self._tmp_spm)) + build = self.run_spm("build {0} -c {1}".format(self.formula_dir, self._tmp_spm)) - install = self.run_spm('install {0} -c {1} -y'.format(spm_file, - self._tmp_spm)) + install = self.run_spm("install {0} -c {1} -y".format(spm_file, self._tmp_spm)) - self.assertTrue(os.path.exists(os.path.join(config['formula_path'], - 'apache', 'apache.sls'))) + self.assertTrue( + os.path.exists(os.path.join(config["formula_path"], "apache", "apache.sls")) + ) # check if it forces the install after its already been installed it - install = self.run_spm('install {0} -c {1} -y -f'.format(spm_file, - self._tmp_spm)) + install = self.run_spm( + "install {0} -c {1} -y -f".format(spm_file, self._tmp_spm) + ) - self.assertEqual(['... installing apache'], install) + self.assertEqual(["... installing apache"], install) diff --git a/tests/integration/shell/test_syndic.py b/tests/integration/shell/test_syndic.py index c695ca3226b..73bf1c4faac 100644 --- a/tests/integration/shell/test_syndic.py +++ b/tests/integration/shell/test_syndic.py @@ -1,85 +1,88 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.shell.syndic ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import python libs from __future__ import absolute_import + import logging from collections import OrderedDict -# Import Salt Testing libs +import psutil +import pytest +import salt.utils.files +import salt.utils.platform +import salt.utils.yaml +from tests.integration.utils import testprogram from tests.support.case import ShellCase from tests.support.mixins import ShellCaseCommonTestsMixin from tests.support.unit import skipIf -from tests.integration.utils import testprogram - -# Import salt libs -import salt.utils.files -import salt.utils.yaml -import salt.utils.platform - -# Import 3rd-party libs -import psutil -#import pytest log = logging.getLogger(__name__) -#@pytest.fixture(scope='module', autouse=True) +# @pytest.fixture(scope='module', autouse=True) def session_salt_syndic(request, session_salt_master_of_masters, session_salt_syndic): - request.session.stats_processes.update(OrderedDict(( - ('Salt Syndic Master', psutil.Process(session_salt_master_of_masters.pid)), - ('Salt Syndic', psutil.Process(session_salt_syndic.pid)), - )).items()) + request.session.stats_processes.update( + OrderedDict( + ( + ( + "Salt Syndic Master", + psutil.Process(session_salt_master_of_masters.pid), + ), + ("Salt Syndic", psutil.Process(session_salt_syndic.pid)), + ) + ).items() + ) yield session_salt_syndic - request.session.stats_processes.pop('Salt Syndic Master') - request.session.stats_processes.pop('Salt Syndic') + request.session.stats_processes.pop("Salt Syndic Master") + request.session.stats_processes.pop("Salt Syndic") # Stop daemons now(they would be stopped at the end of the test run session for daemon in (session_salt_syndic, session_salt_master_of_masters): try: daemon.terminate() except Exception as exc: # pylint: disable=broad-except - log.warning('Failed to terminate daemon: %s', daemon.__class__.__name__) + log.warning("Failed to terminate daemon: %s", daemon.__class__.__name__) +@pytest.mark.windows_whitelisted class SyndicTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin): - ''' + """ Test the salt-syndic command - ''' + """ - _call_binary_ = 'salt-syndic' + _call_binary_ = "salt-syndic" - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_exit_status_unknown_user(self): - ''' + """ Ensure correct exit status when the syndic is configured to run as an unknown user. Skipped on windows because daemonization not supported - ''' + """ syndic = testprogram.TestDaemonSaltSyndic( - name='unknown_user', - config_base={'user': 'some_unknown_user_xyz'}, + name="unknown_user", + config_base={"user": "some_unknown_user_xyz"}, parent_dir=self._test_dir, ) # Call setup here to ensure config and script exist syndic.setup() stdout, stderr, status = syndic.run( - args=['-d'], - catch_stderr=True, - with_retcode=True, + args=["-d"], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_NOUSER', - message='unknown user not on system', - stdout=stdout, stderr=stderr + status, + "EX_NOUSER", + message="unknown user not on system", + stdout=stdout, + stderr=stderr, ) finally: # Although the start-up should fail, call shutdown() to set the @@ -88,30 +91,29 @@ class SyndicTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix syndic.shutdown() # pylint: disable=invalid-name - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_exit_status_unknown_argument(self): - ''' + """ Ensure correct exit status when an unknown argument is passed to salt-syndic. Skipped on windows because daemonization not supported - ''' + """ syndic = testprogram.TestDaemonSaltSyndic( - name='unknown_argument', - parent_dir=self._test_dir, + name="unknown_argument", parent_dir=self._test_dir, ) # Syndic setup here to ensure config and script exist syndic.setup() stdout, stderr, status = syndic.run( - args=['-d', '--unknown-argument'], - catch_stderr=True, - with_retcode=True, + args=["-d", "--unknown-argument"], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_USAGE', - message='unknown argument', - stdout=stdout, stderr=stderr + status, + "EX_USAGE", + message="unknown argument", + stdout=stdout, + stderr=stderr, ) finally: # Although the start-up should fail, call shutdown() to set the @@ -119,30 +121,25 @@ class SyndicTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix # cause timeout exceptions and respective traceback syndic.shutdown() - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_exit_status_correct_usage(self): - ''' + """ Ensure correct exit status when salt-syndic starts correctly. Skipped on windows because daemonization not supported - ''' + """ syndic = testprogram.TestDaemonSaltSyndic( - name='correct_usage', - parent_dir=self._test_dir, + name="correct_usage", parent_dir=self._test_dir, ) # Syndic setup here to ensure config and script exist syndic.setup() stdout, stderr, status = syndic.run( - args=['-d', '-l', 'debug'], - catch_stderr=True, - with_retcode=True, + args=["-d", "-l", "debug"], catch_stderr=True, with_retcode=True, ) try: self.assert_exit_status( - status, 'EX_OK', - message='correct usage', - stdout=stdout, stderr=stderr + status, "EX_OK", message="correct usage", stdout=stdout, stderr=stderr ) finally: syndic.shutdown(wait_for_orphans=3) diff --git a/tests/integration/spm/test_build.py b/tests/integration/spm/test_build.py index 90598aafb69..e20e8c1259b 100644 --- a/tests/integration/spm/test_build.py +++ b/tests/integration/spm/test_build.py @@ -1,86 +1,91 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the spm build utility -''' -# Import python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import os import shutil -# Import Salt libs +import pytest import salt.utils.files import salt.utils.path - -# Import Salt Testing libs -from tests.support.case import SPMCase, ModuleCase +from tests.support.case import ModuleCase, SPMCase from tests.support.helpers import destructiveTest from tests.support.unit import skipIf @destructiveTest +@pytest.mark.windows_whitelisted class SPMBuildTest(SPMCase, ModuleCase): - ''' + """ Validate the spm build command - ''' + """ + def setUp(self): self.config = self._spm_config() self._spm_build_files(self.config) def test_spm_build(self): - ''' + """ test spm build - ''' - self.run_spm('build', self.config, self.formula_dir) - spm_file = os.path.join(self.config['spm_build_dir'], 'apache-201506-2.spm') + """ + self.run_spm("build", self.config, self.formula_dir) + spm_file = os.path.join(self.config["spm_build_dir"], "apache-201506-2.spm") # Make sure .spm file gets created self.assertTrue(os.path.exists(spm_file)) # Make sure formula path dir is created - self.assertTrue(os.path.isdir(self.config['formula_path'])) + self.assertTrue(os.path.isdir(self.config["formula_path"])) - @skipIf(salt.utils.path.which('fallocate') is None, 'fallocate not installed') + @skipIf(salt.utils.path.which("fallocate") is None, "fallocate not installed") def test_spm_build_big_file(self): - ''' + """ test spm build with a big file - ''' + """ # check to make sure there is enough space to run this test - check_space = self.run_function('status.diskusage', ['/tmp']) - space = check_space['/tmp']['available'] + check_space = self.run_function("status.diskusage", ["/tmp"]) + space = check_space["/tmp"]["available"] if space < 3000000000: - self.skipTest('Not enough space on host to run this test') + self.skipTest("Not enough space on host to run this test") - self.run_function('cmd.run', - ['fallocate -l 1G {0}'.format(os.path.join(self.formula_sls_dir, - 'bigfile.txt'))]) - self.run_spm('build', self.config, self.formula_dir) - spm_file = os.path.join(self.config['spm_build_dir'], 'apache-201506-2.spm') - self.run_spm('install', self.config, spm_file) + self.run_function( + "cmd.run", + [ + "fallocate -l 1G {0}".format( + os.path.join(self.formula_sls_dir, "bigfile.txt") + ) + ], + ) + self.run_spm("build", self.config, self.formula_dir) + spm_file = os.path.join(self.config["spm_build_dir"], "apache-201506-2.spm") + self.run_spm("install", self.config, spm_file) - get_files = self.run_spm('files', self.config, 'apache') + get_files = self.run_spm("files", self.config, "apache") - files = ['apache.sls', 'bigfile.txt'] + files = ["apache.sls", "bigfile.txt"] for sls in files: - self.assertIn(sls, ' '.join(get_files)) + self.assertIn(sls, " ".join(get_files)) def test_spm_build_exclude(self): - ''' + """ test spm build while excluding directory - ''' - git_dir = os.path.join(self.formula_sls_dir, '.git') + """ + git_dir = os.path.join(self.formula_sls_dir, ".git") os.makedirs(git_dir) - files = ['donotbuild1', 'donotbuild2', 'donotbuild3'] + files = ["donotbuild1", "donotbuild2", "donotbuild3"] for git_file in files: - with salt.utils.files.fopen(os.path.join(git_dir, git_file), 'w') as fp: - fp.write('Please do not include me in build') + with salt.utils.files.fopen(os.path.join(git_dir, git_file), "w") as fp: + fp.write("Please do not include me in build") - self.run_spm('build', self.config, self.formula_dir) - spm_file = os.path.join(self.config['spm_build_dir'], 'apache-201506-2.spm') - self.run_spm('install', self.config, spm_file) + self.run_spm("build", self.config, self.formula_dir) + spm_file = os.path.join(self.config["spm_build_dir"], "apache-201506-2.spm") + self.run_spm("install", self.config, spm_file) - get_files = self.run_spm('files', self.config, 'apache') + get_files = self.run_spm("files", self.config, "apache") for git_file in files: - self.assertNotIn(git_file, ' '.join(get_files)) + self.assertNotIn(git_file, " ".join(get_files)) def tearDown(self): shutil.rmtree(self._tmp_spm) diff --git a/tests/integration/spm/test_files.py b/tests/integration/spm/test_files.py index 66b774db523..89bc19a6fad 100644 --- a/tests/integration/spm/test_files.py +++ b/tests/integration/spm/test_files.py @@ -1,36 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the spm files utility -''' -# Import python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import os import shutil -# Import Salt Testing libs +import pytest from tests.support.case import SPMCase from tests.support.helpers import destructiveTest @destructiveTest +@pytest.mark.windows_whitelisted class SPMFilesTest(SPMCase): - ''' + """ Validate the spm files command - ''' + """ + def setUp(self): self.config = self._spm_config() self._spm_build_files(self.config) def test_spm_files(self): - ''' + """ test spm files - ''' + """ self._spm_create_update_repo(self.config) - install = self.run_spm('install', self.config, 'apache') - get_files = self.run_spm('files', self.config, 'apache') + install = self.run_spm("install", self.config, "apache") + get_files = self.run_spm("files", self.config, "apache") - os.path.exists(os.path.join(self.config['formula_path'], 'apache', - 'apache.sls')) + os.path.exists( + os.path.join(self.config["formula_path"], "apache", "apache.sls") + ) def tearDown(self): shutil.rmtree(self._tmp_spm) diff --git a/tests/integration/spm/test_info.py b/tests/integration/spm/test_info.py index fb37b937124..28f8f34b096 100644 --- a/tests/integration/spm/test_info.py +++ b/tests/integration/spm/test_info.py @@ -1,36 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the spm info utility -''' -# Import python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import shutil -# Import Salt Testing libs +import pytest from tests.support.case import SPMCase from tests.support.helpers import destructiveTest @destructiveTest +@pytest.mark.windows_whitelisted class SPMInfoTest(SPMCase): - ''' + """ Validate the spm info command - ''' + """ + def setUp(self): self.config = self._spm_config() self._spm_build_files(self.config) def test_spm_info(self): - ''' + """ test spm build - ''' + """ self._spm_create_update_repo(self.config) - install = self.run_spm('install', self.config, 'apache') - get_info = self.run_spm('info', self.config, 'apache') + install = self.run_spm("install", self.config, "apache") + get_info = self.run_spm("info", self.config, "apache") - check_info = ['Supported OSes', 'Supported OS', 'installing Apache'] + check_info = ["Supported OSes", "Supported OS", "installing Apache"] for info in check_info: - self.assertIn(info, ''.join(get_info)) + self.assertIn(info, "".join(get_info)) def tearDown(self): shutil.rmtree(self._tmp_spm) diff --git a/tests/integration/spm/test_install.py b/tests/integration/spm/test_install.py index c4ffaed7962..012d45373c0 100644 --- a/tests/integration/spm/test_install.py +++ b/tests/integration/spm/test_install.py @@ -1,48 +1,49 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the spm install utility -''' -# Import python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import os import shutil -# Import Salt Testing libs +import pytest from tests.support.case import SPMCase from tests.support.helpers import destructiveTest @destructiveTest +@pytest.mark.windows_whitelisted class SPMInstallTest(SPMCase): - ''' + """ Validate the spm install command - ''' + """ + def setUp(self): self.config = self._spm_config() self._spm_build_files(self.config) def test_spm_install_local_dir(self): - ''' + """ test spm install from local directory - ''' - build_spm = self.run_spm('build', self.config, self.formula_dir) - spm_file = os.path.join(self.config['spm_build_dir'], - 'apache-201506-2.spm') + """ + build_spm = self.run_spm("build", self.config, self.formula_dir) + spm_file = os.path.join(self.config["spm_build_dir"], "apache-201506-2.spm") - install = self.run_spm('install', self.config, spm_file) + install = self.run_spm("install", self.config, spm_file) - sls = os.path.join(self.config['formula_path'], 'apache', 'apache.sls') + sls = os.path.join(self.config["formula_path"], "apache", "apache.sls") self.assertTrue(os.path.exists(sls)) def test_spm_install_from_repo(self): - ''' + """ test spm install from repo - ''' + """ self._spm_create_update_repo(self.config) - install = self.run_spm('install', self.config, 'apache') + install = self.run_spm("install", self.config, "apache") - sls = os.path.join(self.config['formula_path'], 'apache', 'apache.sls') + sls = os.path.join(self.config["formula_path"], "apache", "apache.sls") self.assertTrue(os.path.exists(sls)) diff --git a/tests/integration/spm/test_remove.py b/tests/integration/spm/test_remove.py index bf0460062c8..b9d99f3c4fb 100644 --- a/tests/integration/spm/test_remove.py +++ b/tests/integration/spm/test_remove.py @@ -1,45 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the spm remove utility -''' -# Import python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import os import shutil -# Import Salt Testing libs +import pytest from tests.support.case import SPMCase from tests.support.helpers import destructiveTest @destructiveTest +@pytest.mark.windows_whitelisted class SPMRemoveTest(SPMCase): - ''' + """ Validate the spm remove command - ''' + """ + def setUp(self): self.config = self._spm_config() self._spm_build_files(self.config) def test_spm_remove(self): - ''' + """ test spm remove from an inital repo install - ''' + """ # first install apache package self._spm_create_update_repo(self.config) - install = self.run_spm('install', self.config, 'apache') + install = self.run_spm("install", self.config, "apache") - sls = os.path.join(self.config['formula_path'], 'apache', 'apache.sls') + sls = os.path.join(self.config["formula_path"], "apache", "apache.sls") self.assertTrue(os.path.exists(sls)) - #now remove an make sure file is removed - remove = self.run_spm('remove', self.config, 'apache') - sls = os.path.join(self.config['formula_path'], 'apache', 'apache.sls') + # now remove an make sure file is removed + remove = self.run_spm("remove", self.config, "apache") + sls = os.path.join(self.config["formula_path"], "apache", "apache.sls") self.assertFalse(os.path.exists(sls)) - self.assertIn('... removing apache', remove) + self.assertIn("... removing apache", remove) def tearDown(self): shutil.rmtree(self._tmp_spm) diff --git a/tests/integration/spm/test_repo.py b/tests/integration/spm/test_repo.py index 1b81469dae7..009ffa48953 100644 --- a/tests/integration/spm/test_repo.py +++ b/tests/integration/spm/test_repo.py @@ -1,35 +1,37 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the spm repo -''' -# Import python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import os import shutil -# Import Salt Testing libs +import pytest from tests.support.case import SPMCase from tests.support.helpers import destructiveTest @destructiveTest +@pytest.mark.windows_whitelisted class SPMRepoTest(SPMCase): - ''' + """ Validate commands related to spm repo - ''' + """ + def setUp(self): self.config = self._spm_config() self._spm_build_files(self.config) def test_spm_create_update_repo(self): - ''' + """ test spm create_repo - ''' + """ self._spm_create_update_repo(self.config) - self.assertTrue(os.path.exists(self.config['spm_db'])) + self.assertTrue(os.path.exists(self.config["spm_db"])) - l_repo_file = os.path.join(self.config['spm_cache_dir'], 'local_repo.p') + l_repo_file = os.path.join(self.config["spm_cache_dir"], "local_repo.p") self.assertTrue(os.path.exists(l_repo_file)) def tearDown(self): diff --git a/tests/integration/ssh/test_deploy.py b/tests/integration/ssh/test_deploy.py index 60c46492061..518fefeae9b 100644 --- a/tests/integration/ssh/test_deploy.py +++ b/tests/integration/ssh/test_deploy.py @@ -1,41 +1,56 @@ # -*- coding: utf-8 -*- -''' +""" salt-ssh testing -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil # Import salt testing libs from tests.support.case import SSHCase +from tests.support.runtests import RUNTIME_VARS class SSHTest(SSHCase): - ''' + """ Test general salt-ssh functionality - ''' + """ + def test_ping(self): - ''' + """ Test a simple ping - ''' - ret = self.run_function('test.ping') - self.assertTrue(ret, 'Ping did not return true') + """ + ret = self.run_function("test.ping") + self.assertTrue(ret, "Ping did not return true") def test_thin_dir(self): - ''' + """ test to make sure thin_dir is created and salt-call file is included - ''' - thin_dir = self.run_function('config.get', ['thin_dir'], wipe=False) + """ + thin_dir = self.run_function("config.get", ["thin_dir"], wipe=False) os.path.isdir(thin_dir) - os.path.exists(os.path.join(thin_dir, 'salt-call')) - os.path.exists(os.path.join(thin_dir, 'running_data')) + os.path.exists(os.path.join(thin_dir, "salt-call")) + os.path.exists(os.path.join(thin_dir, "running_data")) + + def test_set_path(self): + """ + test setting the path env variable + """ + path = "/pathdoesnotexist/" + roster = os.path.join(RUNTIME_VARS.TMP, "roster-set-path") + self.custom_roster( + roster, data={"set_path": "$PATH:/usr/local/bin/:{0}".format(path)} + ) + ret = self.run_function("environ.get", ["PATH"], roster_file=roster) + assert path in ret def tearDown(self): - ''' + """ make sure to clean up any old ssh directories - ''' - salt_dir = self.run_function('config.get', ['thin_dir'], wipe=False) + """ + salt_dir = self.run_function("config.get", ["thin_dir"], wipe=False) if os.path.exists(salt_dir): shutil.rmtree(salt_dir) diff --git a/tests/integration/ssh/test_grains.py b/tests/integration/ssh/test_grains.py index ddfb1a2196b..92c5eb4a07c 100644 --- a/tests/integration/ssh/test_grains.py +++ b/tests/integration/ssh/test_grains.py @@ -3,28 +3,29 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +# Import Salt Libs +import salt.utils.platform + # Import Salt Testing Libs from tests.support.case import SSHCase from tests.support.unit import skipIf -# Import Salt Libs -import salt.utils.platform - -@skipIf(salt.utils.platform.is_windows(), 'salt-ssh not available on Windows') +@skipIf(salt.utils.platform.is_windows(), "salt-ssh not available on Windows") class SSHGrainsTest(SSHCase): - ''' + """ testing grains with salt-ssh - ''' + """ + def test_grains_items(self): - ''' + """ test grains.items with salt-ssh - ''' - ret = self.run_function('grains.items') - grain = 'Linux' + """ + ret = self.run_function("grains.items") + grain = "Linux" if salt.utils.platform.is_darwin(): - grain = 'Darwin' + grain = "Darwin" if salt.utils.platform.is_aix(): - grain = 'AIX' - self.assertEqual(ret['kernel'], grain) + grain = "AIX" + self.assertEqual(ret["kernel"], grain) self.assertTrue(isinstance(ret, dict)) diff --git a/tests/integration/ssh/test_jinja_filters.py b/tests/integration/ssh/test_jinja_filters.py index a431ebb0545..a7ff5ea1264 100644 --- a/tests/integration/ssh/test_jinja_filters.py +++ b/tests/integration/ssh/test_jinja_filters.py @@ -8,17 +8,18 @@ from tests.support.case import SSHCase class SSHJinjaFiltersTest(SSHCase): - ''' + """ testing Jinja filters are available via state system & salt-ssh - ''' + """ def test_dateutils_strftime(self): - ''' + """ test jinja filter datautils.strftime - ''' - arg = self._arg_str('state.sls', ['jinja_filters.dateutils_strftime']) + """ + arg = self._arg_str("state.sls", ["jinja_filters.dateutils_strftime"]) ret = self.run_ssh(arg) import salt.utils.json - ret = salt.utils.json.loads(ret)['localhost'] - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + + ret = salt.utils.json.loads(ret)["localhost"] + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) diff --git a/tests/integration/ssh/test_master.py b/tests/integration/ssh/test_master.py index 37f9444291a..2204d0e98d0 100644 --- a/tests/integration/ssh/test_master.py +++ b/tests/integration/ssh/test_master.py @@ -1,65 +1,67 @@ # -*- coding: utf-8 -*- -''' +""" Simple Smoke Tests for Connected SSH minions -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs from tests.support.case import SSHCase -from tests.support.helpers import skip_if_not_root, requires_system_grains +from tests.support.helpers import requires_system_grains, skip_if_not_root class SSHMasterTestCase(SSHCase): - ''' + """ Test ssh master functionality - ''' + """ + def test_can_it_ping(self): - ''' + """ Ensure the proxy can ping - ''' - ret = self.run_function('test.ping') + """ + ret = self.run_function("test.ping") self.assertEqual(ret, True) @requires_system_grains @skip_if_not_root def test_service(self, grains): - service = 'cron' - os_family = grains['os_family'] - os_release = grains['osrelease'] - if os_family == 'RedHat': - service = 'crond' - elif os_family == 'Arch': - service = 'sshd' - elif os_family == 'MacOS': - service = 'org.ntp.ntpd' - if int(os_release.split('.')[1]) >= 13: - service = 'com.apple.AirPlayXPCHelper' - ret = self.run_function('service.get_all') + service = "cron" + os_family = grains["os_family"] + os_release = grains["osrelease"] + if os_family == "RedHat": + service = "crond" + elif os_family == "Arch": + service = "sshd" + elif os_family == "MacOS": + service = "org.ntp.ntpd" + if int(os_release.split(".")[1]) >= 13: + service = "com.apple.AirPlayXPCHelper" + self.run_function("service.enable", [service]) + ret = self.run_function("service.get_all") self.assertIn(service, ret) - self.run_function('service.stop', [service]) - ret = self.run_function('service.status', [service]) + self.run_function("service.stop", [service]) + ret = self.run_function("service.status", [service]) self.assertFalse(ret) - self.run_function('service.start', [service]) - ret = self.run_function('service.status', [service]) + self.run_function("service.start", [service]) + ret = self.run_function("service.status", [service]) self.assertTrue(ret) @requires_system_grains def test_grains_items(self, grains): - os_family = grains['os_family'] - ret = self.run_function('grains.items') - if os_family == 'MacOS': - self.assertEqual(ret['kernel'], 'Darwin') + os_family = grains["os_family"] + ret = self.run_function("grains.items") + if os_family == "MacOS": + self.assertEqual(ret["kernel"], "Darwin") else: - self.assertEqual(ret['kernel'], 'Linux') + self.assertEqual(ret["kernel"], "Linux") def test_state_apply(self): - ret = self.run_function('state.apply', ['core']) + ret = self.run_function("state.apply", ["core"]) for key, value in ret.items(): - self.assertTrue(value['result']) + self.assertTrue(value["result"]) def test_state_highstate(self): - ret = self.run_function('state.highstate') + ret = self.run_function("state.highstate") for key, value in ret.items(): - self.assertTrue(value['result']) + self.assertTrue(value["result"]) diff --git a/tests/integration/ssh/test_mine.py b/tests/integration/ssh/test_mine.py index 7e975444579..58aa9e0d4ad 100644 --- a/tests/integration/ssh/test_mine.py +++ b/tests/integration/ssh/test_mine.py @@ -2,33 +2,35 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil +# Import Salt Libs +import salt.utils.platform + # Import Salt Testing Libs from tests.support.case import SSHCase from tests.support.unit import skipIf -# Import Salt Libs -import salt.utils.platform - -@skipIf(salt.utils.platform.is_windows(), 'salt-ssh not available on Windows') +@skipIf(salt.utils.platform.is_windows(), "salt-ssh not available on Windows") class SSHMineTest(SSHCase): - ''' + """ testing salt-ssh with mine - ''' + """ + def test_ssh_mine_get(self): - ''' + """ test salt-ssh with mine - ''' - ret = self.run_function('mine.get', ['localhost test.arg'], wipe=False) - self.assertEqual(ret['localhost']['args'], ['itworked']) + """ + ret = self.run_function("mine.get", ["localhost test.arg"], wipe=False) + self.assertEqual(ret["localhost"]["args"], ["itworked"]) def tearDown(self): - ''' + """ make sure to clean up any old ssh directories - ''' - salt_dir = self.run_function('config.get', ['thin_dir'], wipe=False) + """ + salt_dir = self.run_function("config.get", ["thin_dir"], wipe=False) if os.path.exists(salt_dir): shutil.rmtree(salt_dir) diff --git a/tests/integration/ssh/test_pillar.py b/tests/integration/ssh/test_pillar.py index d20d49696b1..f844eec18d3 100644 --- a/tests/integration/ssh/test_pillar.py +++ b/tests/integration/ssh/test_pillar.py @@ -3,39 +3,40 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +# Import Salt Libs +import salt.utils.platform + # Import Salt Testing Libs from tests.support.case import SSHCase from tests.support.unit import skipIf -# Import Salt Libs -import salt.utils.platform - -@skipIf(salt.utils.platform.is_windows(), 'salt-ssh not available on Windows') +@skipIf(salt.utils.platform.is_windows(), "salt-ssh not available on Windows") class SSHPillarTest(SSHCase): - ''' + """ testing pillar with salt-ssh - ''' + """ + def test_pillar_items(self): - ''' + """ test pillar.items with salt-ssh - ''' - ret = self.run_function('pillar.items') - self.assertDictContainsSubset({'monty': 'python'}, ret) + """ + ret = self.run_function("pillar.items") + self.assertDictContainsSubset({"monty": "python"}, ret) self.assertDictContainsSubset( - {'knights': ['Lancelot', 'Galahad', 'Bedevere', 'Robin']}, - ret) + {"knights": ["Lancelot", "Galahad", "Bedevere", "Robin"]}, ret + ) def test_pillar_get(self): - ''' + """ test pillar.get with salt-ssh - ''' - ret = self.run_function('pillar.get', ['monty']) - self.assertEqual(ret, 'python') + """ + ret = self.run_function("pillar.get", ["monty"]) + self.assertEqual(ret, "python") def test_pillar_get_doesnotexist(self): - ''' + """ test pillar.get when pillar does not exist with salt-ssh - ''' - ret = self.run_function('pillar.get', ['doesnotexist']) - self.assertEqual(ret, '') + """ + ret = self.run_function("pillar.get", ["doesnotexist"]) + self.assertEqual(ret, "") diff --git a/tests/integration/ssh/test_pre_flight.py b/tests/integration/ssh/test_pre_flight.py new file mode 100644 index 00000000000..fb7725785f1 --- /dev/null +++ b/tests/integration/ssh/test_pre_flight.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" +Test for ssh_pre_flight roster option +""" +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +import os + +# import salt libs +import salt.utils.files + +# Import salt testing libs +from tests.support.case import SSHCase +from tests.support.runtests import RUNTIME_VARS + + +class SSHPreFlightTest(SSHCase): + """ + Test ssh_pre_flight roster option + """ + + def setUp(self): + super(SSHPreFlightTest, self).setUp() + self.roster = os.path.join(RUNTIME_VARS.TMP, "pre_flight_roster") + self.data = { + "ssh_pre_flight": os.path.join(RUNTIME_VARS.TMP, "ssh_pre_flight.sh") + } + self.test_script = os.path.join( + RUNTIME_VARS.TMP, "test-pre-flight-script-worked.txt" + ) + + def _create_roster(self): + self.custom_roster(self.roster, self.data) + + with salt.utils.files.fopen(self.data["ssh_pre_flight"], "w") as fp_: + fp_.write("touch {0}".format(self.test_script)) + + def test_ssh_pre_flight(self): + """ + test ssh when ssh_pre_flight is set + ensure the script runs successfully + """ + self._create_roster() + ret = self.run_function("test.ping", roster_file=self.roster) + + assert os.path.exists(self.test_script) + + def test_ssh_run_pre_flight(self): + """ + test ssh when --pre-flight is passed to salt-ssh + to ensure the script runs successfully + """ + self._create_roster() + # make sure we previously ran a command so the thin dir exists + self.run_function("test.ping", wipe=False) + assert not os.path.exists(self.test_script) + + ret = self.run_function( + "test.ping", ssh_opts="--pre-flight", roster_file=self.roster, wipe=False + ) + assert os.path.exists(self.test_script) + + def tearDown(self): + """ + make sure to clean up any old ssh directories + """ + files = [self.roster, self.data["ssh_pre_flight"], self.test_script] + for fp_ in files: + if os.path.exists(fp_): + os.remove(fp_) diff --git a/tests/integration/ssh/test_raw.py b/tests/integration/ssh/test_raw.py index ca07a6c6b64..95ae67db371 100644 --- a/tests/integration/ssh/test_raw.py +++ b/tests/integration/ssh/test_raw.py @@ -3,23 +3,24 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +# Import Salt Libs +import salt.utils.platform + # Import Salt Testing Libs from tests.support.case import SSHCase from tests.support.unit import skipIf -# Import Salt Libs -import salt.utils.platform - -@skipIf(salt.utils.platform.is_windows(), 'salt-ssh not available on Windows') +@skipIf(salt.utils.platform.is_windows(), "salt-ssh not available on Windows") class SSHRawTest(SSHCase): - ''' + """ testing salt-ssh with raw calls - ''' + """ + def test_ssh_raw(self): - ''' + """ test salt-ssh with -r argument - ''' - msg = 'running raw msg' - ret = self.run_function('echo {0}'.format(msg), raw=True) - self.assertEqual(ret['stdout'], msg + '\n') + """ + msg = "running raw msg" + ret = self.run_function("echo {0}".format(msg), raw=True) + self.assertEqual(ret["stdout"], msg + "\n") diff --git a/tests/integration/ssh/test_saltcheck.py b/tests/integration/ssh/test_saltcheck.py index 4a59d299d76..ba9e7dc68a6 100644 --- a/tests/integration/ssh/test_saltcheck.py +++ b/tests/integration/ssh/test_saltcheck.py @@ -3,34 +3,39 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +# Import Salt Libs +import salt.utils.platform + # Import Salt Testing Libs from tests.support.case import SSHCase from tests.support.unit import skipIf -# Import Salt Libs -import salt.utils.platform - -@skipIf(salt.utils.platform.is_windows(), 'salt-ssh not available on Windows') +@skipIf(salt.utils.platform.is_windows(), "salt-ssh not available on Windows") class SSHSaltcheckTest(SSHCase): - ''' + """ testing saltcheck with salt-ssh - ''' + """ + def test_saltcheck_run_test(self): - ''' + """ test saltcheck.run_test with salt-ssh - ''' - saltcheck_test = {"module_and_function": "test.echo", - "assertion": "assertEqual", - "expected-return": "Test Works", - "args": ["Test Works"]} - ret = self.run_function('saltcheck.run_test', test=saltcheck_test) - self.assertDictContainsSubset({'status': 'Pass'}, ret) + """ + saltcheck_test = { + "module_and_function": "test.echo", + "assertion": "assertEqual", + "expected-return": "Test Works", + "args": ["Test Works"], + } + ret = self.run_function("saltcheck.run_test", test=saltcheck_test) + self.assertDictContainsSubset({"status": "Pass"}, ret) def test_saltcheck_state(self): - ''' + """ saltcheck.run_state_tests - ''' - saltcheck_test = 'validate-saltcheck' - ret = self.run_function('saltcheck.run_state_tests', [saltcheck_test]) - self.assertDictContainsSubset({'status': 'Pass'}, ret[0]['validate-saltcheck']['echo_test_hello']) + """ + saltcheck_test = "validate-saltcheck" + ret = self.run_function("saltcheck.run_state_tests", [saltcheck_test]) + self.assertDictContainsSubset( + {"status": "Pass"}, ret[0]["validate-saltcheck"]["echo_test_hello"] + ) diff --git a/tests/integration/ssh/test_state.py b/tests/integration/ssh/test_state.py index e96343a7fff..71fdd50d815 100644 --- a/tests/integration/ssh/test_state.py +++ b/tests/integration/ssh/test_state.py @@ -2,31 +2,33 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import shutil import threading import time -import logging - -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import SSHCase -from tests.support.helpers import flaky # Import Salt Libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=redefined-builtin +from tests.support.case import SSHCase +from tests.support.helpers import flaky -SSH_SLS = 'ssh_state_tests' -SSH_SLS_FILE = '/tmp/test' +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS + +SSH_SLS = "ssh_state_tests" +SSH_SLS_FILE = "/tmp/test" log = logging.getLogger(__name__) class SSHStateTest(SSHCase): - ''' + """ testing the state system with salt-ssh - ''' + """ + def _check_dict_ret(self, ret, val, exp_ret, equal=True): self.assertIsInstance(ret, dict) for key, value in ret.items(): @@ -37,175 +39,177 @@ class SSHStateTest(SSHCase): self.assertNotEqual(value[val], exp_ret) def _check_request(self, empty=False): - check = self.run_function('state.check_request', wipe=False) + check = self.run_function("state.check_request", wipe=False) if empty: - self.assertFalse(bool(check), 'bool({0}) is not False'.format(check)) + self.assertFalse(bool(check), "bool({0}) is not False".format(check)) else: - self._check_dict_ret(ret=check['default']['test_run']['local']['return'], - val='__sls__', exp_ret=SSH_SLS) + self._check_dict_ret( + ret=check["default"]["test_run"]["local"]["return"], + val="__sls__", + exp_ret=SSH_SLS, + ) def test_state_apply(self): - ''' + """ test state.apply with salt-ssh - ''' - ret = self.run_function('state.apply', [SSH_SLS]) - self._check_dict_ret(ret=ret, val='__sls__', exp_ret=SSH_SLS) + """ + ret = self.run_function("state.apply", [SSH_SLS]) + self._check_dict_ret(ret=ret, val="__sls__", exp_ret=SSH_SLS) - check_file = self.run_function('file.file_exists', ['/tmp/test']) + check_file = self.run_function("file.file_exists", ["/tmp/test"]) self.assertTrue(check_file) def test_state_sls_id(self): - ''' + """ test state.sls_id with salt-ssh - ''' + """ # check state.sls_id with test=True - ret = self.run_function('state.sls_id', ['ssh-file-test', SSH_SLS, - 'test=True']) - self._check_dict_ret(ret=ret, val='comment', - exp_ret='The file /tmp/test is set to be changed\nNote: No changes made, actual changes may\nbe different due to other states.') + ret = self.run_function("state.sls_id", ["ssh-file-test", SSH_SLS, "test=True"]) + self._check_dict_ret( + ret=ret, + val="comment", + exp_ret="The file /tmp/test is set to be changed\nNote: No changes made, actual changes may\nbe different due to other states.", + ) # check state.sls_id without test=True - ret = self.run_function('state.sls_id', ['ssh-file-test', SSH_SLS]) - self._check_dict_ret(ret=ret, val='__sls__', exp_ret=SSH_SLS) + ret = self.run_function("state.sls_id", ["ssh-file-test", SSH_SLS]) + self._check_dict_ret(ret=ret, val="__sls__", exp_ret=SSH_SLS) # make sure the other id in the state was not run - self._check_dict_ret(ret=ret, val='__id__', - exp_ret='second_id', equal=False) + self._check_dict_ret(ret=ret, val="__id__", exp_ret="second_id", equal=False) - check_file = self.run_function('file.file_exists', ['/tmp/test']) + check_file = self.run_function("file.file_exists", ["/tmp/test"]) self.assertTrue(check_file) def test_state_sls_wrong_id(self): - ''' + """ test state.sls_id when id does not exist - ''' + """ # check state.sls_id with test=True - ret = self.run_function('state.sls_id', ['doesnotexist', SSH_SLS]) - assert 'No matches for ID' in ret + ret = self.run_function("state.sls_id", ["doesnotexist", SSH_SLS]) + assert "No matches for ID" in ret def test_state_show_sls(self): - ''' + """ test state.show_sls with salt-ssh - ''' - ret = self.run_function('state.show_sls', [SSH_SLS]) - self._check_dict_ret(ret=ret, val='__sls__', exp_ret=SSH_SLS) + """ + ret = self.run_function("state.show_sls", [SSH_SLS]) + self._check_dict_ret(ret=ret, val="__sls__", exp_ret=SSH_SLS) - check_file = self.run_function('file.file_exists', [SSH_SLS_FILE], wipe=False) + check_file = self.run_function("file.file_exists", [SSH_SLS_FILE], wipe=False) self.assertFalse(check_file) def test_state_show_top(self): - ''' + """ test state.show_top with salt-ssh - ''' - ret = self.run_function('state.show_top') - self.assertEqual(ret, {'base': ['core', 'master_tops_test']}) + """ + ret = self.run_function("state.show_top") + self.assertEqual(ret, {"base": ["core", "master_tops_test"]}) def test_state_single(self): - ''' + """ state.single with salt-ssh - ''' - ret_out = {'name': 'itworked', - 'result': True, - 'comment': 'Success!'} + """ + ret_out = {"name": "itworked", "result": True, "comment": "Success!"} - single = self.run_function('state.single', - ['test.succeed_with_changes name=itworked']) + single = self.run_function( + "state.single", ["test.succeed_with_changes name=itworked"] + ) self.assertIsInstance(single, dict) for key, value in six.iteritems(single): self.assertIsInstance(value, dict) - self.assertEqual(value['name'], ret_out['name']) - self.assertEqual(value['result'], ret_out['result']) - self.assertEqual(value['comment'], ret_out['comment']) + self.assertEqual(value["name"], ret_out["name"]) + self.assertEqual(value["result"], ret_out["result"]) + self.assertEqual(value["comment"], ret_out["comment"]) def test_show_highstate(self): - ''' + """ state.show_highstate with salt-ssh - ''' - high = self.run_function('state.show_highstate') - destpath = os.path.join(RUNTIME_VARS.TMP, 'testfile') + """ + high = self.run_function("state.show_highstate") + destpath = os.path.join(RUNTIME_VARS.TMP, "testfile") self.assertIsInstance(high, dict) self.assertIn(destpath, high) - self.assertEqual(high[destpath]['__env__'], 'base') + self.assertEqual(high[destpath]["__env__"], "base") def test_state_high(self): - ''' + """ state.high with salt-ssh - ''' - ret_out = {'name': 'itworked', - 'result': True, - 'comment': 'Success!'} + """ + ret_out = {"name": "itworked", "result": True, "comment": "Success!"} - high = self.run_function('state.high', - ['"{"itworked": {"test": ["succeed_with_changes"]}}"']) + high = self.run_function( + "state.high", ['"{"itworked": {"test": ["succeed_with_changes"]}}"'] + ) self.assertIsInstance(high, dict) for key, value in six.iteritems(high): self.assertIsInstance(value, dict) - self.assertEqual(value['name'], ret_out['name']) - self.assertEqual(value['result'], ret_out['result']) - self.assertEqual(value['comment'], ret_out['comment']) + self.assertEqual(value["name"], ret_out["name"]) + self.assertEqual(value["result"], ret_out["result"]) + self.assertEqual(value["comment"], ret_out["comment"]) def test_show_lowstate(self): - ''' + """ state.show_lowstate with salt-ssh - ''' - low = self.run_function('state.show_lowstate') + """ + low = self.run_function("state.show_lowstate") self.assertIsInstance(low, list) self.assertIsInstance(low[0], dict) def test_state_low(self): - ''' + """ state.low with salt-ssh - ''' - ret_out = {'name': 'itworked', - 'result': True, - 'comment': 'Success!'} + """ + ret_out = {"name": "itworked", "result": True, "comment": "Success!"} low = self.run_function( - 'state.low', - ['"{"state": "test", "fun": "succeed_with_changes", "name": "itworked"}"']) + "state.low", + ['"{"state": "test", "fun": "succeed_with_changes", "name": "itworked"}"'], + ) self.assertIsInstance(low, dict) for key, value in six.iteritems(low): self.assertIsInstance(value, dict) - self.assertEqual(value['name'], ret_out['name']) - self.assertEqual(value['result'], ret_out['result']) - self.assertEqual(value['comment'], ret_out['comment']) + self.assertEqual(value["name"], ret_out["name"]) + self.assertEqual(value["result"], ret_out["result"]) + self.assertEqual(value["comment"], ret_out["comment"]) def test_state_request_check_clear(self): - ''' + """ test state.request system with salt-ssh while also checking and clearing request - ''' - request = self.run_function('state.request', [SSH_SLS], wipe=False) - self._check_dict_ret(ret=request, val='__sls__', exp_ret=SSH_SLS) + """ + request = self.run_function("state.request", [SSH_SLS], wipe=False) + self._check_dict_ret(ret=request, val="__sls__", exp_ret=SSH_SLS) self._check_request() - clear = self.run_function('state.clear_request', wipe=False) + clear = self.run_function("state.clear_request", wipe=False) self._check_request(empty=True) def test_state_run_request(self): - ''' + """ test state.request system with salt-ssh while also running the request later - ''' - request = self.run_function('state.request', [SSH_SLS], wipe=False) - self._check_dict_ret(ret=request, val='__sls__', exp_ret=SSH_SLS) + """ + request = self.run_function("state.request", [SSH_SLS], wipe=False) + self._check_dict_ret(ret=request, val="__sls__", exp_ret=SSH_SLS) - run = self.run_function('state.run_request', wipe=False) + run = self.run_function("state.run_request", wipe=False) - check_file = self.run_function('file.file_exists', [SSH_SLS_FILE], wipe=False) + check_file = self.run_function("file.file_exists", [SSH_SLS_FILE], wipe=False) self.assertTrue(check_file) @flaky def test_state_running(self): - ''' + """ test state.running with salt-ssh - ''' + """ + def _run_in_background(): - self.run_function('state.sls', ['running'], wipe=False) + self.run_function("state.sls", ["running"], wipe=False) bg_thread = threading.Thread(target=_run_in_background) bg_thread.start() @@ -214,29 +218,33 @@ class SSHStateTest(SSHCase): state_ret = [] for _ in range(30): time.sleep(5) - get_sls = self.run_function('state.running', wipe=False) + get_sls = self.run_function("state.running", wipe=False) state_ret.append(get_sls) - if expected in ' '.join(get_sls): + if expected in " ".join(get_sls): # We found the expected return break else: self.fail( - 'Did not find \'{0}\' in state.running return: {1}'.format(expected, state_ret) + "Did not find '{0}' in state.running return: {1}".format( + expected, state_ret + ) ) # make sure we wait until the earlier state is complete future = time.time() + 120 while True: - if expected not in ' '.join(self.run_function('state.running', wipe=False)): + if expected not in " ".join(self.run_function("state.running", wipe=False)): break if time.time() > future: - self.fail('state.pkg is still running overtime. Test did not clean up correctly.') + self.fail( + "state.pkg is still running overtime. Test did not clean up correctly." + ) def tearDown(self): - ''' + """ make sure to clean up any old ssh directories - ''' - salt_dir = self.run_function('config.get', ['thin_dir'], wipe=False) + """ + salt_dir = self.run_function("config.get", ["thin_dir"], wipe=False) self.assertIsInstance(salt_dir, six.string_types) if os.path.exists(salt_dir): shutil.rmtree(salt_dir) diff --git a/tests/integration/states/test_alternatives.py b/tests/integration/states/test_alternatives.py index 715d3548f6b..08579e77bbf 100644 --- a/tests/integration/states/test_alternatives.py +++ b/tests/integration/states/test_alternatives.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the alternatives state module -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt Testing libs @@ -15,46 +16,79 @@ from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf NO_ALTERNATIVES = False -if not os.path.exists('/etc/alternatives'): +if not os.path.exists("/etc/alternatives"): NO_ALTERNATIVES = True -@skipIf(NO_ALTERNATIVES, '/etc/alternatives does not exist on the system') +@skipIf(NO_ALTERNATIVES, "/etc/alternatives does not exist on the system") class AlterantivesStateTest(ModuleCase, SaltReturnAssertsMixin): @destructiveTest def test_install_set_and_remove(self): - ret = self.run_state('alternatives.set', name='alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH) + ret = self.run_state( + "alternatives.set", name="alt-test", path=RUNTIME_VARS.SHELL_TRUE_PATH + ) self.assertSaltFalseReturn(ret) - ret = self.run_state('alternatives.install', name='alt-test', - link='/usr/local/bin/alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH, priority=50) + ret = self.run_state( + "alternatives.install", + name="alt-test", + link="/usr/local/bin/alt-test", + path=RUNTIME_VARS.SHELL_TRUE_PATH, + priority=50, + ) self.assertSaltTrueReturn(ret) - self.assertSaltStateChangesEqual(ret, RUNTIME_VARS.SHELL_TRUE_PATH, keys=['path']) + self.assertSaltStateChangesEqual( + ret, RUNTIME_VARS.SHELL_TRUE_PATH, keys=["path"] + ) - ret = self.run_state('alternatives.install', name='alt-test', - link='/usr/local/bin/alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH, priority=50) + ret = self.run_state( + "alternatives.install", + name="alt-test", + link="/usr/local/bin/alt-test", + path=RUNTIME_VARS.SHELL_TRUE_PATH, + priority=50, + ) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual(ret, {}) - ret = self.run_state('alternatives.install', name='alt-test', - link='/usr/local/bin/alt-test', path=RUNTIME_VARS.SHELL_FALSE_PATH, priority=90) + ret = self.run_state( + "alternatives.install", + name="alt-test", + link="/usr/local/bin/alt-test", + path=RUNTIME_VARS.SHELL_FALSE_PATH, + priority=90, + ) self.assertSaltTrueReturn(ret) - self.assertSaltStateChangesEqual(ret, RUNTIME_VARS.SHELL_FALSE_PATH, keys=['path']) + self.assertSaltStateChangesEqual( + ret, RUNTIME_VARS.SHELL_FALSE_PATH, keys=["path"] + ) - ret = self.run_state('alternatives.set', name='alt-test', path=RUNTIME_VARS.SHELL_FALSE_PATH) + ret = self.run_state( + "alternatives.set", name="alt-test", path=RUNTIME_VARS.SHELL_FALSE_PATH + ) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual(ret, {}) - ret = self.run_state('alternatives.set', name='alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH) + ret = self.run_state( + "alternatives.set", name="alt-test", path=RUNTIME_VARS.SHELL_TRUE_PATH + ) self.assertSaltTrueReturn(ret) - self.assertSaltStateChangesEqual(ret, RUNTIME_VARS.SHELL_TRUE_PATH, keys=['path']) + self.assertSaltStateChangesEqual( + ret, RUNTIME_VARS.SHELL_TRUE_PATH, keys=["path"] + ) - ret = self.run_state('alternatives.set', name='alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH) + ret = self.run_state( + "alternatives.set", name="alt-test", path=RUNTIME_VARS.SHELL_TRUE_PATH + ) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual(ret, {}) - ret = self.run_state('alternatives.remove', name='alt-test', path=RUNTIME_VARS.SHELL_TRUE_PATH) + ret = self.run_state( + "alternatives.remove", name="alt-test", path=RUNTIME_VARS.SHELL_TRUE_PATH + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('alternatives.remove', name='alt-test', path=RUNTIME_VARS.SHELL_FALSE_PATH) + ret = self.run_state( + "alternatives.remove", name="alt-test", path=RUNTIME_VARS.SHELL_FALSE_PATH + ) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_ansiblegate.py b/tests/integration/states/test_ansiblegate.py index 5d2f0ad5127..0f385cead51 100644 --- a/tests/integration/states/test_ansiblegate.py +++ b/tests/integration/states/test_ansiblegate.py @@ -1,87 +1,91 @@ # -*- coding: utf-8 -*- -''' +""" Test AnsibleGate State Module -''' -from __future__ import absolute_import, unicode_literals, print_function +""" +from __future__ import absolute_import, print_function, unicode_literals # Import python libraries import os import shutil import tempfile -import yaml # Import salt libraries import salt.utils.files import salt.utils.path +import yaml # Import testing libraries from tests.support.case import ModuleCase from tests.support.helpers import ( destructiveTest, + flaky, requires_sshd_server, requires_system_grains, - flaky ) from tests.support.mixins import SaltReturnAssertsMixin from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import skipIf, SkipTest +from tests.support.unit import SkipTest, skipIf @destructiveTest @requires_sshd_server -@skipIf(not salt.utils.path.which('ansible-playbook'), 'ansible-playbook is not installed') +@skipIf( + not salt.utils.path.which("ansible-playbook"), "ansible-playbook is not installed" +) class AnsiblePlaybooksTestCase(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Test ansible.playbooks states - ''' + """ @classmethod @requires_system_grains def setUpClass(cls, grains=None): # pylint: disable=arguments-differ - if grains.get('os_family') == 'RedHat' and grains.get('osmajorrelease') == 6: - raise SkipTest('This test hangs the test suite on RedHat 6. Skipping for now.') + if grains.get("os_family") == "RedHat" and grains.get("osmajorrelease") == 6: + raise SkipTest( + "This test hangs the test suite on RedHat 6. Skipping for now." + ) def setUp(self): - priv_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test') + priv_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test") data = { - 'all': { - 'hosts': { - 'localhost': { - 'ansible_host': '127.0.0.1', - 'ansible_port': 2827, - 'ansible_user': RUNTIME_VARS.RUNNING_TESTS_USER, - 'ansible_ssh_private_key_file': priv_file, - 'ansible_ssh_extra_args': ( - '-o StrictHostKeyChecking=false ' - '-o UserKnownHostsFile=/dev/null ' - ) + "all": { + "hosts": { + "localhost": { + "ansible_host": "127.0.0.1", + "ansible_port": 2827, + "ansible_user": RUNTIME_VARS.RUNNING_TESTS_USER, + "ansible_ssh_private_key_file": priv_file, + "ansible_ssh_extra_args": ( + "-o StrictHostKeyChecking=false " + "-o UserKnownHostsFile=/dev/null " + ), }, }, }, } self.tempdir = tempfile.mkdtemp() - self.inventory = self.tempdir + 'inventory' - with salt.utils.files.fopen(self.inventory, 'w') as yaml_file: + self.inventory = self.tempdir + "inventory" + with salt.utils.files.fopen(self.inventory, "w") as yaml_file: yaml.dump(data, yaml_file, default_flow_style=False) def tearDown(self): shutil.rmtree(self.tempdir) - delattr(self, 'tempdir') - delattr(self, 'inventory') + delattr(self, "tempdir") + delattr(self, "inventory") @flaky def test_ansible_playbook(self): ret = self.run_state( - 'ansible.playbooks', - name='remove.yml', - git_repo='git://github.com/gtmanfred/playbooks.git', - ansible_kwargs={'inventory': self.inventory} + "ansible.playbooks", + name="remove.yml", + git_repo="git://github.com/gtmanfred/playbooks.git", + ansible_kwargs={"inventory": self.inventory}, ) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'ansible.playbooks', - name='install.yml', - git_repo='git://github.com/gtmanfred/playbooks.git', - ansible_kwargs={'inventory': self.inventory} + "ansible.playbooks", + name="install.yml", + git_repo="git://github.com/gtmanfred/playbooks.git", + ansible_kwargs={"inventory": self.inventory}, ) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_archive.py b/tests/integration/states/test_archive.py index 1ec561941d3..d08f55995f2 100644 --- a/tests/integration/states/test_archive.py +++ b/tests/integration/states/test_archive.py @@ -1,49 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the archive state -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import logging import os -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.helpers import skip_if_not_root, Webserver -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.runtests import RUNTIME_VARS - # Import Salt libs import salt.utils.files import salt.utils.platform +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import Webserver, skip_if_not_root +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS + # Setup logging log = logging.getLogger(__name__) -ARCHIVE_DIR = os.path.join('c:/', 'tmp') \ - if salt.utils.platform.is_windows() \ - else '/tmp/archive' +ARCHIVE_DIR = ( + os.path.join("c:/", "tmp") if salt.utils.platform.is_windows() else "/tmp/archive" +) -ARCHIVE_NAME = 'custom.tar.gz' -ARCHIVE_TAR_SOURCE = 'http://localhost:{0}/{1}'.format(9999, ARCHIVE_NAME) -ARCHIVE_TAR_HASH = 'md5=7643861ac07c30fe7d2310e9f25ca514' -ARCHIVE_TAR_SHA_HASH = 'sha256=9591159d86f0a180e4e0645b2320d0235e23e66c66797df61508bf185e0ac1d2' -ARCHIVE_TAR_BAD_HASH = 'md5=d41d8cd98f00b204e9800998ecf8427e' -ARCHIVE_TAR_HASH_UPPER = 'md5=7643861AC07C30FE7D2310E9F25CA514' +ARCHIVE_NAME = "custom.tar.gz" +ARCHIVE_TAR_SOURCE = "http://localhost:{0}/{1}".format(9999, ARCHIVE_NAME) +ARCHIVE_TAR_HASH = "md5=7643861ac07c30fe7d2310e9f25ca514" +ARCHIVE_TAR_SHA_HASH = ( + "sha256=9591159d86f0a180e4e0645b2320d0235e23e66c66797df61508bf185e0ac1d2" +) +ARCHIVE_TAR_BAD_HASH = "md5=d41d8cd98f00b204e9800998ecf8427e" +ARCHIVE_TAR_HASH_UPPER = "md5=7643861AC07C30FE7D2310E9F25CA514" class ArchiveTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the archive state - ''' + """ + @classmethod def setUpClass(cls): cls.webserver = Webserver() cls.webserver.start() - cls.archive_tar_source = cls.webserver.url('custom.tar.gz') - cls.archive_local_tar_source = 'file://{0}'.format(os.path.join(RUNTIME_VARS.BASE_FILES, ARCHIVE_NAME)) - cls.untar_file = os.path.join(ARCHIVE_DIR, 'custom/README') + cls.archive_tar_source = cls.webserver.url("custom.tar.gz") + cls.archive_local_tar_source = "file://{0}".format( + os.path.join(RUNTIME_VARS.BASE_FILES, ARCHIVE_NAME) + ) + cls.untar_file = os.path.join(ARCHIVE_DIR, "custom/README") @classmethod def tearDownClass(cls): @@ -64,46 +70,54 @@ class ArchiveTest(ModuleCase, SaltReturnAssertsMixin): raise def _check_extracted(self, path): - ''' + """ function to check if file was extracted - ''' - log.debug('Checking for extracted file: %s', path) + """ + log.debug("Checking for extracted file: %s", path) self.assertTrue(os.path.isfile(path)) def run_function(self, *args, **kwargs): # pylint: disable=arguments-differ ret = super(ArchiveTest, self).run_function(*args, **kwargs) - log.debug('ret = %s', ret) + log.debug("ret = %s", ret) return ret def run_state(self, *args, **kwargs): # pylint: disable=arguments-differ ret = super(ArchiveTest, self).run_state(*args, **kwargs) - log.debug('ret = %s', ret) + log.debug("ret = %s", ret) return ret def test_archive_extracted_skip_verify(self): - ''' + """ test archive.extracted with skip_verify - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_tar_source, archive_format='tar', - skip_verify=True) - if 'Timeout' in ret: - self.skipTest('Timeout talking to local tornado server.') + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_tar_source, + archive_format="tar", + skip_verify=True, + ) + if "Timeout" in ret: + self.skipTest("Timeout talking to local tornado server.") self.assertSaltTrueReturn(ret) self._check_extracted(self.untar_file) def test_archive_extracted_with_source_hash(self): - ''' + """ test archive.extracted without skip_verify only external resources work to check to ensure source_hash is verified correctly - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_tar_source, archive_format='tar', - source_hash=ARCHIVE_TAR_HASH) - if 'Timeout' in ret: - self.skipTest('Timeout talking to local tornado server.') + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_tar_source, + archive_format="tar", + source_hash=ARCHIVE_TAR_HASH, + ) + if "Timeout" in ret: + self.skipTest("Timeout talking to local tornado server.") self.assertSaltTrueReturn(ret) @@ -111,175 +125,224 @@ class ArchiveTest(ModuleCase, SaltReturnAssertsMixin): @skip_if_not_root def test_archive_extracted_with_root_user_and_group(self): - ''' + """ test archive.extracted with user and group set to "root" - ''' - r_group = 'root' + """ + r_group = "root" if salt.utils.platform.is_darwin(): - r_group = 'wheel' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_tar_source, archive_format='tar', - source_hash=ARCHIVE_TAR_HASH, - user='root', group=r_group) - if 'Timeout' in ret: - self.skipTest('Timeout talking to local tornado server.') + r_group = "wheel" + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_tar_source, + archive_format="tar", + source_hash=ARCHIVE_TAR_HASH, + user="root", + group=r_group, + ) + if "Timeout" in ret: + self.skipTest("Timeout talking to local tornado server.") self.assertSaltTrueReturn(ret) self._check_extracted(self.untar_file) def test_archive_extracted_with_strip_in_options(self): - ''' + """ test archive.extracted with --strip in options - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_tar_source, - source_hash=ARCHIVE_TAR_HASH, - options='--strip=1', - enforce_toplevel=False) - if 'Timeout' in ret: - self.skipTest('Timeout talking to local tornado server.') + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_tar_source, + source_hash=ARCHIVE_TAR_HASH, + options="--strip=1", + enforce_toplevel=False, + ) + if "Timeout" in ret: + self.skipTest("Timeout talking to local tornado server.") self.assertSaltTrueReturn(ret) - self._check_extracted(os.path.join(ARCHIVE_DIR, 'README')) + self._check_extracted(os.path.join(ARCHIVE_DIR, "README")) def test_archive_extracted_with_strip_components_in_options(self): - ''' + """ test archive.extracted with --strip-components in options - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_tar_source, - source_hash=ARCHIVE_TAR_HASH, - options='--strip-components=1', - enforce_toplevel=False) - if 'Timeout' in ret: - self.skipTest('Timeout talking to local tornado server.') + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_tar_source, + source_hash=ARCHIVE_TAR_HASH, + options="--strip-components=1", + enforce_toplevel=False, + ) + if "Timeout" in ret: + self.skipTest("Timeout talking to local tornado server.") self.assertSaltTrueReturn(ret) - self._check_extracted(os.path.join(ARCHIVE_DIR, 'README')) + self._check_extracted(os.path.join(ARCHIVE_DIR, "README")) def test_archive_extracted_without_archive_format(self): - ''' + """ test archive.extracted with no archive_format option - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_tar_source, - source_hash=ARCHIVE_TAR_HASH) - if 'Timeout' in ret: - self.skipTest('Timeout talking to local tornado server.') + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_tar_source, + source_hash=ARCHIVE_TAR_HASH, + ) + if "Timeout" in ret: + self.skipTest("Timeout talking to local tornado server.") self.assertSaltTrueReturn(ret) self._check_extracted(self.untar_file) def test_archive_extracted_with_cmd_unzip_false(self): - ''' + """ test archive.extracted using use_cmd_unzip argument as false - ''' + """ - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_tar_source, - source_hash=ARCHIVE_TAR_HASH, - use_cmd_unzip=False, - archive_format='tar') - if 'Timeout' in ret: - self.skipTest('Timeout talking to local tornado server.') + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_tar_source, + source_hash=ARCHIVE_TAR_HASH, + use_cmd_unzip=False, + archive_format="tar", + ) + if "Timeout" in ret: + self.skipTest("Timeout talking to local tornado server.") self.assertSaltTrueReturn(ret) self._check_extracted(self.untar_file) def test_local_archive_extracted(self): - ''' + """ test archive.extracted with local file - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_local_tar_source, archive_format='tar') + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_local_tar_source, + archive_format="tar", + ) self.assertSaltTrueReturn(ret) self._check_extracted(self.untar_file) def test_local_archive_extracted_skip_verify(self): - ''' + """ test archive.extracted with local file, bad hash and skip_verify - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_local_tar_source, archive_format='tar', - source_hash=ARCHIVE_TAR_BAD_HASH, skip_verify=True) + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_local_tar_source, + archive_format="tar", + source_hash=ARCHIVE_TAR_BAD_HASH, + skip_verify=True, + ) self.assertSaltTrueReturn(ret) self._check_extracted(self.untar_file) def test_local_archive_extracted_with_source_hash(self): - ''' + """ test archive.extracted with local file and valid hash - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_local_tar_source, archive_format='tar', - source_hash=ARCHIVE_TAR_HASH) + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_local_tar_source, + archive_format="tar", + source_hash=ARCHIVE_TAR_HASH, + ) self.assertSaltTrueReturn(ret) self._check_extracted(self.untar_file) def test_local_archive_extracted_with_bad_source_hash(self): - ''' + """ test archive.extracted with local file and bad hash - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_local_tar_source, archive_format='tar', - source_hash=ARCHIVE_TAR_BAD_HASH) + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_local_tar_source, + archive_format="tar", + source_hash=ARCHIVE_TAR_BAD_HASH, + ) self.assertSaltFalseReturn(ret) def test_local_archive_extracted_with_uppercase_source_hash(self): - ''' + """ test archive.extracted with local file and bad hash - ''' - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_local_tar_source, archive_format='tar', - source_hash=ARCHIVE_TAR_HASH_UPPER) + """ + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_local_tar_source, + archive_format="tar", + source_hash=ARCHIVE_TAR_HASH_UPPER, + ) self.assertSaltTrueReturn(ret) self._check_extracted(self.untar_file) def test_archive_extracted_with_non_base_saltenv(self): - ''' + """ test archive.extracted with a saltenv other than `base` - ''' + """ ret = self.run_function( - 'state.sls', - ['issue45893'], - pillar={'issue45893.name': ARCHIVE_DIR}, - saltenv='prod') + "state.sls", + ["issue45893"], + pillar={"issue45893.name": ARCHIVE_DIR}, + saltenv="prod", + ) self.assertSaltTrueReturn(ret) self._check_extracted(os.path.join(ARCHIVE_DIR, self.untar_file)) def test_local_archive_extracted_with_skip_files_list_verify(self): - ''' + """ test archive.extracted with local file and skip_files_list_verify set to True - ''' - expected_comment = ('existing source sum is the same as the expected one and ' - 'skip_files_list_verify argument was set to True. ' - 'Extraction is not needed') - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_local_tar_source, archive_format='tar', - skip_files_list_verify=True, - source_hash_update=True, - source_hash=ARCHIVE_TAR_SHA_HASH) + """ + expected_comment = ( + "existing source sum is the same as the expected one and " + "skip_files_list_verify argument was set to True. " + "Extraction is not needed" + ) + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_local_tar_source, + archive_format="tar", + skip_files_list_verify=True, + source_hash_update=True, + source_hash=ARCHIVE_TAR_SHA_HASH, + ) self.assertSaltTrueReturn(ret) self._check_extracted(self.untar_file) - ret = self.run_state('archive.extracted', name=ARCHIVE_DIR, - source=self.archive_local_tar_source, archive_format='tar', - skip_files_list_verify=True, - source_hash_update=True, - source_hash=ARCHIVE_TAR_SHA_HASH) + ret = self.run_state( + "archive.extracted", + name=ARCHIVE_DIR, + source=self.archive_local_tar_source, + archive_format="tar", + skip_files_list_verify=True, + source_hash_update=True, + source_hash=ARCHIVE_TAR_SHA_HASH, + ) self.assertSaltTrueReturn(ret) self.assertInSaltComment(expected_comment, ret) diff --git a/tests/integration/states/test_beacon.py b/tests/integration/states/test_beacon.py index a73f1fb74bc..28c6f293ba8 100644 --- a/tests/integration/states/test_beacon.py +++ b/tests/integration/states/test_beacon.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the beacon states -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing Libs @@ -10,43 +10,33 @@ from tests.support.mixins import SaltReturnAssertsMixin class BeaconStateTestCase(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Test beacon states - ''' + """ + def setUp(self): - ''' - ''' - self.run_function('beacons.reset', f_timeout=300) + """ + """ + self.run_function("beacons.reset", f_timeout=300) self.wait_for_all_jobs() def tearDown(self): - self.run_function('beacons.reset', f_timeout=300) + self.run_function("beacons.reset", f_timeout=300) def test_present_absent(self): - kwargs = {'/': '38%', 'interval': 5} + kwargs = {"/": "38%", "interval": 5} ret = self.run_state( - 'beacon.present', - name='diskusage', - f_timeout=300, - **kwargs + "beacon.present", name="diskusage", f_timeout=300, **kwargs ) self.assertSaltTrueReturn(ret) - ret = self.run_function('beacons.list', - return_yaml=False, - f_timeout=300) - self.assertTrue('diskusage' in ret) - self.assertTrue({'interval': 5} in ret['diskusage']) - self.assertTrue({'/': '38%'} in ret['diskusage']) + ret = self.run_function("beacons.list", return_yaml=False, f_timeout=300) + self.assertTrue("diskusage" in ret) + self.assertTrue({"interval": 5} in ret["diskusage"]) + self.assertTrue({"/": "38%"} in ret["diskusage"]) - ret = self.run_state( - 'beacon.absent', - name='diskusage', - f_timeout=300 - ) + ret = self.run_state("beacon.absent", name="diskusage", f_timeout=300) self.assertSaltTrueReturn(ret) - ret = self.run_function('beacons.list', - return_yaml=False, - f_timeout=300) - self.assertEqual(ret, {'beacons': {}}) + ret = self.run_function("beacons.list", return_yaml=False, f_timeout=300) + self.assertEqual(ret, {"beacons": {}}) diff --git a/tests/integration/states/test_boto_sns.py b/tests/integration/states/test_boto_sns.py index b040d9a215e..ef8103bea42 100644 --- a/tests/integration/states/test_boto_sns.py +++ b/tests/integration/states/test_boto_sns.py @@ -5,16 +5,18 @@ Tests for the boto_sns state # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import re # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.unit import skipIf from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf # Import 3rd-party libs try: import boto + NO_BOTO_MODULE = False except ImportError: NO_BOTO_MODULE = True @@ -22,285 +24,294 @@ except ImportError: @skipIf( NO_BOTO_MODULE, - 'Please install the boto library before running boto integration tests.' + "Please install the boto library before running boto integration tests.", ) class BotoSNSTest(ModuleCase, SaltReturnAssertsMixin): - def setUp(self): try: boto.connect_iam() except boto.exception.NoAuthHandlerFound: - self.skipTest('Please setup boto AWS credentials before running boto integration tests.') + self.skipTest( + "Please setup boto AWS credentials before running boto integration tests." + ) # The name of the topic you want to create. # Constraints: Topic names must be made up of only uppercase and # lowercase ASCII letters, numbers, underscores, and hyphens, # and must be between 1 and 256 characters long. # http://docs.aws.amazon.com/sns/latest/api/API_CreateTopic.html - self.topic_name = re.sub(r'[^a-zA-Z_-]', '_', self.id())[0:256] - self.run_function('boto_sns.delete', name=self.topic_name) + self.topic_name = re.sub(r"[^a-zA-Z_-]", "_", self.id())[0:256] + self.run_function("boto_sns.delete", name=self.topic_name) def tearDown(self): - self.run_function('boto_sns.delete', name=self.topic_name) + self.run_function("boto_sns.delete", name=self.topic_name) def test_present_new_topic_no_subscriptions(self): - ret = self.run_state('boto_sns.present', - name=self.topic_name) + ret = self.run_state("boto_sns.present", name=self.topic_name) self.assertSaltTrueReturn(ret) - self.assertInSaltReturn(self.topic_name, ret, 'name') + self.assertInSaltReturn(self.topic_name, ret, "name") self.assertInSaltComment( - 'AWS SNS topic {0} created.'.format(self.topic_name), - ret + "AWS SNS topic {0} created.".format(self.topic_name), ret + ) + self.assertSaltStateChangesEqual( + ret, {"old": None, "new": {"topic": self.topic_name, "subscriptions": []}} ) - self.assertSaltStateChangesEqual(ret, - {'old': None, 'new': {'topic': self.topic_name, 'subscriptions': []}}) def test_present_new_topic_with_subscriptions(self): ret = self.run_state( - 'boto_sns.present', + "boto_sns.present", name=self.topic_name, subscriptions=[ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint' + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint", }, - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint-2' - } - ] + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint-2", + }, + ], ) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual( ret, - {'old': None, - 'new': { - 'topic': self.topic_name, - 'subscriptions': [ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint' - }, - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint-2' - } - ] - } - } + { + "old": None, + "new": { + "topic": self.topic_name, + "subscriptions": [ + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint", + }, + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint-2", + }, + ], + }, + }, ) self.assertInSaltComment( - 'AWS SNS subscription https:https://www.example.com/sns/endpoint set on topic {0}.' - .format(self.topic_name), - ret + "AWS SNS subscription https:https://www.example.com/sns/endpoint set on topic {0}.".format( + self.topic_name + ), + ret, ) self.assertInSaltComment( - 'AWS SNS subscription https:https://www.example.com/sns/endpoint-2 set on topic {0}.' - .format(self.topic_name), - ret + "AWS SNS subscription https:https://www.example.com/sns/endpoint-2 set on topic {0}.".format( + self.topic_name + ), + ret, + ) + self.assertSubscriptionInTopic( + {"Protocol": "https", "Endpoint": "https://www.example.com/sns/endpoint"}, + self.topic_name, + ) + self.assertSubscriptionInTopic( + {"Protocol": "https", "Endpoint": "https://www.example.com/sns/endpoint-2"}, + self.topic_name, ) - self.assertSubscriptionInTopic({ - 'Protocol': 'https', - 'Endpoint': 'https://www.example.com/sns/endpoint' - }, self.topic_name) - self.assertSubscriptionInTopic({ - 'Protocol': 'https', - 'Endpoint': 'https://www.example.com/sns/endpoint-2' - }, self.topic_name) def test_present_is_idempotent(self): self.run_state( - 'boto_sns.present', + "boto_sns.present", name=self.topic_name, subscriptions=[ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint' + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint", } - ] + ], ) ret = self.run_state( - 'boto_sns.present', + "boto_sns.present", name=self.topic_name, subscriptions=[ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint' + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint", } - ] + ], ) self.assertSaltTrueReturn(ret) - self.assertInSaltReturn(self.topic_name, ret, 'name') + self.assertInSaltReturn(self.topic_name, ret, "name") self.assertInSaltComment( - 'AWS SNS topic {0} present.'.format(self.topic_name), - ret + "AWS SNS topic {0} present.".format(self.topic_name), ret ) self.assertInSaltComment( - 'AWS SNS subscription https:https://www.example.com/sns/endpoint already set on topic {0}.' - .format(self.topic_name), - ret + "AWS SNS subscription https:https://www.example.com/sns/endpoint already set on topic {0}.".format( + self.topic_name + ), + ret, ) self.assertSaltStateChangesEqual(ret, {}) def test_present_add_subscription_to_existing_topic_with_no_subscription(self): - self.run_state('boto_sns.present', name=self.topic_name) + self.run_state("boto_sns.present", name=self.topic_name) ret = self.run_state( - 'boto_sns.present', + "boto_sns.present", name=self.topic_name, subscriptions=[ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint' + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint", } - ] + ], ) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual( ret, - {'old': None, - 'new': {'subscriptions': [ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint' + { + "old": None, + "new": { + "subscriptions": [ + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint", } - ] - } - } + ] + }, + }, ) self.assertInSaltComment( - 'AWS SNS subscription https:https://www.example.com/sns/endpoint set on topic {0}.' - .format(self.topic_name), - ret + "AWS SNS subscription https:https://www.example.com/sns/endpoint set on topic {0}.".format( + self.topic_name + ), + ret, ) - self.assertSubscriptionInTopic({ - 'Protocol': 'https', - 'Endpoint': 'https://www.example.com/sns/endpoint' - }, self.topic_name) + self.assertSubscriptionInTopic( + {"Protocol": "https", "Endpoint": "https://www.example.com/sns/endpoint"}, + self.topic_name, + ) def test_present_add_new_subscription_to_existing_topic_with_subscriptions(self): self.run_state( - 'boto_sns.present', + "boto_sns.present", name=self.topic_name, subscriptions=[ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint' + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint", } - ] + ], ) ret = self.run_state( - 'boto_sns.present', + "boto_sns.present", name=self.topic_name, subscriptions=[ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint-2' + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint-2", } - ] + ], ) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual( ret, - {'old': None, - 'new': { - 'subscriptions': [ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint-2' - } - ] - } - } + { + "old": None, + "new": { + "subscriptions": [ + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint-2", + } + ] + }, + }, ) self.assertInSaltComment( - 'AWS SNS subscription https:https://www.example.com/sns/endpoint-2 set on topic {0}.' - .format(self.topic_name), - ret + "AWS SNS subscription https:https://www.example.com/sns/endpoint-2 set on topic {0}.".format( + self.topic_name + ), + ret, ) - self.assertSubscriptionInTopic({ - 'Protocol': 'https', - 'Endpoint': 'https://www.example.com/sns/endpoint' - }, self.topic_name) - self.assertSubscriptionInTopic({ - 'Protocol': 'https', - 'Endpoint': 'https://www.example.com/sns/endpoint-2' - }, self.topic_name) + self.assertSubscriptionInTopic( + {"Protocol": "https", "Endpoint": "https://www.example.com/sns/endpoint"}, + self.topic_name, + ) + self.assertSubscriptionInTopic( + {"Protocol": "https", "Endpoint": "https://www.example.com/sns/endpoint-2"}, + self.topic_name, + ) def test_present_test_mode_no_subscriptions(self): - ret = self.run_state('boto_sns.present', - name=self.topic_name, - test=True) + ret = self.run_state("boto_sns.present", name=self.topic_name, test=True) self.assertSaltNoneReturn(ret) - self.assertInSaltReturn(self.topic_name, ret, 'name') + self.assertInSaltReturn(self.topic_name, ret, "name") self.assertInSaltComment( - 'AWS SNS topic {0} is set to be created.'.format(self.topic_name), - ret + "AWS SNS topic {0} is set to be created.".format(self.topic_name), ret ) self.assertSaltStateChangesEqual(ret, {}) - ret = self.run_function('boto_sns.exists', name=self.topic_name) + ret = self.run_function("boto_sns.exists", name=self.topic_name) self.assertFalse(ret) def test_present_test_mode_with_subscriptions(self): - self.run_state('boto_sns.present', name=self.topic_name) + self.run_state("boto_sns.present", name=self.topic_name) ret = self.run_state( - 'boto_sns.present', + "boto_sns.present", name=self.topic_name, subscriptions=[ - {'protocol': 'https', - 'endpoint': 'https://www.example.com/sns/endpoint' + { + "protocol": "https", + "endpoint": "https://www.example.com/sns/endpoint", } ], - test=True + test=True, ) self.assertSaltNoneReturn(ret) self.assertSaltStateChangesEqual(ret, {}) self.assertInSaltComment( - 'AWS SNS subscription https:https://www.example.com/sns/endpoint to be set on topic {0}.' - .format(self.topic_name), - ret + "AWS SNS subscription https:https://www.example.com/sns/endpoint to be set on topic {0}.".format( + self.topic_name + ), + ret, ) ret = self.run_function( - 'boto_sns.get_all_subscriptions_by_topic', - name=self.topic_name + "boto_sns.get_all_subscriptions_by_topic", name=self.topic_name ) self.assertEqual([], ret) def test_absent_not_exist(self): - ret = self.run_state('boto_sns.absent', - name=self.topic_name) + ret = self.run_state("boto_sns.absent", name=self.topic_name) self.assertSaltTrueReturn(ret) - self.assertInSaltReturn(self.topic_name, ret, 'name') + self.assertInSaltReturn(self.topic_name, ret, "name") self.assertInSaltComment( - 'AWS SNS topic {0} does not exist.'.format(self.topic_name), - ret + "AWS SNS topic {0} does not exist.".format(self.topic_name), ret ) self.assertSaltStateChangesEqual(ret, {}) def test_absent_already_exists(self): - self.run_state('boto_sns.present', - name=self.topic_name) - ret = self.run_state('boto_sns.absent', - name=self.topic_name) + self.run_state("boto_sns.present", name=self.topic_name) + ret = self.run_state("boto_sns.absent", name=self.topic_name) self.assertSaltTrueReturn(ret) - self.assertInSaltReturn(self.topic_name, ret, 'name') + self.assertInSaltReturn(self.topic_name, ret, "name") self.assertInSaltComment( - 'AWS SNS topic {0} does not exist.'.format(self.topic_name), - ret + "AWS SNS topic {0} does not exist.".format(self.topic_name), ret ) self.assertSaltStateChangesEqual( - ret, {'new': None, 'old': {'topic': self.topic_name}}) + ret, {"new": None, "old": {"topic": self.topic_name}} + ) def test_absent_test_mode(self): - self.run_state('boto_sns.present', name=self.topic_name) - ret = self.run_state('boto_sns.absent', - name=self.topic_name, - test=True) + self.run_state("boto_sns.present", name=self.topic_name) + ret = self.run_state("boto_sns.absent", name=self.topic_name, test=True) self.assertSaltNoneReturn(ret) - self.assertInSaltReturn(self.topic_name, ret, 'name') + self.assertInSaltReturn(self.topic_name, ret, "name") self.assertInSaltComment( - 'AWS SNS topic {0} is set to be removed.'.format(self.topic_name), - ret + "AWS SNS topic {0} is set to be removed.".format(self.topic_name), ret ) self.assertSaltStateChangesEqual(ret, {}) - ret = self.run_function('boto_sns.exists', name=self.topic_name) + ret = self.run_function("boto_sns.exists", name=self.topic_name) self.assertTrue(ret) def assertSubscriptionInTopic(self, subscription, topic_name): ret = self.run_function( - 'boto_sns.get_all_subscriptions_by_topic', - name=topic_name + "boto_sns.get_all_subscriptions_by_topic", name=topic_name ) for _subscription in ret: try: @@ -309,6 +320,7 @@ class BotoSNSTest(ModuleCase, SaltReturnAssertsMixin): except AssertionError: continue raise self.failureException( - 'Subscription {0} not found in topic {1} subscriptions: {2}' - .format(subscription, topic_name, ret) + "Subscription {0} not found in topic {1} subscriptions: {2}".format( + subscription, topic_name, ret + ) ) diff --git a/tests/integration/states/test_bower.py b/tests/integration/states/test_bower.py index 6c01d90e14c..7843d1f4c15 100644 --- a/tests/integration/states/test_bower.py +++ b/tests/integration/states/test_bower.py @@ -1,75 +1,75 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Alexander Pyatkin <asp@thexyz.net -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest -from tests.support.mixins import SaltReturnAssertsMixin - # Import salt libs import salt.utils.json import salt.utils.path +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf -@skipIf(salt.utils.path.which('bower') is None, 'bower not installed') + +@skipIf(salt.utils.path.which("bower") is None, "bower not installed") class BowerStateTest(ModuleCase, SaltReturnAssertsMixin): - @destructiveTest def test_bower_installed_removed(self): - ''' + """ Basic test to determine if Bower package was successfully installed and removed. - ''' - ret = self.run_state('file.directory', name='/salt_test_bower_1', - makedirs=True) + """ + ret = self.run_state("file.directory", name="/salt_test_bower_1", makedirs=True) self.assertSaltTrueReturn(ret) - ret = self.run_state('bower.installed', name='underscore', - dir='/salt_test_bower_1') + ret = self.run_state( + "bower.installed", name="underscore", dir="/salt_test_bower_1" + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('bower.removed', name='underscore', - dir='/salt_test_bower_1') + ret = self.run_state( + "bower.removed", name="underscore", dir="/salt_test_bower_1" + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('file.absent', name='/salt_test_bower_1') + ret = self.run_state("file.absent", name="/salt_test_bower_1") self.assertSaltTrueReturn(ret) @destructiveTest def test_bower_installed_pkgs(self): - ''' + """ Basic test to determine if Bower package successfully installs multiple packages. - ''' - ret = self.run_state('file.directory', name='/salt_test_bower_2', - makedirs=True) + """ + ret = self.run_state("file.directory", name="/salt_test_bower_2", makedirs=True) self.assertSaltTrueReturn(ret) - ret = self.run_state('bower.installed', name='test', - dir='/salt_test_bower_2', - pkgs=['numeral', 'underscore']) + ret = self.run_state( + "bower.installed", + name="test", + dir="/salt_test_bower_2", + pkgs=["numeral", "underscore"], + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('file.absent', name='/salt_test_bower_2') + ret = self.run_state("file.absent", name="/salt_test_bower_2") self.assertSaltTrueReturn(ret) @destructiveTest def test_bower_installed_from_file(self): - ret = self.run_state('file.directory', name='/salt_test_bower_3', - makedirs=True) + ret = self.run_state("file.directory", name="/salt_test_bower_3", makedirs=True) self.assertSaltTrueReturn(ret) - bower_json = salt.utils.json.dumps({ - 'name': 'salt_test_bower_3', - 'dependencies': { - 'numeral': '~1.5.3', - 'underscore': '~1.7.0' + bower_json = salt.utils.json.dumps( + { + "name": "salt_test_bower_3", + "dependencies": {"numeral": "~1.5.3", "underscore": "~1.7.0"}, } - }) - ret = self.run_state('file.managed', - name='/salt_test_bower_3/bower.json', - contents=bower_json) + ) + ret = self.run_state( + "file.managed", name="/salt_test_bower_3/bower.json", contents=bower_json + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('bower.bootstrap', name='/salt_test_bower_3') + ret = self.run_state("bower.bootstrap", name="/salt_test_bower_3") self.assertSaltTrueReturn(ret) - ret = self.run_state('file.absent', name='/salt_test_bower_3') + ret = self.run_state("file.absent", name="/salt_test_bower_3") self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_chocolatey.py b/tests/integration/states/test_chocolatey.py index 2d6fc2858a4..68a1150a6e7 100644 --- a/tests/integration/states/test_chocolatey.py +++ b/tests/integration/states/test_chocolatey.py @@ -1,106 +1,104 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the Chocolatey State -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest +import logging # Import Salt libs import salt.utils.platform +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf + log = logging.getLogger(__name__) __testcontext__ = {} @destructiveTest -@skipIf(not salt.utils.platform.is_windows(), 'Windows Specific Test') +@skipIf(not salt.utils.platform.is_windows(), "Windows Specific Test") class ChocolateyTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Chocolatey State Tests These tests are destructive as the install and remove software - ''' + """ def setUp(self): - ''' + """ Ensure that Chocolatey is installed - ''' + """ super(ChocolateyTest, self).setUp() - if 'chocolatey' not in __testcontext__: - self.run_function('chocolatey.bootstrap') - __testcontext__['chocolatey'] = True + if "chocolatey" not in __testcontext__: + self.run_function("chocolatey.bootstrap") + __testcontext__["chocolatey"] = True def test_chocolatey(self): - ''' + """ Test the following: - `chocolatey.installed` - `chocolatey.upgraded` - `chocolatey.uninstalled` - ''' + """ # If this assert fails, we need to find new targets, this test needs to # be able to test successful installation of packages, so this package # needs to NOT be installed before we run the states below - target = 'firefox' - pre_version = '52.0.2' - upg_version = '57.0.2' - log.debug('Making sure %s is not installed', target) - self.assertFalse( - self.run_function('chocolatey.version', [target])) + target = "firefox" + pre_version = "52.0.2" + upg_version = "57.0.2" + log.debug("Making sure %s is not installed", target) + self.assertFalse(self.run_function("chocolatey.version", [target])) try: #################################################### # Test `chocolatey.installed` #################################################### # Install the package - log.debug('Testing chocolatey.installed') + log.debug("Testing chocolatey.installed") ret = self.run_state( - 'chocolatey.installed', - name=target, - version=pre_version) + "chocolatey.installed", name=target, version=pre_version + ) self.assertSaltTrueReturn(ret) # Verify the package is installed - log.debug('Verifying install success') - ret = self.run_function('chocolatey.version', [target]) - self.assertEqual(ret, {'Firefox': [pre_version]}) + log.debug("Verifying install success") + ret = self.run_function("chocolatey.version", [target]) + self.assertEqual(ret, {"Firefox": [pre_version]}) #################################################### # Test `chocolatey.upgraded` #################################################### # Upgrade the package - log.debug('Testing chocolatey.upgraded') + log.debug("Testing chocolatey.upgraded") ret = self.run_state( - 'chocolatey.upgraded', - name=target, - version=upg_version) + "chocolatey.upgraded", name=target, version=upg_version + ) self.assertSaltTrueReturn(ret) # Verify the package is upgraded - log.debug('Verifying upgrade success') - ret = self.run_function('chocolatey.version', [target]) - self.assertEqual(ret, {'Firefox': [upg_version]}) + log.debug("Verifying upgrade success") + ret = self.run_function("chocolatey.version", [target]) + self.assertEqual(ret, {"Firefox": [upg_version]}) #################################################### # Test `chocolatey.uninstalled` #################################################### # uninstall the package - log.debug('Testing chocolatey.uninstalled') - ret = self.run_state('chocolatey.uninstalled', name=target) + log.debug("Testing chocolatey.uninstalled") + ret = self.run_state("chocolatey.uninstalled", name=target) self.assertSaltTrueReturn(ret) # Verify the package is uninstalled - log.debug('Verifying uninstall success') - ret = self.run_function('chocolatey.version', [target]) + log.debug("Verifying uninstall success") + ret = self.run_function("chocolatey.version", [target]) self.assertEqual(ret, {}) finally: # Always uninstall - log.debug('Uninstalling %s', target) - self.run_function('chocolatey.uninstall', [target]) + log.debug("Uninstalling %s", target) + self.run_function("chocolatey.uninstall", [target]) diff --git a/tests/integration/states/test_cmd.py b/tests/integration/states/test_cmd.py index 12d194bd15f..ba795eeede1 100644 --- a/tests/integration/states/test_cmd.py +++ b/tests/integration/states/test_cmd.py @@ -1,20 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the file state -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import os -import textwrap -import tempfile -import time import sys - -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin +import tempfile +import textwrap +import time # Import Salt libs import salt.utils.files @@ -22,61 +18,69 @@ import salt.utils.platform # Import 3rd-party libs from salt.ext import six +from tests.support.case import ModuleCase +from tests.support.mixins import SaltReturnAssertsMixin + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS class CMDTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the cmd state - ''' + """ + @classmethod def setUpClass(cls): - cls.__cmd = 'dir' if salt.utils.platform.is_windows() else 'ls' + cls.__cmd = "dir" if salt.utils.platform.is_windows() else "ls" def test_run_simple(self): - ''' + """ cmd.run - ''' - ret = self.run_state('cmd.run', name=self.__cmd, cwd=tempfile.gettempdir()) + """ + ret = self.run_state("cmd.run", name=self.__cmd, cwd=tempfile.gettempdir()) self.assertSaltTrueReturn(ret) def test_run_output_loglevel(self): - ''' + """ cmd.run with output_loglevel=quiet - ''' - ret = self.run_state('cmd.run', name=self.__cmd, - cwd=tempfile.gettempdir(), - output_loglevel='quiet') + """ + ret = self.run_state( + "cmd.run", + name=self.__cmd, + cwd=tempfile.gettempdir(), + output_loglevel="quiet", + ) self.assertSaltTrueReturn(ret) def test_test_run_simple(self): - ''' + """ cmd.run test interface - ''' - ret = self.run_state('cmd.run', name=self.__cmd, - cwd=tempfile.gettempdir(), test=True) + """ + ret = self.run_state( + "cmd.run", name=self.__cmd, cwd=tempfile.gettempdir(), test=True + ) self.assertSaltNoneReturn(ret) def test_run_hide_output(self): - ''' + """ cmd.run with output hidden - ''' - ret = self.run_state( - u'cmd.run', - name=self.__cmd, - hide_output=True) + """ + ret = self.run_state("cmd.run", name=self.__cmd, hide_output=True) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes']['stdout'], '') - self.assertEqual(ret['changes']['stderr'], '') + self.assertEqual(ret["changes"]["stdout"], "") + self.assertEqual(ret["changes"]["stderr"], "") class CMDRunRedirectTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the cmd state of run_redirect - ''' + """ + def setUp(self): - self.state_name = 'run_redirect' - state_filename = self.state_name + '.sls' + self.state_name = "run_redirect" + state_filename = self.state_name + ".sls" self.state_file = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, state_filename) # Create the testfile and release the handle @@ -110,91 +114,128 @@ class CMDRunRedirectTest(ModuleCase, SaltReturnAssertsMixin): super(CMDRunRedirectTest, self).tearDown() def test_run_unless(self): - ''' + """ test cmd.run unless - ''' - state_key = 'cmd_|-{0}_|-{0}_|-run'.format(self.test_tmp_path) - with salt.utils.files.fopen(self.state_file, 'w') as fb_: - fb_.write(salt.utils.stringutils.to_str(textwrap.dedent(''' + """ + state_key = "cmd_|-{0}_|-{0}_|-run".format(self.test_tmp_path) + with salt.utils.files.fopen(self.state_file, "w") as fb_: + fb_.write( + salt.utils.stringutils.to_str( + textwrap.dedent( + """ {0}: cmd.run: - unless: echo cheese > {1} - '''.format(self.test_tmp_path, self.test_file)))) + """.format( + self.test_tmp_path, self.test_file + ) + ) + ) + ) - ret = self.run_function('state.sls', [self.state_name]) - self.assertTrue(ret[state_key]['result']) + ret = self.run_function("state.sls", [self.state_name]) + self.assertTrue(ret[state_key]["result"]) def test_run_unless_multiple_cmds(self): - ''' + """ test cmd.run using multiple unless options where the first cmd in the list will pass, but the second will fail. This tests the fix for issue #35384. (The fix is in PR #35545.) - ''' - sls = self.run_function('state.sls', mods='issue-35384') + """ + sls = self.run_function("state.sls", mods="issue-35384") self.assertSaltTrueReturn(sls) # We must assert against the comment here to make sure the comment reads that the # command "echo "hello"" was run. This ensures that we made it to the last unless # command in the state. If the comment reads "unless condition is true", or similar, # then the unless state run bailed out after the first unless command succeeded, # which is the bug we're regression testing for. - self.assertEqual(sls['cmd_|-cmd_run_unless_multiple_|-echo "hello"_|-run']['comment'], - 'Command "echo "hello"" run') + self.assertEqual( + sls['cmd_|-cmd_run_unless_multiple_|-echo "hello"_|-run']["comment"], + 'Command "echo "hello"" run', + ) def test_run_creates_exists(self): - ''' + """ test cmd.run creates already there - ''' - state_key = 'cmd_|-echo >> {0}_|-echo >> {0}_|-run'.format(self.test_file) - with salt.utils.files.fopen(self.state_file, 'w') as fb_: - fb_.write(salt.utils.stringutils.to_str(textwrap.dedent(''' + """ + state_key = "cmd_|-echo >> {0}_|-echo >> {0}_|-run".format(self.test_file) + with salt.utils.files.fopen(self.state_file, "w") as fb_: + fb_.write( + salt.utils.stringutils.to_str( + textwrap.dedent( + """ echo >> {0}: cmd.run: - creates: {0} - '''.format(self.test_file)))) + """.format( + self.test_file + ) + ) + ) + ) - ret = self.run_function('state.sls', [self.state_name]) - self.assertTrue(ret[state_key]['result']) - self.assertEqual(len(ret[state_key]['changes']), 0) + ret = self.run_function("state.sls", [self.state_name]) + self.assertTrue(ret[state_key]["result"]) + self.assertEqual(len(ret[state_key]["changes"]), 0) def test_run_creates_new(self): - ''' + """ test cmd.run creates not there - ''' + """ os.remove(self.test_file) - state_key = 'cmd_|-echo >> {0}_|-echo >> {0}_|-run'.format(self.test_file) - with salt.utils.files.fopen(self.state_file, 'w') as fb_: - fb_.write(salt.utils.stringutils.to_str(textwrap.dedent(''' + state_key = "cmd_|-echo >> {0}_|-echo >> {0}_|-run".format(self.test_file) + with salt.utils.files.fopen(self.state_file, "w") as fb_: + fb_.write( + salt.utils.stringutils.to_str( + textwrap.dedent( + """ echo >> {0}: cmd.run: - creates: {0} - '''.format(self.test_file)))) + """.format( + self.test_file + ) + ) + ) + ) - ret = self.run_function('state.sls', [self.state_name]) - self.assertTrue(ret[state_key]['result']) - self.assertEqual(len(ret[state_key]['changes']), 4) + ret = self.run_function("state.sls", [self.state_name]) + self.assertTrue(ret[state_key]["result"]) + self.assertEqual(len(ret[state_key]["changes"]), 4) def test_run_redirect(self): - ''' + """ test cmd.run with shell redirect - ''' - state_key = 'cmd_|-echo test > {0}_|-echo test > {0}_|-run'.format(self.test_file) - with salt.utils.files.fopen(self.state_file, 'w') as fb_: - fb_.write(salt.utils.stringutils.to_str(textwrap.dedent(''' + """ + state_key = "cmd_|-echo test > {0}_|-echo test > {0}_|-run".format( + self.test_file + ) + with salt.utils.files.fopen(self.state_file, "w") as fb_: + fb_.write( + salt.utils.stringutils.to_str( + textwrap.dedent( + """ echo test > {0}: cmd.run - '''.format(self.test_file)))) + """.format( + self.test_file + ) + ) + ) + ) - ret = self.run_function('state.sls', [self.state_name]) - self.assertTrue(ret[state_key]['result']) + ret = self.run_function("state.sls", [self.state_name]) + self.assertTrue(ret[state_key]["result"]) class CMDRunWatchTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the cmd state of run_watch - ''' + """ + def setUp(self): - self.state_name = 'run_watch' - state_filename = self.state_name + '.sls' + self.state_name = "run_watch" + state_filename = self.state_name + ".sls" self.state_file = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, state_filename) super(CMDRunWatchTest, self).setUp() @@ -203,14 +244,17 @@ class CMDRunWatchTest(ModuleCase, SaltReturnAssertsMixin): super(CMDRunWatchTest, self).tearDown() def test_run_watch(self): - ''' + """ test cmd.run watch - ''' - saltines_key = 'cmd_|-saltines_|-echo changed=true_|-run' - biscuits_key = 'cmd_|-biscuits_|-echo biscuits_|-wait' + """ + saltines_key = "cmd_|-saltines_|-echo changed=true_|-run" + biscuits_key = "cmd_|-biscuits_|-echo biscuits_|-wait" - with salt.utils.files.fopen(self.state_file, 'w') as fb_: - fb_.write(salt.utils.stringutils.to_str(textwrap.dedent(''' + with salt.utils.files.fopen(self.state_file, "w") as fb_: + fb_.write( + salt.utils.stringutils.to_str( + textwrap.dedent( + """ saltines: cmd.run: - name: echo changed=true @@ -223,8 +267,11 @@ class CMDRunWatchTest(ModuleCase, SaltReturnAssertsMixin): - cwd: / - watch: - cmd: saltines - '''))) + """ + ) + ) + ) - ret = self.run_function('state.sls', [self.state_name]) - self.assertTrue(ret[saltines_key]['result']) - self.assertTrue(ret[biscuits_key]['result']) + ret = self.run_function("state.sls", [self.state_name]) + self.assertTrue(ret[saltines_key]["result"]) + self.assertTrue(ret[biscuits_key]["result"]) diff --git a/tests/integration/states/test_compiler.py b/tests/integration/states/test_compiler.py index 31d35dec6e1..4fda01033a3 100644 --- a/tests/integration/states/test_compiler.py +++ b/tests/integration/states/test_compiler.py @@ -1,17 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" tests for host state -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.case import ModuleCase +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.platform +# Import Salt Testing libs +from tests.support.case import ModuleCase + # Import 3rd-Party libs HAS_LSB_RELEASE = True try: @@ -21,29 +21,31 @@ except ImportError: class CompileTest(ModuleCase): - ''' + """ Validate the state compiler - ''' + """ + def test_multi_state(self): - ''' + """ Test the error with multiple states of the same type - ''' - ret = self.run_function('state.sls', mods='fuzz.multi_state') + """ + ret = self.run_function("state.sls", mods="fuzz.multi_state") # Verify that the return is a list, aka, an error self.assertIsInstance(ret, list) def test_jinja_deep_error(self): - ''' + """ Test when we have an error in a execution module called by jinja - ''' + """ if salt.utils.platform.is_linux() and HAS_LSB_RELEASE: release = lsb_release.get_distro_information() - if release.get('ID') == 'Debian' and int(release.get('RELEASE', '0')[0]) < 9: - self.skipTest('This test is flaky on Debian 8. Skipping.') + if ( + release.get("ID") == "Debian" + and int(release.get("RELEASE", "0")[0]) < 9 + ): + self.skipTest("This test is flaky on Debian 8. Skipping.") - ret = self.run_function('state.sls', ['issue-10010']) - self.assertTrue( - ', in jinja_error' in ret[0].strip()) - self.assertTrue( - ret[0].strip().endswith('Exception: hehehe')) + ret = self.run_function("state.sls", ["issue-10010"]) + self.assertTrue(", in jinja_error" in ret[0].strip()) + self.assertTrue(ret[0].strip().endswith("Exception: hehehe")) diff --git a/tests/integration/states/test_cron.py b/tests/integration/states/test_cron.py index 6aec52536d7..d4bbe610543 100644 --- a/tests/integration/states/test_cron.py +++ b/tests/integration/states/test_cron.py @@ -1,55 +1,67 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the cron state -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging -log = logging.getLogger(__name__) +# Import Salt libs +import salt.utils.platform # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.unit import skipIf -# Import Salt libs -import salt.utils.platform +log = logging.getLogger(__name__) -@skipIf(salt.utils.platform.is_windows(), 'minion is windows') +@skipIf(salt.utils.platform.is_windows(), "minion is windows") class CronTest(ModuleCase): - ''' + """ Validate the file state - ''' + """ + def setUp(self): - ''' + """ Setup - ''' - self.run_state('user.present', name='test_cron_user') + """ + self.run_state("user.present", name="test_cron_user") def tearDown(self): - ''' + """ Teardown - ''' + """ # Remove cron file - self.run_function('cmd.run', - cmd='crontab -u test_cron_user -r') + self.run_function("cmd.run", cmd="crontab -u test_cron_user -r") # Delete user - self.run_state('user.absent', name='test_cron_user') + self.run_state("user.absent", name="test_cron_user") def test_managed(self): - ''' + """ file.managed - ''' + """ ret = self.run_state( - 'cron.file', - name='salt://issue-46881/cron', - user='test_cron_user' + "cron.file", name="salt://issue-46881/cron", user="test_cron_user" + ) + _expected = "--- \n+++ \n@@ -1 +1,2 @@\n-\n+# Lines below here are managed by Salt, do not edit\n+@hourly touch /tmp/test-file\n" + self.assertIn( + "changes", + ret["cron_|-salt://issue-46881/cron_|-salt://issue-46881/cron_|-file"], + ) + self.assertIn( + "diff", + ret["cron_|-salt://issue-46881/cron_|-salt://issue-46881/cron_|-file"][ + "changes" + ], + ) + self.assertEqual( + _expected, + ret["cron_|-salt://issue-46881/cron_|-salt://issue-46881/cron_|-file"][ + "changes" + ]["diff"], ) - _expected = '--- \n+++ \n@@ -1 +1,2 @@\n-\n+# Lines below here are managed by Salt, do not edit\n+@hourly touch /tmp/test-file\n' - self.assertIn('changes', ret['cron_|-salt://issue-46881/cron_|-salt://issue-46881/cron_|-file']) - self.assertIn('diff', ret['cron_|-salt://issue-46881/cron_|-salt://issue-46881/cron_|-file']['changes']) - self.assertEqual(_expected, ret['cron_|-salt://issue-46881/cron_|-salt://issue-46881/cron_|-file']['changes']['diff']) diff --git a/tests/integration/states/test_docker_container.py b/tests/integration/states/test_docker_container.py index f5d822dfe76..38c379b5e33 100644 --- a/tests/integration/states/test_docker_container.py +++ b/tests/integration/states/test_docker_container.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the docker_container states -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -13,23 +13,23 @@ import subprocess import sys import tempfile -# Import Salt Testing Libs -from tests.support.case import ModuleCase -from tests.support.docker import with_network, random_name -from tests.support.helpers import destructiveTest, with_tempdir -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import skipIf - # Import Salt Libs import salt.utils.files import salt.utils.network import salt.utils.path from salt.exceptions import CommandExecutionError -from salt.modules.config import DEFAULTS as _config_defaults # Import 3rd-party libs from salt.ext import six +from salt.modules.config import DEFAULTS as _config_defaults + +# Import Salt Testing Libs +from tests.support.case import ModuleCase +from tests.support.docker import random_name, with_network +from tests.support.helpers import destructiveTest, with_tempdir +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf log = logging.getLogger(__name__) @@ -37,53 +37,56 @@ IPV6_ENABLED = bool(salt.utils.network.ip_addrs6(include_loopback=True)) def container_name(func): - ''' + """ Generate a randomized name for a container and clean it up afterward - ''' + """ + @functools.wraps(func) def wrapper(self, *args, **kwargs): - name = random_name(prefix='salt_test_') + name = random_name(prefix="salt_test_") try: return func(self, name, *args, **kwargs) finally: try: - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) except CommandExecutionError as exc: - if 'No such container' not in exc.__str__(): + if "No such container" not in exc.__str__(): raise + return wrapper @destructiveTest -@skipIf(not salt.utils.path.which('busybox'), 'Busybox not installed') -@skipIf(not salt.utils.path.which('dockerd'), 'Docker not installed') +@skipIf(not salt.utils.path.which("busybox"), "Busybox not installed") +@skipIf(not salt.utils.path.which("dockerd"), "Docker not installed") class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Test docker_container states - ''' + """ + @classmethod def setUpClass(cls): - ''' - ''' + """ + """ # Create temp dir cls.image_build_rootdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Generate image name - cls.image = random_name(prefix='salt_busybox_') + cls.image = random_name(prefix="salt_busybox_") - script_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'mkimage-busybox-static') + script_path = os.path.join(RUNTIME_VARS.BASE_FILES, "mkimage-busybox-static") cmd = [script_path, cls.image_build_rootdir, cls.image] - log.debug('Running \'%s\' to build busybox image', ' '.join(cmd)) + log.debug("Running '%s' to build busybox image", " ".join(cmd)) process = subprocess.Popen( - cmd, - close_fds=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + cmd, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) output = process.communicate()[0] - log.debug('Output from mkimge-busybox-static:\n%s', output) + log.debug("Output from mkimge-busybox-static:\n%s", output) if process.returncode != 0: raise Exception( - 'Failed to build image. Output from mkimge-busybox-static:\n{}'.format(output) + "Failed to build image. Output from mkimge-busybox-static:\n{}".format( + output + ) ) try: @@ -94,131 +97,118 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): @classmethod def tearDownClass(cls): - cmd = ['docker', 'rmi', '--force', cls.image] - log.debug('Running \'%s\' to destroy busybox image', ' '.join(cmd)) + cmd = ["docker", "rmi", "--force", cls.image] + log.debug("Running '%s' to destroy busybox image", " ".join(cmd)) process = subprocess.Popen( - cmd, - close_fds=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + cmd, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) output = process.communicate()[0] - log.debug('Output from %s:\n%s', ' '.join(cmd), output) + log.debug("Output from %s:\n%s", " ".join(cmd), output) if process.returncode != 0: - raise Exception('Failed to destroy image') + raise Exception("Failed to destroy image") def run_state(self, function, **kwargs): ret = super(DockerContainerTestCase, self).run_state(function, **kwargs) - log.debug('ret = %s', ret) + log.debug("ret = %s", ret) return ret @with_tempdir() @container_name def test_running_with_no_predefined_volume(self, name, bind_dir_host): - ''' + """ This tests that a container created using the docker_container.running state, with binds defined, will also create the corresponding volumes if they aren't pre-defined in the image. - ''' + """ ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - binds=bind_dir_host + ':/foo', + binds=bind_dir_host + ":/foo", shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Now check to ensure that the container has volumes to match the # binds that we used when creating it. - ret = self.run_function('docker.inspect_container', [name]) - self.assertTrue('/foo' in ret['Config']['Volumes']) + ret = self.run_function("docker.inspect_container", [name]) + self.assertTrue("/foo" in ret["Config"]["Volumes"]) @container_name def test_running_with_no_predefined_ports(self, name): - ''' + """ This tests that a container created using the docker_container.running state, with port_bindings defined, will also configure the corresponding ports if they aren't pre-defined in the image. - ''' + """ ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - port_bindings='14505-14506:24505-24506,2123:2123/udp,8080', + port_bindings="14505-14506:24505-24506,2123:2123/udp,8080", shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Now check to ensure that the container has ports to match the # port_bindings that we used when creating it. - expected_ports = (4505, 4506, 8080, '2123/udp') - ret = self.run_function('docker.inspect_container', [name]) - self.assertTrue(x in ret['NetworkSettings']['Ports'] - for x in expected_ports) + expected_ports = (4505, 4506, 8080, "2123/udp") + ret = self.run_function("docker.inspect_container", [name]) + self.assertTrue(x in ret["NetworkSettings"]["Ports"] for x in expected_ports) @container_name def test_running_updated_image_id(self, name): - ''' + """ This tests the case of an image being changed after the container is created. The next time the state is run, the container should be replaced because the image ID is now different. - ''' + """ # Create and start a container ret = self.run_state( - 'docker_container.running', - name=name, - image=self.image, - shutdown_timeout=1, + "docker_container.running", name=name, image=self.image, shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Get the container's info - c_info = self.run_function('docker.inspect_container', [name]) - c_name, c_id = (c_info[x] for x in ('Name', 'Id')) + c_info = self.run_function("docker.inspect_container", [name]) + c_name, c_id = (c_info[x] for x in ("Name", "Id")) # Alter the filesystem inside the container self.assertEqual( - self.run_function('docker.retcode', [name, 'touch /.salttest']), - 0 + self.run_function("docker.retcode", [name, "touch /.salttest"]), 0 ) # Commit the changes and overwrite the test class' image - self.run_function('docker.commit', [c_id, self.image]) + self.run_function("docker.commit", [c_id, self.image]) # Re-run the state ret = self.run_state( - 'docker_container.running', - name=name, - image=self.image, - shutdown_timeout=1, + "docker_container.running", name=name, image=self.image, shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Discard the outer dict with the state compiler data to make below # asserts easier to read/write ret = ret[next(iter(ret))] # Check to make sure that the container was replaced - self.assertTrue('container_id' in ret['changes']) + self.assertTrue("container_id" in ret["changes"]) # Check to make sure that the image is in the changes dict, since # it should have changed - self.assertTrue('image' in ret['changes']) + self.assertTrue("image" in ret["changes"]) # Check that the comment in the state return states that # container's image has changed - self.assertTrue('Container has a new image' in ret['comment']) + self.assertTrue("Container has a new image" in ret["comment"]) @container_name def test_running_start_false_without_replace(self, name): - ''' + """ Test that we do not start a container which is stopped, when it is not being replaced. - ''' + """ # Create a container ret = self.run_state( - 'docker_container.running', - name=name, - image=self.image, - shutdown_timeout=1, + "docker_container.running", name=name, image=self.image, shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Stop the container - self.run_function('docker.stop', [name], force=True) + self.run_function("docker.stop", [name], force=True) # Re-run the state with start=False ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, start=False, @@ -229,61 +219,58 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): # asserts easier to read/write ret = ret[next(iter(ret))] # Check to make sure that the container was not replaced - self.assertTrue('container_id' not in ret['changes']) + self.assertTrue("container_id" not in ret["changes"]) # Check to make sure that the state is not the changes dict, since # it should not have changed - self.assertTrue('state' not in ret['changes']) + self.assertTrue("state" not in ret["changes"]) - @with_network(subnet='10.247.197.96/27', create=True) + @with_network(subnet="10.247.197.96/27", create=True) @container_name def test_running_no_changes_hostname_network(self, container_name, net): - ''' + """ Test that changes are not detected when a hostname is specified for a container on a custom network - ''' + """ # Create a container kwargs = { - 'name': container_name, - 'image': self.image, - 'shutdown_timeout': 1, - 'network_mode': net.name, - 'networks': [net.name], - 'hostname': 'foo' + "name": container_name, + "image": self.image, + "shutdown_timeout": 1, + "network_mode": net.name, + "networks": [net.name], + "hostname": "foo", } - ret = self.run_state('docker_container.running', **kwargs) + ret = self.run_state("docker_container.running", **kwargs) self.assertSaltTrueReturn(ret) - ret = self.run_state('docker_container.running', **kwargs) + ret = self.run_state("docker_container.running", **kwargs) self.assertSaltTrueReturn(ret) # Discard the outer dict with the state compiler data to make below # asserts easier to read/write ret = ret[next(iter(ret))] # Should be no changes - self.assertFalse(ret['changes']) + self.assertFalse(ret["changes"]) @container_name def test_running_start_false_with_replace(self, name): - ''' + """ Test that we do start a container which was previously stopped, even though start=False, because the container was replaced. - ''' + """ # Create a container ret = self.run_state( - 'docker_container.running', - name=name, - image=self.image, - shutdown_timeout=1, + "docker_container.running", name=name, image=self.image, shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Stop the container - self.run_function('docker.stop', [name], force=True) + self.run_function("docker.stop", [name], force=True) # Re-run the state with start=False but also change the command to # trigger the container being replaced. ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - command='sleep 600', + command="sleep 600", start=False, shutdown_timeout=1, ) @@ -292,30 +279,27 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): # asserts easier to read/write ret = ret[next(iter(ret))] # Check to make sure that the container was not replaced - self.assertTrue('container_id' in ret['changes']) + self.assertTrue("container_id" in ret["changes"]) # Check to make sure that the state is not the changes dict, since # it should not have changed - self.assertTrue('state' not in ret['changes']) + self.assertTrue("state" not in ret["changes"]) @container_name def test_running_start_true(self, name): - ''' + """ This tests that we *do* start a container that is stopped, when the "start" argument is set to True. - ''' + """ # Create a container ret = self.run_state( - 'docker_container.running', - name=name, - image=self.image, - shutdown_timeout=1, + "docker_container.running", name=name, image=self.image, shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Stop the container - self.run_function('docker.stop', [name], force=True) + self.run_function("docker.stop", [name], force=True) # Re-run the state with start=True ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, start=True, @@ -326,27 +310,26 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): # asserts easier to read/write ret = ret[next(iter(ret))] # Check to make sure that the container was not replaced - self.assertTrue('container_id' not in ret['changes']) + self.assertTrue("container_id" not in ret["changes"]) # Check to make sure that the state is in the changes dict, since # it should have changed - self.assertTrue('state' in ret['changes']) + self.assertTrue("state" in ret["changes"]) # Check that the comment in the state return states that # container's state has changed - self.assertTrue( - "State changed from 'stopped' to 'running'" in ret['comment']) + self.assertTrue("State changed from 'stopped' to 'running'" in ret["comment"]) @container_name def test_running_with_invalid_input(self, name): - ''' + """ This tests that the input tranlation code identifies invalid input and includes information about that invalid argument in the state return. - ''' + """ # Try to create a container with invalid input ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - ulimits='nofile:2048', + ulimits="nofile:2048", shutdown_timeout=1, ) self.assertSaltFalseReturn(ret) @@ -354,28 +337,28 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): # asserts easier to read/write ret = ret[next(iter(ret))] # Check to make sure that the container was not created - self.assertTrue('container_id' not in ret['changes']) + self.assertTrue("container_id" not in ret["changes"]) # Check that the error message about the invalid argument is # included in the comment for the state self.assertTrue( - 'Ulimit definition \'nofile:2048\' is not in the format ' - 'type=soft_limit[:hard_limit]' in ret['comment'] + "Ulimit definition 'nofile:2048' is not in the format " + "type=soft_limit[:hard_limit]" in ret["comment"] ) @container_name def test_running_with_argument_collision(self, name): - ''' + """ this tests that the input tranlation code identifies an argument collision (API args and their aliases being simultaneously used) and includes information about them in the state return. - ''' + """ # try to create a container with invalid input ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - ulimits='nofile=2048', - ulimit='nofile=1024:2048', + ulimits="nofile=2048", + ulimit="nofile=1024:2048", shutdown_timeout=1, ) self.assertSaltFalseReturn(ret) @@ -383,27 +366,26 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): # asserts easier to read/write ret = ret[next(iter(ret))] # Check to make sure that the container was not created - self.assertTrue('container_id' not in ret['changes']) + self.assertTrue("container_id" not in ret["changes"]) # Check that the error message about the collision is included in # the comment for the state - self.assertTrue( - '\'ulimit\' is an alias for \'ulimits\'' in ret['comment']) + self.assertTrue("'ulimit' is an alias for 'ulimits'" in ret["comment"]) @container_name def test_running_with_ignore_collisions(self, name): - ''' + """ This tests that the input tranlation code identifies an argument collision (API args and their aliases being simultaneously used) includes information about them in the state return. - ''' + """ # try to create a container with invalid input ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, ignore_collisions=True, - ulimits='nofile=2048', - ulimit='nofile=1024:2048', + ulimits="nofile=2048", + ulimit="nofile=1024:2048", shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) @@ -411,39 +393,36 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): # asserts easier to read/write ret = ret[next(iter(ret))] # Check to make sure that the container was created - self.assertTrue('container_id' in ret['changes']) + self.assertTrue("container_id" in ret["changes"]) # Check that the value from the API argument was one that was used # to create the container - c_info = self.run_function('docker.inspect_container', [name]) - actual = c_info['HostConfig']['Ulimits'] - expected = [{'Name': 'nofile', 'Soft': 2048, 'Hard': 2048}] + c_info = self.run_function("docker.inspect_container", [name]) + actual = c_info["HostConfig"]["Ulimits"] + expected = [{"Name": "nofile", "Soft": 2048, "Hard": 2048}] self.assertEqual(actual, expected) @container_name def test_running_with_removed_argument(self, name): - ''' + """ This tests that removing an argument from a created container will be detected and result in the container being replaced. It also tests that we revert back to the value from the image. This way, when the "command" argument is removed, we confirm that we are reverting back to the image's command. - ''' + """ # Create the container ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - command='sleep 600', + command="sleep 600", shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Run the state again with the "command" argument removed ret = self.run_state( - 'docker_container.running', - name=name, - image=self.image, - shutdown_timeout=1, + "docker_container.running", name=name, image=self.image, shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Discard the outer dict with the state compiler data to make below @@ -451,31 +430,31 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): ret = ret[next(iter(ret))] # Now check to ensure that the changes include the command # reverting back to the image's command. - image_info = self.run_function('docker.inspect_image', [self.image]) + image_info = self.run_function("docker.inspect_image", [self.image]) self.assertEqual( - ret['changes']['container']['Config']['Cmd']['new'], - image_info['Config']['Cmd'] + ret["changes"]["container"]["Config"]["Cmd"]["new"], + image_info["Config"]["Cmd"], ) @container_name def test_running_with_port_bindings(self, name): - ''' + """ This tests that the ports which are being bound are also exposed, even when not explicitly configured. This test will create a container with only some of the ports exposed, including some which aren't even bound. The resulting containers exposed ports should contain all of the ports defined in the "ports" argument, as well as each of the ports which are being bound. - ''' + """ # Create the container ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - command='sleep 600', + command="sleep 600", shutdown_timeout=1, - port_bindings=[1234, '1235-1236', '2234/udp', '2235-2236/udp'], - ports=[1235, '2235/udp', 9999], + port_bindings=[1234, "1235-1236", "2234/udp", "2235-2236/udp"], + ports=[1235, "2235/udp", 9999], ) self.assertSaltTrueReturn(ret) @@ -483,516 +462,487 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): # port bindings should only contain the ports defined in the # port_bindings argument, while the exposed ports should also contain # the extra port (9999/tcp) which was included in the ports argument. - cinfo = self.run_function('docker.inspect_container', [name]) - ports = ['1234/tcp', '1235/tcp', '1236/tcp', - '2234/udp', '2235/udp', '2236/udp'] - self.assertEqual( - sorted(cinfo['HostConfig']['PortBindings']), - ports - ) - self.assertEqual( - sorted(cinfo['Config']['ExposedPorts']), - ports + ['9999/tcp'] - ) + cinfo = self.run_function("docker.inspect_container", [name]) + ports = ["1234/tcp", "1235/tcp", "1236/tcp", "2234/udp", "2235/udp", "2236/udp"] + self.assertEqual(sorted(cinfo["HostConfig"]["PortBindings"]), ports) + self.assertEqual(sorted(cinfo["Config"]["ExposedPorts"]), ports + ["9999/tcp"]) @container_name def test_absent_with_stopped_container(self, name): - ''' + """ This tests the docker_container.absent state on a stopped container - ''' + """ # Create the container - self.run_function('docker.create', [self.image], name=name) + self.run_function("docker.create", [self.image], name=name) # Remove the container - ret = self.run_state( - 'docker_container.absent', - name=name, - ) + ret = self.run_state("docker_container.absent", name=name,) self.assertSaltTrueReturn(ret) # Discard the outer dict with the state compiler data to make below # asserts easier to read/write ret = ret[next(iter(ret))] # Check that we have a removed container ID in the changes dict - self.assertTrue('removed' in ret['changes']) + self.assertTrue("removed" in ret["changes"]) # Run the state again to confirm it changes nothing - ret = self.run_state( - 'docker_container.absent', - name=name, - ) + ret = self.run_state("docker_container.absent", name=name,) self.assertSaltTrueReturn(ret) # Discard the outer dict with the state compiler data to make below # asserts easier to read/write ret = ret[next(iter(ret))] # Nothing should have changed - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["changes"], {}) # Ensure that the comment field says the container does not exist - self.assertEqual( - ret['comment'], - 'Container \'{0}\' does not exist'.format(name) - ) + self.assertEqual(ret["comment"], "Container '{0}' does not exist".format(name)) @container_name def test_absent_with_running_container(self, name): - ''' + """ This tests the docker_container.absent state and - ''' + """ # Create the container ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - command='sleep 600', + command="sleep 600", shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) # Try to remove the container. This should fail because force=True # is needed to remove a container that is running. - ret = self.run_state( - 'docker_container.absent', - name=name, - ) + ret = self.run_state("docker_container.absent", name=name,) self.assertSaltFalseReturn(ret) # Discard the outer dict with the state compiler data to make below # asserts easier to read/write ret = ret[next(iter(ret))] # Nothing should have changed - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["changes"], {}) # Ensure that the comment states that force=True is required self.assertEqual( - ret['comment'], - 'Container is running, set force to True to forcibly remove it' + ret["comment"], + "Container is running, set force to True to forcibly remove it", ) # Try again with force=True. This should succeed. - ret = self.run_state('docker_container.absent', - name=name, - force=True, - ) + ret = self.run_state("docker_container.absent", name=name, force=True,) self.assertSaltTrueReturn(ret) # Discard the outer dict with the state compiler data to make below # asserts easier to read/write ret = ret[next(iter(ret))] # Check that we have a removed container ID in the changes dict - self.assertTrue('removed' in ret['changes']) + self.assertTrue("removed" in ret["changes"]) # The comment should mention that the container was removed self.assertEqual( - ret['comment'], - 'Forcibly removed container \'{0}\''.format(name) + ret["comment"], "Forcibly removed container '{0}'".format(name) ) @container_name def test_running_image_name(self, name): - ''' + """ Ensure that we create the container using the image name instead of ID - ''' + """ ret = self.run_state( - 'docker_container.running', - name=name, - image=self.image, - shutdown_timeout=1, + "docker_container.running", name=name, image=self.image, shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) - ret = self.run_function('docker.inspect_container', [name]) - self.assertEqual(ret['Config']['Image'], self.image) + ret = self.run_function("docker.inspect_container", [name]) + self.assertEqual(ret["Config"]["Image"], self.image) @container_name def test_env_with_running_container(self, name): - ''' + """ docker_container.running environnment part. Testing issue 39838. - ''' + """ ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - env='VAR1=value1,VAR2=value2,VAR3=value3', + env="VAR1=value1,VAR2=value2,VAR3=value3", shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) - ret = self.run_function('docker.inspect_container', [name]) - self.assertTrue('VAR1=value1' in ret['Config']['Env']) - self.assertTrue('VAR2=value2' in ret['Config']['Env']) - self.assertTrue('VAR3=value3' in ret['Config']['Env']) + ret = self.run_function("docker.inspect_container", [name]) + self.assertTrue("VAR1=value1" in ret["Config"]["Env"]) + self.assertTrue("VAR2=value2" in ret["Config"]["Env"]) + self.assertTrue("VAR3=value3" in ret["Config"]["Env"]) ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=name, image=self.image, - env='VAR1=value1,VAR2=value2', + env="VAR1=value1,VAR2=value2", shutdown_timeout=1, ) self.assertSaltTrueReturn(ret) - ret = self.run_function('docker.inspect_container', [name]) - self.assertTrue('VAR1=value1' in ret['Config']['Env']) - self.assertTrue('VAR2=value2' in ret['Config']['Env']) - self.assertTrue('VAR3=value3' not in ret['Config']['Env']) + ret = self.run_function("docker.inspect_container", [name]) + self.assertTrue("VAR1=value1" in ret["Config"]["Env"]) + self.assertTrue("VAR2=value2" in ret["Config"]["Env"]) + self.assertTrue("VAR3=value3" not in ret["Config"]["Env"]) - @with_network(subnet='10.247.197.96/27', create=True) + @with_network(subnet="10.247.197.96/27", create=True) @container_name def test_static_ip_one_network(self, container_name, net): - ''' + """ Ensure that if a network is created and specified as network_mode, that is the only network, and the static IP is applied. - ''' - requested_ip = '10.247.197.100' + """ + requested_ip = "10.247.197.100" kwargs = { - 'name': container_name, - 'image': self.image, - 'network_mode': net.name, - 'networks': [{net.name: [{'ipv4_address': requested_ip}]}], - 'shutdown_timeout': 1, + "name": container_name, + "image": self.image, + "network_mode": net.name, + "networks": [{net.name: [{"ipv4_address": requested_ip}]}], + "shutdown_timeout": 1, } # Create a container - ret = self.run_state('docker_container.running', **kwargs) + ret = self.run_state("docker_container.running", **kwargs) self.assertSaltTrueReturn(ret) - inspect_result = self.run_function('docker.inspect_container', - [container_name]) - connected_networks = inspect_result['NetworkSettings']['Networks'] + inspect_result = self.run_function("docker.inspect_container", [container_name]) + connected_networks = inspect_result["NetworkSettings"]["Networks"] self.assertEqual(list(connected_networks.keys()), [net.name]) - self.assertEqual(inspect_result['HostConfig']['NetworkMode'], net.name) - self.assertEqual(connected_networks[net.name]['IPAMConfig']['IPv4Address'], requested_ip) + self.assertEqual(inspect_result["HostConfig"]["NetworkMode"], net.name) + self.assertEqual( + connected_networks[net.name]["IPAMConfig"]["IPv4Address"], requested_ip + ) def _test_running(self, container_name, *nets): - ''' + """ DRY function for testing static IPs - ''' + """ networks = [] for net in nets: - net_def = { - net.name: [ - {net.ip_arg: net[0]} - ] - } + net_def = {net.name: [{net.ip_arg: net[0]}]} networks.append(net_def) kwargs = { - 'name': container_name, - 'image': self.image, - 'networks': networks, - 'shutdown_timeout': 1, + "name": container_name, + "image": self.image, + "networks": networks, + "shutdown_timeout": 1, } # Create a container - ret = self.run_state('docker_container.running', **kwargs) + ret = self.run_state("docker_container.running", **kwargs) self.assertSaltTrueReturn(ret) - inspect_result = self.run_function('docker.inspect_container', - [container_name]) - connected_networks = inspect_result['NetworkSettings']['Networks'] + inspect_result = self.run_function("docker.inspect_container", [container_name]) + connected_networks = inspect_result["NetworkSettings"]["Networks"] # Check that the correct IP was set try: for net in nets: self.assertEqual( - connected_networks[net.name]['IPAMConfig'][net.arg_map(net.ip_arg)], - net[0] + connected_networks[net.name]["IPAMConfig"][net.arg_map(net.ip_arg)], + net[0], ) except KeyError: # Fail with a meaningful error msg = ( - 'Container does not have the expected network config for ' - 'network {0}'.format(net.name) + "Container does not have the expected network config for " + "network {0}".format(net.name) ) log.error(msg) - log.error('Connected networks: %s', connected_networks) - self.fail('{0}. See log for more information.'.format(msg)) + log.error("Connected networks: %s", connected_networks) + self.fail("{0}. See log for more information.".format(msg)) # Check that container continued running and didn't immediately exit - self.assertTrue(inspect_result['State']['Running']) + self.assertTrue(inspect_result["State"]["Running"]) # Update the SLS configuration to use the second random IP so that we # can test updating a container's network configuration without # replacing the container. for idx, net in enumerate(nets): - kwargs['networks'][idx][net.name][0][net.ip_arg] = net[1] - ret = self.run_state('docker_container.running', **kwargs) + kwargs["networks"][idx][net.name][0][net.ip_arg] = net[1] + ret = self.run_state("docker_container.running", **kwargs) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - expected = {'container': {'Networks': {}}} + expected = {"container": {"Networks": {}}} for net in nets: - expected['container']['Networks'][net.name] = { - 'IPAMConfig': { - 'old': {net.arg_map(net.ip_arg): net[0]}, - 'new': {net.arg_map(net.ip_arg): net[1]}, + expected["container"]["Networks"][net.name] = { + "IPAMConfig": { + "old": {net.arg_map(net.ip_arg): net[0]}, + "new": {net.arg_map(net.ip_arg): net[1]}, } } - self.assertEqual(ret['changes'], expected) + self.assertEqual(ret["changes"], expected) expected = [ - "Container '{0}' is already configured as specified.".format( - container_name - ) + "Container '{0}' is already configured as specified.".format(container_name) ] - expected.extend([ - "Reconnected to network '{0}' with updated configuration.".format( - x.name - ) - for x in sorted(nets, key=lambda y: y.name) - ]) - expected = ' '.join(expected) - self.assertEqual(ret['comment'], expected) + expected.extend( + [ + "Reconnected to network '{0}' with updated configuration.".format( + x.name + ) + for x in sorted(nets, key=lambda y: y.name) + ] + ) + expected = " ".join(expected) + self.assertEqual(ret["comment"], expected) # Update the SLS configuration to remove the last network - kwargs['networks'].pop(-1) - ret = self.run_state('docker_container.running', **kwargs) + kwargs["networks"].pop(-1) + ret = self.run_state("docker_container.running", **kwargs) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] expected = { - 'container': { - 'Networks': { + "container": { + "Networks": { nets[-1].name: { - 'IPAMConfig': { - 'old': { - nets[-1].arg_map(nets[-1].ip_arg): nets[-1][1] - }, - 'new': None, + "IPAMConfig": { + "old": {nets[-1].arg_map(nets[-1].ip_arg): nets[-1][1]}, + "new": None, } } } } } - self.assertEqual(ret['changes'], expected) + self.assertEqual(ret["changes"], expected) expected = ( "Container '{0}' is already configured as specified. Disconnected " "from network '{1}'.".format(container_name, nets[-1].name) ) - self.assertEqual(ret['comment'], expected) + self.assertEqual(ret["comment"], expected) # Update the SLS configuration to add back the last network, only use # an automatic IP instead of static IP. - kwargs['networks'].append(nets[-1].name) - ret = self.run_state('docker_container.running', **kwargs) + kwargs["networks"].append(nets[-1].name) + ret = self.run_state("docker_container.running", **kwargs) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] # Get the automatic IP by inspecting the container, and use it to build # the expected changes. - container_netinfo = self.run_function( - 'docker.inspect_container', - [container_name]).get('NetworkSettings', {}).get('Networks', {})[nets[-1].name] - autoip_keys = _config_defaults['docker.compare_container_networks']['automatic'] + container_netinfo = ( + self.run_function("docker.inspect_container", [container_name]) + .get("NetworkSettings", {}) + .get("Networks", {})[nets[-1].name] + ) + autoip_keys = _config_defaults["docker.compare_container_networks"]["automatic"] autoip_config = { - x: y for x, y in six.iteritems(container_netinfo) - if x in autoip_keys and y + x: y for x, y in six.iteritems(container_netinfo) if x in autoip_keys and y } - expected = {'container': {'Networks': {nets[-1].name: {}}}} + expected = {"container": {"Networks": {nets[-1].name: {}}}} for key, val in six.iteritems(autoip_config): - expected['container']['Networks'][nets[-1].name][key] = { - 'old': None, 'new': val + expected["container"]["Networks"][nets[-1].name][key] = { + "old": None, + "new": val, } - self.assertEqual(ret['changes'], expected) + self.assertEqual(ret["changes"], expected) expected = ( "Container '{0}' is already configured as specified. Connected " "to network '{1}'.".format(container_name, nets[-1].name) ) - self.assertEqual(ret['comment'], expected) + self.assertEqual(ret["comment"], expected) # Update the SLS configuration to remove the last network - kwargs['networks'].pop(-1) - ret = self.run_state('docker_container.running', **kwargs) + kwargs["networks"].pop(-1) + ret = self.run_state("docker_container.running", **kwargs) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - expected = {'container': {'Networks': {nets[-1].name: {}}}} + expected = {"container": {"Networks": {nets[-1].name: {}}}} for key, val in six.iteritems(autoip_config): - expected['container']['Networks'][nets[-1].name][key] = { - 'old': val, 'new': None + expected["container"]["Networks"][nets[-1].name][key] = { + "old": val, + "new": None, } - self.assertEqual(ret['changes'], expected) + self.assertEqual(ret["changes"], expected) expected = ( "Container '{0}' is already configured as specified. Disconnected " "from network '{1}'.".format(container_name, nets[-1].name) ) - self.assertEqual(ret['comment'], expected) + self.assertEqual(ret["comment"], expected) - @with_network(subnet='10.247.197.96/27', create=True) + @with_network(subnet="10.247.197.96/27", create=True) @container_name def test_running_ipv4(self, container_name, *nets): self._test_running(container_name, *nets) - @with_network(subnet='10.247.197.128/27', create=True) - @with_network(subnet='10.247.197.96/27', create=True) + @with_network(subnet="10.247.197.128/27", create=True) + @with_network(subnet="10.247.197.96/27", create=True) @container_name def test_running_dual_ipv4(self, container_name, *nets): self._test_running(container_name, *nets) - @with_network(subnet='fe3f:2180:26:1::/123', create=True) + @with_network(subnet="fe3f:2180:26:1::/123", create=True) @container_name - @skipIf(not IPV6_ENABLED, 'IPv6 not enabled') + @skipIf(not IPV6_ENABLED, "IPv6 not enabled") def test_running_ipv6(self, container_name, *nets): self._test_running(container_name, *nets) - @with_network(subnet='fe3f:2180:26:1::20/123', create=True) - @with_network(subnet='fe3f:2180:26:1::/123', create=True) + @with_network(subnet="fe3f:2180:26:1::20/123", create=True) + @with_network(subnet="fe3f:2180:26:1::/123", create=True) @container_name - @skipIf(not IPV6_ENABLED, 'IPv6 not enabled') + @skipIf(not IPV6_ENABLED, "IPv6 not enabled") def test_running_dual_ipv6(self, container_name, *nets): self._test_running(container_name, *nets) - @with_network(subnet='fe3f:2180:26:1::/123', create=True) - @with_network(subnet='10.247.197.96/27', create=True) + @with_network(subnet="fe3f:2180:26:1::/123", create=True) + @with_network(subnet="10.247.197.96/27", create=True) @container_name - @skipIf(not IPV6_ENABLED, 'IPv6 not enabled') + @skipIf(not IPV6_ENABLED, "IPv6 not enabled") def test_running_mixed_ipv4_and_ipv6(self, container_name, *nets): self._test_running(container_name, *nets) - @with_network(subnet='10.247.197.96/27', create=True) + @with_network(subnet="10.247.197.96/27", create=True) @container_name def test_running_explicit_networks(self, container_name, net): - ''' + """ Ensure that if we use an explicit network configuration, we remove any default networks not specified (e.g. the default "bridge" network). - ''' + """ # Create a container with no specific network configuration. The only # networks connected will be the default ones. ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=container_name, image=self.image, - shutdown_timeout=1) + shutdown_timeout=1, + ) self.assertSaltTrueReturn(ret) - inspect_result = self.run_function('docker.inspect_container', - [container_name]) + inspect_result = self.run_function("docker.inspect_container", [container_name]) # Get the default network names - default_networks = list(inspect_result['NetworkSettings']['Networks']) + default_networks = list(inspect_result["NetworkSettings"]["Networks"]) # Re-run the state with an explicit network configuration. All of the # default networks should be disconnected. ret = self.run_state( - 'docker_container.running', + "docker_container.running", name=container_name, image=self.image, networks=[net.name], - shutdown_timeout=1) + shutdown_timeout=1, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - net_changes = ret['changes']['container']['Networks'] + net_changes = ret["changes"]["container"]["Networks"] self.assertIn( "Container '{0}' is already configured as specified.".format( container_name ), - ret['comment'] + ret["comment"], ) updated_networks = self.run_function( - 'docker.inspect_container', - [container_name])['NetworkSettings']['Networks'] + "docker.inspect_container", [container_name] + )["NetworkSettings"]["Networks"] for default_network in default_networks: self.assertIn( "Disconnected from network '{0}'.".format(default_network), - ret['comment'] + ret["comment"], ) self.assertIn(default_network, net_changes) # We've tested that the state return is correct, but let's be extra # paranoid and check the actual connected networks. self.assertNotIn(default_network, updated_networks) - self.assertIn( - "Connected to network '{0}'.".format(net.name), - ret['comment'] - ) + self.assertIn("Connected to network '{0}'.".format(net.name), ret["comment"]) @container_name def test_run_with_onlyif(self, name): - ''' + """ Test docker_container.run with onlyif. The container should not run (and the state should return a True result) if the onlyif has a nonzero return code, but if the onlyif has a zero return code the container should run. - ''' - for cmd in ('/bin/false', ['/bin/true', '/bin/false']): - log.debug('Trying %s', cmd) + """ + for cmd in ("/bin/false", ["/bin/true", "/bin/false"]): + log.debug("Trying %s", cmd) ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', - onlyif=cmd) + command="whoami", + onlyif=cmd, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertFalse(ret['changes']) + self.assertFalse(ret["changes"]) self.assertTrue( - ret['comment'].startswith( - 'onlyif command /bin/false returned exit code of' + ret["comment"].startswith( + "onlyif command /bin/false returned exit code of" ) ) - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) - for cmd in ('/bin/true', ['/bin/true', 'ls /']): - log.debug('Trying %s', cmd) + for cmd in ("/bin/true", ["/bin/true", "ls /"]): + log.debug("Trying %s", cmd) ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', - onlyif=cmd) + command="whoami", + onlyif=cmd, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes']['Logs'], 'root\n') + self.assertEqual(ret["changes"]["Logs"], "root\n") self.assertEqual( - ret['comment'], - 'Container ran and exited with a return code of 0' + ret["comment"], "Container ran and exited with a return code of 0" ) - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) @container_name def test_run_with_unless(self, name): - ''' + """ Test docker_container.run with unless. The container should not run (and the state should return a True result) if the unless has a zero return code, but if the unless has a nonzero return code the container should run. - ''' - for cmd in ('/bin/true', ['/bin/false', '/bin/true']): - log.debug('Trying %s', cmd) + """ + for cmd in ("/bin/true", ["/bin/false", "/bin/true"]): + log.debug("Trying %s", cmd) ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', - unless=cmd) + command="whoami", + unless=cmd, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertFalse(ret['changes']) + self.assertFalse(ret["changes"]) self.assertEqual( - ret['comment'], - 'unless command /bin/true returned exit code of 0' + ret["comment"], "unless command /bin/true returned exit code of 0" ) - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) - for cmd in ('/bin/false', ['/bin/false', 'ls /paththatdoesnotexist']): - log.debug('Trying %s', cmd) + for cmd in ("/bin/false", ["/bin/false", "ls /paththatdoesnotexist"]): + log.debug("Trying %s", cmd) ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', - unless=cmd) + command="whoami", + unless=cmd, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes']['Logs'], 'root\n') + self.assertEqual(ret["changes"]["Logs"], "root\n") self.assertEqual( - ret['comment'], - 'Container ran and exited with a return code of 0' + ret["comment"], "Container ran and exited with a return code of 0" ) - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) @container_name def test_run_with_creates(self, name): - ''' + """ Test docker_container.run with creates. The container should not run (and the state should return a True result) if all of the files exist, but if if any of the files do not exist the container should run. - ''' + """ + def _mkstemp(): fd, ret = tempfile.mkstemp() try: @@ -1004,150 +954,146 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): self.addCleanup(os.remove, ret) return ret - bad_file = '/tmp/filethatdoesnotexist' + bad_file = "/tmp/filethatdoesnotexist" good_file1 = _mkstemp() good_file2 = _mkstemp() for path in (good_file1, [good_file1, good_file2]): - log.debug('Trying %s', path) + log.debug("Trying %s", path) ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', - creates=path) + command="whoami", + creates=path, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertFalse(ret['changes']) + self.assertFalse(ret["changes"]) self.assertEqual( - ret['comment'], - 'All specified paths in \'creates\' argument exist' + ret["comment"], "All specified paths in 'creates' argument exist" ) - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) for path in (bad_file, [good_file1, bad_file]): - log.debug('Trying %s', path) + log.debug("Trying %s", path) ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', - creates=path) + command="whoami", + creates=path, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes']['Logs'], 'root\n') + self.assertEqual(ret["changes"]["Logs"], "root\n") self.assertEqual( - ret['comment'], - 'Container ran and exited with a return code of 0' + ret["comment"], "Container ran and exited with a return code of 0" ) - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) @container_name def test_run_replace(self, name): - ''' + """ Test the replace and force arguments to make sure they work properly - ''' + """ # Run once to create the container ret = self.run_state( - 'docker_container.run', - name=name, - image=self.image, - command='whoami') + "docker_container.run", name=name, image=self.image, command="whoami" + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes']['Logs'], 'root\n') + self.assertEqual(ret["changes"]["Logs"], "root\n") self.assertEqual( - ret['comment'], - 'Container ran and exited with a return code of 0' + ret["comment"], "Container ran and exited with a return code of 0" ) # Run again with replace=False, this should fail ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', - replace=False) + command="whoami", + replace=False, + ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertFalse(ret['changes']) + self.assertFalse(ret["changes"]) self.assertEqual( - ret['comment'], - 'Encountered error running container: Container \'{0}\' exists. ' - 'Run with replace=True to remove the existing container'.format(name) + ret["comment"], + "Encountered error running container: Container '{0}' exists. " + "Run with replace=True to remove the existing container".format(name), ) # Run again with replace=True, this should proceed and there should be # a "Replaces" key in the changes dict to show that a container was # replaced. ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', - replace=True) + command="whoami", + replace=True, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes']['Logs'], 'root\n') - self.assertTrue('Replaces' in ret['changes']) + self.assertEqual(ret["changes"]["Logs"], "root\n") + self.assertTrue("Replaces" in ret["changes"]) self.assertEqual( - ret['comment'], - 'Container ran and exited with a return code of 0' + ret["comment"], "Container ran and exited with a return code of 0" ) @container_name def test_run_force(self, name): - ''' + """ Test the replace and force arguments to make sure they work properly - ''' + """ # Start up a container that will stay running - ret = self.run_state( - 'docker_container.running', - name=name, - image=self.image) + ret = self.run_state("docker_container.running", name=name, image=self.image) self.assertSaltTrueReturn(ret) # Run again with replace=True, this should fail because the container # is still running ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', + command="whoami", replace=True, - force=False) + force=False, + ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertFalse(ret['changes']) + self.assertFalse(ret["changes"]) self.assertEqual( - ret['comment'], - 'Encountered error running container: Container \'{0}\' exists ' - 'and is running. Run with replace=True and force=True to force ' - 'removal of the existing container.'.format(name) + ret["comment"], + "Encountered error running container: Container '{0}' exists " + "and is running. Run with replace=True and force=True to force " + "removal of the existing container.".format(name), ) # Run again with replace=True and force=True, this should proceed and # there should be a "Replaces" key in the changes dict to show that a # container was replaced. ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='whoami', + command="whoami", replace=True, - force=True) + force=True, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes']['Logs'], 'root\n') - self.assertTrue('Replaces' in ret['changes']) + self.assertEqual(ret["changes"]["Logs"], "root\n") + self.assertTrue("Replaces" in ret["changes"]) self.assertEqual( - ret['comment'], - 'Container ran and exited with a return code of 0' + ret["comment"], "Container ran and exited with a return code of 0" ) @container_name def test_run_failhard(self, name): - ''' + """ Test to make sure that we fail a state when the container exits with nonzero status if failhard is set to True, and that we don't when it is set to False. @@ -1157,61 +1103,59 @@ class DockerContainerTestCase(ModuleCase, SaltReturnAssertsMixin): /usr/bin/false. Therefore, when the host machine running the tests has /usr/bin/false, it will not exist in the container and the Docker Engine API will cause an exception to be raised. - ''' + """ ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='/bin/false', - failhard=True) + command="/bin/false", + failhard=True, + ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes']['Logs'], '') + self.assertEqual(ret["changes"]["Logs"], "") self.assertTrue( - ret['comment'].startswith( - 'Container ran and exited with a return code of' - ) + ret["comment"].startswith("Container ran and exited with a return code of") ) - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, - command='/bin/false', - failhard=False) + command="/bin/false", + failhard=False, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes']['Logs'], '') + self.assertEqual(ret["changes"]["Logs"], "") self.assertTrue( - ret['comment'].startswith( - 'Container ran and exited with a return code of' - ) + ret["comment"].startswith("Container ran and exited with a return code of") ) - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) @container_name def test_run_bg(self, name): - ''' + """ Test to make sure that if the container is run in the background, we do not include an ExitCode or Logs key in the return. Then check the logs for the container to ensure that it ran as expected. - ''' + """ ret = self.run_state( - 'docker_container.run', + "docker_container.run", name=name, image=self.image, command='sh -c "sleep 5 && whoami"', - bg=True) + bg=True, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertTrue('Logs' not in ret['changes']) - self.assertTrue('ExitCode' not in ret['changes']) - self.assertEqual(ret['comment'], 'Container was run in the background') + self.assertTrue("Logs" not in ret["changes"]) + self.assertTrue("ExitCode" not in ret["changes"]) + self.assertEqual(ret["comment"], "Container was run in the background") # Now check the logs. The expectation is that the above asserts # completed during the 5-second sleep. self.assertEqual( - self.run_function('docker.logs', [name], follow=True), - 'root\n' + self.run_function("docker.logs", [name], follow=True), "root\n" ) diff --git a/tests/integration/states/test_docker_network.py b/tests/integration/states/test_docker_network.py index 8600bbda98a..fd563624c00 100644 --- a/tests/integration/states/test_docker_network.py +++ b/tests/integration/states/test_docker_network.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the docker_network states -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import errno import functools import logging @@ -11,67 +12,67 @@ import os import subprocess import tempfile -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import skipIf -from tests.support.case import ModuleCase -from tests.support.docker import with_network, random_name -from tests.support.helpers import destructiveTest, requires_system_grains -from tests.support.mixins import SaltReturnAssertsMixin - # Import Salt Libs import salt.utils.files import salt.utils.network import salt.utils.path from salt.exceptions import CommandExecutionError +from tests.support.case import ModuleCase +from tests.support.docker import random_name, with_network +from tests.support.helpers import destructiveTest, requires_system_grains +from tests.support.mixins import SaltReturnAssertsMixin + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf log = logging.getLogger(__name__) -IMAGE_NAME = random_name(prefix='salt_busybox_') +IMAGE_NAME = random_name(prefix="salt_busybox_") IPV6_ENABLED = bool(salt.utils.network.ip_addrs6(include_loopback=True)) def network_name(func): - ''' + """ Generate a randomized name for a network and clean it up afterward - ''' + """ + @functools.wraps(func) def wrapper(self, *args, **kwargs): - name = random_name(prefix='salt_net_') + name = random_name(prefix="salt_net_") try: return func(self, name, *args, **kwargs) finally: - self.run_function( - 'docker.disconnect_all_containers_from_network', [name]) + self.run_function("docker.disconnect_all_containers_from_network", [name]) try: - self.run_function('docker.remove_network', [name]) + self.run_function("docker.remove_network", [name]) except CommandExecutionError as exc: - if 'No such network' not in exc.__str__(): + if "No such network" not in exc.__str__(): raise + return wrapper def container_name(func): - ''' + """ Generate a randomized name for a container and clean it up afterward - ''' + """ + def build_image(): # Create temp dir image_build_rootdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - script_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'mkimage-busybox-static') + script_path = os.path.join(RUNTIME_VARS.BASE_FILES, "mkimage-busybox-static") cmd = [script_path, image_build_rootdir, IMAGE_NAME] - log.debug('Running \'%s\' to build busybox image', ' '.join(cmd)) + log.debug("Running '%s' to build busybox image", " ".join(cmd)) process = subprocess.Popen( - cmd, - close_fds=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + cmd, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) output = process.communicate()[0] - log.debug('Output from mkimge-busybox-static:\n%s', output) + log.debug("Output from mkimge-busybox-static:\n%s", output) if process.returncode != 0: - raise Exception('Failed to build image') + raise Exception("Failed to build image") try: salt.utils.files.rm_rf(image_build_rootdir) @@ -82,317 +83,259 @@ def container_name(func): @functools.wraps(func) def wrapper(self, *args, **kwargs): try: - self.run_function('docker.inspect_image', [IMAGE_NAME]) + self.run_function("docker.inspect_image", [IMAGE_NAME]) except CommandExecutionError: pass else: build_image() - name = random_name(prefix='salt_test_') + name = random_name(prefix="salt_test_") self.run_function( - 'docker.create', + "docker.create", name=name, image=IMAGE_NAME, - command='sleep 600', - start=True) + command="sleep 600", + start=True, + ) try: return func(self, name, *args, **kwargs) finally: try: - self.run_function('docker.rm', [name], force=True) + self.run_function("docker.rm", [name], force=True) except CommandExecutionError as exc: - if 'No such container' not in exc.__str__(): + if "No such container" not in exc.__str__(): raise + return wrapper @destructiveTest -@skipIf(not salt.utils.path.which('dockerd'), 'Docker not installed') +@skipIf(not salt.utils.path.which("dockerd"), "Docker not installed") class DockerNetworkTestCase(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Test docker_network states - ''' + """ + @classmethod def tearDownClass(cls): - ''' + """ Remove test image if present. Note that this will run a docker rmi even if no test which required the image was run. - ''' - cmd = ['docker', 'rmi', '--force', IMAGE_NAME] - log.debug('Running \'%s\' to destroy busybox image', ' '.join(cmd)) + """ + cmd = ["docker", "rmi", "--force", IMAGE_NAME] + log.debug("Running '%s' to destroy busybox image", " ".join(cmd)) process = subprocess.Popen( - cmd, - close_fds=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + cmd, close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) output = process.communicate()[0] - log.debug('Output from %s:\n%s', ' '.join(cmd), output) + log.debug("Output from %s:\n%s", " ".join(cmd), output) - if process.returncode != 0 and 'No such image' not in output: - raise Exception('Failed to destroy image') + if process.returncode != 0 and "No such image" not in output: + raise Exception("Failed to destroy image") def run_state(self, function, **kwargs): ret = super(DockerNetworkTestCase, self).run_state(function, **kwargs) - log.debug('ret = %s', ret) + log.debug("ret = %s", ret) return ret @with_network(create=False) def test_absent(self, net): self.assertSaltTrueReturn( - self.run_state('docker_network.present', name=net.name)) - ret = self.run_state('docker_network.absent', name=net.name) + self.run_state("docker_network.present", name=net.name) + ) + ret = self.run_state("docker_network.absent", name=net.name) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes'], {'removed': True}) - self.assertEqual( - ret['comment'], - 'Removed network \'{0}\''.format(net.name) - ) + self.assertEqual(ret["changes"], {"removed": True}) + self.assertEqual(ret["comment"], "Removed network '{0}'".format(net.name)) @container_name @with_network(create=False) def test_absent_with_disconnected_container(self, net, container_name): self.assertSaltTrueReturn( - self.run_state('docker_network.present', - name=net.name, - containers=[container_name]) + self.run_state( + "docker_network.present", name=net.name, containers=[container_name] + ) ) - ret = self.run_state('docker_network.absent', name=net.name) + ret = self.run_state("docker_network.absent", name=net.name) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] self.assertEqual( - ret['changes'], - { - 'removed': True, - 'disconnected': [container_name], - } - ) - self.assertEqual( - ret['comment'], - 'Removed network \'{0}\''.format(net.name) + ret["changes"], {"removed": True, "disconnected": [container_name]} ) + self.assertEqual(ret["comment"], "Removed network '{0}'".format(net.name)) @with_network(create=False) def test_absent_when_not_present(self, net): - ret = self.run_state('docker_network.absent', name=net.name) + ret = self.run_state("docker_network.absent", name=net.name) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["changes"], {}) self.assertEqual( - ret['comment'], - 'Network \'{0}\' already absent'.format(net.name) + ret["comment"], "Network '{0}' already absent".format(net.name) ) @with_network(create=False) def test_present(self, net): - ret = self.run_state('docker_network.present', name=net.name) + ret = self.run_state("docker_network.present", name=net.name) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] # Make sure the state return is what we expect - self.assertEqual(ret['changes'], {'created': True}) - self.assertEqual( - ret['comment'], - 'Network \'{0}\' created'.format(net.name) - ) + self.assertEqual(ret["changes"], {"created": True}) + self.assertEqual(ret["comment"], "Network '{0}' created".format(net.name)) # Now check to see that the network actually exists. If it doesn't, # this next function call will raise an exception. - self.run_function('docker.inspect_network', [net.name]) + self.run_function("docker.inspect_network", [net.name]) @container_name @with_network(create=False) def test_present_with_containers(self, net, container_name): ret = self.run_state( - 'docker_network.present', - name=net.name, - containers=[container_name]) + "docker_network.present", name=net.name, containers=[container_name] + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] self.assertEqual( - ret['changes'], - { - 'created': True, - 'connected': [container_name], - } - ) - self.assertEqual( - ret['comment'], - 'Network \'{0}\' created'.format(net.name) + ret["changes"], {"created": True, "connected": [container_name]} ) + self.assertEqual(ret["comment"], "Network '{0}' created".format(net.name)) # Now check to see that the network actually exists. If it doesn't, # this next function call will raise an exception. - self.run_function('docker.inspect_network', [net.name]) + self.run_function("docker.inspect_network", [net.name]) def _test_present_reconnect(self, net, container_name, reconnect=True): - ret = self.run_state( - 'docker_network.present', - name=net.name, - driver='bridge') + ret = self.run_state("docker_network.present", name=net.name, driver="bridge") self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['changes'], {'created': True}) - self.assertEqual( - ret['comment'], - 'Network \'{0}\' created'.format(net.name) - ) + self.assertEqual(ret["changes"], {"created": True}) + self.assertEqual(ret["comment"], "Network '{0}' created".format(net.name)) # Connect the container self.run_function( - 'docker.connect_container_to_network', - [container_name, net.name] + "docker.connect_container_to_network", [container_name, net.name] ) # Change the driver to force the network to be replaced ret = self.run_state( - 'docker_network.present', + "docker_network.present", name=net.name, - driver='macvlan', - reconnect=reconnect) + driver="macvlan", + reconnect=reconnect, + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] self.assertEqual( - ret['changes'], + ret["changes"], { - 'recreated': True, - 'reconnected' if reconnect else 'disconnected': [container_name], - net.name: { - 'Driver': { - 'old': 'bridge', - 'new': 'macvlan', - } - } - } + "recreated": True, + "reconnected" if reconnect else "disconnected": [container_name], + net.name: {"Driver": {"old": "bridge", "new": "macvlan"}}, + }, ) self.assertEqual( - ret['comment'], - 'Network \'{0}\' was replaced with updated config'.format(net.name) + ret["comment"], + "Network '{0}' was replaced with updated config".format(net.name), ) @container_name @with_network(create=False) def test_present_with_reconnect(self, net, container_name): - ''' + """ Test reconnecting with containers not passed to state - ''' + """ self._test_present_reconnect(net, container_name, reconnect=True) @container_name @with_network(create=False) def test_present_with_no_reconnect(self, net, container_name): - ''' + """ Test reconnecting with containers not passed to state - ''' + """ self._test_present_reconnect(net, container_name, reconnect=False) @with_network() def test_present_internal(self, net): self.assertSaltTrueReturn( - self.run_state( - 'docker_network.present', - name=net.name, - internal=True, - ) + self.run_state("docker_network.present", name=net.name, internal=True,) ) - net_info = self.run_function('docker.inspect_network', [net.name]) - self.assertIs(net_info['Internal'], True) + net_info = self.run_function("docker.inspect_network", [net.name]) + self.assertIs(net_info["Internal"], True) @with_network() def test_present_labels(self, net): # Test a mix of different ways of specifying labels self.assertSaltTrueReturn( self.run_state( - 'docker_network.present', + "docker_network.present", name=net.name, - labels=[ - 'foo', - 'bar=baz', - {'hello': 'world'}, - ], + labels=["foo", "bar=baz", {"hello": "world"}], ) ) - net_info = self.run_function('docker.inspect_network', [net.name]) + net_info = self.run_function("docker.inspect_network", [net.name]) self.assertEqual( - net_info['Labels'], - {'foo': '', - 'bar': 'baz', - 'hello': 'world'}, + net_info["Labels"], {"foo": "", "bar": "baz", "hello": "world"}, ) - @with_network(subnet='fe3f:2180:26:1::/123') - @with_network(subnet='10.247.197.96/27') - @skipIf(not IPV6_ENABLED, 'IPv6 not enabled') + @with_network(subnet="fe3f:2180:26:1::/123") + @with_network(subnet="10.247.197.96/27") + @skipIf(not IPV6_ENABLED, "IPv6 not enabled") def test_present_enable_ipv6(self, net1, net2): self.assertSaltTrueReturn( self.run_state( - 'docker_network.present', + "docker_network.present", name=net1.name, enable_ipv6=True, - ipam_pools=[ - {'subnet': net1.subnet}, - {'subnet': net2.subnet}, - ], + ipam_pools=[{"subnet": net1.subnet}, {"subnet": net2.subnet}], ) ) - net_info = self.run_function('docker.inspect_network', [net1.name]) - self.assertIs(net_info['EnableIPv6'], True) + net_info = self.run_function("docker.inspect_network", [net1.name]) + self.assertIs(net_info["EnableIPv6"], True) @requires_system_grains @with_network() def test_present_attachable(self, net, grains): - if grains['os_family'] == 'RedHat' \ - and grains.get('osmajorrelease', 0) <= 7: - self.skipTest('Cannot reliably manage attachable on RHEL <= 7') + if grains["os_family"] == "RedHat" and grains.get("osmajorrelease", 0) <= 7: + self.skipTest("Cannot reliably manage attachable on RHEL <= 7") self.assertSaltTrueReturn( - self.run_state( - 'docker_network.present', - name=net.name, - attachable=True, - ) + self.run_state("docker_network.present", name=net.name, attachable=True,) ) - net_info = self.run_function('docker.inspect_network', [net.name]) - self.assertIs(net_info['Attachable'], True) + net_info = self.run_function("docker.inspect_network", [net.name]) + self.assertIs(net_info["Attachable"], True) - @skipIf(True, 'Skip until we can set up docker swarm testing') + @skipIf(True, "Skip until we can set up docker swarm testing") @with_network() def test_present_scope(self, net): self.assertSaltTrueReturn( - self.run_state( - 'docker_network.present', - name=net.name, - scope='global', - ) + self.run_state("docker_network.present", name=net.name, scope="global",) ) - net_info = self.run_function('docker.inspect_network', [net.name]) - self.assertIs(net_info['Scope'], 'global') + net_info = self.run_function("docker.inspect_network", [net.name]) + self.assertIs(net_info["Scope"], "global") - @skipIf(True, 'Skip until we can set up docker swarm testing') + @skipIf(True, "Skip until we can set up docker swarm testing") @with_network() def test_present_ingress(self, net): self.assertSaltTrueReturn( - self.run_state( - 'docker_network.present', - name=net.name, - ingress=True, - ) + self.run_state("docker_network.present", name=net.name, ingress=True,) ) - net_info = self.run_function('docker.inspect_network', [net.name]) - self.assertIs(net_info['Ingress'], True) + net_info = self.run_function("docker.inspect_network", [net.name]) + self.assertIs(net_info["Ingress"], True) - @with_network(subnet='10.247.197.128/27') - @with_network(subnet='10.247.197.96/27') + @with_network(subnet="10.247.197.128/27") + @with_network(subnet="10.247.197.96/27") def test_present_with_custom_ipv4(self, net1, net2): # First run will test passing the IPAM arguments individually self.assertSaltTrueReturn( self.run_state( - 'docker_network.present', + "docker_network.present", name=net1.name, subnet=net1.subnet, gateway=net1.gateway, @@ -400,12 +343,9 @@ class DockerNetworkTestCase(ModuleCase, SaltReturnAssertsMixin): ) # Second run will pass them in the ipam_pools argument ret = self.run_state( - 'docker_network.present', + "docker_network.present", name=net1.name, # We want to keep the same network name - ipam_pools=[ - {'subnet': net2.subnet, - 'gateway': net2.gateway}, - ], + ipam_pools=[{"subnet": net2.subnet, "gateway": net2.gateway}], ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] @@ -414,58 +354,46 @@ class DockerNetworkTestCase(ModuleCase, SaltReturnAssertsMixin): # provided. So, there will be both an IPv4 and IPv6 pool in the # configuration. expected = { - 'recreated': True, + "recreated": True, net1.name: { - 'IPAM': { - 'Config': { - 'old': [ - {'Subnet': net1.subnet, - 'Gateway': net1.gateway}, - ], - 'new': [ - {'Subnet': net2.subnet, - 'Gateway': net2.gateway}, - ], + "IPAM": { + "Config": { + "old": [{"Subnet": net1.subnet, "Gateway": net1.gateway}], + "new": [{"Subnet": net2.subnet, "Gateway": net2.gateway}], } } - } + }, } - self.assertEqual(ret['changes'], expected) + self.assertEqual(ret["changes"], expected) self.assertEqual( - ret['comment'], - 'Network \'{0}\' was replaced with updated config'.format( - net1.name - ) + ret["comment"], + "Network '{0}' was replaced with updated config".format(net1.name), ) - @with_network(subnet='fe3f:2180:26:1::20/123') - @with_network(subnet='fe3f:2180:26:1::/123') - @with_network(subnet='10.247.197.96/27') - @skipIf(not IPV6_ENABLED, 'IPv6 not enabled') + @with_network(subnet="fe3f:2180:26:1::20/123") + @with_network(subnet="fe3f:2180:26:1::/123") + @with_network(subnet="10.247.197.96/27") + @skipIf(not IPV6_ENABLED, "IPv6 not enabled") def test_present_with_custom_ipv6(self, ipv4_net, ipv6_net1, ipv6_net2): self.assertSaltTrueReturn( self.run_state( - 'docker_network.present', + "docker_network.present", name=ipv4_net.name, enable_ipv6=True, ipam_pools=[ - {'subnet': ipv4_net.subnet, - 'gateway': ipv4_net.gateway}, - {'subnet': ipv6_net1.subnet, - 'gateway': ipv6_net1.gateway} + {"subnet": ipv4_net.subnet, "gateway": ipv4_net.gateway}, + {"subnet": ipv6_net1.subnet, "gateway": ipv6_net1.gateway}, ], ) ) ret = self.run_state( - 'docker_network.present', + "docker_network.present", name=ipv4_net.name, # We want to keep the same network name enable_ipv6=True, ipam_pools=[ - {'subnet': ipv4_net.subnet, - 'gateway': ipv4_net.gateway}, - {'subnet': ipv6_net2.subnet, - 'gateway': ipv6_net2.gateway} + {"subnet": ipv4_net.subnet, "gateway": ipv4_net.gateway}, + {"subnet": ipv6_net2.subnet, "gateway": ipv6_net2.gateway}, ], ) self.assertSaltTrueReturn(ret) @@ -475,30 +403,24 @@ class DockerNetworkTestCase(ModuleCase, SaltReturnAssertsMixin): # provided. So, there will be both an IPv4 and IPv6 pool in the # configuration. expected = { - 'recreated': True, + "recreated": True, ipv4_net.name: { - 'IPAM': { - 'Config': { - 'old': [ - {'Subnet': ipv4_net.subnet, - 'Gateway': ipv4_net.gateway}, - {'Subnet': ipv6_net1.subnet, - 'Gateway': ipv6_net1.gateway} + "IPAM": { + "Config": { + "old": [ + {"Subnet": ipv4_net.subnet, "Gateway": ipv4_net.gateway}, + {"Subnet": ipv6_net1.subnet, "Gateway": ipv6_net1.gateway}, ], - 'new': [ - {'Subnet': ipv4_net.subnet, - 'Gateway': ipv4_net.gateway}, - {'Subnet': ipv6_net2.subnet, - 'Gateway': ipv6_net2.gateway} + "new": [ + {"Subnet": ipv4_net.subnet, "Gateway": ipv4_net.gateway}, + {"Subnet": ipv6_net2.subnet, "Gateway": ipv6_net2.gateway}, ], } } - } + }, } - self.assertEqual(ret['changes'], expected) + self.assertEqual(ret["changes"], expected) self.assertEqual( - ret['comment'], - 'Network \'{0}\' was replaced with updated config'.format( - ipv4_net.name - ) + ret["comment"], + "Network '{0}' was replaced with updated config".format(ipv4_net.name), ) diff --git a/tests/integration/states/test_file.py b/tests/integration/states/test_file.py index f60606c5f1c..309fe0ae8b1 100644 --- a/tests/integration/states/test_file.py +++ b/tests/integration/states/test_file.py @@ -1,48 +1,49 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the file state -''' +""" -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno +import filecmp import logging import os import re -import sys import shutil import stat +import sys import tempfile import textwrap -import filecmp -log = logging.getLogger(__name__) - -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import ( - destructiveTest, - skip_if_not_root, - with_system_user_and_group, - with_tempdir, - with_tempfile, - Webserver, - dedent, -) -from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt libs +import pytest +import salt.serializers.configparser import salt.utils.data import salt.utils.files import salt.utils.json import salt.utils.path import salt.utils.platform import salt.utils.stringutils -import salt.serializers.configparser +from salt.ext import six +from salt.ext.six.moves import range from salt.utils.versions import LooseVersion as _LooseVersion +from tests.support.case import ModuleCase +from tests.support.helpers import ( + Webserver, + dedent, + destructiveTest, + skip_if_not_root, + with_system_user_and_group, + with_tempdir, + with_tempfile, +) +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + +log = logging.getLogger(__name__) + HAS_PWD = True try: @@ -56,29 +57,26 @@ try: except ImportError: HAS_GRP = False -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin IS_WINDOWS = salt.utils.platform.is_windows() -BINARY_FILE = b'GIF89a\x01\x00\x01\x00\x80\x00\x00\x05\x04\x04\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;' +BINARY_FILE = b"GIF89a\x01\x00\x01\x00\x80\x00\x00\x05\x04\x04\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;" -TEST_SYSTEM_USER = 'test_system_user' -TEST_SYSTEM_GROUP = 'test_system_group' +TEST_SYSTEM_USER = "test_system_user" +TEST_SYSTEM_GROUP = "test_system_group" def _test_managed_file_mode_keep_helper(testcase, local=False): - ''' + """ DRY helper function to run the same test with a local or remote path - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'scene33') - grail_fs_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'grail', 'scene33') - grail = 'salt://grail/scene33' if not local else grail_fs_path + """ + name = os.path.join(RUNTIME_VARS.TMP, "scene33") + grail_fs_path = os.path.join(RUNTIME_VARS.BASE_FILES, "grail", "scene33") + grail = "salt://grail/scene33" if not local else grail_fs_path # Get the current mode so that we can put the file back the way we # found it when we're done. - grail_fs_mode = int(testcase.run_function('file.get_mode', [grail_fs_path]), 8) + grail_fs_mode = int(testcase.run_function("file.get_mode", [grail_fs_path]), 8) initial_mode = 0o770 new_mode_1 = 0o600 new_mode_2 = 0o644 @@ -87,10 +85,7 @@ def _test_managed_file_mode_keep_helper(testcase, local=False): # to "keep", we're actually changing the permissions of the file to the # new mode. ret = testcase.run_state( - 'file.managed', - name=name, - mode=oct(initial_mode), - source=grail, + "file.managed", name=name, mode=oct(initial_mode), source=grail, ) if IS_WINDOWS: @@ -102,12 +97,7 @@ def _test_managed_file_mode_keep_helper(testcase, local=False): try: # Update the mode on the fileserver (pass 1) os.chmod(grail_fs_path, new_mode_1) - ret = testcase.run_state( - 'file.managed', - name=name, - mode='keep', - source=grail, - ) + ret = testcase.run_state("file.managed", name=name, mode="keep", source=grail,) testcase.assertSaltTrueReturn(ret) managed_mode = stat.S_IMODE(os.stat(name).st_mode) testcase.assertEqual(oct(managed_mode), oct(new_mode_1)) @@ -116,12 +106,7 @@ def _test_managed_file_mode_keep_helper(testcase, local=False): # to the same mode as new_mode_1, we definitely get an updated mode # this time. os.chmod(grail_fs_path, new_mode_2) - ret = testcase.run_state( - 'file.managed', - name=name, - mode='keep', - source=grail, - ) + ret = testcase.run_state("file.managed", name=name, mode="keep", source=grail,) testcase.assertSaltTrueReturn(ret) managed_mode = stat.S_IMODE(os.stat(name).st_mode) testcase.assertEqual(oct(managed_mode), oct(new_mode_2)) @@ -131,400 +116,417 @@ def _test_managed_file_mode_keep_helper(testcase, local=False): os.chmod(grail_fs_path, grail_fs_mode) +@pytest.mark.windows_whitelisted class FileTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the file state - ''' + """ def _delete_file(self, path): try: os.remove(path) except OSError as exc: if exc.errno != errno.ENOENT: - log.error('Failed to remove %s: %s', path, exc) + log.error("Failed to remove %s: %s", path, exc) def tearDown(self): - ''' + """ remove files created in previous tests - ''' - user = 'salt' - if user in str(self.run_function('user.list_users')): - self.run_function('user.delete', [user]) + """ + user = "salt" + if user in str(self.run_function("user.list_users")): + self.run_function("user.delete", [user]) def test_symlink(self): - ''' + """ file.symlink - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'symlink') - tgt = os.path.join(RUNTIME_VARS.TMP, 'target') + """ + name = os.path.join(RUNTIME_VARS.TMP, "symlink") + tgt = os.path.join(RUNTIME_VARS.TMP, "target") # Windows must have a source directory to link to if IS_WINDOWS and not os.path.isdir(tgt): os.mkdir(tgt) # Windows cannot create a symlink if it already exists - if IS_WINDOWS and self.run_function('file.is_link', [name]): - self.run_function('file.remove', [name]) + if IS_WINDOWS and self.run_function("file.is_link", [name]): + self.run_function("file.remove", [name]) - ret = self.run_state('file.symlink', name=name, target=tgt) + ret = self.run_state("file.symlink", name=name, target=tgt) self.assertSaltTrueReturn(ret) def test_test_symlink(self): - ''' + """ file.symlink test interface - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'symlink2') - tgt = os.path.join(RUNTIME_VARS.TMP, 'target') - ret = self.run_state('file.symlink', test=True, name=name, target=tgt) + """ + name = os.path.join(RUNTIME_VARS.TMP, "symlink2") + tgt = os.path.join(RUNTIME_VARS.TMP, "target") + ret = self.run_state("file.symlink", test=True, name=name, target=tgt) self.assertSaltNoneReturn(ret) def test_absent_file(self): - ''' + """ file.absent - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'file_to_kill') - with salt.utils.files.fopen(name, 'w+') as fp_: - fp_.write('killme') - ret = self.run_state('file.absent', name=name) + """ + name = os.path.join(RUNTIME_VARS.TMP, "file_to_kill") + with salt.utils.files.fopen(name, "w+") as fp_: + fp_.write("killme") + ret = self.run_state("file.absent", name=name) self.assertSaltTrueReturn(ret) self.assertFalse(os.path.isfile(name)) def test_absent_dir(self): - ''' + """ file.absent - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'dir_to_kill') + """ + name = os.path.join(RUNTIME_VARS.TMP, "dir_to_kill") if not os.path.isdir(name): # left behind... Don't fail because of this! os.makedirs(name) - ret = self.run_state('file.absent', name=name) + ret = self.run_state("file.absent", name=name) self.assertSaltTrueReturn(ret) self.assertFalse(os.path.isdir(name)) def test_absent_link(self): - ''' + """ file.absent - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'link_to_kill') - tgt = '{0}.tgt'.format(name) + """ + name = os.path.join(RUNTIME_VARS.TMP, "link_to_kill") + tgt = "{0}.tgt".format(name) # Windows must have a source directory to link to if IS_WINDOWS and not os.path.isdir(tgt): os.mkdir(tgt) - if not self.run_function('file.is_link', [name]): - self.run_function('file.symlink', [tgt, name]) + if not self.run_function("file.is_link", [name]): + self.run_function("file.symlink", [tgt, name]) - ret = self.run_state('file.absent', name=name) + ret = self.run_state("file.absent", name=name) try: self.assertSaltTrueReturn(ret) - self.assertFalse(self.run_function('file.is_link', [name])) + self.assertFalse(self.run_function("file.is_link", [name])) finally: - if self.run_function('file.is_link', [name]): - self.run_function('file.remove', [name]) + if self.run_function("file.is_link", [name]): + self.run_function("file.remove", [name]) @with_tempfile() def test_test_absent(self, name): - ''' + """ file.absent test interface - ''' - with salt.utils.files.fopen(name, 'w+') as fp_: - fp_.write('killme') - ret = self.run_state('file.absent', test=True, name=name) + """ + with salt.utils.files.fopen(name, "w+") as fp_: + fp_.write("killme") + ret = self.run_state("file.absent", test=True, name=name) self.assertSaltNoneReturn(ret) self.assertTrue(os.path.isfile(name)) def test_managed(self): - ''' + """ file.managed - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') - ret = self.run_state( - 'file.managed', name=name, source='salt://grail/scene33' - ) - src = os.path.join(RUNTIME_VARS.BASE_FILES, 'grail', 'scene33') - with salt.utils.files.fopen(src, 'r') as fp_: + """ + name = os.path.join(RUNTIME_VARS.TMP, "grail_scene33") + ret = self.run_state("file.managed", name=name, source="salt://grail/scene33") + src = os.path.join(RUNTIME_VARS.BASE_FILES, "grail", "scene33") + with salt.utils.files.fopen(src, "r") as fp_: master_data = fp_.read() - with salt.utils.files.fopen(name, 'r') as fp_: + with salt.utils.files.fopen(name, "r") as fp_: minion_data = fp_.read() self.assertEqual(master_data, minion_data) self.assertSaltTrueReturn(ret) def test_managed_file_mode(self): - ''' + """ file.managed, correct file permissions - ''' - desired_mode = 504 # 0770 octal - name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') + """ + desired_mode = 504 # 0770 octal + name = os.path.join(RUNTIME_VARS.TMP, "grail_scene33") ret = self.run_state( - 'file.managed', name=name, mode='0770', source='salt://grail/scene33' + "file.managed", name=name, mode="0770", source="salt://grail/scene33" ) if IS_WINDOWS: - expected = 'The \'mode\' option is not supported on Windows' - self.assertEqual(ret[list(ret)[0]]['comment'], expected) + expected = "The 'mode' option is not supported on Windows" + self.assertEqual(ret[list(ret)[0]]["comment"], expected) self.assertSaltFalseReturn(ret) return - resulting_mode = stat.S_IMODE( - os.stat(name).st_mode - ) + resulting_mode = stat.S_IMODE(os.stat(name).st_mode) self.assertEqual(oct(desired_mode), oct(resulting_mode)) self.assertSaltTrueReturn(ret) - @skipIf(IS_WINDOWS, 'Windows does not report any file modes. Skipping.') + @skipIf(IS_WINDOWS, "Windows does not report any file modes. Skipping.") def test_managed_file_mode_keep(self): - ''' + """ Test using "mode: keep" in a file.managed state - ''' + """ _test_managed_file_mode_keep_helper(self, local=False) - @skipIf(IS_WINDOWS, 'Windows does not report any file modes. Skipping.') + @skipIf(IS_WINDOWS, "Windows does not report any file modes. Skipping.") def test_managed_file_mode_keep_local_source(self): - ''' + """ Test using "mode: keep" in a file.managed state, with a local file path as the source. - ''' + """ _test_managed_file_mode_keep_helper(self, local=True) def test_managed_file_mode_file_exists_replace(self): - ''' + """ file.managed, existing file with replace=True, change permissions - ''' - initial_mode = 504 # 0770 octal - desired_mode = 384 # 0600 octal - name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') + """ + initial_mode = 504 # 0770 octal + desired_mode = 384 # 0600 octal + name = os.path.join(RUNTIME_VARS.TMP, "grail_scene33") ret = self.run_state( - 'file.managed', name=name, mode=oct(initial_mode), source='salt://grail/scene33' + "file.managed", + name=name, + mode=oct(initial_mode), + source="salt://grail/scene33", ) if IS_WINDOWS: - expected = 'The \'mode\' option is not supported on Windows' - self.assertEqual(ret[list(ret)[0]]['comment'], expected) + expected = "The 'mode' option is not supported on Windows" + self.assertEqual(ret[list(ret)[0]]["comment"], expected) self.assertSaltFalseReturn(ret) return - resulting_mode = stat.S_IMODE( - os.stat(name).st_mode - ) + resulting_mode = stat.S_IMODE(os.stat(name).st_mode) self.assertEqual(oct(initial_mode), oct(resulting_mode)) - name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') + name = os.path.join(RUNTIME_VARS.TMP, "grail_scene33") ret = self.run_state( - 'file.managed', name=name, replace=True, mode=oct(desired_mode), source='salt://grail/scene33' - ) - resulting_mode = stat.S_IMODE( - os.stat(name).st_mode + "file.managed", + name=name, + replace=True, + mode=oct(desired_mode), + source="salt://grail/scene33", ) + resulting_mode = stat.S_IMODE(os.stat(name).st_mode) self.assertEqual(oct(desired_mode), oct(resulting_mode)) self.assertSaltTrueReturn(ret) def test_managed_file_mode_file_exists_noreplace(self): - ''' + """ file.managed, existing file with replace=False, change permissions - ''' - initial_mode = 504 # 0770 octal - desired_mode = 384 # 0600 octal - name = os.path.join(RUNTIME_VARS.TMP, 'grail_scene33') + """ + initial_mode = 504 # 0770 octal + desired_mode = 384 # 0600 octal + name = os.path.join(RUNTIME_VARS.TMP, "grail_scene33") ret = self.run_state( - 'file.managed', name=name, replace=True, mode=oct(initial_mode), source='salt://grail/scene33' + "file.managed", + name=name, + replace=True, + mode=oct(initial_mode), + source="salt://grail/scene33", ) if IS_WINDOWS: - expected = 'The \'mode\' option is not supported on Windows' - self.assertEqual(ret[list(ret)[0]]['comment'], expected) + expected = "The 'mode' option is not supported on Windows" + self.assertEqual(ret[list(ret)[0]]["comment"], expected) self.assertSaltFalseReturn(ret) return ret = self.run_state( - 'file.managed', name=name, replace=False, mode=oct(desired_mode), source='salt://grail/scene33' - ) - resulting_mode = stat.S_IMODE( - os.stat(name).st_mode + "file.managed", + name=name, + replace=False, + mode=oct(desired_mode), + source="salt://grail/scene33", ) + resulting_mode = stat.S_IMODE(os.stat(name).st_mode) self.assertEqual(oct(desired_mode), oct(resulting_mode)) self.assertSaltTrueReturn(ret) def test_managed_file_with_grains_data(self): - ''' + """ Test to ensure we can render grains data into a managed file. - ''' - grain_path = os.path.join(RUNTIME_VARS.TMP, 'file-grain-test') - state_file = 'file-grainget' + """ + grain_path = os.path.join(RUNTIME_VARS.TMP, "file-grain-test") + state_file = "file-grainget" - self.run_function('state.sls', [state_file], pillar={'grain_path': grain_path}) + self.run_function("state.sls", [state_file], pillar={"grain_path": grain_path}) self.assertTrue(os.path.exists(grain_path)) - with salt.utils.files.fopen(grain_path, 'r') as fp_: + with salt.utils.files.fopen(grain_path, "r") as fp_: file_contents = fp_.readlines() if IS_WINDOWS: - match = '^minion\r\n' + match = "^minion\r\n" else: - match = '^minion\n' + match = "^minion\n" self.assertTrue(re.match(match, file_contents[0])) def test_managed_file_with_pillar_sls(self): - ''' + """ Test to ensure pillar data in sls file is rendered properly and file is created. - ''' + """ - file_pillar = os.path.join(RUNTIME_VARS.TMP, 'filepillar-python') + file_pillar = os.path.join(RUNTIME_VARS.TMP, "filepillar-python") self.addCleanup(self._delete_file, file_pillar) - state_name = 'file-pillarget' + state_name = "file-pillarget" - log.warning('File Path: %s', file_pillar) - ret = self.run_function('state.sls', [state_name]) + log.warning("File Path: %s", file_pillar) + ret = self.run_function("state.sls", [state_name]) self.assertSaltTrueReturn(ret) # Check to make sure the file was created - check_file = self.run_function('file.file_exists', [file_pillar]) + check_file = self.run_function("file.file_exists", [file_pillar]) self.assertTrue(check_file) def test_managed_file_with_pillardefault_sls(self): - ''' + """ Test to ensure when pillar data is not available in sls file with pillar.get it uses the default value. - ''' - file_pillar_def = os.path.join(RUNTIME_VARS.TMP, 'filepillar-defaultvalue') + """ + file_pillar_def = os.path.join(RUNTIME_VARS.TMP, "filepillar-defaultvalue") self.addCleanup(self._delete_file, file_pillar_def) - state_name = 'file-pillardefaultget' + state_name = "file-pillardefaultget" - log.warning('File Path: %s', file_pillar_def) - ret = self.run_function('state.sls', [state_name]) + log.warning("File Path: %s", file_pillar_def) + ret = self.run_function("state.sls", [state_name]) self.assertSaltTrueReturn(ret) # Check to make sure the file was created - check_file = self.run_function('file.file_exists', [file_pillar_def]) + check_file = self.run_function("file.file_exists", [file_pillar_def]) self.assertTrue(check_file) @skip_if_not_root def test_managed_dir_mode(self): - ''' + """ Tests to ensure that file.managed creates directories with the permissions requested with the dir_mode argument - ''' + """ desired_mode = 511 # 0777 in octal - name = os.path.join(RUNTIME_VARS.TMP, 'a', 'managed_dir_mode_test_file') - desired_owner = 'nobody' + name = os.path.join(RUNTIME_VARS.TMP, "a", "managed_dir_mode_test_file") + desired_owner = "nobody" ret = self.run_state( - 'file.managed', + "file.managed", name=name, - source='salt://grail/scene33', + source="salt://grail/scene33", mode=600, makedirs=True, user=desired_owner, - dir_mode=oct(desired_mode) # 0777 + dir_mode=oct(desired_mode), # 0777 ) if IS_WINDOWS: - expected = 'The \'mode\' option is not supported on Windows' - self.assertEqual(ret[list(ret)[0]]['comment'], expected) + expected = "The 'mode' option is not supported on Windows" + self.assertEqual(ret[list(ret)[0]]["comment"], expected) self.assertSaltFalseReturn(ret) return resulting_mode = stat.S_IMODE( - os.stat(os.path.join(RUNTIME_VARS.TMP, 'a')).st_mode + os.stat(os.path.join(RUNTIME_VARS.TMP, "a")).st_mode ) - resulting_owner = pwd.getpwuid(os.stat(os.path.join(RUNTIME_VARS.TMP, 'a')).st_uid).pw_name + resulting_owner = pwd.getpwuid( + os.stat(os.path.join(RUNTIME_VARS.TMP, "a")).st_uid + ).pw_name self.assertEqual(oct(desired_mode), oct(resulting_mode)) self.assertSaltTrueReturn(ret) self.assertEqual(desired_owner, resulting_owner) def test_test_managed(self): - ''' + """ file.managed test interface - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'grail_not_not_scene33') + """ + name = os.path.join(RUNTIME_VARS.TMP, "grail_not_not_scene33") ret = self.run_state( - 'file.managed', test=True, name=name, source='salt://grail/scene33' + "file.managed", test=True, name=name, source="salt://grail/scene33" ) self.assertSaltNoneReturn(ret) self.assertFalse(os.path.isfile(name)) def test_managed_show_changes_false(self): - ''' + """ file.managed test interface - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'grail_not_scene33') - with salt.utils.files.fopen(name, 'wb') as fp_: - fp_.write(b'test_managed_show_changes_false\n') + """ + name = os.path.join(RUNTIME_VARS.TMP, "grail_not_scene33") + with salt.utils.files.fopen(name, "wb") as fp_: + fp_.write(b"test_managed_show_changes_false\n") ret = self.run_state( - 'file.managed', name=name, source='salt://grail/scene33', - show_changes=False + "file.managed", name=name, source="salt://grail/scene33", show_changes=False ) - changes = next(six.itervalues(ret))['changes'] - self.assertEqual('<show_changes=False>', changes['diff']) + changes = next(six.itervalues(ret))["changes"] + self.assertEqual("<show_changes=False>", changes["diff"]) def test_managed_show_changes_true(self): - ''' + """ file.managed test interface - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'grail_not_scene33') - with salt.utils.files.fopen(name, 'wb') as fp_: - fp_.write(b'test_managed_show_changes_false\n') + """ + name = os.path.join(RUNTIME_VARS.TMP, "grail_not_scene33") + with salt.utils.files.fopen(name, "wb") as fp_: + fp_.write(b"test_managed_show_changes_false\n") - ret = self.run_state( - 'file.managed', name=name, source='salt://grail/scene33', - ) + ret = self.run_state("file.managed", name=name, source="salt://grail/scene33",) - changes = next(six.itervalues(ret))['changes'] - self.assertIn('diff', changes) + changes = next(six.itervalues(ret))["changes"] + self.assertIn("diff", changes) - @skipIf(IS_WINDOWS, 'Don\'t know how to fix for Windows') + @skipIf(IS_WINDOWS, "Don't know how to fix for Windows") def test_managed_escaped_file_path(self): - ''' + """ file.managed test that 'salt://|' protects unusual characters in file path - ''' - funny_file = salt.utils.files.mkstemp(prefix='?f!le? n@=3&', suffix='.file type') + """ + funny_file = salt.utils.files.mkstemp( + prefix="?f!le? n@=3&", suffix=".file type" + ) funny_file_name = os.path.split(funny_file)[1] - funny_url = 'salt://|' + funny_file_name + funny_url = "salt://|" + funny_file_name funny_url_path = os.path.join(RUNTIME_VARS.BASE_FILES, funny_file_name) - state_name = 'funny_file' - state_file_name = state_name + '.sls' + state_name = "funny_file" + state_file_name = state_name + ".sls" state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_file_name) - state_key = 'file_|-{0}_|-{0}_|-managed'.format(funny_file) + state_key = "file_|-{0}_|-{0}_|-managed".format(funny_file) self.addCleanup(os.remove, state_file) self.addCleanup(os.remove, funny_file) self.addCleanup(os.remove, funny_url_path) - with salt.utils.files.fopen(funny_url_path, 'w'): + with salt.utils.files.fopen(funny_url_path, "w"): pass - with salt.utils.files.fopen(state_file, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + with salt.utils.files.fopen(state_file, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ {0}: file.managed: - source: {1} - makedirs: True - '''.format(funny_file, funny_url))) + """.format( + funny_file, funny_url + ) + ) + ) - ret = self.run_function('state.sls', [state_name]) - self.assertTrue(ret[state_key]['result']) + ret = self.run_function("state.sls", [state_name]) + self.assertTrue(ret[state_key]["result"]) def test_managed_contents(self): - ''' + """ test file.managed with contents that is a boolean, string, integer, float, list, and dictionary - ''' - state_name = 'file-FileTest-test_managed_contents' - state_filename = state_name + '.sls' + """ + state_name = "file-FileTest-test_managed_contents" + state_filename = state_name + ".sls" state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) managed_files = {} state_keys = {} - for typ in ('bool', 'str', 'int', 'float', 'list', 'dict'): + for typ in ("bool", "str", "int", "float", "list", "dict"): managed_files[typ] = salt.utils.files.mkstemp() - state_keys[typ] = 'file_|-{0} file_|-{1}_|-managed'.format(typ, managed_files[typ]) + state_keys[typ] = "file_|-{0} file_|-{1}_|-managed".format( + typ, managed_files[typ] + ) try: - with salt.utils.files.fopen(state_file, 'w') as fd_: - fd_.write(textwrap.dedent('''\ + with salt.utils.files.fopen(state_file, "w") as fd_: + fd_.write( + textwrap.dedent( + """\ bool file: file.managed: - name: {bool} @@ -557,13 +559,17 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): C: charge P: parity T: time - '''.format(**managed_files))) + """.format( + **managed_files + ) + ) + ) - ret = self.run_function('state.sls', [state_name]) + ret = self.run_function("state.sls", [state_name]) self.assertSaltTrueReturn(ret) for typ in state_keys: - self.assertTrue(ret[state_keys[typ]]['result']) - self.assertIn('diff', ret[state_keys[typ]]['changes']) + self.assertTrue(ret[state_keys[typ]]["result"]) + self.assertIn("diff", ret[state_keys[typ]]["changes"]) finally: if os.path.exists(state_file): os.remove(state_file) @@ -572,105 +578,109 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): os.remove(managed_files[typ]) def test_managed_contents_with_contents_newline(self): - ''' + """ test file.managed with contents by using the default content_newline flag. - ''' - contents = 'test_managed_contents_with_newline_one' - name = os.path.join(RUNTIME_VARS.TMP, 'foo') + """ + contents = "test_managed_contents_with_newline_one" + name = os.path.join(RUNTIME_VARS.TMP, "foo") # Create a file named foo with contents as above but with a \n at EOF - self.run_state('file.managed', name=name, contents=contents, - contents_newline=True) - with salt.utils.files.fopen(name, 'r') as fp_: + self.run_state( + "file.managed", name=name, contents=contents, contents_newline=True + ) + with salt.utils.files.fopen(name, "r") as fp_: last_line = fp_.read() self.assertEqual((contents + os.linesep), last_line) def test_managed_contents_with_contents_newline_false(self): - ''' + """ test file.managed with contents by using the non default content_newline flag. - ''' - contents = 'test_managed_contents_with_newline_one' - name = os.path.join(RUNTIME_VARS.TMP, 'bar') + """ + contents = "test_managed_contents_with_newline_one" + name = os.path.join(RUNTIME_VARS.TMP, "bar") # Create a file named foo with contents as above but with a \n at EOF - self.run_state('file.managed', name=name, contents=contents, - contents_newline=False) - with salt.utils.files.fopen(name, 'r') as fp_: + self.run_state( + "file.managed", name=name, contents=contents, contents_newline=False + ) + with salt.utils.files.fopen(name, "r") as fp_: last_line = fp_.read() self.assertEqual(contents, last_line) def test_managed_multiline_contents_with_contents_newline(self): - ''' + """ test file.managed with contents by using the non default content_newline flag. - ''' - contents = ('this is a cookie{}this is another cookie'. - format(os.linesep)) - name = os.path.join(RUNTIME_VARS.TMP, 'bar') + """ + contents = "this is a cookie{}this is another cookie".format(os.linesep) + name = os.path.join(RUNTIME_VARS.TMP, "bar") # Create a file named foo with contents as above but with a \n at EOF - self.run_state('file.managed', name=name, contents=contents, - contents_newline=True) - with salt.utils.files.fopen(name, 'r') as fp_: + self.run_state( + "file.managed", name=name, contents=contents, contents_newline=True + ) + with salt.utils.files.fopen(name, "r") as fp_: last_line = fp_.read() self.assertEqual((contents + os.linesep), last_line) def test_managed_multiline_contents_with_contents_newline_false(self): - ''' + """ test file.managed with contents by using the non default content_newline flag. - ''' - contents = ('this is a cookie{}this is another cookie'. - format(os.linesep)) - name = os.path.join(RUNTIME_VARS.TMP, 'bar') + """ + contents = "this is a cookie{}this is another cookie".format(os.linesep) + name = os.path.join(RUNTIME_VARS.TMP, "bar") # Create a file named foo with contents as above but with a \n at EOF - self.run_state('file.managed', name=name, contents=contents, - contents_newline=False) - with salt.utils.files.fopen(name, 'r') as fp_: + self.run_state( + "file.managed", name=name, contents=contents, contents_newline=False + ) + with salt.utils.files.fopen(name, "r") as fp_: last_line = fp_.read() self.assertEqual(contents, last_line) @skip_if_not_root @skipIf(IS_WINDOWS, 'Windows does not support "mode" kwarg. Skipping.') - @skipIf(not salt.utils.path.which('visudo'), 'sudo is missing') + @skipIf(not salt.utils.path.which("visudo"), "sudo is missing") def test_managed_check_cmd(self): - ''' + """ Test file.managed passing a basic check_cmd kwarg. See Issue #38111. - ''' - r_group = 'root' + """ + r_group = "root" if salt.utils.platform.is_darwin(): - r_group = 'wheel' + r_group = "wheel" try: ret = self.run_state( - 'file.managed', - name='/tmp/sudoers', - user='root', + "file.managed", + name="/tmp/sudoers", + user="root", group=r_group, mode=440, - check_cmd='visudo -c -s -f' + check_cmd="visudo -c -s -f", ) self.assertSaltTrueReturn(ret) - self.assertInSaltComment('Empty file', ret) - self.assertEqual(ret['file_|-/tmp/sudoers_|-/tmp/sudoers_|-managed']['changes'], - {'new': 'file /tmp/sudoers created', 'mode': '0440'}) + self.assertInSaltComment("Empty file", ret) + self.assertEqual( + ret["file_|-/tmp/sudoers_|-/tmp/sudoers_|-managed"]["changes"], + {"new": "file /tmp/sudoers created", "mode": "0440"}, + ) finally: # Clean Up File - if os.path.exists('/tmp/sudoers'): - os.remove('/tmp/sudoers') + if os.path.exists("/tmp/sudoers"): + os.remove("/tmp/sudoers") def test_managed_local_source_with_source_hash(self): - ''' + """ Make sure that we enforce the source_hash even with local files - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'local_source_with_source_hash') - local_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'grail', 'scene33') - actual_hash = '567fd840bf1548edc35c48eb66cdd78bfdfcccff' + """ + name = os.path.join(RUNTIME_VARS.TMP, "local_source_with_source_hash") + local_path = os.path.join(RUNTIME_VARS.BASE_FILES, "grail", "scene33") + actual_hash = "567fd840bf1548edc35c48eb66cdd78bfdfcccff" if IS_WINDOWS: # CRLF vs LF causes a different hash on windows - actual_hash = 'f658a0ec121d9c17088795afcc6ff3c43cb9842a' + actual_hash = "f658a0ec121d9c17088795afcc6ff3c43cb9842a" # Reverse the actual hash bad_hash = actual_hash[::-1] @@ -682,38 +692,39 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): raise def do_test(clean=False): - for proto in ('file://', ''): + for proto in ("file://", ""): source = proto + local_path - log.debug('Trying source %s', source) + log.debug("Trying source %s", source) try: ret = self.run_state( - 'file.managed', + "file.managed", name=name, source=source, - source_hash='sha1={0}'.format(bad_hash)) + source_hash="sha1={0}".format(bad_hash), + ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] # Shouldn't be any changes - self.assertFalse(ret['changes']) + self.assertFalse(ret["changes"]) # Check that we identified a hash mismatch - self.assertIn( - 'does not match actual checksum', ret['comment']) + self.assertIn("does not match actual checksum", ret["comment"]) ret = self.run_state( - 'file.managed', + "file.managed", name=name, source=source, - source_hash='sha1={0}'.format(actual_hash)) + source_hash="sha1={0}".format(actual_hash), + ) self.assertSaltTrueReturn(ret) finally: if clean: remove_file() remove_file() - log.debug('Trying with nonexistant destination file') + log.debug("Trying with nonexistant destination file") do_test() - log.debug('Trying with destination file already present') - with salt.utils.files.fopen(name, 'w'): + log.debug("Trying with destination file already present") + with salt.utils.files.fopen(name, "w"): pass try: do_test(clean=False) @@ -721,29 +732,25 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): remove_file() def test_managed_local_source_does_not_exist(self): - ''' + """ Make sure that we exit gracefully when a local source doesn't exist - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'local_source_does_not_exist') - local_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'grail', 'scene99') + """ + name = os.path.join(RUNTIME_VARS.TMP, "local_source_does_not_exist") + local_path = os.path.join(RUNTIME_VARS.BASE_FILES, "grail", "scene99") - for proto in ('file://', ''): + for proto in ("file://", ""): source = proto + local_path - log.debug('Trying source %s', source) - ret = self.run_state( - 'file.managed', - name=name, - source=source) + log.debug("Trying source %s", source) + ret = self.run_state("file.managed", name=name, source=source) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] # Shouldn't be any changes - self.assertFalse(ret['changes']) + self.assertFalse(ret["changes"]) # Check that we identified a hash mismatch - self.assertIn( - 'does not exist', ret['comment']) + self.assertIn("does not exist", ret["comment"]) def test_managed_unicode_jinja_with_tojson_filter(self): - ''' + """ Using {{ varname }} with a list or dictionary which contains unicode types on Python 2 will result in Jinja rendering the "u" prefix on each string. This tests that using the "tojson" jinja filter will dump them @@ -767,69 +774,65 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): Dumping the data structure to JSON using the "tojson" jinja filter should produce an inline data structure which is valid YAML and will be loaded properly by our YAML loader. - ''' - test_file = os.path.join(RUNTIME_VARS.TMP, 'test-tojson.txt') + """ + test_file = os.path.join(RUNTIME_VARS.TMP, "test-tojson.txt") ret = self.run_function( - 'state.apply', - mods='tojson', - pillar={'tojson-file': test_file}) + "state.apply", mods="tojson", pillar={"tojson-file": test_file} + ) ret = ret[next(iter(ret))] - assert ret['result'], ret - with salt.utils.files.fopen(test_file, mode='rb') as fp_: + assert ret["result"], ret + with salt.utils.files.fopen(test_file, mode="rb") as fp_: managed = salt.utils.stringutils.to_unicode(fp_.read()) - expected = dedent('''\ + expected = dedent( + """\ Die Webseite ist https://saltstack.com. Der Zucker ist süß. - ''') - assert managed == expected, '{0!r} != {1!r}'.format(managed, expected) # pylint: disable=repr-flag-used-in-string + """ + ) + assert managed == expected, "{0!r} != {1!r}".format(managed, expected) def test_managed_source_hash_indifferent_case(self): - ''' + """ Test passing a source_hash as an uppercase hash. This is a regression test for Issue #38914 and Issue #48230 (test=true use). - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'source_hash_indifferent_case') - state_name = 'file_|-{0}_|' \ - '-{0}_|-managed'.format(name) - local_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'hello_world.txt') - actual_hash = 'c98c24b677eff44860afea6f493bbaec5bb1c4cbb209c6fc2bbb47f66ff2ad31' + """ + name = os.path.join(RUNTIME_VARS.TMP, "source_hash_indifferent_case") + state_name = "file_|-{0}_|" "-{0}_|-managed".format(name) + local_path = os.path.join(RUNTIME_VARS.BASE_FILES, "hello_world.txt") + actual_hash = "c98c24b677eff44860afea6f493bbaec5bb1c4cbb209c6fc2bbb47f66ff2ad31" if IS_WINDOWS: # CRLF vs LF causes a differnt hash on windows - actual_hash = '92b772380a3f8e27a93e57e6deeca6c01da07f5aadce78bb2fbb20de10a66925' + actual_hash = ( + "92b772380a3f8e27a93e57e6deeca6c01da07f5aadce78bb2fbb20de10a66925" + ) uppercase_hash = actual_hash.upper() try: # Lay down tmp file to test against self.run_state( - 'file.managed', - name=name, - source=local_path, - source_hash=actual_hash + "file.managed", name=name, source=local_path, source_hash=actual_hash ) # Test uppercase source_hash: should return True with no changes ret = self.run_state( - 'file.managed', - name=name, - source=local_path, - source_hash=uppercase_hash + "file.managed", name=name, source=local_path, source_hash=uppercase_hash ) - assert ret[state_name]['result'] is True - assert ret[state_name]['changes'] == {} + assert ret[state_name]["result"] is True + assert ret[state_name]["changes"] == {} # Test uppercase source_hash using test=true # Should return True with no changes ret = self.run_state( - 'file.managed', + "file.managed", name=name, source=local_path, source_hash=uppercase_hash, - test=True + test=True, ) - assert ret[state_name]['result'] is True - assert ret[state_name]['changes'] == {} + assert ret[state_name]["result"] is True + assert ret[state_name]["changes"] == {} finally: # Clean Up File @@ -838,363 +841,353 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @with_tempfile(create=False) def test_managed_latin1_diff(self, name): - ''' + """ Tests that latin-1 file contents are represented properly in the diff - ''' + """ # Lay down the initial file ret = self.run_state( - 'file.managed', - name=name, - source='salt://issue-48777/old.html') + "file.managed", name=name, source="salt://issue-48777/old.html" + ) ret = ret[next(iter(ret))] - assert ret['result'] is True, ret + assert ret["result"] is True, ret # Replace it with the new file and check the diff ret = self.run_state( - 'file.managed', - name=name, - source='salt://issue-48777/new.html') + "file.managed", name=name, source="salt://issue-48777/new.html" + ) ret = ret[next(iter(ret))] - assert ret['result'] is True, ret - diff_lines = ret['changes']['diff'].split(os.linesep) - assert '+räksmörgås' in diff_lines, diff_lines + assert ret["result"] is True, ret + diff_lines = ret["changes"]["diff"].split(os.linesep) + assert "+räksmörgås" in diff_lines, diff_lines @with_tempfile() def test_managed_keep_source_false_salt(self, name): - ''' + """ This test ensures that we properly clean the cached file if keep_source is set to False, for source files using a salt:// URL - ''' - source = 'salt://grail/scene33' - saltenv = 'base' + """ + source = "salt://grail/scene33" + saltenv = "base" # Run the state ret = self.run_state( - 'file.managed', - name=name, - source=source, - saltenv=saltenv, - keep_source=False) + "file.managed", name=name, source=source, saltenv=saltenv, keep_source=False + ) ret = ret[next(iter(ret))] - assert ret['result'] is True + assert ret["result"] is True # Now make sure that the file is not cached - result = self.run_function('cp.is_cached', [source, saltenv]) - assert result == '', 'File is still cached at {0}'.format(result) + result = self.run_function("cp.is_cached", [source, saltenv]) + assert result == "", "File is still cached at {0}".format(result) @with_tempfile(create=False) @with_tempfile(create=False) def test_file_managed_onchanges(self, file1, file2): - ''' + """ Test file.managed state with onchanges - ''' - pillar = {'file1': file1, - 'file2': file2, - 'source': 'salt://testfile', - 'req': 'onchanges'} + """ + pillar = { + "file1": file1, + "file2": file2, + "source": "salt://testfile", + "req": "onchanges", + } # Lay down the file used in the below SLS to ensure that when it is # run, there are no changes. - self.run_state( - 'file.managed', - name=pillar['file2'], - source=pillar['source']) + self.run_state("file.managed", name=pillar["file2"], source=pillar["source"]) ret = self.repack_state_returns( self.run_function( - 'state.apply', - mods='onchanges_prereq', - pillar=pillar, - test=True, + "state.apply", mods="onchanges_prereq", pillar=pillar, test=True, ) ) # The file states should both exit with None - assert ret['one']['result'] is None, ret['one']['result'] - assert ret['three']['result'] is True, ret['three']['result'] + assert ret["one"]["result"] is None, ret["one"]["result"] + assert ret["three"]["result"] is True, ret["three"]["result"] # The first file state should have changes, since a new file was # created. The other one should not, since we already created that file # before applying the SLS file. - assert ret['one']['changes'] - assert not ret['three']['changes'], ret['three']['changes'] + assert ret["one"]["changes"] + assert not ret["three"]["changes"], ret["three"]["changes"] # The state watching 'one' should have been run due to changes - assert ret['two']['comment'] == 'Success!', ret['two']['comment'] + assert ret["two"]["comment"] == "Success!", ret["two"]["comment"] # The state watching 'three' should not have been run - assert ret['four']['comment'] == \ - 'State was not run because none of the onchanges reqs changed', \ - ret['four']['comment'] + assert ( + ret["four"]["comment"] + == "State was not run because none of the onchanges reqs changed" + ), ret["four"]["comment"] @with_tempfile(create=False) @with_tempfile(create=False) def test_file_managed_prereq(self, file1, file2): - ''' + """ Test file.managed state with prereq - ''' - pillar = {'file1': file1, - 'file2': file2, - 'source': 'salt://testfile', - 'req': 'prereq'} + """ + pillar = { + "file1": file1, + "file2": file2, + "source": "salt://testfile", + "req": "prereq", + } # Lay down the file used in the below SLS to ensure that when it is # run, there are no changes. - self.run_state( - 'file.managed', - name=pillar['file2'], - source=pillar['source']) + self.run_state("file.managed", name=pillar["file2"], source=pillar["source"]) ret = self.repack_state_returns( self.run_function( - 'state.apply', - mods='onchanges_prereq', - pillar=pillar, - test=True, + "state.apply", mods="onchanges_prereq", pillar=pillar, test=True, ) ) # The file states should both exit with None - assert ret['one']['result'] is None, ret['one']['result'] - assert ret['three']['result'] is True, ret['three']['result'] + assert ret["one"]["result"] is None, ret["one"]["result"] + assert ret["three"]["result"] is True, ret["three"]["result"] # The first file state should have changes, since a new file was # created. The other one should not, since we already created that file # before applying the SLS file. - assert ret['one']['changes'] - assert not ret['three']['changes'], ret['three']['changes'] + assert ret["one"]["changes"] + assert not ret["three"]["changes"], ret["three"]["changes"] # The state watching 'one' should have been run due to changes - assert ret['two']['comment'] == 'Success!', ret['two']['comment'] + assert ret["two"]["comment"] == "Success!", ret["two"]["comment"] # The state watching 'three' should not have been run - assert ret['four']['comment'] == 'No changes detected', \ - ret['four']['comment'] + assert ret["four"]["comment"] == "No changes detected", ret["four"]["comment"] def test_directory(self): - ''' + """ file.directory - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'a_new_dir') - ret = self.run_state('file.directory', name=name) + """ + name = os.path.join(RUNTIME_VARS.TMP, "a_new_dir") + ret = self.run_state("file.directory", name=name) self.assertSaltTrueReturn(ret) self.assertTrue(os.path.isdir(name)) def test_directory_symlink_dry_run(self): - ''' + """ Ensure that symlinks are followed when file.directory is run with test=True - ''' + """ try: - tmp_dir = os.path.join(RUNTIME_VARS.TMP, 'pgdata') - sym_dir = os.path.join(RUNTIME_VARS.TMP, 'pg_data') + tmp_dir = os.path.join(RUNTIME_VARS.TMP, "pgdata") + sym_dir = os.path.join(RUNTIME_VARS.TMP, "pg_data") os.mkdir(tmp_dir, 0o700) - self.run_function('file.symlink', [tmp_dir, sym_dir]) + self.run_function("file.symlink", [tmp_dir, sym_dir]) if IS_WINDOWS: ret = self.run_state( - 'file.directory', test=True, name=sym_dir, - follow_symlinks=True, win_owner='Administrators') + "file.directory", + test=True, + name=sym_dir, + follow_symlinks=True, + win_owner="Administrators", + ) else: ret = self.run_state( - 'file.directory', test=True, name=sym_dir, - follow_symlinks=True, mode=700) + "file.directory", + test=True, + name=sym_dir, + follow_symlinks=True, + mode=700, + ) self.assertSaltTrueReturn(ret) finally: if os.path.isdir(tmp_dir): - self.run_function('file.remove', [tmp_dir]) + self.run_function("file.remove", [tmp_dir]) if os.path.islink(sym_dir): - self.run_function('file.remove', [sym_dir]) + self.run_function("file.remove", [sym_dir]) @skip_if_not_root - @skipIf(IS_WINDOWS, 'Mode not available in Windows') + @skipIf(IS_WINDOWS, "Mode not available in Windows") def test_directory_max_depth(self): - ''' + """ file.directory Test the max_depth option by iteratively increasing the depth and checking that no changes deeper than max_depth have been attempted - ''' + """ def _get_oct_mode(name): - ''' + """ Return a string octal representation of the permissions for name - ''' + """ return salt.utils.files.normalize_mode(oct(os.stat(name).st_mode & 0o777)) - top = os.path.join(RUNTIME_VARS.TMP, 'top_dir') - sub = os.path.join(top, 'sub_dir') - subsub = os.path.join(sub, 'sub_sub_dir') + top = os.path.join(RUNTIME_VARS.TMP, "top_dir") + sub = os.path.join(top, "sub_dir") + subsub = os.path.join(sub, "sub_sub_dir") dirs = [top, sub, subsub] - initial_mode = '0111' - changed_mode = '0555' + initial_mode = "0111" + changed_mode = "0555" - initial_modes = {0: {sub: '0755', - subsub: '0111'}, - 1: {sub: '0111', - subsub: '0111'}, - 2: {sub: '0111', - subsub: '0111'}} + initial_modes = { + 0: {sub: "0755", subsub: "0111"}, + 1: {sub: "0111", subsub: "0111"}, + 2: {sub: "0111", subsub: "0111"}, + } if not os.path.isdir(subsub): os.makedirs(subsub, int(initial_mode, 8)) try: for depth in range(0, 3): - ret = self.run_state('file.directory', - name=top, - max_depth=depth, - dir_mode=changed_mode, - recurse=['mode']) + ret = self.run_state( + "file.directory", + name=top, + max_depth=depth, + dir_mode=changed_mode, + recurse=["mode"], + ) self.assertSaltTrueReturn(ret) - for changed_dir in dirs[0:depth+1]: - self.assertEqual(changed_mode, - _get_oct_mode(changed_dir)) - for untouched_dir in dirs[depth+1:]: + for changed_dir in dirs[0 : depth + 1]: + self.assertEqual(changed_mode, _get_oct_mode(changed_dir)) + for untouched_dir in dirs[depth + 1 :]: # Beginning in Python 3.7, os.makedirs no longer sets # the mode of intermediate directories to the mode that # is passed. if sys.version_info >= (3, 7): _mode = initial_modes[depth][untouched_dir] - self.assertEqual(_mode, - _get_oct_mode(untouched_dir)) + self.assertEqual(_mode, _get_oct_mode(untouched_dir)) else: - self.assertEqual(initial_mode, - _get_oct_mode(untouched_dir)) + self.assertEqual(initial_mode, _get_oct_mode(untouched_dir)) finally: shutil.rmtree(top) def test_test_directory(self): - ''' + """ file.directory - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'a_not_dir') - ret = self.run_state('file.directory', test=True, name=name) + """ + name = os.path.join(RUNTIME_VARS.TMP, "a_not_dir") + ret = self.run_state("file.directory", test=True, name=name) self.assertSaltNoneReturn(ret) self.assertFalse(os.path.isdir(name)) @with_tempdir() def test_directory_clean(self, base_dir): - ''' + """ file.directory with clean=True - ''' - name = os.path.join(base_dir, 'directory_clean_dir') + """ + name = os.path.join(base_dir, "directory_clean_dir") os.mkdir(name) - strayfile = os.path.join(name, 'strayfile') - with salt.utils.files.fopen(strayfile, 'w'): + strayfile = os.path.join(name, "strayfile") + with salt.utils.files.fopen(strayfile, "w"): pass - straydir = os.path.join(name, 'straydir') + straydir = os.path.join(name, "straydir") if not os.path.isdir(straydir): os.makedirs(straydir) - with salt.utils.files.fopen(os.path.join(straydir, 'strayfile2'), 'w'): + with salt.utils.files.fopen(os.path.join(straydir, "strayfile2"), "w"): pass - ret = self.run_state('file.directory', name=name, clean=True) + ret = self.run_state("file.directory", name=name, clean=True) self.assertSaltTrueReturn(ret) self.assertFalse(os.path.exists(strayfile)) self.assertFalse(os.path.exists(straydir)) self.assertTrue(os.path.isdir(name)) def test_directory_is_idempotent(self): - ''' + """ Ensure the file.directory state produces no changes when rerun. - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'a_dir_twice') + """ + name = os.path.join(RUNTIME_VARS.TMP, "a_dir_twice") if IS_WINDOWS: - username = os.environ.get('USERNAME', 'Administrators') - domain = os.environ.get('USERDOMAIN', '') - fullname = '{0}\\{1}'.format(domain, username) + username = os.environ.get("USERNAME", "Administrators") + domain = os.environ.get("USERDOMAIN", "") + fullname = "{0}\\{1}".format(domain, username) - ret = self.run_state('file.directory', name=name, win_owner=fullname) + ret = self.run_state("file.directory", name=name, win_owner=fullname) else: - ret = self.run_state('file.directory', name=name) + ret = self.run_state("file.directory", name=name) self.assertSaltTrueReturn(ret) if IS_WINDOWS: - ret = self.run_state('file.directory', name=name, win_owner=username) + ret = self.run_state("file.directory", name=name, win_owner=username) else: - ret = self.run_state('file.directory', name=name) + ret = self.run_state("file.directory", name=name) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual(ret, {}) @with_tempdir() def test_directory_clean_exclude(self, base_dir): - ''' + """ file.directory with clean=True and exclude_pat set - ''' - name = os.path.join(base_dir, 'directory_clean_dir') + """ + name = os.path.join(base_dir, "directory_clean_dir") if not os.path.isdir(name): os.makedirs(name) - strayfile = os.path.join(name, 'strayfile') - with salt.utils.files.fopen(strayfile, 'w'): + strayfile = os.path.join(name, "strayfile") + with salt.utils.files.fopen(strayfile, "w"): pass - straydir = os.path.join(name, 'straydir') + straydir = os.path.join(name, "straydir") if not os.path.isdir(straydir): os.makedirs(straydir) - strayfile2 = os.path.join(straydir, 'strayfile2') - with salt.utils.files.fopen(strayfile2, 'w'): + strayfile2 = os.path.join(straydir, "strayfile2") + with salt.utils.files.fopen(strayfile2, "w"): pass - keepfile = os.path.join(straydir, 'keepfile') - with salt.utils.files.fopen(keepfile, 'w'): + keepfile = os.path.join(straydir, "keepfile") + with salt.utils.files.fopen(keepfile, "w"): pass - exclude_pat = 'E@^straydir(|/keepfile)$' + exclude_pat = "E@^straydir(|/keepfile)$" if IS_WINDOWS: - exclude_pat = 'E@^straydir(|\\\\keepfile)$' + exclude_pat = "E@^straydir(|\\\\keepfile)$" - ret = self.run_state('file.directory', - name=name, - clean=True, - exclude_pat=exclude_pat) + ret = self.run_state( + "file.directory", name=name, clean=True, exclude_pat=exclude_pat + ) self.assertSaltTrueReturn(ret) self.assertFalse(os.path.exists(strayfile)) self.assertFalse(os.path.exists(strayfile2)) self.assertTrue(os.path.exists(keepfile)) - @skipIf(IS_WINDOWS, 'Skip on windows') + @skipIf(IS_WINDOWS, "Skip on windows") @with_tempdir() def test_test_directory_clean_exclude(self, base_dir): - ''' + """ file.directory with test=True, clean=True and exclude_pat set Skipped on windows because clean and exclude_pat not supported by salt.sates.file._check_directory_win - ''' - name = os.path.join(base_dir, 'directory_clean_dir') + """ + name = os.path.join(base_dir, "directory_clean_dir") os.mkdir(name) - strayfile = os.path.join(name, 'strayfile') - with salt.utils.files.fopen(strayfile, 'w'): + strayfile = os.path.join(name, "strayfile") + with salt.utils.files.fopen(strayfile, "w"): pass - straydir = os.path.join(name, 'straydir') + straydir = os.path.join(name, "straydir") if not os.path.isdir(straydir): os.makedirs(straydir) - strayfile2 = os.path.join(straydir, 'strayfile2') - with salt.utils.files.fopen(strayfile2, 'w'): + strayfile2 = os.path.join(straydir, "strayfile2") + with salt.utils.files.fopen(strayfile2, "w"): pass - keepfile = os.path.join(straydir, 'keepfile') - with salt.utils.files.fopen(keepfile, 'w'): + keepfile = os.path.join(straydir, "keepfile") + with salt.utils.files.fopen(keepfile, "w"): pass - exclude_pat = 'E@^straydir(|/keepfile)$' + exclude_pat = "E@^straydir(|/keepfile)$" if IS_WINDOWS: - exclude_pat = 'E@^straydir(|\\\\keepfile)$' + exclude_pat = "E@^straydir(|\\\\keepfile)$" - ret = self.run_state('file.directory', - test=True, - name=name, - clean=True, - exclude_pat=exclude_pat) + ret = self.run_state( + "file.directory", test=True, name=name, clean=True, exclude_pat=exclude_pat + ) - comment = next(six.itervalues(ret))['comment'] + comment = next(six.itervalues(ret))["comment"] self.assertSaltNoneReturn(ret) self.assertTrue(os.path.exists(strayfile)) @@ -1207,11 +1200,11 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @with_tempdir() def test_directory_clean_require_in(self, name): - ''' + """ file.directory test with clean=True and require_in file - ''' - state_name = 'file-FileTest-test_directory_clean_require_in' - state_filename = state_name + '.sls' + """ + state_name = "file-FileTest-test_directory_clean_require_in" + state_filename = state_name + ".sls" state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) wrong_file = os.path.join(name, "wrong") @@ -1219,9 +1212,11 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): fp.write("foo") good_file = os.path.join(name, "bar") - with salt.utils.files.fopen(state_file, 'w') as fp: + with salt.utils.files.fopen(state_file, "w") as fp: self.addCleanup(lambda: os.remove(state_file)) - fp.write(textwrap.dedent('''\ + fp.write( + textwrap.dedent( + """\ some_dir: file.directory: - name: {name} @@ -1231,20 +1226,24 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): file.managed: - require_in: - file: some_dir - '''.format(name=name, good_file=good_file))) + """.format( + name=name, good_file=good_file + ) + ) + ) - ret = self.run_function('state.sls', [state_name]) + ret = self.run_function("state.sls", [state_name]) self.assertTrue(os.path.exists(good_file)) self.assertFalse(os.path.exists(wrong_file)) @with_tempdir() def test_directory_clean_require_in_with_id(self, name): - ''' + """ file.directory test with clean=True and require_in file with an ID different from the file name - ''' - state_name = 'file-FileTest-test_directory_clean_require_in_with_id' - state_filename = state_name + '.sls' + """ + state_name = "file-FileTest-test_directory_clean_require_in_with_id" + state_filename = state_name + ".sls" state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) wrong_file = os.path.join(name, "wrong") @@ -1252,9 +1251,11 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): fp.write("foo") good_file = os.path.join(name, "bar") - with salt.utils.files.fopen(state_file, 'w') as fp: + with salt.utils.files.fopen(state_file, "w") as fp: self.addCleanup(lambda: os.remove(state_file)) - fp.write(textwrap.dedent('''\ + fp.write( + textwrap.dedent( + """\ some_dir: file.directory: - name: {name} @@ -1265,21 +1266,28 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): - name: {good_file} - require_in: - file: some_dir - '''.format(name=name, good_file=good_file))) + """.format( + name=name, good_file=good_file + ) + ) + ) - ret = self.run_function('state.sls', [state_name]) + ret = self.run_function("state.sls", [state_name]) self.assertTrue(os.path.exists(good_file)) self.assertFalse(os.path.exists(wrong_file)) - @skipIf(salt.utils.platform.is_darwin(), 'WAR ROOM TEMPORARY SKIP, Test is flaky on macosx') + @skipIf( + salt.utils.platform.is_darwin(), + "WAR ROOM TEMPORARY SKIP, Test is flaky on macosx", + ) @with_tempdir() def test_directory_clean_require_with_name(self, name): - ''' + """ file.directory test with clean=True and require with a file state relatively to the state's name, not its ID. - ''' - state_name = 'file-FileTest-test_directory_clean_require_in_with_id' - state_filename = state_name + '.sls' + """ + state_name = "file-FileTest-test_directory_clean_require_in_with_id" + state_filename = state_name + ".sls" state_file = os.path.join(RUNTIME_VARS.BASE_FILES, state_filename) wrong_file = os.path.join(name, "wrong") @@ -1287,9 +1295,11 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): fp.write("foo") good_file = os.path.join(name, "bar") - with salt.utils.files.fopen(state_file, 'w') as fp: + with salt.utils.files.fopen(state_file, "w") as fp: self.addCleanup(lambda: os.remove(state_file)) - fp.write(textwrap.dedent('''\ + fp.write( + textwrap.dedent( + """\ some_dir: file.directory: - name: {name} @@ -1302,186 +1312,193 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): some_file: file.managed: - name: {good_file} - '''.format(name=name, good_file=good_file))) + """.format( + name=name, good_file=good_file + ) + ) + ) - ret = self.run_function('state.sls', [state_name]) + ret = self.run_function("state.sls", [state_name]) self.assertTrue(os.path.exists(good_file)) self.assertFalse(os.path.exists(wrong_file)) def test_directory_broken_symlink(self): - ''' + """ Ensure that file.directory works even if a directory contains broken symbolic link - ''' + """ try: - tmp_dir = os.path.join(RUNTIME_VARS.TMP, 'foo') - null_file = '{0}/null'.format(tmp_dir) - broken_link = '{0}/broken'.format(tmp_dir) + tmp_dir = os.path.join(RUNTIME_VARS.TMP, "foo") + null_file = "{0}/null".format(tmp_dir) + broken_link = "{0}/broken".format(tmp_dir) os.mkdir(tmp_dir, 0o700) - self.run_function('file.symlink', [null_file, broken_link]) + self.run_function("file.symlink", [null_file, broken_link]) if IS_WINDOWS: ret = self.run_state( - 'file.directory', name=tmp_dir, recurse=['mode'], - follow_symlinks=True, win_owner='Administrators') + "file.directory", + name=tmp_dir, + recurse=["mode"], + follow_symlinks=True, + win_owner="Administrators", + ) else: ret = self.run_state( - 'file.directory', name=tmp_dir, recurse=['mode'], - file_mode=644, dir_mode=755) + "file.directory", + name=tmp_dir, + recurse=["mode"], + file_mode=644, + dir_mode=755, + ) self.assertSaltTrueReturn(ret) finally: if os.path.isdir(tmp_dir): - self.run_function('file.remove', [tmp_dir]) + self.run_function("file.remove", [tmp_dir]) @with_tempdir(create=False) def test_recurse(self, name): - ''' + """ file.recurse - ''' - ret = self.run_state('file.recurse', name=name, source='salt://grail') + """ + ret = self.run_state("file.recurse", name=name, source="salt://grail") self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isfile(os.path.join(name, '36', 'scene'))) + self.assertTrue(os.path.isfile(os.path.join(name, "36", "scene"))) @with_tempdir(create=False) @with_tempdir(create=False) def test_recurse_specific_env(self, dir1, dir2): - ''' + """ file.recurse passing __env__ - ''' - ret = self.run_state('file.recurse', - name=dir1, - source='salt://holy', - __env__='prod') + """ + ret = self.run_state( + "file.recurse", name=dir1, source="salt://holy", __env__="prod" + ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isfile(os.path.join(dir1, '32', 'scene'))) + self.assertTrue(os.path.isfile(os.path.join(dir1, "32", "scene"))) - ret = self.run_state('file.recurse', - name=dir2, - source='salt://holy', - saltenv='prod') + ret = self.run_state( + "file.recurse", name=dir2, source="salt://holy", saltenv="prod" + ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isfile(os.path.join(dir2, '32', 'scene'))) + self.assertTrue(os.path.isfile(os.path.join(dir2, "32", "scene"))) @with_tempdir(create=False) @with_tempdir(create=False) def test_recurse_specific_env_in_url(self, dir1, dir2): - ''' + """ file.recurse passing __env__ - ''' - ret = self.run_state('file.recurse', - name=dir1, - source='salt://holy?saltenv=prod') + """ + ret = self.run_state( + "file.recurse", name=dir1, source="salt://holy?saltenv=prod" + ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isfile(os.path.join(dir1, '32', 'scene'))) + self.assertTrue(os.path.isfile(os.path.join(dir1, "32", "scene"))) - ret = self.run_state('file.recurse', - name=dir2, - source='salt://holy?saltenv=prod') + ret = self.run_state( + "file.recurse", name=dir2, source="salt://holy?saltenv=prod" + ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isfile(os.path.join(dir2, '32', 'scene'))) + self.assertTrue(os.path.isfile(os.path.join(dir2, "32", "scene"))) @with_tempdir(create=False) def test_test_recurse(self, name): - ''' + """ file.recurse test interface - ''' + """ ret = self.run_state( - 'file.recurse', test=True, name=name, source='salt://grail', + "file.recurse", test=True, name=name, source="salt://grail", ) self.assertSaltNoneReturn(ret) - self.assertFalse(os.path.isfile(os.path.join(name, '36', 'scene'))) + self.assertFalse(os.path.isfile(os.path.join(name, "36", "scene"))) self.assertFalse(os.path.exists(name)) @with_tempdir(create=False) @with_tempdir(create=False) def test_test_recurse_specific_env(self, dir1, dir2): - ''' + """ file.recurse test interface - ''' - ret = self.run_state('file.recurse', - test=True, - name=dir1, - source='salt://holy', - __env__='prod' + """ + ret = self.run_state( + "file.recurse", test=True, name=dir1, source="salt://holy", __env__="prod" ) self.assertSaltNoneReturn(ret) - self.assertFalse(os.path.isfile(os.path.join(dir1, '32', 'scene'))) + self.assertFalse(os.path.isfile(os.path.join(dir1, "32", "scene"))) self.assertFalse(os.path.exists(dir1)) - ret = self.run_state('file.recurse', - test=True, - name=dir2, - source='salt://holy', - saltenv='prod' + ret = self.run_state( + "file.recurse", test=True, name=dir2, source="salt://holy", saltenv="prod" ) self.assertSaltNoneReturn(ret) - self.assertFalse(os.path.isfile(os.path.join(dir2, '32', 'scene'))) + self.assertFalse(os.path.isfile(os.path.join(dir2, "32", "scene"))) self.assertFalse(os.path.exists(dir2)) @with_tempdir(create=False) def test_recurse_template(self, name): - ''' + """ file.recurse with jinja template enabled - ''' - _ts = 'TEMPLATE TEST STRING' + """ + _ts = "TEMPLATE TEST STRING" ret = self.run_state( - 'file.recurse', name=name, source='salt://grail', - template='jinja', defaults={'spam': _ts}) + "file.recurse", + name=name, + source="salt://grail", + template="jinja", + defaults={"spam": _ts}, + ) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(os.path.join(name, 'scene33'), 'r') as fp_: + with salt.utils.files.fopen(os.path.join(name, "scene33"), "r") as fp_: contents = fp_.read() self.assertIn(_ts, contents) @with_tempdir() def test_recurse_clean(self, name): - ''' + """ file.recurse with clean=True - ''' - strayfile = os.path.join(name, 'strayfile') - with salt.utils.files.fopen(strayfile, 'w'): + """ + strayfile = os.path.join(name, "strayfile") + with salt.utils.files.fopen(strayfile, "w"): pass # Corner cases: replacing file with a directory and vice versa - with salt.utils.files.fopen(os.path.join(name, '36'), 'w'): + with salt.utils.files.fopen(os.path.join(name, "36"), "w"): pass - os.makedirs(os.path.join(name, 'scene33')) + os.makedirs(os.path.join(name, "scene33")) ret = self.run_state( - 'file.recurse', name=name, source='salt://grail', clean=True) + "file.recurse", name=name, source="salt://grail", clean=True + ) self.assertSaltTrueReturn(ret) self.assertFalse(os.path.exists(strayfile)) - self.assertTrue(os.path.isfile(os.path.join(name, '36', 'scene'))) - self.assertTrue(os.path.isfile(os.path.join(name, 'scene33'))) + self.assertTrue(os.path.isfile(os.path.join(name, "36", "scene"))) + self.assertTrue(os.path.isfile(os.path.join(name, "scene33"))) @with_tempdir() def test_recurse_clean_specific_env(self, name): - ''' + """ file.recurse with clean=True and __env__=prod - ''' - strayfile = os.path.join(name, 'strayfile') - with salt.utils.files.fopen(strayfile, 'w'): + """ + strayfile = os.path.join(name, "strayfile") + with salt.utils.files.fopen(strayfile, "w"): pass # Corner cases: replacing file with a directory and vice versa - with salt.utils.files.fopen(os.path.join(name, '32'), 'w'): + with salt.utils.files.fopen(os.path.join(name, "32"), "w"): pass - os.makedirs(os.path.join(name, 'scene34')) - ret = self.run_state('file.recurse', - name=name, - source='salt://holy', - clean=True, - __env__='prod') + os.makedirs(os.path.join(name, "scene34")) + ret = self.run_state( + "file.recurse", name=name, source="salt://holy", clean=True, __env__="prod" + ) self.assertSaltTrueReturn(ret) self.assertFalse(os.path.exists(strayfile)) - self.assertTrue(os.path.isfile(os.path.join(name, '32', 'scene'))) - self.assertTrue(os.path.isfile(os.path.join(name, 'scene34'))) + self.assertTrue(os.path.isfile(os.path.join(name, "32", "scene"))) + self.assertTrue(os.path.isfile(os.path.join(name, "scene34"))) - @skipIf(IS_WINDOWS, 'Skip on windows') + @skipIf(IS_WINDOWS, "Skip on windows") @with_tempdir() def test_recurse_issue_34945(self, base_dir): - ''' + """ This tests the case where the source dir for the file.recurse state does not contain any files (only subdirectories), and the dir_mode is being managed. For a long time, this corner case resulted in the top @@ -1496,61 +1513,58 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): This was fixed in https://github.com/saltstack/salt/pull/35309 Skipped on windows because dir_mode is not supported. - ''' - dir_mode = '2775' - issue_dir = 'issue-34945' + """ + dir_mode = "2775" + issue_dir = "issue-34945" name = os.path.join(base_dir, issue_dir) - ret = self.run_state('file.recurse', - name=name, - source='salt://' + issue_dir, - dir_mode=dir_mode) + ret = self.run_state( + "file.recurse", name=name, source="salt://" + issue_dir, dir_mode=dir_mode + ) self.assertSaltTrueReturn(ret) actual_dir_mode = oct(stat.S_IMODE(os.stat(name).st_mode))[-4:] self.assertEqual(dir_mode, actual_dir_mode) @with_tempdir(create=False) def test_recurse_issue_40578(self, name): - ''' + """ This ensures that the state doesn't raise an exception when it encounters a file with a unicode filename in the process of invoking file.source_list. - ''' - ret = self.run_state('file.recurse', - name=name, - source='salt://соль') + """ + ret = self.run_state("file.recurse", name=name, source="salt://соль") self.assertSaltTrueReturn(ret) if six.PY2 and IS_WINDOWS: # Providing unicode to os.listdir so that we avoid having listdir # try to decode the filenames using the systemencoding on windows # python 2. - files = os.listdir(name.decode('utf-8')) + files = os.listdir(name.decode("utf-8")) else: files = salt.utils.data.decode(os.listdir(name), normalize=True) self.assertEqual( - sorted(files), - sorted(['foo.txt', 'спам.txt', 'яйца.txt']), + sorted(files), sorted(["foo.txt", "спам.txt", "яйца.txt"]), ) @with_tempfile() def test_replace(self, name): - ''' + """ file.replace - ''' - with salt.utils.files.fopen(name, 'w+') as fp_: - fp_.write('change_me') + """ + with salt.utils.files.fopen(name, "w+") as fp_: + fp_.write("change_me") - ret = self.run_state('file.replace', - name=name, pattern='change', repl='salt', backup=False) + ret = self.run_state( + "file.replace", name=name, pattern="change", repl="salt", backup=False + ) - with salt.utils.files.fopen(name, 'r') as fp_: - self.assertIn('salt', fp_.read()) + with salt.utils.files.fopen(name, "r") as fp_: + self.assertIn("salt", fp_.read()) self.assertSaltTrueReturn(ret) @with_tempdir() def test_replace_issue_18612(self, base_dir): - ''' + """ Test the (mis-)behaviour of file.replace as described in #18612: Using 'prepend_if_not_found' or 'append_if_not_found' resulted in @@ -1561,25 +1575,32 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): The tested file contains one commented line The commented line should be uncommented in the end, nothing else should change - ''' - test_name = 'test_replace_issue_18612' + """ + test_name = "test_replace_issue_18612" path_test = os.path.join(base_dir, test_name) - with salt.utils.files.fopen(path_test, 'w+') as fp_test_: - fp_test_.write('# en_US.UTF-8') + with salt.utils.files.fopen(path_test, "w+") as fp_test_: + fp_test_.write("# en_US.UTF-8") ret = [] for x in range(0, 3): - ret.append(self.run_state('file.replace', - name=path_test, pattern='^# en_US.UTF-8$', repl='en_US.UTF-8', append_if_not_found=True)) + ret.append( + self.run_state( + "file.replace", + name=path_test, + pattern="^# en_US.UTF-8$", + repl="en_US.UTF-8", + append_if_not_found=True, + ) + ) # ensure, the number of lines didn't change, even after invoking 'file.replace' 3 times - with salt.utils.files.fopen(path_test, 'r') as fp_test_: + with salt.utils.files.fopen(path_test, "r") as fp_test_: self.assertTrue((sum(1 for _ in fp_test_) == 1)) # ensure, the replacement succeeded - with salt.utils.files.fopen(path_test, 'r') as fp_test_: - self.assertTrue(fp_test_.read().startswith('en_US.UTF-8')) + with salt.utils.files.fopen(path_test, "r") as fp_test_: + self.assertTrue(fp_test_.read().startswith("en_US.UTF-8")) # ensure, all runs of 'file.replace' reported success for item in ret: @@ -1587,7 +1608,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @with_tempdir() def test_replace_issue_18612_prepend(self, base_dir): - ''' + """ Test the (mis-)behaviour of file.replace as described in #18612: Using 'prepend_if_not_found' or 'append_if_not_found' resulted in @@ -1598,13 +1619,13 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): The tested multifile contains multiple lines not matching the pattern or replacement in any way The replacement pattern should be prepended to the file - ''' - test_name = 'test_replace_issue_18612_prepend' + """ + test_name = "test_replace_issue_18612_prepend" path_in = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.in".format(test_name) ) path_out = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.out'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.out".format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1613,14 +1634,21 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): ret = [] for x in range(0, 3): - ret.append(self.run_state('file.replace', - name=path_test, pattern='^# en_US.UTF-8$', repl='en_US.UTF-8', prepend_if_not_found=True)) + ret.append( + self.run_state( + "file.replace", + name=path_test, + pattern="^# en_US.UTF-8$", + repl="en_US.UTF-8", + prepend_if_not_found=True, + ) + ) # ensure, the resulting file contains the expected lines self.assertTrue(filecmp.cmp(path_test, path_out)) # ensure the initial file was properly backed up - self.assertTrue(filecmp.cmp(path_test + '.bak', path_in)) + self.assertTrue(filecmp.cmp(path_test + ".bak", path_in)) # ensure, all runs of 'file.replace' reported success for item in ret: @@ -1628,7 +1656,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @with_tempdir() def test_replace_issue_18612_append(self, base_dir): - ''' + """ Test the (mis-)behaviour of file.replace as described in #18612: Using 'prepend_if_not_found' or 'append_if_not_found' resulted in @@ -1639,13 +1667,13 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): The tested multifile contains multiple lines not matching the pattern or replacement in any way The replacement pattern should be appended to the file - ''' - test_name = 'test_replace_issue_18612_append' + """ + test_name = "test_replace_issue_18612_append" path_in = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.in".format(test_name) ) path_out = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.out'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.out".format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1654,14 +1682,21 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): ret = [] for x in range(0, 3): - ret.append(self.run_state('file.replace', - name=path_test, pattern='^# en_US.UTF-8$', repl='en_US.UTF-8', append_if_not_found=True)) + ret.append( + self.run_state( + "file.replace", + name=path_test, + pattern="^# en_US.UTF-8$", + repl="en_US.UTF-8", + append_if_not_found=True, + ) + ) # ensure, the resulting file contains the expected lines self.assertTrue(filecmp.cmp(path_test, path_out)) # ensure the initial file was properly backed up - self.assertTrue(filecmp.cmp(path_test + '.bak', path_in)) + self.assertTrue(filecmp.cmp(path_test + ".bak", path_in)) # ensure, all runs of 'file.replace' reported success for item in ret: @@ -1669,7 +1704,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @with_tempdir() def test_replace_issue_18612_append_not_found_content(self, base_dir): - ''' + """ Test the (mis-)behaviour of file.replace as described in #18612: Using 'prepend_if_not_found' or 'append_if_not_found' resulted in @@ -1680,13 +1715,13 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): The tested multifile contains multiple lines not matching the pattern or replacement in any way The 'not_found_content' value should be appended to the file - ''' - test_name = 'test_replace_issue_18612_append_not_found_content' + """ + test_name = "test_replace_issue_18612_append_not_found_content" path_in = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.in".format(test_name) ) path_out = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.out'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.out".format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1696,19 +1731,21 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): ret = [] for x in range(0, 3): ret.append( - self.run_state('file.replace', + self.run_state( + "file.replace", name=path_test, - pattern='^# en_US.UTF-8$', - repl='en_US.UTF-8', + pattern="^# en_US.UTF-8$", + repl="en_US.UTF-8", append_if_not_found=True, - not_found_content='THIS LINE WASN\'T FOUND! SO WE\'RE APPENDING IT HERE!' - )) + not_found_content="THIS LINE WASN'T FOUND! SO WE'RE APPENDING IT HERE!", + ) + ) # ensure, the resulting file contains the expected lines self.assertTrue(filecmp.cmp(path_test, path_out)) # ensure the initial file was properly backed up - self.assertTrue(filecmp.cmp(path_test + '.bak', path_in)) + self.assertTrue(filecmp.cmp(path_test + ".bak", path_in)) # ensure, all runs of 'file.replace' reported success for item in ret: @@ -1716,7 +1753,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @with_tempdir() def test_replace_issue_18612_change_mid_line_with_comment(self, base_dir): - ''' + """ Test the (mis-)behaviour of file.replace as described in #18612: Using 'prepend_if_not_found' or 'append_if_not_found' resulted in @@ -1728,13 +1765,13 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): The tested file contains 5 key=value pairs The commented key=value pair #foo=bar should be changed to foo=salt The comment char (#) in front of foo=bar should be removed - ''' - test_name = 'test_replace_issue_18612_change_mid_line_with_comment' + """ + test_name = "test_replace_issue_18612_change_mid_line_with_comment" path_in = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.in".format(test_name) ) path_out = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.out'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.out".format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1743,14 +1780,21 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): ret = [] for x in range(0, 3): - ret.append(self.run_state('file.replace', - name=path_test, pattern='^#foo=bar($|(?=\r\n))', repl='foo=salt', append_if_not_found=True)) + ret.append( + self.run_state( + "file.replace", + name=path_test, + pattern="^#foo=bar($|(?=\r\n))", + repl="foo=salt", + append_if_not_found=True, + ) + ) # ensure, the resulting file contains the expected lines self.assertTrue(filecmp.cmp(path_test, path_out)) # ensure the initial file was properly backed up - self.assertTrue(filecmp.cmp(path_test + '.bak', path_in)) + self.assertTrue(filecmp.cmp(path_test + ".bak", path_in)) # ensure, all 'file.replace' runs reported success for item in ret: @@ -1758,7 +1802,7 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @with_tempdir() def test_replace_issue_18841_no_changes(self, base_dir): - ''' + """ Test the (mis-)behaviour of file.replace as described in #18841: Using file.replace in a way which shouldn't modify the file at all @@ -1771,10 +1815,10 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): The tested file's content shouldn't change at all The tested file's mtime shouldn't change at all No backup file should be created - ''' - test_name = 'test_replace_issue_18841_no_changes' + """ + test_name = "test_replace_issue_18841_no_changes" path_in = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.in".format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1785,18 +1829,19 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): fstats_orig = os.stat(path_test) # define how far we predate the file - age = 5*24*60*60 + age = 5 * 24 * 60 * 60 # set (m|a)time of file 5 days into the past - os.utime(path_test, (fstats_orig.st_mtime-age, fstats_orig.st_atime-age)) + os.utime(path_test, (fstats_orig.st_mtime - age, fstats_orig.st_atime - age)) - ret = self.run_state('file.replace', + ret = self.run_state( + "file.replace", name=path_test, - pattern='^hello world$', - repl='goodbye world', + pattern="^hello world$", + repl="goodbye world", show_changes=True, - flags=['IGNORECASE'], - backup=False + flags=["IGNORECASE"], + backup=False, ) # get (m|a)time of file @@ -1806,97 +1851,105 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): self.assertTrue(filecmp.cmp(path_in, path_test)) # ensure no backup file was created - self.assertFalse(os.path.exists(path_test + '.bak')) + self.assertFalse(os.path.exists(path_test + ".bak")) # ensure the file's mtime didn't change - self.assertTrue(fstats_post.st_mtime, fstats_orig.st_mtime-age) + self.assertTrue(fstats_post.st_mtime, fstats_orig.st_mtime - age) # ensure, all 'file.replace' runs reported success self.assertSaltTrueReturn(ret) def test_serialize(self): - ''' + """ Test to ensure that file.serialize returns a data structure that's both serialized and formatted properly - ''' - path_test = os.path.join(RUNTIME_VARS.TMP, 'test_serialize') - ret = self.run_state('file.serialize', - name=path_test, - dataset={'name': 'naive', - 'description': 'A basic test', - 'a_list': ['first_element', 'second_element'], - 'finally': 'the last item'}, - formatter='json') + """ + path_test = os.path.join(RUNTIME_VARS.TMP, "test_serialize") + ret = self.run_state( + "file.serialize", + name=path_test, + dataset={ + "name": "naive", + "description": "A basic test", + "a_list": ["first_element", "second_element"], + "finally": "the last item", + }, + formatter="json", + ) - with salt.utils.files.fopen(path_test, 'rb') as fp_: + with salt.utils.files.fopen(path_test, "rb") as fp_: serialized_file = salt.utils.stringutils.to_unicode(fp_.read()) # The JSON serializer uses LF even on OSes where os.sep is CRLF. - expected_file = '\n'.join([ - '{', - ' "a_list": [', - ' "first_element",', - ' "second_element"', - ' ],', - ' "description": "A basic test",', - ' "finally": "the last item",', - ' "name": "naive"', - '}', - '', - ]) + expected_file = "\n".join( + [ + "{", + ' "a_list": [', + ' "first_element",', + ' "second_element"', + " ],", + ' "description": "A basic test",', + ' "finally": "the last item",', + ' "name": "naive"', + "}", + "", + ] + ) self.assertEqual(serialized_file, expected_file) @with_tempfile(create=False) def test_serializer_deserializer_opts(self, name): - ''' + """ Test the serializer_opts and deserializer_opts options - ''' - data1 = {'foo': {'bar': '%(x)s'}} - data2 = {'foo': {'abc': 123}} - merged = {'foo': {'y': 'not_used', 'x': 'baz', 'abc': 123, 'bar': u'baz'}} + """ + data1 = {"foo": {"bar": "%(x)s"}} + data2 = {"foo": {"abc": 123}} + merged = {"foo": {"y": "not_used", "x": "baz", "abc": 123, "bar": "baz"}} ret = self.run_state( - 'file.serialize', + "file.serialize", name=name, dataset=data1, - formatter='configparser', - deserializer_opts=[{'defaults': {'y': 'not_used'}}]) + formatter="configparser", + deserializer_opts=[{"defaults": {"y": "not_used"}}], + ) ret = ret[next(iter(ret))] - assert ret['result'], ret + assert ret["result"], ret # We should have warned about deserializer_opts being used when # merge_if_exists was not set to True. - assert 'warnings' in ret + assert "warnings" in ret # Run with merge_if_exists, as well as serializer and deserializer opts # deserializer opts will be used for string interpolation of the %(x)s # that was written to the file with data1 (i.e. bar should become baz) ret = self.run_state( - 'file.serialize', + "file.serialize", name=name, dataset=data2, - formatter='configparser', + formatter="configparser", merge_if_exists=True, - serializer_opts=[{'defaults': {'y': 'not_used'}}], - deserializer_opts=[{'defaults': {'x': 'baz'}}]) + serializer_opts=[{"defaults": {"y": "not_used"}}], + deserializer_opts=[{"defaults": {"x": "baz"}}], + ) ret = ret[next(iter(ret))] - assert ret['result'], ret + assert ret["result"], ret with salt.utils.files.fopen(name) as fp_: serialized_data = salt.serializers.configparser.deserialize(fp_) # If this test fails, this debug logging will help tell us how the # serialized data differs from what was serialized. - log.debug('serialized_data = %r', serialized_data) - log.debug('merged = %r', merged) + log.debug("serialized_data = %r", serialized_data) + log.debug("merged = %r", merged) # serializing with a default of 'y' will add y = not_used into foo - assert serialized_data['foo']['y'] == merged['foo']['y'] + assert serialized_data["foo"]["y"] == merged["foo"]["y"] # deserializing with default of x = baz will perform interpolation on %(x)s # and bar will then = baz - assert serialized_data['foo']['bar'] == merged['foo']['bar'] + assert serialized_data["foo"]["bar"] == merged["foo"]["bar"] @with_tempdir() def test_replace_issue_18841_omit_backup(self, base_dir): - ''' + """ Test the (mis-)behaviour of file.replace as described in #18841: Using file.replace in a way which shouldn't modify the file at all @@ -1909,10 +1962,10 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): The tested file's content shouldn't change at all The tested file's mtime shouldn't change at all No backup file should be created, although backup=False isn't explicitly defined - ''' - test_name = 'test_replace_issue_18841_omit_backup' + """ + test_name = "test_replace_issue_18841_omit_backup" path_in = os.path.join( - RUNTIME_VARS.FILES, 'file.replace', '{0}.in'.format(test_name) + RUNTIME_VARS.FILES, "file.replace", "{0}.in".format(test_name) ) path_test = os.path.join(base_dir, test_name) @@ -1923,17 +1976,18 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): fstats_orig = os.stat(path_test) # define how far we predate the file - age = 5*24*60*60 + age = 5 * 24 * 60 * 60 # set (m|a)time of file 5 days into the past - os.utime(path_test, (fstats_orig.st_mtime-age, fstats_orig.st_atime-age)) + os.utime(path_test, (fstats_orig.st_mtime - age, fstats_orig.st_atime - age)) - ret = self.run_state('file.replace', + ret = self.run_state( + "file.replace", name=path_test, - pattern='^hello world$', - repl='goodbye world', + pattern="^hello world$", + repl="goodbye world", show_changes=True, - flags=['IGNORECASE'] + flags=["IGNORECASE"], ) # get (m|a)time of file @@ -1943,501 +1997,471 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): self.assertTrue(filecmp.cmp(path_in, path_test)) # ensure no backup file was created - self.assertFalse(os.path.exists(path_test + '.bak')) + self.assertFalse(os.path.exists(path_test + ".bak")) # ensure the file's mtime didn't change - self.assertTrue(fstats_post.st_mtime, fstats_orig.st_mtime-age) + self.assertTrue(fstats_post.st_mtime, fstats_orig.st_mtime - age) # ensure, all 'file.replace' runs reported success self.assertSaltTrueReturn(ret) @with_tempfile() def test_comment(self, name): - ''' + """ file.comment - ''' + """ # write a line to file - with salt.utils.files.fopen(name, 'w+') as fp_: - fp_.write('comment_me') + with salt.utils.files.fopen(name, "w+") as fp_: + fp_.write("comment_me") # Look for changes with test=True: return should be "None" at the first run - ret = self.run_state('file.comment', test=True, name=name, regex='^comment') + ret = self.run_state("file.comment", test=True, name=name, regex="^comment") self.assertSaltNoneReturn(ret) # comment once - ret = self.run_state('file.comment', name=name, regex='^comment') + ret = self.run_state("file.comment", name=name, regex="^comment") # result is positive self.assertSaltTrueReturn(ret) # line is commented - with salt.utils.files.fopen(name, 'r') as fp_: - self.assertTrue(fp_.read().startswith('#comment')) + with salt.utils.files.fopen(name, "r") as fp_: + self.assertTrue(fp_.read().startswith("#comment")) # comment twice - ret = self.run_state('file.comment', name=name, regex='^comment') + ret = self.run_state("file.comment", name=name, regex="^comment") # result is still positive self.assertSaltTrueReturn(ret) # line is still commented - with salt.utils.files.fopen(name, 'r') as fp_: - self.assertTrue(fp_.read().startswith('#comment')) + with salt.utils.files.fopen(name, "r") as fp_: + self.assertTrue(fp_.read().startswith("#comment")) # Test previously commented file returns "True" now and not "None" with test=True - ret = self.run_state('file.comment', test=True, name=name, regex='^comment') + ret = self.run_state("file.comment", test=True, name=name, regex="^comment") self.assertSaltTrueReturn(ret) @with_tempfile() def test_test_comment(self, name): - ''' + """ file.comment test interface - ''' - with salt.utils.files.fopen(name, 'w+') as fp_: - fp_.write('comment_me') - ret = self.run_state( - 'file.comment', test=True, name=name, regex='.*comment.*', - ) - with salt.utils.files.fopen(name, 'r') as fp_: - self.assertNotIn('#comment', fp_.read()) + """ + with salt.utils.files.fopen(name, "w+") as fp_: + fp_.write("comment_me") + ret = self.run_state("file.comment", test=True, name=name, regex=".*comment.*",) + with salt.utils.files.fopen(name, "r") as fp_: + self.assertNotIn("#comment", fp_.read()) self.assertSaltNoneReturn(ret) @with_tempfile() def test_uncomment(self, name): - ''' + """ file.uncomment - ''' - with salt.utils.files.fopen(name, 'w+') as fp_: - fp_.write('#comment_me') - ret = self.run_state('file.uncomment', name=name, regex='^comment') - with salt.utils.files.fopen(name, 'r') as fp_: - self.assertNotIn('#comment', fp_.read()) + """ + with salt.utils.files.fopen(name, "w+") as fp_: + fp_.write("#comment_me") + ret = self.run_state("file.uncomment", name=name, regex="^comment") + with salt.utils.files.fopen(name, "r") as fp_: + self.assertNotIn("#comment", fp_.read()) self.assertSaltTrueReturn(ret) @with_tempfile() def test_test_uncomment(self, name): - ''' + """ file.comment test interface - ''' - with salt.utils.files.fopen(name, 'w+') as fp_: - fp_.write('#comment_me') - ret = self.run_state( - 'file.uncomment', test=True, name=name, regex='^comment.*' - ) - with salt.utils.files.fopen(name, 'r') as fp_: - self.assertIn('#comment', fp_.read()) + """ + with salt.utils.files.fopen(name, "w+") as fp_: + fp_.write("#comment_me") + ret = self.run_state("file.uncomment", test=True, name=name, regex="^comment.*") + with salt.utils.files.fopen(name, "r") as fp_: + self.assertIn("#comment", fp_.read()) self.assertSaltNoneReturn(ret) @with_tempfile() def test_append(self, name): - ''' + """ file.append - ''' - with salt.utils.files.fopen(name, 'w+') as fp_: - fp_.write('#salty!') - ret = self.run_state('file.append', name=name, text='cheese') - with salt.utils.files.fopen(name, 'r') as fp_: - self.assertIn('cheese', fp_.read()) + """ + with salt.utils.files.fopen(name, "w+") as fp_: + fp_.write("#salty!") + ret = self.run_state("file.append", name=name, text="cheese") + with salt.utils.files.fopen(name, "r") as fp_: + self.assertIn("cheese", fp_.read()) self.assertSaltTrueReturn(ret) @with_tempfile() def test_test_append(self, name): - ''' + """ file.append test interface - ''' - with salt.utils.files.fopen(name, 'w+') as fp_: - fp_.write('#salty!') - ret = self.run_state( - 'file.append', test=True, name=name, text='cheese' - ) - with salt.utils.files.fopen(name, 'r') as fp_: - self.assertNotIn('cheese', fp_.read()) + """ + with salt.utils.files.fopen(name, "w+") as fp_: + fp_.write("#salty!") + ret = self.run_state("file.append", test=True, name=name, text="cheese") + with salt.utils.files.fopen(name, "r") as fp_: + self.assertNotIn("cheese", fp_.read()) self.assertSaltNoneReturn(ret) @with_tempdir() def test_append_issue_1864_makedirs(self, base_dir): - ''' + """ file.append but create directories if needed as an option, and create the file if it doesn't exist - ''' - fname = 'append_issue_1864_makedirs' + """ + fname = "append_issue_1864_makedirs" name = os.path.join(base_dir, fname) # Non existing file get's touched - ret = self.run_state( - 'file.append', name=name, text='cheese', makedirs=True - ) + ret = self.run_state("file.append", name=name, text="cheese", makedirs=True) self.assertSaltTrueReturn(ret) # Nested directory and file get's touched - name = os.path.join(base_dir, 'issue_1864', fname) - ret = self.run_state( - 'file.append', name=name, text='cheese', makedirs=True - ) + name = os.path.join(base_dir, "issue_1864", fname) + ret = self.run_state("file.append", name=name, text="cheese", makedirs=True) self.assertSaltTrueReturn(ret) # Parent directory exists but file does not and makedirs is False - name = os.path.join(base_dir, 'issue_1864', fname + '2') - ret = self.run_state( - 'file.append', name=name, text='cheese' - ) + name = os.path.join(base_dir, "issue_1864", fname + "2") + ret = self.run_state("file.append", name=name, text="cheese") self.assertSaltTrueReturn(ret) self.assertTrue(os.path.isfile(name)) @with_tempdir() def test_prepend_issue_27401_makedirs(self, base_dir): - ''' + """ file.prepend but create directories if needed as an option, and create the file if it doesn't exist - ''' - fname = 'prepend_issue_27401' + """ + fname = "prepend_issue_27401" name = os.path.join(base_dir, fname) # Non existing file get's touched - ret = self.run_state( - 'file.prepend', name=name, text='cheese', makedirs=True - ) + ret = self.run_state("file.prepend", name=name, text="cheese", makedirs=True) self.assertSaltTrueReturn(ret) # Nested directory and file get's touched - name = os.path.join(base_dir, 'issue_27401', fname) - ret = self.run_state( - 'file.prepend', name=name, text='cheese', makedirs=True - ) + name = os.path.join(base_dir, "issue_27401", fname) + ret = self.run_state("file.prepend", name=name, text="cheese", makedirs=True) self.assertSaltTrueReturn(ret) # Parent directory exists but file does not and makedirs is False - name = os.path.join(base_dir, 'issue_27401', fname + '2') - ret = self.run_state( - 'file.prepend', name=name, text='cheese' - ) + name = os.path.join(base_dir, "issue_27401", fname + "2") + ret = self.run_state("file.prepend", name=name, text="cheese") self.assertSaltTrueReturn(ret) self.assertTrue(os.path.isfile(name)) @with_tempfile() def test_touch(self, name): - ''' + """ file.touch - ''' - ret = self.run_state('file.touch', name=name) + """ + ret = self.run_state("file.touch", name=name) self.assertTrue(os.path.isfile(name)) self.assertSaltTrueReturn(ret) @with_tempfile(create=False) def test_test_touch(self, name): - ''' + """ file.touch test interface - ''' - ret = self.run_state('file.touch', test=True, name=name) + """ + ret = self.run_state("file.touch", test=True, name=name) self.assertFalse(os.path.isfile(name)) self.assertSaltNoneReturn(ret) @with_tempdir() def test_touch_directory(self, base_dir): - ''' + """ file.touch a directory - ''' - name = os.path.join(base_dir, 'touch_test_dir') + """ + name = os.path.join(base_dir, "touch_test_dir") os.mkdir(name) - ret = self.run_state('file.touch', name=name) + ret = self.run_state("file.touch", name=name) self.assertSaltTrueReturn(ret) self.assertTrue(os.path.isdir(name)) @with_tempdir() def test_issue_2227_file_append(self, base_dir): - ''' + """ Text to append includes a percent symbol - ''' + """ # let's make use of existing state to create a file with contents to # test against - tmp_file_append = os.path.join(base_dir, 'test.append') + tmp_file_append = os.path.join(base_dir, "test.append") - self.run_state('file.touch', name=tmp_file_append) + self.run_state("file.touch", name=tmp_file_append) self.run_state( - 'file.append', - name=tmp_file_append, - source='salt://testappend/firstif') + "file.append", name=tmp_file_append, source="salt://testappend/firstif" + ) self.run_state( - 'file.append', - name=tmp_file_append, - source='salt://testappend/secondif') + "file.append", name=tmp_file_append, source="salt://testappend/secondif" + ) # Now our real test try: ret = self.run_state( - 'file.append', - name=tmp_file_append, - text="HISTTIMEFORMAT='%F %T '") + "file.append", name=tmp_file_append, text="HISTTIMEFORMAT='%F %T '" + ) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(tmp_file_append, 'r') as fp_: + with salt.utils.files.fopen(tmp_file_append, "r") as fp_: contents_pre = fp_.read() # It should not append text again ret = self.run_state( - 'file.append', - name=tmp_file_append, - text="HISTTIMEFORMAT='%F %T '") + "file.append", name=tmp_file_append, text="HISTTIMEFORMAT='%F %T '" + ) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(tmp_file_append, 'r') as fp_: + with salt.utils.files.fopen(tmp_file_append, "r") as fp_: contents_post = fp_.read() self.assertEqual(contents_pre, contents_post) except AssertionError: if os.path.exists(tmp_file_append): - shutil.copy(tmp_file_append, tmp_file_append + '.bak') + shutil.copy(tmp_file_append, tmp_file_append + ".bak") raise @with_tempdir() def test_issue_2401_file_comment(self, base_dir): # Get a path to the temporary file - tmp_file = os.path.join(base_dir, 'issue-2041-comment.txt') + tmp_file = os.path.join(base_dir, "issue-2041-comment.txt") # Write some data to it - with salt.utils.files.fopen(tmp_file, 'w') as fp_: - fp_.write('hello\nworld\n') + with salt.utils.files.fopen(tmp_file, "w") as fp_: + fp_.write("hello\nworld\n") # create the sls template template_lines = [ - '{0}:'.format(tmp_file), - ' file.comment:', - ' - regex: ^world' + "{0}:".format(tmp_file), + " file.comment:", + " - regex: ^world", ] - template = '\n'.join(template_lines) + template = "\n".join(template_lines) try: - ret = self.run_function( - 'state.template_str', [template], timeout=120 - ) + ret = self.run_function("state.template_str", [template], timeout=120) self.assertSaltTrueReturn(ret) - self.assertNotInSaltComment('Pattern already commented', ret) - self.assertInSaltComment('Commented lines successfully', ret) + self.assertNotInSaltComment("Pattern already commented", ret) + self.assertInSaltComment("Commented lines successfully", ret) # This next time, it is already commented. - ret = self.run_function( - 'state.template_str', [template], timeout=120 - ) + ret = self.run_function("state.template_str", [template], timeout=120) self.assertSaltTrueReturn(ret) - self.assertInSaltComment('Pattern already commented', ret) + self.assertInSaltComment("Pattern already commented", ret) except AssertionError: - shutil.copy(tmp_file, tmp_file + '.bak') + shutil.copy(tmp_file, tmp_file + ".bak") raise @with_tempdir() def test_issue_2379_file_append(self, base_dir): # Get a path to the temporary file - tmp_file = os.path.join(base_dir, 'issue-2379-file-append.txt') + tmp_file = os.path.join(base_dir, "issue-2379-file-append.txt") # Write some data to it - with salt.utils.files.fopen(tmp_file, 'w') as fp_: + with salt.utils.files.fopen(tmp_file, "w") as fp_: fp_.write( - 'hello\nworld\n' # Some junk - '#PermitRootLogin yes\n' # Commented text - '# PermitRootLogin yes\n' # Commented text with space + "hello\nworld\n" # Some junk + "#PermitRootLogin yes\n" # Commented text + "# PermitRootLogin yes\n" # Commented text with space ) # create the sls template template_lines = [ - '{0}:'.format(tmp_file), - ' file.append:', - ' - text: PermitRootLogin yes' + "{0}:".format(tmp_file), + " file.append:", + " - text: PermitRootLogin yes", ] - template = '\n'.join(template_lines) + template = "\n".join(template_lines) try: - ret = self.run_function('state.template_str', [template]) + ret = self.run_function("state.template_str", [template]) self.assertSaltTrueReturn(ret) - self.assertInSaltComment('Appended 1 lines', ret) + self.assertInSaltComment("Appended 1 lines", ret) except AssertionError: - shutil.copy(tmp_file, tmp_file + '.bak') + shutil.copy(tmp_file, tmp_file + ".bak") raise - @skipIf(IS_WINDOWS, 'Mode not available in Windows') + @skipIf(IS_WINDOWS, "Mode not available in Windows") @with_tempdir(create=False) @with_tempdir(create=False) def test_issue_2726_mode_kwarg(self, dir1, dir2): # Let's test for the wrong usage approach - bad_mode_kwarg_testfile = os.path.join( - dir1, 'bad_mode_kwarg', 'testfile' - ) + bad_mode_kwarg_testfile = os.path.join(dir1, "bad_mode_kwarg", "testfile") bad_template = [ - '{0}:'.format(bad_mode_kwarg_testfile), - ' file.recurse:', - ' - source: salt://testfile', - ' - mode: 644' + "{0}:".format(bad_mode_kwarg_testfile), + " file.recurse:", + " - source: salt://testfile", + " - mode: 644", ] - ret = self.run_function( - 'state.template_str', [os.linesep.join(bad_template)] - ) + ret = self.run_function("state.template_str", [os.linesep.join(bad_template)]) self.assertSaltFalseReturn(ret) self.assertInSaltComment( - '\'mode\' is not allowed in \'file.recurse\'. Please use ' - '\'file_mode\' and \'dir_mode\'.', - ret + "'mode' is not allowed in 'file.recurse'. Please use " + "'file_mode' and 'dir_mode'.", + ret, ) self.assertNotInSaltComment( - 'TypeError: managed() got multiple values for keyword ' - 'argument \'mode\'', - ret + "TypeError: managed() got multiple values for keyword " "argument 'mode'", + ret, ) # Now, the correct usage approach - good_mode_kwargs_testfile = os.path.join( - dir2, 'good_mode_kwargs', 'testappend' - ) + good_mode_kwargs_testfile = os.path.join(dir2, "good_mode_kwargs", "testappend") good_template = [ - '{0}:'.format(good_mode_kwargs_testfile), - ' file.recurse:', - ' - source: salt://testappend', - ' - dir_mode: 744', - ' - file_mode: 644', + "{0}:".format(good_mode_kwargs_testfile), + " file.recurse:", + " - source: salt://testappend", + " - dir_mode: 744", + " - file_mode: 644", ] - ret = self.run_function( - 'state.template_str', [os.linesep.join(good_template)] - ) + ret = self.run_function("state.template_str", [os.linesep.join(good_template)]) self.assertSaltTrueReturn(ret) @with_tempdir() def test_issue_8343_accumulated_require_in(self, base_dir): - template_path = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'issue-8343.sls') - testcase_filedest = os.path.join(base_dir, 'issue-8343.txt') + template_path = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, "issue-8343.sls") + testcase_filedest = os.path.join(base_dir, "issue-8343.txt") if os.path.exists(template_path): os.remove(template_path) if os.path.exists(testcase_filedest): os.remove(testcase_filedest) sls_template = [ - '{0}:', - ' file.managed:', - ' - contents: |', - ' #', - '', - 'prepend-foo-accumulator-from-pillar:', - ' file.accumulated:', - ' - require_in:', - ' - file: prepend-foo-management', - ' - filename: {0}', - ' - text: |', - ' foo', - '', - 'append-foo-accumulator-from-pillar:', - ' file.accumulated:', - ' - require_in:', - ' - file: append-foo-management', - ' - filename: {0}', - ' - text: |', - ' bar', - '', - 'prepend-foo-management:', - ' file.blockreplace:', - ' - name: {0}', + "{0}:", + " file.managed:", + " - contents: |", + " #", + "", + "prepend-foo-accumulator-from-pillar:", + " file.accumulated:", + " - require_in:", + " - file: prepend-foo-management", + " - filename: {0}", + " - text: |", + " foo", + "", + "append-foo-accumulator-from-pillar:", + " file.accumulated:", + " - require_in:", + " - file: append-foo-management", + " - filename: {0}", + " - text: |", + " bar", + "", + "prepend-foo-management:", + " file.blockreplace:", + " - name: {0}", ' - marker_start: "#-- start salt managed zonestart -- PLEASE, DO NOT EDIT"', ' - marker_end: "#-- end salt managed zonestart --"', " - content: ''", - ' - prepend_if_not_found: True', + " - prepend_if_not_found: True", " - backup: '.bak'", - ' - show_changes: True', - '', - 'append-foo-management:', - ' file.blockreplace:', - ' - name: {0}', + " - show_changes: True", + "", + "append-foo-management:", + " file.blockreplace:", + " - name: {0}", ' - marker_start: "#-- start salt managed zoneend -- PLEASE, DO NOT EDIT"', ' - marker_end: "#-- end salt managed zoneend --"', " - content: ''", - ' - append_if_not_found: True', + " - append_if_not_found: True", " - backup: '.bak2'", - ' - show_changes: True', - ''] - with salt.utils.files.fopen(template_path, 'w') as fp_: - fp_.write( - os.linesep.join(sls_template).format(testcase_filedest)) + " - show_changes: True", + "", + ] + with salt.utils.files.fopen(template_path, "w") as fp_: + fp_.write(os.linesep.join(sls_template).format(testcase_filedest)) - ret = self.run_function('state.sls', mods='issue-8343') + ret = self.run_function("state.sls", mods="issue-8343") for name, step in six.iteritems(ret): self.assertSaltTrueReturn({name: step}) with salt.utils.files.fopen(testcase_filedest) as fp_: contents = fp_.read().split(os.linesep) expected = [ - '#-- start salt managed zonestart -- PLEASE, DO NOT EDIT', - 'foo', - '#-- end salt managed zonestart --', - '#', - '#-- start salt managed zoneend -- PLEASE, DO NOT EDIT', - 'bar', - '#-- end salt managed zoneend --', - ''] - - self.assertEqual([salt.utils.stringutils.to_str(line) for line in expected], contents) - - @with_tempdir() - @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2') - def test_issue_11003_immutable_lazy_proxy_sum(self, base_dir): - # causes the Import-Module ServerManager error on Windows - template_path = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'issue-11003.sls') - testcase_filedest = os.path.join(base_dir, 'issue-11003.txt') - sls_template = [ - 'a{0}:', - ' file.absent:', - ' - name: {0}', - '', - '{0}:', - ' file.managed:', - ' - contents: |', - ' #', - '', - 'test-acc1:', - ' file.accumulated:', - ' - require_in:', - ' - file: final', - ' - filename: {0}', - ' - text: |', - ' bar', - '', - 'test-acc2:', - ' file.accumulated:', - ' - watch_in:', - ' - file: final', - ' - filename: {0}', - ' - text: |', - ' baz', - '', - 'final:', - ' file.blockreplace:', - ' - name: {0}', - ' - marker_start: "#-- start managed zone PLEASE, DO NOT EDIT"', - ' - marker_end: "#-- end managed zone"', - ' - content: \'\'', - ' - append_if_not_found: True', - ' - show_changes: True' + "#-- start salt managed zonestart -- PLEASE, DO NOT EDIT", + "foo", + "#-- end salt managed zonestart --", + "#", + "#-- start salt managed zoneend -- PLEASE, DO NOT EDIT", + "bar", + "#-- end salt managed zoneend --", + "", ] - with salt.utils.files.fopen(template_path, 'w') as fp_: + self.assertEqual( + [salt.utils.stringutils.to_str(line) for line in expected], contents + ) + + @with_tempdir() + @skipIf( + salt.utils.platform.is_darwin() and six.PY2, "This test hangs on OS X on Py2" + ) + def test_issue_11003_immutable_lazy_proxy_sum(self, base_dir): + # causes the Import-Module ServerManager error on Windows + template_path = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, "issue-11003.sls") + testcase_filedest = os.path.join(base_dir, "issue-11003.txt") + sls_template = [ + "a{0}:", + " file.absent:", + " - name: {0}", + "", + "{0}:", + " file.managed:", + " - contents: |", + " #", + "", + "test-acc1:", + " file.accumulated:", + " - require_in:", + " - file: final", + " - filename: {0}", + " - text: |", + " bar", + "", + "test-acc2:", + " file.accumulated:", + " - watch_in:", + " - file: final", + " - filename: {0}", + " - text: |", + " baz", + "", + "final:", + " file.blockreplace:", + " - name: {0}", + ' - marker_start: "#-- start managed zone PLEASE, DO NOT EDIT"', + ' - marker_end: "#-- end managed zone"', + " - content: ''", + " - append_if_not_found: True", + " - show_changes: True", + ] + + with salt.utils.files.fopen(template_path, "w") as fp_: fp_.write(os.linesep.join(sls_template).format(testcase_filedest)) - ret = self.run_function('state.sls', mods='issue-11003', timeout=600) + ret = self.run_function("state.sls", mods="issue-11003", timeout=600) for name, step in six.iteritems(ret): self.assertSaltTrueReturn({name: step}) with salt.utils.files.fopen(testcase_filedest) as fp_: contents = fp_.read().split(os.linesep) - begin = contents.index( - '#-- start managed zone PLEASE, DO NOT EDIT') + 1 - end = contents.index('#-- end managed zone') + begin = contents.index("#-- start managed zone PLEASE, DO NOT EDIT") + 1 + end = contents.index("#-- end managed zone") block_contents = contents[begin:end] - for item in ('', 'bar', 'baz'): + for item in ("", "bar", "baz"): block_contents.remove(item) self.assertEqual(block_contents, []) @with_tempdir() def test_issue_8947_utf8_sls(self, base_dir): - ''' + """ Test some file operation with utf-8 characters on the sls This is more generic than just a file test. Feel free to move - ''' + """ self.maxDiff = None - korean_1 = '한국어 시험' - korean_2 = '첫 번째 행' - korean_3 = '마지막 행' - test_file = os.path.join(base_dir, '{0}.txt'.format(korean_1)) + korean_1 = "한국어 시험" + korean_2 = "첫 번째 행" + korean_3 = "마지막 행" + test_file = os.path.join(base_dir, "{0}.txt".format(korean_1)) test_file_encoded = test_file - template_path = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'issue-8947.sls') + template_path = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, "issue-8947.sls") # create the sls template - template = textwrap.dedent('''\ + template = textwrap.dedent( + """\ some-utf8-file-create: file.managed: - name: {test_file} @@ -2454,83 +2478,86 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): {korean_3} - replace: True - show_diff: True - '''.format(**locals())) + """.format( + **locals() + ) + ) if not salt.utils.platform.is_windows(): - template += textwrap.dedent('''\ + template += textwrap.dedent( + """\ some-utf8-file-content-test: cmd.run: - name: 'cat "{test_file}"' - require: - file: some-utf8-file-create2 - '''.format(**locals())) + """.format( + **locals() + ) + ) # Save template file - with salt.utils.files.fopen(template_path, 'wb') as fp_: + with salt.utils.files.fopen(template_path, "wb") as fp_: fp_.write(salt.utils.stringutils.to_bytes(template)) try: - result = self.run_function('state.sls', mods='issue-8947') + result = self.run_function("state.sls", mods="issue-8947") if not isinstance(result, dict): raise AssertionError( - ('Something went really wrong while testing this sls:' - ' {0}').format(repr(result)) + ( + "Something went really wrong while testing this sls:" " {0}" + ).format(repr(result)) ) # difflib produces different output on python 2.6 than on >=2.7 if sys.version_info < (2, 7): - diff = '--- \n+++ \n@@ -1,1 +1,3 @@\n' + diff = "--- \n+++ \n@@ -1,1 +1,3 @@\n" else: - diff = '--- \n+++ \n@@ -1 +1,3 @@\n' - diff += ( - '+첫 번째 행{0}' - ' 한국어 시험{0}' - '+마지막 행{0}' - ).format(os.linesep) + diff = "--- \n+++ \n@@ -1 +1,3 @@\n" + diff += ("+첫 번째 행{0}" " 한국어 시험{0}" "+마지막 행{0}").format(os.linesep) - ret = {x.split('_|-')[1]: y for x, y in six.iteritems(result)} + ret = {x.split("_|-")[1]: y for x, y in six.iteritems(result)} # Confirm initial creation of file self.assertEqual( - ret['some-utf8-file-create']['comment'], - 'File {0} updated'.format(test_file_encoded) + ret["some-utf8-file-create"]["comment"], + "File {0} updated".format(test_file_encoded), ) self.assertEqual( - ret['some-utf8-file-create']['changes'], - {'diff': 'New file'} + ret["some-utf8-file-create"]["changes"], {"diff": "New file"} ) # Confirm file was modified and that the diff was as expected self.assertEqual( - ret['some-utf8-file-create2']['comment'], - 'File {0} updated'.format(test_file_encoded) - ) - self.assertEqual( - ret['some-utf8-file-create2']['changes'], - {'diff': diff} + ret["some-utf8-file-create2"]["comment"], + "File {0} updated".format(test_file_encoded), ) + self.assertEqual(ret["some-utf8-file-create2"]["changes"], {"diff": diff}) if salt.utils.platform.is_windows(): import subprocess import win32api + p = subprocess.Popen( salt.utils.stringutils.to_str( - 'type {}'.format(win32api.GetShortPathName(test_file))), - shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + "type {}".format(win32api.GetShortPathName(test_file)) + ), + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) p.poll() out = p.stdout.read() self.assertEqual( - out.decode('utf-8'), - os.linesep.join((korean_2, korean_1, korean_3)) + os.linesep + out.decode("utf-8"), + os.linesep.join((korean_2, korean_1, korean_3)) + os.linesep, ) else: self.assertEqual( - ret['some-utf8-file-content-test']['comment'], - 'Command "cat "{0}"" run'.format( - test_file_encoded - ) + ret["some-utf8-file-content-test"]["comment"], + 'Command "cat "{0}"" run'.format(test_file_encoded), ) self.assertEqual( - ret['some-utf8-file-content-test']['changes']['stdout'], - '\n'.join((korean_2, korean_1, korean_3)) + ret["some-utf8-file-content-test"]["changes"]["stdout"], + "\n".join((korean_2, korean_1, korean_3)), ) finally: try: @@ -2541,24 +2568,29 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @skip_if_not_root @skipIf(not HAS_PWD, "pwd not available. Skipping test") @skipIf(not HAS_GRP, "grp not available. Skipping test") - @with_system_user_and_group(TEST_SYSTEM_USER, TEST_SYSTEM_GROUP, - on_existing='delete', delete=True) + @with_system_user_and_group( + TEST_SYSTEM_USER, TEST_SYSTEM_GROUP, on_existing="delete", delete=True + ) @with_tempdir() def test_issue_12209_follow_symlinks(self, tempdir, user, group): - ''' + """ Ensure that symlinks are properly chowned when recursing (following symlinks) - ''' + """ # Make the directories for this test - onedir = os.path.join(tempdir, 'one') - twodir = os.path.join(tempdir, 'two') + onedir = os.path.join(tempdir, "one") + twodir = os.path.join(tempdir, "two") os.mkdir(onedir) os.symlink(onedir, twodir) # Run the state ret = self.run_state( - 'file.directory', name=tempdir, follow_symlinks=True, - user=user, group=group, recurse=['user', 'group'] + "file.directory", + name=tempdir, + follow_symlinks=True, + user=user, + group=group, + recurse=["user", "group"], ) self.assertSaltTrueReturn(ret) @@ -2568,33 +2600,38 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): onestats = os.stat(onedir) twostats = os.lstat(twodir) self.assertEqual(pwd.getpwuid(onestats.st_uid).pw_name, user) - self.assertEqual(pwd.getpwuid(twostats.st_uid).pw_name, 'root') + self.assertEqual(pwd.getpwuid(twostats.st_uid).pw_name, "root") self.assertEqual(grp.getgrgid(onestats.st_gid).gr_name, group) - if salt.utils.path.which('id'): - root_group = self.run_function('user.primary_group', ['root']) + if salt.utils.path.which("id"): + root_group = self.run_function("user.primary_group", ["root"]) self.assertEqual(grp.getgrgid(twostats.st_gid).gr_name, root_group) @skip_if_not_root @skipIf(not HAS_PWD, "pwd not available. Skipping test") @skipIf(not HAS_GRP, "grp not available. Skipping test") - @with_system_user_and_group(TEST_SYSTEM_USER, TEST_SYSTEM_GROUP, - on_existing='delete', delete=True) + @with_system_user_and_group( + TEST_SYSTEM_USER, TEST_SYSTEM_GROUP, on_existing="delete", delete=True + ) @with_tempdir() def test_issue_12209_no_follow_symlinks(self, tempdir, user, group): - ''' + """ Ensure that symlinks are properly chowned when recursing (not following symlinks) - ''' + """ # Make the directories for this test - onedir = os.path.join(tempdir, 'one') - twodir = os.path.join(tempdir, 'two') + onedir = os.path.join(tempdir, "one") + twodir = os.path.join(tempdir, "two") os.mkdir(onedir) os.symlink(onedir, twodir) # Run the state ret = self.run_state( - 'file.directory', name=tempdir, follow_symlinks=False, - user=user, group=group, recurse=['user', 'group'] + "file.directory", + name=tempdir, + follow_symlinks=False, + user=user, + group=group, + recurse=["user", "group"], ) self.assertSaltTrueReturn(ret) @@ -2611,55 +2648,55 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @with_tempfile(create=False) @with_tempfile() def test_template_local_file(self, source, dest): - ''' + """ Test a file.managed state with a local file as the source. Test both with the file:// protocol designation prepended, and without it. - ''' - with salt.utils.files.fopen(source, 'w') as fp_: - fp_.write('{{ foo }}\n') + """ + with salt.utils.files.fopen(source, "w") as fp_: + fp_.write("{{ foo }}\n") - for prefix in ('file://', ''): + for prefix in ("file://", ""): ret = self.run_state( - 'file.managed', + "file.managed", name=dest, source=prefix + source, - template='jinja', - context={'foo': 'Hello world!'} + template="jinja", + context={"foo": "Hello world!"}, ) self.assertSaltTrueReturn(ret) @with_tempfile() def test_template_local_file_noclobber(self, source): - ''' + """ Test the case where a source file is in the minion's local filesystem, and the source path is the same as the destination path. - ''' - with salt.utils.files.fopen(source, 'w') as fp_: - fp_.write('{{ foo }}\n') + """ + with salt.utils.files.fopen(source, "w") as fp_: + fp_.write("{{ foo }}\n") ret = self.run_state( - 'file.managed', + "file.managed", name=source, source=source, - template='jinja', - context={'foo': 'Hello world!'} + template="jinja", + context={"foo": "Hello world!"}, ) self.assertSaltFalseReturn(ret) self.assertIn( - ('Source file cannot be the same as destination'), - ret[next(iter(ret))]['comment'], + ("Source file cannot be the same as destination"), + ret[next(iter(ret))]["comment"], ) @with_tempfile(create=False) @with_tempfile(create=False) def test_issue_25250_force_copy_deletes(self, source, dest): - ''' + """ ensure force option in copy state does not delete target file - ''' - shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'hosts'), source) - shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'file/base/cheese'), dest) + """ + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, "hosts"), source) + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, "file/base/cheese"), dest) - self.run_state('file.copy', name=dest, source=source, force=True) + self.run_state("file.copy", name=dest, source=source, force=True) self.assertTrue(os.path.exists(dest)) self.assertTrue(filecmp.cmp(source, dest)) @@ -2667,89 +2704,95 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): os.remove(dest) @destructiveTest - @skipIf(IS_WINDOWS, 'Windows does not report any file modes. Skipping.') + @skipIf(IS_WINDOWS, "Windows does not report any file modes. Skipping.") @with_tempfile() def test_file_copy_make_dirs(self, source): - ''' + """ ensure make_dirs creates correct user perms - ''' - shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'hosts'), source) - dest = os.path.join(RUNTIME_VARS.TMP, 'dir1', 'dir2', 'copied_file.txt') + """ + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, "hosts"), source) + dest = os.path.join(RUNTIME_VARS.TMP, "dir1", "dir2", "copied_file.txt") - user = 'salt' - mode = '0644' - ret = self.run_function('user.add', [user]) - self.assertTrue(ret, 'Failed to add user. Are you running as sudo?') - ret = self.run_state('file.copy', name=dest, source=source, user=user, - makedirs=True, mode=mode) + user = "salt" + mode = "0644" + ret = self.run_function("user.add", [user]) + self.assertTrue(ret, "Failed to add user. Are you running as sudo?") + ret = self.run_state( + "file.copy", name=dest, source=source, user=user, makedirs=True, mode=mode + ) self.assertSaltTrueReturn(ret) - file_checks = [dest, os.path.join(RUNTIME_VARS.TMP, 'dir1'), os.path.join(RUNTIME_VARS.TMP, 'dir1', 'dir2')] + file_checks = [ + dest, + os.path.join(RUNTIME_VARS.TMP, "dir1"), + os.path.join(RUNTIME_VARS.TMP, "dir1", "dir2"), + ] for check in file_checks: - user_check = self.run_function('file.get_user', [check]) - mode_check = self.run_function('file.get_mode', [check]) + user_check = self.run_function("file.get_user", [check]) + mode_check = self.run_function("file.get_mode", [check]) self.assertEqual(user_check, user) self.assertEqual(salt.utils.files.normalize_mode(mode_check), mode) def test_contents_pillar_with_pillar_list(self): - ''' + """ This tests for any regressions for this issue: https://github.com/saltstack/salt/issues/30934 - ''' - state_file = 'file_contents_pillar' + """ + state_file = "file_contents_pillar" - ret = self.run_function('state.sls', mods=state_file) + ret = self.run_function("state.sls", mods=state_file) self.assertSaltTrueReturn(ret) @skip_if_not_root @skipIf(not HAS_PWD, "pwd not available. Skipping test") @skipIf(not HAS_GRP, "grp not available. Skipping test") - @with_system_user_and_group(TEST_SYSTEM_USER, TEST_SYSTEM_GROUP, - on_existing='delete', delete=True) + @with_system_user_and_group( + TEST_SYSTEM_USER, TEST_SYSTEM_GROUP, on_existing="delete", delete=True + ) def test_owner_after_setuid(self, user, group): - ''' + """ Test to check file user/group after setting setuid or setgid. Because Python os.chown() does reset the setuid/setgid to 0. https://github.com/saltstack/salt/pull/45257 - ''' + """ # Desired configuration. desired = { - 'file': os.path.join(RUNTIME_VARS.TMP, 'file_with_setuid'), - 'user': user, - 'group': group, - 'mode': '4750' + "file": os.path.join(RUNTIME_VARS.TMP, "file_with_setuid"), + "user": user, + "group": group, + "mode": "4750", } # Run the state. ret = self.run_state( - 'file.managed', name=desired['file'], - user=desired['user'], group=desired['group'], mode=desired['mode'] + "file.managed", + name=desired["file"], + user=desired["user"], + group=desired["group"], + mode=desired["mode"], ) # Check result. - file_stat = os.stat(desired['file']) + file_stat = os.stat(desired["file"]) result = { - 'user': pwd.getpwuid(file_stat.st_uid).pw_name, - 'group': grp.getgrgid(file_stat.st_gid).gr_name, - 'mode': oct(stat.S_IMODE(file_stat.st_mode)) + "user": pwd.getpwuid(file_stat.st_uid).pw_name, + "group": grp.getgrgid(file_stat.st_gid).gr_name, + "mode": oct(stat.S_IMODE(file_stat.st_mode)), } self.assertSaltTrueReturn(ret) - self.assertEqual(desired['user'], result['user']) - self.assertEqual(desired['group'], result['group']) - self.assertEqual(desired['mode'], result['mode'].lstrip('0Oo')) + self.assertEqual(desired["user"], result["user"]) + self.assertEqual(desired["group"], result["group"]) + self.assertEqual(desired["mode"], result["mode"].lstrip("0Oo")) def test_binary_contents(self): - ''' + """ This tests to ensure that binary contents do not cause a traceback. - ''' - name = os.path.join(RUNTIME_VARS.TMP, '1px.gif') + """ + name = os.path.join(RUNTIME_VARS.TMP, "1px.gif") try: - ret = self.run_state( - 'file.managed', - name=name, - contents=BINARY_FILE) + ret = self.run_state("file.managed", name=name, contents=BINARY_FILE) self.assertSaltTrueReturn(ret) finally: try: @@ -2758,19 +2801,19 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): pass def test_binary_contents_twice(self): - ''' + """ This test ensures that after a binary file is created, salt can confirm that the file is in the correct state. - ''' + """ # Create a binary file - name = os.path.join(RUNTIME_VARS.TMP, '1px.gif') + name = os.path.join(RUNTIME_VARS.TMP, "1px.gif") # First run state ensures file is created - ret = self.run_state('file.managed', name=name, contents=BINARY_FILE) + ret = self.run_state("file.managed", name=name, contents=BINARY_FILE) self.assertSaltTrueReturn(ret) # Second run of state ensures file is in correct state - ret = self.run_state('file.managed', name=name, contents=BINARY_FILE) + ret = self.run_state("file.managed", name=name, contents=BINARY_FILE) self.assertSaltTrueReturn(ret) try: os.remove(name) @@ -2780,20 +2823,20 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): @skip_if_not_root @skipIf(not HAS_PWD, "pwd not available. Skipping test") @skipIf(not HAS_GRP, "grp not available. Skipping test") - @with_system_user_and_group(TEST_SYSTEM_USER, TEST_SYSTEM_GROUP, - on_existing='delete', delete=True) + @with_system_user_and_group( + TEST_SYSTEM_USER, TEST_SYSTEM_GROUP, on_existing="delete", delete=True + ) @with_tempdir() def test_issue_48336_file_managed_mode_setuid(self, tempdir, user, group): - ''' + """ Ensure that mode is correct with changing of ownership and group symlinks) - ''' - tempfile = os.path.join(tempdir, 'temp_file_issue_48336') + """ + tempfile = os.path.join(tempdir, "temp_file_issue_48336") # Run the state ret = self.run_state( - 'file.managed', name=tempfile, - user=user, group=group, mode='4750', + "file.managed", name=tempfile, user=user, group=group, mode="4750", ) self.assertSaltTrueReturn(ret) @@ -2805,101 +2848,101 @@ class FileTest(ModuleCase, SaltReturnAssertsMixin): temp_file_mode = six.text_type(oct(stat.S_IMODE(temp_file_stats.st_mode))) temp_file_mode = salt.utils.files.normalize_mode(temp_file_mode) - self.assertEqual(temp_file_mode, '4750') + self.assertEqual(temp_file_mode, "4750") self.assertEqual(pwd.getpwuid(temp_file_stats.st_uid).pw_name, user) self.assertEqual(grp.getgrgid(temp_file_stats.st_gid).gr_name, group) @with_tempdir() def test_issue_48557(self, tempdir): - tempfile = os.path.join(tempdir, 'temp_file_issue_48557') - with salt.utils.files.fopen(tempfile, 'wb') as fp: - fp.write(os.linesep.join([ - 'test1', - 'test2', - 'test3', - '', - ]).encode('utf-8')) - ret = self.run_state('file.line', - name=tempfile, - after='test2', - mode='insert', - content='test4') + tempfile = os.path.join(tempdir, "temp_file_issue_48557") + with salt.utils.files.fopen(tempfile, "wb") as fp: + fp.write(os.linesep.join(["test1", "test2", "test3", ""]).encode("utf-8")) + ret = self.run_state( + "file.line", name=tempfile, after="test2", mode="insert", content="test4" + ) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(tempfile, 'rb') as fp: + with salt.utils.files.fopen(tempfile, "rb") as fp: content = fp.read() - self.assertEqual(content, os.linesep.join([ - 'test1', - 'test2', - 'test4', - 'test3', - '', - ]).encode('utf-8')) + self.assertEqual( + content, + os.linesep.join(["test1", "test2", "test4", "test3", ""]).encode("utf-8"), + ) @with_tempfile() def test_issue_50221(self, name): - expected = 'abc{0}{0}{0}'.format(os.linesep) - ret = self.run_function( - 'pillar.get', - ['issue-50221'] - ) + expected = "abc{0}{0}{0}".format(os.linesep) + ret = self.run_function("pillar.get", ["issue-50221"]) assert ret == expected - ret = self.run_function( - 'state.apply', - ['issue-50221'], - pillar={ - 'name': name - }, - ) + ret = self.run_function("state.apply", ["issue-50221"], pillar={"name": name},) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(name, 'r') as fp: + with salt.utils.files.fopen(name, "r") as fp: contents = fp.read() assert contents == expected def test_managed_file_issue_51208(self): - ''' + """ Test to ensure we can handle a file with escaped double-quotes - ''' - name = os.path.join(RUNTIME_VARS.TMP, 'issue_51208.txt') + """ + name = os.path.join(RUNTIME_VARS.TMP, "issue_51208.txt") ret = self.run_state( - 'file.managed', name=name, source='salt://issue-51208/vimrc.stub' + "file.managed", name=name, source="salt://issue-51208/vimrc.stub" ) - src = os.path.join(RUNTIME_VARS.BASE_FILES, 'issue-51208', 'vimrc.stub') - with salt.utils.files.fopen(src, 'r') as fp_: + src = os.path.join(RUNTIME_VARS.BASE_FILES, "issue-51208", "vimrc.stub") + with salt.utils.files.fopen(src, "r") as fp_: master_data = fp_.read() - with salt.utils.files.fopen(name, 'r') as fp_: + with salt.utils.files.fopen(name, "r") as fp_: minion_data = fp_.read() self.assertEqual(master_data, minion_data) self.assertSaltTrueReturn(ret) +@pytest.mark.windows_whitelisted class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin): - marker_start = '# start' - marker_end = '# end' - content = dedent(six.text_type('''\ + marker_start = "# start" + marker_end = "# end" + content = dedent( + six.text_type( + """\ Line 1 of block Line 2 of block - ''')) - without_block = dedent(six.text_type('''\ + """ + ) + ) + without_block = dedent( + six.text_type( + """\ Hello world! # comment here - ''')) - with_non_matching_block = dedent(six.text_type('''\ + """ + ) + ) + with_non_matching_block = dedent( + six.text_type( + """\ Hello world! # start No match here # end # comment here - ''')) - with_non_matching_block_and_marker_end_not_after_newline = dedent(six.text_type('''\ + """ + ) + ) + with_non_matching_block_and_marker_end_not_after_newline = dedent( + six.text_type( + """\ Hello world! # start No match here# end # comment here - ''')) - with_matching_block = dedent(six.text_type('''\ + """ + ) + ) + with_matching_block = dedent( + six.text_type( + """\ Hello world! # start @@ -2907,8 +2950,12 @@ class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin): Line 2 of block # end # comment here - ''')) - with_matching_block_and_extra_newline = dedent(six.text_type('''\ + """ + ) + ) + with_matching_block_and_extra_newline = dedent( + six.text_type( + """\ Hello world! # start @@ -2917,1198 +2964,1391 @@ class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin): # end # comment here - ''')) - with_matching_block_and_marker_end_not_after_newline = dedent(six.text_type('''\ + """ + ) + ) + with_matching_block_and_marker_end_not_after_newline = dedent( + six.text_type( + """\ Hello world! # start Line 1 of block Line 2 of block# end # comment here - ''')) - content_explicit_posix_newlines = ('Line 1 of block\n' - 'Line 2 of block\n') - content_explicit_windows_newlines = ('Line 1 of block\r\n' - 'Line 2 of block\r\n') - without_block_explicit_posix_newlines = ('Hello world!\n\n' - '# comment here\n') - without_block_explicit_windows_newlines = ('Hello world!\r\n\r\n' - '# comment here\r\n') - with_block_prepended_explicit_posix_newlines = ('# start\n' - 'Line 1 of block\n' - 'Line 2 of block\n' - '# end\n' - 'Hello world!\n\n' - '# comment here\n') - with_block_prepended_explicit_windows_newlines = ('# start\r\n' - 'Line 1 of block\r\n' - 'Line 2 of block\r\n' - '# end\r\n' - 'Hello world!\r\n\r\n' - '# comment here\r\n') - with_block_appended_explicit_posix_newlines = ('Hello world!\n\n' - '# comment here\n' - '# start\n' - 'Line 1 of block\n' - 'Line 2 of block\n' - '# end\n') - with_block_appended_explicit_windows_newlines = ('Hello world!\r\n\r\n' - '# comment here\r\n' - '# start\r\n' - 'Line 1 of block\r\n' - 'Line 2 of block\r\n' - '# end\r\n') + """ + ) + ) + content_explicit_posix_newlines = "Line 1 of block\n" "Line 2 of block\n" + content_explicit_windows_newlines = "Line 1 of block\r\n" "Line 2 of block\r\n" + without_block_explicit_posix_newlines = "Hello world!\n\n" "# comment here\n" + without_block_explicit_windows_newlines = ( + "Hello world!\r\n\r\n" "# comment here\r\n" + ) + with_block_prepended_explicit_posix_newlines = ( + "# start\n" + "Line 1 of block\n" + "Line 2 of block\n" + "# end\n" + "Hello world!\n\n" + "# comment here\n" + ) + with_block_prepended_explicit_windows_newlines = ( + "# start\r\n" + "Line 1 of block\r\n" + "Line 2 of block\r\n" + "# end\r\n" + "Hello world!\r\n\r\n" + "# comment here\r\n" + ) + with_block_appended_explicit_posix_newlines = ( + "Hello world!\n\n" + "# comment here\n" + "# start\n" + "Line 1 of block\n" + "Line 2 of block\n" + "# end\n" + ) + with_block_appended_explicit_windows_newlines = ( + "Hello world!\r\n\r\n" + "# comment here\r\n" + "# start\r\n" + "Line 1 of block\r\n" + "Line 2 of block\r\n" + "# end\r\n" + ) @staticmethod def _write(dest, content): - with salt.utils.files.fopen(dest, 'wb') as fp_: + with salt.utils.files.fopen(dest, "wb") as fp_: fp_.write(salt.utils.stringutils.to_bytes(content)) @staticmethod def _read(src): - with salt.utils.files.fopen(src, 'rb') as fp_: + with salt.utils.files.fopen(src, "rb") as fp_: return salt.utils.stringutils.to_unicode(fp_.read()) @with_tempfile() def test_prepend(self, name): - ''' + """ Test blockreplace when prepend_if_not_found=True and block doesn't exist in file. - ''' - expected = self.marker_start + os.linesep + self.content + \ - self.marker_end + os.linesep + self.without_block + """ + expected = ( + self.marker_start + + os.linesep + + self.content + + self.marker_end + + os.linesep + + self.without_block + ) # Pass 1: content ends in newline self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2: content does not end in newline self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) @with_tempfile() def test_prepend_append_newline(self, name): - ''' + """ Test blockreplace when prepend_if_not_found=True and block doesn't exist in file. Test with append_newline explicitly set to True. - ''' + """ # Pass 1: content ends in newline - expected = self.marker_start + os.linesep + self.content + \ - os.linesep + self.marker_end + os.linesep + self.without_block + expected = ( + self.marker_start + + os.linesep + + self.content + + os.linesep + + self.marker_end + + os.linesep + + self.without_block + ) self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2: content does not end in newline - expected = self.marker_start + os.linesep + self.content + \ - self.marker_end + os.linesep + self.without_block + expected = ( + self.marker_start + + os.linesep + + self.content + + self.marker_end + + os.linesep + + self.without_block + ) self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) @with_tempfile() def test_prepend_no_append_newline(self, name): - ''' + """ Test blockreplace when prepend_if_not_found=True and block doesn't exist in file. Test with append_newline explicitly set to False. - ''' + """ # Pass 1: content ends in newline - expected = self.marker_start + os.linesep + self.content + \ - self.marker_end + os.linesep + self.without_block + expected = ( + self.marker_start + + os.linesep + + self.content + + self.marker_end + + os.linesep + + self.without_block + ) self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2: content does not end in newline - expected = self.marker_start + os.linesep + \ - self.content.rstrip('\r\n') + self.marker_end + os.linesep + \ - self.without_block + expected = ( + self.marker_start + + os.linesep + + self.content.rstrip("\r\n") + + self.marker_end + + os.linesep + + self.without_block + ) self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) @with_tempfile() def test_append(self, name): - ''' + """ Test blockreplace when append_if_not_found=True and block doesn't exist in file. - ''' - expected = self.without_block + self.marker_start + os.linesep + \ - self.content + self.marker_end + os.linesep + """ + expected = ( + self.without_block + + self.marker_start + + os.linesep + + self.content + + self.marker_end + + os.linesep + ) # Pass 1: content ends in newline self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2: content does not end in newline self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) @with_tempfile() def test_append_append_newline(self, name): - ''' + """ Test blockreplace when append_if_not_found=True and block doesn't exist in file. Test with append_newline explicitly set to True. - ''' + """ # Pass 1: content ends in newline - expected = self.without_block + self.marker_start + os.linesep + \ - self.content + os.linesep + self.marker_end + os.linesep + expected = ( + self.without_block + + self.marker_start + + os.linesep + + self.content + + os.linesep + + self.marker_end + + os.linesep + ) self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2: content does not end in newline - expected = self.without_block + self.marker_start + os.linesep + \ - self.content + self.marker_end + os.linesep + expected = ( + self.without_block + + self.marker_start + + os.linesep + + self.content + + self.marker_end + + os.linesep + ) self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) @with_tempfile() def test_append_no_append_newline(self, name): - ''' + """ Test blockreplace when append_if_not_found=True and block doesn't exist in file. Test with append_newline explicitly set to False. - ''' + """ # Pass 1: content ends in newline - expected = self.without_block + self.marker_start + os.linesep + \ - self.content + self.marker_end + os.linesep + expected = ( + self.without_block + + self.marker_start + + os.linesep + + self.content + + self.marker_end + + os.linesep + ) self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2: content does not end in newline - expected = self.without_block + self.marker_start + os.linesep + \ - self.content.rstrip('\r\n') + self.marker_end + os.linesep + expected = ( + self.without_block + + self.marker_start + + os.linesep + + self.content.rstrip("\r\n") + + self.marker_end + + os.linesep + ) self._write(name, self.without_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), expected) @with_tempfile() def test_prepend_auto_line_separator(self, name): - ''' + """ This tests the line separator auto-detection when prepending the block - ''' + """ # POSIX newlines to Windows newlines self._write(name, self.without_block_explicit_windows_newlines) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content_explicit_posix_newlines, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content_explicit_posix_newlines, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_block_prepended_explicit_windows_newlines) + self._read(name), self.with_block_prepended_explicit_windows_newlines + ) # Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content_explicit_posix_newlines, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content_explicit_posix_newlines, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_block_prepended_explicit_windows_newlines) + self._read(name), self.with_block_prepended_explicit_windows_newlines + ) # Windows newlines to POSIX newlines self._write(name, self.without_block_explicit_posix_newlines) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content_explicit_windows_newlines, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content_explicit_windows_newlines, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_block_prepended_explicit_posix_newlines) + self._read(name), self.with_block_prepended_explicit_posix_newlines + ) # Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content_explicit_windows_newlines, - marker_start=self.marker_start, - marker_end=self.marker_end, - prepend_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content_explicit_windows_newlines, + marker_start=self.marker_start, + marker_end=self.marker_end, + prepend_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_block_prepended_explicit_posix_newlines) + self._read(name), self.with_block_prepended_explicit_posix_newlines + ) @with_tempfile() def test_append_auto_line_separator(self, name): - ''' + """ This tests the line separator auto-detection when appending the block - ''' + """ # POSIX newlines to Windows newlines self._write(name, self.without_block_explicit_windows_newlines) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content_explicit_posix_newlines, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content_explicit_posix_newlines, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_block_appended_explicit_windows_newlines) + self._read(name), self.with_block_appended_explicit_windows_newlines + ) # Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content_explicit_posix_newlines, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content_explicit_posix_newlines, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_block_appended_explicit_windows_newlines) + self._read(name), self.with_block_appended_explicit_windows_newlines + ) # Windows newlines to POSIX newlines self._write(name, self.without_block_explicit_posix_newlines) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content_explicit_windows_newlines, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content_explicit_windows_newlines, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_block_appended_explicit_posix_newlines) + self._read(name), self.with_block_appended_explicit_posix_newlines + ) # Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content_explicit_windows_newlines, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_if_not_found=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content_explicit_windows_newlines, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_if_not_found=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_block_appended_explicit_posix_newlines) + self._read(name), self.with_block_appended_explicit_posix_newlines + ) @with_tempfile() def test_non_matching_block(self, name): - ''' + """ Test blockreplace when block exists but its contents are not a match. - ''' + """ # Pass 1: content ends in newline self._write(name, self.with_non_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2: content does not end in newline self._write(name, self.with_non_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) @with_tempfile() def test_non_matching_block_append_newline(self, name): - ''' + """ Test blockreplace when block exists but its contents are not a match. Test with append_newline explicitly set to True. - ''' + """ # Pass 1: content ends in newline self._write(name, self.with_non_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) - self.assertEqual( - self._read(name), - self.with_matching_block_and_extra_newline) + self.assertTrue(ret[next(iter(ret))]["changes"]) + self.assertEqual(self._read(name), self.with_matching_block_and_extra_newline) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) - self.assertEqual( - self._read(name), - self.with_matching_block_and_extra_newline) + self.assertFalse(ret[next(iter(ret))]["changes"]) + self.assertEqual(self._read(name), self.with_matching_block_and_extra_newline) # Pass 2: content does not end in newline self._write(name, self.with_non_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) @with_tempfile() def test_non_matching_block_no_append_newline(self, name): - ''' + """ Test blockreplace when block exists but its contents are not a match. Test with append_newline explicitly set to False. - ''' + """ # Pass 1: content ends in newline self._write(name, self.with_non_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2: content does not end in newline self._write(name, self.with_non_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_matching_block_and_marker_end_not_after_newline) + self._read(name), self.with_matching_block_and_marker_end_not_after_newline + ) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_matching_block_and_marker_end_not_after_newline) + self._read(name), self.with_matching_block_and_marker_end_not_after_newline + ) @with_tempfile() def test_non_matching_block_and_marker_not_after_newline(self, name): - ''' + """ Test blockreplace when block exists but its contents are not a match, and the marker_end is not directly preceded by a newline. - ''' + """ # Pass 1: content ends in newline - self._write( - name, - self.with_non_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end) + self._write(name, self.with_non_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2: content does not end in newline - self._write( - name, - self.with_non_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end) + self._write(name, self.with_non_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) @with_tempfile() def test_non_matching_block_and_marker_not_after_newline_append_newline(self, name): - ''' + """ Test blockreplace when block exists but its contents are not a match, and the marker_end is not directly preceded by a newline. Test with append_newline explicitly set to True. - ''' + """ # Pass 1: content ends in newline - self._write( - name, - self.with_non_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + self._write(name, self.with_non_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) - self.assertEqual( - self._read(name), - self.with_matching_block_and_extra_newline) + self.assertTrue(ret[next(iter(ret))]["changes"]) + self.assertEqual(self._read(name), self.with_matching_block_and_extra_newline) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) - self.assertEqual( - self._read(name), - self.with_matching_block_and_extra_newline) + self.assertFalse(ret[next(iter(ret))]["changes"]) + self.assertEqual(self._read(name), self.with_matching_block_and_extra_newline) # Pass 2: content does not end in newline - self._write( - name, - self.with_non_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + self._write(name, self.with_non_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) @with_tempfile() - def test_non_matching_block_and_marker_not_after_newline_no_append_newline(self, name): - ''' + def test_non_matching_block_and_marker_not_after_newline_no_append_newline( + self, name + ): + """ Test blockreplace when block exists but its contents are not a match, and the marker_end is not directly preceded by a newline. Test with append_newline explicitly set to False. - ''' + """ # Pass 1: content ends in newline - self._write( - name, - self.with_non_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + self._write(name, self.with_non_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2: content does not end in newline - self._write( - name, - self.with_non_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + self._write(name, self.with_non_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_matching_block_and_marker_end_not_after_newline) + self._read(name), self.with_matching_block_and_marker_end_not_after_newline + ) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_matching_block_and_marker_end_not_after_newline) + self._read(name), self.with_matching_block_and_marker_end_not_after_newline + ) @with_tempfile() def test_matching_block(self, name): - ''' + """ Test blockreplace when block exists and its contents are a match. No changes should be made. - ''' + """ # Pass 1: content ends in newline self._write(name, self.with_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2: content does not end in newline self._write(name, self.with_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) @with_tempfile() def test_matching_block_append_newline(self, name): - ''' + """ Test blockreplace when block exists and its contents are a match. Test with append_newline explicitly set to True. This will result in an extra newline when the content ends in a newline, and will not when the content does not end in a newline. - ''' + """ # Pass 1: content ends in newline self._write(name, self.with_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) - self.assertEqual( - self._read(name), - self.with_matching_block_and_extra_newline) + self.assertTrue(ret[next(iter(ret))]["changes"]) + self.assertEqual(self._read(name), self.with_matching_block_and_extra_newline) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) - self.assertEqual( - self._read(name), - self.with_matching_block_and_extra_newline) + self.assertFalse(ret[next(iter(ret))]["changes"]) + self.assertEqual(self._read(name), self.with_matching_block_and_extra_newline) # Pass 2: content does not end in newline self._write(name, self.with_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) @with_tempfile() def test_matching_block_no_append_newline(self, name): - ''' + """ Test blockreplace when block exists and its contents are a match. Test with append_newline explicitly set to False. This will result in the marker_end not being directly preceded by a newline when the content does not end in a newline. - ''' + """ # Pass 1: content ends in newline self._write(name, self.with_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2: content does not end in newline self._write(name, self.with_matching_block) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_matching_block_and_marker_end_not_after_newline) + self._read(name), self.with_matching_block_and_marker_end_not_after_newline + ) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_matching_block_and_marker_end_not_after_newline) + self._read(name), self.with_matching_block_and_marker_end_not_after_newline + ) @with_tempfile() def test_matching_block_and_marker_not_after_newline(self, name): - ''' + """ Test blockreplace when block exists and its contents are a match, but the marker_end is not directly preceded by a newline. - ''' + """ # Pass 1: content ends in newline - self._write( - name, - self.with_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end) + self._write(name, self.with_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2: content does not end in newline - self._write( - name, - self.with_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end) + self._write(name, self.with_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) @with_tempfile() def test_matching_block_and_marker_not_after_newline_append_newline(self, name): - ''' + """ Test blockreplace when block exists and its contents are a match, but the marker_end is not directly preceded by a newline. Test with append_newline explicitly set to True. This will result in an extra newline when the content ends in a newline, and will not when the content does not end in a newline. - ''' + """ # Pass 1: content ends in newline - self._write( - name, - self.with_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + self._write(name, self.with_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) - self.assertEqual( - self._read(name), - self.with_matching_block_and_extra_newline) + self.assertTrue(ret[next(iter(ret))]["changes"]) + self.assertEqual(self._read(name), self.with_matching_block_and_extra_newline) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) - self.assertEqual( - self._read(name), - self.with_matching_block_and_extra_newline) + self.assertFalse(ret[next(iter(ret))]["changes"]) + self.assertEqual(self._read(name), self.with_matching_block_and_extra_newline) # Pass 2: content does not end in newline - self._write( - name, - self.with_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + self._write(name, self.with_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=True) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=True, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) @with_tempfile() def test_matching_block_and_marker_not_after_newline_no_append_newline(self, name): - ''' + """ Test blockreplace when block exists and its contents are a match, but the marker_end is not directly preceded by a newline. Test with append_newline explicitly set to False. - ''' + """ # Pass 1: content ends in newline - self._write( - name, - self.with_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + self._write(name, self.with_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertTrue(ret[next(iter(ret))]['changes']) + self.assertTrue(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 1a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content, - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content, + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual(self._read(name), self.with_matching_block) # Pass 2: content does not end in newline - self._write( - name, - self.with_matching_block_and_marker_end_not_after_newline) - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + self._write(name, self.with_matching_block_and_marker_end_not_after_newline) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_matching_block_and_marker_end_not_after_newline) + self._read(name), self.with_matching_block_and_marker_end_not_after_newline + ) # Pass 2a: Re-run state, no changes should be made - ret = self.run_state('file.blockreplace', - name=name, - content=self.content.rstrip('\r\n'), - marker_start=self.marker_start, - marker_end=self.marker_end, - append_newline=False) + ret = self.run_state( + "file.blockreplace", + name=name, + content=self.content.rstrip("\r\n"), + marker_start=self.marker_start, + marker_end=self.marker_end, + append_newline=False, + ) self.assertSaltTrueReturn(ret) - self.assertFalse(ret[next(iter(ret))]['changes']) + self.assertFalse(ret[next(iter(ret))]["changes"]) self.assertEqual( - self._read(name), - self.with_matching_block_and_marker_end_not_after_newline) + self._read(name), self.with_matching_block_and_marker_end_not_after_newline + ) @with_tempfile() def test_issue_49043(self, name): - ret = self.run_function( - 'state.sls', - mods='issue-49043', - pillar={'name': name}, - ) + ret = self.run_function("state.sls", mods="issue-49043", pillar={"name": name},) log.error("ret = %s", repr(ret)) - diff = '--- \n+++ \n@@ -0,0 +1,3 @@\n' - diff += dedent('''\ + diff = "--- \n+++ \n@@ -0,0 +1,3 @@\n" + diff += dedent( + """\ +#-- start managed zone -- +äöü +#-- end managed zone -- - ''') - job = 'file_|-somefile-blockreplace_|-{}_|-blockreplace'.format(name) - self.assertEqual( - ret[job]['changes']['diff'], - diff) + """ + ) + job = "file_|-somefile-blockreplace_|-{}_|-blockreplace".format(name) + self.assertEqual(ret[job]["changes"]["diff"], diff) +@pytest.mark.windows_whitelisted class RemoteFileTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Uses a local tornado webserver to test http(s) file.managed states with and without skip_verify - ''' + """ + @classmethod def setUpClass(cls): cls.webserver = Webserver() cls.webserver.start() - cls.source = cls.webserver.url('grail/scene33') + cls.source = cls.webserver.url("grail/scene33") if IS_WINDOWS: # CRLF vs LF causes a different hash on windows - cls.source_hash = '21438b3d5fd2c0028bcab92f7824dc69' + cls.source_hash = "21438b3d5fd2c0028bcab92f7824dc69" else: - cls.source_hash = 'd2feb3beb323c79fc7a0f44f1408b4a3' + cls.source_hash = "d2feb3beb323c79fc7a0f44f1408b4a3" @classmethod def tearDownClass(cls): @@ -4127,112 +4367,113 @@ class RemoteFileTest(ModuleCase, SaltReturnAssertsMixin): def run_state(self, *args, **kwargs): # pylint: disable=arguments-differ ret = super(RemoteFileTest, self).run_state(*args, **kwargs) - log.debug('ret = %s', ret) + log.debug("ret = %s", ret) return ret def test_file_managed_http_source_no_hash(self): - ''' + """ Test a remote file with no hash - ''' - ret = self.run_state('file.managed', - name=self.name, - source=self.source, - skip_verify=False) + """ + ret = self.run_state( + "file.managed", name=self.name, source=self.source, skip_verify=False + ) # This should fail because no hash was provided self.assertSaltFalseReturn(ret) def test_file_managed_http_source(self): - ''' + """ Test a remote file with no hash - ''' - ret = self.run_state('file.managed', - name=self.name, - source=self.source, - source_hash=self.source_hash, - skip_verify=False) + """ + ret = self.run_state( + "file.managed", + name=self.name, + source=self.source, + source_hash=self.source_hash, + skip_verify=False, + ) self.assertSaltTrueReturn(ret) def test_file_managed_http_source_skip_verify(self): - ''' + """ Test a remote file using skip_verify - ''' - ret = self.run_state('file.managed', - name=self.name, - source=self.source, - skip_verify=True) + """ + ret = self.run_state( + "file.managed", name=self.name, source=self.source, skip_verify=True + ) self.assertSaltTrueReturn(ret) def test_file_managed_keep_source_false_http(self): - ''' + """ This test ensures that we properly clean the cached file if keep_source is set to False, for source files using an http:// URL - ''' + """ # Run the state - ret = self.run_state('file.managed', - name=self.name, - source=self.source, - source_hash=self.source_hash, - keep_source=False) + ret = self.run_state( + "file.managed", + name=self.name, + source=self.source, + source_hash=self.source_hash, + keep_source=False, + ) ret = ret[next(iter(ret))] - assert ret['result'] is True + assert ret["result"] is True # Now make sure that the file is not cached - result = self.run_function('cp.is_cached', [self.source]) - assert result == '', 'File is still cached at {0}'.format(result) + result = self.run_function("cp.is_cached", [self.source]) + assert result == "", "File is still cached at {0}".format(result) -@skipIf(not salt.utils.path.which('patch'), 'patch is not installed') +@skipIf(not salt.utils.path.which("patch"), "patch is not installed") +@pytest.mark.windows_whitelisted class PatchTest(ModuleCase, SaltReturnAssertsMixin): def _check_patch_version(self, min_version): - ''' + """ patch version check - ''' - version = self.run_function('cmd.run', ['patch --version']).splitlines()[0] + """ + version = self.run_function("cmd.run", ["patch --version"]).splitlines()[0] version = version.split()[1] if _LooseVersion(version) < _LooseVersion(min_version): - self.skipTest('Minimum patch version required: {0}. ' - 'Patch version installed: {1}'.format(min_version, version)) + self.skipTest( + "Minimum patch version required: {0}. " + "Patch version installed: {1}".format(min_version, version) + ) @classmethod def setUpClass(cls): cls.webserver = Webserver() cls.webserver.start() - cls.numbers_patch_name = 'numbers.patch' - cls.math_patch_name = 'math.patch' - cls.all_patch_name = 'all.patch' - cls.numbers_patch_template_name = cls.numbers_patch_name + '.jinja' - cls.math_patch_template_name = cls.math_patch_name + '.jinja' - cls.all_patch_template_name = cls.all_patch_name + '.jinja' + cls.numbers_patch_name = "numbers.patch" + cls.math_patch_name = "math.patch" + cls.all_patch_name = "all.patch" + cls.numbers_patch_template_name = cls.numbers_patch_name + ".jinja" + cls.math_patch_template_name = cls.math_patch_name + ".jinja" + cls.all_patch_template_name = cls.all_patch_name + ".jinja" - cls.numbers_patch_path = 'patches/' + cls.numbers_patch_name - cls.math_patch_path = 'patches/' + cls.math_patch_name - cls.all_patch_path = 'patches/' + cls.all_patch_name - cls.numbers_patch_template_path = \ - 'patches/' + cls.numbers_patch_template_name - cls.math_patch_template_path = \ - 'patches/' + cls.math_patch_template_name - cls.all_patch_template_path = \ - 'patches/' + cls.all_patch_template_name + cls.numbers_patch_path = "patches/" + cls.numbers_patch_name + cls.math_patch_path = "patches/" + cls.math_patch_name + cls.all_patch_path = "patches/" + cls.all_patch_name + cls.numbers_patch_template_path = "patches/" + cls.numbers_patch_template_name + cls.math_patch_template_path = "patches/" + cls.math_patch_template_name + cls.all_patch_template_path = "patches/" + cls.all_patch_template_name - cls.numbers_patch = 'salt://' + cls.numbers_patch_path - cls.math_patch = 'salt://' + cls.math_patch_path - cls.all_patch = 'salt://' + cls.all_patch_path - cls.numbers_patch_template = 'salt://' + cls.numbers_patch_template_path - cls.math_patch_template = 'salt://' + cls.math_patch_template_path - cls.all_patch_template = 'salt://' + cls.all_patch_template_path + cls.numbers_patch = "salt://" + cls.numbers_patch_path + cls.math_patch = "salt://" + cls.math_patch_path + cls.all_patch = "salt://" + cls.all_patch_path + cls.numbers_patch_template = "salt://" + cls.numbers_patch_template_path + cls.math_patch_template = "salt://" + cls.math_patch_template_path + cls.all_patch_template = "salt://" + cls.all_patch_template_path cls.numbers_patch_http = cls.webserver.url(cls.numbers_patch_path) cls.math_patch_http = cls.webserver.url(cls.math_patch_path) cls.all_patch_http = cls.webserver.url(cls.all_patch_path) - cls.numbers_patch_template_http = \ - cls.webserver.url(cls.numbers_patch_template_path) - cls.math_patch_template_http = \ - cls.webserver.url(cls.math_patch_template_path) - cls.all_patch_template_http = \ - cls.webserver.url(cls.all_patch_template_path) + cls.numbers_patch_template_http = cls.webserver.url( + cls.numbers_patch_template_path + ) + cls.math_patch_template_http = cls.webserver.url(cls.math_patch_template_path) + cls.all_patch_template_http = cls.webserver.url(cls.all_patch_template_path) - patches_dir = os.path.join(RUNTIME_VARS.FILES, 'file', 'base', 'patches') + patches_dir = os.path.join(RUNTIME_VARS.FILES, "file", "base", "patches") cls.numbers_patch_hash = salt.utils.hashutils.get_hash( os.path.join(patches_dir, cls.numbers_patch_name) ) @@ -4252,22 +4493,24 @@ class PatchTest(ModuleCase, SaltReturnAssertsMixin): os.path.join(patches_dir, cls.all_patch_template_name) ) - cls.context = {'two': 'two', 'ten': 10} + cls.context = {"two": "two", "ten": 10} @classmethod def tearDownClass(cls): cls.webserver.stop() def setUp(self): - ''' + """ Create a new unpatched set of files - ''' + """ self.base_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - os.makedirs(os.path.join(self.base_dir, 'foo', 'bar')) - self.numbers_file = os.path.join(self.base_dir, 'foo', 'numbers.txt') - self.math_file = os.path.join(self.base_dir, 'foo', 'bar', 'math.txt') - with salt.utils.files.fopen(self.numbers_file, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + os.makedirs(os.path.join(self.base_dir, "foo", "bar")) + self.numbers_file = os.path.join(self.base_dir, "foo", "numbers.txt") + self.math_file = os.path.join(self.base_dir, "foo", "bar", "math.txt") + with salt.utils.files.fopen(self.numbers_file, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ one two three @@ -4275,153 +4518,141 @@ class PatchTest(ModuleCase, SaltReturnAssertsMixin): 1 2 3 - ''')) - with salt.utils.files.fopen(self.math_file, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + """ + ) + ) + with salt.utils.files.fopen(self.math_file, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ Five plus five is ten Four squared is sixteen - ''')) + """ + ) + ) self.addCleanup(shutil.rmtree, self.base_dir, ignore_errors=True) def test_patch_single_file(self): - ''' + """ Test file.patch using a patch applied to a single file - ''' + """ ret = self.run_state( - 'file.patch', - name=self.numbers_file, - source=self.numbers_patch, + "file.patch", name=self.numbers_file, source=self.numbers_patch, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') + self.assertEqual(ret["comment"], "Patch successfully applied") # Re-run the state, should succeed and there should be a message about # a partially-applied hunk. ret = self.run_state( - 'file.patch', - name=self.numbers_file, - source=self.numbers_patch, + "file.patch", name=self.numbers_file, source=self.numbers_patch, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) def test_patch_directory(self): - ''' + """ Test file.patch using a patch applied to a directory, with changes spanning multiple files. - ''' - self._check_patch_version('2.6') + """ + self._check_patch_version("2.6") ret = self.run_state( - 'file.patch', - name=self.base_dir, - source=self.all_patch, - strip=1, + "file.patch", name=self.base_dir, source=self.all_patch, strip=1, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') + self.assertEqual(ret["comment"], "Patch successfully applied") # Re-run the state, should succeed and there should be a message about # a partially-applied hunk. ret = self.run_state( - 'file.patch', - name=self.base_dir, - source=self.all_patch, - strip=1, + "file.patch", name=self.base_dir, source=self.all_patch, strip=1, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) def test_patch_strip_parsing(self): - ''' + """ Test that we successfuly parse -p/--strip when included in the options - ''' - self._check_patch_version('2.6') + """ + self._check_patch_version("2.6") # Run the state using -p1 ret = self.run_state( - 'file.patch', - name=self.base_dir, - source=self.all_patch, - options='-p1', + "file.patch", name=self.base_dir, source=self.all_patch, options="-p1", ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') + self.assertEqual(ret["comment"], "Patch successfully applied") # Re-run the state using --strip=1 ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch, - options='--strip=1', + options="--strip=1", ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) # Re-run the state using --strip 1 ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch, - options='--strip 1', + options="--strip 1", ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) def test_patch_saltenv(self): - ''' + """ Test that we attempt to download the patch from a non-base saltenv - ''' + """ # This state will fail because we don't have a patch file in that # environment, but that is OK, we just want to test that we're looking # in an environment other than base. ret = self.run_state( - 'file.patch', - name=self.math_file, - source=self.math_patch, - saltenv='prod', + "file.patch", name=self.math_file, source=self.math_patch, saltenv="prod", ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] self.assertEqual( - ret['comment'], - "Source file {0} not found in saltenv 'prod'".format(self.math_patch) + ret["comment"], + "Source file {0} not found in saltenv 'prod'".format(self.math_patch), ) def test_patch_single_file_failure(self): - ''' + """ Test file.patch using a patch applied to a single file. This tests a failed patch. - ''' + """ # Empty the file to ensure that the patch doesn't apply cleanly - with salt.utils.files.fopen(self.numbers_file, 'w'): + with salt.utils.files.fopen(self.numbers_file, "w"): pass ret = self.run_state( - 'file.patch', - name=self.numbers_file, - source=self.numbers_patch, + "file.patch", name=self.numbers_file, source=self.numbers_patch, ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Patch would not apply cleanly', ret['comment']) + self.assertIn("Patch would not apply cleanly", ret["comment"]) # Test the reject_file option and ensure that the rejects are written # to the path specified. reject_file = salt.utils.files.mkstemp() ret = self.run_state( - 'file.patch', + "file.patch", name=self.numbers_file, source=self.numbers_patch, reject_file=reject_file, @@ -4429,36 +4660,32 @@ class PatchTest(ModuleCase, SaltReturnAssertsMixin): ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Patch would not apply cleanly', ret['comment']) + self.assertIn("Patch would not apply cleanly", ret["comment"]) self.assertRegex( - ret['comment'], - 'saving rejects to (file )?{0}'.format(reject_file) + ret["comment"], "saving rejects to (file )?{0}".format(reject_file) ) def test_patch_directory_failure(self): - ''' + """ Test file.patch using a patch applied to a directory, with changes spanning multiple files. - ''' + """ # Empty the file to ensure that the patch doesn't apply - with salt.utils.files.fopen(self.math_file, 'w'): + with salt.utils.files.fopen(self.math_file, "w"): pass ret = self.run_state( - 'file.patch', - name=self.base_dir, - source=self.all_patch, - strip=1, + "file.patch", name=self.base_dir, source=self.all_patch, strip=1, ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Patch would not apply cleanly', ret['comment']) + self.assertIn("Patch would not apply cleanly", ret["comment"]) # Test the reject_file option and ensure that the rejects are written # to the path specified. reject_file = salt.utils.files.mkstemp() ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch, reject_file=reject_file, @@ -4466,74 +4693,68 @@ class PatchTest(ModuleCase, SaltReturnAssertsMixin): ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Patch would not apply cleanly', ret['comment']) + self.assertIn("Patch would not apply cleanly", ret["comment"]) self.assertRegex( - ret['comment'], - 'saving rejects to (file )?{0}'.format(reject_file) + ret["comment"], "saving rejects to (file )?{0}".format(reject_file) ) def test_patch_single_file_remote_source(self): - ''' + """ Test file.patch using a patch applied to a single file, with the patch coming from a remote source. - ''' + """ # Try without a source_hash and without skip_verify=True, this should # fail with a message about the source_hash ret = self.run_state( - 'file.patch', - name=self.math_file, - source=self.math_patch_http, + "file.patch", name=self.math_file, source=self.math_patch_http, ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Unable to verify upstream hash', ret['comment']) + self.assertIn("Unable to verify upstream hash", ret["comment"]) # Re-run the state with a source hash, it should now succeed ret = self.run_state( - 'file.patch', + "file.patch", name=self.math_file, source=self.math_patch_http, source_hash=self.math_patch_hash, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') + self.assertEqual(ret["comment"], "Patch successfully applied") # Re-run again, this time with no hash and skip_verify=True to test # skipping hash verification ret = self.run_state( - 'file.patch', + "file.patch", name=self.math_file, source=self.math_patch_http, skip_verify=True, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) def test_patch_directory_remote_source(self): - ''' + """ Test file.patch using a patch applied to a directory, with changes spanning multiple files, and the patch file coming from a remote source. - ''' - self._check_patch_version('2.6') + """ + self._check_patch_version("2.6") # Try without a source_hash and without skip_verify=True, this should # fail with a message about the source_hash ret = self.run_state( - 'file.patch', - name=self.base_dir, - source=self.all_patch_http, - strip=1, + "file.patch", name=self.base_dir, source=self.all_patch_http, strip=1, ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Unable to verify upstream hash', ret['comment']) + self.assertIn("Unable to verify upstream hash", ret["comment"]) # Re-run the state with a source hash, it should now succeed ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch_http, source_hash=self.all_patch_hash, @@ -4541,12 +4762,12 @@ class PatchTest(ModuleCase, SaltReturnAssertsMixin): ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') + self.assertEqual(ret["comment"], "Patch successfully applied") # Re-run again, this time with no hash and skip_verify=True to test # skipping hash verification ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch_http, strip=1, @@ -4554,217 +4775,209 @@ class PatchTest(ModuleCase, SaltReturnAssertsMixin): ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) def test_patch_single_file_template(self): - ''' + """ Test file.patch using a patch applied to a single file, with jinja templating applied to the patch file. - ''' + """ ret = self.run_state( - 'file.patch', + "file.patch", name=self.numbers_file, source=self.numbers_patch_template, - template='jinja', + template="jinja", context=self.context, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') + self.assertEqual(ret["comment"], "Patch successfully applied") # Re-run the state, should succeed and there should be a message about # a partially-applied hunk. ret = self.run_state( - 'file.patch', + "file.patch", name=self.numbers_file, source=self.numbers_patch_template, - template='jinja', + template="jinja", context=self.context, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) def test_patch_directory_template(self): - ''' + """ Test file.patch using a patch applied to a directory, with changes spanning multiple files, and with jinja templating applied to the patch file. - ''' - self._check_patch_version('2.6') + """ + self._check_patch_version("2.6") ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch_template, - template='jinja', + template="jinja", context=self.context, strip=1, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') + self.assertEqual(ret["comment"], "Patch successfully applied") # Re-run the state, should succeed and there should be a message about # a partially-applied hunk. ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch_template, - template='jinja', + template="jinja", context=self.context, strip=1, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) def test_patch_single_file_remote_source_template(self): - ''' + """ Test file.patch using a patch applied to a single file, with the patch coming from a remote source. - ''' + """ # Try without a source_hash and without skip_verify=True, this should # fail with a message about the source_hash ret = self.run_state( - 'file.patch', + "file.patch", name=self.math_file, source=self.math_patch_template_http, - template='jinja', + template="jinja", context=self.context, ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Unable to verify upstream hash', ret['comment']) + self.assertIn("Unable to verify upstream hash", ret["comment"]) # Re-run the state with a source hash, it should now succeed ret = self.run_state( - 'file.patch', + "file.patch", name=self.math_file, source=self.math_patch_template_http, source_hash=self.math_patch_template_hash, - template='jinja', + template="jinja", context=self.context, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') + self.assertEqual(ret["comment"], "Patch successfully applied") # Re-run again, this time with no hash and skip_verify=True to test # skipping hash verification ret = self.run_state( - 'file.patch', + "file.patch", name=self.math_file, source=self.math_patch_template_http, - template='jinja', + template="jinja", context=self.context, skip_verify=True, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) def test_patch_directory_remote_source_template(self): - ''' + """ Test file.patch using a patch applied to a directory, with changes spanning multiple files, and the patch file coming from a remote source. - ''' - self._check_patch_version('2.6') + """ + self._check_patch_version("2.6") # Try without a source_hash and without skip_verify=True, this should # fail with a message about the source_hash ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch_template_http, - template='jinja', + template="jinja", context=self.context, strip=1, ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Unable to verify upstream hash', ret['comment']) + self.assertIn("Unable to verify upstream hash", ret["comment"]) # Re-run the state with a source hash, it should now succeed ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch_template_http, source_hash=self.all_patch_template_hash, - template='jinja', + template="jinja", context=self.context, strip=1, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') + self.assertEqual(ret["comment"], "Patch successfully applied") # Re-run again, this time with no hash and skip_verify=True to test # skipping hash verification ret = self.run_state( - 'file.patch', + "file.patch", name=self.base_dir, source=self.all_patch_template_http, - template='jinja', + template="jinja", context=self.context, strip=1, skip_verify=True, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) def test_patch_test_mode(self): - ''' + """ Test file.patch using test=True - ''' + """ # Try without a source_hash and without skip_verify=True, this should # fail with a message about the source_hash ret = self.run_state( - 'file.patch', - name=self.numbers_file, - source=self.numbers_patch, - test=True, + "file.patch", name=self.numbers_file, source=self.numbers_patch, test=True, ) self.assertSaltNoneReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'The patch would be applied') - self.assertTrue(ret['changes']) + self.assertEqual(ret["comment"], "The patch would be applied") + self.assertTrue(ret["changes"]) # Apply the patch for real. We'll then be able to test below that we # exit with a True rather than a None result if test=True is used on an # already-applied patch. ret = self.run_state( - 'file.patch', - name=self.numbers_file, - source=self.numbers_patch, + "file.patch", name=self.numbers_file, source=self.numbers_patch, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch successfully applied') - self.assertTrue(ret['changes']) + self.assertEqual(ret["comment"], "Patch successfully applied") + self.assertTrue(ret["changes"]) # Run again with test=True. Since the pre-check happens before we do # the __opts__['test'] check, we should exit with a True result just # the same as if we try to run this state on an already-patched file # *without* test=True. ret = self.run_state( - 'file.patch', - name=self.numbers_file, - source=self.numbers_patch, - test=True, + "file.patch", name=self.numbers_file, source=self.numbers_patch, test=True, ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertEqual(ret['comment'], 'Patch was already applied') - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["comment"], "Patch was already applied") + self.assertEqual(ret["changes"], {}) # Empty the file to ensure that the patch doesn't apply cleanly - with salt.utils.files.fopen(self.numbers_file, 'w'): + with salt.utils.files.fopen(self.numbers_file, "w"): pass # Run again with test=True. Similar to the above run, we are testing @@ -4772,64 +4985,69 @@ class PatchTest(ModuleCase, SaltReturnAssertsMixin): # case we should return a False result because we should already know # by this point that the patch will not apply cleanly. ret = self.run_state( - 'file.patch', - name=self.numbers_file, - source=self.numbers_patch, - test=True, + "file.patch", name=self.numbers_file, source=self.numbers_patch, test=True, ) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Patch would not apply cleanly', ret['comment']) - self.assertEqual(ret['changes'], {}) + self.assertIn("Patch would not apply cleanly", ret["comment"]) + self.assertEqual(ret["changes"], {}) -WIN_TEST_FILE = 'c:/testfile' +WIN_TEST_FILE = "c:/testfile" @destructiveTest -@skipIf(not IS_WINDOWS, 'windows test only') +@skipIf(not IS_WINDOWS, "windows test only") +@pytest.mark.windows_whitelisted class WinFileTest(ModuleCase): - ''' + """ Test for the file state on Windows - ''' + """ + def setUp(self): - self.run_state('file.managed', name=WIN_TEST_FILE, makedirs=True, contents='Only a test') + self.run_state( + "file.managed", name=WIN_TEST_FILE, makedirs=True, contents="Only a test" + ) def tearDown(self): - self.run_state('file.absent', name=WIN_TEST_FILE) + self.run_state("file.absent", name=WIN_TEST_FILE) def test_file_managed(self): - ''' + """ Test file.managed on Windows - ''' - self.assertTrue(self.run_state('file.exists', name=WIN_TEST_FILE)) + """ + self.assertTrue(self.run_state("file.exists", name=WIN_TEST_FILE)) def test_file_copy(self): - ''' + """ Test file.copy on Windows - ''' - ret = self.run_state('file.copy', name='c:/testfile_copy', makedirs=True, source=WIN_TEST_FILE) + """ + ret = self.run_state( + "file.copy", name="c:/testfile_copy", makedirs=True, source=WIN_TEST_FILE + ) self.assertTrue(ret) def test_file_comment(self): - ''' + """ Test file.comment on Windows - ''' - self.run_state('file.comment', name=WIN_TEST_FILE, regex='^Only') - with salt.utils.files.fopen(WIN_TEST_FILE, 'r') as fp_: - self.assertTrue(fp_.read().startswith('#Only')) + """ + self.run_state("file.comment", name=WIN_TEST_FILE, regex="^Only") + with salt.utils.files.fopen(WIN_TEST_FILE, "r") as fp_: + self.assertTrue(fp_.read().startswith("#Only")) def test_file_replace(self): - ''' + """ Test file.replace on Windows - ''' - self.run_state('file.replace', name=WIN_TEST_FILE, pattern='test', repl='testing') - with salt.utils.files.fopen(WIN_TEST_FILE, 'r') as fp_: - self.assertIn('testing', fp_.read()) + """ + self.run_state( + "file.replace", name=WIN_TEST_FILE, pattern="test", repl="testing" + ) + with salt.utils.files.fopen(WIN_TEST_FILE, "r") as fp_: + self.assertIn("testing", fp_.read()) def test_file_absent(self): - ''' + """ Test file.absent on Windows - ''' - ret = self.run_state('file.absent', name=WIN_TEST_FILE) + """ + ret = self.run_state("file.absent", name=WIN_TEST_FILE) self.assertTrue(ret) diff --git a/tests/integration/states/test_git.py b/tests/integration/states/test_git.py index ebc4799a4b5..8ec57ab174b 100644 --- a/tests/integration/states/test_git.py +++ b/tests/integration/states/test_git.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the Git state -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import functools import inspect import os @@ -13,90 +14,94 @@ import socket import string import tempfile -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS +# Import salt libs +import salt.utils.files +import salt.utils.path +from salt.ext.six.moves.urllib.parse import ( # pylint: disable=no-name-in-module + urlparse, +) +from salt.utils.versions import LooseVersion as _LooseVersion from tests.support.case import ModuleCase from tests.support.helpers import with_tempdir from tests.support.mixins import SaltReturnAssertsMixin -# Import salt libs -import salt.utils.files -import salt.utils.path -from salt.utils.versions import LooseVersion as _LooseVersion -from salt.ext.six.moves.urllib.parse import urlparse # pylint: disable=no-name-in-module +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS -TEST_REPO = 'https://github.com/saltstack/salt-test-repo.git' +TEST_REPO = "https://github.com/saltstack/salt-test-repo.git" def __check_git_version(caller, min_version, skip_msg): - ''' + """ Common logic for version check - ''' + """ if inspect.isclass(caller): - actual_setup = getattr(caller, 'setUp', None) + actual_setup = getattr(caller, "setUp", None) def setUp(self, *args, **kwargs): - if not salt.utils.path.which('git'): - self.skipTest('git is not installed') - git_version = self.run_function('git.version') + if not salt.utils.path.which("git"): + self.skipTest("git is not installed") + git_version = self.run_function("git.version") if _LooseVersion(git_version) < _LooseVersion(min_version): self.skipTest(skip_msg.format(min_version, git_version)) if actual_setup is not None: actual_setup(self, *args, **kwargs) + caller.setUp = setUp return caller @functools.wraps(caller) def wrapper(self, *args, **kwargs): - if not salt.utils.path.which('git'): - self.skipTest('git is not installed') - git_version = self.run_function('git.version') + if not salt.utils.path.which("git"): + self.skipTest("git is not installed") + git_version = self.run_function("git.version") if _LooseVersion(git_version) < _LooseVersion(min_version): self.skipTest(skip_msg.format(min_version, git_version)) return caller(self, *args, **kwargs) + return wrapper -def ensure_min_git(caller=None, min_version='1.6.5'): - ''' +def ensure_min_git(caller=None, min_version="1.6.5"): + """ Skip test if minimum supported git version is not installed - ''' + """ if caller is None: return functools.partial(ensure_min_git, min_version=min_version) return __check_git_version( - caller, - min_version, - 'git {0} or newer required to run this test (detected {1})' + caller, min_version, "git {0} or newer required to run this test (detected {1})" ) def uses_git_opts(caller): - ''' + """ Skip test if git_opts is not supported IMPORTANT! This decorator should be at the bottom of any decorators added to a given function. - ''' - min_version = '1.7.2' + """ + min_version = "1.7.2" return __check_git_version( caller, min_version, - 'git_opts only supported in git {0} and newer (detected {1})' + "git_opts only supported in git {0} and newer (detected {1})", ) class WithGitMirror(object): def __init__(self, repo_url, **kwargs): self.repo_url = repo_url - if 'dir' not in kwargs: - kwargs['dir'] = RUNTIME_VARS.TMP + if "dir" not in kwargs: + kwargs["dir"] = RUNTIME_VARS.TMP self.kwargs = kwargs def __call__(self, func): self.func = func return functools.wraps(func)( - lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) # pylint: disable=W0108 + # pylint: disable=unnecessary-lambda + lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) + # pylint: enable=unnecessary-lambda ) def wrap(self, testcase, *args, **kwargs): @@ -109,14 +114,13 @@ class WithGitMirror(object): os.rmdir(admin_dir) os.rmdir(clone_dir) # Create a URL to clone - mirror_url = 'file://' + mirror_dir + mirror_url = "file://" + mirror_dir # Mirror the repo - testcase.run_function( - 'git.clone', [mirror_dir], url=TEST_REPO, opts='--mirror') + testcase.run_function("git.clone", [mirror_dir], url=TEST_REPO, opts="--mirror") # Make sure the directory for the mirror now exists assert os.path.exists(mirror_dir) # Clone to the admin dir - ret = testcase.run_state('git.latest', name=mirror_url, target=admin_dir) + ret = testcase.run_state("git.latest", name=mirror_url, target=admin_dir) ret = ret[next(iter(ret))] assert os.path.exists(admin_dir) @@ -126,7 +130,9 @@ class WithGitMirror(object): # 2. Cloned admin dir for making/pushing changes to the mirror # 3. Yet-nonexistant clone_dir for the test function to use as a # destination for cloning. - return self.func(testcase, mirror_url, admin_dir, clone_dir, *args, **kwargs) + return self.func( + testcase, mirror_url, admin_dir, clone_dir, *args, **kwargs + ) finally: shutil.rmtree(mirror_dir, ignore_errors=True) shutil.rmtree(admin_dir, ignore_errors=True) @@ -138,18 +144,19 @@ with_git_mirror = WithGitMirror @ensure_min_git class GitTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the git state - ''' + """ + def setUp(self): domain = urlparse(TEST_REPO).netloc try: - if hasattr(socket, 'setdefaulttimeout'): + if hasattr(socket, "setdefaulttimeout"): # 10 second dns timeout socket.setdefaulttimeout(10) socket.gethostbyname(domain) except socket.error: - msg = 'error resolving {0}, possible network issue?' + msg = "error resolving {0}, possible network issue?" self.skipTest(msg.format(domain)) def tearDown(self): @@ -157,169 +164,149 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin): socket.setdefaulttimeout(None) def _head(self, cwd): - return self.run_function('git.rev_parse', [cwd, 'HEAD']) + return self.run_function("git.rev_parse", [cwd, "HEAD"]) @with_tempdir(create=False) def test_latest(self, target): - ''' + """ git.latest - ''' - ret = self.run_state( - 'git.latest', - name=TEST_REPO, - target=target - ) + """ + ret = self.run_state("git.latest", name=TEST_REPO, target=target) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isdir(os.path.join(target, '.git'))) + self.assertTrue(os.path.isdir(os.path.join(target, ".git"))) @with_tempdir(create=False) def test_latest_with_rev_and_submodules(self, target): - ''' + """ git.latest - ''' + """ ret = self.run_state( - 'git.latest', - name=TEST_REPO, - rev='develop', - target=target, - submodules=True + "git.latest", name=TEST_REPO, rev="develop", target=target, submodules=True ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isdir(os.path.join(target, '.git'))) + self.assertTrue(os.path.isdir(os.path.join(target, ".git"))) @with_tempdir(create=False) def test_latest_failure(self, target): - ''' + """ git.latest - ''' + """ ret = self.run_state( - 'git.latest', - name='https://youSpelledGitHubWrong.com/saltstack/salt-test-repo.git', - rev='develop', + "git.latest", + name="https://youSpelledGitHubWrong.com/saltstack/salt-test-repo.git", + rev="develop", target=target, - submodules=True + submodules=True, ) self.assertSaltFalseReturn(ret) - self.assertFalse(os.path.isdir(os.path.join(target, '.git'))) + self.assertFalse(os.path.isdir(os.path.join(target, ".git"))) @with_tempdir() def test_latest_empty_dir(self, target): - ''' + """ git.latest - ''' + """ ret = self.run_state( - 'git.latest', - name=TEST_REPO, - rev='develop', - target=target, - submodules=True + "git.latest", name=TEST_REPO, rev="develop", target=target, submodules=True ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isdir(os.path.join(target, '.git'))) + self.assertTrue(os.path.isdir(os.path.join(target, ".git"))) @with_tempdir(create=False) def test_latest_unless_no_cwd_issue_6800(self, target): - ''' + """ cwd=target was being passed to _run_check which blew up if target dir did not already exist. - ''' + """ ret = self.run_state( - 'git.latest', + "git.latest", name=TEST_REPO, - rev='develop', + rev="develop", target=target, - unless='test -e {0}'.format(target), - submodules=True + unless="test -e {0}".format(target), + submodules=True, ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isdir(os.path.join(target, '.git'))) + self.assertTrue(os.path.isdir(os.path.join(target, ".git"))) @with_tempdir(create=False) def test_numeric_rev(self, target): - ''' + """ git.latest with numeric revision - ''' + """ ret = self.run_state( - 'git.latest', + "git.latest", name=TEST_REPO, rev=0.11, target=target, submodules=True, - timeout=120 + timeout=120, ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isdir(os.path.join(target, '.git'))) + self.assertTrue(os.path.isdir(os.path.join(target, ".git"))) @with_tempdir(create=False) def test_latest_with_local_changes(self, target): - ''' + """ Ensure that we fail the state when there are local changes and succeed when force_reset is True. - ''' + """ # Clone repo - ret = self.run_state( - 'git.latest', - name=TEST_REPO, - target=target - ) + ret = self.run_state("git.latest", name=TEST_REPO, target=target) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isdir(os.path.join(target, '.git'))) + self.assertTrue(os.path.isdir(os.path.join(target, ".git"))) # Make change to LICENSE file. - with salt.utils.files.fopen(os.path.join(target, 'LICENSE'), 'a') as fp_: - fp_.write('Lorem ipsum dolor blah blah blah....\n') + with salt.utils.files.fopen(os.path.join(target, "LICENSE"), "a") as fp_: + fp_.write("Lorem ipsum dolor blah blah blah....\n") # Make sure that we now have uncommitted changes - self.assertTrue(self.run_function('git.diff', [target, 'HEAD'])) + self.assertTrue(self.run_function("git.diff", [target, "HEAD"])) # Re-run state with force_reset=False ret = self.run_state( - 'git.latest', - name=TEST_REPO, - target=target, - force_reset=False + "git.latest", name=TEST_REPO, target=target, force_reset=False ) self.assertSaltTrueReturn(ret) self.assertEqual( - ret[next(iter(ret))]['comment'], - ('Repository {0} is up-to-date, but with uncommitted changes. ' - 'Set \'force_reset\' to True to purge uncommitted changes.' - .format(target)) + ret[next(iter(ret))]["comment"], + ( + "Repository {0} is up-to-date, but with uncommitted changes. " + "Set 'force_reset' to True to purge uncommitted changes.".format(target) + ), ) # Now run the state with force_reset=True ret = self.run_state( - 'git.latest', - name=TEST_REPO, - target=target, - force_reset=True + "git.latest", name=TEST_REPO, target=target, force_reset=True ) self.assertSaltTrueReturn(ret) # Make sure that we no longer have uncommitted changes - self.assertFalse(self.run_function('git.diff', [target, 'HEAD'])) + self.assertFalse(self.run_function("git.diff", [target, "HEAD"])) @with_git_mirror(TEST_REPO) @uses_git_opts def test_latest_fast_forward(self, mirror_url, admin_dir, clone_dir): - ''' + """ Test running git.latest state a second time after changes have been made to the remote repo. - ''' + """ # Clone the repo - ret = self.run_state('git.latest', name=mirror_url, target=clone_dir) + ret = self.run_state("git.latest", name=mirror_url, target=clone_dir) ret = ret[next(iter(ret))] - assert ret['result'] + assert ret["result"] # Make a change to the repo by editing the file in the admin copy # of the repo and committing. head_pre = self._head(admin_dir) - with salt.utils.files.fopen(os.path.join(admin_dir, 'LICENSE'), 'a') as fp_: - fp_.write('Hello world!') + with salt.utils.files.fopen(os.path.join(admin_dir, "LICENSE"), "a") as fp_: + fp_.write("Hello world!") self.run_function( - 'git.commit', [admin_dir, 'added a line'], + "git.commit", + [admin_dir, "added a line"], git_opts='-c user.name="Foo Bar" -c user.email=foo@bar.com', - opts='-a', + opts="-a", ) # Make sure HEAD is pointing to a new SHA so we know we properly # committed our change. @@ -329,511 +316,408 @@ class GitTest(ModuleCase, SaltReturnAssertsMixin): # Push the change to the mirror # NOTE: the test will fail if the salt-test-repo's default branch # is changed. - self.run_function('git.push', [admin_dir, 'origin', 'develop']) + self.run_function("git.push", [admin_dir, "origin", "develop"]) # Re-run the git.latest state on the clone_dir - ret = self.run_state('git.latest', name=mirror_url, target=clone_dir) + ret = self.run_state("git.latest", name=mirror_url, target=clone_dir) ret = ret[next(iter(ret))] - assert ret['result'] + assert ret["result"] # Make sure that the clone_dir now has the correct SHA assert head_post == self._head(clone_dir) @with_tempdir(create=False) def _changed_local_branch_helper(self, target, rev, hint): - ''' + """ We're testing two almost identical cases, the only thing that differs is the rev used for the git.latest state. - ''' + """ # Clone repo - ret = self.run_state( - 'git.latest', - name=TEST_REPO, - rev=rev, - target=target - ) + ret = self.run_state("git.latest", name=TEST_REPO, rev=rev, target=target) self.assertSaltTrueReturn(ret) # Check out a new branch in the clone and make a commit, to ensure # that when we re-run the state, it is not a fast-forward change - self.run_function('git.checkout', [target, 'new_branch'], opts='-b') - with salt.utils.files.fopen(os.path.join(target, 'foo'), 'w'): + self.run_function("git.checkout", [target, "new_branch"], opts="-b") + with salt.utils.files.fopen(os.path.join(target, "foo"), "w"): pass - self.run_function('git.add', [target, '.']) + self.run_function("git.add", [target, "."]) self.run_function( - 'git.commit', [target, 'add file'], + "git.commit", + [target, "add file"], git_opts='-c user.name="Foo Bar" -c user.email=foo@bar.com', ) # Re-run the state, this should fail with a specific hint in the # comment field. - ret = self.run_state( - 'git.latest', - name=TEST_REPO, - rev=rev, - target=target - ) + ret = self.run_state("git.latest", name=TEST_REPO, rev=rev, target=target) self.assertSaltFalseReturn(ret) - comment = ret[next(iter(ret))]['comment'] + comment = ret[next(iter(ret))]["comment"] self.assertTrue(hint in comment) @uses_git_opts def test_latest_changed_local_branch_rev_head(self): - ''' + """ Test for presence of hint in failure message when the local branch has been changed and a the rev is set to HEAD This test will fail if the default branch for the salt-test-repo is ever changed. - ''' + """ self._changed_local_branch_helper( # pylint: disable=no-value-for-parameter - 'HEAD', - 'The default remote branch (develop) differs from the local ' - 'branch (new_branch)' + "HEAD", + "The default remote branch (develop) differs from the local " + "branch (new_branch)", ) @uses_git_opts def test_latest_changed_local_branch_rev_develop(self): - ''' + """ Test for presence of hint in failure message when the local branch has been changed and a non-HEAD rev is specified - ''' + """ self._changed_local_branch_helper( # pylint: disable=no-value-for-parameter - 'develop', - 'The desired rev (develop) differs from the name of the local ' - 'branch (new_branch)' + "develop", + "The desired rev (develop) differs from the name of the local " + "branch (new_branch)", ) @uses_git_opts @with_tempdir(create=False) @with_tempdir() def test_latest_updated_remote_rev(self, name, target): - ''' + """ Ensure that we don't exit early when checking for a fast-forward - ''' + """ # Initialize a new git repository - self.run_function('git.init', [name]) + self.run_function("git.init", [name]) # Add and commit a file - with salt.utils.files.fopen(os.path.join(name, 'foo.txt'), 'w') as fp_: - fp_.write('Hello world\n') - self.run_function('git.add', [name, '.']) + with salt.utils.files.fopen(os.path.join(name, "foo.txt"), "w") as fp_: + fp_.write("Hello world\n") + self.run_function("git.add", [name, "."]) self.run_function( - 'git.commit', [name, 'initial commit'], + "git.commit", + [name, "initial commit"], git_opts='-c user.name="Foo Bar" -c user.email=foo@bar.com', ) # Run the state to clone the repo we just created - ret = self.run_state( - 'git.latest', - name=name, - target=target, - ) + ret = self.run_state("git.latest", name=name, target=target,) self.assertSaltTrueReturn(ret) # Add another commit - with salt.utils.files.fopen(os.path.join(name, 'foo.txt'), 'w') as fp_: - fp_.write('Added a line\n') + with salt.utils.files.fopen(os.path.join(name, "foo.txt"), "w") as fp_: + fp_.write("Added a line\n") self.run_function( - 'git.commit', [name, 'added a line'], + "git.commit", + [name, "added a line"], git_opts='-c user.name="Foo Bar" -c user.email=foo@bar.com', - opts='-a', + opts="-a", ) # Run the state again. It should pass, if it doesn't then there was # a problem checking whether or not the change is a fast-forward. - ret = self.run_state( - 'git.latest', - name=name, - target=target, - ) + ret = self.run_state("git.latest", name=name, target=target,) self.assertSaltTrueReturn(ret) @with_tempdir(create=False) def test_latest_depth(self, target): - ''' + """ Test running git.latest state using the "depth" argument to limit the history. See #45394. - ''' + """ ret = self.run_state( - 'git.latest', - name=TEST_REPO, - rev='HEAD', - target=target, - depth=1 + "git.latest", name=TEST_REPO, rev="HEAD", target=target, depth=1 ) # HEAD is not a branch, this should fail self.assertSaltFalseReturn(ret) self.assertIn( - 'must be set to the name of a branch', - ret[next(iter(ret))]['comment'] + "must be set to the name of a branch", ret[next(iter(ret))]["comment"] ) ret = self.run_state( - 'git.latest', + "git.latest", name=TEST_REPO, - rev='non-default-branch', + rev="non-default-branch", target=target, - depth=1 + depth=1, ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isdir(os.path.join(target, '.git'))) + self.assertTrue(os.path.isdir(os.path.join(target, ".git"))) @with_git_mirror(TEST_REPO) @uses_git_opts def test_latest_sync_tags(self, mirror_url, admin_dir, clone_dir): - ''' + """ Test that a removed tag is properly reported as such and removed in the local clone, and that new tags are reported as new. - ''' - tag1 = 'mytag1' - tag2 = 'mytag2' + """ + tag1 = "mytag1" + tag2 = "mytag2" # Add and push a tag - self.run_function('git.tag', [admin_dir, tag1]) - self.run_function('git.push', [admin_dir, 'origin', tag1]) + self.run_function("git.tag", [admin_dir, tag1]) + self.run_function("git.push", [admin_dir, "origin", tag1]) # Clone the repo - ret = self.run_state('git.latest', name=mirror_url, target=clone_dir) + ret = self.run_state("git.latest", name=mirror_url, target=clone_dir) ret = ret[next(iter(ret))] - assert ret['result'] + assert ret["result"] # Now remove the tag - self.run_function('git.push', [admin_dir, 'origin', ':{0}'.format(tag1)]) + self.run_function("git.push", [admin_dir, "origin", ":{0}".format(tag1)]) # Add and push another tag - self.run_function('git.tag', [admin_dir, tag2]) - self.run_function('git.push', [admin_dir, 'origin', tag2]) + self.run_function("git.tag", [admin_dir, tag2]) + self.run_function("git.push", [admin_dir, "origin", tag2]) # Re-run the state with sync_tags=False. This should NOT delete the tag # from the local clone, but should report that a tag has been added. - ret = self.run_state('git.latest', - name=mirror_url, - target=clone_dir, - sync_tags=False) + ret = self.run_state( + "git.latest", name=mirror_url, target=clone_dir, sync_tags=False + ) ret = ret[next(iter(ret))] - assert ret['result'] + assert ret["result"] # Make ABSOLUTELY SURE both tags are present, since we shouldn't have # removed tag1. - all_tags = self.run_function('git.list_tags', [clone_dir]) + all_tags = self.run_function("git.list_tags", [clone_dir]) assert tag1 in all_tags assert tag2 in all_tags # Make sure the reported changes are correct - expected_changes = {'new_tags': [tag2]} - assert ret['changes'] == expected_changes, ret['changes'] + expected_changes = {"new_tags": [tag2]} + assert ret["changes"] == expected_changes, ret["changes"] # Re-run the state with sync_tags=True. This should remove the local # tag, since it doesn't exist in the remote repository. - ret = self.run_state('git.latest', - name=mirror_url, - target=clone_dir, - sync_tags=True) + ret = self.run_state( + "git.latest", name=mirror_url, target=clone_dir, sync_tags=True + ) ret = ret[next(iter(ret))] - assert ret['result'] + assert ret["result"] # Make ABSOLUTELY SURE the expected tags are present/gone - all_tags = self.run_function('git.list_tags', [clone_dir]) + all_tags = self.run_function("git.list_tags", [clone_dir]) assert tag1 not in all_tags assert tag2 in all_tags # Make sure the reported changes are correct - expected_changes = {'deleted_tags': [tag1]} - assert ret['changes'] == expected_changes, ret['changes'] + expected_changes = {"deleted_tags": [tag1]} + assert ret["changes"] == expected_changes, ret["changes"] @with_tempdir(create=False) def test_cloned(self, target): - ''' + """ Test git.cloned state - ''' + """ # Test mode - ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - test=True) + ret = self.run_state("git.cloned", name=TEST_REPO, target=target, test=True) ret = ret[next(iter(ret))] - assert ret['result'] is None - assert ret['changes'] == { - 'new': '{0} => {1}'.format(TEST_REPO, target) - } - assert ret['comment'] == '{0} would be cloned to {1}'.format( - TEST_REPO, - target - ) + assert ret["result"] is None + assert ret["changes"] == {"new": "{0} => {1}".format(TEST_REPO, target)} + assert ret["comment"] == "{0} would be cloned to {1}".format(TEST_REPO, target) # Now actually run the state - ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target) + ret = self.run_state("git.cloned", name=TEST_REPO, target=target) ret = ret[next(iter(ret))] - assert ret['result'] is True - assert ret['changes'] == { - 'new': '{0} => {1}'.format(TEST_REPO, target) - } - assert ret['comment'] == '{0} cloned to {1}'.format(TEST_REPO, target) + assert ret["result"] is True + assert ret["changes"] == {"new": "{0} => {1}".format(TEST_REPO, target)} + assert ret["comment"] == "{0} cloned to {1}".format(TEST_REPO, target) # Run the state again to test idempotence - ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target) + ret = self.run_state("git.cloned", name=TEST_REPO, target=target) ret = ret[next(iter(ret))] - assert ret['result'] is True - assert not ret['changes'] - assert ret['comment'] == 'Repository already exists at {0}'.format(target) + assert ret["result"] is True + assert not ret["changes"] + assert ret["comment"] == "Repository already exists at {0}".format(target) # Run the state again to test idempotence (test mode) - ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - test=True) + ret = self.run_state("git.cloned", name=TEST_REPO, target=target, test=True) ret = ret[next(iter(ret))] - assert not ret['changes'] - assert ret['result'] is True - assert ret['comment'] == 'Repository already exists at {0}'.format(target) + assert not ret["changes"] + assert ret["result"] is True + assert ret["comment"] == "Repository already exists at {0}".format(target) @with_tempdir(create=False) def test_cloned_with_branch(self, target): - ''' + """ Test git.cloned state with branch provided - ''' - old_branch = 'master' - new_branch = 'develop' - bad_branch = 'thisbranchdoesnotexist' + """ + old_branch = "master" + new_branch = "develop" + bad_branch = "thisbranchdoesnotexist" # Test mode ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - branch=old_branch, - test=True) + "git.cloned", name=TEST_REPO, target=target, branch=old_branch, test=True + ) ret = ret[next(iter(ret))] - assert ret['result'] is None - assert ret['changes'] == { - 'new': '{0} => {1}'.format(TEST_REPO, target) - } - assert ret['comment'] == ( - '{0} would be cloned to {1} with branch \'{2}\''.format( - TEST_REPO, - target, - old_branch + assert ret["result"] is None + assert ret["changes"] == {"new": "{0} => {1}".format(TEST_REPO, target)} + assert ret["comment"] == ( + "{0} would be cloned to {1} with branch '{2}'".format( + TEST_REPO, target, old_branch ) ) # Now actually run the state ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - branch=old_branch) + "git.cloned", name=TEST_REPO, target=target, branch=old_branch + ) ret = ret[next(iter(ret))] - assert ret['result'] is True - assert ret['changes'] == { - 'new': '{0} => {1}'.format(TEST_REPO, target) - } - assert ret['comment'] == ( - '{0} cloned to {1} with branch \'{2}\''.format( - TEST_REPO, - target, - old_branch - ) + assert ret["result"] is True + assert ret["changes"] == {"new": "{0} => {1}".format(TEST_REPO, target)} + assert ret["comment"] == ( + "{0} cloned to {1} with branch '{2}'".format(TEST_REPO, target, old_branch) ) # Run the state again to test idempotence ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - branch=old_branch) + "git.cloned", name=TEST_REPO, target=target, branch=old_branch + ) ret = ret[next(iter(ret))] - assert ret['result'] is True - assert not ret['changes'] - assert ret['comment'] == ( - 'Repository already exists at {0} ' - 'and is checked out to branch \'{1}\''.format(target, old_branch) + assert ret["result"] is True + assert not ret["changes"] + assert ret["comment"] == ( + "Repository already exists at {0} " + "and is checked out to branch '{1}'".format(target, old_branch) ) # Run the state again to test idempotence (test mode) ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - test=True, - branch=old_branch) + "git.cloned", name=TEST_REPO, target=target, test=True, branch=old_branch + ) ret = ret[next(iter(ret))] - assert ret['result'] is True - assert not ret['changes'] - assert ret['comment'] == ( - 'Repository already exists at {0} ' - 'and is checked out to branch \'{1}\''.format(target, old_branch) + assert ret["result"] is True + assert not ret["changes"] + assert ret["comment"] == ( + "Repository already exists at {0} " + "and is checked out to branch '{1}'".format(target, old_branch) ) # Change branch (test mode) ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - branch=new_branch, - test=True) - ret = ret[next(iter(ret))] - assert ret['result'] is None - assert ret['changes'] == { - 'branch': {'old': old_branch, 'new': new_branch} - } - assert ret['comment'] == 'Branch would be changed to \'{0}\''.format( - new_branch + "git.cloned", name=TEST_REPO, target=target, branch=new_branch, test=True ) + ret = ret[next(iter(ret))] + assert ret["result"] is None + assert ret["changes"] == {"branch": {"old": old_branch, "new": new_branch}} + assert ret["comment"] == "Branch would be changed to '{0}'".format(new_branch) # Now really change the branch ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - branch=new_branch) - ret = ret[next(iter(ret))] - assert ret['result'] is True - assert ret['changes'] == { - 'branch': {'old': old_branch, 'new': new_branch} - } - assert ret['comment'] == 'Branch changed to \'{0}\''.format( - new_branch + "git.cloned", name=TEST_REPO, target=target, branch=new_branch ) + ret = ret[next(iter(ret))] + assert ret["result"] is True + assert ret["changes"] == {"branch": {"old": old_branch, "new": new_branch}} + assert ret["comment"] == "Branch changed to '{0}'".format(new_branch) # Change back to original branch. This tests that we don't attempt to # checkout a new branch (i.e. git checkout -b) for a branch that exists # locally, as that would fail. ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - branch=old_branch) - ret = ret[next(iter(ret))] - assert ret['result'] is True - assert ret['changes'] == { - 'branch': {'old': new_branch, 'new': old_branch} - } - assert ret['comment'] == 'Branch changed to \'{0}\''.format( - old_branch + "git.cloned", name=TEST_REPO, target=target, branch=old_branch ) + ret = ret[next(iter(ret))] + assert ret["result"] is True + assert ret["changes"] == {"branch": {"old": new_branch, "new": old_branch}} + assert ret["comment"] == "Branch changed to '{0}'".format(old_branch) # Test switching to a nonexistant branch. This should fail. ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - branch=bad_branch) + "git.cloned", name=TEST_REPO, target=target, branch=bad_branch + ) ret = ret[next(iter(ret))] - assert ret['result'] is False - assert not ret['changes'] - assert ret['comment'].startswith( - 'Failed to change branch to \'{0}\':'.format(bad_branch) + assert ret["result"] is False + assert not ret["changes"] + assert ret["comment"].startswith( + "Failed to change branch to '{0}':".format(bad_branch) ) @with_tempdir(create=False) - @ensure_min_git(min_version='1.7.10') + @ensure_min_git(min_version="1.7.10") def test_cloned_with_nonexistant_branch(self, target): - ''' + """ Test git.cloned state with a nonexistant branch provided - ''' - branch = 'thisbranchdoesnotexist' + """ + branch = "thisbranchdoesnotexist" # Test mode ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - branch=branch, - test=True) + "git.cloned", name=TEST_REPO, target=target, branch=branch, test=True + ) ret = ret[next(iter(ret))] - assert ret['result'] is None - assert ret['changes'] - assert ret['comment'] == ( - '{0} would be cloned to {1} with branch \'{2}\''.format( - TEST_REPO, - target, - branch + assert ret["result"] is None + assert ret["changes"] + assert ret["comment"] == ( + "{0} would be cloned to {1} with branch '{2}'".format( + TEST_REPO, target, branch ) ) # Now actually run the state - ret = self.run_state( - 'git.cloned', - name=TEST_REPO, - target=target, - branch=branch) + ret = self.run_state("git.cloned", name=TEST_REPO, target=target, branch=branch) ret = ret[next(iter(ret))] - assert ret['result'] is False - assert not ret['changes'] - assert ret['comment'].startswith('Clone failed:') - assert 'not found in upstream origin' in ret['comment'] + assert ret["result"] is False + assert not ret["changes"] + assert ret["comment"].startswith("Clone failed:") + assert "not found in upstream origin" in ret["comment"] @with_tempdir(create=False) def test_present(self, name): - ''' + """ git.present - ''' - ret = self.run_state( - 'git.present', - name=name, - bare=True - ) + """ + ret = self.run_state("git.present", name=name, bare=True) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isfile(os.path.join(name, 'HEAD'))) + self.assertTrue(os.path.isfile(os.path.join(name, "HEAD"))) @with_tempdir() def test_present_failure(self, name): - ''' + """ git.present - ''' - fname = os.path.join(name, 'stoptheprocess') + """ + fname = os.path.join(name, "stoptheprocess") - with salt.utils.files.fopen(fname, 'a'): + with salt.utils.files.fopen(fname, "a"): pass - ret = self.run_state( - 'git.present', - name=name, - bare=True - ) + ret = self.run_state("git.present", name=name, bare=True) self.assertSaltFalseReturn(ret) - self.assertFalse(os.path.isfile(os.path.join(name, 'HEAD'))) + self.assertFalse(os.path.isfile(os.path.join(name, "HEAD"))) @with_tempdir() def test_present_empty_dir(self, name): - ''' + """ git.present - ''' - ret = self.run_state( - 'git.present', - name=name, - bare=True - ) + """ + ret = self.run_state("git.present", name=name, bare=True) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isfile(os.path.join(name, 'HEAD'))) + self.assertTrue(os.path.isfile(os.path.join(name, "HEAD"))) @with_tempdir() def test_config_set_value_with_space_character(self, name): - ''' + """ git.config - ''' - self.run_function('git.init', [name]) + """ + self.run_function("git.init", [name]) ret = self.run_state( - 'git.config_set', - name='user.name', - value='foo bar', + "git.config_set", + name="user.name", + value="foo bar", repo=name, - **{'global': False}) + **{"global": False} + ) self.assertSaltTrueReturn(ret) @ensure_min_git @uses_git_opts class LocalRepoGitTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Tests which do no require connectivity to github.com - ''' + """ + def setUp(self): self.repo = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.admin = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -842,293 +726,256 @@ class LocalRepoGitTest(ModuleCase, SaltReturnAssertsMixin): self.addCleanup(shutil.rmtree, dirname, ignore_errors=True) # Create bare repo - self.run_function('git.init', [self.repo], bare=True) + self.run_function("git.init", [self.repo], bare=True) # Clone bare repo - self.run_function('git.clone', [self.admin], url=self.repo) - self._commit(self.admin, '', message='initial commit') + self.run_function("git.clone", [self.admin], url=self.repo) + self._commit(self.admin, "", message="initial commit") self._push(self.admin) def _commit(self, repo_path, content, message): - with salt.utils.files.fopen(os.path.join(repo_path, 'foo'), 'a') as fp_: + with salt.utils.files.fopen(os.path.join(repo_path, "foo"), "a") as fp_: fp_.write(content) - self.run_function('git.add', [repo_path, '.']) + self.run_function("git.add", [repo_path, "."]) self.run_function( - 'git.commit', [repo_path, message], + "git.commit", + [repo_path, message], git_opts='-c user.name="Foo Bar" -c user.email=foo@bar.com', ) - def _push(self, repo_path, remote='origin', ref='master'): - self.run_function('git.push', [repo_path], remote=remote, ref=ref) + def _push(self, repo_path, remote="origin", ref="master"): + self.run_function("git.push", [repo_path], remote=remote, ref=ref) def _test_latest_force_reset_setup(self): # Perform the initial clone - ret = self.run_state( - 'git.latest', - name=self.repo, - target=self.target) + ret = self.run_state("git.latest", name=self.repo, target=self.target) self.assertSaltTrueReturn(ret) # Make and push changes to remote repo - self._commit(self.admin, - content='Hello world!\n', - message='added a line') + self._commit(self.admin, content="Hello world!\n", message="added a line") self._push(self.admin) # Make local changes to clone, but don't commit them - with salt.utils.files.fopen(os.path.join(self.target, 'foo'), 'a') as fp_: - fp_.write('Local changes!\n') + with salt.utils.files.fopen(os.path.join(self.target, "foo"), "a") as fp_: + fp_.write("Local changes!\n") def test_latest_force_reset_remote_changes(self): - ''' + """ This tests that an otherwise fast-forward change with local chanegs will not reset local changes when force_reset='remote_changes' - ''' + """ self._test_latest_force_reset_setup() # This should fail because of the local changes - ret = self.run_state( - 'git.latest', - name=self.repo, - target=self.target) + ret = self.run_state("git.latest", name=self.repo, target=self.target) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('there are uncommitted changes', ret['comment']) - self.assertIn( - 'Set \'force_reset\' to True (or \'remote-changes\')', - ret['comment'] - ) - self.assertEqual(ret['changes'], {}) + self.assertIn("there are uncommitted changes", ret["comment"]) + self.assertIn("Set 'force_reset' to True (or 'remote-changes')", ret["comment"]) + self.assertEqual(ret["changes"], {}) # Now run again with force_reset='remote_changes', the state should # succeed and discard the local changes ret = self.run_state( - 'git.latest', + "git.latest", name=self.repo, target=self.target, - force_reset='remote-changes') + force_reset="remote-changes", + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Uncommitted changes were discarded', ret['comment']) - self.assertIn('Repository was fast-forwarded', ret['comment']) - self.assertNotIn('forced update', ret['changes']) - self.assertIn('revision', ret['changes']) + self.assertIn("Uncommitted changes were discarded", ret["comment"]) + self.assertIn("Repository was fast-forwarded", ret["comment"]) + self.assertNotIn("forced update", ret["changes"]) + self.assertIn("revision", ret["changes"]) # Add new local changes, but don't commit them - with salt.utils.files.fopen(os.path.join(self.target, 'foo'), 'a') as fp_: - fp_.write('More local changes!\n') + with salt.utils.files.fopen(os.path.join(self.target, "foo"), "a") as fp_: + fp_.write("More local changes!\n") # Now run again with force_reset='remote_changes', the state should # succeed with an up-to-date message and mention that there are local # changes, telling the user how to discard them. ret = self.run_state( - 'git.latest', + "git.latest", name=self.repo, target=self.target, - force_reset='remote-changes') + force_reset="remote-changes", + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('up-to-date, but with uncommitted changes', ret['comment']) + self.assertIn("up-to-date, but with uncommitted changes", ret["comment"]) self.assertIn( - 'Set \'force_reset\' to True to purge uncommitted changes', - ret['comment'] + "Set 'force_reset' to True to purge uncommitted changes", ret["comment"] ) - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["changes"], {}) def test_latest_force_reset_true_fast_forward(self): - ''' + """ This tests that an otherwise fast-forward change with local chanegs does reset local changes when force_reset=True - ''' + """ self._test_latest_force_reset_setup() # Test that local changes are discarded and that we fast-forward ret = self.run_state( - 'git.latest', - name=self.repo, - target=self.target, - force_reset=True) + "git.latest", name=self.repo, target=self.target, force_reset=True + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Uncommitted changes were discarded', ret['comment']) - self.assertIn('Repository was fast-forwarded', ret['comment']) + self.assertIn("Uncommitted changes were discarded", ret["comment"]) + self.assertIn("Repository was fast-forwarded", ret["comment"]) # Add new local changes - with salt.utils.files.fopen(os.path.join(self.target, 'foo'), 'a') as fp_: - fp_.write('More local changes!\n') + with salt.utils.files.fopen(os.path.join(self.target, "foo"), "a") as fp_: + fp_.write("More local changes!\n") # Running without setting force_reset should mention uncommitted changes - ret = self.run_state( - 'git.latest', - name=self.repo, - target=self.target) + ret = self.run_state("git.latest", name=self.repo, target=self.target) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('up-to-date, but with uncommitted changes', ret['comment']) + self.assertIn("up-to-date, but with uncommitted changes", ret["comment"]) self.assertIn( - 'Set \'force_reset\' to True to purge uncommitted changes', - ret['comment'] + "Set 'force_reset' to True to purge uncommitted changes", ret["comment"] ) - self.assertEqual(ret['changes'], {}) + self.assertEqual(ret["changes"], {}) # Test that local changes are discarded ret = self.run_state( - 'git.latest', - name=TEST_REPO, - target=self.target, - force_reset=True) + "git.latest", name=TEST_REPO, target=self.target, force_reset=True + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - assert 'Uncommitted changes were discarded' in ret['comment'] - assert 'Repository was hard-reset' in ret['comment'] - assert 'forced update' in ret['changes'] + assert "Uncommitted changes were discarded" in ret["comment"] + assert "Repository was hard-reset" in ret["comment"] + assert "forced update" in ret["changes"] def test_latest_force_reset_true_non_fast_forward(self): - ''' + """ This tests that a non fast-forward change with divergent commits fails unless force_reset=True. - ''' + """ self._test_latest_force_reset_setup() # Reset to remote HEAD ret = self.run_state( - 'git.latest', - name=self.repo, - target=self.target, - force_reset=True) + "git.latest", name=self.repo, target=self.target, force_reset=True + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Uncommitted changes were discarded', ret['comment']) - self.assertIn('Repository was fast-forwarded', ret['comment']) + self.assertIn("Uncommitted changes were discarded", ret["comment"]) + self.assertIn("Repository was fast-forwarded", ret["comment"]) # Make and push changes to remote repo - self._commit(self.admin, - content='New line\n', - message='added another line') + self._commit(self.admin, content="New line\n", message="added another line") self._push(self.admin) # Make different changes to local file and commit locally - self._commit(self.target, - content='Different new line\n', - message='added a different line') + self._commit( + self.target, + content="Different new line\n", + message="added a different line", + ) # This should fail since the local clone has diverged and cannot # fast-forward to the remote rev - ret = self.run_state( - 'git.latest', - name=self.repo, - target=self.target) + ret = self.run_state("git.latest", name=self.repo, target=self.target) self.assertSaltFalseReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('this is not a fast-forward merge', ret['comment']) - self.assertIn( - 'Set \'force_reset\' to True to force this update', - ret['comment'] - ) - self.assertEqual(ret['changes'], {}) + self.assertIn("this is not a fast-forward merge", ret["comment"]) + self.assertIn("Set 'force_reset' to True to force this update", ret["comment"]) + self.assertEqual(ret["changes"], {}) # Repeat the state with force_reset=True and confirm that the hard # reset was performed ret = self.run_state( - 'git.latest', - name=self.repo, - target=self.target, - force_reset=True) + "git.latest", name=self.repo, target=self.target, force_reset=True + ) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertIn('Repository was hard-reset', ret['comment']) - self.assertIn('forced update', ret['changes']) - self.assertIn('revision', ret['changes']) + self.assertIn("Repository was hard-reset", ret["comment"]) + self.assertIn("forced update", ret["changes"]) + self.assertIn("revision", ret["changes"]) def test_renamed_default_branch(self): - ''' + """ Test the case where the remote branch has been removed https://github.com/saltstack/salt/issues/36242 - ''' + """ # Rename remote 'master' branch to 'develop' os.rename( - os.path.join(self.repo, 'refs', 'heads', 'master'), - os.path.join(self.repo, 'refs', 'heads', 'develop') + os.path.join(self.repo, "refs", "heads", "master"), + os.path.join(self.repo, "refs", "heads", "develop"), ) # Run git.latest state. This should successfully clone and fail with a # specific error in the comment field. ret = self.run_state( - 'git.latest', - name=self.repo, - target=self.target, - rev='develop', + "git.latest", name=self.repo, target=self.target, rev="develop", ) self.assertSaltFalseReturn(ret) self.assertEqual( - ret[next(iter(ret))]['comment'], - 'Remote HEAD refers to a ref that does not exist. ' - 'This can happen when the default branch on the ' - 'remote repository is renamed or deleted. If you ' - 'are unable to fix the remote repository, you can ' - 'work around this by setting the \'branch\' argument ' - '(which will ensure that the named branch is created ' - 'if it does not already exist).\n\n' - 'Changes already made: {0} cloned to {1}' - .format(self.repo, self.target) + ret[next(iter(ret))]["comment"], + "Remote HEAD refers to a ref that does not exist. " + "This can happen when the default branch on the " + "remote repository is renamed or deleted. If you " + "are unable to fix the remote repository, you can " + "work around this by setting the 'branch' argument " + "(which will ensure that the named branch is created " + "if it does not already exist).\n\n" + "Changes already made: {0} cloned to {1}".format(self.repo, self.target), ) self.assertEqual( - ret[next(iter(ret))]['changes'], - {'new': '{0} => {1}'.format(self.repo, self.target)} + ret[next(iter(ret))]["changes"], + {"new": "{0} => {1}".format(self.repo, self.target)}, ) # Run git.latest state again. This should fail again, with a different # error in the comment field, and should not change anything. ret = self.run_state( - 'git.latest', - name=self.repo, - target=self.target, - rev='develop', + "git.latest", name=self.repo, target=self.target, rev="develop", ) self.assertSaltFalseReturn(ret) self.assertEqual( - ret[next(iter(ret))]['comment'], - 'Cannot set/unset upstream tracking branch, local ' - 'HEAD refers to nonexistent branch. This may have ' - 'been caused by cloning a remote repository for which ' - 'the default branch was renamed or deleted. If you ' - 'are unable to fix the remote repository, you can ' - 'work around this by setting the \'branch\' argument ' - '(which will ensure that the named branch is created ' - 'if it does not already exist).' + ret[next(iter(ret))]["comment"], + "Cannot set/unset upstream tracking branch, local " + "HEAD refers to nonexistent branch. This may have " + "been caused by cloning a remote repository for which " + "the default branch was renamed or deleted. If you " + "are unable to fix the remote repository, you can " + "work around this by setting the 'branch' argument " + "(which will ensure that the named branch is created " + "if it does not already exist).", ) - self.assertEqual(ret[next(iter(ret))]['changes'], {}) + self.assertEqual(ret[next(iter(ret))]["changes"], {}) # Run git.latest state again with a branch manually set. This should # checkout a new branch and the state should pass. ret = self.run_state( - 'git.latest', + "git.latest", name=self.repo, target=self.target, - rev='develop', - branch='develop', + rev="develop", + branch="develop", ) # State should succeed self.assertSaltTrueReturn(ret) self.assertSaltCommentRegexpMatches( ret, - 'New branch \'develop\' was checked out, with origin/develop ' - r'\([0-9a-f]{7}\) as a starting point' + "New branch 'develop' was checked out, with origin/develop " + r"\([0-9a-f]{7}\) as a starting point", ) # Only the revision should be in the changes dict. - self.assertEqual( - list(ret[next(iter(ret))]['changes'].keys()), - ['revision'] - ) + self.assertEqual(list(ret[next(iter(ret))]["changes"].keys()), ["revision"]) # Since the remote repo was incorrectly set up, the local head should # not exist (therefore the old revision should be None). - self.assertEqual( - ret[next(iter(ret))]['changes']['revision']['old'], - None - ) + self.assertEqual(ret[next(iter(ret))]["changes"]["revision"]["old"], None) # Make sure the new revision is a SHA (40 chars, all hex) + self.assertTrue(len(ret[next(iter(ret))]["changes"]["revision"]["new"]) == 40) self.assertTrue( - len(ret[next(iter(ret))]['changes']['revision']['new']) == 40) - self.assertTrue( - all([x in string.hexdigits for x in - ret[next(iter(ret))]['changes']['revision']['new']]) + all( + [ + x in string.hexdigits + for x in ret[next(iter(ret))]["changes"]["revision"]["new"] + ] + ) ) diff --git a/tests/integration/states/test_handle_error.py b/tests/integration/states/test_handle_error.py index 394ebb76e10..7558aacffed 100644 --- a/tests/integration/states/test_handle_error.py +++ b/tests/integration/states/test_handle_error.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" tests for host state -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -11,14 +11,17 @@ from tests.support.case import ModuleCase class HandleErrorTest(ModuleCase): - ''' + """ Validate that ordering works correctly - ''' + """ + def test_function_do_not_return_dictionary_type(self): - ''' + """ Handling a case when function returns anything but a dictionary type - ''' - ret = self.run_function('state.sls', ['issue-9983-handleerror']) - self.assertTrue('Data must be a dictionary type' in ret[[a for a in ret][0]]['comment']) - self.assertTrue(not ret[[a for a in ret][0]]['result']) - self.assertTrue(ret[[a for a in ret][0]]['changes'] == {}) + """ + ret = self.run_function("state.sls", ["issue-9983-handleerror"]) + self.assertTrue( + "Data must be a dictionary type" in ret[[a for a in ret][0]]["comment"] + ) + self.assertTrue(not ret[[a for a in ret][0]]["result"]) + self.assertTrue(ret[[a for a in ret][0]]["changes"] == {}) diff --git a/tests/integration/states/test_handle_iorder.py b/tests/integration/states/test_handle_iorder.py index fb1fba3ca7b..d2c1bcaa8c0 100644 --- a/tests/integration/states/test_handle_iorder.py +++ b/tests/integration/states/test_handle_iorder.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" tests for host state -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -11,16 +11,19 @@ from tests.support.case import ModuleCase class HandleOrderTest(ModuleCase): - ''' + """ Validate that ordering works correctly - ''' + """ + def test_handle_iorder(self): - ''' + """ Test the error with multiple states of the same type - ''' - ret = self.run_function('state.show_low_sls', mods='issue-7649-handle-iorder') + """ + ret = self.run_function("state.show_low_sls", mods="issue-7649-handle-iorder") - sorted_chunks = [chunk['name'] for chunk in sorted(ret, key=lambda c: c.get('order'))] + sorted_chunks = [ + chunk["name"] for chunk in sorted(ret, key=lambda c: c.get("order")) + ] - expected = ['./configure', 'make', 'make install'] + expected = ["./configure", "make", "make install"] self.assertEqual(expected, sorted_chunks) diff --git a/tests/integration/states/test_host.py b/tests/integration/states/test_host.py index d16f3b17ca0..fb57eb08c15 100644 --- a/tests/integration/states/test_host.py +++ b/tests/integration/states/test_host.py @@ -1,55 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" tests for host state -''' +""" -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import shutil -import logging -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin - -# Import salt libs +import pytest import salt.utils.files import salt.utils.stringutils +from tests.support.case import ModuleCase +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS log = logging.getLogger(__name__) +@pytest.mark.windows_whitelisted class HostTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the host state - ''' + """ @classmethod def setUpClass(cls): - cls.hosts_file = os.path.join(RUNTIME_VARS.TMP, 'hosts') + cls.hosts_file = os.path.join(RUNTIME_VARS.TMP, "hosts") def __clear_hosts(self): - ''' + """ Delete the tmp hosts file - ''' + """ if os.path.isfile(self.hosts_file): os.remove(self.hosts_file) def setUp(self): - shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, 'hosts'), self.hosts_file) + shutil.copyfile(os.path.join(RUNTIME_VARS.FILES, "hosts"), self.hosts_file) self.addCleanup(self.__clear_hosts) super(HostTest, self).setUp() def test_present(self): - ''' + """ host.present - ''' - name = 'spam.bacon' - ip = '10.10.10.10' - ret = self.run_state('host.present', name=name, ip=ip) + """ + name = "spam.bacon" + ip = "10.10.10.10" + ret = self.run_state("host.present", name=name, ip=ip) self.assertSaltTrueReturn(ret) with salt.utils.files.fopen(self.hosts_file) as fp_: output = salt.utils.stringutils.to_unicode(fp_.read()) - self.assertIn('{0}\t\t{1}'.format(ip, name), output) + self.assertIn("{0}\t\t{1}".format(ip, name), output) diff --git a/tests/integration/states/test_keystone.py b/tests/integration/states/test_keystone.py index a02b1d8a14c..be9bc0402d2 100644 --- a/tests/integration/states/test_keystone.py +++ b/tests/integration/states/test_keystone.py @@ -1,17 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the Keystone states -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.unit import skipIf from tests.support.helpers import destructiveTest from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf log = logging.getLogger(__name__) @@ -24,258 +25,315 @@ except ImportError: @skipIf( NO_KEYSTONE, - 'Please install keystoneclient and a keystone server before running' - 'keystone integration tests.' + "Please install keystoneclient and a keystone server before running" + "keystone integration tests.", ) class KeystoneStateTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the keystone state - ''' - endpoint = 'http://localhost:35357/v2.0' - token = 'administrator' + """ + + endpoint = "http://localhost:35357/v2.0" + token = "administrator" @destructiveTest def setUp(self): - ret = self.run_state('keystone.service_present', - name='keystone', - description='OpenStack Identity', - service_type='identity', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-keystone_|-keystone_|-service_present']['result']) + ret = self.run_state( + "keystone.service_present", + name="keystone", + description="OpenStack Identity", + service_type="identity", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue( + ret["keystone_|-keystone_|-keystone_|-service_present"]["result"] + ) - ret = self.run_state('keystone.endpoint_present', - name='keystone', - region='RegionOne', - publicurl='http://localhost:5000/v2.0', - internalurl='http://localhost:5000/v2.0', - adminurl='http://localhost:35357/v2.0', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-keystone_|-keystone_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="keystone", + region="RegionOne", + publicurl="http://localhost:5000/v2.0", + internalurl="http://localhost:5000/v2.0", + adminurl="http://localhost:35357/v2.0", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue( + ret["keystone_|-keystone_|-keystone_|-endpoint_present"]["result"] + ) - ret = self.run_state('keystone.tenant_present', - name='admin', - description='Admin Project', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-admin_|-admin_|-tenant_present']['result']) + ret = self.run_state( + "keystone.tenant_present", + name="admin", + description="Admin Project", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-admin_|-admin_|-tenant_present"]["result"]) - ret = self.run_state('keystone.tenant_present', - name='demo', - description='Demo Project', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-demo_|-demo_|-tenant_present']['result']) + ret = self.run_state( + "keystone.tenant_present", + name="demo", + description="Demo Project", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-demo_|-demo_|-tenant_present"]["result"]) - ret = self.run_state('keystone.role_present', - name='admin', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-admin_|-admin_|-role_present']['result']) + ret = self.run_state( + "keystone.role_present", + name="admin", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-admin_|-admin_|-role_present"]["result"]) - ret = self.run_state('keystone.role_present', - name='user', - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-user_|-user_|-role_present']['result']) + ret = self.run_state( + "keystone.role_present", + name="user", + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-user_|-user_|-role_present"]["result"]) - ret = self.run_state('keystone.user_present', - name='admin', - email='admin@example.com', - password='adminpass', - tenant='admin', - roles={'admin': ['admin']}, - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-admin_|-admin_|-user_present']['result']) + ret = self.run_state( + "keystone.user_present", + name="admin", + email="admin@example.com", + password="adminpass", + tenant="admin", + roles={"admin": ["admin"]}, + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-admin_|-admin_|-user_present"]["result"]) - ret = self.run_state('keystone.user_present', - name='demo', - email='demo@example.com', - password='demopass', - tenant='demo', - roles={'demo': ['user']}, - connection_endpoint=self.endpoint, - connection_token=self.token) - self.assertTrue(ret['keystone_|-demo_|-demo_|-user_present']['result']) + ret = self.run_state( + "keystone.user_present", + name="demo", + email="demo@example.com", + password="demopass", + tenant="demo", + roles={"demo": ["user"]}, + connection_endpoint=self.endpoint, + connection_token=self.token, + ) + self.assertTrue(ret["keystone_|-demo_|-demo_|-user_present"]["result"]) @destructiveTest def test_keystone_v2(self): - ret = self.run_state('keystone.service_present', - name='testv2', - description='Nova Service', - service_type='compute', - profile='adminv2') - self.assertTrue(ret['keystone_|-testv2_|-testv2_|-service_present']['result']) + ret = self.run_state( + "keystone.service_present", + name="testv2", + description="Nova Service", + service_type="compute", + profile="adminv2", + ) + self.assertTrue(ret["keystone_|-testv2_|-testv2_|-service_present"]["result"]) - ret = self.run_state('keystone.endpoint_present', - name='nova', - description='Nova Service', - publicurl='http://localhost:8774/v2.1/%(tenant_id)s', - internalurl='http://localhost:8774/v2.1/%(tenant_id)s', - adminurl='http://localhost:8774/v2.1/%(tenant_id)s', - region='RegionOne', - profile='adminv2') - self.assertTrue(ret['keystone_|-nova_|-nova_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="nova", + description="Nova Service", + publicurl="http://localhost:8774/v2.1/%(tenant_id)s", + internalurl="http://localhost:8774/v2.1/%(tenant_id)s", + adminurl="http://localhost:8774/v2.1/%(tenant_id)s", + region="RegionOne", + profile="adminv2", + ) + self.assertTrue(ret["keystone_|-nova_|-nova_|-endpoint_present"]["result"]) # Region Two - ret = self.run_state('keystone.endpoint_present', - name='nova', - description='Nova Service', - publicurl='http://localhost:8774/v2.1/%(tenant_id)s', - internalurl='http://localhost:8774/v2.1/%(tenant_id)s', - adminurl='http://localhost:8774/v2.1/%(tenant_id)s', - region='RegionTwo', - profile='adminv2') - self.assertTrue(ret['keystone_|-nova_|-nova_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="nova", + description="Nova Service", + publicurl="http://localhost:8774/v2.1/%(tenant_id)s", + internalurl="http://localhost:8774/v2.1/%(tenant_id)s", + adminurl="http://localhost:8774/v2.1/%(tenant_id)s", + region="RegionTwo", + profile="adminv2", + ) + self.assertTrue(ret["keystone_|-nova_|-nova_|-endpoint_present"]["result"]) # Region One, change publicurl - ret = self.run_state('keystone.endpoint_present', - name='nova', - description='Nova Service', - publicurl='http://127.0.0.1:8774/v2.1/%(tenant_id)s', - internalurl='http://localhost:8774/v2.1/%(tenant_id)s', - adminurl='http://localhost:8774/v2.1/%(tenant_id)s', - region='RegionOne', - profile='adminv2') - self.assertTrue(ret['keystone_|-nova_|-nova_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="nova", + description="Nova Service", + publicurl="http://127.0.0.1:8774/v2.1/%(tenant_id)s", + internalurl="http://localhost:8774/v2.1/%(tenant_id)s", + adminurl="http://localhost:8774/v2.1/%(tenant_id)s", + region="RegionOne", + profile="adminv2", + ) + self.assertTrue(ret["keystone_|-nova_|-nova_|-endpoint_present"]["result"]) - ret = self.run_state('keystone.endpoint_get', - name='nova', - region='RegionOne', - profile='adminv2') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['result']) - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['publicurl'].start_with('http://127.0.0.1')) + ret = self.run_state( + "keystone.endpoint_get", name="nova", region="RegionOne", profile="adminv2" + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["result"]) + self.assertTrue( + ret["keystone_|-testv3_|-testv3_|-endpoint_present"][ + "publicurl" + ].start_with("http://127.0.0.1") + ) - ret = self.run_state('keystone.tenant_present', - name='test', - description='Test Tenant', - profile='adminv2') - self.assertTrue(ret['keystone_|-test_|-test_|-tenant_present']['result']) + ret = self.run_state( + "keystone.tenant_present", + name="test", + description="Test Tenant", + profile="adminv2", + ) + self.assertTrue(ret["keystone_|-test_|-test_|-tenant_present"]["result"]) - ret = self.run_state('keystone.role_present', - name='user', - profile='adminv2') - self.assertTrue(ret['keystone_|-user_|-user_|-role_present']['result']) + ret = self.run_state("keystone.role_present", name="user", profile="adminv2") + self.assertTrue(ret["keystone_|-user_|-user_|-role_present"]["result"]) - ret = self.run_state('keystone.user_present', - name='test', - email='test@example.com', - tenant='test', - password='testpass', - roles={'test': ['user']}, - profile='adminv2') - self.assertTrue(ret['keystone_|-test_|-test_|-user_present']['result']) + ret = self.run_state( + "keystone.user_present", + name="test", + email="test@example.com", + tenant="test", + password="testpass", + roles={"test": ["user"]}, + profile="adminv2", + ) + self.assertTrue(ret["keystone_|-test_|-test_|-user_present"]["result"]) - ret = self.run_state('keystone.service_absent', - name='testv2', - profile='adminv2') - self.assertTrue(ret['keystone_|-testv2_|-testv2_|-service_absent']['result']) + ret = self.run_state( + "keystone.service_absent", name="testv2", profile="adminv2" + ) + self.assertTrue(ret["keystone_|-testv2_|-testv2_|-service_absent"]["result"]) @destructiveTest def test_keystone_v3(self): - ret = self.run_state('keystone.service_present', - name='testv3', - description='Image Service', - service_type='image', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-service_present']['result']) + ret = self.run_state( + "keystone.service_present", + name="testv3", + description="Image Service", + service_type="image", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-service_present"]["result"]) - ret = self.run_state('keystone.endpoint_present', - name='testv3', - description='Glance Service', - interface='public', - url='http://localhost:9292', - region='RegionOne', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="testv3", + description="Glance Service", + interface="public", + url="http://localhost:9292", + region="RegionOne", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["result"]) - ret = self.run_state('keystone.endpoint_present', - name='testv3', - description='Glance Service', - interface='internal', - url='http://localhost:9292', - region='RegionOne', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="testv3", + description="Glance Service", + interface="internal", + url="http://localhost:9292", + region="RegionOne", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["result"]) - ret = self.run_state('keystone.endpoint_present', - name='testv3', - description='Glance Service', - interface='admin', - url='http://localhost:9292', - region='RegionOne', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="testv3", + description="Glance Service", + interface="admin", + url="http://localhost:9292", + region="RegionOne", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["result"]) # Region Two - ret = self.run_state('keystone.endpoint_present', - name='testv3', - description='Glance Service', - interface='public', - url='http://localhost:9292', - region='RegionTwo', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="testv3", + description="Glance Service", + interface="public", + url="http://localhost:9292", + region="RegionTwo", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["result"]) - ret = self.run_state('keystone.endpoint_present', - name='testv3', - description='Glance Service', - interface='internal', - url='http://localhost:9292', - region='RegionTwo', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="testv3", + description="Glance Service", + interface="internal", + url="http://localhost:9292", + region="RegionTwo", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["result"]) - ret = self.run_state('keystone.endpoint_present', - name='testv3', - description='Glance Service', - interface='admin', - url='http://localhost:9292', - region='RegionTwo', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="testv3", + description="Glance Service", + interface="admin", + url="http://localhost:9292", + region="RegionTwo", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["result"]) # Region One, change - ret = self.run_state('keystone.endpoint_present', - name='testv3', - description='Glance Service', - interface='public', - url='http://127.0.0.1:9292', - region='RegionOne', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['result']) + ret = self.run_state( + "keystone.endpoint_present", + name="testv3", + description="Glance Service", + interface="public", + url="http://127.0.0.1:9292", + region="RegionOne", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["result"]) - ret = self.run_state('keystone.endpoint_get', - name='testv3', - region='RegionOne', - interface='public', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['result']) - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-endpoint_present']['endpoint']['url'] == 'http://127.0.0.1:9292') + ret = self.run_state( + "keystone.endpoint_get", + name="testv3", + region="RegionOne", + interface="public", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["result"]) + self.assertTrue( + ret["keystone_|-testv3_|-testv3_|-endpoint_present"]["endpoint"]["url"] + == "http://127.0.0.1:9292" + ) - ret = self.run_state('keystone.project_present', - name='testv3', - description='Test v3 Tenant', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-project_present']['result']) + ret = self.run_state( + "keystone.project_present", + name="testv3", + description="Test v3 Tenant", + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-project_present"]["result"]) - ret = self.run_state('keystone.role_present', - name='user', - profile='adminv3') - self.assertTrue(ret['keystone_|-user_|-user_|-role_present']['result']) + ret = self.run_state("keystone.role_present", name="user", profile="adminv3") + self.assertTrue(ret["keystone_|-user_|-user_|-role_present"]["result"]) - ret = self.run_state('keystone.user_present', - name='testv3', - email='testv3@example.com', - project='testv3', - password='testv3pass', - roles={'testv3': ['user']}, - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-user_present']['result']) + ret = self.run_state( + "keystone.user_present", + name="testv3", + email="testv3@example.com", + project="testv3", + password="testv3pass", + roles={"testv3": ["user"]}, + profile="adminv3", + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-user_present"]["result"]) - ret = self.run_state('keystone.service_absent', - name='testv3', - profile='adminv3') - self.assertTrue(ret['keystone_|-testv3_|-testv3_|-service_absent']['result']) + ret = self.run_state( + "keystone.service_absent", name="testv3", profile="adminv3" + ) + self.assertTrue(ret["keystone_|-testv3_|-testv3_|-service_absent"]["result"]) diff --git a/tests/integration/states/test_lxd.py b/tests/integration/states/test_lxd.py index 1e2d7aa5120..8dfdcc403b7 100644 --- a/tests/integration/states/test_lxd.py +++ b/tests/integration/states/test_lxd.py @@ -1,30 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the lxd states -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals # Import salt utils import salt.utils.path - -# Import Salt Testing Libs -from tests.support.unit import skipIf from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest, flaky from tests.support.mixins import SaltReturnAssertsMixin +# Import Salt Testing Libs +from tests.support.unit import skipIf + try: import pylxd # pylint: disable=import-error,unused-import + HAS_PYLXD = True except ImportError: HAS_PYLXD = False @destructiveTest -@skipIf(not salt.utils.path.which('lxd'), 'LXD not installed') -@skipIf(not salt.utils.path.which('lxc'), 'LXC not installed') -@skipIf(not HAS_PYLXD, 'pylxd not installed') +@skipIf(not salt.utils.path.which("lxd"), "LXD not installed") +@skipIf(not salt.utils.path.which("lxc"), "LXC not installed") +@skipIf(not HAS_PYLXD, "pylxd not installed") class LxdTestCase(ModuleCase, SaltReturnAssertsMixin): run_once = False @@ -33,9 +34,9 @@ class LxdTestCase(ModuleCase, SaltReturnAssertsMixin): def test_01__init_lxd(self): if LxdTestCase.run_once: return - ret = self.run_state('lxd.init', name='foobar') + ret = self.run_state("lxd.init", name="foobar") self.assertSaltTrueReturn(ret) LxdTestCase.run_once = True - name = 'lxd_|-foobar_|-foobar_|-init' + name = "lxd_|-foobar_|-foobar_|-init" assert name in ret - assert ret[name]['storage_backend'] == 'dir' + assert ret[name]["storage_backend"] == "dir" diff --git a/tests/integration/states/test_lxd_container.py b/tests/integration/states/test_lxd_container.py index ab72890998f..9268e9e202f 100644 --- a/tests/integration/states/test_lxd_container.py +++ b/tests/integration/states/test_lxd_container.py @@ -1,147 +1,140 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the lxd states -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.helpers import flaky - # Import Lxd Test Case import tests.integration.states.test_lxd +# Import Salt Testing libs +from tests.support.helpers import flaky + @flaky class LxdContainerTestCase(tests.integration.states.test_lxd.LxdTestCase): - def setUp(self): self.run_state( - 'lxd_image.present', - name='images:centos/7', + "lxd_image.present", + name="images:centos/7", source={ - 'name': 'centos/7', - 'type': 'simplestreams', - 'server': 'https://images.linuxcontainers.org', + "name": "centos/7", + "type": "simplestreams", + "server": "https://images.linuxcontainers.org", }, ) def tearDown(self): - self.run_state('lxd_image.absent', name='images:centos/7',) - self.run_state('lxd_container.absent', name='test-container',) + self.run_state( + "lxd_image.absent", name="images:centos/7", + ) + self.run_state( + "lxd_container.absent", name="test-container", + ) def test_02__create_container(self): ret = self.run_state( - 'lxd_container.present', - name='test-container', + "lxd_container.present", + name="test-container", running=True, - source={'type': 'image', 'alias': 'images:centos/7'} + source={"type": "image", "alias": "images:centos/7"}, ) - name = 'lxd_container_|-test-container_|-test-container_|-present' + name = "lxd_container_|-test-container_|-test-container_|-present" self.assertSaltTrueReturn(ret) assert name in ret - assert ret[name]['changes']['started'] == 'Started the container "test-container"' + assert ( + ret[name]["changes"]["started"] == 'Started the container "test-container"' + ) def test_03__change_container(self): self.run_state( - 'lxd_container.present', - name='test-container', + "lxd_container.present", + name="test-container", running=True, - source={'type': 'image', 'alias': 'images:centos/7'} + source={"type": "image", "alias": "images:centos/7"}, ) ret = self.run_state( - 'lxd_container.present', - name='test-container', + "lxd_container.present", + name="test-container", running=True, - source={'type': 'image', 'alias': 'images:centos/7'}, + source={"type": "image", "alias": "images:centos/7"}, restart_on_change=True, config=[ - {'key': 'boot.autostart', 'value': 1}, - {'key': 'security.privileged', 'value': '1'}, + {"key": "boot.autostart", "value": 1}, + {"key": "security.privileged", "value": "1"}, ], ) - name = 'lxd_container_|-test-container_|-test-container_|-present' + name = "lxd_container_|-test-container_|-test-container_|-present" self.assertSaltTrueReturn(ret) assert name in ret - assert ret[name]['changes']['config'] == { - 'boot.autostart': 'Added config key "boot.autostart" = "1"', - 'security.privileged': 'Added config key "security.privileged" = "1"', + assert ret[name]["changes"]["config"] == { + "boot.autostart": 'Added config key "boot.autostart" = "1"', + "security.privileged": 'Added config key "security.privileged" = "1"', } def test_08__running_container(self): self.run_state( - 'lxd_container.present', - name='test-container', + "lxd_container.present", + name="test-container", running=True, - source={'type': 'image', 'alias': 'images:centos/7'} - ) - ret = self.run_state( - 'lxd_container.running', - name='test-container', + source={"type": "image", "alias": "images:centos/7"}, ) + ret = self.run_state("lxd_container.running", name="test-container",) self.assertSaltTrueReturn(ret) - name = 'lxd_container_|-test-container_|-test-container_|-running' + name = "lxd_container_|-test-container_|-test-container_|-running" assert name in ret - assert not ret[name]['changes'] - assert ret[name]['comment'] == 'The container "test-container" is already running' + assert not ret[name]["changes"] + assert ( + ret[name]["comment"] == 'The container "test-container" is already running' + ) ret = self.run_state( - 'lxd_container.running', - name='test-container', - restart=True, + "lxd_container.running", name="test-container", restart=True, ) self.assertSaltTrueReturn(ret) assert name in ret - assert ret[name]['changes'] == {'restarted': 'Restarted the container "test-container"'} - assert ret[name]['comment'] == 'Restarted the container "test-container"' + assert ret[name]["changes"] == { + "restarted": 'Restarted the container "test-container"' + } + assert ret[name]["comment"] == 'Restarted the container "test-container"' def test_09__stop_container(self): self.run_state( - 'lxd_container.present', - name='test-container', + "lxd_container.present", + name="test-container", running=True, - source={'type': 'image', 'alias': 'images:centos/7'} + source={"type": "image", "alias": "images:centos/7"}, ) - ret = self.run_state( - 'lxd_container.stopped', - name='test-container', - ) - name = 'lxd_container_|-test-container_|-test-container_|-stopped' + ret = self.run_state("lxd_container.stopped", name="test-container",) + name = "lxd_container_|-test-container_|-test-container_|-stopped" self.assertSaltTrueReturn(ret) - assert ret[name]['changes'] == {'stopped': 'Stopped the container "test-container"'} - ret = self.run_state( - 'lxd_container.stopped', - name='test-container', - ) - name = 'lxd_container_|-test-container_|-test-container_|-stopped' + assert ret[name]["changes"] == { + "stopped": 'Stopped the container "test-container"' + } + ret = self.run_state("lxd_container.stopped", name="test-container",) + name = "lxd_container_|-test-container_|-test-container_|-stopped" self.assertSaltTrueReturn(ret) - assert not ret[name]['changes'] + assert not ret[name]["changes"] def test_10__delete_container(self): self.run_state( - 'lxd_container.present', - name='test-container', + "lxd_container.present", + name="test-container", running=True, - source={'type': 'image', 'alias': 'images:centos/7'} + source={"type": "image", "alias": "images:centos/7"}, ) - ret = self.run_state( - 'lxd_container.absent', - name='test-container', - ) - name = 'lxd_container_|-test-container_|-test-container_|-absent' + ret = self.run_state("lxd_container.absent", name="test-container",) + name = "lxd_container_|-test-container_|-test-container_|-absent" assert name in ret - assert ret[name]['result'] is False - ret = self.run_state( - 'lxd_container.stopped', - name='test-container', - ) - name = 'lxd_container_|-test-container_|-test-container_|-stopped' + assert ret[name]["result"] is False + ret = self.run_state("lxd_container.stopped", name="test-container",) + name = "lxd_container_|-test-container_|-test-container_|-stopped" assert name in ret - assert ret[name]['result'] is True - ret = self.run_state( - 'lxd_container.absent', - name='test-container', - ) - name = 'lxd_container_|-test-container_|-test-container_|-absent' + assert ret[name]["result"] is True + ret = self.run_state("lxd_container.absent", name="test-container",) + name = "lxd_container_|-test-container_|-test-container_|-absent" self.assertSaltTrueReturn(ret) assert name in ret - assert ret[name]['changes'] == {'deleted': 'Container "test-container" has been deleted.'} + assert ret[name]["changes"] == { + "deleted": 'Container "test-container" has been deleted.' + } diff --git a/tests/integration/states/test_lxd_image.py b/tests/integration/states/test_lxd_image.py index 859021e129f..b919b148b5a 100644 --- a/tests/integration/states/test_lxd_image.py +++ b/tests/integration/states/test_lxd_image.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the lxd states -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,28 +10,27 @@ import tests.integration.states.test_lxd class LxdImageTestCase(tests.integration.states.test_lxd.LxdTestCase): - def test_02__pull_image(self): ret = self.run_state( - 'lxd_image.present', - name='images:centos/7', + "lxd_image.present", + name="images:centos/7", source={ - 'name': 'centos/7', - 'type': 'simplestreams', - 'server': 'https://images.linuxcontainers.org', + "name": "centos/7", + "type": "simplestreams", + "server": "https://images.linuxcontainers.org", }, ) - name = 'lxd_image_|-images:centos/7_|-images:centos/7_|-present' + name = "lxd_image_|-images:centos/7_|-images:centos/7_|-present" self.assertSaltTrueReturn(ret) assert name in ret - assert ret[name]['changes']['aliases'] == ['Added alias "images:centos/7"'] + assert ret[name]["changes"]["aliases"] == ['Added alias "images:centos/7"'] def test_03__delete_image(self): - ret = self.run_state( - 'lxd_image.absent', - name='images:centos/7', - ) - name = 'lxd_image_|-images:centos/7_|-images:centos/7_|-absent' + ret = self.run_state("lxd_image.absent", name="images:centos/7",) + name = "lxd_image_|-images:centos/7_|-images:centos/7_|-absent" self.assertSaltTrueReturn(ret) assert name in ret - assert ret[name]['changes']['removed'] == 'Image "images:centos/7" has been deleted.' + assert ( + ret[name]["changes"]["removed"] + == 'Image "images:centos/7" has been deleted.' + ) diff --git a/tests/integration/states/test_lxd_profile.py b/tests/integration/states/test_lxd_profile.py index 4512a5f29e6..4a429531924 100644 --- a/tests/integration/states/test_lxd_profile.py +++ b/tests/integration/states/test_lxd_profile.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the lxd states -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,66 +10,58 @@ import tests.integration.states.test_lxd class LxdProfileTestCase(tests.integration.states.test_lxd.LxdTestCase): - def tearDown(self): self.run_state( - 'lxd_profile.absent', - name='test-profile', + "lxd_profile.absent", name="test-profile", ) def test_02__create_profile(self): self.run_state( - 'lxd_profile.absent', - name='test-profile', + "lxd_profile.absent", name="test-profile", ) ret = self.run_state( - 'lxd_profile.present', - name='test-profile', - config=[ - {'key': 'boot.autostart', 'value': 1}, - ], + "lxd_profile.present", + name="test-profile", + config=[{"key": "boot.autostart", "value": 1}], ) - name = 'lxd_profile_|-test-profile_|-test-profile_|-present' + name = "lxd_profile_|-test-profile_|-test-profile_|-present" self.assertSaltTrueReturn(ret) assert name in ret - assert ret[name]['changes'] == {'created': 'Profile "test-profile" has been created'} + assert ret[name]["changes"] == { + "created": 'Profile "test-profile" has been created' + } def test_03__change_profile(self): self.run_state( - 'lxd_profile.present', - name='test-profile', - config=[ - {'key': 'boot.autostart', 'value': 1}, - ], + "lxd_profile.present", + name="test-profile", + config=[{"key": "boot.autostart", "value": 1}], ) ret = self.run_state( - 'lxd_profile.present', - name='test-profile', + "lxd_profile.present", + name="test-profile", config=[ - {'key': 'boot.autostart', 'value': 1}, - {'key': 'security.privileged', 'value': '1'}, + {"key": "boot.autostart", "value": 1}, + {"key": "security.privileged", "value": "1"}, ], ) - name = 'lxd_profile_|-test-profile_|-test-profile_|-present' + name = "lxd_profile_|-test-profile_|-test-profile_|-present" self.assertSaltTrueReturn(ret) assert name in ret - assert ret[name]['changes']['config'] == { - 'security.privileged': 'Added config key "security.privileged" = "1"' + assert ret[name]["changes"]["config"] == { + "security.privileged": 'Added config key "security.privileged" = "1"' } def test_04__delete_profile(self): self.run_state( - 'lxd_profile.present', - name='test-profile', - config=[ - {'key': 'boot.autostart', 'value': 1}, - ], + "lxd_profile.present", + name="test-profile", + config=[{"key": "boot.autostart", "value": 1}], ) - ret = self.run_state( - 'lxd_profile.absent', - name='test-profile', - ) - name = 'lxd_profile_|-test-profile_|-test-profile_|-absent' + ret = self.run_state("lxd_profile.absent", name="test-profile",) + name = "lxd_profile_|-test-profile_|-test-profile_|-absent" self.assertSaltTrueReturn(ret) assert name in ret - assert ret[name]['changes'] == {'removed': 'Profile "test-profile" has been deleted.'} + assert ret[name]["changes"] == { + "removed": 'Profile "test-profile" has been deleted.' + } diff --git a/tests/integration/states/test_match.py b/tests/integration/states/test_match.py index 6a7f41168ad..e212fbadff7 100644 --- a/tests/integration/states/test_match.py +++ b/tests/integration/states/test_match.py @@ -1,52 +1,51 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.states.match ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os +# Import salt libs +import salt.utils.files +import salt.utils.stringutils + # Import Salt Testing libs from tests.support.case import ModuleCase from tests.support.helpers import skip_if_not_root from tests.support.runtests import RUNTIME_VARS -# Import salt libs -import salt.utils.files -import salt.utils.stringutils - class StateMatchTest(ModuleCase): - ''' + """ Validate the file state - ''' + """ @skip_if_not_root def test_issue_2167_ipcidr_no_AttributeError(self): - subnets = self.run_function('network.subnets') + subnets = self.run_function("network.subnets") self.assertTrue(len(subnets) > 0) - top_filename = 'issue-2167-ipcidr-match.sls' + top_filename = "issue-2167-ipcidr-match.sls" top_file = os.path.join(RUNTIME_VARS.BASE_FILES, top_filename) try: - with salt.utils.files.fopen(top_file, 'w') as fp_: + with salt.utils.files.fopen(top_file, "w") as fp_: fp_.write( salt.utils.stringutils.to_str( - 'base:\n' - ' {0}:\n' - ' - match: ipcidr\n' - ' - test\n'.format(subnets[0]) + "base:\n" + " {0}:\n" + " - match: ipcidr\n" + " - test\n".format(subnets[0]) ) ) - ret = self.run_function('state.top', [top_filename]) + ret = self.run_function("state.top", [top_filename]) self.assertNotIn( - 'AttributeError: \'Matcher\' object has no attribute ' - '\'functions\'', - ret + "AttributeError: 'Matcher' object has no attribute " "'functions'", ret ) finally: os.remove(top_file) diff --git a/tests/integration/states/test_mysql_database.py b/tests/integration/states/test_mysql_database.py index 35c205085d9..7a3d1825c6f 100644 --- a/tests/integration/states/test_mysql_database.py +++ b/tests/integration/states/test_mysql_database.py @@ -1,161 +1,131 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the MySQL states -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest -from tests.support.mixins import SaltReturnAssertsMixin - # Import salt libs import salt.utils.path from salt.ext import six +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf + NO_MYSQL = False try: import MySQLdb # pylint: disable=import-error,unused-import except ImportError: NO_MYSQL = True -if not salt.utils.path.which('mysqladmin'): +if not salt.utils.path.which("mysqladmin"): NO_MYSQL = True @skipIf( NO_MYSQL, - 'Please install MySQL bindings and a MySQL Server before running' - 'MySQL integration tests.' + "Please install MySQL bindings and a MySQL Server before running" + "MySQL integration tests.", ) class MysqlDatabaseStateTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the mysql_database state - ''' + """ - user = 'root' - password = 'poney' + user = "root" + password = "poney" @destructiveTest def setUp(self): - ''' + """ Test presence of MySQL server, enforce a root password - ''' + """ super(MysqlDatabaseStateTest, self).setUp() NO_MYSQL_SERVER = True # now ensure we know the mysql root password # one of theses two at least should work ret1 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' flush-privileges password "' + + self.password + + '"', ) ret2 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' --password="' - + self.password - + '" flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' --password="' + + self.password + + '" flush-privileges password "' + + self.password + + '"', ) key, value = ret2.popitem() - if value['result']: + if value["result"]: NO_MYSQL_SERVER = False else: - self.skipTest('No MySQL Server running, or no root access on it.') + self.skipTest("No MySQL Server running, or no root access on it.") def _test_database(self, db_name, second_db_name, test_conn, **kwargs): - ''' + """ Create db two times, test conn, remove it two times - ''' + """ # In case of... - ret = self.run_state('mysql_database.absent', - name=db_name, - **kwargs - ) - ret = self.run_state('mysql_database.present', - name=db_name, - **kwargs - ) + ret = self.run_state("mysql_database.absent", name=db_name, **kwargs) + ret = self.run_state("mysql_database.present", name=db_name, **kwargs) self.assertSaltTrueReturn(ret) - self.assertInSaltComment( - 'The database ' + db_name + ' has been created', - ret - ) - #2nd run - ret = self.run_state('mysql_database.present', - name=second_db_name, - **kwargs - ) + self.assertInSaltComment("The database " + db_name + " has been created", ret) + # 2nd run + ret = self.run_state("mysql_database.present", name=second_db_name, **kwargs) self.assertSaltTrueReturn(ret) - self.assertInSaltComment( - 'Database ' + db_name + ' is already present', - ret - ) + self.assertInSaltComment("Database " + db_name + " is already present", ret) if test_conn: # test root connection ret = self.run_function( - 'mysql.query', - database=db_name, - query='SELECT 1', - **kwargs + "mysql.query", database=db_name, query="SELECT 1", **kwargs ) - if not isinstance(ret, dict) or 'results' not in ret: + if not isinstance(ret, dict) or "results" not in ret: raise AssertionError( - ('Unexpected result while testing connection' - ' on db \'{0}\': {1}').format( - db_name, - repr(ret) - ) + ( + "Unexpected result while testing connection" " on db '{0}': {1}" + ).format(db_name, repr(ret)) ) - self.assertEqual([['1']], ret['results']) + self.assertEqual([["1"]], ret["results"]) # Now removing databases - kwargs.pop('character_set') - kwargs.pop('collate') - ret = self.run_state('mysql_database.absent', - name=db_name, - **kwargs - ) + kwargs.pop("character_set") + kwargs.pop("collate") + ret = self.run_state("mysql_database.absent", name=db_name, **kwargs) + self.assertSaltTrueReturn(ret) + self.assertInSaltComment("Database " + db_name + " has been removed", ret) + # 2nd run + ret = self.run_state("mysql_database.absent", name=second_db_name, **kwargs) self.assertSaltTrueReturn(ret) self.assertInSaltComment( - 'Database ' + db_name + ' has been removed', - ret - ) - #2nd run - ret = self.run_state('mysql_database.absent', - name=second_db_name, - ** kwargs - ) - self.assertSaltTrueReturn(ret) - self.assertInSaltComment( - 'Database ' + db_name + ' is not present, so it cannot be removed', - ret + "Database " + db_name + " is not present, so it cannot be removed", ret ) self.assertSaltStateChangesEqual(ret, {}) @destructiveTest def test_present_absent(self): - ''' + """ mysql_database.present - ''' + """ self._test_database( - 'testdb1', - 'testdb1', + "testdb1", + "testdb1", test_conn=True, - character_set='utf8', - collate='utf8_general_ci', + character_set="utf8", + collate="utf8_general_ci", connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) # TODO: test with variations on collate and charset, check for db alter @@ -163,25 +133,25 @@ class MysqlDatabaseStateTest(ModuleCase, SaltReturnAssertsMixin): @destructiveTest def test_present_absent_fuzzy(self): - ''' + """ mysql_database.present with utf-8 andf fuzzy db name - ''' + """ # this is : ":() ;,?@=`&'\ - dbname_fuzzy = '":() ;,?@=`&/\'\\' + dbname_fuzzy = "\":() ;,?@=`&/'\\" # \xe6\xa8\x99\ = \u6a19 = 標 # this is : "();,?:@=`&/標'\ - dbname_utf8 = '"();,?@=`&//\xe6\xa8\x99\'\\' - dbname_unicode = u'"();,?@=`&//\u6a19\'\\' + dbname_utf8 = "\"();,?@=`&//\xe6\xa8\x99'\\" + dbname_unicode = "\"();,?@=`&//\u6a19'\\" self._test_database( dbname_fuzzy, dbname_fuzzy, test_conn=True, - character_set='utf8', - collate='utf8_general_ci', + character_set="utf8", + collate="utf8_general_ci", connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) # FIXME: MySQLdb bugs on dbnames with utf-8? @@ -189,43 +159,44 @@ class MysqlDatabaseStateTest(ModuleCase, SaltReturnAssertsMixin): dbname_utf8, dbname_unicode, test_conn=False, - character_set='utf8', - collate='utf8_general_ci', + character_set="utf8", + collate="utf8_general_ci", connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - #saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + # saltenv={"LC_ALL": "en_US.utf8"} ) @destructiveTest - @skipIf(True, 'This tests needs issue #8947 to be fixed first') + @skipIf(True, "This tests needs issue #8947 to be fixed first") def test_utf8_from_sls_file(self): - ''' + """ Try to create/destroy an utf-8 database name from an sls file #8947 - ''' + """ expected_result = { - 'mysql_database_|-A_|-foo \xe6\xba\x96`bar_|-present': { - '__run_num__': 0, - 'comment': 'The database foo \xe6\xba\x96`bar has been created', - 'result': True}, - 'mysql_database_|-B_|-foo \xe6\xba\x96`bar_|-absent': { - '__run_num__': 1, - 'comment': 'Database foo \xe6\xba\x96`bar has been removed', - 'result': True}, + "mysql_database_|-A_|-foo \xe6\xba\x96`bar_|-present": { + "__run_num__": 0, + "comment": "The database foo \xe6\xba\x96`bar has been created", + "result": True, + }, + "mysql_database_|-B_|-foo \xe6\xba\x96`bar_|-absent": { + "__run_num__": 1, + "comment": "Database foo \xe6\xba\x96`bar has been removed", + "result": True, + }, } result = {} - ret = self.run_function('state.sls', mods='mysql_utf8') + ret = self.run_function("state.sls", mods="mysql_utf8") if not isinstance(ret, dict): raise AssertionError( - ('Unexpected result while testing external mysql utf8 sls' - ': {0}').format( - repr(ret) - ) + ( + "Unexpected result while testing external mysql utf8 sls" ": {0}" + ).format(repr(ret)) ) for item, descr in six.iteritems(ret): result[item] = { - '__run_num__': descr['__run_num__'], - 'comment': descr['comment'], - 'result': descr['result'] + "__run_num__": descr["__run_num__"], + "comment": descr["comment"], + "result": descr["result"], } self.assertEqual(expected_result, result) diff --git a/tests/integration/states/test_mysql_grants.py b/tests/integration/states/test_mysql_grants.py index 87ef53a7338..66fd525fcd9 100644 --- a/tests/integration/states/test_mysql_grants.py +++ b/tests/integration/states/test_mysql_grants.py @@ -1,23 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the MySQL states -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest -from tests.support.mixins import SaltReturnAssertsMixin +import logging # Import salt libs import salt.utils.path from salt.ext import six from salt.modules import mysql as mysqlmod +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf + log = logging.getLogger(__name__) NO_MYSQL = False @@ -26,269 +27,255 @@ try: except ImportError: NO_MYSQL = True -if not salt.utils.path.which('mysqladmin'): +if not salt.utils.path.which("mysqladmin"): NO_MYSQL = True @skipIf( NO_MYSQL, - 'Please install MySQL bindings and a MySQL Server before running' - 'MySQL integration tests.' + "Please install MySQL bindings and a MySQL Server before running" + "MySQL integration tests.", ) class MysqlGrantsStateTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the mysql_grants states - ''' + """ - user = 'root' - password = 'poney' + user = "root" + password = "poney" # yep, theses are valid MySQL db names # very special chars are _ % and . - testdb1 = 'tes.t\'"saltdb' - testdb2 = 't_st `(:=salt%b)' - testdb3 = 'test `(:=salteeb)' - table1 = 'foo' - table2 = "foo `\'%_bar" + testdb1 = "tes.t'\"saltdb" + testdb2 = "t_st `(:=salt%b)" + testdb3 = "test `(:=salteeb)" + table1 = "foo" + table2 = "foo `'%_bar" users = { - 'user1': { - 'name': 'foo', - 'pwd': 'bar', - }, - 'user2': { - 'name': 'user ";--,?:&/\\', - 'pwd': '";--(),?:@=&/\\', - }, + "user1": {"name": "foo", "pwd": "bar"}, + "user2": {"name": 'user ";--,?:&/\\', "pwd": '";--(),?:@=&/\\'}, # this is : passwd 標標 - 'user3': { - 'name': 'user( @ )=foobar', - 'pwd': '\xe6\xa8\x99\xe6\xa8\x99', - }, + "user3": {"name": "user( @ )=foobar", "pwd": "\xe6\xa8\x99\xe6\xa8\x99"}, # this is : user/password containing 標標 - 'user4': { - 'name': 'user \xe6\xa8\x99', - 'pwd': '\xe6\xa8\x99\xe6\xa8\x99', - }, + "user4": {"name": "user \xe6\xa8\x99", "pwd": "\xe6\xa8\x99\xe6\xa8\x99"}, } @destructiveTest def setUp(self): - ''' + """ Test presence of MySQL server, enforce a root password - ''' + """ super(MysqlGrantsStateTest, self).setUp() NO_MYSQL_SERVER = True # now ensure we know the mysql root password # one of theses two at least should work ret1 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' flush-privileges password "' + + self.password + + '"', ) ret2 = self.run_state( - 'cmd.run', - name='mysqladmin --host="localhost" -u ' - + self.user - + ' --password="' - + self.password - + '" flush-privileges password "' - + self.password - + '"' + "cmd.run", + name='mysqladmin --host="localhost" -u ' + + self.user + + ' --password="' + + self.password + + '" flush-privileges password "' + + self.password + + '"', ) key, value = ret2.popitem() - if value['result']: + if value["result"]: NO_MYSQL_SERVER = False else: - self.skipTest('No MySQL Server running, or no root access on it.') + self.skipTest("No MySQL Server running, or no root access on it.") # Create some users and a test db for user, userdef in six.iteritems(self.users): - self._userCreation(uname=userdef['name'], password=userdef['pwd']) + self._userCreation(uname=userdef["name"], password=userdef["pwd"]) self.run_state( - 'mysql_database.present', + "mysql_database.present", name=self.testdb1, - character_set='utf8', - collate='utf8_general_ci', + character_set="utf8", + collate="utf8_general_ci", connection_user=self.user, connection_pass=self.password, ) self.run_state( - 'mysql_database.present', + "mysql_database.present", name=self.testdb2, - character_set='utf8', - collate='utf8_general_ci', + character_set="utf8", + collate="utf8_general_ci", connection_user=self.user, connection_pass=self.password, ) - create_query = ('CREATE TABLE {tblname} (' - ' id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,' - ' data VARCHAR(100)) ENGINE={engine};'.format( - tblname=mysqlmod.quote_identifier(self.table1), - engine='MYISAM', - )) - log.info('Adding table \'%s\'', self.table1) - self.run_function( - 'mysql.query', - database=self.testdb2, - query=create_query, - connection_user=self.user, - connection_pass=self.password + create_query = ( + "CREATE TABLE {tblname} (" + " id INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + " data VARCHAR(100)) ENGINE={engine};".format( + tblname=mysqlmod.quote_identifier(self.table1), engine="MYISAM", + ) ) - create_query = ('CREATE TABLE {tblname} (' - ' id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,' - ' data VARCHAR(100)) ENGINE={engine};'.format( - tblname=mysqlmod.quote_identifier(self.table2), - engine='MYISAM', - )) - log.info('Adding table \'%s\'', self.table2) + log.info("Adding table '%s'", self.table1) self.run_function( - 'mysql.query', + "mysql.query", database=self.testdb2, query=create_query, connection_user=self.user, - connection_pass=self.password + connection_pass=self.password, + ) + create_query = ( + "CREATE TABLE {tblname} (" + " id INT NOT NULL AUTO_INCREMENT PRIMARY KEY," + " data VARCHAR(100)) ENGINE={engine};".format( + tblname=mysqlmod.quote_identifier(self.table2), engine="MYISAM", + ) + ) + log.info("Adding table '%s'", self.table2) + self.run_function( + "mysql.query", + database=self.testdb2, + query=create_query, + connection_user=self.user, + connection_pass=self.password, ) @destructiveTest def tearDown(self): - ''' + """ Removes created users and db - ''' + """ for user, userdef in six.iteritems(self.users): - self._userRemoval(uname=userdef['name'], password=userdef['pwd']) + self._userRemoval(uname=userdef["name"], password=userdef["pwd"]) self.run_state( - 'mysql_database.absent', + "mysql_database.absent", name=self.testdb1, connection_user=self.user, connection_pass=self.password, - ) + ) self.run_function( - 'mysql_database.absent', + "mysql_database.absent", name=self.testdb2, connection_user=self.user, connection_pass=self.password, - ) + ) - def _userCreation(self, - uname, - password=None): - ''' + def _userCreation(self, uname, password=None): + """ Create a test user - ''' + """ self.run_state( - 'mysql_user.present', + "mysql_user.present", name=uname, - host='localhost', + host="localhost", password=password, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) - def _userRemoval(self, - uname, - password=None): - ''' + def _userRemoval(self, uname, password=None): + """ Removes a test user - ''' + """ self.run_state( - 'mysql_user.absent', + "mysql_user.absent", name=uname, - host='localhost', + host="localhost", connection_user=self.user, connection_pass=self.password, - connection_charset='utf8', - saltenv={"LC_ALL": "en_US.utf8"} + connection_charset="utf8", + saltenv={"LC_ALL": "en_US.utf8"}, ) @destructiveTest def test_grant_present_absent(self): - ''' + """ mysql_database.present - ''' + """ ret = self.run_state( - 'mysql_grants.present', - name='grant test 1', - grant='SELECT, INSERT', - database=self.testdb1 + '.*', - user=self.users['user1']['name'], - host='localhost', + "mysql_grants.present", + name="grant test 1", + grant="SELECT, INSERT", + database=self.testdb1 + ".*", + user=self.users["user1"]["name"], + host="localhost", grant_option=True, revoke_first=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'mysql_grants.present', - name='grant test 2', - grant='SELECT, ALTER,CREATE TEMPORARY tables, execute', - database=self.testdb1 + '.*', - user=self.users['user1']['name'], - host='localhost', + "mysql_grants.present", + name="grant test 2", + grant="SELECT, ALTER,CREATE TEMPORARY tables, execute", + database=self.testdb1 + ".*", + user=self.users["user1"]["name"], + host="localhost", grant_option=True, revoke_first=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'mysql_grants.present', - name='grant test 3', - grant='SELECT, INSERT', - database=self.testdb2 + '.' + self.table2, - user=self.users['user2']['name'], - host='localhost', + "mysql_grants.present", + name="grant test 3", + grant="SELECT, INSERT", + database=self.testdb2 + "." + self.table2, + user=self.users["user2"]["name"], + host="localhost", grant_option=True, revoke_first=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'mysql_grants.present', - name='grant test 4', - grant='SELECT, INSERT', - database=self.testdb2 + '.' + self.table2, - user=self.users['user2']['name'], - host='localhost', + "mysql_grants.present", + name="grant test 4", + grant="SELECT, INSERT", + database=self.testdb2 + "." + self.table2, + user=self.users["user2"]["name"], + host="localhost", grant_option=True, revoke_first=True, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'mysql_grants.present', - name='grant test 5', - grant='SELECT, UPDATE', - database=self.testdb2 + '.*', - user=self.users['user1']['name'], - host='localhost', + "mysql_grants.present", + name="grant test 5", + grant="SELECT, UPDATE", + database=self.testdb2 + ".*", + user=self.users["user1"]["name"], + host="localhost", grant_option=True, revoke_first=False, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'mysql_grants.absent', - name='grant test 6', - grant='SELECT,update', - database=self.testdb2 + '.*', - user=self.users['user1']['name'], - host='localhost', + "mysql_grants.absent", + name="grant test 6", + grant="SELECT,update", + database=self.testdb2 + ".*", + user=self.users["user1"]["name"], + host="localhost", grant_option=True, revoke_first=False, connection_user=self.user, connection_pass=self.password, - connection_charset='utf8' + connection_charset="utf8", ) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_network.py b/tests/integration/states/test_network.py index 52f7ca01ec8..17e29502926 100644 --- a/tests/integration/states/test_network.py +++ b/tests/integration/states/test_network.py @@ -1,12 +1,12 @@ # -*- encoding: utf-8 -*- -''' +""" :codeauthor: :email: `Justin Anderson <janderson@saltstack.com>` tests.integration.states.network ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import salt testing libs from tests.support.case import ModuleCase @@ -16,45 +16,52 @@ from tests.support.mixins import SaltReturnAssertsMixin @destructiveTest class NetworkTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate network state module - ''' + """ + def setUp(self): - os_family = self.run_function('grains.get', ['os_family']) - if os_family not in ('RedHat', 'Debian'): - self.skipTest('Network state only supported on RedHat and Debian based systems') + os_family = self.run_function("grains.get", ["os_family"]) + if os_family not in ("RedHat", "Debian"): + self.skipTest( + "Network state only supported on RedHat and Debian based systems" + ) def test_managed(self): - ''' + """ network.managed - ''' - state_key = 'network_|-dummy0_|-dummy0_|-managed' + """ + state_key = "network_|-dummy0_|-dummy0_|-managed" - ret = self.run_function('state.sls', mods='network.managed', test=True) - self.assertEqual('Interface dummy0 is set to be added.', ret[state_key]['comment']) + ret = self.run_function("state.sls", mods="network.managed", test=True) + self.assertEqual( + "Interface dummy0 is set to be added.", ret[state_key]["comment"] + ) def test_routes(self): - ''' + """ network.routes - ''' - state_key = 'network_|-routes_|-dummy0_|-routes' - expected_changes = 'Interface dummy0 routes are set to be added.' + """ + state_key = "network_|-routes_|-dummy0_|-routes" + expected_changes = "Interface dummy0 routes are set to be added." - ret = self.run_function('state.sls', mods='network.routes', test=True) + ret = self.run_function("state.sls", mods="network.routes", test=True) - self.assertEqual(ret[state_key]['comment'], 'Interface dummy0 routes are set to be added.') + self.assertEqual( + ret[state_key]["comment"], "Interface dummy0 routes are set to be added." + ) def test_system(self): - ''' + """ network.system - ''' - state_key = 'network_|-system_|-system_|-system' + """ + state_key = "network_|-system_|-system_|-system" - global_settings = self.run_function('ip.get_network_settings') - ret = self.run_function('state.sls', mods='network.system', test=True) + global_settings = self.run_function("ip.get_network_settings") + ret = self.run_function("state.sls", mods="network.system", test=True) self.assertIn( - 'Global network settings are set to be {0}'.format( - 'added' if not global_settings else 'updated' + "Global network settings are set to be {0}".format( + "added" if not global_settings else "updated" ), - ret[state_key]['comment'] + ret[state_key]["comment"], ) diff --git a/tests/integration/states/test_npm.py b/tests/integration/states/test_npm.py index d1406d3a096..6112af868ac 100644 --- a/tests/integration/states/test_npm.py +++ b/tests/integration/states/test_npm.py @@ -1,86 +1,100 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Erik Johnson (erik@saltstack.com) tests.integration.states.npm ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, requires_network -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.runtests import RUNTIME_VARS +import os # Import salt libs import salt.utils.path import salt.utils.platform from salt.utils.versions import LooseVersion -MAX_NPM_VERSION = '5.0.0' +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, requires_network +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + +MAX_NPM_VERSION = "5.0.0" -@skipIf(salt.utils.path.which('npm') is None, 'npm not installed') +@skipIf(salt.utils.path.which("npm") is None, "npm not installed") class NpmStateTest(ModuleCase, SaltReturnAssertsMixin): - @requires_network() @destructiveTest def test_npm_installed_removed(self): - ''' + """ Basic test to determine if NPM module was successfully installed and removed. - ''' - ret = self.run_state('npm.installed', name='pm2@2.10.4', registry="http://registry.npmjs.org/") + """ + ret = self.run_state( + "npm.installed", name="pm2@2.10.4", registry="http://registry.npmjs.org/" + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('npm.removed', name='pm2') + ret = self.run_state("npm.removed", name="pm2") self.assertSaltTrueReturn(ret) - @skipIf(salt.utils.platform.is_darwin(), 'TODO this test hangs on mac.') + @skipIf(salt.utils.platform.is_darwin(), "TODO this test hangs on mac.") @requires_network() @destructiveTest def test_npm_install_url_referenced_package(self): - ''' + """ Determine if URL-referenced NPM module can be successfully installed. - ''' - npm_version = self.run_function('cmd.run', ['npm -v']) + """ + npm_version = self.run_function("cmd.run", ["npm -v"]) if LooseVersion(npm_version) >= LooseVersion(MAX_NPM_VERSION): - user = os.environ.get('SUDO_USER', 'root') - npm_dir = os.path.join(RUNTIME_VARS.TMP, 'git-install-npm') - self.run_state('file.directory', name=npm_dir, user=user, dir_mode='755') + user = os.environ.get("SUDO_USER", "root") + npm_dir = os.path.join(RUNTIME_VARS.TMP, "git-install-npm") + self.run_state("file.directory", name=npm_dir, user=user, dir_mode="755") else: user = None npm_dir = None - ret = self.run_state('npm.installed', - name='request/request#v2.81.1', - runas=user, - dir=npm_dir, - registry="http://registry.npmjs.org/") + ret = self.run_state( + "npm.installed", + name="request/request#v2.81.1", + runas=user, + dir=npm_dir, + registry="http://registry.npmjs.org/", + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('npm.removed', name='git://github.com/request/request', runas=user, dir=npm_dir) + ret = self.run_state( + "npm.removed", + name="git://github.com/request/request", + runas=user, + dir=npm_dir, + ) self.assertSaltTrueReturn(ret) if npm_dir is not None: - self.run_state('file.absent', name=npm_dir) + self.run_state("file.absent", name=npm_dir) @requires_network() @destructiveTest def test_npm_installed_pkgs(self): - ''' + """ Basic test to determine if NPM module successfully installs multiple packages. - ''' - ret = self.run_state('npm.installed', name='unused', pkgs=['pm2@2.10.4', 'grunt@1.0.2'], registry="http://registry.npmjs.org/") + """ + ret = self.run_state( + "npm.installed", + name="unused", + pkgs=["pm2@2.10.4", "grunt@1.0.2"], + registry="http://registry.npmjs.org/", + ) self.assertSaltTrueReturn(ret) @destructiveTest def test_npm_cache_clean(self): - ''' + """ Basic test to determine if NPM successfully cleans its cached packages. - ''' - npm_version = self.run_function('cmd.run', ['npm -v']) + """ + npm_version = self.run_function("cmd.run", ["npm -v"]) if LooseVersion(npm_version) >= LooseVersion(MAX_NPM_VERSION): - self.skipTest('Skip with npm >= 5.0.0 until #41770 is fixed') - ret = self.run_state('npm.cache_cleaned', name='unused', force=True) + self.skipTest("Skip with npm >= 5.0.0 until #41770 is fixed") + ret = self.run_state("npm.cache_cleaned", name="unused", force=True) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_pip_state.py b/tests/integration/states/test_pip_state.py index 99a5cd64c23..174d8549f97 100644 --- a/tests/integration/states/test_pip_state.py +++ b/tests/integration/states/test_pip_state.py @@ -1,42 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.states.pip_state ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" -# Import python libs from __future__ import absolute_import, print_function, unicode_literals + import errno -import os import glob +import os import pprint import shutil import sys -try: - import pwd - HAS_PWD = True -except ImportError: - HAS_PWD = False - -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.helpers import ( - destructiveTest, - requires_system_grains, - with_system_user, - skip_if_not_root, - with_tempdir, - patched_environ -) -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import skipIf - -# Import salt libs +import pytest import salt.utils.files import salt.utils.path import salt.utils.platform @@ -44,11 +24,28 @@ import salt.utils.versions import salt.utils.win_dacl import salt.utils.win_functions import salt.utils.win_runas -from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES from salt.exceptions import CommandExecutionError - -# Import 3rd-party libs from salt.ext import six +from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.case import ModuleCase +from tests.support.helpers import ( + destructiveTest, + patched_environ, + requires_system_grains, + skip_if_not_root, + with_system_user, + with_tempdir, +) +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf + +try: + import pwd + + HAS_PWD = True +except ImportError: + HAS_PWD = False class VirtualEnv(object): @@ -60,21 +57,24 @@ class VirtualEnv(object): def __enter__(self): ret = self.test._create_virtualenv(self.venv_dir) self.test.assertEqual( - ret['retcode'], 0, - msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format( + ret["retcode"], + 0, + msg="Expected 'retcode' key did not match. Full return dictionary:\n{}".format( pprint.pformat(ret) - ) + ), ) def __exit__(self, *args): pass -@skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') +@skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, "virtualenv not installed" +) +@pytest.mark.windows_whitelisted class PipStateTest(ModuleCase, SaltReturnAssertsMixin): - def _create_virtualenv(self, path, **kwargs): - ''' + """ The reason why the virtualenv creation is proxied by this function is mostly because under windows, we can't seem to properly create a virtualenv off of another virtualenv(we can on linux) and also because, we really don't want to @@ -82,63 +82,60 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin): from the original python. Also, one windows, we must also point to the virtualenv binary outside the existing virtualenv because it will fail otherwise - ''' + """ self.addCleanup(shutil.rmtree, path, ignore_errors=True) try: if salt.utils.platform.is_windows(): python = os.path.join(sys.real_prefix, os.path.basename(sys.executable)) else: python_binary_names = [ - 'python{}.{}'.format(*sys.version_info), - 'python{}'.format(*sys.version_info), - 'python' + "python{}.{}".format(*sys.version_info), + "python{}".format(*sys.version_info), + "python", ] for binary_name in python_binary_names: - python = os.path.join(sys.real_prefix, 'bin', binary_name) + python = os.path.join(sys.real_prefix, "bin", binary_name) if os.path.exists(python): break else: self.fail( - 'Couldn\'t find a python binary name under \'{}\' matching: {}'.format( - os.path.join(sys.real_prefix, 'bin'), - python_binary_names + "Couldn't find a python binary name under '{}' matching: {}".format( + os.path.join(sys.real_prefix, "bin"), python_binary_names ) ) # We're running off a virtualenv, and we don't want to create a virtualenv off of # a virtualenv, let's point to the actual python that created the virtualenv - kwargs['python'] = python + kwargs["python"] = python except AttributeError: # We're running off of the system python pass - return self.run_function('virtualenv.create', [path], **kwargs) + return self.run_function("virtualenv.create", [path], **kwargs) def test_pip_installed_removed(self): - ''' + """ Tests installed and removed states - ''' - name = 'pudb' - if name in self.run_function('pip.list'): - self.skipTest('{0} is already installed, uninstall to run this test'.format(name)) - ret = self.run_state('pip.installed', name=name) + """ + name = "pudb" + if name in self.run_function("pip.list"): + self.skipTest( + "{0} is already installed, uninstall to run this test".format(name) + ) + ret = self.run_state("pip.installed", name=name) self.assertSaltTrueReturn(ret) - ret = self.run_state('pip.removed', name=name) + ret = self.run_state("pip.removed", name=name) self.assertSaltTrueReturn(ret) def test_pip_installed_removed_venv(self): - venv_dir = os.path.join( - RUNTIME_VARS.TMP, 'pip_installed_removed' - ) + venv_dir = os.path.join(RUNTIME_VARS.TMP, "pip_installed_removed") with VirtualEnv(self, venv_dir): - name = 'pudb' - ret = self.run_state('pip.installed', name=name, bin_env=venv_dir) + name = "pudb" + ret = self.run_state("pip.installed", name=name, bin_env=venv_dir) self.assertSaltTrueReturn(ret) - ret = self.run_state('pip.removed', name=name, bin_env=venv_dir) + ret = self.run_state("pip.removed", name=name, bin_env=venv_dir) self.assertSaltTrueReturn(ret) def test_pip_installed_errors(self): - venv_dir = os.path.join( - RUNTIME_VARS.TMP, 'pip-installed-errors' - ) + venv_dir = os.path.join(RUNTIME_VARS.TMP, "pip-installed-errors") self.addCleanup(shutil.rmtree, venv_dir, ignore_errors=True) # Since we don't have the virtualenv created, pip.installed will # throw an error. @@ -146,38 +143,35 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin): # * "Error installing 'pep8': /tmp/pip-installed-errors: not found" # * "Error installing 'pep8': /bin/sh: 1: /tmp/pip-installed-errors: not found" # * "Error installing 'pep8': /bin/bash: /tmp/pip-installed-errors: No such file or directory" - with patched_environ(SHELL='/bin/sh'): - ret = self.run_function('state.sls', mods='pip-installed-errors') + with patched_environ(SHELL="/bin/sh"): + ret = self.run_function("state.sls", mods="pip-installed-errors") self.assertSaltFalseReturn(ret) - self.assertSaltCommentRegexpMatches( - ret, - 'Error installing \'pep8\':' - ) + self.assertSaltCommentRegexpMatches(ret, "Error installing 'pep8':") # We now create the missing virtualenv - ret = self.run_function('virtualenv.create', [venv_dir]) - self.assertEqual(ret['retcode'], 0) + ret = self.run_function("virtualenv.create", [venv_dir]) + self.assertEqual(ret["retcode"], 0) # The state should not have any issues running now - ret = self.run_function('state.sls', mods='pip-installed-errors') + ret = self.run_function("state.sls", mods="pip-installed-errors") self.assertSaltTrueReturn(ret) - @skipIf(six.PY3, 'Issue is specific to carbon module, which is PY2-only') + @skipIf(six.PY3, "Issue is specific to carbon module, which is PY2-only") @skipIf(salt.utils.platform.is_windows(), "Carbon does not install in Windows") @requires_system_grains def test_pip_installed_weird_install(self, grains=None): # First, check to see if this is running on CentOS 5 or MacOS. # If so, skip this test. - if grains['os'] in ('CentOS',) and grains['osrelease_info'][0] in (5,): - self.skipTest('This test does not run reliably on CentOS 5') - if grains['os'] in ('MacOS',): - self.skipTest('This test does not run reliably on MacOS') + if grains["os"] in ("CentOS",) and grains["osrelease_info"][0] in (5,): + self.skipTest("This test does not run reliably on CentOS 5") + if grains["os"] in ("MacOS",): + self.skipTest("This test does not run reliably on MacOS") - ographite = '/opt/graphite' + ographite = "/opt/graphite" if os.path.isdir(ographite): self.skipTest( - 'You already have \'{0}\'. This test would overwrite this ' - 'directory'.format(ographite) + "You already have '{0}'. This test would overwrite this " + "directory".format(ographite) ) try: os.makedirs(ographite) @@ -185,296 +179,302 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin): if err.errno == errno.EACCES: # Permission denied self.skipTest( - 'You don\'t have the required permissions to run this test' + "You don't have the required permissions to run this test" ) finally: if os.path.isdir(ographite): shutil.rmtree(ographite, ignore_errors=True) - venv_dir = os.path.join(RUNTIME_VARS.TMP, 'pip-installed-weird-install') + venv_dir = os.path.join(RUNTIME_VARS.TMP, "pip-installed-weird-install") try: # We may be able to remove this, I had to add it because the custom # modules from the test suite weren't available in the jinja # context when running the call to state.sls that comes after. - self.run_function('saltutil.sync_modules') + self.run_function("saltutil.sync_modules") # Since we don't have the virtualenv created, pip.installed will # throw an error. - ret = self.run_function( - 'state.sls', mods='pip-installed-weird-install' - ) + ret = self.run_function("state.sls", mods="pip-installed-weird-install") self.assertSaltTrueReturn(ret) # We cannot use assertInSaltComment here because we need to skip # some of the state return parts for key in six.iterkeys(ret): - self.assertTrue(ret[key]['result']) - if ret[key]['name'] != 'carbon < 1.1': + self.assertTrue(ret[key]["result"]) + if ret[key]["name"] != "carbon < 1.1": continue self.assertEqual( - ret[key]['comment'], - 'There was no error installing package \'carbon < 1.1\' ' - 'although it does not show when calling \'pip.freeze\'.' + ret[key]["comment"], + "There was no error installing package 'carbon < 1.1' " + "although it does not show when calling 'pip.freeze'.", ) break else: - raise Exception('Expected state did not run') + raise Exception("Expected state did not run") finally: if os.path.isdir(ographite): shutil.rmtree(ographite, ignore_errors=True) def test_issue_2028_pip_installed_state(self): - ret = self.run_function('state.sls', mods='issue-2028-pip-installed') + ret = self.run_function("state.sls", mods="issue-2028-pip-installed") - venv_dir = os.path.join( - RUNTIME_VARS.TMP, 'issue-2028-pip-installed' - ) + venv_dir = os.path.join(RUNTIME_VARS.TMP, "issue-2028-pip-installed") self.addCleanup(shutil.rmtree, venv_dir, ignore_errors=True) - pep8_bin = os.path.join(venv_dir, 'bin', 'pep8') + pep8_bin = os.path.join(venv_dir, "bin", "pep8") if salt.utils.platform.is_windows(): - pep8_bin = os.path.join(venv_dir, 'Scripts', 'pep8.exe') + pep8_bin = os.path.join(venv_dir, "Scripts", "pep8.exe") self.assertSaltTrueReturn(ret) - self.assertTrue( - os.path.isfile(pep8_bin) - ) + self.assertTrue(os.path.isfile(pep8_bin)) def test_issue_2087_missing_pip(self): - venv_dir = os.path.join( - RUNTIME_VARS.TMP, 'issue-2087-missing-pip' - ) + venv_dir = os.path.join(RUNTIME_VARS.TMP, "issue-2087-missing-pip") # Let's create the testing virtualenv ret = self._create_virtualenv(venv_dir) self.assertEqual( - ret['retcode'], 0, - msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format( + ret["retcode"], + 0, + msg="Expected 'retcode' key did not match. Full return dictionary:\n{}".format( pprint.pformat(ret) - ) + ), ) # Let's remove the pip binary - pip_bin = os.path.join(venv_dir, 'bin', 'pip') - site_dir = self.run_function('virtualenv.get_distribution_path', [venv_dir, 'pip']) + pip_bin = os.path.join(venv_dir, "bin", "pip") + site_dir = self.run_function( + "virtualenv.get_distribution_path", [venv_dir, "pip"] + ) if salt.utils.platform.is_windows(): - pip_bin = os.path.join(venv_dir, 'Scripts', 'pip.exe') - site_dir = os.path.join(venv_dir, 'lib', 'site-packages') + pip_bin = os.path.join(venv_dir, "Scripts", "pip.exe") + site_dir = os.path.join(venv_dir, "lib", "site-packages") if not os.path.isfile(pip_bin): - self.skipTest( - 'Failed to find the pip binary to the test virtualenv' - ) + self.skipTest("Failed to find the pip binary to the test virtualenv") os.remove(pip_bin) # Also remove the pip dir from site-packages # This is needed now that we're using python -m pip instead of the # pip binary directly. python -m pip will still work even if the # pip binary is missing - shutil.rmtree(os.path.join(site_dir, 'pip')) + shutil.rmtree(os.path.join(site_dir, "pip")) # Let's run the state which should fail because pip is missing - ret = self.run_function('state.sls', mods='issue-2087-missing-pip') + ret = self.run_function("state.sls", mods="issue-2087-missing-pip") self.assertSaltFalseReturn(ret) self.assertInSaltComment( - 'Error installing \'pep8\': Could not find a `pip` binary', - ret + "Error installing 'pep8': Could not find a `pip` binary", ret ) def test_issue_5940_multiple_pip_mirrors(self): - ''' + """ Test multiple pip mirrors. This test only works with pip < 7.0.0 - ''' - ret = self.run_function( - 'state.sls', mods='issue-5940-multiple-pip-mirrors' - ) + """ + ret = self.run_function("state.sls", mods="issue-5940-multiple-pip-mirrors") - venv_dir = os.path.join( - RUNTIME_VARS.TMP, '5940-multiple-pip-mirrors' - ) + venv_dir = os.path.join(RUNTIME_VARS.TMP, "5940-multiple-pip-mirrors") self.addCleanup(shutil.rmtree, venv_dir, ignore_errors=True) try: self.assertSaltTrueReturn(ret) - self.assertTrue( - os.path.isfile(os.path.join(venv_dir, 'bin', 'pep8')) - ) + self.assertTrue(os.path.isfile(os.path.join(venv_dir, "bin", "pep8"))) except (AssertionError, CommandExecutionError): - pip_version = self.run_function('pip.version', [venv_dir]) - if salt.utils.versions.compare(ver1=pip_version, oper='>=', ver2='7.0.0'): - self.skipTest('the --mirrors arg has been deprecated and removed in pip==7.0.0') + pip_version = self.run_function("pip.version", [venv_dir]) + if salt.utils.versions.compare(ver1=pip_version, oper=">=", ver2="7.0.0"): + self.skipTest( + "the --mirrors arg has been deprecated and removed in pip==7.0.0" + ) @destructiveTest @skip_if_not_root - @with_system_user('issue-6912', on_existing='delete', delete=True, - password='PassWord1!') + @with_system_user( + "issue-6912", on_existing="delete", delete=True, password="PassWord1!" + ) @with_tempdir() def test_issue_6912_wrong_owner(self, temp_dir, username): # Setup virtual environment directory to be used throughout the test - venv_dir = os.path.join(temp_dir, '6912-wrong-owner') + venv_dir = os.path.join(temp_dir, "6912-wrong-owner") # The virtual environment needs to be in a location that is accessible # by both the user running the test and the runas user if salt.utils.platform.is_windows(): - salt.utils.win_dacl.set_permissions(temp_dir, username, 'full_control') + salt.utils.win_dacl.set_permissions(temp_dir, username, "full_control") else: - uid = self.run_function('file.user_to_uid', [username]) + uid = self.run_function("file.user_to_uid", [username]) os.chown(temp_dir, uid, -1) # Create the virtual environment - venv_create = self._create_virtualenv(venv_dir, user=username, password='PassWord1!') - if venv_create['retcode'] > 0: - self.skipTest('Failed to create testcase virtual environment: {0}' - ''.format(venv_create)) + venv_create = self._create_virtualenv( + venv_dir, user=username, password="PassWord1!" + ) + if venv_create["retcode"] > 0: + self.skipTest( + "Failed to create testcase virtual environment: {0}" + "".format(venv_create) + ) # pip install passing the package name in `name` ret = self.run_state( - 'pip.installed', name='pep8', user=username, bin_env=venv_dir, - password='PassWord1!') + "pip.installed", + name="pep8", + user=username, + bin_env=venv_dir, + password="PassWord1!", + ) self.assertSaltTrueReturn(ret) if HAS_PWD: uid = pwd.getpwnam(username).pw_uid - for globmatch in (os.path.join(venv_dir, '**', 'pep8*'), - os.path.join(venv_dir, '*', '**', 'pep8*'), - os.path.join(venv_dir, '*', '*', '**', 'pep8*')): + for globmatch in ( + os.path.join(venv_dir, "**", "pep8*"), + os.path.join(venv_dir, "*", "**", "pep8*"), + os.path.join(venv_dir, "*", "*", "**", "pep8*"), + ): for path in glob.glob(globmatch): if HAS_PWD: self.assertEqual(uid, os.stat(path).st_uid) elif salt.utils.platform.is_windows(): - self.assertEqual( - salt.utils.win_dacl.get_owner(path), username) + self.assertEqual(salt.utils.win_dacl.get_owner(path), username) @destructiveTest @skip_if_not_root - @skipIf(salt.utils.platform.is_darwin(), 'Test is flaky on macosx') - @with_system_user('issue-6912', on_existing='delete', delete=True, - password='PassWord1!') + @skipIf(salt.utils.platform.is_darwin(), "Test is flaky on macosx") + @with_system_user( + "issue-6912", on_existing="delete", delete=True, password="PassWord1!" + ) @with_tempdir() def test_issue_6912_wrong_owner_requirements_file(self, temp_dir, username): # Setup virtual environment directory to be used throughout the test - venv_dir = os.path.join(temp_dir, '6912-wrong-owner') + venv_dir = os.path.join(temp_dir, "6912-wrong-owner") # The virtual environment needs to be in a location that is accessible # by both the user running the test and the runas user if salt.utils.platform.is_windows(): - salt.utils.win_dacl.set_permissions(temp_dir, username, 'full_control') + salt.utils.win_dacl.set_permissions(temp_dir, username, "full_control") else: - uid = self.run_function('file.user_to_uid', [username]) + uid = self.run_function("file.user_to_uid", [username]) os.chown(temp_dir, uid, -1) # Create the virtual environment again as it should have been removed - venv_create = self._create_virtualenv(venv_dir, user=username, password='PassWord1!') - if venv_create['retcode'] > 0: - self.skipTest('failed to create testcase virtual environment: {0}' - ''.format(venv_create)) + venv_create = self._create_virtualenv( + venv_dir, user=username, password="PassWord1!" + ) + if venv_create["retcode"] > 0: + self.skipTest( + "failed to create testcase virtual environment: {0}" + "".format(venv_create) + ) # pip install using a requirements file req_filename = os.path.join( - RUNTIME_VARS.TMP_STATE_TREE, 'issue-6912-requirements.txt' + RUNTIME_VARS.TMP_STATE_TREE, "issue-6912-requirements.txt" ) - with salt.utils.files.fopen(req_filename, 'wb') as reqf: - reqf.write(b'pep8\n') + with salt.utils.files.fopen(req_filename, "wb") as reqf: + reqf.write(b"pep8\n") ret = self.run_state( - 'pip.installed', name='', user=username, bin_env=venv_dir, - requirements='salt://issue-6912-requirements.txt', - password='PassWord1!') + "pip.installed", + name="", + user=username, + bin_env=venv_dir, + requirements="salt://issue-6912-requirements.txt", + password="PassWord1!", + ) self.assertSaltTrueReturn(ret) if HAS_PWD: uid = pwd.getpwnam(username).pw_uid - for globmatch in (os.path.join(venv_dir, '**', 'pep8*'), - os.path.join(venv_dir, '*', '**', 'pep8*'), - os.path.join(venv_dir, '*', '*', '**', 'pep8*')): + for globmatch in ( + os.path.join(venv_dir, "**", "pep8*"), + os.path.join(venv_dir, "*", "**", "pep8*"), + os.path.join(venv_dir, "*", "*", "**", "pep8*"), + ): for path in glob.glob(globmatch): if HAS_PWD: self.assertEqual(uid, os.stat(path).st_uid) elif salt.utils.platform.is_windows(): - self.assertEqual( - salt.utils.win_dacl.get_owner(path), username) + self.assertEqual(salt.utils.win_dacl.get_owner(path), username) def test_issue_6833_pip_upgrade_pip(self): # Create the testing virtualenv - venv_dir = os.path.join( - RUNTIME_VARS.TMP, '6833-pip-upgrade-pip' - ) + venv_dir = os.path.join(RUNTIME_VARS.TMP, "6833-pip-upgrade-pip") ret = self._create_virtualenv(venv_dir) self.assertEqual( - ret['retcode'], 0, - msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format( + ret["retcode"], + 0, + msg="Expected 'retcode' key did not match. Full return dictionary:\n{}".format( pprint.pformat(ret) - ) + ), ) import salt.modules.virtualenv_mod - msg = 'New python executable' + + msg = "New python executable" if salt.modules.virtualenv_mod.virtualenv_ver(venv_dir) >= (20, 0, 2): - msg = 'created virtual environment' + msg = "created virtual environment" self.assertIn( msg, - ret['stdout'], - msg='Expected STDOUT did not match. Full return dictionary:\n{}'.format( + ret["stdout"], + msg="Expected STDOUT did not match. Full return dictionary:\n{}".format( pprint.pformat(ret) - ) + ), ) # Let's install a fixed version pip over whatever pip was # previously installed ret = self.run_function( - 'pip.install', ['pip==8.0'], upgrade=True, - bin_env=venv_dir + "pip.install", ["pip==8.0"], upgrade=True, bin_env=venv_dir ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) - self.assertEqual(ret['retcode'], 0) - self.assertIn( - 'Successfully installed pip', - ret['stdout'] - ) + self.assertEqual(ret["retcode"], 0) + self.assertIn("Successfully installed pip", ret["stdout"]) # Let's make sure we have pip 8.0 installed self.assertEqual( - self.run_function('pip.list', ['pip'], bin_env=venv_dir), - {'pip': '8.0.0'} + self.run_function("pip.list", ["pip"], bin_env=venv_dir), {"pip": "8.0.0"} ) # Now the actual pip upgrade pip test ret = self.run_state( - 'pip.installed', name='pip==8.0.1', upgrade=True, - bin_env=venv_dir + "pip.installed", name="pip==8.0.1", upgrade=True, bin_env=venv_dir ) if not isinstance(ret, dict): self.fail( - 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret) + "The 'pip.install' command did not return the excepted dictionary. Output:\n{}".format( + ret + ) ) self.assertSaltTrueReturn(ret) - self.assertSaltStateChangesEqual(ret, {'pip==8.0.1': 'Installed'}) + self.assertSaltStateChangesEqual(ret, {"pip==8.0.1": "Installed"}) def test_pip_installed_specific_env(self): # Create the testing virtualenv - venv_dir = os.path.join( - RUNTIME_VARS.TMP, 'pip-installed-specific-env' - ) + venv_dir = os.path.join(RUNTIME_VARS.TMP, "pip-installed-specific-env") # Let's write a requirements file requirements_file = os.path.join( - RUNTIME_VARS.TMP_PRODENV_STATE_TREE, 'prod-env-requirements.txt' + RUNTIME_VARS.TMP_PRODENV_STATE_TREE, "prod-env-requirements.txt" ) - with salt.utils.files.fopen(requirements_file, 'wb') as reqf: - reqf.write(b'pep8\n') + with salt.utils.files.fopen(requirements_file, "wb") as reqf: + reqf.write(b"pep8\n") try: self._create_virtualenv(venv_dir) # The requirements file should not be found the base environment ret = self.run_state( - 'pip.installed', name='', bin_env=venv_dir, - requirements='salt://prod-env-requirements.txt' + "pip.installed", + name="", + bin_env=venv_dir, + requirements="salt://prod-env-requirements.txt", ) self.assertSaltFalseReturn(ret) self.assertInSaltComment( @@ -483,70 +483,80 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin): # The requirements file must be found in the prod environment ret = self.run_state( - 'pip.installed', name='', bin_env=venv_dir, saltenv='prod', - requirements='salt://prod-env-requirements.txt' + "pip.installed", + name="", + bin_env=venv_dir, + saltenv="prod", + requirements="salt://prod-env-requirements.txt", ) self.assertSaltTrueReturn(ret) self.assertInSaltComment( - 'Successfully processed requirements file ' - 'salt://prod-env-requirements.txt', ret + "Successfully processed requirements file " + "salt://prod-env-requirements.txt", + ret, ) # We're using the base environment but we're passing the prod # environment as an url arg to salt:// ret = self.run_state( - 'pip.installed', name='', bin_env=venv_dir, - requirements='salt://prod-env-requirements.txt?saltenv=prod' + "pip.installed", + name="", + bin_env=venv_dir, + requirements="salt://prod-env-requirements.txt?saltenv=prod", ) self.assertSaltTrueReturn(ret) - self.assertInSaltComment( - 'Requirements were already installed.', - ret - ) + self.assertInSaltComment("Requirements were already installed.", ret) finally: if os.path.isfile(requirements_file): os.unlink(requirements_file) - @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2') + @skipIf( + salt.utils.platform.is_darwin() and six.PY2, "This test hangs on OS X on Py2" + ) def test_22359_pip_installed_unless_does_not_trigger_warnings(self): # This test case should be moved to a format_call unit test specific to # the state internal keywords - venv_dir = os.path.join(RUNTIME_VARS.TMP, 'pip-installed-unless') + venv_dir = os.path.join(RUNTIME_VARS.TMP, "pip-installed-unless") venv_create = self._create_virtualenv(venv_dir) - if venv_create['retcode'] > 0: + if venv_create["retcode"] > 0: self.skipTest( - 'Failed to create testcase virtual environment: {0}'.format( - venv_create - ) + "Failed to create testcase virtual environment: {0}".format(venv_create) ) false_cmd = RUNTIME_VARS.SHELL_FALSE_PATH if salt.utils.platform.is_windows(): - false_cmd = 'exit 1 >nul' + false_cmd = "exit 1 >nul" try: ret = self.run_state( - 'pip.installed', name='pep8', bin_env=venv_dir, unless=false_cmd, timeout=600 + "pip.installed", + name="pep8", + bin_env=venv_dir, + unless=false_cmd, + timeout=600, ) self.assertSaltTrueReturn(ret) - self.assertNotIn('warnings', next(six.itervalues(ret))) + self.assertNotIn("warnings", next(six.itervalues(ret))) finally: if os.path.isdir(venv_dir): shutil.rmtree(venv_dir, ignore_errors=True) - @skipIf(sys.version_info[:2] >= (3, 6), 'Old version of virtualenv too old for python3.6') + @skipIf( + sys.version_info[:2] >= (3, 6), + "Old version of virtualenv too old for python3.6", + ) @skipIf(salt.utils.platform.is_windows(), "Carbon does not install in Windows") def test_46127_pip_env_vars(self): - ''' + """ Test that checks if env_vars passed to pip.installed are also passed to pip.freeze while checking for existing installations - ''' + """ # This issue is most easily checked while installing carbon # Much of the code here comes from the test_weird_install function above - ographite = '/opt/graphite' + ographite = "/opt/graphite" if os.path.isdir(ographite): self.skipTest( - 'You already have \'{0}\'. This test would overwrite this ' - 'directory'.format(ographite) + "You already have '{0}'. This test would overwrite this " + "directory".format(ographite) ) try: os.makedirs(ographite) @@ -554,55 +564,50 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin): if err.errno == errno.EACCES: # Permission denied self.skipTest( - 'You don\'t have the required permissions to run this test' + "You don't have the required permissions to run this test" ) finally: if os.path.isdir(ographite): shutil.rmtree(ographite, ignore_errors=True) - venv_dir = os.path.join(RUNTIME_VARS.TMP, 'issue-46127-pip-env-vars') + venv_dir = os.path.join(RUNTIME_VARS.TMP, "issue-46127-pip-env-vars") try: # We may be able to remove this, I had to add it because the custom # modules from the test suite weren't available in the jinja # context when running the call to state.sls that comes after. - self.run_function('saltutil.sync_modules') + self.run_function("saltutil.sync_modules") # Since we don't have the virtualenv created, pip.installed will # throw an error. - ret = self.run_function( - 'state.sls', mods='issue-46127-pip-env-vars' - ) + ret = self.run_function("state.sls", mods="issue-46127-pip-env-vars") self.assertSaltTrueReturn(ret) for key in six.iterkeys(ret): - self.assertTrue(ret[key]['result']) - if ret[key]['name'] != 'carbon < 1.3': + self.assertTrue(ret[key]["result"]) + if ret[key]["name"] != "carbon < 1.3": continue self.assertEqual( - ret[key]['comment'], - 'All packages were successfully installed' + ret[key]["comment"], "All packages were successfully installed" ) break else: - raise Exception('Expected state did not run') + raise Exception("Expected state did not run") # Run the state again. Now the already installed message should # appear - ret = self.run_function( - 'state.sls', mods='issue-46127-pip-env-vars' - ) + ret = self.run_function("state.sls", mods="issue-46127-pip-env-vars") self.assertSaltTrueReturn(ret) # We cannot use assertInSaltComment here because we need to skip # some of the state return parts for key in six.iterkeys(ret): - self.assertTrue(ret[key]['result']) + self.assertTrue(ret[key]["result"]) # As we are re-running the formula, some states will not be run # and "name" may or may not be present, so we use .get() pattern - if ret[key].get('name', '') != 'carbon < 1.3': + if ret[key].get("name", "") != "carbon < 1.3": continue self.assertEqual( - ret[key]['comment'], - ('All packages were successfully installed')) + ret[key]["comment"], ("All packages were successfully installed") + ) break else: - raise Exception('Expected state did not run') + raise Exception("Expected state did not run") finally: if os.path.isdir(ographite): shutil.rmtree(ographite, ignore_errors=True) @@ -610,11 +615,11 @@ class PipStateTest(ModuleCase, SaltReturnAssertsMixin): shutil.rmtree(venv_dir) +@pytest.mark.windows_whitelisted class PipStateInRequisiteTest(ModuleCase, SaltReturnAssertsMixin): - @with_tempdir() def test_issue_54755(self, tmpdir): - ''' + """ Verify github issue 54755 is resolved. This only fails when there is no pip module in the python environment. Since the test suite normally has a pip module this test will pass and is here for posterity. See also @@ -627,11 +632,13 @@ class PipStateInRequisiteTest(ModuleCase, SaltReturnAssertsMixin): Which also validate this issue and will pass/fail regardless of whether or not pip is installed. - ''' - file_path = os.path.join(tmpdir, 'issue-54755') - ret = self.run_function('state.sls', mods='issue-54755', pillar={'file_path': file_path}) - key = 'file_|-issue-54755_|-{}_|-managed'.format(file_path) + """ + file_path = os.path.join(tmpdir, "issue-54755") + ret = self.run_function( + "state.sls", mods="issue-54755", pillar={"file_path": file_path} + ) + key = "file_|-issue-54755_|-{}_|-managed".format(file_path) assert key in ret - assert ret[key]['result'] is True - with salt.utils.files.fopen(file_path, 'r') as fp: - assert fp.read().strip() == 'issue-54755' + assert ret[key]["result"] is True + with salt.utils.files.fopen(file_path, "r") as fp: + assert fp.read().strip() == "issue-54755" diff --git a/tests/integration/states/test_pkg.py b/tests/integration/states/test_pkg.py index 046757e041d..3553e42f08c 100644 --- a/tests/integration/states/test_pkg.py +++ b/tests/integration/states/test_pkg.py @@ -1,40 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" tests for pkg state -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import logging import os import time -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.unit import skipIf -from tests.support.helpers import ( - destructiveTest, - requires_salt_modules, - requires_salt_states, - requires_system_grains, - runs_on, - not_runs_on) - -# Import Salt libs +import pytest import salt.utils.files import salt.utils.path import salt.utils.pkg.rpm import salt.utils.platform - -# Import 3rd-party libs from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.ext.six.moves import range +from tests.support.case import ModuleCase +from tests.support.helpers import ( + destructiveTest, + not_runs_on, + requires_salt_modules, + requires_salt_states, + requires_system_grains, + runs_on, +) +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf log = logging.getLogger(__name__) @destructiveTest +@pytest.mark.windows_whitelisted class PkgTest(ModuleCase, SaltReturnAssertsMixin): _PKG_EPOCH_TARGETS = [] _PKG_32_TARGETS = [] @@ -47,60 +45,60 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): @requires_system_grains def setUpClass(cls, grains=None): # pylint:disable=W0221 cls.ctx = {} - cls._PKG_TARGETS = ['figlet', 'sl'] - if grains['os'] == 'Windows': - cls._PKG_TARGETS = ['7zip', 'putty'] - elif grains['os'] == 'freebsd': + cls._PKG_TARGETS = ["figlet", "sl"] + if grains["os"] == "Windows": + cls._PKG_TARGETS = ["7zip", "putty"] + elif grains["os"] == "freebsd": cls._VERSION_SPEC_SUPPORTED = False - elif grains['os_family'] in ('Arch', 'Debian'): + elif grains["os_family"] in ("Arch", "Debian"): cls._WILDCARDS_SUPPORTED = True - elif grains['os'] == 'Amazon': - cls._PKG_TARGETS = ['lynx', 'gnuplot'] - elif grains['os_family'] == 'RedHat': - cls._PKG_TARGETS = ['units', 'zsh-html'] + elif grains["os"] == "Amazon": + cls._PKG_TARGETS = ["lynx", "gnuplot"] + elif grains["os_family"] == "RedHat": + cls._PKG_TARGETS = ["units", "zsh-html"] cls._WILDCARDS_SUPPORTED = True - if grains['os'] == 'CentOS': - if grains['osmajorrelease'] == 5: - cls._PKG_32_TARGETS = ['xz-devel.i386'] + if grains["os"] == "CentOS": + if grains["osmajorrelease"] == 5: + cls._PKG_32_TARGETS = ["xz-devel.i386"] else: - cls._PKG_32_TARGETS.append('xz-devel.i686') - if grains['osmajorrelease'] == 5: - cls._PKG_DOT_TARGETS = ['python-migrate0.5'] - elif grains['osmajorrelease'] == 6: - cls._PKG_DOT_TARGETS = ['tomcat6-el-2.1-api'] - elif grains['osmajorrelease'] == 7: - cls._PKG_DOT_TARGETS = ['tomcat-el-2.2-api'] - cls._PKG_EPOCH_TARGETS = ['comps-extras'] - elif grains['os_family'] == 'Suse': - cls._PKG_TARGETS = ['lynx', 'htop'] - if grains['os'] == 'SUSE': - cls._PKG_CAP_TARGETS = [('perl(ZNC)', 'znc-perl')] + cls._PKG_32_TARGETS.append("xz-devel.i686") + if grains["osmajorrelease"] == 5: + cls._PKG_DOT_TARGETS = ["python-migrate0.5"] + elif grains["osmajorrelease"] == 6: + cls._PKG_DOT_TARGETS = ["tomcat6-el-2.1-api"] + elif grains["osmajorrelease"] == 7: + cls._PKG_DOT_TARGETS = ["tomcat-el-2.2-api"] + cls._PKG_EPOCH_TARGETS = ["comps-extras"] + elif grains["os_family"] == "Suse": + cls._PKG_TARGETS = ["lynx", "htop"] + if grains["os"] == "SUSE": + cls._PKG_CAP_TARGETS = [("perl(ZNC)", "znc-perl")] @classmethod def tearDownClass(cls): del cls.ctx def latest_version(self, *names): - ''' + """ Helper function which ensures that we don't make any unnecessary calls to pkg.latest_version to figure out what version we need to install. This won't stop pkg.latest_version from being run in a pkg.latest state, but it will reduce the amount of times we check the latest version here in the test suite. - ''' - key = 'latest_version' + """ + key = "latest_version" if key not in self.ctx: self.ctx[key] = dict() targets = [x for x in names if x not in self.ctx[key]] if targets: - result = self.run_function('pkg.latest_version', targets, refresh=False) + result = self.run_function("pkg.latest_version", targets, refresh=False) try: self.ctx[key].update(result) except ValueError: # Only a single target, pkg.latest_version returned a string self.ctx[key][targets[0]] = result - ret = dict([(x, self.ctx[key].get(x, '')) for x in names]) + ret = dict([(x, self.ctx[key].get(x, "")) for x in names]) if len(names) == 1: return ret[names[0]] return ret @@ -108,45 +106,45 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): @requires_system_grains def setUp(self, grains=None): # pylint:disable=W0221 super(PkgTest, self).setUp() - if 'refresh' not in self.ctx: - self.run_function('pkg.refresh_db') - self.ctx['refresh'] = True + if "refresh" not in self.ctx: + self.run_function("pkg.refresh_db") + self.ctx["refresh"] = True # If this is Arch Linux, check if pacman is in use by another process - if grains['os_family'] == 'Arch': + if grains["os_family"] == "Arch": for _ in range(12): - if not os.path.isfile('/var/lib/pacman/db.lck'): + if not os.path.isfile("/var/lib/pacman/db.lck"): break else: time.sleep(5) else: - raise Exception('Package database locked after 60 seconds, bailing out') + raise Exception("Package database locked after 60 seconds, bailing out") - @requires_salt_modules('pkg.version') - @requires_salt_states('pkg.installed', 'pkg.removed') + @requires_salt_modules("pkg.version") + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_001_installed(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target = self._PKG_TARGETS[0] - version = self.run_function('pkg.version', [target]) + version = self.run_function("pkg.version", [target]) # If this assert fails, we need to find new targets, this test needs to # be able to test successful installation of packages, so this package # needs to not be installed before we run the states below self.assertFalse(version) - ret = self.run_state('pkg.installed', name=target, refresh=False) + ret = self.run_state("pkg.installed", name=target, refresh=False) self.assertSaltTrueReturn(ret) - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @skipIf(not _VERSION_SPEC_SUPPORTED, 'Version specification not supported') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf(not _VERSION_SPEC_SUPPORTED, "Version specification not supported") + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_002_installed_with_version(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target = self._PKG_TARGETS[0] version = self.latest_version(target) @@ -155,43 +153,43 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): # needs to not be installed before we run the states below self.assertTrue(version) - ret = self.run_state('pkg.installed', - name=target, - version=version, - refresh=False) + ret = self.run_state( + "pkg.installed", name=target, version=version, refresh=False + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @requires_salt_states('pkg.installed', 'pkg.removed') + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_003_installed_multipkg(self): - ''' + """ This is a destructive test as it installs and then removes two packages - ''' - version = self.run_function('pkg.version', self._PKG_TARGETS) + """ + version = self.run_function("pkg.version", self._PKG_TARGETS) # If this assert fails, we need to find new targets, this test needs to # be able to test successful installation of packages, so these # packages need to not be installed before we run the states below self.assertFalse(any(version.values())) - self.assertSaltTrueReturn(self.run_state('pkg.removed', name=None, pkgs=self._PKG_TARGETS)) + self.assertSaltTrueReturn( + self.run_state("pkg.removed", name=None, pkgs=self._PKG_TARGETS) + ) try: - ret = self.run_state('pkg.installed', - name=None, - pkgs=self._PKG_TARGETS, - refresh=False) + ret = self.run_state( + "pkg.installed", name=None, pkgs=self._PKG_TARGETS, refresh=False + ) self.assertSaltTrueReturn(ret) finally: - ret = self.run_state('pkg.removed', name=None, pkgs=self._PKG_TARGETS) + ret = self.run_state("pkg.removed", name=None, pkgs=self._PKG_TARGETS) self.assertSaltTrueReturn(ret) - @skipIf(not _VERSION_SPEC_SUPPORTED, 'Version specification not supported') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf(not _VERSION_SPEC_SUPPORTED, "Version specification not supported") + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_004_installed_multipkg_with_version(self): - ''' + """ This is a destructive test as it installs and then removes two packages - ''' + """ version = self.latest_version(self._PKG_TARGETS[0]) # If this assert fails, we need to find new targets, this test needs to @@ -202,28 +200,25 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): pkgs = [{self._PKG_TARGETS[0]: version}, self._PKG_TARGETS[1]] try: - ret = self.run_state('pkg.installed', - name=None, - pkgs=pkgs, - refresh=False) + ret = self.run_state("pkg.installed", name=None, pkgs=pkgs, refresh=False) self.assertSaltTrueReturn(ret) finally: - ret = self.run_state('pkg.removed', name=None, pkgs=self._PKG_TARGETS) + ret = self.run_state("pkg.removed", name=None, pkgs=self._PKG_TARGETS) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_32_TARGETS, 'No 32 bit packages have been specified for testing') - @requires_salt_modules('pkg.version') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf(not _PKG_32_TARGETS, "No 32 bit packages have been specified for testing") + @requires_salt_modules("pkg.version") + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_005_installed_32bit(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target = self._PKG_32_TARGETS[0] # _PKG_TARGETS_32 is only populated for platforms for which Salt has to # munge package names for 32-bit-on-x86_64 (Currently only Ubuntu and # RHEL-based). Don't actually perform this test on other platforms. - version = self.run_function('pkg.version', [target]) + version = self.run_function("pkg.version", [target]) # If this assert fails, we need to find a new target. This test # needs to be able to test successful installation of packages, so @@ -231,19 +226,17 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): # below self.assertFalse(version) - ret = self.run_state('pkg.installed', - name=target, - refresh=False) + ret = self.run_state("pkg.installed", name=target, refresh=False) self.assertSaltTrueReturn(ret) - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_32_TARGETS, 'No 32 bit packages have been specified for testing') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf(not _PKG_32_TARGETS, "No 32 bit packages have been specified for testing") + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_006_installed_32bit_with_version(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target = self._PKG_32_TARGETS[0] # _PKG_TARGETS_32 is only populated for platforms for which Salt has to @@ -257,23 +250,25 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): # below self.assertTrue(version) - ret = self.run_state('pkg.installed', - name=target, - version=version, - refresh=False) + ret = self.run_state( + "pkg.installed", name=target, version=version, refresh=False + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_DOT_TARGETS, 'No packages with "." in their name have been configured for') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf( + not _PKG_DOT_TARGETS, + 'No packages with "." in their name have been configured for', + ) + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_007_with_dot_in_pkgname(self=None): - ''' + """ This tests for the regression found in the following issue: https://github.com/saltstack/salt/issues/8614 This is a destructive test as it installs a package - ''' + """ target = self._PKG_DOT_TARGETS[0] version = self.latest_version(target) @@ -282,20 +277,23 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): # the target needs to not be installed before we run the # pkg.installed state below self.assertTrue(bool(version)) - ret = self.run_state('pkg.installed', name=target, refresh=False) + ret = self.run_state("pkg.installed", name=target, refresh=False) self.assertSaltTrueReturn(ret) - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_EPOCH_TARGETS, 'No targets have been configured with "epoch" in the version') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf( + not _PKG_EPOCH_TARGETS, + 'No targets have been configured with "epoch" in the version', + ) + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_008_epoch_in_version(self): - ''' + """ This tests for the regression found in the following issue: https://github.com/saltstack/salt/issues/8614 This is a destructive test as it installs a package - ''' + """ target = self._PKG_EPOCH_TARGETS[0] version = self.latest_version(target) @@ -304,42 +302,39 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): # the target needs to not be installed before we run the # pkg.installed state below self.assertTrue(version) - ret = self.run_state('pkg.installed', - name=target, - version=version, - refresh=False) + ret = self.run_state( + "pkg.installed", name=target, version=version, refresh=False + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @requires_salt_modules('pkg.version', 'pkg.info_installed') - @requires_salt_states('pkg.installed', 'pkg.removed') - @runs_on(kernel='linux') - @not_runs_on(os='Amazon') + @requires_salt_modules("pkg.version", "pkg.info_installed") + @requires_salt_states("pkg.installed", "pkg.removed") + @runs_on(kernel="linux") + @not_runs_on(os="Amazon") def test_pkg_009_latest_with_epoch(self): - ''' + """ This tests for the following issue: https://github.com/saltstack/salt/issues/31014 This is a destructive test as it installs a package - ''' - package = 'bash-completion' - pkgquery = 'version' + """ + package = "bash-completion" + pkgquery = "version" - ret = self.run_state('pkg.installed', - name=package, - refresh=False) + ret = self.run_state("pkg.installed", name=package, refresh=False) self.assertSaltTrueReturn(ret) - ret = self.run_function('pkg.info_installed', [package]) + ret = self.run_function("pkg.info_installed", [package]) self.assertTrue(pkgquery in six.text_type(ret)) - @requires_salt_states('pkg.latest', 'pkg.removed') + @requires_salt_states("pkg.latest", "pkg.removed") def test_pkg_010_latest(self): - ''' + """ This tests pkg.latest with a package that has no epoch (or a zero epoch). - ''' + """ target = self._PKG_TARGETS[0] version = self.latest_version(target) @@ -348,19 +343,19 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): # needs to not be installed before we run the states below self.assertTrue(version) - ret = self.run_state('pkg.latest', name=target, refresh=False) + ret = self.run_state("pkg.latest", name=target, refresh=False) self.assertSaltTrueReturn(ret) - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @requires_salt_modules('pkg.list_pkgs', 'pkg.list_upgrades', 'pkg.version') - @requires_salt_states('pkg.latest') - @runs_on(kernel='linux', os_family='Debian') + @requires_salt_modules("pkg.list_pkgs", "pkg.list_upgrades", "pkg.version") + @requires_salt_states("pkg.latest") + @runs_on(kernel="linux", os_family="Debian") def test_pkg_011_latest_only_upgrade(self): - ''' + """ WARNING: This test will pick a package with an available upgrade (if there is one) and upgrade it to the latest version. - ''' + """ target = self._PKG_TARGETS[0] # If this assert fails, we need to find new targets, this test needs to @@ -371,99 +366,90 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): version = self.latest_version(target) self.assertTrue(version) - ret = self.run_state('pkg.latest', name=target, refresh=False, only_upgrade=True) + ret = self.run_state( + "pkg.latest", name=target, refresh=False, only_upgrade=True + ) self.assertSaltFalseReturn(ret) # Now look for updates and try to run the state on a package which is already up-to-date. - installed_pkgs = self.run_function('pkg.list_pkgs') - updates = self.run_function('pkg.list_upgrades', refresh=False) + installed_pkgs = self.run_function("pkg.list_pkgs") + updates = self.run_function("pkg.list_upgrades", refresh=False) for pkgname in updates: if pkgname in installed_pkgs: target = pkgname break else: - target = '' + target = "" log.warning( - 'No available upgrades to installed packages, skipping ' - 'only_upgrade=True test with already-installed package. For ' - 'best results run this test on a machine with upgrades ' - 'available.' + "No available upgrades to installed packages, skipping " + "only_upgrade=True test with already-installed package. For " + "best results run this test on a machine with upgrades " + "available." ) if target: - ret = self.run_state('pkg.latest', name=target, refresh=False, - only_upgrade=True) + ret = self.run_state( + "pkg.latest", name=target, refresh=False, only_upgrade=True + ) self.assertSaltTrueReturn(ret) - new_version = self.run_function('pkg.version', [target]) + new_version = self.run_function("pkg.version", [target]) self.assertEqual(new_version, updates[target]) - ret = self.run_state('pkg.latest', name=target, refresh=False, - only_upgrade=True) + ret = self.run_state( + "pkg.latest", name=target, refresh=False, only_upgrade=True + ) self.assertEqual( - ret['pkg_|-{0}_|-{0}_|-latest'.format(target)]['comment'], - 'Package {0} is already up-to-date'.format(target) + ret["pkg_|-{0}_|-{0}_|-latest".format(target)]["comment"], + "Package {0} is already up-to-date".format(target), ) - @skipIf(not _WILDCARDS_SUPPORTED, 'Wildcards in pkg.install are not supported') - @requires_salt_modules('pkg.version') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf(not _WILDCARDS_SUPPORTED, "Wildcards in pkg.install are not supported") + @requires_salt_modules("pkg.version") + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_012_installed_with_wildcard_version(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target = self._PKG_TARGETS[0] - version = self.run_function('pkg.version', [target]) + version = self.run_function("pkg.version", [target]) # If this assert fails, we need to find new targets, this test needs to # be able to test successful installation of packages, so this package # needs to not be installed before we run the states below self.assertFalse(version) - ret = self.run_state( - 'pkg.installed', - name=target, - version='*', - refresh=False, - ) + ret = self.run_state("pkg.installed", name=target, version="*", refresh=False,) self.assertSaltTrueReturn(ret) # Repeat state, should pass - ret = self.run_state( - 'pkg.installed', - name=target, - version='*', - refresh=False, - ) + ret = self.run_state("pkg.installed", name=target, version="*", refresh=False,) expected_comment = ( - 'All specified packages are already installed and are at the ' - 'desired version' + "All specified packages are already installed and are at the " + "desired version" ) self.assertSaltTrueReturn(ret) - self.assertEqual(ret[next(iter(ret))]['comment'], expected_comment) + self.assertEqual(ret[next(iter(ret))]["comment"], expected_comment) # Repeat one more time with unavailable version, test should fail ret = self.run_state( - 'pkg.installed', - name=target, - version='93413*', - refresh=False, + "pkg.installed", name=target, version="93413*", refresh=False, ) self.assertSaltFalseReturn(ret) # Clean up - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @requires_salt_modules('pkg.version', 'pkg.latest_version') - @requires_salt_states('pkg.installed', 'pkg.removed') - @runs_on(kernel='linux', os_family=['Debian', 'RedHat']) + @requires_salt_modules("pkg.version", "pkg.latest_version") + @requires_salt_states("pkg.installed", "pkg.removed") + @runs_on(kernel="linux", os_family=["Debian", "RedHat"]) def test_pkg_013_installed_with_comparison_operator(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target = self._PKG_TARGETS[0] - version = self.run_function('pkg.version', [target]) + version = self.run_function("pkg.version", [target]) # If this assert fails, we need to find new targets, this test needs to # be able to test successful installation of packages, so this package @@ -471,37 +457,33 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): self.assertFalse(version) latest_version = self.run_function( - 'pkg.latest_version', - [target], - refresh=False) + "pkg.latest_version", [target], refresh=False + ) try: ret = self.run_state( - 'pkg.installed', - name=target, - version='<9999999', - refresh=False, + "pkg.installed", name=target, version="<9999999", refresh=False, ) self.assertSaltTrueReturn(ret) # The version that was installed should be the latest available - version = self.run_function('pkg.version', [target]) + version = self.run_function("pkg.version", [target]) self.assertTrue(version, latest_version) finally: # Clean up - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @requires_salt_modules('pkg.version') - @requires_salt_states('pkg.installed', 'pkg.removed') - @runs_on(kernel='linux', os_familiy='RedHat') + @requires_salt_modules("pkg.version") + @requires_salt_states("pkg.installed", "pkg.removed") + @runs_on(kernel="linux", os_familiy="RedHat") def test_pkg_014_installed_missing_release(self): - ''' + """ Tests that a version number missing the release portion still resolves as correctly installed. For example, version 2.0.2 instead of 2.0.2-1.el7 - ''' + """ target = self._PKG_TARGETS[0] - version = self.run_function('pkg.version', [target]) + version = self.run_function("pkg.version", [target]) # If this assert fails, we need to find new targets, this test needs to # be able to test successful installation of packages, so this package @@ -509,7 +491,7 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): self.assertFalse(version) ret = self.run_state( - 'pkg.installed', + "pkg.installed", name=target, version=salt.utils.pkg.rpm.version_to_evr(version)[1], refresh=False, @@ -517,147 +499,170 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): self.assertSaltTrueReturn(ret) # Clean up - ret = self.run_state('pkg.removed', name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) - @requires_salt_modules('pkg.hold', 'pkg.unhold', 'pkg.version', 'pkg.list_pkgs') - @requires_salt_states('pkg.installed', 'pkg.removed') + @requires_salt_modules("pkg.hold", "pkg.unhold", "pkg.version", "pkg.list_pkgs") + @requires_salt_states("pkg.installed", "pkg.removed") @requires_system_grains def test_pkg_015_installed_held(self, grains=None): - ''' + """ Tests that a package can be held even when the package is already installed. - ''' + """ versionlock_pkg = None - if grains['os_family'] == 'RedHat': - pkgs = {p for p in self.run_function('pkg.list_pkgs') if '-versionlock' in p} + if grains["os_family"] == "RedHat": + pkgs = { + p for p in self.run_function("pkg.list_pkgs") if "-versionlock" in p + } if not pkgs: - self.skipTest('No versionlock package found in repositories') + self.skipTest("No versionlock package found in repositories") for versionlock_pkg in pkgs: - ret = self.run_state('pkg.installed', name=versionlock_pkg, refresh=False) + ret = self.run_state( + "pkg.installed", name=versionlock_pkg, refresh=False + ) # Exit loop if a versionlock package installed correctly try: self.assertSaltTrueReturn(ret) - log.debug('Installed versionlock package: {}'.format(versionlock_pkg)) + log.debug( + "Installed versionlock package: {}".format(versionlock_pkg) + ) break except AssertionError as e: - log.debug('Versionlock package not found:\n{}'.format(e)) + log.debug("Versionlock package not found:\n{}".format(e)) else: - self.fail('Could not install versionlock package from {}'.format(pkgs)) + self.fail("Could not install versionlock package from {}".format(pkgs)) target = self._PKG_TARGETS[0] # First we ensure that the package is installed - ret = self.run_state( - 'pkg.installed', - name=target, - refresh=False, - ) + ret = self.run_state("pkg.installed", name=target, refresh=False,) self.assertSaltTrueReturn(ret) # Then we check that the package is now held - ret = self.run_state( - 'pkg.installed', - name=target, - hold=True, - refresh=False, - ) + ret = self.run_state("pkg.installed", name=target, hold=True, refresh=False,) - if versionlock_pkg and '-versionlock is not installed' in str(ret): - self.skipTest('{} `{}` is installed'.format(ret, versionlock_pkg)) + if versionlock_pkg and "-versionlock is not installed" in str(ret): + self.skipTest("{} `{}` is installed".format(ret, versionlock_pkg)) # changes from pkg.hold for Red Hat family are different target_changes = {} - if grains['os_family'] == 'RedHat': - target_changes = {'new': 'hold', 'old': ''} - elif grains['os_family'] == 'Debian': - target_changes = {'new': 'hold', 'old': 'install'} + if grains["os_family"] == "RedHat": + target_changes = {"new": "hold", "old": ""} + elif grains["os_family"] == "Debian": + target_changes = {"new": "hold", "old": "install"} try: - tag = 'pkg_|-{0}_|-{0}_|-installed'.format(target) + tag = "pkg_|-{0}_|-{0}_|-installed".format(target) self.assertSaltTrueReturn(ret) self.assertIn(tag, ret) - self.assertIn('changes', ret[tag]) - self.assertIn(target, ret[tag]['changes']) + self.assertIn("changes", ret[tag]) + self.assertIn(target, ret[tag]["changes"]) if not target_changes: self.skipTest( - 'Test needs to be configured for {}: {}'.format(grains['os'], ret[tag]['changes'][target])) - self.assertEqual(ret[tag]['changes'][target], target_changes) + "Test needs to be configured for {}: {}".format( + grains["os"], ret[tag]["changes"][target] + ) + ) + self.assertEqual(ret[tag]["changes"][target], target_changes) finally: # Clean up, unhold package and remove - self.run_function('pkg.unhold', name=target) - ret = self.run_state('pkg.removed', name=target) + self.run_function("pkg.unhold", name=target) + ret = self.run_state("pkg.removed", name=target) self.assertSaltTrueReturn(ret) if versionlock_pkg: - ret = self.run_state('pkg.removed', name=versionlock_pkg) + ret = self.run_state("pkg.removed", name=versionlock_pkg) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_CAP_TARGETS, 'Capability not provided') - @requires_salt_modules('pkg.version') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf(not _PKG_CAP_TARGETS, "Capability not provided") + @requires_salt_modules("pkg.version") + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_cap_001_installed(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target, realpkg = self._PKG_CAP_TARGETS[0] - version = self.run_function('pkg.version', [target]) - realver = self.run_function('pkg.version', [realpkg]) + version = self.run_function("pkg.version", [target]) + realver = self.run_function("pkg.version", [realpkg]) # If this condition is False, we need to find new targets. # This needs to be able to test successful installation of packages. # These packages need to not be installed before we run the states below if not (version and realver): - self.skipTest('TODO: New pkg cap targets required') + self.skipTest("TODO: New pkg cap targets required") try: - ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True, test=True) - self.assertInSaltComment("The following packages would be installed/updated: {0}".format(realpkg), ret) - ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True) + ret = self.run_state( + "pkg.installed", + name=target, + refresh=False, + resolve_capabilities=True, + test=True, + ) + self.assertInSaltComment( + "The following packages would be installed/updated: {0}".format( + realpkg + ), + ret, + ) + ret = self.run_state( + "pkg.installed", name=target, refresh=False, resolve_capabilities=True + ) self.assertSaltTrueReturn(ret) finally: - ret = self.run_state('pkg.removed', name=realpkg) + ret = self.run_state("pkg.removed", name=realpkg) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_CAP_TARGETS, 'Capability not available') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf(not _PKG_CAP_TARGETS, "Capability not available") + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_cap_002_already_installed(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target, realpkg = self._PKG_CAP_TARGETS[0] - version = self.run_function('pkg.version', [target]) - realver = self.run_function('pkg.version', [realpkg]) + version = self.run_function("pkg.version", [target]) + realver = self.run_function("pkg.version", [realpkg]) # If this condition is False, we need to find new targets. # This needs to be able to test successful installation of packages. # These packages need to not be installed before we run the states below if not (version and realver): - self.skipTest('TODO: New pkg cap targets required') + self.skipTest("TODO: New pkg cap targets required") try: # install the package - ret = self.run_state('pkg.installed', name=realpkg, refresh=False) + ret = self.run_state("pkg.installed", name=realpkg, refresh=False) self.assertSaltTrueReturn(ret) # Try to install again. Nothing should be installed this time. - ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True, test=True) - self.assertInSaltComment("All specified packages are already installed", ret) + ret = self.run_state( + "pkg.installed", + name=target, + refresh=False, + resolve_capabilities=True, + test=True, + ) + self.assertInSaltComment( + "All specified packages are already installed", ret + ) - ret = self.run_state('pkg.installed', name=target, refresh=False, resolve_capabilities=True) + ret = self.run_state( + "pkg.installed", name=target, refresh=False, resolve_capabilities=True + ) self.assertSaltTrueReturn(ret) self.assertInSaltComment("packages are already installed", ret) finally: - ret = self.run_state('pkg.removed', name=realpkg) + ret = self.run_state("pkg.removed", name=realpkg) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_CAP_TARGETS, 'Capability not available') - @skipIf(not _VERSION_SPEC_SUPPORTED, 'Version specification not supported') - @requires_salt_states('pkg.installed', 'pkg.removed') + @skipIf(not _PKG_CAP_TARGETS, "Capability not available") + @skipIf(not _VERSION_SPEC_SUPPORTED, "Version specification not supported") + @requires_salt_states("pkg.installed", "pkg.removed") def test_pkg_cap_003_installed_multipkg_with_version(self): - ''' + """ This is a destructive test as it installs and then removes two packages - ''' + """ target, realpkg = self._PKG_CAP_TARGETS[0] version = self.latest_version(target) realver = self.latest_version(realpkg) @@ -666,121 +671,164 @@ class PkgTest(ModuleCase, SaltReturnAssertsMixin): # This needs to be able to test successful installation of packages. # These packages need to not be installed before we run the states below if not (version and realver): - self.skipTest('TODO: New pkg cap targets required') + self.skipTest("TODO: New pkg cap targets required") cleanup_pkgs = self._PKG_TARGETS try: - pkgs = [{self._PKG_TARGETS[0]: version}, self._PKG_TARGETS[1], {target: realver}] - ret = self.run_state('pkg.installed', - name='test_pkg_cap_003_installed_multipkg_with_version-install', - pkgs=pkgs, - refresh=False) + pkgs = [ + {self._PKG_TARGETS[0]: version}, + self._PKG_TARGETS[1], + {target: realver}, + ] + ret = self.run_state( + "pkg.installed", + name="test_pkg_cap_003_installed_multipkg_with_version-install", + pkgs=pkgs, + refresh=False, + ) self.assertSaltFalseReturn(ret) - ret = self.run_state('pkg.installed', - name='test_pkg_cap_003_installed_multipkg_with_version-install-capability', - pkgs=pkgs, - refresh=False, resolve_capabilities=True, test=True) + ret = self.run_state( + "pkg.installed", + name="test_pkg_cap_003_installed_multipkg_with_version-install-capability", + pkgs=pkgs, + refresh=False, + resolve_capabilities=True, + test=True, + ) self.assertInSaltComment("packages would be installed/updated", ret) self.assertInSaltComment("{0}={1}".format(realpkg, realver), ret) - ret = self.run_state('pkg.installed', - name='test_pkg_cap_003_installed_multipkg_with_version-install-capability', - pkgs=pkgs, - refresh=False, resolve_capabilities=True) + ret = self.run_state( + "pkg.installed", + name="test_pkg_cap_003_installed_multipkg_with_version-install-capability", + pkgs=pkgs, + refresh=False, + resolve_capabilities=True, + ) self.assertSaltTrueReturn(ret) cleanup_pkgs.append(realpkg) finally: - ret = self.run_state('pkg.removed', - name='test_pkg_cap_003_installed_multipkg_with_version-remove', - pkgs=cleanup_pkgs) + ret = self.run_state( + "pkg.removed", + name="test_pkg_cap_003_installed_multipkg_with_version-remove", + pkgs=cleanup_pkgs, + ) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_CAP_TARGETS, 'Capability not available') - @requires_salt_modules('pkg.version') - @requires_salt_states('pkg.latest', 'pkg.removed') + @skipIf(not _PKG_CAP_TARGETS, "Capability not available") + @requires_salt_modules("pkg.version") + @requires_salt_states("pkg.latest", "pkg.removed") def test_pkg_cap_004_latest(self): - ''' + """ This tests pkg.latest with a package that has no epoch (or a zero epoch). - ''' + """ target, realpkg = self._PKG_CAP_TARGETS[0] - version = self.run_function('pkg.version', [target]) - realver = self.run_function('pkg.version', [realpkg]) + version = self.run_function("pkg.version", [target]) + realver = self.run_function("pkg.version", [realpkg]) # If this condition is False, we need to find new targets. # This needs to be able to test successful installation of packages. # These packages need to not be installed before we run the states below if not (version and realver): - self.skipTest('TODO: New pkg cap targets required') + self.skipTest("TODO: New pkg cap targets required") try: - ret = self.run_state('pkg.latest', name=target, refresh=False, resolve_capabilities=True, test=True) - self.assertInSaltComment("The following packages would be installed/upgraded: {0}".format(realpkg), ret) - ret = self.run_state('pkg.latest', name=target, refresh=False, resolve_capabilities=True) + ret = self.run_state( + "pkg.latest", + name=target, + refresh=False, + resolve_capabilities=True, + test=True, + ) + self.assertInSaltComment( + "The following packages would be installed/upgraded: {0}".format( + realpkg + ), + ret, + ) + ret = self.run_state( + "pkg.latest", name=target, refresh=False, resolve_capabilities=True + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('pkg.latest', name=target, refresh=False, resolve_capabilities=True) + ret = self.run_state( + "pkg.latest", name=target, refresh=False, resolve_capabilities=True + ) self.assertSaltTrueReturn(ret) self.assertInSaltComment("is already up-to-date", ret) finally: - ret = self.run_state('pkg.removed', name=realpkg) + ret = self.run_state("pkg.removed", name=realpkg) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_CAP_TARGETS, 'Capability not available') - @requires_salt_modules('pkg.version') - @requires_salt_states('pkg.installed', 'pkg.removed', 'pkg.downloaded') + @skipIf(not _PKG_CAP_TARGETS, "Capability not available") + @requires_salt_modules("pkg.version") + @requires_salt_states("pkg.installed", "pkg.removed", "pkg.downloaded") def test_pkg_cap_005_downloaded(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target, realpkg = self._PKG_CAP_TARGETS[0] - version = self.run_function('pkg.version', [target]) - realver = self.run_function('pkg.version', [realpkg]) + version = self.run_function("pkg.version", [target]) + realver = self.run_function("pkg.version", [realpkg]) # If this condition is False, we need to find new targets. # This needs to be able to test successful installation of packages. # These packages need to not be installed before we run the states below if not (version and realver): - self.skipTest('TODO: New pkg cap targets required') + self.skipTest("TODO: New pkg cap targets required") - ret = self.run_state('pkg.downloaded', name=target, refresh=False) + ret = self.run_state("pkg.downloaded", name=target, refresh=False) self.assertSaltFalseReturn(ret) - ret = self.run_state('pkg.downloaded', name=target, refresh=False, resolve_capabilities=True, test=True) - self.assertInSaltComment("The following packages would be downloaded: {0}".format(realpkg), ret) + ret = self.run_state( + "pkg.downloaded", + name=target, + refresh=False, + resolve_capabilities=True, + test=True, + ) + self.assertInSaltComment( + "The following packages would be downloaded: {0}".format(realpkg), ret + ) - ret = self.run_state('pkg.downloaded', name=target, refresh=False, resolve_capabilities=True) + ret = self.run_state( + "pkg.downloaded", name=target, refresh=False, resolve_capabilities=True + ) self.assertSaltTrueReturn(ret) - @skipIf(not _PKG_CAP_TARGETS, 'Capability not available') - @requires_salt_modules('pkg.version') - @requires_salt_states('pkg.installed', 'pkg.removed', 'pkg.uptodate') + @skipIf(not _PKG_CAP_TARGETS, "Capability not available") + @requires_salt_modules("pkg.version") + @requires_salt_states("pkg.installed", "pkg.removed", "pkg.uptodate") def test_pkg_cap_006_uptodate(self): - ''' + """ This is a destructive test as it installs and then removes a package - ''' + """ target, realpkg = self._PKG_CAP_TARGETS[0] - version = self.run_function('pkg.version', [target]) - realver = self.run_function('pkg.version', [realpkg]) + version = self.run_function("pkg.version", [target]) + realver = self.run_function("pkg.version", [realpkg]) # If this condition is False, we need to find new targets. # This needs to be able to test successful installation of packages. # These packages need to not be installed before we run the states below if not (version and realver): - self.skipTest('TODO: New pkg cap targets required') + self.skipTest("TODO: New pkg cap targets required") try: - ret = self.run_state('pkg.installed', name=target, - refresh=False, resolve_capabilities=True) + ret = self.run_state( + "pkg.installed", name=target, refresh=False, resolve_capabilities=True + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('pkg.uptodate', - name='test_pkg_cap_006_uptodate', - pkgs=[target], - refresh=False, - resolve_capabilities=True) + ret = self.run_state( + "pkg.uptodate", + name="test_pkg_cap_006_uptodate", + pkgs=[target], + refresh=False, + resolve_capabilities=True, + ) self.assertSaltTrueReturn(ret) self.assertInSaltComment("System is already up-to-date", ret) finally: - ret = self.run_state('pkg.removed', name=realpkg) + ret = self.run_state("pkg.removed", name=realpkg) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_pkgrepo.py b/tests/integration/states/test_pkgrepo.py index 07c247b462f..ac6cd515b23 100644 --- a/tests/integration/states/test_pkgrepo.py +++ b/tests/integration/states/test_pkgrepo.py @@ -1,57 +1,58 @@ # -*- coding: utf-8 -*- -''' +""" tests for pkgrepo states -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os +import salt.utils.files + +# Import Salt libs +import salt.utils.platform + +# Import 3rd-party libs +from salt.ext import six + # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.unit import skipIf from tests.support.helpers import ( destructiveTest, requires_salt_modules, requires_salt_states, requires_system_grains, ) - -# Import Salt libs -import salt.utils.platform -import salt.utils.files - -# Import 3rd-party libs -from salt.ext import six +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf @destructiveTest -@skipIf(salt.utils.platform.is_windows(), 'minion is windows') +@skipIf(salt.utils.platform.is_windows(), "minion is windows") class PkgrepoTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ pkgrepo state tests - ''' - @requires_salt_modules('pkgrepo.managed') + """ + + @requires_salt_modules("pkgrepo.managed") @requires_system_grains def test_pkgrepo_01_managed(self, grains): - ''' + """ Test adding a repo - ''' - if grains['os'] == 'Ubuntu' and grains['osrelease_info'] >= (15, 10): + """ + if grains["os"] == "Ubuntu" and grains["osrelease_info"] >= (15, 10): self.skipTest( - 'The PPA used for this test does not exist for Ubuntu Wily' - ' (15.10) and later.' + "The PPA used for this test does not exist for Ubuntu Wily" + " (15.10) and later." ) - if grains['os_family'] == 'Debian': + if grains["os_family"] == "Debian": try: from aptsources import sourceslist # pylint: disable=unused-import except ImportError: - self.skipTest( - 'aptsources.sourceslist python module not found' - ) - ret = self.run_function('state.sls', mods='pkgrepo.managed', timeout=120) + self.skipTest("aptsources.sourceslist python module not found") + ret = self.run_function("state.sls", mods="pkgrepo.managed", timeout=120) # If the below assert fails then no states were run, and the SLS in # tests/integration/files/file/base/pkgrepo/managed.sls needs to be # corrected. @@ -59,19 +60,19 @@ class PkgrepoTest(ModuleCase, SaltReturnAssertsMixin): for state_id, state_result in six.iteritems(ret): self.assertSaltTrueReturn(dict([(state_id, state_result)])) - @requires_salt_modules('pkgrepo.absent') + @requires_salt_modules("pkgrepo.absent") @requires_system_grains def test_pkgrepo_02_absent(self, grains): - ''' + """ Test removing the repo from the above test - ''' - if grains['os'] == 'Ubuntu' and grains['osrelease_info'] >= (15, 10): + """ + if grains["os"] == "Ubuntu" and grains["osrelease_info"] >= (15, 10): self.skipTest( - 'The PPA used for this test does not exist for Ubuntu Wily' - ' (15.10) and later.' + "The PPA used for this test does not exist for Ubuntu Wily" + " (15.10) and later." ) - ret = self.run_function('state.sls', mods='pkgrepo.absent', timeout=120) + ret = self.run_function("state.sls", mods="pkgrepo.absent", timeout=120) # If the below assert fails then no states were run, and the SLS in # tests/integration/files/file/base/pkgrepo/absent.sls needs to be # corrected. @@ -79,186 +80,184 @@ class PkgrepoTest(ModuleCase, SaltReturnAssertsMixin): for state_id, state_result in six.iteritems(ret): self.assertSaltTrueReturn(dict([(state_id, state_result)])) - @requires_salt_states('pkgrepo.absent', 'pkgrepo.managed') + @requires_salt_states("pkgrepo.absent", "pkgrepo.managed") @requires_system_grains def test_pkgrepo_03_with_comments(self, grains): - ''' + """ Test adding a repo with comments - ''' + """ kwargs = {} - if grains['os_family'] == 'RedHat': + if grains["os_family"] == "RedHat": kwargs = { - 'name': 'examplerepo', - 'baseurl': 'http://example.com/repo', - 'enabled': False, - 'comments': ['This is a comment'] + "name": "examplerepo", + "baseurl": "http://example.com/repo", + "enabled": False, + "comments": ["This is a comment"], } else: - self.skipTest('{}/{} test case needed'.format(grains['os_family'], grains['os'])) + self.skipTest( + "{}/{} test case needed".format(grains["os_family"], grains["os"]) + ) try: # Run the state to add the repo - ret = self.run_state('pkgrepo.managed', **kwargs) + ret = self.run_state("pkgrepo.managed", **kwargs) self.assertSaltTrueReturn(ret) # Run again with modified comments - kwargs['comments'].append('This is another comment') - ret = self.run_state('pkgrepo.managed', **kwargs) + kwargs["comments"].append("This is another comment") + ret = self.run_state("pkgrepo.managed", **kwargs) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] self.assertEqual( - ret['changes'], + ret["changes"], { - 'comments': { - 'old': ['This is a comment'], - 'new': ['This is a comment', - 'This is another comment'] + "comments": { + "old": ["This is a comment"], + "new": ["This is a comment", "This is another comment"], } - } + }, ) # Run a third time, no changes should be made - ret = self.run_state('pkgrepo.managed', **kwargs) + ret = self.run_state("pkgrepo.managed", **kwargs) self.assertSaltTrueReturn(ret) ret = ret[next(iter(ret))] - self.assertFalse(ret['changes']) + self.assertFalse(ret["changes"]) self.assertEqual( - ret['comment'], - "Package repo '{0}' already configured".format(kwargs['name']) + ret["comment"], + "Package repo '{0}' already configured".format(kwargs["name"]), ) finally: # Clean up - self.run_state('pkgrepo.absent', name=kwargs['name']) + self.run_state("pkgrepo.absent", name=kwargs["name"]) - @requires_salt_states('pkgrepo.managed') + @requires_salt_states("pkgrepo.managed") @requires_system_grains def test_pkgrepo_04_apt_with_architectures(self, grains): - ''' + """ Test managing a repo with architectures specified - ''' - if grains['os_family'].lower() != 'debian': - self.skipTest('APT-only test') + """ + if grains["os_family"].lower() != "debian": + self.skipTest("APT-only test") - name = 'deb {{arch}}http://foo.com/bar/latest {oscodename} main'.format(oscodename=grains['oscodename']) + name = "deb {{arch}}http://foo.com/bar/latest {oscodename} main".format( + oscodename=grains["oscodename"] + ) def _get_arch(arch): - return '[arch={0}] '.format(arch) if arch else '' + return "[arch={0}] ".format(arch) if arch else "" - def _run(arch='', test=False): + def _run(arch="", test=False): ret = self.run_state( - 'pkgrepo.managed', + "pkgrepo.managed", name=name.format(arch=_get_arch(arch)), file=fn_, refresh=False, - test=test) + test=test, + ) return ret[next(iter(ret))] - fn_ = salt.utils.files.mkstemp(dir='/etc/apt/sources.list.d', suffix='.list') + fn_ = salt.utils.files.mkstemp(dir="/etc/apt/sources.list.d", suffix=".list") try: # Run with test=True ret = _run(test=True) - assert ret['changes'] == {'repo': name.format(arch='')}, ret['changes'] - assert 'would be' in ret['comment'], ret['comment'] - assert ret['result'] is None, ret['result'] + assert ret["changes"] == {"repo": name.format(arch="")}, ret["changes"] + assert "would be" in ret["comment"], ret["comment"] + assert ret["result"] is None, ret["result"] # Run for real ret = _run() - assert ret['changes'] == {'repo': name.format(arch='')}, ret['changes'] - assert ret['comment'].startswith('Configured'), ret['comment'] - assert ret['result'] is True, ret['result'] + assert ret["changes"] == {"repo": name.format(arch="")}, ret["changes"] + assert ret["comment"].startswith("Configured"), ret["comment"] + assert ret["result"] is True, ret["result"] # Run again with test=True, should exit with no changes and a True # result. ret = _run(test=True) - assert not ret['changes'], ret['changes'] - assert 'already' in ret['comment'], ret['comment'] - assert ret['result'] is True, ret['result'] + assert not ret["changes"], ret["changes"] + assert "already" in ret["comment"], ret["comment"] + assert ret["result"] is True, ret["result"] # Run for real again, results should be the same as above (i.e. we # should never get to the point where we exit with a None result). ret = _run() - assert not ret['changes'], ret['changes'] - assert 'already' in ret['comment'], ret['comment'] - assert ret['result'] is True, ret['result'] + assert not ret["changes"], ret["changes"] + assert "already" in ret["comment"], ret["comment"] + assert ret["result"] is True, ret["result"] expected_changes = { - 'line': { - 'new': name.format(arch=_get_arch('amd64')), - 'old': name.format(arch=''), - }, - 'architectures': { - 'new': ['amd64'], - 'old': [], + "line": { + "new": name.format(arch=_get_arch("amd64")), + "old": name.format(arch=""), }, + "architectures": {"new": ["amd64"], "old": []}, } # Run with test=True and the architecture set. We should get a None # result with some expected changes. - ret = _run(arch='amd64', test=True) - assert ret['changes'] == expected_changes, ret['changes'] - assert 'would be' in ret['comment'], ret['comment'] - assert ret['result'] is None, ret['result'] + ret = _run(arch="amd64", test=True) + assert ret["changes"] == expected_changes, ret["changes"] + assert "would be" in ret["comment"], ret["comment"] + assert ret["result"] is None, ret["result"] # Run for real, with the architecture set. We should get a True # result with the same changes. - ret = _run(arch='amd64') - assert ret['changes'] == expected_changes, ret['changes'] - assert ret['comment'].startswith('Configured'), ret['comment'] - assert ret['result'] is True, ret['result'] + ret = _run(arch="amd64") + assert ret["changes"] == expected_changes, ret["changes"] + assert ret["comment"].startswith("Configured"), ret["comment"] + assert ret["result"] is True, ret["result"] # Run again with test=True, should exit with no changes and a True # result. - ret = _run(arch='amd64', test=True) - assert not ret['changes'], ret['changes'] - assert 'already' in ret['comment'], ret['comment'] - assert ret['result'] is True, ret['result'] + ret = _run(arch="amd64", test=True) + assert not ret["changes"], ret["changes"] + assert "already" in ret["comment"], ret["comment"] + assert ret["result"] is True, ret["result"] # Run for real again, results should be the same as above (i.e. we # should never get to the point where we exit with a None result). - ret = _run(arch='amd64') - assert not ret['changes'], ret['changes'] - assert 'already' in ret['comment'], ret['comment'] - assert ret['result'] is True, ret['result'] + ret = _run(arch="amd64") + assert not ret["changes"], ret["changes"] + assert "already" in ret["comment"], ret["comment"] + assert ret["result"] is True, ret["result"] expected_changes = { - 'line': { - 'new': name.format(arch=''), - 'old': name.format(arch=_get_arch('amd64')), - }, - 'architectures': { - 'new': [], - 'old': ['amd64'], + "line": { + "new": name.format(arch=""), + "old": name.format(arch=_get_arch("amd64")), }, + "architectures": {"new": [], "old": ["amd64"]}, } # Run with test=True and the architecture set back to the original # value. We should get a None result with some expected changes. ret = _run(test=True) - assert ret['changes'] == expected_changes, ret['changes'] - assert 'would be' in ret['comment'], ret['comment'] - assert ret['result'] is None, ret['result'] + assert ret["changes"] == expected_changes, ret["changes"] + assert "would be" in ret["comment"], ret["comment"] + assert ret["result"] is None, ret["result"] # Run for real, with the architecture set. We should get a True # result with the same changes. ret = _run() - assert ret['changes'] == expected_changes, ret['changes'] - assert ret['comment'].startswith('Configured'), ret['comment'] - assert ret['result'] is True, ret['result'] + assert ret["changes"] == expected_changes, ret["changes"] + assert ret["comment"].startswith("Configured"), ret["comment"] + assert ret["result"] is True, ret["result"] # Run again with test=True, should exit with no changes and a True # result. ret = _run(test=True) - assert not ret['changes'], ret['changes'] - assert 'already' in ret['comment'], ret['comment'] - assert ret['result'] is True, ret['result'] + assert not ret["changes"], ret["changes"] + assert "already" in ret["comment"], ret["comment"] + assert ret["result"] is True, ret["result"] # Run for real again, results should be the same as above (i.e. we # should never get to the point where we exit with a None result). ret = _run() - assert not ret['changes'], ret['changes'] - assert 'already' in ret['comment'], ret['comment'] - assert ret['result'] is True, ret['result'] + assert not ret["changes"], ret["changes"] + assert "already" in ret["comment"], ret["comment"] + assert ret["result"] is True, ret["result"] finally: try: os.remove(fn_) diff --git a/tests/integration/states/test_rabbitmq_user.py b/tests/integration/states/test_rabbitmq_user.py index c54b6d80e7c..a0615c09175 100644 --- a/tests/integration/states/test_rabbitmq_user.py +++ b/tests/integration/states/test_rabbitmq_user.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the rabbitmq state -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs from tests.support.case import ModuleCase @@ -13,31 +13,28 @@ from tests.support.mixins import SaltReturnAssertsMixin @skip_if_not_root class RabbitUserTestCase(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the rabbitmq user states. - ''' + """ + def setUp(self): super(RabbitUserTestCase, self).setUp() - rabbit_installed = self.run_function('cmd.has_exec', ['rabbitmqctl']) + rabbit_installed = self.run_function("cmd.has_exec", ["rabbitmqctl"]) if not rabbit_installed: - self.skipTest('rabbitmq-server not installed') + self.skipTest("rabbitmq-server not installed") def test_present(self): - ''' + """ rabbitmq_user.present null_name - ''' - ret = self.run_state( - 'rabbitmq_user.present', name='null_name', test=True - ) + """ + ret = self.run_state("rabbitmq_user.present", name="null_name", test=True) self.assertSaltFalseReturn(ret) - self.assertInSaltComment('User \'null_name\' is set to be created', ret) + self.assertInSaltComment("User 'null_name' is set to be created", ret) def absent(self): - ''' + """ rabbitmq_user.absent null_name - ''' - ret = self.run_state( - 'rabbitmq_user.absent', name='null_name', test=True - ) + """ + ret = self.run_state("rabbitmq_user.absent", name="null_name", test=True) self.assertSaltFalseReturn(ret) diff --git a/tests/integration/states/test_rabbitmq_vhost.py b/tests/integration/states/test_rabbitmq_vhost.py index 7e63e1e32c2..c4a005d66a8 100644 --- a/tests/integration/states/test_rabbitmq_vhost.py +++ b/tests/integration/states/test_rabbitmq_vhost.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the rabbitmq state -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs from tests.support.case import ModuleCase @@ -14,30 +14,27 @@ from tests.support.mixins import SaltReturnAssertsMixin @skip_if_not_root class RabbitVHostTestCase(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the rabbitmq virtual host states. - ''' + """ + def setUp(self): super(RabbitVHostTestCase, self).setUp() - rabbit_installed = self.run_function('cmd.has_exec', ['rabbitmqctl']) + rabbit_installed = self.run_function("cmd.has_exec", ["rabbitmqctl"]) if not rabbit_installed: - self.skipTest('rabbitmq-server not installed') + self.skipTest("rabbitmq-server not installed") def test_present(self): - ''' + """ rabbitmq_vhost.present null_host - ''' - ret = self.run_state( - 'rabbitmq_vhost.present', name='null_host', test=True - ) + """ + ret = self.run_state("rabbitmq_vhost.present", name="null_host", test=True) self.assertSaltFalseReturn(ret) def absent(self): - ''' + """ rabbitmq_vhost.absent null_host - ''' - ret = self.run_state( - 'rabbitmq_vhost.absent', name='null_host', test=True - ) + """ + ret = self.run_state("rabbitmq_vhost.absent", name="null_host", test=True) self.assertSaltFalseReturn(ret) diff --git a/tests/integration/states/test_reg.py b/tests/integration/states/test_reg.py index 28789cfae3b..45fb83e48d2 100644 --- a/tests/integration/states/test_reg.py +++ b/tests/integration/states/test_reg.py @@ -1,283 +1,316 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the Reg State -''' -# Import Python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, generate_random_name - -# Import Salt libs +import pytest import salt.utils.platform import salt.utils.win_reg as reg +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, generate_random_name +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf log = logging.getLogger(__name__) -__testcontext__ = {} - -UNICODE_VALUE_NAME = 'Unicode Key \N{TRADE MARK SIGN}' -UNICODE_VALUE = 'Unicode Value ' \ - '\N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN}' -FAKE_KEY = 'SOFTWARE\\{0}'.format(generate_random_name('SaltTesting-')) +UNICODE_VALUE_NAME = "Unicode Key \N{TRADE MARK SIGN}" +UNICODE_VALUE = ( + "Unicode Value " "\N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN}" +) +FAKE_KEY = "SOFTWARE\\{0}".format(generate_random_name("SaltTesting-")) @destructiveTest -@skipIf(not salt.utils.platform.is_windows(), 'Windows Specific Test') +@skipIf(not salt.utils.platform.is_windows(), "Windows Specific Test") +@pytest.mark.windows_whitelisted class RegTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Reg state module tests These tests are destructive as the modify the registry - ''' + """ + def tearDown(self): - reg.delete_key_recursive(hive='HKLM', - key=FAKE_KEY) - reg.delete_key_recursive(hive='HKLM', - key=FAKE_KEY, - use_32bit_registry=True) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY, use_32bit_registry=True) def test_present_reg_sz(self): - ''' + """ Testing reg.present with REG_SZ - ''' - log.debug('Testing reg.present with REG_SZ') + """ + log.debug("Testing reg.present with REG_SZ") # default type is 'REG_SZ' # Does the state return the correct data - ret = self.run_state('reg.present', - name='HKLM\\{0}'.format(FAKE_KEY), - vname='test_reg_sz', - vdata='fake string data') + ret = self.run_state( + "reg.present", + name="HKLM\\{0}".format(FAKE_KEY), + vname="test_reg_sz", + vdata="fake string data", + ) expected = { - 'reg': { - 'Added': { - 'Entry': 'test_reg_sz', - 'Inheritance': True, - 'Key': 'HKLM\\{0}'.format(FAKE_KEY), - 'Owner': None, - 'Perms': {'Deny': None, 'Grant': None}, - 'Value': 'fake string data'}}} + "reg": { + "Added": { + "Entry": "test_reg_sz", + "Inheritance": True, + "Key": "HKLM\\{0}".format(FAKE_KEY), + "Owner": None, + "Perms": {"Deny": None, "Grant": None}, + "Value": "fake string data", + } + } + } self.assertSaltStateChangesEqual(ret, expected) # Is the value actually set - ret = reg.read_value(hive='HKLM', key=FAKE_KEY, vname='test_reg_sz') + ret = reg.read_value(hive="HKLM", key=FAKE_KEY, vname="test_reg_sz") expected = { - 'vtype': 'REG_SZ', - 'vname': 'test_reg_sz', - 'success': True, - 'hive': 'HKLM', - 'vdata': 'fake string data', - 'key': FAKE_KEY} + "vtype": "REG_SZ", + "vname": "test_reg_sz", + "success": True, + "hive": "HKLM", + "vdata": "fake string data", + "key": FAKE_KEY, + } self.assertEqual(ret, expected) def test_present_reg_sz_unicode_value(self): - ''' + """ Testing reg.present with REG_SZ and a unicode value - ''' - log.debug('Testing reg.present with REG_SZ and a unicode value') + """ + log.debug("Testing reg.present with REG_SZ and a unicode value") # default type is 'REG_SZ' # Does the state return the correct data - ret = self.run_state('reg.present', - name='HKLM\\{0}'.format(FAKE_KEY), - vname='test_reg_sz', - vdata=UNICODE_VALUE) + ret = self.run_state( + "reg.present", + name="HKLM\\{0}".format(FAKE_KEY), + vname="test_reg_sz", + vdata=UNICODE_VALUE, + ) expected = { - 'reg': { - 'Added': { - 'Entry': 'test_reg_sz', - 'Inheritance': True, - 'Key': 'HKLM\\{0}'.format(FAKE_KEY), - 'Owner': None, - 'Perms': {'Deny': None, 'Grant': None}, - 'Value': UNICODE_VALUE}}} + "reg": { + "Added": { + "Entry": "test_reg_sz", + "Inheritance": True, + "Key": "HKLM\\{0}".format(FAKE_KEY), + "Owner": None, + "Perms": {"Deny": None, "Grant": None}, + "Value": UNICODE_VALUE, + } + } + } self.assertSaltStateChangesEqual(ret, expected) # Is the value actually set - ret = reg.read_value(hive='HKLM', key=FAKE_KEY, vname='test_reg_sz') + ret = reg.read_value(hive="HKLM", key=FAKE_KEY, vname="test_reg_sz") expected = { - 'vtype': 'REG_SZ', - 'vname': 'test_reg_sz', - 'success': True, - 'hive': 'HKLM', - 'vdata': UNICODE_VALUE, - 'key': FAKE_KEY} + "vtype": "REG_SZ", + "vname": "test_reg_sz", + "success": True, + "hive": "HKLM", + "vdata": UNICODE_VALUE, + "key": FAKE_KEY, + } self.assertEqual(ret, expected) def test_present_reg_sz_unicode_default_value(self): - ''' + """ Testing reg.present with REG_SZ and a unicode default value - ''' - log.debug('Testing reg.present with REG_SZ and a unicode default value') + """ + log.debug("Testing reg.present with REG_SZ and a unicode default value") # default type is 'REG_SZ' # Does the state return the correct data - ret = self.run_state('reg.present', - name='HKLM\\{0}'.format(FAKE_KEY), - vdata=UNICODE_VALUE) + ret = self.run_state( + "reg.present", name="HKLM\\{0}".format(FAKE_KEY), vdata=UNICODE_VALUE + ) expected = { - 'reg': { - 'Added': { - 'Entry': '(Default)', - 'Inheritance': True, - 'Key': 'HKLM\\{0}'.format(FAKE_KEY), - 'Owner': None, - 'Perms': {'Deny': None, 'Grant': None}, - 'Value': UNICODE_VALUE}}} + "reg": { + "Added": { + "Entry": "(Default)", + "Inheritance": True, + "Key": "HKLM\\{0}".format(FAKE_KEY), + "Owner": None, + "Perms": {"Deny": None, "Grant": None}, + "Value": UNICODE_VALUE, + } + } + } self.assertSaltStateChangesEqual(ret, expected) # Is the value actually set - ret = reg.read_value(hive='HKLM', key=FAKE_KEY) + ret = reg.read_value(hive="HKLM", key=FAKE_KEY) expected = { - 'vtype': 'REG_SZ', - 'vname': '(Default)', - 'success': True, - 'hive': 'HKLM', - 'vdata': UNICODE_VALUE, - 'key': FAKE_KEY} + "vtype": "REG_SZ", + "vname": "(Default)", + "success": True, + "hive": "HKLM", + "vdata": UNICODE_VALUE, + "key": FAKE_KEY, + } self.assertEqual(ret, expected) def test_present_reg_sz_unicode_value_name(self): - ''' + """ Testing reg.present with REG_SZ and a unicode value name - ''' - log.debug('Testing reg.present with REG_SZ and a unicode value name') + """ + log.debug("Testing reg.present with REG_SZ and a unicode value name") # default type is 'REG_SZ' # Does the state return the correct data - ret = self.run_state('reg.present', - name='HKLM\\{0}'.format(FAKE_KEY), - vname=UNICODE_VALUE_NAME, - vdata='fake string data') + ret = self.run_state( + "reg.present", + name="HKLM\\{0}".format(FAKE_KEY), + vname=UNICODE_VALUE_NAME, + vdata="fake string data", + ) expected = { - 'reg': { - 'Added': { - 'Entry': UNICODE_VALUE_NAME, - 'Inheritance': True, - 'Key': 'HKLM\\{0}'.format(FAKE_KEY), - 'Owner': None, - 'Perms': {'Deny': None, 'Grant': None}, - 'Value': 'fake string data'}}} + "reg": { + "Added": { + "Entry": UNICODE_VALUE_NAME, + "Inheritance": True, + "Key": "HKLM\\{0}".format(FAKE_KEY), + "Owner": None, + "Perms": {"Deny": None, "Grant": None}, + "Value": "fake string data", + } + } + } self.assertSaltStateChangesEqual(ret, expected) # Is the value actually set - ret = reg.read_value(hive='HKLM', key=FAKE_KEY, vname=UNICODE_VALUE_NAME) + ret = reg.read_value(hive="HKLM", key=FAKE_KEY, vname=UNICODE_VALUE_NAME) expected = { - 'vtype': 'REG_SZ', - 'vname': UNICODE_VALUE_NAME, - 'success': True, - 'hive': 'HKLM', - 'vdata': 'fake string data', - 'key': FAKE_KEY} + "vtype": "REG_SZ", + "vname": UNICODE_VALUE_NAME, + "success": True, + "hive": "HKLM", + "vdata": "fake string data", + "key": FAKE_KEY, + } self.assertEqual(ret, expected) def test_present_reg_binary(self): - ''' + """ Testing reg.present with REG_BINARY - ''' - test_data = 'Salty Test' - log.debug('Testing reg.present with REG_BINARY') + """ + test_data = "Salty Test" + log.debug("Testing reg.present with REG_BINARY") # default type is 'REG_SZ' # Does the state return the correct data - ret = self.run_state('reg.present', - name='HKLM\\{0}'.format(FAKE_KEY), - vname='test_reg_binary', - vtype='REG_BINARY', - vdata=test_data) + ret = self.run_state( + "reg.present", + name="HKLM\\{0}".format(FAKE_KEY), + vname="test_reg_binary", + vtype="REG_BINARY", + vdata=test_data, + ) expected = { - 'reg': { - 'Added': { - 'Entry': 'test_reg_binary', - 'Inheritance': True, - 'Key': 'HKLM\\{0}'.format(FAKE_KEY), - 'Owner': None, - 'Perms': {'Deny': None, 'Grant': None}, - 'Value': test_data}}} + "reg": { + "Added": { + "Entry": "test_reg_binary", + "Inheritance": True, + "Key": "HKLM\\{0}".format(FAKE_KEY), + "Owner": None, + "Perms": {"Deny": None, "Grant": None}, + "Value": test_data, + } + } + } self.assertSaltStateChangesEqual(ret, expected) # Is the value actually set - ret = reg.read_value(hive='HKLM', key=FAKE_KEY, vname='test_reg_binary') + ret = reg.read_value(hive="HKLM", key=FAKE_KEY, vname="test_reg_binary") expected = { - 'vtype': 'REG_BINARY', - 'vname': 'test_reg_binary', - 'success': True, - 'hive': 'HKLM', - 'vdata': test_data.encode('utf-8'), - 'key': FAKE_KEY} + "vtype": "REG_BINARY", + "vname": "test_reg_binary", + "success": True, + "hive": "HKLM", + "vdata": test_data.encode("utf-8"), + "key": FAKE_KEY, + } self.assertEqual(ret, expected) def test_present_reg_multi_sz(self): - ''' + """ Testing reg.present with REG_MULTI_SZ - ''' - log.debug('Testing reg.present with REG_MULTI_SZ') + """ + log.debug("Testing reg.present with REG_MULTI_SZ") # default type is 'REG_SZ' # Does the state return the correct data - ret = self.run_state('reg.present', - name='HKLM\\{0}'.format(FAKE_KEY), - vname='test_reg_multi_sz', - vtype='REG_MULTI_SZ', - vdata=['item1', 'item2']) + ret = self.run_state( + "reg.present", + name="HKLM\\{0}".format(FAKE_KEY), + vname="test_reg_multi_sz", + vtype="REG_MULTI_SZ", + vdata=["item1", "item2"], + ) expected = { - 'reg': { - 'Added': { - 'Entry': 'test_reg_multi_sz', - 'Inheritance': True, - 'Key': 'HKLM\\{0}'.format(FAKE_KEY), - 'Owner': None, - 'Perms': {'Deny': None, 'Grant': None}, - 'Value': ['item1', 'item2']}}} + "reg": { + "Added": { + "Entry": "test_reg_multi_sz", + "Inheritance": True, + "Key": "HKLM\\{0}".format(FAKE_KEY), + "Owner": None, + "Perms": {"Deny": None, "Grant": None}, + "Value": ["item1", "item2"], + } + } + } self.assertSaltStateChangesEqual(ret, expected) # Is the value actually set - ret = reg.read_value(hive='HKLM', - key=FAKE_KEY, - vname='test_reg_multi_sz') + ret = reg.read_value(hive="HKLM", key=FAKE_KEY, vname="test_reg_multi_sz") expected = { - 'vtype': 'REG_MULTI_SZ', - 'vname': 'test_reg_multi_sz', - 'success': True, - 'hive': 'HKLM', - 'vdata': ['item1', 'item2'], - 'key': FAKE_KEY} + "vtype": "REG_MULTI_SZ", + "vname": "test_reg_multi_sz", + "success": True, + "hive": "HKLM", + "vdata": ["item1", "item2"], + "key": FAKE_KEY, + } self.assertEqual(ret, expected) def test_present_32_bit(self): - ''' + """ Testing reg.present with REG_SZ using 32bit registry - ''' - log.debug('Testing reg.present with REG_SZ using 32bit registry') + """ + log.debug("Testing reg.present with REG_SZ using 32bit registry") # default type is 'REG_SZ' # Does the state return the correct data - ret = self.run_state('reg.present', - name='HKLM\\{0}'.format(FAKE_KEY), - vname='test_reg_sz', - vdata='fake string data', - use_32bit_registry=True) + ret = self.run_state( + "reg.present", + name="HKLM\\{0}".format(FAKE_KEY), + vname="test_reg_sz", + vdata="fake string data", + use_32bit_registry=True, + ) expected = { - 'reg': { - 'Added': { - 'Entry': 'test_reg_sz', - 'Inheritance': True, - 'Key': 'HKLM\\{0}'.format(FAKE_KEY), - 'Owner': None, - 'Perms': {'Deny': None, 'Grant': None}, - 'Value': 'fake string data'}}} + "reg": { + "Added": { + "Entry": "test_reg_sz", + "Inheritance": True, + "Key": "HKLM\\{0}".format(FAKE_KEY), + "Owner": None, + "Perms": {"Deny": None, "Grant": None}, + "Value": "fake string data", + } + } + } self.assertSaltStateChangesEqual(ret, expected) # Is the value actually set - ret = reg.read_value(hive='HKLM', - key=FAKE_KEY, - vname='test_reg_sz', - use_32bit_registry=True) + ret = reg.read_value( + hive="HKLM", key=FAKE_KEY, vname="test_reg_sz", use_32bit_registry=True + ) expected = { - 'vtype': 'REG_SZ', - 'vname': 'test_reg_sz', - 'success': True, - 'hive': 'HKLM', - 'vdata': 'fake string data', - 'key': FAKE_KEY} + "vtype": "REG_SZ", + "vname": "test_reg_sz", + "success": True, + "hive": "HKLM", + "vdata": "fake string data", + "key": FAKE_KEY, + } self.assertEqual(ret, expected) diff --git a/tests/integration/states/test_renderers.py b/tests/integration/states/test_renderers.py index 423c880ba4f..e8dcc186313 100644 --- a/tests/integration/states/test_renderers.py +++ b/tests/integration/states/test_renderers.py @@ -1,42 +1,41 @@ # coding: utf-8 -''' +""" Integration tests for renderer functions -''' +""" -# Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs +import pytest +import salt.utils.platform +from salt.ext import six from tests.support.case import ModuleCase from tests.support.helpers import flaky from tests.support.unit import skipIf -# Import Salt libs -import salt.utils.platform - -# Import 3rd-party libs -from salt.ext import six - +@pytest.mark.windows_whitelisted class TestJinjaRenderer(ModuleCase): - ''' + """ Validate that ordering works correctly - ''' + """ + def test_dot_notation(self): - ''' + """ Test the Jinja dot-notation syntax for calling execution modules - ''' - ret = self.run_function('state.sls', ['jinja_dot_notation']) + """ + ret = self.run_function("state.sls", ["jinja_dot_notation"]) for state_ret in ret.values(): - self.assertTrue(state_ret['result']) + self.assertTrue(state_ret["result"]) @flaky - @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2') + @skipIf( + salt.utils.platform.is_darwin() and six.PY2, "This test hangs on OS X on Py2" + ) def test_salt_contains_function(self): - ''' + """ Test if we are able to check if a function exists inside the "salt" wrapper (AliasLoader) which is available on Jinja templates. - ''' - ret = self.run_function('state.sls', ['jinja_salt_contains_function']) + """ + ret = self.run_function("state.sls", ["jinja_salt_contains_function"]) for state_ret in ret.values(): - self.assertTrue(state_ret['result']) + self.assertTrue(state_ret["result"]) diff --git a/tests/integration/states/test_service.py b/tests/integration/states/test_service.py index d8664becb5d..77174bb23a7 100644 --- a/tests/integration/states/test_service.py +++ b/tests/integration/states/test_service.py @@ -1,117 +1,118 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the service state -''' -# Import python libs +""" from __future__ import absolute_import, print_function, unicode_literals + import re -# Import Salt Testing libs +import pytest +import salt.utils.path +import salt.utils.platform from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest from tests.support.mixins import SaltReturnAssertsMixin -# Import salt libs -import salt.utils.path -import salt.utils.platform - INIT_DELAY = 5 @destructiveTest +@pytest.mark.windows_whitelisted class ServiceTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the service state - ''' + """ + def setUp(self): - self.service_name = 'cron' - cmd_name = 'crontab' - os_family = self.run_function('grains.get', ['os_family']) - os_release = self.run_function('grains.get', ['osrelease']) + self.service_name = "cron" + cmd_name = "crontab" + os_family = self.run_function("grains.get", ["os_family"]) + os_release = self.run_function("grains.get", ["osrelease"]) self.stopped = False self.running = True - if os_family == 'RedHat': - self.service_name = 'crond' - elif os_family == 'Arch': - self.service_name = 'sshd' - cmd_name = 'systemctl' - elif os_family == 'MacOS': - self.service_name = 'org.ntp.ntpd' - if int(os_release.split('.')[1]) >= 13: - self.service_name = 'com.apple.AirPlayXPCHelper' - self.stopped = '' - self.running = '[0-9]' - elif os_family == 'Windows': - self.service_name = 'Spooler' + if os_family == "RedHat": + self.service_name = "crond" + elif os_family == "Arch": + self.service_name = "sshd" + cmd_name = "systemctl" + elif os_family == "MacOS": + self.service_name = "org.ntp.ntpd" + if int(os_release.split(".")[1]) >= 13: + self.service_name = "com.apple.AirPlayXPCHelper" + self.stopped = "" + self.running = "[0-9]" + elif os_family == "Windows": + self.service_name = "Spooler" - self.pre_srv_enabled = True if self.service_name in self.run_function('service.get_enabled') else False + self.pre_srv_enabled = ( + True + if self.service_name in self.run_function("service.get_enabled") + else False + ) self.post_srv_disable = False if not self.pre_srv_enabled: - self.run_function('service.enable', name=self.service_name) + self.run_function("service.enable", name=self.service_name) self.post_srv_disable = True - if os_family != 'Windows' and salt.utils.path.which(cmd_name) is None: - self.skipTest('{0} is not installed'.format(cmd_name)) + if os_family != "Windows" and salt.utils.path.which(cmd_name) is None: + self.skipTest("{0} is not installed".format(cmd_name)) def tearDown(self): if self.post_srv_disable: - self.run_function('service.disable', name=self.service_name) + self.run_function("service.disable", name=self.service_name) def check_service_status(self, exp_return): - ''' + """ helper method to check status of service - ''' - check_status = self.run_function('service.status', - name=self.service_name) + """ + check_status = self.run_function("service.status", name=self.service_name) try: if not re.match(exp_return, check_status): - self.fail('status of service is not returning correctly') + self.fail("status of service is not returning correctly") except TypeError: if check_status is not exp_return: - self.fail('status of service is not returning correctly') + self.fail("status of service is not returning correctly") def test_service_running(self): - ''' + """ test service.running state module - ''' - if self.run_function('service.status', name=self.service_name): - stop_service = self.run_function('service.stop', name=self.service_name) + """ + if self.run_function("service.status", name=self.service_name): + stop_service = self.run_function("service.stop", name=self.service_name) self.assertTrue(stop_service) self.check_service_status(self.stopped) if salt.utils.platform.is_darwin(): # make sure the service is enabled on macosx - enable = self.run_function('service.enable', name=self.service_name) + enable = self.run_function("service.enable", name=self.service_name) - start_service = self.run_state('service.running', - name=self.service_name) + start_service = self.run_state("service.running", name=self.service_name) self.assertTrue(start_service) self.check_service_status(self.running) def test_service_dead(self): - ''' + """ test service.dead state module - ''' - start_service = self.run_state('service.running', - name=self.service_name) + """ + start_service = self.run_state("service.running", name=self.service_name) self.assertSaltTrueReturn(start_service) self.check_service_status(self.running) - ret = self.run_state('service.dead', name=self.service_name) + ret = self.run_state("service.dead", name=self.service_name) self.assertSaltTrueReturn(ret) self.check_service_status(self.stopped) def test_service_dead_init_delay(self): - ''' + """ test service.dead state module with init_delay arg - ''' - start_service = self.run_state('service.running', - name=self.service_name) + """ + start_service = self.run_state("service.running", name=self.service_name) self.assertSaltTrueReturn(start_service) self.check_service_status(self.running) - ret = self.run_state('service.dead', name=self.service_name, - init_delay=INIT_DELAY) + ret = self.run_state( + "service.dead", name=self.service_name, init_delay=INIT_DELAY + ) self.assertSaltTrueReturn(ret) self.check_service_status(self.stopped) diff --git a/tests/integration/states/test_ssh_auth.py b/tests/integration/states/test_ssh_auth.py index 17dde1d2700..80ddef9c648 100644 --- a/tests/integration/states/test_ssh_auth.py +++ b/tests/integration/states/test_ssh_auth.py @@ -1,116 +1,108 @@ # -*- coding: utf-8 -*- -''' +""" Test the ssh_auth states -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.runtests import RUNTIME_VARS -from tests.support.helpers import ( - destructiveTest, - with_system_user, - skip_if_not_root -) +import os # Import salt libs import salt.utils.files +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root, with_system_user +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS + class SSHAuthStateTests(ModuleCase, SaltReturnAssertsMixin): - @destructiveTest @skip_if_not_root - @with_system_user('issue_7409', on_existing='delete', delete=True) + @with_system_user("issue_7409", on_existing="delete", delete=True) def test_issue_7409_no_linebreaks_between_keys(self, username): - userdetails = self.run_function('user.info', [username]) - user_ssh_dir = os.path.join(userdetails['home'], '.ssh') - authorized_keys_file = os.path.join(user_ssh_dir, 'authorized_keys') + userdetails = self.run_function("user.info", [username]) + user_ssh_dir = os.path.join(userdetails["home"], ".ssh") + authorized_keys_file = os.path.join(user_ssh_dir, "authorized_keys") ret = self.run_state( - 'file.managed', + "file.managed", name=authorized_keys_file, user=username, makedirs=True, contents_newline=False, # Explicit no ending line break - contents='ssh-rsa AAAAB3NzaC1kc3MAAACBAL0sQ9fJ5bYTEyY== root' + contents="ssh-rsa AAAAB3NzaC1kc3MAAACBAL0sQ9fJ5bYTEyY== root", ) ret = self.run_state( - 'ssh_auth.present', - name='AAAAB3NzaC1kcQ9J5bYTEyZ==', - enc='ssh-rsa', + "ssh_auth.present", + name="AAAAB3NzaC1kcQ9J5bYTEyZ==", + enc="ssh-rsa", user=username, - comment=username + comment=username, ) self.assertSaltTrueReturn(ret) - self.assertSaltStateChangesEqual( - ret, {'AAAAB3NzaC1kcQ9J5bYTEyZ==': 'New'} - ) - with salt.utils.files.fopen(authorized_keys_file, 'r') as fhr: + self.assertSaltStateChangesEqual(ret, {"AAAAB3NzaC1kcQ9J5bYTEyZ==": "New"}) + with salt.utils.files.fopen(authorized_keys_file, "r") as fhr: self.assertEqual( fhr.read(), - 'ssh-rsa AAAAB3NzaC1kc3MAAACBAL0sQ9fJ5bYTEyY== root\n' - 'ssh-rsa AAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n'.format(username) + "ssh-rsa AAAAB3NzaC1kc3MAAACBAL0sQ9fJ5bYTEyY== root\n" + "ssh-rsa AAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n".format(username), ) @destructiveTest @skip_if_not_root - @with_system_user('issue_10198', on_existing='delete', delete=True) + @with_system_user("issue_10198", on_existing="delete", delete=True) def test_issue_10198_keyfile_from_another_env(self, username=None): - userdetails = self.run_function('user.info', [username]) - user_ssh_dir = os.path.join(userdetails['home'], '.ssh') - authorized_keys_file = os.path.join(user_ssh_dir, 'authorized_keys') + userdetails = self.run_function("user.info", [username]) + user_ssh_dir = os.path.join(userdetails["home"], ".ssh") + authorized_keys_file = os.path.join(user_ssh_dir, "authorized_keys") - key_fname = 'issue_10198.id_rsa.pub' + key_fname = "issue_10198.id_rsa.pub" # Create the keyfile that we expect to get back on the state call - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PRODENV_STATE_TREE, key_fname), 'w') as kfh: - kfh.write( - 'ssh-rsa AAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n'.format(username) - ) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_PRODENV_STATE_TREE, key_fname), "w" + ) as kfh: + kfh.write("ssh-rsa AAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n".format(username)) # Create a bogus key file on base environment - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_STATE_TREE, key_fname), 'w') as kfh: - kfh.write( - 'ssh-rsa BAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n'.format(username) - ) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_STATE_TREE, key_fname), "w" + ) as kfh: + kfh.write("ssh-rsa BAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n".format(username)) ret = self.run_state( - 'ssh_auth.present', - name='Setup Keys', - source='salt://{0}?saltenv=prod'.format(key_fname), - enc='ssh-rsa', + "ssh_auth.present", + name="Setup Keys", + source="salt://{0}?saltenv=prod".format(key_fname), + enc="ssh-rsa", user=username, - comment=username + comment=username, ) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(authorized_keys_file, 'r') as fhr: + with salt.utils.files.fopen(authorized_keys_file, "r") as fhr: self.assertEqual( - fhr.read(), - 'ssh-rsa AAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n'.format(username) + fhr.read(), "ssh-rsa AAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n".format(username) ) os.unlink(authorized_keys_file) ret = self.run_state( - 'ssh_auth.present', - name='Setup Keys', - source='salt://{0}'.format(key_fname), - enc='ssh-rsa', + "ssh_auth.present", + name="Setup Keys", + source="salt://{0}".format(key_fname), + enc="ssh-rsa", user=username, comment=username, - saltenv='prod' + saltenv="prod", ) self.assertSaltTrueReturn(ret) - with salt.utils.files.fopen(authorized_keys_file, 'r') as fhr: + with salt.utils.files.fopen(authorized_keys_file, "r") as fhr: self.assertEqual( - fhr.read(), - 'ssh-rsa AAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n'.format(username) + fhr.read(), "ssh-rsa AAAAB3NzaC1kcQ9J5bYTEyZ== {0}\n".format(username) ) diff --git a/tests/integration/states/test_ssh_known_hosts.py b/tests/integration/states/test_ssh_known_hosts.py index 26d17bba9e3..112fcfd6bff 100644 --- a/tests/integration/states/test_ssh_known_hosts.py +++ b/tests/integration/states/test_ssh_known_hosts.py @@ -1,36 +1,37 @@ # -*- coding: utf-8 -*- -''' +""" Test the ssh_known_hosts states -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import os import shutil import sys -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.runtests import RUNTIME_VARS -from tests.support.helpers import skip_if_binaries_missing - # Import 3rd-party libs from salt.ext import six -GITHUB_FINGERPRINT = '9d:38:5b:83:a9:17:52:92:56:1a:5e:c4:d4:81:8e:0a:ca:51:a2:64:f1:74:20:11:2e:f8:8a:c3:a1:39:49:8f' -GITHUB_IP = '192.30.253.113' +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import skip_if_binaries_missing +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS + +GITHUB_FINGERPRINT = "9d:38:5b:83:a9:17:52:92:56:1a:5e:c4:d4:81:8e:0a:ca:51:a2:64:f1:74:20:11:2e:f8:8a:c3:a1:39:49:8f" +GITHUB_IP = "192.30.253.113" -@skip_if_binaries_missing(['ssh', 'ssh-keygen'], check_all=True) +@skip_if_binaries_missing(["ssh", "ssh-keygen"], check_all=True) class SSHKnownHostsStateTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the ssh state - ''' + """ + @classmethod def setUpClass(cls): - cls.known_hosts = os.path.join(RUNTIME_VARS.TMP, 'known_hosts') + cls.known_hosts = os.path.join(RUNTIME_VARS.TMP, "known_hosts") def tearDown(self): if os.path.isfile(self.known_hosts): @@ -38,118 +39,113 @@ class SSHKnownHostsStateTest(ModuleCase, SaltReturnAssertsMixin): super(SSHKnownHostsStateTest, self).tearDown() def test_present(self): - ''' + """ ssh_known_hosts.present - ''' + """ kwargs = { - 'name': 'github.com', - 'user': 'root', - 'fingerprint': GITHUB_FINGERPRINT, - 'config': self.known_hosts + "name": "github.com", + "user": "root", + "fingerprint": GITHUB_FINGERPRINT, + "config": self.known_hosts, } # test first - ret = self.run_state('ssh_known_hosts.present', test=True, **kwargs) + ret = self.run_state("ssh_known_hosts.present", test=True, **kwargs) self.assertSaltNoneReturn(ret) # save once, new key appears - ret = self.run_state('ssh_known_hosts.present', **kwargs) + ret = self.run_state("ssh_known_hosts.present", **kwargs) try: self.assertSaltTrueReturn(ret) except AssertionError as err: try: - self.assertInSaltComment( - 'Unable to receive remote host key', ret - ) - self.skipTest('Unable to receive remote host key') + self.assertInSaltComment("Unable to receive remote host key", ret) + self.skipTest("Unable to receive remote host key") except AssertionError: six.reraise(*sys.exc_info()) self.assertSaltStateChangesEqual( - ret, GITHUB_FINGERPRINT, keys=('new', 0, 'fingerprint') + ret, GITHUB_FINGERPRINT, keys=("new", 0, "fingerprint") ) # save twice, no changes - self.run_state('ssh_known_hosts.present', **kwargs) + self.run_state("ssh_known_hosts.present", **kwargs) # test again, nothing is about to be changed - ret = self.run_state('ssh_known_hosts.present', test=True, **kwargs) + ret = self.run_state("ssh_known_hosts.present", test=True, **kwargs) self.assertSaltTrueReturn(ret) # then add a record for IP address - ret = self.run_state('ssh_known_hosts.present', # pylint: disable=repeated-keyword - **dict(kwargs, name=GITHUB_IP)) + # pylint: disable=repeated-keyword + ret = self.run_state("ssh_known_hosts.present", **dict(kwargs, name=GITHUB_IP)) + # pylint: enable=repeated-keyword try: self.assertSaltStateChangesEqual( - ret, GITHUB_FINGERPRINT, keys=('new', 0, 'fingerprint') + ret, GITHUB_FINGERPRINT, keys=("new", 0, "fingerprint") ) except AssertionError as err: try: - self.assertInSaltComment( - 'Unable to receive remote host key', ret - ) - self.skipTest('Unable to receive remote host key') + self.assertInSaltComment("Unable to receive remote host key", ret) + self.skipTest("Unable to receive remote host key") except AssertionError: six.reraise(*sys.exc_info()) # record for every host must be available ret = self.run_function( - 'ssh.get_known_host_entries', ['root', 'github.com'], config=self.known_hosts + "ssh.get_known_host_entries", + ["root", "github.com"], + config=self.known_hosts, )[0] try: - self.assertNotIn(ret, ('', None)) + self.assertNotIn(ret, ("", None)) except AssertionError: - raise AssertionError( - 'Salt return \'{0}\' is in (\'\', None).'.format(ret) - ) + raise AssertionError("Salt return '{0}' is in ('', None).".format(ret)) ret = self.run_function( - 'ssh.get_known_host_entries', ['root', GITHUB_IP], config=self.known_hosts + "ssh.get_known_host_entries", ["root", GITHUB_IP], config=self.known_hosts )[0] try: - self.assertNotIn(ret, ('', None, {})) + self.assertNotIn(ret, ("", None, {})) except AssertionError: raise AssertionError( - 'Salt return \'{0}\' is in (\'\', None,'.format(ret) + ' {})' + "Salt return '{0}' is in ('', None,".format(ret) + " {})" ) def test_present_fail(self): # save something wrong ret = self.run_state( - 'ssh_known_hosts.present', - name='github.com', - user='root', - fingerprint='aa:bb:cc:dd', - config=self.known_hosts + "ssh_known_hosts.present", + name="github.com", + user="root", + fingerprint="aa:bb:cc:dd", + config=self.known_hosts, ) self.assertSaltFalseReturn(ret) def test_absent(self): - ''' + """ ssh_known_hosts.absent - ''' - known_hosts = os.path.join(RUNTIME_VARS.FILES, 'ssh', 'known_hosts') + """ + known_hosts = os.path.join(RUNTIME_VARS.FILES, "ssh", "known_hosts") shutil.copyfile(known_hosts, self.known_hosts) if not os.path.isfile(self.known_hosts): self.skipTest( - 'Unable to copy {0} to {1}'.format( - known_hosts, self.known_hosts - ) + "Unable to copy {0} to {1}".format(known_hosts, self.known_hosts) ) - kwargs = {'name': 'github.com', 'user': 'root', 'config': self.known_hosts} + kwargs = {"name": "github.com", "user": "root", "config": self.known_hosts} # test first - ret = self.run_state('ssh_known_hosts.absent', test=True, **kwargs) + ret = self.run_state("ssh_known_hosts.absent", test=True, **kwargs) self.assertSaltNoneReturn(ret) # remove once, the key is gone - ret = self.run_state('ssh_known_hosts.absent', **kwargs) + ret = self.run_state("ssh_known_hosts.absent", **kwargs) self.assertSaltStateChangesEqual( - ret, GITHUB_FINGERPRINT, keys=('old', 0, 'fingerprint') + ret, GITHUB_FINGERPRINT, keys=("old", 0, "fingerprint") ) # remove twice, nothing has changed - ret = self.run_state('ssh_known_hosts.absent', **kwargs) + ret = self.run_state("ssh_known_hosts.absent", **kwargs) self.assertSaltStateChangesEqual(ret, {}) # test again - ret = self.run_state('ssh_known_hosts.absent', test=True, **kwargs) + ret = self.run_state("ssh_known_hosts.absent", test=True, **kwargs) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_supervisord.py b/tests/integration/states/test_supervisord.py index 9fb46167f5a..183f726a598 100644 --- a/tests/integration/states/test_supervisord.py +++ b/tests/integration/states/test_supervisord.py @@ -1,77 +1,84 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the supervisord state -''' +""" # Import python lins -from __future__ import absolute_import, unicode_literals, print_function -import os -import time -import subprocess +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.mixins import SaltReturnAssertsMixin +import os +import subprocess +import time # Import salt libs import salt.utils.path -from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES # Import 3rd-party libs from salt.ext import six +from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.case import ModuleCase +from tests.support.mixins import SaltReturnAssertsMixin + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf -@skipIf(six.PY3, 'supervisor does not work under python 3') -@skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') -@skipIf(salt.utils.path.which('supervisorctl') is None, 'supervisord not installed') +@skipIf(six.PY3, "supervisor does not work under python 3") +@skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, "virtualenv not installed" +) +@skipIf(salt.utils.path.which("supervisorctl") is None, "supervisord not installed") class SupervisordTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the supervisord states. - ''' + """ + def setUp(self): super(SupervisordTest, self).setUp() - self.venv_test_dir = os.path.join(RUNTIME_VARS.TMP, 'supervisortests') - self.venv_dir = os.path.join(self.venv_test_dir, 'venv') - self.supervisor_sock = os.path.join(self.venv_dir, 'supervisor.sock') + self.venv_test_dir = os.path.join(RUNTIME_VARS.TMP, "supervisortests") + self.venv_dir = os.path.join(self.venv_test_dir, "venv") + self.supervisor_sock = os.path.join(self.venv_dir, "supervisor.sock") if not os.path.exists(self.venv_dir): os.makedirs(self.venv_test_dir) - self.run_function('virtualenv.create', [self.venv_dir]) + self.run_function("virtualenv.create", [self.venv_dir]) self.run_function( - 'pip.install', [], pkgs='supervisor', bin_env=self.venv_dir) + "pip.install", [], pkgs="supervisor", bin_env=self.venv_dir + ) - self.supervisord = os.path.join(self.venv_dir, 'bin', 'supervisord') + self.supervisord = os.path.join(self.venv_dir, "bin", "supervisord") if not os.path.exists(self.supervisord): - self.skipTest('Failed to installe supervisor in test virtualenv') + self.skipTest("Failed to installe supervisor in test virtualenv") - self.supervisor_conf = os.path.join(self.venv_dir, 'supervisor.conf') + self.supervisor_conf = os.path.join(self.venv_dir, "supervisor.conf") def start_supervisord(self, autostart=True): self.run_state( - 'file.managed', name=self.supervisor_conf, - source='salt://supervisor.conf', template='jinja', + "file.managed", + name=self.supervisor_conf, + source="salt://supervisor.conf", + template="jinja", context={ - 'supervisor_sock': self.supervisor_sock, - 'virtual_env': self.venv_dir, - 'autostart': autostart - } + "supervisor_sock": self.supervisor_sock, + "virtual_env": self.venv_dir, + "autostart": autostart, + }, ) if not os.path.exists(self.supervisor_conf): - self.skipTest('failed to create supervisor config file') + self.skipTest("failed to create supervisor config file") self.supervisor_proc = subprocess.Popen( - [self.supervisord, '-c', self.supervisor_conf] + [self.supervisord, "-c", self.supervisor_conf] ) if self.supervisor_proc.poll() is not None: - self.skipTest('failed to start supervisord') + self.skipTest("failed to start supervisord") timeout = 10 while not os.path.exists(self.supervisor_sock): if timeout == 0: self.skipTest( - 'supervisor socket not found - failed to start supervisord' + "supervisor socket not found - failed to start supervisord" ) break else: @@ -79,11 +86,13 @@ class SupervisordTest(ModuleCase, SaltReturnAssertsMixin): timeout -= 1 def tearDown(self): - if hasattr(self, 'supervisor_proc') and \ - self.supervisor_proc.poll() is not None: + if hasattr(self, "supervisor_proc") and self.supervisor_proc.poll() is not None: self.run_function( - 'supervisord.custom', ['shutdown'], - conf_file=self.supervisor_conf, bin_env=self.venv_dir) + "supervisord.custom", + ["shutdown"], + conf_file=self.supervisor_conf, + bin_env=self.venv_dir, + ) self.supervisor_proc.wait() del self.supervisor_proc del self.venv_test_dir @@ -93,174 +102,192 @@ class SupervisordTest(ModuleCase, SaltReturnAssertsMixin): del self.supervisor_sock def test_running_stopped(self): - ''' + """ supervisord.running restart = False When service is stopped. - ''' + """ self.start_supervisord(autostart=False) ret = self.run_state( - 'supervisord.running', name='sleep_service', - bin_env=self.venv_dir, conf_file=self.supervisor_conf + "supervisord.running", + name="sleep_service", + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) - self.assertInSaltReturn('sleep_service', ret, ['changes']) + self.assertInSaltReturn("sleep_service", ret, ["changes"]) def test_running_started(self): - ''' + """ supervisord.running restart = False When service is running. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_state( - 'supervisord.running', name='sleep_service', - bin_env=self.venv_dir, conf_file=self.supervisor_conf + "supervisord.running", + name="sleep_service", + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) - self.assertNotInSaltReturn('sleep_service', ret, ['changes']) + self.assertNotInSaltReturn("sleep_service", ret, ["changes"]) def test_running_needsupdate(self): - ''' + """ supervisord.running restart = False When service needs to be added. - ''' + """ self.start_supervisord(autostart=False) - self.run_function('supervisord.remove', [ - 'sleep_service', - None, - self.supervisor_conf, - self.venv_dir - ]) + self.run_function( + "supervisord.remove", + ["sleep_service", None, self.supervisor_conf, self.venv_dir], + ) ret = self.run_state( - 'supervisord.running', name='sleep_service', - bin_env=self.venv_dir, conf_file=self.supervisor_conf + "supervisord.running", + name="sleep_service", + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) - self.assertInSaltReturn('sleep_service', ret, ['changes']) + self.assertInSaltReturn("sleep_service", ret, ["changes"]) def test_running_notexists(self): - ''' + """ supervisord.running restart = False When service doesn't exist. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_state( - 'supervisord.running', name='does_not_exist', - bin_env=self.venv_dir, conf_file=self.supervisor_conf + "supervisord.running", + name="does_not_exist", + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltFalseReturn(ret) def test_restart_started(self): - ''' + """ supervisord.running restart = True When service is running. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_state( - 'supervisord.running', name='sleep_service', + "supervisord.running", + name="sleep_service", restart=True, - bin_env=self.venv_dir, conf_file=self.supervisor_conf + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) - self.assertInSaltReturn('sleep_service', ret, ['changes']) + self.assertInSaltReturn("sleep_service", ret, ["changes"]) def test_restart_stopped(self): - ''' + """ supervisord.running restart = True When service is stopped. - ''' + """ self.start_supervisord(autostart=False) ret = self.run_state( - 'supervisord.running', name='sleep_service', + "supervisord.running", + name="sleep_service", restart=True, - bin_env=self.venv_dir, conf_file=self.supervisor_conf + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) - self.assertInSaltReturn('sleep_service', ret, ['changes']) + self.assertInSaltReturn("sleep_service", ret, ["changes"]) def test_restart_needsupdate(self): - ''' + """ supervisord.running restart = True When service needs to be added. - ''' + """ self.start_supervisord(autostart=False) - self.run_function('supervisord.remove', [ - 'sleep_service', - None, - self.supervisor_conf, - self.venv_dir - ]) + self.run_function( + "supervisord.remove", + ["sleep_service", None, self.supervisor_conf, self.venv_dir], + ) ret = self.run_state( - 'supervisord.running', name='sleep_service', + "supervisord.running", + name="sleep_service", restart=True, - bin_env=self.venv_dir, conf_file=self.supervisor_conf + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) - self.assertInSaltReturn('sleep_service', ret, ['changes']) + self.assertInSaltReturn("sleep_service", ret, ["changes"]) def test_restart_notexists(self): - ''' + """ supervisord.running restart = True When service does not exist. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_state( - 'supervisord.running', name='does_not_exist', + "supervisord.running", + name="does_not_exist", restart=True, - bin_env=self.venv_dir, conf_file=self.supervisor_conf + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltFalseReturn(ret) - self.assertNotInSaltReturn('sleep_service', ret, ['changes']) + self.assertNotInSaltReturn("sleep_service", ret, ["changes"]) def test_dead_started(self): - ''' + """ supervisord.dead When service is running. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_state( - 'supervisord.dead', name='sleep_service', - bin_env=self.venv_dir, conf_file=self.supervisor_conf + "supervisord.dead", + name="sleep_service", + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) def test_dead_stopped(self): - ''' + """ supervisord.dead When service is stopped. - ''' + """ self.start_supervisord(autostart=False) ret = self.run_state( - 'supervisord.dead', name='sleep_service', - bin_env=self.venv_dir, conf_file=self.supervisor_conf + "supervisord.dead", + name="sleep_service", + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) def test_dead_removed(self): - ''' + """ supervisord.dead When service needs to be added. - ''' + """ self.start_supervisord(autostart=False) - self.run_function('supervisord.remove', [ - 'sleep_service', - None, - self.supervisor_conf, - self.venv_dir - ]) + self.run_function( + "supervisord.remove", + ["sleep_service", None, self.supervisor_conf, self.venv_dir], + ) ret = self.run_state( - 'supervisord.dead', name='sleep_service', - bin_env=self.venv_dir, conf_file=self.supervisor_conf + "supervisord.dead", + name="sleep_service", + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) def test_dead_notexists(self): - ''' + """ supervisord.dead When service does not exist. - ''' + """ self.start_supervisord(autostart=True) ret = self.run_state( - 'supervisord.dead', name='does_not_exist', - bin_env=self.venv_dir, conf_file=self.supervisor_conf + "supervisord.dead", + name="does_not_exist", + bin_env=self.venv_dir, + conf_file=self.supervisor_conf, ) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/states/test_svn.py b/tests/integration/states/test_svn.py index 115c7133b9c..f1c39f58a49 100644 --- a/tests/integration/states/test_svn.py +++ b/tests/integration/states/test_svn.py @@ -1,47 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the SVN state -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import socket -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS from tests.support.case import ModuleCase from tests.support.mixins import SaltReturnAssertsMixin +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS + class SvnTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Validate the svn state - ''' + """ def setUp(self): super(SvnTest, self).setUp() - if not self.run_function('cmd.has_exec', ['svn']): + if not self.run_function("cmd.has_exec", ["svn"]): self.skipTest("The executable 'svn' is not available.") - self.__domain = 'svn.apache.org' + self.__domain = "svn.apache.org" try: - if hasattr(socket, 'setdefaulttimeout'): + if hasattr(socket, "setdefaulttimeout"): # 10 second dns timeout socket.setdefaulttimeout(10) socket.gethostbyname(self.__domain) except socket.error: - msg = 'error resolving {0}, possible network issue?' + msg = "error resolving {0}, possible network issue?" self.skipTest(msg.format(self.__domain)) - self.target = os.path.join(RUNTIME_VARS.TMP, 'apache_http_test_repo') - self.name = 'http://{0}/repos/asf/httpd/httpd/trunk/test/'.format( - self.__domain - ) - self.new_rev = '1456987' + self.target = os.path.join(RUNTIME_VARS.TMP, "apache_http_test_repo") + self.name = "http://{0}/repos/asf/httpd/httpd/trunk/test/".format(self.__domain) + self.new_rev = "1456987" def tearDown(self): shutil.rmtree(self.target, ignore_errors=True) @@ -49,94 +49,78 @@ class SvnTest(ModuleCase, SaltReturnAssertsMixin): socket.setdefaulttimeout(None) def test_latest(self): - ''' + """ svn.latest - ''' + """ ret = self.run_state( - 'svn.latest', - name=self.name, - rev=self.new_rev, - target=self.target, + "svn.latest", name=self.name, rev=self.new_rev, target=self.target, ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isdir(os.path.join(self.target, '.svn'))) - self.assertSaltStateChangesEqual( - ret, self.name, keys=['new'] - ) - self.assertSaltStateChangesEqual( - ret, self.new_rev, keys=['revision'] - ) + self.assertTrue(os.path.isdir(os.path.join(self.target, ".svn"))) + self.assertSaltStateChangesEqual(ret, self.name, keys=["new"]) + self.assertSaltStateChangesEqual(ret, self.new_rev, keys=["revision"]) def test_latest_failure(self): - ''' + """ svn.latest - ''' + """ ret = self.run_state( - 'svn.latest', - name='https://youSpelledApacheWrong.com/repo/asf/httpd/trunk/', + "svn.latest", + name="https://youSpelledApacheWrong.com/repo/asf/httpd/trunk/", rev=self.new_rev, target=self.target, ) self.assertSaltFalseReturn(ret) - self.assertFalse(os.path.isdir(os.path.join(self.target, '.svn'))) + self.assertFalse(os.path.isdir(os.path.join(self.target, ".svn"))) def test_latest_empty_dir(self): - ''' + """ svn.latest - ''' + """ if not os.path.isdir(self.target): os.mkdir(self.target) ret = self.run_state( - 'svn.latest', - name=self.name, - rev=self.new_rev, - target=self.target, + "svn.latest", name=self.name, rev=self.new_rev, target=self.target, ) self.assertSaltTrueReturn(ret) - self.assertTrue(os.path.isdir(os.path.join(self.target, '.svn'))) + self.assertTrue(os.path.isdir(os.path.join(self.target, ".svn"))) def no_test_latest_existing_repo(self): - ''' + """ svn.latest against existing repository - ''' - current_rev = '1442865' + """ + current_rev = "1442865" cwd, basename = os.path.split(self.target) - opts = ('-r', current_rev) + opts = ("-r", current_rev) - out = self.run_function('svn.checkout', - [cwd, self.name, basename, None, None, opts]) + out = self.run_function( + "svn.checkout", [cwd, self.name, basename, None, None, opts] + ) assert out ret = self.run_state( - 'svn.latest', - name=self.name, - rev=self.new_rev, - target=self.target, + "svn.latest", name=self.name, rev=self.new_rev, target=self.target, ) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual( - ret, - '{0} => {1}'.format(current_rev, self.new_rev), - keys=['revision'] + ret, "{0} => {1}".format(current_rev, self.new_rev), keys=["revision"] ) - self.assertTrue(os.path.isdir(os.path.join(self.target, '.svn'))) + self.assertTrue(os.path.isdir(os.path.join(self.target, ".svn"))) def no_test_latest_existing_repo_no_rev_change(self): - ''' + """ svn.latest against existing repository - ''' + """ current_rev = self.new_rev cwd, basename = os.path.split(self.target) - opts = ('-r', current_rev) - out = self.run_function('svn.checkout', - [cwd, self.name, basename, None, None, opts]) + opts = ("-r", current_rev) + out = self.run_function( + "svn.checkout", [cwd, self.name, basename, None, None, opts] + ) assert out ret = self.run_state( - 'svn.latest', - name=self.name, - rev=self.new_rev, - target=self.target, + "svn.latest", name=self.name, rev=self.new_rev, target=self.target, ) self.assertSaltTrueReturn(ret) self.assertSaltStateChangesEqual(ret, {}) - self.assertTrue(os.path.isdir(os.path.join(self.target, '.svn'))) + self.assertTrue(os.path.isdir(os.path.join(self.target, ".svn"))) diff --git a/tests/integration/states/test_user.py b/tests/integration/states/test_user.py index a1efae88e51..b5bd35647a3 100644 --- a/tests/integration/states/test_user.py +++ b/tests/integration/states/test_user.py @@ -1,26 +1,28 @@ # -*- coding: utf-8 -*- -''' +""" tests for user state user absent user present user present with custom homedir -''' +""" -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import sys from random import randint -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, requires_system_grains, skip_if_not_root -from tests.support.mixins import SaltReturnAssertsMixin - -# Import Salt libs +import pytest import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.helpers import ( + destructiveTest, + requires_system_grains, + skip_if_not_root, +) +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.unit import skipIf try: import grp @@ -28,280 +30,321 @@ except ImportError: grp = None if salt.utils.platform.is_darwin(): - USER = 'macuser' - GROUP = 'macuser' + USER = "macuser" + GROUP = "macuser" GID = randint(400, 500) NOGROUPGID = randint(400, 500) elif salt.utils.platform.is_windows(): - USER = 'winuser' - GROUP = 'winuser' + USER = "winuser" + GROUP = "winuser" GID = randint(400, 500) NOGROUPGID = randint(400, 500) else: - USER = 'nobody' - GROUP = 'nobody' - GID = 'nobody' - NOGROUPGID = 'nogroup' + USER = "nobody" + GROUP = "nobody" + GID = "nobody" + NOGROUPGID = "nogroup" @destructiveTest @skip_if_not_root +@pytest.mark.windows_whitelisted class UserTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ test for user absent - ''' - user_name = 'salt-test' - user_home = '/var/lib/{0}'.format(user_name) if not salt.utils.platform.is_windows() else os.path.join('tmp', user_name) + """ + + user_name = "salt-test" + user_home = ( + "/var/lib/{0}".format(user_name) + if not salt.utils.platform.is_windows() + else os.path.join("tmp", user_name) + ) def test_user_absent(self): - ret = self.run_state('user.absent', name='unpossible') + ret = self.run_state("user.absent", name="unpossible") self.assertSaltTrueReturn(ret) def test_user_if_present(self): - ret = self.run_state('user.present', name=USER) + ret = self.run_state("user.present", name=USER) self.assertSaltTrueReturn(ret) def test_user_if_present_with_gid(self): - if self.run_function('group.info', [USER]): - ret = self.run_state('user.present', name=USER, gid=GID) - elif self.run_function('group.info', ['nogroup']): - ret = self.run_state('user.present', name=USER, gid=NOGROUPGID) + if self.run_function("group.info", [USER]): + ret = self.run_state("user.present", name=USER, gid=GID) + elif self.run_function("group.info", ["nogroup"]): + ret = self.run_state("user.present", name=USER, gid=NOGROUPGID) else: - self.skipTest( - 'Neither \'nobody\' nor \'nogroup\' are valid groups' - ) + self.skipTest("Neither 'nobody' nor 'nogroup' are valid groups") self.assertSaltTrueReturn(ret) def test_user_not_present(self): - ''' + """ This is a DESTRUCTIVE TEST it creates a new user on the minion. And then destroys that user. Assume that it will break any system you run it on. - ''' - ret = self.run_state('user.present', name=self.user_name) + """ + ret = self.run_state("user.present", name=self.user_name) self.assertSaltTrueReturn(ret) def test_user_present_when_home_dir_does_not_18843(self): - ''' + """ This is a DESTRUCTIVE TEST it creates a new user on the minion. And then destroys that user. Assume that it will break any system you run it on. - ''' + """ if salt.utils.platform.is_darwin(): - HOMEDIR = '/Users/home_of_' + self.user_name + HOMEDIR = "/Users/home_of_" + self.user_name else: - HOMEDIR = '/home/home_of_' + self.user_name - ret = self.run_state('user.present', name=self.user_name, - home=HOMEDIR) + HOMEDIR = "/home/home_of_" + self.user_name + ret = self.run_state("user.present", name=self.user_name, home=HOMEDIR) self.assertSaltTrueReturn(ret) - self.run_function('file.absent', name=HOMEDIR) - ret = self.run_state('user.present', name=self.user_name, - home=HOMEDIR) + self.run_function("file.absent", name=HOMEDIR) + ret = self.run_state("user.present", name=self.user_name, home=HOMEDIR) self.assertSaltTrueReturn(ret) @requires_system_grains def test_user_present_nondefault(self, grains=None): - ''' + """ This is a DESTRUCTIVE TEST it creates a new user on the on the minion. - ''' - ret = self.run_state('user.present', name=self.user_name, - home=self.user_home) + """ + ret = self.run_state("user.present", name=self.user_name, home=self.user_home) self.assertSaltTrueReturn(ret) - ret = self.run_function('user.info', [self.user_name]) + ret = self.run_function("user.info", [self.user_name]) self.assertReturnNonEmptySaltType(ret) if salt.utils.platform.is_windows(): - group_name = self.run_function('user.list_groups', [self.user_name]) + group_name = self.run_function("user.list_groups", [self.user_name]) else: - group_name = grp.getgrgid(ret['gid']).gr_name + group_name = grp.getgrgid(ret["gid"]).gr_name if not salt.utils.platform.is_darwin() and not salt.utils.platform.is_windows(): self.assertTrue(os.path.isdir(self.user_home)) - if grains['os_family'] in ('Suse',): - self.assertEqual(group_name, 'users') - elif grains['os_family'] == 'MacOS': - self.assertEqual(group_name, 'staff') + if grains["os_family"] in ("Suse",): + self.assertEqual(group_name, "users") + elif grains["os_family"] == "MacOS": + self.assertEqual(group_name, "staff") elif salt.utils.platform.is_windows(): self.assertEqual([], group_name) else: self.assertEqual(group_name, self.user_name) - @skipIf(salt.utils.platform.is_windows(), 'windows minion does not support gid_from_name') + @skipIf( + salt.utils.platform.is_windows(), + "windows minion does not support gid_from_name", + ) @requires_system_grains def test_user_present_gid_from_name_default(self, grains=None): - ''' + """ This is a DESTRUCTIVE TEST. It creates a new user on the on the minion. This is an integration test. Not all systems will automatically create a group of the same name as the user, but I don't have access to any. If you run the test and it fails, please fix the code it's testing to work on your operating system. - ''' + """ # MacOS users' primary group defaults to staff (20), not the name of # user - gid_from_name = False if grains['os_family'] == 'MacOS' else True + gid_from_name = False if grains["os_family"] == "MacOS" else True - ret_user_present = self.run_state('user.present', name=self.user_name, - gid_from_name=gid_from_name, home=self.user_home) + ret_user_present = self.run_state( + "user.present", + name=self.user_name, + gid_from_name=gid_from_name, + home=self.user_home, + ) if gid_from_name: self.assertSaltFalseReturn(ret_user_present) ret_user_present = ret_user_present[next(iter(ret_user_present))] - self.assertTrue('is not present' in ret_user_present['comment']) + self.assertTrue("is not present" in ret_user_present["comment"]) else: self.assertSaltTrueReturn(ret_user_present) - ret_user_info = self.run_function('user.info', [self.user_name]) + ret_user_info = self.run_function("user.info", [self.user_name]) self.assertReturnNonEmptySaltType(ret_user_info) - group_name = grp.getgrgid(ret_user_info['gid']).gr_name + group_name = grp.getgrgid(ret_user_info["gid"]).gr_name if not salt.utils.platform.is_darwin(): self.assertTrue(os.path.isdir(self.user_home)) - if grains['os_family'] in ('Suse',): - self.assertEqual(group_name, 'users') - elif grains['os_family'] == 'MacOS': - self.assertEqual(group_name, 'staff') + if grains["os_family"] in ("Suse",): + self.assertEqual(group_name, "users") + elif grains["os_family"] == "MacOS": + self.assertEqual(group_name, "staff") else: self.assertEqual(group_name, self.user_name) - @skipIf(salt.utils.platform.is_windows(), 'windows minion does not support gid_from_name') + @skipIf( + salt.utils.platform.is_windows(), + "windows minion does not support gid_from_name", + ) def test_user_present_gid_from_name(self): - ''' + """ This is a DESTRUCTIVE TEST it creates a new user on the on the minion. This is a unit test, NOT an integration test. We create a group of the same name as the user beforehand, so it should all run smoothly. - ''' - ret = self.run_state('group.present', name=self.user_name) + """ + ret = self.run_state("group.present", name=self.user_name) self.assertSaltTrueReturn(ret) - ret = self.run_state('user.present', name=self.user_name, - gid_from_name=True, home=self.user_home) + ret = self.run_state( + "user.present", name=self.user_name, gid_from_name=True, home=self.user_home + ) self.assertSaltTrueReturn(ret) - ret = self.run_function('user.info', [self.user_name]) + ret = self.run_function("user.info", [self.user_name]) self.assertReturnNonEmptySaltType(ret) - group_name = grp.getgrgid(ret['gid']).gr_name + group_name = grp.getgrgid(ret["gid"]).gr_name if not salt.utils.platform.is_darwin(): self.assertTrue(os.path.isdir(self.user_home)) self.assertEqual(group_name, self.user_name) - ret = self.run_state('user.absent', name=self.user_name) + ret = self.run_state("user.absent", name=self.user_name) self.assertSaltTrueReturn(ret) - ret = self.run_state('group.absent', name=self.user_name) + ret = self.run_state("group.absent", name=self.user_name) self.assertSaltTrueReturn(ret) - @skipIf(sys.getfilesystemencoding().startswith('ANSI'), 'A system encoding which supports Unicode characters must be set. Current setting is: {0}. Try setting $LANG=\'en_US.UTF-8\''.format(sys.getfilesystemencoding())) + @skipIf( + sys.getfilesystemencoding().startswith("ANSI"), + "A system encoding which supports Unicode characters must be set. Current setting is: {0}. Try setting $LANG='en_US.UTF-8'".format( + sys.getfilesystemencoding() + ), + ) def test_user_present_unicode(self): - ''' + """ This is a DESTRUCTIVE TEST it creates a new user on the on the minion. It ensures that unicode GECOS data will be properly handled, without any encoding-related failures. - ''' + """ ret = self.run_state( - 'user.present', + "user.present", name=self.user_name, - fullname='Sålt Test', - roomnumber='①②③', - workphone='١٢٣٤', - homephone='६७८' + fullname="Sålt Test", + roomnumber="①②③", + workphone="١٢٣٤", + homephone="६७८", ) self.assertSaltTrueReturn(ret) # Ensure updating a user also works ret = self.run_state( - 'user.present', + "user.present", name=self.user_name, - fullname='Sølt Test', - roomnumber='①③②', - workphone='٣٤١٢', - homephone='६८७' + fullname="Sølt Test", + roomnumber="①③②", + workphone="٣٤١٢", + homephone="६८७", ) self.assertSaltTrueReturn(ret) - @skipIf(salt.utils.platform.is_windows(), 'windows minon does not support roomnumber or phone') + @skipIf( + salt.utils.platform.is_windows(), + "windows minon does not support roomnumber or phone", + ) def test_user_present_gecos(self): - ''' + """ This is a DESTRUCTIVE TEST it creates a new user on the on the minion. It ensures that numeric GECOS data will be properly coerced to strings, otherwise the state will fail because the GECOS fields are written as strings (and show up in the user.info output as such). Thus the comparison will fail, since '12345' != 12345. - ''' + """ ret = self.run_state( - 'user.present', + "user.present", name=self.user_name, fullname=12345, roomnumber=123, workphone=1234567890, - homephone=1234567890 + homephone=1234567890, ) self.assertSaltTrueReturn(ret) - @skipIf(salt.utils.platform.is_windows(), 'windows minon does not support roomnumber or phone') + @skipIf( + salt.utils.platform.is_windows(), + "windows minon does not support roomnumber or phone", + ) def test_user_present_gecos_none_fields(self): - ''' + """ This is a DESTRUCTIVE TEST it creates a new user on the on the minion. It ensures that if no GECOS data is supplied, the fields will be coerced into empty strings as opposed to the string "None". - ''' + """ ret = self.run_state( - 'user.present', + "user.present", name=self.user_name, fullname=None, roomnumber=None, workphone=None, - homephone=None + homephone=None, ) self.assertSaltTrueReturn(ret) - ret = self.run_function('user.info', [self.user_name]) + ret = self.run_function("user.info", [self.user_name]) self.assertReturnNonEmptySaltType(ret) - self.assertEqual('', ret['fullname']) + self.assertEqual("", ret["fullname"]) # MacOS does not supply the following GECOS fields if not salt.utils.platform.is_darwin(): - self.assertEqual('', ret['roomnumber']) - self.assertEqual('', ret['workphone']) - self.assertEqual('', ret['homephone']) + self.assertEqual("", ret["roomnumber"]) + self.assertEqual("", ret["workphone"]) + self.assertEqual("", ret["homephone"]) + + @skipIf( + salt.utils.platform.is_windows(), "windows minon does not support createhome" + ) + def test_user_present_home_directory_created(self): + """ + This is a DESTRUCTIVE TEST it creates a new user on the minion. + + It ensures that the home directory is created. + """ + ret = self.run_state("user.present", name=self.user_name, createhome=True) + self.assertSaltTrueReturn(ret) + + user_info = self.run_function("user.info", [self.user_name]) + self.assertTrue(os.path.exists(user_info["home"])) def tearDown(self): if salt.utils.platform.is_darwin(): - check_user = self.run_function('user.list_users') + check_user = self.run_function("user.list_users") if USER in check_user: - del_user = self.run_function('user.delete', [USER], remove=True) - self.assertSaltTrueReturn( - self.run_state('user.absent', name=self.user_name) - ) + del_user = self.run_function("user.delete", [USER], remove=True) + self.assertSaltTrueReturn(self.run_state("user.absent", name=self.user_name)) @destructiveTest @skip_if_not_root -@skipIf(not salt.utils.platform.is_windows(), 'Windows only tests') +@skipIf(not salt.utils.platform.is_windows(), "Windows only tests") +@pytest.mark.windows_whitelisted class WinUserTest(ModuleCase, SaltReturnAssertsMixin): - ''' + """ test for user absent - ''' + """ + def tearDown(self): - self.assertSaltTrueReturn( - self.run_state('user.absent', name=USER) - ) + self.assertSaltTrueReturn(self.run_state("user.absent", name=USER)) def test_user_present_existing(self): - ret = self.run_state('user.present', - name=USER, - win_homedrive='U:', - win_profile='C:\\User\\{0}'.format(USER), - win_logonscript='C:\\logon.vbs', - win_description='Test User Account') + ret = self.run_state( + "user.present", + name=USER, + win_homedrive="U:", + win_profile="C:\\User\\{0}".format(USER), + win_logonscript="C:\\logon.vbs", + win_description="Test User Account", + ) self.assertSaltTrueReturn(ret) - ret = self.run_state('user.present', - name=USER, - win_homedrive='R:', - win_profile='C:\\Users\\{0}'.format(USER), - win_logonscript='C:\\Windows\\logon.vbs', - win_description='Temporary Account') + ret = self.run_state( + "user.present", + name=USER, + win_homedrive="R:", + win_profile="C:\\Users\\{0}".format(USER), + win_logonscript="C:\\Windows\\logon.vbs", + win_description="Temporary Account", + ) self.assertSaltTrueReturn(ret) - self.assertSaltStateChangesEqual(ret, 'R:', keys=['homedrive']) + self.assertSaltStateChangesEqual(ret, "R:", keys=["homedrive"]) self.assertSaltStateChangesEqual( - ret, 'C:\\Users\\{0}'.format(USER), keys=['profile']) + ret, "C:\\Users\\{0}".format(USER), keys=["profile"] + ) self.assertSaltStateChangesEqual( - ret, 'C:\\Windows\\logon.vbs', keys=['logonscript']) - self.assertSaltStateChangesEqual( - ret, 'Temporary Account', keys=['description']) + ret, "C:\\Windows\\logon.vbs", keys=["logonscript"] + ) + self.assertSaltStateChangesEqual(ret, "Temporary Account", keys=["description"]) diff --git a/tests/integration/states/test_virtualenv_mod.py b/tests/integration/states/test_virtualenv_mod.py index b38e2383e32..80da70b4937 100644 --- a/tests/integration/states/test_virtualenv_mod.py +++ b/tests/integration/states/test_virtualenv_mod.py @@ -1,102 +1,107 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.integration.states.virtualenv ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil -# Import Salt Testing libs -from tests.support.case import ModuleCase -from tests.support.unit import skipIf -from tests.support.helpers import destructiveTest, skip_if_not_root -from tests.support.mixins import SaltReturnAssertsMixin -from tests.support.runtests import RUNTIME_VARS - # Import Salt libs import salt.utils.files import salt.utils.path import salt.utils.platform from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +# Import Salt Testing libs +from tests.support.case import ModuleCase +from tests.support.helpers import destructiveTest, skip_if_not_root +from tests.support.mixins import SaltReturnAssertsMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf -@skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') + +@skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, "virtualenv not installed" +) class VirtualenvTest(ModuleCase, SaltReturnAssertsMixin): - @skipIf(salt.utils.platform.is_darwin(), 'Test is flaky on macosx') + @skipIf(salt.utils.platform.is_darwin(), "Test is flaky on macosx") @destructiveTest @skip_if_not_root def test_issue_1959_virtualenv_runas(self): - user = 'issue-1959' - self.assertSaltTrueReturn(self.run_state('user.present', name=user)) + user = "issue-1959" + self.assertSaltTrueReturn(self.run_state("user.present", name=user)) - uinfo = self.run_function('user.info', [user]) + uinfo = self.run_function("user.info", [user]) if salt.utils.platform.is_darwin(): # MacOS does not support createhome with user.present - self.assertSaltTrueReturn(self.run_state('file.directory', name=uinfo['home'], user=user, group=uinfo['groups'][0], dir_mode=755)) - - venv_dir = os.path.join( - RUNTIME_VARS.SYS_TMP_DIR, 'issue-1959-virtualenv-runas' - ) - try: - ret = self.run_function( - 'state.sls', mods='issue-1959-virtualenv-runas' + self.assertSaltTrueReturn( + self.run_state( + "file.directory", + name=uinfo["home"], + user=user, + group=uinfo["groups"][0], + dir_mode=755, + ) ) + + venv_dir = os.path.join(RUNTIME_VARS.SYS_TMP_DIR, "issue-1959-virtualenv-runas") + try: + ret = self.run_function("state.sls", mods="issue-1959-virtualenv-runas") self.assertSaltTrueReturn(ret) # Lets check proper ownership - statinfo = self.run_function('file.stats', [venv_dir]) - self.assertEqual(statinfo['user'], uinfo['name']) - self.assertEqual(statinfo['uid'], uinfo['uid']) + statinfo = self.run_function("file.stats", [venv_dir]) + self.assertEqual(statinfo["user"], uinfo["name"]) + self.assertEqual(statinfo["uid"], uinfo["uid"]) finally: if os.path.isdir(venv_dir): shutil.rmtree(venv_dir) - self.assertSaltTrueReturn(self.run_state('user.absent', name=user, purge=True)) + self.assertSaltTrueReturn( + self.run_state("user.absent", name=user, purge=True) + ) - @skipIf(salt.utils.platform.is_darwin(), 'Test is flaky on macosx') + @skipIf(salt.utils.platform.is_darwin(), "Test is flaky on macosx") def test_issue_2594_non_invalidated_cache(self): # Testing virtualenv directory - venv_path = os.path.join(RUNTIME_VARS.TMP, 'issue-2594-ve') + venv_path = os.path.join(RUNTIME_VARS.TMP, "issue-2594-ve") if os.path.exists(venv_path): shutil.rmtree(venv_path) # Our virtualenv requirements file requirements_file_path = os.path.join( - RUNTIME_VARS.TMP_STATE_TREE, 'issue-2594-requirements.txt' + RUNTIME_VARS.TMP_STATE_TREE, "issue-2594-requirements.txt" ) if os.path.exists(requirements_file_path): os.unlink(requirements_file_path) # Our state template template = [ - '{0}:'.format(venv_path), - ' virtualenv.managed:', - ' - system_site_packages: False', - ' - clear: false', - ' - requirements: salt://issue-2594-requirements.txt', + "{0}:".format(venv_path), + " virtualenv.managed:", + " - system_site_packages: False", + " - clear: false", + " - requirements: salt://issue-2594-requirements.txt", ] - reqs = ['pep8==1.3.3', 'zope.interface==4.7.1'] + reqs = ["pep8==1.3.3", "zope.interface==4.7.1"] # Let's populate the requirements file, just pep-8 for now - with salt.utils.files.fopen(requirements_file_path, 'a') as fhw: - fhw.write(reqs[0] + '\n') + with salt.utils.files.fopen(requirements_file_path, "a") as fhw: + fhw.write(reqs[0] + "\n") # Let's run our state!!! try: - ret = self.run_function( - 'state.template_str', ['\n'.join(template)] - ) + ret = self.run_function("state.template_str", ["\n".join(template)]) self.assertSaltTrueReturn(ret) - self.assertInSaltComment('Created new virtualenv', ret) - self.assertSaltStateChangesEqual( - ret, [reqs[0]], keys=('packages', 'new') - ) + self.assertInSaltComment("Created new virtualenv", ret) + self.assertSaltStateChangesEqual(ret, [reqs[0]], keys=("packages", "new")) except AssertionError: # Always clean up the tests temp files if os.path.exists(venv_path): @@ -106,25 +111,21 @@ class VirtualenvTest(ModuleCase, SaltReturnAssertsMixin): raise # Let's make sure, it really got installed - ret = self.run_function('pip.freeze', bin_env=venv_path) + ret = self.run_function("pip.freeze", bin_env=venv_path) self.assertIn(reqs[0], ret) self.assertNotIn(reqs[1], ret) # Now let's update the requirements file, which is now cached. - with salt.utils.files.fopen(requirements_file_path, 'w') as fhw: - fhw.write(reqs[1] + '\n') + with salt.utils.files.fopen(requirements_file_path, "w") as fhw: + fhw.write(reqs[1] + "\n") # Let's run our state!!! try: - ret = self.run_function( - 'state.template_str', ['\n'.join(template)] - ) + ret = self.run_function("state.template_str", ["\n".join(template)]) self.assertSaltTrueReturn(ret) - self.assertInSaltComment('virtualenv exists', ret) - self.assertSaltStateChangesEqual( - ret, [reqs[1]], keys=('packages', 'new') - ) + self.assertInSaltComment("virtualenv exists", ret) + self.assertSaltStateChangesEqual(ret, [reqs[1]], keys=("packages", "new")) except AssertionError: # Always clean up the tests temp files if os.path.exists(venv_path): @@ -134,7 +135,7 @@ class VirtualenvTest(ModuleCase, SaltReturnAssertsMixin): raise # Let's make sure, it really got installed - ret = self.run_function('pip.freeze', bin_env=venv_path) + ret = self.run_function("pip.freeze", bin_env=venv_path) self.assertIn(reqs[0], ret) self.assertIn(reqs[1], ret) diff --git a/tests/integration/states/test_x509.py b/tests/integration/states/test_x509.py index c1aa061006c..0a720f8575a 100644 --- a/tests/integration/states/test_x509.py +++ b/tests/integration/states/test_x509.py @@ -1,20 +1,21 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, unicode_literals -import os + import logging +import os +import textwrap import salt.utils.files from salt.ext import six -import textwrap - -from tests.support.helpers import with_tempfile from tests.support.case import ModuleCase -from tests.support.unit import skipIf +from tests.support.helpers import with_tempfile from tests.support.mixins import SaltReturnAssertsMixin from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf try: import M2Crypto # pylint: disable=W0611 + HAS_M2CRYPTO = True except ImportError: HAS_M2CRYPTO = False @@ -23,18 +24,21 @@ except ImportError: log = logging.getLogger(__name__) -@skipIf(not HAS_M2CRYPTO, 'Skip when no M2Crypto found') +@skipIf(not HAS_M2CRYPTO, "Skip when no M2Crypto found") class x509Test(ModuleCase, SaltReturnAssertsMixin): - @classmethod def setUpClass(cls): - cert_path = os.path.join(RUNTIME_VARS.BASE_FILES, 'x509_test.crt') + cert_path = os.path.join(RUNTIME_VARS.BASE_FILES, "x509_test.crt") with salt.utils.files.fopen(cert_path) as fp: cls.x509_cert_text = fp.read() def setUp(self): - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'signing_policies.sls'), 'w') as fp: - fp.write(textwrap.dedent('''\ + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "signing_policies.sls"), "w" + ) as fp: + fp.write( + textwrap.dedent( + """\ x509_signing_policies: ca_policy: - minions: '*' @@ -48,58 +52,70 @@ class x509Test(ModuleCase, SaltReturnAssertsMixin): - authorityKeyIdentifier: keyid - days_valid: 730 - copypath: {0}/pki - '''.format(RUNTIME_VARS.TMP))) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls'), 'w') as fp: - fp.write(textwrap.dedent('''\ + """.format( + RUNTIME_VARS.TMP + ) + ) + ) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls"), "w" + ) as fp: + fp.write( + textwrap.dedent( + """\ base: '*': - signing_policies - ''')) - self.run_function('saltutil.refresh_pillar') + """ + ) + ) + self.run_function("saltutil.refresh_pillar") def tearDown(self): - os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'signing_policies.sls')) - os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, 'top.sls')) - certs_path = os.path.join(RUNTIME_VARS.TMP, 'pki') + os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "signing_policies.sls")) + os.remove(os.path.join(RUNTIME_VARS.TMP_PILLAR_TREE, "top.sls")) + certs_path = os.path.join(RUNTIME_VARS.TMP, "pki") if os.path.exists(certs_path): salt.utils.files.rm_rf(certs_path) - self.run_function('saltutil.refresh_pillar') + self.run_function("saltutil.refresh_pillar") def run_function(self, *args, **kwargs): # pylint: disable=arguments-differ ret = super(x509Test, self).run_function(*args, **kwargs) - log.debug('ret = %s', ret) + log.debug("ret = %s", ret) return ret - @with_tempfile(suffix='.pem', create=False) + @with_tempfile(suffix=".pem", create=False) def test_issue_49027(self, pemfile): - ret = self.run_state( - 'x509.pem_managed', - name=pemfile, - text=self.x509_cert_text) + ret = self.run_state("x509.pem_managed", name=pemfile, text=self.x509_cert_text) assert isinstance(ret, dict), ret ret = ret[next(iter(ret))] - assert ret.get('result') is True, ret + assert ret.get("result") is True, ret with salt.utils.files.fopen(pemfile) as fp: result = fp.readlines() self.assertEqual(self.x509_cert_text.splitlines(True), result) - @with_tempfile(suffix='.crt', create=False) - @with_tempfile(suffix='.key', create=False) + @with_tempfile(suffix=".crt", create=False) + @with_tempfile(suffix=".key", create=False) def test_issue_49008(self, keyfile, crtfile): ret = self.run_function( - 'state.apply', - ['issue-49008'], - pillar={'keyfile': keyfile, 'crtfile': crtfile}) + "state.apply", + ["issue-49008"], + pillar={"keyfile": keyfile, "crtfile": crtfile}, + ) assert isinstance(ret, dict), ret for state_result in six.itervalues(ret): - assert state_result['result'] is True, state_result + assert state_result["result"] is True, state_result assert os.path.exists(keyfile) assert os.path.exists(crtfile) def test_cert_signing(self): - ret = self.run_function('state.apply', ['test_cert'], pillar={'tmp_dir': RUNTIME_VARS.TMP}) - key = 'x509_|-test_crt_|-{}/pki/test.crt_|-certificate_managed'.format(RUNTIME_VARS.TMP) + ret = self.run_function( + "state.apply", ["test_cert"], pillar={"tmp_dir": RUNTIME_VARS.TMP} + ) + key = "x509_|-test_crt_|-{}/pki/test.crt_|-certificate_managed".format( + RUNTIME_VARS.TMP + ) assert key in ret - assert 'changes' in ret[key] - assert 'Certificate' in ret[key]['changes'] - assert 'New' in ret[key]['changes']['Certificate'] + assert "changes" in ret[key] + assert "Certificate" in ret[key]["changes"] + assert "New" in ret[key]["changes"]["Certificate"] diff --git a/tests/integration/states/test_zookeeper.py b/tests/integration/states/test_zookeeper.py index dd21cce2a05..4c72aaa73ff 100644 --- a/tests/integration/states/test_zookeeper.py +++ b/tests/integration/states/test_zookeeper.py @@ -1,23 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" Integration tests for the zookeeper states -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt Testing Libs -from tests.support.unit import skipIf +# Import Salt Libs +import salt.utils.path from tests.support.case import ModuleCase from tests.support.helpers import destructiveTest from tests.support.mixins import SaltReturnAssertsMixin -# Import Salt Libs -import salt.utils.path +# Import Salt Testing Libs +from tests.support.unit import skipIf try: import kazoo # pylint: disable=import-error,unused-import + HAS_KAZOO = True except ImportError: HAS_KAZOO = False @@ -26,89 +28,102 @@ log = logging.getLogger(__name__) @destructiveTest -@skipIf(not salt.utils.path.which('dockerd'), 'Docker not installed') -@skipIf(not HAS_KAZOO, 'kazoo python library not installed') +@skipIf(not salt.utils.path.which("dockerd"), "Docker not installed") +@skipIf(not HAS_KAZOO, "kazoo python library not installed") class ZookeeperTestCase(ModuleCase, SaltReturnAssertsMixin): - ''' + """ Test zookeeper states - ''' + """ + @classmethod def setUpClass(cls): - cls.container_name = 'zookeeper_salt' + cls.container_name = "zookeeper_salt" def setUp(self): - self.run_state('docker_image.present', name='zookeeper') - self.run_state('docker_container.running', name=self.container_name, image='zookeeper', port_bindings='2181:2181') + self.run_state("docker_image.present", name="zookeeper") + self.run_state( + "docker_container.running", + name=self.container_name, + image="zookeeper", + port_bindings="2181:2181", + ) def tearDown(self): - self.run_state('docker_container.stopped', name=self.container_name) - self.run_state('docker_container.absent', name=self.container_name) - self.run_state('docker_image.absent', name='docker.io/zookeeper', force=True) + self.run_state("docker_container.stopped", name=self.container_name) + self.run_state("docker_container.absent", name=self.container_name) + self.run_state("docker_image.absent", name="docker.io/zookeeper", force=True) def test_zookeeper_present(self): ret = self.run_state( - 'zookeeper.present', - name='/test/name', - value='testuser', - makepath=True, + "zookeeper.present", name="/test/name", value="testuser", makepath=True, ) self.assertSaltTrueReturn(ret) ret = self.run_state( - 'zookeeper.present', - name='/test/name', - value='daniel', + "zookeeper.present", + name="/test/name", + value="daniel", acls=[ - {'username': 'daniel', 'password': 'test', 'read': True, 'admin': True, 'write': True, }, - {'username': 'testuser', 'password': 'test', 'read': True}, + { + "username": "daniel", + "password": "test", + "read": True, + "admin": True, + "write": True, + }, + {"username": "testuser", "password": "test", "read": True}, ], - profile='prod', + profile="prod", ) self.assertSaltTrueReturn(ret) def test_zookeeper_absent(self): self.run_state( - 'zookeeper.present', - name='/test/name', - value='testuser', - makepath=True, - ) - ret = self.run_state( - 'zookeeper.absent', - name='/test/name', + "zookeeper.present", name="/test/name", value="testuser", makepath=True, ) + ret = self.run_state("zookeeper.absent", name="/test/name",) self.assertSaltTrueReturn(ret) - self.assertTrue(bool(ret['zookeeper_|-/test/name_|-/test/name_|-absent']['changes'])) - ret = self.run_state( - 'zookeeper.absent', - name='/test/name', + self.assertTrue( + bool(ret["zookeeper_|-/test/name_|-/test/name_|-absent"]["changes"]) + ) + ret = self.run_state("zookeeper.absent", name="/test/name",) + self.assertFalse( + bool(ret["zookeeper_|-/test/name_|-/test/name_|-absent"]["changes"]) ) - self.assertFalse(bool(ret['zookeeper_|-/test/name_|-/test/name_|-absent']['changes'])) def test_zookeeper_acls(self): ret = self.run_state( - 'zookeeper.acls', - name='/test/name', + "zookeeper.acls", + name="/test/name", acls=[ - {'username': 'daniel', 'password': 'test', 'read': True, 'admin': True, 'write': True, }, - {'username': 'testuser', 'password': 'test', 'read': True}, - ] + { + "username": "daniel", + "password": "test", + "read": True, + "admin": True, + "write": True, + }, + {"username": "testuser", "password": "test", "read": True}, + ], ) self.assertSaltFalseReturn(ret) ret = self.run_state( - 'zookeeper.present', - name='/test/name', - value='testuser', - makepath=True, + "zookeeper.present", name="/test/name", value="testuser", makepath=True, ) ret = self.run_state( - 'zookeeper.acls', - name='/test/name', + "zookeeper.acls", + name="/test/name", acls=[ - {'username': 'daniel', 'password': 'test', 'read': True, 'admin': True, 'write': True, }, - {'username': 'testuser', 'password': 'test', 'read': True}, - ] + { + "username": "daniel", + "password": "test", + "read": True, + "admin": True, + "write": True, + }, + {"username": "testuser", "password": "test", "read": True}, + ], ) self.assertSaltTrueReturn(ret) diff --git a/tests/integration/utils/__init__.py b/tests/integration/utils/__init__.py index 5b91b463586..a656be2a221 100644 --- a/tests/integration/utils/__init__.py +++ b/tests/integration/utils/__init__.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Some utils functions to be used throughout the integration test files. -''' +""" def decode_byte_list(byte_list): - ''' + """ Helper function that takes a list of byte strings and decodes each item according to the __salt_system_encoding__ value. Returns a list of strings. - ''' + """ decoded_items = [] for item in byte_list: decoded_items.append(item.decode(__salt_system_encoding__)) diff --git a/tests/integration/utils/test_smb.py b/tests/integration/utils/test_smb.py index 04eac392f63..08f85e19901 100644 --- a/tests/integration/utils/test_smb.py +++ b/tests/integration/utils/test_smb.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" Test utility methods that communicate with SMB shares. -''' +""" from __future__ import absolute_import + import getpass import logging import os @@ -14,60 +15,59 @@ import time import salt.utils.files import salt.utils.path import salt.utils.smb - -from tests.support.unit import skipIf from tests.support.case import TestCase +from tests.support.unit import skipIf log = logging.getLogger(__name__) CONFIG = ( - '[global]\n' - 'realm = saltstack.com\n' - 'interfaces = lo 127.0.0.0/8\n' - 'smb ports = 1445\n' - 'log level = 2\n' - 'map to guest = Bad User\n' - 'enable core files = no\n' - 'passdb backend = smbpasswd\n' - 'smb passwd file = {passwdb}\n' - 'lock directory = {samba_dir}\n' - 'state directory = {samba_dir}\n' - 'cache directory = {samba_dir}\n' - 'pid directory = {samba_dir}\n' - 'private dir = {samba_dir}\n' - 'ncalrpc dir = {samba_dir}\n' - 'socket options = IPTOS_LOWDELAY TCP_NODELAY\n' - 'min receivefile size = 0\n' - 'write cache size = 0\n' - 'client ntlmv2 auth = no\n' - 'client min protocol = SMB3_11\n' - 'client plaintext auth = no\n' - '\n' - '[public]\n' - 'path = {public_dir}\n' - 'read only = no\n' - 'guest ok = no\n' - 'writeable = yes\n' - 'force user = {user}\n' + "[global]\n" + "realm = saltstack.com\n" + "interfaces = lo 127.0.0.0/8\n" + "smb ports = 1445\n" + "log level = 2\n" + "map to guest = Bad User\n" + "enable core files = no\n" + "passdb backend = smbpasswd\n" + "smb passwd file = {passwdb}\n" + "lock directory = {samba_dir}\n" + "state directory = {samba_dir}\n" + "cache directory = {samba_dir}\n" + "pid directory = {samba_dir}\n" + "private dir = {samba_dir}\n" + "ncalrpc dir = {samba_dir}\n" + "socket options = IPTOS_LOWDELAY TCP_NODELAY\n" + "min receivefile size = 0\n" + "write cache size = 0\n" + "client ntlmv2 auth = no\n" + "client min protocol = SMB3_11\n" + "client plaintext auth = no\n" + "\n" + "[public]\n" + "path = {public_dir}\n" + "read only = no\n" + "guest ok = no\n" + "writeable = yes\n" + "force user = {user}\n" ) TBE = ( - '{}:0:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:AC8E657F8' - '3DF82BEEA5D43BDAF7800CC:[U ]:LCT-507C14C7:' + "{}:0:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:AC8E657F8" + "3DF82BEEA5D43BDAF7800CC:[U ]:LCT-507C14C7:" ) def which_smbd(): - ''' + """ Find the smbd executable and cache the result if it exits. - ''' - if hasattr(which_smbd, 'cached_result'): + """ + if hasattr(which_smbd, "cached_result"): return which_smbd.cached_result - smbd = salt.utils.path.which('smbd') + smbd = salt.utils.path.which("smbd") if smbd: which_smbd.cached_result = smbd return smbd -@skipIf(not which_smbd(), 'Skip when no smbd binary found') +@skipIf(not which_smbd(), "Skip when no smbd binary found") class TestSmb(TestCase): _smbd = None @@ -84,18 +84,18 @@ class TestSmb(TestCase): @classmethod def setUpClass(cls): tmpdir = tempfile.mkdtemp() - cls.samba_dir = os.path.join(tmpdir, 'samba') - cls.public_dir = os.path.join(tmpdir, 'public') + cls.samba_dir = os.path.join(tmpdir, "samba") + cls.public_dir = os.path.join(tmpdir, "public") os.makedirs(cls.samba_dir) os.makedirs(cls.public_dir) os.chmod(cls.samba_dir, 0o775) os.chmod(cls.public_dir, 0o775) - passwdb = os.path.join(tmpdir, 'passwdb') + passwdb = os.path.join(tmpdir, "passwdb") cls.username = getpass.getuser() - with salt.utils.files.fopen(passwdb, 'w') as fp: + with salt.utils.files.fopen(passwdb, "w") as fp: fp.write(TBE.format(cls.username)) - samba_conf = os.path.join(tmpdir, 'smb.conf') - with salt.utils.files.fopen(samba_conf, 'w') as fp: + samba_conf = os.path.join(tmpdir, "smb.conf") + with salt.utils.files.fopen(samba_conf, "w") as fp: fp.write( CONFIG.format( samba_dir=cls.samba_dir, @@ -105,114 +105,113 @@ class TestSmb(TestCase): ) ) cls._smbd = subprocess.Popen( - '{0} -FS -P0 -s {1}'.format(which_smbd(), samba_conf), - shell=True + "{0} -FS -P0 -s {1}".format(which_smbd(), samba_conf), shell=True ) time.sleep(1) - pidfile = os.path.join(cls.samba_dir, 'smbd.pid') - with salt.utils.files.fopen(pidfile, 'r') as fp: + pidfile = os.path.join(cls.samba_dir, "smbd.pid") + with salt.utils.files.fopen(pidfile, "r") as fp: cls._pid = int(fp.read().strip()) if not cls.check_pid(cls._pid): - raise Exception('Unable to locate smbd\'s pid file') + raise Exception("Unable to locate smbd's pid file") @classmethod def tearDownClass(cls): - log.warning('teardown') + log.warning("teardown") os.kill(cls._pid, signal.SIGTERM) def test_write_file(self): - ''' + """ Transfer a file over SMB - ''' - name = 'test_write_file.txt' - content = 'write test file content' + """ + name = "test_write_file.txt" + content = "write test file content" share_path = os.path.join(self.public_dir, name) assert not os.path.exists(share_path) local_path = tempfile.mktemp() - with salt.utils.files.fopen(local_path, 'w') as fp: + with salt.utils.files.fopen(local_path, "w") as fp: fp.write(content) - conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) - salt.utils.smb.put_file(local_path, name, 'public', conn=conn) + conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445) + salt.utils.smb.put_file(local_path, name, "public", conn=conn) conn.close() assert os.path.exists(share_path) - with salt.utils.files.fopen(share_path, 'r') as fp: + with salt.utils.files.fopen(share_path, "r") as fp: result = fp.read() assert result == content def test_write_str(self): - ''' + """ Write a string to a file over SMB - ''' - name = 'test_write_str.txt' - content = 'write test file content' + """ + name = "test_write_str.txt" + content = "write test file content" share_path = os.path.join(self.public_dir, name) assert not os.path.exists(share_path) - conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) - salt.utils.smb.put_str(content, name, 'public', conn=conn) + conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445) + salt.utils.smb.put_str(content, name, "public", conn=conn) conn.close() assert os.path.exists(share_path) - with salt.utils.files.fopen(share_path, 'r') as fp: + with salt.utils.files.fopen(share_path, "r") as fp: result = fp.read() assert result == content def test_delete_file(self): - ''' + """ Validate deletion of files over SMB - ''' - name = 'test_delete_file.txt' - content = 'read test file content' + """ + name = "test_delete_file.txt" + content = "read test file content" share_path = os.path.join(self.public_dir, name) - with salt.utils.files.fopen(share_path, 'w') as fp: + with salt.utils.files.fopen(share_path, "w") as fp: fp.write(content) assert os.path.exists(share_path) - conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) - salt.utils.smb.delete_file(name, 'public', conn=conn) + conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445) + salt.utils.smb.delete_file(name, "public", conn=conn) conn.close() assert not os.path.exists(share_path) def test_mkdirs(self): - ''' + """ Create directories over SMB - ''' - dir_name = 'mkdirs/test' + """ + dir_name = "mkdirs/test" share_path = os.path.join(self.public_dir, dir_name) assert not os.path.exists(share_path) - conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) - salt.utils.smb.mkdirs(dir_name, 'public', conn=conn) + conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445) + salt.utils.smb.mkdirs(dir_name, "public", conn=conn) conn.close() assert os.path.exists(share_path) def test_delete_dirs(self): - ''' + """ Validate deletion of directoreies over SMB - ''' - dir_name = 'deldirs' - subdir_name = 'deldirs/test' + """ + dir_name = "deldirs" + subdir_name = "deldirs/test" local_path = os.path.join(self.public_dir, subdir_name) os.makedirs(local_path) assert os.path.exists(local_path) - conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) - salt.utils.smb.delete_directory(subdir_name, 'public', conn=conn) + conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445) + salt.utils.smb.delete_directory(subdir_name, "public", conn=conn) conn.close() - conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) - salt.utils.smb.delete_directory(dir_name, 'public', conn=conn) + conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445) + salt.utils.smb.delete_directory(dir_name, "public", conn=conn) conn.close() assert not os.path.exists(local_path) assert not os.path.exists(os.path.join(self.public_dir, dir_name)) def test_connection(self): - ''' + """ Validate creation of an SMB connection - ''' - conn = salt.utils.smb.get_conn('127.0.0.1', self.username, 'foo', port=1445) + """ + conn = salt.utils.smb.get_conn("127.0.0.1", self.username, "foo", port=1445) conn.close() diff --git a/tests/integration/utils/test_thin.py b/tests/integration/utils/test_thin.py index f315d5fca58..a83d918eb22 100644 --- a/tests/integration/utils/test_thin.py +++ b/tests/integration/utils/test_thin.py @@ -1,24 +1,25 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -import tarfile -import tempfile + +import os import subprocess import sys -import os - -from tests.support.unit import TestCase +import tarfile +import tempfile import salt.utils.files import salt.utils.thin +from tests.support.unit import TestCase + try: import virtualenv + HAS_VENV = True except ImportError: HAS_VENV = False class TestThinDir(TestCase): - def setUp(self): self.tmpdir = tempfile.mkdtemp() @@ -26,27 +27,27 @@ class TestThinDir(TestCase): salt.utils.files.rm_rf(self.tmpdir) def test_thin_dir(self): - ''' + """ Test the thin dir to make sure salt-call can run Run salt call via a python in a new virtual environment to ensure salt-call has all dependencies needed. - ''' - venv_dir = os.path.join(self.tmpdir, 'venv') + """ + venv_dir = os.path.join(self.tmpdir, "venv") virtualenv.create_environment(venv_dir) salt.utils.thin.gen_thin(self.tmpdir) - thin_dir = os.path.join(self.tmpdir, 'thin') - thin_archive = os.path.join(thin_dir, 'thin.tgz') + thin_dir = os.path.join(self.tmpdir, "thin") + thin_archive = os.path.join(thin_dir, "thin.tgz") tar = tarfile.open(thin_archive) tar.extractall(thin_dir) tar.close() - bins = 'bin' - if sys.platform == 'win32': - bins = 'Scripts' + bins = "bin" + if sys.platform == "win32": + bins = "Scripts" cmd = [ - os.path.join(venv_dir, bins, 'python'), - os.path.join(thin_dir, 'salt-call'), - '--version', + os.path.join(venv_dir, bins, "python"), + os.path.join(thin_dir, "salt-call"), + "--version", ] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() diff --git a/tests/integration/utils/test_win_runas.py b/tests/integration/utils/test_win_runas.py index a662770c172..6a5d11c2568 100644 --- a/tests/integration/utils/test_win_runas.py +++ b/tests/integration/utils/test_win_runas.py @@ -1,31 +1,29 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, unicode_literals -import textwrap -import subprocess -import socket + import inspect import io +import logging +import os +import socket +import subprocess # Service manager imports import sys -import os -import logging +import textwrap import threading -import traceback import time +import traceback -import yaml -from tests.support.case import ModuleCase -from tests.support.mock import Mock -from tests.support.unit import skipIf -from tests.support.runtests import RUNTIME_VARS - -from tests.support.helpers import ( - with_system_user, -) +import salt.ext.six import salt.utils.files import salt.utils.win_runas -import salt.ext.six +import yaml +from tests.support.case import ModuleCase +from tests.support.helpers import with_system_user +from tests.support.mock import Mock +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import skipIf try: import win32service @@ -33,6 +31,7 @@ try: import win32event import servicemanager import win32api + CODE_DIR = win32api.GetLongPathName(RUNTIME_VARS.CODE_DIR) HAS_WIN32 = True except ImportError: @@ -43,18 +42,18 @@ except ImportError: logger = logging.getLogger(__name__) -PASSWORD = 'P@ssW0rd' -NOPRIV_STDERR = 'ERROR: Logged-on user does not have administrative privilege.\n' +PASSWORD = "P@ssW0rd" +NOPRIV_STDERR = "ERROR: Logged-on user does not have administrative privilege.\n" PRIV_STDOUT = ( - '\nINFO: The system global flag \'maintain objects list\' needs\n ' - 'to be enabled to see local opened files.\n See Openfiles ' - '/? for more information.\n\n\nFiles opened remotely via local share ' - 'points:\n---------------------------------------------\n\n' - 'INFO: No shared open files found.\n' + "\nINFO: The system global flag 'maintain objects list' needs\n " + "to be enabled to see local opened files.\n See Openfiles " + "/? for more information.\n\n\nFiles opened remotely via local share " + "points:\n---------------------------------------------\n\n" + "INFO: No shared open files found.\n" ) if HAS_WIN32: - RUNAS_PATH = os.path.abspath(os.path.join(CODE_DIR, 'runas.py')) - RUNAS_OUT = os.path.abspath(os.path.join(CODE_DIR, 'runas.out')) + RUNAS_PATH = os.path.abspath(os.path.join(CODE_DIR, "runas.py")) + RUNAS_OUT = os.path.abspath(os.path.join(CODE_DIR, "runas.out")) def default_target(service, *args, **kwargs): @@ -63,9 +62,9 @@ def default_target(service, *args, **kwargs): class _ServiceManager(win32serviceutil.ServiceFramework): - ''' + """ A windows service manager - ''' + """ _svc_name_ = "Service Manager" _svc_display_name_ = "Service Manager" @@ -123,7 +122,7 @@ class _ServiceManager(win32serviceutil.ServiceFramework): servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, - (self._svc_name_, ''), + (self._svc_name_, ""), ) self.log_info("Starting Service {}".format(self._svc_name_)) monitor_thread = threading.Thread(target=self.target_thread) @@ -152,7 +151,7 @@ class _ServiceManager(win32serviceutil.ServiceFramework): @classmethod def install(cls, username=None, password=None, start_type=None): - if hasattr(cls, '_svc_reg_class_'): + if hasattr(cls, "_svc_reg_class_"): svc_class = cls._svc_reg_class_ else: svc_class = win32serviceutil.GetServiceClassString(cls) @@ -168,30 +167,29 @@ class _ServiceManager(win32serviceutil.ServiceFramework): @classmethod def remove(cls): - win32serviceutil.RemoveService( - cls._svc_name_ - ) + win32serviceutil.RemoveService(cls._svc_name_) @classmethod def start(cls): - win32serviceutil.StartService( - cls._svc_name_ - ) + win32serviceutil.StartService(cls._svc_name_) @classmethod def restart(cls): - win32serviceutil.RestartService( - cls._svc_name_ - ) + win32serviceutil.RestartService(cls._svc_name_) @classmethod def stop(cls): - win32serviceutil.StopService( - cls._svc_name_ - ) + win32serviceutil.StopService(cls._svc_name_) -def service_class_factory(cls_name, name, target=default_target, display_name='', description='', run_in_foreground=False): +def service_class_factory( + cls_name, + name, + target=default_target, + display_name="", + description="", + run_in_foreground=False, +): frm = inspect.stack()[1] mod = inspect.getmodule(frm[0]) if salt.ext.six.PY2: @@ -200,21 +198,21 @@ def service_class_factory(cls_name, name, target=default_target, display_name='' cls_name, (_ServiceManager, object), { - '__module__': mod.__name__, - '_svc_name_': name, - '_svc_display_name_': display_name or name, - '_svc_description_': description, - 'run_in_foreground': run_in_foreground, - 'target': target, + "__module__": mod.__name__, + "_svc_name_": name, + "_svc_display_name_": display_name or name, + "_svc_description_": description, + "run_in_foreground": run_in_foreground, + "target": target, }, ) if HAS_WIN32: - test_service = service_class_factory('test_service', 'test service') + test_service = service_class_factory("test_service", "test service") -SERVICE_SOURCE = ''' +SERVICE_SOURCE = """ from __future__ import absolute_import, unicode_literals import logging logger = logging.getLogger() @@ -264,7 +262,7 @@ if __name__ == '__main__': logger.debug("remove service failed, this os ok.") test_service.install() sys.exit(0) -''' +""" def wait_for_service(name, timeout=200): @@ -274,353 +272,419 @@ def wait_for_service(name, timeout=200): if status[1] == win32service.SERVICE_STOPPED: break if time.time() - start > timeout: - raise TimeoutError("Timeout waiting for service") # pylint: disable=undefined-variable + raise TimeoutError( + "Timeout waiting for service" + ) # pylint: disable=undefined-variable - time.sleep(.3) + time.sleep(0.3) -@skipIf(not HAS_WIN32, 'This test runs only on windows.') +@skipIf(not HAS_WIN32, "This test runs only on windows.") class RunAsTest(ModuleCase): - @classmethod def setUpClass(cls): super(RunAsTest, cls).setUpClass() cls.hostname = socket.gethostname() - @with_system_user('test-runas', on_existing='delete', delete=True, - password=PASSWORD) + @with_system_user( + "test-runas", on_existing="delete", delete=True, password=PASSWORD + ) def test_runas(self, username): - ret = salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username, PASSWORD) - self.assertEqual(ret['stdout'], '') - self.assertEqual(ret['stderr'], NOPRIV_STDERR) - self.assertEqual(ret['retcode'], 1) + ret = salt.utils.win_runas.runas("cmd.exe /C OPENFILES", username, PASSWORD) + self.assertEqual(ret["stdout"], "") + self.assertEqual(ret["stderr"], NOPRIV_STDERR) + self.assertEqual(ret["retcode"], 1) - @with_system_user('test-runas', on_existing='delete', delete=True, - password=PASSWORD) + @with_system_user( + "test-runas", on_existing="delete", delete=True, password=PASSWORD + ) def test_runas_no_pass(self, username): - ret = salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username) - self.assertEqual(ret['stdout'], '') - self.assertEqual(ret['stderr'], NOPRIV_STDERR) - self.assertEqual(ret['retcode'], 1) + ret = salt.utils.win_runas.runas("cmd.exe /C OPENFILES", username) + self.assertEqual(ret["stdout"], "") + self.assertEqual(ret["stderr"], NOPRIV_STDERR) + self.assertEqual(ret["retcode"], 1) - @with_system_user('test-runas-admin', on_existing='delete', delete=True, - password=PASSWORD, groups=['Administrators']) + @with_system_user( + "test-runas-admin", + on_existing="delete", + delete=True, + password=PASSWORD, + groups=["Administrators"], + ) def test_runas_admin(self, username): - ret = salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username, PASSWORD) - self.assertEqual(ret['stdout'], PRIV_STDOUT) - self.assertEqual(ret['stderr'], '') - self.assertEqual(ret['retcode'], 0) + ret = salt.utils.win_runas.runas("cmd.exe /C OPENFILES", username, PASSWORD) + self.assertEqual(ret["stdout"], PRIV_STDOUT) + self.assertEqual(ret["stderr"], "") + self.assertEqual(ret["retcode"], 0) - @with_system_user('test-runas-admin', on_existing='delete', delete=True, - password=PASSWORD, groups=['Administrators']) + @with_system_user( + "test-runas-admin", + on_existing="delete", + delete=True, + password=PASSWORD, + groups=["Administrators"], + ) def test_runas_admin_no_pass(self, username): - ret = salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username) - self.assertEqual(ret['stdout'], PRIV_STDOUT) - self.assertEqual(ret['stderr'], '') - self.assertEqual(ret['retcode'], 0) + ret = salt.utils.win_runas.runas("cmd.exe /C OPENFILES", username) + self.assertEqual(ret["stdout"], PRIV_STDOUT) + self.assertEqual(ret["stderr"], "") + self.assertEqual(ret["retcode"], 0) def test_runas_system_user(self): - ret = salt.utils.win_runas.runas('cmd.exe /C OPENFILES', 'SYSTEM') - self.assertEqual(ret['stdout'], PRIV_STDOUT) - self.assertEqual(ret['stderr'], '') - self.assertEqual(ret['retcode'], 0) + ret = salt.utils.win_runas.runas("cmd.exe /C OPENFILES", "SYSTEM") + self.assertEqual(ret["stdout"], PRIV_STDOUT) + self.assertEqual(ret["stderr"], "") + self.assertEqual(ret["retcode"], 0) def test_runas_network_service(self): - ret = salt.utils.win_runas.runas('cmd.exe /C OPENFILES', 'NETWORK SERVICE') - self.assertEqual(ret['stdout'], '') - self.assertEqual(ret['stderr'], NOPRIV_STDERR) - self.assertEqual(ret['retcode'], 1) + ret = salt.utils.win_runas.runas("cmd.exe /C OPENFILES", "NETWORK SERVICE") + self.assertEqual(ret["stdout"], "") + self.assertEqual(ret["stderr"], NOPRIV_STDERR) + self.assertEqual(ret["retcode"], 1) def test_runas_local_service(self): - ret = salt.utils.win_runas.runas('cmd.exe /C OPENFILES', 'LOCAL SERVICE') - self.assertEqual(ret['stdout'], '') - self.assertEqual(ret['stderr'], NOPRIV_STDERR) - self.assertEqual(ret['retcode'], 1) + ret = salt.utils.win_runas.runas("cmd.exe /C OPENFILES", "LOCAL SERVICE") + self.assertEqual(ret["stdout"], "") + self.assertEqual(ret["stderr"], NOPRIV_STDERR) + self.assertEqual(ret["retcode"], 1) - @with_system_user('test-runas', on_existing='delete', delete=True, - password=PASSWORD) + @with_system_user( + "test-runas", on_existing="delete", delete=True, password=PASSWORD + ) def test_runas_winrs(self, username): - runaspy = textwrap.dedent(''' + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas username = '{}' password = '{}' sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username, password)['retcode']) - '''.format(username, PASSWORD)) - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: + """.format( + username, PASSWORD + ) + ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: fp.write(runaspy) - ret = subprocess.call("cmd.exe /C winrs /r:{} python {}".format( - self.hostname, RUNAS_PATH), shell=True) + ret = subprocess.call( + "cmd.exe /C winrs /r:{} python {}".format(self.hostname, RUNAS_PATH), + shell=True, + ) self.assertEqual(ret, 1) - @with_system_user('test-runas', on_existing='delete', delete=True, - password=PASSWORD) + @with_system_user( + "test-runas", on_existing="delete", delete=True, password=PASSWORD + ) def test_runas_winrs_no_pass(self, username): - runaspy = textwrap.dedent(''' + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas username = '{}' sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username)['retcode']) - '''.format(username)) - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: + """.format( + username + ) + ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: fp.write(runaspy) - ret = subprocess.call("cmd.exe /C winrs /r:{} python {}".format( - self.hostname, RUNAS_PATH), shell=True) + ret = subprocess.call( + "cmd.exe /C winrs /r:{} python {}".format(self.hostname, RUNAS_PATH), + shell=True, + ) self.assertEqual(ret, 1) - @with_system_user('test-runas-admin', on_existing='delete', delete=True, - password=PASSWORD, groups=['Administrators']) + @with_system_user( + "test-runas-admin", + on_existing="delete", + delete=True, + password=PASSWORD, + groups=["Administrators"], + ) def test_runas_winrs_admin(self, username): - runaspy = textwrap.dedent(''' + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas username = '{}' password = '{}' sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username, password)['retcode']) - '''.format(username, PASSWORD)) - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: + """.format( + username, PASSWORD + ) + ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: fp.write(runaspy) - ret = subprocess.call("cmd.exe /C winrs /r:{} python {}".format( - self.hostname, RUNAS_PATH), shell=True) + ret = subprocess.call( + "cmd.exe /C winrs /r:{} python {}".format(self.hostname, RUNAS_PATH), + shell=True, + ) self.assertEqual(ret, 0) - @with_system_user('test-runas-admin', on_existing='delete', delete=True, - password=PASSWORD, groups=['Administrators']) + @with_system_user( + "test-runas-admin", + on_existing="delete", + delete=True, + password=PASSWORD, + groups=["Administrators"], + ) def test_runas_winrs_admin_no_pass(self, username): - runaspy = textwrap.dedent(''' + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas username = '{}' sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username)['retcode']) - '''.format(username)) - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: + """.format( + username + ) + ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: fp.write(runaspy) - ret = subprocess.call("cmd.exe /C winrs /r:{} python {}".format( - self.hostname, RUNAS_PATH), shell=True) + ret = subprocess.call( + "cmd.exe /C winrs /r:{} python {}".format(self.hostname, RUNAS_PATH), + shell=True, + ) self.assertEqual(ret, 0) def test_runas_winrs_system_user(self): - runaspy = textwrap.dedent(''' + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', 'SYSTEM')['retcode']) - ''') - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: + """ + ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: fp.write(runaspy) - ret = subprocess.call("cmd.exe /C winrs /r:{} python {}".format( - self.hostname, RUNAS_PATH), shell=True) + ret = subprocess.call( + "cmd.exe /C winrs /r:{} python {}".format(self.hostname, RUNAS_PATH), + shell=True, + ) self.assertEqual(ret, 0) def test_runas_winrs_network_service_user(self): - runaspy = textwrap.dedent(''' + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', 'NETWORK SERVICE')['retcode']) - ''') - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: + """ + ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: fp.write(runaspy) - ret = subprocess.call("cmd.exe /C winrs /r:{} python {}".format( - self.hostname, RUNAS_PATH), shell=True) + ret = subprocess.call( + "cmd.exe /C winrs /r:{} python {}".format(self.hostname, RUNAS_PATH), + shell=True, + ) self.assertEqual(ret, 1) def test_runas_winrs_local_service_user(self): - runaspy = textwrap.dedent(''' + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', 'LOCAL SERVICE')['retcode']) - ''') - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: + """ + ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: fp.write(runaspy) - ret = subprocess.call("cmd.exe /C winrs /r:{} python {}".format( - self.hostname, RUNAS_PATH), shell=True) + ret = subprocess.call( + "cmd.exe /C winrs /r:{} python {}".format(self.hostname, RUNAS_PATH), + shell=True, + ) self.assertEqual(ret, 1) - @with_system_user('test-runas', on_existing='delete', delete=True, - password=PASSWORD) + @with_system_user( + "test-runas", on_existing="delete", delete=True, password=PASSWORD + ) def test_runas_powershell_remoting(self, username): - psrp_wrap = ( - 'powershell Invoke-Command -ComputerName {} -ScriptBlock {{ {} }}' - ) - runaspy = textwrap.dedent(''' + psrp_wrap = "powershell Invoke-Command -ComputerName {} -ScriptBlock {{ {} }}" + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas username = '{}' password = '{}' sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username, password)['retcode']) - '''.format(username, PASSWORD)) - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: - fp.write(runaspy) - cmd = 'python.exe {}'.format(RUNAS_PATH) - ret = subprocess.call( - psrp_wrap.format(self.hostname, cmd), - shell=True + """.format( + username, PASSWORD + ) ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: + fp.write(runaspy) + cmd = "python.exe {}".format(RUNAS_PATH) + ret = subprocess.call(psrp_wrap.format(self.hostname, cmd), shell=True) self.assertEqual(ret, 1) - @with_system_user('test-runas', on_existing='delete', delete=True, - password=PASSWORD) + @with_system_user( + "test-runas", on_existing="delete", delete=True, password=PASSWORD + ) def test_runas_powershell_remoting_no_pass(self, username): - psrp_wrap = ( - 'powershell Invoke-Command -ComputerName {} -ScriptBlock {{ {} }}' - ) - runaspy = textwrap.dedent(''' + psrp_wrap = "powershell Invoke-Command -ComputerName {} -ScriptBlock {{ {} }}" + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas username = '{}' sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username)['retcode']) - '''.format(username)) - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: - fp.write(runaspy) - cmd = 'python.exe {}'.format(RUNAS_PATH) - ret = subprocess.call( - psrp_wrap.format(self.hostname, cmd), - shell=True + """.format( + username + ) ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: + fp.write(runaspy) + cmd = "python.exe {}".format(RUNAS_PATH) + ret = subprocess.call(psrp_wrap.format(self.hostname, cmd), shell=True) self.assertEqual(ret, 1) - @with_system_user('test-runas-admin', on_existing='delete', delete=True, - password=PASSWORD, groups=['Administrators']) + @with_system_user( + "test-runas-admin", + on_existing="delete", + delete=True, + password=PASSWORD, + groups=["Administrators"], + ) def test_runas_powershell_remoting_admin(self, username): - psrp_wrap = ( - 'powershell Invoke-Command -ComputerName {} -ScriptBlock {{ {} }}; exit $LASTEXITCODE' - ) - runaspy = textwrap.dedent(''' + psrp_wrap = "powershell Invoke-Command -ComputerName {} -ScriptBlock {{ {} }}; exit $LASTEXITCODE" + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas username = '{}' password = '{}' ret = salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username, password) sys.exit(ret['retcode']) - '''.format(username, PASSWORD)) - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: - fp.write(runaspy) - cmd = 'python.exe {}; exit $LASTEXITCODE'.format(RUNAS_PATH) - ret = subprocess.call( - psrp_wrap.format(self.hostname, cmd), - shell=True + """.format( + username, PASSWORD + ) ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: + fp.write(runaspy) + cmd = "python.exe {}; exit $LASTEXITCODE".format(RUNAS_PATH) + ret = subprocess.call(psrp_wrap.format(self.hostname, cmd), shell=True) self.assertEqual(ret, 0) - @with_system_user('test-runas-admin', on_existing='delete', delete=True, - password=PASSWORD, groups=['Administrators']) + @with_system_user( + "test-runas-admin", + on_existing="delete", + delete=True, + password=PASSWORD, + groups=["Administrators"], + ) def test_runas_powershell_remoting_admin_no_pass(self, username): - psrp_wrap = ( - 'powershell Invoke-Command -ComputerName {} -ScriptBlock {{ {} }}; exit $LASTEXITCODE' - ) - runaspy = textwrap.dedent(''' + psrp_wrap = "powershell Invoke-Command -ComputerName {} -ScriptBlock {{ {} }}; exit $LASTEXITCODE" + runaspy = textwrap.dedent( + """ import sys import salt.utils.win_runas username = '{}' sys.exit(salt.utils.win_runas.runas('cmd.exe /C OPENFILES', username)['retcode']) - '''.format(username)) - with salt.utils.files.fopen(RUNAS_PATH, 'w') as fp: - fp.write(runaspy) - cmd = 'python.exe {}; exit $LASTEXITCODE'.format(RUNAS_PATH) - ret = subprocess.call( - psrp_wrap.format(self.hostname, cmd), - shell=True + """.format( + username + ) ) + with salt.utils.files.fopen(RUNAS_PATH, "w") as fp: + fp.write(runaspy) + cmd = "python.exe {}; exit $LASTEXITCODE".format(RUNAS_PATH) + ret = subprocess.call(psrp_wrap.format(self.hostname, cmd), shell=True) self.assertEqual(ret, 0) - @with_system_user('test-runas', on_existing='delete', delete=True, - password=PASSWORD) + @with_system_user( + "test-runas", on_existing="delete", delete=True, password=PASSWORD + ) def test_runas_service(self, username, timeout=200): if os.path.exists(RUNAS_OUT): os.remove(RUNAS_OUT) assert not os.path.exists(RUNAS_OUT) runaspy = SERVICE_SOURCE.format(repr(RUNAS_OUT), username, PASSWORD) - with io.open(RUNAS_PATH, 'w', encoding='utf-8') as fp: + with io.open(RUNAS_PATH, "w", encoding="utf-8") as fp: fp.write(runaspy) - cmd = 'python.exe {}'.format(RUNAS_PATH) - ret = subprocess.call( - cmd, - shell=True - ) + cmd = "python.exe {}".format(RUNAS_PATH) + ret = subprocess.call(cmd, shell=True) self.assertEqual(ret, 0) - win32serviceutil.StartService('test service') - wait_for_service('test service') - with salt.utils.files.fopen(RUNAS_OUT, 'r') as fp: + win32serviceutil.StartService("test service") + wait_for_service("test service") + with salt.utils.files.fopen(RUNAS_OUT, "r") as fp: ret = yaml.load(fp) - assert ret['retcode'] == 1, ret + assert ret["retcode"] == 1, ret - @with_system_user('test-runas', on_existing='delete', delete=True, - password=PASSWORD) + @with_system_user( + "test-runas", on_existing="delete", delete=True, password=PASSWORD + ) def test_runas_service_no_pass(self, username, timeout=200): if os.path.exists(RUNAS_OUT): os.remove(RUNAS_OUT) assert not os.path.exists(RUNAS_OUT) - runaspy = SERVICE_SOURCE.format(repr(RUNAS_OUT), username, '') - with io.open(RUNAS_PATH, 'w', encoding='utf-8') as fp: + runaspy = SERVICE_SOURCE.format(repr(RUNAS_OUT), username, "") + with io.open(RUNAS_PATH, "w", encoding="utf-8") as fp: fp.write(runaspy) - cmd = 'python.exe {}'.format(RUNAS_PATH) - ret = subprocess.call( - cmd, - shell=True - ) + cmd = "python.exe {}".format(RUNAS_PATH) + ret = subprocess.call(cmd, shell=True) self.assertEqual(ret, 0) - win32serviceutil.StartService('test service') - wait_for_service('test service') - with salt.utils.files.fopen(RUNAS_OUT, 'r') as fp: + win32serviceutil.StartService("test service") + wait_for_service("test service") + with salt.utils.files.fopen(RUNAS_OUT, "r") as fp: ret = yaml.load(fp) - assert ret['retcode'] == 1, ret + assert ret["retcode"] == 1, ret - @with_system_user('test-runas-admin', on_existing='delete', delete=True, - password=PASSWORD, groups=['Administrators']) + @with_system_user( + "test-runas-admin", + on_existing="delete", + delete=True, + password=PASSWORD, + groups=["Administrators"], + ) def test_runas_service_admin(self, username, timeout=200): if os.path.exists(RUNAS_OUT): os.remove(RUNAS_OUT) assert not os.path.exists(RUNAS_OUT) runaspy = SERVICE_SOURCE.format(repr(RUNAS_OUT), username, PASSWORD) - with io.open(RUNAS_PATH, 'w', encoding='utf-8') as fp: + with io.open(RUNAS_PATH, "w", encoding="utf-8") as fp: fp.write(runaspy) - cmd = 'python.exe {}'.format(RUNAS_PATH) - ret = subprocess.call( - cmd, - shell=True - ) + cmd = "python.exe {}".format(RUNAS_PATH) + ret = subprocess.call(cmd, shell=True) self.assertEqual(ret, 0) - win32serviceutil.StartService('test service') - wait_for_service('test service') - with salt.utils.files.fopen(RUNAS_OUT, 'r') as fp: + win32serviceutil.StartService("test service") + wait_for_service("test service") + with salt.utils.files.fopen(RUNAS_OUT, "r") as fp: ret = yaml.load(fp) - assert ret['retcode'] == 0, ret + assert ret["retcode"] == 0, ret - @with_system_user('test-runas-admin', on_existing='delete', delete=True, - password=PASSWORD, groups=['Administrators']) + @with_system_user( + "test-runas-admin", + on_existing="delete", + delete=True, + password=PASSWORD, + groups=["Administrators"], + ) def test_runas_service_admin_no_pass(self, username, timeout=200): if os.path.exists(RUNAS_OUT): os.remove(RUNAS_OUT) assert not os.path.exists(RUNAS_OUT) - runaspy = SERVICE_SOURCE.format(repr(RUNAS_OUT), username, '') - with io.open(RUNAS_PATH, 'w', encoding='utf-8') as fp: + runaspy = SERVICE_SOURCE.format(repr(RUNAS_OUT), username, "") + with io.open(RUNAS_PATH, "w", encoding="utf-8") as fp: fp.write(runaspy) - cmd = 'python.exe {}'.format(RUNAS_PATH) - ret = subprocess.call( - cmd, - shell=True - ) + cmd = "python.exe {}".format(RUNAS_PATH) + ret = subprocess.call(cmd, shell=True) self.assertEqual(ret, 0) - win32serviceutil.StartService('test service') - wait_for_service('test service') - with salt.utils.files.fopen(RUNAS_OUT, 'r') as fp: + win32serviceutil.StartService("test service") + wait_for_service("test service") + with salt.utils.files.fopen(RUNAS_OUT, "r") as fp: ret = yaml.load(fp) - assert ret['retcode'] == 0, ret + assert ret["retcode"] == 0, ret def test_runas_service_system_user(self): if os.path.exists(RUNAS_OUT): os.remove(RUNAS_OUT) assert not os.path.exists(RUNAS_OUT) - runaspy = SERVICE_SOURCE.format(repr(RUNAS_OUT), 'SYSTEM', '') - with io.open(RUNAS_PATH, 'w', encoding='utf-8') as fp: + runaspy = SERVICE_SOURCE.format(repr(RUNAS_OUT), "SYSTEM", "") + with io.open(RUNAS_PATH, "w", encoding="utf-8") as fp: fp.write(runaspy) - cmd = 'python.exe {}'.format(RUNAS_PATH) - ret = subprocess.call( - cmd, - shell=True - ) + cmd = "python.exe {}".format(RUNAS_PATH) + ret = subprocess.call(cmd, shell=True) self.assertEqual(ret, 0) - win32serviceutil.StartService('test service') - wait_for_service('test service') - with salt.utils.files.fopen(RUNAS_OUT, 'r') as fp: + win32serviceutil.StartService("test service") + wait_for_service("test service") + with salt.utils.files.fopen(RUNAS_OUT, "r") as fp: ret = yaml.load(fp) - assert ret['retcode'] == 0, ret + assert ret["retcode"] == 0, ret diff --git a/tests/integration/utils/testprogram.py b/tests/integration/utils/testprogram.py index d9d8a9991d6..c60cdfa5c4c 100644 --- a/tests/integration/utils/testprogram.py +++ b/tests/integration/utils/testprogram.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Classes for starting/stopping/status salt daemons, auxiliary scripts, generic commands. -''' +""" from __future__ import absolute_import + import atexit import copy -from datetime import datetime, timedelta import errno import getpass import logging @@ -19,123 +19,142 @@ import subprocess import sys import tempfile import time +from datetime import datetime, timedelta +import pytest +import salt.defaults.exitcodes as exitcodes import salt.utils.files import salt.utils.platform import salt.utils.process import salt.utils.psutil_compat as psutils import salt.utils.yaml -import salt.defaults.exitcodes as exitcodes from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin - -from tests.support.unit import TestCase -from tests.support.runtests import RUNTIME_VARS -from tests.support.processes import terminate_process, terminate_process_list from tests.support.cli_scripts import ScriptPathMixin +from tests.support.processes import terminate_process, terminate_process_list +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase + log = logging.getLogger(__name__) -if 'TimeoutError' not in __builtins__: +if "TimeoutError" not in __builtins__: + class TimeoutError(OSError): - '''Compatibility exception with python3''' + """Compatibility exception with python3""" - __builtins__['TimeoutError'] = TimeoutError + __builtins__["TimeoutError"] = TimeoutError +@pytest.mark.windows_whitelisted class TestProgramMeta(type): - ''' + """ Stack all inherited config_attrs and dirtree dirs from the base classes. - ''' + """ + def __new__(mcs, name, bases, attrs): config_vals = {} config_attrs = set() dirtree = set() for base in bases: - config_vals.update(getattr(base, 'config_vals', {})) - config_attrs.update(getattr(base, 'config_attrs', {})) - dirtree.update(getattr(base, 'dirtree', [])) + config_vals.update(getattr(base, "config_vals", {})) + config_attrs.update(getattr(base, "config_attrs", {})) + dirtree.update(getattr(base, "dirtree", [])) - config_vals.update(attrs.get('config_vals', {})) - attrs['config_vals'] = config_vals + config_vals.update(attrs.get("config_vals", {})) + attrs["config_vals"] = config_vals - config_attrs.update(attrs.get('config_attrs', {})) - attrs['config_attrs'] = config_attrs + config_attrs.update(attrs.get("config_attrs", {})) + attrs["config_attrs"] = config_attrs - dirtree.update(attrs.get('dirtree', [])) - attrs['dirtree'] = dirtree + dirtree.update(attrs.get("dirtree", [])) + attrs["dirtree"] = dirtree return super(TestProgramMeta, mcs).__new__(mcs, name, bases, attrs) # pylint: disable=too-many-instance-attributes +@pytest.mark.windows_whitelisted class TestProgram(six.with_metaclass(TestProgramMeta, object)): - ''' + """ Set up an arbitrary executable to run. :attribute dirtree: An iterable of directories to be created - ''' + """ - empty_config = '' - config_file = '' + empty_config = "" + config_file = "" - config_attrs = set([ - 'name', - 'test_dir', - 'config_dirs', - ]) - config_vals = { - } - config_base = '' - config_dir = os.path.join('etc') + config_attrs = set(["name", "test_dir", "config_dirs"]) + config_vals = {} + config_base = "" + config_dir = os.path.join("etc") configs = {} - config_types = (str, six.string_types,) + config_types = ( + str, + six.string_types, + ) dirtree = [ - '&config_dirs', + "&config_dirs", ] @staticmethod def config_caster(cfg): return str(cfg) - def __init__(self, program=None, name=None, env=None, shell=False, parent_dir=None, clean_on_exit=True, **kwargs): - self.program = program or getattr(self, 'program', None) - self.name = name or getattr(self, 'name', '') + def __init__( + self, + program=None, + name=None, + env=None, + shell=False, + parent_dir=None, + clean_on_exit=True, + **kwargs + ): + self.program = program or getattr(self, "program", None) + self.name = name or getattr(self, "name", "") self.env = env or {} self.shell = shell self._parent_dir = parent_dir or None self.clean_on_exit = clean_on_exit - self._root_dir = kwargs.pop('root_dir', self.name) - self.config_dir = kwargs.pop('config_dir', copy.copy(self.config_dir)) + self._root_dir = kwargs.pop("root_dir", self.name) + self.config_dir = kwargs.pop("config_dir", copy.copy(self.config_dir)) config_attrs = copy.copy(self.config_attrs) - config_attrs.update(kwargs.pop('config_attrs', set())) + config_attrs.update(kwargs.pop("config_attrs", set())) self.config_attrs = config_attrs config_vals = copy.copy(self.config_vals) - config_vals.update(kwargs.pop('config_vals', {})) + config_vals.update(kwargs.pop("config_vals", {})) self.config_vals = config_vals config_base = copy.deepcopy(self.config_base) - config_base = self.config_merge(config_base, kwargs.pop('config_base', self.config_types[0]())) + config_base = self.config_merge( + config_base, kwargs.pop("config_base", self.config_types[0]()) + ) self.config_base = config_base configs = copy.deepcopy(self.configs) - for cname, cinfo in kwargs.pop('configs', {}).items(): + for cname, cinfo in kwargs.pop("configs", {}).items(): target = configs.setdefault(cname, {}) - if 'path' in cinfo: - target['path'] = cinfo['path'] - if 'map' in cinfo: - target_map = target.setdefault('map', self.config_types[0]()) - target_map = self.config_merge(target_map, cinfo['map']) - target['map'] = target_map + if "path" in cinfo: + target["path"] = cinfo["path"] + if "map" in cinfo: + target_map = target.setdefault("map", self.config_types[0]()) + target_map = self.config_merge(target_map, cinfo["map"]) + target["map"] = target_map self.configs = configs if not self.name: if not self.program: - raise ValueError('"{0}" object must specify "program" parameter'.format(self.__class__.__name__)) + raise ValueError( + '"{0}" object must specify "program" parameter'.format( + self.__class__.__name__ + ) + ) self.name = os.path.basename(self.program) self.process = None @@ -143,7 +162,7 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): self._setup_done = False dirtree = set(self.dirtree) - dirtree.update(kwargs.pop('dirtree', [])) + dirtree.update(kwargs.pop("dirtree", [])) self.dirtree = dirtree # Register the exit clean-up before making anything needing clean-up @@ -157,12 +176,12 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): @property def test_dir(self): - '''Directory that will contains all of the static and dynamic files for the daemon''' + """Directory that will contains all of the static and dynamic files for the daemon""" return os.path.join(self.parent_dir, self._root_dir) def config_file_get(self, config): - '''Get the filename (viz. path) to the configuration file''' - cfgf = self.configs[config].get('path') + """Get the filename (viz. path) to the configuration file""" + cfgf = self.configs[config].get("path") if cfgf: cfgf.format(**self.config_subs()) else: @@ -170,103 +189,111 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): return cfgf def config_dir_get(self, config): - '''Get the parent directory for the configuration file''' + """Get the parent directory for the configuration file""" return os.path.dirname(self.config_file_get(config)) @property def config_dirs(self): - '''Return a list of configuration directories''' + """Return a list of configuration directories""" cdirs = [self.config_dir_get(config) for config in self.configs.keys()] return cdirs def abs_path(self, path): - '''Absolute path of file including the test_dir''' + """Absolute path of file including the test_dir""" return os.path.join(self.test_dir, path) @property def start_pid(self): - '''PID of the called script prior to daemonizing.''' + """PID of the called script prior to daemonizing.""" return self.process.pid if self.process else None @property def parent_dir(self): - ''' + """ Directory that contains everything generated for running scripts - possibly for multiple scripts. - ''' + """ if self._parent_dir is None: self.created_parent_dir = True - self._parent_dir = tempfile.mkdtemp(prefix='salt-testdaemon-') + self._parent_dir = tempfile.mkdtemp(prefix="salt-testdaemon-") else: self._parent_dir = os.path.abspath(os.path.normpath(self._parent_dir)) if not os.path.exists(self._parent_dir): self.created_parent_dir = True os.makedirs(self._parent_dir) elif not os.path.isdir(self._parent_dir): - raise ValueError('Parent path "{0}" exists but is not a directory'.format(self._parent_dir)) + raise ValueError( + 'Parent path "{0}" exists but is not a directory'.format( + self._parent_dir + ) + ) return self._parent_dir def config_write(self, config): - '''Write out the config to a file''' + """Write out the config to a file""" if not config: return cpath = self.abs_path(self.config_file_get(config)) - with salt.utils.files.fopen(cpath, 'w') as cfo: + with salt.utils.files.fopen(cpath, "w") as cfo: cfg = self.config_stringify(config) - log.debug('Writing configuration for {0} to {1}:\n{2}'.format(self.name, cpath, cfg)) + log.debug( + "Writing configuration for {0} to {1}:\n{2}".format( + self.name, cpath, cfg + ) + ) cfo.write(cfg) cfo.flush() def configs_write(self): - '''Write all configuration files''' + """Write all configuration files""" for config in self.configs: self.config_write(config) def config_type(self, config): - '''Check if a configuration is an acceptable type.''' + """Check if a configuration is an acceptable type.""" return isinstance(config, self.config_types) def config_cast(self, config): - '''Cast a configuration to the internal expected type.''' + """Cast a configuration to the internal expected type.""" if not self.config_type(config): config = self.config_caster(config) return config def config_subs(self): - '''Get the substitution values for use to generate the config''' + """Get the substitution values for use to generate the config""" subs = dict([(attr, getattr(self, attr, None)) for attr in self.config_attrs]) for key, val in self.config_vals.items(): subs[key] = val.format(**subs) return subs def config_stringify(self, config): - '''Get the configuration as a string''' + """Get the configuration as a string""" cfg = self.config_get(config) cfg.format(**self.config_subs()) return cfg def config_merge(self, base, overrides): - '''Merge two configuration hunks''' + """Merge two configuration hunks""" base = self.config_cast(base) overrides = self.config_cast(overrides) - return ''.join([base, overrides]) + return "".join([base, overrides]) def config_get(self, config): - '''Get the configuration data''' + """Get the configuration data""" return self.configs[config] def config_set(self, config, val): - '''Set the configuration data''' + """Set the configuration data""" self.configs[config] = val def make_dirtree(self): - '''Create directory structure.''' + """Create directory structure.""" subdirs = [] for branch in self.dirtree: - log.debug('checking dirtree: {0}'.format(branch)) + log.debug("checking dirtree: {0}".format(branch)) if not branch: continue - if isinstance(branch, six.string_types) and branch[0] == '&': + if isinstance(branch, six.string_types) and branch[0] == "&": log.debug('Looking up dirtree branch "{0}"'.format(branch)) try: dirattr = getattr(self, branch[1:], None) @@ -283,23 +310,27 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): if isinstance(dirattr, six.string_types): subdirs.append(dirattr) - elif hasattr(dirattr, '__iter__'): + elif hasattr(dirattr, "__iter__"): subdirs.extend(dirattr) else: - raise TypeError("Branch type of {0} in dirtree is unhandled".format(branch)) + raise TypeError( + "Branch type of {0} in dirtree is unhandled".format(branch) + ) elif isinstance(branch, six.string_types): subdirs.append(branch) else: - raise TypeError("Branch type of {0} in dirtree is unhandled".format(branch)) + raise TypeError( + "Branch type of {0} in dirtree is unhandled".format(branch) + ) for subdir in subdirs: path = self.abs_path(subdir) if not os.path.exists(path): - log.debug('make_dirtree: {0}'.format(path)) + log.debug("make_dirtree: {0}".format(path)) os.makedirs(path) def setup(self, *args, **kwargs): - '''Create any scaffolding for run-time''' + """Create any scaffolding for run-time""" # unused _ = args, kwargs @@ -310,7 +341,7 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): self._setup_done = True def cleanup(self, *args, **kwargs): - ''' Clean out scaffolding of setup() and any run-time generated files.''' + """ Clean out scaffolding of setup() and any run-time generated files.""" # Unused for now _ = (args, kwargs) @@ -326,17 +357,17 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): shutil.rmtree(self.parent_dir) def run( - self, - args=None, - catch_stderr=False, - with_retcode=False, - timeout=None, - raw=False, - env=None, - verbatim_args=False, - verbatim_env=False, + self, + args=None, + catch_stderr=False, + with_retcode=False, + timeout=None, + raw=False, + env=None, + verbatim_args=False, + verbatim_env=False, ): - ''' + """ Execute a command possibly using a supplied environment. :param args: @@ -361,7 +392,7 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): environment values. :return list: (stdout [,stderr] [,retcode]) - ''' + """ # unused for now _ = verbatim_args @@ -379,11 +410,11 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): env_delta.update(env) if not verbatim_env: - env_pypath = env_delta.get('PYTHONPATH', os.environ.get('PYTHONPATH')) + env_pypath = env_delta.get("PYTHONPATH", os.environ.get("PYTHONPATH")) if not env_pypath: env_pypath = sys.path else: - env_pypath = env_pypath.split(':') + env_pypath = env_pypath.split(":") for path in sys.path: if path not in env_pypath: env_pypath.append(path) @@ -391,9 +422,9 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): if RUNTIME_VARS.CODE_DIR != env_pypath[0]: env_pypath.insert(0, RUNTIME_VARS.CODE_DIR) if salt.utils.platform.is_windows(): - env_delta['PYTHONPATH'] = ';'.join(env_pypath) + env_delta["PYTHONPATH"] = ";".join(env_pypath) else: - env_delta['PYTHONPATH'] = ':'.join(env_pypath) + env_delta["PYTHONPATH"] = ":".join(env_pypath) cmd_env = dict(os.environ) cmd_env.update(env_delta) @@ -401,34 +432,34 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): if salt.utils.platform.is_windows() and six.PY2: for k, v in cmd_env.items(): if isinstance(k, six.text_type) or isinstance(v, six.text_type): - cmd_env[k.encode('ascii')] = v.encode('ascii') + cmd_env[k.encode("ascii")] = v.encode("ascii") popen_kwargs = { - 'shell': self.shell, - 'stdout': subprocess.PIPE, - 'env': cmd_env, + "shell": self.shell, + "stdout": subprocess.PIPE, + "env": cmd_env, } if catch_stderr is True: - popen_kwargs['stderr'] = subprocess.PIPE + popen_kwargs["stderr"] = subprocess.PIPE - if not sys.platform.lower().startswith('win'): - popen_kwargs['close_fds'] = True + if not sys.platform.lower().startswith("win"): + popen_kwargs["close_fds"] = True def detach_from_parent_group(): - ''' + """ A utility function that prevents child process from getting parent signals. - ''' + """ os.setpgrp() - popen_kwargs['preexec_fn'] = detach_from_parent_group + popen_kwargs["preexec_fn"] = detach_from_parent_group if salt.utils.platform.is_windows(): - self.argv = ['python.exe', self.program] + self.argv = ["python.exe", self.program] else: self.argv = [self.program] self.argv.extend(args) - log.debug('TestProgram.run: %s Environment %s', self.argv, env_delta) + log.debug("TestProgram.run: %s Environment %s", self.argv, env_delta) process = subprocess.Popen(self.argv, **popen_kwargs) self.process = process @@ -447,10 +478,12 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): raise out = process.stdout.read().splitlines() - out.extend([ - 'Process took more than {0} seconds to complete. ' - 'Process Killed!'.format(timeout) - ]) + out.extend( + [ + "Process took more than {0} seconds to complete. " + "Process Killed!".format(timeout) + ] + ) if catch_stderr: err = process.stderr.read().splitlines() if with_retcode: @@ -494,7 +527,11 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): if with_retcode: if out is not None and err is not None: if not raw: - return out.splitlines(), err.splitlines(), process.returncode + return ( + out.splitlines(), + err.splitlines(), + process.returncode, + ) else: return out, err, process.returncode return out.splitlines(), [], process.returncode @@ -538,57 +575,62 @@ class TestProgram(six.with_metaclass(TestProgramMeta, object)): pass +@pytest.mark.windows_whitelisted class TestSaltProgramMeta(TestProgramMeta): - ''' + """ A Meta-class to set self.script from the class name when it is not specifically set by a "script" argument. - ''' + """ + def __new__(mcs, name, bases, attrs): - if attrs.get('script') is None: - if 'Salt' in name: - script = 'salt-{0}'.format(name.rsplit('Salt', 1)[-1].lower()) + if attrs.get("script") is None: + if "Salt" in name: + script = "salt-{0}".format(name.rsplit("Salt", 1)[-1].lower()) if script is None: raise AttributeError( 'Class {0}: Unable to set "script" attribute: class name' - ' must include "Salt" or "script" must be explicitly set.'.format(name) + ' must include "Salt" or "script" must be explicitly set.'.format( + name + ) ) - attrs['script'] = script + attrs["script"] = script config_base = {} configs = {} for base in bases: - if 'Salt' not in base.__name__: + if "Salt" not in base.__name__: continue - config_base.update(getattr(base, 'config_base', {})) - configs.update(getattr(base, 'configs', {})) + config_base.update(getattr(base, "config_base", {})) + configs.update(getattr(base, "configs", {})) - config_base.update(attrs.get('config_base', {})) - attrs['config_base'] = config_base + config_base.update(attrs.get("config_base", {})) + attrs["config_base"] = config_base - configs.update(attrs.get('configs', {})) - attrs['configs'] = configs + configs.update(attrs.get("configs", {})) + attrs["configs"] = configs return super(TestSaltProgramMeta, mcs).__new__(mcs, name, bases, attrs) -class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram, ScriptPathMixin)): - ''' +@pytest.mark.windows_whitelisted +class TestSaltProgram( + six.with_metaclass(TestSaltProgramMeta, TestProgram, ScriptPathMixin) +): + """ This is like TestProgram but with some functions to run a salt-specific auxiliary program. - ''' + """ + config_types = (dict,) - config_attrs = set([ - 'log_dir', - 'script_dir', - ]) + config_attrs = set(["log_dir", "script_dir"]) pub_port = 4505 ret_port = 4506 for port in [pub_port, ret_port]: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: - connect = sock.bind(('localhost', port)) + connect = sock.bind(("localhost", port)) except (socket.error, OSError): # these ports are already in use, use different ones pub_port = 4606 @@ -597,31 +639,31 @@ class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram, Scrip sock.close() config_base = { - 'root_dir': '{test_dir}', - 'publish_port': pub_port, - 'ret_port': ret_port, + "root_dir": "{test_dir}", + "publish_port": pub_port, + "ret_port": ret_port, } configs = {} - config_dir = os.path.join('etc', 'salt') + config_dir = os.path.join("etc", "salt") - log_dir = os.path.join('var', 'log', 'salt') + log_dir = os.path.join("var", "log", "salt") dirtree = [ - '&log_dir', - '&script_dir', + "&log_dir", + "&script_dir", ] - script = '' - script_dir = 'bin' + script = "" + script_dir = "bin" @staticmethod def config_caster(cfg): return salt.utils.yaml.safe_load(cfg) def __init__(self, *args, **kwargs): - if len(args) < 2 and 'program' not in kwargs: + if len(args) < 2 and "program" not in kwargs: # This is effectively a place-holder - it gets set correctly after super() - kwargs['program'] = self.script + kwargs["program"] = self.script super(TestSaltProgram, self).__init__(*args, **kwargs) self.program = self.get_script_path(self.script) @@ -636,26 +678,26 @@ class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram, Scrip cfg_base = {} for key, val in self.config_base.items(): _val = val - if val and isinstance(val, six.string_types) and val[0] == '&': + if val and isinstance(val, six.string_types) and val[0] == "&": _val = getattr(self, val[1:], None) if _val is None: continue cfg_base[key] = _val if config in self.configs: cfg = {} - for key, val in self.configs.get(config, {}).get('map', {}).items(): + for key, val in self.configs.get(config, {}).get("map", {}).items(): _val = val - if val and isinstance(val, six.string_types) and val[0] == '&': + if val and isinstance(val, six.string_types) and val[0] == "&": _val = getattr(self, val[1:], None) if _val is None: continue cfg[key] = _val cfg = self.config_merge(cfg_base, cfg) - log.debug('Generated config => {0}'.format(cfg)) + log.debug("Generated config => {0}".format(cfg)) return cfg def config_stringify(self, config): - '''Transform the configuration data into a string (suitable to write to a file)''' + """Transform the configuration data into a string (suitable to write to a file)""" subs = self.config_subs() cfg = {} for key, val in self.config_get(config).items(): @@ -666,68 +708,79 @@ class TestSaltProgram(six.with_metaclass(TestSaltProgramMeta, TestProgram, Scrip return salt.utils.yaml.safe_dump(cfg, default_flow_style=False) def run(self, **kwargs): # pylint: disable=arguments-differ - if not kwargs.get('verbatim_args'): - args = kwargs.setdefault('args', []) - if '-c' not in args and '--config-dir' not in args: - args.extend(['--config-dir', self.abs_path(self.config_dir)]) + if not kwargs.get("verbatim_args"): + args = kwargs.setdefault("args", []) + if "-c" not in args and "--config-dir" not in args: + args.extend(["--config-dir", self.abs_path(self.config_dir)]) return super(TestSaltProgram, self).run(**kwargs) +@pytest.mark.windows_whitelisted class TestProgramSalt(TestSaltProgram): - '''Class to manage salt''' + """Class to manage salt""" - configs = {'master': {}} - script = 'salt' + configs = {"master": {}} + script = "salt" +@pytest.mark.windows_whitelisted class TestProgramSaltCall(TestSaltProgram): - '''Class to manage salt-call''' + """Class to manage salt-call""" - configs = {'minion': {'map': {'id': '{name}'}}} + configs = {"minion": {"map": {"id": "{name}"}}} +@pytest.mark.windows_whitelisted class TestProgramSaltRun(TestSaltProgram): - '''Class to manage salt-run''' + """Class to manage salt-run""" - configs = {'master': {}} + configs = {"master": {}} def __init__(self, *args, **kwargs): - cfgb = kwargs.setdefault('config_base', {}) - _ = cfgb.setdefault('user', getpass.getuser()) + cfgb = kwargs.setdefault("config_base", {}) + _ = cfgb.setdefault("user", getpass.getuser()) super(TestProgramSaltRun, self).__init__(*args, **kwargs) +@pytest.mark.windows_whitelisted class TestDaemon(TestProgram): - ''' + """ Run one of the standard daemons - ''' + """ script = None pid_file = None - pid_dir = os.path.join('var', 'run') + pid_dir = os.path.join("var", "run") dirtree = [ - '&pid_dir', + "&pid_dir", ] def __init__(self, *args, **kwargs): - self.script = kwargs.pop('script', self.script) - self.pid_file = kwargs.pop('pid_file', self.pid_file if self.pid_file else '{0}.pid'.format(self.script)) - self.pid_dir = kwargs.pop('pid_dir', self.pid_dir) + self.script = kwargs.pop("script", self.script) + self.pid_file = kwargs.pop( + "pid_file", + self.pid_file if self.pid_file else "{0}.pid".format(self.script), + ) + self.pid_dir = kwargs.pop("pid_dir", self.pid_dir) self._shutdown = False - if not args and 'program' not in kwargs: + if not args and "program" not in kwargs: # This is effectively a place-holder - it gets set correctly after super() - kwargs['program'] = self.script + kwargs["program"] = self.script super(TestDaemon, self).__init__(*args, **kwargs) @property def pid_path(self): - '''Path to the pid file created by the daemon''' - return os.path.join(self.pid_dir, self.pid_file) if os.path.sep not in self.pid_file else self.pid_file + """Path to the pid file created by the daemon""" + return ( + os.path.join(self.pid_dir, self.pid_file) + if os.path.sep not in self.pid_file + else self.pid_file + ) @property def daemon_pid(self): - '''Return the daemon PID''' + """Return the daemon PID""" daemon_pid = None pid_path = self.abs_path(self.pid_path) if salt.utils.process.check_pidfile(pid_path): @@ -735,20 +788,22 @@ class TestDaemon(TestProgram): return daemon_pid def wait_for_daemon_pid(self, timeout=10): - '''Wait up to timeout seconds for the PID file to appear and return the PID''' + """Wait up to timeout seconds for the PID file to appear and return the PID""" endtime = time.time() + timeout while True: pid = self.daemon_pid if pid: return pid if endtime < time.time(): - raise TimeoutError('Timeout waiting for "{0}" pid in "{1}"'.format( - self.name, self.abs_path(self.pid_path) - )) + raise TimeoutError( + 'Timeout waiting for "{0}" pid in "{1}"'.format( + self.name, self.abs_path(self.pid_path) + ) + ) time.sleep(0.2) def is_running(self): - '''Is the daemon running?''' + """Is the daemon running?""" ret = False if not self._shutdown: try: @@ -759,10 +814,10 @@ class TestDaemon(TestProgram): return ret def find_orphans(self, cmdline): - '''Find orphaned processes matching the specified cmdline''' + """Find orphaned processes matching the specified cmdline""" ret = [] if six.PY3: - cmdline = ' '.join(cmdline) + cmdline = " ".join(cmdline) for proc in psutils.process_iter(): try: for item in proc.cmdline(): @@ -775,10 +830,14 @@ class TestDaemon(TestProgram): except psutils.AccessDenied: # We might get access denied if not running as root if not salt.utils.platform.is_windows(): - pinfo = proc.as_dict(attrs=['pid', 'name', 'username']) - log.error('Unable to access process %s, ' - 'running command %s as user %s', - pinfo['pid'], pinfo['name'], pinfo['username']) + pinfo = proc.as_dict(attrs=["pid", "name", "username"]) + log.error( + "Unable to access process %s, " + "running command %s as user %s", + pinfo["pid"], + pinfo["name"], + pinfo["username"], + ) continue else: cmd_len = len(cmdline) @@ -792,18 +851,24 @@ class TestDaemon(TestProgram): except psutils.AccessDenied: # We might get access denied if not running as root if not salt.utils.platform.is_windows(): - pinfo = proc.as_dict(attrs=['pid', 'name', 'username']) - log.error('Unable to access process %s, ' - 'running command %s as user %s', - pinfo['pid'], pinfo['name'], pinfo['username']) + pinfo = proc.as_dict(attrs=["pid", "name", "username"]) + log.error( + "Unable to access process %s, " + "running command %s as user %s", + pinfo["pid"], + pinfo["name"], + pinfo["username"], + ) continue - if any((cmdline == proc_cmdline[n:n + cmd_len]) - for n in range(len(proc_cmdline) - cmd_len + 1)): + if any( + (cmdline == proc_cmdline[n : n + cmd_len]) + for n in range(len(proc_cmdline) - cmd_len + 1) + ): ret.append(proc) return ret def shutdown(self, signum=signal.SIGTERM, timeout=10, wait_for_orphans=0): - '''Shutdown a running daemon''' + """Shutdown a running daemon""" if not self._shutdown: try: pid = self.wait_for_daemon_pid(timeout) @@ -821,10 +886,7 @@ class TestDaemon(TestProgram): last = time.time() while True: if orphans: - log.debug( - 'Terminating orphaned child processes: %s', - orphans - ) + log.debug("Terminating orphaned child processes: %s", orphans) terminate_process_list(orphans) last = time.time() if (time.time() - last) >= wait_for_orphans: @@ -835,97 +897,106 @@ class TestDaemon(TestProgram): self._shutdown = True def cleanup(self, *args, **kwargs): - '''Remove left-over scaffolding - antithesis of setup()''' + """Remove left-over scaffolding - antithesis of setup()""" # Shutdown if not alreadt shutdown self.shutdown() super(TestDaemon, self).cleanup(*args, **kwargs) -class TestSaltDaemon(six.with_metaclass(TestSaltProgramMeta, TestDaemon, TestSaltProgram)): - ''' +@pytest.mark.windows_whitelisted +class TestSaltDaemon( + six.with_metaclass(TestSaltProgramMeta, TestDaemon, TestSaltProgram) +): + """ A class to run arbitrary salt daemons (master, minion, syndic, etc.) - ''' + """ +@pytest.mark.windows_whitelisted class TestDaemonSaltMaster(TestSaltDaemon): - ''' + """ Manager for salt-master daemon. - ''' + """ - configs = {'master': {}} + configs = {"master": {}} def __init__(self, *args, **kwargs): - cfgb = kwargs.setdefault('config_base', {}) - _ = cfgb.setdefault('user', getpass.getuser()) + cfgb = kwargs.setdefault("config_base", {}) + _ = cfgb.setdefault("user", getpass.getuser()) super(TestDaemonSaltMaster, self).__init__(*args, **kwargs) +@pytest.mark.windows_whitelisted class TestDaemonSaltMinion(TestSaltDaemon): - ''' + """ Manager for salt-minion daemon. - ''' + """ - configs = {'minion': {'map': {'id': '{name}'}}} + configs = {"minion": {"map": {"id": "{name}"}}} def __init__(self, *args, **kwargs): - cfgb = kwargs.setdefault('config_base', {}) - _ = cfgb.setdefault('user', getpass.getuser()) + cfgb = kwargs.setdefault("config_base", {}) + _ = cfgb.setdefault("user", getpass.getuser()) super(TestDaemonSaltMinion, self).__init__(*args, **kwargs) +@pytest.mark.windows_whitelisted class TestDaemonSaltApi(TestSaltDaemon): - ''' + """ Manager for salt-api daemon. - ''' + """ +@pytest.mark.windows_whitelisted class TestDaemonSaltSyndic(TestSaltDaemon): - ''' + """ Manager for salt-syndic daemon. - ''' + """ configs = { - 'master': {'map': {'syndic_master': 'localhost'}}, - 'minion': {'map': {'id': '{name}'}}, + "master": {"map": {"syndic_master": "localhost"}}, + "minion": {"map": {"id": "{name}"}}, } def __init__(self, *args, **kwargs): - cfgb = kwargs.setdefault('config_base', {}) - _ = cfgb.setdefault('user', getpass.getuser()) + cfgb = kwargs.setdefault("config_base", {}) + _ = cfgb.setdefault("user", getpass.getuser()) super(TestDaemonSaltSyndic, self).__init__(*args, **kwargs) +@pytest.mark.windows_whitelisted class TestDaemonSaltProxy(TestSaltDaemon): - ''' + """ Manager for salt-proxy daemon. - ''' + """ - pid_file = 'salt-minion.pid' - configs = {'proxy': {}} + pid_file = "salt-minion.pid" + configs = {"proxy": {}} def __init__(self, *args, **kwargs): - cfgb = kwargs.setdefault('config_base', {}) - _ = cfgb.setdefault('user', getpass.getuser()) + cfgb = kwargs.setdefault("config_base", {}) + _ = cfgb.setdefault("user", getpass.getuser()) super(TestDaemonSaltProxy, self).__init__(*args, **kwargs) def run(self, **kwargs): - if not kwargs.get('verbatim_args'): - args = kwargs.setdefault('args', []) - if '--proxyid' not in args: - args.extend(['--proxyid', self.name]) + if not kwargs.get("verbatim_args"): + args = kwargs.setdefault("args", []) + if "--proxyid" not in args: + args.extend(["--proxyid", self.name]) return super(TestDaemonSaltProxy, self).run(**kwargs) +@pytest.mark.windows_whitelisted class TestProgramCase(TestCase): - ''' + """ Utilities for unit tests that use TestProgram() - ''' + """ def setUp(self): # Setup for scripts - if not getattr(self, '_test_dir', None): - self._test_dir = tempfile.mkdtemp(prefix='salt-testdaemon-') + if not getattr(self, "_test_dir", None): + self._test_dir = tempfile.mkdtemp(prefix="salt-testdaemon-") super(TestProgramCase, self).setUp() def tearDown(self): @@ -935,24 +1006,21 @@ class TestProgramCase(TestCase): self._test_dir = None super(TestProgramCase, self).tearDown() - def assert_exit_status(self, status, ex_status, message=None, stdout=None, stderr=None): - ''' + def assert_exit_status( + self, status, ex_status, message=None, stdout=None, stderr=None + ): + """ Helper function to verify exit status and emit failure information. - ''' + """ ex_val = getattr(exitcodes, ex_status) - _message = '' if not message else ' ({0})'.format(message) - _stdout = '' if not stdout else '\nstdout: {0}'.format(stdout) - _stderr = '' if not stderr else '\nstderr: {0}'.format(stderr) + _message = "" if not message else " ({0})".format(message) + _stdout = "" if not stdout else "\nstdout: {0}".format(stdout) + _stderr = "" if not stderr else "\nstderr: {0}".format(stderr) self.assertEqual( status, ex_val, - 'Exit status was {0}, must be {1} (salt.default.exitcodes.{2}){3}{4}{5}'.format( - status, - ex_val, - ex_status, - _message, - _stdout, - _stderr, - ) + "Exit status was {0}, must be {1} (salt.default.exitcodes.{2}){3}{4}{5}".format( + status, ex_val, ex_status, _message, _stdout, _stderr, + ), ) diff --git a/tests/integration/wheel/test_client.py b/tests/integration/wheel/test_client.py index 20730558d71..a4dcb7368cb 100644 --- a/tests/integration/wheel/test_client.py +++ b/tests/integration/wheel/test_client.py @@ -1,79 +1,73 @@ # -*- coding: utf-8 -*- -# Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mixins import AdaptedConfigurationTestCaseMixin - -# Import Salt libs +import pytest import salt.auth -import salt.wheel import salt.utils.platform +import salt.wheel +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.unit import TestCase, skipIf +@pytest.mark.windows_whitelisted class WheelModuleTest(TestCase, AdaptedConfigurationTestCaseMixin): eauth_creds = { - 'username': 'saltdev_auto', - 'password': 'saltdev', - 'eauth': 'auto', + "username": "saltdev_auto", + "password": "saltdev", + "eauth": "auto", } def setUp(self): - ''' + """ Configure an eauth user to test with - ''' - self.wheel = salt.wheel.Wheel(dict(self.get_config('client_config'))) + """ + self.wheel = salt.wheel.Wheel(dict(self.get_config("client_config"))) def tearDown(self): del self.wheel def test_master_call(self): - ''' + """ Test executing master_call with lowdata The choice of using key.list_all for this is arbitrary and should be changed to some mocked function that is more testing friendly. - ''' - low = { - 'client': 'wheel', - 'fun': 'key.list_all', - 'print_event': False - } + """ + low = {"client": "wheel", "fun": "key.list_all", "print_event": False} low.update(self.eauth_creds) self.wheel.master_call(**low) def test_token(self): - ''' + """ Test executing master_call with lowdata The choice of using key.list_all for this is arbitrary and should be changed to some mocked function that is more testing friendly. - ''' - auth = salt.auth.LoadAuth(dict(self.get_config('client_config'))) + """ + auth = salt.auth.LoadAuth(dict(self.get_config("client_config"))) token = auth.mk_token(self.eauth_creds) - token = auth.mk_token({ - 'username': 'saltdev_auto', - 'password': 'saltdev', - 'eauth': 'auto', - }) + token = auth.mk_token( + {"username": "saltdev_auto", "password": "saltdev", "eauth": "auto"} + ) - self.wheel.master_call(**{ - 'client': 'wheel', - 'fun': 'key.list_all', - 'token': token['token'], - 'print_event': False, - }) + self.wheel.master_call( + **{ + "client": "wheel", + "fun": "key.list_all", + "token": token["token"], + "print_event": False, + } + ) def test_cmd_sync(self): low = { - 'client': 'wheel', - 'fun': 'key.list_all', - 'print_event': False, + "client": "wheel", + "fun": "key.list_all", + "print_event": False, } low.update(self.eauth_creds) @@ -81,13 +75,15 @@ class WheelModuleTest(TestCase, AdaptedConfigurationTestCaseMixin): # Remove this skipIf when Issue #39616 is resolved # https://github.com/saltstack/salt/issues/39616 - @skipIf(salt.utils.platform.is_windows(), - 'Causes pickling error on Windows: Issue #39616') + @skipIf( + salt.utils.platform.is_windows(), + "Causes pickling error on Windows: Issue #39616", + ) def test_cmd_async(self): low = { - 'client': 'wheel_async', - 'fun': 'key.list_all', - 'print_event': False, + "client": "wheel_async", + "fun": "key.list_all", + "print_event": False, } low.update(self.eauth_creds) @@ -95,22 +91,22 @@ class WheelModuleTest(TestCase, AdaptedConfigurationTestCaseMixin): def test_cmd_sync_w_arg(self): low = { - 'fun': 'key.finger', - 'match': '*', - 'print_event': False, + "fun": "key.finger", + "match": "*", + "print_event": False, } low.update(self.eauth_creds) ret = self.wheel.cmd_sync(low) - self.assertIn('return', ret.get('data', {})) + self.assertIn("return", ret.get("data", {})) def test_wildcard_auth(self): low = { - 'username': 'the_s0und_of_t3ch', - 'password': 'willrockyou', - 'eauth': 'auto', - 'fun': 'key.list_all', - 'print_event': False, + "username": "the_s0und_of_t3ch", + "password": "willrockyou", + "eauth": "auto", + "fun": "key.list_all", + "print_event": False, } self.wheel.cmd_sync(low) diff --git a/tests/integration/wheel/test_key.py b/tests/integration/wheel/test_key.py index 1355d61113b..4abb9c55889 100644 --- a/tests/integration/wheel/test_key.py +++ b/tests/integration/wheel/test_key.py @@ -1,39 +1,48 @@ # coding: utf-8 -# Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mixins import AdaptedConfigurationTestCaseMixin - -# Import Salt libs +import pytest import salt.wheel +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.unit import TestCase +@pytest.mark.windows_whitelisted class KeyWheelModuleTest(TestCase, AdaptedConfigurationTestCaseMixin): def setUp(self): - self.wheel = salt.wheel.Wheel(dict(self.get_config('client_config'))) + self.wheel = salt.wheel.Wheel(dict(self.get_config("client_config"))) def tearDown(self): del self.wheel def test_list_all(self): - ret = self.wheel.cmd('key.list_all', print_event=False) - for host in ['minion', 'sub_minion']: - self.assertIn(host, ret['minions']) + ret = self.wheel.cmd("key.list_all", print_event=False) + for host in ["minion", "sub_minion"]: + self.assertIn(host, ret["minions"]) def test_gen(self): - ret = self.wheel.cmd('key.gen', kwarg={'id_': 'soundtechniciansrock'}, print_event=False) + ret = self.wheel.cmd( + "key.gen", kwarg={"id_": "soundtechniciansrock"}, print_event=False + ) - self.assertIn('pub', ret) - self.assertIn('priv', ret) + self.assertIn("pub", ret) + self.assertIn("priv", ret) try: - self.assertTrue( - ret.get('pub', '').startswith('-----BEGIN PUBLIC KEY-----')) + self.assertTrue(ret.get("pub", "").startswith("-----BEGIN PUBLIC KEY-----")) except AssertionError: self.assertTrue( - ret.get('pub', '').startswith('-----BEGIN RSA PUBLIC KEY-----')) + ret.get("pub", "").startswith("-----BEGIN RSA PUBLIC KEY-----") + ) self.assertTrue( - ret.get('priv', '').startswith('-----BEGIN RSA PRIVATE KEY-----')) + ret.get("priv", "").startswith("-----BEGIN RSA PRIVATE KEY-----") + ) + + def test_master_key_str(self): + ret = self.wheel.cmd("key.master_key_str", print_event=False) + self.assertIn("local", ret) + self.assertIn("master.pub", ret.get("local")) + self.assertTrue( + ret.get("local").get("master.pub").startswith("-----BEGIN PUBLIC KEY-----") + ) diff --git a/tests/jenkins.py b/tests/jenkins.py index 42f7f30ca06..ed0278f607d 100644 --- a/tests/jenkins.py +++ b/tests/jenkins.py @@ -1,36 +1,36 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -''' +""" This script is used to test Salt from a Jenkins server, specifically jenkins.saltstack.com. This script is intended to be shell-centric!! -''' +""" # Import python libs from __future__ import absolute_import, print_function + import glob +import optparse import os +import random import re +import shutil +import subprocess import sys import time -import shutil -import optparse -import subprocess -import random # Import Salt libs import salt.utils.files import salt.utils.json import salt.utils.stringutils import salt.utils.yaml + try: from salt.utils.nb_popen import NonBlockingPopen except ImportError: # Salt not installed, or nb_popen was not yet shipped with it - SALT_LIB = os.path.abspath( - os.path.dirname(os.path.dirname(__file__)) - ) + SALT_LIB = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) if SALT_LIB not in sys.path: sys.path.insert(0, SALT_LIB) try: @@ -38,75 +38,77 @@ except ImportError: from salt.utils.nb_popen import NonBlockingPopen except ImportError: # Still an ImportError??? Let's use some "brute-force" - sys.path.insert( - 0, - os.path.join(SALT_LIB, 'salt', 'utils') - ) + sys.path.insert(0, os.path.join(SALT_LIB, "salt", "utils")) from nb_popen import NonBlockingPopen # Import 3rd-party libs try: import requests + HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False -SALT_GIT_URL = 'https://github.com/saltstack/salt.git' +SALT_GIT_URL = "https://github.com/saltstack/salt.git" def build_pillar_data(options): - ''' + """ Build a YAML formatted string to properly pass pillar data - ''' - pillar = {'test_transport': options.test_transport, - 'cloud_only': options.cloud_only, - 'with_coverage': options.test_without_coverage is False} + """ + pillar = { + "test_transport": options.test_transport, + "cloud_only": options.cloud_only, + "with_coverage": options.test_without_coverage is False, + } if options.test_git_commit is not None: - pillar['test_git_commit'] = options.test_git_commit + pillar["test_git_commit"] = options.test_git_commit if options.test_git_url is not None: - pillar['test_git_url'] = options.test_git_url + pillar["test_git_url"] = options.test_git_url if options.bootstrap_salt_url is not None: - pillar['bootstrap_salt_url'] = options.bootstrap_salt_url + pillar["bootstrap_salt_url"] = options.bootstrap_salt_url if options.bootstrap_salt_commit is not None: - pillar['bootstrap_salt_commit'] = options.bootstrap_salt_commit + pillar["bootstrap_salt_commit"] = options.bootstrap_salt_commit if options.package_source_dir: - pillar['package_source_dir'] = options.package_source_dir + pillar["package_source_dir"] = options.package_source_dir if options.package_build_dir: - pillar['package_build_dir'] = options.package_build_dir + pillar["package_build_dir"] = options.package_build_dir if options.package_artifact_dir: - pillar['package_artifact_dir'] = options.package_artifact_dir + pillar["package_artifact_dir"] = options.package_artifact_dir if options.pillar: pillar.update(dict(options.pillar)) - return salt.utils.yaml.safe_dump(pillar, default_flow_style=True, indent=0, width=sys.maxint).rstrip() + return salt.utils.yaml.safe_dump( + pillar, default_flow_style=True, indent=0, width=sys.maxint + ).rstrip() def build_minion_target(options, vm_name): target = vm_name for grain in options.grain_target: - target += ' and G@{0}'.format(grain) + target += " and G@{0}".format(grain) if options.grain_target: return '"{0}"'.format(target) return target def generate_vm_name(options): - ''' + """ Generate a random enough vm name - ''' - if 'BUILD_NUMBER' in os.environ: - random_part = 'BUILD{0:0>6}'.format(os.environ.get('BUILD_NUMBER')) + """ + if "BUILD_NUMBER" in os.environ: + random_part = "BUILD{0:0>6}".format(os.environ.get("BUILD_NUMBER")) else: - random_part = os.urandom(3).encode('hex') + random_part = os.urandom(3).encode("hex") - return '{0}-{1}-{2}'.format(options.vm_prefix, options.platform, random_part) + return "{0}-{1}-{2}".format(options.vm_prefix, options.platform, random_part) def delete_vm(options): - ''' + """ Stop a VM - ''' - cmd = 'salt-cloud -d {0} -y'.format(options.delete_vm) - print('Running CMD: {0}'.format(cmd)) + """ + cmd = "salt-cloud -d {0} -y".format(options.delete_vm) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( @@ -114,93 +116,92 @@ def delete_vm(options): shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stream_stds=True + stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() def echo_parseable_environment(options, parser): - ''' + """ Echo NAME=VAL parseable output - ''' + """ output = [] if options.platform: name = generate_vm_name(options) - output.extend([ - 'JENKINS_SALTCLOUD_VM_PLATFORM={0}'.format(options.platform), - 'JENKINS_SALTCLOUD_VM_NAME={0}'.format(name) - ]) + output.extend( + [ + "JENKINS_SALTCLOUD_VM_PLATFORM={0}".format(options.platform), + "JENKINS_SALTCLOUD_VM_NAME={0}".format(name), + ] + ) if options.provider: - output.append( - 'JENKINS_SALTCLOUD_VM_PROVIDER={0}'.format(options.provider) - ) + output.append("JENKINS_SALTCLOUD_VM_PROVIDER={0}".format(options.provider)) if options.pull_request: # This is a Jenkins triggered Pull Request # We need some more data about the Pull Request available to the # environment if HAS_REQUESTS is False: - parser.error( - 'The python \'requests\' library needs to be installed' - ) + parser.error("The python 'requests' library needs to be installed") headers = {} - url = 'https://api.github.com/repos/saltstack/salt/pulls/{0}'.format(options.pull_request) + url = "https://api.github.com/repos/saltstack/salt/pulls/{0}".format( + options.pull_request + ) github_access_token_path = os.path.join( - os.environ.get('JENKINS_HOME', os.path.expanduser('~')), - '.github_token' + os.environ.get("JENKINS_HOME", os.path.expanduser("~")), ".github_token" ) if os.path.isfile(github_access_token_path): with salt.utils.files.fopen(github_access_token_path) as rfh: - headers = { - 'Authorization': 'token {0}'.format(rfh.read().strip()) - } + headers = {"Authorization": "token {0}".format(rfh.read().strip())} http_req = requests.get(url, headers=headers) if http_req.status_code != 200: parser.error( - 'Unable to get the pull request: {0[message]}'.format(http_req.json()) + "Unable to get the pull request: {0[message]}".format(http_req.json()) ) pr_details = http_req.json() - output.extend([ - 'SALT_PR_GIT_URL={0}'.format(pr_details['head']['repo']['clone_url']), - 'SALT_PR_GIT_BRANCH={0}'.format(pr_details['head']['ref']), - 'SALT_PR_GIT_COMMIT={0}'.format(pr_details['head']['sha']), - 'SALT_PR_GIT_BASE_BRANCH={0}'.format(pr_details['base']['ref']), - ]) + output.extend( + [ + "SALT_PR_GIT_URL={0}".format(pr_details["head"]["repo"]["clone_url"]), + "SALT_PR_GIT_BRANCH={0}".format(pr_details["head"]["ref"]), + "SALT_PR_GIT_COMMIT={0}".format(pr_details["head"]["sha"]), + "SALT_PR_GIT_BASE_BRANCH={0}".format(pr_details["base"]["ref"]), + ] + ) - sys.stdout.write('\n\n{0}\n\n'.format('\n'.join(output))) + sys.stdout.write("\n\n{0}\n\n".format("\n".join(output))) sys.stdout.flush() def download_unittest_reports(options): - print('Downloading remote unittest reports...') + print("Downloading remote unittest reports...") sys.stdout.flush() workspace = options.workspace - xml_reports_path = os.path.join(workspace, 'xml-test-reports') + xml_reports_path = os.path.join(workspace, "xml-test-reports") if os.path.isdir(xml_reports_path): shutil.rmtree(xml_reports_path) os.makedirs(xml_reports_path) cmds = ( - 'salt {0} archive.tar zcvf /tmp/xml-test-reports.tar.gz \'*.xml\' cwd=/tmp/xml-unittests-output/', - 'salt {0} cp.push /tmp/xml-test-reports.tar.gz', - 'mv -f /var/cache/salt/master/minions/{1}/files/tmp/xml-test-reports.tar.gz {2} && ' - 'tar zxvf {2}/xml-test-reports.tar.gz -C {2}/xml-test-reports && ' - 'rm -f {2}/xml-test-reports.tar.gz' + "salt {0} archive.tar zcvf /tmp/xml-test-reports.tar.gz '*.xml' cwd=/tmp/xml-unittests-output/", + "salt {0} cp.push /tmp/xml-test-reports.tar.gz", + "mv -f /var/cache/salt/master/minions/{1}/files/tmp/xml-test-reports.tar.gz {2} && " + "tar zxvf {2}/xml-test-reports.tar.gz -C {2}/xml-test-reports && " + "rm -f {2}/xml-test-reports.tar.gz", ) vm_name = options.download_unittest_reports for cmd in cmds: cmd = cmd.format(build_minion_target(options, vm_name), vm_name, workspace) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( @@ -208,39 +209,35 @@ def download_unittest_reports(options): shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stream_stds=True + stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() if proc.returncode != 0: - print( - '\nFailed to execute command. Exit code: {0}'.format( - proc.returncode - ) - ) + print("\nFailed to execute command. Exit code: {0}".format(proc.returncode)) time.sleep(0.25) def download_coverage_report(options): - print('Downloading remote coverage report...') + print("Downloading remote coverage report...") sys.stdout.flush() workspace = options.workspace vm_name = options.download_coverage_report - if os.path.isfile(os.path.join(workspace, 'coverage.xml')): - os.unlink(os.path.join(workspace, 'coverage.xml')) + if os.path.isfile(os.path.join(workspace, "coverage.xml")): + os.unlink(os.path.join(workspace, "coverage.xml")) cmds = ( - 'salt {0} archive.gzip /tmp/coverage.xml', - 'salt {0} cp.push /tmp/coverage.xml.gz', - 'gunzip /var/cache/salt/master/minions/{1}/files/tmp/coverage.xml.gz', - 'mv /var/cache/salt/master/minions/{1}/files/tmp/coverage.xml {2}' + "salt {0} archive.gzip /tmp/coverage.xml", + "salt {0} cp.push /tmp/coverage.xml.gz", + "gunzip /var/cache/salt/master/minions/{1}/files/tmp/coverage.xml.gz", + "mv /var/cache/salt/master/minions/{1}/files/tmp/coverage.xml {2}", ) for cmd in cmds: cmd = cmd.format(build_minion_target(options, vm_name), vm_name, workspace) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( @@ -248,55 +245,52 @@ def download_coverage_report(options): shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stream_stds=True + stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() if proc.returncode != 0: - print( - '\nFailed to execute command. Exit code: {0}'.format( - proc.returncode - ) - ) + print("\nFailed to execute command. Exit code: {0}".format(proc.returncode)) time.sleep(0.25) def download_remote_logs(options): - print('Downloading remote logs...') + print("Downloading remote logs...") sys.stdout.flush() workspace = options.workspace vm_name = options.download_remote_logs - for fname in ('salt-runtests.log', 'minion.log'): + for fname in ("salt-runtests.log", "minion.log"): if os.path.isfile(os.path.join(workspace, fname)): os.unlink(os.path.join(workspace, fname)) if not options.remote_log_path: - options.remote_log_path = [ - '/tmp/salt-runtests.log', - '/var/log/salt/minion' - ] + options.remote_log_path = ["/tmp/salt-runtests.log", "/var/log/salt/minion"] cmds = [] for remote_log in options.remote_log_path: - cmds.extend([ - 'salt {{0}} archive.gzip {0}'.format(remote_log), - 'salt {{0}} cp.push {0}.gz'.format(remote_log), - 'gunzip /var/cache/salt/master/minions/{{1}}/files{0}.gz'.format(remote_log), - 'mv /var/cache/salt/master/minions/{{1}}/files{0} {{2}}/{1}'.format( - remote_log, - '{0}{1}'.format( - os.path.basename(remote_log), - '' if remote_log.endswith('.log') else '.log' - ) - ) - ]) + cmds.extend( + [ + "salt {{0}} archive.gzip {0}".format(remote_log), + "salt {{0}} cp.push {0}.gz".format(remote_log), + "gunzip /var/cache/salt/master/minions/{{1}}/files{0}.gz".format( + remote_log + ), + "mv /var/cache/salt/master/minions/{{1}}/files{0} {{2}}/{1}".format( + remote_log, + "{0}{1}".format( + os.path.basename(remote_log), + "" if remote_log.endswith(".log") else ".log", + ), + ), + ] + ) for cmd in cmds: cmd = cmd.format(build_minion_target(options, vm_name), vm_name, workspace) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( @@ -304,45 +298,44 @@ def download_remote_logs(options): shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stream_stds=True + stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() if proc.returncode != 0: - print( - '\nFailed to execute command. Exit code: {0}'.format( - proc.returncode - ) - ) + print("\nFailed to execute command. Exit code: {0}".format(proc.returncode)) time.sleep(0.25) def download_packages(options): - print('Downloading packages...') + print("Downloading packages...") sys.stdout.flush() workspace = options.workspace vm_name = options.download_packages - for fglob in ('salt-*.rpm', - 'salt-*.deb', - 'salt-*.pkg.xz', - 'salt-buildpackage.log'): + for fglob in ("salt-*.rpm", "salt-*.deb", "salt-*.pkg.xz", "salt-buildpackage.log"): for fname in glob.glob(os.path.join(workspace, fglob)): if os.path.isfile(fname): os.unlink(fname) cmds = [ - ('salt {{0}} archive.tar czf {0}.tar.gz sources=\'*.*\' cwd={0}' - .format(options.package_artifact_dir)), - 'salt {{0}} cp.push {0}.tar.gz'.format(options.package_artifact_dir), - ('tar -C {{2}} -xzf /var/cache/salt/master/minions/{{1}}/files{0}.tar.gz' - .format(options.package_artifact_dir)), + ( + "salt {{0}} archive.tar czf {0}.tar.gz sources='*.*' cwd={0}".format( + options.package_artifact_dir + ) + ), + "salt {{0}} cp.push {0}.tar.gz".format(options.package_artifact_dir), + ( + "tar -C {{2}} -xzf /var/cache/salt/master/minions/{{1}}/files{0}.tar.gz".format( + options.package_artifact_dir + ) + ), ] for cmd in cmds: cmd = cmd.format(build_minion_target(options, vm_name), vm_name, workspace) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( @@ -350,27 +343,20 @@ def download_packages(options): shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stream_stds=True + stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() if proc.returncode != 0: - print( - '\nFailed to execute command. Exit code: {0}'.format( - proc.returncode - ) - ) + print("\nFailed to execute command. Exit code: {0}".format(proc.returncode)) time.sleep(0.25) def run(opts): - ''' + """ RUN! - ''' - vm_name = os.environ.get( - 'JENKINS_SALTCLOUD_VM_NAME', - generate_vm_name(opts) - ) + """ + vm_name = os.environ.get("JENKINS_SALTCLOUD_VM_NAME", generate_vm_name(opts)) if opts.download_remote_reports: if opts.test_without_coverage is False: @@ -380,27 +366,25 @@ def run(opts): if opts.bootstrap_salt_commit is not None: if opts.bootstrap_salt_url is None: - opts.bootstrap_salt_url = 'https://github.com/saltstack/salt.git' + opts.bootstrap_salt_url = "https://github.com/saltstack/salt.git" cmd = ( - 'salt-cloud -l debug' + "salt-cloud -l debug" ' --script-args "-D -g {bootstrap_salt_url} -n git {1}"' - ' -p {provider}_{platform} {0}'.format( + " -p {provider}_{platform} {0}".format( vm_name, os.environ.get( - 'SALT_MINION_BOOTSTRAP_RELEASE', - opts.bootstrap_salt_commit + "SALT_MINION_BOOTSTRAP_RELEASE", opts.bootstrap_salt_commit ), **opts.__dict__ ) ) else: cmd = ( - 'salt-cloud -l debug' + "salt-cloud -l debug" ' --script-args "-D -n git {1}" -p {provider}_{platform} {0}'.format( vm_name, os.environ.get( - 'SALT_MINION_BOOTSTRAP_RELEASE', - opts.bootstrap_salt_commit + "SALT_MINION_BOOTSTRAP_RELEASE", opts.bootstrap_salt_commit ), **opts.__dict__ ) @@ -408,9 +392,13 @@ def run(opts): if opts.splay is not None: # Sleep a random number of seconds cloud_downtime = random.randint(0, opts.splay) - print('Sleeping random period before calling salt-cloud: {0}'.format(cloud_downtime)) + print( + "Sleeping random period before calling salt-cloud: {0}".format( + cloud_downtime + ) + ) time.sleep(cloud_downtime) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( @@ -418,84 +406,95 @@ def run(opts): shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - stream_stds=True + stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() retcode = proc.returncode if retcode != 0: - print('Failed to bootstrap VM. Exit code: {0}'.format(retcode)) + print("Failed to bootstrap VM. Exit code: {0}".format(retcode)) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) - print('VM Bootstrapped. Exit code: {0}'.format(retcode)) + print("VM Bootstrapped. Exit code: {0}".format(retcode)) sys.stdout.flush() # Sleep a random number of seconds bootstrap_downtime = random.randint(0, opts.splay) - print('Sleeping for {0} seconds to allow the minion to breathe a little'.format(bootstrap_downtime)) + print( + "Sleeping for {0} seconds to allow the minion to breathe a little".format( + bootstrap_downtime + ) + ) sys.stdout.flush() time.sleep(bootstrap_downtime) if opts.bootstrap_salt_commit is not None: # Let's find out if the installed version matches the passed in pillar # information - print('Grabbing bootstrapped minion version information ... ') - cmd = 'salt -t 100 {0} --out json test.version'.format(build_minion_target(opts, vm_name)) - print('Running CMD: {0}'.format(cmd)) + print("Grabbing bootstrapped minion version information ... ") + cmd = "salt -t 100 {0} --out json test.version".format( + build_minion_target(opts, vm_name) + ) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, _ = proc.communicate() retcode = proc.returncode if retcode != 0: - print('Failed to get the bootstrapped minion version. Exit code: {0}'.format(retcode)) + print( + "Failed to get the bootstrapped minion version. Exit code: {0}".format( + retcode + ) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) outstr = salt.utils.stringutils.to_str(stdout).strip() if not outstr: - print('Failed to get the bootstrapped minion version(no output). Exit code: {0}'.format(retcode)) + print( + "Failed to get the bootstrapped minion version(no output). Exit code: {0}".format( + retcode + ) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) try: version_info = salt.utils.json.loads(outstr) bootstrap_minion_version = os.environ.get( - 'SALT_MINION_BOOTSTRAP_RELEASE', - opts.bootstrap_salt_commit[:7] + "SALT_MINION_BOOTSTRAP_RELEASE", opts.bootstrap_salt_commit[:7] ) - print('Minion reported salt version: {0}'.format(version_info)) + print("Minion reported salt version: {0}".format(version_info)) if bootstrap_minion_version not in version_info[vm_name]: - print('\n\nATTENTION!!!!\n') - print('The boostrapped minion version commit does not contain the desired commit:') + print("\n\nATTENTION!!!!\n") print( - ' \'{0}\' does not contain \'{1}\''.format( - version_info[vm_name], - bootstrap_minion_version + "The boostrapped minion version commit does not contain the desired commit:" + ) + print( + " '{0}' does not contain '{1}'".format( + version_info[vm_name], bootstrap_minion_version ) ) - print('\n\n') + print("\n\n") sys.stdout.flush() - #if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + # if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: # delete_vm(opts) - #sys.exit(retcode) + # sys.exit(retcode) else: - print('matches!') + print("matches!") except ValueError: - print('Failed to load any JSON from \'{0}\''.format(outstr)) + print("Failed to load any JSON from '{0}'".format(outstr)) if opts.cloud_only: # Run Cloud Provider tests preparation SLS @@ -503,9 +502,9 @@ def run(opts): time.sleep(cloud_provider_downtime) cmd = ( 'salt -t 900 {target} state.sls {cloud_prep_sls} pillar="{pillar}" ' - '--no-color'.format( + "--no-color".format( target=build_minion_target(opts, vm_name), - cloud_prep_sls='cloud-only', + cloud_prep_sls="cloud-only", pillar=build_pillar_data(opts), ) ) @@ -515,20 +514,17 @@ def run(opts): time.sleep(standard_sls_downtime) cmd = ( 'salt -t 1800 {target} state.sls {prep_sls} pillar="{pillar}" ' - '--no-color'.format( + "--no-color".format( target=build_minion_target(opts, vm_name), prep_sls=opts.prep_sls, pillar=build_pillar_data(opts), ) ) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = proc.communicate() @@ -540,9 +536,11 @@ def run(opts): retcode = proc.returncode if retcode != 0: - print('Failed to execute the preparation SLS file. Exit code: {0}'.format(retcode)) + print( + "Failed to execute the preparation SLS file. Exit code: {0}".format(retcode) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) @@ -552,35 +550,36 @@ def run(opts): # Run Cloud Provider tests pillar preparation SLS cmd = ( 'salt -t 600 {target} state.sls {cloud_prep_sls} pillar="{pillar}" ' - '--no-color'.format( + "--no-color".format( target=build_minion_target(opts, vm_name), - cloud_prep_sls='cloud-test-configs', + cloud_prep_sls="cloud-test-configs", pillar=build_pillar_data(opts), ) ) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) stdout, stderr = proc.communicate() if stdout: # DO NOT print the state return here! - print('Cloud configuration files provisioned via pillar.') + print("Cloud configuration files provisioned via pillar.") if stderr: print(salt.utils.stringutils.to_str(stderr)) sys.stdout.flush() retcode = proc.returncode if retcode != 0: - print('Failed to execute the preparation SLS file. Exit code: {0}'.format(retcode)) + print( + "Failed to execute the preparation SLS file. Exit code: {0}".format( + retcode + ) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) @@ -591,20 +590,17 @@ def run(opts): # Run the 2nd preparation SLS cmd = ( 'salt -t 30 {target} state.sls {prep_sls_2} pillar="{pillar}" ' - '--no-color'.format( + "--no-color".format( prep_sls_2=opts.prep_sls_2, pillar=build_pillar_data(opts), target=build_minion_target(opts, vm_name), ) ) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) stdout, stderr = proc.communicate() @@ -616,9 +612,13 @@ def run(opts): retcode = proc.returncode if retcode != 0: - print('Failed to execute the 2nd preparation SLS file. Exit code: {0}'.format(retcode)) + print( + "Failed to execute the 2nd preparation SLS file. Exit code: {0}".format( + retcode + ) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) @@ -628,46 +628,61 @@ def run(opts): time.sleep(test_git_downtime) # Let's find out if the cloned repository if checked out from the # desired repository - print('Grabbing the cloned repository remotes information ... ') - cmd = 'salt -t 100 {0} --out json git.remote_get /testing'.format(build_minion_target(opts, vm_name)) - print('Running CMD: {0}'.format(cmd)) + print("Grabbing the cloned repository remotes information ... ") + cmd = "salt -t 100 {0} --out json git.remote_get /testing".format( + build_minion_target(opts, vm_name) + ) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, _ = proc.communicate() retcode = proc.returncode if retcode != 0: - print('Failed to get the cloned repository remote. Exit code: {0}'.format(retcode)) + print( + "Failed to get the cloned repository remote. Exit code: {0}".format( + retcode + ) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) if not stdout: - print('Failed to get the cloned repository remote(no output). Exit code: {0}'.format(retcode)) + print( + "Failed to get the cloned repository remote(no output). Exit code: {0}".format( + retcode + ) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) try: remotes_info = salt.utils.json.loads(stdout.strip()) - if remotes_info is None or remotes_info[vm_name] is None or opts.test_git_url not in remotes_info[vm_name]: - print('The cloned repository remote is not the desired one:') - print(' \'{0}\' is not in {1}'.format(opts.test_git_url, remotes_info)) + if ( + remotes_info is None + or remotes_info[vm_name] is None + or opts.test_git_url not in remotes_info[vm_name] + ): + print("The cloned repository remote is not the desired one:") + print(" '{0}' is not in {1}".format(opts.test_git_url, remotes_info)) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) - print('matches!') + print("matches!") except ValueError: - print('Failed to load any JSON from \'{0}\''.format(salt.utils.stringutils.to_str(stdout).strip())) + print( + "Failed to load any JSON from '{0}'".format( + salt.utils.stringutils.to_str(stdout).strip() + ) + ) if opts.test_git_commit is not None: test_git_commit_downtime = random.randint(1, opts.splay) @@ -675,65 +690,75 @@ def run(opts): # Let's find out if the cloned repository is checked out at the desired # commit - print('Grabbing the cloned repository commit information ... ') - cmd = 'salt -t 100 {0} --out json git.revision /testing'.format(build_minion_target(opts, vm_name)) - print('Running CMD: {0}'.format(cmd)) + print("Grabbing the cloned repository commit information ... ") + cmd = "salt -t 100 {0} --out json git.revision /testing".format( + build_minion_target(opts, vm_name) + ) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, _ = proc.communicate() sys.stdout.flush() retcode = proc.returncode if retcode != 0: - print('Failed to get the cloned repository revision. Exit code: {0}'.format(retcode)) + print( + "Failed to get the cloned repository revision. Exit code: {0}".format( + retcode + ) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) if not stdout: - print('Failed to get the cloned repository revision(no output). Exit code: {0}'.format(retcode)) + print( + "Failed to get the cloned repository revision(no output). Exit code: {0}".format( + retcode + ) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) try: revision_info = salt.utils.json.loads(stdout.strip()) if revision_info[vm_name][7:] != opts.test_git_commit[7:]: - print('The cloned repository commit is not the desired one:') - print(' \'{0}\' != \'{1}\''.format(revision_info[vm_name][:7], opts.test_git_commit[:7])) + print("The cloned repository commit is not the desired one:") + print( + " '{0}' != '{1}'".format( + revision_info[vm_name][:7], opts.test_git_commit[:7] + ) + ) sys.stdout.flush() - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) - print('matches!') + print("matches!") except ValueError: - print('Failed to load any JSON from \'{0}\''.format(salt.utils.stringutils.to_str(stdout).strip())) + print( + "Failed to load any JSON from '{0}'".format( + salt.utils.stringutils.to_str(stdout).strip() + ) + ) # Run tests here test_begin_downtime = random.randint(3, opts.splay) time.sleep(test_begin_downtime) - cmd = ( - 'salt -t 1800 {target} state.sls {sls} pillar="{pillar}" --no-color'.format( - sls=opts.sls, - pillar=build_pillar_data(opts), - target=build_minion_target(opts, vm_name), - ) + cmd = 'salt -t 1800 {target} state.sls {sls} pillar="{pillar}" --no-color'.format( + sls=opts.sls, + pillar=build_pillar_data(opts), + target=build_minion_target(opts, vm_name), ) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = proc.communicate() @@ -745,8 +770,8 @@ def run(opts): sys.stdout.flush() try: - match = re.search(r'Test Suite Exit Code: (?P<exitcode>[\d]+)', outstr) - retcode = int(match.group('exitcode')) + match = re.search(r"Test Suite Exit Code: (?P<exitcode>[\d]+)", outstr) + retcode = int(match.group("exitcode")) except AttributeError: # No regex matching retcode = 1 @@ -763,20 +788,14 @@ def run(opts): if retcode == 0: # Build packages time.sleep(3) - cmd = ( - 'salt -t 1800 {target} state.sls buildpackage pillar="{pillar}" --no-color'.format( - pillar=build_pillar_data(opts), - target=build_minion_target(opts, vm_name), - ) + cmd = 'salt -t 1800 {target} state.sls buildpackage pillar="{pillar}" --no-color'.format( + pillar=build_pillar_data(opts), target=build_minion_target(opts, vm_name), ) - print('Running CMD: {0}'.format(cmd)) + print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( - cmd, - shell=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = proc.communicate() @@ -796,175 +815,170 @@ def run(opts): if opts.test_without_coverage is False: download_coverage_report(opts) - if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: + if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) return retcode def parse(): - ''' + """ Parse the CLI options - ''' + """ parser = optparse.OptionParser() parser.add_option( - '--vm-prefix', - default=os.environ.get('JENKINS_VM_NAME_PREFIX', 'ZJENKINS'), - help='The bootstrapped machine name prefix' + "--vm-prefix", + default=os.environ.get("JENKINS_VM_NAME_PREFIX", "ZJENKINS"), + help="The bootstrapped machine name prefix", ) parser.add_option( - '-w', '--workspace', + "-w", + "--workspace", default=os.path.abspath( - os.environ.get( - 'WORKSPACE', - os.path.dirname(os.path.dirname(__file__)) - ) + os.environ.get("WORKSPACE", os.path.dirname(os.path.dirname(__file__))) ), - help='Path the execution workspace' + help="Path the execution workspace", ) parser.add_option( - '--platform', - default=os.environ.get('JENKINS_SALTCLOUD_VM_PLATFORM', None), - help='The target platform, choose from:\ncent6\ncent5\nubuntu12.04') - parser.add_option( - '--provider', - default=os.environ.get('JENKINS_SALTCLOUD_VM_PROVIDER', None), - help='The vm provider') - parser.add_option( - '--bootstrap-salt-url', - default=None, - help='The salt git repository url used to boostrap a minion') - parser.add_option( - '--bootstrap-salt-commit', - default=None, - help='The salt git commit used to boostrap a minion') - parser.add_option( - '--test-git-url', - default=None, - help='The testing git repository url') - parser.add_option( - '--test-git-commit', - default=None, - help='The testing git commit to track') - parser.add_option( - '--test-transport', - default='zeromq', - choices=('zeromq', 'tcp'), - help=('Select which transport to run the integration tests with, ' - 'zeromq or tcp. Default: %default') + "--platform", + default=os.environ.get("JENKINS_SALTCLOUD_VM_PLATFORM", None), + help="The target platform, choose from:\ncent6\ncent5\nubuntu12.04", ) parser.add_option( - '--test-without-coverage', + "--provider", + default=os.environ.get("JENKINS_SALTCLOUD_VM_PROVIDER", None), + help="The vm provider", + ) + parser.add_option( + "--bootstrap-salt-url", + default=None, + help="The salt git repository url used to boostrap a minion", + ) + parser.add_option( + "--bootstrap-salt-commit", + default=None, + help="The salt git commit used to boostrap a minion", + ) + parser.add_option( + "--test-git-url", default=None, help="The testing git repository url" + ) + parser.add_option( + "--test-git-commit", default=None, help="The testing git commit to track" + ) + parser.add_option( + "--test-transport", + default="zeromq", + choices=("zeromq", "tcp"), + help=( + "Select which transport to run the integration tests with, " + "zeromq or tcp. Default: %default" + ), + ) + parser.add_option( + "--test-without-coverage", default=False, - action='store_true', - help='Do not generate coverage reports' + action="store_true", + help="Do not generate coverage reports", ) parser.add_option( - '--prep-sls', - default='git.salt', - help='The sls file to execute to prepare the system') + "--prep-sls", + default="git.salt", + help="The sls file to execute to prepare the system", + ) parser.add_option( - '--prep-sls-2', - default=None, - help='An optional 2nd system preparation SLS') + "--prep-sls-2", default=None, help="An optional 2nd system preparation SLS" + ) parser.add_option( - '--sls', - default='testrun-no-deps', - help='The final sls file to execute') + "--sls", default="testrun-no-deps", help="The final sls file to execute" + ) parser.add_option( - '--pillar', - action='append', + "--pillar", + action="append", nargs=2, - help='Pillar (key, value)s to pass to the sls file. ' - 'Example: \'--pillar pillar_key pillar_value\'') + help="Pillar (key, value)s to pass to the sls file. " + "Example: '--pillar pillar_key pillar_value'", + ) parser.add_option( - '--no-clean', - dest='clean', + "--no-clean", + dest="clean", default=True, - action='store_false', - help='Clean up the built vm') + action="store_false", + help="Clean up the built vm", + ) parser.add_option( - '--echo-parseable-environment', + "--echo-parseable-environment", default=False, - action='store_true', - help='Print a parseable KEY=VAL output' + action="store_true", + help="Print a parseable KEY=VAL output", ) + parser.add_option("--pull-request", type=int, help="Include the PR info only") + parser.add_option("--delete-vm", default=None, help="Delete a running VM") parser.add_option( - '--pull-request', - type=int, - help='Include the PR info only' - ) - parser.add_option( - '--delete-vm', - default=None, - help='Delete a running VM' - ) - parser.add_option( - '--download-remote-reports', + "--download-remote-reports", default=False, - action='store_true', - help='Download remote reports when running remote \'testrun\' state' + action="store_true", + help="Download remote reports when running remote 'testrun' state", ) parser.add_option( - '--download-unittest-reports', + "--download-unittest-reports", default=None, - help='Download the XML unittest results' + help="Download the XML unittest results", ) parser.add_option( - '--download-coverage-report', + "--download-coverage-report", default=None, - help='Download the XML coverage reports' + help="Download the XML coverage reports", ) parser.add_option( - '--remote-log-path', - action='append', + "--remote-log-path", + action="append", default=[], - help='Provide additional log paths to download from remote minion' + help="Provide additional log paths to download from remote minion", ) parser.add_option( - '--download-remote-logs', + "--download-remote-logs", default=None, - help='Download remote minion and runtests log files' + help="Download remote minion and runtests log files", ) parser.add_option( - '--grain-target', - action='append', + "--grain-target", + action="append", default=[], - help='Match minions using compound matchers, the minion ID, plus the passed grain.' + help="Match minions using compound matchers, the minion ID, plus the passed grain.", ) parser.add_option( - '--cloud-only', + "--cloud-only", default=False, - action='store_true', - help='Run the cloud provider tests only.' + action="store_true", + help="Run the cloud provider tests only.", ) parser.add_option( - '--build-packages', + "--build-packages", default=True, - action='store_true', - help='Run buildpackage.py to create packages off of the git build.' + action="store_true", + help="Run buildpackage.py to create packages off of the git build.", ) # These next three options are ignored if --build-packages is False parser.add_option( - '--package-source-dir', - default='/testing', - help='Directory where the salt source code checkout is found ' - '(default: %default)', + "--package-source-dir", + default="/testing", + help="Directory where the salt source code checkout is found " + "(default: %default)", ) parser.add_option( - '--package-build-dir', - default='/tmp/salt-buildpackage', - help='Build root for automated package builds (default: %default)', + "--package-build-dir", + default="/tmp/salt-buildpackage", + help="Build root for automated package builds (default: %default)", ) parser.add_option( - '--package-artifact-dir', - default='/tmp/salt-packages', - help='Location on the minion from which packages should be ' - 'retrieved (default: %default)', + "--package-artifact-dir", + default="/tmp/salt-packages", + help="Location on the minion from which packages should be " + "retrieved (default: %default)", ) parser.add_option( - '--splay', - default='10', - help='The number of seconds across which calls to provisioning components should be made' + "--splay", + default="10", + help="The number of seconds across which calls to provisioning components should be made", ) options, args = parser.parse_args() @@ -987,22 +1001,22 @@ def parse(): parser.exit(0) if not options.platform and not options.pull_request: - parser.exit('--platform or --pull-request is required') + parser.exit("--platform or --pull-request is required") if not options.provider and not options.pull_request: - parser.exit('--provider or --pull-request is required') + parser.exit("--provider or --pull-request is required") if options.echo_parseable_environment: echo_parseable_environment(options, parser) parser.exit(0) if not options.test_git_commit and not options.pull_request: - parser.exit('--commit or --pull-request is required') + parser.exit("--commit or --pull-request is required") return options -if __name__ == '__main__': +if __name__ == "__main__": exit_code = run(parse()) - print('Exit Code: {0}'.format(exit_code)) + print("Exit Code: {0}".format(exit_code)) sys.exit(exit_code) diff --git a/tests/kitchen/test_kitchen.py b/tests/kitchen/test_kitchen.py index 013d9e403ef..d0e57be211d 100644 --- a/tests/kitchen/test_kitchen.py +++ b/tests/kitchen/test_kitchen.py @@ -1,39 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" Test wrapper for running all KitchenSalt tests All directories in 'tests/kitchen/' will be treated as a separate test under the KitchenTestCase. -''' +""" from __future__ import absolute_import + import os -from tests.support.unit import TestCase, skipIf -import setup - import salt.utils.path +import setup from salt.modules import cmdmod as cmd +from tests.support.unit import TestCase, skipIf CURRENT_DIR = os.path.dirname(os.path.realpath(__file__)) -@skipIf(not salt.utils.path.which('bundle'), 'Bundler is not installed') +@skipIf(not salt.utils.path.which("bundle"), "Bundler is not installed") class KitchenTestCase(TestCase): - ''' + """ Test kitchen environments - ''' + """ + @classmethod def setUpClass(cls): - ''' + """ setup kitchen tests - ''' - cls.topdir = '/' + os.path.join(*CURRENT_DIR.split('/')[:-2]) - cls.use_vt = int(os.environ.get('TESTS_LOG_LEVEL')) >= 5 - cmd.run('python setup.py sdist', cwd=cls.topdir) - cmd.run('bundle install', cwd=CURRENT_DIR) + """ + cls.topdir = "/" + os.path.join(*CURRENT_DIR.split("/")[:-2]) + cls.use_vt = int(os.environ.get("TESTS_LOG_LEVEL")) >= 5 + cmd.run("python setup.py sdist", cwd=cls.topdir) + cmd.run("bundle install", cwd=CURRENT_DIR) cls.env = { - 'KITCHEN_YAML': os.path.join(CURRENT_DIR, '.kitchen.yml'), - 'SALT_SDIST_PATH': os.path.join(cls.topdir, 'dist', 'salt-{0}.tar.gz'.format(setup.__version__)), + "KITCHEN_YAML": os.path.join(CURRENT_DIR, ".kitchen.yml"), + "SALT_SDIST_PATH": os.path.join( + cls.topdir, "dist", "salt-{0}.tar.gz".format(setup.__version__) + ), } @classmethod @@ -43,8 +46,8 @@ class KitchenTestCase(TestCase): def tearDown(self): cmd.run( - 'bundle exec kitchen destroy all', - cwd=os.path.join(CURRENT_DIR, 'tests', self.testdir), + "bundle exec kitchen destroy all", + cwd=os.path.join(CURRENT_DIR, "tests", self.testdir), env=self.env, use_vt=self.use_vt, ) @@ -54,31 +57,33 @@ class KitchenTestCase(TestCase): def func_builder(testdir): def func(self): self.testdir = testdir - if 'TESTS_XML_OUTPUT_DIR' in os.environ: - self.env['TESTS_JUNIT_XML_PATH'] = '{0}/kitchen.tests.{1}.$KITCHEN_SUITE.$KITCHEN_PLATFORM.xml'.format( - os.environ.get('TESTS_XML_OUTPUT_DIR'), - self.testdir, + if "TESTS_XML_OUTPUT_DIR" in os.environ: + self.env[ + "TESTS_JUNIT_XML_PATH" + ] = "{0}/kitchen.tests.{1}.$KITCHEN_SUITE.$KITCHEN_PLATFORM.xml".format( + os.environ.get("TESTS_XML_OUTPUT_DIR"), self.testdir, ) self.assertEqual( cmd.retcode( - 'bundle exec kitchen converge -c 999 all', - cwd=os.path.join(CURRENT_DIR, 'tests', self.testdir), + "bundle exec kitchen converge -c 999 all", + cwd=os.path.join(CURRENT_DIR, "tests", self.testdir), env=self.env, use_vt=self.use_vt, ), - 0 + 0, ) self.assertEqual( cmd.retcode( - 'bundle exec kitchen verify all', - cwd=os.path.join(CURRENT_DIR, 'tests', self.testdir), + "bundle exec kitchen verify all", + cwd=os.path.join(CURRENT_DIR, "tests", self.testdir), env=self.env, use_vt=self.use_vt, ), - 0 + 0, ) + return func -for testdir in os.listdir(os.path.join(CURRENT_DIR, 'tests')): - setattr(KitchenTestCase, 'test_kitchen_{0}'.format(testdir), func_builder(testdir)) +for testdir in os.listdir(os.path.join(CURRENT_DIR, "tests")): + setattr(KitchenTestCase, "test_kitchen_{0}".format(testdir), func_builder(testdir)) diff --git a/tests/minionswarm.py b/tests/minionswarm.py index 5dc8de6ec9a..2feee0d5ff6 100644 --- a/tests/minionswarm.py +++ b/tests/minionswarm.py @@ -1,154 +1,174 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -''' +""" The minionswarm script will start a group of salt minions with different ids on a single system to test scale capabilities -''' +""" # pylint: disable=resource-leakage # Import Python Libs from __future__ import absolute_import, print_function -import os -import time -import signal -import optparse -import subprocess -import random -import tempfile -import shutil -import sys + import hashlib +import optparse +import os +import random +import shutil +import signal +import subprocess +import sys +import tempfile +import time import uuid # Import salt libs import salt import salt.utils.files import salt.utils.yaml +import tests.support.runtests # Import third party libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -import tests.support.runtests - OSES = [ - 'Arch', - 'Ubuntu', - 'Debian', - 'CentOS', - 'Fedora', - 'Gentoo', - 'AIX', - 'Solaris', - ] + "Arch", + "Ubuntu", + "Debian", + "CentOS", + "Fedora", + "Gentoo", + "AIX", + "Solaris", +] VERS = [ - '2014.1.6', - '2014.7.4', - '2015.5.5', - '2015.8.0', - ] + "2014.1.6", + "2014.7.4", + "2015.5.5", + "2015.8.0", +] def parse(): - ''' + """ Parse the cli options - ''' + """ parser = optparse.OptionParser() parser.add_option( - '-m', - '--minions', - dest='minions', + "-m", + "--minions", + dest="minions", default=5, - type='int', - help='The number of minions to make') + type="int", + help="The number of minions to make", + ) parser.add_option( - '-M', - action='store_true', - dest='master_too', + "-M", + action="store_true", + dest="master_too", default=False, - help='Run a local master and tell the minions to connect to it') + help="Run a local master and tell the minions to connect to it", + ) parser.add_option( - '--master', - dest='master', - default='salt', - help='The location of the salt master that this swarm will serve') + "--master", + dest="master", + default="salt", + help="The location of the salt master that this swarm will serve", + ) parser.add_option( - '--name', - '-n', - dest='name', - default='ms', - help=('Give the minions an alternative id prefix, this is used ' - 'when minions from many systems are being aggregated onto ' - 'a single master')) + "--name", + "-n", + dest="name", + default="ms", + help=( + "Give the minions an alternative id prefix, this is used " + "when minions from many systems are being aggregated onto " + "a single master" + ), + ) parser.add_option( - '--rand-os', - dest='rand_os', + "--rand-os", + dest="rand_os", default=False, - action='store_true', - help='Each Minion claims a different os grain') + action="store_true", + help="Each Minion claims a different os grain", + ) parser.add_option( - '--rand-ver', - dest='rand_ver', + "--rand-ver", + dest="rand_ver", default=False, - action='store_true', - help='Each Minion claims a different version grain') + action="store_true", + help="Each Minion claims a different version grain", + ) parser.add_option( - '--rand-machine-id', - dest='rand_machine_id', + "--rand-machine-id", + dest="rand_machine_id", default=False, - action='store_true', - help='Each Minion claims a different machine id grain') + action="store_true", + help="Each Minion claims a different machine id grain", + ) parser.add_option( - '--rand-uuid', - dest='rand_uuid', + "--rand-uuid", + dest="rand_uuid", default=False, - action='store_true', - help='Each Minion claims a different UUID grain') + action="store_true", + help="Each Minion claims a different UUID grain", + ) parser.add_option( - '-k', - '--keep-modules', - dest='keep', - default='', - help='A comma delimited list of modules to enable') + "-k", + "--keep-modules", + dest="keep", + default="", + help="A comma delimited list of modules to enable", + ) parser.add_option( - '-f', - '--foreground', - dest='foreground', + "-f", + "--foreground", + dest="foreground", default=False, - action='store_true', - help=('Run the minions with debug output of the swarm going to ' - 'the terminal')) + action="store_true", + help=( + "Run the minions with debug output of the swarm going to " "the terminal" + ), + ) parser.add_option( - '--temp-dir', - dest='temp_dir', + "--temp-dir", + dest="temp_dir", default=None, - help='Place temporary files/directories here') + help="Place temporary files/directories here", + ) parser.add_option( - '--no-clean', - action='store_true', + "--no-clean", + action="store_true", default=False, - help='Don\'t cleanup temporary files/directories') + help="Don't cleanup temporary files/directories", + ) parser.add_option( - '--root-dir', - dest='root_dir', + "--root-dir", + dest="root_dir", default=None, - help='Override the minion root_dir config') + help="Override the minion root_dir config", + ) parser.add_option( - '--transport', - dest='transport', - default='zeromq', - help='Declare which transport to use, default is zeromq') + "--transport", + dest="transport", + default="zeromq", + help="Declare which transport to use, default is zeromq", + ) parser.add_option( - '--start-delay', - dest='start_delay', + "--start-delay", + dest="start_delay", default=0.0, - type='float', - help='Seconds to wait between minion starts') + type="float", + help="Seconds to wait between minion starts", + ) parser.add_option( - '-c', '--config-dir', default='', - help=('Pass in a configuration directory containing base configuration.') - ) - parser.add_option('-u', '--user', default=tests.support.runtests.this_user()) + "-c", + "--config-dir", + default="", + help=("Pass in a configuration directory containing base configuration."), + ) + parser.add_option("-u", "--user", default=tests.support.runtests.this_user()) options, _args = parser.parse_args() @@ -161,98 +181,94 @@ def parse(): class Swarm(object): - ''' + """ Create a swarm of minions - ''' + """ + def __init__(self, opts): self.opts = opts # If given a temp_dir, use it for temporary files - if opts['temp_dir']: - self.swarm_root = opts['temp_dir'] + if opts["temp_dir"]: + self.swarm_root = opts["temp_dir"] else: # If given a root_dir, keep the tmp files there as well - if opts['root_dir']: - tmpdir = os.path.join(opts['root_dir'], 'tmp') + if opts["root_dir"]: + tmpdir = os.path.join(opts["root_dir"], "tmp") else: - tmpdir = opts['root_dir'] + tmpdir = opts["root_dir"] self.swarm_root = tempfile.mkdtemp( - prefix='mswarm-root', suffix='.d', - dir=tmpdir) + prefix="mswarm-root", suffix=".d", dir=tmpdir + ) - if self.opts['transport'] == 'zeromq': + if self.opts["transport"] == "zeromq": self.pki = self._pki_dir() - self.zfill = len(str(self.opts['minions'])) + self.zfill = len(str(self.opts["minions"])) self.confs = set() random.seed(0) def _pki_dir(self): - ''' + """ Create the shared pki directory - ''' - path = os.path.join(self.swarm_root, 'pki') + """ + path = os.path.join(self.swarm_root, "pki") if not os.path.exists(path): os.makedirs(path) - print('Creating shared pki keys for the swarm on: {0}'.format(path)) + print("Creating shared pki keys for the swarm on: {0}".format(path)) subprocess.call( - 'salt-key -c {0} --gen-keys minion --gen-keys-dir {0} ' - '--log-file {1} --user {2}'.format( - path, os.path.join(path, 'keys.log'), self.opts['user'], - ), shell=True + "salt-key -c {0} --gen-keys minion --gen-keys-dir {0} " + "--log-file {1} --user {2}".format( + path, os.path.join(path, "keys.log"), self.opts["user"], + ), + shell=True, ) - print('Keys generated') + print("Keys generated") return path def start(self): - ''' + """ Start the magic!! - ''' - if self.opts['master_too']: + """ + if self.opts["master_too"]: master_swarm = MasterSwarm(self.opts) master_swarm.start() minions = MinionSwarm(self.opts) minions.start_minions() - print('Starting minions...') - #self.start_minions() - print('All {0} minions have started.'.format(self.opts['minions'])) - print('Waiting for CTRL-C to properly shutdown minions...') + print("Starting minions...") + # self.start_minions() + print("All {0} minions have started.".format(self.opts["minions"])) + print("Waiting for CTRL-C to properly shutdown minions...") while True: try: time.sleep(5) except KeyboardInterrupt: - print('\nShutting down minions') + print("\nShutting down minions") self.clean_configs() break def shutdown(self): - ''' + """ Tear it all down - ''' - print('Killing any remaining running minions') - subprocess.call( - 'pkill -KILL -f "python.*salt-minion"', - shell=True - ) - if self.opts['master_too']: - print('Killing any remaining masters') - subprocess.call( - 'pkill -KILL -f "python.*salt-master"', - shell=True - ) - if not self.opts['no_clean']: - print('Remove ALL related temp files/directories') + """ + print("Killing any remaining running minions") + subprocess.call('pkill -KILL -f "python.*salt-minion"', shell=True) + if self.opts["master_too"]: + print("Killing any remaining masters") + subprocess.call('pkill -KILL -f "python.*salt-master"', shell=True) + if not self.opts["no_clean"]: + print("Remove ALL related temp files/directories") shutil.rmtree(self.swarm_root) - print('Done') + print("Done") def clean_configs(self): - ''' + """ Clean up the config files - ''' + """ for path in self.confs: - pidfile = '{0}.pid'.format(path) + pidfile = "{0}.pid".format(path) try: try: with salt.utils.files.fopen(pidfile) as fp_: @@ -262,171 +278,169 @@ class Swarm(object): pass if os.path.exists(pidfile): os.remove(pidfile) - if not self.opts['no_clean']: + if not self.opts["no_clean"]: shutil.rmtree(path) except (OSError, IOError): pass class MinionSwarm(Swarm): - ''' + """ Create minions - ''' + """ + def start_minions(self): - ''' + """ Iterate over the config files and start up the minions - ''' + """ self.prep_configs() for path in self.confs: - cmd = 'salt-minion -c {0} --pid-file {1}'.format( - path, - '{0}.pid'.format(path) - ) - if self.opts['foreground']: - cmd += ' -l debug &' + cmd = "salt-minion -c {0} --pid-file {1}".format( + path, "{0}.pid".format(path) + ) + if self.opts["foreground"]: + cmd += " -l debug &" else: - cmd += ' -d &' + cmd += " -d &" subprocess.call(cmd, shell=True) - time.sleep(self.opts['start_delay']) + time.sleep(self.opts["start_delay"]) def mkconf(self, idx): - ''' + """ Create a config file for a single minion - ''' + """ data = {} - if self.opts['config_dir']: - spath = os.path.join(self.opts['config_dir'], 'minion') + if self.opts["config_dir"]: + spath = os.path.join(self.opts["config_dir"], "minion") with salt.utils.files.fopen(spath) as conf: data = salt.utils.yaml.safe_load(conf) or {} - minion_id = '{0}-{1}'.format( - self.opts['name'], - str(idx).zfill(self.zfill) - ) + minion_id = "{0}-{1}".format(self.opts["name"], str(idx).zfill(self.zfill)) dpath = os.path.join(self.swarm_root, minion_id) if not os.path.exists(dpath): os.makedirs(dpath) - data.update({ - 'id': minion_id, - 'user': self.opts['user'], - 'cachedir': os.path.join(dpath, 'cache'), - 'master': self.opts['master'], - 'log_file': os.path.join(dpath, 'minion.log'), - 'grains': {}, - }) + data.update( + { + "id": minion_id, + "user": self.opts["user"], + "cachedir": os.path.join(dpath, "cache"), + "master": self.opts["master"], + "log_file": os.path.join(dpath, "minion.log"), + "grains": {}, + } + ) - if self.opts['transport'] == 'zeromq': - minion_pkidir = os.path.join(dpath, 'pki') + if self.opts["transport"] == "zeromq": + minion_pkidir = os.path.join(dpath, "pki") if not os.path.exists(minion_pkidir): os.makedirs(minion_pkidir) - minion_pem = os.path.join(self.pki, 'minion.pem') - minion_pub = os.path.join(self.pki, 'minion.pub') + minion_pem = os.path.join(self.pki, "minion.pem") + minion_pub = os.path.join(self.pki, "minion.pub") shutil.copy(minion_pem, minion_pkidir) shutil.copy(minion_pub, minion_pkidir) - data['pki_dir'] = minion_pkidir - elif self.opts['transport'] == 'tcp': - data['transport'] = 'tcp' + data["pki_dir"] = minion_pkidir + elif self.opts["transport"] == "tcp": + data["transport"] = "tcp" - if self.opts['root_dir']: - data['root_dir'] = self.opts['root_dir'] + if self.opts["root_dir"]: + data["root_dir"] = self.opts["root_dir"] - path = os.path.join(dpath, 'minion') + path = os.path.join(dpath, "minion") - if self.opts['keep']: - keep = self.opts['keep'].split(',') - modpath = os.path.join(os.path.dirname(salt.__file__), 'modules') - fn_prefixes = (fn_.partition('.')[0] for fn_ in os.listdir(modpath)) + if self.opts["keep"]: + keep = self.opts["keep"].split(",") + modpath = os.path.join(os.path.dirname(salt.__file__), "modules") + fn_prefixes = (fn_.partition(".")[0] for fn_ in os.listdir(modpath)) ignore = [fn_prefix for fn_prefix in fn_prefixes if fn_prefix not in keep] - data['disable_modules'] = ignore + data["disable_modules"] = ignore - if self.opts['rand_os']: - data['grains']['os'] = random.choice(OSES) - if self.opts['rand_ver']: - data['grains']['saltversion'] = random.choice(VERS) - if self.opts['rand_machine_id']: - data['grains']['machine_id'] = hashlib.md5(minion_id).hexdigest() - if self.opts['rand_uuid']: - data['grains']['uuid'] = str(uuid.uuid4()) + if self.opts["rand_os"]: + data["grains"]["os"] = random.choice(OSES) + if self.opts["rand_ver"]: + data["grains"]["saltversion"] = random.choice(VERS) + if self.opts["rand_machine_id"]: + data["grains"]["machine_id"] = hashlib.md5(minion_id).hexdigest() + if self.opts["rand_uuid"]: + data["grains"]["uuid"] = str(uuid.uuid4()) - with salt.utils.files.fopen(path, 'w+') as fp_: + with salt.utils.files.fopen(path, "w+") as fp_: salt.utils.yaml.safe_dump(data, fp_) self.confs.add(dpath) def prep_configs(self): - ''' + """ Prepare the confs set - ''' - for idx in range(self.opts['minions']): + """ + for idx in range(self.opts["minions"]): self.mkconf(idx) class MasterSwarm(Swarm): - ''' + """ Create one or more masters - ''' + """ + def __init__(self, opts): super(MasterSwarm, self).__init__(opts) - self.conf = os.path.join(self.swarm_root, 'master') + self.conf = os.path.join(self.swarm_root, "master") def start(self): - ''' + """ Prep the master start and fire it off - ''' + """ # sys.stdout for no newline - sys.stdout.write('Generating master config...') + sys.stdout.write("Generating master config...") self.mkconf() - print('done') + print("done") - sys.stdout.write('Starting master...') + sys.stdout.write("Starting master...") self.start_master() - print('done') + print("done") def start_master(self): - ''' + """ Do the master start - ''' - cmd = 'salt-master -c {0} --pid-file {1}'.format( - self.conf, - '{0}.pid'.format(self.conf) - ) - if self.opts['foreground']: - cmd += ' -l debug &' + """ + cmd = "salt-master -c {0} --pid-file {1}".format( + self.conf, "{0}.pid".format(self.conf) + ) + if self.opts["foreground"]: + cmd += " -l debug &" else: - cmd += ' -d &' + cmd += " -d &" subprocess.call(cmd, shell=True) def mkconf(self): # pylint: disable=W0221 - ''' + """ Make a master config and write it' - ''' + """ data = {} - if self.opts['config_dir']: - spath = os.path.join(self.opts['config_dir'], 'master') + if self.opts["config_dir"]: + spath = os.path.join(self.opts["config_dir"], "master") with salt.utils.files.fopen(spath) as conf: data = salt.utils.yaml.safe_load(conf) - data.update({ - 'log_file': os.path.join(self.conf, 'master.log'), - 'open_mode': True # TODO Pre-seed keys - }) + data.update( + { + "log_file": os.path.join(self.conf, "master.log"), + "open_mode": True, # TODO Pre-seed keys + } + ) os.makedirs(self.conf) - path = os.path.join(self.conf, 'master') + path = os.path.join(self.conf, "master") - with salt.utils.files.fopen(path, 'w+') as fp_: + with salt.utils.files.fopen(path, "w+") as fp_: salt.utils.yaml.safe_dump(data, fp_) def shutdown(self): - print('Killing master') - subprocess.call( - 'pkill -KILL -f "python.*salt-master"', - shell=True - ) - print('Master killed') + print("Killing master") + subprocess.call('pkill -KILL -f "python.*salt-master"', shell=True) + print("Master killed") # pylint: disable=C0103 -if __name__ == '__main__': +if __name__ == "__main__": swarm = Swarm(parse()) try: swarm.start() diff --git a/tests/modparser.py b/tests/modparser.py index dbdc6d7ce3d..081b0d29b79 100644 --- a/tests/modparser.py +++ b/tests/modparser.py @@ -2,107 +2,104 @@ #!/usr/bin/env python2 from __future__ import absolute_import, print_function -try: - import argparse # pylint: disable=minimum-python-version - HAS_ARGPARSE = True -except ImportError: - HAS_ARGPARSE = False -import os -import sys -import os + import modulefinder +import os import pprint +import sys + import salt.utils.json import salt.utils.yaml +try: + import argparse # pylint: disable=minimum-python-version + + HAS_ARGPARSE = True +except ImportError: + HAS_ARGPARSE = False + def parse(): - ''' + """ Parse the arguments - ''' + """ parser = argparse.ArgumentParser() parser.add_argument( - '-r', - '--root', - dest='root', - default='.', - help='The base code directory to look in') + "-r", + "--root", + dest="root", + default=".", + help="The base code directory to look in", + ) + parser.add_argument("-i", "--bif", dest="bif", default="site-packages") parser.add_argument( - '-i', - '--bif', - dest='bif', - default='site-packages') - parser.add_argument( - '-f', - '--format', - dest='format', - choices=('pprint', 'yaml'), - default='pprint') + "-f", "--format", dest="format", choices=("pprint", "yaml"), default="pprint" + ) out = parser.parse_args() return out.__dict__ def mod_data(opts, full): - ''' + """ Grab the module's data - ''' + """ ret = {} finder = modulefinder.ModuleFinder() try: finder.load_file(full) except ImportError as exc: - print('ImportError - {0} (Reason: {1})'.format(full, exc), file=sys.stderr) + print("ImportError - {0} (Reason: {1})".format(full, exc), file=sys.stderr) return ret for name, mod in finder.modules.items(): - basemod = name.split('.')[0] + basemod = name.split(".")[0] if basemod in ret: continue - if basemod.startswith('_'): + if basemod.startswith("_"): continue if not mod.__file__: continue - if opts['bif'] not in mod.__file__: + if opts["bif"] not in mod.__file__: # Bif - skip continue if name == os.path.basename(mod.__file__)[:-3]: continue ret[basemod] = mod.__file__ for name, err in finder.badmodules.items(): - basemod = name.split('.')[0] + basemod = name.split(".")[0] if basemod in ret: continue - if basemod.startswith('_'): + if basemod.startswith("_"): continue ret[basemod] = err return ret def scan(opts): - ''' + """ Scan the provided root for python source files - ''' + """ ret = {} - for root, dirs, files in os.walk(opts['root']): + for root, dirs, files in os.walk(opts["root"]): for fn_ in files: full = os.path.join(root, fn_) - if full.endswith('.py'): + if full.endswith(".py"): ret.update(mod_data(opts, full)) return ret -if __name__ == '__main__': +if __name__ == "__main__": if not HAS_ARGPARSE: - print('The argparse python module is required') + print("The argparse python module is required") opts = parse() try: scand = scan(opts) - if opts['format'] == 'yaml': + if opts["format"] == "yaml": print(salt.utils.yaml.safe_dump(scand)) - if opts['format'] == 'json': + if opts["format"] == "json": print(salt.utils.json.dumps(scand)) else: pprint.pprint(scand) exit(0) except KeyboardInterrupt: - print('\nInterrupted on user request', file=sys.stderr) + print("\nInterrupted on user request", file=sys.stderr) exit(1) diff --git a/tests/multimaster/__init__.py b/tests/multimaster/__init__.py index 0663c008cbc..237c81f868d 100644 --- a/tests/multimaster/__init__.py +++ b/tests/multimaster/__init__.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" Set up the Salt multimaster test suite -''' +""" # Import Python libs from __future__ import absolute_import, print_function + import copy import logging import os @@ -16,27 +17,6 @@ import threading import time from collections import OrderedDict -# Import salt tests support dirs -from tests.support.paths import ( - ENGINES_DIR, - FILES, - INTEGRATION_TEST_DIR, - LOG_HANDLERS_DIR, - SCRIPT_DIR, - TMP, - ) -from tests.support.runtests import RUNTIME_VARS -from tests.support.parser import PNUM, print_header -from tests.support.processes import start_daemon - -# Import Salt libs -from tests.integration import ( - SocketServerRequestHandler, - TestDaemon, - TestDaemonStartFailed, - ThreadedSocketServer, - get_unused_localhost_port, - ) import salt.config import salt.log.setup as salt_log_setup import salt.utils.path @@ -44,8 +24,29 @@ import salt.utils.platform from salt.utils.immutabletypes import freeze from salt.utils.verify import verify_env +# Import Salt libs +from tests.integration import ( + SocketServerRequestHandler, + TestDaemon, + TestDaemonStartFailed, + ThreadedSocketServer, + get_unused_localhost_port, +) +from tests.support.parser import PNUM, print_header + +# Import salt tests support dirs +from tests.support.paths import ( + ENGINES_DIR, + FILES, + INTEGRATION_TEST_DIR, + LOG_HANDLERS_DIR, + SCRIPT_DIR, + TMP, +) + # Import salt tests support libs -from tests.support.processes import SaltMaster, SaltMinion +from tests.support.processes import SaltMaster, SaltMinion, start_daemon +from tests.support.runtests import RUNTIME_VARS log = logging.getLogger(__name__) @@ -54,72 +55,74 @@ SALT_LOG_PORT = get_unused_localhost_port() class MultimasterTestDaemon(TestDaemon): - ''' + """ Set up the master and minion daemons, and run related cases - ''' + """ def __enter__(self): - ''' + """ Start a master and minion - ''' + """ # Setup the multiprocessing logging queue listener - salt_log_setup.setup_multiprocessing_logging_listener( - self.mm_master_opts - ) + salt_log_setup.setup_multiprocessing_logging_listener(self.mm_master_opts) # Set up PATH to mockbin self._enter_mockbin() self.master_targets = [self.mm_master_opts, self.mm_sub_master_opts] - self.minion_targets = set(['mm-minion', 'mm-sub-minion']) + self.minion_targets = set(["mm-minion", "mm-sub-minion"]) - if self.parser.options.transport == 'zeromq': + if self.parser.options.transport == "zeromq": self.start_zeromq_daemons() - elif self.parser.options.transport == 'raet': + elif self.parser.options.transport == "raet": self.start_raet_daemons() - elif self.parser.options.transport == 'tcp': + elif self.parser.options.transport == "tcp": self.start_tcp_daemons() self.pre_setup_minions() self.setup_minions() - #if getattr(self.parser.options, 'ssh', False): - #self.prep_ssh() + # if getattr(self.parser.options, 'ssh', False): + # self.prep_ssh() self.wait_for_minions(time.time(), self.MINIONS_CONNECT_TIMEOUT) if self.parser.options.sysinfo: try: print_header( - '~~~~~~~ Versions Report ', inline=True, - width=getattr(self.parser.options, 'output_columns', PNUM) + "~~~~~~~ Versions Report ", + inline=True, + width=getattr(self.parser.options, "output_columns", PNUM), ) except TypeError: - print_header('~~~~~~~ Versions Report ', inline=True) + print_header("~~~~~~~ Versions Report ", inline=True) - print('\n'.join(salt.version.versions_report())) + print("\n".join(salt.version.versions_report())) try: print_header( - '~~~~~~~ Minion Grains Information ', inline=True, - width=getattr(self.parser.options, 'output_columns', PNUM) + "~~~~~~~ Minion Grains Information ", + inline=True, + width=getattr(self.parser.options, "output_columns", PNUM), ) except TypeError: - print_header('~~~~~~~ Minion Grains Information ', inline=True) + print_header("~~~~~~~ Minion Grains Information ", inline=True) - grains = self.client.cmd('minion', 'grains.items') + grains = self.client.cmd("minion", "grains.items") minion_opts = self.mm_minion_opts.copy() - minion_opts['color'] = self.parser.options.no_colors is False - salt.output.display_output(grains, 'grains', minion_opts) + minion_opts["color"] = self.parser.options.no_colors is False + salt.output.display_output(grains, "grains", minion_opts) try: print_header( - '=', sep='=', inline=True, - width=getattr(self.parser.options, 'output_columns', PNUM) + "=", + sep="=", + inline=True, + width=getattr(self.parser.options, "output_columns", PNUM), ) except TypeError: - print_header('', sep='=', inline=True) + print_header("", sep="=", inline=True) try: return self @@ -127,38 +130,38 @@ class MultimasterTestDaemon(TestDaemon): self.post_setup_minions() def __exit__(self, type, value, traceback): - ''' + """ Kill the minion and master processes - ''' + """ try: - if hasattr(self.sub_minion_process, 'terminate'): + if hasattr(self.sub_minion_process, "terminate"): self.sub_minion_process.terminate() else: - log.error('self.sub_minion_process can\'t be terminate.') + log.error("self.sub_minion_process can't be terminate.") except AttributeError: pass try: - if hasattr(self.minion_process, 'terminate'): + if hasattr(self.minion_process, "terminate"): self.minion_process.terminate() else: - log.error('self.minion_process can\'t be terminate.') + log.error("self.minion_process can't be terminate.") except AttributeError: pass try: - if hasattr(self.sub_master_process, 'terminate'): + if hasattr(self.sub_master_process, "terminate"): self.sub_master_process.terminate() else: - log.error('self.sub_master_process can\'t be terminate.') + log.error("self.sub_master_process can't be terminate.") except AttributeError: pass try: - if hasattr(self.master_process, 'terminate'): + if hasattr(self.master_process, "terminate"): self.master_process.terminate() else: - log.error('self.master_process can\'t be terminate.') + log.error("self.master_process can't be terminate.") except AttributeError: pass @@ -173,163 +176,193 @@ class MultimasterTestDaemon(TestDaemon): self.log_server_process.join() def start_zeromq_daemons(self): - ''' + """ Fire up the daemons used for zeromq tests - ''' - self.log_server = ThreadedSocketServer(('localhost', SALT_LOG_PORT), SocketServerRequestHandler) + """ + self.log_server = ThreadedSocketServer( + ("localhost", SALT_LOG_PORT), SocketServerRequestHandler + ) self.log_server_process = threading.Thread(target=self.log_server.serve_forever) self.log_server_process.start() try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting salt-master ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting salt-master ... {ENDC}".format(**self.colors) ) sys.stdout.flush() self.master_process = start_daemon( - daemon_name='salt-master', - daemon_id=self.mm_master_opts['id'], - daemon_log_prefix='salt-master/{}'.format(self.mm_master_opts['id']), - daemon_cli_script_name='master', + daemon_name="salt-master", + daemon_id=self.mm_master_opts["id"], + daemon_log_prefix="salt-master/{}".format(self.mm_master_opts["id"]), + daemon_cli_script_name="master", daemon_config=self.mm_master_opts, daemon_config_dir=RUNTIME_VARS.TMP_MM_CONF_DIR, daemon_class=SaltMaster, bin_dir_path=SCRIPT_DIR, fail_hard=True, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting salt-master ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting salt-master ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting salt-master ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting salt-master ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() # Clone the master key to sub-master's pki dir - for keyfile in ('master.pem', 'master.pub'): + for keyfile in ("master.pem", "master.pub"): shutil.copyfile( - os.path.join(self.mm_master_opts['pki_dir'], keyfile), - os.path.join(self.mm_sub_master_opts['pki_dir'], keyfile) + os.path.join(self.mm_master_opts["pki_dir"], keyfile), + os.path.join(self.mm_sub_master_opts["pki_dir"], keyfile), ) try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting second salt-master ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting second salt-master ... {ENDC}".format( + **self.colors + ) ) sys.stdout.flush() self.sub_master_process = start_daemon( - daemon_name='sub salt-master', - daemon_id=self.mm_master_opts['id'], - daemon_log_prefix='sub-salt-master/{}'.format(self.mm_sub_master_opts['id']), - daemon_cli_script_name='master', + daemon_name="sub salt-master", + daemon_id=self.mm_master_opts["id"], + daemon_log_prefix="sub-salt-master/{}".format( + self.mm_sub_master_opts["id"] + ), + daemon_cli_script_name="master", daemon_config=self.mm_sub_master_opts, daemon_config_dir=RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, daemon_class=SaltMaster, bin_dir_path=SCRIPT_DIR, fail_hard=True, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting second salt-master ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting second salt-master ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting second salt-master ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting second salt-master ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting salt-minion ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting salt-minion ... {ENDC}".format(**self.colors) ) sys.stdout.flush() self.minion_process = start_daemon( - daemon_name='salt-minion', - daemon_id=self.mm_master_opts['id'], - daemon_log_prefix='salt-minion/{}'.format(self.mm_minion_opts['id']), - daemon_cli_script_name='minion', + daemon_name="salt-minion", + daemon_id=self.mm_master_opts["id"], + daemon_log_prefix="salt-minion/{}".format(self.mm_minion_opts["id"]), + daemon_cli_script_name="minion", daemon_config=self.mm_minion_opts, daemon_config_dir=RUNTIME_VARS.TMP_MM_CONF_DIR, daemon_class=SaltMinion, bin_dir_path=SCRIPT_DIR, fail_hard=True, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting salt-minion ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting salt-minion ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting salt-minion ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting salt-minion ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() try: sys.stdout.write( - ' * {LIGHT_YELLOW}Starting sub salt-minion ... {ENDC}'.format(**self.colors) + " * {LIGHT_YELLOW}Starting sub salt-minion ... {ENDC}".format( + **self.colors + ) ) sys.stdout.flush() self.sub_minion_process = start_daemon( - daemon_name='sub salt-minion', - daemon_id=self.mm_master_opts['id'], - daemon_log_prefix='sub-salt-minion/{}'.format(self.mm_sub_minion_opts['id']), - daemon_cli_script_name='minion', + daemon_name="sub salt-minion", + daemon_id=self.mm_master_opts["id"], + daemon_log_prefix="sub-salt-minion/{}".format( + self.mm_sub_minion_opts["id"] + ), + daemon_cli_script_name="minion", daemon_config=self.mm_sub_minion_opts, daemon_config_dir=RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, daemon_class=SaltMinion, bin_dir_path=SCRIPT_DIR, fail_hard=True, - start_timeout=120) + start_timeout=120, + ) sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_GREEN}Starting sub salt-minion ... STARTED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_GREEN}Starting sub salt-minion ... STARTED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() except (RuntimeWarning, RuntimeError): sys.stdout.write( - '\r{0}\r'.format( - ' ' * getattr(self.parser.options, 'output_columns', PNUM) + "\r{0}\r".format( + " " * getattr(self.parser.options, "output_columns", PNUM) ) ) sys.stdout.write( - ' * {LIGHT_RED}Starting sub salt-minion ... FAILED!\n{ENDC}'.format(**self.colors) + " * {LIGHT_RED}Starting sub salt-minion ... FAILED!\n{ENDC}".format( + **self.colors + ) ) sys.stdout.flush() raise TestDaemonStartFailed() @@ -337,21 +370,21 @@ class MultimasterTestDaemon(TestDaemon): start_tcp_daemons = start_zeromq_daemons def wait_for_minions(self, start, timeout, sleep=5): - ''' + """ Ensure all minions and masters (including sub-masters) are connected. - ''' + """ success = [False] * len(self.master_targets) while True: for num, client in enumerate(self.clients): if success[num]: continue try: - ret = self.client.run_job('*', 'test.ping') + ret = self.client.run_job("*", "test.ping") except salt.exceptions.SaltClientError: ret = None - if ret and 'minions' not in ret: + if ret and "minions" not in ret: continue - if ret and sorted(ret['minions']) == sorted(self.minion_targets): + if ret and sorted(ret["minions"]) == sorted(self.minion_targets): success[num] = True continue if all(success): @@ -362,64 +395,79 @@ class MultimasterTestDaemon(TestDaemon): @property def clients(self): - ''' + """ Return a local client which will be used for example to ping and sync the test minions. This client is defined as a class attribute because its creation needs to be deferred to a latter stage. If created it on `__enter__` like it previously was, it would not receive the master events. - ''' - if 'runtime_clients' not in RUNTIME_VARS.RUNTIME_CONFIGS: - RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] = OrderedDict() + """ + if "runtime_clients" not in RUNTIME_VARS.RUNTIME_CONFIGS: + RUNTIME_VARS.RUNTIME_CONFIGS["runtime_clients"] = OrderedDict() - runtime_clients = RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] + runtime_clients = RUNTIME_VARS.RUNTIME_CONFIGS["runtime_clients"] for mopts in self.master_targets: - if mopts['id'] in runtime_clients: + if mopts["id"] in runtime_clients: continue - runtime_clients[mopts['id']] = salt.client.get_local_client(mopts=mopts) + runtime_clients[mopts["id"]] = salt.client.get_local_client(mopts=mopts) return runtime_clients @property def client(self): - return self.clients['mm-master'] + return self.clients["mm-master"] @classmethod - def transplant_configs(cls, transport='zeromq'): + def transplant_configs(cls, transport="zeromq"): os.makedirs(RUNTIME_VARS.TMP_MM_CONF_DIR) os.makedirs(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR) - print(' * Transplanting multimaster configuration files to \'{0}\''.format( - RUNTIME_VARS.TMP_CONF_DIR)) - tests_known_hosts_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'salt_ssh_known_hosts') + print( + " * Transplanting multimaster configuration files to '{0}'".format( + RUNTIME_VARS.TMP_CONF_DIR + ) + ) + tests_known_hosts_file = os.path.join( + RUNTIME_VARS.TMP_CONF_DIR, "salt_ssh_known_hosts" + ) # Primary master in multimaster environment - master_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'master')) - master_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, - 'mm_master'))) - master_opts['known_hosts_file'] = tests_known_hosts_file - master_opts['cachedir'] = 'cache' - master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - master_opts['config_dir'] = RUNTIME_VARS.TMP_MM_CONF_DIR - master_opts['root_dir'] = os.path.join(TMP, 'rootdir-multimaster') - master_opts['pki_dir'] = 'pki' + master_opts = salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "master") + ) + master_opts.update( + salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "mm_master") + ) + ) + master_opts["known_hosts_file"] = tests_known_hosts_file + master_opts["cachedir"] = "cache" + master_opts["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + master_opts["config_dir"] = RUNTIME_VARS.TMP_MM_CONF_DIR + master_opts["root_dir"] = os.path.join(TMP, "rootdir-multimaster") + master_opts["pki_dir"] = "pki" file_tree = { - 'root_dir': os.path.join(FILES, 'pillar', 'base', 'file_tree'), - 'follow_dir_links': False, - 'keep_newline': True, + "root_dir": os.path.join(FILES, "pillar", "base", "file_tree"), + "follow_dir_links": False, + "keep_newline": True, } - master_opts['ext_pillar'].append({'file_tree': file_tree}) + master_opts["ext_pillar"].append({"file_tree": file_tree}) # Secondary master in multimaster environment - sub_master_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'master')) - sub_master_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, - 'mm_sub_master'))) - sub_master_opts['known_hosts_file'] = tests_known_hosts_file - sub_master_opts['cachedir'] = 'cache' - sub_master_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - sub_master_opts['config_dir'] = RUNTIME_VARS.TMP_MM_SUB_CONF_DIR - sub_master_opts['root_dir'] = os.path.join(TMP, 'rootdir-sub-multimaster') - sub_master_opts['pki_dir'] = 'pki' - sub_master_opts['ext_pillar'].append({'file_tree': copy.deepcopy(file_tree)}) + sub_master_opts = salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "master") + ) + sub_master_opts.update( + salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "mm_sub_master") + ) + ) + sub_master_opts["known_hosts_file"] = tests_known_hosts_file + sub_master_opts["cachedir"] = "cache" + sub_master_opts["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + sub_master_opts["config_dir"] = RUNTIME_VARS.TMP_MM_SUB_CONF_DIR + sub_master_opts["root_dir"] = os.path.join(TMP, "rootdir-sub-multimaster") + sub_master_opts["pki_dir"] = "pki" + sub_master_opts["ext_pillar"].append({"file_tree": copy.deepcopy(file_tree)}) # Under windows we can't seem to properly create a virtualenv off of another # virtualenv, we can on linux but we will still point to the virtualenv binary @@ -428,21 +476,23 @@ class MultimasterTestDaemon(TestDaemon): real_prefix = sys.real_prefix # The above attribute exists, this is a virtualenv if salt.utils.platform.is_windows(): - virtualenv_binary = os.path.join(real_prefix, 'Scripts', 'virtualenv.exe') + virtualenv_binary = os.path.join( + real_prefix, "Scripts", "virtualenv.exe" + ) else: # We need to remove the virtualenv from PATH or we'll get the virtualenv binary # from within the virtualenv, we don't want that - path = os.environ.get('PATH') + path = os.environ.get("PATH") if path is not None: path_items = path.split(os.pathsep) for item in path_items[:]: if item.startswith(sys.base_prefix): path_items.remove(item) - os.environ['PATH'] = os.pathsep.join(path_items) - virtualenv_binary = salt.utils.path.which('virtualenv') + os.environ["PATH"] = os.pathsep.join(path_items) + virtualenv_binary = salt.utils.path.which("virtualenv") if path is not None: # Restore previous environ PATH - os.environ['PATH'] = path + os.environ["PATH"] = path if not virtualenv_binary.startswith(real_prefix): virtualenv_binary = None if virtualenv_binary and not os.path.exists(virtualenv_binary): @@ -453,225 +503,266 @@ class MultimasterTestDaemon(TestDaemon): virtualenv_binary = None # This minion connects to both masters - minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'minion')) - minion_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, - 'mm_minion'))) - minion_opts['cachedir'] = 'cache' - minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - minion_opts['config_dir'] = RUNTIME_VARS.TMP_MM_CONF_DIR - minion_opts['root_dir'] = os.path.join(TMP, 'rootdir-multimaster') - minion_opts['pki_dir'] = 'pki' - minion_opts['hosts.file'] = os.path.join(TMP, 'rootdir', 'hosts') - minion_opts['aliases.file'] = os.path.join(TMP, 'rootdir', 'aliases') + minion_opts = salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "minion") + ) + minion_opts.update( + salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "mm_minion") + ) + ) + minion_opts["cachedir"] = "cache" + minion_opts["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + minion_opts["config_dir"] = RUNTIME_VARS.TMP_MM_CONF_DIR + minion_opts["root_dir"] = os.path.join(TMP, "rootdir-multimaster") + minion_opts["pki_dir"] = "pki" + minion_opts["hosts.file"] = os.path.join(TMP, "rootdir", "hosts") + minion_opts["aliases.file"] = os.path.join(TMP, "rootdir", "aliases") if virtualenv_binary: - minion_opts['venv_bin'] = virtualenv_binary + minion_opts["venv_bin"] = virtualenv_binary # This sub_minion also connects to both masters - sub_minion_opts = salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, 'sub_minion')) - sub_minion_opts.update(salt.config._read_conf_file(os.path.join(RUNTIME_VARS.CONF_DIR, - 'mm_sub_minion'))) - sub_minion_opts['cachedir'] = 'cache' - sub_minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - sub_minion_opts['config_dir'] = RUNTIME_VARS.TMP_MM_SUB_CONF_DIR - sub_minion_opts['root_dir'] = os.path.join(TMP, 'rootdir-sub-multimaster') - sub_minion_opts['pki_dir'] = 'pki' - sub_minion_opts['hosts.file'] = os.path.join(TMP, 'rootdir', 'hosts') - sub_minion_opts['aliases.file'] = os.path.join(TMP, 'rootdir', 'aliases') + sub_minion_opts = salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "sub_minion") + ) + sub_minion_opts.update( + salt.config._read_conf_file( + os.path.join(RUNTIME_VARS.CONF_DIR, "mm_sub_minion") + ) + ) + sub_minion_opts["cachedir"] = "cache" + sub_minion_opts["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + sub_minion_opts["config_dir"] = RUNTIME_VARS.TMP_MM_SUB_CONF_DIR + sub_minion_opts["root_dir"] = os.path.join(TMP, "rootdir-sub-multimaster") + sub_minion_opts["pki_dir"] = "pki" + sub_minion_opts["hosts.file"] = os.path.join(TMP, "rootdir", "hosts") + sub_minion_opts["aliases.file"] = os.path.join(TMP, "rootdir", "aliases") if virtualenv_binary: - sub_minion_opts['venv_bin'] = virtualenv_binary + sub_minion_opts["venv_bin"] = virtualenv_binary - if transport == 'raet': - master_opts['transport'] = 'raet' - master_opts['raet_port'] = 64506 - sub_master_opts['transport'] = 'raet' - sub_master_opts['raet_port'] = 64556 - minion_opts['transport'] = 'raet' - minion_opts['raet_port'] = 64510 - sub_minion_opts['transport'] = 'raet' - sub_minion_opts['raet_port'] = 64520 + if transport == "raet": + master_opts["transport"] = "raet" + master_opts["raet_port"] = 64506 + sub_master_opts["transport"] = "raet" + sub_master_opts["raet_port"] = 64556 + minion_opts["transport"] = "raet" + minion_opts["raet_port"] = 64510 + sub_minion_opts["transport"] = "raet" + sub_minion_opts["raet_port"] = 64520 # syndic_master_opts['transport'] = 'raet' - if transport == 'tcp': - master_opts['transport'] = 'tcp' - sub_master_opts['transport'] = 'tcp' - minion_opts['transport'] = 'tcp' - sub_minion_opts['transport'] = 'tcp' + if transport == "tcp": + master_opts["transport"] = "tcp" + sub_master_opts["transport"] = "tcp" + minion_opts["transport"] = "tcp" + sub_minion_opts["transport"] = "tcp" # Set up config options that require internal data - master_opts['pillar_roots'] = sub_master_opts['pillar_roots'] = { - 'base': [ + master_opts["pillar_roots"] = sub_master_opts["pillar_roots"] = { + "base": [ RUNTIME_VARS.TMP_PILLAR_TREE, - os.path.join(FILES, 'pillar', 'base'), + os.path.join(FILES, "pillar", "base"), ] } - minion_opts['pillar_roots'] = { - 'base': [ + minion_opts["pillar_roots"] = { + "base": [ RUNTIME_VARS.TMP_PILLAR_TREE, - os.path.join(FILES, 'pillar', 'base'), + os.path.join(FILES, "pillar", "base"), ] } - master_opts['file_roots'] = sub_master_opts['file_roots'] = { - 'base': [ - os.path.join(FILES, 'file', 'base'), + master_opts["file_roots"] = sub_master_opts["file_roots"] = { + "base": [ + os.path.join(FILES, "file", "base"), # Let's support runtime created files that can be used like: # salt://my-temp-file.txt - RUNTIME_VARS.TMP_STATE_TREE + RUNTIME_VARS.TMP_STATE_TREE, ], # Alternate root to test __env__ choices - 'prod': [ - os.path.join(FILES, 'file', 'prod'), - RUNTIME_VARS.TMP_PRODENV_STATE_TREE - ] + "prod": [ + os.path.join(FILES, "file", "prod"), + RUNTIME_VARS.TMP_PRODENV_STATE_TREE, + ], } - minion_opts['file_roots'] = { - 'base': [ - os.path.join(FILES, 'file', 'base'), + minion_opts["file_roots"] = { + "base": [ + os.path.join(FILES, "file", "base"), # Let's support runtime created files that can be used like: # salt://my-temp-file.txt - RUNTIME_VARS.TMP_STATE_TREE + RUNTIME_VARS.TMP_STATE_TREE, ], # Alternate root to test __env__ choices - 'prod': [ - os.path.join(FILES, 'file', 'prod'), - RUNTIME_VARS.TMP_PRODENV_STATE_TREE - ] + "prod": [ + os.path.join(FILES, "file", "prod"), + RUNTIME_VARS.TMP_PRODENV_STATE_TREE, + ], } - master_opts.setdefault('reactor', []).append( - { - 'salt/minion/*/start': [ - os.path.join(FILES, 'reactor-sync-minion.sls') - ], - } + master_opts.setdefault("reactor", []).append( + {"salt/minion/*/start": [os.path.join(FILES, "reactor-sync-minion.sls")]} ) for opts_dict in (master_opts, sub_master_opts): - if 'ext_pillar' not in opts_dict: - opts_dict['ext_pillar'] = [] + if "ext_pillar" not in opts_dict: + opts_dict["ext_pillar"] = [] if salt.utils.platform.is_windows(): - opts_dict['ext_pillar'].append( - {'cmd_yaml': 'type {0}'.format(os.path.join(FILES, 'ext.yaml'))}) + opts_dict["ext_pillar"].append( + {"cmd_yaml": "type {0}".format(os.path.join(FILES, "ext.yaml"))} + ) else: - opts_dict['ext_pillar'].append( - {'cmd_yaml': 'cat {0}'.format(os.path.join(FILES, 'ext.yaml'))}) + opts_dict["ext_pillar"].append( + {"cmd_yaml": "cat {0}".format(os.path.join(FILES, "ext.yaml"))} + ) # all read, only owner write - autosign_file_permissions = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + autosign_file_permissions = ( + stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | stat.S_IWUSR + ) for opts_dict in (master_opts, sub_master_opts): # We need to copy the extension modules into the new master root_dir or # it will be prefixed by it - new_extension_modules_path = os.path.join(opts_dict['root_dir'], 'extension_modules') + new_extension_modules_path = os.path.join( + opts_dict["root_dir"], "extension_modules" + ) if not os.path.exists(new_extension_modules_path): shutil.copytree( - os.path.join( - INTEGRATION_TEST_DIR, 'files', 'extension_modules' - ), - new_extension_modules_path + os.path.join(INTEGRATION_TEST_DIR, "files", "extension_modules"), + new_extension_modules_path, ) - opts_dict['extension_modules'] = os.path.join(opts_dict['root_dir'], 'extension_modules') + opts_dict["extension_modules"] = os.path.join( + opts_dict["root_dir"], "extension_modules" + ) # Copy the autosign_file to the new master root_dir - new_autosign_file_path = os.path.join(opts_dict['root_dir'], 'autosign_file') + new_autosign_file_path = os.path.join( + opts_dict["root_dir"], "autosign_file" + ) shutil.copyfile( - os.path.join(INTEGRATION_TEST_DIR, 'files', 'autosign_file'), - new_autosign_file_path + os.path.join(INTEGRATION_TEST_DIR, "files", "autosign_file"), + new_autosign_file_path, ) os.chmod(new_autosign_file_path, autosign_file_permissions) # Point the config values to the correct temporary paths - for name in ('hosts', 'aliases'): - optname = '{0}.file'.format(name) + for name in ("hosts", "aliases"): + optname = "{0}.file".format(name) optname_path = os.path.join(TMP, name) master_opts[optname] = optname_path sub_master_opts[optname] = optname_path minion_opts[optname] = optname_path sub_minion_opts[optname] = optname_path - master_opts['runtests_conn_check_port'] = get_unused_localhost_port() - sub_master_opts['runtests_conn_check_port'] = get_unused_localhost_port() - minion_opts['runtests_conn_check_port'] = get_unused_localhost_port() - sub_minion_opts['runtests_conn_check_port'] = get_unused_localhost_port() + master_opts["runtests_conn_check_port"] = get_unused_localhost_port() + sub_master_opts["runtests_conn_check_port"] = get_unused_localhost_port() + minion_opts["runtests_conn_check_port"] = get_unused_localhost_port() + sub_minion_opts["runtests_conn_check_port"] = get_unused_localhost_port() for conf in (master_opts, sub_master_opts, minion_opts, sub_minion_opts): - if 'engines' not in conf: - conf['engines'] = [] - conf['engines'].append({'salt_runtests': {}}) + if "engines" not in conf: + conf["engines"] = [] + conf["engines"].append({"salt_runtests": {}}) - if 'engines_dirs' not in conf: - conf['engines_dirs'] = [] + if "engines_dirs" not in conf: + conf["engines_dirs"] = [] - conf['engines_dirs'].insert(0, ENGINES_DIR) + conf["engines_dirs"].insert(0, ENGINES_DIR) - if 'log_handlers_dirs' not in conf: - conf['log_handlers_dirs'] = [] - conf['log_handlers_dirs'].insert(0, LOG_HANDLERS_DIR) - conf['runtests_log_port'] = SALT_LOG_PORT - conf['runtests_log_level'] = os.environ.get('TESTS_MIN_LOG_LEVEL_NAME') or 'debug' + if "log_handlers_dirs" not in conf: + conf["log_handlers_dirs"] = [] + conf["log_handlers_dirs"].insert(0, LOG_HANDLERS_DIR) + conf["runtests_log_port"] = SALT_LOG_PORT + conf["runtests_log_level"] = ( + os.environ.get("TESTS_MIN_LOG_LEVEL_NAME") or "debug" + ) # ----- Transcribe Configuration ----------------------------------------------------------------------------> computed_config = copy.deepcopy(master_opts) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, 'master'), 'w') as wfh: - salt.utils.yaml.safe_dump(copy.deepcopy(master_opts), wfh, default_flow_style=False) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, 'master'), 'w') as wfh: - salt.utils.yaml.safe_dump(copy.deepcopy(sub_master_opts), wfh, default_flow_style=False) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, 'minion'), 'w') as wfh: - salt.utils.yaml.safe_dump(copy.deepcopy(minion_opts), wfh, default_flow_style=False) - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, 'minion'), 'w') as wfh: - salt.utils.yaml.safe_dump(copy.deepcopy(sub_minion_opts), wfh, default_flow_style=False) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, "master"), "w" + ) as wfh: + salt.utils.yaml.safe_dump( + copy.deepcopy(master_opts), wfh, default_flow_style=False + ) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, "master"), "w" + ) as wfh: + salt.utils.yaml.safe_dump( + copy.deepcopy(sub_master_opts), wfh, default_flow_style=False + ) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, "minion"), "w" + ) as wfh: + salt.utils.yaml.safe_dump( + copy.deepcopy(minion_opts), wfh, default_flow_style=False + ) + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, "minion"), "w" + ) as wfh: + salt.utils.yaml.safe_dump( + copy.deepcopy(sub_minion_opts), wfh, default_flow_style=False + ) # <---- Transcribe Configuration ----------------------------------------------------------------------------- # ----- Verify Environment ----------------------------------------------------------------------------------> - master_opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, 'master')) - sub_master_opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, 'master')) - minion_opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, 'minion')) - sub_minion_opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, 'minion')) + master_opts = salt.config.master_config( + os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, "master") + ) + sub_master_opts = salt.config.master_config( + os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, "master") + ) + minion_opts = salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, "minion") + ) + sub_minion_opts = salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, "minion") + ) - RUNTIME_VARS.RUNTIME_CONFIGS['mm_master'] = freeze(master_opts) - RUNTIME_VARS.RUNTIME_CONFIGS['mm_sub_master'] = freeze(sub_master_opts) - RUNTIME_VARS.RUNTIME_CONFIGS['mm_minion'] = freeze(minion_opts) - RUNTIME_VARS.RUNTIME_CONFIGS['mm_sub_minion'] = freeze(sub_minion_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["mm_master"] = freeze(master_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["mm_sub_master"] = freeze(sub_master_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["mm_minion"] = freeze(minion_opts) + RUNTIME_VARS.RUNTIME_CONFIGS["mm_sub_minion"] = freeze(sub_minion_opts) - verify_env([os.path.join(master_opts['pki_dir'], 'minions'), - os.path.join(master_opts['pki_dir'], 'minions_pre'), - os.path.join(master_opts['pki_dir'], 'minions_rejected'), - os.path.join(master_opts['pki_dir'], 'minions_denied'), - os.path.join(master_opts['cachedir'], 'jobs'), - os.path.join(master_opts['cachedir'], 'raet'), - os.path.join(master_opts['root_dir'], 'cache', 'tokens'), - os.path.join(master_opts['pki_dir'], 'accepted'), - os.path.join(master_opts['pki_dir'], 'rejected'), - os.path.join(master_opts['pki_dir'], 'pending'), - os.path.join(master_opts['cachedir'], 'raet'), - os.path.join(sub_master_opts['pki_dir'], 'minions'), - os.path.join(sub_master_opts['pki_dir'], 'minions_pre'), - os.path.join(sub_master_opts['pki_dir'], 'minions_rejected'), - os.path.join(sub_master_opts['pki_dir'], 'minions_denied'), - os.path.join(sub_master_opts['cachedir'], 'jobs'), - os.path.join(sub_master_opts['cachedir'], 'raet'), - os.path.join(sub_master_opts['root_dir'], 'cache', 'tokens'), - os.path.join(sub_master_opts['pki_dir'], 'accepted'), - os.path.join(sub_master_opts['pki_dir'], 'rejected'), - os.path.join(sub_master_opts['pki_dir'], 'pending'), - os.path.join(sub_master_opts['cachedir'], 'raet'), - - os.path.join(minion_opts['pki_dir'], 'accepted'), - os.path.join(minion_opts['pki_dir'], 'rejected'), - os.path.join(minion_opts['pki_dir'], 'pending'), - os.path.join(minion_opts['cachedir'], 'raet'), - os.path.join(sub_minion_opts['pki_dir'], 'accepted'), - os.path.join(sub_minion_opts['pki_dir'], 'rejected'), - os.path.join(sub_minion_opts['pki_dir'], 'pending'), - os.path.join(sub_minion_opts['cachedir'], 'raet'), - os.path.dirname(master_opts['log_file']), - minion_opts['extension_modules'], - sub_minion_opts['extension_modules'], - sub_minion_opts['pki_dir'], - master_opts['sock_dir'], - sub_master_opts['sock_dir'], - sub_minion_opts['sock_dir'], - minion_opts['sock_dir'], - ], - RUNTIME_VARS.RUNNING_TESTS_USER, - root_dir=master_opts['root_dir'], - ) + verify_env( + [ + os.path.join(master_opts["pki_dir"], "minions"), + os.path.join(master_opts["pki_dir"], "minions_pre"), + os.path.join(master_opts["pki_dir"], "minions_rejected"), + os.path.join(master_opts["pki_dir"], "minions_denied"), + os.path.join(master_opts["cachedir"], "jobs"), + os.path.join(master_opts["cachedir"], "raet"), + os.path.join(master_opts["root_dir"], "cache", "tokens"), + os.path.join(master_opts["pki_dir"], "accepted"), + os.path.join(master_opts["pki_dir"], "rejected"), + os.path.join(master_opts["pki_dir"], "pending"), + os.path.join(master_opts["cachedir"], "raet"), + os.path.join(sub_master_opts["pki_dir"], "minions"), + os.path.join(sub_master_opts["pki_dir"], "minions_pre"), + os.path.join(sub_master_opts["pki_dir"], "minions_rejected"), + os.path.join(sub_master_opts["pki_dir"], "minions_denied"), + os.path.join(sub_master_opts["cachedir"], "jobs"), + os.path.join(sub_master_opts["cachedir"], "raet"), + os.path.join(sub_master_opts["root_dir"], "cache", "tokens"), + os.path.join(sub_master_opts["pki_dir"], "accepted"), + os.path.join(sub_master_opts["pki_dir"], "rejected"), + os.path.join(sub_master_opts["pki_dir"], "pending"), + os.path.join(sub_master_opts["cachedir"], "raet"), + os.path.join(minion_opts["pki_dir"], "accepted"), + os.path.join(minion_opts["pki_dir"], "rejected"), + os.path.join(minion_opts["pki_dir"], "pending"), + os.path.join(minion_opts["cachedir"], "raet"), + os.path.join(sub_minion_opts["pki_dir"], "accepted"), + os.path.join(sub_minion_opts["pki_dir"], "rejected"), + os.path.join(sub_minion_opts["pki_dir"], "pending"), + os.path.join(sub_minion_opts["cachedir"], "raet"), + os.path.dirname(master_opts["log_file"]), + minion_opts["extension_modules"], + sub_minion_opts["extension_modules"], + sub_minion_opts["pki_dir"], + master_opts["sock_dir"], + sub_master_opts["sock_dir"], + sub_minion_opts["sock_dir"], + minion_opts["sock_dir"], + ], + RUNTIME_VARS.RUNNING_TESTS_USER, + root_dir=master_opts["root_dir"], + ) cls.mm_master_opts = master_opts cls.mm_sub_master_opts = sub_master_opts diff --git a/tests/multimaster/beacons/test_inotify.py b/tests/multimaster/beacons/test_inotify.py index bf734d7a0d0..f45380c8c69 100644 --- a/tests/multimaster/beacons/test_inotify.py +++ b/tests/multimaster/beacons/test_inotify.py @@ -3,34 +3,39 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import logging import os import shutil import tempfile import time -try: - import pyinotify # pylint: disable=unused-import - HAS_PYINOTIFY = True -except ImportError: - HAS_PYINOTIFY = False + +import salt.config + +# Import salt libs +import salt.version # Import Salt Testing libs from tests.support.case import MultimasterModuleCase from tests.support.mixins import AdaptedConfigurationTestCaseMixin from tests.support.unit import skipIf -# Import salt libs -import salt.version -import salt.config +try: + import pyinotify # pylint: disable=unused-import + + HAS_PYINOTIFY = True +except ImportError: + HAS_PYINOTIFY = False + -import logging log = logging.getLogger(__name__) -@skipIf(not HAS_PYINOTIFY, 'pyinotify is not available') +@skipIf(not HAS_PYINOTIFY, "pyinotify is not available") class TestBeaconsInotify(MultimasterModuleCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Validate the inotify beacon in multimaster environment - ''' + """ + def setUp(self): self.tmpdir = salt.utils.stringutils.to_unicode(tempfile.mkdtemp()) self.addCleanup(shutil.rmtree, self.tmpdir, ignore_errors=True) @@ -38,58 +43,52 @@ class TestBeaconsInotify(MultimasterModuleCase, AdaptedConfigurationTestCaseMixi def test_beacons_duplicate_53344(self): # Also add a status beacon to use it for interval checks res = self.run_function( - 'beacons.add', - ('inotify', [{'files': {self.tmpdir: {'mask': ['create']}}}]), - master_tgt='mm-master', + "beacons.add", + ("inotify", [{"files": {self.tmpdir: {"mask": ["create"]}}}]), + master_tgt="mm-master", + ) + log.debug("Inotify beacon add returned: %s", res) + self.assertTrue(res.get("result")) + self.addCleanup( + self.run_function, "beacons.delete", ("inotify",), master_tgt="mm-master" ) - log.debug('Inotify beacon add returned: %s', res) - self.assertTrue(res.get('result')) - self.addCleanup(self.run_function, 'beacons.delete', ('inotify',), master_tgt='mm-master') res = self.run_function( - 'beacons.add', - ('status', [{'time': ['all']}]), - master_tgt='mm-master', + "beacons.add", ("status", [{"time": ["all"]}]), master_tgt="mm-master", + ) + log.debug("Status beacon add returned: %s", res) + self.assertTrue(res.get("result")) + self.addCleanup( + self.run_function, "beacons.delete", ("status",), master_tgt="mm-master" ) - log.debug('Status beacon add returned: %s', res) - self.assertTrue(res.get('result')) - self.addCleanup(self.run_function, 'beacons.delete', ('status',), master_tgt='mm-master') # Ensure beacons are added. res = self.run_function( - 'beacons.list', - (), - return_yaml=False, - master_tgt='mm-master', + "beacons.list", (), return_yaml=False, master_tgt="mm-master", + ) + log.debug("Beacons list: %s", res) + self.assertEqual( + { + "inotify": [{"files": {self.tmpdir: {"mask": ["create"]}}}], + "status": [{"time": ["all"]}], + }, + res, ) - log.debug('Beacons list: %s', res) - self.assertEqual({ - 'inotify': [{ - 'files': { - self.tmpdir: { - 'mask': ['create'] - } - } - }], - 'status': [{ - 'time': ['all'] - }] - }, res) - file_path = os.path.join(self.tmpdir, 'tmpfile') + file_path = os.path.join(self.tmpdir, "tmpfile") master_listener = salt.utils.event.get_master_event( - self.mm_master_opts, - sock_dir=self.mm_master_opts['sock_dir']) + self.mm_master_opts, sock_dir=self.mm_master_opts["sock_dir"] + ) self.addCleanup(master_listener.destroy) sub_master_listener = salt.utils.event.get_master_event( - self.mm_sub_master_opts, - sock_dir=self.mm_sub_master_opts['sock_dir']) + self.mm_sub_master_opts, sock_dir=self.mm_sub_master_opts["sock_dir"] + ) self.addCleanup(sub_master_listener.destroy) # We have to wait beacon first execution that would configure the inotify watch. # Since beacons will be executed both together waiting for the first status beacon event # which will mean the inotify beacon was executed too. start = time.time() - stop_at = start + self.mm_minion_opts['loop_interval'] * 2 + 60 + stop_at = start + self.mm_minion_opts["loop_interval"] * 2 + 60 event = sub_event = None while True: if time.time() > stop_at: @@ -98,71 +97,62 @@ class TestBeaconsInotify(MultimasterModuleCase, AdaptedConfigurationTestCaseMixi event = master_listener.get_event( full=True, wait=1, - tag='salt/beacon/mm-minion/status', - match_type='startswith' + tag="salt/beacon/mm-minion/status", + match_type="startswith", ) if sub_event is None: sub_event = sub_master_listener.get_event( full=True, wait=1, - tag='salt/beacon/mm-minion/status', - match_type='startswith' + tag="salt/beacon/mm-minion/status", + match_type="startswith", ) if event and sub_event: break - log.debug('Status events received: %s, %s', event, sub_event) + log.debug("Status events received: %s, %s", event, sub_event) if not event or not sub_event: - self.fail('Failed to receive at least one of the status events') + self.fail("Failed to receive at least one of the status events") - with salt.utils.files.fopen(file_path, 'w') as f: + with salt.utils.files.fopen(file_path, "w") as f: pass start = time.time() # Now in successful case this test will get results at most in 2 loop intervals. # Waiting for 2 loops intervals + some seconds to the hardware stupidity. - stop_at = start + self.mm_minion_opts['loop_interval'] * 3 + 60 + stop_at = start + self.mm_minion_opts["loop_interval"] * 3 + 60 event = sub_event = None expected_tag = salt.utils.stringutils.to_str( - 'salt/beacon/mm-minion/inotify/{}'.format(self.tmpdir)) + "salt/beacon/mm-minion/inotify/{}".format(self.tmpdir) + ) while True: if time.time() > stop_at: break if not event: event = master_listener.get_event( - full=True, - wait=1, - tag=expected_tag, - match_type='startswith' + full=True, wait=1, tag=expected_tag, match_type="startswith" ) if sub_event is None: sub_event = sub_master_listener.get_event( - full=True, - wait=1, - tag=expected_tag, - match_type='startswith' + full=True, wait=1, tag=expected_tag, match_type="startswith" ) if event and sub_event: break - log.debug('Inotify events received: %s, %s', event, sub_event) + log.debug("Inotify events received: %s, %s", event, sub_event) if not event or not sub_event: - self.fail('Failed to receive at least one of the inotify events') + self.fail("Failed to receive at least one of the inotify events") # We can't determine the timestamp so remove it from results if event: - del event['data']['_stamp'] + del event["data"]["_stamp"] if sub_event: - del sub_event['data']['_stamp'] + del sub_event["data"]["_stamp"] expected = { - 'data': { - 'path': file_path, - 'change': 'IN_CREATE', - 'id': 'mm-minion', - }, - 'tag': expected_tag - } + "data": {"path": file_path, "change": "IN_CREATE", "id": "mm-minion"}, + "tag": expected_tag, + } # It's better to compare both at once to see both responses in the error log. self.assertEqual((expected, expected), (event, sub_event)) diff --git a/tests/multimaster/conftest.py b/tests/multimaster/conftest.py index 039529cb56c..0cb9a371516 100644 --- a/tests/multimaster/conftest.py +++ b/tests/multimaster/conftest.py @@ -1,130 +1,132 @@ # -*- coding: utf-8 -*- -''' +""" tests.multimaster.conftest ~~~~~~~~~~~~~~~~~~~~~~~~~~ Multimaster PyTest prep routines -''' +""" from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import shutil -import logging from collections import OrderedDict -import pytest import psutil - +import pytest import salt.utils.files +from pytestsalt.fixtures.config import apply_master_config, apply_minion_config +from pytestsalt.fixtures.daemons import SaltMaster, SaltMinion, start_daemon +from pytestsalt.fixtures.ports import get_unused_localhost_port from salt.serializers import yaml from salt.utils.immutabletypes import freeze from tests.support.runtests import RUNTIME_VARS -from pytestsalt.fixtures.ports import get_unused_localhost_port -from pytestsalt.fixtures.config import apply_master_config, apply_minion_config -from pytestsalt.fixtures.daemons import SaltMaster, SaltMinion, start_daemon log = logging.getLogger(__name__) -SESSION_ROOT_DIR = 'session-mm-root' -SESSION_SECONDARY_ROOT_DIR = 'session-secondary-mm-root' +SESSION_ROOT_DIR = "session-mm-root" +SESSION_SECONDARY_ROOT_DIR = "session-secondary-mm-root" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_root_dir(tempdir): - ''' + """ Return the session scoped salt root dir - ''' + """ return tempdir.mkdir(SESSION_ROOT_DIR) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_conf_dir(session_mm_root_dir): - ''' + """ Return the session scoped salt root dir - ''' - return session_mm_root_dir.join('conf').ensure(dir=True) + """ + return session_mm_root_dir.join("conf").ensure(dir=True) # ----- Master Fixtures ---------------------------------------------------------------------------------------------> -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_id(): - ''' + """ Returns the session scoped master id - ''' - return 'mm-master' + """ + return "mm-master" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_publish_port(): - ''' + """ Returns an unused localhost port for the master publish interface - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_return_port(): - ''' + """ Returns an unused localhost port for the master return interface - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_engine_port(): - ''' + """ Returns an unused localhost port for the pytest session salt master engine - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_tcp_master_pub_port(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_tcp_master_pull_port(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_tcp_master_publish_pull(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_tcp_master_workers(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_log_prefix(session_mm_master_id): - return 'salt-master/{}'.format(session_mm_master_id) + return "salt-master/{}".format(session_mm_master_id) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_config_file(session_mm_conf_dir): - ''' + """ Returns the path to the salt master configuration file - ''' - return session_mm_conf_dir.join('master').realpath().strpath + """ + return session_mm_conf_dir.join("master").realpath().strpath -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_master_default_options(session_master_default_options): - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_master')) as rfh: + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.CONF_DIR, "mm_master") + ) as rfh: config_file_opts = yaml.deserialize(rfh.read()) opts = session_master_default_options.copy() if config_file_opts: @@ -132,603 +134,685 @@ def session_mm_master_default_options(session_master_default_options): return opts -@pytest.fixture(scope='session') -def session_mm_master_config_overrides(session_master_config_overrides, - session_mm_root_dir): +@pytest.fixture(scope="session") +def session_mm_master_config_overrides( + session_master_config_overrides, session_mm_root_dir +): overrides = session_master_config_overrides.copy() - pytest_stop_sending_events_file = session_mm_root_dir.join('pytest_mm_stop_sending_events_file').strpath - with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: - wfh.write('') - overrides['pytest_stop_sending_events_file'] = pytest_stop_sending_events_file + pytest_stop_sending_events_file = session_mm_root_dir.join( + "pytest_mm_stop_sending_events_file" + ).strpath + with salt.utils.files.fopen(pytest_stop_sending_events_file, "w") as wfh: + wfh.write("") + overrides["pytest_stop_sending_events_file"] = pytest_stop_sending_events_file return overrides -@pytest.fixture(scope='session') -def session_mm_master_config(session_mm_root_dir, - session_mm_master_default_options, - session_mm_master_config_file, - session_mm_master_publish_port, - session_mm_master_return_port, - session_mm_master_engine_port, - session_mm_master_config_overrides, - session_mm_master_id, - session_base_env_state_tree_root_dir, - session_prod_env_state_tree_root_dir, - session_base_env_pillar_tree_root_dir, - session_prod_env_pillar_tree_root_dir, - running_username, - log_server_port, - log_server_level, - engines_dir, - log_handlers_dir, - session_mm_master_log_prefix, - session_mm_master_tcp_master_pub_port, - session_mm_master_tcp_master_pull_port, - session_mm_master_tcp_master_publish_pull, - session_mm_master_tcp_master_workers): - ''' +@pytest.fixture(scope="session") +def session_mm_master_config( + session_mm_root_dir, + session_mm_master_default_options, + session_mm_master_config_file, + session_mm_master_publish_port, + session_mm_master_return_port, + session_mm_master_engine_port, + session_mm_master_config_overrides, + session_mm_master_id, + session_base_env_state_tree_root_dir, + session_prod_env_state_tree_root_dir, + session_base_env_pillar_tree_root_dir, + session_prod_env_pillar_tree_root_dir, + running_username, + log_server_port, + log_server_level, + engines_dir, + log_handlers_dir, + session_mm_master_log_prefix, + session_mm_master_tcp_master_pub_port, + session_mm_master_tcp_master_pull_port, + session_mm_master_tcp_master_publish_pull, + session_mm_master_tcp_master_workers, +): + """ This fixture will return the salt master configuration options after being overridden with any options passed from ``session_master_config_overrides`` - ''' - return apply_master_config(session_mm_master_default_options, - session_mm_root_dir, - session_mm_master_config_file, - session_mm_master_publish_port, - session_mm_master_return_port, - session_mm_master_engine_port, - session_mm_master_config_overrides, - session_mm_master_id, - [session_base_env_state_tree_root_dir.strpath], - [session_prod_env_state_tree_root_dir.strpath], - [session_base_env_pillar_tree_root_dir.strpath], - [session_prod_env_pillar_tree_root_dir.strpath], - running_username, - log_server_port, - log_server_level, - engines_dir, - log_handlers_dir, - session_mm_master_log_prefix, - session_mm_master_tcp_master_pub_port, - session_mm_master_tcp_master_pull_port, - session_mm_master_tcp_master_publish_pull, - session_mm_master_tcp_master_workers) + """ + return apply_master_config( + session_mm_master_default_options, + session_mm_root_dir, + session_mm_master_config_file, + session_mm_master_publish_port, + session_mm_master_return_port, + session_mm_master_engine_port, + session_mm_master_config_overrides, + session_mm_master_id, + [session_base_env_state_tree_root_dir.strpath], + [session_prod_env_state_tree_root_dir.strpath], + [session_base_env_pillar_tree_root_dir.strpath], + [session_prod_env_pillar_tree_root_dir.strpath], + running_username, + log_server_port, + log_server_level, + engines_dir, + log_handlers_dir, + session_mm_master_log_prefix, + session_mm_master_tcp_master_pub_port, + session_mm_master_tcp_master_pull_port, + session_mm_master_tcp_master_publish_pull, + session_mm_master_tcp_master_workers, + ) -@pytest.fixture(scope='session') -def session_mm_salt_master(request, - session_mm_conf_dir, - session_mm_master_id, - session_mm_master_config, - log_server, # pylint: disable=unused-argument - session_mm_master_log_prefix, - cli_master_script_name, - _cli_bin_dir, - _salt_fail_hard, - ): - ''' +@pytest.fixture(scope="session") +def session_mm_salt_master( + request, + session_mm_conf_dir, + session_mm_master_id, + session_mm_master_config, + log_server, # pylint: disable=unused-argument + session_mm_master_log_prefix, + cli_master_script_name, + _cli_bin_dir, + _salt_fail_hard, +): + """ Returns a running salt-master - ''' - return start_daemon(request, - daemon_name='salt-master', - daemon_id=session_mm_master_id, - daemon_log_prefix=session_mm_master_log_prefix, - daemon_cli_script_name=cli_master_script_name, - daemon_config=session_mm_master_config, - daemon_config_dir=session_mm_conf_dir, - daemon_class=SaltMaster, - bin_dir_path=_cli_bin_dir, - fail_hard=_salt_fail_hard, - event_listener_config_dir=session_mm_conf_dir, - start_timeout=60) + """ + return start_daemon( + request, + daemon_name="salt-master", + daemon_id=session_mm_master_id, + daemon_log_prefix=session_mm_master_log_prefix, + daemon_cli_script_name=cli_master_script_name, + daemon_config=session_mm_master_config, + daemon_config_dir=session_mm_conf_dir, + daemon_class=SaltMaster, + bin_dir_path=_cli_bin_dir, + fail_hard=_salt_fail_hard, + event_listener_config_dir=session_mm_conf_dir, + start_timeout=60, + ) + + # <---- Master Fixtures ---------------------------------------------------------------------------------------------- -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_root_dir(tempdir): - ''' + """ Return the session scoped salt secondary root dir - ''' + """ return tempdir.mkdir(SESSION_SECONDARY_ROOT_DIR) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_conf_dir(session_mm_secondary_root_dir): - ''' + """ Return the session scoped salt root dir - ''' - return session_mm_secondary_root_dir.join('conf').ensure(dir=True) + """ + return session_mm_secondary_root_dir.join("conf").ensure(dir=True) # ----- Sub Master Fixtures -----------------------------------------------------------------------------------------> -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_id(): - ''' + """ Returns the session scoped master id - ''' - return 'mm-sub-master' + """ + return "mm-sub-master" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_publish_port(): - ''' + """ Returns an unused localhost port for the master publish interface - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_return_port(): - ''' + """ Returns an unused localhost port for the master return interface - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_engine_port(): - ''' + """ Returns an unused localhost port for the pytest session salt master engine - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_tcp_master_pub_port(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_tcp_master_pull_port(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_tcp_master_publish_pull(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_tcp_master_workers(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_log_prefix(session_mm_secondary_master_id): - return 'salt-master/{}'.format(session_mm_secondary_master_id) + return "salt-master/{}".format(session_mm_secondary_master_id) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_config_file(session_mm_secondary_conf_dir): - ''' + """ Returns the path to the salt master configuration file - ''' - return session_mm_secondary_conf_dir.join('master').realpath().strpath + """ + return session_mm_secondary_conf_dir.join("master").realpath().strpath -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_master_default_options(session_master_default_options): opts = session_master_default_options.copy() - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_sub_master')) as rfh: + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.CONF_DIR, "mm_sub_master") + ) as rfh: opts.update(yaml.deserialize(rfh.read())) return opts -@pytest.fixture(scope='session') -def session_mm_secondary_master_config_overrides(session_master_config_overrides, - session_mm_secondary_root_dir): +@pytest.fixture(scope="session") +def session_mm_secondary_master_config_overrides( + session_master_config_overrides, session_mm_secondary_root_dir +): overrides = session_master_config_overrides.copy() - pytest_stop_sending_events_file = session_mm_secondary_root_dir.join('pytest_mm_stop_sending_events_file').strpath - with salt.utils.files.fopen(pytest_stop_sending_events_file, 'w') as wfh: - wfh.write('') - overrides['pytest_stop_sending_events_file'] = pytest_stop_sending_events_file + pytest_stop_sending_events_file = session_mm_secondary_root_dir.join( + "pytest_mm_stop_sending_events_file" + ).strpath + with salt.utils.files.fopen(pytest_stop_sending_events_file, "w") as wfh: + wfh.write("") + overrides["pytest_stop_sending_events_file"] = pytest_stop_sending_events_file return overrides -@pytest.fixture(scope='session') -def session_mm_secondary_master_config(session_mm_secondary_root_dir, - session_mm_secondary_master_default_options, - session_mm_secondary_master_config_file, - session_mm_secondary_master_publish_port, - session_mm_secondary_master_return_port, - session_mm_secondary_master_engine_port, - session_mm_secondary_master_config_overrides, - session_mm_secondary_master_id, - session_base_env_state_tree_root_dir, - session_prod_env_state_tree_root_dir, - session_base_env_pillar_tree_root_dir, - session_prod_env_pillar_tree_root_dir, - running_username, - log_server_port, - log_server_level, - engines_dir, - log_handlers_dir, - session_mm_secondary_master_log_prefix, - session_mm_secondary_master_tcp_master_pub_port, - session_mm_secondary_master_tcp_master_pull_port, - session_mm_secondary_master_tcp_master_publish_pull, - session_mm_secondary_master_tcp_master_workers): - ''' +@pytest.fixture(scope="session") +def session_mm_secondary_master_config( + session_mm_secondary_root_dir, + session_mm_secondary_master_default_options, + session_mm_secondary_master_config_file, + session_mm_secondary_master_publish_port, + session_mm_secondary_master_return_port, + session_mm_secondary_master_engine_port, + session_mm_secondary_master_config_overrides, + session_mm_secondary_master_id, + session_base_env_state_tree_root_dir, + session_prod_env_state_tree_root_dir, + session_base_env_pillar_tree_root_dir, + session_prod_env_pillar_tree_root_dir, + running_username, + log_server_port, + log_server_level, + engines_dir, + log_handlers_dir, + session_mm_secondary_master_log_prefix, + session_mm_secondary_master_tcp_master_pub_port, + session_mm_secondary_master_tcp_master_pull_port, + session_mm_secondary_master_tcp_master_publish_pull, + session_mm_secondary_master_tcp_master_workers, +): + """ This fixture will return the salt master configuration options after being overridden with any options passed from ``session_master_config_overrides`` - ''' - return apply_master_config(session_mm_secondary_master_default_options, - session_mm_secondary_root_dir, - session_mm_secondary_master_config_file, - session_mm_secondary_master_publish_port, - session_mm_secondary_master_return_port, - session_mm_secondary_master_engine_port, - session_mm_secondary_master_config_overrides, - session_mm_secondary_master_id, - [session_base_env_state_tree_root_dir.strpath], - [session_prod_env_state_tree_root_dir.strpath], - [session_base_env_pillar_tree_root_dir.strpath], - [session_prod_env_pillar_tree_root_dir.strpath], - running_username, - log_server_port, - log_server_level, - engines_dir, - log_handlers_dir, - session_mm_secondary_master_log_prefix, - session_mm_secondary_master_tcp_master_pub_port, - session_mm_secondary_master_tcp_master_pull_port, - session_mm_secondary_master_tcp_master_publish_pull, - session_mm_secondary_master_tcp_master_workers) + """ + return apply_master_config( + session_mm_secondary_master_default_options, + session_mm_secondary_root_dir, + session_mm_secondary_master_config_file, + session_mm_secondary_master_publish_port, + session_mm_secondary_master_return_port, + session_mm_secondary_master_engine_port, + session_mm_secondary_master_config_overrides, + session_mm_secondary_master_id, + [session_base_env_state_tree_root_dir.strpath], + [session_prod_env_state_tree_root_dir.strpath], + [session_base_env_pillar_tree_root_dir.strpath], + [session_prod_env_pillar_tree_root_dir.strpath], + running_username, + log_server_port, + log_server_level, + engines_dir, + log_handlers_dir, + session_mm_secondary_master_log_prefix, + session_mm_secondary_master_tcp_master_pub_port, + session_mm_secondary_master_tcp_master_pull_port, + session_mm_secondary_master_tcp_master_publish_pull, + session_mm_secondary_master_tcp_master_workers, + ) -@pytest.fixture(scope='session') -def session_mm_secondary_salt_master(request, - session_mm_secondary_conf_dir, - session_mm_secondary_master_id, - session_mm_secondary_master_config, - log_server, # pylint: disable=unused-argument - session_mm_secondary_master_log_prefix, - cli_master_script_name, - _cli_bin_dir, - _salt_fail_hard, - session_mm_master_config, - session_mm_salt_master - ): - ''' +@pytest.fixture(scope="session") +def session_mm_secondary_salt_master( + request, + session_mm_secondary_conf_dir, + session_mm_secondary_master_id, + session_mm_secondary_master_config, + log_server, # pylint: disable=unused-argument + session_mm_secondary_master_log_prefix, + cli_master_script_name, + _cli_bin_dir, + _salt_fail_hard, + session_mm_master_config, + session_mm_salt_master, +): + """ Returns a running salt-master - ''' + """ # The secondary salt master depends on the primarily salt master fixture # because we need to clone the keys - for keyfile in ('master.pem', 'master.pub'): + for keyfile in ("master.pem", "master.pub"): shutil.copyfile( - os.path.join(session_mm_master_config['pki_dir'], keyfile), - os.path.join(session_mm_secondary_master_config['pki_dir'], keyfile) + os.path.join(session_mm_master_config["pki_dir"], keyfile), + os.path.join(session_mm_secondary_master_config["pki_dir"], keyfile), ) - return start_daemon(request, - daemon_name='salt-master', - daemon_id=session_mm_secondary_master_id, - daemon_log_prefix=session_mm_secondary_master_log_prefix, - daemon_cli_script_name=cli_master_script_name, - daemon_config=session_mm_secondary_master_config, - daemon_config_dir=session_mm_secondary_conf_dir, - daemon_class=SaltMaster, - bin_dir_path=_cli_bin_dir, - fail_hard=_salt_fail_hard, - event_listener_config_dir=session_mm_secondary_conf_dir, - start_timeout=60) + return start_daemon( + request, + daemon_name="salt-master", + daemon_id=session_mm_secondary_master_id, + daemon_log_prefix=session_mm_secondary_master_log_prefix, + daemon_cli_script_name=cli_master_script_name, + daemon_config=session_mm_secondary_master_config, + daemon_config_dir=session_mm_secondary_conf_dir, + daemon_class=SaltMaster, + bin_dir_path=_cli_bin_dir, + fail_hard=_salt_fail_hard, + event_listener_config_dir=session_mm_secondary_conf_dir, + start_timeout=60, + ) + + # <---- Sub Master Fixtures ------------------------------------------------------------------------------------------ # ----- Sub Minion Fixtures ---------------------------------------------------------------------------------------------> -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_minion_id(): - ''' + """ Returns the session scoped minion id - ''' - return 'mm-sub-minion' + """ + return "mm-sub-minion" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_minion_tcp_pub_port(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_minion_tcp_pull_port(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_minion_log_prefix(session_mm_secondary_minion_id): - return 'salt-minion/{}'.format(session_mm_secondary_minion_id) + return "salt-minion/{}".format(session_mm_secondary_minion_id) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_secondary_minion_config_file(session_mm_secondary_conf_dir): - ''' + """ Returns the path to the salt minion configuration file - ''' - return session_mm_secondary_conf_dir.join('minion').realpath().strpath + """ + return session_mm_secondary_conf_dir.join("minion").realpath().strpath -@pytest.fixture(scope='session') -def session_mm_secondary_minion_default_options(session_secondary_minion_default_options): +@pytest.fixture(scope="session") +def session_mm_secondary_minion_default_options( + session_secondary_minion_default_options, +): opts = session_secondary_minion_default_options.copy() - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_sub_minion')) as rfh: + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.CONF_DIR, "mm_sub_minion") + ) as rfh: opts.update(yaml.deserialize(rfh.read())) return opts -@pytest.fixture(scope='session') -def session_mm_secondary_minion_config_overrides(session_secondary_minion_config_overrides, - session_mm_master_return_port, - session_mm_secondary_master_return_port): +@pytest.fixture(scope="session") +def session_mm_secondary_minion_config_overrides( + session_secondary_minion_config_overrides, + session_mm_master_return_port, + session_mm_secondary_master_return_port, +): if session_secondary_minion_config_overrides: opts = session_secondary_minion_config_overrides.copy() else: opts = {} - opts['master_port'] = None - opts['master'] = [ - 'localhost:{}'.format(session_mm_master_return_port), - 'localhost:{}'.format(session_mm_secondary_master_return_port) + opts["master_port"] = None + opts["master"] = [ + "localhost:{}".format(session_mm_master_return_port), + "localhost:{}".format(session_mm_secondary_master_return_port), ] return opts -@pytest.fixture(scope='session') -def session_mm_secondary_minion_config(session_mm_secondary_root_dir, - session_mm_secondary_minion_config_file, - session_mm_secondary_master_return_port, - session_mm_secondary_minion_default_options, - session_mm_secondary_minion_config_overrides, - session_mm_secondary_minion_id, - running_username, - log_server_port, - log_server_level, - log_handlers_dir, - session_mm_secondary_minion_log_prefix, - session_mm_secondary_minion_tcp_pub_port, - session_mm_secondary_minion_tcp_pull_port): - ''' +@pytest.fixture(scope="session") +def session_mm_secondary_minion_config( + session_mm_secondary_root_dir, + session_mm_secondary_minion_config_file, + session_mm_secondary_master_return_port, + session_mm_secondary_minion_default_options, + session_mm_secondary_minion_config_overrides, + session_mm_secondary_minion_id, + running_username, + log_server_port, + log_server_level, + log_handlers_dir, + session_mm_secondary_minion_log_prefix, + session_mm_secondary_minion_tcp_pub_port, + session_mm_secondary_minion_tcp_pull_port, +): + """ This fixture will return the session salt minion configuration options after being overrided with any options passed from ``session_secondary_minion_config_overrides`` - ''' - return apply_minion_config(session_mm_secondary_minion_default_options, - session_mm_secondary_root_dir, - session_mm_secondary_minion_config_file, - session_mm_secondary_master_return_port, - session_mm_secondary_minion_config_overrides, - session_mm_secondary_minion_id, - running_username, - log_server_port, - log_server_level, - log_handlers_dir, - session_mm_secondary_minion_log_prefix, - session_mm_secondary_minion_tcp_pub_port, - session_mm_secondary_minion_tcp_pull_port) + """ + return apply_minion_config( + session_mm_secondary_minion_default_options, + session_mm_secondary_root_dir, + session_mm_secondary_minion_config_file, + session_mm_secondary_master_return_port, + session_mm_secondary_minion_config_overrides, + session_mm_secondary_minion_id, + running_username, + log_server_port, + log_server_level, + log_handlers_dir, + session_mm_secondary_minion_log_prefix, + session_mm_secondary_minion_tcp_pub_port, + session_mm_secondary_minion_tcp_pull_port, + ) -@pytest.fixture(scope='session') -def session_mm_secondary_salt_minion(request, - session_mm_salt_master, - session_mm_secondary_salt_master, - session_mm_secondary_minion_id, - session_mm_secondary_minion_config, - session_mm_secondary_minion_log_prefix, - cli_minion_script_name, - log_server, - _cli_bin_dir, - _salt_fail_hard, - session_mm_secondary_conf_dir): - ''' +@pytest.fixture(scope="session") +def session_mm_secondary_salt_minion( + request, + session_mm_salt_master, + session_mm_secondary_salt_master, + session_mm_secondary_minion_id, + session_mm_secondary_minion_config, + session_mm_secondary_minion_log_prefix, + cli_minion_script_name, + log_server, + _cli_bin_dir, + _salt_fail_hard, + session_mm_secondary_conf_dir, +): + """ Returns a running salt-minion - ''' - return start_daemon(request, - daemon_name='salt-minion', - daemon_id=session_mm_secondary_minion_id, - daemon_log_prefix=session_mm_secondary_minion_log_prefix, - daemon_cli_script_name=cli_minion_script_name, - daemon_config=session_mm_secondary_minion_config, - daemon_config_dir=session_mm_secondary_conf_dir, - daemon_class=SaltMinion, - bin_dir_path=_cli_bin_dir, - fail_hard=_salt_fail_hard, - event_listener_config_dir=session_mm_secondary_conf_dir, - start_timeout=60) + """ + return start_daemon( + request, + daemon_name="salt-minion", + daemon_id=session_mm_secondary_minion_id, + daemon_log_prefix=session_mm_secondary_minion_log_prefix, + daemon_cli_script_name=cli_minion_script_name, + daemon_config=session_mm_secondary_minion_config, + daemon_config_dir=session_mm_secondary_conf_dir, + daemon_class=SaltMinion, + bin_dir_path=_cli_bin_dir, + fail_hard=_salt_fail_hard, + event_listener_config_dir=session_mm_secondary_conf_dir, + start_timeout=60, + ) + + # <---- Minion Fixtures ---------------------------------------------------------------------------------------------- # ----- Minion Fixtures -----------------------------------------------------------------------------------------> -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_minion_id(): - ''' + """ Returns the session scoped minion id - ''' - return 'mm-minion' + """ + return "mm-minion" -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_minion_tcp_pub_port(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_minion_tcp_pull_port(): - ''' + """ Returns an unused localhost port - ''' + """ return get_unused_localhost_port() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_minion_log_prefix(session_mm_minion_id): - return 'salt-minion/{}'.format(session_mm_minion_id) + return "salt-minion/{}".format(session_mm_minion_id) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_minion_config_file(session_mm_conf_dir): - ''' + """ Returns the path to the salt minion configuration file - ''' - return session_mm_conf_dir.join('minion').realpath().strpath + """ + return session_mm_conf_dir.join("minion").realpath().strpath -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def session_mm_minion_default_options(session_minion_default_options): opts = session_minion_default_options.copy() - with salt.utils.files.fopen(os.path.join(RUNTIME_VARS.CONF_DIR, 'mm_sub_minion')) as rfh: + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.CONF_DIR, "mm_sub_minion") + ) as rfh: opts.update(yaml.deserialize(rfh.read())) return opts -@pytest.fixture(scope='session') -def session_mm_minion_config_overrides(session_minion_config_overrides, - session_mm_master_return_port, - session_mm_secondary_master_return_port): +@pytest.fixture(scope="session") +def session_mm_minion_config_overrides( + session_minion_config_overrides, + session_mm_master_return_port, + session_mm_secondary_master_return_port, +): if session_minion_config_overrides: opts = session_minion_config_overrides.copy() else: opts = {} - opts['master_port'] = None - opts['master'] = [ - 'localhost:{}'.format(session_mm_master_return_port), - 'localhost:{}'.format(session_mm_secondary_master_return_port) + opts["master_port"] = None + opts["master"] = [ + "localhost:{}".format(session_mm_master_return_port), + "localhost:{}".format(session_mm_secondary_master_return_port), ] return opts -@pytest.fixture(scope='session') -def session_mm_minion_config(session_mm_root_dir, - session_mm_minion_config_file, - session_mm_master_return_port, - session_mm_minion_default_options, - session_mm_minion_config_overrides, - session_mm_minion_id, - running_username, - log_server_port, - log_server_level, - log_handlers_dir, - session_mm_minion_log_prefix, - session_mm_minion_tcp_pub_port, - session_mm_minion_tcp_pull_port): - ''' +@pytest.fixture(scope="session") +def session_mm_minion_config( + session_mm_root_dir, + session_mm_minion_config_file, + session_mm_master_return_port, + session_mm_minion_default_options, + session_mm_minion_config_overrides, + session_mm_minion_id, + running_username, + log_server_port, + log_server_level, + log_handlers_dir, + session_mm_minion_log_prefix, + session_mm_minion_tcp_pub_port, + session_mm_minion_tcp_pull_port, +): + """ This fixture will return the session salt minion configuration options after being overrided with any options passed from ``session_minion_config_overrides`` - ''' - return apply_minion_config(session_mm_minion_default_options, - session_mm_root_dir, - session_mm_minion_config_file, - session_mm_master_return_port, - session_mm_minion_config_overrides, - session_mm_minion_id, - running_username, - log_server_port, - log_server_level, - log_handlers_dir, - session_mm_minion_log_prefix, - session_mm_minion_tcp_pub_port, - session_mm_minion_tcp_pull_port) + """ + return apply_minion_config( + session_mm_minion_default_options, + session_mm_root_dir, + session_mm_minion_config_file, + session_mm_master_return_port, + session_mm_minion_config_overrides, + session_mm_minion_id, + running_username, + log_server_port, + log_server_level, + log_handlers_dir, + session_mm_minion_log_prefix, + session_mm_minion_tcp_pub_port, + session_mm_minion_tcp_pull_port, + ) -@pytest.fixture(scope='session') -def session_mm_salt_minion(request, - session_mm_salt_master, - session_mm_secondary_salt_master, - session_mm_minion_id, - session_mm_minion_config, - session_mm_minion_log_prefix, - cli_minion_script_name, - log_server, - _cli_bin_dir, - _salt_fail_hard, - session_mm_conf_dir): - ''' +@pytest.fixture(scope="session") +def session_mm_salt_minion( + request, + session_mm_salt_master, + session_mm_secondary_salt_master, + session_mm_minion_id, + session_mm_minion_config, + session_mm_minion_log_prefix, + cli_minion_script_name, + log_server, + _cli_bin_dir, + _salt_fail_hard, + session_mm_conf_dir, +): + """ Returns a running salt-minion - ''' - return start_daemon(request, - daemon_name='salt-minion', - daemon_id=session_mm_minion_id, - daemon_log_prefix=session_mm_minion_log_prefix, - daemon_cli_script_name=cli_minion_script_name, - daemon_config=session_mm_minion_config, - daemon_config_dir=session_mm_conf_dir, - daemon_class=SaltMinion, - bin_dir_path=_cli_bin_dir, - fail_hard=_salt_fail_hard, - event_listener_config_dir=session_mm_conf_dir, - start_timeout=60) + """ + return start_daemon( + request, + daemon_name="salt-minion", + daemon_id=session_mm_minion_id, + daemon_log_prefix=session_mm_minion_log_prefix, + daemon_cli_script_name=cli_minion_script_name, + daemon_config=session_mm_minion_config, + daemon_config_dir=session_mm_conf_dir, + daemon_class=SaltMinion, + bin_dir_path=_cli_bin_dir, + fail_hard=_salt_fail_hard, + event_listener_config_dir=session_mm_conf_dir, + start_timeout=60, + ) + + # <---- Sub Minion Fixtures ------------------------------------------------------------------------------------------ -@pytest.fixture(scope='session') -def default_session_daemons(request, - log_server, - session_mm_salt_master, - session_mm_secondary_salt_master, - session_mm_salt_minion, - session_mm_secondary_salt_minion, - ): +@pytest.fixture(scope="session") +def default_session_daemons( + request, + log_server, + session_mm_salt_master, + session_mm_secondary_salt_master, + session_mm_salt_minion, + session_mm_secondary_salt_minion, +): - request.session.stats_processes.update(OrderedDict(( - ('Salt MM Master', psutil.Process(session_mm_salt_master.pid)), - ('Salt MM Minion', psutil.Process(session_mm_salt_minion.pid)), - ('Salt MM Sub Master', psutil.Process(session_mm_secondary_salt_master.pid)), - ('Salt MM Sub Minion', psutil.Process(session_mm_secondary_salt_minion.pid)), - )).items()) + request.session.stats_processes.update( + OrderedDict( + ( + ("Salt MM Master", psutil.Process(session_mm_salt_master.pid)), + ("Salt MM Minion", psutil.Process(session_mm_salt_minion.pid)), + ( + "Salt MM Sub Master", + psutil.Process(session_mm_secondary_salt_master.pid), + ), + ( + "Salt MM Sub Minion", + psutil.Process(session_mm_secondary_salt_minion.pid), + ), + ) + ).items() + ) # Run tests yield # Stop daemons now(they would be stopped at the end of the test run session - for daemon in (session_mm_secondary_salt_minion, - session_mm_secondary_salt_master, - session_mm_salt_minion, - session_mm_salt_master - ): + for daemon in ( + session_mm_secondary_salt_minion, + session_mm_secondary_salt_master, + session_mm_salt_minion, + session_mm_salt_master, + ): try: daemon.terminate() except Exception as exc: # pylint: disable=broad-except - log.warning('Failed to terminate daemon: %s', daemon.__class__.__name__) + log.warning("Failed to terminate daemon: %s", daemon.__class__.__name__) -@pytest.fixture(scope='session', autouse=True) -def mm_bridge_pytest_and_runtests(reap_stray_processes, - session_mm_conf_dir, - session_mm_secondary_conf_dir, - session_base_env_pillar_tree_root_dir, - session_base_env_state_tree_root_dir, - session_prod_env_state_tree_root_dir, - session_mm_master_config, - session_mm_minion_config, - session_mm_secondary_master_config, - session_mm_secondary_minion_config, - default_session_daemons): +@pytest.fixture(scope="session", autouse=True) +def mm_bridge_pytest_and_runtests( + reap_stray_processes, + session_mm_conf_dir, + session_mm_secondary_conf_dir, + session_base_env_pillar_tree_root_dir, + session_base_env_state_tree_root_dir, + session_prod_env_state_tree_root_dir, + session_mm_master_config, + session_mm_minion_config, + session_mm_secondary_master_config, + session_mm_secondary_minion_config, + default_session_daemons, +): # Make sure unittest2 classes know their paths RUNTIME_VARS.TMP_MM_CONF_DIR = session_mm_conf_dir.realpath().strpath RUNTIME_VARS.TMP_MM_SUB_CONF_DIR = session_mm_secondary_conf_dir.realpath().strpath - RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR = session_mm_secondary_conf_dir.realpath().strpath - RUNTIME_VARS.TMP_PILLAR_TREE = session_base_env_pillar_tree_root_dir.realpath().strpath - RUNTIME_VARS.TMP_STATE_TREE = session_base_env_state_tree_root_dir.realpath().strpath - RUNTIME_VARS.TMP_PRODENV_STATE_TREE = session_prod_env_state_tree_root_dir.realpath().strpath + RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR = ( + session_mm_secondary_conf_dir.realpath().strpath + ) + RUNTIME_VARS.TMP_PILLAR_TREE = ( + session_base_env_pillar_tree_root_dir.realpath().strpath + ) + RUNTIME_VARS.TMP_STATE_TREE = ( + session_base_env_state_tree_root_dir.realpath().strpath + ) + RUNTIME_VARS.TMP_PRODENV_STATE_TREE = ( + session_prod_env_state_tree_root_dir.realpath().strpath + ) # Make sure unittest2 uses the pytest generated configuration - RUNTIME_VARS.RUNTIME_CONFIGS['mm_master'] = freeze(session_mm_master_config) - RUNTIME_VARS.RUNTIME_CONFIGS['mm_minion'] = freeze(session_mm_minion_config) - RUNTIME_VARS.RUNTIME_CONFIGS['mm_sub_master'] = freeze(session_mm_secondary_master_config) - RUNTIME_VARS.RUNTIME_CONFIGS['mm_sub_minion'] = freeze(session_mm_secondary_minion_config) + RUNTIME_VARS.RUNTIME_CONFIGS["mm_master"] = freeze(session_mm_master_config) + RUNTIME_VARS.RUNTIME_CONFIGS["mm_minion"] = freeze(session_mm_minion_config) + RUNTIME_VARS.RUNTIME_CONFIGS["mm_sub_master"] = freeze( + session_mm_secondary_master_config + ) + RUNTIME_VARS.RUNTIME_CONFIGS["mm_sub_minion"] = freeze( + session_mm_secondary_minion_config + ) diff --git a/tests/multimaster/minion/test_event.py b/tests/multimaster/minion/test_event.py index 5eb6a081b48..2934d32612e 100644 --- a/tests/multimaster/minion/test_event.py +++ b/tests/multimaster/minion/test_event.py @@ -3,13 +3,14 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.modules.iptables + # Import Salt Testing libs from tests.support.case import MultimasterModuleCase, MultiMasterTestShellCase -from tests.support.helpers import skip_if_not_root, destructiveTest +from tests.support.helpers import destructiveTest, skip_if_not_root from tests.support.mixins import AdaptedConfigurationTestCaseMixin from tests.support.unit import skipIf -import salt.modules.iptables HAS_IPTABLES = salt.modules.iptables.__virtual__() if isinstance(HAS_IPTABLES, tuple): HAS_IPTABLES = HAS_IPTABLES[0] @@ -17,61 +18,64 @@ if isinstance(HAS_IPTABLES, tuple): @destructiveTest @skip_if_not_root -@skipIf(not HAS_IPTABLES, 'iptables command is not available') -class TestHandleEvents(MultimasterModuleCase, MultiMasterTestShellCase, AdaptedConfigurationTestCaseMixin): - ''' +@skipIf(not HAS_IPTABLES, "iptables command is not available") +class TestHandleEvents( + MultimasterModuleCase, MultiMasterTestShellCase, AdaptedConfigurationTestCaseMixin +): + """ Validate the events handling in multimaster environment - ''' + """ + def test_minion_hangs_on_master_failure_50814(self): - ''' + """ Check minion handling events for the alive master when another master is dead. The case being checked here is described in details in issue #50814. - ''' - disconnect_master_rule = '-i lo -p tcp --dport {0} -j DROP'.format( - self.mm_master_opts['ret_port']) + """ + disconnect_master_rule = "-i lo -p tcp --dport {0} -j DROP".format( + self.mm_master_opts["ret_port"] + ) # Disconnect the master. res = self.run_function( - 'iptables.append', - ('filter', 'INPUT', disconnect_master_rule), - master_tgt='mm-sub-master', - ) + "iptables.append", + ("filter", "INPUT", disconnect_master_rule), + master_tgt="mm-sub-master", + ) # Workaround slow beacons.list_available response if not res: res = self.run_function( - 'iptables.append', - ('filter', 'INPUT', disconnect_master_rule), - master_tgt='mm-sub-master', - ) + "iptables.append", + ("filter", "INPUT", disconnect_master_rule), + master_tgt="mm-sub-master", + ) self.assertTrue(res) try: # Send an event. This would return okay. res = self.run_function( - 'event.send', - ('myco/foo/bar',), - master_tgt='mm-sub-master', - ) + "event.send", ("myco/foo/bar",), master_tgt="mm-sub-master", + ) self.assertTrue(res) # Send one more event. Minion was hanging on this. This is fixed by #53417 res = self.run_function( - 'event.send', - ('myco/foo/bar',), - master_tgt='mm-sub-master', - timeout=60, - ) - self.assertTrue(res, 'Minion is not responding to the second master after the first ' - 'one has gone. Check #50814 for details.') + "event.send", ("myco/foo/bar",), master_tgt="mm-sub-master", timeout=60, + ) + self.assertTrue( + res, + "Minion is not responding to the second master after the first " + "one has gone. Check #50814 for details.", + ) finally: # Remove the firewall rule taking master online back. # Since minion could be not responsive now use `salt-call --local` for this. res = self.run_call( - "iptables.delete filter INPUT rule='{0}'".format(disconnect_master_rule), - local=True, - timeout=30) - self.assertEqual(res, ['local:']) + "iptables.delete filter INPUT rule='{0}'".format( + disconnect_master_rule + ), + local=True, + timeout=30, + ) + self.assertEqual(res, ["local:"]) # Ensure the master is back. res = self.run_function( - 'event.send', - ('myco/foo/bar',), - master_tgt='mm-master', - ) + "event.send", ("myco/foo/bar",), master_tgt="mm-master", + ) self.assertTrue(res) diff --git a/tests/multimaster/modules/test_test.py b/tests/multimaster/modules/test_test.py index 596225558c3..7eb16548095 100644 --- a/tests/multimaster/modules/test_test.py +++ b/tests/multimaster/modules/test_test.py @@ -3,90 +3,85 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.config + +# Import salt libs +import salt.version + # Import Salt Testing libs from tests.support.case import MultimasterModuleCase from tests.support.mixins import AdaptedConfigurationTestCaseMixin -# Import salt libs -import salt.version -import salt.config - class TestModuleTest(MultimasterModuleCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Validate the test module - ''' + """ + def test_ping(self): - ''' + """ test.ping - ''' - self.assertEqual(self.run_function_all_masters('test.ping'), [True] * 2) + """ + self.assertEqual(self.run_function_all_masters("test.ping"), [True] * 2) def test_echo(self): - ''' + """ test.echo - ''' - self.assertEqual(self.run_function_all_masters('test.echo', ['text']), ['text'] * 2) + """ + self.assertEqual( + self.run_function_all_masters("test.echo", ["text"]), ["text"] * 2 + ) def test_version(self): - ''' + """ test.version - ''' - self.assertEqual(self.run_function_all_masters('test.version'), - [salt.version.__saltstack_version__.string] * 2) + """ + self.assertEqual( + self.run_function_all_masters("test.version"), + [salt.version.__saltstack_version__.string] * 2, + ) def test_conf_test(self): - ''' + """ test.conf_test - ''' - self.assertEqual(self.run_function_all_masters('test.conf_test'), ['baz'] * 2) + """ + self.assertEqual(self.run_function_all_masters("test.conf_test"), ["baz"] * 2) def test_get_opts(self): - ''' + """ test.get_opts - ''' - opts = salt.config.minion_config( - self.get_config_file_path('mm_minion') - ) - ret = self.run_function_all_masters('test.get_opts') - self.assertEqual( - ret[0]['cachedir'], - opts['cachedir'] - ) - self.assertEqual( - ret[1]['cachedir'], - opts['cachedir'] - ) + """ + opts = salt.config.minion_config(self.get_config_file_path("mm_minion")) + ret = self.run_function_all_masters("test.get_opts") + self.assertEqual(ret[0]["cachedir"], opts["cachedir"]) + self.assertEqual(ret[1]["cachedir"], opts["cachedir"]) def test_cross_test(self): - ''' + """ test.cross_test - ''' - self.assertTrue( - self.run_function_all_masters( - 'test.cross_test', - ['test.ping'] - ) - ) + """ + self.assertTrue(self.run_function_all_masters("test.cross_test", ["test.ping"])) def test_fib(self): - ''' + """ test.fib - ''' - ret = self.run_function_all_masters('test.fib', ['20']) + """ + ret = self.run_function_all_masters("test.fib", ["20"]) self.assertEqual(ret[0][0], 6765) self.assertEqual(ret[1][0], 6765) def test_collatz(self): - ''' + """ test.collatz - ''' - ret = self.run_function_all_masters('test.collatz', ['40']) + """ + ret = self.run_function_all_masters("test.collatz", ["40"]) self.assertEqual(ret[0][0][-1], 2) self.assertEqual(ret[1][0][-1], 2) def test_outputter(self): - ''' + """ test.outputter - ''' - self.assertEqual(self.run_function_all_masters('test.outputter', ['text']), ['text'] * 2) + """ + self.assertEqual( + self.run_function_all_masters("test.outputter", ["text"]), ["text"] * 2 + ) diff --git a/tests/packdump.py b/tests/packdump.py index 5a230eed946..eaff6bd8c5b 100644 --- a/tests/packdump.py +++ b/tests/packdump.py @@ -1,29 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" Simple script to dump the contents of msgpack files to the terminal -''' +""" # pylint: disable=resource-leakage # Import python libs from __future__ import absolute_import, print_function + import os -import sys import pprint +import sys # Import Salt libs import salt.utils.msgpack def dump(path): - ''' + """ Read in a path and dump the contents to the screen - ''' + """ if not os.path.isfile(path): - print('Not a file') + print("Not a file") return - with open(path, 'rb') as fp_: + with open(path, "rb") as fp_: data = salt.utils.msgpack.loads(fp_.read()) pprint.pprint(data) -if __name__ == '__main__': +if __name__ == "__main__": dump(sys.argv[1]) diff --git a/tests/runtests.py b/tests/runtests.py index 5481383834e..2f9d504c544 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -1,25 +1,27 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -''' +""" Discover all instances of unittest.TestCase in this directory. -''' +""" # pylint: disable=file-perms # Import python libs from __future__ import absolute_import, print_function + +import collections import os import sys import time import warnings -import collections + TESTS_DIR = os.path.dirname(os.path.normpath(os.path.abspath(__file__))) -if os.name == 'nt': - TESTS_DIR = TESTS_DIR.replace('\\', '\\\\') +if os.name == "nt": + TESTS_DIR = TESTS_DIR.replace("\\", "\\\\") CODE_DIR = os.path.dirname(TESTS_DIR) # Let's inject CODE_DIR so salt is importable if not there already -if '' in sys.path: - sys.path.remove('') +if "" in sys.path: + sys.path.remove("") if TESTS_DIR in sys.path: sys.path.remove(TESTS_DIR) if CODE_DIR in sys.path and sys.path[0] != CODE_DIR: @@ -31,9 +33,10 @@ if TESTS_DIR not in sys.path: try: import tests + if not tests.__file__.startswith(CODE_DIR): - print('Found tests module not from salt in {}'.format(tests.__file__)) - sys.modules.pop('tests') + print("Found tests module not from salt in {}".format(tests.__file__)) + sys.modules.pop("tests") module_dir = os.path.dirname(tests.__file__) if module_dir in sys.path: sys.path.remove(module_dir) @@ -42,7 +45,7 @@ except ImportError: pass # Import salt libs -from salt.ext import six +from salt.ext import six # isort:skip try: from tests.support.paths import TMP, SYS_TMP_DIR, INTEGRATION_TEST_DIR @@ -50,33 +53,33 @@ try: except ImportError as exc: try: import tests - print('Found tests module not from salt in {}'.format(tests.__file__)) + + print("Found tests module not from salt in {}".format(tests.__file__)) except ImportError: - print('Unable to import salt test module') - print('PYTHONPATH:', os.environ.get('PYTHONPATH')) - print('Current sys.path:') + print("Unable to import salt test module") + print("PYTHONPATH:", os.environ.get("PYTHONPATH")) + print("Current sys.path:") import pprint + pprint.pprint(sys.path) six.reraise(*sys.exc_info()) -from tests.integration import TestDaemon, TestDaemonStartFailed -from tests.multimaster import MultimasterTestDaemon -import salt.utils.platform +from tests.integration import TestDaemon, TestDaemonStartFailed # isort:skip +from tests.multimaster import MultimasterTestDaemon # isort:skip +import salt.utils.platform # isort:skip if not salt.utils.platform.is_windows(): import resource # Import Salt Testing libs -from tests.support.parser import PNUM, print_header -from tests.support.parser.cover import SaltCoverageTestingParser +from tests.support.parser import PNUM, print_header # isort:skip +from tests.support.parser.cover import SaltCoverageTestingParser # isort:skip XML_OUTPUT_DIR = os.environ.get( - 'SALT_XML_TEST_REPORTS_DIR', - os.path.join(TMP, 'xml-test-reports') + "SALT_XML_TEST_REPORTS_DIR", os.path.join(TMP, "xml-test-reports") ) HTML_OUTPUT_DIR = os.environ.get( - 'SALT_HTML_TEST_REPORTS_DIR', - os.path.join(TMP, 'html-test-reports') + "SALT_HTML_TEST_REPORTS_DIR", os.path.join(TMP, "html-test-reports") ) TEST_DIR = os.path.dirname(INTEGRATION_TEST_DIR) @@ -84,117 +87,56 @@ try: if SALT_ROOT: os.chdir(SALT_ROOT) except OSError as err: - print('Failed to change directory to salt\'s source: {0}'.format(err)) + print("Failed to change directory to salt's source: {0}".format(err)) # Soft and hard limits on max open filehandles MAX_OPEN_FILES = { - 'integration': { - 'soft_limit': 3072, - 'hard_limit': 4096, - }, - 'unit': { - 'soft_limit': 1024, - 'hard_limit': 2048, - }, + "integration": {"soft_limit": 3072, "hard_limit": 4096}, + "unit": {"soft_limit": 1024, "hard_limit": 2048}, } # Combine info from command line options and test suite directories. A test # suite is a python package of test modules relative to the tests directory. TEST_SUITES_UNORDERED = { - 'unit': - {'display_name': 'Unit', - 'path': 'unit'}, - 'kitchen': - {'display_name': 'Kitchen', - 'path': 'kitchen'}, - 'multimaster': - {'display_name': 'Multimaster', - 'path': 'multimaster'}, - 'module': - {'display_name': 'Module', - 'path': 'integration/modules'}, - 'state': - {'display_name': 'State', - 'path': 'integration/states'}, - 'cli': - {'display_name': 'CLI', - 'path': 'integration/cli'}, - 'client': - {'display_name': 'Client', - 'path': 'integration/client'}, - 'doc': - {'display_name': 'Documentation', - 'path': 'integration/doc'}, - 'ext_pillar': - {'display_name': 'External Pillar', - 'path': 'integration/pillar'}, - 'grains': - {'display_name': 'Grains', - 'path': 'integration/grains'}, - 'shell': - {'display_name': 'Shell', - 'path': 'integration/shell'}, - 'runners': - {'display_name': 'Runners', - 'path': 'integration/runners'}, - 'renderers': - {'display_name': 'Renderers', - 'path': 'integration/renderers'}, - 'returners': - {'display_name': 'Returners', - 'path': 'integration/returners'}, - 'ssh-int': - {'display_name': 'SSH Integration', - 'path': 'integration/ssh'}, - 'spm': - {'display_name': 'SPM', - 'path': 'integration/spm'}, - 'loader': - {'display_name': 'Loader', - 'path': 'integration/loader'}, - 'outputter': - {'display_name': 'Outputter', - 'path': 'integration/output'}, - 'fileserver': - {'display_name': 'Fileserver', - 'path': 'integration/fileserver'}, - 'wheel': - {'display_name': 'Wheel', - 'path': 'integration/wheel'}, - 'api': - {'display_name': 'NetAPI', - 'path': 'integration/netapi'}, - 'cloud_provider': - {'display_name': 'Cloud Provider', - 'path': 'integration/cloud/clouds'}, - 'minion': - {'display_name': 'Minion', - 'path': 'integration/minion'}, - 'reactor': - {'display_name': 'Reactor', - 'path': 'integration/reactor'}, - 'proxy': - {'display_name': 'Proxy', - 'path': 'integration/proxy'}, - 'external_api': - {'display_name': 'ExternalAPIs', - 'path': 'integration/externalapi'}, - 'daemons': - {'display_name': 'Daemon', - 'path': 'integration/daemons'}, - 'scheduler': - {'display_name': 'Scheduler', - 'path': 'integration/scheduler'}, - 'sdb': - {'display_name': 'Sdb', - 'path': 'integration/sdb'}, - 'logging': - {'display_name': 'Logging', - 'path': 'integration/logging'}, + "unit": {"display_name": "Unit", "path": "unit"}, + "kitchen": {"display_name": "Kitchen", "path": "kitchen"}, + "multimaster": {"display_name": "Multimaster", "path": "multimaster"}, + "module": {"display_name": "Module", "path": "integration/modules"}, + "state": {"display_name": "State", "path": "integration/states"}, + "cli": {"display_name": "CLI", "path": "integration/cli"}, + "client": {"display_name": "Client", "path": "integration/client"}, + "doc": {"display_name": "Documentation", "path": "integration/doc"}, + "ext_pillar": {"display_name": "External Pillar", "path": "integration/pillar"}, + "grains": {"display_name": "Grains", "path": "integration/grains"}, + "shell": {"display_name": "Shell", "path": "integration/shell"}, + "runners": {"display_name": "Runners", "path": "integration/runners"}, + "renderers": {"display_name": "Renderers", "path": "integration/renderers"}, + "returners": {"display_name": "Returners", "path": "integration/returners"}, + "setup": {"display_name": "Setup", "path": "integration/setup"}, + "ssh-int": {"display_name": "SSH Integration", "path": "integration/ssh"}, + "spm": {"display_name": "SPM", "path": "integration/spm"}, + "loader": {"display_name": "Loader", "path": "integration/loader"}, + "outputter": {"display_name": "Outputter", "path": "integration/output"}, + "fileserver": {"display_name": "Fileserver", "path": "integration/fileserver"}, + "wheel": {"display_name": "Wheel", "path": "integration/wheel"}, + "api": {"display_name": "NetAPI", "path": "integration/netapi"}, + "cloud_provider": { + "display_name": "Cloud Provider", + "path": "integration/cloud/clouds", + }, + "minion": {"display_name": "Minion", "path": "integration/minion"}, + "reactor": {"display_name": "Reactor", "path": "integration/reactor"}, + "proxy": {"display_name": "Proxy", "path": "integration/proxy"}, + "external_api": {"display_name": "ExternalAPIs", "path": "integration/externalapi"}, + "daemons": {"display_name": "Daemon", "path": "integration/daemons"}, + "scheduler": {"display_name": "Scheduler", "path": "integration/scheduler"}, + "sdb": {"display_name": "Sdb", "path": "integration/sdb"}, + "logging": {"display_name": "Logging", "path": "integration/logging"}, } -TEST_SUITES = collections.OrderedDict(sorted(TEST_SUITES_UNORDERED.items(), - key=lambda x: x[0])) +TEST_SUITES = collections.OrderedDict( + sorted(TEST_SUITES_UNORDERED.items(), key=lambda x: x[0]) +) class SaltTestsuiteParser(SaltCoverageTestingParser): @@ -202,402 +144,438 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): support_destructive_tests_selection = True source_code_basedir = SALT_ROOT - def _get_suites(self, include_unit=False, include_cloud_provider=False, - include_proxy=False, include_kitchen=False, include_multimaster=False): - ''' + def _get_suites( + self, + include_unit=False, + include_cloud_provider=False, + include_proxy=False, + include_kitchen=False, + include_multimaster=False, + ): + """ Return a set of all test suites except unit and cloud provider tests unless requested - ''' + """ suites = set(TEST_SUITES.keys()) if not include_unit: - suites -= set(['unit']) + suites -= set(["unit"]) if not include_cloud_provider: - suites -= set(['cloud_provider']) + suites -= set(["cloud_provider"]) if not include_proxy: - suites -= set(['proxy']) + suites -= set(["proxy"]) if not include_kitchen: - suites -= set(['kitchen']) + suites -= set(["kitchen"]) if not include_multimaster: - suites -= set(['multimaster']) + suites -= set(["multimaster"]) return suites - def _check_enabled_suites(self, include_unit=False, - include_cloud_provider=False, - include_proxy=False, - include_kitchen=False, - include_multimaster=False): - ''' + def _check_enabled_suites( + self, + include_unit=False, + include_cloud_provider=False, + include_proxy=False, + include_kitchen=False, + include_multimaster=False, + ): + """ Query whether test suites have been enabled - ''' - suites = self._get_suites(include_unit=include_unit, - include_cloud_provider=include_cloud_provider, - include_proxy=include_proxy, - include_kitchen=include_kitchen, - include_multimaster=include_multimaster) + """ + suites = self._get_suites( + include_unit=include_unit, + include_cloud_provider=include_cloud_provider, + include_proxy=include_proxy, + include_kitchen=include_kitchen, + include_multimaster=include_multimaster, + ) return any([getattr(self.options, suite) for suite in suites]) - def _enable_suites(self, include_unit=False, include_cloud_provider=False, - include_proxy=False, include_kitchen=False, include_multimaster=False): - ''' + def _enable_suites( + self, + include_unit=False, + include_cloud_provider=False, + include_proxy=False, + include_kitchen=False, + include_multimaster=False, + ): + """ Enable test suites for current test run - ''' - suites = self._get_suites(include_unit=include_unit, - include_cloud_provider=include_cloud_provider, - include_proxy=include_proxy, - include_kitchen=include_kitchen, - include_multimaster=include_multimaster) + """ + suites = self._get_suites( + include_unit=include_unit, + include_cloud_provider=include_cloud_provider, + include_proxy=include_proxy, + include_kitchen=include_kitchen, + include_multimaster=include_multimaster, + ) for suite in suites: setattr(self.options, suite, True) def setup_additional_options(self): self.add_option( - '--sysinfo', + "--sysinfo", default=False, - action='store_true', - help='Print some system information.' + action="store_true", + help="Print some system information.", ) self.add_option( - '--transport', - default='zeromq', - choices=('zeromq', 'tcp'), - help=('Select which transport to run the integration tests with, ' - 'zeromq or tcp. Default: %default') + "--transport", + default="zeromq", + choices=("zeromq", "tcp"), + help=( + "Select which transport to run the integration tests with, " + "zeromq or tcp. Default: %default" + ), ) self.add_option( - '--interactive', + "--interactive", default=False, - action='store_true', - help='Do not run any tests. Simply start the daemons.' + action="store_true", + help="Do not run any tests. Simply start the daemons.", ) self.output_options_group.add_option( - '--no-colors', - '--no-colours', + "--no-colors", + "--no-colours", default=False, - action='store_true', - help='Disable colour printing.' + action="store_true", + help="Disable colour printing.", ) self.test_selection_group.add_option( - '-m', - '--module', - '--module-tests', - dest='module', + "-m", + "--module", + "--module-tests", + dest="module", default=False, - action='store_true', - help='Run tests for modules' + action="store_true", + help="Run tests for modules", ) self.test_selection_group.add_option( - '-S', - '--state', - '--state-tests', - dest='state', + "-S", + "--state", + "--state-tests", + dest="state", default=False, - action='store_true', - help='Run tests for states' + action="store_true", + help="Run tests for states", ) self.test_selection_group.add_option( - '-C', - '--cli', - '--cli-tests', - dest='cli', + "-C", + "--cli", + "--cli-tests", + dest="cli", default=False, - action='store_true', - help='Run tests for cli' + action="store_true", + help="Run tests for cli", ) self.test_selection_group.add_option( - '-c', - '--client', - '--client-tests', - dest='client', + "-c", + "--client", + "--client-tests", + dest="client", default=False, - action='store_true', - help='Run tests for client' + action="store_true", + help="Run tests for client", ) self.test_selection_group.add_option( - '-d', - '--doc', - '--doc-tests', - dest='doc', + "-d", + "--doc", + "--doc-tests", + dest="doc", default=False, - action='store_true', - help='Run tests for documentation' + action="store_true", + help="Run tests for documentation", ) self.test_selection_group.add_option( - '-I', - '--ext-pillar', - '--ext-pillar-tests', - dest='ext_pillar', + "-I", + "--ext-pillar", + "--ext-pillar-tests", + dest="ext_pillar", default=False, - action='store_true', - help='Run ext_pillar tests' + action="store_true", + help="Run ext_pillar tests", ) self.test_selection_group.add_option( - '-G', - '--grains', - '--grains-tests', - dest='grains', + "-G", + "--grains", + "--grains-tests", + dest="grains", default=False, - action='store_true', - help='Run tests for grains' + action="store_true", + help="Run tests for grains", ) self.test_selection_group.add_option( - '-s', - '--shell', - '--shell-tests', - dest='shell', + "-s", + "--shell", + "--shell-tests", + dest="shell", default=False, - action='store_true', - help='Run shell tests' + action="store_true", + help="Run shell tests", ) self.test_selection_group.add_option( - '-r', - '--runners', - '--runner-tests', - dest='runners', + "-r", + "--runners", + "--runner-tests", + dest="runners", default=False, - action='store_true', - help='Run salt/runners/*.py tests' + action="store_true", + help="Run salt/runners/*.py tests", ) self.test_selection_group.add_option( - '-R', - '--renderers', - '--renderer-tests', - dest='renderers', + "-R", + "--renderers", + "--renderer-tests", + dest="renderers", default=False, - action='store_true', - help='Run salt/renderers/*.py tests' + action="store_true", + help="Run salt/renderers/*.py tests", ) self.test_selection_group.add_option( - '--reactor', - dest='reactor', + "--reactor", + dest="reactor", default=False, - action='store_true', - help='Run salt/reactor/*.py tests' + action="store_true", + help="Run salt/reactor/*.py tests", ) self.test_selection_group.add_option( - '--minion', - '--minion-tests', - dest='minion', + "--minion", + "--minion-tests", + dest="minion", default=False, - action='store_true', - help='Run tests for minion' + action="store_true", + help="Run tests for minion", ) self.test_selection_group.add_option( - '--returners', - dest='returners', + "--returners", + dest="returners", default=False, - action='store_true', - help='Run salt/returners/*.py tests' + action="store_true", + help="Run salt/returners/*.py tests", ) self.test_selection_group.add_option( - '--spm', - dest='spm', + "--spm", + dest="spm", default=False, - action='store_true', - help='Run spm integration tests' + action="store_true", + help="Run spm integration tests", ) self.test_selection_group.add_option( - '-l', - '--loader', - '--loader-tests', - dest='loader', + "--setup", + dest="setup", default=False, - action='store_true', - help='Run loader tests' + action="store_true", + help="Run setup integration tests", ) self.test_selection_group.add_option( - '-u', - '--unit', - '--unit-tests', - dest='unit', + "-l", + "--loader", + "--loader-tests", + dest="loader", default=False, - action='store_true', - help='Run unit tests' + action="store_true", + help="Run loader tests", ) self.test_selection_group.add_option( - '-k', - '--kitchen', - '--kitchen-tests', - dest='kitchen', + "-u", + "--unit", + "--unit-tests", + dest="unit", default=False, - action='store_true', - help='Run kitchen tests' + action="store_true", + help="Run unit tests", ) self.test_selection_group.add_option( - '--fileserver', - '--fileserver-tests', - dest='fileserver', + "-k", + "--kitchen", + "--kitchen-tests", + dest="kitchen", default=False, - action='store_true', - help='Run Fileserver tests' + action="store_true", + help="Run kitchen tests", ) self.test_selection_group.add_option( - '-w', - '--wheel', - '--wheel-tests', - dest='wheel', - action='store_true', + "--fileserver", + "--fileserver-tests", + dest="fileserver", default=False, - help='Run wheel tests' + action="store_true", + help="Run Fileserver tests", ) self.test_selection_group.add_option( - '-o', - '--outputter', - '--outputter-tests', - dest='outputter', - action='store_true', + "-w", + "--wheel", + "--wheel-tests", + dest="wheel", + action="store_true", default=False, - help='Run outputter tests' + help="Run wheel tests", ) self.test_selection_group.add_option( - '--cloud-provider', - '--cloud-provider-tests', - dest='cloud_provider', - action='store_true', + "-o", + "--outputter", + "--outputter-tests", + dest="outputter", + action="store_true", default=False, - help=('Run cloud provider tests. These tests create and delete ' - 'instances on cloud providers. Must provide valid credentials ' - 'in salt/tests/integration/files/conf/cloud.*.d to run tests.') + help="Run outputter tests", ) self.test_selection_group.add_option( - '--ssh', - '--ssh-tests', - dest='ssh', - action='store_true', + "--cloud-provider", + "--cloud-provider-tests", + dest="cloud_provider", + action="store_true", default=False, - help='Run salt-ssh tests. These tests will spin up a temporary ' - 'SSH server on your machine. In certain environments, this ' - 'may be insecure! Default: False' + help=( + "Run cloud provider tests. These tests create and delete " + "instances on cloud providers. Must provide valid credentials " + "in salt/tests/integration/files/conf/cloud.*.d to run tests." + ), ) self.test_selection_group.add_option( - '--ssh-int', - dest='ssh-int', - action='store_true', + "--ssh", + "--ssh-tests", + dest="ssh", + action="store_true", default=False, - help='Run salt-ssh integration tests. Requires to be run with --ssh' - 'to spin up the SSH server on your machine.' + help="Run salt-ssh tests. These tests will spin up a temporary " + "SSH server on your machine. In certain environments, this " + "may be insecure! Default: False", ) self.test_selection_group.add_option( - '-A', - '--api', - '--api-tests', - dest='api', - action='store_true', + "--ssh-int", + dest="ssh-int", + action="store_true", default=False, - help='Run salt-api tests' + help="Run salt-ssh integration tests. Requires to be run with --ssh" + "to spin up the SSH server on your machine.", ) self.test_selection_group.add_option( - '--sdb', - '--sdb-tests', - dest='sdb', - action='store_true', + "-A", + "--api", + "--api-tests", + dest="api", + action="store_true", default=False, - help='Run sdb tests' + help="Run salt-api tests", ) self.test_selection_group.add_option( - '-P', - '--proxy', - '--proxy-tests', - dest='proxy', - action='store_true', + "--sdb", + "--sdb-tests", + dest="sdb", + action="store_true", default=False, - help='Run salt-proxy tests' + help="Run sdb tests", ) self.test_selection_group.add_option( - '--external', - '--external-api', - '--external-api-tests', - dest='external_api', - action='store_true', + "-P", + "--proxy", + "--proxy-tests", + dest="proxy", + action="store_true", default=False, - help='Run venafi runner tests' + help="Run salt-proxy tests", ) self.test_selection_group.add_option( - '--daemons', - '--daemon-tests', - dest='daemons', - action='store_true', + "--external", + "--external-api", + "--external-api-tests", + dest="external_api", + action="store_true", default=False, - help='Run salt/daemons/*.py tests' + help="Run venafi runner tests", ) self.test_selection_group.add_option( - '--scheduler', - dest='scheduler', - action='store_true', + "--daemons", + "--daemon-tests", + dest="daemons", + action="store_true", default=False, - help='Run scheduler integration tests' + help="Run salt/daemons/*.py tests", ) self.test_selection_group.add_option( - '--logging', - dest='logging', - action='store_true', + "--scheduler", + dest="scheduler", + action="store_true", default=False, - help='Run logging integration tests' + help="Run scheduler integration tests", ) self.test_selection_group.add_option( - '--multimaster', - dest='multimaster', - action='store_true', + "--logging", + dest="logging", + action="store_true", default=False, - help='Start multimaster daemons and run multimaster integration tests' + help="Run logging integration tests", + ) + self.test_selection_group.add_option( + "--multimaster", + dest="multimaster", + action="store_true", + default=False, + help="Start multimaster daemons and run multimaster integration tests", ) def validate_options(self): if self.options.cloud_provider or self.options.external_api: # Turn on expensive tests execution - os.environ['EXPENSIVE_TESTS'] = 'True' + os.environ["EXPENSIVE_TESTS"] = "True" # This fails even with salt.utils.platform imported in the global # scope, unless we import it again here. import salt.utils.platform + if salt.utils.platform.is_windows(): import salt.utils.win_functions + current_user = salt.utils.win_functions.get_current_user() - if current_user == 'SYSTEM': + if current_user == "SYSTEM": is_admin = True else: is_admin = salt.utils.win_functions.is_admin(current_user) - if self.options.coverage and any(( - self.options.name, - not is_admin, - not self.options.run_destructive)) \ - and self._check_enabled_suites(include_unit=True): + if ( + self.options.coverage + and any( + (self.options.name, not is_admin, not self.options.run_destructive) + ) + and self._check_enabled_suites(include_unit=True) + ): warnings.warn("Test suite not running with elevated priviledges") else: is_admin = os.geteuid() == 0 - if self.options.coverage and any(( - self.options.name, - not is_admin, - not self.options.run_destructive)) \ - and self._check_enabled_suites(include_unit=True): + if ( + self.options.coverage + and any( + (self.options.name, not is_admin, not self.options.run_destructive) + ) + and self._check_enabled_suites(include_unit=True) + ): self.error( - 'No sense in generating the tests coverage report when ' - 'not running the full test suite, including the ' - 'destructive tests, as \'root\'. It would only produce ' - 'incorrect results.' + "No sense in generating the tests coverage report when " + "not running the full test suite, including the " + "destructive tests, as 'root'. It would only produce " + "incorrect results." ) # When no tests are specifically enumerated on the command line, setup # a default run: +unit -cloud_provider - if not self.options.name and not \ - self._check_enabled_suites(include_unit=True, - include_cloud_provider=True, - include_proxy=True, - include_kitchen=True, - include_multimaster=True): + if not self.options.name and not self._check_enabled_suites( + include_unit=True, + include_cloud_provider=True, + include_proxy=True, + include_kitchen=True, + include_multimaster=True, + ): self._enable_suites(include_unit=True, include_multimaster=True) self.start_coverage( - branch=True, - source=[os.path.join(SALT_ROOT, 'salt')], + branch=True, source=[os.path.join(SALT_ROOT, "salt")], ) # Print out which version of python this test suite is running on - print(' * Python Version: {0}'.format(' '.join(sys.version.split()))) + print(" * Python Version: {0}".format(" ".join(sys.version.split()))) # Transplant configuration TestDaemon.transplant_configs(transport=self.options.transport) @@ -608,84 +586,94 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): if self.options.clean: TestDaemon.clean() - def run_integration_suite(self, path='', display_name=''): - ''' + def run_integration_suite(self, path="", display_name=""): + """ Run an integration test suite - ''' + """ full_path = os.path.join(TEST_DIR, path) return self.run_suite( - full_path, display_name, suffix='test_*.py', - failfast=self.options.failfast, + full_path, display_name, suffix="test_*.py", failfast=self.options.failfast, ) def start_daemons_only(self): if not salt.utils.platform.is_windows(): - self.set_filehandle_limits('integration') + self.set_filehandle_limits("integration") try: print_header( - ' * Setting up Salt daemons for interactive use', - top=False, width=getattr(self.options, 'output_columns', PNUM) + " * Setting up Salt daemons for interactive use", + top=False, + width=getattr(self.options, "output_columns", PNUM), ) except TypeError: - print_header(' * Setting up Salt daemons for interactive use', top=False) + print_header(" * Setting up Salt daemons for interactive use", top=False) try: with TestDaemon(self): - print_header(' * Salt daemons started') - master_conf = TestDaemon.config('master') - minion_conf = TestDaemon.config('minion') - proxy_conf = TestDaemon.config('proxy') - sub_minion_conf = TestDaemon.config('sub_minion') - syndic_conf = TestDaemon.config('syndic') - syndic_master_conf = TestDaemon.config('syndic_master') + print_header(" * Salt daemons started") + master_conf = TestDaemon.config("master") + minion_conf = TestDaemon.config("minion") + proxy_conf = TestDaemon.config("proxy") + sub_minion_conf = TestDaemon.config("sub_minion") + syndic_conf = TestDaemon.config("syndic") + syndic_master_conf = TestDaemon.config("syndic_master") - print_header(' * Syndic master configuration values (MoM)', top=False) - print('interface: {0}'.format(syndic_master_conf['interface'])) - print('publish port: {0}'.format(syndic_master_conf['publish_port'])) - print('return port: {0}'.format(syndic_master_conf['ret_port'])) - print('\n') + print_header(" * Syndic master configuration values (MoM)", top=False) + print("interface: {0}".format(syndic_master_conf["interface"])) + print("publish port: {0}".format(syndic_master_conf["publish_port"])) + print("return port: {0}".format(syndic_master_conf["ret_port"])) + print("\n") - print_header(' * Syndic configuration values', top=True) - print('interface: {0}'.format(syndic_conf['interface'])) - print('syndic master: {0}'.format(syndic_conf['syndic_master'])) - print('syndic master port: {0}'.format(syndic_conf['syndic_master_port'])) - print('\n') + print_header(" * Syndic configuration values", top=True) + print("interface: {0}".format(syndic_conf["interface"])) + print("syndic master: {0}".format(syndic_conf["syndic_master"])) + print( + "syndic master port: {0}".format(syndic_conf["syndic_master_port"]) + ) + print("\n") - print_header(' * Master configuration values', top=True) - print('interface: {0}'.format(master_conf['interface'])) - print('publish port: {0}'.format(master_conf['publish_port'])) - print('return port: {0}'.format(master_conf['ret_port'])) - print('\n') + print_header(" * Master configuration values", top=True) + print("interface: {0}".format(master_conf["interface"])) + print("publish port: {0}".format(master_conf["publish_port"])) + print("return port: {0}".format(master_conf["ret_port"])) + print("\n") - print_header(' * Minion configuration values', top=True) - print('interface: {0}'.format(minion_conf['interface'])) - print('master: {0}'.format(minion_conf['master'])) - print('master port: {0}'.format(minion_conf['master_port'])) - if minion_conf['ipc_mode'] == 'tcp': - print('tcp pub port: {0}'.format(minion_conf['tcp_pub_port'])) - print('tcp pull port: {0}'.format(minion_conf['tcp_pull_port'])) - print('\n') + print_header(" * Minion configuration values", top=True) + print("interface: {0}".format(minion_conf["interface"])) + print("master: {0}".format(minion_conf["master"])) + print("master port: {0}".format(minion_conf["master_port"])) + if minion_conf["ipc_mode"] == "tcp": + print("tcp pub port: {0}".format(minion_conf["tcp_pub_port"])) + print("tcp pull port: {0}".format(minion_conf["tcp_pull_port"])) + print("\n") - print_header(' * Sub Minion configuration values', top=True) - print('interface: {0}'.format(sub_minion_conf['interface'])) - print('master: {0}'.format(sub_minion_conf['master'])) - print('master port: {0}'.format(sub_minion_conf['master_port'])) - if sub_minion_conf['ipc_mode'] == 'tcp': - print('tcp pub port: {0}'.format(sub_minion_conf['tcp_pub_port'])) - print('tcp pull port: {0}'.format(sub_minion_conf['tcp_pull_port'])) - print('\n') + print_header(" * Sub Minion configuration values", top=True) + print("interface: {0}".format(sub_minion_conf["interface"])) + print("master: {0}".format(sub_minion_conf["master"])) + print("master port: {0}".format(sub_minion_conf["master_port"])) + if sub_minion_conf["ipc_mode"] == "tcp": + print("tcp pub port: {0}".format(sub_minion_conf["tcp_pub_port"])) + print("tcp pull port: {0}".format(sub_minion_conf["tcp_pull_port"])) + print("\n") - print_header(' * Proxy Minion configuration values', top=True) - print('interface: {0}'.format(proxy_conf['interface'])) - print('master: {0}'.format(proxy_conf['master'])) - print('master port: {0}'.format(proxy_conf['master_port'])) - if minion_conf['ipc_mode'] == 'tcp': - print('tcp pub port: {0}'.format(proxy_conf['tcp_pub_port'])) - print('tcp pull port: {0}'.format(proxy_conf['tcp_pull_port'])) - print('\n') + print_header(" * Proxy Minion configuration values", top=True) + print("interface: {0}".format(proxy_conf["interface"])) + print("master: {0}".format(proxy_conf["master"])) + print("master port: {0}".format(proxy_conf["master_port"])) + if minion_conf["ipc_mode"] == "tcp": + print("tcp pub port: {0}".format(proxy_conf["tcp_pub_port"])) + print("tcp pull port: {0}".format(proxy_conf["tcp_pull_port"])) + print("\n") - print_header(' Your client configuration is at {0}'.format(TestDaemon.config_location())) - print('To access the minion: salt -c {0} minion test.ping'.format(TestDaemon.config_location())) + print_header( + " Your client configuration is at {0}".format( + TestDaemon.config_location() + ) + ) + print( + "To access the minion: salt -c {0} minion test.ping".format( + TestDaemon.config_location() + ) + ) while True: time.sleep(1) @@ -694,78 +682,83 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): def start_multimaster_daemons_only(self): if not salt.utils.platform.is_windows(): - self.set_filehandle_limits('integration') + self.set_filehandle_limits("integration") try: print_header( - ' * Setting up Salt daemons for interactive use', - top=False, width=getattr(self.options, 'output_columns', PNUM) + " * Setting up Salt daemons for interactive use", + top=False, + width=getattr(self.options, "output_columns", PNUM), ) except TypeError: - print_header(' * Setting up Salt daemons for interactive use', top=False) + print_header(" * Setting up Salt daemons for interactive use", top=False) try: with MultimasterTestDaemon(self): - print_header(' * Salt daemons started') - master_conf = MultimasterTestDaemon.config('mm_master') - sub_master_conf = MultimasterTestDaemon.config('mm_sub_master') - minion_conf = MultimasterTestDaemon.config('mm_minion') - sub_minion_conf = MultimasterTestDaemon.config('mm_sub_minion') + print_header(" * Salt daemons started") + master_conf = MultimasterTestDaemon.config("mm_master") + sub_master_conf = MultimasterTestDaemon.config("mm_sub_master") + minion_conf = MultimasterTestDaemon.config("mm_minion") + sub_minion_conf = MultimasterTestDaemon.config("mm_sub_minion") - print_header(' * Master configuration values', top=True) - print('interface: {0}'.format(master_conf['interface'])) - print('publish port: {0}'.format(master_conf['publish_port'])) - print('return port: {0}'.format(master_conf['ret_port'])) - print('\n') + print_header(" * Master configuration values", top=True) + print("interface: {0}".format(master_conf["interface"])) + print("publish port: {0}".format(master_conf["publish_port"])) + print("return port: {0}".format(master_conf["ret_port"])) + print("\n") - print_header(' * Second master configuration values', top=True) - print('interface: {0}'.format(sub_master_conf['interface'])) - print('publish port: {0}'.format(sub_master_conf['publish_port'])) - print('return port: {0}'.format(sub_master_conf['ret_port'])) - print('\n') + print_header(" * Second master configuration values", top=True) + print("interface: {0}".format(sub_master_conf["interface"])) + print("publish port: {0}".format(sub_master_conf["publish_port"])) + print("return port: {0}".format(sub_master_conf["ret_port"])) + print("\n") - print_header(' * Minion configuration values', top=True) - print('interface: {0}'.format(minion_conf['interface'])) - print('masters: {0}'.format(', '.join(minion_conf['master']))) - if minion_conf['ipc_mode'] == 'tcp': - print('tcp pub port: {0}'.format(minion_conf['tcp_pub_port'])) - print('tcp pull port: {0}'.format(minion_conf['tcp_pull_port'])) - print('\n') + print_header(" * Minion configuration values", top=True) + print("interface: {0}".format(minion_conf["interface"])) + print("masters: {0}".format(", ".join(minion_conf["master"]))) + if minion_conf["ipc_mode"] == "tcp": + print("tcp pub port: {0}".format(minion_conf["tcp_pub_port"])) + print("tcp pull port: {0}".format(minion_conf["tcp_pull_port"])) + print("\n") - print_header(' * Sub Minion configuration values', top=True) - print('interface: {0}'.format(sub_minion_conf['interface'])) - print('masters: {0}'.format(', '.join(sub_minion_conf['master']))) - if sub_minion_conf['ipc_mode'] == 'tcp': - print('tcp pub port: {0}'.format(sub_minion_conf['tcp_pub_port'])) - print('tcp pull port: {0}'.format(sub_minion_conf['tcp_pull_port'])) - print('\n') + print_header(" * Sub Minion configuration values", top=True) + print("interface: {0}".format(sub_minion_conf["interface"])) + print("masters: {0}".format(", ".join(sub_minion_conf["master"]))) + if sub_minion_conf["ipc_mode"] == "tcp": + print("tcp pub port: {0}".format(sub_minion_conf["tcp_pub_port"])) + print("tcp pull port: {0}".format(sub_minion_conf["tcp_pull_port"])) + print("\n") - print_header(' Your client configurations are at {0}'.format( - ', '.join(MultimasterTestDaemon.config_location()))) - print('To access minions from different masters use:') + print_header( + " Your client configurations are at {0}".format( + ", ".join(MultimasterTestDaemon.config_location()) + ) + ) + print("To access minions from different masters use:") for location in MultimasterTestDaemon.config_location(): - print(' salt -c {0} minion test.ping'.format(location)) + print(" salt -c {0} minion test.ping".format(location)) while True: time.sleep(1) except TestDaemonStartFailed: self.exit(status=2) - def set_filehandle_limits(self, limits='integration'): - ''' + def set_filehandle_limits(self, limits="integration"): + """ Set soft and hard limits on open file handles at required thresholds for integration tests or unit tests - ''' + """ # Get current limits if salt.utils.platform.is_windows(): import win32file + prev_hard = win32file._getmaxstdio() prev_soft = 512 else: prev_soft, prev_hard = resource.getrlimit(resource.RLIMIT_NOFILE) # Get required limits - min_soft = MAX_OPEN_FILES[limits]['soft_limit'] - min_hard = MAX_OPEN_FILES[limits]['hard_limit'] + min_soft = MAX_OPEN_FILES[limits]["soft_limit"] + min_hard = MAX_OPEN_FILES[limits]["hard_limit"] # Check minimum required limits set_limits = False @@ -784,12 +777,12 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): # Increase limits if set_limits: print( - ' * Max open files settings is too low (soft: {0}, hard: {1}) ' - 'for running the tests'.format(prev_soft, prev_hard) + " * Max open files settings is too low (soft: {0}, hard: {1}) " + "for running the tests".format(prev_soft, prev_hard) ) print( - ' * Trying to raise the limits to soft: ' - '{0}, hard: {1}'.format(soft, hard) + " * Trying to raise the limits to soft: " + "{0}, hard: {1}".format(soft, hard) ) try: if salt.utils.platform.is_windows(): @@ -799,35 +792,51 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): resource.setrlimit(resource.RLIMIT_NOFILE, (soft, hard)) except Exception as err: # pylint: disable=broad-except print( - 'ERROR: Failed to raise the max open files settings -> ' - '{0}'.format(err) + "ERROR: Failed to raise the max open files settings -> " + "{0}".format(err) ) - print('Please issue the following command on your console:') - print(' ulimit -n {0}'.format(soft)) + print("Please issue the following command on your console:") + print(" ulimit -n {0}".format(soft)) self.exit() finally: - print('~' * getattr(self.options, 'output_columns', PNUM)) + print("~" * getattr(self.options, "output_columns", PNUM)) def run_integration_tests(self): - ''' + """ Execute the integration tests suite - ''' + """ named_tests = [] named_unit_test = [] if self.options.name: for test in self.options.name: - if test.startswith(('tests.unit.', 'unit.', - 'test.kitchen.', 'kitchen.', - 'test.multimaster.', 'multimaster.')): + if test.startswith( + ( + "tests.unit.", + "unit.", + "test.kitchen.", + "kitchen.", + "test.multimaster.", + "multimaster.", + ) + ): named_unit_test.append(test) continue named_tests.append(test) - if (self.options.unit or self.options.kitchen or self.options.multimaster or named_unit_test) \ - and not named_tests \ - and (self.options.from_filenames or - not self._check_enabled_suites(include_cloud_provider=True)): + if ( + ( + self.options.unit + or self.options.kitchen + or self.options.multimaster + or named_unit_test + ) + and not named_tests + and ( + self.options.from_filenames + or not self._check_enabled_suites(include_cloud_provider=True) + ) + ): # We're either not running any integration test suites, or we're # only running unit tests by passing --unit or by passing only # `unit.<whatever>` to --name. We don't need the tests daemon @@ -835,19 +844,25 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): return [True] if not salt.utils.platform.is_windows(): - self.set_filehandle_limits('integration') + self.set_filehandle_limits("integration") try: print_header( - ' * Setting up Salt daemons to execute tests', - top=False, width=getattr(self.options, 'output_columns', PNUM) + " * Setting up Salt daemons to execute tests", + top=False, + width=getattr(self.options, "output_columns", PNUM), ) except TypeError: - print_header(' * Setting up Salt daemons to execute tests', top=False) + print_header(" * Setting up Salt daemons to execute tests", top=False) status = [] # Return an empty status if no tests have been enabled - if not self._check_enabled_suites(include_cloud_provider=True, include_proxy=True) and not self.options.name: + if ( + not self._check_enabled_suites( + include_cloud_provider=True, include_proxy=True + ) + and not self.options.name + ): return status try: @@ -858,44 +873,63 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): if not name: continue if os.path.isfile(name): - if not name.endswith('.py'): + if not name.endswith(".py"): continue - if name.startswith((os.path.join('tests', 'unit'), - os.path.join('tests', 'multimaster'))): + if name.startswith( + ( + os.path.join("tests", "unit"), + os.path.join("tests", "multimaster"), + ) + ): continue - results = self.run_suite(os.path.dirname(name), - name, - suffix=os.path.basename(name), - failfast=self.options.failfast, - load_from_name=False) + results = self.run_suite( + os.path.dirname(name), + name, + suffix=os.path.basename(name), + failfast=self.options.failfast, + load_from_name=False, + ) status.append(results) continue - if name.startswith(('tests.unit.', 'unit.', - 'tests.multimaster.', 'multimaster.')): + if name.startswith( + ( + "tests.unit.", + "unit.", + "tests.multimaster.", + "multimaster.", + ) + ): continue results = self.run_suite( - '', name, suffix='test_*.py', load_from_name=True, + "", + name, + suffix="test_*.py", + load_from_name=True, failfast=self.options.failfast, ) status.append(results) return status for suite in TEST_SUITES: - if suite != 'unit' and suite != 'multimaster' and getattr(self.options, suite): + if ( + suite != "unit" + and suite != "multimaster" + and getattr(self.options, suite) + ): status.append(self.run_integration_suite(**TEST_SUITES[suite])) return status except TestDaemonStartFailed: self.exit(status=2) def run_multimaster_tests(self): - ''' + """ Execute the multimaster tests suite - ''' + """ named_tests = [] named_unit_test = [] if self.options.name: for test in self.options.name: - if test.startswith(('tests.multimaster.', 'multimaster.')): + if test.startswith(("tests.multimaster.", "multimaster.")): named_tests.append(test) # TODO: check 'from_filenames' @@ -904,15 +938,18 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): return [True] if not salt.utils.platform.is_windows(): - self.set_filehandle_limits('integration') + self.set_filehandle_limits("integration") try: print_header( - ' * Setting up multimaster Salt daemons to execute tests', - top=False, width=getattr(self.options, 'output_columns', PNUM) + " * Setting up multimaster Salt daemons to execute tests", + top=False, + width=getattr(self.options, "output_columns", PNUM), ) except TypeError: - print_header(' * Setting up multimaster Salt daemons to execute tests', top=False) + print_header( + " * Setting up multimaster Salt daemons to execute tests", top=False + ) status = [] @@ -924,39 +961,46 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): if not name: continue if os.path.isfile(name): - if not name.endswith('.py'): + if not name.endswith(".py"): continue - if not name.startswith(os.path.join('tests', 'multimaster')): + if not name.startswith( + os.path.join("tests", "multimaster") + ): continue - results = self.run_suite(os.path.dirname(name), - name, - suffix=os.path.basename(name), - load_from_name=False) + results = self.run_suite( + os.path.dirname(name), + name, + suffix=os.path.basename(name), + load_from_name=False, + ) status.append(results) continue - if not name.startswith(('tests.multimaster.', 'multimaster.')): + if not name.startswith(("tests.multimaster.", "multimaster.")): continue - results = self.run_suite('', name, suffix='test_*.py', load_from_name=True) + results = self.run_suite( + "", name, suffix="test_*.py", load_from_name=True + ) status.append(results) return status - status.append(self.run_integration_suite(**TEST_SUITES['multimaster'])) + status.append(self.run_integration_suite(**TEST_SUITES["multimaster"])) return status except TestDaemonStartFailed: self.exit(status=2) def run_unit_tests(self): - ''' + """ Execute the unit tests - ''' + """ named_unit_test = [] if self.options.name: for test in self.options.name: - if not test.startswith(('tests.unit.', 'unit.')): + if not test.startswith(("tests.unit.", "unit.")): continue named_unit_test.append(test) - if not named_unit_test \ - and (self.options.from_filenames or not self.options.unit): + if not named_unit_test and ( + self.options.from_filenames or not self.options.unit + ): # We are not explicitly running the unit tests and none of the # names passed to --name (or derived via --from-filenames) is a # unit test. @@ -965,10 +1009,12 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): status = [] if self.options.unit: # MacOS needs more open filehandles for running unit test suite - self.set_filehandle_limits('unit') + self.set_filehandle_limits("unit") results = self.run_suite( - os.path.join(TEST_DIR, 'unit'), 'Unit', suffix='test_*.py', + os.path.join(TEST_DIR, "unit"), + "Unit", + suffix="test_*.py", failfast=self.options.failfast, ) status.append(results) @@ -978,20 +1024,23 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): for name in named_unit_test: results = self.run_suite( - os.path.join(TEST_DIR, 'unit'), name, suffix='test_*.py', - load_from_name=True, failfast=self.options.failfast, + os.path.join(TEST_DIR, "unit"), + name, + suffix="test_*.py", + load_from_name=True, + failfast=self.options.failfast, ) status.append(results) return status def run_kitchen_tests(self): - ''' + """ Execute the kitchen tests - ''' + """ named_kitchen_test = [] if self.options.name: for test in self.options.name: - if not test.startswith(('tests.kitchen.', 'kitchen.')): + if not test.startswith(("tests.kitchen.", "kitchen.")): continue named_kitchen_test.append(test) @@ -1003,7 +1052,7 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): status = [] if self.options.kitchen: results = self.run_suite( - os.path.join(TEST_DIR, 'kitchen'), 'Kitchen', suffix='test_*.py' + os.path.join(TEST_DIR, "kitchen"), "Kitchen", suffix="test_*.py" ) status.append(results) # We executed ALL unittests, we can skip running unittests by name @@ -1012,21 +1061,24 @@ class SaltTestsuiteParser(SaltCoverageTestingParser): for name in named_kitchen_test: results = self.run_suite( - os.path.join(TEST_DIR, 'kitchen'), name, suffix='test_*.py', load_from_name=True + os.path.join(TEST_DIR, "kitchen"), + name, + suffix="test_*.py", + load_from_name=True, ) status.append(results) return status def main(**kwargs): - ''' + """ Parse command line options for running specific tests - ''' + """ try: parser = SaltTestsuiteParser( TEST_DIR, xml_output_dir=XML_OUTPUT_DIR, - tests_logfile=os.path.join(SYS_TMP_DIR, 'salt-runtests.log') + tests_logfile=os.path.join(SYS_TMP_DIR, "salt-runtests.log"), ) parser.parse_args() @@ -1056,9 +1108,9 @@ def main(**kwargs): parser.finalize(1) parser.finalize(0) except KeyboardInterrupt: - print('\nCaught keyboard interrupt. Exiting.\n') + print("\nCaught keyboard interrupt. Exiting.\n") exit(0) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tests/salt-tcpdump.py b/tests/salt-tcpdump.py index 7da9b8fcb9f..bfd48e1a7dd 100644 --- a/tests/salt-tcpdump.py +++ b/tests/salt-tcpdump.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -''' +""" Author: Volker Schwicking, vs@heg.com Salt tcpdumper to visualize whats happening network-wise on @@ -32,80 +32,86 @@ tcpdump "tcp[tcpflags] & tcp-syn != 0" and port 4505 and "tcp[tcpflags] & tcp-ac For Port 4506 tcpdump "tcp[tcpflags] & tcp-syn != 0" and port 4506 and "tcp[tcpflags] & tcp-ack == 0" -''' +""" # pylint: disable=resource-leakage # Import Python Libs from __future__ import absolute_import, print_function -import socket -from struct import unpack -import pcapy # pylint: disable=import-error,3rd-party-module-not-gated -import sys + import argparse # pylint: disable=minimum-python-version +import socket +import sys import time +from struct import unpack + +import pcapy # pylint: disable=import-error,3rd-party-module-not-gated class ArgParser(object): - ''' + """ Simple Argument-Parser class - ''' + """ + def __init__(self): - ''' + """ Init the Parser - ''' + """ self.main_parser = argparse.ArgumentParser() self.add_args() def add_args(self): - ''' + """ Add new arguments - ''' + """ - self.main_parser.add_argument('-i', - type=str, - default='eth0', - dest='iface', - required=False, - help=('the interface to dump the' - 'master runs on(default:eth0)')) + self.main_parser.add_argument( + "-i", + type=str, + default="eth0", + dest="iface", + required=False, + help=("the interface to dump the" "master runs on(default:eth0)"), + ) - self.main_parser.add_argument('-n', - type=int, - default=5, - dest='ival', - required=False, - help=('interval for printing stats ' - '(default:5)')) + self.main_parser.add_argument( + "-n", + type=int, + default=5, + dest="ival", + required=False, + help=("interval for printing stats " "(default:5)"), + ) - self.main_parser.add_argument('-I', - type=bool, - default=False, - const=True, - nargs='?', - dest='only_ip', - required=False, - help=('print unique IPs making new ' - 'connections with SYN set')) + self.main_parser.add_argument( + "-I", + type=bool, + default=False, + const=True, + nargs="?", + dest="only_ip", + required=False, + help=("print unique IPs making new " "connections with SYN set"), + ) def parse_args(self): - ''' + """ parses and returns the given arguments in a namespace object - ''' + """ return self.main_parser.parse_args() class PCAPParser(object): - ''' + """ parses a network packet on given device and returns source, target, source_port and dest_port - ''' + """ def __init__(self, iface): self.iface = iface def run(self): - ''' + """ main loop for the packet-parser - ''' + """ # open device # Arguments here are: # device @@ -119,10 +125,7 @@ class PCAPParser(object): while 1: - packet_data = { - 'ip': {}, - 'tcp': {} - } + packet_data = {"ip": {}, "tcp": {}} (header, packet) = cap.next() # pylint: disable=incompatible-py3-code @@ -132,41 +135,52 @@ class PCAPParser(object): if eth_protocol == 8: # Parse IP header # take first 20 characters for the ip header - version_ihl, version, ihl, iph_length, ttl, protocol, s_addr, d_addr = self.parse_ip(packet, eth_length) - packet_data['ip']['s_addr'] = s_addr - packet_data['ip']['d_addr'] = d_addr + ( + version_ihl, + version, + ihl, + iph_length, + ttl, + protocol, + s_addr, + d_addr, + ) = self.parse_ip(packet, eth_length) + packet_data["ip"]["s_addr"] = s_addr + packet_data["ip"]["d_addr"] = d_addr # TCP protocol if protocol == 6: - source_port, dest_port, flags, data = self.parse_tcp(packet, iph_length, eth_length) - packet_data['tcp']['d_port'] = dest_port - packet_data['tcp']['s_port'] = source_port - packet_data['tcp']['flags'] = flags - packet_data['tcp']['data'] = data + source_port, dest_port, flags, data = self.parse_tcp( + packet, iph_length, eth_length + ) + packet_data["tcp"]["d_port"] = dest_port + packet_data["tcp"]["s_port"] = source_port + packet_data["tcp"]["flags"] = flags + packet_data["tcp"]["data"] = data yield packet_data def parse_ether(self, packet): - ''' + """ parse ethernet_header and return size and protocol - ''' + """ eth_length = 14 eth_header = packet[:eth_length] - eth = unpack('!6s6sH', eth_header) + eth = unpack("!6s6sH", eth_header) eth_protocol = socket.ntohs(eth[2]) return eth_length, eth_protocol def parse_ip(self, packet, eth_length): - ''' + """ parse ip_header and return all ip data fields - ''' + """ # Parse IP header # take first 20 characters for the ip header - ip_header = packet[eth_length:20+eth_length] + ip_header = packet[eth_length : 20 + eth_length] # now unpack them:) - iph = unpack('!BBHHHBBH4s4s', ip_header) + iph = unpack("!BBHHHBBH4s4s", ip_header) version_ihl = iph[0] version = version_ihl >> 4 @@ -179,25 +193,18 @@ class PCAPParser(object): s_addr = socket.inet_ntoa(iph[8]) d_addr = socket.inet_ntoa(iph[9]) - return [version_ihl, - version, - ihl, - iph_length, - ttl, - protocol, - s_addr, - d_addr] + return [version_ihl, version, ihl, iph_length, ttl, protocol, s_addr, d_addr] def parse_tcp(self, packet, iph_length, eth_length): - ''' + """ parse tcp_data and return source_port, dest_port and actual packet data - ''' + """ p_len = iph_length + eth_length - tcp_header = packet[p_len:p_len+20] + tcp_header = packet[p_len : p_len + 20] # now unpack them:) - tcph = unpack('!H HLLBBHHH', tcp_header) + tcph = unpack("!H HLLBBHHH", tcp_header) # H H L L B B H H H # 2b 2b 4b 4b 1b 1b 2b 2b 2b # sport dport seq ack res flags win chk up @@ -220,81 +227,80 @@ class PCAPParser(object): class SaltNetstat(object): - ''' + """ Reads /proc/net/tcp and returns all connections - ''' + """ def proc_tcp(self): - ''' + """ Read the table of tcp connections & remove header - ''' - with open('/proc/net/tcp', 'r') as tcp_f: + """ + with open("/proc/net/tcp", "r") as tcp_f: content = tcp_f.readlines() content.pop(0) return content def hex2dec(self, hex_s): - ''' + """ convert hex to dezimal - ''' + """ return str(int(hex_s, 16)) def ip(self, hex_s): - ''' + """ convert into readable ip - ''' - ip = [(self.hex2dec(hex_s[6:8])), - (self.hex2dec(hex_s[4:6])), - (self.hex2dec(hex_s[2:4])), - (self.hex2dec(hex_s[0:2]))] - return '.'.join(ip) + """ + ip = [ + (self.hex2dec(hex_s[6:8])), + (self.hex2dec(hex_s[4:6])), + (self.hex2dec(hex_s[2:4])), + (self.hex2dec(hex_s[0:2])), + ] + return ".".join(ip) def remove_empty(self, array): - ''' + """ create new list without empty entries - ''' - return [x for x in array if x != ''] + """ + return [x for x in array if x != ""] def convert_ip_port(self, array): - ''' + """ hex_ip:hex_port to str_ip:str_port - ''' - host, port = array.split(':') + """ + host, port = array.split(":") return self.ip(host), self.hex2dec(port) def run(self): - ''' + """ main loop for netstat - ''' + """ while 1: - ips = { - 'ips/4505': {}, - 'ips/4506': {} - } + ips = {"ips/4505": {}, "ips/4506": {}} content = self.proc_tcp() for line in content: - line_array = self.remove_empty(line.split(' ')) + line_array = self.remove_empty(line.split(" ")) l_host, l_port = self.convert_ip_port(line_array[1]) r_host, r_port = self.convert_ip_port(line_array[2]) - if l_port == '4505': - if r_host not in ips['ips/4505']: - ips['ips/4505'][r_host] = 0 - ips['ips/4505'][r_host] += 1 - if l_port == '4506': - if r_host not in ips['ips/4506']: - ips['ips/4506'][r_host] = 0 - ips['ips/4506'][r_host] += 1 + if l_port == "4505": + if r_host not in ips["ips/4505"]: + ips["ips/4505"][r_host] = 0 + ips["ips/4505"][r_host] += 1 + if l_port == "4506": + if r_host not in ips["ips/4506"]: + ips["ips/4506"][r_host] = 0 + ips["ips/4506"][r_host] += 1 - yield (len(ips['ips/4505']), len(ips['ips/4506'])) + yield (len(ips["ips/4505"]), len(ips["ips/4506"])) time.sleep(0.5) def filter_new_cons(packet): - ''' + """ filter packets by there tcp-state and returns codes for specific states - ''' + """ flags = [] TCP_FIN = 0x01 TCP_SYN = 0x02 @@ -305,39 +311,39 @@ def filter_new_cons(packet): TCP_ECE = 0x40 TCP_CWK = 0x80 - if packet['tcp']['flags'] & TCP_FIN: - flags.append('FIN') - elif packet['tcp']['flags'] & TCP_SYN: - flags.append('SYN') - elif packet['tcp']['flags'] & TCP_RST: - flags.append('RST') - elif packet['tcp']['flags'] & TCP_PSH: - flags.append('PSH') - elif packet['tcp']['flags'] & TCP_ACK: - flags.append('ACK') - elif packet['tcp']['flags'] & TCP_URG: - flags.append('URG') - elif packet['tcp']['flags'] & TCP_ECE: - flags.append('ECE') - elif packet['tcp']['flags'] & TCP_CWK: - flags.append('CWK') + if packet["tcp"]["flags"] & TCP_FIN: + flags.append("FIN") + elif packet["tcp"]["flags"] & TCP_SYN: + flags.append("SYN") + elif packet["tcp"]["flags"] & TCP_RST: + flags.append("RST") + elif packet["tcp"]["flags"] & TCP_PSH: + flags.append("PSH") + elif packet["tcp"]["flags"] & TCP_ACK: + flags.append("ACK") + elif packet["tcp"]["flags"] & TCP_URG: + flags.append("URG") + elif packet["tcp"]["flags"] & TCP_ECE: + flags.append("ECE") + elif packet["tcp"]["flags"] & TCP_CWK: + flags.append("CWK") else: print("UNKNOWN PACKET") - if packet['tcp']['d_port'] == 4505: + if packet["tcp"]["d_port"] == 4505: # track new connections - if 'SYN' in flags and len(flags) == 1: + if "SYN" in flags and len(flags) == 1: return 10 # track closing connections - elif 'FIN' in flags: + elif "FIN" in flags: return 12 - elif packet['tcp']['d_port'] == 4506: + elif packet["tcp"]["d_port"] == 4506: # track new connections - if 'SYN' in flags and len(flags) == 1: + if "SYN" in flags and len(flags) == 1: return 100 # track closing connections - elif 'FIN' in flags: + elif "FIN" in flags: return 120 # packet does not match requirements else: @@ -345,9 +351,9 @@ def filter_new_cons(packet): def main(): - ''' + """ main loop for whole script - ''' + """ # passed parameters args = vars(ArgParser().parse_args()) @@ -357,36 +363,34 @@ def main(): # the ports we want to monitor ports = [4505, 4506] - print("Sniffing device {0}".format(args['iface'])) + print("Sniffing device {0}".format(args["iface"])) stat = { - '4506/new': 0, - '4506/est': 0, - '4506/fin': 0, - '4505/new': 0, - '4505/est': 0, - '4505/fin': 0, - 'ips/4505': 0, - 'ips/4506': 0 - } + "4506/new": 0, + "4506/est": 0, + "4506/fin": 0, + "4505/new": 0, + "4505/est": 0, + "4505/fin": 0, + "ips/4505": 0, + "ips/4506": 0, + } - if args['only_ip']: + if args["only_ip"]: print( - 'IPs making new connections ' - '(ports:{0}, interval:{1})'.format(ports, - args['ival']) - ) + "IPs making new connections " + "(ports:{0}, interval:{1})".format(ports, args["ival"]) + ) else: print( - 'Salt-Master Network Status ' - '(ports:{0}, interval:{1})'.format(ports, - args['ival']) - ) + "Salt-Master Network Status " + "(ports:{0}, interval:{1})".format(ports, args["ival"]) + ) try: while 1: s_time = int(time.time()) - packet = next(PCAPParser(args['iface']).run()) + packet = next(PCAPParser(args["iface"]).run()) p_state = filter_new_cons(packet) @@ -395,42 +399,43 @@ def main(): # new connection to 4505 if p_state == 10: - stat['4505/new'] += 1 - if packet['ip']['s_addr'] not in ips_auth: - ips_auth.append(packet['ip']['s_addr']) + stat["4505/new"] += 1 + if packet["ip"]["s_addr"] not in ips_auth: + ips_auth.append(packet["ip"]["s_addr"]) # closing connection to 4505 elif p_state == 12: - stat['4505/fin'] += 1 + stat["4505/fin"] += 1 # new connection to 4506 elif p_state == 100: - stat['4506/new'] += 1 - if packet['ip']['s_addr'] not in ips_push: - ips_push.append(packet['ip']['s_addr']) + stat["4506/new"] += 1 + if packet["ip"]["s_addr"] not in ips_push: + ips_push.append(packet["ip"]["s_addr"]) # closing connection to 4506 elif p_state == 120: - stat['4506/fin'] += 1 + stat["4506/fin"] += 1 # get the established connections to 4505 and 4506 # these would only show up in tcpdump if data is transferred # but then with different flags (PSH, etc.) - stat['4505/est'], stat['4506/est'] = next(SaltNetstat().run()) + stat["4505/est"], stat["4506/est"] = next(SaltNetstat().run()) # print only in intervals - if (s_time % args['ival']) == 0: + if (s_time % args["ival"]) == 0: # prevent printing within the same second if r_time != s_time: - if args['only_ip']: - msg = 'IPs/4505: {0}, IPs/4506: {1}'.format(len(ips_auth), - len(ips_push)) + if args["only_ip"]: + msg = "IPs/4505: {0}, IPs/4506: {1}".format( + len(ips_auth), len(ips_push) + ) else: - msg = "4505=>[ est: {0}, ".format(stat['4505/est']) - msg += "new: {0}/s, ".format(stat['4505/new'] / args['ival']) - msg += "fin: {0}/s ] ".format(stat['4505/fin'] / args['ival']) + msg = "4505=>[ est: {0}, ".format(stat["4505/est"]) + msg += "new: {0}/s, ".format(stat["4505/new"] / args["ival"]) + msg += "fin: {0}/s ] ".format(stat["4505/fin"] / args["ival"]) - msg += " 4506=>[ est: {0}, ".format(stat['4506/est']) - msg += "new: {0}/s, ".format(stat['4506/new'] / args['ival']) - msg += "fin: {0}/s ]".format(stat['4506/fin'] / args['ival']) + msg += " 4506=>[ est: {0}, ".format(stat["4506/est"]) + msg += "new: {0}/s, ".format(stat["4506/new"] / args["ival"]) + msg += "fin: {0}/s ]".format(stat["4506/fin"] / args["ival"]) print(msg) diff --git a/tests/saltsh.py b/tests/saltsh.py index acaa3a6b4b6..91e0d7d8883 100644 --- a/tests/saltsh.py +++ b/tests/saltsh.py @@ -24,12 +24,17 @@ completion behavior can be customized via the ~/.inputrc file. # Import python libs from __future__ import absolute_import + import atexit import os +import pprint # pylint: disable=unused-import import readline import sys from code import InteractiveConsole +# Import 3rd party libs +import jinja2 + # Import salt libs import salt.client import salt.config @@ -38,83 +43,77 @@ import salt.output import salt.pillar import salt.runner -# Import 3rd party libs -import jinja2 -from salt.ext.six.moves import builtins # pylint: disable=import-error - # pylint: disable=unused-import # These are imported to be available in the spawned shell import salt.utils.yaml -import pprint +from salt.ext.six.moves import builtins # pylint: disable=import-error + # pylint: enable=unused-import -HISTFILE = '{HOME}/.saltsh_history'.format(**os.environ) +HISTFILE = "{HOME}/.saltsh_history".format(**os.environ) def savehist(): - ''' + """ Save the history file - ''' + """ readline.write_history_file(HISTFILE) def get_salt_vars(): - ''' + """ Return all the Salt-usual double-under data structures for a minion - ''' + """ # Create the Salt __opts__ variable __opts__ = salt.config.client_config( - os.environ.get('SALT_MINION_CONFIG', '/etc/salt/minion')) + os.environ.get("SALT_MINION_CONFIG", "/etc/salt/minion") + ) # Populate grains if it hasn't been done already - if 'grains' not in __opts__ or not __opts__['grains']: - __opts__['grains'] = salt.loader.grains(__opts__) + if "grains" not in __opts__ or not __opts__["grains"]: + __opts__["grains"] = salt.loader.grains(__opts__) # file_roots and pillar_roots should be set in the minion config - if 'file_client' not in __opts__ or not __opts__['file_client']: - __opts__['file_client'] = 'local' + if "file_client" not in __opts__ or not __opts__["file_client"]: + __opts__["file_client"] = "local" # ensure we have a minion id - if 'id' not in __opts__ or not __opts__['id']: - __opts__['id'] = 'saltsh_mid' + if "id" not in __opts__ or not __opts__["id"]: + __opts__["id"] = "saltsh_mid" # Populate template variables __salt__ = salt.loader.minion_mods(__opts__) - __grains__ = __opts__['grains'] + __grains__ = __opts__["grains"] - if __opts__['file_client'] == 'local': + if __opts__["file_client"] == "local": __pillar__ = salt.pillar.get_pillar( - __opts__, - __grains__, - __opts__.get('id'), - __opts__.get('saltenv'), + __opts__, __grains__, __opts__.get("id"), __opts__.get("saltenv"), ).compile_pillar() else: __pillar__ = {} - JINJA = lambda x, **y: jinja2.Template(x).render( # pylint: disable=invalid-name,unused-variable,possibly-unused-variable - grains=__grains__, - salt=__salt__, - opts=__opts__, - pillar=__pillar__, - **y) + # pylint: disable=invalid-name,unused-variable,possibly-unused-variable + JINJA = lambda x, **y: jinja2.Template(x).render( + grains=__grains__, salt=__salt__, opts=__opts__, pillar=__pillar__, **y + ) + # pylint: enable=invalid-name,unused-variable,possibly-unused-variable return locals() def main(): - ''' + """ The main entry point - ''' + """ salt_vars = get_salt_vars() def salt_outputter(value): - ''' + """ Use Salt's outputters to print values to the shell - ''' + """ if value is not None: builtins._ = value - salt.output.display_output(value, '', salt_vars['__opts__']) + salt.output.display_output(value, "", salt_vars["__opts__"]) sys.displayhook = salt_outputter @@ -125,11 +124,11 @@ def main(): readline.read_history_file(HISTFILE) atexit.register(savehist) - atexit.register(lambda: sys.stdout.write('Salt you later!\n')) + atexit.register(lambda: sys.stdout.write("Salt you later!\n")) saltrepl = InteractiveConsole(locals=salt_vars) saltrepl.interact(banner=__doc__) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tests/support/case.py b/tests/support/case.py index c0e3186b8f2..66686e606ee 100644 --- a/tests/support/case.py +++ b/tests/support/case.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -9,49 +9,56 @@ Custom reusable :class:`TestCase<python2:unittest.TestCase>` implementations. -''' +""" # pylint: disable=repr-flag-used-in-string # Import python libs from __future__ import absolute_import, unicode_literals + +import errno +import logging import os import re -import sys -import time -import errno -import textwrap -import logging -import tempfile import subprocess +import sys +import tempfile +import textwrap +import time from datetime import datetime, timedelta +# Import 3rd-party libs +import salt.utils.files +from salt.ext import six +from salt.ext.six.moves import cStringIO # pylint: disable=import-error +from tests.support.cli_scripts import ScriptPathMixin +from tests.support.helpers import RedirectStdStreams, requires_sshd_server + +# ----- Backwards Compatible Imports --------------------------------------------------------------------------------> +from tests.support.mixins import ( # pylint: disable=unused-import + AdaptedConfigurationTestCaseMixin, + SaltClientTestCaseMixin, + SaltMultimasterClientTestCaseMixin, + ShellCaseCommonTestsMixin, +) +from tests.support.paths import CODE_DIR, INTEGRATION_TEST_DIR, PYEXEC, SCRIPT_DIR +from tests.support.processes import terminate_process +from tests.support.runtests import RUNTIME_VARS + # Import salt testing libs from tests.support.unit import TestCase -from tests.support.helpers import RedirectStdStreams, requires_sshd_server -from tests.support.processes import terminate_process -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import (AdaptedConfigurationTestCaseMixin, - SaltClientTestCaseMixin, - SaltMultimasterClientTestCaseMixin) -from tests.support.paths import INTEGRATION_TEST_DIR, CODE_DIR, PYEXEC, SCRIPT_DIR -from tests.support.cli_scripts import ScriptPathMixin - -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import cStringIO # pylint: disable=import-error STATE_FUNCTION_RUNNING_RE = re.compile( - r'''The function (?:"|')(?P<state_func>.*)(?:"|') is running as PID ''' - r'(?P<pid>[\d]+) and was started at (?P<date>.*) with jid (?P<jid>[\d]+)' + r"""The function (?:"|')(?P<state_func>.*)(?:"|') is running as PID """ + r"(?P<pid>[\d]+) and was started at (?P<date>.*) with jid (?P<jid>[\d]+)" ) log = logging.getLogger(__name__) class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin): - ''' + """ Execute a test for a shell command - ''' + """ def run_salt(self, arg_str, with_retcode=False, catch_stderr=False, timeout=15): r''' @@ -68,57 +75,85 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin data = '\n'.join(data) self.assertIn('minion', data) ''' - arg_str = '-c {0} -t {1} {2}'.format(self.config_dir, timeout, arg_str) - return self.run_script('salt', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr, timeout=timeout) - - def run_ssh(self, arg_str, with_retcode=False, timeout=25, - catch_stderr=False, wipe=False, raw=False, **kwargs): - ''' - Execute salt-ssh - ''' - arg_str = '{0} {1} -c {2} -i --priv {3} --roster-file {4} localhost {5} --out=json'.format( - ' -W' if wipe else '', - ' -r' if raw else '', - self.config_dir, - os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test'), - os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'roster'), - arg_str + arg_str = "-c {0} -t {1} {2}".format(self.config_dir, timeout, arg_str) + return self.run_script( + "salt", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout, ) - return self.run_script('salt-ssh', - arg_str, with_retcode=with_retcode, - catch_stderr=catch_stderr, - raw=True, - timeout=timeout, - **kwargs) - def run_run(self, - arg_str, - with_retcode=False, - catch_stderr=False, - asynchronous=False, - timeout=60, - config_dir=None, - **kwargs): - ''' + def run_ssh( + self, + arg_str, + with_retcode=False, + timeout=25, + catch_stderr=False, + wipe=False, + raw=False, + roster_file=None, + ssh_opts="", + **kwargs + ): + """ + Execute salt-ssh + """ + if not roster_file: + roster_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "roster") + + arg_str = "{0} {1} -c {2} -i --priv {3} --roster-file {4} {5} localhost {6} --out=json".format( + " -W" if wipe else "", + " -r" if raw else "", + self.config_dir, + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test"), + roster_file, + ssh_opts, + arg_str, + ) + return self.run_script( + "salt-ssh", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + raw=True, + timeout=timeout, + **kwargs + ) + + def run_run( + self, + arg_str, + with_retcode=False, + catch_stderr=False, + asynchronous=False, + timeout=60, + config_dir=None, + **kwargs + ): + """ Execute salt-run - ''' - asynchronous = kwargs.get('async', asynchronous) - arg_str = '-c {0}{async_flag} -t {timeout} {1}'.format( - config_dir or self.config_dir, - arg_str, - timeout=timeout, - async_flag=' --async' if asynchronous else '') - return self.run_script('salt-run', - arg_str, - with_retcode=with_retcode, - catch_stderr=catch_stderr, - timeout=timeout) + """ + asynchronous = kwargs.get("async", asynchronous) + arg_str = "-c {0}{async_flag} -t {timeout} {1}".format( + config_dir or self.config_dir, + arg_str, + timeout=timeout, + async_flag=" --async" if asynchronous else "", + ) + return self.run_script( + "salt-run", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout, + ) def run_run_plus(self, fun, *arg, **kwargs): - ''' + """ Execute the runner function and return the return data and output in a dict - ''' - ret = {'fun': fun} + """ + ret = {"fun": fun} # Late import import salt.config @@ -126,105 +161,116 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin import salt.runner from salt.ext.six.moves import cStringIO - opts = salt.config.master_config( - self.get_config_file_path('master') - ) + opts = salt.config.master_config(self.get_config_file_path("master")) opts_arg = list(arg) if kwargs: - opts_arg.append({'__kwarg__': True}) + opts_arg.append({"__kwarg__": True}) opts_arg[-1].update(kwargs) - opts.update({'doc': False, 'fun': fun, 'arg': opts_arg}) + opts.update({"doc": False, "fun": fun, "arg": opts_arg}) with RedirectStdStreams(): runner = salt.runner.Runner(opts) - ret['return'] = runner.run() + ret["return"] = runner.run() try: - ret['jid'] = runner.jid + ret["jid"] = runner.jid except AttributeError: - ret['jid'] = None + ret["jid"] = None # Compile output # TODO: Support outputters other than nested - opts['color'] = False - opts['output_file'] = cStringIO() + opts["color"] = False + opts["output_file"] = cStringIO() try: - salt.output.display_output(ret['return'], opts=opts) - ret['out'] = opts['output_file'].getvalue() + salt.output.display_output(ret["return"], opts=opts) + ret["out"] = opts["output_file"].getvalue() finally: - opts['output_file'].close() + opts["output_file"].close() return ret def run_key(self, arg_str, catch_stderr=False, with_retcode=False): - ''' + """ Execute salt-key - ''' - arg_str = '-c {0} {1}'.format(self.config_dir, arg_str) + """ + arg_str = "-c {0} {1}".format(self.config_dir, arg_str) return self.run_script( - 'salt-key', - arg_str, - catch_stderr=catch_stderr, - with_retcode=with_retcode + "salt-key", arg_str, catch_stderr=catch_stderr, with_retcode=with_retcode ) def run_cp(self, arg_str, with_retcode=False, catch_stderr=False): - ''' + """ Execute salt-cp - ''' - arg_str = '--config-dir {0} {1}'.format(self.config_dir, arg_str) - return self.run_script('salt-cp', arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr) + """ + arg_str = "--config-dir {0} {1}".format(self.config_dir, arg_str) + return self.run_script( + "salt-cp", arg_str, with_retcode=with_retcode, catch_stderr=catch_stderr + ) - def run_call(self, arg_str, with_retcode=False, catch_stderr=False, - local=False, timeout=15, config_dir=None): + def run_call( + self, + arg_str, + with_retcode=False, + catch_stderr=False, + local=False, + timeout=15, + config_dir=None, + ): if not config_dir: config_dir = self.config_dir - arg_str = '{0} --config-dir {1} {2}'.format('--local' if local else '', - config_dir, arg_str) + arg_str = "{0} --config-dir {1} {2}".format( + "--local" if local else "", config_dir, arg_str + ) - return self.run_script('salt-call', - arg_str, - with_retcode=with_retcode, - catch_stderr=catch_stderr, - timeout=timeout) + return self.run_script( + "salt-call", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout, + ) def assertRunCall(self, arg_str, **kwargs): - ''' + """ Assert no error code was returned with run_call, give stderr as error message - ''' - stdout, stderr, retcode = self.run_call(arg_str, catch_stderr=True, with_retcode=True, **kwargs) + """ + stdout, stderr, retcode = self.run_call( + arg_str, catch_stderr=True, with_retcode=True, **kwargs + ) if stderr: log.warning(stderr) self.assertFalse(retcode, stderr) return stdout def run_cloud(self, arg_str, catch_stderr=False, timeout=None): - ''' + """ Execute salt-cloud - ''' - arg_str = '-c {0} {1}'.format(self.config_dir, arg_str) - return self.run_script('salt-cloud', arg_str, catch_stderr, timeout) + """ + arg_str = "-c {0} {1}".format(self.config_dir, arg_str) + return self.run_script("salt-cloud", arg_str, catch_stderr, timeout) - def run_script(self, - script, - arg_str, - catch_stderr=False, - with_retcode=False, - catch_timeout=False, - # FIXME A timeout of zero or disabling timeouts may not return results! - timeout=15, - raw=False, - popen_kwargs=None, - log_output=None, - **kwargs): - ''' + def run_script( + self, + script, + arg_str, + catch_stderr=False, + with_retcode=False, + catch_timeout=False, + # FIXME A timeout of zero or disabling timeouts may not return results! + timeout=15, + raw=False, + popen_kwargs=None, + log_output=None, + **kwargs + ): + """ Execute a script with the given argument string The ``log_output`` argument is ternary, it can be True, False, or None. If the value is boolean, then it forces the results to either be logged or not logged. If it is None, then the return code of the subprocess determines whether or not to log results. - ''' + """ import salt.utils.platform @@ -234,83 +280,84 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin popen_kwargs = popen_kwargs or {} if salt.utils.platform.is_windows(): - cmd = 'python ' - if 'cwd' not in popen_kwargs: - popen_kwargs['cwd'] = os.getcwd() - if 'env' not in popen_kwargs: - popen_kwargs['env'] = os.environ.copy() + cmd = "python " + if "cwd" not in popen_kwargs: + popen_kwargs["cwd"] = os.getcwd() + if "env" not in popen_kwargs: + popen_kwargs["env"] = os.environ.copy() if sys.version_info[0] < 3: - popen_kwargs['env'][b'PYTHONPATH'] = CODE_DIR.encode() + popen_kwargs["env"][b"PYTHONPATH"] = CODE_DIR.encode() else: - popen_kwargs['env']['PYTHONPATH'] = CODE_DIR + popen_kwargs["env"]["PYTHONPATH"] = CODE_DIR else: - cmd = 'PYTHONPATH=' - python_path = os.environ.get('PYTHONPATH', None) + cmd = "PYTHONPATH=" + python_path = os.environ.get("PYTHONPATH", None) if python_path is not None: - cmd += '{0}:'.format(python_path) + cmd += "{0}:".format(python_path) if sys.version_info[0] < 3: - cmd += '{0} '.format(':'.join(sys.path[1:])) + cmd += "{0} ".format(":".join(sys.path[1:])) else: - cmd += '{0} '.format(':'.join(sys.path[0:])) - cmd += 'python{0}.{1} '.format(*sys.version_info) - cmd += '{0} '.format(script_path) - cmd += '{0} '.format(arg_str) + cmd += "{0} ".format(":".join(sys.path[0:])) + cmd += "python{0}.{1} ".format(*sys.version_info) + cmd += "{0} ".format(script_path) + cmd += "{0} ".format(arg_str) if kwargs: # late import import salt.utils.json + for key, value in kwargs.items(): cmd += "'{0}={1} '".format(key, salt.utils.json.dumps(value)) tmp_file = tempfile.SpooledTemporaryFile() - popen_kwargs = dict({ - 'shell': True, - 'stdout': tmp_file, - 'universal_newlines': True, - }, **popen_kwargs) + popen_kwargs = dict( + {"shell": True, "stdout": tmp_file, "universal_newlines": True}, + **popen_kwargs + ) if catch_stderr is True: - popen_kwargs['stderr'] = subprocess.PIPE + popen_kwargs["stderr"] = subprocess.PIPE - if not sys.platform.lower().startswith('win'): - popen_kwargs['close_fds'] = True + if not sys.platform.lower().startswith("win"): + popen_kwargs["close_fds"] = True def detach_from_parent_group(): # detach from parent group (no more inherited signals!) os.setpgrp() - popen_kwargs['preexec_fn'] = detach_from_parent_group + popen_kwargs["preexec_fn"] = detach_from_parent_group def format_return(retcode, stdout, stderr=None, timed_out=False): - ''' + """ DRY helper to log script result if it failed, and then return the desired output based on whether or not stderr was desired, and wither or not a retcode was desired. - ''' + """ log_func = log.debug if timed_out: log.error( - 'run_script timed out after %d seconds (process killed)', - timeout + "run_script timed out after %d seconds (process killed)", timeout ) log_func = log.error - if log_output is True \ - or timed_out \ - or (log_output is None and retcode != 0): + if log_output is True or timed_out or (log_output is None and retcode != 0): log_func( - 'run_script results for: %s %s\n' - 'return code: %s\n' - 'stdout:\n' - '%s\n\n' - 'stderr:\n' - '%s', - script, arg_str, retcode, stdout, stderr + "run_script results for: %s %s\n" + "return code: %s\n" + "stdout:\n" + "%s\n\n" + "stderr:\n" + "%s", + script, + arg_str, + retcode, + stdout, + stderr, ) - stdout = stdout or '' - stderr = stderr or '' + stdout = stdout or "" + stderr = stderr or "" if not raw: stdout = stdout.splitlines() @@ -341,9 +388,7 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin else: terminate_process(process.pid, kill_children=True) return format_return( - process.returncode, - *process.communicate(), - timed_out=True + process.returncode, *process.communicate(), timed_out=True ) tmp_file.seek(0) @@ -353,7 +398,7 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin out = tmp_file.read().decode(__salt_system_encoding__) except (NameError, UnicodeDecodeError): # Let's cross our fingers and hope for the best - out = tmp_file.read().decode('utf-8') + out = tmp_file.read().decode("utf-8") else: out = tmp_file.read() @@ -383,7 +428,7 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin # pylint: disable=maybe-no-member try: - return format_return(process.returncode, out, err or '') + return format_return(process.returncode, out, err or "") finally: try: if os.path.exists(tmp_file.name): @@ -424,9 +469,9 @@ class ShellTestCase(TestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin class MultiMasterTestShellCase(ShellTestCase): - ''' + """ Execute a test for a shell command when running multi-master tests - ''' + """ @property def config_dir(self): @@ -434,9 +479,9 @@ class MultiMasterTestShellCase(ShellTestCase): class ShellCase(ShellTestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixin): - ''' + """ Execute a test for a shell command - ''' + """ _code_dir_ = CODE_DIR _script_dir_ = SCRIPT_DIR @@ -449,175 +494,243 @@ class ShellCase(ShellTestCase, AdaptedConfigurationTestCaseMixin, ScriptPathMixi except OSError: os.chdir(INTEGRATION_TEST_DIR) - def run_salt(self, arg_str, with_retcode=False, catch_stderr=False, # pylint: disable=W0221 - timeout=RUN_TIMEOUT, popen_kwargs=None): - ''' + # pylint: disable=arguments-differ + def run_salt( + self, + arg_str, + with_retcode=False, + catch_stderr=False, + timeout=RUN_TIMEOUT, + popen_kwargs=None, + ): + """ Execute salt - ''' - arg_str = '-c {0} -t {1} {2}'.format(self.config_dir, timeout, arg_str) - ret = self.run_script('salt', - arg_str, - with_retcode=with_retcode, - catch_stderr=catch_stderr, - timeout=timeout, - popen_kwargs=popen_kwargs) - log.debug('Result of run_salt for command \'%s\': %s', arg_str, ret) + """ + arg_str = "-c {0} -t {1} {2}".format(self.config_dir, timeout, arg_str) + ret = self.run_script( + "salt", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout, + popen_kwargs=popen_kwargs, + ) + log.debug("Result of run_salt for command '%s': %s", arg_str, ret) return ret - def run_spm(self, arg_str, with_retcode=False, catch_stderr=False, timeout=RUN_TIMEOUT): # pylint: disable=W0221 - ''' + def run_spm( + self, arg_str, with_retcode=False, catch_stderr=False, timeout=RUN_TIMEOUT + ): + """ Execute spm - ''' - ret = self.run_script('spm', - arg_str, - with_retcode=with_retcode, - catch_stderr=catch_stderr, - timeout=timeout) - log.debug('Result of run_spm for command \'%s\': %s', arg_str, ret) + """ + ret = self.run_script( + "spm", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout, + ) + log.debug("Result of run_spm for command '%s': %s", arg_str, ret) return ret - def run_ssh(self, arg_str, with_retcode=False, catch_stderr=False, # pylint: disable=W0221 - timeout=RUN_TIMEOUT, wipe=True, raw=False, **kwargs): - ''' + def run_ssh( + self, + arg_str, + with_retcode=False, + catch_stderr=False, # pylint: disable=W0221 + timeout=RUN_TIMEOUT, + wipe=True, + raw=False, + roster_file=None, + ssh_opts="", + **kwargs + ): + """ Execute salt-ssh - ''' - arg_str = '{0} -ldebug{1} -c {2} -i --priv {3} --roster-file {4} --out=json localhost {5}'.format( - ' -W' if wipe else '', - ' -r' if raw else '', + """ + if not roster_file: + roster_file = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "roster") + + arg_str = "{0} -ldebug{1} -c {2} -i --priv {3} --roster-file {4} {5} --out=json localhost {6}".format( + " -W" if wipe else "", + " -r" if raw else "", self.config_dir, - os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'key_test'), - os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'roster'), - arg_str) - ret = self.run_script('salt-ssh', - arg_str, - with_retcode=with_retcode, - catch_stderr=catch_stderr, - timeout=timeout, - raw=True, - **kwargs) - log.debug('Result of run_ssh for command \'%s %s\': %s', arg_str, kwargs, ret) + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "key_test"), + roster_file, + ssh_opts, + arg_str, + ) + ret = self.run_script( + "salt-ssh", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout, + raw=True, + **kwargs + ) + log.debug("Result of run_ssh for command '%s %s': %s", arg_str, kwargs, ret) return ret - def run_run(self, arg_str, with_retcode=False, catch_stderr=False, - asynchronous=False, timeout=RUN_TIMEOUT, config_dir=None, **kwargs): - ''' + # pylint: enable=arguments-differ + + def run_run( + self, + arg_str, + with_retcode=False, + catch_stderr=False, + asynchronous=False, + timeout=RUN_TIMEOUT, + config_dir=None, + **kwargs + ): + """ Execute salt-run - ''' - asynchronous = kwargs.get('async', asynchronous) - arg_str = '-c {0}{async_flag} -t {timeout} {1}'.format(config_dir or self.config_dir, - arg_str, - timeout=timeout, - async_flag=' --async' if asynchronous else '') - ret = self.run_script('salt-run', - arg_str, - with_retcode=with_retcode, - catch_stderr=catch_stderr, - timeout=timeout + 10) - log.debug('Result of run_run for command \'%s\': %s', arg_str, ret) + """ + asynchronous = kwargs.get("async", asynchronous) + arg_str = "-c {0}{async_flag} -t {timeout} {1}".format( + config_dir or self.config_dir, + arg_str, + timeout=timeout, + async_flag=" --async" if asynchronous else "", + ) + ret = self.run_script( + "salt-run", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout + 10, + ) + log.debug("Result of run_run for command '%s': %s", arg_str, ret) return ret def run_run_plus(self, fun, *arg, **kwargs): - ''' + """ Execute the runner function and return the return data and output in a dict - ''' + """ # Late import import salt.runner import salt.output - ret = {'fun': fun} - from_scratch = bool(kwargs.pop('__reload_config', False)) + + ret = {"fun": fun} + from_scratch = bool(kwargs.pop("__reload_config", False)) # Have to create an empty dict and then update it, as the result from # self.get_config() is an ImmutableDict which cannot be updated. opts = {} - opts.update(self.get_config('client_config', from_scratch=from_scratch)) + opts.update(self.get_config("client_config", from_scratch=from_scratch)) opts_arg = list(arg) if kwargs: - opts_arg.append({'__kwarg__': True}) + opts_arg.append({"__kwarg__": True}) opts_arg[-1].update(kwargs) - opts.update({'doc': False, 'fun': fun, 'arg': opts_arg}) + opts.update({"doc": False, "fun": fun, "arg": opts_arg}) with RedirectStdStreams(): runner = salt.runner.Runner(opts) - ret['return'] = runner.run() + ret["return"] = runner.run() try: - ret['jid'] = runner.jid + ret["jid"] = runner.jid except AttributeError: - ret['jid'] = None + ret["jid"] = None # Compile output # TODO: Support outputters other than nested - opts['color'] = False - opts['output_file'] = cStringIO() + opts["color"] = False + opts["output_file"] = cStringIO() try: - salt.output.display_output(ret['return'], opts=opts) - ret['out'] = opts['output_file'].getvalue().splitlines() + salt.output.display_output(ret["return"], opts=opts) + ret["out"] = opts["output_file"].getvalue().splitlines() finally: - opts['output_file'].close() + opts["output_file"].close() - log.debug('Result of run_run_plus for fun \'%s\' with arg \'%s\': %s', - fun, opts_arg, ret) + log.debug( + "Result of run_run_plus for fun '%s' with arg '%s': %s", fun, opts_arg, ret + ) return ret - def run_key(self, arg_str, catch_stderr=False, with_retcode=False, # pylint: disable=W0221 - timeout=RUN_TIMEOUT): - ''' + # pylint: disable=arguments-differ + def run_key( + self, arg_str, catch_stderr=False, with_retcode=False, timeout=RUN_TIMEOUT, + ): + """ Execute salt-key - ''' - arg_str = '-c {0} {1}'.format(self.config_dir, arg_str) - ret = self.run_script('salt-key', - arg_str, - catch_stderr=catch_stderr, - with_retcode=with_retcode, - timeout=timeout) - log.debug('Result of run_key for command \'%s\': %s', arg_str, ret) + """ + arg_str = "-c {0} {1}".format(self.config_dir, arg_str) + ret = self.run_script( + "salt-key", + arg_str, + catch_stderr=catch_stderr, + with_retcode=with_retcode, + timeout=timeout, + ) + log.debug("Result of run_key for command '%s': %s", arg_str, ret) return ret - def run_cp(self, arg_str, with_retcode=False, catch_stderr=False, # pylint: disable=W0221 - timeout=RUN_TIMEOUT): - ''' + # pylint: disable=arguments-differ + + # pylint: disable=arguments-differ + def run_cp( + self, arg_str, with_retcode=False, catch_stderr=False, timeout=RUN_TIMEOUT, + ): + """ Execute salt-cp - ''' + """ # Note: not logging result of run_cp because it will log a bunch of # bytes which will not be very helpful. - arg_str = '--config-dir {0} {1}'.format(self.config_dir, arg_str) - return self.run_script('salt-cp', - arg_str, - with_retcode=with_retcode, - catch_stderr=catch_stderr, - timeout=timeout) + arg_str = "--config-dir {0} {1}".format(self.config_dir, arg_str) + return self.run_script( + "salt-cp", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout, + ) - def run_call(self, arg_str, with_retcode=False, catch_stderr=False, # pylint: disable=W0221 - local=False, timeout=RUN_TIMEOUT, config_dir=None): - ''' + # pylint: enable=arguments-differ + # pylint: disable=arguments-differ + def run_call( + self, + arg_str, + with_retcode=False, + catch_stderr=False, + local=False, + timeout=RUN_TIMEOUT, + config_dir=None, + ): + """ Execute salt-call. - ''' + """ if not config_dir: config_dir = self.config_dir - arg_str = '{0} --config-dir {1} {2}'.format('--local' if local else '', - config_dir, arg_str) - ret = self.run_script('salt-call', - arg_str, - with_retcode=with_retcode, - catch_stderr=catch_stderr, - timeout=timeout) - log.debug('Result of run_call for command \'%s\': %s', arg_str, ret) + arg_str = "{0} --config-dir {1} {2}".format( + "--local" if local else "", config_dir, arg_str + ) + ret = self.run_script( + "salt-call", + arg_str, + with_retcode=with_retcode, + catch_stderr=catch_stderr, + timeout=timeout, + ) + log.debug("Result of run_call for command '%s': %s", arg_str, ret) return ret + # pylint: enable=arguments-differ + def run_cloud(self, arg_str, catch_stderr=False, timeout=RUN_TIMEOUT): - ''' + """ Execute salt-cloud - ''' - arg_str = '-c {0} {1}'.format(self.config_dir, arg_str) - ret = self.run_script('salt-cloud', - arg_str, - catch_stderr, - timeout=timeout) - log.debug('Result of run_cloud for command \'%s\': %s', arg_str, ret) + """ + arg_str = "-c {0} {1}".format(self.config_dir, arg_str) + ret = self.run_script("salt-cloud", arg_str, catch_stderr, timeout=timeout) + log.debug("Result of run_cloud for command '%s': %s", arg_str, ret) return ret class SPMTestUserInterface(object): - ''' + """ Test user interface to SPMClient - ''' + """ + def __init__(self): self._status = [] self._confirm = [] @@ -634,32 +747,37 @@ class SPMTestUserInterface(object): class SPMCase(TestCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Class for handling spm commands - ''' + """ def _spm_build_files(self, config): - self.formula_dir = os.path.join(' '.join(config['file_roots']['base']), 'formulas') - self.formula_sls_dir = os.path.join(self.formula_dir, 'apache') - self.formula_sls = os.path.join(self.formula_sls_dir, 'apache.sls') - self.formula_file = os.path.join(self.formula_dir, 'FORMULA') + self.formula_dir = os.path.join( + " ".join(config["file_roots"]["base"]), "formulas" + ) + self.formula_sls_dir = os.path.join(self.formula_dir, "apache") + self.formula_sls = os.path.join(self.formula_sls_dir, "apache.sls") + self.formula_file = os.path.join(self.formula_dir, "FORMULA") dirs = [self.formula_dir, self.formula_sls_dir] for f_dir in dirs: os.makedirs(f_dir) - # Late import - import salt.utils.files - - with salt.utils.files.fopen(self.formula_sls, 'w') as fp: - fp.write(textwrap.dedent('''\ + with salt.utils.files.fopen(self.formula_sls, "w") as fp: + fp.write( + textwrap.dedent( + """\ install-apache: pkg.installed: - name: apache2 - ''')) + """ + ) + ) - with salt.utils.files.fopen(self.formula_file, 'w') as fp: - fp.write(textwrap.dedent('''\ + with salt.utils.files.fopen(self.formula_file, "w") as fp: + fp.write( + textwrap.dedent( + """\ name: apache os: RedHat, Debian, Ubuntu, Suse, FreeBSD os_family: RedHat, Debian, Suse, FreeBSD @@ -667,67 +785,74 @@ class SPMCase(TestCase, AdaptedConfigurationTestCaseMixin): release: 2 summary: Formula for installing Apache description: Formula for installing Apache - ''')) + """ + ) + ) def _spm_config(self, assume_yes=True): self._tmp_spm = tempfile.mkdtemp() - config = self.get_temp_config('minion', **{ - 'spm_logfile': os.path.join(self._tmp_spm, 'log'), - 'spm_repos_config': os.path.join(self._tmp_spm, 'etc', 'spm.repos'), - 'spm_cache_dir': os.path.join(self._tmp_spm, 'cache'), - 'spm_build_dir': os.path.join(self._tmp_spm, 'build'), - 'spm_build_exclude': ['apache/.git'], - 'spm_db_provider': 'sqlite3', - 'spm_files_provider': 'local', - 'spm_db': os.path.join(self._tmp_spm, 'packages.db'), - 'extension_modules': os.path.join(self._tmp_spm, 'modules'), - 'file_roots': {'base': [self._tmp_spm, ]}, - 'formula_path': os.path.join(self._tmp_spm, 'salt'), - 'pillar_path': os.path.join(self._tmp_spm, 'pillar'), - 'reactor_path': os.path.join(self._tmp_spm, 'reactor'), - 'assume_yes': True if assume_yes else False, - 'force': False, - 'verbose': False, - 'cache': 'localfs', - 'cachedir': os.path.join(self._tmp_spm, 'cache'), - 'spm_repo_dups': 'ignore', - 'spm_share_dir': os.path.join(self._tmp_spm, 'share'), - }) + config = self.get_temp_config( + "minion", + **{ + "spm_logfile": os.path.join(self._tmp_spm, "log"), + "spm_repos_config": os.path.join(self._tmp_spm, "etc", "spm.repos"), + "spm_cache_dir": os.path.join(self._tmp_spm, "cache"), + "spm_build_dir": os.path.join(self._tmp_spm, "build"), + "spm_build_exclude": ["apache/.git"], + "spm_db_provider": "sqlite3", + "spm_files_provider": "local", + "spm_db": os.path.join(self._tmp_spm, "packages.db"), + "extension_modules": os.path.join(self._tmp_spm, "modules"), + "file_roots": {"base": [self._tmp_spm]}, + "formula_path": os.path.join(self._tmp_spm, "salt"), + "pillar_path": os.path.join(self._tmp_spm, "pillar"), + "reactor_path": os.path.join(self._tmp_spm, "reactor"), + "assume_yes": True if assume_yes else False, + "force": False, + "verbose": False, + "cache": "localfs", + "cachedir": os.path.join(self._tmp_spm, "cache"), + "spm_repo_dups": "ignore", + "spm_share_dir": os.path.join(self._tmp_spm, "share"), + } + ) - import salt.utils.files import salt.utils.yaml - if not os.path.isdir(config['formula_path']): - os.makedirs(config['formula_path']) + if not os.path.isdir(config["formula_path"]): + os.makedirs(config["formula_path"]) - with salt.utils.files.fopen(os.path.join(self._tmp_spm, 'spm'), 'w') as fp: + with salt.utils.files.fopen(os.path.join(self._tmp_spm, "spm"), "w") as fp: salt.utils.yaml.safe_dump(config, fp) return config def _spm_create_update_repo(self, config): - build_spm = self.run_spm('build', self.config, self.formula_dir) + build_spm = self.run_spm("build", self.config, self.formula_dir) - c_repo = self.run_spm('create_repo', self.config, - self.config['spm_build_dir']) + c_repo = self.run_spm("create_repo", self.config, self.config["spm_build_dir"]) - repo_conf_dir = self.config['spm_repos_config'] + '.d' + repo_conf_dir = self.config["spm_repos_config"] + ".d" os.makedirs(repo_conf_dir) - # Late import - import salt.utils.files - - with salt.utils.files.fopen(os.path.join(repo_conf_dir, 'spm.repo'), 'w') as fp: - fp.write(textwrap.dedent('''\ + with salt.utils.files.fopen(os.path.join(repo_conf_dir, "spm.repo"), "w") as fp: + fp.write( + textwrap.dedent( + """\ local_repo: url: file://{0} - '''.format(self.config['spm_build_dir']))) + """.format( + self.config["spm_build_dir"] + ) + ) + ) - u_repo = self.run_spm('update_repo', self.config) + u_repo = self.run_spm("update_repo", self.config) def _spm_client(self, config): import salt.spm + self.ui = SPMTestUserInterface() client = salt.spm.SPMClient(self.ui, config) return client @@ -740,53 +865,59 @@ class SPMCase(TestCase, AdaptedConfigurationTestCaseMixin): class ModuleCase(TestCase, SaltClientTestCaseMixin): - ''' + """ Execute a module function - ''' + """ - def wait_for_all_jobs(self, minions=('minion', 'sub_minion',), sleep=.3): - ''' + def wait_for_all_jobs(self, minions=("minion", "sub_minion",), sleep=0.3): + """ Wait for all jobs currently running on the list of minions to finish - ''' + """ for minion in minions: while True: - ret = self.run_function('saltutil.running', minion_tgt=minion, timeout=300) + ret = self.run_function( + "saltutil.running", minion_tgt=minion, timeout=300 + ) if ret: - log.debug('Waiting for minion\'s jobs: %s', minion) + log.debug("Waiting for minion's jobs: %s", minion) time.sleep(sleep) else: break def minion_run(self, _function, *args, **kw): - ''' + """ Run a single salt function on the 'minion' target and condition the return down to match the behavior of the raw function call - ''' + """ return self.run_function(_function, args, **kw) - def run_function(self, function, arg=(), minion_tgt='minion', timeout=300, master_tgt=None, **kwargs): - ''' + def run_function( + self, + function, + arg=(), + minion_tgt="minion", + timeout=300, + master_tgt=None, + **kwargs + ): + """ Run a single salt function and condition the return down to match the behavior of the raw function call - ''' + """ known_to_return_none = ( - 'data.get', - 'file.chown', - 'file.chgrp', - 'pkg.refresh_db', - 'ssh.recv_known_host_entries', - 'time.sleep' + "data.get", + "file.chown", + "file.chgrp", + "pkg.refresh_db", + "ssh.recv_known_host_entries", + "time.sleep", ) - if 'f_arg' in kwargs: - kwargs['arg'] = kwargs.pop('f_arg') - if 'f_timeout' in kwargs: - kwargs['timeout'] = kwargs.pop('f_timeout') + if "f_arg" in kwargs: + kwargs["arg"] = kwargs.pop("f_arg") + if "f_timeout" in kwargs: + kwargs["timeout"] = kwargs.pop("f_timeout") client = self.client if master_tgt is None else self.clients[master_tgt] - orig = client.cmd(minion_tgt, - function, - arg, - timeout=timeout, - kwarg=kwargs) + orig = client.cmd(minion_tgt, function, arg, timeout=timeout, kwarg=kwargs) if RUNTIME_VARS.PYTEST_SESSION: fail_or_skip_func = self.fail @@ -795,15 +926,13 @@ class ModuleCase(TestCase, SaltClientTestCaseMixin): if minion_tgt not in orig: fail_or_skip_func( - 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get a reply ' - 'from the minion \'{0}\'. Command output: {1}'.format( - minion_tgt, orig - ) + "WARNING(SHOULD NOT HAPPEN #1935): Failed to get a reply " + "from the minion '{0}'. Command output: {1}".format(minion_tgt, orig) ) elif orig[minion_tgt] is None and function not in known_to_return_none: fail_or_skip_func( - 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get \'{0}\' from ' - 'the minion \'{1}\'. Command output: {2}'.format( + "WARNING(SHOULD NOT HAPPEN #1935): Failed to get '{0}' from " + "the minion '{1}'. Command output: {2}".format( function, minion_tgt, orig ) ) @@ -814,10 +943,10 @@ class ModuleCase(TestCase, SaltClientTestCaseMixin): return orig[minion_tgt] def run_state(self, function, **kwargs): - ''' + """ Run the state.single command and return the state return structure - ''' - ret = self.run_function('state.single', [function], **kwargs) + """ + ret = self.run_function("state.single", [function], **kwargs) return self._check_state_return(ret) def _check_state_return(self, ret): @@ -836,60 +965,65 @@ class ModuleCase(TestCase, SaltClientTestCaseMixin): if not match: # We don't know how to handle this continue - jid = match.group('jid') + jid = match.group("jid") if jid in jids: continue jids.append(jid) - job_data = self.run_function('saltutil.find_job', [jid]) - job_kill = self.run_function('saltutil.kill_job', [jid]) + job_data = self.run_function("saltutil.find_job", [jid]) + job_kill = self.run_function("saltutil.kill_job", [jid]) msg = ( - 'A running state.single was found causing a state lock. ' - 'Job details: \'{0}\' Killing Job Returned: \'{1}\''.format( + "A running state.single was found causing a state lock. " + "Job details: '{0}' Killing Job Returned: '{1}'".format( job_data, job_kill ) ) - ret.append('[TEST SUITE ENFORCED]{0}' - '[/TEST SUITE ENFORCED]'.format(msg)) + ret.append( + "[TEST SUITE ENFORCED]{0}" "[/TEST SUITE ENFORCED]".format(msg) + ) return ret class MultimasterModuleCase(ModuleCase, SaltMultimasterClientTestCaseMixin): - ''' + """ Execute a module function - ''' + """ - def run_function(self, function, arg=(), minion_tgt='mm-minion', timeout=300, master_tgt='mm-master', **kwargs): - ''' + def run_function( + self, + function, + arg=(), + minion_tgt="mm-minion", + timeout=300, + master_tgt="mm-master", + **kwargs + ): + """ Run a single salt function and condition the return down to match the behavior of the raw function call - ''' + """ known_to_return_none = ( - 'data.get', - 'file.chown', - 'file.chgrp', - 'pkg.refresh_db', - 'ssh.recv_known_host_entries', - 'time.sleep' + "data.get", + "file.chown", + "file.chgrp", + "pkg.refresh_db", + "ssh.recv_known_host_entries", + "time.sleep", ) - if minion_tgt == 'mm-sub-minion': - known_to_return_none += ('mine.update',) - if 'f_arg' in kwargs: - kwargs['arg'] = kwargs.pop('f_arg') - if 'f_timeout' in kwargs: - kwargs['timeout'] = kwargs.pop('f_timeout') + if minion_tgt == "mm-sub-minion": + known_to_return_none += ("mine.update",) + if "f_arg" in kwargs: + kwargs["arg"] = kwargs.pop("f_arg") + if "f_timeout" in kwargs: + kwargs["timeout"] = kwargs.pop("f_timeout") if master_tgt is None: - client = self.clients['mm-master'] + client = self.clients["mm-master"] elif isinstance(master_tgt, int): client = self.clients[list(self.clients)[master_tgt]] else: client = self.clients[master_tgt] - orig = client.cmd(minion_tgt, - function, - arg, - timeout=timeout, - kwarg=kwargs) + orig = client.cmd(minion_tgt, function, arg, timeout=timeout, kwarg=kwargs) if RUNTIME_VARS.PYTEST_SESSION: fail_or_skip_func = self.fail @@ -898,15 +1032,13 @@ class MultimasterModuleCase(ModuleCase, SaltMultimasterClientTestCaseMixin): if minion_tgt not in orig: fail_or_skip_func( - 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get a reply ' - 'from the minion \'{0}\'. Command output: {1}'.format( - minion_tgt, orig - ) + "WARNING(SHOULD NOT HAPPEN #1935): Failed to get a reply " + "from the minion '{0}'. Command output: {1}".format(minion_tgt, orig) ) elif orig[minion_tgt] is None and function not in known_to_return_none: fail_or_skip_func( - 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get \'{0}\' from ' - 'the minion \'{1}\'. Command output: {2}'.format( + "WARNING(SHOULD NOT HAPPEN #1935): Failed to get '{0}' from " + "the minion '{1}'. Command output: {2}".format( function, minion_tgt, orig ) ) @@ -916,83 +1048,113 @@ class MultimasterModuleCase(ModuleCase, SaltMultimasterClientTestCaseMixin): return orig[minion_tgt] - def run_function_all_masters(self, function, arg=(), minion_tgt='mm-minion', timeout=300, **kwargs): - ''' + def run_function_all_masters( + self, function, arg=(), minion_tgt="mm-minion", timeout=300, **kwargs + ): + """ Run a single salt function from all the masters in multimaster environment and condition the return down to match the behavior of the raw function call - ''' + """ ret = [] for master_id in self.clients: ret.append( - self.run_function(function, - arg=arg, - minion_tgt=minion_tgt, - timeout=timeout, - master_tgt=master_id, - **kwargs)) + self.run_function( + function, + arg=arg, + minion_tgt=minion_tgt, + timeout=timeout, + master_tgt=master_id, + **kwargs + ) + ) return ret class SyndicCase(TestCase, SaltClientTestCaseMixin): - ''' + """ Execute a syndic based execution test - ''' - _salt_client_config_file_name_ = 'syndic_master' + """ + + _salt_client_config_file_name_ = "syndic_master" def run_function(self, function, arg=(), timeout=90): - ''' + """ Run a single salt function and condition the return down to match the behavior of the raw function call - ''' - orig = self.client.cmd('minion', function, arg, timeout=timeout) + """ + orig = self.client.cmd("minion", function, arg, timeout=timeout) if RUNTIME_VARS.PYTEST_SESSION: fail_or_skip_func = self.fail else: fail_or_skip_func = self.skipTest - if 'minion' not in orig: + if "minion" not in orig: fail_or_skip_func( - 'WARNING(SHOULD NOT HAPPEN #1935): Failed to get a reply ' - 'from the minion. Command output: {0}'.format(orig) + "WARNING(SHOULD NOT HAPPEN #1935): Failed to get a reply " + "from the minion. Command output: {0}".format(orig) ) - return orig['minion'] + return orig["minion"] @requires_sshd_server class SSHCase(ShellCase): - ''' + """ Execute a command via salt-ssh - ''' - def _arg_str(self, function, arg): - return '{0} {1}'.format(function, ' '.join(arg)) + """ - def run_function(self, function, arg=(), timeout=180, wipe=True, raw=False, **kwargs): - ''' + def _arg_str(self, function, arg): + return "{0} {1}".format(function, " ".join(arg)) + + def run_function( + self, function, arg=(), timeout=180, wipe=True, raw=False, **kwargs + ): + """ We use a 180s timeout here, which some slower systems do end up needing - ''' - ret = self.run_ssh(self._arg_str(function, arg), timeout=timeout, - wipe=wipe, raw=raw, **kwargs) - log.debug('SSHCase run_function executed %s with arg %s and kwargs %s', function, arg, kwargs) - log.debug('SSHCase JSON return: %s', ret) + """ + ret = self.run_ssh( + self._arg_str(function, arg), timeout=timeout, wipe=wipe, raw=raw, **kwargs + ) + log.debug( + "SSHCase run_function executed %s with arg %s and kwargs %s", + function, + arg, + kwargs, + ) + log.debug("SSHCase JSON return: %s", ret) # Late import import salt.utils.json try: - return salt.utils.json.loads(ret)['localhost'] + return salt.utils.json.loads(ret)["localhost"] except Exception: # pylint: disable=broad-except return ret + def custom_roster(self, new_roster, data): + """ + helper method to create a custom roster to use for a ssh test + """ + roster = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "roster") + + with salt.utils.files.fopen(roster, "r") as fp_: + conf = salt.utils.yaml.safe_load(fp_) + + conf["localhost"].update(data) + + with salt.utils.files.fopen(new_roster, "w") as fp_: + salt.utils.yaml.safe_dump(conf, fp_) + class ClientCase(AdaptedConfigurationTestCaseMixin, TestCase): - ''' + """ A base class containing relevant options for starting the various Salt Python API entrypoints - ''' + """ + def get_opts(self): # Late import import salt.config - return salt.config.client_config(self.get_config_file_path('master')) + return salt.config.client_config(self.get_config_file_path("master")) def mkdir_p(self, path): try: @@ -1004,6 +1166,4 @@ class ClientCase(AdaptedConfigurationTestCaseMixin, TestCase): raise -# ----- Backwards Compatible Imports --------------------------------------------------------------------------------> -from tests.support.mixins import ShellCaseCommonTestsMixin # pylint: disable=unused-import # <---- Backwards Compatible Imports --------------------------------------------------------------------------------- diff --git a/tests/support/cherrypy_testclasses.py b/tests/support/cherrypy_testclasses.py index 537c610b4e8..d557eaae8c8 100644 --- a/tests/support/cherrypy_testclasses.py +++ b/tests/support/cherrypy_testclasses.py @@ -1,25 +1,27 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -try: - import cherrypy - HAS_CHERRYPY = True -except ImportError: - HAS_CHERRYPY = False - import os import salt.config from tests.support.mock import patch from tests.support.runtests import RUNTIME_VARS +try: + import cherrypy + + HAS_CHERRYPY = True +except ImportError: + HAS_CHERRYPY = False + + if HAS_CHERRYPY: from tests.support.cptestcase import BaseCherryPyTestCase from salt.netapi.rest_cherrypy import app else: from tests.support.unit import TestCase, skipIf - @skipIf(HAS_CHERRYPY is False, 'The CherryPy python package needs to be installed') + @skipIf(HAS_CHERRYPY is False, "The CherryPy python package needs to be installed") class BaseCherryPyTestCase(TestCase): pass @@ -28,18 +30,19 @@ else: class BaseRestCherryPyTest(BaseCherryPyTestCase): - ''' + """ A base TestCase subclass for the rest_cherrypy module This mocks all interactions with Salt-core and sets up a dummy (unsubscribed) CherryPy web server. - ''' + """ + def __get_opts__(self): return None @classmethod def setUpClass(cls): - master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master') + master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master") cls.config = salt.config.client_config(master_conf) cls.base_opts = {} cls.base_opts.update(cls.config) @@ -52,39 +55,26 @@ class BaseRestCherryPyTest(BaseCherryPyTestCase): def setUp(self): # Make a local reference to the CherryPy app so we can mock attributes. self.app = app - self.addCleanup(delattr, self, 'app') + self.addCleanup(delattr, self, "app") - master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master') + master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master") client_config = salt.config.client_config(master_conf) base_opts = {} base_opts.update(client_config) - base_opts.update(self.__get_opts__() or { - 'external_auth': { - 'auto': { - 'saltdev': [ - '@wheel', - '@runner', - '.*', - ], - }, - 'pam': { - 'saltdev': [ - '@wheel', - '@runner', - '.*', - ], - - } - }, - 'rest_cherrypy': { - 'port': 8000, - 'debug': True, - }, - }) + base_opts.update( + self.__get_opts__() + or { + "external_auth": { + "auto": {"saltdev": ["@wheel", "@runner", ".*"]}, + "pam": {"saltdev": ["@wheel", "@runner", ".*"]}, + }, + "rest_cherrypy": {"port": 8000, "debug": True}, + } + ) root, apiopts, conf = app.get_app(base_opts) - cherrypy.tree.mount(root, '/', conf) + cherrypy.tree.mount(root, "/", conf) cherrypy.server.unsubscribe() cherrypy.engine.start() @@ -99,30 +89,31 @@ class BaseRestCherryPyTest(BaseCherryPyTestCase): class Root(object): - ''' + """ The simplest CherryPy app needed to test individual tools - ''' + """ + exposed = True _cp_config = {} def GET(self): - return {'return': ['Hello world.']} + return {"return": ["Hello world."]} def POST(self, *args, **kwargs): - return {'return': [{'args': args}, {'kwargs': kwargs}]} + return {"return": [{"args": args}, {"kwargs": kwargs}]} if HAS_CHERRYPY: + class BaseToolsTest(BaseCherryPyTestCase): # pylint: disable=E0102 - ''' + """ A base class so tests can selectively turn individual tools on for testing - ''' + """ + def __get_conf__(self): return { - '/': { - 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), - }, + "/": {"request.dispatch": cherrypy.dispatch.MethodDispatcher()}, } def __get_cp_config__(self): @@ -130,7 +121,7 @@ if HAS_CHERRYPY: def setUp(self): root = Root() - patcher = patch.object(root, '_cp_config', self.__get_cp_config__()) + patcher = patch.object(root, "_cp_config", self.__get_cp_config__()) patcher.start() self.addCleanup(patcher.stop) @@ -141,7 +132,7 @@ if HAS_CHERRYPY: value.clear() cherrypy.engine._priorities.clear() - app = cherrypy.tree.mount(root, '/', self.__get_conf__()) + app = cherrypy.tree.mount(root, "/", self.__get_conf__()) cherrypy.server.unsubscribe() cherrypy.engine.start() self.addCleanup(cherrypy.engine.exit) diff --git a/tests/support/cli_scripts.py b/tests/support/cli_scripts.py index 774be16c243..fcab3d9e33f 100644 --- a/tests/support/cli_scripts.py +++ b/tests/support/cli_scripts.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" tests.support.cli_scripts ~~~~~~~~~~~~~~~~~~~~~~~~~ Code to generate Salt CLI scripts for test runs -''' +""" # Import Python Libs from __future__ import absolute_import, unicode_literals + +import logging import os import sys -import logging # Import Pytest Salt libs from pytestsalt.utils import cli_scripts @@ -19,16 +20,16 @@ log = logging.getLogger(__name__) def get_script_path(bin_dir, script_name): - ''' + """ Return the path to a testing runtime script, generating one if it does not yet exist - ''' + """ # Late import from tests.support.runtests import RUNTIME_VARS if not os.path.isdir(bin_dir): os.makedirs(bin_dir) - cli_script_name = 'cli_{}.py'.format(script_name.replace('-', '_')) + cli_script_name = "cli_{}.py".format(script_name.replace("-", "_")) script_path = os.path.join(bin_dir, cli_script_name) if not os.path.isfile(script_path): @@ -37,18 +38,18 @@ def get_script_path(bin_dir, script_name): script_name=script_name, executable=sys.executable, code_dir=RUNTIME_VARS.CODE_DIR, - inject_sitecustomize='COVERAGE_PROCESS_START' in os.environ + inject_sitecustomize="COVERAGE_PROCESS_START" in os.environ, ) - log.info('Returning script path %r', script_path) + log.info("Returning script path %r", script_path) return script_path class ScriptPathMixin(object): - def get_script_path(self, script_name): - ''' + """ Return the path to a testing runtime script - ''' + """ # Late import from tests.support.runtests import RUNTIME_VARS + return get_script_path(RUNTIME_VARS.TMP_SCRIPT_DIR, script_name) diff --git a/tests/support/copyartifacts.py b/tests/support/copyartifacts.py index 31533a48d93..bf6f315956e 100644 --- a/tests/support/copyartifacts.py +++ b/tests/support/copyartifacts.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" Script for copying back xml junit files from tests -''' +""" from __future__ import absolute_import, print_function + import argparse # pylint: disable=minimum-python-version import os -import paramiko import subprocess +import paramiko import salt.utils.yaml @@ -20,62 +21,69 @@ class DownloadArtifacts(object): def setup_transport(self): # pylint: disable=minimum-python-version - config = salt.utils.yaml.safe_load(subprocess.check_output(['bundle', 'exec', 'kitchen', 'diagnose', self.instance])) + config = salt.utils.yaml.safe_load( + subprocess.check_output( + ["bundle", "exec", "kitchen", "diagnose", self.instance] + ) + ) # pylint: enable=minimum-python-version - state = config['instances'][self.instance]['state_file'] - tport = config['instances'][self.instance]['transport'] - transport = paramiko.Transport(( - state['hostname'], - state.get('port', tport.get('port', 22)) - )) + state = config["instances"][self.instance]["state_file"] + tport = config["instances"][self.instance]["transport"] + transport = paramiko.Transport( + (state["hostname"], state.get("port", tport.get("port", 22))) + ) pkey = paramiko.rsakey.RSAKey( - filename=state.get('ssh_key', tport.get('ssh_key', '~/.ssh/id_rsa')) + filename=state.get("ssh_key", tport.get("ssh_key", "~/.ssh/id_rsa")) ) transport.connect( - username=state.get('username', tport.get('username', 'root')), - pkey=pkey + username=state.get("username", tport.get("username", "root")), pkey=pkey ) return transport def _set_permissions(self): - ''' + """ Make sure all xml files are readable by the world so that anyone can grab them - ''' + """ for remote, _ in self.artifacts: - self.transport.open_session().exec_command('sudo chmod -R +r {}'.format(remote)) + self.transport.open_session().exec_command( + "sudo chmod -R +r {}".format(remote) + ) def download(self): self._set_permissions() for remote, local in self.artifacts: - if remote.endswith('/'): + if remote.endswith("/"): for fxml in self.sftpclient.listdir(remote): - self._do_download(os.path.join(remote, fxml), os.path.join(local, os.path.basename(fxml))) + self._do_download( + os.path.join(remote, fxml), + os.path.join(local, os.path.basename(fxml)), + ) else: self._do_download(remote, os.path.join(local, os.path.basename(remote))) def _do_download(self, remote, local): - print('Copying from {0} to {1}'.format(remote, local)) + print("Copying from {0} to {1}".format(remote, local)) try: self.sftpclient.get(remote, local) except IOError: - print('Failed to copy: {0}'.format(remote)) + print("Failed to copy: {0}".format(remote)) -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Jenkins Artifact Download Helper') +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Jenkins Artifact Download Helper") parser.add_argument( - '--instance', + "--instance", required=True, - action='store', - help='Instance on Test Kitchen to pull from', + action="store", + help="Instance on Test Kitchen to pull from", ) parser.add_argument( - '--download-artifacts', - dest='artifacts', + "--download-artifacts", + dest="artifacts", nargs=2, - action='append', - metavar=('REMOTE_PATH', 'LOCAL_PATH'), - help='Download remote artifacts', + action="append", + metavar=("REMOTE_PATH", "LOCAL_PATH"), + help="Download remote artifacts", ) args = parser.parse_args() downloader = DownloadArtifacts(args.instance, args.artifacts) diff --git a/tests/support/coverage/sitecustomize.py b/tests/support/coverage/sitecustomize.py index 76f7babf5f9..2353b41e554 100644 --- a/tests/support/coverage/sitecustomize.py +++ b/tests/support/coverage/sitecustomize.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" Python will always try to import sitecustomize. We use that fact to try and support code coverage for sub-processes -''' +""" from __future__ import absolute_import + try: import coverage + coverage.process_startup() except ImportError: pass diff --git a/tests/support/cptestcase.py b/tests/support/cptestcase.py index 8f331d752d2..1c472b91e69 100644 --- a/tests/support/cptestcase.py +++ b/tests/support/cptestcase.py @@ -31,20 +31,21 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.case import TestCase - # Import 3rd-party libs # pylint: disable=import-error import cherrypy # pylint: disable=3rd-party-module-not-gated +import salt.utils.stringutils from salt.ext import six from salt.ext.six import BytesIO + +# Import Salt Testing libs +from tests.support.case import TestCase + # pylint: enable=import-error -import salt.utils.stringutils # Not strictly speaking mandatory but just makes sense -cherrypy.config.update({'environment': "test_suite"}) +cherrypy.config.update({"environment": "test_suite"}) # This is mandatory so that the HTTP server isn't started # if you need to actually start (why would you?), simply @@ -52,15 +53,25 @@ cherrypy.config.update({'environment': "test_suite"}) cherrypy.server.unsubscribe() # simulate fake socket address... they are irrelevant in our context -local = cherrypy.lib.httputil.Host('127.0.0.1', 50000, "") -remote = cherrypy.lib.httputil.Host('127.0.0.1', 50001, "") +local = cherrypy.lib.httputil.Host("127.0.0.1", 50000, "") +remote = cherrypy.lib.httputil.Host("127.0.0.1", 50001, "") -__all__ = ['BaseCherryPyTestCase'] +__all__ = ["BaseCherryPyTestCase"] class BaseCherryPyTestCase(TestCase): - def request(self, path='/', method='GET', app_path='', scheme='http', - proto='HTTP/1.1', body=None, qs=None, headers=None, **kwargs): + def request( + self, + path="/", + method="GET", + app_path="", + scheme="http", + proto="HTTP/1.1", + body=None, + qs=None, + headers=None, + **kwargs + ): """ CherryPy does not have a facility for serverless unit testing. However this recipe demonstrates a way of doing it by @@ -87,13 +98,13 @@ class BaseCherryPyTestCase(TestCase): .. seealso: http://docs.cherrypy.org/stable/refman/_cprequest.html#cherrypy._cprequest.Response """ # This is a required header when running HTTP/1.1 - h = {'Host': '127.0.0.1'} + h = {"Host": "127.0.0.1"} # if we had some data passed as the request entity # let's make sure we have the content-length set fd = None if body is not None: - h['content-length'] = '{0}'.format(len(body)) + h["content-length"] = "{0}".format(len(body)) fd = BytesIO(salt.utils.stringutils.to_bytes(body)) if headers is not None: @@ -119,7 +130,7 @@ class BaseCherryPyTestCase(TestCase): fd.close() fd = None - if response.output_status.startswith(b'500'): + if response.output_status.startswith(b"500"): response_body = response.collapse_body() if six.PY3: response_body = response_body.decode(__salt_system_encoding__) diff --git a/tests/support/docker.py b/tests/support/docker.py index 11182f17c3d..37e351427a1 100644 --- a/tests/support/docker.py +++ b/tests/support/docker.py @@ -1,22 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" Common code used in Docker integration tests -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import functools import random import string -# Import Salt libs -from salt.exceptions import CommandExecutionError - # Import 3rd-party libs from salt._compat import ipaddress + +# Import Salt libs +from salt.exceptions import CommandExecutionError from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -def random_name(prefix=''): +def random_name(prefix=""): ret = prefix for _ in range(8): ret += random.choice(string.ascii_lowercase) @@ -28,11 +29,11 @@ class Network(object): self.kwargs = kwargs self.name = name try: - self.net = ipaddress.ip_network(self.kwargs['subnet']) + self.net = ipaddress.ip_network(self.kwargs["subnet"]) self._rand_indexes = random.sample( - range(2, self.net.num_addresses - 1), - self.net.num_addresses - 3) - self.ip_arg = 'ipv{0}_address'.format(self.net.version) + range(2, self.net.num_addresses - 1), self.net.num_addresses - 3 + ) + self.ip_arg = "ipv{0}_address".format(self.net.version) except KeyError: # No explicit subnet passed self.net = self.ip_arg = None @@ -42,13 +43,16 @@ class Network(object): return self.net[self._rand_indexes[index]].compressed except (TypeError, AttributeError): raise ValueError( - 'Indexing not supported for networks without a custom subnet') + "Indexing not supported for networks without a custom subnet" + ) def arg_map(self, arg_name): - return {'ipv4_address': 'IPv4Address', - 'ipv6_address': 'IPv6Address', - 'links': 'Links', - 'aliases': 'Aliases'}[arg_name] + return { + "ipv4_address": "IPv4Address", + "ipv6_address": "IPv6Address", + "links": "Links", + "aliases": "Aliases", + }[arg_name] @property def subnet(self): @@ -60,7 +64,7 @@ class Network(object): @property def gateway(self): try: - return self.kwargs['gateway'] + return self.kwargs["gateway"] except KeyError: try: return self.net[1].compressed @@ -69,41 +73,41 @@ class Network(object): class with_network(object): - ''' + """ Generate a network for the test. Information about the network will be passed to the wrapped function. - ''' + """ + def __init__(self, **kwargs): - self.create = kwargs.pop('create', False) - self.network = Network(random_name(prefix='salt_net_'), **kwargs) + self.create = kwargs.pop("create", False) + self.network = Network(random_name(prefix="salt_net_"), **kwargs) if self.network.net is not None: - if 'enable_ipv6' not in kwargs: - kwargs['enable_ipv6'] = self.network.net.version == 6 + if "enable_ipv6" not in kwargs: + kwargs["enable_ipv6"] = self.network.net.version == 6 self.kwargs = kwargs def __call__(self, func): self.func = func return functools.wraps(func)( - lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) # pylint: disable=W0108 + # pylint: disable=W0108 + lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) + # pylint: enable=W0108 ) def wrap(self, testcase, *args, **kwargs): if self.create: testcase.run_function( - 'docker.create_network', - [self.network.name], - **self.kwargs) + "docker.create_network", [self.network.name], **self.kwargs + ) try: return self.func(testcase, self.network, *args, **kwargs) finally: try: testcase.run_function( - 'docker.disconnect_all_containers_from_network', - [self.network.name]) + "docker.disconnect_all_containers_from_network", [self.network.name] + ) except CommandExecutionError as exc: - if '404' not in exc.__str__(): + if "404" not in exc.__str__(): raise else: - testcase.run_function( - 'docker.remove_network', - [self.network.name]) + testcase.run_function("docker.remove_network", [self.network.name]) diff --git a/tests/support/events.py b/tests/support/events.py index f6e09be8f08..5d4bed19a41 100644 --- a/tests/support/events.py +++ b/tests/support/events.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" tests.support.events ~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + +import multiprocessing import os import time -import multiprocessing from contextlib import contextmanager # Import Salt libs @@ -18,10 +19,10 @@ from salt.utils.process import clean_proc @contextmanager def eventpublisher_process(sock_dir): - proc = salt.utils.event.EventPublisher({'sock_dir': sock_dir}) + proc = salt.utils.event.EventPublisher({"sock_dir": sock_dir}) proc.start() try: - if os.environ.get('TRAVIS_PYTHON_VERSION', None) is not None: + if os.environ.get("TRAVIS_PYTHON_VERSION", None) is not None: # Travis is slow time.sleep(10) else: @@ -44,7 +45,7 @@ class EventSender(multiprocessing.Process): time.sleep(self.wait) me.fire_event(self.data, self.tag) # Wait a few seconds before tearing down the zmq context - if os.environ.get('TRAVIS_PYTHON_VERSION', None) is not None: + if os.environ.get("TRAVIS_PYTHON_VERSION", None) is not None: # Travis is slow time.sleep(10) else: diff --git a/tests/support/ext/console.py b/tests/support/ext/console.py index 46ba195aabe..888f0521fcc 100644 --- a/tests/support/ext/console.py +++ b/tests/support/ext/console.py @@ -1,38 +1,42 @@ # -*- coding: utf-8 -*- # vim: sw=4 ts=4 fenc=utf-8 -''' +""" getTerminalSize() - get width and height of console - works on linux,os x,windows,cygwin(windows) - taken from http://stackoverflow.com/questions/566746/how-to-get-console-window-width-in-python -''' +""" # Import python libs from __future__ import absolute_import, print_function + +import ctypes +import fcntl import os import platform import struct -import ctypes import subprocess -import fcntl import termios -__all__ = ['getTerminalSize'] +__all__ = ["getTerminalSize"] def getTerminalSize(): current_os = platform.system() tuple_xy = None - if current_os == 'Windows': + if current_os == "Windows": tuple_xy = _getTerminalSize_windows() if tuple_xy is None: tuple_xy = _getTerminalSize_tput() # needed for window's python in cygwin's xterm! - if current_os == 'Linux' or current_os == 'Darwin' or \ - current_os.startswith('CYGWIN'): + if ( + current_os == "Linux" + or current_os == "Darwin" + or current_os.startswith("CYGWIN") + ): tuple_xy = _getTerminalSize_linux() if tuple_xy is None: - tuple_xy = (80, 25) # default value + tuple_xy = (80, 25) # default value return tuple_xy @@ -49,9 +53,19 @@ def _getTerminalSize_windows(): except Exception: # pylint: disable=broad-except return None if res: - (bufx, bufy, curx, cury, wattr, - left, top, right, bottom, maxx, maxy) = struct.unpack( - b'hhhhHhhhhhh', csbi.raw) + ( + bufx, + bufy, + curx, + cury, + wattr, + left, + top, + right, + bottom, + maxx, + maxy, + ) = struct.unpack(b"hhhhHhhhhhh", csbi.raw) sizex = right - left + 1 sizey = bottom - top + 1 return sizex, sizey @@ -64,12 +78,12 @@ def _getTerminalSize_tput(): # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window try: proc = subprocess.Popen( - ['tput', 'cols'], stdin=subprocess.PIPE, stdout=subprocess.PIPE + ["tput", "cols"], stdin=subprocess.PIPE, stdout=subprocess.PIPE ) output = proc.communicate(input=None) cols = int(output[0]) proc = subprocess.Popen( - ['tput', 'lines'], stdin=subprocess.PIPE, stdout=subprocess.PIPE + ["tput", "lines"], stdin=subprocess.PIPE, stdout=subprocess.PIPE ) output = proc.communicate(input=None) rows = int(output[0]) @@ -81,12 +95,11 @@ def _getTerminalSize_tput(): def _getTerminalSize_linux(): def ioctl_GWINSZ(fd): try: - cr = struct.unpack( - b'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234') - ) + cr = struct.unpack(b"hh", fcntl.ioctl(fd, termios.TIOCGWINSZ, "1234")) except Exception: # pylint: disable=broad-except return None return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) if not cr: try: @@ -97,12 +110,12 @@ def _getTerminalSize_linux(): pass if not cr: try: - cr = (os.environ['LINES'], os.environ['COLUMNS']) + cr = (os.environ["LINES"], os.environ["COLUMNS"]) except Exception: # pylint: disable=broad-except return None return int(cr[1]), int(cr[0]) -if __name__ == '__main__': +if __name__ == "__main__": sizex, sizey = getTerminalSize() - print('width = {0} height = {1}'.format(sizex, sizey)) + print("width = {0} height = {1}".format(sizex, sizey)) diff --git a/tests/support/generate-names-file-from-failed-test-reports.py b/tests/support/generate-names-file-from-failed-test-reports.py index a6c15bcd9e3..480485db074 100644 --- a/tests/support/generate-names-file-from-failed-test-reports.py +++ b/tests/support/generate-names-file-from-failed-test-reports.py @@ -1,22 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" tests.support.generate-from-names-from-failed-test-reports ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This script is meant as a stop-gap until we move to PyTest to provide a functionality similar to PyTest's --last-failed where PyTest only runs last failed tests. -''' +""" # pylint: disable=resource-leakage -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import argparse +import glob import os import sys -import glob -import argparse try: import xunitparser except ImportError: - sys.stderr.write('Please install the xunitparser python package to run this script\n') + sys.stderr.write( + "Please install the xunitparser python package to run this script\n" + ) sys.stderr.flush() sys.exit(1) @@ -26,18 +29,18 @@ REPO_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__fi def main(): parser = argparse.ArgumentParser() parser.add_argument( - '--reports-dir', - default=os.path.join(REPO_ROOT, 'artifacts', 'xml-unittests-output'), - help='Path to the directory where the JUnit XML reports can be found' + "--reports-dir", + default=os.path.join(REPO_ROOT, "artifacts", "xml-unittests-output"), + help="Path to the directory where the JUnit XML reports can be found", ) parser.add_argument( - 'output_file', - help='Path to the file containing the failed tests listing to be fed to --names-files' + "output_file", + help="Path to the file containing the failed tests listing to be fed to --names-files", ) options = parser.parse_args() total_xml_reports = 0 failures = set() - for fname in sorted(glob.glob(os.path.join(options.reports_dir, '*.xml'))): + for fname in sorted(glob.glob(os.path.join(options.reports_dir, "*.xml"))): total_xml_reports += 1 with open(fname) as rfh: test_suite, test_result = xunitparser.parse(rfh) @@ -45,16 +48,16 @@ def main(): continue for test in test_suite: if test.bad: - failures.add('{classname}.{methodname}'.format(**test.__dict__)) + failures.add("{classname}.{methodname}".format(**test.__dict__)) if not total_xml_reports: - parser.exit(status=1, message='No JUnit XML files were parsed') + parser.exit(status=1, message="No JUnit XML files were parsed") - with open(options.output_file, 'w') as wfh: + with open(options.output_file, "w") as wfh: wfh.write(os.linesep.join(sorted(failures))) parser.exit(status=0) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/tests/support/gitfs.py b/tests/support/gitfs.py index e2b8107b1ed..ad638bda59f 100644 --- a/tests/support/gitfs.py +++ b/tests/support/gitfs.py @@ -1,122 +1,140 @@ # -*- coding: utf-8 -*- -''' +""" Base classes for gitfs/git_pillar integration tests -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import sys + import copy import errno import logging import os import pprint import shutil +import subprocess +import sys import tempfile import textwrap import threading -import subprocess import time # Import 3rd-party libs import psutil +import salt.ext.six as six # Import Salt libs import salt.utils.files import salt.utils.path import salt.utils.yaml -import salt.ext.six as six +from pytestsalt.utils import SaltDaemonScriptBase as _SaltDaemonScriptBase +from pytestsalt.utils import terminate_process from salt.fileserver import gitfs from salt.pillar import git_pillar # Import Salt Testing libs from tests.support.case import ModuleCase -from tests.support.unit import SkipTest -from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin, SaltReturnAssertsMixin -from tests.support.helpers import get_unused_localhost_port, requires_system_grains, patched_environ -from tests.support.runtests import RUNTIME_VARS +from tests.support.helpers import ( + get_unused_localhost_port, + patched_environ, + requires_system_grains, +) +from tests.support.mixins import ( + AdaptedConfigurationTestCaseMixin, + LoaderModuleMockMixin, + SaltReturnAssertsMixin, +) from tests.support.mock import patch -from pytestsalt.utils import SaltDaemonScriptBase as _SaltDaemonScriptBase, terminate_process +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import SkipTest log = logging.getLogger(__name__) -USERNAME = 'gitpillaruser' -PASSWORD = 'saltrules' +USERNAME = "gitpillaruser" +PASSWORD = "saltrules" _OPTS = { - '__role': 'minion', - 'environment': None, - 'pillarenv': None, - 'hash_type': 'sha256', - 'file_roots': {}, - 'state_top': 'top.sls', - 'state_top_saltenv': None, - 'renderer': 'yaml_jinja', - 'renderer_whitelist': [], - 'renderer_blacklist': [], - 'pillar_merge_lists': False, - 'git_pillar_base': 'master', - 'git_pillar_branch': 'master', - 'git_pillar_env': '', - 'git_pillar_root': '', - 'git_pillar_ssl_verify': True, - 'git_pillar_global_lock': True, - 'git_pillar_user': '', - 'git_pillar_password': '', - 'git_pillar_insecure_auth': False, - 'git_pillar_privkey': '', - 'git_pillar_pubkey': '', - 'git_pillar_passphrase': '', - 'git_pillar_refspecs': [ - '+refs/heads/*:refs/remotes/origin/*', - '+refs/tags/*:refs/tags/*', + "__role": "minion", + "environment": None, + "pillarenv": None, + "hash_type": "sha256", + "file_roots": {}, + "state_top": "top.sls", + "state_top_saltenv": None, + "renderer": "yaml_jinja", + "renderer_whitelist": [], + "renderer_blacklist": [], + "pillar_merge_lists": False, + "git_pillar_base": "master", + "git_pillar_branch": "master", + "git_pillar_env": "", + "git_pillar_fallback": "", + "git_pillar_root": "", + "git_pillar_ssl_verify": True, + "git_pillar_global_lock": True, + "git_pillar_user": "", + "git_pillar_password": "", + "git_pillar_insecure_auth": False, + "git_pillar_privkey": "", + "git_pillar_pubkey": "", + "git_pillar_passphrase": "", + "git_pillar_refspecs": [ + "+refs/heads/*:refs/remotes/origin/*", + "+refs/tags/*:refs/tags/*", ], - 'git_pillar_includes': True, + "git_pillar_includes": True, } PROC_TIMEOUT = 10 -def start_daemon(daemon_cli_script_name, - daemon_config_dir, - daemon_check_port, - daemon_class, - fail_hard=False, - start_timeout=10, - slow_stop=True, - environ=None, - cwd=None, - max_attempts=3, - **kwargs): - ''' +def start_daemon( + daemon_cli_script_name, + daemon_config_dir, + daemon_check_port, + daemon_class, + fail_hard=False, + start_timeout=10, + slow_stop=True, + environ=None, + cwd=None, + max_attempts=3, + **kwargs +): + """ Returns a running process daemon - ''' - log.info('[%s] Starting %s', daemon_class.log_prefix, daemon_class.__name__) + """ + log.info("[%s] Starting %s", daemon_class.log_prefix, daemon_class.__name__) attempts = 0 process = None while attempts <= max_attempts: # pylint: disable=too-many-nested-blocks attempts += 1 - process = daemon_class(str(daemon_config_dir), - daemon_check_port, - cli_script_name=daemon_cli_script_name, - slow_stop=slow_stop, - environ=environ, - cwd=cwd, - **kwargs) + process = daemon_class( + str(daemon_config_dir), + daemon_check_port, + cli_script_name=daemon_cli_script_name, + slow_stop=slow_stop, + environ=environ, + cwd=cwd, + **kwargs + ) process.start() if process.is_alive(): try: connectable = process.wait_until_running(timeout=start_timeout) if connectable is False: - connectable = process.wait_until_running(timeout=start_timeout/2) + connectable = process.wait_until_running(timeout=start_timeout / 2) if connectable is False: process.terminate() if attempts >= max_attempts: raise AssertionError( - 'The {} has failed to confirm running status ' - 'after {} attempts'.format(daemon_class.__name__, attempts)) + "The {} has failed to confirm running status " + "after {} attempts".format( + daemon_class.__name__, attempts + ) + ) continue except Exception as exc: # pylint: disable=broad-except - log.exception('[%s] %s', daemon_class.log_prefix, exc, exc_info=True) + log.exception("[%s] %s", daemon_class.log_prefix, exc, exc_info=True) terminate_process(process.pid, kill_children=True, slow_stop=slow_stop) if attempts >= max_attempts: raise AssertionError(str(exc)) @@ -124,10 +142,10 @@ def start_daemon(daemon_cli_script_name, # A little breathing before returning the process time.sleep(0.5) log.info( - '[%s] The %s is running after %d attempts', + "[%s] The %s is running after %d attempts", daemon_class.log_prefix, daemon_class.__name__, - attempts + attempts, ) break else: @@ -138,40 +156,54 @@ def start_daemon(daemon_cli_script_name, if process is not None: terminate_process(process.pid, kill_children=True, slow_stop=slow_stop) raise AssertionError( - 'The {} has failed to start after {} attempts'.format( - daemon_class.__name__, - attempts-1 + "The {} has failed to start after {} attempts".format( + daemon_class.__name__, attempts - 1 ) ) return process class SaltDaemonScriptBase(_SaltDaemonScriptBase): - def start(self): - ''' + """ Start the daemon subprocess - ''' + """ # Late import - log.info('[%s][%s] Starting DAEMON in CWD: %s', self.log_prefix, self.cli_display_name, self.cwd) - proc_args = [ - self.get_script_path(self.cli_script_name) - ] + self.get_base_script_args() + self.get_script_args() + log.info( + "[%s][%s] Starting DAEMON in CWD: %s", + self.log_prefix, + self.cli_display_name, + self.cwd, + ) + proc_args = ( + [self.get_script_path(self.cli_script_name)] + + self.get_base_script_args() + + self.get_script_args() + ) - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): # Windows needs the python executable to come first proc_args.insert(0, sys.executable) - log.info('[%s][%s] Running \'%s\'...', self.log_prefix, self.cli_display_name, ' '.join(proc_args)) + log.info( + "[%s][%s] Running '%s'...", + self.log_prefix, + self.cli_display_name, + " ".join(proc_args), + ) - self.init_terminal(proc_args, - env=self.environ, - cwd=self.cwd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + self.init_terminal( + proc_args, + env=self.environ, + cwd=self.cwd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) self._running.set() if self._process_cli_output_in_thread: - process_output_thread = threading.Thread(target=self._process_output_in_thread) + process_output_thread = threading.Thread( + target=self._process_output_in_thread + ) process_output_thread.daemon = True process_output_thread.start() return True @@ -179,38 +211,36 @@ class SaltDaemonScriptBase(_SaltDaemonScriptBase): class UwsgiDaemon(SaltDaemonScriptBase): - log_prefix = 'uWSGI' + log_prefix = "uWSGI" - def __init__(self, - config_dir, - uwsgi_port, - cli_script_name='uwsgi', - **kwargs): - super(UwsgiDaemon, self).__init__(None, # request - {'check_port': uwsgi_port}, # config - config_dir, # config_dir - None, # bin_dir_path - self.__class__.log_prefix, # log_prefix - cli_script_name=cli_script_name, - **kwargs) + def __init__(self, config_dir, uwsgi_port, cli_script_name="uwsgi", **kwargs): + super(UwsgiDaemon, self).__init__( + None, # request + {"check_port": uwsgi_port}, # config + config_dir, # config_dir + None, # bin_dir_path + self.__class__.log_prefix, # log_prefix + cli_script_name=cli_script_name, + **kwargs + ) def get_script_path(self, script_name): - ''' + """ Returns the path to the script to run - ''' + """ return script_name def get_base_script_args(self): - ''' + """ Returns any additional arguments to pass to the CLI script - ''' - return ['--yaml', os.path.join(self.config_dir, 'uwsgi.yml')] + """ + return ["--yaml", os.path.join(self.config_dir, "uwsgi.yml")] def get_check_ports(self): - ''' + """ Return a list of ports to check against to ensure the daemon is running - ''' - return [self.config['check_port']] + """ + return [self.config["check_port"]] def get_salt_run_event_listener(self): # Remove this method once pytest-salt get's past 2019.7.20 @@ -224,38 +254,36 @@ class UwsgiDaemon(SaltDaemonScriptBase): class NginxDaemon(SaltDaemonScriptBase): - log_prefix = 'Nginx' + log_prefix = "Nginx" - def __init__(self, - config_dir, - nginx_port, - cli_script_name='nginx', - **kwargs): - super(NginxDaemon, self).__init__(None, # request - {'check_port': nginx_port}, # config - config_dir, # config_dir - None, # bin_dir_path - self.__class__.log_prefix, # log_prefix - cli_script_name=cli_script_name, - **kwargs) + def __init__(self, config_dir, nginx_port, cli_script_name="nginx", **kwargs): + super(NginxDaemon, self).__init__( + None, # request + {"check_port": nginx_port}, # config + config_dir, # config_dir + None, # bin_dir_path + self.__class__.log_prefix, # log_prefix + cli_script_name=cli_script_name, + **kwargs + ) def get_script_path(self, script_name): - ''' + """ Returns the path to the script to run - ''' + """ return script_name def get_base_script_args(self): - ''' + """ Returns any additional arguments to pass to the CLI script - ''' - return ['-c', os.path.join(self.config_dir, 'nginx.conf')] + """ + return ["-c", os.path.join(self.config_dir, "nginx.conf")] def get_check_ports(self): - ''' + """ Return a list of ports to check against to ensure the daemon is running - ''' - return [self.config['check_port']] + """ + return [self.config["check_port"]] def get_salt_run_event_listener(self): # Remove this method once pytest-salt get's past 2019.7.20 @@ -269,38 +297,36 @@ class NginxDaemon(SaltDaemonScriptBase): class SshdDaemon(SaltDaemonScriptBase): - log_prefix = 'SSHD' + log_prefix = "SSHD" - def __init__(self, - config_dir, - sshd_port, - cli_script_name='sshd', - **kwargs): - super(SshdDaemon, self).__init__(None, # request - {'check_port': sshd_port}, # config - config_dir, # config_dir - None, # bin_dir_path - self.__class__.log_prefix, # log_prefix - cli_script_name=cli_script_name, - **kwargs) + def __init__(self, config_dir, sshd_port, cli_script_name="sshd", **kwargs): + super(SshdDaemon, self).__init__( + None, # request + {"check_port": sshd_port}, # config + config_dir, # config_dir + None, # bin_dir_path + self.__class__.log_prefix, # log_prefix + cli_script_name=cli_script_name, + **kwargs + ) def get_script_path(self, script_name): - ''' + """ Returns the path to the script to run - ''' + """ return script_name def get_base_script_args(self): - ''' + """ Returns any additional arguments to pass to the CLI script - ''' - return ['-D', '-e', '-f', os.path.join(self.config_dir, 'sshd_config')] + """ + return ["-D", "-e", "-f", os.path.join(self.config_dir, "sshd_config")] def get_check_ports(self): - ''' + """ Return a list of ports to check against to ensure the daemon is running - ''' - return [self.config['check_port']] + """ + return [self.config["check_port"]] def get_salt_run_event_listener(self): # Remove this method once pytest-salt get's past 2019.7.20 @@ -321,13 +347,20 @@ class SaltClientMixin(ModuleCase): def setUpClass(cls, grains=None): # pylint: disable=arguments-differ # Cent OS 6 has too old a version of git to handle the make_repo code, as # it lacks the -c option for git itself. - make_repo = getattr(cls, 'make_repo', None) - if callable(make_repo) and grains['os_family'] == 'RedHat' and grains['osmajorrelease'] < 7: - raise SkipTest('RHEL < 7 has too old a version of git to run these tests') + make_repo = getattr(cls, "make_repo", None) + if ( + callable(make_repo) + and grains["os_family"] == "RedHat" + and grains["osmajorrelease"] < 7 + ): + raise SkipTest("RHEL < 7 has too old a version of git to run these tests") # Late import import salt.client - mopts = AdaptedConfigurationTestCaseMixin.get_config('master', from_scratch=True) - cls.user = mopts['user'] + + mopts = AdaptedConfigurationTestCaseMixin.get_config( + "master", from_scratch=True + ) + cls.user = mopts["user"] cls.client = salt.client.get_local_client(mopts=mopts) @classmethod @@ -336,18 +369,15 @@ class SaltClientMixin(ModuleCase): @classmethod def cls_run_function(cls, function, *args, **kwargs): - orig = cls.client.cmd('minion', - function, - arg=args, - timeout=300, - kwarg=kwargs) - return orig['minion'] + orig = cls.client.cmd("minion", function, arg=args, timeout=300, kwarg=kwargs) + return orig["minion"] class SSHDMixin(SaltClientMixin, SaltReturnAssertsMixin): - ''' + """ Functions to stand up an SSHD server to serve up git repos for tests. - ''' + """ + sshd_proc = None prep_states_ran = False known_hosts_setup = False @@ -356,73 +386,81 @@ class SSHDMixin(SaltClientMixin, SaltReturnAssertsMixin): def setUpClass(cls): # pylint: disable=arguments-differ super(SSHDMixin, cls).setUpClass() try: - log.info('%s: prep_server()', cls.__name__) - cls.sshd_bin = salt.utils.path.which('sshd') + log.info("%s: prep_server()", cls.__name__) + cls.sshd_bin = salt.utils.path.which("sshd") cls.sshd_config_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - cls.sshd_config = os.path.join(cls.sshd_config_dir, 'sshd_config') + cls.sshd_config = os.path.join(cls.sshd_config_dir, "sshd_config") cls.sshd_port = get_unused_localhost_port() - cls.url = 'ssh://{username}@127.0.0.1:{port}/~/repo.git'.format( - username=cls.username, - port=cls.sshd_port) - cls.url_extra_repo = 'ssh://{username}@127.0.0.1:{port}/~/extra_repo.git'.format( - username=cls.username, - port=cls.sshd_port) - home = '/root/.ssh' + cls.url = "ssh://{username}@127.0.0.1:{port}/~/repo.git".format( + username=cls.username, port=cls.sshd_port + ) + cls.url_extra_repo = "ssh://{username}@127.0.0.1:{port}/~/extra_repo.git".format( + username=cls.username, port=cls.sshd_port + ) + home = "/root/.ssh" cls.ext_opts = { - 'url': cls.url, - 'url_extra_repo': cls.url_extra_repo, - 'privkey_nopass': os.path.join(home, cls.id_rsa_nopass), - 'pubkey_nopass': os.path.join(home, cls.id_rsa_nopass + '.pub'), - 'privkey_withpass': os.path.join(home, cls.id_rsa_withpass), - 'pubkey_withpass': os.path.join(home, cls.id_rsa_withpass + '.pub'), - 'passphrase': cls.passphrase} + "url": cls.url, + "url_extra_repo": cls.url_extra_repo, + "privkey_nopass": os.path.join(home, cls.id_rsa_nopass), + "pubkey_nopass": os.path.join(home, cls.id_rsa_nopass + ".pub"), + "privkey_withpass": os.path.join(home, cls.id_rsa_withpass), + "pubkey_withpass": os.path.join(home, cls.id_rsa_withpass + ".pub"), + "passphrase": cls.passphrase, + } if cls.prep_states_ran is False: ret = cls.cls_run_function( - 'state.apply', - mods='git_pillar.ssh', - pillar={'git_pillar': {'git_ssh': cls.git_ssh, - 'id_rsa_nopass': cls.id_rsa_nopass, - 'id_rsa_withpass': cls.id_rsa_withpass, - 'sshd_bin': cls.sshd_bin, - 'sshd_port': cls.sshd_port, - 'sshd_config_dir': cls.sshd_config_dir, - 'master_user': cls.user, - 'user': cls.username}} + "state.apply", + mods="git_pillar.ssh", + pillar={ + "git_pillar": { + "git_ssh": cls.git_ssh, + "id_rsa_nopass": cls.id_rsa_nopass, + "id_rsa_withpass": cls.id_rsa_withpass, + "sshd_bin": cls.sshd_bin, + "sshd_port": cls.sshd_port, + "sshd_config_dir": cls.sshd_config_dir, + "master_user": cls.user, + "user": cls.username, + } + }, ) - assert next(six.itervalues(ret))['result'] is True + assert next(six.itervalues(ret))["result"] is True cls.prep_states_ran = True - log.info('%s: States applied', cls.__name__) + log.info("%s: States applied", cls.__name__) if cls.sshd_proc is not None: if not psutil.pid_exists(cls.sshd_proc.pid): - log.info('%s: sshd started but appears to be dead now. Will try to restart it.', - cls.__name__) + log.info( + "%s: sshd started but appears to be dead now. Will try to restart it.", + cls.__name__, + ) cls.sshd_proc = None if cls.sshd_proc is None: - cls.sshd_proc = start_daemon(cls.sshd_bin, cls.sshd_config_dir, cls.sshd_port, SshdDaemon) - log.info('%s: sshd started', cls.__name__) + cls.sshd_proc = start_daemon( + cls.sshd_bin, cls.sshd_config_dir, cls.sshd_port, SshdDaemon + ) + log.info("%s: sshd started", cls.__name__) except AssertionError: cls.tearDownClass() six.reraise(*sys.exc_info()) if cls.known_hosts_setup is False: known_hosts_ret = cls.cls_run_function( - 'ssh.set_known_host', + "ssh.set_known_host", user=cls.user, - hostname='127.0.0.1', + hostname="127.0.0.1", port=cls.sshd_port, - enc='ssh-rsa', - fingerprint='fd:6f:7f:5d:06:6b:f2:06:0d:26:93:9e:5a:b5:19:46', + enc="ssh-rsa", + fingerprint="fd:6f:7f:5d:06:6b:f2:06:0d:26:93:9e:5a:b5:19:46", hash_known_hosts=False, - fingerprint_hash_type='md5', + fingerprint_hash_type="md5", ) - if 'error' in known_hosts_ret: + if "error" in known_hosts_ret: cls.tearDownClass() raise AssertionError( - 'Failed to add key to {0} user\'s known_hosts ' - 'file: {1}'.format( - cls.master_opts['user'], - known_hosts_ret['error'] + "Failed to add key to {0} user's known_hosts " + "file: {1}".format( + cls.master_opts["user"], known_hosts_ret["error"] ) ) cls.known_hosts_setup = True @@ -430,29 +468,43 @@ class SSHDMixin(SaltClientMixin, SaltReturnAssertsMixin): @classmethod def tearDownClass(cls): if cls.sshd_proc is not None: - log.info('[%s] Stopping %s', cls.sshd_proc.log_prefix, cls.sshd_proc.__class__.__name__) + log.info( + "[%s] Stopping %s", + cls.sshd_proc.log_prefix, + cls.sshd_proc.__class__.__name__, + ) terminate_process(cls.sshd_proc.pid, kill_children=True, slow_stop=True) - log.info('[%s] %s stopped', cls.sshd_proc.log_prefix, cls.sshd_proc.__class__.__name__) + log.info( + "[%s] %s stopped", + cls.sshd_proc.log_prefix, + cls.sshd_proc.__class__.__name__, + ) cls.sshd_proc = None if cls.prep_states_ran: - ret = cls.cls_run_function('state.single', 'user.absent', name=cls.username, purge=True) + ret = cls.cls_run_function( + "state.single", "user.absent", name=cls.username, purge=True + ) try: - if ret and 'minion' in ret: - ret_data = next(six.itervalues(ret['minion'])) - if not ret_data['result']: - log.warning('Failed to delete test account %s', cls.username) + if ret and "minion" in ret: + ret_data = next(six.itervalues(ret["minion"])) + if not ret_data["result"]: + log.warning("Failed to delete test account %s", cls.username) except KeyError: - log.warning('Failed to delete test account. Salt return:\n%s', - pprint.pformat(ret)) + log.warning( + "Failed to delete test account. Salt return:\n%s", + pprint.pformat(ret), + ) cls.prep_states_ran = False cls.known_hosts_setup = False shutil.rmtree(cls.sshd_config_dir, ignore_errors=True) - ssh_dir = os.path.expanduser('~/.ssh') - for filename in (cls.id_rsa_nopass, - cls.id_rsa_nopass + '.pub', - cls.id_rsa_withpass, - cls.id_rsa_withpass + '.pub', - cls.git_ssh): + ssh_dir = os.path.expanduser("~/.ssh") + for filename in ( + cls.id_rsa_nopass, + cls.id_rsa_nopass + ".pub", + cls.id_rsa_withpass, + cls.id_rsa_withpass + ".pub", + cls.git_ssh, + ): try: os.remove(os.path.join(ssh_dir, filename)) except OSError as exc: @@ -462,85 +514,104 @@ class SSHDMixin(SaltClientMixin, SaltReturnAssertsMixin): class WebserverMixin(SaltClientMixin, SaltReturnAssertsMixin): - ''' + """ Functions to stand up an nginx + uWSGI + git-http-backend webserver to serve up git repos for tests. - ''' + """ + nginx_proc = uwsgi_proc = None prep_states_ran = False @classmethod def setUpClass(cls): # pylint: disable=arguments-differ - ''' + """ Set up all the webserver paths. Designed to be run once in a setUpClass function. - ''' + """ super(WebserverMixin, cls).setUpClass() cls.root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - cls.config_dir = os.path.join(cls.root_dir, 'config') - cls.nginx_conf = os.path.join(cls.config_dir, 'nginx.conf') - cls.uwsgi_conf = os.path.join(cls.config_dir, 'uwsgi.yml') - cls.git_dir = os.path.join(cls.root_dir, 'git') - cls.repo_dir = os.path.join(cls.git_dir, 'repos') - cls.venv_dir = os.path.join(cls.root_dir, 'venv') - cls.uwsgi_bin = os.path.join(cls.venv_dir, 'bin', 'uwsgi') + cls.config_dir = os.path.join(cls.root_dir, "config") + cls.nginx_conf = os.path.join(cls.config_dir, "nginx.conf") + cls.uwsgi_conf = os.path.join(cls.config_dir, "uwsgi.yml") + cls.git_dir = os.path.join(cls.root_dir, "git") + cls.repo_dir = os.path.join(cls.git_dir, "repos") + cls.venv_dir = os.path.join(cls.root_dir, "venv") + cls.uwsgi_bin = os.path.join(cls.venv_dir, "bin", "uwsgi") cls.nginx_port = cls.uwsgi_port = get_unused_localhost_port() while cls.uwsgi_port == cls.nginx_port: # Ensure we don't hit a corner case in which two sucessive calls to # get_unused_localhost_port() return identical port numbers. cls.uwsgi_port = get_unused_localhost_port() - cls.url = 'http://127.0.0.1:{port}/repo.git'.format(port=cls.nginx_port) - cls.url_extra_repo = 'http://127.0.0.1:{port}/extra_repo.git'.format(port=cls.nginx_port) - cls.ext_opts = {'url': cls.url, 'url_extra_repo': cls.url_extra_repo} + cls.url = "http://127.0.0.1:{port}/repo.git".format(port=cls.nginx_port) + cls.url_extra_repo = "http://127.0.0.1:{port}/extra_repo.git".format( + port=cls.nginx_port + ) + cls.ext_opts = {"url": cls.url, "url_extra_repo": cls.url_extra_repo} # Add auth params if present (if so this will trigger the spawned # server to turn on HTTP basic auth). - for credential_param in ('user', 'password'): + for credential_param in ("user", "password"): if hasattr(cls, credential_param): cls.ext_opts[credential_param] = getattr(cls, credential_param) - auth_enabled = hasattr(cls, 'username') and hasattr(cls, 'password') - pillar = {'git_pillar': {'config_dir': cls.config_dir, - 'git_dir': cls.git_dir, - 'venv_dir': cls.venv_dir, - 'root_dir': cls.root_dir, - 'nginx_port': cls.nginx_port, - 'uwsgi_port': cls.uwsgi_port, - 'auth_enabled': auth_enabled}} + auth_enabled = hasattr(cls, "username") and hasattr(cls, "password") + pillar = { + "git_pillar": { + "config_dir": cls.config_dir, + "git_dir": cls.git_dir, + "venv_dir": cls.venv_dir, + "root_dir": cls.root_dir, + "nginx_port": cls.nginx_port, + "uwsgi_port": cls.uwsgi_port, + "auth_enabled": auth_enabled, + } + } # Different libexec dir for git backend on Debian-based systems - git_core = '/usr/libexec/git-core' + git_core = "/usr/libexec/git-core" if not os.path.exists(git_core): - git_core = '/usr/lib/git-core' + git_core = "/usr/lib/git-core" if not os.path.exists(git_core): cls.tearDownClass() raise AssertionError( - '{} not found. Either git is not installed, or the test ' - 'class needs to be updated.'.format(git_core) + "{} not found. Either git is not installed, or the test " + "class needs to be updated.".format(git_core) ) - pillar['git_pillar']['git-http-backend'] = os.path.join(git_core, 'git-http-backend') + pillar["git_pillar"]["git-http-backend"] = os.path.join( + git_core, "git-http-backend" + ) try: if cls.prep_states_ran is False: - ret = cls.cls_run_function('state.apply', mods='git_pillar.http', pillar=pillar) - assert next(six.itervalues(ret))['result'] is True + ret = cls.cls_run_function( + "state.apply", mods="git_pillar.http", pillar=pillar + ) + assert next(six.itervalues(ret))["result"] is True cls.prep_states_ran = True - log.info('%s: States applied', cls.__name__) + log.info("%s: States applied", cls.__name__) if cls.uwsgi_proc is not None: if not psutil.pid_exists(cls.uwsgi_proc.pid): - log.warning('%s: uWsgi started but appears to be dead now. Will try to restart it.', - cls.__name__) + log.warning( + "%s: uWsgi started but appears to be dead now. Will try to restart it.", + cls.__name__, + ) cls.uwsgi_proc = None if cls.uwsgi_proc is None: - cls.uwsgi_proc = start_daemon(cls.uwsgi_bin, cls.config_dir, cls.uwsgi_port, UwsgiDaemon) - log.info('%s: %s started', cls.__name__, cls.uwsgi_bin) + cls.uwsgi_proc = start_daemon( + cls.uwsgi_bin, cls.config_dir, cls.uwsgi_port, UwsgiDaemon + ) + log.info("%s: %s started", cls.__name__, cls.uwsgi_bin) if cls.nginx_proc is not None: if not psutil.pid_exists(cls.nginx_proc.pid): - log.warning('%s: nginx started but appears to be dead now. Will try to restart it.', - cls.__name__) + log.warning( + "%s: nginx started but appears to be dead now. Will try to restart it.", + cls.__name__, + ) cls.nginx_proc = None if cls.nginx_proc is None: - cls.nginx_proc = start_daemon('nginx', cls.config_dir, cls.nginx_port, NginxDaemon) - log.info('%s: nginx started', cls.__name__) + cls.nginx_proc = start_daemon( + "nginx", cls.config_dir, cls.nginx_port, NginxDaemon + ) + log.info("%s: nginx started", cls.__name__) except AssertionError: cls.tearDownClass() six.reraise(*sys.exc_info()) @@ -548,14 +619,30 @@ class WebserverMixin(SaltClientMixin, SaltReturnAssertsMixin): @classmethod def tearDownClass(cls): if cls.nginx_proc is not None: - log.info('[%s] Stopping %s', cls.nginx_proc.log_prefix, cls.nginx_proc.__class__.__name__) + log.info( + "[%s] Stopping %s", + cls.nginx_proc.log_prefix, + cls.nginx_proc.__class__.__name__, + ) terminate_process(cls.nginx_proc.pid, kill_children=True, slow_stop=True) - log.info('[%s] %s stopped', cls.nginx_proc.log_prefix, cls.nginx_proc.__class__.__name__) + log.info( + "[%s] %s stopped", + cls.nginx_proc.log_prefix, + cls.nginx_proc.__class__.__name__, + ) cls.nginx_proc = None if cls.uwsgi_proc is not None: - log.info('[%s] Stopping %s', cls.uwsgi_proc.log_prefix, cls.uwsgi_proc.__class__.__name__) + log.info( + "[%s] Stopping %s", + cls.uwsgi_proc.log_prefix, + cls.uwsgi_proc.__class__.__name__, + ) terminate_process(cls.uwsgi_proc.pid, kill_children=True, slow_stop=True) - log.info('[%s] %s stopped', cls.uwsgi_proc.log_prefix, cls.uwsgi_proc.__class__.__name__) + log.info( + "[%s] %s stopped", + cls.uwsgi_proc.log_prefix, + cls.uwsgi_proc.__class__.__name__, + ) cls.uwsgi_proc = None shutil.rmtree(cls.root_dir, ignore_errors=True) cls.prep_states_ran = False @@ -563,132 +650,118 @@ class WebserverMixin(SaltClientMixin, SaltReturnAssertsMixin): class GitTestBase(ModuleCase): - ''' + """ Base class for all gitfs/git_pillar tests. Must be subclassed and paired with either SSHDMixin or WebserverMixin to provide the server. - ''' + """ + maxDiff = None git_opts = '-c user.name="Foo Bar" -c user.email=foo@bar.com' ext_opts = {} - def make_repo(self, root_dir, user='root'): + def make_repo(self, root_dir, user="root"): raise NotImplementedError() class GitFSTestBase(GitTestBase, LoaderModuleMockMixin): - ''' + """ Base class for all gitfs tests - ''' + """ + @requires_system_grains def setup_loader_modules(self, grains): # pylint: disable=W0221 - return { - gitfs: { - '__opts__': copy.copy(_OPTS), - '__grains__': grains, - } - } + return {gitfs: {"__opts__": copy.copy(_OPTS), "__grains__": grains}} - def make_repo(self, root_dir, user='root'): + def make_repo(self, root_dir, user="root"): raise NotImplementedError() class GitPillarTestBase(GitTestBase, LoaderModuleMockMixin): - ''' + """ Base class for all git_pillar tests - ''' + """ + bare_repo = bare_repo_backup = bare_extra_repo = bare_extra_repo_backup = None admin_repo = admin_repo_backup = admin_extra_repo = admin_extra_repo_backup = None @requires_system_grains def setup_loader_modules(self, grains): # pylint: disable=W0221 - return { - git_pillar: { - '__opts__': copy.copy(_OPTS), - '__grains__': grains, - } - } + return {git_pillar: {"__opts__": copy.copy(_OPTS), "__grains__": grains}} def get_pillar(self, ext_pillar_conf): - ''' + """ Run git_pillar with the specified configuration - ''' + """ cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, cachedir, ignore_errors=True) - ext_pillar_opts = {'optimization_order': [0, 1, 2]} + ext_pillar_opts = {"optimization_order": [0, 1, 2]} ext_pillar_opts.update( salt.utils.yaml.safe_load( ext_pillar_conf.format( cachedir=cachedir, - extmods=os.path.join(cachedir, 'extmods'), + extmods=os.path.join(cachedir, "extmods"), **self.ext_opts ) ) ) with patch.dict(git_pillar.__opts__, ext_pillar_opts): return git_pillar.ext_pillar( - 'minion', - {}, - *ext_pillar_opts['ext_pillar'][0]['git'] + "minion", {}, *ext_pillar_opts["ext_pillar"][0]["git"] ) - def make_repo(self, root_dir, user='root'): - log.info('Creating test Git repo....') - self.bare_repo = os.path.join(root_dir, 'repo.git') - self.bare_repo_backup = '{}.backup'.format(self.bare_repo) - self.admin_repo = os.path.join(root_dir, 'admin') - self.admin_repo_backup = '{}.backup'.format(self.admin_repo) + def make_repo(self, root_dir, user="root"): + log.info("Creating test Git repo....") + self.bare_repo = os.path.join(root_dir, "repo.git") + self.bare_repo_backup = "{}.backup".format(self.bare_repo) + self.admin_repo = os.path.join(root_dir, "admin") + self.admin_repo_backup = "{}.backup".format(self.admin_repo) for dirname in (self.bare_repo, self.admin_repo): shutil.rmtree(dirname, ignore_errors=True) - if os.path.exists(self.bare_repo_backup) and os.path.exists(self.admin_repo_backup): + if os.path.exists(self.bare_repo_backup) and os.path.exists( + self.admin_repo_backup + ): shutil.copytree(self.bare_repo_backup, self.bare_repo) shutil.copytree(self.admin_repo_backup, self.admin_repo) return # Create bare repo - self.run_function( - 'git.init', - [self.bare_repo], - user=user, - bare=True) + self.run_function("git.init", [self.bare_repo], user=user, bare=True) # Clone bare repo - self.run_function( - 'git.clone', - [self.admin_repo], - url=self.bare_repo, - user=user) + self.run_function("git.clone", [self.admin_repo], url=self.bare_repo, user=user) def _push(branch, message): + self.run_function("git.add", [self.admin_repo, "."], user=user) self.run_function( - 'git.add', - [self.admin_repo, '.'], - user=user) - self.run_function( - 'git.commit', + "git.commit", [self.admin_repo, message], user=user, git_opts=self.git_opts, ) self.run_function( - 'git.push', - [self.admin_repo], - remote='origin', - ref=branch, - user=user, + "git.push", [self.admin_repo], remote="origin", ref=branch, user=user, ) with salt.utils.files.fopen( - os.path.join(self.admin_repo, 'top.sls'), 'w') as fp_: - fp_.write(textwrap.dedent('''\ + os.path.join(self.admin_repo, "top.sls"), "w" + ) as fp_: + fp_.write( + textwrap.dedent( + """\ base: '*': - foo - ''')) + """ + ) + ) with salt.utils.files.fopen( - os.path.join(self.admin_repo, 'foo.sls'), 'w') as fp_: - fp_.write(textwrap.dedent('''\ + os.path.join(self.admin_repo, "foo.sls"), "w" + ) as fp_: + fp_.write( + textwrap.dedent( + """\ branch: master mylist: - master @@ -698,39 +771,44 @@ class GitPillarTestBase(GitTestBase, LoaderModuleMockMixin): - master nested_dict: master: True - ''')) + """ + ) + ) # Add another file to be referenced using git_pillar_includes with salt.utils.files.fopen( - os.path.join(self.admin_repo, 'bar.sls'), 'w') as fp_: - fp_.write('included_pillar: True\n') + os.path.join(self.admin_repo, "bar.sls"), "w" + ) as fp_: + fp_.write("included_pillar: True\n") # Add another file in subdir - os.mkdir(os.path.join(self.admin_repo, 'subdir')) + os.mkdir(os.path.join(self.admin_repo, "subdir")) with salt.utils.files.fopen( - os.path.join(self.admin_repo, 'subdir', 'bar.sls'), 'w') as fp_: - fp_.write('from_subdir: True\n') - _push('master', 'initial commit') + os.path.join(self.admin_repo, "subdir", "bar.sls"), "w" + ) as fp_: + fp_.write("from_subdir: True\n") + _push("master", "initial commit") # Do the same with different values for "dev" branch - self.run_function( - 'git.checkout', - [self.admin_repo], - user=user, - opts='-b dev') + self.run_function("git.checkout", [self.admin_repo], user=user, opts="-b dev") # The bar.sls shouldn't be in any branch but master - self.run_function( - 'git.rm', - [self.admin_repo, 'bar.sls'], - user=user) + self.run_function("git.rm", [self.admin_repo, "bar.sls"], user=user) with salt.utils.files.fopen( - os.path.join(self.admin_repo, 'top.sls'), 'w') as fp_: - fp_.write(textwrap.dedent('''\ + os.path.join(self.admin_repo, "top.sls"), "w" + ) as fp_: + fp_.write( + textwrap.dedent( + """\ dev: '*': - foo - ''')) + """ + ) + ) with salt.utils.files.fopen( - os.path.join(self.admin_repo, 'foo.sls'), 'w') as fp_: - fp_.write(textwrap.dedent('''\ + os.path.join(self.admin_repo, "foo.sls"), "w" + ) as fp_: + fp_.write( + textwrap.dedent( + """\ branch: dev mylist: - dev @@ -740,177 +818,190 @@ class GitPillarTestBase(GitTestBase, LoaderModuleMockMixin): - dev nested_dict: dev: True - ''')) - _push('dev', 'add dev branch') + """ + ) + ) + _push("dev", "add dev branch") # Create just a top file in a separate repo, to be mapped to the base # env and referenced using git_pillar_includes self.run_function( - 'git.checkout', - [self.admin_repo], - user=user, - opts='-b top_only') + "git.checkout", [self.admin_repo], user=user, opts="-b top_only" + ) # The top.sls should be the only file in this branch self.run_function( - 'git.rm', - [self.admin_repo, 'foo.sls', os.path.join('subdir', 'bar.sls')], - user=user) + "git.rm", + [self.admin_repo, "foo.sls", os.path.join("subdir", "bar.sls")], + user=user, + ) with salt.utils.files.fopen( - os.path.join(self.admin_repo, 'top.sls'), 'w') as fp_: - fp_.write(textwrap.dedent('''\ + os.path.join(self.admin_repo, "top.sls"), "w" + ) as fp_: + fp_.write( + textwrap.dedent( + """\ base: '*': - bar - ''')) - _push('top_only', 'add top_only branch') + """ + ) + ) + _push("top_only", "add top_only branch") # Create just another top file in a separate repo, to be mapped to the base # env and including mounted.bar self.run_function( - 'git.checkout', - [self.admin_repo], - user=user, - opts='-b top_mounted') + "git.checkout", [self.admin_repo], user=user, opts="-b top_mounted" + ) # The top.sls should be the only file in this branch with salt.utils.files.fopen( - os.path.join(self.admin_repo, 'top.sls'), 'w') as fp_: - fp_.write(textwrap.dedent('''\ + os.path.join(self.admin_repo, "top.sls"), "w" + ) as fp_: + fp_.write( + textwrap.dedent( + """\ base: '*': - mounted.bar - ''')) - _push('top_mounted', 'add top_mounted branch') + """ + ) + ) + _push("top_mounted", "add top_mounted branch") shutil.copytree(self.bare_repo, self.bare_repo_backup) shutil.copytree(self.admin_repo, self.admin_repo_backup) - log.info('Test Git repo created.') + log.info("Test Git repo created.") - def make_extra_repo(self, root_dir, user='root'): - log.info('Creating extra test Git repo....') - self.bare_extra_repo = os.path.join(root_dir, 'extra_repo.git') - self.bare_extra_repo_backup = '{}.backup'.format(self.bare_extra_repo) - self.admin_extra_repo = os.path.join(root_dir, 'admin_extra') - self.admin_extra_repo_backup = '{}.backup'.format(self.admin_extra_repo) + def make_extra_repo(self, root_dir, user="root"): + log.info("Creating extra test Git repo....") + self.bare_extra_repo = os.path.join(root_dir, "extra_repo.git") + self.bare_extra_repo_backup = "{}.backup".format(self.bare_extra_repo) + self.admin_extra_repo = os.path.join(root_dir, "admin_extra") + self.admin_extra_repo_backup = "{}.backup".format(self.admin_extra_repo) for dirname in (self.bare_extra_repo, self.admin_extra_repo): shutil.rmtree(dirname, ignore_errors=True) - if os.path.exists(self.bare_extra_repo_backup) and os.path.exists(self.admin_extra_repo_backup): + if os.path.exists(self.bare_extra_repo_backup) and os.path.exists( + self.admin_extra_repo_backup + ): shutil.copytree(self.bare_extra_repo_backup, self.bare_extra_repo) shutil.copytree(self.admin_extra_repo_backup, self.admin_extra_repo) return # Create bare extra repo - self.run_function( - 'git.init', - [self.bare_extra_repo], - user=user, - bare=True) + self.run_function("git.init", [self.bare_extra_repo], user=user, bare=True) # Clone bare repo self.run_function( - 'git.clone', - [self.admin_extra_repo], - url=self.bare_extra_repo, - user=user) + "git.clone", [self.admin_extra_repo], url=self.bare_extra_repo, user=user + ) def _push(branch, message): + self.run_function("git.add", [self.admin_extra_repo, "."], user=user) self.run_function( - 'git.add', - [self.admin_extra_repo, '.'], - user=user) - self.run_function( - 'git.commit', + "git.commit", [self.admin_extra_repo, message], user=user, git_opts=self.git_opts, ) self.run_function( - 'git.push', + "git.push", [self.admin_extra_repo], - remote='origin', + remote="origin", ref=branch, user=user, ) with salt.utils.files.fopen( - os.path.join(self.admin_extra_repo, 'top.sls'), 'w') as fp_: - fp_.write(textwrap.dedent('''\ + os.path.join(self.admin_extra_repo, "top.sls"), "w" + ) as fp_: + fp_.write( + textwrap.dedent( + """\ "{{saltenv}}": '*': - motd - nowhere.foo - ''')) + """ + ) + ) with salt.utils.files.fopen( - os.path.join(self.admin_extra_repo, 'motd.sls'), 'w') as fp_: - fp_.write(textwrap.dedent('''\ + os.path.join(self.admin_extra_repo, "motd.sls"), "w" + ) as fp_: + fp_.write( + textwrap.dedent( + """\ motd: The force will be with you. Always. - ''')) - _push('master', 'initial commit') + """ + ) + ) + _push("master", "initial commit") shutil.copytree(self.bare_extra_repo, self.bare_extra_repo_backup) shutil.copytree(self.admin_extra_repo, self.admin_extra_repo_backup) - log.info('Extra test Git repo created.') + log.info("Extra test Git repo created.") @classmethod def tearDownClass(cls): super(GitPillarTestBase, cls).tearDownClass() - for dirname in (cls.admin_repo, - cls.admin_repo_backup, - cls.admin_extra_repo, - cls.admin_extra_repo_backup, - cls.bare_repo, - cls.bare_repo_backup, - cls.bare_extra_repo, - cls.bare_extra_repo_backup): + for dirname in ( + cls.admin_repo, + cls.admin_repo_backup, + cls.admin_extra_repo, + cls.admin_extra_repo_backup, + cls.bare_repo, + cls.bare_repo_backup, + cls.bare_extra_repo, + cls.bare_extra_repo_backup, + ): if dirname is not None: shutil.rmtree(dirname, ignore_errors=True) class GitPillarSSHTestBase(GitPillarTestBase, SSHDMixin): - ''' + """ Base class for GitPython and Pygit2 SSH tests - ''' + """ + id_rsa_nopass = id_rsa_withpass = None - git_ssh = '/tmp/git_ssh' + git_ssh = "/tmp/git_ssh" def setUp(self): - ''' + """ Create the SSH server and user, and create the git repo - ''' - log.info('%s.setUp() started...', self.__class__.__name__) + """ + log.info("%s.setUp() started...", self.__class__.__name__) super(GitPillarSSHTestBase, self).setUp() - root_dir = os.path.expanduser('~{0}'.format(self.username)) - if root_dir.startswith('~'): + root_dir = os.path.expanduser("~{0}".format(self.username)) + if root_dir.startswith("~"): raise AssertionError( - 'Unable to resolve homedir for user \'{0}\''.format( - self.username - ) + "Unable to resolve homedir for user '{0}'".format(self.username) ) self.make_repo(root_dir, user=self.username) self.make_extra_repo(root_dir, user=self.username) - log.info('%s.setUp() complete.', self.__class__.__name__) + log.info("%s.setUp() complete.", self.__class__.__name__) def get_pillar(self, ext_pillar_conf): - ''' + """ Wrap the parent class' get_pillar() func in logic that temporarily changes the GIT_SSH to use our custom script, ensuring that the passphraselsess key is used to auth without needing to modify the root user's ssh config file. - ''' + """ with patched_environ(GIT_SSH=self.git_ssh): return super(GitPillarSSHTestBase, self).get_pillar(ext_pillar_conf) class GitPillarHTTPTestBase(GitPillarTestBase, WebserverMixin): - ''' + """ Base class for GitPython and Pygit2 HTTP tests - ''' + """ def setUp(self): - ''' + """ Create and start the webserver, and create the git repo - ''' - log.info('%s.setUp() started...', self.__class__.__name__) + """ + log.info("%s.setUp() started...", self.__class__.__name__) super(GitPillarHTTPTestBase, self).setUp() self.make_repo(self.repo_dir) self.make_extra_repo(self.repo_dir) - log.info('%s.setUp() complete', self.__class__.__name__) + log.info("%s.setUp() complete", self.__class__.__name__) diff --git a/tests/support/helpers.py b/tests/support/helpers.py index e145f493571..352dd8daf40 100644 --- a/tests/support/helpers.py +++ b/tests/support/helpers.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :copyright: Copyright 2013-2017 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. @@ -8,11 +8,12 @@ ~~~~~~~~~~~~~~~~~~~~~ Test support helpers -''' +""" # pylint: disable=repr-flag-used-in-string,wrong-import-order # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import base64 import errno import fnmatch @@ -30,25 +31,26 @@ import tempfile import textwrap import threading import time -import salt.ext.tornado.ioloop -import salt.ext.tornado.web import types -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import range, builtins # pylint: disable=import-error,redefined-builtin -from pytestsalt.utils import get_unused_localhost_port - -# Import Salt Tests Support libs -from tests.support.unit import skip, _id, SkipTest -from tests.support.mock import patch -from tests.support.runtests import RUNTIME_VARS -from tests.support.sminion import create_sminion +import salt.ext.tornado.ioloop +import salt.ext.tornado.web # Import Salt libs import salt.utils.files import salt.utils.platform import salt.utils.stringutils +from pytestsalt.utils import get_unused_localhost_port + +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import builtins, range +from tests.support.mock import patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.sminion import create_sminion + +# Import Salt Tests Support libs +from tests.support.unit import SkipTest, _id, skip log = logging.getLogger(__name__) @@ -56,18 +58,19 @@ HAS_SYMLINKS = None def no_symlinks(): - ''' + """ Check if git is installed and has symlinks enabled in the configuration. - ''' + """ global HAS_SYMLINKS if HAS_SYMLINKS is not None: return not HAS_SYMLINKS - output = '' + output = "" try: output = subprocess.Popen( - ['git', 'config', '--get', 'core.symlinks'], + ["git", "config", "--get", "core.symlinks"], cwd=RUNTIME_VARS.TMP, - stdout=subprocess.PIPE).communicate()[0] + stdout=subprocess.PIPE, + ).communicate()[0] except OSError as exc: if exc.errno != errno.ENOENT: raise @@ -75,13 +78,13 @@ def no_symlinks(): # git returned non-zero status pass HAS_SYMLINKS = False - if output.strip() == 'true': + if output.strip() == "true": HAS_SYMLINKS = True return not HAS_SYMLINKS def destructiveTest(caller): - ''' + """ Mark a test case as a destructive test for example adding or removing users from your system. @@ -92,35 +95,38 @@ def destructiveTest(caller): @destructiveTest def test_create_user(self): pass - ''' + """ # Late import from tests.support.runtests import RUNTIME_VARS + if RUNTIME_VARS.PYTEST_SESSION: - setattr(caller, '__destructive_test__', True) + setattr(caller, "__destructive_test__", True) if inspect.isclass(caller): # We're decorating a class - old_setup = getattr(caller, 'setUp', None) + old_setup = getattr(caller, "setUp", None) def setUp(self, *args, **kwargs): - if os.environ.get('DESTRUCTIVE_TESTS', 'False').lower() == 'false': - self.skipTest('Destructive tests are disabled') + if os.environ.get("DESTRUCTIVE_TESTS", "False").lower() == "false": + self.skipTest("Destructive tests are disabled") if old_setup is not None: old_setup(self, *args, **kwargs) + caller.setUp = setUp return caller # We're simply decorating functions @functools.wraps(caller) def wrap(cls): - if os.environ.get('DESTRUCTIVE_TESTS', 'False').lower() == 'false': - cls.skipTest('Destructive tests are disabled') + if os.environ.get("DESTRUCTIVE_TESTS", "False").lower() == "false": + cls.skipTest("Destructive tests are disabled") return caller(cls) + return wrap def expensiveTest(caller): - ''' + """ Mark a test case as an expensive test, for example, a test which can cost money(Salt's cloud provider tests). @@ -131,35 +137,38 @@ def expensiveTest(caller): @expensiveTest def test_create_user(self): pass - ''' + """ # Late import from tests.support.runtests import RUNTIME_VARS + if RUNTIME_VARS.PYTEST_SESSION: - setattr(caller, '__expensive_test__', True) + setattr(caller, "__expensive_test__", True) if inspect.isclass(caller): # We're decorating a class - old_setup = getattr(caller, 'setUp', None) + old_setup = getattr(caller, "setUp", None) def setUp(self, *args, **kwargs): - if os.environ.get('EXPENSIVE_TESTS', 'False').lower() == 'false': - self.skipTest('Expensive tests are disabled') + if os.environ.get("EXPENSIVE_TESTS", "False").lower() == "false": + self.skipTest("Expensive tests are disabled") if old_setup is not None: old_setup(self, *args, **kwargs) + caller.setUp = setUp return caller # We're simply decorating functions @functools.wraps(caller) def wrap(cls): - if os.environ.get('EXPENSIVE_TESTS', 'False').lower() == 'false': - cls.skipTest('Expensive tests are disabled') + if os.environ.get("EXPENSIVE_TESTS", "False").lower() == "false": + cls.skipTest("Expensive tests are disabled") return caller(cls) + return wrap def flaky(caller=None, condition=True, attempts=4): - ''' + """ Mark a test as flaky. The test will attempt to run five times, looking for a successful run. After an immediate second try, it will use an exponential backoff starting with one second. @@ -171,7 +180,7 @@ def flaky(caller=None, condition=True, attempts=4): @flaky def test_sometimes_works(self): pass - ''' + """ if caller is None: return functools.partial(flaky, condition=condition, attempts=attempts) @@ -184,13 +193,17 @@ def flaky(caller=None, condition=True, attempts=4): return caller if inspect.isclass(caller): - attrs = [n for n in dir(caller) if n.startswith('test_')] + attrs = [n for n in dir(caller) if n.startswith("test_")] for attrname in attrs: try: function = getattr(caller, attrname) if not inspect.isfunction(function) and not inspect.ismethod(function): continue - setattr(caller, attrname, flaky(caller=function, condition=condition, attempts=attempts)) + setattr( + caller, + attrname, + flaky(caller=function, condition=condition, attempts=attempts), + ) except Exception as exc: # pylint: disable=broad-except log.exception(exc) continue @@ -204,7 +217,7 @@ def flaky(caller=None, condition=True, attempts=4): # Run through setUp again # We only run it after the first iteration(>0) because the regular # test runner will have already ran setUp the first time - setup = getattr(cls, 'setUp', None) + setup = getattr(cls, "setUp", None) if callable(setup): setup() return caller(cls) @@ -214,28 +227,28 @@ def flaky(caller=None, condition=True, attempts=4): exc_info = sys.exc_info() if isinstance(exc, SkipTest): six.reraise(*exc_info) - if not isinstance(exc, AssertionError) and log.isEnabledFor(logging.DEBUG): + if not isinstance(exc, AssertionError) and log.isEnabledFor( + logging.DEBUG + ): log.exception(exc, exc_info=exc_info) - if attempt >= attempts -1: + if attempt >= attempts - 1: # We won't try to run tearDown once the attempts are exhausted # because the regular test runner will do that for us six.reraise(*exc_info) # Run through tearDown again - teardown = getattr(cls, 'tearDown', None) + teardown = getattr(cls, "tearDown", None) if callable(teardown): teardown() backoff_time = attempt ** 2 - log.info( - 'Found Exception. Waiting %s seconds to retry.', - backoff_time - ) + log.info("Found Exception. Waiting %s seconds to retry.", backoff_time) time.sleep(backoff_time) return cls + return wrap def requires_sshd_server(caller): - ''' + """ Mark a test as requiring the tests SSH daemon running. .. code-block:: python @@ -245,42 +258,49 @@ def requires_sshd_server(caller): @requiresSshdServer def test_create_user(self): pass - ''' + """ if inspect.isclass(caller): # We're decorating a class - old_setup = getattr(caller, 'setUp', None) + old_setup = getattr(caller, "setUp", None) def setUp(self, *args, **kwargs): - if os.environ.get('SSH_DAEMON_RUNNING', 'False').lower() == 'false': - self.skipTest('SSH tests are disabled') + if os.environ.get("SSH_DAEMON_RUNNING", "False").lower() == "false": + self.skipTest("SSH tests are disabled") if old_setup is not None: old_setup(self, *args, **kwargs) + caller.setUp = setUp return caller # We're simply decorating functions @functools.wraps(caller) def wrap(cls): - if os.environ.get('SSH_DAEMON_RUNNING', 'False').lower() == 'false': - cls.skipTest('SSH tests are disabled') + if os.environ.get("SSH_DAEMON_RUNNING", "False").lower() == "false": + cls.skipTest("SSH tests are disabled") return caller(cls) + return wrap class RedirectStdStreams(object): - ''' + """ Temporarily redirect system output to file like objects. Default is to redirect to `os.devnull`, which just mutes output, `stdout` and `stderr`. - ''' + """ def __init__(self, stdout=None, stderr=None): # Late import import salt.utils.files + if stdout is None: - stdout = salt.utils.files.fopen(os.devnull, 'w') # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + stdout = salt.utils.files.fopen(os.devnull, "w") + # pylint: enable=resource-leakage if stderr is None: - stderr = salt.utils.files.fopen(os.devnull, 'w') # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + stderr = salt.utils.files.fopen(os.devnull, "w") + # pylint: enable=resource-leakage self.__stdout = stdout self.__stderr = stderr @@ -332,7 +352,7 @@ class RedirectStdStreams(object): class TstSuiteLoggingHandler(object): - ''' + """ Simple logging handler which can be used to test if certain logging messages get emitted or not: @@ -342,8 +362,9 @@ class TstSuiteLoggingHandler(object): # (...) Do what ever you wish here handler.messages # here are the emitted log messages - ''' - def __init__(self, level=0, format='%(levelname)s:%(message)s'): + """ + + def __init__(self, level=0, format="%(levelname)s:%(message)s"): self.level = level self.format = format self.activated = False @@ -415,7 +436,7 @@ class TstSuiteLoggingHandler(object): class ForceImportErrorOn(object): - ''' + """ This class is meant to be used in mock'ed test cases which require an ``ImportError`` to be raised. @@ -456,7 +477,8 @@ class ForceImportErrorOn(object): 'Forced ImportError raised for {0!r}'.format(name) ImportError: Forced ImportError raised for 'os.path' >>> - ''' + """ + def __init__(self, *module_names): self.__module_names = {} for entry in module_names: @@ -466,7 +488,7 @@ class ForceImportErrorOn(object): else: self.__module_names[entry] = None self.__original_import = builtins.__import__ - self.patcher = patch.object(builtins, '__import__', self.__fake_import__) + self.patcher = patch.object(builtins, "__import__", self.__fake_import__) def patch_import_function(self): self.patcher.start() @@ -474,12 +496,9 @@ class ForceImportErrorOn(object): def restore_import_funtion(self): self.patcher.stop() - def __fake_import__(self, - name, - globals_=None, - locals_=None, - fromlist=None, - level=None): + def __fake_import__( + self, name, globals_=None, locals_=None, fromlist=None, level=None + ): if six.PY2: if globals_ is None: globals_ = {} @@ -497,16 +516,12 @@ class ForceImportErrorOn(object): if name in self.__module_names: importerror_fromlist = self.__module_names.get(name) if importerror_fromlist is None: - raise ImportError( - 'Forced ImportError raised for {0!r}'.format(name) - ) + raise ImportError("Forced ImportError raised for {0!r}".format(name)) if importerror_fromlist.intersection(set(fromlist)): raise ImportError( - 'Forced ImportError raised for {0!r}'.format( - 'from {0} import {1}'.format( - name, ', '.join(fromlist) - ) + "Forced ImportError raised for {0!r}".format( + "from {0} import {1}".format(name, ", ".join(fromlist)) ) ) return self.__original_import(name, globals_, locals_, fromlist, level) @@ -520,7 +535,7 @@ class ForceImportErrorOn(object): class MockWraps(object): - ''' + """ Helper class to be used with the mock library. To be used in the ``wraps`` keyword of ``Mock`` or ``MagicMock`` where you want to trigger a side effect for X times, and afterwards, call the @@ -543,7 +558,8 @@ class MockWraps(object): original >>> - ''' + """ + def __init__(self, original, expected_failures, side_effect): self.__original = original self.__expected_failures = expected_failures @@ -562,10 +578,11 @@ class MockWraps(object): def requires_network(only_local_network=False): - ''' + """ Simple decorator which is supposed to skip a test case in case there's no network connection to the internet. - ''' + """ + def decorator(func): @functools.wraps(func) def wrapper(cls): @@ -576,10 +593,10 @@ def requires_network(only_local_network=False): pubsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) retsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) pubsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - pubsock.bind(('', 18000)) + pubsock.bind(("", 18000)) pubsock.close() retsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - retsock.bind(('', 18001)) + retsock.bind(("", 18001)) retsock.close() has_local_network = True except socket.error: @@ -587,15 +604,11 @@ def requires_network(only_local_network=False): try: pubsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) retsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - pubsock.setsockopt( - socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 - ) - pubsock.bind(('', 18000)) + pubsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + pubsock.bind(("", 18000)) pubsock.close() - retsock.setsockopt( - socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 - ) - retsock.bind(('', 18001)) + retsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + retsock.bind(("", 18001)) retsock.close() has_local_network = True except socket.error: @@ -606,15 +619,24 @@ def requires_network(only_local_network=False): if has_local_network is False: # Since we're only supposed to check local network, and no # local network was detected, skip the test - cls.skipTest('No local network was detected') + cls.skipTest("No local network was detected") return func(cls) # We are using the google.com DNS records as numerical IPs to avoid # DNS lookups which could greatly slow down this check - for addr in ('173.194.41.198', '173.194.41.199', '173.194.41.200', - '173.194.41.201', '173.194.41.206', '173.194.41.192', - '173.194.41.193', '173.194.41.194', '173.194.41.195', - '173.194.41.196', '173.194.41.197'): + for addr in ( + "173.194.41.198", + "173.194.41.199", + "173.194.41.200", + "173.194.41.201", + "173.194.41.206", + "173.194.41.192", + "173.194.41.193", + "173.194.41.194", + "173.194.41.195", + "173.194.41.196", + "173.194.41.197", + ): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.settimeout(0.25) @@ -625,16 +647,20 @@ def requires_network(only_local_network=False): # Let's check the next IP continue else: - cls.skipTest('No internet network connection was detected') + cls.skipTest("No internet network connection was detected") finally: sock.close() return func(cls) + return wrapper + return decorator -def with_system_user(username, on_existing='delete', delete=True, password=None, groups=None): - ''' +def with_system_user( + username, on_existing="delete", delete=True, password=None, groups=None +): + """ Create and optionally destroy a system user to be used within a test case. The system user is created using the ``user`` salt module. @@ -647,65 +673,52 @@ def with_system_user(username, on_existing='delete', delete=True, password=None, * nothing: Do nothing, act as if the user was created. * delete: delete and re-create the existing user * skip: skip the test case - ''' - if on_existing not in ('nothing', 'delete', 'skip'): + """ + if on_existing not in ("nothing", "delete", "skip"): raise RuntimeError( - 'The value of \'on_existing\' can only be one of, ' - '\'nothing\', \'delete\' and \'skip\'' + "The value of 'on_existing' can only be one of, " + "'nothing', 'delete' and 'skip'" ) if not isinstance(delete, bool): - raise RuntimeError( - 'The value of \'delete\' can only be \'True\' or \'False\'' - ) + raise RuntimeError("The value of 'delete' can only be 'True' or 'False'") def decorator(func): - @functools.wraps(func) def wrap(cls): # Let's add the user to the system. - log.debug('Creating system user {0!r}'.format(username)) - kwargs = {'timeout': 60, 'groups': groups} + log.debug("Creating system user {0!r}".format(username)) + kwargs = {"timeout": 60, "groups": groups} if salt.utils.platform.is_windows(): - kwargs.update({'password': password}) - create_user = cls.run_function('user.add', [username], **kwargs) + kwargs.update({"password": password}) + create_user = cls.run_function("user.add", [username], **kwargs) if not create_user: - log.debug('Failed to create system user') + log.debug("Failed to create system user") # The user was not created - if on_existing == 'skip': - cls.skipTest( - 'Failed to create system user {0!r}'.format( - username - ) - ) + if on_existing == "skip": + cls.skipTest("Failed to create system user {0!r}".format(username)) - if on_existing == 'delete': - log.debug( - 'Deleting the system user {0!r}'.format( - username - ) - ) + if on_existing == "delete": + log.debug("Deleting the system user {0!r}".format(username)) delete_user = cls.run_function( - 'user.delete', [username, True, True] + "user.delete", [username, True, True] ) if not delete_user: cls.skipTest( - 'A user named {0!r} already existed on the ' - 'system and re-creating it was not possible' - .format(username) + "A user named {0!r} already existed on the " + "system and re-creating it was not possible".format( + username + ) ) - log.debug( - 'Second time creating system user {0!r}'.format( - username - ) - ) - create_user = cls.run_function('user.add', [username], **kwargs) + log.debug("Second time creating system user {0!r}".format(username)) + create_user = cls.run_function("user.add", [username], **kwargs) if not create_user: cls.skipTest( - 'A user named {0!r} already existed, was deleted ' - 'as requested, but re-creating it was not possible' - .format(username) + "A user named {0!r} already existed, was deleted " + "as requested, but re-creating it was not possible".format( + username + ) ) failure = None @@ -714,10 +727,8 @@ def with_system_user(username, on_existing='delete', delete=True, password=None, return func(cls, username) except Exception as exc: # pylint: disable=W0703 log.error( - 'Running {0!r} raised an exception: {1}'.format( - func, exc - ), - exc_info=True + "Running {0!r} raised an exception: {1}".format(func, exc), + exc_info=True, ) # Store the original exception details which will be raised # a little further down the code @@ -725,29 +736,31 @@ def with_system_user(username, on_existing='delete', delete=True, password=None, finally: if delete: delete_user = cls.run_function( - 'user.delete', [username, True, True], timeout=60 + "user.delete", [username, True, True], timeout=60 ) if not delete_user: if failure is None: log.warning( - 'Although the actual test-case did not fail, ' - 'deleting the created system user {0!r} ' - 'afterwards did.'.format(username) + "Although the actual test-case did not fail, " + "deleting the created system user {0!r} " + "afterwards did.".format(username) ) else: log.warning( - 'The test-case failed and also did the removal' - ' of the system user {0!r}'.format(username) + "The test-case failed and also did the removal" + " of the system user {0!r}".format(username) ) if failure is not None: # If an exception was thrown, raise it six.reraise(failure[0], failure[1], failure[2]) + return wrap + return decorator -def with_system_group(group, on_existing='delete', delete=True): - ''' +def with_system_group(group, on_existing="delete", delete=True): + """ Create and optionally destroy a system group to be used within a test case. The system user is crated using the ``group`` salt module. @@ -760,56 +773,45 @@ def with_system_group(group, on_existing='delete', delete=True): * nothing: Do nothing, act as if the group was created * delete: delete and re-create the existing user * skip: skip the test case - ''' - if on_existing not in ('nothing', 'delete', 'skip'): + """ + if on_existing not in ("nothing", "delete", "skip"): raise RuntimeError( - 'The value of \'on_existing\' can only be one of, ' - '\'nothing\', \'delete\' and \'skip\'' + "The value of 'on_existing' can only be one of, " + "'nothing', 'delete' and 'skip'" ) if not isinstance(delete, bool): - raise RuntimeError( - 'The value of \'delete\' can only be \'True\' or \'False\'' - ) + raise RuntimeError("The value of 'delete' can only be 'True' or 'False'") def decorator(func): - @functools.wraps(func) def wrap(cls): # Let's add the user to the system. - log.debug('Creating system group {0!r}'.format(group)) - create_group = cls.run_function('group.add', [group]) + log.debug("Creating system group {0!r}".format(group)) + create_group = cls.run_function("group.add", [group]) if not create_group: - log.debug('Failed to create system group') + log.debug("Failed to create system group") # The group was not created - if on_existing == 'skip': - cls.skipTest( - 'Failed to create system group {0!r}'.format(group) - ) + if on_existing == "skip": + cls.skipTest("Failed to create system group {0!r}".format(group)) - if on_existing == 'delete': - log.debug( - 'Deleting the system group {0!r}'.format(group) - ) - delete_group = cls.run_function('group.delete', [group]) + if on_existing == "delete": + log.debug("Deleting the system group {0!r}".format(group)) + delete_group = cls.run_function("group.delete", [group]) if not delete_group: cls.skipTest( - 'A group named {0!r} already existed on the ' - 'system and re-creating it was not possible' - .format(group) + "A group named {0!r} already existed on the " + "system and re-creating it was not possible".format(group) ) - log.debug( - 'Second time creating system group {0!r}'.format( - group - ) - ) - create_group = cls.run_function('group.add', [group]) + log.debug("Second time creating system group {0!r}".format(group)) + create_group = cls.run_function("group.add", [group]) if not create_group: cls.skipTest( - 'A group named {0!r} already existed, was deleted ' - 'as requested, but re-creating it was not possible' - .format(group) + "A group named {0!r} already existed, was deleted " + "as requested, but re-creating it was not possible".format( + group + ) ) failure = None @@ -818,39 +820,38 @@ def with_system_group(group, on_existing='delete', delete=True): return func(cls, group) except Exception as exc: # pylint: disable=W0703 log.error( - 'Running {0!r} raised an exception: {1}'.format( - func, exc - ), - exc_info=True + "Running {0!r} raised an exception: {1}".format(func, exc), + exc_info=True, ) # Store the original exception details which will be raised # a little further down the code failure = sys.exc_info() finally: if delete: - delete_group = cls.run_function('group.delete', [group]) + delete_group = cls.run_function("group.delete", [group]) if not delete_group: if failure is None: log.warning( - 'Although the actual test-case did not fail, ' - 'deleting the created system group {0!r} ' - 'afterwards did.'.format(group) + "Although the actual test-case did not fail, " + "deleting the created system group {0!r} " + "afterwards did.".format(group) ) else: log.warning( - 'The test-case failed and also did the removal' - ' of the system group {0!r}'.format(group) + "The test-case failed and also did the removal" + " of the system group {0!r}".format(group) ) if failure is not None: # If an exception was thrown, raise it six.reraise(failure[0], failure[1], failure[2]) + return wrap + return decorator -def with_system_user_and_group(username, group, - on_existing='delete', delete=True): - ''' +def with_system_user_and_group(username, group, on_existing="delete", delete=True): + """ Create and optionally destroy a system user and group to be used within a test case. The system user is crated using the ``user`` salt module, and the system group is created with the ``group`` salt module. @@ -866,95 +867,74 @@ def with_system_user_and_group(username, group, * nothing: Do nothing, act as if the user was created. * delete: delete and re-create the existing user * skip: skip the test case - ''' - if on_existing not in ('nothing', 'delete', 'skip'): + """ + if on_existing not in ("nothing", "delete", "skip"): raise RuntimeError( - 'The value of \'on_existing\' can only be one of, ' - '\'nothing\', \'delete\' and \'skip\'' + "The value of 'on_existing' can only be one of, " + "'nothing', 'delete' and 'skip'" ) if not isinstance(delete, bool): - raise RuntimeError( - 'The value of \'delete\' can only be \'True\' or \'False\'' - ) + raise RuntimeError("The value of 'delete' can only be 'True' or 'False'") def decorator(func): - @functools.wraps(func) def wrap(cls): # Let's add the user to the system. - log.debug('Creating system user {0!r}'.format(username)) - create_user = cls.run_function('user.add', [username]) - log.debug('Creating system group {0!r}'.format(group)) - create_group = cls.run_function('group.add', [group]) + log.debug("Creating system user {0!r}".format(username)) + create_user = cls.run_function("user.add", [username]) + log.debug("Creating system group {0!r}".format(group)) + create_group = cls.run_function("group.add", [group]) if not create_user: - log.debug('Failed to create system user') + log.debug("Failed to create system user") # The user was not created - if on_existing == 'skip': - cls.skipTest( - 'Failed to create system user {0!r}'.format( - username - ) - ) + if on_existing == "skip": + cls.skipTest("Failed to create system user {0!r}".format(username)) - if on_existing == 'delete': - log.debug( - 'Deleting the system user {0!r}'.format( - username - ) - ) + if on_existing == "delete": + log.debug("Deleting the system user {0!r}".format(username)) delete_user = cls.run_function( - 'user.delete', [username, True, True] + "user.delete", [username, True, True] ) if not delete_user: cls.skipTest( - 'A user named {0!r} already existed on the ' - 'system and re-creating it was not possible' - .format(username) + "A user named {0!r} already existed on the " + "system and re-creating it was not possible".format( + username + ) ) - log.debug( - 'Second time creating system user {0!r}'.format( - username - ) - ) - create_user = cls.run_function('user.add', [username]) + log.debug("Second time creating system user {0!r}".format(username)) + create_user = cls.run_function("user.add", [username]) if not create_user: cls.skipTest( - 'A user named {0!r} already existed, was deleted ' - 'as requested, but re-creating it was not possible' - .format(username) + "A user named {0!r} already existed, was deleted " + "as requested, but re-creating it was not possible".format( + username + ) ) if not create_group: - log.debug('Failed to create system group') + log.debug("Failed to create system group") # The group was not created - if on_existing == 'skip': - cls.skipTest( - 'Failed to create system group {0!r}'.format(group) - ) + if on_existing == "skip": + cls.skipTest("Failed to create system group {0!r}".format(group)) - if on_existing == 'delete': - log.debug( - 'Deleting the system group {0!r}'.format(group) - ) - delete_group = cls.run_function('group.delete', [group]) + if on_existing == "delete": + log.debug("Deleting the system group {0!r}".format(group)) + delete_group = cls.run_function("group.delete", [group]) if not delete_group: cls.skipTest( - 'A group named {0!r} already existed on the ' - 'system and re-creating it was not possible' - .format(group) + "A group named {0!r} already existed on the " + "system and re-creating it was not possible".format(group) ) - log.debug( - 'Second time creating system group {0!r}'.format( - group - ) - ) - create_group = cls.run_function('group.add', [group]) + log.debug("Second time creating system group {0!r}".format(group)) + create_group = cls.run_function("group.add", [group]) if not create_group: cls.skipTest( - 'A group named {0!r} already existed, was deleted ' - 'as requested, but re-creating it was not possible' - .format(group) + "A group named {0!r} already existed, was deleted " + "as requested, but re-creating it was not possible".format( + group + ) ) failure = None @@ -963,10 +943,8 @@ def with_system_user_and_group(username, group, return func(cls, username, group) except Exception as exc: # pylint: disable=W0703 log.error( - 'Running {0!r} raised an exception: {1}'.format( - func, exc - ), - exc_info=True + "Running {0!r} raised an exception: {1}".format(func, exc), + exc_info=True, ) # Store the original exception details which will be raised # a little further down the code @@ -974,53 +952,57 @@ def with_system_user_and_group(username, group, finally: if delete: delete_user = cls.run_function( - 'user.delete', [username, True, True] + "user.delete", [username, True, True] ) - delete_group = cls.run_function('group.delete', [group]) + delete_group = cls.run_function("group.delete", [group]) if not delete_user: if failure is None: log.warning( - 'Although the actual test-case did not fail, ' - 'deleting the created system user {0!r} ' - 'afterwards did.'.format(username) + "Although the actual test-case did not fail, " + "deleting the created system user {0!r} " + "afterwards did.".format(username) ) else: log.warning( - 'The test-case failed and also did the removal' - ' of the system user {0!r}'.format(username) + "The test-case failed and also did the removal" + " of the system user {0!r}".format(username) ) if not delete_group: if failure is None: log.warning( - 'Although the actual test-case did not fail, ' - 'deleting the created system group {0!r} ' - 'afterwards did.'.format(group) + "Although the actual test-case did not fail, " + "deleting the created system group {0!r} " + "afterwards did.".format(group) ) else: log.warning( - 'The test-case failed and also did the removal' - ' of the system group {0!r}'.format(group) + "The test-case failed and also did the removal" + " of the system group {0!r}".format(group) ) if failure is not None: # If an exception was thrown, raise it six.reraise(failure[0], failure[1], failure[2]) + return wrap + return decorator class WithTempfile(object): def __init__(self, **kwargs): - self.create = kwargs.pop('create', True) - if 'dir' not in kwargs: - kwargs['dir'] = RUNTIME_VARS.TMP - if 'prefix' not in kwargs: - kwargs['prefix'] = '__salt.test.' + self.create = kwargs.pop("create", True) + if "dir" not in kwargs: + kwargs["dir"] = RUNTIME_VARS.TMP + if "prefix" not in kwargs: + kwargs["prefix"] = "__salt.test." self.kwargs = kwargs def __call__(self, func): self.func = func return functools.wraps(func)( - lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) # pylint: disable=W0108 + # pylint: disable=unnecessary-lambda + lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) + # pylint: enable=unnecessary-lambda ) def wrap(self, testcase, *args, **kwargs): @@ -1041,15 +1023,17 @@ with_tempfile = WithTempfile class WithTempdir(object): def __init__(self, **kwargs): - self.create = kwargs.pop('create', True) - if 'dir' not in kwargs: - kwargs['dir'] = RUNTIME_VARS.TMP + self.create = kwargs.pop("create", True) + if "dir" not in kwargs: + kwargs["dir"] = RUNTIME_VARS.TMP self.kwargs = kwargs def __call__(self, func): self.func = func return functools.wraps(func)( - lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) # pylint: disable=W0108 + # pylint: disable=unnecessary-lambda + lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) + # pylint: enable=unnecessary-lambda ) def wrap(self, testcase, *args, **kwargs): @@ -1066,79 +1050,105 @@ with_tempdir = WithTempdir def requires_system_grains(func): - ''' + """ Function decorator which loads and passes the system's grains to the test case. - ''' + """ + @functools.wraps(func) def decorator(*args, **kwargs): - if not hasattr(requires_system_grains, '__grains__'): + if not hasattr(requires_system_grains, "__grains__"): # Late import from tests.support.sminion import build_minion_opts - opts = build_minion_opts(minion_id='runtests-internal-sminion') + + opts = build_minion_opts(minion_id="runtests-internal-sminion") requires_system_grains.__grains__ = salt.loader.grains(opts) - kwargs['grains'] = requires_system_grains.__grains__ + kwargs["grains"] = requires_system_grains.__grains__ return func(*args, **kwargs) + return decorator @requires_system_grains def runs_on(grains=None, **kwargs): - ''' + """ Skip the test if grains don't match the values passed into **kwargs if a kwarg value is a list then skip if the grains don't match any item in the list - ''' + """ + def decorator(caller): @functools.wraps(caller) def wrapper(cls): for kw, value in kwargs.items(): if isinstance(value, list): - if not any(str(grains.get(kw)).lower() != str(v).lower() for v in value): - cls.skipTest('This test does not run on {}={}'.format(kw, grains.get(kw))) + if not any( + str(grains.get(kw)).lower() != str(v).lower() for v in value + ): + cls.skipTest( + "This test does not run on {}={}".format(kw, grains.get(kw)) + ) else: if str(grains.get(kw)).lower() != str(value).lower(): - cls.skipTest('This test runs on {}={}, not {}'.format(kw, value, grains.get(kw))) + cls.skipTest( + "This test runs on {}={}, not {}".format( + kw, value, grains.get(kw) + ) + ) return caller(cls) + return wrapper + return decorator @requires_system_grains def not_runs_on(grains=None, **kwargs): - ''' + """ Reverse of `runs_on`. Skip the test if any grains match the values passed into **kwargs if a kwarg value is a list then skip if the grains match any item in the list - ''' + """ + def decorator(caller): @functools.wraps(caller) def wrapper(cls): for kw, value in kwargs.items(): if isinstance(value, list): - if any(str(grains.get(kw)).lower() == str(v).lower() for v in value): - cls.skipTest('This test does not run on {}={}'.format(kw, grains.get(kw))) + if any( + str(grains.get(kw)).lower() == str(v).lower() for v in value + ): + cls.skipTest( + "This test does not run on {}={}".format(kw, grains.get(kw)) + ) else: if str(grains.get(kw)).lower() == str(value).lower(): - cls.skipTest('This test does not run on {}={}, got {}'.format(kw, value, grains.get(kw))) + cls.skipTest( + "This test does not run on {}={}, got {}".format( + kw, value, grains.get(kw) + ) + ) return caller(cls) + return wrapper + return decorator def _check_required_sminion_attributes(sminion_attr, *required_items): - ''' + """ :param sminion_attr: The name of the sminion attribute to check, such as 'functions' or 'states' :param required_items: The items that must be part of the designated sminion attribute for the decorated test :return The packages that are not available - ''' + """ # Late import from tests.support.sminion import create_sminion + required_salt_items = set(required_items) - sminion = create_sminion(minion_id='runtests-internal-sminion') + sminion = create_sminion(minion_id="runtests-internal-sminion") available_items = list(getattr(sminion, sminion_attr)) not_available_items = set() - name = '__not_available_{items}s__'.format(items=sminion_attr) + name = "__not_available_{items}s__".format(items=sminion_attr) if not hasattr(sminion, name): setattr(sminion, name, set()) @@ -1151,8 +1161,8 @@ def _check_required_sminion_attributes(sminion_attr, *required_items): for required_item_name in required_salt_items: search_name = required_item_name - if '.' not in search_name: - search_name += '.*' + if "." not in search_name: + search_name += ".*" if not fnmatch.filter(available_items, search_name): not_available_items.add(required_item_name) cached_not_available_items.add(required_item_name) @@ -1161,21 +1171,21 @@ def _check_required_sminion_attributes(sminion_attr, *required_items): def requires_salt_states(*names): - ''' + """ Makes sure the passed salt state is available. Skips the test if not .. versionadded:: 3000 - ''' - not_available = _check_required_sminion_attributes('states', *names) + """ + not_available = _check_required_sminion_attributes("states", *names) def decorator(caller): if inspect.isclass(caller): # We're decorating a class - old_setup = getattr(caller, 'setUp', None) + old_setup = getattr(caller, "setUp", None) def setUp(self, *args, **kwargs): if not_available: - raise SkipTest('Unavailable salt states: {}'.format(*not_available)) + raise SkipTest("Unavailable salt states: {}".format(*not_available)) if old_setup is not None: old_setup(self, *args, **kwargs) @@ -1187,7 +1197,7 @@ def requires_salt_states(*names): @functools.wraps(caller) def wrapper(cls): if not_available: - raise SkipTest('Unavailable salt states: {}'.format(*not_available)) + raise SkipTest("Unavailable salt states: {}".format(*not_available)) return caller(cls) return wrapper @@ -1196,21 +1206,23 @@ def requires_salt_states(*names): def requires_salt_modules(*names): - ''' + """ Makes sure the passed salt module is available. Skips the test if not .. versionadded:: 0.5.2 - ''' - not_available = _check_required_sminion_attributes('functions', *names) + """ + not_available = _check_required_sminion_attributes("functions", *names) def decorator(caller): if inspect.isclass(caller): # We're decorating a class - old_setup = getattr(caller, 'setUp', None) + old_setup = getattr(caller, "setUp", None) def setUp(self, *args, **kwargs): if not_available: - raise SkipTest('Unavailable salt modules: {}'.format(*not_available)) + raise SkipTest( + "Unavailable salt modules: {}".format(*not_available) + ) if old_setup is not None: old_setup(self, *args, **kwargs) @@ -1221,7 +1233,7 @@ def requires_salt_modules(*names): @functools.wraps(caller) def wrapper(cls): if not_available: - raise SkipTest('Unavailable salt modules: {}'.format(*not_available)) + raise SkipTest("Unavailable salt modules: {}".format(*not_available)) return caller(cls) return wrapper @@ -1231,32 +1243,29 @@ def requires_salt_modules(*names): def skip_if_binaries_missing(*binaries, **kwargs): import salt.utils.path + if len(binaries) == 1: if isinstance(binaries[0], (list, tuple, set, frozenset)): binaries = binaries[0] - check_all = kwargs.pop('check_all', False) - message = kwargs.pop('message', None) + check_all = kwargs.pop("check_all", False) + message = kwargs.pop("message", None) if kwargs: raise RuntimeError( - 'The only supported keyword argument is \'check_all\' and ' - '\'message\'. Invalid keyword arguments: {0}'.format( - ', '.join(kwargs.keys()) - ) + "The only supported keyword argument is 'check_all' and " + "'message'. Invalid keyword arguments: {0}".format(", ".join(kwargs.keys())) ) if check_all: for binary in binaries: if salt.utils.path.which(binary) is None: return skip( - '{0}The {1!r} binary was not found'.format( - message and '{0}. '.format(message) or '', - binary + "{0}The {1!r} binary was not found".format( + message and "{0}. ".format(message) or "", binary ) ) elif salt.utils.path.which_bin(binaries) is None: return skip( - '{0}None of the following binaries was found: {1}'.format( - message and '{0}. '.format(message) or '', - ', '.join(binaries) + "{0}None of the following binaries was found: {1}".format( + message and "{0}. ".format(message) or "", ", ".join(binaries) ) ) return _id @@ -1265,24 +1274,29 @@ def skip_if_binaries_missing(*binaries, **kwargs): def skip_if_not_root(func): # Late import from tests.support.runtests import RUNTIME_VARS - if RUNTIME_VARS.PYTEST_SESSION: - setattr(func, '__skip_if_not_root__', True) - if not sys.platform.startswith('win'): + if RUNTIME_VARS.PYTEST_SESSION: + setattr(func, "__skip_if_not_root__", True) + + if not sys.platform.startswith("win"): if os.getuid() != 0: func.__unittest_skip__ = True - func.__unittest_skip_why__ = 'You must be logged in as root to run this test' + func.__unittest_skip_why__ = ( + "You must be logged in as root to run this test" + ) else: current_user = salt.utils.win_functions.get_current_user() - if current_user != 'SYSTEM': + if current_user != "SYSTEM": if not salt.utils.win_functions.is_admin(current_user): func.__unittest_skip__ = True - func.__unittest_skip_why__ = 'You must be logged in as an Administrator to run this test' + func.__unittest_skip_why__ = ( + "You must be logged in as an Administrator to run this test" + ) return func def repeat(caller=None, condition=True, times=5): - ''' + """ Repeat a test X amount of times until the first failure. .. code-block:: python @@ -1292,7 +1306,7 @@ def repeat(caller=None, condition=True, times=5): @repeat def test_sometimes_works(self): pass - ''' + """ if caller is None: return functools.partial(repeat, condition=condition, times=times) @@ -1305,13 +1319,17 @@ def repeat(caller=None, condition=True, times=5): return caller if inspect.isclass(caller): - attrs = [n for n in dir(caller) if n.startswith('test_')] + attrs = [n for n in dir(caller) if n.startswith("test_")] for attrname in attrs: try: function = getattr(caller, attrname) if not inspect.isfunction(function) and not inspect.ismethod(function): continue - setattr(caller, attrname, repeat(caller=function, condition=condition, times=times)) + setattr( + caller, + attrname, + repeat(caller=function, condition=condition, times=times), + ) except Exception as exc: # pylint: disable=broad-except log.exception(exc) continue @@ -1321,14 +1339,15 @@ def repeat(caller=None, condition=True, times=5): def wrap(cls): result = None for attempt in range(1, times + 1): - log.info('%s test run %d of %s times', cls, attempt, times) + log.info("%s test run %d of %s times", cls, attempt, times) caller(cls) return cls + return wrap def http_basic_auth(login_cb=lambda username, password: False): - ''' + """ A crude decorator to force a handler to request HTTP Basic Authentication Example usage: @@ -1338,25 +1357,24 @@ def http_basic_auth(login_cb=lambda username, password: False): @http_basic_auth(lambda u, p: u == 'foo' and p == 'bar') class AuthenticatedHandler(salt.ext.tornado.web.RequestHandler): pass - ''' + """ + def wrapper(handler_class): def wrap_execute(handler_execute): def check_auth(handler, kwargs): - auth = handler.request.headers.get('Authorization') + auth = handler.request.headers.get("Authorization") - if auth is None or not auth.startswith('Basic '): + if auth is None or not auth.startswith("Basic "): # No username/password entered yet, we need to return a 401 # and set the WWW-Authenticate header to request login. handler.set_status(401) - handler.set_header( - 'WWW-Authenticate', 'Basic realm=Restricted') + handler.set_header("WWW-Authenticate", "Basic realm=Restricted") else: # Strip the 'Basic ' from the beginning of the auth header # leaving the base64-encoded secret - username, password = \ - base64.b64decode(auth[6:]).split(':', 1) + username, password = base64.b64decode(auth[6:]).split(":", 1) if login_cb(username, password): # Authentication successful @@ -1376,11 +1394,12 @@ def http_basic_auth(login_cb=lambda username, password: False): handler_class._execute = wrap_execute(handler_class._execute) return handler_class + return wrapper def generate_random_name(prefix, size=6): - ''' + """ Generates a random name by combining the provided prefix with a randomly generated ascii string. @@ -1391,15 +1410,14 @@ def generate_random_name(prefix, size=6): size The number of characters to generate. Default: 6. - ''' - return prefix + ''.join( - random.choice(string.ascii_uppercase + string.digits) - for x in range(size) + """ + return prefix + "".join( + random.choice(string.ascii_uppercase + string.digits) for x in range(size) ) class Webserver(object): - ''' + """ Starts a tornado webserver on 127.0.0.1 on a random available port USAGE: @@ -1411,13 +1429,10 @@ class Webserver(object): webserver = Webserver('/path/to/web/root') webserver.start() webserver.stop() - ''' - def __init__(self, - root=None, - port=None, - wait=5, - handler=None): - ''' + """ + + def __init__(self, root=None, port=None, wait=5, handler=None): + """ root Root directory of webserver. If not passed, it will default to the location of the base environment of the integration suite's file @@ -1435,36 +1450,38 @@ class Webserver(object): Can be used to use a subclass of tornado.web.StaticFileHandler, such as when enforcing authentication with the http_basic_auth decorator. - ''' + """ if port is not None and not isinstance(port, six.integer_types): - raise ValueError('port must be an integer') + raise ValueError("port must be an integer") if root is None: root = RUNTIME_VARS.BASE_FILES try: self.root = os.path.realpath(root) except AttributeError: - raise ValueError('root must be a string') + raise ValueError("root must be a string") self.port = port self.wait = wait - self.handler = handler \ - if handler is not None \ - else salt.ext.tornado.web.StaticFileHandler + self.handler = ( + handler if handler is not None else salt.ext.tornado.web.StaticFileHandler + ) self.web_root = None def target(self): - ''' + """ Threading target which stands up the tornado application - ''' + """ self.ioloop = salt.ext.tornado.ioloop.IOLoop() self.ioloop.make_current() if self.handler == salt.ext.tornado.web.StaticFileHandler: self.application = salt.ext.tornado.web.Application( - [(r'/(.*)', self.handler, {'path': self.root})]) + [(r"/(.*)", self.handler, {"path": self.root})] + ) else: self.application = salt.ext.tornado.web.Application( - [(r'/(.*)', self.handler)]) + [(r"/(.*)", self.handler)] + ) self.application.listen(self.port) self.ioloop.start() @@ -1473,36 +1490,38 @@ class Webserver(object): if self.port is None: return False sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - return sock.connect_ex(('127.0.0.1', self.port)) == 0 + return sock.connect_ex(("127.0.0.1", self.port)) == 0 def url(self, path): - ''' + """ Convenience function which, given a file path, will return a URL that points to that path. If the path is relative, it will just be appended to self.web_root. - ''' + """ if self.web_root is None: - raise RuntimeError('Webserver instance has not been started') - err_msg = 'invalid path, must be either a relative path or a path ' \ - 'within {0}'.format(self.root) + raise RuntimeError("Webserver instance has not been started") + err_msg = ( + "invalid path, must be either a relative path or a path " + "within {0}".format(self.root) + ) try: - relpath = path \ - if not os.path.isabs(path) \ - else os.path.relpath(path, self.root) - if relpath.startswith('..' + os.sep): + relpath = ( + path if not os.path.isabs(path) else os.path.relpath(path, self.root) + ) + if relpath.startswith(".." + os.sep): raise ValueError(err_msg) - return '/'.join((self.web_root, relpath)) + return "/".join((self.web_root, relpath)) except AttributeError: raise ValueError(err_msg) def start(self): - ''' + """ Starts the webserver - ''' + """ if self.port is None: self.port = get_unused_localhost_port() - self.web_root = 'http://127.0.0.1:{0}'.format(self.port) + self.web_root = "http://127.0.0.1:{0}".format(self.port) self.server_thread = threading.Thread(target=self.target) self.server_thread.daemon = True @@ -1515,64 +1534,66 @@ class Webserver(object): time.sleep(1) else: raise Exception( - 'Failed to start tornado webserver on 127.0.0.1:{0} within ' - '{1} seconds'.format(self.port, self.wait) + "Failed to start tornado webserver on 127.0.0.1:{0} within " + "{1} seconds".format(self.port, self.wait) ) def stop(self): - ''' + """ Stops the webserver - ''' + """ self.ioloop.add_callback(self.ioloop.stop) self.server_thread.join() class SaveRequestsPostHandler(salt.ext.tornado.web.RequestHandler): - ''' + """ Save all requests sent to the server. - ''' + """ + received_requests = [] def post(self, *args): # pylint: disable=arguments-differ - ''' + """ Handle the post - ''' + """ self.received_requests.append(self.request) def data_received(self): # pylint: disable=arguments-differ - ''' + """ Streaming not used for testing - ''' + """ raise NotImplementedError() class MirrorPostHandler(salt.ext.tornado.web.RequestHandler): - ''' + """ Mirror a POST body back to the client - ''' + """ + def post(self, *args): # pylint: disable=arguments-differ - ''' + """ Handle the post - ''' + """ body = self.request.body - log.debug('Incoming body: %s Incoming args: %s', body, args) + log.debug("Incoming body: %s Incoming args: %s", body, args) self.write(body) def data_received(self): # pylint: disable=arguments-differ - ''' + """ Streaming not used for testing - ''' + """ raise NotImplementedError() def dedent(text, linesep=os.linesep): - ''' + """ A wrapper around textwrap.dedent that also sets line endings. - ''' + """ linesep = salt.utils.stringutils.to_unicode(linesep) unicode_text = textwrap.dedent(salt.utils.stringutils.to_unicode(text)) clean_text = linesep.join(unicode_text.splitlines()) - if unicode_text.endswith(u'\n'): + if unicode_text.endswith("\n"): clean_text += linesep if not isinstance(text, six.text_type): return salt.utils.stringutils.to_bytes(clean_text) @@ -1580,9 +1601,8 @@ def dedent(text, linesep=os.linesep): class PatchedEnviron(object): - def __init__(self, **kwargs): - self.cleanup_keys = kwargs.pop('__cleanup__', ()) + self.cleanup_keys = kwargs.pop("__cleanup__", ()) self.kwargs = kwargs self.original_environ = None @@ -1600,9 +1620,9 @@ class PatchedEnviron(object): for k in self.kwargs: key = k if isinstance(key, six.text_type): - key = key.encode('utf-8') + key = key.encode("utf-8") if isinstance(self.kwargs[k], six.text_type): - kwargs[k] = kwargs[k].encode('utf-8') + kwargs[k] = kwargs[k].encode("utf-8") clean_kwargs[key] = kwargs[k] self.kwargs = clean_kwargs @@ -1621,27 +1641,25 @@ class VirtualEnv(object): def __init__(self, venv_dir=None): self.venv_dir = venv_dir or tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) if salt.utils.platform.is_windows(): - self.venv_python = os.path.join(self.venv_dir, 'Scripts', 'python.exe') + self.venv_python = os.path.join(self.venv_dir, "Scripts", "python.exe") else: - self.venv_python = os.path.join(self.venv_dir, 'bin', 'python') + self.venv_python = os.path.join(self.venv_dir, "bin", "python") def __enter__(self): try: self._create_virtualenv() except subprocess.CalledProcessError: - raise AssertionError('Failed to create virtualenv') + raise AssertionError("Failed to create virtualenv") return self def __exit__(self, *args): shutil.rmtree(self.venv_dir, ignore_errors=True) def install(self, *args): - subprocess.check_call( - [self.venv_python, '-m', 'pip', 'install'] + list(args) - ) + subprocess.check_call([self.venv_python, "-m", "pip", "install"] + list(args)) def _get_real_python(self): - ''' + """ The reason why the virtualenv creation is proxied by this function is mostly because under windows, we can't seem to properly create a virtualenv off of another virtualenv(we can on linux) and also because, we really don't want to @@ -1649,25 +1667,24 @@ class VirtualEnv(object): from the original python. Also, on windows, we must also point to the virtualenv binary outside the existing virtualenv because it will fail otherwise - ''' + """ try: if salt.utils.platform.is_windows(): return os.path.join(sys.real_prefix, os.path.basename(sys.executable)) else: python_binary_names = [ - 'python{}.{}'.format(*sys.version_info), - 'python{}'.format(*sys.version_info), - 'python' + "python{}.{}".format(*sys.version_info), + "python{}".format(*sys.version_info), + "python", ] for binary_name in python_binary_names: - python = os.path.join(sys.real_prefix, 'bin', binary_name) + python = os.path.join(sys.real_prefix, "bin", binary_name) if os.path.exists(python): break else: raise AssertionError( - 'Couldn\'t find a python binary name under \'{}\' matching: {}'.format( - os.path.join(sys.real_prefix, 'bin'), - python_binary_names + "Couldn't find a python binary name under '{}' matching: {}".format( + os.path.join(sys.real_prefix, "bin"), python_binary_names ) ) return python @@ -1676,4 +1693,6 @@ class VirtualEnv(object): def _create_virtualenv(self): sminion = create_sminion() - sminion.functions.virtualenv.create(self.venv_dir, python=self._get_real_python()) + sminion.functions.virtualenv.create( + self.venv_dir, python=self._get_real_python() + ) diff --git a/tests/support/jinja_filters.py b/tests/support/jinja_filters.py index 0091063afe8..d4714e0e105 100644 --- a/tests/support/jinja_filters.py +++ b/tests/support/jinja_filters.py @@ -2,752 +2,664 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os -from tests.support.unit import skipIf - -import salt.utils.platform import salt.utils.files +import salt.utils.platform +from tests.support.unit import skipIf class JinjaFiltersTest(object): - ''' + """ testing Jinja filters are available via state system - ''' + """ def test_data_compare_dicts(self): - ''' + """ test jinja filter data.compare_dicts - ''' - _expected = {'ret': {'a': {'new': 'c', 'old': 'b'}}} + """ + _expected = {"ret": {"a": {"new": "c", "old": "b"}}} - ret = self.run_function('state.sls', - ['jinja_filters.data_compare_dicts']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + ret = self.run_function("state.sls", ["jinja_filters.data_compare_dicts"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_data_compare_lists(self): - ''' + """ test jinja filter data.compare_list - ''' - _expected = {'ret': {'old': ['b']}} - ret = self.run_function('state.sls', - ['jinja_filters.data_compare_lists']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": {"old": ["b"]}} + ret = self.run_function("state.sls", ["jinja_filters.data_compare_lists"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_data_decode_dict(self): - ''' + """ test jinja filter data.decode_dict - ''' - _expected = {'ret': {'a': 'b', 'c': 'd'}} - ret = self.run_function('state.sls', - ['jinja_filters.data_decode_dict']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + _expected = {"ret": {"a": "b", "c": "d"}} + ret = self.run_function("state.sls", ["jinja_filters.data_decode_dict"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_data_decode_list(self): - ''' + """ test jinja filter data.decode_list - ''' - _expected = {'ret': ['a', 'b', 'c', 'd']} - ret = self.run_function('state.sls', - ['jinja_filters.data_decode_list']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + _expected = {"ret": ["a", "b", "c", "d"]} + ret = self.run_function("state.sls", ["jinja_filters.data_decode_list"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_data_encode_dict(self): - ''' + """ test jinja filter data.encode_dict - ''' - _expected = {'ret': {'a': 'b', 'c': 'd'}} - ret = self.run_function('state.sls', - ['jinja_filters.data_encode_dict']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + _expected = {"ret": {"a": "b", "c": "d"}} + ret = self.run_function("state.sls", ["jinja_filters.data_encode_dict"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_data_encode_list(self): - ''' + """ test jinja filter data.encode_list - ''' - _expected = {'ret': ['a', 'b', 'c', 'd']} - ret = self.run_function('state.sls', - ['jinja_filters.data_encode_list']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + _expected = {"ret": ["a", "b", "c", "d"]} + ret = self.run_function("state.sls", ["jinja_filters.data_encode_list"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_data_exactly_n(self): - ''' + """ test jinja filter data.exactly_n - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.data_exactly_n']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.data_exactly_n"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_data_exactly_one(self): - ''' + """ test jinja filter data.exactly_one - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.data_exactly_one']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.data_exactly_one"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_data_is_iter(self): - ''' + """ test jinja filter data.is_iter - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.data_is_iter']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.data_is_iter"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_data_is_list(self): - ''' + """ test jinja filter data.is_list - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.data_is_list']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.data_is_list"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_data_mysql_to_dict(self): - ''' + """ test jinja filter data.mysql_to_dict - ''' - _expected = {'ret': {'show processlist': {'Info': 'show processlist', 'db': 'NULL', 'Host': 'localhost', 'State': 'init', 'Command': 'Query', 'User': 'root', 'Time': 0, 'Id': 7}}} - ret = self.run_function('state.sls', - ['jinja_filters.data_mysql_to_dict']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = { + "ret": { + "show processlist": { + "Info": "show processlist", + "db": "NULL", + "Host": "localhost", + "State": "init", + "Command": "Query", + "User": "root", + "Time": 0, + "Id": 7, + } + } + } + ret = self.run_function("state.sls", ["jinja_filters.data_mysql_to_dict"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_data_sorted_ignorecase(self): - ''' + """ test jinja filter data.softed_ignorecase - ''' - _expected = {'ret': ['Abcd', 'efgh', 'Ijk', 'lmno', 'Pqrs']} - ret = self.run_function('state.sls', - ['jinja_filters.data_sorted_ignorecase']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": ["Abcd", "efgh", "Ijk", "lmno", "Pqrs"]} + ret = self.run_function("state.sls", ["jinja_filters.data_sorted_ignorecase"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_data_substring_in_list(self): - ''' + """ test jinja filter data.substring_in_list - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.data_substring_in_list']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.data_substring_in_list"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_dateutils_strftime(self): - ''' + """ test jinja filter datautils.strftime - ''' - ret = self.run_function('state.sls', - ['jinja_filters.dateutils_strftime']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + ret = self.run_function("state.sls", ["jinja_filters.dateutils_strftime"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_files_is_binary(self): - ''' + """ test jinja filter files.is_binary - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.files_is_binary']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.files_is_binary"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_files_is_empty(self): - ''' + """ test jinja filter files.is_empty - ''' + """ try: if salt.utils.platform.is_windows(): - with salt.utils.files.fopen('c:\\empty_file', 'w') as fp: + with salt.utils.files.fopen("c:\\empty_file", "w") as fp: pass - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.files_is_empty']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.files_is_empty"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual( + ret["module_|-test_|-test.echo_|-run"]["changes"], _expected + ) finally: if salt.utils.platform.is_windows(): - os.remove('c:\\empty_file') + os.remove("c:\\empty_file") def test_files_is_text(self): - ''' + """ test jinja filter files.is_text - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.files_is_text']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.files_is_text"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_files_list_files(self): - ''' + """ test jinja filter files.list_files - ''' - ret = self.run_function('state.sls', - ['jinja_filters.files_list_files']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) + """ + ret = self.run_function("state.sls", ["jinja_filters.files_list_files"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) if salt.utils.platform.is_windows(): - self.assertIn('c:\\\\salt\\\\conf\\\\minion', - ret['module_|-test_|-test.echo_|-run']['changes']['ret']) + self.assertIn( + "c:\\\\salt\\\\conf\\\\minion", + ret["module_|-test_|-test.echo_|-run"]["changes"]["ret"], + ) else: - self.assertIn('/bin/ls', - ret['module_|-test_|-test.echo_|-run']['changes']['ret']) + self.assertIn( + "/bin/ls", ret["module_|-test_|-test.echo_|-run"]["changes"]["ret"] + ) def test_hashutils_base4_64decode(self): - ''' + """ test jinja filter hashutils.base64_64decode - ''' - _expected = {'ret': 'Salt Rocks!'} - ret = self.run_function('state.sls', - ['jinja_filters.hashutils_base4_64decode']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "Salt Rocks!"} + ret = self.run_function("state.sls", ["jinja_filters.hashutils_base4_64decode"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_hashutils_base4_64encode(self): - ''' + """ test jinja filter hashutils.base64_64encode - ''' - _expected = {'ret': 'U2FsdCBSb2NrcyE='} - ret = self.run_function('state.sls', - ['jinja_filters.hashutils_base4_64encode']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "U2FsdCBSb2NrcyE="} + ret = self.run_function("state.sls", ["jinja_filters.hashutils_base4_64encode"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_hashutils_file_hashsum(self): - ''' + """ test jinja filter hashutils.file_hashsum - ''' - ret = self.run_function('state.sls', - ['jinja_filters.hashutils_file_hashsum']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + ret = self.run_function("state.sls", ["jinja_filters.hashutils_file_hashsum"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_hashutils_hmac(self): - ''' + """ test jinja filter hashutils.hmac - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.hashutils_hmac']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.hashutils_hmac"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_hashutils_md5_digest(self): - ''' + """ test jinja filter hashutils.md5_digest - ''' - _expected = {'ret': '85d6e71db655ee8e42c8b18475f0925f'} - ret = self.run_function('state.sls', - ['jinja_filters.hashutils_md5_digest']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "85d6e71db655ee8e42c8b18475f0925f"} + ret = self.run_function("state.sls", ["jinja_filters.hashutils_md5_digest"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_hashutils_random_hash(self): - ''' + """ test jinja filter hashutils.random_hash - ''' - ret = self.run_function('state.sls', - ['jinja_filters.hashutils_random_hash']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', - ret['module_|-test_|-test.echo_|-run']['changes']) + """ + ret = self.run_function("state.sls", ["jinja_filters.hashutils_random_hash"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_hashutils_sha256_digest(self): - ''' + """ test jinja filter hashutils.sha256_digest - ''' - _expected = {'ret': 'cce7fe00fd9cc6122fd3b2ed22feae215bcfe7ac4a7879d336c1993426a763fe'} - ret = self.run_function('state.sls', - ['jinja_filters.hashutils_sha256_digest']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = { + "ret": "cce7fe00fd9cc6122fd3b2ed22feae215bcfe7ac4a7879d336c1993426a763fe" + } + ret = self.run_function("state.sls", ["jinja_filters.hashutils_sha256_digest"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_hashutils_sha512_digest(self): - ''' + """ test jinja filter hashutils.sha512_digest - ''' - _expected = {'ret': '44d829491d8caa7039ad08a5b7fa9dd0f930138c614411c5326dd4755fdc9877ec6148219fccbe404139e7bb850e77237429d64f560c204f3697ab489fd8bfa5'} - ret = self.run_function('state.sls', - ['jinja_filters.hashutils_sha512_digest']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = { + "ret": "44d829491d8caa7039ad08a5b7fa9dd0f930138c614411c5326dd4755fdc9877ec6148219fccbe404139e7bb850e77237429d64f560c204f3697ab489fd8bfa5" + } + ret = self.run_function("state.sls", ["jinja_filters.hashutils_sha512_digest"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_http_query(self): - ''' + """ test jinja filter http.query - ''' - _expected = {'ret': {}} - ret = self.run_function('state.sls', - ['jinja_filters.http_query']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": {}} + ret = self.run_function("state.sls", ["jinja_filters.http_query"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_avg(self): - ''' + """ test jinja filter jinja.avg - ''' - ret = self.run_function('state.sls', - ['jinja_filters.jinja_avg']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + ret = self.run_function("state.sls", ["jinja_filters.jinja_avg"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_jinja_difference(self): - ''' + """ test jinja filter jinja.difference - ''' - _expected = {'ret': [1, 3]} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_difference']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": [1, 3]} + ret = self.run_function("state.sls", ["jinja_filters.jinja_difference"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_intersect(self): - ''' + """ test jinja filter jinja.intersect - ''' - _expected = {'ret': [2, 4]} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_intersect']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": [2, 4]} + ret = self.run_function("state.sls", ["jinja_filters.jinja_intersect"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_max(self): - ''' + """ test jinja filter jinja.max - ''' - _expected = {'ret': 4} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_max']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": 4} + ret = self.run_function("state.sls", ["jinja_filters.jinja_max"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_min(self): - ''' + """ test jinja filter jinja.min - ''' - _expected = {'ret': 1} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_min']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": 1} + ret = self.run_function("state.sls", ["jinja_filters.jinja_min"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_quote(self): - ''' + """ test jinja filter jinja.quote - ''' - _expected = {'ret': 'Salt Rocks!'} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_quote']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "Salt Rocks!"} + ret = self.run_function("state.sls", ["jinja_filters.jinja_quote"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_regex_escape(self): - ''' + """ test jinja filter jinja.regex_escape - ''' - _expected = {'ret': 'Salt\\ Rocks'} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_regex_escape']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "Salt\\ Rocks"} + ret = self.run_function("state.sls", ["jinja_filters.jinja_regex_escape"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_regex_match(self): - ''' + """ test jinja filter jinja.regex_match - ''' - _expected = {'ret': "('a', 'd')"} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_regex_match']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "('a', 'd')"} + ret = self.run_function("state.sls", ["jinja_filters.jinja_regex_match"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_regex_replace(self): - ''' + """ test jinja filter jinja.regex_replace - ''' - _expected = {'ret': 'lets__replace__spaces'} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_regex_replace']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "lets__replace__spaces"} + ret = self.run_function("state.sls", ["jinja_filters.jinja_regex_replace"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_regex_search(self): - ''' + """ test jinja filter jinja.regex_search - ''' - _expected = {'ret': "('a', 'd')"} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_regex_search']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "('a', 'd')"} + ret = self.run_function("state.sls", ["jinja_filters.jinja_regex_search"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_sequence(self): - ''' + """ test jinja filter jinja.sequence - ''' - _expected = {'ret': ['Salt Rocks!']} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_sequence']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": ["Salt Rocks!"]} + ret = self.run_function("state.sls", ["jinja_filters.jinja_sequence"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_skip(self): - ''' + """ test jinja filter jinja.skip - ''' - _expected = {'ret': None} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_skip']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": None} + ret = self.run_function("state.sls", ["jinja_filters.jinja_skip"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_symmetric_difference(self): - ''' + """ test jinja filter jinja.symmetric_difference - ''' - _expected = {'ret': [1, 3, 6]} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_symmetric_difference']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": [1, 3, 6]} + ret = self.run_function( + "state.sls", ["jinja_filters.jinja_symmetric_difference"] + ) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_to_bool(self): - ''' + """ test jinja filter jinja.to_bool - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_to_bool']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.jinja_to_bool"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_union(self): - ''' + """ test jinja filter jinja.union - ''' - _expected = {'ret': [1, 2, 3, 4, 6]} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_union']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": [1, 2, 3, 4, 6]} + ret = self.run_function("state.sls", ["jinja_filters.jinja_union"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_unique(self): - ''' + """ test jinja filter jinja.unique - ''' - _expected = {'ret': ['a', 'b', 'c']} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_unique']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": ["a", "b", "c"]} + ret = self.run_function("state.sls", ["jinja_filters.jinja_unique"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_jinja_uuid(self): - ''' + """ test jinja filter jinja.uuid - ''' - _expected = {'ret': '799192d9-7f32-5227-a45f-dfeb4a34e06f'} - ret = self.run_function('state.sls', - ['jinja_filters.jinja_uuid']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "799192d9-7f32-5227-a45f-dfeb4a34e06f"} + ret = self.run_function("state.sls", ["jinja_filters.jinja_uuid"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_network_gen_mac(self): - ''' + """ test jinja filter network.gen_mac - ''' - _expected = 'AC:DE:48:' - ret = self.run_function('state.sls', - ['jinja_filters.network_gen_mac']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertTrue(ret['module_|-test_|-test.echo_|-run']['changes']['ret'].startswith(_expected)) + """ + _expected = "AC:DE:48:" + ret = self.run_function("state.sls", ["jinja_filters.network_gen_mac"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertTrue( + ret["module_|-test_|-test.echo_|-run"]["changes"]["ret"].startswith( + _expected + ) + ) def test_network_ipaddr(self): - ''' + """ test jinja filter network.ipaddr - ''' - ret = self.run_function('state.sls', - ['jinja_filters.network_ipaddr']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + ret = self.run_function("state.sls", ["jinja_filters.network_ipaddr"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_network_ip_host(self): - ''' + """ test jinja filter network.ip_host - ''' - _expected = {'ret': '192.168.0.12/24'} - ret = self.run_function('state.sls', - ['jinja_filters.network_ip_host']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "192.168.0.12/24"} + ret = self.run_function("state.sls", ["jinja_filters.network_ip_host"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_network_ipv4(self): - ''' + """ test jinja filter network.ipv4 - ''' - _expected = {'ret': ['127.0.0.1']} - ret = self.run_function('state.sls', - ['jinja_filters.network_ipv4']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": ["127.0.0.1"]} + ret = self.run_function("state.sls", ["jinja_filters.network_ipv4"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_network_ipv6(self): - ''' + """ test jinja filter network.ipv6 - ''' - ret = self.run_function('state.sls', - ['jinja_filters.network_ipv6']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + ret = self.run_function("state.sls", ["jinja_filters.network_ipv6"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_network_is_ip(self): - ''' + """ test jinja filter network.is_ip - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.network_is_ip']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.network_is_ip"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_network_is_ipv4(self): - ''' + """ test jinja filter network.is_ipv4 - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.network_is_ipv4']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.network_is_ipv4"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_network_is_ipv6(self): - ''' + """ test jinja filter network.is_ipv6 - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.network_is_ipv6']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.network_is_ipv6"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_network_network_hosts(self): - ''' + """ test jinja filter network.network_hosts - ''' - _expected = {'ret': ['192.168.1.1', '192.168.1.2']} - ret = self.run_function('state.sls', - ['jinja_filters.network_network_hosts']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": ["192.168.1.1", "192.168.1.2"]} + ret = self.run_function("state.sls", ["jinja_filters.network_network_hosts"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_network_network_size(self): - ''' + """ test jinja filter network.network_size - ''' - _expected = {'ret': 16} - ret = self.run_function('state.sls', - ['jinja_filters.network_network_size']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": 16} + ret = self.run_function("state.sls", ["jinja_filters.network_network_size"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_path_join(self): - ''' + """ test jinja filter path.join - ''' - _expected = {'ret': os.path.sep + os.path.join('a', 'b', 'c', 'd')} - ret = self.run_function('state.sls', - ['jinja_filters.path_join']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": os.path.sep + os.path.join("a", "b", "c", "d")} + ret = self.run_function("state.sls", ["jinja_filters.path_join"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_path_which(self): - ''' + """ test jinja filter path.which - ''' - ret = self.run_function('state.sls', - ['jinja_filters.path_which']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + ret = self.run_function("state.sls", ["jinja_filters.path_which"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_stringutils_contains_whitespace(self): - ''' + """ test jinja filter stringutils.contains_whitespace - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.stringutils_contains_whitespace']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function( + "state.sls", ["jinja_filters.stringutils_contains_whitespace"] + ) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_stringutils_is_hex(self): - ''' + """ test jinja filter stringutils.is_hex - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.stringutils_is_hex']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function("state.sls", ["jinja_filters.stringutils_is_hex"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_stringutils_random_str(self): - ''' + """ test jinja filter stringutils.random_str - ''' - ret = self.run_function('state.sls', - ['jinja_filters.stringutils_random_str']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + ret = self.run_function("state.sls", ["jinja_filters.stringutils_random_str"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_stringutils_to_bytes(self): - ''' + """ test jinja filter stringutils.to_bytes - ''' - _expected = {'ret': 'Salt Rocks!'} - ret = self.run_function('state.sls', - ['jinja_filters.stringutils_to_bytes']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertIn('ret', ret['module_|-test_|-test.echo_|-run']['changes']) + """ + _expected = {"ret": "Salt Rocks!"} + ret = self.run_function("state.sls", ["jinja_filters.stringutils_to_bytes"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertIn("ret", ret["module_|-test_|-test.echo_|-run"]["changes"]) def test_stringutils_to_num(self): - ''' + """ test jinja filter stringutils.to_num - ''' - _expected = {'ret': 42} - ret = self.run_function('state.sls', - ['jinja_filters.stringutils_to_num']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": 42} + ret = self.run_function("state.sls", ["jinja_filters.stringutils_to_num"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_stringutils_whitelist_blacklist(self): - ''' + """ test jinja filter stringutils.whitelist_blacklist - ''' - _expected = {'ret': True} - ret = self.run_function('state.sls', - ['jinja_filters.stringutils_whitelist_blacklist']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": True} + ret = self.run_function( + "state.sls", ["jinja_filters.stringutils_whitelist_blacklist"] + ) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) - @skipIf(salt.utils.platform.is_windows(), 'Skip on windows') + @skipIf(salt.utils.platform.is_windows(), "Skip on windows") def test_user_get_uid(self): - ''' + """ test jinja filter user.get_uid - ''' - _expected = {'ret': 0} - ret = self.run_function('state.sls', - ['jinja_filters.user_get_uid']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": 0} + ret = self.run_function("state.sls", ["jinja_filters.user_get_uid"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_yamlencoding_yaml_dquote(self): - ''' + """ test jinja filter yamlencoding.yaml_dquote - ''' - _expected = {'ret': 'A double-quoted string in YAML'} - ret = self.run_function('state.sls', - ['jinja_filters.yamlencoding_yaml_dquote']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "A double-quoted string in YAML"} + ret = self.run_function("state.sls", ["jinja_filters.yamlencoding_yaml_dquote"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_yamlencoding_yaml_encode(self): - ''' + """ test jinja filter yamlencoding.yaml_encode - ''' - _expected = {'ret': 'An encoded string in YAML'} - ret = self.run_function('state.sls', - ['jinja_filters.yamlencoding_yaml_encode']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "An encoded string in YAML"} + ret = self.run_function("state.sls", ["jinja_filters.yamlencoding_yaml_encode"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_yamlencoding_yaml_squote(self): - ''' + """ test jinja filter yamlencoding.yaml_squote - ''' - _expected = {'ret': 'A single-quoted string in YAML'} - ret = self.run_function('state.sls', - ['jinja_filters.yamlencoding_yaml_squote']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "A single-quoted string in YAML"} + ret = self.run_function("state.sls", ["jinja_filters.yamlencoding_yaml_squote"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_yaml(self): - ''' + """ test yaml filter - ''' - _expected = {'ret': "{Question: 'Quieres Café?'}"} - ret = self.run_function('state.sls', ['jinja_filters.yaml']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": "{Question: 'Quieres Café?'}"} + ret = self.run_function("state.sls", ["jinja_filters.yaml"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) def test_json(self): - ''' + """ test json filter - ''' - _expected = {'ret': '{"Question": "Quieres Café?"}'} - ret = self.run_function('state.sls', ['jinja_filters.json']) - self.assertIn('module_|-test_|-test.echo_|-run', ret) - self.assertEqual(ret['module_|-test_|-test.echo_|-run']['changes'], - _expected) + """ + _expected = {"ret": '{"Question": "Quieres Café?"}'} + ret = self.run_function("state.sls", ["jinja_filters.json"]) + self.assertIn("module_|-test_|-test.echo_|-run", ret) + self.assertEqual(ret["module_|-test_|-test.echo_|-run"]["changes"], _expected) diff --git a/tests/support/kernelpkg.py b/tests/support/kernelpkg.py index 74028086176..dba75613104 100644 --- a/tests/support/kernelpkg.py +++ b/tests/support/kernelpkg.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Base class for kernelpkg modules :platform: Linux :maturity: develop .. versionadded:: 2018.3.0 -''' +""" # pylint: disable=invalid-name,no-member from __future__ import absolute_import, print_function, unicode_literals @@ -18,181 +18,241 @@ except ImportError: class KernelPkgTestCase(object): - ''' + """ Test cases shared by all kernelpkg virtual modules - ''' + """ def test_active(self): - ''' + """ Test - Return return the active kernel version - ''' + """ self.assertEqual(self._kernelpkg.active(), self.KERNEL_LIST[0]) def test_latest_available_no_results(self): - ''' + """ Test - Return the latest available kernel version - ''' - mock = MagicMock(return_value='') - with patch.dict(self._kernelpkg.__salt__, {'pkg.latest_version': mock}): - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[0]): - self.assertEqual(self._kernelpkg.latest_available(), self.KERNEL_LIST[-1]) + """ + mock = MagicMock(return_value="") + with patch.dict(self._kernelpkg.__salt__, {"pkg.latest_version": mock}): + with patch.object( + self._kernelpkg, "active", return_value=self.KERNEL_LIST[0] + ): + self.assertEqual( + self._kernelpkg.latest_available(), self.KERNEL_LIST[-1] + ) def test_latest_available_at_latest(self): - ''' + """ Test - Return the latest available kernel version - ''' + """ mock = MagicMock(return_value=self.LATEST) - with patch.dict(self._kernelpkg.__salt__, {'pkg.latest_version': mock}): - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - self.assertEqual(self._kernelpkg.latest_available(), self.KERNEL_LIST[-1]) + with patch.dict(self._kernelpkg.__salt__, {"pkg.latest_version": mock}): + with patch.object( + self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1] + ): + self.assertEqual( + self._kernelpkg.latest_available(), self.KERNEL_LIST[-1] + ) def test_latest_available_with_updates(self): - ''' + """ Test - Return the latest available kernel version - ''' + """ mock = MagicMock(return_value=self.LATEST) - with patch.dict(self._kernelpkg.__salt__, {'pkg.latest_version': mock}): - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[0]): - self.assertEqual(self._kernelpkg.latest_available(), self.KERNEL_LIST[-1]) + with patch.dict(self._kernelpkg.__salt__, {"pkg.latest_version": mock}): + with patch.object( + self._kernelpkg, "active", return_value=self.KERNEL_LIST[0] + ): + self.assertEqual( + self._kernelpkg.latest_available(), self.KERNEL_LIST[-1] + ) def test_latest_installed_with_updates(self): - ''' + """ Test - Return the latest installed kernel version - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[0]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): - self.assertEqual(self._kernelpkg.latest_installed(), self.KERNEL_LIST[-1]) + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[0]): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): + self.assertEqual( + self._kernelpkg.latest_installed(), self.KERNEL_LIST[-1] + ) def test_latest_installed_at_latest(self): - ''' + """ Test - Return the latest installed kernel version - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): - self.assertEqual(self._kernelpkg.latest_installed(), self.KERNEL_LIST[-1]) + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1]): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): + self.assertEqual( + self._kernelpkg.latest_installed(), self.KERNEL_LIST[-1] + ) def test_needs_reboot_with_update(self): - ''' + """ Test - Return True if a new kernel is ready to be booted - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[0]): - with patch.object(self._kernelpkg, 'latest_installed', return_value=self.KERNEL_LIST[1]): + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[0]): + with patch.object( + self._kernelpkg, "latest_installed", return_value=self.KERNEL_LIST[1] + ): self.assertTrue(self._kernelpkg.needs_reboot()) def test_needs_reboot_at_latest(self): - ''' + """ Test - Return True if a new kernel is ready to be booted - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[1]): - with patch.object(self._kernelpkg, 'latest_installed', return_value=self.KERNEL_LIST[1]): + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[1]): + with patch.object( + self._kernelpkg, "latest_installed", return_value=self.KERNEL_LIST[1] + ): self.assertFalse(self._kernelpkg.needs_reboot()) def test_needs_reboot_order_inverted(self): - ''' + """ Test - Return True if a new kernel is ready to be booted - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[1]): - with patch.object(self._kernelpkg, 'latest_installed', return_value=self.KERNEL_LIST[0]): + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[1]): + with patch.object( + self._kernelpkg, "latest_installed", return_value=self.KERNEL_LIST[0] + ): self.assertFalse(self._kernelpkg.needs_reboot()) def test_upgrade_not_needed_with_reboot(self): - ''' + """ Test - Upgrade function when no upgrade is available and reboot has been requested - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1]): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): result = self._kernelpkg.upgrade(reboot=True) - self.assertIn('upgrades', result) - self.assertEqual(result['active'], self.KERNEL_LIST[-1]) - self.assertEqual(result['latest_installed'], self.KERNEL_LIST[-1]) - self.assertEqual(result['reboot_requested'], True) - self.assertEqual(result['reboot_required'], False) - self._kernelpkg.__salt__['system.reboot'].assert_not_called() + self.assertIn("upgrades", result) + self.assertEqual(result["active"], self.KERNEL_LIST[-1]) + self.assertEqual(result["latest_installed"], self.KERNEL_LIST[-1]) + self.assertEqual(result["reboot_requested"], True) + self.assertEqual(result["reboot_required"], False) + self._kernelpkg.__salt__["system.reboot"].assert_not_called() def test_upgrade_not_needed_without_reboot(self): - ''' + """ Test - Upgrade function when no upgrade is available and no reboot has been requested - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1]): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): result = self._kernelpkg.upgrade(reboot=False) - self.assertIn('upgrades', result) - self.assertEqual(result['active'], self.KERNEL_LIST[-1]) - self.assertEqual(result['latest_installed'], self.KERNEL_LIST[-1]) - self.assertEqual(result['reboot_requested'], False) - self.assertEqual(result['reboot_required'], False) - self._kernelpkg.__salt__['system.reboot'].assert_not_called() + self.assertIn("upgrades", result) + self.assertEqual(result["active"], self.KERNEL_LIST[-1]) + self.assertEqual(result["latest_installed"], self.KERNEL_LIST[-1]) + self.assertEqual(result["reboot_requested"], False) + self.assertEqual(result["reboot_required"], False) + self._kernelpkg.__salt__["system.reboot"].assert_not_called() def test_upgrade_needed_with_reboot(self): - ''' + """ Test - Upgrade function when an upgrade is available and reboot has been requested - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[0]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[0]): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): result = self._kernelpkg.upgrade(reboot=True) - self.assertIn('upgrades', result) - self.assertEqual(result['active'], self.KERNEL_LIST[0]) - self.assertEqual(result['latest_installed'], self.KERNEL_LIST[-1]) - self.assertEqual(result['reboot_requested'], True) - self.assertEqual(result['reboot_required'], True) - self.assert_called_once(self._kernelpkg.__salt__['system.reboot']) + self.assertIn("upgrades", result) + self.assertEqual(result["active"], self.KERNEL_LIST[0]) + self.assertEqual(result["latest_installed"], self.KERNEL_LIST[-1]) + self.assertEqual(result["reboot_requested"], True) + self.assertEqual(result["reboot_required"], True) + self.assert_called_once(self._kernelpkg.__salt__["system.reboot"]) def test_upgrade_needed_without_reboot(self): - ''' + """ Test - Upgrade function when an upgrade is available and no reboot has been requested - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[0]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[0]): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): result = self._kernelpkg.upgrade(reboot=False) - self.assertIn('upgrades', result) - self.assertEqual(result['active'], self.KERNEL_LIST[0]) - self.assertEqual(result['latest_installed'], self.KERNEL_LIST[-1]) - self.assertEqual(result['reboot_requested'], False) - self.assertEqual(result['reboot_required'], True) - self._kernelpkg.__salt__['system.reboot'].assert_not_called() + self.assertIn("upgrades", result) + self.assertEqual(result["active"], self.KERNEL_LIST[0]) + self.assertEqual(result["latest_installed"], self.KERNEL_LIST[-1]) + self.assertEqual(result["reboot_requested"], False) + self.assertEqual(result["reboot_required"], True) + self._kernelpkg.__salt__["system.reboot"].assert_not_called() def test_upgrade_available_true(self): - ''' + """ Test - upgrade_available - ''' - with patch.object(self._kernelpkg, 'latest_available', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'latest_installed', return_value=self.KERNEL_LIST[0]): + """ + with patch.object( + self._kernelpkg, "latest_available", return_value=self.KERNEL_LIST[-1] + ): + with patch.object( + self._kernelpkg, "latest_installed", return_value=self.KERNEL_LIST[0] + ): self.assertTrue(self._kernelpkg.upgrade_available()) def test_upgrade_available_false(self): - ''' + """ Test - upgrade_available - ''' - with patch.object(self._kernelpkg, 'latest_available', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'latest_installed', return_value=self.KERNEL_LIST[-1]): + """ + with patch.object( + self._kernelpkg, "latest_available", return_value=self.KERNEL_LIST[-1] + ): + with patch.object( + self._kernelpkg, "latest_installed", return_value=self.KERNEL_LIST[-1] + ): self.assertFalse(self._kernelpkg.upgrade_available()) def test_upgrade_available_inverted(self): - ''' + """ Test - upgrade_available - ''' - with patch.object(self._kernelpkg, 'latest_available', return_value=self.KERNEL_LIST[0]): - with patch.object(self._kernelpkg, 'latest_installed', return_value=self.KERNEL_LIST[-1]): + """ + with patch.object( + self._kernelpkg, "latest_available", return_value=self.KERNEL_LIST[0] + ): + with patch.object( + self._kernelpkg, "latest_installed", return_value=self.KERNEL_LIST[-1] + ): self.assertFalse(self._kernelpkg.upgrade_available()) def test_remove_active(self): - ''' + """ Test - remove kernel package - ''' - mock = MagicMock(return_value={'retcode': 0, 'stderr': []}) - with patch.dict(self._kernelpkg.__salt__, {'cmd.run_all': mock}): - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): - self.assertRaises(CommandExecutionError, self._kernelpkg.remove, release=self.KERNEL_LIST[-1]) - self._kernelpkg.__salt__['cmd.run_all'].assert_not_called() + """ + mock = MagicMock(return_value={"retcode": 0, "stderr": []}) + with patch.dict(self._kernelpkg.__salt__, {"cmd.run_all": mock}): + with patch.object( + self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1] + ): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): + self.assertRaises( + CommandExecutionError, + self._kernelpkg.remove, + release=self.KERNEL_LIST[-1], + ) + self._kernelpkg.__salt__["cmd.run_all"].assert_not_called() def test_remove_invalid(self): - ''' + """ Test - remove kernel package - ''' - mock = MagicMock(return_value={'retcode': 0, 'stderr': []}) - with patch.dict(self._kernelpkg.__salt__, {'cmd.run_all': mock}): - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): - self.assertRaises(CommandExecutionError, self._kernelpkg.remove, release='invalid') - self._kernelpkg.__salt__['cmd.run_all'].assert_not_called() + """ + mock = MagicMock(return_value={"retcode": 0, "stderr": []}) + with patch.dict(self._kernelpkg.__salt__, {"cmd.run_all": mock}): + with patch.object( + self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1] + ): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): + self.assertRaises( + CommandExecutionError, self._kernelpkg.remove, release="invalid" + ) + self._kernelpkg.__salt__["cmd.run_all"].assert_not_called() diff --git a/tests/support/mixins.py b/tests/support/mixins.py index c54b8e7d6dd..c7a011647d2 100644 --- a/tests/support/mixins.py +++ b/tests/support/mixins.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) ============= @@ -7,81 +7,82 @@ ============= Some reusable class Mixins -''' +""" # pylint: disable=repr-flag-used-in-string # Import python libs from __future__ import absolute_import, print_function -import os -import sys + +import atexit import copy +import functools +import logging +import multiprocessing +import os +import pprint +import subprocess +import sys +import tempfile import time import types -import atexit -import pprint -import logging -import tempfile -import functools -import subprocess -import multiprocessing from collections import OrderedDict -# Import Salt Testing Libs -from tests.support.mock import patch -from tests.support.runtests import RUNTIME_VARS -from tests.support.paths import CODE_DIR - # Import salt libs import salt.config +import salt.exceptions import salt.utils.event import salt.utils.files import salt.utils.functools import salt.utils.path +import salt.utils.process import salt.utils.stringutils import salt.utils.yaml import salt.version -import salt.exceptions -import salt.utils.process -from salt.utils.verify import verify_env -from salt.utils.immutabletypes import freeze from salt._compat import ElementTree as etree # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import zip # pylint: disable=import-error,redefined-builtin -from salt.ext.six.moves.queue import Empty # pylint: disable=import-error,no-name-in-module +from salt.ext.six.moves.queue import ( # pylint: disable=import-error,no-name-in-module + Empty, +) +from salt.utils.immutabletypes import freeze +from salt.utils.verify import verify_env + +# Import Salt Testing Libs +from tests.support.mock import patch +from tests.support.paths import CODE_DIR +from tests.support.runtests import RUNTIME_VARS log = logging.getLogger(__name__) class CheckShellBinaryNameAndVersionMixin(object): - ''' + """ Simple class mix-in to subclass in companion to :class:`ShellTestCase<tests.support.case.ShellTestCase>` which adds a test case to verify proper version report from Salt's CLI tools. - ''' + """ _call_binary_ = None _call_binary_expected_version_ = None def test_version_includes_binary_name(self): - if getattr(self, '_call_binary_', None) is None: - self.skipTest('\'_call_binary_\' not defined.') + if getattr(self, "_call_binary_", None) is None: + self.skipTest("'_call_binary_' not defined.") if self._call_binary_expected_version_ is None: # Late import self._call_binary_expected_version_ = salt.version.__version__ - out = '\n'.join(self.run_script(self._call_binary_, '--version')) + out = "\n".join(self.run_script(self._call_binary_, "--version")) # Assert that the binary name is in the output try: self.assertIn(self._call_binary_, out) except AssertionError: # We might have generated the CLI scripts in which case we replace '-' with '_' - alternate_binary_name = self._call_binary_.replace('-', '_') - errmsg = 'Neither \'{}\' or \'{}\' were found as part of the binary name in:\n\'{}\''.format( - self._call_binary_, - alternate_binary_name, - out + alternate_binary_name = self._call_binary_.replace("-", "_") + errmsg = "Neither '{}' or '{}' were found as part of the binary name in:\n'{}'".format( + self._call_binary_, alternate_binary_name, out ) self.assertIn(alternate_binary_name, out, msg=errmsg) @@ -95,92 +96,106 @@ class AdaptedConfigurationTestCaseMixin(object): @staticmethod def get_temp_config(config_for, **config_overrides): - rootdir = config_overrides.get('root_dir', tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)) - conf_dir = config_overrides.pop('conf_dir', os.path.join(rootdir, 'conf')) - for key in ('cachedir', 'pki_dir', 'sock_dir'): + rootdir = config_overrides.get( + "root_dir", tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + ) + conf_dir = config_overrides.pop("conf_dir", os.path.join(rootdir, "conf")) + for key in ("cachedir", "pki_dir", "sock_dir"): if key not in config_overrides: config_overrides[key] = key - if 'log_file' not in config_overrides: - config_overrides['log_file'] = 'logs/{}.log'.format(config_for) - if 'user' not in config_overrides: - config_overrides['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - config_overrides['root_dir'] = rootdir + if "log_file" not in config_overrides: + config_overrides["log_file"] = "logs/{}.log".format(config_for) + if "user" not in config_overrides: + config_overrides["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + config_overrides["root_dir"] = rootdir - cdict = AdaptedConfigurationTestCaseMixin.get_config(config_for, from_scratch=True) + cdict = AdaptedConfigurationTestCaseMixin.get_config( + config_for, from_scratch=True + ) - if config_for in ('master', 'client_config'): + if config_for in ("master", "client_config"): rdict = salt.config.apply_master_config(config_overrides, cdict) - if config_for == 'minion': + if config_for == "minion": rdict = salt.config.apply_minion_config(config_overrides, cdict) - verify_env([os.path.join(rdict['pki_dir'], 'minions'), - os.path.join(rdict['pki_dir'], 'minions_pre'), - os.path.join(rdict['pki_dir'], 'minions_rejected'), - os.path.join(rdict['pki_dir'], 'minions_denied'), - os.path.join(rdict['cachedir'], 'jobs'), - os.path.join(rdict['cachedir'], 'tokens'), - os.path.join(rdict['root_dir'], 'cache', 'tokens'), - os.path.join(rdict['pki_dir'], 'accepted'), - os.path.join(rdict['pki_dir'], 'rejected'), - os.path.join(rdict['pki_dir'], 'pending'), - os.path.dirname(rdict['log_file']), - rdict['sock_dir'], - conf_dir - ], - RUNTIME_VARS.RUNNING_TESTS_USER, - root_dir=rdict['root_dir'], - ) + verify_env( + [ + os.path.join(rdict["pki_dir"], "minions"), + os.path.join(rdict["pki_dir"], "minions_pre"), + os.path.join(rdict["pki_dir"], "minions_rejected"), + os.path.join(rdict["pki_dir"], "minions_denied"), + os.path.join(rdict["cachedir"], "jobs"), + os.path.join(rdict["cachedir"], "tokens"), + os.path.join(rdict["root_dir"], "cache", "tokens"), + os.path.join(rdict["pki_dir"], "accepted"), + os.path.join(rdict["pki_dir"], "rejected"), + os.path.join(rdict["pki_dir"], "pending"), + os.path.dirname(rdict["log_file"]), + rdict["sock_dir"], + conf_dir, + ], + RUNTIME_VARS.RUNNING_TESTS_USER, + root_dir=rdict["root_dir"], + ) - rdict['conf_file'] = os.path.join(conf_dir, config_for) - with salt.utils.files.fopen(rdict['conf_file'], 'w') as wfh: + rdict["conf_file"] = os.path.join(conf_dir, config_for) + with salt.utils.files.fopen(rdict["conf_file"], "w") as wfh: salt.utils.yaml.safe_dump(rdict, wfh, default_flow_style=False) return rdict @staticmethod def get_config(config_for, from_scratch=False): if from_scratch: - if config_for in ('master', 'syndic_master', 'mm_master', 'mm_sub_master'): + if config_for in ("master", "syndic_master", "mm_master", "mm_sub_master"): return salt.config.master_config( AdaptedConfigurationTestCaseMixin.get_config_file_path(config_for) ) - elif config_for in ('minion', 'sub_minion'): + elif config_for in ("minion", "sub_minion"): return salt.config.minion_config( AdaptedConfigurationTestCaseMixin.get_config_file_path(config_for) ) - elif config_for in ('syndic',): + elif config_for in ("syndic",): return salt.config.syndic_config( AdaptedConfigurationTestCaseMixin.get_config_file_path(config_for), - AdaptedConfigurationTestCaseMixin.get_config_file_path('minion') + AdaptedConfigurationTestCaseMixin.get_config_file_path("minion"), ) - elif config_for == 'client_config': + elif config_for == "client_config": return salt.config.client_config( - AdaptedConfigurationTestCaseMixin.get_config_file_path('master') + AdaptedConfigurationTestCaseMixin.get_config_file_path("master") ) if config_for not in RUNTIME_VARS.RUNTIME_CONFIGS: - if config_for in ('master', 'syndic_master', 'mm_master', 'mm_sub_master'): + if config_for in ("master", "syndic_master", "mm_master", "mm_sub_master"): RUNTIME_VARS.RUNTIME_CONFIGS[config_for] = freeze( salt.config.master_config( - AdaptedConfigurationTestCaseMixin.get_config_file_path(config_for) + AdaptedConfigurationTestCaseMixin.get_config_file_path( + config_for + ) ) ) - elif config_for in ('minion', 'sub_minion'): + elif config_for in ("minion", "sub_minion"): RUNTIME_VARS.RUNTIME_CONFIGS[config_for] = freeze( salt.config.minion_config( - AdaptedConfigurationTestCaseMixin.get_config_file_path(config_for) + AdaptedConfigurationTestCaseMixin.get_config_file_path( + config_for + ) ) ) - elif config_for in ('syndic',): + elif config_for in ("syndic",): RUNTIME_VARS.RUNTIME_CONFIGS[config_for] = freeze( salt.config.syndic_config( - AdaptedConfigurationTestCaseMixin.get_config_file_path(config_for), - AdaptedConfigurationTestCaseMixin.get_config_file_path('minion') + AdaptedConfigurationTestCaseMixin.get_config_file_path( + config_for + ), + AdaptedConfigurationTestCaseMixin.get_config_file_path( + "minion" + ), ) ) - elif config_for == 'client_config': + elif config_for == "client_config": RUNTIME_VARS.RUNTIME_CONFIGS[config_for] = freeze( salt.config.client_config( - AdaptedConfigurationTestCaseMixin.get_config_file_path('master') + AdaptedConfigurationTestCaseMixin.get_config_file_path("master") ) ) return RUNTIME_VARS.RUNTIME_CONFIGS[config_for] @@ -190,72 +205,72 @@ class AdaptedConfigurationTestCaseMixin(object): return RUNTIME_VARS.TMP_CONF_DIR def get_config_dir(self): - log.warning('Use the config_dir attribute instead of calling get_config_dir()') + log.warning("Use the config_dir attribute instead of calling get_config_dir()") return self.config_dir @staticmethod def get_config_file_path(filename): - if filename == 'syndic_master': - return os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, 'master') - if filename == 'syndic': - return os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, 'minion') - if filename == 'sub_minion': - return os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, 'minion') - if filename == 'mm_master': - return os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, 'master') - if filename == 'mm_sub_master': - return os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, 'master') - if filename == 'mm_minion': - return os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, 'minion') - if filename == 'mm_sub_minion': - return os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, 'minion') + if filename == "syndic_master": + return os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, "master") + if filename == "syndic": + return os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, "minion") + if filename == "sub_minion": + return os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, "minion") + if filename == "mm_master": + return os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, "master") + if filename == "mm_sub_master": + return os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, "master") + if filename == "mm_minion": + return os.path.join(RUNTIME_VARS.TMP_MM_CONF_DIR, "minion") + if filename == "mm_sub_minion": + return os.path.join(RUNTIME_VARS.TMP_MM_SUB_CONF_DIR, "minion") return os.path.join(RUNTIME_VARS.TMP_CONF_DIR, filename) @property def master_opts(self): - ''' + """ Return the options used for the master - ''' - return self.get_config('master') + """ + return self.get_config("master") @property def minion_opts(self): - ''' + """ Return the options used for the minion - ''' - return self.get_config('minion') + """ + return self.get_config("minion") @property def sub_minion_opts(self): - ''' + """ Return the options used for the sub_minion - ''' - return self.get_config('sub_minion') + """ + return self.get_config("sub_minion") @property def mm_master_opts(self): - ''' + """ Return the options used for the multimaster master - ''' - return self.get_config('mm_master') + """ + return self.get_config("mm_master") @property def mm_sub_master_opts(self): - ''' + """ Return the options used for the multimaster sub-master - ''' - return self.get_config('mm_sub_master') + """ + return self.get_config("mm_sub_master") @property def mm_minion_opts(self): - ''' + """ Return the options used for the minion - ''' - return self.get_config('mm_minion') + """ + return self.get_config("mm_minion") class SaltClientTestCaseMixin(AdaptedConfigurationTestCaseMixin): - ''' + """ Mix-in class that provides a ``client`` attribute which returns a Salt :class:`LocalClient<salt:salt.client.LocalClient>`. @@ -278,21 +293,27 @@ class SaltClientTestCaseMixin(AdaptedConfigurationTestCaseMixin): {}, self.client._check_pub_data({'jid': '0'}), 'Passing JID of zero is not handled gracefully') - ''' - _salt_client_config_file_name_ = 'master' + """ + + _salt_client_config_file_name_ = "master" @property def client(self): # Late import import salt.client - if 'runtime_client' not in RUNTIME_VARS.RUNTIME_CONFIGS: - mopts = self.get_config(self._salt_client_config_file_name_, from_scratch=True) - RUNTIME_VARS.RUNTIME_CONFIGS['runtime_client'] = salt.client.get_local_client(mopts=mopts) - return RUNTIME_VARS.RUNTIME_CONFIGS['runtime_client'] + + if "runtime_client" not in RUNTIME_VARS.RUNTIME_CONFIGS: + mopts = self.get_config( + self._salt_client_config_file_name_, from_scratch=True + ) + RUNTIME_VARS.RUNTIME_CONFIGS[ + "runtime_client" + ] = salt.client.get_local_client(mopts=mopts) + return RUNTIME_VARS.RUNTIME_CONFIGS["runtime_client"] class SaltMultimasterClientTestCaseMixin(AdaptedConfigurationTestCaseMixin): - ''' + """ Mix-in class that provides a ``clients`` attribute which returns a list of Salt :class:`LocalClient<salt:salt.client.LocalClient>`. @@ -316,21 +337,23 @@ class SaltMultimasterClientTestCaseMixin(AdaptedConfigurationTestCaseMixin): {}, client._check_pub_data({'jid': '0'}), 'Passing JID of zero is not handled gracefully') - ''' - _salt_client_config_file_name_ = 'master' + """ + + _salt_client_config_file_name_ = "master" @property def clients(self): # Late import import salt.client - if 'runtime_clients' not in RUNTIME_VARS.RUNTIME_CONFIGS: - RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] = OrderedDict() - runtime_clients = RUNTIME_VARS.RUNTIME_CONFIGS['runtime_clients'] - for master_id in ('mm-master', 'mm-sub-master'): + if "runtime_clients" not in RUNTIME_VARS.RUNTIME_CONFIGS: + RUNTIME_VARS.RUNTIME_CONFIGS["runtime_clients"] = OrderedDict() + + runtime_clients = RUNTIME_VARS.RUNTIME_CONFIGS["runtime_clients"] + for master_id in ("mm-master", "mm-sub-master"): if master_id in runtime_clients: continue - mopts = self.get_config(master_id.replace('-', '_'), from_scratch=True) + mopts = self.get_config(master_id.replace("-", "_"), from_scratch=True) runtime_clients[master_id] = salt.client.get_local_client(mopts=mopts) return runtime_clients @@ -340,109 +363,124 @@ class ShellCaseCommonTestsMixin(CheckShellBinaryNameAndVersionMixin): _call_binary_expected_version_ = salt.version.__version__ def test_salt_with_git_version(self): - if getattr(self, '_call_binary_', None) is None: - self.skipTest('\'_call_binary_\' not defined.') + if getattr(self, "_call_binary_", None) is None: + self.skipTest("'_call_binary_' not defined.") from salt.version import __version_info__, SaltStackVersion - git = salt.utils.path.which('git') + + git = salt.utils.path.which("git") if not git: - self.skipTest('The git binary is not available') + self.skipTest("The git binary is not available") opts = { - 'stdout': subprocess.PIPE, - 'stderr': subprocess.PIPE, - 'cwd': CODE_DIR, + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + "cwd": CODE_DIR, } if not salt.utils.platform.is_windows(): - opts['close_fds'] = True + opts["close_fds"] = True # Let's get the output of git describe process = subprocess.Popen( - [git, 'describe', '--tags', '--first-parent', '--match', 'v[0-9]*'], - **opts + [git, "describe", "--tags", "--first-parent", "--match", "v[0-9]*"], **opts ) out, err = process.communicate() if process.returncode != 0: process = subprocess.Popen( - [git, 'describe', '--tags', '--match', 'v[0-9]*'], - **opts + [git, "describe", "--tags", "--match", "v[0-9]*"], **opts ) out, err = process.communicate() if not out: self.skipTest( - 'Failed to get the output of \'git describe\'. ' - 'Error: \'{0}\''.format( - salt.utils.stringutils.to_str(err) - ) + "Failed to get the output of 'git describe'. " + "Error: '{0}'".format(salt.utils.stringutils.to_str(err)) ) parsed_version = SaltStackVersion.parse(out) if parsed_version.info < __version_info__: self.skipTest( - 'We\'re likely about to release a new version. This test ' - 'would fail. Parsed(\'{0}\') < Expected(\'{1}\')'.format( + "We're likely about to release a new version. This test " + "would fail. Parsed('{0}') < Expected('{1}')".format( parsed_version.info, __version_info__ ) ) elif parsed_version.info != __version_info__: self.skipTest( - 'In order to get the proper salt version with the ' - 'git hash you need to update salt\'s local git ' - 'tags. Something like: \'git fetch --tags\' or ' - '\'git fetch --tags upstream\' if you followed ' - 'salt\'s contribute documentation. The version ' - 'string WILL NOT include the git hash.' + "In order to get the proper salt version with the " + "git hash you need to update salt's local git " + "tags. Something like: 'git fetch --tags' or " + "'git fetch --tags upstream' if you followed " + "salt's contribute documentation. The version " + "string WILL NOT include the git hash." ) - out = '\n'.join(self.run_script(self._call_binary_, '--version')) + out = "\n".join(self.run_script(self._call_binary_, "--version")) self.assertIn(parsed_version.string, out) class _FixLoaderModuleMockMixinMroOrder(type): - ''' + """ This metaclass will make sure that LoaderModuleMockMixin will always come as the first base class in order for LoaderModuleMockMixin.setUp to actually run - ''' + """ + def __new__(mcs, cls_name, cls_bases, cls_dict): - if cls_name == 'LoaderModuleMockMixin': - return super(_FixLoaderModuleMockMixinMroOrder, mcs).__new__(mcs, cls_name, cls_bases, cls_dict) + if cls_name == "LoaderModuleMockMixin": + return super(_FixLoaderModuleMockMixinMroOrder, mcs).__new__( + mcs, cls_name, cls_bases, cls_dict + ) bases = list(cls_bases) for idx, base in enumerate(bases): - if base.__name__ == 'LoaderModuleMockMixin': + if base.__name__ == "LoaderModuleMockMixin": bases.insert(0, bases.pop(idx)) break # Create the class instance - instance = super(_FixLoaderModuleMockMixinMroOrder, mcs).__new__(mcs, cls_name, tuple(bases), cls_dict) + instance = super(_FixLoaderModuleMockMixinMroOrder, mcs).__new__( + mcs, cls_name, tuple(bases), cls_dict + ) # Apply our setUp function decorator - instance.setUp = LoaderModuleMockMixin.__setup_loader_modules_mocks__(instance.setUp) + instance.setUp = LoaderModuleMockMixin.__setup_loader_modules_mocks__( + instance.setUp + ) return instance -class LoaderModuleMockMixin(six.with_metaclass(_FixLoaderModuleMockMixinMroOrder, object)): - ''' +class LoaderModuleMockMixin( + six.with_metaclass(_FixLoaderModuleMockMixinMroOrder, object) +): + """ This class will setup salt loader dunders. Please check `set_up_loader_mocks` above - ''' + """ # Define our setUp function decorator @staticmethod def __setup_loader_modules_mocks__(setup_func): - @functools.wraps(setup_func) def wrapper(self): loader_modules_configs = self.setup_loader_modules() if not isinstance(loader_modules_configs, dict): raise RuntimeError( - '{}.setup_loader_modules() must return a dictionary where the keys are the ' - 'modules that require loader mocking setup and the values, the global module ' - 'variables for each of the module being mocked. For example \'__salt__\', ' - '\'__opts__\', etc.'.format(self.__class__.__name__) + "{}.setup_loader_modules() must return a dictionary where the keys are the " + "modules that require loader mocking setup and the values, the global module " + "variables for each of the module being mocked. For example '__salt__', " + "'__opts__', etc.".format(self.__class__.__name__) ) salt_dunders = ( - '__opts__', '__salt__', '__runner__', '__context__', '__utils__', - '__ext_pillar__', '__thorium__', '__states__', '__serializers__', '__ret__', - '__grains__', '__pillar__', '__sdb__', + "__opts__", + "__salt__", + "__runner__", + "__context__", + "__utils__", + "__ext_pillar__", + "__thorium__", + "__states__", + "__serializers__", + "__ret__", + "__grains__", + "__pillar__", + "__sdb__", # Proxy is commented out on purpose since some code in salt expects a NameError # and is most of the time not a required dunder # '__proxy__' @@ -451,42 +489,48 @@ class LoaderModuleMockMixin(six.with_metaclass(_FixLoaderModuleMockMixinMroOrder for module, module_globals in six.iteritems(loader_modules_configs): if not isinstance(module, types.ModuleType): raise RuntimeError( - 'The dictionary keys returned by {}.setup_loader_modules() ' - 'must be an imported module, not {}'.format( - self.__class__.__name__, - type(module) + "The dictionary keys returned by {}.setup_loader_modules() " + "must be an imported module, not {}".format( + self.__class__.__name__, type(module) ) ) if not isinstance(module_globals, dict): raise RuntimeError( - 'The dictionary values returned by {}.setup_loader_modules() ' - 'must be a dictionary, not {}'.format( - self.__class__.__name__, - type(module_globals) + "The dictionary values returned by {}.setup_loader_modules() " + "must be a dictionary, not {}".format( + self.__class__.__name__, type(module_globals) ) ) - module_blacklisted_dunders = module_globals.pop('blacklisted_dunders', ()) + module_blacklisted_dunders = module_globals.pop( + "blacklisted_dunders", () + ) minion_funcs = {} - if '__salt__' in module_globals and module_globals['__salt__'] == 'autoload': - if '__opts__' not in module_globals: + if ( + "__salt__" in module_globals + and module_globals["__salt__"] == "autoload" + ): + if "__opts__" not in module_globals: raise RuntimeError( - 'You must provide \'__opts__\' on the {} module globals dictionary ' - 'to auto load the minion functions'.format(module.__name__) + "You must provide '__opts__' on the {} module globals dictionary " + "to auto load the minion functions".format(module.__name__) ) import salt.loader + ctx = {} - if '__utils__' not in module_globals: - utils = salt.loader.utils(module_globals['__opts__'], - context=module_globals.get('__context__') or ctx) - module_globals['__utils__'] = utils + if "__utils__" not in module_globals: + utils = salt.loader.utils( + module_globals["__opts__"], + context=module_globals.get("__context__") or ctx, + ) + module_globals["__utils__"] = utils minion_funcs = salt.loader.minion_mods( - module_globals['__opts__'], - context=module_globals.get('__context__') or ctx, - utils=module_globals.get('__utils__'), + module_globals["__opts__"], + context=module_globals.get("__context__") or ctx, + utils=module_globals.get("__utils__"), ) - module_globals['__salt__'] = minion_funcs + module_globals["__salt__"] = minion_funcs for dunder_name in salt_dunders: if dunder_name not in module_globals: @@ -494,11 +538,11 @@ class LoaderModuleMockMixin(six.with_metaclass(_FixLoaderModuleMockMixinMroOrder continue module_globals[dunder_name] = {} - sys_modules = module_globals.pop('sys.modules', None) + sys_modules = module_globals.pop("sys.modules", None) if sys_modules is not None: if not isinstance(sys_modules, dict): raise RuntimeError( - '\'sys.modules\' must be a dictionary not: {}'.format( + "'sys.modules' must be a dictionary not: {}".format( type(sys_modules) ) ) @@ -534,28 +578,29 @@ class LoaderModuleMockMixin(six.with_metaclass(_FixLoaderModuleMockMixinMroOrder # Since we autoloaded the minion_funcs, let's namespace the functions with the globals # used to patch above import salt.utils + for func in minion_funcs: minion_funcs[func] = salt.utils.functools.namespaced_function( - minion_funcs[func], - module_globals, - preserve_context=True + minion_funcs[func], module_globals, preserve_context=True ) return setup_func(self) + return wrapper def setup_loader_modules(self): raise NotImplementedError( - '\'{}.setup_loader_modules()\' must be implemented'.format(self.__class__.__name__) + "'{}.setup_loader_modules()' must be implemented".format( + self.__class__.__name__ + ) ) class XMLEqualityMixin(object): - def assertEqualXML(self, e1, e2): if six.PY3 and isinstance(e1, bytes): - e1 = e1.decode('utf-8') + e1 = e1.decode("utf-8") if six.PY3 and isinstance(e2, bytes): - e2 = e2.decode('utf-8') + e2 = e2.decode("utf-8") if isinstance(e1, six.string_types): e1 = etree.XML(e1) if isinstance(e2, six.string_types): @@ -574,15 +619,12 @@ class XMLEqualityMixin(object): class SaltReturnAssertsMixin(object): - def assertReturnSaltType(self, ret): try: self.assertTrue(isinstance(ret, dict)) except AssertionError: raise AssertionError( - '{0} is not dict. Salt returned: {1}'.format( - type(ret).__name__, ret - ) + "{0} is not dict. Salt returned: {1}".format(type(ret).__name__, ret) ) def assertReturnNonEmptySaltType(self, ret): @@ -591,7 +633,7 @@ class SaltReturnAssertsMixin(object): self.assertNotEqual(ret, {}) except AssertionError: raise AssertionError( - '{} is equal to {}. Salt returned an empty dictionary.' + "{} is equal to {}. Salt returned an empty dictionary." ) def __return_valid_keys(self, keys): @@ -603,7 +645,7 @@ class SaltReturnAssertsMixin(object): keys = [keys] elif not isinstance(keys, list): # If we've reached here, it's a bad type passed to keys - raise RuntimeError('The passed keys need to be a list') + raise RuntimeError("The passed keys need to be a list") return keys def __getWithinSaltReturn(self, ret, keys): @@ -616,8 +658,8 @@ class SaltReturnAssertsMixin(object): ret_item = part[okeys.pop(0)] except (KeyError, TypeError): raise AssertionError( - 'Could not get ret{0} from salt\'s return: {1}'.format( - ''.join(['[\'{0}\']'.format(k) for k in keys]), part + "Could not get ret{0} from salt's return: {1}".format( + "".join(["['{0}']".format(k) for k in keys]), part ) ) while okeys: @@ -625,8 +667,8 @@ class SaltReturnAssertsMixin(object): ret_item = ret_item[okeys.pop(0)] except (KeyError, TypeError): raise AssertionError( - 'Could not get ret{0} from salt\'s return: {1}'.format( - ''.join(['[\'{0}\']'.format(k) for k in keys]), part + "Could not get ret{0} from salt's return: {1}".format( + "".join(["['{0}']".format(k) for k in keys]), part ) ) ret_data.append(ret_item) @@ -634,74 +676,74 @@ class SaltReturnAssertsMixin(object): def assertSaltTrueReturn(self, ret): try: - for saltret in self.__getWithinSaltReturn(ret, 'result'): + for saltret in self.__getWithinSaltReturn(ret, "result"): self.assertTrue(saltret) except AssertionError: - log.info('Salt Full Return:\n{0}'.format(pprint.pformat(ret))) + log.info("Salt Full Return:\n{0}".format(pprint.pformat(ret))) try: raise AssertionError( - '{result} is not True. Salt Comment:\n{comment}'.format( + "{result} is not True. Salt Comment:\n{comment}".format( **(next(six.itervalues(ret))) ) ) except (AttributeError, IndexError): raise AssertionError( - 'Failed to get result. Salt Returned:\n{0}'.format( + "Failed to get result. Salt Returned:\n{0}".format( pprint.pformat(ret) ) ) def assertSaltFalseReturn(self, ret): try: - for saltret in self.__getWithinSaltReturn(ret, 'result'): + for saltret in self.__getWithinSaltReturn(ret, "result"): self.assertFalse(saltret) except AssertionError: - log.info('Salt Full Return:\n{0}'.format(pprint.pformat(ret))) + log.info("Salt Full Return:\n{0}".format(pprint.pformat(ret))) try: raise AssertionError( - '{result} is not False. Salt Comment:\n{comment}'.format( + "{result} is not False. Salt Comment:\n{comment}".format( **(next(six.itervalues(ret))) ) ) except (AttributeError, IndexError): raise AssertionError( - 'Failed to get result. Salt Returned: {0}'.format(ret) + "Failed to get result. Salt Returned: {0}".format(ret) ) def assertSaltNoneReturn(self, ret): try: - for saltret in self.__getWithinSaltReturn(ret, 'result'): + for saltret in self.__getWithinSaltReturn(ret, "result"): self.assertIsNone(saltret) except AssertionError: - log.info('Salt Full Return:\n{0}'.format(pprint.pformat(ret))) + log.info("Salt Full Return:\n{0}".format(pprint.pformat(ret))) try: raise AssertionError( - '{result} is not None. Salt Comment:\n{comment}'.format( + "{result} is not None. Salt Comment:\n{comment}".format( **(next(six.itervalues(ret))) ) ) except (AttributeError, IndexError): raise AssertionError( - 'Failed to get result. Salt Returned: {0}'.format(ret) + "Failed to get result. Salt Returned: {0}".format(ret) ) def assertInSaltComment(self, in_comment, ret): - for saltret in self.__getWithinSaltReturn(ret, 'comment'): + for saltret in self.__getWithinSaltReturn(ret, "comment"): self.assertIn(in_comment, saltret) def assertNotInSaltComment(self, not_in_comment, ret): - for saltret in self.__getWithinSaltReturn(ret, 'comment'): + for saltret in self.__getWithinSaltReturn(ret, "comment"): self.assertNotIn(not_in_comment, saltret) def assertSaltCommentRegexpMatches(self, ret, pattern): - return self.assertInSaltReturnRegexpMatches(ret, pattern, 'comment') + return self.assertInSaltReturnRegexpMatches(ret, pattern, "comment") def assertInSaltStateWarning(self, in_comment, ret): - for saltret in self.__getWithinSaltReturn(ret, 'warnings'): + for saltret in self.__getWithinSaltReturn(ret, "warnings"): self.assertIn(in_comment, saltret) def assertNotInSaltStateWarning(self, not_in_comment, ret): - for saltret in self.__getWithinSaltReturn(ret, 'warnings'): + for saltret in self.__getWithinSaltReturn(ret, "warnings"): self.assertNotIn(not_in_comment, saltret) def assertInSaltReturn(self, item_to_check, ret, keys): @@ -717,35 +759,36 @@ class SaltReturnAssertsMixin(object): self.assertRegex(saltret, pattern) def assertSaltStateChangesEqual(self, ret, comparison, keys=()): - keys = ['changes'] + self.__return_valid_keys(keys) + keys = ["changes"] + self.__return_valid_keys(keys) for saltret in self.__getWithinSaltReturn(ret, keys): self.assertEqual(saltret, comparison) def assertSaltStateChangesNotEqual(self, ret, comparison, keys=()): - keys = ['changes'] + self.__return_valid_keys(keys) + keys = ["changes"] + self.__return_valid_keys(keys) for saltret in self.__getWithinSaltReturn(ret, keys): self.assertNotEqual(saltret, comparison) def _fetch_events(q, opts): - ''' + """ Collect events and store them - ''' + """ + def _clean_queue(): - log.info('Cleaning queue!') + log.info("Cleaning queue!") while not q.empty(): queue_item = q.get() queue_item.task_done() atexit.register(_clean_queue) - event = salt.utils.event.get_event('minion', sock_dir=opts['sock_dir'], opts=opts) + event = salt.utils.event.get_event("minion", sock_dir=opts["sock_dir"], opts=opts) # Wait for event bus to be connected while not event.connect_pull(30): time.sleep(1) # Notify parent process that the event bus is connected - q.put('CONNECTED') + q.put("CONNECTED") while True: try: @@ -758,25 +801,25 @@ def _fetch_events(q, opts): class SaltMinionEventAssertsMixin(object): - ''' + """ Asserts to verify that a given event was seen - ''' + """ @classmethod def setUpClass(cls): - opts = copy.deepcopy(RUNTIME_VARS.RUNTIME_CONFIGS['minion']) + opts = copy.deepcopy(RUNTIME_VARS.RUNTIME_CONFIGS["minion"]) cls.q = multiprocessing.Queue() cls.fetch_proc = salt.utils.process.SignalHandlingProcess( target=_fetch_events, args=(cls.q, opts), - name='Process-{}-Queue'.format(cls.__name__) + name="Process-{}-Queue".format(cls.__name__), ) cls.fetch_proc.start() # Wait for the event bus to be connected msg = cls.q.get(block=True) - if msg != 'CONNECTED': + if msg != "CONNECTED": # Just in case something very bad happens - raise RuntimeError('Unexpected message in test\'s event queue') + raise RuntimeError("Unexpected message in test's event queue") @classmethod def tearDownClass(cls): @@ -785,8 +828,8 @@ class SaltMinionEventAssertsMixin(object): del cls.fetch_proc def assertMinionEventFired(self, tag): - #TODO - raise salt.exceptions.NotImplemented('assertMinionEventFired() not implemented') + # TODO + raise salt.exceptions.NotImplemented("assertMinionEventFired() not implemented") def assertMinionEventReceived(self, desired_event, timeout=5, sleep_time=0.5): start = time.time() @@ -799,11 +842,13 @@ class SaltMinionEventAssertsMixin(object): break continue if isinstance(event, dict): - event.pop('_stamp') + event.pop("_stamp") if desired_event == event: self.fetch_proc.terminate() return True if time.time() - start >= timeout: break self.fetch_proc.terminate() - raise AssertionError('Event {0} was not received by minion'.format(desired_event)) + raise AssertionError( + "Event {0} was not received by minion".format(desired_event) + ) diff --git a/tests/support/mock.py b/tests/support/mock.py index 805a60377ca..c6c67e89ef0 100644 --- a/tests/support/mock.py +++ b/tests/support/mock.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.support.mock @@ -11,40 +11,50 @@ Note: mock >= 2.0.0 required since unittest.mock does not have MagicMock.assert_called in Python < 3.6. -''' +""" # pylint: disable=unused-import,function-redefined,blacklisted-module,blacklisted-external-module from __future__ import absolute_import + import collections import copy import errno import fnmatch import sys -# Import salt libs -from salt.ext import six -import salt.utils.stringutils - # By these days, we should blowup if mock is not available import mock # pylint: disable=blacklisted-external-import -__mock_version = tuple([int(part) for part in mock.__version__.split('.') if part.isdigit()]) # pylint: disable=no-member +import salt.utils.stringutils + +# pylint: disable=no-name-in-module,no-member +from mock import ( + ANY, + DEFAULT, + FILTER_DIR, + MagicMock, + Mock, + NonCallableMagicMock, + NonCallableMock, + PropertyMock, + __version__, + call, + create_autospec, + patch, + sentinel, +) + +# Import salt libs +from salt.ext import six + +# pylint: disable=no-name-in-module,no-member + + +__mock_version = tuple( + [int(part) for part in mock.__version__.split(".") if part.isdigit()] +) # pylint: disable=no-member if sys.version_info < (3, 6) and __mock_version < (2,): # We need mock >= 2.0.0 before Py3.6 - raise ImportError('Please install mock>=2.0.0') - -from mock import (Mock, # pylint: disable=no-name-in-module,no-member - MagicMock, - patch, - sentinel, - DEFAULT, - create_autospec, - FILTER_DIR, - NonCallableMock, - NonCallableMagicMock, - PropertyMock, - __version__, - ANY, - call) + raise ImportError("Please install mock>=2.0.0") class MockFH(object): @@ -54,11 +64,11 @@ class MockFH(object): try: self.mode = args[0] except IndexError: - self.mode = kwargs.get('mode', 'r') - self.binary_mode = 'b' in self.mode - self.read_mode = any(x in self.mode for x in ('r', '+')) - self.write_mode = any(x in self.mode for x in ('w', 'a', '+')) - self.empty_string = b'' if self.binary_mode else '' + self.mode = kwargs.get("mode", "r") + self.binary_mode = "b" in self.mode + self.read_mode = any(x in self.mode for x in ("r", "+")) + self.write_mode = any(x in self.mode for x in ("w", "a", "+")) + self.empty_string = b"" if self.binary_mode else "" self.call = MockCall(filename, *args, **kwargs) self.read_data_iter = self._iterate_read_data(read_data) self.read = Mock(side_effect=self._read) @@ -72,14 +82,14 @@ class MockFH(object): self.__read_data_ok = False def _iterate_read_data(self, read_data): - ''' + """ Helper for mock_open: Retrieve lines from read_data via a generator so that separate calls to readline, read, and readlines are properly interleaved - ''' + """ # Newline will always be a bytestring on PY2 because mock_open will have # normalized it to one. - newline = b'\n' if isinstance(read_data, six.binary_type) else '\n' + newline = b"\n" if isinstance(read_data, six.binary_type) else "\n" read_data = [line + newline for line in read_data.split(newline)] @@ -98,16 +108,16 @@ class MockFH(object): @property def write_calls(self): - ''' + """ Return a list of all calls to the .write() mock - ''' + """ return [x[1][0] for x in self.write.mock_calls] @property def writelines_calls(self): - ''' + """ Return a list of all calls to the .writelines() mock - ''' + """ return [x[1][0] for x in self.writelines.mock_calls] def tell(self): @@ -118,19 +128,17 @@ class MockFH(object): if self.binary_mode: if not isinstance(self.read_data, six.binary_type): raise TypeError( - '{0} opened in binary mode, expected read_data to be ' - 'bytes, not {1}'.format( - self.filename, - type(self.read_data).__name__ + "{0} opened in binary mode, expected read_data to be " + "bytes, not {1}".format( + self.filename, type(self.read_data).__name__ ) ) else: if not isinstance(self.read_data, str): raise TypeError( - '{0} opened in non-binary mode, expected read_data to ' - 'be str, not {1}'.format( - self.filename, - type(self.read_data).__name__ + "{0} opened in non-binary mode, expected read_data to " + "be str, not {1}".format( + self.filename, type(self.read_data).__name__ ) ) # No need to repeat this the next time we check @@ -139,9 +147,9 @@ class MockFH(object): def _read(self, size=0): self.__check_read_data() if not self.read_mode: - raise IOError('File not open for reading') + raise IOError("File not open for reading") if not isinstance(size, six.integer_types) or size < 0: - raise TypeError('a positive integer is required') + raise TypeError("a positive integer is required") joined = self.empty_string.join(self.read_data_iter) if not size: @@ -161,7 +169,7 @@ class MockFH(object): # TODO: Implement "size" argument self.__check_read_data() if not self.read_mode: - raise IOError('File not open for reading') + raise IOError("File not open for reading") ret = list(self.read_data_iter) self.__loc += sum(len(x) for x in ret) return ret @@ -170,7 +178,7 @@ class MockFH(object): # TODO: Implement "size" argument self.__check_read_data() if not self.read_mode: - raise IOError('File not open for reading') + raise IOError("File not open for reading") try: ret = next(self.read_data_iter) self.__loc += len(ret) @@ -181,7 +189,7 @@ class MockFH(object): def __iter__(self): self.__check_read_data() if not self.read_mode: - raise IOError('File not open for reading') + raise IOError("File not open for reading") while True: try: ret = next(self.read_data_iter) @@ -192,7 +200,7 @@ class MockFH(object): def _write(self, content): if not self.write_mode: - raise IOError('File not open for writing') + raise IOError("File not open for writing") if six.PY2: if isinstance(content, six.text_type): # encoding intentionally not specified to force a @@ -202,20 +210,20 @@ class MockFH(object): content_type = type(content) if self.binary_mode and content_type is not bytes: raise TypeError( - 'a bytes-like object is required, not \'{0}\''.format( + "a bytes-like object is required, not '{0}'".format( content_type.__name__ ) ) elif not self.binary_mode and content_type is not str: raise TypeError( - 'write() argument must be str, not {0}'.format( + "write() argument must be str, not {0}".format( content_type.__name__ ) ) def _writelines(self, lines): if not self.write_mode: - raise IOError('File not open for writing') + raise IOError("File not open for writing") for line in lines: self._write(line) @@ -233,20 +241,19 @@ class MockCall(object): def __repr__(self): # future lint: disable=blacklisted-function - ret = str('MockCall(') + ret = str("MockCall(") for arg in self.args: - ret += repr(arg) + str(', ') + ret += repr(arg) + str(", ") if not self.kwargs: if self.args: # Remove trailing ', ' ret = ret[:-2] else: for key, val in six.iteritems(self.kwargs): - ret += str('{0}={1}').format( - salt.utils.stringutils.to_str(key), - repr(val) + ret += str("{0}={1}").format( + salt.utils.stringutils.to_str(key), repr(val) ) - ret += str(')') + ret += str(")") return ret # future lint: enable=blacklisted-function @@ -357,7 +364,8 @@ class MockOpen(object): * filehandles - This is a dictionary mapping filenames to lists of MockFH objects, representing the individual times that a given file was opened. ''' - def __init__(self, read_data=''): + + def __init__(self, read_data=""): # If the read_data contains lists, we will be popping it. So, don't # modify the original value passed. read_data = copy.copy(read_data) @@ -365,7 +373,7 @@ class MockOpen(object): # Normalize read_data, Python 2 filehandles should never produce unicode # types on read. if not isinstance(read_data, dict): - read_data = {'*': read_data} + read_data = {"*": read_data} if six.PY2: # .__class__() used here to preserve the dict class in the event that @@ -388,22 +396,22 @@ class MockOpen(object): self.call_count = 0 def __call__(self, name, *args, **kwargs): - ''' + """ Match the file being opened to the patterns in the read_data and spawn a mocked filehandle with the corresponding file contents. - ''' + """ call = MockCall(name, *args, **kwargs) self.calls.append(call) self.call_count += 1 for pat in self.read_data: - if pat == '*': + if pat == "*": continue if fnmatch.fnmatch(name, pat): matched_pattern = pat break else: # No non-glob match in read_data, fall back to '*' - matched_pattern = '*' + matched_pattern = "*" try: matched_contents = self.read_data[matched_pattern] try: @@ -416,8 +424,8 @@ class MockOpen(object): except IndexError: # We've run out of file contents, abort! raise RuntimeError( - 'File matching expression \'{0}\' opened more times than ' - 'expected'.format(matched_pattern) + "File matching expression '{0}' opened more times than " + "expected".format(matched_pattern) ) try: @@ -435,13 +443,13 @@ class MockOpen(object): except KeyError: # No matching glob in read_data, treat this as a file that does # not exist and raise the appropriate exception. - raise IOError(errno.ENOENT, 'No such file or directory', name) + raise IOError(errno.ENOENT, "No such file or directory", name) def write_calls(self, path=None): - ''' + """ Returns the contents passed to all .write() calls. Use `path` to narrow the results to files matching a given pattern. - ''' + """ ret = [] for filename, handles in six.iteritems(self.filehandles): if path is None or fnmatch.fnmatch(filename, path): @@ -450,10 +458,10 @@ class MockOpen(object): return ret def writelines_calls(self, path=None): - ''' + """ Returns the contents passed to all .writelines() calls. Use `path` to narrow the results to files matching a given pattern. - ''' + """ ret = [] for filename, handles in six.iteritems(self.filehandles): if path is None or fnmatch.fnmatch(filename, path): diff --git a/tests/support/napalm.py b/tests/support/napalm.py index 5d5899c49d2..19ea5554ad3 100644 --- a/tests/support/napalm.py +++ b/tests/support/napalm.py @@ -1,57 +1,53 @@ # -*- coding: utf-8 -*- -''' +""" Base classes for napalm unit tests :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" from __future__ import absolute_import + from functools import wraps - TEST_INTERFACES = { - 'Management1': { - 'is_up': False, - 'is_enabled': False, - 'description': u'', - 'last_flapped': -1, - 'speed': 1000, - 'mac_address': u'dead:beef:dead', + "Management1": { + "is_up": False, + "is_enabled": False, + "description": u"", + "last_flapped": -1, + "speed": 1000, + "mac_address": u"dead:beef:dead", } } # Test data TEST_FACTS = { - '__opts__': {}, - 'OPTIONAL_ARGS': {}, - 'uptime': 'Forever', - 'UP': True, - 'HOSTNAME': 'test-device.com', - 'hostname': 'test-device.com', - 'username': 'admin', - 'os_version': '1.2.3', - 'model': 'test_model', - 'serial_number': '123456', - 'vendor': 'cisco', - 'interface_list': TEST_INTERFACES + "__opts__": {}, + "OPTIONAL_ARGS": {}, + "uptime": "Forever", + "UP": True, + "HOSTNAME": "test-device.com", + "hostname": "test-device.com", + "username": "admin", + "os_version": "1.2.3", + "model": "test_model", + "serial_number": "123456", + "vendor": "cisco", + "interface_list": TEST_INTERFACES, } -TEST_ENVIRONMENT = { - 'hot': 'yes' -} +TEST_ENVIRONMENT = {"hot": "yes"} -TEST_COMMAND_RESPONSE = { - 'show run': 'all the command output' -} +TEST_COMMAND_RESPONSE = {"show run": "all the command output"} TEST_TRACEROUTE_RESPONSE = { - 'success': { + "success": { 1: { - 'probes': { + "probes": { 1: { - 'rtt': 1.123, - 'ip_address': u'206.223.116.21', - 'host_name': u'eqixsj-google-gige.google.com' + "rtt": 1.123, + "ip_address": u"206.223.116.21", + "host_name": u"eqixsj-google-gige.google.com", } } } @@ -59,329 +55,294 @@ TEST_TRACEROUTE_RESPONSE = { } TEST_PING_RESPONSE = { - 'success': { - 'probes_sent': 5, - 'packet_loss': 0, - 'rtt_min': 72.158, - 'rtt_max': 72.433, - 'rtt_avg': 72.268, - 'rtt_stddev': 0.094, - 'results': [ - { - 'ip_address': '1.1.1.1', - 'rtt': 72.248 - } - ] + "success": { + "probes_sent": 5, + "packet_loss": 0, + "rtt_min": 72.158, + "rtt_max": 72.433, + "rtt_avg": 72.268, + "rtt_stddev": 0.094, + "results": [{"ip_address": "1.1.1.1", "rtt": 72.248}], } } TEST_ARP_TABLE = [ { - 'interface': 'MgmtEth0/RSP0/CPU0/0', - 'mac': '5C:5E:AB:DA:3C:F0', - 'ip': '172.17.17.1', - 'age': 1454496274.84 + "interface": "MgmtEth0/RSP0/CPU0/0", + "mac": "5C:5E:AB:DA:3C:F0", + "ip": "172.17.17.1", + "age": 1454496274.84, } ] -TEST_IPADDRS = { - 'FastEthernet8': { - 'ipv4': { - '10.66.43.169': { - 'prefix_length': 22 - } - } - } -} +TEST_IPADDRS = {"FastEthernet8": {"ipv4": {"10.66.43.169": {"prefix_length": 22}}}} TEST_INTERFACES = { - 'Management1': { - 'is_up': False, - 'is_enabled': False, - 'description': u'', - 'last_flapped': -1, - 'speed': 1000, - 'mac_address': u'dead:beef:dead', + "Management1": { + "is_up": False, + "is_enabled": False, + "description": u"", + "last_flapped": -1, + "speed": 1000, + "mac_address": u"dead:beef:dead", } } -TEST_LLDP_NEIGHBORS = { - u'Ethernet2': - [ - { - 'hostname': u'junos-unittest', - 'port': u'520', - } - ] -} +TEST_LLDP_NEIGHBORS = {u"Ethernet2": [{"hostname": u"junos-unittest", "port": u"520"}]} TEST_MAC_TABLE = [ { - 'mac': '00:1C:58:29:4A:71', - 'interface': 'Ethernet47', - 'vlan': 100, - 'static': False, - 'active': True, - 'moves': 1, - 'last_move': 1454417742.58 + "mac": "00:1C:58:29:4A:71", + "interface": "Ethernet47", + "vlan": 100, + "static": False, + "active": True, + "moves": 1, + "last_move": 1454417742.58, } ] -TEST_RUNNING_CONFIG = { - 'one': 'two' -} +TEST_RUNNING_CONFIG = {"one": "two"} TEST_OPTICS = { - 'et1': { - 'physical_channels': { - 'channel': [ + "et1": { + "physical_channels": { + "channel": [ { - 'index': 0, - 'state': { - 'input_power': { - 'instant': 0.0, - 'avg': 0.0, - 'min': 0.0, - 'max': 0.0, + "index": 0, + "state": { + "input_power": { + "instant": 0.0, + "avg": 0.0, + "min": 0.0, + "max": 0.0, }, - 'output_power': { - 'instant': 0.0, - 'avg': 0.0, - 'min': 0.0, - 'max': 0.0, + "output_power": { + "instant": 0.0, + "avg": 0.0, + "min": 0.0, + "max": 0.0, }, - 'laser_bias_current': { - 'instant': 0.0, - 'avg': 0.0, - 'min': 0.0, - 'max': 0.0, + "laser_bias_current": { + "instant": 0.0, + "avg": 0.0, + "min": 0.0, + "max": 0.0, }, - } + }, } ] } } } -TEST_BGP_CONFIG = { - 'test': 'value' -} +TEST_BGP_CONFIG = {"test": "value"} TEST_BGP_NEIGHBORS = { - 'default': { + "default": { 8121: [ { - 'up': True, - 'local_as': 13335, - 'remote_as': 8121, - 'local_address': u'172.101.76.1', - 'local_address_configured': True, - 'local_port': 179, - 'remote_address': u'192.247.78.0', - 'router_id': u'192.168.0.1', - 'remote_port': 58380, - 'multihop': False, - 'import_policy': u'4-NTT-TRANSIT-IN', - 'export_policy': u'4-NTT-TRANSIT-OUT', - 'input_messages': 123, - 'output_messages': 13, - 'input_updates': 123, - 'output_updates': 5, - 'messages_queued_out': 23, - 'connection_state': u'Established', - 'previous_connection_state': u'EstabSync', - 'last_event': u'RecvKeepAlive', - 'suppress_4byte_as': False, - 'local_as_prepend': False, - 'holdtime': 90, - 'configured_holdtime': 90, - 'keepalive': 30, - 'configured_keepalive': 30, - 'active_prefix_count': 132808, - 'received_prefix_count': 566739, - 'accepted_prefix_count': 566479, - 'suppressed_prefix_count': 0, - 'advertise_prefix_count': 0, - 'flap_count': 27 + "up": True, + "local_as": 13335, + "remote_as": 8121, + "local_address": u"172.101.76.1", + "local_address_configured": True, + "local_port": 179, + "remote_address": u"192.247.78.0", + "router_id": u"192.168.0.1", + "remote_port": 58380, + "multihop": False, + "import_policy": u"4-NTT-TRANSIT-IN", + "export_policy": u"4-NTT-TRANSIT-OUT", + "input_messages": 123, + "output_messages": 13, + "input_updates": 123, + "output_updates": 5, + "messages_queued_out": 23, + "connection_state": u"Established", + "previous_connection_state": u"EstabSync", + "last_event": u"RecvKeepAlive", + "suppress_4byte_as": False, + "local_as_prepend": False, + "holdtime": 90, + "configured_holdtime": 90, + "keepalive": 30, + "configured_keepalive": 30, + "active_prefix_count": 132808, + "received_prefix_count": 566739, + "accepted_prefix_count": 566479, + "suppressed_prefix_count": 0, + "advertise_prefix_count": 0, + "flap_count": 27, } ] } } -TEST_TERM_CONFIG = { - 'result': True, - 'already_configured': False -} +TEST_TERM_CONFIG = {"result": True, "already_configured": False} TEST_NTP_PEERS = { - '192.168.0.1': 1, - '172.17.17.1': 2, - '172.17.17.2': 3, - '2400:cb00:6:1024::c71b:840a': 4 + "192.168.0.1": 1, + "172.17.17.1": 2, + "172.17.17.2": 3, + "2400:cb00:6:1024::c71b:840a": 4, } TEST_NTP_SERVERS = { - '192.168.0.1': 1, - '172.17.17.1': 2, - '172.17.17.2': 3, - '2400:cb00:6:1024::c71b:840a': 4 + "192.168.0.1": 1, + "172.17.17.1": 2, + "172.17.17.2": 3, + "2400:cb00:6:1024::c71b:840a": 4, } TEST_NTP_STATS = [ { - 'remote': u'188.114.101.4', - 'referenceid': u'188.114.100.1', - 'synchronized': True, - 'stratum': 4, - 'type': u'-', - 'when': u'107', - 'hostpoll': 256, - 'reachability': 377, - 'delay': 164.228, - 'offset': -13.866, - 'jitter': 2.695 + "remote": u"188.114.101.4", + "referenceid": u"188.114.100.1", + "synchronized": True, + "stratum": 4, + "type": u"-", + "when": u"107", + "hostpoll": 256, + "reachability": 377, + "delay": 164.228, + "offset": -13.866, + "jitter": 2.695, } ] TEST_PROBES_CONFIG = { - 'probe1': { - 'test1': { - 'probe_type': 'icmp-ping', - 'target': '192.168.0.1', - 'source': '192.168.0.2', - 'probe_count': 13, - 'test_interval': 3 + "probe1": { + "test1": { + "probe_type": "icmp-ping", + "target": "192.168.0.1", + "source": "192.168.0.2", + "probe_count": 13, + "test_interval": 3, + }, + "test2": { + "probe_type": "http-ping", + "target": "172.17.17.1", + "source": "192.17.17.2", + "probe_count": 5, + "test_interval": 60, }, - 'test2': { - 'probe_type': 'http-ping', - 'target': '172.17.17.1', - 'source': '192.17.17.2', - 'probe_count': 5, - 'test_interval': 60 - } } } TEST_PROBES_RESULTS = { - 'probe1': { - 'test1': { - 'last_test_min_delay': 63.120, - 'global_test_min_delay': 62.912, - 'current_test_avg_delay': 63.190, - 'global_test_max_delay': 177.349, - 'current_test_max_delay': 63.302, - 'global_test_avg_delay': 63.802, - 'last_test_avg_delay': 63.438, - 'last_test_max_delay': 65.356, - 'probe_type': 'icmp-ping', - 'rtt': 63.138, - 'last_test_loss': 0, - 'round_trip_jitter': -59.0, - 'target': '192.168.0.1', - 'source': '192.168.0.2', - 'probe_count': 15, - 'current_test_min_delay': 63.138 + "probe1": { + "test1": { + "last_test_min_delay": 63.120, + "global_test_min_delay": 62.912, + "current_test_avg_delay": 63.190, + "global_test_max_delay": 177.349, + "current_test_max_delay": 63.302, + "global_test_avg_delay": 63.802, + "last_test_avg_delay": 63.438, + "last_test_max_delay": 65.356, + "probe_type": "icmp-ping", + "rtt": 63.138, + "last_test_loss": 0, + "round_trip_jitter": -59.0, + "target": "192.168.0.1", + "source": "192.168.0.2", + "probe_count": 15, + "current_test_min_delay": 63.138, + }, + "test2": { + "last_test_min_delay": 176.384, + "global_test_min_delay": 169.226, + "current_test_avg_delay": 177.098, + "global_test_max_delay": 292.628, + "current_test_max_delay": 180.055, + "global_test_avg_delay": 177.959, + "last_test_avg_delay": 177.178, + "last_test_max_delay": 184.671, + "probe_type": "icmp-ping", + "rtt": 176.449, + "last_test_loss": 0, + "round_trip_jitter": -34.0, + "target": "172.17.17.1", + "source": "172.17.17.2", + "probe_count": 15, + "current_test_min_delay": 176.402, }, - 'test2': { - 'last_test_min_delay': 176.384, - 'global_test_min_delay': 169.226, - 'current_test_avg_delay': 177.098, - 'global_test_max_delay': 292.628, - 'current_test_max_delay': 180.055, - 'global_test_avg_delay': 177.959, - 'last_test_avg_delay': 177.178, - 'last_test_max_delay': 184.671, - 'probe_type': 'icmp-ping', - 'rtt': 176.449, - 'last_test_loss': 0, - 'round_trip_jitter': -34.0, - 'target': '172.17.17.1', - 'source': '172.17.17.2', - 'probe_count': 15, - 'current_test_min_delay': 176.402 - } } } TEST_ROUTE = { - '172.16.0.0/25': [ + "172.16.0.0/25": [ { - 'protocol': 'BGP', - 'last_active': True, - 'current_active': True, - 'age': 1178693, - 'routing_table': 'inet.0', - 'next_hop': '192.168.0.11', - 'outgoing_interface': 'xe-1/1/1.100', - 'preference': 170, - 'selected_next_hop': False, - 'protocol_attributes': { - 'remote_as': 65001, - 'metric': 5, - 'local_as': 13335, - 'as_path': '', - 'remote_address': '192.168.0.11', - 'metric2': 0, - 'local_preference': 0, - 'communities': [ - '0:2', - 'no-export' - ], - 'preference2': -1 + "protocol": "BGP", + "last_active": True, + "current_active": True, + "age": 1178693, + "routing_table": "inet.0", + "next_hop": "192.168.0.11", + "outgoing_interface": "xe-1/1/1.100", + "preference": 170, + "selected_next_hop": False, + "protocol_attributes": { + "remote_as": 65001, + "metric": 5, + "local_as": 13335, + "as_path": "", + "remote_address": "192.168.0.11", + "metric2": 0, + "local_preference": 0, + "communities": ["0:2", "no-export"], + "preference2": -1, }, - 'inactive_reason': '' + "inactive_reason": "", }, { - 'protocol': 'BGP', - 'last_active': False, - 'current_active': False, - 'age': 2359429, - 'routing_table': 'inet.0', - 'next_hop': '192.168.0.17', - 'outgoing_interface': 'xe-1/1/1.100', - 'preference': 170, - 'selected_next_hop': True, - 'protocol_attributes': { - 'remote_as': 65001, - 'metric': 5, - 'local_as': 13335, - 'as_path': '', - 'remote_address': '192.168.0.17', - 'metric2': 0, - 'local_preference': 0, - 'communities': [ - '0:3', - 'no-export' - ], - 'preference2': -1 + "protocol": "BGP", + "last_active": False, + "current_active": False, + "age": 2359429, + "routing_table": "inet.0", + "next_hop": "192.168.0.17", + "outgoing_interface": "xe-1/1/1.100", + "preference": 170, + "selected_next_hop": True, + "protocol_attributes": { + "remote_as": 65001, + "metric": 5, + "local_as": 13335, + "as_path": "", + "remote_address": "192.168.0.17", + "metric2": 0, + "local_preference": 0, + "communities": ["0:3", "no-export"], + "preference2": -1, }, - 'inactive_reason': 'Not Best in its group - Router ID' - } + "inactive_reason": "Not Best in its group - Router ID", + }, ] } -TEST_SNMP_INFO = { - 'test_': 'value' -} +TEST_SNMP_INFO = {"test_": "value"} TEST_USERS = { - 'mircea': { - 'level': 15, - 'password': '$1$0P70xKPa$4jt5/10cBTckk6I/w/', - 'sshkeys': [ - 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4pFn+shPwTb2yELO4L7NtQrKOJXNeCl1je\ + "mircea": { + "level": 15, + "password": "$1$0P70xKPa$4jt5/10cBTckk6I/w/", + "sshkeys": [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4pFn+shPwTb2yELO4L7NtQrKOJXNeCl1je\ l9STXVaGnRAnuc2PXl35vnWmcUq6YbUEcgUTRzzXfmelJKuVJTJIlMXii7h2xkbQp0YZIEs4P\ 8ipwnRBAxFfk/ZcDsdfsdfsdfsdN56ejk345jhk345jk345jk341p3A/9LIL7l6YewLBCwJj6\ D+fWSJ0/YW+7oH17Fk2HH+tw0L5PcWLHkwA4t60iXn16qDbIk/ze6jv2hDGdCdz7oYQeCE55C\ CHOHMJWYfN3jcL4s0qv8/u6Ka1FVkV7iMmro7ChThoV/5snI4Ljf2wKqgHH7TfNaCfpU0WvHA\ - nTs8zhOrGScSrtb mircea@master-roshi' - ] + nTs8zhOrGScSrtb mircea@master-roshi" + ], } } class MockNapalmDevice(object): - '''Setup a mock device for our tests''' + """Setup a mock device for our tests""" + def get_facts(self): return TEST_FACTS @@ -393,26 +354,26 @@ class MockNapalmDevice(object): def get(self, key, default=None, *args, **kwargs): try: - if key == 'DRIVER': + if key == "DRIVER": return self return TEST_FACTS[key] except KeyError: return default def cli(self, commands, *args, **kwargs): - assert commands[0] == 'show run' + assert commands[0] == "show run" return TEST_COMMAND_RESPONSE def traceroute(self, destination, **kwargs): - assert destination == 'destination.com' + assert destination == "destination.com" return TEST_TRACEROUTE_RESPONSE def ping(self, destination, **kwargs): - assert destination == 'destination.com' + assert destination == "destination.com" return TEST_PING_RESPONSE - def get_config(self, retrieve='all'): - assert retrieve == 'running' + def get_config(self, retrieve="all"): + assert retrieve == "running" return TEST_RUNNING_CONFIG def get_interfaces_ip(self, **kwargs): @@ -431,11 +392,11 @@ class MockNapalmDevice(object): return TEST_OPTICS def load_merge_candidate(self, filename=None, config=None): - assert config == 'new config' + assert config == "new config" return TEST_RUNNING_CONFIG def load_replace_candidate(self, filename=None, config=None): - assert config == 'new config' + assert config == "new config" return TEST_RUNNING_CONFIG def commit_config(self, **kwargs): @@ -473,7 +434,7 @@ class MockNapalmDevice(object): return TEST_PROBES_RESULTS def get_route_to(self, destination, protocol=None, **kwargs): - assert destination == '1.2.3.4' + assert destination == "1.2.3.4" return TEST_ROUTE def get_snmp_information(self, **kwargs): @@ -484,19 +445,21 @@ class MockNapalmDevice(object): def mock_proxy_napalm_wrap(func): - ''' + """ The proper decorator checks for proxy minions. We don't care so just pass back to the origination function - ''' + """ @wraps(func) def func_wrapper(*args, **kwargs): - func.__globals__['napalm_device'] = MockNapalmDevice() + func.__globals__["napalm_device"] = MockNapalmDevice() return func(*args, **kwargs) + return func_wrapper import salt.utils.napalm as napalm_utils # NOQA + napalm_utils.proxy_napalm_wrap = mock_proxy_napalm_wrap # NOQA @@ -513,4 +476,4 @@ def join(*files): def get_managed_file(*args, **kwargs): - return 'True' + return "True" diff --git a/tests/support/parser/__init__.py b/tests/support/parser/__init__.py index 06dbc4c63c9..4e899db4e0a 100644 --- a/tests/support/parser/__init__.py +++ b/tests/support/parser/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" tests.support.parser ~~~~~~~~~~~~~~~~~~~~ @@ -8,43 +8,45 @@ :codeauthor: Pedro Algarvio (pedro@algarvio.me) :copyright: Copyright 2013-2017 by the SaltStack Team, see AUTHORS for more details :license: Apache 2.0, see LICENSE for more details. -''' +""" # pylint: disable=repr-flag-used-in-string from __future__ import absolute_import, print_function + import fnmatch -import os -import sys -import time -import signal -import shutil import logging -import platform import optparse +import os +import platform import re -import tempfile -import traceback +import shutil +import signal import subprocess +import sys +import tempfile +import time +import traceback import warnings -from functools import partial from collections import namedtuple +from functools import partial -import tests.support.paths -from tests.support import processes -from tests.support.unit import TestLoader, TextTestRunner -from tests.support.xmlunit import HAS_XMLRUNNER, XMLTestRunner - -# Import 3rd-party libs -from salt.ext import six import salt.utils.data import salt.utils.files import salt.utils.path import salt.utils.platform import salt.utils.stringutils import salt.utils.yaml +import tests.support.paths + +# Import 3rd-party libs +from salt.ext import six +from tests.support import processes +from tests.support.unit import TestLoader, TextTestRunner +from tests.support.xmlunit import HAS_XMLRUNNER, XMLTestRunner try: from tests.support.ext import console + WIDTH, HEIGHT = console.getTerminalSize() PNUM = WIDTH except Exception: # pylint: disable=broad-except @@ -57,13 +59,17 @@ log = logging.getLogger(__name__) WEIRD_SIGNAL_NUM = -45654 -def __global_logging_exception_handler(exc_type, exc_value, exc_traceback, - _logger=logging.getLogger(__name__), - _stderr=sys.__stderr__, - _format_exception=traceback.format_exception): - ''' +def __global_logging_exception_handler( + exc_type, + exc_value, + exc_traceback, + _logger=logging.getLogger(__name__), + _stderr=sys.__stderr__, + _format_exception=traceback.format_exception, +): + """ This function will log all python exceptions. - ''' + """ if exc_type.__name__ == "KeyboardInterrupt": # Call the original sys.excepthook sys.__excepthook__(exc_type, exc_value, exc_traceback) @@ -71,27 +77,22 @@ def __global_logging_exception_handler(exc_type, exc_value, exc_traceback, # Log the exception try: - msg = ( - 'An un-handled exception was caught by salt\'s testing global exception handler:\n{}: {}\n{}'.format( - exc_type.__name__, - exc_value, - ''.join(_format_exception(exc_type, exc_value, exc_traceback)).strip() - ) + msg = "An un-handled exception was caught by salt's testing global exception handler:\n{}: {}\n{}".format( + exc_type.__name__, + exc_value, + "".join(_format_exception(exc_type, exc_value, exc_traceback)).strip(), ) except Exception: # pylint: disable=broad-except msg = ( - 'An un-handled exception was caught by salt-testing\'s global exception handler:\n{}: {}\n' - '(UNABLE TO FORMAT TRACEBACK)'.format( - exc_type.__name__, - exc_value, - ) + "An un-handled exception was caught by salt-testing's global exception handler:\n{}: {}\n" + "(UNABLE TO FORMAT TRACEBACK)".format(exc_type.__name__, exc_value,) ) try: _logger(__name__).error(msg) except Exception: # pylint: disable=broad-except # Python is shutting down and logging has been set to None already try: - _stderr.write(msg + '\n') + _stderr.write(msg + "\n") except Exception: # pylint: disable=broad-except # We have also lost reference to sys.__stderr__ ?! print(msg) @@ -107,27 +108,30 @@ def __global_logging_exception_handler(exc_type, exc_value, exc_traceback, # Set our own exception handler as the one to use sys.excepthook = __global_logging_exception_handler -TestsuiteResult = namedtuple('TestsuiteResult', ['header', 'errors', 'skipped', 'failures', 'passed']) -TestResult = namedtuple('TestResult', ['id', 'reason']) +TestsuiteResult = namedtuple( + "TestsuiteResult", ["header", "errors", "skipped", "failures", "passed"] +) +TestResult = namedtuple("TestResult", ["id", "reason"]) -def print_header(header, sep='~', top=True, bottom=True, inline=False, - centered=False, width=PNUM): - ''' +def print_header( + header, sep="~", top=True, bottom=True, inline=False, centered=False, width=PNUM +): + """ Allows some pretty printing of headers on the console, either with a "ruler" on bottom and/or top, inline, centered, etc. - ''' + """ if top and not inline: print(sep * width) if centered and not inline: - fmt = u'{0:^{width}}' + fmt = u"{0:^{width}}" elif inline and not centered: - fmt = u'{0:{sep}<{width}}' + fmt = u"{0:{sep}<{width}}" elif inline and centered: - fmt = u'{0:{sep}^{width}}' + fmt = u"{0:{sep}^{width}}" else: - fmt = u'{0}' + fmt = u"{0}" print(fmt.format(header, sep=sep, width=width)) if bottom and not inline: @@ -141,264 +145,274 @@ class SaltTestingParser(optparse.OptionParser): source_code_basedir = None _known_interpreters = { - 'salttest/arch': 'python2', - 'salttest/centos-5': 'python2.6', - 'salttest/centos-6': 'python2.6', - 'salttest/debian-7': 'python2.7', - 'salttest/opensuse-12.3': 'python2.7', - 'salttest/ubuntu-12.04': 'python2.7', - 'salttest/ubuntu-12.10': 'python2.7', - 'salttest/ubuntu-13.04': 'python2.7', - 'salttest/ubuntu-13.10': 'python2.7', - 'salttest/py3': 'python3' + "salttest/arch": "python2", + "salttest/centos-5": "python2.6", + "salttest/centos-6": "python2.6", + "salttest/debian-7": "python2.7", + "salttest/opensuse-12.3": "python2.7", + "salttest/ubuntu-12.04": "python2.7", + "salttest/ubuntu-12.10": "python2.7", + "salttest/ubuntu-13.04": "python2.7", + "salttest/ubuntu-13.10": "python2.7", + "salttest/py3": "python3", } def __init__(self, testsuite_directory, *args, **kwargs): - if kwargs.pop('html_output_from_env', None) is not None or \ - kwargs.pop('html_output_dir', None) is not None: + if ( + kwargs.pop("html_output_from_env", None) is not None + or kwargs.pop("html_output_dir", None) is not None + ): warnings.warn( - 'The unit tests HTML support was removed from {0}. Please ' - 'stop passing \'html_output_dir\' or \'html_output_from_env\' ' - 'as arguments to {0}'.format(self.__class__.__name__), + "The unit tests HTML support was removed from {0}. Please " + "stop passing 'html_output_dir' or 'html_output_from_env' " + "as arguments to {0}".format(self.__class__.__name__), category=DeprecationWarning, - stacklevel=2 + stacklevel=2, ) # Get XML output settings xml_output_dir_env_var = kwargs.pop( - 'xml_output_from_env', - 'XML_TESTS_OUTPUT_DIR' + "xml_output_from_env", "XML_TESTS_OUTPUT_DIR" ) - xml_output_dir = kwargs.pop('xml_output_dir', None) + xml_output_dir = kwargs.pop("xml_output_dir", None) if xml_output_dir_env_var in os.environ: xml_output_dir = os.environ.get(xml_output_dir_env_var) if not xml_output_dir: xml_output_dir = os.path.join( - tempfile.gettempdir() if platform.system() != 'Darwin' else '/tmp', - 'xml-tests-output' + tempfile.gettempdir() if platform.system() != "Darwin" else "/tmp", + "xml-tests-output", ) self.xml_output_dir = xml_output_dir # Get the desired logfile to use while running tests - self.tests_logfile = kwargs.pop('tests_logfile', None) + self.tests_logfile = kwargs.pop("tests_logfile", None) optparse.OptionParser.__init__(self, *args, **kwargs) self.testsuite_directory = testsuite_directory self.testsuite_results = [] self.test_selection_group = optparse.OptionGroup( - self, - 'Tests Selection Options', - 'Select which tests are to be executed' + self, "Tests Selection Options", "Select which tests are to be executed" ) if self.support_destructive_tests_selection is True: self.test_selection_group.add_option( - '--run-destructive', - action='store_true', + "--run-destructive", + action="store_true", default=False, - help=('Run destructive tests. These tests can include adding ' - 'or removing users from your system for example. ' - 'Default: %default') + help=( + "Run destructive tests. These tests can include adding " + "or removing users from your system for example. " + "Default: %default" + ), ) if self.support_expensive_tests_selection is True: self.test_selection_group.add_option( - '--run-expensive', - action='store_true', + "--run-expensive", + action="store_true", default=False, - help=('Run expensive tests. Expensive tests are any tests that, ' - 'once configured, cost money to run, such as creating or ' - 'destroying cloud instances on a cloud provider.') + help=( + "Run expensive tests. Expensive tests are any tests that, " + "once configured, cost money to run, such as creating or " + "destroying cloud instances on a cloud provider." + ), ) self.test_selection_group.add_option( - '-n', - '--name', - dest='name', - action='append', + "-n", + "--name", + dest="name", + action="append", default=[], - help=('Specific test name to run. A named test is the module path ' - 'relative to the tests directory') + help=( + "Specific test name to run. A named test is the module path " + "relative to the tests directory" + ), ) self.test_selection_group.add_option( - '--names-file', - dest='names_file', + "--names-file", + dest="names_file", default=None, - help=('The location of a newline delimited file of test names to ' - 'run') + help=("The location of a newline delimited file of test names to " "run"), ) self.test_selection_group.add_option( - '--from-filenames', - dest='from_filenames', - action='append', + "--from-filenames", + dest="from_filenames", + action="append", default=None, - help=('Pass a comma-separated list of file paths, and any ' - 'unit/integration test module which corresponds to the ' - 'specified file(s) will be run. For example, a path of ' - 'salt/modules/git.py would result in unit.modules.test_git ' - 'and integration.modules.test_git being run. Absolute paths ' - 'are assumed to be files containing relative paths, one per ' - 'line. Providing the paths in a file can help get around ' - 'shell character limits when the list of files is long.') + help=( + "Pass a comma-separated list of file paths, and any " + "unit/integration test module which corresponds to the " + "specified file(s) will be run. For example, a path of " + "salt/modules/git.py would result in unit.modules.test_git " + "and integration.modules.test_git being run. Absolute paths " + "are assumed to be files containing relative paths, one per " + "line. Providing the paths in a file can help get around " + "shell character limits when the list of files is long." + ), ) self.test_selection_group.add_option( - '--filename-map', - dest='filename_map', + "--filename-map", + dest="filename_map", default=None, - help=('Path to a YAML file mapping paths/path globs to a list ' - 'of test names to run. See tests/filename_map.yml ' - 'for example usage (when --from-filenames is used, this ' - 'map file will be the default one used).') + help=( + "Path to a YAML file mapping paths/path globs to a list " + "of test names to run. See tests/filename_map.yml " + "for example usage (when --from-filenames is used, this " + "map file will be the default one used)." + ), ) self.add_option_group(self.test_selection_group) if self.support_docker_execution is True: self.docked_selection_group = optparse.OptionGroup( self, - 'Docked Tests Execution', - 'Run the tests suite under a Docker container. This allows, ' - 'for example, to run destructive tests on your machine ' - 'without actually breaking it in any way.' + "Docked Tests Execution", + "Run the tests suite under a Docker container. This allows, " + "for example, to run destructive tests on your machine " + "without actually breaking it in any way.", ) self.docked_selection_group.add_option( - '--docked', + "--docked", default=None, - metavar='CONTAINER', - help='Run the tests suite in the chosen Docker container' + metavar="CONTAINER", + help="Run the tests suite in the chosen Docker container", ) self.docked_selection_group.add_option( - '--docked-interpreter', + "--docked-interpreter", default=None, - metavar='PYTHON_INTERPRETER', - help='The python binary name to use when calling the tests ' - 'suite.' + metavar="PYTHON_INTERPRETER", + help="The python binary name to use when calling the tests " "suite.", ) self.docked_selection_group.add_option( - '--docked-skip-delete', + "--docked-skip-delete", default=False, - action='store_true', - help='Skip docker container deletion on exit. Default: False' + action="store_true", + help="Skip docker container deletion on exit. Default: False", ) self.docked_selection_group.add_option( - '--docked-skip-delete-on-errors', + "--docked-skip-delete-on-errors", default=False, - action='store_true', - help='Skip docker container deletion on exit if errors ' - 'occurred. Default: False' + action="store_true", + help="Skip docker container deletion on exit if errors " + "occurred. Default: False", ) self.docked_selection_group.add_option( - '--docker-binary', - help='The docker binary on the host system. Default: %default', - default='/usr/bin/docker', + "--docker-binary", + help="The docker binary on the host system. Default: %default", + default="/usr/bin/docker", ) self.add_option_group(self.docked_selection_group) - self.output_options_group = optparse.OptionGroup( - self, 'Output Options' - ) + self.output_options_group = optparse.OptionGroup(self, "Output Options") self.output_options_group.add_option( - '-F', - '--fail-fast', - dest='failfast', + "-F", + "--fail-fast", + dest="failfast", default=False, - action='store_true', - help='Stop on first failure' + action="store_true", + help="Stop on first failure", ) self.output_options_group.add_option( - '-v', - '--verbose', - dest='verbosity', + "-v", + "--verbose", + dest="verbosity", default=1, - action='count', - help='Verbose test runner output' + action="count", + help="Verbose test runner output", ) self.output_options_group.add_option( - '--output-columns', + "--output-columns", default=PNUM, type=int, - help='Number of maximum columns to use on the output' + help="Number of maximum columns to use on the output", ) self.output_options_group.add_option( - '--tests-logfile', + "--tests-logfile", default=self.tests_logfile, - help='The path to the tests suite logging logfile' + help="The path to the tests suite logging logfile", ) if self.xml_output_dir is not None: self.output_options_group.add_option( - '-x', - '--xml', - '--xml-out', - dest='xml_out', + "-x", + "--xml", + "--xml-out", + dest="xml_out", default=False, - help='XML test runner output(Output directory: {0})'.format( + help="XML test runner output(Output directory: {0})".format( self.xml_output_dir - ) + ), ) self.output_options_group.add_option( - '--no-report', + "--no-report", default=False, - action='store_true', - help='Do NOT show the overall tests result' + action="store_true", + help="Do NOT show the overall tests result", ) self.add_option_group(self.output_options_group) self.fs_cleanup_options_group = optparse.OptionGroup( - self, 'File system cleanup Options' + self, "File system cleanup Options" ) self.fs_cleanup_options_group.add_option( - '--clean', - dest='clean', + "--clean", + dest="clean", default=True, - action='store_true', - help=('Clean up test environment before and after running the ' - 'tests suite (default behaviour)') + action="store_true", + help=( + "Clean up test environment before and after running the " + "tests suite (default behaviour)" + ), ) self.fs_cleanup_options_group.add_option( - '--no-clean', - dest='clean', - action='store_false', - help=('Don\'t clean up test environment before and after the ' - 'tests suite execution (speed up test process)') + "--no-clean", + dest="clean", + action="store_false", + help=( + "Don't clean up test environment before and after the " + "tests suite execution (speed up test process)" + ), ) self.add_option_group(self.fs_cleanup_options_group) self.setup_additional_options() @staticmethod def _expand_paths(paths): - ''' + """ Expand any comma-separated lists of paths, and return a set of all paths to ensure there are no duplicates. - ''' + """ ret = set() for path in paths: - for item in [x.strip() for x in path.split(',')]: + for item in [x.strip() for x in path.split(",")]: if not item: continue elif os.path.isabs(item): try: - with salt.utils.files.fopen(item, 'rb') as fp_: + with salt.utils.files.fopen(item, "rb") as fp_: for line in fp_: line = salt.utils.stringutils.to_unicode(line.strip()) if os.path.isabs(line): log.warning( - 'Invalid absolute path %s in %s, ' - 'ignoring', line, item + "Invalid absolute path %s in %s, " "ignoring", + line, + item, ) else: ret.add(line) except (IOError, OSError) as exc: - log.error('Failed to read from %s: %s', item, exc) + log.error("Failed to read from %s: %s", item, exc) else: ret.add(item) return ret @property def _test_mods(self): - ''' + """ Use the test_mods generator to get all of the test module names, and then store them in a set so that further references to this attribute will not need to re-walk the test dir. - ''' + """ try: return self.__test_mods except AttributeError: @@ -406,10 +420,10 @@ class SaltTestingParser(optparse.OptionParser): return self.__test_mods def _map_files(self, files): - ''' + """ Map the passed paths to test modules, returning a set of the mapped module names. - ''' + """ ret = set() if self.options.filename_map is not None: @@ -417,38 +431,40 @@ class SaltTestingParser(optparse.OptionParser): with salt.utils.files.fopen(self.options.filename_map) as fp_: filename_map = salt.utils.yaml.safe_load(fp_) except Exception as exc: # pylint: disable=broad-except - raise RuntimeError( - 'Failed to load filename map: {0}'.format(exc) - ) + raise RuntimeError("Failed to load filename map: {0}".format(exc)) else: filename_map = {} def _add(comps): - ''' + """ Helper to add unit and integration tests matching a given mod path - ''' - mod_relname = '.'.join(comps) + """ + mod_relname = ".".join(comps) ret.update( - x for x in - ['.'.join(('unit', mod_relname)), - '.'.join(('integration', mod_relname)), - '.'.join(('multimaster', mod_relname))] + x + for x in [ + ".".join(("unit", mod_relname)), + ".".join(("integration", mod_relname)), + ".".join(("multimaster", mod_relname)), + ] if x in self._test_mods ) # First, try a path match for path in files: - match = re.match(r'^(salt/|tests/(unit|integration|multimaster)/)(.+\.py)$', path) + match = re.match( + r"^(salt/|tests/(unit|integration|multimaster)/)(.+\.py)$", path + ) if match: - comps = match.group(3).split('/') + comps = match.group(3).split("/") # Find matches for a source file - if match.group(1) == 'salt/': - if comps[-1] == '__init__.py': + if match.group(1) == "salt/": + if comps[-1] == "__init__.py": comps.pop(-1) - comps[-1] = 'test_' + comps[-1] + comps[-1] = "test_" + comps[-1] else: - comps[-1] = 'test_{0}'.format(comps[-1][:-3]) + comps[-1] = "test_{0}".format(comps[-1][:-3]) # Direct name matches _add(comps) @@ -457,8 +473,8 @@ class SaltTestingParser(optparse.OptionParser): # (e.g. unit.states.test_archive if # unit.modules.test_archive is being run) try: - if comps[-2] == 'modules': - comps[-2] = 'states' + if comps[-2] == "modules": + comps[-2] = "states" _add(comps) except IndexError: # Not an execution module. This is either directly in @@ -466,11 +482,11 @@ class SaltTestingParser(optparse.OptionParser): pass # Make sure to run a test module if it's been modified - elif match.group(1).startswith('tests/'): + elif match.group(1).startswith("tests/"): comps.insert(0, match.group(2)) - if fnmatch.fnmatch(comps[-1], 'test_*.py'): + if fnmatch.fnmatch(comps[-1], "test_*.py"): comps[-1] = comps[-1][:-3] - test_name = '.'.join(comps) + test_name = ".".join(comps) if test_name in self._test_mods: ret.add(test_name) @@ -481,11 +497,11 @@ class SaltTestingParser(optparse.OptionParser): ret.update(filename_map[path_expr]) break - if any(x.startswith('integration.proxy.') for x in ret): + if any(x.startswith("integration.proxy.") for x in ret): # Ensure that the salt-proxy daemon is started for these tests. self.options.proxy = True - if any(x.startswith('integration.ssh.') for x in ret): + if any(x.startswith("integration.ssh.") for x in ret): # Ensure that an ssh daemon is started for these tests. self.options.ssh = True @@ -496,32 +512,38 @@ class SaltTestingParser(optparse.OptionParser): file_names = [] if self.options.names_file: - with open(self.options.names_file, 'rb') as fp_: # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + with open(self.options.names_file, "rb") as fp_: for line in fp_.readlines(): if six.PY2: file_names.append(line.strip()) else: - file_names.append( - line.decode(__salt_system_encoding__).strip()) + file_names.append(line.decode(__salt_system_encoding__).strip()) + # pylint: enable=resource-leakage if self.args: for fpath in self.args: - if os.path.isfile(fpath) and \ - fpath.endswith('.py') and \ - os.path.basename(fpath).startswith('test_'): + if ( + os.path.isfile(fpath) + and fpath.endswith(".py") + and os.path.basename(fpath).startswith("test_") + ): if fpath in file_names: self.options.name.append(fpath) continue - self.exit(status=1, msg='\'{}\' is not a valid test module\n'.format(fpath)) + self.exit( + status=1, msg="'{}' is not a valid test module\n".format(fpath) + ) if self.options.from_filenames is not None: - self.options.from_filenames = self._expand_paths(self.options.from_filenames) + self.options.from_filenames = self._expand_paths( + self.options.from_filenames + ) # Locate the default map file if one was not passed if self.options.filename_map is None: self.options.filename_map = salt.utils.path.join( - tests.support.paths.TESTS_DIR, - 'filename_map.yml' + tests.support.paths.TESTS_DIR, "filename_map.yml" ) self.options.name.extend(self._map_files(self.options.from_filenames)) @@ -531,24 +553,22 @@ class SaltTestingParser(optparse.OptionParser): elif file_names: self.options.name = file_names - print_header(u'', inline=True, width=self.options.output_columns) + print_header(u"", inline=True, width=self.options.output_columns) self.pre_execution_cleanup() if self.support_docker_execution and self.options.docked is not None: if self.source_code_basedir is None: raise RuntimeError( - 'You need to define the \'source_code_basedir\' attribute ' - 'in \'{0}\'.'.format(self.__class__.__name__) + "You need to define the 'source_code_basedir' attribute " + "in '{0}'.".format(self.__class__.__name__) ) - if '/' not in self.options.docked: - self.options.docked = 'salttest/{0}'.format( - self.options.docked - ) + if "/" not in self.options.docked: + self.options.docked = "salttest/{0}".format(self.options.docked) if self.options.docked_interpreter is None: self.options.docked_interpreter = self._known_interpreters.get( - self.options.docked, 'python' + self.options.docked, "python" ) # No more processing should be done. We'll exit with the return @@ -559,28 +579,31 @@ class SaltTestingParser(optparse.OptionParser): # tests suite under a docker container self._validate_options() - print(' * Current Directory: {0}'.format(os.getcwd())) - print(' * Test suite is running under PID {0}'.format(os.getpid())) + print(" * Current Directory: {0}".format(os.getcwd())) + print(" * Test suite is running under PID {0}".format(os.getpid())) self._setup_logging() try: return (self.options, self.args) finally: - print_header(u'', inline=True, width=self.options.output_columns) + print_header(u"", inline=True, width=self.options.output_columns) def setup_additional_options(self): - ''' + """ Subclasses should add additional options in this overridden method - ''' + """ def _validate_options(self): - ''' + """ Validate the default available options - ''' - if self.xml_output_dir is not None and self.options.xml_out and HAS_XMLRUNNER is False: + """ + if ( + self.xml_output_dir is not None + and self.options.xml_out + and HAS_XMLRUNNER is False + ): self.error( - '\'--xml\' is not available. The xmlrunner library is not ' - 'installed.' + "'--xml' is not available. The xmlrunner library is not " "installed." ) if self.options.xml_out: @@ -590,46 +613,50 @@ class SaltTestingParser(optparse.OptionParser): if self.xml_output_dir is not None and self.options.xml_out: if not os.path.isdir(self.xml_output_dir): os.makedirs(self.xml_output_dir) - os.environ['TESTS_XML_OUTPUT_DIR'] = self.xml_output_dir + os.environ["TESTS_XML_OUTPUT_DIR"] = self.xml_output_dir print( - ' * Generated unit test XML reports will be stored ' - 'at {0!r}'.format(self.xml_output_dir) + " * Generated unit test XML reports will be stored " + "at {0!r}".format(self.xml_output_dir) ) self.validate_options() - if self.support_destructive_tests_selection and not os.environ.get('DESTRUCTIVE_TESTS', None): + if self.support_destructive_tests_selection and not os.environ.get( + "DESTRUCTIVE_TESTS", None + ): # Set the required environment variable in order to know if # destructive tests should be executed or not. - os.environ['DESTRUCTIVE_TESTS'] = str(self.options.run_destructive) + os.environ["DESTRUCTIVE_TESTS"] = str(self.options.run_destructive) - if self.support_expensive_tests_selection and not os.environ.get('EXPENSIVE_TESTS', None): + if self.support_expensive_tests_selection and not os.environ.get( + "EXPENSIVE_TESTS", None + ): # Set the required environment variable in order to know if # expensive tests should be executed or not. - os.environ['EXPENSIVE_TESTS'] = str(self.options.run_expensive) + os.environ["EXPENSIVE_TESTS"] = str(self.options.run_expensive) def validate_options(self): - ''' + """ Validate the provided options. Override this method to run your own validation procedures. - ''' + """ def _setup_logging(self): - ''' + """ Setup python's logging system to work with/for the tests suite - ''' + """ # Setup tests logging formatter = logging.Formatter( - '%(asctime)s,%(msecs)03.0f [%(name)-5s:%(lineno)-4d]' - '[%(levelname)-8s] %(message)s', - datefmt='%H:%M:%S' + "%(asctime)s,%(msecs)03.0f [%(name)-5s:%(lineno)-4d]" + "[%(levelname)-8s] %(message)s", + datefmt="%H:%M:%S", ) - if not hasattr(logging, 'TRACE'): + if not hasattr(logging, "TRACE"): logging.TRACE = 5 - logging.addLevelName(logging.TRACE, 'TRACE') - if not hasattr(logging, 'GARBAGE'): + logging.addLevelName(logging.TRACE, "TRACE") + if not hasattr(logging, "GARBAGE"): logging.GARBAGE = 1 - logging.addLevelName(logging.GARBAGE, 'GARBAGE') + logging.addLevelName(logging.GARBAGE, "GARBAGE") # Default logging level: ERROR logging.root.setLevel(logging.NOTSET) @@ -639,9 +666,9 @@ class SaltTestingParser(optparse.OptionParser): ] if self.options.tests_logfile: filehandler = logging.FileHandler( - mode='w', # Not preserved between re-runs + mode="w", # Not preserved between re-runs filename=self.options.tests_logfile, - encoding='utf-8', + encoding="utf-8", ) # The logs of the file are the most verbose possible filehandler.setLevel(logging.DEBUG) @@ -649,36 +676,40 @@ class SaltTestingParser(optparse.OptionParser): logging.root.addHandler(filehandler) log_levels_to_evaluate.append(logging.DEBUG) - print(' * Logging tests on {0}'.format(self.options.tests_logfile)) + print(" * Logging tests on {0}".format(self.options.tests_logfile)) # With greater verbosity we can also log to the console if self.options.verbosity >= 2: consolehandler = logging.StreamHandler(sys.stderr) consolehandler.setFormatter(formatter) - if self.options.verbosity >= 6: # -vvvvv + if self.options.verbosity >= 6: # -vvvvv logging_level = logging.GARBAGE - elif self.options.verbosity == 5: # -vvvv + elif self.options.verbosity == 5: # -vvvv logging_level = logging.TRACE - elif self.options.verbosity == 4: # -vvv + elif self.options.verbosity == 4: # -vvv logging_level = logging.DEBUG - elif self.options.verbosity == 3: # -vv + elif self.options.verbosity == 3: # -vv logging_level = logging.INFO else: logging_level = logging.ERROR log_levels_to_evaluate.append(logging_level) - os.environ['TESTS_LOG_LEVEL'] = str(self.options.verbosity) # future lint: disable=blacklisted-function + os.environ["TESTS_LOG_LEVEL"] = str( + self.options.verbosity + ) # future lint: disable=blacklisted-function consolehandler.setLevel(logging_level) logging.root.addHandler(consolehandler) - log.info('Runtests logging has been setup') + log.info("Runtests logging has been setup") - os.environ['TESTS_MIN_LOG_LEVEL_NAME'] = logging.getLevelName(min(log_levels_to_evaluate)) + os.environ["TESTS_MIN_LOG_LEVEL_NAME"] = logging.getLevelName( + min(log_levels_to_evaluate) + ) def pre_execution_cleanup(self): - ''' + """ Run any initial clean up operations. If sub-classed, don't forget to call SaltTestingParser.pre_execution_cleanup(self) from the overridden method. - ''' + """ if self.options.clean is True: for path in (self.xml_output_dir,): if path is None: @@ -686,24 +717,33 @@ class SaltTestingParser(optparse.OptionParser): if os.path.isdir(path): shutil.rmtree(path) - def run_suite(self, path, display_name, suffix='test_*.py', - load_from_name=False, additional_test_dirs=None, failfast=False): - ''' + def run_suite( + self, + path, + display_name, + suffix="test_*.py", + load_from_name=False, + additional_test_dirs=None, + failfast=False, + ): + """ Execute a unit test suite - ''' + """ loaded_custom = False loader = TestLoader() try: if load_from_name: tests = loader.loadTestsFromName(display_name) else: - if additional_test_dirs is None or self.testsuite_directory.startswith(path): + if additional_test_dirs is None or self.testsuite_directory.startswith( + path + ): tests = loader.discover(path, suffix, self.testsuite_directory) else: tests = loader.discover(path, suffix) loaded_custom = True except (AttributeError, ImportError): - print('Could not locate test \'{0}\'. Exiting.'.format(display_name)) + print("Could not locate test '{0}'. Exiting.".format(display_name)) sys.exit(1) if additional_test_dirs and not loaded_custom: @@ -711,9 +751,8 @@ class SaltTestingParser(optparse.OptionParser): additional_tests = loader.discover(test_dir, suffix, test_dir) tests.addTests(additional_tests) - header = '{0} Tests'.format(display_name) - print_header('Starting {0}'.format(header), - width=self.options.output_columns) + header = "{0} Tests".format(display_name) + print_header("Starting {0}".format(header), width=self.options.output_columns) if self.options.xml_out: runner = XMLTestRunner( @@ -724,9 +763,7 @@ class SaltTestingParser(optparse.OptionParser): ).run(tests) else: runner = TextTestRunner( - stream=sys.stdout, - verbosity=self.options.verbosity, - failfast=failfast + stream=sys.stdout, verbosity=self.options.verbosity, failfast=failfast ).run(tests) errors = [] @@ -739,11 +776,13 @@ class SaltTestingParser(optparse.OptionParser): for testcase, reason in runner.failures: failures.append(TestResult(testcase.id(), reason)) self.testsuite_results.append( - TestsuiteResult(header, - errors, - skipped, - failures, - runner.testsRun - len(errors + skipped + failures)) + TestsuiteResult( + header, + errors, + skipped, + failures, + runner.testsRun - len(errors + skipped + failures), + ) ) success = runner.wasSuccessful() del loader @@ -751,13 +790,16 @@ class SaltTestingParser(optparse.OptionParser): return success def print_overall_testsuite_report(self): - ''' + """ Print a nicely formatted report about the test suite results - ''' + """ print() print_header( - u' Overall Tests Report ', sep=u'=', centered=True, inline=True, - width=self.options.output_columns + u" Overall Tests Report ", + sep=u"=", + centered=True, + inline=True, + width=self.options.output_columns, ) failures = errors = skipped = passed = 0 @@ -774,91 +816,114 @@ class SaltTestingParser(optparse.OptionParser): no_problems_found = False print_header( - u'*** {0} '.format(results.header), sep=u'*', inline=True, - width=self.options.output_columns + u"*** {0} ".format(results.header), + sep=u"*", + inline=True, + width=self.options.output_columns, ) if results.skipped: print_header( - u' -------- Skipped Tests ', sep='-', inline=True, - width=self.options.output_columns + u" -------- Skipped Tests ", + sep="-", + inline=True, + width=self.options.output_columns, ) maxlen = len( max([testcase.id for testcase in results.skipped], key=len) ) - fmt = u' -> {0: <{maxlen}} -> {1}' + fmt = u" -> {0: <{maxlen}} -> {1}" for testcase in results.skipped: print(fmt.format(testcase.id, testcase.reason, maxlen=maxlen)) - print_header(u' ', sep='-', inline=True, - width=self.options.output_columns) + print_header( + u" ", sep="-", inline=True, width=self.options.output_columns + ) if results.errors: print_header( - u' -------- Tests with Errors ', sep='-', inline=True, - width=self.options.output_columns + u" -------- Tests with Errors ", + sep="-", + inline=True, + width=self.options.output_columns, ) for testcase in results.errors: print_header( - u' -> {0} '.format(testcase.id), - sep=u'.', inline=True, - width=self.options.output_columns + u" -> {0} ".format(testcase.id), + sep=u".", + inline=True, + width=self.options.output_columns, ) for line in testcase.reason.rstrip().splitlines(): - print(' {0}'.format(line.rstrip())) - print_header(u' ', sep=u'.', inline=True, - width=self.options.output_columns) - print_header(u' ', sep='-', inline=True, - width=self.options.output_columns) + print(" {0}".format(line.rstrip())) + print_header( + u" ", sep=u".", inline=True, width=self.options.output_columns + ) + print_header( + u" ", sep="-", inline=True, width=self.options.output_columns + ) if results.failures: print_header( - u' -------- Failed Tests ', sep='-', inline=True, - width=self.options.output_columns + u" -------- Failed Tests ", + sep="-", + inline=True, + width=self.options.output_columns, ) for testcase in results.failures: print_header( - u' -> {0} '.format(testcase.id), - sep=u'.', inline=True, - width=self.options.output_columns + u" -> {0} ".format(testcase.id), + sep=u".", + inline=True, + width=self.options.output_columns, ) for line in testcase.reason.rstrip().splitlines(): - print(' {0}'.format(line.rstrip())) - print_header(u' ', sep=u'.', inline=True, - width=self.options.output_columns) - print_header(u' ', sep='-', inline=True, - width=self.options.output_columns) + print(" {0}".format(line.rstrip())) + print_header( + u" ", sep=u".", inline=True, width=self.options.output_columns + ) + print_header( + u" ", sep="-", inline=True, width=self.options.output_columns + ) if no_problems_found: print_header( - u'*** No Problems Found While Running Tests ', - sep=u'*', inline=True, width=self.options.output_columns + u"*** No Problems Found While Running Tests ", + sep=u"*", + inline=True, + width=self.options.output_columns, ) - print_header(u'', sep=u'=', inline=True, - width=self.options.output_columns) + print_header(u"", sep=u"=", inline=True, width=self.options.output_columns) total = sum([passed, skipped, errors, failures]) print( - '{0} (total={1}, skipped={2}, passed={3}, failures={4}, ' - 'errors={5}) '.format( - (errors or failures) and 'FAILED' or 'OK', - total, skipped, passed, failures, errors + "{0} (total={1}, skipped={2}, passed={3}, failures={4}, " + "errors={5}) ".format( + (errors or failures) and "FAILED" or "OK", + total, + skipped, + passed, + failures, + errors, ) ) print_header( - ' Overall Tests Report ', sep='=', centered=True, inline=True, - width=self.options.output_columns + " Overall Tests Report ", + sep="=", + centered=True, + inline=True, + width=self.options.output_columns, ) def post_execution_cleanup(self): - ''' + """ Run any final clean-up operations. If sub-classed, don't forget to call SaltTestingParser.post_execution_cleanup(self) from the overridden method. - ''' + """ def finalize(self, exit_code=0): - ''' + """ Run the finalization procedures. Show report, clean-up file-system, etc - ''' + """ # Collect any child processes still laying around children = processes.collect_child_processes(os.getpid()) if self.options.no_report is False: @@ -866,49 +931,57 @@ class SaltTestingParser(optparse.OptionParser): self.post_execution_cleanup() # Brute force approach to terminate this process and its children if children: - log.info('Terminating test suite child processes: %s', children) + log.info("Terminating test suite child processes: %s", children) processes.terminate_process(children=children, kill_children=True) children = processes.collect_child_processes(os.getpid()) if children: - log.info('Second run at terminating test suite child processes: %s', children) + log.info( + "Second run at terminating test suite child processes: %s", children + ) processes.terminate_process(children=children, kill_children=True) - exit_msg = 'Test suite execution finalized with exit code: {}'.format(exit_code) + exit_msg = "Test suite execution finalized with exit code: {}".format(exit_code) log.info(exit_msg) - self.exit(status=exit_code, msg=exit_msg + '\n') + self.exit(status=exit_code, msg=exit_msg + "\n") def run_suite_in_docker(self): - ''' + """ Run the tests suite in a Docker container - ''' + """ + def stop_running_docked_container(cid, signum=None, frame=None): # Allow some time for the container to stop if it's going to be # stopped by docker or any signals docker might have received time.sleep(0.5) - print_header('', inline=True, width=self.options.output_columns) + print_header("", inline=True, width=self.options.output_columns) # Let's check if, in fact, the container is stopped scode_call = subprocess.Popen( - [self.options.docker_binary, 'inspect', '--format={{.State.Running}}', cid], + [ + self.options.docker_binary, + "inspect", + "--format={{.State.Running}}", + cid, + ], env=os.environ.copy(), close_fds=True, - stdout=subprocess.PIPE + stdout=subprocess.PIPE, ) scode_call.wait() parsed_scode = scode_call.stdout.read().strip() if six.PY3: parsed_scode = parsed_scode.decode(__salt_system_encoding__) - if parsed_scode != 'false': + if parsed_scode != "false": # If the container is still running, let's make sure it # properly stops - sys.stdout.write(' * Making sure the container is stopped. CID: ') + sys.stdout.write(" * Making sure the container is stopped. CID: ") sys.stdout.flush() stop_call = subprocess.Popen( - [self.options.docker_binary, 'stop', '--time=15', cid], + [self.options.docker_binary, "stop", "--time=15", cid], env=os.environ.copy(), close_fds=True, - stdout=subprocess.PIPE + stdout=subprocess.PIPE, ) stop_call.wait() output = stop_call.stdout.read().strip() @@ -921,13 +994,18 @@ class SaltTestingParser(optparse.OptionParser): # Let's get the container's exit code. We can't trust on Popen's # returncode because it's not reporting the proper one? Still # haven't narrowed it down why. - sys.stdout.write(' * Container exit code: ') + sys.stdout.write(" * Container exit code: ") sys.stdout.flush() rcode_call = subprocess.Popen( - [self.options.docker_binary, 'inspect', '--format={{.State.ExitCode}}', cid], + [ + self.options.docker_binary, + "inspect", + "--format={{.State.ExitCode}}", + cid, + ], env=os.environ.copy(), close_fds=True, - stdout=subprocess.PIPE + stdout=subprocess.PIPE, ) rcode_call.wait() parsed_rcode = rcode_call.stdout.read().strip() @@ -940,16 +1018,17 @@ class SaltTestingParser(optparse.OptionParser): print(parsed_rcode) sys.stdout.flush() - if self.options.docked_skip_delete is False and \ - (self.options.docked_skip_delete_on_errors is False or - (self.options.docked_skip_delete_on_error and returncode == 0)): - sys.stdout.write(' * Cleaning Up Temporary Docker Container. CID: ') + if self.options.docked_skip_delete is False and ( + self.options.docked_skip_delete_on_errors is False + or (self.options.docked_skip_delete_on_error and returncode == 0) + ): + sys.stdout.write(" * Cleaning Up Temporary Docker Container. CID: ") sys.stdout.flush() cleanup_call = subprocess.Popen( - [self.options.docker_binary, 'rm', cid], + [self.options.docker_binary, "rm", cid], env=os.environ.copy(), close_fds=True, - stdout=subprocess.PIPE + stdout=subprocess.PIPE, ) cleanup_call.wait() output = cleanup_call.stdout.read().strip() @@ -957,29 +1036,32 @@ class SaltTestingParser(optparse.OptionParser): output = output.decode(__salt_system_encoding__) print(output) - if 'DOCKER_CIDFILE' not in os.environ: + if "DOCKER_CIDFILE" not in os.environ: # The CID file was not created "from the outside", so delete it os.unlink(cidfile) - print_header('', inline=True, width=self.options.output_columns) + print_header("", inline=True, width=self.options.output_columns) # Finally, EXIT! sys.exit(returncode) # Let's start the Docker container and run the tests suite there - if '/' not in self.options.docked: - container = 'salttest/{0}'.format(self.options.docked) + if "/" not in self.options.docked: + container = "salttest/{0}".format(self.options.docked) else: container = self.options.docked - calling_args = [self.options.docked_interpreter, - '/salt-source/tests/runtests.py'] + calling_args = [ + self.options.docked_interpreter, + "/salt-source/tests/runtests.py", + ] for option in self._get_all_options(): if option.dest is None: # For example --version continue - if option.dest and (option.dest in ('verbosity',) or - option.dest.startswith('docked')): + if option.dest and ( + option.dest in ("verbosity",) or option.dest.startswith("docked") + ): # We don't need to pass any docker related arguments inside the # container, and verbose will be handled bellow continue @@ -992,60 +1074,62 @@ class SaltTestingParser(optparse.OptionParser): # parser continue - if option.action.startswith('store_'): + if option.action.startswith("store_"): calling_args.append(option.get_opt_string()) - elif option.action == 'append': + elif option.action == "append": for val in value is not None and value or default: calling_args.extend([option.get_opt_string(), str(val)]) - elif option.action == 'count': + elif option.action == "count": calling_args.extend([option.get_opt_string()] * value) else: calling_args.extend( - [option.get_opt_string(), - str(value is not None and value or default)] + [ + option.get_opt_string(), + str(value is not None and value or default), + ] ) if not self.options.run_destructive: - calling_args.append('--run-destructive') + calling_args.append("--run-destructive") if self.options.verbosity > 1: - calling_args.append( - '-{0}'.format('v' * (self.options.verbosity - 1)) - ) + calling_args.append("-{0}".format("v" * (self.options.verbosity - 1))) - sys.stdout.write(' * Docker command: {0}\n'.format(' '.join(calling_args))) - sys.stdout.write(' * Running the tests suite under the {0!r} docker ' - 'container. CID: '.format(container)) + sys.stdout.write(" * Docker command: {0}\n".format(" ".join(calling_args))) + sys.stdout.write( + " * Running the tests suite under the {0!r} docker " + "container. CID: ".format(container) + ) sys.stdout.flush() cidfile = os.environ.get( - 'DOCKER_CIDFILE', - tempfile.mktemp(prefix='docked-testsuite-', suffix='.cid') + "DOCKER_CIDFILE", tempfile.mktemp(prefix="docked-testsuite-", suffix=".cid") ) call = subprocess.Popen( - [self.options.docker_binary, - 'run', - # '--rm=true', Do not remove the container automatically, we need - # to get information back, even for stopped containers - '--tty', - '--interactive', - '-v', - '{0}:/salt-source'.format(self.source_code_basedir), - '-w', - '/salt-source', - '-e', - 'SHELL=/bin/sh', - '-e', - 'COLUMNS={0}'.format(WIDTH), - '-e', - 'LINES={0}'.format(HEIGHT), - '--cidfile={0}'.format(cidfile), - container, - # We need to pass the runtests.py arguments as a single string so - # that the start-me-up.sh script can handle them properly - ' '.join(calling_args), - ], + [ + self.options.docker_binary, + "run", + # '--rm=true', Do not remove the container automatically, we need + # to get information back, even for stopped containers + "--tty", + "--interactive", + "-v", + "{0}:/salt-source".format(self.source_code_basedir), + "-w", + "/salt-source", + "-e", + "SHELL=/bin/sh", + "-e", + "COLUMNS={0}".format(WIDTH), + "-e", + "LINES={0}".format(HEIGHT), + "--cidfile={0}".format(cidfile), + container, + # We need to pass the runtests.py arguments as a single string so + # that the start-me-up.sh script can handle them properly + " ".join(calling_args), + ], env=os.environ.copy(), close_fds=True, ) @@ -1060,7 +1144,8 @@ class SaltTestingParser(optparse.OptionParser): try: time.sleep(0.15) if cid_printed is False: - with open(cidfile) as cidfile_fd: # pylint: disable=resource-leakage + # pylint: disable=resource-leakage + with open(cidfile) as cidfile_fd: cid = cidfile_fd.read() if cid: print(cid) @@ -1068,13 +1153,17 @@ class SaltTestingParser(optparse.OptionParser): cid_printed = True # Install our signal handler to properly shutdown # the docker container - for sig in (signal.SIGTERM, signal.SIGINT, - signal.SIGHUP, signal.SIGQUIT): + for sig in ( + signal.SIGTERM, + signal.SIGINT, + signal.SIGHUP, + signal.SIGQUIT, + ): signal.signal( - sig, - partial(stop_running_docked_container, cid) + sig, partial(stop_running_docked_container, cid) ) signal_handler_installed = True + # pylint: enable=resource-leakage if exiting: break @@ -1091,7 +1180,7 @@ class SaltTestingParser(optparse.OptionParser): # Finished break except KeyboardInterrupt: - print('Caught CTRL-C, exiting...') + print("Caught CTRL-C, exiting...") signalled = True call.send_signal(signal.SIGINT) @@ -1101,33 +1190,33 @@ class SaltTestingParser(optparse.OptionParser): # Finish up if signal_handler_installed: stop_running_docked_container( - cid, - signum=(signal.SIGINT if signalled else WEIRD_SIGNAL_NUM) + cid, signum=(signal.SIGINT if signalled else WEIRD_SIGNAL_NUM) ) else: sys.exit(call.returncode) class SaltTestcaseParser(SaltTestingParser): - ''' + """ Option parser to run one or more ``unittest.case.TestCase``, ie, no discovery involved. - ''' + """ + def __init__(self, *args, **kwargs): SaltTestingParser.__init__(self, None, *args, **kwargs) - self.usage = '%prog [options]' + self.usage = "%prog [options]" self.option_groups.remove(self.test_selection_group) - if self.has_option('--xml-out'): - self.remove_option('--xml-out') + if self.has_option("--xml-out"): + self.remove_option("--xml-out") def get_prog_name(self): - return '{0} {1}'.format(sys.executable.split(os.sep)[-1], sys.argv[0]) + return "{0} {1}".format(sys.executable.split(os.sep)[-1], sys.argv[0]) def run_testcase(self, testcase): - ''' + """ Run one or more ``unittest.case.TestCase`` - ''' - header = '' + """ + header = "" loader = TestLoader() if isinstance(testcase, list): for case in testcase: @@ -1136,13 +1225,13 @@ class SaltTestcaseParser(SaltTestingParser): tests = loader.loadTestsFromTestCase(testcase) if not isinstance(testcase, list): - header = '{0} Tests'.format(testcase.__name__) - print_header('Starting {0}'.format(header), - width=self.options.output_columns) + header = "{0} Tests".format(testcase.__name__) + print_header( + "Starting {0}".format(header), width=self.options.output_columns + ) runner = TextTestRunner( - verbosity=self.options.verbosity, - failfast=self.options.failfast, + verbosity=self.options.verbosity, failfast=self.options.failfast, ).run(tests) self.testsuite_results.append((header, runner)) return runner.wasSuccessful() diff --git a/tests/support/parser/cover.py b/tests/support/parser/cover.py index cedf2e3876e..c3abed48174 100644 --- a/tests/support/parser/cover.py +++ b/tests/support/parser/cover.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" tests.support.parser.cover ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -8,15 +8,16 @@ :codeauthor: Pedro Algarvio (pedro@algarvio.me) :copyright: Copyright 2013 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. -''' +""" # pylint: disable=repr-flag-used-in-string # Import python libs from __future__ import absolute_import, print_function + import os import re -import sys import shutil +import sys import warnings # Import Salt libs @@ -28,43 +29,43 @@ from tests.support.parser import SaltTestingParser # Import coverage libs try: import coverage + COVERAGE_AVAILABLE = True except ImportError: COVERAGE_AVAILABLE = False try: import multiprocessing.util + # Force forked multiprocessing processes to be measured as well def multiprocessing_stop(coverage_object): - ''' + """ Save the multiprocessing process coverage object - ''' + """ coverage_object.stop() coverage_object.save() def multiprocessing_start(obj): - coverage_options = salt.utils.json.loads(os.environ.get('COVERAGE_OPTIONS', '{}')) + coverage_options = salt.utils.json.loads( + os.environ.get("COVERAGE_OPTIONS", "{}") + ) if not coverage_options: return - if coverage_options.get('data_suffix', False) is False: + if coverage_options.get("data_suffix", False) is False: return coverage_object = coverage.coverage(**coverage_options) coverage_object.start() multiprocessing.util.Finalize( - None, - multiprocessing_stop, - args=(coverage_object,), - exitpriority=1000 + None, multiprocessing_stop, args=(coverage_object,), exitpriority=1000 ) if COVERAGE_AVAILABLE: multiprocessing.util.register_after_fork( - multiprocessing_start, - multiprocessing_start + multiprocessing_start, multiprocessing_start ) except ImportError: pass @@ -75,18 +76,21 @@ if COVERAGE_AVAILABLE: class SaltCoverageTestingParser(SaltTestingParser): - ''' + """ Code coverage aware testing option parser - ''' + """ + def __init__(self, *args, **kwargs): - if kwargs.pop('html_output_from_env', None) is not None or \ - kwargs.pop('html_output_dir', None) is not None: + if ( + kwargs.pop("html_output_from_env", None) is not None + or kwargs.pop("html_output_dir", None) is not None + ): warnings.warn( - 'The unit tests HTML support was removed from {0}. Please ' - 'stop passing \'html_output_dir\' or \'html_output_from_env\' ' - 'as arguments to {0}'.format(self.__class__.__name__), + "The unit tests HTML support was removed from {0}. Please " + "stop passing 'html_output_dir' or 'html_output_from_env' " + "as arguments to {0}".format(self.__class__.__name__), category=DeprecationWarning, - stacklevel=2 + stacklevel=2, ) SaltTestingParser.__init__(self, *args, **kwargs) @@ -94,53 +98,60 @@ class SaltCoverageTestingParser(SaltTestingParser): # Add the coverage related options self.output_options_group.add_option( - '--coverage', + "--coverage", default=False, - action='store_true', - help='Run tests and report code coverage' + action="store_true", + help="Run tests and report code coverage", ) self.output_options_group.add_option( - '--no-processes-coverage', + "--no-processes-coverage", default=False, - action='store_true', - help='Do not track subprocess and/or multiprocessing processes' + action="store_true", + help="Do not track subprocess and/or multiprocessing processes", ) self.output_options_group.add_option( - '--coverage-xml', + "--coverage-xml", default=None, - help='If provided, the path to where a XML report of the code ' - 'coverage will be written to' + help="If provided, the path to where a XML report of the code " + "coverage will be written to", ) self.output_options_group.add_option( - '--coverage-html', + "--coverage-html", default=None, - help=('The directory where the generated HTML coverage report ' - 'will be saved to. The directory, if existing, will be ' - 'deleted before the report is generated.') + help=( + "The directory where the generated HTML coverage report " + "will be saved to. The directory, if existing, will be " + "deleted before the report is generated." + ), ) def _validate_options(self): - if (self.options.coverage_xml or self.options.coverage_html) and \ - not self.options.coverage: + if ( + self.options.coverage_xml or self.options.coverage_html + ) and not self.options.coverage: self.options.coverage = True if self.options.coverage is True and COVERAGE_AVAILABLE is False: self.error( - 'Cannot run tests with coverage report. ' - 'Please install coverage>=3.5.3' + "Cannot run tests with coverage report. " + "Please install coverage>=3.5.3" ) if self.options.coverage is True: - coverage_version = tuple([ - int(part) for part in re.search( - r'([0-9.]+)', coverage.__version__).group(0).split('.') - ]) + coverage_version = tuple( + [ + int(part) + for part in re.search(r"([0-9.]+)", coverage.__version__) + .group(0) + .split(".") + ] + ) if coverage_version < (3, 5, 3): # Should we just print the error instead of exiting? self.error( - 'Versions lower than 3.5.3 of the coverage library are ' - 'know to produce incorrect results. Please consider ' - 'upgrading...' + "Versions lower than 3.5.3 of the coverage library are " + "know to produce incorrect results. Please consider " + "upgrading..." ) SaltTestingParser._validate_options(self) @@ -154,83 +165,79 @@ class SaltCoverageTestingParser(SaltTestingParser): SaltTestingParser.pre_execution_cleanup(self) def start_coverage(self, **coverage_options): - ''' + """ Start code coverage. You can pass any coverage options as keyword arguments. For the available options please see: http://nedbatchelder.com/code/coverage/api.html - ''' + """ if self.options.coverage is False: return - if coverage_options.pop('track_processes', None) is not None: + if coverage_options.pop("track_processes", None) is not None: raise RuntimeWarning( - 'Please stop passing \'track_processes\' to ' - '\'start_coverage()\'. It\'s now the default and ' - '\'--no-processes-coverage\' was added to the parser to ' - 'disable it.' + "Please stop passing 'track_processes' to " + "'start_coverage()'. It's now the default and " + "'--no-processes-coverage' was added to the parser to " + "disable it." ) - print(' * Starting Coverage') + print(" * Starting Coverage") if self.options.no_processes_coverage is False: # Update environ so that any subprocess started on tests are also # included in the report - coverage_options['data_suffix'] = True - os.environ['COVERAGE_PROCESS_START'] = '' - os.environ['COVERAGE_OPTIONS'] = salt.utils.json.dumps(coverage_options) + coverage_options["data_suffix"] = True + os.environ["COVERAGE_PROCESS_START"] = "" + os.environ["COVERAGE_OPTIONS"] = salt.utils.json.dumps(coverage_options) # Setup coverage self.code_coverage = coverage.coverage(**coverage_options) self.code_coverage.start() def stop_coverage(self, save_coverage=True): - ''' + """ Stop code coverage. - ''' + """ if self.options.coverage is False: return # Clean up environment - os.environ.pop('COVERAGE_OPTIONS', None) - os.environ.pop('COVERAGE_PROCESS_START', None) + os.environ.pop("COVERAGE_OPTIONS", None) + os.environ.pop("COVERAGE_PROCESS_START", None) - print(' * Stopping coverage') + print(" * Stopping coverage") self.code_coverage.stop() if save_coverage: - print(' * Saving coverage info') + print(" * Saving coverage info") self.code_coverage.save() if self.options.no_processes_coverage is False: # Combine any multiprocessing coverage data files - sys.stdout.write(' * Combining multiple coverage info files ... ') + sys.stdout.write(" * Combining multiple coverage info files ... ") sys.stdout.flush() self.code_coverage.combine() - print('Done.') + print("Done.") if self.options.coverage_xml is not None: sys.stdout.write( - ' * Generating Coverage XML Report At {0!r} ... '.format( + " * Generating Coverage XML Report At {0!r} ... ".format( self.options.coverage_xml ) ) sys.stdout.flush() - self.code_coverage.xml_report( - outfile=self.options.coverage_xml - ) - print('Done.') + self.code_coverage.xml_report(outfile=self.options.coverage_xml) + print("Done.") if self.options.coverage_html is not None: sys.stdout.write( - ' * Generating Coverage HTML Report Under {0!r} ... '.format( + " * Generating Coverage HTML Report Under {0!r} ... ".format( self.options.coverage_html ) ) sys.stdout.flush() - self.code_coverage.html_report( - directory=self.options.coverage_html - ) - print('Done.') + self.code_coverage.html_report(directory=self.options.coverage_html) + print("Done.") def finalize(self, exit_code=0): if self.options.coverage is True: diff --git a/tests/support/paths.py b/tests/support/paths.py index b8dadeddebf..60228ae5ee1 100644 --- a/tests/support/paths.py +++ b/tests/support/paths.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. @@ -9,32 +9,35 @@ ~~~~~~~~~~~~~~~~~~~ Tests related paths -''' +""" # Import python libs from __future__ import absolute_import + +import logging import os import re import sys -import logging import tempfile import salt.utils.path log = logging.getLogger(__name__) -TESTS_DIR = os.path.dirname(os.path.dirname(os.path.normpath(os.path.abspath(__file__)))) -if TESTS_DIR.startswith('//'): +TESTS_DIR = os.path.dirname( + os.path.dirname(os.path.normpath(os.path.abspath(__file__))) +) +if TESTS_DIR.startswith("//"): # Have we been given an initial double forward slash? Ditch it! TESTS_DIR = TESTS_DIR[1:] -if sys.platform.startswith('win'): +if sys.platform.startswith("win"): TESTS_DIR = os.path.normcase(TESTS_DIR) CODE_DIR = os.path.dirname(TESTS_DIR) -if sys.platform.startswith('win'): - CODE_DIR = CODE_DIR.replace('\\', '\\\\') -UNIT_TEST_DIR = os.path.join(TESTS_DIR, 'unit') -INTEGRATION_TEST_DIR = os.path.join(TESTS_DIR, 'integration') -MULTIMASTER_TEST_DIR = os.path.join(TESTS_DIR, 'multimaster') +if sys.platform.startswith("win"): + CODE_DIR = CODE_DIR.replace("\\", "\\\\") +UNIT_TEST_DIR = os.path.join(TESTS_DIR, "unit") +INTEGRATION_TEST_DIR = os.path.join(TESTS_DIR, "integration") +MULTIMASTER_TEST_DIR = os.path.join(TESTS_DIR, "multimaster") # Let's inject CODE_DIR so salt is importable if not there already if TESTS_DIR in sys.path: @@ -46,50 +49,54 @@ if CODE_DIR not in sys.path: if TESTS_DIR not in sys.path: sys.path.insert(1, TESTS_DIR) -SYS_TMP_DIR = os.path.abspath(os.path.realpath( - # Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long - # for unix sockets: ``error: AF_UNIX path too long`` - # Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR} - os.environ.get('TMPDIR', tempfile.gettempdir()) if not sys.platform.startswith('darwin') else '/tmp' -)) -TMP = os.path.join(SYS_TMP_DIR, 'salt-tests-tmpdir') -TMP_ROOT_DIR = os.path.join(TMP, 'rootdir') -FILES = os.path.join(INTEGRATION_TEST_DIR, 'files') -BASE_FILES = os.path.join(INTEGRATION_TEST_DIR, 'files', 'file', 'base') -PROD_FILES = os.path.join(INTEGRATION_TEST_DIR, 'files', 'file', 'prod') -PYEXEC = 'python{0}.{1}'.format(*sys.version_info) -MOCKBIN = os.path.join(INTEGRATION_TEST_DIR, 'mockbin') -SCRIPT_DIR = os.path.join(CODE_DIR, 'scripts') -TMP_STATE_TREE = os.path.join(SYS_TMP_DIR, 'salt-temp-state-tree') -TMP_PILLAR_TREE = os.path.join(SYS_TMP_DIR, 'salt-temp-pillar-tree') -TMP_PRODENV_STATE_TREE = os.path.join(SYS_TMP_DIR, 'salt-temp-prodenv-state-tree') -TMP_CONF_DIR = os.path.join(TMP, 'config') -TMP_SUB_MINION_CONF_DIR = os.path.join(TMP_CONF_DIR, 'sub-minion') -TMP_SYNDIC_MINION_CONF_DIR = os.path.join(TMP_CONF_DIR, 'syndic-minion') -TMP_SYNDIC_MASTER_CONF_DIR = os.path.join(TMP_CONF_DIR, 'syndic-master') -TMP_MM_CONF_DIR = os.path.join(TMP_CONF_DIR, 'multimaster') -TMP_MM_SUB_CONF_DIR = os.path.join(TMP_CONF_DIR, 'sub-multimaster') -TMP_PROXY_CONF_DIR = os.path.join(TMP_CONF_DIR, 'proxy') -CONF_DIR = os.path.join(INTEGRATION_TEST_DIR, 'files', 'conf') -PILLAR_DIR = os.path.join(FILES, 'pillar') -TMP_SCRIPT_DIR = os.path.join(TMP, 'scripts') -ENGINES_DIR = os.path.join(FILES, 'engines') -LOG_HANDLERS_DIR = os.path.join(FILES, 'log_handlers') +SYS_TMP_DIR = os.path.abspath( + os.path.realpath( + # Avoid ${TMPDIR} and gettempdir() on MacOS as they yield a base path too long + # for unix sockets: ``error: AF_UNIX path too long`` + # Gentoo Portage prefers ebuild tests are rooted in ${TMPDIR} + os.environ.get("TMPDIR", tempfile.gettempdir()) + if not sys.platform.startswith("darwin") + else "/tmp" + ) +) +TMP = os.path.join(SYS_TMP_DIR, "salt-tests-tmpdir") +TMP_ROOT_DIR = os.path.join(TMP, "rootdir") +FILES = os.path.join(INTEGRATION_TEST_DIR, "files") +BASE_FILES = os.path.join(INTEGRATION_TEST_DIR, "files", "file", "base") +PROD_FILES = os.path.join(INTEGRATION_TEST_DIR, "files", "file", "prod") +PYEXEC = "python{0}.{1}".format(*sys.version_info) +MOCKBIN = os.path.join(INTEGRATION_TEST_DIR, "mockbin") +SCRIPT_DIR = os.path.join(CODE_DIR, "scripts") +TMP_STATE_TREE = os.path.join(SYS_TMP_DIR, "salt-temp-state-tree") +TMP_PILLAR_TREE = os.path.join(SYS_TMP_DIR, "salt-temp-pillar-tree") +TMP_PRODENV_STATE_TREE = os.path.join(SYS_TMP_DIR, "salt-temp-prodenv-state-tree") +TMP_CONF_DIR = os.path.join(TMP, "config") +TMP_SUB_MINION_CONF_DIR = os.path.join(TMP_CONF_DIR, "sub-minion") +TMP_SYNDIC_MINION_CONF_DIR = os.path.join(TMP_CONF_DIR, "syndic-minion") +TMP_SYNDIC_MASTER_CONF_DIR = os.path.join(TMP_CONF_DIR, "syndic-master") +TMP_MM_CONF_DIR = os.path.join(TMP_CONF_DIR, "multimaster") +TMP_MM_SUB_CONF_DIR = os.path.join(TMP_CONF_DIR, "sub-multimaster") +TMP_PROXY_CONF_DIR = os.path.join(TMP_CONF_DIR, "proxy") +CONF_DIR = os.path.join(INTEGRATION_TEST_DIR, "files", "conf") +PILLAR_DIR = os.path.join(FILES, "pillar") +TMP_SCRIPT_DIR = os.path.join(TMP, "scripts") +ENGINES_DIR = os.path.join(FILES, "engines") +LOG_HANDLERS_DIR = os.path.join(FILES, "log_handlers") def list_test_mods(): - ''' + """ A generator which returns all of the test files - ''' - test_re = re.compile(r'^test_.+\.py$') + """ + test_re = re.compile(r"^test_.+\.py$") for dirname in (UNIT_TEST_DIR, INTEGRATION_TEST_DIR, MULTIMASTER_TEST_DIR): test_type = os.path.basename(dirname) for root, _, files in salt.utils.path.os_walk(dirname): - parent_mod = root[len(dirname):].lstrip(os.sep).replace(os.sep, '.') + parent_mod = root[len(dirname) :].lstrip(os.sep).replace(os.sep, ".") for filename in files: if test_re.match(filename): mod_name = test_type if parent_mod: - mod_name += '.' + parent_mod - mod_name += '.' + filename[:-3] + mod_name += "." + parent_mod + mod_name += "." + filename[:-3] yield mod_name diff --git a/tests/support/processes.py b/tests/support/processes.py index 6fe5e850ef7..92e6a4b84e2 100644 --- a/tests/support/processes.py +++ b/tests/support/processes.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. @@ -8,22 +8,28 @@ ~~~~~~~~~~~~~~~~~~~~~~~ Process handling utilities -''' +""" # Import python libs from __future__ import absolute_import + import logging -# Import pytest-salt libs -from pytestsalt.utils import collect_child_processes, terminate_process, terminate_process_list # pylint: disable=unused-import from pytestsalt.fixtures.daemons import Salt as PytestSalt -from pytestsalt.fixtures.daemons import SaltKey as PytestSaltKey -from pytestsalt.fixtures.daemons import SaltRun as PytestSaltRun from pytestsalt.fixtures.daemons import SaltCall as PytestSaltCall +from pytestsalt.fixtures.daemons import SaltKey as PytestSaltKey from pytestsalt.fixtures.daemons import SaltMaster as PytestSaltMaster from pytestsalt.fixtures.daemons import SaltMinion as PytestSaltMinion -from pytestsalt.fixtures.daemons import SaltSyndic as PytestSaltSyndic from pytestsalt.fixtures.daemons import SaltProxy as PytestSaltProxy +from pytestsalt.fixtures.daemons import SaltRun as PytestSaltRun +from pytestsalt.fixtures.daemons import SaltSyndic as PytestSaltSyndic + +# Import pytest-salt libs +from pytestsalt.utils import ( # pylint: disable=unused-import + collect_child_processes, + terminate_process, + terminate_process_list, +) # Import tests support libs from tests.support.cli_scripts import ScriptPathMixin @@ -32,148 +38,161 @@ log = logging.getLogger(__name__) class GetSaltRunFixtureMixin(ScriptPathMixin): - ''' + """ Override this classes `get_salt_run_fixture` because we're still not running under pytest - ''' + """ def get_salt_run_fixture(self): pass class Salt(ScriptPathMixin, PytestSalt): - ''' + """ Class which runs salt-call commands - ''' + """ + def __init__(self, *args, **kwargs): super(Salt, self).__init__(None, *args, **kwargs) class SaltCall(ScriptPathMixin, PytestSaltCall): - ''' + """ Class which runs salt-call commands - ''' + """ + def __init__(self, *args, **kwargs): super(SaltCall, self).__init__(None, *args, **kwargs) class SaltKey(ScriptPathMixin, PytestSaltKey): - ''' + """ Class which runs salt-key commands - ''' + """ + def __init__(self, *args, **kwargs): super(SaltKey, self).__init__(None, *args, **kwargs) class SaltRun(ScriptPathMixin, PytestSaltRun): - ''' + """ Class which runs salt-run commands - ''' + """ + def __init__(self, *args, **kwargs): super(SaltRun, self).__init__(None, *args, **kwargs) class SaltProxy(GetSaltRunFixtureMixin, PytestSaltProxy): - ''' + """ Class which runs the salt-proxy daemon - ''' + """ class SaltMinion(GetSaltRunFixtureMixin, PytestSaltMinion): - ''' + """ Class which runs the salt-minion daemon - ''' + """ class SaltMaster(GetSaltRunFixtureMixin, PytestSaltMaster): - ''' + """ Class which runs the salt-master daemon - ''' + """ class SaltSyndic(GetSaltRunFixtureMixin, PytestSaltSyndic): - ''' + """ Class which runs the salt-syndic daemon - ''' + """ -def start_daemon(daemon_name=None, - daemon_id=None, - daemon_log_prefix=None, - daemon_cli_script_name=None, - daemon_config=None, - daemon_config_dir=None, - daemon_class=None, - bin_dir_path=None, - fail_hard=False, - start_timeout=10, - slow_stop=False, - environ=None, - cwd=None, - event_listener_config_dir=None): - ''' +def start_daemon( + daemon_name=None, + daemon_id=None, + daemon_log_prefix=None, + daemon_cli_script_name=None, + daemon_config=None, + daemon_config_dir=None, + daemon_class=None, + bin_dir_path=None, + fail_hard=False, + start_timeout=10, + slow_stop=False, + environ=None, + cwd=None, + event_listener_config_dir=None, +): + """ Returns a running salt daemon - ''' + """ # Old config name - daemon_config['pytest_port'] = daemon_config['runtests_conn_check_port'] + daemon_config["pytest_port"] = daemon_config["runtests_conn_check_port"] # New config name - daemon_config['pytest_engine_port'] = daemon_config['runtests_conn_check_port'] + daemon_config["pytest_engine_port"] = daemon_config["runtests_conn_check_port"] request = None if fail_hard: fail_method = RuntimeError else: fail_method = RuntimeWarning - log.info('[%s] Starting pytest %s(%s)', daemon_name, daemon_log_prefix, daemon_id) + log.info("[%s] Starting pytest %s(%s)", daemon_name, daemon_log_prefix, daemon_id) attempts = 0 process = None while attempts <= 3: # pylint: disable=too-many-nested-blocks attempts += 1 try: - process = daemon_class(request=request, - config=daemon_config, - config_dir=daemon_config_dir, - bin_dir_path=bin_dir_path, - log_prefix=daemon_log_prefix, - cli_script_name=daemon_cli_script_name, - slow_stop=slow_stop, - environ=environ, - cwd=cwd, - event_listener_config_dir=event_listener_config_dir) + process = daemon_class( + request=request, + config=daemon_config, + config_dir=daemon_config_dir, + bin_dir_path=bin_dir_path, + log_prefix=daemon_log_prefix, + cli_script_name=daemon_cli_script_name, + slow_stop=slow_stop, + environ=environ, + cwd=cwd, + event_listener_config_dir=event_listener_config_dir, + ) except TypeError: - process = daemon_class(request=request, - config=daemon_config, - config_dir=daemon_config_dir, - bin_dir_path=bin_dir_path, - log_prefix=daemon_log_prefix, - cli_script_name=daemon_cli_script_name, - slow_stop=slow_stop, - environ=environ, - cwd=cwd) + process = daemon_class( + request=request, + config=daemon_config, + config_dir=daemon_config_dir, + bin_dir_path=bin_dir_path, + log_prefix=daemon_log_prefix, + cli_script_name=daemon_cli_script_name, + slow_stop=slow_stop, + environ=environ, + cwd=cwd, + ) process.start() if process.is_alive(): try: connectable = process.wait_until_running(timeout=start_timeout) if connectable is False: - connectable = process.wait_until_running(timeout=start_timeout/2) + connectable = process.wait_until_running(timeout=start_timeout / 2) if connectable is False: process.terminate() if attempts >= 3: fail_method( - 'The pytest {0}({1}) has failed to confirm running status ' - 'after {2} attempts'.format(daemon_name, daemon_id, attempts)) + "The pytest {0}({1}) has failed to confirm running status " + "after {2} attempts".format( + daemon_name, daemon_id, attempts + ) + ) continue except Exception as exc: # pylint: disable=broad-except - log.exception('[%s] %s', daemon_log_prefix, exc, exc_info=True) + log.exception("[%s] %s", daemon_log_prefix, exc, exc_info=True) terminate_process(process.pid, kill_children=True, slow_stop=slow_stop) if attempts >= 3: raise fail_method(str(exc)) continue log.info( - '[%s] The pytest %s(%s) is running and accepting commands ' - 'after %d attempts', + "[%s] The pytest %s(%s) is running and accepting commands " + "after %d attempts", daemon_log_prefix, daemon_name, daemon_id, - attempts + attempts, ) break @@ -184,10 +203,8 @@ def start_daemon(daemon_name=None, if process is not None: terminate_process(process.pid, kill_children=True, slow_stop=slow_stop) raise fail_method( - 'The pytest {0}({1}) has failed to start after {2} attempts'.format( - daemon_name, - daemon_id, - attempts-1 + "The pytest {0}({1}) has failed to start after {2} attempts".format( + daemon_name, daemon_id, attempts - 1 ) ) return process diff --git a/tests/support/runtests.py b/tests/support/runtests.py index 4ee5810838a..6ca31e887ad 100644 --- a/tests/support/runtests.py +++ b/tests/support/runtests.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) .. _runtime_vars: @@ -44,36 +44,38 @@ .. _`pytest`: http://pytest.org .. _`nose`: https://nose.readthedocs.org - ''' + """ # Import Python modules from __future__ import absolute_import, print_function + +import logging import os import shutil -import logging # Import Salt libs import salt.utils.path import salt.utils.platform -try: - import pwd -except ImportError: - import salt.utils.win_functions - # Import tests support libs import tests.support.paths as paths # Import 3rd-party libs from salt.ext import six +try: + import pwd +except ImportError: + import salt.utils.win_functions + + log = logging.getLogger(__name__) def this_user(): - ''' + """ Get the user associated with the current process. - ''' + """ if salt.utils.platform.is_windows(): return salt.utils.win_functions.get_current_user(with_domain=False) return pwd.getpwuid(os.getuid())[0] @@ -98,28 +100,32 @@ def recursive_copytree(source, destination, overwrite=False): for root, dirs, files in os.walk(source): for item in dirs: src_path = os.path.join(root, item) - dst_path = os.path.join(destination, src_path.replace(source, '').lstrip(os.sep)) + dst_path = os.path.join( + destination, src_path.replace(source, "").lstrip(os.sep) + ) if not os.path.exists(dst_path): - log.debug('Creating directory: %s', dst_path) + log.debug("Creating directory: %s", dst_path) os.makedirs(dst_path) for item in files: src_path = os.path.join(root, item) - dst_path = os.path.join(destination, src_path.replace(source, '').lstrip(os.sep)) + dst_path = os.path.join( + destination, src_path.replace(source, "").lstrip(os.sep) + ) if os.path.exists(dst_path) and not overwrite: if os.stat(src_path).st_mtime > os.stat(dst_path).st_mtime: - log.debug('Copying %s to %s', src_path, dst_path) + log.debug("Copying %s to %s", src_path, dst_path) shutil.copy2(src_path, dst_path) else: if not os.path.isdir(os.path.dirname(dst_path)): - log.debug('Creating directory: %s', os.path.dirname(dst_path)) + log.debug("Creating directory: %s", os.path.dirname(dst_path)) os.makedirs(os.path.dirname(dst_path)) - log.debug('Copying %s to %s', src_path, dst_path) + log.debug("Copying %s to %s", src_path, dst_path) shutil.copy2(src_path, dst_path) class RuntimeVars(object): - __self_attributes__ = ('_vars', '_locked', 'lock') + __self_attributes__ = ("_vars", "_locked", "lock") def __init__(self, **kwargs): self._vars = kwargs @@ -128,6 +134,7 @@ class RuntimeVars(object): def lock(self): # Late import from salt.utils.immutabletypes import freeze + frozen_vars = freeze(self._vars.copy()) self._vars = frozen_vars self._locked = True @@ -137,18 +144,18 @@ class RuntimeVars(object): yield name, value def __getattribute__(self, name): - if name in object.__getattribute__(self, '_vars'): - return object.__getattribute__(self, '_vars')[name] + if name in object.__getattribute__(self, "_vars"): + return object.__getattribute__(self, "_vars")[name] return object.__getattribute__(self, name) def __setattr__(self, name, value): - if getattr(self, '_locked', False) is True: + if getattr(self, "_locked", False) is True: raise RuntimeError( - 'After {0} is locked, no additional data can be added to it'.format( + "After {0} is locked, no additional data can be added to it".format( self.__class__.__name__ ) ) - if name in object.__getattribute__(self, '__self_attributes__'): + if name in object.__getattribute__(self, "__self_attributes__"): object.__setattr__(self, name, value) return self._vars[name] = value @@ -158,7 +165,9 @@ class RuntimeVars(object): # ----- Global Variables --------------------------------------------------------------------------------------------> -XML_OUTPUT_DIR = os.environ.get('SALT_XML_TEST_REPORTS_DIR', os.path.join(paths.TMP, 'xml-test-reports')) +XML_OUTPUT_DIR = os.environ.get( + "SALT_XML_TEST_REPORTS_DIR", os.path.join(paths.TMP, "xml-test-reports") +) # <---- Global Variables --------------------------------------------------------------------------------------------- @@ -174,12 +183,16 @@ RUNTIME_VARS = RuntimeVars( LOG_HANDLERS_DIR=paths.LOG_HANDLERS_DIR, TMP_ROOT_DIR=paths.TMP_ROOT_DIR, TMP_CONF_DIR=paths.TMP_CONF_DIR, - TMP_CONF_MASTER_INCLUDES=os.path.join(paths.TMP_CONF_DIR, 'master.d'), - TMP_CONF_MINION_INCLUDES=os.path.join(paths.TMP_CONF_DIR, 'minion.d'), - TMP_CONF_PROXY_INCLUDES=os.path.join(paths.TMP_CONF_DIR, 'proxy.d'), - TMP_CONF_CLOUD_INCLUDES=os.path.join(paths.TMP_CONF_DIR, 'cloud.conf.d'), - TMP_CONF_CLOUD_PROFILE_INCLUDES=os.path.join(paths.TMP_CONF_DIR, 'cloud.profiles.d'), - TMP_CONF_CLOUD_PROVIDER_INCLUDES=os.path.join(paths.TMP_CONF_DIR, 'cloud.providers.d'), + TMP_CONF_MASTER_INCLUDES=os.path.join(paths.TMP_CONF_DIR, "master.d"), + TMP_CONF_MINION_INCLUDES=os.path.join(paths.TMP_CONF_DIR, "minion.d"), + TMP_CONF_PROXY_INCLUDES=os.path.join(paths.TMP_CONF_DIR, "proxy.d"), + TMP_CONF_CLOUD_INCLUDES=os.path.join(paths.TMP_CONF_DIR, "cloud.conf.d"), + TMP_CONF_CLOUD_PROFILE_INCLUDES=os.path.join( + paths.TMP_CONF_DIR, "cloud.profiles.d" + ), + TMP_CONF_CLOUD_PROVIDER_INCLUDES=os.path.join( + paths.TMP_CONF_DIR, "cloud.providers.d" + ), TMP_SUB_MINION_CONF_DIR=paths.TMP_SUB_MINION_CONF_DIR, TMP_SYNDIC_MASTER_CONF_DIR=paths.TMP_SYNDIC_MASTER_CONF_DIR, TMP_SYNDIC_MINION_CONF_DIR=paths.TMP_SYNDIC_MINION_CONF_DIR, @@ -189,14 +202,18 @@ RUNTIME_VARS = RuntimeVars( TMP_STATE_TREE=paths.TMP_STATE_TREE, TMP_PILLAR_TREE=paths.TMP_PILLAR_TREE, TMP_PRODENV_STATE_TREE=paths.TMP_PRODENV_STATE_TREE, - SHELL_TRUE_PATH=salt.utils.path.which('true') if not salt.utils.platform.is_windows() else 'cmd /c exit 0 > nul', - SHELL_FALSE_PATH=salt.utils.path.which('false') if not salt.utils.platform.is_windows() else 'cmd /c exit 1 > nul', + SHELL_TRUE_PATH=salt.utils.path.which("true") + if not salt.utils.platform.is_windows() + else "cmd /c exit 0 > nul", + SHELL_FALSE_PATH=salt.utils.path.which("false") + if not salt.utils.platform.is_windows() + else "cmd /c exit 1 > nul", RUNNING_TESTS_USER=this_user(), RUNTIME_CONFIGS={}, CODE_DIR=paths.CODE_DIR, BASE_FILES=paths.BASE_FILES, PROD_FILES=paths.PROD_FILES, TESTS_DIR=paths.TESTS_DIR, - PYTEST_SESSION=False + PYTEST_SESSION=False, ) # <---- Tests Runtime Variables -------------------------------------------------------------------------------------- diff --git a/tests/support/sminion.py b/tests/support/sminion.py index 86730f8a293..0df7a84a4e9 100644 --- a/tests/support/sminion.py +++ b/tests/support/sminion.py @@ -1,17 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" tests.support.sminion ~~~~~~~~~~~~~~~~~~~~~ SMinion's support functions -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys -import shutil + import hashlib import logging +import os +import shutil +import sys # Import salt libs import salt.minion @@ -24,15 +25,17 @@ from tests.support.runtests import RUNTIME_VARS log = logging.getLogger(__name__) -def build_minion_opts(minion_id=None, - root_dir=None, - initial_conf_file=None, - minion_opts_overrides=None, - skip_cached_opts=False, - cache_opts=True, - minion_role=None): +def build_minion_opts( + minion_id=None, + root_dir=None, + initial_conf_file=None, + minion_opts_overrides=None, + skip_cached_opts=False, + cache_opts=True, + minion_role=None, +): if minion_id is None: - minion_id = 'pytest-internal-sminion' + minion_id = "pytest-internal-sminion" if skip_cached_opts is False: try: opts_cache = build_minion_opts.__cached_opts__ @@ -42,77 +45,75 @@ def build_minion_opts(minion_id=None, if cached_opts: return cached_opts - log.info('Generating testing minion %r configuration...', minion_id) + log.info("Generating testing minion %r configuration...", minion_id) if root_dir is None: hashed_minion_id = hashlib.sha1() hashed_minion_id.update(salt.utils.stringutils.to_bytes(minion_id)) - root_dir = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, hashed_minion_id.hexdigest()[:6]) + root_dir = os.path.join( + RUNTIME_VARS.TMP_ROOT_DIR, hashed_minion_id.hexdigest()[:6] + ) if initial_conf_file is not None: - minion_opts = salt.config._read_conf_file(initial_conf_file) # pylint: disable=protected-access + minion_opts = salt.config._read_conf_file( + initial_conf_file + ) # pylint: disable=protected-access else: minion_opts = {} - conf_dir = os.path.join(root_dir, 'conf') - conf_file = os.path.join(conf_dir, 'minion') + conf_dir = os.path.join(root_dir, "conf") + conf_file = os.path.join(conf_dir, "minion") - minion_opts['id'] = minion_id - minion_opts['conf_file'] = conf_file - minion_opts['root_dir'] = root_dir - minion_opts['cachedir'] = 'cache' - minion_opts['user'] = RUNTIME_VARS.RUNNING_TESTS_USER - minion_opts['pki_dir'] = 'pki' - minion_opts['hosts.file'] = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'hosts') - minion_opts['aliases.file'] = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'aliases') - minion_opts['file_client'] = 'local' - minion_opts['server_id_use_crc'] = 'adler32' - minion_opts['pillar_roots'] = { - 'base': [ - RUNTIME_VARS.TMP_PILLAR_TREE, - ] - } - minion_opts['file_roots'] = { - 'base': [ + minion_opts["id"] = minion_id + minion_opts["conf_file"] = conf_file + minion_opts["root_dir"] = root_dir + minion_opts["cachedir"] = "cache" + minion_opts["user"] = RUNTIME_VARS.RUNNING_TESTS_USER + minion_opts["pki_dir"] = "pki" + minion_opts["hosts.file"] = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, "hosts") + minion_opts["aliases.file"] = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, "aliases") + minion_opts["file_client"] = "local" + minion_opts["server_id_use_crc"] = "adler32" + minion_opts["pillar_roots"] = {"base": [RUNTIME_VARS.TMP_PILLAR_TREE]} + minion_opts["file_roots"] = { + "base": [ # Let's support runtime created files that can be used like: # salt://my-temp-file.txt RUNTIME_VARS.TMP_STATE_TREE ], # Alternate root to test __env__ choices - 'prod': [ - os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), - RUNTIME_VARS.TMP_PRODENV_STATE_TREE - ] + "prod": [ + os.path.join(RUNTIME_VARS.FILES, "file", "prod"), + RUNTIME_VARS.TMP_PRODENV_STATE_TREE, + ], } if initial_conf_file and initial_conf_file.startswith(RUNTIME_VARS.FILES): # We assume we were passed a minion configuration file defined fo testing and, as such # we define the file and pillar roots to include the testing states/pillar trees - minion_opts['pillar_roots']['base'].append( - os.path.join(RUNTIME_VARS.FILES, 'pillar', 'base'), + minion_opts["pillar_roots"]["base"].append( + os.path.join(RUNTIME_VARS.FILES, "pillar", "base"), ) - minion_opts['file_roots']['base'].append( - os.path.join(RUNTIME_VARS.FILES, 'file', 'base'), + minion_opts["file_roots"]["base"].append( + os.path.join(RUNTIME_VARS.FILES, "file", "base"), ) - minion_opts['file_roots']['prod'].append( - os.path.join(RUNTIME_VARS.FILES, 'file', 'prod'), + minion_opts["file_roots"]["prod"].append( + os.path.join(RUNTIME_VARS.FILES, "file", "prod"), ) # We need to copy the extension modules into the new master root_dir or # it will be prefixed by it - extension_modules_path = os.path.join(root_dir, 'extension_modules') + extension_modules_path = os.path.join(root_dir, "extension_modules") if not os.path.exists(extension_modules_path): shutil.copytree( - os.path.join( - RUNTIME_VARS.FILES, 'extension_modules' - ), - extension_modules_path + os.path.join(RUNTIME_VARS.FILES, "extension_modules"), + extension_modules_path, ) - minion_opts['extension_modules'] = extension_modules_path + minion_opts["extension_modules"] = extension_modules_path # Custom grains - if 'grains' not in minion_opts: - minion_opts['grains'] = {} + if "grains" not in minion_opts: + minion_opts["grains"] = {} if minion_role is not None: - minion_opts['grains']['role'] = minion_role + minion_opts["grains"]["role"] = minion_role # Under windows we can't seem to properly create a virtualenv off of another # virtualenv, we can on linux but we will still point to the virtualenv binary @@ -121,21 +122,21 @@ def build_minion_opts(minion_id=None, real_prefix = sys.real_prefix # The above attribute exists, this is a virtualenv if salt.utils.platform.is_windows(): - virtualenv_binary = os.path.join(real_prefix, 'Scripts', 'virtualenv.exe') + virtualenv_binary = os.path.join(real_prefix, "Scripts", "virtualenv.exe") else: # We need to remove the virtualenv from PATH or we'll get the virtualenv binary # from within the virtualenv, we don't want that - path = os.environ.get('PATH') + path = os.environ.get("PATH") if path is not None: path_items = path.split(os.pathsep) for item in path_items[:]: if item.startswith(sys.base_prefix): path_items.remove(item) - os.environ['PATH'] = os.pathsep.join(path_items) - virtualenv_binary = salt.utils.path.which('virtualenv') + os.environ["PATH"] = os.pathsep.join(path_items) + virtualenv_binary = salt.utils.path.which("virtualenv") if path is not None: # Restore previous environ PATH - os.environ['PATH'] = path + os.environ["PATH"] = path if not virtualenv_binary.startswith(real_prefix): virtualenv_binary = None if virtualenv_binary and not os.path.exists(virtualenv_binary): @@ -145,7 +146,7 @@ def build_minion_opts(minion_id=None, # We're not running inside a virtualenv virtualenv_binary = None if virtualenv_binary: - minion_opts['venv_bin'] = virtualenv_binary + minion_opts["venv_bin"] = virtualenv_binary # Override minion_opts with minion_opts_overrides if minion_opts_overrides: @@ -154,27 +155,29 @@ def build_minion_opts(minion_id=None, if not os.path.exists(conf_dir): os.makedirs(conf_dir) - with salt.utils.files.fopen(conf_file, 'w') as fp_: + with salt.utils.files.fopen(conf_file, "w") as fp_: salt.utils.yaml.safe_dump(minion_opts, fp_, default_flow_style=False) - log.info('Generating testing minion %r configuration completed.', minion_id) - minion_opts = salt.config.minion_config(conf_file, minion_id=minion_id, cache_minion_id=True) + log.info("Generating testing minion %r configuration completed.", minion_id) + minion_opts = salt.config.minion_config( + conf_file, minion_id=minion_id, cache_minion_id=True + ) salt.utils.verify.verify_env( [ - os.path.join(minion_opts['pki_dir'], 'accepted'), - os.path.join(minion_opts['pki_dir'], 'rejected'), - os.path.join(minion_opts['pki_dir'], 'pending'), - os.path.dirname(minion_opts['log_file']), - minion_opts['extension_modules'], - minion_opts['cachedir'], - minion_opts['sock_dir'], + os.path.join(minion_opts["pki_dir"], "accepted"), + os.path.join(minion_opts["pki_dir"], "rejected"), + os.path.join(minion_opts["pki_dir"], "pending"), + os.path.dirname(minion_opts["log_file"]), + minion_opts["extension_modules"], + minion_opts["cachedir"], + minion_opts["sock_dir"], RUNTIME_VARS.TMP_STATE_TREE, RUNTIME_VARS.TMP_PILLAR_TREE, RUNTIME_VARS.TMP_PRODENV_STATE_TREE, RUNTIME_VARS.TMP, ], RUNTIME_VARS.RUNNING_TESTS_USER, - root_dir=root_dir + root_dir=root_dir, ) if cache_opts: try: @@ -185,15 +188,17 @@ def build_minion_opts(minion_id=None, return minion_opts -def create_sminion(minion_id=None, - root_dir=None, - initial_conf_file=None, - sminion_cls=salt.minion.SMinion, - minion_opts_overrides=None, - skip_cached_minion=False, - cache_sminion=True): +def create_sminion( + minion_id=None, + root_dir=None, + initial_conf_file=None, + sminion_cls=salt.minion.SMinion, + minion_opts_overrides=None, + skip_cached_minion=False, + cache_sminion=True, +): if minion_id is None: - minion_id = 'pytest-internal-sminion' + minion_id = "pytest-internal-sminion" if skip_cached_minion is False: try: minions_cache = create_sminion.__cached_minions__ @@ -202,13 +207,15 @@ def create_sminion(minion_id=None, cached_minion = create_sminion.__cached_minions__.get(minion_id) if cached_minion: return cached_minion - minion_opts = build_minion_opts(minion_id=minion_id, - root_dir=root_dir, - initial_conf_file=initial_conf_file, - minion_opts_overrides=minion_opts_overrides, - skip_cached_opts=skip_cached_minion, - cache_opts=cache_sminion) - log.info('Instantiating a testing %s(%s)', sminion_cls.__name__, minion_id) + minion_opts = build_minion_opts( + minion_id=minion_id, + root_dir=root_dir, + initial_conf_file=initial_conf_file, + minion_opts_overrides=minion_opts_overrides, + skip_cached_opts=skip_cached_minion, + cache_opts=cache_sminion, + ) + log.info("Instantiating a testing %s(%s)", sminion_cls.__name__, minion_id) sminion = sminion_cls(minion_opts) if cache_sminion: try: diff --git a/tests/support/unit.py b/tests/support/unit.py index 5ac6e8ec71e..927caf4a4f1 100644 --- a/tests/support/unit.py +++ b/tests/support/unit.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -18,29 +18,30 @@ compatibility layer. .. _`unittest2`: https://pypi.python.org/pypi/unittest2 -''' +""" # pylint: disable=unused-import,blacklisted-module,deprecated-method # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import sys -import logging -from unittest import ( - TestLoader as _TestLoader, - TextTestRunner as _TextTestRunner, - TestCase as _TestCase, - expectedFailure, - TestSuite as _TestSuite, - skip, - skipIf as _skipIf, - TestResult, - TextTestResult as _TextTestResult -) -from unittest.case import _id, SkipTest +from unittest import TestCase as _TestCase +from unittest import TestLoader as _TestLoader +from unittest import TestResult +from unittest import TestSuite as _TestSuite +from unittest import TextTestResult as _TextTestResult +from unittest import TextTestRunner as _TextTestRunner +from unittest import expectedFailure, skip +from unittest import skipIf as _skipIf +from unittest.case import SkipTest, _id + from salt.ext import six + try: import psutil + HAS_PSUTIL = True except ImportError: HAS_PSUTIL = False @@ -50,9 +51,9 @@ log = logging.getLogger(__name__) # Set SHOW_PROC to True to show # process details when running in verbose mode # i.e. [CPU:15.1%|MEM:48.3%|Z:0] -SHOW_PROC = 'NO_SHOW_PROC' not in os.environ +SHOW_PROC = "NO_SHOW_PROC" not in os.environ -LOREM_IPSUM = '''\ +LOREM_IPSUM = """\ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eget urna a arcu lacinia sagittis. Sed scelerisque, lacus eget malesuada vestibulum, justo diam facilisis tortor, in sodales dolor nibh eu urna. Aliquam iaculis massa risus, sed elementum risus accumsan id. Suspendisse mattis, @@ -61,15 +62,17 @@ Duis ac cursus leo, non varius metus. Sed laoreet felis magna, vel tempor diam m Quisque cursus odio tortor. In consequat augue nisl, eget lacinia odio vestibulum eget. Donec venenatis elementum arcu at rhoncus. Nunc pharetra erat in lacinia convallis. Ut condimentum eu mauris sit amet convallis. Morbi vulputate vel odio non laoreet. Nullam in suscipit tellus. -Sed quis posuere urna.''' +Sed quis posuere urna.""" class TestSuite(_TestSuite): - def _handleClassSetUp(self, test, result): - previousClass = getattr(result, '_previousTestClass', None) + previousClass = getattr(result, "_previousTestClass", None) currentClass = test.__class__ - if currentClass == previousClass or getattr(currentClass, 'setUpClass', None) is None: + if ( + currentClass == previousClass + or getattr(currentClass, "setUpClass", None) is None + ): return super(TestSuite, self)._handleClassSetUp(test, result) # Store a reference to all class attributes before running the setUpClass method @@ -77,18 +80,21 @@ class TestSuite(_TestSuite): super(TestSuite, self)._handleClassSetUp(test, result) # Store the difference in in a variable in order to check later if they were deleted test.__class__._prerun_class_attributes = [ - attr for attr in dir(test.__class__) if attr not in initial_class_attributes] + attr for attr in dir(test.__class__) if attr not in initial_class_attributes + ] def _tearDownPreviousClass(self, test, result): # Run any tearDownClass code defined super(TestSuite, self)._tearDownPreviousClass(test, result) - previousClass = getattr(result, '_previousTestClass', None) + previousClass = getattr(result, "_previousTestClass", None) currentClass = test.__class__ if currentClass == previousClass: return # See if the previous class attributes have been cleaned - if previousClass and getattr(previousClass, 'tearDownClass', None): - prerun_class_attributes = getattr(previousClass, '_prerun_class_attributes', None) + if previousClass and getattr(previousClass, "tearDownClass", None): + prerun_class_attributes = getattr( + previousClass, "_prerun_class_attributes", None + ) if prerun_class_attributes is not None: previousClass._prerun_class_attributes = None del previousClass._prerun_class_attributes @@ -97,13 +103,20 @@ class TestSuite(_TestSuite): attr_value = getattr(previousClass, attr, None) if attr_value is None: continue - if isinstance(attr_value, (bool,) + six.string_types + six.integer_types): + if isinstance( + attr_value, (bool,) + six.string_types + six.integer_types + ): setattr(previousClass, attr, None) continue - log.warning('Deleting extra class attribute after test run: %s.%s(%s). ' - 'Please consider using \'del self.%s\' on the test class ' - '\'tearDownClass()\' method', previousClass.__name__, attr, - str(getattr(previousClass, attr))[:100], attr) + log.warning( + "Deleting extra class attribute after test run: %s.%s(%s). " + "Please consider using 'del self.%s' on the test class " + "'tearDownClass()' method", + previousClass.__name__, + attr, + str(getattr(previousClass, attr))[:100], + attr, + ) delattr(previousClass, attr) @@ -115,25 +128,25 @@ class TestLoader(_TestLoader): class TestCase(_TestCase): # pylint: disable=expected-an-indented-block-comment,too-many-leading-hastag-for-block-comment -## Commented out because it may be causing tests to hang -## at the end of the run -# -# _cwd = os.getcwd() -# _chdir_counter = 0 + ## Commented out because it may be causing tests to hang + ## at the end of the run + # + # _cwd = os.getcwd() + # _chdir_counter = 0 -# @classmethod -# def tearDownClass(cls): -# ''' -# Overriden method for tearing down all classes in salttesting -# -# This hard-resets the environment between test classes -# ''' -# # Compare where we are now compared to where we were when we began this family of tests -# if not cls._cwd == os.getcwd() and cls._chdir_counter > 0: -# os.chdir(cls._cwd) -# print('\nWARNING: A misbehaving test has modified the working directory!\nThe test suite has reset the working directory ' -# 'on tearDown() to {0}\n'.format(cls._cwd)) -# cls._chdir_counter += 1 + # @classmethod + # def tearDownClass(cls): + # ''' + # Overriden method for tearing down all classes in salttesting + # + # This hard-resets the environment between test classes + # ''' + # # Compare where we are now compared to where we were when we began this family of tests + # if not cls._cwd == os.getcwd() and cls._chdir_counter > 0: + # os.chdir(cls._cwd) + # print('\nWARNING: A misbehaving test has modified the working directory!\nThe test suite has reset the working directory ' + # 'on tearDown() to {0}\n'.format(cls._cwd)) + # cls._chdir_counter += 1 # pylint: enable=expected-an-indented-block-comment,too-many-leading-hastag-for-block-comment def run(self, result=None): @@ -141,21 +154,28 @@ class TestCase(_TestCase): self.maxDiff = None outcome = super(TestCase, self).run(result=result) for attr in dir(self): - if attr == '_prerun_instance_attributes': + if attr == "_prerun_instance_attributes": continue - if attr in getattr(self.__class__, '_prerun_class_attributes', ()): + if attr in getattr(self.__class__, "_prerun_class_attributes", ()): continue if attr not in self._prerun_instance_attributes: attr_value = getattr(self, attr, None) if attr_value is None: continue - if isinstance(attr_value, (bool,) + six.string_types + six.integer_types): + if isinstance( + attr_value, (bool,) + six.string_types + six.integer_types + ): setattr(self, attr, None) continue - log.warning('Deleting extra class attribute after test run: %s.%s(%s). ' - 'Please consider using \'del self.%s\' on the test case ' - '\'tearDown()\' method', self.__class__.__name__, attr, - getattr(self, attr), attr) + log.warning( + "Deleting extra class attribute after test run: %s.%s(%s). " + "Please consider using 'del self.%s' on the test case " + "'tearDown()' method", + self.__class__.__name__, + attr, + getattr(self, attr), + attr, + ) delattr(self, attr) self._prerun_instance_attributes = None del self._prerun_instance_attributes @@ -164,9 +184,10 @@ class TestCase(_TestCase): def shortDescription(self): desc = _TestCase.shortDescription(self) if HAS_PSUTIL and SHOW_PROC: - show_zombie_processes = 'SHOW_PROC_ZOMBIES' in os.environ - proc_info = '[CPU:{0}%|MEM:{1}%'.format(psutil.cpu_percent(), - psutil.virtual_memory().percent) + show_zombie_processes = "SHOW_PROC_ZOMBIES" in os.environ + proc_info = "[CPU:{0}%|MEM:{1}%".format( + psutil.cpu_percent(), psutil.virtual_memory().percent + ) if show_zombie_processes: found_zombies = 0 try: @@ -175,23 +196,23 @@ class TestCase(_TestCase): found_zombies += 1 except Exception: # pylint: disable=broad-except pass - proc_info += '|Z:{0}'.format(found_zombies) - proc_info += '] {short_desc}'.format(short_desc=desc if desc else '') + proc_info += "|Z:{0}".format(found_zombies) + proc_info += "] {short_desc}".format(short_desc=desc if desc else "") return proc_info else: return _TestCase.shortDescription(self) def assertEquals(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('assertEquals', 'assertEqual') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("assertEquals", "assertEqual") ) # return _TestCase.assertEquals(self, *args, **kwargs) def assertNotEquals(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('assertNotEquals', 'assertNotEqual') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("assertNotEquals", "assertNotEqual") ) # return _TestCase.assertNotEquals(self, *args, **kwargs) @@ -199,27 +220,27 @@ class TestCase(_TestCase): # The unittest2 library uses this deprecated method, we can't raise # the exception. raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('assert_', 'assertTrue') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("assert_", "assertTrue") ) # return _TestCase.assert_(self, *args, **kwargs) def assertAlmostEquals(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('assertAlmostEquals', 'assertAlmostEqual') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("assertAlmostEquals", "assertAlmostEqual") ) # return _TestCase.assertAlmostEquals(self, *args, **kwargs) def assertNotAlmostEquals(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('assertNotAlmostEquals', 'assertNotAlmostEqual') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("assertNotAlmostEquals", "assertNotAlmostEqual") ) # return _TestCase.assertNotAlmostEquals(self, *args, **kwargs) def repack_state_returns(self, state_ret): - ''' + """ Accepts a state return dict and returns it back with the top level key names rewritten such that the ID declaration is the key instead of the State's unique tag. For example: 'foo' instead of @@ -227,172 +248,168 @@ class TestCase(_TestCase): This makes it easier to work with state returns when crafting asserts after running states. - ''' + """ assert isinstance(state_ret, dict), state_ret - return {x.split('_|-')[1]: y for x, y in six.iteritems(state_ret)} + return {x.split("_|-")[1]: y for x, y in six.iteritems(state_ret)} def failUnlessEqual(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('failUnlessEqual', 'assertEqual') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("failUnlessEqual", "assertEqual") ) # return _TestCase.failUnlessEqual(self, *args, **kwargs) def failIfEqual(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('failIfEqual', 'assertNotEqual') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("failIfEqual", "assertNotEqual") ) # return _TestCase.failIfEqual(self, *args, **kwargs) def failUnless(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('failUnless', 'assertTrue') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("failUnless", "assertTrue") ) # return _TestCase.failUnless(self, *args, **kwargs) def failIf(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('failIf', 'assertFalse') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("failIf", "assertFalse") ) # return _TestCase.failIf(self, *args, **kwargs) def failUnlessRaises(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('failUnlessRaises', 'assertRaises') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("failUnlessRaises", "assertRaises") ) # return _TestCase.failUnlessRaises(self, *args, **kwargs) def failUnlessAlmostEqual(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('failUnlessAlmostEqual', 'assertAlmostEqual') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("failUnlessAlmostEqual", "assertAlmostEqual") ) # return _TestCase.failUnlessAlmostEqual(self, *args, **kwargs) def failIfAlmostEqual(self, *args, **kwargs): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format('failIfAlmostEqual', 'assertNotAlmostEqual') + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("failIfAlmostEqual", "assertNotAlmostEqual") ) # return _TestCase.failIfAlmostEqual(self, *args, **kwargs) @staticmethod def assert_called_once(mock): - ''' + """ mock.assert_called_once only exists in PY3 in 3.6 and newer - ''' + """ try: mock.assert_called_once() except AttributeError: - log.warning('assert_called_once invoked, but not available') + log.warning("assert_called_once invoked, but not available") if six.PY2: + def assertRegexpMatches(self, *args, **kwds): raise DeprecationWarning( - 'The {0}() function will be deprecated in python 3. Please start ' - 'using {1}() instead.'.format( - 'assertRegexpMatches', - 'assertRegex' - ) + "The {0}() function will be deprecated in python 3. Please start " + "using {1}() instead.".format("assertRegexpMatches", "assertRegex") ) - def assertRegex(self, text, regex, msg=None): # pylint: disable=arguments-differ + def assertRegex( + self, text, regex, msg=None + ): # pylint: disable=arguments-differ # In python 2, alias to the future python 3 function return _TestCase.assertRegexpMatches(self, text, regex, msg=msg) def assertNotRegexpMatches(self, *args, **kwds): raise DeprecationWarning( - 'The {0}() function will be deprecated in python 3. Please start ' - 'using {1}() instead.'.format( - 'assertNotRegexpMatches', - 'assertNotRegex' + "The {0}() function will be deprecated in python 3. Please start " + "using {1}() instead.".format( + "assertNotRegexpMatches", "assertNotRegex" ) ) - def assertNotRegex(self, text, regex, msg=None): # pylint: disable=arguments-differ + def assertNotRegex( + self, text, regex, msg=None + ): # pylint: disable=arguments-differ # In python 2, alias to the future python 3 function return _TestCase.assertNotRegexpMatches(self, text, regex, msg=msg) def assertRaisesRegexp(self, *args, **kwds): raise DeprecationWarning( - 'The {0}() function will be deprecated in python 3. Please start ' - 'using {1}() instead.'.format( - 'assertRaisesRegexp', - 'assertRaisesRegex' - ) + "The {0}() function will be deprecated in python 3. Please start " + "using {1}() instead.".format("assertRaisesRegexp", "assertRaisesRegex") ) - def assertRaisesRegex(self, exception, regexp, *args, **kwds): # pylint: disable=arguments-differ + def assertRaisesRegex( + self, exception, regexp, *args, **kwds + ): # pylint: disable=arguments-differ # In python 2, alias to the future python 3 function return _TestCase.assertRaisesRegexp(self, exception, regexp, *args, **kwds) + else: + def assertRegexpMatches(self, *args, **kwds): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format( - 'assertRegexpMatches', - 'assertRegex' - ) + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("assertRegexpMatches", "assertRegex") ) def assertNotRegexpMatches(self, *args, **kwds): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format( - 'assertNotRegexpMatches', - 'assertNotRegex' - ) + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("assertNotRegexpMatches", "assertNotRegex") ) def assertRaisesRegexp(self, *args, **kwds): raise DeprecationWarning( - 'The {0}() function is deprecated. Please start using {1}() ' - 'instead.'.format( - 'assertRaisesRegexp', - 'assertRaisesRegex' - ) + "The {0}() function is deprecated. Please start using {1}() " + "instead.".format("assertRaisesRegexp", "assertRaisesRegex") ) class TextTestResult(_TextTestResult): - ''' + """ Custom TestResult class whith logs the start and the end of a test - ''' + """ def startTest(self, test): - log.debug('>>>>> START >>>>> {0}'.format(test.id())) + log.debug(">>>>> START >>>>> {0}".format(test.id())) return super(TextTestResult, self).startTest(test) def stopTest(self, test): - log.debug('<<<<< END <<<<<<< {0}'.format(test.id())) + log.debug("<<<<< END <<<<<<< {0}".format(test.id())) return super(TextTestResult, self).stopTest(test) class TextTestRunner(_TextTestRunner): - ''' + """ Custom Text tests runner to log the start and the end of a test case - ''' + """ + resultclass = TextTestResult def skipIf(skip, reason): from tests.support.runtests import RUNTIME_VARS + if RUNTIME_VARS.PYTEST_SESSION: import pytest + return pytest.mark.skipif(skip, reason=reason) return _skipIf(skip, reason) __all__ = [ - 'TestLoader', - 'TextTestRunner', - 'TestCase', - 'expectedFailure', - 'TestSuite', - 'skipIf', - 'TestResult' + "TestLoader", + "TextTestRunner", + "TestCase", + "expectedFailure", + "TestSuite", + "skipIf", + "TestResult", ] diff --git a/tests/support/win_installer.py b/tests/support/win_installer.py index 6656cdd4b6e..a4bb8dca665 100644 --- a/tests/support/win_installer.py +++ b/tests/support/win_installer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :copyright: Copyright 2013-2017 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. @@ -8,31 +8,33 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fetches the binary Windows installer -''' +""" from __future__ import absolute_import + import hashlib -import requests import re -PREFIX = 'Salt-Minion-' +import requests + +PREFIX = "Salt-Minion-" REPO = "https://repo.saltstack.com/windows" def iter_installers(content): - ''' + """ Parse a list of windows installer links and their corresponding md5 checksum links. - ''' - HREF_RE = "<a href=\"(.*?)\">" + """ + HREF_RE = '<a href="(.*?)">' installer, md5 = None, None for m in re.finditer(HREF_RE, content): x = m.groups()[0] if not x.startswith(PREFIX): continue - if x.endswith(('zip', 'sha256')): + if x.endswith(("zip", "sha256")): continue if installer: - if x != installer + '.md5': + if x != installer + ".md5": raise Exception("Unable to parse response") md5 = x yield installer, md5 @@ -42,42 +44,42 @@ def iter_installers(content): def split_installer(name): - ''' + """ Return a tuple of the salt version, python verison and architecture from an installer name. - ''' - x = name[len(PREFIX):] - return x.split('-')[:3] + """ + x = name[len(PREFIX) :] + return x.split("-")[:3] def latest_version(repo=REPO): - ''' + """ Return the latest version found on the salt repository webpage. - ''' - content = requests.get(repo).content.decode('utf-8') + """ + content = requests.get(repo).content.decode("utf-8") for name, md5 in iter_installers(content): pass return split_installer(name)[0] -def installer_name(salt_ver, py_ver='Py2', arch='AMD64'): - ''' +def installer_name(salt_ver, py_ver="Py2", arch="AMD64"): + """ Create an installer file name - ''' + """ return "Salt-Minion-{}-{}-{}-Setup.exe".format(salt_ver, py_ver, arch) def latest_installer_name(repo=REPO, **kwargs): - ''' + """ Fetch the latest installer name - ''' + """ return installer_name(latest_version(repo), **kwargs) def download_and_verify(fp, name, repo=REPO): - ''' + """ Download an installer and verify its contents. - ''' + """ md5 = "{}.md5".format(name) url = lambda x: "{}/{}".format(repo, x) resp = requests.get(url(md5)) @@ -92,6 +94,8 @@ def download_and_verify(fp, name, repo=REPO): md5hsh.update(chunk) fp.write(chunk) if md5hsh.hexdigest() != installer_md5: - raise Exception("Installer's hash does not match {} != {}".format( - md5hsh.hexdigest(), installer_md5 - )) + raise Exception( + "Installer's hash does not match {} != {}".format( + md5hsh.hexdigest(), installer_md5 + ) + ) diff --git a/tests/support/xmlunit.py b/tests/support/xmlunit.py index 5ddfd5cd6b3..ffc08bf4e50 100644 --- a/tests/support/xmlunit.py +++ b/tests/support/xmlunit.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) :copyright: Copyright 2014 by the SaltStack Team, see AUTHORS for more details. :license: Apache 2.0, see LICENSE for more details. @@ -9,11 +9,12 @@ ~~~~~~~~~~~~~~~~~~~ XML Unit Tests -''' +""" # pylint: disable=wrong-import-order,wrong-import-position # Import python libs from __future__ import absolute_import + import io import logging @@ -23,6 +24,7 @@ log = logging.getLogger(__name__) try: import xmlrunner.runner import xmlrunner.result + HAS_XMLRUNNER = True class _DuplicateWriter(io.TextIOBase): @@ -70,33 +72,31 @@ try: class _XMLTestResult(xmlrunner.result._XMLTestResult): def startTest(self, test): - log.debug('>>>>> START >>>>> %s', test.id()) + log.debug(">>>>> START >>>>> %s", test.id()) # xmlrunner classes are NOT new-style classes xmlrunner.result._XMLTestResult.startTest(self, test) def stopTest(self, test): - log.debug('<<<<< END <<<<<<< %s', test.id()) + log.debug("<<<<< END <<<<<<< %s", test.id()) # xmlrunner classes are NOT new-style classes return xmlrunner.result._XMLTestResult.stopTest(self, test) class XMLTestRunner(xmlrunner.runner.XMLTestRunner): def _make_result(self): return _XMLTestResult( - self.stream, - self.descriptions, - self.verbosity, - self.elapsed_times + self.stream, self.descriptions, self.verbosity, self.elapsed_times ) def run(self, test): result = xmlrunner.runner.XMLTestRunner.run(self, test) - self.stream.writeln('Finished generating XML reports') + self.stream.writeln("Finished generating XML reports") return result + except ImportError: HAS_XMLRUNNER = False class XMLTestRunner(object): - ''' + """ This is a dumb class just so we don't break projects at import time - ''' + """ diff --git a/tests/support/zfs.py b/tests/support/zfs.py index 5b260ff82fa..e2dd6786419 100644 --- a/tests/support/zfs.py +++ b/tests/support/zfs.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" tests.support.zfs ~~~~~~~~~~~~~~~~~ ZFS related unit test data structures -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.utils.zfs @@ -17,854 +17,733 @@ from tests.support.mock import MagicMock, patch class ZFSMockData(object): - def __init__(self): # property_map mocks self.pmap_exec_zpool = { - 'retcode': 2, - 'stdout': '', - 'stderr': "\n".join([ - 'missing property argument', - 'usage:', - ' get [-Hp] [-o "all" | field[,...]] <"all" | property[,...]> <pool> ...', - '', - 'the following properties are supported:', - '', - ' PROPERTY EDIT VALUES', - '', - ' allocated NO <size>', - ' capacity NO <size>', - ' dedupratio NO <1.00x or higher if deduped>', - ' expandsize NO <size>', - ' fragmentation NO <percent>', - ' free NO <size>', - ' freeing NO <size>', - ' guid NO <guid>', - ' health NO <state>', - ' leaked NO <size>', - ' size NO <size>', - ' altroot YES <path>', - ' autoexpand YES on | off', - ' autoreplace YES on | off', - ' bootfs YES <filesystem>', - ' bootsize YES <size>', - ' cachefile YES <file> | none', - ' comment YES <comment-string>', - ' dedupditto YES <threshold (min 100)>', - ' delegation YES on | off', - ' failmode YES wait | continue | panic', - ' listsnapshots YES on | off', - ' readonly YES on | off', - ' version YES <version>', - ' feature@... YES disabled | enabled | active', - '', - 'The feature@ properties must be appended with a feature name.', - 'See zpool-features(5). ', - ]), + "retcode": 2, + "stdout": "", + "stderr": "\n".join( + [ + "missing property argument", + "usage:", + ' get [-Hp] [-o "all" | field[,...]] <"all" | property[,...]> <pool> ...', + "", + "the following properties are supported:", + "", + " PROPERTY EDIT VALUES", + "", + " allocated NO <size>", + " capacity NO <size>", + " dedupratio NO <1.00x or higher if deduped>", + " expandsize NO <size>", + " fragmentation NO <percent>", + " free NO <size>", + " freeing NO <size>", + " guid NO <guid>", + " health NO <state>", + " leaked NO <size>", + " size NO <size>", + " altroot YES <path>", + " autoexpand YES on | off", + " autoreplace YES on | off", + " bootfs YES <filesystem>", + " bootsize YES <size>", + " cachefile YES <file> | none", + " comment YES <comment-string>", + " dedupditto YES <threshold (min 100)>", + " delegation YES on | off", + " failmode YES wait | continue | panic", + " listsnapshots YES on | off", + " readonly YES on | off", + " version YES <version>", + " feature@... YES disabled | enabled | active", + "", + "The feature@ properties must be appended with a feature name.", + "See zpool-features(5). ", + ] + ), } self.pmap_zpool = { - 'comment': { - 'edit': True, - 'type': 'str', - 'values': '<comment-string>' - }, - 'freeing': { - 'edit': False, - 'type': 'size', - 'values': '<size>' - }, - 'listsnapshots': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'leaked': { - 'edit': False, - 'type': 'size', - 'values': '<size>' - }, - 'version': { - 'edit': True, - 'type': 'numeric', - 'values': '<version>' - }, - 'write': { - 'edit': False, - 'type': 'size', - 'values': '<size>' - }, - 'replace': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'delegation': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'dedupditto': { - 'edit': True, - 'type': 'str', - 'values': '<threshold (min 100)>' - }, - 'autoexpand': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'alloc': { - 'edit': False, - 'type': 'size', - 'values': '<size>' - }, - 'allocated': { - 'edit': False, - 'type': 'size', - 'values': '<size>' - }, - 'guid': { - 'edit': False, - 'type': 'numeric', - 'values': '<guid>' - }, - 'size': { - 'edit': False, - 'type': 'size', - 'values': '<size>' - }, - 'cap': { - 'edit': False, - 'type': 'numeric', - 'values': '<count>' - }, - 'capacity': { - 'edit': False, - 'type': 'size', - 'values': '<size>' - }, - "capacity-alloc": { - "edit": False, - "type": "size", - "values": "<size>" - }, - "capacity-free": { - "edit": False, - "type": "size", - "values": "<size>" - }, - 'cachefile': { - 'edit': True, - 'type': 'str', - 'values': '<file> | none' - }, - "cksum": { - "edit": False, - "type": "numeric", - "values": "<count>" - }, - 'bootfs': { - 'edit': True, - 'type': 'str', - 'values': '<filesystem>' - }, - 'autoreplace': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - "bandwith-read": { - "edit": False, - "type": "size", - "values": "<size>" - }, - "bandwith-write": { - "edit": False, - "type": "size", - "values": "<size>" - }, - "operations-read": { - "edit": False, - "type": "size", - "values": "<size>" - }, - "operations-write": { - "edit": False, - "type": "size", - "values": "<size>" - }, - "read": { - "edit": False, - "type": "size", - "values": "<size>" - }, - 'readonly': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'dedupratio': { - 'edit': False, - 'type': 'str', - 'values': '<1.00x or higher if deduped>' - }, - 'health': { - 'edit': False, - 'type': 'str', - 'values': '<state>' - }, - 'feature@': { - 'edit': True, - 'type': 'str', - 'values': 'disabled | enabled | active' - }, - 'expandsize': { - 'edit': False, - 'type': 'size', - 'values': '<size>' - }, - 'listsnaps': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'bootsize': { - 'edit': True, - 'type': 'size', - 'values': '<size>' - }, - 'free': { - 'edit': False, - 'type': 'size', - 'values': '<size>' - }, - 'failmode': { - 'edit': True, - 'type': 'str', - 'values': 'wait | continue | panic' - }, - 'altroot': { - 'edit': True, - 'type': 'str', - 'values': '<path>' - }, - 'expand': { - 'edit': True, - 'type': 'bool', - 'values': 'on | off' - }, - 'frag': { - 'edit': False, - 'type': 'str', - 'values': '<percent>' - }, - 'fragmentation': { - 'edit': False, - 'type': 'str', - 'values': '<percent>' - } + "comment": {"edit": True, "type": "str", "values": "<comment-string>"}, + "freeing": {"edit": False, "type": "size", "values": "<size>"}, + "listsnapshots": {"edit": True, "type": "bool", "values": "on | off"}, + "leaked": {"edit": False, "type": "size", "values": "<size>"}, + "version": {"edit": True, "type": "numeric", "values": "<version>"}, + "write": {"edit": False, "type": "size", "values": "<size>"}, + "replace": {"edit": True, "type": "bool", "values": "on | off"}, + "delegation": {"edit": True, "type": "bool", "values": "on | off"}, + "dedupditto": { + "edit": True, + "type": "str", + "values": "<threshold (min 100)>", + }, + "autoexpand": {"edit": True, "type": "bool", "values": "on | off"}, + "alloc": {"edit": False, "type": "size", "values": "<size>"}, + "allocated": {"edit": False, "type": "size", "values": "<size>"}, + "guid": {"edit": False, "type": "numeric", "values": "<guid>"}, + "size": {"edit": False, "type": "size", "values": "<size>"}, + "cap": {"edit": False, "type": "numeric", "values": "<count>"}, + "capacity": {"edit": False, "type": "size", "values": "<size>"}, + "capacity-alloc": {"edit": False, "type": "size", "values": "<size>"}, + "capacity-free": {"edit": False, "type": "size", "values": "<size>"}, + "cachefile": {"edit": True, "type": "str", "values": "<file> | none"}, + "cksum": {"edit": False, "type": "numeric", "values": "<count>"}, + "bootfs": {"edit": True, "type": "str", "values": "<filesystem>"}, + "autoreplace": {"edit": True, "type": "bool", "values": "on | off"}, + "bandwith-read": {"edit": False, "type": "size", "values": "<size>"}, + "bandwith-write": {"edit": False, "type": "size", "values": "<size>"}, + "operations-read": {"edit": False, "type": "size", "values": "<size>"}, + "operations-write": {"edit": False, "type": "size", "values": "<size>"}, + "read": {"edit": False, "type": "size", "values": "<size>"}, + "readonly": {"edit": True, "type": "bool", "values": "on | off"}, + "dedupratio": { + "edit": False, + "type": "str", + "values": "<1.00x or higher if deduped>", + }, + "health": {"edit": False, "type": "str", "values": "<state>"}, + "feature@": { + "edit": True, + "type": "str", + "values": "disabled | enabled | active", + }, + "expandsize": {"edit": False, "type": "size", "values": "<size>"}, + "listsnaps": {"edit": True, "type": "bool", "values": "on | off"}, + "bootsize": {"edit": True, "type": "size", "values": "<size>"}, + "free": {"edit": False, "type": "size", "values": "<size>"}, + "failmode": { + "edit": True, + "type": "str", + "values": "wait | continue | panic", + }, + "altroot": {"edit": True, "type": "str", "values": "<path>"}, + "expand": {"edit": True, "type": "bool", "values": "on | off"}, + "frag": {"edit": False, "type": "str", "values": "<percent>"}, + "fragmentation": {"edit": False, "type": "str", "values": "<percent>"}, } self.pmap_exec_zfs = { - 'retcode': 2, - 'stdout': '', - 'stderr': "\n".join([ - 'missing property argument', - 'usage:', - ' get [-crHp] [-d max] [-o "all" | field[,...]]', - ' [-t type[,...]] [-s source[,...]]', - ' <"all" | property[,...]> [filesystem|volume|snapshot|bookmark] ...', - '', - 'The following properties are supported:', - '', - ' PROPERTY EDIT INHERIT VALUES', - '', - ' available NO NO <size>', - ' clones NO NO <dataset>[,...]', - ' compressratio NO NO <1.00x or higher if compressed>', - ' creation NO NO <date>', - ' defer_destroy NO NO yes | no', - ' filesystem_count NO NO <count>', - ' logicalreferenced NO NO <size>', - ' logicalused NO NO <size>', - ' mounted NO NO yes | no', - ' origin NO NO <snapshot>', - ' receive_resume_token NO NO <string token>', - ' refcompressratio NO NO <1.00x or higher if compressed>', - ' referenced NO NO <size>', - ' snapshot_count NO NO <count>', - ' type NO NO filesystem | volume | snapshot | bookmark', - ' used NO NO <size>', - ' usedbychildren NO NO <size>', - ' usedbydataset NO NO <size>', - ' usedbyrefreservation NO NO <size>', - ' usedbysnapshots NO NO <size>', - ' userrefs NO NO <count>', - ' written NO NO <size>', - ' aclinherit YES YES discard | noallow | restricted | passthrough | passthrough-x', - ' aclmode YES YES discard | groupmask | passthrough | restricted', - ' atime YES YES on | off', - ' canmount YES NO on | off | noauto', - ' casesensitivity NO YES sensitive | insensitive | mixed', - ' checksum YES YES on | off | fletcher2 | fletcher4 | sha256 | sha512 | skein | edonr', - ' compression YES YES on | off | lzjb | gzip | gzip-[1-9] | zle | lz4', - ' copies YES YES 1 | 2 | 3', - ' dedup YES YES on | off | verify | sha256[,verify], sha512[,verify], skein[,verify], edonr,verify', - ' devices YES YES on | off', - ' exec YES YES on | off', - ' filesystem_limit YES NO <count> | none', - ' logbias YES YES latency | throughput', - ' mlslabel YES YES <sensitivity label>', - ' mountpoint YES YES <path> | legacy | none', - ' nbmand YES YES on | off', - ' normalization NO YES none | formC | formD | formKC | formKD', - ' primarycache YES YES all | none | metadata', - ' quota YES NO <size> | none', - ' readonly YES YES on | off', - ' recordsize YES YES 512 to 1M, power of 2', - ' redundant_metadata YES YES all | most', - ' refquota YES NO <size> | none', - ' refreservation YES NO <size> | none', - ' reservation YES NO <size> | none', - ' secondarycache YES YES all | none | metadata', - ' setuid YES YES on | off', - ' sharenfs YES YES on | off | share(1M) options', - ' sharesmb YES YES on | off | sharemgr(1M) options', - ' snapdir YES YES hidden | visible', - ' snapshot_limit YES NO <count> | none', - ' sync YES YES standard | always | disabled', - ' utf8only NO YES on | off', - ' version YES NO 1 | 2 | 3 | 4 | 5 | current', - ' volblocksize NO YES 512 to 128k, power of 2', - ' volsize YES NO <size>', - ' vscan YES YES on | off', - ' xattr YES YES on | off', - ' zoned YES YES on | off', - ' userused@... NO NO <size>', - ' groupused@... NO NO <size>', - ' userquota@... YES NO <size> | none', - ' groupquota@... YES NO <size> | none', - ' written@<snap> NO NO <size>', - '', - 'Sizes are specified in bytes with standard units such as K, M, G, etc.', - '', - 'User-defined properties can be specified by using a name containing a colon (:).', - '', - 'The {user|group}{used|quota}@ properties must be appended with', - 'a user or group specifier of one of these forms:', - ' POSIX name (eg: "matt")', - ' POSIX id (eg: "126829")', - ' SMB name@domain (eg: "matt@sun")', - ' SMB SID (eg: "S-1-234-567-89")', - ]), + "retcode": 2, + "stdout": "", + "stderr": "\n".join( + [ + "missing property argument", + "usage:", + ' get [-crHp] [-d max] [-o "all" | field[,...]]', + " [-t type[,...]] [-s source[,...]]", + ' <"all" | property[,...]> [filesystem|volume|snapshot|bookmark] ...', + "", + "The following properties are supported:", + "", + " PROPERTY EDIT INHERIT VALUES", + "", + " available NO NO <size>", + " clones NO NO <dataset>[,...]", + " compressratio NO NO <1.00x or higher if compressed>", + " creation NO NO <date>", + " defer_destroy NO NO yes | no", + " filesystem_count NO NO <count>", + " logicalreferenced NO NO <size>", + " logicalused NO NO <size>", + " mounted NO NO yes | no", + " origin NO NO <snapshot>", + " receive_resume_token NO NO <string token>", + " refcompressratio NO NO <1.00x or higher if compressed>", + " referenced NO NO <size>", + " snapshot_count NO NO <count>", + " type NO NO filesystem | volume | snapshot | bookmark", + " used NO NO <size>", + " usedbychildren NO NO <size>", + " usedbydataset NO NO <size>", + " usedbyrefreservation NO NO <size>", + " usedbysnapshots NO NO <size>", + " userrefs NO NO <count>", + " written NO NO <size>", + " aclinherit YES YES discard | noallow | restricted | passthrough | passthrough-x", + " aclmode YES YES discard | groupmask | passthrough | restricted", + " atime YES YES on | off", + " canmount YES NO on | off | noauto", + " casesensitivity NO YES sensitive | insensitive | mixed", + " checksum YES YES on | off | fletcher2 | fletcher4 | sha256 | sha512 | skein | edonr", + " compression YES YES on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", + " copies YES YES 1 | 2 | 3", + " dedup YES YES on | off | verify | sha256[,verify], sha512[,verify], skein[,verify], edonr,verify", + " devices YES YES on | off", + " exec YES YES on | off", + " filesystem_limit YES NO <count> | none", + " logbias YES YES latency | throughput", + " mlslabel YES YES <sensitivity label>", + " mountpoint YES YES <path> | legacy | none", + " nbmand YES YES on | off", + " normalization NO YES none | formC | formD | formKC | formKD", + " primarycache YES YES all | none | metadata", + " quota YES NO <size> | none", + " readonly YES YES on | off", + " recordsize YES YES 512 to 1M, power of 2", + " redundant_metadata YES YES all | most", + " refquota YES NO <size> | none", + " refreservation YES NO <size> | none", + " reservation YES NO <size> | none", + " secondarycache YES YES all | none | metadata", + " setuid YES YES on | off", + " sharenfs YES YES on | off | share(1M) options", + " sharesmb YES YES on | off | sharemgr(1M) options", + " snapdir YES YES hidden | visible", + " snapshot_limit YES NO <count> | none", + " sync YES YES standard | always | disabled", + " utf8only NO YES on | off", + " version YES NO 1 | 2 | 3 | 4 | 5 | current", + " volblocksize NO YES 512 to 128k, power of 2", + " volsize YES NO <size>", + " vscan YES YES on | off", + " xattr YES YES on | off", + " zoned YES YES on | off", + " userused@... NO NO <size>", + " groupused@... NO NO <size>", + " userquota@... YES NO <size> | none", + " groupquota@... YES NO <size> | none", + " written@<snap> NO NO <size>", + "", + "Sizes are specified in bytes with standard units such as K, M, G, etc.", + "", + "User-defined properties can be specified by using a name containing a colon (:).", + "", + "The {user|group}{used|quota}@ properties must be appended with", + "a user or group specifier of one of these forms:", + ' POSIX name (eg: "matt")', + ' POSIX id (eg: "126829")', + ' SMB name@domain (eg: "matt@sun")', + ' SMB SID (eg: "S-1-234-567-89")', + ] + ), } self.pmap_zfs = { - "origin": { - "edit": False, - "inherit": False, - "values": "<snapshot>", - "type": "str" - }, - "setuid": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "referenced": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "vscan": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "logicalused": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "userrefs": { - "edit": False, - "inherit": False, - "values": "<count>", - "type": "numeric" - }, - "primarycache": { - "edit": True, - "inherit": True, - "values": "all | none | metadata", - "type": "str" - }, - "logbias": { - "edit": True, - "inherit": True, - "values": "latency | throughput", - "type": "str" - }, - "creation": { - "edit": False, - "inherit": False, - "values": "<date>", - "type": "str" - }, - "sync": { - "edit": True, - "inherit": True, - "values": "standard | always | disabled", - "type": "str" - }, - "dedup": { - "edit": True, - "inherit": True, - "values": "on | off | verify | sha256[,verify], sha512[,verify], skein[,verify], edonr,verify", - "type": "bool" - }, - "sharenfs": { - "edit": True, - "inherit": True, - "values": "on | off | share(1m) options", - "type": "bool" - }, - "receive_resume_token": { - "edit": False, - "inherit": False, - "values": "<string token>", - "type": "str" - }, - "usedbyrefreservation": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "sharesmb": { - "edit": True, - "inherit": True, - "values": "on | off | sharemgr(1m) options", - "type": "bool" - }, - "rdonly": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "reservation": { - "edit": True, - "inherit": False, - "values": "<size> | none", - "type": "size" - }, - "reserv": { - "edit": True, - "inherit": False, - "values": "<size> | none", - "type": "size" - }, - "mountpoint": { - "edit": True, - "inherit": True, - "values": "<path> | legacy | none", - "type": "str" - }, - "casesensitivity": { - "edit": False, - "inherit": True, - "values": "sensitive | insensitive | mixed", - "type": "str" - }, - "utf8only": { - "edit": False, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "usedbysnapshots": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "readonly": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "written@": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "avail": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "recsize": { - "edit": True, - "inherit": True, - "values": "512 to 1m, power of 2", - "type": "str" - }, - "atime": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "compression": { - "edit": True, - "inherit": True, - "values": "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", - "type": "bool" - }, - "snapdir": { - "edit": True, - "inherit": True, - "values": "hidden | visible", - "type": "str" - }, - "aclmode": { - "edit": True, - "inherit": True, - "values": "discard | groupmask | passthrough | restricted", - "type": "str" - }, - "zoned": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "copies": { - "edit": True, - "inherit": True, - "values": "1 | 2 | 3", - "type": "numeric" - }, - "snapshot_limit": { - "edit": True, - "inherit": False, - "values": "<count> | none", - "type": "numeric" - }, - "aclinherit": { - "edit": True, - "inherit": True, - "values": "discard | noallow | restricted | passthrough | passthrough-x", - "type": "str" - }, - "compressratio": { - "edit": False, - "inherit": False, - "values": "<1.00x or higher if compressed>", - "type": "str" - }, - "xattr": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "written": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "version": { - "edit": True, - "inherit": False, - "values": "1 | 2 | 3 | 4 | 5 | current", - "type": "numeric" - }, - "recordsize": { - "edit": True, - "inherit": True, - "values": "512 to 1m, power of 2", - "type": "str" - }, - "refquota": { - "edit": True, - "inherit": False, - "values": "<size> | none", - "type": "size" - }, - "filesystem_limit": { - "edit": True, - "inherit": False, - "values": "<count> | none", - "type": "numeric" - }, - "lrefer.": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "type": { - "edit": False, - "inherit": False, - "values": "filesystem | volume | snapshot | bookmark", - "type": "str" - }, - "secondarycache": { - "edit": True, - "inherit": True, - "values": "all | none | metadata", - "type": "str" - }, - "refer": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "available": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "used": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "exec": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "compress": { - "edit": True, - "inherit": True, - "values": "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", - "type": "bool" - }, - "volblock": { - "edit": False, - "inherit": True, - "values": "512 to 128k, power of 2", - "type": "str" - }, - "refcompressratio": { - "edit": False, - "inherit": False, - "values": "<1.00x or higher if compressed>", - "type": "str" - }, - "quota": { - "edit": True, - "inherit": False, - "values": "<size> | none", - "type": "size" - }, - "groupquota@": { - "edit": True, - "inherit": False, - "values": "<size> | none", - "type": "size" - }, - "userquota@": { - "edit": True, - "inherit": False, - "values": "<size> | none", - "type": "size" - }, - "snapshot_count": { - "edit": False, - "inherit": False, - "values": "<count>", - "type": "numeric" - }, - "volsize": { - "edit": True, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "clones": { - "edit": False, - "inherit": False, - "values": "<dataset>[,...]", - "type": "str" - }, - "canmount": { - "edit": True, - "inherit": False, - "values": "on | off | noauto", - "type": "bool" - }, - "mounted": { - "edit": False, - "inherit": False, - "values": "yes | no", - "type": "bool_alt" - }, - "groupused@": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "normalization": { - "edit": False, - "inherit": True, - "values": "none | formc | formd | formkc | formkd", - "type": "str" - }, - "usedbychildren": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "usedbydataset": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "mlslabel": { - "edit": True, - "inherit": True, - "values": "<sensitivity label>", - "type": "str" - }, - "refreserv": { - "edit": True, - "inherit": False, - "values": "<size> | none", - "type": "size" - }, - "defer_destroy": { - "edit": False, - "inherit": False, - "values": "yes | no", - "type": "bool_alt" - }, - "volblocksize": { - "edit": False, - "inherit": True, - "values": "512 to 128k, power of 2", - "type": "str" - }, - "lused.": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "redundant_metadata": { - "edit": True, - "inherit": True, - "values": "all | most", - "type": "str" - }, - "filesystem_count": { - "edit": False, - "inherit": False, - "values": "<count>", - "type": "numeric" - }, - "devices": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - }, - "refreservation": { - "edit": True, - "inherit": False, - "values": "<size> | none", - "type": "size" - }, - "userused@": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "logicalreferenced": { - "edit": False, - "inherit": False, - "values": "<size>", - "type": "size" - }, - "checksum": { - "edit": True, - "inherit": True, - "values": "on | off | fletcher2 | fletcher4 | sha256 | sha512 | skein | edonr", - "type": "bool" - }, - "nbmand": { - "edit": True, - "inherit": True, - "values": "on | off", - "type": "bool" - } + "origin": { + "edit": False, + "inherit": False, + "values": "<snapshot>", + "type": "str", + }, + "setuid": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "referenced": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "vscan": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "logicalused": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "userrefs": { + "edit": False, + "inherit": False, + "values": "<count>", + "type": "numeric", + }, + "primarycache": { + "edit": True, + "inherit": True, + "values": "all | none | metadata", + "type": "str", + }, + "logbias": { + "edit": True, + "inherit": True, + "values": "latency | throughput", + "type": "str", + }, + "creation": { + "edit": False, + "inherit": False, + "values": "<date>", + "type": "str", + }, + "sync": { + "edit": True, + "inherit": True, + "values": "standard | always | disabled", + "type": "str", + }, + "dedup": { + "edit": True, + "inherit": True, + "values": "on | off | verify | sha256[,verify], sha512[,verify], skein[,verify], edonr,verify", + "type": "bool", + }, + "sharenfs": { + "edit": True, + "inherit": True, + "values": "on | off | share(1m) options", + "type": "bool", + }, + "receive_resume_token": { + "edit": False, + "inherit": False, + "values": "<string token>", + "type": "str", + }, + "usedbyrefreservation": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "sharesmb": { + "edit": True, + "inherit": True, + "values": "on | off | sharemgr(1m) options", + "type": "bool", + }, + "rdonly": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "reservation": { + "edit": True, + "inherit": False, + "values": "<size> | none", + "type": "size", + }, + "reserv": { + "edit": True, + "inherit": False, + "values": "<size> | none", + "type": "size", + }, + "mountpoint": { + "edit": True, + "inherit": True, + "values": "<path> | legacy | none", + "type": "str", + }, + "casesensitivity": { + "edit": False, + "inherit": True, + "values": "sensitive | insensitive | mixed", + "type": "str", + }, + "utf8only": { + "edit": False, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "usedbysnapshots": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "readonly": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "written@": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "avail": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "recsize": { + "edit": True, + "inherit": True, + "values": "512 to 1m, power of 2", + "type": "str", + }, + "atime": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "compression": { + "edit": True, + "inherit": True, + "values": "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", + "type": "bool", + }, + "snapdir": { + "edit": True, + "inherit": True, + "values": "hidden | visible", + "type": "str", + }, + "aclmode": { + "edit": True, + "inherit": True, + "values": "discard | groupmask | passthrough | restricted", + "type": "str", + }, + "zoned": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "copies": { + "edit": True, + "inherit": True, + "values": "1 | 2 | 3", + "type": "numeric", + }, + "snapshot_limit": { + "edit": True, + "inherit": False, + "values": "<count> | none", + "type": "numeric", + }, + "aclinherit": { + "edit": True, + "inherit": True, + "values": "discard | noallow | restricted | passthrough | passthrough-x", + "type": "str", + }, + "compressratio": { + "edit": False, + "inherit": False, + "values": "<1.00x or higher if compressed>", + "type": "str", + }, + "xattr": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "written": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "version": { + "edit": True, + "inherit": False, + "values": "1 | 2 | 3 | 4 | 5 | current", + "type": "numeric", + }, + "recordsize": { + "edit": True, + "inherit": True, + "values": "512 to 1m, power of 2", + "type": "str", + }, + "refquota": { + "edit": True, + "inherit": False, + "values": "<size> | none", + "type": "size", + }, + "filesystem_limit": { + "edit": True, + "inherit": False, + "values": "<count> | none", + "type": "numeric", + }, + "lrefer.": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "type": { + "edit": False, + "inherit": False, + "values": "filesystem | volume | snapshot | bookmark", + "type": "str", + }, + "secondarycache": { + "edit": True, + "inherit": True, + "values": "all | none | metadata", + "type": "str", + }, + "refer": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "available": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "used": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "exec": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "compress": { + "edit": True, + "inherit": True, + "values": "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", + "type": "bool", + }, + "volblock": { + "edit": False, + "inherit": True, + "values": "512 to 128k, power of 2", + "type": "str", + }, + "refcompressratio": { + "edit": False, + "inherit": False, + "values": "<1.00x or higher if compressed>", + "type": "str", + }, + "quota": { + "edit": True, + "inherit": False, + "values": "<size> | none", + "type": "size", + }, + "groupquota@": { + "edit": True, + "inherit": False, + "values": "<size> | none", + "type": "size", + }, + "userquota@": { + "edit": True, + "inherit": False, + "values": "<size> | none", + "type": "size", + }, + "snapshot_count": { + "edit": False, + "inherit": False, + "values": "<count>", + "type": "numeric", + }, + "volsize": { + "edit": True, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "clones": { + "edit": False, + "inherit": False, + "values": "<dataset>[,...]", + "type": "str", + }, + "canmount": { + "edit": True, + "inherit": False, + "values": "on | off | noauto", + "type": "bool", + }, + "mounted": { + "edit": False, + "inherit": False, + "values": "yes | no", + "type": "bool_alt", + }, + "groupused@": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "normalization": { + "edit": False, + "inherit": True, + "values": "none | formc | formd | formkc | formkd", + "type": "str", + }, + "usedbychildren": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "usedbydataset": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "mlslabel": { + "edit": True, + "inherit": True, + "values": "<sensitivity label>", + "type": "str", + }, + "refreserv": { + "edit": True, + "inherit": False, + "values": "<size> | none", + "type": "size", + }, + "defer_destroy": { + "edit": False, + "inherit": False, + "values": "yes | no", + "type": "bool_alt", + }, + "volblocksize": { + "edit": False, + "inherit": True, + "values": "512 to 128k, power of 2", + "type": "str", + }, + "lused.": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "redundant_metadata": { + "edit": True, + "inherit": True, + "values": "all | most", + "type": "str", + }, + "filesystem_count": { + "edit": False, + "inherit": False, + "values": "<count>", + "type": "numeric", + }, + "devices": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, + "refreservation": { + "edit": True, + "inherit": False, + "values": "<size> | none", + "type": "size", + }, + "userused@": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "logicalreferenced": { + "edit": False, + "inherit": False, + "values": "<size>", + "type": "size", + }, + "checksum": { + "edit": True, + "inherit": True, + "values": "on | off | fletcher2 | fletcher4 | sha256 | sha512 | skein | edonr", + "type": "bool", + }, + "nbmand": { + "edit": True, + "inherit": True, + "values": "on | off", + "type": "bool", + }, } - def _from_auto(self, name, value, source='auto'): - ''' + def _from_auto(self, name, value, source="auto"): + """ some more complex patching for zfs.from_auto - ''' - with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)), \ - patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + """ + with patch.object( + salt.utils.zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ), patch.object( + salt.utils.zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): return salt.utils.zfs.from_auto(name, value, source) - def _from_auto_dict(self, values, source='auto'): - ''' + def _from_auto_dict(self, values, source="auto"): + """ some more complex patching for zfs.from_auto_dict - ''' - with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)), \ - patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + """ + with patch.object( + salt.utils.zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ), patch.object( + salt.utils.zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): return salt.utils.zfs.from_auto_dict(values, source) - def _to_auto(self, name, value, source='auto', convert_to_human=True): - ''' + def _to_auto(self, name, value, source="auto", convert_to_human=True): + """ some more complex patching for zfs.to_auto - ''' - with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)), \ - patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + """ + with patch.object( + salt.utils.zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ), patch.object( + salt.utils.zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): return salt.utils.zfs.to_auto(name, value, source, convert_to_human) - def _to_auto_dict(self, values, source='auto', convert_to_human=True): - ''' + def _to_auto_dict(self, values, source="auto", convert_to_human=True): + """ some more complex patching for zfs.to_auto_dict - ''' - with patch.object(salt.utils.zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)), \ - patch.object(salt.utils.zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): + """ + with patch.object( + salt.utils.zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ), patch.object( + salt.utils.zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): return salt.utils.zfs.to_auto_dict(values, source, convert_to_human) def get_patched_utils(self): return { - 'zfs.is_supported': MagicMock(return_value=True), - 'zfs.has_feature_flags': MagicMock(return_value=True), - 'zfs.property_data_zpool': MagicMock(return_value=self.pmap_zpool), - 'zfs.property_data_zfs': MagicMock(return_value=self.pmap_zfs), + "zfs.is_supported": MagicMock(return_value=True), + "zfs.has_feature_flags": MagicMock(return_value=True), + "zfs.property_data_zpool": MagicMock(return_value=self.pmap_zpool), + "zfs.property_data_zfs": MagicMock(return_value=self.pmap_zfs), # NOTE: we make zpool_command and zfs_command a NOOP # these are extensively tested in tests.unit.utils.test_zfs - 'zfs.zpool_command': MagicMock(return_value='/bin/false'), - 'zfs.zfs_command': MagicMock(return_value='/bin/false'), + "zfs.zpool_command": MagicMock(return_value="/bin/false"), + "zfs.zfs_command": MagicMock(return_value="/bin/false"), # NOTE: from_auto_dict is a special snowflake # internally it calls multiple calls from # salt.utils.zfs but we cannot patch those using # the common methode, __utils__ is not available # so they are direct calls, we do some voodoo here. - 'zfs.from_auto_dict': self._from_auto_dict, - 'zfs.from_auto': self._from_auto, - 'zfs.to_auto_dict': self._to_auto_dict, - 'zfs.to_auto': self._to_auto, + "zfs.from_auto_dict": self._from_auto_dict, + "zfs.from_auto": self._from_auto, + "zfs.to_auto_dict": self._to_auto_dict, + "zfs.to_auto": self._to_auto, } diff --git a/tests/unit/auth/test_ldap.py b/tests/unit/auth/test_ldap.py index 5433ba3372a..a0e48fa6789 100644 --- a/tests/unit/auth/test_ldap.py +++ b/tests/unit/auth/test_ldap.py @@ -8,101 +8,113 @@ import salt.auth.ldap # Import Salt Testing Libs from tests.support.mock import patch -from tests.support.unit import skipIf, TestCase +from tests.support.unit import TestCase, skipIf salt.auth.ldap.__opts__ = {} class Bind(object): - ''' + """ fake search_s return - ''' + """ @staticmethod def search_s(*args, **kwargs): return [ ( - 'cn=saltusers,cn=groups,cn=compat,dc=saltstack,dc=com', - {'memberUid': [b'saltuser'], 'cn': [b'saltusers']}, + "cn=saltusers,cn=groups,cn=compat,dc=saltstack,dc=com", + {"memberUid": [b"saltuser"], "cn": [b"saltusers"]}, ), ] -@skipIf(not salt.auth.ldap.HAS_LDAP, 'Install python-ldap for this test') +@skipIf(not salt.auth.ldap.HAS_LDAP, "Install python-ldap for this test") class LDAPAuthTestCase(TestCase): - ''' + """ Unit tests for salt.auth.ldap - ''' + """ def setUp(self): self.opts = { - 'auth.ldap.binddn': 'uid={{username}},cn=users,cn=compat,dc=saltstack,dc=com', - 'auth.ldap.port': 389, - 'auth.ldap.tls': False, - 'auth.ldap.server': '172.18.0.2', - 'auth.ldap.accountattributename': 'memberUid', - 'auth.ldap.groupattribute': 'memberOf', - 'auth.ldap.group_basedn': 'cn=groups,cn=compat,dc=saltstack,dc=com', - 'auth.ldap.basedn': 'dc=saltstack,dc=com', - 'auth.ldap.group_filter': '(&(memberUid={{ username }})(objectClass=posixgroup))'} + "auth.ldap.binddn": "uid={{username}},cn=users,cn=compat,dc=saltstack,dc=com", + "auth.ldap.port": 389, + "auth.ldap.tls": False, + "auth.ldap.server": "172.18.0.2", + "auth.ldap.accountattributename": "memberUid", + "auth.ldap.groupattribute": "memberOf", + "auth.ldap.group_basedn": "cn=groups,cn=compat,dc=saltstack,dc=com", + "auth.ldap.basedn": "dc=saltstack,dc=com", + "auth.ldap.group_filter": "(&(memberUid={{ username }})(objectClass=posixgroup))", + } def tearDown(self): - self.opts['auth.ldap.freeipa'] = False - self.opts['auth.ldap.activedirectory'] = False + self.opts["auth.ldap.freeipa"] = False + self.opts["auth.ldap.activedirectory"] = False def test_config(self): - ''' + """ Test that the _config function works correctly - ''' + """ with patch.dict(salt.auth.ldap.__opts__, self.opts): - self.assertEqual(salt.auth.ldap._config('basedn'), 'dc=saltstack,dc=com') - self.assertEqual(salt.auth.ldap._config('group_filter'), '(&(memberUid={{ username }})(objectClass=posixgroup))') - self.assertEqual(salt.auth.ldap._config('accountattributename'), 'memberUid') - self.assertEqual(salt.auth.ldap._config('groupattribute'), 'memberOf') + self.assertEqual(salt.auth.ldap._config("basedn"), "dc=saltstack,dc=com") + self.assertEqual( + salt.auth.ldap._config("group_filter"), + "(&(memberUid={{ username }})(objectClass=posixgroup))", + ) + self.assertEqual( + salt.auth.ldap._config("accountattributename"), "memberUid" + ) + self.assertEqual(salt.auth.ldap._config("groupattribute"), "memberOf") def test_groups_freeipa(self): - ''' + """ test groups in freeipa - ''' - self.opts['auth.ldap.freeipa'] = True + """ + self.opts["auth.ldap.freeipa"] = True with patch.dict(salt.auth.ldap.__opts__, self.opts): - with patch('salt.auth.ldap._bind', return_value=Bind): - self.assertIn('saltusers', salt.auth.ldap.groups('saltuser', password='password')) + with patch("salt.auth.ldap._bind", return_value=Bind): + self.assertIn( + "saltusers", salt.auth.ldap.groups("saltuser", password="password") + ) def test_groups(self): - ''' + """ test groups in ldap - ''' + """ with patch.dict(salt.auth.ldap.__opts__, self.opts): - with patch('salt.auth.ldap._bind', return_value=Bind): - self.assertIn('saltusers', salt.auth.ldap.groups('saltuser', password='password')) + with patch("salt.auth.ldap._bind", return_value=Bind): + self.assertIn( + "saltusers", salt.auth.ldap.groups("saltuser", password="password") + ) def test_groups_activedirectory(self): - ''' + """ test groups in activedirectory - ''' - self.opts['auth.ldap.activedirectory'] = True + """ + self.opts["auth.ldap.activedirectory"] = True with patch.dict(salt.auth.ldap.__opts__, self.opts): - with patch('salt.auth.ldap._bind', return_value=Bind): - self.assertIn('saltusers', salt.auth.ldap.groups('saltuser', password='password')) + with patch("salt.auth.ldap._bind", return_value=Bind): + self.assertIn( + "saltusers", salt.auth.ldap.groups("saltuser", password="password") + ) def test_auth_nopass(self): opts = self.opts.copy() - opts['auth.ldap.bindpw'] = 'p@ssw0rd!' + opts["auth.ldap.bindpw"] = "p@ssw0rd!" with patch.dict(salt.auth.ldap.__opts__, opts): - with patch('salt.auth.ldap._bind_for_search', return_value=Bind): - self.assertFalse(salt.auth.ldap.auth('foo', None)) + with patch("salt.auth.ldap._bind_for_search", return_value=Bind): + self.assertFalse(salt.auth.ldap.auth("foo", None)) def test_auth_nouser(self): opts = self.opts.copy() - opts['auth.ldap.bindpw'] = 'p@ssw0rd!' + opts["auth.ldap.bindpw"] = "p@ssw0rd!" with patch.dict(salt.auth.ldap.__opts__, opts): - with patch('salt.auth.ldap._bind_for_search', return_value=Bind): - self.assertFalse(salt.auth.ldap.auth(None, 'foo')) + with patch("salt.auth.ldap._bind_for_search", return_value=Bind): + self.assertFalse(salt.auth.ldap.auth(None, "foo")) def test_auth_nouserandpass(self): opts = self.opts.copy() - opts['auth.ldap.bindpw'] = 'p@ssw0rd!' + opts["auth.ldap.bindpw"] = "p@ssw0rd!" with patch.dict(salt.auth.ldap.__opts__, opts): - with patch('salt.auth.ldap._bind_for_search', return_value=Bind): + with patch("salt.auth.ldap._bind_for_search", return_value=Bind): self.assertFalse(salt.auth.ldap.auth(None, None)) diff --git a/tests/unit/beacons/test_adb.py b/tests/unit/beacons/test_adb.py index 4094d7078ac..352bb04ae6f 100644 --- a/tests/unit/beacons/test_adb.py +++ b/tests/unit/beacons/test_adb.py @@ -3,452 +3,497 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, Mock -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.adb as adb +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock, patch + +# Salt testing libs +from tests.support.unit import TestCase class ADBBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.adb - ''' + """ def setup_loader_modules(self): - return { - adb: { - 'last_state': {}, - 'last_state_extra': {'no_devices': False} - } - } + return {adb: {"last_state": {}, "last_state_extra": {"no_devices": False}}} def test_no_adb_command(self): - with patch('salt.utils.path.which') as mock: + with patch("salt.utils.path.which") as mock: mock.return_value = None ret = adb.__virtual__() - mock.assert_called_once_with('adb') + mock.assert_called_once_with("adb") self.assertFalse(ret) def test_with_adb_command(self): - with patch('salt.utils.path.which') as mock: - mock.return_value = '/usr/bin/adb' + with patch("salt.utils.path.which") as mock: + mock.return_value = "/usr/bin/adb" ret = adb.__virtual__() - mock.assert_called_once_with('adb') - self.assertEqual(ret, 'adb') + mock.assert_called_once_with("adb") + self.assertEqual(ret, "adb") def test_non_list_config(self): config = {} ret = adb.validate(config) - self.assertEqual(ret, (False, 'Configuration for adb beacon must' - ' be a list.')) + self.assertEqual(ret, (False, "Configuration for adb beacon must be a list.")) def test_empty_config(self): config = [{}] ret = adb.validate(config) - self.assertEqual(ret, (False, 'Configuration for adb beacon must' - ' include a states array.')) + self.assertEqual( + ret, (False, "Configuration for adb beacon must include a states array.") + ) def test_invalid_states(self): - config = [{'states': ['Random', 'Failings']}] + config = [{"states": ["Random", "Failings"]}] ret = adb.validate(config) - self.assertEqual(ret, (False, 'Need a one of the following' - ' adb states: offline, bootloader,' - ' device, host, recovery, no' - ' permissions, sideload,' - ' unauthorized, unknown, missing')) + self.assertEqual( + ret, + ( + False, + "Need a one of the following" + " adb states: offline, bootloader," + " device, host, recovery, no" + " permissions, sideload," + " unauthorized, unknown, missing", + ), + ) def test_device_state(self): - config = [{'states': ['device']}] + config = [{"states": ["device"]}] - mock = Mock(return_value='List of devices attached\nHTC\tdevice',) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + mock = Mock(return_value="List of devices attached\nHTC\tdevice",) + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'device', - 'tag': 'device'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) def test_device_state_change(self): - config = [{'states': ['offline']}] + config = [{"states": ["offline"]}] out = [ - 'List of devices attached\nHTC\tdevice', - 'List of devices attached\nHTC\toffline' + "List of devices attached\nHTC\tdevice", + "List of devices attached\nHTC\toffline", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) self.assertEqual(ret, []) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'offline', - 'tag': 'offline'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "offline", "tag": "offline"}] + ) def test_multiple_devices(self): - config = [{'states': ['offline', 'device']}] + config = [{"states": ["offline", "device"]}] out = [ - 'List of devices attached\nHTC\tdevice', - 'List of devices attached\nHTC\toffline\nNexus\tdevice' + "List of devices attached\nHTC\tdevice", + "List of devices attached\nHTC\toffline\nNexus\tdevice", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'device', - 'tag': 'device'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) ret = adb.beacon(config) - self.assertEqual(ret, [ - {'device': 'HTC', 'state': 'offline', 'tag': 'offline'}, - {'device': 'Nexus', 'state': 'device', 'tag': 'device'} - ]) + self.assertEqual( + ret, + [ + {"device": "HTC", "state": "offline", "tag": "offline"}, + {"device": "Nexus", "state": "device", "tag": "device"}, + ], + ) def test_no_devices_with_different_states(self): - config = [{'states': ['offline'], 'no_devices_event': True}] + config = [{"states": ["offline"], "no_devices_event": True}] - mock = Mock(return_value='List of devices attached\nHTC\tdevice') - with patch.dict(adb.__salt__, {'cmd.run': mock}): + mock = Mock(return_value="List of devices attached\nHTC\tdevice") + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) self.assertEqual(ret, []) def test_no_devices_no_repeat(self): - config = [{'states': ['offline', 'device'], 'no_devices_event': True}] + config = [{"states": ["offline", "device"], "no_devices_event": True}] out = [ - 'List of devices attached\nHTC\tdevice', - 'List of devices attached', - 'List of devices attached' + "List of devices attached\nHTC\tdevice", + "List of devices attached", + "List of devices attached", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'device', - 'tag': 'device'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) ret = adb.beacon(config) - self.assertEqual(ret, [{'tag': 'no_devices'}]) + self.assertEqual(ret, [{"tag": "no_devices"}]) ret = adb.beacon(config) self.assertEqual(ret, []) def test_no_devices(self): - config = [{'states': ['offline', 'device'], 'no_devices_event': True}] + config = [{"states": ["offline", "device"], "no_devices_event": True}] - out = [ - 'List of devices attached', - 'List of devices attached' - ] + out = ["List of devices attached", "List of devices attached"] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'tag': 'no_devices'}]) + self.assertEqual(ret, [{"tag": "no_devices"}]) ret = adb.beacon(config) self.assertEqual(ret, []) def test_device_missing(self): - config = [{'states': ['device', 'missing']}] + config = [{"states": ["device", "missing"]}] out = [ - 'List of devices attached\nHTC\tdevice', - 'List of devices attached', - 'List of devices attached\nHTC\tdevice', - 'List of devices attached\nHTC\tdevice' + "List of devices attached\nHTC\tdevice", + "List of devices attached", + "List of devices attached\nHTC\tdevice", + "List of devices attached\nHTC\tdevice", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'device', - 'tag': 'device'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'missing', - 'tag': 'missing'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "missing", "tag": "missing"}] + ) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'device', - 'tag': 'device'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) ret = adb.beacon(config) self.assertEqual(ret, []) def test_with_startup(self): - config = [{'states': ['device']}] + config = [{"states": ["device"]}] - mock = Mock(return_value='* daemon started successfully *\nList of devices attached\nHTC\tdevice',) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + mock = Mock( + return_value="* daemon started successfully *\nList of devices attached\nHTC\tdevice", + ) + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'device', - 'tag': 'device'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) def test_with_user(self): - config = [{'states': ['device'], 'user': 'fred'}] + config = [{"states": ["device"], "user": "fred"}] - mock = Mock(return_value='* daemon started successfully *\nList of devices attached\nHTC\tdevice') - with patch.dict(adb.__salt__, {'cmd.run': mock}): + mock = Mock( + return_value="* daemon started successfully *\nList of devices attached\nHTC\tdevice" + ) + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - mock.assert_called_once_with('adb devices', runas='fred') - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'device', - 'tag': 'device'}]) + mock.assert_called_once_with("adb devices", runas="fred") + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) def test_device_low_battery(self): - config = [{'states': ['device'], 'battery_low': 30}] + config = [{"states": ["device"], "battery_low": 30}] out = [ - 'List of devices attached\nHTC\tdevice', - '25', + "List of devices attached\nHTC\tdevice", + "25", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'state': 'device', 'tag': 'device'}, - {'device': 'HTC', 'battery_level': 25, 'tag': 'battery_low'}]) + self.assertEqual( + ret, + [ + {"device": "HTC", "state": "device", "tag": "device"}, + {"device": "HTC", "battery_level": 25, "tag": "battery_low"}, + ], + ) def test_device_no_repeat(self): - config = [{'states': ['device'], 'battery_low': 30}] + config = [{"states": ["device"], "battery_low": 30}] out = [ - 'List of devices attached\nHTC\tdevice', - '25', - 'List of devices attached\nHTC\tdevice', - '25' + "List of devices attached\nHTC\tdevice", + "25", + "List of devices attached\nHTC\tdevice", + "25", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'state': 'device', 'tag': 'device'}, - {'device': 'HTC', 'battery_level': 25, 'tag': 'battery_low'}]) + self.assertEqual( + ret, + [ + {"device": "HTC", "state": "device", "tag": "device"}, + {"device": "HTC", "battery_level": 25, "tag": "battery_low"}, + ], + ) ret = adb.beacon(config) self.assertEqual(ret, []) def test_device_no_repeat_capacity_increase(self): - config = [{'states': ['device'], 'battery_low': 75}] + config = [{"states": ["device"], "battery_low": 75}] out = [ - 'List of devices attached\nHTC\tdevice', - '25', - 'List of devices attached\nHTC\tdevice', - '30' + "List of devices attached\nHTC\tdevice", + "25", + "List of devices attached\nHTC\tdevice", + "30", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'state': 'device', 'tag': 'device'}, - {'device': 'HTC', 'battery_level': 25, 'tag': 'battery_low'}]) + self.assertEqual( + ret, + [ + {"device": "HTC", "state": "device", "tag": "device"}, + {"device": "HTC", "battery_level": 25, "tag": "battery_low"}, + ], + ) ret = adb.beacon(config) self.assertEqual(ret, []) def test_device_no_repeat_with_not_found_state(self): - config = [{'states': ['offline'], 'battery_low': 30}] + config = [{"states": ["offline"], "battery_low": 30}] out = [ - 'List of devices attached\nHTC\tdevice', - '25', - 'List of devices attached\nHTC\tdevice', - '25' + "List of devices attached\nHTC\tdevice", + "25", + "List of devices attached\nHTC\tdevice", + "25", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'battery_level': 25, 'tag': 'battery_low'}]) + self.assertEqual( + ret, [{"device": "HTC", "battery_level": 25, "tag": "battery_low"}] + ) ret = adb.beacon(config) self.assertEqual(ret, []) def test_device_battery_charged(self): - config = [{'states': ['device'], 'battery_low': 30}] + config = [{"states": ["device"], "battery_low": 30}] out = [ - 'List of devices attached\nHTC\tdevice', - '100', + "List of devices attached\nHTC\tdevice", + "100", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', - 'state': 'device', - 'tag': 'device'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) def test_device_low_battery_equal(self): - config = [{'states': ['device'], 'battery_low': 25}] + config = [{"states": ["device"], "battery_low": 25}] out = [ - 'List of devices attached\nHTC\tdevice', - '25', + "List of devices attached\nHTC\tdevice", + "25", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'state': 'device', 'tag': 'device'}, - {'device': 'HTC', 'battery_level': 25, 'tag': 'battery_low'}]) + self.assertEqual( + ret, + [ + {"device": "HTC", "state": "device", "tag": "device"}, + {"device": "HTC", "battery_level": 25, "tag": "battery_low"}, + ], + ) def test_device_battery_not_found(self): - config = [{'states': ['device'], 'battery_low': 25}] + config = [{"states": ["device"], "battery_low": 25}] out = [ - 'List of devices attached\nHTC\tdevice', - '/system/bin/sh: cat: /sys/class/power_supply/*/capacity: No such file or directory', + "List of devices attached\nHTC\tdevice", + "/system/bin/sh: cat: /sys/class/power_supply/*/capacity: No such file or directory", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'state': 'device', 'tag': 'device'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) def test_device_repeat_multi(self): - config = [{'states': ['offline'], 'battery_low': 35}] + config = [{"states": ["offline"], "battery_low": 35}] out = [ - 'List of devices attached\nHTC\tdevice', - '25', - 'List of devices attached\nHTC\tdevice', - '40', - 'List of devices attached\nHTC\tdevice', - '25', - 'List of devices attached\nHTC\tdevice', - '80' + "List of devices attached\nHTC\tdevice", + "25", + "List of devices attached\nHTC\tdevice", + "40", + "List of devices attached\nHTC\tdevice", + "25", + "List of devices attached\nHTC\tdevice", + "80", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'battery_level': 25, 'tag': 'battery_low'}]) + self.assertEqual( + ret, [{"device": "HTC", "battery_level": 25, "tag": "battery_low"}] + ) ret = adb.beacon(config) self.assertEqual(ret, []) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'battery_level': 25, 'tag': 'battery_low'}]) + self.assertEqual( + ret, [{"device": "HTC", "battery_level": 25, "tag": "battery_low"}] + ) ret = adb.beacon(config) self.assertEqual(ret, []) def test_weird_batteries(self): - config = [{'states': ['device'], 'battery_low': 25}] + config = [{"states": ["device"], "battery_low": 25}] out = [ - 'List of devices attached\nHTC\tdevice', - '-9000', + "List of devices attached\nHTC\tdevice", + "-9000", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'state': 'device', 'tag': 'device'}]) + self.assertEqual( + ret, [{"device": "HTC", "state": "device", "tag": "device"}] + ) def test_multiple_batteries(self): - config = [{'states': ['device'], 'battery_low': 30}] + config = [{"states": ["device"], "battery_low": 30}] out = [ - 'List of devices attached\nHTC\tdevice', - '25\n40', + "List of devices attached\nHTC\tdevice", + "25\n40", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'state': 'device', 'tag': 'device'}, - {'device': 'HTC', 'battery_level': 25, 'tag': 'battery_low'}]) + self.assertEqual( + ret, + [ + {"device": "HTC", "state": "device", "tag": "device"}, + {"device": "HTC", "battery_level": 25, "tag": "battery_low"}, + ], + ) def test_multiple_low_batteries(self): - config = [{'states': ['device'], 'battery_low': 30}] + config = [{"states": ["device"], "battery_low": 30}] out = [ - 'List of devices attached\nHTC\tdevice', - '25\n14', + "List of devices attached\nHTC\tdevice", + "25\n14", ] mock = Mock(side_effect=out) - with patch.dict(adb.__salt__, {'cmd.run': mock}): + with patch.dict(adb.__salt__, {"cmd.run": mock}): ret = adb.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = adb.beacon(config) - self.assertEqual(ret, [{'device': 'HTC', 'state': 'device', 'tag': 'device'}, - {'device': 'HTC', 'battery_level': 25, 'tag': 'battery_low'}]) + self.assertEqual( + ret, + [ + {"device": "HTC", "state": "device", "tag": "device"}, + {"device": "HTC", "battery_level": 25, "tag": "battery_low"}, + ], + ) diff --git a/tests/unit/beacons/test_avahi_announce.py b/tests/unit/beacons/test_avahi_announce.py index 9cdc92b8ea6..6bf954c1910 100644 --- a/tests/unit/beacons/test_avahi_announce.py +++ b/tests/unit/beacons/test_avahi_announce.py @@ -3,24 +3,24 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.avahi_announce as avahi_announce +from tests.support.mixins import LoaderModuleMockMixin + +# Salt testing libs +from tests.support.unit import TestCase class AvahiAnnounceBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.avahi_announce - ''' + """ def setup_loader_modules(self): return { avahi_announce: { - 'last_state': {}, - 'last_state_extra': {'no_devices': False} + "last_state": {}, + "last_state_extra": {"no_devices": False}, } } @@ -29,14 +29,21 @@ class AvahiAnnounceBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = avahi_announce.validate(config) - self.assertEqual(ret, (False, 'Configuration for avahi_announce' - ' beacon must be a list.')) + self.assertEqual( + ret, (False, "Configuration for avahi_announce beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = avahi_announce.validate(config) - self.assertEqual(ret, (False, 'Configuration for avahi_announce' - ' beacon must contain servicetype, port' - ' and txt items.')) + self.assertEqual( + ret, + ( + False, + "Configuration for avahi_announce" + " beacon must contain servicetype, port" + " and txt items.", + ), + ) diff --git a/tests/unit/beacons/test_bonjour_announce.py b/tests/unit/beacons/test_bonjour_announce.py index c611784f5b4..689f5308d15 100644 --- a/tests/unit/beacons/test_bonjour_announce.py +++ b/tests/unit/beacons/test_bonjour_announce.py @@ -3,24 +3,24 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.bonjour_announce as bonjour_announce +from tests.support.mixins import LoaderModuleMockMixin + +# Salt testing libs +from tests.support.unit import TestCase class BonjourAnnounceBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.avahi_announce - ''' + """ def setup_loader_modules(self): return { bonjour_announce: { - 'last_state': {}, - 'last_state_extra': {'no_devices': False} + "last_state": {}, + "last_state_extra": {"no_devices": False}, } } @@ -29,14 +29,21 @@ class BonjourAnnounceBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = bonjour_announce.validate(config) - self.assertEqual(ret, (False, 'Configuration for bonjour_announce' - ' beacon must be a list.')) + self.assertEqual( + ret, (False, "Configuration for bonjour_announce beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = bonjour_announce.validate(config) - self.assertEqual(ret, (False, 'Configuration for bonjour_announce' - ' beacon must contain servicetype, port' - ' and txt items.')) + self.assertEqual( + ret, + ( + False, + "Configuration for bonjour_announce" + " beacon must contain servicetype, port" + " and txt items.", + ), + ) diff --git a/tests/unit/beacons/test_btmp.py b/tests/unit/beacons/test_btmp.py index e3bb50477d4..74053c02c76 100644 --- a/tests/unit/beacons/test_btmp.py +++ b/tests/unit/beacons/test_btmp.py @@ -2,204 +2,269 @@ # Python libs from __future__ import absolute_import + import datetime import logging -# Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import patch, MagicMock, mock_open -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.btmp as btmp from salt.ext import six +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch + +# Salt testing libs +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error try: import dateutil.parser as dateutil_parser # pylint: disable=unused-import + _TIME_SUPPORTED = True except ImportError: _TIME_SUPPORTED = False -raw = b'\x06\x00\x00\x00Nt\x00\x00ssh:notty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00garet\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdd\xc7\xc2Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' -pack = (6, 29774, b'ssh:notty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'\x00\x00\x00\x00', b'garet\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 0, 0, 0, 1505937373, 0, 0, 0, 0, 16777216) +raw = b"\x06\x00\x00\x00Nt\x00\x00ssh:notty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00garet\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdd\xc7\xc2Y\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +pack = ( + 6, + 29774, + b"ssh:notty\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + b"\x00\x00\x00\x00", + b"garet\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + b"::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + 0, + 0, + 0, + 1505937373, + 0, + 0, + 0, + 0, + 16777216, +) log = logging.getLogger(__name__) class BTMPBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.[s] - ''' + """ def setup_loader_modules(self): - return { - btmp: { - '__context__': {'btmp.loc': 2}, - '__salt__': {}, - } - } + return {btmp: {"__context__": {"btmp.loc": 2}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = btmp.validate(config) - self.assertEqual(ret, (False, 'Configuration for btmp beacon must' - ' be a list.')) + self.assertEqual(ret, (False, "Configuration for btmp beacon must be a list.")) def test_empty_config(self): config = [{}] ret = btmp.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) def test_no_match(self): - config = [{'users': {'gareth': {'time_range': {'end': '09-22-2017 5pm', - 'start': '09-22-2017 3pm'}}}} - ] + config = [ + { + "users": { + "gareth": { + "time_range": { + "end": "09-22-2017 5pm", + "start": "09-22-2017 3pm", + } + } + } + } + ] ret = btmp.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - with patch('salt.utils.files.fopen', mock_open(b'')) as m_open: + with patch("salt.utils.files.fopen", mock_open(b"")) as m_open: ret = btmp.beacon(config) call_args = next(six.itervalues(m_open.filehandles))[0].call.args - assert call_args == (btmp.BTMP, 'rb'), call_args + assert call_args == (btmp.BTMP, "rb"), call_args assert ret == [], ret def test_invalid_users(self): - config = [{'users': ['gareth']}] + config = [{"users": ["gareth"]}] ret = btmp.validate(config) - self.assertEqual(ret, (False, 'User configuration for btmp beacon must be a dictionary.')) + self.assertEqual( + ret, (False, "User configuration for btmp beacon must be a dictionary.") + ) def test_invalid_groups(self): - config = [{'groups': ['docker']}] + config = [{"groups": ["docker"]}] ret = btmp.validate(config) - self.assertEqual(ret, (False, 'Group configuration for btmp beacon must be a dictionary.')) + self.assertEqual( + ret, (False, "Group configuration for btmp beacon must be a dictionary.") + ) def test_default_invalid_time_range(self): - config = [{'defaults': {'time_range': {'start': '3pm'}}}] + config = [{"defaults": {"time_range": {"start": "3pm"}}}] ret = btmp.validate(config) - self.assertEqual(ret, (False, 'The time_range parameter for btmp beacon must contain start & end options.')) + self.assertEqual( + ret, + ( + False, + "The time_range parameter for btmp beacon must contain start & end options.", + ), + ) def test_users_invalid_time_range(self): - config = [{'users': {'gareth': {'time_range': {'start': '3pm'}}}}] + config = [{"users": {"gareth": {"time_range": {"start": "3pm"}}}}] ret = btmp.validate(config) - self.assertEqual(ret, (False, 'The time_range parameter for btmp beacon must contain start & end options.')) + self.assertEqual( + ret, + ( + False, + "The time_range parameter for btmp beacon must contain start & end options.", + ), + ) def test_groups_invalid_time_range(self): - config = [{'groups': {'docker': {'time_range': {'start': '3pm'}}}}] + config = [{"groups": {"docker": {"time_range": {"start": "3pm"}}}}] ret = btmp.validate(config) - self.assertEqual(ret, (False, 'The time_range parameter for btmp beacon must contain start & end options.')) + self.assertEqual( + ret, + ( + False, + "The time_range parameter for btmp beacon must contain start & end options.", + ), + ) def test_match(self): - with patch('salt.utils.files.fopen', - mock_open(read_data=raw)): - with patch('struct.unpack', - MagicMock(return_value=pack)): - config = [{'users': {'garet': {}}}] + with patch("salt.utils.files.fopen", mock_open(read_data=raw)): + with patch("struct.unpack", MagicMock(return_value=pack)): + config = [{"users": {"garet": {}}}] ret = btmp.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected = [{'addr': 1505937373, - 'exit_status': 0, - 'inittab': '', - 'hostname': '::1', - 'PID': 29774, - 'session': 0, - 'user': - 'garet', - 'time': 0, - 'line': 'ssh:notty', - 'type': 6}] + _expected = [ + { + "addr": 1505937373, + "exit_status": 0, + "inittab": "", + "hostname": "::1", + "PID": 29774, + "session": 0, + "user": "garet", + "time": 0, + "line": "ssh:notty", + "type": 6, + } + ] ret = btmp.beacon(config) self.assertEqual(ret, _expected) - @skipIf(not _TIME_SUPPORTED, 'dateutil.parser is missing.') + @skipIf(not _TIME_SUPPORTED, "dateutil.parser is missing.") def test_match_time(self): - with patch('salt.utils.files.fopen', - mock_open(read_data=raw)): + with patch("salt.utils.files.fopen", mock_open(read_data=raw)): mock_now = datetime.datetime(2017, 9, 22, 16, 0, 0, 0) - with patch('datetime.datetime', MagicMock()), \ - patch('datetime.datetime.now', - MagicMock(return_value=mock_now)): - with patch('struct.unpack', - MagicMock(return_value=pack)): - config = [{'users': {'garet': {'time_range': {'end': '09-22-2017 5pm', - 'start': '09-22-2017 3pm'}}}} - ] + with patch("datetime.datetime", MagicMock()), patch( + "datetime.datetime.now", MagicMock(return_value=mock_now) + ): + with patch("struct.unpack", MagicMock(return_value=pack)): + config = [ + { + "users": { + "garet": { + "time_range": { + "end": "09-22-2017 5pm", + "start": "09-22-2017 3pm", + } + } + } + } + ] ret = btmp.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected = [{'addr': 1505937373, - 'exit_status': 0, - 'inittab': '', - 'hostname': '::1', - 'PID': 29774, - 'session': 0, - 'user': - 'garet', - 'time': 0, - 'line': 'ssh:notty', - 'type': 6}] + _expected = [ + { + "addr": 1505937373, + "exit_status": 0, + "inittab": "", + "hostname": "::1", + "PID": 29774, + "session": 0, + "user": "garet", + "time": 0, + "line": "ssh:notty", + "type": 6, + } + ] ret = btmp.beacon(config) self.assertEqual(ret, _expected) def test_match_group(self): - for groupadd in ('salt.modules.aix_group', - 'salt.modules.mac_group', - 'salt.modules.pw_group', - 'salt.modules.solaris_group', - 'salt.modules.win_groupadd'): - mock_group_info = {'passwd': 'x', - 'gid': 100, - 'name': 'users', - 'members': ['garet']} + for groupadd in ( + "salt.modules.aix_group", + "salt.modules.mac_group", + "salt.modules.pw_group", + "salt.modules.solaris_group", + "salt.modules.win_groupadd", + ): + mock_group_info = { + "passwd": "x", + "gid": 100, + "name": "users", + "members": ["garet"], + } - with patch('salt.utils.files.fopen', - mock_open(read_data=raw)): - with patch('time.time', - MagicMock(return_value=1506121200)): - with patch('struct.unpack', - MagicMock(return_value=pack)): - with patch('{0}.info'.format(groupadd), - new=MagicMock(return_value=mock_group_info)): - config = [{'group': {'users': {'time_range': {'end': '5pm', - 'start': '3pm'}}}} - ] + with patch("salt.utils.files.fopen", mock_open(read_data=raw)): + with patch("time.time", MagicMock(return_value=1506121200)): + with patch("struct.unpack", MagicMock(return_value=pack)): + with patch( + "{0}.info".format(groupadd), + new=MagicMock(return_value=mock_group_info), + ): + config = [ + { + "group": { + "users": { + "time_range": {"end": "5pm", "start": "3pm"} + } + } + } + ] ret = btmp.validate(config) - self.assertEqual(ret, - (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected = [{'addr': 1505937373, - 'exit_status': 0, - 'inittab': '', - 'hostname': '::1', - 'PID': 29774, - 'session': 0, - 'user': - 'garet', - 'time': 0, - 'line': 'ssh:notty', - 'type': 6}] + _expected = [ + { + "addr": 1505937373, + "exit_status": 0, + "inittab": "", + "hostname": "::1", + "PID": 29774, + "session": 0, + "user": "garet", + "time": 0, + "line": "ssh:notty", + "type": 6, + } + ] ret = btmp.beacon(config) self.assertEqual(ret, _expected) diff --git a/tests/unit/beacons/test_cert_info.py b/tests/unit/beacons/test_cert_info.py index b52a662a4a6..dc68714e5c9 100644 --- a/tests/unit/beacons/test_cert_info.py +++ b/tests/unit/beacons/test_cert_info.py @@ -2,20 +2,21 @@ # Python libs from __future__ import absolute_import -import logging -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, mock_open -from tests.support.mixins import LoaderModuleMockMixin +import logging # Salt libs import salt.beacons.cert_info as cert_info +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import mock_open, patch + +# Salt testing libs +from tests.support.unit import TestCase log = logging.getLogger(__name__) -_TEST_CERT = ''' +_TEST_CERT = """ -----BEGIN CERTIFICATE----- MIIC/jCCAeagAwIBAgIJAIQMfu6ShHvfMA0GCSqGSIb3DQEBCwUAMCQxIjAgBgNV BAMMGXNhbHR0ZXN0LTAxLmV4YW1wbGUubG9jYWwwHhcNMTkwNjAzMjA1OTIyWhcN @@ -35,70 +36,71 @@ seZBENjwjJA6zZmTXvYyzV5OBP4JyOhYuG9aqr7e6/yjPBEtZv0TJ9KMMbcywvE9 9FF+l4Y+wgKR/icrpDEpPlC4wYn64sy5vk7EGVagnVyhkjLJ52rn4trzyPox8FmO 2Zw= -----END CERTIFICATE----- -''' +""" class CertInfoBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.cert_info - ''' + """ def setup_loader_modules(self): - return { - cert_info: { - '__context__': {}, - '__salt__': {}, - } - } + return {cert_info: {"__context__": {}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = cert_info.validate(config) - self.assertEqual(ret, (False, 'Configuration for cert_info beacon must' - ' be a list.')) + self.assertEqual( + ret, (False, "Configuration for cert_info beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = cert_info.validate(config) - self.assertEqual(ret, (False, 'Configuration for cert_info beacon ' - 'must contain files option.')) + self.assertEqual( + ret, + (False, "Configuration for cert_info beacon must contain files option."), + ) def test_cert_information(self): - with patch('salt.utils.files.fopen', - mock_open(read_data=_TEST_CERT)): - config = [{'files': ['/etc/pki/tls/certs/mycert.pem'], - 'notify_days': -1 - }] + with patch("salt.utils.files.fopen", mock_open(read_data=_TEST_CERT)): + config = [{"files": ["/etc/pki/tls/certs/mycert.pem"], "notify_days": -1}] ret = cert_info.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) _expected_return = [ { - 'certificates': [ + "certificates": [ { - 'cert_path': '/etc/pki/tls/certs/mycert.pem', - 'extensions': [{'ext_data': 'CA:FALSE', - 'ext_name': 'basicConstraints'}, - {'ext_data': 'DNS:salttest-01.example.local', - 'ext_name': 'subjectAltName'}], - 'has_expired': False, - 'issuer': 'CN="salttest-01.example.local"', - 'issuer_dict': {'CN': 'salttest-01.example.local'}, - 'notAfter': '2029-05-31 20:59:22Z', - 'notAfter_raw': '20290531205922Z', - 'notBefore': '2019-06-03 20:59:22Z', - 'notBefore_raw': '20190603205922Z', - 'serial_number': 9515119675852487647, - 'signature_algorithm': 'sha256WithRSAEncryption', - 'subject': 'CN="salttest-01.example.local"', - 'subject_dict': {'CN': 'salttest-01.example.local'}, - 'version': 2 + "cert_path": "/etc/pki/tls/certs/mycert.pem", + "extensions": [ + { + "ext_data": "CA:FALSE", + "ext_name": "basicConstraints", + }, + { + "ext_data": "DNS:salttest-01.example.local", + "ext_name": "subjectAltName", + }, + ], + "has_expired": False, + "issuer": 'CN="salttest-01.example.local"', + "issuer_dict": {"CN": "salttest-01.example.local"}, + "notAfter": "2029-05-31 20:59:22Z", + "notAfter_raw": "20290531205922Z", + "notBefore": "2019-06-03 20:59:22Z", + "notBefore_raw": "20190603205922Z", + "serial_number": 9515119675852487647, + "signature_algorithm": "sha256WithRSAEncryption", + "subject": 'CN="salttest-01.example.local"', + "subject_dict": {"CN": "salttest-01.example.local"}, + "version": 2, } ] } diff --git a/tests/unit/beacons/test_diskusage.py b/tests/unit/beacons/test_diskusage.py index 41c5deb3d14..b3f747467b1 100644 --- a/tests/unit/beacons/test_diskusage.py +++ b/tests/unit/beacons/test_diskusage.py @@ -2,56 +2,49 @@ # Python libs from __future__ import absolute_import -from collections import namedtuple -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock, Mock -from tests.support.mixins import LoaderModuleMockMixin +from collections import namedtuple # Salt libs import salt.beacons.diskusage as diskusage +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch + +# Salt testing libs +from tests.support.unit import TestCase STUB_DISK_PARTITION = [ - namedtuple( - 'partition', - 'device mountpoint fstype, opts')( - 'tmpfs', '/mnt/tmp', 'tmpfs', - 'rw,nosuid,nodev,relatime,size=10240k'), - namedtuple( - 'partition', - 'device mountpoint fstype, opts')( - '/dev/disk0s2', '/', 'hfs', - 'rw,local,rootfs,dovolfs,journaled,multilabel')] + namedtuple("partition", "device mountpoint fstype, opts")( + "tmpfs", "/mnt/tmp", "tmpfs", "rw,nosuid,nodev,relatime,size=10240k" + ), + namedtuple("partition", "device mountpoint fstype, opts")( + "/dev/disk0s2", "/", "hfs", "rw,local,rootfs,dovolfs,journaled,multilabel" + ), +] WINDOWS_STUB_DISK_PARTITION = [ - namedtuple( - 'partition', - 'device mountpoint fstype, opts')( - 'C:\\', 'C:\\', 'NTFS', - 'rw,fixed'), - namedtuple( - 'partition', - 'device mountpoint fstype, opts')( - 'D:\\', 'D:\\', 'CDFS', - 'ro,cdrom')] + namedtuple("partition", "device mountpoint fstype, opts")( + "C:\\", "C:\\", "NTFS", "rw,fixed" + ), + namedtuple("partition", "device mountpoint fstype, opts")( + "D:\\", "D:\\", "CDFS", "ro,cdrom" + ), +] -STUB_DISK_USAGE = [namedtuple('usage', - 'total used free percent')(1000, 500, 500, 50), - namedtuple('usage', - 'total used free percent')(100, 75, 25, 25)] +STUB_DISK_USAGE = [ + namedtuple("usage", "total used free percent")(1000, 500, 500, 50), + namedtuple("usage", "total used free percent")(100, 75, 25, 25), +] -WINDOWS_STUB_DISK_USAGE = namedtuple('usage', - 'total used free percent')(1000, - 500, - 500, - 50) +WINDOWS_STUB_DISK_USAGE = namedtuple("usage", "total used free percent")( + 1000, 500, 500, 50 +) class DiskUsageBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.adb - ''' + """ def setup_loader_modules(self): return {} @@ -61,135 +54,144 @@ class DiskUsageBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = diskusage.validate(config) - self.assertEqual(ret, (False, 'Configuration for diskusage beacon must' - ' be a list.')) + self.assertEqual( + ret, (False, "Configuration for diskusage beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = diskusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) def test_diskusage_match(self): disk_usage_mock = Mock(side_effect=STUB_DISK_USAGE) - with patch('salt.utils.platform.is_windows', - MagicMock(return_value=False)),\ - patch('psutil.disk_partitions', - MagicMock(return_value=STUB_DISK_PARTITION)), \ - patch('psutil.disk_usage', disk_usage_mock): - config = [{'/': '50%'}] + with patch( + "salt.utils.platform.is_windows", MagicMock(return_value=False) + ), patch( + "psutil.disk_partitions", MagicMock(return_value=STUB_DISK_PARTITION) + ), patch( + "psutil.disk_usage", disk_usage_mock + ): + config = [{"/": "50%"}] ret = diskusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = diskusage.beacon(config) - self.assertEqual(ret, [{'diskusage': 50, 'mount': '/'}]) + self.assertEqual(ret, [{"diskusage": 50, "mount": "/"}]) def test_diskusage_nomatch(self): disk_usage_mock = Mock(side_effect=STUB_DISK_USAGE) - with patch('salt.utils.platform.is_windows', - MagicMock(return_value=False)),\ - patch('psutil.disk_partitions', - MagicMock(return_value=STUB_DISK_PARTITION)), \ - patch('psutil.disk_usage', disk_usage_mock): - config = [{'/': '70%'}] + with patch( + "salt.utils.platform.is_windows", MagicMock(return_value=False) + ), patch( + "psutil.disk_partitions", MagicMock(return_value=STUB_DISK_PARTITION) + ), patch( + "psutil.disk_usage", disk_usage_mock + ): + config = [{"/": "70%"}] ret = diskusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = diskusage.beacon(config) - self.assertNotEqual(ret, [{'diskusage': 50, 'mount': '/'}]) + self.assertNotEqual(ret, [{"diskusage": 50, "mount": "/"}]) def test_diskusage_match_regex(self): disk_usage_mock = Mock(side_effect=STUB_DISK_USAGE) - with patch('salt.utils.platform.is_windows', - MagicMock(return_value=False)),\ - patch('psutil.disk_partitions', - MagicMock(return_value=STUB_DISK_PARTITION)), \ - patch('psutil.disk_usage', disk_usage_mock): - config = [{r'^\/': '50%'}] + with patch( + "salt.utils.platform.is_windows", MagicMock(return_value=False) + ), patch( + "psutil.disk_partitions", MagicMock(return_value=STUB_DISK_PARTITION) + ), patch( + "psutil.disk_usage", disk_usage_mock + ): + config = [{r"^\/": "50%"}] ret = diskusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = diskusage.beacon(config) - self.assertEqual(ret, [{'diskusage': 50, 'mount': '/'}]) + self.assertEqual(ret, [{"diskusage": 50, "mount": "/"}]) def test_diskusage_windows_single_slash(self): - r''' + r""" This tests new behavior (C:\) - ''' + """ disk_usage_mock = Mock(return_value=WINDOWS_STUB_DISK_USAGE) - with patch('salt.utils.platform.is_windows', - MagicMock(return_value=True)): - with patch('psutil.disk_partitions', - MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION)), \ - patch('psutil.disk_usage', disk_usage_mock): - config = [{'C:\\': '50%'}] + with patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): + with patch( + "psutil.disk_partitions", + MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION), + ), patch("psutil.disk_usage", disk_usage_mock): + config = [{"C:\\": "50%"}] ret = diskusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = diskusage.beacon(config) - self.assertEqual(ret, [{'diskusage': 50, 'mount': 'C:\\'}]) + self.assertEqual(ret, [{"diskusage": 50, "mount": "C:\\"}]) def test_diskusage_windows_double_slash(self): - ''' + """ This tests original behavior (C:\\) - ''' + """ disk_usage_mock = Mock(return_value=WINDOWS_STUB_DISK_USAGE) - with patch('salt.utils.platform.is_windows', - MagicMock(return_value=True)): - with patch('psutil.disk_partitions', - MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION)), \ - patch('psutil.disk_usage', disk_usage_mock): - config = [{'C:\\\\': '50%'}] + with patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): + with patch( + "psutil.disk_partitions", + MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION), + ), patch("psutil.disk_usage", disk_usage_mock): + config = [{"C:\\\\": "50%"}] ret = diskusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = diskusage.beacon(config) - self.assertEqual(ret, [{'diskusage': 50, 'mount': 'C:\\'}]) + self.assertEqual(ret, [{"diskusage": 50, "mount": "C:\\"}]) def test_diskusage_windows_lowercase(self): - r''' + r""" This tests lowercase drive letter (c:\) - ''' + """ disk_usage_mock = Mock(return_value=WINDOWS_STUB_DISK_USAGE) - with patch('salt.utils.platform.is_windows', - MagicMock(return_value=True)): - with patch('psutil.disk_partitions', - MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION)), \ - patch('psutil.disk_usage', disk_usage_mock): - config = [{'c:\\': '50%'}] + with patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): + with patch( + "psutil.disk_partitions", + MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION), + ), patch("psutil.disk_usage", disk_usage_mock): + config = [{"c:\\": "50%"}] ret = diskusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = diskusage.beacon(config) - self.assertEqual(ret, [{'diskusage': 50, 'mount': 'C:\\'}]) + self.assertEqual(ret, [{"diskusage": 50, "mount": "C:\\"}]) def test_diskusage_windows_match_regex(self): disk_usage_mock = Mock(return_value=WINDOWS_STUB_DISK_USAGE) - with patch('salt.utils.platform.is_windows', - MagicMock(return_value=True)): - with patch('psutil.disk_partitions', - MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION)), \ - patch('psutil.disk_usage', disk_usage_mock): - config = [{'^[a-zA-Z]:\\': '50%'}] + with patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): + with patch( + "psutil.disk_partitions", + MagicMock(return_value=WINDOWS_STUB_DISK_PARTITION), + ), patch("psutil.disk_usage", disk_usage_mock): + config = [{"^[a-zA-Z]:\\": "50%"}] ret = diskusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = diskusage.beacon(config) - _expected = [{u'diskusage': 50, u'mount': 'C:\\'}, - {u'diskusage': 50, u'mount': 'D:\\'}] + _expected = [ + {u"diskusage": 50, u"mount": "C:\\"}, + {u"diskusage": 50, u"mount": "D:\\"}, + ] self.assertEqual(ret, _expected) diff --git a/tests/unit/beacons/test_glxinfo.py b/tests/unit/beacons/test_glxinfo.py index 5f356e5bdf1..8311c8cf50b 100644 --- a/tests/unit/beacons/test_glxinfo.py +++ b/tests/unit/beacons/test_glxinfo.py @@ -6,112 +6,111 @@ from __future__ import absolute_import # Salt libs import salt.beacons.glxinfo as glxinfo -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, Mock - # Import test suite libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock, patch + +# Salt testing libs +from tests.support.unit import TestCase class GLXInfoBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.glxinfo - ''' + """ def setup_loader_modules(self): - return {glxinfo: {'last_state': {}}} + return {glxinfo: {"last_state": {}}} def test_no_glxinfo_command(self): - with patch('salt.utils.path.which') as mock: + with patch("salt.utils.path.which") as mock: mock.return_value = None ret = glxinfo.__virtual__() - mock.assert_called_once_with('glxinfo') + mock.assert_called_once_with("glxinfo") self.assertFalse(ret) def test_with_glxinfo_command(self): - with patch('salt.utils.path.which') as mock: - mock.return_value = '/usr/bin/glxinfo' + with patch("salt.utils.path.which") as mock: + mock.return_value = "/usr/bin/glxinfo" ret = glxinfo.__virtual__() - mock.assert_called_once_with('glxinfo') - self.assertEqual(ret, 'glxinfo') + mock.assert_called_once_with("glxinfo") + self.assertEqual(ret, "glxinfo") def test_non_list_config(self): config = {} ret = glxinfo.validate(config) - self.assertEqual(ret, (False, 'Configuration for glxinfo ' - 'beacon must be a list.')) + self.assertEqual( + ret, (False, "Configuration for glxinfo beacon must be a list.") + ) def test_no_user(self): - config = [{'screen_event': True}] + config = [{"screen_event": True}] - _expected = (False, 'Configuration for glxinfo beacon must ' - 'include a user as glxinfo is not ' - 'available to root.') + _expected = ( + False, + "Configuration for glxinfo beacon must " + "include a user as glxinfo is not " + "available to root.", + ) ret = glxinfo.validate(config) self.assertEqual(ret, _expected) def test_screen_state(self): - config = [{'screen_event': True, - 'user': 'frank'}] + config = [{"screen_event": True, "user": "frank"}] mock = Mock(return_value=0) - with patch.dict(glxinfo.__salt__, {'cmd.retcode': mock}): + with patch.dict(glxinfo.__salt__, {"cmd.retcode": mock}): ret = glxinfo.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = glxinfo.beacon(config) - self.assertEqual(ret, [{'tag': 'screen_event', - 'screen_available': True}]) - mock.assert_called_once_with('DISPLAY=:0 glxinfo', - runas='frank', python_shell=True) + self.assertEqual(ret, [{"tag": "screen_event", "screen_available": True}]) + mock.assert_called_once_with( + "DISPLAY=:0 glxinfo", runas="frank", python_shell=True + ) def test_screen_state_missing(self): - config = [{'screen_event': True, 'user': 'frank'}] + config = [{"screen_event": True, "user": "frank"}] mock = Mock(return_value=255) - with patch.dict(glxinfo.__salt__, {'cmd.retcode': mock}): + with patch.dict(glxinfo.__salt__, {"cmd.retcode": mock}): ret = glxinfo.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = glxinfo.beacon(config) - self.assertEqual(ret, [{'tag': 'screen_event', - 'screen_available': False}]) + self.assertEqual(ret, [{"tag": "screen_event", "screen_available": False}]) def test_screen_state_no_repeat(self): - config = [{'screen_event': True, 'user': 'frank'}] + config = [{"screen_event": True, "user": "frank"}] mock = Mock(return_value=255) - with patch.dict(glxinfo.__salt__, {'cmd.retcode': mock}): + with patch.dict(glxinfo.__salt__, {"cmd.retcode": mock}): ret = glxinfo.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = glxinfo.beacon(config) - self.assertEqual(ret, [{'tag': 'screen_event', - 'screen_available': False}]) + self.assertEqual(ret, [{"tag": "screen_event", "screen_available": False}]) ret = glxinfo.beacon(config) self.assertEqual(ret, []) def test_screen_state_change(self): - config = [{'screen_event': True, 'user': 'frank'}] + config = [{"screen_event": True, "user": "frank"}] mock = Mock(side_effect=[255, 0]) - with patch.dict(glxinfo.__salt__, {'cmd.retcode': mock}): + with patch.dict(glxinfo.__salt__, {"cmd.retcode": mock}): ret = glxinfo.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = glxinfo.beacon(config) - self.assertEqual(ret, [{'tag': 'screen_event', - 'screen_available': False}]) + self.assertEqual(ret, [{"tag": "screen_event", "screen_available": False}]) ret = glxinfo.beacon(config) - self.assertEqual(ret, [{'tag': 'screen_event', - 'screen_available': True}]) + self.assertEqual(ret, [{"tag": "screen_event", "screen_available": True}]) diff --git a/tests/unit/beacons/test_haproxy.py b/tests/unit/beacons/test_haproxy.py index fb608c92f29..2d4c45c3e5e 100644 --- a/tests/unit/beacons/test_haproxy.py +++ b/tests/unit/beacons/test_haproxy.py @@ -3,75 +3,67 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.haproxy as haproxy +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Salt testing libs +from tests.support.unit import TestCase class HAProxyBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.haproxy - ''' + """ def setup_loader_modules(self): - return { - haproxy: { - '__context__': {}, - '__salt__': {}, - } - } + return {haproxy: {"__context__": {}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = haproxy.validate(config) - self.assertEqual(ret, (False, 'Configuration for haproxy beacon must' - ' be a list.')) + self.assertEqual( + ret, (False, "Configuration for haproxy beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = haproxy.validate(config) - self.assertEqual(ret, (False, 'Configuration for haproxy beacon ' - 'requires backends.')) + self.assertEqual( + ret, (False, "Configuration for haproxy beacon requires backends.") + ) def test_no_servers(self): - config = [{'backends': {'www-backend': {'threshold': 45}}}] + config = [{"backends": {"www-backend": {"threshold": 45}}}] ret = haproxy.validate(config) - self.assertEqual(ret, (False, 'Backends for haproxy ' - 'beacon require servers.')) + self.assertEqual(ret, (False, "Backends for haproxy beacon require servers.")) def test_threshold_reached(self): - config = [{'backends': {'www-backend': {'threshold': 45, - 'servers': ['web1'] - }}}] + config = [{"backends": {"www-backend": {"threshold": 45, "servers": ["web1"]}}}] ret = haproxy.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) mock = MagicMock(return_value=46) - with patch.dict(haproxy.__salt__, {'haproxy.get_sessions': mock}): + with patch.dict(haproxy.__salt__, {"haproxy.get_sessions": mock}): ret = haproxy.beacon(config) - self.assertEqual(ret, [{'threshold': 45, - 'scur': 46, - 'server': 'web1'}]) + self.assertEqual(ret, [{"threshold": 45, "scur": 46, "server": "web1"}]) def test_threshold_not_reached(self): - config = [{'backends': {'www-backend': {'threshold': 100, - 'servers': ['web1'] - }}}] + config = [ + {"backends": {"www-backend": {"threshold": 100, "servers": ["web1"]}}} + ] ret = haproxy.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) mock = MagicMock(return_value=50) - with patch.dict(haproxy.__salt__, {'haproxy.get_sessions': mock}): + with patch.dict(haproxy.__salt__, {"haproxy.get_sessions": mock}): ret = haproxy.beacon(config) self.assertEqual(ret, []) diff --git a/tests/unit/beacons/test_inotify.py b/tests/unit/beacons/test_inotify.py index 41b6f847576..d1917b50637 100644 --- a/tests/unit/beacons/test_inotify.py +++ b/tests/unit/beacons/test_inotify.py @@ -2,6 +2,8 @@ # Python libs from __future__ import absolute_import + +import logging import os import shutil import tempfile @@ -9,26 +11,28 @@ import tempfile # Salt libs import salt.utils.files from salt.beacons import inotify +from tests.support.mixins import LoaderModuleMockMixin # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase, skipIf + # Third-party libs try: import pyinotify # pylint: disable=unused-import + HAS_PYINOTIFY = True except ImportError: HAS_PYINOTIFY = False -import logging + log = logging.getLogger(__name__) -@skipIf(not HAS_PYINOTIFY, 'pyinotify is not available') +@skipIf(not HAS_PYINOTIFY, "pyinotify is not available") class INotifyBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.inotify - ''' + """ def setup_loader_modules(self): return {inotify: {}} @@ -44,98 +48,105 @@ class INotifyBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = inotify.validate(config) - self.assertEqual(ret, (False, 'Configuration for inotify beacon must' - ' be a list.')) + self.assertEqual( + ret, (False, "Configuration for inotify beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = inotify.validate(config) - _expected = (False, 'Configuration for inotify beacon must include files.') + _expected = (False, "Configuration for inotify beacon must include files.") self.assertEqual(ret, _expected) def test_files_none_config(self): - config = [{'files': None}] + config = [{"files": None}] ret = inotify.validate(config) - _expected = (False, 'Configuration for inotify beacon invalid, ' - 'files must be a dict.') + _expected = ( + False, + "Configuration for inotify beacon invalid, files must be a dict.", + ) self.assertEqual(ret, _expected) def test_files_list_config(self): - config = [{'files': [{u'/importantfile': {u'mask': [u'modify']}}]}] + config = [{"files": [{u"/importantfile": {u"mask": [u"modify"]}}]}] ret = inotify.validate(config) - _expected = (False, 'Configuration for inotify beacon invalid, ' - 'files must be a dict.') + _expected = ( + False, + "Configuration for inotify beacon invalid, files must be a dict.", + ) self.assertEqual(ret, _expected) def test_file_open(self): path = os.path.realpath(__file__) - config = [{'files': {path: {'mask': ['open']}}}] + config = [{"files": {path: {"mask": ["open"]}}}] ret = inotify.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = inotify.beacon(config) self.assertEqual(ret, []) - with salt.utils.files.fopen(path, 'r') as f: + with salt.utils.files.fopen(path, "r") as f: pass ret = inotify.beacon(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], path) - self.assertEqual(ret[0]['change'], 'IN_OPEN') + self.assertEqual(ret[0]["path"], path) + self.assertEqual(ret[0]["change"], "IN_OPEN") def test_dir_no_auto_add(self): - config = [{'files': {self.tmpdir: {'mask': ['create']}}}] + config = [{"files": {self.tmpdir: {"mask": ["create"]}}}] ret = inotify.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = inotify.beacon(config) self.assertEqual(ret, []) - fp = os.path.join(self.tmpdir, 'tmpfile') - with salt.utils.files.fopen(fp, 'w') as f: + fp = os.path.join(self.tmpdir, "tmpfile") + with salt.utils.files.fopen(fp, "w") as f: pass ret = inotify.beacon(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], fp) - self.assertEqual(ret[0]['change'], 'IN_CREATE') - with salt.utils.files.fopen(fp, 'r') as f: + self.assertEqual(ret[0]["path"], fp) + self.assertEqual(ret[0]["change"], "IN_CREATE") + with salt.utils.files.fopen(fp, "r") as f: pass ret = inotify.beacon(config) self.assertEqual(ret, []) def test_dir_auto_add(self): - config = [{'files': {self.tmpdir: {'mask': ['create', 'open'], 'auto_add': True}}}] + config = [ + {"files": {self.tmpdir: {"mask": ["create", "open"], "auto_add": True}}} + ] ret = inotify.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = inotify.beacon(config) self.assertEqual(ret, []) - fp = os.path.join(self.tmpdir, 'tmpfile') - with salt.utils.files.fopen(fp, 'w') as f: + fp = os.path.join(self.tmpdir, "tmpfile") + with salt.utils.files.fopen(fp, "w") as f: pass ret = inotify.beacon(config) self.assertEqual(len(ret), 2) - self.assertEqual(ret[0]['path'], fp) - self.assertEqual(ret[0]['change'], 'IN_CREATE') - self.assertEqual(ret[1]['path'], fp) - self.assertEqual(ret[1]['change'], 'IN_OPEN') - with salt.utils.files.fopen(fp, 'r') as f: + self.assertEqual(ret[0]["path"], fp) + self.assertEqual(ret[0]["change"], "IN_CREATE") + self.assertEqual(ret[1]["path"], fp) + self.assertEqual(ret[1]["change"], "IN_OPEN") + with salt.utils.files.fopen(fp, "r") as f: pass ret = inotify.beacon(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], fp) - self.assertEqual(ret[0]['change'], 'IN_OPEN') + self.assertEqual(ret[0]["path"], fp) + self.assertEqual(ret[0]["change"], "IN_OPEN") def test_dir_recurse(self): - dp1 = os.path.join(self.tmpdir, 'subdir1') + dp1 = os.path.join(self.tmpdir, "subdir1") os.mkdir(dp1) - dp2 = os.path.join(dp1, 'subdir2') + dp2 = os.path.join(dp1, "subdir2") os.mkdir(dp2) - fp = os.path.join(dp2, 'tmpfile') - with salt.utils.files.fopen(fp, 'w') as f: + fp = os.path.join(dp2, "tmpfile") + with salt.utils.files.fopen(fp, "w") as f: pass - config = [{'files': {self.tmpdir: {'mask': ['open'], 'recurse': True}}}] + config = [{"files": {self.tmpdir: {"mask": ["open"], "recurse": True}}}] ret = inotify.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = inotify.beacon(config) self.assertEqual(ret, []) @@ -143,63 +154,85 @@ class INotifyBeaconTestCase(TestCase, LoaderModuleMockMixin): pass ret = inotify.beacon(config) self.assertEqual(len(ret), 3) - self.assertEqual(ret[0]['path'], dp1) - self.assertEqual(ret[0]['change'], 'IN_OPEN|IN_ISDIR') - self.assertEqual(ret[1]['path'], dp2) - self.assertEqual(ret[1]['change'], 'IN_OPEN|IN_ISDIR') - self.assertEqual(ret[2]['path'], fp) - self.assertEqual(ret[2]['change'], 'IN_OPEN') + self.assertEqual(ret[0]["path"], dp1) + self.assertEqual(ret[0]["change"], "IN_OPEN|IN_ISDIR") + self.assertEqual(ret[1]["path"], dp2) + self.assertEqual(ret[1]["change"], "IN_OPEN|IN_ISDIR") + self.assertEqual(ret[2]["path"], fp) + self.assertEqual(ret[2]["change"], "IN_OPEN") def test_dir_recurse_auto_add(self): - dp1 = os.path.join(self.tmpdir, 'subdir1') + dp1 = os.path.join(self.tmpdir, "subdir1") os.mkdir(dp1) - config = [{'files': {self.tmpdir: {'mask': ['create', 'delete'], - 'recurse': True, - 'auto_add': True}}}] + config = [ + { + "files": { + self.tmpdir: { + "mask": ["create", "delete"], + "recurse": True, + "auto_add": True, + } + } + } + ] ret = inotify.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = inotify.beacon(config) self.assertEqual(ret, []) - dp2 = os.path.join(dp1, 'subdir2') + dp2 = os.path.join(dp1, "subdir2") os.mkdir(dp2) ret = inotify.beacon(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], dp2) - self.assertEqual(ret[0]['change'], 'IN_CREATE|IN_ISDIR') - fp = os.path.join(dp2, 'tmpfile') - with salt.utils.files.fopen(fp, 'w') as f: + self.assertEqual(ret[0]["path"], dp2) + self.assertEqual(ret[0]["change"], "IN_CREATE|IN_ISDIR") + fp = os.path.join(dp2, "tmpfile") + with salt.utils.files.fopen(fp, "w") as f: pass ret = inotify.beacon(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], fp) - self.assertEqual(ret[0]['change'], 'IN_CREATE') + self.assertEqual(ret[0]["path"], fp) + self.assertEqual(ret[0]["change"], "IN_CREATE") os.remove(fp) ret = inotify.beacon(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], fp) - self.assertEqual(ret[0]['change'], 'IN_DELETE') + self.assertEqual(ret[0]["path"], fp) + self.assertEqual(ret[0]["change"], "IN_DELETE") def test_multi_files_exclude(self): - dp1 = os.path.join(self.tmpdir, 'subdir1') - dp2 = os.path.join(self.tmpdir, 'subdir2') + dp1 = os.path.join(self.tmpdir, "subdir1") + dp2 = os.path.join(self.tmpdir, "subdir2") os.mkdir(dp1) os.mkdir(dp2) - _exclude1 = '{0}/subdir1/*tmpfile*$'.format(self.tmpdir) - _exclude2 = '{0}/subdir2/*filetmp*$'.format(self.tmpdir) - config = [{'files': {dp1: {'mask': ['create', 'delete'], - 'recurse': True, - 'exclude': [{_exclude1: {'regex': True}}], - 'auto_add': True}}}, - {'files': {dp2: {'mask': ['create', 'delete'], - 'recurse': True, - 'exclude': [{_exclude2: {'regex': True}}], - 'auto_add': True}}}] + _exclude1 = "{0}/subdir1/*tmpfile*$".format(self.tmpdir) + _exclude2 = "{0}/subdir2/*filetmp*$".format(self.tmpdir) + config = [ + { + "files": { + dp1: { + "mask": ["create", "delete"], + "recurse": True, + "exclude": [{_exclude1: {"regex": True}}], + "auto_add": True, + } + } + }, + { + "files": { + dp2: { + "mask": ["create", "delete"], + "recurse": True, + "exclude": [{_exclude2: {"regex": True}}], + "auto_add": True, + } + } + }, + ] ret = inotify.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - fp = os.path.join(dp1, 'tmpfile') - with salt.utils.files.fopen(fp, 'w') as f: + fp = os.path.join(dp1, "tmpfile") + with salt.utils.files.fopen(fp, "w") as f: pass ret = inotify.beacon(config) self.assertEqual(len(ret), 0) @@ -207,15 +240,15 @@ class INotifyBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = inotify.beacon(config) self.assertEqual(len(ret), 0) - fp = os.path.join(dp2, 'tmpfile') - with salt.utils.files.fopen(fp, 'w') as f: + fp = os.path.join(dp2, "tmpfile") + with salt.utils.files.fopen(fp, "w") as f: pass ret = inotify.beacon(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], fp) - self.assertEqual(ret[0]['change'], 'IN_CREATE') + self.assertEqual(ret[0]["path"], fp) + self.assertEqual(ret[0]["change"], "IN_CREATE") os.remove(fp) ret = inotify.beacon(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], fp) - self.assertEqual(ret[0]['change'], 'IN_DELETE') + self.assertEqual(ret[0]["path"], fp) + self.assertEqual(ret[0]["change"], "IN_DELETE") diff --git a/tests/unit/beacons/test_journald.py b/tests/unit/beacons/test_journald.py index 94bb411be1e..874acece607 100644 --- a/tests/unit/beacons/test_journald.py +++ b/tests/unit/beacons/test_journald.py @@ -2,58 +2,57 @@ # Python libs from __future__ import absolute_import -import datetime -from uuid import UUID -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import Mock -from tests.support.mixins import LoaderModuleMockMixin +import datetime +import logging +from uuid import UUID # Salt libs import salt.beacons.journald as journald import salt.utils.data +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock + +# Salt testing libs +from tests.support.unit import TestCase -import logging log = logging.getLogger(__name__) -_STUB_JOURNALD_ENTRY = {'_BOOT_ID': UUID('ad3915a5-9008-4fec-a635-140606525497'), - '__MONOTONIC_TIMESTAMP': (datetime.timedelta(4, 28586, 703069), - UUID('ad3915a5-9008-4fec-a635-140606525497')), - '_AUDIT_LOGINUID': 1000, 'SYSLOG_FACILITY': 10, - '_SYSTEMD_SLICE': u'system.slice', - '_GID': 0, - '__REALTIME_TIMESTAMP': datetime.datetime(2017, 6, 27, - 20, 8, 16, - 468468), - '_AUDIT_SESSION': 351, 'PRIORITY': 6, - '_TRANSPORT': u'syslog', - '_HOSTNAME': u'hostname', - '_CAP_EFFECTIVE': u'3fffffffff', - '_SYSTEMD_UNIT': u'ssh.service', - '_MACHINE_ID': UUID('14fab5bb-228d-414b-bdf4-cbc62cb7ba54'), - '_PID': 15091, - 'SYSLOG_IDENTIFIER': u'sshd', - '_SOURCE_REALTIME_TIMESTAMP': datetime.datetime(2017, - 6, - 27, - 20, - 8, - 16, - 468454), - '_SYSTEMD_CGROUP': u'/system.slice/ssh.service', - '__CURSOR': 's=7711ee01b03446309383870171dd5839;i=a74e;b=ad3915a590084feca635140606525497;m=571f43f8 dd;t=552fc7ed1cdf4;x=4ca0a3d4f1905736', - '_COMM': u'sshd', - '_CMDLINE': u'sshd: gareth [priv]', - '_SYSTEMD_INVOCATION_ID': u'38a5d5aad292426d93bfaab72a69c2ab', - '_EXE': u'/usr/sbin/sshd', - '_UID': 0, - 'SYSLOG_PID': 15091, - 'MESSAGE': u'pam_unix(sshd:session): session opened for user username by (uid=0)'} +_STUB_JOURNALD_ENTRY = { + "_BOOT_ID": UUID("ad3915a5-9008-4fec-a635-140606525497"), + "__MONOTONIC_TIMESTAMP": ( + datetime.timedelta(4, 28586, 703069), + UUID("ad3915a5-9008-4fec-a635-140606525497"), + ), + "_AUDIT_LOGINUID": 1000, + "SYSLOG_FACILITY": 10, + "_SYSTEMD_SLICE": u"system.slice", + "_GID": 0, + "__REALTIME_TIMESTAMP": datetime.datetime(2017, 6, 27, 20, 8, 16, 468468), + "_AUDIT_SESSION": 351, + "PRIORITY": 6, + "_TRANSPORT": u"syslog", + "_HOSTNAME": u"hostname", + "_CAP_EFFECTIVE": u"3fffffffff", + "_SYSTEMD_UNIT": u"ssh.service", + "_MACHINE_ID": UUID("14fab5bb-228d-414b-bdf4-cbc62cb7ba54"), + "_PID": 15091, + "SYSLOG_IDENTIFIER": u"sshd", + "_SOURCE_REALTIME_TIMESTAMP": datetime.datetime(2017, 6, 27, 20, 8, 16, 468454), + "_SYSTEMD_CGROUP": u"/system.slice/ssh.service", + "__CURSOR": "s=7711ee01b03446309383870171dd5839;i=a74e;b=ad3915a590084feca635140606525497;m=571f43f8 dd;t=552fc7ed1cdf4;x=4ca0a3d4f1905736", + "_COMM": u"sshd", + "_CMDLINE": u"sshd: gareth [priv]", + "_SYSTEMD_INVOCATION_ID": u"38a5d5aad292426d93bfaab72a69c2ab", + "_EXE": u"/usr/sbin/sshd", + "_UID": 0, + "SYSLOG_PID": 15091, + "MESSAGE": u"pam_unix(sshd:session): session opened for user username by (uid=0)", +} class SystemdJournaldMock(Mock): - ''' Request Mock''' + """ Request Mock""" returned_once = False @@ -75,17 +74,15 @@ SYSTEMD_MOCK = SystemdJournaldMock() class JournaldBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.journald - ''' + """ def setup_loader_modules(self): return { journald: { - '__context__': { - 'systemd.journald': SYSTEMD_MOCK, - }, - '__salt__': {}, + "__context__": {"systemd.journald": SYSTEMD_MOCK}, + "__salt__": {}, } } @@ -94,26 +91,26 @@ class JournaldBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = journald.validate(config) - self.assertEqual(ret, (False, 'Configuration for journald beacon must' - ' be a list.')) + self.assertEqual( + ret, (False, "Configuration for journald beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = journald.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) def test_journald_match(self): - config = [{'services': {'sshd': {'SYSLOG_IDENTIFIER': 'sshd', - 'PRIORITY': 6}}}] + config = [{"services": {"sshd": {"SYSLOG_IDENTIFIER": "sshd", "PRIORITY": 6}}}] ret = journald.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) _expected_return = salt.utils.data.simple_types_filter(_STUB_JOURNALD_ENTRY) - _expected_return['tag'] = 'sshd' + _expected_return["tag"] = "sshd" ret = journald.beacon(config) self.assertEqual(ret, [_expected_return]) diff --git a/tests/unit/beacons/test_load.py b/tests/unit/beacons/test_load.py index 2d8df48758b..a98f3c46748 100644 --- a/tests/unit/beacons/test_load.py +++ b/tests/unit/beacons/test_load.py @@ -3,63 +3,59 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import LoaderModuleMockMixin +import logging # Salt libs import salt.beacons.load as load import salt.utils.platform +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Salt testing libs +from tests.support.unit import TestCase, skipIf -import logging log = logging.getLogger(__name__) class LoadBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.load - ''' + """ def setup_loader_modules(self): - return { - load: { - '__context__': {}, - '__salt__': {}, - } - } + return {load: {"__context__": {}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = load.validate(config) - self.assertEqual(ret, (False, 'Configuration for load beacon must' - ' be a list.')) + self.assertEqual(ret, (False, "Configuration for load beacon must be a list.")) def test_empty_config(self): config = [{}] ret = load.validate(config) - self.assertEqual(ret, (False, 'Averages configuration is required' - ' for load beacon.')) + self.assertEqual( + ret, (False, "Averages configuration is required for load beacon.") + ) - @skipIf(salt.utils.platform.is_windows(), - 'os.getloadavg not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "os.getloadavg not available on Windows") def test_load_match(self): - with patch('os.getloadavg', - MagicMock(return_value=(1.82, 1.84, 1.56))): - config = [{'averages': {'1m': [0.0, 2.0], - '5m': [0.0, 1.5], - '15m': [0.0, 1.0]}, - 'emitatstartup': True, - 'onchangeonly': False}] + with patch("os.getloadavg", MagicMock(return_value=(1.82, 1.84, 1.56))): + config = [ + { + "averages": {"1m": [0.0, 2.0], "5m": [0.0, 1.5], "15m": [0.0, 1.0]}, + "emitatstartup": True, + "onchangeonly": False, + } + ] ret = load.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected_return = [{'1m': 1.82, '5m': 1.84, '15m': 1.56}] + _expected_return = [{"1m": 1.82, "5m": 1.84, "15m": 1.56}] ret = load.beacon(config) self.assertEqual(ret, _expected_return) diff --git a/tests/unit/beacons/test_log_beacon.py b/tests/unit/beacons/test_log_beacon.py index 6e11d9a7165..cd6bf93a97c 100644 --- a/tests/unit/beacons/test_log_beacon.py +++ b/tests/unit/beacons/test_log_beacon.py @@ -3,67 +3,67 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, mock_open -from tests.support.mixins import LoaderModuleMockMixin +import logging # Salt libs import salt.beacons.log_beacon as log_beacon +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import mock_open, patch + +# Salt testing libs +from tests.support.unit import TestCase -import logging log = logging.getLogger(__name__) -_STUB_LOG_ENTRY = 'Jun 29 12:58:51 hostname sshd[6536]: ' \ - 'pam_unix(sshd:session): session opened ' \ - 'for user username by (uid=0)\n' +_STUB_LOG_ENTRY = ( + "Jun 29 12:58:51 hostname sshd[6536]: " + "pam_unix(sshd:session): session opened " + "for user username by (uid=0)\n" +) class LogBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.log - ''' + """ def setup_loader_modules(self): - return { - log_beacon: { - '__context__': {'log.loc': 2}, - '__salt__': {}, - } - } + return {log_beacon: {"__context__": {"log.loc": 2}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = log_beacon.validate(config) - self.assertEqual(ret, (False, 'Configuration for log beacon must' - ' be a list.')) + self.assertEqual(ret, (False, "Configuration for log beacon must be a list.")) def test_empty_config(self): config = [{}] ret = log_beacon.validate(config) - self.assertEqual(ret, (False, 'Configuration for log beacon ' - 'must contain file option.')) + self.assertEqual( + ret, (False, "Configuration for log beacon must contain file option.") + ) def test_log_match(self): - with patch('salt.utils.files.fopen', - mock_open(read_data=_STUB_LOG_ENTRY)): - config = [{'file': '/var/log/auth.log', - 'tags': {'sshd': {'regex': '.*sshd.*'}} - }] + with patch("salt.utils.files.fopen", mock_open(read_data=_STUB_LOG_ENTRY)): + config = [ + {"file": "/var/log/auth.log", "tags": {"sshd": {"regex": ".*sshd.*"}}} + ] ret = log_beacon.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected_return = [{'error': '', - 'match': 'yes', - 'raw': _STUB_LOG_ENTRY.rstrip('\n'), - 'tag': 'sshd' - }] + _expected_return = [ + { + "error": "", + "match": "yes", + "raw": _STUB_LOG_ENTRY.rstrip("\n"), + "tag": "sshd", + } + ] ret = log_beacon.beacon(config) self.assertEqual(ret, _expected_return) diff --git a/tests/unit/beacons/test_memusage.py b/tests/unit/beacons/test_memusage.py index faa81dcb671..4fee231795b 100644 --- a/tests/unit/beacons/test_memusage.py +++ b/tests/unit/beacons/test_memusage.py @@ -2,26 +2,37 @@ # Python libs from __future__ import absolute_import -from collections import namedtuple -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import LoaderModuleMockMixin +from collections import namedtuple # Salt libs import salt.beacons.memusage as memusage +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch -STUB_MEMORY_USAGE = namedtuple('vmem', 'total available percent used free active inactive buffers cached shared')( - 15722012672, 9329594368, 40.7, 5137018880, - 4678086656, 6991405056, 2078953472, - 1156378624, 4750528512, 898908160) +# Salt testing libs +from tests.support.unit import TestCase + +STUB_MEMORY_USAGE = namedtuple( + "vmem", "total available percent used free active inactive buffers cached shared" +)( + 15722012672, + 9329594368, + 40.7, + 5137018880, + 4678086656, + 6991405056, + 2078953472, + 1156378624, + 4750528512, + 898908160, +) class MemUsageBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.memusage - ''' + """ def setup_loader_modules(self): return {} @@ -31,39 +42,39 @@ class MemUsageBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = memusage.validate(config) - self.assertEqual(ret, (False, 'Configuration for memusage beacon must' - ' be a list.')) + self.assertEqual( + ret, (False, "Configuration for memusage beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = memusage.validate(config) - self.assertEqual(ret, (False, 'Configuration for memusage beacon ' - 'requires percent.')) + self.assertEqual( + ret, (False, "Configuration for memusage beacon requires percent.") + ) def test_memusage_match(self): - with patch('psutil.virtual_memory', - MagicMock(return_value=STUB_MEMORY_USAGE)): + with patch("psutil.virtual_memory", MagicMock(return_value=STUB_MEMORY_USAGE)): - config = [{'percent': '40%'}, {'interval': 30}] + config = [{"percent": "40%"}, {"interval": 30}] ret = memusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = memusage.beacon(config) - self.assertEqual(ret, [{'memusage': 40.7}]) + self.assertEqual(ret, [{"memusage": 40.7}]) def test_memusage_nomatch(self): - with patch('psutil.virtual_memory', - MagicMock(return_value=STUB_MEMORY_USAGE)): + with patch("psutil.virtual_memory", MagicMock(return_value=STUB_MEMORY_USAGE)): - config = [{'percent': '70%'}] + config = [{"percent": "70%"}] ret = memusage.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = memusage.beacon(config) - self.assertNotEqual(ret, [{'memusage': 50}]) + self.assertNotEqual(ret, [{"memusage": 50}]) diff --git a/tests/unit/beacons/test_network_info.py b/tests/unit/beacons/test_network_info.py index a8726455e14..5c99a591b69 100644 --- a/tests/unit/beacons/test_network_info.py +++ b/tests/unit/beacons/test_network_info.py @@ -2,113 +2,144 @@ # Python libs from __future__ import absolute_import -from collections import namedtuple -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import LoaderModuleMockMixin +import logging +from collections import namedtuple # Salt libs import salt.beacons.network_info as network_info +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Salt testing libs +from tests.support.unit import TestCase -import logging log = logging.getLogger(__name__) -STUB_NET_IO_COUNTERS = {'eth0': namedtuple('snetio', - 'bytes_sent bytes_recv \ +STUB_NET_IO_COUNTERS = { + "eth0": namedtuple( + "snetio", + "bytes_sent bytes_recv \ packets_sent packets_recv \ errin errout \ dropin \ - dropout')(93662618, 914626664, - 465694, 903802, 0, - 0, 0, 0)} + dropout", + )(93662618, 914626664, 465694, 903802, 0, 0, 0, 0) +} class NetworkInfoBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.network_info - ''' + """ def setup_loader_modules(self): - return { - network_info: { - '__context__': {}, - '__salt__': {}, - } - } + return {network_info: {"__context__": {}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = network_info.validate(config) - self.assertEqual(ret, (False, 'Configuration for network_info beacon' - ' must be a list.')) + self.assertEqual( + ret, (False, "Configuration for network_info beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = network_info.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) def test_network_info_equal(self): - with patch('salt.utils.psutil_compat.net_io_counters', - MagicMock(return_value=STUB_NET_IO_COUNTERS)): - config = [{'interfaces': {'eth0': {'type': 'equal', - 'bytes_sent': 914626664, - 'bytes_recv': 93662618, - 'packets_sent': 465694, - 'packets_recv': 903802, - 'errin': 0, - 'errout': 0, - 'dropin': 0, - 'dropout': 0}}}] + with patch( + "salt.utils.psutil_compat.net_io_counters", + MagicMock(return_value=STUB_NET_IO_COUNTERS), + ): + config = [ + { + "interfaces": { + "eth0": { + "type": "equal", + "bytes_sent": 914626664, + "bytes_recv": 93662618, + "packets_sent": 465694, + "packets_recv": 903802, + "errin": 0, + "errout": 0, + "dropin": 0, + "dropout": 0, + } + } + } + ] ret = network_info.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected_return = [{'interface': 'eth0', - 'network_info': {'bytes_recv': 914626664, - 'bytes_sent': 93662618, - 'dropin': 0, - 'dropout': 0, - 'errin': 0, - 'errout': 0, - 'packets_recv': 903802, - 'packets_sent': 465694}}] + _expected_return = [ + { + "interface": "eth0", + "network_info": { + "bytes_recv": 914626664, + "bytes_sent": 93662618, + "dropin": 0, + "dropout": 0, + "errin": 0, + "errout": 0, + "packets_recv": 903802, + "packets_sent": 465694, + }, + } + ] ret = network_info.beacon(config) self.assertEqual(ret, _expected_return) def test_network_info_greater_than(self): - with patch('salt.utils.psutil_compat.net_io_counters', - MagicMock(return_value=STUB_NET_IO_COUNTERS)): - config = [{'interfaces': {'eth0': {'type': 'greater', - 'bytes_sent': 100000, - 'bytes_recv': 100000, - 'packets_sent': 100000, - 'packets_recv': 100000, - 'errin': 0, - 'errout': 0, - 'dropin': 0, - 'dropout': 0}}}] + with patch( + "salt.utils.psutil_compat.net_io_counters", + MagicMock(return_value=STUB_NET_IO_COUNTERS), + ): + config = [ + { + "interfaces": { + "eth0": { + "type": "greater", + "bytes_sent": 100000, + "bytes_recv": 100000, + "packets_sent": 100000, + "packets_recv": 100000, + "errin": 0, + "errout": 0, + "dropin": 0, + "dropout": 0, + } + } + } + ] ret = network_info.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected_return = [{'interface': 'eth0', - 'network_info': {'bytes_recv': 914626664, - 'bytes_sent': 93662618, - 'dropin': 0, - 'dropout': 0, - 'errin': 0, - 'errout': 0, - 'packets_recv': 903802, - 'packets_sent': 465694}}] + _expected_return = [ + { + "interface": "eth0", + "network_info": { + "bytes_recv": 914626664, + "bytes_sent": 93662618, + "dropin": 0, + "dropout": 0, + "errin": 0, + "errout": 0, + "packets_recv": 903802, + "packets_sent": 465694, + }, + } + ] ret = network_info.beacon(config) self.assertEqual(ret, _expected_return) diff --git a/tests/unit/beacons/test_network_settings.py b/tests/unit/beacons/test_network_settings.py index af060dab092..f9932f2c3d9 100644 --- a/tests/unit/beacons/test_network_settings.py +++ b/tests/unit/beacons/test_network_settings.py @@ -3,20 +3,24 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import patch, MagicMock +import logging + +# Salt libs +import salt.beacons.network_settings as network_settings from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Salt testing libs +from tests.support.unit import TestCase, skipIf + try: from pyroute2 import IPDB + HAS_PYROUTE2 = True except ImportError: HAS_PYROUTE2 = False -# Salt libs -import salt.beacons.network_settings as network_settings -import logging log = logging.getLogger(__name__) @@ -30,77 +34,80 @@ class MockIPClass(object): class NetworkSettingsBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.network_settings - ''' + """ def setup_loader_modules(self): - return { - network_settings: { - '__context__': {}, - '__salt__': {}, - } - } + return {network_settings: {"__context__": {}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = network_settings.validate(config) - self.assertEqual(ret, (False, 'Configuration for network_settings' - ' beacon must be a list.')) + self.assertEqual( + ret, (False, "Configuration for network_settings beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = network_settings.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) def test_interface(self): - config = [{'interfaces': {'enp14s0u1u2': {'promiscuity': None}}}] - LAST_STATS = network_settings._copy_interfaces_info({'enp14s0u1u2': {'family': '0', - 'promiscuity': '0', - 'group': '0'}}) + config = [{"interfaces": {"enp14s0u1u2": {"promiscuity": None}}}] + LAST_STATS = network_settings._copy_interfaces_info( + {"enp14s0u1u2": {"family": "0", "promiscuity": "0", "group": "0"}} + ) - NEW_STATS = network_settings._copy_interfaces_info({'enp14s0u1u2': {'family': '0', - 'promiscuity': '1', - 'group': '0'}}) + NEW_STATS = network_settings._copy_interfaces_info( + {"enp14s0u1u2": {"family": "0", "promiscuity": "1", "group": "0"}} + ) ret = network_settings.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - with patch.object(network_settings, 'LAST_STATS', {}), \ - patch.object(network_settings, 'IP', MockIPClass), \ - patch('salt.beacons.network_settings._copy_interfaces_info', - MagicMock(side_effect=[LAST_STATS, NEW_STATS])): + with patch.object(network_settings, "LAST_STATS", {}), patch.object( + network_settings, "IP", MockIPClass + ), patch( + "salt.beacons.network_settings._copy_interfaces_info", + MagicMock(side_effect=[LAST_STATS, NEW_STATS]), + ): ret = network_settings.beacon(config) self.assertEqual(ret, []) ret = network_settings.beacon(config) - _expected = [{'interface': 'enp14s0u1u2', - 'tag': 'enp14s0u1u2', - 'change': {'promiscuity': '1'} - }] + _expected = [ + { + "interface": "enp14s0u1u2", + "tag": "enp14s0u1u2", + "change": {"promiscuity": "1"}, + } + ] self.assertEqual(ret, _expected) def test_interface_no_change(self): - config = [{'interfaces': {'enp14s0u1u2': {'promiscuity': None}}}] - LAST_STATS = network_settings._copy_interfaces_info({'enp14s0u1u2': {'family': '0', - 'promiscuity': '0', - 'group': '0'}}) + config = [{"interfaces": {"enp14s0u1u2": {"promiscuity": None}}}] + LAST_STATS = network_settings._copy_interfaces_info( + {"enp14s0u1u2": {"family": "0", "promiscuity": "0", "group": "0"}} + ) - NEW_STATS = network_settings._copy_interfaces_info({'enp14s0u1u2': {'family': '0', - 'promiscuity': '0', - 'group': '0'}}) + NEW_STATS = network_settings._copy_interfaces_info( + {"enp14s0u1u2": {"family": "0", "promiscuity": "0", "group": "0"}} + ) ret = network_settings.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - with patch.object(network_settings, 'LAST_STATS', {}), \ - patch.object(network_settings, 'IP', MockIPClass), \ - patch('salt.beacons.network_settings._copy_interfaces_info', - MagicMock(side_effect=[LAST_STATS, NEW_STATS])): + with patch.object(network_settings, "LAST_STATS", {}), patch.object( + network_settings, "IP", MockIPClass + ), patch( + "salt.beacons.network_settings._copy_interfaces_info", + MagicMock(side_effect=[LAST_STATS, NEW_STATS]), + ): ret = network_settings.beacon(config) self.assertEqual(ret, []) @@ -108,36 +115,40 @@ class NetworkSettingsBeaconTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(ret, []) def test_wildcard_interface(self): - config = [{'interfaces': {'en*': {'promiscuity': None}}}] - LAST_STATS = network_settings._copy_interfaces_info({'enp14s0u1u2': {'family': '0', - 'promiscuity': '0', - 'group': '0'}}) + config = [{"interfaces": {"en*": {"promiscuity": None}}}] + LAST_STATS = network_settings._copy_interfaces_info( + {"enp14s0u1u2": {"family": "0", "promiscuity": "0", "group": "0"}} + ) - NEW_STATS = network_settings._copy_interfaces_info({'enp14s0u1u2': {'family': '0', - 'promiscuity': '1', - 'group': '0'}}) + NEW_STATS = network_settings._copy_interfaces_info( + {"enp14s0u1u2": {"family": "0", "promiscuity": "1", "group": "0"}} + ) ret = network_settings.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - with patch.object(network_settings, 'LAST_STATS', {}), \ - patch.object(network_settings, 'IP', MockIPClass), \ - patch('salt.beacons.network_settings._copy_interfaces_info', - MagicMock(side_effect=[LAST_STATS, NEW_STATS])): + with patch.object(network_settings, "LAST_STATS", {}), patch.object( + network_settings, "IP", MockIPClass + ), patch( + "salt.beacons.network_settings._copy_interfaces_info", + MagicMock(side_effect=[LAST_STATS, NEW_STATS]), + ): ret = network_settings.beacon(config) self.assertEqual(ret, []) ret = network_settings.beacon(config) - _expected = [{'interface': 'enp14s0u1u2', - 'tag': 'enp14s0u1u2', - 'change': {'promiscuity': '1'} - }] + _expected = [ + { + "interface": "enp14s0u1u2", + "tag": "enp14s0u1u2", + "change": {"promiscuity": "1"}, + } + ] self.assertEqual(ret, _expected) -@skipIf(not HAS_PYROUTE2, 'no pyroute2 installed, skipping') +@skipIf(not HAS_PYROUTE2, "no pyroute2 installed, skipping") class Pyroute2TestCase(TestCase): - def test_interface_dict_fields(self): with IPDB() as ipdb: for attr in network_settings.ATTRS: diff --git a/tests/unit/beacons/test_ps.py b/tests/unit/beacons/test_ps.py index 5e7dc11e418..f8c685fb9a7 100644 --- a/tests/unit/beacons/test_ps.py +++ b/tests/unit/beacons/test_ps.py @@ -3,19 +3,18 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.ps as ps +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch + +# Salt testing libs +from tests.support.unit import TestCase PATCH_OPTS = dict(autospec=True, spec_set=True) class FakeProcess(object): - def __init__(self, _name, pid): self._name = _name self.pid = pid @@ -25,9 +24,9 @@ class FakeProcess(object): class PSBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.[s] - ''' + """ def setup_loader_modules(self): return {} @@ -37,39 +36,47 @@ class PSBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = ps.validate(config) - self.assertEqual(ret, (False, 'Configuration for ps beacon must' - ' be a list.')) + self.assertEqual(ret, (False, "Configuration for ps beacon must be a list.")) def test_empty_config(self): config = [{}] ret = ps.validate(config) - self.assertEqual(ret, (False, 'Configuration for ps ' - 'beacon requires processes.')) + self.assertEqual( + ret, (False, "Configuration for ps beacon requires processes.") + ) def test_ps_running(self): - with patch('salt.utils.psutil_compat.process_iter', **PATCH_OPTS) as mock_process_iter: - mock_process_iter.return_value = [FakeProcess(_name='salt-master', pid=3), - FakeProcess(_name='salt-minion', pid=4)] - config = [{'processes': {'salt-master': 'running'}}] + with patch( + "salt.utils.psutil_compat.process_iter", **PATCH_OPTS + ) as mock_process_iter: + mock_process_iter.return_value = [ + FakeProcess(_name="salt-master", pid=3), + FakeProcess(_name="salt-minion", pid=4), + ] + config = [{"processes": {"salt-master": "running"}}] ret = ps.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = ps.beacon(config) - self.assertEqual(ret, [{'salt-master': 'Running'}]) + self.assertEqual(ret, [{"salt-master": "Running"}]) def test_ps_not_running(self): - with patch('salt.utils.psutil_compat.process_iter', **PATCH_OPTS) as mock_process_iter: - mock_process_iter.return_value = [FakeProcess(_name='salt-master', pid=3), - FakeProcess(_name='salt-minion', pid=4)] - config = [{'processes': {'mysql': 'stopped'}}] + with patch( + "salt.utils.psutil_compat.process_iter", **PATCH_OPTS + ) as mock_process_iter: + mock_process_iter.return_value = [ + FakeProcess(_name="salt-master", pid=3), + FakeProcess(_name="salt-minion", pid=4), + ] + config = [{"processes": {"mysql": "stopped"}}] ret = ps.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = ps.beacon(config) - self.assertEqual(ret, [{'mysql': 'Stopped'}]) + self.assertEqual(ret, [{"mysql": "Stopped"}]) diff --git a/tests/unit/beacons/test_salt_proxy.py b/tests/unit/beacons/test_salt_proxy.py index 320481bfa4f..96f30b6b2fe 100644 --- a/tests/unit/beacons/test_salt_proxy.py +++ b/tests/unit/beacons/test_salt_proxy.py @@ -2,80 +2,84 @@ # Python libs from __future__ import absolute_import -from collections import namedtuple -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import LoaderModuleMockMixin +from collections import namedtuple # Salt libs import salt.beacons.salt_proxy as salt_proxy +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Salt testing libs +from tests.support.unit import TestCase PATCH_OPTS = dict(autospec=True, spec_set=True) -FakeProcess = namedtuple('Process', 'cmdline pid') +FakeProcess = namedtuple("Process", "cmdline pid") class SaltProxyBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.[s] - ''' + """ def setup_loader_modules(self): - return { - salt_proxy: { - '__context__': {}, - '__salt__': {}, - } - } + return {salt_proxy: {"__context__": {}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = salt_proxy.validate(config) - self.assertEqual(ret, (False, 'Configuration for salt_proxy beacon' - ' must be a list.')) + self.assertEqual( + ret, (False, "Configuration for salt_proxy beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = salt_proxy.validate(config) - self.assertEqual(ret, (False, 'Configuration for salt_proxy ' - 'beacon requires proxies.')) + self.assertEqual( + ret, (False, "Configuration for salt_proxy beacon requires proxies.") + ) def test_salt_proxy_running(self): - mock = MagicMock(return_value={'result': True}) - with patch.dict(salt_proxy.__salt__, {'salt_proxy.is_running': mock}): - config = [{'proxies': {'p8000': ''}}] + mock = MagicMock(return_value={"result": True}) + with patch.dict(salt_proxy.__salt__, {"salt_proxy.is_running": mock}): + config = [{"proxies": {"p8000": ""}}] ret = salt_proxy.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = salt_proxy.beacon(config) - self.assertEqual(ret, [{'p8000': 'Proxy p8000 is already running'}]) + self.assertEqual(ret, [{"p8000": "Proxy p8000 is already running"}]) def test_salt_proxy_not_running(self): - is_running_mock = MagicMock(return_value={'result': False}) - configure_mock = MagicMock(return_value={'result': True, - 'changes': {'new': 'Salt Proxy: Started proxy process for p8000', - 'old': []}}) - cmd_run_mock = MagicMock(return_value={'pid': 1000, - 'retcode': 0, - 'stderr': '', - 'stdout': ''}) - with patch.dict(salt_proxy.__salt__, - {'salt_proxy.is_running': is_running_mock}), \ - patch.dict(salt_proxy.__salt__, - {'salt_proxy.configure_proxy': configure_mock}), \ - patch.dict(salt_proxy.__salt__, - {'cmd.run_all': cmd_run_mock}): - config = [{'proxies': {'p8000': ''}}] + is_running_mock = MagicMock(return_value={"result": False}) + configure_mock = MagicMock( + return_value={ + "result": True, + "changes": { + "new": "Salt Proxy: Started proxy process for p8000", + "old": [], + }, + } + ) + cmd_run_mock = MagicMock( + return_value={"pid": 1000, "retcode": 0, "stderr": "", "stdout": ""} + ) + with patch.dict( + salt_proxy.__salt__, {"salt_proxy.is_running": is_running_mock} + ), patch.dict( + salt_proxy.__salt__, {"salt_proxy.configure_proxy": configure_mock} + ), patch.dict( + salt_proxy.__salt__, {"cmd.run_all": cmd_run_mock} + ): + config = [{"proxies": {"p8000": ""}}] ret = salt_proxy.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = salt_proxy.beacon(config) - self.assertEqual(ret, [{'p8000': 'Proxy p8000 was started'}]) + self.assertEqual(ret, [{"p8000": "Proxy p8000 was started"}]) diff --git a/tests/unit/beacons/test_sensehat.py b/tests/unit/beacons/test_sensehat.py index 9f3e1e14152..eead9bbfb7e 100644 --- a/tests/unit/beacons/test_sensehat.py +++ b/tests/unit/beacons/test_sensehat.py @@ -3,19 +3,19 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import MagicMock -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.sensehat as sensehat +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock + +# Salt testing libs +from tests.support.unit import TestCase class SensehatBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.[s] - ''' + """ def setup_loader_modules(self): @@ -23,17 +23,18 @@ class SensehatBeaconTestCase(TestCase, LoaderModuleMockMixin): self.TEMPERATURE_MOCK = MagicMock(return_value=30) self.PRESSURE_MOCK = MagicMock(return_value=1500) - self.addCleanup(delattr, self, 'HUMIDITY_MOCK') - self.addCleanup(delattr, self, 'TEMPERATURE_MOCK') - self.addCleanup(delattr, self, 'PRESSURE_MOCK') + self.addCleanup(delattr, self, "HUMIDITY_MOCK") + self.addCleanup(delattr, self, "TEMPERATURE_MOCK") + self.addCleanup(delattr, self, "PRESSURE_MOCK") return { sensehat: { - '__context__': {}, - '__salt__': {'sensehat.get_humidity': self.HUMIDITY_MOCK, - 'sensehat.get_temperature': self.TEMPERATURE_MOCK, - 'sensehat.get_pressure': self.PRESSURE_MOCK - }, + "__context__": {}, + "__salt__": { + "sensehat.get_humidity": self.HUMIDITY_MOCK, + "sensehat.get_temperature": self.TEMPERATURE_MOCK, + "sensehat.get_pressure": self.PRESSURE_MOCK, + }, } } @@ -42,67 +43,65 @@ class SensehatBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = sensehat.validate(config) - self.assertEqual(ret, (False, 'Configuration for sensehat beacon' - ' must be a list.')) + self.assertEqual( + ret, (False, "Configuration for sensehat beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = sensehat.validate(config) - self.assertEqual(ret, (False, 'Configuration for sensehat ' - 'beacon requires sensors.')) + self.assertEqual( + ret, (False, "Configuration for sensehat beacon requires sensors.") + ) def test_sensehat_humidity_match(self): - config = [{'sensors': {'humidity': '70%'}}] + config = [{"sensors": {"humidity": "70%"}}] ret = sensehat.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = sensehat.beacon(config) - self.assertEqual(ret, [{'tag': 'sensehat/humidity', - 'humidity': 80}]) + self.assertEqual(ret, [{"tag": "sensehat/humidity", "humidity": 80}]) def test_sensehat_temperature_match(self): - config = [{'sensors': {'temperature': 20}}] + config = [{"sensors": {"temperature": 20}}] ret = sensehat.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = sensehat.beacon(config) - self.assertEqual(ret, [{'tag': 'sensehat/temperature', - 'temperature': 30}]) + self.assertEqual(ret, [{"tag": "sensehat/temperature", "temperature": 30}]) def test_sensehat_temperature_match_range(self): - config = [{'sensors': {'temperature': [20, 29]}}] + config = [{"sensors": {"temperature": [20, 29]}}] ret = sensehat.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = sensehat.beacon(config) - self.assertEqual(ret, [{'tag': 'sensehat/temperature', - 'temperature': 30}]) + self.assertEqual(ret, [{"tag": "sensehat/temperature", "temperature": 30}]) def test_sensehat_pressure_match(self): - config = [{'sensors': {'pressure': '1400'}}] + config = [{"sensors": {"pressure": "1400"}}] ret = sensehat.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = sensehat.beacon(config) - self.assertEqual(ret, [{'tag': 'sensehat/pressure', - 'pressure': 1500}]) + self.assertEqual(ret, [{"tag": "sensehat/pressure", "pressure": 1500}]) def test_sensehat_no_match(self): - config = [{'sensors': {'pressure': '1600'}}] + config = [{"sensors": {"pressure": "1600"}}] ret = sensehat.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = sensehat.beacon(config) self.assertEqual(ret, []) diff --git a/tests/unit/beacons/test_service.py b/tests/unit/beacons/test_service.py index d6fd9a8b4ba..307b8776753 100644 --- a/tests/unit/beacons/test_service.py +++ b/tests/unit/beacons/test_service.py @@ -2,74 +2,88 @@ # Python libs from __future__ import absolute_import -from collections import namedtuple -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import LoaderModuleMockMixin +from collections import namedtuple # Salt libs import salt.beacons.service as service_beacon +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Salt testing libs +from tests.support.unit import TestCase PATCH_OPTS = dict(autospec=True, spec_set=True) -FakeProcess = namedtuple('Process', 'cmdline pid') +FakeProcess = namedtuple("Process", "cmdline pid") class ServiceBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.service - ''' + """ def setup_loader_modules(self): - return { - service_beacon: { - '__context__': {}, - '__salt__': {}, - } - } + return {service_beacon: {"__context__": {}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = service_beacon.validate(config) - self.assertEqual(ret, (False, 'Configuration for service beacon must' - ' be a list.')) + self.assertEqual( + ret, (False, "Configuration for service beacon must be a list.") + ) def test_empty_config(self): config = [{}] ret = service_beacon.validate(config) - self.assertEqual(ret, (False, 'Configuration for service ' - 'beacon requires services.')) + self.assertEqual( + ret, (False, "Configuration for service beacon requires services.") + ) def test_service_running(self): - with patch.dict(service_beacon.__salt__, - {'service.status': MagicMock(return_value=True)}): - config = [{'services': {'salt-master': {}}}] + with patch.dict( + service_beacon.__salt__, {"service.status": MagicMock(return_value=True)} + ): + config = [{"services": {"salt-master": {}}}] ret = service_beacon.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = service_beacon.beacon(config) - self.assertEqual(ret, [{'service_name': 'salt-master', - 'tag': 'salt-master', - 'salt-master': {'running': True}}]) + self.assertEqual( + ret, + [ + { + "service_name": "salt-master", + "tag": "salt-master", + "salt-master": {"running": True}, + } + ], + ) def test_service_not_running(self): - with patch.dict(service_beacon.__salt__, - {'service.status': MagicMock(return_value=False)}): - config = [{'services': {'salt-master': {}}}] + with patch.dict( + service_beacon.__salt__, {"service.status": MagicMock(return_value=False)} + ): + config = [{"services": {"salt-master": {}}}] ret = service_beacon.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = service_beacon.beacon(config) - self.assertEqual(ret, [{'service_name': 'salt-master', - 'tag': 'salt-master', - 'salt-master': {'running': False}}]) + self.assertEqual( + ret, + [ + { + "service_name": "salt-master", + "tag": "salt-master", + "salt-master": {"running": False}, + } + ], + ) diff --git a/tests/unit/beacons/test_smartos_imgadm.py b/tests/unit/beacons/test_smartos_imgadm.py index e80f724f4c9..2e2cd03f24f 100644 --- a/tests/unit/beacons/test_smartos_imgadm.py +++ b/tests/unit/beacons/test_smartos_imgadm.py @@ -3,194 +3,225 @@ # Python libs from __future__ import absolute_import, print_function, unicode_literals -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.smartos_imgadm as imgadm +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +# Salt testing libs +from tests.support.unit import TestCase # Mock Results -MOCK_CLEAN_STATE = {'first_run': True, 'images': []} +MOCK_CLEAN_STATE = {"first_run": True, "images": []} MOCK_IMAGE_NONE = {} MOCK_IMAGE_ONE = { - '00000000-0000-0000-0000-000000000001': { - 'description': 'Example Image 1', - 'name': 'example-1', - 'os': 'smartos', - 'published': '2018-01-01T00:42:00Z', - 'source': 'https://images.joyent.com', - 'version': '18.1.0', + "00000000-0000-0000-0000-000000000001": { + "description": "Example Image 1", + "name": "example-1", + "os": "smartos", + "published": "2018-01-01T00:42:00Z", + "source": "https://images.joyent.com", + "version": "18.1.0", }, } MOCK_IMAGE_TWO = { - '00000000-0000-0000-0000-000000000001': { - 'description': 'Example Image 1', - 'name': 'example-1', - 'os': 'smartos', - 'published': '2018-01-01T00:42:00Z', - 'source': 'https://images.joyent.com', - 'version': '18.1.0', + "00000000-0000-0000-0000-000000000001": { + "description": "Example Image 1", + "name": "example-1", + "os": "smartos", + "published": "2018-01-01T00:42:00Z", + "source": "https://images.joyent.com", + "version": "18.1.0", }, - '00000000-0000-0000-0000-000000000002': { - 'description': 'Example Image 2', - 'name': 'example-2', - 'os': 'smartos', - 'published': '2018-01-01T00:42:00Z', - 'source': 'https://images.joyent.com', - 'version': '18.2.0', + "00000000-0000-0000-0000-000000000002": { + "description": "Example Image 2", + "name": "example-2", + "os": "smartos", + "published": "2018-01-01T00:42:00Z", + "source": "https://images.joyent.com", + "version": "18.2.0", }, } class SmartOSImgAdmBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.imgadm - ''' + """ def setup_loader_modules(self): - return { - imgadm: { - '__context__': {}, - '__salt__': {}, - } - } + return {imgadm: {"__context__": {}, "__salt__": {}}} def test_non_list_config(self): - ''' + """ We only have minimal validation so we test that here - ''' - assert imgadm.validate({}) == (False, 'Configuration for imgadm beacon must be a list!') + """ + assert imgadm.validate({}) == ( + False, + "Configuration for imgadm beacon must be a list!", + ) def test_imported_startup(self): - ''' + """ Test with one image and startup_import_event - ''' + """ # NOTE: this should yield 1 imported event - with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(imgadm.__salt__, {'imgadm.list': MagicMock(return_value=MOCK_IMAGE_ONE)}): + with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), patch.dict( + imgadm.__salt__, {"imgadm.list": MagicMock(return_value=MOCK_IMAGE_ONE)} + ): - config = [{'startup_import_event': True}] - assert imgadm.validate(config) == (True, 'Valid beacon configuration') + config = [{"startup_import_event": True}] + assert imgadm.validate(config) == (True, "Valid beacon configuration") ret = imgadm.beacon(config) - res = [{'description': 'Example Image 1', - 'name': 'example-1', - 'os': 'smartos', - 'published': '2018-01-01T00:42:00Z', - 'source': 'https://images.joyent.com', - 'tag': 'imported/00000000-0000-0000-0000-000000000001', - 'version': '18.1.0'}] + res = [ + { + "description": "Example Image 1", + "name": "example-1", + "os": "smartos", + "published": "2018-01-01T00:42:00Z", + "source": "https://images.joyent.com", + "tag": "imported/00000000-0000-0000-0000-000000000001", + "version": "18.1.0", + } + ] assert ret == res def test_imported_nostartup(self): - ''' + """ Test with one image and startup_import_event unset/false - ''' + """ # NOTE: this should yield 0 imported event - with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(imgadm.__salt__, {'imgadm.list': MagicMock(return_value=MOCK_IMAGE_ONE)}): + with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), patch.dict( + imgadm.__salt__, {"imgadm.list": MagicMock(return_value=MOCK_IMAGE_ONE)} + ): config = [] - assert imgadm.validate(config) == (True, 'Valid beacon configuration') + assert imgadm.validate(config) == (True, "Valid beacon configuration") assert imgadm.beacon(config) == [] def test_imported(self): - ''' + """ Test with one image and a new image added on the 2nd pass - ''' + """ # NOTE: this should yield 1 imported event - with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(imgadm.__salt__, {'imgadm.list': MagicMock(side_effect=[MOCK_IMAGE_ONE, MOCK_IMAGE_TWO])}): + with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), patch.dict( + imgadm.__salt__, + {"imgadm.list": MagicMock(side_effect=[MOCK_IMAGE_ONE, MOCK_IMAGE_TWO])}, + ): config = [] - assert imgadm.validate(config) == (True, 'Valid beacon configuration') + assert imgadm.validate(config) == (True, "Valid beacon configuration") # Initial pass (Initialized state and do not yield imported images at startup) imgadm.beacon(config) # Second pass (After importing a new image) ret = imgadm.beacon(config) - res = [{'description': 'Example Image 2', - 'name': 'example-2', - 'os': 'smartos', - 'published': '2018-01-01T00:42:00Z', - 'source': 'https://images.joyent.com', - 'tag': 'imported/00000000-0000-0000-0000-000000000002', - 'version': '18.2.0'}] + res = [ + { + "description": "Example Image 2", + "name": "example-2", + "os": "smartos", + "published": "2018-01-01T00:42:00Z", + "source": "https://images.joyent.com", + "tag": "imported/00000000-0000-0000-0000-000000000002", + "version": "18.2.0", + } + ] assert ret == res def test_deleted(self): - ''' + """ Test with two images and one gets deletes - ''' + """ # NOTE: this should yield 1 deleted event - with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(imgadm.__salt__, {'imgadm.list': MagicMock(side_effect=[MOCK_IMAGE_TWO, MOCK_IMAGE_ONE])}): + with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), patch.dict( + imgadm.__salt__, + {"imgadm.list": MagicMock(side_effect=[MOCK_IMAGE_TWO, MOCK_IMAGE_ONE])}, + ): config = [] - assert imgadm.validate(config) == (True, 'Valid beacon configuration') + assert imgadm.validate(config) == (True, "Valid beacon configuration") # Initial pass (Initialized state and do not yield imported images at startup) imgadm.beacon(config) # Second pass (After deleting one image) ret = imgadm.beacon(config) - res = [{'description': 'Example Image 2', - 'name': 'example-2', - 'os': 'smartos', - 'published': '2018-01-01T00:42:00Z', - 'source': 'https://images.joyent.com', - 'tag': 'deleted/00000000-0000-0000-0000-000000000002', - 'version': '18.2.0'}] + res = [ + { + "description": "Example Image 2", + "name": "example-2", + "os": "smartos", + "published": "2018-01-01T00:42:00Z", + "source": "https://images.joyent.com", + "tag": "deleted/00000000-0000-0000-0000-000000000002", + "version": "18.2.0", + } + ] assert ret == res def test_complex(self): - ''' + """ Test with one image, delete both, import 2 - ''' + """ # NOTE: this should yield 1 delete and 2 import events - with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(imgadm.__salt__, {'imgadm.list': MagicMock(side_effect=[MOCK_IMAGE_ONE, MOCK_IMAGE_NONE, MOCK_IMAGE_TWO])}): + with patch.dict(imgadm.IMGADM_STATE, MOCK_CLEAN_STATE), patch.dict( + imgadm.__salt__, + { + "imgadm.list": MagicMock( + side_effect=[MOCK_IMAGE_ONE, MOCK_IMAGE_NONE, MOCK_IMAGE_TWO] + ) + }, + ): config = [] - assert imgadm.validate(config), (True, 'Valid beacon configuration') + assert imgadm.validate(config), (True, "Valid beacon configuration") # Initial pass (Initialized state and do not yield imported images at startup) imgadm.beacon(config) # Second pass (After deleting one image) ret = imgadm.beacon(config) - res = [{'description': 'Example Image 1', - 'name': 'example-1', - 'os': 'smartos', - 'published': '2018-01-01T00:42:00Z', - 'source': 'https://images.joyent.com', - 'tag': 'deleted/00000000-0000-0000-0000-000000000001', - 'version': '18.1.0'}] + res = [ + { + "description": "Example Image 1", + "name": "example-1", + "os": "smartos", + "published": "2018-01-01T00:42:00Z", + "source": "https://images.joyent.com", + "tag": "deleted/00000000-0000-0000-0000-000000000001", + "version": "18.1.0", + } + ] assert ret == res # Third pass (After importing two images) ret = imgadm.beacon(config) - res = [{'description': 'Example Image 1', - 'name': 'example-1', - 'os': 'smartos', - 'published': '2018-01-01T00:42:00Z', - 'source': 'https://images.joyent.com', - 'tag': 'imported/00000000-0000-0000-0000-000000000001', - 'version': '18.1.0'}, - {'description': 'Example Image 2', - 'name': 'example-2', - 'os': 'smartos', - 'published': '2018-01-01T00:42:00Z', - 'source': 'https://images.joyent.com', - 'tag': 'imported/00000000-0000-0000-0000-000000000002', - 'version': '18.2.0'}] + res = [ + { + "description": "Example Image 1", + "name": "example-1", + "os": "smartos", + "published": "2018-01-01T00:42:00Z", + "source": "https://images.joyent.com", + "tag": "imported/00000000-0000-0000-0000-000000000001", + "version": "18.1.0", + }, + { + "description": "Example Image 2", + "name": "example-2", + "os": "smartos", + "published": "2018-01-01T00:42:00Z", + "source": "https://images.joyent.com", + "tag": "imported/00000000-0000-0000-0000-000000000002", + "version": "18.2.0", + }, + ] assert len(ret) == 2 for item in ret: assert item in res diff --git a/tests/unit/beacons/test_smartos_vmadm.py b/tests/unit/beacons/test_smartos_vmadm.py index 5f79f3d4687..e736f646ac9 100644 --- a/tests/unit/beacons/test_smartos_vmadm.py +++ b/tests/unit/beacons/test_smartos_vmadm.py @@ -3,207 +3,242 @@ # Python libs from __future__ import absolute_import -# Salt testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.smartos_vmadm as vmadm +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Salt testing libs +from tests.support.unit import TestCase # Mock Results -MOCK_CLEAN_STATE = {'first_run': True, 'vms': []} +MOCK_CLEAN_STATE = {"first_run": True, "vms": []} MOCK_VM_NONE = {} MOCK_VM_ONE = { - '00000000-0000-0000-0000-000000000001': { - 'state': 'running', - 'alias': 'vm1', - 'hostname': 'vm1', - 'dns_domain': 'example.org', + "00000000-0000-0000-0000-000000000001": { + "state": "running", + "alias": "vm1", + "hostname": "vm1", + "dns_domain": "example.org", }, } MOCK_VM_TWO_STOPPED = { - '00000000-0000-0000-0000-000000000001': { - 'state': 'running', - 'alias': 'vm1', - 'hostname': 'vm1', - 'dns_domain': 'example.org', + "00000000-0000-0000-0000-000000000001": { + "state": "running", + "alias": "vm1", + "hostname": "vm1", + "dns_domain": "example.org", }, - '00000000-0000-0000-0000-000000000002': { - 'state': 'stopped', - 'alias': 'vm2', - 'hostname': 'vm2', - 'dns_domain': 'example.org', + "00000000-0000-0000-0000-000000000002": { + "state": "stopped", + "alias": "vm2", + "hostname": "vm2", + "dns_domain": "example.org", }, } MOCK_VM_TWO_STARTED = { - '00000000-0000-0000-0000-000000000001': { - 'state': 'running', - 'alias': 'vm1', - 'hostname': 'vm1', - 'dns_domain': 'example.org', + "00000000-0000-0000-0000-000000000001": { + "state": "running", + "alias": "vm1", + "hostname": "vm1", + "dns_domain": "example.org", }, - '00000000-0000-0000-0000-000000000002': { - 'state': 'running', - 'alias': 'vm2', - 'hostname': 'vm2', - 'dns_domain': 'example.org', + "00000000-0000-0000-0000-000000000002": { + "state": "running", + "alias": "vm2", + "hostname": "vm2", + "dns_domain": "example.org", }, } class SmartOSImgAdmBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.vmadm - ''' + """ def setup_loader_modules(self): - return { - vmadm: { - '__context__': {}, - '__salt__': {}, - } - } + return {vmadm: {"__context__": {}, "__salt__": {}}} def test_non_list_config(self): - ''' + """ We only have minimal validation so we test that here - ''' + """ config = {} ret = vmadm.validate(config) - self.assertEqual(ret, (False, 'Configuration for vmadm beacon must be a list!')) + self.assertEqual(ret, (False, "Configuration for vmadm beacon must be a list!")) def test_created_startup(self): - ''' + """ Test with one vm and startup_create_event - ''' + """ # NOTE: this should yield 1 created event + one state event - with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(vmadm.__salt__, {'vmadm.list': MagicMock(return_value=MOCK_VM_ONE)}): + with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), patch.dict( + vmadm.__salt__, {"vmadm.list": MagicMock(return_value=MOCK_VM_ONE)} + ): - config = [{'startup_create_event': True}] + config = [{"startup_create_event": True}] ret = vmadm.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = vmadm.beacon(config) - res = [{'alias': 'vm1', - 'tag': 'created/00000000-0000-0000-0000-000000000001', - 'hostname': 'vm1', - 'dns_domain': 'example.org'}, - {'alias': 'vm1', - 'tag': 'running/00000000-0000-0000-0000-000000000001', - 'hostname': 'vm1', - 'dns_domain': 'example.org'}] + res = [ + { + "alias": "vm1", + "tag": "created/00000000-0000-0000-0000-000000000001", + "hostname": "vm1", + "dns_domain": "example.org", + }, + { + "alias": "vm1", + "tag": "running/00000000-0000-0000-0000-000000000001", + "hostname": "vm1", + "dns_domain": "example.org", + }, + ] self.assertEqual(ret, res) def test_created_nostartup(self): - ''' + """ Test with one image and startup_import_event unset/false - ''' + """ # NOTE: this should yield 0 created event _ one state event - with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(vmadm.__salt__, {'vmadm.list': MagicMock(return_value=MOCK_VM_ONE)}): + with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), patch.dict( + vmadm.__salt__, {"vmadm.list": MagicMock(return_value=MOCK_VM_ONE)} + ): config = [] ret = vmadm.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = vmadm.beacon(config) - res = [{'alias': 'vm1', - 'tag': 'running/00000000-0000-0000-0000-000000000001', - 'hostname': 'vm1', - 'dns_domain': 'example.org'}] + res = [ + { + "alias": "vm1", + "tag": "running/00000000-0000-0000-0000-000000000001", + "hostname": "vm1", + "dns_domain": "example.org", + } + ] self.assertEqual(ret, res) def test_created(self): - ''' + """ Test with one vm, create a 2nd one - ''' + """ # NOTE: this should yield 1 created event + state event - with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(vmadm.__salt__, {'vmadm.list': MagicMock(side_effect=[MOCK_VM_ONE, MOCK_VM_TWO_STARTED])}): + with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), patch.dict( + vmadm.__salt__, + {"vmadm.list": MagicMock(side_effect=[MOCK_VM_ONE, MOCK_VM_TWO_STARTED])}, + ): config = [] ret = vmadm.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) # Initial pass (Initialized state and do not yield created events at startup) ret = vmadm.beacon(config) # Second pass (After create a new vm) ret = vmadm.beacon(config) - res = [{'alias': 'vm2', - 'tag': 'created/00000000-0000-0000-0000-000000000002', - 'hostname': 'vm2', - 'dns_domain': 'example.org'}, - {'alias': 'vm2', - 'tag': 'running/00000000-0000-0000-0000-000000000002', - 'hostname': 'vm2', - 'dns_domain': 'example.org'}] + res = [ + { + "alias": "vm2", + "tag": "created/00000000-0000-0000-0000-000000000002", + "hostname": "vm2", + "dns_domain": "example.org", + }, + { + "alias": "vm2", + "tag": "running/00000000-0000-0000-0000-000000000002", + "hostname": "vm2", + "dns_domain": "example.org", + }, + ] self.assertEqual(ret, res) def test_deleted(self): - ''' + """ Test with two vms and one gets destroyed - ''' + """ # NOTE: this should yield 1 destroyed event - with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(vmadm.__salt__, {'vmadm.list': MagicMock(side_effect=[MOCK_VM_TWO_STOPPED, MOCK_VM_ONE])}): + with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), patch.dict( + vmadm.__salt__, + {"vmadm.list": MagicMock(side_effect=[MOCK_VM_TWO_STOPPED, MOCK_VM_ONE])}, + ): config = [] ret = vmadm.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) # Initial pass (Initialized state and do not yield created vms at startup) ret = vmadm.beacon(config) # Second pass (Destroying one vm) ret = vmadm.beacon(config) - res = [{'alias': 'vm2', - 'tag': 'deleted/00000000-0000-0000-0000-000000000002', - 'hostname': 'vm2', - 'dns_domain': 'example.org'}] + res = [ + { + "alias": "vm2", + "tag": "deleted/00000000-0000-0000-0000-000000000002", + "hostname": "vm2", + "dns_domain": "example.org", + } + ] self.assertEqual(ret, res) def test_complex(self): - ''' + """ Test with two vms, stop one, delete one - ''' + """ # NOTE: this should yield 1 delete and 2 import events - with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), \ - patch.dict(vmadm.__salt__, {'vmadm.list': MagicMock(side_effect=[MOCK_VM_TWO_STARTED, MOCK_VM_TWO_STOPPED, MOCK_VM_ONE])}): + with patch.dict(vmadm.VMADM_STATE, MOCK_CLEAN_STATE), patch.dict( + vmadm.__salt__, + { + "vmadm.list": MagicMock( + side_effect=[MOCK_VM_TWO_STARTED, MOCK_VM_TWO_STOPPED, MOCK_VM_ONE] + ) + }, + ): config = [] ret = vmadm.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) # Initial pass (Initialized state and do not yield created events at startup) ret = vmadm.beacon(config) # Second pass (Stop one vm) ret = vmadm.beacon(config) - res = [{'alias': 'vm2', - 'tag': 'stopped/00000000-0000-0000-0000-000000000002', - 'hostname': 'vm2', - 'dns_domain': 'example.org'}] + res = [ + { + "alias": "vm2", + "tag": "stopped/00000000-0000-0000-0000-000000000002", + "hostname": "vm2", + "dns_domain": "example.org", + } + ] self.assertEqual(ret, res) # Third pass (Delete one vm) ret = vmadm.beacon(config) - res = [{'alias': 'vm2', - 'tag': 'deleted/00000000-0000-0000-0000-000000000002', - 'hostname': 'vm2', - 'dns_domain': 'example.org'}] + res = [ + { + "alias": "vm2", + "tag": "deleted/00000000-0000-0000-0000-000000000002", + "hostname": "vm2", + "dns_domain": "example.org", + } + ] self.assertEqual(ret, res) diff --git a/tests/unit/beacons/test_status.py b/tests/unit/beacons/test_status.py index 175716ffcad..b37a4c4d963 100644 --- a/tests/unit/beacons/test_status.py +++ b/tests/unit/beacons/test_status.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) :copyright: Copyright 2017 by the SaltStack Team, see AUTHORS for more details. @@ -8,7 +8,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Status beacon test cases -''' +""" # Python libs from __future__ import absolute_import @@ -16,57 +16,56 @@ from __future__ import absolute_import # Salt libs import salt.config import salt.loader -from salt.beacons import status import salt.modules.status as status_module +from salt.beacons import status +from tests.support.mixins import LoaderModuleMockMixin # Salt testing libs from tests.support.unit import TestCase -from tests.support.mixins import LoaderModuleMockMixin class StatusBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.status - ''' + """ def setup_loader_modules(self): opts = salt.config.DEFAULT_MINION_OPTS.copy() - opts['grains'] = salt.loader.grains(opts) + opts["grains"] = salt.loader.grains(opts) module_globals = { - '__opts__': opts, - '__salt__': 'autoload', - '__context__': {}, - '__grains__': opts['grains'] - } - return { - status: module_globals, - status_module: module_globals + "__opts__": opts, + "__salt__": "autoload", + "__context__": {}, + "__grains__": opts["grains"], } + return {status: module_globals, status_module: module_globals} def test_empty_config(self, *args, **kwargs): config = [] ret = status.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = status.beacon(config) - expected = sorted(['loadavg', 'meminfo', 'cpustats', 'vmstats', 'time']) + expected = sorted(["loadavg", "meminfo", "cpustats", "vmstats", "time"]) - self.assertEqual(sorted(list(ret[0]['data'])), expected) + self.assertEqual(sorted(list(ret[0]["data"])), expected) def test_deprecated_dict_config(self): - config = {'time': ['all']} + config = {"time": ["all"]} ret = status.validate(config) - self.assertEqual(ret, (False, 'Configuration for status beacon must be a list.')) + self.assertEqual( + ret, (False, "Configuration for status beacon must be a list.") + ) def test_list_config(self): - config = [{'time': ['all']}] + config = [{"time": ["all"]}] ret = status.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) ret = status.beacon(config) - expected = ['time'] + expected = ["time"] - self.assertEqual(list(ret[0]['data']), expected) + self.assertEqual(list(ret[0]["data"]), expected) diff --git a/tests/unit/beacons/test_telegram_bot_msg.py b/tests/unit/beacons/test_telegram_bot_msg.py index 2ccadf580b6..488f3ab2064 100644 --- a/tests/unit/beacons/test_telegram_bot_msg.py +++ b/tests/unit/beacons/test_telegram_bot_msg.py @@ -2,104 +2,105 @@ # Python libs from __future__ import absolute_import + import datetime +import logging # Salt libs from salt.beacons import telegram_bot_msg # Salt testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # Third-party libs try: import telegram + HAS_TELEGRAM = True except ImportError: HAS_TELEGRAM = False -import logging + log = logging.getLogger(__name__) -@skipIf(not HAS_TELEGRAM, 'telegram is not available') +@skipIf(not HAS_TELEGRAM, "telegram is not available") class TelegramBotMsgBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.telegram_bot - ''' + """ + def setup_loader_modules(self): return {telegram_bot_msg: {}} def test_validate_empty_config(self, *args, **kwargs): ret = telegram_bot_msg.validate(None) - self.assertEqual(ret, (False, ('Configuration for telegram_bot_msg ' - 'beacon must be a list.'))) + self.assertEqual( + ret, (False, ("Configuration for telegram_bot_msg beacon must be a list.")), + ) def test_validate_missing_accept_from_config(self, *args, **kwargs): - ret = telegram_bot_msg.validate([{ - 'token': 'bcd' - }]) - self.assertEqual(ret, (False, ('Not all required configuration for ' - 'telegram_bot_msg are set.'))) + ret = telegram_bot_msg.validate([{"token": "bcd"}]) + self.assertEqual( + ret, + (False, ("Not all required configuration for telegram_bot_msg are set."),), + ) def test_validate_missing_token_config(self, *args, **kwargs): - ret = telegram_bot_msg.validate([{ - 'accept_from': [] - }]) - self.assertEqual(ret, (False, ('Not all required configuration for ' - 'telegram_bot_msg are set.'))) + ret = telegram_bot_msg.validate([{"accept_from": []}]) + self.assertEqual( + ret, + (False, ("Not all required configuration for telegram_bot_msg are set."),), + ) def test_validate_config_not_list_in_accept_from(self, *args, **kwargs): - ret = telegram_bot_msg.validate([{ - 'token': 'bcd', - 'accept_from': {'nodict': "1"} - }]) - self.assertEqual(ret, (False, ('Configuration for telegram_bot_msg, ' - 'accept_from must be a list of ' - 'usernames.'))) + ret = telegram_bot_msg.validate( + [{"token": "bcd", "accept_from": {"nodict": "1"}}] + ) + self.assertEqual( + ret, + ( + False, + ( + "Configuration for telegram_bot_msg, " + "accept_from must be a list of " + "usernames." + ), + ), + ) def test_validate_valid_config(self, *args, **kwargs): - ret = telegram_bot_msg.validate([{ - 'token': 'bcd', - 'accept_from': [ - 'username' - ] - }]) - self.assertEqual(ret, (True, 'Valid beacon configuration.')) + ret = telegram_bot_msg.validate([{"token": "bcd", "accept_from": ["username"]}]) + self.assertEqual(ret, (True, "Valid beacon configuration.")) def test_call_no_updates(self): with patch("salt.beacons.telegram_bot_msg.telegram") as telegram_api: - token = 'abc' - config = [{ - 'token': token, - 'accept_from': ['tester'] - }] - inst = MagicMock(name='telegram.Bot()') - telegram_api.Bot = MagicMock(name='telegram', return_value=inst) + token = "abc" + config = [{"token": token, "accept_from": ["tester"]}] + inst = MagicMock(name="telegram.Bot()") + telegram_api.Bot = MagicMock(name="telegram", return_value=inst) inst.get_updates.return_value = [] ret = telegram_bot_msg.beacon(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) telegram_api.Bot.assert_called_once_with(token) self.assertEqual(ret, []) def test_call_telegram_return_no_updates_for_user(self): with patch("salt.beacons.telegram_bot_msg.telegram") as telegram_api: - token = 'abc' - username = 'tester' - config = [{ - 'token': token, - 'accept_from': [username] - }] - inst = MagicMock(name='telegram.Bot()') - telegram_api.Bot = MagicMock(name='telegram', return_value=inst) + token = "abc" + username = "tester" + config = [{"token": token, "accept_from": [username]}] + inst = MagicMock(name="telegram.Bot()") + telegram_api.Bot = MagicMock(name="telegram", return_value=inst) - log.debug('telegram {}'.format(telegram)) - username = 'different_user' - user = telegram.user.User(id=1, first_name='', username=username) - chat = telegram.chat.Chat(1, 'private', username=username) + log.debug("telegram {}".format(telegram)) + username = "different_user" + user = telegram.user.User(id=1, first_name="", username=username) + chat = telegram.chat.Chat(1, "private", username=username) date = datetime.datetime(2016, 12, 18, 0, 0) message = telegram.message.Message(1, user, date=date, chat=chat) update = telegram.update.Update(update_id=1, message=message) @@ -107,24 +108,21 @@ class TelegramBotMsgBeaconTestCase(TestCase, LoaderModuleMockMixin): inst.get_updates.return_value = [update] ret = telegram_bot_msg.beacon(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) telegram_api.Bot.assert_called_once_with(token) self.assertEqual(ret, []) def test_call_telegram_returning_updates(self): with patch("salt.beacons.telegram_bot_msg.telegram") as telegram_api: - token = 'abc' - username = 'tester' - config = [{ - 'token': token, - 'accept_from': [username] - }] - inst = MagicMock(name='telegram.Bot()') - telegram_api.Bot = MagicMock(name='telegram', return_value=inst) + token = "abc" + username = "tester" + config = [{"token": token, "accept_from": [username]}] + inst = MagicMock(name="telegram.Bot()") + telegram_api.Bot = MagicMock(name="telegram", return_value=inst) - user = telegram.User(id=1, first_name='', username=username) - chat = telegram.Chat(1, 'private', username=username) + user = telegram.User(id=1, first_name="", username=username) + chat = telegram.Chat(1, "private", username=username) date = datetime.datetime(2016, 12, 18, 0, 0) message = telegram.Message(1, user, date=date, chat=chat) update = telegram.update.Update(update_id=1, message=message) @@ -132,8 +130,8 @@ class TelegramBotMsgBeaconTestCase(TestCase, LoaderModuleMockMixin): inst.get_updates.return_value = [update] ret = telegram_bot_msg.beacon(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) telegram_api.Bot.assert_called_once_with(token) self.assertTrue(ret) - self.assertEqual(ret[0]['msgs'][0], message.to_dict()) + self.assertEqual(ret[0]["msgs"][0], message.to_dict()) diff --git a/tests/unit/beacons/test_twilio_txt_msg.py b/tests/unit/beacons/test_twilio_txt_msg.py index 64e0b548f9d..e7b560f6a3e 100644 --- a/tests/unit/beacons/test_twilio_txt_msg.py +++ b/tests/unit/beacons/test_twilio_txt_msg.py @@ -3,20 +3,23 @@ # Python libs from __future__ import absolute_import +import logging + # Salt libs from salt.beacons import twilio_txt_msg # Salt testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # Import 3rd Party libs try: import twilio + # Grab version, ensure elements are ints twilio_version = tuple([int(x) for x in twilio.__version_info__]) - if twilio_version > (5, ): + if twilio_version > (5,): TWILIO_5 = False else: TWILIO_5 = True @@ -24,44 +27,46 @@ try: except ImportError: HAS_TWILIO = False -import logging + log = logging.getLogger(__name__) class MockTwilioRestException(Exception): - ''' + """ Mock TwilioRestException class - ''' + """ + def __init__(self): - self.code = 'error code' - self.msg = 'Exception error' - self.status = 'Not send' + self.code = "error code" + self.msg = "Exception error" + self.status = "Not send" super(MockTwilioRestException, self).__init__(self.msg) class MockMessages(object): - ''' + """ Mock SMS class - ''' + """ + flag = None def __init__(self): - self.sid = '011' - self.price = '200' - self.price_unit = '1' - self.status = 'Sent' - self.num_segments = '2' - self.num_media = '0' + self.sid = "011" + self.price = "200" + self.price_unit = "1" + self.status = "Sent" + self.num_segments = "2" + self.num_media = "0" self.body = None - self.date_sent = '01-01-2015' - self.date_created = '01-01-2015' + self.date_sent = "01-01-2015" + self.date_created = "01-01-2015" self.to = None self.from_ = None def create(self, body, to, from_): - ''' + """ Mock create method - ''' + """ msg = MockMessages() if self.flag == 1: raise MockTwilioRestException() @@ -71,31 +76,33 @@ class MockMessages(object): return msg def list(self, to): - ''' + """ Mock list method - ''' + """ msg = MockMessages() return [msg] def delete(self): - ''' + """ Mock delete method - ''' + """ return None class MockSMS(object): - ''' + """ Mock SMS class - ''' + """ + def __init__(self): self.messages = MockMessages() class MockTwilioRestClient(object): - ''' + """ Mock TwilioRestClient class - ''' + """ + def __init__(self): if TWILIO_5: self.sms = MockSMS() @@ -103,62 +110,96 @@ class MockTwilioRestClient(object): self.messages = MockMessages() -@skipIf(not HAS_TWILIO, 'twilio.rest is not available') +@skipIf(not HAS_TWILIO, "twilio.rest is not available") class TwilioMsgTxtBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.twilio_txt_msg - ''' + """ + def setup_loader_modules(self): return {twilio_txt_msg: {}} def test_validate_dictionary_config(self): - ''' + """ Test empty configuration - ''' + """ config = {} ret = twilio_txt_msg.validate(config) - self.assertEqual(ret, (False, ('Configuration for twilio_txt_msg ' - 'beacon must be a list.'))) + self.assertEqual( + ret, (False, ("Configuration for twilio_txt_msg beacon must be a list.")) + ) def test_validate_empty_config(self): - ''' + """ Test empty configuration - ''' + """ config = [{}] ret = twilio_txt_msg.validate(config) - self.assertEqual(ret, (False, ('Configuration for twilio_txt_msg ' - 'beacon must contain account_sid, ' - 'auth_token and twilio_number items.'))) + self.assertEqual( + ret, + ( + False, + ( + "Configuration for twilio_txt_msg " + "beacon must contain account_sid, " + "auth_token and twilio_number items." + ), + ), + ) def test_validate_missing_config_item(self): - ''' + """ Test empty configuration - ''' - config = [{'account_sid': 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', - 'twilio_number': '+15555555555'}] + """ + config = [ + { + "account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "twilio_number": "+15555555555", + } + ] ret = twilio_txt_msg.validate(config) - self.assertEqual(ret, (False, ('Configuration for twilio_txt_msg ' - 'beacon must contain account_sid, ' - 'auth_token and twilio_number items.'))) + self.assertEqual( + ret, + ( + False, + ( + "Configuration for twilio_txt_msg " + "beacon must contain account_sid, " + "auth_token and twilio_number items." + ), + ), + ) def test_receive_message(self): - ''' + """ Test receive a message - ''' - config = [{'account_sid': 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', - 'auth_token': 'my_token', - 'twilio_number': '+15555555555'}] + """ + config = [ + { + "account_sid": "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "auth_token": "my_token", + "twilio_number": "+15555555555", + } + ] ret = twilio_txt_msg.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected_return = [{'texts': [{'body': 'None', - 'images': [], - 'from': 'None', - 'id': '011', - 'sent': '01-01-2015'}]}] + _expected_return = [ + { + "texts": [ + { + "body": "None", + "images": [], + "from": "None", + "id": "011", + "sent": "01-01-2015", + } + ] + } + ] mock = MagicMock(return_value=MockTwilioRestClient()) - with patch.object(twilio_txt_msg, 'TwilioRestClient', mock): + with patch.object(twilio_txt_msg, "TwilioRestClient", mock): ret = twilio_txt_msg.beacon(config) self.assertEqual(ret, _expected_return) diff --git a/tests/unit/beacons/test_watchdog.py b/tests/unit/beacons/test_watchdog.py index ffaa2d6d293..fbfab756422 100644 --- a/tests/unit/beacons/test_watchdog.py +++ b/tests/unit/beacons/test_watchdog.py @@ -2,6 +2,7 @@ # Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import tempfile @@ -12,10 +13,10 @@ import salt.utils.files import salt.utils.platform from salt.beacons import watchdog from salt.ext.six.moves import range +from tests.support.mixins import LoaderModuleMockMixin # Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase, skipIf def check_events(config): @@ -34,18 +35,21 @@ def check_events(config): def create(path, content=None): - with salt.utils.files.fopen(path, 'w') as f: + with salt.utils.files.fopen(path, "w") as f: if content: f.write(content) os.fsync(f) -@skipIf(not watchdog.HAS_WATCHDOG, 'watchdog is not available') -@skipIf(salt.utils.platform.is_darwin(), 'Tests were being skipped pre macos under nox. Keep it like that for now.') +@skipIf(not watchdog.HAS_WATCHDOG, "watchdog is not available") +@skipIf( + salt.utils.platform.is_darwin(), + "Tests were being skipped pre macos under nox. Keep it like that for now.", +) class IWatchdogBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.watchdog - ''' + """ def setup_loader_modules(self): return {watchdog: {}} @@ -59,7 +63,7 @@ class IWatchdogBeaconTestCase(TestCase, LoaderModuleMockMixin): def assertValid(self, config): ret = watchdog.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) def test_empty_config(self): config = [{}] @@ -67,9 +71,9 @@ class IWatchdogBeaconTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(ret, []) def test_file_create(self): - path = os.path.join(self.tmpdir, 'tmpfile') + path = os.path.join(self.tmpdir, "tmpfile") - config = [{'directories': {self.tmpdir: {'mask': ['create']}}}] + config = [{"directories": {self.tmpdir: {"mask": ["create"]}}}] self.assertValid(config) self.assertEqual(watchdog.beacon(config), []) @@ -77,20 +81,20 @@ class IWatchdogBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = check_events(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], path) - self.assertEqual(ret[0]['change'], 'created') + self.assertEqual(ret[0]["path"], path) + self.assertEqual(ret[0]["change"], "created") def test_file_modified(self): - path = os.path.join(self.tmpdir, 'tmpfile') + path = os.path.join(self.tmpdir, "tmpfile") # Create triggers a modify event along with the create event in Py3 # So, let's do this before configuring the beacon create(path) - config = [{'directories': {self.tmpdir: {'mask': ['modify']}}}] + config = [{"directories": {self.tmpdir: {"mask": ["modify"]}}}] self.assertValid(config) self.assertEqual(watchdog.beacon(config), []) - create(path, 'some content') + create(path, "some content") ret = check_events(config) @@ -103,18 +107,18 @@ class IWatchdogBeaconTestCase(TestCase, LoaderModuleMockMixin): # path to the temp file (path), other modified events will contain # the path minus "tmpfile" and will not match. That's how we'll # distinguish the two - if event['change'] == 'modified': - if event['path'] == path: + if event["change"] == "modified": + if event["path"] == path: modified = True # Check results of the for loop to validate modified self.assertTrue(modified) def test_file_deleted(self): - path = os.path.join(self.tmpdir, 'tmpfile') + path = os.path.join(self.tmpdir, "tmpfile") create(path) - config = [{'directories': {self.tmpdir: {'mask': ['delete']}}}] + config = [{"directories": {self.tmpdir: {"mask": ["delete"]}}}] self.assertValid(config) self.assertEqual(watchdog.beacon(config), []) @@ -122,51 +126,49 @@ class IWatchdogBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = check_events(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], path) - self.assertEqual(ret[0]['change'], 'deleted') + self.assertEqual(ret[0]["path"], path) + self.assertEqual(ret[0]["change"], "deleted") def test_file_moved(self): - path = os.path.join(self.tmpdir, 'tmpfile') + path = os.path.join(self.tmpdir, "tmpfile") create(path) - config = [{'directories': {self.tmpdir: {'mask': ['move']}}}] + config = [{"directories": {self.tmpdir: {"mask": ["move"]}}}] self.assertValid(config) self.assertEqual(watchdog.beacon(config), []) - os.rename(path, path + '_moved') + os.rename(path, path + "_moved") ret = check_events(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], path) - self.assertEqual(ret[0]['change'], 'moved') + self.assertEqual(ret[0]["path"], path) + self.assertEqual(ret[0]["change"], "moved") def test_file_create_in_directory(self): - config = [{'directories': {self.tmpdir: {'mask': ['create']}}}] + config = [{"directories": {self.tmpdir: {"mask": ["create"]}}}] self.assertValid(config) self.assertEqual(watchdog.beacon(config), []) - path = os.path.join(self.tmpdir, 'tmpfile') + path = os.path.join(self.tmpdir, "tmpfile") create(path) ret = check_events(config) self.assertEqual(len(ret), 1) - self.assertEqual(ret[0]['path'], path) - self.assertEqual(ret[0]['change'], 'created') + self.assertEqual(ret[0]["path"], path) + self.assertEqual(ret[0]["change"], "created") def test_trigger_all_possible_events(self): - path = os.path.join(self.tmpdir, 'tmpfile') - moved = path + '_moved' + path = os.path.join(self.tmpdir, "tmpfile") + moved = path + "_moved" - config = [{'directories': { - self.tmpdir: {}, - }}] + config = [{"directories": {self.tmpdir: {}}}] self.assertValid(config) self.assertEqual(watchdog.beacon(config), []) # create create(path) # modify - create(path, 'modified content') + create(path, "modified content") # move os.rename(path, moved) # delete @@ -177,20 +179,18 @@ class IWatchdogBeaconTestCase(TestCase, LoaderModuleMockMixin): ret = check_events(config) - events = {'created': '', - 'deleted': '', - 'moved': ''} + events = {"created": "", "deleted": "", "moved": ""} modified = False for event in ret: - if event['change'] == 'created': - self.assertEqual(event['path'], path) - events.pop('created', '') - if event['change'] == 'moved': - self.assertEqual(event['path'], path) - events.pop('moved', '') - if event['change'] == 'deleted': - self.assertEqual(event['path'], moved) - events.pop('deleted', '') + if event["change"] == "created": + self.assertEqual(event["path"], path) + events.pop("created", "") + if event["change"] == "moved": + self.assertEqual(event["path"], path) + events.pop("moved", "") + if event["change"] == "deleted": + self.assertEqual(event["path"], moved) + events.pop("deleted", "") # "modified" requires special handling # All events [created, moved, deleted] also trigger a "modified" # event on Linux @@ -199,8 +199,8 @@ class IWatchdogBeaconTestCase(TestCase, LoaderModuleMockMixin): # path to the temp file (path), other modified events will contain # the path minus "tmpfile" and will not match. That's how we'll # distinguish the two - if event['change'] == 'modified': - if event['path'] == path: + if event["change"] == "modified": + if event["path"] == path: modified = True # Check results of the for loop to validate modified diff --git a/tests/unit/beacons/test_wtmp.py b/tests/unit/beacons/test_wtmp.py index 81d40ee8ba5..d25b899c491 100644 --- a/tests/unit/beacons/test_wtmp.py +++ b/tests/unit/beacons/test_wtmp.py @@ -2,208 +2,279 @@ # Python libs from __future__ import absolute_import + import datetime import logging -# Salt testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import patch, MagicMock, mock_open -from tests.support.mixins import LoaderModuleMockMixin - # Salt libs import salt.beacons.wtmp as wtmp from salt.ext import six +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch + +# Salt testing libs +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error try: import dateutil.parser as dateutil_parser # pylint: disable=unused-import + _TIME_SUPPORTED = True except ImportError: _TIME_SUPPORTED = False -raw = b'\x07\x00\x00\x00H\x18\x00\x00pts/14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00s/14gareth\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13I\xc5YZf\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' -pack = (7, 6216, b'pts/14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b's/14', b'gareth\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', 0, 0, 0, 1506101523, 353882, 0, 0, 0, 16777216) +raw = b"\x07\x00\x00\x00H\x18\x00\x00pts/14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00s/14gareth\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13I\xc5YZf\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +pack = ( + 7, + 6216, + b"pts/14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + b"s/14", + b"gareth\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + b"::1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + 0, + 0, + 0, + 1506101523, + 353882, + 0, + 0, + 0, + 16777216, +) log = logging.getLogger(__name__) class WTMPBeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.beacons.[s] - ''' + """ def setup_loader_modules(self): - return { - wtmp: { - '__context__': {'wtmp.loc': 2}, - '__salt__': {}, - } - } + return {wtmp: {"__context__": {"wtmp.loc": 2}, "__salt__": {}}} def test_non_list_config(self): config = {} ret = wtmp.validate(config) - self.assertEqual(ret, (False, 'Configuration for wtmp beacon must' - ' be a list.')) + self.assertEqual(ret, (False, "Configuration for wtmp beacon must be a list.")) def test_empty_config(self): config = [{}] ret = wtmp.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) def test_no_match(self): - config = [{'users': {'gareth': {'time_range': {'end': '09-22-2017 5pm', - 'start': '09-22-2017 3pm'}}}} - ] + config = [ + { + "users": { + "gareth": { + "time_range": { + "end": "09-22-2017 5pm", + "start": "09-22-2017 3pm", + } + } + } + } + ] ret = wtmp.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - with patch('salt.utils.files.fopen', mock_open(b'')) as m_open: + with patch("salt.utils.files.fopen", mock_open(b"")) as m_open: ret = wtmp.beacon(config) call_args = next(six.itervalues(m_open.filehandles))[0].call.args - assert call_args == (wtmp.WTMP, 'rb'), call_args + assert call_args == (wtmp.WTMP, "rb"), call_args assert ret == [], ret def test_invalid_users(self): - config = [{'users': ['gareth']}] + config = [{"users": ["gareth"]}] ret = wtmp.validate(config) - self.assertEqual(ret, (False, 'User configuration for wtmp beacon must be a dictionary.')) + self.assertEqual( + ret, (False, "User configuration for wtmp beacon must be a dictionary.") + ) def test_invalid_groups(self): - config = [{'groups': ['docker']}] + config = [{"groups": ["docker"]}] ret = wtmp.validate(config) - self.assertEqual(ret, (False, 'Group configuration for wtmp beacon must be a dictionary.')) + self.assertEqual( + ret, (False, "Group configuration for wtmp beacon must be a dictionary.") + ) def test_default_invalid_time_range(self): - config = [{'defaults': {'time_range': {'start': '3pm'}}}] + config = [{"defaults": {"time_range": {"start": "3pm"}}}] ret = wtmp.validate(config) - self.assertEqual(ret, (False, 'The time_range parameter for wtmp beacon must contain start & end options.')) + self.assertEqual( + ret, + ( + False, + "The time_range parameter for wtmp beacon must contain start & end options.", + ), + ) def test_users_invalid_time_range(self): - config = [{'users': {'gareth': {'time_range': {'start': '3pm'}}}}] + config = [{"users": {"gareth": {"time_range": {"start": "3pm"}}}}] ret = wtmp.validate(config) - self.assertEqual(ret, (False, 'The time_range parameter for wtmp beacon must contain start & end options.')) + self.assertEqual( + ret, + ( + False, + "The time_range parameter for wtmp beacon must contain start & end options.", + ), + ) def test_groups_invalid_time_range(self): - config = [{'groups': {'docker': {'time_range': {'start': '3pm'}}}}] + config = [{"groups": {"docker": {"time_range": {"start": "3pm"}}}}] ret = wtmp.validate(config) - self.assertEqual(ret, (False, 'The time_range parameter for wtmp beacon must contain start & end options.')) + self.assertEqual( + ret, + ( + False, + "The time_range parameter for wtmp beacon must contain start & end options.", + ), + ) def test_match(self): - with patch('salt.utils.files.fopen', - mock_open(read_data=raw)): - with patch('struct.unpack', - MagicMock(return_value=pack)): - config = [{'users': {'gareth': {}}}] + with patch("salt.utils.files.fopen", mock_open(read_data=raw)): + with patch("struct.unpack", MagicMock(return_value=pack)): + config = [{"users": {"gareth": {}}}] ret = wtmp.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected = [{'PID': 6216, - 'action': 'login', - 'line': 'pts/14', - 'session': 0, - 'time': 0, - 'exit_status': 0, - 'inittab': 's/14', - 'type': 7, - 'addr': 1506101523, - 'hostname': '::1', - 'user': 'gareth'}] + _expected = [ + { + "PID": 6216, + "action": "login", + "line": "pts/14", + "session": 0, + "time": 0, + "exit_status": 0, + "inittab": "s/14", + "type": 7, + "addr": 1506101523, + "hostname": "::1", + "user": "gareth", + } + ] ret = wtmp.beacon(config) - log.debug('{}'.format(ret)) + log.debug("{}".format(ret)) self.assertEqual(ret, _expected) - @skipIf(not _TIME_SUPPORTED, 'dateutil.parser is missing.') + @skipIf(not _TIME_SUPPORTED, "dateutil.parser is missing.") def test_match_time(self): - with patch('salt.utils.files.fopen', - mock_open(read_data=raw)): + with patch("salt.utils.files.fopen", mock_open(read_data=raw)): mock_now = datetime.datetime(2017, 9, 22, 16, 0, 0, 0) - with patch('datetime.datetime', MagicMock()), \ - patch('datetime.datetime.now', - MagicMock(return_value=mock_now)): - with patch('struct.unpack', - MagicMock(return_value=pack)): - config = [{'users': {'gareth': {'time': {'end': '09-22-2017 5pm', - 'start': '09-22-2017 3pm'}}}} - ] + with patch("datetime.datetime", MagicMock()), patch( + "datetime.datetime.now", MagicMock(return_value=mock_now) + ): + with patch("struct.unpack", MagicMock(return_value=pack)): + config = [ + { + "users": { + "gareth": { + "time": { + "end": "09-22-2017 5pm", + "start": "09-22-2017 3pm", + } + } + } + } + ] ret = wtmp.validate(config) - self.assertEqual(ret, (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected = [{'PID': 6216, - 'action': 'login', - 'line': 'pts/14', - 'session': 0, - 'time': 0, - 'exit_status': 0, - 'inittab': 's/14', - 'type': 7, - 'addr': 1506101523, - 'hostname': '::1', - 'user': 'gareth'}] + _expected = [ + { + "PID": 6216, + "action": "login", + "line": "pts/14", + "session": 0, + "time": 0, + "exit_status": 0, + "inittab": "s/14", + "type": 7, + "addr": 1506101523, + "hostname": "::1", + "user": "gareth", + } + ] ret = wtmp.beacon(config) self.assertEqual(ret, _expected) def test_match_group(self): - for groupadd in ('salt.modules.aix_group', - 'salt.modules.mac_group', - 'salt.modules.pw_group', - 'salt.modules.solaris_group', - 'salt.modules.win_groupadd'): - mock_group_info = {'passwd': 'x', - 'gid': 100, - 'name': 'users', - 'members': ['gareth']} + for groupadd in ( + "salt.modules.aix_group", + "salt.modules.mac_group", + "salt.modules.pw_group", + "salt.modules.solaris_group", + "salt.modules.win_groupadd", + ): + mock_group_info = { + "passwd": "x", + "gid": 100, + "name": "users", + "members": ["gareth"], + } - with patch('salt.utils.files.fopen', - mock_open(read_data=raw)): - with patch('time.time', - MagicMock(return_value=1506121200)): - with patch('struct.unpack', - MagicMock(return_value=pack)): - with patch('{0}.info'.format(groupadd), - new=MagicMock(return_value=mock_group_info)): - config = [{'group': {'users': {'time': {'end': '09-22-2017 5pm', - 'start': '09-22-2017 3pm'}}}} - ] + with patch("salt.utils.files.fopen", mock_open(read_data=raw)): + with patch("time.time", MagicMock(return_value=1506121200)): + with patch("struct.unpack", MagicMock(return_value=pack)): + with patch( + "{0}.info".format(groupadd), + new=MagicMock(return_value=mock_group_info), + ): + config = [ + { + "group": { + "users": { + "time": { + "end": "09-22-2017 5pm", + "start": "09-22-2017 3pm", + } + } + } + } + ] ret = wtmp.validate(config) - self.assertEqual(ret, - (True, 'Valid beacon configuration')) + self.assertEqual(ret, (True, "Valid beacon configuration")) - _expected = [{'PID': 6216, - 'action': 'login', - 'line': 'pts/14', - 'session': 0, - 'time': 0, - 'exit_status': 0, - 'inittab': 's/14', - 'type': 7, - 'addr': 1506101523, - 'hostname': '::1', - 'user': 'gareth'}] + _expected = [ + { + "PID": 6216, + "action": "login", + "line": "pts/14", + "session": 0, + "time": 0, + "exit_status": 0, + "inittab": "s/14", + "type": 7, + "addr": 1506101523, + "hostname": "::1", + "user": "gareth", + } + ] ret = wtmp.beacon(config) self.assertEqual(ret, _expected) diff --git a/tests/unit/cache/test_cache.py b/tests/unit/cache/test_cache.py index 81698d5f6b5..a7ef445b3a0 100644 --- a/tests/unit/cache/test_cache.py +++ b/tests/unit/cache/test_cache.py @@ -1,213 +1,237 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for salt.cache -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -# import integration -from tests.support.unit import TestCase -from tests.support.mock import patch +import salt.cache # Import Salt libs import salt.payload -import salt.cache +from tests.support.mock import patch + +# Import Salt Testing libs +# import integration +from tests.support.unit import TestCase class CacheFunctionsTest(TestCase): - ''' + """ Validate the cache package functions. - ''' + """ + def setUp(self): - self.opts = {'cache': 'localfs', - 'memcache_expire_seconds': 0, - 'memcache_max_items': 0, - 'memcache_full_cleanup': False, - 'memcache_debug': False} + self.opts = { + "cache": "localfs", + "memcache_expire_seconds": 0, + "memcache_max_items": 0, + "memcache_full_cleanup": False, + "memcache_debug": False, + } def test_factory_cache(self): ret = salt.cache.factory(self.opts) self.assertIsInstance(ret, salt.cache.Cache) def test_factory_memcache(self): - self.opts['memcache_expire_seconds'] = 10 + self.opts["memcache_expire_seconds"] = 10 ret = salt.cache.factory(self.opts) self.assertIsInstance(ret, salt.cache.MemCache) class MemCacheTest(TestCase): - ''' + """ Validate Cache class methods - ''' - @patch('salt.payload.Serial') + """ + + @patch("salt.payload.Serial") def setUp(self, serial_mock): # pylint: disable=W0221 salt.cache.MemCache.data = {} - self.opts = {'cache': 'fake_driver', - 'memcache_expire_seconds': 10, - 'memcache_max_items': 3, - 'memcache_full_cleanup': False, - 'memcache_debug': False} + self.opts = { + "cache": "fake_driver", + "memcache_expire_seconds": 10, + "memcache_max_items": 3, + "memcache_full_cleanup": False, + "memcache_debug": False, + } self.cache = salt.cache.factory(self.opts) - @patch('salt.cache.Cache.fetch', return_value='fake_data') - @patch('salt.loader.cache', return_value={}) + @patch("salt.cache.Cache.fetch", return_value="fake_data") + @patch("salt.loader.cache", return_value={}) def test_fetch(self, loader_mock, cache_fetch_mock): # Fetch value, it will be kept in cache. - with patch('time.time', return_value=0): - ret = self.cache.fetch('bank', 'key') - self.assertEqual(ret, 'fake_data') - self.assertDictEqual(salt.cache.MemCache.data, { - 'fake_driver': { - ('bank', 'key'): [0, 'fake_data'], - }}) - cache_fetch_mock.assert_called_once_with('bank', 'key') + with patch("time.time", return_value=0): + ret = self.cache.fetch("bank", "key") + self.assertEqual(ret, "fake_data") + self.assertDictEqual( + salt.cache.MemCache.data, + {"fake_driver": {("bank", "key"): [0, "fake_data"]}}, + ) + cache_fetch_mock.assert_called_once_with("bank", "key") cache_fetch_mock.reset_mock() # Fetch again, cached value is used, time updated. - with patch('time.time', return_value=1): - ret = self.cache.fetch('bank', 'key') - self.assertEqual(ret, 'fake_data') - self.assertDictEqual(salt.cache.MemCache.data, { - 'fake_driver': { - ('bank', 'key'): [1, 'fake_data'], - }}) + with patch("time.time", return_value=1): + ret = self.cache.fetch("bank", "key") + self.assertEqual(ret, "fake_data") + self.assertDictEqual( + salt.cache.MemCache.data, + {"fake_driver": {("bank", "key"): [1, "fake_data"]}}, + ) cache_fetch_mock.assert_not_called() # Fetch after expire - with patch('time.time', return_value=12): - ret = self.cache.fetch('bank', 'key') - self.assertEqual(ret, 'fake_data') - self.assertDictEqual(salt.cache.MemCache.data, { - 'fake_driver': { - ('bank', 'key'): [12, 'fake_data'], - }}) - cache_fetch_mock.assert_called_once_with('bank', 'key') + with patch("time.time", return_value=12): + ret = self.cache.fetch("bank", "key") + self.assertEqual(ret, "fake_data") + self.assertDictEqual( + salt.cache.MemCache.data, + {"fake_driver": {("bank", "key"): [12, "fake_data"]}}, + ) + cache_fetch_mock.assert_called_once_with("bank", "key") cache_fetch_mock.reset_mock() - @patch('salt.cache.Cache.store') - @patch('salt.loader.cache', return_value={}) + @patch("salt.cache.Cache.store") + @patch("salt.loader.cache", return_value={}) def test_store(self, loader_mock, cache_store_mock): # Fetch value, it will be kept in cache. - with patch('time.time', return_value=0): - self.cache.store('bank', 'key', 'fake_data') - self.assertDictEqual(salt.cache.MemCache.data, { - 'fake_driver': { - ('bank', 'key'): [0, 'fake_data'], - }}) - cache_store_mock.assert_called_once_with('bank', 'key', 'fake_data') + with patch("time.time", return_value=0): + self.cache.store("bank", "key", "fake_data") + self.assertDictEqual( + salt.cache.MemCache.data, + {"fake_driver": {("bank", "key"): [0, "fake_data"]}}, + ) + cache_store_mock.assert_called_once_with("bank", "key", "fake_data") cache_store_mock.reset_mock() # Store another value. - with patch('time.time', return_value=1): - self.cache.store('bank', 'key2', 'fake_data2') - self.assertDictEqual(salt.cache.MemCache.data, { - 'fake_driver': { - ('bank', 'key'): [0, 'fake_data'], - ('bank', 'key2'): [1, 'fake_data2'], - }}) - cache_store_mock.assert_called_once_with('bank', 'key2', 'fake_data2') + with patch("time.time", return_value=1): + self.cache.store("bank", "key2", "fake_data2") + self.assertDictEqual( + salt.cache.MemCache.data, + { + "fake_driver": { + ("bank", "key"): [0, "fake_data"], + ("bank", "key2"): [1, "fake_data2"], + } + }, + ) + cache_store_mock.assert_called_once_with("bank", "key2", "fake_data2") - @patch('salt.cache.Cache.store') - @patch('salt.cache.Cache.flush') - @patch('salt.loader.cache', return_value={}) + @patch("salt.cache.Cache.store") + @patch("salt.cache.Cache.flush") + @patch("salt.loader.cache", return_value={}) def test_flush(self, loader_mock, cache_flush_mock, cache_store_mock): # Flush non-existing bank - self.cache.flush('bank') - self.assertDictEqual(salt.cache.MemCache.data, {'fake_driver': {}}) - cache_flush_mock.assert_called_once_with('bank', None) + self.cache.flush("bank") + self.assertDictEqual(salt.cache.MemCache.data, {"fake_driver": {}}) + cache_flush_mock.assert_called_once_with("bank", None) cache_flush_mock.reset_mock() # Flush non-existing key - self.cache.flush('bank', 'key') - self.assertDictEqual(salt.cache.MemCache.data, {'fake_driver': {}}) - cache_flush_mock.assert_called_once_with('bank', 'key') + self.cache.flush("bank", "key") + self.assertDictEqual(salt.cache.MemCache.data, {"fake_driver": {}}) + cache_flush_mock.assert_called_once_with("bank", "key") cache_flush_mock.reset_mock() # Flush existing key - with patch('time.time', return_value=0): - self.cache.store('bank', 'key', 'fake_data') - self.assertEqual(salt.cache.MemCache.data['fake_driver'][('bank', 'key')], - [0, 'fake_data']) - self.assertDictEqual(salt.cache.MemCache.data, { - 'fake_driver': { - ('bank', 'key'): [0, 'fake_data'], - }}) - self.cache.flush('bank', 'key') - self.assertDictEqual(salt.cache.MemCache.data, {'fake_driver': {}}) - cache_flush_mock.assert_called_once_with('bank', 'key') + with patch("time.time", return_value=0): + self.cache.store("bank", "key", "fake_data") + self.assertEqual( + salt.cache.MemCache.data["fake_driver"][("bank", "key")], [0, "fake_data"] + ) + self.assertDictEqual( + salt.cache.MemCache.data, + {"fake_driver": {("bank", "key"): [0, "fake_data"]}}, + ) + self.cache.flush("bank", "key") + self.assertDictEqual(salt.cache.MemCache.data, {"fake_driver": {}}) + cache_flush_mock.assert_called_once_with("bank", "key") cache_flush_mock.reset_mock() - @patch('salt.cache.Cache.store') - @patch('salt.loader.cache', return_value={}) + @patch("salt.cache.Cache.store") + @patch("salt.loader.cache", return_value={}) def test_max_items(self, loader_mock, cache_store_mock): # Put MAX=3 values - with patch('time.time', return_value=0): - self.cache.store('bank1', 'key1', 'fake_data11') - with patch('time.time', return_value=1): - self.cache.store('bank1', 'key2', 'fake_data12') - with patch('time.time', return_value=2): - self.cache.store('bank2', 'key1', 'fake_data21') - self.assertDictEqual(salt.cache.MemCache.data['fake_driver'], { - ('bank1', 'key1'): [0, 'fake_data11'], - ('bank1', 'key2'): [1, 'fake_data12'], - ('bank2', 'key1'): [2, 'fake_data21'], - }) + with patch("time.time", return_value=0): + self.cache.store("bank1", "key1", "fake_data11") + with patch("time.time", return_value=1): + self.cache.store("bank1", "key2", "fake_data12") + with patch("time.time", return_value=2): + self.cache.store("bank2", "key1", "fake_data21") + self.assertDictEqual( + salt.cache.MemCache.data["fake_driver"], + { + ("bank1", "key1"): [0, "fake_data11"], + ("bank1", "key2"): [1, "fake_data12"], + ("bank2", "key1"): [2, "fake_data21"], + }, + ) # Put one more and check the oldest was removed - with patch('time.time', return_value=3): - self.cache.store('bank2', 'key2', 'fake_data22') - self.assertDictEqual(salt.cache.MemCache.data['fake_driver'], { - ('bank1', 'key2'): [1, 'fake_data12'], - ('bank2', 'key1'): [2, 'fake_data21'], - ('bank2', 'key2'): [3, 'fake_data22'], - }) + with patch("time.time", return_value=3): + self.cache.store("bank2", "key2", "fake_data22") + self.assertDictEqual( + salt.cache.MemCache.data["fake_driver"], + { + ("bank1", "key2"): [1, "fake_data12"], + ("bank2", "key1"): [2, "fake_data21"], + ("bank2", "key2"): [3, "fake_data22"], + }, + ) - @patch('salt.cache.Cache.store') - @patch('salt.loader.cache', return_value={}) + @patch("salt.cache.Cache.store") + @patch("salt.loader.cache", return_value={}) def test_full_cleanup(self, loader_mock, cache_store_mock): # Enable full cleanup self.cache.cleanup = True # Put MAX=3 values - with patch('time.time', return_value=0): - self.cache.store('bank1', 'key1', 'fake_data11') - with patch('time.time', return_value=1): - self.cache.store('bank1', 'key2', 'fake_data12') - with patch('time.time', return_value=2): - self.cache.store('bank2', 'key1', 'fake_data21') - self.assertDictEqual(salt.cache.MemCache.data['fake_driver'], { - ('bank1', 'key1'): [0, 'fake_data11'], - ('bank1', 'key2'): [1, 'fake_data12'], - ('bank2', 'key1'): [2, 'fake_data21'], - }) + with patch("time.time", return_value=0): + self.cache.store("bank1", "key1", "fake_data11") + with patch("time.time", return_value=1): + self.cache.store("bank1", "key2", "fake_data12") + with patch("time.time", return_value=2): + self.cache.store("bank2", "key1", "fake_data21") + self.assertDictEqual( + salt.cache.MemCache.data["fake_driver"], + { + ("bank1", "key1"): [0, "fake_data11"], + ("bank1", "key2"): [1, "fake_data12"], + ("bank2", "key1"): [2, "fake_data21"], + }, + ) # Put one more and check all expired was removed - with patch('time.time', return_value=12): - self.cache.store('bank2', 'key2', 'fake_data22') - self.assertDictEqual(salt.cache.MemCache.data['fake_driver'], { - ('bank2', 'key1'): [2, 'fake_data21'], - ('bank2', 'key2'): [12, 'fake_data22'], - }) + with patch("time.time", return_value=12): + self.cache.store("bank2", "key2", "fake_data22") + self.assertDictEqual( + salt.cache.MemCache.data["fake_driver"], + { + ("bank2", "key1"): [2, "fake_data21"], + ("bank2", "key2"): [12, "fake_data22"], + }, + ) - @patch('salt.cache.Cache.fetch', return_value='fake_data') - @patch('salt.loader.cache', return_value={}) + @patch("salt.cache.Cache.fetch", return_value="fake_data") + @patch("salt.loader.cache", return_value={}) def test_fetch_debug(self, loader_mock, cache_fetch_mock): # Recreate cache with debug enabled - self.opts['memcache_debug'] = True + self.opts["memcache_debug"] = True self.cache = salt.cache.factory(self.opts) # Fetch 2 values (no cache hit) - with patch('time.time', return_value=0): - ret = self.cache.fetch('bank', 'key1') - with patch('time.time', return_value=1): - ret = self.cache.fetch('bank', 'key2') + with patch("time.time", return_value=0): + ret = self.cache.fetch("bank", "key1") + with patch("time.time", return_value=1): + ret = self.cache.fetch("bank", "key2") # Fetch 3 times (cache hit) - with patch('time.time', return_value=2): - ret = self.cache.fetch('bank', 'key2') - with patch('time.time', return_value=3): - ret = self.cache.fetch('bank', 'key1') - with patch('time.time', return_value=4): - ret = self.cache.fetch('bank', 'key1') + with patch("time.time", return_value=2): + ret = self.cache.fetch("bank", "key2") + with patch("time.time", return_value=3): + ret = self.cache.fetch("bank", "key1") + with patch("time.time", return_value=4): + ret = self.cache.fetch("bank", "key1") # Fetch an expired value (no cache hit) - with patch('time.time', return_value=13): - ret = self.cache.fetch('bank', 'key2') + with patch("time.time", return_value=13): + ret = self.cache.fetch("bank", "key2") # Check debug data self.assertEqual(self.cache.call, 6) diff --git a/tests/unit/cache/test_localfs.py b/tests/unit/cache/test_localfs.py index 579dfd468be..e733ac46b8a 100644 --- a/tests/unit/cache/test_localfs.py +++ b/tests/unit/cache/test_localfs.py @@ -1,94 +1,110 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the localfs cache -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import shutil import tempfile -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import salt.cache.localfs as localfs # Import Salt libs import salt.payload import salt.utils.files -import salt.cache.localfs as localfs from salt.exceptions import SaltCacheError +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class LocalFSTest(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the functions in the localfs cache - ''' + """ def setup_loader_modules(self): return {localfs: {}} def _create_tmp_cache_file(self, tmp_dir, serializer): - ''' + """ Helper function that creates a temporary cache file using localfs.store. This is to used to create DRY unit tests for the localfs cache. - ''' + """ self.addCleanup(shutil.rmtree, tmp_dir) - with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}): - with patch.dict(localfs.__context__, {'serial': serializer}): - localfs.store(bank='bank', key='key', data='payload data', cachedir=tmp_dir) + with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}): + with patch.dict(localfs.__context__, {"serial": serializer}): + localfs.store( + bank="bank", key="key", data="payload data", cachedir=tmp_dir + ) # 'store' function tests: 5 def test_handled_exception_cache_dir(self): - ''' + """ Tests that a SaltCacheError is raised when the base directory doesn't exist and cannot be created. - ''' - with patch('os.makedirs', MagicMock(side_effect=OSError(errno.EEXIST, ''))): - with patch('tempfile.mkstemp', MagicMock(side_effect=Exception)): - self.assertRaises(Exception, localfs.store, bank='', key='', data='', cachedir='') + """ + with patch("os.makedirs", MagicMock(side_effect=OSError(errno.EEXIST, ""))): + with patch("tempfile.mkstemp", MagicMock(side_effect=Exception)): + self.assertRaises( + Exception, localfs.store, bank="", key="", data="", cachedir="" + ) def test_unhandled_exception_cache_dir(self): - ''' + """ Tests that a SaltCacheError is raised when the base directory doesn't exist and cannot be created. - ''' - with patch('os.makedirs', MagicMock(side_effect=OSError(1, ''))): - self.assertRaises(SaltCacheError, localfs.store, bank='', key='', data='', cachedir='') + """ + with patch("os.makedirs", MagicMock(side_effect=OSError(1, ""))): + self.assertRaises( + SaltCacheError, localfs.store, bank="", key="", data="", cachedir="" + ) def test_store_close_mkstemp_file_handle(self): - ''' + """ Tests that the file descriptor that is opened by os.open during the mkstemp call in localfs.store is closed before calling salt.utils.files.fopen on the filename. This test mocks the call to mkstemp, but forces an OSError to be raised when the close() function is called on a file descriptor that doesn't exist. - ''' - with patch('os.makedirs', MagicMock(side_effect=OSError(errno.EEXIST, ''))): - with patch('tempfile.mkstemp', MagicMock(return_value=(12345, 'foo'))): - self.assertRaises(OSError, localfs.store, bank='', key='', data='', cachedir='') + """ + with patch("os.makedirs", MagicMock(side_effect=OSError(errno.EEXIST, ""))): + with patch("tempfile.mkstemp", MagicMock(return_value=(12345, "foo"))): + self.assertRaises( + OSError, localfs.store, bank="", key="", data="", cachedir="" + ) def test_store_error_writing_cache(self): - ''' + """ Tests that a SaltCacheError is raised when there is a problem writing to the cache file. - ''' - with patch('os.makedirs', MagicMock(side_effect=OSError(errno.EEXIST, ''))): - with patch('tempfile.mkstemp', MagicMock(return_value=('one', 'two'))): - with patch('os.close', MagicMock(return_value=None)): - with patch('salt.utils.files.fopen', MagicMock(side_effect=IOError)): - self.assertRaises(SaltCacheError, localfs.store, bank='', key='', data='', cachedir='') + """ + with patch("os.makedirs", MagicMock(side_effect=OSError(errno.EEXIST, ""))): + with patch("tempfile.mkstemp", MagicMock(return_value=("one", "two"))): + with patch("os.close", MagicMock(return_value=None)): + with patch( + "salt.utils.files.fopen", MagicMock(side_effect=IOError) + ): + self.assertRaises( + SaltCacheError, + localfs.store, + bank="", + key="", + data="", + cachedir="", + ) def test_store_success(self): - ''' + """ Tests that the store function writes the data to the serializer for storage. - ''' + """ # Create a temporary cache dir tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -96,33 +112,35 @@ class LocalFSTest(TestCase, LoaderModuleMockMixin): self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) # Read in the contents of the key.p file and assert "payload data" was written - with salt.utils.files.fopen(tmp_dir + '/bank/key.p', 'rb') as fh_: + with salt.utils.files.fopen(tmp_dir + "/bank/key.p", "rb") as fh_: for line in fh_: - self.assertIn(b'payload data', line) + self.assertIn(b"payload data", line) # 'fetch' function tests: 3 def test_fetch_return_when_cache_file_does_not_exist(self): - ''' + """ Tests that the fetch function returns an empty dic when the cache key file doesn't exist. - ''' - with patch('os.path.isfile', MagicMock(return_value=False)): - self.assertEqual(localfs.fetch(bank='', key='', cachedir=''), {}) + """ + with patch("os.path.isfile", MagicMock(return_value=False)): + self.assertEqual(localfs.fetch(bank="", key="", cachedir=""), {}) def test_fetch_error_reading_cache(self): - ''' + """ Tests that a SaltCacheError is raised when there is a problem reading the cache file. - ''' - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('salt.utils.files.fopen', MagicMock(side_effect=IOError)): - self.assertRaises(SaltCacheError, localfs.fetch, bank='', key='', cachedir='') + """ + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("salt.utils.files.fopen", MagicMock(side_effect=IOError)): + self.assertRaises( + SaltCacheError, localfs.fetch, bank="", key="", cachedir="" + ) def test_fetch_success(self): - ''' + """ Tests that the fetch function is able to read the cache file and return its data. - ''' + """ # Create a temporary cache dir tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -133,66 +151,73 @@ class LocalFSTest(TestCase, LoaderModuleMockMixin): self._create_tmp_cache_file(tmp_dir, serializer) # Now fetch the data from the new cache key file - with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}): - with patch.dict(localfs.__context__, {'serial': serializer}): - self.assertIn('payload data', localfs.fetch(bank='bank', key='key', cachedir=tmp_dir)) + with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}): + with patch.dict(localfs.__context__, {"serial": serializer}): + self.assertIn( + "payload data", + localfs.fetch(bank="bank", key="key", cachedir=tmp_dir), + ) # 'updated' function tests: 3 def test_updated_return_when_cache_file_does_not_exist(self): - ''' + """ Tests that the updated function returns None when the cache key file doesn't exist. - ''' - with patch('os.path.isfile', MagicMock(return_value=False)): - self.assertIsNone(localfs.updated(bank='', key='', cachedir='')) + """ + with patch("os.path.isfile", MagicMock(return_value=False)): + self.assertIsNone(localfs.updated(bank="", key="", cachedir="")) def test_updated_error_when_reading_mtime(self): - ''' + """ Tests that a SaltCacheError is raised when there is a problem reading the mtime of the cache file. - ''' - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.path.getmtime', MagicMock(side_effect=IOError)): - self.assertRaises(SaltCacheError, localfs.updated, bank='', key='', cachedir='') + """ + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.path.getmtime", MagicMock(side_effect=IOError)): + self.assertRaises( + SaltCacheError, localfs.updated, bank="", key="", cachedir="" + ) def test_updated_success(self): - ''' + """ Test that the updated function returns the modification time of the cache file - ''' + """ # Create a temporary cache dir tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) # Use the helper function to create the cache file using localfs.store() self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) - with patch('os.path.join', MagicMock(return_value=tmp_dir + '/bank/key.p')): - self.assertIsInstance(localfs.updated(bank='bank', key='key', cachedir=tmp_dir), int) + with patch("os.path.join", MagicMock(return_value=tmp_dir + "/bank/key.p")): + self.assertIsInstance( + localfs.updated(bank="bank", key="key", cachedir=tmp_dir), int + ) # 'flush' function tests: 4 def test_flush_key_is_none_and_no_target_dir(self): - ''' + """ Tests that the flush function returns False when no key is passed in and the target directory doesn't exist. - ''' - with patch('os.path.isdir', MagicMock(return_value=False)): - self.assertFalse(localfs.flush(bank='', key=None, cachedir='')) + """ + with patch("os.path.isdir", MagicMock(return_value=False)): + self.assertFalse(localfs.flush(bank="", key=None, cachedir="")) def test_flush_key_provided_and_no_key_file_false(self): - ''' + """ Tests that the flush function returns False when a key file is provided but the target key file doesn't exist in the cache bank. - ''' - with patch('os.path.isfile', MagicMock(return_value=False)): - self.assertFalse(localfs.flush(bank='', key='key', cachedir='')) + """ + with patch("os.path.isfile", MagicMock(return_value=False)): + self.assertFalse(localfs.flush(bank="", key="key", cachedir="")) def test_flush_success(self): - ''' + """ Tests that the flush function returns True when a key file is provided and the target key exists in the cache bank. - ''' - with patch('os.path.isfile', MagicMock(return_value=True)): + """ + with patch("os.path.isfile", MagicMock(return_value=True)): # Create a temporary cache dir tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -200,41 +225,47 @@ class LocalFSTest(TestCase, LoaderModuleMockMixin): self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) # Now test the return of the flush function - with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}): - self.assertTrue(localfs.flush(bank='bank', key='key', cachedir=tmp_dir)) + with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}): + self.assertTrue(localfs.flush(bank="bank", key="key", cachedir=tmp_dir)) def test_flush_error_raised(self): - ''' + """ Tests that a SaltCacheError is raised when there is a problem removing the key file from the cache bank - ''' - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.remove', MagicMock(side_effect=OSError)): - self.assertRaises(SaltCacheError, localfs.flush, bank='', key='key', cachedir='/var/cache/salt') + """ + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.remove", MagicMock(side_effect=OSError)): + self.assertRaises( + SaltCacheError, + localfs.flush, + bank="", + key="key", + cachedir="/var/cache/salt", + ) # 'list' function tests: 3 def test_list_no_base_dir(self): - ''' + """ Tests that the ls function returns an empty list if the bank directory doesn't exist. - ''' - with patch('os.path.isdir', MagicMock(return_value=False)): - self.assertEqual(localfs.list_(bank='', cachedir=''), []) + """ + with patch("os.path.isdir", MagicMock(return_value=False)): + self.assertEqual(localfs.list_(bank="", cachedir=""), []) def test_list_error_raised_no_bank_directory_access(self): - ''' + """ Tests that a SaltCacheError is raised when there is a problem accessing the cache bank directory. - ''' - with patch('os.path.isdir', MagicMock(return_value=True)): - with patch('os.listdir', MagicMock(side_effect=OSError)): - self.assertRaises(SaltCacheError, localfs.list_, bank='', cachedir='') + """ + with patch("os.path.isdir", MagicMock(return_value=True)): + with patch("os.listdir", MagicMock(side_effect=OSError)): + self.assertRaises(SaltCacheError, localfs.list_, bank="", cachedir="") def test_list_success(self): - ''' + """ Tests the return of the ls function containing bank entries. - ''' + """ # Create a temporary cache dir tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -242,16 +273,16 @@ class LocalFSTest(TestCase, LoaderModuleMockMixin): self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) # Now test the return of the ls function - with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}): - self.assertEqual(localfs.list_(bank='bank', cachedir=tmp_dir), ['key']) + with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}): + self.assertEqual(localfs.list_(bank="bank", cachedir=tmp_dir), ["key"]) # 'contains' function tests: 1 def test_contains(self): - ''' + """ Test the return of the contains function when key=None and when a key is provided. - ''' + """ # Create a temporary cache dir tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -259,9 +290,9 @@ class LocalFSTest(TestCase, LoaderModuleMockMixin): self._create_tmp_cache_file(tmp_dir, salt.payload.Serial(self)) # Now test the return of the contains function when key=None - with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}): - self.assertTrue(localfs.contains(bank='bank', key=None, cachedir=tmp_dir)) + with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}): + self.assertTrue(localfs.contains(bank="bank", key=None, cachedir=tmp_dir)) # Now test the return of the contains function when key='key' - with patch.dict(localfs.__opts__, {'cachedir': tmp_dir}): - self.assertTrue(localfs.contains(bank='bank', key='key', cachedir=tmp_dir)) + with patch.dict(localfs.__opts__, {"cachedir": tmp_dir}): + self.assertTrue(localfs.contains(bank="bank", key="key", cachedir=tmp_dir)) diff --git a/tests/unit/cli/test_batch.py b/tests/unit/cli/test_batch.py index acabbe51f56..27d6fff065c 100644 --- a/tests/unit/cli/test_batch.py +++ b/tests/unit/cli/test_batch.py @@ -1,74 +1,76 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs from salt.cli.batch import Batch +from tests.support.mock import MagicMock, patch # Import Salt Testing Libs from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch class BatchTestCase(TestCase): - ''' + """ Unit Tests for the salt.cli.batch module - ''' + """ def setUp(self): - opts = {'batch': '', - 'conf_file': {}, - 'tgt': '', - 'transport': '', - 'timeout': 5, - 'gather_job_timeout': 5} + opts = { + "batch": "", + "conf_file": {}, + "tgt": "", + "transport": "", + "timeout": 5, + "gather_job_timeout": 5, + } mock_client = MagicMock() - with patch('salt.client.get_local_client', MagicMock(return_value=mock_client)): - with patch('salt.client.LocalClient.cmd_iter', MagicMock(return_value=[])): - self.batch = Batch(opts, quiet='quiet') + with patch("salt.client.get_local_client", MagicMock(return_value=mock_client)): + with patch("salt.client.LocalClient.cmd_iter", MagicMock(return_value=[])): + self.batch = Batch(opts, quiet="quiet") # get_bnum tests def test_get_bnum_str(self): - ''' + """ Tests passing batch value as a number(str) - ''' - self.batch.opts = {'batch': '2', 'timeout': 5} - self.batch.minions = ['foo', 'bar'] + """ + self.batch.opts = {"batch": "2", "timeout": 5} + self.batch.minions = ["foo", "bar"] self.assertEqual(Batch.get_bnum(self.batch), 2) def test_get_bnum_int(self): - ''' + """ Tests passing batch value as a number(int) - ''' - self.batch.opts = {'batch': 2, 'timeout': 5} - self.batch.minions = ['foo', 'bar'] + """ + self.batch.opts = {"batch": 2, "timeout": 5} + self.batch.minions = ["foo", "bar"] self.assertEqual(Batch.get_bnum(self.batch), 2) def test_get_bnum_percentage(self): - ''' + """ Tests passing batch value as percentage - ''' - self.batch.opts = {'batch': '50%', 'timeout': 5} - self.batch.minions = ['foo'] + """ + self.batch.opts = {"batch": "50%", "timeout": 5} + self.batch.minions = ["foo"] self.assertEqual(Batch.get_bnum(self.batch), 1) def test_get_bnum_high_percentage(self): - ''' + """ Tests passing batch value as percentage over 100% - ''' - self.batch.opts = {'batch': '160%', 'timeout': 5} - self.batch.minions = ['foo', 'bar', 'baz'] + """ + self.batch.opts = {"batch": "160%", "timeout": 5} + self.batch.minions = ["foo", "bar", "baz"] self.assertEqual(Batch.get_bnum(self.batch), 4) def test_get_bnum_invalid_batch_data(self): - ''' + """ Tests when an invalid batch value is passed - ''' + """ ret = Batch.get_bnum(self.batch) self.assertEqual(ret, None) diff --git a/tests/unit/cli/test_daemons.py b/tests/unit/cli/test_daemons.py index afa744ddf93..ae207bac1ec 100644 --- a/tests/unit/cli/test_daemons.py +++ b/tests/unit/cli/test_daemons.py @@ -1,74 +1,79 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Bo Maryniuk <bo@suse.de> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import multiprocessing -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import SaltClientTestCaseMixin - # Import Salt libs import salt.cli.daemons as daemons +from tests.support.mixins import SaltClientTestCaseMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase log = logging.getLogger(__name__) class LoggerMock(object): - ''' + """ Logger data collector - ''' + """ def __init__(self): - ''' + """ init :return: - ''' + """ self.reset() def reset(self): - ''' + """ Reset values :return: - ''' + """ self.messages = [] def info(self, message, *args, **kwargs): - ''' + """ Collects the data from the logger of info type. :param data: :return: - ''' - self.messages.append({'message': message, 'args': args, - 'kwargs': kwargs, 'type': 'info'}) + """ + self.messages.append( + {"message": message, "args": args, "kwargs": kwargs, "type": "info"} + ) def warning(self, message, *args, **kwargs): - ''' + """ Collects the data from the logger of warning type. :param data: :return: - ''' - self.messages.append({'message': message, 'args': args, - 'kwargs': kwargs, 'type': 'warning'}) + """ + self.messages.append( + {"message": message, "args": args, "kwargs": kwargs, "type": "warning"} + ) def has_message(self, msg, log_type=None): - ''' + """ Check if log has message. :param data: :return: - ''' + """ for data in self.messages: - log_str = data['message'] % data['args'] # pylint: disable=incompatible-py3-code - if (data['type'] == log_type or not log_type) and log_str.find(msg) > -1: + log_str = ( + data["message"] % data["args"] + ) # pylint: disable=incompatible-py3-code + if (data["type"] == log_type or not log_type) and log_str.find(msg) > -1: return True return False @@ -76,13 +81,13 @@ class LoggerMock(object): def _master_exec_test(child_pipe): def _create_master(): - ''' + """ Create master instance :return: - ''' + """ obj = daemons.Master() - obj.config = {'user': 'dummy', 'hash_type': alg} - for attr in ['start_log_info', 'prepare', 'shutdown', 'master']: + obj.config = {"user": "dummy", "hash_type": alg} + for attr in ["start_log_info", "prepare", "shutdown", "master"]: setattr(obj, attr, MagicMock()) return obj @@ -90,22 +95,25 @@ def _master_exec_test(child_pipe): _logger = LoggerMock() ret = True try: - with patch('salt.cli.daemons.check_user', MagicMock(return_value=True)): - with patch('salt.cli.daemons.log', _logger): - for alg in ['md5', 'sha1']: + with patch("salt.cli.daemons.check_user", MagicMock(return_value=True)): + with patch("salt.cli.daemons.log", _logger): + for alg in ["md5", "sha1"]: _create_master().start() ret = ret and _logger.has_message( - 'Do not use {alg}'.format(alg=alg), - log_type='warning') + "Do not use {alg}".format(alg=alg), log_type="warning" + ) _logger.reset() - for alg in ['sha224', 'sha256', 'sha384', 'sha512']: + for alg in ["sha224", "sha256", "sha384", "sha512"]: _create_master().start() - ret = ret and _logger.messages \ - and not _logger.has_message('Do not use ') + ret = ( + ret + and _logger.messages + and not _logger.has_message("Do not use ") + ) except Exception: # pylint: disable=broad-except - log.exception('Exception raised in master daemon unit test') + log.exception("Exception raised in master daemon unit test") ret = False child_pipe.send(ret) child_pipe.close() @@ -113,36 +121,39 @@ def _master_exec_test(child_pipe): def _minion_exec_test(child_pipe): def _create_minion(): - ''' + """ Create minion instance :return: - ''' + """ obj = daemons.Minion() - obj.config = {'user': 'dummy', 'hash_type': alg} - for attr in ['start_log_info', 'prepare', 'shutdown']: + obj.config = {"user": "dummy", "hash_type": alg} + for attr in ["start_log_info", "prepare", "shutdown"]: setattr(obj, attr, MagicMock()) - setattr(obj, 'minion', MagicMock(restart=False)) + setattr(obj, "minion", MagicMock(restart=False)) return obj ret = True try: _logger = LoggerMock() - with patch('salt.cli.daemons.check_user', MagicMock(return_value=True)): - with patch('salt.cli.daemons.log', _logger): - for alg in ['md5', 'sha1']: + with patch("salt.cli.daemons.check_user", MagicMock(return_value=True)): + with patch("salt.cli.daemons.log", _logger): + for alg in ["md5", "sha1"]: _create_minion().start() ret = ret and _logger.has_message( - 'Do not use {alg}'.format(alg=alg), - log_type='warning') + "Do not use {alg}".format(alg=alg), log_type="warning" + ) _logger.reset() - for alg in ['sha224', 'sha256', 'sha384', 'sha512']: + for alg in ["sha224", "sha256", "sha384", "sha512"]: _create_minion().start() - ret = ret and _logger.messages \ - and not _logger.has_message('Do not use ') + ret = ( + ret + and _logger.messages + and not _logger.has_message("Do not use ") + ) except Exception: # pylint: disable=broad-except - log.exception('Exception raised in minion daemon unit test') + log.exception("Exception raised in minion daemon unit test") ret = False child_pipe.send(ret) child_pipe.close() @@ -150,13 +161,13 @@ def _minion_exec_test(child_pipe): def _proxy_exec_test(child_pipe): def _create_proxy_minion(): - ''' + """ Create proxy minion instance :return: - ''' + """ obj = daemons.ProxyMinion() - obj.config = {'user': 'dummy', 'hash_type': alg} - for attr in ['minion', 'start_log_info', 'prepare', 'shutdown', 'tune_in']: + obj.config = {"user": "dummy", "hash_type": alg} + for attr in ["minion", "start_log_info", "prepare", "shutdown", "tune_in"]: setattr(obj, attr, MagicMock()) obj.minion.restart = False @@ -165,22 +176,25 @@ def _proxy_exec_test(child_pipe): ret = True try: _logger = LoggerMock() - with patch('salt.cli.daemons.check_user', MagicMock(return_value=True)): - with patch('salt.cli.daemons.log', _logger): - for alg in ['md5', 'sha1']: + with patch("salt.cli.daemons.check_user", MagicMock(return_value=True)): + with patch("salt.cli.daemons.log", _logger): + for alg in ["md5", "sha1"]: _create_proxy_minion().start() ret = ret and _logger.has_message( - 'Do not use {alg}'.format(alg=alg), - log_type='warning') + "Do not use {alg}".format(alg=alg), log_type="warning" + ) _logger.reset() - for alg in ['sha224', 'sha256', 'sha384', 'sha512']: + for alg in ["sha224", "sha256", "sha384", "sha512"]: _create_proxy_minion().start() - ret = ret and _logger.messages \ - and not _logger.has_message('Do not use ') + ret = ( + ret + and _logger.messages + and not _logger.has_message("Do not use ") + ) except Exception: # pylint: disable=broad-except - log.exception('Exception raised in proxy daemon unit test') + log.exception("Exception raised in proxy daemon unit test") ret = False child_pipe.send(ret) child_pipe.close() @@ -188,13 +202,13 @@ def _proxy_exec_test(child_pipe): def _syndic_exec_test(child_pipe): def _create_syndic(): - ''' + """ Create syndic instance :return: - ''' + """ obj = daemons.Syndic() - obj.config = {'user': 'dummy', 'hash_type': alg} - for attr in ['syndic', 'start_log_info', 'prepare', 'shutdown']: + obj.config = {"user": "dummy", "hash_type": alg} + for attr in ["syndic", "start_log_info", "prepare", "shutdown"]: setattr(obj, attr, MagicMock()) return obj @@ -202,31 +216,34 @@ def _syndic_exec_test(child_pipe): ret = True try: _logger = LoggerMock() - with patch('salt.cli.daemons.check_user', MagicMock(return_value=True)): - with patch('salt.cli.daemons.log', _logger): - for alg in ['md5', 'sha1']: + with patch("salt.cli.daemons.check_user", MagicMock(return_value=True)): + with patch("salt.cli.daemons.log", _logger): + for alg in ["md5", "sha1"]: _create_syndic().start() ret = ret and _logger.has_message( - 'Do not use {alg}'.format(alg=alg), - log_type='warning') + "Do not use {alg}".format(alg=alg), log_type="warning" + ) _logger.reset() - for alg in ['sha224', 'sha256', 'sha384', 'sha512']: + for alg in ["sha224", "sha256", "sha384", "sha512"]: _create_syndic().start() - ret = ret and _logger.messages \ - and not _logger.has_message('Do not use ') + ret = ( + ret + and _logger.messages + and not _logger.has_message("Do not use ") + ) except Exception: # pylint: disable=broad-except - log.exception('Exception raised in syndic daemon unit test') + log.exception("Exception raised in syndic daemon unit test") ret = False child_pipe.send(ret) child_pipe.close() class DaemonsStarterTestCase(TestCase, SaltClientTestCaseMixin): - ''' + """ Unit test for the daemons starter classes. - ''' + """ def _multiproc_exec_test(self, exec_test): m_parent, m_child = multiprocessing.Pipe() @@ -236,33 +253,33 @@ class DaemonsStarterTestCase(TestCase, SaltClientTestCaseMixin): p_.join() def test_master_daemon_hash_type_verified(self): - ''' + """ Verify if Master is verifying hash_type config option. :return: - ''' + """ self._multiproc_exec_test(_master_exec_test) def test_minion_daemon_hash_type_verified(self): - ''' + """ Verify if Minion is verifying hash_type config option. :return: - ''' + """ self._multiproc_exec_test(_minion_exec_test) def test_proxy_minion_daemon_hash_type_verified(self): - ''' + """ Verify if ProxyMinion is verifying hash_type config option. :return: - ''' + """ self._multiproc_exec_test(_proxy_exec_test) def test_syndic_daemon_hash_type_verified(self): - ''' + """ Verify if Syndic is verifying hash_type config option. :return: - ''' + """ self._multiproc_exec_test(_syndic_exec_test) diff --git a/tests/unit/client/ssh/wrapper/test_state.py b/tests/unit/client/ssh/wrapper/test_state.py index a57b68e1505..c23cffe64c4 100644 --- a/tests/unit/client/ssh/wrapper/test_state.py +++ b/tests/unit/client/ssh/wrapper/test_state.py @@ -1,25 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`saltybob <bbaker@saltstack.com` -''' +""" from __future__ import absolute_import -# Import python libs - -# Import Salt Testing libs -from tests.support.unit import TestCase - # Import Salt libs from salt.client.ssh.wrapper import state +# Import Salt Testing libs +from tests.support.unit import TestCase + +# Import python libs + class StateTests(TestCase): def test_parse_mods(self): - ''' + """ Test _parse_mods - ''' - expected = ['a', 'b', 'c', 'd', 'e', 'f'] - mods = 'a,b, c, d,e ,f ' + """ + expected = ["a", "b", "c", "d", "e", "f"] + mods = "a,b, c, d,e ,f " actual = state._parse_mods(mods) self.assertEqual(expected, actual) diff --git a/tests/unit/client/test_ssh.py b/tests/unit/client/test_ssh.py index bc75be4003a..c353f210715 100644 --- a/tests/unit/client/test_ssh.py +++ b/tests/unit/client/test_ssh.py @@ -1,20 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Daniel Wallace <dwallace@saltstack.com` -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os +import re import shutil import tempfile -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import skipIf, TestCase -from tests.support.case import ShellCase -from tests.support.mock import patch, MagicMock - # Import Salt libs import salt.config import salt.roster @@ -22,83 +18,94 @@ import salt.utils.files import salt.utils.path import salt.utils.thin import salt.utils.yaml - from salt.client import ssh +from tests.support.case import ShellCase +from tests.support.mock import MagicMock, call, patch -ROSTER = ''' +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + +ROSTER = """ localhost: host: 127.0.0.1 port: 2827 self: host: 0.0.0.0 port: 42 -''' +""" -@skipIf(not salt.utils.path.which('ssh'), "No ssh binary found in path") +@skipIf(not salt.utils.path.which("ssh"), "No ssh binary found in path") class SSHPasswordTests(ShellCase): def test_password_failure(self): - ''' + """ Check password failures when trying to deploy keys - ''' - opts = salt.config.client_config(self.get_config_file_path('master')) - opts['list_hosts'] = False - opts['argv'] = ['test.ping'] - opts['selected_target_option'] = 'glob' - opts['tgt'] = 'localhost' - opts['arg'] = [] - roster = os.path.join(self.config_dir, 'roster') + """ + opts = salt.config.client_config(self.get_config_file_path("master")) + opts["list_hosts"] = False + opts["argv"] = ["test.ping"] + opts["selected_target_option"] = "glob" + opts["tgt"] = "localhost" + opts["arg"] = [] + roster = os.path.join(self.config_dir, "roster") handle_ssh_ret = [ - {'localhost': {'retcode': 255, 'stderr': u'Permission denied (publickey).\r\n', 'stdout': ''}}, + { + "localhost": { + "retcode": 255, + "stderr": "Permission denied (publickey).\r\n", + "stdout": "", + } + }, ] - expected = {'localhost': 'Permission denied (publickey)'} + expected = {"localhost": "Permission denied (publickey)"} display_output = MagicMock() - with patch('salt.roster.get_roster_file', MagicMock(return_value=roster)), \ - patch('salt.client.ssh.SSH.handle_ssh', MagicMock(return_value=handle_ssh_ret)), \ - patch('salt.client.ssh.SSH.key_deploy', MagicMock(return_value=expected)), \ - patch('salt.output.display_output', display_output): + with patch( + "salt.roster.get_roster_file", MagicMock(return_value=roster) + ), patch( + "salt.client.ssh.SSH.handle_ssh", MagicMock(return_value=handle_ssh_ret) + ), patch( + "salt.client.ssh.SSH.key_deploy", MagicMock(return_value=expected) + ), patch( + "salt.output.display_output", display_output + ): client = ssh.SSH(opts) ret = next(client.run_iter()) with self.assertRaises(SystemExit): client.run() - display_output.assert_called_once_with(expected, 'nested', opts) + display_output.assert_called_once_with(expected, "nested", opts) self.assertIs(ret, handle_ssh_ret[0]) class SSHRosterDefaults(TestCase): def test_roster_defaults_flat(self): - ''' + """ Test Roster Defaults on the flat roster - ''' + """ tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) expected = { - 'self': { - 'host': '0.0.0.0', - 'user': 'daniel', - 'port': 42, - }, - 'localhost': { - 'host': '127.0.0.1', - 'user': 'daniel', - 'port': 2827, - }, + "self": {"host": "0.0.0.0", "user": "daniel", "port": 42}, + "localhost": {"host": "127.0.0.1", "user": "daniel", "port": 2827}, } try: - root_dir = os.path.join(tempdir, 'foo', 'bar') + root_dir = os.path.join(tempdir, "foo", "bar") os.makedirs(root_dir) - fpath = os.path.join(root_dir, 'config') - with salt.utils.files.fopen(fpath, 'w') as fp_: + fpath = os.path.join(root_dir, "config") + with salt.utils.files.fopen(fpath, "w") as fp_: fp_.write( - ''' + """ roster_defaults: user: daniel - ''' + """ ) opts = salt.config.master_config(fpath) - with patch('salt.roster.get_roster_file', MagicMock(return_value=ROSTER)): - with patch('salt.template.compile_template', MagicMock(return_value=salt.utils.yaml.safe_load(ROSTER))): + with patch("salt.roster.get_roster_file", MagicMock(return_value=ROSTER)): + with patch( + "salt.template.compile_template", + MagicMock(return_value=salt.utils.yaml.safe_load(ROSTER)), + ): roster = salt.roster.Roster(opts=opts) - self.assertEqual(roster.targets('*', 'glob'), expected) + self.assertEqual(roster.targets("*", "glob"), expected) finally: if os.path.isdir(tempdir): shutil.rmtree(tempdir) @@ -107,44 +114,323 @@ class SSHRosterDefaults(TestCase): class SSHSingleTests(TestCase): def setUp(self): self.tmp_cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) + self.argv = [ + "ssh.set_auth_key", + "root", + "hobn+amNAXSBTiOXEqlBjGB...rsa root@master", + ] + self.opts = { + "argv": self.argv, + "__role": "master", + "cachedir": self.tmp_cachedir, + "extension_modules": os.path.join(self.tmp_cachedir, "extmods"), + } + self.target = { + "passwd": "abc123", + "ssh_options": None, + "sudo": False, + "identities_only": False, + "host": "login1", + "user": "root", + "timeout": 65, + "remote_port_forwards": None, + "sudo_user": "", + "port": "22", + "priv": "/etc/salt/pki/master/ssh/salt-ssh.rsa", + } def test_single_opts(self): - ''' Sanity check for ssh.Single options - ''' - argv = ['ssh.set_auth_key', 'root', 'hobn+amNAXSBTiOXEqlBjGB...rsa root@master'] - opts = { - 'argv': argv, - '__role': 'master', - 'cachedir': self.tmp_cachedir, - 'extension_modules': os.path.join(self.tmp_cachedir, 'extmods'), - } - target = { - 'passwd': 'abc123', - 'ssh_options': None, - 'sudo': False, - 'identities_only': False, - 'host': 'login1', - 'user': 'root', - 'timeout': 65, - 'remote_port_forwards': None, - 'sudo_user': '', - 'port': '22', - 'priv': '/etc/salt/pki/master/ssh/salt-ssh.rsa' - } + """ Sanity check for ssh.Single options + """ single = ssh.Single( - opts, - opts['argv'], - 'localhost', - mods={}, - fsclient=None, - thin=salt.utils.thin.thin_path(opts['cachedir']), - mine=False, - **target) + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + **self.target + ) - self.assertEqual(single.shell._ssh_opts(), '') - self.assertEqual(single.shell._cmd_str('date +%s'), 'ssh login1 ' - '-o KbdInteractiveAuthentication=no -o ' - 'PasswordAuthentication=yes -o ConnectTimeout=65 -o Port=22 ' - '-o IdentityFile=/etc/salt/pki/master/ssh/salt-ssh.rsa ' - '-o User=root date +%s') + self.assertEqual(single.shell._ssh_opts(), "") + self.assertEqual( + single.shell._cmd_str("date +%s"), + "ssh login1 " + "-o KbdInteractiveAuthentication=no -o " + "PasswordAuthentication=yes -o ConnectTimeout=65 -o Port=22 " + "-o IdentityFile=/etc/salt/pki/master/ssh/salt-ssh.rsa " + "-o User=root date +%s", + ) + + def test_run_with_pre_flight(self): + """ + test Single.run() when ssh_pre_flight is set + and script successfully runs + """ + target = self.target.copy() + target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh") + single = ssh.Single( + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + **target + ) + + cmd_ret = ("Success", "", 0) + mock_flight = MagicMock(return_value=cmd_ret) + mock_cmd = MagicMock(return_value=cmd_ret) + patch_flight = patch("salt.client.ssh.Single.run_ssh_pre_flight", mock_flight) + patch_cmd = patch("salt.client.ssh.Single.cmd_block", mock_cmd) + patch_exec_cmd = patch( + "salt.client.ssh.shell.Shell.exec_cmd", return_value=("", "", 1) + ) + patch_os = patch("os.path.exists", side_effect=[True]) + + with patch_os, patch_flight, patch_cmd, patch_exec_cmd: + ret = single.run() + mock_cmd.assert_called() + mock_flight.assert_called() + assert ret == cmd_ret + + def test_run_with_pre_flight_stderr(self): + """ + test Single.run() when ssh_pre_flight is set + and script errors when run + """ + target = self.target.copy() + target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh") + single = ssh.Single( + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + **target + ) + + cmd_ret = ("", "Error running script", 1) + mock_flight = MagicMock(return_value=cmd_ret) + mock_cmd = MagicMock(return_value=cmd_ret) + patch_flight = patch("salt.client.ssh.Single.run_ssh_pre_flight", mock_flight) + patch_cmd = patch("salt.client.ssh.Single.cmd_block", mock_cmd) + patch_exec_cmd = patch( + "salt.client.ssh.shell.Shell.exec_cmd", return_value=("", "", 1) + ) + patch_os = patch("os.path.exists", side_effect=[True]) + + with patch_os, patch_flight, patch_cmd, patch_exec_cmd: + ret = single.run() + mock_cmd.assert_not_called() + mock_flight.assert_called() + assert ret == cmd_ret + + def test_run_with_pre_flight_script_doesnot_exist(self): + """ + test Single.run() when ssh_pre_flight is set + and the script does not exist + """ + target = self.target.copy() + target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh") + single = ssh.Single( + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + **target + ) + + cmd_ret = ("Success", "", 0) + mock_flight = MagicMock(return_value=cmd_ret) + mock_cmd = MagicMock(return_value=cmd_ret) + patch_flight = patch("salt.client.ssh.Single.run_ssh_pre_flight", mock_flight) + patch_cmd = patch("salt.client.ssh.Single.cmd_block", mock_cmd) + patch_exec_cmd = patch( + "salt.client.ssh.shell.Shell.exec_cmd", return_value=("", "", 1) + ) + patch_os = patch("os.path.exists", side_effect=[False]) + + with patch_os, patch_flight, patch_cmd, patch_exec_cmd: + ret = single.run() + mock_cmd.assert_called() + mock_flight.assert_not_called() + assert ret == cmd_ret + + def test_run_with_pre_flight_thin_dir_exists(self): + """ + test Single.run() when ssh_pre_flight is set + and thin_dir already exists + """ + target = self.target.copy() + target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh") + single = ssh.Single( + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + **target + ) + + cmd_ret = ("", "", 0) + mock_flight = MagicMock(return_value=cmd_ret) + mock_cmd = MagicMock(return_value=cmd_ret) + patch_flight = patch("salt.client.ssh.Single.run_ssh_pre_flight", mock_flight) + patch_cmd = patch("salt.client.ssh.shell.Shell.exec_cmd", mock_cmd) + patch_cmd_block = patch("salt.client.ssh.Single.cmd_block", mock_cmd) + patch_os = patch("os.path.exists", return_value=True) + + with patch_os, patch_flight, patch_cmd, patch_cmd_block: + ret = single.run() + mock_cmd.assert_called() + mock_flight.assert_not_called() + assert ret == cmd_ret + + def test_execute_script(self): + """ + test Single.execute_script() + """ + single = ssh.Single( + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + winrm=False, + **self.target + ) + + exp_ret = ("Success", "", 0) + mock_cmd = MagicMock(return_value=exp_ret) + patch_cmd = patch("salt.client.ssh.shell.Shell.exec_cmd", mock_cmd) + script = os.path.join(RUNTIME_VARS.TMP, "script.sh") + + with patch_cmd: + ret = single.execute_script(script=script) + assert ret == exp_ret + assert mock_cmd.call_count == 2 + assert [ + call("/bin/sh '{0}'".format(script)), + call("rm '{0}'".format(script)), + ] == mock_cmd.call_args_list + + def test_shim_cmd(self): + """ + test Single.shim_cmd() + """ + single = ssh.Single( + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + winrm=False, + tty=True, + **self.target + ) + + exp_ret = ("Success", "", 0) + mock_cmd = MagicMock(return_value=exp_ret) + patch_cmd = patch("salt.client.ssh.shell.Shell.exec_cmd", mock_cmd) + patch_send = patch("salt.client.ssh.shell.Shell.send", return_value=("", "", 0)) + patch_rand = patch("os.urandom", return_value=b"5\xd9l\xca\xc2\xff") + + with patch_cmd, patch_rand, patch_send: + ret = single.shim_cmd(cmd_str="echo test") + assert ret == exp_ret + assert [ + call("/bin/sh '$HOME/.35d96ccac2ff.py'"), + call("rm '$HOME/.35d96ccac2ff.py'"), + ] == mock_cmd.call_args_list + + def test_run_ssh_pre_flight(self): + """ + test Single.run_ssh_pre_flight + """ + target = self.target.copy() + target["ssh_pre_flight"] = os.path.join(RUNTIME_VARS.TMP, "script.sh") + single = ssh.Single( + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + winrm=False, + tty=True, + **target + ) + + exp_ret = ("Success", "", 0) + mock_cmd = MagicMock(return_value=exp_ret) + patch_cmd = patch("salt.client.ssh.shell.Shell.exec_cmd", mock_cmd) + patch_send = patch("salt.client.ssh.shell.Shell.send", return_value=exp_ret) + exp_tmp = os.path.join( + tempfile.gettempdir(), os.path.basename(target["ssh_pre_flight"]) + ) + + with patch_cmd, patch_send: + ret = single.run_ssh_pre_flight() + assert ret == exp_ret + assert [ + call("/bin/sh '{0}'".format(exp_tmp)), + call("rm '{0}'".format(exp_tmp)), + ] == mock_cmd.call_args_list + + @skipIf(salt.utils.platform.is_windows(), "SSH_PY_SHIM not set on windows") + def test_cmd_run_set_path(self): + """ + test when set_path is set + """ + target = self.target + target["set_path"] = "$PATH:/tmp/path/" + single = ssh.Single( + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + **self.target + ) + + ret = single._cmd_str() + assert re.search("\\" + target["set_path"], ret) + + @skipIf(salt.utils.platform.is_windows(), "SSH_PY_SHIM not set on windows") + def test_cmd_run_not_set_path(self): + """ + test when set_path is not set + """ + target = self.target + single = ssh.Single( + self.opts, + self.opts["argv"], + "localhost", + mods={}, + fsclient=None, + thin=salt.utils.thin.thin_path(self.opts["cachedir"]), + mine=False, + **self.target + ) + + ret = single._cmd_str() + assert re.search('SET_PATH=""', ret) diff --git a/tests/unit/cloud/__init__.py b/tests/unit/cloud/__init__.py index cae15ad6c5e..f7137edee87 100644 --- a/tests/unit/cloud/__init__.py +++ b/tests/unit/cloud/__init__.py @@ -1,70 +1,66 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.cloud ~~~~~~~~~~~~~~~~ -''' +""" from __future__ import absolute_import, print_function, unicode_literals -from tests.support.unit import TestCase import salt.cloud +from tests.support.unit import TestCase class CloudTest(TestCase): - def test_vm_config_merger(self): - ''' + """ Validate the vm's config is generated correctly. https://github.com/saltstack/salt/issues/49226 - ''' + """ main = { - 'minion': {'master': '172.31.39.213'}, - 'log_file': 'var/log/salt/cloud.log', - 'pool_size': 10 + "minion": {"master": "172.31.39.213"}, + "log_file": "var/log/salt/cloud.log", + "pool_size": 10, } provider = { - 'private_key': 'dwoz.pem', - 'grains': {'foo1': 'bar', 'foo2': 'bang'}, - 'availability_zone': 'us-west-2b', - 'driver': 'ec2', - 'ssh_interface': 'private_ips', - 'ssh_username': 'admin', - 'location': 'us-west-2' + "private_key": "dwoz.pem", + "grains": {"foo1": "bar", "foo2": "bang"}, + "availability_zone": "us-west-2b", + "driver": "ec2", + "ssh_interface": "private_ips", + "ssh_username": "admin", + "location": "us-west-2", } profile = { - 'profile': 'default', - 'grains': {'meh2': 'bar', 'meh1': 'foo'}, - 'provider': 'ec2-default:ec2', - 'ssh_username': 'admin', - 'image': 'ami-0a1fbca0e5b419fd1', - 'size': 't2.micro' + "profile": "default", + "grains": {"meh2": "bar", "meh1": "foo"}, + "provider": "ec2-default:ec2", + "ssh_username": "admin", + "image": "ami-0a1fbca0e5b419fd1", + "size": "t2.micro", } - vm = salt.cloud.Cloud.vm_config( - 'test_vm', - main, - provider, - profile, - {} - ) - self.assertEqual({ - 'minion': {'master': '172.31.39.213'}, - 'log_file': 'var/log/salt/cloud.log', - 'pool_size': 10, - 'private_key': 'dwoz.pem', - 'grains': { - 'foo1': 'bar', - 'foo2': 'bang', - 'meh2': 'bar', - 'meh1': 'foo', + vm = salt.cloud.Cloud.vm_config("test_vm", main, provider, profile, {}) + self.assertEqual( + { + "minion": {"master": "172.31.39.213"}, + "log_file": "var/log/salt/cloud.log", + "pool_size": 10, + "private_key": "dwoz.pem", + "grains": { + "foo1": "bar", + "foo2": "bang", + "meh2": "bar", + "meh1": "foo", + }, + "availability_zone": "us-west-2b", + "driver": "ec2", + "ssh_interface": "private_ips", + "ssh_username": "admin", + "location": "us-west-2", + "profile": "default", + "provider": "ec2-default:ec2", + "image": "ami-0a1fbca0e5b419fd1", + "size": "t2.micro", + "name": "test_vm", }, - 'availability_zone': 'us-west-2b', - 'driver': 'ec2', - 'ssh_interface': 'private_ips', - 'ssh_username': 'admin', - 'location': 'us-west-2', - 'profile': 'default', - 'provider': 'ec2-default:ec2', - 'image': 'ami-0a1fbca0e5b419fd1', - 'size': 't2.micro', - 'name': 'test_vm', - }, vm) + vm, + ) diff --git a/tests/unit/cloud/clouds/__init__.py b/tests/unit/cloud/clouds/__init__.py index 2efb80de660..e12d4570941 100644 --- a/tests/unit/cloud/clouds/__init__.py +++ b/tests/unit/cloud/clouds/__init__.py @@ -2,12 +2,12 @@ def _preferred_ip(ip_set, preferred=None): - ''' + """ Returns a function that reacts which ip is preferred :param ip_set: :param private: :return: - ''' + """ def _ip_decider(vm, ips): for ip in ips: diff --git a/tests/unit/cloud/clouds/test_dimensiondata.py b/tests/unit/cloud/clouds/test_dimensiondata.py index f461166e17a..29047879614 100644 --- a/tests/unit/cloud/clouds/test_dimensiondata.py +++ b/tests/unit/cloud/clouds/test_dimensiondata.py @@ -1,20 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: `Anthony Shaw <anthonyshaw@apache.org>` tests.unit.cloud.clouds.dimensiondata_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -try: - import libcloud.security - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - # Import Salt Libs from salt.cloud.clouds import dimensiondata from salt.exceptions import SaltCloudSystemExit @@ -22,11 +16,21 @@ from salt.utils.versions import LooseVersion # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock +from tests.support.mock import __version__ as mock_version +from tests.support.mock import patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, __version__ as mock_version from tests.unit.cloud.clouds import _preferred_ip -VM_NAME = 'winterfell' +try: + import libcloud.security + + HAS_LIBCLOUD = True +except ImportError: + HAS_LIBCLOUD = False + + +VM_NAME = "winterfell" # Use certifi if installed try: @@ -36,17 +40,18 @@ try: # with this work-around. This work-around can be removed when the # required minimum version of libcloud is 2.0.0 (See PR #40837 - which # is implemented in Salt 2018.3.0). - if LooseVersion(libcloud.__version__) < LooseVersion('1.4.0'): + if LooseVersion(libcloud.__version__) < LooseVersion("1.4.0"): import certifi + libcloud.security.CA_CERTS_PATH.append(certifi.where()) except (ImportError, NameError): pass class ExtendedTestCase(TestCase): - ''' + """ Extended TestCase class containing additional helper methods. - ''' + """ def assertRaisesWithMessage(self, exc_type, exc_msg, func, *args, **kwargs): try: @@ -58,109 +63,90 @@ class ExtendedTestCase(TestCase): class DimensionDataTestCase(ExtendedTestCase, LoaderModuleMockMixin): - ''' + """ Unit TestCase for salt.cloud.clouds.dimensiondata module. - ''' + """ def setup_loader_modules(self): return { dimensiondata: { - '__virtual__': MagicMock(return_value='dimensiondata'), - '__active_provider_name__': '', - '__opts__': { - 'providers': { - 'my-dimensiondata-cloud': { - 'dimensiondata': { - 'driver': 'dimensiondata', - 'region': 'dd-au', - 'user_id': 'jon_snow', - 'key': 'IKnowNothing' + "__virtual__": MagicMock(return_value="dimensiondata"), + "__active_provider_name__": "", + "__opts__": { + "providers": { + "my-dimensiondata-cloud": { + "dimensiondata": { + "driver": "dimensiondata", + "region": "dd-au", + "user_id": "jon_snow", + "key": "IKnowNothing", } } } - } + }, } } def test_avail_images_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call avail_images with --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - dimensiondata.avail_images, - call='action' + SaltCloudSystemExit, dimensiondata.avail_images, call="action" ) def test_avail_locations_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call avail_locations with --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - dimensiondata.avail_locations, - call='action' + SaltCloudSystemExit, dimensiondata.avail_locations, call="action" ) def test_avail_sizes_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call avail_sizes with --action or -a. - ''' - self.assertRaises( - SaltCloudSystemExit, - dimensiondata.avail_sizes, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, dimensiondata.avail_sizes, call="action") def test_list_nodes_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_nodes with --action or -a. - ''' - self.assertRaises( - SaltCloudSystemExit, - dimensiondata.list_nodes, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, dimensiondata.list_nodes, call="action") def test_destroy_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call destroy with --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - dimensiondata.destroy, - name=VM_NAME, - call='function' + SaltCloudSystemExit, dimensiondata.destroy, name=VM_NAME, call="function" ) - @skipIf(HAS_LIBCLOUD is False, "Install 'libcloud' to be able to run this unit test.") + @skipIf( + HAS_LIBCLOUD is False, "Install 'libcloud' to be able to run this unit test." + ) def test_avail_sizes(self): - ''' + """ Tests that avail_sizes returns an empty dictionary. - ''' - sizes = dimensiondata.avail_sizes(call='foo') - self.assertEqual( - len(sizes), - 1 - ) - self.assertEqual( - sizes['default']['name'], - 'default' - ) + """ + sizes = dimensiondata.avail_sizes(call="foo") + self.assertEqual(len(sizes), 1) + self.assertEqual(sizes["default"]["name"], "default") def test_import(self): """ Test that the module picks up installed deps """ - with patch('salt.config.check_driver_dependencies', return_value=True) as p: + with patch("salt.config.check_driver_dependencies", return_value=True) as p: get_deps = dimensiondata.get_dependencies() self.assertEqual(get_deps, True) - if LooseVersion(mock_version) >= LooseVersion('2.0.0'): + if LooseVersion(mock_version) >= LooseVersion("2.0.0"): self.assertTrue(p.call_count >= 1) def test_provider_matches(self): @@ -171,24 +157,38 @@ class DimensionDataTestCase(ExtendedTestCase, LoaderModuleMockMixin): self.assertNotEqual(p, None) def test_query_node_data_filter_preferred_ip_addresses(self): - ''' + """ Test if query node data is filtering out unpreferred IP addresses. - ''' - zero_ip = '0.0.0.0' - private_ips = [zero_ip, '1.1.1.1', '2.2.2.2'] - vm = {'name': None} + """ + zero_ip = "0.0.0.0" + private_ips = [zero_ip, "1.1.1.1", "2.2.2.2"] + vm = {"name": None} data = MagicMock() data.public_ips = [] - dimensiondata.NodeState = MagicMock() # pylint: disable=blacklisted-unmocked-patching + # pylint: disable=blacklisted-unmocked-patching + dimensiondata.NodeState = MagicMock() + # pylint: enable=blacklisted-unmocked-patching dimensiondata.NodeState.RUNNING = True - with patch('salt.cloud.clouds.dimensiondata.show_instance', - MagicMock(return_value={'state': True, - 'name': 'foo', - 'public_ips': [], - 'private_ips': private_ips})): - with patch('salt.cloud.clouds.dimensiondata.preferred_ip', - _preferred_ip(private_ips, [zero_ip])): - with patch('salt.cloud.clouds.dimensiondata.ssh_interface', - MagicMock(return_value='private_ips')): - self.assertEqual(dimensiondata._query_node_data(vm, data).public_ips, [zero_ip]) + with patch( + "salt.cloud.clouds.dimensiondata.show_instance", + MagicMock( + return_value={ + "state": True, + "name": "foo", + "public_ips": [], + "private_ips": private_ips, + } + ), + ): + with patch( + "salt.cloud.clouds.dimensiondata.preferred_ip", + _preferred_ip(private_ips, [zero_ip]), + ): + with patch( + "salt.cloud.clouds.dimensiondata.ssh_interface", + MagicMock(return_value="private_ips"), + ): + self.assertEqual( + dimensiondata._query_node_data(vm, data).public_ips, [zero_ip] + ) diff --git a/tests/unit/cloud/clouds/test_ec2.py b/tests/unit/cloud/clouds/test_ec2.py index ec264d1eb1c..71ad3e0a0cf 100644 --- a/tests/unit/cloud/clouds/test_ec2.py +++ b/tests/unit/cloud/clouds/test_ec2.py @@ -2,38 +2,42 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import tempfile +import salt.utils.files + # Import Salt Libs from salt.cloud.clouds import ec2 from salt.exceptions import SaltCloudSystemExit -import salt.utils.files +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import PropertyMock, patch # Import Salt Testing Libs from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase, skipIf -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import patch, PropertyMock from tests.unit.test_crypt import PRIVKEY_DATA PASS_DATA = ( - b'qOjCKDlBdcNEbJ/J8eRl7sH+bYIIm4cvHHY86gh2NEUnufFlFo0gGVTZR05Fj0cw3n/w7gR' - b'urNXz5JoeSIHVuNI3YTwzL9yEAaC0kuy8EbOlO2yx8yPGdfml9BRwOV7A6b8UFo9co4H7fz' - b'DdScMKU2yzvRYvp6N6Q2cJGBmPsemnXWWusb+1vZVWxcRAQmG3ogF6Z5rZSYAYH0N4rqJgH' - b'mQfzuyb+jrBvV/IOoV1EdO9jGSH9338aS47NjrmNEN/SpnS6eCWZUwwyHbPASuOvWiY4QH/' - b'0YZC6EGccwiUmt0ZOxIynk+tEyVPTkiS0V8RcZK6YKqMWHpKmPtLBzfuoA==' + b"qOjCKDlBdcNEbJ/J8eRl7sH+bYIIm4cvHHY86gh2NEUnufFlFo0gGVTZR05Fj0cw3n/w7gR" + b"urNXz5JoeSIHVuNI3YTwzL9yEAaC0kuy8EbOlO2yx8yPGdfml9BRwOV7A6b8UFo9co4H7fz" + b"DdScMKU2yzvRYvp6N6Q2cJGBmPsemnXWWusb+1vZVWxcRAQmG3ogF6Z5rZSYAYH0N4rqJgH" + b"mQfzuyb+jrBvV/IOoV1EdO9jGSH9338aS47NjrmNEN/SpnS6eCWZUwwyHbPASuOvWiY4QH/" + b"0YZC6EGccwiUmt0ZOxIynk+tEyVPTkiS0V8RcZK6YKqMWHpKmPtLBzfuoA==" ) class EC2TestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Unit TestCase for salt.cloud.clouds.ec2 module. - ''' + """ def setUp(self): super(EC2TestCase, self).setUp() - with tempfile.NamedTemporaryFile(dir=RUNTIME_VARS.TMP, suffix='.pem', delete=True) as fp: + with tempfile.NamedTemporaryFile( + dir=RUNTIME_VARS.TMP, suffix=".pem", delete=True + ) as fp: self.key_file = fp.name def tearDown(self): @@ -42,63 +46,67 @@ class EC2TestCase(TestCase, LoaderModuleMockMixin): os.remove(self.key_file) def setup_loader_modules(self): - return {ec2: {'__opts__': {}}} + return {ec2: {"__opts__": {}}} def test__validate_key_path_and_mode(self): # Key file exists - with patch('os.path.exists', return_value=True): - with patch('os.stat') as patched_stat: - type(patched_stat.return_value).st_mode = PropertyMock(return_value=0o644) + with patch("os.path.exists", return_value=True): + with patch("os.stat") as patched_stat: + type(patched_stat.return_value).st_mode = PropertyMock( + return_value=0o644 + ) self.assertRaises( - SaltCloudSystemExit, ec2._validate_key_path_and_mode, 'key_file') + SaltCloudSystemExit, ec2._validate_key_path_and_mode, "key_file" + ) - type(patched_stat.return_value).st_mode = PropertyMock(return_value=0o600) - self.assertTrue(ec2._validate_key_path_and_mode('key_file')) + type(patched_stat.return_value).st_mode = PropertyMock( + return_value=0o600 + ) + self.assertTrue(ec2._validate_key_path_and_mode("key_file")) - type(patched_stat.return_value).st_mode = PropertyMock(return_value=0o400) - self.assertTrue(ec2._validate_key_path_and_mode('key_file')) + type(patched_stat.return_value).st_mode = PropertyMock( + return_value=0o400 + ) + self.assertTrue(ec2._validate_key_path_and_mode("key_file")) # Key file does not exist - with patch('os.path.exists', return_value=False): + with patch("os.path.exists", return_value=False): self.assertRaises( - SaltCloudSystemExit, ec2._validate_key_path_and_mode, 'key_file') + SaltCloudSystemExit, ec2._validate_key_path_and_mode, "key_file" + ) - @skipIf(not ec2.HAS_M2 and not ec2.HAS_PYCRYPTO, 'Needs crypto library') - @patch('salt.cloud.clouds.ec2._get_node') - @patch('salt.cloud.clouds.ec2.get_location') - @patch('salt.cloud.clouds.ec2.get_provider') - @patch('salt.utils.aws.query') + @skipIf(not ec2.HAS_M2 and not ec2.HAS_PYCRYPTO, "Needs crypto library") + @patch("salt.cloud.clouds.ec2._get_node") + @patch("salt.cloud.clouds.ec2.get_location") + @patch("salt.cloud.clouds.ec2.get_provider") + @patch("salt.utils.aws.query") def test_get_password_data(self, query, get_provider, get_location, _get_node): - query.return_value = [ - { - 'passwordData': PASS_DATA - } - ] - _get_node.return_value = {'instanceId': 'i-abcdef'} - get_location.return_value = 'us-west2' - get_provider.return_value = 'ec2' - with salt.utils.files.fopen(self.key_file, 'w') as fp: + query.return_value = [{"passwordData": PASS_DATA}] + _get_node.return_value = {"instanceId": "i-abcdef"} + get_location.return_value = "us-west2" + get_provider.return_value = "ec2" + with salt.utils.files.fopen(self.key_file, "w") as fp: fp.write(PRIVKEY_DATA) ret = ec2.get_password_data( - name='i-abcddef', kwargs={'key_file': self.key_file}, call='action' + name="i-abcddef", kwargs={"key_file": self.key_file}, call="action" ) - assert ret['passwordData'] == PASS_DATA - assert ret['password'] == 'testp4ss!' + assert ret["passwordData"] == PASS_DATA + assert ret["password"] == "testp4ss!" - @patch('salt.cloud.clouds.ec2.config.get_cloud_config_value') - @patch('salt.cloud.clouds.ec2.get_location') - @patch('salt.cloud.clouds.ec2.get_provider') - @patch('salt.cloud.clouds.ec2.aws.query') + @patch("salt.cloud.clouds.ec2.config.get_cloud_config_value") + @patch("salt.cloud.clouds.ec2.get_location") + @patch("salt.cloud.clouds.ec2.get_provider") + @patch("salt.cloud.clouds.ec2.aws.query") def test_get_imageid(self, aws_query, get_provider, get_location, config): - ''' + """ test querying imageid function - ''' + """ vm = {} - ami = 'ami-1234' - config.return_value = 'test/*' - get_location.return_value = 'us-west2' - get_provider.return_value = 'ec2' - aws_query.return_value = [{'imageId': ami}] + ami = "ami-1234" + config.return_value = "test/*" + get_location.return_value = "us-west2" + get_provider.return_value = "ec2" + aws_query.return_value = [{"imageId": ami}] # test image filter self.assertEqual(ec2.get_imageid(vm), ami) @@ -110,60 +118,75 @@ class EC2TestCase(TestCase, LoaderModuleMockMixin): # we should have only ran aws.query once when testing the aws filter aws_query.assert_called_once() - @patch('salt.cloud.clouds.ec2.config.get_cloud_config_value') - @patch('salt.cloud.clouds.ec2.get_location') - @patch('salt.cloud.clouds.ec2.get_availability_zone') - @patch('salt.cloud.clouds.ec2.get_provider') - @patch('salt.cloud.clouds.ec2.get_spot_config') - @patch('salt.cloud.clouds.ec2._param_from_config') - @patch('salt.cloud.clouds.ec2.securitygroupid') - def test_termination_protection(self, - securitygroupid, - _param_from_config, - get_spot_config, - get_provider, - get_availability_zone, - get_location, - config): - ''' + @patch("salt.cloud.clouds.ec2.config.get_cloud_config_value") + @patch("salt.cloud.clouds.ec2.get_location") + @patch("salt.cloud.clouds.ec2.get_availability_zone") + @patch("salt.cloud.clouds.ec2.get_provider") + @patch("salt.cloud.clouds.ec2.get_spot_config") + @patch("salt.cloud.clouds.ec2._param_from_config") + @patch("salt.cloud.clouds.ec2.securitygroupid") + def test_termination_protection( + self, + securitygroupid, + _param_from_config, + get_spot_config, + get_provider, + get_availability_zone, + get_location, + config, + ): + """ Verify that `set_termination_protection` updates the right parameters - ''' - vm = {'name': 'taco'} - set_del_root_vol_on_destroy = 'yes' + """ + vm = {"name": "taco"} + set_del_root_vol_on_destroy = "yes" termination_protection = True - config.side_effect = [None] * 2 + ['test/*'] + [None] * 13 + [set_del_root_vol_on_destroy, termination_protection] - get_location.return_value = 'us-west2' + config.side_effect = ( + [None] * 2 + + ["test/*"] + + [None] * 13 + + [set_del_root_vol_on_destroy, termination_protection] + ) + get_location.return_value = "us-west2" get_availability_zone.return_value = None - get_provider.return_value = 'ec2' + get_provider.return_value = "ec2" get_spot_config.return_value = None securitygroupid.return_value = None - self.assertRaises(salt.exceptions.SaltCloudConfigError, ec2.request_instance, vm) - _param_from_config.assert_called_once_with('DisableApiTermination', True) + self.assertRaises( + salt.exceptions.SaltCloudConfigError, ec2.request_instance, vm + ) + _param_from_config.assert_called_once_with("DisableApiTermination", True) - @patch('salt.cloud.clouds.ec2.config.get_cloud_config_value') - @patch('salt.cloud.clouds.ec2.get_location') - @patch('salt.cloud.clouds.ec2.get_availability_zone') - @patch('salt.cloud.clouds.ec2.get_provider') - @patch('salt.cloud.clouds.ec2.get_spot_config') - @patch('salt.cloud.clouds.ec2.securitygroupid') - def test_termination_protection_exception(self, - securitygroupid, - get_spot_config, - get_provider, - get_availability_zone, - get_location, - config): - ''' + @patch("salt.cloud.clouds.ec2.config.get_cloud_config_value") + @patch("salt.cloud.clouds.ec2.get_location") + @patch("salt.cloud.clouds.ec2.get_availability_zone") + @patch("salt.cloud.clouds.ec2.get_provider") + @patch("salt.cloud.clouds.ec2.get_spot_config") + @patch("salt.cloud.clouds.ec2.securitygroupid") + def test_termination_protection_exception( + self, + securitygroupid, + get_spot_config, + get_provider, + get_availability_zone, + get_location, + config, + ): + """ Verify improper `set_termination_protection` parameters raises an exception - ''' - vm = {'name': 'taco'} - termination_protection = 'not a bool' - config.side_effect = [None] * 2 + ['test/*'] + [None] * 14 + [termination_protection] - get_location.return_value = 'us-west2' + """ + vm = {"name": "taco"} + termination_protection = "not a bool" + config.side_effect = ( + [None] * 2 + ["test/*"] + [None] * 14 + [termination_protection] + ) + get_location.return_value = "us-west2" get_availability_zone.return_value = None - get_provider.return_value = 'ec2' + get_provider.return_value = "ec2" get_spot_config.return_value = None securitygroupid.return_value = None - self.assertRaises(salt.exceptions.SaltCloudConfigError, ec2.request_instance, vm) + self.assertRaises( + salt.exceptions.SaltCloudConfigError, ec2.request_instance, vm + ) diff --git a/tests/unit/cloud/clouds/test_gce.py b/tests/unit/cloud/clouds/test_gce.py index 1e3cac3dfd2..660ab9942da 100644 --- a/tests/unit/cloud/clouds/test_gce.py +++ b/tests/unit/cloud/clouds/test_gce.py @@ -1,20 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: `Anthony Shaw <anthonyshaw@apache.org>` tests.unit.cloud.clouds.gce_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -try: - import libcloud.security - HAS_LIBCLOUD = True -except ImportError: - HAS_LIBCLOUD = False - # Import Salt Libs from salt.cloud.clouds import gce from salt.exceptions import SaltCloudSystemExit @@ -22,15 +16,24 @@ from salt.utils.versions import LooseVersion # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import __version__ as mock_version +from tests.support.mock import patch from tests.support.unit import TestCase -from tests.support.mock import patch, __version__ as mock_version -VM_NAME = 'kings_landing' +try: + import libcloud.security + + HAS_LIBCLOUD = True +except ImportError: + HAS_LIBCLOUD = False + + +VM_NAME = "kings_landing" DUMMY_TOKEN = { - 'refresh_token': None, - 'client_id': 'dany123', - 'client_secret': 'lalalalalalala', - 'grant_type': 'refresh_token' + "refresh_token": None, + "client_id": "dany123", + "client_secret": "lalalalalalala", + "grant_type": "refresh_token", } # Use certifi if installed @@ -41,59 +44,57 @@ try: # with this work-around. This work-around can be removed when the # required minimum version of libcloud is 2.0.0 (See PR #40837 - which # is implemented in Salt 2018.3.0). - if LooseVersion(libcloud.__version__) < LooseVersion('1.4.0'): + if LooseVersion(libcloud.__version__) < LooseVersion("1.4.0"): import certifi + libcloud.security.CA_CERTS_PATH.append(certifi.where()) except ImportError: pass class GCETestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Unit TestCase for salt.cloud.clouds.gce module. - ''' + """ def setup_loader_modules(self): return { gce: { - '__active_provider_name__': '', - '__opts__': { - 'providers': { - 'my-google-cloud': { - 'gce': { - 'project': 'daenerys-cloud', - 'service_account_email_address': 'dany@targaryen.westeros.cloud', - 'service_account_private_key': '/home/dany/PRIVKEY.pem', - 'driver': 'gce', - 'ssh_interface': 'public_ips' + "__active_provider_name__": "", + "__opts__": { + "providers": { + "my-google-cloud": { + "gce": { + "project": "daenerys-cloud", + "service_account_email_address": "dany@targaryen.westeros.cloud", + "service_account_private_key": "/home/dany/PRIVKEY.pem", + "driver": "gce", + "ssh_interface": "public_ips", } } } - } + }, } } def test_destroy_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call destroy with --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - gce.destroy, - vm_name=VM_NAME, - call='function' + SaltCloudSystemExit, gce.destroy, vm_name=VM_NAME, call="function" ) def test_fail_virtual_missing_deps(self): # Missing deps - with patch('salt.config.check_driver_dependencies', return_value=False): + with patch("salt.config.check_driver_dependencies", return_value=False): v = gce.__virtual__() self.assertEqual(v, False) def test_fail_virtual_deps_missing_config(self): - with patch('salt.config.check_driver_dependencies', return_value=True): - with patch('salt.config.is_provider_configured', return_value=False): + with patch("salt.config.check_driver_dependencies", return_value=True): + with patch("salt.config.is_provider_configured", return_value=False): v = gce.__virtual__() self.assertEqual(v, False) @@ -101,10 +102,10 @@ class GCETestCase(TestCase, LoaderModuleMockMixin): """ Test that the module picks up installed deps """ - with patch('salt.config.check_driver_dependencies', return_value=True) as p: + with patch("salt.config.check_driver_dependencies", return_value=True) as p: get_deps = gce.get_dependencies() self.assertEqual(get_deps, True) - if LooseVersion(mock_version) >= LooseVersion('2.0.0'): + if LooseVersion(mock_version) >= LooseVersion("2.0.0"): self.assert_called_once(p) def test_provider_matches(self): diff --git a/tests/unit/cloud/clouds/test_joyent.py b/tests/unit/cloud/clouds/test_joyent.py index 5534a7b0456..25a95ea96e1 100644 --- a/tests/unit/cloud/clouds/test_joyent.py +++ b/tests/unit/cloud/clouds/test_joyent.py @@ -1,102 +1,101 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Eric Radman <ericshane@eradman.com> -''' +""" # Import Salt Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs from salt.cloud.clouds import joyent +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + # Stubs -def fake_wait_for_ip(check_for_ip_fn, - interval=None, - timeout=None, - interval_multiplier=None): - ''' +def fake_wait_for_ip( + check_for_ip_fn, interval=None, timeout=None, interval_multiplier=None +): + """ Callback that returns immediately instead of waiting - ''' + """ assert isinstance(interval, int) assert isinstance(timeout, int) assert isinstance(interval_multiplier, int) return check_for_ip_fn() -@skipIf(joyent.HAS_REQUIRED_CRYPTO is False, reason='PyCrypto or Cryptodome not installed') +@skipIf( + joyent.HAS_REQUIRED_CRYPTO is False, reason="PyCrypto or Cryptodome not installed" +) class JoyentTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Unit TestCase for the salt.cloud.clouds.joyent module - ''' + """ + def setup_loader_modules(self): - patcher = patch('salt.utils.cloud.wait_for_ip', fake_wait_for_ip) + patcher = patch("salt.utils.cloud.wait_for_ip", fake_wait_for_ip) patcher.start() self.addCleanup(patcher.stop) return { joyent: { - '__utils__': { - 'cloud.fire_event': MagicMock(), - 'cloud.bootstrap': MagicMock() + "__utils__": { + "cloud.fire_event": MagicMock(), + "cloud.bootstrap": MagicMock(), }, - '__opts__': { - 'sock_dir': True, - 'transport': True, - 'providers': {'my_joyent': {}}, - 'profiles': {'my_joyent': {}} + "__opts__": { + "sock_dir": True, + "transport": True, + "providers": {"my_joyent": {}}, + "profiles": {"my_joyent": {}}, }, - '__active_provider_name__': 'my_joyent:joyent' + "__active_provider_name__": "my_joyent:joyent", } } def setUp(self): self.vm_ = { - 'profile': 'my_joyent', - 'name': 'vm3', - 'driver': 'joyent', - 'size': 'k4-highcpu-kvm-750M', - 'image': 'freebsd10', - 'location': 'us-east-1' + "profile": "my_joyent", + "name": "vm3", + "driver": "joyent", + "size": "k4-highcpu-kvm-750M", + "image": "freebsd10", + "location": "us-east-1", } def tearDown(self): del self.vm_ def test_query_instance_init(self): - ''' + """ Initial provisioning, no IP assigned - ''' + """ # Not yet reachable - reply = (200, {'state': 'provisioning'}) - with patch.object(joyent, 'show_instance', return_value=reply): + reply = (200, {"state": "provisioning"}) + with patch.object(joyent, "show_instance", return_value=reply): result = joyent.query_instance(self.vm_) - self.assertTrue(joyent.__utils__['cloud.fire_event'].called_once()) + self.assertTrue(joyent.__utils__["cloud.fire_event"].called_once()) self.assertEqual(result, None) def test_query_instance_has_ip(self): - ''' + """ IP address assigned but not yet ready - ''' - reply = (200, {'primaryIp': '1.1.1.1', 'state': 'provisioning'}) - with patch.object(joyent, 'show_instance', return_value=reply): + """ + reply = (200, {"primaryIp": "1.1.1.1", "state": "provisioning"}) + with patch.object(joyent, "show_instance", return_value=reply): result = joyent.query_instance(self.vm_) - self.assertTrue(joyent.__utils__['cloud.fire_event'].called_once()) + self.assertTrue(joyent.__utils__["cloud.fire_event"].called_once()) self.assertEqual(result, None) def test_query_instance_ready(self): - ''' + """ IP address assigned, and VM is ready - ''' - reply = (200, {'primaryIp': '1.1.1.1', 'state': 'running'}) - with patch.object(joyent, 'show_instance', return_value=reply): + """ + reply = (200, {"primaryIp": "1.1.1.1", "state": "running"}) + with patch.object(joyent, "show_instance", return_value=reply): result = joyent.query_instance(self.vm_) - self.assertTrue(joyent.__utils__['cloud.fire_event'].called_once()) - self.assertEqual(result, '1.1.1.1') + self.assertTrue(joyent.__utils__["cloud.fire_event"].called_once()) + self.assertEqual(result, "1.1.1.1") diff --git a/tests/unit/cloud/clouds/test_linode.py b/tests/unit/cloud/clouds/test_linode.py index e1782b41908..1b7a3c2e3de 100644 --- a/tests/unit/cloud/clouds/test_linode.py +++ b/tests/unit/cloud/clouds/test_linode.py @@ -1,101 +1,102 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Salt Libs from __future__ import absolute_import, print_function, unicode_literals +# Import Salt Libs +from salt.cloud.clouds import linode + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -# Import Salt Libs -from salt.cloud.clouds import linode - class LinodeTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Unit TestCase for the salt.cloud.clouds.linode module. - ''' + """ + def setup_loader_modules(self): return {linode: {}} # _validate_name tests def test_validate_name_first_character_invalid(self): - ''' + """ Tests when name starts with an invalid character. - ''' + """ # Test when name begins with a hyphen - self.assertFalse(linode._validate_name('-foo')) + self.assertFalse(linode._validate_name("-foo")) # Test when name begins with an underscore - self.assertFalse(linode._validate_name('_foo')) + self.assertFalse(linode._validate_name("_foo")) def test_validate_name_last_character_invalid(self): - ''' + """ Tests when name ends with an invalid character. - ''' + """ # Test when name ends with a hyphen - self.assertFalse(linode._validate_name('foo-')) + self.assertFalse(linode._validate_name("foo-")) # Test when name ends with an underscore - self.assertFalse(linode._validate_name('foo_')) + self.assertFalse(linode._validate_name("foo_")) def test_validate_name_too_short(self): - ''' + """ Tests when name has less than three letters. - ''' + """ # Test when name is an empty string - self.assertFalse(linode._validate_name('')) + self.assertFalse(linode._validate_name("")) # Test when name is two letters long - self.assertFalse(linode._validate_name('ab')) + self.assertFalse(linode._validate_name("ab")) # Test when name is three letters long (valid) - self.assertTrue(linode._validate_name('abc')) + self.assertTrue(linode._validate_name("abc")) def test_validate_name_too_long(self): - ''' + """ Tests when name has more than 48 letters. - ''' - long_name = '1111-2222-3333-4444-5555-6666-7777-8888-9999-111' + """ + long_name = "1111-2222-3333-4444-5555-6666-7777-8888-9999-111" # Test when name is 48 letters long (valid) self.assertEqual(len(long_name), 48) self.assertTrue(linode._validate_name(long_name)) # Test when name is more than 48 letters long - long_name += '1' + long_name += "1" self.assertEqual(len(long_name), 49) self.assertFalse(linode._validate_name(long_name)) def test_validate_name_invalid_characters(self): - ''' + """ Tests when name contains invalid characters. - ''' + """ # Test when name contains an invalid character - self.assertFalse(linode._validate_name('foo;bar')) + self.assertFalse(linode._validate_name("foo;bar")) # Test when name contains non-ascii letters - self.assertFalse(linode._validate_name('fooàààààbar')) + self.assertFalse(linode._validate_name("fooàààààbar")) # Test when name contains spaces - self.assertFalse(linode._validate_name('foo bar')) + self.assertFalse(linode._validate_name("foo bar")) def test_validate_name_valid_characters(self): - ''' + """ Tests when name contains valid characters. - ''' + """ # Test when name contains letters and numbers - self.assertTrue(linode._validate_name('foo123bar')) + self.assertTrue(linode._validate_name("foo123bar")) # Test when name contains hyphens - self.assertTrue(linode._validate_name('foo-bar')) + self.assertTrue(linode._validate_name("foo-bar")) # Test when name contains underscores - self.assertTrue(linode._validate_name('foo_bar')) + self.assertTrue(linode._validate_name("foo_bar")) # Test when name start and end with numbers - self.assertTrue(linode._validate_name('1foo')) - self.assertTrue(linode._validate_name('foo0')) + self.assertTrue(linode._validate_name("1foo")) + self.assertTrue(linode._validate_name("foo0")) diff --git a/tests/unit/cloud/clouds/test_opennebula.py b/tests/unit/cloud/clouds/test_opennebula.py index c517a06f2de..c45cac0ddbf 100644 --- a/tests/unit/cloud/clouds/test_opennebula.py +++ b/tests/unit/cloud/clouds/test_opennebula.py @@ -1,1648 +1,1722 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch - # Import Salt Libs from salt.cloud.clouds import opennebula -from salt.exceptions import SaltCloudSystemExit, SaltCloudNotFound +from salt.exceptions import SaltCloudNotFound, SaltCloudSystemExit + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # Import Third Party Libs try: from lxml import etree # pylint: disable=W0611 + HAS_XML_LIBS = True except ImportError: HAS_XML_LIBS = False -VM_NAME = 'my-vm' +VM_NAME = "my-vm" class OpenNebulaTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Unit TestCase for salt.cloud.clouds.opennebula module. - ''' + """ + def setup_loader_modules(self): return { opennebula: { - '__virtual__': MagicMock(return_value='opennebula'), - '__utils__': { - 'cloud.cache_node': MagicMock() - }, - '__active_provider_name__': '' + "__virtual__": MagicMock(return_value="opennebula"), + "__utils__": {"cloud.cache_node": MagicMock()}, + "__active_provider_name__": "", } } def test_avail_images_action(self): - ''' + """ Tests that a SaltCloudSystemExit error is raised when trying to call avail_images with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.avail_images, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.avail_images, "action") def test_avail_locations_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call avail_locations with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.avail_locations, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.avail_locations, "action") def test_avail_sizes_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call avail_sizes with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.avail_sizes, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.avail_sizes, "action") def test_avail_sizes(self): - ''' + """ Tests that avail_sizes returns an empty dictionary. - ''' - self.assertEqual(opennebula.avail_sizes(call='foo'), {}) + """ + self.assertEqual(opennebula.avail_sizes(call="foo"), {}) def test_list_clusters_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_clusters with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.list_clusters, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.list_clusters, "action") def test_list_datastores_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_datastores with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.list_datastores, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.list_datastores, "action") def test_list_hosts_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_datastores with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.list_hosts, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.list_hosts, "action") def test_list_nodes_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_nodes with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.list_nodes, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.list_nodes, "action") def test_list_nodes_full_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_nodes_full with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.list_nodes_full, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.list_nodes_full, "action") def test_list_nodes_select_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_nodes_full with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.list_nodes_select, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.list_nodes_select, "action") def test_list_security_groups_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_security_groups with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.list_security_groups, 'action') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.list_security_groups, "action" + ) def test_list_templates_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_templates with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.list_templates, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.list_templates, "action") def test_list_vns_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_vns with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.list_vns, 'action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.list_vns, "action") def test_reboot_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call reboot with anything other that --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.reboot, 'my-vm', 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.reboot, "my-vm", "foo") def test_start_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call start with anything other that --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.start, 'my-vm', 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.start, "my-vm", "foo") def test_stop_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call stop with anything other that --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.stop, 'my-vm', 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.stop, "my-vm", "foo") def test_get_cluster_id_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call get_cluster_id with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_cluster_id, - call='action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.get_cluster_id, call="action") def test_get_cluster_id_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_cluster_id, - None, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.get_cluster_id, None, call="foo" + ) def test_get_cluster_id_not_found(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - with patch('salt.cloud.clouds.opennebula.list_clusters', - MagicMock(return_value={'foo': {'id': 'bar'}})): - self.assertRaises(SaltCloudSystemExit, - opennebula.get_cluster_id, - kwargs={'name': 'test'}, - call='function') + """ + with patch( + "salt.cloud.clouds.opennebula.list_clusters", + MagicMock(return_value={"foo": {"id": "bar"}}), + ): + self.assertRaises( + SaltCloudSystemExit, + opennebula.get_cluster_id, + kwargs={"name": "test"}, + call="function", + ) def test_get_cluster_id_success(self): - ''' + """ Tests that the function returns successfully. - ''' - with patch('salt.cloud.clouds.opennebula.list_clusters', - MagicMock(return_value={'test-cluster': {'id': '100'}})): - mock_id = '100' - mock_kwargs = {'name': 'test-cluster'} - self.assertEqual(opennebula.get_cluster_id(mock_kwargs, 'foo'), - mock_id) + """ + with patch( + "salt.cloud.clouds.opennebula.list_clusters", + MagicMock(return_value={"test-cluster": {"id": "100"}}), + ): + mock_id = "100" + mock_kwargs = {"name": "test-cluster"} + self.assertEqual(opennebula.get_cluster_id(mock_kwargs, "foo"), mock_id) def test_get_datastore_id_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call get_datastore_id with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_datastore_id, - call='action') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.get_datastore_id, call="action" + ) def test_get_datastore_id_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_datastore_id, - None, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.get_datastore_id, None, call="foo" + ) def test_get_datastore_id_not_found(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - with patch('salt.cloud.clouds.opennebula.list_datastores', - MagicMock(return_value={'test-datastore': {'id': '100'}})): - self.assertRaises(SaltCloudSystemExit, - opennebula.get_datastore_id, - kwargs={'name': 'test'}, - call='function') + """ + with patch( + "salt.cloud.clouds.opennebula.list_datastores", + MagicMock(return_value={"test-datastore": {"id": "100"}}), + ): + self.assertRaises( + SaltCloudSystemExit, + opennebula.get_datastore_id, + kwargs={"name": "test"}, + call="function", + ) def test_get_datastore_id_success(self): - ''' + """ Tests that the function returns successfully. - ''' - with patch('salt.cloud.clouds.opennebula.list_datastores', - MagicMock(return_value={'test-datastore': {'id': '100'}})): - mock_id = '100' - mock_kwargs = {'name': 'test-datastore'} - self.assertEqual(opennebula.get_datastore_id(mock_kwargs, 'foo'), - mock_id) + """ + with patch( + "salt.cloud.clouds.opennebula.list_datastores", + MagicMock(return_value={"test-datastore": {"id": "100"}}), + ): + mock_id = "100" + mock_kwargs = {"name": "test-datastore"} + self.assertEqual(opennebula.get_datastore_id(mock_kwargs, "foo"), mock_id) def test_get_host_id_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call get_host_id with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_host_id, - call='action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.get_host_id, call="action") def test_get_host_id_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_host_id, - None, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.get_host_id, None, call="foo") def test_get_host_id_not_found(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - with patch('salt.cloud.clouds.opennebula.avail_locations', - MagicMock(return_value={'test-host': {'id': '100'}})): - self.assertRaises(SaltCloudSystemExit, - opennebula.get_host_id, - kwargs={'name': 'test'}, - call='function') + """ + with patch( + "salt.cloud.clouds.opennebula.avail_locations", + MagicMock(return_value={"test-host": {"id": "100"}}), + ): + self.assertRaises( + SaltCloudSystemExit, + opennebula.get_host_id, + kwargs={"name": "test"}, + call="function", + ) def test_get_host_id_success(self): - ''' + """ Tests that the function returns successfully. - ''' - with patch('salt.cloud.clouds.opennebula.avail_locations', - MagicMock(return_value={'test-host': {'id': '100'}})): - mock_id = '100' - mock_kwargs = {'name': 'test-host'} - self.assertEqual(opennebula.get_host_id(mock_kwargs, 'foo'), - mock_id) + """ + with patch( + "salt.cloud.clouds.opennebula.avail_locations", + MagicMock(return_value={"test-host": {"id": "100"}}), + ): + mock_id = "100" + mock_kwargs = {"name": "test-host"} + self.assertEqual(opennebula.get_host_id(mock_kwargs, "foo"), mock_id) def test_get_image_not_found(self): - ''' + """ Tests that a SaltCloudNotFound is raised when the image doesn't exist. - ''' - with patch('salt.cloud.clouds.opennebula.avail_images', MagicMock(return_value={})): - with patch('salt.config.get_cloud_config_value', MagicMock(return_value='foo')): - self.assertRaises(SaltCloudNotFound, opennebula.get_image, 'my-vm') + """ + with patch( + "salt.cloud.clouds.opennebula.avail_images", MagicMock(return_value={}) + ): + with patch( + "salt.config.get_cloud_config_value", MagicMock(return_value="foo") + ): + self.assertRaises(SaltCloudNotFound, opennebula.get_image, "my-vm") def test_get_image_success(self): - ''' + """ Tests that the image is returned successfully. - ''' - with patch('salt.cloud.clouds.opennebula.avail_images', - MagicMock(return_value={'my-vm': {'name': 'my-vm', 'id': 0}})): - with patch('salt.config.get_cloud_config_value', MagicMock(return_value='my-vm')): - self.assertEqual(opennebula.get_image('my-vm'), 0) + """ + with patch( + "salt.cloud.clouds.opennebula.avail_images", + MagicMock(return_value={"my-vm": {"name": "my-vm", "id": 0}}), + ): + with patch( + "salt.config.get_cloud_config_value", MagicMock(return_value="my-vm") + ): + self.assertEqual(opennebula.get_image("my-vm"), 0) def test_get_image_id_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call get_image_id with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_image_id, - call='action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.get_image_id, call="action") def test_get_image_id_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_image_id, - None, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.get_image_id, None, call="foo" + ) def test_get_image_id_not_found(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - with patch('salt.cloud.clouds.opennebula.avail_images', - MagicMock(return_value={'test-image': {'id': '100'}})): - self.assertRaises(SaltCloudSystemExit, - opennebula.get_image_id, - kwargs={'name': 'test'}, - call='function') + """ + with patch( + "salt.cloud.clouds.opennebula.avail_images", + MagicMock(return_value={"test-image": {"id": "100"}}), + ): + self.assertRaises( + SaltCloudSystemExit, + opennebula.get_image_id, + kwargs={"name": "test"}, + call="function", + ) def test_get_image_id_success(self): - ''' + """ Tests that the function returns successfully. - ''' - with patch('salt.cloud.clouds.opennebula.avail_images', - MagicMock(return_value={'test-image': {'id': '100'}})): - mock_id = '100' - mock_kwargs = {'name': 'test-image'} - self.assertEqual(opennebula.get_image_id(mock_kwargs, 'foo'), - mock_id) + """ + with patch( + "salt.cloud.clouds.opennebula.avail_images", + MagicMock(return_value={"test-image": {"id": "100"}}), + ): + mock_id = "100" + mock_kwargs = {"name": "test-image"} + self.assertEqual(opennebula.get_image_id(mock_kwargs, "foo"), mock_id) def test_get_location_not_found(self): - ''' + """ Tests that a SaltCloudNotFound is raised when the location doesn't exist. - ''' - with patch('salt.cloud.clouds.opennebula.avail_locations', MagicMock(return_value={})): - with patch('salt.config.get_cloud_config_value', MagicMock(return_value='foo')): - self.assertRaises(SaltCloudNotFound, opennebula.get_location, 'my-vm') + """ + with patch( + "salt.cloud.clouds.opennebula.avail_locations", MagicMock(return_value={}) + ): + with patch( + "salt.config.get_cloud_config_value", MagicMock(return_value="foo") + ): + self.assertRaises(SaltCloudNotFound, opennebula.get_location, "my-vm") def test_get_location_success(self): - ''' + """ Tests that the image is returned successfully. - ''' - with patch('salt.cloud.clouds.opennebula.avail_locations', - MagicMock(return_value={'my-host': {'name': 'my-host', 'id': 0}})): - with patch('salt.config.get_cloud_config_value', MagicMock(return_value='my-host')): - self.assertEqual(opennebula.get_location('my-host'), 0) + """ + with patch( + "salt.cloud.clouds.opennebula.avail_locations", + MagicMock(return_value={"my-host": {"name": "my-host", "id": 0}}), + ): + with patch( + "salt.config.get_cloud_config_value", MagicMock(return_value="my-host") + ): + self.assertEqual(opennebula.get_location("my-host"), 0) def test_get_secgroup_id_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call get_host_id with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_secgroup_id, - call='action') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.get_secgroup_id, call="action" + ) def test_get_secgroup_id_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_secgroup_id, - None, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.get_secgroup_id, None, call="foo" + ) def test_get_secgroup_id_not_found(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - with patch('salt.cloud.clouds.opennebula.list_security_groups', - MagicMock(return_value={'test-security-group': {'id': '100'}})): - self.assertRaises(SaltCloudSystemExit, - opennebula.get_secgroup_id, - kwargs={'name': 'test'}, - call='function') + """ + with patch( + "salt.cloud.clouds.opennebula.list_security_groups", + MagicMock(return_value={"test-security-group": {"id": "100"}}), + ): + self.assertRaises( + SaltCloudSystemExit, + opennebula.get_secgroup_id, + kwargs={"name": "test"}, + call="function", + ) def test_get_secgroup_id_success(self): - ''' + """ Tests that the function returns successfully. - ''' - with patch('salt.cloud.clouds.opennebula.list_security_groups', - MagicMock(return_value={'test-secgroup': {'id': '100'}})): - mock_id = '100' - mock_kwargs = {'name': 'test-secgroup'} - self.assertEqual(opennebula.get_secgroup_id(mock_kwargs, 'foo'), - mock_id) + """ + with patch( + "salt.cloud.clouds.opennebula.list_security_groups", + MagicMock(return_value={"test-secgroup": {"id": "100"}}), + ): + mock_id = "100" + mock_kwargs = {"name": "test-secgroup"} + self.assertEqual(opennebula.get_secgroup_id(mock_kwargs, "foo"), mock_id) def test_get_template_id_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call get_template_id with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_template_id, - call='action') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.get_template_id, call="action" + ) def test_get_template_id_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_template_id, - None, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.get_template_id, None, call="foo" + ) def test_get_template_id_not_found(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - with patch('salt.cloud.clouds.opennebula.list_templates', - MagicMock(return_value={'test-template': {'id': '100'}})): - self.assertRaises(SaltCloudSystemExit, - opennebula.get_template_id, - kwargs={'name': 'test'}, - call='function') + """ + with patch( + "salt.cloud.clouds.opennebula.list_templates", + MagicMock(return_value={"test-template": {"id": "100"}}), + ): + self.assertRaises( + SaltCloudSystemExit, + opennebula.get_template_id, + kwargs={"name": "test"}, + call="function", + ) def test_get_template_id_success(self): - ''' + """ Tests that the function returns successfully. - ''' - with patch('salt.cloud.clouds.opennebula.list_templates', - MagicMock(return_value={'test-template': {'id': '100'}})): - mock_id = '100' - mock_kwargs = {'name': 'test-template'} - self.assertEqual(opennebula.get_template_id(mock_kwargs, 'foo'), - mock_id) + """ + with patch( + "salt.cloud.clouds.opennebula.list_templates", + MagicMock(return_value={"test-template": {"id": "100"}}), + ): + mock_id = "100" + mock_kwargs = {"name": "test-template"} + self.assertEqual(opennebula.get_template_id(mock_kwargs, "foo"), mock_id) def test_get_vm_id_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call get_vm_id with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_vm_id, - call='action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.get_vm_id, call="action") def test_get_vm_id_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_vm_id, - None, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.get_vm_id, None, call="foo") def test_get_vm_id_not_found(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - with patch('salt.cloud.clouds.opennebula.list_nodes', - MagicMock(return_value={'test-vm': {'id': '100'}})): - self.assertRaises(SaltCloudSystemExit, - opennebula.get_vm_id, - kwargs={'name': 'test'}, - call='function') + """ + with patch( + "salt.cloud.clouds.opennebula.list_nodes", + MagicMock(return_value={"test-vm": {"id": "100"}}), + ): + self.assertRaises( + SaltCloudSystemExit, + opennebula.get_vm_id, + kwargs={"name": "test"}, + call="function", + ) def test_get_vm_id_success(self): - ''' + """ Tests that the function returns successfully. - ''' - with patch('salt.cloud.clouds.opennebula.list_nodes', - MagicMock(return_value={'test-vm': {'id': '100'}})): - mock_id = '100' - mock_kwargs = {'name': 'test-vm'} - self.assertEqual(opennebula.get_vm_id(mock_kwargs, 'foo'), - mock_id) + """ + with patch( + "salt.cloud.clouds.opennebula.list_nodes", + MagicMock(return_value={"test-vm": {"id": "100"}}), + ): + mock_id = "100" + mock_kwargs = {"name": "test-vm"} + self.assertEqual(opennebula.get_vm_id(mock_kwargs, "foo"), mock_id) def test_get_vn_id_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call get_vn_id with --action or -a. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_vn_id, - call='action') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.get_vn_id, call="action") def test_get_vn_id_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.get_vn_id, - None, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.get_vn_id, None, call="foo") def test_get_vn_id_not_found(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no name is provided. - ''' - with patch('salt.cloud.clouds.opennebula.list_vns', - MagicMock(return_value={'test-vn': {'id': '100'}})): - self.assertRaises(SaltCloudSystemExit, - opennebula.get_vn_id, - kwargs={'name': 'test'}, - call='function') + """ + with patch( + "salt.cloud.clouds.opennebula.list_vns", + MagicMock(return_value={"test-vn": {"id": "100"}}), + ): + self.assertRaises( + SaltCloudSystemExit, + opennebula.get_vn_id, + kwargs={"name": "test"}, + call="function", + ) def test_get_vn_id_success(self): - ''' + """ Tests that the function returns successfully. - ''' - with patch('salt.cloud.clouds.opennebula.list_vns', - MagicMock(return_value={'test-vn': {'id': '100'}})): - mock_id = '100' - mock_kwargs = {'name': 'test-vn'} - self.assertEqual(opennebula.get_vn_id(mock_kwargs, 'foo'), - mock_id) + """ + with patch( + "salt.cloud.clouds.opennebula.list_vns", + MagicMock(return_value={"test-vn": {"id": "100"}}), + ): + mock_id = "100" + mock_kwargs = {"name": "test-vn"} + self.assertEqual(opennebula.get_vn_id(mock_kwargs, "foo"), mock_id) # TODO: Write tests for create function def test_destroy_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.destroy, 'my-vm', 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.destroy, "my-vm", "function") def test_image_allocate_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_allocate, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_allocate, "foo") def test_image_allocate_no_name_or_datastore_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when a neither a datastore_id nor a datastore_name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_allocate, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_allocate, "function") def test_image_allocate_no_path_or_data(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when neither the path nor data args are provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_allocate, - 'function', - kwargs={'datastore_id': '5'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_allocate, + "function", + kwargs={"datastore_id": "5"}, + ) def test_image_clone_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_clone, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_clone, "foo") def test_image_clone_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when a name isn't provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_clone, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_clone, "function") def test_image_clone_no_image_id_or_image_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when neither the image_id nor the image_name args are provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_clone, - 'function', - kwargs={'name': 'test'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_clone, + "function", + kwargs={"name": "test"}, + ) - @skipIf(True, 'Need to figure out how to mock calls to the O.N. API first.') + @skipIf(True, "Need to figure out how to mock calls to the O.N. API first.") def test_image_clone_success(self): - ''' + """ Tests that image_clone returns successfully - ''' - with patch('image.clone', MagicMock(return_value=[True, 11, 0])): - name = 'test-image' + """ + with patch("image.clone", MagicMock(return_value=[True, 11, 0])): + name = "test-image" expected = { - 'action': 'image.clone', - 'cloned': 'True', - 'cloned_image_id': '11', - 'cloned_image_name': name, - 'error_code': '0', + "action": "image.clone", + "cloned": "True", + "cloned_image_id": "11", + "cloned_image_name": name, + "error_code": "0", } - ret = opennebula.image_clone('function', kwargs={'name': name, 'image_id': 1}) + ret = opennebula.image_clone( + "function", kwargs={"name": name, "image_id": 1} + ) self.assertEqual(expected, ret) def test_image_delete_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_delete, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_delete, "foo") def test_image_delete_no_name_or_image_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when a neither an image_id nor a name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_delete, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_delete, "function") def test_image_info_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_info, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_info, "foo") def test_image_info_no_image_id_or_image_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when a neither an image_id nor a name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_info, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_info, "function") def test_image_persist_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_persistent, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_persistent, "foo") def test_image_persist_no_persist(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the persist kwarg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_persistent, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_persistent, "function") def test_image_persist_no_name_or_image_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when a neither an image_id nor a name is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_delete, - 'function', - kwargs={'persist': False}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_delete, + "function", + kwargs={"persist": False}, + ) def test_image_snapshot_delete_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_snapshot_delete, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.image_snapshot_delete, call="foo" + ) def test_image_snapshot_delete_no_snapshot_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the snapshot_id kwarg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_snapshot_delete, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_snapshot_delete, + call="function", + kwargs=None, + ) def test_image_snapshot_delete_no_image_name_or_image_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the image_id and image_name kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_snapshot_delete, - call='function', - kwargs={'snapshot_id': 0}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_snapshot_delete, + call="function", + kwargs={"snapshot_id": 0}, + ) def test_image_snapshot_revert_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_snapshot_revert, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.image_snapshot_revert, call="foo" + ) def test_image_snapshot_revert_no_snapshot_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the snapshot_id kwarg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_snapshot_revert, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_snapshot_revert, + call="function", + kwargs=None, + ) def test_image_snapshot_revert_no_image_name_or_image_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the image_id and image_name kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_snapshot_revert, - call='function', - kwargs={'snapshot_id': 0}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_snapshot_revert, + call="function", + kwargs={"snapshot_id": 0}, + ) def test_image_snapshot_flatten_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_snapshot_flatten, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.image_snapshot_flatten, call="foo" + ) def test_image_snapshot_flatten_no_snapshot_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the snapshot_id kwarg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_snapshot_flatten, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_snapshot_flatten, + call="function", + kwargs=None, + ) def test_image_snapshot_flatten_no_image_name_or_image_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the image_id and image_name kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_snapshot_flatten, - call='function', - kwargs={'snapshot_id': 0}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_snapshot_flatten, + call="function", + kwargs={"snapshot_id": 0}, + ) def test_image_update_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_update, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_update, "foo") def test_image_update_no_update_type(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the update_type kwarg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.image_update, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.image_update, "function") def test_image_update_bad_update_type_value(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the update_type kwarg is not a valid value. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_update, - 'function', - kwargs={'update_type': 'foo'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_update, + "function", + kwargs={"update_type": "foo"}, + ) def test_image_update_no_image_id_or_image_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the image_id and image_name kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_update, - 'function', - kwargs={'update_type': 'merge'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_update, + "function", + kwargs={"update_type": "merge"}, + ) def test_image_update_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.image_update, - 'function', - kwargs={'update_type': 'merge', 'image_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.image_update, + "function", + kwargs={"update_type": "merge", "image_id": "0"}, + ) def test_show_instance_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.show_instance, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.show_instance, VM_NAME, call="foo" + ) def test_show_instance_success(self): - ''' + """ Tests that the node was found successfully. - ''' - with patch('salt.cloud.clouds.opennebula._get_node', - MagicMock(return_value={'my-vm': {'name': 'my-vm', 'id': 0}})): - ret = {'my-vm': {'name': 'my-vm', 'id': 0}} - self.assertEqual(opennebula.show_instance('my-vm', call='action'), ret) + """ + with patch( + "salt.cloud.clouds.opennebula._get_node", + MagicMock(return_value={"my-vm": {"name": "my-vm", "id": 0}}), + ): + ret = {"my-vm": {"name": "my-vm", "id": 0}} + self.assertEqual(opennebula.show_instance("my-vm", call="action"), ret) def test_secgroup_allocate_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_allocate, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_allocate, "foo") def test_secgroup_allocate_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_allocate, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_allocate, "function") def test_secgroup_clone_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_clone, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_clone, "foo") def test_secgroup_clone_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the name kwarg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_clone, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_clone, "function") def test_secgroup_clone_no_secgroup_id_or_secgroup_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the secgroup_id and secgroup_name kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.secgroup_clone, - 'function', - kwargs={'name': 'test'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.secgroup_clone, + "function", + kwargs={"name": "test"}, + ) def test_secgroup_delete_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_delete, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_delete, "foo") def test_secgroup_delete_no_secgroup_id_or_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the secgroup_id and name kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_clone, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_clone, "function") def test_secgroup_info_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_info, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_info, "foo") def test_secgroup_info_no_secgroup_id_or_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the secgroup_id and name kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_info, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_info, "function") def test_secgroup_update_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_update, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_update, "foo") def test_secgroup_update_no_update_type(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the update_type arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_update, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.secgroup_update, "function") def test_secgroup_update_bad_update_type_value(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the update_type contains an invalid value. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.secgroup_update, - 'function', - kwargs={'update_type': 'foo'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.secgroup_update, + "function", + kwargs={"update_type": "foo"}, + ) def test_secgroup_update_no_secgroup_id_or_secgroup_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the secgroup_id and secgroup_name kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.secgroup_update, - 'function', - kwargs={'update_type': 'merge'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.secgroup_update, + "function", + kwargs={"update_type": "merge"}, + ) def test_secgroup_update_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.secgroup_update, - 'function', - kwargs={'update_type': 'merge', 'secgroup_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.secgroup_update, + "function", + kwargs={"update_type": "merge", "secgroup_id": "0"}, + ) def test_template_allocate_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.template_allocate, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.template_allocate, "foo") def test_template_allocate_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.template_allocate, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.template_allocate, "function") def test_template_clone_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.template_clone, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.template_clone, "foo") def test_template_clone_no_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the name arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.template_clone, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.template_clone, "function") def test_template_clone_no_template_name_or_template_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the template_name and template_id args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.template_clone, - 'function', - kwargs={'name': 'foo'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.template_clone, + "function", + kwargs={"name": "foo"}, + ) def test_template_delete_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.template_delete, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.template_delete, "foo") def test_template_delete_no_name_or_template_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the name and template_id args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.template_delete, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.template_delete, "function") def test_template_instantiate_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.template_instantiate, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.template_instantiate, "foo") def test_template_instantiate_no_vm_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the vm_name arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.template_instantiate, - 'function', - None) + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.template_instantiate, "function", None + ) def test_template_instantiate_no_template_id_or_template_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the template_name and template_id args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.template_instantiate, - 'function', - kwargs={'vm_name': 'test'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.template_instantiate, + "function", + kwargs={"vm_name": "test"}, + ) def test_template_update_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.template_update, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.template_update, call="foo") def test_template_update_bad_update_type_value(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the update_type contains and invalid value. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.template_update, - call='function', - kwargs={'update_type': 'foo'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.template_update, + call="function", + kwargs={"update_type": "foo"}, + ) def test_template_update_no_template_id_or_template_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the template_id and the template_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.template_update, - call='function', - kwargs={'update_type': 'merge'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.template_update, + call="function", + kwargs={"update_type": "merge"}, + ) def test_template_update_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and the path args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.template_update, - call='function', - kwargs={'update_type': 'merge', - 'template_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.template_update, + call="function", + kwargs={"update_type": "merge", "template_id": "0"}, + ) def test_vm_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_action, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_action, VM_NAME, call="foo" + ) def test_vm_action_no_action(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the action arg is missing - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_action, - VM_NAME, - call='action') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_action, VM_NAME, call="action" + ) def test_vm_allocate_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.vm_allocate, 'foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vm_allocate, "foo") def test_vm_allocate_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, opennebula.vm_allocate, 'function') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vm_allocate, "function") def test_vm_attach_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_attach, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_attach, VM_NAME, call="foo" + ) def test_vm_attach_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_attach, - VM_NAME, - call='action') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_attach, VM_NAME, call="action" + ) def test_vm_attach_nic_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_attach_nic, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_attach_nic, VM_NAME, call="foo" + ) def test_vm_attach_nic_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path kwargs are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_attach_nic, - VM_NAME, - call='action') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_attach_nic, VM_NAME, call="action" + ) def test_vm_deploy_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_deploy, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_deploy, VM_NAME, call="foo" + ) def test_vm_deploy_no_host_id_or_host_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the host_id and the host_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_deploy, - VM_NAME, - call='action', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_deploy, + VM_NAME, + call="action", + kwargs=None, + ) def test_vm_detach_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_detach, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_detach, VM_NAME, call="foo" + ) def test_vm_detach_no_disk_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the disk_id ar is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_detach, - VM_NAME, - call='action') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_detach, VM_NAME, call="action" + ) def test_vm_detach_nic_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_detach_nic, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_detach_nic, VM_NAME, call="foo" + ) def test_vm_detach_nic_no_nic_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the nic_id arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_detach_nic, - VM_NAME, - call='action') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_detach_nic, VM_NAME, call="action" + ) def test_vm_disk_save_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_save, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_disk_save, VM_NAME, call="foo" + ) def test_vm_disk_save_no_disk_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the disk_id arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_save, - VM_NAME, - call='action', - kwargs={'image_name': 'foo'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_disk_save, + VM_NAME, + call="action", + kwargs={"image_name": "foo"}, + ) def test_vm_disk_save_no_image_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the image_name arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_save, - VM_NAME, - call='action', - kwargs={'disk_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_disk_save, + VM_NAME, + call="action", + kwargs={"disk_id": "0"}, + ) def test_vm_disk_snapshot_create_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_snapshot_create, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_disk_snapshot_create, VM_NAME, call="foo" + ) def test_vm_disk_snapshot_create_no_disk_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the disk_id arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_snapshot_create, - VM_NAME, - call='action', - kwargs={'description': 'foo'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_disk_snapshot_create, + VM_NAME, + call="action", + kwargs={"description": "foo"}, + ) def test_vm_disk_snapshot_create_no_description(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the image_name arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_snapshot_create, - VM_NAME, - call='action', - kwargs={'disk_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_disk_snapshot_create, + VM_NAME, + call="action", + kwargs={"disk_id": "0"}, + ) def test_vm_disk_snapshot_delete_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_snapshot_delete, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_disk_snapshot_delete, VM_NAME, call="foo" + ) def test_vm_disk_snapshot_delete_no_disk_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the disk_id arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_snapshot_delete, - VM_NAME, - call='action', - kwargs={'snapshot_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_disk_snapshot_delete, + VM_NAME, + call="action", + kwargs={"snapshot_id": "0"}, + ) def test_vm_disk_snapshot_delete_no_snapshot_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the snapshot_id arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_snapshot_delete, - VM_NAME, - call='action', - kwargs={'disk_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_disk_snapshot_delete, + VM_NAME, + call="action", + kwargs={"disk_id": "0"}, + ) def test_vm_disk_snapshot_revert_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_snapshot_revert, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_disk_snapshot_revert, VM_NAME, call="foo" + ) def test_vm_disk_snapshot_revert_no_disk_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the disk_id arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_snapshot_revert, - VM_NAME, - call='action', - kwargs={'snapshot_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_disk_snapshot_revert, + VM_NAME, + call="action", + kwargs={"snapshot_id": "0"}, + ) def test_vm_disk_snapshot_revert_no_snapshot_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the snapshot_id arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_disk_snapshot_revert, - VM_NAME, - call='action', - kwargs={'disk_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_disk_snapshot_revert, + VM_NAME, + call="action", + kwargs={"disk_id": "0"}, + ) def test_vm_info_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_info, - VM_NAME, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vm_info, VM_NAME, call="foo") def test_vm_migrate_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_migrate, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_migrate, VM_NAME, call="foo" + ) def test_vm_migrate_no_datastore_id_or_datastore_name(self): - ''' + """ Tests that a SaltCLoudSystemExit is raised when the datastore_id and the datastore_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_migrate, - VM_NAME, - call='action', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_migrate, + VM_NAME, + call="action", + kwargs=None, + ) def test_vm_migrate_no_host_id_or_host_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the the host_id and the host_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_migrate, - VM_NAME, - call='action', - kwargs={'datastore_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_migrate, + VM_NAME, + call="action", + kwargs={"datastore_id": "0"}, + ) def test_vm_monitoring_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_monitoring, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_monitoring, VM_NAME, call="foo" + ) def test_vm_resize_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_resize, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_resize, VM_NAME, call="foo" + ) def test_vm_resize_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_resize, - VM_NAME, - call='action', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_resize, + VM_NAME, + call="action", + kwargs=None, + ) def test_vm_snapshot_create_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_snapshot_create, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_snapshot_create, VM_NAME, call="foo" + ) def test_vm_snapshot_create_no_snapshot_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the snapshot_name arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_snapshot_create, - VM_NAME, - call='action', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_snapshot_create, + VM_NAME, + call="action", + kwargs=None, + ) def test_vm_snapshot_delete_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_snapshot_delete, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_snapshot_delete, VM_NAME, call="foo" + ) def test_vm_snapshot_delete_no_snapshot_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the snapshot_id arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_snapshot_delete, - VM_NAME, - call='action', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_snapshot_delete, + VM_NAME, + call="action", + kwargs=None, + ) def test_vm_snapshot_revert_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_snapshot_revert, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_snapshot_revert, VM_NAME, call="foo" + ) def test_vm_snapshot_revert_no_snapshot_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the snapshot_id arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_snapshot_revert, - VM_NAME, - call='action', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_snapshot_revert, + VM_NAME, + call="action", + kwargs=None, + ) def test_vm_update_action_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --action or -a is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_update, - VM_NAME, - call='foo') + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vm_update, VM_NAME, call="foo" + ) def test_vm_update_no_update_type(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the update_type arg is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_update, - VM_NAME, - call='action', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_update, + VM_NAME, + call="action", + kwargs=None, + ) def test_vm_update_bad_update_type_value(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the update_type kwarg is not a valid value. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_update, - VM_NAME, - call='action', - kwargs={'update_type': 'foo'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_update, + VM_NAME, + call="action", + kwargs={"update_type": "foo"}, + ) def test_vm_update_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vm_update, - VM_NAME, - call='action', - kwargs={'update_type': 'merge'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vm_update, + VM_NAME, + call="action", + kwargs={"update_type": "merge"}, + ) def test_vn_add_ar_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_add_ar, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vn_add_ar, call="foo") def test_vn_add_ar_no_vn_id_or_vn_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the vn_id and vn_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_add_ar, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vn_add_ar, call="function", kwargs=None + ) def test_vn_add_ar_no_path_or_data(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the path and data args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_add_ar, - call='function', - kwargs={'vn_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vn_add_ar, + call="function", + kwargs={"vn_id": "0"}, + ) def test_vn_allocate_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_allocate, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vn_allocate, call="foo") def test_vn_allocate_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the path and data args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_allocate, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vn_allocate, call="function", kwargs=None + ) def test_vn_delete_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_delete, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vn_delete, call="foo") def test_vn_delete_no_vn_id_or_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the vn_id and name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_delete, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vn_delete, call="function", kwargs=None + ) def test_vn_free_ar_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_free_ar, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vn_free_ar, call="foo") def test_vn_free_ar_no_ar_id(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the ar_id is missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_free_ar, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vn_free_ar, call="function", kwargs=None + ) def test_vn_free_ar_no_vn_id_or_vn_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the vn_id and vn_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_free_ar, - call='function', - kwargs={'ar_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vn_free_ar, + call="function", + kwargs={"ar_id": "0"}, + ) def test_vn_hold_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_hold, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vn_hold, call="foo") def test_vn_hold_no_vn_id_or_vn_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the vn_id and vn_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_hold, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vn_hold, call="function", kwargs=None + ) def test_vn_hold_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_hold, - call='function', - kwargs={'vn_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vn_hold, + call="function", + kwargs={"vn_id": "0"}, + ) def test_vn_info_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_info, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vn_info, call="foo") def test_vn_info_no_vn_id_or_vn_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the vn_id and vn_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_info, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vn_info, call="function", kwargs=None + ) def test_vn_release_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_release, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vn_release, call="foo") def test_vn_release_no_vn_id_or_vn_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the vn_id and vn_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_release, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vn_release, call="function", kwargs=None + ) def test_vn_release_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_release, - call='function', - kwargs={'vn_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vn_release, + call="function", + kwargs={"vn_id": "0"}, + ) def test_vn_reserve_function_error(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when something other than --function or -f is provided. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_reserve, - call='foo') + """ + self.assertRaises(SaltCloudSystemExit, opennebula.vn_reserve, call="foo") def test_vn_reserve_no_vn_id_or_vn_name(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the vn_id and vn_name args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_reserve, - call='function', - kwargs=None) + """ + self.assertRaises( + SaltCloudSystemExit, opennebula.vn_reserve, call="function", kwargs=None + ) def test_vn_reserve_no_data_or_path(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the data and path args are missing. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula.vn_reserve, - call='function', - kwargs={'vn_id': '0'}) + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula.vn_reserve, + call="function", + kwargs={"vn_id": "0"}, + ) - @skipIf(not HAS_XML_LIBS, 'cannot find lxml python library') + @skipIf(not HAS_XML_LIBS, "cannot find lxml python library") def test__get_xml(self): - ''' + """ Tests that invalid XML raises SaltCloudSystemExit. - ''' - self.assertRaises(SaltCloudSystemExit, - opennebula._get_xml, - "[VirtualMachinePoolInfo] User couldn't be" - " authenticated, aborting call.") + """ + self.assertRaises( + SaltCloudSystemExit, + opennebula._get_xml, + "[VirtualMachinePoolInfo] User couldn't be" + " authenticated, aborting call.", + ) diff --git a/tests/unit/cloud/clouds/test_openstack.py b/tests/unit/cloud/clouds/test_openstack.py index 59ae1bdb181..b4ed92128cf 100644 --- a/tests/unit/cloud/clouds/test_openstack.py +++ b/tests/unit/cloud/clouds/test_openstack.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: `Tyler Johnson <tjohnson@saltstack.com>` tests.unit.cloud.clouds.openstack_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -14,20 +14,20 @@ from salt.cloud.clouds import openstack # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import patch +from tests.support.unit import TestCase class MockImage(object): - name = 'image name' - id = 'image id' + name = "image name" + id = "image id" class MockNode(object): - name = 'node name' - id = 'node id' + name = "node name" + id = "node id" flavor = MockImage() - status = 'node status' + status = "node status" def __init__(self, image): self.image = image @@ -54,78 +54,84 @@ class MockConn(object): class OpenstackTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Unit TestCase for salt.cloud.clouds.openstack module. - ''' + """ def setup_loader_modules(self): return { openstack: { - '__active_provider_name__': '', - '__opts__': { - 'providers': { - 'my-openstack-cloud': { - 'openstack': { - 'auth': 'daenerys', - 'region_name': 'westeros', - 'cloud': 'openstack', + "__active_provider_name__": "", + "__opts__": { + "providers": { + "my-openstack-cloud": { + "openstack": { + "auth": "daenerys", + "region_name": "westeros", + "cloud": "openstack", } } } - } + }, } } def test_get_configured_provider_bad(self): - with patch.dict(openstack.__opts__, {'providers': {}}): + with patch.dict(openstack.__opts__, {"providers": {}}): result = openstack.get_configured_provider() self.assertEqual(result, False) def test_get_configured_provider_auth(self): config = { - 'region_name': 'westeros', - 'auth': 'daenerys', + "region_name": "westeros", + "auth": "daenerys", } - with patch.dict(openstack.__opts__, {'providers': {'my-openstack-cloud': {'openstack': config}}}): + with patch.dict( + openstack.__opts__, + {"providers": {"my-openstack-cloud": {"openstack": config}}}, + ): result = openstack.get_configured_provider() self.assertEqual(config, result) def test_get_configured_provider_cloud(self): config = { - 'region_name': 'westeros', - 'cloud': 'foo', + "region_name": "westeros", + "cloud": "foo", } - with patch.dict(openstack.__opts__, {'providers': {'my-openstack-cloud': {'openstack': config}}}): + with patch.dict( + openstack.__opts__, + {"providers": {"my-openstack-cloud": {"openstack": config}}}, + ): result = openstack.get_configured_provider() self.assertEqual(config, result) def test_get_dependencies(self): - HAS_SHADE = (True, 'Please install newer version of shade: >= 1.19.0') - with patch('salt.cloud.clouds.openstack.HAS_SHADE', HAS_SHADE): + HAS_SHADE = (True, "Please install newer version of shade: >= 1.19.0") + with patch("salt.cloud.clouds.openstack.HAS_SHADE", HAS_SHADE): result = openstack.get_dependencies() self.assertEqual(result, True) def test_get_dependencies_no_shade(self): - HAS_SHADE = (False, 'Install pypi module shade >= 1.19.0') - with patch('salt.cloud.clouds.openstack.HAS_SHADE', HAS_SHADE): + HAS_SHADE = (False, "Install pypi module shade >= 1.19.0") + with patch("salt.cloud.clouds.openstack.HAS_SHADE", HAS_SHADE): result = openstack.get_dependencies() self.assertEqual(result, False) def test_list_nodes_full_image_str(self): - node_image = 'node image' + node_image = "node image" conn = MockConn(node_image) - with patch('salt.cloud.clouds.openstack._get_ips', return_value=[]): + with patch("salt.cloud.clouds.openstack._get_ips", return_value=[]): ret = openstack.list_nodes_full(conn=conn) - self.assertEqual(ret[conn.node.name]['image'], node_image) + self.assertEqual(ret[conn.node.name]["image"], node_image) def test_list_nodes_full_image_obj(self): conn = MockConn(MockImage()) - with patch('salt.cloud.clouds.openstack._get_ips', return_value=[]): + with patch("salt.cloud.clouds.openstack._get_ips", return_value=[]): ret = openstack.list_nodes_full(conn=conn) - self.assertEqual(ret[conn.node.name]['image'], MockImage.name) + self.assertEqual(ret[conn.node.name]["image"], MockImage.name) def test_show_instance(self): conn = MockConn(MockImage()) - with patch('salt.cloud.clouds.openstack._get_ips', return_value=[]): - ret = openstack.show_instance(conn.node.name, conn=conn, call='action') - self.assertEqual(ret['image'], MockImage.name) + with patch("salt.cloud.clouds.openstack._get_ips", return_value=[]): + ret = openstack.show_instance(conn.node.name, conn=conn, call="action") + self.assertEqual(ret["image"], MockImage.name) diff --git a/tests/unit/cloud/clouds/test_proxmox.py b/tests/unit/cloud/clouds/test_proxmox.py index cc6b22f1249..b531dcba3dc 100644 --- a/tests/unit/cloud/clouds/test_proxmox.py +++ b/tests/unit/cloud/clouds/test_proxmox.py @@ -1,82 +1,89 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Tyler Johnson <tjohnson@saltstack.com> -''' +""" # Import Salt Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs from salt.cloud.clouds import proxmox +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ProxmoxTest(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { proxmox: { - '__utils__': { - 'cloud.fire_event': MagicMock(), - 'cloud.bootstrap': MagicMock() + "__utils__": { + "cloud.fire_event": MagicMock(), + "cloud.bootstrap": MagicMock(), }, - '__opts__': { - 'sock_dir': True, - 'transport': True, - 'providers': {'my_proxmox': {}}, - 'profiles': {'my_proxmox': {}} + "__opts__": { + "sock_dir": True, + "transport": True, + "providers": {"my_proxmox": {}}, + "profiles": {"my_proxmox": {}}, }, - '__active_provider_name__': 'my_proxmox:proxmox' + "__active_provider_name__": "my_proxmox:proxmox", } } def setUp(self): self.vm_ = { - 'profile': 'my_proxmox', - 'name': 'vm4', - 'driver': 'proxmox', - 'technology': 'qemu', - 'host': '127.0.0.1', - 'clone': True, - 'ide0': 'data', - 'sata0': 'data', - 'scsi0': 'data', - 'net0': 'a=b,c=d', + "profile": "my_proxmox", + "name": "vm4", + "driver": "proxmox", + "technology": "qemu", + "host": "127.0.0.1", + "clone": True, + "ide0": "data", + "sata0": "data", + "scsi0": "data", + "net0": "a=b,c=d", } def tearDown(self): del self.vm_ def test__stringlist_to_dictionary(self): - result = proxmox._stringlist_to_dictionary('') + result = proxmox._stringlist_to_dictionary("") self.assertEqual(result, {}) - result = proxmox._stringlist_to_dictionary('foo=bar, ignored_space=bar,internal space=bar') - self.assertEqual(result, {'foo': 'bar', 'ignored_space': 'bar', 'internal space': 'bar'}) + result = proxmox._stringlist_to_dictionary( + "foo=bar, ignored_space=bar,internal space=bar" + ) + self.assertEqual( + result, {"foo": "bar", "ignored_space": "bar", "internal space": "bar"} + ) # Negative cases - self.assertRaises(ValueError, proxmox._stringlist_to_dictionary, 'foo=bar,foo') - self.assertRaises(ValueError, proxmox._stringlist_to_dictionary, 'foo=bar,totally=invalid=assignment') + self.assertRaises(ValueError, proxmox._stringlist_to_dictionary, "foo=bar,foo") + self.assertRaises( + ValueError, + proxmox._stringlist_to_dictionary, + "foo=bar,totally=invalid=assignment", + ) def test__dictionary_to_stringlist(self): result = proxmox._dictionary_to_stringlist({}) - self.assertEqual(result, '') + self.assertEqual(result, "") - result = proxmox._dictionary_to_stringlist({'a': 'a'}) - self.assertEqual(result, 'a=a') + result = proxmox._dictionary_to_stringlist({"a": "a"}) + self.assertEqual(result, "a=a") - result = proxmox._dictionary_to_stringlist({'a': 'a', 'b': 'b'}) - self.assertEqual(result, 'a=a,b=b') + result = proxmox._dictionary_to_stringlist({"a": "a", "b": "b"}) + self.assertEqual(result, "a=a,b=b") def test__reconfigure_clone(self): # The return_value is for the net reconfigure assertions, it is irrelevant for the rest - with patch.object(proxmox, 'query', return_value={'net0': 'c=overwritten,g=h'}) as query: + with patch.object( + proxmox, "query", return_value={"net0": "c=overwritten,g=h"} + ) as query: # Test a vm that lacks the required attributes proxmox._reconfigure_clone({}, 0) query.assert_not_called() @@ -85,10 +92,18 @@ class ProxmoxTest(TestCase, LoaderModuleMockMixin): proxmox._reconfigure_clone(self.vm_, 0) # net reconfigure - query.assert_any_call('get', 'nodes/127.0.0.1/qemu/0/config') - query.assert_any_call('post', 'nodes/127.0.0.1/qemu/0/config', {'net0': 'a=b,c=d,g=h'}) + query.assert_any_call("get", "nodes/127.0.0.1/qemu/0/config") + query.assert_any_call( + "post", "nodes/127.0.0.1/qemu/0/config", {"net0": "a=b,c=d,g=h"} + ) # hdd reconfigure - query.assert_any_call('post', 'nodes/127.0.0.1/qemu/0/config', {'ide0': 'data'}) - query.assert_any_call('post', 'nodes/127.0.0.1/qemu/0/config', {'sata0': 'data'}) - query.assert_any_call('post', 'nodes/127.0.0.1/qemu/0/config', {'scsi0': 'data'}) + query.assert_any_call( + "post", "nodes/127.0.0.1/qemu/0/config", {"ide0": "data"} + ) + query.assert_any_call( + "post", "nodes/127.0.0.1/qemu/0/config", {"sata0": "data"} + ) + query.assert_any_call( + "post", "nodes/127.0.0.1/qemu/0/config", {"scsi0": "data"} + ) diff --git a/tests/unit/cloud/clouds/test_saltify.py b/tests/unit/cloud/clouds/test_saltify.py index 0d8a6c15d4e..83cb45adbc9 100644 --- a/tests/unit/cloud/clouds/test_saltify.py +++ b/tests/unit/cloud/clouds/test_saltify.py @@ -1,188 +1,176 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Alexander Schwartz <alexander.schwartz@gmx.net> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch, ANY - - # Import Salt Libs import salt.client from salt.cloud.clouds import saltify +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import ANY, MagicMock, patch +from tests.support.unit import TestCase + TEST_PROFILES = { - 'testprofile1': NotImplemented, - 'testprofile2': { # this profile is used in test_saltify_destroy() - 'ssh_username': 'fred', - 'remove_config_on_destroy': False, # expected for test - 'shutdown_on_destroy': True # expected value for test - }, - 'testprofile3': { # this profile is used in test_create_wake_on_lan() - 'wake_on_lan_mac': 'aa-bb-cc-dd-ee-ff', - 'wol_sender_node': 'friend1', - 'wol_boot_wait': 0.01 # we want the wait to be very short - } - } -TEST_PROFILE_NAMES = ['testprofile1', 'testprofile2', 'testprofile3'] + "testprofile1": NotImplemented, + "testprofile2": { # this profile is used in test_saltify_destroy() + "ssh_username": "fred", + "remove_config_on_destroy": False, # expected for test + "shutdown_on_destroy": True, # expected value for test + }, + "testprofile3": { # this profile is used in test_create_wake_on_lan() + "wake_on_lan_mac": "aa-bb-cc-dd-ee-ff", + "wol_sender_node": "friend1", + "wol_boot_wait": 0.01, # we want the wait to be very short + }, +} +TEST_PROFILE_NAMES = ["testprofile1", "testprofile2", "testprofile3"] class SaltifyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.cloud.clouds.saltify - ''' + """ + LOCAL_OPTS = { - 'providers': { - 'sfy1': { - 'saltify': { - 'driver': 'saltify', - 'profiles': TEST_PROFILES - } - }, - }, - 'profiles': TEST_PROFILES, - 'sock_dir': '/var/sockxxx', - 'transport': 'tcp', - } + "providers": { + "sfy1": {"saltify": {"driver": "saltify", "profiles": TEST_PROFILES}}, + }, + "profiles": TEST_PROFILES, + "sock_dir": "/var/sockxxx", + "transport": "tcp", + } def setup_loader_modules(self): saltify_globals = { - '__active_provider_name__': '', - '__utils__': { - 'cloud.bootstrap': MagicMock(), - 'cloud.fire_event': MagicMock(), - }, - '__opts__': self.LOCAL_OPTS, - } + "__active_provider_name__": "", + "__utils__": { + "cloud.bootstrap": MagicMock(), + "cloud.fire_event": MagicMock(), + }, + "__opts__": self.LOCAL_OPTS, + } return {saltify: saltify_globals} def test_create_no_deploy(self): - ''' + """ Test if deployment fails. This is the most basic test as saltify doesn't contain much logic - ''' - with patch('salt.cloud.clouds.saltify._verify', MagicMock(return_value=True)): - vm = {'deploy': False, - 'driver': 'saltify', - 'name': 'dummy' - } + """ + with patch("salt.cloud.clouds.saltify._verify", MagicMock(return_value=True)): + vm = {"deploy": False, "driver": "saltify", "name": "dummy"} self.assertTrue(saltify.create(vm)) def test_create_and_deploy(self): - ''' + """ Test if deployment can be done. - ''' + """ mock_cmd = MagicMock(return_value=True) with patch.dict( - 'salt.cloud.clouds.saltify.__utils__', - {'cloud.bootstrap': mock_cmd}): - vm_ = {'deploy': True, - 'driver': 'saltify', - 'name': 'new2', - 'profile': 'testprofile2', - } + "salt.cloud.clouds.saltify.__utils__", {"cloud.bootstrap": mock_cmd} + ): + vm_ = { + "deploy": True, + "driver": "saltify", + "name": "new2", + "profile": "testprofile2", + } result = saltify.create(vm_) mock_cmd.assert_called_once_with(vm_, ANY) self.assertTrue(result) def test_create_wake_on_lan(self): - ''' + """ Test if wake on lan works - ''' + """ mock_sleep = MagicMock() mock_cmd = MagicMock(return_value=True) - mm_cmd = MagicMock(return_value={'friend1': True}) + mm_cmd = MagicMock(return_value={"friend1": True}) lcl = salt.client.LocalClient() lcl.cmd = mm_cmd - with patch('time.sleep', mock_sleep): - with patch('salt.client.LocalClient', return_value=lcl): + with patch("time.sleep", mock_sleep): + with patch("salt.client.LocalClient", return_value=lcl): with patch.dict( - 'salt.cloud.clouds.saltify.__utils__', - {'cloud.bootstrap': mock_cmd}): - vm_ = {'deploy': True, - 'driver': 'saltify', - 'name': 'new1', - 'profile': 'testprofile3', - } + "salt.cloud.clouds.saltify.__utils__", {"cloud.bootstrap": mock_cmd} + ): + vm_ = { + "deploy": True, + "driver": "saltify", + "name": "new1", + "profile": "testprofile3", + } result = saltify.create(vm_) mock_cmd.assert_called_once_with(vm_, ANY) - mm_cmd.assert_called_with('friend1', 'network.wol', ['aa-bb-cc-dd-ee-ff']) + mm_cmd.assert_called_with( + "friend1", "network.wol", ["aa-bb-cc-dd-ee-ff"] + ) # The test suite might call time.sleep, look for any call # that has the expected wait time. mock_sleep.assert_any_call(0.01) self.assertTrue(result) def test_avail_locations(self): - ''' + """ Test the avail_locations will always return {} - ''' + """ self.assertEqual(saltify.avail_locations(), {}) def test_avail_sizes(self): - ''' + """ Test the avail_sizes will always return {} - ''' + """ self.assertEqual(saltify.avail_sizes(), {}) def test_avail_images(self): - ''' + """ Test the avail_images will return profiles - ''' + """ testlist = list(TEST_PROFILE_NAMES) # copy - self.assertEqual( - saltify.avail_images()['Profiles'].sort(), - testlist.sort()) + self.assertEqual(saltify.avail_images()["Profiles"].sort(), testlist.sort()) def test_list_nodes(self): - ''' + """ Test list_nodes will return required fields only - ''' + """ testgrains = { - 'nodeX1': { - 'id': 'nodeX1', - 'ipv4': [ - '127.0.0.1', '192.1.2.22', '172.16.17.18'], - 'ipv6': [ - '::1', 'fdef:bad:add::f00', '3001:DB8::F00D'], - 'salt-cloud': { - 'driver': 'saltify', - 'provider': 'saltyfy', - 'profile': 'testprofile2' - }, - 'extra_stuff': 'does not belong' - } + "nodeX1": { + "id": "nodeX1", + "ipv4": ["127.0.0.1", "192.1.2.22", "172.16.17.18"], + "ipv6": ["::1", "fdef:bad:add::f00", "3001:DB8::F00D"], + "salt-cloud": { + "driver": "saltify", + "provider": "saltyfy", + "profile": "testprofile2", + }, + "extra_stuff": "does not belong", } + } expected_result = { - 'nodeX1': { - 'id': 'nodeX1', - 'image': 'testprofile2', - 'private_ips': [ - '172.16.17.18', 'fdef:bad:add::f00'], - 'public_ips': [ - '192.1.2.22', '3001:DB8::F00D'], - 'size': '', - 'state': 'running' - } + "nodeX1": { + "id": "nodeX1", + "image": "testprofile2", + "private_ips": ["172.16.17.18", "fdef:bad:add::f00"], + "public_ips": ["192.1.2.22", "3001:DB8::F00D"], + "size": "", + "state": "running", } + } mm_cmd = MagicMock(return_value=testgrains) lcl = salt.client.LocalClient() lcl.cmd = mm_cmd - with patch('salt.client.LocalClient', return_value=lcl): - self.assertEqual( - saltify.list_nodes(), - expected_result) + with patch("salt.client.LocalClient", return_value=lcl): + self.assertEqual(saltify.list_nodes(), expected_result) def test_saltify_reboot(self): mm_cmd = MagicMock(return_value=True) lcl = salt.client.LocalClient() lcl.cmd = mm_cmd - with patch('salt.client.LocalClient', return_value=lcl): - result = saltify.reboot('nodeS1', 'action') - mm_cmd.assert_called_with('nodeS1', 'system.reboot') + with patch("salt.client.LocalClient", return_value=lcl): + result = saltify.reboot("nodeS1", "action") + mm_cmd.assert_called_with("nodeS1", "system.reboot") self.assertTrue(result) def test_saltify_destroy(self): @@ -192,21 +180,24 @@ class SaltifyTestCase(TestCase, LoaderModuleMockMixin): # NOTE: this assumes that the call order never changes, # so to keep things simple, we will not use remove_config... result_list = [ - {'nodeS1': { # first call is grains.get - 'driver': 'saltify', - 'provider': 'saltify', - 'profile': 'testprofile2'} - }, - # Note: - # testprofile2 has remove_config_on_destroy: False - # and shutdown_on_destroy: True - {'nodeS1': # last call shuts down the minion - 'a system.shutdown worked message'}, - ] + { + "nodeS1": { # first call is grains.get + "driver": "saltify", + "provider": "saltify", + "profile": "testprofile2", + } + }, + # Note: + # testprofile2 has remove_config_on_destroy: False + # and shutdown_on_destroy: True + { + "nodeS1": "a system.shutdown worked message" # last call shuts down the minion + }, + ] mm_cmd = MagicMock(side_effect=result_list) lcl = salt.client.LocalClient() lcl.cmd = mm_cmd - with patch('salt.client.LocalClient', return_value=lcl): - result = saltify.destroy('nodeS1', 'action') - mm_cmd.assert_called_with('nodeS1', 'system.shutdown') + with patch("salt.client.LocalClient", return_value=lcl): + result = saltify.destroy("nodeS1", "action") + mm_cmd.assert_called_with("nodeS1", "system.shutdown") self.assertTrue(result) diff --git a/tests/unit/cloud/clouds/test_vmware.py b/tests/unit/cloud/clouds/test_vmware.py index e9edbce9c41..0af01a0606d 100644 --- a/tests/unit/cloud/clouds/test_vmware.py +++ b/tests/unit/cloud/clouds/test_vmware.py @@ -1,25 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: `Nitin Madhok <nmadhok@clemson.edu>` tests.unit.cloud.clouds.vmware_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -from copy import deepcopy -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch +from copy import deepcopy # Import Salt Libs from salt import config from salt.cloud.clouds import vmware from salt.exceptions import SaltCloudSystemExit +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + # Attempt to import pyVim and pyVmomi libs HAS_LIBS = True # pylint: disable=import-error,no-name-in-module,unused-import @@ -32,36 +33,36 @@ except ImportError: # Global Variables PROVIDER_CONFIG = { - 'vcenter01': { - 'vmware': { - 'driver': 'vmware', - 'url': 'vcenter01.domain.com', - 'user': 'DOMAIN\\user', - 'password': 'verybadpass', + "vcenter01": { + "vmware": { + "driver": "vmware", + "url": "vcenter01.domain.com", + "user": "DOMAIN\\user", + "password": "verybadpass", } } } -VM_NAME = 'test-vm' +VM_NAME = "test-vm" PROFILE = { - 'base-gold': { - 'provider': 'vcenter01:vmware', - 'datastore': 'Datastore1', - 'resourcepool': 'Resources', - 'folder': 'vm' + "base-gold": { + "provider": "vcenter01:vmware", + "datastore": "Datastore1", + "resourcepool": "Resources", + "folder": "vm", } } class ExtendedTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Extended TestCase class containing additional helper methods. - ''' + """ def setup_loader_modules(self): return { vmware: { - '__virtual__': MagicMock(return_value='vmware'), - '__active_provider_name__': '' + "__virtual__": MagicMock(return_value="vmware"), + "__active_provider_name__": "", } } @@ -74,831 +75,679 @@ class ExtendedTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(exc.message, exc_msg) -@skipIf(not HAS_LIBS, 'Install pyVmomi to be able to run this test.') +@skipIf(not HAS_LIBS, "Install pyVmomi to be able to run this test.") class VMwareTestCase(ExtendedTestCase): - ''' + """ Unit TestCase for salt.cloud.clouds.vmware module. - ''' + """ def test_test_vcenter_connection_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call test_vcenter_connection with anything other than --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.test_vcenter_connection, - call='action' + SaltCloudSystemExit, vmware.test_vcenter_connection, call="action" ) def test_get_vcenter_version_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call get_vcenter_version with anything other than --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.get_vcenter_version, - call='action' + SaltCloudSystemExit, vmware.get_vcenter_version, call="action" ) def test_avail_images_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call avail_images with --action or -a. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.avail_images, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.avail_images, call="action") def test_avail_locations_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call avail_locations with --action or -a. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.avail_locations, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.avail_locations, call="action") def test_avail_sizes_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call avail_sizes with --action or -a. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.avail_sizes, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.avail_sizes, call="action") def test_list_datacenters_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_datacenters with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_datacenters, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_datacenters, call="action") def test_list_clusters_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_clusters with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_clusters, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_clusters, call="action") def test_list_datastore_clusters_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_datastore_clusters with anything other than --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.list_datastore_clusters, - call='action' + SaltCloudSystemExit, vmware.list_datastore_clusters, call="action" ) def test_list_datastores_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_datastores with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_datastores, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_datastores, call="action") def test_list_hosts_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_hosts with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_hosts, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_hosts, call="action") def test_list_resourcepools_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_resourcepools with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_resourcepools, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_resourcepools, call="action") def test_list_networks_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_networks with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_networks, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_networks, call="action") def test_list_nodes_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_nodes with --action or -a. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_nodes, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_nodes, call="action") def test_list_nodes_min_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_nodes_min with --action or -a. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_nodes_min, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_nodes_min, call="action") def test_list_nodes_full_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_nodes_full with --action or -a. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_nodes_full, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_nodes_full, call="action") def test_list_nodes_select_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_nodes_full with --action or -a. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_nodes_select, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_nodes_select, call="action") def test_list_folders_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_folders with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_folders, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_folders, call="action") def test_list_snapshots_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_snapshots with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_snapshots, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_snapshots, call="action") def test_list_hosts_by_cluster_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_hosts_by_cluster with anything other than --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.list_hosts_by_cluster, - call='action' + SaltCloudSystemExit, vmware.list_hosts_by_cluster, call="action" ) def test_list_clusters_by_datacenter_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_clusters_by_datacenter with anything other than --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.list_clusters_by_datacenter, - call='action' + SaltCloudSystemExit, vmware.list_clusters_by_datacenter, call="action" ) def test_list_hosts_by_datacenter_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_hosts_by_datacenter with anything other than --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.list_hosts_by_datacenter, - call='action' + SaltCloudSystemExit, vmware.list_hosts_by_datacenter, call="action" ) def test_list_hbas_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_hbas with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_hbas, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_hbas, call="action") def test_list_dvs_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_dvs with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_dvs, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_dvs, call="action") def test_list_vapps_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_vapps with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_vapps, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_vapps, call="action") def test_list_templates_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call list_templates with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.list_templates, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.list_templates, call="action") def test_create_datacenter_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call create_datacenter with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.create_datacenter, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.create_datacenter, call="action") def test_create_cluster_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call create_cluster with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.create_cluster, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.create_cluster, call="action") def test_rescan_hba_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call rescan_hba with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.rescan_hba, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.rescan_hba, call="action") def test_upgrade_tools_all_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call upgrade_tools_all with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.upgrade_tools_all, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.upgrade_tools_all, call="action") def test_enter_maintenance_mode_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call enter_maintenance_mode with anything other than --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.enter_maintenance_mode, - call='action' + SaltCloudSystemExit, vmware.enter_maintenance_mode, call="action" ) def test_exit_maintenance_mode_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call exit_maintenance_mode with anything other than --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.exit_maintenance_mode, - call='action' + SaltCloudSystemExit, vmware.exit_maintenance_mode, call="action" ) def test_create_folder_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call create_folder with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.create_folder, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.create_folder, call="action") def test_add_host_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call add_host with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.add_host, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.add_host, call="action") def test_remove_host_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call remove_host with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.remove_host, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.remove_host, call="action") def test_connect_host_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call connect_host with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.connect_host, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.connect_host, call="action") def test_disconnect_host_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call disconnect_host with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.disconnect_host, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.disconnect_host, call="action") def test_reboot_host_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call reboot_host with anything other than --function or -f. - ''' - self.assertRaises( - SaltCloudSystemExit, - vmware.reboot_host, - call='action' - ) + """ + self.assertRaises(SaltCloudSystemExit, vmware.reboot_host, call="action") def test_create_datastore_cluster_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call create_datastore_cluster with anything other than --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.create_datastore_cluster, - call='action' + SaltCloudSystemExit, vmware.create_datastore_cluster, call="action" ) def test_show_instance_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call show_instance with anything other than --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.show_instance, - name=VM_NAME, - call='function' + SaltCloudSystemExit, vmware.show_instance, name=VM_NAME, call="function" ) def test_start_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call start with anything other than --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.start, - name=VM_NAME, - call='function' + SaltCloudSystemExit, vmware.start, name=VM_NAME, call="function" ) def test_stop_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call stop with anything other than --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.stop, - name=VM_NAME, - call='function' + SaltCloudSystemExit, vmware.stop, name=VM_NAME, call="function" ) def test_suspend_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call suspend with anything other than --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.suspend, - name=VM_NAME, - call='function' + SaltCloudSystemExit, vmware.suspend, name=VM_NAME, call="function" ) def test_reset_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call reset with anything other than --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.reset, - name=VM_NAME, - call='function' + SaltCloudSystemExit, vmware.reset, name=VM_NAME, call="function" ) def test_terminate_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call terminate with anything other than --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.terminate, - name=VM_NAME, - call='function' + SaltCloudSystemExit, vmware.terminate, name=VM_NAME, call="function" ) def test_destroy_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call destroy with --function or -f. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.destroy, - name=VM_NAME, - call='function' + SaltCloudSystemExit, vmware.destroy, name=VM_NAME, call="function" ) def test_upgrade_tools_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call upgrade_tools with anything other than --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.upgrade_tools, - name=VM_NAME, - call='function' + SaltCloudSystemExit, vmware.upgrade_tools, name=VM_NAME, call="function" ) def test_create_snapshot_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call create_snapshot with anything other than --action or -a. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.create_snapshot, - name=VM_NAME, - call='function' + SaltCloudSystemExit, vmware.create_snapshot, name=VM_NAME, call="function" ) def test_revert_to_snapshot_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call revert_to_snapshot with anything other than --action or -a. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.revert_to_snapshot, name=VM_NAME, - call='function' + call="function", ) def test_remove_snapshot_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call remove_snapshot with anything other than --action or -a. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.remove_snapshot, name=VM_NAME, - kwargs={'snapshot_name': 'mySnapshot'}, - call='function' + kwargs={"snapshot_name": "mySnapshot"}, + call="function", ) def test_remove_snapshot_call_no_snapshot_name_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when name is not present in kwargs. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.remove_snapshot, - name=VM_NAME, - call='action' + SaltCloudSystemExit, vmware.remove_snapshot, name=VM_NAME, call="action" ) def test_remove_all_snapshots_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call remove_all_snapshots with anything other than --action or -a. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.remove_all_snapshots, name=VM_NAME, - call='function' + call="function", ) def test_convert_to_template_call(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when trying to call convert_to_template with anything other than --action or -a. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.convert_to_template, name=VM_NAME, - call='function' + call="function", ) def test_avail_sizes(self): - ''' + """ Tests that avail_sizes returns an empty dictionary. - ''' - self.assertEqual( - vmware.avail_sizes(call='foo'), - {} - ) + """ + self.assertEqual(vmware.avail_sizes(call="foo"), {}) def test_create_datacenter_no_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no kwargs are provided to create_datacenter. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.create_datacenter, - kwargs=None, - call='function') + SaltCloudSystemExit, vmware.create_datacenter, kwargs=None, call="function" + ) def test_create_datacenter_no_name_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when name is not present in kwargs that are provided to create_datacenter. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_datacenter, - kwargs={'foo': 'bar'}, - call='function') + kwargs={"foo": "bar"}, + call="function", + ) def test_create_datacenter_name_too_short(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when name is present in kwargs that are provided to create_datacenter but is an empty string. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_datacenter, - kwargs={'name': ''}, - call='function') + kwargs={"name": ""}, + call="function", + ) def test_create_datacenter_name_too_long(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when name is present in kwargs that are provided to create_datacenter but is a string with length <= 80. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_datacenter, - kwargs={'name': 'cCD2GgJGPG1DUnPeFBoPeqtdmUxIWxDoVFbA14vIG0BPoUECkgbRMnnY6gaUPBvIDCcsZ5HU48ubgQu5c'}, - call='function') + kwargs={ + "name": "cCD2GgJGPG1DUnPeFBoPeqtdmUxIWxDoVFbA14vIG0BPoUECkgbRMnnY6gaUPBvIDCcsZ5HU48ubgQu5c" + }, + call="function", + ) def test_create_cluster_no_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no kwargs are provided to create_cluster. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.create_cluster, - kwargs=None, - call='function') + SaltCloudSystemExit, vmware.create_cluster, kwargs=None, call="function" + ) def test_create_cluster_no_name_no_datacenter_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when neither the name nor the datacenter is present in kwargs that are provided to create_cluster. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_cluster, - kwargs={'foo': 'bar'}, - call='function') + kwargs={"foo": "bar"}, + call="function", + ) def test_create_cluster_no_datacenter_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the name is present but the datacenter is not present in kwargs that are provided to create_cluster. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_cluster, - kwargs={'name': 'my-cluster'}, - call='function') + kwargs={"name": "my-cluster"}, + call="function", + ) def test_create_cluster_no_name_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the datacenter is present but the name is not present in kwargs that are provided to create_cluster. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_cluster, - kwargs={'datacenter': 'my-datacenter'}, - call='function') + kwargs={"datacenter": "my-datacenter"}, + call="function", + ) def test_rescan_hba_no_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no kwargs are provided to rescan_hba. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.rescan_hba, - kwargs=None, - call='function') + SaltCloudSystemExit, vmware.rescan_hba, kwargs=None, call="function" + ) def test_rescan_hba_no_host_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when host is not present in kwargs that are provided to rescan_hba. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.rescan_hba, - kwargs={'foo': 'bar'}, - call='function') + kwargs={"foo": "bar"}, + call="function", + ) def test_create_snapshot_no_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no kwargs are provided to create_snapshot. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_snapshot, name=VM_NAME, kwargs=None, - call='action') + call="action", + ) def test_create_snapshot_no_snapshot_name_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when snapshot_name is not present in kwargs that are provided to create_snapshot. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_snapshot, name=VM_NAME, - kwargs={'foo': 'bar'}, - call='action') + kwargs={"foo": "bar"}, + call="action", + ) def test_add_host_no_esxi_host_user_in_config(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when esxi_host_user is not specified in the cloud provider configuration when calling add_host. - ''' - with patch.dict(vmware.__opts__, {'providers': PROVIDER_CONFIG}, clean=True): + """ + with patch.dict(vmware.__opts__, {"providers": PROVIDER_CONFIG}, clean=True): self.assertRaisesWithMessage( SaltCloudSystemExit, - 'You must specify the ESXi host username in your providers config.', + "You must specify the ESXi host username in your providers config.", vmware.add_host, kwargs=None, - call='function') + call="function", + ) def test_add_host_no_esxi_host_password_in_config(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when esxi_host_password is not specified in the cloud provider configuration when calling add_host. - ''' + """ provider_config_additions = { - 'esxi_host_user': 'root', + "esxi_host_user": "root", } provider_config = deepcopy(PROVIDER_CONFIG) - provider_config['vcenter01']['vmware'].update(provider_config_additions) + provider_config["vcenter01"]["vmware"].update(provider_config_additions) - with patch.dict(vmware.__opts__, {'providers': provider_config}, clean=True): + with patch.dict(vmware.__opts__, {"providers": provider_config}, clean=True): self.assertRaisesWithMessage( SaltCloudSystemExit, - 'You must specify the ESXi host password in your providers config.', + "You must specify the ESXi host password in your providers config.", vmware.add_host, kwargs=None, - call='function') + call="function", + ) def test_no_clonefrom_just_image(self): - ''' + """ Tests that the profile is configured correctly when deploying using an image - ''' + """ - profile_additions = { - 'image': 'some-image.iso' - } + profile_additions = {"image": "some-image.iso"} provider_config = deepcopy(PROVIDER_CONFIG) profile = deepcopy(PROFILE) - profile['base-gold'].update(profile_additions) + profile["base-gold"].update(profile_additions) - provider_config_additions = { - 'profiles': profile - } - provider_config['vcenter01']['vmware'].update(provider_config_additions) - vm_ = {'profile': profile} - with patch.dict(vmware.__opts__, {'providers': provider_config}, clean=True): - self.assertEqual(config.is_profile_configured(vmware.__opts__, 'vcenter01:vmware', - 'base-gold', vm_=vm_), True) + provider_config_additions = {"profiles": profile} + provider_config["vcenter01"]["vmware"].update(provider_config_additions) + vm_ = {"profile": profile} + with patch.dict(vmware.__opts__, {"providers": provider_config}, clean=True): + self.assertEqual( + config.is_profile_configured( + vmware.__opts__, "vcenter01:vmware", "base-gold", vm_=vm_ + ), + True, + ) def test_just_clonefrom(self): - ''' + """ Tests that the profile is configured correctly when deploying by cloning from a template - ''' + """ profile_additions = { - 'clonefrom': 'test-template', - 'image': 'should ignore image' + "clonefrom": "test-template", + "image": "should ignore image", } provider_config = deepcopy(PROVIDER_CONFIG) profile = deepcopy(PROFILE) - profile['base-gold'].update(profile_additions) + profile["base-gold"].update(profile_additions) - provider_config_additions = { - 'profiles': profile - } - provider_config['vcenter01']['vmware'].update(provider_config_additions) - vm_ = {'profile': profile} - with patch.dict(vmware.__opts__, {'providers': provider_config}, clean=True): - self.assertEqual(config.is_profile_configured(vmware.__opts__, 'vcenter01:vmware', - 'base-gold', vm_=vm_), True) + provider_config_additions = {"profiles": profile} + provider_config["vcenter01"]["vmware"].update(provider_config_additions) + vm_ = {"profile": profile} + with patch.dict(vmware.__opts__, {"providers": provider_config}, clean=True): + self.assertEqual( + config.is_profile_configured( + vmware.__opts__, "vcenter01:vmware", "base-gold", vm_=vm_ + ), + True, + ) def test_add_new_ide_controller_helper(self): - ''' + """ Tests that creating a new controller, ensuring that it will generate a controller key if one is not provided - ''' - with patch('salt.cloud.clouds.vmware.randint', return_value=101) as randint_mock: - controller_label = 'Some label' + """ + with patch( + "salt.cloud.clouds.vmware.randint", return_value=101 + ) as randint_mock: + controller_label = "Some label" bus_number = 1 - spec = vmware._add_new_ide_controller_helper(controller_label, None, bus_number) + spec = vmware._add_new_ide_controller_helper( + controller_label, None, bus_number + ) self.assertEqual(spec.device.key, randint_mock.return_value) - spec = vmware._add_new_ide_controller_helper(controller_label, 200, bus_number) + spec = vmware._add_new_ide_controller_helper( + controller_label, 200, bus_number + ) self.assertEqual(spec.device.key, 200) self.assertEqual(spec.device.busNumber, bus_number) @@ -906,421 +755,474 @@ class VMwareTestCase(ExtendedTestCase): self.assertEqual(spec.device.deviceInfo.summary, controller_label) def test_manage_devices_just_cd(self): - ''' + """ Tests that when adding IDE/CD drives, controller keys will be in the apparent safe-range on ESX 5.5 but randomly generated on other versions (i.e. 6) - ''' + """ device_map = { - 'ide': { - 'IDE 0': {}, - 'IDE 1': {} - }, - 'cd': { - 'CD/DVD Drive 1': {'controller': 'IDE 0'} - } + "ide": {"IDE 0": {}, "IDE 1": {}}, + "cd": {"CD/DVD Drive 1": {"controller": "IDE 0"}}, } - with patch('salt.cloud.clouds.vmware.get_vcenter_version', return_value='VMware ESXi 5.5.0'): - specs = vmware._manage_devices(device_map, vm=None)['device_specs'] + with patch( + "salt.cloud.clouds.vmware.get_vcenter_version", + return_value="VMware ESXi 5.5.0", + ): + specs = vmware._manage_devices(device_map, vm=None)["device_specs"] - self.assertEqual(specs[0].device.key, vmware.SAFE_ESX_5_5_CONTROLLER_KEY_INDEX) - self.assertEqual(specs[1].device.key, vmware.SAFE_ESX_5_5_CONTROLLER_KEY_INDEX+1) - self.assertEqual(specs[2].device.controllerKey, vmware.SAFE_ESX_5_5_CONTROLLER_KEY_INDEX) + self.assertEqual( + specs[0].device.key, vmware.SAFE_ESX_5_5_CONTROLLER_KEY_INDEX + ) + self.assertEqual( + specs[1].device.key, vmware.SAFE_ESX_5_5_CONTROLLER_KEY_INDEX + 1 + ) + self.assertEqual( + specs[2].device.controllerKey, vmware.SAFE_ESX_5_5_CONTROLLER_KEY_INDEX + ) - with patch('salt.cloud.clouds.vmware.get_vcenter_version', return_value='VMware ESXi 6'): - with patch('salt.cloud.clouds.vmware.randint', return_value=100) as first_key: - specs = vmware._manage_devices(device_map, vm=None)['device_specs'] + with patch( + "salt.cloud.clouds.vmware.get_vcenter_version", return_value="VMware ESXi 6" + ): + with patch( + "salt.cloud.clouds.vmware.randint", return_value=100 + ) as first_key: + specs = vmware._manage_devices(device_map, vm=None)["device_specs"] self.assertEqual(specs[0].device.key, first_key.return_value) self.assertEqual(specs[2].device.controllerKey, first_key.return_value) def test_add_host_no_host_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when host is not present in kwargs that are provided to add_host. - ''' + """ provider_config_additions = { - 'esxi_host_user': 'root', - 'esxi_host_password': 'myhostpassword' + "esxi_host_user": "root", + "esxi_host_password": "myhostpassword", } provider_config = deepcopy(PROVIDER_CONFIG) - provider_config['vcenter01']['vmware'].update(provider_config_additions) + provider_config["vcenter01"]["vmware"].update(provider_config_additions) - with patch.dict(vmware.__opts__, {'providers': provider_config}, clean=True): + with patch.dict(vmware.__opts__, {"providers": provider_config}, clean=True): self.assertRaisesWithMessage( SaltCloudSystemExit, - 'You must specify either the IP or DNS name of the host system.', + "You must specify either the IP or DNS name of the host system.", vmware.add_host, - kwargs={'foo': 'bar'}, - call='function') + kwargs={"foo": "bar"}, + call="function", + ) def test_add_host_both_cluster_and_datacenter_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when both cluster and datacenter are present in kwargs that are provided to add_host. - ''' + """ provider_config_additions = { - 'esxi_host_user': 'root', - 'esxi_host_password': 'myhostpassword' + "esxi_host_user": "root", + "esxi_host_password": "myhostpassword", } provider_config = deepcopy(PROVIDER_CONFIG) - provider_config['vcenter01']['vmware'].update(provider_config_additions) + provider_config["vcenter01"]["vmware"].update(provider_config_additions) - with patch.dict(vmware.__opts__, {'providers': provider_config}, clean=True): + with patch.dict(vmware.__opts__, {"providers": provider_config}, clean=True): self.assertRaisesWithMessage( SaltCloudSystemExit, - 'You must specify either the cluster name or the datacenter name.', + "You must specify either the cluster name or the datacenter name.", vmware.add_host, - kwargs={'host': 'my-esxi-host', 'datacenter': 'my-datacenter', 'cluster': 'my-cluster'}, - call='function') + kwargs={ + "host": "my-esxi-host", + "datacenter": "my-datacenter", + "cluster": "my-cluster", + }, + call="function", + ) def test_add_host_neither_cluster_nor_datacenter_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when neither cluster nor datacenter is present in kwargs that are provided to add_host. - ''' + """ provider_config_additions = { - 'esxi_host_user': 'root', - 'esxi_host_password': 'myhostpassword' + "esxi_host_user": "root", + "esxi_host_password": "myhostpassword", } provider_config = deepcopy(PROVIDER_CONFIG) - provider_config['vcenter01']['vmware'].update(provider_config_additions) + provider_config["vcenter01"]["vmware"].update(provider_config_additions) - with patch.dict(vmware.__opts__, {'providers': provider_config}, clean=True): + with patch.dict(vmware.__opts__, {"providers": provider_config}, clean=True): self.assertRaisesWithMessage( SaltCloudSystemExit, - 'You must specify either the cluster name or the datacenter name.', + "You must specify either the cluster name or the datacenter name.", vmware.add_host, - kwargs={'host': 'my-esxi-host'}, - call='function') + kwargs={"host": "my-esxi-host"}, + call="function", + ) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_add_host_cluster_not_exists(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the specified cluster present in kwargs that are provided to add_host does not exist in the VMware environment. - ''' - with patch('salt.cloud.clouds.vmware._get_si', MagicMock(return_value=None)): - with patch('salt.utils.vmware.get_mor_by_property', MagicMock(return_value=None)): + """ + with patch("salt.cloud.clouds.vmware._get_si", MagicMock(return_value=None)): + with patch( + "salt.utils.vmware.get_mor_by_property", MagicMock(return_value=None) + ): provider_config_additions = { - 'esxi_host_user': 'root', - 'esxi_host_password': 'myhostpassword' + "esxi_host_user": "root", + "esxi_host_password": "myhostpassword", } provider_config = deepcopy(PROVIDER_CONFIG) - provider_config['vcenter01']['vmware'].update(provider_config_additions) + provider_config["vcenter01"]["vmware"].update(provider_config_additions) - with patch.dict(vmware.__opts__, {'providers': provider_config}, clean=True): + with patch.dict( + vmware.__opts__, {"providers": provider_config}, clean=True + ): self.assertRaisesWithMessage( SaltCloudSystemExit, - 'Specified cluster does not exist.', + "Specified cluster does not exist.", vmware.add_host, - kwargs={'host': 'my-esxi-host', 'cluster': 'my-cluster'}, - call='function') + kwargs={"host": "my-esxi-host", "cluster": "my-cluster"}, + call="function", + ) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_add_host_datacenter_not_exists(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the specified datacenter present in kwargs that are provided to add_host does not exist in the VMware environment. - ''' - with patch('salt.cloud.clouds.vmware._get_si', MagicMock(return_value=None)): - with patch('salt.utils.vmware.get_mor_by_property', MagicMock(return_value=None)): + """ + with patch("salt.cloud.clouds.vmware._get_si", MagicMock(return_value=None)): + with patch( + "salt.utils.vmware.get_mor_by_property", MagicMock(return_value=None) + ): provider_config_additions = { - 'esxi_host_user': 'root', - 'esxi_host_password': 'myhostpassword' + "esxi_host_user": "root", + "esxi_host_password": "myhostpassword", } provider_config = deepcopy(PROVIDER_CONFIG) - provider_config['vcenter01']['vmware'].update(provider_config_additions) + provider_config["vcenter01"]["vmware"].update(provider_config_additions) - with patch.dict(vmware.__opts__, {'providers': provider_config}, clean=True): + with patch.dict( + vmware.__opts__, {"providers": provider_config}, clean=True + ): self.assertRaisesWithMessage( SaltCloudSystemExit, - 'Specified datacenter does not exist.', + "Specified datacenter does not exist.", vmware.add_host, - kwargs={'host': 'my-esxi-host', 'datacenter': 'my-datacenter'}, - call='function') + kwargs={"host": "my-esxi-host", "datacenter": "my-datacenter"}, + call="function", + ) def test_remove_host_no_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no kwargs are provided to remove_host. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.remove_host, - kwargs=None, - call='function') + SaltCloudSystemExit, vmware.remove_host, kwargs=None, call="function" + ) def test_remove_host_no_host_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when host is not present in kwargs that are provided to remove_host. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.remove_host, - kwargs={'foo': 'bar'}, - call='function') + kwargs={"foo": "bar"}, + call="function", + ) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_remove_host_not_exists(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the specified host present in kwargs that are provided to remove_host does not exist in the VMware environment. - ''' - with patch('salt.cloud.clouds.vmware._get_si', MagicMock(return_value=None)): - with patch('salt.utils.vmware.get_mor_by_property', MagicMock(return_value=None)): + """ + with patch("salt.cloud.clouds.vmware._get_si", MagicMock(return_value=None)): + with patch( + "salt.utils.vmware.get_mor_by_property", MagicMock(return_value=None) + ): self.assertRaises( SaltCloudSystemExit, vmware.remove_host, - kwargs={'host': 'my-host'}, - call='function') + kwargs={"host": "my-host"}, + call="function", + ) def test_connect_host_no_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no kwargs are provided to connect_host. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.connect_host, - kwargs=None, - call='function') + SaltCloudSystemExit, vmware.connect_host, kwargs=None, call="function" + ) def test_connect_host_no_host_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when host is not present in kwargs that are provided to connect_host. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.connect_host, - kwargs={'foo': 'bar'}, - call='function') + kwargs={"foo": "bar"}, + call="function", + ) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_connect_host_not_exists(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the specified host present in kwargs that are provided to connect_host does not exist in the VMware environment. - ''' - with patch('salt.cloud.clouds.vmware._get_si', MagicMock(return_value=None)): - with patch('salt.utils.vmware.get_mor_by_property', MagicMock(return_value=None)): + """ + with patch("salt.cloud.clouds.vmware._get_si", MagicMock(return_value=None)): + with patch( + "salt.utils.vmware.get_mor_by_property", MagicMock(return_value=None) + ): self.assertRaises( SaltCloudSystemExit, vmware.connect_host, - kwargs={'host': 'my-host'}, - call='function') + kwargs={"host": "my-host"}, + call="function", + ) def test_disconnect_host_no_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no kwargs are provided to disconnect_host. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.disconnect_host, - kwargs=None, - call='function') + SaltCloudSystemExit, vmware.disconnect_host, kwargs=None, call="function" + ) def test_disconnect_host_no_host_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when host is not present in kwargs that are provided to disconnect_host. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.disconnect_host, - kwargs={'foo': 'bar'}, - call='function') + kwargs={"foo": "bar"}, + call="function", + ) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_disconnect_host_not_exists(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the specified host present in kwargs that are provided to disconnect_host does not exist in the VMware environment. - ''' - with patch('salt.cloud.clouds.vmware._get_si', MagicMock(return_value=None)): - with patch('salt.utils.vmware.get_mor_by_property', MagicMock(return_value=None)): + """ + with patch("salt.cloud.clouds.vmware._get_si", MagicMock(return_value=None)): + with patch( + "salt.utils.vmware.get_mor_by_property", MagicMock(return_value=None) + ): self.assertRaises( SaltCloudSystemExit, vmware.disconnect_host, - kwargs={'host': 'my-host'}, - call='function') + kwargs={"host": "my-host"}, + call="function", + ) def test_reboot_host_no_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no kwargs are provided to reboot_host. - ''' + """ self.assertRaises( - SaltCloudSystemExit, - vmware.reboot_host, - kwargs=None, - call='function') + SaltCloudSystemExit, vmware.reboot_host, kwargs=None, call="function" + ) def test_reboot_host_no_host_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when host is not present in kwargs that are provided to reboot_host. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.reboot_host, - kwargs={'foo': 'bar'}, - call='function') + kwargs={"foo": "bar"}, + call="function", + ) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_reboot_host_not_exists(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when the specified host present in kwargs that are provided to connect_host does not exist in the VMware environment. - ''' - with patch('salt.cloud.clouds.vmware._get_si', MagicMock(return_value=None)): - with patch('salt.utils.vmware.get_mor_by_property', MagicMock(return_value=None)): + """ + with patch("salt.cloud.clouds.vmware._get_si", MagicMock(return_value=None)): + with patch( + "salt.utils.vmware.get_mor_by_property", MagicMock(return_value=None) + ): self.assertRaises( SaltCloudSystemExit, vmware.reboot_host, - kwargs={'host': 'my-host'}, - call='function') + kwargs={"host": "my-host"}, + call="function", + ) def test_create_datastore_cluster_no_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when no kwargs are provided to create_datastore_cluster. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_datastore_cluster, kwargs=None, - call='function') + call="function", + ) def test_create_datastore_cluster_no_name_in_kwargs(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when name is not present in kwargs that are provided to create_datastore_cluster. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_datastore_cluster, - kwargs={'foo': 'bar'}, - call='function') + kwargs={"foo": "bar"}, + call="function", + ) def test_create_datastore_cluster_name_too_short(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when name is present in kwargs that are provided to create_datastore_cluster but is an empty string. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_datastore_cluster, - kwargs={'name': ''}, - call='function') + kwargs={"name": ""}, + call="function", + ) def test_create_datastore_cluster_name_too_long(self): - ''' + """ Tests that a SaltCloudSystemExit is raised when name is present in kwargs that are provided to create_datastore_cluster but is a string with length <= 80. - ''' + """ self.assertRaises( SaltCloudSystemExit, vmware.create_datastore_cluster, - kwargs={'name': 'cCD2GgJGPG1DUnPeFBoPeqtdmUxIWxDoVFbA14vIG0BPoUECkgbRMnnY6gaUPBvIDCcsZ5HU48ubgQu5c'}, - call='function') + kwargs={ + "name": "cCD2GgJGPG1DUnPeFBoPeqtdmUxIWxDoVFbA14vIG0BPoUECkgbRMnnY6gaUPBvIDCcsZ5HU48ubgQu5c" + }, + call="function", + ) def test__add_new_hard_disk_helper(self): - with patch('salt.cloud.clouds.vmware._get_si', MagicMock(return_value=None)): - with patch('salt.utils.vmware.get_mor_using_container_view', side_effect=[None, None]): + with patch("salt.cloud.clouds.vmware._get_si", MagicMock(return_value=None)): + with patch( + "salt.utils.vmware.get_mor_using_container_view", + side_effect=[None, None], + ): self.assertRaises( SaltCloudSystemExit, vmware._add_new_hard_disk_helper, - disk_label='test', + disk_label="test", size_gb=100, unit_number=0, - datastore='whatever' - ) - with patch('salt.utils.vmware.get_mor_using_container_view', side_effect=['Datastore', None]): + datastore="whatever", + ) + with patch( + "salt.utils.vmware.get_mor_using_container_view", + side_effect=["Datastore", None], + ): self.assertRaises( AttributeError, vmware._add_new_hard_disk_helper, - disk_label='test', + disk_label="test", size_gb=100, unit_number=0, - datastore='whatever' - ) - vmware.salt.utils.vmware.get_mor_using_container_view.assert_called_with(None, vim.Datastore, 'whatever') - with patch('salt.utils.vmware.get_mor_using_container_view', side_effect=[None, 'Cluster']): + datastore="whatever", + ) + vmware.salt.utils.vmware.get_mor_using_container_view.assert_called_with( + None, vim.Datastore, "whatever" + ) + with patch( + "salt.utils.vmware.get_mor_using_container_view", + side_effect=[None, "Cluster"], + ): self.assertRaises( AttributeError, vmware._add_new_hard_disk_helper, - disk_label='test', + disk_label="test", size_gb=100, unit_number=0, - datastore='whatever' - ) - vmware.salt.utils.vmware.get_mor_using_container_view.assert_called_with(None, vim.StoragePod, 'whatever') + datastore="whatever", + ) + vmware.salt.utils.vmware.get_mor_using_container_view.assert_called_with( + None, vim.StoragePod, "whatever" + ) class CloneFromSnapshotTest(TestCase): - ''' + """ Test functionality to clone from snapshot - ''' + """ + @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_quick_linked_clone(self): - ''' + """ Test that disk move type is set to createNewChildDiskBacking - ''' + """ self._test_clone_type(vmware.QUICK_LINKED_CLONE) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_current_state_linked_clone(self): - ''' + """ Test that disk move type is set to moveChildMostDiskBacking - ''' + """ self._test_clone_type(vmware.CURRENT_STATE_LINKED_CLONE) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_copy_all_disks_full_clone(self): - ''' + """ Test that disk move type is set to moveAllDiskBackingsAndAllowSharing - ''' + """ self._test_clone_type(vmware.COPY_ALL_DISKS_FULL_CLONE) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_flatten_all_all_disks_full_clone(self): - ''' + """ Test that disk move type is set to moveAllDiskBackingsAndDisallowSharing - ''' + """ self._test_clone_type(vmware.FLATTEN_DISK_FULL_CLONE) @skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.") def test_raises_error_for_invalid_disk_move_type(self): - ''' + """ Test that invalid disk move type raises error - ''' + """ with self.assertRaises(SaltCloudSystemExit): - self._test_clone_type('foobar') + self._test_clone_type("foobar") def _test_clone_type(self, clone_type): - ''' + """ Assertions for checking that a certain clone type works - ''' + """ obj_ref = MagicMock() obj_ref.snapshot = vim.vm.Snapshot(None, None) obj_ref.snapshot.currentSnapshot = vim.vm.Snapshot(None, None) @@ -1329,8 +1231,8 @@ class CloneFromSnapshotTest(TestCase): obj_ref, vim.vm.RelocateSpec(), False, - {'snapshot': { - 'disk_move_type': clone_type}}) + {"snapshot": {"disk_move_type": clone_type}}, + ) self.assertEqual(clone_spec.location.diskMoveType, clone_type) obj_ref2 = MagicMock() @@ -1342,7 +1244,7 @@ class CloneFromSnapshotTest(TestCase): obj_ref2, vim.vm.RelocateSpec(), True, - {'snapshot': { - 'disk_move_type': clone_type}}) + {"snapshot": {"disk_move_type": clone_type}}, + ) self.assertEqual(clone_spec2.location.diskMoveType, clone_type) diff --git a/tests/unit/cloud/test_libcloudfuncs.py b/tests/unit/cloud/test_libcloudfuncs.py index b3903cea39b..7ae1f89ead2 100644 --- a/tests/unit/cloud/test_libcloudfuncs.py +++ b/tests/unit/cloud/test_libcloudfuncs.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.cloud.test_libcloudfuncs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase - # Import Salt Libs import salt.cloud.libcloudfuncs as libcloud +# Import Salt Testing Libs +from tests.support.unit import TestCase + class LibcloudTestCase(TestCase): def test_node_state_libcloud_020(self): state = libcloud.node_state(2) - self.assertEqual('TERMINATED', state) + self.assertEqual("TERMINATED", state) def test_node_state_libcloud_100(self): - state = libcloud.node_state('terminated') - self.assertEqual('TERMINATED', state) + state = libcloud.node_state("terminated") + self.assertEqual("TERMINATED", state) diff --git a/tests/unit/config/schemas/test_ssh.py b/tests/unit/config/schemas/test_ssh.py index be53b142039..72c5ad307b2 100644 --- a/tests/unit/config/schemas/test_ssh.py +++ b/tests/unit/config/schemas/test_ssh.py @@ -1,31 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.unit.config.schemas.test_ssh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +import salt.utils.stringutils # Import Salt Libs from salt.config.schemas import ssh as ssh_schemas from salt.config.schemas.minion import MinionConfiguration -import salt.utils.stringutils from salt.utils.versions import LooseVersion as _LooseVersion +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf + # Import 3rd-party libs try: import jsonschema import jsonschema.exceptions + HAS_JSONSCHEMA = True JSONSCHEMA_VERSION = _LooseVersion(jsonschema.__version__) except ImportError: HAS_JSONSCHEMA = False - JSONSCHEMA_VERSION = _LooseVersion('0') + JSONSCHEMA_VERSION = _LooseVersion("0") class RosterEntryConfigTest(TestCase): @@ -33,229 +35,193 @@ class RosterEntryConfigTest(TestCase): config = ssh_schemas.RosterEntryConfig() expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'Roster Entry', - 'description': 'Salt SSH roster entry definition', - 'type': 'object', - 'properties': { - 'host': { - 'title': 'Host', - 'description': 'The IP address or DNS name of the remote host', - 'type': 'string', - 'pattern': r'^((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([A-Za-z0-9][A-Za-z0-9\.\-]{1,255}))$', - 'minLength': 1 + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Roster Entry", + "description": "Salt SSH roster entry definition", + "type": "object", + "properties": { + "host": { + "title": "Host", + "description": "The IP address or DNS name of the remote host", + "type": "string", + "pattern": r"^((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|([A-Za-z0-9][A-Za-z0-9\.\-]{1,255}))$", + "minLength": 1, }, - 'port': { - 'description': 'The target system\'s ssh port number', - 'title': 'Port', - 'default': 22, - 'maximum': 65535, - 'minimum': 0, - 'type': 'integer' + "port": { + "description": "The target system's ssh port number", + "title": "Port", + "default": 22, + "maximum": 65535, + "minimum": 0, + "type": "integer", }, - 'user': { - 'default': 'root', - 'type': 'string', - 'description': 'The user to log in as. Defaults to root', - 'title': 'User', - 'minLength': 1 + "user": { + "default": "root", + "type": "string", + "description": "The user to log in as. Defaults to root", + "title": "User", + "minLength": 1, }, - 'passwd': { - 'title': 'Password', - 'type': 'string', - 'description': 'The password to log in with', - 'format': 'secret', - 'minLength': 1 + "passwd": { + "title": "Password", + "type": "string", + "description": "The password to log in with", + "format": "secret", + "minLength": 1, }, - 'priv': { - 'type': 'string', - 'description': 'File path to ssh private key, defaults to salt-ssh.rsa', - 'title': 'Private Key', - 'minLength': 1 + "priv": { + "type": "string", + "description": "File path to ssh private key, defaults to salt-ssh.rsa", + "title": "Private Key", + "minLength": 1, }, - 'priv_passwd': { - 'type': 'string', - 'description': 'Passphrase for private key file', - 'title': 'Private Key passphrase', - 'format': 'secret', - 'minLength': 1, + "priv_passwd": { + "type": "string", + "description": "Passphrase for private key file", + "title": "Private Key passphrase", + "format": "secret", + "minLength": 1, }, - 'sudo': { - 'default': False, - 'type': 'boolean', - 'description': 'run command via sudo. Defaults to False', - 'title': 'Sudo' + "sudo": { + "default": False, + "type": "boolean", + "description": "run command via sudo. Defaults to False", + "title": "Sudo", }, - 'timeout': { - 'type': 'integer', - 'description': 'Number of seconds to wait for response when establishing an SSH connection', - 'title': 'Timeout' + "timeout": { + "type": "integer", + "description": "Number of seconds to wait for response when establishing an SSH connection", + "title": "Timeout", }, - 'thin_dir': { - 'type': 'string', - 'description': 'The target system\'s storage directory for Salt components. Defaults to /tmp/salt-<hash>.', - 'title': 'Thin Directory' + "thin_dir": { + "type": "string", + "description": "The target system's storage directory for Salt components. Defaults to /tmp/salt-<hash>.", + "title": "Thin Directory", }, # The actuall representation of the minion options would make this HUGE! - 'minion_opts': ssh_schemas.DictItem(title='Minion Options', - description='Dictionary of minion options', - properties=MinionConfiguration()).serialize(), + "minion_opts": ssh_schemas.DictItem( + title="Minion Options", + description="Dictionary of minion options", + properties=MinionConfiguration(), + ).serialize(), }, - 'anyOf': [ - { - 'required': [ - 'passwd' - ] - }, - { - 'required': [ - 'priv' - ] - } + "anyOf": [{"required": ["passwd"]}, {"required": ["priv"]}], + "required": ["host", "user"], + "x-ordering": [ + "host", + "port", + "user", + "passwd", + "priv", + "priv_passwd", + "sudo", + "timeout", + "thin_dir", + "minion_opts", ], - 'required': [ - 'host', - 'user', - ], - 'x-ordering': [ - 'host', - 'port', - 'user', - 'passwd', - 'priv', - 'priv_passwd', - 'sudo', - 'timeout', - 'thin_dir', - 'minion_opts' - ], - 'additionalProperties': False + "additionalProperties": False, } try: - self.assertDictContainsSubset(expected['properties'], config.serialize()['properties']) + self.assertDictContainsSubset( + expected["properties"], config.serialize()["properties"] + ) self.assertDictContainsSubset(expected, config.serialize()) except AssertionError: import salt.utils.json + print(salt.utils.json.dumps(config.serialize(), indent=4)) raise - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_config_validate(self): try: jsonschema.validate( - { - 'host': 'localhost', - 'user': 'root', - 'passwd': 'foo' - }, + {"host": "localhost", "user": "root", "passwd": "foo"}, ssh_schemas.RosterEntryConfig.serialize(), - format_checker=jsonschema.FormatChecker() + format_checker=jsonschema.FormatChecker(), ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) + + try: + jsonschema.validate( + {"host": "127.0.0.1", "user": "root", "passwd": "foo"}, + ssh_schemas.RosterEntryConfig.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + except jsonschema.exceptions.ValidationError as exc: + self.fail("ValidationError raised: {0}".format(exc)) + + try: + jsonschema.validate( + {"host": "127.1.0.1", "user": "root", "priv": "foo", "passwd": "foo"}, + ssh_schemas.RosterEntryConfig.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + except jsonschema.exceptions.ValidationError as exc: + self.fail("ValidationError raised: {0}".format(exc)) + + try: + jsonschema.validate( + {"host": "127.1.0.1", "user": "root", "passwd": "foo", "sudo": False}, + ssh_schemas.RosterEntryConfig.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + except jsonschema.exceptions.ValidationError as exc: + self.fail("ValidationError raised: {0}".format(exc)) try: jsonschema.validate( { - 'host': '127.0.0.1', - 'user': 'root', - 'passwd': 'foo' + "host": "127.1.0.1", + "user": "root", + "priv": "foo", + "passwd": "foo", + "thin_dir": "/foo/bar", }, ssh_schemas.RosterEntryConfig.serialize(), - format_checker=jsonschema.FormatChecker() + format_checker=jsonschema.FormatChecker(), ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: jsonschema.validate( { - 'host': '127.1.0.1', - 'user': 'root', - 'priv': 'foo', - 'passwd': 'foo' + "host": "127.1.0.1", + "user": "root", + "passwd": "foo", + "minion_opts": {"interface": "0.0.0.0"}, }, ssh_schemas.RosterEntryConfig.serialize(), - format_checker=jsonschema.FormatChecker() + format_checker=jsonschema.FormatChecker(), ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) - try: + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate( - { - 'host': '127.1.0.1', - 'user': 'root', - 'passwd': 'foo', - 'sudo': False - }, + {"host": "127.1.0.1", "user": "", "passwd": "foo"}, ssh_schemas.RosterEntryConfig.serialize(), - format_checker=jsonschema.FormatChecker() + format_checker=jsonschema.FormatChecker(), ) - except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) - - try: - jsonschema.validate( - { - 'host': '127.1.0.1', - 'user': 'root', - 'priv': 'foo', - 'passwd': 'foo', - 'thin_dir': '/foo/bar' - }, - ssh_schemas.RosterEntryConfig.serialize(), - format_checker=jsonschema.FormatChecker() - ) - except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) - - try: - jsonschema.validate( - { - 'host': '127.1.0.1', - 'user': 'root', - 'passwd': 'foo', - 'minion_opts': { - 'interface': '0.0.0.0' - } - }, - ssh_schemas.RosterEntryConfig.serialize(), - format_checker=jsonschema.FormatChecker() - ) - except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.assertIn("is too short", excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate( { - 'host': '127.1.0.1', - 'user': '', - 'passwd': 'foo', + "host": "127.1.0.1", + "user": "root", + "passwd": "foo", + "minion_opts": {"interface": 0}, }, ssh_schemas.RosterEntryConfig.serialize(), - format_checker=jsonschema.FormatChecker() + format_checker=jsonschema.FormatChecker(), ) - self.assertIn('is too short', excinfo.exception.message) - - with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate( - { - 'host': '127.1.0.1', - 'user': 'root', - 'passwd': 'foo', - 'minion_opts': { - 'interface': 0 - } - }, - ssh_schemas.RosterEntryConfig.serialize(), - format_checker=jsonschema.FormatChecker() - ) - self.assertIn('is not of type', excinfo.exception.message) + self.assertIn("is not of type", excinfo.exception.message) class RosterItemTest(TestCase): - def test_roster_config(self): try: self.assertDictContainsSubset( @@ -267,51 +233,46 @@ class RosterItemTest(TestCase): "patternProperties": { r"^([^:]+)$": ssh_schemas.RosterEntryConfig.serialize() }, - "additionalProperties": False + "additionalProperties": False, }, - ssh_schemas.RosterItem.serialize() + ssh_schemas.RosterItem.serialize(), ) except AssertionError: import salt.utils.json + print(salt.utils.json.dumps(ssh_schemas.RosterItem.serialize(), indent=4)) raise - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_roster_config_validate(self): try: jsonschema.validate( - {'target-1': - { - 'host': 'localhost', - 'user': 'root', - 'passwd': 'foo' - } - }, + {"target-1": {"host": "localhost", "user": "root", "passwd": "foo"}}, ssh_schemas.RosterItem.serialize(), - format_checker=jsonschema.FormatChecker() + format_checker=jsonschema.FormatChecker(), ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate( - {salt.utils.stringutils.to_str('target-1:1'): - { - 'host': 'localhost', - 'user': 'root', - 'passwd': 'foo' + { + salt.utils.stringutils.to_str("target-1:1"): { + "host": "localhost", + "user": "root", + "passwd": "foo", } }, ssh_schemas.RosterItem.serialize(), - format_checker=jsonschema.FormatChecker() + format_checker=jsonschema.FormatChecker(), ) - if JSONSCHEMA_VERSION < _LooseVersion('2.6.0'): + if JSONSCHEMA_VERSION < _LooseVersion("2.6.0"): self.assertIn( - 'Additional properties are not allowed (\'target-1:1\' was unexpected)', - excinfo.exception.message + "Additional properties are not allowed ('target-1:1' was unexpected)", + excinfo.exception.message, ) else: self.assertIn( - '\'target-1:1\' does not match any of the regexes', - excinfo.exception.message + "'target-1:1' does not match any of the regexes", + excinfo.exception.message, ) diff --git a/tests/unit/daemons/test_masterapi.py b/tests/unit/daemons/test_masterapi.py index f9c1abfef0a..c787840f0f0 100644 --- a/tests/unit/daemons/test_masterapi.py +++ b/tests/unit/daemons/test_masterapi.py @@ -2,37 +2,35 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -from functools import wraps -import os + import io +import os import stat +from functools import wraps # Import Salt libs import salt.config import salt.daemons.masterapi as masterapi import salt.utils.platform +from tests.support.mock import MagicMock, patch # Import Salt Testing Libs from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase -from tests.support.mock import ( - patch, - MagicMock, -) -def gen_permissions(owner='', group='', others=''): - ''' +def gen_permissions(owner="", group="", others=""): + """ Helper method to generate file permission bits Usage: gen_permissions('rw', 'r', 'r') - ''' + """ ret = 0 for c in owner: - ret |= getattr(stat, 'S_I{}USR'.format(c.upper()), 0) + ret |= getattr(stat, "S_I{}USR".format(c.upper()), 0) for c in group: - ret |= getattr(stat, 'S_I{}GRP'.format(c.upper()), 0) + ret |= getattr(stat, "S_I{}GRP".format(c.upper()), 0) for c in others: - ret |= getattr(stat, 'S_I{}OTH'.format(c.upper()), 0) + ret |= getattr(stat, "S_I{}OTH".format(c.upper()), 0) return ret @@ -43,142 +41,157 @@ def patch_check_permissions(uid=1, groups=None, is_windows=False, permissive_pki def decorator(func): @wraps(func) def wrapper(self): - self.auto_key.opts['permissive_pki_access'] = permissive_pki + self.auto_key.opts["permissive_pki_access"] = permissive_pki if salt.utils.platform.is_windows(): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=True)): + with patch( + "salt.utils.platform.is_windows", MagicMock(return_value=True) + ): func(self) else: - with patch('os.stat', self.os_stat_mock), \ - patch('os.getuid', MagicMock(return_value=uid)), \ - patch('salt.utils.user.get_gid_list', MagicMock(return_value=groups)), \ - patch('salt.utils.platform.is_windows', MagicMock(return_value=is_windows)): + with patch("os.stat", self.os_stat_mock), patch( + "os.getuid", MagicMock(return_value=uid) + ), patch( + "salt.utils.user.get_gid_list", MagicMock(return_value=groups) + ), patch( + "salt.utils.platform.is_windows", MagicMock(return_value=is_windows) + ): func(self) + return wrapper + return decorator class AutoKeyTest(TestCase): - ''' + """ Tests for the salt.daemons.masterapi.AutoKey class - ''' + """ def setUp(self): opts = salt.config.master_config(None) - opts['user'] = 'test_user' + opts["user"] = "test_user" self.auto_key = masterapi.AutoKey(opts) self.stats = {} def os_stat_mock(self, filename): fmode = MagicMock() fstats = self.stats.get(filename, {}) - fmode.st_mode = fstats.get('mode', 0) - fmode.st_gid = fstats.get('gid', 0) + fmode.st_mode = fstats.get("mode", 0) + fmode.st_gid = fstats.get("gid", 0) return fmode @patch_check_permissions(uid=0, is_windows=True) def test_check_permissions_windows(self): - ''' + """ Assert that all files are accepted on windows - ''' - self.stats['testfile'] = {'mode': gen_permissions('rwx', 'rwx', 'rwx'), 'gid': 2} - self.assertTrue(self.auto_key.check_permissions('testfile')) + """ + self.stats["testfile"] = { + "mode": gen_permissions("rwx", "rwx", "rwx"), + "gid": 2, + } + self.assertTrue(self.auto_key.check_permissions("testfile")) @patch_check_permissions(permissive_pki=True) def test_check_permissions_others_can_write(self): - ''' + """ Assert that no file is accepted, when others can write to it - ''' - self.stats['testfile'] = {'mode': gen_permissions('', '', 'w'), 'gid': 1} + """ + self.stats["testfile"] = {"mode": gen_permissions("", "", "w"), "gid": 1} if salt.utils.platform.is_windows(): - self.assertTrue(self.auto_key.check_permissions('testfile')) + self.assertTrue(self.auto_key.check_permissions("testfile")) else: - self.assertFalse(self.auto_key.check_permissions('testfile')) + self.assertFalse(self.auto_key.check_permissions("testfile")) @patch_check_permissions() def test_check_permissions_group_can_write_not_permissive(self): - ''' + """ Assert that a file is accepted, when group can write to it and perkissive_pki_access=False - ''' - self.stats['testfile'] = {'mode': gen_permissions('w', 'w', ''), 'gid': 1} + """ + self.stats["testfile"] = {"mode": gen_permissions("w", "w", ""), "gid": 1} if salt.utils.platform.is_windows(): - self.assertTrue(self.auto_key.check_permissions('testfile')) + self.assertTrue(self.auto_key.check_permissions("testfile")) else: - self.assertFalse(self.auto_key.check_permissions('testfile')) + self.assertFalse(self.auto_key.check_permissions("testfile")) @patch_check_permissions(permissive_pki=True) def test_check_permissions_group_can_write_permissive(self): - ''' + """ Assert that a file is accepted, when group can write to it and perkissive_pki_access=True - ''' - self.stats['testfile'] = {'mode': gen_permissions('w', 'w', ''), 'gid': 1} - self.assertTrue(self.auto_key.check_permissions('testfile')) + """ + self.stats["testfile"] = {"mode": gen_permissions("w", "w", ""), "gid": 1} + self.assertTrue(self.auto_key.check_permissions("testfile")) @patch_check_permissions(uid=0, permissive_pki=True) def test_check_permissions_group_can_write_permissive_root_in_group(self): - ''' + """ Assert that a file is accepted, when group can write to it, perkissive_pki_access=False, salt is root and in the file owning group - ''' - self.stats['testfile'] = {'mode': gen_permissions('w', 'w', ''), 'gid': 0} - self.assertTrue(self.auto_key.check_permissions('testfile')) + """ + self.stats["testfile"] = {"mode": gen_permissions("w", "w", ""), "gid": 0} + self.assertTrue(self.auto_key.check_permissions("testfile")) @patch_check_permissions(uid=0, permissive_pki=True) def test_check_permissions_group_can_write_permissive_root_not_in_group(self): - ''' + """ Assert that no file is accepted, when group can write to it, perkissive_pki_access=False, salt is root and **not** in the file owning group - ''' - self.stats['testfile'] = {'mode': gen_permissions('w', 'w', ''), 'gid': 1} + """ + self.stats["testfile"] = {"mode": gen_permissions("w", "w", ""), "gid": 1} if salt.utils.platform.is_windows(): - self.assertTrue(self.auto_key.check_permissions('testfile')) + self.assertTrue(self.auto_key.check_permissions("testfile")) else: - self.assertFalse(self.auto_key.check_permissions('testfile')) + self.assertFalse(self.auto_key.check_permissions("testfile")) @patch_check_permissions() def test_check_permissions_only_owner_can_write(self): - ''' + """ Assert that a file is accepted, when only the owner can write to it - ''' - self.stats['testfile'] = {'mode': gen_permissions('w', '', ''), 'gid': 1} - self.assertTrue(self.auto_key.check_permissions('testfile')) + """ + self.stats["testfile"] = {"mode": gen_permissions("w", "", ""), "gid": 1} + self.assertTrue(self.auto_key.check_permissions("testfile")) @patch_check_permissions(uid=0) def test_check_permissions_only_owner_can_write_root(self): - ''' + """ Assert that a file is accepted, when only the owner can write to it and salt is root - ''' - self.stats['testfile'] = {'mode': gen_permissions('w', '', ''), 'gid': 0} - self.assertTrue(self.auto_key.check_permissions('testfile')) + """ + self.stats["testfile"] = {"mode": gen_permissions("w", "", ""), "gid": 0} + self.assertTrue(self.auto_key.check_permissions("testfile")) - def _test_check_autosign_grains(self, - test_func, - file_content='test_value', - file_name='test_grain', - autosign_grains_dir='test_dir', - permissions_ret=True): - ''' + def _test_check_autosign_grains( + self, + test_func, + file_content="test_value", + file_name="test_grain", + autosign_grains_dir="test_dir", + permissions_ret=True, + ): + """ Helper function for testing autosign_grains(). Patches ``os.walk`` to return only ``file_name`` and ``salt.utils.files.fopen`` to open a mock file with ``file_content`` as content. Optionally sets ``opts`` values. Then executes test_func. The ``os.walk`` and ``salt.utils.files.fopen`` mock objects are passed to the function as arguments. - ''' + """ if autosign_grains_dir: - self.auto_key.opts['autosign_grains_dir'] = autosign_grains_dir + self.auto_key.opts["autosign_grains_dir"] = autosign_grains_dir mock_file = io.StringIO(file_content) mock_dirs = [(None, None, [file_name])] - with patch('os.walk', MagicMock(return_value=mock_dirs)) as mock_walk, \ - patch('salt.utils.files.fopen', MagicMock(return_value=mock_file)) as mock_open, \ - patch('salt.daemons.masterapi.AutoKey.check_permissions', - MagicMock(return_value=permissions_ret)) as mock_permissions: + with patch("os.walk", MagicMock(return_value=mock_dirs)) as mock_walk, patch( + "salt.utils.files.fopen", MagicMock(return_value=mock_file) + ) as mock_open, patch( + "salt.daemons.masterapi.AutoKey.check_permissions", + MagicMock(return_value=permissions_ret), + ) as mock_permissions: test_func(mock_walk, mock_open, mock_permissions) def test_check_autosign_grains_no_grains(self): - ''' + """ Asserts that autosigning from grains fails when no grain values are passed. - ''' + """ + def test_func(mock_walk, mock_open, mock_permissions): self.assertFalse(self.auto_key.check_autosign_grains(None)) self.assertEqual(mock_walk.call_count, 0) @@ -193,12 +206,15 @@ class AutoKeyTest(TestCase): self._test_check_autosign_grains(test_func) def test_check_autosign_grains_no_autosign_grains_dir(self): - ''' + """ Asserts that autosigning from grains fails when the \'autosign_grains_dir\' config option is undefined. - ''' + """ + def test_func(mock_walk, mock_open, mock_permissions): - self.assertFalse(self.auto_key.check_autosign_grains({'test_grain': 'test_value'})) + self.assertFalse( + self.auto_key.check_autosign_grains({"test_grain": "test_value"}) + ) self.assertEqual(mock_walk.call_count, 0) self.assertEqual(mock_open.call_count, 0) self.assertEqual(mock_permissions.call_count, 0) @@ -206,128 +222,176 @@ class AutoKeyTest(TestCase): self._test_check_autosign_grains(test_func, autosign_grains_dir=None) def test_check_autosign_grains_accept(self): - ''' + """ Asserts that autosigning from grains passes when a matching grain value is in an autosign_grain file. - ''' - def test_func(*args): - self.assertTrue(self.auto_key.check_autosign_grains({'test_grain': 'test_value'})) + """ - file_content = '#test_ignore\ntest_value' + def test_func(*args): + self.assertTrue( + self.auto_key.check_autosign_grains({"test_grain": "test_value"}) + ) + + file_content = "#test_ignore\ntest_value" self._test_check_autosign_grains(test_func, file_content=file_content) def test_check_autosign_grains_accept_not(self): - ''' + """ Asserts that autosigning from grains fails when the grain value is not in the autosign_grain files. - ''' - def test_func(*args): - self.assertFalse(self.auto_key.check_autosign_grains({'test_grain': 'test_invalid'})) + """ - file_content = '#test_invalid\ntest_value' + def test_func(*args): + self.assertFalse( + self.auto_key.check_autosign_grains({"test_grain": "test_invalid"}) + ) + + file_content = "#test_invalid\ntest_value" self._test_check_autosign_grains(test_func, file_content=file_content) def test_check_autosign_grains_invalid_file_permissions(self): - ''' + """ Asserts that autosigning from grains fails when the grain file has the wrong permissions. - ''' - def test_func(*args): - self.assertFalse(self.auto_key.check_autosign_grains({'test_grain': 'test_value'})) + """ - file_content = '#test_ignore\ntest_value' - self._test_check_autosign_grains(test_func, file_content=file_content, permissions_ret=False) + def test_func(*args): + self.assertFalse( + self.auto_key.check_autosign_grains({"test_grain": "test_value"}) + ) + + file_content = "#test_ignore\ntest_value" + self._test_check_autosign_grains( + test_func, file_content=file_content, permissions_ret=False + ) class LocalFuncsTestCase(TestCase): - ''' + """ TestCase for salt.daemons.masterapi.LocalFuncs class - ''' + """ def setUp(self): opts = salt.config.master_config(None) - self.local_funcs = masterapi.LocalFuncs(opts, 'test-key') + self.local_funcs = masterapi.LocalFuncs(opts, "test-key") # runner tests def test_runner_token_not_authenticated(self): - ''' + """ Asserts that a TokenAuthenticationError is returned when the token can't authenticate. - ''' - mock_ret = {'error': {'name': 'TokenAuthenticationError', - 'message': 'Authentication failure of type "token" occurred.'}} - ret = self.local_funcs.runner({'token': 'asdfasdfasdfasdf'}) + """ + mock_ret = { + "error": { + "name": "TokenAuthenticationError", + "message": 'Authentication failure of type "token" occurred.', + } + } + ret = self.local_funcs.runner({"token": "asdfasdfasdfasdf"}) self.assertDictEqual(mock_ret, ret) def test_runner_token_authorization_error(self): - ''' + """ Asserts that a TokenAuthenticationError is returned when the token authenticates, but is not authorized. - ''' - token = 'asdfasdfasdfasdf' - load = {'token': token, 'fun': 'test.arg', 'kwarg': {}} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'TokenAuthenticationError', - 'message': 'Authentication failure of type "token" occurred ' - 'for user test.'}} + """ + token = "asdfasdfasdfasdf" + load = {"token": token, "fun": "test.arg", "kwarg": {}} + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "TokenAuthenticationError", + "message": 'Authentication failure of type "token" occurred ' + "for user test.", + } + } - with patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + with patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])): ret = self.local_funcs.runner(load) self.assertDictEqual(mock_ret, ret) def test_runner_token_salt_invocation_error(self): - ''' + """ Asserts that a SaltInvocationError is returned when the token authenticates, but the command is malformed. - ''' - token = 'asdfasdfasdfasdf' - load = {'token': token, 'fun': 'badtestarg', 'kwarg': {}} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} + """ + token = "asdfasdfasdfasdf" + load = {"token": token, "fun": "badtestarg", "kwarg": {}} + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } - with patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=['testing'])): + with patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"]) + ): ret = self.local_funcs.runner(load) self.assertDictEqual(mock_ret, ret) def test_runner_eauth_not_authenticated(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user can't authenticate. - ''' - mock_ret = {'error': {'name': 'EauthAuthenticationError', - 'message': 'Authentication failure of type "eauth" occurred for ' - 'user UNKNOWN.'}} - ret = self.local_funcs.runner({'eauth': 'foo'}) + """ + mock_ret = { + "error": { + "name": "EauthAuthenticationError", + "message": 'Authentication failure of type "eauth" occurred for ' + "user UNKNOWN.", + } + } + ret = self.local_funcs.runner({"eauth": "foo"}) self.assertDictEqual(mock_ret, ret) def test_runner_eauth_authorization_error(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user authenticates, but is not authorized. - ''' - load = {'eauth': 'foo', 'username': 'test', 'fun': 'test.arg', 'kwarg': {}} - mock_ret = {'error': {'name': 'EauthAuthenticationError', - 'message': 'Authentication failure of type "eauth" occurred for ' - 'user test.'}} - with patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + """ + load = {"eauth": "foo", "username": "test", "fun": "test.arg", "kwarg": {}} + mock_ret = { + "error": { + "name": "EauthAuthenticationError", + "message": 'Authentication failure of type "eauth" occurred for ' + "user test.", + } + } + with patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])): ret = self.local_funcs.runner(load) self.assertDictEqual(mock_ret, ret) def test_runner_eauth_salt_invocation_error(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user authenticates, but the command is malformed. - ''' - load = {'eauth': 'foo', 'username': 'test', 'fun': 'bad.test.arg.func', 'kwarg': {}} - mock_ret = {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} - with patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=['testing'])): + """ + load = { + "eauth": "foo", + "username": "test", + "fun": "bad.test.arg.func", + "kwarg": {}, + } + mock_ret = { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } + with patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"]) + ): ret = self.local_funcs.runner(load) self.assertDictEqual(mock_ret, ret) @@ -335,221 +399,367 @@ class LocalFuncsTestCase(TestCase): # wheel tests def test_wheel_token_not_authenticated(self): - ''' + """ Asserts that a TokenAuthenticationError is returned when the token can't authenticate. - ''' - mock_ret = {'error': {'name': 'TokenAuthenticationError', - 'message': 'Authentication failure of type "token" occurred.'}} - ret = self.local_funcs.wheel({'token': 'asdfasdfasdfasdf'}) + """ + mock_ret = { + "error": { + "name": "TokenAuthenticationError", + "message": 'Authentication failure of type "token" occurred.', + } + } + ret = self.local_funcs.wheel({"token": "asdfasdfasdfasdf"}) self.assertDictEqual(mock_ret, ret) def test_wheel_token_authorization_error(self): - ''' + """ Asserts that a TokenAuthenticationError is returned when the token authenticates, but is not authorized. - ''' - token = 'asdfasdfasdfasdf' - load = {'token': token, 'fun': 'test.arg', 'kwarg': {}} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'TokenAuthenticationError', - 'message': 'Authentication failure of type "token" occurred ' - 'for user test.'}} + """ + token = "asdfasdfasdfasdf" + load = {"token": token, "fun": "test.arg", "kwarg": {}} + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "TokenAuthenticationError", + "message": 'Authentication failure of type "token" occurred ' + "for user test.", + } + } - with patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + with patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])): ret = self.local_funcs.wheel(load) self.assertDictEqual(mock_ret, ret) def test_wheel_token_salt_invocation_error(self): - ''' + """ Asserts that a SaltInvocationError is returned when the token authenticates, but the command is malformed. - ''' - token = 'asdfasdfasdfasdf' - load = {'token': token, 'fun': 'badtestarg', 'kwarg': {}} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} + """ + token = "asdfasdfasdfasdf" + load = {"token": token, "fun": "badtestarg", "kwarg": {}} + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } - with patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=['testing'])): + with patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"]) + ): ret = self.local_funcs.wheel(load) self.assertDictEqual(mock_ret, ret) def test_wheel_eauth_not_authenticated(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user can't authenticate. - ''' - mock_ret = {'error': {'name': 'EauthAuthenticationError', - 'message': 'Authentication failure of type "eauth" occurred for ' - 'user UNKNOWN.'}} - ret = self.local_funcs.wheel({'eauth': 'foo'}) + """ + mock_ret = { + "error": { + "name": "EauthAuthenticationError", + "message": 'Authentication failure of type "eauth" occurred for ' + "user UNKNOWN.", + } + } + ret = self.local_funcs.wheel({"eauth": "foo"}) self.assertDictEqual(mock_ret, ret) def test_wheel_eauth_authorization_error(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user authenticates, but is not authorized. - ''' - load = {'eauth': 'foo', 'username': 'test', 'fun': 'test.arg', 'kwarg': {}} - mock_ret = {'error': {'name': 'EauthAuthenticationError', - 'message': 'Authentication failure of type "eauth" occurred for ' - 'user test.'}} - with patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + """ + load = {"eauth": "foo", "username": "test", "fun": "test.arg", "kwarg": {}} + mock_ret = { + "error": { + "name": "EauthAuthenticationError", + "message": 'Authentication failure of type "eauth" occurred for ' + "user test.", + } + } + with patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])): ret = self.local_funcs.wheel(load) self.assertDictEqual(mock_ret, ret) def test_wheel_eauth_salt_invocation_error(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user authenticates, but the command is malformed. - ''' - load = {'eauth': 'foo', 'username': 'test', 'fun': 'bad.test.arg.func', 'kwarg': {}} - mock_ret = {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} - with patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=['testing'])): + """ + load = { + "eauth": "foo", + "username": "test", + "fun": "bad.test.arg.func", + "kwarg": {}, + } + mock_ret = { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } + with patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"]) + ): ret = self.local_funcs.wheel(load) self.assertDictEqual(mock_ret, ret) def test_wheel_user_not_authenticated(self): - ''' + """ Asserts that an UserAuthenticationError is returned when the user can't authenticate. - ''' - mock_ret = {'error': {'name': 'UserAuthenticationError', - 'message': 'Authentication failure of type "user" occurred for ' - 'user UNKNOWN.'}} + """ + mock_ret = { + "error": { + "name": "UserAuthenticationError", + "message": 'Authentication failure of type "user" occurred for ' + "user UNKNOWN.", + } + } ret = self.local_funcs.wheel({}) self.assertDictEqual(mock_ret, ret) # publish tests def test_publish_user_is_blacklisted(self): - ''' + """ Asserts that an AuthorizationError is returned when the user has been blacklisted. - ''' - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=True)): - self.assertEqual(mock_ret, self.local_funcs.publish({'user': 'foo', 'fun': 'test.arg'})) + """ + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=True) + ): + self.assertEqual( + mock_ret, self.local_funcs.publish({"user": "foo", "fun": "test.arg"}) + ) def test_publish_cmd_blacklisted(self): - ''' + """ Asserts that an AuthorizationError is returned when the command has been blacklisted. - ''' - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=True)): - self.assertEqual(mock_ret, self.local_funcs.publish({'user': 'foo', 'fun': 'test.arg'})) + """ + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=True) + ): + self.assertEqual( + mock_ret, self.local_funcs.publish({"user": "foo", "fun": "test.arg"}) + ) def test_publish_token_not_authenticated(self): - ''' + """ Asserts that an AuthenticationError is returned when the token can't authenticate. - ''' - load = {'user': 'foo', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'token': 'asdfasdfasdfasdf'}} - mock_ret = {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)): + """ + load = { + "user": "foo", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"token": "asdfasdfasdfasdf"}, + } + mock_ret = { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ): self.assertEqual(mock_ret, self.local_funcs.publish(load)) def test_publish_token_authorization_error(self): - ''' + """ Asserts that an AuthorizationError is returned when the token authenticates, but is not authorized. - ''' - token = 'asdfasdfasdfasdf' - load = {'user': 'foo', 'fun': 'test.arg', 'tgt': 'test_minion', - 'arg': 'bar', 'kwargs': {'token': token}} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} + """ + token = "asdfasdfasdfasdf" + load = { + "user": "foo", + "fun": "test.arg", + "tgt": "test_minion", + "arg": "bar", + "kwargs": {"token": token}, + } + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[]) + ): self.assertEqual(mock_ret, self.local_funcs.publish(load)) def test_publish_eauth_not_authenticated(self): - ''' + """ Asserts that an AuthenticationError is returned when the user can't authenticate. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'eauth': 'foo'}} - mock_ret = {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)): + """ + load = { + "user": "test", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"eauth": "foo"}, + } + mock_ret = { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ): self.assertEqual(mock_ret, self.local_funcs.publish(load)) def test_publish_eauth_authorization_error(self): - ''' + """ Asserts that an AuthorizationError is returned when the user authenticates, but is not authorized. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'eauth': 'foo'}, 'arg': 'bar'} - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + """ + load = { + "user": "test", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"eauth": "foo"}, + "arg": "bar", + } + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[]) + ): self.assertEqual(mock_ret, self.local_funcs.publish(load)) def test_publish_user_not_authenticated(self): - ''' + """ Asserts that an AuthenticationError is returned when the user can't authenticate. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion'} - mock_ret = {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)): + """ + load = {"user": "test", "fun": "test.arg", "tgt": "test_minion"} + mock_ret = { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ): self.assertEqual(mock_ret, self.local_funcs.publish(load)) def test_publish_user_authenticated_missing_auth_list(self): - ''' + """ Asserts that an AuthenticationError is returned when the user has an effective user id and is authenticated, but the auth_list is empty. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'user': 'test'}, 'arg': 'foo'} - mock_ret = {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.auth.LoadAuth.authenticate_key', MagicMock(return_value='fake-user-key')), \ - patch('salt.utils.master.get_values_of_matching_keys', MagicMock(return_value=[])): + """ + load = { + "user": "test", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"user": "test"}, + "arg": "foo", + } + mock_ret = { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.auth.LoadAuth.authenticate_key", + MagicMock(return_value="fake-user-key"), + ), patch( + "salt.utils.master.get_values_of_matching_keys", MagicMock(return_value=[]) + ): self.assertEqual(mock_ret, self.local_funcs.publish(load)) def test_publish_user_authorization_error(self): - ''' + """ Asserts that an AuthorizationError is returned when the user authenticates, but is not authorized. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'user': 'test'}, 'arg': 'foo'} - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.auth.LoadAuth.authenticate_key', MagicMock(return_value='fake-user-key')), \ - patch('salt.utils.master.get_values_of_matching_keys', MagicMock(return_value=['test'])), \ - patch('salt.utils.minions.CkMinions.auth_check', MagicMock(return_value=False)): + """ + load = { + "user": "test", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"user": "test"}, + "arg": "foo", + } + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.auth.LoadAuth.authenticate_key", + MagicMock(return_value="fake-user-key"), + ), patch( + "salt.utils.master.get_values_of_matching_keys", + MagicMock(return_value=["test"]), + ), patch( + "salt.utils.minions.CkMinions.auth_check", MagicMock(return_value=False) + ): self.assertEqual(mock_ret, self.local_funcs.publish(load)) class FakeCache(object): - def __init__(self): self.data = {} @@ -561,50 +771,53 @@ class FakeCache(object): class RemoteFuncsTestCase(TestCase): - ''' + """ TestCase for salt.daemons.masterapi.RemoteFuncs class - ''' + """ def setUp(self): - opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master')) + opts = salt.config.master_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master") + ) self.funcs = masterapi.RemoteFuncs(opts) self.funcs.cache = FakeCache() - def test_mine_get(self, tgt_type_key='tgt_type'): - ''' + def test_mine_get(self, tgt_type_key="tgt_type"): + """ Asserts that ``mine_get`` gives the expected results. Actually this only tests that: - the correct check minions method is called - the correct cache key is subsequently used - ''' - self.funcs.cache.store('minions/webserver', 'mine', - dict(ip_addr='2001:db8::1:3')) - with patch('salt.utils.minions.CkMinions._check_compound_minions', - MagicMock(return_value=(dict( - minions=['webserver'], - missing=[])))): + """ + self.funcs.cache.store( + "minions/webserver", "mine", dict(ip_addr="2001:db8::1:3") + ) + with patch( + "salt.utils.minions.CkMinions._check_compound_minions", + MagicMock(return_value=(dict(minions=["webserver"], missing=[]))), + ): ret = self.funcs._mine_get( { - 'id': 'requester_minion', - 'tgt': 'G@roles:web', - 'fun': 'ip_addr', - tgt_type_key: 'compound', + "id": "requester_minion", + "tgt": "G@roles:web", + "fun": "ip_addr", + tgt_type_key: "compound", } ) - self.assertDictEqual(ret, dict(webserver='2001:db8::1:3')) + self.assertDictEqual(ret, dict(webserver="2001:db8::1:3")) def test_mine_get_pre_nitrogen_compat(self): - ''' + """ Asserts that pre-Nitrogen API key ``expr_form`` is still accepted. This is what minions before Nitrogen would issue. - ''' - self.test_mine_get(tgt_type_key='expr_form') + """ + self.test_mine_get(tgt_type_key="expr_form") - def test_mine_get_dict_str(self, tgt_type_key='tgt_type'): - ''' + def test_mine_get_dict_str(self, tgt_type_key="tgt_type"): + """ Asserts that ``mine_get`` gives the expected results when request is a comma-separated list. @@ -612,25 +825,34 @@ class RemoteFuncsTestCase(TestCase): - the correct check minions method is called - the correct cache key is subsequently used - ''' - self.funcs.cache.store('minions/webserver', 'mine', - dict(ip_addr='2001:db8::1:3', ip4_addr='127.0.0.1')) - with patch('salt.utils.minions.CkMinions._check_compound_minions', - MagicMock(return_value=(dict( - minions=['webserver'], - missing=[])))): + """ + self.funcs.cache.store( + "minions/webserver", + "mine", + dict(ip_addr="2001:db8::1:3", ip4_addr="127.0.0.1"), + ) + with patch( + "salt.utils.minions.CkMinions._check_compound_minions", + MagicMock(return_value=(dict(minions=["webserver"], missing=[]))), + ): ret = self.funcs._mine_get( { - 'id': 'requester_minion', - 'tgt': 'G@roles:web', - 'fun': 'ip_addr,ip4_addr', - tgt_type_key: 'compound', + "id": "requester_minion", + "tgt": "G@roles:web", + "fun": "ip_addr,ip4_addr", + tgt_type_key: "compound", } ) - self.assertDictEqual(ret, dict(ip_addr=dict(webserver='2001:db8::1:3'), ip4_addr=dict(webserver='127.0.0.1'))) + self.assertDictEqual( + ret, + dict( + ip_addr=dict(webserver="2001:db8::1:3"), + ip4_addr=dict(webserver="127.0.0.1"), + ), + ) - def test_mine_get_dict_list(self, tgt_type_key='tgt_type'): - ''' + def test_mine_get_dict_list(self, tgt_type_key="tgt_type"): + """ Asserts that ``mine_get`` gives the expected results when request is a list. @@ -638,98 +860,103 @@ class RemoteFuncsTestCase(TestCase): - the correct check minions method is called - the correct cache key is subsequently used - ''' - self.funcs.cache.store('minions/webserver', 'mine', - dict(ip_addr='2001:db8::1:3', ip4_addr='127.0.0.1')) - with patch('salt.utils.minions.CkMinions._check_compound_minions', - MagicMock(return_value=(dict( - minions=['webserver'], - missing=[])))): - ret = self.funcs._mine_get( - { - 'id': 'requester_minion', - 'tgt': 'G@roles:web', - 'fun': ['ip_addr', 'ip4_addr'], - tgt_type_key: 'compound', - } - ) - self.assertDictEqual(ret, dict(ip_addr=dict(webserver='2001:db8::1:3'), ip4_addr=dict(webserver='127.0.0.1'))) - - def test_mine_get_acl_allowed(self): - ''' - Asserts that ``mine_get`` gives the expected results when this is allowed - in the client-side ACL that was stored in the mine data. - ''' + """ self.funcs.cache.store( - 'minions/webserver', - 'mine', - { - 'ip_addr': { - salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:4', - salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION, - 'allow_tgt': 'requester_minion', - 'allow_tgt_type': 'glob', - }, - } + "minions/webserver", + "mine", + dict(ip_addr="2001:db8::1:3", ip4_addr="127.0.0.1"), ) - # The glob check is for the resolution of the allow_tgt - # The compound check is for the resolution of the tgt in the mine_get request. - with \ - patch('salt.utils.minions.CkMinions._check_glob_minions', - MagicMock(return_value={'minions': ['requester_minion'], 'missing': []}) - ), \ - patch('salt.utils.minions.CkMinions._check_compound_minions', - MagicMock(return_value={'minions': ['webserver'], 'missing': []}) - ): + with patch( + "salt.utils.minions.CkMinions._check_compound_minions", + MagicMock(return_value=(dict(minions=["webserver"], missing=[]))), + ): ret = self.funcs._mine_get( { - 'id': 'requester_minion', - 'tgt': 'anything', - 'tgt_type': 'compound', - 'fun': ['ip_addr'], + "id": "requester_minion", + "tgt": "G@roles:web", + "fun": ["ip_addr", "ip4_addr"], + tgt_type_key: "compound", } ) self.assertDictEqual( ret, - {'ip_addr': {'webserver': '2001:db8::1:4'}} + dict( + ip_addr=dict(webserver="2001:db8::1:3"), + ip4_addr=dict(webserver="127.0.0.1"), + ), ) + def test_mine_get_acl_allowed(self): + """ + Asserts that ``mine_get`` gives the expected results when this is allowed + in the client-side ACL that was stored in the mine data. + """ + self.funcs.cache.store( + "minions/webserver", + "mine", + { + "ip_addr": { + salt.utils.mine.MINE_ITEM_ACL_DATA: "2001:db8::1:4", + salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION, + "allow_tgt": "requester_minion", + "allow_tgt_type": "glob", + }, + }, + ) + # The glob check is for the resolution of the allow_tgt + # The compound check is for the resolution of the tgt in the mine_get request. + with patch( + "salt.utils.minions.CkMinions._check_glob_minions", + MagicMock(return_value={"minions": ["requester_minion"], "missing": []}), + ), patch( + "salt.utils.minions.CkMinions._check_compound_minions", + MagicMock(return_value={"minions": ["webserver"], "missing": []}), + ): + ret = self.funcs._mine_get( + { + "id": "requester_minion", + "tgt": "anything", + "tgt_type": "compound", + "fun": ["ip_addr"], + } + ) + self.assertDictEqual(ret, {"ip_addr": {"webserver": "2001:db8::1:4"}}) + def test_mine_get_acl_rejected(self): - ''' + """ Asserts that ``mine_get`` gives the expected results when this is rejected in the client-side ACL that was stored in the mine data. This results in no data being sent back (just as if the entry wouldn't exist). - ''' + """ self.funcs.cache.store( - 'minions/webserver', - 'mine', + "minions/webserver", + "mine", { - 'ip_addr': { - salt.utils.mine.MINE_ITEM_ACL_DATA: '2001:db8::1:4', + "ip_addr": { + salt.utils.mine.MINE_ITEM_ACL_DATA: "2001:db8::1:4", salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION, - 'allow_tgt': 'not_requester_minion', - 'allow_tgt_type': 'glob', + "allow_tgt": "not_requester_minion", + "allow_tgt_type": "glob", } - } + }, ) # The glob check is for the resolution of the allow_tgt # The compound check is for the resolution of the tgt in the mine_get request. - with \ - patch('salt.utils.minions.CkMinions._check_glob_minions', - MagicMock(return_value={'minions': ['not_requester_minion'], 'missing': []}) - ), \ - patch('salt.utils.minions.CkMinions._check_compound_minions', - MagicMock(return_value={'minions': ['webserver'], 'missing': []}) - ): + with patch( + "salt.utils.minions.CkMinions._check_glob_minions", + MagicMock( + return_value={"minions": ["not_requester_minion"], "missing": []} + ), + ), patch( + "salt.utils.minions.CkMinions._check_compound_minions", + MagicMock(return_value={"minions": ["webserver"], "missing": []}), + ): ret = self.funcs._mine_get( { - 'id': 'requester_minion', - 'tgt': 'anything', - 'tgt_type': 'compound', - 'fun': ['ip_addr'], + "id": "requester_minion", + "tgt": "anything", + "tgt_type": "compound", + "fun": ["ip_addr"], } ) - self.assertDictEqual( - ret, - {} - ) + self.assertDictEqual(ret, {}) diff --git a/tests/unit/engines/test_libvirt_events.py b/tests/unit/engines/test_libvirt_events.py index d9143a320b1..5600b35e0f1 100644 --- a/tests/unit/engines/test_libvirt_events.py +++ b/tests/unit/engines/test_libvirt_events.py @@ -1,87 +1,90 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the libvirt_events engine -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.engines.libvirt_events as libvirt_events +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase # pylint: disable=protected-access,attribute-defined-outside-init,invalid-name,unused-argument,no-self-use class EngineLibvirtEventTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.engine.libvirt_events - ''' + """ def setup_loader_modules(self): - patcher = patch('salt.engines.libvirt_events.libvirt') + patcher = patch("salt.engines.libvirt_events.libvirt") self.mock_libvirt = patcher.start() self.mock_libvirt.getVersion.return_value = 2000000 - self.mock_libvirt.virEventRunDefaultImpl.return_value = -1 # Don't loop for ever + self.mock_libvirt.virEventRunDefaultImpl.return_value = ( + -1 + ) # Don't loop for ever self.mock_libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0 self.mock_libvirt.VIR_DOMAIN_EVENT_ID_REBOOT = 1 self.addCleanup(patcher.stop) - self.addCleanup(delattr, self, 'mock_libvirt') + self.addCleanup(delattr, self, "mock_libvirt") return {libvirt_events: {}} - @patch('salt.engines.libvirt_events.libvirt', - VIR_PREFIX_NONE=0, - VIR_PREFIX_ONE=1, - VIR_PREFIX_TWO=2, - VIR_PREFIX_SUB_FOO=0, - VIR_PREFIX_SUB_BAR=1, - VIR_PREFIX_SUB_FOOBAR=2) + @patch( + "salt.engines.libvirt_events.libvirt", + VIR_PREFIX_NONE=0, + VIR_PREFIX_ONE=1, + VIR_PREFIX_TWO=2, + VIR_PREFIX_SUB_FOO=0, + VIR_PREFIX_SUB_BAR=1, + VIR_PREFIX_SUB_FOOBAR=2, + ) def test_get_libvirt_enum_string_subprefix(self, libvirt_mock): - ''' + """ Make sure the libvirt enum value to string works reliably with elements with a sub prefix, eg VIR_PREFIX_SUB_* in this case. - ''' + """ # Test case with a sub prefix - assert libvirt_events._get_libvirt_enum_string('VIR_PREFIX_', 2) == 'two' + assert libvirt_events._get_libvirt_enum_string("VIR_PREFIX_", 2) == "two" - @patch('salt.engines.libvirt_events.libvirt', - VIR_PREFIX_FOO=0, - VIR_PREFIX_BAR_FOO=1) + @patch( + "salt.engines.libvirt_events.libvirt", VIR_PREFIX_FOO=0, VIR_PREFIX_BAR_FOO=1 + ) def test_get_libvirt_enum_string_underscores(self, libvirt_mock): - ''' + """ Make sure the libvirt enum value to string works reliably and items with an underscore aren't confused with sub prefixes. - ''' - assert libvirt_events._get_libvirt_enum_string('VIR_PREFIX_', 1) == 'bar foo' + """ + assert libvirt_events._get_libvirt_enum_string("VIR_PREFIX_", 1) == "bar foo" - @patch('salt.engines.libvirt_events.libvirt', - VIR_DOMAIN_EVENT_CRASHED_PANICKED=0, - VIR_DOMAIN_EVENT_DEFINED=0, - VIR_DOMAIN_EVENT_UNDEFINED=1, - VIR_DOMAIN_EVENT_CRASHED=2, - VIR_DOMAIN_EVENT_DEFINED_ADDED=0, - VIR_DOMAIN_EVENT_DEFINED_UPDATED=1) + @patch( + "salt.engines.libvirt_events.libvirt", + VIR_DOMAIN_EVENT_CRASHED_PANICKED=0, + VIR_DOMAIN_EVENT_DEFINED=0, + VIR_DOMAIN_EVENT_UNDEFINED=1, + VIR_DOMAIN_EVENT_CRASHED=2, + VIR_DOMAIN_EVENT_DEFINED_ADDED=0, + VIR_DOMAIN_EVENT_DEFINED_UPDATED=1, + ) def test_get_domain_event_detail(self, mock_libvirt): - ''' + """ Test get_domain_event_detail function - ''' - assert libvirt_events._get_domain_event_detail(1, 2) == ('undefined', 'unknown') - assert libvirt_events._get_domain_event_detail(0, 1) == ('defined', 'updated') - assert libvirt_events._get_domain_event_detail(4, 2) == ('unknown', 'unknown') + """ + assert libvirt_events._get_domain_event_detail(1, 2) == ("undefined", "unknown") + assert libvirt_events._get_domain_event_detail(0, 1) == ("defined", "updated") + assert libvirt_events._get_domain_event_detail(4, 2) == ("unknown", "unknown") - @patch('salt.engines.libvirt_events.libvirt', VIR_NETWORK_EVENT_ID_LIFECYCLE=1000) + @patch("salt.engines.libvirt_events.libvirt", VIR_NETWORK_EVENT_ID_LIFECYCLE=1000) def test_event_register(self, mock_libvirt): - ''' + """ Test that the libvirt_events engine actually registers events catch them and cleans before leaving the place. - ''' + """ mock_cnx = MagicMock() mock_libvirt.openReadOnly.return_value = mock_cnx @@ -90,71 +93,88 @@ class EngineLibvirtEventTestCase(TestCase, LoaderModuleMockMixin): mock_cnx.networkEventRegisterAny.return_value = 10000 - libvirt_events.start('test:///', 'test/prefix') + libvirt_events.start("test:///", "test/prefix") # Check that the connection has been opened - mock_libvirt.openReadOnly.assert_called_once_with('test:///') + mock_libvirt.openReadOnly.assert_called_once_with("test:///") # Check that the connection has been closed mock_cnx.close.assert_called_once() # Check events registration and deregistration mock_cnx.domainEventRegisterAny.assert_any_call( - None, mock_libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, - libvirt_events._domain_event_lifecycle_cb, - {'prefix': 'test/prefix', 'object': 'domain', 'event': 'lifecycle'}) + None, + mock_libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, + libvirt_events._domain_event_lifecycle_cb, + {"prefix": "test/prefix", "object": "domain", "event": "lifecycle"}, + ) mock_cnx.networkEventRegisterAny.assert_any_call( - None, mock_libvirt.VIR_NETWORK_EVENT_ID_LIFECYCLE, - libvirt_events._network_event_lifecycle_cb, - {'prefix': 'test/prefix', 'object': 'network', 'event': 'lifecycle'}) + None, + mock_libvirt.VIR_NETWORK_EVENT_ID_LIFECYCLE, + libvirt_events._network_event_lifecycle_cb, + {"prefix": "test/prefix", "object": "network", "event": "lifecycle"}, + ) # Check that the deregister events are called with the result of register mock_cnx.networkEventDeregisterAny.assert_called_with( - mock_cnx.networkEventRegisterAny.return_value) + mock_cnx.networkEventRegisterAny.return_value + ) # Check that the default 'all' filter actually worked - counts = {obj: len(callback_def) for obj, callback_def in libvirt_events.CALLBACK_DEFS.items()} + counts = { + obj: len(callback_def) + for obj, callback_def in libvirt_events.CALLBACK_DEFS.items() + } for obj, count in counts.items(): register = libvirt_events.REGISTER_FUNCTIONS[obj] assert getattr(mock_cnx, register).call_count == count def test_event_skipped(self): - ''' + """ Test that events are skipped if their ID isn't defined in the libvirt module (older libvirt) - ''' - self.mock_libvirt.mock_add_spec([ - 'openReadOnly', - 'virEventRegisterDefaultImpl', - 'virEventRunDefaultImpl', - 'VIR_DOMAIN_EVENT_ID_LIFECYCLE'], spec_set=True) + """ + self.mock_libvirt.mock_add_spec( + [ + "openReadOnly", + "virEventRegisterDefaultImpl", + "virEventRunDefaultImpl", + "VIR_DOMAIN_EVENT_ID_LIFECYCLE", + ], + spec_set=True, + ) - libvirt_events.start('test:///', 'test/prefix') + libvirt_events.start("test:///", "test/prefix") # Check events registration and deregistration mock_cnx = self.mock_libvirt.openReadOnly.return_value mock_cnx.domainEventRegisterAny.assert_any_call( - None, self.mock_libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, - libvirt_events._domain_event_lifecycle_cb, - {'prefix': 'test/prefix', 'object': 'domain', 'event': 'lifecycle'}) + None, + self.mock_libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE, + libvirt_events._domain_event_lifecycle_cb, + {"prefix": "test/prefix", "object": "domain", "event": "lifecycle"}, + ) # Network events should have been skipped mock_cnx.networkEventRegisterAny.assert_not_called() def test_event_filtered(self): - ''' + """ Test that events are skipped if their ID isn't defined in the libvirt module (older libvirt) - ''' - libvirt_events.start('test', 'test/prefix', 'domain/lifecycle') + """ + libvirt_events.start("test", "test/prefix", "domain/lifecycle") # Check events registration and deregistration mock_cnx = self.mock_libvirt.openReadOnly.return_value mock_cnx.domainEventRegisterAny.assert_any_call( - None, 0, libvirt_events._domain_event_lifecycle_cb, - {'prefix': 'test/prefix', 'object': 'domain', 'event': 'lifecycle'}) + None, + 0, + libvirt_events._domain_event_lifecycle_cb, + {"prefix": "test/prefix", "object": "domain", "event": "lifecycle"}, + ) # Network events should have been filtered out mock_cnx.networkEventRegisterAny.assert_not_called() diff --git a/tests/unit/engines/test_script.py b/tests/unit/engines/test_script.py index 1b1ffedd5ee..e552b7afe09 100644 --- a/tests/unit/engines/test_script.py +++ b/tests/unit/engines/test_script.py @@ -1,50 +1,46 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the script engine -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch - # Import Salt Libs import salt.config import salt.engines.script as script from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class EngineScriptTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.engine.script - ''' + """ def setup_loader_modules(self): opts = salt.config.DEFAULT_MASTER_OPTS - return { - script: { - '__opts__': opts - } - } + return {script: {"__opts__": opts}} def test__get_serializer(self): - ''' + """ Test known serializer is returned or exception is raised if unknown serializer - ''' - for serializers in ('json', 'yaml', 'msgpack'): + """ + for serializers in ("json", "yaml", "msgpack"): self.assertTrue(script._get_serializer(serializers)) with self.assertRaises(CommandExecutionError): - script._get_serializer('bad') + script._get_serializer("bad") def test__read_stdout(self): - ''' + """ Test we can yield stdout - ''' - with patch('subprocess.Popen') as popen_mock: - popen_mock.stdout.readline.return_value = 'test' - self.assertEqual(next(script._read_stdout(popen_mock)), 'test') + """ + with patch("subprocess.Popen") as popen_mock: + popen_mock.stdout.readline.return_value = "test" + self.assertEqual(next(script._read_stdout(popen_mock)), "test") diff --git a/tests/unit/engines/test_slack.py b/tests/unit/engines/test_slack.py index 730fc493db7..64cff5d4fb4 100644 --- a/tests/unit/engines/test_slack.py +++ b/tests/unit/engines/test_slack.py @@ -1,96 +1,100 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the slack engine -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch) +import salt.config # Import Salt Libs import salt.engines.slack as slack -import salt.config + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -@skipIf(slack.HAS_SLACKCLIENT is False, 'The SlackClient is not installed') +@skipIf(slack.HAS_SLACKCLIENT is False, "The SlackClient is not installed") class EngineSlackTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.engine.slack - ''' + """ def setup_loader_modules(self): return {slack: {}} def setUp(self): mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() - token = 'xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx' + token = "xoxb-xxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx" with patch.dict(slack.__opts__, mock_opts): - with patch('slackclient.SlackClient.rtm_connect', MagicMock(return_value=True)): + with patch( + "slackclient.SlackClient.rtm_connect", MagicMock(return_value=True) + ): self.client = slack.SlackClient(token) def test_control_message_target(self): - ''' + """ Test slack engine: control_message_target - ''' - trigger_string = '!' + """ + trigger_string = "!" - loaded_groups = {'default': {'targets': {}, - 'commands': set(['cmd.run', 'test.ping']), - 'default_target': {'tgt_type': 'glob', 'target': '*'}, - 'users': set(['gareth']), - 'aliases': {'whoami': {'cmd': 'cmd.run whoami'}, - 'list_pillar': {'cmd': 'pillar.items'}} - }} + loaded_groups = { + "default": { + "targets": {}, + "commands": set(["cmd.run", "test.ping"]), + "default_target": {"tgt_type": "glob", "target": "*"}, + "users": set(["gareth"]), + "aliases": { + "whoami": {"cmd": "cmd.run whoami"}, + "list_pillar": {"cmd": "pillar.items"}, + }, + } + } - slack_user_name = 'gareth' + slack_user_name = "gareth" # Check for correct cmdline - _expected = (True, - {'tgt_type': 'glob', 'target': '*'}, - ['cmd.run', 'whoami']) - text = '!cmd.run whoami' - target_commandline = self.client.control_message_target(slack_user_name, - text, - loaded_groups, - trigger_string) + _expected = (True, {"tgt_type": "glob", "target": "*"}, ["cmd.run", "whoami"]) + text = "!cmd.run whoami" + target_commandline = self.client.control_message_target( + slack_user_name, text, loaded_groups, trigger_string + ) self.assertEqual(target_commandline, _expected) # Check aliases result in correct cmdline - text = '!whoami' - target_commandline = self.client.control_message_target(slack_user_name, - text, - loaded_groups, - trigger_string) + text = "!whoami" + target_commandline = self.client.control_message_target( + slack_user_name, text, loaded_groups, trigger_string + ) self.assertEqual(target_commandline, _expected) # Check pillar is overrided - _expected = (True, - {'tgt_type': 'glob', 'target': '*'}, - ['pillar.items', 'pillar={"hello": "world"}']) + _expected = ( + True, + {"tgt_type": "glob", "target": "*"}, + ["pillar.items", 'pillar={"hello": "world"}'], + ) text = r"""!list_pillar pillar='{"hello": "world"}'""" - target_commandline = self.client.control_message_target(slack_user_name, - text, - loaded_groups, - trigger_string) + target_commandline = self.client.control_message_target( + slack_user_name, text, loaded_groups, trigger_string + ) self.assertEqual(target_commandline, _expected) # Check target is overrided - _expected = (True, - {'tgt_type': 'glob', 'target': 'localhost'}, - ['cmd.run', 'whoami']) + _expected = ( + True, + {"tgt_type": "glob", "target": "localhost"}, + ["cmd.run", "whoami"], + ) text = "!cmd.run whoami target='localhost'" - target_commandline = self.client.control_message_target(slack_user_name, - text, - loaded_groups, - trigger_string) + target_commandline = self.client.control_message_target( + slack_user_name, text, loaded_groups, trigger_string + ) self.assertEqual(target_commandline, _expected) diff --git a/tests/unit/engines/test_sqs_events.py b/tests/unit/engines/test_sqs_events.py index 196300cc820..cf4d5fc3e17 100644 --- a/tests/unit/engines/test_sqs_events.py +++ b/tests/unit/engines/test_sqs_events.py @@ -1,32 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the sqs_events engine -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.engines.sqs_events as sqs_events +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -@skipIf(sqs_events.HAS_BOTO is False, 'The boto library is not installed') + +@skipIf(sqs_events.HAS_BOTO is False, "The boto library is not installed") class EngineSqsEventTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.engine.sqs_events - ''' + """ def setup_loader_modules(self): - patcher = patch('salt.engines.sqs_events.boto.sqs') + patcher = patch("salt.engines.sqs_events.boto.sqs") self.mock_sqs = patcher.start() self.addCleanup(patcher.stop) - self.addCleanup(delattr, self, 'mock_sqs') + self.addCleanup(delattr, self, "mock_sqs") return {sqs_events: {}} def sample_msg(self): @@ -37,41 +35,41 @@ class EngineSqsEventTestCase(TestCase, LoaderModuleMockMixin): # 'present' function tests: 1 def test_no_queue_present(self): - ''' + """ Test to ensure the SQS engine logs a warning when queue not present - ''' - with patch('salt.engines.sqs_events.log') as mock_logging: - with patch('time.sleep', return_value=None) as mock_sleep: + """ + with patch("salt.engines.sqs_events.log") as mock_logging: + with patch("time.sleep", return_value=None) as mock_sleep: q = None - q_name = 'mysqs' + q_name = "mysqs" mock_fire = MagicMock(return_value=True) sqs_events._process_queue(q, q_name, mock_fire) self.assertTrue(mock_logging.warning.called) self.assertFalse(self.mock_sqs.queue.Queue().get_messages.called) def test_minion_message_fires(self): - ''' + """ Test SQS engine correctly gets and fires messages on minion - ''' + """ msgs = [self.sample_msg(), self.sample_msg()] self.mock_sqs.queue.Queue().get_messages.return_value = msgs q = self.mock_sqs.queue.Queue() - q_name = 'mysqs' + q_name = "mysqs" mock_event = MagicMock(return_value=True) mock_fire = MagicMock(return_value=True) - with patch.dict(sqs_events.__salt__, {'event.send': mock_event}): + with patch.dict(sqs_events.__salt__, {"event.send": mock_event}): sqs_events._process_queue(q, q_name, mock_fire) self.assertTrue(self.mock_sqs.queue.Queue().get_messages.called) self.assertTrue(all(x.delete.called for x in msgs)) def test_master_message_fires(self): - ''' + """ Test SQS engine correctly gets and fires messages on master - ''' + """ msgs = [self.sample_msg(), self.sample_msg()] self.mock_sqs.queue.Queue().get_messages.return_value = msgs q = self.mock_sqs.queue.Queue() - q_name = 'mysqs' + q_name = "mysqs" mock_fire = MagicMock(return_value=True) sqs_events._process_queue(q, q_name, mock_fire) self.assertTrue(self.mock_sqs.queue.Queue().get_messages.called, len(msgs)) diff --git a/tests/unit/executors/test_splay.py b/tests/unit/executors/test_splay.py index c057b574ec8..be12f4811c9 100644 --- a/tests/unit/executors/test_splay.py +++ b/tests/unit/executors/test_splay.py @@ -2,21 +2,16 @@ from __future__ import absolute_import, print_function, unicode_literals +import salt.executors.splay as splay_exec + # Import Salt libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -import salt.executors.splay as splay_exec - class SplayTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - return { - splay_exec: { - '__grains__': {'id': 'foo'}, - } - } + return {splay_exec: {"__grains__": {"id": "foo"}}} def test__get_hash(self): # We just want to make sure that this function does not result in an diff --git a/tests/unit/files/rosters/ansible/roster.py b/tests/unit/files/rosters/ansible/roster.py index 1052e980e31..b54b74ede81 100755 --- a/tests/unit/files/rosters/ansible/roster.py +++ b/tests/unit/files/rosters/ansible/roster.py @@ -1,44 +1,27 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import absolute_import + import json import sys inventory = { - "usa": { - "children": [ - "southeast", - ] - }, - "southeast": { - "children": [ - "atlanta", - "raleigh", - ], - "vars": { - "some_server": "foo.southeast.example.com", - "halon_system_timeout": 30, - "self_destruct_countdown": 60, - "escape_pods": 2, - } - }, - "raleigh": [ - "host2", - "host3", - ], - "atlanta": [ - "host1", - "host2", - ], -} -hostvars = { - "host1": { - }, - "host2": { - }, - "host3": { - } + "usa": {"children": ["southeast"]}, + "southeast": { + "children": ["atlanta", "raleigh"], + "vars": { + "some_server": "foo.southeast.example.com", + "halon_system_timeout": 30, + "self_destruct_countdown": 60, + "escape_pods": 2, + }, + }, + "raleigh": ["host2", "host3"], + "atlanta": ["host1", "host2"], } +hostvars = {"host1": {}, "host2": {}, "host3": {}} -if '--host' in sys.argv: +if "--host" in sys.argv: print(json.dumps(hostvars.get(sys.argv[-1], {}))) -if '--list' in sys.argv: +if "--list" in sys.argv: print(json.dumps(inventory)) diff --git a/tests/unit/fileserver/test_gitfs.py b/tests/unit/fileserver/test_gitfs.py index 34e234e020c..73cef2b3b69 100644 --- a/tests/unit/fileserver/test_gitfs.py +++ b/tests/unit/fileserver/test_gitfs.py @@ -1,50 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Erik Johnson <erik@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno +import logging import os import shutil +import stat import tempfile import textwrap + +import salt.ext.six import salt.ext.tornado.ioloop -import logging -import stat + +# Import salt libs +import salt.fileserver.gitfs as gitfs +import salt.utils.files +import salt.utils.gitfs +import salt.utils.platform +import salt.utils.win_functions +import salt.utils.yaml +from salt.utils.gitfs import ( + GITPYTHON_MINVER, + GITPYTHON_VERSION, + LIBGIT2_MINVER, + LIBGIT2_VERSION, + PYGIT2_MINVER, + PYGIT2_VERSION, +) +from tests.support.helpers import patched_environ +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + try: import pwd # pylint: disable=unused-import except ImportError: pass -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch -from tests.support.helpers import patched_environ - -# Import salt libs -import salt.fileserver.gitfs as gitfs -import salt.utils.files -import salt.utils.platform -import salt.utils.win_functions -import salt.utils.yaml -import salt.ext.six - -import salt.utils.gitfs -from salt.utils.gitfs import ( - GITPYTHON_VERSION, - GITPYTHON_MINVER, - PYGIT2_VERSION, - PYGIT2_MINVER, - LIBGIT2_VERSION, - LIBGIT2_MINVER -) try: import git + # We still need to use GitPython here for temp repo setup, so we do need to # actually import it. But we don't need import pygit2 in this module, we # can just use the LooseVersion instances imported along with @@ -54,16 +58,15 @@ except (ImportError, AttributeError): HAS_GITPYTHON = False try: - HAS_PYGIT2 = PYGIT2_VERSION >= PYGIT2_MINVER \ - and LIBGIT2_VERSION >= LIBGIT2_MINVER + HAS_PYGIT2 = PYGIT2_VERSION >= PYGIT2_MINVER and LIBGIT2_VERSION >= LIBGIT2_MINVER except AttributeError: HAS_PYGIT2 = False log = logging.getLogger(__name__) -UNICODE_FILENAME = 'питон.txt' -UNICODE_DIRNAME = UNICODE_ENVNAME = 'соль' -TAG_NAME = 'mytag' +UNICODE_FILENAME = "питон.txt" +UNICODE_DIRNAME = UNICODE_ENVNAME = "соль" +TAG_NAME = "mytag" def _rmtree_error(func, path, excinfo): @@ -73,68 +76,66 @@ def _rmtree_error(func, path, excinfo): def _clear_instance_map(): try: - del salt.utils.gitfs.GitFS.instance_map[salt.ext.tornado.ioloop.IOLoop.current()] + del salt.utils.gitfs.GitFS.instance_map[ + salt.ext.tornado.ioloop.IOLoop.current() + ] except KeyError: pass -@skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required'.format(GITPYTHON_MINVER)) +@skipIf(not HAS_GITPYTHON, "GitPython >= {0} required".format(GITPYTHON_MINVER)) class GitfsConfigTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): opts = { - 'sock_dir': self.tmp_sock_dir, - 'gitfs_remotes': ['file://' + self.tmp_repo_dir], - 'gitfs_root': '', - 'fileserver_backend': ['gitfs'], - 'gitfs_base': 'master', - 'fileserver_events': True, - 'transport': 'zeromq', - 'gitfs_mountpoint': '', - 'gitfs_saltenv': [], - 'gitfs_saltenv_whitelist': [], - 'gitfs_saltenv_blacklist': [], - 'gitfs_user': '', - 'gitfs_password': '', - 'gitfs_insecure_auth': False, - 'gitfs_privkey': '', - 'gitfs_pubkey': '', - 'gitfs_passphrase': '', - 'gitfs_refspecs': [ - '+refs/heads/*:refs/remotes/origin/*', - '+refs/tags/*:refs/tags/*' + "sock_dir": self.tmp_sock_dir, + "gitfs_remotes": ["file://" + self.tmp_repo_dir], + "gitfs_root": "", + "fileserver_backend": ["gitfs"], + "gitfs_base": "master", + "gitfs_fallback": "", + "fileserver_events": True, + "transport": "zeromq", + "gitfs_mountpoint": "", + "gitfs_saltenv": [], + "gitfs_saltenv_whitelist": [], + "gitfs_saltenv_blacklist": [], + "gitfs_user": "", + "gitfs_password": "", + "gitfs_insecure_auth": False, + "gitfs_privkey": "", + "gitfs_pubkey": "", + "gitfs_passphrase": "", + "gitfs_refspecs": [ + "+refs/heads/*:refs/remotes/origin/*", + "+refs/tags/*:refs/tags/*", ], - 'gitfs_ssl_verify': True, - 'gitfs_disable_saltenv_mapping': False, - 'gitfs_ref_types': ['branch', 'tag', 'sha'], - 'gitfs_update_interval': 60, - '__role': 'master', - } - opts['cachedir'] = self.tmp_cachedir - opts['sock_dir'] = self.tmp_sock_dir - return { - gitfs: { - '__opts__': opts, - } + "gitfs_ssl_verify": True, + "gitfs_disable_saltenv_mapping": False, + "gitfs_ref_types": ["branch", "tag", "sha"], + "gitfs_update_interval": 60, + "__role": "master", } + opts["cachedir"] = self.tmp_cachedir + opts["sock_dir"] = self.tmp_sock_dir + return {gitfs: {"__opts__": opts}} @classmethod def setUpClass(cls): # Clear the instance map so that we make sure to create a new instance # for this test class. _clear_instance_map() - cls.tmp_repo_dir = os.path.join(RUNTIME_VARS.TMP, 'gitfs_root') + cls.tmp_repo_dir = os.path.join(RUNTIME_VARS.TMP, "gitfs_root") if salt.utils.platform.is_windows(): - cls.tmp_repo_dir = cls.tmp_repo_dir.replace('\\', '/') + cls.tmp_repo_dir = cls.tmp_repo_dir.replace("\\", "/") cls.tmp_cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) cls.tmp_sock_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @classmethod def tearDownClass(cls): - ''' + """ Remove the temporary git repository and gitfs cache directory to ensure a clean environment for the other test class(es). - ''' + """ for path in (cls.tmp_cachedir, cls.tmp_sock_dir): try: shutil.rmtree(path, onerror=_rmtree_error) @@ -146,7 +147,8 @@ class GitfsConfigTestCase(TestCase, LoaderModuleMockMixin): raise def test_per_saltenv_config(self): - opts_override = textwrap.dedent(''' + opts_override = textwrap.dedent( + """ gitfs_root: salt gitfs_saltenv: @@ -169,65 +171,69 @@ class GitfsConfigTestCase(TestCase, LoaderModuleMockMixin): - saltenv: - baz: - mountpoint: abc - '''.format('/' if salt.utils.platform.is_windows() else '')) + """.format( + "/" if salt.utils.platform.is_windows() else "" + ) + ) with patch.dict(gitfs.__opts__, salt.utils.yaml.safe_load(opts_override)): git_fs = salt.utils.gitfs.GitFS( gitfs.__opts__, - gitfs.__opts__['gitfs_remotes'], + gitfs.__opts__["gitfs_remotes"], per_remote_overrides=gitfs.PER_REMOTE_OVERRIDES, - per_remote_only=gitfs.PER_REMOTE_ONLY) + per_remote_only=gitfs.PER_REMOTE_ONLY, + ) # repo1 (branch: foo) # The mountpoint should take the default (from gitfs_mountpoint), while # ref and root should take the per-saltenv params. - self.assertEqual(git_fs.remotes[0].mountpoint('foo'), '') - self.assertEqual(git_fs.remotes[0].ref('foo'), 'foo_branch') - self.assertEqual(git_fs.remotes[0].root('foo'), 'foo_root') + self.assertEqual(git_fs.remotes[0].mountpoint("foo"), "") + self.assertEqual(git_fs.remotes[0].ref("foo"), "foo_branch") + self.assertEqual(git_fs.remotes[0].root("foo"), "foo_root") # repo1 (branch: bar) # The 'bar' branch does not have a per-saltenv configuration set, so # each of the below values should fall back to global values. - self.assertEqual(git_fs.remotes[0].mountpoint('bar'), '') - self.assertEqual(git_fs.remotes[0].ref('bar'), 'bar') - self.assertEqual(git_fs.remotes[0].root('bar'), 'salt') + self.assertEqual(git_fs.remotes[0].mountpoint("bar"), "") + self.assertEqual(git_fs.remotes[0].ref("bar"), "bar") + self.assertEqual(git_fs.remotes[0].root("bar"), "salt") # repo1 (branch: baz) # The 'baz' branch does not have a per-saltenv configuration set, but # it is defined in the gitfs_saltenv parameter, so the values # from that parameter should be returned. - self.assertEqual(git_fs.remotes[0].mountpoint('baz'), 'baz_mountpoint') - self.assertEqual(git_fs.remotes[0].ref('baz'), 'baz_branch') - self.assertEqual(git_fs.remotes[0].root('baz'), 'baz_root') + self.assertEqual(git_fs.remotes[0].mountpoint("baz"), "baz_mountpoint") + self.assertEqual(git_fs.remotes[0].ref("baz"), "baz_branch") + self.assertEqual(git_fs.remotes[0].root("baz"), "baz_root") # repo2 (branch: foo) # The mountpoint should take the per-remote mountpoint value of # 'repo2', while ref and root should fall back to global values. - self.assertEqual(git_fs.remotes[1].mountpoint('foo'), 'repo2') - self.assertEqual(git_fs.remotes[1].ref('foo'), 'foo') - self.assertEqual(git_fs.remotes[1].root('foo'), 'salt') + self.assertEqual(git_fs.remotes[1].mountpoint("foo"), "repo2") + self.assertEqual(git_fs.remotes[1].ref("foo"), "foo") + self.assertEqual(git_fs.remotes[1].root("foo"), "salt") # repo2 (branch: bar) # The 'bar' branch does not have a per-saltenv configuration set, so # the mountpoint should take the per-remote mountpoint value of # 'repo2', while ref and root should fall back to global values. - self.assertEqual(git_fs.remotes[1].mountpoint('bar'), 'repo2') - self.assertEqual(git_fs.remotes[1].ref('bar'), 'bar') - self.assertEqual(git_fs.remotes[1].root('bar'), 'salt') + self.assertEqual(git_fs.remotes[1].mountpoint("bar"), "repo2") + self.assertEqual(git_fs.remotes[1].ref("bar"), "bar") + self.assertEqual(git_fs.remotes[1].root("bar"), "salt") # repo2 (branch: baz) # The 'baz' branch has the mountpoint configured as a per-saltenv # parameter. The other two should take the values defined in # gitfs_saltenv. - self.assertEqual(git_fs.remotes[1].mountpoint('baz'), 'abc') - self.assertEqual(git_fs.remotes[1].ref('baz'), 'baz_branch') - self.assertEqual(git_fs.remotes[1].root('baz'), 'baz_root') + self.assertEqual(git_fs.remotes[1].mountpoint("baz"), "abc") + self.assertEqual(git_fs.remotes[1].ref("baz"), "baz_branch") + self.assertEqual(git_fs.remotes[1].root("baz"), "baz_root") -LOAD = {'saltenv': 'base'} +LOAD = {"saltenv": "base"} class GitFSTestFuncs(object): - ''' + """ These are where the tests go, so that they can be run using both GitPython and pygit2. @@ -244,149 +250,243 @@ class GitFSTestFuncs(object): 1. Each test needs to call gitfs.update() *after* any patching, and *before* calling the function being tested. 2. Do *NOT* move the gitfs.update() into the setUp. - ''' + """ + def test_file_list(self): gitfs.update() ret = gitfs.file_list(LOAD) - self.assertIn('testfile', ret) + self.assertIn("testfile", ret) self.assertIn(UNICODE_FILENAME, ret) # This function does not use os.sep, the Salt fileserver uses the # forward slash, hence it being explicitly used to join here. - self.assertIn('/'.join((UNICODE_DIRNAME, 'foo.txt')), ret) + self.assertIn("/".join((UNICODE_DIRNAME, "foo.txt")), ret) def test_dir_list(self): gitfs.update() ret = gitfs.dir_list(LOAD) - self.assertIn('grail', ret) + self.assertIn("grail", ret) self.assertIn(UNICODE_DIRNAME, ret) + def test_find_and_serve_file(self): + with patch.dict(gitfs.__opts__, {"file_buffer_size": 262144}): + gitfs.update() + + # find_file + ret = gitfs.find_file("testfile") + self.assertEqual("testfile", ret["rel"]) + + full_path_to_file = salt.utils.path.join( + gitfs.__opts__["cachedir"], "gitfs", "refs", "base", "testfile" + ) + self.assertEqual(full_path_to_file, ret["path"]) + + # serve_file + load = {"saltenv": "base", "path": full_path_to_file, "loc": 0} + fnd = {"path": full_path_to_file, "rel": "testfile"} + ret = gitfs.serve_file(load, fnd) + + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.BASE_FILES, "testfile"), "r" + ) as fp_: # NB: Why not 'rb'? + data = fp_.read() + + self.assertDictEqual(ret, {"data": data, "dest": "testfile"}) + + def test_file_list_fallback(self): + with patch.dict(gitfs.__opts__, {"gitfs_fallback": "master"}): + gitfs.update() + ret = gitfs.file_list({"saltenv": "notexisting"}) + self.assertIn("testfile", ret) + self.assertIn(UNICODE_FILENAME, ret) + # This function does not use os.sep, the Salt fileserver uses the + # forward slash, hence it being explicitly used to join here. + self.assertIn("/".join((UNICODE_DIRNAME, "foo.txt")), ret) + + def test_dir_list_fallback(self): + with patch.dict(gitfs.__opts__, {"gitfs_fallback": "master"}): + gitfs.update() + ret = gitfs.dir_list({"saltenv": "notexisting"}) + self.assertIn("grail", ret) + self.assertIn(UNICODE_DIRNAME, ret) + + def test_find_and_serve_file_fallback(self): + with patch.dict( + gitfs.__opts__, {"file_buffer_size": 262144, "gitfs_fallback": "master"} + ): + gitfs.update() + + # find_file + ret = gitfs.find_file("testfile", tgt_env="notexisting") + self.assertEqual("testfile", ret["rel"]) + + full_path_to_file = salt.utils.path.join( + gitfs.__opts__["cachedir"], "gitfs", "refs", "notexisting", "testfile" + ) + self.assertEqual(full_path_to_file, ret["path"]) + + # serve_file + load = {"saltenv": "notexisting", "path": full_path_to_file, "loc": 0} + fnd = {"path": full_path_to_file, "rel": "testfile"} + ret = gitfs.serve_file(load, fnd) + + with salt.utils.files.fopen( + os.path.join(RUNTIME_VARS.BASE_FILES, "testfile"), "r" + ) as fp_: # NB: Why not 'rb'? + data = fp_.read() + + self.assertDictEqual(ret, {"data": data, "dest": "testfile"}) + def test_envs(self): gitfs.update() ret = gitfs.envs(ignore_cache=True) - self.assertIn('base', ret) + self.assertIn("base", ret) self.assertIn(UNICODE_ENVNAME, ret) self.assertIn(TAG_NAME, ret) def test_ref_types_global(self): - ''' + """ Test the global gitfs_ref_types config option - ''' - with patch.dict(gitfs.__opts__, {'gitfs_ref_types': ['branch']}): + """ + with patch.dict(gitfs.__opts__, {"gitfs_ref_types": ["branch"]}): gitfs.update() ret = gitfs.envs(ignore_cache=True) # Since we are restricting to branches only, the tag should not # appear in the envs list. - self.assertIn('base', ret) + self.assertIn("base", ret) self.assertIn(UNICODE_ENVNAME, ret) self.assertNotIn(TAG_NAME, ret) def test_ref_types_per_remote(self): - ''' + """ Test the per_remote ref_types config option, using a different ref_types setting than the global test. - ''' - remotes = [{'file://' + self.tmp_repo_dir: [{'ref_types': ['tag']}]}] - with patch.dict(gitfs.__opts__, {'gitfs_remotes': remotes}): + """ + remotes = [{"file://" + self.tmp_repo_dir: [{"ref_types": ["tag"]}]}] + with patch.dict(gitfs.__opts__, {"gitfs_remotes": remotes}): gitfs.update() ret = gitfs.envs(ignore_cache=True) # Since we are restricting to tags only, the tag should appear in # the envs list, but the branches should not. - self.assertNotIn('base', ret) + self.assertNotIn("base", ret) self.assertNotIn(UNICODE_ENVNAME, ret) self.assertIn(TAG_NAME, ret) def test_disable_saltenv_mapping_global_with_mapping_defined_globally(self): - ''' + """ Test the global gitfs_disable_saltenv_mapping config option, combined with the per-saltenv mapping being defined in the global gitfs_saltenv option. - ''' - opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ gitfs_disable_saltenv_mapping: True gitfs_saltenv: - foo: - ref: somebranch - ''')) + """ + ) + ) with patch.dict(gitfs.__opts__, opts): gitfs.update() ret = gitfs.envs(ignore_cache=True) # Since we are restricting to tags only, the tag should appear in # the envs list, but the branches should not. - self.assertEqual(ret, ['base', 'foo']) + self.assertEqual(ret, ["base", "foo"]) def test_saltenv_blacklist(self): - ''' + """ test saltenv_blacklist - ''' - opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ gitfs_saltenv_blacklist: base - ''')) + """ + ) + ) with patch.dict(gitfs.__opts__, opts): gitfs.update() ret = gitfs.envs(ignore_cache=True) - assert 'base' not in ret + assert "base" not in ret assert UNICODE_ENVNAME in ret - assert 'mytag' in ret + assert "mytag" in ret def test_saltenv_whitelist(self): - ''' + """ test saltenv_whitelist - ''' - opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ gitfs_saltenv_whitelist: base - ''')) + """ + ) + ) with patch.dict(gitfs.__opts__, opts): gitfs.update() ret = gitfs.envs(ignore_cache=True) - assert 'base' in ret + assert "base" in ret assert UNICODE_ENVNAME not in ret - assert 'mytag' not in ret + assert "mytag" not in ret def test_env_deprecated_opts(self): - ''' + """ ensure deprecated options gitfs_env_whitelist and gitfs_env_blacklist do not cause gitfs to not load. - ''' - opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ gitfs_env_whitelist: base gitfs_env_blacklist: '' - ''')) + """ + ) + ) with patch.dict(gitfs.__opts__, opts): gitfs.update() ret = gitfs.envs(ignore_cache=True) - assert 'base' in ret + assert "base" in ret assert UNICODE_ENVNAME in ret - assert 'mytag' in ret + assert "mytag" in ret def test_disable_saltenv_mapping_global_with_mapping_defined_per_remote(self): - ''' + """ Test the global gitfs_disable_saltenv_mapping config option, combined with the per-saltenv mapping being defined in the remote itself via the "saltenv" per-remote option. - ''' - opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ gitfs_disable_saltenv_mapping: True gitfs_remotes: - {0}: - saltenv: - bar: - ref: somebranch - '''.format(self.tmp_repo_dir))) + """.format( + self.tmp_repo_dir + ) + ) + ) with patch.dict(gitfs.__opts__, opts): gitfs.update() ret = gitfs.envs(ignore_cache=True) # Since we are restricting to tags only, the tag should appear in # the envs list, but the branches should not. - self.assertEqual(ret, ['bar', 'base']) + self.assertEqual(ret, ["bar", "base"]) def test_disable_saltenv_mapping_per_remote_with_mapping_defined_globally(self): - ''' + """ Test the per-remote disable_saltenv_mapping config option, combined with the per-saltenv mapping being defined in the global gitfs_saltenv option. - ''' - opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ gitfs_remotes: - {0}: - disable_saltenv_mapping: True @@ -394,43 +494,52 @@ class GitFSTestFuncs(object): gitfs_saltenv: - hello: - ref: somebranch - '''.format(self.tmp_repo_dir))) + """.format( + self.tmp_repo_dir + ) + ) + ) with patch.dict(gitfs.__opts__, opts): gitfs.update() ret = gitfs.envs(ignore_cache=True) # Since we are restricting to tags only, the tag should appear in # the envs list, but the branches should not. - self.assertEqual(ret, ['base', 'hello']) + self.assertEqual(ret, ["base", "hello"]) def test_disable_saltenv_mapping_per_remote_with_mapping_defined_per_remote(self): - ''' + """ Test the per-remote disable_saltenv_mapping config option, combined with the per-saltenv mapping being defined in the remote itself via the "saltenv" per-remote option. - ''' - opts = salt.utils.yaml.safe_load(textwrap.dedent('''\ + """ + opts = salt.utils.yaml.safe_load( + textwrap.dedent( + """\ gitfs_remotes: - {0}: - disable_saltenv_mapping: True - saltenv: - world: - ref: somebranch - '''.format(self.tmp_repo_dir))) + """.format( + self.tmp_repo_dir + ) + ) + ) with patch.dict(gitfs.__opts__, opts): gitfs.update() ret = gitfs.envs(ignore_cache=True) # Since we are restricting to tags only, the tag should appear in # the envs list, but the branches should not. - self.assertEqual(ret, ['base', 'world']) + self.assertEqual(ret, ["base", "world"]) class GitFSTestBase(object): - @classmethod def setUpClass(cls): - cls.tmp_repo_dir = os.path.join(RUNTIME_VARS.TMP, 'gitfs_root') + cls.tmp_repo_dir = os.path.join(RUNTIME_VARS.TMP, "gitfs_root") if salt.utils.platform.is_windows(): - cls.tmp_repo_dir = cls.tmp_repo_dir.replace('\\', '/') + cls.tmp_repo_dir = cls.tmp_repo_dir.replace("\\", "/") cls.tmp_cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) cls.tmp_sock_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -444,8 +553,8 @@ class GitFSTestBase(object): shutil.copytree( salt.ext.six.text_type(RUNTIME_VARS.BASE_FILES), - salt.ext.six.text_type(cls.tmp_repo_dir + '/'), - symlinks=True + salt.ext.six.text_type(cls.tmp_repo_dir + "/"), + symlinks=True, ) repo = git.Repo.init(cls.tmp_repo_dir) @@ -456,31 +565,28 @@ class GitFSTestBase(object): else: username = pwd.getpwuid(os.geteuid()).pw_name except AttributeError: - log.error( - 'Unable to get effective username, falling back to \'root\'.' - ) - username = str('root') + log.error("Unable to get effective username, falling back to 'root'.") + username = str("root") with patched_environ(USERNAME=username): - repo.index.add([x for x in os.listdir(cls.tmp_repo_dir) - if x != '.git']) - repo.index.commit('Test') + repo.index.add([x for x in os.listdir(cls.tmp_repo_dir) if x != ".git"]) + repo.index.commit("Test") # Add another branch with unicode characters in the name - repo.create_head(UNICODE_ENVNAME, 'HEAD') + repo.create_head(UNICODE_ENVNAME, "HEAD") # Add a tag - repo.create_tag(TAG_NAME, 'HEAD') + repo.create_tag(TAG_NAME, "HEAD") # Older GitPython versions do not have a close method. - if hasattr(repo, 'close'): + if hasattr(repo, "close"): repo.close() @classmethod def tearDownClass(cls): - ''' + """ Remove the temporary git repository and gitfs cache directory to ensure a clean environment for the other test class(es). - ''' + """ for path in (cls.tmp_cachedir, cls.tmp_sock_dir, cls.tmp_repo_dir): try: salt.utils.files.rm_rf(path) @@ -491,7 +597,7 @@ class GitFSTestBase(object): raise def setUp(self): - ''' + """ We don't want to check in another .git dir into GH because that just gets messy. Instead, we'll create a temporary repo on the fly for the tests to examine. @@ -499,17 +605,20 @@ class GitFSTestBase(object): Also ensure we A) don't re-use the singleton, and B) that the cachedirs are cleared. This keeps these performance enhancements from affecting the results of subsequent tests. - ''' + """ if not gitfs.__virtual__(): self.skipTest("GitFS could not be loaded. Skipping GitFS tests!") _clear_instance_map() - for subdir in ('gitfs', 'file_lists'): + for subdir in ("gitfs", "file_lists"): try: salt.utils.files.rm_rf(os.path.join(self.tmp_cachedir, subdir)) except OSError as exc: if exc.errno == errno.EACCES: - log.warning("Access error removeing file %s", os.path.join(self.tmp_cachedir, subdir)) + log.warning( + "Access error removeing file %s", + os.path.join(self.tmp_cachedir, subdir), + ) continue if exc.errno != errno.ENOENT: raise @@ -518,87 +627,88 @@ class GitFSTestBase(object): self.setup_loader_modules() -@skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required'.format(GITPYTHON_MINVER)) +@skipIf(not HAS_GITPYTHON, "GitPython >= {0} required".format(GITPYTHON_MINVER)) class GitPythonTest(GitFSTestBase, GitFSTestFuncs, TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): opts = { - 'sock_dir': self.tmp_sock_dir, - 'gitfs_remotes': ['file://' + self.tmp_repo_dir], - 'gitfs_root': '', - 'fileserver_backend': ['gitfs'], - 'gitfs_base': 'master', - 'fileserver_events': True, - 'transport': 'zeromq', - 'gitfs_mountpoint': '', - 'gitfs_saltenv': [], - 'gitfs_saltenv_whitelist': [], - 'gitfs_saltenv_blacklist': [], - 'gitfs_user': '', - 'gitfs_password': '', - 'gitfs_insecure_auth': False, - 'gitfs_privkey': '', - 'gitfs_pubkey': '', - 'gitfs_passphrase': '', - 'gitfs_refspecs': [ - '+refs/heads/*:refs/remotes/origin/*', - '+refs/tags/*:refs/tags/*' + "sock_dir": self.tmp_sock_dir, + "gitfs_remotes": ["file://" + self.tmp_repo_dir], + "gitfs_root": "", + "fileserver_backend": ["gitfs"], + "gitfs_base": "master", + "gitfs_fallback": "", + "fileserver_events": True, + "transport": "zeromq", + "gitfs_mountpoint": "", + "gitfs_saltenv": [], + "gitfs_saltenv_whitelist": [], + "gitfs_saltenv_blacklist": [], + "gitfs_user": "", + "gitfs_password": "", + "gitfs_insecure_auth": False, + "gitfs_privkey": "", + "gitfs_pubkey": "", + "gitfs_passphrase": "", + "gitfs_refspecs": [ + "+refs/heads/*:refs/remotes/origin/*", + "+refs/tags/*:refs/tags/*", ], - 'gitfs_ssl_verify': True, - 'gitfs_disable_saltenv_mapping': False, - 'gitfs_ref_types': ['branch', 'tag', 'sha'], - 'gitfs_update_interval': 60, - '__role': 'master', - } - opts['cachedir'] = self.tmp_cachedir - opts['sock_dir'] = self.tmp_sock_dir - opts['gitfs_provider'] = 'gitpython' - return { - gitfs: { - '__opts__': opts, - } + "gitfs_ssl_verify": True, + "gitfs_disable_saltenv_mapping": False, + "gitfs_ref_types": ["branch", "tag", "sha"], + "gitfs_update_interval": 60, + "__role": "master", } + opts["cachedir"] = self.tmp_cachedir + opts["sock_dir"] = self.tmp_sock_dir + opts["gitfs_provider"] = "gitpython" + return {gitfs: {"__opts__": opts}} -@skipIf(not HAS_GITPYTHON, 'GitPython >= {0} required for temp repo setup'.format(GITPYTHON_MINVER)) -@skipIf(not HAS_PYGIT2, 'pygit2 >= {0} and libgit2 >= {1} required'.format(PYGIT2_MINVER, LIBGIT2_MINVER)) -@skipIf(salt.utils.platform.is_windows(), 'Skip Pygit2 on windows, due to pygit2 access error on windows') +@skipIf( + not HAS_GITPYTHON, + "GitPython >= {0} required for temp repo setup".format(GITPYTHON_MINVER), +) +@skipIf( + not HAS_PYGIT2, + "pygit2 >= {0} and libgit2 >= {1} required".format(PYGIT2_MINVER, LIBGIT2_MINVER), +) +@skipIf( + salt.utils.platform.is_windows(), + "Skip Pygit2 on windows, due to pygit2 access error on windows", +) class Pygit2Test(GitFSTestBase, GitFSTestFuncs, TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): opts = { - 'sock_dir': self.tmp_sock_dir, - 'gitfs_remotes': ['file://' + self.tmp_repo_dir], - 'gitfs_root': '', - 'fileserver_backend': ['gitfs'], - 'gitfs_base': 'master', - 'fileserver_events': True, - 'transport': 'zeromq', - 'gitfs_mountpoint': '', - 'gitfs_saltenv': [], - 'gitfs_saltenv_whitelist': [], - 'gitfs_saltenv_blacklist': [], - 'gitfs_user': '', - 'gitfs_password': '', - 'gitfs_insecure_auth': False, - 'gitfs_privkey': '', - 'gitfs_pubkey': '', - 'gitfs_passphrase': '', - 'gitfs_refspecs': [ - '+refs/heads/*:refs/remotes/origin/*', - '+refs/tags/*:refs/tags/*' + "sock_dir": self.tmp_sock_dir, + "gitfs_remotes": ["file://" + self.tmp_repo_dir], + "gitfs_root": "", + "fileserver_backend": ["gitfs"], + "gitfs_base": "master", + "gitfs_fallback": "", + "fileserver_events": True, + "transport": "zeromq", + "gitfs_mountpoint": "", + "gitfs_saltenv": [], + "gitfs_saltenv_whitelist": [], + "gitfs_saltenv_blacklist": [], + "gitfs_user": "", + "gitfs_password": "", + "gitfs_insecure_auth": False, + "gitfs_privkey": "", + "gitfs_pubkey": "", + "gitfs_passphrase": "", + "gitfs_refspecs": [ + "+refs/heads/*:refs/remotes/origin/*", + "+refs/tags/*:refs/tags/*", ], - 'gitfs_ssl_verify': True, - 'gitfs_disable_saltenv_mapping': False, - 'gitfs_ref_types': ['branch', 'tag', 'sha'], - 'gitfs_update_interval': 60, - '__role': 'master', - } - opts['cachedir'] = self.tmp_cachedir - opts['sock_dir'] = self.tmp_sock_dir - opts['gitfs_provider'] = 'pygit2' - return { - gitfs: { - '__opts__': opts, - } + "gitfs_ssl_verify": True, + "gitfs_disable_saltenv_mapping": False, + "gitfs_ref_types": ["branch", "tag", "sha"], + "gitfs_update_interval": 60, + "__role": "master", } + opts["cachedir"] = self.tmp_cachedir + opts["sock_dir"] = self.tmp_sock_dir + opts["gitfs_provider"] = "pygit2" + return {gitfs: {"__opts__": opts}} diff --git a/tests/unit/fileserver/test_hgfs.py b/tests/unit/fileserver/test_hgfs.py index b1fc50952b7..f93c93cc477 100644 --- a/tests/unit/fileserver/test_hgfs.py +++ b/tests/unit/fileserver/test_hgfs.py @@ -3,37 +3,37 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch - # Import Salt libs import salt.fileserver.hgfs as hgfs +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class HgfsFileTest(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - return { - hgfs: {} - } + return {hgfs: {}} def test_env_is_exposed(self): - ''' + """ test _env_is_exposed method when base is in whitelist - ''' - with patch.dict(hgfs.__opts__, - {'hgfs_saltenv_whitelist': 'base', - 'hgfs_saltenv_blacklist': ''}): - assert hgfs._env_is_exposed('base') + """ + with patch.dict( + hgfs.__opts__, + {"hgfs_saltenv_whitelist": "base", "hgfs_saltenv_blacklist": ""}, + ): + assert hgfs._env_is_exposed("base") def test_env_is_exposed_blacklist(self): - ''' + """ test _env_is_exposed method when base is in blacklist - ''' - with patch.dict(hgfs.__opts__, - {'hgfs_saltenv_whitelist': '', - 'hgfs_saltenv_blacklist': 'base'}): - assert not hgfs._env_is_exposed('base') + """ + with patch.dict( + hgfs.__opts__, + {"hgfs_saltenv_whitelist": "", "hgfs_saltenv_blacklist": "base"}, + ): + assert not hgfs._env_is_exposed("base") diff --git a/tests/unit/fileserver/test_roots.py b/tests/unit/fileserver/test_roots.py index e0d939a0861..2b71582780a 100644 --- a/tests/unit/fileserver/test_roots.py +++ b/tests/unit/fileserver/test_roots.py @@ -1,79 +1,85 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place <mp@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import os import tempfile -# Import Salt Testing libs -from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch -from tests.support.runtests import RUNTIME_VARS +import salt.fileclient # Import Salt libs import salt.fileserver.roots as roots -import salt.fileclient import salt.utils.files import salt.utils.hashutils import salt.utils.platform +# Import Salt Testing libs +from tests.support.mixins import ( + AdaptedConfigurationTestCaseMixin, + LoaderModuleMockMixin, +) +from tests.support.mock import patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase + try: import win32file except ImportError: pass -UNICODE_FILENAME = 'питон.txt' -UNICODE_DIRNAME = UNICODE_ENVNAME = 'соль' +UNICODE_FILENAME = "питон.txt" +UNICODE_DIRNAME = UNICODE_ENVNAME = "соль" class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): - def setup_loader_modules(self): - self.opts = self.get_temp_config('master') - empty_dir = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, 'empty_dir') + self.opts = self.get_temp_config("master") + empty_dir = os.path.join(RUNTIME_VARS.TMP_STATE_TREE, "empty_dir") if not os.path.isdir(empty_dir): os.makedirs(empty_dir) - return {roots: {'__opts__': self.opts}} + return {roots: {"__opts__": self.opts}} @classmethod def setUpClass(cls): - ''' + """ Create special file_roots for symlink test on Windows - ''' + """ if salt.utils.platform.is_windows(): root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - source_sym = os.path.join(root_dir, 'source_sym') - with salt.utils.files.fopen(source_sym, 'w') as fp_: - fp_.write('hello world!\n') + source_sym = os.path.join(root_dir, "source_sym") + with salt.utils.files.fopen(source_sym, "w") as fp_: + fp_.write("hello world!\n") cwd = os.getcwd() try: os.chdir(root_dir) - win32file.CreateSymbolicLink('dest_sym', 'source_sym', 0) + win32file.CreateSymbolicLink("dest_sym", "source_sym", 0) finally: os.chdir(cwd) - cls.test_symlink_list_file_roots = {'base': [root_dir]} + cls.test_symlink_list_file_roots = {"base": [root_dir]} else: cls.test_symlink_list_file_roots = None cls.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - full_path_to_file = os.path.join(RUNTIME_VARS.BASE_FILES, 'testfile') - with salt.utils.files.fopen(full_path_to_file, 'rb') as s_fp: - with salt.utils.files.fopen(os.path.join(cls.tmp_dir, 'testfile'), 'wb') as d_fp: + full_path_to_file = os.path.join(RUNTIME_VARS.BASE_FILES, "testfile") + with salt.utils.files.fopen(full_path_to_file, "rb") as s_fp: + with salt.utils.files.fopen( + os.path.join(cls.tmp_dir, "testfile"), "wb" + ) as d_fp: for line in s_fp: d_fp.write(line) @classmethod def tearDownClass(cls): - ''' + """ Remove special file_roots for symlink test - ''' + """ if salt.utils.platform.is_windows(): try: - salt.utils.files.rm_rf(cls.test_symlink_list_file_roots['base'][0]) + salt.utils.files.rm_rf(cls.test_symlink_list_file_roots["base"][0]) except OSError: pass salt.utils.files.rm_rf(cls.tmp_dir) @@ -82,113 +88,103 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix del self.opts def test_file_list(self): - ret = roots.file_list({'saltenv': 'base'}) - self.assertIn('testfile', ret) + ret = roots.file_list({"saltenv": "base"}) + self.assertIn("testfile", ret) self.assertIn(UNICODE_FILENAME, ret) def test_find_file(self): - ret = roots.find_file('testfile') - self.assertEqual('testfile', ret['rel']) + ret = roots.find_file("testfile") + self.assertEqual("testfile", ret["rel"]) - full_path_to_file = os.path.join(RUNTIME_VARS.BASE_FILES, 'testfile') - self.assertEqual(full_path_to_file, ret['path']) + full_path_to_file = os.path.join(RUNTIME_VARS.BASE_FILES, "testfile") + self.assertEqual(full_path_to_file, ret["path"]) def test_serve_file(self): - with patch.dict(roots.__opts__, {'file_buffer_size': 262144}): - load = {'saltenv': 'base', - 'path': os.path.join(self.tmp_dir, 'testfile'), - 'loc': 0 - } - fnd = {'path': os.path.join(self.tmp_dir, 'testfile'), - 'rel': 'testfile'} + with patch.dict(roots.__opts__, {"file_buffer_size": 262144}): + load = { + "saltenv": "base", + "path": os.path.join(self.tmp_dir, "testfile"), + "loc": 0, + } + fnd = {"path": os.path.join(self.tmp_dir, "testfile"), "rel": "testfile"} ret = roots.serve_file(load, fnd) with salt.utils.files.fopen( - os.path.join(RUNTIME_VARS.BASE_FILES, 'testfile'), 'rb') as fp_: + os.path.join(RUNTIME_VARS.BASE_FILES, "testfile"), "rb" + ) as fp_: data = fp_.read() - self.assertDictEqual( - ret, - {'data': data, - 'dest': 'testfile'}) + self.assertDictEqual(ret, {"data": data, "dest": "testfile"}) def test_envs(self): - opts = {'file_roots': copy.copy(self.opts['file_roots'])} - opts['file_roots'][UNICODE_ENVNAME] = opts['file_roots']['base'] + opts = {"file_roots": copy.copy(self.opts["file_roots"])} + opts["file_roots"][UNICODE_ENVNAME] = opts["file_roots"]["base"] with patch.dict(roots.__opts__, opts): ret = roots.envs() - self.assertIn('base', ret) + self.assertIn("base", ret) self.assertIn(UNICODE_ENVNAME, ret) def test_file_hash(self): load = { - 'saltenv': 'base', - 'path': os.path.join(self.tmp_dir, 'testfile'), - } - fnd = { - 'path': os.path.join(self.tmp_dir, 'testfile'), - 'rel': 'testfile' + "saltenv": "base", + "path": os.path.join(self.tmp_dir, "testfile"), } + fnd = {"path": os.path.join(self.tmp_dir, "testfile"), "rel": "testfile"} ret = roots.file_hash(load, fnd) # Hashes are different in Windows. May be how git translates line # endings with salt.utils.files.fopen( - os.path.join(RUNTIME_VARS.BASE_FILES, 'testfile'), 'rb') as fp_: + os.path.join(RUNTIME_VARS.BASE_FILES, "testfile"), "rb" + ) as fp_: hsum = salt.utils.hashutils.sha256_digest(fp_.read()) - self.assertDictEqual( - ret, - { - 'hsum': hsum, - 'hash_type': 'sha256' - } - ) + self.assertDictEqual(ret, {"hsum": hsum, "hash_type": "sha256"}) def test_file_list_emptydirs(self): - ret = roots.file_list_emptydirs({'saltenv': 'base'}) - self.assertIn('empty_dir', ret) + ret = roots.file_list_emptydirs({"saltenv": "base"}) + self.assertIn("empty_dir", ret) def test_file_list_with_slash(self): - opts = {'file_roots': copy.copy(self.opts['file_roots'])} - opts['file_roots']['foo/bar'] = opts['file_roots']['base'] + opts = {"file_roots": copy.copy(self.opts["file_roots"])} + opts["file_roots"]["foo/bar"] = opts["file_roots"]["base"] load = { - 'saltenv': 'foo/bar', - } + "saltenv": "foo/bar", + } with patch.dict(roots.__opts__, opts): ret = roots.file_list(load) - self.assertIn('testfile', ret) + self.assertIn("testfile", ret) self.assertIn(UNICODE_FILENAME, ret) def test_dir_list(self): - ret = roots.dir_list({'saltenv': 'base'}) - self.assertIn('empty_dir', ret) + ret = roots.dir_list({"saltenv": "base"}) + self.assertIn("empty_dir", ret) self.assertIn(UNICODE_DIRNAME, ret) def test_symlink_list(self): - orig_file_roots = self.opts['file_roots'] + orig_file_roots = self.opts["file_roots"] try: if self.test_symlink_list_file_roots: - self.opts['file_roots'] = self.test_symlink_list_file_roots - ret = roots.symlink_list({'saltenv': 'base'}) - self.assertDictEqual(ret, {'dest_sym': 'source_sym'}) + self.opts["file_roots"] = self.test_symlink_list_file_roots + ret = roots.symlink_list({"saltenv": "base"}) + self.assertDictEqual(ret, {"dest_sym": "source_sym"}) finally: if self.test_symlink_list_file_roots: - self.opts['file_roots'] = orig_file_roots + self.opts["file_roots"] = orig_file_roots def test_dynamic_file_roots(self): dyn_root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - top_sls = os.path.join(dyn_root_dir, 'top.sls') - with salt.utils.files.fopen(top_sls, 'w') as fp_: + top_sls = os.path.join(dyn_root_dir, "top.sls") + with salt.utils.files.fopen(top_sls, "w") as fp_: fp_.write("{{saltenv}}:\n '*':\n - dynamo\n") - dynamo_sls = os.path.join(dyn_root_dir, 'dynamo.sls') - with salt.utils.files.fopen(dynamo_sls, 'w') as fp_: + dynamo_sls = os.path.join(dyn_root_dir, "dynamo.sls") + with salt.utils.files.fopen(dynamo_sls, "w") as fp_: fp_.write("foo:\n test.nop\n") - opts = {'file_roots': copy.copy(self.opts['file_roots'])} - opts['file_roots']['__env__'] = [dyn_root_dir] + opts = {"file_roots": copy.copy(self.opts["file_roots"])} + opts["file_roots"]["__env__"] = [dyn_root_dir] with patch.dict(roots.__opts__, opts): - ret1 = roots.find_file('dynamo.sls', 'dyn') - ret2 = roots.file_list({'saltenv': 'dyn'}) - self.assertEqual('dynamo.sls', ret1['rel']) - self.assertIn('top.sls', ret2) - self.assertIn('dynamo.sls', ret2) + ret1 = roots.find_file("dynamo.sls", "dyn") + ret2 = roots.file_list({"saltenv": "dyn"}) + self.assertEqual("dynamo.sls", ret1["rel"]) + self.assertIn("top.sls", ret2) + self.assertIn("dynamo.sls", ret2) diff --git a/tests/unit/fileserver/test_svnfs.py b/tests/unit/fileserver/test_svnfs.py index b9b39edc07f..d5869360ddd 100644 --- a/tests/unit/fileserver/test_svnfs.py +++ b/tests/unit/fileserver/test_svnfs.py @@ -3,37 +3,37 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch - # Import Salt libs import salt.fileserver.svnfs as svnfs +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class SvnfsFileTest(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - return { - svnfs: {} - } + return {svnfs: {}} def test_env_is_exposed(self): - ''' + """ test _env_is_exposed method when base is in whitelist - ''' - with patch.dict(svnfs.__opts__, - {'svnfs_saltenv_whitelist': 'base', - 'svnfs_saltenv_blacklist': ''}): - assert svnfs._env_is_exposed('base') + """ + with patch.dict( + svnfs.__opts__, + {"svnfs_saltenv_whitelist": "base", "svnfs_saltenv_blacklist": ""}, + ): + assert svnfs._env_is_exposed("base") def test_env_is_exposed_blacklist(self): - ''' + """ test _env_is_exposed method when base is in blacklist - ''' - with patch.dict(svnfs.__opts__, - {'svnfs_saltenv_whitelist': '', - 'svnfs_saltenv_blacklist': 'base'}): - assert not svnfs._env_is_exposed('base') + """ + with patch.dict( + svnfs.__opts__, + {"svnfs_saltenv_whitelist": "", "svnfs_saltenv_blacklist": "base"}, + ): + assert not svnfs._env_is_exposed("base") diff --git a/tests/unit/grains/test_core.py b/tests/unit/grains/test_core.py index 53aaff3cc2f..beee1f3f39a 100644 --- a/tests/unit/grains/test_core.py +++ b/tests/unit/grains/test_core.py @@ -1,15 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Erik Johnson <erik@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os +import platform import socket import textwrap -import platform + +import salt.grains.core as core +import salt.modules.cmdmod +import salt.modules.smbios + +# Import Salt Libs +import salt.utils.dns +import salt.utils.files +import salt.utils.network +import salt.utils.path +import salt.utils.platform +from salt._compat import ipaddress + +# Import 3rd-party libs +from salt.ext import six +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, mock_open, patch +from tests.support.unit import TestCase, skipIf # Import Salt Testing Libs try: @@ -17,178 +36,205 @@ try: except ImportError as import_error: pytest = None -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - Mock, - MagicMock, - patch, - mock_open, -) - -# Import Salt Libs -import salt.utils.dns -import salt.utils.files -import salt.utils.network -import salt.utils.platform -import salt.utils.path -import salt.modules.cmdmod -import salt.modules.smbios -import salt.grains.core as core - -# Import 3rd-party libs -from salt.ext import six -from salt._compat import ipaddress log = logging.getLogger(__name__) # Globals IPv4Address = ipaddress.IPv4Address IPv6Address = ipaddress.IPv6Address -IP4_LOCAL = '127.0.0.1' -IP4_ADD1 = '10.0.0.1' -IP4_ADD2 = '10.0.0.2' -IP6_LOCAL = '::1' -IP6_ADD1 = '2001:4860:4860::8844' -IP6_ADD2 = '2001:4860:4860::8888' -IP6_ADD_SCOPE = 'fe80::6238:e0ff:fe06:3f6b%enp2s0' +IP4_LOCAL = "127.0.0.1" +IP4_ADD1 = "10.0.0.1" +IP4_ADD2 = "10.0.0.2" +IP6_LOCAL = "::1" +IP6_ADD1 = "2001:4860:4860::8844" +IP6_ADD2 = "2001:4860:4860::8888" +IP6_ADD_SCOPE = "fe80::6238:e0ff:fe06:3f6b%enp2s0" OS_RELEASE_DIR = os.path.join(os.path.dirname(__file__), "os-releases") -SOLARIS_DIR = os.path.join(os.path.dirname(__file__), 'solaris') +SOLARIS_DIR = os.path.join(os.path.dirname(__file__), "solaris") @skipIf(not pytest, False) class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for core grains - ''' + """ + def setup_loader_modules(self): return {core: {}} @patch("os.path.isfile") def test_parse_etc_os_release(self, path_isfile_mock): path_isfile_mock.side_effect = lambda x: x == "/usr/lib/os-release" - with salt.utils.files.fopen(os.path.join(OS_RELEASE_DIR, "ubuntu-17.10")) as os_release_file: + with salt.utils.files.fopen( + os.path.join(OS_RELEASE_DIR, "ubuntu-17.10") + ) as os_release_file: os_release_content = os_release_file.read() with patch("salt.utils.files.fopen", mock_open(read_data=os_release_content)): os_release = core._parse_os_release( - '/etc/os-release', - '/usr/lib/os-release') - self.assertEqual(os_release, { - "NAME": "Ubuntu", - "VERSION": "17.10 (Artful Aardvark)", - "ID": "ubuntu", - "ID_LIKE": "debian", - "PRETTY_NAME": "Ubuntu 17.10", - "VERSION_ID": "17.10", - "HOME_URL": "https://www.ubuntu.com/", - "SUPPORT_URL": "https://help.ubuntu.com/", - "BUG_REPORT_URL": "https://bugs.launchpad.net/ubuntu/", - "PRIVACY_POLICY_URL": "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy", - "VERSION_CODENAME": "artful", - "UBUNTU_CODENAME": "artful", - }) + "/etc/os-release", "/usr/lib/os-release" + ) + self.assertEqual( + os_release, + { + "NAME": "Ubuntu", + "VERSION": "17.10 (Artful Aardvark)", + "ID": "ubuntu", + "ID_LIKE": "debian", + "PRETTY_NAME": "Ubuntu 17.10", + "VERSION_ID": "17.10", + "HOME_URL": "https://www.ubuntu.com/", + "SUPPORT_URL": "https://help.ubuntu.com/", + "BUG_REPORT_URL": "https://bugs.launchpad.net/ubuntu/", + "PRIVACY_POLICY_URL": "https://www.ubuntu.com/legal/terms-and-policies/privacy-policy", + "VERSION_CODENAME": "artful", + "UBUNTU_CODENAME": "artful", + }, + ) def test_parse_cpe_name_wfn(self): - ''' + """ Parse correct CPE_NAME data WFN formatted :return: - ''' - for cpe, cpe_ret in [('cpe:/o:opensuse:leap:15.0', - {'phase': None, 'version': '15.0', 'product': 'leap', - 'vendor': 'opensuse', 'part': 'operating system'}), - ('cpe:/o:vendor:product:42:beta', - {'phase': 'beta', 'version': '42', 'product': 'product', - 'vendor': 'vendor', 'part': 'operating system'})]: + """ + for cpe, cpe_ret in [ + ( + "cpe:/o:opensuse:leap:15.0", + { + "phase": None, + "version": "15.0", + "product": "leap", + "vendor": "opensuse", + "part": "operating system", + }, + ), + ( + "cpe:/o:vendor:product:42:beta", + { + "phase": "beta", + "version": "42", + "product": "product", + "vendor": "vendor", + "part": "operating system", + }, + ), + ]: ret = core._parse_cpe_name(cpe) for key in cpe_ret: assert key in ret assert cpe_ret[key] == ret[key] def test_parse_cpe_name_v23(self): - ''' + """ Parse correct CPE_NAME data v2.3 formatted :return: - ''' - for cpe, cpe_ret in [('cpe:2.3:o:microsoft:windows_xp:5.1.601:beta:*:*:*:*:*:*', - {'phase': 'beta', 'version': '5.1.601', 'product': 'windows_xp', - 'vendor': 'microsoft', 'part': 'operating system'}), - ('cpe:2.3:h:corellian:millenium_falcon:1.0:*:*:*:*:*:*:*', - {'phase': None, 'version': '1.0', 'product': 'millenium_falcon', - 'vendor': 'corellian', 'part': 'hardware'}), - ('cpe:2.3:*:dark_empire:light_saber:3.0:beta:*:*:*:*:*:*', - {'phase': 'beta', 'version': '3.0', 'product': 'light_saber', - 'vendor': 'dark_empire', 'part': None})]: + """ + for cpe, cpe_ret in [ + ( + "cpe:2.3:o:microsoft:windows_xp:5.1.601:beta:*:*:*:*:*:*", + { + "phase": "beta", + "version": "5.1.601", + "product": "windows_xp", + "vendor": "microsoft", + "part": "operating system", + }, + ), + ( + "cpe:2.3:h:corellian:millenium_falcon:1.0:*:*:*:*:*:*:*", + { + "phase": None, + "version": "1.0", + "product": "millenium_falcon", + "vendor": "corellian", + "part": "hardware", + }, + ), + ( + "cpe:2.3:*:dark_empire:light_saber:3.0:beta:*:*:*:*:*:*", + { + "phase": "beta", + "version": "3.0", + "product": "light_saber", + "vendor": "dark_empire", + "part": None, + }, + ), + ]: ret = core._parse_cpe_name(cpe) for key in cpe_ret: assert key in ret assert cpe_ret[key] == ret[key] def test_parse_cpe_name_broken(self): - ''' + """ Parse broken CPE_NAME data :return: - ''' - for cpe in ['cpe:broken', 'cpe:broken:in:all:ways:*:*:*:*', - 'cpe:x:still:broken:123', 'who:/knows:what:is:here']: + """ + for cpe in [ + "cpe:broken", + "cpe:broken:in:all:ways:*:*:*:*", + "cpe:x:still:broken:123", + "who:/knows:what:is:here", + ]: assert core._parse_cpe_name(cpe) == {} def test_missing_os_release(self): - with patch('salt.utils.files.fopen', mock_open(read_data={})): - os_release = core._parse_os_release('/etc/os-release', '/usr/lib/os-release') + with patch("salt.utils.files.fopen", mock_open(read_data={})): + os_release = core._parse_os_release( + "/etc/os-release", "/usr/lib/os-release" + ) self.assertEqual(os_release, {}) - @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + @skipIf(not salt.utils.platform.is_windows(), "System is not Windows") def test__windows_platform_data(self): grains = core._windows_platform_data() - keys = ['biosversion', - 'osrelease', - 'kernelrelease', - 'motherboard', - 'serialnumber', - 'timezone', - 'manufacturer', - 'kernelversion', - 'osservicepack', - 'virtual', - 'productname', - 'osfullname', - 'osmanufacturer', - 'osversion', - 'windowsdomain'] + keys = [ + "biosversion", + "osrelease", + "kernelrelease", + "motherboard", + "serialnumber", + "timezone", + "manufacturer", + "kernelversion", + "osservicepack", + "virtual", + "productname", + "osfullname", + "osmanufacturer", + "osversion", + "windowsdomain", + ] for key in keys: self.assertIn(key, grains) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_gnu_slash_linux_in_os_name(self): - ''' + """ Test to return a list of all enabled services - ''' - _path_exists_map = { - '/proc/1/cmdline': False - } + """ + _path_exists_map = {"/proc/1/cmdline": False} _path_isfile_map = {} _cmd_run_map = { - 'dpkg --print-architecture': 'amd64', + "dpkg --print-architecture": "amd64", } path_exists_mock = MagicMock(side_effect=lambda x: _path_exists_map[x]) path_isfile_mock = MagicMock( side_effect=lambda x: _path_isfile_map.get(x, False) ) - cmd_run_mock = MagicMock( - side_effect=lambda x: _cmd_run_map[x] - ) + cmd_run_mock = MagicMock(side_effect=lambda x: _cmd_run_map[x]) empty_mock = MagicMock(return_value={}) orig_import = __import__ if six.PY2: - built_in = '__builtin__' + built_in = "__builtin__" else: - built_in = 'builtins' + built_in = "builtins" def _import_mock(name, *args): - if name == 'lsb_release': - raise ImportError('No module named lsb_release') + if name == "lsb_release": + raise ImportError("No module named lsb_release") return orig_import(name, *args) # - Skip the first if statement @@ -200,45 +246,58 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): # - Make a bunch of functions return empty dicts, we don't care about # these grains for the purposes of this test. # - Mock the osarch - distro_mock = MagicMock(return_value=('Debian GNU/Linux', '8.3', '')) - with patch.object(salt.utils.platform, 'is_proxy', - MagicMock(return_value=False)), \ - patch.object(core, '_linux_bin_exists', - MagicMock(return_value=False)), \ - patch.object(os.path, 'exists', path_exists_mock), \ - patch('{0}.__import__'.format(built_in), side_effect=_import_mock), \ - patch.object(os.path, 'isfile', path_isfile_mock), \ - patch.object(core, '_parse_lsb_release', empty_mock), \ - patch.object(core, '_parse_os_release', empty_mock), \ - patch.object(core, '_parse_lsb_release', empty_mock), \ - patch.object(core, 'linux_distribution', distro_mock), \ - patch.object(core, '_linux_cpudata', empty_mock), \ - patch.object(core, '_linux_gpu_data', empty_mock), \ - patch.object(core, '_memdata', empty_mock), \ - patch.object(core, '_hw_data', empty_mock), \ - patch.object(core, '_virtual', empty_mock), \ - patch.object(core, '_ps', empty_mock), \ - patch.dict(core.__salt__, {'cmd.run': cmd_run_mock}): + distro_mock = MagicMock(return_value=("Debian GNU/Linux", "8.3", "")) + with patch.object( + salt.utils.platform, "is_proxy", MagicMock(return_value=False) + ), patch.object( + core, "_linux_bin_exists", MagicMock(return_value=False) + ), patch.object( + os.path, "exists", path_exists_mock + ), patch( + "{0}.__import__".format(built_in), side_effect=_import_mock + ), patch.object( + os.path, "isfile", path_isfile_mock + ), patch.object( + core, "_parse_lsb_release", empty_mock + ), patch.object( + core, "_parse_os_release", empty_mock + ), patch.object( + core, "_parse_lsb_release", empty_mock + ), patch.object( + core, "linux_distribution", distro_mock + ), patch.object( + core, "_linux_cpudata", empty_mock + ), patch.object( + core, "_linux_gpu_data", empty_mock + ), patch.object( + core, "_memdata", empty_mock + ), patch.object( + core, "_hw_data", empty_mock + ), patch.object( + core, "_virtual", empty_mock + ), patch.object( + core, "_ps", empty_mock + ), patch.dict( + core.__salt__, {"cmd.run": cmd_run_mock} + ): os_grains = core.os_data() - self.assertEqual(os_grains.get('os_family'), 'Debian') + self.assertEqual(os_grains.get("os_family"), "Debian") - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_suse_os_from_cpe_data(self): - ''' + """ Test if 'os' grain is parsed from CPE_NAME of /etc/os-release - ''' - _path_exists_map = { - '/proc/1/cmdline': False - } + """ + _path_exists_map = {"/proc/1/cmdline": False} _os_release_map = { - 'NAME': 'SLES', - 'VERSION': '12-SP1', - 'VERSION_ID': '12.1', - 'PRETTY_NAME': 'SUSE Linux Enterprise Server 12 SP1', - 'ID': 'sles', - 'ANSI_COLOR': '0;32', - 'CPE_NAME': 'cpe:/o:suse:sles:12:sp1' + "NAME": "SLES", + "VERSION": "12-SP1", + "VERSION_ID": "12.1", + "PRETTY_NAME": "SUSE Linux Enterprise Server 12 SP1", + "ID": "sles", + "ANSI_COLOR": "0;32", + "CPE_NAME": "cpe:/o:suse:sles:12:sp1", } path_exists_mock = MagicMock(side_effect=lambda x: _path_exists_map[x]) @@ -248,17 +307,17 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): orig_import = __import__ if six.PY2: - built_in = '__builtin__' + built_in = "__builtin__" else: - built_in = 'builtins' + built_in = "builtins" def _import_mock(name, *args): - if name == 'lsb_release': - raise ImportError('No module named lsb_release') + if name == "lsb_release": + raise ImportError("No module named lsb_release") return orig_import(name, *args) distro_mock = MagicMock( - return_value=('SUSE Linux Enterprise Server ', '12', 'x86_64') + return_value=("SUSE Linux Enterprise Server ", "12", "x86_64") ) # - Skip the first if statement @@ -268,29 +327,42 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): # - Skip all the /etc/*-release stuff (not pertinent) # - Mock linux_distribution to give us the OS name that we want # - Mock the osarch - with patch.object(salt.utils.platform, 'is_proxy', - MagicMock(return_value=False)), \ - patch.object(core, '_linux_bin_exists', - MagicMock(return_value=False)), \ - patch.object(os.path, 'exists', path_exists_mock), \ - patch('{0}.__import__'.format(built_in), - side_effect=_import_mock), \ - patch.object(os.path, 'isfile', MagicMock(return_value=False)), \ - patch.object(core, '_parse_os_release', os_release_mock), \ - patch.object(core, '_parse_lsb_release', empty_mock), \ - patch.object(core, 'linux_distribution', distro_mock), \ - patch.object(core, '_linux_gpu_data', empty_mock), \ - patch.object(core, '_hw_data', empty_mock), \ - patch.object(core, '_linux_cpudata', empty_mock), \ - patch.object(core, '_virtual', empty_mock), \ - patch.dict(core.__salt__, {'cmd.run': osarch_mock}): + with patch.object( + salt.utils.platform, "is_proxy", MagicMock(return_value=False) + ), patch.object( + core, "_linux_bin_exists", MagicMock(return_value=False) + ), patch.object( + os.path, "exists", path_exists_mock + ), patch( + "{0}.__import__".format(built_in), side_effect=_import_mock + ), patch.object( + os.path, "isfile", MagicMock(return_value=False) + ), patch.object( + core, "_parse_os_release", os_release_mock + ), patch.object( + core, "_parse_lsb_release", empty_mock + ), patch.object( + core, "linux_distribution", distro_mock + ), patch.object( + core, "_linux_gpu_data", empty_mock + ), patch.object( + core, "_hw_data", empty_mock + ), patch.object( + core, "_linux_cpudata", empty_mock + ), patch.object( + core, "_virtual", empty_mock + ), patch.dict( + core.__salt__, {"cmd.run": osarch_mock} + ): os_grains = core.os_data() - self.assertEqual(os_grains.get('os_family'), 'Suse') - self.assertEqual(os_grains.get('os'), 'SUSE') + self.assertEqual(os_grains.get("os_family"), "Suse") + self.assertEqual(os_grains.get("os"), "SUSE") def _run_os_grains_tests(self, os_release_filename, os_release_map, expectation): - path_isfile_mock = MagicMock(side_effect=lambda x: x in os_release_map.get('files', [])) + path_isfile_mock = MagicMock( + side_effect=lambda x: x in os_release_map.get("files", []) + ) empty_mock = MagicMock(return_value={}) osarch_mock = MagicMock(return_value="amd64") if os_release_filename: @@ -298,25 +370,25 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): os.path.join(OS_RELEASE_DIR, os_release_filename) ) else: - os_release_data = os_release_map.get('os_release_file', {}) + os_release_data = os_release_map.get("os_release_file", {}) os_release_mock = MagicMock(return_value=os_release_data) orig_import = __import__ if six.PY2: - built_in = '__builtin__' + built_in = "__builtin__" else: - built_in = 'builtins' + built_in = "builtins" def _import_mock(name, *args): - if name == 'lsb_release': - raise ImportError('No module named lsb_release') + if name == "lsb_release": + raise ImportError("No module named lsb_release") return orig_import(name, *args) - suse_release_file = os_release_map.get('suse_release_file') + suse_release_file = os_release_map.get("suse_release_file") - file_contents = {'/proc/1/cmdline': ''} + file_contents = {"/proc/1/cmdline": ""} if suse_release_file: - file_contents['/etc/SuSE-release'] = suse_release_file + file_contents["/etc/SuSE-release"] = suse_release_file # - Skip the first if statement # - Skip the selinux/systemd stuff (not pertinent) @@ -325,949 +397,1093 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): # - Skip all the /etc/*-release stuff (not pertinent) # - Mock linux_distribution to give us the OS name that we want # - Mock the osarch - distro_mock = MagicMock(return_value=os_release_map['linux_distribution']) - with patch.object(salt.utils.platform, 'is_proxy', MagicMock(return_value=False)), \ - patch.object(core, '_linux_bin_exists', MagicMock(return_value=False)), \ - patch.object(os.path, 'exists', path_isfile_mock), \ - patch('{0}.__import__'.format(built_in), side_effect=_import_mock), \ - patch.object(os.path, 'isfile', path_isfile_mock), \ - patch.object(core, '_parse_os_release', os_release_mock), \ - patch.object(core, '_parse_lsb_release', empty_mock), \ - patch('salt.utils.files.fopen', mock_open(read_data=file_contents)), \ - patch.object(core, 'linux_distribution', distro_mock), \ - patch.object(core, '_linux_gpu_data', empty_mock), \ - patch.object(core, '_linux_cpudata', empty_mock), \ - patch.object(core, '_virtual', empty_mock), \ - patch.dict(core.__salt__, {'cmd.run': osarch_mock}): + distro_mock = MagicMock(return_value=os_release_map["linux_distribution"]) + with patch.object( + salt.utils.platform, "is_proxy", MagicMock(return_value=False) + ), patch.object( + core, "_linux_bin_exists", MagicMock(return_value=False) + ), patch.object( + os.path, "exists", path_isfile_mock + ), patch( + "{0}.__import__".format(built_in), side_effect=_import_mock + ), patch.object( + os.path, "isfile", path_isfile_mock + ), patch.object( + core, "_parse_os_release", os_release_mock + ), patch.object( + core, "_parse_lsb_release", empty_mock + ), patch( + "salt.utils.files.fopen", mock_open(read_data=file_contents) + ), patch.object( + core, "linux_distribution", distro_mock + ), patch.object( + core, "_linux_gpu_data", empty_mock + ), patch.object( + core, "_linux_cpudata", empty_mock + ), patch.object( + core, "_virtual", empty_mock + ), patch.dict( + core.__salt__, {"cmd.run": osarch_mock} + ): os_grains = core.os_data() - grains = {k: v for k, v in os_grains.items() - if k in set(["os", "os_family", "osfullname", "oscodename", "osfinger", - "osrelease", "osrelease_info", "osmajorrelease"])} + grains = { + k: v + for k, v in os_grains.items() + if k + in set( + [ + "os", + "os_family", + "osfullname", + "oscodename", + "osfinger", + "osrelease", + "osrelease_info", + "osmajorrelease", + ] + ) + } self.assertEqual(grains, expectation) def _run_suse_os_grains_tests(self, os_release_map, expectation): - os_release_map['linux_distribution'] = ('SUSE test', 'version', 'arch') - expectation['os'] = 'SUSE' - expectation['os_family'] = 'Suse' + os_release_map["linux_distribution"] = ("SUSE test", "version", "arch") + expectation["os"] = "SUSE" + expectation["os_family"] = "Suse" self._run_os_grains_tests(None, os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_suse_os_grains_sles11sp3(self): - ''' + """ Test if OS grains are parsed correctly in SLES 11 SP3 - ''' + """ _os_release_map = { - 'suse_release_file': textwrap.dedent(''' + "suse_release_file": textwrap.dedent( + """ SUSE Linux Enterprise Server 11 (x86_64) VERSION = 11 PATCHLEVEL = 3 - '''), - 'files': ["/etc/SuSE-release"], + """ + ), + "files": ["/etc/SuSE-release"], } expectation = { - 'oscodename': 'SUSE Linux Enterprise Server 11 SP3', - 'osfullname': "SLES", - 'osrelease': '11.3', - 'osrelease_info': (11, 3), - 'osmajorrelease': 11, - 'osfinger': 'SLES-11', + "oscodename": "SUSE Linux Enterprise Server 11 SP3", + "osfullname": "SLES", + "osrelease": "11.3", + "osrelease_info": (11, 3), + "osmajorrelease": 11, + "osfinger": "SLES-11", } self._run_suse_os_grains_tests(_os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_suse_os_grains_sles11sp4(self): - ''' + """ Test if OS grains are parsed correctly in SLES 11 SP4 - ''' + """ _os_release_map = { - 'os_release_file': { - 'NAME': 'SLES', - 'VERSION': '11.4', - 'VERSION_ID': '11.4', - 'PRETTY_NAME': 'SUSE Linux Enterprise Server 11 SP4', - 'ID': 'sles', - 'ANSI_COLOR': '0;32', - 'CPE_NAME': 'cpe:/o:suse:sles:11:4' + "os_release_file": { + "NAME": "SLES", + "VERSION": "11.4", + "VERSION_ID": "11.4", + "PRETTY_NAME": "SUSE Linux Enterprise Server 11 SP4", + "ID": "sles", + "ANSI_COLOR": "0;32", + "CPE_NAME": "cpe:/o:suse:sles:11:4", }, } expectation = { - 'oscodename': 'SUSE Linux Enterprise Server 11 SP4', - 'osfullname': "SLES", - 'osrelease': '11.4', - 'osrelease_info': (11, 4), - 'osmajorrelease': 11, - 'osfinger': 'SLES-11', + "oscodename": "SUSE Linux Enterprise Server 11 SP4", + "osfullname": "SLES", + "osrelease": "11.4", + "osrelease_info": (11, 4), + "osmajorrelease": 11, + "osfinger": "SLES-11", } self._run_suse_os_grains_tests(_os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_suse_os_grains_sles12(self): - ''' + """ Test if OS grains are parsed correctly in SLES 12 - ''' + """ _os_release_map = { - 'os_release_file': { - 'NAME': 'SLES', - 'VERSION': '12', - 'VERSION_ID': '12', - 'PRETTY_NAME': 'SUSE Linux Enterprise Server 12', - 'ID': 'sles', - 'ANSI_COLOR': '0;32', - 'CPE_NAME': 'cpe:/o:suse:sles:12' + "os_release_file": { + "NAME": "SLES", + "VERSION": "12", + "VERSION_ID": "12", + "PRETTY_NAME": "SUSE Linux Enterprise Server 12", + "ID": "sles", + "ANSI_COLOR": "0;32", + "CPE_NAME": "cpe:/o:suse:sles:12", }, } expectation = { - 'oscodename': 'SUSE Linux Enterprise Server 12', - 'osfullname': "SLES", - 'osrelease': '12', - 'osrelease_info': (12,), - 'osmajorrelease': 12, - 'osfinger': 'SLES-12', + "oscodename": "SUSE Linux Enterprise Server 12", + "osfullname": "SLES", + "osrelease": "12", + "osrelease_info": (12,), + "osmajorrelease": 12, + "osfinger": "SLES-12", } self._run_suse_os_grains_tests(_os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_suse_os_grains_sles12sp1(self): - ''' + """ Test if OS grains are parsed correctly in SLES 12 SP1 - ''' + """ _os_release_map = { - 'os_release_file': { - 'NAME': 'SLES', - 'VERSION': '12-SP1', - 'VERSION_ID': '12.1', - 'PRETTY_NAME': 'SUSE Linux Enterprise Server 12 SP1', - 'ID': 'sles', - 'ANSI_COLOR': '0;32', - 'CPE_NAME': 'cpe:/o:suse:sles:12:sp1' + "os_release_file": { + "NAME": "SLES", + "VERSION": "12-SP1", + "VERSION_ID": "12.1", + "PRETTY_NAME": "SUSE Linux Enterprise Server 12 SP1", + "ID": "sles", + "ANSI_COLOR": "0;32", + "CPE_NAME": "cpe:/o:suse:sles:12:sp1", }, } expectation = { - 'oscodename': 'SUSE Linux Enterprise Server 12 SP1', - 'osfullname': "SLES", - 'osrelease': '12.1', - 'osrelease_info': (12, 1), - 'osmajorrelease': 12, - 'osfinger': 'SLES-12', + "oscodename": "SUSE Linux Enterprise Server 12 SP1", + "osfullname": "SLES", + "osrelease": "12.1", + "osrelease_info": (12, 1), + "osmajorrelease": 12, + "osfinger": "SLES-12", } self._run_suse_os_grains_tests(_os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_suse_os_grains_opensuse_leap_42_1(self): - ''' + """ Test if OS grains are parsed correctly in openSUSE Leap 42.1 - ''' + """ _os_release_map = { - 'os_release_file': { - 'NAME': 'openSUSE Leap', - 'VERSION': '42.1', - 'VERSION_ID': '42.1', - 'PRETTY_NAME': 'openSUSE Leap 42.1 (x86_64)', - 'ID': 'opensuse', - 'ANSI_COLOR': '0;32', - 'CPE_NAME': 'cpe:/o:opensuse:opensuse:42.1' + "os_release_file": { + "NAME": "openSUSE Leap", + "VERSION": "42.1", + "VERSION_ID": "42.1", + "PRETTY_NAME": "openSUSE Leap 42.1 (x86_64)", + "ID": "opensuse", + "ANSI_COLOR": "0;32", + "CPE_NAME": "cpe:/o:opensuse:opensuse:42.1", }, } expectation = { - 'oscodename': 'openSUSE Leap 42.1 (x86_64)', - 'osfullname': "Leap", - 'osrelease': '42.1', - 'osrelease_info': (42, 1), - 'osmajorrelease': 42, - 'osfinger': 'Leap-42', + "oscodename": "openSUSE Leap 42.1 (x86_64)", + "osfullname": "Leap", + "osrelease": "42.1", + "osrelease_info": (42, 1), + "osmajorrelease": 42, + "osfinger": "Leap-42", } self._run_suse_os_grains_tests(_os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_suse_os_grains_tumbleweed(self): - ''' + """ Test if OS grains are parsed correctly in openSUSE Tumbleweed - ''' + """ _os_release_map = { - 'os_release_file': { - 'NAME': 'openSUSE', - 'VERSION': 'Tumbleweed', - 'VERSION_ID': '20160504', - 'PRETTY_NAME': 'openSUSE Tumbleweed (20160504) (x86_64)', - 'ID': 'opensuse', - 'ANSI_COLOR': '0;32', - 'CPE_NAME': 'cpe:/o:opensuse:opensuse:20160504' + "os_release_file": { + "NAME": "openSUSE", + "VERSION": "Tumbleweed", + "VERSION_ID": "20160504", + "PRETTY_NAME": "openSUSE Tumbleweed (20160504) (x86_64)", + "ID": "opensuse", + "ANSI_COLOR": "0;32", + "CPE_NAME": "cpe:/o:opensuse:opensuse:20160504", }, } expectation = { - 'oscodename': 'openSUSE Tumbleweed (20160504) (x86_64)', - 'osfullname': "Tumbleweed", - 'osrelease': '20160504', - 'osrelease_info': (20160504,), - 'osmajorrelease': 20160504, - 'osfinger': 'Tumbleweed-20160504', + "oscodename": "openSUSE Tumbleweed (20160504) (x86_64)", + "osfullname": "Tumbleweed", + "osrelease": "20160504", + "osrelease_info": (20160504,), + "osmajorrelease": 20160504, + "osfinger": "Tumbleweed-20160504", } self._run_suse_os_grains_tests(_os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_debian_7_os_grains(self): - ''' + """ Test if OS grains are parsed correctly in Debian 7 "wheezy" - ''' + """ _os_release_map = { - 'linux_distribution': ('debian', '7.11', ''), + "linux_distribution": ("debian", "7.11", ""), } expectation = { - 'os': 'Debian', - 'os_family': 'Debian', - 'oscodename': 'wheezy', - 'osfullname': 'Debian GNU/Linux', - 'osrelease': '7', - 'osrelease_info': (7,), - 'osmajorrelease': 7, - 'osfinger': 'Debian-7', + "os": "Debian", + "os_family": "Debian", + "oscodename": "wheezy", + "osfullname": "Debian GNU/Linux", + "osrelease": "7", + "osrelease_info": (7,), + "osmajorrelease": 7, + "osfinger": "Debian-7", } self._run_os_grains_tests("debian-7", _os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_debian_8_os_grains(self): - ''' + """ Test if OS grains are parsed correctly in Debian 8 "jessie" - ''' + """ _os_release_map = { - 'linux_distribution': ('debian', '8.10', ''), + "linux_distribution": ("debian", "8.10", ""), } expectation = { - 'os': 'Debian', - 'os_family': 'Debian', - 'oscodename': 'jessie', - 'osfullname': 'Debian GNU/Linux', - 'osrelease': '8', - 'osrelease_info': (8,), - 'osmajorrelease': 8, - 'osfinger': 'Debian-8', + "os": "Debian", + "os_family": "Debian", + "oscodename": "jessie", + "osfullname": "Debian GNU/Linux", + "osrelease": "8", + "osrelease_info": (8,), + "osmajorrelease": 8, + "osfinger": "Debian-8", } self._run_os_grains_tests("debian-8", _os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_debian_9_os_grains(self): - ''' + """ Test if OS grains are parsed correctly in Debian 9 "stretch" - ''' + """ _os_release_map = { - 'linux_distribution': ('debian', '9.3', ''), + "linux_distribution": ("debian", "9.3", ""), } expectation = { - 'os': 'Debian', - 'os_family': 'Debian', - 'oscodename': 'stretch', - 'osfullname': 'Debian GNU/Linux', - 'osrelease': '9', - 'osrelease_info': (9,), - 'osmajorrelease': 9, - 'osfinger': 'Debian-9', + "os": "Debian", + "os_family": "Debian", + "oscodename": "stretch", + "osfullname": "Debian GNU/Linux", + "osrelease": "9", + "osrelease_info": (9,), + "osmajorrelease": 9, + "osfinger": "Debian-9", } self._run_os_grains_tests("debian-9", _os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_ubuntu_xenial_os_grains(self): - ''' + """ Test if OS grains are parsed correctly in Ubuntu 16.04 "Xenial Xerus" - ''' + """ _os_release_map = { - 'linux_distribution': ('Ubuntu', '16.04', 'xenial'), + "linux_distribution": ("Ubuntu", "16.04", "xenial"), } expectation = { - 'os': 'Ubuntu', - 'os_family': 'Debian', - 'oscodename': 'xenial', - 'osfullname': 'Ubuntu', - 'osrelease': '16.04', - 'osrelease_info': (16, 4), - 'osmajorrelease': 16, - 'osfinger': 'Ubuntu-16.04', + "os": "Ubuntu", + "os_family": "Debian", + "oscodename": "xenial", + "osfullname": "Ubuntu", + "osrelease": "16.04", + "osrelease_info": (16, 4), + "osmajorrelease": 16, + "osfinger": "Ubuntu-16.04", } self._run_os_grains_tests("ubuntu-16.04", _os_release_map, expectation) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_ubuntu_artful_os_grains(self): - ''' + """ Test if OS grains are parsed correctly in Ubuntu 17.10 "Artful Aardvark" - ''' + """ _os_release_map = { - 'linux_distribution': ('Ubuntu', '17.10', 'artful'), + "linux_distribution": ("Ubuntu", "17.10", "artful"), } expectation = { - 'os': 'Ubuntu', - 'os_family': 'Debian', - 'oscodename': 'artful', - 'osfullname': 'Ubuntu', - 'osrelease': '17.10', - 'osrelease_info': (17, 10), - 'osmajorrelease': 17, - 'osfinger': 'Ubuntu-17.10', + "os": "Ubuntu", + "os_family": "Debian", + "oscodename": "artful", + "osfullname": "Ubuntu", + "osrelease": "17.10", + "osrelease_info": (17, 10), + "osmajorrelease": 17, + "osfinger": "Ubuntu-17.10", } self._run_os_grains_tests("ubuntu-17.10", _os_release_map, expectation) - @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + @skipIf(not salt.utils.platform.is_windows(), "System is not Windows") def test_windows_platform_data(self): - ''' + """ Test the _windows_platform_data function - ''' - grains = ['biosversion', 'kernelrelease', 'kernelversion', - 'manufacturer', 'motherboard', 'osfullname', 'osmanufacturer', - 'osrelease', 'osservicepack', 'osversion', 'productname', - 'serialnumber', 'timezone', 'virtual', 'windowsdomain', - 'windowsdomaintype'] + """ + grains = [ + "biosversion", + "kernelrelease", + "kernelversion", + "manufacturer", + "motherboard", + "osfullname", + "osmanufacturer", + "osrelease", + "osservicepack", + "osversion", + "productname", + "serialnumber", + "timezone", + "virtual", + "windowsdomain", + "windowsdomaintype", + ] returned_grains = core._windows_platform_data() for grain in grains: self.assertIn(grain, returned_grains) - valid_types = ['Unknown', 'Unjoined', 'Workgroup', 'Domain'] - self.assertIn(returned_grains['windowsdomaintype'], valid_types) - valid_releases = ['Vista', '7', '8', '8.1', '10', '2008Server', - '2008ServerR2', '2012Server', '2012ServerR2', - '2016Server', '2019Server'] - self.assertIn(returned_grains['osrelease'], valid_releases) + valid_types = ["Unknown", "Unjoined", "Workgroup", "Domain"] + self.assertIn(returned_grains["windowsdomaintype"], valid_types) + valid_releases = [ + "Vista", + "7", + "8", + "8.1", + "10", + "2008Server", + "2008ServerR2", + "2012Server", + "2012ServerR2", + "2016Server", + "2019Server", + ] + self.assertIn(returned_grains["osrelease"], valid_releases) def test__windows_os_release_grain(self): versions = { - 'Windows 10 Home': '10', - 'Windows 10 Pro': '10', - 'Windows 10 Pro for Workstations': '10', - 'Windows 10 Pro Education': '10', - 'Windows 10 Enterprise': '10', - 'Windows 10 Enterprise LTSB': '10', - 'Windows 10 Education': '10', - 'Windows 10 IoT Core': '10', - 'Windows 10 IoT Enterprise': '10', - 'Windows 10 S': '10', - 'Windows 8.1': '8.1', - 'Windows 8.1 Pro': '8.1', - 'Windows 8.1 Enterprise': '8.1', - 'Windows 8.1 OEM': '8.1', - 'Windows 8.1 with Bing': '8.1', - 'Windows 8': '8', - 'Windows 8 Pro': '8', - 'Windows 8 Enterprise': '8', - 'Windows 8 OEM': '8', - 'Windows 7 Starter': '7', - 'Windows 7 Home Basic': '7', - 'Windows 7 Home Premium': '7', - 'Windows 7 Professional': '7', - 'Windows 7 Enterprise': '7', - 'Windows 7 Ultimate': '7', - 'Windows Thin PC': 'Thin', - 'Windows Vista Starter': 'Vista', - 'Windows Vista Home Basic': 'Vista', - 'Windows Vista Home Premium': 'Vista', - 'Windows Vista Business': 'Vista', - 'Windows Vista Enterprise': 'Vista', - 'Windows Vista Ultimate': 'Vista', - 'Windows Server 2019 Essentials': '2019Server', - 'Windows Server 2019 Standard': '2019Server', - 'Windows Server 2019 Datacenter': '2019Server', - 'Windows Server 2016 Essentials': '2016Server', - 'Windows Server 2016 Standard': '2016Server', - 'Windows Server 2016 Datacenter': '2016Server', - 'Windows Server 2012 R2 Foundation': '2012ServerR2', - 'Windows Server 2012 R2 Essentials': '2012ServerR2', - 'Windows Server 2012 R2 Standard': '2012ServerR2', - 'Windows Server 2012 R2 Datacenter': '2012ServerR2', - 'Windows Server 2012 Foundation': '2012Server', - 'Windows Server 2012 Essentials': '2012Server', - 'Windows Server 2012 Standard': '2012Server', - 'Windows Server 2012 Datacenter': '2012Server', - 'Windows MultiPoint Server 2012': '2012Server', - 'Windows Small Business Server 2011': '2011Server', - 'Windows MultiPoint Server 2011': '2011Server', - 'Windows Home Server 2011': '2011Server', - 'Windows MultiPoint Server 2010': '2010Server', - 'Windows Server 2008 R2 Foundation': '2008ServerR2', - 'Windows Server 2008 R2 Standard': '2008ServerR2', - 'Windows Server 2008 R2 Enterprise': '2008ServerR2', - 'Windows Server 2008 R2 Datacenter': '2008ServerR2', - 'Windows Server 2008 R2 for Itanium-based Systems': '2008ServerR2', - 'Windows Web Server 2008 R2': '2008ServerR2', - 'Windows Storage Server 2008 R2': '2008ServerR2', - 'Windows HPC Server 2008 R2': '2008ServerR2', - 'Windows Server 2008 Standard': '2008Server', - 'Windows Server 2008 Enterprise': '2008Server', - 'Windows Server 2008 Datacenter': '2008Server', - 'Windows Server 2008 for Itanium-based Systems': '2008Server', - 'Windows Server Foundation 2008': '2008Server', - 'Windows Essential Business Server 2008': '2008Server', - 'Windows HPC Server 2008': '2008Server', - 'Windows Small Business Server 2008': '2008Server', - 'Windows Storage Server 2008': '2008Server', - 'Windows Web Server 2008': '2008Server' + "Windows 10 Home": "10", + "Windows 10 Pro": "10", + "Windows 10 Pro for Workstations": "10", + "Windows 10 Pro Education": "10", + "Windows 10 Enterprise": "10", + "Windows 10 Enterprise LTSB": "10", + "Windows 10 Education": "10", + "Windows 10 IoT Core": "10", + "Windows 10 IoT Enterprise": "10", + "Windows 10 S": "10", + "Windows 8.1": "8.1", + "Windows 8.1 Pro": "8.1", + "Windows 8.1 Enterprise": "8.1", + "Windows 8.1 OEM": "8.1", + "Windows 8.1 with Bing": "8.1", + "Windows 8": "8", + "Windows 8 Pro": "8", + "Windows 8 Enterprise": "8", + "Windows 8 OEM": "8", + "Windows 7 Starter": "7", + "Windows 7 Home Basic": "7", + "Windows 7 Home Premium": "7", + "Windows 7 Professional": "7", + "Windows 7 Enterprise": "7", + "Windows 7 Ultimate": "7", + "Windows Thin PC": "Thin", + "Windows Vista Starter": "Vista", + "Windows Vista Home Basic": "Vista", + "Windows Vista Home Premium": "Vista", + "Windows Vista Business": "Vista", + "Windows Vista Enterprise": "Vista", + "Windows Vista Ultimate": "Vista", + "Windows Server 2019 Essentials": "2019Server", + "Windows Server 2019 Standard": "2019Server", + "Windows Server 2019 Datacenter": "2019Server", + "Windows Server 2016 Essentials": "2016Server", + "Windows Server 2016 Standard": "2016Server", + "Windows Server 2016 Datacenter": "2016Server", + "Windows Server 2012 R2 Foundation": "2012ServerR2", + "Windows Server 2012 R2 Essentials": "2012ServerR2", + "Windows Server 2012 R2 Standard": "2012ServerR2", + "Windows Server 2012 R2 Datacenter": "2012ServerR2", + "Windows Server 2012 Foundation": "2012Server", + "Windows Server 2012 Essentials": "2012Server", + "Windows Server 2012 Standard": "2012Server", + "Windows Server 2012 Datacenter": "2012Server", + "Windows MultiPoint Server 2012": "2012Server", + "Windows Small Business Server 2011": "2011Server", + "Windows MultiPoint Server 2011": "2011Server", + "Windows Home Server 2011": "2011Server", + "Windows MultiPoint Server 2010": "2010Server", + "Windows Server 2008 R2 Foundation": "2008ServerR2", + "Windows Server 2008 R2 Standard": "2008ServerR2", + "Windows Server 2008 R2 Enterprise": "2008ServerR2", + "Windows Server 2008 R2 Datacenter": "2008ServerR2", + "Windows Server 2008 R2 for Itanium-based Systems": "2008ServerR2", + "Windows Web Server 2008 R2": "2008ServerR2", + "Windows Storage Server 2008 R2": "2008ServerR2", + "Windows HPC Server 2008 R2": "2008ServerR2", + "Windows Server 2008 Standard": "2008Server", + "Windows Server 2008 Enterprise": "2008Server", + "Windows Server 2008 Datacenter": "2008Server", + "Windows Server 2008 for Itanium-based Systems": "2008Server", + "Windows Server Foundation 2008": "2008Server", + "Windows Essential Business Server 2008": "2008Server", + "Windows HPC Server 2008": "2008Server", + "Windows Small Business Server 2008": "2008Server", + "Windows Storage Server 2008": "2008Server", + "Windows Web Server 2008": "2008Server", } for caption in versions: version = core._windows_os_release_grain(caption, 1) self.assertEqual( version, versions[caption], - 'version: {0}\n' - 'found: {1}\n' - 'caption: {2}'.format(version, versions[caption], caption) + "version: {0}\n" + "found: {1}\n" + "caption: {2}".format(version, versions[caption], caption), ) embedded_versions = { - 'Windows Embedded 8.1 Industry Pro': '8.1', - 'Windows Embedded 8 Industry Pro': '8', - 'Windows POSReady 7': '7', - 'Windows Embedded Standard 7': '7', - 'Windows Embedded POSReady 2009': '2009', - 'Windows Embedded Standard 2009': '2009', - 'Windows XP Embedded': 'XP', + "Windows Embedded 8.1 Industry Pro": "8.1", + "Windows Embedded 8 Industry Pro": "8", + "Windows POSReady 7": "7", + "Windows Embedded Standard 7": "7", + "Windows Embedded POSReady 2009": "2009", + "Windows Embedded Standard 2009": "2009", + "Windows XP Embedded": "XP", } for caption in embedded_versions: version = core._windows_os_release_grain(caption, 1) self.assertEqual( version, embedded_versions[caption], - '{0} != {1}\n' - 'version: {0}\n' - 'found: {1}\n' - 'caption: {2}'.format(version, embedded_versions[caption], caption) + "{0} != {1}\n" + "version: {0}\n" + "found: {1}\n" + "caption: {2}".format(version, embedded_versions[caption], caption), ) # Special Cases # Windows Embedded Standard is Windows 7 - caption = 'Windows Embedded Standard' - with patch('platform.release', MagicMock(return_value='7')): + caption = "Windows Embedded Standard" + with patch("platform.release", MagicMock(return_value="7")): version = core._windows_os_release_grain(caption, 1) - self.assertEqual(version, '7') + self.assertEqual(version, "7") - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_linux_memdata(self): - ''' + """ Test memdata on Linux systems - ''' - _proc_meminfo = textwrap.dedent('''\ + """ + _proc_meminfo = textwrap.dedent( + """\ MemTotal: 16277028 kB - SwapTotal: 4789244 kB''') - with patch('salt.utils.files.fopen', mock_open(read_data=_proc_meminfo)): + SwapTotal: 4789244 kB""" + ) + with patch("salt.utils.files.fopen", mock_open(read_data=_proc_meminfo)): memdata = core._linux_memdata() - self.assertEqual(memdata.get('mem_total'), 15895) - self.assertEqual(memdata.get('swap_total'), 4676) + self.assertEqual(memdata.get("mem_total"), 15895) + self.assertEqual(memdata.get("swap_total"), 4676) - @skipIf(salt.utils.platform.is_windows(), 'System is Windows') + @skipIf(salt.utils.platform.is_windows(), "System is Windows") def test_bsd_memdata(self): - ''' + """ Test to memdata on *BSD systems - ''' + """ _path_exists_map = {} _path_isfile_map = {} _cmd_run_map = { - 'freebsd-version -u': '10.3-RELEASE', - '/sbin/sysctl -n hw.physmem': '2121781248', - '/sbin/sysctl -n vm.swap_total': '419430400' + "freebsd-version -u": "10.3-RELEASE", + "/sbin/sysctl -n hw.physmem": "2121781248", + "/sbin/sysctl -n vm.swap_total": "419430400", } path_exists_mock = MagicMock(side_effect=lambda x: _path_exists_map[x]) path_isfile_mock = MagicMock( side_effect=lambda x: _path_isfile_map.get(x, False) ) - cmd_run_mock = MagicMock( - side_effect=lambda x: _cmd_run_map[x] - ) + cmd_run_mock = MagicMock(side_effect=lambda x: _cmd_run_map[x]) empty_mock = MagicMock(return_value={}) - mock_freebsd_uname = ('FreeBSD', - 'freebsd10.3-hostname-8148', - '10.3-RELEASE', - 'FreeBSD 10.3-RELEASE #0 r297264: Fri Mar 25 02:10:02 UTC 2016 root@releng1.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC', - 'amd64', - 'amd64') + mock_freebsd_uname = ( + "FreeBSD", + "freebsd10.3-hostname-8148", + "10.3-RELEASE", + "FreeBSD 10.3-RELEASE #0 r297264: Fri Mar 25 02:10:02 UTC 2016 root@releng1.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC", + "amd64", + "amd64", + ) - with patch('platform.uname', - MagicMock(return_value=mock_freebsd_uname)): - with patch.object(salt.utils.platform, 'is_linux', - MagicMock(return_value=False)): - with patch.object(salt.utils.platform, 'is_freebsd', - MagicMock(return_value=True)): + with patch("platform.uname", MagicMock(return_value=mock_freebsd_uname)): + with patch.object( + salt.utils.platform, "is_linux", MagicMock(return_value=False) + ): + with patch.object( + salt.utils.platform, "is_freebsd", MagicMock(return_value=True) + ): # Skip the first if statement - with patch.object(salt.utils.platform, 'is_proxy', - MagicMock(return_value=False)): + with patch.object( + salt.utils.platform, "is_proxy", MagicMock(return_value=False) + ): # Skip the init grain compilation (not pertinent) - with patch.object(os.path, 'exists', path_exists_mock): - with patch('salt.utils.path.which') as mock: - mock.return_value = '/sbin/sysctl' + with patch.object(os.path, "exists", path_exists_mock): + with patch("salt.utils.path.which") as mock: + mock.return_value = "/sbin/sysctl" # Make a bunch of functions return empty dicts, # we don't care about these grains for the # purposes of this test. - with patch.object( - core, - '_bsd_cpudata', - empty_mock): - with patch.object( - core, - '_hw_data', - empty_mock): - with patch.object( - core, - '_virtual', - empty_mock): - with patch.object( - core, - '_ps', - empty_mock): + with patch.object(core, "_bsd_cpudata", empty_mock): + with patch.object(core, "_hw_data", empty_mock): + with patch.object(core, "_virtual", empty_mock): + with patch.object(core, "_ps", empty_mock): # Mock the osarch with patch.dict( - core.__salt__, - {'cmd.run': cmd_run_mock}): + core.__salt__, + {"cmd.run": cmd_run_mock}, + ): os_grains = core.os_data() - self.assertEqual(os_grains.get('mem_total'), 2023) - self.assertEqual(os_grains.get('swap_total'), 400) + self.assertEqual(os_grains.get("mem_total"), 2023) + self.assertEqual(os_grains.get("swap_total"), 400) - @skipIf(salt.utils.platform.is_windows(), 'System is Windows') + @skipIf(salt.utils.platform.is_windows(), "System is Windows") def test_docker_virtual(self): - ''' + """ Test if virtual grains are parsed correctly in Docker. - ''' - with patch.object(os.path, 'isdir', MagicMock(return_value=False)): - with patch.object(os.path, - 'isfile', - MagicMock(side_effect=lambda x: True if x == '/proc/1/cgroup' else False)): - for cgroup_substr in (':/system.slice/docker', ':/docker/', - ':/docker-ce/'): - cgroup_data = \ - '10:memory{0}a_long_sha256sum'.format(cgroup_substr) - log.debug( - 'Testing Docker cgroup substring \'%s\'', cgroup_substr) - with patch('salt.utils.files.fopen', mock_open(read_data=cgroup_data)): - with patch.dict(core.__salt__, {'cmd.run_all': MagicMock()}): - grains = core._virtual({'kernel': 'Linux'}) + """ + with patch.object(os.path, "isdir", MagicMock(return_value=False)): + with patch.object( + os.path, + "isfile", + MagicMock( + side_effect=lambda x: True if x == "/proc/1/cgroup" else False + ), + ): + for cgroup_substr in ( + ":/system.slice/docker", + ":/docker/", + ":/docker-ce/", + ): + cgroup_data = "10:memory{0}a_long_sha256sum".format(cgroup_substr) + log.debug("Testing Docker cgroup substring '%s'", cgroup_substr) + with patch( + "salt.utils.files.fopen", mock_open(read_data=cgroup_data) + ): + with patch.dict(core.__salt__, {"cmd.run_all": MagicMock()}): + grains = core._virtual({"kernel": "Linux"}) + self.assertEqual(grains.get("virtual_subtype"), "Docker") self.assertEqual( - grains.get('virtual_subtype'), - 'Docker' - ) - self.assertEqual( - grains.get('virtual'), - 'container', + grains.get("virtual"), "container", ) - @skipIf(salt.utils.platform.is_windows(), 'System is Windows') + @skipIf(salt.utils.platform.is_windows(), "System is Windows") def test_lxc_virtual(self): - ''' + """ Test if virtual grains are parsed correctly in LXC. - ''' - with patch.object(os.path, 'isdir', MagicMock(return_value=False)): - with patch.object(os.path, - 'isfile', - MagicMock(side_effect=lambda x: True if x == '/proc/1/cgroup' else False)): - cgroup_data = '10:memory:/lxc/a_long_sha256sum' - with patch('salt.utils.files.fopen', mock_open(read_data=cgroup_data)): - with patch.dict(core.__salt__, {'cmd.run_all': MagicMock()}): - grains = core._virtual({'kernel': 'Linux'}) + """ + with patch.object(os.path, "isdir", MagicMock(return_value=False)): + with patch.object( + os.path, + "isfile", + MagicMock( + side_effect=lambda x: True if x == "/proc/1/cgroup" else False + ), + ): + cgroup_data = "10:memory:/lxc/a_long_sha256sum" + with patch("salt.utils.files.fopen", mock_open(read_data=cgroup_data)): + with patch.dict(core.__salt__, {"cmd.run_all": MagicMock()}): + grains = core._virtual({"kernel": "Linux"}) + self.assertEqual(grains.get("virtual_subtype"), "LXC") self.assertEqual( - grains.get('virtual_subtype'), - 'LXC' - ) - self.assertEqual( - grains.get('virtual'), - 'container', + grains.get("virtual"), "container", ) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_xen_virtual(self): - ''' + """ Test if OS grains are parsed correctly in Ubuntu Xenial Xerus - ''' - with patch.multiple(os.path, isdir=MagicMock(side_effect=lambda x: x == '/sys/bus/xen'), - isfile=MagicMock(side_effect=lambda x: - x == '/sys/bus/xen/drivers/xenconsole')): - with patch.dict(core.__salt__, {'cmd.run': MagicMock(return_value='')}): - log.debug('Testing Xen') + """ + with patch.multiple( + os.path, + isdir=MagicMock(side_effect=lambda x: x == "/sys/bus/xen"), + isfile=MagicMock( + side_effect=lambda x: x == "/sys/bus/xen/drivers/xenconsole" + ), + ): + with patch.dict(core.__salt__, {"cmd.run": MagicMock(return_value="")}): + log.debug("Testing Xen") self.assertEqual( - core._virtual({'kernel': 'Linux'}).get('virtual_subtype'), - 'Xen PV DomU' + core._virtual({"kernel": "Linux"}).get("virtual_subtype"), + "Xen PV DomU", ) def test_if_virtual_subtype_exists_virtual_should_fallback_to_virtual(self): def mockstat(path): - if path == '/': - return 'fnord' - elif path == '/proc/1/root/.': - return 'roscivs' + if path == "/": + return "fnord" + elif path == "/proc/1/root/.": + return "roscivs" return None + with patch.dict( core.__salt__, { - 'cmd.run': MagicMock(return_value=''), - 'cmd.run_all': MagicMock(return_value={'retcode': 0, 'stdout': ''}), - } + "cmd.run": MagicMock(return_value=""), + "cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": ""}), + }, ): with patch.multiple( os.path, isfile=MagicMock(return_value=False), - isdir=MagicMock(side_effect=lambda x: x == '/proc'), + isdir=MagicMock(side_effect=lambda x: x == "/proc"), ): with patch.multiple( - os, - stat=MagicMock(side_effect=mockstat), + os, stat=MagicMock(side_effect=mockstat), ): - grains = core._virtual({'kernel': 'Linux'}) - assert grains.get('virtual_subtype') is not None - assert grains.get('virtual') == 'virtual' + grains = core._virtual({"kernel": "Linux"}) + assert grains.get("virtual_subtype") is not None + assert grains.get("virtual") == "virtual" def _check_ipaddress(self, value, ip_v): - ''' + """ check if ip address in a list is valid - ''' + """ for val in value: assert isinstance(val, six.string_types) - ip_method = 'is_ipv{0}'.format(ip_v) + ip_method = "is_ipv{0}".format(ip_v) self.assertTrue(getattr(salt.utils.network, ip_method)(val)) def _check_empty(self, key, value, empty): - ''' + """ if empty is False and value does not exist assert error if empty is True and value exists assert error - ''' + """ if not empty and not value: raise Exception("{0} is empty, expecting a value".format(key)) elif empty and value: - raise Exception("{0} is suppose to be empty. value: {1} \ - exists".format(key, value)) + raise Exception( + "{0} is suppose to be empty. value: {1} \ + exists".format( + key, value + ) + ) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_fqdn_return(self): - ''' + """ test ip4 and ip6 return values - ''' + """ net_ip4_mock = [IP4_LOCAL, IP4_ADD1, IP4_ADD2] net_ip6_mock = [IP6_LOCAL, IP6_ADD1, IP6_ADD2] - self._run_fqdn_tests(net_ip4_mock, net_ip6_mock, - ip4_empty=False, ip6_empty=False) + self._run_fqdn_tests( + net_ip4_mock, net_ip6_mock, ip4_empty=False, ip6_empty=False + ) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_fqdn6_empty(self): - ''' + """ test when ip6 is empty - ''' + """ net_ip4_mock = [IP4_LOCAL, IP4_ADD1, IP4_ADD2] net_ip6_mock = [] - self._run_fqdn_tests(net_ip4_mock, net_ip6_mock, - ip4_empty=False) + self._run_fqdn_tests(net_ip4_mock, net_ip6_mock, ip4_empty=False) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_fqdn4_empty(self): - ''' + """ test when ip4 is empty - ''' + """ net_ip4_mock = [] net_ip6_mock = [IP6_LOCAL, IP6_ADD1, IP6_ADD2] - self._run_fqdn_tests(net_ip4_mock, net_ip6_mock, - ip6_empty=False) + self._run_fqdn_tests(net_ip4_mock, net_ip6_mock, ip6_empty=False) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_fqdn_all_empty(self): - ''' + """ test when both ip4 and ip6 are empty - ''' + """ net_ip4_mock = [] net_ip6_mock = [] self._run_fqdn_tests(net_ip4_mock, net_ip6_mock) - def _run_fqdn_tests(self, net_ip4_mock, net_ip6_mock, - ip6_empty=True, ip4_empty=True): - + def _run_fqdn_tests( + self, net_ip4_mock, net_ip6_mock, ip6_empty=True, ip4_empty=True + ): def _check_type(key, value, ip4_empty, ip6_empty): - ''' + """ check type and other checks - ''' + """ assert isinstance(value, list) - if '4' in key: + if "4" in key: self._check_empty(key, value, ip4_empty) - self._check_ipaddress(value, ip_v='4') - elif '6' in key: + self._check_ipaddress(value, ip_v="4") + elif "6" in key: self._check_empty(key, value, ip6_empty) - self._check_ipaddress(value, ip_v='6') + self._check_ipaddress(value, ip_v="6") - ip4_mock = [(2, 1, 6, '', (IP4_ADD1, 0)), - (2, 3, 0, '', (IP4_ADD2, 0))] - ip6_mock = [(10, 1, 6, '', (IP6_ADD1, 0, 0, 0)), - (10, 3, 0, '', (IP6_ADD2, 0, 0, 0))] + ip4_mock = [(2, 1, 6, "", (IP4_ADD1, 0)), (2, 3, 0, "", (IP4_ADD2, 0))] + ip6_mock = [ + (10, 1, 6, "", (IP6_ADD1, 0, 0, 0)), + (10, 3, 0, "", (IP6_ADD2, 0, 0, 0)), + ] - with patch.dict(core.__opts__, {'ipv6': False}): - with patch.object(salt.utils.network, 'ip_addrs', - MagicMock(return_value=net_ip4_mock)): - with patch.object(salt.utils.network, 'ip_addrs6', - MagicMock(return_value=net_ip6_mock)): - with patch.object(core.socket, 'getaddrinfo', side_effect=[ip4_mock, ip6_mock]): + with patch.dict(core.__opts__, {"ipv6": False}): + with patch.object( + salt.utils.network, "ip_addrs", MagicMock(return_value=net_ip4_mock) + ): + with patch.object( + salt.utils.network, + "ip_addrs6", + MagicMock(return_value=net_ip6_mock), + ): + with patch.object( + core.socket, "getaddrinfo", side_effect=[ip4_mock, ip6_mock] + ): get_fqdn = core.ip_fqdn() - ret_keys = ['fqdn_ip4', 'fqdn_ip6', 'ipv4', 'ipv6'] + ret_keys = ["fqdn_ip4", "fqdn_ip6", "ipv4", "ipv6"] for key in ret_keys: value = get_fqdn[key] _check_type(key, value, ip4_empty, ip6_empty) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') - @patch.object(salt.utils.platform, 'is_windows', MagicMock(return_value=False)) - @patch('salt.grains.core.__opts__', {'ipv6': False}) + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") + @patch.object(salt.utils.platform, "is_windows", MagicMock(return_value=False)) + @patch("salt.grains.core.__opts__", {"ipv6": False}) def test_dns_return(self): - ''' + """ test the return for a dns grain. test for issue: https://github.com/saltstack/salt/issues/41230 - ''' - resolv_mock = {'domain': '', 'sortlist': [], 'nameservers': - [ipaddress.IPv4Address(IP4_ADD1), - ipaddress.IPv6Address(IP6_ADD1), - IP6_ADD_SCOPE], 'ip4_nameservers': - [ipaddress.IPv4Address(IP4_ADD1)], - 'search': ['test.saltstack.com'], 'ip6_nameservers': - [ipaddress.IPv6Address(IP6_ADD1), - IP6_ADD_SCOPE], 'options': []} - ret = {'dns': {'domain': '', 'sortlist': [], 'nameservers': - [IP4_ADD1, IP6_ADD1, - IP6_ADD_SCOPE], 'ip4_nameservers': - [IP4_ADD1], 'search': ['test.saltstack.com'], - 'ip6_nameservers': [IP6_ADD1, IP6_ADD_SCOPE], - 'options': []}} - with patch.object(salt.utils.dns, 'parse_resolv', MagicMock(return_value=resolv_mock)): + """ + resolv_mock = { + "domain": "", + "sortlist": [], + "nameservers": [ + ipaddress.IPv4Address(IP4_ADD1), + ipaddress.IPv6Address(IP6_ADD1), + IP6_ADD_SCOPE, + ], + "ip4_nameservers": [ipaddress.IPv4Address(IP4_ADD1)], + "search": ["test.saltstack.com"], + "ip6_nameservers": [ipaddress.IPv6Address(IP6_ADD1), IP6_ADD_SCOPE], + "options": [], + } + ret = { + "dns": { + "domain": "", + "sortlist": [], + "nameservers": [IP4_ADD1, IP6_ADD1, IP6_ADD_SCOPE], + "ip4_nameservers": [IP4_ADD1], + "search": ["test.saltstack.com"], + "ip6_nameservers": [IP6_ADD1, IP6_ADD_SCOPE], + "options": [], + } + } + with patch.object( + salt.utils.dns, "parse_resolv", MagicMock(return_value=resolv_mock) + ): assert core.dns() == ret - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') - @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '5.6.7.8'])) - @patch('salt.utils.network.ip_addrs6', - MagicMock(return_value=['fe80::a8b2:93ff:fe00:0', 'fe80::a8b2:93ff:dead:beef'])) - @patch('salt.utils.network.socket.getfqdn', MagicMock(side_effect=lambda v: v)) # Just pass-through + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") + @patch( + "salt.utils.network.ip_addrs", MagicMock(return_value=["1.2.3.4", "5.6.7.8"]) + ) + @patch( + "salt.utils.network.ip_addrs6", + MagicMock(return_value=["fe80::a8b2:93ff:fe00:0", "fe80::a8b2:93ff:dead:beef"]), + ) + @patch( + "salt.utils.network.socket.getfqdn", MagicMock(side_effect=lambda v: v) + ) # Just pass-through def test_fqdns_return(self): - ''' + """ test the return for a dns grain. test for issue: https://github.com/saltstack/salt/issues/41230 - ''' - reverse_resolv_mock = [('foo.bar.baz', [], ['1.2.3.4']), - ('rinzler.evil-corp.com', [], ['5.6.7.8']), - ('foo.bar.baz', [], ['fe80::a8b2:93ff:fe00:0']), - ('bluesniff.foo.bar', [], ['fe80::a8b2:93ff:dead:beef'])] - ret = {'fqdns': ['bluesniff.foo.bar', 'foo.bar.baz', 'rinzler.evil-corp.com']} - with patch.object(socket, 'gethostbyaddr', side_effect=reverse_resolv_mock): + """ + reverse_resolv_mock = [ + ("foo.bar.baz", [], ["1.2.3.4"]), + ("rinzler.evil-corp.com", [], ["5.6.7.8"]), + ("foo.bar.baz", [], ["fe80::a8b2:93ff:fe00:0"]), + ("bluesniff.foo.bar", [], ["fe80::a8b2:93ff:dead:beef"]), + ] + ret = {"fqdns": ["bluesniff.foo.bar", "foo.bar.baz", "rinzler.evil-corp.com"]} + with patch.object(socket, "gethostbyaddr", side_effect=reverse_resolv_mock): fqdns = core.fqdns() - self.assertIn('fqdns', fqdns) - self.assertEqual(len(fqdns['fqdns']), len(ret['fqdns'])) - self.assertEqual(set(fqdns['fqdns']), set(ret['fqdns'])) + self.assertIn("fqdns", fqdns) + self.assertEqual(len(fqdns["fqdns"]), len(ret["fqdns"])) + self.assertEqual(set(fqdns["fqdns"]), set(ret["fqdns"])) - @skipIf(not salt.utils.platform.is_linux(), 'System is not Linux') - @patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4'])) - @patch('salt.utils.network.ip_addrs6', MagicMock(return_value=[])) + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") + @patch("salt.utils.network.ip_addrs", MagicMock(return_value=["1.2.3.4"])) + @patch("salt.utils.network.ip_addrs6", MagicMock(return_value=[])) def test_fqdns_socket_error(self): - ''' + """ test the behavior on non-critical socket errors of the dns grain - ''' + """ + def _gen_gethostbyaddr(errno): def _gethostbyaddr(_): herror = socket.herror() herror.errno = errno raise herror + return _gethostbyaddr for errno in (0, core.HOST_NOT_FOUND, core.NO_DATA): mock_log = MagicMock() - with patch.object(socket, 'gethostbyaddr', - side_effect=_gen_gethostbyaddr(errno)): - with patch('salt.grains.core.log', mock_log): - self.assertEqual(core.fqdns(), {'fqdns': []}) + with patch.object( + socket, "gethostbyaddr", side_effect=_gen_gethostbyaddr(errno) + ): + with patch("salt.grains.core.log", mock_log): + self.assertEqual(core.fqdns(), {"fqdns": []}) mock_log.debug.assert_called_once() mock_log.error.assert_not_called() mock_log = MagicMock() - with patch.object(socket, 'gethostbyaddr', - side_effect=_gen_gethostbyaddr(-1)): - with patch('salt.grains.core.log', mock_log): - self.assertEqual(core.fqdns(), {'fqdns': []}) + with patch.object(socket, "gethostbyaddr", side_effect=_gen_gethostbyaddr(-1)): + with patch("salt.grains.core.log", mock_log): + self.assertEqual(core.fqdns(), {"fqdns": []}) mock_log.debug.assert_not_called() mock_log.error.assert_called_once() def test_core_virtual(self): - ''' + """ test virtual grain with cmd virt-what - ''' - virt = 'kvm' - with patch.object(salt.utils.platform, 'is_windows', - MagicMock(return_value=False)): - with patch.object(salt.utils.path, 'which', - MagicMock(return_value=True)): - with patch.dict(core.__salt__, {'cmd.run_all': - MagicMock(return_value={'pid': 78, - 'retcode': 0, - 'stderr': '', - 'stdout': virt})}): - osdata = {'kernel': 'test', } + """ + virt = "kvm" + with patch.object( + salt.utils.platform, "is_windows", MagicMock(return_value=False) + ): + with patch.object(salt.utils.path, "which", MagicMock(return_value=True)): + with patch.dict( + core.__salt__, + { + "cmd.run_all": MagicMock( + return_value={ + "pid": 78, + "retcode": 0, + "stderr": "", + "stdout": virt, + } + ) + }, + ): + osdata = { + "kernel": "test", + } ret = core._virtual(osdata) - self.assertEqual(ret['virtual'], virt) + self.assertEqual(ret["virtual"], virt) def test_solaris_sparc_s7zone(self): - ''' + """ verify productname grain for s7 zone - ''' + """ expectation = { - 'productname': 'SPARC S7-2', - 'product': 'SPARC S7-2', + "productname": "SPARC S7-2", + "product": "SPARC S7-2", } - with salt.utils.files.fopen(os.path.join(SOLARIS_DIR, 'prtconf.s7-zone')) as sparc_return_data: - this_sparc_return_data = '\n'.join(sparc_return_data.readlines()) - this_sparc_return_data += '\n' - self._check_solaris_sparc_productname_grains(this_sparc_return_data, expectation) + with salt.utils.files.fopen( + os.path.join(SOLARIS_DIR, "prtconf.s7-zone") + ) as sparc_return_data: + this_sparc_return_data = "\n".join(sparc_return_data.readlines()) + this_sparc_return_data += "\n" + self._check_solaris_sparc_productname_grains( + this_sparc_return_data, expectation + ) def test_solaris_sparc_s7(self): - ''' + """ verify productname grain for s7 - ''' + """ expectation = { - 'productname': 'SPARC S7-2', - 'product': 'SPARC S7-2', + "productname": "SPARC S7-2", + "product": "SPARC S7-2", } - with salt.utils.files.fopen(os.path.join(SOLARIS_DIR, 'prtdiag.s7')) as sparc_return_data: - this_sparc_return_data = '\n'.join(sparc_return_data.readlines()) - this_sparc_return_data += '\n' - self._check_solaris_sparc_productname_grains(this_sparc_return_data, expectation) + with salt.utils.files.fopen( + os.path.join(SOLARIS_DIR, "prtdiag.s7") + ) as sparc_return_data: + this_sparc_return_data = "\n".join(sparc_return_data.readlines()) + this_sparc_return_data += "\n" + self._check_solaris_sparc_productname_grains( + this_sparc_return_data, expectation + ) def test_solaris_sparc_t5220(self): - ''' + """ verify productname grain for t5220 - ''' + """ expectation = { - 'productname': 'SPARC Enterprise T5220', - 'product': 'SPARC Enterprise T5220', + "productname": "SPARC Enterprise T5220", + "product": "SPARC Enterprise T5220", } - with salt.utils.files.fopen(os.path.join(SOLARIS_DIR, 'prtdiag.t5220')) as sparc_return_data: - this_sparc_return_data = '\n'.join(sparc_return_data.readlines()) - this_sparc_return_data += '\n' - self._check_solaris_sparc_productname_grains(this_sparc_return_data, expectation) + with salt.utils.files.fopen( + os.path.join(SOLARIS_DIR, "prtdiag.t5220") + ) as sparc_return_data: + this_sparc_return_data = "\n".join(sparc_return_data.readlines()) + this_sparc_return_data += "\n" + self._check_solaris_sparc_productname_grains( + this_sparc_return_data, expectation + ) def test_solaris_sparc_t5220zone(self): - ''' + """ verify productname grain for t5220 zone - ''' + """ expectation = { - 'productname': 'SPARC Enterprise T5220', - 'product': 'SPARC Enterprise T5220', + "productname": "SPARC Enterprise T5220", + "product": "SPARC Enterprise T5220", } - with salt.utils.files.fopen(os.path.join(SOLARIS_DIR, 'prtconf.t5220-zone')) as sparc_return_data: - this_sparc_return_data = '\n'.join(sparc_return_data.readlines()) - this_sparc_return_data += '\n' - self._check_solaris_sparc_productname_grains(this_sparc_return_data, expectation) + with salt.utils.files.fopen( + os.path.join(SOLARIS_DIR, "prtconf.t5220-zone") + ) as sparc_return_data: + this_sparc_return_data = "\n".join(sparc_return_data.readlines()) + this_sparc_return_data += "\n" + self._check_solaris_sparc_productname_grains( + this_sparc_return_data, expectation + ) def _check_solaris_sparc_productname_grains(self, prtdata, expectation): - ''' + """ verify product grains on solaris sparc - ''' + """ import platform - path_isfile_mock = MagicMock(side_effect=lambda x: x in ['/etc/release']) - with salt.utils.files.fopen(os.path.join(OS_RELEASE_DIR, "solaris-11.3")) as os_release_file: + + path_isfile_mock = MagicMock(side_effect=lambda x: x in ["/etc/release"]) + with salt.utils.files.fopen( + os.path.join(OS_RELEASE_DIR, "solaris-11.3") + ) as os_release_file: os_release_content = os_release_file.readlines() - uname_mock = MagicMock(return_value=( - 'SunOS', 'testsystem', '5.11', '11.3', 'sunv4', 'sparc' - )) - with patch.object(platform, 'uname', uname_mock), \ - patch.object(salt.utils.platform, 'is_proxy', - MagicMock(return_value=False)), \ - patch.object(salt.utils.platform, 'is_linux', - MagicMock(return_value=False)), \ - patch.object(salt.utils.platform, 'is_windows', - MagicMock(return_value=False)), \ - patch.object(salt.utils.platform, 'is_smartos', - MagicMock(return_value=False)), \ - patch.object(salt.utils.path, 'which_bin', - MagicMock(return_value=None)), \ - patch.object(os.path, 'isfile', path_isfile_mock), \ - patch('salt.utils.files.fopen', - mock_open(read_data=os_release_content)) as os_release_file, \ - patch.object(core, '_sunos_cpudata', - MagicMock(return_value={ - 'cpuarch': 'sparcv9', - 'num_cpus': '1', - 'cpu_model': 'MOCK_CPU_MODEL', - 'cpu_flags': []})), \ - patch.object(core, '_memdata', - MagicMock(return_value={'mem_total': 16384})), \ - patch.object(core, '_virtual', - MagicMock(return_value={})), \ - patch.object(core, '_ps', MagicMock(return_value={})), \ - patch.object(salt.utils.path, 'which', - MagicMock(return_value=True)), \ - patch.dict(core.__salt__, - {'cmd.run': MagicMock(return_value=prtdata)}): + uname_mock = MagicMock( + return_value=("SunOS", "testsystem", "5.11", "11.3", "sunv4", "sparc") + ) + with patch.object(platform, "uname", uname_mock), patch.object( + salt.utils.platform, "is_proxy", MagicMock(return_value=False) + ), patch.object( + salt.utils.platform, "is_linux", MagicMock(return_value=False) + ), patch.object( + salt.utils.platform, "is_windows", MagicMock(return_value=False) + ), patch.object( + salt.utils.platform, "is_smartos", MagicMock(return_value=False) + ), patch.object( + salt.utils.path, "which_bin", MagicMock(return_value=None) + ), patch.object( + os.path, "isfile", path_isfile_mock + ), patch( + "salt.utils.files.fopen", mock_open(read_data=os_release_content) + ) as os_release_file, patch.object( + core, + "_sunos_cpudata", + MagicMock( + return_value={ + "cpuarch": "sparcv9", + "num_cpus": "1", + "cpu_model": "MOCK_CPU_MODEL", + "cpu_flags": [], + } + ), + ), patch.object( + core, "_memdata", MagicMock(return_value={"mem_total": 16384}) + ), patch.object( + core, "_virtual", MagicMock(return_value={}) + ), patch.object( + core, "_ps", MagicMock(return_value={}) + ), patch.object( + salt.utils.path, "which", MagicMock(return_value=True) + ), patch.dict( + core.__salt__, {"cmd.run": MagicMock(return_value=prtdata)} + ): os_grains = core.os_data() - grains = {k: v for k, v in os_grains.items() - if k in set(['product', 'productname'])} + grains = { + k: v for k, v in os_grains.items() if k in set(["product", "productname"]) + } self.assertEqual(grains, expectation) - @patch('os.path.isfile') - @patch('os.path.isdir') + @patch("os.path.isfile") + @patch("os.path.isdir") def test_core_virtual_unicode(self, mock_file, mock_dir): - ''' + """ test virtual grain with unicode character in product_name file - ''' + """ + def path_side_effect(path): - if path == '/sys/devices/virtual/dmi/id/product_name': + if path == "/sys/devices/virtual/dmi/id/product_name": return True return False - virt = 'kvm' + virt = "kvm" mock_file.side_effect = path_side_effect mock_dir.side_effect = path_side_effect - with patch.object(salt.utils.platform, 'is_windows', - MagicMock(return_value=False)): - with patch.object(salt.utils.path, 'which', - MagicMock(return_value=True)): - with patch.dict(core.__salt__, {'cmd.run_all': - MagicMock(return_value={'pid': 78, - 'retcode': 0, - 'stderr': '', - 'stdout': virt})}): - with patch('salt.utils.files.fopen', - mock_open(read_data='嗨')): - osdata = {'kernel': 'Linux', } + with patch.object( + salt.utils.platform, "is_windows", MagicMock(return_value=False) + ): + with patch.object(salt.utils.path, "which", MagicMock(return_value=True)): + with patch.dict( + core.__salt__, + { + "cmd.run_all": MagicMock( + return_value={ + "pid": 78, + "retcode": 0, + "stderr": "", + "stdout": virt, + } + ) + }, + ): + with patch("salt.utils.files.fopen", mock_open(read_data="嗨")): + osdata = { + "kernel": "Linux", + } ret = core._virtual(osdata) - self.assertEqual(ret['virtual'], virt) + self.assertEqual(ret["virtual"], virt) - @patch('salt.utils.path.which', MagicMock(return_value='/usr/sbin/sysctl')) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/sbin/sysctl")) def test_osx_memdata_with_comma(self): - ''' + """ test osx memdata method when comma returns - ''' - def _cmd_side_effect(cmd): - if 'hw.memsize' in cmd: - return '4294967296' - elif 'vm.swapusage' in cmd: - return 'total = 1024,00M used = 160,75M free = 863,25M (encrypted)' - with patch.dict(core.__salt__, {'cmd.run': MagicMock(side_effect=_cmd_side_effect)}): - ret = core._osx_memdata() - assert ret['swap_total'] == 1024 - assert ret['mem_total'] == 4096 + """ - @patch('salt.utils.path.which', MagicMock(return_value='/usr/sbin/sysctl')) + def _cmd_side_effect(cmd): + if "hw.memsize" in cmd: + return "4294967296" + elif "vm.swapusage" in cmd: + return "total = 1024,00M used = 160,75M free = 863,25M (encrypted)" + + with patch.dict( + core.__salt__, {"cmd.run": MagicMock(side_effect=_cmd_side_effect)} + ): + ret = core._osx_memdata() + assert ret["swap_total"] == 1024 + assert ret["mem_total"] == 4096 + + @patch("salt.utils.path.which", MagicMock(return_value="/usr/sbin/sysctl")) def test_osx_memdata(self): - ''' + """ test osx memdata - ''' - def _cmd_side_effect(cmd): - if 'hw.memsize' in cmd: - return '4294967296' - elif 'vm.swapusage' in cmd: - return 'total = 0.00M used = 0.00M free = 0.00M (encrypted)' - with patch.dict(core.__salt__, {'cmd.run': MagicMock(side_effect=_cmd_side_effect)}): - ret = core._osx_memdata() - assert ret['swap_total'] == 0 - assert ret['mem_total'] == 4096 + """ - @skipIf(not core._DATEUTIL_TZ, 'Missing dateutil.tz') + def _cmd_side_effect(cmd): + if "hw.memsize" in cmd: + return "4294967296" + elif "vm.swapusage" in cmd: + return "total = 0.00M used = 0.00M free = 0.00M (encrypted)" + + with patch.dict( + core.__salt__, {"cmd.run": MagicMock(side_effect=_cmd_side_effect)} + ): + ret = core._osx_memdata() + assert ret["swap_total"] == 0 + assert ret["mem_total"] == 4096 + + @skipIf(not core._DATEUTIL_TZ, "Missing dateutil.tz") def test_locale_info_tzname(self): # mock datetime.now().tzname() # cant just mock now because it is read only - tzname = Mock(return_value='MDT_FAKE') + tzname = Mock(return_value="MDT_FAKE") now_ret_object = Mock(tzname=tzname) now = Mock(return_value=now_ret_object) datetime = Mock(now=now) - with patch.object(core, 'datetime', datetime=datetime) as datetime_module: - with patch.object(core.dateutil.tz, 'tzlocal', return_value=object) as tzlocal: - with patch.object(salt.utils.platform, 'is_proxy', return_value=False) as is_proxy: + with patch.object(core, "datetime", datetime=datetime) as datetime_module: + with patch.object( + core.dateutil.tz, "tzlocal", return_value=object + ) as tzlocal: + with patch.object( + salt.utils.platform, "is_proxy", return_value=False + ) as is_proxy: ret = core.locale_info() tzname.assert_called_once_with() @@ -1278,59 +1494,69 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): tzlocal.assert_called_once_with() is_proxy.assert_called_once_with() - self.assertEqual(ret['locale_info']['timezone'], 'MDT_FAKE') + self.assertEqual(ret["locale_info"]["timezone"], "MDT_FAKE") - @skipIf(not core._DATEUTIL_TZ, 'Missing dateutil.tz') + @skipIf(not core._DATEUTIL_TZ, "Missing dateutil.tz") def test_locale_info_unicode_error_tzname(self): # UnicodeDecodeError most have the default string encoding - unicode_error = UnicodeDecodeError(str('fake'), b'\x00\x00', 1, 2, str('fake')) + unicode_error = UnicodeDecodeError(str("fake"), b"\x00\x00", 1, 2, str("fake")) # mock datetime.now().tzname() # cant just mock now because it is read only - tzname = Mock(return_value='MDT_FAKE') + tzname = Mock(return_value="MDT_FAKE") now_ret_object = Mock(tzname=tzname) now = Mock(return_value=now_ret_object) datetime = Mock(now=now) # mock tzname[0].decode() - decode = Mock(return_value='CST_FAKE') + decode = Mock(return_value="CST_FAKE") tzname2 = (Mock(decode=decode,),) - with patch.object(core, 'datetime', datetime=datetime) as datetime_module: - with patch.object(core.dateutil.tz, 'tzlocal', side_effect=unicode_error) as tzlocal: - with patch.object(salt.utils.platform, 'is_proxy', return_value=False) as is_proxy: - with patch.object(core.salt.utils.platform, 'is_windows', return_value=True) as is_windows: - with patch.object(core, 'time', tzname=tzname2): + with patch.object(core, "datetime", datetime=datetime) as datetime_module: + with patch.object( + core.dateutil.tz, "tzlocal", side_effect=unicode_error + ) as tzlocal: + with patch.object( + salt.utils.platform, "is_proxy", return_value=False + ) as is_proxy: + with patch.object( + core.salt.utils.platform, "is_windows", return_value=True + ) as is_windows: + with patch.object(core, "time", tzname=tzname2): ret = core.locale_info() tzname.assert_not_called() self.assertEqual(len(now_ret_object.method_calls), 0) now.assert_not_called() self.assertEqual(len(datetime.method_calls), 0) - decode.assert_called_once_with('mbcs') + decode.assert_called_once_with("mbcs") self.assertEqual(len(tzname2[0].method_calls), 1) self.assertEqual(len(datetime_module.method_calls), 0) tzlocal.assert_called_once_with() is_proxy.assert_called_once_with() is_windows.assert_called_once_with() - self.assertEqual(ret['locale_info']['timezone'], 'CST_FAKE') + self.assertEqual(ret["locale_info"]["timezone"], "CST_FAKE") - @skipIf(core._DATEUTIL_TZ, 'Not Missing dateutil.tz') + @skipIf(core._DATEUTIL_TZ, "Not Missing dateutil.tz") def test_locale_info_no_tz_tzname(self): - with patch.object(salt.utils.platform, 'is_proxy', return_value=False) as is_proxy: - with patch.object(core.salt.utils.platform, 'is_windows', return_value=True) as is_windows: + with patch.object( + salt.utils.platform, "is_proxy", return_value=False + ) as is_proxy: + with patch.object( + core.salt.utils.platform, "is_windows", return_value=True + ) as is_windows: ret = core.locale_info() is_proxy.assert_called_once_with() is_windows.assert_not_called() - self.assertEqual(ret['locale_info']['timezone'], 'unknown') + self.assertEqual(ret["locale_info"]["timezone"], "unknown") def test_cwd_exists(self): cwd_grain = core.cwd() self.assertIsInstance(cwd_grain, dict) - self.assertTrue('cwd' in cwd_grain) - self.assertEqual(cwd_grain['cwd'], os.getcwd()) + self.assertTrue("cwd" in cwd_grain) + self.assertEqual(cwd_grain["cwd"], os.getcwd()) def test_cwd_is_cwd(self): cwd = os.getcwd() @@ -1342,7 +1568,7 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): cwd_grain = core.cwd() - self.assertEqual(cwd_grain['cwd'], new_dir) + self.assertEqual(cwd_grain["cwd"], new_dir) finally: # change back to original directory os.chdir(cwd) @@ -1350,148 +1576,205 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): def test_virtual_set_virtual_grain(self): osdata = {} - (osdata['kernel'], osdata['nodename'], - osdata['kernelrelease'], osdata['kernelversion'], osdata['cpuarch'], _) = platform.uname() + ( + osdata["kernel"], + osdata["nodename"], + osdata["kernelrelease"], + osdata["kernelversion"], + osdata["cpuarch"], + _, + ) = platform.uname() - with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, - 'cmd.run_all': salt.modules.cmdmod.run_all, - 'cmd.retcode': salt.modules.cmdmod.retcode, - 'smbios.get': salt.modules.smbios.get}): + with patch.dict( + core.__salt__, + { + "cmd.run": salt.modules.cmdmod.run, + "cmd.run_all": salt.modules.cmdmod.run_all, + "cmd.retcode": salt.modules.cmdmod.retcode, + "smbios.get": salt.modules.smbios.get, + }, + ): virtual_grains = core._virtual(osdata) - self.assertIn('virtual', virtual_grains) + self.assertIn("virtual", virtual_grains) def test_virtual_has_virtual_grain(self): - osdata = {'virtual': 'something'} + osdata = {"virtual": "something"} - (osdata['kernel'], osdata['nodename'], - osdata['kernelrelease'], osdata['kernelversion'], osdata['cpuarch'], _) = platform.uname() + ( + osdata["kernel"], + osdata["nodename"], + osdata["kernelrelease"], + osdata["kernelversion"], + osdata["cpuarch"], + _, + ) = platform.uname() - with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, - 'cmd.run_all': salt.modules.cmdmod.run_all, - 'cmd.retcode': salt.modules.cmdmod.retcode, - 'smbios.get': salt.modules.smbios.get}): + with patch.dict( + core.__salt__, + { + "cmd.run": salt.modules.cmdmod.run, + "cmd.run_all": salt.modules.cmdmod.run_all, + "cmd.retcode": salt.modules.cmdmod.retcode, + "smbios.get": salt.modules.smbios.get, + }, + ): virtual_grains = core._virtual(osdata) - self.assertIn('virtual', virtual_grains) - self.assertNotEqual(virtual_grains['virtual'], 'physical') + self.assertIn("virtual", virtual_grains) + self.assertNotEqual(virtual_grains["virtual"], "physical") - @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + @skipIf(not salt.utils.platform.is_windows(), "System is not Windows") def test_windows_virtual_set_virtual_grain(self): osdata = {} - (osdata['kernel'], osdata['nodename'], - osdata['kernelrelease'], osdata['kernelversion'], osdata['cpuarch'], _) = platform.uname() + ( + osdata["kernel"], + osdata["nodename"], + osdata["kernelrelease"], + osdata["kernelversion"], + osdata["cpuarch"], + _, + ) = platform.uname() - with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, - 'cmd.run_all': salt.modules.cmdmod.run_all, - 'cmd.retcode': salt.modules.cmdmod.retcode, - 'smbios.get': salt.modules.smbios.get}): + with patch.dict( + core.__salt__, + { + "cmd.run": salt.modules.cmdmod.run, + "cmd.run_all": salt.modules.cmdmod.run_all, + "cmd.retcode": salt.modules.cmdmod.retcode, + "smbios.get": salt.modules.smbios.get, + }, + ): virtual_grains = core._windows_virtual(osdata) - self.assertIn('virtual', virtual_grains) + self.assertIn("virtual", virtual_grains) - @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + @skipIf(not salt.utils.platform.is_windows(), "System is not Windows") def test_windows_virtual_has_virtual_grain(self): - osdata = {'virtual': 'something'} + osdata = {"virtual": "something"} - (osdata['kernel'], osdata['nodename'], - osdata['kernelrelease'], osdata['kernelversion'], osdata['cpuarch'], _) = platform.uname() + ( + osdata["kernel"], + osdata["nodename"], + osdata["kernelrelease"], + osdata["kernelversion"], + osdata["cpuarch"], + _, + ) = platform.uname() - with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, - 'cmd.run_all': salt.modules.cmdmod.run_all, - 'cmd.retcode': salt.modules.cmdmod.retcode, - 'smbios.get': salt.modules.smbios.get}): + with patch.dict( + core.__salt__, + { + "cmd.run": salt.modules.cmdmod.run, + "cmd.run_all": salt.modules.cmdmod.run_all, + "cmd.retcode": salt.modules.cmdmod.retcode, + "smbios.get": salt.modules.smbios.get, + }, + ): virtual_grains = core._windows_virtual(osdata) - self.assertIn('virtual', virtual_grains) - self.assertNotEqual(virtual_grains['virtual'], 'physical') + self.assertIn("virtual", virtual_grains) + self.assertNotEqual(virtual_grains["virtual"], "physical") - @skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + @skipIf(not salt.utils.platform.is_windows(), "System is not Windows") def test_osdata_virtual_key_win(self): - with patch.dict(core.__salt__, {'cmd.run': salt.modules.cmdmod.run, - 'cmd.run_all': salt.modules.cmdmod.run_all, - 'cmd.retcode': salt.modules.cmdmod.retcode, - 'smbios.get': salt.modules.smbios.get}): + with patch.dict( + core.__salt__, + { + "cmd.run": salt.modules.cmdmod.run, + "cmd.run_all": salt.modules.cmdmod.run_all, + "cmd.retcode": salt.modules.cmdmod.retcode, + "smbios.get": salt.modules.smbios.get, + }, + ): _windows_platform_data_ret = core.os_data() - _windows_platform_data_ret['virtual'] = 'something' + _windows_platform_data_ret["virtual"] = "something" - with patch.object(core, - '_windows_platform_data', - return_value=_windows_platform_data_ret) as _windows_platform_data: + with patch.object( + core, "_windows_platform_data", return_value=_windows_platform_data_ret + ) as _windows_platform_data: osdata_grains = core.os_data() _windows_platform_data.assert_called_once() - self.assertIn('virtual', osdata_grains) - self.assertNotEqual(osdata_grains['virtual'], 'physical') + self.assertIn("virtual", osdata_grains) + self.assertNotEqual(osdata_grains["virtual"], "physical") - @skipIf(salt.utils.platform.is_windows(), 'System is Windows') + @skipIf(salt.utils.platform.is_windows(), "System is Windows") def test_bsd_osfullname(self): - ''' + """ Test to ensure osfullname exists on *BSD systems - ''' + """ _path_exists_map = {} _path_isfile_map = {} _cmd_run_map = { - 'freebsd-version -u': '10.3-RELEASE', - '/sbin/sysctl -n hw.physmem': '2121781248', - '/sbin/sysctl -n vm.swap_total': '419430400' + "freebsd-version -u": "10.3-RELEASE", + "/sbin/sysctl -n hw.physmem": "2121781248", + "/sbin/sysctl -n vm.swap_total": "419430400", } path_exists_mock = MagicMock(side_effect=lambda x: _path_exists_map[x]) path_isfile_mock = MagicMock( side_effect=lambda x: _path_isfile_map.get(x, False) ) - cmd_run_mock = MagicMock( - side_effect=lambda x: _cmd_run_map[x] - ) + cmd_run_mock = MagicMock(side_effect=lambda x: _cmd_run_map[x]) empty_mock = MagicMock(return_value={}) - mock_freebsd_uname = ('FreeBSD', - 'freebsd10.3-hostname-8148', - '10.3-RELEASE', - 'FreeBSD 10.3-RELEASE #0 r297264: Fri Mar 25 02:10:02 UTC 2016 root@releng1.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC', - 'amd64', - 'amd64') + mock_freebsd_uname = ( + "FreeBSD", + "freebsd10.3-hostname-8148", + "10.3-RELEASE", + "FreeBSD 10.3-RELEASE #0 r297264: Fri Mar 25 02:10:02 UTC 2016 root@releng1.nyi.freebsd.org:/usr/obj/usr/src/sys/GENERIC", + "amd64", + "amd64", + ) - with patch('platform.uname', - MagicMock(return_value=mock_freebsd_uname)): - with patch.object(salt.utils.platform, 'is_linux', - MagicMock(return_value=False)): - with patch.object(salt.utils.platform, 'is_freebsd', - MagicMock(return_value=True)): + with patch("platform.uname", MagicMock(return_value=mock_freebsd_uname)): + with patch.object( + salt.utils.platform, "is_linux", MagicMock(return_value=False) + ): + with patch.object( + salt.utils.platform, "is_freebsd", MagicMock(return_value=True) + ): # Skip the first if statement - with patch.object(salt.utils.platform, 'is_proxy', - MagicMock(return_value=False)): + with patch.object( + salt.utils.platform, "is_proxy", MagicMock(return_value=False) + ): # Skip the init grain compilation (not pertinent) - with patch.object(os.path, 'exists', path_exists_mock): - with patch('salt.utils.path.which') as mock: - mock.return_value = '/sbin/sysctl' + with patch.object(os.path, "exists", path_exists_mock): + with patch("salt.utils.path.which") as mock: + mock.return_value = "/sbin/sysctl" # Make a bunch of functions return empty dicts, # we don't care about these grains for the # purposes of this test. - with patch.object(core, '_bsd_cpudata', empty_mock), \ - patch.object(core, '_hw_data', empty_mock), \ - patch.object(core, '_virtual', empty_mock), \ - patch.object(core, '_ps', empty_mock), \ - patch.dict(core.__salt__, {'cmd.run': cmd_run_mock}): + with patch.object( + core, "_bsd_cpudata", empty_mock + ), patch.object( + core, "_hw_data", empty_mock + ), patch.object( + core, "_virtual", empty_mock + ), patch.object( + core, "_ps", empty_mock + ), patch.dict( + core.__salt__, {"cmd.run": cmd_run_mock} + ): os_grains = core.os_data() - self.assertIn('osfullname', os_grains) - self.assertEqual(os_grains.get('osfullname'), 'FreeBSD') + self.assertIn("osfullname", os_grains) + self.assertEqual(os_grains.get("osfullname"), "FreeBSD") def test_saltversioninfo(self): - ''' + """ test saltversioninfo core grain. - ''' + """ ret = core.saltversioninfo() - info = ret['saltversioninfo'] + info = ret["saltversioninfo"] assert isinstance(ret, dict) assert isinstance(info, list) try: @@ -1501,3 +1784,10 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): assert len(info) == 2 assert all([x is not None for x in info]) assert all([isinstance(x, int) for x in info]) + + def test_path(self): + comps = ["foo", "bar", "baz"] + path = os.path.pathsep.join(comps) + with patch.dict(os.environ, {"PATH": path}): + result = core.path() + assert result == {"path": path, "systempath": comps}, result diff --git a/tests/unit/grains/test_disks.py b/tests/unit/grains/test_disks.py index 9b23f1e8bc8..8a59170f0a5 100644 --- a/tests/unit/grains/test_disks.py +++ b/tests/unit/grains/test_disks.py @@ -1,84 +1,85 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Shane Lee <slee@saltstack.com>` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import textwrap -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, - MagicMock, -) +import textwrap # Import Salt Libs import salt.grains.disks as disks +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class IscsiGrainsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for _windows_disks grains - ''' + """ + def setup_loader_modules(self): return { - disks: { - '__salt__': {}, - }, + disks: {"__salt__": {}}, } def test__windows_disks(self): - ''' + """ Test grains._windows_disks, normal return Should return a populated dictionary - ''' - mock_which = MagicMock(return_value='C:\\Windows\\System32\\wbem\\WMIC.exe') - wmic_result = textwrap.dedent(''' + """ + mock_which = MagicMock(return_value="C:\\Windows\\System32\\wbem\\WMIC.exe") + wmic_result = textwrap.dedent( + """ DeviceId MediaType 0 4 1 0 2 3 3 5 - ''') - mock_run_all = MagicMock(return_value={'stdout': wmic_result, - 'retcode': 0}) + """ + ) + mock_run_all = MagicMock(return_value={"stdout": wmic_result, "retcode": 0}) - with patch('salt.utils.path.which', mock_which), \ - patch.dict(disks.__salt__, {'cmd.run_all': mock_run_all}): + with patch("salt.utils.path.which", mock_which), patch.dict( + disks.__salt__, {"cmd.run_all": mock_run_all} + ): result = disks._windows_disks() expected = { - 'SSDs': ['\\\\.\\PhysicalDrive0'], - 'disks': [ - '\\\\.\\PhysicalDrive0', - '\\\\.\\PhysicalDrive1', - '\\\\.\\PhysicalDrive2', - '\\\\.\\PhysicalDrive3']} + "SSDs": ["\\\\.\\PhysicalDrive0"], + "disks": [ + "\\\\.\\PhysicalDrive0", + "\\\\.\\PhysicalDrive1", + "\\\\.\\PhysicalDrive2", + "\\\\.\\PhysicalDrive3", + ], + } self.assertDictEqual(result, expected) - cmd = ' '.join([ - 'C:\\Windows\\System32\\wbem\\WMIC.exe', - '/namespace:\\\\root\\microsoft\\windows\\storage', - 'path', - 'MSFT_PhysicalDisk', - 'get', - 'DeviceID,MediaType', - '/format:table' - ]) + cmd = " ".join( + [ + "C:\\Windows\\System32\\wbem\\WMIC.exe", + "/namespace:\\\\root\\microsoft\\windows\\storage", + "path", + "MSFT_PhysicalDisk", + "get", + "DeviceID,MediaType", + "/format:table", + ] + ) mock_run_all.assert_called_once_with(cmd) def test__windows_disks_retcode(self): - ''' + """ Test grains._windows_disks, retcode 1 Should return empty lists - ''' - mock_which = MagicMock(return_value='C:\\Windows\\System32\\wbem\\WMIC.exe') - mock_run_all = MagicMock(return_value={'stdout': '', - 'retcode': 1}) - with patch('salt.utils.path.which', mock_which), \ - patch.dict(disks.__salt__, {'cmd.run_all': mock_run_all}): + """ + mock_which = MagicMock(return_value="C:\\Windows\\System32\\wbem\\WMIC.exe") + mock_run_all = MagicMock(return_value={"stdout": "", "retcode": 1}) + with patch("salt.utils.path.which", mock_which), patch.dict( + disks.__salt__, {"cmd.run_all": mock_run_all} + ): result = disks._windows_disks() - expected = { - 'SSDs': [], - 'disks': []} + expected = {"SSDs": [], "disks": []} self.assertDictEqual(result, expected) diff --git a/tests/unit/grains/test_fibre_channel.py b/tests/unit/grains/test_fibre_channel.py index a7d9058f6a2..1697db9e004 100644 --- a/tests/unit/grains/test_fibre_channel.py +++ b/tests/unit/grains/test_fibre_channel.py @@ -1,42 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Shane Lee <slee@saltstack.com>` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, - mock_open, - MagicMock, -) - # Import Salt Libs import salt.grains.fibre_channel as fibre_channel +from tests.support.mock import MagicMock, mock_open, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase class FibreChannelGrainsTestCase(TestCase): - ''' + """ Test cases for iscsi grains - ''' + """ + def test_windows_fibre_channel_wwns_grains(self): - wwns = ['20:00:00:25:b5:11:11:4c', - '20:00:00:25:b5:11:11:5c', - '20:00:00:25:b5:44:44:4c', - '20:00:00:25:b5:44:44:5c'] + wwns = [ + "20:00:00:25:b5:11:11:4c", + "20:00:00:25:b5:11:11:5c", + "20:00:00:25:b5:44:44:4c", + "20:00:00:25:b5:44:44:5c", + ] cmd_run_mock = MagicMock(return_value=wwns) - with patch('salt.modules.cmdmod.powershell', cmd_run_mock): + with patch("salt.modules.cmdmod.powershell", cmd_run_mock): ret = fibre_channel._windows_wwns() assert ret == wwns, ret def test_linux_fibre_channel_wwns_grains(self): - contents = ['0x500143802426baf4', '0x500143802426baf5'] - files = ['file1', 'file2'] - with patch('glob.glob', MagicMock(return_value=files)), \ - patch('salt.utils.files.fopen', mock_open(read_data=contents)): + contents = ["0x500143802426baf4", "0x500143802426baf5"] + files = ["file1", "file2"] + with patch("glob.glob", MagicMock(return_value=files)), patch( + "salt.utils.files.fopen", mock_open(read_data=contents) + ): ret = fibre_channel._linux_wwns() - assert ret == ['500143802426baf4', '500143802426baf5'], ret + assert ret == ["500143802426baf4", "500143802426baf5"], ret diff --git a/tests/unit/grains/test_iscsi.py b/tests/unit/grains/test_iscsi.py index 168ea0746e4..f29727d489e 100644 --- a/tests/unit/grains/test_iscsi.py +++ b/tests/unit/grains/test_iscsi.py @@ -1,93 +1,98 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Shane Lee <slee@saltstack.com>` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import textwrap -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, - mock_open, - MagicMock, -) - # Import Salt Libs import salt.grains.iscsi as iscsi +from tests.support.mock import MagicMock, mock_open, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase class IscsiGrainsTestCase(TestCase): - ''' + """ Test cases for iscsi grains - ''' + """ + def test_windows_iscsi_iqn_grains(self): cmd_run_mock = MagicMock( - return_value={'stdout': 'iSCSINodeName\n' - 'iqn.1991-05.com.microsoft:simon-x1\n'} + return_value={ + "stdout": "iSCSINodeName\n" "iqn.1991-05.com.microsoft:simon-x1\n" + } ) _grains = {} - with patch('salt.utils.path.which', MagicMock(return_value=True)): - with patch('salt.modules.cmdmod.run_all', cmd_run_mock): - _grains['iscsi_iqn'] = iscsi._windows_iqn() + with patch("salt.utils.path.which", MagicMock(return_value=True)): + with patch("salt.modules.cmdmod.run_all", cmd_run_mock): + _grains["iscsi_iqn"] = iscsi._windows_iqn() - self.assertEqual(_grains.get('iscsi_iqn'), - ['iqn.1991-05.com.microsoft:simon-x1']) + self.assertEqual( + _grains.get("iscsi_iqn"), ["iqn.1991-05.com.microsoft:simon-x1"] + ) def test_aix_iscsi_iqn_grains(self): cmd_run_mock = MagicMock( - return_value='initiator_name iqn.localhost.hostid.7f000001' + return_value="initiator_name iqn.localhost.hostid.7f000001" ) _grains = {} - with patch('salt.modules.cmdmod.run', cmd_run_mock): - _grains['iscsi_iqn'] = iscsi._aix_iqn() + with patch("salt.modules.cmdmod.run", cmd_run_mock): + _grains["iscsi_iqn"] = iscsi._aix_iqn() - self.assertEqual(_grains.get('iscsi_iqn'), - ['iqn.localhost.hostid.7f000001']) + self.assertEqual(_grains.get("iscsi_iqn"), ["iqn.localhost.hostid.7f000001"]) def test_linux_iscsi_iqn_grains(self): - _iscsi_file = textwrap.dedent('''\ + _iscsi_file = textwrap.dedent( + """\ ## DO NOT EDIT OR REMOVE THIS FILE! ## If you remove this file, the iSCSI daemon will not start. ## If you change the InitiatorName, existing access control lists ## may reject this initiator. The InitiatorName must be unique ## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames. InitiatorName=iqn.1993-08.org.debian:01:d12f7aba36 - ''') + """ + ) - with patch('salt.utils.files.fopen', mock_open(read_data=_iscsi_file)): + with patch("salt.utils.files.fopen", mock_open(read_data=_iscsi_file)): iqn = iscsi._linux_iqn() assert isinstance(iqn, list) assert len(iqn) == 1 - assert iqn == ['iqn.1993-08.org.debian:01:d12f7aba36'] + assert iqn == ["iqn.1993-08.org.debian:01:d12f7aba36"] - @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(errno.EPERM, - 'The cables are not the same length.'))) - @patch('salt.grains.iscsi.log', MagicMock()) + @patch( + "salt.utils.files.fopen", + MagicMock( + side_effect=IOError(errno.EPERM, "The cables are not the same length.") + ), + ) + @patch("salt.grains.iscsi.log", MagicMock()) def test_linux_iqn_non_root(self): - ''' + """ Test if linux_iqn is running on salt-master as non-root and handling access denial properly. :return: - ''' + """ assert iscsi._linux_iqn() == [] iscsi.log.debug.assert_called() - assert 'Error while accessing' in iscsi.log.debug.call_args[0][0] - assert 'cables are not the same' in iscsi.log.debug.call_args[0][2].strerror + assert "Error while accessing" in iscsi.log.debug.call_args[0][0] + assert "cables are not the same" in iscsi.log.debug.call_args[0][2].strerror assert iscsi.log.debug.call_args[0][2].errno == errno.EPERM - assert iscsi.log.debug.call_args[0][1] == '/etc/iscsi/initiatorname.iscsi' + assert iscsi.log.debug.call_args[0][1] == "/etc/iscsi/initiatorname.iscsi" - @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(errno.ENOENT, ''))) - @patch('salt.grains.iscsi.log', MagicMock()) + @patch("salt.utils.files.fopen", MagicMock(side_effect=IOError(errno.ENOENT, ""))) + @patch("salt.grains.iscsi.log", MagicMock()) def test_linux_iqn_no_iscsii_initiator(self): - ''' + """ Test if linux_iqn is running on salt-master as root. iscsii initiator is not there accessible or is not supported. :return: - ''' + """ assert iscsi._linux_iqn() == [] iscsi.log.debug.assert_not_called() diff --git a/tests/unit/grains/test_napalm.py b/tests/unit/grains/test_napalm.py index 3446975a51a..aa288f1e948 100644 --- a/tests/unit/grains/test_napalm.py +++ b/tests/unit/grains/test_napalm.py @@ -1,55 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import tests.support.napalm as napalm_test_support + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) -import tests.support.napalm as napalm_test_support import salt.grains.napalm as napalm_grains # NOQA import salt.proxy.napalm as napalm_proxy # NOQA + napalm_grains.salt.utils.napalm.is_proxy = MagicMock(return_value=True) TEST_DEVICE_CACHE = { - 'DRIVER': napalm_test_support.MockNapalmDevice(), - 'DRIVER_NAME': 'cisco', - 'OS_VERSION': '1.2.3', - 'HOSTNAME': 'test-device.com', - 'USERNAME': 'admin' + "DRIVER": napalm_test_support.MockNapalmDevice(), + "DRIVER_NAME": "cisco", + "OS_VERSION": "1.2.3", + "HOSTNAME": "test-device.com", + "USERNAME": "admin", } -TEST_CACHE = { - 'result': True, - 'out': napalm_test_support.TEST_FACTS -} +TEST_CACHE = {"result": True, "out": napalm_test_support.TEST_FACTS} -@patch('salt.grains.napalm.DEVICE_CACHE', TEST_DEVICE_CACHE) -@patch('salt.grains.napalm.GRAINS_CACHE', TEST_CACHE) +@patch("salt.grains.napalm.DEVICE_CACHE", TEST_DEVICE_CACHE) +@patch("salt.grains.napalm.GRAINS_CACHE", TEST_CACHE) class NapalmGrainsTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash, + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, } } @@ -57,40 +49,40 @@ class NapalmGrainsTestCase(TestCase, LoaderModuleMockMixin): def test_os(self): ret = napalm_grains.getos(proxy=napalm_proxy) - assert ret['os'] == 'cisco' + assert ret["os"] == "cisco" def test_os_version(self): ret = napalm_grains.version(proxy=napalm_proxy) - assert ret['version'] == '1.2.3' + assert ret["version"] == "1.2.3" def test_model(self): ret = napalm_grains.model(proxy=napalm_proxy) - assert ret['model'] == 'test_model' + assert ret["model"] == "test_model" def test_serial(self): ret = napalm_grains.serial(proxy=napalm_proxy) - assert ret['serial'] == '123456' + assert ret["serial"] == "123456" def test_vendor(self): ret = napalm_grains.vendor(proxy=napalm_proxy) - assert ret['vendor'] == 'cisco' + assert ret["vendor"] == "cisco" def test_uptime(self): ret = napalm_grains.uptime(proxy=napalm_proxy) - assert ret['uptime'] == 'Forever' + assert ret["uptime"] == "Forever" def test_interfaces(self): ret = napalm_grains.interfaces(proxy=napalm_proxy) - assert ret['interfaces'] == napalm_test_support.TEST_INTERFACES + assert ret["interfaces"] == napalm_test_support.TEST_INTERFACES def test_username(self): ret = napalm_grains.username(proxy=napalm_proxy) - assert ret['username'] == 'admin' + assert ret["username"] == "admin" def test_hostname(self): ret = napalm_grains.hostname(proxy=napalm_proxy) - assert ret['hostname'] == 'test-device.com' + assert ret["hostname"] == "test-device.com" def test_host(self): ret = napalm_grains.host(proxy=napalm_proxy) - assert ret['host'] == 'test-device.com' + assert ret["host"] == "test-device.com" diff --git a/tests/unit/grains/test_nvme.py b/tests/unit/grains/test_nvme.py index c323e425506..6b3a53695a2 100644 --- a/tests/unit/grains/test_nvme.py +++ b/tests/unit/grains/test_nvme.py @@ -1,64 +1,69 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Simon Dodsley <simon@purestorage.com>` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import textwrap -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, - mock_open, - MagicMock -) - # Import Salt Libs import salt.grains.nvme as nvme +from tests.support.mock import MagicMock, mock_open, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase class NvmeGrainsTestCase(TestCase): - ''' + """ Test cases for nvme grains - ''' + """ def test_linux_nvme_nqn_grains(self): - _nvme_file = textwrap.dedent('''\ + _nvme_file = textwrap.dedent( + """\ nqn.2014-08.org.nvmexpress:fc_lif:uuid:2cd61a74-17f9-4c22-b350-3020020c458d - ''') + """ + ) - with patch('salt.utils.files.fopen', mock_open(read_data=_nvme_file)): + with patch("salt.utils.files.fopen", mock_open(read_data=_nvme_file)): nqn = nvme._linux_nqn() assert isinstance(nqn, list) assert len(nqn) == 1 - assert nqn == ['nqn.2014-08.org.nvmexpress:fc_lif:uuid:2cd61a74-17f9-4c22-b350-3020020c458d'] + assert nqn == [ + "nqn.2014-08.org.nvmexpress:fc_lif:uuid:2cd61a74-17f9-4c22-b350-3020020c458d" + ] - @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(errno.EPERM, - 'The cables are not the same length.'))) - @patch('salt.grains.nvme.log', MagicMock()) + @patch( + "salt.utils.files.fopen", + MagicMock( + side_effect=IOError(errno.EPERM, "The cables are not the same length.") + ), + ) + @patch("salt.grains.nvme.log", MagicMock()) def test_linux_nqn_non_root(self): - ''' + """ Test if linux_nqn is running on salt-master as non-root and handling access denial properly. :return: - ''' + """ assert nvme._linux_nqn() == [] nvme.log.debug.assert_called() - assert 'Error while accessing' in nvme.log.debug.call_args[0][0] - assert 'cables are not the same' in nvme.log.debug.call_args[0][2].strerror + assert "Error while accessing" in nvme.log.debug.call_args[0][0] + assert "cables are not the same" in nvme.log.debug.call_args[0][2].strerror assert nvme.log.debug.call_args[0][2].errno == errno.EPERM - assert nvme.log.debug.call_args[0][1] == '/etc/nvme/hostnqn' + assert nvme.log.debug.call_args[0][1] == "/etc/nvme/hostnqn" - @patch('salt.utils.files.fopen', MagicMock(side_effect=IOError(errno.ENOENT, ''))) - @patch('salt.grains.nvme.log', MagicMock()) + @patch("salt.utils.files.fopen", MagicMock(side_effect=IOError(errno.ENOENT, ""))) + @patch("salt.grains.nvme.log", MagicMock()) def test_linux_nqn_no_nvme_initiator(self): - ''' + """ Test if linux_nqn is running on salt-master as root. nvme initiator is not there accessible or is not supported. :return: - ''' + """ assert nvme._linux_nqn() == [] nvme.log.debug.assert_not_called() diff --git a/tests/unit/modules/inspectlib/test_collector.py b/tests/unit/modules/inspectlib/test_collector.py index 677d0357e7f..3d767af0dbb 100644 --- a/tests/unit/modules/inspectlib/test_collector.py +++ b/tests/unit/modules/inspectlib/test_collector.py @@ -13,116 +13,128 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -''' +""" :codeauthor: Bo Maryniuk <bo@suse.de> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.helpers import no_symlinks -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import salt libs from salt.modules.inspectlib.collector import Inspector +from tests.support.helpers import no_symlinks +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf @skipIf(no_symlinks(), "Git missing 'core.symlinks=true' config") class InspectorCollectorTestCase(TestCase): - ''' + """ Test inspectlib:collector:Inspector - ''' + """ + def setUp(self): patcher = patch("os.mkdir", MagicMock()) patcher.start() self.addCleanup(patcher.stop) def test_env_loader(self): - ''' + """ Get packages on the different distros. :return: - ''' - cachedir = os.sep + os.sep.join(['foo', 'cache']) - piddir = os.sep + os.sep.join(['foo', 'pid']) - inspector = Inspector(cachedir=cachedir, piddir=piddir, pidfilename='bar.pid') + """ + cachedir = os.sep + os.sep.join(["foo", "cache"]) + piddir = os.sep + os.sep.join(["foo", "pid"]) + inspector = Inspector(cachedir=cachedir, piddir=piddir, pidfilename="bar.pid") self.assertEqual( inspector.dbfile, - os.sep + os.sep.join(['foo', 'cache', '_minion_collector.db'])) + os.sep + os.sep.join(["foo", "cache", "_minion_collector.db"]), + ) self.assertEqual( - inspector.pidfile, - os.sep + os.sep.join(['foo', 'pid', 'bar.pid'])) + inspector.pidfile, os.sep + os.sep.join(["foo", "pid", "bar.pid"]) + ) def test_file_tree(self): - ''' + """ Test file tree. :return: - ''' + """ - inspector = Inspector(cachedir=os.sep + 'test', - piddir=os.sep + 'test', - pidfilename='bar.pid') - tree_root = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tree_test') - expected_tree = ([os.sep + os.sep.join(['a', 'a', 'dummy.a']), - os.sep + os.sep.join(['a', 'b', 'dummy.b']), - os.sep + os.sep.join(['b', 'b.1']), - os.sep + os.sep.join(['b', 'b.2']), - os.sep + os.sep.join(['b', 'b.3'])], - [os.sep + 'a', - os.sep + os.sep.join(['a', 'a']), - os.sep + os.sep.join(['a', 'b']), - os.sep + os.sep.join(['a', 'c']), - os.sep + 'b', - os.sep + 'c'], - [os.sep + os.sep.join(['a', 'a', 'dummy.ln.a']), - os.sep + os.sep.join(['a', 'b', 'dummy.ln.b']), - os.sep + os.sep.join(['a', 'c', 'b.1']), - os.sep + os.sep.join(['b', 'b.4']), - os.sep + os.sep.join(['b', 'b.5']), - os.sep + os.sep.join(['c', 'b.1']), - os.sep + os.sep.join(['c', 'b.2']), - os.sep + os.sep.join(['c', 'b.3'])]) + inspector = Inspector( + cachedir=os.sep + "test", piddir=os.sep + "test", pidfilename="bar.pid" + ) + tree_root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "tree_test" + ) + expected_tree = ( + [ + os.sep + os.sep.join(["a", "a", "dummy.a"]), + os.sep + os.sep.join(["a", "b", "dummy.b"]), + os.sep + os.sep.join(["b", "b.1"]), + os.sep + os.sep.join(["b", "b.2"]), + os.sep + os.sep.join(["b", "b.3"]), + ], + [ + os.sep + "a", + os.sep + os.sep.join(["a", "a"]), + os.sep + os.sep.join(["a", "b"]), + os.sep + os.sep.join(["a", "c"]), + os.sep + "b", + os.sep + "c", + ], + [ + os.sep + os.sep.join(["a", "a", "dummy.ln.a"]), + os.sep + os.sep.join(["a", "b", "dummy.ln.b"]), + os.sep + os.sep.join(["a", "c", "b.1"]), + os.sep + os.sep.join(["b", "b.4"]), + os.sep + os.sep.join(["b", "b.5"]), + os.sep + os.sep.join(["c", "b.1"]), + os.sep + os.sep.join(["c", "b.2"]), + os.sep + os.sep.join(["c", "b.3"]), + ], + ) tree_result = [] for chunk in inspector._get_all_files(tree_root): buff = [] for pth in chunk: - buff.append(pth.replace(tree_root, '')) + buff.append(pth.replace(tree_root, "")) tree_result.append(buff) tree_result = tuple(tree_result) self.assertEqual(expected_tree, tree_result) def test_get_unmanaged_files(self): - ''' + """ Test get_unmanaged_files. :return: - ''' - inspector = Inspector(cachedir='/test', piddir='/test', pidfilename='bar.pid') + """ + inspector = Inspector(cachedir="/test", piddir="/test", pidfilename="bar.pid") managed = ( - ['a', 'b', 'c'], - ['d', 'e', 'f'], - ['g', 'h', 'i'], + ["a", "b", "c"], + ["d", "e", "f"], + ["g", "h", "i"], ) system_all = ( - ['a', 'b', 'c'], - ['d', 'E', 'f'], - ['G', 'H', 'i'], + ["a", "b", "c"], + ["d", "E", "f"], + ["G", "H", "i"], + ) + self.assertEqual( + inspector._get_unmanaged_files(managed=managed, system_all=system_all), + ([], ["E"], ["G", "H"]), ) - self.assertEqual(inspector._get_unmanaged_files(managed=managed, system_all=system_all), - ([], ['E'], ['G', 'H'])) def test_pkg_get(self): - ''' + """ Test if grains switching the pkg get method. :return: - ''' + """ debian_list = """ g++ g++-4.9 @@ -138,17 +150,21 @@ gcc-5-base:i386 gcc-6-base:amd64 gcc-6-base:i386 """ - inspector = Inspector(cachedir='/test', piddir='/test', pidfilename='bar.pid') + inspector = Inspector(cachedir="/test", piddir="/test", pidfilename="bar.pid") inspector.grains_core = MagicMock() inspector.grains_core.os_data = MagicMock() - inspector.grains_core.os_data.get = MagicMock(return_value='Debian') - with patch.object(inspector, '_Inspector__get_cfg_pkgs_dpkg', MagicMock(return_value='dpkg')): - with patch.object(inspector, '_Inspector__get_cfg_pkgs_rpm', MagicMock(return_value='rpm')): + inspector.grains_core.os_data.get = MagicMock(return_value="Debian") + with patch.object( + inspector, "_Inspector__get_cfg_pkgs_dpkg", MagicMock(return_value="dpkg") + ): + with patch.object( + inspector, "_Inspector__get_cfg_pkgs_rpm", MagicMock(return_value="rpm") + ): inspector.grains_core = MagicMock() inspector.grains_core.os_data = MagicMock() - inspector.grains_core.os_data().get = MagicMock(return_value='Debian') - self.assertEqual(inspector._get_cfg_pkgs(), 'dpkg') - inspector.grains_core.os_data().get = MagicMock(return_value='Suse') - self.assertEqual(inspector._get_cfg_pkgs(), 'rpm') - inspector.grains_core.os_data().get = MagicMock(return_value='redhat') - self.assertEqual(inspector._get_cfg_pkgs(), 'rpm') + inspector.grains_core.os_data().get = MagicMock(return_value="Debian") + self.assertEqual(inspector._get_cfg_pkgs(), "dpkg") + inspector.grains_core.os_data().get = MagicMock(return_value="Suse") + self.assertEqual(inspector._get_cfg_pkgs(), "rpm") + inspector.grains_core.os_data().get = MagicMock(return_value="redhat") + self.assertEqual(inspector._get_cfg_pkgs(), "rpm") diff --git a/tests/unit/modules/inspectlib/test_fsdb.py b/tests/unit/modules/inspectlib/test_fsdb.py index 3a3e2dd6fe0..8c91e5de000 100644 --- a/tests/unit/modules/inspectlib/test_fsdb.py +++ b/tests/unit/modules/inspectlib/test_fsdb.py @@ -14,33 +14,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -''' +""" :codeauthor: Bo Maryniuk <bo@suse.de> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import io -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - -from salt.modules.inspectlib.fsdb import CsvDB -from salt.modules.inspectlib.entities import CsvDBEntity -from salt.utils.odict import OrderedDict - from salt.ext import six from salt.ext.six.moves import StringIO +from salt.modules.inspectlib.entities import CsvDBEntity +from salt.modules.inspectlib.fsdb import CsvDB +from salt.utils.odict import OrderedDict +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase def mock_open(data=None): - ''' + """ Mock "open" function in a simple way. :param data: :return: - ''' + """ data = StringIO(data) mock = MagicMock(spec=io.FileIO) handle = MagicMock(spec=io.FileIO) @@ -70,19 +70,20 @@ class Writable(StringIO): class FoobarEntity(CsvDBEntity): - ''' + """ Entity for test purposes. - ''' - _TABLE = 'some_table' + """ + + _TABLE = "some_table" def __init__(self): self.foo = 0 - self.bar = '' - self.spam = 0. + self.bar = "" + self.spam = 0.0 class InspectorFSDBTestCase(TestCase): - ''' + """ Test case for the FSDB: FileSystem Database. FSDB is a very simple object-to-CSV storage with a very inefficient @@ -91,7 +92,7 @@ class InspectorFSDBTestCase(TestCase): Main advantage of FSDB is to store Python objects in just a CSV files, and have a very small code base. - ''' + """ def setUp(self): patcher = patch("os.makedirs", MagicMock()) @@ -99,89 +100,112 @@ class InspectorFSDBTestCase(TestCase): self.addCleanup(patcher.stop) def test_open(self): - ''' + """ Test opening the database. :return: - ''' - with patch("os.listdir", MagicMock(return_value=['test_db'])), \ - patch("gzip.open", mock_open("foo:int,bar:str")): - csvdb = CsvDB('/foobar') + """ + with patch("os.listdir", MagicMock(return_value=["test_db"])), patch( + "gzip.open", mock_open("foo:int,bar:str") + ): + csvdb = CsvDB("/foobar") csvdb.open() - assert list(csvdb.list_tables()) == ['test_db'] + assert list(csvdb.list_tables()) == ["test_db"] assert csvdb.is_closed() is False def test_close(self): - ''' + """ Test closing the database. :return: - ''' - with patch("os.listdir", MagicMock(return_value=['test_db'])), \ - patch("gzip.open", mock_open("foo:int,bar:str")): - csvdb = CsvDB('/foobar') + """ + with patch("os.listdir", MagicMock(return_value=["test_db"])), patch( + "gzip.open", mock_open("foo:int,bar:str") + ): + csvdb = CsvDB("/foobar") csvdb.open() csvdb.close() assert csvdb.is_closed() is True def test_create_table(self): - ''' + """ Test creating table. :return: - ''' - with patch("os.path.exists", MagicMock(return_value=False)), \ - patch("os.listdir", MagicMock(return_value=['some_table'])): + """ + with patch("os.path.exists", MagicMock(return_value=False)), patch( + "os.listdir", MagicMock(return_value=["some_table"]) + ): writable = Writable() with patch("gzip.open", MagicMock(return_value=writable)): - csvdb = CsvDB('/foobar') + csvdb = CsvDB("/foobar") csvdb.open() csvdb.create_table_from_object(FoobarEntity()) - sorted_writable_data = sorted(writable.data[0].strip().split(',')) + sorted_writable_data = sorted(writable.data[0].strip().split(",")) if six.PY2: - sorted_expected_data = sorted("foo:int,bar:unicode,spam:float".split(',')) + sorted_expected_data = sorted( + "foo:int,bar:unicode,spam:float".split(",") + ) else: - sorted_expected_data = sorted("foo:int,bar:str,spam:float".split(',')) + sorted_expected_data = sorted("foo:int,bar:str,spam:float".split(",")) self.assertEqual(sorted_writable_data, sorted_expected_data) def test_list_databases(self): - ''' + """ Test list databases. :return: - ''' - with patch("os.listdir", MagicMock(return_value=['test_db'])): - csvdb = CsvDB('/foobar') - assert csvdb.list() == ['test_db'] + """ + with patch("os.listdir", MagicMock(return_value=["test_db"])): + csvdb = CsvDB("/foobar") + assert csvdb.list() == ["test_db"] def test_add_object(self): - ''' + """ Test storing object into the database. :return: - ''' - with patch("os.path.exists", MagicMock(return_value=False)), \ - patch("os.listdir", MagicMock(return_value=['some_table'])): + """ + with patch("os.path.exists", MagicMock(return_value=False)), patch( + "os.listdir", MagicMock(return_value=["some_table"]) + ): writable = Writable() with patch("gzip.open", MagicMock(return_value=writable)): obj = FoobarEntity() obj.foo = 123 - obj.bar = 'test entity' + obj.bar = "test entity" obj.spam = 0.123 - csvdb = CsvDB('/foobar') + csvdb = CsvDB("/foobar") csvdb.open() - csvdb._tables = {'some_table': OrderedDict([tuple(elm.split(':')) - for elm in ["foo:int", "bar:str", "spam:float"]])} + csvdb._tables = { + "some_table": OrderedDict( + [ + tuple(elm.split(":")) + for elm in ["foo:int", "bar:str", "spam:float"] + ] + ) + } csvdb.store(obj) - assert writable.data[0].strip() == '123,test entity,0.123' + assert writable.data[0].strip() == "123,test entity,0.123" def test_delete_object(self): - ''' + """ Deleting an object from the store. :return: - ''' - with patch("gzip.open", MagicMock()), \ - patch("os.listdir", MagicMock(return_value=['test_db'])), \ - patch("csv.reader", MagicMock(return_value=iter([[], ['foo:int', 'bar:str', 'spam:float'], - ['123', 'test', '0.123'], - ['234', 'another', '0.456']]))): + """ + with patch("gzip.open", MagicMock()), patch( + "os.listdir", MagicMock(return_value=["test_db"]) + ), patch( + "csv.reader", + MagicMock( + return_value=iter( + [ + [], + ["foo:int", "bar:str", "spam:float"], + ["123", "test", "0.123"], + ["234", "another", "0.456"], + ] + ) + ), + ): + class InterceptedCsvDB(CsvDB): def __init__(self, path): CsvDB.__init__(self, path) @@ -190,31 +214,41 @@ class InspectorFSDBTestCase(TestCase): def store(self, obj, distinct=False): self._remained.append(obj) - csvdb = InterceptedCsvDB('/foobar') + csvdb = InterceptedCsvDB("/foobar") csvdb.open() csvdb.create_table_from_object = MagicMock() csvdb.flush = MagicMock() - assert csvdb.delete(FoobarEntity, eq={'foo': 123}) is True + assert csvdb.delete(FoobarEntity, eq={"foo": 123}) is True assert len(csvdb._remained) == 1 assert csvdb._remained[0].foo == 234 - assert csvdb._remained[0].bar == 'another' + assert csvdb._remained[0].bar == "another" assert csvdb._remained[0].spam == 0.456 def test_update_object(self): - ''' + """ Updating an object from the store. :return: - ''' - with patch("gzip.open", MagicMock()), \ - patch("os.listdir", MagicMock(return_value=['test_db'])), \ - patch("csv.reader", MagicMock(return_value=iter([[], ['foo:int', 'bar:str', 'spam:float'], - ['123', 'test', '0.123'], - ['234', 'another', '0.456']]))): + """ + with patch("gzip.open", MagicMock()), patch( + "os.listdir", MagicMock(return_value=["test_db"]) + ), patch( + "csv.reader", + MagicMock( + return_value=iter( + [ + [], + ["foo:int", "bar:str", "spam:float"], + ["123", "test", "0.123"], + ["234", "another", "0.456"], + ] + ) + ), + ): obj = FoobarEntity() obj.foo = 123 - obj.bar = 'updated' + obj.bar = "updated" obj.spam = 0.5 class InterceptedCsvDB(CsvDB): @@ -225,201 +259,299 @@ class InspectorFSDBTestCase(TestCase): def store(self, obj, distinct=False): self._remained.append(obj) - csvdb = InterceptedCsvDB('/foobar') + csvdb = InterceptedCsvDB("/foobar") csvdb.open() csvdb.create_table_from_object = MagicMock() csvdb.flush = MagicMock() - assert csvdb.update(obj, eq={'foo': 123}) is True + assert csvdb.update(obj, eq={"foo": 123}) is True assert len(csvdb._remained) == 2 assert csvdb._remained[0].foo == 123 - assert csvdb._remained[0].bar == 'updated' + assert csvdb._remained[0].bar == "updated" assert csvdb._remained[0].spam == 0.5 assert csvdb._remained[1].foo == 234 - assert csvdb._remained[1].bar == 'another' + assert csvdb._remained[1].bar == "another" assert csvdb._remained[1].spam == 0.456 def test_get_object(self): - ''' + """ Getting an object from the store. :return: - ''' - with patch("os.listdir", MagicMock(return_value=['test_db'])), \ - patch("gzip.open", MagicMock()), \ - patch("csv.reader", MagicMock(return_value=iter([[], ['foo:int', 'bar:str', 'spam:float'], - ['123', 'test', '0.123'], - ['234', 'another', '0.456']]))): - csvdb = CsvDB('/foobar') + """ + with patch("os.listdir", MagicMock(return_value=["test_db"])), patch( + "gzip.open", MagicMock() + ), patch( + "csv.reader", + MagicMock( + return_value=iter( + [ + [], + ["foo:int", "bar:str", "spam:float"], + ["123", "test", "0.123"], + ["234", "another", "0.456"], + ] + ) + ), + ): + csvdb = CsvDB("/foobar") csvdb.open() entities = csvdb.get(FoobarEntity) assert list == type(entities) assert len(entities) == 2 assert entities[0].foo == 123 - assert entities[0].bar == 'test' + assert entities[0].bar == "test" assert entities[0].spam == 0.123 assert entities[1].foo == 234 - assert entities[1].bar == 'another' + assert entities[1].bar == "another" assert entities[1].spam == 0.456 def test_get_obj_equals(self): - ''' + """ Getting an object from the store with conditions :return: - ''' - with patch("os.listdir", MagicMock(return_value=['test_db'])), \ - patch("gzip.open", MagicMock()), \ - patch("csv.reader", MagicMock(return_value=iter([[], ['foo:int', 'bar:str', 'spam:float'], - ['123', 'test', '0.123'], - ['234', 'another', '0.456']]))): - csvdb = CsvDB('/foobar') + """ + with patch("os.listdir", MagicMock(return_value=["test_db"])), patch( + "gzip.open", MagicMock() + ), patch( + "csv.reader", + MagicMock( + return_value=iter( + [ + [], + ["foo:int", "bar:str", "spam:float"], + ["123", "test", "0.123"], + ["234", "another", "0.456"], + ] + ) + ), + ): + csvdb = CsvDB("/foobar") csvdb.open() - entities = csvdb.get(FoobarEntity, eq={'foo': 123}) + entities = csvdb.get(FoobarEntity, eq={"foo": 123}) assert list == type(entities) assert len(entities) == 1 assert entities[0].foo == 123 - assert entities[0].bar == 'test' + assert entities[0].bar == "test" assert entities[0].spam == 0.123 def test_get_obj_more_than(self): - ''' + """ Getting an object from the store with conditions :return: - ''' - with patch("gzip.open", MagicMock()), \ - patch("os.listdir", MagicMock(return_value=['test_db'])), \ - patch("csv.reader", MagicMock(return_value=iter([[], ['foo:int', 'bar:str', 'spam:float'], - ['123', 'test', '0.123'], - ['234', 'another', '0.456']]))): - csvdb = CsvDB('/foobar') + """ + with patch("gzip.open", MagicMock()), patch( + "os.listdir", MagicMock(return_value=["test_db"]) + ), patch( + "csv.reader", + MagicMock( + return_value=iter( + [ + [], + ["foo:int", "bar:str", "spam:float"], + ["123", "test", "0.123"], + ["234", "another", "0.456"], + ] + ) + ), + ): + csvdb = CsvDB("/foobar") csvdb.open() - entities = csvdb.get(FoobarEntity, mt={'foo': 123}) + entities = csvdb.get(FoobarEntity, mt={"foo": 123}) assert list == type(entities) assert len(entities) == 1 assert entities[0].foo == 234 - assert entities[0].bar == 'another' + assert entities[0].bar == "another" assert entities[0].spam == 0.456 def test_get_obj_less_than(self): - ''' + """ Getting an object from the store with conditions :return: - ''' - with patch("gzip.open", MagicMock()), \ - patch("os.listdir", MagicMock(return_value=['test_db'])), \ - patch("csv.reader", MagicMock(return_value=iter([[], ['foo:int', 'bar:str', 'spam:float'], - ['123', 'test', '0.123'], - ['234', 'another', '0.456']]))): - csvdb = CsvDB('/foobar') + """ + with patch("gzip.open", MagicMock()), patch( + "os.listdir", MagicMock(return_value=["test_db"]) + ), patch( + "csv.reader", + MagicMock( + return_value=iter( + [ + [], + ["foo:int", "bar:str", "spam:float"], + ["123", "test", "0.123"], + ["234", "another", "0.456"], + ] + ) + ), + ): + csvdb = CsvDB("/foobar") csvdb.open() - entities = csvdb.get(FoobarEntity, lt={'foo': 234}) + entities = csvdb.get(FoobarEntity, lt={"foo": 234}) assert list == type(entities) assert len(entities) == 1 assert entities[0].foo == 123 - assert entities[0].bar == 'test' + assert entities[0].bar == "test" assert entities[0].spam == 0.123 def test_get_obj_matching(self): - ''' + """ Getting an object from the store with conditions :return: - ''' - with patch("gzip.open", MagicMock()), \ - patch("os.listdir", MagicMock(return_value=['test_db'])), \ - patch("csv.reader", MagicMock(return_value=iter([[], ['foo:int', 'bar:str', 'spam:float'], - ['123', 'this is test of something', '0.123'], - ['234', 'another test of stuff', '0.456']]))): - csvdb = CsvDB('/foobar') + """ + with patch("gzip.open", MagicMock()), patch( + "os.listdir", MagicMock(return_value=["test_db"]) + ), patch( + "csv.reader", + MagicMock( + return_value=iter( + [ + [], + ["foo:int", "bar:str", "spam:float"], + ["123", "this is test of something", "0.123"], + ["234", "another test of stuff", "0.456"], + ] + ) + ), + ): + csvdb = CsvDB("/foobar") csvdb.open() - entities = csvdb.get(FoobarEntity, matches={'bar': r'is\stest'}) + entities = csvdb.get(FoobarEntity, matches={"bar": r"is\stest"}) assert list == type(entities) assert len(entities) == 1 assert entities[0].foo == 123 - assert entities[0].bar == 'this is test of something' + assert entities[0].bar == "this is test of something" assert entities[0].spam == 0.123 def test_obj_serialization(self): - ''' + """ Test object serialization. :return: - ''' + """ obj = FoobarEntity() obj.foo = 123 - obj.bar = 'test entity' + obj.bar = "test entity" obj.spam = 0.123 - descr = OrderedDict([tuple(elm.split(':')) for elm in ["foo:int", "bar:str", "spam:float"]]) - assert obj._serialize(descr) == [123, 'test entity', 0.123] + descr = OrderedDict( + [tuple(elm.split(":")) for elm in ["foo:int", "bar:str", "spam:float"]] + ) + assert obj._serialize(descr) == [123, "test entity", 0.123] def test_obj_validation(self): - ''' + """ Test object validation. :return: - ''' - with patch("os.path.exists", MagicMock(return_value=False)), \ - patch("os.listdir", MagicMock(return_value=['some_table'])): + """ + with patch("os.path.exists", MagicMock(return_value=False)), patch( + "os.listdir", MagicMock(return_value=["some_table"]) + ): obj = FoobarEntity() obj.foo = 123 - obj.bar = 'test entity' + obj.bar = "test entity" obj.spam = 0.123 - csvdb = CsvDB('/foobar') - csvdb._tables = {'some_table': OrderedDict([tuple(elm.split(':')) - for elm in ["foo:int", "bar:str", "spam:float"]])} - assert csvdb._validate_object(obj) == [123, 'test entity', 0.123] + csvdb = CsvDB("/foobar") + csvdb._tables = { + "some_table": OrderedDict( + [ + tuple(elm.split(":")) + for elm in ["foo:int", "bar:str", "spam:float"] + ] + ) + } + assert csvdb._validate_object(obj) == [123, "test entity", 0.123] def test_criteria(self): - ''' + """ Test criteria selector. :return: - ''' - with patch("os.path.exists", MagicMock(return_value=False)), \ - patch("os.listdir", MagicMock(return_value=['some_table'])): + """ + with patch("os.path.exists", MagicMock(return_value=False)), patch( + "os.listdir", MagicMock(return_value=["some_table"]) + ): obj = FoobarEntity() obj.foo = 123 - obj.bar = 'test entity' + obj.bar = "test entity" obj.spam = 0.123 obj.pi = 3.14 - cmp = CsvDB('/foobar')._CsvDB__criteria + cmp = CsvDB("/foobar")._CsvDB__criteria # Single - assert cmp(obj, eq={'foo': 123}) is True - assert cmp(obj, lt={'foo': 124}) is True - assert cmp(obj, mt={'foo': 122}) is True + assert cmp(obj, eq={"foo": 123}) is True + assert cmp(obj, lt={"foo": 124}) is True + assert cmp(obj, mt={"foo": 122}) is True - assert cmp(obj, eq={'foo': 0}) is False - assert cmp(obj, lt={'foo': 123}) is False - assert cmp(obj, mt={'foo': 123}) is False + assert cmp(obj, eq={"foo": 0}) is False + assert cmp(obj, lt={"foo": 123}) is False + assert cmp(obj, mt={"foo": 123}) is False - assert cmp(obj, matches={'bar': r't\se.*?'}) is True - assert cmp(obj, matches={'bar': r'\s\sentity'}) is False + assert cmp(obj, matches={"bar": r"t\se.*?"}) is True + assert cmp(obj, matches={"bar": r"\s\sentity"}) is False # Combined - assert cmp(obj, eq={'foo': 123, 'bar': r'test entity', 'spam': 0.123}) is True - assert cmp(obj, eq={'foo': 123, 'bar': r'test', 'spam': 0.123}) is False + assert ( + cmp(obj, eq={"foo": 123, "bar": r"test entity", "spam": 0.123}) is True + ) + assert cmp(obj, eq={"foo": 123, "bar": r"test", "spam": 0.123}) is False - assert cmp(obj, lt={'foo': 124, 'spam': 0.124}) is True - assert cmp(obj, lt={'foo': 124, 'spam': 0.123}) is False + assert cmp(obj, lt={"foo": 124, "spam": 0.124}) is True + assert cmp(obj, lt={"foo": 124, "spam": 0.123}) is False - assert cmp(obj, mt={'foo': 122, 'spam': 0.122}) is True - assert cmp(obj, mt={'foo': 122, 'spam': 0.123}) is False + assert cmp(obj, mt={"foo": 122, "spam": 0.122}) is True + assert cmp(obj, mt={"foo": 122, "spam": 0.123}) is False - assert cmp(obj, matches={'bar': r'test'}, mt={'foo': 122}, lt={'spam': 0.124}, eq={'pi': 3.14}) is True + assert ( + cmp( + obj, + matches={"bar": r"test"}, + mt={"foo": 122}, + lt={"spam": 0.124}, + eq={"pi": 3.14}, + ) + is True + ) - assert cmp(obj, matches={'bar': r'^test.*?y$'}, mt={'foo': 122}, lt={'spam': 0.124}, eq={'pi': 3.14}) is True - assert cmp(obj, matches={'bar': r'^ent'}, mt={'foo': 122}, lt={'spam': 0.124}, eq={'pi': 3.14}) is False - assert cmp(obj, matches={'bar': r'^test.*?y$'}, mt={'foo': 123}, lt={'spam': 0.124}, eq={'pi': 3.14}) is False + assert ( + cmp( + obj, + matches={"bar": r"^test.*?y$"}, + mt={"foo": 122}, + lt={"spam": 0.124}, + eq={"pi": 3.14}, + ) + is True + ) + assert ( + cmp( + obj, + matches={"bar": r"^ent"}, + mt={"foo": 122}, + lt={"spam": 0.124}, + eq={"pi": 3.14}, + ) + is False + ) + assert ( + cmp( + obj, + matches={"bar": r"^test.*?y$"}, + mt={"foo": 123}, + lt={"spam": 0.124}, + eq={"pi": 3.14}, + ) + is False + ) diff --git a/tests/unit/modules/test_acme.py b/tests/unit/modules/test_acme.py index f5ade73a2bb..7d3b97df118 100644 --- a/tests/unit/modules/test_acme.py +++ b/tests/unit/modules/test_acme.py @@ -1,99 +1,139 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Herbert Buurman <herbert.buurman@ogd.nl> -''' +""" # Import future libs from __future__ import absolute_import, print_function, unicode_literals -# Import Python libs -import textwrap import datetime import os -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +# Import Python libs +import textwrap # Import Salt Module import salt.modules.acme as acme import salt.utils.dictupdate from salt.exceptions import SaltInvocationError +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class AcmeTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.acme - ''' + """ def setup_loader_modules(self): return {acme: {}} def test_certs(self): - ''' + """ Test listing certs - ''' - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'file.readdir': MagicMock(return_value=['.', '..', 'README', 'test_expired', 'test_valid']) - }), \ - patch('os.path.isdir', side_effect=[False, True, True]): - self.assertEqual(acme.certs(), ['test_expired', 'test_valid']) + """ + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "file.readdir": MagicMock( + return_value=[".", "..", "README", "test_expired", "test_valid"] + ) + }, + ), patch("os.path.isdir", side_effect=[False, True, True]): + self.assertEqual(acme.certs(), ["test_expired", "test_valid"]) def test_has(self): - ''' + """ Test checking if certificate (does not) exist. - ''' - with patch.dict(acme.__salt__, {'file.file_exists': MagicMock(return_value=True)}): # pylint: disable=no-member - self.assertTrue(acme.has('test_expired')) - with patch.dict(acme.__salt__, {'file.file_exists': MagicMock(return_value=False)}): # pylint: disable=no-member - self.assertFalse(acme.has('test_invalid')) + """ + with patch.dict( + acme.__salt__, {"file.file_exists": MagicMock(return_value=True)} + ): # pylint: disable=no-member + self.assertTrue(acme.has("test_expired")) + with patch.dict( + acme.__salt__, {"file.file_exists": MagicMock(return_value=False)} + ): # pylint: disable=no-member + self.assertFalse(acme.has("test_invalid")) def test_needs_renewal(self): - ''' + """ Test if expired certs do indeed need renewal. - ''' - expired = datetime.date.today() - datetime.timedelta(days=3) - datetime.date(1970, 1, 1) - valid = datetime.date.today() + datetime.timedelta(days=3) - datetime.date(1970, 1, 1) - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'tls.cert_info': MagicMock(return_value={'not_after': expired.total_seconds()}) - }): - self.assertTrue(acme.needs_renewal('test_expired')) - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'tls.cert_info': MagicMock(return_value={'not_after': valid.total_seconds()}) - }): - self.assertFalse(acme.needs_renewal('test_valid')) + """ + expired = ( + datetime.date.today() + - datetime.timedelta(days=3) + - datetime.date(1970, 1, 1) + ) + valid = ( + datetime.date.today() + + datetime.timedelta(days=3) + - datetime.date(1970, 1, 1) + ) + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "tls.cert_info": MagicMock( + return_value={"not_after": expired.total_seconds()} + ) + }, + ): + self.assertTrue(acme.needs_renewal("test_expired")) + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "tls.cert_info": MagicMock( + return_value={"not_after": valid.total_seconds()} + ) + }, + ): + self.assertFalse(acme.needs_renewal("test_valid")) # Test with integer window parameter - self.assertTrue(acme.needs_renewal('test_valid', window=5)) + self.assertTrue(acme.needs_renewal("test_valid", window=5)) # Test with string-like window parameter - self.assertTrue(acme.needs_renewal('test_valid', window='5')) + self.assertTrue(acme.needs_renewal("test_valid", window="5")) + # Test with 'force' parameter + self.assertTrue(acme.needs_renewal("test_valid", window="force")) + # Test with 'true' parameter + self.assertTrue(acme.needs_renewal("test_valid", window=True)) # Test with invalid window parameter - self.assertRaises(SaltInvocationError, acme.needs_renewal, 'test_valid', window='foo') + self.assertRaises( + SaltInvocationError, acme.needs_renewal, "test_valid", window="foo" + ) def test_expires(self): - ''' + """ Test if expires function functions properly. - ''' + """ test_value = datetime.datetime.today() - datetime.timedelta(days=3) test_stamp = test_value - datetime.datetime(1970, 1, 1) - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'tls.cert_info': MagicMock(return_value={'not_after': test_stamp.total_seconds()}) - }): + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "tls.cert_info": MagicMock( + return_value={"not_after": test_stamp.total_seconds()} + ) + }, + ): self.assertEqual( - acme.expires('test_expired'), - datetime.datetime.fromtimestamp(test_stamp.total_seconds()).isoformat() + acme.expires("test_expired"), + datetime.datetime.fromtimestamp(test_stamp.total_seconds()).isoformat(), ) def test_info(self): - ''' + """ Test certificate information retrieval. - ''' + """ certinfo_tls_result = { "not_after": 1559471377, "signature_algorithm": "sha256WithRSAEncryption", "extensions": {}, - "fingerprint": ("FB:A4:5F:71:D6:5D:6C:B6:1D:2C:FD:91:09:2C:1C:52:" - "3C:EC:B6:4D:1A:95:65:37:04:D0:E2:5E:C7:64:0C:9C"), + "fingerprint": ( + "FB:A4:5F:71:D6:5D:6C:B6:1D:2C:FD:91:09:2C:1C:52:" + "3C:EC:B6:4D:1A:95:65:37:04:D0:E2:5E:C7:64:0C:9C" + ), "serial_number": 6461481982668892235, "issuer": {}, "not_before": 1559557777, @@ -104,52 +144,72 @@ class AcmeTestCase(TestCase, LoaderModuleMockMixin): "Subject Hash": "54:3B:6C:A4", "Serial Number": "59:AB:CB:A0:FB:90:E8:4B", "SHA1 Finger Print": "F1:8D:F3:26:1B:D3:88:32:CD:B6:FA:3B:85:58:DA:C7:6F:62:BE:7E", - "SHA-256 Finger Print": ("FB:A4:5F:71:D6:5D:6C:B6:1D:2C:FD:91:09:2C:1C:52:" - "3C:EC:B6:4D:1A:95:65:37:04:D0:E2:5E:C7:64:0C:9C"), + "SHA-256 Finger Print": ( + "FB:A4:5F:71:D6:5D:6C:B6:1D:2C:FD:91:09:2C:1C:52:" + "3C:EC:B6:4D:1A:95:65:37:04:D0:E2:5E:C7:64:0C:9C" + ), "MD5 Finger Print": "95:B5:96:9B:42:A5:9E:20:78:FD:99:09:4B:21:1E:97", "Version": 3, "Key Size": 2048, - "Public Key": ("-----BEGIN PUBLIC KEY-----\n" - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsVO2vwQPKU92PSBnuGid\n" - "k8t6KWVE2jEBM10u7CgqQmD/JCnYflEHAo1nOsD7wxdhBrxhf5Qs+pEX1HOsh8VA\n" - "HDTim0iE8nQVJ0Iuen2SrwaWMhwKmZTSJRYMgd46oCMi2RdlCvcgF2Hw6RTwF7FT\n" - "hnksc4HBT91XddnP32N558tOT3YejafQNvClz5WcR+E0JzqGrV/+wfe3o+j/q5eK\n" - "UowttWazeSMvuROtqj/fEk0rop4D14pgzZqWi30tjwhJNl6fSPFWBrLEHGNyDJ+O\n" - "zfov0B2MRLJibH7GMkOCwsP2g1lVOReqcml+ju6zAKW8nHBTRg0iXB18Ifxef57Y\n" - "AQIDAQAB\n" - "-----END PUBLIC KEY-----\n"), + "Public Key": ( + "-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsVO2vwQPKU92PSBnuGid\n" + "k8t6KWVE2jEBM10u7CgqQmD/JCnYflEHAo1nOsD7wxdhBrxhf5Qs+pEX1HOsh8VA\n" + "HDTim0iE8nQVJ0Iuen2SrwaWMhwKmZTSJRYMgd46oCMi2RdlCvcgF2Hw6RTwF7FT\n" + "hnksc4HBT91XddnP32N558tOT3YejafQNvClz5WcR+E0JzqGrV/+wfe3o+j/q5eK\n" + "UowttWazeSMvuROtqj/fEk0rop4D14pgzZqWi30tjwhJNl6fSPFWBrLEHGNyDJ+O\n" + "zfov0B2MRLJibH7GMkOCwsP2g1lVOReqcml+ju6zAKW8nHBTRg0iXB18Ifxef57Y\n" + "AQIDAQAB\n" + "-----END PUBLIC KEY-----\n" + ), "Issuer": {}, "Issuer Hash": "54:3B:6C:A4", "Not Before": "2019-06-03 10:29:37", - "Subject": {} + "Subject": {}, } - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'file.file_exists': MagicMock(return_value=True), - 'tls.cert_info': MagicMock(return_value=certinfo_tls_result), - }): - self.assertEqual(acme.info('test'), certinfo_tls_result) - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'file.file_exists': MagicMock(return_value=True), - 'x509.read_certificate': MagicMock(return_value=certinfo_x509_result), - }): - self.assertEqual(acme.info('test'), certinfo_x509_result) - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'file.file_exists': MagicMock(return_value=True), - 'cmd.run': MagicMock(return_value='foo'), - }): - self.assertEqual(acme.info('test'), {'text': 'foo'}) + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "file.file_exists": MagicMock(return_value=True), + "tls.cert_info": MagicMock(return_value=certinfo_tls_result), + }, + ): + self.assertEqual(acme.info("test"), certinfo_tls_result) + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "file.file_exists": MagicMock(return_value=True), + "x509.read_certificate": MagicMock(return_value=certinfo_x509_result), + }, + ): + self.assertEqual(acme.info("test"), certinfo_x509_result) + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "file.file_exists": MagicMock(return_value=True), + "cmd.run": MagicMock(return_value="foo"), + }, + ): + self.assertEqual(acme.info("test"), {"text": "foo"}) def test_cert(self): - ''' + """ Test certificate retrieval/renewal - ''' - valid_timestamp = (datetime.datetime.now() + datetime.timedelta(days=30) - - datetime.datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds() - expired_timestamp = (datetime.datetime.now() - datetime.timedelta(days=3) - - datetime.datetime(1970, 1, 1, 0, 0, 0, 0)).total_seconds() + """ + valid_timestamp = ( + datetime.datetime.now() + + datetime.timedelta(days=30) + - datetime.datetime(1970, 1, 1, 0, 0, 0, 0) + ).total_seconds() + expired_timestamp = ( + datetime.datetime.now() + - datetime.timedelta(days=3) + - datetime.datetime(1970, 1, 1, 0, 0, 0, 0) + ).total_seconds() cmd_new_cert = { - 'stdout': textwrap.dedent(''' + "stdout": textwrap.dedent( + """ IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/test/fullchain.pem @@ -163,91 +223,108 @@ class AcmeTestCase(TestCase, LoaderModuleMockMixin): Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le - '''), - 'stderr': textwrap.dedent(''' + """ + ), + "stderr": textwrap.dedent( + """ Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator standalone, Installer None Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org Obtaining a new certificate Resetting dropped connection: acme-v02.api.letsencrypt.org - '''), - 'retcode': 0, + """ + ), + "retcode": 0, } result_new_cert = { "comment": "Certificate test obtained", "not_after": datetime.datetime.fromtimestamp(valid_timestamp).isoformat(), - "changes": { - "mode": "0640" - }, - "result": True + "changes": {"mode": "0640"}, + "result": True, } cmd_no_renew = { - 'stdout': textwrap.dedent(''' + "stdout": textwrap.dedent( + """ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Certificate not yet due for renewal; no action taken. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - '''), - 'stderr': textwrap.dedent('''Saving debug log to /var/log/letsencrypt/letsencrypt.log + """ + ), + "stderr": textwrap.dedent( + """Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator standalone, Installer None Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org Cert not yet due for renewal Keeping the existing certificate - '''), - 'retcode': 0 + """ + ), + "retcode": 0, } result_no_renew = { - "comment": "Certificate " + os.path.join('/etc/letsencrypt/live/test', 'cert.pem') + " unchanged", + "comment": "Certificate " + + os.path.join("/etc/letsencrypt/live/test", "cert.pem") + + " unchanged", "not_after": datetime.datetime.fromtimestamp(valid_timestamp).isoformat(), "changes": {}, - "result": True + "result": True, } result_renew = { "comment": "Certificate test renewed", "not_after": datetime.datetime.fromtimestamp(expired_timestamp).isoformat(), "changes": {}, - "result": True + "result": True, } # Test fetching new certificate - with patch('salt.modules.acme.LEA', 'certbot'), \ - patch.dict(acme.__salt__, { # pylint: disable=no-member - 'cmd.run_all': MagicMock(return_value=cmd_new_cert), - 'file.file_exists': MagicMock(return_value=False), - 'tls.cert_info': MagicMock(return_value={'not_after': valid_timestamp}), - 'file.check_perms': MagicMock( - side_effect=lambda a, x, b, c, d, follow_symlinks: ( - salt.utils.dictupdate.set_dict_key_value(x, 'changes:mode', '0640'), - None - ) + with patch("salt.modules.acme.LEA", "certbot"), patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "cmd.run_all": MagicMock(return_value=cmd_new_cert), + "file.file_exists": MagicMock(return_value=False), + "tls.cert_info": MagicMock(return_value={"not_after": valid_timestamp}), + "file.check_perms": MagicMock( + side_effect=lambda a, x, b, c, d, follow_symlinks: ( + salt.utils.dictupdate.set_dict_key_value( + x, "changes:mode", "0640" + ), + None, ) - }): - self.assertEqual(acme.cert('test'), result_new_cert) + ), + }, + ): + self.assertEqual(acme.cert("test"), result_new_cert) # Test not renewing a valid certificate - with patch('salt.modules.acme.LEA', 'certbot'), \ - patch.dict(acme.__salt__, { # pylint: disable=no-member - 'cmd.run_all': MagicMock(return_value=cmd_no_renew), - 'file.file_exists': MagicMock(return_value=True), - 'tls.cert_info': MagicMock(return_value={'not_after': valid_timestamp}), - 'file.check_perms': MagicMock( - side_effect=lambda a, x, b, c, d, follow_symlinks: ( - salt.utils.dictupdate.set_dict_key_value(x, 'result', True), - None - ) + with patch("salt.modules.acme.LEA", "certbot"), patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "cmd.run_all": MagicMock(return_value=cmd_no_renew), + "file.file_exists": MagicMock(return_value=True), + "tls.cert_info": MagicMock(return_value={"not_after": valid_timestamp}), + "file.check_perms": MagicMock( + side_effect=lambda a, x, b, c, d, follow_symlinks: ( + salt.utils.dictupdate.set_dict_key_value(x, "result", True), + None, ) - }): - self.assertEqual(acme.cert('test'), result_no_renew) + ), + }, + ): + self.assertEqual(acme.cert("test"), result_no_renew) # Test renewing an expired certificate - with patch('salt.modules.acme.LEA', 'certbot'), \ - patch.dict(acme.__salt__, { # pylint: disable=no-member - 'cmd.run_all': MagicMock(return_value=cmd_new_cert), - 'file.file_exists': MagicMock(return_value=True), - 'tls.cert_info': MagicMock(return_value={'not_after': expired_timestamp}), - 'file.check_perms': MagicMock( - side_effect=lambda a, x, b, c, d, follow_symlinks: ( - salt.utils.dictupdate.set_dict_key_value(x, 'result', True), - None - ) + with patch("salt.modules.acme.LEA", "certbot"), patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "cmd.run_all": MagicMock(return_value=cmd_new_cert), + "file.file_exists": MagicMock(return_value=True), + "tls.cert_info": MagicMock( + return_value={"not_after": expired_timestamp} + ), + "file.check_perms": MagicMock( + side_effect=lambda a, x, b, c, d, follow_symlinks: ( + salt.utils.dictupdate.set_dict_key_value(x, "result", True), + None, ) - }): - self.assertEqual(acme.cert('test'), result_renew) + ), + }, + ): + self.assertEqual(acme.cert("test"), result_renew) diff --git a/tests/unit/modules/test_aliases.py b/tests/unit/modules/test_aliases.py index a9217393ffc..4b1b8e488d8 100644 --- a/tests/unit/modules/test_aliases.py +++ b/tests/unit/modules/test_aliases.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -12,157 +12,181 @@ from salt.exceptions import SaltInvocationError # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class AliasesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.aliases module - ''' + """ - mock_alias = [('foo', 'bar@example.com', '')] - mock_alias_mult = [('foo', 'bar@example.com', ''), - ('hello', 'world@earth.com, earth@world.com', '')] + mock_alias = [("foo", "bar@example.com", "")] + mock_alias_mult = [ + ("foo", "bar@example.com", ""), + ("hello", "world@earth.com, earth@world.com", ""), + ] def setup_loader_modules(self): return {aliases: {}} def test_list_aliases(self): - ''' + """ Tests the return of a file containing one alias - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias)): - ret = {'foo': 'bar@example.com'} + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias), + ): + ret = {"foo": "bar@example.com"} self.assertEqual(aliases.list_aliases(), ret) def test_list_aliases_mult(self): - ''' + """ Tests the return of a file containing multiple aliases - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias_mult)): - ret = {'foo': 'bar@example.com', - 'hello': 'world@earth.com, earth@world.com'} + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias_mult), + ): + ret = { + "foo": "bar@example.com", + "hello": "world@earth.com, earth@world.com", + } self.assertEqual(aliases.list_aliases(), ret) def test_get_target(self): - ''' + """ Tests the target returned by an alias with one target - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias)): - ret = 'bar@example.com' - self.assertEqual(aliases.get_target('foo'), ret) + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias), + ): + ret = "bar@example.com" + self.assertEqual(aliases.get_target("foo"), ret) def test_get_target_mult(self): - ''' + """ Tests the target returned by an alias with multiple targets - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias_mult)): - ret = 'world@earth.com, earth@world.com' - self.assertEqual(aliases.get_target('hello'), ret) + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias_mult), + ): + ret = "world@earth.com, earth@world.com" + self.assertEqual(aliases.get_target("hello"), ret) def test_get_target_no_alias(self): - ''' + """ Tests return of an alias doesn't exist - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias)): - self.assertEqual(aliases.get_target('pizza'), '') + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias), + ): + self.assertEqual(aliases.get_target("pizza"), "") def test_has_target(self): - ''' + """ Tests simple return known alias and target - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias)): - ret = aliases.has_target('foo', 'bar@example.com') + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias), + ): + ret = aliases.has_target("foo", "bar@example.com") self.assertTrue(ret) def test_has_target_no_alias(self): - ''' + """ Tests return of empty alias and known target - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias)): - ret = aliases.has_target('', 'bar@example.com') + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias), + ): + ret = aliases.has_target("", "bar@example.com") self.assertFalse(ret) def test_has_target_no_target(self): - ''' + """ Tests return of known alias and empty target - ''' - self.assertRaises(SaltInvocationError, aliases.has_target, 'foo', '') + """ + self.assertRaises(SaltInvocationError, aliases.has_target, "foo", "") def test_has_target_mult(self): - ''' + """ Tests return of multiple targets to one alias - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias_mult)): - ret = aliases.has_target('hello', - 'world@earth.com, earth@world.com') + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias_mult), + ): + ret = aliases.has_target("hello", "world@earth.com, earth@world.com") self.assertTrue(ret) def test_has_target_mult_differs(self): - ''' + """ Tests return of multiple targets to one alias in opposite order - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias_mult)): - ret = aliases.has_target('hello', - 'earth@world.com, world@earth.com') + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias_mult), + ): + ret = aliases.has_target("hello", "earth@world.com, world@earth.com") self.assertFalse(ret) def test_has_target_list_mult(self): - ''' + """ Tests return of target as same list to know alias - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias_mult)): - ret = aliases.has_target('hello', ['world@earth.com', - 'earth@world.com']) + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias_mult), + ): + ret = aliases.has_target("hello", ["world@earth.com", "earth@world.com"]) self.assertTrue(ret) def test_has_target_list_mult_differs(self): - ''' + """ Tests return of target as differing list to known alias - ''' - with patch('salt.modules.aliases.__parse_aliases', - MagicMock(return_value=self.mock_alias_mult)): - ret = aliases.has_target('hello', ['world@earth.com', - 'mars@space.com']) + """ + with patch( + "salt.modules.aliases.__parse_aliases", + MagicMock(return_value=self.mock_alias_mult), + ): + ret = aliases.has_target("hello", ["world@earth.com", "mars@space.com"]) self.assertFalse(ret) def test_set_target_equal(self): - ''' + """ Tests return when target is already present - ''' - with patch('salt.modules.aliases.get_target', MagicMock(return_value='bar@example.com')): - alias = 'foo' - target = 'bar@example.com' + """ + with patch( + "salt.modules.aliases.get_target", MagicMock(return_value="bar@example.com") + ): + alias = "foo" + target = "bar@example.com" ret = aliases.set_target(alias, target) self.assertTrue(ret) def test_set_target_empty_alias(self): - ''' + """ Tests return of empty alias - ''' - self.assertRaises(SaltInvocationError, aliases.set_target, '', 'foo') + """ + self.assertRaises(SaltInvocationError, aliases.set_target, "", "foo") def test_set_target_empty_target(self): - ''' + """ Tests return of known alias and empty target - ''' - self.assertRaises(SaltInvocationError, aliases.set_target, 'foo', '') + """ + self.assertRaises(SaltInvocationError, aliases.set_target, "foo", "") def test_rm_alias_absent(self): - ''' + """ Tests return when alias is not present - ''' - with patch('salt.modules.aliases.get_target', MagicMock(return_value='')): - ret = aliases.rm_alias('foo') + """ + with patch("salt.modules.aliases.get_target", MagicMock(return_value="")): + ret = aliases.rm_alias("foo") self.assertTrue(ret) diff --git a/tests/unit/modules/test_alternatives.py b/tests/unit/modules/test_alternatives.py index 06ac44d9eb9..e9951f323a3 100644 --- a/tests/unit/modules/test_alternatives.py +++ b/tests/unit/modules/test_alternatives.py @@ -1,195 +1,195 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.unit.modules.alternatives_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase +# Import salt libs +import salt.modules.alternatives as alternatives from tests.support.helpers import TstSuiteLoggingHandler from tests.support.mixins import LoaderModuleMockMixin from tests.support.mock import MagicMock, patch -# Import salt libs -import salt.modules.alternatives as alternatives +# Import Salt Testing libs +from tests.support.unit import TestCase class AlternativesTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {alternatives: {}} def test_display(self): - with patch.dict(alternatives.__grains__, {'os_family': 'RedHat'}): - mock = MagicMock(return_value={'retcode': 0, 'stdout': 'salt'}) - with patch.dict(alternatives.__salt__, {'cmd.run_all': mock}): - solution = alternatives.display('better-world') - self.assertEqual('salt', solution) + with patch.dict(alternatives.__grains__, {"os_family": "RedHat"}): + mock = MagicMock(return_value={"retcode": 0, "stdout": "salt"}) + with patch.dict(alternatives.__salt__, {"cmd.run_all": mock}): + solution = alternatives.display("better-world") + self.assertEqual("salt", solution) mock.assert_called_once_with( - ['alternatives', '--display', 'better-world'], - python_shell=False + ["alternatives", "--display", "better-world"], + ignore_retcode=True, + python_shell=False, ) - with patch.dict(alternatives.__grains__, {'os_family': 'Suse'}): - mock = MagicMock( - return_value={'retcode': 0, 'stdout': 'undoubtedly-salt'} - ) - with patch.dict(alternatives.__salt__, {'cmd.run_all': mock}): - solution = alternatives.display('better-world') - self.assertEqual('undoubtedly-salt', solution) + with patch.dict(alternatives.__grains__, {"os_family": "Suse"}): + mock = MagicMock(return_value={"retcode": 0, "stdout": "undoubtedly-salt"}) + with patch.dict(alternatives.__salt__, {"cmd.run_all": mock}): + solution = alternatives.display("better-world") + self.assertEqual("undoubtedly-salt", solution) mock.assert_called_once_with( - ['update-alternatives', '--display', 'better-world'], - python_shell=False + ["update-alternatives", "--display", "better-world"], + ignore_retcode=True, + python_shell=False, ) - with patch.dict(alternatives.__grains__, {'os_family': 'RedHat'}): + with patch.dict(alternatives.__grains__, {"os_family": "RedHat"}): mock = MagicMock( - return_value={ - 'retcode': 1, - 'stdout': 'salt-out', - 'stderr': 'salt-err' - } + return_value={"retcode": 1, "stdout": "salt-out", "stderr": "salt-err"} ) - with patch.dict(alternatives.__salt__, {'cmd.run_all': mock}): - solution = alternatives.display('better-world') - self.assertEqual('salt-err', solution) + with patch.dict(alternatives.__salt__, {"cmd.run_all": mock}): + solution = alternatives.display("better-world") + self.assertEqual("salt-err", solution) mock.assert_called_once_with( - ['alternatives', '--display', 'better-world'], - python_shell=False + ["alternatives", "--display", "better-world"], + ignore_retcode=True, + python_shell=False, ) def test_show_current(self): - mock = MagicMock(return_value='/etc/alternatives/salt') - with patch('salt.utils.path.readlink', mock): - ret = alternatives.show_current('better-world') - self.assertEqual('/etc/alternatives/salt', ret) - mock.assert_called_once_with('/etc/alternatives/better-world') + mock = MagicMock(return_value="/etc/alternatives/salt") + with patch("salt.utils.path.readlink", mock): + ret = alternatives.show_current("better-world") + self.assertEqual("/etc/alternatives/salt", ret) + mock.assert_called_once_with("/etc/alternatives/better-world") with TstSuiteLoggingHandler() as handler: - mock.side_effect = OSError('Hell was not found!!!') - self.assertFalse(alternatives.show_current('hell')) - mock.assert_called_with('/etc/alternatives/hell') - self.assertIn('ERROR:alternative: hell does not exist', - handler.messages) + mock.side_effect = OSError("Hell was not found!!!") + self.assertFalse(alternatives.show_current("hell")) + mock.assert_called_with("/etc/alternatives/hell") + self.assertIn( + "ERROR:alternative: hell does not exist", handler.messages + ) def test_check_installed(self): - mock = MagicMock(return_value='/etc/alternatives/salt') - with patch('salt.utils.path.readlink', mock): + mock = MagicMock(return_value="/etc/alternatives/salt") + with patch("salt.utils.path.readlink", mock): self.assertTrue( - alternatives.check_installed( - 'better-world', '/etc/alternatives/salt' - ) + alternatives.check_installed("better-world", "/etc/alternatives/salt") ) mock.return_value = False self.assertFalse( - alternatives.check_installed( - 'help', '/etc/alternatives/salt' - ) + alternatives.check_installed("help", "/etc/alternatives/salt") ) def test_install(self): - with patch.dict(alternatives.__grains__, {'os_family': 'RedHat'}): - mock = MagicMock(return_value={'retcode': 0, 'stdout': 'salt'}) - with patch.dict(alternatives.__salt__, {'cmd.run_all': mock}): + with patch.dict(alternatives.__grains__, {"os_family": "RedHat"}): + mock = MagicMock(return_value={"retcode": 0, "stdout": "salt"}) + with patch.dict(alternatives.__salt__, {"cmd.run_all": mock}): solution = alternatives.install( - 'better-world', - '/usr/bin/better-world', - '/usr/bin/salt', - 100 + "better-world", "/usr/bin/better-world", "/usr/bin/salt", 100 ) - self.assertEqual('salt', solution) + self.assertEqual("salt", solution) mock.assert_called_once_with( - ['alternatives', '--install', '/usr/bin/better-world', - 'better-world', '/usr/bin/salt', '100'], - python_shell=False + [ + "alternatives", + "--install", + "/usr/bin/better-world", + "better-world", + "/usr/bin/salt", + "100", + ], + python_shell=False, ) - with patch.dict(alternatives.__grains__, {'os_family': 'Debian'}): - mock = MagicMock(return_value={'retcode': 0, 'stdout': 'salt'}) - with patch.dict(alternatives.__salt__, {'cmd.run_all': mock}): + with patch.dict(alternatives.__grains__, {"os_family": "Debian"}): + mock = MagicMock(return_value={"retcode": 0, "stdout": "salt"}) + with patch.dict(alternatives.__salt__, {"cmd.run_all": mock}): solution = alternatives.install( - 'better-world', - '/usr/bin/better-world', - '/usr/bin/salt', - 100 + "better-world", "/usr/bin/better-world", "/usr/bin/salt", 100 ) - self.assertEqual('salt', solution) + self.assertEqual("salt", solution) mock.assert_called_once_with( - ['update-alternatives', '--install', '/usr/bin/better-world', - 'better-world', '/usr/bin/salt', '100'], - python_shell=False + [ + "update-alternatives", + "--install", + "/usr/bin/better-world", + "better-world", + "/usr/bin/salt", + "100", + ], + python_shell=False, ) - with patch.dict(alternatives.__grains__, {'os_family': 'RedHat'}): + with patch.dict(alternatives.__grains__, {"os_family": "RedHat"}): mock = MagicMock( - return_value={ - 'retcode': 1, - 'stdout': 'salt-out', - 'stderr': 'salt-err' - } + return_value={"retcode": 1, "stdout": "salt-out", "stderr": "salt-err"} ) - with patch.dict(alternatives.__salt__, {'cmd.run_all': mock}): + with patch.dict(alternatives.__salt__, {"cmd.run_all": mock}): ret = alternatives.install( - 'better-world', - '/usr/bin/better-world', - '/usr/bin/salt', - 100 + "better-world", "/usr/bin/better-world", "/usr/bin/salt", 100 ) - self.assertEqual('salt-err', ret) + self.assertEqual("salt-err", ret) mock.assert_called_once_with( - ['alternatives', '--install', '/usr/bin/better-world', - 'better-world', '/usr/bin/salt', '100'], - python_shell=False + [ + "alternatives", + "--install", + "/usr/bin/better-world", + "better-world", + "/usr/bin/salt", + "100", + ], + python_shell=False, ) def test_remove(self): - with patch.dict(alternatives.__grains__, {'os_family': 'RedHat'}): - mock = MagicMock(return_value={'retcode': 0, 'stdout': 'salt'}) - with patch.dict(alternatives.__salt__, {'cmd.run_all': mock}): - solution = alternatives.remove( - 'better-world', - '/usr/bin/better-world', - ) - self.assertEqual('salt', solution) + with patch.dict(alternatives.__grains__, {"os_family": "RedHat"}): + mock = MagicMock(return_value={"retcode": 0, "stdout": "salt"}) + with patch.dict(alternatives.__salt__, {"cmd.run_all": mock}): + solution = alternatives.remove("better-world", "/usr/bin/better-world",) + self.assertEqual("salt", solution) mock.assert_called_once_with( - ['alternatives', '--remove', 'better-world', - '/usr/bin/better-world'], python_shell=False + [ + "alternatives", + "--remove", + "better-world", + "/usr/bin/better-world", + ], + python_shell=False, ) - with patch.dict(alternatives.__grains__, {'os_family': 'Debian'}): - mock = MagicMock(return_value={'retcode': 0, 'stdout': 'salt'}) - with patch.dict(alternatives.__salt__, {'cmd.run_all': mock}): - solution = alternatives.remove( - 'better-world', - '/usr/bin/better-world', - ) - self.assertEqual('salt', solution) + with patch.dict(alternatives.__grains__, {"os_family": "Debian"}): + mock = MagicMock(return_value={"retcode": 0, "stdout": "salt"}) + with patch.dict(alternatives.__salt__, {"cmd.run_all": mock}): + solution = alternatives.remove("better-world", "/usr/bin/better-world",) + self.assertEqual("salt", solution) mock.assert_called_once_with( - ['update-alternatives', '--remove', 'better-world', - '/usr/bin/better-world'], python_shell=False + [ + "update-alternatives", + "--remove", + "better-world", + "/usr/bin/better-world", + ], + python_shell=False, ) - with patch.dict(alternatives.__grains__, {'os_family': 'RedHat'}): + with patch.dict(alternatives.__grains__, {"os_family": "RedHat"}): mock = MagicMock( - return_value={ - 'retcode': 1, - 'stdout': 'salt-out', - 'stderr': 'salt-err' - } + return_value={"retcode": 1, "stdout": "salt-out", "stderr": "salt-err"} ) - with patch.dict(alternatives.__salt__, {'cmd.run_all': mock}): - solution = alternatives.remove( - 'better-world', - '/usr/bin/better-world', - ) - self.assertEqual('salt-err', solution) + with patch.dict(alternatives.__salt__, {"cmd.run_all": mock}): + solution = alternatives.remove("better-world", "/usr/bin/better-world",) + self.assertEqual("salt-err", solution) mock.assert_called_once_with( - ['alternatives', '--remove', 'better-world', - '/usr/bin/better-world'], python_shell=False + [ + "alternatives", + "--remove", + "better-world", + "/usr/bin/better-world", + ], + python_shell=False, ) diff --git a/tests/unit/modules/test_ansiblegate.py b/tests/unit/modules/test_ansiblegate.py index 5613a0e79b3..3f63181e417 100644 --- a/tests/unit/modules/test_ansiblegate.py +++ b/tests/unit/modules/test_ansiblegate.py @@ -17,34 +17,32 @@ # Import Salt Testing Libs from __future__ import absolute_import, print_function, unicode_literals + import os + +import salt.modules.ansiblegate as ansible +import salt.utils.platform +from salt.exceptions import LoaderError +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + try: import pytest except ImportError as import_error: pytest = None NO_PYTEST = not bool(pytest) -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - MagicMock, -) - -import salt.modules.ansiblegate as ansible -import salt.utils.platform -from salt.exceptions import LoaderError - @skipIf(NO_PYTEST, False) -@skipIf(salt.utils.platform.is_windows(), 'Not supported on Windows') +@skipIf(salt.utils.platform.is_windows(), "Not supported on Windows") class AnsiblegateTestCase(TestCase, LoaderModuleMockMixin): def setUp(self): self.resolver = ansible.AnsibleModuleResolver({}) self.resolver._modules_map = { - 'one.two.three': os.sep + os.path.join('one', 'two', 'three.py'), - 'four.five.six': os.sep + os.path.join('four', 'five', 'six.py'), - 'three.six.one': os.sep + os.path.join('three', 'six', 'one.py'), + "one.two.three": os.sep + os.path.join("one", "two", "three.py"), + "four.five.six": os.sep + os.path.join("four", "five", "six.py"), + "three.six.one": os.sep + os.path.join("three", "six", "one.py"), } def tearDown(self): @@ -54,15 +52,17 @@ class AnsiblegateTestCase(TestCase, LoaderModuleMockMixin): return {ansible: {}} def test_ansible_module_help(self): - ''' + """ Test help extraction from the module :return: - ''' + """ + class Module(object): - ''' + """ An ansible module mock. - ''' - __name__ = 'foo' + """ + + __name__ = "foo" DOCUMENTATION = """ --- one: @@ -74,63 +74,80 @@ description: describe the second part """ - with patch.object(ansible, '_resolver', self.resolver), \ - patch.object(ansible._resolver, 'load_module', MagicMock(return_value=Module())): - ret = ansible.help('dummy') - assert sorted(ret.get('Available sections on module "{0}"'.format( - Module().__name__))) == ['one', 'two'] - assert ret.get('Description') == 'describe the second part' + with patch.object(ansible, "_resolver", self.resolver), patch.object( + ansible._resolver, "load_module", MagicMock(return_value=Module()) + ): + ret = ansible.help("dummy") + assert sorted( + ret.get('Available sections on module "{0}"'.format(Module().__name__)) + ) == ["one", "two"] + assert ret.get("Description") == "describe the second part" def test_module_resolver_modlist(self): - ''' + """ Test Ansible resolver modules list. :return: - ''' - assert self.resolver.get_modules_list() == ['four.five.six', 'one.two.three', 'three.six.one'] - for ptr in ['five', 'fi', 've']: - assert self.resolver.get_modules_list(ptr) == ['four.five.six'] - for ptr in ['si', 'ix', 'six']: - assert self.resolver.get_modules_list(ptr) == ['four.five.six', 'three.six.one'] - assert self.resolver.get_modules_list('one') == ['one.two.three', 'three.six.one'] - assert self.resolver.get_modules_list('one.two') == ['one.two.three'] - assert self.resolver.get_modules_list('four') == ['four.five.six'] + """ + assert self.resolver.get_modules_list() == [ + "four.five.six", + "one.two.three", + "three.six.one", + ] + for ptr in ["five", "fi", "ve"]: + assert self.resolver.get_modules_list(ptr) == ["four.five.six"] + for ptr in ["si", "ix", "six"]: + assert self.resolver.get_modules_list(ptr) == [ + "four.five.six", + "three.six.one", + ] + assert self.resolver.get_modules_list("one") == [ + "one.two.three", + "three.six.one", + ] + assert self.resolver.get_modules_list("one.two") == ["one.two.three"] + assert self.resolver.get_modules_list("four") == ["four.five.six"] def test_resolver_module_loader_failure(self): - ''' + """ Test Ansible module loader. :return: - ''' - mod = 'four.five.six' + """ + mod = "four.five.six" with pytest.raises(ImportError) as import_error: self.resolver.load_module(mod) - mod = 'i.even.do.not.exist.at.all' + mod = "i.even.do.not.exist.at.all" with pytest.raises(LoaderError) as loader_error: self.resolver.load_module(mod) def test_resolver_module_loader(self): - ''' + """ Test Ansible module loader. :return: - ''' - with patch('salt.modules.ansiblegate.importlib', MagicMock()),\ - patch('salt.modules.ansiblegate.importlib.import_module', lambda x: x): - assert self.resolver.load_module('four.five.six') == 'ansible.modules.four.five.six' + """ + with patch("salt.modules.ansiblegate.importlib", MagicMock()), patch( + "salt.modules.ansiblegate.importlib.import_module", lambda x: x + ): + assert ( + self.resolver.load_module("four.five.six") + == "ansible.modules.four.five.six" + ) def test_resolver_module_loader_import_failure(self): - ''' + """ Test Ansible module loader failure. :return: - ''' - with patch('salt.modules.ansiblegate.importlib', MagicMock()),\ - patch('salt.modules.ansiblegate.importlib.import_module', lambda x: x): + """ + with patch("salt.modules.ansiblegate.importlib", MagicMock()), patch( + "salt.modules.ansiblegate.importlib.import_module", lambda x: x + ): with pytest.raises(LoaderError) as loader_error: - self.resolver.load_module('something.strange') + self.resolver.load_module("something.strange") def test_virtual_function(self): - ''' + """ Test Ansible module __virtual__ when ansible is not installed on the minion. :return: - ''' - with patch('salt.modules.ansiblegate.ansible', None): - assert ansible.__virtual__() == 'ansible' + """ + with patch("salt.modules.ansiblegate.ansible", None): + assert ansible.__virtual__() == "ansible" diff --git a/tests/unit/modules/test_apache.py b/tests/unit/modules/test_apache.py index accf112304a..ce75b00d740 100644 --- a/tests/unit/modules/test_apache.py +++ b/tests/unit/modules/test_apache.py @@ -1,208 +1,218 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - mock_open, -) - # Import Salt Libs import salt.modules.apache as apache -from salt.ext.six.moves.urllib.error import URLError # pylint: disable=import-error,no-name-in-module +from salt.ext.six.moves.urllib.error import ( # pylint: disable=import-error,no-name-in-module + URLError, +) from salt.utils.odict import OrderedDict +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class ApacheTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.apache - ''' + """ def setup_loader_modules(self): return {apache: {}} # 'version' function tests: 1 def test_version(self): - ''' + """ Test if return server version (``apachectl -v``) - ''' - with patch('salt.modules.apache._detect_os', - MagicMock(return_value='apachectl')): + """ + with patch( + "salt.modules.apache._detect_os", MagicMock(return_value="apachectl") + ): mock = MagicMock(return_value="Server version: Apache/2.4.7") - with patch.dict(apache.__salt__, {'cmd.run': mock}): - assert apache.version() == 'Apache/2.4.7' + with patch.dict(apache.__salt__, {"cmd.run": mock}): + assert apache.version() == "Apache/2.4.7" # 'fullversion' function tests: 1 def test_fullversion(self): - ''' + """ Test if return server version (``apachectl -V``) - ''' - with patch('salt.modules.apache._detect_os', - MagicMock(return_value='apachectl')): + """ + with patch( + "salt.modules.apache._detect_os", MagicMock(return_value="apachectl") + ): mock = MagicMock(return_value="Server version: Apache/2.4.7") - with patch.dict(apache.__salt__, {'cmd.run': mock}): - assert apache.fullversion() == {'compiled_with': [], - 'server_version': 'Apache/2.4.7'} + with patch.dict(apache.__salt__, {"cmd.run": mock}): + assert apache.fullversion() == { + "compiled_with": [], + "server_version": "Apache/2.4.7", + } # 'modules' function tests: 1 def test_modules(self): - ''' + """ Test if return list of static and shared modules - ''' - with patch('salt.modules.apache._detect_os', - MagicMock(return_value='apachectl')): - mock = MagicMock(return_value= - "unixd_module (static)\n \ - access_compat_module (shared)") - with patch.dict(apache.__salt__, {'cmd.run': mock}): - assert apache.modules() == {'shared': ['access_compat_module'], - 'static': ['unixd_module']} + """ + with patch( + "salt.modules.apache._detect_os", MagicMock(return_value="apachectl") + ): + mock = MagicMock( + return_value="unixd_module (static)\n \ + access_compat_module (shared)" + ) + with patch.dict(apache.__salt__, {"cmd.run": mock}): + assert apache.modules() == { + "shared": ["access_compat_module"], + "static": ["unixd_module"], + } # 'servermods' function tests: 1 def test_servermods(self): - ''' + """ Test if return list of modules compiled into the server - ''' - with patch('salt.modules.apache._detect_os', - MagicMock(return_value='apachectl')): + """ + with patch( + "salt.modules.apache._detect_os", MagicMock(return_value="apachectl") + ): mock = MagicMock(return_value="core.c\nmod_so.c") - with patch.dict(apache.__salt__, {'cmd.run': mock}): - assert apache.servermods() == ['core.c', 'mod_so.c'] + with patch.dict(apache.__salt__, {"cmd.run": mock}): + assert apache.servermods() == ["core.c", "mod_so.c"] # 'directives' function tests: 1 def test_directives(self): - ''' + """ Test if return list of directives - ''' - with patch('salt.modules.apache._detect_os', - MagicMock(return_value='apachectl')): + """ + with patch( + "salt.modules.apache._detect_os", MagicMock(return_value="apachectl") + ): mock = MagicMock(return_value="Salt") - with patch.dict(apache.__salt__, {'cmd.run': mock}): - assert apache.directives() == {'Salt': ''} + with patch.dict(apache.__salt__, {"cmd.run": mock}): + assert apache.directives() == {"Salt": ""} # 'vhosts' function tests: 1 def test_vhosts(self): - ''' + """ Test if it shows the virtualhost settings - ''' - with patch('salt.modules.apache._detect_os', - MagicMock(return_value='apachectl')): - mock = MagicMock(return_value='') - with patch.dict(apache.__salt__, {'cmd.run': mock}): + """ + with patch( + "salt.modules.apache._detect_os", MagicMock(return_value="apachectl") + ): + mock = MagicMock(return_value="") + with patch.dict(apache.__salt__, {"cmd.run": mock}): assert apache.vhosts() == {} # 'signal' function tests: 2 def test_signal(self): - ''' + """ Test if return no signal for httpd - ''' - with patch('salt.modules.apache._detect_os', - MagicMock(return_value='apachectl')): - mock = MagicMock(return_value='') - with patch.dict(apache.__salt__, {'cmd.run': mock}): + """ + with patch( + "salt.modules.apache._detect_os", MagicMock(return_value="apachectl") + ): + mock = MagicMock(return_value="") + with patch.dict(apache.__salt__, {"cmd.run": mock}): assert apache.signal(None) is None def test_signal_args(self): - ''' + """ Test if return httpd signal to start, restart, or stop. - ''' - with patch('salt.modules.apache._detect_os', - MagicMock(return_value='apachectl')): + """ + with patch( + "salt.modules.apache._detect_os", MagicMock(return_value="apachectl") + ): ret = 'Command: "apachectl -k start" completed successfully!' - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': ''}) - with patch.dict(apache.__salt__, {'cmd.run_all': mock}): - assert apache.signal('start') == ret + mock = MagicMock(return_value={"retcode": 1, "stderr": "", "stdout": ""}) + with patch.dict(apache.__salt__, {"cmd.run_all": mock}): + assert apache.signal("start") == ret - mock = MagicMock(return_value={'retcode': 1, - 'stderr': 'Syntax OK', - 'stdout': ''}) - with patch.dict(apache.__salt__, {'cmd.run_all': mock}): - assert apache.signal('start') == 'Syntax OK' + mock = MagicMock( + return_value={"retcode": 1, "stderr": "Syntax OK", "stdout": ""} + ) + with patch.dict(apache.__salt__, {"cmd.run_all": mock}): + assert apache.signal("start") == "Syntax OK" - mock = MagicMock(return_value={'retcode': 0, - 'stderr': 'Syntax OK', - 'stdout': ''}) - with patch.dict(apache.__salt__, {'cmd.run_all': mock}): - assert apache.signal('start') == 'Syntax OK' + mock = MagicMock( + return_value={"retcode": 0, "stderr": "Syntax OK", "stdout": ""} + ) + with patch.dict(apache.__salt__, {"cmd.run_all": mock}): + assert apache.signal("start") == "Syntax OK" - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(apache.__salt__, {'cmd.run_all': mock}): - assert apache.signal('start') == 'Salt' + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(apache.__salt__, {"cmd.run_all": mock}): + assert apache.signal("start") == "Salt" # 'useradd' function tests: 1 def test_useradd(self): - ''' + """ Test if it add HTTP user using the ``htpasswd`` command - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(apache.__salt__, {'webutil.useradd': mock}): - assert apache.useradd('htpasswd', 'salt', 'badpassword') is True + with patch.dict(apache.__salt__, {"webutil.useradd": mock}): + assert apache.useradd("htpasswd", "salt", "badpassword") is True # 'userdel' function tests: 1 def test_userdel(self): - ''' + """ Test if it delete HTTP user using the ``htpasswd`` file - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(apache.__salt__, {'webutil.userdel': mock}): - assert apache.userdel('htpasswd', 'salt') is True + with patch.dict(apache.__salt__, {"webutil.userdel": mock}): + assert apache.userdel("htpasswd", "salt") is True # 'server_status' function tests: 2 def test_server_status(self): - ''' + """ Test if return get information from the Apache server-status - ''' - with patch('salt.modules.apache.server_status', MagicMock(return_value={})): - mock = MagicMock(return_value='') - with patch.dict(apache.__salt__, {'config.get': mock}): + """ + with patch("salt.modules.apache.server_status", MagicMock(return_value={})): + mock = MagicMock(return_value="") + with patch.dict(apache.__salt__, {"config.get": mock}): assert apache.server_status() == {} def test_server_status_error(self): - ''' + """ Test if return get error from the Apache server-status - ''' - mock = MagicMock(side_effect=URLError('error')) - with patch.object(apache, '_urlopen', mock): - mock = MagicMock(return_value='') - with patch.dict(apache.__salt__, {'config.get': mock}): - assert apache.server_status() == 'error' + """ + mock = MagicMock(side_effect=URLError("error")) + with patch.object(apache, "_urlopen", mock): + mock = MagicMock(return_value="") + with patch.dict(apache.__salt__, {"config.get": mock}): + assert apache.server_status() == "error" # 'config' function tests: 1 def test_config(self): - ''' + """ Test if it create VirtualHost configuration files - ''' - with patch('salt.modules.apache._parse_config', - MagicMock(return_value='Listen 22')): - with patch('salt.utils.files.fopen', mock_open()): - assert apache.config('/ports.conf', [{'Listen': '22'}]) == 'Listen 22' + """ + with patch( + "salt.modules.apache._parse_config", MagicMock(return_value="Listen 22") + ): + with patch("salt.utils.files.fopen", mock_open()): + assert apache.config("/ports.conf", [{"Listen": "22"}]) == "Listen 22" # '_parse_config' function tests: 2 def test__parse_config_dict(self): - ''' + """ Test parsing function which creates configs from dict like (legacy way): - VirtualHost: this: '*:80' @@ -217,32 +227,42 @@ class ApacheTestCase(TestCase, LoaderModuleMockMixin): - 127.0.0.1 - 192.168.100.0/24 - ''' - data_in = OrderedDict([ - ('Directory', OrderedDict([ - ('this', '/var/www/vhosts/website.com'), - ('Order', 'Deny,Allow'), - ('Allow from', ['127.0.0.1', '192.168.100.0/24'])])), - ('this', '*:80'), - ('ServerName', 'website.com'), - ('ServerAlias', ['www', 'dev']) - ]) - dataout = '<VirtualHost *:80>\n' \ - '<Directory /var/www/vhosts/website.com>\n' \ - 'Order Deny,Allow\n' \ - 'Allow from 127.0.0.1\n' \ - 'Allow from 192.168.100.0/24\n\n' \ - '</Directory>\n\n' \ - 'ServerName website.com\n' \ - 'ServerAlias www\n' \ - 'ServerAlias dev\n\n' \ - '</VirtualHost>\n' + """ + data_in = OrderedDict( + [ + ( + "Directory", + OrderedDict( + [ + ("this", "/var/www/vhosts/website.com"), + ("Order", "Deny,Allow"), + ("Allow from", ["127.0.0.1", "192.168.100.0/24"]), + ] + ), + ), + ("this", "*:80"), + ("ServerName", "website.com"), + ("ServerAlias", ["www", "dev"]), + ] + ) + dataout = ( + "<VirtualHost *:80>\n" + "<Directory /var/www/vhosts/website.com>\n" + "Order Deny,Allow\n" + "Allow from 127.0.0.1\n" + "Allow from 192.168.100.0/24\n\n" + "</Directory>\n\n" + "ServerName website.com\n" + "ServerAlias www\n" + "ServerAlias dev\n\n" + "</VirtualHost>\n" + ) # pylint: disable=protected-access - parse = apache._parse_config(data_in, 'VirtualHost') + parse = apache._parse_config(data_in, "VirtualHost") assert parse == dataout def test__parse_config_list(self): - ''' + """ Test parsing function which creates configs from variable structure (list of dicts or list of dicts of dicts/lists) like: - VirtualHost: @@ -266,31 +286,47 @@ class ApacheTestCase(TestCase, LoaderModuleMockMixin): - If: this: some condition do: something - ''' - data_in = [OrderedDict([ - ('ServerName', 'website.com'), - ('ServerAlias', ['www', 'dev']), - ('Directory', [OrderedDict([ - ('this', '/var/www/vhosts/website.com/private'), - ('Order', 'Deny,Allow'), - ('Allow from', ['127.0.0.1', '192.168.100.0/24']), - ('If', {'this': 'some condition', 'do': 'something'}) - ])]), - ('this', '*:80') - ])] - dataout = '<VirtualHost *:80>\n' \ - 'ServerName website.com\n' \ - 'ServerAlias www\n' \ - 'ServerAlias dev\n\n' \ - '<Directory /var/www/vhosts/website.com/private>\n' \ - 'Order Deny,Allow\n' \ - 'Allow from 127.0.0.1\n' \ - 'Allow from 192.168.100.0/24\n\n' \ - '<If some condition>\n' \ - 'do something\n' \ - '</If>\n\n' \ - '</Directory>\n\n' \ - '</VirtualHost>\n' + """ + data_in = [ + OrderedDict( + [ + ("ServerName", "website.com"), + ("ServerAlias", ["www", "dev"]), + ( + "Directory", + [ + OrderedDict( + [ + ("this", "/var/www/vhosts/website.com/private"), + ("Order", "Deny,Allow"), + ("Allow from", ["127.0.0.1", "192.168.100.0/24"]), + ( + "If", + {"this": "some condition", "do": "something"}, + ), + ] + ) + ], + ), + ("this", "*:80"), + ] + ) + ] + dataout = ( + "<VirtualHost *:80>\n" + "ServerName website.com\n" + "ServerAlias www\n" + "ServerAlias dev\n\n" + "<Directory /var/www/vhosts/website.com/private>\n" + "Order Deny,Allow\n" + "Allow from 127.0.0.1\n" + "Allow from 192.168.100.0/24\n\n" + "<If some condition>\n" + "do something\n" + "</If>\n\n" + "</Directory>\n\n" + "</VirtualHost>\n" + ) # pylint: disable=protected-access - parse = apache._parse_config(data_in, 'VirtualHost') + parse = apache._parse_config(data_in, "VirtualHost") assert parse == dataout diff --git a/tests/unit/modules/test_aptpkg.py b/tests/unit/modules/test_aptpkg.py index e1b6602df54..76479064c60 100644 --- a/tests/unit/modules/test_aptpkg.py +++ b/tests/unit/modules/test_aptpkg.py @@ -1,25 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for Advanced Packaging Tool module 'module.aptpkg' :platform: Linux :maturity: develop versionadded:: 2017.7.0 -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import copy import textwrap -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import Mock, MagicMock, patch +import salt.modules.aptpkg as aptpkg +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import Salt Libs from salt.ext import six -from salt.exceptions import CommandExecutionError, SaltInvocationError -import salt.modules.aptpkg as aptpkg + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase, skipIf try: import pytest @@ -27,93 +29,91 @@ except ImportError: pytest = None -APT_KEY_LIST = r''' +APT_KEY_LIST = r""" pub:-:1024:17:46181433FBB75451:1104433784:::-:::scSC: fpr:::::::::C5986B4F1257FFA86632CBA746181433FBB75451: uid:-::::1104433784::B4D41942D4B35FF44182C7F9D00C99AF27B93AD0::Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>: -''' +""" REPO_KEYS = { - '46181433FBB75451': { - 'algorithm': 17, - 'bits': 1024, - 'capability': 'scSC', - 'date_creation': 1104433784, - 'date_expiration': None, - 'fingerprint': 'C5986B4F1257FFA86632CBA746181433FBB75451', - 'keyid': '46181433FBB75451', - 'uid': 'Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>', - 'uid_hash': 'B4D41942D4B35FF44182C7F9D00C99AF27B93AD0', - 'validity': '-' + "46181433FBB75451": { + "algorithm": 17, + "bits": 1024, + "capability": "scSC", + "date_creation": 1104433784, + "date_expiration": None, + "fingerprint": "C5986B4F1257FFA86632CBA746181433FBB75451", + "keyid": "46181433FBB75451", + "uid": "Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>", + "uid_hash": "B4D41942D4B35FF44182C7F9D00C99AF27B93AD0", + "validity": "-", } } -PACKAGES = { - 'wget': '1.15-1ubuntu1.14.04.2' -} +PACKAGES = {"wget": "1.15-1ubuntu1.14.04.2"} LOWPKG_FILES = { - 'errors': {}, - 'packages': { - 'wget': [ - '/.', - '/etc', - '/etc/wgetrc', - '/usr', - '/usr/bin', - '/usr/bin/wget', - '/usr/share', - '/usr/share/info', - '/usr/share/info/wget.info.gz', - '/usr/share/doc', - '/usr/share/doc/wget', - '/usr/share/doc/wget/MAILING-LIST', - '/usr/share/doc/wget/NEWS.gz', - '/usr/share/doc/wget/AUTHORS', - '/usr/share/doc/wget/copyright', - '/usr/share/doc/wget/changelog.Debian.gz', - '/usr/share/doc/wget/README', - '/usr/share/man', - '/usr/share/man/man1', - '/usr/share/man/man1/wget.1.gz', + "errors": {}, + "packages": { + "wget": [ + "/.", + "/etc", + "/etc/wgetrc", + "/usr", + "/usr/bin", + "/usr/bin/wget", + "/usr/share", + "/usr/share/info", + "/usr/share/info/wget.info.gz", + "/usr/share/doc", + "/usr/share/doc/wget", + "/usr/share/doc/wget/MAILING-LIST", + "/usr/share/doc/wget/NEWS.gz", + "/usr/share/doc/wget/AUTHORS", + "/usr/share/doc/wget/copyright", + "/usr/share/doc/wget/changelog.Debian.gz", + "/usr/share/doc/wget/README", + "/usr/share/man", + "/usr/share/man/man1", + "/usr/share/man/man1/wget.1.gz", ] - } + }, } LOWPKG_INFO = { - 'wget': { - 'architecture': 'amd64', - 'description': 'retrieves files from the web', - 'homepage': 'http://www.gnu.org/software/wget/', - 'install_date': '2016-08-30T22:20:15Z', - 'maintainer': 'Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>', - 'name': 'wget', - 'section': 'web', - 'source': 'wget', - 'version': '1.15-1ubuntu1.14.04.2', - 'status': 'ii', + "wget": { + "architecture": "amd64", + "description": "retrieves files from the web", + "homepage": "http://www.gnu.org/software/wget/", + "install_date": "2016-08-30T22:20:15Z", + "maintainer": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>", + "name": "wget", + "section": "web", + "source": "wget", + "version": "1.15-1ubuntu1.14.04.2", + "status": "ii", }, - 'apache2': { - 'architecture': 'amd64', - 'description': """Apache HTTP Server + "apache2": { + "architecture": "amd64", + "description": """Apache HTTP Server The Apache HTTP Server Project's goal is to build a secure, efficient and extensible HTTP server as standards-compliant open source software. The result has long been the number one web server on the Internet. . Installing this package results in a full installation, including the configuration files, init scripts and support scripts.""", - 'homepage': 'http://httpd.apache.org/', - 'install_date': '2016-08-30T22:20:15Z', - 'maintainer': 'Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>', - 'name': 'apache2', - 'section': 'httpd', - 'source': 'apache2', - 'version': '2.4.18-2ubuntu3.9', - 'status': 'rc', - } + "homepage": "http://httpd.apache.org/", + "install_date": "2016-08-30T22:20:15Z", + "maintainer": "Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>", + "name": "apache2", + "section": "httpd", + "source": "apache2", + "version": "2.4.18-2ubuntu3.9", + "status": "rc", + }, } -APT_Q_UPDATE = ''' +APT_Q_UPDATE = """ Get:1 http://security.ubuntu.com trusty-security InRelease [65 kB] Get:2 http://security.ubuntu.com trusty-security/main Sources [120 kB] Get:3 http://security.ubuntu.com trusty-security/main amd64 Packages [548 kB] @@ -121,9 +121,9 @@ Get:4 http://security.ubuntu.com trusty-security/main i386 Packages [507 kB] Hit http://security.ubuntu.com trusty-security/main Translation-en Fetched 1240 kB in 10s (124 kB/s) Reading package lists... -''' +""" -APT_Q_UPDATE_ERROR = ''' +APT_Q_UPDATE_ERROR = """ Err http://security.ubuntu.com trusty InRelease Err http://security.ubuntu.com trusty Release.gpg @@ -134,196 +134,183 @@ W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/trusty/InRelease W: Failed to fetch http://security.ubuntu.com/ubuntu/dists/trusty/Release.gpg Unable to connect to security.ubuntu.com:http: W: Some index files failed to download. They have been ignored, or old ones used instead. -''' +""" -AUTOREMOVE = ''' +AUTOREMOVE = """ Reading package lists... Done Building dependency tree Reading state information... Done 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. -''' +""" -UPGRADE = ''' +UPGRADE = """ Reading package lists... Building dependency tree... Reading state information... 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. -''' +""" -UNINSTALL = { - 'tmux': { - 'new': six.text_type(), - 'old': '1.8-5' - } -} +UNINSTALL = {"tmux": {"new": six.text_type(), "old": "1.8-5"}} class AptPkgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.aptpkg - ''' + """ def setup_loader_modules(self): return {aptpkg: {}} def test_version(self): - ''' + """ Test - Returns a string representing the package version or an empty string if not installed. - ''' - version = LOWPKG_INFO['wget']['version'] + """ + version = LOWPKG_INFO["wget"]["version"] mock = MagicMock(return_value=version) - with patch.dict(aptpkg.__salt__, {'pkg_resource.version': mock}): - self.assertEqual(aptpkg.version(*['wget']), version) + with patch.dict(aptpkg.__salt__, {"pkg_resource.version": mock}): + self.assertEqual(aptpkg.version(*["wget"]), version) def test_upgrade_available(self): - ''' + """ Test - Check whether or not an upgrade is available for a given package. - ''' - with patch('salt.modules.aptpkg.latest_version', - MagicMock(return_value='')): - self.assertFalse(aptpkg.upgrade_available('wget')) + """ + with patch("salt.modules.aptpkg.latest_version", MagicMock(return_value="")): + self.assertFalse(aptpkg.upgrade_available("wget")) def test_add_repo_key(self): - ''' + """ Test - Add a repo key. - ''' - with patch('salt.modules.aptpkg.get_repo_keys', - MagicMock(return_value=REPO_KEYS)): - mock = MagicMock(return_value={ - 'retcode': 0, - 'stdout': 'OK' - }) - with patch.dict(aptpkg.__salt__, {'cmd.run_all': mock}): - self.assertTrue(aptpkg.add_repo_key(keyserver='keyserver.ubuntu.com', - keyid='FBB75451')) + """ + with patch( + "salt.modules.aptpkg.get_repo_keys", MagicMock(return_value=REPO_KEYS) + ): + mock = MagicMock(return_value={"retcode": 0, "stdout": "OK"}) + with patch.dict(aptpkg.__salt__, {"cmd.run_all": mock}): + self.assertTrue( + aptpkg.add_repo_key( + keyserver="keyserver.ubuntu.com", keyid="FBB75451" + ) + ) def test_add_repo_key_failed(self): - ''' + """ Test - Add a repo key using incomplete input data. - ''' - with patch('salt.modules.aptpkg.get_repo_keys', - MagicMock(return_value=REPO_KEYS)): - kwargs = {'keyserver': 'keyserver.ubuntu.com'} - mock = MagicMock(return_value={ - 'retcode': 0, - 'stdout': 'OK' - }) - with patch.dict(aptpkg.__salt__, {'cmd.run_all': mock}): + """ + with patch( + "salt.modules.aptpkg.get_repo_keys", MagicMock(return_value=REPO_KEYS) + ): + kwargs = {"keyserver": "keyserver.ubuntu.com"} + mock = MagicMock(return_value={"retcode": 0, "stdout": "OK"}) + with patch.dict(aptpkg.__salt__, {"cmd.run_all": mock}): self.assertRaises(SaltInvocationError, aptpkg.add_repo_key, **kwargs) def test_get_repo_keys(self): - ''' + """ Test - List known repo key details. - ''' - mock = MagicMock(return_value={ - 'retcode': 0, - 'stdout': APT_KEY_LIST - }) - with patch.dict(aptpkg.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": APT_KEY_LIST}) + with patch.dict(aptpkg.__salt__, {"cmd.run_all": mock}): self.assertEqual(aptpkg.get_repo_keys(), REPO_KEYS) def test_file_dict(self): - ''' + """ Test - List the files that belong to a package, grouped by package. - ''' + """ mock = MagicMock(return_value=LOWPKG_FILES) - with patch.dict(aptpkg.__salt__, {'lowpkg.file_dict': mock}): - self.assertEqual(aptpkg.file_dict('wget'), LOWPKG_FILES) + with patch.dict(aptpkg.__salt__, {"lowpkg.file_dict": mock}): + self.assertEqual(aptpkg.file_dict("wget"), LOWPKG_FILES) def test_file_list(self): - ''' + """ Test - List the files that belong to a package. - ''' + """ files = { - 'errors': LOWPKG_FILES['errors'], - 'files': LOWPKG_FILES['packages']['wget'], + "errors": LOWPKG_FILES["errors"], + "files": LOWPKG_FILES["packages"]["wget"], } mock = MagicMock(return_value=files) - with patch.dict(aptpkg.__salt__, {'lowpkg.file_list': mock}): - self.assertEqual(aptpkg.file_list('wget'), files) + with patch.dict(aptpkg.__salt__, {"lowpkg.file_list": mock}): + self.assertEqual(aptpkg.file_list("wget"), files) def test_get_selections(self): - ''' + """ Test - View package state from the dpkg database. - ''' - selections = {'install': ['wget']} - mock = MagicMock(return_value='wget\t\t\t\t\t\tinstall') - with patch.dict(aptpkg.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(aptpkg.get_selections('wget'), selections) + """ + selections = {"install": ["wget"]} + mock = MagicMock(return_value="wget\t\t\t\t\t\tinstall") + with patch.dict(aptpkg.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual(aptpkg.get_selections("wget"), selections) def test_info_installed(self): - ''' + """ Test - Return the information of the named package(s) installed on the system. - ''' - names = { - 'group': 'section', - 'packager': 'maintainer', - 'url': 'homepage' - } + """ + names = {"group": "section", "packager": "maintainer", "url": "homepage"} - installed = copy.deepcopy({'wget': LOWPKG_INFO['wget']}) + installed = copy.deepcopy({"wget": LOWPKG_INFO["wget"]}) for name in names: - if installed['wget'].get(names[name], False): - installed['wget'][name] = installed['wget'].pop(names[name]) + if installed["wget"].get(names[name], False): + installed["wget"][name] = installed["wget"].pop(names[name]) mock = MagicMock(return_value=LOWPKG_INFO) - with patch.dict(aptpkg.__salt__, {'lowpkg.info': mock}): - del installed['wget']['status'] - self.assertEqual(aptpkg.info_installed('wget'), installed) + with patch.dict(aptpkg.__salt__, {"lowpkg.info": mock}): + del installed["wget"]["status"] + self.assertEqual(aptpkg.info_installed("wget"), installed) self.assertEqual(len(aptpkg.info_installed()), 1) def test_owner(self): - ''' + """ Test - Return the name of the package that owns the file. - ''' - paths = ['/usr/bin/wget'] - mock = MagicMock(return_value='wget: /usr/bin/wget') - with patch.dict(aptpkg.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(aptpkg.owner(*paths), 'wget') + """ + paths = ["/usr/bin/wget"] + mock = MagicMock(return_value="wget: /usr/bin/wget") + with patch.dict(aptpkg.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual(aptpkg.owner(*paths), "wget") def test_refresh_db(self): - ''' + """ Test - Updates the APT database to latest packages based upon repositories. - ''' + """ refresh_db = { - 'http://security.ubuntu.com trusty-security InRelease': True, - 'http://security.ubuntu.com trusty-security/main Sources': True, - 'http://security.ubuntu.com trusty-security/main Translation-en': None, - 'http://security.ubuntu.com trusty-security/main amd64 Packages': True, - 'http://security.ubuntu.com trusty-security/main i386 Packages': True + "http://security.ubuntu.com trusty-security InRelease": True, + "http://security.ubuntu.com trusty-security/main Sources": True, + "http://security.ubuntu.com trusty-security/main Translation-en": None, + "http://security.ubuntu.com trusty-security/main amd64 Packages": True, + "http://security.ubuntu.com trusty-security/main i386 Packages": True, } - mock = MagicMock(return_value={ - 'retcode': 0, - 'stdout': APT_Q_UPDATE - }) - with patch('salt.utils.pkg.clear_rtag', MagicMock()): - with patch.dict(aptpkg.__salt__, {'cmd.run_all': mock, 'config.get': MagicMock(return_value=False)}): + mock = MagicMock(return_value={"retcode": 0, "stdout": APT_Q_UPDATE}) + with patch("salt.utils.pkg.clear_rtag", MagicMock()): + with patch.dict( + aptpkg.__salt__, + {"cmd.run_all": mock, "config.get": MagicMock(return_value=False)}, + ): self.assertEqual(aptpkg.refresh_db(), refresh_db) def test_refresh_db_failed(self): - ''' + """ Test - Update the APT database using unreachable repositories. - ''' - kwargs = {'failhard': True} - mock = MagicMock(return_value={ - 'retcode': 0, - 'stdout': APT_Q_UPDATE_ERROR - }) - with patch('salt.utils.pkg.clear_rtag', MagicMock()): - with patch.dict(aptpkg.__salt__, {'cmd.run_all': mock, 'config.get': MagicMock(return_value=False)}): + """ + kwargs = {"failhard": True} + mock = MagicMock(return_value={"retcode": 0, "stdout": APT_Q_UPDATE_ERROR}) + with patch("salt.utils.pkg.clear_rtag", MagicMock()): + with patch.dict( + aptpkg.__salt__, + {"cmd.run_all": mock, "config.get": MagicMock(return_value=False)}, + ): self.assertRaises(CommandExecutionError, aptpkg.refresh_db, **kwargs) def test_autoremove(self): - ''' + """ Test - Remove packages not required by another package. - ''' - with patch('salt.modules.aptpkg.list_pkgs', - MagicMock(return_value=PACKAGES)): + """ + with patch("salt.modules.aptpkg.list_pkgs", MagicMock(return_value=PACKAGES)): patch_kwargs = { - '__salt__': { - 'config.get': MagicMock(return_value=True), - 'cmd.run_all': MagicMock(return_value=MagicMock(return_value=AUTOREMOVE)) + "__salt__": { + "config.get": MagicMock(return_value=True), + "cmd.run_all": MagicMock( + return_value=MagicMock(return_value=AUTOREMOVE) + ), } } with patch.multiple(aptpkg, **patch_kwargs): @@ -333,84 +320,92 @@ class AptPkgTestCase(TestCase, LoaderModuleMockMixin): assert aptpkg.autoremove(list_only=True, purge=True) == [] def test_remove(self): - ''' + """ Test - Remove packages. - ''' - with patch('salt.modules.aptpkg._uninstall', - MagicMock(return_value=UNINSTALL)): - self.assertEqual(aptpkg.remove(name='tmux'), UNINSTALL) + """ + with patch("salt.modules.aptpkg._uninstall", MagicMock(return_value=UNINSTALL)): + self.assertEqual(aptpkg.remove(name="tmux"), UNINSTALL) def test_purge(self): - ''' + """ Test - Remove packages along with all configuration files. - ''' - with patch('salt.modules.aptpkg._uninstall', - MagicMock(return_value=UNINSTALL)): - self.assertEqual(aptpkg.purge(name='tmux'), UNINSTALL) + """ + with patch("salt.modules.aptpkg._uninstall", MagicMock(return_value=UNINSTALL)): + self.assertEqual(aptpkg.purge(name="tmux"), UNINSTALL) def test_upgrade(self): - ''' + """ Test - Upgrades all packages. - ''' - with patch('salt.utils.pkg.clear_rtag', MagicMock()): - with patch('salt.modules.aptpkg.list_pkgs', - MagicMock(return_value=UNINSTALL)): - mock_cmd = MagicMock(return_value={ - 'retcode': 0, - 'stdout': UPGRADE - }) + """ + with patch("salt.utils.pkg.clear_rtag", MagicMock()): + with patch( + "salt.modules.aptpkg.list_pkgs", MagicMock(return_value=UNINSTALL) + ): + mock_cmd = MagicMock(return_value={"retcode": 0, "stdout": UPGRADE}) patch_kwargs = { - '__salt__': { - 'config.get': MagicMock(return_value=True), - 'cmd.run_all': mock_cmd + "__salt__": { + "config.get": MagicMock(return_value=True), + "cmd.run_all": mock_cmd, } } with patch.multiple(aptpkg, **patch_kwargs): self.assertEqual(aptpkg.upgrade(), dict()) def test_upgrade_downloadonly(self): - ''' + """ Tests the download-only options for upgrade. - ''' - with patch('salt.utils.pkg.clear_rtag', MagicMock()): - with patch('salt.modules.aptpkg.list_pkgs', - MagicMock(return_value=UNINSTALL)): - mock_cmd = MagicMock(return_value={ - 'retcode': 0, - 'stdout': UPGRADE - }) + """ + with patch("salt.utils.pkg.clear_rtag", MagicMock()): + with patch( + "salt.modules.aptpkg.list_pkgs", MagicMock(return_value=UNINSTALL) + ): + mock_cmd = MagicMock(return_value={"retcode": 0, "stdout": UPGRADE}) patch_kwargs = { - '__salt__': { - 'config.get': MagicMock(return_value=True), - 'cmd.run_all': mock_cmd + "__salt__": { + "config.get": MagicMock(return_value=True), + "cmd.run_all": mock_cmd, }, } with patch.multiple(aptpkg, **patch_kwargs): aptpkg.upgrade() - args_matching = [True for args in patch_kwargs['__salt__']['cmd.run_all'].call_args[0] if "--download-only" in args] + args_matching = [ + True + for args in patch_kwargs["__salt__"]["cmd.run_all"].call_args[0] + if "--download-only" in args + ] # Here we shouldn't see the parameter and args_matching should be empty. self.assertFalse(any(args_matching)) aptpkg.upgrade(downloadonly=True) - args_matching = [True for args in patch_kwargs['__salt__']['cmd.run_all'].call_args[0] if "--download-only" in args] + args_matching = [ + True + for args in patch_kwargs["__salt__"]["cmd.run_all"].call_args[0] + if "--download-only" in args + ] # --download-only should be in the args list and we should have at least on True in the list. self.assertTrue(any(args_matching)) aptpkg.upgrade(download_only=True) - args_matching = [True for args in patch_kwargs['__salt__']['cmd.run_all'].call_args[0] if "--download-only" in args] + args_matching = [ + True + for args in patch_kwargs["__salt__"]["cmd.run_all"].call_args[0] + if "--download-only" in args + ] # --download-only should be in the args list and we should have at least on True in the list. self.assertTrue(any(args_matching)) def test_show(self): - ''' + """ Test that the pkg.show function properly parses apt-cache show output. This test uses an abridged output per package, for simplicity. - ''' - show_mock_success = MagicMock(return_value={ - 'retcode': 0, - 'pid': 12345, - 'stderr': '', - 'stdout': textwrap.dedent('''\ + """ + show_mock_success = MagicMock( + return_value={ + "retcode": 0, + "pid": 12345, + "stderr": "", + "stdout": textwrap.dedent( + """\ Package: foo1.0 Architecture: amd64 Version: 1.0.5-3ubuntu4 @@ -435,46 +430,52 @@ class AptPkgTestCase(TestCase, LoaderModuleMockMixin): Version: 1.0.4-2ubuntu1 Description: Silly documentation for a silly package (1.0 release cycle) - '''), - }) + """ + ), + } + ) - show_mock_failure = MagicMock(return_value={ - 'retcode': 1, - 'pid': 12345, - 'stderr': textwrap.dedent('''\ + show_mock_failure = MagicMock( + return_value={ + "retcode": 1, + "pid": 12345, + "stderr": textwrap.dedent( + """\ N: Unable to locate package foo* N: Couldn't find any package by glob 'foo*' N: Couldn't find any package by regex 'foo*' E: No packages found - '''), - 'stdout': '', - }) + """ + ), + "stdout": "", + } + ) refresh_mock = Mock() expected = { - 'foo1.0': { - '1.0.5-3ubuntu4': { - 'Architecture': 'amd64', - 'Description': 'A silly package (1.0 release cycle)', - 'Provides': 'foo', - 'Suggests': 'foo-doc', + "foo1.0": { + "1.0.5-3ubuntu4": { + "Architecture": "amd64", + "Description": "A silly package (1.0 release cycle)", + "Provides": "foo", + "Suggests": "foo-doc", }, - '1.0.4-2ubuntu1': { - 'Architecture': 'amd64', - 'Description': 'A silly package (1.0 release cycle)', - 'Provides': 'foo', - 'Suggests': 'foo-doc', + "1.0.4-2ubuntu1": { + "Architecture": "amd64", + "Description": "A silly package (1.0 release cycle)", + "Provides": "foo", + "Suggests": "foo-doc", }, }, - 'foo-doc': { - '1.0.5-3ubuntu4': { - 'Architecture': 'all', - 'Description': 'Silly documentation for a silly package (1.0 release cycle)', + "foo-doc": { + "1.0.5-3ubuntu4": { + "Architecture": "all", + "Description": "Silly documentation for a silly package (1.0 release cycle)", }, - '1.0.4-2ubuntu1': { - 'Architecture': 'all', - 'Description': 'Silly documentation for a silly package (1.0 release cycle)', + "1.0.4-2ubuntu1": { + "Architecture": "all", + "Description": "Silly documentation for a silly package (1.0 release cycle)", }, }, } @@ -486,134 +487,188 @@ class AptPkgTestCase(TestCase, LoaderModuleMockMixin): for k2 in filtered[k1]: # Using list() because we will modify the dict during iteration for k3 in list(filtered[k1][k2]): - if k3 not in ('Description', 'Provides'): + if k3 not in ("Description", "Provides"): filtered[k1][k2].pop(k3) - with patch.dict(aptpkg.__salt__, {'cmd.run_all': show_mock_success}), \ - patch.object(aptpkg, 'refresh_db', refresh_mock): + with patch.dict( + aptpkg.__salt__, {"cmd.run_all": show_mock_success} + ), patch.object(aptpkg, "refresh_db", refresh_mock): # Test success (no refresh) - self.assertEqual(aptpkg.show('foo*'), expected) + self.assertEqual(aptpkg.show("foo*"), expected) refresh_mock.assert_not_called() refresh_mock.reset_mock() # Test success (with refresh) - self.assertEqual(aptpkg.show('foo*', refresh=True), expected) + self.assertEqual(aptpkg.show("foo*", refresh=True), expected) self.assert_called_once(refresh_mock) refresh_mock.reset_mock() # Test filtered return self.assertEqual( - aptpkg.show('foo*', filter='description,provides'), - filtered + aptpkg.show("foo*", filter="description,provides"), filtered ) refresh_mock.assert_not_called() refresh_mock.reset_mock() - with patch.dict(aptpkg.__salt__, {'cmd.run_all': show_mock_failure}), \ - patch.object(aptpkg, 'refresh_db', refresh_mock): + with patch.dict( + aptpkg.__salt__, {"cmd.run_all": show_mock_failure} + ), patch.object(aptpkg, "refresh_db", refresh_mock): # Test failure (no refresh) - self.assertEqual(aptpkg.show('foo*'), {}) + self.assertEqual(aptpkg.show("foo*"), {}) refresh_mock.assert_not_called() refresh_mock.reset_mock() # Test failure (with refresh) - self.assertEqual(aptpkg.show('foo*', refresh=True), {}) + self.assertEqual(aptpkg.show("foo*", refresh=True), {}) self.assert_called_once(refresh_mock) refresh_mock.reset_mock() def test_mod_repo_enabled(self): - ''' + """ Checks if a repo is enabled or disabled depending on the passed kwargs. - ''' - with patch.dict(aptpkg.__salt__, {'config.option': MagicMock(), 'no_proxy': MagicMock(return_value=False)}): - with patch('salt.modules.aptpkg._check_apt', MagicMock(return_value=True)): - with patch('salt.modules.aptpkg.refresh_db', MagicMock(return_value={})): - with patch('salt.utils.data.is_true', MagicMock(return_value=True)) as data_is_true: - with patch('salt.modules.aptpkg.sourceslist', MagicMock(), create=True): - repo = aptpkg.mod_repo('foo', enabled=False) + """ + with patch.dict( + aptpkg.__salt__, + {"config.option": MagicMock(), "no_proxy": MagicMock(return_value=False)}, + ): + with patch("salt.modules.aptpkg._check_apt", MagicMock(return_value=True)): + with patch( + "salt.modules.aptpkg.refresh_db", MagicMock(return_value={}) + ): + with patch( + "salt.utils.data.is_true", MagicMock(return_value=True) + ) as data_is_true: + with patch( + "salt.modules.aptpkg.sourceslist", MagicMock(), create=True + ): + repo = aptpkg.mod_repo("foo", enabled=False) data_is_true.assert_called_with(False) # with disabled=True; should call salt.utils.data.is_true True data_is_true.reset_mock() - repo = aptpkg.mod_repo('foo', disabled=True) + repo = aptpkg.mod_repo("foo", disabled=True) data_is_true.assert_called_with(True) # with enabled=True; should call salt.utils.data.is_true with False data_is_true.reset_mock() - repo = aptpkg.mod_repo('foo', enabled=True) + repo = aptpkg.mod_repo("foo", enabled=True) data_is_true.assert_called_with(True) # with disabled=True; should call salt.utils.data.is_true False data_is_true.reset_mock() - repo = aptpkg.mod_repo('foo', disabled=False) + repo = aptpkg.mod_repo("foo", disabled=False) data_is_true.assert_called_with(False) - @patch('salt.utils.path.os_walk', MagicMock(return_value=[('test', 'test', 'test')])) - @patch('os.path.getsize', MagicMock(return_value=123456)) - @patch('os.path.getctime', MagicMock(return_value=1234567890.123456)) - @patch('fnmatch.filter', MagicMock(return_value=['/var/cache/apt/archive/test_package.rpm'])) + @patch( + "salt.utils.path.os_walk", MagicMock(return_value=[("test", "test", "test")]) + ) + @patch("os.path.getsize", MagicMock(return_value=123456)) + @patch("os.path.getctime", MagicMock(return_value=1234567890.123456)) + @patch( + "fnmatch.filter", + MagicMock(return_value=["/var/cache/apt/archive/test_package.rpm"]), + ) def test_list_downloaded(self): - ''' + """ Test downloaded packages listing. :return: - ''' + """ DOWNLOADED_RET = { - 'test-package': { - '1.0': { - 'path': '/var/cache/apt/archive/test_package.rpm', - 'size': 123456, - 'creation_date_time_t': 1234567890, - 'creation_date_time': '2009-02-13T23:31:30', + "test-package": { + "1.0": { + "path": "/var/cache/apt/archive/test_package.rpm", + "size": 123456, + "creation_date_time_t": 1234567890, + "creation_date_time": "2009-02-13T23:31:30", } } } - with patch.dict(aptpkg.__salt__, {'lowpkg.bin_pkg_info': MagicMock(return_value={'name': 'test-package', - 'version': '1.0'})}): + with patch.dict( + aptpkg.__salt__, + { + "lowpkg.bin_pkg_info": MagicMock( + return_value={"name": "test-package", "version": "1.0"} + ) + }, + ): list_downloaded = aptpkg.list_downloaded() self.assertEqual(len(list_downloaded), 1) self.assertDictEqual(list_downloaded, DOWNLOADED_RET) -@skipIf(pytest is None, 'PyTest is missing') +@skipIf(pytest is None, "PyTest is missing") class AptUtilsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ apt utils test case - ''' + """ + def setup_loader_modules(self): return {aptpkg: {}} def test_call_apt_default(self): - ''' + """ Call default apt. :return: - ''' - with patch.dict(aptpkg.__salt__, {'cmd.run_all': MagicMock(), 'config.get': MagicMock(return_value=False)}): - aptpkg._call_apt(['apt-get', 'install', 'emacs']) # pylint: disable=W0106 - aptpkg.__salt__['cmd.run_all'].assert_called_once_with( - ['apt-get', 'install', 'emacs'], env={}, - output_loglevel='trace', python_shell=False) + """ + with patch.dict( + aptpkg.__salt__, + {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=False)}, + ): + aptpkg._call_apt(["apt-get", "install", "emacs"]) # pylint: disable=W0106 + aptpkg.__salt__["cmd.run_all"].assert_called_once_with( + ["apt-get", "install", "emacs"], + env={}, + output_loglevel="trace", + python_shell=False, + ) - @patch('salt.utils.systemd.has_scope', MagicMock(return_value=True)) + @patch("salt.utils.systemd.has_scope", MagicMock(return_value=True)) def test_call_apt_in_scope(self): - ''' + """ Call apt within the scope. :return: - ''' - with patch.dict(aptpkg.__salt__, {'cmd.run_all': MagicMock(), 'config.get': MagicMock(return_value=True)}): - aptpkg._call_apt(['apt-get', 'purge', 'vim']) # pylint: disable=W0106 - aptpkg.__salt__['cmd.run_all'].assert_called_once_with( - ['systemd-run', '--scope', 'apt-get', 'purge', 'vim'], env={}, - output_loglevel='trace', python_shell=False) + """ + with patch.dict( + aptpkg.__salt__, + {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=True)}, + ): + aptpkg._call_apt(["apt-get", "purge", "vim"]) # pylint: disable=W0106 + aptpkg.__salt__["cmd.run_all"].assert_called_once_with( + [ + "systemd-run", + "--scope", + "--description", + '"salt.modules.aptpkg"', + "apt-get", + "purge", + "vim", + ], + env={}, + output_loglevel="trace", + python_shell=False, + ) def test_call_apt_with_kwargs(self): - ''' + """ Call apt with the optinal keyword arguments. :return: - ''' - with patch.dict(aptpkg.__salt__, {'cmd.run_all': MagicMock(), 'config.get': MagicMock(return_value=False)}): - aptpkg._call_apt(['dpkg', '-l', 'python'], - python_shell=True, output_loglevel='quiet', ignore_retcode=False, - username='Darth Vader') # pylint: disable=W0106 - aptpkg.__salt__['cmd.run_all'].assert_called_once_with( - ['dpkg', '-l', 'python'], env={}, ignore_retcode=False, - output_loglevel='quiet', python_shell=True, username='Darth Vader') + """ + with patch.dict( + aptpkg.__salt__, + {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=False)}, + ): + aptpkg._call_apt( + ["dpkg", "-l", "python"], + python_shell=True, + output_loglevel="quiet", + ignore_retcode=False, + username="Darth Vader", + ) # pylint: disable=W0106 + aptpkg.__salt__["cmd.run_all"].assert_called_once_with( + ["dpkg", "-l", "python"], + env={}, + ignore_retcode=False, + output_loglevel="quiet", + python_shell=True, + username="Darth Vader", + ) diff --git a/tests/unit/modules/test_archive.py b/tests/unit/modules/test_archive.py index d0a402e395d..4092c9aec60 100644 --- a/tests/unit/modules/test_archive.py +++ b/tests/unit/modules/test_archive.py @@ -1,20 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.unit.modules.archive_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os.path -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch +import os.path # Import salt libs import salt.modules.archive as archive @@ -24,11 +20,16 @@ from salt.exceptions import CommandNotFoundError # Import 3rd-party libs from salt.ext.six.moves import zip +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + class ZipFileMock(MagicMock): def __init__(self, files=None, **kwargs): # pylint: disable=W0231 if files is None: - files = ['salt'] + files = ["salt"] MagicMock.__init__(self, **kwargs) self._files = files self.external_attr = 0o0777 << 16 @@ -38,409 +39,538 @@ class ZipFileMock(MagicMock): class ArchiveTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - return {archive: {'__grains__': {'id': 0}}} + return {archive: {"__grains__": {"id": 0}}} def test_tar(self): - with patch('glob.glob', lambda pathname: [pathname]): - with patch('salt.utils.path.which', lambda exe: exe): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + with patch("glob.glob", lambda pathname: [pathname]): + with patch("salt.utils.path.which", lambda exe: exe): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): ret = archive.tar( - '-zcvf', 'foo.tar', - ['/tmp/something-to-compress-1', - '/tmp/something-to-compress-2'], - template=None + "-zcvf", + "foo.tar", + [ + "/tmp/something-to-compress-1", + "/tmp/something-to-compress-2", + ], + template=None, ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['tar', '-zcvf', 'foo.tar', '/tmp/something-to-compress-1', - '/tmp/something-to-compress-2'], - runas=None, python_shell=False, template=None, cwd=None + [ + "tar", + "-zcvf", + "foo.tar", + "/tmp/something-to-compress-1", + "/tmp/something-to-compress-2", + ], + runas=None, + python_shell=False, + template=None, + cwd=None, ) - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): ret = archive.tar( - '-zcvf', 'foo.tar', - '/tmp/something-to-compress-1,/tmp/something-to-compress-2', - template=None + "-zcvf", + "foo.tar", + "/tmp/something-to-compress-1,/tmp/something-to-compress-2", + template=None, ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['tar', '-zcvf', 'foo.tar', '/tmp/something-to-compress-1', - '/tmp/something-to-compress-2'], - runas=None, python_shell=False, template=None, cwd=None + [ + "tar", + "-zcvf", + "foo.tar", + "/tmp/something-to-compress-1", + "/tmp/something-to-compress-2", + ], + runas=None, + python_shell=False, + template=None, + cwd=None, ) def test_tar_raises_exception_if_not_found(self): - with patch('salt.utils.path.which', lambda exe: None): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + with patch("salt.utils.path.which", lambda exe: None): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): self.assertRaises( CommandNotFoundError, archive.tar, - 'zxvf', - 'foo.tar', - '/tmp/something-to-compress' + "zxvf", + "foo.tar", + "/tmp/something-to-compress", ) self.assertFalse(mock.called) def test_gzip(self): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): - with patch('salt.utils.path.which', lambda exe: exe): - ret = archive.gzip('/tmp/something-to-compress') - self.assertEqual(['salt'], ret) + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): + with patch("salt.utils.path.which", lambda exe: exe): + ret = archive.gzip("/tmp/something-to-compress") + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['gzip', '/tmp/something-to-compress'], - runas=None, python_shell=False, template=None + ["gzip", "/tmp/something-to-compress"], + runas=None, + python_shell=False, + template=None, ) def test_gzip_raises_exception_if_not_found(self): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): - with patch('salt.utils.path.which', lambda exe: None): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): + with patch("salt.utils.path.which", lambda exe: None): self.assertRaises( - CommandNotFoundError, - archive.gzip, '/tmp/something-to-compress' + CommandNotFoundError, archive.gzip, "/tmp/something-to-compress" ) self.assertFalse(mock.called) def test_gunzip(self): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): - with patch('salt.utils.path.which', lambda exe: exe): - ret = archive.gunzip('/tmp/something-to-decompress.tar.gz') - self.assertEqual(['salt'], ret) + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): + with patch("salt.utils.path.which", lambda exe: exe): + ret = archive.gunzip("/tmp/something-to-decompress.tar.gz") + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['gunzip', '/tmp/something-to-decompress.tar.gz'], - runas=None, python_shell=False, template=None + ["gunzip", "/tmp/something-to-decompress.tar.gz"], + runas=None, + python_shell=False, + template=None, ) def test_gunzip_raises_exception_if_not_found(self): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): - with patch('salt.utils.path.which', lambda exe: None): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): + with patch("salt.utils.path.which", lambda exe: None): self.assertRaises( CommandNotFoundError, archive.gunzip, - '/tmp/something-to-decompress.tar.gz' + "/tmp/something-to-decompress.tar.gz", ) self.assertFalse(mock.called) def test_cmd_zip(self): - with patch('glob.glob', lambda pathname: [pathname]): - with patch('salt.utils.path.which', lambda exe: exe): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + with patch("glob.glob", lambda pathname: [pathname]): + with patch("salt.utils.path.which", lambda exe: exe): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): ret = archive.cmd_zip( - '/tmp/salt.{{grains.id}}.zip', - '/tmp/tmpePe8yO,/tmp/tmpLeSw1A', - template='jinja' + "/tmp/salt.{{grains.id}}.zip", + "/tmp/tmpePe8yO,/tmp/tmpLeSw1A", + template="jinja", ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['zip', '-r', '/tmp/salt.{{grains.id}}.zip', - '/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - runas=None, python_shell=False, template='jinja', cwd=None + [ + "zip", + "-r", + "/tmp/salt.{{grains.id}}.zip", + "/tmp/tmpePe8yO", + "/tmp/tmpLeSw1A", + ], + runas=None, + python_shell=False, + template="jinja", + cwd=None, ) - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): ret = archive.cmd_zip( - '/tmp/salt.{{grains.id}}.zip', - ['/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - template='jinja' + "/tmp/salt.{{grains.id}}.zip", + ["/tmp/tmpePe8yO", "/tmp/tmpLeSw1A"], + template="jinja", ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['zip', '-r', '/tmp/salt.{{grains.id}}.zip', - '/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - runas=None, python_shell=False, template='jinja', cwd=None + [ + "zip", + "-r", + "/tmp/salt.{{grains.id}}.zip", + "/tmp/tmpePe8yO", + "/tmp/tmpLeSw1A", + ], + runas=None, + python_shell=False, + template="jinja", + cwd=None, ) def test_zip(self): - with patch('glob.glob', lambda pathname: [pathname]): - with patch.multiple(os.path, **{'isdir': MagicMock(return_value=False), - 'exists': MagicMock(return_value=True)}): - with patch('zipfile.ZipFile', MagicMock()): + with patch("glob.glob", lambda pathname: [pathname]): + with patch.multiple( + os.path, + **{ + "isdir": MagicMock(return_value=False), + "exists": MagicMock(return_value=True), + } + ): + with patch("zipfile.ZipFile", MagicMock()): ret = archive.zip_( - '/tmp/salt.{{grains.id}}.zip', - '/tmp/tmpePe8yO,/tmp/tmpLeSw1A', - template='jinja' + "/tmp/salt.{{grains.id}}.zip", + "/tmp/tmpePe8yO,/tmp/tmpLeSw1A", + template="jinja", ) - expected = [os.path.join('tmp', 'tmpePe8yO'), - os.path.join('tmp', 'tmpLeSw1A')] + expected = [ + os.path.join("tmp", "tmpePe8yO"), + os.path.join("tmp", "tmpLeSw1A"), + ] self.assertEqual(expected, ret) def test_zip_raises_exception_if_not_found(self): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): - with patch('salt.utils.path.which', lambda exe: None): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): + with patch("salt.utils.path.which", lambda exe: None): self.assertRaises( CommandNotFoundError, archive.cmd_zip, - '/tmp/salt.{{grains.id}}.zip', - '/tmp/tmpePe8yO,/tmp/tmpLeSw1A', - template='jinja', + "/tmp/salt.{{grains.id}}.zip", + "/tmp/tmpePe8yO,/tmp/tmpLeSw1A", + template="jinja", ) self.assertFalse(mock.called) def test_cmd_unzip(self): def _get_mock(): - ''' + """ Create a new MagicMock for each scenario in this test, so that assert_called_once_with doesn't complain that the same mock object is called more than once. - ''' - return MagicMock(return_value={'stdout': 'salt', - 'stderr': '', - 'pid': 12345, - 'retcode': 0}) + """ + return MagicMock( + return_value={ + "stdout": "salt", + "stderr": "", + "pid": 12345, + "retcode": 0, + } + ) - with patch('salt.utils.path.which', lambda exe: exe): + with patch("salt.utils.path.which", lambda exe: exe): mock = _get_mock() - with patch.dict(archive.__salt__, {'cmd.run_all': mock}): + with patch.dict(archive.__salt__, {"cmd.run_all": mock}): ret = archive.cmd_unzip( - '/tmp/salt.{{grains.id}}.zip', - '/tmp/dest', - excludes='/tmp/tmpePe8yO,/tmp/tmpLeSw1A', - template='jinja' + "/tmp/salt.{{grains.id}}.zip", + "/tmp/dest", + excludes="/tmp/tmpePe8yO,/tmp/tmpLeSw1A", + template="jinja", ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['unzip', '/tmp/salt.{{grains.id}}.zip', '-d', '/tmp/dest', - '-x', '/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - output_loglevel='debug', + [ + "unzip", + "/tmp/salt.{{grains.id}}.zip", + "-d", + "/tmp/dest", + "-x", + "/tmp/tmpePe8yO", + "/tmp/tmpLeSw1A", + ], + output_loglevel="debug", python_shell=False, redirect_stderr=True, runas=None, - template='jinja' + template="jinja", ) mock = _get_mock() - with patch.dict(archive.__salt__, {'cmd.run_all': mock}): + with patch.dict(archive.__salt__, {"cmd.run_all": mock}): ret = archive.cmd_unzip( - '/tmp/salt.{{grains.id}}.zip', - '/tmp/dest', - excludes=['/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - template='jinja' + "/tmp/salt.{{grains.id}}.zip", + "/tmp/dest", + excludes=["/tmp/tmpePe8yO", "/tmp/tmpLeSw1A"], + template="jinja", ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['unzip', '/tmp/salt.{{grains.id}}.zip', '-d', '/tmp/dest', - '-x', '/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - output_loglevel='debug', + [ + "unzip", + "/tmp/salt.{{grains.id}}.zip", + "-d", + "/tmp/dest", + "-x", + "/tmp/tmpePe8yO", + "/tmp/tmpLeSw1A", + ], + output_loglevel="debug", python_shell=False, redirect_stderr=True, runas=None, - template='jinja' + template="jinja", ) mock = _get_mock() - with patch.dict(archive.__salt__, {'cmd.run_all': mock}): + with patch.dict(archive.__salt__, {"cmd.run_all": mock}): ret = archive.cmd_unzip( - '/tmp/salt.{{grains.id}}.zip', - '/tmp/dest', - excludes='/tmp/tmpePe8yO,/tmp/tmpLeSw1A', - template='jinja', - options='-fo' + "/tmp/salt.{{grains.id}}.zip", + "/tmp/dest", + excludes="/tmp/tmpePe8yO,/tmp/tmpLeSw1A", + template="jinja", + options="-fo", ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['unzip', '-fo', '/tmp/salt.{{grains.id}}.zip', '-d', - '/tmp/dest', '-x', '/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - output_loglevel='debug', + [ + "unzip", + "-fo", + "/tmp/salt.{{grains.id}}.zip", + "-d", + "/tmp/dest", + "-x", + "/tmp/tmpePe8yO", + "/tmp/tmpLeSw1A", + ], + output_loglevel="debug", python_shell=False, redirect_stderr=True, runas=None, - template='jinja' + template="jinja", ) mock = _get_mock() - with patch.dict(archive.__salt__, {'cmd.run_all': mock}): + with patch.dict(archive.__salt__, {"cmd.run_all": mock}): ret = archive.cmd_unzip( - '/tmp/salt.{{grains.id}}.zip', - '/tmp/dest', - excludes=['/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - template='jinja', - options='-fo' + "/tmp/salt.{{grains.id}}.zip", + "/tmp/dest", + excludes=["/tmp/tmpePe8yO", "/tmp/tmpLeSw1A"], + template="jinja", + options="-fo", ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['unzip', '-fo', '/tmp/salt.{{grains.id}}.zip', '-d', - '/tmp/dest', '-x', '/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - output_loglevel='debug', + [ + "unzip", + "-fo", + "/tmp/salt.{{grains.id}}.zip", + "-d", + "/tmp/dest", + "-x", + "/tmp/tmpePe8yO", + "/tmp/tmpLeSw1A", + ], + output_loglevel="debug", python_shell=False, redirect_stderr=True, runas=None, - template='jinja' + template="jinja", ) mock = _get_mock() - with patch.dict(archive.__salt__, {'cmd.run_all': mock}): + with patch.dict(archive.__salt__, {"cmd.run_all": mock}): ret = archive.cmd_unzip( - '/tmp/salt.{{grains.id}}.zip', - '/tmp/dest', - excludes=['/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - template='jinja', - options='-fo', - password='asdf', + "/tmp/salt.{{grains.id}}.zip", + "/tmp/dest", + excludes=["/tmp/tmpePe8yO", "/tmp/tmpLeSw1A"], + template="jinja", + options="-fo", + password="asdf", ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['unzip', '-P', 'asdf', '-fo', '/tmp/salt.{{grains.id}}.zip', - '-d', '/tmp/dest', '-x', '/tmp/tmpePe8yO', '/tmp/tmpLeSw1A'], - output_loglevel='quiet', + [ + "unzip", + "-P", + "asdf", + "-fo", + "/tmp/salt.{{grains.id}}.zip", + "-d", + "/tmp/dest", + "-x", + "/tmp/tmpePe8yO", + "/tmp/tmpLeSw1A", + ], + output_loglevel="quiet", python_shell=False, redirect_stderr=True, runas=None, - template='jinja' + template="jinja", ) def test_unzip(self): mock = ZipFileMock() - with patch('zipfile.ZipFile', mock): + with patch("zipfile.ZipFile", mock): ret = archive.unzip( - '/tmp/salt.{{grains.id}}.zip', - '/tmp/dest', - excludes='/tmp/tmpePe8yO,/tmp/tmpLeSw1A', - template='jinja', - extract_perms=False + "/tmp/salt.{{grains.id}}.zip", + "/tmp/dest", + excludes="/tmp/tmpePe8yO,/tmp/tmpLeSw1A", + template="jinja", + extract_perms=False, ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) def test_unzip_raises_exception_if_not_found(self): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): - with patch('salt.utils.path.which', lambda exe: None): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): + with patch("salt.utils.path.which", lambda exe: None): self.assertRaises( CommandNotFoundError, archive.cmd_unzip, - '/tmp/salt.{{grains.id}}.zip', - '/tmp/dest', - excludes='/tmp/tmpePe8yO,/tmp/tmpLeSw1A', - template='jinja', + "/tmp/salt.{{grains.id}}.zip", + "/tmp/dest", + excludes="/tmp/tmpePe8yO,/tmp/tmpLeSw1A", + template="jinja", ) self.assertFalse(mock.called) def test_rar(self): - with patch('glob.glob', lambda pathname: [pathname]): - with patch('salt.utils.path.which', lambda exe: exe): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + with patch("glob.glob", lambda pathname: [pathname]): + with patch("salt.utils.path.which", lambda exe: exe): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): ret = archive.rar( - '/tmp/rarfile.rar', - '/tmp/sourcefile1,/tmp/sourcefile2' + "/tmp/rarfile.rar", "/tmp/sourcefile1,/tmp/sourcefile2" ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['rar', 'a', '-idp', '/tmp/rarfile.rar', - '/tmp/sourcefile1', '/tmp/sourcefile2'], - runas=None, python_shell=False, template=None, cwd=None + [ + "rar", + "a", + "-idp", + "/tmp/rarfile.rar", + "/tmp/sourcefile1", + "/tmp/sourcefile2", + ], + runas=None, + python_shell=False, + template=None, + cwd=None, ) - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): ret = archive.rar( - '/tmp/rarfile.rar', - ['/tmp/sourcefile1', '/tmp/sourcefile2'] + "/tmp/rarfile.rar", ["/tmp/sourcefile1", "/tmp/sourcefile2"] ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['rar', 'a', '-idp', '/tmp/rarfile.rar', - '/tmp/sourcefile1', '/tmp/sourcefile2'], - runas=None, python_shell=False, template=None, cwd=None + [ + "rar", + "a", + "-idp", + "/tmp/rarfile.rar", + "/tmp/sourcefile1", + "/tmp/sourcefile2", + ], + runas=None, + python_shell=False, + template=None, + cwd=None, ) def test_rar_raises_exception_if_not_found(self): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): - with patch('salt.utils.path.which', lambda exe: None): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): + with patch("salt.utils.path.which", lambda exe: None): self.assertRaises( CommandNotFoundError, archive.rar, - '/tmp/rarfile.rar', - '/tmp/sourcefile1,/tmp/sourcefile2' + "/tmp/rarfile.rar", + "/tmp/sourcefile1,/tmp/sourcefile2", ) self.assertFalse(mock.called) - @skipIf(salt.utils.path.which_bin(('unrar', 'rar')) is None, 'unrar not installed') + @skipIf(salt.utils.path.which_bin(("unrar", "rar")) is None, "unrar not installed") def test_unrar(self): - with patch('salt.utils.path.which_bin', lambda exe: exe[0] if isinstance(exe, (list, tuple)) else exe): - with patch('salt.utils.path.which', lambda exe: exe[0] if isinstance(exe, (list, tuple)) else exe): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + with patch( + "salt.utils.path.which_bin", + lambda exe: exe[0] if isinstance(exe, (list, tuple)) else exe, + ): + with patch( + "salt.utils.path.which", + lambda exe: exe[0] if isinstance(exe, (list, tuple)) else exe, + ): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): ret = archive.unrar( - '/tmp/rarfile.rar', - '/home/strongbad/', - excludes='file_1,file_2' + "/tmp/rarfile.rar", "/home/strongbad/", excludes="file_1,file_2" ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['unrar', 'x', '-idp', '/tmp/rarfile.rar', - '-x', 'file_1', '-x', 'file_2', '/home/strongbad/'], - runas=None, python_shell=False, template=None + [ + "unrar", + "x", + "-idp", + "/tmp/rarfile.rar", + "-x", + "file_1", + "-x", + "file_2", + "/home/strongbad/", + ], + runas=None, + python_shell=False, + template=None, ) - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): ret = archive.unrar( - '/tmp/rarfile.rar', - '/home/strongbad/', - excludes=['file_1', 'file_2'] + "/tmp/rarfile.rar", + "/home/strongbad/", + excludes=["file_1", "file_2"], ) - self.assertEqual(['salt'], ret) + self.assertEqual(["salt"], ret) mock.assert_called_once_with( - ['unrar', 'x', '-idp', '/tmp/rarfile.rar', - '-x', 'file_1', '-x', 'file_2', '/home/strongbad/'], - runas=None, python_shell=False, template=None + [ + "unrar", + "x", + "-idp", + "/tmp/rarfile.rar", + "-x", + "file_1", + "-x", + "file_2", + "/home/strongbad/", + ], + runas=None, + python_shell=False, + template=None, ) def test_unrar_raises_exception_if_not_found(self): - with patch('salt.utils.path.which_bin', lambda exe: None): - mock = MagicMock(return_value='salt') - with patch.dict(archive.__salt__, {'cmd.run': mock}): + with patch("salt.utils.path.which_bin", lambda exe: None): + mock = MagicMock(return_value="salt") + with patch.dict(archive.__salt__, {"cmd.run": mock}): self.assertRaises( CommandNotFoundError, archive.unrar, - '/tmp/rarfile.rar', - '/home/strongbad/', + "/tmp/rarfile.rar", + "/home/strongbad/", ) self.assertFalse(mock.called) def test_trim_files(self): - with patch('salt.utils.path.which_bin', lambda exe: exe): - source = 'file.tar.gz' - tmp_dir = 'temp' + with patch("salt.utils.path.which_bin", lambda exe: exe): + source = "file.tar.gz" + tmp_dir = "temp" files = [ - '\n'.join(['file1'] * 200), - '\n'.join(['file2'] * 200), - 'this\nthat\nother', - 'this\nthat\nother', - 'this\nthat\nother' + "\n".join(["file1"] * 200), + "\n".join(["file2"] * 200), + "this\nthat\nother", + "this\nthat\nother", + "this\nthat\nother", ] - trim_opt = [ - True, - False, - 3, - 1, - 0 - ] - trim_100 = (['file1'] * 100) + trim_opt = [True, False, 3, 1, 0] + trim_100 = ["file1"] * 100 trim_100.append("List trimmed after 100 files.") expected = [ trim_100, - ['file2'] * 200, - ['this', 'that', 'other'], - ['this', 'List trimmed after 1 files.'], - ['List trimmed after 0 files.'], + ["file2"] * 200, + ["this", "that", "other"], + ["this", "List trimmed after 1 files."], + ["List trimmed after 0 files."], ] - for test_files, test_trim_opts, test_expected in zip(files, trim_opt, expected): - with patch.dict(archive.__salt__, {'cmd.run': MagicMock(return_value=test_files)}): + for test_files, test_trim_opts, test_expected in zip( + files, trim_opt, expected + ): + with patch.dict( + archive.__salt__, {"cmd.run": MagicMock(return_value=test_files)} + ): ret = archive.unrar(source, tmp_dir, trim_output=test_trim_opts) self.assertEqual(ret, test_expected) diff --git a/tests/unit/modules/test_artifactory.py b/tests/unit/modules/test_artifactory.py index 67bcf67a6c3..f26300848e9 100644 --- a/tests/unit/modules/test_artifactory.py +++ b/tests/unit/modules/test_artifactory.py @@ -3,22 +3,24 @@ # Import pytohn libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt libs import salt.modules.artifactory as artifactory +# Import Salt testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ArtifactoryTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {artifactory: {}} def test_artifact_get_metadata(self): - with patch('salt.modules.artifactory._get_artifact_metadata_xml', MagicMock(return_value='''<?xml version="1.0" encoding="UTF-8"?> + with patch( + "salt.modules.artifactory._get_artifact_metadata_xml", + MagicMock( + return_value="""<?xml version="1.0" encoding="UTF-8"?> <metadata> <groupId>com.company.sampleapp.web-module</groupId> <artifactId>web</artifactId> @@ -32,16 +34,23 @@ class ArtifactoryTestCase(TestCase, LoaderModuleMockMixin): <lastUpdated>20140623120632</lastUpdated> </versioning> </metadata> - ''')): - metadata = artifactory._get_artifact_metadata(artifactory_url='http://artifactory.example.com/artifactory', - repository='libs-releases', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - headers={}) - self.assertEqual(metadata['latest_version'], '1.1_RC11') + """ + ), + ): + metadata = artifactory._get_artifact_metadata( + artifactory_url="http://artifactory.example.com/artifactory", + repository="libs-releases", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + headers={}, + ) + self.assertEqual(metadata["latest_version"], "1.1_RC11") def test_snapshot_version_get_metadata(self): - with patch('salt.modules.artifactory._get_snapshot_version_metadata_xml', MagicMock(return_value='''<?xml version="1.0" encoding="UTF-8"?> + with patch( + "salt.modules.artifactory._get_snapshot_version_metadata_xml", + MagicMock( + return_value="""<?xml version="1.0" encoding="UTF-8"?> <metadata> <groupId>com.company.sampleapp.web-module</groupId> <artifactId>web</artifactId> @@ -66,53 +75,86 @@ class ArtifactoryTestCase(TestCase, LoaderModuleMockMixin): </snapshotVersions> </versioning> </metadata> - ''')): - metadata = artifactory._get_snapshot_version_metadata(artifactory_url='http://artifactory.example.com/artifactory', - repository='libs-releases', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - version='1.1_RC8-SNAPSHOT', - headers={}) - self.assertEqual(metadata['snapshot_versions']['war'], '1.1_RC8-20140418.150212-1') + """ + ), + ): + metadata = artifactory._get_snapshot_version_metadata( + artifactory_url="http://artifactory.example.com/artifactory", + repository="libs-releases", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + version="1.1_RC8-SNAPSHOT", + headers={}, + ) + self.assertEqual( + metadata["snapshot_versions"]["war"], "1.1_RC8-20140418.150212-1" + ) def test_artifact_metadata_url(self): - metadata_url = artifactory._get_artifact_metadata_url(artifactory_url='http://artifactory.example.com/artifactory', - repository='libs-releases', - group_id='com.company.sampleapp.web-module', - artifact_id='web') + metadata_url = artifactory._get_artifact_metadata_url( + artifactory_url="http://artifactory.example.com/artifactory", + repository="libs-releases", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + ) - self.assertEqual(metadata_url, "http://artifactory.example.com/artifactory/libs-releases/com/company/sampleapp/web-module/web/maven-metadata.xml") + self.assertEqual( + metadata_url, + "http://artifactory.example.com/artifactory/libs-releases/com/company/sampleapp/web-module/web/maven-metadata.xml", + ) def test_snapshot_version_metadata_url(self): - metadata_url = artifactory._get_snapshot_version_metadata_url(artifactory_url='http://artifactory.example.com/artifactory', - repository='libs-snapshots', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - version='1.0_RC10-SNAPSHOT') + metadata_url = artifactory._get_snapshot_version_metadata_url( + artifactory_url="http://artifactory.example.com/artifactory", + repository="libs-snapshots", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + version="1.0_RC10-SNAPSHOT", + ) - self.assertEqual(metadata_url, "http://artifactory.example.com/artifactory/libs-snapshots/com/company/sampleapp/web-module/web/1.0_RC10-SNAPSHOT/maven-metadata.xml") + self.assertEqual( + metadata_url, + "http://artifactory.example.com/artifactory/libs-snapshots/com/company/sampleapp/web-module/web/1.0_RC10-SNAPSHOT/maven-metadata.xml", + ) def test_construct_url_for_released_version(self): - artifact_url, file_name = artifactory._get_release_url(repository='libs-releases', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - packaging='war', - artifactory_url='http://artifactory.example.com/artifactory', - version='1.0_RC20') + artifact_url, file_name = artifactory._get_release_url( + repository="libs-releases", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + packaging="war", + artifactory_url="http://artifactory.example.com/artifactory", + version="1.0_RC20", + ) - self.assertEqual(artifact_url, "http://artifactory.example.com/artifactory/libs-releases/com/company/sampleapp/web-module/web/1.0_RC20/web-1.0_RC20.war") + self.assertEqual( + artifact_url, + "http://artifactory.example.com/artifactory/libs-releases/com/company/sampleapp/web-module/web/1.0_RC20/web-1.0_RC20.war", + ) self.assertEqual(file_name, "web-1.0_RC20.war") def test_construct_url_for_snapshot_version(self): - with patch('salt.modules.artifactory._get_snapshot_version_metadata', MagicMock(return_value={'snapshot_versions': {'war': '1.0_RC10-20131127.105838-2'}})): + with patch( + "salt.modules.artifactory._get_snapshot_version_metadata", + MagicMock( + return_value={ + "snapshot_versions": {"war": "1.0_RC10-20131127.105838-2"} + } + ), + ): - artifact_url, file_name = artifactory._get_snapshot_url(artifactory_url='http://artifactory.example.com/artifactory', - repository='libs-snapshots', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - version='1.0_RC10-SNAPSHOT', - packaging='war', - headers={}) + artifact_url, file_name = artifactory._get_snapshot_url( + artifactory_url="http://artifactory.example.com/artifactory", + repository="libs-snapshots", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + version="1.0_RC10-SNAPSHOT", + packaging="war", + headers={}, + ) - self.assertEqual(artifact_url, "http://artifactory.example.com/artifactory/libs-snapshots/com/company/sampleapp/web-module/web/1.0_RC10-SNAPSHOT/web-1.0_RC10-20131127.105838-2.war") + self.assertEqual( + artifact_url, + "http://artifactory.example.com/artifactory/libs-snapshots/com/company/sampleapp/web-module/web/1.0_RC10-SNAPSHOT/web-1.0_RC10-20131127.105838-2.war", + ) self.assertEqual(file_name, "web-1.0_RC10-20131127.105838-2.war") diff --git a/tests/unit/modules/test_at.py b/tests/unit/modules/test_at.py index 0b5978a2b01..1638f672cde 100644 --- a/tests/unit/modules/test_at.py +++ b/tests/unit/modules/test_at.py @@ -1,200 +1,252 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.at as at # Import Salt Libs import salt.utils.path -import salt.modules.at as at + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class AtTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for the salt.modules.at module - ''' + """ def setup_loader_modules(self): return {at: {}} - atq_output = {'jobs': [{'date': '2014-12-11', 'job': 101, 'queue': 'A', - 'tag': '', 'time': '19:48:47', 'user': 'B'}]} + atq_output = { + "jobs": [ + { + "date": "2014-12-11", + "job": 101, + "queue": "A", + "tag": "", + "time": "19:48:47", + "user": "B", + } + ] + } @classmethod def tearDownClass(cls): del cls.atq_output def test_atq_not_available(self): - ''' + """ Tests the at.atq not available for any type of os_family. - ''' - with patch('salt.modules.at._cmd', MagicMock(return_value=None)): - with patch.dict(at.__grains__, {'os_family': 'RedHat'}): - self.assertEqual(at.atq(), '\'at.atq\' is not available.') + """ + with patch("salt.modules.at._cmd", MagicMock(return_value=None)): + with patch.dict(at.__grains__, {"os_family": "RedHat"}): + self.assertEqual(at.atq(), "'at.atq' is not available.") - with patch.dict(at.__grains__, {'os_family': ''}): - self.assertEqual(at.atq(), '\'at.atq\' is not available.') + with patch.dict(at.__grains__, {"os_family": ""}): + self.assertEqual(at.atq(), "'at.atq' is not available.") def test_atq_no_jobs_available(self): - ''' + """ Tests the no jobs available for any type of os_family. - ''' - with patch('salt.modules.at._cmd', MagicMock(return_value='')): - with patch.dict(at.__grains__, {'os_family': 'RedHat'}): - self.assertDictEqual(at.atq(), {'jobs': []}) + """ + with patch("salt.modules.at._cmd", MagicMock(return_value="")): + with patch.dict(at.__grains__, {"os_family": "RedHat"}): + self.assertDictEqual(at.atq(), {"jobs": []}) - with patch.dict(at.__grains__, {'os_family': ''}): - self.assertDictEqual(at.atq(), {'jobs': []}) + with patch.dict(at.__grains__, {"os_family": ""}): + self.assertDictEqual(at.atq(), {"jobs": []}) def test_atq_list(self): - ''' + """ Tests the list all queued and running jobs. - ''' - with patch('salt.modules.at._cmd') as salt_modules_at__cmd_mock: - salt_modules_at__cmd_mock.return_value = '101\tThu Dec 11 \ - 19:48:47 2014 A B' - with patch.dict(at.__grains__, {'os_family': '', 'os': ''}): - self.assertDictEqual(at.atq(), {'jobs': [{'date': '2014-12-11', - 'job': 101, - 'queue': 'A', - 'tag': '', - 'time': '19:48:00', - 'user': 'B'}]}) + """ + with patch("salt.modules.at._cmd") as salt_modules_at__cmd_mock: + salt_modules_at__cmd_mock.return_value = "101\tThu Dec 11 \ + 19:48:47 2014 A B" + with patch.dict(at.__grains__, {"os_family": "", "os": ""}): + self.assertDictEqual( + at.atq(), + { + "jobs": [ + { + "date": "2014-12-11", + "job": 101, + "queue": "A", + "tag": "", + "time": "19:48:00", + "user": "B", + } + ] + }, + ) - salt_modules_at__cmd_mock.return_value = '101\t2014-12-11 \ - 19:48:47 A B' - with patch.dict(at.__grains__, {'os_family': 'RedHat', 'os': ''}): - self.assertDictEqual(at.atq(), {'jobs': [{'date': '2014-12-11', - 'job': 101, - 'queue': 'A', - 'tag': '', - 'time': '19:48:47', - 'user': 'B'}]}) + salt_modules_at__cmd_mock.return_value = "101\t2014-12-11 \ + 19:48:47 A B" + with patch.dict(at.__grains__, {"os_family": "RedHat", "os": ""}): + self.assertDictEqual( + at.atq(), + { + "jobs": [ + { + "date": "2014-12-11", + "job": 101, + "queue": "A", + "tag": "", + "time": "19:48:47", + "user": "B", + } + ] + }, + ) - salt_modules_at__cmd_mock.return_value = 'SALT: Dec 11, \ - 2014 19:48 A 101 B' - with patch.dict(at.__grains__, {'os_family': '', 'os': 'OpenBSD'}): - self.assertDictEqual(at.atq(), {'jobs': [{'date': '2014-12-11', - 'job': '101', - 'queue': 'B', - 'tag': '', - 'time': '19:48:00', - 'user': 'A'}]}) + salt_modules_at__cmd_mock.return_value = "SALT: Dec 11, \ + 2014 19:48 A 101 B" + with patch.dict(at.__grains__, {"os_family": "", "os": "OpenBSD"}): + self.assertDictEqual( + at.atq(), + { + "jobs": [ + { + "date": "2014-12-11", + "job": "101", + "queue": "B", + "tag": "", + "time": "19:48:00", + "user": "A", + } + ] + }, + ) def test_atrm(self): """ Tests for remove jobs from the queue. """ - with patch('salt.modules.at.atq', MagicMock(return_value=self.atq_output)): - with patch.object(salt.utils.path, 'which', return_value=None): + with patch("salt.modules.at.atq", MagicMock(return_value=self.atq_output)): + with patch.object(salt.utils.path, "which", return_value=None): self.assertEqual(at.atrm(), "'at.atrm' is not available.") - with patch.object(salt.utils.path, 'which', return_value=True): - self.assertDictEqual(at.atrm(), {'jobs': {'removed': [], - 'tag': None}}) + with patch.object(salt.utils.path, "which", return_value=True): + self.assertDictEqual(at.atrm(), {"jobs": {"removed": [], "tag": None}}) - with patch.object(at, '_cmd', return_value=True): - with patch.object(salt.utils.path, 'which', return_value=True): - self.assertDictEqual(at.atrm('all'), - {'jobs': {'removed': ['101'], - 'tag': None}}) + with patch.object(at, "_cmd", return_value=True): + with patch.object(salt.utils.path, "which", return_value=True): + self.assertDictEqual( + at.atrm("all"), {"jobs": {"removed": ["101"], "tag": None}} + ) - with patch.object(at, '_cmd', return_value=True): - with patch.object(salt.utils.path, 'which', return_value=True): - self.assertDictEqual(at.atrm(101), - {'jobs': {'removed': ['101'], - 'tag': None}}) + with patch.object(at, "_cmd", return_value=True): + with patch.object(salt.utils.path, "which", return_value=True): + self.assertDictEqual( + at.atrm(101), {"jobs": {"removed": ["101"], "tag": None}} + ) - with patch.object(at, '_cmd', return_value=None): - self.assertEqual(at.atrm(101), '\'at.atrm\' is not available.') + with patch.object(at, "_cmd", return_value=None): + self.assertEqual(at.atrm(101), "'at.atrm' is not available.") def test_jobcheck(self): """ Tests for check the job from queue. """ - with patch('salt.modules.at.atq', MagicMock(return_value=self.atq_output)): - self.assertDictEqual(at.jobcheck(), - {'error': 'You have given a condition'}) + with patch("salt.modules.at.atq", MagicMock(return_value=self.atq_output)): + self.assertDictEqual(at.jobcheck(), {"error": "You have given a condition"}) - self.assertDictEqual(at.jobcheck(runas='foo'), - {'note': 'No match jobs or time format error', - 'jobs': []}) + self.assertDictEqual( + at.jobcheck(runas="foo"), + {"note": "No match jobs or time format error", "jobs": []}, + ) - self.assertDictEqual(at.jobcheck(runas='B', tag='', hour=19, minute=48, - day=11, month=12, Year=2014), - {'jobs': [{'date': '2014-12-11', - 'job': 101, - 'queue': 'A', - 'tag': '', - 'time': '19:48:47', - 'user': 'B'}]}) + self.assertDictEqual( + at.jobcheck( + runas="B", tag="", hour=19, minute=48, day=11, month=12, Year=2014 + ), + { + "jobs": [ + { + "date": "2014-12-11", + "job": 101, + "queue": "A", + "tag": "", + "time": "19:48:47", + "user": "B", + } + ] + }, + ) def test_at(self): """ Tests for add a job to the queue. """ - with patch('salt.modules.at.atq', MagicMock(return_value=self.atq_output)): - self.assertDictEqual(at.at(), {'jobs': []}) + with patch("salt.modules.at.atq", MagicMock(return_value=self.atq_output)): + self.assertDictEqual(at.at(), {"jobs": []}) - with patch.object(salt.utils.path, 'which', return_value=None): - self.assertEqual(at.at('12:05am', '/sbin/reboot', tag='reboot'), - "'at.at' is not available.") + with patch.object(salt.utils.path, "which", return_value=None): + self.assertEqual( + at.at("12:05am", "/sbin/reboot", tag="reboot"), + "'at.at' is not available.", + ) - with patch.object(salt.utils.path, 'which', return_value=True): - with patch.dict(at.__grains__, {'os_family': 'RedHat'}): + with patch.object(salt.utils.path, "which", return_value=True): + with patch.dict(at.__grains__, {"os_family": "RedHat"}): mock = MagicMock(return_value=None) - with patch.dict(at.__salt__, {'cmd.run': mock}): - self.assertEqual(at.at('12:05am', '/sbin/reboot', - tag='reboot'), - "'at.at' is not available.") + with patch.dict(at.__salt__, {"cmd.run": mock}): + self.assertEqual( + at.at("12:05am", "/sbin/reboot", tag="reboot"), + "'at.at' is not available.", + ) - mock = MagicMock(return_value='Garbled time') - with patch.dict(at.__salt__, {'cmd.run': mock}): - self.assertDictEqual(at.at('12:05am', '/sbin/reboot', - tag='reboot'), - {'jobs': [], - 'error': 'invalid timespec'}) + mock = MagicMock(return_value="Garbled time") + with patch.dict(at.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + at.at("12:05am", "/sbin/reboot", tag="reboot"), + {"jobs": [], "error": "invalid timespec"}, + ) - mock = MagicMock(return_value='warning: commands\nA B') - with patch.dict(at.__salt__, {'cmd.run': mock}): - with patch.dict(at.__grains__, {'os': 'OpenBSD'}): - self.assertDictEqual(at.at('12:05am', '/sbin/reboot', - tag='reboot'), - {'jobs': [{'date': '2014-12-11', - 'job': 101, - 'queue': 'A', - 'tag': '', - 'time': '19:48:47', - 'user': 'B'}]}) + mock = MagicMock(return_value="warning: commands\nA B") + with patch.dict(at.__salt__, {"cmd.run": mock}): + with patch.dict(at.__grains__, {"os": "OpenBSD"}): + self.assertDictEqual( + at.at("12:05am", "/sbin/reboot", tag="reboot"), + { + "jobs": [ + { + "date": "2014-12-11", + "job": 101, + "queue": "A", + "tag": "", + "time": "19:48:47", + "user": "B", + } + ] + }, + ) - with patch.dict(at.__grains__, {'os_family': ''}): + with patch.dict(at.__grains__, {"os_family": ""}): mock = MagicMock(return_value=None) - with patch.dict(at.__salt__, {'cmd.run': mock}): - self.assertEqual(at.at('12:05am', '/sbin/reboot', - tag='reboot'), - "'at.at' is not available.") + with patch.dict(at.__salt__, {"cmd.run": mock}): + self.assertEqual( + at.at("12:05am", "/sbin/reboot", tag="reboot"), + "'at.at' is not available.", + ) def test_atc(self): """ Tests for atc """ - with patch.object(at, '_cmd', return_value=None): - self.assertEqual(at.atc(101), '\'at.atc\' is not available.') + with patch.object(at, "_cmd", return_value=None): + self.assertEqual(at.atc(101), "'at.atc' is not available.") - with patch.object(at, '_cmd', return_value=''): - self.assertDictEqual(at.atc(101), - {'error': 'invalid job id \'101\''}) + with patch.object(at, "_cmd", return_value=""): + self.assertDictEqual(at.atc(101), {"error": "invalid job id '101'"}) - with patch.object(at, '_cmd', - return_value='101\tThu Dec 11 19:48:47 2014 A B'): - self.assertEqual(at.atc(101), '101\tThu Dec 11 19:48:47 2014 A B') + with patch.object(at, "_cmd", return_value="101\tThu Dec 11 19:48:47 2014 A B"): + self.assertEqual(at.atc(101), "101\tThu Dec 11 19:48:47 2014 A B") diff --git a/tests/unit/modules/test_augeas_cfg.py b/tests/unit/modules/test_augeas_cfg.py index 4ec3c2409d2..17fbb6f1668 100644 --- a/tests/unit/modules/test_augeas_cfg.py +++ b/tests/unit/modules/test_augeas_cfg.py @@ -1,21 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) # Import Salt Libs import salt.modules.augeas_cfg as augeas_cfg from salt.exceptions import SaltInvocationError from salt.ext import six +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf + # Make sure augeas python interface is installed if augeas_cfg.HAS_AUGEAS: from augeas import Augeas as _Augeas @@ -23,129 +22,145 @@ if augeas_cfg.HAS_AUGEAS: @skipIf(augeas_cfg.HAS_AUGEAS is False, "python-augeas is required for this test case") class AugeasCfgTestCase(TestCase): - ''' + """ Test cases for salt.modules.augeas_cfg - ''' + """ + # 'execute' function tests: 3 - @skipIf(six.PY3, 'Disabled pending https://github.com/hercules-team/python-augeas/issues/30') + @skipIf( + six.PY3, + "Disabled pending https://github.com/hercules-team/python-augeas/issues/30", + ) def test_execute(self): - ''' + """ Test if it execute Augeas commands - ''' - self.assertEqual(augeas_cfg.execute(), {'retval': True}) + """ + self.assertEqual(augeas_cfg.execute(), {"retval": True}) def test_execute_io_error(self): - ''' + """ Test if it execute Augeas commands - ''' - ret = {'error': 'Command is not supported (yet)', 'retval': False} + """ + ret = {"error": "Command is not supported (yet)", "retval": False} self.assertEqual(augeas_cfg.execute(None, None, [" "]), ret) def test_execute_value_error(self): - ''' + """ Test if it execute Augeas commands - ''' - ret = {'retval': False, - 'error': - 'Invalid formatted command, see debug log for details: '} + """ + ret = { + "retval": False, + "error": "Invalid formatted command, see debug log for details: ", + } self.assertEqual(augeas_cfg.execute(None, None, ["set "]), ret) # 'get' function tests: 1 def test_get(self): - ''' + """ Test if it get a value for a specific augeas path - ''' - mock = MagicMock(side_effect=RuntimeError('error')) - with patch.object(_Augeas, 'match', mock): - self.assertEqual(augeas_cfg.get('/etc/hosts'), - {'error': 'error'}) + """ + mock = MagicMock(side_effect=RuntimeError("error")) + with patch.object(_Augeas, "match", mock): + self.assertEqual(augeas_cfg.get("/etc/hosts"), {"error": "error"}) mock = MagicMock(return_value=True) - with patch.object(_Augeas, 'match', mock): - self.assertEqual(augeas_cfg.get('/etc/hosts'), - {'/etc/hosts': None}) + with patch.object(_Augeas, "match", mock): + self.assertEqual(augeas_cfg.get("/etc/hosts"), {"/etc/hosts": None}) # 'setvalue' function tests: 4 def test_setvalue(self): - ''' + """ Test if it set a value for a specific augeas path - ''' - self.assertEqual(augeas_cfg.setvalue('prefix=/etc/hosts'), - {'retval': True}) + """ + self.assertEqual(augeas_cfg.setvalue("prefix=/etc/hosts"), {"retval": True}) def test_setvalue_io_error(self): - ''' + """ Test if it set a value for a specific augeas path - ''' - mock = MagicMock(side_effect=IOError('')) - with patch.object(_Augeas, 'save', mock): - self.assertEqual(augeas_cfg.setvalue('prefix=/files/etc/'), - {'retval': False, 'error': ''}) + """ + mock = MagicMock(side_effect=IOError("")) + with patch.object(_Augeas, "save", mock): + self.assertEqual( + augeas_cfg.setvalue("prefix=/files/etc/"), + {"retval": False, "error": ""}, + ) def test_setvalue_uneven_path(self): - ''' + """ Test if it set a value for a specific augeas path - ''' - mock = MagicMock(side_effect=RuntimeError('error')) - with patch.object(_Augeas, 'match', mock): - self.assertRaises(SaltInvocationError, augeas_cfg.setvalue, - ['/files/etc/hosts/1/canonical', 'localhost']) + """ + mock = MagicMock(side_effect=RuntimeError("error")) + with patch.object(_Augeas, "match", mock): + self.assertRaises( + SaltInvocationError, + augeas_cfg.setvalue, + ["/files/etc/hosts/1/canonical", "localhost"], + ) def test_setvalue_one_prefix(self): - ''' + """ Test if it set a value for a specific augeas path - ''' - self.assertRaises(SaltInvocationError, augeas_cfg.setvalue, - 'prefix=/files', '10.18.1.1', 'prefix=/etc', 'test') + """ + self.assertRaises( + SaltInvocationError, + augeas_cfg.setvalue, + "prefix=/files", + "10.18.1.1", + "prefix=/etc", + "test", + ) # 'match' function tests: 2 def test_match(self): - ''' + """ Test if it matches for path expression - ''' - self.assertEqual(augeas_cfg.match('/etc/service', 'ssh'), {}) + """ + self.assertEqual(augeas_cfg.match("/etc/service", "ssh"), {}) def test_match_runtime_error(self): - ''' + """ Test if it matches for path expression - ''' - mock = MagicMock(side_effect=RuntimeError('error')) - with patch.object(_Augeas, 'match', mock): - self.assertEqual(augeas_cfg.match('/etc/service-name', 'ssh'), {}) + """ + mock = MagicMock(side_effect=RuntimeError("error")) + with patch.object(_Augeas, "match", mock): + self.assertEqual(augeas_cfg.match("/etc/service-name", "ssh"), {}) # 'remove' function tests: 2 def test_remove(self): - ''' + """ Test if it removes for path expression - ''' - self.assertEqual(augeas_cfg.remove('/etc/service'), - {'count': 0, 'retval': True}) + """ + self.assertEqual( + augeas_cfg.remove("/etc/service"), {"count": 0, "retval": True} + ) def test_remove_io_runtime_error(self): - ''' + """ Test if it removes for path expression - ''' - mock = MagicMock(side_effect=RuntimeError('error')) - with patch.object(_Augeas, 'save', mock): - self.assertEqual(augeas_cfg.remove('/etc/service-name'), - {'count': 0, 'error': 'error', 'retval': False}) + """ + mock = MagicMock(side_effect=RuntimeError("error")) + with patch.object(_Augeas, "save", mock): + self.assertEqual( + augeas_cfg.remove("/etc/service-name"), + {"count": 0, "error": "error", "retval": False}, + ) # 'ls' function tests: 1 def test_ls(self): - ''' + """ Test if it list the direct children of a node - ''' - self.assertEqual(augeas_cfg.ls('/etc/passwd'), {}) + """ + self.assertEqual(augeas_cfg.ls("/etc/passwd"), {}) # 'tree' function tests: 1 def test_tree(self): - ''' + """ Test if it returns recursively the complete tree of a node - ''' - self.assertEqual(augeas_cfg.tree('/etc/'), {'/etc': None}) + """ + self.assertEqual(augeas_cfg.tree("/etc/"), {"/etc": None}) diff --git a/tests/unit/modules/test_azurearm_dns.py b/tests/unit/modules/test_azurearm_dns.py index c641a399df9..4f204b18901 100644 --- a/tests/unit/modules/test_azurearm_dns.py +++ b/tests/unit/modules/test_azurearm_dns.py @@ -1,8 +1,8 @@ - # -*- coding: utf-8 -*- # import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt Libs @@ -12,14 +12,15 @@ import salt.modules.azurearm_dns as azurearm_dns # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase from tests.support.mock import MagicMock +from tests.support.unit import TestCase, skipIf # Azure libs # pylint: disable=import-error HAS_LIBS = False try: import azure.mgmt.dns.models + HAS_LIBS = True except ImportError: pass @@ -29,17 +30,18 @@ except ImportError: log = logging.getLogger(__name__) MOCK_CREDENTIALS = { - 'client_id': 'CLIENT_ID', - 'secret': 'SECRET', - 'subscription_id': 'SUBSCRIPTION_ID', - 'tenant': 'TENANT' + "client_id": "CLIENT_ID", + "secret": "SECRET", + "subscription_id": "SUBSCRIPTION_ID", + "tenant": "TENANT", } class AzureObjMock(object): - ''' + """ mock azure object for as_dict calls - ''' + """ + args = None kwargs = None @@ -59,9 +61,10 @@ class AzureObjMock(object): class AzureFuncMock(object): - ''' + """ mock azure client function calls - ''' + """ + def __init__(self, return_value=None): self.__return_value = return_value @@ -77,9 +80,10 @@ class AzureFuncMock(object): class AzureSubMock(object): - ''' + """ mock azure client sub-modules - ''' + """ + record_sets = AzureFuncMock() zones = AzureFuncMock() @@ -94,9 +98,10 @@ class AzureSubMock(object): class AzureClientMock(object): - ''' + """ mock azure client - ''' + """ + def __init__(self, return_value=AzureSubMock): self.__return_value = return_value @@ -107,63 +112,63 @@ class AzureClientMock(object): return MagicMock(return_value=self.__return_value)() -@skipIf(HAS_LIBS is False, 'The azure.mgmt.dns module must be installed.') +@skipIf(HAS_LIBS is False, "The azure.mgmt.dns module must be installed.") class AzureRmDnsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.azurearm_dns module - ''' + """ + def setup_loader_modules(self): - ''' + """ setup loader modules and override the azurearm.get_client utility - ''' + """ self.opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils(self.opts) - funcs = salt.loader.minion_mods(self.opts, utils=utils, whitelist=['azurearm_dns', 'config']) - utils['azurearm.get_client'] = AzureClientMock() + funcs = salt.loader.minion_mods( + self.opts, utils=utils, whitelist=["azurearm_dns", "config"] + ) + utils["azurearm.get_client"] = AzureClientMock() return { azurearm_dns: { - '__opts__': self.opts, - '__utils__': utils, - '__salt__': funcs + "__opts__": self.opts, + "__utils__": utils, + "__salt__": funcs, }, } def setUp(self): - ''' + """ setup - ''' + """ TestCase.setUp(self) azurearm_dns.__virtual__() def tearDown(self): - ''' + """ tear down - ''' + """ del self.opts def test_record_set_create_or_update(self): # pylint: disable=invalid-name - ''' + """ tests record set object creation - ''' + """ expected = { - 'if_match': None, - 'if_none_match': None, - 'parameters': { - 'arecords': [{'ipv4_address': '10.0.0.1'}], - 'ttl': 300 - }, - 'record_type': 'A', - 'relative_record_set_name': 'myhost', - 'resource_group_name': 'testgroup', - 'zone_name': 'myzone' + "if_match": None, + "if_none_match": None, + "parameters": {"arecords": [{"ipv4_address": "10.0.0.1"}], "ttl": 300}, + "record_type": "A", + "relative_record_set_name": "myhost", + "resource_group_name": "testgroup", + "zone_name": "myzone", } record_set_args, record_set_kwargs = azurearm_dns.record_set_create_or_update( - 'myhost', - 'myzone', - 'testgroup', - 'A', - arecords=[{'ipv4_address': '10.0.0.1'}], + "myhost", + "myzone", + "testgroup", + "A", + arecords=[{"ipv4_address": "10.0.0.1"}], ttl=300, **MOCK_CREDENTIALS ) @@ -175,24 +180,19 @@ class AzureRmDnsTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(record_set_kwargs, expected) def test_zone_create_or_update(self): - ''' + """ tests zone object creation - ''' + """ expected = { - 'if_match': None, - 'if_none_match': None, - 'parameters': { - 'location': 'global', - 'zone_type': 'Public' - }, - 'resource_group_name': 'testgroup', - 'zone_name': 'myzone' + "if_match": None, + "if_none_match": None, + "parameters": {"location": "global", "zone_type": "Public"}, + "resource_group_name": "testgroup", + "zone_name": "myzone", } zone_args, zone_kwargs = azurearm_dns.zone_create_or_update( - 'myzone', - 'testgroup', - **MOCK_CREDENTIALS + "myzone", "testgroup", **MOCK_CREDENTIALS ) for key, val in zone_kwargs.items(): diff --git a/tests/unit/modules/test_beacons.py b/tests/unit/modules/test_beacons.py index b254562bb79..e975d7289ba 100644 --- a/tests/unit/modules/test_beacons.py +++ b/tests/unit/modules/test_beacons.py @@ -1,194 +1,299 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>` -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.beacons as beacons from salt.utils.event import SaltEvent +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class BeaconsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.beacons - ''' + """ @classmethod def setUpClass(cls): - cls.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + cls.sock_dir = os.path.join(RUNTIME_VARS.TMP, "test-socks") def setup_loader_modules(self): return {beacons: {}} def test_delete(self): - ''' + """ Test deleting a beacon. - ''' - comm1 = 'Deleted beacon: ps.' + """ + comm1 = "Deleted beacon: ps." event_returns = [ - {'complete': True, - 'tag': '/salt/minion/minion_beacons_delete_complete', - 'beacons': {}}, - ] + { + "complete": True, + "tag": "/salt/minion/minion_beacons_delete_complete", + "beacons": {}, + }, + ] - with patch.dict(beacons.__opts__, {'beacons': {'ps': [{'processes': {'salt-master': 'stopped', 'apache2': 'stopped'}}]}, 'sock_dir': self.sock_dir}): + with patch.dict( + beacons.__opts__, + { + "beacons": { + "ps": [ + {"processes": {"salt-master": "stopped", "apache2": "stopped"}} + ] + }, + "sock_dir": self.sock_dir, + }, + ): mock = MagicMock(return_value=True) - with patch.dict(beacons.__salt__, {'event.fire': mock}): - with patch.object(SaltEvent, 'get_event', side_effect=event_returns): - self.assertDictEqual(beacons.delete('ps'), - {'comment': comm1, 'result': True}) + with patch.dict(beacons.__salt__, {"event.fire": mock}): + with patch.object(SaltEvent, "get_event", side_effect=event_returns): + self.assertDictEqual( + beacons.delete("ps"), {"comment": comm1, "result": True} + ) def test_add(self): - ''' + """ Test adding a beacon - ''' - comm1 = 'Added beacon: ps.' - event_returns = [{'complete': True, - 'tag': '/salt/minion/minion_beacons_list_complete', - 'beacons': {}}, - {'complete': True, - 'tag': '/salt/minion/minion_beacons_list_available_complete', - 'beacons': ['ps']}, - {'complete': True, - 'valid': True, - 'vcomment': '', - 'tag': '/salt/minion/minion_beacons_list_complete'}, - {'complete': True, - 'tag': '/salt/minion/minion_beacon_add_complete', - 'beacons': {'ps': [{'processes': {'salt-master': 'stopped', 'apache2': 'stopped'}}]}}] + """ + comm1 = "Added beacon: ps." + event_returns = [ + { + "complete": True, + "tag": "/salt/minion/minion_beacons_list_complete", + "beacons": {}, + }, + { + "complete": True, + "tag": "/salt/minion/minion_beacons_list_available_complete", + "beacons": ["ps"], + }, + { + "complete": True, + "valid": True, + "vcomment": "", + "tag": "/salt/minion/minion_beacons_list_complete", + }, + { + "complete": True, + "tag": "/salt/minion/minion_beacon_add_complete", + "beacons": { + "ps": [ + {"processes": {"salt-master": "stopped", "apache2": "stopped"}} + ] + }, + }, + ] - with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': self.sock_dir}): + with patch.dict(beacons.__opts__, {"beacons": {}, "sock_dir": self.sock_dir}): mock = MagicMock(return_value=True) - with patch.dict(beacons.__salt__, {'event.fire': mock}): - with patch.object(SaltEvent, 'get_event', side_effect=event_returns): - self.assertDictEqual(beacons.add('ps', [{'processes': {'salt-master': 'stopped', 'apache2': 'stopped'}}]), - {'comment': comm1, 'result': True}) + with patch.dict(beacons.__salt__, {"event.fire": mock}): + with patch.object(SaltEvent, "get_event", side_effect=event_returns): + self.assertDictEqual( + beacons.add( + "ps", + [ + { + "processes": { + "salt-master": "stopped", + "apache2": "stopped", + } + } + ], + ), + {"comment": comm1, "result": True}, + ) def test_save(self): - ''' + """ Test saving beacons. - ''' - comm1 = 'Beacons saved to {0}beacons.conf.'.format(RUNTIME_VARS.TMP + os.sep) - with patch.dict(beacons.__opts__, {'conf_file': os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'foo'), - 'beacons': {}, - 'default_include': RUNTIME_VARS.TMP + os.sep, - 'sock_dir': self.sock_dir}): + """ + comm1 = "Beacons saved to {0}beacons.conf.".format(RUNTIME_VARS.TMP + os.sep) + with patch.dict( + beacons.__opts__, + { + "conf_file": os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "foo"), + "beacons": {}, + "default_include": RUNTIME_VARS.TMP + os.sep, + "sock_dir": self.sock_dir, + }, + ): mock = MagicMock(return_value=True) - with patch.dict(beacons.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'beacons': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(beacons.save(), - {'comment': comm1, 'result': True}) + with patch.dict(beacons.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "beacons": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + beacons.save(), {"comment": comm1, "result": True} + ) def test_disable(self): - ''' + """ Test disabling beacons - ''' - comm1 = 'Disabled beacons on minion.' - event_returns = [{'complete': True, - 'tag': '/salt/minion/minion_beacons_disabled_complete', - 'beacons': {'enabled': False, - 'ps': [{'processes': {'salt-master': 'stopped', - 'apache2': 'stopped'}}]}}] + """ + comm1 = "Disabled beacons on minion." + event_returns = [ + { + "complete": True, + "tag": "/salt/minion/minion_beacons_disabled_complete", + "beacons": { + "enabled": False, + "ps": [ + {"processes": {"salt-master": "stopped", "apache2": "stopped"}} + ], + }, + } + ] - with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': self.sock_dir}): + with patch.dict(beacons.__opts__, {"beacons": {}, "sock_dir": self.sock_dir}): mock = MagicMock(return_value=True) - with patch.dict(beacons.__salt__, {'event.fire': mock}): - with patch.object(SaltEvent, 'get_event', side_effect=event_returns): - self.assertDictEqual(beacons.disable(), - {'comment': comm1, 'result': True}) + with patch.dict(beacons.__salt__, {"event.fire": mock}): + with patch.object(SaltEvent, "get_event", side_effect=event_returns): + self.assertDictEqual( + beacons.disable(), {"comment": comm1, "result": True} + ) def test_enable(self): - ''' + """ Test enabling beacons - ''' - comm1 = 'Enabled beacons on minion.' - event_returns = [{'complete': True, - 'tag': '/salt/minion/minion_beacon_enabled_complete', - 'beacons': {'enabled': True, - 'ps': [{'processes': {'salt-master': 'stopped', - 'apache2': 'stopped'}}]}}] + """ + comm1 = "Enabled beacons on minion." + event_returns = [ + { + "complete": True, + "tag": "/salt/minion/minion_beacon_enabled_complete", + "beacons": { + "enabled": True, + "ps": [ + {"processes": {"salt-master": "stopped", "apache2": "stopped"}} + ], + }, + } + ] - with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': self.sock_dir}): + with patch.dict(beacons.__opts__, {"beacons": {}, "sock_dir": self.sock_dir}): mock = MagicMock(return_value=True) - with patch.dict(beacons.__salt__, {'event.fire': mock}): - with patch.object(SaltEvent, 'get_event', side_effect=event_returns): - self.assertDictEqual(beacons.enable(), - {'comment': comm1, 'result': True}) + with patch.dict(beacons.__salt__, {"event.fire": mock}): + with patch.object(SaltEvent, "get_event", side_effect=event_returns): + self.assertDictEqual( + beacons.enable(), {"comment": comm1, "result": True} + ) def test_add_beacon_module(self): - ''' + """ Test adding a beacon - ''' - comm1 = 'Added beacon: watch_salt_master.' - event_returns = [{'complete': True, - 'tag': '/salt/minion/minion_beacons_list_complete', - 'beacons': {}}, - {'complete': True, - 'tag': '/salt/minion/minion_beacons_list_available_complete', - 'beacons': ['ps']}, - {'complete': True, - 'valid': True, - 'vcomment': '', - 'tag': '/salt/minion/minion_beacons_list_complete'}, - {'complete': True, - 'tag': '/salt/minion/minion_beacon_add_complete', - 'beacons': {'watch_salt_master': [{'processes': {'salt-master': 'stopped'}}, - {'beacon_module': 'ps'}]}}] + """ + comm1 = "Added beacon: watch_salt_master." + event_returns = [ + { + "complete": True, + "tag": "/salt/minion/minion_beacons_list_complete", + "beacons": {}, + }, + { + "complete": True, + "tag": "/salt/minion/minion_beacons_list_available_complete", + "beacons": ["ps"], + }, + { + "complete": True, + "valid": True, + "vcomment": "", + "tag": "/salt/minion/minion_beacons_list_complete", + }, + { + "complete": True, + "tag": "/salt/minion/minion_beacon_add_complete", + "beacons": { + "watch_salt_master": [ + {"processes": {"salt-master": "stopped"}}, + {"beacon_module": "ps"}, + ] + }, + }, + ] - with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': self.sock_dir}): + with patch.dict(beacons.__opts__, {"beacons": {}, "sock_dir": self.sock_dir}): mock = MagicMock(return_value=True) - with patch.dict(beacons.__salt__, {'event.fire': mock}): - with patch.object(SaltEvent, 'get_event', side_effect=event_returns): - self.assertDictEqual(beacons.add('watch_salt_master', [{'processes': {'salt-master': 'stopped'}}, {'beacon_module': 'ps'}]), - {'comment': comm1, 'result': True}) + with patch.dict(beacons.__salt__, {"event.fire": mock}): + with patch.object(SaltEvent, "get_event", side_effect=event_returns): + self.assertDictEqual( + beacons.add( + "watch_salt_master", + [ + {"processes": {"salt-master": "stopped"}}, + {"beacon_module": "ps"}, + ], + ), + {"comment": comm1, "result": True}, + ) def test_enable_beacon_module(self): - ''' + """ Test enabling beacons - ''' - comm1 = 'Enabled beacons on minion.' - event_returns = [{'complete': True, - 'tag': '/salt/minion/minion_beacon_enabled_complete', - 'beacons': {'enabled': True, - 'watch_salt_master': [{'processes': {'salt-master': 'stopped'}}, - {'beacon_module': 'ps'}]}}] + """ + comm1 = "Enabled beacons on minion." + event_returns = [ + { + "complete": True, + "tag": "/salt/minion/minion_beacon_enabled_complete", + "beacons": { + "enabled": True, + "watch_salt_master": [ + {"processes": {"salt-master": "stopped"}}, + {"beacon_module": "ps"}, + ], + }, + } + ] - with patch.dict(beacons.__opts__, {'beacons': {}, 'sock_dir': self.sock_dir}): + with patch.dict(beacons.__opts__, {"beacons": {}, "sock_dir": self.sock_dir}): mock = MagicMock(return_value=True) - with patch.dict(beacons.__salt__, {'event.fire': mock}): - with patch.object(SaltEvent, 'get_event', side_effect=event_returns): - self.assertDictEqual(beacons.enable(), - {'comment': comm1, 'result': True}) + with patch.dict(beacons.__salt__, {"event.fire": mock}): + with patch.object(SaltEvent, "get_event", side_effect=event_returns): + self.assertDictEqual( + beacons.enable(), {"comment": comm1, "result": True} + ) def test_delete_beacon_module(self): - ''' + """ Test deleting a beacon. - ''' - comm1 = 'Deleted beacon: watch_salt_master.' + """ + comm1 = "Deleted beacon: watch_salt_master." event_returns = [ - {'complete': True, - 'tag': '/salt/minion/minion_beacons_delete_complete', - 'beacons': {}}, - ] + { + "complete": True, + "tag": "/salt/minion/minion_beacons_delete_complete", + "beacons": {}, + }, + ] - with patch.dict(beacons.__opts__, {'beacons': {'watch_salt_master': [{'processes': {'salt-master': 'stopped'}}, {'beacon_module': 'ps'}]}, 'sock_dir': self.sock_dir}): + with patch.dict( + beacons.__opts__, + { + "beacons": { + "watch_salt_master": [ + {"processes": {"salt-master": "stopped"}}, + {"beacon_module": "ps"}, + ] + }, + "sock_dir": self.sock_dir, + }, + ): mock = MagicMock(return_value=True) - with patch.dict(beacons.__salt__, {'event.fire': mock}): - with patch.object(SaltEvent, 'get_event', side_effect=event_returns): - self.assertDictEqual(beacons.delete('watch_salt_master'), - {'comment': comm1, 'result': True}) + with patch.dict(beacons.__salt__, {"event.fire": mock}): + with patch.object(SaltEvent, "get_event", side_effect=event_returns): + self.assertDictEqual( + beacons.delete("watch_salt_master"), + {"comment": comm1, "result": True}, + ) diff --git a/tests/unit/modules/test_bluez_bluetooth.py b/tests/unit/modules/test_bluez_bluetooth.py index 78b5cfcf9fc..0dd846a721d 100644 --- a/tests/unit/modules/test_bluez_bluetooth.py +++ b/tests/unit/modules/test_bluez_bluetooth.py @@ -1,206 +1,208 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.bluez_bluetooth as bluez -from salt.exceptions import CommandExecutionError import salt.utils.validate.net +from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class MockBluetooth(object): - ''' + """ Mock class for bluetooth - ''' + """ + def __init__(self): pass @staticmethod def discover_devices(lookup_names): - ''' + """ Mock method to return all Discoverable devices - ''' - return [['a', 'b', 'c'], ['d', 'e', 'f']] + """ + return [["a", "b", "c"], ["d", "e", "f"]] class BluezTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.bluez - ''' + """ def setup_loader_modules(self): - return {bluez: {'bluetooth': MockBluetooth()}} + return {bluez: {"bluetooth": MockBluetooth()}} def test_version(self): - ''' + """ Test if return bluetooth version - ''' + """ mock = MagicMock(return_value="5.7") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): - self.assertDictEqual(bluez.version(), - {'PyBluez': '<= 0.18 (Unknown, but installed)', - 'Bluez': '5.7'}) + with patch.dict(bluez.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + bluez.version(), + {"PyBluez": "<= 0.18 (Unknown, but installed)", "Bluez": "5.7"}, + ) def test_address_(self): - ''' + """ Test of getting address of bluetooth adapter - ''' - mock = MagicMock(return_value='hci : hci0') - with patch.dict(bluez.__salt__, {'cmd.run': mock}): - self.assertDictEqual(bluez.address_(), - {'hci ': {'device': 'hci ', - 'path': '/sys/class/bluetooth/hci '}}) + """ + mock = MagicMock(return_value="hci : hci0") + with patch.dict(bluez.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + bluez.address_(), + {"hci ": {"device": "hci ", "path": "/sys/class/bluetooth/hci "}}, + ) def test_power(self): - ''' + """ Test of getting address of bluetooth adapter - ''' + """ mock = MagicMock(return_value={}) - with patch.object(bluez, 'address_', mock): + with patch.object(bluez, "address_", mock): self.assertRaises(CommandExecutionError, bluez.power, "hci0", "on") - mock = MagicMock(return_value={'hci0': - {'device': 'hci0', 'power': 'on'}}) - with patch.object(bluez, 'address_', mock): + mock = MagicMock(return_value={"hci0": {"device": "hci0", "power": "on"}}) + with patch.object(bluez, "address_", mock): mock = MagicMock(return_value="") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): + with patch.dict(bluez.__salt__, {"cmd.run": mock}): self.assertTrue(bluez.power("hci0", "on")) - mock = MagicMock(return_value={'hci0': - {'device': 'hci0', 'power': 'on'}}) - with patch.object(bluez, 'address_', mock): + mock = MagicMock(return_value={"hci0": {"device": "hci0", "power": "on"}}) + with patch.object(bluez, "address_", mock): mock = MagicMock(return_value="") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): + with patch.dict(bluez.__salt__, {"cmd.run": mock}): self.assertFalse(bluez.power("hci0", "off")) def test_discoverable(self): - ''' + """ Test of enabling bluetooth device - ''' - mock = MagicMock(side_effect=[{}, {'hci0': - {'device': 'hci0', 'power': 'on'}}, - {'hci0': {'device': 'hci0', - 'power': 'on'}}]) - with patch.object(bluez, 'address_', mock): - self.assertRaises(CommandExecutionError, - bluez.discoverable, "hci0") + """ + mock = MagicMock( + side_effect=[ + {}, + {"hci0": {"device": "hci0", "power": "on"}}, + {"hci0": {"device": "hci0", "power": "on"}}, + ] + ) + with patch.object(bluez, "address_", mock): + self.assertRaises(CommandExecutionError, bluez.discoverable, "hci0") mock = MagicMock(return_value="UP RUNNING ISCAN") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): + with patch.dict(bluez.__salt__, {"cmd.run": mock}): self.assertTrue(bluez.discoverable("hci0")) mock = MagicMock(return_value="") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): + with patch.dict(bluez.__salt__, {"cmd.run": mock}): self.assertFalse(bluez.discoverable("hci0")) def test_noscan(self): - ''' + """ Test of turning off of scanning modes - ''' - mock = MagicMock(side_effect=[{}, {'hci0': - {'device': 'hci0', 'power': 'on'}}, - {'hci0': {'device': 'hci0', - 'power': 'on'}}]) - with patch.object(bluez, 'address_', mock): + """ + mock = MagicMock( + side_effect=[ + {}, + {"hci0": {"device": "hci0", "power": "on"}}, + {"hci0": {"device": "hci0", "power": "on"}}, + ] + ) + with patch.object(bluez, "address_", mock): self.assertRaises(CommandExecutionError, bluez.noscan, "hci0") mock = MagicMock(return_value="SCAN") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): + with patch.dict(bluez.__salt__, {"cmd.run": mock}): self.assertFalse(bluez.noscan("hci0")) mock = MagicMock(return_value="") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): + with patch.dict(bluez.__salt__, {"cmd.run": mock}): self.assertTrue(bluez.noscan("hci0")) def test_scan(self): - ''' + """ Test of scanning of bluetooth devices - ''' - self.assertListEqual(bluez.scan(), [{'a': 'b'}, {'d': 'e'}]) + """ + self.assertListEqual(bluez.scan(), [{"a": "b"}, {"d": "e"}]) def test_block(self): - ''' + """ Test of blocking specific bluetooth device - ''' + """ mock = MagicMock(side_effect=[False, True]) - with patch.object(salt.utils.validate.net, 'mac', mock): - self.assertRaises(CommandExecutionError, - bluez.block, "DE:AD:BE:EF:CA:ZE") + with patch.object(salt.utils.validate.net, "mac", mock): + self.assertRaises(CommandExecutionError, bluez.block, "DE:AD:BE:EF:CA:ZE") mock = MagicMock(return_value="") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): + with patch.dict(bluez.__salt__, {"cmd.run": mock}): self.assertIsNone(bluez.block("DE:AD:BE:EF:CA:FE")) def test_unblock(self): - ''' + """ Test to unblock specific bluetooth device - ''' + """ mock = MagicMock(side_effect=[False, True]) - with patch.object(salt.utils.validate.net, 'mac', mock): - self.assertRaises(CommandExecutionError, - bluez.block, "DE:AD:BE:EF:CA:ZE") + with patch.object(salt.utils.validate.net, "mac", mock): + self.assertRaises(CommandExecutionError, bluez.block, "DE:AD:BE:EF:CA:ZE") mock = MagicMock(return_value="") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): + with patch.dict(bluez.__salt__, {"cmd.run": mock}): self.assertIsNone(bluez.unblock("DE:AD:BE:EF:CA:FE")) def test_pair(self): - ''' + """ Test to pair bluetooth adapter with a device - ''' + """ mock = MagicMock(side_effect=[False, True, True]) - with patch.object(salt.utils.validate.net, 'mac', mock): - self.assertRaises(CommandExecutionError, - bluez.pair, "DE:AD:BE:EF:CA:FE", "1234") + with patch.object(salt.utils.validate.net, "mac", mock): + self.assertRaises( + CommandExecutionError, bluez.pair, "DE:AD:BE:EF:CA:FE", "1234" + ) - self.assertRaises(CommandExecutionError, - bluez.pair, "DE:AD:BE:EF:CA:FE", "abcd") + self.assertRaises( + CommandExecutionError, bluez.pair, "DE:AD:BE:EF:CA:FE", "abcd" + ) - mock = MagicMock(return_value={'device': 'hci0'}) - with patch.object(bluez, 'address_', mock): + mock = MagicMock(return_value={"device": "hci0"}) + with patch.object(bluez, "address_", mock): mock = MagicMock(return_value="Ok") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): - self.assertListEqual(bluez. - pair("DE:AD:BE:EF:CA:FE", "1234"), - ["Ok"]) + with patch.dict(bluez.__salt__, {"cmd.run": mock}): + self.assertListEqual( + bluez.pair("DE:AD:BE:EF:CA:FE", "1234"), ["Ok"] + ) def test_unpair(self): - ''' + """ Test to unpair bluetooth adaptor with a device - ''' + """ mock = MagicMock(side_effect=[False, True]) - with patch.object(salt.utils.validate.net, 'mac', mock): - self.assertRaises(CommandExecutionError, - bluez.unpair, "DE:AD:BE:EF:CA:FE") + with patch.object(salt.utils.validate.net, "mac", mock): + self.assertRaises(CommandExecutionError, bluez.unpair, "DE:AD:BE:EF:CA:FE") mock = MagicMock(return_value="Ok") - with patch.dict(bluez.__salt__, {'cmd.run': mock}): + with patch.dict(bluez.__salt__, {"cmd.run": mock}): self.assertListEqual(bluez.unpair("DE:AD:BE:EF:CA:FE"), ["Ok"]) def test_start(self): - ''' + """ Test to start bluetooth service - ''' + """ mock = MagicMock(return_value="Ok") - with patch.dict(bluez.__salt__, {'service.start': mock}): + with patch.dict(bluez.__salt__, {"service.start": mock}): self.assertEqual(bluez.start(), "Ok") def test_stop(self): - ''' + """ Test to stop bluetooth service - ''' + """ mock = MagicMock(return_value="Ok") - with patch.dict(bluez.__salt__, {'service.stop': mock}): + with patch.dict(bluez.__salt__, {"service.stop": mock}): self.assertEqual(bluez.stop(), "Ok") diff --git a/tests/unit/modules/test_boto3_elasticsearch.py b/tests/unit/modules/test_boto3_elasticsearch.py index 30fab891462..3151bfaac29 100644 --- a/tests/unit/modules/test_boto3_elasticsearch.py +++ b/tests/unit/modules/test_boto3_elasticsearch.py @@ -1,30 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.modules.boto3_elasticsearch -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime import random import string -import datetime import textwrap -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import (MagicMock, patch) - # Import Salt libs import salt.loader -from salt.utils.versions import LooseVersion import salt.modules.boto3_elasticsearch as boto3_elasticsearch from salt.ext.six.moves import range +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # Import 3rd-party libs try: import boto3 from botocore.exceptions import ClientError + HAS_BOTO3 = True except ImportError: HAS_BOTO3 = False @@ -32,123 +34,120 @@ except ImportError: # the boto3_elasticsearch module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -REQUIRED_BOTO3_VERSION = '1.2.1' +REQUIRED_BOTO3_VERSION = "1.2.1" def __virtual__(): - ''' + """ Returns True/False boolean depending on if Boto3 is installed and correct version. - ''' + """ if not HAS_BOTO3: return False if LooseVersion(boto3.__version__) < LooseVersion(REQUIRED_BOTO3_VERSION): - return False, ('The boto3 module must be greater or equal to version {}' - ''.format(REQUIRED_BOTO3_VERSION)) + return ( + False, + ( + "The boto3 module must be greater or equal to version {}" + "".format(REQUIRED_BOTO3_VERSION) + ), + ) return True -REGION = 'us-east-1' -ACCESS_KEY = 'GKTADJGHEIQSXMKKRBJ08H' -SECRET_KEY = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -CONN_PARAMETERS = {'region': REGION, 'key': ACCESS_KEY, 'keyid': SECRET_KEY, 'profile': {}} -ERROR_MESSAGE = 'An error occurred ({}) when calling the {} operation: Test-defined error' -ERROR_CONTENT = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } +REGION = "us-east-1" +ACCESS_KEY = "GKTADJGHEIQSXMKKRBJ08H" +SECRET_KEY = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +CONN_PARAMETERS = { + "region": REGION, + "key": ACCESS_KEY, + "keyid": SECRET_KEY, + "profile": {}, } -NOT_FOUND_ERROR = ClientError({ - 'Error': { - 'Code': 'ResourceNotFoundException', - 'Message': "Test-defined error" - } -}, 'msg') +ERROR_MESSAGE = ( + "An error occurred ({}) when calling the {} operation: Test-defined error" +) +ERROR_CONTENT = {"Error": {"Code": 101, "Message": "Test-defined error"}} +NOT_FOUND_ERROR = ClientError( + {"Error": {"Code": "ResourceNotFoundException", "Message": "Test-defined error"}}, + "msg", +) DOMAIN_RET = { - 'DomainId': 'accountno/testdomain', - 'DomainName': 'testdomain', - 'ARN': 'arn:aws:es:region:accountno:domain/testdomain', - 'Created': True, - 'Deleted': False, - 'Endpoints': { - 'vpc': 'vpc-testdomain-1234567890.region.es.amazonaws.com' + "DomainId": "accountno/testdomain", + "DomainName": "testdomain", + "ARN": "arn:aws:es:region:accountno:domain/testdomain", + "Created": True, + "Deleted": False, + "Endpoints": {"vpc": "vpc-testdomain-1234567890.region.es.amazonaws.com"}, + "Processing": False, + "UpgradeProcessing": False, + "ElasticsearchVersion": "6.3", + "ElasticsearchClusterConfig": { + "InstanceType": "t2.medium.elasticsearch", + "InstanceCount": 1, + "DedicatedMasterEnabled": False, + "ZoneAwarenessEnabled": False, }, - 'Processing': False, - 'UpgradeProcessing': False, - 'ElasticsearchVersion': '6.3', - 'ElasticsearchClusterConfig': { - 'InstanceType': 't2.medium.elasticsearch', - 'InstanceCount': 1, - 'DedicatedMasterEnabled': False, - 'ZoneAwarenessEnabled': False, + "EBSOptions": { + "EBSEnabled": True, + "VolumeType": "gp2", + "VolumeSize": 123, + "Iops": 12, }, - 'EBSOptions': { - 'EBSEnabled': True, - 'VolumeType': 'gp2', - 'VolumeSize': 123, - 'Iops': 12 - }, - 'AccessPolicies': textwrap.dedent(''' + "AccessPolicies": textwrap.dedent( + """ {"Version":"2012-10-17","Statement":[{"Effect":"Allow", "Principal":{"AWS":"*"},"Action":"es:*", - "Resource":"arn:aws:es:region:accountno:domain/testdomain/*"}]}'''), - 'SnapshotOptions': { - 'AutomatedSnapshotStartHour': 1 + "Resource":"arn:aws:es:region:accountno:domain/testdomain/*"}]}""" + ), + "SnapshotOptions": {"AutomatedSnapshotStartHour": 1}, + "VPCOptions": { + "VPCId": "vpc-12345678", + "SubnetIds": ["subnet-deadbeef"], + "AvailabilityZones": ["regiona"], + "SecurityGroupIds": ["sg-87654321"], }, - 'VPCOptions': { - 'VPCId': 'vpc-12345678', - 'SubnetIds': [ - 'subnet-deadbeef', - ], - 'AvailabilityZones': [ - 'regiona', - ], - 'SecurityGroupIds': [ - 'sg-87654321', - ] + "CognitoOptions": {"Enabled": False}, + "EncryptionAtRestOptions": {"Enabled": False}, + "NodeToNodeEncryptionOptions": {"Enabled": False}, + "AdvancedOptions": {"rest.action.multi.allow_explicit_index": "true"}, + "ServiceSoftwareOptions": { + "CurrentVersion": "R20190221-P1", + "NewVersion": "R20190418", + "UpdateAvailable": True, + "Cancellable": False, + "UpdateStatus": "ELIGIBLE", + "Description": ( + "A newer release R20190418 is available. This release " + "will be automatically deployed after somedate" + ), + "AutomatedUpdateDate": None, }, - 'CognitoOptions': { - 'Enabled': False, - }, - 'EncryptionAtRestOptions': { - 'Enabled': False, - }, - 'NodeToNodeEncryptionOptions': { - 'Enabled': False - }, - 'AdvancedOptions': { - 'rest.action.multi.allow_explicit_index': 'true' - }, - 'ServiceSoftwareOptions': { - 'CurrentVersion': 'R20190221-P1', - 'NewVersion': 'R20190418', - 'UpdateAvailable': True, - 'Cancellable': False, - 'UpdateStatus': 'ELIGIBLE', - 'Description': ('A newer release R20190418 is available. This release ' - 'will be automatically deployed after somedate'), - 'AutomatedUpdateDate': None - } } -@skipIf(HAS_BOTO3 is False, 'The boto module must be installed.') -@skipIf(LooseVersion(boto3.__version__) < LooseVersion(REQUIRED_BOTO3_VERSION), - 'The boto3 module must be greater or equal to version {}'.format(REQUIRED_BOTO3_VERSION)) +@skipIf(HAS_BOTO3 is False, "The boto module must be installed.") +@skipIf( + LooseVersion(boto3.__version__) < LooseVersion(REQUIRED_BOTO3_VERSION), + "The boto3 module must be greater or equal to version {}".format( + REQUIRED_BOTO3_VERSION + ), +) class Boto3ElasticsearchTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.boto3_elasticsearch module - ''' + """ + conn = None def setup_loader_modules(self): self.opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( self.opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context={}) - return {boto3_elasticsearch: {'__utils__': utils}} + whitelist=["boto3", "args", "systemd", "path", "platform"], + context={}, + ) + return {boto3_elasticsearch: {"__utils__": utils}} def setUp(self): super(Boto3ElasticsearchTestCase, self).setUp() @@ -159,994 +158,1075 @@ class Boto3ElasticsearchTestCase(TestCase, LoaderModuleMockMixin): # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - CONN_PARAMETERS['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) - for _ in range(50)) + CONN_PARAMETERS["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') - self.patcher = patch('boto3.session.Session') + self.addCleanup(delattr, self, "conn") + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value session_instance.configure_mock(client=MagicMock(return_value=self.conn)) self.paginator = MagicMock() - self.addCleanup(delattr, self, 'paginator') + self.addCleanup(delattr, self, "paginator") self.conn.configure_mock(get_paginator=MagicMock(return_value=self.paginator)) def test_describe_elasticsearch_domain_positive(self): - ''' + """ Test that when describing a domain when the domain actually exists, the .exists method returns a dict with 'result': True and 'response' with the domain status information. - ''' + """ # The patch below is not neccesary per se, # as .exists returns positive as long as no exception is raised. - with patch.object(self.conn, - 'describe_elasticsearch_domain', - return_value={'DomainStatus': DOMAIN_RET}): + with patch.object( + self.conn, + "describe_elasticsearch_domain", + return_value={"DomainStatus": DOMAIN_RET}, + ): self.assertEqual( boto3_elasticsearch.describe_elasticsearch_domain( - domain_name='testdomain', - **CONN_PARAMETERS), - {'result': True, 'response': DOMAIN_RET} + domain_name="testdomain", **CONN_PARAMETERS + ), + {"result": True, "response": DOMAIN_RET}, ) def test_describe_elasticsearch_domain_error(self): - ''' + """ Test that when describing a domain when the domain does not exist, the .exists method returns a dict with 'result': False and 'error' with boto's ResourceNotFoundException. - ''' - with patch.object(self.conn, - 'describe_elasticsearch_domain', - side_effect=NOT_FOUND_ERROR): + """ + with patch.object( + self.conn, "describe_elasticsearch_domain", side_effect=NOT_FOUND_ERROR + ): result = boto3_elasticsearch.describe_elasticsearch_domain( - domain_name='testdomain', - **CONN_PARAMETERS) - self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format('ResourceNotFoundException', 'msg') + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertEqual( + result.get("error", ""), + ERROR_MESSAGE.format("ResourceNotFoundException", "msg"), + ) + self.assertFalse(result["result"]) def test_create_elasticsearch_domain_positive(self): - ''' + """ Test that when creating a domain, and it succeeds, the .create method returns a dict with 'result': True and 'response' with the newly created domain's status information. - ''' - with patch.object(self.conn, - 'create_elasticsearch_domain', - return_value={'DomainStatus': DOMAIN_RET}): + """ + with patch.object( + self.conn, + "create_elasticsearch_domain", + return_value={"DomainStatus": DOMAIN_RET}, + ): kwargs = { - 'elasticsearch_version': DOMAIN_RET['ElasticsearchVersion'], - 'elasticsearch_cluster_config': DOMAIN_RET['ElasticsearchClusterConfig'], - 'ebs_options': DOMAIN_RET['EBSOptions'], - 'access_policies': DOMAIN_RET['AccessPolicies'], - 'snapshot_options': DOMAIN_RET['SnapshotOptions'], - 'vpc_options': DOMAIN_RET['VPCOptions'], - 'cognito_options': DOMAIN_RET['CognitoOptions'], - 'encryption_at_rest_options': DOMAIN_RET['EncryptionAtRestOptions'], - 'advanced_options': DOMAIN_RET['AdvancedOptions'], + "elasticsearch_version": DOMAIN_RET["ElasticsearchVersion"], + "elasticsearch_cluster_config": DOMAIN_RET[ + "ElasticsearchClusterConfig" + ], + "ebs_options": DOMAIN_RET["EBSOptions"], + "access_policies": DOMAIN_RET["AccessPolicies"], + "snapshot_options": DOMAIN_RET["SnapshotOptions"], + "vpc_options": DOMAIN_RET["VPCOptions"], + "cognito_options": DOMAIN_RET["CognitoOptions"], + "encryption_at_rest_options": DOMAIN_RET["EncryptionAtRestOptions"], + "advanced_options": DOMAIN_RET["AdvancedOptions"], } kwargs.update(CONN_PARAMETERS) self.assertEqual( - boto3_elasticsearch.create_elasticsearch_domain(domain_name='testdomain', **kwargs), - {'result': True, 'response': DOMAIN_RET} + boto3_elasticsearch.create_elasticsearch_domain( + domain_name="testdomain", **kwargs + ), + {"result": True, "response": DOMAIN_RET}, ) def test_create_elasticsearch_domain_error(self): - ''' + """ Test that when creating a domain, and boto3 returns an error, the .create method returns a dict with 'result': False and 'error' with the error reported by boto3. - ''' - with patch.object(self.conn, - 'create_elasticsearch_domain', - side_effect=ClientError(ERROR_CONTENT, 'create_domain')): + """ + with patch.object( + self.conn, + "create_elasticsearch_domain", + side_effect=ClientError(ERROR_CONTENT, "create_domain"), + ): kwargs = { - 'elasticsearch_version': DOMAIN_RET['ElasticsearchVersion'], - 'elasticsearch_cluster_config': DOMAIN_RET['ElasticsearchClusterConfig'], - 'ebs_options': DOMAIN_RET['EBSOptions'], - 'access_policies': DOMAIN_RET['AccessPolicies'], - 'snapshot_options': DOMAIN_RET['SnapshotOptions'], - 'vpc_options': DOMAIN_RET['VPCOptions'], - 'cognito_options': DOMAIN_RET['CognitoOptions'], - 'encryption_at_rest_options': DOMAIN_RET['EncryptionAtRestOptions'], - 'advanced_options': DOMAIN_RET['AdvancedOptions'], + "elasticsearch_version": DOMAIN_RET["ElasticsearchVersion"], + "elasticsearch_cluster_config": DOMAIN_RET[ + "ElasticsearchClusterConfig" + ], + "ebs_options": DOMAIN_RET["EBSOptions"], + "access_policies": DOMAIN_RET["AccessPolicies"], + "snapshot_options": DOMAIN_RET["SnapshotOptions"], + "vpc_options": DOMAIN_RET["VPCOptions"], + "cognito_options": DOMAIN_RET["CognitoOptions"], + "encryption_at_rest_options": DOMAIN_RET["EncryptionAtRestOptions"], + "advanced_options": DOMAIN_RET["AdvancedOptions"], } kwargs.update(CONN_PARAMETERS) - result = boto3_elasticsearch.create_elasticsearch_domain('testdomain', **kwargs) + result = boto3_elasticsearch.create_elasticsearch_domain( + "testdomain", **kwargs + ) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'create_domain') + result.get("error", ""), ERROR_MESSAGE.format(101, "create_domain") ) def test_delete_domain_positive(self): - ''' + """ Test that when deleting a domain, and it succeeds, the .delete method returns {'result': True}. - ''' - with patch.object(self.conn, 'delete_elasticsearch_domain'): + """ + with patch.object(self.conn, "delete_elasticsearch_domain"): self.assertEqual( - boto3_elasticsearch.delete_elasticsearch_domain('testdomain', **CONN_PARAMETERS), - {'result': True} + boto3_elasticsearch.delete_elasticsearch_domain( + "testdomain", **CONN_PARAMETERS + ), + {"result": True}, ) def test_delete_domain_error(self): - ''' + """ Test that when deleting a domain, and boto3 returns an error, the .delete method returns {'result': False, 'error' :'the error'}. - ''' - with patch.object(self.conn, - 'delete_elasticsearch_domain', - side_effect=ClientError(ERROR_CONTENT, 'delete_domain')): - result = boto3_elasticsearch.delete_elasticsearch_domain('testdomain', **CONN_PARAMETERS) - self.assertFalse(result['result']) + """ + with patch.object( + self.conn, + "delete_elasticsearch_domain", + side_effect=ClientError(ERROR_CONTENT, "delete_domain"), + ): + result = boto3_elasticsearch.delete_elasticsearch_domain( + "testdomain", **CONN_PARAMETERS + ) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'delete_domain') + result.get("error", ""), ERROR_MESSAGE.format(101, "delete_domain") ) def test_update_domain_positive(self): - ''' + """ Test that when updating a domain succeeds, the .update method returns {'result': True}. - ''' - with patch.object(self.conn, - 'update_elasticsearch_domain_config', - return_value={'DomainConfig': DOMAIN_RET}): + """ + with patch.object( + self.conn, + "update_elasticsearch_domain_config", + return_value={"DomainConfig": DOMAIN_RET}, + ): kwargs = { - 'elasticsearch_cluster_config': DOMAIN_RET['ElasticsearchClusterConfig'], - 'ebs_options': DOMAIN_RET['EBSOptions'], - 'snapshot_options': DOMAIN_RET['SnapshotOptions'], - 'vpc_options': DOMAIN_RET['VPCOptions'], - 'cognito_options': DOMAIN_RET['CognitoOptions'], - 'advanced_options': DOMAIN_RET['AdvancedOptions'], - 'access_policies': DOMAIN_RET['AccessPolicies'], - 'log_publishing_options': {}, + "elasticsearch_cluster_config": DOMAIN_RET[ + "ElasticsearchClusterConfig" + ], + "ebs_options": DOMAIN_RET["EBSOptions"], + "snapshot_options": DOMAIN_RET["SnapshotOptions"], + "vpc_options": DOMAIN_RET["VPCOptions"], + "cognito_options": DOMAIN_RET["CognitoOptions"], + "advanced_options": DOMAIN_RET["AdvancedOptions"], + "access_policies": DOMAIN_RET["AccessPolicies"], + "log_publishing_options": {}, } kwargs.update(CONN_PARAMETERS) self.assertEqual( - boto3_elasticsearch.update_elasticsearch_domain_config('testdomain', **kwargs), - {'result': True, 'response': DOMAIN_RET} + boto3_elasticsearch.update_elasticsearch_domain_config( + "testdomain", **kwargs + ), + {"result": True, "response": DOMAIN_RET}, ) def test_update_domain_error(self): - ''' + """ Test that when updating a domain fails, and boto3 returns an error, the .update method returns the error. - ''' - with patch.object(self.conn, - 'update_elasticsearch_domain_config', - side_effect=ClientError(ERROR_CONTENT, 'update_domain')): + """ + with patch.object( + self.conn, + "update_elasticsearch_domain_config", + side_effect=ClientError(ERROR_CONTENT, "update_domain"), + ): kwargs = { - 'elasticsearch_cluster_config': DOMAIN_RET['ElasticsearchClusterConfig'], - 'ebs_options': DOMAIN_RET['EBSOptions'], - 'snapshot_options': DOMAIN_RET['SnapshotOptions'], - 'vpc_options': DOMAIN_RET['VPCOptions'], - 'cognito_options': DOMAIN_RET['CognitoOptions'], - 'advanced_options': DOMAIN_RET['AdvancedOptions'], - 'access_policies': DOMAIN_RET['AccessPolicies'], - 'log_publishing_options': {}, + "elasticsearch_cluster_config": DOMAIN_RET[ + "ElasticsearchClusterConfig" + ], + "ebs_options": DOMAIN_RET["EBSOptions"], + "snapshot_options": DOMAIN_RET["SnapshotOptions"], + "vpc_options": DOMAIN_RET["VPCOptions"], + "cognito_options": DOMAIN_RET["CognitoOptions"], + "advanced_options": DOMAIN_RET["AdvancedOptions"], + "access_policies": DOMAIN_RET["AccessPolicies"], + "log_publishing_options": {}, } kwargs.update(CONN_PARAMETERS) - result = boto3_elasticsearch.update_elasticsearch_domain_config('testdomain', **kwargs) + result = boto3_elasticsearch.update_elasticsearch_domain_config( + "testdomain", **kwargs + ) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'update_domain') + result.get("error", ""), ERROR_MESSAGE.format(101, "update_domain") ) def test_add_tags_positive(self): - ''' + """ Test that when adding tags is succesful, the .add_tags method returns {'result': True}. - ''' - with patch.object(self.conn, - 'describe_elasticsearch_domain', - return_value={'DomainStatus': DOMAIN_RET}): + """ + with patch.object( + self.conn, + "describe_elasticsearch_domain", + return_value={"DomainStatus": DOMAIN_RET}, + ): self.assertEqual( boto3_elasticsearch.add_tags( - 'testdomain', - tags={'foo': 'bar', 'baz': 'qux'}, - **CONN_PARAMETERS + "testdomain", tags={"foo": "bar", "baz": "qux"}, **CONN_PARAMETERS ), - {'result': True} + {"result": True}, ) def test_add_tags_error(self): - ''' + """ Test that when adding tags fails, and boto3 returns an error, the .add_tags function returns {'tagged': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'add_tags', - side_effect=ClientError(ERROR_CONTENT, 'add_tags')), \ - patch.object(self.conn, - 'describe_elasticsearch_domain', - return_value={'DomainStatus': DOMAIN_RET}): + """ + with patch.object( + self.conn, "add_tags", side_effect=ClientError(ERROR_CONTENT, "add_tags") + ), patch.object( + self.conn, + "describe_elasticsearch_domain", + return_value={"DomainStatus": DOMAIN_RET}, + ): result = boto3_elasticsearch.add_tags( - 'testdomain', - tags={'foo': 'bar', 'baz': 'qux'}, - **CONN_PARAMETERS + "testdomain", tags={"foo": "bar", "baz": "qux"}, **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'add_tags') + result.get("error", ""), ERROR_MESSAGE.format(101, "add_tags") ) def test_remove_tags_positive(self): - ''' + """ Test that when removing tags is succesful, the .remove_tags method returns {'tagged': True}. - ''' - with patch.object(self.conn, - 'describe_elasticsearch_domain', - return_value={'DomainStatus': DOMAIN_RET}): + """ + with patch.object( + self.conn, + "describe_elasticsearch_domain", + return_value={"DomainStatus": DOMAIN_RET}, + ): self.assertEqual( boto3_elasticsearch.remove_tags( - tag_keys=['foo', 'bar'], - domain_name='testdomain', - **CONN_PARAMETERS), - {'result': True} + tag_keys=["foo", "bar"], domain_name="testdomain", **CONN_PARAMETERS + ), + {"result": True}, ) def test_remove_tag_error(self): - ''' + """ Test that when removing tags fails, and boto3 returns an error, the .remove_tags method returns {'tagged': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'remove_tags', - side_effect=ClientError(ERROR_CONTENT, 'remove_tags')), \ - patch.object(self.conn, - 'describe_elasticsearch_domain', - return_value={'DomainStatus': DOMAIN_RET}): + """ + with patch.object( + self.conn, + "remove_tags", + side_effect=ClientError(ERROR_CONTENT, "remove_tags"), + ), patch.object( + self.conn, + "describe_elasticsearch_domain", + return_value={"DomainStatus": DOMAIN_RET}, + ): result = boto3_elasticsearch.remove_tags( - tag_keys=['foo', 'bar'], - domain_name='testdomain', - **CONN_PARAMETERS + tag_keys=["foo", "bar"], domain_name="testdomain", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'remove_tags') + result.get("error", ""), ERROR_MESSAGE.format(101, "remove_tags") ) def test_list_tags_positive(self): - ''' + """ Test that when listing tags is succesful, the .list_tags method returns a dict with key 'tags'. Also test that the tags returned are manipulated properly (i.e. transformed into a dict with tags). - ''' - with patch.object(self.conn, - 'describe_elasticsearch_domain', - return_value={'DomainStatus': DOMAIN_RET}), \ - patch.object(self.conn, - 'list_tags', - return_value={'TagList': [{'Key': 'foo', 'Value': 'bar'}]}): + """ + with patch.object( + self.conn, + "describe_elasticsearch_domain", + return_value={"DomainStatus": DOMAIN_RET}, + ), patch.object( + self.conn, + "list_tags", + return_value={"TagList": [{"Key": "foo", "Value": "bar"}]}, + ): result = boto3_elasticsearch.list_tags( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertEqual(result, { - 'result': True, - 'response': {'foo': 'bar'} - }) + self.assertEqual(result, {"result": True, "response": {"foo": "bar"}}) def test_list_tags_error(self): - ''' + """ Test that when listing tags causes boto3 to return an error, the .list_tags method returns the error. - ''' - with patch.object(self.conn, - 'list_tags', - side_effect=ClientError(ERROR_CONTENT, 'list_tags')), \ - patch.object(self.conn, - 'describe_elasticsearch_domain', - return_value={'DomainStatus': DOMAIN_RET}): + """ + with patch.object( + self.conn, "list_tags", side_effect=ClientError(ERROR_CONTENT, "list_tags") + ), patch.object( + self.conn, + "describe_elasticsearch_domain", + return_value={"DomainStatus": DOMAIN_RET}, + ): result = boto3_elasticsearch.list_tags( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'list_tags') + result.get("error", ""), ERROR_MESSAGE.format(101, "list_tags") ) def test_cancel_elasticsearch_service_software_update_positive(self): - ''' + """ Test that when calling cancel_elasticsearch_service_software_update and it is succesful, it returns {'result': True}. - ''' + """ retval = { - 'ServiceSoftwareOptions': { - 'CurrentVersion': 'string', - 'NewVersion': 'string', - 'UpdateAvailable': True, - 'Cancellable': True, - 'UpdateStatus': 'ELIGIBLE', - 'Description': 'string', - 'AutomatedUpdateDate': datetime.datetime(2015, 1, 1), + "ServiceSoftwareOptions": { + "CurrentVersion": "string", + "NewVersion": "string", + "UpdateAvailable": True, + "Cancellable": True, + "UpdateStatus": "ELIGIBLE", + "Description": "string", + "AutomatedUpdateDate": datetime.datetime(2015, 1, 1), } } - with patch.object(self.conn, - 'cancel_elasticsearch_service_software_update', - return_value=retval): + with patch.object( + self.conn, + "cancel_elasticsearch_service_software_update", + return_value=retval, + ): result = boto3_elasticsearch.cancel_elasticsearch_service_software_update( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertEqual(result, { - 'result': True, - }) + self.assertEqual(result, {"result": True}) def test_cancel_elasticsearch_service_software_update_error(self): - ''' + """ Test that when calling cancel_elasticsearch_service_software_update and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'cancel_elasticsearch_service_software_update', - side_effect=ClientError(ERROR_CONTENT, 'cancel_elasticsearch_service_software_update')): + """ + with patch.object( + self.conn, + "cancel_elasticsearch_service_software_update", + side_effect=ClientError( + ERROR_CONTENT, "cancel_elasticsearch_service_software_update" + ), + ): result = boto3_elasticsearch.cancel_elasticsearch_service_software_update( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'cancel_elasticsearch_service_software_update') + result.get("error", ""), + ERROR_MESSAGE.format( + 101, "cancel_elasticsearch_service_software_update" + ), ) def test_delete_elasticsearch_service_role_positive(self): - ''' + """ Test that when calling delete_elasticsearch_service_role and it is succesful, it returns {'result': True}. - ''' - with patch.object(self.conn, - 'delete_elasticsearch_service_role', - return_value=None): + """ + with patch.object( + self.conn, "delete_elasticsearch_service_role", return_value=None + ): result = boto3_elasticsearch.delete_elasticsearch_service_role( **CONN_PARAMETERS ) - self.assertEqual(result, { - 'result': True, - }) + self.assertEqual(result, {"result": True}) def test_delete_elasticsearch_service_role_error(self): - ''' + """ Test that when calling delete_elasticsearch_service_role and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'delete_elasticsearch_service_role', - side_effect=ClientError(ERROR_CONTENT, 'delete_elasticsearch_service_role')): + """ + with patch.object( + self.conn, + "delete_elasticsearch_service_role", + side_effect=ClientError(ERROR_CONTENT, "delete_elasticsearch_service_role"), + ): result = boto3_elasticsearch.delete_elasticsearch_service_role( **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'delete_elasticsearch_service_role') + result.get("error", ""), + ERROR_MESSAGE.format(101, "delete_elasticsearch_service_role"), ) def test_describe_elasticsearch_domain_config_positive(self): - ''' + """ Test that when calling describe_elasticsearch_domain_config and it is succesful, it returns {'result': True}. - ''' - with patch.object(self.conn, - 'describe_elasticsearch_domain_config', - return_value={'DomainConfig': DOMAIN_RET}): + """ + with patch.object( + self.conn, + "describe_elasticsearch_domain_config", + return_value={"DomainConfig": DOMAIN_RET}, + ): self.assertEqual( - boto3_elasticsearch.describe_elasticsearch_domain_config('testdomain', **CONN_PARAMETERS), - {'result': True, 'response': DOMAIN_RET} + boto3_elasticsearch.describe_elasticsearch_domain_config( + "testdomain", **CONN_PARAMETERS + ), + {"result": True, "response": DOMAIN_RET}, ) def test_describe_elasticsearch_domain_config_error(self): - ''' + """ Test that when calling describe_elasticsearch_domain_config and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'describe_elasticsearch_domain_config', - side_effect=ClientError(ERROR_CONTENT, 'describe_elasticsearch_domain_config')): + """ + with patch.object( + self.conn, + "describe_elasticsearch_domain_config", + side_effect=ClientError( + ERROR_CONTENT, "describe_elasticsearch_domain_config" + ), + ): result = boto3_elasticsearch.describe_elasticsearch_domain_config( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'describe_elasticsearch_domain_config') + result.get("error", ""), + ERROR_MESSAGE.format(101, "describe_elasticsearch_domain_config"), ) def test_describe_elasticsearch_domains_positive(self): - ''' + """ Test that when calling describe_elasticsearch_domains and it is succesful, it returns {'result': True, 'response': some_data}. - ''' - with patch.object(self.conn, - 'describe_elasticsearch_domains', - return_value={'DomainStatusList': [DOMAIN_RET]}): + """ + with patch.object( + self.conn, + "describe_elasticsearch_domains", + return_value={"DomainStatusList": [DOMAIN_RET]}, + ): self.assertEqual( boto3_elasticsearch.describe_elasticsearch_domains( - domain_names=['test_domain'], - **CONN_PARAMETERS + domain_names=["test_domain"], **CONN_PARAMETERS ), - {'result': True, 'response': [DOMAIN_RET]} + {"result": True, "response": [DOMAIN_RET]}, ) def test_describe_elasticsearch_domains_error(self): - ''' + """ Test that when calling describe_elasticsearch_domains and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'describe_elasticsearch_domains', - side_effect=ClientError(ERROR_CONTENT, 'describe_elasticsearch_domains')): + """ + with patch.object( + self.conn, + "describe_elasticsearch_domains", + side_effect=ClientError(ERROR_CONTENT, "describe_elasticsearch_domains"), + ): result = boto3_elasticsearch.describe_elasticsearch_domains( - domain_names=['testdomain'], - **CONN_PARAMETERS + domain_names=["testdomain"], **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'describe_elasticsearch_domains') + result.get("error", ""), + ERROR_MESSAGE.format(101, "describe_elasticsearch_domains"), ) def test_describe_elasticsearch_instance_type_limits_positive(self): - ''' + """ Test that when calling describe_elasticsearch_instance_type_limits and it succeeds, it returns {'result': True, 'response' some_value}. - ''' + """ ret_val = { - 'LimitsByRole': { - 'string': { - 'StorageTypes': [{ - 'StorageTypeName': 'string', - 'StorageSubTypeName': 'string', - 'StorageTypeLimits': [{ - 'LimitName': 'string', - 'LimitValues': ['string'], - }], - }], - 'InstanceLimits': { - 'InstanceCountLimits': { - 'MinimumInstanceCount': 123, - 'MaximumInstanceCount': 123 + "LimitsByRole": { + "string": { + "StorageTypes": [ + { + "StorageTypeName": "string", + "StorageSubTypeName": "string", + "StorageTypeLimits": [ + {"LimitName": "string", "LimitValues": ["string"]} + ], + } + ], + "InstanceLimits": { + "InstanceCountLimits": { + "MinimumInstanceCount": 123, + "MaximumInstanceCount": 123, } }, - 'AdditionalLimits': [{ - 'LimitName': 'string', - 'LimitValues': ['string'] - }], + "AdditionalLimits": [ + {"LimitName": "string", "LimitValues": ["string"]} + ], } } } - with patch.object(self.conn, - 'describe_elasticsearch_instance_type_limits', - return_value=ret_val): + with patch.object( + self.conn, + "describe_elasticsearch_instance_type_limits", + return_value=ret_val, + ): self.assertEqual( boto3_elasticsearch.describe_elasticsearch_instance_type_limits( - domain_name='testdomain', - instance_type='foo', - elasticsearch_version='1.0', + domain_name="testdomain", + instance_type="foo", + elasticsearch_version="1.0", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val['LimitsByRole']} + {"result": True, "response": ret_val["LimitsByRole"]}, ) def test_describe_elasticsearch_instance_type_limits_error(self): - ''' + """ Test that when calling describe_elasticsearch_instance_type_limits and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'describe_elasticsearch_instance_type_limits', - side_effect=ClientError(ERROR_CONTENT, 'describe_elasticsearch_instance_type_limits')): + """ + with patch.object( + self.conn, + "describe_elasticsearch_instance_type_limits", + side_effect=ClientError( + ERROR_CONTENT, "describe_elasticsearch_instance_type_limits" + ), + ): result = boto3_elasticsearch.describe_elasticsearch_instance_type_limits( - domain_name='testdomain', - instance_type='foo', - elasticsearch_version='1.0', + domain_name="testdomain", + instance_type="foo", + elasticsearch_version="1.0", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'describe_elasticsearch_instance_type_limits') + result.get("error", ""), + ERROR_MESSAGE.format( + 101, "describe_elasticsearch_instance_type_limits" + ), ) def test_describe_reserved_elasticsearch_instance_offerings_positive(self): - ''' + """ Test that when calling describe_reserved_elasticsearch_instance_offerings and it succeeds, it returns {'result': True, 'response': some_value}. - ''' + """ ret_val = { - 'NextToken': 'string', - 'ReservedElasticsearchInstanceOfferings': [{ - 'ReservedElasticsearchInstanceOfferingId': 'string', - 'ElasticsearchInstanceType': 't2.medium.elasticsearch', - 'Duration': 123, - 'FixedPrice': 123.0, - 'UsagePrice': 123.0, - 'CurrencyCode': 'string', - 'PaymentOption': 'NO_UPFRONT', - 'RecurringCharges': [{ - 'RecurringChargeAmount': 123.0, - 'RecurringChargeFrequency': 'string' - }] - }] + "NextToken": "string", + "ReservedElasticsearchInstanceOfferings": [ + { + "ReservedElasticsearchInstanceOfferingId": "string", + "ElasticsearchInstanceType": "t2.medium.elasticsearch", + "Duration": 123, + "FixedPrice": 123.0, + "UsagePrice": 123.0, + "CurrencyCode": "string", + "PaymentOption": "NO_UPFRONT", + "RecurringCharges": [ + { + "RecurringChargeAmount": 123.0, + "RecurringChargeFrequency": "string", + } + ], + } + ], } - with patch.object(self.paginator, - 'paginate', - return_value=[ret_val]): + with patch.object(self.paginator, "paginate", return_value=[ret_val]): self.assertEqual( boto3_elasticsearch.describe_reserved_elasticsearch_instance_offerings( - reserved_elasticsearch_instance_offering_id='foo', - **CONN_PARAMETERS + reserved_elasticsearch_instance_offering_id="foo", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val['ReservedElasticsearchInstanceOfferings']} + { + "result": True, + "response": ret_val["ReservedElasticsearchInstanceOfferings"], + }, ) def test_describe_reserved_elasticsearch_instance_offerings_error(self): - ''' + """ Test that when calling describe_reserved_elasticsearch_instance_offerings and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.paginator, - 'paginate', - side_effect=ClientError(ERROR_CONTENT, 'describe_reserved_elasticsearch_instance_offerings')): + """ + with patch.object( + self.paginator, + "paginate", + side_effect=ClientError( + ERROR_CONTENT, "describe_reserved_elasticsearch_instance_offerings" + ), + ): result = boto3_elasticsearch.describe_reserved_elasticsearch_instance_offerings( - reserved_elasticsearch_instance_offering_id='foo', - **CONN_PARAMETERS + reserved_elasticsearch_instance_offering_id="foo", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'describe_reserved_elasticsearch_instance_offerings') + result.get("error", ""), + ERROR_MESSAGE.format( + 101, "describe_reserved_elasticsearch_instance_offerings" + ), ) def test_describe_reserved_elasticsearch_instances_positive(self): - ''' + """ Test that when calling describe_reserved_elasticsearch_instances and it succeeds, it returns {'result': True, 'response': some_value}. - ''' + """ ret_val = { - 'NextToken': 'string', - 'ReservedElasticsearchInstances': [{ - 'ReservationName': 'string', - 'ReservedElasticsearchInstanceId': 'string', - 'ReservedElasticsearchInstanceOfferingId': 'string', - 'ElasticsearchInstanceType': 't2.medium.elasticsearch', - 'StartTime': datetime.datetime(2015, 1, 1), - 'Duration': 123, - 'FixedPrice': 123.0, - 'UsagePrice': 123.0, - 'CurrencyCode': 'string', - 'ElasticsearchInstanceCount': 123, - 'State': 'string', - 'PaymentOption': 'ALL_UPFRONT', - 'RecurringCharges': [{ - 'RecurringChargeAmount': 123.0, - 'RecurringChargeFrequency': 'string' + "NextToken": "string", + "ReservedElasticsearchInstances": [ + { + "ReservationName": "string", + "ReservedElasticsearchInstanceId": "string", + "ReservedElasticsearchInstanceOfferingId": "string", + "ElasticsearchInstanceType": "t2.medium.elasticsearch", + "StartTime": datetime.datetime(2015, 1, 1), + "Duration": 123, + "FixedPrice": 123.0, + "UsagePrice": 123.0, + "CurrencyCode": "string", + "ElasticsearchInstanceCount": 123, + "State": "string", + "PaymentOption": "ALL_UPFRONT", + "RecurringCharges": [ + { + "RecurringChargeAmount": 123.0, + "RecurringChargeFrequency": "string", }, - ] + ], }, - ] + ], } - with patch.object(self.paginator, - 'paginate', - return_value=[ret_val]): + with patch.object(self.paginator, "paginate", return_value=[ret_val]): self.assertEqual( boto3_elasticsearch.describe_reserved_elasticsearch_instances( - reserved_elasticsearch_instance_id='foo', - **CONN_PARAMETERS + reserved_elasticsearch_instance_id="foo", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val['ReservedElasticsearchInstances']} + {"result": True, "response": ret_val["ReservedElasticsearchInstances"]}, ) def test_describe_reserved_elasticsearch_instances_error(self): - ''' + """ Test that when calling describe_reserved_elasticsearch_instances and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.paginator, - 'paginate', - side_effect=ClientError(ERROR_CONTENT, 'describe_reserved_elasticsearch_instances')): + """ + with patch.object( + self.paginator, + "paginate", + side_effect=ClientError( + ERROR_CONTENT, "describe_reserved_elasticsearch_instances" + ), + ): result = boto3_elasticsearch.describe_reserved_elasticsearch_instances( - reserved_elasticsearch_instance_id='foo', - **CONN_PARAMETERS + reserved_elasticsearch_instance_id="foo", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'describe_reserved_elasticsearch_instances') + result.get("error", ""), + ERROR_MESSAGE.format(101, "describe_reserved_elasticsearch_instances"), ) def test_get_compatible_elasticsearch_versions_positive(self): - ''' + """ Test that when calling get_compatible_elasticsearch_versions and it succeeds, it returns {'result': True, 'response': some_value}. - ''' + """ ret_val = { - 'CompatibleElasticsearchVersions': [{ - 'SourceVersion': 'string', - 'TargetVersions': [ - 'string', - ] - }] + "CompatibleElasticsearchVersions": [ + {"SourceVersion": "string", "TargetVersions": ["string"]} + ] } - with patch.object(self.conn, - 'get_compatible_elasticsearch_versions', - return_value=ret_val): + with patch.object( + self.conn, "get_compatible_elasticsearch_versions", return_value=ret_val + ): self.assertEqual( boto3_elasticsearch.get_compatible_elasticsearch_versions( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val['CompatibleElasticsearchVersions']} + { + "result": True, + "response": ret_val["CompatibleElasticsearchVersions"], + }, ) def test_get_compatible_elasticsearch_versions_error(self): - ''' + """ Test that when calling get_compatible_elasticsearch_versions and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'get_compatible_elasticsearch_versions', - side_effect=ClientError(ERROR_CONTENT, 'get_compatible_elasticsearch_versions')): + """ + with patch.object( + self.conn, + "get_compatible_elasticsearch_versions", + side_effect=ClientError( + ERROR_CONTENT, "get_compatible_elasticsearch_versions" + ), + ): result = boto3_elasticsearch.get_compatible_elasticsearch_versions( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'get_compatible_elasticsearch_versions') + result.get("error", ""), + ERROR_MESSAGE.format(101, "get_compatible_elasticsearch_versions"), ) def test_get_upgrade_history_positive(self): - ''' + """ Test that when calling get_upgrade_history and it succeeds, it returns {'result': True, 'response': some_value}. - ''' + """ ret_val = { - 'UpgradeHistories': [{ - 'UpgradeName': 'string', - 'StartTimestamp': datetime.datetime(2015, 1, 1), - 'UpgradeStatus': 'IN_PROGRESS', - 'StepsList': [{ - 'UpgradeStep': 'PRE_UPGRADE_CHECK', - 'UpgradeStepStatus': 'IN_PROGRESS', - 'Issues': [ - 'string', - ], - 'ProgressPercent': 123.0 - }] - }], - 'NextToken': 'string' + "UpgradeHistories": [ + { + "UpgradeName": "string", + "StartTimestamp": datetime.datetime(2015, 1, 1), + "UpgradeStatus": "IN_PROGRESS", + "StepsList": [ + { + "UpgradeStep": "PRE_UPGRADE_CHECK", + "UpgradeStepStatus": "IN_PROGRESS", + "Issues": ["string"], + "ProgressPercent": 123.0, + } + ], + } + ], + "NextToken": "string", } - with patch.object(self.paginator, - 'paginate', - return_value=[ret_val]): + with patch.object(self.paginator, "paginate", return_value=[ret_val]): self.assertEqual( boto3_elasticsearch.get_upgrade_history( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val['UpgradeHistories']} + {"result": True, "response": ret_val["UpgradeHistories"]}, ) def test_get_upgrade_history_error(self): - ''' + """ Test that when calling get_upgrade_history and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.paginator, - 'paginate', - side_effect=ClientError(ERROR_CONTENT, 'get_upgrade_history')): + """ + with patch.object( + self.paginator, + "paginate", + side_effect=ClientError(ERROR_CONTENT, "get_upgrade_history"), + ): result = boto3_elasticsearch.get_upgrade_history( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'get_upgrade_history') + result.get("error", ""), + ERROR_MESSAGE.format(101, "get_upgrade_history"), ) def test_get_upgrade_status_positive(self): - ''' + """ Test that when calling get_upgrade_status and it succeeds, it returns {'result': True, 'response': some_value}. - ''' + """ ret_val = { - 'UpgradeStep': 'PRE_UPGRADE_CHECK', - 'StepStatus': 'IN_PROGRESS', - 'UpgradeName': 'string', - 'ResponseMetadata': None, + "UpgradeStep": "PRE_UPGRADE_CHECK", + "StepStatus": "IN_PROGRESS", + "UpgradeName": "string", + "ResponseMetadata": None, } - with patch.object(self.conn, - 'get_upgrade_status', - return_value=ret_val): + with patch.object(self.conn, "get_upgrade_status", return_value=ret_val): self.assertEqual( boto3_elasticsearch.get_upgrade_status( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val} + {"result": True, "response": ret_val}, ) def test_get_upgrade_status_error(self): - ''' + """ Test that when calling get_upgrade_status and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'get_upgrade_status', - side_effect=ClientError(ERROR_CONTENT, 'get_upgrade_status')): + """ + with patch.object( + self.conn, + "get_upgrade_status", + side_effect=ClientError(ERROR_CONTENT, "get_upgrade_status"), + ): result = boto3_elasticsearch.get_upgrade_status( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'get_upgrade_status') + result.get("error", ""), ERROR_MESSAGE.format(101, "get_upgrade_status") ) def test_list_domain_names_positive(self): - ''' + """ Test that when calling list_domain_names and it succeeds, it returns {'result': True, 'response': some_value}. - ''' - ret_val = { - 'DomainNames': [{ - 'DomainName': 'string' - }] - } - with patch.object(self.conn, - 'list_domain_names', - return_value=ret_val): + """ + ret_val = {"DomainNames": [{"DomainName": "string"}]} + with patch.object(self.conn, "list_domain_names", return_value=ret_val): self.assertEqual( - boto3_elasticsearch.list_domain_names( - **CONN_PARAMETERS - ), - {'result': True, 'response': [item['DomainName'] for item in ret_val['DomainNames']]} + boto3_elasticsearch.list_domain_names(**CONN_PARAMETERS), + { + "result": True, + "response": [item["DomainName"] for item in ret_val["DomainNames"]], + }, ) def test_list_domain_names_error(self): - ''' + """ Test that when calling list_domain_names and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'list_domain_names', - side_effect=ClientError(ERROR_CONTENT, 'list_domain_names')): - result = boto3_elasticsearch.list_domain_names( - **CONN_PARAMETERS - ) - self.assertFalse(result['result']) + """ + with patch.object( + self.conn, + "list_domain_names", + side_effect=ClientError(ERROR_CONTENT, "list_domain_names"), + ): + result = boto3_elasticsearch.list_domain_names(**CONN_PARAMETERS) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'list_domain_names') + result.get("error", ""), ERROR_MESSAGE.format(101, "list_domain_names") ) def test_list_elasticsearch_instance_types_positive(self): - ''' + """ Test that when calling list_elasticsearch_instance_types and it succeeds, it returns {'result': True, 'response': some_value}. - ''' + """ ret_val = { - 'ElasticsearchInstanceTypes': [ - 'm3.medium.elasticsearch', 'm3.large.elasticsearch', 'm3.xlarge.elasticsearch', - 'm3.2xlarge.elasticsearch', 'm4.large.elasticsearch', 'm4.xlarge.elasticsearch', - 'm4.2xlarge.elasticsearch', 'm4.4xlarge.elasticsearch', 'm4.10xlarge.elasticsearch', - 't2.micro.elasticsearch', 't2.small.elasticsearch', 't2.medium.elasticsearch', - 'r3.large.elasticsearch', 'r3.xlarge.elasticsearch', 'r3.2xlarge.elasticsearch', - 'r3.4xlarge.elasticsearch', 'r3.8xlarge.elasticsearch', 'i2.xlarge.elasticsearch', - 'i2.2xlarge.elasticsearch', 'd2.xlarge.elasticsearch', 'd2.2xlarge.elasticsearch', - 'd2.4xlarge.elasticsearch', 'd2.8xlarge.elasticsearch', 'c4.large.elasticsearch', - 'c4.xlarge.elasticsearch', 'c4.2xlarge.elasticsearch', 'c4.4xlarge.elasticsearch', - 'c4.8xlarge.elasticsearch', 'r4.large.elasticsearch', 'r4.xlarge.elasticsearch', - 'r4.2xlarge.elasticsearch', 'r4.4xlarge.elasticsearch', 'r4.8xlarge.elasticsearch', - 'r4.16xlarge.elasticsearch', 'i3.large.elasticsearch', 'i3.xlarge.elasticsearch', - 'i3.2xlarge.elasticsearch', 'i3.4xlarge.elasticsearch', 'i3.8xlarge.elasticsearch', - 'i3.16xlarge.elasticsearch', + "ElasticsearchInstanceTypes": [ + "m3.medium.elasticsearch", + "m3.large.elasticsearch", + "m3.xlarge.elasticsearch", + "m3.2xlarge.elasticsearch", + "m4.large.elasticsearch", + "m4.xlarge.elasticsearch", + "m4.2xlarge.elasticsearch", + "m4.4xlarge.elasticsearch", + "m4.10xlarge.elasticsearch", + "t2.micro.elasticsearch", + "t2.small.elasticsearch", + "t2.medium.elasticsearch", + "r3.large.elasticsearch", + "r3.xlarge.elasticsearch", + "r3.2xlarge.elasticsearch", + "r3.4xlarge.elasticsearch", + "r3.8xlarge.elasticsearch", + "i2.xlarge.elasticsearch", + "i2.2xlarge.elasticsearch", + "d2.xlarge.elasticsearch", + "d2.2xlarge.elasticsearch", + "d2.4xlarge.elasticsearch", + "d2.8xlarge.elasticsearch", + "c4.large.elasticsearch", + "c4.xlarge.elasticsearch", + "c4.2xlarge.elasticsearch", + "c4.4xlarge.elasticsearch", + "c4.8xlarge.elasticsearch", + "r4.large.elasticsearch", + "r4.xlarge.elasticsearch", + "r4.2xlarge.elasticsearch", + "r4.4xlarge.elasticsearch", + "r4.8xlarge.elasticsearch", + "r4.16xlarge.elasticsearch", + "i3.large.elasticsearch", + "i3.xlarge.elasticsearch", + "i3.2xlarge.elasticsearch", + "i3.4xlarge.elasticsearch", + "i3.8xlarge.elasticsearch", + "i3.16xlarge.elasticsearch", ], - 'NextToken': 'string' + "NextToken": "string", } - with patch.object(self.paginator, - 'paginate', - return_value=[ret_val]): + with patch.object(self.paginator, "paginate", return_value=[ret_val]): self.assertEqual( boto3_elasticsearch.list_elasticsearch_instance_types( - elasticsearch_version='1.0', - **CONN_PARAMETERS + elasticsearch_version="1.0", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val['ElasticsearchInstanceTypes']} + {"result": True, "response": ret_val["ElasticsearchInstanceTypes"]}, ) def test_list_elasticsearch_instance_types_error(self): - ''' + """ Test that when calling list_elasticsearch_instance_types and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.paginator, - 'paginate', - side_effect=ClientError(ERROR_CONTENT, 'list_elasticsearch_instance_types')): + """ + with patch.object( + self.paginator, + "paginate", + side_effect=ClientError(ERROR_CONTENT, "list_elasticsearch_instance_types"), + ): result = boto3_elasticsearch.list_elasticsearch_instance_types( - elasticsearch_version='1.0', - **CONN_PARAMETERS + elasticsearch_version="1.0", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'list_elasticsearch_instance_types') + result.get("error", ""), + ERROR_MESSAGE.format(101, "list_elasticsearch_instance_types"), ) def test_list_elasticsearch_versions_positive(self): - ''' + """ Test that when calling list_elasticsearch_versions and it succeeds, it returns {'result': True, 'response': some_value}. - ''' - ret_val = { - 'ElasticsearchVersions': ['string'], - 'NextToken': 'string' - } - with patch.object(self.paginator, - 'paginate', - return_value=[ret_val]): + """ + ret_val = {"ElasticsearchVersions": ["string"], "NextToken": "string"} + with patch.object(self.paginator, "paginate", return_value=[ret_val]): self.assertEqual( - boto3_elasticsearch.list_elasticsearch_versions( - **CONN_PARAMETERS - ), - {'result': True, 'response': ret_val['ElasticsearchVersions']} + boto3_elasticsearch.list_elasticsearch_versions(**CONN_PARAMETERS), + {"result": True, "response": ret_val["ElasticsearchVersions"]}, ) def test_list_elasticsearch_versions_error(self): - ''' + """ Test that when calling list_elasticsearch_versions and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.paginator, - 'paginate', - side_effect=ClientError(ERROR_CONTENT, 'list_elasticsearch_versions')): - result = boto3_elasticsearch.list_elasticsearch_versions( - **CONN_PARAMETERS - ) - self.assertFalse(result['result']) + """ + with patch.object( + self.paginator, + "paginate", + side_effect=ClientError(ERROR_CONTENT, "list_elasticsearch_versions"), + ): + result = boto3_elasticsearch.list_elasticsearch_versions(**CONN_PARAMETERS) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'list_elasticsearch_versions') + result.get("error", ""), + ERROR_MESSAGE.format(101, "list_elasticsearch_versions"), ) def test_purchase_reserved_elasticsearch_instance_offering_positive(self): - ''' + """ Test that when calling purchase_reserved_elasticsearch_instance_offering and it succeeds, it returns {'result': True, 'response': some_value}. - ''' + """ ret_val = { - 'ReservedElasticsearchInstanceId': 'string', - 'ReservationName': 'string' + "ReservedElasticsearchInstanceId": "string", + "ReservationName": "string", } - with patch.object(self.conn, - 'purchase_reserved_elasticsearch_instance_offering', - return_value=ret_val): + with patch.object( + self.conn, + "purchase_reserved_elasticsearch_instance_offering", + return_value=ret_val, + ): self.assertEqual( boto3_elasticsearch.purchase_reserved_elasticsearch_instance_offering( - reserved_elasticsearch_instance_offering_id='foo', - reservation_name='bar', + reserved_elasticsearch_instance_offering_id="foo", + reservation_name="bar", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val} + {"result": True, "response": ret_val}, ) def test_purchase_reserved_elasticsearch_instance_offering_error(self): - ''' + """ Test that when calling purchase_reserved_elasticsearch_instance_offering and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'purchase_reserved_elasticsearch_instance_offering', - side_effect=ClientError(ERROR_CONTENT, 'purchase_reserved_elasticsearch_instance_offering')): + """ + with patch.object( + self.conn, + "purchase_reserved_elasticsearch_instance_offering", + side_effect=ClientError( + ERROR_CONTENT, "purchase_reserved_elasticsearch_instance_offering" + ), + ): result = boto3_elasticsearch.purchase_reserved_elasticsearch_instance_offering( - reserved_elasticsearch_instance_offering_id='foo', - reservation_name='bar', + reserved_elasticsearch_instance_offering_id="foo", + reservation_name="bar", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'purchase_reserved_elasticsearch_instance_offering') + result.get("error", ""), + ERROR_MESSAGE.format( + 101, "purchase_reserved_elasticsearch_instance_offering" + ), ) def test_start_elasticsearch_service_software_update_positive(self): - ''' + """ Test that when calling start_elasticsearch_service_software_update and it succeeds, it returns {'result': True, 'response': some_value}. - ''' + """ ret_val = { - 'ServiceSoftwareOptions': { - 'CurrentVersion': 'string', - 'NewVersion': 'string', - 'UpdateAvailable': True, - 'Cancellable': True, - 'UpdateStatus': 'PENDING_UPDATE', - 'Description': 'string', - 'AutomatedUpdateDate': datetime.datetime(2015, 1, 1) + "ServiceSoftwareOptions": { + "CurrentVersion": "string", + "NewVersion": "string", + "UpdateAvailable": True, + "Cancellable": True, + "UpdateStatus": "PENDING_UPDATE", + "Description": "string", + "AutomatedUpdateDate": datetime.datetime(2015, 1, 1), } } - with patch.object(self.conn, - 'start_elasticsearch_service_software_update', - return_value=ret_val): + with patch.object( + self.conn, + "start_elasticsearch_service_software_update", + return_value=ret_val, + ): self.assertEqual( boto3_elasticsearch.start_elasticsearch_service_software_update( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val['ServiceSoftwareOptions']} + {"result": True, "response": ret_val["ServiceSoftwareOptions"]}, ) def test_start_elasticsearch_service_software_update_error(self): - ''' + """ Test that when calling start_elasticsearch_service_software_update and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'start_elasticsearch_service_software_update', - side_effect=ClientError(ERROR_CONTENT, 'start_elasticsearch_service_software_update')): + """ + with patch.object( + self.conn, + "start_elasticsearch_service_software_update", + side_effect=ClientError( + ERROR_CONTENT, "start_elasticsearch_service_software_update" + ), + ): result = boto3_elasticsearch.start_elasticsearch_service_software_update( - domain_name='testdomain', - **CONN_PARAMETERS + domain_name="testdomain", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'start_elasticsearch_service_software_update') + result.get("error", ""), + ERROR_MESSAGE.format( + 101, "start_elasticsearch_service_software_update" + ), ) def test_upgrade_elasticsearch_domain_positive(self): - ''' + """ Test that when calling upgrade_elasticsearch_domain and it succeeds, it returns {'result': True, 'response': some_value}. - ''' + """ ret_val = { - 'DomainName': 'string', - 'TargetVersion': 'string', - 'PerformCheckOnly': True + "DomainName": "string", + "TargetVersion": "string", + "PerformCheckOnly": True, } - with patch.object(self.conn, - 'upgrade_elasticsearch_domain', - return_value=ret_val): + with patch.object( + self.conn, "upgrade_elasticsearch_domain", return_value=ret_val + ): self.assertEqual( boto3_elasticsearch.upgrade_elasticsearch_domain( - domain_name='testdomain', - target_version='1.1', - **CONN_PARAMETERS + domain_name="testdomain", target_version="1.1", **CONN_PARAMETERS ), - {'result': True, 'response': ret_val} + {"result": True, "response": ret_val}, ) def test_upgrade_elasticsearch_domain_error(self): - ''' + """ Test that when calling upgrade_elasticsearch_domain and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. - ''' - with patch.object(self.conn, - 'upgrade_elasticsearch_domain', - side_effect=ClientError(ERROR_CONTENT, 'upgrade_elasticsearch_domain')): + """ + with patch.object( + self.conn, + "upgrade_elasticsearch_domain", + side_effect=ClientError(ERROR_CONTENT, "upgrade_elasticsearch_domain"), + ): result = boto3_elasticsearch.upgrade_elasticsearch_domain( - domain_name='testdomain', - target_version='1.1', - **CONN_PARAMETERS + domain_name="testdomain", target_version="1.1", **CONN_PARAMETERS ) - self.assertFalse(result['result']) + self.assertFalse(result["result"]) self.assertEqual( - result.get('error', ''), - ERROR_MESSAGE.format(101, 'upgrade_elasticsearch_domain') + result.get("error", ""), + ERROR_MESSAGE.format(101, "upgrade_elasticsearch_domain"), ) diff --git a/tests/unit/modules/test_boto_apigateway.py b/tests/unit/modules/test_boto_apigateway.py index ac36d2c23ce..dd348550a31 100644 --- a/tests/unit/modules/test_boto_apigateway.py +++ b/tests/unit/modules/test_boto_apigateway.py @@ -2,153 +2,147 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import datetime import logging import random import string -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch - # Import Salt libs import salt.loader import salt.modules.boto_apigateway as boto_apigateway +from salt.ext.six.moves import range, zip from salt.utils.versions import LooseVersion +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module try: import boto3 import botocore from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False -from salt.ext.six.moves import range, zip # pylint: enable=import-error,no-name-in-module # the boto_lambda module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' -required_botocore_version = '1.4.49' +required_boto3_version = "1.2.1" +required_botocore_version = "1.4.49" -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} -error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' -error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } +error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" +) +error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} api_key_ret = { - u'description': u'test-lambda-api-key', u'enabled': True, - u'stageKeys': [u'123yd1l123/test'], - u'lastUpdatedDate': datetime.datetime(2015, 11, 4, 19, 22, 18), - u'createdDate': datetime.datetime(2015, 11, 4, 19, 21, 7), - u'id': u'88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - u'name': u'test-salt-key', - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} + "description": "test-lambda-api-key", + "enabled": True, + "stageKeys": ["123yd1l123/test"], + "lastUpdatedDate": datetime.datetime(2015, 11, 4, 19, 22, 18), + "createdDate": datetime.datetime(2015, 11, 4, 19, 21, 7), + "id": "88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", + "name": "test-salt-key", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, +} -api_model_error_schema = u'{"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"},"fields":{"type":"string"}},"definitions":{}}' +api_model_error_schema = '{"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"},"fields":{"type":"string"}},"definitions":{}}' api_model_ret = { - u'contentType': u'application/json', - u'name': u'Error', - u'description': u'Error Model', - u'id': u'iltqcb', - u'schema': api_model_error_schema, - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + "contentType": "application/json", + "name": "Error", + "description": "Error Model", + "id": "iltqcb", + "schema": api_model_error_schema, + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, +} api_resources_ret = { - u'items': [{u'id': u'hhg2t8f4h9', - u'path': u'/'}, - {u'id': u'isr8q2', - u'parentId': u'hhg2t8f4h9', - u'path': u'/api', - u'pathPart': u'api'}, - {u'id': u'5pvx7w', - u'parentId': 'isr8q2', - u'path': u'/api/users', - u'pathPart': u'users', - u'resourceMethods': {u'OPTIONS': {}, - u'POST': {}}}], - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + "items": [ + {"id": "hhg2t8f4h9", "path": "/"}, + {"id": "isr8q2", "parentId": "hhg2t8f4h9", "path": "/api", "pathPart": "api"}, + { + "id": "5pvx7w", + "parentId": "isr8q2", + "path": "/api/users", + "pathPart": "users", + "resourceMethods": {"OPTIONS": {}, "POST": {}}, + }, + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, +} api_create_resource_ret = { - u'id': u'123abc', - u'parentId': u'hhg2t8f4h9', - u'path': u'/api3', - u'pathPart': u'api3', - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + "id": "123abc", + "parentId": "hhg2t8f4h9", + "path": "/api3", + "pathPart": "api3", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, +} usage_plan1 = dict( - id='plan1_id', - name='plan1_name', - description='plan1_desc', + id="plan1_id", + name="plan1_name", + description="plan1_desc", apiStages=[], - throttle=dict( - burstLimit=123, - rateLimit=123.0 - ), - quota=dict( - limit=123, - offset=123, - period='DAY' - ) + throttle=dict(burstLimit=123, rateLimit=123.0), + quota=dict(limit=123, offset=123, period="DAY"), ) usage_plan2 = dict( - id='plan2_id', - name='plan2_name', - description='plan2_desc', + id="plan2_id", + name="plan2_name", + description="plan2_desc", apiStages=[], - throttle=dict( - burstLimit=123, - rateLimit=123.0 - ), - quota=dict( - limit=123, - offset=123, - period='DAY' - ) + throttle=dict(burstLimit=123, rateLimit=123.0), + quota=dict(limit=123, offset=123, period="DAY"), ) usage_plan1b = dict( - id='another_plan1_id', - name='plan1_name', - description='another_plan1_desc', + id="another_plan1_id", + name="plan1_name", + description="another_plan1_desc", apiStages=[], - throttle=dict( - burstLimit=123, - rateLimit=123.0 - ), - quota=dict( - limit=123, - offset=123, - period='DAY' - ) -) -usage_plans_ret = dict( - items=[ - usage_plan1, usage_plan2, usage_plan1b - ] + throttle=dict(burstLimit=123, rateLimit=123.0), + quota=dict(limit=123, offset=123, period="DAY"), ) +usage_plans_ret = dict(items=[usage_plan1, usage_plan2, usage_plan1b]) log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -158,9 +152,9 @@ def _has_required_boto(): def _has_required_botocore(): - ''' + """ Returns True/False boolean depending on if botocore supports usage plan - ''' + """ if not HAS_BOTO: return False elif LooseVersion(botocore.__version__) < LooseVersion(required_botocore_version): @@ -175,44 +169,41 @@ class BotoApiGatewayTestCaseBase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform']) - return { - boto_apigateway: { - '__opts__': opts, - '__utils__': utils, - } - } + opts, whitelist=["boto3", "args", "systemd", "path", "platform"] + ) + return {boto_apigateway: {"__opts__": opts, "__utils__": utils}} def setUp(self): TestCase.setUp(self) # __virtual__ must be caller in order for _get_conn to be injected boto_apigateway.__init__(self.opts) - delattr(self, 'opts') + delattr(self, "opts") # Set up MagicMock to replace the boto3 session # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() session_instance.client.return_value = self.conn - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") class BotoApiGatewayTestCaseMixin(object): def _diff_list_dicts(self, listdict1, listdict2, sortkey): - ''' + """ Compares the two list of dictionaries to ensure they have same content. Returns True if there is difference, else False - ''' + """ if len(listdict1) != len(listdict2): return True @@ -224,1430 +215,2317 @@ class BotoApiGatewayTestCaseMixin(object): return False -#@skipIf(True, 'Skip these tests while investigating failures') -@skipIf(HAS_BOTO is False, 'The boto3 module must be installed.') -@skipIf(_has_required_boto() is False, - 'The boto3 module must be greater than' - ' or equal to version {0}'.format(required_boto3_version)) -@skipIf(_has_required_botocore() is False, - 'The botocore module must be greater than' - ' or equal to version {0}'.format(required_botocore_version)) +# @skipIf(True, 'Skip these tests while investigating failures') +@skipIf(HAS_BOTO is False, "The boto3 module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) +@skipIf( + _has_required_botocore() is False, + "The botocore module must be greater than" + " or equal to version {0}".format(required_botocore_version), +) class BotoApiGatewayTestCase(BotoApiGatewayTestCaseBase, BotoApiGatewayTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_apigateway module - ''' + """ - def test_that_when_checking_if_a_rest_api_exists_and_a_rest_api_exists_the_api_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_rest_api_exists_and_a_rest_api_exists_the_api_exists_method_returns_true( + self, + ): + """ Tests checking an apigateway rest api existence when api's name exists - ''' - self.conn.get_rest_apis.return_value = {'items': [{'name': 'myapi', 'id': '1234def'}]} - api_exists_result = boto_apigateway.api_exists(name='myapi', **conn_parameters) + """ + self.conn.get_rest_apis.return_value = { + "items": [{"name": "myapi", "id": "1234def"}] + } + api_exists_result = boto_apigateway.api_exists(name="myapi", **conn_parameters) - self.assertTrue(api_exists_result['exists']) + self.assertTrue(api_exists_result["exists"]) - def test_that_when_checking_if_a_rest_api_exists_and_multiple_rest_api_exist_the_api_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_rest_api_exists_and_multiple_rest_api_exist_the_api_exists_method_returns_true( + self, + ): + """ Tests checking an apigateway rest api existence when multiple api's with same name exists - ''' - self.conn.get_rest_apis.return_value = {'items': [{'name': 'myapi', 'id': '1234abc'}, - {'name': 'myapi', 'id': '1234def'}]} - api_exists_result = boto_apigateway.api_exists(name='myapi', **conn_parameters) - self.assertTrue(api_exists_result['exists']) + """ + self.conn.get_rest_apis.return_value = { + "items": [ + {"name": "myapi", "id": "1234abc"}, + {"name": "myapi", "id": "1234def"}, + ] + } + api_exists_result = boto_apigateway.api_exists(name="myapi", **conn_parameters) + self.assertTrue(api_exists_result["exists"]) - def test_that_when_checking_if_a_rest_api_exists_and_no_rest_api_exists_the_api_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_rest_api_exists_and_no_rest_api_exists_the_api_exists_method_returns_false( + self, + ): + """ Tests checking an apigateway rest api existence when no matching rest api name exists - ''' - self.conn.get_rest_apis.return_value = {'items': [{'name': 'myapi', 'id': '1234abc'}, - {'name': 'myapi', 'id': '1234def'}]} - api_exists_result = boto_apigateway.api_exists(name='myapi123', **conn_parameters) - self.assertFalse(api_exists_result['exists']) + """ + self.conn.get_rest_apis.return_value = { + "items": [ + {"name": "myapi", "id": "1234abc"}, + {"name": "myapi", "id": "1234def"}, + ] + } + api_exists_result = boto_apigateway.api_exists( + name="myapi123", **conn_parameters + ) + self.assertFalse(api_exists_result["exists"]) - def test_that_when_describing_rest_apis_and_no_name_given_the_describe_apis_method_returns_list_of_all_rest_apis(self): - ''' + def test_that_when_describing_rest_apis_and_no_name_given_the_describe_apis_method_returns_list_of_all_rest_apis( + self, + ): + """ Tests that all rest apis defined for a region is returned - ''' - self.conn.get_rest_apis.return_value = {u'items': [{u'description': u'A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'id': u'2ut6i4vyle', - u'name': u'Swagger Petstore'}, - {u'description': u'testingabcd', - u'createdDate': datetime.datetime(2015, 12, 3, 21, 57, 58), - u'id': u'g41ls77hz0', - u'name': u'testingabc'}, - {u'description': u'a simple food delivery service test', - u'createdDate': datetime.datetime(2015, 11, 4, 23, 57, 28), - u'id': u'h7pbwydho9', - u'name': u'Food Delivery Service'}, - {u'description': u'Created by AWS Lambda', - u'createdDate': datetime.datetime(2015, 11, 4, 17, 55, 41), - u'id': u'i2yyd1ldvj', - u'name': u'LambdaMicroservice'}, - {u'description': u'cloud tap service with combination of API GW and Lambda', - u'createdDate': datetime.datetime(2015, 11, 17, 22, 3, 18), - u'id': u'rm06h9oac4', - u'name': u'API Gateway Cloudtap Service'}, - {u'description': u'testing1234', - u'createdDate': datetime.datetime(2015, 12, 2, 19, 51, 44), - u'id': u'vtir6ssxvd', - u'name': u'testing123'}], - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - items = self.conn.get_rest_apis.return_value['items'] + """ + self.conn.get_rest_apis.return_value = { + "items": [ + { + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "id": "2ut6i4vyle", + "name": "Swagger Petstore", + }, + { + "description": "testingabcd", + "createdDate": datetime.datetime(2015, 12, 3, 21, 57, 58), + "id": "g41ls77hz0", + "name": "testingabc", + }, + { + "description": "a simple food delivery service test", + "createdDate": datetime.datetime(2015, 11, 4, 23, 57, 28), + "id": "h7pbwydho9", + "name": "Food Delivery Service", + }, + { + "description": "Created by AWS Lambda", + "createdDate": datetime.datetime(2015, 11, 4, 17, 55, 41), + "id": "i2yyd1ldvj", + "name": "LambdaMicroservice", + }, + { + "description": "cloud tap service with combination of API GW and Lambda", + "createdDate": datetime.datetime(2015, 11, 17, 22, 3, 18), + "id": "rm06h9oac4", + "name": "API Gateway Cloudtap Service", + }, + { + "description": "testing1234", + "createdDate": datetime.datetime(2015, 12, 2, 19, 51, 44), + "id": "vtir6ssxvd", + "name": "testing123", + }, + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + items = self.conn.get_rest_apis.return_value["items"] get_apis_result = boto_apigateway.describe_apis(**conn_parameters) items_dt = [boto_apigateway._convert_datetime_str(item) for item in items] - apis = get_apis_result.get('restapi') + apis = get_apis_result.get("restapi") - diff = self._diff_list_dicts(apis, items_dt, 'id') + diff = self._diff_list_dicts(apis, items_dt, "id") self.assertTrue(apis) self.assertEqual(len(apis), len(items)) self.assertFalse(diff) - def test_that_when_describing_rest_apis_and_name_is_testing123_the_describe_apis_method_returns_list_of_two_rest_apis(self): - ''' + def test_that_when_describing_rest_apis_and_name_is_testing123_the_describe_apis_method_returns_list_of_two_rest_apis( + self, + ): + """ Tests that exactly 2 apis are returned matching 'testing123' - ''' - self.conn.get_rest_apis.return_value = {u'items': [{u'description': u'A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'id': u'2ut6i4vyle', - u'name': u'Swagger Petstore'}, - {u'description': u'testingabcd', - u'createdDate': datetime.datetime(2015, 12, 3, 21, 57, 58), - u'id': u'g41ls77hz0', - u'name': u'testing123'}, - {u'description': u'a simple food delivery service test', - u'createdDate': datetime.datetime(2015, 11, 4, 23, 57, 28), - u'id': u'h7pbwydho9', - u'name': u'Food Delivery Service'}, - {u'description': u'Created by AWS Lambda', - u'createdDate': datetime.datetime(2015, 11, 4, 17, 55, 41), - u'id': u'i2yyd1ldvj', - u'name': u'LambdaMicroservice'}, - {u'description': u'cloud tap service with combination of API GW and Lambda', - u'createdDate': datetime.datetime(2015, 11, 17, 22, 3, 18), - u'id': u'rm06h9oac4', - u'name': u'API Gateway Cloudtap Service'}, - {u'description': u'testing1234', - u'createdDate': datetime.datetime(2015, 12, 2, 19, 51, 44), - u'id': u'vtir6ssxvd', - u'name': u'testing123'}], - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - expected_items = [{u'description': u'testingabcd', u'createdDate': datetime.datetime(2015, 12, 3, 21, 57, 58), - u'id': u'g41ls77hz0', u'name': u'testing123'}, - {u'description': u'testing1234', u'createdDate': datetime.datetime(2015, 12, 2, 19, 51, 44), - u'id': u'vtir6ssxvd', u'name': u'testing123'}] + """ + self.conn.get_rest_apis.return_value = { + "items": [ + { + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "id": "2ut6i4vyle", + "name": "Swagger Petstore", + }, + { + "description": "testingabcd", + "createdDate": datetime.datetime(2015, 12, 3, 21, 57, 58), + "id": "g41ls77hz0", + "name": "testing123", + }, + { + "description": "a simple food delivery service test", + "createdDate": datetime.datetime(2015, 11, 4, 23, 57, 28), + "id": "h7pbwydho9", + "name": "Food Delivery Service", + }, + { + "description": "Created by AWS Lambda", + "createdDate": datetime.datetime(2015, 11, 4, 17, 55, 41), + "id": "i2yyd1ldvj", + "name": "LambdaMicroservice", + }, + { + "description": "cloud tap service with combination of API GW and Lambda", + "createdDate": datetime.datetime(2015, 11, 17, 22, 3, 18), + "id": "rm06h9oac4", + "name": "API Gateway Cloudtap Service", + }, + { + "description": "testing1234", + "createdDate": datetime.datetime(2015, 12, 2, 19, 51, 44), + "id": "vtir6ssxvd", + "name": "testing123", + }, + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + expected_items = [ + { + "description": "testingabcd", + "createdDate": datetime.datetime(2015, 12, 3, 21, 57, 58), + "id": "g41ls77hz0", + "name": "testing123", + }, + { + "description": "testing1234", + "createdDate": datetime.datetime(2015, 12, 2, 19, 51, 44), + "id": "vtir6ssxvd", + "name": "testing123", + }, + ] - get_apis_result = boto_apigateway.describe_apis(name='testing123', **conn_parameters) - expected_items_dt = [boto_apigateway._convert_datetime_str(item) for item in expected_items] - apis = get_apis_result.get('restapi') - diff = self._diff_list_dicts(apis, expected_items_dt, 'id') + get_apis_result = boto_apigateway.describe_apis( + name="testing123", **conn_parameters + ) + expected_items_dt = [ + boto_apigateway._convert_datetime_str(item) for item in expected_items + ] + apis = get_apis_result.get("restapi") + diff = self._diff_list_dicts(apis, expected_items_dt, "id") self.assertTrue(apis) self.assertIs(diff, False) - def test_that_when_describing_rest_apis_and_name_is_testing123_the_describe_apis_method_returns_no_matching_items(self): - ''' + def test_that_when_describing_rest_apis_and_name_is_testing123_the_describe_apis_method_returns_no_matching_items( + self, + ): + """ Tests that no apis are returned matching 'testing123' - ''' - self.conn.get_rest_apis.return_value = {u'items': [{u'description': u'A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'id': u'2ut6i4vyle', - u'name': u'Swagger Petstore'}, - {u'description': u'a simple food delivery service test', - u'createdDate': datetime.datetime(2015, 11, 4, 23, 57, 28), - u'id': u'h7pbwydho9', - u'name': u'Food Delivery Service'}, - {u'description': u'Created by AWS Lambda', - u'createdDate': datetime.datetime(2015, 11, 4, 17, 55, 41), - u'id': u'i2yyd1ldvj', - u'name': u'LambdaMicroservice'}, - {u'description': u'cloud tap service with combination of API GW and Lambda', - u'createdDate': datetime.datetime(2015, 11, 17, 22, 3, 18), - u'id': u'rm06h9oac4', - u'name': u'API Gateway Cloudtap Service'}], - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - get_apis_result = boto_apigateway.describe_apis(name='testing123', **conn_parameters) - apis = get_apis_result.get('restapi') + """ + self.conn.get_rest_apis.return_value = { + "items": [ + { + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "id": "2ut6i4vyle", + "name": "Swagger Petstore", + }, + { + "description": "a simple food delivery service test", + "createdDate": datetime.datetime(2015, 11, 4, 23, 57, 28), + "id": "h7pbwydho9", + "name": "Food Delivery Service", + }, + { + "description": "Created by AWS Lambda", + "createdDate": datetime.datetime(2015, 11, 4, 17, 55, 41), + "id": "i2yyd1ldvj", + "name": "LambdaMicroservice", + }, + { + "description": "cloud tap service with combination of API GW and Lambda", + "createdDate": datetime.datetime(2015, 11, 17, 22, 3, 18), + "id": "rm06h9oac4", + "name": "API Gateway Cloudtap Service", + }, + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + get_apis_result = boto_apigateway.describe_apis( + name="testing123", **conn_parameters + ) + apis = get_apis_result.get("restapi") self.assertFalse(apis) - def test_that_when_creating_a_rest_api_succeeds_the_create_api_method_returns_true(self): - ''' + def test_that_when_creating_a_rest_api_succeeds_the_create_api_method_returns_true( + self, + ): + """ test True if rest api is created - ''' + """ created_date = datetime.datetime.now() - assigned_api_id = 'created_api_id' - self.conn.create_rest_api.return_value = {u'description': u'unit-testing1234', - u'createdDate': created_date, - u'id': assigned_api_id, - u'name': u'unit-testing123', - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + assigned_api_id = "created_api_id" + self.conn.create_rest_api.return_value = { + "description": "unit-testing1234", + "createdDate": created_date, + "id": assigned_api_id, + "name": "unit-testing123", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } - create_api_result = boto_apigateway.create_api(name='unit-testing123', description='unit-testing1234', **conn_parameters) - api = create_api_result.get('restapi') - self.assertTrue(create_api_result.get('created')) + create_api_result = boto_apigateway.create_api( + name="unit-testing123", description="unit-testing1234", **conn_parameters + ) + api = create_api_result.get("restapi") + self.assertTrue(create_api_result.get("created")) self.assertTrue(api) - self.assertEqual(api['id'], assigned_api_id) - self.assertEqual(api['createdDate'], '{0}'.format(created_date)) - self.assertEqual(api['name'], 'unit-testing123') - self.assertEqual(api['description'], 'unit-testing1234') + self.assertEqual(api["id"], assigned_api_id) + self.assertEqual(api["createdDate"], "{0}".format(created_date)) + self.assertEqual(api["name"], "unit-testing123") + self.assertEqual(api["description"], "unit-testing1234") - def test_that_when_creating_a_rest_api_fails_the_create_api_method_returns_error(self): - ''' + def test_that_when_creating_a_rest_api_fails_the_create_api_method_returns_error( + self, + ): + """ test True for rest api creation error. - ''' - self.conn.create_rest_api.side_effect = ClientError(error_content, 'create_rest_api') - create_api_result = boto_apigateway.create_api(name='unit-testing123', description='unit-testing1234', **conn_parameters) - api = create_api_result.get('restapi') - self.assertEqual(create_api_result.get('error').get('message'), error_message.format('create_rest_api')) + """ + self.conn.create_rest_api.side_effect = ClientError( + error_content, "create_rest_api" + ) + create_api_result = boto_apigateway.create_api( + name="unit-testing123", description="unit-testing1234", **conn_parameters + ) + api = create_api_result.get("restapi") + self.assertEqual( + create_api_result.get("error").get("message"), + error_message.format("create_rest_api"), + ) - def test_that_when_deleting_rest_apis_and_name_is_testing123_matching_two_apis_the_delete_api_method_returns_delete_count_of_two(self): - ''' + def test_that_when_deleting_rest_apis_and_name_is_testing123_matching_two_apis_the_delete_api_method_returns_delete_count_of_two( + self, + ): + """ test True if the deleted count for "testing123" api is 2. - ''' - self.conn.get_rest_apis.return_value = {u'items': [{u'description': u'A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'id': u'2ut6i4vyle', - u'name': u'Swagger Petstore'}, - {u'description': u'testingabcd', - u'createdDate': datetime.datetime(2015, 12, 3, 21, 57, 58), - u'id': u'g41ls77hz0', - u'name': u'testing123'}, - {u'description': u'a simple food delivery service test', - u'createdDate': datetime.datetime(2015, 11, 4, 23, 57, 28), - u'id': u'h7pbwydho9', - u'name': u'Food Delivery Service'}, - {u'description': u'Created by AWS Lambda', - u'createdDate': datetime.datetime(2015, 11, 4, 17, 55, 41), - u'id': u'i2yyd1ldvj', - u'name': u'LambdaMicroservice'}, - {u'description': u'cloud tap service with combination of API GW and Lambda', - u'createdDate': datetime.datetime(2015, 11, 17, 22, 3, 18), - u'id': u'rm06h9oac4', - u'name': u'API Gateway Cloudtap Service'}, - {u'description': u'testing1234', - u'createdDate': datetime.datetime(2015, 12, 2, 19, 51, 44), - u'id': u'vtir6ssxvd', - u'name': u'testing123'}], - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + """ + self.conn.get_rest_apis.return_value = { + "items": [ + { + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "id": "2ut6i4vyle", + "name": "Swagger Petstore", + }, + { + "description": "testingabcd", + "createdDate": datetime.datetime(2015, 12, 3, 21, 57, 58), + "id": "g41ls77hz0", + "name": "testing123", + }, + { + "description": "a simple food delivery service test", + "createdDate": datetime.datetime(2015, 11, 4, 23, 57, 28), + "id": "h7pbwydho9", + "name": "Food Delivery Service", + }, + { + "description": "Created by AWS Lambda", + "createdDate": datetime.datetime(2015, 11, 4, 17, 55, 41), + "id": "i2yyd1ldvj", + "name": "LambdaMicroservice", + }, + { + "description": "cloud tap service with combination of API GW and Lambda", + "createdDate": datetime.datetime(2015, 11, 17, 22, 3, 18), + "id": "rm06h9oac4", + "name": "API Gateway Cloudtap Service", + }, + { + "description": "testing1234", + "createdDate": datetime.datetime(2015, 12, 2, 19, 51, 44), + "id": "vtir6ssxvd", + "name": "testing123", + }, + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } self.conn.delete_rest_api.return_value = None - delete_api_result = boto_apigateway.delete_api(name='testing123', **conn_parameters) + delete_api_result = boto_apigateway.delete_api( + name="testing123", **conn_parameters + ) - self.assertTrue(delete_api_result.get('deleted')) - self.assertEqual(delete_api_result.get('count'), 2) + self.assertTrue(delete_api_result.get("deleted")) + self.assertEqual(delete_api_result.get("count"), 2) - def test_that_when_deleting_rest_apis_and_name_given_provides_no_match_the_delete_api_method_returns_false(self): - ''' + def test_that_when_deleting_rest_apis_and_name_given_provides_no_match_the_delete_api_method_returns_false( + self, + ): + """ Test that the given api name doesn't exists, and delete_api should return deleted status of False - ''' - self.conn.get_rest_apis.return_value = {u'items': [{u'description': u'testing1234', - u'createdDate': datetime.datetime(2015, 12, 2, 19, 51, 44), - u'id': u'vtir6ssxvd', - u'name': u'testing1234'}], - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + """ + self.conn.get_rest_apis.return_value = { + "items": [ + { + "description": "testing1234", + "createdDate": datetime.datetime(2015, 12, 2, 19, 51, 44), + "id": "vtir6ssxvd", + "name": "testing1234", + } + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } self.conn.delete_rest_api.return_value = None - delete_api_result = boto_apigateway.delete_api(name='testing123', **conn_parameters) + delete_api_result = boto_apigateway.delete_api( + name="testing123", **conn_parameters + ) - self.assertFalse(delete_api_result.get('deleted')) + self.assertFalse(delete_api_result.get("deleted")) - def test_that_describing_api_keys_the_describe_api_keys_method_returns_all_api_keys(self): - ''' + def test_that_describing_api_keys_the_describe_api_keys_method_returns_all_api_keys( + self, + ): + """ tests True if all api_keys are returned. - ''' + """ self.conn.get_api_keys.return_value = { - u'items': [{u'description': u'test-lambda-api-key', u'enabled': True, - u'stageKeys': [u'123yd1l123/test'], - u'lastUpdatedDate': datetime.datetime(2015, 11, 4, 19, 22, 18), - u'createdDate': datetime.datetime(2015, 11, 4, 19, 21, 7), - u'id': u'88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - u'name': u'test-salt-key'}, - {u'description': u'testing_salt_123', u'enabled': True, - u'stageKeys': [], - u'lastUpdatedDate': datetime.datetime(2015, 12, 5, 0, 14, 49), - u'createdDate': datetime.datetime(2015, 12, 4, 22, 29, 33), - u'id': u'999999989b8cNSp4505pL6OgDe3oW7oY29Z3eIZ4', - u'name': u'testing_salt'}], - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} + "items": [ + { + "description": "test-lambda-api-key", + "enabled": True, + "stageKeys": ["123yd1l123/test"], + "lastUpdatedDate": datetime.datetime(2015, 11, 4, 19, 22, 18), + "createdDate": datetime.datetime(2015, 11, 4, 19, 21, 7), + "id": "88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", + "name": "test-salt-key", + }, + { + "description": "testing_salt_123", + "enabled": True, + "stageKeys": [], + "lastUpdatedDate": datetime.datetime(2015, 12, 5, 0, 14, 49), + "createdDate": datetime.datetime(2015, 12, 4, 22, 29, 33), + "id": "999999989b8cNSp4505pL6OgDe3oW7oY29Z3eIZ4", + "name": "testing_salt", + }, + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, + } - items = self.conn.get_api_keys.return_value['items'] + items = self.conn.get_api_keys.return_value["items"] get_api_keys_result = boto_apigateway.describe_api_keys(**conn_parameters) items_dt = [boto_apigateway._convert_datetime_str(item) for item in items] - api_keys = get_api_keys_result.get('apiKeys') + api_keys = get_api_keys_result.get("apiKeys") diff = False if len(api_keys) != len(items): diff = True else: # compare individual items. - diff = self._diff_list_dicts(api_keys, items_dt, 'id') + diff = self._diff_list_dicts(api_keys, items_dt, "id") self.assertTrue(api_keys) self.assertIs(diff, False) - def test_that_describing_api_keys_fails_the_desribe_api_keys_method_returns_error(self): - ''' + def test_that_describing_api_keys_fails_the_desribe_api_keys_method_returns_error( + self, + ): + """ test True for describe api keys error. - ''' - self.conn.get_api_keys.side_effect = ClientError(error_content, 'get_api_keys') + """ + self.conn.get_api_keys.side_effect = ClientError(error_content, "get_api_keys") result = boto_apigateway.describe_api_keys(**conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('get_api_keys')) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("get_api_keys") + ) - def test_that_describing_an_api_key_the_describe_api_key_method_returns_matching_api_key(self): - ''' + def test_that_describing_an_api_key_the_describe_api_key_method_returns_matching_api_key( + self, + ): + """ tests True if the key is found. - ''' + """ self.conn.get_api_key.return_value = api_key_ret - result = boto_apigateway.describe_api_key(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - **conn_parameters) - self.assertEqual(result.get('apiKey', {}).get('id'), self.conn.get_api_key.return_value.get('id')) + result = boto_apigateway.describe_api_key( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", **conn_parameters + ) + self.assertEqual( + result.get("apiKey", {}).get("id"), + self.conn.get_api_key.return_value.get("id"), + ) - def test_that_describing_an_api_key_that_does_not_exists_the_desribe_api_key_method_returns_error(self): - ''' + def test_that_describing_an_api_key_that_does_not_exists_the_desribe_api_key_method_returns_error( + self, + ): + """ test True for error being thrown. - ''' - self.conn.get_api_key.side_effect = ClientError(error_content, 'get_api_keys') - result = boto_apigateway.describe_api_key(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('get_api_keys')) + """ + self.conn.get_api_key.side_effect = ClientError(error_content, "get_api_keys") + result = boto_apigateway.describe_api_key( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("get_api_keys") + ) - def test_that_when_creating_an_api_key_succeeds_the_create_api_key_method_returns_true(self): - ''' + def test_that_when_creating_an_api_key_succeeds_the_create_api_key_method_returns_true( + self, + ): + """ tests that we can successfully create an api key and the createDat and lastUpdateDate are converted to string - ''' + """ now = datetime.datetime.now() self.conn.create_api_key.return_value = { - u'description': u'test-lambda-api-key', u'enabled': True, - u'stageKeys': [u'123yd1l123/test'], - u'lastUpdatedDate': now, - u'createdDate': now, - u'id': u'88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - u'name': u'test-salt-key', - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} + "description": "test-lambda-api-key", + "enabled": True, + "stageKeys": ["123yd1l123/test"], + "lastUpdatedDate": now, + "createdDate": now, + "id": "88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", + "name": "test-salt-key", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, + } - create_api_key_result = boto_apigateway.create_api_key('test-salt-key', 'test-lambda-api-key', **conn_parameters) - api_key = create_api_key_result.get('apiKey') - now_str = '{0}'.format(now) + create_api_key_result = boto_apigateway.create_api_key( + "test-salt-key", "test-lambda-api-key", **conn_parameters + ) + api_key = create_api_key_result.get("apiKey") + now_str = "{0}".format(now) - self.assertTrue(create_api_key_result.get('created')) - self.assertEqual(api_key.get('lastUpdatedDate'), now_str) - self.assertEqual(api_key.get('createdDate'), now_str) + self.assertTrue(create_api_key_result.get("created")) + self.assertEqual(api_key.get("lastUpdatedDate"), now_str) + self.assertEqual(api_key.get("createdDate"), now_str) - def test_that_when_creating_an_api_key_fails_the_create_api_key_method_returns_error(self): - ''' + def test_that_when_creating_an_api_key_fails_the_create_api_key_method_returns_error( + self, + ): + """ tests that we properly handle errors when create an api key fails. - ''' + """ - self.conn.create_api_key.side_effect = ClientError(error_content, 'create_api_key') - create_api_key_result = boto_apigateway.create_api_key('test-salt-key', 'unit-testing1234') - api_key = create_api_key_result.get('apiKey') + self.conn.create_api_key.side_effect = ClientError( + error_content, "create_api_key" + ) + create_api_key_result = boto_apigateway.create_api_key( + "test-salt-key", "unit-testing1234" + ) + api_key = create_api_key_result.get("apiKey") self.assertFalse(api_key) - self.assertIs(create_api_key_result.get('created'), False) - self.assertEqual(create_api_key_result.get('error').get('message'), error_message.format('create_api_key')) + self.assertIs(create_api_key_result.get("created"), False) + self.assertEqual( + create_api_key_result.get("error").get("message"), + error_message.format("create_api_key"), + ) - def test_that_when_deleting_an_api_key_that_exists_the_delete_api_key_method_returns_true(self): - ''' + def test_that_when_deleting_an_api_key_that_exists_the_delete_api_key_method_returns_true( + self, + ): + """ test True if the api key is successfully deleted. - ''' - self.conn.delete_api_key.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.delete_api_key(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', **conn_parameters) + """ + self.conn.delete_api_key.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } + result = boto_apigateway.delete_api_key( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", **conn_parameters + ) - self.assertTrue(result.get('deleted')) + self.assertTrue(result.get("deleted")) - def test_that_when_deleting_an_api_key_that_does_not_exist_the_delete_api_key_method_returns_false(self): - ''' + def test_that_when_deleting_an_api_key_that_does_not_exist_the_delete_api_key_method_returns_false( + self, + ): + """ Test that the given api key doesn't exists, and delete_api_key should return deleted status of False - ''' - self.conn.delete_api_key.side_effect = ClientError(error_content, 'delete_api_key') - result = boto_apigateway.delete_api_key(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', **conn_parameters) + """ + self.conn.delete_api_key.side_effect = ClientError( + error_content, "delete_api_key" + ) + result = boto_apigateway.delete_api_key( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", **conn_parameters + ) - self.assertFalse(result.get('deleted')) + self.assertFalse(result.get("deleted")) - def test_that_when_updating_an_api_key_description_successfully_the_update_api_key_description_method_returns_true(self): - ''' + def test_that_when_updating_an_api_key_description_successfully_the_update_api_key_description_method_returns_true( + self, + ): + """ Test True if api key descriptipn update is successful - ''' + """ self.conn.update_api_key.return_value = api_key_ret - result = boto_apigateway.update_api_key_description(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - description='test-lambda-api-key', **conn_parameters) - self.assertTrue(result.get('updated')) + result = boto_apigateway.update_api_key_description( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", + description="test-lambda-api-key", + **conn_parameters + ) + self.assertTrue(result.get("updated")) - def test_that_when_updating_an_api_key_description_for_a_key_that_does_not_exist_the_update_api_key_description_method_returns_false(self): - ''' + def test_that_when_updating_an_api_key_description_for_a_key_that_does_not_exist_the_update_api_key_description_method_returns_false( + self, + ): + """ Test False if api key doesn't exists for the update request - ''' - self.conn.update_api_key.side_effect = ClientError(error_content, 'update_api_key') - result = boto_apigateway.update_api_key_description(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - description='test-lambda-api-key', **conn_parameters) - self.assertFalse(result.get('updated')) + """ + self.conn.update_api_key.side_effect = ClientError( + error_content, "update_api_key" + ) + result = boto_apigateway.update_api_key_description( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", + description="test-lambda-api-key", + **conn_parameters + ) + self.assertFalse(result.get("updated")) - def test_that_when_enabling_an_api_key_that_exists_the_enable_api_key_method_returns_api_key(self): - ''' + def test_that_when_enabling_an_api_key_that_exists_the_enable_api_key_method_returns_api_key( + self, + ): + """ Test True for the status of the enabled flag of the returned api key - ''' + """ self.conn.update_api_key.return_value = api_key_ret - result = boto_apigateway.enable_api_key(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - **conn_parameters) - self.assertTrue(result.get('apiKey', {}).get('enabled')) + result = boto_apigateway.enable_api_key( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", **conn_parameters + ) + self.assertTrue(result.get("apiKey", {}).get("enabled")) - def test_that_when_enabling_an_api_key_that_does_not_exist_the_enable_api_key_method_returns_error(self): - ''' + def test_that_when_enabling_an_api_key_that_does_not_exist_the_enable_api_key_method_returns_error( + self, + ): + """ Test Equality of the returned value of 'erorr' - ''' - self.conn.update_api_key.side_effect = ClientError(error_content, 'update_api_key') - result = boto_apigateway.enable_api_key(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('update_api_key')) + """ + self.conn.update_api_key.side_effect = ClientError( + error_content, "update_api_key" + ) + result = boto_apigateway.enable_api_key( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("update_api_key") + ) - def test_that_when_disabling_an_api_key_that_exists_the_disable_api_key_method_returns_api_key(self): - ''' + def test_that_when_disabling_an_api_key_that_exists_the_disable_api_key_method_returns_api_key( + self, + ): + """ Test False for the status of the enabled flag of the returned api key - ''' + """ self.conn.update_api_key.return_value = api_key_ret.copy() - self.conn.update_api_key.return_value['enabled'] = False - result = boto_apigateway.disable_api_key(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - **conn_parameters) - self.assertFalse(result.get('apiKey', {}).get('enabled')) + self.conn.update_api_key.return_value["enabled"] = False + result = boto_apigateway.disable_api_key( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", **conn_parameters + ) + self.assertFalse(result.get("apiKey", {}).get("enabled")) - def test_that_when_disabling_an_api_key_that_does_not_exist_the_disable_api_key_method_returns_error(self): - ''' + def test_that_when_disabling_an_api_key_that_does_not_exist_the_disable_api_key_method_returns_error( + self, + ): + """ Test Equality of the returned value of 'erorr' - ''' - self.conn.update_api_key.side_effect = ClientError(error_content, 'update_api_key') - result = boto_apigateway.disable_api_key(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('update_api_key')) + """ + self.conn.update_api_key.side_effect = ClientError( + error_content, "update_api_key" + ) + result = boto_apigateway.disable_api_key( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("update_api_key") + ) - def test_that_when_associating_stages_to_an_api_key_that_exists_the_associate_api_key_stagekeys_method_returns_true(self): - ''' + def test_that_when_associating_stages_to_an_api_key_that_exists_the_associate_api_key_stagekeys_method_returns_true( + self, + ): + """ Test True for returned value of 'associated' - ''' + """ self.conn.update_api_key.retuen_value = api_key_ret - result = boto_apigateway.associate_api_key_stagekeys(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - stagekeyslist=[u'123yd1l123/test'], - **conn_parameters) - self.assertTrue(result.get('associated')) + result = boto_apigateway.associate_api_key_stagekeys( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", + stagekeyslist=["123yd1l123/test"], + **conn_parameters + ) + self.assertTrue(result.get("associated")) - def test_that_when_associating_stages_to_an_api_key_that_does_not_exist_the_associate_api_key_stagekeys_method_returns_false(self): - ''' + def test_that_when_associating_stages_to_an_api_key_that_does_not_exist_the_associate_api_key_stagekeys_method_returns_false( + self, + ): + """ Test False returned value of 'associated' - ''' - self.conn.update_api_key.side_effect = ClientError(error_content, 'update_api_key') - result = boto_apigateway.associate_api_key_stagekeys(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - stagekeyslist=[u'123yd1l123/test'], - **conn_parameters) - self.assertFalse(result.get('associated')) + """ + self.conn.update_api_key.side_effect = ClientError( + error_content, "update_api_key" + ) + result = boto_apigateway.associate_api_key_stagekeys( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", + stagekeyslist=["123yd1l123/test"], + **conn_parameters + ) + self.assertFalse(result.get("associated")) - def test_that_when_disassociating_stages_to_an_api_key_that_exists_the_disassociate_api_key_stagekeys_method_returns_true(self): - ''' + def test_that_when_disassociating_stages_to_an_api_key_that_exists_the_disassociate_api_key_stagekeys_method_returns_true( + self, + ): + """ Test True for returned value of 'associated' - ''' + """ self.conn.update_api_key.retuen_value = None - result = boto_apigateway.disassociate_api_key_stagekeys(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - stagekeyslist=[u'123yd1l123/test'], - **conn_parameters) - self.assertTrue(result.get('disassociated')) + result = boto_apigateway.disassociate_api_key_stagekeys( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", + stagekeyslist=["123yd1l123/test"], + **conn_parameters + ) + self.assertTrue(result.get("disassociated")) - def test_that_when_disassociating_stages_to_an_api_key_that_does_not_exist_the_disassociate_api_key_stagekeys_method_returns_false(self): - ''' + def test_that_when_disassociating_stages_to_an_api_key_that_does_not_exist_the_disassociate_api_key_stagekeys_method_returns_false( + self, + ): + """ Test False returned value of 'associated' - ''' - self.conn.update_api_key.side_effect = ClientError(error_content, 'update_api_key') - result = boto_apigateway.disassociate_api_key_stagekeys(apiKey='88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2', - stagekeyslist=[u'123yd1l123/test'], - **conn_parameters) - self.assertFalse(result.get('disassociated')) + """ + self.conn.update_api_key.side_effect = ClientError( + error_content, "update_api_key" + ) + result = boto_apigateway.disassociate_api_key_stagekeys( + apiKey="88883333amaa1ZMVGCoLeaTrQk8kzOC36vCgRcT2", + stagekeyslist=["123yd1l123/test"], + **conn_parameters + ) + self.assertFalse(result.get("disassociated")) - def test_that_when_describing_api_deployments_the_describe_api_deployments_method_returns_list_of_deployments(self): - ''' + def test_that_when_describing_api_deployments_the_describe_api_deployments_method_returns_list_of_deployments( + self, + ): + """ Test Equality for number of deployments is 2 - ''' - self.conn.get_deployments.return_value = {u'items': [{u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'id': u'n05smo'}, - {u'createdDate': datetime.datetime(2015, 12, 2, 19, 51, 44), - u'id': u'n05sm1'}], - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.describe_api_deployments(restApiId='rm06h9oac4', **conn_parameters) - self.assertEqual(len(result.get('deployments', {})), 2) + """ + self.conn.get_deployments.return_value = { + "items": [ + { + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "id": "n05smo", + }, + { + "createdDate": datetime.datetime(2015, 12, 2, 19, 51, 44), + "id": "n05sm1", + }, + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + result = boto_apigateway.describe_api_deployments( + restApiId="rm06h9oac4", **conn_parameters + ) + self.assertEqual(len(result.get("deployments", {})), 2) - def test_that_when_describing_api_deployments_and_an_error_occurred_the_describe_api_deployments_method_returns_error(self): - ''' + def test_that_when_describing_api_deployments_and_an_error_occurred_the_describe_api_deployments_method_returns_error( + self, + ): + """ Test Equality of error returned - ''' - self.conn.get_deployments.side_effect = ClientError(error_content, 'get_deployments') - result = boto_apigateway.describe_api_deployments(restApiId='rm06h9oac4', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_deployments')) + """ + self.conn.get_deployments.side_effect = ClientError( + error_content, "get_deployments" + ) + result = boto_apigateway.describe_api_deployments( + restApiId="rm06h9oac4", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_deployments") + ) - def test_that_when_describing_an_api_deployment_the_describe_api_deployment_method_returns_the_deployment(self): - ''' + def test_that_when_describing_an_api_deployment_the_describe_api_deployment_method_returns_the_deployment( + self, + ): + """ Test True for the returned deployment - ''' - self.conn.get_deployment.return_value = {u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'id': u'n05smo', - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.describe_api_deployment(restApiId='rm06h9oac4', deploymentId='n05smo', **conn_parameters) - self.assertTrue(result.get('deployment')) + """ + self.conn.get_deployment.return_value = { + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "id": "n05smo", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + result = boto_apigateway.describe_api_deployment( + restApiId="rm06h9oac4", deploymentId="n05smo", **conn_parameters + ) + self.assertTrue(result.get("deployment")) - def test_that_when_describing_api_deployment_that_does_not_exist_the_describe_api_deployment_method_returns_error(self): - ''' + def test_that_when_describing_api_deployment_that_does_not_exist_the_describe_api_deployment_method_returns_error( + self, + ): + """ Test Equality of error returned - ''' - self.conn.get_deployment.side_effect = ClientError(error_content, 'get_deployment') - result = boto_apigateway.describe_api_deployment(restApiId='rm06h9oac4', deploymentId='n05smo', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_deployment')) + """ + self.conn.get_deployment.side_effect = ClientError( + error_content, "get_deployment" + ) + result = boto_apigateway.describe_api_deployment( + restApiId="rm06h9oac4", deploymentId="n05smo", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_deployment") + ) - def test_that_when_activating_api_deployment_for_stage_and_deployment_that_exist_the_activate_api_deployment_method_returns_true(self): - ''' + def test_that_when_activating_api_deployment_for_stage_and_deployment_that_exist_the_activate_api_deployment_method_returns_true( + self, + ): + """ Test True for value of 'set' - ''' - self.conn.update_stage.return_value = {u'cacheClusterEnabled': False, - u'cacheClusterStatus': 'NOT_AVAAILABLE', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'deploymentId': 'n05smo', - u'description': 'test', - u'lastUpdatedDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'stageName': 'test', - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.activate_api_deployment(restApiId='rm06h9oac4', stageName='test', deploymentId='n05smo', - **conn_parameters) - self.assertTrue(result.get('set')) + """ + self.conn.update_stage.return_value = { + "cacheClusterEnabled": False, + "cacheClusterStatus": "NOT_AVAAILABLE", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "deploymentId": "n05smo", + "description": "test", + "lastUpdatedDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "stageName": "test", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + result = boto_apigateway.activate_api_deployment( + restApiId="rm06h9oac4", + stageName="test", + deploymentId="n05smo", + **conn_parameters + ) + self.assertTrue(result.get("set")) - def test_that_when_activating_api_deployment_for_stage_that_does_not_exist_the_activate_api_deployment_method_returns_false(self): - ''' + def test_that_when_activating_api_deployment_for_stage_that_does_not_exist_the_activate_api_deployment_method_returns_false( + self, + ): + """ Test False for value of 'set' - ''' - self.conn.update_stage.side_effect = ClientError(error_content, 'update_stage') - result = boto_apigateway.activate_api_deployment(restApiId='rm06h9oac4', stageName='test', deploymentId='n05smo', - **conn_parameters) - self.assertFalse(result.get('set')) + """ + self.conn.update_stage.side_effect = ClientError(error_content, "update_stage") + result = boto_apigateway.activate_api_deployment( + restApiId="rm06h9oac4", + stageName="test", + deploymentId="n05smo", + **conn_parameters + ) + self.assertFalse(result.get("set")) - def test_that_when_creating_an_api_deployment_succeeds_the_create_api_deployment_method_returns_true(self): - ''' + def test_that_when_creating_an_api_deployment_succeeds_the_create_api_deployment_method_returns_true( + self, + ): + """ tests that we can successfully create an api deployment and the createDate is converted to string - ''' + """ now = datetime.datetime.now() self.conn.create_deployment.return_value = { - u'description': u'test-lambda-api-key', - u'id': 'n05smo', - u'createdDate': now, - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} + "description": "test-lambda-api-key", + "id": "n05smo", + "createdDate": now, + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, + } - result = boto_apigateway.create_api_deployment(restApiId='rm06h9oac4', stageName='test', **conn_parameters) - deployment = result.get('deployment') - now_str = '{0}'.format(now) + result = boto_apigateway.create_api_deployment( + restApiId="rm06h9oac4", stageName="test", **conn_parameters + ) + deployment = result.get("deployment") + now_str = "{0}".format(now) - self.assertTrue(result.get('created')) - self.assertEqual(deployment.get('createdDate'), now_str) + self.assertTrue(result.get("created")) + self.assertEqual(deployment.get("createdDate"), now_str) - def test_that_when_creating_an_deployment_fails_the_create_api_deployment_method_returns_error(self): - ''' + def test_that_when_creating_an_deployment_fails_the_create_api_deployment_method_returns_error( + self, + ): + """ tests that we properly handle errors when create an api deployment fails. - ''' + """ - self.conn.create_deployment.side_effect = ClientError(error_content, 'create_deployment') - result = boto_apigateway.create_api_deployment(restApiId='rm06h9oac4', stageName='test', **conn_parameters) - self.assertIs(result.get('created'), False) - self.assertEqual(result.get('error').get('message'), error_message.format('create_deployment')) + self.conn.create_deployment.side_effect = ClientError( + error_content, "create_deployment" + ) + result = boto_apigateway.create_api_deployment( + restApiId="rm06h9oac4", stageName="test", **conn_parameters + ) + self.assertIs(result.get("created"), False) + self.assertEqual( + result.get("error").get("message"), + error_message.format("create_deployment"), + ) - def test_that_when_deleting_an_api_deployment_that_exists_the_delete_api_deployment_method_returns_true(self): - ''' + def test_that_when_deleting_an_api_deployment_that_exists_the_delete_api_deployment_method_returns_true( + self, + ): + """ test True if the api deployment is successfully deleted. - ''' - self.conn.delete_deployment.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.delete_api_deployment(restApiId='rm06h9oac4', deploymentId='n05smo', **conn_parameters) - self.assertTrue(result.get('deleted')) + """ + self.conn.delete_deployment.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } + result = boto_apigateway.delete_api_deployment( + restApiId="rm06h9oac4", deploymentId="n05smo", **conn_parameters + ) + self.assertTrue(result.get("deleted")) - def test_that_when_deleting_an_api_deployment_that_does_not_exist_the_delete_api_deployment_method_returns_false(self): - ''' + def test_that_when_deleting_an_api_deployment_that_does_not_exist_the_delete_api_deployment_method_returns_false( + self, + ): + """ Test that the given api deployment doesn't exists, and delete_api_deployment should return deleted status of False - ''' - self.conn.delete_deployment.side_effect = ClientError(error_content, 'delete_deployment') - result = boto_apigateway.delete_api_deployment(restApiId='rm06h9oac4', deploymentId='n05smo1', **conn_parameters) - self.assertFalse(result.get('deleted')) + """ + self.conn.delete_deployment.side_effect = ClientError( + error_content, "delete_deployment" + ) + result = boto_apigateway.delete_api_deployment( + restApiId="rm06h9oac4", deploymentId="n05smo1", **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_describing_api_stages_the_describe_api_stages_method_returns_list_of_stages(self): - ''' + def test_that_when_describing_api_stages_the_describe_api_stages_method_returns_list_of_stages( + self, + ): + """ Test Equality for number of stages for the given deployment is 2 - ''' - self.conn.get_stages.return_value = {u'item': [{u'cacheClusterEnabled': False, - u'cacheClusterStatus': 'NOT_AVAILABLE', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'deploymentId': u'n05smo', - u'description': u'test', - u'lastUpdatedDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'stageName': u'test'}, - {u'cacheClusterEnabled': False, - u'cacheClusterStatus': 'NOT_AVAILABLE', - u'createdDate': datetime.datetime(2015, 12, 17, 16, 33, 50), - u'deploymentId': u'n05smo', - u'description': u'dev', - u'lastUpdatedDate': datetime.datetime(2015, 12, 17, 16, 33, 50), - u'stageName': u'dev'}], - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.describe_api_stages(restApiId='rm06h9oac4', deploymentId='n05smo', **conn_parameters) - self.assertEqual(len(result.get('stages', {})), 2) + """ + self.conn.get_stages.return_value = { + "item": [ + { + "cacheClusterEnabled": False, + "cacheClusterStatus": "NOT_AVAILABLE", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "deploymentId": "n05smo", + "description": "test", + "lastUpdatedDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "stageName": "test", + }, + { + "cacheClusterEnabled": False, + "cacheClusterStatus": "NOT_AVAILABLE", + "createdDate": datetime.datetime(2015, 12, 17, 16, 33, 50), + "deploymentId": "n05smo", + "description": "dev", + "lastUpdatedDate": datetime.datetime(2015, 12, 17, 16, 33, 50), + "stageName": "dev", + }, + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + result = boto_apigateway.describe_api_stages( + restApiId="rm06h9oac4", deploymentId="n05smo", **conn_parameters + ) + self.assertEqual(len(result.get("stages", {})), 2) - def test_that_when_describing_api_stages_and_that_the_deployment_does_not_exist_the_describe_api_stages_method_returns_error(self): - ''' + def test_that_when_describing_api_stages_and_that_the_deployment_does_not_exist_the_describe_api_stages_method_returns_error( + self, + ): + """ Test Equality of error returned - ''' - self.conn.get_stages.side_effect = ClientError(error_content, 'get_stages') - result = boto_apigateway.describe_api_stages(restApiId='rm06h9oac4', deploymentId='n05smo', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_stages')) + """ + self.conn.get_stages.side_effect = ClientError(error_content, "get_stages") + result = boto_apigateway.describe_api_stages( + restApiId="rm06h9oac4", deploymentId="n05smo", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_stages") + ) - def test_that_when_describing_an_api_stage_the_describe_api_stage_method_returns_the_stage(self): - ''' + def test_that_when_describing_an_api_stage_the_describe_api_stage_method_returns_the_stage( + self, + ): + """ Test True for the returned stage - ''' - self.conn.get_stage.return_value = {u'cacheClusterEnabled': False, - u'cacheClusterStatus': 'NOT_AVAILABLE', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'deploymentId': u'n05smo', - u'description': u'test', - u'lastUpdatedDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'stageName': u'test', - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.describe_api_stage(restApiId='rm06h9oac4', stageName='test', **conn_parameters) - self.assertTrue(result.get('stage')) + """ + self.conn.get_stage.return_value = { + "cacheClusterEnabled": False, + "cacheClusterStatus": "NOT_AVAILABLE", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "deploymentId": "n05smo", + "description": "test", + "lastUpdatedDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "stageName": "test", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + result = boto_apigateway.describe_api_stage( + restApiId="rm06h9oac4", stageName="test", **conn_parameters + ) + self.assertTrue(result.get("stage")) - def test_that_when_describing_api_stage_that_does_not_exist_the_describe_api_stage_method_returns_error(self): - ''' + def test_that_when_describing_api_stage_that_does_not_exist_the_describe_api_stage_method_returns_error( + self, + ): + """ Test Equality of error returned - ''' - self.conn.get_stage.side_effect = ClientError(error_content, 'get_stage') - result = boto_apigateway.describe_api_stage(restApiId='rm06h9oac4', stageName='no_such_stage', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_stage')) + """ + self.conn.get_stage.side_effect = ClientError(error_content, "get_stage") + result = boto_apigateway.describe_api_stage( + restApiId="rm06h9oac4", stageName="no_such_stage", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_stage") + ) - def test_that_when_overwriting_stage_variables_to_an_existing_stage_the_overwrite_api_stage_variables_method_returns_the_updated_stage(self): - ''' + def test_that_when_overwriting_stage_variables_to_an_existing_stage_the_overwrite_api_stage_variables_method_returns_the_updated_stage( + self, + ): + """ Test True for the returned stage - ''' - self.conn.get_stage.return_value = {u'cacheClusterEnabled': False, - u'cacheClusterStatus': 'NOT_AVAILABLE', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'deploymentId': u'n05smo', - u'description': u'test', - u'lastUpdatedDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'stageName': u'test', - u'variables': {'key1': 'val1'}, - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - self.conn.update_stage.return_value = {u'cacheClusterEnabled': False, - u'cacheClusterStatus': 'NOT_AVAILABLE', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'deploymentId': u'n05smo', - u'description': u'test', - u'lastUpdatedDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'stageName': u'test', - u'variables': {'key1': 'val2'}, - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.overwrite_api_stage_variables(restApiId='rm06h9oac4', stageName='test', - variables=dict(key1='val2'), **conn_parameters) - self.assertEqual(result.get('stage').get('variables').get('key1'), 'val2') + """ + self.conn.get_stage.return_value = { + "cacheClusterEnabled": False, + "cacheClusterStatus": "NOT_AVAILABLE", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "deploymentId": "n05smo", + "description": "test", + "lastUpdatedDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "stageName": "test", + "variables": {"key1": "val1"}, + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + self.conn.update_stage.return_value = { + "cacheClusterEnabled": False, + "cacheClusterStatus": "NOT_AVAILABLE", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "deploymentId": "n05smo", + "description": "test", + "lastUpdatedDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "stageName": "test", + "variables": {"key1": "val2"}, + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + result = boto_apigateway.overwrite_api_stage_variables( + restApiId="rm06h9oac4", + stageName="test", + variables=dict(key1="val2"), + **conn_parameters + ) + self.assertEqual(result.get("stage").get("variables").get("key1"), "val2") - def test_that_when_overwriting_stage_variables_to_a_nonexisting_stage_the_overwrite_api_stage_variables_method_returns_error(self): - ''' + def test_that_when_overwriting_stage_variables_to_a_nonexisting_stage_the_overwrite_api_stage_variables_method_returns_error( + self, + ): + """ Test Equality of error returned - ''' - self.conn.get_stage.side_effect = ClientError(error_content, 'get_stage') - result = boto_apigateway.overwrite_api_stage_variables(restApiId='rm06h9oac4', stageName='no_such_stage', - variables=dict(key1="val1", key2="val2"), **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_stage')) + """ + self.conn.get_stage.side_effect = ClientError(error_content, "get_stage") + result = boto_apigateway.overwrite_api_stage_variables( + restApiId="rm06h9oac4", + stageName="no_such_stage", + variables=dict(key1="val1", key2="val2"), + **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_stage") + ) - def test_that_when_overwriting_stage_variables_to_an_existing_stage_the_overwrite_api_stage_variables_method_returns_error(self): - ''' + def test_that_when_overwriting_stage_variables_to_an_existing_stage_the_overwrite_api_stage_variables_method_returns_error( + self, + ): + """ Test Equality of error returned due to update_stage - ''' - self.conn.get_stage.return_value = {u'cacheClusterEnabled': False, - u'cacheClusterStatus': 'NOT_AVAILABLE', - u'createdDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'deploymentId': u'n05smo', - u'description': u'test', - u'lastUpdatedDate': datetime.datetime(2015, 11, 17, 16, 33, 50), - u'stageName': u'test', - u'variables': {'key1': 'val1'}, - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - self.conn.update_stage.side_effect = ClientError(error_content, 'update_stage') - result = boto_apigateway.overwrite_api_stage_variables(restApiId='rm06h9oac4', stageName='test', - variables=dict(key1='val2'), **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('update_stage')) + """ + self.conn.get_stage.return_value = { + "cacheClusterEnabled": False, + "cacheClusterStatus": "NOT_AVAILABLE", + "createdDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "deploymentId": "n05smo", + "description": "test", + "lastUpdatedDate": datetime.datetime(2015, 11, 17, 16, 33, 50), + "stageName": "test", + "variables": {"key1": "val1"}, + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + self.conn.update_stage.side_effect = ClientError(error_content, "update_stage") + result = boto_apigateway.overwrite_api_stage_variables( + restApiId="rm06h9oac4", + stageName="test", + variables=dict(key1="val2"), + **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("update_stage") + ) - def test_that_when_creating_an_api_stage_succeeds_the_create_api_stage_method_returns_true(self): - ''' + def test_that_when_creating_an_api_stage_succeeds_the_create_api_stage_method_returns_true( + self, + ): + """ tests that we can successfully create an api stage and the createDate is converted to string - ''' + """ now = datetime.datetime.now() - self.conn.create_stage.return_value = {u'cacheClusterEnabled': False, - u'cacheClusterStatus': 'NOT_AVAILABLE', - u'createdDate': now, - u'deploymentId': u'n05smo', - u'description': u'test', - u'lastUpdatedDate': now, - u'stageName': u'test', - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + self.conn.create_stage.return_value = { + "cacheClusterEnabled": False, + "cacheClusterStatus": "NOT_AVAILABLE", + "createdDate": now, + "deploymentId": "n05smo", + "description": "test", + "lastUpdatedDate": now, + "stageName": "test", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } - result = boto_apigateway.create_api_stage(restApiId='rm06h9oac4', stageName='test', deploymentId='n05smo', - **conn_parameters) - stage = result.get('stage') - now_str = '{0}'.format(now) - self.assertIs(result.get('created'), True) - self.assertEqual(stage.get('createdDate'), now_str) - self.assertEqual(stage.get('lastUpdatedDate'), now_str) + result = boto_apigateway.create_api_stage( + restApiId="rm06h9oac4", + stageName="test", + deploymentId="n05smo", + **conn_parameters + ) + stage = result.get("stage") + now_str = "{0}".format(now) + self.assertIs(result.get("created"), True) + self.assertEqual(stage.get("createdDate"), now_str) + self.assertEqual(stage.get("lastUpdatedDate"), now_str) - def test_that_when_creating_an_api_stage_fails_the_create_api_stage_method_returns_error(self): - ''' + def test_that_when_creating_an_api_stage_fails_the_create_api_stage_method_returns_error( + self, + ): + """ tests that we properly handle errors when create an api stage fails. - ''' + """ - self.conn.create_stage.side_effect = ClientError(error_content, 'create_stage') - result = boto_apigateway.create_api_stage(restApiId='rm06h9oac4', stageName='test', deploymentId='n05smo', - **conn_parameters) - self.assertIs(result.get('created'), False) - self.assertEqual(result.get('error').get('message'), error_message.format('create_stage')) + self.conn.create_stage.side_effect = ClientError(error_content, "create_stage") + result = boto_apigateway.create_api_stage( + restApiId="rm06h9oac4", + stageName="test", + deploymentId="n05smo", + **conn_parameters + ) + self.assertIs(result.get("created"), False) + self.assertEqual( + result.get("error").get("message"), error_message.format("create_stage") + ) - def test_that_when_deleting_an_api_stage_that_exists_the_delete_api_stage_method_returns_true(self): - ''' + def test_that_when_deleting_an_api_stage_that_exists_the_delete_api_stage_method_returns_true( + self, + ): + """ test True if the api stage is successfully deleted. - ''' - self.conn.delete_stage.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.delete_api_stage(restApiId='rm06h9oac4', stageName='test', **conn_parameters) - self.assertTrue(result.get('deleted')) + """ + self.conn.delete_stage.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } + result = boto_apigateway.delete_api_stage( + restApiId="rm06h9oac4", stageName="test", **conn_parameters + ) + self.assertTrue(result.get("deleted")) - def test_that_when_deleting_an_api_stage_that_does_not_exist_the_delete_api_stage_method_returns_false(self): - ''' + def test_that_when_deleting_an_api_stage_that_does_not_exist_the_delete_api_stage_method_returns_false( + self, + ): + """ Test that the given api stage doesn't exists, and delete_api_stage should return deleted status of False - ''' - self.conn.delete_stage.side_effect = ClientError(error_content, 'delete_stage') - result = boto_apigateway.delete_api_stage(restApiId='rm06h9oac4', stageName='no_such_stage', **conn_parameters) - self.assertFalse(result.get('deleted')) + """ + self.conn.delete_stage.side_effect = ClientError(error_content, "delete_stage") + result = boto_apigateway.delete_api_stage( + restApiId="rm06h9oac4", stageName="no_such_stage", **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_flushing_api_stage_cache_for_an_existing_stage_the_flush_api_stage_cache_method_returns_true(self): - ''' + def test_that_when_flushing_api_stage_cache_for_an_existing_stage_the_flush_api_stage_cache_method_returns_true( + self, + ): + """ Test True for 'flushed' - ''' - self.conn.flush_stage_cache.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.flush_api_stage_cache(restApiId='rm06h9oac4', stageName='no_such_stage', **conn_parameters) - self.assertTrue(result.get('flushed')) + """ + self.conn.flush_stage_cache.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } + result = boto_apigateway.flush_api_stage_cache( + restApiId="rm06h9oac4", stageName="no_such_stage", **conn_parameters + ) + self.assertTrue(result.get("flushed")) - def test_that_when_flushing_api_stage_cache_and_the_stage_does_not_exist_the_flush_api_stage_cache_method_returns_false(self): - ''' + def test_that_when_flushing_api_stage_cache_and_the_stage_does_not_exist_the_flush_api_stage_cache_method_returns_false( + self, + ): + """ Test False for 'flushed' - ''' - self.conn.flush_stage_cache.side_effect = ClientError(error_content, 'flush_stage_cache') - result = boto_apigateway.flush_api_stage_cache(restApiId='rm06h9oac4', stageName='no_such_stage', **conn_parameters) - self.assertFalse(result.get('flushed')) + """ + self.conn.flush_stage_cache.side_effect = ClientError( + error_content, "flush_stage_cache" + ) + result = boto_apigateway.flush_api_stage_cache( + restApiId="rm06h9oac4", stageName="no_such_stage", **conn_parameters + ) + self.assertFalse(result.get("flushed")) - def test_that_when_describing_api_models_the_describe_api_models_method_returns_list_of_models(self): - ''' + def test_that_when_describing_api_models_the_describe_api_models_method_returns_list_of_models( + self, + ): + """ Test Equality for number of models for the given api is 2 - ''' - self.conn.get_models.return_value = {u'items': [{u'contentType': u'application/json', - u'name': u'Error', - u'description': u'Error Model', - u'id': u'iltqcb', - u'schema': u'{"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"},"fields":{"type":"string"}},"definitions":{}}'}, - {u'contentType': u'application/json', - u'name': u'User', - u'description': u'User Model', - u'id': u'iltqcc', - u'schema': u'{"properties":{"username":{"type":"string","description":"A unique username for the user"},"password":{"type":"string","description":"A password for the new user"}},"definitions":{}}'}], - 'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.describe_api_models(restApiId='rm06h9oac4', **conn_parameters) - self.assertEqual(len(result.get('models', {})), 2) + """ + self.conn.get_models.return_value = { + "items": [ + { + "contentType": "application/json", + "name": "Error", + "description": "Error Model", + "id": "iltqcb", + "schema": '{"properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"},"fields":{"type":"string"}},"definitions":{}}', + }, + { + "contentType": "application/json", + "name": "User", + "description": "User Model", + "id": "iltqcc", + "schema": '{"properties":{"username":{"type":"string","description":"A unique username for the user"},"password":{"type":"string","description":"A password for the new user"}},"definitions":{}}', + }, + ], + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + }, + } + result = boto_apigateway.describe_api_models( + restApiId="rm06h9oac4", **conn_parameters + ) + self.assertEqual(len(result.get("models", {})), 2) - def test_that_when_describing_api_models_and_that_the_api_does_not_exist_the_describe_api_models_method_returns_error(self): - ''' + def test_that_when_describing_api_models_and_that_the_api_does_not_exist_the_describe_api_models_method_returns_error( + self, + ): + """ Test Equality of error returned - ''' - self.conn.get_models.side_effect = ClientError(error_content, 'get_models') - result = boto_apigateway.describe_api_models(restApiId='rm06h9oac4', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_models')) + """ + self.conn.get_models.side_effect = ClientError(error_content, "get_models") + result = boto_apigateway.describe_api_models( + restApiId="rm06h9oac4", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_models") + ) - def test_that_when_describing_api_model_the_describe_api_model_method_returns_the_model(self): - ''' + def test_that_when_describing_api_model_the_describe_api_model_method_returns_the_model( + self, + ): + """ Test True for the returned stage - ''' + """ self.conn.get_model.return_value = api_model_ret - result = boto_apigateway.describe_api_model(restApiId='rm06h9oac4', modelName='Error', **conn_parameters) - self.assertTrue(result.get('model')) + result = boto_apigateway.describe_api_model( + restApiId="rm06h9oac4", modelName="Error", **conn_parameters + ) + self.assertTrue(result.get("model")) - def test_that_when_describing_api_model_and_that_the_model_does_not_exist_the_describe_api_model_method_returns_error(self): - ''' + def test_that_when_describing_api_model_and_that_the_model_does_not_exist_the_describe_api_model_method_returns_error( + self, + ): + """ Test Equality of error returned - ''' - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') - result = boto_apigateway.describe_api_model(restApiId='rm06h9oac4', modelName='Error', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_model')) + """ + self.conn.get_model.side_effect = ClientError(error_content, "get_model") + result = boto_apigateway.describe_api_model( + restApiId="rm06h9oac4", modelName="Error", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_model") + ) def test_that_model_exists_the_api_model_exists_method_returns_true(self): - ''' + """ Tests True when model exists - ''' + """ self.conn.get_model.return_value = api_model_ret - result = boto_apigateway.api_model_exists(restApiId='rm06h9oac4', modelName='Error', **conn_parameters) - self.assertTrue(result.get('exists')) + result = boto_apigateway.api_model_exists( + restApiId="rm06h9oac4", modelName="Error", **conn_parameters + ) + self.assertTrue(result.get("exists")) def test_that_model_does_not_exists_the_api_model_exists_method_returns_false(self): - ''' + """ Tests False when model does not exist - ''' - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') - result = boto_apigateway.api_model_exists(restApiId='rm06h9oac4', modelName='Error', **conn_parameters) - self.assertFalse(result.get('exists')) + """ + self.conn.get_model.side_effect = ClientError(error_content, "get_model") + result = boto_apigateway.api_model_exists( + restApiId="rm06h9oac4", modelName="Error", **conn_parameters + ) + self.assertFalse(result.get("exists")) - def test_that_updating_model_schema_the_update_api_model_schema_method_returns_true(self): - ''' + def test_that_updating_model_schema_the_update_api_model_schema_method_returns_true( + self, + ): + """ Tests True when model schema is updated. - ''' + """ self.conn.update_model.return_value = api_model_ret - result = boto_apigateway.update_api_model_schema(restApiId='rm06h9oac4', modelName='Error', - schema=api_model_error_schema, **conn_parameters) - self.assertTrue(result.get('updated')) + result = boto_apigateway.update_api_model_schema( + restApiId="rm06h9oac4", + modelName="Error", + schema=api_model_error_schema, + **conn_parameters + ) + self.assertTrue(result.get("updated")) - def test_that_updating_model_schema_when_model_does_not_exist_the_update_api_model_schema_emthod_returns_false(self): - ''' + def test_that_updating_model_schema_when_model_does_not_exist_the_update_api_model_schema_emthod_returns_false( + self, + ): + """ Tests False when model schema is not upated. - ''' - self.conn.update_model.side_effect = ClientError(error_content, 'update_model') - result = boto_apigateway.update_api_model_schema(restApiId='rm06h9oac4', modelName='no_such_model', - schema=api_model_error_schema, **conn_parameters) - self.assertFalse(result.get('updated')) + """ + self.conn.update_model.side_effect = ClientError(error_content, "update_model") + result = boto_apigateway.update_api_model_schema( + restApiId="rm06h9oac4", + modelName="no_such_model", + schema=api_model_error_schema, + **conn_parameters + ) + self.assertFalse(result.get("updated")) - def test_that_when_creating_an_api_model_succeeds_the_create_api_model_method_returns_true(self): - ''' + def test_that_when_creating_an_api_model_succeeds_the_create_api_model_method_returns_true( + self, + ): + """ tests that we can successfully create an api model - ''' + """ self.conn.create_model.return_value = api_model_ret - result = boto_apigateway.create_api_model(restApiId='rm06h9oac4', modelName='Error', - modelDescription='Error Model', schema=api_model_error_schema, - **conn_parameters) - self.assertTrue(result.get('created')) + result = boto_apigateway.create_api_model( + restApiId="rm06h9oac4", + modelName="Error", + modelDescription="Error Model", + schema=api_model_error_schema, + **conn_parameters + ) + self.assertTrue(result.get("created")) - def test_that_when_creating_an_api_model_fails_the_create_api_model_method_returns_error(self): - ''' + def test_that_when_creating_an_api_model_fails_the_create_api_model_method_returns_error( + self, + ): + """ tests that we properly handle errors when create an api model fails. - ''' - self.conn.create_model.side_effect = ClientError(error_content, 'create_model') - result = boto_apigateway.create_api_model(restApiId='rm06h9oac4', modelName='Error', - modelDescription='Error Model', schema=api_model_error_schema, - **conn_parameters) - self.assertFalse(result.get('created')) + """ + self.conn.create_model.side_effect = ClientError(error_content, "create_model") + result = boto_apigateway.create_api_model( + restApiId="rm06h9oac4", + modelName="Error", + modelDescription="Error Model", + schema=api_model_error_schema, + **conn_parameters + ) + self.assertFalse(result.get("created")) - def test_that_when_deleting_an_api_model_that_exists_the_delete_api_model_method_returns_true(self): - ''' + def test_that_when_deleting_an_api_model_that_exists_the_delete_api_model_method_returns_true( + self, + ): + """ test True if the api model is successfully deleted. - ''' - self.conn.delete_model.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.delete_api_model(restApiId='rm06h9oac4', modelName='Error', **conn_parameters) - self.assertTrue(result.get('deleted')) + """ + self.conn.delete_model.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } + result = boto_apigateway.delete_api_model( + restApiId="rm06h9oac4", modelName="Error", **conn_parameters + ) + self.assertTrue(result.get("deleted")) - def test_that_when_deleting_an_api_model_that_does_not_exist_the_delete_api_model_method_returns_false(self): - ''' + def test_that_when_deleting_an_api_model_that_does_not_exist_the_delete_api_model_method_returns_false( + self, + ): + """ Test that the given api model doesn't exists, and delete_api_model should return deleted status of False - ''' - self.conn.delete_model.side_effect = ClientError(error_content, 'delete_model') - result = boto_apigateway.delete_api_model(restApiId='rm06h9oac4', modelName='no_such_model', **conn_parameters) - self.assertFalse(result.get('deleted')) + """ + self.conn.delete_model.side_effect = ClientError(error_content, "delete_model") + result = boto_apigateway.delete_api_model( + restApiId="rm06h9oac4", modelName="no_such_model", **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_describing_api_resources_the_describe_api_resources_method_returns_list_of_3_resources(self): - ''' + def test_that_when_describing_api_resources_the_describe_api_resources_method_returns_list_of_3_resources( + self, + ): + """ Test Equality for number of resources for the given api is 3 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.describe_api_resources(restApiId='rm06h9oac4', **conn_parameters) - self.assertEqual(len(result.get('resources')), len(api_resources_ret.get('items'))) + result = boto_apigateway.describe_api_resources( + restApiId="rm06h9oac4", **conn_parameters + ) + self.assertEqual( + len(result.get("resources")), len(api_resources_ret.get("items")) + ) - def test_that_when_describing_api_resources_and_that_the_api_does_not_exist_the_describe_api_resources_method_returns_error(self): - ''' + def test_that_when_describing_api_resources_and_that_the_api_does_not_exist_the_describe_api_resources_method_returns_error( + self, + ): + """ Test Equality of error returned - ''' - self.conn.get_resources.side_effect = ClientError(error_content, 'get_resources') - result = boto_apigateway.describe_api_resources(restApiId='rm06h9oac4', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_resources')) + """ + self.conn.get_resources.side_effect = ClientError( + error_content, "get_resources" + ) + result = boto_apigateway.describe_api_resources( + restApiId="rm06h9oac4", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_resources") + ) - def test_that_when_describing_an_api_resource_that_exists_the_describe_api_resource_method_returns_the_resource(self): - ''' + def test_that_when_describing_an_api_resource_that_exists_the_describe_api_resource_method_returns_the_resource( + self, + ): + """ Test Equality of the resource path returned is /api - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.describe_api_resource(restApiId='rm06h9oac4', path="/api", **conn_parameters) - self.assertEqual(result.get('resource', {}).get('path'), '/api') + result = boto_apigateway.describe_api_resource( + restApiId="rm06h9oac4", path="/api", **conn_parameters + ) + self.assertEqual(result.get("resource", {}).get("path"), "/api") - def test_that_when_describing_an_api_resource_that_does_not_exist_the_describe_api_resource_method_returns_the_resource_as_none(self): - ''' + def test_that_when_describing_an_api_resource_that_does_not_exist_the_describe_api_resource_method_returns_the_resource_as_none( + self, + ): + """ Test Equality of the 'resource' is None - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.describe_api_resource(restApiId='rm06h9oac4', path='/path/does/not/exist', - **conn_parameters) - self.assertEqual(result.get('resource'), None) + result = boto_apigateway.describe_api_resource( + restApiId="rm06h9oac4", path="/path/does/not/exist", **conn_parameters + ) + self.assertEqual(result.get("resource"), None) - def test_that_when_describing_an_api_resource_and_that_the_api_does_not_exist_the_describe_api_resource_method_returns_error(self): - ''' + def test_that_when_describing_an_api_resource_and_that_the_api_does_not_exist_the_describe_api_resource_method_returns_error( + self, + ): + """ Test Equality of error returned - ''' - self.conn.get_resources.side_effect = ClientError(error_content, 'get_resources') - result = boto_apigateway.describe_api_resource(restApiId='bad_id', path="/api", **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_resources')) + """ + self.conn.get_resources.side_effect = ClientError( + error_content, "get_resources" + ) + result = boto_apigateway.describe_api_resource( + restApiId="bad_id", path="/api", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_resources") + ) - def test_that_when_creating_api_resources_for_a_path_that_creates_one_new_resource_the_create_resources_api_method_returns_all_resources(self): - ''' + def test_that_when_creating_api_resources_for_a_path_that_creates_one_new_resource_the_create_resources_api_method_returns_all_resources( + self, + ): + """ Tests that a path of '/api3' returns 2 resources, named '/' and '/api'. - ''' + """ self.conn.get_resources.return_value = api_resources_ret self.conn.create_resource.return_value = api_create_resource_ret - result = boto_apigateway.create_api_resources(restApiId='rm06h9oac4', path='/api3', **conn_parameters) + result = boto_apigateway.create_api_resources( + restApiId="rm06h9oac4", path="/api3", **conn_parameters + ) - resources = result.get('resources') - self.assertIs(result.get('created'), True) + resources = result.get("resources") + self.assertIs(result.get("created"), True) self.assertEqual(len(resources), 2) - self.assertEqual(resources[0].get('path'), '/') - self.assertEqual(resources[1].get('path'), '/api3') + self.assertEqual(resources[0].get("path"), "/") + self.assertEqual(resources[1].get("path"), "/api3") - def test_that_when_creating_api_resources_for_a_path_whose_resources_exist_the_create_resources_api_method_returns_all_resources(self): - ''' + def test_that_when_creating_api_resources_for_a_path_whose_resources_exist_the_create_resources_api_method_returns_all_resources( + self, + ): + """ Tests that a path of '/api/users' as defined in api_resources_ret return resources named '/', '/api', and '/api/users' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.create_api_resources(restApiId='rm06h9oac4', path='/api/users', **conn_parameters) - resources = result.get('resources') - self.assertIs(result.get('created'), True) - self.assertEqual(len(resources), len(api_resources_ret.get('items'))) - self.assertEqual(resources[0].get('path'), '/') - self.assertEqual(resources[1].get('path'), '/api') - self.assertEqual(resources[2].get('path'), '/api/users') + result = boto_apigateway.create_api_resources( + restApiId="rm06h9oac4", path="/api/users", **conn_parameters + ) + resources = result.get("resources") + self.assertIs(result.get("created"), True) + self.assertEqual(len(resources), len(api_resources_ret.get("items"))) + self.assertEqual(resources[0].get("path"), "/") + self.assertEqual(resources[1].get("path"), "/api") + self.assertEqual(resources[2].get("path"), "/api/users") - def test_that_when_creating_api_resource_fails_the_create_resources_api_method_returns_false(self): - ''' + def test_that_when_creating_api_resource_fails_the_create_resources_api_method_returns_false( + self, + ): + """ Tests False if we failed to create a resource - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.create_resource.side_effect = ClientError(error_content, 'create_resource') - result = boto_apigateway.create_api_resources(restApiId='rm06h9oac4', path='/api4', **conn_parameters) - self.assertFalse(result.get('created')) + self.conn.create_resource.side_effect = ClientError( + error_content, "create_resource" + ) + result = boto_apigateway.create_api_resources( + restApiId="rm06h9oac4", path="/api4", **conn_parameters + ) + self.assertFalse(result.get("created")) - def test_that_when_deleting_api_resources_for_a_resource_that_exists_the_delete_api_resources_method_returns_true(self): - ''' + def test_that_when_deleting_api_resources_for_a_resource_that_exists_the_delete_api_resources_method_returns_true( + self, + ): + """ Tests True for '/api' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.delete_api_resources(restApiId='rm06h9oac4', path='/api', **conn_parameters) - self.assertTrue(result.get('deleted')) + result = boto_apigateway.delete_api_resources( + restApiId="rm06h9oac4", path="/api", **conn_parameters + ) + self.assertTrue(result.get("deleted")) - def test_that_when_deleting_api_resources_for_a_resource_that_does_not_exist_the_delete_api_resources_method_returns_false(self): - ''' + def test_that_when_deleting_api_resources_for_a_resource_that_does_not_exist_the_delete_api_resources_method_returns_false( + self, + ): + """ Tests False for '/api5' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.delete_api_resources(restApiId='rm06h9oac4', path='/api5', **conn_parameters) - self.assertFalse(result.get('deleted')) + result = boto_apigateway.delete_api_resources( + restApiId="rm06h9oac4", path="/api5", **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_deleting_the_root_api_resource_the_delete_api_resources_method_returns_false(self): - ''' + def test_that_when_deleting_the_root_api_resource_the_delete_api_resources_method_returns_false( + self, + ): + """ Tests False for '/' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.delete_api_resources(restApiId='rm06h9oac4', path='/', **conn_parameters) - self.assertFalse(result.get('deleted')) + result = boto_apigateway.delete_api_resources( + restApiId="rm06h9oac4", path="/", **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_deleting_api_resources_and_delete_resource_throws_error_the_delete_api_resources_method_returns_false(self): - ''' + def test_that_when_deleting_api_resources_and_delete_resource_throws_error_the_delete_api_resources_method_returns_false( + self, + ): + """ Tests False delete_resource side side_effect - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.delete_resource.side_effect = ClientError(error_content, 'delete_resource') - result = boto_apigateway.delete_api_resources(restApiId='rm06h9oac4', path='/api', **conn_parameters) - self.assertFalse(result.get('deleted')) + self.conn.delete_resource.side_effect = ClientError( + error_content, "delete_resource" + ) + result = boto_apigateway.delete_api_resources( + restApiId="rm06h9oac4", path="/api", **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_describing_an_api_resource_method_that_exists_the_describe_api_resource_method_returns_the_method(self): - ''' + def test_that_when_describing_an_api_resource_method_that_exists_the_describe_api_resource_method_returns_the_method( + self, + ): + """ Tests True for '/api/users' and POST - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.get_method.return_value = {u'httpMethod': 'POST', - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} - result = boto_apigateway.describe_api_resource_method(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='POST', **conn_parameters) - self.assertTrue(result.get('method')) + self.conn.get_method.return_value = { + "httpMethod": "POST", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, + } + result = boto_apigateway.describe_api_resource_method( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + **conn_parameters + ) + self.assertTrue(result.get("method")) - def test_that_when_describing_an_api_resource_method_whose_method_does_not_exist_the_describe_api_resource_method_returns_error(self): - ''' + def test_that_when_describing_an_api_resource_method_whose_method_does_not_exist_the_describe_api_resource_method_returns_error( + self, + ): + """ Tests Equality of returned error for '/api/users' and PUT - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.get_method.side_effect = ClientError(error_content, 'get_method') - result = boto_apigateway.describe_api_resource_method(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='PUT', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_method')) + self.conn.get_method.side_effect = ClientError(error_content, "get_method") + result = boto_apigateway.describe_api_resource_method( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="PUT", + **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_method") + ) - def test_that_when_describing_an_api_resource_method_whose_resource_does_not_exist_the_describe_api_resrouce_method_returns_error(self): - ''' + def test_that_when_describing_an_api_resource_method_whose_resource_does_not_exist_the_describe_api_resrouce_method_returns_error( + self, + ): + """ Tests True for resource not found error for '/does/not/exist' and POST - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.describe_api_resource_method(restApiId='rm06h9oac4', - resourcePath='/does/not/exist', - httpMethod='POST', **conn_parameters) - self.assertTrue(result.get('error')) + result = boto_apigateway.describe_api_resource_method( + restApiId="rm06h9oac4", + resourcePath="/does/not/exist", + httpMethod="POST", + **conn_parameters + ) + self.assertTrue(result.get("error")) - def test_that_when_creating_an_api_method_the_create_api_method_method_returns_true(self): - ''' + def test_that_when_creating_an_api_method_the_create_api_method_method_returns_true( + self, + ): + """ Tests True on 'created' for '/api/users' and 'GET' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.put_method.return_value = {u'httpMethod': 'GET', - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} - result = boto_apigateway.create_api_method(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='GET', - authorizationType='NONE', **conn_parameters) - self.assertTrue(result.get('created')) + self.conn.put_method.return_value = { + "httpMethod": "GET", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, + } + result = boto_apigateway.create_api_method( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="GET", + authorizationType="NONE", + **conn_parameters + ) + self.assertTrue(result.get("created")) - def test_that_when_creating_an_api_method_and_resource_does_not_exist_the_create_api_method_method_returns_false(self): - ''' + def test_that_when_creating_an_api_method_and_resource_does_not_exist_the_create_api_method_method_returns_false( + self, + ): + """ Tests False on 'created' for '/api5', and 'GET' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.create_api_method(restApiId='rm06h9oac4', - resourcePath='/api5', - httpMethod='GET', - authorizationType='NONE', **conn_parameters) - self.assertFalse(result.get('created')) + result = boto_apigateway.create_api_method( + restApiId="rm06h9oac4", + resourcePath="/api5", + httpMethod="GET", + authorizationType="NONE", + **conn_parameters + ) + self.assertFalse(result.get("created")) - def test_that_when_creating_an_api_method_and_error_thrown_on_put_method_the_create_api_method_method_returns_false(self): - ''' + def test_that_when_creating_an_api_method_and_error_thrown_on_put_method_the_create_api_method_method_returns_false( + self, + ): + """ Tests False on 'created' for '/api/users' and 'GET' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.put_method.side_effect = ClientError(error_content, 'put_method') - result = boto_apigateway.create_api_method(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='GET', - authorizationType='NONE', **conn_parameters) - self.assertFalse(result.get('created')) + self.conn.put_method.side_effect = ClientError(error_content, "put_method") + result = boto_apigateway.create_api_method( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="GET", + authorizationType="NONE", + **conn_parameters + ) + self.assertFalse(result.get("created")) - def test_that_when_deleting_an_api_method_for_a_method_that_exist_the_delete_api_method_method_returns_true(self): - ''' + def test_that_when_deleting_an_api_method_for_a_method_that_exist_the_delete_api_method_method_returns_true( + self, + ): + """ Tests True for '/api/users' and 'POST' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.delete_method.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.delete_api_method(restApiId='rm06h9oac4', resourcePath='/api/users', - httpMethod='POST', **conn_parameters) - self.assertTrue(result.get('deleted')) + self.conn.delete_method.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } + result = boto_apigateway.delete_api_method( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + **conn_parameters + ) + self.assertTrue(result.get("deleted")) - def test_that_when_deleting_an_api_method_for_a_method_that_does_not_exist_the_delete_api_method_method_returns_false(self): - ''' + def test_that_when_deleting_an_api_method_for_a_method_that_does_not_exist_the_delete_api_method_method_returns_false( + self, + ): + """ Tests False for '/api/users' and 'GET' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.delete_method.side_effect = ClientError(error_content, 'delete_method') - result = boto_apigateway.delete_api_method(restApiId='rm06h9oac4', resourcePath='/api/users', - httpMethod='GET', **conn_parameters) - self.assertFalse(result.get('deleted')) + self.conn.delete_method.side_effect = ClientError( + error_content, "delete_method" + ) + result = boto_apigateway.delete_api_method( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="GET", + **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_deleting_an_api_method_for_a_resource_that_does_not_exist_the_delete_api_method_method_returns_false(self): - ''' + def test_that_when_deleting_an_api_method_for_a_resource_that_does_not_exist_the_delete_api_method_method_returns_false( + self, + ): + """ Tests False for '/api/users5' and 'POST' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.delete_api_method(restApiId='rm06h9oac4', resourcePath='/api/users5', - httpMethod='POST', **conn_parameters) - self.assertFalse(result.get('deleted')) + result = boto_apigateway.delete_api_method( + restApiId="rm06h9oac4", + resourcePath="/api/users5", + httpMethod="POST", + **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_describing_an_api_method_response_that_exists_the_describe_api_method_respond_method_returns_the_response(self): - ''' + def test_that_when_describing_an_api_method_response_that_exists_the_describe_api_method_respond_method_returns_the_response( + self, + ): + """ Tests True for 'response' for '/api/users', 'POST', and 200 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.get_method_response.return_value = {u'statusCode': 200, - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} - result = boto_apigateway.describe_api_method_response(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='POST', - statusCode=200, **conn_parameters) - self.assertTrue(result.get('response')) + self.conn.get_method_response.return_value = { + "statusCode": 200, + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, + } + result = boto_apigateway.describe_api_method_response( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + statusCode=200, + **conn_parameters + ) + self.assertTrue(result.get("response")) - def test_that_when_describing_an_api_method_response_and_response_code_does_not_exist_the_describe_api_method_response_method_returns_error(self): - ''' + def test_that_when_describing_an_api_method_response_and_response_code_does_not_exist_the_describe_api_method_response_method_returns_error( + self, + ): + """ Tests Equality of error msg thrown from get_method_response for '/api/users', 'POST', and 250 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.get_method_response.side_effect = ClientError(error_content, 'get_method_response') - result = boto_apigateway.describe_api_method_response(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='POST', - statusCode=250, **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_method_response')) + self.conn.get_method_response.side_effect = ClientError( + error_content, "get_method_response" + ) + result = boto_apigateway.describe_api_method_response( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + statusCode=250, + **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), + error_message.format("get_method_response"), + ) - def test_that_when_describing_an_api_method_response_and_resource_does_not_exist_the_describe_api_method_response_method_returns_error(self): - ''' + def test_that_when_describing_an_api_method_response_and_resource_does_not_exist_the_describe_api_method_response_method_returns_error( + self, + ): + """ Tests True for existence of 'error' for '/api5/users', 'POST', and 200 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.describe_api_method_response(restApiId='rm06h9oac4', - resourcePath='/api5/users', - httpMethod='POST', - statusCode=200, **conn_parameters) - self.assertTrue(result.get('error')) + result = boto_apigateway.describe_api_method_response( + restApiId="rm06h9oac4", + resourcePath="/api5/users", + httpMethod="POST", + statusCode=200, + **conn_parameters + ) + self.assertTrue(result.get("error")) - def test_that_when_creating_an_api_method_response_the_create_api_method_response_method_returns_true(self): - ''' + def test_that_when_creating_an_api_method_response_the_create_api_method_response_method_returns_true( + self, + ): + """ Tests True on 'created' for '/api/users', 'POST', 201 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.put_method_response.return_value = {u'statusCode': '201', - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} - result = boto_apigateway.create_api_method_response(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='POST', - statusCode='201', **conn_parameters) - self.assertTrue(result.get('created')) + self.conn.put_method_response.return_value = { + "statusCode": "201", + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, + } + result = boto_apigateway.create_api_method_response( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + statusCode="201", + **conn_parameters + ) + self.assertTrue(result.get("created")) - def test_that_when_creating_an_api_method_response_and_resource_does_not_exist_the_create_api_method_response_method_returns_false(self): - ''' + def test_that_when_creating_an_api_method_response_and_resource_does_not_exist_the_create_api_method_response_method_returns_false( + self, + ): + """ Tests False on 'created' for '/api5', 'POST', 200 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.create_api_method_response(restApiId='rm06h9oac4', - resourcePath='/api5', - httpMethod='POST', - statusCode='200', **conn_parameters) - self.assertFalse(result.get('created')) + result = boto_apigateway.create_api_method_response( + restApiId="rm06h9oac4", + resourcePath="/api5", + httpMethod="POST", + statusCode="200", + **conn_parameters + ) + self.assertFalse(result.get("created")) - def test_that_when_creating_an_api_method_response_and_error_thrown_on_put_method_response_the_create_api_method_response_method_returns_false(self): - ''' + def test_that_when_creating_an_api_method_response_and_error_thrown_on_put_method_response_the_create_api_method_response_method_returns_false( + self, + ): + """ Tests False on 'created' for '/api/users', 'POST', 200 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.put_method_response.side_effect = ClientError(error_content, 'put_method_response') - result = boto_apigateway.create_api_method_response(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='POST', - statusCode='200', **conn_parameters) - self.assertFalse(result.get('created')) + self.conn.put_method_response.side_effect = ClientError( + error_content, "put_method_response" + ) + result = boto_apigateway.create_api_method_response( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + statusCode="200", + **conn_parameters + ) + self.assertFalse(result.get("created")) - def test_that_when_deleting_an_api_method_response_for_a_response_that_exist_the_delete_api_method_response_method_returns_true(self): - ''' + def test_that_when_deleting_an_api_method_response_for_a_response_that_exist_the_delete_api_method_response_method_returns_true( + self, + ): + """ Tests True for '/api/users', 'POST', 200 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.delete_method_response.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} - result = boto_apigateway.delete_api_method_response(restApiId='rm06h9oac4', resourcePath='/api/users', - httpMethod='POST', statusCode='200', **conn_parameters) - self.assertTrue(result.get('deleted')) + self.conn.delete_method_response.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } + result = boto_apigateway.delete_api_method_response( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + statusCode="200", + **conn_parameters + ) + self.assertTrue(result.get("deleted")) - def test_that_when_deleting_an_api_method_response_for_a_response_that_does_not_exist_the_delete_api_method_response_method_returns_false(self): - ''' + def test_that_when_deleting_an_api_method_response_for_a_response_that_does_not_exist_the_delete_api_method_response_method_returns_false( + self, + ): + """ Tests False for '/api/users', 'POST', 201 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.delete_method_response.side_effect = ClientError(error_content, 'delete_method_response') - result = boto_apigateway.delete_api_method_response(restApiId='rm06h9oac4', resourcePath='/api/users', - httpMethod='GET', statusCode='201', **conn_parameters) - self.assertFalse(result.get('deleted')) + self.conn.delete_method_response.side_effect = ClientError( + error_content, "delete_method_response" + ) + result = boto_apigateway.delete_api_method_response( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="GET", + statusCode="201", + **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_deleting_an_api_method_response_for_a_resource_that_does_not_exist_the_delete_api_method_response_method_returns_false(self): - ''' + def test_that_when_deleting_an_api_method_response_for_a_resource_that_does_not_exist_the_delete_api_method_response_method_returns_false( + self, + ): + """ Tests False for '/api/users5', 'POST', 200 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.delete_api_method_response(restApiId='rm06h9oac4', resourcePath='/api/users5', - httpMethod='POST', statusCode='200', **conn_parameters) - self.assertFalse(result.get('deleted')) + result = boto_apigateway.delete_api_method_response( + restApiId="rm06h9oac4", + resourcePath="/api/users5", + httpMethod="POST", + statusCode="200", + **conn_parameters + ) + self.assertFalse(result.get("deleted")) - def test_that_when_describing_an_api_integration_that_exists_the_describe_api_integration_method_returns_the_intgration(self): - ''' + def test_that_when_describing_an_api_integration_that_exists_the_describe_api_integration_method_returns_the_intgration( + self, + ): + """ Tests True for 'integration' for '/api/users', 'POST' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.get_integration.return_value = {u'type': 'AWS', - u'uri': 'arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:1234568992820:function:echo_event/invocations', - u'credentials': 'testing', - u'httpMethod': 'POST', - u'intgrationResponses': {'200': {}}, - u'requestTemplates': {'application/json': {}}, - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} - result = boto_apigateway.describe_api_integration(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='POST', - **conn_parameters) - self.assertTrue(result.get('integration')) + self.conn.get_integration.return_value = { + "type": "AWS", + "uri": "arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:1234568992820:function:echo_event/invocations", + "credentials": "testing", + "httpMethod": "POST", + "intgrationResponses": {"200": {}}, + "requestTemplates": {"application/json": {}}, + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, + } + result = boto_apigateway.describe_api_integration( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + **conn_parameters + ) + self.assertTrue(result.get("integration")) - def test_that_when_describing_an_api_integration_and_method_does_not_have_integration_defined_the_describe_api_integration_method_returns_error(self): - ''' + def test_that_when_describing_an_api_integration_and_method_does_not_have_integration_defined_the_describe_api_integration_method_returns_error( + self, + ): + """ Tests Equality of error msg thrown from get_method_response for '/api/users', 'GET' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.get_integration.side_effect = ClientError(error_content, 'get_integration') - result = boto_apigateway.describe_api_integration(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='GET', - **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_integration')) + self.conn.get_integration.side_effect = ClientError( + error_content, "get_integration" + ) + result = boto_apigateway.describe_api_integration( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="GET", + **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format("get_integration") + ) - def test_that_when_describing_an_api_integration_and_resource_does_not_exist_the_describe_api_integration_method_returns_error(self): - ''' + def test_that_when_describing_an_api_integration_and_resource_does_not_exist_the_describe_api_integration_method_returns_error( + self, + ): + """ Tests True for existence of 'error' for '/api5/users', 'POST' - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.describe_api_integration(restApiId='rm06h9oac4', - resourcePath='/api5/users', - httpMethod='POST', - **conn_parameters) - self.assertTrue(result.get('error')) + result = boto_apigateway.describe_api_integration( + restApiId="rm06h9oac4", + resourcePath="/api5/users", + httpMethod="POST", + **conn_parameters + ) + self.assertTrue(result.get("error")) - def test_that_when_describing_an_api_integration_response_that_exists_the_describe_api_integration_response_method_returns_the_intgration(self): - ''' + def test_that_when_describing_an_api_integration_response_that_exists_the_describe_api_integration_response_method_returns_the_intgration( + self, + ): + """ Tests True for 'response' for '/api/users', 'POST', 200 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.get_integration_response.return_value = {u'responseParameters': {}, - u'statusCode': 200, - 'ResponseMetadata': {'HTTPStatusCode': 200, - 'RequestId': '7cc233dd-9dc8-11e5-ba47-1b7350cc2757'}} - result = boto_apigateway.describe_api_integration_response(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='POST', - statusCode='200', - **conn_parameters) - self.assertTrue(result.get('response')) + self.conn.get_integration_response.return_value = { + "responseParameters": {}, + "statusCode": 200, + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "7cc233dd-9dc8-11e5-ba47-1b7350cc2757", + }, + } + result = boto_apigateway.describe_api_integration_response( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + statusCode="200", + **conn_parameters + ) + self.assertTrue(result.get("response")) - def test_that_when_describing_an_api_integration_response_and_status_code_does_not_exist_the_describe_api_integration_response_method_returns_error(self): - ''' + def test_that_when_describing_an_api_integration_response_and_status_code_does_not_exist_the_describe_api_integration_response_method_returns_error( + self, + ): + """ Tests Equality of error msg thrown from get_method_response for '/api/users', 'POST', 201 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - self.conn.get_integration_response.side_effect = ClientError(error_content, 'get_integration_response') - result = boto_apigateway.describe_api_integration_response(restApiId='rm06h9oac4', - resourcePath='/api/users', - httpMethod='POST', - statusCode='201', - **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_integration_response')) + self.conn.get_integration_response.side_effect = ClientError( + error_content, "get_integration_response" + ) + result = boto_apigateway.describe_api_integration_response( + restApiId="rm06h9oac4", + resourcePath="/api/users", + httpMethod="POST", + statusCode="201", + **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), + error_message.format("get_integration_response"), + ) - def test_that_when_describing_an_api_integration_response_and_resource_does_not_exist_the_describe_api_integration_response_method_returns_error(self): - ''' + def test_that_when_describing_an_api_integration_response_and_resource_does_not_exist_the_describe_api_integration_response_method_returns_error( + self, + ): + """ Tests True for existence of 'error' for '/api5/users', 'POST', 200 - ''' + """ self.conn.get_resources.return_value = api_resources_ret - result = boto_apigateway.describe_api_integration_response(restApiId='rm06h9oac4', - resourcePath='/api5/users', - httpMethod='POST', - statusCode='200', - **conn_parameters) - self.assertTrue(result.get('error')) + result = boto_apigateway.describe_api_integration_response( + restApiId="rm06h9oac4", + resourcePath="/api5/users", + httpMethod="POST", + statusCode="200", + **conn_parameters + ) + self.assertTrue(result.get("error")) - def test_that_when_describing_usage_plans_and_an_exception_is_thrown_in_get_usage_plans(self): - ''' + def test_that_when_describing_usage_plans_and_an_exception_is_thrown_in_get_usage_plans( + self, + ): + """ Tests True for existence of 'error' - ''' - self.conn.get_usage_plans.side_effect = ClientError(error_content, 'get_usage_plans_exception') - result = boto_apigateway.describe_usage_plans(name='some plan', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('get_usage_plans_exception')) + """ + self.conn.get_usage_plans.side_effect = ClientError( + error_content, "get_usage_plans_exception" + ) + result = boto_apigateway.describe_usage_plans( + name="some plan", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), + error_message.format("get_usage_plans_exception"), + ) - def test_that_when_describing_usage_plans_and_plan_name_or_id_does_not_exist_that_results_have_empty_plans_list(self): - ''' + def test_that_when_describing_usage_plans_and_plan_name_or_id_does_not_exist_that_results_have_empty_plans_list( + self, + ): + """ Tests for plans equaling empty list - ''' + """ self.conn.get_usage_plans.return_value = usage_plans_ret - result = boto_apigateway.describe_usage_plans(name='does not exist', **conn_parameters) - self.assertEqual(result.get('plans'), []) + result = boto_apigateway.describe_usage_plans( + name="does not exist", **conn_parameters + ) + self.assertEqual(result.get("plans"), []) - result = boto_apigateway.describe_usage_plans(plan_id='does not exist', **conn_parameters) - self.assertEqual(result.get('plans'), []) + result = boto_apigateway.describe_usage_plans( + plan_id="does not exist", **conn_parameters + ) + self.assertEqual(result.get("plans"), []) - result = boto_apigateway.describe_usage_plans(name='does not exist', plan_id='does not exist', **conn_parameters) - self.assertEqual(result.get('plans'), []) + result = boto_apigateway.describe_usage_plans( + name="does not exist", plan_id="does not exist", **conn_parameters + ) + self.assertEqual(result.get("plans"), []) - result = boto_apigateway.describe_usage_plans(name='plan1_name', plan_id='does not exist', **conn_parameters) - self.assertEqual(result.get('plans'), []) + result = boto_apigateway.describe_usage_plans( + name="plan1_name", plan_id="does not exist", **conn_parameters + ) + self.assertEqual(result.get("plans"), []) - result = boto_apigateway.describe_usage_plans(name='does not exist', plan_id='plan1_id', **conn_parameters) - self.assertEqual(result.get('plans'), []) + result = boto_apigateway.describe_usage_plans( + name="does not exist", plan_id="plan1_id", **conn_parameters + ) + self.assertEqual(result.get("plans"), []) - def test_that_when_describing_usage_plans_for_plans_that_exist_that_the_function_returns_all_matching_plans(self): - ''' + def test_that_when_describing_usage_plans_for_plans_that_exist_that_the_function_returns_all_matching_plans( + self, + ): + """ Tests for plans filtering properly if they exist - ''' + """ self.conn.get_usage_plans.return_value = usage_plans_ret - result = boto_apigateway.describe_usage_plans(name=usage_plan1['name'], **conn_parameters) - self.assertEqual(len(result.get('plans')), 2) - for plan in result['plans']: + result = boto_apigateway.describe_usage_plans( + name=usage_plan1["name"], **conn_parameters + ) + self.assertEqual(len(result.get("plans")), 2) + for plan in result["plans"]: self.assertTrue(plan in [usage_plan1, usage_plan1b]) - def test_that_when_creating_or_updating_a_usage_plan_and_throttle_or_quota_failed_to_validate_that_an_error_is_returned(self): - ''' + def test_that_when_creating_or_updating_a_usage_plan_and_throttle_or_quota_failed_to_validate_that_an_error_is_returned( + self, + ): + """ Tests for TypeError and ValueError in throttle and quota - ''' - for throttle, quota in (([], None), (None, []), ('abc', None), (None, 'def')): - res = boto_apigateway.create_usage_plan('plan1_name', description=None, throttle=throttle, quota=quota, **conn_parameters) - self.assertNotEqual(None, res.get('error')) - res = boto_apigateway.update_usage_plan('plan1_id', throttle=throttle, quota=quota, **conn_parameters) - self.assertNotEqual(None, res.get('error')) + """ + for throttle, quota in (([], None), (None, []), ("abc", None), (None, "def")): + res = boto_apigateway.create_usage_plan( + "plan1_name", + description=None, + throttle=throttle, + quota=quota, + **conn_parameters + ) + self.assertNotEqual(None, res.get("error")) + res = boto_apigateway.update_usage_plan( + "plan1_id", throttle=throttle, quota=quota, **conn_parameters + ) + self.assertNotEqual(None, res.get("error")) - for quota in ({'limit': 123}, {'period': 123}, {'period': 'DAY'}): - res = boto_apigateway.create_usage_plan('plan1_name', description=None, throttle=None, quota=quota, **conn_parameters) - self.assertNotEqual(None, res.get('error')) - res = boto_apigateway.update_usage_plan('plan1_id', quota=quota, **conn_parameters) - self.assertNotEqual(None, res.get('error')) + for quota in ({"limit": 123}, {"period": 123}, {"period": "DAY"}): + res = boto_apigateway.create_usage_plan( + "plan1_name", + description=None, + throttle=None, + quota=quota, + **conn_parameters + ) + self.assertNotEqual(None, res.get("error")) + res = boto_apigateway.update_usage_plan( + "plan1_id", quota=quota, **conn_parameters + ) + self.assertNotEqual(None, res.get("error")) self.assertTrue(self.conn.get_usage_plans.call_count == 0) self.assertTrue(self.conn.create_usage_plan.call_count == 0) self.assertTrue(self.conn.update_usage_plan.call_count == 0) - def test_that_when_creating_a_usage_plan_and_create_usage_plan_throws_an_exception_that_an_error_is_returned(self): - ''' + def test_that_when_creating_a_usage_plan_and_create_usage_plan_throws_an_exception_that_an_error_is_returned( + self, + ): + """ tests for ClientError - ''' - self.conn.create_usage_plan.side_effect = ClientError(error_content, 'create_usage_plan_exception') - result = boto_apigateway.create_usage_plan(name='some plan', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('create_usage_plan_exception')) + """ + self.conn.create_usage_plan.side_effect = ClientError( + error_content, "create_usage_plan_exception" + ) + result = boto_apigateway.create_usage_plan(name="some plan", **conn_parameters) + self.assertEqual( + result.get("error").get("message"), + error_message.format("create_usage_plan_exception"), + ) def test_that_create_usage_plan_succeeds(self): - ''' + """ tests for success user plan creation - ''' - res = 'unit test create_usage_plan succeeded' + """ + res = "unit test create_usage_plan succeeded" self.conn.create_usage_plan.return_value = res - result = boto_apigateway.create_usage_plan(name='some plan', **conn_parameters) - self.assertEqual(result.get('created'), True) - self.assertEqual(result.get('result'), res) + result = boto_apigateway.create_usage_plan(name="some plan", **conn_parameters) + self.assertEqual(result.get("created"), True) + self.assertEqual(result.get("result"), res) - def test_that_when_udpating_a_usage_plan_and_update_usage_plan_throws_an_exception_that_an_error_is_returned(self): - ''' + def test_that_when_udpating_a_usage_plan_and_update_usage_plan_throws_an_exception_that_an_error_is_returned( + self, + ): + """ tests for ClientError - ''' - self.conn.update_usage_plan.side_effect = ClientError(error_content, 'update_usage_plan_exception') - result = boto_apigateway.update_usage_plan(plan_id='plan1_id', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format('update_usage_plan_exception')) + """ + self.conn.update_usage_plan.side_effect = ClientError( + error_content, "update_usage_plan_exception" + ) + result = boto_apigateway.update_usage_plan( + plan_id="plan1_id", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), + error_message.format("update_usage_plan_exception"), + ) - def test_that_when_updating_a_usage_plan_and_if_throttle_and_quota_parameters_are_none_update_usage_plan_removes_throttle_and_quota(self): - ''' + def test_that_when_updating_a_usage_plan_and_if_throttle_and_quota_parameters_are_none_update_usage_plan_removes_throttle_and_quota( + self, + ): + """ tests for throttle and quota removal - ''' - ret = 'some success status' + """ + ret = "some success status" self.conn.update_usage_plan.return_value = ret - result = boto_apigateway.update_usage_plan(plan_id='plan1_id', throttle=None, quota=None, **conn_parameters) - self.assertEqual(result.get('updated'), True) - self.assertEqual(result.get('result'), ret) + result = boto_apigateway.update_usage_plan( + plan_id="plan1_id", throttle=None, quota=None, **conn_parameters + ) + self.assertEqual(result.get("updated"), True) + self.assertEqual(result.get("result"), ret) self.assertTrue(self.conn.update_usage_plan.call_count >= 1) - def test_that_when_deleting_usage_plan_and_describe_usage_plans_had_error_that_the_same_error_is_returned(self): - ''' + def test_that_when_deleting_usage_plan_and_describe_usage_plans_had_error_that_the_same_error_is_returned( + self, + ): + """ tests for error in describe_usage_plans returns error - ''' - ret = 'get_usage_plans_exception' + """ + ret = "get_usage_plans_exception" self.conn.get_usage_plans.side_effect = ClientError(error_content, ret) - result = boto_apigateway.delete_usage_plan(plan_id='some plan id', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format(ret)) - self.assertTrue(self.conn.delete_usage_plan.call_count == 0) - - def test_that_when_deleting_usage_plan_and_plan_exists_that_the_functions_returns_deleted_true(self): - self.conn.get_usage_plans.return_value = usage_plans_ret - ret = 'delete_usage_plan_retval' - self.conn.delete_usage_plan.return_value = ret - result = boto_apigateway.delete_usage_plan(plan_id='plan1_id', **conn_parameters) - self.assertEqual(result.get('deleted'), True) - self.assertEqual(result.get('usagePlanId'), 'plan1_id') - self.assertTrue(self.conn.delete_usage_plan.call_count >= 1) - - def test_that_when_deleting_usage_plan_and_plan_does_not_exist_that_the_functions_returns_deleted_true(self): - ''' - tests for ClientError - ''' - self.conn.get_usage_plans.return_value = dict( - items=[] + result = boto_apigateway.delete_usage_plan( + plan_id="some plan id", **conn_parameters ) - ret = 'delete_usage_plan_retval' - self.conn.delete_usage_plan.return_value = ret - result = boto_apigateway.delete_usage_plan(plan_id='plan1_id', **conn_parameters) - self.assertEqual(result.get('deleted'), True) - self.assertEqual(result.get('usagePlanId'), 'plan1_id') + self.assertEqual(result.get("error").get("message"), error_message.format(ret)) self.assertTrue(self.conn.delete_usage_plan.call_count == 0) - def test_that_when_deleting_usage_plan_and_delete_usage_plan_throws_exception_that_an_error_is_returned(self): - ''' - tests for ClientError - ''' + def test_that_when_deleting_usage_plan_and_plan_exists_that_the_functions_returns_deleted_true( + self, + ): self.conn.get_usage_plans.return_value = usage_plans_ret - error_msg = 'delete_usage_plan_exception' - self.conn.delete_usage_plan.side_effect = ClientError(error_content, error_msg) - result = boto_apigateway.delete_usage_plan(plan_id='plan1_id', **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format(error_msg)) + ret = "delete_usage_plan_retval" + self.conn.delete_usage_plan.return_value = ret + result = boto_apigateway.delete_usage_plan( + plan_id="plan1_id", **conn_parameters + ) + self.assertEqual(result.get("deleted"), True) + self.assertEqual(result.get("usagePlanId"), "plan1_id") self.assertTrue(self.conn.delete_usage_plan.call_count >= 1) - def test_that_attach_or_detach_usage_plan_when_apis_is_empty_that_success_is_returned(self): - ''' - tests for border cases when apis is empty list - ''' - result = boto_apigateway.attach_usage_plan_to_apis(plan_id='plan1_id', apis=[], **conn_parameters) - self.assertEqual(result.get('success'), True) - self.assertEqual(result.get('result', 'no result?'), None) - self.assertTrue(self.conn.update_usage_plan.call_count == 0) - - result = boto_apigateway.detach_usage_plan_from_apis(plan_id='plan1_id', apis=[], **conn_parameters) - self.assertEqual(result.get('success'), True) - self.assertEqual(result.get('result', 'no result?'), None) - self.assertTrue(self.conn.update_usage_plan.call_count == 0) - - def test_that_attach_or_detach_usage_plan_when_api_does_not_contain_apiId_or_stage_that_an_error_is_returned(self): - ''' - tests for invalid key in api object - ''' - for api in ({'apiId': 'some Id'}, {'stage': 'some stage'}, {}): - result = boto_apigateway.attach_usage_plan_to_apis(plan_id='plan1_id', apis=[api], **conn_parameters) - self.assertNotEqual(result.get('error'), None) - - result = boto_apigateway.detach_usage_plan_from_apis(plan_id='plan1_id', apis=[api], **conn_parameters) - self.assertNotEqual(result.get('error'), None) - - self.assertTrue(self.conn.update_usage_plan.call_count == 0) - - def test_that_attach_or_detach_usage_plan_and_update_usage_plan_throws_exception_that_an_error_is_returned(self): - ''' + def test_that_when_deleting_usage_plan_and_plan_does_not_exist_that_the_functions_returns_deleted_true( + self, + ): + """ tests for ClientError - ''' - api = {'apiId': 'some_id', 'stage': 'some_stage'} - error_msg = 'update_usage_plan_exception' + """ + self.conn.get_usage_plans.return_value = dict(items=[]) + ret = "delete_usage_plan_retval" + self.conn.delete_usage_plan.return_value = ret + result = boto_apigateway.delete_usage_plan( + plan_id="plan1_id", **conn_parameters + ) + self.assertEqual(result.get("deleted"), True) + self.assertEqual(result.get("usagePlanId"), "plan1_id") + self.assertTrue(self.conn.delete_usage_plan.call_count == 0) + + def test_that_when_deleting_usage_plan_and_delete_usage_plan_throws_exception_that_an_error_is_returned( + self, + ): + """ + tests for ClientError + """ + self.conn.get_usage_plans.return_value = usage_plans_ret + error_msg = "delete_usage_plan_exception" + self.conn.delete_usage_plan.side_effect = ClientError(error_content, error_msg) + result = boto_apigateway.delete_usage_plan( + plan_id="plan1_id", **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format(error_msg) + ) + self.assertTrue(self.conn.delete_usage_plan.call_count >= 1) + + def test_that_attach_or_detach_usage_plan_when_apis_is_empty_that_success_is_returned( + self, + ): + """ + tests for border cases when apis is empty list + """ + result = boto_apigateway.attach_usage_plan_to_apis( + plan_id="plan1_id", apis=[], **conn_parameters + ) + self.assertEqual(result.get("success"), True) + self.assertEqual(result.get("result", "no result?"), None) + self.assertTrue(self.conn.update_usage_plan.call_count == 0) + + result = boto_apigateway.detach_usage_plan_from_apis( + plan_id="plan1_id", apis=[], **conn_parameters + ) + self.assertEqual(result.get("success"), True) + self.assertEqual(result.get("result", "no result?"), None) + self.assertTrue(self.conn.update_usage_plan.call_count == 0) + + def test_that_attach_or_detach_usage_plan_when_api_does_not_contain_apiId_or_stage_that_an_error_is_returned( + self, + ): + """ + tests for invalid key in api object + """ + for api in ({"apiId": "some Id"}, {"stage": "some stage"}, {}): + result = boto_apigateway.attach_usage_plan_to_apis( + plan_id="plan1_id", apis=[api], **conn_parameters + ) + self.assertNotEqual(result.get("error"), None) + + result = boto_apigateway.detach_usage_plan_from_apis( + plan_id="plan1_id", apis=[api], **conn_parameters + ) + self.assertNotEqual(result.get("error"), None) + + self.assertTrue(self.conn.update_usage_plan.call_count == 0) + + def test_that_attach_or_detach_usage_plan_and_update_usage_plan_throws_exception_that_an_error_is_returned( + self, + ): + """ + tests for ClientError + """ + api = {"apiId": "some_id", "stage": "some_stage"} + error_msg = "update_usage_plan_exception" self.conn.update_usage_plan.side_effect = ClientError(error_content, error_msg) - result = boto_apigateway.attach_usage_plan_to_apis(plan_id='plan1_id', apis=[api], **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format(error_msg)) + result = boto_apigateway.attach_usage_plan_to_apis( + plan_id="plan1_id", apis=[api], **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format(error_msg) + ) - result = boto_apigateway.detach_usage_plan_from_apis(plan_id='plan1_id', apis=[api], **conn_parameters) - self.assertEqual(result.get('error').get('message'), error_message.format(error_msg)) + result = boto_apigateway.detach_usage_plan_from_apis( + plan_id="plan1_id", apis=[api], **conn_parameters + ) + self.assertEqual( + result.get("error").get("message"), error_message.format(error_msg) + ) def test_that_attach_or_detach_usage_plan_updated_successfully(self): - ''' + """ tests for update_usage_plan called - ''' - api = {'apiId': 'some_id', 'stage': 'some_stage'} - attach_ret = 'update_usage_plan_add_op_succeeded' - detach_ret = 'update_usage_plan_remove_op_succeeded' + """ + api = {"apiId": "some_id", "stage": "some_stage"} + attach_ret = "update_usage_plan_add_op_succeeded" + detach_ret = "update_usage_plan_remove_op_succeeded" self.conn.update_usage_plan.side_effect = [attach_ret, detach_ret] - result = boto_apigateway.attach_usage_plan_to_apis(plan_id='plan1_id', apis=[api], **conn_parameters) - self.assertEqual(result.get('success'), True) - self.assertEqual(result.get('result'), attach_ret) + result = boto_apigateway.attach_usage_plan_to_apis( + plan_id="plan1_id", apis=[api], **conn_parameters + ) + self.assertEqual(result.get("success"), True) + self.assertEqual(result.get("result"), attach_ret) - result = boto_apigateway.detach_usage_plan_from_apis(plan_id='plan1_id', apis=[api], **conn_parameters) - self.assertEqual(result.get('success'), True) - self.assertEqual(result.get('result'), detach_ret) + result = boto_apigateway.detach_usage_plan_from_apis( + plan_id="plan1_id", apis=[api], **conn_parameters + ) + self.assertEqual(result.get("success"), True) + self.assertEqual(result.get("result"), detach_ret) diff --git a/tests/unit/modules/test_boto_cloudtrail.py b/tests/unit/modules/test_boto_cloudtrail.py index f044bed6431..43ee95e2b31 100644 --- a/tests/unit/modules/test_boto_cloudtrail.py +++ b/tests/unit/modules/test_boto_cloudtrail.py @@ -2,24 +2,22 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import random import string -import logging - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch -) # Import Salt libs import salt.config import salt.loader import salt.modules.boto_cloudtrail as boto_cloudtrail -from salt.utils.versions import LooseVersion from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module,unused-import @@ -27,6 +25,7 @@ try: import boto import boto3 from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -36,16 +35,16 @@ except ImportError: # the boto_cloudtrail module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' +required_boto3_version = "1.2.1" log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -55,60 +54,61 @@ def _has_required_boto(): if _has_required_boto(): - region = 'us-east-1' - access_key = 'GKTADJGHEIQSXMKKRBJ08H' - secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' - conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} - error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' - not_found_error = ClientError({ - 'Error': { - 'Code': 'TrailNotFoundException', - 'Message': "Test-defined error" - } - }, 'msg') - error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } + region = "us-east-1" + access_key = "GKTADJGHEIQSXMKKRBJ08H" + secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" + conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } - trail_ret = dict(Name='testtrail', - IncludeGlobalServiceEvents=True, - KmsKeyId=None, - LogFileValidationEnabled=False, - S3BucketName='auditinfo', - TrailARN='arn:aws:cloudtrail:us-east-1:214351231622:trail/testtrail') - status_ret = dict(IsLogging=False, - LatestCloudWatchLogsDeliveryError=None, - LatestCloudWatchLogsDeliveryTime=None, - LatestDeliveryError=None, - LatestDeliveryTime=None, - LatestDigestDeliveryError=None, - LatestDigestDeliveryTime=None, - LatestNotificationError=None, - LatestNotificationTime=None, - StartLoggingTime=None, - StopLoggingTime=None) + error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" + ) + not_found_error = ClientError( + {"Error": {"Code": "TrailNotFoundException", "Message": "Test-defined error"}}, + "msg", + ) + error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} + trail_ret = dict( + Name="testtrail", + IncludeGlobalServiceEvents=True, + KmsKeyId=None, + LogFileValidationEnabled=False, + S3BucketName="auditinfo", + TrailARN="arn:aws:cloudtrail:us-east-1:214351231622:trail/testtrail", + ) + status_ret = dict( + IsLogging=False, + LatestCloudWatchLogsDeliveryError=None, + LatestCloudWatchLogsDeliveryTime=None, + LatestDeliveryError=None, + LatestDeliveryTime=None, + LatestDigestDeliveryError=None, + LatestDigestDeliveryTime=None, + LatestNotificationError=None, + LatestNotificationTime=None, + StartLoggingTime=None, + StopLoggingTime=None, + ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) class BotoCloudTrailTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context={}) - return { - boto_cloudtrail: { - '__utils__': utils, - } - } + opts, whitelist=["boto3", "args", "systemd", "path", "platform"], context={} + ) + return {boto_cloudtrail: {"__utils__": utils}} def setUp(self): super(BotoCloudTrailTestCaseBase, self).setUp() @@ -119,16 +119,18 @@ class BotoCloudTrailTestCaseBase(TestCase, LoaderModuleMockMixin): # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn @@ -137,260 +139,349 @@ class BotoCloudTrailTestCaseMixin(object): class BotoCloudTrailTestCase(BotoCloudTrailTestCaseBase, BotoCloudTrailTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_cloudtrail module - ''' + """ - def test_that_when_checking_if_a_trail_exists_and_a_trail_exists_the_trail_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_trail_exists_and_a_trail_exists_the_trail_exists_method_returns_true( + self, + ): + """ Tests checking cloudtrail trail existence when the cloudtrail trail already exists - ''' + """ self.conn.get_trail_status.return_value = trail_ret - result = boto_cloudtrail.exists(Name=trail_ret['Name'], **conn_parameters) + result = boto_cloudtrail.exists(Name=trail_ret["Name"], **conn_parameters) - self.assertTrue(result['exists']) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_a_trail_exists_and_a_trail_does_not_exist_the_trail_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_trail_exists_and_a_trail_does_not_exist_the_trail_exists_method_returns_false( + self, + ): + """ Tests checking cloudtrail trail existence when the cloudtrail trail does not exist - ''' + """ self.conn.get_trail_status.side_effect = not_found_error - result = boto_cloudtrail.exists(Name='mytrail', **conn_parameters) + result = boto_cloudtrail.exists(Name="mytrail", **conn_parameters) - self.assertFalse(result['exists']) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_a_trail_exists_and_boto3_returns_an_error_the_trail_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_a_trail_exists_and_boto3_returns_an_error_the_trail_exists_method_returns_error( + self, + ): + """ Tests checking cloudtrail trail existence when boto returns an error - ''' - self.conn.get_trail_status.side_effect = ClientError(error_content, 'get_trail_status') - result = boto_cloudtrail.exists(Name='mytrail', **conn_parameters) + """ + self.conn.get_trail_status.side_effect = ClientError( + error_content, "get_trail_status" + ) + result = boto_cloudtrail.exists(Name="mytrail", **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('get_trail_status')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("get_trail_status"), + ) - def test_that_when_creating_a_trail_succeeds_the_create_trail_method_returns_true(self): - ''' + def test_that_when_creating_a_trail_succeeds_the_create_trail_method_returns_true( + self, + ): + """ tests True trail created. - ''' + """ self.conn.create_trail.return_value = trail_ret - result = boto_cloudtrail.create(Name=trail_ret['Name'], - S3BucketName=trail_ret['S3BucketName'], - **conn_parameters) + result = boto_cloudtrail.create( + Name=trail_ret["Name"], + S3BucketName=trail_ret["S3BucketName"], + **conn_parameters + ) - self.assertTrue(result['created']) + self.assertTrue(result["created"]) - def test_that_when_creating_a_trail_fails_the_create_trail_method_returns_error(self): - ''' + def test_that_when_creating_a_trail_fails_the_create_trail_method_returns_error( + self, + ): + """ tests False trail not created. - ''' - self.conn.create_trail.side_effect = ClientError(error_content, 'create_trail') - result = boto_cloudtrail.create(Name=trail_ret['Name'], - S3BucketName=trail_ret['S3BucketName'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('create_trail')) + """ + self.conn.create_trail.side_effect = ClientError(error_content, "create_trail") + result = boto_cloudtrail.create( + Name=trail_ret["Name"], + S3BucketName=trail_ret["S3BucketName"], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("create_trail") + ) - def test_that_when_deleting_a_trail_succeeds_the_delete_trail_method_returns_true(self): - ''' + def test_that_when_deleting_a_trail_succeeds_the_delete_trail_method_returns_true( + self, + ): + """ tests True trail deleted. - ''' - result = boto_cloudtrail.delete(Name='testtrail', - **conn_parameters) + """ + result = boto_cloudtrail.delete(Name="testtrail", **conn_parameters) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_a_trail_fails_the_delete_trail_method_returns_false(self): - ''' + def test_that_when_deleting_a_trail_fails_the_delete_trail_method_returns_false( + self, + ): + """ tests False trail not deleted. - ''' - self.conn.delete_trail.side_effect = ClientError(error_content, 'delete_trail') - result = boto_cloudtrail.delete(Name='testtrail', - **conn_parameters) - self.assertFalse(result['deleted']) + """ + self.conn.delete_trail.side_effect = ClientError(error_content, "delete_trail") + result = boto_cloudtrail.delete(Name="testtrail", **conn_parameters) + self.assertFalse(result["deleted"]) - def test_that_when_describing_trail_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_trail_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters if trail exists - ''' - self.conn.describe_trails.return_value = {'trailList': [trail_ret]} + """ + self.conn.describe_trails.return_value = {"trailList": [trail_ret]} - result = boto_cloudtrail.describe(Name=trail_ret['Name'], **conn_parameters) + result = boto_cloudtrail.describe(Name=trail_ret["Name"], **conn_parameters) - self.assertTrue(result['trail']) + self.assertTrue(result["trail"]) - def test_that_when_describing_trail_it_returns_the_dict_of_properties_returns_false(self): - ''' + def test_that_when_describing_trail_it_returns_the_dict_of_properties_returns_false( + self, + ): + """ Tests describing parameters if trail does not exist - ''' + """ self.conn.describe_trails.side_effect = not_found_error - result = boto_cloudtrail.describe(Name='testtrail', **conn_parameters) + result = boto_cloudtrail.describe(Name="testtrail", **conn_parameters) - self.assertFalse(result['trail']) + self.assertFalse(result["trail"]) def test_that_when_describing_trail_on_client_error_it_returns_error(self): - ''' + """ Tests describing parameters failure - ''' - self.conn.describe_trails.side_effect = ClientError(error_content, 'get_trail') - result = boto_cloudtrail.describe(Name='testtrail', **conn_parameters) - self.assertTrue('error' in result) + """ + self.conn.describe_trails.side_effect = ClientError(error_content, "get_trail") + result = boto_cloudtrail.describe(Name="testtrail", **conn_parameters) + self.assertTrue("error" in result) - def test_that_when_getting_status_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_getting_status_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests getting status if trail exists - ''' + """ self.conn.get_trail_status.return_value = status_ret - result = boto_cloudtrail.status(Name=trail_ret['Name'], **conn_parameters) + result = boto_cloudtrail.status(Name=trail_ret["Name"], **conn_parameters) - self.assertTrue(result['trail']) + self.assertTrue(result["trail"]) - def test_that_when_getting_status_it_returns_the_dict_of_properties_returns_false(self): - ''' + def test_that_when_getting_status_it_returns_the_dict_of_properties_returns_false( + self, + ): + """ Tests getting status if trail does not exist - ''' + """ self.conn.get_trail_status.side_effect = not_found_error - result = boto_cloudtrail.status(Name='testtrail', **conn_parameters) + result = boto_cloudtrail.status(Name="testtrail", **conn_parameters) - self.assertFalse(result['trail']) + self.assertFalse(result["trail"]) def test_that_when_getting_status_on_client_error_it_returns_error(self): - ''' + """ Tests getting status failure - ''' - self.conn.get_trail_status.side_effect = ClientError(error_content, 'get_trail_status') - result = boto_cloudtrail.status(Name='testtrail', **conn_parameters) - self.assertTrue('error' in result) + """ + self.conn.get_trail_status.side_effect = ClientError( + error_content, "get_trail_status" + ) + result = boto_cloudtrail.status(Name="testtrail", **conn_parameters) + self.assertTrue("error" in result) - def test_that_when_listing_trails_succeeds_the_list_trails_method_returns_true(self): - ''' + def test_that_when_listing_trails_succeeds_the_list_trails_method_returns_true( + self, + ): + """ tests True trails listed. - ''' - self.conn.describe_trails.return_value = {'trailList': [trail_ret]} + """ + self.conn.describe_trails.return_value = {"trailList": [trail_ret]} result = boto_cloudtrail.list(**conn_parameters) - self.assertTrue(result['trails']) + self.assertTrue(result["trails"]) def test_that_when_listing_trail_fails_the_list_trail_method_returns_false(self): - ''' + """ tests False no trail listed. - ''' - self.conn.describe_trails.return_value = {'trailList': []} + """ + self.conn.describe_trails.return_value = {"trailList": []} result = boto_cloudtrail.list(**conn_parameters) - self.assertFalse(result['trails']) + self.assertFalse(result["trails"]) def test_that_when_listing_trail_fails_the_list_trail_method_returns_error(self): - ''' + """ tests False trail error. - ''' - self.conn.describe_trails.side_effect = ClientError(error_content, 'list_trails') + """ + self.conn.describe_trails.side_effect = ClientError( + error_content, "list_trails" + ) result = boto_cloudtrail.list(**conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('list_trails')) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("list_trails") + ) - def test_that_when_updating_a_trail_succeeds_the_update_trail_method_returns_true(self): - ''' + def test_that_when_updating_a_trail_succeeds_the_update_trail_method_returns_true( + self, + ): + """ tests True trail updated. - ''' + """ self.conn.update_trail.return_value = trail_ret - result = boto_cloudtrail.update(Name=trail_ret['Name'], - S3BucketName=trail_ret['S3BucketName'], - **conn_parameters) + result = boto_cloudtrail.update( + Name=trail_ret["Name"], + S3BucketName=trail_ret["S3BucketName"], + **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_updating_a_trail_fails_the_update_trail_method_returns_error(self): - ''' + def test_that_when_updating_a_trail_fails_the_update_trail_method_returns_error( + self, + ): + """ tests False trail not updated. - ''' - self.conn.update_trail.side_effect = ClientError(error_content, 'update_trail') - result = boto_cloudtrail.update(Name=trail_ret['Name'], - S3BucketName=trail_ret['S3BucketName'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('update_trail')) + """ + self.conn.update_trail.side_effect = ClientError(error_content, "update_trail") + result = boto_cloudtrail.update( + Name=trail_ret["Name"], + S3BucketName=trail_ret["S3BucketName"], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("update_trail") + ) - def test_that_when_starting_logging_succeeds_the_start_logging_method_returns_true(self): - ''' + def test_that_when_starting_logging_succeeds_the_start_logging_method_returns_true( + self, + ): + """ tests True logging started. - ''' - result = boto_cloudtrail.start_logging(Name=trail_ret['Name'], **conn_parameters) + """ + result = boto_cloudtrail.start_logging( + Name=trail_ret["Name"], **conn_parameters + ) - self.assertTrue(result['started']) + self.assertTrue(result["started"]) def test_that_when_start_logging_fails_the_start_logging_method_returns_false(self): - ''' + """ tests False logging not started. - ''' - self.conn.describe_trails.return_value = {'trailList': []} - self.conn.start_logging.side_effect = ClientError(error_content, 'start_logging') - result = boto_cloudtrail.start_logging(Name=trail_ret['Name'], **conn_parameters) - self.assertFalse(result['started']) + """ + self.conn.describe_trails.return_value = {"trailList": []} + self.conn.start_logging.side_effect = ClientError( + error_content, "start_logging" + ) + result = boto_cloudtrail.start_logging( + Name=trail_ret["Name"], **conn_parameters + ) + self.assertFalse(result["started"]) - def test_that_when_stopping_logging_succeeds_the_stop_logging_method_returns_true(self): - ''' + def test_that_when_stopping_logging_succeeds_the_stop_logging_method_returns_true( + self, + ): + """ tests True logging stopped. - ''' - result = boto_cloudtrail.stop_logging(Name=trail_ret['Name'], **conn_parameters) + """ + result = boto_cloudtrail.stop_logging(Name=trail_ret["Name"], **conn_parameters) - self.assertTrue(result['stopped']) + self.assertTrue(result["stopped"]) def test_that_when_stop_logging_fails_the_stop_logging_method_returns_false(self): - ''' + """ tests False logging not stopped. - ''' - self.conn.describe_trails.return_value = {'trailList': []} - self.conn.stop_logging.side_effect = ClientError(error_content, 'stop_logging') - result = boto_cloudtrail.stop_logging(Name=trail_ret['Name'], **conn_parameters) - self.assertFalse(result['stopped']) + """ + self.conn.describe_trails.return_value = {"trailList": []} + self.conn.stop_logging.side_effect = ClientError(error_content, "stop_logging") + result = boto_cloudtrail.stop_logging(Name=trail_ret["Name"], **conn_parameters) + self.assertFalse(result["stopped"]) def test_that_when_adding_tags_succeeds_the_add_tags_method_returns_true(self): - ''' + """ tests True tags added. - ''' - with patch.dict(boto_cloudtrail.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = boto_cloudtrail.add_tags(Name=trail_ret['Name'], a='b', **conn_parameters) + """ + with patch.dict( + boto_cloudtrail.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + result = boto_cloudtrail.add_tags( + Name=trail_ret["Name"], a="b", **conn_parameters + ) - self.assertTrue(result['tagged']) + self.assertTrue(result["tagged"]) def test_that_when_adding_tags_fails_the_add_tags_method_returns_false(self): - ''' + """ tests False tags not added. - ''' - self.conn.add_tags.side_effect = ClientError(error_content, 'add_tags') - with patch.dict(boto_cloudtrail.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = boto_cloudtrail.add_tags(Name=trail_ret['Name'], a='b', **conn_parameters) - self.assertFalse(result['tagged']) + """ + self.conn.add_tags.side_effect = ClientError(error_content, "add_tags") + with patch.dict( + boto_cloudtrail.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + result = boto_cloudtrail.add_tags( + Name=trail_ret["Name"], a="b", **conn_parameters + ) + self.assertFalse(result["tagged"]) def test_that_when_removing_tags_succeeds_the_remove_tags_method_returns_true(self): - ''' + """ tests True tags removed. - ''' - with patch.dict(boto_cloudtrail.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = boto_cloudtrail.remove_tags(Name=trail_ret['Name'], a='b', **conn_parameters) + """ + with patch.dict( + boto_cloudtrail.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + result = boto_cloudtrail.remove_tags( + Name=trail_ret["Name"], a="b", **conn_parameters + ) - self.assertTrue(result['tagged']) + self.assertTrue(result["tagged"]) def test_that_when_removing_tags_fails_the_remove_tags_method_returns_false(self): - ''' + """ tests False tags not removed. - ''' - self.conn.remove_tags.side_effect = ClientError(error_content, 'remove_tags') - with patch.dict(boto_cloudtrail.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = boto_cloudtrail.remove_tags(Name=trail_ret['Name'], a='b', **conn_parameters) - self.assertFalse(result['tagged']) + """ + self.conn.remove_tags.side_effect = ClientError(error_content, "remove_tags") + with patch.dict( + boto_cloudtrail.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + result = boto_cloudtrail.remove_tags( + Name=trail_ret["Name"], a="b", **conn_parameters + ) + self.assertFalse(result["tagged"]) def test_that_when_listing_tags_succeeds_the_list_tags_method_returns_true(self): - ''' + """ tests True tags listed. - ''' - with patch.dict(boto_cloudtrail.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = boto_cloudtrail.list_tags(Name=trail_ret['Name'], **conn_parameters) + """ + with patch.dict( + boto_cloudtrail.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + result = boto_cloudtrail.list_tags( + Name=trail_ret["Name"], **conn_parameters + ) - self.assertEqual(result['tags'], {}) + self.assertEqual(result["tags"], {}) def test_that_when_listing_tags_fails_the_list_tags_method_returns_false(self): - ''' + """ tests False tags not listed. - ''' - self.conn.list_tags.side_effect = ClientError(error_content, 'list_tags') - with patch.dict(boto_cloudtrail.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = boto_cloudtrail.list_tags(Name=trail_ret['Name'], **conn_parameters) - self.assertTrue(result['error']) + """ + self.conn.list_tags.side_effect = ClientError(error_content, "list_tags") + with patch.dict( + boto_cloudtrail.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + result = boto_cloudtrail.list_tags( + Name=trail_ret["Name"], **conn_parameters + ) + self.assertTrue(result["error"]) diff --git a/tests/unit/modules/test_boto_cloudwatch_event.py b/tests/unit/modules/test_boto_cloudwatch_event.py index a191b9be4d8..1b2a4927f59 100644 --- a/tests/unit/modules/test_boto_cloudwatch_event.py +++ b/tests/unit/modules/test_boto_cloudwatch_event.py @@ -2,17 +2,10 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import random import string -import logging - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch -) # Import Salt libs import salt.config @@ -20,6 +13,11 @@ import salt.loader import salt.modules.boto_cloudwatch_event as boto_cloudwatch_event from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + # Import 3rd-party libs # pylint: disable=import-error,no-name-in-module,unused-import try: @@ -27,6 +25,7 @@ try: import boto3 from botocore.exceptions import ClientError from botocore import __version__ as found_botocore_version + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -36,10 +35,10 @@ log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False else: @@ -47,27 +46,32 @@ def _has_required_boto(): if _has_required_boto(): - region = 'us-east-1' - access_key = 'GKTADJGHEIQSXMKKRBJ08H' - secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' - conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} - error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' - not_found_error = ClientError({ - 'Error': { - 'Code': 'ResourceNotFoundException', - 'Message': "Test-defined error" - } - }, 'msg') - error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } + region = "us-east-1" + access_key = "GKTADJGHEIQSXMKKRBJ08H" + secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" + conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } - rule_name = 'test_thing_type' - rule_desc = 'test_thing_type_desc' - rule_sched = 'rate(20 min)' - rule_arn = 'arn:::::rule/arn' + error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" + ) + not_found_error = ClientError( + { + "Error": { + "Code": "ResourceNotFoundException", + "Message": "Test-defined error", + } + }, + "msg", + ) + error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} + rule_name = "test_thing_type" + rule_desc = "test_thing_type_desc" + rule_sched = "rate(20 min)" + rule_arn = "arn:::::rule/arn" rule_ret = dict( Arn=rule_arn, Description=rule_desc, @@ -75,14 +79,10 @@ if _has_required_boto(): Name=rule_name, RoleArn=None, ScheduleExpression=rule_sched, - State='ENABLED' - ) - create_rule_ret = dict( - Name=rule_name, - ) - target_ret = dict( - Id='target1', + State="ENABLED", ) + create_rule_ret = dict(Name=rule_name,) + target_ret = dict(Id="target1",) class BotoCloudWatchEventTestCaseBase(TestCase, LoaderModuleMockMixin): @@ -91,10 +91,9 @@ class BotoCloudWatchEventTestCaseBase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context={}) - return {boto_cloudwatch_event: {'__utils__': utils}} + opts, whitelist=["boto3", "args", "systemd", "path", "platform"], context={} + ) + return {boto_cloudwatch_event: {"__utils__": utils}} def setUp(self): super(BotoCloudWatchEventTestCaseBase, self).setUp() @@ -105,16 +104,18 @@ class BotoCloudWatchEventTestCaseBase(TestCase, LoaderModuleMockMixin): # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn @@ -122,156 +123,208 @@ class BotoCloudWatchEventTestCaseMixin(object): pass -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -class BotoCloudWatchEventTestCase(BotoCloudWatchEventTestCaseBase, BotoCloudWatchEventTestCaseMixin): - ''' +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +class BotoCloudWatchEventTestCase( + BotoCloudWatchEventTestCaseBase, BotoCloudWatchEventTestCaseMixin +): + """ TestCase for salt.modules.boto_cloudwatch_event module - ''' + """ - def test_that_when_checking_if_a_rule_exists_and_a_rule_exists_the_rule_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_rule_exists_and_a_rule_exists_the_rule_exists_method_returns_true( + self, + ): + """ Tests checking event existence when the event already exists - ''' - self.conn.list_rules.return_value = {'Rules': [rule_ret]} + """ + self.conn.list_rules.return_value = {"Rules": [rule_ret]} result = boto_cloudwatch_event.exists(Name=rule_name, **conn_parameters) - self.assertTrue(result['exists']) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_a_rule_exists_and_a_rule_does_not_exist_the_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_rule_exists_and_a_rule_does_not_exist_the_exists_method_returns_false( + self, + ): + """ Tests checking rule existence when the rule does not exist - ''' - self.conn.list_rules.return_value = {'Rules': []} + """ + self.conn.list_rules.return_value = {"Rules": []} result = boto_cloudwatch_event.exists(Name=rule_name, **conn_parameters) - self.assertFalse(result['exists']) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_a_rule_exists_and_boto3_returns_an_error_the_rule_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_a_rule_exists_and_boto3_returns_an_error_the_rule_exists_method_returns_error( + self, + ): + """ Tests checking rule existence when boto returns an error - ''' - self.conn.list_rules.side_effect = ClientError(error_content, 'list_rules') + """ + self.conn.list_rules.side_effect = ClientError(error_content, "list_rules") result = boto_cloudwatch_event.exists(Name=rule_name, **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('list_rules')) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("list_rules") + ) - def test_that_when_describing_rule_and_rule_exists_the_describe_rule_method_returns_rule(self): - ''' + def test_that_when_describing_rule_and_rule_exists_the_describe_rule_method_returns_rule( + self, + ): + """ Tests describe rule for an existing rule - ''' + """ self.conn.describe_rule.return_value = rule_ret result = boto_cloudwatch_event.describe(Name=rule_name, **conn_parameters) - self.assertEqual(result.get('rule'), rule_ret) + self.assertEqual(result.get("rule"), rule_ret) - def test_that_when_describing_rule_and_rule_does_not_exists_the_describe_method_returns_none(self): - ''' + def test_that_when_describing_rule_and_rule_does_not_exists_the_describe_method_returns_none( + self, + ): + """ Tests describe rule for an non existent rule - ''' + """ self.conn.describe_rule.side_effect = not_found_error result = boto_cloudwatch_event.describe(Name=rule_name, **conn_parameters) - self.assertNotEqual(result.get('error'), None) + self.assertNotEqual(result.get("error"), None) - def test_that_when_describing_rule_and_boto3_returns_error_the_describe_method_returns_error(self): - self.conn.describe_rule.side_effect = ClientError(error_content, 'describe_rule') + def test_that_when_describing_rule_and_boto3_returns_error_the_describe_method_returns_error( + self, + ): + self.conn.describe_rule.side_effect = ClientError( + error_content, "describe_rule" + ) result = boto_cloudwatch_event.describe(Name=rule_name, **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('describe_rule')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("describe_rule"), + ) - def test_that_when_creating_a_rule_succeeds_the_create_rule_method_returns_true(self): - ''' + def test_that_when_creating_a_rule_succeeds_the_create_rule_method_returns_true( + self, + ): + """ tests True when rule created - ''' + """ self.conn.put_rule.return_value = create_rule_ret - result = boto_cloudwatch_event.create_or_update(Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - **conn_parameters) - self.assertTrue(result['created']) + result = boto_cloudwatch_event.create_or_update( + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + **conn_parameters + ) + self.assertTrue(result["created"]) def test_that_when_creating_a_rule_fails_the_create_method_returns_error(self): - ''' + """ tests False when rule not created - ''' - self.conn.put_rule.side_effect = ClientError(error_content, 'put_rule') - result = boto_cloudwatch_event.create_or_update(Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('put_rule')) + """ + self.conn.put_rule.side_effect = ClientError(error_content, "put_rule") + result = boto_cloudwatch_event.create_or_update( + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("put_rule") + ) def test_that_when_deleting_a_rule_succeeds_the_delete_method_returns_true(self): - ''' + """ tests True when delete rule succeeds - ''' + """ self.conn.delete_rule.return_value = {} result = boto_cloudwatch_event.delete(Name=rule_name, **conn_parameters) - self.assertTrue(result.get('deleted')) - self.assertEqual(result.get('error'), None) + self.assertTrue(result.get("deleted")) + self.assertEqual(result.get("error"), None) def test_that_when_deleting_a_rule_fails_the_delete_method_returns_error(self): - ''' + """ tests False when delete rule fails - ''' - self.conn.delete_rule.side_effect = ClientError(error_content, 'delete_rule') + """ + self.conn.delete_rule.side_effect = ClientError(error_content, "delete_rule") result = boto_cloudwatch_event.delete(Name=rule_name, **conn_parameters) - self.assertFalse(result.get('deleted')) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('delete_rule')) + self.assertFalse(result.get("deleted")) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("delete_rule") + ) - def test_that_when_listing_targets_and_rule_exists_the_list_targets_method_returns_targets(self): - ''' + def test_that_when_listing_targets_and_rule_exists_the_list_targets_method_returns_targets( + self, + ): + """ Tests listing targets for an existing rule - ''' - self.conn.list_targets_by_rule.return_value = {'Targets': [target_ret]} + """ + self.conn.list_targets_by_rule.return_value = {"Targets": [target_ret]} result = boto_cloudwatch_event.list_targets(Rule=rule_name, **conn_parameters) - self.assertEqual(result.get('targets'), [target_ret]) + self.assertEqual(result.get("targets"), [target_ret]) - def test_that_when_listing_targets_and_rule_does_not_exist_the_list_targets_method_returns_error(self): - ''' + def test_that_when_listing_targets_and_rule_does_not_exist_the_list_targets_method_returns_error( + self, + ): + """ Tests list targets for an non existent rule - ''' + """ self.conn.list_targets_by_rule.side_effect = not_found_error result = boto_cloudwatch_event.list_targets(Rule=rule_name, **conn_parameters) - self.assertNotEqual(result.get('error'), None) + self.assertNotEqual(result.get("error"), None) - def test_that_when_putting_targets_succeeds_the_put_target_method_returns_no_failures(self): - ''' + def test_that_when_putting_targets_succeeds_the_put_target_method_returns_no_failures( + self, + ): + """ tests None when targets added - ''' - self.conn.put_targets.return_value = {'FailedEntryCount': 0} - result = boto_cloudwatch_event.put_targets(Rule=rule_name, - Targets=[], - **conn_parameters) - self.assertIsNone(result['failures']) + """ + self.conn.put_targets.return_value = {"FailedEntryCount": 0} + result = boto_cloudwatch_event.put_targets( + Rule=rule_name, Targets=[], **conn_parameters + ) + self.assertIsNone(result["failures"]) def test_that_when_putting_targets_fails_the_put_targets_method_returns_error(self): - ''' + """ tests False when thing type not created - ''' - self.conn.put_targets.side_effect = ClientError(error_content, 'put_targets') - result = boto_cloudwatch_event.put_targets(Rule=rule_name, - Targets=[], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('put_targets')) + """ + self.conn.put_targets.side_effect = ClientError(error_content, "put_targets") + result = boto_cloudwatch_event.put_targets( + Rule=rule_name, Targets=[], **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("put_targets") + ) - def test_that_when_removing_targets_succeeds_the_remove_targets_method_returns_true(self): - ''' + def test_that_when_removing_targets_succeeds_the_remove_targets_method_returns_true( + self, + ): + """ tests True when remove targets succeeds - ''' - self.conn.remove_targets.return_value = {'FailedEntryCount': 0} - result = boto_cloudwatch_event.remove_targets(Rule=rule_name, Ids=[], **conn_parameters) + """ + self.conn.remove_targets.return_value = {"FailedEntryCount": 0} + result = boto_cloudwatch_event.remove_targets( + Rule=rule_name, Ids=[], **conn_parameters + ) - self.assertIsNone(result['failures']) - self.assertEqual(result.get('error'), None) + self.assertIsNone(result["failures"]) + self.assertEqual(result.get("error"), None) - def test_that_when_removing_targets_fails_the_remove_targets_method_returns_error(self): - ''' + def test_that_when_removing_targets_fails_the_remove_targets_method_returns_error( + self, + ): + """ tests False when remove targets fails - ''' - self.conn.remove_targets.side_effect = ClientError(error_content, 'remove_targets') - result = boto_cloudwatch_event.remove_targets(Rule=rule_name, Ids=[], **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('remove_targets')) + """ + self.conn.remove_targets.side_effect = ClientError( + error_content, "remove_targets" + ) + result = boto_cloudwatch_event.remove_targets( + Rule=rule_name, Ids=[], **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("remove_targets"), + ) diff --git a/tests/unit/modules/test_boto_cognitoidentity.py b/tests/unit/modules/test_boto_cognitoidentity.py index 2661547b32a..cc65eaf5d7e 100644 --- a/tests/unit/modules/test_boto_cognitoidentity.py +++ b/tests/unit/modules/test_boto_cognitoidentity.py @@ -2,20 +2,22 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import random import string -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch - # Import Salt libs import salt.config import salt.loader -from salt.utils.versions import LooseVersion import salt.modules.boto_cognitoidentity as boto_cognitoidentity +from salt.ext.six.moves import range # pylint: disable=import-error +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # Import 3rd-party libs @@ -23,87 +25,102 @@ import salt.modules.boto_cognitoidentity as boto_cognitoidentity try: import boto3 from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False -from salt.ext.six.moves import range # pylint: disable=import-error # pylint: enable=import-error,no-name-in-module # the boto_lambda module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' +required_boto3_version = "1.2.1" -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} -error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' -error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } +error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" +) +error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} -first_pool_id = 'first_pool_id' -first_pool_name = 'first_pool' -second_pool_id = 'second_pool_id' -second_pool_name = 'second_pool' -second_pool_name_updated = 'second_pool_updated' -third_pool_id = 'third_pool_id' +first_pool_id = "first_pool_id" +first_pool_name = "first_pool" +second_pool_id = "second_pool_id" +second_pool_name = "second_pool" +second_pool_name_updated = "second_pool_updated" +third_pool_id = "third_pool_id" third_pool_name = first_pool_name -identity_pools_ret = dict(IdentityPools=[dict(IdentityPoolId=first_pool_id, - IdentityPoolName=first_pool_name), - dict(IdentityPoolId=second_pool_id, - IdentityPoolName=second_pool_name), - dict(IdentityPoolId=third_pool_id, - IdentityPoolName=third_pool_name)]) +identity_pools_ret = dict( + IdentityPools=[ + dict(IdentityPoolId=first_pool_id, IdentityPoolName=first_pool_name), + dict(IdentityPoolId=second_pool_id, IdentityPoolName=second_pool_name), + dict(IdentityPoolId=third_pool_id, IdentityPoolName=third_pool_name), + ] +) -first_pool_ret = dict(IdentityPoolId=first_pool_id, - IdentityPoolName=first_pool_name, - AllowUnauthenticatedIdentities=False, - SupportedLoginProviders={'accounts.google.com': 'testing123', - 'api.twitter.com': 'testing123', - 'graph.facebook.com': 'testing123', - 'www.amazon.com': 'testing123'}, - DeveloperProviderName='test_provider', - OpenIdConnectProviderARNs=['some_provider_arn', - 'another_provider_arn']) +first_pool_ret = dict( + IdentityPoolId=first_pool_id, + IdentityPoolName=first_pool_name, + AllowUnauthenticatedIdentities=False, + SupportedLoginProviders={ + "accounts.google.com": "testing123", + "api.twitter.com": "testing123", + "graph.facebook.com": "testing123", + "www.amazon.com": "testing123", + }, + DeveloperProviderName="test_provider", + OpenIdConnectProviderARNs=["some_provider_arn", "another_provider_arn"], +) -first_pool_role_ret = dict(IdentityPoolId=first_pool_id, - Roles=dict(authenticated='first_pool_auth_role', - unauthenticated='first_pool_unauth_role')) +first_pool_role_ret = dict( + IdentityPoolId=first_pool_id, + Roles=dict( + authenticated="first_pool_auth_role", unauthenticated="first_pool_unauth_role" + ), +) -second_pool_ret = dict(IdentityPoolId=second_pool_id, - IdentityPoolName=second_pool_name, - AllowUnauthenticatedIdentities=False) +second_pool_ret = dict( + IdentityPoolId=second_pool_id, + IdentityPoolName=second_pool_name, + AllowUnauthenticatedIdentities=False, +) -third_pool_ret = dict(IdentityPoolId=third_pool_id, - IdentityPoolName=third_pool_name, - AllowUnauthenticatedIdentities=False, - DeveloperProviderName='test_provider2') +third_pool_ret = dict( + IdentityPoolId=third_pool_id, + IdentityPoolName=third_pool_name, + AllowUnauthenticatedIdentities=False, + DeveloperProviderName="test_provider2", +) third_pool_role_ret = dict(IdentityPoolId=third_pool_id) -default_pool_ret = dict(IdentityPoolId='default_pool_id', - IdentityPoolName='default_pool_name', - AllowUnauthenticatedIdentities=False, - DeveloperProviderName='test_provider_default') +default_pool_ret = dict( + IdentityPoolId="default_pool_id", + IdentityPoolName="default_pool_name", + AllowUnauthenticatedIdentities=False, + DeveloperProviderName="test_provider_default", +) -default_pool_role_ret = dict(IdentityPoolId='default_pool_id') +default_pool_role_ret = dict(IdentityPoolId="default_pool_id") log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -118,10 +135,9 @@ class BotoCognitoIdentityTestCaseBase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context={}) - return {boto_cognitoidentity: {'__utils__': utils}} + opts, whitelist=["boto3", "args", "systemd", "path", "platform"], context={} + ) + return {boto_cognitoidentity: {"__utils__": utils}} def setUp(self): super(BotoCognitoIdentityTestCaseBase, self).setUp() @@ -132,16 +148,18 @@ class BotoCognitoIdentityTestCaseBase(TestCase, LoaderModuleMockMixin): # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn @@ -149,368 +167,574 @@ class BotoCognitoIdentityTestCaseMixin(object): pass -@skipIf(True, 'Skip these tests while investigating failures') -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -class BotoCognitoIdentityTestCase(BotoCognitoIdentityTestCaseBase, BotoCognitoIdentityTestCaseMixin): - ''' +@skipIf(True, "Skip these tests while investigating failures") +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) +class BotoCognitoIdentityTestCase( + BotoCognitoIdentityTestCaseBase, BotoCognitoIdentityTestCaseMixin +): + """ TestCase for salt.modules.boto_cognitoidentity module - ''' + """ def _describe_identity_pool_side_effect(self, *args, **kwargs): - if kwargs.get('IdentityPoolId') == first_pool_id: + if kwargs.get("IdentityPoolId") == first_pool_id: return first_pool_ret - elif kwargs.get('IdentityPoolId') == third_pool_id: + elif kwargs.get("IdentityPoolId") == third_pool_id: return third_pool_ret else: return default_pool_ret - def test_that_when_describing_a_named_identity_pool_and_pool_exists_the_describe_identity_pool_method_returns_pools_properties(self): - ''' + def test_that_when_describing_a_named_identity_pool_and_pool_exists_the_describe_identity_pool_method_returns_pools_properties( + self, + ): + """ Tests describing identity pool when the pool's name exists - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect - result = boto_cognitoidentity.describe_identity_pools(IdentityPoolName=first_pool_name, **conn_parameters) - self.assertEqual(result.get('identity_pools'), [first_pool_ret, third_pool_ret]) + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) + result = boto_cognitoidentity.describe_identity_pools( + IdentityPoolName=first_pool_name, **conn_parameters + ) + self.assertEqual(result.get("identity_pools"), [first_pool_ret, third_pool_ret]) - def test_that_when_describing_a_identity_pool_by_its_id_and_pool_exists_the_desribe_identity_pool_method_returns_pools_properties(self): - ''' + def test_that_when_describing_a_identity_pool_by_its_id_and_pool_exists_the_desribe_identity_pool_method_returns_pools_properties( + self, + ): + """ Tests describing identity pool when the given pool's id exists - ''' + """ self.conn.describe_identity_pool.return_value = third_pool_ret - result = boto_cognitoidentity.describe_identity_pools(IdentityPoolName='', IdentityPoolId=third_pool_id, **conn_parameters) - self.assertEqual(result.get('identity_pools'), [third_pool_ret]) + result = boto_cognitoidentity.describe_identity_pools( + IdentityPoolName="", IdentityPoolId=third_pool_id, **conn_parameters + ) + self.assertEqual(result.get("identity_pools"), [third_pool_ret]) self.assertTrue(self.conn.list_identity_pools.call_count == 0) - def test_that_when_describing_a_named_identity_pool_and_pool_does_not_exist_the_describe_identity_pool_method_returns_none(self): - ''' + def test_that_when_describing_a_named_identity_pool_and_pool_does_not_exist_the_describe_identity_pool_method_returns_none( + self, + ): + """ Tests describing identity pool when the pool's name doesn't exist - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret self.conn.describe_identity_pool.return_value = first_pool_ret - result = boto_cognitoidentity.describe_identity_pools(IdentityPoolName='no_such_pool', **conn_parameters) - self.assertEqual(result.get('identity_pools', 'no such key'), None) + result = boto_cognitoidentity.describe_identity_pools( + IdentityPoolName="no_such_pool", **conn_parameters + ) + self.assertEqual(result.get("identity_pools", "no such key"), None) - def test_that_when_describing_a_named_identity_pool_and_error_thrown_the_describe_identity_pool_method_returns_error(self): - ''' + def test_that_when_describing_a_named_identity_pool_and_error_thrown_the_describe_identity_pool_method_returns_error( + self, + ): + """ Tests describing identity pool returns error when there is an exception to boto3 calls - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = ClientError(error_content, 'error on describe identity pool') - result = boto_cognitoidentity.describe_identity_pools(IdentityPoolName=first_pool_name, **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('error on describe identity pool')) + self.conn.describe_identity_pool.side_effect = ClientError( + error_content, "error on describe identity pool" + ) + result = boto_cognitoidentity.describe_identity_pools( + IdentityPoolName=first_pool_name, **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("error on describe identity pool"), + ) - def test_that_when_create_identity_pool_the_create_identity_pool_method_returns_created_identity_pool(self): - ''' + def test_that_when_create_identity_pool_the_create_identity_pool_method_returns_created_identity_pool( + self, + ): + """ Tests the positive case where create identity pool succeeds - ''' + """ return_val = default_pool_ret.copy() - return_val.pop('DeveloperProviderName', None) + return_val.pop("DeveloperProviderName", None) self.conn.create_identity_pool.return_value = return_val - result = boto_cognitoidentity.create_identity_pool(IdentityPoolName='default_pool_name', **conn_parameters) + result = boto_cognitoidentity.create_identity_pool( + IdentityPoolName="default_pool_name", **conn_parameters + ) mock_calls = self.conn.mock_calls - #name, args, kwargs = mock_calls[0] - self.assertTrue(result.get('created')) + # name, args, kwargs = mock_calls[0] + self.assertTrue(result.get("created")) self.assertEqual(len(mock_calls), 1) - self.assertEqual(mock_calls[0][0], 'create_identity_pool') - self.assertNotIn('DeveloperProviderName', mock_calls[0][2]) + self.assertEqual(mock_calls[0][0], "create_identity_pool") + self.assertNotIn("DeveloperProviderName", mock_calls[0][2]) - def test_that_when_create_identity_pool_and_error_thrown_the_create_identity_pool_method_returns_error(self): - ''' + def test_that_when_create_identity_pool_and_error_thrown_the_create_identity_pool_method_returns_error( + self, + ): + """ Tests the negative case where create identity pool has a boto3 client error - ''' - self.conn.create_identity_pool.side_effect = ClientError(error_content, 'create_identity_pool') - result = boto_cognitoidentity.create_identity_pool(IdentityPoolName='default_pool_name', **conn_parameters) - self.assertIs(result.get('created'), False) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('create_identity_pool')) + """ + self.conn.create_identity_pool.side_effect = ClientError( + error_content, "create_identity_pool" + ) + result = boto_cognitoidentity.create_identity_pool( + IdentityPoolName="default_pool_name", **conn_parameters + ) + self.assertIs(result.get("created"), False) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("create_identity_pool"), + ) - def test_that_when_delete_identity_pools_with_multiple_matching_pool_names_the_delete_identity_pools_methos_returns_true_and_deleted_count(self): - ''' + def test_that_when_delete_identity_pools_with_multiple_matching_pool_names_the_delete_identity_pools_methos_returns_true_and_deleted_count( + self, + ): + """ Tests that given 2 matching pool ids, the operation returns deleted status of true and count 2 - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret self.conn.delete_identity_pool.return_value = None - result = boto_cognitoidentity.delete_identity_pools(IdentityPoolName=first_pool_name, **conn_parameters) + result = boto_cognitoidentity.delete_identity_pools( + IdentityPoolName=first_pool_name, **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertTrue(result.get('deleted')) - self.assertEqual(result.get('count'), 2) + self.assertTrue(result.get("deleted")) + self.assertEqual(result.get("count"), 2) self.assertEqual(len(mock_calls), 3) - self.assertEqual(mock_calls[1][0], 'delete_identity_pool') - self.assertEqual(mock_calls[2][0], 'delete_identity_pool') - self.assertEqual(mock_calls[1][2].get('IdentityPoolId'), first_pool_id) - self.assertEqual(mock_calls[2][2].get('IdentityPoolId'), third_pool_id) + self.assertEqual(mock_calls[1][0], "delete_identity_pool") + self.assertEqual(mock_calls[2][0], "delete_identity_pool") + self.assertEqual(mock_calls[1][2].get("IdentityPoolId"), first_pool_id) + self.assertEqual(mock_calls[2][2].get("IdentityPoolId"), third_pool_id) - def test_that_when_delete_identity_pools_with_no_matching_pool_names_the_delete_identity_pools_method_returns_false(self): - ''' + def test_that_when_delete_identity_pools_with_no_matching_pool_names_the_delete_identity_pools_method_returns_false( + self, + ): + """ Tests that the given pool name does not exist, the operation returns deleted status of false and count 0 - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - result = boto_cognitoidentity.delete_identity_pools(IdentityPoolName='no_such_pool_name', **conn_parameters) + result = boto_cognitoidentity.delete_identity_pools( + IdentityPoolName="no_such_pool_name", **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertIs(result.get('deleted'), False) - self.assertEqual(result.get('count'), 0) + self.assertIs(result.get("deleted"), False) + self.assertEqual(result.get("count"), 0) self.assertEqual(len(mock_calls), 1) - def test_that_when_delete_identity_pools_and_error_thrown_the_delete_identity_pools_method_returns_false_and_the_error(self): - ''' + def test_that_when_delete_identity_pools_and_error_thrown_the_delete_identity_pools_method_returns_false_and_the_error( + self, + ): + """ Tests that the delete_identity_pool method throws an exception - ''' - self.conn.delete_identity_pool.side_effect = ClientError(error_content, 'delete_identity_pool') + """ + self.conn.delete_identity_pool.side_effect = ClientError( + error_content, "delete_identity_pool" + ) # try to delete an unexistent pool id "no_such_pool_id" - result = boto_cognitoidentity.delete_identity_pools(IdentityPoolName=first_pool_name, IdentityPoolId='no_such_pool_id', **conn_parameters) + result = boto_cognitoidentity.delete_identity_pools( + IdentityPoolName=first_pool_name, + IdentityPoolId="no_such_pool_id", + **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertIs(result.get('deleted'), False) - self.assertIs(result.get('count'), None) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('delete_identity_pool')) + self.assertIs(result.get("deleted"), False) + self.assertIs(result.get("count"), None) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("delete_identity_pool"), + ) self.assertEqual(len(mock_calls), 1) def _get_identity_pool_roles_side_effect(self, *args, **kwargs): - if kwargs.get('IdentityPoolId') == first_pool_id: + if kwargs.get("IdentityPoolId") == first_pool_id: return first_pool_role_ret - elif kwargs.get('IdentityPoolId') == third_pool_id: + elif kwargs.get("IdentityPoolId") == third_pool_id: return third_pool_role_ret else: return default_pool_role_ret - def test_that_when_get_identity_pool_roles_with_matching_pool_names_the_get_identity_pool_roles_method_returns_pools_role_properties(self): - ''' + def test_that_when_get_identity_pool_roles_with_matching_pool_names_the_get_identity_pool_roles_method_returns_pools_role_properties( + self, + ): + """ Tests that the given 2 pool id's matching the given pool name, the results are passed through as a list of identity_pool_roles - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.get_identity_pool_roles.side_effect = self._get_identity_pool_roles_side_effect - result = boto_cognitoidentity.get_identity_pool_roles(IdentityPoolName=first_pool_name, **conn_parameters) - id_pool_roles = result.get('identity_pool_roles') + self.conn.get_identity_pool_roles.side_effect = ( + self._get_identity_pool_roles_side_effect + ) + result = boto_cognitoidentity.get_identity_pool_roles( + IdentityPoolName=first_pool_name, **conn_parameters + ) + id_pool_roles = result.get("identity_pool_roles") self.assertIsNot(id_pool_roles, None) - self.assertEqual(result.get('error', 'key_should_not_be_there'), 'key_should_not_be_there') + self.assertEqual( + result.get("error", "key_should_not_be_there"), "key_should_not_be_there" + ) self.assertEqual(len(id_pool_roles), 2) self.assertEqual(id_pool_roles[0], first_pool_role_ret) self.assertEqual(id_pool_roles[1], third_pool_role_ret) - def test_that_when_get_identity_pool_roles_with_no_matching_pool_names_the_get_identity_pool_roles_method_returns_none(self): - ''' + def test_that_when_get_identity_pool_roles_with_no_matching_pool_names_the_get_identity_pool_roles_method_returns_none( + self, + ): + """ Tests that the given no pool id's matching the given pool name, the results returned is None and get_identity_pool_roles should never be called - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - result = boto_cognitoidentity.get_identity_pool_roles(IdentityPoolName="no_such_pool_name", **conn_parameters) + result = boto_cognitoidentity.get_identity_pool_roles( + IdentityPoolName="no_such_pool_name", **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertIs(result.get('identity_pool_roles', 'key_should_be_there'), None) + self.assertIs(result.get("identity_pool_roles", "key_should_be_there"), None) self.assertEqual(len(mock_calls), 1) - self.assertEqual(result.get('error', 'key_should_not_be_there'), 'key_should_not_be_there') + self.assertEqual( + result.get("error", "key_should_not_be_there"), "key_should_not_be_there" + ) - def test_that_when_get_identity_pool_roles_and_error_thrown_due_to_invalid_pool_id_the_get_identity_pool_roles_method_returns_error(self): - ''' + def test_that_when_get_identity_pool_roles_and_error_thrown_due_to_invalid_pool_id_the_get_identity_pool_roles_method_returns_error( + self, + ): + """ Tests that given an invalid pool id, we properly handle error generated from get_identity_pool_roles - ''' - self.conn.get_identity_pool_roles.side_effect = ClientError(error_content, 'get_identity_pool_roles') + """ + self.conn.get_identity_pool_roles.side_effect = ClientError( + error_content, "get_identity_pool_roles" + ) # try to delete an unexistent pool id "no_such_pool_id" - result = boto_cognitoidentity.get_identity_pool_roles(IdentityPoolName='', IdentityPoolId='no_such_pool_id', **conn_parameters) + result = boto_cognitoidentity.get_identity_pool_roles( + IdentityPoolName="", IdentityPoolId="no_such_pool_id", **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertEqual(result.get('error', {}).get('message'), error_message.format('get_identity_pool_roles')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("get_identity_pool_roles"), + ) self.assertEqual(len(mock_calls), 1) - def test_that_when_set_identity_pool_roles_with_invalid_pool_id_the_set_identity_pool_roles_method_returns_set_false_and_error(self): - ''' + def test_that_when_set_identity_pool_roles_with_invalid_pool_id_the_set_identity_pool_roles_method_returns_set_false_and_error( + self, + ): + """ Tests that given an invalid pool id, we properly handle error generated from set_identity_pool_roles - ''' - self.conn.set_identity_pool_roles.side_effect = ClientError(error_content, 'set_identity_pool_roles') - result = boto_cognitoidentity.set_identity_pool_roles(IdentityPoolId='no_such_pool_id', **conn_parameters) + """ + self.conn.set_identity_pool_roles.side_effect = ClientError( + error_content, "set_identity_pool_roles" + ) + result = boto_cognitoidentity.set_identity_pool_roles( + IdentityPoolId="no_such_pool_id", **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertIs(result.get('set'), False) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('set_identity_pool_roles')) + self.assertIs(result.get("set"), False) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("set_identity_pool_roles"), + ) self.assertEqual(len(mock_calls), 1) - def test_that_when_set_identity_pool_roles_with_no_roles_specified_the_set_identity_pool_roles_method_unset_the_roles(self): - ''' + def test_that_when_set_identity_pool_roles_with_no_roles_specified_the_set_identity_pool_roles_method_unset_the_roles( + self, + ): + """ Tests that given a valid pool id, and no other roles given, the role for the pool is cleared. - ''' + """ self.conn.set_identity_pool_roles.return_value = None - result = boto_cognitoidentity.set_identity_pool_roles(IdentityPoolId='some_id', **conn_parameters) + result = boto_cognitoidentity.set_identity_pool_roles( + IdentityPoolId="some_id", **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertTrue(result.get('set')) + self.assertTrue(result.get("set")) self.assertEqual(len(mock_calls), 1) - self.assertEqual(mock_calls[0][2].get('Roles'), {}) + self.assertEqual(mock_calls[0][2].get("Roles"), {}) - def test_that_when_set_identity_pool_roles_with_only_auth_role_specified_the_set_identity_pool_roles_method_only_set_the_auth_role(self): - ''' + def test_that_when_set_identity_pool_roles_with_only_auth_role_specified_the_set_identity_pool_roles_method_only_set_the_auth_role( + self, + ): + """ Tests that given a valid pool id, and only other given role is the AuthenticatedRole, the auth role for the pool is set and unauth role is cleared. - ''' + """ self.conn.set_identity_pool_roles.return_value = None - with patch.dict(boto_cognitoidentity.__salt__, {'boto_iam.describe_role': MagicMock(return_value={'arn': 'my_auth_role_arn'})}): - expected_roles = dict(authenticated='my_auth_role_arn') - result = boto_cognitoidentity.set_identity_pool_roles(IdentityPoolId='some_id', AuthenticatedRole='my_auth_role', **conn_parameters) + with patch.dict( + boto_cognitoidentity.__salt__, + { + "boto_iam.describe_role": MagicMock( + return_value={"arn": "my_auth_role_arn"} + ) + }, + ): + expected_roles = dict(authenticated="my_auth_role_arn") + result = boto_cognitoidentity.set_identity_pool_roles( + IdentityPoolId="some_id", + AuthenticatedRole="my_auth_role", + **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertTrue(result.get('set')) + self.assertTrue(result.get("set")) self.assertEqual(len(mock_calls), 1) - self.assertEqual(mock_calls[0][2].get('Roles'), expected_roles) + self.assertEqual(mock_calls[0][2].get("Roles"), expected_roles) - def test_that_when_set_identity_pool_roles_with_only_unauth_role_specified_the_set_identity_pool_roles_method_only_set_the_unauth_role(self): - ''' + def test_that_when_set_identity_pool_roles_with_only_unauth_role_specified_the_set_identity_pool_roles_method_only_set_the_unauth_role( + self, + ): + """ Tests that given a valid pool id, and only other given role is the UnauthenticatedRole, the unauth role for the pool is set and the auth role is cleared. - ''' + """ self.conn.set_identity_pool_roles.return_value = None - with patch.dict(boto_cognitoidentity.__salt__, {'boto_iam.describe_role': MagicMock(return_value={'arn': 'my_unauth_role_arn'})}): - expected_roles = dict(unauthenticated='my_unauth_role_arn') - result = boto_cognitoidentity.set_identity_pool_roles(IdentityPoolId='some_id', UnauthenticatedRole='my_unauth_role', **conn_parameters) + with patch.dict( + boto_cognitoidentity.__salt__, + { + "boto_iam.describe_role": MagicMock( + return_value={"arn": "my_unauth_role_arn"} + ) + }, + ): + expected_roles = dict(unauthenticated="my_unauth_role_arn") + result = boto_cognitoidentity.set_identity_pool_roles( + IdentityPoolId="some_id", + UnauthenticatedRole="my_unauth_role", + **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertTrue(result.get('set')) + self.assertTrue(result.get("set")) self.assertEqual(len(mock_calls), 1) - self.assertEqual(mock_calls[0][2].get('Roles'), expected_roles) + self.assertEqual(mock_calls[0][2].get("Roles"), expected_roles) - def test_that_when_set_identity_pool_roles_with_both_roles_specified_the_set_identity_pool_role_method_set_both_roles(self): - ''' + def test_that_when_set_identity_pool_roles_with_both_roles_specified_the_set_identity_pool_role_method_set_both_roles( + self, + ): + """ Tests setting of both roles to valid given roles - ''' + """ self.conn.set_identity_pool_roles.return_value = None - with patch.dict(boto_cognitoidentity.__salt__, {'boto_iam.describe_role': MagicMock(return_value={'arn': 'my_unauth_role_arn'})}): - expected_roles = dict(authenticated='arn:aws:iam:my_auth_role', - unauthenticated='my_unauth_role_arn') - result = boto_cognitoidentity.set_identity_pool_roles(IdentityPoolId='some_id', AuthenticatedRole='arn:aws:iam:my_auth_role', UnauthenticatedRole='my_unauth_role', **conn_parameters) + with patch.dict( + boto_cognitoidentity.__salt__, + { + "boto_iam.describe_role": MagicMock( + return_value={"arn": "my_unauth_role_arn"} + ) + }, + ): + expected_roles = dict( + authenticated="arn:aws:iam:my_auth_role", + unauthenticated="my_unauth_role_arn", + ) + result = boto_cognitoidentity.set_identity_pool_roles( + IdentityPoolId="some_id", + AuthenticatedRole="arn:aws:iam:my_auth_role", + UnauthenticatedRole="my_unauth_role", + **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertTrue(result.get('set')) + self.assertTrue(result.get("set")) self.assertEqual(len(mock_calls), 1) - self.assertEqual(mock_calls[0][2].get('Roles'), expected_roles) + self.assertEqual(mock_calls[0][2].get("Roles"), expected_roles) - def test_that_when_set_identity_pool_roles_given_invalid_auth_role_the_set_identity_pool_method_returns_set_false_and_error(self): - ''' + def test_that_when_set_identity_pool_roles_given_invalid_auth_role_the_set_identity_pool_method_returns_set_false_and_error( + self, + ): + """ Tests error handling for invalid auth role - ''' - with patch.dict(boto_cognitoidentity.__salt__, {'boto_iam.describe_role': MagicMock(return_value=False)}): - result = boto_cognitoidentity.set_identity_pool_roles(IdentityPoolId='some_id', AuthenticatedRole='no_such_auth_role', **conn_parameters) + """ + with patch.dict( + boto_cognitoidentity.__salt__, + {"boto_iam.describe_role": MagicMock(return_value=False)}, + ): + result = boto_cognitoidentity.set_identity_pool_roles( + IdentityPoolId="some_id", + AuthenticatedRole="no_such_auth_role", + **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertIs(result.get('set'), False) - self.assertIn('no_such_auth_role', result.get('error', '')) + self.assertIs(result.get("set"), False) + self.assertIn("no_such_auth_role", result.get("error", "")) self.assertEqual(len(mock_calls), 0) - def test_that_when_set_identity_pool_roles_given_invalid_unauth_role_the_set_identity_pool_method_returns_set_false_and_error(self): - ''' + def test_that_when_set_identity_pool_roles_given_invalid_unauth_role_the_set_identity_pool_method_returns_set_false_and_error( + self, + ): + """ Tests error handling for invalid unauth role - ''' - with patch.dict(boto_cognitoidentity.__salt__, {'boto_iam.describe_role': MagicMock(return_value=False)}): - result = boto_cognitoidentity.set_identity_pool_roles(IdentityPoolId='some_id', AuthenticatedRole='arn:aws:iam:my_auth_role', UnauthenticatedRole='no_such_unauth_role', **conn_parameters) + """ + with patch.dict( + boto_cognitoidentity.__salt__, + {"boto_iam.describe_role": MagicMock(return_value=False)}, + ): + result = boto_cognitoidentity.set_identity_pool_roles( + IdentityPoolId="some_id", + AuthenticatedRole="arn:aws:iam:my_auth_role", + UnauthenticatedRole="no_such_unauth_role", + **conn_parameters + ) mock_calls = self.conn.mock_calls - self.assertIs(result.get('set'), False) - self.assertIn('no_such_unauth_role', result.get('error', '')) + self.assertIs(result.get("set"), False) + self.assertIn("no_such_unauth_role", result.get("error", "")) self.assertEqual(len(mock_calls), 0) - def test_that_when_update_identity_pool_given_invalid_pool_id_the_update_identity_pool_method_returns_updated_false_and_error(self): - ''' + def test_that_when_update_identity_pool_given_invalid_pool_id_the_update_identity_pool_method_returns_updated_false_and_error( + self, + ): + """ Tests error handling for invalid pool id - ''' - self.conn.describe_identity_pool.side_effect = ClientError(error_content, 'error on describe identity pool with pool id') - result = boto_cognitoidentity.update_identity_pool(IdentityPoolId='no_such_pool_id', **conn_parameters) - self.assertIs(result.get('updated'), False) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('error on describe identity pool with pool id')) + """ + self.conn.describe_identity_pool.side_effect = ClientError( + error_content, "error on describe identity pool with pool id" + ) + result = boto_cognitoidentity.update_identity_pool( + IdentityPoolId="no_such_pool_id", **conn_parameters + ) + self.assertIs(result.get("updated"), False) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("error on describe identity pool with pool id"), + ) - def test_that_when_update_identity_pool_given_only_valid_pool_id_the_update_identity_pool_method_returns_udpated_identity(self): - ''' + def test_that_when_update_identity_pool_given_only_valid_pool_id_the_update_identity_pool_method_returns_udpated_identity( + self, + ): + """ Tests the base case of calling update_identity_pool with only the pool id, verify that the passed parameters into boto3 update_identity_pool has at least all the expected required parameters in IdentityPoolId, IdentityPoolName and AllowUnauthenticatedIdentities - ''' + """ self.conn.describe_identity_pool.return_value = second_pool_ret self.conn.update_identity_pool.return_value = second_pool_ret expected_params = second_pool_ret - result = boto_cognitoidentity.update_identity_pool(IdentityPoolId=second_pool_id, **conn_parameters) - self.assertTrue(result.get('updated')) - self.assertEqual(result.get('identity_pool'), second_pool_ret) + result = boto_cognitoidentity.update_identity_pool( + IdentityPoolId=second_pool_id, **conn_parameters + ) + self.assertTrue(result.get("updated")) + self.assertEqual(result.get("identity_pool"), second_pool_ret) self.conn.update_identity_pool.assert_called_with(**expected_params) - def test_that_when_update_identity_pool_given_valid_pool_id_and_pool_name_the_update_identity_pool_method_returns_updated_identity_pool(self): - ''' + def test_that_when_update_identity_pool_given_valid_pool_id_and_pool_name_the_update_identity_pool_method_returns_updated_identity_pool( + self, + ): + """ Tests successful update of a pool's name - ''' + """ self.conn.describe_identity_pool.return_value = second_pool_ret second_pool_updated_ret = second_pool_ret.copy() - second_pool_updated_ret['IdentityPoolName'] = second_pool_name_updated + second_pool_updated_ret["IdentityPoolName"] = second_pool_name_updated self.conn.update_identity_pool.return_value = second_pool_updated_ret expected_params = second_pool_updated_ret - result = boto_cognitoidentity.update_identity_pool(IdentityPoolId=second_pool_id, IdentityPoolName=second_pool_name_updated, **conn_parameters) - self.assertTrue(result.get('updated')) - self.assertEqual(result.get('identity_pool'), second_pool_updated_ret) + result = boto_cognitoidentity.update_identity_pool( + IdentityPoolId=second_pool_id, + IdentityPoolName=second_pool_name_updated, + **conn_parameters + ) + self.assertTrue(result.get("updated")) + self.assertEqual(result.get("identity_pool"), second_pool_updated_ret) self.conn.update_identity_pool.assert_called_with(**expected_params) - def test_that_when_update_identity_pool_given_empty_dictionary_for_supported_login_providers_the_update_identity_pool_method_is_called_with_proper_request_params(self): - ''' + def test_that_when_update_identity_pool_given_empty_dictionary_for_supported_login_providers_the_update_identity_pool_method_is_called_with_proper_request_params( + self, + ): + """ Tests the request parameters to boto3 update_identity_pool's AllowUnauthenticatedIdentities is {} - ''' + """ self.conn.describe_identity_pool.return_value = first_pool_ret first_pool_updated_ret = first_pool_ret.copy() - first_pool_updated_ret.pop('SupportedLoginProviders') + first_pool_updated_ret.pop("SupportedLoginProviders") self.conn.update_identity_pool.return_value = first_pool_updated_ret expected_params = first_pool_ret.copy() - expected_params['SupportedLoginProviders'] = {} - expected_params.pop('DeveloperProviderName') - expected_params.pop('OpenIdConnectProviderARNs') - result = boto_cognitoidentity.update_identity_pool(IdentityPoolId=first_pool_id, SupportedLoginProviders={}, **conn_parameters) - self.assertTrue(result.get('updated')) - self.assertEqual(result.get('identity_pool'), first_pool_updated_ret) + expected_params["SupportedLoginProviders"] = {} + expected_params.pop("DeveloperProviderName") + expected_params.pop("OpenIdConnectProviderARNs") + result = boto_cognitoidentity.update_identity_pool( + IdentityPoolId=first_pool_id, SupportedLoginProviders={}, **conn_parameters + ) + self.assertTrue(result.get("updated")) + self.assertEqual(result.get("identity_pool"), first_pool_updated_ret) self.conn.update_identity_pool.assert_called_with(**expected_params) - def test_that_when_update_identity_pool_given_empty_list_for_openid_connect_provider_arns_the_update_identity_pool_method_is_called_with_proper_request_params(self): - ''' + def test_that_when_update_identity_pool_given_empty_list_for_openid_connect_provider_arns_the_update_identity_pool_method_is_called_with_proper_request_params( + self, + ): + """ Tests the request parameters to boto3 update_identity_pool's OpenIdConnectProviderARNs is [] - ''' + """ self.conn.describe_identity_pool.return_value = first_pool_ret first_pool_updated_ret = first_pool_ret.copy() - first_pool_updated_ret.pop('OpenIdConnectProviderARNs') + first_pool_updated_ret.pop("OpenIdConnectProviderARNs") self.conn.update_identity_pool.return_value = first_pool_updated_ret expected_params = first_pool_ret.copy() - expected_params.pop('SupportedLoginProviders') - expected_params.pop('DeveloperProviderName') - expected_params['OpenIdConnectProviderARNs'] = [] - result = boto_cognitoidentity.update_identity_pool(IdentityPoolId=first_pool_id, OpenIdConnectProviderARNs=[], **conn_parameters) - self.assertTrue(result.get('updated')) - self.assertEqual(result.get('identity_pool'), first_pool_updated_ret) + expected_params.pop("SupportedLoginProviders") + expected_params.pop("DeveloperProviderName") + expected_params["OpenIdConnectProviderARNs"] = [] + result = boto_cognitoidentity.update_identity_pool( + IdentityPoolId=first_pool_id, + OpenIdConnectProviderARNs=[], + **conn_parameters + ) + self.assertTrue(result.get("updated")) + self.assertEqual(result.get("identity_pool"), first_pool_updated_ret) self.conn.update_identity_pool.assert_called_with(**expected_params) - def test_that_when_update_identity_pool_given_developer_provider_name_when_developer_provider_name_was_set_previously_the_udpate_identity_pool_method_is_called_without_developer_provider_name_param(self): - ''' + def test_that_when_update_identity_pool_given_developer_provider_name_when_developer_provider_name_was_set_previously_the_udpate_identity_pool_method_is_called_without_developer_provider_name_param( + self, + ): + """ Tests the request parameters do not include 'DeveloperProviderName' if this was previously set for the given pool id - ''' + """ self.conn.describe_identity_pool.return_value = first_pool_ret self.conn.update_identity_pool.return_value = first_pool_ret expected_params = first_pool_ret.copy() - expected_params.pop('SupportedLoginProviders') - expected_params.pop('DeveloperProviderName') - expected_params.pop('OpenIdConnectProviderARNs') - result = boto_cognitoidentity.update_identity_pool(IdentityPoolId=first_pool_id, DeveloperProviderName='this should not change', **conn_parameters) - self.assertTrue(result.get('updated')) - self.assertEqual(result.get('identity_pool'), first_pool_ret) + expected_params.pop("SupportedLoginProviders") + expected_params.pop("DeveloperProviderName") + expected_params.pop("OpenIdConnectProviderARNs") + result = boto_cognitoidentity.update_identity_pool( + IdentityPoolId=first_pool_id, + DeveloperProviderName="this should not change", + **conn_parameters + ) + self.assertTrue(result.get("updated")) + self.assertEqual(result.get("identity_pool"), first_pool_ret) self.conn.update_identity_pool.assert_called_with(**expected_params) - def test_that_when_update_identity_pool_given_developer_provider_name_is_included_in_the_params_when_associated_for_the_first_time(self): - ''' + def test_that_when_update_identity_pool_given_developer_provider_name_is_included_in_the_params_when_associated_for_the_first_time( + self, + ): + """ Tests the request parameters include 'DeveloperProviderName' when the pool did not have this property set previously. - ''' + """ self.conn.describe_identity_pool.return_value = second_pool_ret second_pool_updated_ret = second_pool_ret.copy() - second_pool_updated_ret['DeveloperProviderName'] = 'added_developer_provider' + second_pool_updated_ret["DeveloperProviderName"] = "added_developer_provider" self.conn.update_identity_pool.return_value = second_pool_updated_ret expected_params = second_pool_updated_ret.copy() - result = boto_cognitoidentity.update_identity_pool(IdentityPoolId=second_pool_id, DeveloperProviderName='added_developer_provider', **conn_parameters) - self.assertTrue(result.get('updated')) - self.assertEqual(result.get('identity_pool'), second_pool_updated_ret) + result = boto_cognitoidentity.update_identity_pool( + IdentityPoolId=second_pool_id, + DeveloperProviderName="added_developer_provider", + **conn_parameters + ) + self.assertTrue(result.get("updated")) + self.assertEqual(result.get("identity_pool"), second_pool_updated_ret) self.conn.update_identity_pool.assert_called_with(**expected_params) def test_that_the_update_identity_pool_method_handles_exception_from_boto3(self): - ''' + """ Tests the error handling of exception generated by boto3 update_identity_pool - ''' + """ self.conn.describe_identity_pool.return_value = second_pool_ret second_pool_updated_ret = second_pool_ret.copy() - second_pool_updated_ret['DeveloperProviderName'] = 'added_developer_provider' - self.conn.update_identity_pool.side_effect = ClientError(error_content, 'update_identity_pool') - result = boto_cognitoidentity.update_identity_pool(IdentityPoolId=second_pool_id, DeveloperProviderName='added_developer_provider', **conn_parameters) - self.assertIs(result.get('updated'), False) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('update_identity_pool')) + second_pool_updated_ret["DeveloperProviderName"] = "added_developer_provider" + self.conn.update_identity_pool.side_effect = ClientError( + error_content, "update_identity_pool" + ) + result = boto_cognitoidentity.update_identity_pool( + IdentityPoolId=second_pool_id, + DeveloperProviderName="added_developer_provider", + **conn_parameters + ) + self.assertIs(result.get("updated"), False) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("update_identity_pool"), + ) diff --git a/tests/unit/modules/test_boto_elasticsearch_domain.py b/tests/unit/modules/test_boto_elasticsearch_domain.py index 50c826d0d36..4652e1c4e96 100644 --- a/tests/unit/modules/test_boto_elasticsearch_domain.py +++ b/tests/unit/modules/test_boto_elasticsearch_domain.py @@ -2,24 +2,24 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import random import string -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import salt.loader +import salt.modules.boto_elasticsearch_domain as boto_elasticsearch_domain # Import Salt libs from salt.ext import six -import salt.loader +from salt.ext.six.moves import range from salt.utils.versions import LooseVersion -import salt.modules.boto_elasticsearch_domain as boto_elasticsearch_domain + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # Import 3rd-party libs @@ -27,25 +27,25 @@ import salt.modules.boto_elasticsearch_domain as boto_elasticsearch_domain try: import boto3 from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False -from salt.ext.six.moves import range # pylint: enable=import-error,no-name-in-module # the boto_elasticsearch_domain module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' +required_boto3_version = "1.2.1" def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -55,29 +55,36 @@ def _has_required_boto(): if _has_required_boto(): - region = 'us-east-1' - access_key = 'GKTADJGHEIQSXMKKRBJ08H' - secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' - conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} - error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' - error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } + region = "us-east-1" + access_key = "GKTADJGHEIQSXMKKRBJ08H" + secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" + conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } - not_found_error = ClientError({ - 'Error': { - 'Code': 'ResourceNotFoundException', - 'Message': "Test-defined error" - } - }, 'msg') - domain_ret = dict(DomainName='testdomain', - ElasticsearchClusterConfig={}, - EBSOptions={}, - AccessPolicies={}, - SnapshotOptions={}, - AdvancedOptions={}) + error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" + ) + error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} + not_found_error = ClientError( + { + "Error": { + "Code": "ResourceNotFoundException", + "Message": "Test-defined error", + } + }, + "msg", + ) + domain_ret = dict( + DomainName="testdomain", + ElasticsearchClusterConfig={}, + EBSOptions={}, + AccessPolicies={}, + SnapshotOptions={}, + AdvancedOptions={}, + ) log = logging.getLogger(__name__) @@ -89,9 +96,10 @@ class BotoElasticsearchDomainTestCaseBase(TestCase, LoaderModuleMockMixin): self.opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( self.opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context={}) - return {boto_elasticsearch_domain: {'__utils__': utils}} + whitelist=["boto3", "args", "systemd", "path", "platform"], + context={}, + ) + return {boto_elasticsearch_domain: {"__utils__": utils}} def setUp(self): super(BotoElasticsearchDomainTestCaseBase, self).setUp() @@ -102,16 +110,18 @@ class BotoElasticsearchDomainTestCaseBase(TestCase, LoaderModuleMockMixin): # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn @@ -119,195 +129,293 @@ class BotoElasticsearchDomainTestCaseMixin(object): pass -@skipIf(True, 'Skip these tests while investigating failures') -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -class BotoElasticsearchDomainTestCase(BotoElasticsearchDomainTestCaseBase, BotoElasticsearchDomainTestCaseMixin): - ''' +@skipIf(True, "Skip these tests while investigating failures") +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) +class BotoElasticsearchDomainTestCase( + BotoElasticsearchDomainTestCaseBase, BotoElasticsearchDomainTestCaseMixin +): + """ TestCase for salt.modules.boto_elasticsearch_domain module - ''' + """ - def test_that_when_checking_if_a_domain_exists_and_a_domain_exists_the_domain_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_domain_exists_and_a_domain_exists_the_domain_exists_method_returns_true( + self, + ): + """ Tests checking domain existence when the domain already exists - ''' - result = boto_elasticsearch_domain.exists(DomainName='testdomain', **conn_parameters) + """ + result = boto_elasticsearch_domain.exists( + DomainName="testdomain", **conn_parameters + ) - self.assertTrue(result['exists']) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_a_domain_exists_and_a_domain_does_not_exist_the_domain_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_domain_exists_and_a_domain_does_not_exist_the_domain_exists_method_returns_false( + self, + ): + """ Tests checking domain existence when the domain does not exist - ''' + """ self.conn.describe_elasticsearch_domain.side_effect = not_found_error - result = boto_elasticsearch_domain.exists(DomainName='mydomain', **conn_parameters) + result = boto_elasticsearch_domain.exists( + DomainName="mydomain", **conn_parameters + ) - self.assertFalse(result['exists']) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_a_domain_exists_and_boto3_returns_an_error_the_domain_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_a_domain_exists_and_boto3_returns_an_error_the_domain_exists_method_returns_error( + self, + ): + """ Tests checking domain existence when boto returns an error - ''' - self.conn.describe_elasticsearch_domain.side_effect = ClientError(error_content, 'list_domains') - result = boto_elasticsearch_domain.exists(DomainName='mydomain', **conn_parameters) + """ + self.conn.describe_elasticsearch_domain.side_effect = ClientError( + error_content, "list_domains" + ) + result = boto_elasticsearch_domain.exists( + DomainName="mydomain", **conn_parameters + ) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('list_domains')) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("list_domains") + ) - def test_that_when_checking_domain_status_and_a_domain_exists_the_domain_status_method_returns_info(self): - ''' + def test_that_when_checking_domain_status_and_a_domain_exists_the_domain_status_method_returns_info( + self, + ): + """ Tests checking domain existence when the domain already exists - ''' - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - result = boto_elasticsearch_domain.status(DomainName='testdomain', **conn_parameters) + """ + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + result = boto_elasticsearch_domain.status( + DomainName="testdomain", **conn_parameters + ) - self.assertTrue(result['domain']) + self.assertTrue(result["domain"]) - def test_that_when_checking_domain_status_and_boto3_returns_an_error_the_domain_status_method_returns_error(self): - ''' + def test_that_when_checking_domain_status_and_boto3_returns_an_error_the_domain_status_method_returns_error( + self, + ): + """ Tests checking domain existence when boto returns an error - ''' - self.conn.describe_elasticsearch_domain.side_effect = ClientError(error_content, 'list_domains') - result = boto_elasticsearch_domain.status(DomainName='mydomain', **conn_parameters) + """ + self.conn.describe_elasticsearch_domain.side_effect = ClientError( + error_content, "list_domains" + ) + result = boto_elasticsearch_domain.status( + DomainName="mydomain", **conn_parameters + ) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('list_domains')) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("list_domains") + ) - def test_that_when_describing_domain_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_domain_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters if domain exists - ''' + """ domainconfig = {} for k, v in six.iteritems(domain_ret): - if k == 'DomainName': + if k == "DomainName": continue - domainconfig[k] = {'Options': v} - self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': domainconfig} + domainconfig[k] = {"Options": v} + self.conn.describe_elasticsearch_domain_config.return_value = { + "DomainConfig": domainconfig + } - result = boto_elasticsearch_domain.describe(DomainName=domain_ret['DomainName'], **conn_parameters) + result = boto_elasticsearch_domain.describe( + DomainName=domain_ret["DomainName"], **conn_parameters + ) log.warning(result) desired_ret = copy.copy(domain_ret) - desired_ret.pop('DomainName') - self.assertEqual(result, {'domain': desired_ret}) + desired_ret.pop("DomainName") + self.assertEqual(result, {"domain": desired_ret}) def test_that_when_describing_domain_on_client_error_it_returns_error(self): - ''' + """ Tests describing parameters failure - ''' - self.conn.describe_elasticsearch_domain_config.side_effect = ClientError(error_content, 'list_domains') - result = boto_elasticsearch_domain.describe(DomainName='testdomain', **conn_parameters) - self.assertTrue('error' in result) + """ + self.conn.describe_elasticsearch_domain_config.side_effect = ClientError( + error_content, "list_domains" + ) + result = boto_elasticsearch_domain.describe( + DomainName="testdomain", **conn_parameters + ) + self.assertTrue("error" in result) - def test_that_when_creating_a_domain_succeeds_the_create_domain_method_returns_true(self): - ''' + def test_that_when_creating_a_domain_succeeds_the_create_domain_method_returns_true( + self, + ): + """ tests True domain created. - ''' - self.conn.create_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} + """ + self.conn.create_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } args = copy.copy(domain_ret) args.update(conn_parameters) result = boto_elasticsearch_domain.create(**args) - self.assertTrue(result['created']) + self.assertTrue(result["created"]) - def test_that_when_creating_a_domain_fails_the_create_domain_method_returns_error(self): - ''' + def test_that_when_creating_a_domain_fails_the_create_domain_method_returns_error( + self, + ): + """ tests False domain not created. - ''' - self.conn.create_elasticsearch_domain.side_effect = ClientError(error_content, 'create_domain') + """ + self.conn.create_elasticsearch_domain.side_effect = ClientError( + error_content, "create_domain" + ) args = copy.copy(domain_ret) args.update(conn_parameters) result = boto_elasticsearch_domain.create(**args) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('create_domain')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("create_domain"), + ) - def test_that_when_deleting_a_domain_succeeds_the_delete_domain_method_returns_true(self): - ''' + def test_that_when_deleting_a_domain_succeeds_the_delete_domain_method_returns_true( + self, + ): + """ tests True domain deleted. - ''' - result = boto_elasticsearch_domain.delete(DomainName='testdomain', - **conn_parameters) - self.assertTrue(result['deleted']) + """ + result = boto_elasticsearch_domain.delete( + DomainName="testdomain", **conn_parameters + ) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_a_domain_fails_the_delete_domain_method_returns_false(self): - ''' + def test_that_when_deleting_a_domain_fails_the_delete_domain_method_returns_false( + self, + ): + """ tests False domain not deleted. - ''' - self.conn.delete_elasticsearch_domain.side_effect = ClientError(error_content, 'delete_domain') - result = boto_elasticsearch_domain.delete(DomainName='testdomain', - **conn_parameters) - self.assertFalse(result['deleted']) + """ + self.conn.delete_elasticsearch_domain.side_effect = ClientError( + error_content, "delete_domain" + ) + result = boto_elasticsearch_domain.delete( + DomainName="testdomain", **conn_parameters + ) + self.assertFalse(result["deleted"]) - def test_that_when_updating_a_domain_succeeds_the_update_domain_method_returns_true(self): - ''' + def test_that_when_updating_a_domain_succeeds_the_update_domain_method_returns_true( + self, + ): + """ tests True domain updated. - ''' - self.conn.update_elasticsearch_domain_config.return_value = {'DomainConfig': domain_ret} + """ + self.conn.update_elasticsearch_domain_config.return_value = { + "DomainConfig": domain_ret + } args = copy.copy(domain_ret) args.update(conn_parameters) result = boto_elasticsearch_domain.update(**args) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_updating_a_domain_fails_the_update_domain_method_returns_error(self): - ''' + def test_that_when_updating_a_domain_fails_the_update_domain_method_returns_error( + self, + ): + """ tests False domain not updated. - ''' - self.conn.update_elasticsearch_domain_config.side_effect = ClientError(error_content, 'update_domain') + """ + self.conn.update_elasticsearch_domain_config.side_effect = ClientError( + error_content, "update_domain" + ) args = copy.copy(domain_ret) args.update(conn_parameters) result = boto_elasticsearch_domain.update(**args) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('update_domain')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("update_domain"), + ) def test_that_when_adding_tags_succeeds_the_add_tags_method_returns_true(self): - ''' + """ tests True tags added. - ''' - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - result = boto_elasticsearch_domain.add_tags(DomainName='testdomain', a='b', **conn_parameters) + """ + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + result = boto_elasticsearch_domain.add_tags( + DomainName="testdomain", a="b", **conn_parameters + ) - self.assertTrue(result['tagged']) + self.assertTrue(result["tagged"]) def test_that_when_adding_tags_fails_the_add_tags_method_returns_false(self): - ''' + """ tests False tags not added. - ''' - self.conn.add_tags.side_effect = ClientError(error_content, 'add_tags') - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - result = boto_elasticsearch_domain.add_tags(DomainName=domain_ret['DomainName'], a='b', **conn_parameters) - self.assertFalse(result['tagged']) + """ + self.conn.add_tags.side_effect = ClientError(error_content, "add_tags") + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + result = boto_elasticsearch_domain.add_tags( + DomainName=domain_ret["DomainName"], a="b", **conn_parameters + ) + self.assertFalse(result["tagged"]) def test_that_when_removing_tags_succeeds_the_remove_tags_method_returns_true(self): - ''' + """ tests True tags removed. - ''' - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - result = boto_elasticsearch_domain.remove_tags(DomainName=domain_ret['DomainName'], TagKeys=['a'], **conn_parameters) + """ + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + result = boto_elasticsearch_domain.remove_tags( + DomainName=domain_ret["DomainName"], TagKeys=["a"], **conn_parameters + ) - self.assertTrue(result['tagged']) + self.assertTrue(result["tagged"]) def test_that_when_removing_tags_fails_the_remove_tags_method_returns_false(self): - ''' + """ tests False tags not removed. - ''' - self.conn.remove_tags.side_effect = ClientError(error_content, 'remove_tags') - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - result = boto_elasticsearch_domain.remove_tags(DomainName=domain_ret['DomainName'], TagKeys=['b'], **conn_parameters) - self.assertFalse(result['tagged']) + """ + self.conn.remove_tags.side_effect = ClientError(error_content, "remove_tags") + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + result = boto_elasticsearch_domain.remove_tags( + DomainName=domain_ret["DomainName"], TagKeys=["b"], **conn_parameters + ) + self.assertFalse(result["tagged"]) def test_that_when_listing_tags_succeeds_the_list_tags_method_returns_true(self): - ''' + """ tests True tags listed. - ''' - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - result = boto_elasticsearch_domain.list_tags(DomainName=domain_ret['DomainName'], **conn_parameters) + """ + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + result = boto_elasticsearch_domain.list_tags( + DomainName=domain_ret["DomainName"], **conn_parameters + ) - self.assertEqual(result['tags'], {}) + self.assertEqual(result["tags"], {}) def test_that_when_listing_tags_fails_the_list_tags_method_returns_false(self): - ''' + """ tests False tags not listed. - ''' - self.conn.list_tags.side_effect = ClientError(error_content, 'list_tags') - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - result = boto_elasticsearch_domain.list_tags(DomainName=domain_ret['DomainName'], **conn_parameters) - self.assertTrue(result['error']) + """ + self.conn.list_tags.side_effect = ClientError(error_content, "list_tags") + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + result = boto_elasticsearch_domain.list_tags( + DomainName=domain_ret["DomainName"], **conn_parameters + ) + self.assertTrue(result["error"]) diff --git a/tests/unit/modules/test_boto_elb.py b/tests/unit/modules/test_boto_elb.py index b84f8b8805b..f3d505f3cbd 100644 --- a/tests/unit/modules/test_boto_elb.py +++ b/tests/unit/modules/test_boto_elb.py @@ -2,118 +2,139 @@ # import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging -from copy import deepcopy -import pkg_resources import os.path import sys +from copy import deepcopy + +import pkg_resources + +# Import Salt Libs +import salt.config +import salt.loader +import salt.modules.boto_elb as boto_elb +import salt.utils.versions +from salt.ext import six + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin # Import test support libs from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf # import Python Third Party Libs # pylint: disable=import-error try: import boto - boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') + + boto.ENDPOINTS_PATH = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/endpoints.json" + ) import boto.ec2.elb + HAS_BOTO = True except ImportError: HAS_BOTO = False try: from moto import mock_ec2_deprecated, mock_elb_deprecated + HAS_MOTO = True except ImportError: HAS_MOTO = False def mock_ec2_deprecated(self): - ''' + """ if the mock_ec2_deprecated function is not available due to import failure this replaces the decorated function with stub_function. Allows boto_elb unit tests to use the @mock_ec2_deprecated decorator without a "NameError: name 'mock_ec2_deprecated' is not defined" error. - ''' + """ + def stub_function(self): pass + return stub_function def mock_elb_deprecated(self): - ''' + """ if the mock_elb_deprecated function is not available due to import failure this replaces the decorated function with stub_function. Allows boto_elb unit tests to use the @mock_elb_deprecated decorator without a "NameError: name 'mock_elb_deprecated' is not defined" error. - ''' + """ + def stub_function(self): pass + return stub_function + + # pylint: enable=import-error -# Import Salt Libs -import salt.config -from salt.ext import six -import salt.loader -import salt.modules.boto_elb as boto_elb -import salt.utils.versions - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase log = logging.getLogger(__name__) -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, - 'profile': {}} -boto_conn_parameters = {'aws_access_key_id': access_key, - 'aws_secret_access_key': secret_key} -instance_parameters = {'instance_type': 't1.micro'} -required_moto = '0.3.7' -required_moto_py3 = '1.0.1' +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, +} +boto_conn_parameters = { + "aws_access_key_id": access_key, + "aws_secret_access_key": secret_key, +} +instance_parameters = {"instance_type": "t1.micro"} +required_moto = "0.3.7" +required_moto_py3 = "1.0.1" def _has_required_moto(): - ''' + """ Returns True or False depending on if ``moto`` is installed and at the correct version, depending on what version of Python is running these tests. - ''' + """ if not HAS_MOTO: return False else: - moto_version = salt.utils.versions.LooseVersion(pkg_resources.get_distribution('moto').version) + moto_version = salt.utils.versions.LooseVersion( + pkg_resources.get_distribution("moto").version + ) if moto_version < salt.utils.versions.LooseVersion(required_moto): return False - elif six.PY3 and moto_version < salt.utils.versions.LooseVersion(required_moto_py3): + elif six.PY3 and moto_version < salt.utils.versions.LooseVersion( + required_moto_py3 + ): return False return True -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_moto() is False, 'The moto module must be >= to {0} for ' - 'PY2 or {1} for PY3.'.format(required_moto, required_moto_py3)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_moto() is False, + "The moto module must be >= to {0} for " + "PY2 or {1} for PY3.".format(required_moto, required_moto_py3), +) class BotoElbTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.boto_elb module - ''' + """ def setup_loader_modules(self): opts = salt.config.DEFAULT_MASTER_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto', 'args', 'systemd', 'path', 'platform']) + opts, whitelist=["boto", "args", "systemd", "path", "platform"] + ) funcs = salt.loader.minion_mods(opts, utils=utils) - return { - boto_elb: { - '__opts__': opts, - '__utils__': utils, - '__salt__': funcs - } - } + return {boto_elb: {"__opts__": opts, "__utils__": utils, "__salt__": funcs}} def setUp(self): TestCase.setUp(self) @@ -122,123 +143,139 @@ class BotoElbTestCase(TestCase, LoaderModuleMockMixin): @mock_ec2_deprecated @mock_elb_deprecated - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) def test_register_instances_valid_id_result_true(self): - ''' + """ tests that given a valid instance id and valid ELB that register_instances returns True. - ''' + """ conn_ec2 = boto.ec2.connect_to_region(region, **boto_conn_parameters) - conn_elb = boto.ec2.elb.connect_to_region(region, - **boto_conn_parameters) + conn_elb = boto.ec2.elb.connect_to_region(region, **boto_conn_parameters) zones = [zone.name for zone in conn_ec2.get_all_zones()] - elb_name = 'TestRegisterInstancesValidIdResult' - conn_elb.create_load_balancer(elb_name, zones, [(80, 80, 'http')]) - reservations = conn_ec2.run_instances('ami-08389d60') - register_result = boto_elb.register_instances(elb_name, - reservations.instances[0].id, - **conn_parameters) + elb_name = "TestRegisterInstancesValidIdResult" + conn_elb.create_load_balancer(elb_name, zones, [(80, 80, "http")]) + reservations = conn_ec2.run_instances("ami-08389d60") + register_result = boto_elb.register_instances( + elb_name, reservations.instances[0].id, **conn_parameters + ) self.assertEqual(True, register_result) @mock_ec2_deprecated @mock_elb_deprecated - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) def test_register_instances_valid_id_string(self): - ''' + """ tests that given a string containing a instance id and valid ELB that register_instances adds the given instance to an ELB - ''' + """ conn_ec2 = boto.ec2.connect_to_region(region, **boto_conn_parameters) - conn_elb = boto.ec2.elb.connect_to_region(region, - **boto_conn_parameters) + conn_elb = boto.ec2.elb.connect_to_region(region, **boto_conn_parameters) zones = [zone.name for zone in conn_ec2.get_all_zones()] - elb_name = 'TestRegisterInstancesValidIdResult' - conn_elb.create_load_balancer(elb_name, zones, [(80, 80, 'http')]) - reservations = conn_ec2.run_instances('ami-08389d60') - boto_elb.register_instances(elb_name, reservations.instances[0].id, - **conn_parameters) + elb_name = "TestRegisterInstancesValidIdResult" + conn_elb.create_load_balancer(elb_name, zones, [(80, 80, "http")]) + reservations = conn_ec2.run_instances("ami-08389d60") + boto_elb.register_instances( + elb_name, reservations.instances[0].id, **conn_parameters + ) load_balancer_refreshed = conn_elb.get_all_load_balancers(elb_name)[0] - registered_instance_ids = [instance.id for instance in - load_balancer_refreshed.instances] + registered_instance_ids = [ + instance.id for instance in load_balancer_refreshed.instances + ] log.debug(load_balancer_refreshed.instances) self.assertEqual([reservations.instances[0].id], registered_instance_ids) @mock_ec2_deprecated @mock_elb_deprecated - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) def test_deregister_instances_valid_id_result_true(self): - ''' + """ tests that given an valid id the boto_elb deregister_instances method removes exactly one of a number of ELB registered instances - ''' + """ conn_ec2 = boto.ec2.connect_to_region(region, **boto_conn_parameters) - conn_elb = boto.ec2.elb.connect_to_region(region, - **boto_conn_parameters) + conn_elb = boto.ec2.elb.connect_to_region(region, **boto_conn_parameters) zones = [zone.name for zone in conn_ec2.get_all_zones()] - elb_name = 'TestDeregisterInstancesValidIdResult' - load_balancer = conn_elb.create_load_balancer(elb_name, zones, - [(80, 80, 'http')]) - reservations = conn_ec2.run_instances('ami-08389d60') + elb_name = "TestDeregisterInstancesValidIdResult" + load_balancer = conn_elb.create_load_balancer( + elb_name, zones, [(80, 80, "http")] + ) + reservations = conn_ec2.run_instances("ami-08389d60") load_balancer.register_instances(reservations.instances[0].id) - deregister_result = boto_elb.deregister_instances(elb_name, - reservations.instances[0].id, - **conn_parameters) + deregister_result = boto_elb.deregister_instances( + elb_name, reservations.instances[0].id, **conn_parameters + ) self.assertEqual(True, deregister_result) @mock_ec2_deprecated @mock_elb_deprecated - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) def test_deregister_instances_valid_id_string(self): - ''' + """ tests that given an valid id the boto_elb deregister_instances method removes exactly one of a number of ELB registered instances - ''' + """ conn_ec2 = boto.ec2.connect_to_region(region, **boto_conn_parameters) - conn_elb = boto.ec2.elb.connect_to_region(region, - **boto_conn_parameters) + conn_elb = boto.ec2.elb.connect_to_region(region, **boto_conn_parameters) zones = [zone.name for zone in conn_ec2.get_all_zones()] - elb_name = 'TestDeregisterInstancesValidIdString' - load_balancer = conn_elb.create_load_balancer(elb_name, zones, - [(80, 80, 'http')]) - reservations = conn_ec2.run_instances('ami-08389d60', min_count=2) + elb_name = "TestDeregisterInstancesValidIdString" + load_balancer = conn_elb.create_load_balancer( + elb_name, zones, [(80, 80, "http")] + ) + reservations = conn_ec2.run_instances("ami-08389d60", min_count=2) all_instance_ids = [instance.id for instance in reservations.instances] load_balancer.register_instances(all_instance_ids) - boto_elb.deregister_instances(elb_name, reservations.instances[0].id, - **conn_parameters) + boto_elb.deregister_instances( + elb_name, reservations.instances[0].id, **conn_parameters + ) load_balancer_refreshed = conn_elb.get_all_load_balancers(elb_name)[0] expected_instances = deepcopy(all_instance_ids) expected_instances.remove(reservations.instances[0].id) - actual_instances = [instance.id for instance in - load_balancer_refreshed.instances] + actual_instances = [ + instance.id for instance in load_balancer_refreshed.instances + ] self.assertEqual(actual_instances, expected_instances) @mock_ec2_deprecated @mock_elb_deprecated - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) def test_deregister_instances_valid_id_list(self): - ''' + """ tests that given an valid ids in the form of a list that the boto_elb deregister_instances all members of the given list - ''' + """ conn_ec2 = boto.ec2.connect_to_region(region, **boto_conn_parameters) - conn_elb = boto.ec2.elb.connect_to_region(region, - **boto_conn_parameters) + conn_elb = boto.ec2.elb.connect_to_region(region, **boto_conn_parameters) zones = [zone.name for zone in conn_ec2.get_all_zones()] - elb_name = 'TestDeregisterInstancesValidIdList' - load_balancer = conn_elb.create_load_balancer(elb_name, zones, - [(80, 80, 'http')]) - reservations = conn_ec2.run_instances('ami-08389d60', min_count=3) + elb_name = "TestDeregisterInstancesValidIdList" + load_balancer = conn_elb.create_load_balancer( + elb_name, zones, [(80, 80, "http")] + ) + reservations = conn_ec2.run_instances("ami-08389d60", min_count=3) all_instance_ids = [instance.id for instance in reservations.instances] load_balancer.register_instances(all_instance_ids) # reservations.instances[:-1] refers to all instances except list # instance - deregister_instances = [instance.id for instance in - reservations.instances[:-1]] + deregister_instances = [instance.id for instance in reservations.instances[:-1]] expected_instances = [reservations.instances[-1].id] - boto_elb.deregister_instances(elb_name, deregister_instances, - **conn_parameters) + boto_elb.deregister_instances(elb_name, deregister_instances, **conn_parameters) load_balancer_refreshed = conn_elb.get_all_load_balancers(elb_name)[0] - actual_instances = [instance.id for instance in - load_balancer_refreshed.instances] + actual_instances = [ + instance.id for instance in load_balancer_refreshed.instances + ] self.assertEqual(actual_instances, expected_instances) diff --git a/tests/unit/modules/test_boto_iot.py b/tests/unit/modules/test_boto_iot.py index 7b14bd57b0c..b075adb462a 100644 --- a/tests/unit/modules/test_boto_iot.py +++ b/tests/unit/modules/test_boto_iot.py @@ -2,33 +2,32 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import random import string -import logging - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch -) # Import Salt libs import salt.config import salt.loader import salt.modules.boto_iot as boto_iot -from salt.utils.versions import LooseVersion -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin # Import 3rd-party libs from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + # pylint: disable=import-error,no-name-in-module,unused-import try: import boto import boto3 from botocore.exceptions import ClientError from botocore import __version__ as found_botocore_version + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -38,17 +37,17 @@ except ImportError: # the boto_iot module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' -required_botocore_version = '1.4.41' +required_boto3_version = "1.2.1" +required_botocore_version = "1.4.41" log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -60,44 +59,51 @@ def _has_required_boto(): if _has_required_boto(): - region = 'us-east-1' - access_key = 'GKTADJGHEIQSXMKKRBJ08H' - secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' - conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} - error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' - not_found_error = ClientError({ - 'Error': { - 'Code': 'ResourceNotFoundException', - 'Message': "Test-defined error" - } - }, 'msg') - topic_rule_not_found_error = ClientError({ - 'Error': { - 'Code': 'UnauthorizedException', - 'Message': "Test-defined error" - } - }, 'msg') - error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } + region = "us-east-1" + access_key = "GKTADJGHEIQSXMKKRBJ08H" + secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" + conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } - policy_ret = dict(policyName='testpolicy', - policyDocument='{"Version": "2012-10-17", "Statement": [{"Action": ["iot:Publish"], "Resource": ["*"], "Effect": "Allow"}]}', - policyArn='arn:aws:iot:us-east-1:123456:policy/my_policy', - policyVersionId=1, - defaultVersionId=1) - topic_rule_ret = dict(ruleName='testrule', - sql="SELECT * FROM 'iot/test'", - description='topic rule description', - createdAt='1970-01-01', - actions=[{'lambda': {'functionArn': 'arn:aws:::function'}}], - ruleDisabled=True) + error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" + ) + not_found_error = ClientError( + { + "Error": { + "Code": "ResourceNotFoundException", + "Message": "Test-defined error", + } + }, + "msg", + ) + topic_rule_not_found_error = ClientError( + {"Error": {"Code": "UnauthorizedException", "Message": "Test-defined error"}}, + "msg", + ) + error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} + policy_ret = dict( + policyName="testpolicy", + policyDocument='{"Version": "2012-10-17", "Statement": [{"Action": ["iot:Publish"], "Resource": ["*"], "Effect": "Allow"}]}', + policyArn="arn:aws:iot:us-east-1:123456:policy/my_policy", + policyVersionId=1, + defaultVersionId=1, + ) + topic_rule_ret = dict( + ruleName="testrule", + sql="SELECT * FROM 'iot/test'", + description="topic rule description", + createdAt="1970-01-01", + actions=[{"lambda": {"functionArn": "arn:aws:::function"}}], + ruleDisabled=True, + ) - thing_type_name = 'test_thing_type' - thing_type_desc = 'test_thing_type_desc' - thing_type_attr_1 = 'test_thing_type_search_attr_1' + thing_type_name = "test_thing_type" + thing_type_desc = "test_thing_type_desc" + thing_type_attr_1 = "test_thing_type_search_attr_1" thing_type_ret = dict( thingTypeName=thing_type_name, thingTypeProperties=dict( @@ -105,31 +111,30 @@ if _has_required_boto(): searchableAttributes=[thing_type_attr_1], ), thingTypeMetadata=dict( - deprecated=False, - creationDate='test_thing_type_create_date' - ) + deprecated=False, creationDate="test_thing_type_create_date" + ), ) - thing_type_arn = 'test_thing_type_arn' + thing_type_arn = "test_thing_type_arn" create_thing_type_ret = dict( - thingTypeName=thing_type_name, - thingTypeArn=thing_type_arn + thingTypeName=thing_type_name, thingTypeArn=thing_type_arn ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) class BotoIoTTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context={}) - return {boto_iot: {'__utils__': utils}} + opts, whitelist=["boto3", "args", "systemd", "path", "platform"], context={} + ) + return {boto_iot: {"__utils__": utils}} def setUp(self): super(BotoIoTTestCaseBase, self).setUp() @@ -140,16 +145,18 @@ class BotoIoTTestCaseBase(TestCase, LoaderModuleMockMixin): # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn @@ -158,614 +165,868 @@ class BotoIoTTestCaseMixin(object): class BotoIoTThingTypeTestCase(BotoIoTTestCaseBase, BotoIoTTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_iot module - ''' + """ - def test_that_when_checking_if_a_thing_type_exists_and_a_thing_type_exists_the_thing_type_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_thing_type_exists_and_a_thing_type_exists_the_thing_type_exists_method_returns_true( + self, + ): + """ Tests checking iot thing type existence when the iot thing type already exists - ''' + """ self.conn.describe_thing_type.return_value = thing_type_ret - result = boto_iot.thing_type_exists(thingTypeName=thing_type_name, **conn_parameters) + result = boto_iot.thing_type_exists( + thingTypeName=thing_type_name, **conn_parameters + ) - self.assertTrue(result['exists']) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_a_thing_type_exists_and_a_thing_type_does_not_exist_the_thing_type_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_thing_type_exists_and_a_thing_type_does_not_exist_the_thing_type_exists_method_returns_false( + self, + ): + """ Tests checking iot thing type existence when the iot thing type does not exist - ''' + """ self.conn.describe_thing_type.side_effect = not_found_error - result = boto_iot.thing_type_exists(thingTypeName='non existent thing type', **conn_parameters) + result = boto_iot.thing_type_exists( + thingTypeName="non existent thing type", **conn_parameters + ) - self.assertFalse(result['exists']) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_a_thing_type_exists_and_boto3_returns_an_error_the_thing_type_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_a_thing_type_exists_and_boto3_returns_an_error_the_thing_type_exists_method_returns_error( + self, + ): + """ Tests checking iot thing type existence when boto returns an error - ''' - self.conn.describe_thing_type.side_effect = ClientError(error_content, 'describe_thing_type') - result = boto_iot.thing_type_exists(thingTypeName='mythingtype', **conn_parameters) + """ + self.conn.describe_thing_type.side_effect = ClientError( + error_content, "describe_thing_type" + ) + result = boto_iot.thing_type_exists( + thingTypeName="mythingtype", **conn_parameters + ) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('describe_thing_type')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("describe_thing_type"), + ) - def test_that_when_describing_thing_type_and_thing_type_exists_the_describe_thing_type_method_returns_thing_type(self): - ''' + def test_that_when_describing_thing_type_and_thing_type_exists_the_describe_thing_type_method_returns_thing_type( + self, + ): + """ Tests describe thing type for an existing thing type - ''' + """ self.conn.describe_thing_type.return_value = thing_type_ret - result = boto_iot.describe_thing_type(thingTypeName=thing_type_name, **conn_parameters) + result = boto_iot.describe_thing_type( + thingTypeName=thing_type_name, **conn_parameters + ) - self.assertEqual(result.get('thing_type'), thing_type_ret) + self.assertEqual(result.get("thing_type"), thing_type_ret) - def test_that_when_describing_thing_type_and_thing_type_does_not_exists_the_describe_thing_type_method_returns_none(self): - ''' + def test_that_when_describing_thing_type_and_thing_type_does_not_exists_the_describe_thing_type_method_returns_none( + self, + ): + """ Tests describe thing type for an non existent thing type - ''' + """ self.conn.describe_thing_type.side_effect = not_found_error - result = boto_iot.describe_thing_type(thingTypeName='non existent thing type', **conn_parameters) + result = boto_iot.describe_thing_type( + thingTypeName="non existent thing type", **conn_parameters + ) - self.assertEqual(result.get('thing_type'), None) + self.assertEqual(result.get("thing_type"), None) - def test_that_when_describing_thing_type_and_boto3_returns_error_an_error_the_describe_thing_type_method_returns_error(self): - self.conn.describe_thing_type.side_effect = ClientError(error_content, 'describe_thing_type') - result = boto_iot.describe_thing_type(thingTypeName='mythingtype', **conn_parameters) + def test_that_when_describing_thing_type_and_boto3_returns_error_an_error_the_describe_thing_type_method_returns_error( + self, + ): + self.conn.describe_thing_type.side_effect = ClientError( + error_content, "describe_thing_type" + ) + result = boto_iot.describe_thing_type( + thingTypeName="mythingtype", **conn_parameters + ) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('describe_thing_type')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("describe_thing_type"), + ) - def test_that_when_creating_a_thing_type_succeeds_the_create_thing_type_method_returns_true(self): - ''' + def test_that_when_creating_a_thing_type_succeeds_the_create_thing_type_method_returns_true( + self, + ): + """ tests True when thing type created - ''' + """ self.conn.create_thing_type.return_value = create_thing_type_ret - result = boto_iot.create_thing_type(thingTypeName=thing_type_name, - thingTypeDescription=thing_type_desc, - searchableAttributesList=[thing_type_attr_1], - **conn_parameters) - self.assertTrue(result['created']) - self.assertTrue(result['thingTypeArn'], thing_type_arn) + result = boto_iot.create_thing_type( + thingTypeName=thing_type_name, + thingTypeDescription=thing_type_desc, + searchableAttributesList=[thing_type_attr_1], + **conn_parameters + ) + self.assertTrue(result["created"]) + self.assertTrue(result["thingTypeArn"], thing_type_arn) - def test_that_when_creating_a_thing_type_fails_the_create_thing_type_method_returns_error(self): - ''' + def test_that_when_creating_a_thing_type_fails_the_create_thing_type_method_returns_error( + self, + ): + """ tests False when thing type not created - ''' - self.conn.create_thing_type.side_effect = ClientError(error_content, 'create_thing_type') - result = boto_iot.create_thing_type(thingTypeName=thing_type_name, - thingTypeDescription=thing_type_desc, - searchableAttributesList=[thing_type_attr_1], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('create_thing_type')) + """ + self.conn.create_thing_type.side_effect = ClientError( + error_content, "create_thing_type" + ) + result = boto_iot.create_thing_type( + thingTypeName=thing_type_name, + thingTypeDescription=thing_type_desc, + searchableAttributesList=[thing_type_attr_1], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("create_thing_type"), + ) - def test_that_when_deprecating_a_thing_type_succeeds_the_deprecate_thing_type_method_returns_true(self): - ''' + def test_that_when_deprecating_a_thing_type_succeeds_the_deprecate_thing_type_method_returns_true( + self, + ): + """ tests True when deprecate thing type succeeds - ''' + """ self.conn.deprecate_thing_type.return_value = {} - result = boto_iot.deprecate_thing_type(thingTypeName=thing_type_name, - undoDeprecate=False, - **conn_parameters) + result = boto_iot.deprecate_thing_type( + thingTypeName=thing_type_name, undoDeprecate=False, **conn_parameters + ) - self.assertTrue(result.get('deprecated')) - self.assertEqual(result.get('error'), None) + self.assertTrue(result.get("deprecated")) + self.assertEqual(result.get("error"), None) - def test_that_when_deprecating_a_thing_type_fails_the_deprecate_thing_type_method_returns_error(self): - ''' + def test_that_when_deprecating_a_thing_type_fails_the_deprecate_thing_type_method_returns_error( + self, + ): + """ tests False when thing type fails to deprecate - ''' - self.conn.deprecate_thing_type.side_effect = ClientError(error_content, 'deprecate_thing_type') - result = boto_iot.deprecate_thing_type(thingTypeName=thing_type_name, - undoDeprecate=False, - **conn_parameters) + """ + self.conn.deprecate_thing_type.side_effect = ClientError( + error_content, "deprecate_thing_type" + ) + result = boto_iot.deprecate_thing_type( + thingTypeName=thing_type_name, undoDeprecate=False, **conn_parameters + ) - self.assertFalse(result.get('deprecated')) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('deprecate_thing_type')) + self.assertFalse(result.get("deprecated")) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("deprecate_thing_type"), + ) - def test_that_when_deleting_a_thing_type_succeeds_the_delete_thing_type_method_returns_true(self): - ''' + def test_that_when_deleting_a_thing_type_succeeds_the_delete_thing_type_method_returns_true( + self, + ): + """ tests True when delete thing type succeeds - ''' + """ self.conn.delete_thing_type.return_value = {} - result = boto_iot.delete_thing_type(thingTypeName=thing_type_name, **conn_parameters) + result = boto_iot.delete_thing_type( + thingTypeName=thing_type_name, **conn_parameters + ) - self.assertTrue(result.get('deleted')) - self.assertEqual(result.get('error'), None) + self.assertTrue(result.get("deleted")) + self.assertEqual(result.get("error"), None) - def test_that_when_deleting_a_thing_type_fails_the_delete_thing_type_method_returns_error(self): - ''' + def test_that_when_deleting_a_thing_type_fails_the_delete_thing_type_method_returns_error( + self, + ): + """ tests False when delete thing type fails - ''' - self.conn.delete_thing_type.side_effect = ClientError(error_content, 'delete_thing_type') - result = boto_iot.delete_thing_type(thingTypeName=thing_type_name, **conn_parameters) - self.assertFalse(result.get('deleted')) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('delete_thing_type')) + """ + self.conn.delete_thing_type.side_effect = ClientError( + error_content, "delete_thing_type" + ) + result = boto_iot.delete_thing_type( + thingTypeName=thing_type_name, **conn_parameters + ) + self.assertFalse(result.get("deleted")) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("delete_thing_type"), + ) class BotoIoTPolicyTestCase(BotoIoTTestCaseBase, BotoIoTTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_iot module - ''' + """ - def test_that_when_checking_if_a_policy_exists_and_a_policy_exists_the_policy_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_policy_exists_and_a_policy_exists_the_policy_exists_method_returns_true( + self, + ): + """ Tests checking iot policy existence when the iot policy already exists - ''' - self.conn.get_policy.return_value = {'policy': policy_ret} - result = boto_iot.policy_exists(policyName=policy_ret['policyName'], **conn_parameters) + """ + self.conn.get_policy.return_value = {"policy": policy_ret} + result = boto_iot.policy_exists( + policyName=policy_ret["policyName"], **conn_parameters + ) - self.assertTrue(result['exists']) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_a_policy_exists_and_a_policy_does_not_exist_the_policy_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_policy_exists_and_a_policy_does_not_exist_the_policy_exists_method_returns_false( + self, + ): + """ Tests checking iot policy existence when the iot policy does not exist - ''' + """ self.conn.get_policy.side_effect = not_found_error - result = boto_iot.policy_exists(policyName='mypolicy', **conn_parameters) + result = boto_iot.policy_exists(policyName="mypolicy", **conn_parameters) - self.assertFalse(result['exists']) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_a_policy_exists_and_boto3_returns_an_error_the_policy_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_a_policy_exists_and_boto3_returns_an_error_the_policy_exists_method_returns_error( + self, + ): + """ Tests checking iot policy existence when boto returns an error - ''' - self.conn.get_policy.side_effect = ClientError(error_content, 'get_policy') - result = boto_iot.policy_exists(policyName='mypolicy', **conn_parameters) + """ + self.conn.get_policy.side_effect = ClientError(error_content, "get_policy") + result = boto_iot.policy_exists(policyName="mypolicy", **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('get_policy')) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("get_policy") + ) - def test_that_when_creating_a_policy_succeeds_the_create_policy_method_returns_true(self): - ''' + def test_that_when_creating_a_policy_succeeds_the_create_policy_method_returns_true( + self, + ): + """ tests True policy created. - ''' + """ self.conn.create_policy.return_value = policy_ret - result = boto_iot.create_policy(policyName=policy_ret['policyName'], - policyDocument=policy_ret['policyDocument'], - **conn_parameters) + result = boto_iot.create_policy( + policyName=policy_ret["policyName"], + policyDocument=policy_ret["policyDocument"], + **conn_parameters + ) - self.assertTrue(result['created']) + self.assertTrue(result["created"]) - def test_that_when_creating_a_policy_fails_the_create_policy_method_returns_error(self): - ''' + def test_that_when_creating_a_policy_fails_the_create_policy_method_returns_error( + self, + ): + """ tests False policy not created. - ''' - self.conn.create_policy.side_effect = ClientError(error_content, 'create_policy') - result = boto_iot.create_policy(policyName=policy_ret['policyName'], - policyDocument=policy_ret['policyDocument'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('create_policy')) + """ + self.conn.create_policy.side_effect = ClientError( + error_content, "create_policy" + ) + result = boto_iot.create_policy( + policyName=policy_ret["policyName"], + policyDocument=policy_ret["policyDocument"], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("create_policy"), + ) - def test_that_when_deleting_a_policy_succeeds_the_delete_policy_method_returns_true(self): - ''' + def test_that_when_deleting_a_policy_succeeds_the_delete_policy_method_returns_true( + self, + ): + """ tests True policy deleted. - ''' - result = boto_iot.delete_policy(policyName='testpolicy', - **conn_parameters) + """ + result = boto_iot.delete_policy(policyName="testpolicy", **conn_parameters) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_a_policy_fails_the_delete_policy_method_returns_false(self): - ''' + def test_that_when_deleting_a_policy_fails_the_delete_policy_method_returns_false( + self, + ): + """ tests False policy not deleted. - ''' - self.conn.delete_policy.side_effect = ClientError(error_content, 'delete_policy') - result = boto_iot.delete_policy(policyName='testpolicy', - **conn_parameters) - self.assertFalse(result['deleted']) + """ + self.conn.delete_policy.side_effect = ClientError( + error_content, "delete_policy" + ) + result = boto_iot.delete_policy(policyName="testpolicy", **conn_parameters) + self.assertFalse(result["deleted"]) - def test_that_when_describing_policy_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_policy_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters if policy exists - ''' - self.conn.get_policy.return_value = {'policy': policy_ret} + """ + self.conn.get_policy.return_value = {"policy": policy_ret} - result = boto_iot.describe_policy(policyName=policy_ret['policyName'], **conn_parameters) + result = boto_iot.describe_policy( + policyName=policy_ret["policyName"], **conn_parameters + ) - self.assertTrue(result['policy']) + self.assertTrue(result["policy"]) - def test_that_when_describing_policy_it_returns_the_dict_of_properties_returns_false(self): - ''' + def test_that_when_describing_policy_it_returns_the_dict_of_properties_returns_false( + self, + ): + """ Tests describing parameters if policy does not exist - ''' + """ self.conn.get_policy.side_effect = not_found_error - result = boto_iot.describe_policy(policyName='testpolicy', **conn_parameters) + result = boto_iot.describe_policy(policyName="testpolicy", **conn_parameters) - self.assertFalse(result['policy']) + self.assertFalse(result["policy"]) def test_that_when_describing_policy_on_client_error_it_returns_error(self): - ''' + """ Tests describing parameters failure - ''' - self.conn.get_policy.side_effect = ClientError(error_content, 'get_policy') - result = boto_iot.describe_policy(policyName='testpolicy', **conn_parameters) - self.assertTrue('error' in result) + """ + self.conn.get_policy.side_effect = ClientError(error_content, "get_policy") + result = boto_iot.describe_policy(policyName="testpolicy", **conn_parameters) + self.assertTrue("error" in result) - def test_that_when_checking_if_a_policy_version_exists_and_a_policy_version_exists_the_policy_version_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_policy_version_exists_and_a_policy_version_exists_the_policy_version_exists_method_returns_true( + self, + ): + """ Tests checking iot policy existence when the iot policy version already exists - ''' - self.conn.get_policy.return_value = {'policy': policy_ret} - result = boto_iot.policy_version_exists(policyName=policy_ret['policyName'], - policyVersionId=1, - **conn_parameters) + """ + self.conn.get_policy.return_value = {"policy": policy_ret} + result = boto_iot.policy_version_exists( + policyName=policy_ret["policyName"], policyVersionId=1, **conn_parameters + ) - self.assertTrue(result['exists']) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_a_policy_version_exists_and_a_policy_version_does_not_exist_the_policy_version_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_policy_version_exists_and_a_policy_version_does_not_exist_the_policy_version_exists_method_returns_false( + self, + ): + """ Tests checking iot policy_version existence when the iot policy_version does not exist - ''' + """ self.conn.get_policy_version.side_effect = not_found_error - result = boto_iot.policy_version_exists(policyName=policy_ret['policyName'], - policyVersionId=1, - **conn_parameters) + result = boto_iot.policy_version_exists( + policyName=policy_ret["policyName"], policyVersionId=1, **conn_parameters + ) - self.assertFalse(result['exists']) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_a_policy_version_exists_and_boto3_returns_an_error_the_policy_version_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_a_policy_version_exists_and_boto3_returns_an_error_the_policy_version_exists_method_returns_error( + self, + ): + """ Tests checking iot policy_version existence when boto returns an error - ''' - self.conn.get_policy_version.side_effect = ClientError(error_content, 'get_policy_version') - result = boto_iot.policy_version_exists(policyName=policy_ret['policyName'], - policyVersionId=1, - **conn_parameters) + """ + self.conn.get_policy_version.side_effect = ClientError( + error_content, "get_policy_version" + ) + result = boto_iot.policy_version_exists( + policyName=policy_ret["policyName"], policyVersionId=1, **conn_parameters + ) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('get_policy_version')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("get_policy_version"), + ) - def test_that_when_creating_a_policy_version_succeeds_the_create_policy_version_method_returns_true(self): - ''' + def test_that_when_creating_a_policy_version_succeeds_the_create_policy_version_method_returns_true( + self, + ): + """ tests True policy_version created. - ''' + """ self.conn.create_policy_version.return_value = policy_ret - result = boto_iot.create_policy_version(policyName=policy_ret['policyName'], - policyDocument=policy_ret['policyDocument'], - **conn_parameters) + result = boto_iot.create_policy_version( + policyName=policy_ret["policyName"], + policyDocument=policy_ret["policyDocument"], + **conn_parameters + ) - self.assertTrue(result['created']) + self.assertTrue(result["created"]) - def test_that_when_creating_a_policy_version_fails_the_create_policy_version_method_returns_error(self): - ''' + def test_that_when_creating_a_policy_version_fails_the_create_policy_version_method_returns_error( + self, + ): + """ tests False policy_version not created. - ''' - self.conn.create_policy_version.side_effect = ClientError(error_content, 'create_policy_version') - result = boto_iot.create_policy_version(policyName=policy_ret['policyName'], - policyDocument=policy_ret['policyDocument'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('create_policy_version')) + """ + self.conn.create_policy_version.side_effect = ClientError( + error_content, "create_policy_version" + ) + result = boto_iot.create_policy_version( + policyName=policy_ret["policyName"], + policyDocument=policy_ret["policyDocument"], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("create_policy_version"), + ) - def test_that_when_deleting_a_policy_version_succeeds_the_delete_policy_version_method_returns_true(self): - ''' + def test_that_when_deleting_a_policy_version_succeeds_the_delete_policy_version_method_returns_true( + self, + ): + """ tests True policy_version deleted. - ''' - result = boto_iot.delete_policy_version(policyName='testpolicy', - policyVersionId=1, - **conn_parameters) + """ + result = boto_iot.delete_policy_version( + policyName="testpolicy", policyVersionId=1, **conn_parameters + ) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_a_policy_version_fails_the_delete_policy_version_method_returns_false(self): - ''' + def test_that_when_deleting_a_policy_version_fails_the_delete_policy_version_method_returns_false( + self, + ): + """ tests False policy_version not deleted. - ''' - self.conn.delete_policy_version.side_effect = ClientError(error_content, 'delete_policy_version') - result = boto_iot.delete_policy_version(policyName='testpolicy', - policyVersionId=1, - **conn_parameters) - self.assertFalse(result['deleted']) + """ + self.conn.delete_policy_version.side_effect = ClientError( + error_content, "delete_policy_version" + ) + result = boto_iot.delete_policy_version( + policyName="testpolicy", policyVersionId=1, **conn_parameters + ) + self.assertFalse(result["deleted"]) - def test_that_when_describing_policy_version_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_policy_version_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters if policy_version exists - ''' - self.conn.get_policy_version.return_value = {'policy': policy_ret} + """ + self.conn.get_policy_version.return_value = {"policy": policy_ret} - result = boto_iot.describe_policy_version(policyName=policy_ret['policyName'], - policyVersionId=1, - **conn_parameters) + result = boto_iot.describe_policy_version( + policyName=policy_ret["policyName"], policyVersionId=1, **conn_parameters + ) - self.assertTrue(result['policy']) + self.assertTrue(result["policy"]) - def test_that_when_describing_policy_version_it_returns_the_dict_of_properties_returns_false(self): - ''' + def test_that_when_describing_policy_version_it_returns_the_dict_of_properties_returns_false( + self, + ): + """ Tests describing parameters if policy_version does not exist - ''' + """ self.conn.get_policy_version.side_effect = not_found_error - result = boto_iot.describe_policy_version(policyName=policy_ret['policyName'], - policyVersionId=1, - **conn_parameters) + result = boto_iot.describe_policy_version( + policyName=policy_ret["policyName"], policyVersionId=1, **conn_parameters + ) - self.assertFalse(result['policy']) + self.assertFalse(result["policy"]) def test_that_when_describing_policy_version_on_client_error_it_returns_error(self): - ''' + """ Tests describing parameters failure - ''' - self.conn.get_policy_version.side_effect = ClientError(error_content, 'get_policy_version') - result = boto_iot.describe_policy_version(policyName=policy_ret['policyName'], - policyVersionId=1, - **conn_parameters) - self.assertTrue('error' in result) + """ + self.conn.get_policy_version.side_effect = ClientError( + error_content, "get_policy_version" + ) + result = boto_iot.describe_policy_version( + policyName=policy_ret["policyName"], policyVersionId=1, **conn_parameters + ) + self.assertTrue("error" in result) - def test_that_when_listing_policies_succeeds_the_list_policies_method_returns_true(self): - ''' + def test_that_when_listing_policies_succeeds_the_list_policies_method_returns_true( + self, + ): + """ tests True policies listed. - ''' - self.conn.list_policies.return_value = {'policies': [policy_ret]} + """ + self.conn.list_policies.return_value = {"policies": [policy_ret]} result = boto_iot.list_policies(**conn_parameters) - self.assertTrue(result['policies']) + self.assertTrue(result["policies"]) def test_that_when_listing_policy_fails_the_list_policy_method_returns_false(self): - ''' + """ tests False no policy listed. - ''' - self.conn.list_policies.return_value = {'policies': []} + """ + self.conn.list_policies.return_value = {"policies": []} result = boto_iot.list_policies(**conn_parameters) - self.assertFalse(result['policies']) + self.assertFalse(result["policies"]) def test_that_when_listing_policy_fails_the_list_policy_method_returns_error(self): - ''' + """ tests False policy error. - ''' - self.conn.list_policies.side_effect = ClientError(error_content, 'list_policies') + """ + self.conn.list_policies.side_effect = ClientError( + error_content, "list_policies" + ) result = boto_iot.list_policies(**conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('list_policies')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("list_policies"), + ) - def test_that_when_listing_policy_versions_succeeds_the_list_policy_versions_method_returns_true(self): - ''' + def test_that_when_listing_policy_versions_succeeds_the_list_policy_versions_method_returns_true( + self, + ): + """ tests True policy versions listed. - ''' - self.conn.list_policy_versions.return_value = {'policyVersions': [policy_ret]} - result = boto_iot.list_policy_versions(policyName='testpolicy', - **conn_parameters) + """ + self.conn.list_policy_versions.return_value = {"policyVersions": [policy_ret]} + result = boto_iot.list_policy_versions( + policyName="testpolicy", **conn_parameters + ) - self.assertTrue(result['policyVersions']) + self.assertTrue(result["policyVersions"]) - def test_that_when_listing_policy_versions_fails_the_list_policy_versions_method_returns_false(self): - ''' + def test_that_when_listing_policy_versions_fails_the_list_policy_versions_method_returns_false( + self, + ): + """ tests False no policy versions listed. - ''' - self.conn.list_policy_versions.return_value = {'policyVersions': []} - result = boto_iot.list_policy_versions(policyName='testpolicy', - **conn_parameters) - self.assertFalse(result['policyVersions']) + """ + self.conn.list_policy_versions.return_value = {"policyVersions": []} + result = boto_iot.list_policy_versions( + policyName="testpolicy", **conn_parameters + ) + self.assertFalse(result["policyVersions"]) - def test_that_when_listing_policy_versions_fails_the_list_policy_versions_method_returns_error(self): - ''' + def test_that_when_listing_policy_versions_fails_the_list_policy_versions_method_returns_error( + self, + ): + """ tests False policy versions error. - ''' - self.conn.list_policy_versions.side_effect = ClientError(error_content, 'list_policy_versions') - result = boto_iot.list_policy_versions(policyName='testpolicy', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('list_policy_versions')) + """ + self.conn.list_policy_versions.side_effect = ClientError( + error_content, "list_policy_versions" + ) + result = boto_iot.list_policy_versions( + policyName="testpolicy", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("list_policy_versions"), + ) - def test_that_when_setting_default_policy_version_succeeds_the_set_default_policy_version_method_returns_true(self): - ''' + def test_that_when_setting_default_policy_version_succeeds_the_set_default_policy_version_method_returns_true( + self, + ): + """ tests True policy version set. - ''' - result = boto_iot.set_default_policy_version(policyName='testpolicy', - policyVersionId=1, - **conn_parameters) + """ + result = boto_iot.set_default_policy_version( + policyName="testpolicy", policyVersionId=1, **conn_parameters + ) - self.assertTrue(result['changed']) + self.assertTrue(result["changed"]) - def test_that_when_set_default_policy_version_fails_the_set_default_policy_version_method_returns_error(self): - ''' + def test_that_when_set_default_policy_version_fails_the_set_default_policy_version_method_returns_error( + self, + ): + """ tests False policy version error. - ''' - self.conn.set_default_policy_version.side_effect = \ - ClientError(error_content, 'set_default_policy_version') - result = boto_iot.set_default_policy_version(policyName='testpolicy', - policyVersionId=1, - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('set_default_policy_version')) + """ + self.conn.set_default_policy_version.side_effect = ClientError( + error_content, "set_default_policy_version" + ) + result = boto_iot.set_default_policy_version( + policyName="testpolicy", policyVersionId=1, **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("set_default_policy_version"), + ) - def test_that_when_list_principal_policies_succeeds_the_list_principal_policies_method_returns_true(self): - ''' + def test_that_when_list_principal_policies_succeeds_the_list_principal_policies_method_returns_true( + self, + ): + """ tests True policies listed. - ''' - self.conn.list_principal_policies.return_value = {'policies': [policy_ret]} - result = boto_iot.list_principal_policies(principal='us-east-1:GUID-GUID-GUID', - **conn_parameters) + """ + self.conn.list_principal_policies.return_value = {"policies": [policy_ret]} + result = boto_iot.list_principal_policies( + principal="us-east-1:GUID-GUID-GUID", **conn_parameters + ) - self.assertTrue(result['policies']) + self.assertTrue(result["policies"]) - def test_that_when_list_principal_policies_fails_the_list_principal_policies_method_returns_error(self): - ''' + def test_that_when_list_principal_policies_fails_the_list_principal_policies_method_returns_error( + self, + ): + """ tests False policy version error. - ''' - self.conn.list_principal_policies.side_effect = \ - ClientError(error_content, 'list_principal_policies') - result = boto_iot.list_principal_policies(principal='us-east-1:GUID-GUID-GUID', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('list_principal_policies')) + """ + self.conn.list_principal_policies.side_effect = ClientError( + error_content, "list_principal_policies" + ) + result = boto_iot.list_principal_policies( + principal="us-east-1:GUID-GUID-GUID", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("list_principal_policies"), + ) - def test_that_when_attach_principal_policy_succeeds_the_attach_principal_policy_method_returns_true(self): - ''' + def test_that_when_attach_principal_policy_succeeds_the_attach_principal_policy_method_returns_true( + self, + ): + """ tests True policy attached. - ''' - result = boto_iot.attach_principal_policy(policyName='testpolicy', - principal='us-east-1:GUID-GUID-GUID', - **conn_parameters) + """ + result = boto_iot.attach_principal_policy( + policyName="testpolicy", + principal="us-east-1:GUID-GUID-GUID", + **conn_parameters + ) - self.assertTrue(result['attached']) + self.assertTrue(result["attached"]) - def test_that_when_attach_principal_policy_version_fails_the_attach_principal_policy_version_method_returns_error(self): - ''' + def test_that_when_attach_principal_policy_version_fails_the_attach_principal_policy_version_method_returns_error( + self, + ): + """ tests False policy version error. - ''' - self.conn.attach_principal_policy.side_effect = \ - ClientError(error_content, 'attach_principal_policy') - result = boto_iot.attach_principal_policy(policyName='testpolicy', - principal='us-east-1:GUID-GUID-GUID', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('attach_principal_policy')) + """ + self.conn.attach_principal_policy.side_effect = ClientError( + error_content, "attach_principal_policy" + ) + result = boto_iot.attach_principal_policy( + policyName="testpolicy", + principal="us-east-1:GUID-GUID-GUID", + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("attach_principal_policy"), + ) - def test_that_when_detach_principal_policy_succeeds_the_detach_principal_policy_method_returns_true(self): - ''' + def test_that_when_detach_principal_policy_succeeds_the_detach_principal_policy_method_returns_true( + self, + ): + """ tests True policy detached. - ''' - result = boto_iot.detach_principal_policy(policyName='testpolicy', - principal='us-east-1:GUID-GUID-GUID', - **conn_parameters) + """ + result = boto_iot.detach_principal_policy( + policyName="testpolicy", + principal="us-east-1:GUID-GUID-GUID", + **conn_parameters + ) - self.assertTrue(result['detached']) + self.assertTrue(result["detached"]) - def test_that_when_detach_principal_policy_version_fails_the_detach_principal_policy_version_method_returns_error(self): - ''' + def test_that_when_detach_principal_policy_version_fails_the_detach_principal_policy_version_method_returns_error( + self, + ): + """ tests False policy version error. - ''' - self.conn.detach_principal_policy.side_effect = \ - ClientError(error_content, 'detach_principal_policy') - result = boto_iot.detach_principal_policy(policyName='testpolicy', - principal='us-east-1:GUID-GUID-GUID', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('detach_principal_policy')) + """ + self.conn.detach_principal_policy.side_effect = ClientError( + error_content, "detach_principal_policy" + ) + result = boto_iot.detach_principal_policy( + policyName="testpolicy", + principal="us-east-1:GUID-GUID-GUID", + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("detach_principal_policy"), + ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}. The botocore' - ' module must be greater than or equal to' - ' version {1}.' - .format(required_boto3_version, required_botocore_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}. The botocore" + " module must be greater than or equal to" + " version {1}.".format(required_boto3_version, required_botocore_version), +) class BotoIoTTopicRuleTestCase(BotoIoTTestCaseBase, BotoIoTTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_iot module - ''' + """ - def test_that_when_checking_if_a_topic_rule_exists_and_a_topic_rule_exists_the_topic_rule_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_topic_rule_exists_and_a_topic_rule_exists_the_topic_rule_exists_method_returns_true( + self, + ): + """ Tests checking iot topic_rule existence when the iot topic_rule already exists - ''' - self.conn.get_topic_rule.return_value = {'rule': topic_rule_ret} - result = boto_iot.topic_rule_exists(ruleName=topic_rule_ret['ruleName'], - **conn_parameters) + """ + self.conn.get_topic_rule.return_value = {"rule": topic_rule_ret} + result = boto_iot.topic_rule_exists( + ruleName=topic_rule_ret["ruleName"], **conn_parameters + ) - self.assertTrue(result['exists']) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_a_rule_exists_and_a_rule_does_not_exist_the_topic_rule_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_rule_exists_and_a_rule_does_not_exist_the_topic_rule_exists_method_returns_false( + self, + ): + """ Tests checking iot rule existence when the iot rule does not exist - ''' + """ self.conn.get_topic_rule.side_effect = topic_rule_not_found_error - result = boto_iot.topic_rule_exists(ruleName='mypolicy', **conn_parameters) + result = boto_iot.topic_rule_exists(ruleName="mypolicy", **conn_parameters) - self.assertFalse(result['exists']) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_a_topic_rule_exists_and_boto3_returns_an_error_the_topic_rule_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_a_topic_rule_exists_and_boto3_returns_an_error_the_topic_rule_exists_method_returns_error( + self, + ): + """ Tests checking iot topic_rule existence when boto returns an error - ''' - self.conn.get_topic_rule.side_effect = ClientError(error_content, 'get_topic_rule') - result = boto_iot.topic_rule_exists(ruleName='myrule', **conn_parameters) + """ + self.conn.get_topic_rule.side_effect = ClientError( + error_content, "get_topic_rule" + ) + result = boto_iot.topic_rule_exists(ruleName="myrule", **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('get_topic_rule')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("get_topic_rule"), + ) - def test_that_when_creating_a_topic_rule_succeeds_the_create_topic_rule_method_returns_true(self): - ''' + def test_that_when_creating_a_topic_rule_succeeds_the_create_topic_rule_method_returns_true( + self, + ): + """ tests True topic_rule created. - ''' + """ self.conn.create_topic_rule.return_value = topic_rule_ret - result = boto_iot.create_topic_rule(ruleName=topic_rule_ret['ruleName'], - sql=topic_rule_ret['sql'], - description=topic_rule_ret['description'], - actions=topic_rule_ret['actions'], - **conn_parameters) + result = boto_iot.create_topic_rule( + ruleName=topic_rule_ret["ruleName"], + sql=topic_rule_ret["sql"], + description=topic_rule_ret["description"], + actions=topic_rule_ret["actions"], + **conn_parameters + ) - self.assertTrue(result['created']) + self.assertTrue(result["created"]) - def test_that_when_creating_a_topic_rule_fails_the_create_topic_rule_method_returns_error(self): - ''' + def test_that_when_creating_a_topic_rule_fails_the_create_topic_rule_method_returns_error( + self, + ): + """ tests False topic_rule not created. - ''' - self.conn.create_topic_rule.side_effect = ClientError(error_content, 'create_topic_rule') - result = boto_iot.create_topic_rule(ruleName=topic_rule_ret['ruleName'], - sql=topic_rule_ret['sql'], - description=topic_rule_ret['description'], - actions=topic_rule_ret['actions'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('create_topic_rule')) + """ + self.conn.create_topic_rule.side_effect = ClientError( + error_content, "create_topic_rule" + ) + result = boto_iot.create_topic_rule( + ruleName=topic_rule_ret["ruleName"], + sql=topic_rule_ret["sql"], + description=topic_rule_ret["description"], + actions=topic_rule_ret["actions"], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("create_topic_rule"), + ) - def test_that_when_replacing_a_topic_rule_succeeds_the_replace_topic_rule_method_returns_true(self): - ''' + def test_that_when_replacing_a_topic_rule_succeeds_the_replace_topic_rule_method_returns_true( + self, + ): + """ tests True topic_rule replaced. - ''' + """ self.conn.replace_topic_rule.return_value = topic_rule_ret - result = boto_iot.replace_topic_rule(ruleName=topic_rule_ret['ruleName'], - sql=topic_rule_ret['sql'], - description=topic_rule_ret['description'], - actions=topic_rule_ret['actions'], - **conn_parameters) + result = boto_iot.replace_topic_rule( + ruleName=topic_rule_ret["ruleName"], + sql=topic_rule_ret["sql"], + description=topic_rule_ret["description"], + actions=topic_rule_ret["actions"], + **conn_parameters + ) - self.assertTrue(result['replaced']) + self.assertTrue(result["replaced"]) - def test_that_when_replacing_a_topic_rule_fails_the_replace_topic_rule_method_returns_error(self): - ''' + def test_that_when_replacing_a_topic_rule_fails_the_replace_topic_rule_method_returns_error( + self, + ): + """ tests False topic_rule not replaced. - ''' - self.conn.replace_topic_rule.side_effect = ClientError(error_content, 'replace_topic_rule') - result = boto_iot.replace_topic_rule(ruleName=topic_rule_ret['ruleName'], - sql=topic_rule_ret['sql'], - description=topic_rule_ret['description'], - actions=topic_rule_ret['actions'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('replace_topic_rule')) + """ + self.conn.replace_topic_rule.side_effect = ClientError( + error_content, "replace_topic_rule" + ) + result = boto_iot.replace_topic_rule( + ruleName=topic_rule_ret["ruleName"], + sql=topic_rule_ret["sql"], + description=topic_rule_ret["description"], + actions=topic_rule_ret["actions"], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("replace_topic_rule"), + ) - def test_that_when_deleting_a_topic_rule_succeeds_the_delete_topic_rule_method_returns_true(self): - ''' + def test_that_when_deleting_a_topic_rule_succeeds_the_delete_topic_rule_method_returns_true( + self, + ): + """ tests True topic_rule deleted. - ''' - result = boto_iot.delete_topic_rule(ruleName='testrule', - **conn_parameters) + """ + result = boto_iot.delete_topic_rule(ruleName="testrule", **conn_parameters) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_a_topic_rule_fails_the_delete_topic_rule_method_returns_false(self): - ''' + def test_that_when_deleting_a_topic_rule_fails_the_delete_topic_rule_method_returns_false( + self, + ): + """ tests False topic_rule not deleted. - ''' - self.conn.delete_topic_rule.side_effect = ClientError(error_content, 'delete_topic_rule') - result = boto_iot.delete_topic_rule(ruleName='testrule', - **conn_parameters) - self.assertFalse(result['deleted']) + """ + self.conn.delete_topic_rule.side_effect = ClientError( + error_content, "delete_topic_rule" + ) + result = boto_iot.delete_topic_rule(ruleName="testrule", **conn_parameters) + self.assertFalse(result["deleted"]) - def test_that_when_describing_topic_rule_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_topic_rule_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters if topic_rule exists - ''' - self.conn.get_topic_rule.return_value = {'rule': topic_rule_ret} + """ + self.conn.get_topic_rule.return_value = {"rule": topic_rule_ret} - result = boto_iot.describe_topic_rule(ruleName=topic_rule_ret['ruleName'], - **conn_parameters) + result = boto_iot.describe_topic_rule( + ruleName=topic_rule_ret["ruleName"], **conn_parameters + ) - self.assertTrue(result['rule']) + self.assertTrue(result["rule"]) def test_that_when_describing_topic_rule_on_client_error_it_returns_error(self): - ''' + """ Tests describing parameters failure - ''' - self.conn.get_topic_rule.side_effect = ClientError(error_content, 'get_topic_rule') - result = boto_iot.describe_topic_rule(ruleName='testrule', **conn_parameters) - self.assertTrue('error' in result) + """ + self.conn.get_topic_rule.side_effect = ClientError( + error_content, "get_topic_rule" + ) + result = boto_iot.describe_topic_rule(ruleName="testrule", **conn_parameters) + self.assertTrue("error" in result) - def test_that_when_listing_topic_rules_succeeds_the_list_topic_rules_method_returns_true(self): - ''' + def test_that_when_listing_topic_rules_succeeds_the_list_topic_rules_method_returns_true( + self, + ): + """ tests True topic_rules listed. - ''' - self.conn.list_topic_rules.return_value = {'rules': [topic_rule_ret]} + """ + self.conn.list_topic_rules.return_value = {"rules": [topic_rule_ret]} result = boto_iot.list_topic_rules(**conn_parameters) - self.assertTrue(result['rules']) + self.assertTrue(result["rules"]) - def test_that_when_listing_topic_rules_fails_the_list_topic_rules_method_returns_error(self): - ''' + def test_that_when_listing_topic_rules_fails_the_list_topic_rules_method_returns_error( + self, + ): + """ tests False policy error. - ''' - self.conn.list_topic_rules.side_effect = ClientError(error_content, 'list_topic_rules') + """ + self.conn.list_topic_rules.side_effect = ClientError( + error_content, "list_topic_rules" + ) result = boto_iot.list_topic_rules(**conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('list_topic_rules')) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("list_topic_rules"), + ) diff --git a/tests/unit/modules/test_boto_lambda.py b/tests/unit/modules/test_boto_lambda.py index bb37484ade9..3c1d98809b7 100644 --- a/tests/unit/modules/test_boto_lambda.py +++ b/tests/unit/modules/test_boto_lambda.py @@ -2,48 +2,49 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import random -import string + import logging import os +import random +import string from tempfile import NamedTemporaryFile +# Import Salt libs +import salt.config +import salt.loader +import salt.modules.boto_lambda as boto_lambda +import salt.utils.stringutils +from salt.exceptions import SaltInvocationError + +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + # linux_distribution deprecated in py3.7 try: from platform import linux_distribution except ImportError: from distro import linux_distribution -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch -) -# Import Salt libs -import salt.config -import salt.loader -import salt.modules.boto_lambda as boto_lambda -from salt.exceptions import SaltInvocationError -from salt.utils.versions import LooseVersion -import salt.utils.stringutils - -# Import 3rd-party libs -from salt.ext import six -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin # pylint: disable=import-error,no-name-in-module try: import boto3 from botocore.exceptions import ClientError from botocore import __version__ as found_botocore_version + HAS_BOTO = True except ImportError: HAS_BOTO = False ON_SUSE = False -if 'SuSE' in linux_distribution(full_distribution_name=False): +if "SuSE" in linux_distribution(full_distribution_name=False): ON_SUSE = True # pylint: enable=import-error,no-name-in-module @@ -51,55 +52,62 @@ if 'SuSE' in linux_distribution(full_distribution_name=False): # the boto_lambda module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' -required_botocore_version = '1.5.2' +required_boto3_version = "1.2.1" +required_botocore_version = "1.5.2" -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, - 'keyid': secret_key, 'profile': {}} -error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' -error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } -function_ret = dict(FunctionName='testfunction', - Runtime='python2.7', - Role=None, - Handler='handler', - Description='abcdefg', - Timeout=5, - MemorySize=128, - CodeSha256='abcdef', - CodeSize=199, - FunctionArn='arn:lambda:us-east-1:1234:Something', - LastModified='yes', - VpcConfig=None, - Environment=None) -alias_ret = dict(AliasArn='arn:lambda:us-east-1:1234:Something', - Name='testalias', - FunctionVersion='3', - Description='Alias description') -event_source_mapping_ret = dict(UUID='1234-1-123', - BatchSize=123, - EventSourceArn='arn:lambda:us-east-1:1234:Something', - FunctionArn='arn:lambda:us-east-1:1234:Something', - LastModified='yes', - LastProcessingResult='SUCCESS', - State='Enabled', - StateTransitionReason='Random') +error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" +) +error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} +function_ret = dict( + FunctionName="testfunction", + Runtime="python2.7", + Role=None, + Handler="handler", + Description="abcdefg", + Timeout=5, + MemorySize=128, + CodeSha256="abcdef", + CodeSize=199, + FunctionArn="arn:lambda:us-east-1:1234:Something", + LastModified="yes", + VpcConfig=None, + Environment=None, +) +alias_ret = dict( + AliasArn="arn:lambda:us-east-1:1234:Something", + Name="testalias", + FunctionVersion="3", + Description="Alias description", +) +event_source_mapping_ret = dict( + UUID="1234-1-123", + BatchSize=123, + EventSourceArn="arn:lambda:us-east-1:1234:Something", + FunctionArn="arn:lambda:us-east-1:1234:Something", + LastModified="yes", + LastProcessingResult="SUCCESS", + State="Enabled", + StateTransitionReason="Random", +) log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -110,21 +118,25 @@ def _has_required_boto(): return True -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, - ('The boto3 module must be greater than or equal to version {0}, ' - 'and botocore must be greater than or equal to {1}'.format( - required_boto3_version, required_botocore_version))) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + ( + "The boto3 module must be greater than or equal to version {0}, " + "and botocore must be greater than or equal to {1}".format( + required_boto3_version, required_botocore_version + ) + ), +) class BotoLambdaTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context={}) - return {boto_lambda: {'__utils__': utils}} + opts, whitelist=["boto3", "args", "systemd", "path", "platform"], context={} + ) + return {boto_lambda: {"__utils__": utils}} def setUp(self): super(BotoLambdaTestCaseBase, self).setUp() @@ -134,26 +146,27 @@ class BotoLambdaTestCaseBase(TestCase, LoaderModuleMockMixin): # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice( - string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() session_instance.client.return_value = self.conn - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") class TempZipFile(object): - def __enter__(self): with NamedTemporaryFile( - suffix='.zip', prefix='salt_test_', delete=False) as tmp: - to_write = '###\n' + suffix=".zip", prefix="salt_test_", delete=False + ) as tmp: + to_write = "###\n" if six.PY3: to_write = salt.utils.stringutils.to_bytes(to_write) tmp.write(to_write) @@ -169,651 +182,881 @@ class BotoLambdaTestCaseMixin(object): class BotoLambdaFunctionTestCase(BotoLambdaTestCaseBase, BotoLambdaTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_lambda module - ''' + """ - def test_that_when_checking_if_a_function_exists_and_a_function_exists_the_function_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_function_exists_and_a_function_exists_the_function_exists_method_returns_true( + self, + ): + """ Tests checking lambda function existence when the lambda function already exists - ''' - self.conn.list_functions.return_value = {'Functions': [function_ret]} + """ + self.conn.list_functions.return_value = {"Functions": [function_ret]} func_exists_result = boto_lambda.function_exists( - FunctionName=function_ret['FunctionName'], **conn_parameters) + FunctionName=function_ret["FunctionName"], **conn_parameters + ) - self.assertTrue(func_exists_result['exists']) + self.assertTrue(func_exists_result["exists"]) - def test_that_when_checking_if_a_function_exists_and_a_function_does_not_exist_the_function_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_function_exists_and_a_function_does_not_exist_the_function_exists_method_returns_false( + self, + ): + """ Tests checking lambda function existence when the lambda function does not exist - ''' - self.conn.list_functions.return_value = {'Functions': [function_ret]} + """ + self.conn.list_functions.return_value = {"Functions": [function_ret]} func_exists_result = boto_lambda.function_exists( - FunctionName='myfunc', **conn_parameters) + FunctionName="myfunc", **conn_parameters + ) - self.assertFalse(func_exists_result['exists']) + self.assertFalse(func_exists_result["exists"]) - def test_that_when_checking_if_a_function_exists_and_boto3_returns_an_error_the_function_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_a_function_exists_and_boto3_returns_an_error_the_function_exists_method_returns_error( + self, + ): + """ Tests checking lambda function existence when boto returns an error - ''' + """ self.conn.list_functions.side_effect = ClientError( - error_content, 'list_functions') + error_content, "list_functions" + ) func_exists_result = boto_lambda.function_exists( - FunctionName='myfunc', **conn_parameters) + FunctionName="myfunc", **conn_parameters + ) - self.assertEqual(func_exists_result.get('error', {}).get( - 'message'), error_message.format('list_functions')) + self.assertEqual( + func_exists_result.get("error", {}).get("message"), + error_message.format("list_functions"), + ) - def test_that_when_creating_a_function_from_zipfile_succeeds_the_create_function_method_returns_true(self): - ''' + def test_that_when_creating_a_function_from_zipfile_succeeds_the_create_function_method_returns_true( + self, + ): + """ tests True function created. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): with TempZipFile() as zipfile: self.conn.create_function.return_value = function_ret lambda_creation_result = boto_lambda.create_function( - FunctionName='testfunction', - Runtime='python2.7', - Role='myrole', - Handler='file.method', + FunctionName="testfunction", + Runtime="python2.7", + Role="myrole", + Handler="file.method", ZipFile=zipfile, - **conn_parameters) + **conn_parameters + ) - self.assertTrue(lambda_creation_result['created']) + self.assertTrue(lambda_creation_result["created"]) - def test_that_when_creating_a_function_from_s3_succeeds_the_create_function_method_returns_true(self): - ''' + def test_that_when_creating_a_function_from_s3_succeeds_the_create_function_method_returns_true( + self, + ): + """ tests True function created. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): self.conn.create_function.return_value = function_ret lambda_creation_result = boto_lambda.create_function( - FunctionName='testfunction', - Runtime='python2.7', - Role='myrole', - Handler='file.method', - S3Bucket='bucket', - S3Key='key', - **conn_parameters) + FunctionName="testfunction", + Runtime="python2.7", + Role="myrole", + Handler="file.method", + S3Bucket="bucket", + S3Key="key", + **conn_parameters + ) - self.assertTrue(lambda_creation_result['created']) + self.assertTrue(lambda_creation_result["created"]) - def test_that_when_creating_a_function_without_code_raises_a_salt_invocation_error(self): - ''' + def test_that_when_creating_a_function_without_code_raises_a_salt_invocation_error( + self, + ): + """ tests Creating a function without code - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - with self.assertRaisesRegex(SaltInvocationError, - 'Either ZipFile must be specified, or S3Bucket and S3Key must be provided.'): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + with self.assertRaisesRegex( + SaltInvocationError, + "Either ZipFile must be specified, or S3Bucket and S3Key must be provided.", + ): lambda_creation_result = boto_lambda.create_function( - FunctionName='testfunction', - Runtime='python2.7', - Role='myrole', - Handler='file.method', - **conn_parameters) + FunctionName="testfunction", + Runtime="python2.7", + Role="myrole", + Handler="file.method", + **conn_parameters + ) - def test_that_when_creating_a_function_with_zipfile_and_s3_raises_a_salt_invocation_error(self): - ''' + def test_that_when_creating_a_function_with_zipfile_and_s3_raises_a_salt_invocation_error( + self, + ): + """ tests Creating a function without code - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - with self.assertRaisesRegex(SaltInvocationError, - 'Either ZipFile must be specified, or S3Bucket and S3Key must be provided.'): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + with self.assertRaisesRegex( + SaltInvocationError, + "Either ZipFile must be specified, or S3Bucket and S3Key must be provided.", + ): with TempZipFile() as zipfile: lambda_creation_result = boto_lambda.create_function( - FunctionName='testfunction', - Runtime='python2.7', - Role='myrole', - Handler='file.method', + FunctionName="testfunction", + Runtime="python2.7", + Role="myrole", + Handler="file.method", ZipFile=zipfile, - S3Bucket='bucket', - S3Key='key', - **conn_parameters) + S3Bucket="bucket", + S3Key="key", + **conn_parameters + ) - def test_that_when_creating_a_function_fails_the_create_function_method_returns_error(self): - ''' + def test_that_when_creating_a_function_fails_the_create_function_method_returns_error( + self, + ): + """ tests False function not created. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): self.conn.create_function.side_effect = ClientError( - error_content, 'create_function') + error_content, "create_function" + ) with TempZipFile() as zipfile: lambda_creation_result = boto_lambda.create_function( - FunctionName='testfunction', - Runtime='python2.7', - Role='myrole', - Handler='file.method', + FunctionName="testfunction", + Runtime="python2.7", + Role="myrole", + Handler="file.method", ZipFile=zipfile, - **conn_parameters) - self.assertEqual(lambda_creation_result.get('error', {}).get( - 'message'), error_message.format('create_function')) + **conn_parameters + ) + self.assertEqual( + lambda_creation_result.get("error", {}).get("message"), + error_message.format("create_function"), + ) - def test_that_when_deleting_a_function_succeeds_the_delete_function_method_returns_true(self): - ''' + def test_that_when_deleting_a_function_succeeds_the_delete_function_method_returns_true( + self, + ): + """ tests True function deleted. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = boto_lambda.delete_function(FunctionName='testfunction', - Qualifier=1, - **conn_parameters) + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + result = boto_lambda.delete_function( + FunctionName="testfunction", Qualifier=1, **conn_parameters + ) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_a_function_fails_the_delete_function_method_returns_false(self): - ''' + def test_that_when_deleting_a_function_fails_the_delete_function_method_returns_false( + self, + ): + """ tests False function not deleted. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): self.conn.delete_function.side_effect = ClientError( - error_content, 'delete_function') - result = boto_lambda.delete_function(FunctionName='testfunction', - **conn_parameters) - self.assertFalse(result['deleted']) + error_content, "delete_function" + ) + result = boto_lambda.delete_function( + FunctionName="testfunction", **conn_parameters + ) + self.assertFalse(result["deleted"]) - def test_that_when_describing_function_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_function_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters if function exists - ''' - self.conn.list_functions.return_value = {'Functions': [function_ret]} + """ + self.conn.list_functions.return_value = {"Functions": [function_ret]} - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): result = boto_lambda.describe_function( - FunctionName=function_ret['FunctionName'], **conn_parameters) + FunctionName=function_ret["FunctionName"], **conn_parameters + ) - self.assertEqual(result, {'function': function_ret}) + self.assertEqual(result, {"function": function_ret}) - def test_that_when_describing_function_it_returns_the_dict_of_properties_returns_false(self): - ''' + def test_that_when_describing_function_it_returns_the_dict_of_properties_returns_false( + self, + ): + """ Tests describing parameters if function does not exist - ''' - self.conn.list_functions.return_value = {'Functions': []} - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + self.conn.list_functions.return_value = {"Functions": []} + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): result = boto_lambda.describe_function( - FunctionName='testfunction', **conn_parameters) + FunctionName="testfunction", **conn_parameters + ) - self.assertFalse(result['function']) + self.assertFalse(result["function"]) def test_that_when_describing_lambda_on_client_error_it_returns_error(self): - ''' + """ Tests describing parameters failure - ''' + """ self.conn.list_functions.side_effect = ClientError( - error_content, 'list_functions') + error_content, "list_functions" + ) result = boto_lambda.describe_function( - FunctionName='testfunction', **conn_parameters) - self.assertTrue('error' in result) + FunctionName="testfunction", **conn_parameters + ) + self.assertTrue("error" in result) - def test_that_when_updating_a_function_succeeds_the_update_function_method_returns_true(self): - ''' + def test_that_when_updating_a_function_succeeds_the_update_function_method_returns_true( + self, + ): + """ tests True function updated. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): self.conn.update_function_config.return_value = function_ret result = boto_lambda.update_function_config( - FunctionName=function_ret['FunctionName'], Role='myrole', **conn_parameters) + FunctionName=function_ret["FunctionName"], + Role="myrole", + **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_updating_a_function_fails_the_update_function_method_returns_error(self): - ''' + def test_that_when_updating_a_function_fails_the_update_function_method_returns_error( + self, + ): + """ tests False function not updated. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): self.conn.update_function_configuration.side_effect = ClientError( - error_content, 'update_function') - result = boto_lambda.update_function_config(FunctionName='testfunction', - Role='myrole', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('update_function')) + error_content, "update_function" + ) + result = boto_lambda.update_function_config( + FunctionName="testfunction", Role="myrole", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("update_function"), + ) - def test_that_when_updating_function_code_from_zipfile_succeeds_the_update_function_method_returns_true(self): - ''' + def test_that_when_updating_function_code_from_zipfile_succeeds_the_update_function_method_returns_true( + self, + ): + """ tests True function updated. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): with TempZipFile() as zipfile: self.conn.update_function_code.return_value = function_ret result = boto_lambda.update_function_code( - FunctionName=function_ret['FunctionName'], - ZipFile=zipfile, **conn_parameters) + FunctionName=function_ret["FunctionName"], + ZipFile=zipfile, + **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_updating_function_code_from_s3_succeeds_the_update_function_method_returns_true(self): - ''' + def test_that_when_updating_function_code_from_s3_succeeds_the_update_function_method_returns_true( + self, + ): + """ tests True function updated. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): self.conn.update_function_code.return_value = function_ret result = boto_lambda.update_function_code( - FunctionName='testfunction', - S3Bucket='bucket', - S3Key='key', - **conn_parameters) + FunctionName="testfunction", + S3Bucket="bucket", + S3Key="key", + **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_updating_function_code_without_code_raises_a_salt_invocation_error(self): - ''' + def test_that_when_updating_function_code_without_code_raises_a_salt_invocation_error( + self, + ): + """ tests Creating a function without code - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): with self.assertRaisesRegex( SaltInvocationError, - ('Either ZipFile must be specified, or S3Bucket ' - 'and S3Key must be provided.')): + ( + "Either ZipFile must be specified, or S3Bucket " + "and S3Key must be provided." + ), + ): result = boto_lambda.update_function_code( - FunctionName='testfunction', - **conn_parameters) + FunctionName="testfunction", **conn_parameters + ) - def test_that_when_updating_function_code_fails_the_update_function_method_returns_error(self): - ''' + def test_that_when_updating_function_code_fails_the_update_function_method_returns_error( + self, + ): + """ tests False function not updated. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): self.conn.update_function_code.side_effect = ClientError( - error_content, 'update_function_code') + error_content, "update_function_code" + ) result = boto_lambda.update_function_code( - FunctionName='testfunction', - S3Bucket='bucket', - S3Key='key', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('update_function_code')) + FunctionName="testfunction", + S3Bucket="bucket", + S3Key="key", + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("update_function_code"), + ) - def test_that_when_listing_function_versions_succeeds_the_list_function_versions_method_returns_true(self): - ''' + def test_that_when_listing_function_versions_succeeds_the_list_function_versions_method_returns_true( + self, + ): + """ tests True function versions listed. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): self.conn.list_versions_by_function.return_value = { - 'Versions': [function_ret]} + "Versions": [function_ret] + } result = boto_lambda.list_function_versions( - FunctionName='testfunction', - **conn_parameters) + FunctionName="testfunction", **conn_parameters + ) - self.assertTrue(result['Versions']) + self.assertTrue(result["Versions"]) - def test_that_when_listing_function_versions_fails_the_list_function_versions_method_returns_false(self): - ''' + def test_that_when_listing_function_versions_fails_the_list_function_versions_method_returns_false( + self, + ): + """ tests False no function versions listed. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - self.conn.list_versions_by_function.return_value = {'Versions': []} + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): + self.conn.list_versions_by_function.return_value = {"Versions": []} result = boto_lambda.list_function_versions( - FunctionName='testfunction', - **conn_parameters) - self.assertFalse(result['Versions']) + FunctionName="testfunction", **conn_parameters + ) + self.assertFalse(result["Versions"]) - def test_that_when_listing_function_versions_fails_the_list_function_versions_method_returns_error(self): - ''' + def test_that_when_listing_function_versions_fails_the_list_function_versions_method_returns_error( + self, + ): + """ tests False function versions error. - ''' - with patch.dict(boto_lambda.__salt__, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + """ + with patch.dict( + boto_lambda.__salt__, + {"boto_iam.get_account_id": MagicMock(return_value="1234")}, + ): self.conn.list_versions_by_function.side_effect = ClientError( - error_content, 'list_versions_by_function') + error_content, "list_versions_by_function" + ) result = boto_lambda.list_function_versions( - FunctionName='testfunction', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('list_versions_by_function')) + FunctionName="testfunction", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("list_versions_by_function"), + ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) class BotoLambdaAliasTestCase(BotoLambdaTestCaseBase, BotoLambdaTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_lambda module aliases - ''' + """ - def test_that_when_creating_an_alias_succeeds_the_create_alias_method_returns_true(self): - ''' + def test_that_when_creating_an_alias_succeeds_the_create_alias_method_returns_true( + self, + ): + """ tests True alias created. - ''' + """ self.conn.create_alias.return_value = alias_ret - result = boto_lambda.create_alias(FunctionName='testfunction', - Name=alias_ret['Name'], - FunctionVersion=alias_ret[ - 'FunctionVersion'], - **conn_parameters) + result = boto_lambda.create_alias( + FunctionName="testfunction", + Name=alias_ret["Name"], + FunctionVersion=alias_ret["FunctionVersion"], + **conn_parameters + ) - self.assertTrue(result['created']) + self.assertTrue(result["created"]) - def test_that_when_creating_an_alias_fails_the_create_alias_method_returns_error(self): - ''' + def test_that_when_creating_an_alias_fails_the_create_alias_method_returns_error( + self, + ): + """ tests False alias not created. - ''' - self.conn.create_alias.side_effect = ClientError( - error_content, 'create_alias') - result = boto_lambda.create_alias(FunctionName='testfunction', - Name=alias_ret['Name'], - FunctionVersion=alias_ret[ - 'FunctionVersion'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get( - 'message'), error_message.format('create_alias')) + """ + self.conn.create_alias.side_effect = ClientError(error_content, "create_alias") + result = boto_lambda.create_alias( + FunctionName="testfunction", + Name=alias_ret["Name"], + FunctionVersion=alias_ret["FunctionVersion"], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("create_alias") + ) - def test_that_when_deleting_an_alias_succeeds_the_delete_alias_method_returns_true(self): - ''' + def test_that_when_deleting_an_alias_succeeds_the_delete_alias_method_returns_true( + self, + ): + """ tests True alias deleted. - ''' - result = boto_lambda.delete_alias(FunctionName='testfunction', - Name=alias_ret['Name'], - **conn_parameters) + """ + result = boto_lambda.delete_alias( + FunctionName="testfunction", Name=alias_ret["Name"], **conn_parameters + ) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_an_alias_fails_the_delete_alias_method_returns_false(self): - ''' + def test_that_when_deleting_an_alias_fails_the_delete_alias_method_returns_false( + self, + ): + """ tests False alias not deleted. - ''' - self.conn.delete_alias.side_effect = ClientError( - error_content, 'delete_alias') - result = boto_lambda.delete_alias(FunctionName='testfunction', - Name=alias_ret['Name'], - **conn_parameters) - self.assertFalse(result['deleted']) + """ + self.conn.delete_alias.side_effect = ClientError(error_content, "delete_alias") + result = boto_lambda.delete_alias( + FunctionName="testfunction", Name=alias_ret["Name"], **conn_parameters + ) + self.assertFalse(result["deleted"]) - def test_that_when_checking_if_an_alias_exists_and_the_alias_exists_the_alias_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_an_alias_exists_and_the_alias_exists_the_alias_exists_method_returns_true( + self, + ): + """ Tests checking lambda alias existence when the lambda alias already exists - ''' - self.conn.list_aliases.return_value = {'Aliases': [alias_ret]} - result = boto_lambda.alias_exists(FunctionName='testfunction', - Name=alias_ret['Name'], - **conn_parameters) - self.assertTrue(result['exists']) + """ + self.conn.list_aliases.return_value = {"Aliases": [alias_ret]} + result = boto_lambda.alias_exists( + FunctionName="testfunction", Name=alias_ret["Name"], **conn_parameters + ) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_an_alias_exists_and_the_alias_does_not_exist_the_alias_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_an_alias_exists_and_the_alias_does_not_exist_the_alias_exists_method_returns_false( + self, + ): + """ Tests checking lambda alias existence when the lambda alias does not exist - ''' - self.conn.list_aliases.return_value = {'Aliases': [alias_ret]} - result = boto_lambda.alias_exists(FunctionName='testfunction', - Name='otheralias', - **conn_parameters) + """ + self.conn.list_aliases.return_value = {"Aliases": [alias_ret]} + result = boto_lambda.alias_exists( + FunctionName="testfunction", Name="otheralias", **conn_parameters + ) - self.assertFalse(result['exists']) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_an_alias_exists_and_boto3_returns_an_error_the_alias_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_an_alias_exists_and_boto3_returns_an_error_the_alias_exists_method_returns_error( + self, + ): + """ Tests checking lambda alias existence when boto returns an error - ''' - self.conn.list_aliases.side_effect = ClientError( - error_content, 'list_aliases') - result = boto_lambda.alias_exists(FunctionName='testfunction', - Name=alias_ret['Name'], - **conn_parameters) + """ + self.conn.list_aliases.side_effect = ClientError(error_content, "list_aliases") + result = boto_lambda.alias_exists( + FunctionName="testfunction", Name=alias_ret["Name"], **conn_parameters + ) - self.assertEqual(result.get('error', {}).get( - 'message'), error_message.format('list_aliases')) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("list_aliases") + ) - def test_that_when_describing_alias_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_alias_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters if alias exists - ''' - self.conn.list_aliases.return_value = {'Aliases': [alias_ret]} + """ + self.conn.list_aliases.return_value = {"Aliases": [alias_ret]} - result = boto_lambda.describe_alias(FunctionName='testfunction', - Name=alias_ret['Name'], - **conn_parameters) + result = boto_lambda.describe_alias( + FunctionName="testfunction", Name=alias_ret["Name"], **conn_parameters + ) - self.assertEqual(result, {'alias': alias_ret}) + self.assertEqual(result, {"alias": alias_ret}) - def test_that_when_describing_alias_it_returns_the_dict_of_properties_returns_false(self): - ''' + def test_that_when_describing_alias_it_returns_the_dict_of_properties_returns_false( + self, + ): + """ Tests describing parameters if alias does not exist - ''' - self.conn.list_aliases.return_value = {'Aliases': [alias_ret]} - result = boto_lambda.describe_alias(FunctionName='testfunction', - Name='othername', - **conn_parameters) + """ + self.conn.list_aliases.return_value = {"Aliases": [alias_ret]} + result = boto_lambda.describe_alias( + FunctionName="testfunction", Name="othername", **conn_parameters + ) - self.assertFalse(result['alias']) + self.assertFalse(result["alias"]) def test_that_when_describing_lambda_on_client_error_it_returns_error(self): - ''' + """ Tests describing parameters failure - ''' - self.conn.list_aliases.side_effect = ClientError( - error_content, 'list_aliases') - result = boto_lambda.describe_alias(FunctionName='testfunction', - Name=alias_ret['Name'], - **conn_parameters) - self.assertTrue('error' in result) + """ + self.conn.list_aliases.side_effect = ClientError(error_content, "list_aliases") + result = boto_lambda.describe_alias( + FunctionName="testfunction", Name=alias_ret["Name"], **conn_parameters + ) + self.assertTrue("error" in result) - def test_that_when_updating_an_alias_succeeds_the_update_alias_method_returns_true(self): - ''' + def test_that_when_updating_an_alias_succeeds_the_update_alias_method_returns_true( + self, + ): + """ tests True alias updated. - ''' + """ self.conn.update_alias.return_value = alias_ret - result = boto_lambda.update_alias(FunctionName='testfunctoin', - Name=alias_ret['Name'], - Description=alias_ret['Description'], - **conn_parameters) + result = boto_lambda.update_alias( + FunctionName="testfunctoin", + Name=alias_ret["Name"], + Description=alias_ret["Description"], + **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_updating_an_alias_fails_the_update_alias_method_returns_error(self): - ''' + def test_that_when_updating_an_alias_fails_the_update_alias_method_returns_error( + self, + ): + """ tests False alias not updated. - ''' - self.conn.update_alias.side_effect = ClientError( - error_content, 'update_alias') - result = boto_lambda.update_alias(FunctionName='testfunction', - Name=alias_ret['Name'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get( - 'message'), error_message.format('update_alias')) + """ + self.conn.update_alias.side_effect = ClientError(error_content, "update_alias") + result = boto_lambda.update_alias( + FunctionName="testfunction", Name=alias_ret["Name"], **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("update_alias") + ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -class BotoLambdaEventSourceMappingTestCase(BotoLambdaTestCaseBase, BotoLambdaTestCaseMixin): - ''' +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) +class BotoLambdaEventSourceMappingTestCase( + BotoLambdaTestCaseBase, BotoLambdaTestCaseMixin +): + """ TestCase for salt.modules.boto_lambda module mappings - ''' + """ - def test_that_when_creating_a_mapping_succeeds_the_create_event_source_mapping_method_returns_true(self): - ''' + def test_that_when_creating_a_mapping_succeeds_the_create_event_source_mapping_method_returns_true( + self, + ): + """ tests True mapping created. - ''' + """ self.conn.create_event_source_mapping.return_value = event_source_mapping_ret result = boto_lambda.create_event_source_mapping( - EventSourceArn=event_source_mapping_ret['EventSourceArn'], - FunctionName=event_source_mapping_ret['FunctionArn'], - StartingPosition='LATEST', - **conn_parameters) + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName=event_source_mapping_ret["FunctionArn"], + StartingPosition="LATEST", + **conn_parameters + ) - self.assertTrue(result['created']) + self.assertTrue(result["created"]) - def test_that_when_creating_an_event_source_mapping_fails_the_create_event_source_mapping_method_returns_error(self): - ''' + def test_that_when_creating_an_event_source_mapping_fails_the_create_event_source_mapping_method_returns_error( + self, + ): + """ tests False mapping not created. - ''' + """ self.conn.create_event_source_mapping.side_effect = ClientError( - error_content, 'create_event_source_mapping') + error_content, "create_event_source_mapping" + ) result = boto_lambda.create_event_source_mapping( - EventSourceArn=event_source_mapping_ret['EventSourceArn'], - FunctionName=event_source_mapping_ret['FunctionArn'], - StartingPosition='LATEST', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('create_event_source_mapping')) + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName=event_source_mapping_ret["FunctionArn"], + StartingPosition="LATEST", + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("create_event_source_mapping"), + ) - def test_that_when_listing_mapping_ids_succeeds_the_get_event_source_mapping_ids_method_returns_true(self): - ''' + def test_that_when_listing_mapping_ids_succeeds_the_get_event_source_mapping_ids_method_returns_true( + self, + ): + """ tests True mapping ids listed. - ''' + """ self.conn.list_event_source_mappings.return_value = { - 'EventSourceMappings': [event_source_mapping_ret]} + "EventSourceMappings": [event_source_mapping_ret] + } result = boto_lambda.get_event_source_mapping_ids( - EventSourceArn=event_source_mapping_ret['EventSourceArn'], - FunctionName=event_source_mapping_ret['FunctionArn'], - **conn_parameters) + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName=event_source_mapping_ret["FunctionArn"], + **conn_parameters + ) self.assertTrue(result) - def test_that_when_listing_event_source_mapping_ids_fails_the_get_event_source_mapping_ids_versions_method_returns_false(self): - ''' + def test_that_when_listing_event_source_mapping_ids_fails_the_get_event_source_mapping_ids_versions_method_returns_false( + self, + ): + """ tests False no mapping ids listed. - ''' - self.conn.list_event_source_mappings.return_value = { - 'EventSourceMappings': []} + """ + self.conn.list_event_source_mappings.return_value = {"EventSourceMappings": []} result = boto_lambda.get_event_source_mapping_ids( - EventSourceArn=event_source_mapping_ret['EventSourceArn'], - FunctionName=event_source_mapping_ret['FunctionArn'], - **conn_parameters) + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName=event_source_mapping_ret["FunctionArn"], + **conn_parameters + ) self.assertFalse(result) - def test_that_when_listing_event_source_mapping_ids_fails_the_get_event_source_mapping_ids_method_returns_error(self): - ''' + def test_that_when_listing_event_source_mapping_ids_fails_the_get_event_source_mapping_ids_method_returns_error( + self, + ): + """ tests False mapping ids error. - ''' + """ self.conn.list_event_source_mappings.side_effect = ClientError( - error_content, 'list_event_source_mappings') + error_content, "list_event_source_mappings" + ) result = boto_lambda.get_event_source_mapping_ids( - EventSourceArn=event_source_mapping_ret['EventSourceArn'], - FunctionName=event_source_mapping_ret['FunctionArn'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('list_event_source_mappings')) + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName=event_source_mapping_ret["FunctionArn"], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("list_event_source_mappings"), + ) - def test_that_when_deleting_an_event_source_mapping_by_UUID_succeeds_the_delete_event_source_mapping_method_returns_true(self): - ''' + def test_that_when_deleting_an_event_source_mapping_by_UUID_succeeds_the_delete_event_source_mapping_method_returns_true( + self, + ): + """ tests True mapping deleted. - ''' + """ result = boto_lambda.delete_event_source_mapping( - UUID=event_source_mapping_ret['UUID'], - **conn_parameters) - self.assertTrue(result['deleted']) + UUID=event_source_mapping_ret["UUID"], **conn_parameters + ) + self.assertTrue(result["deleted"]) - @skipIf(True, 'This appears to leak memory and crash the unit test suite') - def test_that_when_deleting_an_event_source_mapping_by_name_succeeds_the_delete_event_source_mapping_method_returns_true(self): - ''' + @skipIf(True, "This appears to leak memory and crash the unit test suite") + def test_that_when_deleting_an_event_source_mapping_by_name_succeeds_the_delete_event_source_mapping_method_returns_true( + self, + ): + """ tests True mapping deleted. - ''' + """ self.conn.list_event_source_mappings.return_value = { - 'EventSourceMappings': [event_source_mapping_ret]} + "EventSourceMappings": [event_source_mapping_ret] + } result = boto_lambda.delete_event_source_mapping( - EventSourceArn=event_source_mapping_ret['EventSourceArn'], - FunctionName=event_source_mapping_ret['FunctionArn'], - **conn_parameters) - self.assertTrue(result['deleted']) + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName=event_source_mapping_ret["FunctionArn"], + **conn_parameters + ) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_an_event_source_mapping_without_identifier_the_delete_event_source_mapping_method_raises_saltinvocationexception(self): - ''' + def test_that_when_deleting_an_event_source_mapping_without_identifier_the_delete_event_source_mapping_method_raises_saltinvocationexception( + self, + ): + """ tests Deleting a mapping without identifier - ''' + """ with self.assertRaisesRegex( SaltInvocationError, - ('Either UUID must be specified, or EventSourceArn ' - 'and FunctionName must be provided.')): + ( + "Either UUID must be specified, or EventSourceArn " + "and FunctionName must be provided." + ), + ): result = boto_lambda.delete_event_source_mapping(**conn_parameters) - def test_that_when_deleting_an_event_source_mapping_fails_the_delete_event_source_mapping_method_returns_false(self): - ''' + def test_that_when_deleting_an_event_source_mapping_fails_the_delete_event_source_mapping_method_returns_false( + self, + ): + """ tests False mapping not deleted. - ''' + """ self.conn.delete_event_source_mapping.side_effect = ClientError( - error_content, 'delete_event_source_mapping') - result = boto_lambda.delete_event_source_mapping(UUID=event_source_mapping_ret['UUID'], - **conn_parameters) - self.assertFalse(result['deleted']) + error_content, "delete_event_source_mapping" + ) + result = boto_lambda.delete_event_source_mapping( + UUID=event_source_mapping_ret["UUID"], **conn_parameters + ) + self.assertFalse(result["deleted"]) - def test_that_when_checking_if_an_event_source_mapping_exists_and_the_event_source_mapping_exists_the_event_source_mapping_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_an_event_source_mapping_exists_and_the_event_source_mapping_exists_the_event_source_mapping_exists_method_returns_true( + self, + ): + """ Tests checking lambda event_source_mapping existence when the lambda event_source_mapping already exists - ''' + """ self.conn.get_event_source_mapping.return_value = event_source_mapping_ret result = boto_lambda.event_source_mapping_exists( - UUID=event_source_mapping_ret['UUID'], - **conn_parameters) - self.assertTrue(result['exists']) + UUID=event_source_mapping_ret["UUID"], **conn_parameters + ) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_an_event_source_mapping_exists_and_the_event_source_mapping_does_not_exist_the_event_source_mapping_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_an_event_source_mapping_exists_and_the_event_source_mapping_does_not_exist_the_event_source_mapping_exists_method_returns_false( + self, + ): + """ Tests checking lambda event_source_mapping existence when the lambda event_source_mapping does not exist - ''' + """ self.conn.get_event_source_mapping.return_value = None result = boto_lambda.event_source_mapping_exists( - UUID='other_UUID', - **conn_parameters) - self.assertFalse(result['exists']) + UUID="other_UUID", **conn_parameters + ) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_an_event_source_mapping_exists_and_boto3_returns_an_error_the_event_source_mapping_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_an_event_source_mapping_exists_and_boto3_returns_an_error_the_event_source_mapping_exists_method_returns_error( + self, + ): + """ Tests checking lambda event_source_mapping existence when boto returns an error - ''' + """ self.conn.get_event_source_mapping.side_effect = ClientError( - error_content, 'list_event_source_mappings') + error_content, "list_event_source_mappings" + ) result = boto_lambda.event_source_mapping_exists( - UUID=event_source_mapping_ret['UUID'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('list_event_source_mappings')) + UUID=event_source_mapping_ret["UUID"], **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("list_event_source_mappings"), + ) - def test_that_when_describing_event_source_mapping_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_event_source_mapping_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters if event_source_mapping exists - ''' + """ self.conn.get_event_source_mapping.return_value = event_source_mapping_ret result = boto_lambda.describe_event_source_mapping( - UUID=event_source_mapping_ret['UUID'], - **conn_parameters) - self.assertEqual( - result, {'event_source_mapping': event_source_mapping_ret}) + UUID=event_source_mapping_ret["UUID"], **conn_parameters + ) + self.assertEqual(result, {"event_source_mapping": event_source_mapping_ret}) - def test_that_when_describing_event_source_mapping_it_returns_the_dict_of_properties_returns_false(self): - ''' + def test_that_when_describing_event_source_mapping_it_returns_the_dict_of_properties_returns_false( + self, + ): + """ Tests describing parameters if event_source_mapping does not exist - ''' + """ self.conn.get_event_source_mapping.return_value = None result = boto_lambda.describe_event_source_mapping( - UUID=event_source_mapping_ret['UUID'], - **conn_parameters) - self.assertFalse(result['event_source_mapping']) + UUID=event_source_mapping_ret["UUID"], **conn_parameters + ) + self.assertFalse(result["event_source_mapping"]) - def test_that_when_describing_event_source_mapping_on_client_error_it_returns_error(self): - ''' + def test_that_when_describing_event_source_mapping_on_client_error_it_returns_error( + self, + ): + """ Tests describing parameters failure - ''' + """ self.conn.get_event_source_mapping.side_effect = ClientError( - error_content, 'get_event_source_mapping') + error_content, "get_event_source_mapping" + ) result = boto_lambda.describe_event_source_mapping( - UUID=event_source_mapping_ret['UUID'], - **conn_parameters) - self.assertTrue('error' in result) + UUID=event_source_mapping_ret["UUID"], **conn_parameters + ) + self.assertTrue("error" in result) - def test_that_when_updating_an_event_source_mapping_succeeds_the_update_event_source_mapping_method_returns_true(self): - ''' + def test_that_when_updating_an_event_source_mapping_succeeds_the_update_event_source_mapping_method_returns_true( + self, + ): + """ tests True event_source_mapping updated. - ''' + """ self.conn.update_event_source_mapping.return_value = event_source_mapping_ret result = boto_lambda.update_event_source_mapping( - UUID=event_source_mapping_ret['UUID'], - FunctionName=event_source_mapping_ret['FunctionArn'], - **conn_parameters) + UUID=event_source_mapping_ret["UUID"], + FunctionName=event_source_mapping_ret["FunctionArn"], + **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_updating_an_event_source_mapping_fails_the_update_event_source_mapping_method_returns_error(self): - ''' + def test_that_when_updating_an_event_source_mapping_fails_the_update_event_source_mapping_method_returns_error( + self, + ): + """ tests False event_source_mapping not updated. - ''' + """ self.conn.update_event_source_mapping.side_effect = ClientError( - error_content, 'update_event_source_mapping') + error_content, "update_event_source_mapping" + ) result = boto_lambda.update_event_source_mapping( - UUID=event_source_mapping_ret['UUID'], - FunctionName=event_source_mapping_ret['FunctionArn'], - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('update_event_source_mapping')) + UUID=event_source_mapping_ret["UUID"], + FunctionName=event_source_mapping_ret["FunctionArn"], + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("update_event_source_mapping"), + ) diff --git a/tests/unit/modules/test_boto_route53.py b/tests/unit/modules/test_boto_route53.py index a01e29c7f7a..ce0be17b044 100644 --- a/tests/unit/modules/test_boto_route53.py +++ b/tests/unit/modules/test_boto_route53.py @@ -1,89 +1,111 @@ - # -*- coding: utf-8 -*- # import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging -import pkg_resources import os.path import sys +import pkg_resources + # Import Salt Libs import salt.config -from salt.ext import six import salt.loader import salt.modules.boto_route53 as boto_route53 import salt.utils.versions +from salt.ext import six # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf # import Python Third Party Libs # pylint: disable=import-error try: import boto - boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') + + boto.ENDPOINTS_PATH = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/endpoints.json" + ) from moto import mock_route53_deprecated + HAS_MOTO = True except ImportError: HAS_MOTO = False def mock_route53_deprecated(self): - ''' + """ if the mock_route53_deprecated function is not available due to import failure this replaces the decorated function with stub_function. Allows boto_route53 unit tests to use the @mock_route53_deprecated decorator without a "NameError: name 'mock_route53_deprecated' is not defined" error. - ''' + """ + def stub_function(self): pass + return stub_function + + # pylint: enable=import-error log = logging.getLogger(__name__) -required_moto = '0.3.7' -required_moto_py3 = '1.0.1' +required_moto = "0.3.7" +required_moto_py3 = "1.0.1" def _has_required_moto(): - ''' + """ Returns True or False depending on if ``moto`` is installed and at the correct version, depending on what version of Python is running these tests. - ''' + """ if not HAS_MOTO: return False else: - moto_version = salt.utils.versions.LooseVersion(pkg_resources.get_distribution('moto').version) + moto_version = salt.utils.versions.LooseVersion( + pkg_resources.get_distribution("moto").version + ) if moto_version < salt.utils.versions.LooseVersion(required_moto): return False - elif six.PY3 and moto_version < salt.utils.versions.LooseVersion(required_moto_py3): + elif six.PY3 and moto_version < salt.utils.versions.LooseVersion( + required_moto_py3 + ): return False return True -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_moto() is False, 'The moto module must be >= to {0} for ' - 'PY2 or {1} for PY3.'.format(required_moto, required_moto_py3)) -@skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_moto() is False, + "The moto module must be >= to {0} for " + "PY2 or {1} for PY3.".format(required_moto, required_moto_py3), +) +@skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", +) class BotoRoute53TestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.boto_route53 module - ''' + """ + def setup_loader_modules(self): self.opts = salt.config.DEFAULT_MINION_OPTS.copy() - self.opts['route53.keyid'] = 'GKTADJGHEIQSXMKKRBJ08H' - self.opts['route53.key'] = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' + self.opts["route53.keyid"] = "GKTADJGHEIQSXMKKRBJ08H" + self.opts["route53.key"] = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" utils = salt.loader.utils(self.opts) - funcs = salt.loader.minion_mods(self.opts, utils=utils, whitelist=['boto_route53', 'config']) + funcs = salt.loader.minion_mods( + self.opts, utils=utils, whitelist=["boto_route53", "config"] + ) return { boto_route53: { - '__opts__': self.opts, - '__utils__': utils, - '__salt__': funcs + "__opts__": self.opts, + "__utils__": utils, + "__salt__": funcs, }, } @@ -98,35 +120,37 @@ class BotoRoute53TestCase(TestCase, LoaderModuleMockMixin): @mock_route53_deprecated def test_create_healthcheck(self): - ''' + """ tests that given a valid instance id and valid ELB that register_instances returns True. - ''' + """ expected = { - 'result': { - 'CreateHealthCheckResponse': { - 'HealthCheck': { - 'HealthCheckConfig': { - 'FailureThreshold': '3', - 'IPAddress': '10.0.0.1', - 'ResourcePath': '/', - 'RequestInterval': '30', - 'Type': 'HTTPS', - 'Port': '443', - 'FullyQualifiedDomainName': 'blog.saltstack.furniture', + "result": { + "CreateHealthCheckResponse": { + "HealthCheck": { + "HealthCheckConfig": { + "FailureThreshold": "3", + "IPAddress": "10.0.0.1", + "ResourcePath": "/", + "RequestInterval": "30", + "Type": "HTTPS", + "Port": "443", + "FullyQualifiedDomainName": "blog.saltstack.furniture", }, - 'HealthCheckVersion': '1', + "HealthCheckVersion": "1", }, }, }, } healthcheck = boto_route53.create_healthcheck( - '10.0.0.1', - fqdn='blog.saltstack.furniture', - hc_type='HTTPS', + "10.0.0.1", + fqdn="blog.saltstack.furniture", + hc_type="HTTPS", port=443, - resource_path='/', + resource_path="/", ) - del healthcheck['result']['CreateHealthCheckResponse']['HealthCheck']['CallerReference'] - del healthcheck['result']['CreateHealthCheckResponse']['HealthCheck']['Id'] + del healthcheck["result"]["CreateHealthCheckResponse"]["HealthCheck"][ + "CallerReference" + ] + del healthcheck["result"]["CreateHealthCheckResponse"]["HealthCheck"]["Id"] self.assertEqual(healthcheck, expected) diff --git a/tests/unit/modules/test_boto_s3_bucket.py b/tests/unit/modules/test_boto_s3_bucket.py index ab990a81c1d..2339b4a731e 100644 --- a/tests/unit/modules/test_boto_s3_bucket.py +++ b/tests/unit/modules/test_boto_s3_bucket.py @@ -2,33 +2,32 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import random import string -import logging from copy import deepcopy -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt libs import salt.loader import salt.modules.boto_s3_bucket as boto_s3_bucket -from salt.utils.versions import LooseVersion # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error,no-name-in-module,unused-import try: import boto import boto3 from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -38,16 +37,16 @@ except ImportError: # the boto_s3_bucket module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' +required_boto3_version = "1.2.1" log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -57,157 +56,124 @@ def _has_required_boto(): if _has_required_boto(): - region = 'us-east-1' - access_key = 'GKTADJGHEIQSXMKKRBJ08H' - secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' - conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} - error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' - e404_error = ClientError({ - 'Error': { - 'Code': '404', - 'Message': "Test-defined error" - } - }, 'msg') - not_found_error = ClientError({ - 'Error': { - 'Code': 'NoSuchBucket', - 'Message': "Test-defined error" - } - }, 'msg') - error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } + region = "us-east-1" + access_key = "GKTADJGHEIQSXMKKRBJ08H" + secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" + conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } + error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" + ) + e404_error = ClientError( + {"Error": {"Code": "404", "Message": "Test-defined error"}}, "msg" + ) + not_found_error = ClientError( + {"Error": {"Code": "NoSuchBucket", "Message": "Test-defined error"}}, "msg" + ) + error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} create_ret = { - 'Location': 'nowhere', + "Location": "nowhere", } list_ret = { - 'Buckets': [{ - 'Name': 'mybucket', - 'CreationDate': None - }], - 'Owner': { - 'DisplayName': 'testuser', - 'ID': '12341234123' - }, - 'ResponseMetadata': {'Key': 'Value'} + "Buckets": [{"Name": "mybucket", "CreationDate": None}], + "Owner": {"DisplayName": "testuser", "ID": "12341234123"}, + "ResponseMetadata": {"Key": "Value"}, } config_ret = { - 'get_bucket_acl': { - 'Grants': [{ - 'Grantee': { - 'DisplayName': 'testowner', - 'ID': 'sdfghjklqwertyuiopzxcvbnm' + "get_bucket_acl": { + "Grants": [ + { + "Grantee": { + "DisplayName": "testowner", + "ID": "sdfghjklqwertyuiopzxcvbnm", + }, + "Permission": "FULL_CONTROL", }, - 'Permission': 'FULL_CONTROL' - }, { - 'Grantee': { - 'URI': 'http://acs.amazonaws.com/groups/global/AllUsers' + { + "Grantee": { + "URI": "http://acs.amazonaws.com/groups/global/AllUsers" + }, + "Permission": "READ", }, - 'Permission': 'READ' - }], - 'Owner': { - 'DisplayName': 'testowner', - 'ID': 'sdfghjklqwertyuiopzxcvbnm' - } + ], + "Owner": {"DisplayName": "testowner", "ID": "sdfghjklqwertyuiopzxcvbnm"}, }, - 'get_bucket_cors': { - 'CORSRules': [{ - 'AllowedMethods': ["GET"], - 'AllowedOrigins': ["*"], - }] + "get_bucket_cors": { + "CORSRules": [{"AllowedMethods": ["GET"], "AllowedOrigins": ["*"]}] }, - 'get_bucket_lifecycle_configuration': { - 'Rules': [{ - 'Expiration': { - 'Days': 1 - }, - 'Prefix': 'prefix', - 'Status': 'Enabled', - 'ID': 'asdfghjklpoiuytrewq' - }] - }, - 'get_bucket_location': { - 'LocationConstraint': 'EU' - }, - 'get_bucket_logging': { - 'LoggingEnabled': { - 'TargetBucket': 'my-bucket', - 'TargetPrefix': 'prefix' - } - }, - 'get_bucket_notification_configuration': { - 'LambdaFunctionConfigurations': [{ - 'LambdaFunctionArn': 'arn:aws:lambda:us-east-1:111111222222:function:my-function', - 'Id': 'zxcvbnmlkjhgfdsa', - 'Events': ["s3:ObjectCreated:*"], - 'Filter': { - 'Key': { - 'FilterRules': [{ - 'Name': 'prefix', - 'Value': 'string' - }] - } + "get_bucket_lifecycle_configuration": { + "Rules": [ + { + "Expiration": {"Days": 1}, + "Prefix": "prefix", + "Status": "Enabled", + "ID": "asdfghjklpoiuytrewq", } - }] + ] }, - 'get_bucket_policy': { - 'Policy': - '{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111222222:root"},"Action":"s3:PutObject","Resource":"arn:aws:s3:::my-bucket/*"}]}' + "get_bucket_location": {"LocationConstraint": "EU"}, + "get_bucket_logging": { + "LoggingEnabled": {"TargetBucket": "my-bucket", "TargetPrefix": "prefix"} }, - 'get_bucket_replication': { - 'ReplicationConfiguration': { - 'Role': 'arn:aws:iam::11111222222:my-role', - 'Rules': [{ - 'ID': "r1", - 'Prefix': "prefix", - 'Status': "Enabled", - 'Destination': { - 'Bucket': "arn:aws:s3:::my-bucket" + "get_bucket_notification_configuration": { + "LambdaFunctionConfigurations": [ + { + "LambdaFunctionArn": "arn:aws:lambda:us-east-1:111111222222:function:my-function", + "Id": "zxcvbnmlkjhgfdsa", + "Events": ["s3:ObjectCreated:*"], + "Filter": { + "Key": {"FilterRules": [{"Name": "prefix", "Value": "string"}]} + }, + } + ] + }, + "get_bucket_policy": { + "Policy": '{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111222222:root"},"Action":"s3:PutObject","Resource":"arn:aws:s3:::my-bucket/*"}]}' + }, + "get_bucket_replication": { + "ReplicationConfiguration": { + "Role": "arn:aws:iam::11111222222:my-role", + "Rules": [ + { + "ID": "r1", + "Prefix": "prefix", + "Status": "Enabled", + "Destination": {"Bucket": "arn:aws:s3:::my-bucket"}, } - }] + ], } }, - 'get_bucket_request_payment': {'Payer': 'Requester'}, - 'get_bucket_tagging': { - 'TagSet': [{ - 'Key': 'c', - 'Value': 'd' - }, { - 'Key': 'a', - 'Value': 'b', - }] + "get_bucket_request_payment": {"Payer": "Requester"}, + "get_bucket_tagging": { + "TagSet": [{"Key": "c", "Value": "d"}, {"Key": "a", "Value": "b"}] }, - 'get_bucket_versioning': { - 'Status': 'Enabled' + "get_bucket_versioning": {"Status": "Enabled"}, + "get_bucket_website": { + "ErrorDocument": {"Key": "error.html"}, + "IndexDocument": {"Suffix": "index.html"}, }, - 'get_bucket_website': { - 'ErrorDocument': { - 'Key': 'error.html' - }, - 'IndexDocument': { - 'Suffix': 'index.html' - } - } } -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) class BotoS3BucketTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context={}) - return {boto_s3_bucket: {'__utils__': utils}} + opts, whitelist=["boto3", "args", "systemd", "path", "platform"], context={} + ) + return {boto_s3_bucket: {"__utils__": utils}} def setUp(self): super(BotoS3BucketTestCaseBase, self).setUp() @@ -217,16 +183,18 @@ class BotoS3BucketTestCaseBase(TestCase, LoaderModuleMockMixin): # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn @@ -235,486 +203,606 @@ class BotoS3BucketTestCaseMixin(object): class BotoS3BucketTestCase(BotoS3BucketTestCaseBase, BotoS3BucketTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_s3_bucket module - ''' + """ - def test_that_when_checking_if_a_bucket_exists_and_a_bucket_exists_the_bucket_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_bucket_exists_and_a_bucket_exists_the_bucket_exists_method_returns_true( + self, + ): + """ Tests checking s3 bucket existence when the s3 bucket already exists - ''' + """ self.conn.head_bucket.return_value = None - result = boto_s3_bucket.exists(Bucket='mybucket', **conn_parameters) + result = boto_s3_bucket.exists(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['exists']) + self.assertTrue(result["exists"]) - def test_that_when_checking_if_a_bucket_exists_and_a_bucket_does_not_exist_the_bucket_exists_method_returns_false(self): - ''' + def test_that_when_checking_if_a_bucket_exists_and_a_bucket_does_not_exist_the_bucket_exists_method_returns_false( + self, + ): + """ Tests checking s3 bucket existence when the s3 bucket does not exist - ''' + """ self.conn.head_bucket.side_effect = e404_error - result = boto_s3_bucket.exists(Bucket='mybucket', **conn_parameters) + result = boto_s3_bucket.exists(Bucket="mybucket", **conn_parameters) - self.assertFalse(result['exists']) + self.assertFalse(result["exists"]) - def test_that_when_checking_if_a_bucket_exists_and_boto3_returns_an_error_the_bucket_exists_method_returns_error(self): - ''' + def test_that_when_checking_if_a_bucket_exists_and_boto3_returns_an_error_the_bucket_exists_method_returns_error( + self, + ): + """ Tests checking s3 bucket existence when boto returns an error - ''' - self.conn.head_bucket.side_effect = ClientError(error_content, 'head_bucket') - result = boto_s3_bucket.exists(Bucket='mybucket', **conn_parameters) + """ + self.conn.head_bucket.side_effect = ClientError(error_content, "head_bucket") + result = boto_s3_bucket.exists(Bucket="mybucket", **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('head_bucket')) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("head_bucket") + ) - def test_that_when_creating_a_bucket_succeeds_the_create_bucket_method_returns_true(self): - ''' + def test_that_when_creating_a_bucket_succeeds_the_create_bucket_method_returns_true( + self, + ): + """ tests True bucket created. - ''' + """ self.conn.create_bucket.return_value = create_ret - result = boto_s3_bucket.create(Bucket='mybucket', - LocationConstraint='nowhere', - **conn_parameters) + result = boto_s3_bucket.create( + Bucket="mybucket", LocationConstraint="nowhere", **conn_parameters + ) - self.assertTrue(result['created']) + self.assertTrue(result["created"]) - def test_that_when_creating_a_bucket_fails_the_create_bucket_method_returns_error(self): - ''' + def test_that_when_creating_a_bucket_fails_the_create_bucket_method_returns_error( + self, + ): + """ tests False bucket not created. - ''' - self.conn.create_bucket.side_effect = ClientError(error_content, 'create_bucket') - result = boto_s3_bucket.create(Bucket='mybucket', - LocationConstraint='nowhere', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('create_bucket')) + """ + self.conn.create_bucket.side_effect = ClientError( + error_content, "create_bucket" + ) + result = boto_s3_bucket.create( + Bucket="mybucket", LocationConstraint="nowhere", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("create_bucket"), + ) - def test_that_when_deleting_a_bucket_succeeds_the_delete_bucket_method_returns_true(self): - ''' + def test_that_when_deleting_a_bucket_succeeds_the_delete_bucket_method_returns_true( + self, + ): + """ tests True bucket deleted. - ''' - result = boto_s3_bucket.delete(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.delete(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_a_bucket_fails_the_delete_bucket_method_returns_false(self): - ''' + def test_that_when_deleting_a_bucket_fails_the_delete_bucket_method_returns_false( + self, + ): + """ tests False bucket not deleted. - ''' - self.conn.delete_bucket.side_effect = ClientError(error_content, 'delete_bucket') - result = boto_s3_bucket.delete(Bucket='mybucket', - **conn_parameters) - self.assertFalse(result['deleted']) + """ + self.conn.delete_bucket.side_effect = ClientError( + error_content, "delete_bucket" + ) + result = boto_s3_bucket.delete(Bucket="mybucket", **conn_parameters) + self.assertFalse(result["deleted"]) - def test_that_when_describing_bucket_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_bucket_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters if bucket exists - ''' + """ for key, value in six.iteritems(config_ret): getattr(self.conn, key).return_value = deepcopy(value) - result = boto_s3_bucket.describe(Bucket='mybucket', **conn_parameters) + result = boto_s3_bucket.describe(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['bucket']) + self.assertTrue(result["bucket"]) - def test_that_when_describing_bucket_it_returns_the_dict_of_properties_returns_false(self): - ''' + def test_that_when_describing_bucket_it_returns_the_dict_of_properties_returns_false( + self, + ): + """ Tests describing parameters if bucket does not exist - ''' + """ self.conn.get_bucket_acl.side_effect = not_found_error - result = boto_s3_bucket.describe(Bucket='mybucket', **conn_parameters) + result = boto_s3_bucket.describe(Bucket="mybucket", **conn_parameters) - self.assertFalse(result['bucket']) + self.assertFalse(result["bucket"]) def test_that_when_describing_bucket_on_client_error_it_returns_error(self): - ''' + """ Tests describing parameters failure - ''' - self.conn.get_bucket_acl.side_effect = ClientError(error_content, 'get_bucket_acl') - result = boto_s3_bucket.describe(Bucket='mybucket', **conn_parameters) - self.assertTrue('error' in result) + """ + self.conn.get_bucket_acl.side_effect = ClientError( + error_content, "get_bucket_acl" + ) + result = boto_s3_bucket.describe(Bucket="mybucket", **conn_parameters) + self.assertTrue("error" in result) - def test_that_when_listing_buckets_succeeds_the_list_buckets_method_returns_true(self): - ''' + def test_that_when_listing_buckets_succeeds_the_list_buckets_method_returns_true( + self, + ): + """ tests True buckets listed. - ''' + """ self.conn.list_buckets.return_value = deepcopy(list_ret) result = boto_s3_bucket.list(**conn_parameters) - self.assertTrue(result['Buckets']) + self.assertTrue(result["Buckets"]) def test_that_when_listing_bucket_fails_the_list_bucket_method_returns_false(self): - ''' + """ tests False no bucket listed. - ''' + """ ret = deepcopy(list_ret) log.info(ret) - ret['Buckets'] = list() + ret["Buckets"] = list() self.conn.list_buckets.return_value = ret result = boto_s3_bucket.list(**conn_parameters) - self.assertFalse(result['Buckets']) + self.assertFalse(result["Buckets"]) def test_that_when_listing_bucket_fails_the_list_bucket_method_returns_error(self): - ''' + """ tests False bucket error. - ''' - self.conn.list_buckets.side_effect = ClientError(error_content, 'list_buckets') + """ + self.conn.list_buckets.side_effect = ClientError(error_content, "list_buckets") result = boto_s3_bucket.list(**conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), error_message.format('list_buckets')) + self.assertEqual( + result.get("error", {}).get("message"), error_message.format("list_buckets") + ) def test_that_when_putting_acl_succeeds_the_put_acl_method_returns_true(self): - ''' + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_acl(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.put_acl(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) def test_that_when_putting_acl_fails_the_put_acl_method_returns_error(self): - ''' + """ tests False bucket not updated. - ''' - self.conn.put_bucket_acl.side_effect = ClientError(error_content, - 'put_bucket_acl') - result = boto_s3_bucket.put_acl(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_acl')) + """ + self.conn.put_bucket_acl.side_effect = ClientError( + error_content, "put_bucket_acl" + ) + result = boto_s3_bucket.put_acl(Bucket="mybucket", **conn_parameters) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_acl"), + ) def test_that_when_putting_cors_succeeds_the_put_cors_method_returns_true(self): - ''' + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_cors(Bucket='mybucket', CORSRules='[]', - **conn_parameters) + """ + result = boto_s3_bucket.put_cors( + Bucket="mybucket", CORSRules="[]", **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) def test_that_when_putting_cors_fails_the_put_cors_method_returns_error(self): - ''' + """ tests False bucket not updated. - ''' - self.conn.put_bucket_cors.side_effect = ClientError(error_content, - 'put_bucket_cors') - result = boto_s3_bucket.put_cors(Bucket='mybucket', CORSRules='[]', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_cors')) + """ + self.conn.put_bucket_cors.side_effect = ClientError( + error_content, "put_bucket_cors" + ) + result = boto_s3_bucket.put_cors( + Bucket="mybucket", CORSRules="[]", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_cors"), + ) - def test_that_when_putting_lifecycle_configuration_succeeds_the_put_lifecycle_configuration_method_returns_true(self): - ''' + def test_that_when_putting_lifecycle_configuration_succeeds_the_put_lifecycle_configuration_method_returns_true( + self, + ): + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_lifecycle_configuration(Bucket='mybucket', - Rules='[]', - **conn_parameters) + """ + result = boto_s3_bucket.put_lifecycle_configuration( + Bucket="mybucket", Rules="[]", **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_putting_lifecycle_configuration_fails_the_put_lifecycle_configuration_method_returns_error(self): - ''' + def test_that_when_putting_lifecycle_configuration_fails_the_put_lifecycle_configuration_method_returns_error( + self, + ): + """ tests False bucket not updated. - ''' - self.conn.put_bucket_lifecycle_configuration.side_effect = ClientError(error_content, - 'put_bucket_lifecycle_configuration') - result = boto_s3_bucket.put_lifecycle_configuration(Bucket='mybucket', - Rules='[]', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_lifecycle_configuration')) + """ + self.conn.put_bucket_lifecycle_configuration.side_effect = ClientError( + error_content, "put_bucket_lifecycle_configuration" + ) + result = boto_s3_bucket.put_lifecycle_configuration( + Bucket="mybucket", Rules="[]", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_lifecycle_configuration"), + ) - def test_that_when_putting_logging_succeeds_the_put_logging_method_returns_true(self): - ''' + def test_that_when_putting_logging_succeeds_the_put_logging_method_returns_true( + self, + ): + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_logging(Bucket='mybucket', - TargetBucket='arn:::::', - TargetPrefix='asdf', - TargetGrants='[]', - **conn_parameters) + """ + result = boto_s3_bucket.put_logging( + Bucket="mybucket", + TargetBucket="arn:::::", + TargetPrefix="asdf", + TargetGrants="[]", + **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) def test_that_when_putting_logging_fails_the_put_logging_method_returns_error(self): - ''' + """ tests False bucket not updated. - ''' - self.conn.put_bucket_logging.side_effect = ClientError(error_content, - 'put_bucket_logging') - result = boto_s3_bucket.put_logging(Bucket='mybucket', - TargetBucket='arn:::::', - TargetPrefix='asdf', - TargetGrants='[]', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_logging')) + """ + self.conn.put_bucket_logging.side_effect = ClientError( + error_content, "put_bucket_logging" + ) + result = boto_s3_bucket.put_logging( + Bucket="mybucket", + TargetBucket="arn:::::", + TargetPrefix="asdf", + TargetGrants="[]", + **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_logging"), + ) - def test_that_when_putting_notification_configuration_succeeds_the_put_notification_configuration_method_returns_true(self): - ''' + def test_that_when_putting_notification_configuration_succeeds_the_put_notification_configuration_method_returns_true( + self, + ): + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_notification_configuration(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.put_notification_configuration( + Bucket="mybucket", **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_putting_notification_configuration_fails_the_put_notification_configuration_method_returns_error(self): - ''' + def test_that_when_putting_notification_configuration_fails_the_put_notification_configuration_method_returns_error( + self, + ): + """ tests False bucket not updated. - ''' - self.conn.put_bucket_notification_configuration.side_effect = ClientError(error_content, - 'put_bucket_notification_configuration') - result = boto_s3_bucket.put_notification_configuration(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_notification_configuration')) + """ + self.conn.put_bucket_notification_configuration.side_effect = ClientError( + error_content, "put_bucket_notification_configuration" + ) + result = boto_s3_bucket.put_notification_configuration( + Bucket="mybucket", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_notification_configuration"), + ) def test_that_when_putting_policy_succeeds_the_put_policy_method_returns_true(self): - ''' + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_policy(Bucket='mybucket', - Policy='{}', - **conn_parameters) + """ + result = boto_s3_bucket.put_policy( + Bucket="mybucket", Policy="{}", **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) def test_that_when_putting_policy_fails_the_put_policy_method_returns_error(self): - ''' + """ tests False bucket not updated. - ''' - self.conn.put_bucket_policy.side_effect = ClientError(error_content, - 'put_bucket_policy') - result = boto_s3_bucket.put_policy(Bucket='mybucket', - Policy='{}', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_policy')) + """ + self.conn.put_bucket_policy.side_effect = ClientError( + error_content, "put_bucket_policy" + ) + result = boto_s3_bucket.put_policy( + Bucket="mybucket", Policy="{}", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_policy"), + ) - def test_that_when_putting_replication_succeeds_the_put_replication_method_returns_true(self): - ''' + def test_that_when_putting_replication_succeeds_the_put_replication_method_returns_true( + self, + ): + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_replication(Bucket='mybucket', - Role='arn:aws:iam:::', - Rules='[]', - **conn_parameters) + """ + result = boto_s3_bucket.put_replication( + Bucket="mybucket", Role="arn:aws:iam:::", Rules="[]", **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_putting_replication_fails_the_put_replication_method_returns_error(self): - ''' + def test_that_when_putting_replication_fails_the_put_replication_method_returns_error( + self, + ): + """ tests False bucket not updated. - ''' - self.conn.put_bucket_replication.side_effect = ClientError(error_content, - 'put_bucket_replication') - result = boto_s3_bucket.put_replication(Bucket='mybucket', - Role='arn:aws:iam:::', - Rules='[]', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_replication')) + """ + self.conn.put_bucket_replication.side_effect = ClientError( + error_content, "put_bucket_replication" + ) + result = boto_s3_bucket.put_replication( + Bucket="mybucket", Role="arn:aws:iam:::", Rules="[]", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_replication"), + ) - def test_that_when_putting_request_payment_succeeds_the_put_request_payment_method_returns_true(self): - ''' + def test_that_when_putting_request_payment_succeeds_the_put_request_payment_method_returns_true( + self, + ): + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_request_payment(Bucket='mybucket', - Payer='Requester', - **conn_parameters) + """ + result = boto_s3_bucket.put_request_payment( + Bucket="mybucket", Payer="Requester", **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_putting_request_payment_fails_the_put_request_payment_method_returns_error(self): - ''' + def test_that_when_putting_request_payment_fails_the_put_request_payment_method_returns_error( + self, + ): + """ tests False bucket not updated. - ''' - self.conn.put_bucket_request_payment.side_effect = ClientError(error_content, - 'put_bucket_request_payment') - result = boto_s3_bucket.put_request_payment(Bucket='mybucket', - Payer='Requester', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_request_payment')) + """ + self.conn.put_bucket_request_payment.side_effect = ClientError( + error_content, "put_bucket_request_payment" + ) + result = boto_s3_bucket.put_request_payment( + Bucket="mybucket", Payer="Requester", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_request_payment"), + ) - def test_that_when_putting_tagging_succeeds_the_put_tagging_method_returns_true(self): - ''' + def test_that_when_putting_tagging_succeeds_the_put_tagging_method_returns_true( + self, + ): + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_tagging(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.put_tagging(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) def test_that_when_putting_tagging_fails_the_put_tagging_method_returns_error(self): - ''' + """ tests False bucket not updated. - ''' - self.conn.put_bucket_tagging.side_effect = ClientError(error_content, - 'put_bucket_tagging') - result = boto_s3_bucket.put_tagging(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_tagging')) + """ + self.conn.put_bucket_tagging.side_effect = ClientError( + error_content, "put_bucket_tagging" + ) + result = boto_s3_bucket.put_tagging(Bucket="mybucket", **conn_parameters) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_tagging"), + ) - def test_that_when_putting_versioning_succeeds_the_put_versioning_method_returns_true(self): - ''' + def test_that_when_putting_versioning_succeeds_the_put_versioning_method_returns_true( + self, + ): + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_versioning(Bucket='mybucket', - Status='Enabled', - **conn_parameters) + """ + result = boto_s3_bucket.put_versioning( + Bucket="mybucket", Status="Enabled", **conn_parameters + ) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) - def test_that_when_putting_versioning_fails_the_put_versioning_method_returns_error(self): - ''' + def test_that_when_putting_versioning_fails_the_put_versioning_method_returns_error( + self, + ): + """ tests False bucket not updated. - ''' - self.conn.put_bucket_versioning.side_effect = ClientError(error_content, - 'put_bucket_versioning') - result = boto_s3_bucket.put_versioning(Bucket='mybucket', - Status='Enabled', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_versioning')) + """ + self.conn.put_bucket_versioning.side_effect = ClientError( + error_content, "put_bucket_versioning" + ) + result = boto_s3_bucket.put_versioning( + Bucket="mybucket", Status="Enabled", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_versioning"), + ) - def test_that_when_putting_website_succeeds_the_put_website_method_returns_true(self): - ''' + def test_that_when_putting_website_succeeds_the_put_website_method_returns_true( + self, + ): + """ tests True bucket updated. - ''' - result = boto_s3_bucket.put_website(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.put_website(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['updated']) + self.assertTrue(result["updated"]) def test_that_when_putting_website_fails_the_put_website_method_returns_error(self): - ''' + """ tests False bucket not updated. - ''' - self.conn.put_bucket_website.side_effect = ClientError(error_content, - 'put_bucket_website') - result = boto_s3_bucket.put_website(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('put_bucket_website')) + """ + self.conn.put_bucket_website.side_effect = ClientError( + error_content, "put_bucket_website" + ) + result = boto_s3_bucket.put_website(Bucket="mybucket", **conn_parameters) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("put_bucket_website"), + ) def test_that_when_deleting_cors_succeeds_the_delete_cors_method_returns_true(self): - ''' + """ tests True bucket attribute deleted. - ''' - result = boto_s3_bucket.delete_cors(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.delete_cors(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) def test_that_when_deleting_cors_fails_the_delete_cors_method_returns_error(self): - ''' + """ tests False bucket attribute not deleted. - ''' - self.conn.delete_bucket_cors.side_effect = ClientError(error_content, - 'delete_bucket_cors') - result = boto_s3_bucket.delete_cors(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('delete_bucket_cors')) + """ + self.conn.delete_bucket_cors.side_effect = ClientError( + error_content, "delete_bucket_cors" + ) + result = boto_s3_bucket.delete_cors(Bucket="mybucket", **conn_parameters) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("delete_bucket_cors"), + ) - def test_that_when_deleting_lifecycle_configuration_succeeds_the_delete_lifecycle_configuration_method_returns_true(self): - ''' + def test_that_when_deleting_lifecycle_configuration_succeeds_the_delete_lifecycle_configuration_method_returns_true( + self, + ): + """ tests True bucket attribute deleted. - ''' - result = boto_s3_bucket.delete_lifecycle_configuration(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.delete_lifecycle_configuration( + Bucket="mybucket", **conn_parameters + ) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_lifecycle_configuration_fails_the_delete_lifecycle_configuration_method_returns_error(self): - ''' + def test_that_when_deleting_lifecycle_configuration_fails_the_delete_lifecycle_configuration_method_returns_error( + self, + ): + """ tests False bucket attribute not deleted. - ''' - self.conn.delete_bucket_lifecycle.side_effect = ClientError(error_content, - 'delete_bucket_lifecycle_configuration') - result = boto_s3_bucket.delete_lifecycle_configuration(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('delete_bucket_lifecycle_configuration')) + """ + self.conn.delete_bucket_lifecycle.side_effect = ClientError( + error_content, "delete_bucket_lifecycle_configuration" + ) + result = boto_s3_bucket.delete_lifecycle_configuration( + Bucket="mybucket", **conn_parameters + ) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("delete_bucket_lifecycle_configuration"), + ) - def test_that_when_deleting_policy_succeeds_the_delete_policy_method_returns_true(self): - ''' + def test_that_when_deleting_policy_succeeds_the_delete_policy_method_returns_true( + self, + ): + """ tests True bucket attribute deleted. - ''' - result = boto_s3_bucket.delete_policy(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.delete_policy(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_policy_fails_the_delete_policy_method_returns_error(self): - ''' + def test_that_when_deleting_policy_fails_the_delete_policy_method_returns_error( + self, + ): + """ tests False bucket attribute not deleted. - ''' - self.conn.delete_bucket_policy.side_effect = ClientError(error_content, - 'delete_bucket_policy') - result = boto_s3_bucket.delete_policy(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('delete_bucket_policy')) + """ + self.conn.delete_bucket_policy.side_effect = ClientError( + error_content, "delete_bucket_policy" + ) + result = boto_s3_bucket.delete_policy(Bucket="mybucket", **conn_parameters) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("delete_bucket_policy"), + ) - def test_that_when_deleting_replication_succeeds_the_delete_replication_method_returns_true(self): - ''' + def test_that_when_deleting_replication_succeeds_the_delete_replication_method_returns_true( + self, + ): + """ tests True bucket attribute deleted. - ''' - result = boto_s3_bucket.delete_replication(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.delete_replication(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_replication_fails_the_delete_replication_method_returns_error(self): - ''' + def test_that_when_deleting_replication_fails_the_delete_replication_method_returns_error( + self, + ): + """ tests False bucket attribute not deleted. - ''' - self.conn.delete_bucket_replication.side_effect = ClientError(error_content, - 'delete_bucket_replication') - result = boto_s3_bucket.delete_replication(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('delete_bucket_replication')) + """ + self.conn.delete_bucket_replication.side_effect = ClientError( + error_content, "delete_bucket_replication" + ) + result = boto_s3_bucket.delete_replication(Bucket="mybucket", **conn_parameters) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("delete_bucket_replication"), + ) - def test_that_when_deleting_tagging_succeeds_the_delete_tagging_method_returns_true(self): - ''' + def test_that_when_deleting_tagging_succeeds_the_delete_tagging_method_returns_true( + self, + ): + """ tests True bucket attribute deleted. - ''' - result = boto_s3_bucket.delete_tagging(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.delete_tagging(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_tagging_fails_the_delete_tagging_method_returns_error(self): - ''' + def test_that_when_deleting_tagging_fails_the_delete_tagging_method_returns_error( + self, + ): + """ tests False bucket attribute not deleted. - ''' - self.conn.delete_bucket_tagging.side_effect = ClientError(error_content, - 'delete_bucket_tagging') - result = boto_s3_bucket.delete_tagging(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('delete_bucket_tagging')) + """ + self.conn.delete_bucket_tagging.side_effect = ClientError( + error_content, "delete_bucket_tagging" + ) + result = boto_s3_bucket.delete_tagging(Bucket="mybucket", **conn_parameters) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("delete_bucket_tagging"), + ) - def test_that_when_deleting_website_succeeds_the_delete_website_method_returns_true(self): - ''' + def test_that_when_deleting_website_succeeds_the_delete_website_method_returns_true( + self, + ): + """ tests True bucket attribute deleted. - ''' - result = boto_s3_bucket.delete_website(Bucket='mybucket', - **conn_parameters) + """ + result = boto_s3_bucket.delete_website(Bucket="mybucket", **conn_parameters) - self.assertTrue(result['deleted']) + self.assertTrue(result["deleted"]) - def test_that_when_deleting_website_fails_the_delete_website_method_returns_error(self): - ''' + def test_that_when_deleting_website_fails_the_delete_website_method_returns_error( + self, + ): + """ tests False bucket attribute not deleted. - ''' - self.conn.delete_bucket_website.side_effect = ClientError(error_content, - 'delete_bucket_website') - result = boto_s3_bucket.delete_website(Bucket='mybucket', - **conn_parameters) - self.assertEqual(result.get('error', {}).get('message'), - error_message.format('delete_bucket_website')) + """ + self.conn.delete_bucket_website.side_effect = ClientError( + error_content, "delete_bucket_website" + ) + result = boto_s3_bucket.delete_website(Bucket="mybucket", **conn_parameters) + self.assertEqual( + result.get("error", {}).get("message"), + error_message.format("delete_bucket_website"), + ) diff --git a/tests/unit/modules/test_boto_secgroup.py b/tests/unit/modules/test_boto_secgroup.py index 76eb5aa0e96..17e92833066 100644 --- a/tests/unit/modules/test_boto_secgroup.py +++ b/tests/unit/modules/test_boto_secgroup.py @@ -2,78 +2,99 @@ # import Python Libs from __future__ import absolute_import, print_function, unicode_literals + +import os.path import random import string -from copy import deepcopy -import os.path import sys - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.runtests import RUNTIME_VARS +from copy import deepcopy # Import Salt libs import salt.config import salt.loader -from salt.utils.versions import LooseVersion -from salt.utils.odict import OrderedDict import salt.modules.boto_secgroup as boto_secgroup # Import Third Party Libs # pylint: disable=import-error from salt.ext.six.moves import range # pylint: disable=redefined-builtin +from salt.utils.odict import OrderedDict +from salt.utils.versions import LooseVersion + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + try: import boto - boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') + + boto.ENDPOINTS_PATH = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/endpoints.json" + ) import boto.ec2 # pylint: enable=unused-import + HAS_BOTO = True except ImportError: HAS_BOTO = False try: from moto import mock_ec2_deprecated + HAS_MOTO = True except ImportError: HAS_MOTO = False def mock_ec2_deprecated(self): - ''' + """ if the mock_ec2_deprecated function is not available due to import failure this replaces the decorated function with stub_function. Allows boto_secgroup unit tests to use the @mock_ec2_deprecated decorator without a "NameError: name 'mock_ec2_deprecated' is not defined" error. - ''' + """ + def stub_function(self): pass + return stub_function + + # pylint: enable=import-error -required_boto_version = '2.4.0' -vpc_id = 'vpc-mjm05d27' -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} -boto_conn_parameters = {'aws_access_key_id': access_key, 'aws_secret_access_key': secret_key} +required_boto_version = "2.4.0" +vpc_id = "vpc-mjm05d27" +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, +} +boto_conn_parameters = { + "aws_access_key_id": access_key, + "aws_secret_access_key": secret_key, +} def _random_group_id(): - group_id = 'sg-{0:x}'.format(random.randrange(2 ** 32)) + group_id = "sg-{0:x}".format(random.randrange(2 ** 32)) return group_id def _random_group_name(): - group_name = 'boto_secgroup-{0}'.format(''.join((random.choice(string.ascii_lowercase)) for char in range(12))) + group_name = "boto_secgroup-{0}".format( + "".join((random.choice(string.ascii_lowercase)) for char in range(12)) + ) return group_name def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto.__version__) < LooseVersion(required_boto_version): @@ -82,28 +103,26 @@ def _has_required_boto(): return True -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {0}".format(required_boto_version), +) class BotoSecgroupTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.boto_secgroup module - ''' + """ def setup_loader_modules(self): opts = salt.config.DEFAULT_MASTER_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto', 'args', 'systemd', 'path', 'platform']) + opts, whitelist=["boto", "args", "systemd", "path", "platform"] + ) funcs = salt.loader.minion_mods(opts, utils=utils) return { - boto_secgroup: { - '__opts__': opts, - '__utils__': utils, - '__salt__': funcs - } + boto_secgroup: {"__opts__": opts, "__utils__": utils, "__salt__": funcs} } def setUp(self): @@ -112,192 +131,300 @@ class BotoSecgroupTestCase(TestCase, LoaderModuleMockMixin): boto_secgroup.__virtual__() def test__split_rules(self): - ''' + """ tests the splitting of a list of rules into individual rules - ''' - rules = [OrderedDict([('ip_protocol', u'tcp'), ('from_port', 22), ('to_port', 22), ('grants', [OrderedDict([('cidr_ip', u'0.0.0.0/0')])])]), - OrderedDict([('ip_protocol', u'tcp'), ('from_port', 80), ('to_port', 80), ('grants', [OrderedDict([('cidr_ip', u'0.0.0.0/0')])])])] - split_rules = [{'to_port': 22, 'from_port': 22, 'ip_protocol': u'tcp', 'cidr_ip': u'0.0.0.0/0'}, - {'to_port': 80, 'from_port': 80, 'ip_protocol': u'tcp', 'cidr_ip': u'0.0.0.0/0'}] + """ + rules = [ + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 22), + ("to_port", 22), + ("grants", [OrderedDict([("cidr_ip", "0.0.0.0/0")])]), + ] + ), + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 80), + ("to_port", 80), + ("grants", [OrderedDict([("cidr_ip", "0.0.0.0/0")])]), + ] + ), + ] + split_rules = [ + { + "to_port": 22, + "from_port": 22, + "ip_protocol": "tcp", + "cidr_ip": "0.0.0.0/0", + }, + { + "to_port": 80, + "from_port": 80, + "ip_protocol": "tcp", + "cidr_ip": "0.0.0.0/0", + }, + ] self.assertEqual(boto_secgroup._split_rules(rules), split_rules) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_create_ec2_classic(self): - ''' + """ Test of creation of an EC2-Classic security group. The test ensures that a group was created with the desired name and description - ''' + """ group_name = _random_group_name() - group_description = 'test_create_ec2_classic' + group_description = "test_create_ec2_classic" boto_secgroup.create(group_name, group_description, **conn_parameters) conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) - group_filter = {'group-name': group_name} + group_filter = {"group-name": group_name} secgroup_created_group = conn.get_all_security_groups(filters=group_filter) - expected_create_result = [group_name, - group_description, - None] - secgroup_create_result = [secgroup_created_group[0].name, - secgroup_created_group[0].description, - secgroup_created_group[0].vpc_id] + expected_create_result = [group_name, group_description, None] + secgroup_create_result = [ + secgroup_created_group[0].name, + secgroup_created_group[0].description, + secgroup_created_group[0].vpc_id, + ] self.assertEqual(expected_create_result, secgroup_create_result) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_create_ec2_vpc(self): - ''' + """ test of creation of an EC2-VPC security group. The test ensures that a group was created in a given vpc with the desired name and description - ''' + """ group_name = _random_group_name() - group_description = 'test_create_ec2_vpc' + group_description = "test_create_ec2_vpc" # create a group using boto_secgroup - boto_secgroup.create(group_name, group_description, vpc_id=vpc_id, **conn_parameters) + boto_secgroup.create( + group_name, group_description, vpc_id=vpc_id, **conn_parameters + ) # confirm that the group actually exists conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) - group_filter = {'group-name': group_name, 'vpc-id': vpc_id} + group_filter = {"group-name": group_name, "vpc-id": vpc_id} secgroup_created_group = conn.get_all_security_groups(filters=group_filter) expected_create_result = [group_name, group_description, vpc_id] - secgroup_create_result = [secgroup_created_group[0].name, secgroup_created_group[0].description, secgroup_created_group[0].vpc_id] + secgroup_create_result = [ + secgroup_created_group[0].name, + secgroup_created_group[0].description, + secgroup_created_group[0].vpc_id, + ] self.assertEqual(expected_create_result, secgroup_create_result) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_get_group_id_ec2_classic(self): - ''' + """ tests that given a name of a group in EC2-Classic that the correct group id will be retrieved - ''' + """ group_name = _random_group_name() - group_description = 'test_get_group_id_ec2_classic' + group_description = "test_get_group_id_ec2_classic" conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) - group_classic = conn.create_security_group(name=group_name, - description=group_description) + group_classic = conn.create_security_group( + name=group_name, description=group_description + ) # note that the vpc_id does not need to be created in order to create # a security group within the vpc when using moto - group_vpc = conn.create_security_group(name=group_name, - description=group_description, - vpc_id=vpc_id) - retrieved_group_id = boto_secgroup.get_group_id(group_name, - **conn_parameters) + group_vpc = conn.create_security_group( + name=group_name, description=group_description, vpc_id=vpc_id + ) + retrieved_group_id = boto_secgroup.get_group_id(group_name, **conn_parameters) self.assertEqual(group_classic.id, retrieved_group_id) - @skipIf(True, 'test skipped because moto does not yet support group' - ' filters https://github.com/spulec/moto/issues/154') + @skipIf( + True, + "test skipped because moto does not yet support group" + " filters https://github.com/spulec/moto/issues/154", + ) @mock_ec2_deprecated def test_get_group_id_ec2_vpc(self): - ''' + """ tests that given a name of a group in EC2-VPC that the correct group id will be retrieved - ''' + """ group_name = _random_group_name() - group_description = 'test_get_group_id_ec2_vpc' + group_description = "test_get_group_id_ec2_vpc" conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) - group_classic = conn.create_security_group(name=group_name, - description=group_description) + group_classic = conn.create_security_group( + name=group_name, description=group_description + ) # note that the vpc_id does not need to be created in order to create # a security group within the vpc when using moto - group_vpc = conn.create_security_group(name=group_name, - description=group_description, - vpc_id=vpc_id) - retrieved_group_id = boto_secgroup.get_group_id(group_name, group_vpc, - **conn_parameters) + group_vpc = conn.create_security_group( + name=group_name, description=group_description, vpc_id=vpc_id + ) + retrieved_group_id = boto_secgroup.get_group_id( + group_name, group_vpc, **conn_parameters + ) self.assertEqual(group_vpc.id, retrieved_group_id) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_get_config_single_rule_group_name(self): - ''' + """ tests return of 'config' when given group name. get_config returns an OrderedDict. - ''' + """ group_name = _random_group_name() - ip_protocol = u'tcp' + ip_protocol = "tcp" from_port = 22 to_port = 22 - cidr_ip = u'0.0.0.0/0' - rules_egress = [{'to_port': None, 'from_port': None, 'ip_protocol': u'-1', 'cidr_ip': u'0.0.0.0/0'}] + cidr_ip = "0.0.0.0/0" + rules_egress = [ + { + "to_port": None, + "from_port": None, + "ip_protocol": "-1", + "cidr_ip": "0.0.0.0/0", + } + ] conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) group = conn.create_security_group(name=group_name, description=group_name) - group.authorize(ip_protocol=ip_protocol, from_port=from_port, to_port=to_port, cidr_ip=cidr_ip) + group.authorize( + ip_protocol=ip_protocol, + from_port=from_port, + to_port=to_port, + cidr_ip=cidr_ip, + ) # setup the expected get_config result - expected_get_config_result = OrderedDict([('name', group.name), ('group_id', group.id), ('owner_id', u'123456789012'), - ('description', group.description), ('tags', {}), - ('rules', [{'to_port': to_port, 'from_port': from_port, - 'ip_protocol': ip_protocol, 'cidr_ip': cidr_ip}]), - ('rules_egress', rules_egress)]) - secgroup_get_config_result = boto_secgroup.get_config(group_id=group.id, **conn_parameters) + expected_get_config_result = OrderedDict( + [ + ("name", group.name), + ("group_id", group.id), + ("owner_id", "123456789012"), + ("description", group.description), + ("tags", {}), + ( + "rules", + [ + { + "to_port": to_port, + "from_port": from_port, + "ip_protocol": ip_protocol, + "cidr_ip": cidr_ip, + } + ], + ), + ("rules_egress", rules_egress), + ] + ) + secgroup_get_config_result = boto_secgroup.get_config( + group_id=group.id, **conn_parameters + ) self.assertEqual(expected_get_config_result, secgroup_get_config_result) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_exists_true_name_classic(self): - ''' + """ tests 'true' existence of a group in EC2-Classic when given name - ''' + """ group_name = _random_group_name() - group_description = 'test_exists_true_ec2_classic' + group_description = "test_exists_true_ec2_classic" conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) group_classic = conn.create_security_group(group_name, group_description) - group_vpc = conn.create_security_group(group_name, group_description, vpc_id=vpc_id) + group_vpc = conn.create_security_group( + group_name, group_description, vpc_id=vpc_id + ) salt_exists_result = boto_secgroup.exists(name=group_name, **conn_parameters) self.assertTrue(salt_exists_result) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_exists_false_name_classic(self): pass - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_exists_true_name_vpc(self): - ''' + """ tests 'true' existence of a group in EC2-VPC when given name and vpc_id - ''' + """ group_name = _random_group_name() - group_description = 'test_exists_true_ec2_vpc' + group_description = "test_exists_true_ec2_vpc" conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) conn.create_security_group(group_name, group_description, vpc_id=vpc_id) - salt_exists_result = boto_secgroup.exists(name=group_name, vpc_id=vpc_id, **conn_parameters) + salt_exists_result = boto_secgroup.exists( + name=group_name, vpc_id=vpc_id, **conn_parameters + ) self.assertTrue(salt_exists_result) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_exists_false_name_vpc(self): - ''' + """ tests 'false' existence of a group in vpc when given name and vpc_id - ''' + """ group_name = _random_group_name() - salt_exists_result = boto_secgroup.exists(group_name, vpc_id=vpc_id, **conn_parameters) + salt_exists_result = boto_secgroup.exists( + group_name, vpc_id=vpc_id, **conn_parameters + ) self.assertFalse(salt_exists_result) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_exists_true_group_id(self): - ''' + """ tests 'true' existence of a group when given group_id - ''' + """ group_name = _random_group_name() - group_description = 'test_exists_true_group_id' + group_description = "test_exists_true_group_id" conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) group = conn.create_security_group(group_name, group_description) salt_exists_result = boto_secgroup.exists(group_id=group.id, **conn_parameters) self.assertTrue(salt_exists_result) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_exists_false_group_id(self): - ''' + """ tests 'false' existence of a group when given group_id - ''' + """ group_id = _random_group_id() salt_exists_result = boto_secgroup.exists(group_id=group_id, **conn_parameters) self.assertFalse(salt_exists_result) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_delete_group_ec2_classic(self): - ''' + """ test deletion of a group in EC2-Classic. Test does the following: 1. creates two groups, in EC2-Classic and one in EC2-VPC 2. saves the group_ids to group_ids_pre_delete @@ -305,13 +432,17 @@ class BotoSecgroupTestCase(TestCase, LoaderModuleMockMixin): 4. saves the group ids of groups to group_ids_post_delete 5. compares the group_ids_pre_delete and group_ids_post_delete lists to ensure that the correct group was deleted - ''' + """ group_name = _random_group_name() - group_description = 'test_delete_group_ec2_classic' + group_description = "test_delete_group_ec2_classic" # create two groups using boto, one in EC2-Classic and one in EC2-VPC conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) - group_classic = conn.create_security_group(name=group_name, description=group_description) - group_vpc = conn.create_security_group(name=group_name, description=group_description, vpc_id=vpc_id) + group_classic = conn.create_security_group( + name=group_name, description=group_description + ) + group_vpc = conn.create_security_group( + name=group_name, description=group_description, vpc_id=vpc_id + ) # creates a list of all the existing all_group_ids in an AWS account all_groups = [group.id for group in conn.get_all_security_groups()] # removes the EC2-Classic Security Group @@ -321,17 +452,23 @@ class BotoSecgroupTestCase(TestCase, LoaderModuleMockMixin): actual_groups = [group.id for group in conn.get_all_security_groups()] self.assertEqual(expected_groups, actual_groups) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_delete_group_name_ec2_vpc(self): pass - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test__get_conn_true(self): - ''' + """ tests ensures that _get_conn returns an boto.ec2.connection.EC2Connection object. - ''' + """ conn = boto.ec2.connect_to_region(region, **boto_conn_parameters) salt_conn = boto_secgroup._get_conn(**conn_parameters) self.assertEqual(conn.__class__, salt_conn.__class__) diff --git a/tests/unit/modules/test_boto_vpc.py b/tests/unit/modules/test_boto_vpc.py index 2c147d029f0..e3f8ea6e0d8 100644 --- a/tests/unit/modules/test_boto_vpc.py +++ b/tests/unit/modules/test_boto_vpc.py @@ -5,39 +5,49 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import os.path import random import string -import os.path import sys + # pylint: disable=3rd-party-module-not-gated import pkg_resources from pkg_resources import DistributionNotFound -# pylint: enable=3rd-party-module-not-gated - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch -from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.config import salt.loader import salt.modules.boto_vpc as boto_vpc -from salt.utils.versions import LooseVersion -from salt.exceptions import SaltInvocationError, CommandExecutionError -from salt.modules.boto_vpc import _maybe_set_name_tag, _maybe_set_tags +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import 3rd-party libs from salt.ext import six + # pylint: disable=import-error from salt.ext.six.moves import range # pylint: disable=redefined-builtin +from salt.modules.boto_vpc import _maybe_set_name_tag, _maybe_set_tags +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +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, skipIf + +# pylint: enable=3rd-party-module-not-gated + + # pylint: disable=no-name-in-module,unused-import try: import boto - boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') + + boto.ENDPOINTS_PATH = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/endpoints.json" + ) import boto3 from boto.exception import BotoServerError + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -45,46 +55,59 @@ except ImportError: try: import moto from moto import mock_ec2_deprecated + HAS_MOTO = True except ImportError: HAS_MOTO = False def mock_ec2_deprecated(self): - ''' + """ if the mock_ec2_deprecated function is not available due to import failure this replaces the decorated function with stub_function. Allows boto_vpc unit tests to use the @mock_ec2_deprecated decorator without a "NameError: name 'mock_ec2_deprecated' is not defined" error. - ''' + """ def stub_function(self): pass return stub_function + + # pylint: enable=import-error,no-name-in-module,unused-import # the boto_vpc module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto_version = '2.8.0' -required_moto_version = '1.0.0' +required_boto_version = "2.8.0" +required_moto_version = "1.0.0" -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} -cidr_block = '10.0.0.0/24' -dhcp_options_parameters = {'domain_name': 'example.com', 'domain_name_servers': ['1.2.3.4'], 'ntp_servers': ['5.6.7.8'], - 'netbios_name_servers': ['10.0.0.1'], 'netbios_node_type': 2} -network_acl_entry_parameters = ('fake', 100, -1, 'allow', cidr_block) +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, +} +cidr_block = "10.0.0.0/24" +dhcp_options_parameters = { + "domain_name": "example.com", + "domain_name_servers": ["1.2.3.4"], + "ntp_servers": ["5.6.7.8"], + "netbios_name_servers": ["10.0.0.1"], + "netbios_node_type": 2, +} +network_acl_entry_parameters = ("fake", 100, -1, "allow", cidr_block) dhcp_options_parameters.update(conn_parameters) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto.__version__) < LooseVersion(required_boto_version): @@ -94,32 +117,32 @@ def _has_required_boto(): def _get_boto_version(): - ''' + """ Returns the boto version - ''' + """ if not HAS_BOTO: return False return LooseVersion(boto.__version__) def _get_moto_version(): - ''' + """ Returns the moto version - ''' + """ try: return LooseVersion(six.text_type(moto.__version__)) except AttributeError: try: - return LooseVersion(pkg_resources.get_distribution('moto').version) + return LooseVersion(pkg_resources.get_distribution("moto").version) except DistributionNotFound: return False def _has_required_moto(): - ''' + """ Returns True/False boolean depending on if Moto is installed and correct version. - ''' + """ if not HAS_MOTO: return False else: @@ -128,41 +151,51 @@ def _has_required_moto(): return True -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {}. Installed: {}' - .format(required_boto_version, _get_boto_version() if HAS_BOTO else 'None')) -@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {}. Installed: {}' - .format(required_moto_version, _get_moto_version() if HAS_MOTO else 'None')) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {}. Installed: {}".format( + required_boto_version, _get_boto_version() if HAS_BOTO else "None" + ), +) +@skipIf( + _has_required_moto() is False, + "The moto version must be >= to version {}. Installed: {}".format( + required_moto_version, _get_moto_version() if HAS_MOTO else "None" + ), +) class BotoVpcTestCaseBase(TestCase, LoaderModuleMockMixin): conn3 = None def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils( - opts, - whitelist=['boto', 'boto3', 'args', 'systemd', 'path', 'platform']) - return {boto_vpc: {'__utils__': utils}} + opts, whitelist=["boto", "boto3", "args", "systemd", "path", "platform"] + ) + return {boto_vpc: {"__utils__": utils}} # Set up MagicMock to replace the boto3 session def setUp(self): super(BotoVpcTestCaseBase, self).setUp() boto_vpc.__init__(self.opts) - delattr(self, 'opts') + delattr(self, "opts") # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn3 = MagicMock() - self.addCleanup(delattr, self, 'conn3') + self.addCleanup(delattr, self, "conn3") session_instance.client.return_value = self.conn3 @@ -170,9 +203,9 @@ class BotoVpcTestCaseMixin(object): conn = None def _create_vpc(self, name=None, tags=None): - ''' + """ Helper function to create a test vpc - ''' + """ if not self.conn: self.conn = boto.vpc.connect_to_region(region) @@ -182,22 +215,31 @@ class BotoVpcTestCaseMixin(object): _maybe_set_tags(tags, vpc) return vpc - def _create_subnet(self, vpc_id, cidr_block='10.0.0.0/25', name=None, tags=None, availability_zone=None): - ''' + def _create_subnet( + self, + vpc_id, + cidr_block="10.0.0.0/25", + name=None, + tags=None, + availability_zone=None, + ): + """ Helper function to create a test subnet - ''' + """ if not self.conn: self.conn = boto.vpc.connect_to_region(region) - subnet = self.conn.create_subnet(vpc_id, cidr_block, availability_zone=availability_zone) + subnet = self.conn.create_subnet( + vpc_id, cidr_block, availability_zone=availability_zone + ) _maybe_set_name_tag(name, subnet) _maybe_set_tags(tags, subnet) return subnet def _create_internet_gateway(self, vpc_id, name=None, tags=None): - ''' + """ Helper function to create a test internet gateway - ''' + """ if not self.conn: self.conn = boto.vpc.connect_to_region(region) @@ -207,9 +249,9 @@ class BotoVpcTestCaseMixin(object): return igw def _create_customer_gateway(self, vpc_id, name=None, tags=None): - ''' + """ Helper function to create a test customer gateway - ''' + """ if not self.conn: self.conn = boto.vpc.connect_to_region(region) @@ -218,52 +260,80 @@ class BotoVpcTestCaseMixin(object): _maybe_set_tags(tags, gw) return gw - def _create_dhcp_options(self, domain_name='example.com', domain_name_servers=None, ntp_servers=None, - netbios_name_servers=None, netbios_node_type=2): - ''' + def _create_dhcp_options( + self, + domain_name="example.com", + domain_name_servers=None, + ntp_servers=None, + netbios_name_servers=None, + netbios_node_type=2, + ): + """ Helper function to create test dchp options - ''' + """ if not netbios_name_servers: - netbios_name_servers = ['10.0.0.1'] + netbios_name_servers = ["10.0.0.1"] if not ntp_servers: - ntp_servers = ['5.6.7.8'] + ntp_servers = ["5.6.7.8"] if not domain_name_servers: - domain_name_servers = ['1.2.3.4'] + domain_name_servers = ["1.2.3.4"] if not self.conn: self.conn = boto.vpc.connect_to_region(region) - return self.conn.create_dhcp_options(domain_name=domain_name, domain_name_servers=domain_name_servers, - ntp_servers=ntp_servers, netbios_name_servers=netbios_name_servers, - netbios_node_type=netbios_node_type) + return self.conn.create_dhcp_options( + domain_name=domain_name, + domain_name_servers=domain_name_servers, + ntp_servers=ntp_servers, + netbios_name_servers=netbios_name_servers, + netbios_node_type=netbios_node_type, + ) def _create_network_acl(self, vpc_id): - ''' + """ Helper function to create test network acl - ''' + """ if not self.conn: self.conn = boto.vpc.connect_to_region(region) return self.conn.create_network_acl(vpc_id) - def _create_network_acl_entry(self, network_acl_id, rule_number, protocol, rule_action, cidr_block, egress=None, - icmp_code=None, icmp_type=None, port_range_from=None, port_range_to=None): - ''' + def _create_network_acl_entry( + self, + network_acl_id, + rule_number, + protocol, + rule_action, + cidr_block, + egress=None, + icmp_code=None, + icmp_type=None, + port_range_from=None, + port_range_to=None, + ): + """ Helper function to create test network acl entry - ''' + """ if not self.conn: self.conn = boto.vpc.connect_to_region(region) - return self.conn.create_network_acl_entry(network_acl_id, rule_number, protocol, rule_action, - cidr_block, - egress=egress, - icmp_code=icmp_code, icmp_type=icmp_type, - port_range_from=port_range_from, port_range_to=port_range_to) + return self.conn.create_network_acl_entry( + network_acl_id, + rule_number, + protocol, + rule_action, + cidr_block, + egress=egress, + icmp_code=icmp_code, + icmp_type=icmp_type, + port_range_from=port_range_from, + port_range_to=port_range_to, + ) def _create_route_table(self, vpc_id, name=None, tags=None): - ''' + """ Helper function to create a test route table - ''' + """ if not self.conn: self.conn = boto.vpc.connect_to_region(region) @@ -273,242 +343,290 @@ class BotoVpcTestCaseMixin(object): return rtbl -@skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') +@skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", +) class BotoVpcTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_vpc module - ''' + """ @mock_ec2_deprecated - def test_that_when_checking_if_a_vpc_exists_by_id_and_a_vpc_exists_the_vpc_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_vpc_exists_by_id_and_a_vpc_exists_the_vpc_exists_method_returns_true( + self, + ): + """ Tests checking vpc existence via id when the vpc already exists - ''' + """ vpc = self._create_vpc() vpc_exists_result = boto_vpc.exists(vpc_id=vpc.id, **conn_parameters) - self.assertTrue(vpc_exists_result['exists']) + self.assertTrue(vpc_exists_result["exists"]) @mock_ec2_deprecated def test_that_when_checking_if_a_vpc_exists_by_id_and_a_vpc_does_not_exist_the_vpc_exists_method_returns_false( - self): - ''' + self, + ): + """ Tests checking vpc existence via id when the vpc does not exist - ''' + """ self._create_vpc() # Created to ensure that the filters are applied correctly - vpc_exists_result = boto_vpc.exists(vpc_id='fake', **conn_parameters) + vpc_exists_result = boto_vpc.exists(vpc_id="fake", **conn_parameters) - self.assertFalse(vpc_exists_result['exists']) + self.assertFalse(vpc_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_checking_if_a_vpc_exists_by_name_and_a_vpc_exists_the_vpc_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_vpc_exists_by_name_and_a_vpc_exists_the_vpc_exists_method_returns_true( + self, + ): + """ Tests checking vpc existence via name when vpc exists - ''' - self._create_vpc(name='test') + """ + self._create_vpc(name="test") - vpc_exists_result = boto_vpc.exists(name='test', **conn_parameters) + vpc_exists_result = boto_vpc.exists(name="test", **conn_parameters) - self.assertTrue(vpc_exists_result['exists']) + self.assertTrue(vpc_exists_result["exists"]) @mock_ec2_deprecated def test_that_when_checking_if_a_vpc_exists_by_name_and_a_vpc_does_not_exist_the_vpc_exists_method_returns_false( - self): - ''' + self, + ): + """ Tests checking vpc existence via name when vpc does not exist - ''' + """ self._create_vpc() # Created to ensure that the filters are applied correctly - vpc_exists_result = boto_vpc.exists(name='test', **conn_parameters) + vpc_exists_result = boto_vpc.exists(name="test", **conn_parameters) - self.assertFalse(vpc_exists_result['exists']) + self.assertFalse(vpc_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_checking_if_a_vpc_exists_by_tags_and_a_vpc_exists_the_vpc_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_vpc_exists_by_tags_and_a_vpc_exists_the_vpc_exists_method_returns_true( + self, + ): + """ Tests checking vpc existence via tag when vpc exists - ''' - self._create_vpc(tags={'test': 'testvalue'}) + """ + self._create_vpc(tags={"test": "testvalue"}) - vpc_exists_result = boto_vpc.exists(tags={'test': 'testvalue'}, **conn_parameters) + vpc_exists_result = boto_vpc.exists( + tags={"test": "testvalue"}, **conn_parameters + ) - self.assertTrue(vpc_exists_result['exists']) + self.assertTrue(vpc_exists_result["exists"]) @mock_ec2_deprecated def test_that_when_checking_if_a_vpc_exists_by_tags_and_a_vpc_does_not_exist_the_vpc_exists_method_returns_false( - self): - ''' + self, + ): + """ Tests checking vpc existence via tag when vpc does not exist - ''' + """ self._create_vpc() # Created to ensure that the filters are applied correctly - vpc_exists_result = boto_vpc.exists(tags={'test': 'testvalue'}, **conn_parameters) + vpc_exists_result = boto_vpc.exists( + tags={"test": "testvalue"}, **conn_parameters + ) - self.assertFalse(vpc_exists_result['exists']) + self.assertFalse(vpc_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_checking_if_a_vpc_exists_by_cidr_and_a_vpc_exists_the_vpc_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_vpc_exists_by_cidr_and_a_vpc_exists_the_vpc_exists_method_returns_true( + self, + ): + """ Tests checking vpc existence via cidr when vpc exists - ''' + """ self._create_vpc() - vpc_exists_result = boto_vpc.exists(cidr=u'10.0.0.0/24', **conn_parameters) + vpc_exists_result = boto_vpc.exists(cidr="10.0.0.0/24", **conn_parameters) - self.assertTrue(vpc_exists_result['exists']) + self.assertTrue(vpc_exists_result["exists"]) @mock_ec2_deprecated def test_that_when_checking_if_a_vpc_exists_by_cidr_and_a_vpc_does_not_exist_the_vpc_exists_method_returns_false( - self): - ''' + self, + ): + """ Tests checking vpc existence via cidr when vpc does not exist - ''' + """ self._create_vpc() # Created to ensure that the filters are applied correctly - vpc_exists_result = boto_vpc.exists(cidr=u'10.10.10.10/24', **conn_parameters) + vpc_exists_result = boto_vpc.exists(cidr="10.10.10.10/24", **conn_parameters) - self.assertFalse(vpc_exists_result['exists']) + self.assertFalse(vpc_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_checking_if_a_vpc_exists_but_providing_no_filters_the_vpc_exists_method_raises_a_salt_invocation_error(self): - ''' + def test_that_when_checking_if_a_vpc_exists_but_providing_no_filters_the_vpc_exists_method_raises_a_salt_invocation_error( + self, + ): + """ Tests checking vpc existence when no filters are provided - ''' - with self.assertRaisesRegex(SaltInvocationError, 'At least one of the following ' - 'must be provided: vpc_id, vpc_name, ' - 'cidr or tags.'): + """ + with self.assertRaisesRegex( + SaltInvocationError, + "At least one of the following " + "must be provided: vpc_id, vpc_name, " + "cidr or tags.", + ): boto_vpc.exists(**conn_parameters) @mock_ec2_deprecated def test_get_vpc_id_method_when_filtering_by_name(self): - ''' + """ Tests getting vpc id when filtering by name - ''' - vpc = self._create_vpc(name='test') + """ + vpc = self._create_vpc(name="test") - get_id_result = boto_vpc.get_id(name='test', **conn_parameters) + get_id_result = boto_vpc.get_id(name="test", **conn_parameters) - self.assertEqual(vpc.id, get_id_result['id']) + self.assertEqual(vpc.id, get_id_result["id"]) @mock_ec2_deprecated def test_get_vpc_id_method_when_filtering_by_invalid_name(self): - ''' + """ Tests getting vpc id when filtering by invalid name - ''' - self._create_vpc(name='test') + """ + self._create_vpc(name="test") - get_id_result = boto_vpc.get_id(name='test_fake', **conn_parameters) + get_id_result = boto_vpc.get_id(name="test_fake", **conn_parameters) - self.assertEqual(get_id_result['id'], None) + self.assertEqual(get_id_result["id"], None) @mock_ec2_deprecated def test_get_vpc_id_method_when_filtering_by_cidr(self): - ''' + """ Tests getting vpc id when filtering by cidr - ''' + """ vpc = self._create_vpc() - get_id_result = boto_vpc.get_id(cidr=u'10.0.0.0/24', **conn_parameters) + get_id_result = boto_vpc.get_id(cidr="10.0.0.0/24", **conn_parameters) - self.assertEqual(vpc.id, get_id_result['id']) + self.assertEqual(vpc.id, get_id_result["id"]) @mock_ec2_deprecated def test_get_vpc_id_method_when_filtering_by_invalid_cidr(self): - ''' + """ Tests getting vpc id when filtering by invalid cidr - ''' + """ self._create_vpc() - get_id_result = boto_vpc.get_id(cidr=u'10.10.10.10/24', **conn_parameters) + get_id_result = boto_vpc.get_id(cidr="10.10.10.10/24", **conn_parameters) - self.assertEqual(get_id_result['id'], None) + self.assertEqual(get_id_result["id"], None) @mock_ec2_deprecated def test_get_vpc_id_method_when_filtering_by_tags(self): - ''' + """ Tests getting vpc id when filtering by tags - ''' - vpc = self._create_vpc(tags={'test': 'testvalue'}) + """ + vpc = self._create_vpc(tags={"test": "testvalue"}) - get_id_result = boto_vpc.get_id(tags={'test': 'testvalue'}, **conn_parameters) + get_id_result = boto_vpc.get_id(tags={"test": "testvalue"}, **conn_parameters) - self.assertEqual(vpc.id, get_id_result['id']) + self.assertEqual(vpc.id, get_id_result["id"]) @mock_ec2_deprecated def test_get_vpc_id_method_when_filtering_by_invalid_tags(self): - ''' + """ Tests getting vpc id when filtering by invalid tags - ''' - self._create_vpc(tags={'test': 'testvalue'}) + """ + self._create_vpc(tags={"test": "testvalue"}) - get_id_result = boto_vpc.get_id(tags={'test': 'fake-testvalue'}, **conn_parameters) + get_id_result = boto_vpc.get_id( + tags={"test": "fake-testvalue"}, **conn_parameters + ) - self.assertEqual(get_id_result['id'], None) + self.assertEqual(get_id_result["id"], None) @mock_ec2_deprecated - def test_get_vpc_id_method_when_not_providing_filters_raises_a_salt_invocation_error(self): - ''' + def test_get_vpc_id_method_when_not_providing_filters_raises_a_salt_invocation_error( + self, + ): + """ Tests getting vpc id but providing no filters - ''' - with self.assertRaisesRegex(SaltInvocationError, 'At least one of the following must be provided: vpc_id, vpc_name, cidr or tags.'): + """ + with self.assertRaisesRegex( + SaltInvocationError, + "At least one of the following must be provided: vpc_id, vpc_name, cidr or tags.", + ): boto_vpc.get_id(**conn_parameters) @mock_ec2_deprecated - def test_get_vpc_id_method_when_more_than_one_vpc_is_matched_raises_a_salt_command_execution_error(self): - ''' + def test_get_vpc_id_method_when_more_than_one_vpc_is_matched_raises_a_salt_command_execution_error( + self, + ): + """ Tests getting vpc id but providing no filters - ''' - vpc1 = self._create_vpc(name='vpc-test1') - vpc2 = self._create_vpc(name='vpc-test2') + """ + vpc1 = self._create_vpc(name="vpc-test1") + vpc2 = self._create_vpc(name="vpc-test2") - with self.assertRaisesRegex(CommandExecutionError, 'Found more than one VPC matching the criteria.'): - boto_vpc.get_id(cidr=u'10.0.0.0/24', **conn_parameters) + with self.assertRaisesRegex( + CommandExecutionError, "Found more than one VPC matching the criteria." + ): + boto_vpc.get_id(cidr="10.0.0.0/24", **conn_parameters) @mock_ec2_deprecated def test_that_when_creating_a_vpc_succeeds_the_create_vpc_method_returns_true(self): - ''' + """ tests True VPC created. - ''' + """ vpc_creation_result = boto_vpc.create(cidr_block, **conn_parameters) self.assertTrue(vpc_creation_result) @mock_ec2_deprecated - def test_that_when_creating_a_vpc_and_specifying_a_vpc_name_succeeds_the_create_vpc_method_returns_true(self): - ''' + def test_that_when_creating_a_vpc_and_specifying_a_vpc_name_succeeds_the_create_vpc_method_returns_true( + self, + ): + """ tests True VPC created. - ''' - vpc_creation_result = boto_vpc.create(cidr_block, vpc_name='test', **conn_parameters) + """ + vpc_creation_result = boto_vpc.create( + cidr_block, vpc_name="test", **conn_parameters + ) self.assertTrue(vpc_creation_result) @mock_ec2_deprecated - def test_that_when_creating_a_vpc_and_specifying_tags_succeeds_the_create_vpc_method_returns_true(self): - ''' + def test_that_when_creating_a_vpc_and_specifying_tags_succeeds_the_create_vpc_method_returns_true( + self, + ): + """ tests True VPC created. - ''' - vpc_creation_result = boto_vpc.create(cidr_block, tags={'test': 'value'}, **conn_parameters) + """ + vpc_creation_result = boto_vpc.create( + cidr_block, tags={"test": "value"}, **conn_parameters + ) self.assertTrue(vpc_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") def test_that_when_creating_a_vpc_fails_the_create_vpc_method_returns_false(self): - ''' + """ tests False VPC not created. - ''' - with patch('moto.ec2.models.VPCBackend.create_vpc', side_effect=BotoServerError(400, 'Mocked error')): + """ + with patch( + "moto.ec2.models.VPCBackend.create_vpc", + side_effect=BotoServerError(400, "Mocked error"), + ): vpc_creation_result = boto_vpc.create(cidr_block, **conn_parameters) - self.assertFalse(vpc_creation_result['created']) - self.assertTrue('error' in vpc_creation_result) + self.assertFalse(vpc_creation_result["created"]) + self.assertTrue("error" in vpc_creation_result) @mock_ec2_deprecated - def test_that_when_deleting_an_existing_vpc_the_delete_vpc_method_returns_true(self): - ''' + def test_that_when_deleting_an_existing_vpc_the_delete_vpc_method_returns_true( + self, + ): + """ Tests deleting an existing vpc - ''' + """ vpc = self._create_vpc() vpc_deletion_result = boto_vpc.delete(vpc.id, **conn_parameters) @@ -516,1311 +634,1663 @@ class BotoVpcTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): self.assertTrue(vpc_deletion_result) @mock_ec2_deprecated - def test_that_when_deleting_a_non_existent_vpc_the_delete_vpc_method_returns_false(self): - ''' + def test_that_when_deleting_a_non_existent_vpc_the_delete_vpc_method_returns_false( + self, + ): + """ Tests deleting a non-existent vpc - ''' - delete_vpc_result = boto_vpc.delete('1234', **conn_parameters) + """ + delete_vpc_result = boto_vpc.delete("1234", **conn_parameters) - self.assertFalse(delete_vpc_result['deleted']) + self.assertFalse(delete_vpc_result["deleted"]) @mock_ec2_deprecated - def test_that_when_describing_vpc_by_id_it_returns_the_dict_of_properties_returns_true(self): - ''' + def test_that_when_describing_vpc_by_id_it_returns_the_dict_of_properties_returns_true( + self, + ): + """ Tests describing parameters via vpc id if vpc exist - ''' + """ # With moto 0.4.25 through 0.4.30, is_default is set to True. # 0.4.24 and older and 0.4.31 and newer, is_default is False - if LooseVersion('0.4.25') <= _get_moto_version() < LooseVersion('0.4.31'): + if LooseVersion("0.4.25") <= _get_moto_version() < LooseVersion("0.4.31"): is_default = True else: is_default = False - vpc = self._create_vpc(name='test', tags={'test': 'testvalue'}) + vpc = self._create_vpc(name="test", tags={"test": "testvalue"}) describe_vpc = boto_vpc.describe(vpc_id=vpc.id, **conn_parameters) - vpc_properties = dict(id=vpc.id, - cidr_block=six.text_type(cidr_block), - is_default=is_default, - state=u'available', - tags={u'Name': u'test', u'test': u'testvalue'}, - dhcp_options_id=u'dopt-7a8b9c2d', - region=u'us-east-1', - instance_tenancy=u'default') + vpc_properties = dict( + id=vpc.id, + cidr_block=six.text_type(cidr_block), + is_default=is_default, + state="available", + tags={"Name": "test", "test": "testvalue"}, + dhcp_options_id="dopt-7a8b9c2d", + region="us-east-1", + instance_tenancy="default", + ) - self.assertEqual(describe_vpc, {'vpc': vpc_properties}) + self.assertEqual(describe_vpc, {"vpc": vpc_properties}) @mock_ec2_deprecated - def test_that_when_describing_vpc_by_id_it_returns_the_dict_of_properties_returns_false(self): - ''' + def test_that_when_describing_vpc_by_id_it_returns_the_dict_of_properties_returns_false( + self, + ): + """ Tests describing parameters via vpc id if vpc does not exist - ''' - vpc = self._create_vpc(name='test', tags={'test': 'testvalue'}) + """ + vpc = self._create_vpc(name="test", tags={"test": "testvalue"}) - describe_vpc = boto_vpc.describe(vpc_id='vpc-fake', **conn_parameters) + describe_vpc = boto_vpc.describe(vpc_id="vpc-fake", **conn_parameters) - self.assertFalse(describe_vpc['vpc']) + self.assertFalse(describe_vpc["vpc"]) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") def test_that_when_describing_vpc_by_id_on_connection_error_it_returns_error(self): - ''' + """ Tests describing parameters failure - ''' - vpc = self._create_vpc(name='test', tags={'test': 'testvalue'}) + """ + vpc = self._create_vpc(name="test", tags={"test": "testvalue"}) - with patch('moto.ec2.models.VPCBackend.get_all_vpcs', - side_effect=BotoServerError(400, 'Mocked error')): + with patch( + "moto.ec2.models.VPCBackend.get_all_vpcs", + side_effect=BotoServerError(400, "Mocked error"), + ): describe_result = boto_vpc.describe(vpc_id=vpc.id, **conn_parameters) - self.assertTrue('error' in describe_result) + self.assertTrue("error" in describe_result) @mock_ec2_deprecated - def test_that_when_describing_vpc_but_providing_no_vpc_id_the_describe_method_raises_a_salt_invocation_error(self): - ''' + def test_that_when_describing_vpc_but_providing_no_vpc_id_the_describe_method_raises_a_salt_invocation_error( + self, + ): + """ Tests describing vpc without vpc id - ''' - with self.assertRaisesRegex(SaltInvocationError, - 'A valid vpc id or name needs to be specified.'): + """ + with self.assertRaisesRegex( + SaltInvocationError, "A valid vpc id or name needs to be specified." + ): boto_vpc.describe(vpc_id=None, **conn_parameters) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {}. Installed: {}' - .format(required_boto_version, _get_boto_version())) -@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {0}'.format(required_moto_version)) -@skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {}. Installed: {}".format( + required_boto_version, _get_boto_version() + ), +) +@skipIf( + _has_required_moto() is False, + "The moto version must be >= to version {0}".format(required_moto_version), +) +@skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", +) class BotoVpcSubnetsTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): @mock_ec2_deprecated def test_get_subnet_association_single_subnet(self): - ''' + """ tests that given multiple subnet ids in the same VPC that the VPC ID is returned. The test is valuable because it uses a string as an argument to subnets as opposed to a list. - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - subnet_association = boto_vpc.get_subnet_association(subnets=subnet.id, - **conn_parameters) - self.assertEqual(vpc.id, subnet_association['vpc_id']) + subnet_association = boto_vpc.get_subnet_association( + subnets=subnet.id, **conn_parameters + ) + self.assertEqual(vpc.id, subnet_association["vpc_id"]) @mock_ec2_deprecated def test_get_subnet_association_multiple_subnets_same_vpc(self): - ''' + """ tests that given multiple subnet ids in the same VPC that the VPC ID is returned. - ''' + """ vpc = self._create_vpc() - subnet_a = self._create_subnet(vpc.id, '10.0.0.0/25') - subnet_b = self._create_subnet(vpc.id, '10.0.0.128/25') - subnet_association = boto_vpc.get_subnet_association([subnet_a.id, subnet_b.id], - **conn_parameters) - self.assertEqual(vpc.id, subnet_association['vpc_id']) + subnet_a = self._create_subnet(vpc.id, "10.0.0.0/25") + subnet_b = self._create_subnet(vpc.id, "10.0.0.128/25") + subnet_association = boto_vpc.get_subnet_association( + [subnet_a.id, subnet_b.id], **conn_parameters + ) + self.assertEqual(vpc.id, subnet_association["vpc_id"]) @mock_ec2_deprecated def test_get_subnet_association_multiple_subnets_different_vpc(self): - ''' + """ tests that given multiple subnet ids in different VPCs that False is returned. - ''' + """ vpc_a = self._create_vpc() vpc_b = self.conn.create_vpc(cidr_block) - subnet_a = self._create_subnet(vpc_a.id, '10.0.0.0/24') - subnet_b = self._create_subnet(vpc_b.id, '10.0.0.0/24') - subnet_association = boto_vpc.get_subnet_association([subnet_a.id, subnet_b.id], - **conn_parameters) - self.assertEqual(set(subnet_association['vpc_ids']), set([vpc_a.id, vpc_b.id])) + subnet_a = self._create_subnet(vpc_a.id, "10.0.0.0/24") + subnet_b = self._create_subnet(vpc_b.id, "10.0.0.0/24") + subnet_association = boto_vpc.get_subnet_association( + [subnet_a.id, subnet_b.id], **conn_parameters + ) + self.assertEqual(set(subnet_association["vpc_ids"]), set([vpc_a.id, vpc_b.id])) @mock_ec2_deprecated - def test_that_when_creating_a_subnet_succeeds_the_create_subnet_method_returns_true(self): - ''' + def test_that_when_creating_a_subnet_succeeds_the_create_subnet_method_returns_true( + self, + ): + """ Tests creating a subnet successfully - ''' + """ vpc = self._create_vpc() - subnet_creation_result = boto_vpc.create_subnet(vpc.id, '10.0.0.0/24', **conn_parameters) + subnet_creation_result = boto_vpc.create_subnet( + vpc.id, "10.0.0.0/24", **conn_parameters + ) - self.assertTrue(subnet_creation_result['created']) - self.assertTrue('id' in subnet_creation_result) + self.assertTrue(subnet_creation_result["created"]) + self.assertTrue("id" in subnet_creation_result) @mock_ec2_deprecated - def test_that_when_creating_a_subnet_and_specifying_a_name_succeeds_the_create_subnet_method_returns_true(self): - ''' + def test_that_when_creating_a_subnet_and_specifying_a_name_succeeds_the_create_subnet_method_returns_true( + self, + ): + """ Tests creating a subnet successfully when specifying a name - ''' + """ vpc = self._create_vpc() - subnet_creation_result = boto_vpc.create_subnet(vpc.id, '10.0.0.0/24', subnet_name='test', **conn_parameters) + subnet_creation_result = boto_vpc.create_subnet( + vpc.id, "10.0.0.0/24", subnet_name="test", **conn_parameters + ) - self.assertTrue(subnet_creation_result['created']) + self.assertTrue(subnet_creation_result["created"]) @mock_ec2_deprecated - def test_that_when_creating_a_subnet_and_specifying_tags_succeeds_the_create_subnet_method_returns_true(self): - ''' + def test_that_when_creating_a_subnet_and_specifying_tags_succeeds_the_create_subnet_method_returns_true( + self, + ): + """ Tests creating a subnet successfully when specifying a tag - ''' + """ vpc = self._create_vpc() - subnet_creation_result = boto_vpc.create_subnet(vpc.id, '10.0.0.0/24', tags={'test': 'testvalue'}, - **conn_parameters) + subnet_creation_result = boto_vpc.create_subnet( + vpc.id, "10.0.0.0/24", tags={"test": "testvalue"}, **conn_parameters + ) - self.assertTrue(subnet_creation_result['created']) + self.assertTrue(subnet_creation_result["created"]) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') - def test_that_when_creating_a_subnet_fails_the_create_subnet_method_returns_error(self): - ''' + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") + def test_that_when_creating_a_subnet_fails_the_create_subnet_method_returns_error( + self, + ): + """ Tests creating a subnet failure - ''' + """ vpc = self._create_vpc() - with patch('moto.ec2.models.SubnetBackend.create_subnet', side_effect=BotoServerError(400, 'Mocked error')): - subnet_creation_result = boto_vpc.create_subnet(vpc.id, '10.0.0.0/24', **conn_parameters) - self.assertTrue('error' in subnet_creation_result) + with patch( + "moto.ec2.models.SubnetBackend.create_subnet", + side_effect=BotoServerError(400, "Mocked error"), + ): + subnet_creation_result = boto_vpc.create_subnet( + vpc.id, "10.0.0.0/24", **conn_parameters + ) + self.assertTrue("error" in subnet_creation_result) @mock_ec2_deprecated - def test_that_when_deleting_an_existing_subnet_the_delete_subnet_method_returns_true(self): - ''' + def test_that_when_deleting_an_existing_subnet_the_delete_subnet_method_returns_true( + self, + ): + """ Tests deleting an existing subnet - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - subnet_deletion_result = boto_vpc.delete_subnet(subnet_id=subnet.id, **conn_parameters) + subnet_deletion_result = boto_vpc.delete_subnet( + subnet_id=subnet.id, **conn_parameters + ) - self.assertTrue(subnet_deletion_result['deleted']) + self.assertTrue(subnet_deletion_result["deleted"]) @mock_ec2_deprecated - def test_that_when_deleting_a_non_existent_subnet_the_delete_vpc_method_returns_false(self): - ''' + def test_that_when_deleting_a_non_existent_subnet_the_delete_vpc_method_returns_false( + self, + ): + """ Tests deleting a subnet that doesn't exist - ''' - delete_subnet_result = boto_vpc.delete_subnet(subnet_id='1234', **conn_parameters) - self.assertTrue('error' in delete_subnet_result) + """ + delete_subnet_result = boto_vpc.delete_subnet( + subnet_id="1234", **conn_parameters + ) + self.assertTrue("error" in delete_subnet_result) @mock_ec2_deprecated - def test_that_when_checking_if_a_subnet_exists_by_id_the_subnet_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_subnet_exists_by_id_the_subnet_exists_method_returns_true( + self, + ): + """ Tests checking if a subnet exists when it does exist - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - subnet_exists_result = boto_vpc.subnet_exists(subnet_id=subnet.id, **conn_parameters) + subnet_exists_result = boto_vpc.subnet_exists( + subnet_id=subnet.id, **conn_parameters + ) - self.assertTrue(subnet_exists_result['exists']) + self.assertTrue(subnet_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_a_subnet_does_not_exist_the_subnet_exists_method_returns_false(self): - ''' + def test_that_when_a_subnet_does_not_exist_the_subnet_exists_method_returns_false( + self, + ): + """ Tests checking if a subnet exists which doesn't exist - ''' - subnet_exists_result = boto_vpc.subnet_exists('fake', **conn_parameters) + """ + subnet_exists_result = boto_vpc.subnet_exists("fake", **conn_parameters) - self.assertFalse(subnet_exists_result['exists']) + self.assertFalse(subnet_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_checking_if_a_subnet_exists_by_name_the_subnet_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_subnet_exists_by_name_the_subnet_exists_method_returns_true( + self, + ): + """ Tests checking subnet existence by name - ''' + """ vpc = self._create_vpc() - self._create_subnet(vpc.id, name='test') + self._create_subnet(vpc.id, name="test") - subnet_exists_result = boto_vpc.subnet_exists(name='test', **conn_parameters) + subnet_exists_result = boto_vpc.subnet_exists(name="test", **conn_parameters) - self.assertTrue(subnet_exists_result['exists']) + self.assertTrue(subnet_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_checking_if_a_subnet_exists_by_name_the_subnet_does_not_exist_the_subnet_method_returns_false(self): - ''' + def test_that_when_checking_if_a_subnet_exists_by_name_the_subnet_does_not_exist_the_subnet_method_returns_false( + self, + ): + """ Tests checking subnet existence by name when it doesn't exist - ''' + """ vpc = self._create_vpc() self._create_subnet(vpc.id) - subnet_exists_result = boto_vpc.subnet_exists(name='test', **conn_parameters) + subnet_exists_result = boto_vpc.subnet_exists(name="test", **conn_parameters) - self.assertFalse(subnet_exists_result['exists']) + self.assertFalse(subnet_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_checking_if_a_subnet_exists_by_tags_the_subnet_exists_method_returns_true(self): - ''' + def test_that_when_checking_if_a_subnet_exists_by_tags_the_subnet_exists_method_returns_true( + self, + ): + """ Tests checking subnet existence by tag - ''' + """ vpc = self._create_vpc() - self._create_subnet(vpc.id, tags={'test': 'testvalue'}) + self._create_subnet(vpc.id, tags={"test": "testvalue"}) - subnet_exists_result = boto_vpc.subnet_exists(tags={'test': 'testvalue'}, **conn_parameters) + subnet_exists_result = boto_vpc.subnet_exists( + tags={"test": "testvalue"}, **conn_parameters + ) - self.assertTrue(subnet_exists_result['exists']) + self.assertTrue(subnet_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_checking_if_a_subnet_exists_by_tags_the_subnet_does_not_exist_the_subnet_method_returns_false(self): - ''' + def test_that_when_checking_if_a_subnet_exists_by_tags_the_subnet_does_not_exist_the_subnet_method_returns_false( + self, + ): + """ Tests checking subnet existence by tag when subnet doesn't exist - ''' + """ vpc = self._create_vpc() self._create_subnet(vpc.id) - subnet_exists_result = boto_vpc.subnet_exists(tags={'test': 'testvalue'}, **conn_parameters) + subnet_exists_result = boto_vpc.subnet_exists( + tags={"test": "testvalue"}, **conn_parameters + ) - self.assertFalse(subnet_exists_result['exists']) + self.assertFalse(subnet_exists_result["exists"]) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') - def test_that_when_checking_if_a_subnet_exists_but_providing_no_filters_the_subnet_exists_method_raises_a_salt_invocation_error(self): - ''' + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") + def test_that_when_checking_if_a_subnet_exists_but_providing_no_filters_the_subnet_exists_method_raises_a_salt_invocation_error( + self, + ): + """ Tests checking subnet existence without any filters - ''' - with self.assertRaisesRegex(SaltInvocationError, - 'At least one of the following must be specified: ' - 'subnet id, cidr, subnet_name, tags, or zones.'): + """ + with self.assertRaisesRegex( + SaltInvocationError, + "At least one of the following must be specified: " + "subnet id, cidr, subnet_name, tags, or zones.", + ): boto_vpc.subnet_exists(**conn_parameters) - @skipIf(True, 'Skip these tests while investigating failures') + @skipIf(True, "Skip these tests while investigating failures") @mock_ec2_deprecated def test_that_describe_subnet_by_id_for_existing_subnet_returns_correct_data(self): - ''' + """ Tests describing a subnet by id. - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - describe_subnet_results = boto_vpc.describe_subnet(region=region, - key=secret_key, - keyid=access_key, - subnet_id=subnet.id) - self.assertEqual(set(describe_subnet_results['subnet'].keys()), - set(['id', 'cidr_block', 'availability_zone', 'tags'])) + describe_subnet_results = boto_vpc.describe_subnet( + region=region, key=secret_key, keyid=access_key, subnet_id=subnet.id + ) + self.assertEqual( + set(describe_subnet_results["subnet"].keys()), + set(["id", "cidr_block", "availability_zone", "tags"]), + ) @mock_ec2_deprecated def test_that_describe_subnet_by_id_for_non_existent_subnet_returns_none(self): - ''' + """ Tests describing a non-existent subnet by id. - ''' + """ self._create_vpc() - describe_subnet_results = boto_vpc.describe_subnet(region=region, - key=secret_key, - keyid=access_key, - subnet_id='subnet-a1b2c3') - self.assertEqual(describe_subnet_results['subnet'], None) + describe_subnet_results = boto_vpc.describe_subnet( + region=region, key=secret_key, keyid=access_key, subnet_id="subnet-a1b2c3" + ) + self.assertEqual(describe_subnet_results["subnet"], None) - @skipIf(True, 'Skip these tests while investigating failures') + @skipIf(True, "Skip these tests while investigating failures") @mock_ec2_deprecated - def test_that_describe_subnet_by_name_for_existing_subnet_returns_correct_data(self): - ''' + def test_that_describe_subnet_by_name_for_existing_subnet_returns_correct_data( + self, + ): + """ Tests describing a subnet by name. - ''' + """ vpc = self._create_vpc() - self._create_subnet(vpc.id, name='test') + self._create_subnet(vpc.id, name="test") - describe_subnet_results = boto_vpc.describe_subnet(region=region, - key=secret_key, - keyid=access_key, - subnet_name='test') - self.assertEqual(set(describe_subnet_results['subnet'].keys()), - set(['id', 'cidr_block', 'availability_zone', 'tags'])) + describe_subnet_results = boto_vpc.describe_subnet( + region=region, key=secret_key, keyid=access_key, subnet_name="test" + ) + self.assertEqual( + set(describe_subnet_results["subnet"].keys()), + set(["id", "cidr_block", "availability_zone", "tags"]), + ) @mock_ec2_deprecated def test_that_describe_subnet_by_name_for_non_existent_subnet_returns_none(self): - ''' + """ Tests describing a non-existent subnet by id. - ''' + """ self._create_vpc() - describe_subnet_results = boto_vpc.describe_subnet(region=region, - key=secret_key, - keyid=access_key, - subnet_name='test') - self.assertEqual(describe_subnet_results['subnet'], None) + describe_subnet_results = boto_vpc.describe_subnet( + region=region, key=secret_key, keyid=access_key, subnet_name="test" + ) + self.assertEqual(describe_subnet_results["subnet"], None) - @skipIf(True, 'Skip these tests while investigating failures') + @skipIf(True, "Skip these tests while investigating failures") @mock_ec2_deprecated def test_that_describe_subnets_by_id_for_existing_subnet_returns_correct_data(self): - ''' + """ Tests describing multiple subnets by id. - ''' + """ vpc = self._create_vpc() subnet1 = self._create_subnet(vpc.id) subnet2 = self._create_subnet(vpc.id) - describe_subnet_results = boto_vpc.describe_subnets(region=region, - key=secret_key, - keyid=access_key, - subnet_ids=[subnet1.id, subnet2.id]) - self.assertEqual(len(describe_subnet_results['subnets']), 2) - self.assertEqual(set(describe_subnet_results['subnets'][0].keys()), - set(['id', 'cidr_block', 'availability_zone', 'tags'])) + describe_subnet_results = boto_vpc.describe_subnets( + region=region, + key=secret_key, + keyid=access_key, + subnet_ids=[subnet1.id, subnet2.id], + ) + self.assertEqual(len(describe_subnet_results["subnets"]), 2) + self.assertEqual( + set(describe_subnet_results["subnets"][0].keys()), + set(["id", "cidr_block", "availability_zone", "tags"]), + ) - @skipIf(True, 'Skip these tests while investigating failures') + @skipIf(True, "Skip these tests while investigating failures") @mock_ec2_deprecated - def test_that_describe_subnets_by_name_for_existing_subnets_returns_correct_data(self): - ''' + def test_that_describe_subnets_by_name_for_existing_subnets_returns_correct_data( + self, + ): + """ Tests describing multiple subnets by id. - ''' + """ vpc = self._create_vpc() - self._create_subnet(vpc.id, name='subnet1') - self._create_subnet(vpc.id, name='subnet2') + self._create_subnet(vpc.id, name="subnet1") + self._create_subnet(vpc.id, name="subnet2") - describe_subnet_results = boto_vpc.describe_subnets(region=region, - key=secret_key, - keyid=access_key, - subnet_names=['subnet1', 'subnet2']) - self.assertEqual(len(describe_subnet_results['subnets']), 2) - self.assertEqual(set(describe_subnet_results['subnets'][0].keys()), - set(['id', 'cidr_block', 'availability_zone', 'tags'])) + describe_subnet_results = boto_vpc.describe_subnets( + region=region, + key=secret_key, + keyid=access_key, + subnet_names=["subnet1", "subnet2"], + ) + self.assertEqual(len(describe_subnet_results["subnets"]), 2) + self.assertEqual( + set(describe_subnet_results["subnets"][0].keys()), + set(["id", "cidr_block", "availability_zone", "tags"]), + ) @mock_ec2_deprecated def test_create_subnet_passes_availability_zone(self): - ''' + """ Tests that the availability_zone kwarg is passed on to _create_resource - ''' + """ vpc = self._create_vpc() - self._create_subnet(vpc.id, name='subnet1', availability_zone='us-east-1a') - describe_subnet_results = boto_vpc.describe_subnets(region=region, - key=secret_key, - keyid=access_key, - subnet_names=['subnet1']) - self.assertEqual(describe_subnet_results['subnets'][0]['availability_zone'], 'us-east-1a') + self._create_subnet(vpc.id, name="subnet1", availability_zone="us-east-1a") + describe_subnet_results = boto_vpc.describe_subnets( + region=region, key=secret_key, keyid=access_key, subnet_names=["subnet1"] + ) + self.assertEqual( + describe_subnet_results["subnets"][0]["availability_zone"], "us-east-1a" + ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {}. Installed: {}' - .format(required_boto_version, _get_boto_version())) -@skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {}. Installed: {}".format( + required_boto_version, _get_boto_version() + ), +) +@skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", +) class BotoVpcInternetGatewayTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): @mock_ec2_deprecated - def test_that_when_creating_an_internet_gateway_the_create_internet_gateway_method_returns_true(self): - ''' + def test_that_when_creating_an_internet_gateway_the_create_internet_gateway_method_returns_true( + self, + ): + """ Tests creating an internet gateway successfully (with no vpc id or name) - ''' + """ - igw_creation_result = boto_vpc.create_internet_gateway(region=region, - key=secret_key, - keyid=access_key) - self.assertTrue(igw_creation_result.get('created')) + igw_creation_result = boto_vpc.create_internet_gateway( + region=region, key=secret_key, keyid=access_key + ) + self.assertTrue(igw_creation_result.get("created")) @mock_ec2_deprecated - def test_that_when_creating_an_internet_gateway_with_non_existent_vpc_the_create_internet_gateway_method_returns_an_error(self): - ''' + def test_that_when_creating_an_internet_gateway_with_non_existent_vpc_the_create_internet_gateway_method_returns_an_error( + self, + ): + """ Tests that creating an internet gateway for a non-existent VPC fails. - ''' + """ - igw_creation_result = boto_vpc.create_internet_gateway(region=region, - key=secret_key, - keyid=access_key, - vpc_name='non-existent-vpc') - self.assertTrue('error' in igw_creation_result) + igw_creation_result = boto_vpc.create_internet_gateway( + region=region, key=secret_key, keyid=access_key, vpc_name="non-existent-vpc" + ) + self.assertTrue("error" in igw_creation_result) @mock_ec2_deprecated - def test_that_when_creating_an_internet_gateway_with_vpc_name_specified_the_create_internet_gateway_method_returns_true(self): - ''' + def test_that_when_creating_an_internet_gateway_with_vpc_name_specified_the_create_internet_gateway_method_returns_true( + self, + ): + """ Tests creating an internet gateway with vpc name specified. - ''' + """ - self._create_vpc(name='test-vpc') + self._create_vpc(name="test-vpc") - igw_creation_result = boto_vpc.create_internet_gateway(region=region, - key=secret_key, - keyid=access_key, - vpc_name='test-vpc') + igw_creation_result = boto_vpc.create_internet_gateway( + region=region, key=secret_key, keyid=access_key, vpc_name="test-vpc" + ) - self.assertTrue(igw_creation_result.get('created')) + self.assertTrue(igw_creation_result.get("created")) @mock_ec2_deprecated - def test_that_when_creating_an_internet_gateway_with_vpc_id_specified_the_create_internet_gateway_method_returns_true(self): - ''' + def test_that_when_creating_an_internet_gateway_with_vpc_id_specified_the_create_internet_gateway_method_returns_true( + self, + ): + """ Tests creating an internet gateway with vpc name specified. - ''' + """ vpc = self._create_vpc() - igw_creation_result = boto_vpc.create_internet_gateway(region=region, - key=secret_key, - keyid=access_key, - vpc_id=vpc.id) + igw_creation_result = boto_vpc.create_internet_gateway( + region=region, key=secret_key, keyid=access_key, vpc_id=vpc.id + ) - self.assertTrue(igw_creation_result.get('created')) + self.assertTrue(igw_creation_result.get("created")) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {}. Installed: {}' - .format(required_boto_version, _get_boto_version())) -@skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {}. Installed: {}".format( + required_boto_version, _get_boto_version() + ), +) +@skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", +) class BotoVpcNatGatewayTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): @mock_ec2_deprecated - def test_that_when_creating_an_nat_gateway_the_create_nat_gateway_method_returns_true(self): - ''' + def test_that_when_creating_an_nat_gateway_the_create_nat_gateway_method_returns_true( + self, + ): + """ Tests creating an nat gateway successfully (with subnet_id specified) - ''' + """ vpc = self._create_vpc() - subnet = self._create_subnet(vpc.id, name='subnet1', availability_zone='us-east-1a') - ngw_creation_result = boto_vpc.create_nat_gateway(subnet_id=subnet.id, - region=region, - key=secret_key, - keyid=access_key) - self.assertTrue(ngw_creation_result.get('created')) + subnet = self._create_subnet( + vpc.id, name="subnet1", availability_zone="us-east-1a" + ) + ngw_creation_result = boto_vpc.create_nat_gateway( + subnet_id=subnet.id, region=region, key=secret_key, keyid=access_key + ) + self.assertTrue(ngw_creation_result.get("created")) @mock_ec2_deprecated - def test_that_when_creating_an_nat_gateway_with_non_existent_subnet_the_create_nat_gateway_method_returns_an_error(self): - ''' + def test_that_when_creating_an_nat_gateway_with_non_existent_subnet_the_create_nat_gateway_method_returns_an_error( + self, + ): + """ Tests that creating an nat gateway for a non-existent subnet fails. - ''' + """ - ngw_creation_result = boto_vpc.create_nat_gateway(region=region, - key=secret_key, - keyid=access_key, - subnet_name='non-existent-subnet') - self.assertTrue('error' in ngw_creation_result) + ngw_creation_result = boto_vpc.create_nat_gateway( + region=region, + key=secret_key, + keyid=access_key, + subnet_name="non-existent-subnet", + ) + self.assertTrue("error" in ngw_creation_result) @mock_ec2_deprecated - def test_that_when_creating_an_nat_gateway_with_subnet_name_specified_the_create_nat_gateway_method_returns_true(self): - ''' + def test_that_when_creating_an_nat_gateway_with_subnet_name_specified_the_create_nat_gateway_method_returns_true( + self, + ): + """ Tests creating an nat gateway with subnet name specified. - ''' + """ vpc = self._create_vpc() - subnet = self._create_subnet(vpc.id, name='test-subnet', availability_zone='us-east-1a') - ngw_creation_result = boto_vpc.create_nat_gateway(region=region, - key=secret_key, - keyid=access_key, - subnet_name='test-subnet') + subnet = self._create_subnet( + vpc.id, name="test-subnet", availability_zone="us-east-1a" + ) + ngw_creation_result = boto_vpc.create_nat_gateway( + region=region, key=secret_key, keyid=access_key, subnet_name="test-subnet" + ) - self.assertTrue(ngw_creation_result.get('created')) + self.assertTrue(ngw_creation_result.get("created")) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {}. Installed: {}' - .format(required_boto_version, _get_boto_version())) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {}. Installed: {}".format( + required_boto_version, _get_boto_version() + ), +) class BotoVpcCustomerGatewayTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_creating_a_customer_gateway_the_create_customer_gateway_method_returns_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_creating_a_customer_gateway_the_create_customer_gateway_method_returns_true( + self, + ): + """ Tests creating an internet gateway successfully (with no vpc id or name) - ''' + """ - gw_creation_result = boto_vpc.create_customer_gateway('ipsec.1', '10.1.1.1', None) - self.assertTrue(gw_creation_result.get('created')) + gw_creation_result = boto_vpc.create_customer_gateway( + "ipsec.1", "10.1.1.1", None + ) + self.assertTrue(gw_creation_result.get("created")) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_checking_if_a_subnet_exists_by_id_the_subnet_exists_method_returns_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_checking_if_a_subnet_exists_by_id_the_subnet_exists_method_returns_true( + self, + ): + """ Tests checking if a subnet exists when it does exist - ''' + """ - gw_creation_result = boto_vpc.create_customer_gateway('ipsec.1', '10.1.1.1', None) - gw_exists_result = boto_vpc.customer_gateway_exists(customer_gateway_id=gw_creation_result['id']) - self.assertTrue(gw_exists_result['exists']) + gw_creation_result = boto_vpc.create_customer_gateway( + "ipsec.1", "10.1.1.1", None + ) + gw_exists_result = boto_vpc.customer_gateway_exists( + customer_gateway_id=gw_creation_result["id"] + ) + self.assertTrue(gw_exists_result["exists"]) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_a_subnet_does_not_exist_the_subnet_exists_method_returns_false(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_a_subnet_does_not_exist_the_subnet_exists_method_returns_false( + self, + ): + """ Tests checking if a subnet exists which doesn't exist - ''' - gw_exists_result = boto_vpc.customer_gateway_exists('fake') - self.assertFalse(gw_exists_result['exists']) + """ + gw_exists_result = boto_vpc.customer_gateway_exists("fake") + self.assertFalse(gw_exists_result["exists"]) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {}. Installed: {}' - .format(required_boto_version, _get_boto_version())) -@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {0}'.format(required_moto_version)) -@skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {}. Installed: {}".format( + required_boto_version, _get_boto_version() + ), +) +@skipIf( + _has_required_moto() is False, + "The moto version must be >= to version {0}".format(required_moto_version), +) +@skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", +) class BotoVpcDHCPOptionsTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): @mock_ec2_deprecated - def test_that_when_creating_dhcp_options_succeeds_the_create_dhcp_options_method_returns_true(self): - ''' + def test_that_when_creating_dhcp_options_succeeds_the_create_dhcp_options_method_returns_true( + self, + ): + """ Tests creating dhcp options successfully - ''' - dhcp_options_creation_result = boto_vpc.create_dhcp_options(**dhcp_options_parameters) + """ + dhcp_options_creation_result = boto_vpc.create_dhcp_options( + **dhcp_options_parameters + ) - self.assertTrue(dhcp_options_creation_result['created']) + self.assertTrue(dhcp_options_creation_result["created"]) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_creating_dhcp_options_and_specifying_a_name_succeeds_the_create_dhcp_options_method_returns_true( - self): - ''' + self, + ): + """ Tests creating dchp options with name successfully - ''' - dhcp_options_creation_result = boto_vpc.create_dhcp_options(dhcp_options_name='test', - **dhcp_options_parameters) + """ + dhcp_options_creation_result = boto_vpc.create_dhcp_options( + dhcp_options_name="test", **dhcp_options_parameters + ) - self.assertTrue(dhcp_options_creation_result['created']) + self.assertTrue(dhcp_options_creation_result["created"]) @mock_ec2_deprecated def test_that_when_creating_dhcp_options_and_specifying_tags_succeeds_the_create_dhcp_options_method_returns_true( - self): - ''' + self, + ): + """ Tests creating dchp options with tag successfully - ''' - dhcp_options_creation_result = boto_vpc.create_dhcp_options(tags={'test': 'testvalue'}, - **dhcp_options_parameters) + """ + dhcp_options_creation_result = boto_vpc.create_dhcp_options( + tags={"test": "testvalue"}, **dhcp_options_parameters + ) - self.assertTrue(dhcp_options_creation_result['created']) + self.assertTrue(dhcp_options_creation_result["created"]) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') - def test_that_when_creating_dhcp_options_fails_the_create_dhcp_options_method_returns_error(self): - ''' + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") + def test_that_when_creating_dhcp_options_fails_the_create_dhcp_options_method_returns_error( + self, + ): + """ Tests creating dhcp options failure - ''' - with patch('moto.ec2.models.DHCPOptionsSetBackend.create_dhcp_options', - side_effect=BotoServerError(400, 'Mocked error')): - r = dhcp_options_creation_result = boto_vpc.create_dhcp_options(**dhcp_options_parameters) - self.assertTrue('error' in r) + """ + with patch( + "moto.ec2.models.DHCPOptionsSetBackend.create_dhcp_options", + side_effect=BotoServerError(400, "Mocked error"), + ): + r = dhcp_options_creation_result = boto_vpc.create_dhcp_options( + **dhcp_options_parameters + ) + self.assertTrue("error" in r) @mock_ec2_deprecated def test_that_when_associating_an_existing_dhcp_options_set_to_an_existing_vpc_the_associate_dhcp_options_method_returns_true( - self): - ''' + self, + ): + """ Tests associating existing dchp options successfully - ''' + """ vpc = self._create_vpc() dhcp_options = self._create_dhcp_options() - dhcp_options_association_result = boto_vpc.associate_dhcp_options_to_vpc(dhcp_options.id, vpc.id, - **conn_parameters) + dhcp_options_association_result = boto_vpc.associate_dhcp_options_to_vpc( + dhcp_options.id, vpc.id, **conn_parameters + ) - self.assertTrue(dhcp_options_association_result['associated']) + self.assertTrue(dhcp_options_association_result["associated"]) @mock_ec2_deprecated def test_that_when_associating_a_non_existent_dhcp_options_set_to_an_existing_vpc_the_associate_dhcp_options_method_returns_error( - self): - ''' + self, + ): + """ Tests associating non-existanct dhcp options successfully - ''' + """ vpc = self._create_vpc() - dhcp_options_association_result = boto_vpc.associate_dhcp_options_to_vpc('fake', vpc.id, **conn_parameters) + dhcp_options_association_result = boto_vpc.associate_dhcp_options_to_vpc( + "fake", vpc.id, **conn_parameters + ) - self.assertTrue('error' in dhcp_options_association_result) + self.assertTrue("error" in dhcp_options_association_result) @mock_ec2_deprecated def test_that_when_associating_an_existing_dhcp_options_set_to_a_non_existent_vpc_the_associate_dhcp_options_method_returns_false( - self): - ''' + self, + ): + """ Tests associating existing dhcp options to non-existence vpc - ''' + """ dhcp_options = self._create_dhcp_options() - dhcp_options_association_result = boto_vpc.associate_dhcp_options_to_vpc(dhcp_options.id, 'fake', - **conn_parameters) + dhcp_options_association_result = boto_vpc.associate_dhcp_options_to_vpc( + dhcp_options.id, "fake", **conn_parameters + ) - self.assertTrue('error' in dhcp_options_association_result) + self.assertTrue("error" in dhcp_options_association_result) @mock_ec2_deprecated def test_that_when_creating_dhcp_options_set_to_an_existing_vpc_succeeds_the_associate_new_dhcp_options_method_returns_true( - self): - ''' + self, + ): + """ Tests creation/association of dchp options to an existing vpc successfully - ''' + """ vpc = self._create_vpc() - dhcp_creation_result = boto_vpc.create_dhcp_options(vpc_id=vpc.id, **dhcp_options_parameters) + dhcp_creation_result = boto_vpc.create_dhcp_options( + vpc_id=vpc.id, **dhcp_options_parameters + ) - self.assertTrue(dhcp_creation_result['created']) + self.assertTrue(dhcp_creation_result["created"]) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") def test_that_when_creating_and_associating_dhcp_options_set_to_an_existing_vpc_fails_creating_the_dhcp_options_the_associate_new_dhcp_options_method_raises_exception( - self): - ''' + self, + ): + """ Tests creation failure during creation/association of dchp options to an existing vpc - ''' + """ vpc = self._create_vpc() - with patch('moto.ec2.models.DHCPOptionsSetBackend.create_dhcp_options', - side_effect=BotoServerError(400, 'Mocked error')): - r = boto_vpc.associate_new_dhcp_options_to_vpc(vpc.id, **dhcp_options_parameters) - self.assertTrue('error' in r) + with patch( + "moto.ec2.models.DHCPOptionsSetBackend.create_dhcp_options", + side_effect=BotoServerError(400, "Mocked error"), + ): + r = boto_vpc.associate_new_dhcp_options_to_vpc( + vpc.id, **dhcp_options_parameters + ) + self.assertTrue("error" in r) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') - def test_that_when_creating_and_associating_dhcp_options_set_to_an_existing_vpc_fails_associating_the_dhcp_options_the_associate_new_dhcp_options_method_raises_exception(self): - ''' + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") + def test_that_when_creating_and_associating_dhcp_options_set_to_an_existing_vpc_fails_associating_the_dhcp_options_the_associate_new_dhcp_options_method_raises_exception( + self, + ): + """ Tests association failure during creation/association of dchp options to existing vpc - ''' + """ vpc = self._create_vpc() - with patch('moto.ec2.models.DHCPOptionsSetBackend.associate_dhcp_options', - side_effect=BotoServerError(400, 'Mocked error')): - r = boto_vpc.associate_new_dhcp_options_to_vpc(vpc.id, **dhcp_options_parameters) - self.assertTrue('error' in r) + with patch( + "moto.ec2.models.DHCPOptionsSetBackend.associate_dhcp_options", + side_effect=BotoServerError(400, "Mocked error"), + ): + r = boto_vpc.associate_new_dhcp_options_to_vpc( + vpc.id, **dhcp_options_parameters + ) + self.assertTrue("error" in r) @mock_ec2_deprecated def test_that_when_creating_dhcp_options_set_to_a_non_existent_vpc_the_dhcp_options_the_associate_new_dhcp_options_method_returns_false( - self): - ''' + self, + ): + """ Tests creation/association of dhcp options to non-existent vpc - ''' + """ - r = boto_vpc.create_dhcp_options(vpc_name='fake', **dhcp_options_parameters) - self.assertTrue('error' in r) + r = boto_vpc.create_dhcp_options(vpc_name="fake", **dhcp_options_parameters) + self.assertTrue("error" in r) @mock_ec2_deprecated - def test_that_when_dhcp_options_exists_the_dhcp_options_exists_method_returns_true(self): - ''' + def test_that_when_dhcp_options_exists_the_dhcp_options_exists_method_returns_true( + self, + ): + """ Tests existence of dhcp options successfully - ''' + """ dhcp_options = self._create_dhcp_options() - dhcp_options_exists_result = boto_vpc.dhcp_options_exists(dhcp_options.id, **conn_parameters) + dhcp_options_exists_result = boto_vpc.dhcp_options_exists( + dhcp_options.id, **conn_parameters + ) - self.assertTrue(dhcp_options_exists_result['exists']) + self.assertTrue(dhcp_options_exists_result["exists"]) @mock_ec2_deprecated - def test_that_when_dhcp_options_do_not_exist_the_dhcp_options_exists_method_returns_false(self): - ''' + def test_that_when_dhcp_options_do_not_exist_the_dhcp_options_exists_method_returns_false( + self, + ): + """ Tests existence of dhcp options failure - ''' - r = boto_vpc.dhcp_options_exists('fake', **conn_parameters) - self.assertFalse(r['exists']) + """ + r = boto_vpc.dhcp_options_exists("fake", **conn_parameters) + self.assertFalse(r["exists"]) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') - def test_that_when_checking_if_dhcp_options_exists_but_providing_no_filters_the_dhcp_options_exists_method_raises_a_salt_invocation_error(self): - ''' + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") + def test_that_when_checking_if_dhcp_options_exists_but_providing_no_filters_the_dhcp_options_exists_method_raises_a_salt_invocation_error( + self, + ): + """ Tests checking dhcp option existence with no filters - ''' - with self.assertRaisesRegex(SaltInvocationError, 'At least one of the following must be provided: id, name, or tags.'): + """ + with self.assertRaisesRegex( + SaltInvocationError, + "At least one of the following must be provided: id, name, or tags.", + ): boto_vpc.dhcp_options_exists(**conn_parameters) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {}. Installed: {}' - .format(required_boto_version, _get_boto_version())) -@skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {}. Installed: {}".format( + required_boto_version, _get_boto_version() + ), +) +@skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", +) class BotoVpcNetworkACLTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): @mock_ec2_deprecated - def test_that_when_creating_network_acl_for_an_existing_vpc_the_create_network_acl_method_returns_true(self): - ''' + def test_that_when_creating_network_acl_for_an_existing_vpc_the_create_network_acl_method_returns_true( + self, + ): + """ Tests creation of network acl with existing vpc - ''' + """ vpc = self._create_vpc() - network_acl_creation_result = boto_vpc.create_network_acl(vpc.id, **conn_parameters) + network_acl_creation_result = boto_vpc.create_network_acl( + vpc.id, **conn_parameters + ) self.assertTrue(network_acl_creation_result) @mock_ec2_deprecated def test_that_when_creating_network_acl_for_an_existing_vpc_and_specifying_a_name_the_create_network_acl_method_returns_true( - self): - ''' + self, + ): + """ Tests creation of network acl via name with an existing vpc - ''' + """ vpc = self._create_vpc() - network_acl_creation_result = boto_vpc.create_network_acl(vpc.id, network_acl_name='test', **conn_parameters) + network_acl_creation_result = boto_vpc.create_network_acl( + vpc.id, network_acl_name="test", **conn_parameters + ) self.assertTrue(network_acl_creation_result) @mock_ec2_deprecated def test_that_when_creating_network_acl_for_an_existing_vpc_and_specifying_tags_the_create_network_acl_method_returns_true( - self): - ''' + self, + ): + """ Tests creation of network acl via tags with an existing vpc - ''' + """ vpc = self._create_vpc() - network_acl_creation_result = boto_vpc.create_network_acl(vpc.id, tags={'test': 'testvalue'}, **conn_parameters) + network_acl_creation_result = boto_vpc.create_network_acl( + vpc.id, tags={"test": "testvalue"}, **conn_parameters + ) self.assertTrue(network_acl_creation_result) @mock_ec2_deprecated - def test_that_when_creating_network_acl_for_a_non_existent_vpc_the_create_network_acl_method_returns_an_error(self): - ''' + def test_that_when_creating_network_acl_for_a_non_existent_vpc_the_create_network_acl_method_returns_an_error( + self, + ): + """ Tests creation of network acl with a non-existent vpc - ''' - network_acl_creation_result = boto_vpc.create_network_acl('fake', **conn_parameters) + """ + network_acl_creation_result = boto_vpc.create_network_acl( + "fake", **conn_parameters + ) - self.assertTrue('error' in network_acl_creation_result) + self.assertTrue("error" in network_acl_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_creating_network_acl_fails_the_create_network_acl_method_returns_false(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_creating_network_acl_fails_the_create_network_acl_method_returns_false( + self, + ): + """ Tests creation of network acl failure - ''' + """ vpc = self._create_vpc() - with patch('moto.ec2.models.NetworkACLBackend.create_network_acl', - side_effect=BotoServerError(400, 'Mocked error')): - network_acl_creation_result = boto_vpc.create_network_acl(vpc.id, **conn_parameters) + with patch( + "moto.ec2.models.NetworkACLBackend.create_network_acl", + side_effect=BotoServerError(400, "Mocked error"), + ): + network_acl_creation_result = boto_vpc.create_network_acl( + vpc.id, **conn_parameters + ) self.assertFalse(network_acl_creation_result) @mock_ec2_deprecated - def test_that_when_deleting_an_existing_network_acl_the_delete_network_acl_method_returns_true(self): - ''' + def test_that_when_deleting_an_existing_network_acl_the_delete_network_acl_method_returns_true( + self, + ): + """ Tests deletion of existing network acl successfully - ''' + """ vpc = self._create_vpc() network_acl = self._create_network_acl(vpc.id) - network_acl_deletion_result = boto_vpc.delete_network_acl(network_acl.id, **conn_parameters) + network_acl_deletion_result = boto_vpc.delete_network_acl( + network_acl.id, **conn_parameters + ) self.assertTrue(network_acl_deletion_result) @mock_ec2_deprecated - def test_that_when_deleting_a_non_existent_network_acl_the_delete_network_acl_method_returns_an_error(self): - ''' + def test_that_when_deleting_a_non_existent_network_acl_the_delete_network_acl_method_returns_an_error( + self, + ): + """ Tests deleting a non-existent network acl - ''' - network_acl_deletion_result = boto_vpc.delete_network_acl('fake', **conn_parameters) + """ + network_acl_deletion_result = boto_vpc.delete_network_acl( + "fake", **conn_parameters + ) - self.assertTrue('error' in network_acl_deletion_result) + self.assertTrue("error" in network_acl_deletion_result) @mock_ec2_deprecated - def test_that_when_a_network_acl_exists_the_network_acl_exists_method_returns_true(self): - ''' + def test_that_when_a_network_acl_exists_the_network_acl_exists_method_returns_true( + self, + ): + """ Tests existence of network acl - ''' + """ vpc = self._create_vpc() network_acl = self._create_network_acl(vpc.id) - network_acl_deletion_result = boto_vpc.network_acl_exists(network_acl.id, **conn_parameters) + network_acl_deletion_result = boto_vpc.network_acl_exists( + network_acl.id, **conn_parameters + ) self.assertTrue(network_acl_deletion_result) @mock_ec2_deprecated - def test_that_when_a_network_acl_does_not_exist_the_network_acl_exists_method_returns_false(self): - ''' + def test_that_when_a_network_acl_does_not_exist_the_network_acl_exists_method_returns_false( + self, + ): + """ Tests checking network acl does not exist - ''' - network_acl_deletion_result = boto_vpc.network_acl_exists('fake', **conn_parameters) + """ + network_acl_deletion_result = boto_vpc.network_acl_exists( + "fake", **conn_parameters + ) - self.assertFalse(network_acl_deletion_result['exists']) + self.assertFalse(network_acl_deletion_result["exists"]) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') - def test_that_when_checking_if_network_acl_exists_but_providing_no_filters_the_network_acl_exists_method_raises_a_salt_invocation_error(self): - ''' + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") + def test_that_when_checking_if_network_acl_exists_but_providing_no_filters_the_network_acl_exists_method_raises_a_salt_invocation_error( + self, + ): + """ Tests checking existence of network acl with no filters - ''' + """ with self.assertRaisesRegex( - SaltInvocationError, - 'At least one of the following must be provided: id, name, or tags.' + SaltInvocationError, + "At least one of the following must be provided: id, name, or tags.", ): boto_vpc.dhcp_options_exists(**conn_parameters) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_creating_a_network_acl_entry_successfully_the_create_network_acl_entry_method_returns_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_creating_a_network_acl_entry_successfully_the_create_network_acl_entry_method_returns_true( + self, + ): + """ Tests creating network acl successfully - ''' + """ vpc = self._create_vpc() network_acl = self._create_network_acl(vpc.id) - network_acl_entry_creation_result = boto_vpc.create_network_acl_entry(network_acl.id, - *network_acl_entry_parameters, - **conn_parameters) + network_acl_entry_creation_result = boto_vpc.create_network_acl_entry( + network_acl.id, *network_acl_entry_parameters, **conn_parameters + ) self.assertTrue(network_acl_entry_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_creating_a_network_acl_entry_for_a_non_existent_network_acl_the_create_network_acl_entry_method_returns_false( - self): - ''' + self, + ): + """ Tests creating network acl entry for non-existent network acl - ''' - network_acl_entry_creation_result = boto_vpc.create_network_acl_entry(*network_acl_entry_parameters, - **conn_parameters) + """ + network_acl_entry_creation_result = boto_vpc.create_network_acl_entry( + *network_acl_entry_parameters, **conn_parameters + ) self.assertFalse(network_acl_entry_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_replacing_a_network_acl_entry_successfully_the_replace_network_acl_entry_method_returns_true( - self): - ''' + self, + ): + """ Tests replacing network acl entry successfully - ''' + """ vpc = self._create_vpc() network_acl = self._create_network_acl(vpc.id) self._create_network_acl_entry(network_acl.id, *network_acl_entry_parameters) - network_acl_entry_creation_result = boto_vpc.replace_network_acl_entry(network_acl.id, - *network_acl_entry_parameters, - **conn_parameters) + network_acl_entry_creation_result = boto_vpc.replace_network_acl_entry( + network_acl.id, *network_acl_entry_parameters, **conn_parameters + ) self.assertTrue(network_acl_entry_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_replacing_a_network_acl_entry_for_a_non_existent_network_acl_the_replace_network_acl_entry_method_returns_false( - self): - ''' + self, + ): + """ Tests replacing a network acl entry for a non-existent network acl - ''' - network_acl_entry_creation_result = boto_vpc.create_network_acl_entry(*network_acl_entry_parameters, - **conn_parameters) + """ + network_acl_entry_creation_result = boto_vpc.create_network_acl_entry( + *network_acl_entry_parameters, **conn_parameters + ) self.assertFalse(network_acl_entry_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_deleting_an_existing_network_acl_entry_the_delete_network_acl_entry_method_returns_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_deleting_an_existing_network_acl_entry_the_delete_network_acl_entry_method_returns_true( + self, + ): + """ Tests deleting existing network acl entry successfully - ''' + """ vpc = self._create_vpc() network_acl = self._create_network_acl(vpc.id) - network_acl_entry = self._create_network_acl_entry(network_acl.id, *network_acl_entry_parameters) + network_acl_entry = self._create_network_acl_entry( + network_acl.id, *network_acl_entry_parameters + ) - network_acl_entry_deletion_result = boto_vpc.delete_network_acl_entry(network_acl_entry.id, 100, - **conn_parameters) + network_acl_entry_deletion_result = boto_vpc.delete_network_acl_entry( + network_acl_entry.id, 100, **conn_parameters + ) self.assertTrue(network_acl_entry_deletion_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_deleting_a_non_existent_network_acl_entry_the_delete_network_acl_entry_method_returns_false( - self): - ''' + self, + ): + """ Tests deleting a non-existent network acl entry - ''' - network_acl_entry_deletion_result = boto_vpc.delete_network_acl_entry('fake', 100, - **conn_parameters) + """ + network_acl_entry_deletion_result = boto_vpc.delete_network_acl_entry( + "fake", 100, **conn_parameters + ) self.assertFalse(network_acl_entry_deletion_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_associating_an_existing_network_acl_to_an_existing_subnet_the_associate_network_acl_method_returns_true( - self): - ''' + self, + ): + """ Tests association of existing network acl to existing subnet successfully - ''' + """ vpc = self._create_vpc() network_acl = self._create_network_acl(vpc.id) subnet = self._create_subnet(vpc.id) - network_acl_association_result = boto_vpc.associate_network_acl_to_subnet(network_acl.id, subnet.id, - **conn_parameters) + network_acl_association_result = boto_vpc.associate_network_acl_to_subnet( + network_acl.id, subnet.id, **conn_parameters + ) self.assertTrue(network_acl_association_result) @mock_ec2_deprecated def test_that_when_associating_a_non_existent_network_acl_to_an_existing_subnet_the_associate_network_acl_method_returns_an_error( - self): - ''' + self, + ): + """ Tests associating a non-existent network acl to existing subnet failure - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - network_acl_association_result = boto_vpc.associate_network_acl_to_subnet('fake', subnet.id, - **conn_parameters) + network_acl_association_result = boto_vpc.associate_network_acl_to_subnet( + "fake", subnet.id, **conn_parameters + ) - self.assertTrue('error' in network_acl_association_result) + self.assertTrue("error" in network_acl_association_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_associating_an_existing_network_acl_to_a_non_existent_subnet_the_associate_network_acl_method_returns_false( - self): - ''' + self, + ): + """ Tests associating an existing network acl to a non-existent subnet - ''' + """ vpc = self._create_vpc() network_acl = self._create_network_acl(vpc.id) - network_acl_association_result = boto_vpc.associate_network_acl_to_subnet(network_acl.id, 'fake', - **conn_parameters) + network_acl_association_result = boto_vpc.associate_network_acl_to_subnet( + network_acl.id, "fake", **conn_parameters + ) self.assertFalse(network_acl_association_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_creating_and_associating_a_network_acl_to_a_subnet_succeeds_the_associate_new_network_acl_to_subnet_method_returns_true( - self): - ''' + self, + ): + """ Tests creating/associating a network acl to a subnet to a new network - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - network_acl_creation_and_association_result = boto_vpc.associate_new_network_acl_to_subnet(vpc.id, subnet.id, - **conn_parameters) + network_acl_creation_and_association_result = boto_vpc.associate_new_network_acl_to_subnet( + vpc.id, subnet.id, **conn_parameters + ) self.assertTrue(network_acl_creation_and_association_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_creating_and_associating_a_network_acl_to_a_subnet_and_specifying_a_name_succeeds_the_associate_new_network_acl_to_subnet_method_returns_true( - self): - ''' + self, + ): + """ Tests creation/association of a network acl to subnet via name successfully - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - network_acl_creation_and_association_result = boto_vpc.associate_new_network_acl_to_subnet(vpc.id, subnet.id, - network_acl_name='test', - **conn_parameters) + network_acl_creation_and_association_result = boto_vpc.associate_new_network_acl_to_subnet( + vpc.id, subnet.id, network_acl_name="test", **conn_parameters + ) self.assertTrue(network_acl_creation_and_association_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_creating_and_associating_a_network_acl_to_a_subnet_and_specifying_tags_succeeds_the_associate_new_network_acl_to_subnet_method_returns_true( - self): - ''' + self, + ): + """ Tests creating/association of a network acl to a subnet via tag successfully - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - network_acl_creation_and_association_result = boto_vpc.associate_new_network_acl_to_subnet(vpc.id, subnet.id, - tags={ - 'test': 'testvalue'}, - **conn_parameters) + network_acl_creation_and_association_result = boto_vpc.associate_new_network_acl_to_subnet( + vpc.id, subnet.id, tags={"test": "testvalue"}, **conn_parameters + ) self.assertTrue(network_acl_creation_and_association_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_creating_and_associating_a_network_acl_to_a_non_existent_subnet_the_associate_new_network_acl_to_subnet_method_returns_false( - self): - ''' + self, + ): + """ Tests creation/association of a network acl to a non-existent vpc - ''' + """ vpc = self._create_vpc() - network_acl_creation_and_association_result = boto_vpc.associate_new_network_acl_to_subnet(vpc.id, 'fake', - **conn_parameters) + network_acl_creation_and_association_result = boto_vpc.associate_new_network_acl_to_subnet( + vpc.id, "fake", **conn_parameters + ) self.assertFalse(network_acl_creation_and_association_result) @mock_ec2_deprecated def test_that_when_creating_a_network_acl_to_a_non_existent_vpc_the_associate_new_network_acl_to_subnet_method_returns_an_error( - self): - ''' + self, + ): + """ Tests creation/association of network acl to a non-existent subnet - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - network_acl_creation_result = boto_vpc.create_network_acl(vpc_name='fake', subnet_id=subnet.id, **conn_parameters) + network_acl_creation_result = boto_vpc.create_network_acl( + vpc_name="fake", subnet_id=subnet.id, **conn_parameters + ) - self.assertTrue('error' in network_acl_creation_result) + self.assertTrue("error" in network_acl_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_disassociating_network_acl_succeeds_the_disassociate_network_acl_method_should_return_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_disassociating_network_acl_succeeds_the_disassociate_network_acl_method_should_return_true( + self, + ): + """ Tests disassociation of network acl success - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - dhcp_disassociate_result = boto_vpc.disassociate_network_acl(subnet.id, vpc_id=vpc.id, **conn_parameters) + dhcp_disassociate_result = boto_vpc.disassociate_network_acl( + subnet.id, vpc_id=vpc.id, **conn_parameters + ) self.assertTrue(dhcp_disassociate_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_disassociating_network_acl_for_a_non_existent_vpc_the_disassociate_network_acl_method_should_return_false( - self): - ''' + self, + ): + """ Tests disassociation of network acl from non-existent vpc - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - dhcp_disassociate_result = boto_vpc.disassociate_network_acl(subnet.id, vpc_id='fake', **conn_parameters) + dhcp_disassociate_result = boto_vpc.disassociate_network_acl( + subnet.id, vpc_id="fake", **conn_parameters + ) self.assertFalse(dhcp_disassociate_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_disassociating_network_acl_for_a_non_existent_subnet_the_disassociate_network_acl_method_should_return_false( - self): - ''' + self, + ): + """ Tests disassociation of network acl from non-existent subnet - ''' + """ vpc = self._create_vpc() - dhcp_disassociate_result = boto_vpc.disassociate_network_acl('fake', vpc_id=vpc.id, **conn_parameters) + dhcp_disassociate_result = boto_vpc.disassociate_network_acl( + "fake", vpc_id=vpc.id, **conn_parameters + ) self.assertFalse(dhcp_disassociate_result) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {}. Installed: {}' - .format(required_boto_version, _get_boto_version())) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {}. Installed: {}".format( + required_boto_version, _get_boto_version() + ), +) class BotoVpcRouteTablesTestCase(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_creating_a_route_table_succeeds_the_create_route_table_method_returns_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_creating_a_route_table_succeeds_the_create_route_table_method_returns_true( + self, + ): + """ Tests creating route table successfully - ''' + """ vpc = self._create_vpc() - route_table_creation_result = boto_vpc.create_route_table(vpc.id, **conn_parameters) + route_table_creation_result = boto_vpc.create_route_table( + vpc.id, **conn_parameters + ) self.assertTrue(route_table_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_creating_a_route_table_on_a_non_existent_vpc_the_create_route_table_method_returns_false(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_creating_a_route_table_on_a_non_existent_vpc_the_create_route_table_method_returns_false( + self, + ): + """ Tests creating route table on a non-existent vpc - ''' - route_table_creation_result = boto_vpc.create_route_table('fake', **conn_parameters) + """ + route_table_creation_result = boto_vpc.create_route_table( + "fake", **conn_parameters + ) self.assertTrue(route_table_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_deleting_a_route_table_succeeds_the_delete_route_table_method_returns_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_deleting_a_route_table_succeeds_the_delete_route_table_method_returns_true( + self, + ): + """ Tests deleting route table successfully - ''' + """ vpc = self._create_vpc() route_table = self._create_route_table(vpc.id) - route_table_deletion_result = boto_vpc.delete_route_table(route_table.id, **conn_parameters) + route_table_deletion_result = boto_vpc.delete_route_table( + route_table.id, **conn_parameters + ) self.assertTrue(route_table_deletion_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_deleting_a_non_existent_route_table_the_delete_route_table_method_returns_false(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_deleting_a_non_existent_route_table_the_delete_route_table_method_returns_false( + self, + ): + """ Tests deleting non-existent route table - ''' - route_table_deletion_result = boto_vpc.delete_route_table('fake', **conn_parameters) + """ + route_table_deletion_result = boto_vpc.delete_route_table( + "fake", **conn_parameters + ) self.assertFalse(route_table_deletion_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_route_table_exists_the_route_table_exists_method_returns_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_route_table_exists_the_route_table_exists_method_returns_true( + self, + ): + """ Tests existence of route table success - ''' + """ vpc = self._create_vpc() route_table = self._create_route_table(vpc.id) - route_table_existence_result = boto_vpc.route_table_exists(route_table.id, **conn_parameters) + route_table_existence_result = boto_vpc.route_table_exists( + route_table.id, **conn_parameters + ) self.assertTrue(route_table_existence_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_route_table_does_not_exist_the_route_table_exists_method_returns_false(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_route_table_does_not_exist_the_route_table_exists_method_returns_false( + self, + ): + """ Tests existence of route table failure - ''' - route_table_existence_result = boto_vpc.route_table_exists('fake', **conn_parameters) + """ + route_table_existence_result = boto_vpc.route_table_exists( + "fake", **conn_parameters + ) self.assertFalse(route_table_existence_result) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') - def test_that_when_checking_if_a_route_table_exists_but_providing_no_filters_the_route_table_exists_method_raises_a_salt_invocation_error(self): - ''' + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") + def test_that_when_checking_if_a_route_table_exists_but_providing_no_filters_the_route_table_exists_method_raises_a_salt_invocation_error( + self, + ): + """ Tests checking route table without filters - ''' + """ with self.assertRaisesRegex( - SaltInvocationError, - 'At least one of the following must be provided: id, name, or tags.' + SaltInvocationError, + "At least one of the following must be provided: id, name, or tags.", ): boto_vpc.dhcp_options_exists(**conn_parameters) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_associating_a_route_table_succeeds_the_associate_route_table_method_should_return_the_association_id( - self): - ''' + self, + ): + """ Tests associating route table successfully - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) route_table = self._create_route_table(vpc.id) - association_id = boto_vpc.associate_route_table(route_table.id, subnet.id, **conn_parameters) + association_id = boto_vpc.associate_route_table( + route_table.id, subnet.id, **conn_parameters + ) self.assertTrue(association_id) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_associating_a_route_table_with_a_non_existent_route_table_the_associate_route_table_method_should_return_false( - self): - ''' + self, + ): + """ Tests associating of route table to non-existent route table - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) - association_id = boto_vpc.associate_route_table('fake', subnet.id, **conn_parameters) + association_id = boto_vpc.associate_route_table( + "fake", subnet.id, **conn_parameters + ) self.assertFalse(association_id) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_associating_a_route_table_with_a_non_existent_subnet_the_associate_route_table_method_should_return_false( - self): - ''' + self, + ): + """ Tests associating of route table with non-existent subnet - ''' + """ vpc = self._create_vpc() route_table = self._create_route_table(vpc.id) - association_id = boto_vpc.associate_route_table(route_table.id, 'fake', **conn_parameters) + association_id = boto_vpc.associate_route_table( + route_table.id, "fake", **conn_parameters + ) self.assertFalse(association_id) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_disassociating_a_route_table_succeeds_the_disassociate_route_table_method_should_return_true( - self): - ''' + self, + ): + """ Tests disassociation of a route - ''' + """ vpc = self._create_vpc() subnet = self._create_subnet(vpc.id) route_table = self._create_route_table(vpc.id) association_id = self._associate_route_table(route_table.id, subnet.id) - dhcp_disassociate_result = boto_vpc.disassociate_route_table(association_id, **conn_parameters) + dhcp_disassociate_result = boto_vpc.disassociate_route_table( + association_id, **conn_parameters + ) self.assertTrue(dhcp_disassociate_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_creating_a_route_succeeds_the_create_route_method_should_return_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_creating_a_route_succeeds_the_create_route_method_should_return_true( + self, + ): + """ Tests successful creation of a route - ''' + """ vpc = self._create_vpc() route_table = self._create_route_table(vpc.id) - route_creation_result = boto_vpc.create_route(route_table.id, cidr_block, **conn_parameters) + route_creation_result = boto_vpc.create_route( + route_table.id, cidr_block, **conn_parameters + ) self.assertTrue(route_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_creating_a_route_with_a_non_existent_route_table_the_create_route_method_should_return_false( - self): - ''' + self, + ): + """ Tests creation of route on non-existent route table - ''' - route_creation_result = boto_vpc.create_route('fake', cidr_block, **conn_parameters) + """ + route_creation_result = boto_vpc.create_route( + "fake", cidr_block, **conn_parameters + ) self.assertFalse(route_creation_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_deleting_a_route_succeeds_the_delete_route_method_should_return_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_deleting_a_route_succeeds_the_delete_route_method_should_return_true( + self, + ): + """ Tests deleting route from route table - ''' + """ vpc = self._create_vpc() route_table = self._create_route_table(vpc.id) - route_deletion_result = boto_vpc.delete_route(route_table.id, cidr_block, **conn_parameters) + route_deletion_result = boto_vpc.delete_route( + route_table.id, cidr_block, **conn_parameters + ) self.assertTrue(route_deletion_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_deleting_a_route_with_a_non_existent_route_table_the_delete_route_method_should_return_false( - self): - ''' + self, + ): + """ Tests deleting route from a non-existent route table - ''' - route_deletion_result = boto_vpc.delete_route('fake', cidr_block, **conn_parameters) + """ + route_deletion_result = boto_vpc.delete_route( + "fake", cidr_block, **conn_parameters + ) self.assertFalse(route_deletion_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') - def test_that_when_replacing_a_route_succeeds_the_replace_route_method_should_return_true(self): - ''' + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") + def test_that_when_replacing_a_route_succeeds_the_replace_route_method_should_return_true( + self, + ): + """ Tests replacing route successfully - ''' + """ vpc = self._create_vpc() route_table = self._create_route_table(vpc.id) - route_replacing_result = boto_vpc.replace_route(route_table.id, cidr_block, **conn_parameters) + route_replacing_result = boto_vpc.replace_route( + route_table.id, cidr_block, **conn_parameters + ) self.assertTrue(route_replacing_result) @mock_ec2_deprecated - @skipIf(True, 'Moto has not implemented this feature. Skipping for now.') + @skipIf(True, "Moto has not implemented this feature. Skipping for now.") def test_that_when_replacing_a_route_with_a_non_existent_route_table_the_replace_route_method_should_return_false( - self): - ''' + self, + ): + """ Tests replacing a route when the route table doesn't exist - ''' - route_replacing_result = boto_vpc.replace_route('fake', cidr_block, **conn_parameters) + """ + route_replacing_result = boto_vpc.replace_route( + "fake", cidr_block, **conn_parameters + ) self.assertFalse(route_replacing_result) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {}. Installed: {}' - .format(required_boto_version, _get_boto_version())) -@skipIf(_has_required_moto() is False, 'The moto version must be >= to version {0}'.format(required_moto_version)) -@skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {}. Installed: {}".format( + required_boto_version, _get_boto_version() + ), +) +@skipIf( + _has_required_moto() is False, + "The moto version must be >= to version {0}".format(required_moto_version), +) +@skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", +) class BotoVpcPeeringConnectionsTest(BotoVpcTestCaseBase, BotoVpcTestCaseMixin): - @mock_ec2_deprecated def test_request_vpc_peering_connection(self): - ''' + """ Run with 2 vpc ids and returns a message - ''' + """ my_vpc = self._create_vpc() other_vpc = self._create_vpc() - self.assertTrue('msg' in boto_vpc.request_vpc_peering_connection( - name='my_peering', - requester_vpc_id=my_vpc.id, - peer_vpc_id=other_vpc.id, - **conn_parameters)) + self.assertTrue( + "msg" + in boto_vpc.request_vpc_peering_connection( + name="my_peering", + requester_vpc_id=my_vpc.id, + peer_vpc_id=other_vpc.id, + **conn_parameters + ) + ) @mock_ec2_deprecated def test_raises_error_if_both_vpc_name_and_vpc_id_are_specified(self): - ''' + """ Must specify only one - ''' + """ my_vpc = self._create_vpc() other_vpc = self._create_vpc() with self.assertRaises(SaltInvocationError): - boto_vpc.request_vpc_peering_connection(name='my_peering', - requester_vpc_id=my_vpc.id, - requester_vpc_name='foobar', - peer_vpc_id=other_vpc.id, - **conn_parameters) + boto_vpc.request_vpc_peering_connection( + name="my_peering", + requester_vpc_id=my_vpc.id, + requester_vpc_name="foobar", + peer_vpc_id=other_vpc.id, + **conn_parameters + ) - boto_vpc.request_vpc_peering_connection(name='my_peering', - requester_vpc_name='my_peering', - peer_vpc_id=other_vpc.id, - **conn_parameters) + boto_vpc.request_vpc_peering_connection( + name="my_peering", + requester_vpc_name="my_peering", + peer_vpc_id=other_vpc.id, + **conn_parameters + ) diff --git a/tests/unit/modules/test_bower.py b/tests/unit/modules/test_bower.py index 381a04a3d77..5bf1add3560 100644 --- a/tests/unit/modules/test_bower.py +++ b/tests/unit/modules/test_bower.py @@ -1,105 +1,96 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Alexander Pyatkin <asp@thexyz.net> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.modules.bower as bower from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BowerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.bower - ''' + """ + def setup_loader_modules(self): - return {bower: {'_check_valid_version': MagicMock(return_value=True)}} + return {bower: {"_check_valid_version": MagicMock(return_value=True)}} def test_install_with_error(self): - ''' + """ Test if it raises an exception when install package fails - ''' - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'}) - with patch.dict(bower.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "error"}) + with patch.dict(bower.__salt__, {"cmd.run_all": mock}): self.assertRaises( - CommandExecutionError, - bower.install, - '/path/to/project', - 'underscore') + CommandExecutionError, bower.install, "/path/to/project", "underscore" + ) def test_install_new_package(self): - ''' + """ Test if it returns True when install package succeeds - ''' - mock = MagicMock(return_value={'retcode': 0, - 'stdout': '{"underscore":{}}'}) - with patch.dict(bower.__salt__, {'cmd.run_all': mock}): - self.assertTrue(bower.install('/path/to/project', 'underscore')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": '{"underscore":{}}'}) + with patch.dict(bower.__salt__, {"cmd.run_all": mock}): + self.assertTrue(bower.install("/path/to/project", "underscore")) def test_install_existing_package(self): - ''' + """ Test if it returns False when package already installed - ''' - mock = MagicMock(return_value={'retcode': 0, - 'stdout': '{}'}) - with patch.dict(bower.__salt__, {'cmd.run_all': mock}): - self.assertFalse(bower.install('/path/to/project', 'underscore')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": "{}"}) + with patch.dict(bower.__salt__, {"cmd.run_all": mock}): + self.assertFalse(bower.install("/path/to/project", "underscore")) def test_uninstall_with_error(self): - ''' + """ Test if it raises an exception when uninstall package fails - ''' - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'}) - with patch.dict(bower.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "error"}) + with patch.dict(bower.__salt__, {"cmd.run_all": mock}): self.assertRaises( - CommandExecutionError, - bower.uninstall, - '/path/to/project', - 'underscore') + CommandExecutionError, bower.uninstall, "/path/to/project", "underscore" + ) def test_uninstall_existing_package(self): - ''' + """ Test if it returns True when uninstall package succeeds - ''' - mock = MagicMock(return_value={'retcode': 0, - 'stdout': '{"underscore": {}}'}) - with patch.dict(bower.__salt__, {'cmd.run_all': mock}): - self.assertTrue(bower.uninstall('/path/to/project', 'underscore')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": '{"underscore": {}}'}) + with patch.dict(bower.__salt__, {"cmd.run_all": mock}): + self.assertTrue(bower.uninstall("/path/to/project", "underscore")) def test_uninstall_missing_package(self): - ''' + """ Test if it returns False when package is not installed - ''' - mock = MagicMock(return_value={'retcode': 0, - 'stdout': '{}'}) - with patch.dict(bower.__salt__, {'cmd.run_all': mock}): - self.assertFalse(bower.uninstall('/path/to/project', 'underscore')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": "{}"}) + with patch.dict(bower.__salt__, {"cmd.run_all": mock}): + self.assertFalse(bower.uninstall("/path/to/project", "underscore")) def test_list_packages_with_error(self): - ''' + """ Test if it raises an exception when list installed packages fails - ''' - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'}) - with patch.dict(bower.__salt__, {'cmd.run_all': mock}): - self.assertRaises( - CommandExecutionError, - bower.list_, - '/path/to/project') + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "error"}) + with patch.dict(bower.__salt__, {"cmd.run_all": mock}): + self.assertRaises(CommandExecutionError, bower.list_, "/path/to/project") def test_list_packages_success(self): - ''' + """ Test if it lists installed Bower packages - ''' + """ output = '{"dependencies": {"underscore": {}, "jquery":{}}}' - mock = MagicMock(return_value={'retcode': 0, 'stdout': output}) - with patch.dict(bower.__salt__, {'cmd.run_all': mock}): - self.assertEqual(bower.list_('/path/to/project'), - {'underscore': {}, 'jquery': {}}) + mock = MagicMock(return_value={"retcode": 0, "stdout": output}) + with patch.dict(bower.__salt__, {"cmd.run_all": mock}): + self.assertEqual( + bower.list_("/path/to/project"), {"underscore": {}, "jquery": {}} + ) diff --git a/tests/unit/modules/test_bridge.py b/tests/unit/modules/test_bridge.py index fba8cc3785b..5df491d04ec 100644 --- a/tests/unit/modules/test_bridge.py +++ b/tests/unit/modules/test_bridge.py @@ -1,115 +1,116 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.modules.bridge as bridge +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BridgeTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.bridge - ''' + """ + def setup_loader_modules(self): return {bridge: {}} def test_show(self): - ''' + """ Test for Returns bridges interfaces along with enslaved physical interfaces - ''' + """ mock = MagicMock(return_value=True) - with patch.object(bridge, '_os_dispatch', mock): - self.assertTrue(bridge.show('br')) + with patch.object(bridge, "_os_dispatch", mock): + self.assertTrue(bridge.show("br")) def test_list_(self): - ''' + """ Test for Returns the machine's bridges list - ''' + """ mock = MagicMock(return_value=None) - with patch.object(bridge, '_os_dispatch', mock): + with patch.object(bridge, "_os_dispatch", mock): self.assertEqual(bridge.list_(), None) - mock = MagicMock(return_value=['A', 'B']) - with patch.object(bridge, '_os_dispatch', mock): - self.assertEqual(bridge.list_(), ['A', 'B']) + mock = MagicMock(return_value=["A", "B"]) + with patch.object(bridge, "_os_dispatch", mock): + self.assertEqual(bridge.list_(), ["A", "B"]) def test_interfaces(self): - ''' + """ Test for Returns interfaces attached to a bridge - ''' + """ self.assertEqual(bridge.interfaces(), None) - mock = MagicMock(return_value={'interfaces': 'A'}) - with patch.object(bridge, '_os_dispatch', mock): - self.assertEqual(bridge.interfaces('br'), 'A') + mock = MagicMock(return_value={"interfaces": "A"}) + with patch.object(bridge, "_os_dispatch", mock): + self.assertEqual(bridge.interfaces("br"), "A") def test_find_interfaces(self): - ''' + """ Test for Returns the bridge to which the interfaces are bond to - ''' + """ mock = MagicMock(return_value=None) - with patch.object(bridge, '_os_dispatch', mock): + with patch.object(bridge, "_os_dispatch", mock): self.assertEqual(bridge.find_interfaces(), None) - mock = MagicMock(return_value={'interfaces': 'A'}) - with patch.object(bridge, '_os_dispatch', mock): + mock = MagicMock(return_value={"interfaces": "A"}) + with patch.object(bridge, "_os_dispatch", mock): self.assertEqual(bridge.find_interfaces(), {}) def test_add(self): - ''' + """ Test for Creates a bridge - ''' - mock = MagicMock(return_value='A') - with patch.object(bridge, '_os_dispatch', mock): - self.assertEqual(bridge.add(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(bridge, "_os_dispatch", mock): + self.assertEqual(bridge.add(), "A") def test_delete(self): - ''' + """ Test for Deletes a bridge - ''' - mock = MagicMock(return_value='A') - with patch.object(bridge, '_os_dispatch', mock): - self.assertEqual(bridge.delete(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(bridge, "_os_dispatch", mock): + self.assertEqual(bridge.delete(), "A") def test_addif(self): - ''' + """ Test for Adds an interface to a bridge - ''' - mock = MagicMock(return_value='A') - with patch.object(bridge, '_os_dispatch', mock): - self.assertEqual(bridge.addif(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(bridge, "_os_dispatch", mock): + self.assertEqual(bridge.addif(), "A") def test_delif(self): - ''' + """ Test for Removes an interface from a bridge - ''' - mock = MagicMock(return_value='A') - with patch.object(bridge, '_os_dispatch', mock): - self.assertEqual(bridge.delif(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(bridge, "_os_dispatch", mock): + self.assertEqual(bridge.delif(), "A") def test_stp(self): - ''' + """ Test for Sets Spanning Tree Protocol state for a bridge - ''' - with patch.dict(bridge.__grains__, {'kernel': 'Linux'}): - mock = MagicMock(return_value='Linux') - with patch.object(bridge, '_os_dispatch', mock): - self.assertEqual(bridge.stp(), 'Linux') + """ + with patch.dict(bridge.__grains__, {"kernel": "Linux"}): + mock = MagicMock(return_value="Linux") + with patch.object(bridge, "_os_dispatch", mock): + self.assertEqual(bridge.stp(), "Linux") - with patch.dict(bridge.__grains__, {'kernel': 'FreeBSD'}): - mock = MagicMock(return_value='FreeBSD') - with patch.object(bridge, '_os_dispatch', mock): - self.assertEqual(bridge.stp(), 'FreeBSD') + with patch.dict(bridge.__grains__, {"kernel": "FreeBSD"}): + mock = MagicMock(return_value="FreeBSD") + with patch.object(bridge, "_os_dispatch", mock): + self.assertEqual(bridge.stp(), "FreeBSD") - with patch.dict(bridge.__grains__, {'kernel': None}): + with patch.dict(bridge.__grains__, {"kernel": None}): self.assertFalse(bridge.stp()) diff --git a/tests/unit/modules/test_btrfs.py b/tests/unit/modules/test_btrfs.py index 4ece2f54379..09c2238d8ad 100644 --- a/tests/unit/modules/test_btrfs.py +++ b/tests/unit/modules/test_btrfs.py @@ -1,677 +1,718 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals import pytest - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - mock_open, - MagicMock, - patch, -) +import salt.modules.btrfs as btrfs # Import Salt Libs import salt.utils.files import salt.utils.fsutils import salt.utils.platform -import salt.modules.btrfs as btrfs from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase, skipIf + class BtrfsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.btrfs - ''' + """ + def setup_loader_modules(self): - return {btrfs: {'__salt__': {}}} + return {btrfs: {"__salt__": {}}} # 'version' function tests: 1 def test_version(self): - ''' + """ Test if it return BTRFS version. - ''' - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(btrfs.version(), {'version': 'Salt'}) + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "", "stdout": "Salt"}) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual(btrfs.version(), {"version": "Salt"}) # 'info' function tests: 1 def test_info(self): - ''' + """ Test if it get BTRFS filesystem information. - ''' - with patch('salt.utils.fsutils._verify_run', MagicMock(return_value=True)): - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value={'Salt': 'salt'}) - with patch.object(btrfs, '_parse_btrfs_info', mock): - self.assertDictEqual(btrfs.info('/dev/sda1'), - {'Salt': 'salt'}) + """ + with patch("salt.utils.fsutils._verify_run", MagicMock(return_value=True)): + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(return_value={"Salt": "salt"}) + with patch.object(btrfs, "_parse_btrfs_info", mock): + self.assertDictEqual(btrfs.info("/dev/sda1"), {"Salt": "salt"}) # 'devices' function tests: 1 def test_devices(self): - ''' + """ Test if it get known BTRFS formatted devices on the system. - ''' - with patch('salt.utils.fsutils._blkid_output', - MagicMock(return_value='Salt')): - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - self.assertEqual(btrfs.devices(), 'Salt') + """ + with patch("salt.utils.fsutils._blkid_output", MagicMock(return_value="Salt")): + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + self.assertEqual(btrfs.devices(), "Salt") # 'defragment' function tests: 2 def test_defragment(self): - ''' + """ Test if it defragment mounted BTRFS filesystem. - ''' - with patch('salt.utils.fsutils._is_device', MagicMock(return_value=False)): - with patch('os.path.exists', MagicMock(return_value=True)): - ret = [{'range': '/dev/sda1', - 'mount_point': False, - 'log': False, 'passed': True}] - mock_run = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock_run}): - mock_file = mock_open(read_data='/dev/sda1 / ext4 rw,data=ordered 0 0') - with patch.object(salt.utils.files, 'fopen', mock_file): - self.assertListEqual(btrfs.defragment('/dev/sda1'), ret) + """ + with patch("salt.utils.fsutils._is_device", MagicMock(return_value=False)): + with patch("os.path.exists", MagicMock(return_value=True)): + ret = [ + { + "range": "/dev/sda1", + "mount_point": False, + "log": False, + "passed": True, + } + ] + mock_run = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock_run}): + mock_file = mock_open( + read_data="/dev/sda1 / ext4 rw,data=ordered 0 0" + ) + with patch.object(salt.utils.files, "fopen", mock_file): + self.assertListEqual(btrfs.defragment("/dev/sda1"), ret) def test_defragment_error(self): - ''' + """ Test if it gives device not mount error - ''' - with patch('salt.utils.fsutils._is_device', MagicMock(return_value=True)): - mock_run = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock_run}): - mock_file = mock_open(read_data='/dev/sda1 / ext4 rw,data=ordered 0 0') - with patch.object(salt.utils.files, 'fopen', mock_file): - self.assertRaises(CommandExecutionError, btrfs.defragment, - '/dev/sda1') + """ + with patch("salt.utils.fsutils._is_device", MagicMock(return_value=True)): + mock_run = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock_run}): + mock_file = mock_open(read_data="/dev/sda1 / ext4 rw,data=ordered 0 0") + with patch.object(salt.utils.files, "fopen", mock_file): + self.assertRaises( + CommandExecutionError, btrfs.defragment, "/dev/sda1" + ) # 'features' function tests: 1 def test_features(self): - ''' + """ Test if it list currently available BTRFS features. - ''' - with patch('salt.utils.fsutils._verify_run', MagicMock(return_value=True)): - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): + """ + with patch("salt.utils.fsutils._verify_run", MagicMock(return_value=True)): + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): self.assertDictEqual(btrfs.features(), {}) # 'usage' function tests: 1 def test_usage(self): - ''' + """ Test if it shows in which disk the chunks are allocated. - ''' - with patch('salt.utils.fsutils._verify_run', MagicMock(return_value=True)): - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value={'Salt': 'salt'}) - with patch.object(btrfs, '_usage_specific', mock): - self.assertDictEqual(btrfs.usage('/dev/sda1'), - {'Salt': 'salt'}) + """ + with patch("salt.utils.fsutils._verify_run", MagicMock(return_value=True)): + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(return_value={"Salt": "salt"}) + with patch.object(btrfs, "_usage_specific", mock): + self.assertDictEqual(btrfs.usage("/dev/sda1"), {"Salt": "salt"}) - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Unallocated:\n'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value={'/dev/sda1': True}) - with patch.object(btrfs, '_usage_unallocated', mock): - self.assertDictEqual(btrfs.usage('/dev/sda1'), - {'unallocated': {'/dev/sda1': True}}) + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Unallocated:\n"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(return_value={"/dev/sda1": True}) + with patch.object(btrfs, "_usage_unallocated", mock): + self.assertDictEqual( + btrfs.usage("/dev/sda1"), {"unallocated": {"/dev/sda1": True}} + ) - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Overall:\n'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value={'/dev/sda1': True}) - with patch.object(btrfs, '_usage_overall', mock): - self.assertDictEqual(btrfs.usage('/dev/sda1'), - {'overall': {'/dev/sda1': True}}) + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Overall:\n"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(return_value={"/dev/sda1": True}) + with patch.object(btrfs, "_usage_overall", mock): + self.assertDictEqual( + btrfs.usage("/dev/sda1"), {"overall": {"/dev/sda1": True}} + ) # 'mkfs' function tests: 3 def test_mkfs(self): - ''' + """ Test if it create a file system on the specified device. - ''' - mock_cmd = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) + """ + mock_cmd = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) mock_info = MagicMock(return_value=[]) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock_cmd, - 'btrfs.info': mock_info}): - mock_file = mock_open(read_data='/dev/sda1 / ext4 rw,data=ordered 0 0') - with patch.object(salt.utils.files, 'fopen', mock_file): - self.assertDictEqual(btrfs.mkfs('/dev/sda1'), {'log': 'Salt'}) + with patch.dict( + btrfs.__salt__, {"cmd.run_all": mock_cmd, "btrfs.info": mock_info} + ): + mock_file = mock_open(read_data="/dev/sda1 / ext4 rw,data=ordered 0 0") + with patch.object(salt.utils.files, "fopen", mock_file): + self.assertDictEqual(btrfs.mkfs("/dev/sda1"), {"log": "Salt"}) def test_mkfs_error(self): - ''' + """ Test if it No devices specified error - ''' + """ self.assertRaises(CommandExecutionError, btrfs.mkfs) def test_mkfs_mount_error(self): - ''' + """ Test if it device mount error - ''' - mock = MagicMock(return_value={'/dev/sda1': True}) - with patch.object(salt.utils.fsutils, '_get_mounts', mock): - self.assertRaises(CommandExecutionError, btrfs.mkfs, '/dev/sda1') + """ + mock = MagicMock(return_value={"/dev/sda1": True}) + with patch.object(salt.utils.fsutils, "_get_mounts", mock): + self.assertRaises(CommandExecutionError, btrfs.mkfs, "/dev/sda1") # 'resize' function tests: 4 def test_resize(self): - ''' + """ Test if it resize filesystem. - ''' - with patch('salt.utils.fsutils._is_device', MagicMock(return_value=True)): - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) + """ + with patch("salt.utils.fsutils._is_device", MagicMock(return_value=True)): + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) mock_info = MagicMock(return_value=[]) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock, - 'btrfs.info': mock_info}): - mock = MagicMock(return_value={'/dev/sda1': True}) - with patch.object(salt.utils.fsutils, '_get_mounts', mock): - self.assertDictEqual(btrfs.resize('/dev/sda1', 'max'), - {'log': 'Salt'}) + with patch.dict( + btrfs.__salt__, {"cmd.run_all": mock, "btrfs.info": mock_info} + ): + mock = MagicMock(return_value={"/dev/sda1": True}) + with patch.object(salt.utils.fsutils, "_get_mounts", mock): + self.assertDictEqual( + btrfs.resize("/dev/sda1", "max"), {"log": "Salt"} + ) def test_resize_valid_error(self): - ''' + """ Test if it gives device should be mounted error - ''' - with patch('salt.utils.fsutils._is_device', MagicMock(return_value=False)): - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, btrfs.resize, - '/dev/sda1', 'max') + """ + with patch("salt.utils.fsutils._is_device", MagicMock(return_value=False)): + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + self.assertRaises( + CommandExecutionError, btrfs.resize, "/dev/sda1", "max" + ) def test_resize_mount_error(self): - ''' + """ Test if it gives mount point error - ''' - with patch('salt.utils.fsutils._is_device', MagicMock(return_value=True)): - mock = MagicMock(return_value={'/dev/sda1': False}) - with patch.object(salt.utils.fsutils, '_get_mounts', mock): - self.assertRaises(CommandExecutionError, btrfs.resize, - '/dev/sda1', 'max') + """ + with patch("salt.utils.fsutils._is_device", MagicMock(return_value=True)): + mock = MagicMock(return_value={"/dev/sda1": False}) + with patch.object(salt.utils.fsutils, "_get_mounts", mock): + self.assertRaises( + CommandExecutionError, btrfs.resize, "/dev/sda1", "max" + ) def test_resize_size_error(self): - ''' + """ Test if it gives unknown size error - ''' - self.assertRaises(CommandExecutionError, btrfs.resize, - '/dev/sda1', '250m') + """ + self.assertRaises(CommandExecutionError, btrfs.resize, "/dev/sda1", "250m") # 'convert' function tests: 5 def test_convert(self): - ''' + """ Test if it convert ext2/3/4 to BTRFS - ''' - with patch('os.path.exists', MagicMock(return_value=True)): - ret = {'after': {'balance_log': 'Salt', - 'ext4_image': 'removed', - 'ext4_image_info': 'N/A', - 'fsck_status': 'N/A', - 'mount_point': None, - 'type': 'ext4'}, - 'before': {'fsck_status': 'Filesystem errors corrected', - 'mount_point': None, - 'type': 'ext4'}} - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value={'/dev/sda3': {'type': 'ext4'}}) - with patch.object(salt.utils.fsutils, '_blkid_output', mock): - mock = MagicMock(return_value={'/dev/sda3': [{'mount_point': None}]}) - with patch.object(salt.utils.fsutils, '_get_mounts', mock): - self.assertDictEqual(btrfs.convert('/dev/sda3', permanent=True), - ret) + """ + with patch("os.path.exists", MagicMock(return_value=True)): + ret = { + "after": { + "balance_log": "Salt", + "ext4_image": "removed", + "ext4_image_info": "N/A", + "fsck_status": "N/A", + "mount_point": None, + "type": "ext4", + }, + "before": { + "fsck_status": "Filesystem errors corrected", + "mount_point": None, + "type": "ext4", + }, + } + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(return_value={"/dev/sda3": {"type": "ext4"}}) + with patch.object(salt.utils.fsutils, "_blkid_output", mock): + mock = MagicMock( + return_value={"/dev/sda3": [{"mount_point": None}]} + ) + with patch.object(salt.utils.fsutils, "_get_mounts", mock): + self.assertDictEqual( + btrfs.convert("/dev/sda3", permanent=True), ret + ) def test_convert_device_error(self): - ''' + """ Test if it gives device not found error - ''' - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value={'/dev/sda1': False}) - with patch.object(salt.utils.fsutils, '_blkid_output', mock): - self.assertRaises(CommandExecutionError, btrfs.convert, - '/dev/sda1') + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "", "stdout": "Salt"}) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(return_value={"/dev/sda1": False}) + with patch.object(salt.utils.fsutils, "_blkid_output", mock): + self.assertRaises(CommandExecutionError, btrfs.convert, "/dev/sda1") def test_convert_filesystem_error(self): - ''' + """ Test if it gives file system error - ''' - with patch('salt.utils.fsutils._is_device', MagicMock(return_value=True)): - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value={'/dev/sda1': {'type': 'ext'}}) - with patch.object(salt.utils.fsutils, '_blkid_output', mock): - self.assertRaises(CommandExecutionError, btrfs.convert, - '/dev/sda1') + """ + with patch("salt.utils.fsutils._is_device", MagicMock(return_value=True)): + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(return_value={"/dev/sda1": {"type": "ext"}}) + with patch.object(salt.utils.fsutils, "_blkid_output", mock): + self.assertRaises(CommandExecutionError, btrfs.convert, "/dev/sda1") def test_convert_error(self): - ''' + """ Test if it gives error cannot convert root - ''' - with patch('salt.utils.fsutils._is_device', MagicMock(return_value=True)): - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value={'/dev/sda1': {'type': 'ext4', - 'mount_point': '/'}}) - with patch.object(salt.utils.fsutils, '_blkid_output', mock): - mock = MagicMock(return_value={'/dev/sda1': - [{'mount_point': '/'}]}) - with patch.object(salt.utils.fsutils, '_get_mounts', mock): - self.assertRaises(CommandExecutionError, btrfs.convert, - '/dev/sda1') + """ + with patch("salt.utils.fsutils._is_device", MagicMock(return_value=True)): + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + mock = MagicMock( + return_value={"/dev/sda1": {"type": "ext4", "mount_point": "/"}} + ) + with patch.object(salt.utils.fsutils, "_blkid_output", mock): + mock = MagicMock(return_value={"/dev/sda1": [{"mount_point": "/"}]}) + with patch.object(salt.utils.fsutils, "_get_mounts", mock): + self.assertRaises( + CommandExecutionError, btrfs.convert, "/dev/sda1" + ) def test_convert_migration_error(self): - ''' + """ Test if it gives migration error - ''' - with patch('salt.utils.fsutils._is_device', MagicMock(return_value=True)): - mock_run = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock_run}): - mock_blk = MagicMock(return_value={'/dev/sda1': {'type': 'ext4'}}) - with patch.object(salt.utils.fsutils, '_blkid_output', mock_blk): - mock_file = mock_open(read_data='/dev/sda1 / ext4 rw,data=ordered 0 0') - with patch.object(salt.utils.files, 'fopen', mock_file): - self.assertRaises(CommandExecutionError, btrfs.convert, - '/dev/sda1') + """ + with patch("salt.utils.fsutils._is_device", MagicMock(return_value=True)): + mock_run = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock_run}): + mock_blk = MagicMock(return_value={"/dev/sda1": {"type": "ext4"}}) + with patch.object(salt.utils.fsutils, "_blkid_output", mock_blk): + mock_file = mock_open( + read_data="/dev/sda1 / ext4 rw,data=ordered 0 0" + ) + with patch.object(salt.utils.files, "fopen", mock_file): + self.assertRaises( + CommandExecutionError, btrfs.convert, "/dev/sda1" + ) # 'add' function tests: 1 def test_add(self): - ''' + """ Test if it add a devices to a BTRFS filesystem. - ''' - with patch('salt.modules.btrfs._restripe', MagicMock(return_value={})): - self.assertDictEqual(btrfs.add('/mountpoint', '/dev/sda1', '/dev/sda2'), {}) + """ + with patch("salt.modules.btrfs._restripe", MagicMock(return_value={})): + self.assertDictEqual(btrfs.add("/mountpoint", "/dev/sda1", "/dev/sda2"), {}) # 'delete' function tests: 1 def test_delete(self): - ''' + """ Test if it delete a devices to a BTRFS filesystem. - ''' - with patch('salt.modules.btrfs._restripe', MagicMock(return_value={})): - self.assertDictEqual(btrfs.delete('/mountpoint', '/dev/sda1', - '/dev/sda2'), {}) + """ + with patch("salt.modules.btrfs._restripe", MagicMock(return_value={})): + self.assertDictEqual( + btrfs.delete("/mountpoint", "/dev/sda1", "/dev/sda2"), {} + ) # 'properties' function tests: 1 def test_properties(self): - ''' + """ Test if list properties for given btrfs object - ''' - with patch('salt.utils.fsutils._verify_run', MagicMock(return_value=True)): - mock = MagicMock(return_value={'retcode': 1, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(btrfs.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(btrfs.properties('/dev/sda1', 'subvol'), {}) + """ + with patch("salt.utils.fsutils._verify_run", MagicMock(return_value=True)): + mock = MagicMock( + return_value={"retcode": 1, "stderr": "", "stdout": "Salt"} + ) + with patch.dict(btrfs.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual(btrfs.properties("/dev/sda1", "subvol"), {}) def test_properties_unknown_error(self): - ''' + """ Test if it gives unknown property error - ''' - self.assertRaises(CommandExecutionError, btrfs.properties, - '/dev/sda1', 'a') + """ + self.assertRaises(CommandExecutionError, btrfs.properties, "/dev/sda1", "a") def test_properties_error(self): - ''' + """ Test if it gives exception error - ''' - self.assertRaises(CommandExecutionError, btrfs.properties, - '/dev/sda1', 'subvol', True) + """ + self.assertRaises( + CommandExecutionError, btrfs.properties, "/dev/sda1", "subvol", True + ) def test_subvolume_exists(self): - ''' + """ Test subvolume_exists - ''' + """ salt_mock = { - 'cmd.retcode': MagicMock(return_value=0), + "cmd.retcode": MagicMock(return_value=0), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_exists('/mnt/one') + assert btrfs.subvolume_exists("/mnt/one") def test_subvolume_not_exists(self): - ''' + """ Test subvolume_exists - ''' + """ salt_mock = { - 'cmd.retcode': MagicMock(return_value=1), + "cmd.retcode": MagicMock(return_value=1), } with patch.dict(btrfs.__salt__, salt_mock): - assert not btrfs.subvolume_exists('/mnt/nowhere') + assert not btrfs.subvolume_exists("/mnt/nowhere") def test_subvolume_create_fails_parameters(self): - ''' + """ Test btrfs subvolume create - ''' + """ # Fails when qgroupids is not a list with pytest.raises(CommandExecutionError): - btrfs.subvolume_create('var', qgroupids='1') + btrfs.subvolume_create("var", qgroupids="1") - @patch('salt.modules.btrfs.subvolume_exists') + @patch("salt.modules.btrfs.subvolume_exists") def test_subvolume_create_already_exists(self, subvolume_exists): - ''' + """ Test btrfs subvolume create - ''' + """ subvolume_exists.return_value = True - assert not btrfs.subvolume_create('var', dest='/mnt') + assert not btrfs.subvolume_create("var", dest="/mnt") - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows') - @patch('salt.modules.btrfs.subvolume_exists') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows") + @patch("salt.modules.btrfs.subvolume_exists") def test_subvolume_create(self, subvolume_exists): - ''' + """ Test btrfs subvolume create - ''' + """ subvolume_exists.return_value = False salt_mock = { - 'cmd.run_all': MagicMock(return_value={'recode': 0}), + "cmd.run_all": MagicMock(return_value={"recode": 0}), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_create('var', dest='/mnt') + assert btrfs.subvolume_create("var", dest="/mnt") subvolume_exists.assert_called_once() - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'create', '/mnt/var']) + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "create", "/mnt/var"] + ) def test_subvolume_delete_fails_parameters(self): - ''' + """ Test btrfs subvolume delete - ''' + """ # We need to provide name or names with pytest.raises(CommandExecutionError): btrfs.subvolume_delete() with pytest.raises(CommandExecutionError): - btrfs.subvolume_delete(names='var') + btrfs.subvolume_delete(names="var") def test_subvolume_delete_fails_parameter_commit(self): - ''' + """ Test btrfs subvolume delete - ''' + """ # Parameter commit can be 'after' or 'each' with pytest.raises(CommandExecutionError): - btrfs.subvolume_delete(name='var', commit='maybe') + btrfs.subvolume_delete(name="var", commit="maybe") - @patch('salt.modules.btrfs.subvolume_exists') + @patch("salt.modules.btrfs.subvolume_exists") def test_subvolume_delete_already_missing(self, subvolume_exists): - ''' + """ Test btrfs subvolume delete - ''' + """ subvolume_exists.return_value = False - assert not btrfs.subvolume_delete(name='var', names=['tmp']) + assert not btrfs.subvolume_delete(name="var", names=["tmp"]) - @patch('salt.modules.btrfs.subvolume_exists') + @patch("salt.modules.btrfs.subvolume_exists") def test_subvolume_delete_already_missing_name(self, subvolume_exists): - ''' + """ Test btrfs subvolume delete - ''' + """ subvolume_exists.return_value = False - assert not btrfs.subvolume_delete(name='var') + assert not btrfs.subvolume_delete(name="var") - @patch('salt.modules.btrfs.subvolume_exists') + @patch("salt.modules.btrfs.subvolume_exists") def test_subvolume_delete_already_missing_names(self, subvolume_exists): - ''' + """ Test btrfs subvolume delete - ''' + """ subvolume_exists.return_value = False - assert not btrfs.subvolume_delete(names=['tmp']) + assert not btrfs.subvolume_delete(names=["tmp"]) - @patch('salt.modules.btrfs.subvolume_exists') + @patch("salt.modules.btrfs.subvolume_exists") def test_subvolume_delete(self, subvolume_exists): - ''' + """ Test btrfs subvolume delete - ''' + """ subvolume_exists.return_value = True salt_mock = { - 'cmd.run_all': MagicMock(return_value={'recode': 0}), + "cmd.run_all": MagicMock(return_value={"recode": 0}), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_delete('var', names=['tmp']) - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'delete', 'var', 'tmp']) + assert btrfs.subvolume_delete("var", names=["tmp"]) + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "delete", "var", "tmp"] + ) def test_subvolume_find_new_empty(self): - ''' + """ Test btrfs subvolume find-new - ''' + """ salt_mock = { - 'cmd.run_all': MagicMock(return_value={ - 'recode': 0, - 'stdout': 'transid marker was 1024' - }), + "cmd.run_all": MagicMock( + return_value={"recode": 0, "stdout": "transid marker was 1024"} + ), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_find_new('var', '2000') == { - 'files': [], - 'transid': '1024' + assert btrfs.subvolume_find_new("var", "2000") == { + "files": [], + "transid": "1024", } - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'find-new', 'var', '2000']) + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "find-new", "var", "2000"] + ) def test_subvolume_find_new(self): - ''' + """ Test btrfs subvolume find-new - ''' + """ salt_mock = { - 'cmd.run_all': MagicMock(return_value={ - 'recode': 0, - 'stdout': '''inode 185148 ... gen 2108 flags NONE var/log/audit/audit.log + "cmd.run_all": MagicMock( + return_value={ + "recode": 0, + "stdout": """inode 185148 ... gen 2108 flags NONE var/log/audit/audit.log inode 187390 ... INLINE etc/openvpn/openvpn-status.log -transid marker was 1024''' - }), +transid marker was 1024""", + } + ), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_find_new('var', '1023') == { - 'files': ['var/log/audit/audit.log', - 'etc/openvpn/openvpn-status.log'], - 'transid': '1024' + assert btrfs.subvolume_find_new("var", "1023") == { + "files": ["var/log/audit/audit.log", "etc/openvpn/openvpn-status.log"], + "transid": "1024", } - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'find-new', 'var', '1023']) + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "find-new", "var", "1023"] + ) def test_subvolume_get_default_free(self): - ''' + """ Test btrfs subvolume get-default - ''' + """ salt_mock = { - 'cmd.run_all': MagicMock(return_value={ - 'recode': 0, - 'stdout': 'ID 5 (FS_TREE)', - }), + "cmd.run_all": MagicMock( + return_value={"recode": 0, "stdout": "ID 5 (FS_TREE)"} + ), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_get_default('/mnt') == { - 'id': '5', - 'name': '(FS_TREE)', + assert btrfs.subvolume_get_default("/mnt") == { + "id": "5", + "name": "(FS_TREE)", } - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'get-default', '/mnt']) + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "get-default", "/mnt"] + ) def test_subvolume_get_default(self): - ''' + """ Test btrfs subvolume get-default - ''' + """ salt_mock = { - 'cmd.run_all': MagicMock(return_value={ - 'recode': 0, - 'stdout': 'ID 257 gen 8 top level 5 path var', - }), + "cmd.run_all": MagicMock( + return_value={ + "recode": 0, + "stdout": "ID 257 gen 8 top level 5 path var", + } + ), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_get_default('/mnt') == { - 'id': '257', - 'name': 'var', + assert btrfs.subvolume_get_default("/mnt") == { + "id": "257", + "name": "var", } - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'get-default', '/mnt']) + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "get-default", "/mnt"] + ) def test_subvolume_list_fails_parameters(self): - ''' + """ Test btrfs subvolume list - ''' + """ # Fails when sort is not a list with pytest.raises(CommandExecutionError): - btrfs.subvolume_list('/mnt', sort='-rootid') + btrfs.subvolume_list("/mnt", sort="-rootid") # Fails when sort is not recognized with pytest.raises(CommandExecutionError): - btrfs.subvolume_list('/mnt', sort=['-root']) + btrfs.subvolume_list("/mnt", sort=["-root"]) def test_subvolume_list_simple(self): - ''' + """ Test btrfs subvolume list - ''' + """ salt_mock = { - 'cmd.run_all': MagicMock(return_value={ - 'recode': 0, - 'stdout': '''ID 257 gen 8 top level 5 path one + "cmd.run_all": MagicMock( + return_value={ + "recode": 0, + "stdout": """ID 257 gen 8 top level 5 path one ID 258 gen 10 top level 5 path another one -''', - }), +""", + } + ), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_list('/mnt') == [ - { - 'id': '257', - 'gen': '8', - 'top level': '5', - 'path': 'one', - }, - { - 'id': '258', - 'gen': '10', - 'top level': '5', - 'path': 'another one', - }, + assert btrfs.subvolume_list("/mnt") == [ + {"id": "257", "gen": "8", "top level": "5", "path": "one"}, + {"id": "258", "gen": "10", "top level": "5", "path": "another one"}, ] - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'list', '/mnt']) + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "list", "/mnt"] + ) def test_subvolume_list(self): - ''' + """ Test btrfs subvolume list - ''' + """ salt_mock = { - 'cmd.run_all': MagicMock(return_value={ - 'recode': 0, - 'stdout': '''\ + "cmd.run_all": MagicMock( + return_value={ + "recode": 0, + "stdout": """\ ID 257 gen 8 cgen 8 parent 5 top level 5 parent_uuid - received_uuid - \ uuid 777...-..05 path one ID 258 gen 10 cgen 10 parent 5 top level 5 parent_uuid - received_uuid - \ uuid a90...-..01 path another one -''', - }), +""", + } + ), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_list('/mnt', parent_id=True, - absolute=True, - ogeneration=True, - generation=True, - subvolumes=True, uuid=True, - parent_uuid=True, - sent_subvolume_uuid=True, - generation_cmp='-100', - ogeneration_cmp='+5', - sort=['-rootid', 'gen']) == [ + assert btrfs.subvolume_list( + "/mnt", + parent_id=True, + absolute=True, + ogeneration=True, + generation=True, + subvolumes=True, + uuid=True, + parent_uuid=True, + sent_subvolume_uuid=True, + generation_cmp="-100", + ogeneration_cmp="+5", + sort=["-rootid", "gen"], + ) == [ { - 'id': '257', - 'gen': '8', - 'cgen': '8', - 'parent': '5', - 'top level': '5', - 'parent_uuid': '-', - 'received_uuid': '-', - 'uuid': '777...-..05', - 'path': 'one', + "id": "257", + "gen": "8", + "cgen": "8", + "parent": "5", + "top level": "5", + "parent_uuid": "-", + "received_uuid": "-", + "uuid": "777...-..05", + "path": "one", }, { - 'id': '258', - 'gen': '10', - 'cgen': '10', - 'parent': '5', - 'top level': '5', - 'parent_uuid': '-', - 'received_uuid': '-', - 'uuid': 'a90...-..01', - 'path': 'another one', + "id": "258", + "gen": "10", + "cgen": "10", + "parent": "5", + "top level": "5", + "parent_uuid": "-", + "received_uuid": "-", + "uuid": "a90...-..01", + "path": "another one", }, ] - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'list', '-p', '-a', '-c', '-g', - '-o', '-u', '-q', '-R', '-G', '-100', '-C', '+5', - '--sort=-rootid,gen', '/mnt']) + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + [ + "btrfs", + "subvolume", + "list", + "-p", + "-a", + "-c", + "-g", + "-o", + "-u", + "-q", + "-R", + "-G", + "-100", + "-C", + "+5", + "--sort=-rootid,gen", + "/mnt", + ] + ) def test_subvolume_set_default(self): - ''' + """ Test btrfs subvolume set-default - ''' + """ salt_mock = { - 'cmd.run_all': MagicMock(return_value={'recode': 0}), + "cmd.run_all": MagicMock(return_value={"recode": 0}), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_set_default('257', '/mnt') - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'set-default', '257', '/mnt']) + assert btrfs.subvolume_set_default("257", "/mnt") + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "set-default", "257", "/mnt"] + ) def test_subvolume_show(self): - ''' + """ Test btrfs subvolume show - ''' + """ salt_mock = { - 'cmd.run_all': MagicMock(return_value={ - 'recode': 0, - 'stdout': '''@/var + "cmd.run_all": MagicMock( + return_value={ + "recode": 0, + "stdout": """@/var Name: var UUID: 7a14...-...04 Parent UUID: - @@ -684,48 +725,50 @@ ID 258 gen 10 cgen 10 parent 5 top level 5 parent_uuid - received_uuid - \ Top level ID: 256 Flags: - Snapshot(s): -''', - }), +""", + } + ), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_show('/var') == { - '@/var': { - 'name': 'var', - 'uuid': '7a14...-...04', - 'parent uuid': '-', - 'received uuid': '-', - 'creation time': '2018-10-01 14:33:12 +0200', - 'subvolume id': '258', - 'generation': '82479', - 'gen at creation': '10', - 'parent id': '256', - 'top level id': '256', - 'flags': '-', - 'snapshot(s)': '', + assert btrfs.subvolume_show("/var") == { + "@/var": { + "name": "var", + "uuid": "7a14...-...04", + "parent uuid": "-", + "received uuid": "-", + "creation time": "2018-10-01 14:33:12 +0200", + "subvolume id": "258", + "generation": "82479", + "gen at creation": "10", + "parent id": "256", + "top level id": "256", + "flags": "-", + "snapshot(s)": "", }, } - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'show', '/var']) + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "show", "/var"] + ) def test_subvolume_sync_fail_parameters(self): - ''' + """ Test btrfs subvolume sync - ''' + """ # Fails when subvolids is not a list with pytest.raises(CommandExecutionError): - btrfs.subvolume_sync('/mnt', subvolids='257') + btrfs.subvolume_sync("/mnt", subvolids="257") def test_subvolume_sync(self): - ''' + """ Test btrfs subvolume sync - ''' + """ salt_mock = { - 'cmd.run_all': MagicMock(return_value={'recode': 0}), + "cmd.run_all": MagicMock(return_value={"recode": 0}), } with patch.dict(btrfs.__salt__, salt_mock): - assert btrfs.subvolume_sync('/mnt', subvolids=['257'], - sleep='1') - salt_mock['cmd.run_all'].assert_called_once() - salt_mock['cmd.run_all'].assert_called_with( - ['btrfs', 'subvolume', 'sync', '-s', '1', '/mnt', '257']) + assert btrfs.subvolume_sync("/mnt", subvolids=["257"], sleep="1") + salt_mock["cmd.run_all"].assert_called_once() + salt_mock["cmd.run_all"].assert_called_with( + ["btrfs", "subvolume", "sync", "-s", "1", "/mnt", "257"] + ) diff --git a/tests/unit/modules/test_cassandra.py b/tests/unit/modules/test_cassandra.py index 4f561682ef4..01133e7d0d9 100644 --- a/tests/unit/modules/test_cassandra.py +++ b/tests/unit/modules/test_cassandra.py @@ -1,83 +1,82 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.cassandra as cassandra # Import Salt Libs from salt.ext import six -import salt.modules.cassandra as cassandra + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class CassandraTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.cassandra - ''' + """ + def setup_loader_modules(self): return {cassandra: {}} def test_compactionstats(self): - ''' + """ Test for Return compactionstats info - ''' - mock = MagicMock(return_value='A') - with patch.object(cassandra, '_nodetool', mock): - self.assertEqual(cassandra.compactionstats(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(cassandra, "_nodetool", mock): + self.assertEqual(cassandra.compactionstats(), "A") def test_version(self): - ''' + """ Test for Return the cassandra version - ''' - mock = MagicMock(return_value='A') - with patch.object(cassandra, '_nodetool', mock): - self.assertEqual(cassandra.version(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(cassandra, "_nodetool", mock): + self.assertEqual(cassandra.version(), "A") def test_netstats(self): - ''' + """ Test for Return netstats info - ''' - mock = MagicMock(return_value='A') - with patch.object(cassandra, '_nodetool', mock): - self.assertEqual(cassandra.netstats(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(cassandra, "_nodetool", mock): + self.assertEqual(cassandra.netstats(), "A") def test_tpstats(self): - ''' + """ Test for Return tpstats info - ''' - mock = MagicMock(return_value='A') - with patch.object(cassandra, '_nodetool', mock): - self.assertEqual(cassandra.tpstats(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(cassandra, "_nodetool", mock): + self.assertEqual(cassandra.tpstats(), "A") def test_info(self): - ''' + """ Test for Return cassandra node info - ''' - mock = MagicMock(return_value='A') - with patch.object(cassandra, '_nodetool', mock): - self.assertEqual(cassandra.info(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(cassandra, "_nodetool", mock): + self.assertEqual(cassandra.info(), "A") def test_ring(self): - ''' + """ Test for Return ring info - ''' - mock = MagicMock(return_value='A') - with patch.object(cassandra, '_nodetool', mock): - self.assertEqual(cassandra.ring(), 'A') + """ + mock = MagicMock(return_value="A") + with patch.object(cassandra, "_nodetool", mock): + self.assertEqual(cassandra.ring(), "A") def test_keyspaces(self): - ''' + """ Test for Return existing keyspaces - ''' - mock_keyspaces = ['A', 'B', 'C', 'D'] + """ + mock_keyspaces = ["A", "B", "C", "D"] class MockSystemManager(object): def list_keyspaces(self): @@ -85,58 +84,56 @@ class CassandraTestCase(TestCase, LoaderModuleMockMixin): mock_sys_mgr = MagicMock(return_value=MockSystemManager()) - with patch.object(cassandra, '_sys_mgr', mock_sys_mgr): + with patch.object(cassandra, "_sys_mgr", mock_sys_mgr): self.assertEqual(cassandra.keyspaces(), mock_keyspaces) def test_column_families(self): - ''' + """ Test for Return existing column families for all keyspaces - ''' - mock_keyspaces = ['A', 'B'] + """ + mock_keyspaces = ["A", "B"] class MockSystemManager(object): def list_keyspaces(self): return mock_keyspaces def get_keyspace_column_families(self, keyspace): - if keyspace == 'A': - return {'a': 'saltines', 'b': 'biscuits'} - if keyspace == 'B': - return {'c': 'cheese', 'd': 'crackers'} + if keyspace == "A": + return {"a": "saltines", "b": "biscuits"} + if keyspace == "B": + return {"c": "cheese", "d": "crackers"} mock_sys_mgr = MagicMock(return_value=MockSystemManager()) - with patch.object(cassandra, '_sys_mgr', mock_sys_mgr): - self.assertEqual(cassandra.column_families('Z'), - None) + with patch.object(cassandra, "_sys_mgr", mock_sys_mgr): + self.assertEqual(cassandra.column_families("Z"), None) if six.PY3: - self.assertCountEqual(cassandra.column_families('A'), - ['a', 'b']) - self.assertCountEqual(cassandra.column_families(), - {'A': ['a', 'b'], 'B': ['c', 'd']}) + self.assertCountEqual(cassandra.column_families("A"), ["a", "b"]) + self.assertCountEqual( + cassandra.column_families(), {"A": ["a", "b"], "B": ["c", "d"]} + ) else: - self.assertEqual(sorted(cassandra.column_families('A')), - ['a', 'b']) + self.assertEqual(sorted(cassandra.column_families("A")), ["a", "b"]) column_families = cassandra.column_families() - for key in ('A', 'B'): + for key in ("A", "B"): column_families[key] = sorted(column_families[key]) - self.assertEqual(column_families, - {'A': ['a', 'b'], 'B': ['c', 'd']}) + self.assertEqual(column_families, {"A": ["a", "b"], "B": ["c", "d"]}) def test_column_family_definition(self): - ''' + """ Test for Return a dictionary of column family definitions for the given keyspace/column_family - ''' + """ + class MockSystemManager(object): def get_keyspace_column_families(self, keyspace): - if keyspace == 'A': - return {'a': object, 'b': object} - if keyspace == 'B': + if keyspace == "A": + return {"a": object, "b": object} + if keyspace == "B": raise Exception mock_sys_mgr = MagicMock(return_value=MockSystemManager()) - with patch.object(cassandra, '_sys_mgr', mock_sys_mgr): - self.assertEqual(cassandra.column_family_definition('A', 'a'), vars(object)) - self.assertEqual(cassandra.column_family_definition('B', 'a'), None) + with patch.object(cassandra, "_sys_mgr", mock_sys_mgr): + self.assertEqual(cassandra.column_family_definition("A", "a"), vars(object)) + self.assertEqual(cassandra.column_family_definition("B", "a"), None) diff --git a/tests/unit/modules/test_cassandra_cql.py b/tests/unit/modules/test_cassandra_cql.py index 664ba04597b..bb81d66c381 100644 --- a/tests/unit/modules/test_cassandra_cql.py +++ b/tests/unit/modules/test_cassandra_cql.py @@ -1,24 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.returners.cassandra_cql_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import ssl +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, MagicMock +import ssl # Import salt libs import salt.modules.cassandra_cql as cassandra_cql from salt.exceptions import CommandExecutionError +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + try: import cassandra # pylint: disable=unused-import,wrong-import-position + HAS_CASSANDRA = True except ImportError: HAS_CASSANDRA = False @@ -26,56 +28,69 @@ except ImportError: @skipIf( not HAS_CASSANDRA, - 'Please install the cassandra datastax driver to run cassandra_cql module unit tests.' + "Please install the cassandra datastax driver to run cassandra_cql module unit tests.", ) class CassandraCQLReturnerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cassandra CQL module - ''' + """ + def setup_loader_modules(self): return {cassandra_cql: {}} def test_returns_opts_if_specified(self): - ''' + """ If ssl options are present then check that they are parsed and returned - ''' - options = MagicMock(return_value={ - 'cluster': [ - '192.168.50.10', '192.168.50.11', '192.168.50.12'], - 'port': 9000, - 'ssl_options': { - 'ca_certs': '/etc/ssl/certs/ca-bundle.trust.crt', - 'ssl_version': 'PROTOCOL_TLSv1'}, - 'username': 'cas_admin'} + """ + options = MagicMock( + return_value={ + "cluster": ["192.168.50.10", "192.168.50.11", "192.168.50.12"], + "port": 9000, + "ssl_options": { + "ca_certs": "/etc/ssl/certs/ca-bundle.trust.crt", + "ssl_version": "PROTOCOL_TLSv1", + }, + "username": "cas_admin", + } ) - with patch.dict(cassandra_cql.__salt__, {'config.option': options}): + with patch.dict(cassandra_cql.__salt__, {"config.option": options}): - self.assertEqual(cassandra_cql._get_ssl_opts(), { # pylint: disable=protected-access - 'ca_certs': '/etc/ssl/certs/ca-bundle.trust.crt', 'ssl_version': ssl.PROTOCOL_TLSv1}) # pylint: disable=no-member + self.assertEqual( + cassandra_cql._get_ssl_opts(), + { # pylint: disable=protected-access + "ca_certs": "/etc/ssl/certs/ca-bundle.trust.crt", + "ssl_version": ssl.PROTOCOL_TLSv1, + }, + ) # pylint: disable=no-member def test_invalid_protocol_version(self): - ''' + """ Check that the protocol version is imported only if it isvalid - ''' - options = MagicMock(return_value={ - 'cluster': [ - '192.168.50.10', '192.168.50.11', '192.168.50.12'], - 'port': 9000, - 'ssl_options': { - 'ca_certs': '/etc/ssl/certs/ca-bundle.trust.crt', - 'ssl_version': 'Invalid'}, - 'username': 'cas_admin'} + """ + options = MagicMock( + return_value={ + "cluster": ["192.168.50.10", "192.168.50.11", "192.168.50.12"], + "port": 9000, + "ssl_options": { + "ca_certs": "/etc/ssl/certs/ca-bundle.trust.crt", + "ssl_version": "Invalid", + }, + "username": "cas_admin", + } ) - with patch.dict(cassandra_cql.__salt__, {'config.option': options}): + with patch.dict(cassandra_cql.__salt__, {"config.option": options}): with self.assertRaises(CommandExecutionError): cassandra_cql._get_ssl_opts() # pylint: disable=protected-access def test_unspecified_opts(self): - ''' + """ Check that it returns None when ssl opts aren't specified - ''' - with patch.dict(cassandra_cql.__salt__, {'config.option': MagicMock(return_value={})}): - self.assertEqual(cassandra_cql._get_ssl_opts(), # pylint: disable=protected-access - None) + """ + with patch.dict( + cassandra_cql.__salt__, {"config.option": MagicMock(return_value={})} + ): + self.assertEqual( + cassandra_cql._get_ssl_opts(), None # pylint: disable=protected-access + ) diff --git a/tests/unit/modules/test_chef.py b/tests/unit/modules/test_chef.py index 6dff5113437..fda499f5f7b 100644 --- a/tests/unit/modules/test_chef.py +++ b/tests/unit/modules/test_chef.py @@ -1,46 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.chef as chef +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ChefTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.chef - ''' + """ + def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', MagicMock(return_value=True)) + patcher = patch("salt.utils.path.which", MagicMock(return_value=True)) patcher.start() self.addCleanup(patcher.stop) - return {chef: {'_exec_cmd': MagicMock(return_value={})}} + return {chef: {"_exec_cmd": MagicMock(return_value={})}} # 'client' function tests: 1 def test_client(self): - ''' + """ Test if it execute a chef client run and return a dict - ''' - with patch.dict(chef.__opts__, {'cachedir': r'c:\salt\var\cache\salt\minion'}): + """ + with patch.dict(chef.__opts__, {"cachedir": r"c:\salt\var\cache\salt\minion"}): self.assertDictEqual(chef.client(), {}) # 'solo' function tests: 1 def test_solo(self): - ''' + """ Test if it execute a chef solo run and return a dict - ''' - with patch.dict(chef.__opts__, {'cachedir': r'c:\salt\var\cache\salt\minion'}): - self.assertDictEqual(chef.solo('/dev/sda1'), {}) + """ + with patch.dict(chef.__opts__, {"cachedir": r"c:\salt\var\cache\salt\minion"}): + self.assertDictEqual(chef.solo("/dev/sda1"), {}) diff --git a/tests/unit/modules/test_chocolatey.py b/tests/unit/modules/test_chocolatey.py index e3f2987f080..66ebef7d8d7 100644 --- a/tests/unit/modules/test_chocolatey.py +++ b/tests/unit/modules/test_chocolatey.py @@ -1,36 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Test for the chocolatey module -''' +""" # Import Python libs from __future__ import absolute_import + import os # Import Salt Libs import salt.modules.chocolatey as chocolatey -import salt.utils.platform import salt.utils +import salt.utils.platform # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -@skipIf(not salt.utils.platform.is_windows(), 'Not a Windows system') +@skipIf(not salt.utils.platform.is_windows(), "Not a Windows system") class ChocolateyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Chocolatey private functions tests - ''' + """ @classmethod def setUpClass(cls): - cls.choco_path = 'C:\\path\\to\\chocolatey.exe' + cls.choco_path = "C:\\path\\to\\chocolatey.exe" cls.choco_path_pd = os.path.join( - os.environ.get('ProgramData'), 'Chocolatey', 'bin', 'chocolatey.exe') + os.environ.get("ProgramData"), "Chocolatey", "bin", "chocolatey.exe" + ) cls.choco_path_sd = os.path.join( - os.environ.get('SystemDrive'), 'Chocolatey', 'bin', 'chocolatey.bat') + os.environ.get("SystemDrive"), "Chocolatey", "bin", "chocolatey.bat" + ) cls.mock_false = MagicMock(return_value=False) cls.mock_true = MagicMock(return_value=True) @@ -43,112 +46,106 @@ class ChocolateyTestCase(TestCase, LoaderModuleMockMixin): del cls.mock_true def setup_loader_modules(self): - return {chocolatey: { - '__context__': {}, - '__salt__': {} - }} + return {chocolatey: {"__context__": {}, "__salt__": {}}} def test__clear_context(self): - ''' + """ Tests _clear_context function - ''' - context = {'chocolatey._yes': ['--yes'], - 'chocolatey._path': self.choco_path, - 'chocolatey._version': '0.9.9'} + """ + context = { + "chocolatey._yes": ["--yes"], + "chocolatey._path": self.choco_path, + "chocolatey._version": "0.9.9", + } with patch.dict(chocolatey.__context__, context): chocolatey._clear_context() # Did it clear all chocolatey items from __context__P? self.assertEqual(chocolatey.__context__, {}) def test__yes_context(self): - ''' + """ Tests _yes function when it exists in __context__ - ''' - with patch.dict(chocolatey.__context__, {'chocolatey._yes': ['--yes']}): + """ + with patch.dict(chocolatey.__context__, {"chocolatey._yes": ["--yes"]}): result = chocolatey._yes() - expected = ['--yes'] + expected = ["--yes"] # Did it return correctly self.assertListEqual(result, expected) # Did it populate __context__ - self.assertEqual(chocolatey.__context__['chocolatey._yes'], - expected) + self.assertEqual(chocolatey.__context__["chocolatey._yes"], expected) def test__yes_version_greater(self): - ''' + """ Test _yes when Chocolatey version is greater than 0.9.9 - ''' - mock_version = MagicMock(return_value='10.0.0') - with patch('salt.modules.chocolatey.chocolatey_version', mock_version): + """ + mock_version = MagicMock(return_value="10.0.0") + with patch("salt.modules.chocolatey.chocolatey_version", mock_version): result = chocolatey._yes() - expected = ['--yes'] + expected = ["--yes"] # Did it return correctly self.assertListEqual(result, expected) # Did it populate __context__ - self.assertEqual(chocolatey.__context__['chocolatey._yes'], - expected) + self.assertEqual(chocolatey.__context__["chocolatey._yes"], expected) def test__yes_version_less_than(self): - ''' + """ Test _yes when Chocolatey version is less than 0.9.9 - ''' - mock_version = MagicMock(return_value='0.9.0') - with patch('salt.modules.chocolatey.chocolatey_version', mock_version): + """ + mock_version = MagicMock(return_value="0.9.0") + with patch("salt.modules.chocolatey.chocolatey_version", mock_version): result = chocolatey._yes() expected = [] # Did it return correctly self.assertListEqual(result, expected) # Did it populate __context__ - self.assertEqual(chocolatey.__context__['chocolatey._yes'], - expected) + self.assertEqual(chocolatey.__context__["chocolatey._yes"], expected) def test__find_chocolatey_context(self): - ''' + """ Test _find_chocolatey when it exists in __context__ - ''' - with patch.dict(chocolatey.__context__, - {'chocolatey._path': self.choco_path}): + """ + with patch.dict(chocolatey.__context__, {"chocolatey._path": self.choco_path}): result = chocolatey._find_chocolatey() expected = self.choco_path self.assertEqual(result, expected) def test__find_chocolatey_which(self): - ''' + """ Test _find_chocolatey when found with `cmd.which` - ''' + """ mock_which = MagicMock(return_value=self.choco_path) - with patch.dict(chocolatey.__salt__, {'cmd.which': mock_which}): + with patch.dict(chocolatey.__salt__, {"cmd.which": mock_which}): result = chocolatey._find_chocolatey() expected = self.choco_path # Does it return the correct path self.assertEqual(result, expected) # Does it populate __context__ - self.assertEqual(chocolatey.__context__['chocolatey._path'], - expected) + self.assertEqual(chocolatey.__context__["chocolatey._path"], expected) def test__find_chocolatey_programdata(self): - ''' + """ Test _find_chocolatey when found in ProgramData - ''' - with patch.dict(chocolatey.__salt__, {'cmd.which': self.mock_false}),\ - patch('os.path.isfile', self.mock_true): + """ + with patch.dict(chocolatey.__salt__, {"cmd.which": self.mock_false}), patch( + "os.path.isfile", self.mock_true + ): result = chocolatey._find_chocolatey() expected = self.choco_path_pd # Does it return the correct path self.assertEqual(result, expected) # Does it populate __context__ - self.assertEqual(chocolatey.__context__['chocolatey._path'], - expected) + self.assertEqual(chocolatey.__context__["chocolatey._path"], expected) def test__find_chocolatey_systemdrive(self): - ''' + """ Test _find_chocolatey when found on SystemDrive (older versions) - ''' - with patch.dict(chocolatey.__salt__, {'cmd.which': self.mock_false}),\ - patch('os.path.isfile', MagicMock(side_effect=[False, True])): + """ + with patch.dict(chocolatey.__salt__, {"cmd.which": self.mock_false}), patch( + "os.path.isfile", MagicMock(side_effect=[False, True]) + ): result = chocolatey._find_chocolatey() expected = self.choco_path_sd # Does it return the correct path self.assertEqual(result, expected) # Does it populate __context__ - self.assertEqual(chocolatey.__context__['chocolatey._path'], - expected) + self.assertEqual(chocolatey.__context__["chocolatey._path"], expected) diff --git a/tests/unit/modules/test_chroot.py b/tests/unit/modules/test_chroot.py index de3041e98fb..76811df46e7 100644 --- a/tests/unit/modules/test_chroot.py +++ b/tests/unit/modules/test_chroot.py @@ -21,257 +21,296 @@ # specific language governing permissions and limitations # under the License. -''' +""" :maintainer: Alberto Planas <aplanas@suse.com> :platform: Linux -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import sys +import salt.modules.chroot as chroot +import salt.utils.platform +from salt.exceptions import CommandExecutionError + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase from tests.support.mock import MagicMock, patch - -from salt.exceptions import CommandExecutionError -import salt.utils.platform -import salt.modules.chroot as chroot +from tests.support.unit import TestCase, skipIf -@skipIf(salt.utils.platform.is_windows(), 'This test cannot work on Windows') +@skipIf(salt.utils.platform.is_windows(), "This test cannot work on Windows") class ChrootTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.chroot - ''' + """ def setup_loader_modules(self): - return { - chroot: { - '__salt__': {}, - '__utils__': {}, - '__opts__': {'cachedir': ''}, - } - } + return {chroot: {"__salt__": {}, "__utils__": {}, "__opts__": {"cachedir": ""}}} - @patch('os.path.isdir') + @patch("os.path.isdir") def test_exist(self, isdir): - ''' + """ Test if the chroot environment exist. - ''' + """ isdir.side_effect = (True, True, True, True) - self.assertTrue(chroot.exist('/chroot')) + self.assertTrue(chroot.exist("/chroot")) isdir.side_effect = (True, True, True, False) - self.assertFalse(chroot.exist('/chroot')) + self.assertFalse(chroot.exist("/chroot")) - @patch('os.makedirs') - @patch('salt.modules.chroot.exist') + @patch("os.makedirs") + @patch("salt.modules.chroot.exist") def test_create(self, exist, makedirs): - ''' + """ Test the creation of an empty chroot environment. - ''' + """ exist.return_value = True - self.assertTrue(chroot.create('/chroot')) + self.assertTrue(chroot.create("/chroot")) makedirs.assert_not_called() exist.return_value = False - self.assertTrue(chroot.create('/chroot')) + self.assertTrue(chroot.create("/chroot")) makedirs.assert_called() - @patch('salt.modules.chroot.exist') + @patch("salt.modules.chroot.exist") def test_call_fails_input_validation(self, exist): - ''' + """ Test execution of Salt functions in chroot. - ''' + """ # Basic input validation exist.return_value = False - self.assertRaises(CommandExecutionError, chroot.call, '/chroot', '') - self.assertRaises(CommandExecutionError, chroot.call, '/chroot', - 'test.ping') + self.assertRaises(CommandExecutionError, chroot.call, "/chroot", "") + self.assertRaises(CommandExecutionError, chroot.call, "/chroot", "test.ping") - @patch('salt.modules.chroot.exist') - @patch('tempfile.mkdtemp') + @patch("salt.modules.chroot.exist") + @patch("tempfile.mkdtemp") def test_call_fails_untar(self, mkdtemp, exist): - ''' + """ Test execution of Salt functions in chroot. - ''' + """ # Fail the tar command exist.return_value = True - mkdtemp.return_value = '/chroot/tmp01' + mkdtemp.return_value = "/chroot/tmp01" utils_mock = { - 'thin.gen_thin': MagicMock(return_value='/salt-thin.tgz'), - 'files.rm_rf': MagicMock(), + "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"), + "files.rm_rf": MagicMock(), } salt_mock = { - 'cmd.run': MagicMock(return_value='Error'), - 'config.option': MagicMock(), + "cmd.run": MagicMock(return_value="Error"), + "config.option": MagicMock(), } - with patch.dict(chroot.__utils__, utils_mock), \ - patch.dict(chroot.__salt__, salt_mock): - self.assertEqual(chroot.call('/chroot', 'test.ping'), { - 'result': False, - 'comment': 'Error' - }) - utils_mock['thin.gen_thin'].assert_called_once() - salt_mock['config.option'].assert_called() - salt_mock['cmd.run'].assert_called_once() - utils_mock['files.rm_rf'].assert_called_once() + with patch.dict(chroot.__utils__, utils_mock), patch.dict( + chroot.__salt__, salt_mock + ): + self.assertEqual( + chroot.call("/chroot", "test.ping"), + {"result": False, "comment": "Error"}, + ) + utils_mock["thin.gen_thin"].assert_called_once() + salt_mock["config.option"].assert_called() + salt_mock["cmd.run"].assert_called_once() + utils_mock["files.rm_rf"].assert_called_once() - @patch('salt.modules.chroot.exist') - @patch('tempfile.mkdtemp') + @patch("salt.modules.chroot.exist") + @patch("tempfile.mkdtemp") def test_call_fails_salt_thin(self, mkdtemp, exist): - ''' + """ Test execution of Salt functions in chroot. - ''' + """ # Fail the inner command exist.return_value = True - mkdtemp.return_value = '/chroot/tmp01' + mkdtemp.return_value = "/chroot/tmp01" utils_mock = { - 'thin.gen_thin': MagicMock(return_value='/salt-thin.tgz'), - 'files.rm_rf': MagicMock(), - 'json.find_json': MagicMock(return_value={'return': {}}) + "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"), + "files.rm_rf": MagicMock(), + "json.find_json": MagicMock(return_value={"return": {}}), } salt_mock = { - 'cmd.run': MagicMock(return_value=''), - 'config.option': MagicMock(), - 'cmd.run_chroot': MagicMock(return_value={ - 'retcode': 1, - 'stderr': 'Error', - }), + "cmd.run": MagicMock(return_value=""), + "config.option": MagicMock(), + "cmd.run_chroot": MagicMock(return_value={"retcode": 1, "stderr": "Error"}), } - with patch.dict(chroot.__utils__, utils_mock), \ - patch.dict(chroot.__salt__, salt_mock): - self.assertEqual(chroot.call('/chroot', 'test.ping'), { - 'result': False, - 'comment': "Can't parse container command output" - }) - utils_mock['thin.gen_thin'].assert_called_once() - salt_mock['config.option'].assert_called() - salt_mock['cmd.run'].assert_called_once() - salt_mock['cmd.run_chroot'].assert_called_with( - '/chroot', - ['python{}'.format(sys.version_info[0]), '/tmp01/salt-call', - '--metadata', '--local', - '--log-file', '/tmp01/log', '--cachedir', '/tmp01/cache', - '--out', 'json', '-l', 'quiet', '--', 'test.ping']) - utils_mock['files.rm_rf'].assert_called_once() + with patch.dict(chroot.__utils__, utils_mock), patch.dict( + chroot.__salt__, salt_mock + ): + self.assertEqual( + chroot.call("/chroot", "test.ping"), + {"result": False, "comment": "Can't parse container command output"}, + ) + utils_mock["thin.gen_thin"].assert_called_once() + salt_mock["config.option"].assert_called() + salt_mock["cmd.run"].assert_called_once() + salt_mock["cmd.run_chroot"].assert_called_with( + "/chroot", + [ + "python{}".format(sys.version_info[0]), + "/tmp01/salt-call", + "--metadata", + "--local", + "--log-file", + "/tmp01/log", + "--cachedir", + "/tmp01/cache", + "--out", + "json", + "-l", + "quiet", + "--", + "test.ping", + ], + ) + utils_mock["files.rm_rf"].assert_called_once() - @patch('salt.modules.chroot.exist') - @patch('tempfile.mkdtemp') + @patch("salt.modules.chroot.exist") + @patch("tempfile.mkdtemp") def test_call_success(self, mkdtemp, exist): - ''' + """ Test execution of Salt functions in chroot. - ''' + """ # Success test exist.return_value = True - mkdtemp.return_value = '/chroot/tmp01' + mkdtemp.return_value = "/chroot/tmp01" utils_mock = { - 'thin.gen_thin': MagicMock(return_value='/salt-thin.tgz'), - 'files.rm_rf': MagicMock(), - 'json.find_json': MagicMock(return_value={'return': 'result'}) + "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"), + "files.rm_rf": MagicMock(), + "json.find_json": MagicMock(return_value={"return": "result"}), } salt_mock = { - 'cmd.run': MagicMock(return_value=''), - 'config.option': MagicMock(), - 'cmd.run_chroot': MagicMock(return_value={ - 'retcode': 0, - 'stdout': '', - }), + "cmd.run": MagicMock(return_value=""), + "config.option": MagicMock(), + "cmd.run_chroot": MagicMock(return_value={"retcode": 0, "stdout": ""}), } - with patch.dict(chroot.__utils__, utils_mock), \ - patch.dict(chroot.__salt__, salt_mock): - self.assertEqual(chroot.call('/chroot', 'test.ping'), 'result') - utils_mock['thin.gen_thin'].assert_called_once() - salt_mock['config.option'].assert_called() - salt_mock['cmd.run'].assert_called_once() - salt_mock['cmd.run_chroot'].assert_called_with( - '/chroot', - ['python{}'.format(sys.version_info[0]), '/tmp01/salt-call', - '--metadata', '--local', - '--log-file', '/tmp01/log', '--cachedir', '/tmp01/cache', - '--out', 'json', '-l', 'quiet', '--', 'test.ping']) - utils_mock['files.rm_rf'].assert_called_once() + with patch.dict(chroot.__utils__, utils_mock), patch.dict( + chroot.__salt__, salt_mock + ): + self.assertEqual(chroot.call("/chroot", "test.ping"), "result") + utils_mock["thin.gen_thin"].assert_called_once() + salt_mock["config.option"].assert_called() + salt_mock["cmd.run"].assert_called_once() + salt_mock["cmd.run_chroot"].assert_called_with( + "/chroot", + [ + "python{}".format(sys.version_info[0]), + "/tmp01/salt-call", + "--metadata", + "--local", + "--log-file", + "/tmp01/log", + "--cachedir", + "/tmp01/cache", + "--out", + "json", + "-l", + "quiet", + "--", + "test.ping", + ], + ) + utils_mock["files.rm_rf"].assert_called_once() - @patch('salt.modules.chroot.exist') - @patch('tempfile.mkdtemp') + @patch("salt.modules.chroot.exist") + @patch("tempfile.mkdtemp") def test_call_success_parameters(self, mkdtemp, exist): - ''' + """ Test execution of Salt functions in chroot with parameters. - ''' + """ # Success test exist.return_value = True - mkdtemp.return_value = '/chroot/tmp01' + mkdtemp.return_value = "/chroot/tmp01" utils_mock = { - 'thin.gen_thin': MagicMock(return_value='/salt-thin.tgz'), - 'files.rm_rf': MagicMock(), - 'json.find_json': MagicMock(return_value={'return': 'result'}) + "thin.gen_thin": MagicMock(return_value="/salt-thin.tgz"), + "files.rm_rf": MagicMock(), + "json.find_json": MagicMock(return_value={"return": "result"}), } salt_mock = { - 'cmd.run': MagicMock(return_value=''), - 'config.option': MagicMock(), - 'cmd.run_chroot': MagicMock(return_value={ - 'retcode': 0, - 'stdout': '', - }), + "cmd.run": MagicMock(return_value=""), + "config.option": MagicMock(), + "cmd.run_chroot": MagicMock(return_value={"retcode": 0, "stdout": ""}), } - with patch.dict(chroot.__utils__, utils_mock), \ - patch.dict(chroot.__salt__, salt_mock): - self.assertEqual(chroot.call('/chroot', 'module.function', - key='value'), 'result') - utils_mock['thin.gen_thin'].assert_called_once() - salt_mock['config.option'].assert_called() - salt_mock['cmd.run'].assert_called_once() - salt_mock['cmd.run_chroot'].assert_called_with( - '/chroot', - ['python{}'.format(sys.version_info[0]), '/tmp01/salt-call', - '--metadata', '--local', - '--log-file', '/tmp01/log', '--cachedir', '/tmp01/cache', - '--out', 'json', '-l', 'quiet', - '--', 'module.function', 'key=value']) - utils_mock['files.rm_rf'].assert_called_once() + with patch.dict(chroot.__utils__, utils_mock), patch.dict( + chroot.__salt__, salt_mock + ): + self.assertEqual( + chroot.call("/chroot", "module.function", key="value"), "result" + ) + utils_mock["thin.gen_thin"].assert_called_once() + salt_mock["config.option"].assert_called() + salt_mock["cmd.run"].assert_called_once() + salt_mock["cmd.run_chroot"].assert_called_with( + "/chroot", + [ + "python{}".format(sys.version_info[0]), + "/tmp01/salt-call", + "--metadata", + "--local", + "--log-file", + "/tmp01/log", + "--cachedir", + "/tmp01/cache", + "--out", + "json", + "-l", + "quiet", + "--", + "module.function", + "key=value", + ], + ) + utils_mock["files.rm_rf"].assert_called_once() - @patch('salt.modules.chroot._create_and_execute_salt_state') - @patch('salt.client.ssh.state.SSHHighState') - @patch('salt.fileclient.get_file_client') - @patch('salt.utils.state.get_sls_opts') - def test_sls(self, get_sls_opts, get_file_client, SSHHighState, - _create_and_execute_salt_state): - ''' + @patch("salt.modules.chroot._create_and_execute_salt_state") + @patch("salt.client.ssh.state.SSHHighState") + @patch("salt.fileclient.get_file_client") + @patch("salt.utils.state.get_sls_opts") + def test_sls( + self, + get_sls_opts, + get_file_client, + SSHHighState, + _create_and_execute_salt_state, + ): + """ Test execution of Salt states in chroot. - ''' + """ SSHHighState.return_value = SSHHighState SSHHighState.render_highstate.return_value = (None, []) SSHHighState.state.reconcile_extend.return_value = (None, []) SSHHighState.state.requisite_in.return_value = (None, []) SSHHighState.state.verify_high.return_value = [] - _create_and_execute_salt_state.return_value = 'result' + _create_and_execute_salt_state.return_value = "result" opts_mock = { - 'hash_type': 'md5', + "hash_type": "md5", } get_sls_opts.return_value = opts_mock with patch.dict(chroot.__opts__, opts_mock): - self.assertEqual(chroot.sls('/chroot', 'module'), 'result') + self.assertEqual(chroot.sls("/chroot", "module"), "result") _create_and_execute_salt_state.assert_called_once() - @patch('salt.modules.chroot._create_and_execute_salt_state') - @patch('salt.client.ssh.state.SSHHighState') - @patch('salt.fileclient.get_file_client') - @patch('salt.utils.state.get_sls_opts') - def test_highstate(self, get_sls_opts, get_file_client, SSHHighState, - _create_and_execute_salt_state): - ''' + @patch("salt.modules.chroot._create_and_execute_salt_state") + @patch("salt.client.ssh.state.SSHHighState") + @patch("salt.fileclient.get_file_client") + @patch("salt.utils.state.get_sls_opts") + def test_highstate( + self, + get_sls_opts, + get_file_client, + SSHHighState, + _create_and_execute_salt_state, + ): + """ Test execution of Salt states in chroot. - ''' + """ SSHHighState.return_value = SSHHighState - _create_and_execute_salt_state.return_value = 'result' + _create_and_execute_salt_state.return_value = "result" opts_mock = { - 'hash_type': 'md5', + "hash_type": "md5", } get_sls_opts.return_value = opts_mock with patch.dict(chroot.__opts__, opts_mock): - self.assertEqual(chroot.highstate('/chroot'), 'result') + self.assertEqual(chroot.highstate("/chroot"), "result") _create_and_execute_salt_state.assert_called_once() diff --git a/tests/unit/modules/test_cmdmod.py b/tests/unit/modules/test_cmdmod.py index 8170a56b4e1..78146b825e5 100644 --- a/tests/unit/modules/test_cmdmod.py +++ b/tests/unit/modules/test_cmdmod.py @@ -1,58 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os import sys import tempfile +import salt.modules.cmdmod as cmdmod + # Import Salt Libs import salt.utils.files import salt.utils.platform import salt.utils.stringutils -import salt.modules.cmdmod as cmdmod from salt.exceptions import CommandExecutionError -from salt.log import LOG_LEVELS from salt.ext.six.moves import builtins # pylint: disable=import-error +from salt.log import LOG_LEVELS +from tests.support.helpers import TstSuiteLoggingHandler # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf +from tests.support.mock import MagicMock, Mock, mock_open, patch from tests.support.runtests import RUNTIME_VARS -from tests.support.helpers import TstSuiteLoggingHandler -from tests.support.mock import ( - mock_open, - Mock, - MagicMock, - patch -) +from tests.support.unit import TestCase, skipIf -DEFAULT_SHELL = 'foo/bar' -MOCK_SHELL_FILE = '# List of acceptable shells\n' \ - '\n'\ - '/bin/bash\n' +DEFAULT_SHELL = "foo/bar" +MOCK_SHELL_FILE = "# List of acceptable shells\n" "\n" "/bin/bash\n" class MockTimedProc(object): - ''' + """ Class used as a stand-in for salt.utils.timed_subprocess.TimedProc - ''' + """ + class _Process(object): - ''' + """ Used to provide a dummy "process" attribute - ''' + """ + def __init__(self, returncode=0, pid=12345): self.returncode = returncode self.pid = pid def __init__(self, stdout=None, stderr=None, returncode=0, pid=12345): if stdout is not None and not isinstance(stdout, bytes): - raise TypeError('Must pass stdout to MockTimedProc as bytes') + raise TypeError("Must pass stdout to MockTimedProc as bytes") if stderr is not None and not isinstance(stderr, bytes): - raise TypeError('Must pass stderr to MockTimedProc as bytes') + raise TypeError("Must pass stderr to MockTimedProc as bytes") self._stdout = stdout self._stderr = stderr self.process = self._Process(returncode=returncode, pid=pid) @@ -70,280 +67,317 @@ class MockTimedProc(object): class CMDMODTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Unit tests for the salt.modules.cmdmod module - ''' + """ def setup_loader_modules(self): return {cmdmod: {}} @classmethod def setUpClass(cls): - cls.mock_loglevels = {'info': 'foo', 'all': 'bar', 'critical': 'bar', - 'trace': 'bar', 'garbage': 'bar', 'error': 'bar', - 'debug': 'bar', 'warning': 'bar', 'quiet': 'bar'} + cls.mock_loglevels = { + "info": "foo", + "all": "bar", + "critical": "bar", + "trace": "bar", + "garbage": "bar", + "error": "bar", + "debug": "bar", + "warning": "bar", + "quiet": "bar", + } @classmethod def tearDownClass(cls): del cls.mock_loglevels def test_render_cmd_no_template(self): - ''' + """ Tests return when template=None - ''' - self.assertEqual(cmdmod._render_cmd('foo', 'bar', None), - ('foo', 'bar')) + """ + self.assertEqual(cmdmod._render_cmd("foo", "bar", None), ("foo", "bar")) def test_render_cmd_unavailable_engine(self): - ''' + """ Tests CommandExecutionError raised when template isn't in the template registry - ''' - self.assertRaises(CommandExecutionError, - cmdmod._render_cmd, - 'boo', 'bar', 'baz') + """ + self.assertRaises( + CommandExecutionError, cmdmod._render_cmd, "boo", "bar", "baz" + ) def test_check_loglevel_bad_level(self): - ''' + """ Tests return of providing an invalid loglevel option - ''' + """ with patch.dict(LOG_LEVELS, self.mock_loglevels): - self.assertEqual(cmdmod._check_loglevel(level='bad_loglevel'), 'foo') + self.assertEqual(cmdmod._check_loglevel(level="bad_loglevel"), "foo") def test_check_loglevel_bad_level_not_str(self): - ''' + """ Tests the return of providing an invalid loglevel option that is not a string - ''' + """ with patch.dict(LOG_LEVELS, self.mock_loglevels): - self.assertEqual(cmdmod._check_loglevel(level=1000), 'foo') + self.assertEqual(cmdmod._check_loglevel(level=1000), "foo") def test_check_loglevel_quiet(self): - ''' + """ Tests the return of providing a loglevel of 'quiet' - ''' + """ with patch.dict(LOG_LEVELS, self.mock_loglevels): - self.assertEqual(cmdmod._check_loglevel(level='quiet'), None) + self.assertEqual(cmdmod._check_loglevel(level="quiet"), None) def test_parse_env_not_env(self): - ''' + """ Tests the return of an env that is not an env - ''' + """ self.assertEqual(cmdmod._parse_env(None), {}) def test_parse_env_list(self): - ''' + """ Tests the return of an env that is a list - ''' - ret = {'foo': None, 'bar': None} - self.assertEqual(ret, cmdmod._parse_env(['foo', 'bar'])) + """ + ret = {"foo": None, "bar": None} + self.assertEqual(ret, cmdmod._parse_env(["foo", "bar"])) def test_parse_env_dict(self): - ''' + """ Test the return of an env that is not a dict - ''' - self.assertEqual(cmdmod._parse_env('test'), {}) + """ + self.assertEqual(cmdmod._parse_env("test"), {}) def test_run_shell_is_not_file(self): - ''' + """ Tests error raised when shell is not available after _is_valid_shell error msg and os.path.isfile returns False - ''' - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): - with patch('os.path.isfile', MagicMock(return_value=False)): - self.assertRaises(CommandExecutionError, cmdmod._run, 'foo', 'bar') + """ + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=False)): + self.assertRaises(CommandExecutionError, cmdmod._run, "foo", "bar") def test_run_shell_file_no_access(self): - ''' + """ Tests error raised when shell is not available after _is_valid_shell error msg, os.path.isfile returns True, but os.access returns False - ''' - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.access', MagicMock(return_value=False)): - self.assertRaises(CommandExecutionError, cmdmod._run, 'foo', 'bar') + """ + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.access", MagicMock(return_value=False)): + self.assertRaises( + CommandExecutionError, cmdmod._run, "foo", "bar" + ) def test_run_runas_with_windows(self): - ''' + """ Tests error raised when runas is passed on windows - ''' - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=True)): - with patch.dict(cmdmod.__grains__, {'os': 'fake_os'}): - self.assertRaises(CommandExecutionError, - cmdmod._run, - 'foo', 'bar', runas='baz') + """ + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): + with patch.dict(cmdmod.__grains__, {"os": "fake_os"}): + self.assertRaises( + CommandExecutionError, cmdmod._run, "foo", "bar", runas="baz" + ) def test_run_user_not_available(self): - ''' + """ Tests return when runas user is not available - ''' + """ mock_true = MagicMock(return_value=True) - with patch('salt.modules.cmdmod._is_valid_shell', mock_true), \ - patch('os.path.isfile', mock_true), \ - patch('os.access', mock_true): - self.assertRaises(CommandExecutionError, - cmdmod._run, 'foo', 'bar', runas='baz') + with patch("salt.modules.cmdmod._is_valid_shell", mock_true), patch( + "os.path.isfile", mock_true + ), patch("os.access", mock_true): + self.assertRaises( + CommandExecutionError, cmdmod._run, "foo", "bar", runas="baz" + ) def test_run_zero_umask(self): - ''' + """ Tests error raised when umask is set to zero - ''' - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.access', MagicMock(return_value=True)): - self.assertRaises(CommandExecutionError, cmdmod._run, 'foo', 'bar', umask=0) + """ + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.access", MagicMock(return_value=True)): + self.assertRaises( + CommandExecutionError, cmdmod._run, "foo", "bar", umask=0 + ) def test_run_invalid_umask(self): - ''' + """ Tests error raised when an invalid umask is given - ''' - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.access', MagicMock(return_value=True)): - self.assertRaises(CommandExecutionError, cmdmod._run, 'foo', 'bar', umask='baz') + """ + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.access", MagicMock(return_value=True)): + self.assertRaises( + CommandExecutionError, + cmdmod._run, + "foo", + "bar", + umask="baz", + ) def test_run_invalid_cwd_not_abs_path(self): - ''' + """ Tests error raised when cwd is not an absolute path - ''' - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.access', MagicMock(return_value=True)): - self.assertRaises(CommandExecutionError, cmdmod._run, 'foo', 'bar') + """ + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.access", MagicMock(return_value=True)): + self.assertRaises( + CommandExecutionError, cmdmod._run, "foo", "bar" + ) def test_run_invalid_cwd_not_dir(self): - ''' + """ Tests error raised when cwd is not a dir - ''' - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.access', MagicMock(return_value=True)): - with patch('os.path.isabs', MagicMock(return_value=True)): - self.assertRaises(CommandExecutionError, cmdmod._run, 'foo', 'bar') + """ + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.access", MagicMock(return_value=True)): + with patch("os.path.isabs", MagicMock(return_value=True)): + self.assertRaises( + CommandExecutionError, cmdmod._run, "foo", "bar" + ) def test_run_no_vt_os_error(self): - ''' + """ Tests error raised when not useing vt and OSError is provided - ''' + """ expected_error = "expect error" - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.access', MagicMock(return_value=True)): - with patch('salt.utils.timed_subprocess.TimedProc', MagicMock(side_effect=OSError(expected_error))): + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.access", MagicMock(return_value=True)): + with patch( + "salt.utils.timed_subprocess.TimedProc", + MagicMock(side_effect=OSError(expected_error)), + ): with self.assertRaises(CommandExecutionError) as error: - cmdmod.run('foo') - assert error.exception.args[0].endswith(expected_error), repr(error.exception.args[0]) + cmdmod.run("foo") + assert error.exception.args[0].endswith( + expected_error + ), repr(error.exception.args[0]) def test_run_no_vt_io_error(self): - ''' + """ Tests error raised when not useing vt and IOError is provided - ''' + """ expected_error = "expect error" - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.access', MagicMock(return_value=True)): - with patch('salt.utils.timed_subprocess.TimedProc', MagicMock(side_effect=IOError(expected_error))): + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.access", MagicMock(return_value=True)): + with patch( + "salt.utils.timed_subprocess.TimedProc", + MagicMock(side_effect=IOError(expected_error)), + ): with self.assertRaises(CommandExecutionError) as error: - cmdmod.run('foo') - assert error.exception.args[0].endswith(expected_error), repr(error.exception.args[0]) + cmdmod.run("foo") + assert error.exception.args[0].endswith( + expected_error + ), repr(error.exception.args[0]) - @skipIf(salt.utils.platform.is_windows(), 'Do not run on Windows') - @skipIf(True, 'Test breaks unittests runs') + @skipIf(salt.utils.platform.is_windows(), "Do not run on Windows") + @skipIf(True, "Test breaks unittests runs") def test_run(self): - ''' + """ Tests end result when a command is not found - ''' - with patch('salt.modules.cmdmod._is_valid_shell', MagicMock(return_value=True)): - with patch('salt.utils.platform.is_windows', MagicMock(return_value=False)): - with patch('os.path.isfile', MagicMock(return_value=True)): - with patch('os.access', MagicMock(return_value=True)): - ret = cmdmod._run('foo', cwd=os.getcwd(), use_vt=True).get('stderr') - self.assertIn('foo', ret) + """ + with patch("salt.modules.cmdmod._is_valid_shell", MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=False)): + with patch("os.path.isfile", MagicMock(return_value=True)): + with patch("os.access", MagicMock(return_value=True)): + ret = cmdmod._run("foo", cwd=os.getcwd(), use_vt=True).get( + "stderr" + ) + self.assertIn("foo", ret) def test_is_valid_shell_windows(self): - ''' + """ Tests return if running on windows - ''' - with patch('salt.utils.platform.is_windows', MagicMock(return_value=True)): - self.assertTrue(cmdmod._is_valid_shell('foo')) + """ + with patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): + self.assertTrue(cmdmod._is_valid_shell("foo")) - @skipIf(salt.utils.platform.is_windows(), 'Do not run on Windows') + @skipIf(salt.utils.platform.is_windows(), "Do not run on Windows") def test_is_valid_shell_none(self): - ''' + """ Tests return of when os.path.exists(/etc/shells) isn't available - ''' - with patch('os.path.exists', MagicMock(return_value=False)): - self.assertIsNone(cmdmod._is_valid_shell('foo')) + """ + with patch("os.path.exists", MagicMock(return_value=False)): + self.assertIsNone(cmdmod._is_valid_shell("foo")) def test_is_valid_shell_available(self): - ''' + """ Tests return when provided shell is available - ''' - with patch('os.path.exists', MagicMock(return_value=True)): - with patch('salt.utils.files.fopen', mock_open(read_data=MOCK_SHELL_FILE)): - self.assertTrue(cmdmod._is_valid_shell('/bin/bash')) + """ + with patch("os.path.exists", MagicMock(return_value=True)): + with patch("salt.utils.files.fopen", mock_open(read_data=MOCK_SHELL_FILE)): + self.assertTrue(cmdmod._is_valid_shell("/bin/bash")) - @skipIf(salt.utils.platform.is_windows(), 'Do not run on Windows') + @skipIf(salt.utils.platform.is_windows(), "Do not run on Windows") def test_is_valid_shell_unavailable(self): - ''' + """ Tests return when provided shell is not available - ''' - with patch('os.path.exists', MagicMock(return_value=True)): - with patch('salt.utils.files.fopen', mock_open(read_data=MOCK_SHELL_FILE)): - self.assertFalse(cmdmod._is_valid_shell('foo')) + """ + with patch("os.path.exists", MagicMock(return_value=True)): + with patch("salt.utils.files.fopen", mock_open(read_data=MOCK_SHELL_FILE)): + self.assertFalse(cmdmod._is_valid_shell("foo")) - @skipIf(salt.utils.platform.is_windows(), 'Do not run on Windows') + @skipIf(salt.utils.platform.is_windows(), "Do not run on Windows") def test_os_environment_remains_intact(self): - ''' + """ Make sure the OS environment is not tainted after running a command that specifies runas. - ''' - with patch('pwd.getpwnam') as getpwnam_mock: - with patch('subprocess.Popen') as popen_mock: + """ + with patch("pwd.getpwnam") as getpwnam_mock: + with patch("subprocess.Popen") as popen_mock: environment = os.environ.copy() popen_mock.return_value = Mock( - communicate=lambda *args, **kwags: [b'', None], + communicate=lambda *args, **kwags: [b"", None], pid=lambda: 1, - retcode=0 + retcode=0, ) - with patch.dict(cmdmod.__grains__, {'os': 'Darwin', 'os_family': 'Solaris'}): - if sys.platform.startswith(('freebsd', 'openbsd')): - shell = '/bin/sh' + with patch.dict( + cmdmod.__grains__, {"os": "Darwin", "os_family": "Solaris"} + ): + if sys.platform.startswith(("freebsd", "openbsd")): + shell = "/bin/sh" else: - shell = '/bin/bash' + shell = "/bin/bash" - cmdmod._run('ls', - cwd=tempfile.gettempdir(), - runas='foobar', - shell=shell) + cmdmod._run( + "ls", cwd=tempfile.gettempdir(), runas="foobar", shell=shell + ) environment2 = os.environ.copy() self.assertEqual(environment, environment2) if not salt.utils.platform.is_darwin(): - getpwnam_mock.assert_called_with('foobar') + getpwnam_mock.assert_called_with("foobar") - @skipIf(not salt.utils.platform.is_darwin(), 'applicable to macOS only') + @skipIf(not salt.utils.platform.is_darwin(), "applicable to macOS only") def test_shell_properly_handled_on_macOS(self): - ''' + """ cmd.run should invoke a new bash login only when bash is the default shell for the selected user - ''' + """ + class _CommandHandler(object): - ''' + """ Class for capturing cmd - ''' + """ + def __init__(self): self.cmd = None @@ -353,48 +387,64 @@ class CMDMODTestCase(TestCase, LoaderModuleMockMixin): cmd_handler = _CommandHandler() def mock_proc(__cmd__, **kwargs): - cmd_handler.cmd = ' '.join(__cmd__) + cmd_handler.cmd = " ".join(__cmd__) return MagicMock(return_value=MockTimedProc(stdout=None, stderr=None)) - with patch('pwd.getpwnam') as getpwnam_mock: - with patch('salt.utils.timed_subprocess.TimedProc', mock_proc): + with patch("pwd.getpwnam") as getpwnam_mock: + with patch("salt.utils.timed_subprocess.TimedProc", mock_proc): # User default shell is '/usr/local/bin/bash' - user_default_shell = '/usr/local/bin/bash' - with patch.dict(cmdmod.__salt__, - {'user.info': MagicMock(return_value={'shell': user_default_shell})}): + user_default_shell = "/usr/local/bin/bash" + with patch.dict( + cmdmod.__salt__, + { + "user.info": MagicMock( + return_value={"shell": user_default_shell} + ) + }, + ): cmd_handler.clear() - cmdmod._run('ls', - cwd=tempfile.gettempdir(), - runas='foobar', - use_vt=False) + cmdmod._run( + "ls", cwd=tempfile.gettempdir(), runas="foobar", use_vt=False + ) - self.assertRegex(cmd_handler.cmd, "{} -l -c".format(user_default_shell), - "cmd invokes right bash session on macOS") + self.assertRegex( + cmd_handler.cmd, + "{} -l -c".format(user_default_shell), + "cmd invokes right bash session on macOS", + ) # User default shell is '/bin/zsh' - user_default_shell = '/bin/zsh' - with patch.dict(cmdmod.__salt__, - {'user.info': MagicMock(return_value={'shell': user_default_shell})}): + user_default_shell = "/bin/zsh" + with patch.dict( + cmdmod.__salt__, + { + "user.info": MagicMock( + return_value={"shell": user_default_shell} + ) + }, + ): cmd_handler.clear() - cmdmod._run('ls', - cwd=tempfile.gettempdir(), - runas='foobar', - use_vt=False) + cmdmod._run( + "ls", cwd=tempfile.gettempdir(), runas="foobar", use_vt=False + ) - self.assertNotRegex(cmd_handler.cmd, "bash -l -c", - "cmd does not invoke user shell on macOS") + self.assertNotRegex( + cmd_handler.cmd, + "bash -l -c", + "cmd does not invoke user shell on macOS", + ) def test_run_cwd_doesnt_exist_issue_7154(self): - ''' + """ cmd.run should fail and raise salt.exceptions.CommandExecutionError if the cwd dir does not exist - ''' - cmd = 'echo OHAI' - cwd = '/path/to/nowhere' + """ + cmd = "echo OHAI" + cwd = "/path/to/nowhere" try: cmdmod.run_all(cmd, cwd=cwd) except CommandExecutionError: @@ -403,14 +453,14 @@ class CMDMODTestCase(TestCase, LoaderModuleMockMixin): raise RuntimeError def test_run_all_binary_replace(self): - ''' + """ Test for failed decoding of binary data, for instance when doing something silly like using dd to read from /dev/urandom and write to /dev/stdout. - ''' + """ # Since we're using unicode_literals, read the random bytes from a file - rand_bytes_file = os.path.join(RUNTIME_VARS.BASE_FILES, 'random_bytes') - with salt.utils.files.fopen(rand_bytes_file, 'rb') as fp_: + rand_bytes_file = os.path.join(RUNTIME_VARS.BASE_FILES, "random_bytes") + with salt.utils.files.fopen(rand_bytes_file, "rb") as fp_: stdout_bytes = fp_.read() # kitchen-salt uses unix2dos on all the files before copying them over @@ -421,137 +471,137 @@ class CMDMODTestCase(TestCase, LoaderModuleMockMixin): # stdout with the non-decodable bits replaced with the unicode # replacement character U+FFFD. - stdout_unicode = '\ufffd\x1b\ufffd\ufffd' + os.linesep - stderr_bytes = os.linesep.encode().join([ - b'1+0 records in', - b'1+0 records out', - b'4 bytes copied, 9.1522e-05 s, 43.7 kB/s']) + os.linesep.encode() + stdout_unicode = "\ufffd\x1b\ufffd\ufffd" + os.linesep + stderr_bytes = ( + os.linesep.encode().join( + [ + b"1+0 records in", + b"1+0 records out", + b"4 bytes copied, 9.1522e-05 s, 43.7 kB/s", + ] + ) + + os.linesep.encode() + ) stderr_unicode = stderr_bytes.decode() proc = MagicMock( - return_value=MockTimedProc( - stdout=stdout_bytes, - stderr=stderr_bytes - ) + return_value=MockTimedProc(stdout=stdout_bytes, stderr=stderr_bytes) ) - with patch('salt.utils.timed_subprocess.TimedProc', proc): + with patch("salt.utils.timed_subprocess.TimedProc", proc): ret = cmdmod.run_all( - 'dd if=/dev/urandom of=/dev/stdout bs=4 count=1', - rstrip=False) + "dd if=/dev/urandom of=/dev/stdout bs=4 count=1", rstrip=False + ) - self.assertEqual(ret['stdout'], stdout_unicode) - self.assertEqual(ret['stderr'], stderr_unicode) + self.assertEqual(ret["stdout"], stdout_unicode) + self.assertEqual(ret["stderr"], stderr_unicode) def test_run_all_none(self): - ''' + """ Tests cases when proc.stdout or proc.stderr are None. These should be caught and replaced with empty strings. - ''' + """ proc = MagicMock(return_value=MockTimedProc(stdout=None, stderr=None)) - with patch('salt.utils.timed_subprocess.TimedProc', proc): - ret = cmdmod.run_all('some command', rstrip=False) + with patch("salt.utils.timed_subprocess.TimedProc", proc): + ret = cmdmod.run_all("some command", rstrip=False) - self.assertEqual(ret['stdout'], '') - self.assertEqual(ret['stderr'], '') + self.assertEqual(ret["stdout"], "") + self.assertEqual(ret["stderr"], "") def test_run_all_unicode(self): - ''' + """ Ensure that unicode stdout and stderr are decoded properly - ''' - stdout_unicode = 'Here is some unicode: спам' - stderr_unicode = 'Here is some unicode: яйца' - stdout_bytes = stdout_unicode.encode('utf-8') - stderr_bytes = stderr_unicode.encode('utf-8') + """ + stdout_unicode = "Here is some unicode: спам" + stderr_unicode = "Here is some unicode: яйца" + stdout_bytes = stdout_unicode.encode("utf-8") + stderr_bytes = stderr_unicode.encode("utf-8") proc = MagicMock( - return_value=MockTimedProc( - stdout=stdout_bytes, - stderr=stderr_bytes - ) + return_value=MockTimedProc(stdout=stdout_bytes, stderr=stderr_bytes) ) - with patch('salt.utils.timed_subprocess.TimedProc', proc), \ - patch.object(builtins, '__salt_system_encoding__', 'utf-8'): - ret = cmdmod.run_all('some command', rstrip=False) + with patch("salt.utils.timed_subprocess.TimedProc", proc), patch.object( + builtins, "__salt_system_encoding__", "utf-8" + ): + ret = cmdmod.run_all("some command", rstrip=False) - self.assertEqual(ret['stdout'], stdout_unicode) - self.assertEqual(ret['stderr'], stderr_unicode) + self.assertEqual(ret["stdout"], stdout_unicode) + self.assertEqual(ret["stderr"], stderr_unicode) def test_run_all_output_encoding(self): - ''' + """ Test that specifying the output encoding works as expected - ''' - stdout = 'Æ' - stdout_latin1_enc = stdout.encode('latin1') + """ + stdout = "Æ" + stdout_latin1_enc = stdout.encode("latin1") proc = MagicMock(return_value=MockTimedProc(stdout=stdout_latin1_enc)) - with patch('salt.utils.timed_subprocess.TimedProc', proc), \ - patch.object(builtins, '__salt_system_encoding__', 'utf-8'): - ret = cmdmod.run_all('some command', output_encoding='latin1') + with patch("salt.utils.timed_subprocess.TimedProc", proc), patch.object( + builtins, "__salt_system_encoding__", "utf-8" + ): + ret = cmdmod.run_all("some command", output_encoding="latin1") - self.assertEqual(ret['stdout'], stdout) + self.assertEqual(ret["stdout"], stdout) def test_run_all_output_loglevel_quiet(self): - ''' + """ Test that specifying quiet for loglevel does not log the command. - ''' - stdout = b'test' + """ + stdout = b"test" proc = MagicMock(return_value=MockTimedProc(stdout=stdout)) msg = "INFO:Executing command 'some command' in directory" - with patch('salt.utils.timed_subprocess.TimedProc', proc): + with patch("salt.utils.timed_subprocess.TimedProc", proc): with TstSuiteLoggingHandler() as log_handler: - ret = cmdmod.run_all('some command', output_loglevel='quiet') + ret = cmdmod.run_all("some command", output_loglevel="quiet") assert not [x for x in log_handler.messages if msg in x] - self.assertEqual(ret['stdout'], - salt.utils.stringutils.to_unicode(stdout)) + self.assertEqual(ret["stdout"], salt.utils.stringutils.to_unicode(stdout)) def test_run_all_output_loglevel_debug(self): - ''' + """ Test that specifying debug for loglevel does log the command. - ''' - stdout = b'test' + """ + stdout = b"test" proc = MagicMock(return_value=MockTimedProc(stdout=stdout)) msg = "INFO:Executing command 'some command' in directory" - with patch('salt.utils.timed_subprocess.TimedProc', proc): + with patch("salt.utils.timed_subprocess.TimedProc", proc): with TstSuiteLoggingHandler() as log_handler: - ret = cmdmod.run_all('some command', output_loglevel='debug') + ret = cmdmod.run_all("some command", output_loglevel="debug") assert [x for x in log_handler.messages if msg in x] - self.assertEqual(ret['stdout'], - salt.utils.stringutils.to_unicode(stdout)) + self.assertEqual(ret["stdout"], salt.utils.stringutils.to_unicode(stdout)) def test_run_chroot_mount(self): - ''' + """ Test cmdmod.run_chroot mount / umount balance - ''' + """ mock_mount = MagicMock() mock_umount = MagicMock() mock_run_all = MagicMock() - with patch.dict(cmdmod.__salt__, { - 'mount.mount': mock_mount, - 'mount.umount': mock_umount}): - with patch('salt.modules.cmdmod.run_all', mock_run_all): - cmdmod.run_chroot('/mnt', 'cmd') + with patch.dict( + cmdmod.__salt__, {"mount.mount": mock_mount, "mount.umount": mock_umount} + ): + with patch("salt.modules.cmdmod.run_all", mock_run_all): + cmdmod.run_chroot("/mnt", "cmd") self.assertEqual(mock_mount.call_count, 3) self.assertEqual(mock_umount.call_count, 3) def test_run_chroot_mount_bind(self): - ''' + """ Test cmdmod.run_chroot mount / umount balance with bind mount - ''' + """ mock_mount = MagicMock() mock_umount = MagicMock() mock_run_all = MagicMock() - with patch.dict(cmdmod.__salt__, { - 'mount.mount': mock_mount, - 'mount.umount': mock_umount}): - with patch('salt.modules.cmdmod.run_all', mock_run_all): - cmdmod.run_chroot('/mnt', 'cmd', binds=['/var']) + with patch.dict( + cmdmod.__salt__, {"mount.mount": mock_mount, "mount.umount": mock_umount} + ): + with patch("salt.modules.cmdmod.run_all", mock_run_all): + cmdmod.run_chroot("/mnt", "cmd", binds=["/var"]) self.assertEqual(mock_mount.call_count, 4) self.assertEqual(mock_umount.call_count, 4) diff --git a/tests/unit/modules/test_composer.py b/tests/unit/modules/test_composer.py index 2663e3a92e5..3bb66463200 100644 --- a/tests/unit/modules/test_composer.py +++ b/tests/unit/modules/test_composer.py @@ -1,161 +1,199 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.composer as composer -from salt.exceptions import CommandExecutionError, CommandNotFoundError, SaltInvocationError +from salt.exceptions import ( + CommandExecutionError, + CommandNotFoundError, + SaltInvocationError, +) + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class ComposerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.composer - ''' + """ + def setup_loader_modules(self): return {composer: {}} def test_install(self): - ''' + """ Test for Install composer dependencies for a directory. - ''' + """ # Test _valid_composer=False throws exception mock = MagicMock(return_value=False) - with patch.object(composer, '_valid_composer', mock): - self.assertRaises(CommandNotFoundError, composer.install, 'd') + with patch.object(composer, "_valid_composer", mock): + self.assertRaises(CommandNotFoundError, composer.install, "d") # Test no directory specified throws exception mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): + with patch.object(composer, "_valid_composer", mock): self.assertRaises(SaltInvocationError, composer.install, None) # Test `composer install` exit status != 0 throws exception mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'A'}) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, composer.install, 'd') + with patch.object(composer, "_valid_composer", mock): + mock = MagicMock(return_value={"retcode": 1, "stderr": "A"}) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): + self.assertRaises(CommandExecutionError, composer.install, "d") # Test success with quiet=True returns True mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'A'}) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): - self.assertTrue(composer.install('dir', None, None, None, None, - None, None, None, None, None, - True)) + with patch.object(composer, "_valid_composer", mock): + mock = MagicMock(return_value={"retcode": 0, "stderr": "A"}) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): + self.assertTrue( + composer.install( + "dir", + None, + None, + None, + None, + None, + None, + None, + None, + None, + True, + ) + ) # Test success with quiet=False returns object mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): - rval = {'retcode': 0, 'stderr': 'A', 'stdout': 'B'} + with patch.object(composer, "_valid_composer", mock): + rval = {"retcode": 0, "stderr": "A", "stdout": "B"} mock = MagicMock(return_value=rval) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): - self.assertEqual(composer.install('dir'), rval) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): + self.assertEqual(composer.install("dir"), rval) def test_update(self): - ''' + """ Test for Update composer dependencies for a directory. - ''' + """ # Test _valid_composer=False throws exception mock = MagicMock(return_value=False) - with patch.object(composer, '_valid_composer', mock): - self.assertRaises(CommandNotFoundError, composer.update, 'd') + with patch.object(composer, "_valid_composer", mock): + self.assertRaises(CommandNotFoundError, composer.update, "d") # Test no directory specified throws exception mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): + with patch.object(composer, "_valid_composer", mock): mock = MagicMock(return_value=True) - with patch.object(composer, 'did_composer_install', mock): + with patch.object(composer, "did_composer_install", mock): self.assertRaises(SaltInvocationError, composer.update, None) # Test update with error exit status throws exception mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): + with patch.object(composer, "_valid_composer", mock): mock = MagicMock(return_value=True) - with patch.object(composer, 'did_composer_install', mock): - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'A'}) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, composer.update, 'd') + with patch.object(composer, "did_composer_install", mock): + mock = MagicMock(return_value={"retcode": 1, "stderr": "A"}) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): + self.assertRaises(CommandExecutionError, composer.update, "d") # Test update with existing vendor directory and quiet=True mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): + with patch.object(composer, "_valid_composer", mock): mock = MagicMock(return_value=True) - with patch.object(composer, 'did_composer_install', mock): - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'A'}) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): - self.assertTrue(composer.update('dir', None, None, None, None, - None, None, None, None, None, - True)) + with patch.object(composer, "did_composer_install", mock): + mock = MagicMock(return_value={"retcode": 0, "stderr": "A"}) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): + self.assertTrue( + composer.update( + "dir", + None, + None, + None, + None, + None, + None, + None, + None, + None, + True, + ) + ) # Test update with no vendor directory and quiet=True mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): + with patch.object(composer, "_valid_composer", mock): mock = MagicMock(return_value=False) - with patch.object(composer, 'did_composer_install', mock): - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'A'}) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): - self.assertTrue(composer.update('dir', None, None, None, None, - None, None, None, None, None, - True)) + with patch.object(composer, "did_composer_install", mock): + mock = MagicMock(return_value={"retcode": 0, "stderr": "A"}) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): + self.assertTrue( + composer.update( + "dir", + None, + None, + None, + None, + None, + None, + None, + None, + None, + True, + ) + ) # Test update with existing vendor directory mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): + with patch.object(composer, "_valid_composer", mock): mock = MagicMock(return_value=True) - with patch.object(composer, 'did_composer_install', mock): - rval = {'retcode': 0, 'stderr': 'A', 'stdout': 'B'} + with patch.object(composer, "did_composer_install", mock): + rval = {"retcode": 0, "stderr": "A", "stdout": "B"} mock = MagicMock(return_value=rval) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): - self.assertEqual(composer.update('dir'), rval) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): + self.assertEqual(composer.update("dir"), rval) # Test update with no vendor directory mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): + with patch.object(composer, "_valid_composer", mock): mock = MagicMock(return_value=False) - with patch.object(composer, 'did_composer_install', mock): - rval = {'retcode': 0, 'stderr': 'A', 'stdout': 'B'} + with patch.object(composer, "did_composer_install", mock): + rval = {"retcode": 0, "stderr": "A", "stdout": "B"} mock = MagicMock(return_value=rval) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): - self.assertEqual(composer.update('dir'), rval) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): + self.assertEqual(composer.update("dir"), rval) def test_selfupdate(self): - ''' + """ Test for Composer selfupdate - ''' + """ mock = MagicMock(return_value=False) - with patch.object(composer, '_valid_composer', mock): + with patch.object(composer, "_valid_composer", mock): self.assertRaises(CommandNotFoundError, composer.selfupdate) mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'A'}) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): + with patch.object(composer, "_valid_composer", mock): + mock = MagicMock(return_value={"retcode": 1, "stderr": "A"}) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): self.assertRaises(CommandExecutionError, composer.selfupdate) mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'A'}) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): + with patch.object(composer, "_valid_composer", mock): + mock = MagicMock(return_value={"retcode": 0, "stderr": "A"}) + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): self.assertTrue(composer.selfupdate(quiet=True)) mock = MagicMock(return_value=True) - with patch.object(composer, '_valid_composer', mock): - rval = {'retcode': 0, 'stderr': 'A', 'stdout': 'B'} + with patch.object(composer, "_valid_composer", mock): + rval = {"retcode": 0, "stderr": "A", "stdout": "B"} mock = MagicMock(return_value=rval) - with patch.dict(composer.__salt__, {'cmd.run_all': mock}): + with patch.dict(composer.__salt__, {"cmd.run_all": mock}): self.assertEqual(composer.selfupdate(), rval) diff --git a/tests/unit/modules/test_config.py b/tests/unit/modules/test_config.py index 088e3c0675a..1d8db969be0 100644 --- a/tests/unit/modules/test_config.py +++ b/tests/unit/modules/test_config.py @@ -1,53 +1,54 @@ # -*- coding: utf-8 -*- # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import fnmatch -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch +import fnmatch # Import Salt libs import salt.modules.config as config +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + DEFAULTS = { - 'test.option.foo': 'value of test.option.foo in DEFAULTS', - 'test.option.bar': 'value of test.option.bar in DEFAULTS', - 'test.option.baz': 'value of test.option.baz in DEFAULTS', - 'test.option': 'value of test.option in DEFAULTS', + "test.option.foo": "value of test.option.foo in DEFAULTS", + "test.option.bar": "value of test.option.bar in DEFAULTS", + "test.option.baz": "value of test.option.baz in DEFAULTS", + "test.option": "value of test.option in DEFAULTS", } class TestModulesConfig(TestCase, LoaderModuleMockMixin): - no_match = 'test.option.nope' - opt_name = 'test.option.foo' - wildcard_opt_name = 'test.option.b*' + no_match = "test.option.nope" + opt_name = "test.option.foo" + wildcard_opt_name = "test.option.b*" def setup_loader_modules(self): return { config: { - '__opts__': { - 'test.option.foo': 'value of test.option.foo in __opts__', - 'test.option.bar': 'value of test.option.bar in __opts__', - 'test.option.baz': 'value of test.option.baz in __opts__', + "__opts__": { + "test.option.foo": "value of test.option.foo in __opts__", + "test.option.bar": "value of test.option.bar in __opts__", + "test.option.baz": "value of test.option.baz in __opts__", }, - '__pillar__': { - 'test.option.foo': 'value of test.option.foo in __pillar__', - 'test.option.bar': 'value of test.option.bar in __pillar__', - 'test.option.baz': 'value of test.option.baz in __pillar__', - 'master': { - 'test.option.foo': 'value of test.option.foo in master', - 'test.option.bar': 'value of test.option.bar in master', - 'test.option.baz': 'value of test.option.baz in master', - } + "__pillar__": { + "test.option.foo": "value of test.option.foo in __pillar__", + "test.option.bar": "value of test.option.bar in __pillar__", + "test.option.baz": "value of test.option.baz in __pillar__", + "master": { + "test.option.foo": "value of test.option.foo in master", + "test.option.bar": "value of test.option.bar in master", + "test.option.baz": "value of test.option.baz in master", + }, + }, + "__grains__": { + "test.option.foo": "value of test.option.foo in __grains__", + "test.option.bar": "value of test.option.bar in __grains__", + "test.option.baz": "value of test.option.baz in __grains__", }, - '__grains__': { - 'test.option.foo': 'value of test.option.foo in __grains__', - 'test.option.bar': 'value of test.option.bar in __grains__', - 'test.option.baz': 'value of test.option.baz in __grains__', - } } } @@ -56,32 +57,32 @@ class TestModulesConfig(TestCase, LoaderModuleMockMixin): def test_defaults_only_name(self): with patch.dict(config.DEFAULTS, DEFAULTS): - opt_name = 'test.option' + opt_name = "test.option" opt = config.option(opt_name) self.assertEqual(opt, config.DEFAULTS[opt_name]) def test_no_match(self): - ''' + """ Make sure that the defa - ''' + """ with patch.dict(config.DEFAULTS, DEFAULTS): ret = config.option(self.no_match) - assert ret == '', ret + assert ret == "", ret - default = 'wat' + default = "wat" ret = config.option(self.no_match, default=default) assert ret == default, ret ret = config.option(self.no_match, wildcard=True) assert ret == {}, ret - default = {'foo': 'bar'} + default = {"foo": "bar"} ret = config.option(self.no_match, default=default, wildcard=True) assert ret == default, ret # Should be no match since wildcard=False ret = config.option(self.wildcard_opt_name) - assert ret == '', ret + assert ret == "", ret def test_omits(self): with patch.dict(config.DEFAULTS, DEFAULTS): @@ -99,74 +100,70 @@ class TestModulesConfig(TestCase, LoaderModuleMockMixin): # ********** OMIT __opts__ ********** # Match should be in __grains__ dict - ret = config.option(self.opt_name, - omit_opts=True) + ret = config.option(self.opt_name, omit_opts=True) assert ret == config.__grains__[self.opt_name], ret # Wildcard match - ret = config.option(self.wildcard_opt_name, - omit_opts=True, - wildcard=True) + ret = config.option(self.wildcard_opt_name, omit_opts=True, wildcard=True) assert ret == self._wildcard_match(config.__grains__), ret # ********** OMIT __opts__, __grains__ ********** # Match should be in __pillar__ dict - ret = config.option(self.opt_name, - omit_opts=True, - omit_grains=True) + ret = config.option(self.opt_name, omit_opts=True, omit_grains=True) assert ret == config.__pillar__[self.opt_name], ret # Wildcard match - ret = config.option(self.wildcard_opt_name, - omit_opts=True, - omit_grains=True, - wildcard=True) + ret = config.option( + self.wildcard_opt_name, omit_opts=True, omit_grains=True, wildcard=True + ) assert ret == self._wildcard_match(config.__pillar__), ret # ********** OMIT __opts__, __grains__, __pillar__ ********** # Match should be in master opts - ret = config.option(self.opt_name, - omit_opts=True, - omit_grains=True, - omit_pillar=True) - assert ret == config.__pillar__['master'][self.opt_name], ret + ret = config.option( + self.opt_name, omit_opts=True, omit_grains=True, omit_pillar=True + ) + assert ret == config.__pillar__["master"][self.opt_name], ret # Wildcard match - ret = config.option(self.wildcard_opt_name, - omit_opts=True, - omit_grains=True, - omit_pillar=True, - wildcard=True) - assert ret == self._wildcard_match(config.__pillar__['master']), ret + ret = config.option( + self.wildcard_opt_name, + omit_opts=True, + omit_grains=True, + omit_pillar=True, + wildcard=True, + ) + assert ret == self._wildcard_match(config.__pillar__["master"]), ret # ********** OMIT ALL THE THINGS ********** # Match should be in master opts - ret = config.option(self.opt_name, - omit_opts=True, - omit_grains=True, - omit_pillar=True, - omit_master=True) + ret = config.option( + self.opt_name, + omit_opts=True, + omit_grains=True, + omit_pillar=True, + omit_master=True, + ) assert ret == config.DEFAULTS[self.opt_name], ret # Wildcard match - ret = config.option(self.wildcard_opt_name, - omit_opts=True, - omit_grains=True, - omit_pillar=True, - omit_master=True, - wildcard=True) + ret = config.option( + self.wildcard_opt_name, + omit_opts=True, + omit_grains=True, + omit_pillar=True, + omit_master=True, + wildcard=True, + ) assert ret == self._wildcard_match(config.DEFAULTS), ret # Match should be in master opts - ret = config.option(self.opt_name, - omit_all=True) + ret = config.option(self.opt_name, omit_all=True) assert ret == config.DEFAULTS[self.opt_name], ret # Wildcard match - ret = config.option(self.wildcard_opt_name, - omit_all=True, - wildcard=True) + ret = config.option(self.wildcard_opt_name, omit_all=True, wildcard=True) assert ret == self._wildcard_match(config.DEFAULTS), ret diff --git a/tests/unit/modules/test_cp.py b/tests/unit/modules/test_cp.py index 738ea9b6d45..c0e19877032 100644 --- a/tests/unit/modules/test_cp.py +++ b/tests/unit/modules/test_cp.py @@ -1,146 +1,149 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: jmoney <justin@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - mock_open, - patch, -) +import salt.modules.cp as cp +import salt.transport.client # Import Salt Libs import salt.utils.files -import salt.utils.templates as templates import salt.utils.platform -import salt.transport.client -import salt.modules.cp as cp +import salt.utils.templates as templates from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, mock_open, patch +from tests.support.unit import TestCase + class CpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.cp module - ''' + """ def setup_loader_modules(self): return {cp: {}} def test__render_filenames_undefined_template(self): - ''' + """ Test if _render_filenames fails upon getting a template not in TEMPLATE_REGISTRY. - ''' - path = '/srv/salt/saltines' - dest = '/srv/salt/cheese' - saltenv = 'base' - template = 'biscuits' + """ + path = "/srv/salt/saltines" + dest = "/srv/salt/cheese" + saltenv = "base" + template = "biscuits" ret = (path, dest) - self.assertRaises(CommandExecutionError, - cp._render_filenames, - path, dest, saltenv, template) + self.assertRaises( + CommandExecutionError, cp._render_filenames, path, dest, saltenv, template + ) def test__render_filenames_render_failed(self): - ''' + """ Test if _render_filenames fails when template rendering fails. - ''' - path = 'salt://saltines' - dest = '/srv/salt/cheese' - saltenv = 'base' - template = 'jinja' - file_data = 'Remember to keep your files well salted.' - mock_jinja = lambda *args, **kwargs: {'result': False, - 'data': file_data} - with patch.dict(templates.TEMPLATE_REGISTRY, - {'jinja': mock_jinja}): - with patch('salt.utils.files.fopen', mock_open(read_data=file_data)): - self.assertRaises(CommandExecutionError, - cp._render_filenames, - path, dest, saltenv, template) + """ + path = "salt://saltines" + dest = "/srv/salt/cheese" + saltenv = "base" + template = "jinja" + file_data = "Remember to keep your files well salted." + mock_jinja = lambda *args, **kwargs: {"result": False, "data": file_data} + with patch.dict(templates.TEMPLATE_REGISTRY, {"jinja": mock_jinja}): + with patch("salt.utils.files.fopen", mock_open(read_data=file_data)): + self.assertRaises( + CommandExecutionError, + cp._render_filenames, + path, + dest, + saltenv, + template, + ) def test__render_filenames_success(self): - ''' + """ Test if _render_filenames succeeds. - ''' - path = 'salt://saltines' - dest = '/srv/salt/cheese' - saltenv = 'base' - template = 'jinja' - file_data = '/srv/salt/biscuits' - mock_jinja = lambda *args, **kwargs: {'result': True, - 'data': file_data} + """ + path = "salt://saltines" + dest = "/srv/salt/cheese" + saltenv = "base" + template = "jinja" + file_data = "/srv/salt/biscuits" + mock_jinja = lambda *args, **kwargs: {"result": True, "data": file_data} ret = (file_data, file_data) # salt.utils.files.fopen can only be mocked once - with patch.dict(templates.TEMPLATE_REGISTRY, - {'jinja': mock_jinja}): - with patch('salt.utils.files.fopen', mock_open(read_data=file_data)): - self.assertEqual(cp._render_filenames( - path, dest, saltenv, template), ret) + with patch.dict(templates.TEMPLATE_REGISTRY, {"jinja": mock_jinja}): + with patch("salt.utils.files.fopen", mock_open(read_data=file_data)): + self.assertEqual( + cp._render_filenames(path, dest, saltenv, template), ret + ) def test_get_file_not_found(self): - ''' + """ Test if get_file can't find the file. - ''' - with patch('salt.modules.cp.hash_file', MagicMock(return_value=False)): - path = 'salt://saltines' - dest = '/srv/salt/cheese' - ret = '' + """ + with patch("salt.modules.cp.hash_file", MagicMock(return_value=False)): + path = "salt://saltines" + dest = "/srv/salt/cheese" + ret = "" self.assertEqual(cp.get_file(path, dest), ret) def test_get_file_str_success(self): - ''' + """ Test if get_file_str succeeds. - ''' - path = 'salt://saltines' - dest = '/srv/salt/cheese/saltines' - file_data = 'Remember to keep your files well salted.' - saltenv = 'base' + """ + path = "salt://saltines" + dest = "/srv/salt/cheese/saltines" + file_data = "Remember to keep your files well salted." + saltenv = "base" ret = file_data - with patch('salt.utils.files.fopen', mock_open(read_data=file_data)): - with patch('salt.modules.cp.cache_file', - MagicMock(return_value=dest)): + with patch("salt.utils.files.fopen", mock_open(read_data=file_data)): + with patch("salt.modules.cp.cache_file", MagicMock(return_value=dest)): self.assertEqual(cp.get_file_str(path, dest), ret) def test_push_non_absolute_path(self): - ''' + """ Test if push fails on a non absolute path. - ''' - path = '../saltines' + """ + path = "../saltines" ret = False self.assertEqual(cp.push(path), ret) def test_push_dir_non_absolute_path(self): - ''' + """ Test if push_dir fails on a non absolute path. - ''' - path = '../saltines' + """ + path = "../saltines" ret = False self.assertEqual(cp.push_dir(path), ret) def test_push(self): - ''' + """ Test if push works with good posix path. - ''' - filename = '/saltines/test.file' + """ + filename = "/saltines/test.file" if salt.utils.platform.is_windows(): - filename = 'c:\\saltines\\test.file' - with patch('salt.modules.cp.os.path', - MagicMock(isfile=Mock(return_value=True), wraps=cp.os.path)), \ - patch('salt.modules.cp.os.path', - MagicMock(getsize=MagicMock(return_value=10), wraps=cp.os.path)), \ - patch.multiple('salt.modules.cp', - _auth=MagicMock(**{'return_value.gen_token.return_value': 'token'}), - __opts__={'id': 'abc', 'file_buffer_size': 10}), \ - patch('salt.utils.files.fopen', mock_open(read_data=b'content')) as m_open, \ - patch('salt.transport.client.ReqChannel.factory', MagicMock()) as req_channel_factory_mock: + filename = "c:\\saltines\\test.file" + with patch( + "salt.modules.cp.os.path", + MagicMock(isfile=Mock(return_value=True), wraps=cp.os.path), + ), patch( + "salt.modules.cp.os.path", + MagicMock(getsize=MagicMock(return_value=10), wraps=cp.os.path), + ), patch.multiple( + "salt.modules.cp", + _auth=MagicMock(**{"return_value.gen_token.return_value": "token"}), + __opts__={"id": "abc", "file_buffer_size": 10}, + ), patch( + "salt.utils.files.fopen", mock_open(read_data=b"content") + ) as m_open, patch( + "salt.transport.client.ReqChannel.factory", MagicMock() + ) as req_channel_factory_mock: response = cp.push(filename) assert response, response num_opens = len(m_open.filehandles[filename]) @@ -150,11 +153,11 @@ class CpTestCase(TestCase, LoaderModuleMockMixin): req_channel_factory_mock().__enter__().send.assert_called_once_with( dict( loc=fh_.tell(), # pylint: disable=resource-leakage - cmd='_file_recv', - tok='token', - path=['saltines', 'test.file'], + cmd="_file_recv", + tok="token", + path=["saltines", "test.file"], size=10, - data=b'', # data is empty here because load['data'] is overwritten - id='abc' + data=b"", # data is empty here because load['data'] is overwritten + id="abc", ) ) diff --git a/tests/unit/modules/test_cpan.py b/tests/unit/modules/test_cpan.py index 648a7fb1d34..616e1de6c2c 100644 --- a/tests/unit/modules/test_cpan.py +++ b/tests/unit/modules/test_cpan.py @@ -1,134 +1,142 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) # Import Salt Libs import salt.modules.cpan as cpan +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class CpanTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.cpan - ''' + """ + # 'install' function tests: 2 def setup_loader_modules(self): return {cpan: {}} def test_install(self): - ''' + """ Test if it install a module from cpan - ''' - mock = MagicMock(return_value='') - with patch.dict(cpan.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'installed version': None}, - {'installed version': '3.1'}]) - with patch.object(cpan, 'show', mock): - self.assertDictEqual(cpan.install('Alloy'), - {'new': '3.1', 'old': None}) + """ + mock = MagicMock(return_value="") + with patch.dict(cpan.__salt__, {"cmd.run": mock}): + mock = MagicMock( + side_effect=[{"installed version": None}, {"installed version": "3.1"}] + ) + with patch.object(cpan, "show", mock): + self.assertDictEqual(cpan.install("Alloy"), {"new": "3.1", "old": None}) def test_install_error(self): - ''' + """ Test if it install a module from cpan - ''' + """ mock = MagicMock(return_value="don't know what it is") - with patch.dict(cpan.__salt__, {'cmd.run': mock}): - self.assertDictEqual(cpan.install('Alloy'), - {'error': 'CPAN cannot identify this package', - 'new': None, 'old': None}) + with patch.dict(cpan.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + cpan.install("Alloy"), + { + "error": "CPAN cannot identify this package", + "new": None, + "old": None, + }, + ) # 'remove' function tests: 4 def test_remove(self): - ''' + """ Test if it remove a module using cpan - ''' - with patch('os.listdir', MagicMock(return_value=[''])): - mock = MagicMock(return_value='') - with patch.dict(cpan.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'installed version': '2.1', - 'cpan build dirs': [''], - 'installed file': '/root'}) - with patch.object(cpan, 'show', mock): - self.assertDictEqual(cpan.remove('Alloy'), - {'new': None, 'old': '2.1'}) + """ + with patch("os.listdir", MagicMock(return_value=[""])): + mock = MagicMock(return_value="") + with patch.dict(cpan.__salt__, {"cmd.run": mock}): + mock = MagicMock( + return_value={ + "installed version": "2.1", + "cpan build dirs": [""], + "installed file": "/root", + } + ) + with patch.object(cpan, "show", mock): + self.assertDictEqual( + cpan.remove("Alloy"), {"new": None, "old": "2.1"} + ) def test_remove_unexist_error(self): - ''' + """ Test if it try to remove an unexist module using cpan - ''' + """ mock = MagicMock(return_value="don't know what it is") - with patch.dict(cpan.__salt__, {'cmd.run': mock}): - self.assertDictEqual(cpan.remove('Alloy'), - {'error': - 'This package does not seem to exist'}) + with patch.dict(cpan.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + cpan.remove("Alloy"), {"error": "This package does not seem to exist"} + ) def test_remove_noninstalled_error(self): - ''' + """ Test if it remove non installed module using cpan - ''' - mock = MagicMock(return_value={'installed version': None}) - with patch.object(cpan, 'show', mock): - self.assertDictEqual(cpan.remove('Alloy'), - {'new': None, 'old': None}) + """ + mock = MagicMock(return_value={"installed version": None}) + with patch.object(cpan, "show", mock): + self.assertDictEqual(cpan.remove("Alloy"), {"new": None, "old": None}) def test_remove_nopan_error(self): - ''' + """ Test if it gives no cpan error while removing - ''' - ret = {'error': 'No CPAN data available to use for uninstalling'} - mock = MagicMock(return_value={'installed version': '2.1'}) - with patch.object(cpan, 'show', mock): - self.assertDictEqual(cpan.remove('Alloy'), ret) + """ + ret = {"error": "No CPAN data available to use for uninstalling"} + mock = MagicMock(return_value={"installed version": "2.1"}) + with patch.object(cpan, "show", mock): + self.assertDictEqual(cpan.remove("Alloy"), ret) # 'list' function tests: 1 def test_list(self): - ''' + """ Test if it list installed Perl module - ''' - mock = MagicMock(return_value='') - with patch.dict(cpan.__salt__, {'cmd.run': mock}): + """ + mock = MagicMock(return_value="") + with patch.dict(cpan.__salt__, {"cmd.run": mock}): self.assertDictEqual(cpan.list_(), {}) # 'show' function tests: 2 def test_show(self): - ''' + """ Test if it show information about a specific Perl module - ''' - mock = MagicMock(return_value='') - with patch.dict(cpan.__salt__, {'cmd.run': mock}): - self.assertDictEqual(cpan.show('Alloy'), - {'error': - 'This package does not seem to exist', - 'name': 'Alloy'}) + """ + mock = MagicMock(return_value="") + with patch.dict(cpan.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + cpan.show("Alloy"), + {"error": "This package does not seem to exist", "name": "Alloy"}, + ) def test_show_mock(self): - ''' + """ Test if it show information about a specific Perl module - ''' - with patch('salt.modules.cpan.show', MagicMock(return_value={'Salt': 'salt'})): - mock = MagicMock(return_value='Salt module installed') - with patch.dict(cpan.__salt__, {'cmd.run': mock}): - self.assertDictEqual(cpan.show('Alloy'), {'Salt': 'salt'}) + """ + with patch("salt.modules.cpan.show", MagicMock(return_value={"Salt": "salt"})): + mock = MagicMock(return_value="Salt module installed") + with patch.dict(cpan.__salt__, {"cmd.run": mock}): + self.assertDictEqual(cpan.show("Alloy"), {"Salt": "salt"}) # 'show_config' function tests: 1 def test_show_config(self): - ''' + """ Test if it return a dict of CPAN configuration values - ''' - mock = MagicMock(return_value='') - with patch.dict(cpan.__salt__, {'cmd.run': mock}): + """ + mock = MagicMock(return_value="") + with patch.dict(cpan.__salt__, {"cmd.run": mock}): self.assertDictEqual(cpan.show_config(), {}) diff --git a/tests/unit/modules/test_cron.py b/tests/unit/modules/test_cron.py index 90cd0312990..f6ae36821e3 100644 --- a/tests/unit/modules/test_cron.py +++ b/tests/unit/modules/test_cron.py @@ -1,31 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place <mp@saltstack.com> -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch, call +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.modules.cron as cron -from salt.ext.six.moves import builtins, range, StringIO +from salt.ext.six.moves import StringIO, builtins, range -STUB_USER = 'root' -STUB_PATH = '/tmp' +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase -STUB_CRON_TIMESTAMP = {'minute': '1', - 'hour': '2', - 'daymonth': '3', - 'month': '4', - 'dayweek': '5'} +STUB_USER = "root" +STUB_PATH = "/tmp" -STUB_SIMPLE_RAW_CRON = '5 0 * * * /tmp/no_script.sh' -STUB_SIMPLE_CRON_DICT = {'pre': ['5 0 * * * /tmp/no_script.sh'], 'crons': [], 'env': [], 'special': []} +STUB_CRON_TIMESTAMP = { + "minute": "1", + "hour": "2", + "daymonth": "3", + "month": "4", + "dayweek": "5", +} + +STUB_SIMPLE_RAW_CRON = "5 0 * * * /tmp/no_script.sh" +STUB_SIMPLE_CRON_DICT = { + "pre": ["5 0 * * * /tmp/no_script.sh"], + "crons": [], + "env": [], + "special": [], +} STUB_CRON_SPACES = """ # Lines below here are managed by Salt, do not edit TEST_VAR="a string with plenty of spaces" @@ -39,7 +46,7 @@ STUB_AT_SIGN = """ @daily """ -L = '# Lines below here are managed by Salt, do not edit\n' +L = "# Lines below here are managed by Salt, do not edit\n" CRONTAB = StringIO() @@ -55,830 +62,1171 @@ def set_crontab(val): def write_crontab(*args, **kw): - set_crontab('\n'.join( - [a.strip() for a in args[1]])) + set_crontab("\n".join([a.strip() for a in args[1]])) return MagicMock() class CronTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {cron: {}} def test__need_changes_new(self): - ''' + """ New behavior, identifier will get track of the managed lines! - ''' - with patch('salt.modules.cron.raw_cron', - new=MagicMock(side_effect=get_crontab)), \ - patch('salt.modules.cron._write_cron_lines', - new=MagicMock(side_effect=write_crontab)): + """ + with patch( + "salt.modules.cron.raw_cron", new=MagicMock(side_effect=get_crontab) + ), patch( + "salt.modules.cron._write_cron_lines", + new=MagicMock(side_effect=write_crontab), + ): # when there are no identifiers, # we do not touch it - set_crontab( - L + '# SALT_CRON_IDENTIFIER:booh\n' - '* * * * * ls\n') + set_crontab(L + "# SALT_CRON_IDENTIFIER:booh\n" "* * * * * ls\n") cron.set_job( - user='root', - minute='*', - hour='*', - daymonth='*', - month='*', - dayweek='*', - cmd='ls', + user="root", + minute="*", + hour="*", + daymonth="*", + month="*", + dayweek="*", + cmd="ls", comment=None, identifier=None, ) c1 = get_crontab() - set_crontab(L + '* * * * * ls\n') + set_crontab(L + "* * * * * ls\n") self.assertEqual( c1, - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:booh\n' - '* * * * * ls\n' - '* * * * * ls' + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:booh\n" + "* * * * * ls\n" + "* * * * * ls", ) # whenever we have an identifier, hourray even without comment # we can match and edit the crontab in place # without cluttering the crontab with new cmds - set_crontab( - L + '# SALT_CRON_IDENTIFIER:bar\n' - '* * * * * ls\n') + set_crontab(L + "# SALT_CRON_IDENTIFIER:bar\n" "* * * * * ls\n") cron.set_job( - user='root', - minute='*', - hour='*', - daymonth='*', - month='*', - dayweek='*', - cmd='ls', + user="root", + minute="*", + hour="*", + daymonth="*", + month="*", + dayweek="*", + cmd="ls", comment=None, - identifier='bar', + identifier="bar", ) c5 = get_crontab() - set_crontab(L + '* * * * * ls\n') + set_crontab(L + "* * * * * ls\n") self.assertEqual( c5, - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:bar\n' - '* * * * * ls\n' + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:bar\n" + "* * * * * ls\n", ) # we can even change the other parameters as well # thx to the id - set_crontab( - L + '# SALT_CRON_IDENTIFIER:bar\n* * * * * ls\n') + set_crontab(L + "# SALT_CRON_IDENTIFIER:bar\n* * * * * ls\n") cron.set_job( - user='root', - minute='1', - hour='2', - daymonth='3', - month='4', - dayweek='5', - cmd='foo', - comment='moo', - identifier='bar', + user="root", + minute="1", + hour="2", + daymonth="3", + month="4", + dayweek="5", + cmd="foo", + comment="moo", + identifier="bar", ) c6 = get_crontab() self.assertEqual( c6, - '# Lines below here are managed by Salt, do not edit\n' - '# moo SALT_CRON_IDENTIFIER:bar\n' - '1 2 3 4 5 foo' + "# Lines below here are managed by Salt, do not edit\n" + "# moo SALT_CRON_IDENTIFIER:bar\n" + "1 2 3 4 5 foo", ) def test__unicode_match(self): - with patch.object(builtins, '__salt_system_encoding__', 'utf-8'): - self.assertTrue(cron._cron_matched({'identifier': '1'}, 'foo', 1)) - self.assertTrue(cron._cron_matched({'identifier': 'é'}, 'foo', 'é')) - self.assertTrue(cron._cron_matched({'identifier': u'é'}, 'foo', 'é')) - self.assertTrue(cron._cron_matched({'identifier': 'é'}, 'foo', u'é')) - self.assertTrue(cron._cron_matched({'identifier': u'é'}, 'foo', u'é')) + with patch.object(builtins, "__salt_system_encoding__", "utf-8"): + self.assertTrue(cron._cron_matched({"identifier": "1"}, "foo", 1)) + self.assertTrue(cron._cron_matched({"identifier": "é"}, "foo", "é")) + self.assertTrue(cron._cron_matched({"identifier": "é"}, "foo", "é")) + self.assertTrue(cron._cron_matched({"identifier": "é"}, "foo", "é")) + self.assertTrue(cron._cron_matched({"identifier": "é"}, "foo", "é")) def test__need_changes_old(self): - ''' + """ old behavior; ID has no special action - If an id is found, it will be added as a new crontab even if there is a cmd that looks like this one - no comment, delete the cmd and readd it - comment: idem - ''' - with patch('salt.modules.cron.raw_cron', - new=MagicMock(side_effect=get_crontab)), \ - patch('salt.modules.cron._write_cron_lines', - new=MagicMock(side_effect=write_crontab)): - set_crontab(L + '* * * * * ls\n\n') + """ + with patch( + "salt.modules.cron.raw_cron", new=MagicMock(side_effect=get_crontab) + ), patch( + "salt.modules.cron._write_cron_lines", + new=MagicMock(side_effect=write_crontab), + ): + set_crontab(L + "* * * * * ls\n\n") cron.set_job( - user='root', - minute='*', - hour='*', - daymonth='*', - month='*', - dayweek='*', - cmd='ls', + user="root", + minute="*", + hour="*", + daymonth="*", + month="*", + dayweek="*", + cmd="ls", comment=None, identifier=cron.SALT_CRON_NO_IDENTIFIER, ) c1 = get_crontab() - set_crontab(L + '* * * * * ls\n') + set_crontab(L + "* * * * * ls\n") self.assertEqual( c1, - '# Lines below here are managed by Salt, do not edit\n' - '* * * * * ls\n' - '\n' + "# Lines below here are managed by Salt, do not edit\n" + "* * * * * ls\n" + "\n", ) cron.set_job( - user='root', - minute='*', - hour='*', - daymonth='*', - month='*', - dayweek='*', - cmd='ls', - comment='foo', + user="root", + minute="*", + hour="*", + daymonth="*", + month="*", + dayweek="*", + cmd="ls", + comment="foo", identifier=cron.SALT_CRON_NO_IDENTIFIER, ) c2 = get_crontab() self.assertEqual( c2, - '# Lines below here are managed by Salt, do not edit\n' - '# foo\n* * * * * ls' + "# Lines below here are managed by Salt, do not edit\n" + "# foo\n* * * * * ls", ) - set_crontab(L + '* * * * * ls\n') + set_crontab(L + "* * * * * ls\n") cron.set_job( - user='root', - minute='*', - hour='*', - daymonth='*', - month='*', - dayweek='*', - cmd='lsa', - comment='foo', - identifier='bar', + user="root", + minute="*", + hour="*", + daymonth="*", + month="*", + dayweek="*", + cmd="lsa", + comment="foo", + identifier="bar", ) c3 = get_crontab() self.assertEqual( c3, - '# Lines below here are managed by Salt, do not edit\n' - '* * * * * ls\n' - '# foo SALT_CRON_IDENTIFIER:bar\n' - '* * * * * lsa' + "# Lines below here are managed by Salt, do not edit\n" + "* * * * * ls\n" + "# foo SALT_CRON_IDENTIFIER:bar\n" + "* * * * * lsa", ) - set_crontab(L + '* * * * * ls\n') + set_crontab(L + "* * * * * ls\n") cron.set_job( - user='root', - minute='*', - hour='*', - daymonth='*', - month='*', - dayweek='*', - cmd='foo', - comment='foo', - identifier='bar', + user="root", + minute="*", + hour="*", + daymonth="*", + month="*", + dayweek="*", + cmd="foo", + comment="foo", + identifier="bar", ) c4 = get_crontab() self.assertEqual( c4, - '# Lines below here are managed by Salt, do not edit\n' - '* * * * * ls\n' - '# foo SALT_CRON_IDENTIFIER:bar\n' - '* * * * * foo' + "# Lines below here are managed by Salt, do not edit\n" + "* * * * * ls\n" + "# foo SALT_CRON_IDENTIFIER:bar\n" + "* * * * * foo", ) - set_crontab(L + '* * * * * ls\n') + set_crontab(L + "* * * * * ls\n") cron.set_job( - user='root', - minute='*', - hour='*', - daymonth='*', - month='*', - dayweek='*', - cmd='ls', - comment='foo', - identifier='bbar', + user="root", + minute="*", + hour="*", + daymonth="*", + month="*", + dayweek="*", + cmd="ls", + comment="foo", + identifier="bbar", ) c4 = get_crontab() self.assertEqual( c4, - '# Lines below here are managed by Salt, do not edit\n' - '# foo SALT_CRON_IDENTIFIER:bbar\n' - '* * * * * ls' + "# Lines below here are managed by Salt, do not edit\n" + "# foo SALT_CRON_IDENTIFIER:bbar\n" + "* * * * * ls", ) def test__issue10959(self): - ''' + """ handle multi old style crontabs https://github.com/saltstack/salt/issues/10959 - ''' - with patch('salt.modules.cron.raw_cron', - new=MagicMock(side_effect=get_crontab)), \ - patch('salt.modules.cron._write_cron_lines', - new=MagicMock(side_effect=write_crontab)): + """ + with patch( + "salt.modules.cron.raw_cron", new=MagicMock(side_effect=get_crontab) + ), patch( + "salt.modules.cron._write_cron_lines", + new=MagicMock(side_effect=write_crontab), + ): set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" # as managed per salt, the last lines will be merged together ! - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n' - '* * * * * samecmd\n' - '* * * * * otheridcmd\n' - '* * * * * otheridcmd\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n0 * * * * samecmd1\n' - '1 * * * * samecmd1\n' - '0 * * * * otheridcmd1\n' - '1 * * * * otheridcmd1\n' + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n" + "* * * * * samecmd\n" + "* * * * * otheridcmd\n" + "* * * * * otheridcmd\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n0 * * * * samecmd1\n" + "1 * * * * samecmd1\n" + "0 * * * * otheridcmd1\n" + "1 * * * * otheridcmd1\n" # special case here, none id managed line with same command # as a later id managed line will become managed - '# SALT_CRON_IDENTIFIER:1\n0 * * * * otheridcmd1\n' - '# SALT_CRON_IDENTIFIER:2\n0 * * * * otheridcmd1\n' + "# SALT_CRON_IDENTIFIER:1\n0 * * * * otheridcmd1\n" + "# SALT_CRON_IDENTIFIER:2\n0 * * * * otheridcmd1\n" ) - crons1 = cron.list_tab('root') + crons1 = cron.list_tab("root") # the filtering is done on save, we reflect in listing # the same that we have in a file, no matter what we # have - self.assertEqual(crons1, { - 'crons': [ - {'cmd': 'ls', 'comment': 'uoo', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'NO ID SET', - 'minute': '*', 'month': '*', 'commented': False}, - {'cmd': 'too', 'comment': 'uuoo', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'NO ID SET', - 'minute': '*', 'month': '*', 'commented': False}, - {'cmd': 'zoo', 'comment': 'uuuoo', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'NO ID SET', - 'minute': '*', 'month': '*', 'commented': False}, - {'cmd': 'yoo', 'comment': '', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'NO ID SET', - 'minute': '*', 'month': '*', 'commented': False}, - {'cmd': 'xoo', 'comment': '', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'NO ID SET', - 'minute': '*', 'month': '*', 'commented': False}, - {'cmd': 'samecmd', 'comment': '', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'NO ID SET', - 'minute': '*', 'month': '*', 'commented': False}, - {'cmd': 'samecmd', 'comment': None, 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': None, - 'minute': '*', 'month': '*', 'commented': False}, - {'cmd': 'otheridcmd', 'comment': None, 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': None, - 'minute': '*', 'month': '*', 'commented': False}, - {'cmd': 'otheridcmd', 'comment': None, 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': None, - 'minute': '*', 'month': '*', 'commented': False}, - {'cmd': 'samecmd1', 'comment': '', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'NO ID SET', - 'minute': '0', 'month': '*', 'commented': False}, - {'cmd': 'samecmd1', 'comment': None, 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': None, - 'minute': '1', 'month': '*', 'commented': False}, - {'cmd': 'otheridcmd1', 'comment': None, 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': None, - 'minute': '0', 'month': '*', 'commented': False}, - {'cmd': 'otheridcmd1', 'comment': None, 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': None, - 'minute': '1', 'month': '*', 'commented': False}, - {'cmd': 'otheridcmd1', 'comment': '', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': '1', - 'minute': '0', 'month': '*', 'commented': False}, - {'cmd': 'otheridcmd1', - 'comment': '', 'daymonth': '*', 'dayweek': '*', - 'hour': '*', 'identifier': '2', 'minute': '0', - 'month': '*', 'commented': False} - ], - 'env': [], - 'pre': [], - 'special': []}) + self.assertEqual( + crons1, + { + "crons": [ + { + "cmd": "ls", + "comment": "uoo", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "NO ID SET", + "minute": "*", + "month": "*", + "commented": False, + }, + { + "cmd": "too", + "comment": "uuoo", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "NO ID SET", + "minute": "*", + "month": "*", + "commented": False, + }, + { + "cmd": "zoo", + "comment": "uuuoo", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "NO ID SET", + "minute": "*", + "month": "*", + "commented": False, + }, + { + "cmd": "yoo", + "comment": "", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "NO ID SET", + "minute": "*", + "month": "*", + "commented": False, + }, + { + "cmd": "xoo", + "comment": "", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "NO ID SET", + "minute": "*", + "month": "*", + "commented": False, + }, + { + "cmd": "samecmd", + "comment": "", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "NO ID SET", + "minute": "*", + "month": "*", + "commented": False, + }, + { + "cmd": "samecmd", + "comment": None, + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": None, + "minute": "*", + "month": "*", + "commented": False, + }, + { + "cmd": "otheridcmd", + "comment": None, + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": None, + "minute": "*", + "month": "*", + "commented": False, + }, + { + "cmd": "otheridcmd", + "comment": None, + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": None, + "minute": "*", + "month": "*", + "commented": False, + }, + { + "cmd": "samecmd1", + "comment": "", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "NO ID SET", + "minute": "0", + "month": "*", + "commented": False, + }, + { + "cmd": "samecmd1", + "comment": None, + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": None, + "minute": "1", + "month": "*", + "commented": False, + }, + { + "cmd": "otheridcmd1", + "comment": None, + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": None, + "minute": "0", + "month": "*", + "commented": False, + }, + { + "cmd": "otheridcmd1", + "comment": None, + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": None, + "minute": "1", + "month": "*", + "commented": False, + }, + { + "cmd": "otheridcmd1", + "comment": "", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "1", + "minute": "0", + "month": "*", + "commented": False, + }, + { + "cmd": "otheridcmd1", + "comment": "", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "2", + "minute": "0", + "month": "*", + "commented": False, + }, + ], + "env": [], + "pre": [], + "special": [], + }, + ) # so yood so far, no problem for now, trying to save the # multilines without id crons now inc_tests = [ - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n' - '* * * * * otheridcmd'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n" + "* * * * * otheridcmd" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n' - '* * * * * otheridcmd'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n" + "* * * * * otheridcmd" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n' - '* * * * * otheridcmd\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n' - '0 * * * * samecmd1'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n" + "* * * * * otheridcmd\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n" + "0 * * * * samecmd1" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n' - '* * * * * otheridcmd\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n" + "* * * * * otheridcmd\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n' - '* * * * * otheridcmd\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1\n' - '0 * * * * otheridcmd1'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n" + "* * * * * otheridcmd\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1\n" + "0 * * * * otheridcmd1" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n' - '* * * * * otheridcmd\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1\n' - '1 * * * * otheridcmd1'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n" + "* * * * * otheridcmd\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1\n" + "1 * * * * otheridcmd1" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n' - '* * * * * otheridcmd\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1\n' - '# SALT_CRON_IDENTIFIER:1\n0 * * * * otheridcmd1'), + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n" + "* * * * * otheridcmd\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1\n" + "# SALT_CRON_IDENTIFIER:1\n0 * * * * otheridcmd1" + ), # - ('# Lines below here are managed by Salt, do not edit\n' - '# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n' - '# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n' - '# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n' - '* * * * * otheridcmd\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1\n' - '# SALT_CRON_IDENTIFIER:1\n0 * * * * otheridcmd1\n' - '# SALT_CRON_IDENTIFIER:2\n0 * * * * otheridcmd1') + ( + "# Lines below here are managed by Salt, do not edit\n" + "# uoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * ls\n" + "# uuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * too\n" + "# uuuoo SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * zoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * yoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * xoo\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n* * * * * samecmd\n" + "* * * * * otheridcmd\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n1 * * * * samecmd1\n" + "# SALT_CRON_IDENTIFIER:1\n0 * * * * otheridcmd1\n" + "# SALT_CRON_IDENTIFIER:2\n0 * * * * otheridcmd1" + ), ] - set_crontab('') - for idx, cr in enumerate(crons1['crons']): - cron.set_job('root', **cr) + set_crontab("") + for idx, cr in enumerate(crons1["crons"]): + cron.set_job("root", **cr) self.assertEqual( get_crontab(), - inc_tests[idx], ( - "idx {0}\n'{1}'\n != \n'{2}'\n\n\n" - "\'{1}\' != \'{2}\'" - ).format( - idx, get_crontab(), inc_tests[idx])) + inc_tests[idx], + ("idx {0}\n'{1}'\n != \n'{2}'\n\n\n" "'{1}' != '{2}'").format( + idx, get_crontab(), inc_tests[idx] + ), + ) def test_list_tab_commented_cron_jobs(self): - ''' + """ handle commented cron jobs https://github.com/saltstack/salt/issues/29082 - ''' - with patch('salt.modules.cron.raw_cron', MagicMock(side_effect=get_crontab)), \ - patch('salt.modules.cron._write_cron_lines', - MagicMock(side_effect=write_crontab)): + """ + with patch( + "salt.modules.cron.raw_cron", MagicMock(side_effect=get_crontab) + ), patch( + "salt.modules.cron._write_cron_lines", MagicMock(side_effect=write_crontab) + ): set_crontab( - '# An unmanaged commented cron job\n' - '#0 * * * * /bin/true\n' - '# Lines below here are managed by Salt, do not edit\n' - '# cron_1 SALT_CRON_IDENTIFIER:cron_1\n#DISABLED#0 * * * * my_cmd_1\n' - '# cron_2 SALT_CRON_IDENTIFIER:cron_2\n#DISABLED#* * * * * my_cmd_2\n' - '# cron_3 SALT_CRON_IDENTIFIER:cron_3\n' - '#DISABLED#but it is a comment line' - '#DISABLED#0 * * * * my_cmd_3\n' - '# cron_4 SALT_CRON_IDENTIFIER:cron_4\n0 * * * * my_cmd_4\n' + "# An unmanaged commented cron job\n" + "#0 * * * * /bin/true\n" + "# Lines below here are managed by Salt, do not edit\n" + "# cron_1 SALT_CRON_IDENTIFIER:cron_1\n#DISABLED#0 * * * * my_cmd_1\n" + "# cron_2 SALT_CRON_IDENTIFIER:cron_2\n#DISABLED#* * * * * my_cmd_2\n" + "# cron_3 SALT_CRON_IDENTIFIER:cron_3\n" + "#DISABLED#but it is a comment line" + "#DISABLED#0 * * * * my_cmd_3\n" + "# cron_4 SALT_CRON_IDENTIFIER:cron_4\n0 * * * * my_cmd_4\n" ) - crons1 = cron.list_tab('root') - self.assertEqual(crons1, { - 'crons': [ - {'cmd': 'my_cmd_1', 'comment': 'cron_1', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'cron_1', - 'minute': '0', 'month': '*', 'commented': True}, - {'cmd': 'my_cmd_2', 'comment': 'cron_2', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'cron_2', - 'minute': '*', 'month': '*', 'commented': True}, - {'cmd': 'line#DISABLED#0 * * * * my_cmd_3', - 'comment': 'cron_3', 'daymonth': 'is', - 'dayweek': 'comment', 'hour': 'it', 'identifier': 'cron_3', - 'minute': 'but', 'month': 'a', 'commented': True}, - {'cmd': 'my_cmd_4', 'comment': 'cron_4', 'daymonth': '*', - 'dayweek': '*', 'hour': '*', 'identifier': 'cron_4', - 'minute': '0', 'month': '*', 'commented': False}, - ], - 'env': [], - 'pre': ['# An unmanaged commented cron job', '#0 * * * * /bin/true'], - 'special': []}) + crons1 = cron.list_tab("root") + self.assertEqual( + crons1, + { + "crons": [ + { + "cmd": "my_cmd_1", + "comment": "cron_1", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "cron_1", + "minute": "0", + "month": "*", + "commented": True, + }, + { + "cmd": "my_cmd_2", + "comment": "cron_2", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "cron_2", + "minute": "*", + "month": "*", + "commented": True, + }, + { + "cmd": "line#DISABLED#0 * * * * my_cmd_3", + "comment": "cron_3", + "daymonth": "is", + "dayweek": "comment", + "hour": "it", + "identifier": "cron_3", + "minute": "but", + "month": "a", + "commented": True, + }, + { + "cmd": "my_cmd_4", + "comment": "cron_4", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "cron_4", + "minute": "0", + "month": "*", + "commented": False, + }, + ], + "env": [], + "pre": [ + "# An unmanaged commented cron job", + "#0 * * * * /bin/true", + ], + "special": [], + }, + ) + + def test_get_entry(self): + """ + test get_entry function + """ + list_tab_output = { + "crons": [ + { + "cmd": "my_cmd_1", + "comment": "cron_1", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "cron_1", + "minute": "0", + "month": "*", + "commented": True, + }, + { + "cmd": "my_cmd_2", + "comment": "cron_2", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "cron_2", + "minute": "*", + "month": "*", + "commented": True, + }, + { + "cmd": "line#DISABLED#0 * * * * my_cmd_3", + "comment": "cron_3", + "daymonth": "is", + "dayweek": "comment", + "hour": "it", + "identifier": "cron_3", + "minute": "but", + "month": "a", + "commented": True, + }, + { + "cmd": "my_cmd_4", + "comment": "cron_4", + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": "cron_4", + "minute": "0", + "month": "*", + "commented": False, + }, + ], + "env": [], + "pre": ["# An unmanaged commented cron job", "#0 * * * * /bin/true"], + "special": [], + } + get_entry_2 = { + "comment": "cron_2", + "cmd": "my_cmd_2", + "identifier": "cron_2", + "dayweek": "*", + "daymonth": "*", + "hour": "*", + "minute": "*", + "month": "*", + "commented": True, + } + get_entry_3 = { + "comment": "cron_3", + "identifier": "cron_3", + "dayweek": "comment", + "hour": "it", + "cmd": "line#DISABLED#0 * * * * my_cmd_3", + "daymonth": "is", + "commented": True, + "minute": "but", + "month": "a", + } + with patch( + "salt.modules.cron.list_tab", new=MagicMock(return_value=list_tab_output) + ): + # Test get_entry identifier + get_entry_output = cron.get_entry("root", identifier="cron_3") + self.assertDictEqual(get_entry_output, get_entry_3) + # Test get_entry cmd + get_entry_output = cron.get_entry("root", cmd="my_cmd_2") + self.assertDictEqual(get_entry_output, get_entry_2) + # Test identifier wins when both specified + get_entry_output = cron.get_entry( + "root", identifier="cron_3", cmd="my_cmd_2" + ) + self.assertDictEqual(get_entry_output, get_entry_3) def test_cron_extra_spaces(self): - ''' + """ Issue #38449 - ''' - with patch.dict(cron.__grains__, {'os': None}), \ - patch('salt.modules.cron.raw_cron', - MagicMock(return_value=STUB_CRON_SPACES)): - ret = cron.list_tab('root') - eret = {'crons': [{'cmd': 'echo "must be double spaced"', - 'comment': '', - 'commented': False, - 'daymonth': '*', - 'dayweek': '*', - 'hour': '*', - 'identifier': 'echo "must be double spaced"', - 'minute': '11', - 'month': '*'}], - 'env': [{'name': 'TEST_VAR', 'value': '"a string with plenty of spaces"'}], - 'pre': [''], - 'special': []} + """ + with patch.dict(cron.__grains__, {"os": None}), patch( + "salt.modules.cron.raw_cron", MagicMock(return_value=STUB_CRON_SPACES) + ): + ret = cron.list_tab("root") + eret = { + "crons": [ + { + "cmd": 'echo "must be double spaced"', + "comment": "", + "commented": False, + "daymonth": "*", + "dayweek": "*", + "hour": "*", + "identifier": 'echo "must be double spaced"', + "minute": "11", + "month": "*", + } + ], + "env": [ + {"name": "TEST_VAR", "value": '"a string with plenty of spaces"'} + ], + "pre": [""], + "special": [], + } self.assertEqual(eret, ret) def test_cron_at_sign(self): - with patch.dict(cron.__grains__, {'os': None}), \ - patch('salt.modules.cron.raw_cron', - MagicMock(return_value=STUB_AT_SIGN)): - ret = cron.list_tab('root') - eret = {'crons': [], - 'env': [], - 'pre': [''], - 'special': [{u'cmd': u'echo "cron with @ sign"', - u'comment': u'', - u'commented': False, - u'identifier': u'echo "cron with @ sign"', - u'spec': u'@daily'}]} + with patch.dict(cron.__grains__, {"os": None}), patch( + "salt.modules.cron.raw_cron", MagicMock(return_value=STUB_AT_SIGN) + ): + ret = cron.list_tab("root") + eret = { + "crons": [], + "env": [], + "pre": [""], + "special": [ + { + "cmd": 'echo "cron with @ sign"', + "comment": "", + "commented": False, + "identifier": 'echo "cron with @ sign"', + "spec": "@daily", + } + ], + } self.assertDictEqual(eret, ret) def test__load_tab(self): - with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch('salt.modules.cron.raw_cron', - new=MagicMock(side_effect=[ - (L + '\n'), - (L + '* * * * * ls\nn'), - (L + '# commented\n' - '#DISABLED#* * * * * ls\n'), - (L + '# foo\n' - '* * * * * ls\n'), - (L + '# foo {0}:blah\n'.format( - cron.SALT_CRON_IDENTIFIER) + - '* * * * * ls\n')])): - crons1 = cron.list_tab('root') - crons2 = cron.list_tab('root') - crons3 = cron.list_tab('root') - crons4 = cron.list_tab('root') - crons5 = cron.list_tab('root') + with patch.dict(cron.__grains__, {"os_family": "Solaris"}), patch( + "salt.modules.cron.raw_cron", + new=MagicMock( + side_effect=[ + (L + "\n"), + (L + "* * * * * ls\nn"), + (L + "# commented\n" "#DISABLED#* * * * * ls\n"), + (L + "# foo\n" "* * * * * ls\n"), + ( + L + + "# foo {0}:blah\n".format(cron.SALT_CRON_IDENTIFIER) + + "* * * * * ls\n" + ), + ] + ), + ): + crons1 = cron.list_tab("root") + crons2 = cron.list_tab("root") + crons3 = cron.list_tab("root") + crons4 = cron.list_tab("root") + crons5 = cron.list_tab("root") + self.assertEqual(crons1, {"pre": [], "crons": [], "env": [], "special": []}) self.assertEqual( - crons1, - {'pre': [], 'crons': [], 'env': [], 'special': []}) + crons2["crons"][0], + { + "comment": None, + "commented": False, + "dayweek": "*", + "hour": "*", + "identifier": None, + "cmd": "ls", + "daymonth": "*", + "minute": "*", + "month": "*", + }, + ) self.assertEqual( - crons2['crons'][0], - {'comment': None, - 'commented': False, - 'dayweek': '*', - 'hour': '*', - 'identifier': None, - 'cmd': 'ls', - 'daymonth': '*', - 'minute': '*', - 'month': '*'}) + crons3["crons"][0], + { + "comment": "commented", + "commented": True, + "dayweek": "*", + "hour": "*", + "identifier": None, + "cmd": "ls", + "daymonth": "*", + "minute": "*", + "month": "*", + }, + ) self.assertEqual( - crons3['crons'][0], - {'comment': 'commented', - 'commented': True, - 'dayweek': '*', - 'hour': '*', - 'identifier': None, - 'cmd': 'ls', - 'daymonth': '*', - 'minute': '*', - 'month': '*'}) + crons4["crons"][0], + { + "comment": "foo", + "commented": False, + "dayweek": "*", + "hour": "*", + "identifier": None, + "cmd": "ls", + "daymonth": "*", + "minute": "*", + "month": "*", + }, + ) self.assertEqual( - crons4['crons'][0], - {'comment': 'foo', - 'commented': False, - 'dayweek': '*', - 'hour': '*', - 'identifier': None, - 'cmd': 'ls', - 'daymonth': '*', - 'minute': '*', - 'month': '*'}) - self.assertEqual( - crons5['crons'][0], - {'comment': 'foo', - 'commented': False, - 'dayweek': '*', - 'hour': '*', - 'identifier': 'blah', - 'cmd': 'ls', - 'daymonth': '*', - 'minute': '*', - 'month': '*'}) + crons5["crons"][0], + { + "comment": "foo", + "commented": False, + "dayweek": "*", + "hour": "*", + "identifier": "blah", + "cmd": "ls", + "daymonth": "*", + "minute": "*", + "month": "*", + }, + ) def test_write_cron_file_root_rh(self): - ''' + """ Assert that write_cron_file() is called with the correct cron command and user: RedHat - If instance running uid matches crontab user uid, run without -u flag. - ''' - with patch.dict(cron.__grains__, {'os_family': 'RedHat'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - new=MagicMock(return_value=True)): + """ + with patch.dict(cron.__grains__, {"os_family": "RedHat"}), patch.dict( + cron.__salt__, {"cmd.retcode": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", + new=MagicMock(return_value=True), + ): cron.write_cron_file(STUB_USER, STUB_PATH) - cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", - python_shell=False) + cron.__salt__["cmd.retcode"].assert_called_with( + "crontab /tmp", python_shell=False + ) def test_write_cron_file_foo_rh(self): - ''' + """ Assert that write_cron_file() is called with the correct cron command and user: RedHat - If instance running with uid that doesn't match crontab user uid, runas foo - ''' - with patch.dict(cron.__grains__, {'os_family': 'RedHat'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=False)): - cron.write_cron_file('foo', STUB_PATH) - cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", - runas='foo', - python_shell=False) + """ + with patch.dict(cron.__grains__, {"os_family": "RedHat"}), patch.dict( + cron.__salt__, {"cmd.retcode": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", MagicMock(return_value=False) + ): + cron.write_cron_file("foo", STUB_PATH) + cron.__salt__["cmd.retcode"].assert_called_with( + "crontab /tmp", runas="foo", python_shell=False + ) def test_write_cron_file_root_sol(self): - ''' + """ Assert that write_cron_file() is called with the correct cron command and user: Solaris - Solaris should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}): + """ + with patch.dict(cron.__grains__, {"os_family": "Solaris"}), patch.dict( + cron.__salt__, {"cmd.retcode": MagicMock()} + ): cron.write_cron_file(STUB_USER, STUB_PATH) - cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", - runas=STUB_USER, - python_shell=False) + cron.__salt__["cmd.retcode"].assert_called_with( + "crontab /tmp", runas=STUB_USER, python_shell=False + ) def test_write_cron_file_foo_sol(self): - ''' + """ Assert that write_cron_file() is called with the correct cron command and user: Solaris - Solaris should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}): - cron.write_cron_file('foo', STUB_PATH) - cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", - runas='foo', - python_shell=False) + """ + with patch.dict(cron.__grains__, {"os_family": "Solaris"}), patch.dict( + cron.__salt__, {"cmd.retcode": MagicMock()} + ): + cron.write_cron_file("foo", STUB_PATH) + cron.__salt__["cmd.retcode"].assert_called_with( + "crontab /tmp", runas="foo", python_shell=False + ) def test_write_cron_file_root_aix(self): - ''' + """ Assert that write_cron_file() is called with the correct cron command and user: AIX - AIX should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}): + """ + with patch.dict(cron.__grains__, {"os_family": "AIX"}), patch.dict( + cron.__salt__, {"cmd.retcode": MagicMock()} + ): cron.write_cron_file(STUB_USER, STUB_PATH) - cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", - runas=STUB_USER, - python_shell=False) + cron.__salt__["cmd.retcode"].assert_called_with( + "crontab /tmp", runas=STUB_USER, python_shell=False + ) def test_write_cron_file_foo_aix(self): - ''' + """ Assert that write_cron_file() is called with the correct cron command and user: AIX - AIX should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.retcode': MagicMock()}): - cron.write_cron_file('foo', STUB_PATH) - cron.__salt__['cmd.retcode'].assert_called_with("crontab /tmp", - runas='foo', - python_shell=False) + """ + with patch.dict(cron.__grains__, {"os_family": "AIX"}), patch.dict( + cron.__salt__, {"cmd.retcode": MagicMock()} + ): + cron.write_cron_file("foo", STUB_PATH) + cron.__salt__["cmd.retcode"].assert_called_with( + "crontab /tmp", runas="foo", python_shell=False + ) def test_write_cr_file_v_root_rh(self): - ''' + """ Assert that write_cron_file_verbose() is called with the correct cron command and user: RedHat - If instance running uid matches crontab user uid, run without -u flag. - ''' - with patch.dict(cron.__grains__, {'os_family': 'Redhat'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=True)): + """ + with patch.dict(cron.__grains__, {"os_family": "Redhat"}), patch.dict( + cron.__salt__, {"cmd.run_all": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", MagicMock(return_value=True) + ): cron.write_cron_file_verbose(STUB_USER, STUB_PATH) - cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", - python_shell=False) + cron.__salt__["cmd.run_all"].assert_called_with( + "crontab /tmp", python_shell=False + ) def test_write_cr_file_v_foo_rh(self): - ''' + """ Assert that write_cron_file_verbose() is called with the correct cron command and user: RedHat - If instance running with uid that doesn't match crontab user uid, runas 'foo' - ''' - with patch.dict(cron.__grains__, {'os_family': 'Redhat'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=False)): - cron.write_cron_file_verbose('foo', STUB_PATH) - cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", - runas='foo', - python_shell=False) + """ + with patch.dict(cron.__grains__, {"os_family": "Redhat"}), patch.dict( + cron.__salt__, {"cmd.run_all": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", MagicMock(return_value=False) + ): + cron.write_cron_file_verbose("foo", STUB_PATH) + cron.__salt__["cmd.run_all"].assert_called_with( + "crontab /tmp", runas="foo", python_shell=False + ) def test_write_cr_file_v_root_sol(self): - ''' + """ Assert that write_cron_file_verbose() is called with the correct cron command and user: Solaris - Solaris should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}): + """ + with patch.dict(cron.__grains__, {"os_family": "Solaris"}), patch.dict( + cron.__salt__, {"cmd.run_all": MagicMock()} + ): cron.write_cron_file_verbose(STUB_USER, STUB_PATH) - cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", - runas=STUB_USER, - python_shell=False) + cron.__salt__["cmd.run_all"].assert_called_with( + "crontab /tmp", runas=STUB_USER, python_shell=False + ) def test_write_cr_file_v_foo_sol(self): - ''' + """ Assert that write_cron_file_verbose() is called with the correct cron command and user: Solaris - Solaris should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}): - cron.write_cron_file_verbose('foo', STUB_PATH) - cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", - runas='foo', - python_shell=False) + """ + with patch.dict(cron.__grains__, {"os_family": "Solaris"}), patch.dict( + cron.__salt__, {"cmd.run_all": MagicMock()} + ): + cron.write_cron_file_verbose("foo", STUB_PATH) + cron.__salt__["cmd.run_all"].assert_called_with( + "crontab /tmp", runas="foo", python_shell=False + ) def test_write_cr_file_v_root_aix(self): - ''' + """ Assert that write_cron_file_verbose() is called with the correct cron command and user: AIX - AIX should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}): + """ + with patch.dict(cron.__grains__, {"os_family": "AIX"}), patch.dict( + cron.__salt__, {"cmd.run_all": MagicMock()} + ): cron.write_cron_file_verbose(STUB_USER, STUB_PATH) - cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", - runas=STUB_USER, - python_shell=False) + cron.__salt__["cmd.run_all"].assert_called_with( + "crontab /tmp", runas=STUB_USER, python_shell=False + ) def test_write_cr_file_v_foo_aix(self): - ''' + """ Assert that write_cron_file_verbose() is called with the correct cron command and user: AIX - AIX should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.run_all': MagicMock()}): - cron.write_cron_file_verbose('foo', STUB_PATH) - cron.__salt__['cmd.run_all'].assert_called_with("crontab /tmp", - runas='foo', - python_shell=False) + """ + with patch.dict(cron.__grains__, {"os_family": "AIX"}), patch.dict( + cron.__salt__, {"cmd.run_all": MagicMock()} + ): + cron.write_cron_file_verbose("foo", STUB_PATH) + cron.__salt__["cmd.run_all"].assert_called_with( + "crontab /tmp", runas="foo", python_shell=False + ) def test_raw_cron_root_redhat(self): - ''' + """ Assert that raw_cron() is called with the correct cron command and user: RedHat - If instance running uid matches crontab user uid, runas STUB_USER without -u flag. - ''' - with patch.dict(cron.__grains__, {'os_family': 'Redhat'}), \ - patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=True)): + """ + with patch.dict(cron.__grains__, {"os_family": "Redhat"}), patch.dict( + cron.__salt__, {"cmd.run_stdout": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", MagicMock(return_value=True) + ): cron.raw_cron(STUB_USER) - cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l", - ignore_retcode=True, - rstrip=False, - python_shell=False) + cron.__salt__["cmd.run_stdout"].assert_called_with( + "crontab -l", ignore_retcode=True, rstrip=False, python_shell=False + ) def test_raw_cron_foo_redhat(self): - ''' + """ Assert that raw_cron() is called with the correct cron command and user: RedHat - If instance running with uid that doesn't match crontab user uid, run with -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'Redhat'}), \ - patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=False)): + """ + with patch.dict(cron.__grains__, {"os_family": "Redhat"}), patch.dict( + cron.__salt__, {"cmd.run_stdout": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", MagicMock(return_value=False) + ): cron.raw_cron(STUB_USER) - cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l", - runas=STUB_USER, - ignore_retcode=True, - rstrip=False, - python_shell=False) + cron.__salt__["cmd.run_stdout"].assert_called_with( + "crontab -l", + runas=STUB_USER, + ignore_retcode=True, + rstrip=False, + python_shell=False, + ) def test_raw_cron_root_solaris(self): - ''' + """ Assert that raw_cron() is called with the correct cron command and user: Solaris - Solaris should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=True)): + """ + with patch.dict(cron.__grains__, {"os_family": "Solaris"}), patch.dict( + cron.__salt__, {"cmd.run_stdout": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", MagicMock(return_value=True) + ): cron.raw_cron(STUB_USER) - cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l", - runas=STUB_USER, - ignore_retcode=True, - rstrip=False, - python_shell=False) + cron.__salt__["cmd.run_stdout"].assert_called_with( + "crontab -l", + runas=STUB_USER, + ignore_retcode=True, + rstrip=False, + python_shell=False, + ) def test_raw_cron_foo_solaris(self): - ''' + """ Assert that raw_cron() is called with the correct cron command and user: Solaris - Solaris should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'Solaris'}), \ - patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=False)): + """ + with patch.dict(cron.__grains__, {"os_family": "Solaris"}), patch.dict( + cron.__salt__, {"cmd.run_stdout": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", MagicMock(return_value=False) + ): cron.raw_cron(STUB_USER) - cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l", - runas=STUB_USER, - ignore_retcode=True, - rstrip=False, - python_shell=False) + cron.__salt__["cmd.run_stdout"].assert_called_with( + "crontab -l", + runas=STUB_USER, + ignore_retcode=True, + rstrip=False, + python_shell=False, + ) def test_raw_cron_root_aix(self): - ''' + """ Assert that raw_cron() is called with the correct cron command and user: AIX - AIX should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=True)): + """ + with patch.dict(cron.__grains__, {"os_family": "AIX"}), patch.dict( + cron.__salt__, {"cmd.run_stdout": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", MagicMock(return_value=True) + ): cron.raw_cron(STUB_USER) - cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l", - runas=STUB_USER, - ignore_retcode=True, - rstrip=False, - python_shell=False) + cron.__salt__["cmd.run_stdout"].assert_called_with( + "crontab -l", + runas=STUB_USER, + ignore_retcode=True, + rstrip=False, + python_shell=False, + ) def test_raw_cron_foo_aix(self): - ''' + """ Assert that raw_cron() is called with the correct cron command and user: AIX - AIX should always run without a -u flag - ''' - with patch.dict(cron.__grains__, {'os_family': 'AIX'}), \ - patch.dict(cron.__salt__, {'cmd.run_stdout': MagicMock()}), \ - patch('salt.modules.cron._check_instance_uid_match', - MagicMock(return_value=False)): + """ + with patch.dict(cron.__grains__, {"os_family": "AIX"}), patch.dict( + cron.__salt__, {"cmd.run_stdout": MagicMock()} + ), patch( + "salt.modules.cron._check_instance_uid_match", MagicMock(return_value=False) + ): cron.raw_cron(STUB_USER) - cron.__salt__['cmd.run_stdout'].assert_called_with("crontab -l", - runas=STUB_USER, - ignore_retcode=True, - rstrip=False, - python_shell=False) + cron.__salt__["cmd.run_stdout"].assert_called_with( + "crontab -l", + runas=STUB_USER, + ignore_retcode=True, + rstrip=False, + python_shell=False, + ) class PsTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {cron: {}} @@ -886,118 +1234,167 @@ class PsTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(cron._needs_change(True, False)) def test__needs_change_random(self): - ''' + """ Assert that if the new var is 'random' and old is '* that we return True - ''' - self.assertTrue(cron._needs_change('*', 'random')) + """ + self.assertTrue(cron._needs_change("*", "random")) ## Still trying to figure this one out. # def test__render_tab(self): # pass def test__get_cron_cmdstr(self): - self.assertEqual('crontab /tmp', cron._get_cron_cmdstr(STUB_PATH)) + self.assertEqual("crontab /tmp", cron._get_cron_cmdstr(STUB_PATH)) # Test get_cron_cmdstr() when user is added def test__get_cron_cmdstr_user(self): - ''' + """ Passes if a user is added to crontab command - ''' - self.assertEqual('crontab -u root /tmp', cron._get_cron_cmdstr(STUB_PATH, STUB_USER)) + """ + self.assertEqual( + "crontab -u root /tmp", cron._get_cron_cmdstr(STUB_PATH, STUB_USER) + ) def test__date_time_match(self): - ''' + """ Passes if a match is found on all elements. Note the conversions to strings here! :return: - ''' - self.assertTrue(cron._date_time_match(STUB_CRON_TIMESTAMP, - minute=STUB_CRON_TIMESTAMP['minute'], - hour=STUB_CRON_TIMESTAMP['hour'], - daymonth=STUB_CRON_TIMESTAMP['daymonth'], - dayweek=STUB_CRON_TIMESTAMP['dayweek'] - )) + """ + self.assertTrue( + cron._date_time_match( + STUB_CRON_TIMESTAMP, + minute=STUB_CRON_TIMESTAMP["minute"], + hour=STUB_CRON_TIMESTAMP["hour"], + daymonth=STUB_CRON_TIMESTAMP["daymonth"], + dayweek=STUB_CRON_TIMESTAMP["dayweek"], + ) + ) def test_list_tab(self): - with patch('salt.modules.cron.raw_cron', new=MagicMock(return_value=STUB_SIMPLE_RAW_CRON)): - self.assertDictEqual(STUB_SIMPLE_CRON_DICT, cron.list_tab('DUMMY_USER')) + with patch( + "salt.modules.cron.raw_cron", + new=MagicMock(return_value=STUB_SIMPLE_RAW_CRON), + ): + self.assertDictEqual(STUB_SIMPLE_CRON_DICT, cron.list_tab("DUMMY_USER")) def test_set_special(self): - with patch('salt.modules.cron._write_cron_lines') as write_cron_lines_mock, \ - patch('salt.modules.cron.list_tab', new=MagicMock(return_value=STUB_SIMPLE_CRON_DICT)): - expected_write_call = call('DUMMY_USER', - ['5 0 * * * /tmp/no_script.sh\n', - '# Lines below here are managed by Salt, do not edit\n', - '@hourly echo Hi!\n']) - ret = cron.set_special('DUMMY_USER', '@hourly', 'echo Hi!') - write_cron_lines_mock.assert_has_calls((expected_write_call,), any_order=True) + with patch( + "salt.modules.cron._write_cron_lines" + ) as write_cron_lines_mock, patch( + "salt.modules.cron.list_tab", + new=MagicMock(return_value=STUB_SIMPLE_CRON_DICT), + ): + expected_write_call = call( + "DUMMY_USER", + [ + "5 0 * * * /tmp/no_script.sh\n", + "# Lines below here are managed by Salt, do not edit\n", + "@hourly echo Hi!\n", + ], + ) + ret = cron.set_special("DUMMY_USER", "@hourly", "echo Hi!") + write_cron_lines_mock.assert_has_calls( + (expected_write_call,), any_order=True + ) def test__get_cron_date_time(self): - ret = cron._get_cron_date_time(minute=STUB_CRON_TIMESTAMP['minute'], - hour=STUB_CRON_TIMESTAMP['hour'], - daymonth=STUB_CRON_TIMESTAMP['daymonth'], - dayweek=STUB_CRON_TIMESTAMP['dayweek'], - month=STUB_CRON_TIMESTAMP['month']) + ret = cron._get_cron_date_time( + minute=STUB_CRON_TIMESTAMP["minute"], + hour=STUB_CRON_TIMESTAMP["hour"], + daymonth=STUB_CRON_TIMESTAMP["daymonth"], + dayweek=STUB_CRON_TIMESTAMP["dayweek"], + month=STUB_CRON_TIMESTAMP["month"], + ) self.assertDictEqual(ret, STUB_CRON_TIMESTAMP) def test__get_cron_date_time_daymonth_max(self): - ret = cron._get_cron_date_time(minute='random', - hour='random', - daymonth='random', - dayweek='random', - month='random') - self.assertTrue(int(ret['minute']) in range(0, 60)) - self.assertTrue(int(ret['hour']) in range(0, 24)) - self.assertTrue(int(ret['daymonth']) in range(1, 32)) - self.assertTrue(int(ret['dayweek']) in range(0, 8)) - self.assertTrue(int(ret['month']) in range(1, 13)) + ret = cron._get_cron_date_time( + minute="random", + hour="random", + daymonth="random", + dayweek="random", + month="random", + ) + self.assertTrue(int(ret["minute"]) in range(0, 60)) + self.assertTrue(int(ret["hour"]) in range(0, 24)) + self.assertTrue(int(ret["daymonth"]) in range(1, 32)) + self.assertTrue(int(ret["dayweek"]) in range(0, 8)) + self.assertTrue(int(ret["month"]) in range(1, 13)) def test_set_job(self): - with patch.dict(cron.__grains__, {'os': None}), \ - patch('salt.modules.cron._write_cron_lines', - new=MagicMock(return_value={'retcode': False})), \ - patch('salt.modules.cron.raw_cron', - new=MagicMock(return_value=STUB_SIMPLE_RAW_CRON)): - cron.set_job('DUMMY_USER', 1, 2, 3, 4, 5, - '/bin/echo NOT A DROID', - 'WERE YOU LOOKING FOR ME?') - expected_call = call('DUMMY_USER', - ['5 0 * * * /tmp/no_script.sh\n', - '# Lines below here are managed by Salt, do not edit\n', - '# WERE YOU LOOKING FOR ME?\n', - '1 2 3 4 5 /bin/echo NOT A DROID\n']) + with patch.dict(cron.__grains__, {"os": None}), patch( + "salt.modules.cron._write_cron_lines", + new=MagicMock(return_value={"retcode": False}), + ), patch( + "salt.modules.cron.raw_cron", + new=MagicMock(return_value=STUB_SIMPLE_RAW_CRON), + ): + cron.set_job( + "DUMMY_USER", + 1, + 2, + 3, + 4, + 5, + "/bin/echo NOT A DROID", + "WERE YOU LOOKING FOR ME?", + ) + expected_call = call( + "DUMMY_USER", + [ + "5 0 * * * /tmp/no_script.sh\n", + "# Lines below here are managed by Salt, do not edit\n", + "# WERE YOU LOOKING FOR ME?\n", + "1 2 3 4 5 /bin/echo NOT A DROID\n", + ], + ) cron._write_cron_lines.call_args.assert_called_with(expected_call) def test_rm_special(self): - with patch.dict(cron.__grains__, {'os': None}), \ - patch('salt.modules.cron._write_cron_lines', - new=MagicMock(return_value={'retcode': False})), \ - patch('salt.modules.cron.raw_cron', - new=MagicMock(return_value=STUB_AT_SIGN)): - ret = cron.rm_special('root', 'echo "cron with @ sign"', special='@daily', identifier='echo "cron with @ sign"') - self.assertEqual('removed', ret) + with patch.dict(cron.__grains__, {"os": None}), patch( + "salt.modules.cron._write_cron_lines", + new=MagicMock(return_value={"retcode": False}), + ), patch( + "salt.modules.cron.raw_cron", new=MagicMock(return_value=STUB_AT_SIGN) + ): + ret = cron.rm_special( + "root", + 'echo "cron with @ sign"', + special="@daily", + identifier='echo "cron with @ sign"', + ) + self.assertEqual("removed", ret) def test_rm_special_default_special(self): - with patch.dict(cron.__grains__, {'os': None}), \ - patch('salt.modules.cron._write_cron_lines', - new=MagicMock(return_value={'retcode': False})), \ - patch('salt.modules.cron.raw_cron', - new=MagicMock(return_value=STUB_AT_SIGN)): - ret = cron.rm_special('root', 'echo "cron with @ sign"', identifier='echo "cron with @ sign"') - self.assertEqual('removed', ret) + with patch.dict(cron.__grains__, {"os": None}), patch( + "salt.modules.cron._write_cron_lines", + new=MagicMock(return_value={"retcode": False}), + ), patch( + "salt.modules.cron.raw_cron", new=MagicMock(return_value=STUB_AT_SIGN) + ): + ret = cron.rm_special( + "root", 'echo "cron with @ sign"', identifier='echo "cron with @ sign"' + ) + self.assertEqual("removed", ret) def test_rm_special_absent(self): - with patch.dict(cron.__grains__, {'os': None}), \ - patch('salt.modules.cron._write_cron_lines', - new=MagicMock(return_value={'retcode': False})), \ - patch('salt.modules.cron.raw_cron', - new=MagicMock(return_value=STUB_AT_SIGN)): - ret = cron.rm_special('root', 'echo "there is no job"', identifier='echo "there is no job"') - self.assertEqual('absent', ret) + with patch.dict(cron.__grains__, {"os": None}), patch( + "salt.modules.cron._write_cron_lines", + new=MagicMock(return_value={"retcode": False}), + ), patch( + "salt.modules.cron.raw_cron", new=MagicMock(return_value=STUB_AT_SIGN) + ): + ret = cron.rm_special( + "root", 'echo "there is no job"', identifier='echo "there is no job"' + ) + self.assertEqual("absent", ret) def test_rm_job_is_absent(self): - with patch.dict(cron.__grains__, {'os': None}), \ - patch('salt.modules.cron._write_cron_lines', - new=MagicMock(return_value={'retcode': False})), \ - patch('salt.modules.cron.raw_cron', - new=MagicMock(return_value=STUB_SIMPLE_RAW_CRON)): - ret = cron.rm_job('DUMMY_USER', '/bin/echo NOT A DROID', 1, 2, 3, 4, 5) - self.assertEqual('absent', ret) + with patch.dict(cron.__grains__, {"os": None}), patch( + "salt.modules.cron._write_cron_lines", + new=MagicMock(return_value={"retcode": False}), + ), patch( + "salt.modules.cron.raw_cron", + new=MagicMock(return_value=STUB_SIMPLE_RAW_CRON), + ): + ret = cron.rm_job("DUMMY_USER", "/bin/echo NOT A DROID", 1, 2, 3, 4, 5) + self.assertEqual("absent", ret) diff --git a/tests/unit/modules/test_daemontools.py b/tests/unit/modules/test_daemontools.py index 0e5fa558ec7..6a5a553ca2c 100644 --- a/tests/unit/modules/test_daemontools.py +++ b/tests/unit/modules/test_daemontools.py @@ -1,127 +1,125 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) import os # Import Salt Libs import salt.modules.daemontools as daemontools from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DaemontoolsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.daemontools - ''' + """ def setup_loader_modules(self): return {daemontools: {}} def test_start(self): - ''' + """ Test for Starts service via daemontools - ''' + """ mock = MagicMock(return_value=None) - with patch.dict(daemontools.__salt__, {'file.remove': mock}): - mock = MagicMock(return_value='') - with patch.object(daemontools, '_service_path', mock): + with patch.dict(daemontools.__salt__, {"file.remove": mock}): + mock = MagicMock(return_value="") + with patch.object(daemontools, "_service_path", mock): mock = MagicMock(return_value=False) - with patch.dict(daemontools.__salt__, {'cmd.retcode': mock}): - self.assertTrue(daemontools.start('name')) + with patch.dict(daemontools.__salt__, {"cmd.retcode": mock}): + self.assertTrue(daemontools.start("name")) def test_stop(self): - ''' + """ Test for Stops service via daemontools - ''' + """ mock = MagicMock(return_value=None) - with patch.dict(daemontools.__salt__, {'file.touch': mock}): - mock = MagicMock(return_value='') - with patch.object(daemontools, '_service_path', mock): + with patch.dict(daemontools.__salt__, {"file.touch": mock}): + mock = MagicMock(return_value="") + with patch.object(daemontools, "_service_path", mock): mock = MagicMock(return_value=False) - with patch.dict(daemontools.__salt__, {'cmd.retcode': mock}): - self.assertTrue(daemontools.stop('name')) + with patch.dict(daemontools.__salt__, {"cmd.retcode": mock}): + self.assertTrue(daemontools.stop("name")) def test_term(self): - ''' + """ Test for Send a TERM to service via daemontools - ''' - mock = MagicMock(return_value='') - with patch.object(daemontools, '_service_path', mock): + """ + mock = MagicMock(return_value="") + with patch.object(daemontools, "_service_path", mock): mock = MagicMock(return_value=False) - with patch.dict(daemontools.__salt__, {'cmd.retcode': mock}): - self.assertTrue(daemontools.term('name')) + with patch.dict(daemontools.__salt__, {"cmd.retcode": mock}): + self.assertTrue(daemontools.term("name")) def test_reload_(self): - ''' + """ Test for Wrapper for term() - ''' + """ mock = MagicMock(return_value=None) - with patch.object(daemontools, 'term', mock): - self.assertEqual(daemontools.reload_('name'), None) + with patch.object(daemontools, "term", mock): + self.assertEqual(daemontools.reload_("name"), None) def test_restart(self): - ''' + """ Test for Restart service via daemontools. This will stop/start service - ''' + """ mock = MagicMock(return_value=False) - with patch.object(daemontools, 'stop', mock): - self.assertEqual(daemontools.restart('name'), 'restart False') + with patch.object(daemontools, "stop", mock): + self.assertEqual(daemontools.restart("name"), "restart False") def test_full_restart(self): - ''' + """ Test for Calls daemontools.restart() function - ''' + """ mock = MagicMock(return_value=None) - with patch.object(daemontools, 'restart', mock): - self.assertEqual(daemontools.restart('name'), None) + with patch.object(daemontools, "restart", mock): + self.assertEqual(daemontools.restart("name"), None) def test_status(self): - ''' + """ Test for Return the status for a service via daemontools, return pid if running - ''' - with patch('re.search', MagicMock(return_value=1)): - mock = MagicMock(return_value='') - with patch.object(daemontools, '_service_path', mock): - mock = MagicMock(return_value='name') - with patch.dict(daemontools.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(daemontools.status('name'), '') + """ + with patch("re.search", MagicMock(return_value=1)): + mock = MagicMock(return_value="") + with patch.object(daemontools, "_service_path", mock): + mock = MagicMock(return_value="name") + with patch.dict(daemontools.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual(daemontools.status("name"), "") def test_available(self): - ''' + """ Test for Returns ``True`` if the specified service is available, otherwise returns``False``. - ''' + """ mock = MagicMock(return_value=[]) - with patch.object(daemontools, 'get_all', mock): - self.assertFalse(daemontools.available('name')) + with patch.object(daemontools, "get_all", mock): + self.assertFalse(daemontools.available("name")) def test_missing(self): - ''' + """ Test for The inverse of daemontools.available. - ''' + """ mock = MagicMock(return_value=[]) - with patch.object(daemontools, 'get_all', mock): - self.assertTrue(daemontools.missing('name')) + with patch.object(daemontools, "get_all", mock): + self.assertTrue(daemontools.missing("name")) def test_get_all(self): - ''' + """ Test for Return a list of all available services - ''' + """ self.assertRaises(CommandExecutionError, daemontools.get_all) - with patch.object(daemontools, 'SERVICE_DIR', 'A'): - mock = MagicMock(return_value='A') - with patch.object(os, 'listdir', mock): - self.assertEqual(daemontools.get_all(), ['A']) + with patch.object(daemontools, "SERVICE_DIR", "A"): + mock = MagicMock(return_value="A") + with patch.object(os, "listdir", mock): + self.assertEqual(daemontools.get_all(), ["A"]) diff --git a/tests/unit/modules/test_data.py b/tests/unit/modules/test_data.py index 2a6e52ae19e..117e96cfd5e 100644 --- a/tests/unit/modules/test_data.py +++ b/tests/unit/modules/test_data.py @@ -1,132 +1,136 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - mock_open, -) - # Import Salt Libs import salt.modules.data as data +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class DataTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.data - ''' + """ + def setup_loader_modules(self): return {data: {}} # 'clear' function tests: 1 def test_clear(self): - ''' + """ Test if it clear out all of the data in the minion datastore - ''' - with patch('os.remove', MagicMock(return_value='')): - with patch.dict(data.__opts__, {'cachedir': ''}): + """ + with patch("os.remove", MagicMock(return_value="")): + with patch.dict(data.__opts__, {"cachedir": ""}): self.assertTrue(data.clear()) # 'load' function tests: 1 def test_load(self): - ''' + """ Test if it return all of the data in the minion datastore - ''' - with patch('salt.payload.Serial.load', MagicMock(return_value=True)): + """ + with patch("salt.payload.Serial.load", MagicMock(return_value=True)): mocked_fopen = MagicMock(return_value=True) mocked_fopen.__enter__ = MagicMock(return_value=mocked_fopen) mocked_fopen.__exit__ = MagicMock() - with patch('salt.utils.files.fopen', MagicMock(return_value=mocked_fopen)): - with patch('salt.payload.Serial.loads', MagicMock(return_value=True)): - with patch.dict(data.__opts__, {'cachedir': '/'}): + with patch("salt.utils.files.fopen", MagicMock(return_value=mocked_fopen)): + with patch("salt.payload.Serial.loads", MagicMock(return_value=True)): + with patch.dict(data.__opts__, {"cachedir": "/"}): self.assertTrue(data.load()) # 'dump' function tests: 3 def test_dump(self): - ''' + """ Test if it replace the entire datastore with a passed data structure - ''' - with patch.dict(data.__opts__, {'cachedir': '/'}): - with patch('salt.utils.files.fopen', mock_open()): + """ + with patch.dict(data.__opts__, {"cachedir": "/"}): + with patch("salt.utils.files.fopen", mock_open()): self.assertTrue(data.dump('{"eggs": "spam"}')) def test_dump_isinstance(self): - ''' + """ Test if it replace the entire datastore with a passed data structure - ''' - with patch('ast.literal_eval', MagicMock(return_value='')): - self.assertFalse(data.dump('salt')) + """ + with patch("ast.literal_eval", MagicMock(return_value="")): + self.assertFalse(data.dump("salt")) def test_dump_ioerror(self): - ''' + """ Test if it replace the entire datastore with a passed data structure - ''' - with patch.dict(data.__opts__, {'cachedir': '/'}): - mock = MagicMock(side_effect=IOError('')) - with patch('salt.utils.files.fopen', mock): + """ + with patch.dict(data.__opts__, {"cachedir": "/"}): + mock = MagicMock(side_effect=IOError("")) + with patch("salt.utils.files.fopen", mock): self.assertFalse(data.dump('{"eggs": "spam"}')) # 'update' function tests: 1 def test_update(self): - ''' + """ Test if it update a key with a value in the minion datastore - ''' - with patch('salt.modules.data.load', MagicMock(return_value={})), \ - patch('salt.modules.data.dump', MagicMock(return_value=True)): - self.assertTrue(data.update('foo', 'salt')) + """ + with patch("salt.modules.data.load", MagicMock(return_value={})), patch( + "salt.modules.data.dump", MagicMock(return_value=True) + ): + self.assertTrue(data.update("foo", "salt")) # 'get' function tests: 2 def test_get(self): - ''' + """ Test if it gets a value from the minion datastore - ''' - with patch('salt.modules.data.load', MagicMock(return_value={'salt': 'SALT'})): - self.assertEqual(data.get('salt'), 'SALT') + """ + with patch("salt.modules.data.load", MagicMock(return_value={"salt": "SALT"})): + self.assertEqual(data.get("salt"), "SALT") def test_get_vals(self): - ''' + """ Test if it gets values from the minion datastore - ''' - with patch('salt.modules.data.load', - MagicMock(return_value={'salt': 'SALT', 'salt1': 'SALT1'})): - self.assertEqual(data.get(['salt', 'salt1']), ['SALT', 'SALT1']) + """ + with patch( + "salt.modules.data.load", + MagicMock(return_value={"salt": "SALT", "salt1": "SALT1"}), + ): + self.assertEqual(data.get(["salt", "salt1"]), ["SALT", "SALT1"]) # 'cas' function tests: 1 def test_cas_not_load(self): - ''' + """ Test if it check and set a value in the minion datastore - ''' - with patch('salt.modules.data.load', - MagicMock(return_value={'salt': 'SALT', 'salt1': 'SALT1'})): - self.assertFalse(data.cas('salt3', 'SALT', 'SALTSTACK')) + """ + with patch( + "salt.modules.data.load", + MagicMock(return_value={"salt": "SALT", "salt1": "SALT1"}), + ): + self.assertFalse(data.cas("salt3", "SALT", "SALTSTACK")) def test_cas_not_equal(self): - ''' + """ Test if it check and set a value in the minion datastore - ''' - with patch('salt.modules.data.load', - MagicMock(return_value={'salt': 'SALT', 'salt1': 'SALT1'})): - self.assertFalse(data.cas('salt', 'SALT', 'SALTSTACK')) + """ + with patch( + "salt.modules.data.load", + MagicMock(return_value={"salt": "SALT", "salt1": "SALT1"}), + ): + self.assertFalse(data.cas("salt", "SALT", "SALTSTACK")) def test_cas(self): - ''' + """ Test if it check and set a value in the minion datastore - ''' - with patch('salt.modules.data.load', - MagicMock(return_value={'salt': 'SALT', 'salt1': 'SALT1'})), \ - patch('salt.modules.data.dump', - MagicMock(return_value=True)): - self.assertTrue(data.cas('salt', 'SALTSTACK', 'SALT')) + """ + with patch( + "salt.modules.data.load", + MagicMock(return_value={"salt": "SALT", "salt1": "SALT1"}), + ), patch("salt.modules.data.dump", MagicMock(return_value=True)): + self.assertTrue(data.cas("salt", "SALTSTACK", "SALT")) diff --git a/tests/unit/modules/test_ddns.py b/tests/unit/modules/test_ddns.py index 191c53cfd58..d432426a3a5 100644 --- a/tests/unit/modules/test_ddns.py +++ b/tests/unit/modules/test_ddns.py @@ -1,74 +1,68 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import textwrap + +import salt.modules.ddns as ddns + +# Import Salt Libs +import salt.utils.json + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase, skipIf + try: import dns.query import dns.tsigkeyring + HAS_DNS = True except ImportError: HAS_DNS = False -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - mock_open, - MagicMock, - patch, -) - -# Import Salt Libs -import salt.utils.json -import salt.modules.ddns as ddns - - -@skipIf(HAS_DNS is False, 'dnspython libs not installed') +@skipIf(HAS_DNS is False, "dnspython libs not installed") class DDNSTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for the salt.modules.ddns module - ''' + """ def setup_loader_modules(self): return {ddns: {}} def test_add_host(self): - ''' + """ Test cases for Add, replace, or update the A and PTR (reverse) records for a host. - ''' - with patch('salt.modules.ddns.update') as ddns_update: + """ + with patch("salt.modules.ddns.update") as ddns_update: ddns_update.return_value = False - self.assertFalse(ddns.add_host(zone='A', - name='B', - ttl=1, - ip='172.27.0.0')) + self.assertFalse(ddns.add_host(zone="A", name="B", ttl=1, ip="172.27.0.0")) ddns_update.return_value = True - self.assertTrue(ddns.add_host(zone='A', - name='B', - ttl=1, - ip='172.27.0.0')) + self.assertTrue(ddns.add_host(zone="A", name="B", ttl=1, ip="172.27.0.0")) def test_delete_host(self): - ''' + """ Tests for delete the forward and reverse records for a host. - ''' - with patch('salt.modules.ddns.delete') as ddns_delete: + """ + with patch("salt.modules.ddns.delete") as ddns_delete: ddns_delete.return_value = False - with patch.object(dns.query, 'udp') as mock: - mock.answer = [{'address': 'localhost'}] - self.assertFalse(ddns.delete_host(zone='A', name='B')) + with patch.object(dns.query, "udp") as mock: + mock.answer = [{"address": "localhost"}] + self.assertFalse(ddns.delete_host(zone="A", name="B")) def test_update(self): - ''' + """ Test to add, replace, or update a DNS record. - ''' - mock_request = textwrap.dedent('''\ + """ + mock_request = textwrap.dedent( + """\ id 29380 opcode QUERY rcode NOERROR @@ -77,12 +71,13 @@ class DDNSTestCase(TestCase, LoaderModuleMockMixin): name.zone. IN AAAA ;ANSWER ;AUTHORITY - ;ADDITIONAL''') + ;ADDITIONAL""" + ) mock_rdtype = 28 # rdtype of AAAA record class MockRrset(object): def __init__(self): - self.items = [{'address': 'localhost'}] + self.items = [{"address": "localhost"}] self.ttl = 2 class MockAnswer(object): @@ -95,22 +90,28 @@ class DDNSTestCase(TestCase, LoaderModuleMockMixin): def mock_udp_query(*args, **kwargs): return MockAnswer - with patch.object(dns.message, 'make_query', MagicMock(return_value=mock_request)): - with patch.object(dns.query, 'udp', mock_udp_query()): - with patch.object(dns.rdatatype, 'from_text', MagicMock(return_value=mock_rdtype)): - with patch.object(ddns, '_get_keyring', return_value=None): - with patch.object(ddns, '_config', return_value=None): - self.assertTrue(ddns.update('zone', 'name', 1, 'AAAA', '::1')) + with patch.object( + dns.message, "make_query", MagicMock(return_value=mock_request) + ): + with patch.object(dns.query, "udp", mock_udp_query()): + with patch.object( + dns.rdatatype, "from_text", MagicMock(return_value=mock_rdtype) + ): + with patch.object(ddns, "_get_keyring", return_value=None): + with patch.object(ddns, "_config", return_value=None): + self.assertTrue( + ddns.update("zone", "name", 1, "AAAA", "::1") + ) def test_delete(self): - ''' + """ Test to delete a DNS record. - ''' - file_data = salt.utils.json.dumps({'A': 'B'}) + """ + file_data = salt.utils.json.dumps({"A": "B"}) class MockAnswer(object): def __init__(self, *args, **kwargs): - self.answer = [{'address': 'localhost'}] + self.answer = [{"address": "localhost"}] def rcode(self): return 0 @@ -118,9 +119,11 @@ class DDNSTestCase(TestCase, LoaderModuleMockMixin): def mock_udp_query(*args, **kwargs): return MockAnswer - with patch.object(dns.query, 'udp', mock_udp_query()): - with patch('salt.utils.files.fopen', mock_open(read_data=file_data), create=True): - with patch.object(dns.tsigkeyring, 'from_text', return_value=True): - with patch.object(ddns, '_get_keyring', return_value=None): - with patch.object(ddns, '_config', return_value=None): - self.assertTrue(ddns.delete(zone='A', name='B')) + with patch.object(dns.query, "udp", mock_udp_query()): + with patch( + "salt.utils.files.fopen", mock_open(read_data=file_data), create=True + ): + with patch.object(dns.tsigkeyring, "from_text", return_value=True): + with patch.object(ddns, "_get_keyring", return_value=None): + with patch.object(ddns, "_config", return_value=None): + self.assertTrue(ddns.delete(zone="A", name="B")) diff --git a/tests/unit/modules/test_deb_apache.py b/tests/unit/modules/test_deb_apache.py index 11a6ac61f82..4c5af91c89a 100644 --- a/tests/unit/modules/test_deb_apache.py +++ b/tests/unit/modules/test_deb_apache.py @@ -1,354 +1,402 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.deb_apache as deb_apache from salt.ext import six +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DebApacheTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.deb_apache - ''' + """ + def setup_loader_modules(self): return {deb_apache: {}} # 'check_site_enabled' function tests: 3 def test_check_site_enabled(self): - ''' + """ Test if the specific Site symlink is enabled. - ''' - with patch('os.path.islink', MagicMock(return_value=True)): - self.assertTrue(deb_apache.check_site_enabled('saltstack.com')) + """ + with patch("os.path.islink", MagicMock(return_value=True)): + self.assertTrue(deb_apache.check_site_enabled("saltstack.com")) def test_check_site_enabled_default(self): - ''' + """ Test if the specific Site symlink is enabled. - ''' - with patch('os.path.islink', MagicMock(side_effect=[False, True])): - self.assertTrue(deb_apache.check_site_enabled('default')) + """ + with patch("os.path.islink", MagicMock(side_effect=[False, True])): + self.assertTrue(deb_apache.check_site_enabled("default")) def test_check_site_enabled_false(self): - ''' + """ Test if the specific Site symlink is enabled. - ''' - with patch('os.path.islink', MagicMock(return_value=False)): - self.assertFalse(deb_apache.check_site_enabled('saltstack.com')) + """ + with patch("os.path.islink", MagicMock(return_value=False)): + self.assertFalse(deb_apache.check_site_enabled("saltstack.com")) # 'a2ensite' function tests: 4 def test_a2ensite_notfound(self): - ''' + """ Test if it runs a2ensite for the given site. - ''' + """ mock = MagicMock(return_value=1) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2ensite('saltstack.com'), - {'Name': 'Apache2 Enable Site', - 'Site': 'saltstack.com', - 'Status': 'Site saltstack.com Not found'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2ensite("saltstack.com"), + { + "Name": "Apache2 Enable Site", + "Site": "saltstack.com", + "Status": "Site saltstack.com Not found", + }, + ) def test_a2ensite_enabled(self): - ''' + """ Test if it runs a2ensite for the given site. - ''' + """ mock = MagicMock(return_value=0) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2ensite('saltstack.com'), - {'Name': 'Apache2 Enable Site', - 'Site': 'saltstack.com', - 'Status': 'Site saltstack.com enabled'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2ensite("saltstack.com"), + { + "Name": "Apache2 Enable Site", + "Site": "saltstack.com", + "Status": "Site saltstack.com enabled", + }, + ) def test_a2ensite(self): - ''' + """ Test if it runs a2ensite for the given site. - ''' + """ mock = MagicMock(return_value=2) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2ensite('saltstack.com'), - {'Name': 'Apache2 Enable Site', - 'Site': 'saltstack.com', - 'Status': 2}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2ensite("saltstack.com"), + {"Name": "Apache2 Enable Site", "Site": "saltstack.com", "Status": 2}, + ) def test_a2ensite_exception(self): - ''' + """ Test if it runs a2ensite for the given site. - ''' - mock = MagicMock(side_effect=Exception('error')) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(six.text_type(deb_apache.a2ensite('saltstack.com')), - 'error') + """ + mock = MagicMock(side_effect=Exception("error")) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + six.text_type(deb_apache.a2ensite("saltstack.com")), "error" + ) # 'a2dissite' function tests: 4 def test_a2dissite_notfound(self): - ''' + """ Test if it runs a2dissite for the given site. - ''' + """ mock = MagicMock(return_value=256) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2dissite('saltstack.com'), - {'Name': 'Apache2 Disable Site', - 'Site': 'saltstack.com', - 'Status': 'Site saltstack.com Not found'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2dissite("saltstack.com"), + { + "Name": "Apache2 Disable Site", + "Site": "saltstack.com", + "Status": "Site saltstack.com Not found", + }, + ) def test_a2dissite_disabled(self): - ''' + """ Test if it runs a2dissite for the given site. - ''' + """ mock = MagicMock(return_value=0) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2dissite('saltstack.com'), - {'Name': 'Apache2 Disable Site', - 'Site': 'saltstack.com', - 'Status': 'Site saltstack.com disabled'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2dissite("saltstack.com"), + { + "Name": "Apache2 Disable Site", + "Site": "saltstack.com", + "Status": "Site saltstack.com disabled", + }, + ) def test_a2dissite(self): - ''' + """ Test if it runs a2dissite for the given site. - ''' + """ mock = MagicMock(return_value=2) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2dissite('saltstack.com'), - {'Name': 'Apache2 Disable Site', - 'Site': 'saltstack.com', - 'Status': 2}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2dissite("saltstack.com"), + {"Name": "Apache2 Disable Site", "Site": "saltstack.com", "Status": 2}, + ) def test_a2dissite_exception(self): - ''' + """ Test if it runs a2dissite for the given site. - ''' - mock = MagicMock(side_effect=Exception('error')) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(six.text_type(deb_apache.a2dissite('saltstack.com')), - 'error') + """ + mock = MagicMock(side_effect=Exception("error")) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + six.text_type(deb_apache.a2dissite("saltstack.com")), "error" + ) # 'check_mod_enabled' function tests: 2 def test_check_mod_enabled(self): - ''' + """ Test if the specific mod symlink is enabled. - ''' - with patch('os.path.islink', MagicMock(return_value=True)): - self.assertTrue(deb_apache.check_mod_enabled('status.conf')) + """ + with patch("os.path.islink", MagicMock(return_value=True)): + self.assertTrue(deb_apache.check_mod_enabled("status.conf")) def test_check_mod_enabled_false(self): - ''' + """ Test if the specific mod symlink is enabled. - ''' - with patch('os.path.islink', MagicMock(return_value=False)): - self.assertFalse(deb_apache.check_mod_enabled('status.conf')) + """ + with patch("os.path.islink", MagicMock(return_value=False)): + self.assertFalse(deb_apache.check_mod_enabled("status.conf")) # 'a2enmod' function tests: 4 def test_a2enmod_notfound(self): - ''' + """ Test if it runs a2enmod for the given module. - ''' + """ mock = MagicMock(return_value=1) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2enmod('vhost_alias'), - {'Name': 'Apache2 Enable Mod', - 'Mod': 'vhost_alias', - 'Status': 'Mod vhost_alias Not found'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2enmod("vhost_alias"), + { + "Name": "Apache2 Enable Mod", + "Mod": "vhost_alias", + "Status": "Mod vhost_alias Not found", + }, + ) def test_a2enmod_enabled(self): - ''' + """ Test if it runs a2enmod for the given module. - ''' + """ mock = MagicMock(return_value=0) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2enmod('vhost_alias'), - {'Name': 'Apache2 Enable Mod', - 'Mod': 'vhost_alias', - 'Status': 'Mod vhost_alias enabled'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2enmod("vhost_alias"), + { + "Name": "Apache2 Enable Mod", + "Mod": "vhost_alias", + "Status": "Mod vhost_alias enabled", + }, + ) def test_a2enmod(self): - ''' + """ Test if it runs a2enmod for the given module. - ''' + """ mock = MagicMock(return_value=2) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2enmod('vhost_alias'), - {'Name': 'Apache2 Enable Mod', - 'Mod': 'vhost_alias', - 'Status': 2}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2enmod("vhost_alias"), + {"Name": "Apache2 Enable Mod", "Mod": "vhost_alias", "Status": 2}, + ) def test_a2enmod_exception(self): - ''' + """ Test if it runs a2enmod for the given module. - ''' - mock = MagicMock(side_effect=Exception('error')) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(six.text_type(deb_apache.a2enmod('vhost_alias')), - 'error') + """ + mock = MagicMock(side_effect=Exception("error")) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual(six.text_type(deb_apache.a2enmod("vhost_alias")), "error") # 'a2dismod' function tests: 4 def test_a2dismod_notfound(self): - ''' + """ Test if it runs a2dismod for the given module. - ''' + """ mock = MagicMock(return_value=256) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2dismod('vhost_alias'), - {'Name': 'Apache2 Disable Mod', - 'Mod': 'vhost_alias', - 'Status': 'Mod vhost_alias Not found'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2dismod("vhost_alias"), + { + "Name": "Apache2 Disable Mod", + "Mod": "vhost_alias", + "Status": "Mod vhost_alias Not found", + }, + ) def test_a2dismod_disabled(self): - ''' + """ Test if it runs a2dismod for the given module. - ''' + """ mock = MagicMock(return_value=0) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2dismod('vhost_alias'), - {'Name': 'Apache2 Disable Mod', - 'Mod': 'vhost_alias', - 'Status': 'Mod vhost_alias disabled'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2dismod("vhost_alias"), + { + "Name": "Apache2 Disable Mod", + "Mod": "vhost_alias", + "Status": "Mod vhost_alias disabled", + }, + ) def test_a2dismod(self): - ''' + """ Test if it runs a2dismod for the given module. - ''' + """ mock = MagicMock(return_value=2) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2dismod('vhost_alias'), - {'Name': 'Apache2 Disable Mod', - 'Mod': 'vhost_alias', - 'Status': 2}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2dismod("vhost_alias"), + {"Name": "Apache2 Disable Mod", "Mod": "vhost_alias", "Status": 2}, + ) def test_a2dismod_exception(self): - ''' + """ Test if it runs a2dismod for the given module. - ''' - mock = MagicMock(side_effect=Exception('error')) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(six.text_type(deb_apache.a2dismod('vhost_alias')), - 'error') + """ + mock = MagicMock(side_effect=Exception("error")) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual(six.text_type(deb_apache.a2dismod("vhost_alias")), "error") # 'check_conf_enabled' function tests: 2 def test_check_conf_enabled(self): - ''' + """ Test if the specific conf symlink is enabled. - ''' - with patch('os.path.islink', MagicMock(return_value=True)): - self.assertTrue(deb_apache.check_conf_enabled('security.conf')) + """ + with patch("os.path.islink", MagicMock(return_value=True)): + self.assertTrue(deb_apache.check_conf_enabled("security.conf")) def test_check_conf_enabled_false(self): - ''' + """ Test if the specific conf symlink is enabled. - ''' - with patch('os.path.islink', MagicMock(return_value=False)): - self.assertFalse(deb_apache.check_conf_enabled('security.conf')) + """ + with patch("os.path.islink", MagicMock(return_value=False)): + self.assertFalse(deb_apache.check_conf_enabled("security.conf")) # 'a2enconf' function tests: 4 def test_a2enconf_notfound(self): - ''' + """ Test if it runs a2enconf for the given conf. - ''' - with patch('salt.utils.path.which', MagicMock(return_value='a2enconf')): + """ + with patch("salt.utils.path.which", MagicMock(return_value="a2enconf")): mock = MagicMock(return_value=1) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2enconf('security'), - {'Name': 'Apache2 Enable Conf', - 'Conf': 'security', - 'Status': 'Conf security Not found'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2enconf("security"), + { + "Name": "Apache2 Enable Conf", + "Conf": "security", + "Status": "Conf security Not found", + }, + ) def test_a2enconf_enabled(self): - ''' + """ Test if it runs a2enconf for the given conf. - ''' - with patch('salt.utils.path.which', MagicMock(return_value='a2enconf')): + """ + with patch("salt.utils.path.which", MagicMock(return_value="a2enconf")): mock = MagicMock(return_value=0) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2enconf('security'), - {'Name': 'Apache2 Enable Conf', - 'Conf': 'security', - 'Status': 'Conf security enabled'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2enconf("security"), + { + "Name": "Apache2 Enable Conf", + "Conf": "security", + "Status": "Conf security enabled", + }, + ) def test_a2enconf(self): - ''' + """ Test if it runs a2enconf for the given conf. - ''' - with patch('salt.utils.path.which', MagicMock(return_value='a2enconf')): + """ + with patch("salt.utils.path.which", MagicMock(return_value="a2enconf")): mock = MagicMock(return_value=2) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2enconf('security'), - {'Name': 'Apache2 Enable Conf', - 'Conf': 'security', - 'Status': 2}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2enconf("security"), + {"Name": "Apache2 Enable Conf", "Conf": "security", "Status": 2}, + ) def test_a2enconf_exception(self): - ''' + """ Test if it runs a2enconf for the given conf. - ''' - with patch('salt.utils.path.which', MagicMock(return_value='a2enconf')): - mock = MagicMock(side_effect=Exception('error')) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(six.text_type(deb_apache.a2enconf('security')), - 'error') + """ + with patch("salt.utils.path.which", MagicMock(return_value="a2enconf")): + mock = MagicMock(side_effect=Exception("error")) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + six.text_type(deb_apache.a2enconf("security")), "error" + ) # 'a2disconf' function tests: 4 def test_a2disconf_notfound(self): - ''' + """ Test if it runs a2disconf for the given conf. - ''' - with patch('salt.utils.path.which', MagicMock(return_value='a2disconf')): + """ + with patch("salt.utils.path.which", MagicMock(return_value="a2disconf")): mock = MagicMock(return_value=256) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2disconf('security'), - {'Name': 'Apache2 Disable Conf', - 'Conf': 'security', - 'Status': 'Conf security Not found'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2disconf("security"), + { + "Name": "Apache2 Disable Conf", + "Conf": "security", + "Status": "Conf security Not found", + }, + ) def test_a2disconf_disabled(self): - ''' + """ Test if it runs a2disconf for the given conf. - ''' - with patch('salt.utils.path.which', MagicMock(return_value='a2disconf')): + """ + with patch("salt.utils.path.which", MagicMock(return_value="a2disconf")): mock = MagicMock(return_value=0) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2disconf('security'), - {'Name': 'Apache2 Disable Conf', - 'Conf': 'security', - 'Status': 'Conf security disabled'}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2disconf("security"), + { + "Name": "Apache2 Disable Conf", + "Conf": "security", + "Status": "Conf security disabled", + }, + ) def test_a2disconf(self): - ''' + """ Test if it runs a2disconf for the given conf. - ''' - with patch('salt.utils.path.which', MagicMock(return_value='a2disconf')): + """ + with patch("salt.utils.path.which", MagicMock(return_value="a2disconf")): mock = MagicMock(return_value=2) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(deb_apache.a2disconf('security'), - {'Name': 'Apache2 Disable Conf', - 'Conf': 'security', - 'Status': 2}) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + deb_apache.a2disconf("security"), + {"Name": "Apache2 Disable Conf", "Conf": "security", "Status": 2}, + ) def test_a2disconf_exception(self): - ''' + """ Test if it runs a2disconf for the given conf. - ''' - with patch('salt.utils.path.which', MagicMock(return_value='a2disconf')): - mock = MagicMock(side_effect=Exception('error')) - with patch.dict(deb_apache.__salt__, {'cmd.retcode': mock}): - self.assertEqual(six.text_type(deb_apache.a2disconf('security')), - 'error') + """ + with patch("salt.utils.path.which", MagicMock(return_value="a2disconf")): + mock = MagicMock(side_effect=Exception("error")) + with patch.dict(deb_apache.__salt__, {"cmd.retcode": mock}): + self.assertEqual( + six.text_type(deb_apache.a2disconf("security")), "error" + ) diff --git a/tests/unit/modules/test_deb_postgres.py b/tests/unit/modules/test_deb_postgres.py index 8439b87c5b0..679e9c45524 100644 --- a/tests/unit/modules/test_deb_postgres.py +++ b/tests/unit/modules/test_deb_postgres.py @@ -3,55 +3,59 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import Mock, patch +import salt.modules.deb_postgres as deb_postgres # Import salt libs from salt.ext import six -import salt.modules.deb_postgres as deb_postgres -LSCLUSTER = '''\ +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock, patch +from tests.support.unit import TestCase + +LSCLUSTER = """\ 8.4 main 5432 online postgres /srv/8.4/main \ /var/log/postgresql/postgresql-8.4-main.log 9.1 main 5433 online postgres /srv/9.1/main \ /var/log/postgresql/postgresql-9.1-main.log -''' +""" class PostgresClusterTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - self.cmd_run_all_mock = Mock(return_value={'stdout': LSCLUSTER}) - self.addCleanup(delattr, self, 'cmd_run_all_mock') - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/pg_createcluster')) + self.cmd_run_all_mock = Mock(return_value={"stdout": LSCLUSTER}) + self.addCleanup(delattr, self, "cmd_run_all_mock") + patcher = patch( + "salt.utils.path.which", Mock(return_value="/usr/bin/pg_createcluster") + ) patcher.start() self.addCleanup(patcher.stop) return { deb_postgres: { - '__salt__': { - 'config.option': Mock(), - 'cmd.run_all': self.cmd_run_all_mock, - 'file.chown': Mock(), - 'file.remove': Mock(), + "__salt__": { + "config.option": Mock(), + "cmd.run_all": self.cmd_run_all_mock, + "file.chown": Mock(), + "file.remove": Mock(), } } } def test_cluster_create(self): deb_postgres.cluster_create( - '9.3', - 'main', - port='5432', - locale='fr_FR', - encoding='UTF-8', - datadir='/opt/postgresql' + "9.3", + "main", + port="5432", + locale="fr_FR", + encoding="UTF-8", + datadir="/opt/postgresql", + ) + cmdstr = ( + "/usr/bin/pg_createcluster " + "--port 5432 --locale fr_FR --encoding UTF-8 " + "--datadir /opt/postgresql " + "9.3 main" ) - cmdstr = '/usr/bin/pg_createcluster ' \ - '--port 5432 --locale fr_FR --encoding UTF-8 ' \ - '--datadir /opt/postgresql ' \ - '9.3 main' self.assertEqual(cmdstr, self.cmd_run_all_mock.call_args[0][0]) # XXX version should be a string but from cmdline you get a float @@ -65,20 +69,21 @@ class PostgresClusterTestCase(TestCase, LoaderModuleMockMixin): class PostgresLsClusterTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - self.cmd_run_all_mock = Mock(return_value={'stdout': LSCLUSTER}) - self.addCleanup(delattr, self, 'cmd_run_all_mock') - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/pg_lsclusters')) + self.cmd_run_all_mock = Mock(return_value={"stdout": LSCLUSTER}) + self.addCleanup(delattr, self, "cmd_run_all_mock") + patcher = patch( + "salt.utils.path.which", Mock(return_value="/usr/bin/pg_lsclusters") + ) patcher.start() self.addCleanup(patcher.stop) return { deb_postgres: { - '__salt__': { - 'config.option': Mock(), - 'cmd.run_all': self.cmd_run_all_mock, - 'file.chown': Mock(), - 'file.remove': Mock(), + "__salt__": { + "config.option": Mock(), + "cmd.run_all": self.cmd_run_all_mock, + "file.chown": Mock(), + "file.remove": Mock(), } } } @@ -87,24 +92,30 @@ class PostgresLsClusterTestCase(TestCase, LoaderModuleMockMixin): stdout = LSCLUSTER self.maxDiff = None self.assertDictEqual( - {('8.4/main'): { - 'port': 5432, - 'status': 'online', - 'user': 'postgres', - 'datadir': '/srv/8.4/main', - 'log': '/var/log/postgresql/postgresql-8.4-main.log'}, - ('9.1/main'): { - 'port': 5433, - 'status': 'online', - 'user': 'postgres', - 'datadir': '/srv/9.1/main', - 'log': '/var/log/postgresql/postgresql-9.1-main.log'}}, - deb_postgres._parse_pg_lscluster(stdout)) + { + ("8.4/main"): { + "port": 5432, + "status": "online", + "user": "postgres", + "datadir": "/srv/8.4/main", + "log": "/var/log/postgresql/postgresql-8.4-main.log", + }, + ("9.1/main"): { + "port": 5433, + "status": "online", + "user": "postgres", + "datadir": "/srv/9.1/main", + "log": "/var/log/postgresql/postgresql-9.1-main.log", + }, + }, + deb_postgres._parse_pg_lscluster(stdout), + ) def test_cluster_list(self): return_list = deb_postgres.cluster_list() - self.assertEqual('/usr/bin/pg_lsclusters --no-header', - self.cmd_run_all_mock.call_args[0][0]) + self.assertEqual( + "/usr/bin/pg_lsclusters --no-header", self.cmd_run_all_mock.call_args[0][0] + ) if six.PY2: # Python 3 returns iterable views (dict_keys in this case) on # dict.keys() calls instead of lists. We should only perform @@ -114,34 +125,38 @@ class PostgresLsClusterTestCase(TestCase, LoaderModuleMockMixin): self.assertIsInstance(return_dict, dict) def test_cluster_exists(self): - self.assertTrue(deb_postgres.cluster_exists('8.4') is True) - self.assertTrue(deb_postgres.cluster_exists('8.4', 'main') is True) - self.assertFalse(deb_postgres.cluster_exists('3.4', 'main')) + self.assertTrue(deb_postgres.cluster_exists("8.4") is True) + self.assertTrue(deb_postgres.cluster_exists("8.4", "main") is True) + self.assertFalse(deb_postgres.cluster_exists("3.4", "main")) class PostgresDeleteClusterTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - self.cmd_run_all_mock = Mock(return_value={'stdout': LSCLUSTER}) - self.addCleanup(delattr, self, 'cmd_run_all_mock') - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/pg_dropcluster')) + self.cmd_run_all_mock = Mock(return_value={"stdout": LSCLUSTER}) + self.addCleanup(delattr, self, "cmd_run_all_mock") + patcher = patch( + "salt.utils.path.which", Mock(return_value="/usr/bin/pg_dropcluster") + ) patcher.start() self.addCleanup(patcher.stop) return { deb_postgres: { - '__salt__': { - 'config.option': Mock(), - 'cmd.run_all': self.cmd_run_all_mock, - 'file.chown': Mock(), - 'file.remove': Mock(), + "__salt__": { + "config.option": Mock(), + "cmd.run_all": self.cmd_run_all_mock, + "file.chown": Mock(), + "file.remove": Mock(), } } } def test_cluster_delete(self): - deb_postgres.cluster_remove('9.3', 'main') - self.assertEqual('/usr/bin/pg_dropcluster 9.3 main', - self.cmd_run_all_mock.call_args[0][0]) - deb_postgres.cluster_remove('9.3', 'main', stop=True) - self.assertEqual('/usr/bin/pg_dropcluster --stop 9.3 main', - self.cmd_run_all_mock.call_args[0][0]) + deb_postgres.cluster_remove("9.3", "main") + self.assertEqual( + "/usr/bin/pg_dropcluster 9.3 main", self.cmd_run_all_mock.call_args[0][0] + ) + deb_postgres.cluster_remove("9.3", "main", stop=True) + self.assertEqual( + "/usr/bin/pg_dropcluster --stop 9.3 main", + self.cmd_run_all_mock.call_args[0][0], + ) diff --git a/tests/unit/modules/test_debconfmod.py b/tests/unit/modules/test_debconfmod.py index c0a89d62597..13ab1be1734 100644 --- a/tests/unit/modules/test_debconfmod.py +++ b/tests/unit/modules/test_debconfmod.py @@ -1,85 +1,85 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.debconfmod as debconfmod -import os + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class DebconfmodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.DebconfmodTestCase - ''' + """ + def setup_loader_modules(self): return {debconfmod: {}} def test_get_selections(self): - ''' + """ Test for Answers to debconf questions for all packages - ''' + """ mock = MagicMock(return_value=[]) - with patch.dict(debconfmod.__salt__, {'cmd.run_stdout': mock}): - with patch.object(debconfmod, '_unpack_lines', mock): + with patch.dict(debconfmod.__salt__, {"cmd.run_stdout": mock}): + with patch.object(debconfmod, "_unpack_lines", mock): self.assertEqual(debconfmod.get_selections(False), {}) def test_show(self): - ''' + """ Test for Answers to debconf questions for a package - ''' + """ mock = MagicMock(return_value={}) - with patch.object(debconfmod, 'get_selections', mock): - self.assertEqual(debconfmod.show('name'), None) + with patch.object(debconfmod, "get_selections", mock): + self.assertEqual(debconfmod.show("name"), None) def test_set_(self): - ''' + """ Test for Set answers to debconf questions for a package. - ''' + """ mock = MagicMock(return_value=None) - with patch.object(os, 'write', mock): - with patch.object(os, 'close', mock): - with patch.object(debconfmod, '_set_file', mock): - with patch.object(os, 'unlink', mock): - self.assertTrue(debconfmod.set_('package', - 'question', - 'type', 'value')) + with patch.object(os, "write", mock): + with patch.object(os, "close", mock): + with patch.object(debconfmod, "_set_file", mock): + with patch.object(os, "unlink", mock): + self.assertTrue( + debconfmod.set_("package", "question", "type", "value") + ) def test_set_template(self): - ''' + """ Test for Set answers to debconf questions from a template. - ''' - mock = MagicMock(return_value='A') - with patch.dict(debconfmod.__salt__, {'cp.get_template': mock}): - with patch.object(debconfmod, 'set_file', mock): - self.assertEqual(debconfmod.set_template('path', - 'template', - 'context', - 'defaults', - 'saltenv'), 'A') + """ + mock = MagicMock(return_value="A") + with patch.dict(debconfmod.__salt__, {"cp.get_template": mock}): + with patch.object(debconfmod, "set_file", mock): + self.assertEqual( + debconfmod.set_template( + "path", "template", "context", "defaults", "saltenv" + ), + "A", + ) def test_set_file(self): - ''' + """ Test for Set answers to debconf questions from a file. - ''' - mock = MagicMock(return_value='A') - with patch.dict(debconfmod.__salt__, {'cp.cache_file': mock}): + """ + mock = MagicMock(return_value="A") + with patch.dict(debconfmod.__salt__, {"cp.cache_file": mock}): mock = MagicMock(return_value=None) - with patch.object(debconfmod, '_set_file', mock): - self.assertTrue(debconfmod.set_file('path')) + with patch.object(debconfmod, "_set_file", mock): + self.assertTrue(debconfmod.set_file("path")) mock = MagicMock(return_value=False) - with patch.dict(debconfmod.__salt__, {'cp.cache_file': mock}): + with patch.dict(debconfmod.__salt__, {"cp.cache_file": mock}): mock = MagicMock(return_value=None) - with patch.object(debconfmod, '_set_file', mock): - self.assertFalse(debconfmod.set_file('path')) + with patch.object(debconfmod, "_set_file", mock): + self.assertFalse(debconfmod.set_file("path")) diff --git a/tests/unit/modules/test_debian_ip.py b/tests/unit/modules/test_debian_ip.py index 74b4e54b684..ccb40a5cd6d 100644 --- a/tests/unit/modules/test_debian_ip.py +++ b/tests/unit/modules/test_debian_ip.py @@ -1,570 +1,686 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import tempfile -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) +# Import third party libs +import jinja2.exceptions # Import Salt Libs import salt.modules.debian_ip as debian_ip import salt.utils.platform -# Import third party libs -import jinja2.exceptions +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -@skipIf(salt.utils.platform.is_windows(), 'Do not run these tests on Windows') +@skipIf(salt.utils.platform.is_windows(), "Do not run these tests on Windows") class DebianIpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.debian_ip - ''' + """ + def setup_loader_modules(self): return {debian_ip: {}} # 'build_bond' function tests: 3 def test_build_bond(self): - ''' + """ Test if it create a bond script in /etc/modprobe.d with the passed settings and load the bonding kernel module. - ''' - with patch('salt.modules.debian_ip._parse_settings_bond', - MagicMock(return_value={})), \ - patch('salt.modules.debian_ip._write_file', - MagicMock(return_value=True)): + """ + with patch( + "salt.modules.debian_ip._parse_settings_bond", MagicMock(return_value={}) + ), patch("salt.modules.debian_ip._write_file", MagicMock(return_value=True)): mock = MagicMock(return_value=1) - with patch.dict(debian_ip.__grains__, {'osrelease': mock}): + with patch.dict(debian_ip.__grains__, {"osrelease": mock}): mock = MagicMock(return_value=True) - with patch.dict(debian_ip.__salt__, {'kmod.load': mock, - 'pkg.install': mock}): - self.assertEqual(debian_ip.build_bond('bond0'), '') + with patch.dict( + debian_ip.__salt__, {"kmod.load": mock, "pkg.install": mock} + ): + self.assertEqual(debian_ip.build_bond("bond0"), "") def test_error_message_iface_should_process_non_str_expected(self): - values = [1, True, False, 'no-kaboom'] - iface = 'ethtest' - option = 'test' + values = [1, True, False, "no-kaboom"] + iface = "ethtest" + option = "test" msg = debian_ip._error_msg_iface(iface, option, values) - self.assertTrue(msg.endswith('[1|True|False|no-kaboom]'), msg) + self.assertTrue(msg.endswith("[1|True|False|no-kaboom]"), msg) def test_error_message_network_should_process_non_str_expected(self): - values = [1, True, False, 'no-kaboom'] - msg = debian_ip._error_msg_network('fnord', values) - self.assertTrue(msg.endswith('[1|True|False|no-kaboom]'), msg) + values = [1, True, False, "no-kaboom"] + msg = debian_ip._error_msg_network("fnord", values) + self.assertTrue(msg.endswith("[1|True|False|no-kaboom]"), msg) def test_build_bond_exception(self): - ''' + """ Test if it create a bond script in /etc/modprobe.d with the passed settings and load the bonding kernel module. - ''' - with patch('salt.modules.debian_ip._parse_settings_bond', - MagicMock(return_value={})): + """ + with patch( + "salt.modules.debian_ip._parse_settings_bond", MagicMock(return_value={}) + ): mock = MagicMock(return_value=1) - with patch.dict(debian_ip.__grains__, {'osrelease': mock}): - mock = MagicMock(side_effect= - jinja2.exceptions.TemplateNotFound('error')) - with patch.object(jinja2.Environment, 'get_template', mock): - self.assertEqual(debian_ip.build_bond('bond0'), '') + with patch.dict(debian_ip.__grains__, {"osrelease": mock}): + mock = MagicMock( + side_effect=jinja2.exceptions.TemplateNotFound("error") + ) + with patch.object(jinja2.Environment, "get_template", mock): + self.assertEqual(debian_ip.build_bond("bond0"), "") def test_build_bond_data(self): - ''' + """ Test if it create a bond script in /etc/modprobe.d with the passed settings and load the bonding kernel module. - ''' - with patch('salt.modules.debian_ip._parse_settings_bond', - MagicMock(return_value={})), \ - patch('salt.modules.debian_ip._read_temp', - MagicMock(return_value=True)): + """ + with patch( + "salt.modules.debian_ip._parse_settings_bond", MagicMock(return_value={}) + ), patch("salt.modules.debian_ip._read_temp", MagicMock(return_value=True)): mock = MagicMock(return_value=1) - with patch.dict(debian_ip.__grains__, {'osrelease': mock}): - self.assertTrue(debian_ip.build_bond('bond0', test='True')) + with patch.dict(debian_ip.__grains__, {"osrelease": mock}): + self.assertTrue(debian_ip.build_bond("bond0", test="True")) # 'build_routes' function tests: 2 def test_build_routes(self): - ''' + """ Test if it add route scripts for a network interface using up commands. - ''' - with patch('salt.modules.debian_ip._parse_routes', - MagicMock(return_value={'routes': []})), \ - patch('salt.modules.debian_ip._write_file_routes', - MagicMock(return_value=True)), \ - patch('salt.modules.debian_ip._read_file', - MagicMock(return_value='salt')): - self.assertEqual(debian_ip.build_routes('eth0'), 'saltsalt') + """ + with patch( + "salt.modules.debian_ip._parse_routes", + MagicMock(return_value={"routes": []}), + ), patch( + "salt.modules.debian_ip._write_file_routes", MagicMock(return_value=True) + ), patch( + "salt.modules.debian_ip._read_file", MagicMock(return_value="salt") + ): + self.assertEqual(debian_ip.build_routes("eth0"), "saltsalt") def test_build_routes_exception(self): - ''' + """ Test if it add route scripts for a network interface using up commands. - ''' - with patch('salt.modules.debian_ip._parse_routes', - MagicMock(return_value={'routes': []})): - self.assertTrue(debian_ip.build_routes('eth0', test='True')) + """ + with patch( + "salt.modules.debian_ip._parse_routes", + MagicMock(return_value={"routes": []}), + ): + self.assertTrue(debian_ip.build_routes("eth0", test="True")) - mock = MagicMock(side_effect=jinja2.exceptions.TemplateNotFound('err')) - with patch.object(jinja2.Environment, 'get_template', mock): - self.assertEqual(debian_ip.build_routes('eth0'), '') + mock = MagicMock(side_effect=jinja2.exceptions.TemplateNotFound("err")) + with patch.object(jinja2.Environment, "get_template", mock): + self.assertEqual(debian_ip.build_routes("eth0"), "") # 'down' function tests: 1 def test_down(self): - ''' + """ Test if it shutdown a network interface - ''' - self.assertEqual(debian_ip.down('eth0', 'slave'), None) + """ + self.assertEqual(debian_ip.down("eth0", "slave"), None) - mock = MagicMock(return_value='Salt') - with patch.dict(debian_ip.__salt__, {'cmd.run': mock}): - self.assertEqual(debian_ip.down('eth0', 'eth'), 'Salt') + mock = MagicMock(return_value="Salt") + with patch.dict(debian_ip.__salt__, {"cmd.run": mock}): + self.assertEqual(debian_ip.down("eth0", "eth"), "Salt") # 'get_bond' function tests: 1 def test_get_bond(self): - ''' + """ Test if it return the content of a bond script - ''' - self.assertEqual(debian_ip.get_bond('bond0'), '') + """ + self.assertEqual(debian_ip.get_bond("bond0"), "") # 'get_interface' function tests: 1 def test_get_interface(self): - ''' + """ Test if it return the contents of an interface script - ''' - with patch.object(debian_ip, '_parse_interfaces', - MagicMock(return_value={})): - self.assertListEqual(debian_ip.get_interface('eth0'), []) + """ + with patch.object(debian_ip, "_parse_interfaces", MagicMock(return_value={})): + self.assertListEqual(debian_ip.get_interface("eth0"), []) - mock_ret = {'lo': {'enabled': True, 'data': - {'inet': {'addrfam': 'inet', 'proto': 'loopback'}}}} - with patch.object(debian_ip, '_parse_interfaces', - MagicMock(return_value=mock_ret)): - self.assertListEqual(debian_ip.get_interface('lo'), - ['auto lo\n', - 'iface lo inet loopback\n', - '\n']) + mock_ret = { + "lo": { + "enabled": True, + "data": {"inet": {"addrfam": "inet", "proto": "loopback"}}, + } + } + with patch.object( + debian_ip, "_parse_interfaces", MagicMock(return_value=mock_ret) + ): + self.assertListEqual( + debian_ip.get_interface("lo"), + ["auto lo\n", "iface lo inet loopback\n", "\n"], + ) - mock = MagicMock(side_effect=jinja2.exceptions.TemplateNotFound - ('error')) - with patch.object(jinja2.Environment, 'get_template', mock): - self.assertEqual(debian_ip.get_interface('lo'), '') + mock = MagicMock(side_effect=jinja2.exceptions.TemplateNotFound("error")) + with patch.object(jinja2.Environment, "get_template", mock): + self.assertEqual(debian_ip.get_interface("lo"), "") # 'build_interface' function tests: 1 def test_build_interface(self): - ''' + """ Test if it builds an interface script for a network interface. - ''' - with patch('salt.modules.debian_ip._write_file_ifaces', - MagicMock(return_value='salt')): - self.assertEqual(debian_ip.build_interface('eth0', 'eth', 'enabled'), - ['s\n', 'a\n', 'l\n', 't\n']) + """ + with patch( + "salt.modules.debian_ip._write_file_ifaces", MagicMock(return_value="salt") + ): + self.assertEqual( + debian_ip.build_interface("eth0", "eth", "enabled"), + ["s\n", "a\n", "l\n", "t\n"], + ) - self.assertTrue(debian_ip.build_interface('eth0', 'eth', 'enabled', - test='True')) + self.assertTrue( + debian_ip.build_interface("eth0", "eth", "enabled", test="True") + ) - with patch.object(debian_ip, '_parse_settings_eth', - MagicMock(return_value={'routes': []})): - self.assertRaises(AttributeError, debian_ip.build_interface, - 'eth0', 'bridge', 'enabled') + with patch.object( + debian_ip, "_parse_settings_eth", MagicMock(return_value={"routes": []}) + ): + self.assertRaises( + AttributeError, + debian_ip.build_interface, + "eth0", + "bridge", + "enabled", + ) - self.assertRaises(AttributeError, debian_ip.build_interface, - 'eth0', 'slave', 'enabled') + self.assertRaises( + AttributeError, + debian_ip.build_interface, + "eth0", + "slave", + "enabled", + ) - self.assertRaises(AttributeError, debian_ip.build_interface, - 'eth0', 'bond', 'enabled') + self.assertRaises( + AttributeError, debian_ip.build_interface, "eth0", "bond", "enabled" + ) - self.assertTrue(debian_ip.build_interface('eth0', 'eth', 'enabled', - test='True')) + self.assertTrue( + debian_ip.build_interface("eth0", "eth", "enabled", test="True") + ) interfaces = [ - # IPv4-only interface; single address - {'iface_name': 'eth1', 'iface_type': 'eth', 'enabled': True, - 'settings': { - 'proto': 'static', - 'ipaddr': '192.168.4.9', - 'netmask': '255.255.255.0', - 'gateway': '192.168.4.1', - 'enable_ipv6': False, - 'noifupdown': True, - }, - 'return': [ - 'auto eth1\n', - 'iface eth1 inet static\n', - ' address 192.168.4.9\n', - ' netmask 255.255.255.0\n', - ' gateway 192.168.4.1\n', - '\n']}, - # IPv6-only; single address - {'iface_name': 'eth2', 'iface_type': 'eth', 'enabled': True, - 'settings': { - 'proto': 'static', - 'ipv6proto': 'static', - 'ipv6ipaddr': '2001:db8:dead:beef::3', - 'ipv6netmask': '64', - 'ipv6gateway': '2001:db8:dead:beef::1', - 'enable_ipv6': True, - 'noifupdown': True, - }, - 'return': [ - 'auto eth2\n', - 'iface eth2 inet6 static\n', - ' address 2001:db8:dead:beef::3\n', - ' netmask 64\n', - ' gateway 2001:db8:dead:beef::1\n', - '\n']}, - # IPv4 and IPv6; shared/overridden settings - {'iface_name': 'eth3', 'iface_type': 'eth', 'enabled': True, - 'settings': { - 'proto': 'static', - 'ipaddr': '192.168.4.9', - 'netmask': '255.255.255.0', - 'gateway': '192.168.4.1', - 'ipv6proto': 'static', - 'ipv6ipaddr': '2001:db8:dead:beef::3', - 'ipv6netmask': '64', - 'ipv6gateway': '2001:db8:dead:beef::1', - 'ttl': '18', # shared - 'ipv6ttl': '15', # overriden for v6 - 'mtu': '1480', # shared - 'enable_ipv6': True, - 'noifupdown': True, - }, - 'return': [ - 'auto eth3\n', - 'iface eth3 inet static\n', - ' address 192.168.4.9\n', - ' netmask 255.255.255.0\n', - ' gateway 192.168.4.1\n', - ' ttl 18\n', - ' mtu 1480\n', - 'iface eth3 inet6 static\n', - ' address 2001:db8:dead:beef::3\n', - ' netmask 64\n', - ' gateway 2001:db8:dead:beef::1\n', - ' ttl 15\n', - ' mtu 1480\n', - '\n']}, - # Slave iface - {'iface_name': 'eth4', 'iface_type': 'slave', 'enabled': True, - 'settings': { - 'master': 'bond0', - 'noifupdown': True, - }, - 'return': [ - 'auto eth4\n', - 'iface eth4 inet manual\n', - ' bond-master bond0\n', - '\n']}, - # Bond; with address IPv4 and IPv6 address; slaves as string - {'iface_name': 'bond5', 'iface_type': 'bond', 'enabled': True, - 'settings': { - 'proto': 'static', - 'ipaddr': '10.1.0.14', - 'netmask': '255.255.255.0', - 'gateway': '10.1.0.1', - 'ipv6proto': 'static', - 'ipv6ipaddr': '2001:db8:dead:c0::3', - 'ipv6netmask': '64', - 'ipv6gateway': '2001:db8:dead:c0::1', - 'mode': '802.3ad', - 'slaves': 'eth4 eth5', - 'enable_ipv6': True, - 'noifupdown': True, - }, - 'return': [ - 'auto bond5\n', - 'iface bond5 inet static\n', - ' address 10.1.0.14\n', - ' netmask 255.255.255.0\n', - ' gateway 10.1.0.1\n', - ' bond-ad_select 0\n', - ' bond-downdelay 200\n', - ' bond-lacp_rate 0\n', - ' bond-miimon 100\n', - ' bond-mode 4\n', - ' bond-slaves eth4 eth5\n', - ' bond-updelay 0\n', - ' bond-use_carrier on\n', - 'iface bond5 inet6 static\n', - ' address 2001:db8:dead:c0::3\n', - ' netmask 64\n', - ' gateway 2001:db8:dead:c0::1\n', - # TODO: I suspect there should be more here. - '\n']}, - # Bond; with address IPv4 and IPv6 address; slaves as list - {'iface_name': 'bond6', 'iface_type': 'bond', 'enabled': True, - 'settings': { - 'proto': 'static', - 'ipaddr': '10.1.0.14', - 'netmask': '255.255.255.0', - 'gateway': '10.1.0.1', - 'ipv6proto': 'static', - 'ipv6ipaddr': '2001:db8:dead:c0::3', - 'ipv6netmask': '64', - 'ipv6gateway': '2001:db8:dead:c0::1', - 'mode': '802.3ad', - # TODO: Need to add this support - #'slaves': ['eth4', 'eth5'], - 'slaves': 'eth4 eth5', - 'enable_ipv6': True, - 'noifupdown': True, - }, - 'return': [ - 'auto bond6\n', - 'iface bond6 inet static\n', - ' address 10.1.0.14\n', - ' netmask 255.255.255.0\n', - ' gateway 10.1.0.1\n', - ' bond-ad_select 0\n', - ' bond-downdelay 200\n', - ' bond-lacp_rate 0\n', - ' bond-miimon 100\n', - ' bond-mode 4\n', - ' bond-slaves eth4 eth5\n', - ' bond-updelay 0\n', - ' bond-use_carrier on\n', - 'iface bond6 inet6 static\n', - ' address 2001:db8:dead:c0::3\n', - ' netmask 64\n', - ' gateway 2001:db8:dead:c0::1\n', - # TODO: I suspect there should be more here. - '\n']}, - # Bond VLAN; with IPv4 address - {'iface_name': 'bond1.7', 'iface_type': 'vlan', 'enabled': True, - 'settings': { - 'proto': 'static', - 'ipaddr': '10.7.0.8', - 'netmask': '255.255.255.0', - 'gateway': '10.7.0.1', - 'slaves': 'eth6 eth7', - 'mode': '802.3ad', - 'enable_ipv6': False, - 'noifupdown': True, - }, - 'return': [ - 'auto bond1.7\n', - 'iface bond1.7 inet static\n', - ' vlan-raw-device bond1\n', - ' address 10.7.0.8\n', - ' netmask 255.255.255.0\n', - ' gateway 10.7.0.1\n', - ' mode 802.3ad\n', - '\n']}, - # Bond; without address - {'iface_name': 'bond1.8', 'iface_type': 'vlan', 'enabled': True, - 'settings': { - 'proto': 'static', - 'slaves': 'eth6 eth7', - 'mode': '802.3ad', - 'enable_ipv6': False, - 'noifupdown': True, - }, - 'return': [ - 'auto bond1.8\n', - 'iface bond1.8 inet static\n', - ' vlan-raw-device bond1\n', - ' mode 802.3ad\n', - '\n']}, - # DNS NS as list - {'iface_name': 'eth9', 'iface_type': 'eth', 'enabled': True, - 'settings': { - 'proto': 'static', - 'ipaddr': '192.168.4.9', - 'netmask': '255.255.255.0', - 'gateway': '192.168.4.1', - 'enable_ipv6': False, - 'noifupdown': True, - 'dns': ['8.8.8.8', '8.8.4.4'], - }, - 'return': [ - 'auto eth9\n', - 'iface eth9 inet static\n', - ' address 192.168.4.9\n', - ' netmask 255.255.255.0\n', - ' gateway 192.168.4.1\n', - ' dns-nameservers 8.8.8.8 8.8.4.4\n', - '\n']}, - # DNS NS as string - {'iface_name': 'eth10', 'iface_type': 'eth', 'enabled': True, - 'settings': { - 'proto': 'static', - 'ipaddr': '192.168.4.9', - 'netmask': '255.255.255.0', - 'gateway': '192.168.4.1', - 'enable_ipv6': False, - 'noifupdown': True, - 'dns': '8.8.8.8 8.8.4.4', - }, - 'return': [ - 'auto eth10\n', - 'iface eth10 inet static\n', - ' address 192.168.4.9\n', - ' netmask 255.255.255.0\n', - ' gateway 192.168.4.1\n', - ' dns-nameservers 8.8.8.8 8.8.4.4\n', - '\n']}, - # Loopback; with IPv4 and IPv6 address - {'iface_name': 'lo11', 'iface_type': 'eth', 'enabled': True, - 'settings': { - 'proto': 'loopback', - 'ipaddr': '192.168.4.9', - 'netmask': '255.255.255.0', - 'gateway': '192.168.4.1', - 'ipv6ipaddr': 'fc00::1', - 'ipv6netmask': '128', - 'ipv6_autoconf': False, - 'enable_ipv6': True, - 'noifupdown': True, - }, - 'return': [ - 'auto lo11\n', - 'iface lo11 inet loopback\n', - ' address 192.168.4.9\n', - ' netmask 255.255.255.0\n', - ' gateway 192.168.4.1\n', - 'iface lo11 inet6 loopback\n', - ' address fc00::1\n', - ' netmask 128\n', - '\n']}, - # Loopback; without address - {'iface_name': 'lo12', 'iface_type': 'eth', 'enabled': True, - 'settings': { - 'proto': 'loopback', - 'enable_ipv6': False, - 'noifupdown': True, - }, - 'return': [ - 'auto lo12\n', - 'iface lo12 inet loopback\n', - '\n']}, - ] + # IPv4-only interface; single address + { + "iface_name": "eth1", + "iface_type": "eth", + "enabled": True, + "settings": { + "proto": "static", + "ipaddr": "192.168.4.9", + "netmask": "255.255.255.0", + "gateway": "192.168.4.1", + "enable_ipv6": False, + "noifupdown": True, + }, + "return": [ + "auto eth1\n", + "iface eth1 inet static\n", + " address 192.168.4.9\n", + " netmask 255.255.255.0\n", + " gateway 192.168.4.1\n", + "\n", + ], + }, + # IPv6-only; single address + { + "iface_name": "eth2", + "iface_type": "eth", + "enabled": True, + "settings": { + "proto": "static", + "ipv6proto": "static", + "ipv6ipaddr": "2001:db8:dead:beef::3", + "ipv6netmask": "64", + "ipv6gateway": "2001:db8:dead:beef::1", + "enable_ipv6": True, + "noifupdown": True, + }, + "return": [ + "auto eth2\n", + "iface eth2 inet6 static\n", + " address 2001:db8:dead:beef::3\n", + " netmask 64\n", + " gateway 2001:db8:dead:beef::1\n", + "\n", + ], + }, + # IPv4 and IPv6; shared/overridden settings + { + "iface_name": "eth3", + "iface_type": "eth", + "enabled": True, + "settings": { + "proto": "static", + "ipaddr": "192.168.4.9", + "netmask": "255.255.255.0", + "gateway": "192.168.4.1", + "ipv6proto": "static", + "ipv6ipaddr": "2001:db8:dead:beef::3", + "ipv6netmask": "64", + "ipv6gateway": "2001:db8:dead:beef::1", + "ttl": "18", # shared + "ipv6ttl": "15", # overriden for v6 + "mtu": "1480", # shared + "enable_ipv6": True, + "noifupdown": True, + }, + "return": [ + "auto eth3\n", + "iface eth3 inet static\n", + " address 192.168.4.9\n", + " netmask 255.255.255.0\n", + " gateway 192.168.4.1\n", + " ttl 18\n", + " mtu 1480\n", + "iface eth3 inet6 static\n", + " address 2001:db8:dead:beef::3\n", + " netmask 64\n", + " gateway 2001:db8:dead:beef::1\n", + " ttl 15\n", + " mtu 1480\n", + "\n", + ], + }, + # Slave iface + { + "iface_name": "eth4", + "iface_type": "slave", + "enabled": True, + "settings": {"master": "bond0", "noifupdown": True}, + "return": [ + "auto eth4\n", + "iface eth4 inet manual\n", + " bond-master bond0\n", + "\n", + ], + }, + # Bond; with address IPv4 and IPv6 address; slaves as string + { + "iface_name": "bond5", + "iface_type": "bond", + "enabled": True, + "settings": { + "proto": "static", + "ipaddr": "10.1.0.14", + "netmask": "255.255.255.0", + "gateway": "10.1.0.1", + "ipv6proto": "static", + "ipv6ipaddr": "2001:db8:dead:c0::3", + "ipv6netmask": "64", + "ipv6gateway": "2001:db8:dead:c0::1", + "mode": "802.3ad", + "slaves": "eth4 eth5", + "enable_ipv6": True, + "noifupdown": True, + }, + "return": [ + "auto bond5\n", + "iface bond5 inet static\n", + " address 10.1.0.14\n", + " netmask 255.255.255.0\n", + " gateway 10.1.0.1\n", + " bond-ad_select 0\n", + " bond-downdelay 200\n", + " bond-lacp_rate 0\n", + " bond-miimon 100\n", + " bond-mode 4\n", + " bond-slaves eth4 eth5\n", + " bond-updelay 0\n", + " bond-use_carrier on\n", + "iface bond5 inet6 static\n", + " address 2001:db8:dead:c0::3\n", + " netmask 64\n", + " gateway 2001:db8:dead:c0::1\n", + # TODO: I suspect there should be more here. + "\n", + ], + }, + # Bond; with address IPv4 and IPv6 address; slaves as list + { + "iface_name": "bond6", + "iface_type": "bond", + "enabled": True, + "settings": { + "proto": "static", + "ipaddr": "10.1.0.14", + "netmask": "255.255.255.0", + "gateway": "10.1.0.1", + "ipv6proto": "static", + "ipv6ipaddr": "2001:db8:dead:c0::3", + "ipv6netmask": "64", + "ipv6gateway": "2001:db8:dead:c0::1", + "mode": "802.3ad", + # TODO: Need to add this support + #'slaves': ['eth4', 'eth5'], + "slaves": "eth4 eth5", + "enable_ipv6": True, + "noifupdown": True, + }, + "return": [ + "auto bond6\n", + "iface bond6 inet static\n", + " address 10.1.0.14\n", + " netmask 255.255.255.0\n", + " gateway 10.1.0.1\n", + " bond-ad_select 0\n", + " bond-downdelay 200\n", + " bond-lacp_rate 0\n", + " bond-miimon 100\n", + " bond-mode 4\n", + " bond-slaves eth4 eth5\n", + " bond-updelay 0\n", + " bond-use_carrier on\n", + "iface bond6 inet6 static\n", + " address 2001:db8:dead:c0::3\n", + " netmask 64\n", + " gateway 2001:db8:dead:c0::1\n", + # TODO: I suspect there should be more here. + "\n", + ], + }, + # Bond VLAN; with IPv4 address + { + "iface_name": "bond1.7", + "iface_type": "vlan", + "enabled": True, + "settings": { + "proto": "static", + "ipaddr": "10.7.0.8", + "netmask": "255.255.255.0", + "gateway": "10.7.0.1", + "slaves": "eth6 eth7", + "mode": "802.3ad", + "enable_ipv6": False, + "noifupdown": True, + }, + "return": [ + "auto bond1.7\n", + "iface bond1.7 inet static\n", + " vlan-raw-device bond1\n", + " address 10.7.0.8\n", + " netmask 255.255.255.0\n", + " gateway 10.7.0.1\n", + " mode 802.3ad\n", + "\n", + ], + }, + # Bond; without address + { + "iface_name": "bond1.8", + "iface_type": "vlan", + "enabled": True, + "settings": { + "proto": "static", + "slaves": "eth6 eth7", + "mode": "802.3ad", + "enable_ipv6": False, + "noifupdown": True, + }, + "return": [ + "auto bond1.8\n", + "iface bond1.8 inet static\n", + " vlan-raw-device bond1\n", + " mode 802.3ad\n", + "\n", + ], + }, + # DNS NS as list + { + "iface_name": "eth9", + "iface_type": "eth", + "enabled": True, + "settings": { + "proto": "static", + "ipaddr": "192.168.4.9", + "netmask": "255.255.255.0", + "gateway": "192.168.4.1", + "enable_ipv6": False, + "noifupdown": True, + "dns": ["8.8.8.8", "8.8.4.4"], + }, + "return": [ + "auto eth9\n", + "iface eth9 inet static\n", + " address 192.168.4.9\n", + " netmask 255.255.255.0\n", + " gateway 192.168.4.1\n", + " dns-nameservers 8.8.8.8 8.8.4.4\n", + "\n", + ], + }, + # DNS NS as string + { + "iface_name": "eth10", + "iface_type": "eth", + "enabled": True, + "settings": { + "proto": "static", + "ipaddr": "192.168.4.9", + "netmask": "255.255.255.0", + "gateway": "192.168.4.1", + "enable_ipv6": False, + "noifupdown": True, + "dns": "8.8.8.8 8.8.4.4", + }, + "return": [ + "auto eth10\n", + "iface eth10 inet static\n", + " address 192.168.4.9\n", + " netmask 255.255.255.0\n", + " gateway 192.168.4.1\n", + " dns-nameservers 8.8.8.8 8.8.4.4\n", + "\n", + ], + }, + # Loopback; with IPv4 and IPv6 address + { + "iface_name": "lo11", + "iface_type": "eth", + "enabled": True, + "settings": { + "proto": "loopback", + "ipaddr": "192.168.4.9", + "netmask": "255.255.255.0", + "gateway": "192.168.4.1", + "ipv6ipaddr": "fc00::1", + "ipv6netmask": "128", + "ipv6_autoconf": False, + "enable_ipv6": True, + "noifupdown": True, + }, + "return": [ + "auto lo11\n", + "iface lo11 inet loopback\n", + " address 192.168.4.9\n", + " netmask 255.255.255.0\n", + " gateway 192.168.4.1\n", + "iface lo11 inet6 loopback\n", + " address fc00::1\n", + " netmask 128\n", + "\n", + ], + }, + # Loopback; without address + { + "iface_name": "lo12", + "iface_type": "eth", + "enabled": True, + "settings": { + "proto": "loopback", + "enable_ipv6": False, + "noifupdown": True, + }, + "return": ["auto lo12\n", "iface lo12 inet loopback\n", "\n"], + }, + ] - with tempfile.NamedTemporaryFile(mode='r', delete=True) as tfile: - with patch('salt.modules.debian_ip._DEB_NETWORK_FILE', str(tfile.name)): + with tempfile.NamedTemporaryFile(mode="r", delete=True) as tfile: + with patch("salt.modules.debian_ip._DEB_NETWORK_FILE", str(tfile.name)): for iface in interfaces: # Skip tests that require __salt__['pkg.install']() - if iface['iface_type'] not in ['bridge', 'pppoe', 'vlan']: - self.assertListEqual(debian_ip.build_interface( - iface=iface['iface_name'], - iface_type=iface['iface_type'], - enabled=iface['enabled'], - interface_file=tfile.name, - **iface['settings']), - iface['return']) + if iface["iface_type"] not in ["bridge", "pppoe", "vlan"]: + self.assertListEqual( + debian_ip.build_interface( + iface=iface["iface_name"], + iface_type=iface["iface_type"], + enabled=iface["enabled"], + interface_file=tfile.name, + **iface["settings"] + ), + iface["return"], + ) # 'up' function tests: 1 def test_up(self): - ''' + """ Test if it start up a network interface - ''' - self.assertEqual(debian_ip.down('eth0', 'slave'), None) + """ + self.assertEqual(debian_ip.down("eth0", "slave"), None) - mock = MagicMock(return_value='Salt') - with patch.dict(debian_ip.__salt__, {'cmd.run': mock}): - self.assertEqual(debian_ip.up('eth0', 'eth'), 'Salt') + mock = MagicMock(return_value="Salt") + with patch.dict(debian_ip.__salt__, {"cmd.run": mock}): + self.assertEqual(debian_ip.up("eth0", "eth"), "Salt") # 'get_network_settings' function tests: 1 def test_get_network_settings(self): - ''' + """ Test if it return the contents of the global network script. - ''' - with patch.dict(debian_ip.__grains__, {'osfullname': 'Ubuntu', - 'osrelease': '14'}), \ - patch('salt.modules.debian_ip._parse_hostname', - MagicMock(return_value='SaltStack')), \ - patch('salt.modules.debian_ip._parse_domainname', - MagicMock(return_value='saltstack.com')), \ - patch('salt.modules.debian_ip._parse_searchdomain', - MagicMock(return_value='test.saltstack.com')): + """ + with patch.dict( + debian_ip.__grains__, {"osfullname": "Ubuntu", "osrelease": "14"} + ), patch( + "salt.modules.debian_ip._parse_hostname", + MagicMock(return_value="SaltStack"), + ), patch( + "salt.modules.debian_ip._parse_domainname", + MagicMock(return_value="saltstack.com"), + ), patch( + "salt.modules.debian_ip._parse_searchdomain", + MagicMock(return_value="test.saltstack.com"), + ): mock_avai = MagicMock(return_value=True) - with patch.dict(debian_ip.__salt__, {'service.available': mock_avai, - 'service.status': mock_avai}): - self.assertEqual(debian_ip.get_network_settings(), - [u'NETWORKING=yes\n', - u'HOSTNAME=SaltStack\n', - u'DOMAIN=saltstack.com\n', - u'SEARCH=test.saltstack.com\n']) + with patch.dict( + debian_ip.__salt__, + {"service.available": mock_avai, "service.status": mock_avai}, + ): + self.assertEqual( + debian_ip.get_network_settings(), + [ + "NETWORKING=yes\n", + "HOSTNAME=SaltStack\n", + "DOMAIN=saltstack.com\n", + "SEARCH=test.saltstack.com\n", + ], + ) - mock = MagicMock(side_effect=jinja2.exceptions.TemplateNotFound - ('error')) - with patch.object(jinja2.Environment, 'get_template', mock): - self.assertEqual(debian_ip.get_network_settings(), '') + mock = MagicMock( + side_effect=jinja2.exceptions.TemplateNotFound("error") + ) + with patch.object(jinja2.Environment, "get_template", mock): + self.assertEqual(debian_ip.get_network_settings(), "") # 'get_routes' function tests: 1 def test_get_routes(self): - ''' + """ Test if it return the routes for the interface - ''' - with patch('salt.modules.debian_ip._read_file', MagicMock(return_value='salt')): - self.assertEqual(debian_ip.get_routes('eth0'), 'saltsalt') + """ + with patch("salt.modules.debian_ip._read_file", MagicMock(return_value="salt")): + self.assertEqual(debian_ip.get_routes("eth0"), "saltsalt") # 'apply_network_settings' function tests: 1 def test_apply_network_settings(self): - ''' + """ Test if it apply global network configuration. - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(debian_ip.__salt__, {'network.mod_hostname': mock, - 'service.stop': mock, - 'service.start': mock}): + with patch.dict( + debian_ip.__salt__, + {"network.mod_hostname": mock, "service.stop": mock, "service.start": mock}, + ): self.assertEqual(debian_ip.apply_network_settings(), True) # 'build_network_settings' function tests: 1 def test_build_network_settings(self): - ''' + """ Test if it build the global network script. - ''' - with patch('salt.modules.debian_ip._parse_network_settings', - MagicMock(return_value={'networking': 'yes', - 'hostname': 'Salt.saltstack.com', - 'domainname': 'saltstack.com', - 'search': 'test.saltstack.com'})), \ - patch('salt.modules.debian_ip._write_file_network', - MagicMock(return_value=True)): - with patch.dict(debian_ip.__grains__, {'osfullname': 'Ubuntu', - 'osrelease': '14'}): + """ + with patch( + "salt.modules.debian_ip._parse_network_settings", + MagicMock( + return_value={ + "networking": "yes", + "hostname": "Salt.saltstack.com", + "domainname": "saltstack.com", + "search": "test.saltstack.com", + } + ), + ), patch( + "salt.modules.debian_ip._write_file_network", MagicMock(return_value=True) + ): + with patch.dict( + debian_ip.__grains__, {"osfullname": "Ubuntu", "osrelease": "14"} + ): mock = MagicMock(return_value=True) - with patch.dict(debian_ip.__salt__, {'service.available': mock, - 'service.disable': mock, - 'service.enable': mock}): - self.assertEqual(debian_ip.build_network_settings(), - ['NETWORKING=yes\n', - 'HOSTNAME=Salt\n', - 'DOMAIN=saltstack.com\n', - 'SEARCH=test.saltstack.com\n']) + with patch.dict( + debian_ip.__salt__, + { + "service.available": mock, + "service.disable": mock, + "service.enable": mock, + }, + ): + self.assertEqual( + debian_ip.build_network_settings(), + [ + "NETWORKING=yes\n", + "HOSTNAME=Salt\n", + "DOMAIN=saltstack.com\n", + "SEARCH=test.saltstack.com\n", + ], + ) - mock = MagicMock(side_effect=jinja2.exceptions.TemplateNotFound - ('error')) - with patch.object(jinja2.Environment, 'get_template', mock): - self.assertEqual(debian_ip.build_network_settings(), '') + mock = MagicMock( + side_effect=jinja2.exceptions.TemplateNotFound("error") + ) + with patch.object(jinja2.Environment, "get_template", mock): + self.assertEqual(debian_ip.build_network_settings(), "") - with patch.dict(debian_ip.__grains__, {'osfullname': 'Ubuntu', - 'osrelease': '10'}): + with patch.dict( + debian_ip.__grains__, {"osfullname": "Ubuntu", "osrelease": "10"} + ): mock = MagicMock(return_value=True) - with patch.dict(debian_ip.__salt__, {'service.available': mock, - 'service.disable': mock, - 'service.enable': mock}): - mock = MagicMock(side_effect=jinja2.exceptions.TemplateNotFound - ('error')) - with patch.object(jinja2.Environment, 'get_template', mock): - self.assertEqual(debian_ip.build_network_settings(), '') + with patch.dict( + debian_ip.__salt__, + { + "service.available": mock, + "service.disable": mock, + "service.enable": mock, + }, + ): + mock = MagicMock( + side_effect=jinja2.exceptions.TemplateNotFound("error") + ) + with patch.object(jinja2.Environment, "get_template", mock): + self.assertEqual(debian_ip.build_network_settings(), "") - with patch.object(debian_ip, '_read_temp', - MagicMock(return_value=True)): - self.assertTrue(debian_ip.build_network_settings - (test='True')) + with patch.object( + debian_ip, "_read_temp", MagicMock(return_value=True) + ): + self.assertTrue(debian_ip.build_network_settings(test="True")) diff --git a/tests/unit/modules/test_debian_service.py b/tests/unit/modules/test_debian_service.py index 3ef7a3d35c2..b954e2b9355 100644 --- a/tests/unit/modules/test_debian_service.py +++ b/tests/unit/modules/test_debian_service.py @@ -1,171 +1,168 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.debian_service as debian_service +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DebianServicesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.debian_service - ''' + """ + def setup_loader_modules(self): return {debian_service: {}} def test_get_enabled(self): - ''' + """ Test for Return a list of service that are enabled on boot - ''' + """ mock_runlevel = MagicMock(return_value=1) - mock_prefix = '/etc/rc1.d/S' - mock_glob = MagicMock(return_value=[mock_prefix + '01name']) + mock_prefix = "/etc/rc1.d/S" + mock_glob = MagicMock(return_value=[mock_prefix + "01name"]) - with patch.object(debian_service, '_get_runlevel', mock_runlevel): - with patch.object(debian_service.glob, 'glob', mock_glob): - self.assertEqual(debian_service.get_enabled()[0], 'name') + with patch.object(debian_service, "_get_runlevel", mock_runlevel): + with patch.object(debian_service.glob, "glob", mock_glob): + self.assertEqual(debian_service.get_enabled()[0], "name") def test_get_disabled(self): - ''' + """ Test for Return a set of services that are installed but disabled - ''' - mock = MagicMock(return_value=['A']) - with patch.object(debian_service, 'get_all', mock): - mock = MagicMock(return_value=['B']) - with patch.object(debian_service, 'get_enabled', mock): - self.assertEqual(debian_service.get_disabled(), ['A']) + """ + mock = MagicMock(return_value=["A"]) + with patch.object(debian_service, "get_all", mock): + mock = MagicMock(return_value=["B"]) + with patch.object(debian_service, "get_enabled", mock): + self.assertEqual(debian_service.get_disabled(), ["A"]) def test_available(self): - ''' + """ Test for Returns ``True`` if the specified service is available, otherwise returns ``False``. - ''' - mock = MagicMock(return_value=['A']) - with patch.object(debian_service, 'get_all', mock): - self.assertFalse(debian_service.available('name')) + """ + mock = MagicMock(return_value=["A"]) + with patch.object(debian_service, "get_all", mock): + self.assertFalse(debian_service.available("name")) def test_missing(self): - ''' + """ Test for The inverse of service.available. - ''' - mock = MagicMock(return_value=['A']) - with patch.object(debian_service, 'get_all', mock): - self.assertTrue(debian_service.missing('name')) + """ + mock = MagicMock(return_value=["A"]) + with patch.object(debian_service, "get_all", mock): + self.assertTrue(debian_service.missing("name")) def test_getall(self): - ''' + """ Test for Return all available boot services - ''' - mock = MagicMock(return_value=('A')) - with patch.object(debian_service, 'get_enabled', mock): - self.assertEqual(debian_service.get_all()[0], 'A') + """ + mock = MagicMock(return_value=("A")) + with patch.object(debian_service, "get_enabled", mock): + self.assertEqual(debian_service.get_all()[0], "A") def test_start(self): - ''' + """ Test for Start the specified service - ''' + """ mock = MagicMock(return_value=True) - with patch.object(debian_service, '_service_cmd', mock): - with patch.dict(debian_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(debian_service.start('name')) + with patch.object(debian_service, "_service_cmd", mock): + with patch.dict(debian_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(debian_service.start("name")) def test_stop(self): - ''' + """ Test for Stop the specified service - ''' + """ mock = MagicMock(return_value=True) - with patch.object(debian_service, '_service_cmd', mock): - with patch.dict(debian_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(debian_service.stop('name')) + with patch.object(debian_service, "_service_cmd", mock): + with patch.dict(debian_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(debian_service.stop("name")) def test_restart(self): - ''' + """ Test for Restart the named service - ''' + """ mock = MagicMock(return_value=True) - with patch.object(debian_service, '_service_cmd', mock): - with patch.dict(debian_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(debian_service.restart('name')) + with patch.object(debian_service, "_service_cmd", mock): + with patch.dict(debian_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(debian_service.restart("name")) def test_reload_(self): - ''' + """ Test for Reload the named service - ''' + """ mock = MagicMock(return_value=True) - with patch.object(debian_service, '_service_cmd', mock): - with patch.dict(debian_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(debian_service.reload_('name')) + with patch.object(debian_service, "_service_cmd", mock): + with patch.dict(debian_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(debian_service.reload_("name")) def test_force_reload(self): - ''' + """ Test for Force-reload the named service - ''' + """ mock = MagicMock(return_value=True) - with patch.object(debian_service, '_service_cmd', mock): - with patch.dict(debian_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(debian_service.force_reload('name')) + with patch.object(debian_service, "_service_cmd", mock): + with patch.dict(debian_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(debian_service.force_reload("name")) def test_status(self): - ''' + """ Test for Return the status for a service - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(debian_service.__salt__, {'status.pid': mock}): - self.assertTrue(debian_service.status('name', 1)) + with patch.dict(debian_service.__salt__, {"status.pid": mock}): + self.assertTrue(debian_service.status("name", 1)) - mock = MagicMock(return_value='A') - with patch.object(debian_service, '_service_cmd', mock): + mock = MagicMock(return_value="A") + with patch.object(debian_service, "_service_cmd", mock): mock = MagicMock(return_value=True) - with patch.dict(debian_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(debian_service.status('name')) + with patch.dict(debian_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(debian_service.status("name")) def test_enable(self): - ''' + """ Test for Enable the named service to start at boot - ''' - mock = MagicMock(return_value='5') - with patch.object(debian_service, '_osrel', mock): - mock = MagicMock(return_value='') - with patch.object(debian_service, '_cmd_quote', mock): + """ + mock = MagicMock(return_value="5") + with patch.object(debian_service, "_osrel", mock): + mock = MagicMock(return_value="") + with patch.object(debian_service, "_cmd_quote", mock): mock = MagicMock(return_value=True) - with patch.dict(debian_service.__salt__, - {'cmd.retcode': mock}): - self.assertFalse(debian_service.enable('name')) + with patch.dict(debian_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(debian_service.enable("name")) def test_disable(self): - ''' + """ Test for Disable the named service to start at boot - ''' - mock = MagicMock(return_value='5') - with patch.object(debian_service, '_osrel', mock): + """ + mock = MagicMock(return_value="5") + with patch.object(debian_service, "_osrel", mock): mock = MagicMock(return_value=True) - with patch.dict(debian_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(debian_service.disable('name')) + with patch.dict(debian_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(debian_service.disable("name")) def test_enabled(self): - ''' + """ Test for Return True if the named service is enabled, false otherwise - ''' - mock = MagicMock(return_value=['A']) - with patch.object(debian_service, 'get_enabled', mock): - self.assertFalse(debian_service.enabled('name')) + """ + mock = MagicMock(return_value=["A"]) + with patch.object(debian_service, "get_enabled", mock): + self.assertFalse(debian_service.enabled("name")) def test_disabled(self): - ''' + """ Test for Return True if the named service is enabled, false otherwise - ''' - mock = MagicMock(return_value=['A']) - with patch.object(debian_service, 'get_enabled', mock): - self.assertFalse(debian_service.disabled('name')) + """ + mock = MagicMock(return_value=["A"]) + with patch.object(debian_service, "get_enabled", mock): + self.assertFalse(debian_service.disabled("name")) diff --git a/tests/unit/modules/test_defaults.py b/tests/unit/modules/test_defaults.py index 6a65435c525..ed64f017907 100644 --- a/tests/unit/modules/test_defaults.py +++ b/tests/unit/modules/test_defaults.py @@ -1,205 +1,145 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import inspect -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import inspect # Import Salt Libs import salt.modules.defaults as defaults +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DefaultsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.defaults - ''' + """ + def setup_loader_modules(self): return {defaults: {}} def test_get_mock(self): - ''' + """ Test if it execute a defaults client run and return a dict - ''' - with patch.object(inspect, 'stack', MagicMock(return_value=[])), \ - patch('salt.modules.defaults.get', - MagicMock(return_value={'users': {'root': [0]}})): - self.assertEqual(defaults.get('core:users:root'), - {'users': {'root': [0]}}) + """ + with patch.object(inspect, "stack", MagicMock(return_value=[])), patch( + "salt.modules.defaults.get", + MagicMock(return_value={"users": {"root": [0]}}), + ): + self.assertEqual(defaults.get("core:users:root"), {"users": {"root": [0]}}) def test_merge_with_list_merging(self): - ''' + """ Test deep merging of dicts with merge_lists enabled. - ''' + """ src_dict = { - 'string_key': 'string_val_src', - 'list_key': [ - 'list_val_src', - ], - 'dict_key': { - 'dict_key_src': 'dict_val_src', - } + "string_key": "string_val_src", + "list_key": ["list_val_src"], + "dict_key": {"dict_key_src": "dict_val_src"}, } dest_dict = { - 'string_key': 'string_val_dest', - 'list_key': [ - 'list_val_dest', - ], - 'dict_key': { - 'dict_key_dest': 'dict_val_dest', - } + "string_key": "string_val_dest", + "list_key": ["list_val_dest"], + "dict_key": {"dict_key_dest": "dict_val_dest"}, } merged_dict = { - 'string_key': 'string_val_src', - 'list_key': [ - 'list_val_dest', - 'list_val_src' - ], - 'dict_key': { - 'dict_key_dest': 'dict_val_dest', - 'dict_key_src': 'dict_val_src' - } + "string_key": "string_val_src", + "list_key": ["list_val_dest", "list_val_src"], + "dict_key": { + "dict_key_dest": "dict_val_dest", + "dict_key_src": "dict_val_src", + }, } defaults.merge(dest_dict, src_dict, merge_lists=True) self.assertEqual(dest_dict, merged_dict) def test_merge_without_list_merging(self): - ''' + """ Test deep merging of dicts with merge_lists disabled. - ''' + """ src = { - 'string_key': 'string_val_src', - 'list_key': [ - 'list_val_src', - ], - 'dict_key': { - 'dict_key_src': 'dict_val_src', - } + "string_key": "string_val_src", + "list_key": ["list_val_src"], + "dict_key": {"dict_key_src": "dict_val_src"}, } dest = { - 'string_key': 'string_val_dest', - 'list_key': [ - 'list_val_dest', - ], - 'dict_key': { - 'dict_key_dest': 'dict_val_dest', - } + "string_key": "string_val_dest", + "list_key": ["list_val_dest"], + "dict_key": {"dict_key_dest": "dict_val_dest"}, } merged = { - 'string_key': 'string_val_src', - 'list_key': [ - 'list_val_src' - ], - 'dict_key': { - 'dict_key_dest': 'dict_val_dest', - 'dict_key_src': 'dict_val_src' - } + "string_key": "string_val_src", + "list_key": ["list_val_src"], + "dict_key": { + "dict_key_dest": "dict_val_dest", + "dict_key_src": "dict_val_src", + }, } defaults.merge(dest, src, merge_lists=False) self.assertEqual(dest, merged) def test_merge_not_in_place(self): - ''' + """ Test deep merging of dicts not in place. - ''' + """ - src = { - 'nested_dict': { - 'A': 'A' - } - } + src = {"nested_dict": {"A": "A"}} - dest = { - 'nested_dict': { - 'B': 'B' - } - } + dest = {"nested_dict": {"B": "B"}} - dest_orig = { - 'nested_dict': { - 'B': 'B' - } - } + dest_orig = {"nested_dict": {"B": "B"}} - merged = { - 'nested_dict': { - 'A': 'A', - 'B': 'B' - } - } + merged = {"nested_dict": {"A": "A", "B": "B"}} final = defaults.merge(dest, src, in_place=False) self.assertEqual(dest, dest_orig) self.assertEqual(final, merged) def test_deepcopy(self): - ''' + """ Test a deep copy of object. - ''' + """ - src = { - 'A': 'A', - 'B': 'B' - } + src = {"A": "A", "B": "B"} dist = defaults.deepcopy(src) - dist.update({'C': 'C'}) + dist.update({"C": "C"}) - result = { - 'A': 'A', - 'B': 'B', - 'C': 'C' - } + result = {"A": "A", "B": "B", "C": "C"} self.assertFalse(src == dist) self.assertTrue(dist == result) def test_update_in_place(self): - ''' + """ Test update with defaults values in place. - ''' + """ group01 = { - 'defaults': { - 'enabled': True, - 'extra': [ - 'test', - 'stage' - ] - }, - 'nodes': { - 'host01': { - 'index': 'foo', - 'upstream': 'bar' - } - } + "defaults": {"enabled": True, "extra": ["test", "stage"]}, + "nodes": {"host01": {"index": "foo", "upstream": "bar"}}, } host01 = { - 'enabled': True, - 'index': 'foo', - 'upstream': 'bar', - 'extra': [ - 'test', - 'stage' - ], + "enabled": True, + "index": "foo", + "upstream": "bar", + "extra": ["test", "stage"], } - defaults.update(group01['nodes'], group01['defaults']) - self.assertEqual(group01['nodes']['host01'], host01) + defaults.update(group01["nodes"], group01["defaults"]) + self.assertEqual(group01["nodes"]["host01"], host01) diff --git a/tests/unit/modules/test_devmap.py b/tests/unit/modules/test_devmap.py index 4a48f00f276..9301619b0b4 100644 --- a/tests/unit/modules/test_devmap.py +++ b/tests/unit/modules/test_devmap.py @@ -1,50 +1,48 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os.path -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os.path # Import Salt Libs import salt.modules.devmap as devmap +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DevMapTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.devmap - ''' + """ + def setup_loader_modules(self): return {devmap: {}} def test_multipath_list(self): - ''' + """ Test for Device-Mapper Multipath list - ''' - mock = MagicMock(return_value='A') - with patch.dict(devmap.__salt__, {'cmd.run': mock}): - self.assertEqual(devmap.multipath_list(), ['A']) + """ + mock = MagicMock(return_value="A") + with patch.dict(devmap.__salt__, {"cmd.run": mock}): + self.assertEqual(devmap.multipath_list(), ["A"]) def test_multipath_flush(self): - ''' + """ Test for Device-Mapper Multipath flush - ''' + """ mock = MagicMock(return_value=False) - with patch.object(os.path, 'exists', mock): - self.assertEqual(devmap.multipath_flush('device'), - 'device does not exist') + with patch.object(os.path, "exists", mock): + self.assertEqual(devmap.multipath_flush("device"), "device does not exist") mock = MagicMock(return_value=True) - with patch.object(os.path, 'exists', mock): - mock = MagicMock(return_value='A') - with patch.dict(devmap.__salt__, {'cmd.run': mock}): - self.assertEqual(devmap.multipath_flush('device'), ['A']) + with patch.object(os.path, "exists", mock): + mock = MagicMock(return_value="A") + with patch.dict(devmap.__salt__, {"cmd.run": mock}): + self.assertEqual(devmap.multipath_flush("device"), ["A"]) diff --git a/tests/unit/modules/test_dig.py b/tests/unit/modules/test_dig.py index 501862d5a35..8ac0ae32f0c 100644 --- a/tests/unit/modules/test_dig.py +++ b/tests/unit/modules/test_dig.py @@ -1,189 +1,178 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch - # Import salt libs import salt.modules.dig as dig +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf _SPF_VALUES = { - 'dig +short xmission.com TXT': { - 'pid': 27282, - 'retcode': 0, - 'stderr': '', - 'stdout': '"v=spf1 a mx include:_spf.xmission.com ?all"' + "dig +short xmission.com TXT": { + "pid": 27282, + "retcode": 0, + "stderr": "", + "stdout": '"v=spf1 a mx include:_spf.xmission.com ?all"', }, - 'dig +short _spf.xmission.com TXT': { - 'pid': 27282, - 'retcode': 0, - 'stderr': '', - 'stdout': '"v=spf1 a mx ip4:198.60.22.0/24 ip4:166.70.13.0/24 ~all"' + "dig +short _spf.xmission.com TXT": { + "pid": 27282, + "retcode": 0, + "stderr": "", + "stdout": '"v=spf1 a mx ip4:198.60.22.0/24 ip4:166.70.13.0/24 ~all"', }, - 'dig +short xmission-redirect.com TXT': { - 'pid': 27282, - 'retcode': 0, - 'stderr': '', - 'stdout': 'v=spf1 redirect=_spf.xmission.com' + "dig +short xmission-redirect.com TXT": { + "pid": 27282, + "retcode": 0, + "stderr": "", + "stdout": "v=spf1 redirect=_spf.xmission.com", }, - 'dig +short foo.com TXT': { - 'pid': 27282, - 'retcode': 0, - 'stderr': '', - 'stdout': 'v=spf1 ip4:216.73.93.70/31 ip4:216.73.93.72/31 ~all' + "dig +short foo.com TXT": { + "pid": 27282, + "retcode": 0, + "stderr": "", + "stdout": "v=spf1 ip4:216.73.93.70/31 ip4:216.73.93.72/31 ~all", }, } def _spf_side_effect(key, python_shell=False): - return _SPF_VALUES.get(' '.join(key), {'pid': 27310, - 'retcode': 0, - 'stderr': '', - 'stdout': ''}) + return _SPF_VALUES.get( + " ".join(key), {"pid": 27310, "retcode": 0, "stderr": "", "stdout": ""} + ) -@skipIf(dig.__virtual__() is False, 'Dig must be installed') +@skipIf(dig.__virtual__() is False, "Dig must be installed") class DigTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {dig: {}} def test_check_ip(self): - self.assertTrue(dig.check_ip('127.0.0.1'), msg='Not a valid ip address') + self.assertTrue(dig.check_ip("127.0.0.1"), msg="Not a valid ip address") def test_check_ip_ipv6(self): self.assertTrue( - dig.check_ip('1111:2222:3333:4444:5555:6666:7777:8888'), - msg='Not a valid ip address' + dig.check_ip("1111:2222:3333:4444:5555:6666:7777:8888"), + msg="Not a valid ip address", ) def test_check_ip_ipv6_valid(self): - self.assertTrue(dig.check_ip('2607:fa18:0:3::4')) + self.assertTrue(dig.check_ip("2607:fa18:0:3::4")) def test_check_ip_neg(self): self.assertFalse( - dig.check_ip('-127.0.0.1'), - msg="Did not detect negative value as invalid" + dig.check_ip("-127.0.0.1"), msg="Did not detect negative value as invalid" ) def test_check_ip_empty(self): - self.assertFalse( - dig.check_ip(''), - msg="Did not detect empty value as invalid" - ) + self.assertFalse(dig.check_ip(""), msg="Did not detect empty value as invalid") def test_a(self): dig_mock = MagicMock( return_value={ - 'pid': 3656, - 'retcode': 0, - 'stderr': '', - 'stdout': '74.125.193.104\n' - '74.125.193.105\n' - '74.125.193.99\n' - '74.125.193.106\n' - '74.125.193.103\n' - '74.125.193.147' + "pid": 3656, + "retcode": 0, + "stderr": "", + "stdout": "74.125.193.104\n" + "74.125.193.105\n" + "74.125.193.99\n" + "74.125.193.106\n" + "74.125.193.103\n" + "74.125.193.147", } ) - with patch.dict(dig.__salt__, {'cmd.run_all': dig_mock}): + with patch.dict(dig.__salt__, {"cmd.run_all": dig_mock}): self.assertEqual( - dig.A('www.google.com'), - ['74.125.193.104', - '74.125.193.105', - '74.125.193.99', - '74.125.193.106', - '74.125.193.103', - '74.125.193.147'] + dig.A("www.google.com"), + [ + "74.125.193.104", + "74.125.193.105", + "74.125.193.99", + "74.125.193.106", + "74.125.193.103", + "74.125.193.147", + ], ) def test_aaaa(self): dig_mock = MagicMock( return_value={ - 'pid': 25451, - 'retcode': 0, - 'stderr': '', - 'stdout': '2607:f8b0:400f:801::1014' + "pid": 25451, + "retcode": 0, + "stderr": "", + "stdout": "2607:f8b0:400f:801::1014", } ) - with patch.dict(dig.__salt__, {'cmd.run_all': dig_mock}): - self.assertEqual( - dig.AAAA('www.google.com'), - ['2607:f8b0:400f:801::1014'] - ) + with patch.dict(dig.__salt__, {"cmd.run_all": dig_mock}): + self.assertEqual(dig.AAAA("www.google.com"), ["2607:f8b0:400f:801::1014"]) def test_ns(self): - with patch('salt.modules.dig.A', MagicMock(return_value=['ns4.google.com.'])): + with patch("salt.modules.dig.A", MagicMock(return_value=["ns4.google.com."])): dig_mock = MagicMock( return_value={ - 'pid': 26136, - 'retcode': 0, - 'stderr': '', - 'stdout': 'ns4.google.com.' + "pid": 26136, + "retcode": 0, + "stderr": "", + "stdout": "ns4.google.com.", } ) - with patch.dict(dig.__salt__, {'cmd.run_all': dig_mock}): - self.assertEqual(dig.NS('google.com'), ['ns4.google.com.']) + with patch.dict(dig.__salt__, {"cmd.run_all": dig_mock}): + self.assertEqual(dig.NS("google.com"), ["ns4.google.com."]) def test_spf(self): dig_mock = MagicMock(side_effect=_spf_side_effect) - with patch.dict(dig.__salt__, {'cmd.run_all': dig_mock}): - self.assertEqual( - dig.SPF('foo.com'), - ['216.73.93.70/31', '216.73.93.72/31'] - ) + with patch.dict(dig.__salt__, {"cmd.run_all": dig_mock}): + self.assertEqual(dig.SPF("foo.com"), ["216.73.93.70/31", "216.73.93.72/31"]) def test_spf_redir(self): - ''' + """ Test for SPF records which use the 'redirect' SPF mechanism https://en.wikipedia.org/wiki/Sender_Policy_Framework#Mechanisms - ''' + """ dig_mock = MagicMock(side_effect=_spf_side_effect) - with patch.dict(dig.__salt__, {'cmd.run_all': dig_mock}): + with patch.dict(dig.__salt__, {"cmd.run_all": dig_mock}): self.assertEqual( - dig.SPF('xmission-redirect.com'), - ['198.60.22.0/24', '166.70.13.0/24'] + dig.SPF("xmission-redirect.com"), ["198.60.22.0/24", "166.70.13.0/24"] ) def test_spf_include(self): - ''' + """ Test for SPF records which use the 'include' SPF mechanism https://en.wikipedia.org/wiki/Sender_Policy_Framework#Mechanisms - ''' + """ dig_mock = MagicMock(side_effect=_spf_side_effect) - with patch.dict(dig.__salt__, {'cmd.run_all': dig_mock}): + with patch.dict(dig.__salt__, {"cmd.run_all": dig_mock}): self.assertEqual( - dig.SPF('xmission.com'), - ['198.60.22.0/24', '166.70.13.0/24'] + dig.SPF("xmission.com"), ["198.60.22.0/24", "166.70.13.0/24"] ) def test_mx(self): dig_mock = MagicMock( return_value={ - 'pid': 27780, - 'retcode': 0, - 'stderr': '', - 'stdout': '10 aspmx.l.google.com.\n' - '20 alt1.aspmx.l.google.com.\n' - '40 alt3.aspmx.l.google.com.\n' - '50 alt4.aspmx.l.google.com.\n' - '30 alt2.aspmx.l.google.com.' + "pid": 27780, + "retcode": 0, + "stderr": "", + "stdout": "10 aspmx.l.google.com.\n" + "20 alt1.aspmx.l.google.com.\n" + "40 alt3.aspmx.l.google.com.\n" + "50 alt4.aspmx.l.google.com.\n" + "30 alt2.aspmx.l.google.com.", } ) - with patch.dict(dig.__salt__, {'cmd.run_all': dig_mock}): + with patch.dict(dig.__salt__, {"cmd.run_all": dig_mock}): self.assertEqual( - dig.MX('google.com'), - [['10', 'aspmx.l.google.com.'], - ['20', 'alt1.aspmx.l.google.com.'], - ['40', 'alt3.aspmx.l.google.com.'], - ['50', 'alt4.aspmx.l.google.com.'], - ['30', 'alt2.aspmx.l.google.com.']] + dig.MX("google.com"), + [ + ["10", "aspmx.l.google.com."], + ["20", "alt1.aspmx.l.google.com."], + ["40", "alt3.aspmx.l.google.com."], + ["50", "alt4.aspmx.l.google.com."], + ["30", "alt2.aspmx.l.google.com."], + ], ) diff --git a/tests/unit/modules/test_disk.py b/tests/unit/modules/test_disk.py index 754008381e5..17327ba8258 100644 --- a/tests/unit/modules/test_disk.py +++ b/tests/unit/modules/test_disk.py @@ -1,132 +1,219 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch - # Import Salt libs import salt.modules.disk as disk import salt.utils.path import salt.utils.platform +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + STUB_DISK_USAGE = { - '/': {'filesystem': None, '1K-blocks': 10000, 'used': 10000, 'available': 10000, 'capacity': 10000}, - '/dev': {'filesystem': None, '1K-blocks': 10000, 'used': 10000, 'available': 10000, 'capacity': 10000}, - '/run': {'filesystem': None, '1K-blocks': 10000, 'used': 10000, 'available': 10000, 'capacity': 10000}, - '/run/lock': {'filesystem': None, '1K-blocks': 10000, 'used': 10000, 'available': 10000, 'capacity': 10000}, - '/run/shm': {'filesystem': None, '1K-blocks': 10000, 'used': 10000, 'available': 10000, 'capacity': 10000}, - '/run/user': {'filesystem': None, '1K-blocks': 10000, 'used': 10000, 'available': 10000, 'capacity': 10000}, - '/sys/fs/cgroup': {'filesystem': None, '1K-blocks': 10000, 'used': 10000, 'available': 10000, 'capacity': 10000} - } + "/": { + "filesystem": None, + "1K-blocks": 10000, + "used": 10000, + "available": 10000, + "capacity": 10000, + }, + "/dev": { + "filesystem": None, + "1K-blocks": 10000, + "used": 10000, + "available": 10000, + "capacity": 10000, + }, + "/run": { + "filesystem": None, + "1K-blocks": 10000, + "used": 10000, + "available": 10000, + "capacity": 10000, + }, + "/run/lock": { + "filesystem": None, + "1K-blocks": 10000, + "used": 10000, + "available": 10000, + "capacity": 10000, + }, + "/run/shm": { + "filesystem": None, + "1K-blocks": 10000, + "used": 10000, + "available": 10000, + "capacity": 10000, + }, + "/run/user": { + "filesystem": None, + "1K-blocks": 10000, + "used": 10000, + "available": 10000, + "capacity": 10000, + }, + "/sys/fs/cgroup": { + "filesystem": None, + "1K-blocks": 10000, + "used": 10000, + "available": 10000, + "capacity": 10000, + }, +} STUB_DISK_INODEUSAGE = { - '/': {'inodes': 10000, 'used': 10000, 'free': 10000, 'use': 10000, 'filesystem': None}, - '/dev': {'inodes': 10000, 'used': 10000, 'free': 10000, 'use': 10000, 'filesystem': None}, - '/run': {'inodes': 10000, 'used': 10000, 'free': 10000, 'use': 10000, 'filesystem': None}, - '/run/lock': {'inodes': 10000, 'used': 10000, 'free': 10000, 'use': 10000, 'filesystem': None}, - '/run/shm': {'inodes': 10000, 'used': 10000, 'free': 10000, 'use': 10000, 'filesystem': None}, - '/run/user': {'inodes': 10000, 'used': 10000, 'free': 10000, 'use': 10000, 'filesystem': None}, - '/sys/fs/cgroup': {'inodes': 10000, 'used': 10000, 'free': 10000, 'use': 10000, 'filesystem': None} - } + "/": { + "inodes": 10000, + "used": 10000, + "free": 10000, + "use": 10000, + "filesystem": None, + }, + "/dev": { + "inodes": 10000, + "used": 10000, + "free": 10000, + "use": 10000, + "filesystem": None, + }, + "/run": { + "inodes": 10000, + "used": 10000, + "free": 10000, + "use": 10000, + "filesystem": None, + }, + "/run/lock": { + "inodes": 10000, + "used": 10000, + "free": 10000, + "use": 10000, + "filesystem": None, + }, + "/run/shm": { + "inodes": 10000, + "used": 10000, + "free": 10000, + "use": 10000, + "filesystem": None, + }, + "/run/user": { + "inodes": 10000, + "used": 10000, + "free": 10000, + "use": 10000, + "filesystem": None, + }, + "/sys/fs/cgroup": { + "inodes": 10000, + "used": 10000, + "free": 10000, + "use": 10000, + "filesystem": None, + }, +} STUB_DISK_PERCENT = { - '/': 50, - '/dev': 10, - '/run': 10, - '/run/lock': 10, - '/run/shm': 10, - '/run/user': 10, - '/sys/fs/cgroup': 10 - } + "/": 50, + "/dev": 10, + "/run": 10, + "/run/lock": 10, + "/run/shm": 10, + "/run/user": 10, + "/sys/fs/cgroup": 10, +} -STUB_DISK_BLKID = {'/dev/sda': {'TYPE': 'ext4', 'UUID': None}} +STUB_DISK_BLKID = {"/dev/sda": {"TYPE": "ext4", "UUID": None}} class DiskTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.disk module - ''' + """ + def setup_loader_modules(self): return {disk: {}} def test_usage_dict(self): - with patch.dict(disk.__grains__, {'kernel': 'Linux'}), \ - patch('salt.modules.disk.usage', - MagicMock(return_value=STUB_DISK_USAGE)): + with patch.dict(disk.__grains__, {"kernel": "Linux"}), patch( + "salt.modules.disk.usage", MagicMock(return_value=STUB_DISK_USAGE) + ): mock_cmd = MagicMock(return_value=1) - with patch.dict(disk.__salt__, {'cmd.run': mock_cmd}): + with patch.dict(disk.__salt__, {"cmd.run": mock_cmd}): self.assertDictEqual(STUB_DISK_USAGE, disk.usage(args=None)) def test_usage_none(self): - with patch.dict(disk.__grains__, {'kernel': 'Linux'}), \ - patch('salt.modules.disk.usage', MagicMock(return_value='')): + with patch.dict(disk.__grains__, {"kernel": "Linux"}), patch( + "salt.modules.disk.usage", MagicMock(return_value="") + ): mock_cmd = MagicMock(return_value=1) - with patch.dict(disk.__salt__, {'cmd.run': mock_cmd}): - self.assertEqual('', disk.usage(args=None)) + with patch.dict(disk.__salt__, {"cmd.run": mock_cmd}): + self.assertEqual("", disk.usage(args=None)) def test_inodeusage(self): - with patch.dict(disk.__grains__, {'kernel': 'OpenBSD'}), \ - patch('salt.modules.disk.inodeusage', - MagicMock(return_value=STUB_DISK_INODEUSAGE)): + with patch.dict(disk.__grains__, {"kernel": "OpenBSD"}), patch( + "salt.modules.disk.inodeusage", MagicMock(return_value=STUB_DISK_INODEUSAGE) + ): mock = MagicMock() - with patch.dict(disk.__salt__, {'cmd.run': mock}): + with patch.dict(disk.__salt__, {"cmd.run": mock}): self.assertDictEqual(STUB_DISK_INODEUSAGE, disk.inodeusage(args=None)) def test_percent(self): - with patch.dict(disk.__grains__, {'kernel': 'Linux'}), \ - patch('salt.modules.disk.percent', - MagicMock(return_value=STUB_DISK_PERCENT)): + with patch.dict(disk.__grains__, {"kernel": "Linux"}), patch( + "salt.modules.disk.percent", MagicMock(return_value=STUB_DISK_PERCENT) + ): mock = MagicMock() - with patch.dict(disk.__salt__, {'cmd.run': mock}): + with patch.dict(disk.__salt__, {"cmd.run": mock}): self.assertDictEqual(STUB_DISK_PERCENT, disk.percent(args=None)) def test_percent_args(self): - with patch.dict(disk.__grains__, {'kernel': 'Linux'}), \ - patch('salt.modules.disk.percent', MagicMock(return_value='/')): + with patch.dict(disk.__grains__, {"kernel": "Linux"}), patch( + "salt.modules.disk.percent", MagicMock(return_value="/") + ): mock = MagicMock() - with patch.dict(disk.__salt__, {'cmd.run': mock}): - self.assertEqual('/', disk.percent('/')) + with patch.dict(disk.__salt__, {"cmd.run": mock}): + self.assertEqual("/", disk.percent("/")) def test_blkid(self): - with patch.dict(disk.__salt__, {'cmd.run_stdout': MagicMock(return_value=1)}), \ - patch('salt.modules.disk.blkid', MagicMock(return_value=STUB_DISK_BLKID)): + with patch.dict( + disk.__salt__, {"cmd.run_stdout": MagicMock(return_value=1)} + ), patch("salt.modules.disk.blkid", MagicMock(return_value=STUB_DISK_BLKID)): self.assertDictEqual(STUB_DISK_BLKID, disk.blkid()) def test_dump(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(disk.__salt__, {'cmd.run_all': mock}): - disk.dump('/dev/sda') + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(disk.__salt__, {"cmd.run_all": mock}): + disk.dump("/dev/sda") mock.assert_called_once_with( - 'blockdev --getro --getsz --getss --getpbsz --getiomin ' - '--getioopt --getalignoff --getmaxsect --getsize ' - '--getsize64 --getra --getfra /dev/sda', - python_shell=False + "blockdev --getro --getsz --getss --getpbsz --getiomin " + "--getioopt --getalignoff --getmaxsect --getsize " + "--getsize64 --getra --getfra /dev/sda", + python_shell=False, ) def test_wipe(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(disk.__salt__, {'cmd.run_all': mock}): - disk.wipe('/dev/sda') - mock.assert_called_once_with( - 'wipefs -a /dev/sda', - python_shell=False - ) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(disk.__salt__, {"cmd.run_all": mock}): + disk.wipe("/dev/sda") + mock.assert_called_once_with("wipefs -a /dev/sda", python_shell=False) def test_tune(self): - mock = MagicMock(return_value='712971264\n512\n512\n512\n0\n0\n88\n712971264\n365041287168\n512\n512') - with patch.dict(disk.__salt__, {'cmd.run': mock}): - mock_dump = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch('salt.modules.disk.dump', mock_dump): - kwargs = {'read-ahead': 512, 'filesystem-read-ahead': 1024} - disk.tune('/dev/sda', **kwargs) + mock = MagicMock( + return_value="712971264\n512\n512\n512\n0\n0\n88\n712971264\n365041287168\n512\n512" + ) + with patch.dict(disk.__salt__, {"cmd.run": mock}): + mock_dump = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch("salt.modules.disk.dump", mock_dump): + kwargs = {"read-ahead": 512, "filesystem-read-ahead": 1024} + disk.tune("/dev/sda", **kwargs) self.assert_called_once(mock) @@ -135,83 +222,90 @@ class DiskTestCase(TestCase, LoaderModuleMockMixin): # Assert called once with either 'blockdev --setra 512 --setfra 512 /dev/sda' or # 'blockdev --setfra 512 --setra 512 /dev/sda' and python_shell=False kwarg. self.assertEqual(len(args), 1) - self.assertTrue(args[0].startswith('blockdev ')) - self.assertTrue(args[0].endswith(' /dev/sda')) - self.assertIn(' --setra 512 ', args[0]) - self.assertIn(' --setfra 1024 ', args[0]) + self.assertTrue(args[0].startswith("blockdev ")) + self.assertTrue(args[0].endswith(" /dev/sda")) + self.assertIn(" --setra 512 ", args[0]) + self.assertIn(" --setfra 1024 ", args[0]) self.assertEqual(len(args[0].split()), 6) - self.assertEqual(kwargs, {'python_shell': False}) + self.assertEqual(kwargs, {"python_shell": False}) def test_format(self): - ''' + """ unit tests for disk.format - ''' - device = '/dev/sdX1' + """ + device = "/dev/sdX1" mock = MagicMock(return_value=0) - with patch.dict(disk.__salt__, {'cmd.retcode': mock}),\ - patch('salt.utils.path.which', MagicMock(return_value=True)): + with patch.dict(disk.__salt__, {"cmd.retcode": mock}), patch( + "salt.utils.path.which", MagicMock(return_value=True) + ): self.assertEqual(disk.format_(device), True) def test_fat_format(self): - ''' + """ unit tests for disk.format when using fat argument - ''' - device = '/dev/sdX1' - expected = ['mkfs', '-t', 'fat', '-F', 12, '/dev/sdX1'] + """ + device = "/dev/sdX1" + expected = ["mkfs", "-t", "fat", "-F", 12, "/dev/sdX1"] mock = MagicMock(return_value=0) - with patch.dict(disk.__salt__, {'cmd.retcode': mock}),\ - patch('salt.utils.path.which', MagicMock(return_value=True)): - self.assertEqual(disk.format_(device, fs_type='fat', fat=12), True) + with patch.dict(disk.__salt__, {"cmd.retcode": mock}), patch( + "salt.utils.path.which", MagicMock(return_value=True) + ): + self.assertEqual(disk.format_(device, fs_type="fat", fat=12), True) args, kwargs = mock.call_args_list[0] assert expected == args[0] - @skipIf(not salt.utils.path.which('lsblk') and not salt.utils.path.which('df'), - 'lsblk or df not found') + @skipIf( + not salt.utils.path.which("lsblk") and not salt.utils.path.which("df"), + "lsblk or df not found", + ) def test_fstype(self): - ''' + """ unit tests for disk.fstype - ''' - device = '/dev/sdX1' - fs_type = 'ext4' - mock = MagicMock(return_value='FSTYPE\n{0}'.format(fs_type)) - with patch.dict(disk.__grains__, {'kernel': 'Linux'}), \ - patch.dict(disk.__salt__, {'cmd.run': mock}), \ - patch('salt.utils.path.which', MagicMock(return_value=True)): + """ + device = "/dev/sdX1" + fs_type = "ext4" + mock = MagicMock(return_value="FSTYPE\n{0}".format(fs_type)) + with patch.dict(disk.__grains__, {"kernel": "Linux"}), patch.dict( + disk.__salt__, {"cmd.run": mock} + ), patch("salt.utils.path.which", MagicMock(return_value=True)): self.assertEqual(disk.fstype(device), fs_type) def test_resize2fs(self): - ''' + """ unit tests for disk.resize2fs - ''' - device = '/dev/sdX1' + """ + device = "/dev/sdX1" mock = MagicMock() - with patch.dict(disk.__salt__, {'cmd.run_all': mock}), \ - patch('salt.utils.path.which', MagicMock(return_value=True)): + with patch.dict(disk.__salt__, {"cmd.run_all": mock}), patch( + "salt.utils.path.which", MagicMock(return_value=True) + ): disk.resize2fs(device) - mock.assert_called_once_with('resize2fs {0}'.format(device), python_shell=False) + mock.assert_called_once_with( + "resize2fs {0}".format(device), python_shell=False + ) - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows') - @skipIf(not salt.utils.path.which('mkfs'), 'mkfs not found') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows") + @skipIf(not salt.utils.path.which("mkfs"), "mkfs not found") def test_format_(self): - ''' + """ unit tests for disk.format_ - ''' - device = '/dev/sdX1' + """ + device = "/dev/sdX1" mock = MagicMock(return_value=0) - with patch.dict(disk.__salt__, {'cmd.retcode': mock}): + with patch.dict(disk.__salt__, {"cmd.retcode": mock}): disk.format_(device=device) - mock.assert_any_call(['mkfs', '-t', 'ext4', device], - ignore_retcode=True) + mock.assert_any_call(["mkfs", "-t", "ext4", device], ignore_retcode=True) - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows') - @skipIf(not salt.utils.path.which('mkfs'), 'mkfs not found') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows") + @skipIf(not salt.utils.path.which("mkfs"), "mkfs not found") def test_format__fat(self): - ''' + """ unit tests for disk.format_ with FAT parameter - ''' - device = '/dev/sdX1' + """ + device = "/dev/sdX1" mock = MagicMock(return_value=0) - with patch.dict(disk.__salt__, {'cmd.retcode': mock}): - disk.format_(device=device, fs_type='fat', fat=12) - mock.assert_any_call(['mkfs', '-t', 'fat', '-F', 12, device], - ignore_retcode=True) + with patch.dict(disk.__salt__, {"cmd.retcode": mock}): + disk.format_(device=device, fs_type="fat", fat=12) + mock.assert_any_call( + ["mkfs", "-t", "fat", "-F", 12, device], ignore_retcode=True + ) diff --git a/tests/unit/modules/test_djangomod.py b/tests/unit/modules/test_djangomod.py index 81bea4472a3..847a87b0131 100644 --- a/tests/unit/modules/test_djangomod.py +++ b/tests/unit/modules/test_djangomod.py @@ -1,251 +1,238 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.djangomod as djangomod +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DjangomodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.djangomod - ''' + """ + def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', lambda exe: exe) + patcher = patch("salt.utils.path.which", lambda exe: exe) patcher.start() self.addCleanup(patcher.stop) - return {djangomod: {'_get_django_admin': MagicMock(return_value=True)}} + return {djangomod: {"_get_django_admin": MagicMock(return_value=True)}} # 'command' function tests: 1 def test_command(self): - ''' + """ Test if it runs arbitrary django management command - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(djangomod.__salt__, {'cmd.run': mock}): - self.assertTrue(djangomod.command('DJANGO_SETTINGS_MODULE', - 'validate')) + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + self.assertTrue(djangomod.command("DJANGO_SETTINGS_MODULE", "validate")) # 'syncdb' function tests: 1 def test_syncdb(self): - ''' + """ Test if it runs the Django-Admin syncdb command - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(djangomod.__salt__, {'cmd.run': mock}): - self.assertTrue(djangomod.syncdb('DJANGO_SETTINGS_MODULE')) + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + self.assertTrue(djangomod.syncdb("DJANGO_SETTINGS_MODULE")) # 'migrate' function tests: 1 def test_migrate(self): - ''' + """ Test if it runs the Django-Admin migrate command - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(djangomod.__salt__, {'cmd.run': mock}): - self.assertTrue(djangomod.migrate('DJANGO_SETTINGS_MODULE')) + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + self.assertTrue(djangomod.migrate("DJANGO_SETTINGS_MODULE")) # 'createsuperuser' function tests: 1 def test_createsuperuser(self): - ''' + """ Test if it create a super user for the database. - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(djangomod.__salt__, {'cmd.run': mock}): - self.assertTrue(djangomod.createsuperuser('DJANGO_SETTINGS_MODULE', - 'SALT', - 'salt@slatstack.com')) + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + self.assertTrue( + djangomod.createsuperuser( + "DJANGO_SETTINGS_MODULE", "SALT", "salt@slatstack.com" + ) + ) # 'loaddata' function tests: 1 def test_loaddata(self): - ''' + """ Test if it loads fixture data - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(djangomod.__salt__, {'cmd.run': mock}): - self.assertTrue(djangomod.loaddata('DJANGO_SETTINGS_MODULE', - 'mydata')) + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + self.assertTrue(djangomod.loaddata("DJANGO_SETTINGS_MODULE", "mydata")) # 'collectstatic' function tests: 1 def test_collectstatic(self): - ''' + """ Test if it collect static files from each of your applications into a single location - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(djangomod.__salt__, {'cmd.run': mock}): - self.assertTrue(djangomod.collectstatic('DJANGO_SETTINGS_MODULE')) + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + self.assertTrue(djangomod.collectstatic("DJANGO_SETTINGS_MODULE")) class DjangomodCliCommandTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.djangomod - ''' + """ + def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', lambda exe: exe) + patcher = patch("salt.utils.path.which", lambda exe: exe) patcher.start() self.addCleanup(patcher.stop) return {djangomod: {}} def test_django_admin_cli_command(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): - djangomod.command('settings.py', 'runserver') + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + djangomod.command("settings.py", "runserver") mock.assert_called_once_with( - 'django-admin.py runserver --settings=settings.py', + "django-admin.py runserver --settings=settings.py", python_shell=False, env=None, - runas=None + runas=None, ) def test_django_admin_cli_command_with_args(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): djangomod.command( - 'settings.py', - 'runserver', + "settings.py", + "runserver", None, None, None, None, - 'noinput', - 'somethingelse' + "noinput", + "somethingelse", ) mock.assert_called_once_with( - 'django-admin.py runserver --settings=settings.py ' - '--noinput --somethingelse', + "django-admin.py runserver --settings=settings.py " + "--noinput --somethingelse", python_shell=False, env=None, - runas=None + runas=None, ) def test_django_admin_cli_command_with_kwargs(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): djangomod.command( - 'settings.py', - 'runserver', - None, - None, - None, - database='something' + "settings.py", "runserver", None, None, None, database="something" ) mock.assert_called_once_with( - 'django-admin.py runserver --settings=settings.py ' - '--database=something', + "django-admin.py runserver --settings=settings.py " + "--database=something", python_shell=False, env=None, - runas=None + runas=None, ) def test_django_admin_cli_command_with_kwargs_ignore_dunder(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): djangomod.command( - 'settings.py', 'runserver', None, None, None, __ignore='something' + "settings.py", "runserver", None, None, None, __ignore="something" ) mock.assert_called_once_with( - 'django-admin.py runserver --settings=settings.py', + "django-admin.py runserver --settings=settings.py", python_shell=False, env=None, - runas=None + runas=None, ) def test_django_admin_cli_syncdb(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): - djangomod.syncdb('settings.py') + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + djangomod.syncdb("settings.py") mock.assert_called_once_with( - 'django-admin.py syncdb --settings=settings.py --noinput', + "django-admin.py syncdb --settings=settings.py --noinput", python_shell=False, env=None, - runas=None + runas=None, ) def test_django_admin_cli_syncdb_migrate(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): - djangomod.syncdb('settings.py', migrate=True) + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + djangomod.syncdb("settings.py", migrate=True) mock.assert_called_once_with( - 'django-admin.py syncdb --settings=settings.py --migrate ' - '--noinput', + "django-admin.py syncdb --settings=settings.py --migrate " "--noinput", python_shell=False, env=None, - runas=None + runas=None, ) def test_django_admin_cli_migrate(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): - djangomod.migrate('settings.py') + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + djangomod.migrate("settings.py") mock.assert_called_once_with( - 'django-admin.py migrate --settings=settings.py --noinput', + "django-admin.py migrate --settings=settings.py --noinput", python_shell=False, env=None, - runas=None + runas=None, ) def test_django_admin_cli_createsuperuser(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): - djangomod.createsuperuser( - 'settings.py', 'testuser', 'user@example.com' - ) + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + djangomod.createsuperuser("settings.py", "testuser", "user@example.com") self.assertEqual(mock.call_count, 1) args, kwargs = mock.call_args # cmdline arguments are extracted from a kwargs dict so order isn't guaranteed. self.assertEqual(len(args), 1) - self.assertTrue(args[0].startswith('django-admin.py createsuperuser --')) - self.assertEqual(set(args[0].split()), - set('django-admin.py createsuperuser --settings=settings.py --noinput ' - '--username=testuser --email=user@example.com'.split())) - self.assertDictEqual(kwargs, {'python_shell': False, 'env': None, 'runas': None}) + self.assertTrue(args[0].startswith("django-admin.py createsuperuser --")) + self.assertEqual( + set(args[0].split()), + set( + "django-admin.py createsuperuser --settings=settings.py --noinput " + "--username=testuser --email=user@example.com".split() + ), + ) + self.assertDictEqual( + kwargs, {"python_shell": False, "env": None, "runas": None} + ) def no_test_loaddata(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): - djangomod.loaddata('settings.py', 'app1,app2') + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): + djangomod.loaddata("settings.py", "app1,app2") mock.assert_called_once_with( - 'django-admin.py loaddata --settings=settings.py app1 app2', + "django-admin.py loaddata --settings=settings.py app1 app2", ) def test_django_admin_cli_collectstatic(self): mock = MagicMock() - with patch.dict(djangomod.__salt__, - {'cmd.run': mock}): + with patch.dict(djangomod.__salt__, {"cmd.run": mock}): djangomod.collectstatic( - 'settings.py', None, True, 'something', True, True, True, True + "settings.py", None, True, "something", True, True, True, True ) mock.assert_called_once_with( - 'django-admin.py collectstatic --settings=settings.py ' - '--noinput --no-post-process --dry-run --clear --link ' - '--no-default-ignore --ignore=something', + "django-admin.py collectstatic --settings=settings.py " + "--noinput --no-post-process --dry-run --clear --link " + "--no-default-ignore --ignore=something", python_shell=False, env=None, - runas=None + runas=None, ) diff --git a/tests/unit/modules/test_dnsmasq.py b/tests/unit/modules/test_dnsmasq.py index 375d9a39150..0f7edc9972b 100644 --- a/tests/unit/modules/test_dnsmasq.py +++ b/tests/unit/modules/test_dnsmasq.py @@ -1,108 +1,113 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import textwrap -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - mock_open, - MagicMock, - patch, -) +import salt.modules.dnsmasq as dnsmasq # Import Salt Libs from salt.exceptions import CommandExecutionError -import salt.modules.dnsmasq as dnsmasq + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase class DnsmasqTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for the salt.modules.at module - ''' + """ + def setup_loader_modules(self): return {dnsmasq: {}} def test_version(self): - ''' + """ test to show installed version of dnsmasq. - ''' - mock = MagicMock(return_value='A B C') - with patch.dict(dnsmasq.__salt__, {'cmd.run': mock}): + """ + mock = MagicMock(return_value="A B C") + with patch.dict(dnsmasq.__salt__, {"cmd.run": mock}): self.assertEqual(dnsmasq.version(), "C") def test_fullversion(self): - ''' + """ Test to Show installed version of dnsmasq and compile options. - ''' - mock = MagicMock(return_value='A B C\nD E F G H I') - with patch.dict(dnsmasq.__salt__, {'cmd.run': mock}): - self.assertDictEqual(dnsmasq.fullversion(), - {'version': 'C', - 'compile options': ['G', 'H', 'I']}) + """ + mock = MagicMock(return_value="A B C\nD E F G H I") + with patch.dict(dnsmasq.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + dnsmasq.fullversion(), + {"version": "C", "compile options": ["G", "H", "I"]}, + ) def test_set_config(self): - ''' + """ test to show installed version of dnsmasq. - ''' - mock = MagicMock(return_value={'conf-dir': 'A'}) - with patch.object(dnsmasq, 'get_config', mock): - mock = MagicMock(return_value=['.', '~', 'bak', '#']) - with patch.object(os, 'listdir', mock): + """ + mock = MagicMock(return_value={"conf-dir": "A"}) + with patch.object(dnsmasq, "get_config", mock): + mock = MagicMock(return_value=[".", "~", "bak", "#"]) + with patch.object(os, "listdir", mock): self.assertDictEqual(dnsmasq.set_config(), {}) def test_set_config_filter_pub_kwargs(self): - ''' + """ Test that the kwargs returned from running the set_config function do not contain the __pub that may have been passed through in **kwargs. - ''' - with patch('salt.modules.dnsmasq.get_config', MagicMock(return_value={'conf-dir': 'A'})): - mock_domain = 'local' - mock_address = '/some-test-address.local/8.8.4.4' - with patch.dict(dnsmasq.__salt__, {'file.append': MagicMock()}): - ret = dnsmasq.set_config(follow=False, - domain=mock_domain, - address=mock_address, - __pub_pid=8184, - __pub_jid=20161101194639387946, - __pub_tgt='salt-call') - self.assertEqual(ret, {'domain': mock_domain, 'address': mock_address}) + """ + with patch( + "salt.modules.dnsmasq.get_config", MagicMock(return_value={"conf-dir": "A"}) + ): + mock_domain = "local" + mock_address = "/some-test-address.local/8.8.4.4" + with patch.dict(dnsmasq.__salt__, {"file.append": MagicMock()}): + ret = dnsmasq.set_config( + follow=False, + domain=mock_domain, + address=mock_address, + __pub_pid=8184, + __pub_jid=20161101194639387946, + __pub_tgt="salt-call", + ) + self.assertEqual(ret, {"domain": mock_domain, "address": mock_address}) def test_get_config(self): - ''' + """ test to dumps all options from the config file. - ''' - mock = MagicMock(return_value={'conf-dir': 'A'}) - with patch.object(dnsmasq, 'get_config', mock): - mock = MagicMock(return_value=['.', '~', 'bak', '#']) - with patch.object(os, 'listdir', mock): - self.assertDictEqual(dnsmasq.get_config(), {'conf-dir': 'A'}) + """ + mock = MagicMock(return_value={"conf-dir": "A"}) + with patch.object(dnsmasq, "get_config", mock): + mock = MagicMock(return_value=[".", "~", "bak", "#"]) + with patch.object(os, "listdir", mock): + self.assertDictEqual(dnsmasq.get_config(), {"conf-dir": "A"}) def test_parse_dnsmasq_no_file(self): - ''' + """ Tests that a CommandExecutionError is when a filename that doesn't exist is passed in. - ''' - self.assertRaises(CommandExecutionError, dnsmasq._parse_dnamasq, 'filename') + """ + self.assertRaises(CommandExecutionError, dnsmasq._parse_dnamasq, "filename") def test_parse_dnamasq(self): - ''' + """ test for generic function for parsing dnsmasq files including includes. - ''' - with patch('os.path.isfile', MagicMock(return_value=True)): - text_file_data = textwrap.dedent('''\ + """ + with patch("os.path.isfile", MagicMock(return_value=True)): + text_file_data = textwrap.dedent( + """\ line here second line A=B - #''') - with patch('salt.utils.files.fopen', - mock_open(read_data=text_file_data)): - self.assertDictEqual(dnsmasq._parse_dnamasq('filename'), - {'A': 'B', - 'unparsed': ['line here\n', - 'second line\n']}) + #""" + ) + with patch("salt.utils.files.fopen", mock_open(read_data=text_file_data)): + self.assertDictEqual( + dnsmasq._parse_dnamasq("filename"), + {"A": "B", "unparsed": ["line here\n", "second line\n"]}, + ) diff --git a/tests/unit/modules/test_dnsutil.py b/tests/unit/modules/test_dnsutil.py index 7ba6730bc52..a8a526e2722 100644 --- a/tests/unit/modules/test_dnsutil.py +++ b/tests/unit/modules/test_dnsutil.py @@ -1,121 +1,150 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, - mock_open, -) +import logging # Import Salt Libs import salt.modules.dnsutil as dnsutil import salt.utils.stringutils +from tests.support.mock import MagicMock, mock_open, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf log = logging.getLogger(__name__) mock_hosts_file = salt.utils.stringutils.to_str( - '##\n' - '# Host Database\n' - '#\n' - '# localhost is used to configure the loopback interface\n' - '# when the system is booting. Do not change this entry.\n' - '##\n' - '127.0.0.1 localhost\n' - '255.255.255.255 broadcasthost\n' - '::1 localhost\n' - 'fe80::1%lo0 localhost') + "##\n" + "# Host Database\n" + "#\n" + "# localhost is used to configure the loopback interface\n" + "# when the system is booting. Do not change this entry.\n" + "##\n" + "127.0.0.1 localhost\n" + "255.255.255.255 broadcasthost\n" + "::1 localhost\n" + "fe80::1%lo0 localhost" +) -mock_hosts_file_rtn = {'::1': ['localhost'], '255.255.255.255': ['broadcasthost'], - '127.0.0.1': ['localhost'], 'fe80::1%lo0': ['localhost']} +mock_hosts_file_rtn = { + "::1": ["localhost"], + "255.255.255.255": ["broadcasthost"], + "127.0.0.1": ["localhost"], + "fe80::1%lo0": ["localhost"], +} mock_soa_zone = salt.utils.stringutils.to_str( - '$TTL 3D\n' - '@ IN SOA land-5.com. root.land-5.com. (\n' - '199609203 ; Serial\n' - '28800 ; Refresh\n' - '7200 ; Retry\n' - '604800 ; Expire\n' - '86400) ; Minimum TTL\n' - 'NS land-5.com.\n\n' - '1 PTR localhost.') + "$TTL 3D\n" + "@ IN SOA land-5.com. root.land-5.com. (\n" + "199609203 ; Serial\n" + "28800 ; Refresh\n" + "7200 ; Retry\n" + "604800 ; Expire\n" + "86400) ; Minimum TTL\n" + "NS land-5.com.\n\n" + "1 PTR localhost." +) -mock_writes_list = salt.utils.data.decode([ - '##\n', - '# Host Database\n', - '#\n', - '# localhost is used to configure the loopback interface\n', - '# when the system is booting. Do not change this entry.\n', - '##\n', - '127.0.0.1 localhost', - '\n', - '255.255.255.255 broadcasthost', - '\n', - '::1 localhost', - '\n', - 'fe80::1%lo0 localhost', - '\n' - ], to_str=True +mock_writes_list = salt.utils.data.decode( + [ + "##\n", + "# Host Database\n", + "#\n", + "# localhost is used to configure the loopback interface\n", + "# when the system is booting. Do not change this entry.\n", + "##\n", + "127.0.0.1 localhost", + "\n", + "255.255.255.255 broadcasthost", + "\n", + "::1 localhost", + "\n", + "fe80::1%lo0 localhost", + "\n", + ], + to_str=True, ) class DNSUtilTestCase(TestCase): def test_parse_hosts(self): - with patch('salt.utils.files.fopen', mock_open(read_data=mock_hosts_file)): - self.assertEqual(dnsutil.parse_hosts(), {'::1': ['localhost'], - '255.255.255.255': ['broadcasthost'], - '127.0.0.1': ['localhost'], - 'fe80::1%lo0': ['localhost']}) + with patch("salt.utils.files.fopen", mock_open(read_data=mock_hosts_file)): + self.assertEqual( + dnsutil.parse_hosts(), + { + "::1": ["localhost"], + "255.255.255.255": ["broadcasthost"], + "127.0.0.1": ["localhost"], + "fe80::1%lo0": ["localhost"], + }, + ) def test_hosts_append(self): - with patch('salt.utils.files.fopen', mock_open(read_data=mock_hosts_file)) as m_open, \ - patch('salt.modules.dnsutil.parse_hosts', MagicMock(return_value=mock_hosts_file_rtn)): - dnsutil.hosts_append('/etc/hosts', '127.0.0.1', 'ad1.yuk.co,ad2.yuk.co') + with patch( + "salt.utils.files.fopen", mock_open(read_data=mock_hosts_file) + ) as m_open, patch( + "salt.modules.dnsutil.parse_hosts", + MagicMock(return_value=mock_hosts_file_rtn), + ): + dnsutil.hosts_append("/etc/hosts", "127.0.0.1", "ad1.yuk.co,ad2.yuk.co") writes = m_open.write_calls() # We should have called .write() only once, with the expected # content num_writes = len(writes) assert num_writes == 1, num_writes - expected = salt.utils.stringutils.to_str('\n127.0.0.1 ad1.yuk.co ad2.yuk.co') + expected = salt.utils.stringutils.to_str( + "\n127.0.0.1 ad1.yuk.co ad2.yuk.co" + ) assert writes[0] == expected, writes[0] def test_hosts_remove(self): - to_remove = 'ad1.yuk.co' - new_mock_file = mock_hosts_file + '\n127.0.0.1 ' + to_remove + '\n' - with patch('salt.utils.files.fopen', mock_open(read_data=new_mock_file)) as m_open: - dnsutil.hosts_remove('/etc/hosts', to_remove) + to_remove = "ad1.yuk.co" + new_mock_file = mock_hosts_file + "\n127.0.0.1 " + to_remove + "\n" + with patch( + "salt.utils.files.fopen", mock_open(read_data=new_mock_file) + ) as m_open: + dnsutil.hosts_remove("/etc/hosts", to_remove) writes = m_open.write_calls() assert writes == mock_writes_list, writes - @skipIf(True, 'Waiting on bug report fixes') + @skipIf(True, "Waiting on bug report fixes") def test_parse_zone(self): - with patch('salt.utils.files.fopen', mock_open(read_data=mock_soa_zone)): + with patch("salt.utils.files.fopen", mock_open(read_data=mock_soa_zone)): log.debug(mock_soa_zone) - log.debug(dnsutil.parse_zone('/var/lib/named/example.com.zone')) + log.debug(dnsutil.parse_zone("/var/lib/named/example.com.zone")) def test_to_seconds_hour(self): - self.assertEqual(dnsutil._to_seconds('4H'), 14400, - msg='Did not detect valid hours as invalid') + self.assertEqual( + dnsutil._to_seconds("4H"), + 14400, + msg="Did not detect valid hours as invalid", + ) def test_to_seconds_day(self): - self.assertEqual(dnsutil._to_seconds('1D'), 86400, - msg='Did not detect valid day as invalid') + self.assertEqual( + dnsutil._to_seconds("1D"), 86400, msg="Did not detect valid day as invalid" + ) def test_to_seconds_week(self): - self.assertEqual(dnsutil._to_seconds('2W'), 604800, - msg='Did not set time greater than one week to one week') + self.assertEqual( + dnsutil._to_seconds("2W"), + 604800, + msg="Did not set time greater than one week to one week", + ) def test_to_seconds_empty(self): - self.assertEqual(dnsutil._to_seconds(''), 604800, - msg='Did not set empty time to one week') + self.assertEqual( + dnsutil._to_seconds(""), 604800, msg="Did not set empty time to one week" + ) def test_to_seconds_large(self): - self.assertEqual(dnsutil._to_seconds('604801'), 604800, - msg='Did not set time greater than one week to one week') + self.assertEqual( + dnsutil._to_seconds("604801"), + 604800, + msg="Did not set time greater than one week to one week", + ) diff --git a/tests/unit/modules/test_dockermod.py b/tests/unit/modules/test_dockermod.py index 191bfc123f8..ba71d80b8c4 100644 --- a/tests/unit/modules/test_dockermod.py +++ b/tests/unit/modules/test_dockermod.py @@ -1,29 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for the docker module -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - Mock, - patch, - call -) import logging -log = logging.getLogger(__name__) # Import Salt Libs import salt.config import salt.loader -from salt.ext.six.moves import range -from salt.exceptions import CommandExecutionError, SaltInvocationError import salt.modules.dockermod as docker_mod +from salt.exceptions import CommandExecutionError, SaltInvocationError +from salt.ext.six.moves import range + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, call, patch +from tests.support.unit import TestCase, skipIf + +log = logging.getLogger(__name__) def _docker_py_version(): @@ -38,64 +35,56 @@ def _docker_py_version(): class DockerUnitTestCase(TestCase, LoaderModuleMockMixin): def fake_run(self, *args, **kwargs): print(args, kwargs) - return '{}' + return "{}" def setup_loader_modules(self): return { docker_mod: { - '__utils__': { - 'state.get_sls_opts': MagicMock(return_value={ - 'pillarenv': MagicMock(), - 'pillar': {}, - 'grains': {}, - }), - 'args.clean_kwargs': lambda **x: x, + "__utils__": { + "state.get_sls_opts": MagicMock( + return_value={ + "pillarenv": MagicMock(), + "pillar": {}, + "grains": {}, + } + ), + "args.clean_kwargs": lambda **x: x, }, - '__salt__': { - 'config.option': MagicMock(return_value=None), - 'cmd.run': self.fake_run, - }, - '__opts__': { - 'id': 'dockermod-unit-test', + "__salt__": { + "config.option": MagicMock(return_value=None), + "cmd.run": self.fake_run, }, + "__opts__": {"id": "dockermod-unit-test"}, }, } def test_trans_tar_should_have_grains_in_sls_opts_including_pillar_override(self): - container_name = 'fnord' + container_name = "fnord" expected_grains = { - 'roscivs': 'bottia', - 'fnord': 'dronf', - 'salt': 'NaCl', + "roscivs": "bottia", + "fnord": "dronf", + "salt": "NaCl", } expected_pillars = { - 'this': { - 'is': { - 'my': { - 'pillar': 'data', - }, - }, - }, + "this": {"is": {"my": {"pillar": "data"}}}, } - extra_pillar_data = {'some': 'extras'} - fake_trans_tar = MagicMock(return_value=b'hi') + extra_pillar_data = {"some": "extras"} + fake_trans_tar = MagicMock(return_value=b"hi") patch_trans_tar = patch( - 'salt.modules.dockermod._prepare_trans_tar', - fake_trans_tar, + "salt.modules.dockermod._prepare_trans_tar", fake_trans_tar, ) patch_call = patch( - 'salt.modules.dockermod.call', - MagicMock(return_value=expected_grains), + "salt.modules.dockermod.call", MagicMock(return_value=expected_grains), ) fake_get_pillar = MagicMock() fake_get_pillar.compile_pillar.return_value = expected_pillars patch_pillar = patch( - 'salt.modules.dockermod.salt.pillar.get_pillar', + "salt.modules.dockermod.salt.pillar.get_pillar", MagicMock(return_value=fake_get_pillar), ) patch_run_all = patch( - 'salt.modules.dockermod.run_all', - MagicMock(return_value={'retcode': 1, 'stderr': 'early exit test'}), + "salt.modules.dockermod.run_all", + MagicMock(return_value={"retcode": 1, "stderr": "early exit test"}), ) with patch_trans_tar, patch_call, patch_pillar, patch_run_all: docker_mod.sls(container_name, pillar=extra_pillar_data) @@ -103,581 +92,691 @@ class DockerUnitTestCase(TestCase, LoaderModuleMockMixin): actual_sls_opts = fake_trans_tar.call_args[0][1] self.assertDictContainsSubset( expected_grains, - actual_sls_opts['grains'], - 'Docker container grains not provided to thin client creation', + actual_sls_opts["grains"], + "Docker container grains not provided to thin client creation", ) expected_pillars.update(extra_pillar_data) self.assertDictContainsSubset( expected_pillars, - actual_sls_opts['pillar'], - 'Docker container pillar not provided to thin client creation', + actual_sls_opts["pillar"], + "Docker container pillar not provided to thin client creation", ) -@skipIf(docker_mod.HAS_DOCKER_PY is False, 'docker-py must be installed to run these tests. Skipping.') +@skipIf( + docker_mod.HAS_DOCKER_PY is False, + "docker-py must be installed to run these tests. Skipping.", +) class DockerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate docker module - ''' + """ + def setup_loader_modules(self): utils = salt.loader.utils( salt.config.DEFAULT_MINION_OPTS.copy(), - whitelist=['args', 'docker', 'json', 'state', 'thin', - 'systemd', 'path', 'platform'] + whitelist=[ + "args", + "docker", + "json", + "state", + "thin", + "systemd", + "path", + "platform", + ], ) - return {docker_mod: {'__context__': {'docker.docker_version': ''}, - '__utils__': utils}} + return { + docker_mod: { + "__context__": {"docker.docker_version": ""}, + "__utils__": utils, + } + } try: docker_version = docker_mod.docker.version_info except AttributeError: - docker_version = 0, + docker_version = (0,) def setUp(self): - ''' + """ Ensure we aren't persisting context dunders between tests - ''' - docker_mod.__context__.pop('docker.client', None) + """ + docker_mod.__context__.pop("docker.client", None) def test_failed_login(self): - ''' + """ Check that when docker.login failed a retcode other then 0 is part of the return. - ''' + """ client = Mock() get_client_mock = MagicMock(return_value=client) - ref_out = { - 'stdout': '', - 'stderr': 'login failed', - 'retcode': 1 - } - with patch.dict(docker_mod.__pillar__, {'docker-registries': {'portus.example.com:5000': - {'username': 'admin', 'password': 'linux12345', 'email': 'tux@example.com'}}}): - with patch.object(docker_mod, '_get_client', get_client_mock): + ref_out = {"stdout": "", "stderr": "login failed", "retcode": 1} + with patch.dict( + docker_mod.__pillar__, + { + "docker-registries": { + "portus.example.com:5000": { + "username": "admin", + "password": "linux12345", + "email": "tux@example.com", + } + } + }, + ): + with patch.object(docker_mod, "_get_client", get_client_mock): dunder_salt = { - 'cmd.run_all': MagicMock(return_value=ref_out), - 'config.get': MagicMock(return_value={}), - 'config.option': MagicMock(return_value={}), + "cmd.run_all": MagicMock(return_value=ref_out), + "config.get": MagicMock(return_value={}), + "config.option": MagicMock(return_value={}), } with patch.dict(docker_mod.__salt__, dunder_salt): - ret = docker_mod.login('portus.example.com:5000') - self.assertIn('retcode', ret) - self.assertNotEqual(ret['retcode'], 0) + ret = docker_mod.login("portus.example.com:5000") + self.assertIn("retcode", ret) + self.assertNotEqual(ret["retcode"], 0) def test_ps_with_host_true(self): - ''' + """ Check that docker.ps called with host is ``True``, include resutlt of ``network.interfaces`` command in returned result. - ''' + """ client = Mock() client.containers = MagicMock(return_value=[]) get_client_mock = MagicMock(return_value=client) - network_interfaces = Mock(return_value={'mocked': None}) + network_interfaces = Mock(return_value={"mocked": None}) - with patch.dict(docker_mod.__salt__, - {'network.interfaces': network_interfaces}): - with patch.object(docker_mod, '_get_client', get_client_mock): + with patch.dict( + docker_mod.__salt__, {"network.interfaces": network_interfaces} + ): + with patch.object(docker_mod, "_get_client", get_client_mock): ret = docker_mod.ps_(host=True) - self.assertEqual(ret, - {'host': {'interfaces': {'mocked': None}}}) + self.assertEqual(ret, {"host": {"interfaces": {"mocked": None}}}) def test_ps_with_filters(self): - ''' + """ Check that docker.ps accept filters parameter. - ''' + """ client = Mock() client.containers = MagicMock(return_value=[]) get_client_mock = MagicMock(return_value=client) - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.ps_(filters={'label': 'KEY'}) + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.ps_(filters={"label": "KEY"}) client.containers.assert_called_once_with( - all=True, - filters={'label': 'KEY'}) - - def test_check_mine_cache_is_refreshed_on_container_change_event(self): - ''' - Every command that might modify docker containers state. - Should trig an update on ``mine.send`` - ''' - with patch.object(docker_mod, '_get_exec_driver'): - client_args_mock = MagicMock(return_value={ - 'create_container': [ - 'image', 'command', 'hostname', 'user', 'detach', 'stdin_open', - 'tty', 'ports', 'environment', 'volumes', 'network_disabled', - 'name', 'entrypoint', 'working_dir', 'domainname', 'cpuset', - 'host_config', 'mac_address', 'labels', 'volume_driver', - 'stop_signal', 'networking_config', 'healthcheck', - 'stop_timeout'], - 'host_config': [ - 'binds', 'port_bindings', 'lxc_conf', 'publish_all_ports', - 'links', 'privileged', 'dns', 'dns_search', 'volumes_from', - 'network_mode', 'restart_policy', 'cap_add', 'cap_drop', - 'devices', 'extra_hosts', 'read_only', 'pid_mode', 'ipc_mode', - 'security_opt', 'ulimits', 'log_config', 'mem_limit', - 'memswap_limit', 'mem_reservation', 'kernel_memory', - 'mem_swappiness', 'cgroup_parent', 'group_add', 'cpu_quota', - 'cpu_period', 'blkio_weight', 'blkio_weight_device', - 'device_read_bps', 'device_write_bps', 'device_read_iops', - 'device_write_iops', 'oom_kill_disable', 'shm_size', 'sysctls', - 'tmpfs', 'oom_score_adj', 'dns_opt', 'cpu_shares', - 'cpuset_cpus', 'userns_mode', 'pids_limit', 'isolation', - 'auto_remove', 'storage_opt'], - 'networking_config': [ - 'aliases', 'links', 'ipv4_address', 'ipv6_address', - 'link_local_ips'], - } - + all=True, filters={"label": "KEY"} ) - for command_name, args in (('create', ()), - ('rm_', ()), - ('kill', ()), - ('pause', ()), - ('signal_', ('KILL',)), - ('start_', ()), - ('stop', ()), - ('unpause', ()), - ('_run', ('command',)), - ('_script', ('command',)), - ): + def test_check_mine_cache_is_refreshed_on_container_change_event(self): + """ + Every command that might modify docker containers state. + Should trig an update on ``mine.send`` + """ + with patch.object(docker_mod, "_get_exec_driver"): + client_args_mock = MagicMock( + return_value={ + "create_container": [ + "image", + "command", + "hostname", + "user", + "detach", + "stdin_open", + "tty", + "ports", + "environment", + "volumes", + "network_disabled", + "name", + "entrypoint", + "working_dir", + "domainname", + "cpuset", + "host_config", + "mac_address", + "labels", + "volume_driver", + "stop_signal", + "networking_config", + "healthcheck", + "stop_timeout", + ], + "host_config": [ + "binds", + "port_bindings", + "lxc_conf", + "publish_all_ports", + "links", + "privileged", + "dns", + "dns_search", + "volumes_from", + "network_mode", + "restart_policy", + "cap_add", + "cap_drop", + "devices", + "extra_hosts", + "read_only", + "pid_mode", + "ipc_mode", + "security_opt", + "ulimits", + "log_config", + "mem_limit", + "memswap_limit", + "mem_reservation", + "kernel_memory", + "mem_swappiness", + "cgroup_parent", + "group_add", + "cpu_quota", + "cpu_period", + "blkio_weight", + "blkio_weight_device", + "device_read_bps", + "device_write_bps", + "device_read_iops", + "device_write_iops", + "oom_kill_disable", + "shm_size", + "sysctls", + "tmpfs", + "oom_score_adj", + "dns_opt", + "cpu_shares", + "cpuset_cpus", + "userns_mode", + "pids_limit", + "isolation", + "auto_remove", + "storage_opt", + ], + "networking_config": [ + "aliases", + "links", + "ipv4_address", + "ipv6_address", + "link_local_ips", + ], + } + ) + + for command_name, args in ( + ("create", ()), + ("rm_", ()), + ("kill", ()), + ("pause", ()), + ("signal_", ("KILL",)), + ("start_", ()), + ("stop", ()), + ("unpause", ()), + ("_run", ("command",)), + ("_script", ("command",)), + ): mine_send = Mock() command = getattr(docker_mod, command_name) client = MagicMock() - client.api_version = '1.12' - with patch.dict(docker_mod.__salt__, - {'mine.send': mine_send, - 'container_resource.run': MagicMock(), - 'config.get': MagicMock(return_value=True), - 'config.option': MagicMock(return_value=True), - 'cp.cache_file': MagicMock(return_value=False)}): - with patch.dict(docker_mod.__utils__, - {'docker.get_client_args': client_args_mock}): - with patch.object(docker_mod, '_get_client', client): - command('container', *args) + client.api_version = "1.12" + with patch.dict( + docker_mod.__salt__, + { + "mine.send": mine_send, + "container_resource.run": MagicMock(), + "config.get": MagicMock(return_value=True), + "config.option": MagicMock(return_value=True), + "cp.cache_file": MagicMock(return_value=False), + }, + ): + with patch.dict( + docker_mod.__utils__, + {"docker.get_client_args": client_args_mock}, + ): + with patch.object(docker_mod, "_get_client", client): + command("container", *args) try: mine_send.assert_called_with( - 'docker.ps', verbose=True, all=True, host=True) + "docker.ps", verbose=True, all=True, host=True + ) except AssertionError as exc: raise Exception( - 'command \'{0}\' did not call docker.ps with expected ' - 'arguments: {1}'.format(command_name, exc) + "command '{0}' did not call docker.ps with expected " + "arguments: {1}".format(command_name, exc) ) def test_update_mine(self): - ''' + """ Test the docker.update_mine config option - ''' + """ + def config_get_disabled(val, default): - return {'base_url': docker_mod.NOTSET, - 'version': docker_mod.NOTSET, - 'docker.url': docker_mod.NOTSET, - 'docker.version': docker_mod.NOTSET, - 'docker.machine': docker_mod.NOTSET, - 'docker.update_mine': False}[val] + return { + "base_url": docker_mod.NOTSET, + "version": docker_mod.NOTSET, + "docker.url": docker_mod.NOTSET, + "docker.version": docker_mod.NOTSET, + "docker.machine": docker_mod.NOTSET, + "docker.update_mine": False, + }[val] def config_get_enabled(val, default): - return {'base_url': docker_mod.NOTSET, - 'version': docker_mod.NOTSET, - 'docker.url': docker_mod.NOTSET, - 'docker.version': docker_mod.NOTSET, - 'docker.machine': docker_mod.NOTSET, - 'docker.update_mine': True}[val] + return { + "base_url": docker_mod.NOTSET, + "version": docker_mod.NOTSET, + "docker.url": docker_mod.NOTSET, + "docker.version": docker_mod.NOTSET, + "docker.machine": docker_mod.NOTSET, + "docker.update_mine": True, + }[val] mine_mock = Mock() dunder_salt = { - 'config.get': MagicMock(side_effect=config_get_disabled), - 'config.option': MagicMock(return_value=False), - 'mine.send': mine_mock, + "config.get": MagicMock(side_effect=config_get_disabled), + "config.option": MagicMock(return_value=False), + "mine.send": mine_mock, } - with patch.dict(docker_mod.__salt__, dunder_salt), \ - patch.dict(docker_mod.__context__, {'docker.client': Mock()}), \ - patch.object(docker_mod, 'state', MagicMock(return_value='stopped')): - docker_mod.stop('foo', timeout=1) + with patch.dict(docker_mod.__salt__, dunder_salt), patch.dict( + docker_mod.__context__, {"docker.client": Mock()} + ), patch.object(docker_mod, "state", MagicMock(return_value="stopped")): + docker_mod.stop("foo", timeout=1) mine_mock.assert_not_called() - with patch.dict(docker_mod.__salt__, dunder_salt), \ - patch.dict(docker_mod.__context__, {'docker.client': Mock()}), \ - patch.object(docker_mod, 'state', MagicMock(return_value='stopped')): - dunder_salt['config.get'].side_effect = config_get_enabled - dunder_salt['config.option'].return_value = True - docker_mod.stop('foo', timeout=1) + with patch.dict(docker_mod.__salt__, dunder_salt), patch.dict( + docker_mod.__context__, {"docker.client": Mock()} + ), patch.object(docker_mod, "state", MagicMock(return_value="stopped")): + dunder_salt["config.get"].side_effect = config_get_enabled + dunder_salt["config.option"].return_value = True + docker_mod.stop("foo", timeout=1) self.assert_called_once(mine_mock) - @skipIf(_docker_py_version() < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') + @skipIf( + _docker_py_version() < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) def test_list_networks(self, *args): - ''' + """ test list networks. - ''' + """ __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), + "config.get": Mock(), + "mine.send": Mock(), } host_config = {} client = Mock() - client.api_version = '1.21' - client.networks = Mock(return_value=[ - {'Name': 'foo', - 'Id': '01234', - 'Containers': {}} - ]) + client.api_version = "1.21" + client.networks = Mock( + return_value=[{"Name": "foo", "Id": "01234", "Containers": {}}] + ) get_client_mock = MagicMock(return_value=client) - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.networks(names=['foo'], ids=['01234']) - client.networks.assert_called_once_with(names=['foo'], ids=['01234']) + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.networks(names=["foo"], ids=["01234"]) + client.networks.assert_called_once_with(names=["foo"], ids=["01234"]) - @skipIf(docker_version < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') + @skipIf( + docker_version < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) def test_create_network(self, *args): - ''' + """ test create network. - ''' + """ __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), + "config.get": Mock(), + "mine.send": Mock(), } host_config = {} client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" get_client_mock = MagicMock(return_value=client) - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.create_network('foo', - driver='bridge', - driver_opts={}, - gateway='192.168.0.1', - ip_range='192.168.0.128/25', - subnet='192.168.0.0/24' - ) - client.create_network.assert_called_once_with('foo', - driver='bridge', - options={}, - ipam={ - 'Config': [{ - 'Gateway': '192.168.0.1', - 'IPRange': '192.168.0.128/25', - 'Subnet': '192.168.0.0/24' - }], - 'Driver': 'default', - }, - check_duplicate=True) - - @skipIf(docker_version < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') - def test_remove_network(self, *args): - ''' - test remove network. - ''' - __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), - } - host_config = {} - client = Mock() - client.api_version = '1.21' - get_client_mock = MagicMock(return_value=client) - - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.remove_network('foo') - client.remove_network.assert_called_once_with('foo') - - @skipIf(docker_version < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') - def test_inspect_network(self, *args): - ''' - test inspect network. - ''' - __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), - } - host_config = {} - client = Mock() - client.api_version = '1.21' - get_client_mock = MagicMock(return_value=client) - - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.inspect_network('foo') - client.inspect_network.assert_called_once_with('foo') - - @skipIf(docker_version < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') - def test_connect_container_to_network(self, *args): - ''' - test connect_container_to_network - ''' - __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), - } - host_config = {} - client = Mock() - client.api_version = '1.21' - get_client_mock = MagicMock(return_value=client) - - context = {'docker.exec_driver': 'docker-exec'} - - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.dict(docker_mod.__context__, context): - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.connect_container_to_network('container', 'foo') - client.connect_container_to_network.assert_called_once_with( - 'container', 'foo') - - @skipIf(docker_version < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') - def test_disconnect_container_from_network(self, *args): - ''' - test disconnect_container_from_network - ''' - __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), - } - host_config = {} - client = Mock() - client.api_version = '1.21' - get_client_mock = MagicMock(return_value=client) - - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.disconnect_container_from_network('container', 'foo') - client.disconnect_container_from_network.assert_called_once_with( - 'container', 'foo') - - @skipIf(docker_version < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') - def test_list_volumes(self, *args): - ''' - test list volumes. - ''' - __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), - } - client = Mock() - client.api_version = '1.21' - get_client_mock = MagicMock(return_value=client) - - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.volumes( - filters={'dangling': [True]}, + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.create_network( + "foo", + driver="bridge", + driver_opts={}, + gateway="192.168.0.1", + ip_range="192.168.0.128/25", + subnet="192.168.0.0/24", ) - client.volumes.assert_called_once_with( - filters={'dangling': [True]}, + client.create_network.assert_called_once_with( + "foo", + driver="bridge", + options={}, + ipam={ + "Config": [ + { + "Gateway": "192.168.0.1", + "IPRange": "192.168.0.128/25", + "Subnet": "192.168.0.0/24", + } + ], + "Driver": "default", + }, + check_duplicate=True, ) - @skipIf(docker_version < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') - def test_create_volume(self, *args): - ''' - test create volume. - ''' + @skipIf( + docker_version < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) + def test_remove_network(self, *args): + """ + test remove network. + """ __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), + "config.get": Mock(), + "mine.send": Mock(), } + host_config = {} client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" get_client_mock = MagicMock(return_value=client) - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.object(docker_mod, '_get_client', get_client_mock): + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.remove_network("foo") + client.remove_network.assert_called_once_with("foo") + + @skipIf( + docker_version < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) + def test_inspect_network(self, *args): + """ + test inspect network. + """ + __salt__ = { + "config.get": Mock(), + "mine.send": Mock(), + } + host_config = {} + client = Mock() + client.api_version = "1.21" + get_client_mock = MagicMock(return_value=client) + + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.inspect_network("foo") + client.inspect_network.assert_called_once_with("foo") + + @skipIf( + docker_version < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) + def test_connect_container_to_network(self, *args): + """ + test connect_container_to_network + """ + __salt__ = { + "config.get": Mock(), + "mine.send": Mock(), + } + host_config = {} + client = Mock() + client.api_version = "1.21" + get_client_mock = MagicMock(return_value=client) + + context = {"docker.exec_driver": "docker-exec"} + + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.dict(docker_mod.__context__, context): + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.connect_container_to_network("container", "foo") + client.connect_container_to_network.assert_called_once_with("container", "foo") + + @skipIf( + docker_version < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) + def test_disconnect_container_from_network(self, *args): + """ + test disconnect_container_from_network + """ + __salt__ = { + "config.get": Mock(), + "mine.send": Mock(), + } + host_config = {} + client = Mock() + client.api_version = "1.21" + get_client_mock = MagicMock(return_value=client) + + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.disconnect_container_from_network("container", "foo") + client.disconnect_container_from_network.assert_called_once_with( + "container", "foo" + ) + + @skipIf( + docker_version < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) + def test_list_volumes(self, *args): + """ + test list volumes. + """ + __salt__ = { + "config.get": Mock(), + "mine.send": Mock(), + } + client = Mock() + client.api_version = "1.21" + get_client_mock = MagicMock(return_value=client) + + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.volumes(filters={"dangling": [True]},) + client.volumes.assert_called_once_with(filters={"dangling": [True]},) + + @skipIf( + docker_version < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) + def test_create_volume(self, *args): + """ + test create volume. + """ + __salt__ = { + "config.get": Mock(), + "mine.send": Mock(), + } + client = Mock() + client.api_version = "1.21" + get_client_mock = MagicMock(return_value=client) + + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.object(docker_mod, "_get_client", get_client_mock): docker_mod.create_volume( - 'foo', - driver='bridge', - driver_opts={}, + "foo", driver="bridge", driver_opts={}, ) client.create_volume.assert_called_once_with( - 'foo', - driver='bridge', - driver_opts={}, + "foo", driver="bridge", driver_opts={}, ) - @skipIf(docker_version < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') + @skipIf( + docker_version < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) def test_remove_volume(self, *args): - ''' + """ test remove volume. - ''' + """ __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), + "config.get": Mock(), + "mine.send": Mock(), } client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" get_client_mock = MagicMock(return_value=client) - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.remove_volume('foo') - client.remove_volume.assert_called_once_with('foo') + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.remove_volume("foo") + client.remove_volume.assert_called_once_with("foo") - @skipIf(docker_version < (1, 5, 0), - 'docker module must be installed to run this test or is too old. >=1.5.0') + @skipIf( + docker_version < (1, 5, 0), + "docker module must be installed to run this test or is too old. >=1.5.0", + ) def test_inspect_volume(self, *args): - ''' + """ test inspect volume. - ''' + """ __salt__ = { - 'config.get': Mock(), - 'mine.send': Mock(), + "config.get": Mock(), + "mine.send": Mock(), } client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" get_client_mock = MagicMock(return_value=client) - with patch.dict(docker_mod.__dict__, - {'__salt__': __salt__}): - with patch.object(docker_mod, '_get_client', get_client_mock): - docker_mod.inspect_volume('foo') - client.inspect_volume.assert_called_once_with('foo') + with patch.dict(docker_mod.__dict__, {"__salt__": __salt__}): + with patch.object(docker_mod, "_get_client", get_client_mock): + docker_mod.inspect_volume("foo") + client.inspect_volume.assert_called_once_with("foo") def test_wait_success(self): client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" client.wait = Mock(return_value=0) get_client_mock = MagicMock(return_value=client) - docker_inspect_container = Mock(side_effect=[ - {'State': {'Running': True}}, - {'State': {'Stopped': True}}]) - with patch.object(docker_mod, 'inspect_container', - docker_inspect_container): - with patch.object(docker_mod, '_get_client', get_client_mock): + docker_inspect_container = Mock( + side_effect=[{"State": {"Running": True}}, {"State": {"Stopped": True}}] + ) + with patch.object(docker_mod, "inspect_container", docker_inspect_container): + with patch.object(docker_mod, "_get_client", get_client_mock): docker_mod._clear_context() - result = docker_mod.wait('foo') - self.assertEqual(result, {'result': True, - 'exit_status': 0, - 'state': {'new': 'stopped', - 'old': 'running'}}) + result = docker_mod.wait("foo") + self.assertEqual( + result, + { + "result": True, + "exit_status": 0, + "state": {"new": "stopped", "old": "running"}, + }, + ) def test_wait_fails_already_stopped(self): client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" client.wait = Mock(return_value=0) get_client_mock = MagicMock(return_value=client) - docker_inspect_container = Mock(side_effect=[ - {'State': {'Stopped': True}}, - {'State': {'Stopped': True}}, - ]) - with patch.object(docker_mod, 'inspect_container', - docker_inspect_container): - with patch.object(docker_mod, '_get_client', get_client_mock): + docker_inspect_container = Mock( + side_effect=[{"State": {"Stopped": True}}, {"State": {"Stopped": True}}] + ) + with patch.object(docker_mod, "inspect_container", docker_inspect_container): + with patch.object(docker_mod, "_get_client", get_client_mock): docker_mod._clear_context() - result = docker_mod.wait('foo') - self.assertEqual(result, {'result': False, - 'comment': "Container 'foo' already stopped", - 'exit_status': 0, - 'state': {'new': 'stopped', - 'old': 'stopped'}}) + result = docker_mod.wait("foo") + self.assertEqual( + result, + { + "result": False, + "comment": "Container 'foo' already stopped", + "exit_status": 0, + "state": {"new": "stopped", "old": "stopped"}, + }, + ) def test_wait_success_already_stopped(self): client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" client.wait = Mock(return_value=0) get_client_mock = MagicMock(return_value=client) - docker_inspect_container = Mock(side_effect=[ - {'State': {'Stopped': True}}, - {'State': {'Stopped': True}}, - ]) - with patch.object(docker_mod, 'inspect_container', - docker_inspect_container): - with patch.object(docker_mod, '_get_client', get_client_mock): + docker_inspect_container = Mock( + side_effect=[{"State": {"Stopped": True}}, {"State": {"Stopped": True}}] + ) + with patch.object(docker_mod, "inspect_container", docker_inspect_container): + with patch.object(docker_mod, "_get_client", get_client_mock): docker_mod._clear_context() - result = docker_mod.wait('foo', ignore_already_stopped=True) - self.assertEqual(result, {'result': True, - 'comment': "Container 'foo' already stopped", - 'exit_status': 0, - 'state': {'new': 'stopped', - 'old': 'stopped'}}) + result = docker_mod.wait("foo", ignore_already_stopped=True) + self.assertEqual( + result, + { + "result": True, + "comment": "Container 'foo' already stopped", + "exit_status": 0, + "state": {"new": "stopped", "old": "stopped"}, + }, + ) def test_wait_success_absent_container(self): client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" get_client_mock = MagicMock(return_value=client) docker_inspect_container = Mock(side_effect=CommandExecutionError) - with patch.object(docker_mod, 'inspect_container', - docker_inspect_container): - with patch.object(docker_mod, '_get_client', get_client_mock): + with patch.object(docker_mod, "inspect_container", docker_inspect_container): + with patch.object(docker_mod, "_get_client", get_client_mock): docker_mod._clear_context() - result = docker_mod.wait('foo', ignore_already_stopped=True) - self.assertEqual(result, {'result': True, - 'comment': "Container 'foo' absent"}) + result = docker_mod.wait("foo", ignore_already_stopped=True) + self.assertEqual(result, {"result": True, "comment": "Container 'foo' absent"}) def test_wait_fails_on_exit_status(self): client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" client.wait = Mock(return_value=1) get_client_mock = MagicMock(return_value=client) - docker_inspect_container = Mock(side_effect=[ - {'State': {'Running': True}}, - {'State': {'Stopped': True}}]) - with patch.object(docker_mod, 'inspect_container', - docker_inspect_container): - with patch.object(docker_mod, '_get_client', get_client_mock): + docker_inspect_container = Mock( + side_effect=[{"State": {"Running": True}}, {"State": {"Stopped": True}}] + ) + with patch.object(docker_mod, "inspect_container", docker_inspect_container): + with patch.object(docker_mod, "_get_client", get_client_mock): docker_mod._clear_context() - result = docker_mod.wait('foo', fail_on_exit_status=True) - self.assertEqual(result, {'result': False, - 'exit_status': 1, - 'state': {'new': 'stopped', - 'old': 'running'}}) + result = docker_mod.wait("foo", fail_on_exit_status=True) + self.assertEqual( + result, + { + "result": False, + "exit_status": 1, + "state": {"new": "stopped", "old": "running"}, + }, + ) def test_wait_fails_on_exit_status_and_already_stopped(self): client = Mock() - client.api_version = '1.21' + client.api_version = "1.21" client.wait = Mock(return_value=1) get_client_mock = MagicMock(return_value=client) - docker_inspect_container = Mock(side_effect=[ - {'State': {'Stopped': True}}, - {'State': {'Stopped': True}}]) - with patch.object(docker_mod, 'inspect_container', - docker_inspect_container): - with patch.object(docker_mod, '_get_client', get_client_mock): + docker_inspect_container = Mock( + side_effect=[{"State": {"Stopped": True}}, {"State": {"Stopped": True}}] + ) + with patch.object(docker_mod, "inspect_container", docker_inspect_container): + with patch.object(docker_mod, "_get_client", get_client_mock): docker_mod._clear_context() - result = docker_mod.wait('foo', - ignore_already_stopped=True, - fail_on_exit_status=True) - self.assertEqual(result, {'result': False, - 'comment': "Container 'foo' already stopped", - 'exit_status': 1, - 'state': {'new': 'stopped', - 'old': 'stopped'}}) + result = docker_mod.wait( + "foo", ignore_already_stopped=True, fail_on_exit_status=True + ) + self.assertEqual( + result, + { + "result": False, + "comment": "Container 'foo' already stopped", + "exit_status": 1, + "state": {"new": "stopped", "old": "stopped"}, + }, + ) def test_sls_build(self, *args): - ''' + """ test build sls image. - ''' - docker_start_mock = MagicMock( - return_value={}) - docker_create_mock = MagicMock( - return_value={'Id': 'ID', 'Name': 'NAME'}) + """ + docker_start_mock = MagicMock(return_value={}) + docker_create_mock = MagicMock(return_value={"Id": "ID", "Name": "NAME"}) docker_stop_mock = MagicMock( - return_value={'state': {'old': 'running', 'new': 'stopped'}, - 'result': True}) + return_value={"state": {"old": "running", "new": "stopped"}, "result": True} + ) docker_rm_mock = MagicMock(return_value={}) docker_commit_mock = MagicMock( - return_value={'Id': 'ID2', 'Image': 'foo', 'Time_Elapsed': 42}) + return_value={"Id": "ID2", "Image": "foo", "Time_Elapsed": 42} + ) docker_sls_mock = MagicMock( return_value={ @@ -688,7 +787,7 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): "result": True, "duration": 13.492, "__run_num__": 0, - "changes": {} + "changes": {}, }, "test_|-always-passes_|-foo_|-succeed_without_changes": { "comment": "Success!", @@ -697,42 +796,42 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): "result": True, "duration": 0.363, "__run_num__": 1, - "changes": {} - } - }) + "changes": {}, + }, + } + ) ret = None - with patch.object(docker_mod, 'start_', docker_start_mock), \ - patch.object(docker_mod, 'create', docker_create_mock), \ - patch.object(docker_mod, 'stop', docker_stop_mock), \ - patch.object(docker_mod, 'commit', docker_commit_mock), \ - patch.object(docker_mod, 'sls', docker_sls_mock), \ - patch.object(docker_mod, 'rm_', docker_rm_mock): - ret = docker_mod.sls_build('foo', mods='foo') + with patch.object(docker_mod, "start_", docker_start_mock), patch.object( + docker_mod, "create", docker_create_mock + ), patch.object(docker_mod, "stop", docker_stop_mock), patch.object( + docker_mod, "commit", docker_commit_mock + ), patch.object( + docker_mod, "sls", docker_sls_mock + ), patch.object( + docker_mod, "rm_", docker_rm_mock + ): + ret = docker_mod.sls_build("foo", mods="foo") docker_create_mock.assert_called_once_with( - cmd='sleep infinity', - image='opensuse/python', interactive=True, tty=True) - docker_start_mock.assert_called_once_with('ID') - docker_sls_mock.assert_called_once_with('ID', 'foo') - docker_stop_mock.assert_called_once_with('ID') - docker_rm_mock.assert_called_once_with('ID') - docker_commit_mock.assert_called_once_with('ID', 'foo', tag='latest') - self.assertEqual( - {'Id': 'ID2', 'Image': 'foo', 'Time_Elapsed': 42}, ret) + cmd="sleep infinity", image="opensuse/python", interactive=True, tty=True + ) + docker_start_mock.assert_called_once_with("ID") + docker_sls_mock.assert_called_once_with("ID", "foo") + docker_stop_mock.assert_called_once_with("ID") + docker_rm_mock.assert_called_once_with("ID") + docker_commit_mock.assert_called_once_with("ID", "foo", tag="latest") + self.assertEqual({"Id": "ID2", "Image": "foo", "Time_Elapsed": 42}, ret) def test_sls_build_dryrun(self, *args): - ''' + """ test build sls image in dryrun mode. - ''' - docker_start_mock = MagicMock( - return_value={}) - docker_create_mock = MagicMock( - return_value={'Id': 'ID', 'Name': 'NAME'}) + """ + docker_start_mock = MagicMock(return_value={}) + docker_create_mock = MagicMock(return_value={"Id": "ID", "Name": "NAME"}) docker_stop_mock = MagicMock( - return_value={'state': {'old': 'running', 'new': 'stopped'}, - 'result': True}) - docker_rm_mock = MagicMock( - return_value={}) + return_value={"state": {"old": "running", "new": "stopped"}, "result": True} + ) + docker_rm_mock = MagicMock(return_value={}) docker_sls_mock = MagicMock( return_value={ @@ -743,7 +842,7 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): "result": True, "duration": 13.492, "__run_num__": 0, - "changes": {} + "changes": {}, }, "test_|-always-passes_|-foo_|-succeed_without_changes": { "comment": "Success!", @@ -752,26 +851,29 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): "result": True, "duration": 0.363, "__run_num__": 1, - "changes": {} - } - }) + "changes": {}, + }, + } + ) ret = None - with patch.object(docker_mod, 'start_', docker_start_mock), \ - patch.object(docker_mod, 'create', docker_create_mock), \ - patch.object(docker_mod, 'stop', docker_stop_mock), \ - patch.object(docker_mod, 'rm_', docker_rm_mock), \ - patch.object(docker_mod, 'sls', docker_sls_mock): - ret = docker_mod.sls_build('foo', mods='foo', dryrun=True) + with patch.object(docker_mod, "start_", docker_start_mock), patch.object( + docker_mod, "create", docker_create_mock + ), patch.object(docker_mod, "stop", docker_stop_mock), patch.object( + docker_mod, "rm_", docker_rm_mock + ), patch.object( + docker_mod, "sls", docker_sls_mock + ): + ret = docker_mod.sls_build("foo", mods="foo", dryrun=True) docker_create_mock.assert_called_once_with( - cmd='sleep infinity', - image='opensuse/python', interactive=True, tty=True) - docker_start_mock.assert_called_once_with('ID') - docker_sls_mock.assert_called_once_with('ID', 'foo') - docker_stop_mock.assert_called_once_with('ID') - docker_rm_mock.assert_called_once_with('ID') + cmd="sleep infinity", image="opensuse/python", interactive=True, tty=True + ) + docker_start_mock.assert_called_once_with("ID") + docker_sls_mock.assert_called_once_with("ID", "foo") + docker_stop_mock.assert_called_once_with("ID") + docker_rm_mock.assert_called_once_with("ID") self.assertEqual( - { + { "file_|-/etc/test.sh_|-/etc/test.sh_|-managed": { "comment": "File /etc/test.sh is in the correct state", "name": "/etc/test.sh", @@ -779,7 +881,7 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): "result": True, "duration": 13.492, "__run_num__": 0, - "changes": {} + "changes": {}, }, "test_|-always-passes_|-foo_|-succeed_without_changes": { "comment": "Success!", @@ -788,294 +890,297 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): "result": True, "duration": 0.363, "__run_num__": 1, - "changes": {} - } + "changes": {}, }, - ret) + }, + ret, + ) def test_call_success(self): - ''' + """ test module calling inside containers - ''' + """ ret = None docker_run_all_mock = MagicMock( return_value={ - 'retcode': 0, - 'stdout': '{"retcode": 0, "comment": "container cmd"}', - 'stderr': 'err', - }) - docker_copy_to_mock = MagicMock( - return_value={ - 'retcode': 0 - }) - docker_config_mock = MagicMock( - return_value='' - ) + "retcode": 0, + "stdout": '{"retcode": 0, "comment": "container cmd"}', + "stderr": "err", + } + ) + docker_copy_to_mock = MagicMock(return_value={"retcode": 0}) + docker_config_mock = MagicMock(return_value="") client = Mock() client.put_archive = Mock() get_client_mock = MagicMock(return_value=client) - context = {'docker.exec_driver': 'docker-exec'} - salt_dunder = {'config.option': docker_config_mock} + context = {"docker.exec_driver": "docker-exec"} + salt_dunder = {"config.option": docker_config_mock} - with patch.object(docker_mod, 'run_all', docker_run_all_mock), \ - patch.object(docker_mod, 'copy_to', docker_copy_to_mock), \ - patch.object(docker_mod, '_get_client', get_client_mock), \ - patch.dict(docker_mod.__opts__, {'cachedir': '/tmp'}), \ - patch.dict(docker_mod.__salt__, salt_dunder), \ - patch.dict(docker_mod.__context__, context): + with patch.object(docker_mod, "run_all", docker_run_all_mock), patch.object( + docker_mod, "copy_to", docker_copy_to_mock + ), patch.object(docker_mod, "_get_client", get_client_mock), patch.dict( + docker_mod.__opts__, {"cachedir": "/tmp"} + ), patch.dict( + docker_mod.__salt__, salt_dunder + ), patch.dict( + docker_mod.__context__, context + ): # call twice to verify tmp path later for i in range(2): - ret = docker_mod.call('ID', 'test.arg', 1, 2, arg1='val1') + ret = docker_mod.call("ID", "test.arg", 1, 2, arg1="val1") # Check that the directory is different each time # [ call(name, [args]), ... self.maxDiff = None - self.assertIn('mkdir', docker_run_all_mock.mock_calls[0][1][1]) - self.assertIn('mkdir', docker_run_all_mock.mock_calls[4][1][1]) - self.assertNotEqual(docker_run_all_mock.mock_calls[0][1][1], - docker_run_all_mock.mock_calls[4][1][1]) + self.assertIn("mkdir", docker_run_all_mock.mock_calls[0][1][1]) + self.assertIn("mkdir", docker_run_all_mock.mock_calls[4][1][1]) + self.assertNotEqual( + docker_run_all_mock.mock_calls[0][1][1], + docker_run_all_mock.mock_calls[4][1][1], + ) - self.assertIn('salt-call', docker_run_all_mock.mock_calls[2][1][1]) - self.assertIn('salt-call', docker_run_all_mock.mock_calls[6][1][1]) - self.assertNotEqual(docker_run_all_mock.mock_calls[2][1][1], - docker_run_all_mock.mock_calls[6][1][1]) + self.assertIn("salt-call", docker_run_all_mock.mock_calls[2][1][1]) + self.assertIn("salt-call", docker_run_all_mock.mock_calls[6][1][1]) + self.assertNotEqual( + docker_run_all_mock.mock_calls[2][1][1], + docker_run_all_mock.mock_calls[6][1][1], + ) # check thin untar - self.assertIn('tarfile', docker_run_all_mock.mock_calls[1][1][1]) - self.assertIn('tarfile', docker_run_all_mock.mock_calls[5][1][1]) - self.assertNotEqual(docker_run_all_mock.mock_calls[1][1][1], - docker_run_all_mock.mock_calls[5][1][1]) + self.assertIn("tarfile", docker_run_all_mock.mock_calls[1][1][1]) + self.assertIn("tarfile", docker_run_all_mock.mock_calls[5][1][1]) + self.assertNotEqual( + docker_run_all_mock.mock_calls[1][1][1], + docker_run_all_mock.mock_calls[5][1][1], + ) # check directory cleanup - self.assertIn('rm -rf', docker_run_all_mock.mock_calls[3][1][1]) - self.assertIn('rm -rf', docker_run_all_mock.mock_calls[7][1][1]) - self.assertNotEqual(docker_run_all_mock.mock_calls[3][1][1], - docker_run_all_mock.mock_calls[7][1][1]) + self.assertIn("rm -rf", docker_run_all_mock.mock_calls[3][1][1]) + self.assertIn("rm -rf", docker_run_all_mock.mock_calls[7][1][1]) + self.assertNotEqual( + docker_run_all_mock.mock_calls[3][1][1], + docker_run_all_mock.mock_calls[7][1][1], + ) self.assertEqual({"retcode": 0, "comment": "container cmd"}, ret) def test_images_with_empty_tags(self): - ''' + """ docker 1.12 reports also images without tags with `null`. - ''' + """ client = Mock() - client.api_version = '1.24' + client.api_version = "1.24" client.images = Mock( - return_value=[{'Id': 'sha256:abcde', - 'RepoTags': None}, - {'Id': 'sha256:abcdef'}, - {'Id': 'sha256:abcdefg', - 'RepoTags': ['image:latest']}]) + return_value=[ + {"Id": "sha256:abcde", "RepoTags": None}, + {"Id": "sha256:abcdef"}, + {"Id": "sha256:abcdefg", "RepoTags": ["image:latest"]}, + ] + ) get_client_mock = MagicMock(return_value=client) - with patch.object(docker_mod, '_get_client', get_client_mock): + with patch.object(docker_mod, "_get_client", get_client_mock): docker_mod._clear_context() result = docker_mod.images() - self.assertEqual(result, - {'sha256:abcdefg': {'RepoTags': ['image:latest']}}) + self.assertEqual(result, {"sha256:abcdefg": {"RepoTags": ["image:latest"]}}) def test_compare_container_image_id_resolution(self): - ''' + """ Test comparing two containers when one's inspect output is an ID and not formatted in image:tag notation. - ''' + """ + def _inspect_container_effect(id_): return { - 'container1': {'Config': {'Image': 'realimage:latest'}, - 'HostConfig': {}}, - 'container2': {'Config': {'Image': 'image_id'}, - 'HostConfig': {}}, + "container1": { + "Config": {"Image": "realimage:latest"}, + "HostConfig": {}, + }, + "container2": {"Config": {"Image": "image_id"}, "HostConfig": {}}, }[id_] def _inspect_image_effect(id_): return { - 'realimage:latest': {'Id': 'image_id'}, - 'image_id': {'Id': 'image_id'}, + "realimage:latest": {"Id": "image_id"}, + "image_id": {"Id": "image_id"}, }[id_] inspect_container_mock = MagicMock(side_effect=_inspect_container_effect) inspect_image_mock = MagicMock(side_effect=_inspect_image_effect) - with patch.object(docker_mod, 'inspect_container', inspect_container_mock): - with patch.object(docker_mod, 'inspect_image', inspect_image_mock): - ret = docker_mod.compare_containers('container1', 'container2') + with patch.object(docker_mod, "inspect_container", inspect_container_mock): + with patch.object(docker_mod, "inspect_image", inspect_image_mock): + ret = docker_mod.compare_containers("container1", "container2") self.assertEqual(ret, {}) def test_compare_container_ulimits_order(self): - ''' + """ Test comparing two containers when the order of the Ulimits HostConfig values are different, but the values are the same. - ''' + """ + def _inspect_container_effect(id_): return { - 'container1': {'Config': {}, - 'HostConfig': { - 'Ulimits': [ - {'Hard': -1, 'Soft': -1, 'Name': 'core'}, - {'Hard': 65536, 'Soft': 65536, 'Name': 'nofile'} - ] - }}, - 'container2': {'Config': {}, - 'HostConfig': { - 'Ulimits': [ - {'Hard': 65536, 'Soft': 65536, 'Name': 'nofile'}, - {'Hard': -1, 'Soft': -1, 'Name': 'core'} - ] - }}, + "container1": { + "Config": {}, + "HostConfig": { + "Ulimits": [ + {"Hard": -1, "Soft": -1, "Name": "core"}, + {"Hard": 65536, "Soft": 65536, "Name": "nofile"}, + ] + }, + }, + "container2": { + "Config": {}, + "HostConfig": { + "Ulimits": [ + {"Hard": 65536, "Soft": 65536, "Name": "nofile"}, + {"Hard": -1, "Soft": -1, "Name": "core"}, + ] + }, + }, }[id_] inspect_container_mock = MagicMock(side_effect=_inspect_container_effect) - with patch.object(docker_mod, 'inspect_container', inspect_container_mock): - ret = docker_mod.compare_container('container1', 'container2') # pylint: disable=not-callable + with patch.object(docker_mod, "inspect_container", inspect_container_mock): + # pylint: disable=not-callable + ret = docker_mod.compare_container("container1", "container2") + # pylint: enable=not-callable self.assertEqual(ret, {}) def test_compare_container_env_order(self): - ''' + """ Test comparing two containers when the order of the Env HostConfig values are different, but the values are the same. - ''' + """ + def _inspect_container_effect(id_): return { - 'container1': {'Config': {}, - 'HostConfig': { - 'Env': [ - 'FOO=bar', - 'HELLO=world', - ] - }}, - 'container2': {'Config': {}, - 'HostConfig': { - 'Env': [ - 'HELLO=world', - 'FOO=bar', - ] - }}, + "container1": { + "Config": {}, + "HostConfig": {"Env": ["FOO=bar", "HELLO=world"]}, + }, + "container2": { + "Config": {}, + "HostConfig": {"Env": ["HELLO=world", "FOO=bar"]}, + }, }[id_] inspect_container_mock = MagicMock(side_effect=_inspect_container_effect) - with patch.object(docker_mod, 'inspect_container', inspect_container_mock): - ret = docker_mod.compare_container('container1', 'container2') # pylint: disable=not-callable + with patch.object(docker_mod, "inspect_container", inspect_container_mock): + # pylint: disable=not-callable + ret = docker_mod.compare_container("container1", "container2") + # pylint: enable=not-callable self.assertEqual(ret, {}) def test_resolve_tag(self): - ''' + """ Test the resolve_tag function. It runs docker.insect_image on the image name passed and then looks for the RepoTags key in the result - ''' - id_ = 'sha256:6ad733544a6317992a6fac4eb19fe1df577d4dec7529efec28a5bd0edad0fd30' - tags = ['foo:latest', 'foo:bar'] - mock_tagged = MagicMock(return_value={'Id': id_, 'RepoTags': tags}) - mock_untagged = MagicMock(return_value={'Id': id_, 'RepoTags': []}) - mock_unexpected = MagicMock(return_value={'Id': id_}) + """ + id_ = "sha256:6ad733544a6317992a6fac4eb19fe1df577d4dec7529efec28a5bd0edad0fd30" + tags = ["foo:latest", "foo:bar"] + mock_tagged = MagicMock(return_value={"Id": id_, "RepoTags": tags}) + mock_untagged = MagicMock(return_value={"Id": id_, "RepoTags": []}) + mock_unexpected = MagicMock(return_value={"Id": id_}) mock_not_found = MagicMock(side_effect=CommandExecutionError()) - with patch.object(docker_mod, 'inspect_image', mock_tagged): - self.assertEqual(docker_mod.resolve_tag('foo'), tags[0]) - self.assertEqual(docker_mod.resolve_tag('foo', all=True), tags) + with patch.object(docker_mod, "inspect_image", mock_tagged): + self.assertEqual(docker_mod.resolve_tag("foo"), tags[0]) + self.assertEqual(docker_mod.resolve_tag("foo", all=True), tags) - with patch.object(docker_mod, 'inspect_image', mock_untagged): - self.assertEqual(docker_mod.resolve_tag('foo'), id_) - self.assertEqual(docker_mod.resolve_tag('foo', all=True), [id_]) + with patch.object(docker_mod, "inspect_image", mock_untagged): + self.assertEqual(docker_mod.resolve_tag("foo"), id_) + self.assertEqual(docker_mod.resolve_tag("foo", all=True), [id_]) - with patch.object(docker_mod, 'inspect_image', mock_unexpected): - self.assertEqual(docker_mod.resolve_tag('foo'), id_) - self.assertEqual(docker_mod.resolve_tag('foo', all=True), [id_]) + with patch.object(docker_mod, "inspect_image", mock_unexpected): + self.assertEqual(docker_mod.resolve_tag("foo"), id_) + self.assertEqual(docker_mod.resolve_tag("foo", all=True), [id_]) - with patch.object(docker_mod, 'inspect_image', mock_not_found): - self.assertIs(docker_mod.resolve_tag('foo'), False) - self.assertIs(docker_mod.resolve_tag('foo', all=True), False) + with patch.object(docker_mod, "inspect_image", mock_not_found): + self.assertIs(docker_mod.resolve_tag("foo"), False) + self.assertIs(docker_mod.resolve_tag("foo", all=True), False) def test_prune(self): - ''' + """ Test the prune function - ''' + """ + def _run(**kwargs): - side_effect = kwargs.pop('side_effect', None) + side_effect = kwargs.pop("side_effect", None) # No arguments passed, we should be pruning everything but volumes client = Mock() if side_effect is not None: client.side_effect = side_effect - with patch.object(docker_mod, '_client_wrapper', client): + with patch.object(docker_mod, "_client_wrapper", client): docker_mod.prune(**kwargs) return client # Containers only, no filters client = _run(containers=True) - client.assert_called_once_with( - 'prune_containers', - filters={} - ) + client.assert_called_once_with("prune_containers", filters={}) # Containers only, with filters - client = _run(containers=True, until='24h', label='foo,bar=baz') + client = _run(containers=True, until="24h", label="foo,bar=baz") client.assert_called_once_with( - 'prune_containers', - filters={'until': ['24h'], 'label': ['foo', 'bar=baz']} + "prune_containers", filters={"until": ["24h"], "label": ["foo", "bar=baz"]} ) # Images only, no filters client = _run(images=True) - client.assert_called_once_with( - 'prune_images', - filters={} - ) + client.assert_called_once_with("prune_images", filters={}) # Images only, with filters - client = _run(images=True, dangling=True, - until='24h', label='foo,bar=baz') + client = _run(images=True, dangling=True, until="24h", label="foo,bar=baz") client.assert_called_once_with( - 'prune_images', - filters={'dangling': True, - 'until': ['24h'], - 'label': ['foo', 'bar=baz']} + "prune_images", + filters={"dangling": True, "until": ["24h"], "label": ["foo", "bar=baz"]}, ) # Networks only, no filters client = _run(networks=True) - client.assert_called_once_with('prune_networks', filters={}) + client.assert_called_once_with("prune_networks", filters={}) # Networks only, with filters - client = _run(networks=True, until='24h', label='foo,bar=baz') + client = _run(networks=True, until="24h", label="foo,bar=baz") client.assert_called_once_with( - 'prune_networks', - filters={'until': ['24h'], 'label': ['foo', 'bar=baz']} + "prune_networks", filters={"until": ["24h"], "label": ["foo", "bar=baz"]} ) # Volumes only, no filters client = _run(system=False, volumes=True) - client.assert_called_once_with('prune_volumes', filters={}) + client.assert_called_once_with("prune_volumes", filters={}) # Volumes only, with filters - client = _run(system=False, volumes=True, label='foo,bar=baz') + client = _run(system=False, volumes=True, label="foo,bar=baz") client.assert_called_once_with( - 'prune_volumes', - filters={'label': ['foo', 'bar=baz']} + "prune_volumes", filters={"label": ["foo", "bar=baz"]} ) # Containers and images, no filters client = _run(containers=True, images=True) self.assertEqual( client.call_args_list, - [ - call('prune_containers', filters={}), - call('prune_images', filters={}), - ] + [call("prune_containers", filters={}), call("prune_images", filters={})], ) # Containers and images, with filters - client = _run(containers=True, images=True, - until='24h', label='foo,bar=baz') + client = _run(containers=True, images=True, until="24h", label="foo,bar=baz") self.assertEqual( client.call_args_list, [ - call('prune_containers', - filters={'until': ['24h'], 'label': ['foo', 'bar=baz']}), - call('prune_images', - filters={'until': ['24h'], 'label': ['foo', 'bar=baz']}), - ] + call( + "prune_containers", + filters={"until": ["24h"], "label": ["foo", "bar=baz"]}, + ), + call( + "prune_images", + filters={"until": ["24h"], "label": ["foo", "bar=baz"]}, + ), + ], ) # System, no volumes, no filters, assuming prune_build in docker-py @@ -1083,62 +1188,60 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual( client.call_args_list, [ - call('prune_containers', filters={}), - call('prune_images', filters={}), - call('prune_networks', filters={}), - call('prune_build', filters={}), - ] + call("prune_containers", filters={}), + call("prune_images", filters={}), + call("prune_networks", filters={}), + call("prune_build", filters={}), + ], ) # System, no volumes, no filters, assuming prune_build not in docker-py client = _run( system=True, - side_effect=[None, None, None, SaltInvocationError(), - None, None, None] + side_effect=[None, None, None, SaltInvocationError(), None, None, None], ) self.assertEqual( client.call_args_list, [ - call('prune_containers', filters={}), - call('prune_images', filters={}), - call('prune_networks', filters={}), - call('prune_build', filters={}), - call('_url', '/build/prune'), - call('_post', None), - call('_result', None, True), - ] + call("prune_containers", filters={}), + call("prune_images", filters={}), + call("prune_networks", filters={}), + call("prune_build", filters={}), + call("_url", "/build/prune"), + call("_post", None), + call("_result", None, True), + ], ) # System, no volumes, with filters, assuming prune_build in docker-py - client = _run(system=True, label='foo,bar=baz') + client = _run(system=True, label="foo,bar=baz") self.assertEqual( client.call_args_list, [ - call('prune_containers', filters={'label': ['foo', 'bar=baz']}), - call('prune_images', filters={'label': ['foo', 'bar=baz']}), - call('prune_networks', filters={'label': ['foo', 'bar=baz']}), - call('prune_build', filters={'label': ['foo', 'bar=baz']}), - ] + call("prune_containers", filters={"label": ["foo", "bar=baz"]}), + call("prune_images", filters={"label": ["foo", "bar=baz"]}), + call("prune_networks", filters={"label": ["foo", "bar=baz"]}), + call("prune_build", filters={"label": ["foo", "bar=baz"]}), + ], ) # System, no volumes, with filters, assuming prune_build not in docker-py client = _run( system=True, - label='foo,bar=baz', - side_effect=[None, None, None, SaltInvocationError(), - None, None, None] + label="foo,bar=baz", + side_effect=[None, None, None, SaltInvocationError(), None, None, None], ) self.assertEqual( client.call_args_list, [ - call('prune_containers', filters={'label': ['foo', 'bar=baz']}), - call('prune_images', filters={'label': ['foo', 'bar=baz']}), - call('prune_networks', filters={'label': ['foo', 'bar=baz']}), - call('prune_build', filters={'label': ['foo', 'bar=baz']}), - call('_url', '/build/prune'), - call('_post', None), - call('_result', None, True), - ] + call("prune_containers", filters={"label": ["foo", "bar=baz"]}), + call("prune_images", filters={"label": ["foo", "bar=baz"]}), + call("prune_networks", filters={"label": ["foo", "bar=baz"]}), + call("prune_build", filters={"label": ["foo", "bar=baz"]}), + call("_url", "/build/prune"), + call("_post", None), + call("_result", None, True), + ], ) # System and volumes, no filters, assuming prune_build in docker-py @@ -1146,167 +1249,173 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual( client.call_args_list, [ - call('prune_containers', filters={}), - call('prune_images', filters={}), - call('prune_networks', filters={}), - call('prune_build', filters={}), - call('prune_volumes', filters={}), - ] + call("prune_containers", filters={}), + call("prune_images", filters={}), + call("prune_networks", filters={}), + call("prune_build", filters={}), + call("prune_volumes", filters={}), + ], ) # System and volumes, no filters, assuming prune_build not in docker-py client = _run( system=True, volumes=True, - side_effect=[None, None, None, SaltInvocationError(), - None, None, None, None] + side_effect=[ + None, + None, + None, + SaltInvocationError(), + None, + None, + None, + None, + ], ) self.assertEqual( client.call_args_list, [ - call('prune_containers', filters={}), - call('prune_images', filters={}), - call('prune_networks', filters={}), - call('prune_build', filters={}), - call('_url', '/build/prune'), - call('_post', None), - call('_result', None, True), - call('prune_volumes', filters={}), - ] + call("prune_containers", filters={}), + call("prune_images", filters={}), + call("prune_networks", filters={}), + call("prune_build", filters={}), + call("_url", "/build/prune"), + call("_post", None), + call("_result", None, True), + call("prune_volumes", filters={}), + ], ) # System and volumes with filters, assuming prune_build in docker-py - client = _run(system=True, volumes=True, label='foo,bar=baz') + client = _run(system=True, volumes=True, label="foo,bar=baz") self.assertEqual( client.call_args_list, [ - call('prune_containers', filters={'label': ['foo', 'bar=baz']}), - call('prune_images', filters={'label': ['foo', 'bar=baz']}), - call('prune_networks', filters={'label': ['foo', 'bar=baz']}), - call('prune_build', filters={'label': ['foo', 'bar=baz']}), - call('prune_volumes', filters={'label': ['foo', 'bar=baz']}), - ] + call("prune_containers", filters={"label": ["foo", "bar=baz"]}), + call("prune_images", filters={"label": ["foo", "bar=baz"]}), + call("prune_networks", filters={"label": ["foo", "bar=baz"]}), + call("prune_build", filters={"label": ["foo", "bar=baz"]}), + call("prune_volumes", filters={"label": ["foo", "bar=baz"]}), + ], ) # System and volumes, with filters, assuming prune_build not in docker-py client = _run( system=True, volumes=True, - label='foo,bar=baz', - side_effect=[None, None, None, SaltInvocationError(), - None, None, None, None] + label="foo,bar=baz", + side_effect=[ + None, + None, + None, + SaltInvocationError(), + None, + None, + None, + None, + ], ) self.assertEqual( client.call_args_list, [ - call('prune_containers', filters={'label': ['foo', 'bar=baz']}), - call('prune_images', filters={'label': ['foo', 'bar=baz']}), - call('prune_networks', filters={'label': ['foo', 'bar=baz']}), - call('prune_build', filters={'label': ['foo', 'bar=baz']}), - call('_url', '/build/prune'), - call('_post', None), - call('_result', None, True), - call('prune_volumes', filters={'label': ['foo', 'bar=baz']}), - ] + call("prune_containers", filters={"label": ["foo", "bar=baz"]}), + call("prune_images", filters={"label": ["foo", "bar=baz"]}), + call("prune_networks", filters={"label": ["foo", "bar=baz"]}), + call("prune_build", filters={"label": ["foo", "bar=baz"]}), + call("_url", "/build/prune"), + call("_post", None), + call("_result", None, True), + call("prune_volumes", filters={"label": ["foo", "bar=baz"]}), + ], ) def test_port(self): - ''' + """ Test docker.port function. Note that this test case does not test what happens when a specific container name is passed and that container does not exist. When that happens, the Docker API will just raise a 404 error. Since we're using as side_effect to mock docker.inspect_container, it would be meaningless to code raising an exception into it and then test that we raised that exception. - ''' + """ ports = { - 'foo': { - '5555/tcp': [ - {'HostIp': '0.0.0.0', 'HostPort': '32768'} - ], - '6666/tcp': [ - {'HostIp': '0.0.0.0', 'HostPort': '32769'} - ], + "foo": { + "5555/tcp": [{"HostIp": "0.0.0.0", "HostPort": "32768"}], + "6666/tcp": [{"HostIp": "0.0.0.0", "HostPort": "32769"}], }, - 'bar': { - '4444/udp': [ - {'HostIp': '0.0.0.0', 'HostPort': '32767'} - ], - '5555/tcp': [ - {'HostIp': '0.0.0.0', 'HostPort': '32768'} - ], - '6666/tcp': [ - {'HostIp': '0.0.0.0', 'HostPort': '32769'} - ], + "bar": { + "4444/udp": [{"HostIp": "0.0.0.0", "HostPort": "32767"}], + "5555/tcp": [{"HostIp": "0.0.0.0", "HostPort": "32768"}], + "6666/tcp": [{"HostIp": "0.0.0.0", "HostPort": "32769"}], }, - 'baz': { - '5555/tcp': [ - {'HostIp': '0.0.0.0', 'HostPort': '32768'} - ], - '6666/udp': [ - {'HostIp': '0.0.0.0', 'HostPort': '32769'} - ], + "baz": { + "5555/tcp": [{"HostIp": "0.0.0.0", "HostPort": "32768"}], + "6666/udp": [{"HostIp": "0.0.0.0", "HostPort": "32769"}], }, } - list_mock = MagicMock(return_value=['bar', 'baz', 'foo']) + list_mock = MagicMock(return_value=["bar", "baz", "foo"]) inspect_mock = MagicMock( - side_effect=lambda x: {'NetworkSettings': {'Ports': ports.get(x)}} + side_effect=lambda x: {"NetworkSettings": {"Ports": ports.get(x)}} ) - with patch.object(docker_mod, 'list_containers', list_mock), \ - patch.object(docker_mod, 'inspect_container', inspect_mock): + with patch.object(docker_mod, "list_containers", list_mock), patch.object( + docker_mod, "inspect_container", inspect_mock + ): # Test with specific container name - ret = docker_mod.port('foo') - self.assertEqual(ret, ports['foo']) + ret = docker_mod.port("foo") + self.assertEqual(ret, ports["foo"]) # Test with specific container name and filtering on port - ret = docker_mod.port('foo', private_port='5555/tcp') - self.assertEqual(ret, {'5555/tcp': ports['foo']['5555/tcp']}) + ret = docker_mod.port("foo", private_port="5555/tcp") + self.assertEqual(ret, {"5555/tcp": ports["foo"]["5555/tcp"]}) # Test using pattern expression - ret = docker_mod.port('ba*') - self.assertEqual(ret, {'bar': ports['bar'], 'baz': ports['baz']}) - ret = docker_mod.port('ba?') - self.assertEqual(ret, {'bar': ports['bar'], 'baz': ports['baz']}) - ret = docker_mod.port('ba[rz]') - self.assertEqual(ret, {'bar': ports['bar'], 'baz': ports['baz']}) + ret = docker_mod.port("ba*") + self.assertEqual(ret, {"bar": ports["bar"], "baz": ports["baz"]}) + ret = docker_mod.port("ba?") + self.assertEqual(ret, {"bar": ports["bar"], "baz": ports["baz"]}) + ret = docker_mod.port("ba[rz]") + self.assertEqual(ret, {"bar": ports["bar"], "baz": ports["baz"]}) # Test using pattern expression and port filtering - ret = docker_mod.port('ba*', private_port='6666/tcp') + ret = docker_mod.port("ba*", private_port="6666/tcp") self.assertEqual( - ret, - {'bar': {'6666/tcp': ports['bar']['6666/tcp']}, 'baz': {}} + ret, {"bar": {"6666/tcp": ports["bar"]["6666/tcp"]}, "baz": {}} ) - ret = docker_mod.port('ba?', private_port='6666/tcp') + ret = docker_mod.port("ba?", private_port="6666/tcp") self.assertEqual( - ret, - {'bar': {'6666/tcp': ports['bar']['6666/tcp']}, 'baz': {}} + ret, {"bar": {"6666/tcp": ports["bar"]["6666/tcp"]}, "baz": {}} ) - ret = docker_mod.port('ba[rz]', private_port='6666/tcp') + ret = docker_mod.port("ba[rz]", private_port="6666/tcp") self.assertEqual( - ret, - {'bar': {'6666/tcp': ports['bar']['6666/tcp']}, 'baz': {}} + ret, {"bar": {"6666/tcp": ports["bar"]["6666/tcp"]}, "baz": {}} ) - ret = docker_mod.port('*') + ret = docker_mod.port("*") self.assertEqual(ret, ports) - ret = docker_mod.port('*', private_port='5555/tcp') + ret = docker_mod.port("*", private_port="5555/tcp") self.assertEqual( ret, - {'foo': {'5555/tcp': ports['foo']['5555/tcp']}, - 'bar': {'5555/tcp': ports['bar']['5555/tcp']}, - 'baz': {'5555/tcp': ports['baz']['5555/tcp']}} + { + "foo": {"5555/tcp": ports["foo"]["5555/tcp"]}, + "bar": {"5555/tcp": ports["bar"]["5555/tcp"]}, + "baz": {"5555/tcp": ports["baz"]["5555/tcp"]}, + }, ) - ret = docker_mod.port('*', private_port=6666) + ret = docker_mod.port("*", private_port=6666) self.assertEqual( ret, - {'foo': {'6666/tcp': ports['foo']['6666/tcp']}, - 'bar': {'6666/tcp': ports['bar']['6666/tcp']}, - 'baz': {'6666/udp': ports['baz']['6666/udp']}} + { + "foo": {"6666/tcp": ports["foo"]["6666/tcp"]}, + "bar": {"6666/tcp": ports["bar"]["6666/tcp"]}, + "baz": {"6666/udp": ports["baz"]["6666/udp"]}, + }, ) - ret = docker_mod.port('*', private_port='6666/tcp') + ret = docker_mod.port("*", private_port="6666/tcp") self.assertEqual( ret, - {'foo': {'6666/tcp': ports['foo']['6666/tcp']}, - 'bar': {'6666/tcp': ports['bar']['6666/tcp']}, - 'baz': {}} + { + "foo": {"6666/tcp": ports["foo"]["6666/tcp"]}, + "bar": {"6666/tcp": ports["bar"]["6666/tcp"]}, + "baz": {}, + }, ) diff --git a/tests/unit/modules/test_dpkg_lowpkg.py b/tests/unit/modules/test_dpkg_lowpkg.py index 6c07a75417c..f2f7c4e5078 100644 --- a/tests/unit/modules/test_dpkg_lowpkg.py +++ b/tests/unit/modules/test_dpkg_lowpkg.py @@ -1,168 +1,175 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.dpkg_lowpkg as dpkg +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DpkgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.dpkg - ''' + """ + def setup_loader_modules(self): return {dpkg: {}} # 'unpurge' function tests: 2 def test_unpurge(self): - ''' + """ Test if it change package selection for each package specified to 'install' - ''' + """ mock = MagicMock(return_value=[]) - with patch.dict(dpkg.__salt__, {'pkg.list_pkgs': mock, - 'cmd.run': mock}): - self.assertDictEqual(dpkg.unpurge('curl'), {}) + with patch.dict(dpkg.__salt__, {"pkg.list_pkgs": mock, "cmd.run": mock}): + self.assertDictEqual(dpkg.unpurge("curl"), {}) def test_unpurge_empty_package(self): - ''' + """ Test if it change package selection for each package specified to 'install' - ''' + """ self.assertDictEqual(dpkg.unpurge(), {}) # 'list_pkgs' function tests: 1 def test_list_pkgs(self): - ''' + """ Test if it lists the packages currently installed - ''' - mock = MagicMock(return_value={'retcode': 0, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(dpkg.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(dpkg.list_pkgs('httpd'), {}) + """ + mock = MagicMock(return_value={"retcode": 0, "stderr": "", "stdout": "Salt"}) + with patch.dict(dpkg.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual(dpkg.list_pkgs("httpd"), {}) - mock = MagicMock(return_value={'retcode': 1, - 'stderr': 'error', - 'stdout': 'Salt'}) - with patch.dict(dpkg.__salt__, {'cmd.run_all': mock}): - self.assertEqual(dpkg.list_pkgs('httpd'), 'Error: error') + mock = MagicMock( + return_value={"retcode": 1, "stderr": "error", "stdout": "Salt"} + ) + with patch.dict(dpkg.__salt__, {"cmd.run_all": mock}): + self.assertEqual(dpkg.list_pkgs("httpd"), "Error: error") # 'file_list' function tests: 1 def test_file_list(self): - ''' + """ Test if it lists the files that belong to a package. - ''' - mock = MagicMock(return_value={'retcode': 0, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(dpkg.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(dpkg.file_list('httpd'), - {'errors': [], 'files': []}) + """ + mock = MagicMock(return_value={"retcode": 0, "stderr": "", "stdout": "Salt"}) + with patch.dict(dpkg.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual(dpkg.file_list("httpd"), {"errors": [], "files": []}) - mock = MagicMock(return_value={'retcode': 1, - 'stderr': 'error', - 'stdout': 'Salt'}) - with patch.dict(dpkg.__salt__, {'cmd.run_all': mock}): - self.assertEqual(dpkg.file_list('httpd'), 'Error: error') + mock = MagicMock( + return_value={"retcode": 1, "stderr": "error", "stdout": "Salt"} + ) + with patch.dict(dpkg.__salt__, {"cmd.run_all": mock}): + self.assertEqual(dpkg.file_list("httpd"), "Error: error") # 'file_dict' function tests: 1 def test_file_dict(self): - ''' + """ Test if it lists the files that belong to a package, grouped by package - ''' - mock = MagicMock(return_value={'retcode': 0, - 'stderr': '', - 'stdout': 'Salt'}) - with patch.dict(dpkg.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(dpkg.file_dict('httpd'), - {'errors': [], 'packages': {}}) + """ + mock = MagicMock(return_value={"retcode": 0, "stderr": "", "stdout": "Salt"}) + with patch.dict(dpkg.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual( + dpkg.file_dict("httpd"), {"errors": [], "packages": {}} + ) - mock = MagicMock(return_value={'retcode': 1, - 'stderr': 'error', - 'stdout': 'Salt'}) - with patch.dict(dpkg.__salt__, {'cmd.run_all': mock}): - self.assertEqual(dpkg.file_dict('httpd'), 'Error: error') + mock = MagicMock( + return_value={"retcode": 1, "stderr": "error", "stdout": "Salt"} + ) + with patch.dict(dpkg.__salt__, {"cmd.run_all": mock}): + self.assertEqual(dpkg.file_dict("httpd"), "Error: error") def test_info(self): - ''' + """ Test package info - ''' - mock = MagicMock(return_value={'retcode': 0, - 'stderr': '', - 'stdout': - os.linesep.join([ - 'package:bash', - 'revision:', - 'architecture:amd64', - 'maintainer:Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>', - 'summary:', - 'source:bash', - 'version:4.4.18-2ubuntu1', - 'section:shells', - 'installed_size:1588', - 'size:', - 'MD5:', - 'SHA1:', - 'SHA256:', - 'origin:', - 'homepage:http://tiswww.case.edu/php/chet/bash/bashtop.html', - 'status:ii ', - '======', - 'description:GNU Bourne Again SHell', - ' Bash is an sh-compatible command language interpreter that executes', - ' commands read from the standard input or from a file. Bash also', - ' incorporates useful features from the Korn and C shells (ksh and csh).', - ' .', - ' Bash is ultimately intended to be a conformant implementation of the', - ' IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).', - ' .', - ' The Programmable Completion Code, by Ian Macdonald, is now found in', - ' the bash-completion package.', - '------' - ])}) + """ + mock = MagicMock( + return_value={ + "retcode": 0, + "stderr": "", + "stdout": os.linesep.join( + [ + "package:bash", + "revision:", + "architecture:amd64", + "maintainer:Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>", + "summary:", + "source:bash", + "version:4.4.18-2ubuntu1", + "section:shells", + "installed_size:1588", + "size:", + "MD5:", + "SHA1:", + "SHA256:", + "origin:", + "homepage:http://tiswww.case.edu/php/chet/bash/bashtop.html", + "status:ii ", + "======", + "description:GNU Bourne Again SHell", + " Bash is an sh-compatible command language interpreter that executes", + " commands read from the standard input or from a file. Bash also", + " incorporates useful features from the Korn and C shells (ksh and csh).", + " .", + " Bash is ultimately intended to be a conformant implementation of the", + " IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).", + " .", + " The Programmable Completion Code, by Ian Macdonald, is now found in", + " the bash-completion package.", + "------", + ] + ), + } + ) - with patch.dict(dpkg.__salt__, {'cmd.run_all': mock}), \ - patch.dict(dpkg.__grains__, {'os': 'Ubuntu', 'osrelease_info': (18, 4)}), \ - patch('salt.utils.path.which', MagicMock(return_value=False)), \ - patch('os.path.exists', MagicMock(return_value=False)),\ - patch('os.path.getmtime', MagicMock(return_value=1560199259.0)): - self.assertDictEqual(dpkg.info('bash'), - {'bash': {'architecture': 'amd64', - 'description': os.linesep.join([ - 'GNU Bourne Again SHell', - ' Bash is an sh-compatible command language interpreter that executes', - ' commands read from the standard input or from a file. Bash also', - ' incorporates useful features from the Korn and C shells (ksh and csh).', - ' .', - ' Bash is ultimately intended to be a conformant implementation of the', - ' IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).', - ' .', - ' The Programmable Completion Code, by Ian Macdonald, is now found in', - ' the bash-completion package.' + os.linesep - ]), - 'homepage': 'http://tiswww.case.edu/php/chet/bash/bashtop.html', - 'maintainer': 'Ubuntu Developers ' - '<ubuntu-devel-discuss@lists.ubuntu.com>', - 'package': 'bash', - 'section': 'shells', - 'source': 'bash', - 'status': 'ii', - 'version': '4.4.18-2ubuntu1'}}) + with patch.dict(dpkg.__salt__, {"cmd.run_all": mock}), patch.dict( + dpkg.__grains__, {"os": "Ubuntu", "osrelease_info": (18, 4)} + ), patch("salt.utils.path.which", MagicMock(return_value=False)), patch( + "os.path.exists", MagicMock(return_value=False) + ), patch( + "os.path.getmtime", MagicMock(return_value=1560199259.0) + ): + self.assertDictEqual( + dpkg.info("bash"), + { + "bash": { + "architecture": "amd64", + "description": os.linesep.join( + [ + "GNU Bourne Again SHell", + " Bash is an sh-compatible command language interpreter that executes", + " commands read from the standard input or from a file. Bash also", + " incorporates useful features from the Korn and C shells (ksh and csh).", + " .", + " Bash is ultimately intended to be a conformant implementation of the", + " IEEE POSIX Shell and Tools specification (IEEE Working Group 1003.2).", + " .", + " The Programmable Completion Code, by Ian Macdonald, is now found in", + " the bash-completion package." + os.linesep, + ] + ), + "homepage": "http://tiswww.case.edu/php/chet/bash/bashtop.html", + "maintainer": "Ubuntu Developers " + "<ubuntu-devel-discuss@lists.ubuntu.com>", + "package": "bash", + "section": "shells", + "source": "bash", + "status": "ii", + "version": "4.4.18-2ubuntu1", + } + }, + ) diff --git a/tests/unit/modules/test_drac.py b/tests/unit/modules/test_drac.py index a1858ac57b4..80e793e47e0 100644 --- a/tests/unit/modules/test_drac.py +++ b/tests/unit/modules/test_drac.py @@ -1,258 +1,274 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.drac as drac +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DracTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.drac - ''' + """ + def setup_loader_modules(self): return {drac: {}} def test_system_info(self): - ''' + """ Tests to return System information - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': None}) - with patch.dict(drac.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value='ABC') - with patch.object(drac, '__parse_drac', mock): - self.assertEqual(drac.system_info(), 'ABC') + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": None}) + with patch.dict(drac.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(return_value="ABC") + with patch.object(drac, "__parse_drac", mock): + self.assertEqual(drac.system_info(), "ABC") def test_network_info(self): - ''' + """ Tests to return Network Configuration - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': None}) - with patch.dict(drac.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(return_value='ABC') - with patch.object(drac, '__parse_drac', mock): - self.assertEqual(drac.network_info(), 'ABC') + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": None}) + with patch.dict(drac.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(return_value="ABC") + with patch.object(drac, "__parse_drac", mock): + self.assertEqual(drac.network_info(), "ABC") def test_nameservers(self): - ''' + """ tests for configure the nameservers on the DRAC - ''' - self.assertFalse(drac.nameservers('a', 'b', 'c')) + """ + self.assertFalse(drac.nameservers("a", "b", "c")) mock = MagicMock(return_value=False) - with patch.object(drac, '__execute_cmd', mock): - self.assertFalse(drac.nameservers('a')) + with patch.object(drac, "__execute_cmd", mock): + self.assertFalse(drac.nameservers("a")) mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): - self.assertTrue(drac.nameservers('a')) + with patch.object(drac, "__execute_cmd", mock): + self.assertTrue(drac.nameservers("a")) def test_syslog(self): - ''' + """ Tests for configure syslog remote logging, by default syslog will automatically be enabled if a server is specified. However, if you want to disable syslog you will need to specify a server followed by False - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): - self.assertTrue(drac.syslog('server')) + with patch.object(drac, "__execute_cmd", mock): + self.assertTrue(drac.syslog("server")) mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): - self.assertTrue(drac.syslog('server', False)) + with patch.object(drac, "__execute_cmd", mock): + self.assertTrue(drac.syslog("server", False)) def test_email_alerts(self): - ''' + """ Test to Enable/Disable email alerts - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): self.assertTrue(drac.email_alerts(True)) mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): self.assertTrue(drac.email_alerts(False)) def test_list_users(self): - ''' + """ Test for list all DRAC users - ''' - mock = MagicMock(return_value={'retcode': 0, - 'stdout': 'cfgUserAdminUserName=value'}) - with patch.dict(drac.__salt__, {'cmd.run_all': mock}): - self.assertEqual(drac.list_users(), {'value': {'index': 16}}) + """ + mock = MagicMock( + return_value={"retcode": 0, "stdout": "cfgUserAdminUserName=value"} + ) + with patch.dict(drac.__salt__, {"cmd.run_all": mock}): + self.assertEqual(drac.list_users(), {"value": {"index": 16}}) def test_delete_user(self): - ''' + """ Tests to delete a user - ''' - mock = MagicMock(return_value='ABC') - with patch.object(drac, '__execute_cmd', mock): - self.assertEqual(drac.delete_user('username', 1), 'ABC') + """ + mock = MagicMock(return_value="ABC") + with patch.object(drac, "__execute_cmd", mock): + self.assertEqual(drac.delete_user("username", 1), "ABC") - self.assertFalse(drac.delete_user('username', False)) + self.assertFalse(drac.delete_user("username", False)) def test_change_password(self): - ''' + """ Tests to change users password - ''' - mock = MagicMock(return_value='ABC') - with patch.object(drac, '__execute_cmd', mock): - self.assertEqual(drac.change_password('username', - 'password', 1), 'ABC') + """ + mock = MagicMock(return_value="ABC") + with patch.object(drac, "__execute_cmd", mock): + self.assertEqual(drac.change_password("username", "password", 1), "ABC") - self.assertFalse(drac.change_password('username', - 'password', False), False) + self.assertFalse(drac.change_password("username", "password", False), False) def test_create_user(self): - ''' + """ Tests to create user accounts - ''' - self.assertFalse(drac.create_user('username', 'password', - 'permissions', {'username': None})) + """ + self.assertFalse( + drac.create_user("username", "password", "permissions", {"username": None}) + ) mock = MagicMock(return_value=False) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): mock = MagicMock(return_value=None) - with patch.object(drac, 'delete_user', mock): - self.assertFalse(drac.create_user('username', 'password', - 'permissions', - {'username1': {'index': 1}})) + with patch.object(drac, "delete_user", mock): + self.assertFalse( + drac.create_user( + "username", + "password", + "permissions", + {"username1": {"index": 1}}, + ) + ) mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): mock = MagicMock(return_value=False) - with patch.object(drac, 'set_permissions', mock): + with patch.object(drac, "set_permissions", mock): mock = MagicMock(return_value=None) - with patch.object(drac, 'delete_user', mock): - self.assertFalse(drac.create_user('username', 'password', - 'permissions', - {'username1': - {'index': 1}})) + with patch.object(drac, "delete_user", mock): + self.assertFalse( + drac.create_user( + "username", + "password", + "permissions", + {"username1": {"index": 1}}, + ) + ) mock = MagicMock(return_value=True) - with patch.object(drac, 'set_permissions', mock): + with patch.object(drac, "set_permissions", mock): mock = MagicMock(return_value=False) - with patch.object(drac, 'change_password', mock): + with patch.object(drac, "change_password", mock): mock = MagicMock(return_value=None) - with patch.object(drac, 'delete_user', mock): - self.assertFalse(drac.create_user('username', - 'password', - 'permissions', - {'username1': - {'index': 1}})) + with patch.object(drac, "delete_user", mock): + self.assertFalse( + drac.create_user( + "username", + "password", + "permissions", + {"username1": {"index": 1}}, + ) + ) mock = MagicMock(side_effect=[True, False]) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): mock = MagicMock(return_value=True) - with patch.object(drac, 'set_permissions', mock): + with patch.object(drac, "set_permissions", mock): mock = MagicMock(return_value=True) - with patch.object(drac, 'change_password', mock): + with patch.object(drac, "change_password", mock): mock = MagicMock(return_value=None) - with patch.object(drac, 'delete_user', mock): - self.assertFalse(drac.create_user('username', - 'password', - 'permissions', - {'username1': - {'index': 1}})) + with patch.object(drac, "delete_user", mock): + self.assertFalse( + drac.create_user( + "username", + "password", + "permissions", + {"username1": {"index": 1}}, + ) + ) mock = MagicMock(side_effect=[True, True]) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): mock = MagicMock(return_value=True) - with patch.object(drac, 'set_permissions', mock): + with patch.object(drac, "set_permissions", mock): mock = MagicMock(return_value=True) - with patch.object(drac, 'change_password', mock): + with patch.object(drac, "change_password", mock): mock = MagicMock(return_value=None) - with patch.object(drac, 'delete_user', mock): - self.assertTrue(drac.create_user('username', - 'password', - 'permissions', - {'username1': - {'index': 1}})) + with patch.object(drac, "delete_user", mock): + self.assertTrue( + drac.create_user( + "username", + "password", + "permissions", + {"username1": {"index": 1}}, + ) + ) def test_set_permissions(self): - ''' + """ Test to configure users permissions - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): - self.assertTrue(drac.set_permissions('username', 'A,B,C', 1)) + with patch.object(drac, "__execute_cmd", mock): + self.assertTrue(drac.set_permissions("username", "A,B,C", 1)) def test_set_snmp(self): - ''' + """ Test to configure SNMP community string - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): - self.assertTrue(drac.set_snmp('username')) + with patch.object(drac, "__execute_cmd", mock): + self.assertTrue(drac.set_snmp("username")) def test_set_network(self): - ''' + """ Test to configure Network - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): - self.assertTrue(drac.set_network('ip', 'netmask', 'gateway')) + with patch.object(drac, "__execute_cmd", mock): + self.assertTrue(drac.set_network("ip", "netmask", "gateway")) def test_server_reboot(self): - ''' + """ Tests for issues a power-cycle operation on the managed server. This action is similar to pressing the power button on the system's front panel to power down and then power up the system. - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): self.assertTrue(drac.server_reboot()) def test_server_poweroff(self): - ''' + """ Tests for powers down the managed server. - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): self.assertTrue(drac.server_poweroff()) def test_server_poweron(self): - ''' + """ Tests for powers up the managed server. - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): self.assertTrue(drac.server_poweron()) def test_server_hardreset(self): - ''' + """ Tests for performs a reset (reboot) operation on the managed server. - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): self.assertTrue(drac.server_hardreset()) def test_server_pxe(self): - ''' + """ Tests to configure server to PXE perform a one off PXE boot - ''' + """ mock = MagicMock(return_value=True) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): self.assertTrue(drac.server_pxe()) mock = MagicMock(side_effect=[True, False]) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): self.assertFalse(drac.server_pxe()) mock = MagicMock(return_value=False) - with patch.object(drac, '__execute_cmd', mock): + with patch.object(drac, "__execute_cmd", mock): self.assertFalse(drac.server_pxe()) diff --git a/tests/unit/modules/test_drbd.py b/tests/unit/modules/test_drbd.py index e81323d4cf8..189d5a2289b 100644 --- a/tests/unit/modules/test_drbd.py +++ b/tests/unit/modules/test_drbd.py @@ -1,64 +1,70 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.drbd as drbd +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DrbdTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.drbd - ''' + """ + def setup_loader_modules(self): return {drbd: {}} # 'overview' function tests: 1 def test_overview(self): - ''' + """ Test if it shows status of the DRBD devices - ''' - ret = {'connection state': 'True', - 'device': 'Stack', - 'fs': 'None', - 'local disk state': 'UpToDate', - 'local role': 'master', - 'minor number': 'Salt', - 'mountpoint': 'True', - 'partner disk state': 'UpToDate', - 'partner role': 'minion', - 'percent': '888', - 'remains': '666', - 'total size': '50', - 'used': '50'} - mock = MagicMock(return_value='Salt:Stack True master/minion \ - UpToDate/UpToDate True None 50 50 666 888') - with patch.dict(drbd.__salt__, {'cmd.run': mock}): + """ + ret = { + "connection state": "True", + "device": "Stack", + "fs": "None", + "local disk state": "UpToDate", + "local role": "master", + "minor number": "Salt", + "mountpoint": "True", + "partner disk state": "UpToDate", + "partner role": "minion", + "percent": "888", + "remains": "666", + "total size": "50", + "used": "50", + } + mock = MagicMock( + return_value="Salt:Stack True master/minion \ + UpToDate/UpToDate True None 50 50 666 888" + ) + with patch.dict(drbd.__salt__, {"cmd.run": mock}): self.assertDictEqual(drbd.overview(), ret) - ret = {'connection state': 'True', - 'device': 'Stack', - 'local disk state': 'UpToDate', - 'local role': 'master', - 'minor number': 'Salt', - 'partner disk state': 'partner', - 'partner role': 'minion', - 'synched': '5050', - 'synchronisation: ': 'syncbar'} - mock = MagicMock(return_value='Salt:Stack True master/minion \ - UpToDate/partner syncbar None 50 50') - with patch.dict(drbd.__salt__, {'cmd.run': mock}): + ret = { + "connection state": "True", + "device": "Stack", + "local disk state": "UpToDate", + "local role": "master", + "minor number": "Salt", + "partner disk state": "partner", + "partner role": "minion", + "synched": "5050", + "synchronisation: ": "syncbar", + } + mock = MagicMock( + return_value="Salt:Stack True master/minion \ + UpToDate/partner syncbar None 50 50" + ) + with patch.dict(drbd.__salt__, {"cmd.run": mock}): self.assertDictEqual(drbd.overview(), ret) diff --git a/tests/unit/modules/test_elasticsearch.py b/tests/unit/modules/test_elasticsearch.py index a98ac104a5f..0aaa77d4eae 100644 --- a/tests/unit/modules/test_elasticsearch.py +++ b/tests/unit/modules/test_elasticsearch.py @@ -1,21 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Lukas Raska <lukas@raska.me> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import Salt Libs from salt.modules import elasticsearch -from salt.exceptions import CommandExecutionError, SaltInvocationError +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf # Import elasticsearch exceptions NO_ELASTIC = False @@ -25,11 +23,12 @@ except Exception: # pylint: disable=broad-except NO_ELASTIC = True -@skipIf(NO_ELASTIC, 'Install elasticsearch-py before running Elasticsearch unit tests.') +@skipIf(NO_ELASTIC, "Install elasticsearch-py before running Elasticsearch unit tests.") class ElasticsearchTestCase(TestCase): - ''' + """ Test cases for salt.modules.elasticsearch - ''' + """ + @staticmethod def es_return_true(hosts=None, profile=None): return True @@ -41,978 +40,1170 @@ class ElasticsearchTestCase(TestCase): # 'ping' function tests: 2 def test_ping(self): - ''' + """ Test if ping succeeds - ''' - with patch.object(elasticsearch, '_get_instance', self.es_return_true): + """ + with patch.object(elasticsearch, "_get_instance", self.es_return_true): self.assertTrue(elasticsearch.ping()) def test_ping_failure(self): - ''' + """ Test if ping fails - ''' - with patch.object(elasticsearch, '_get_instance', self.es_raise_command_execution_error): + """ + with patch.object( + elasticsearch, "_get_instance", self.es_raise_command_execution_error + ): self.assertFalse(elasticsearch.ping()) # 'info' function tests: 2 def test_info(self): - ''' + """ Test if status fetch succeeds - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def info(self): - ''' + """ Mock of info method - ''' + """ return [{"test": "key"}] - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertListEqual(elasticsearch.info(), [{"test": "key"}]) def test_info_failure(self): - ''' + """ Test if status fetch fails with CommandExecutionError - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def info(self): - ''' + """ Mock of info method - ''' + """ raise TransportError("custom error", 123) - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertRaises(CommandExecutionError, elasticsearch.info) # 'node_info' function tests: 2 def test_node_info(self): - ''' + """ Test if node status fetch succeeds - ''' + """ + class MockElasticNodes(object): - ''' + """ Mock of Elasticsearch NodesClient - ''' + """ + def info(self, node_id=None, flat_settings=None): - ''' + """ Mock of info method - ''' + """ return [{"test": "key"}] class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + nodes = MockElasticNodes() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertListEqual(elasticsearch.node_info(), [{"test": "key"}]) def test_node_info_failure(self): - ''' + """ Test if node status fetch fails with CommandExecutionError - ''' + """ + class MockElasticNodes(object): - ''' + """ Mock of Elasticsearch NodesClient - ''' + """ + def info(self, node_id=None, flat_settings=None): - ''' + """ Mock of info method - ''' + """ raise TransportError("custom error", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + nodes = MockElasticNodes() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertRaises(CommandExecutionError, elasticsearch.node_info) # 'cluster_health' function tests: 2 def test_cluster_health(self): - ''' + """ Test if cluster status health fetch succeeds - ''' + """ + class MockElasticCluster(object): - ''' + """ Mock of Elasticsearch ClusterClient - ''' + """ + def health(self, index=None, level=None, local=None): - ''' + """ Mock of health method - ''' + """ return [{"test": "key"}] class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + cluster = MockElasticCluster() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertListEqual(elasticsearch.cluster_health(), [{"test": "key"}]) def test_cluster_health_failure(self): - ''' + """ Test if cluster status health fetch fails with CommandExecutionError - ''' + """ + class MockElasticCluster(object): - ''' + """ Mock of Elasticsearch ClusterClient - ''' + """ + def health(self, index=None, level=None, local=None): - ''' + """ Mock of health method - ''' + """ raise TransportError("custom error", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + cluster = MockElasticCluster() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertRaises(CommandExecutionError, elasticsearch.cluster_health) # 'cluster_stats' function tests: 2 def test_cluster_stats(self): - ''' + """ Test if cluster stats fetch succeeds - ''' + """ + class MockElasticCluster(object): - ''' + """ Mock of Elasticsearch ClusterClient - ''' + """ + def stats(self, node_id=None): - ''' + """ Mock of health method - ''' + """ return [{"test": "key"}] class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + cluster = MockElasticCluster() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertListEqual(elasticsearch.cluster_stats(), [{"test": "key"}]) def test_cluster_stats_failure(self): - ''' + """ Test if cluster stats fetch fails with CommandExecutionError - ''' + """ + class MockElasticCluster(object): - ''' + """ Mock of Elasticsearch ClusterClient - ''' + """ + def stats(self, node_id=None): - ''' + """ Mock of health method - ''' + """ raise TransportError("custom error", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + cluster = MockElasticCluster() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertRaises(CommandExecutionError, elasticsearch.cluster_stats) # 'alias_create' function tests: 3 def test_alias_create(self): - ''' + """ Test if alias is created - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_alias(self, index=None, name=None, body=None): - ''' + """ Mock of put_alias method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.alias_create("foo", "bar", body="baz")) def test_alias_create_unack(self): - ''' + """ Test if alias creation is not acked - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_alias(self, index=None, name=None, body=None): - ''' + """ Mock of put_alias method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.alias_create("foo", "bar", body="baz")) def test_alias_create_failure(self): - ''' + """ Test if alias creation fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_alias(self, index=None, name=None, body=None): - ''' + """ Mock of put_alias method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.alias_create, "foo", "bar", body="baz") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, + elasticsearch.alias_create, + "foo", + "bar", + body="baz", + ) # 'alias_delete' function tests: 3 def test_alias_delete(self): - ''' + """ Test if alias is deleted - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_alias(self, index=None, name=None): - ''' + """ Mock of delete_alias method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.alias_delete("foo", "bar")) def test_alias_delete_unack(self): - ''' + """ Test if alias deletion is not acked - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_alias(self, index=None, name=None): - ''' + """ Mock of delete_alias method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.alias_delete("foo", "bar")) def test_alias_delete_failure(self): - ''' + """ Test if alias deletion fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_alias(self, index=None, name=None): - ''' + """ Mock of delete_alias method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.alias_delete, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.alias_delete, "foo", "bar" + ) # 'alias_exists' function tests: 3 def test_alias_exists(self): - ''' + """ Test if alias exists - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def exists_alias(self, index=None, name=None): - ''' + """ Mock of exists_alias method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.alias_exists("foo", "bar")) def test_alias_exists_not(self): - ''' + """ Test if alias doesn't exist - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def exists_alias(self, index=None, name=None): - ''' + """ Mock of exists_alias method - ''' + """ raise NotFoundError class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.alias_exists("foo", "bar")) def test_alias_exists_failure(self): - ''' + """ Test if alias status obtain fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def exists_alias(self, index=None, name=None): - ''' + """ Mock of exists_alias method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.alias_exists, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.alias_exists, "foo", "bar" + ) # 'alias_get' function tests: 3 def test_alias_get(self): - ''' + """ Test if alias can be obtained - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_alias(self, index=None, name=None): - ''' + """ Mock of get_alias method - ''' + """ return {"test": "key"} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertDictEqual(elasticsearch.alias_get("foo", "bar"), {"test": "key"}) def test_alias_get_not(self): - ''' + """ Test if alias doesn't exist - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_alias(self, index=None, name=None): - ''' + """ Mock of get_alias method - ''' + """ raise NotFoundError class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertIs(elasticsearch.alias_get("foo", "bar"), None) def test_alias_get_failure(self): - ''' + """ Test if alias obtain fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_alias(self, index=None, name=None): - ''' + """ Mock of get_alias method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.alias_get, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.alias_get, "foo", "bar" + ) # 'document_create' function tests: 2 def test_document_create(self): - ''' + """ Test if document can be created - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def index(self, index=None, doc_type=None, body=None, id=None): - ''' + """ Mock of index method - ''' + """ return {"test": "key"} - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertDictEqual(elasticsearch.document_create("foo", "bar"), {"test": "key"}) + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertDictEqual( + elasticsearch.document_create("foo", "bar"), {"test": "key"} + ) def test_document_create_failure(self): - ''' + """ Test if document creation fails with CommandExecutionError - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def index(self, index=None, doc_type=None, body=None, id=None): - ''' + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.document_create, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.document_create, "foo", "bar" + ) # 'document_delete' function tests: 2 def test_document_delete(self): - ''' + """ Test if document can be deleted - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def delete(self, index=None, doc_type=None, id=None): - ''' + """ Mock of index method - ''' + """ return {"test": "key"} - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertDictEqual(elasticsearch.document_delete("foo", "bar", "baz"), {"test": "key"}) + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertDictEqual( + elasticsearch.document_delete("foo", "bar", "baz"), {"test": "key"} + ) def test_document_delete_failure(self): - ''' + """ Test if document deletion fails with CommandExecutionError - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def delete(self, index=None, doc_type=None, id=None): - ''' + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.document_delete, "foo", "bar", "baz") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, + elasticsearch.document_delete, + "foo", + "bar", + "baz", + ) # 'document_exists' function tests: 3 def test_document_exists(self): - ''' + """ Test if document status can be obtained - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def exists(self, index=None, doc_type=None, id=None): - ''' + """ Mock of index method - ''' + """ return True - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.document_exists("foo", "bar")) def test_document_exists_not(self): - ''' + """ Test if document doesn't exist - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def exists(self, index=None, doc_type=None, id=None): - ''' + """ Mock of index method - ''' + """ return False - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.document_exists("foo", "bar")) def test_document_exists_failure(self): - ''' + """ Test if document exist state fails with CommandExecutionError - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def exists(self, index=None, doc_type=None, id=None): - ''' + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.document_exists, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.document_exists, "foo", "bar" + ) # 'document_get' function tests: 3 def test_document_get(self): - ''' + """ Test if document exists - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def get(self, index=None, doc_type=None, id=None): - ''' + """ Mock of index method - ''' + """ return {"test": "key"} - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertDictEqual(elasticsearch.document_get("foo", "bar"), {"test": "key"}) + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertDictEqual( + elasticsearch.document_get("foo", "bar"), {"test": "key"} + ) def test_document_get_not(self): - ''' + """ Test if document doesn't exit - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def get(self, index=None, doc_type=None, id=None): - ''' + """ Mock of index method - ''' + """ raise NotFoundError - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertIs(elasticsearch.document_get("foo", "bar"), None) def test_document_get_failure(self): - ''' + """ Test if document obtain fails with CommandExecutionError - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def get(self, index=None, doc_type=None, id=None): - ''' + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.document_get, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.document_get, "foo", "bar" + ) # 'index_create' function tests: 5 def test_index_create(self): - ''' + """ Test if index can be created - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def create(self, index=None, body=None): - ''' + """ Mock of index method - ''' + """ return {"acknowledged": True, "shards_acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.index_create("foo", "bar")) def test_index_create_no_shards(self): - ''' + """ Test if index is created and no shards info is returned - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def create(self, index=None, body=None): - ''' + """ Mock of index method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.index_create("foo", "bar")) def test_index_create_not_shards(self): - ''' + """ Test if index is created and shards didn't acked - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def create(self, index=None, body=None): - ''' + """ Mock of index method - ''' + """ return {"acknowledged": True, "shards_acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.index_create("foo", "bar")) def test_index_create_not(self): - ''' + """ Test if index is created and shards didn't acked - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def create(self, index=None, body=None): - ''' + """ Mock of index method - ''' + """ return {"acknowledged": False, "shards_acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.index_create("foo", "bar")) def test_index_create_failure(self): - ''' + """ Test if index creation fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def create(self, index=None, body=None): - ''' + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_create, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_create, "foo", "bar" + ) # 'index_delete' function tests: 3 def test_index_delete(self): - ''' + """ Test if index can be deleted - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete(self, index=None): - ''' + """ Mock of index method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.index_delete("foo", "bar")) def test_index_delete_not(self): - ''' + """ Test if index is deleted and shards didn't acked - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete(self, index=None): - ''' + """ Mock of index method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.index_delete("foo", "bar")) def test_index_delete_failure(self): - ''' + """ Test if index deletion fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete(self, index=None): - ''' + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_delete, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_delete, "foo", "bar" + ) # 'index_exists' function tests: 3 def test_index_exists(self): - ''' + """ Test if index exists - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def exists(self, index=None): - ''' + """ Mock of index method - ''' + """ return True class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.index_exists("foo", "bar")) def test_index_exists_not(self): - ''' + """ Test if index doesn't exist - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def exists(self, index=None): - ''' + """ Mock of index method - ''' + """ raise NotFoundError class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.index_exists("foo", "bar")) def test_index_exists_failure(self): - ''' + """ Test if alias exist state fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def exists(self, index=None): - ''' + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_exists, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_exists, "foo", "bar" + ) def test_index_get_settings(self): - ''' + """ Test if settings can be obtained from the index - ''' + """ fake_es = MagicMock() fake_es.indices = MagicMock() fake_es.indices.get_settings = MagicMock(return_value={"foo": "key"}) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): - self.assertDictEqual(elasticsearch.index_get_settings("foo", "bar"), {"foo": "key"}) + with patch.object(elasticsearch, "_get_instance", fake_instance): + self.assertDictEqual( + elasticsearch.index_get_settings("foo", "bar"), {"foo": "key"} + ) def test_index_get_settings_not_exists(self): - ''' + """ Test index_get_settings if index doesn't exist - ''' + """ fake_es = MagicMock() fake_es.indices = MagicMock() @@ -1020,13 +1211,13 @@ class ElasticsearchTestCase(TestCase): fake_es.indices.get_settings.side_effect = NotFoundError("custom error", 123) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): - self.assertIs(elasticsearch.index_get_settings(index='foo'), None) + with patch.object(elasticsearch, "_get_instance", fake_instance): + self.assertIs(elasticsearch.index_get_settings(index="foo"), None) def test_get_settings_failure(self): - ''' + """ Test if index settings get fails with CommandExecutionError - ''' + """ fake_es = MagicMock() fake_es.indices = MagicMock() @@ -1034,13 +1225,15 @@ class ElasticsearchTestCase(TestCase): fake_es.indices.get_settings.side_effect = TransportError("custom error", 123) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): - self.assertRaises(CommandExecutionError, elasticsearch.index_get_settings, index='foo') + with patch.object(elasticsearch, "_get_instance", fake_instance): + self.assertRaises( + CommandExecutionError, elasticsearch.index_get_settings, index="foo" + ) def test_index_put_settings(self): - ''' + """ Test if we can put settings for the index - ''' + """ body = {"settings": {"index": {"number_of_replicas": 2}}} fake_es = MagicMock() @@ -1048,13 +1241,13 @@ class ElasticsearchTestCase(TestCase): fake_es.indices.put_settings = MagicMock(return_value={"acknowledged": True}) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): - self.assertTrue(elasticsearch.index_put_settings(index='foo', body=body)) + with patch.object(elasticsearch, "_get_instance", fake_instance): + self.assertTrue(elasticsearch.index_put_settings(index="foo", body=body)) def test_index_put_settings_not_exists(self): - ''' + """ Test if settings put executed agains non-existinf index - ''' + """ body = {"settings": {"index": {"number_of_replicas": 2}}} fake_es = MagicMock() @@ -1063,13 +1256,15 @@ class ElasticsearchTestCase(TestCase): fake_es.indices.put_settings.side_effect = NotFoundError("custom error", 123) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): - self.assertIs(elasticsearch.index_put_settings(index='foo', body=body), None) + with patch.object(elasticsearch, "_get_instance", fake_instance): + self.assertIs( + elasticsearch.index_put_settings(index="foo", body=body), None + ) def test_index_put_settings_failure(self): - ''' + """ Test if settings put failed with CommandExecutionError - ''' + """ body = {"settings": {"index": {"number_of_replicas": 2}}} fake_es = MagicMock() @@ -1078,1282 +1273,1578 @@ class ElasticsearchTestCase(TestCase): fake_es.indices.put_settings.side_effect = TransportError("custom error", 123) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): - self.assertRaises(CommandExecutionError, elasticsearch.index_put_settings, index='foo', body=body) + with patch.object(elasticsearch, "_get_instance", fake_instance): + self.assertRaises( + CommandExecutionError, + elasticsearch.index_put_settings, + index="foo", + body=body, + ) # 'index_get' function tests: 3 def test_index_get(self): - ''' + """ Test if index can be obtained - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get(self, index=None): - ''' + """ Mock of index method - ''' + """ return {"test": "key"} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertDictEqual(elasticsearch.index_get("foo", "bar"), {"test": "key"}) def test_index_get_not(self): - ''' + """ Test if index doesn't exist - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get(self, index=None): - ''' + """ Mock of index method - ''' + """ raise NotFoundError class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertIs(elasticsearch.index_get("foo", "bar"), None) def test_index_get_failure(self): - ''' + """ Test if index obtain fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get(self, index=None): - ''' + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_get, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_get, "foo", "bar" + ) # 'index_open' function tests: 3 def test_index_open(self): - ''' + """ Test if index can be opened - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' - def open(self, index=None, allow_no_indices=None, expand_wildcards=None, ignore_unavailable=None): - ''' + """ + + def open( + self, + index=None, + allow_no_indices=None, + expand_wildcards=None, + ignore_unavailable=None, + ): + """ Mock of index method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.index_open("foo", "bar")) def test_index_open_not(self): - ''' + """ Test if index open isn't acked - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' - def open(self, index=None, allow_no_indices=None, expand_wildcards=None, ignore_unavailable=None): - ''' + """ + + def open( + self, + index=None, + allow_no_indices=None, + expand_wildcards=None, + ignore_unavailable=None, + ): + """ Mock of index method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.index_open("foo", "bar")) def test_index_open_failure(self): - ''' + """ Test if alias opening fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' - def open(self, index=None, allow_no_indices=None, expand_wildcards=None, ignore_unavailable=None): - ''' + """ + + def open( + self, + index=None, + allow_no_indices=None, + expand_wildcards=None, + ignore_unavailable=None, + ): + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_open, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_open, "foo", "bar" + ) # 'index_close' function tests: 3 def test_index_close(self): - ''' + """ Test if index can be closed - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' - def close(self, index=None, allow_no_indices=None, expand_wildcards=None, ignore_unavailable=None): - ''' + """ + + def close( + self, + index=None, + allow_no_indices=None, + expand_wildcards=None, + ignore_unavailable=None, + ): + """ Mock of index method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.index_close("foo", "bar")) def test_index_close_not(self): - ''' + """ Test if index close isn't acked - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' - def close(self, index=None, allow_no_indices=None, expand_wildcards=None, ignore_unavailable=None): - ''' + """ + + def close( + self, + index=None, + allow_no_indices=None, + expand_wildcards=None, + ignore_unavailable=None, + ): + """ Mock of index method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.index_close("foo", "bar")) def test_index_close_failure(self): - ''' + """ Test if index closing fails with CommandExecutionError - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' - def close(self, index=None, allow_no_indices=None, expand_wildcards=None, ignore_unavailable=None): - ''' + """ + + def close( + self, + index=None, + allow_no_indices=None, + expand_wildcards=None, + ignore_unavailable=None, + ): + """ Mock of index method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_close, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_close, "foo", "bar" + ) # 'mapping_create' function tests: 3 def test_mapping_create(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_mapping(self, index=None, doc_type=None, body=None): - ''' + """ Mock of put_mapping method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.mapping_create("foo", "bar", "baz")) def test_mapping_create_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_mapping(self, index=None, doc_type=None, body=None): - ''' + """ Mock of put_mapping method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.mapping_create("foo", "bar", "baz")) def test_mapping_create_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_mapping(self, index=None, doc_type=None, body=None): - ''' + """ Mock of put_mapping method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.mapping_create, "foo", "bar", "baz") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.mapping_create, "foo", "bar", "baz" + ) # 'mapping_delete' function tests: 3 def test_mapping_delete(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_mapping(self, index=None, doc_type=None): - ''' + """ Mock of put_mapping method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.mapping_delete("foo", "bar", "baz")) def test_mapping_delete_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_mapping(self, index=None, doc_type=None): - ''' + """ Mock of put_mapping method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.mapping_delete("foo", "bar", "baz")) def test_mapping_delete_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_mapping(self, index=None, doc_type=None): - ''' + """ Mock of put_mapping method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.mapping_delete, "foo", "bar", "baz") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.mapping_delete, "foo", "bar", "baz" + ) # 'mapping_get' function tests: 3 def test_mapping_get(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_mapping(self, index=None, doc_type=None): - ''' + """ Mock of get_mapping method - ''' + """ return {"test": "key"} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertDictEqual(elasticsearch.mapping_get("foo", "bar", "baz"), {"test": "key"}) + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertDictEqual( + elasticsearch.mapping_get("foo", "bar", "baz"), {"test": "key"} + ) def test_mapping_get_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_mapping(self, index=None, doc_type=None): - ''' + """ Mock of get_mapping method - ''' + """ raise NotFoundError class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertIs(elasticsearch.mapping_get("foo", "bar", "baz"), None) def test_mapping_get_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_mapping(self, index=None, doc_type=None): - ''' + """ Mock of get_mapping method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.mapping_get, "foo", "bar", "baz") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.mapping_get, "foo", "bar", "baz" + ) # 'index_template_create' function tests: 3 def test_index_template_create(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_template(self, name=None, body=None): - ''' + """ Mock of put_template method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.index_template_create("foo", "bar")) def test_index_template_create_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_template(self, name=None, body=None): - ''' + """ Mock of put_template method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.index_template_create("foo", "bar")) def test_index_template_create_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_template(self, name=None, body=None): - ''' + """ Mock of put_template method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_template_create, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_template_create, "foo", "bar" + ) # 'index_template_delete' function tests: 3 def test_index_template_delete(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_template(self, name=None): - ''' + """ Mock of delete_template method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.index_template_delete("foo")) def test_index_template_delete_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_template(self, name=None): - ''' + """ Mock of delete_template method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.index_template_delete("foo")) def test_index_template_delete_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_template(self, name=None): - ''' + """ Mock of delete_template method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_template_delete, "foo") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_template_delete, "foo" + ) # 'index_template_exists' function tests: 3 def test_index_template_exists(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def exists_template(self, name=None): - ''' + """ Mock of exists_template method - ''' + """ return True class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.index_template_exists("foo")) def test_index_template_exists_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def exists_template(self, name=None): - ''' + """ Mock of exists_template method - ''' + """ return False class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.index_template_exists("foo")) def test_index_template_exists_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def exists_template(self, name=None): - ''' + """ Mock of exists_template method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_template_exists, "foo") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_template_exists, "foo" + ) # 'index_template_get' function tests: 3 def test_index_template_get(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_template(self, name=None): - ''' + """ Mock of get_template method - ''' + """ return {"test": "key"} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertDictEqual(elasticsearch.index_template_get("foo"), {"test": "key"}) + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertDictEqual( + elasticsearch.index_template_get("foo"), {"test": "key"} + ) def test_index_template_get_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_template(self, name=None): - ''' + """ Mock of get_template method - ''' + """ raise NotFoundError class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertIs(elasticsearch.index_template_get("foo"), None) def test_index_template_get_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIndices(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_template(self, name=None): - ''' + """ Mock of get_template method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + indices = MockElasticIndices() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.index_template_get, "foo") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.index_template_get, "foo" + ) # 'pipeline_get' function tests: 4 def test_pipeline_get(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_pipeline(self, id=None): - ''' + """ Mock of get_pipeline method - ''' + """ return {"test": "key"} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertDictEqual(elasticsearch.pipeline_get("foo"), {"test": "key"}) def test_pipeline_get_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_pipeline(self, id=None): - ''' + """ Mock of get_pipeline method - ''' + """ raise NotFoundError class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertIs(elasticsearch.pipeline_get("foo"), None) def test_pipeline_get_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def get_pipeline(self, id=None): - ''' + """ Mock of get_pipeline method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertRaises(CommandExecutionError, elasticsearch.pipeline_get, "foo") def test_pipeline_get_wrong_version(self): - ''' + """ Test if mapping creation fails with CEE on invalid elasticsearch-py version - ''' + """ + class MockElasticIngest(object): pass class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertRaises(CommandExecutionError, elasticsearch.pipeline_get, "foo") # 'pipeline_delete' function tests: 4 def test_pipeline_delete(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_pipeline(self, id=None): - ''' + """ Mock of delete_pipeline method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.pipeline_delete("foo")) def test_pipeline_delete_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_pipeline(self, id=None): - ''' + """ Mock of delete_pipeline method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.pipeline_delete("foo")) def test_pipeline_delete_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def delete_pipeline(self, id=None): - ''' + """ Mock of delete_pipeline method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.pipeline_delete, "foo") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.pipeline_delete, "foo" + ) def test_pipeline_delete_wrong_version(self): - ''' + """ Test if mapping creation fails with CEE on invalid elasticsearch-py version - ''' + """ + class MockElasticIngest(object): pass class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.pipeline_delete, "foo") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.pipeline_delete, "foo" + ) # 'pipeline_create' function tests: 4 def test_pipeline_create(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_pipeline(self, id=None, body=None): - ''' + """ Mock of put_pipeline method - ''' + """ return {"acknowledged": True} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.pipeline_create("foo", "bar")) def test_pipeline_create_not(self): - ''' + """ Test if mapping creation didn't ack - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_pipeline(self, id=None, body=None): - ''' + """ Mock of put_pipeline method - ''' + """ return {"acknowledged": False} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.pipeline_create("foo", "bar")) def test_pipeline_create_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def put_pipeline(self, id=None, body=None): - ''' + """ Mock of put_pipeline method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.pipeline_create, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.pipeline_create, "foo", "bar" + ) def test_pipeline_create_wrong_version(self): - ''' + """ Test if mapping creation fails with CEE on invalid elasticsearch-py version - ''' + """ + class MockElasticIngest(object): pass class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.pipeline_create, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.pipeline_create, "foo", "bar" + ) # 'pipeline_simulate' function tests: 3 def test_pipeline_simulate(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def simulate(self, id=None, body=None, verbose=None): - ''' + """ Mock of simulate method - ''' + """ return {"test": "key"} class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertDictEqual(elasticsearch.pipeline_simulate("foo", "bar"), {"test": "key"}) + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertDictEqual( + elasticsearch.pipeline_simulate("foo", "bar"), {"test": "key"} + ) def test_pipeline_simulate_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElasticIngest(object): - ''' + """ Mock of Elasticsearch IndicesClient - ''' + """ + def simulate(self, id=None, body=None, verbose=None): - ''' + """ Mock of simulate method - ''' + """ raise TransportError("custom message", 123) class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.pipeline_simulate, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.pipeline_simulate, "foo", "bar" + ) def test_pipeline_simulate_wrong_version(self): - ''' + """ Test if mapping creation fails with CEE on invalid elasticsearch-py version - ''' + """ + class MockElasticIngest(object): pass class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + ingest = MockElasticIngest() - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.pipeline_simulate, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.pipeline_simulate, "foo", "bar" + ) # 'search_template_get' function tests: 3 def test_search_template_get(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def get_template(self, id=None): - ''' + """ Mock of get_template method - ''' + """ return {"test": "key"} - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertDictEqual(elasticsearch.search_template_get("foo"), {"test": "key"}) + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertDictEqual( + elasticsearch.search_template_get("foo"), {"test": "key"} + ) def test_search_template_get_not(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def get_template(self, id=None): - ''' + """ Mock of get_template method - ''' + """ raise NotFoundError - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertIs(elasticsearch.search_template_get("foo"), None) def test_search_template_get_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def get_template(self, id=None): - ''' + """ Mock of get_template method - ''' + """ raise TransportError("custom message", 123) - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.search_template_get, "foo") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.search_template_get, "foo" + ) # 'search_template_create' function tests: 3 def test_search_template_create(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def put_template(self, id=None, body=None): - ''' + """ Mock of put_template method - ''' + """ return {"acknowledged": True} - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.search_template_create("foo", "bar")) def test_search_template_create_not(self): - ''' + """ Test if mapping can be created - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def put_template(self, id=None, body=None): - ''' + """ Mock of put_template method - ''' + """ return {"acknowledged": False} - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.search_template_create("foo", "bar")) def test_search_template_create_failure(self): - ''' + """ Test if mapping creation fails - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def put_template(self, id=None, body=None): - ''' + """ Mock of put_template method - ''' + """ raise TransportError("custom message", 123) - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.search_template_create, "foo", "bar") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, + elasticsearch.search_template_create, + "foo", + "bar", + ) # 'search_template_delete' function tests: 4 def test_search_template_delete(self): - ''' + """ Test if mapping can be deleted - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def delete_template(self, id=None): - ''' + """ Mock of delete_template method - ''' + """ return {"acknowledged": True} - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.search_template_delete("foo")) def test_search_template_delete_not(self): - ''' + """ Test if mapping can be deleted but not acked - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def delete_template(self, id=None): - ''' + """ Mock of delete_template method - ''' + """ return {"acknowledged": False} - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertFalse(elasticsearch.search_template_delete("foo")) def test_search_template_delete_not_exists(self): - ''' + """ Test if deleting mapping doesn't exist - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def delete_template(self, id=None): - ''' + """ Mock of delete_template method - ''' + """ raise NotFoundError - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): self.assertTrue(elasticsearch.search_template_delete("foo")) def test_search_template_delete_failure(self): - ''' + """ Test if mapping deletion fails - ''' + """ + class MockElastic(object): - ''' + """ Mock of Elasticsearch client - ''' + """ + def delete_template(self, id=None): - ''' + """ Mock of delete_template method - ''' + """ raise TransportError("custom message", 123) - with patch.object(elasticsearch, '_get_instance', - MagicMock(return_value=MockElastic())): - self.assertRaises(CommandExecutionError, elasticsearch.search_template_delete, "foo") + with patch.object( + elasticsearch, "_get_instance", MagicMock(return_value=MockElastic()) + ): + self.assertRaises( + CommandExecutionError, elasticsearch.search_template_delete, "foo" + ) # Cluster settings tests below. # We're assuming that _get_instance is properly tested # These tests are very simple in nature, mostly checking default arguments. def test_cluster_get_settings_succeess(self): - ''' + """ Test if cluster get_settings fetch succeeds - ''' + """ expected_settings = {"transient": {}, "persistent": {}} fake_es = MagicMock() @@ -2361,15 +2852,17 @@ class ElasticsearchTestCase(TestCase): fake_es.cluster.get_settings = MagicMock(return_value=expected_settings) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): + with patch.object(elasticsearch, "_get_instance", fake_instance): actual_settings = elasticsearch.cluster_get_settings() - fake_es.cluster.get_settings.assert_called_with(flat_settings=False, include_defaults=False) + fake_es.cluster.get_settings.assert_called_with( + flat_settings=False, include_defaults=False + ) assert actual_settings == expected_settings def test_cluster_get_settings_failure(self): - ''' + """ Test if cluster get_settings fetch fails with CommandExecutionError - ''' + """ fake_es = MagicMock() fake_es.cluster = MagicMock() @@ -2377,48 +2870,59 @@ class ElasticsearchTestCase(TestCase): fake_es.cluster.get_settings.side_effect = TransportError("custom error", 123) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): + with patch.object(elasticsearch, "_get_instance", fake_instance): self.assertRaises(CommandExecutionError, elasticsearch.cluster_get_settings) def test_cluster_put_settings_succeess(self): - ''' + """ Test if cluster put_settings succeeds - ''' + """ - expected_settings = {"acknowledged": True, - "transient": {}, - "persistent": {"indices": {"recovery": {"max_bytes_per_sec": "50mb"}}} - } - body = {"transient": {}, "persistent": {"indices.recovery.max_bytes_per_sec": "50mb"}} + expected_settings = { + "acknowledged": True, + "transient": {}, + "persistent": {"indices": {"recovery": {"max_bytes_per_sec": "50mb"}}}, + } + body = { + "transient": {}, + "persistent": {"indices.recovery.max_bytes_per_sec": "50mb"}, + } fake_es = MagicMock() fake_es.cluster = MagicMock() fake_es.cluster.put_settings = MagicMock(return_value=expected_settings) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): + with patch.object(elasticsearch, "_get_instance", fake_instance): actual_settings = elasticsearch.cluster_put_settings(body=body) - fake_es.cluster.put_settings.assert_called_with(body=body, flat_settings=False) + fake_es.cluster.put_settings.assert_called_with( + body=body, flat_settings=False + ) assert actual_settings == expected_settings def test_cluster_put_settings_failure(self): - ''' + """ Test if cluster put_settings fails with CommandExecutionError - ''' + """ - body = {"transient": {}, "persistent": {"indices.recovery.max_bytes_per_sec": "50mb"}} + body = { + "transient": {}, + "persistent": {"indices.recovery.max_bytes_per_sec": "50mb"}, + } fake_es = MagicMock() fake_es.cluster = MagicMock() fake_es.cluster.put_settings = MagicMock() fake_es.cluster.put_settings.side_effect = TransportError("custom error", 123) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): - self.assertRaises(CommandExecutionError, elasticsearch.cluster_put_settings, body=body) + with patch.object(elasticsearch, "_get_instance", fake_instance): + self.assertRaises( + CommandExecutionError, elasticsearch.cluster_put_settings, body=body + ) def test_cluster_put_settings_nobody(self): - ''' + """ Test if cluster put_settings fails with SaltInvocationError - ''' + """ self.assertRaises(SaltInvocationError, elasticsearch.cluster_put_settings) @@ -2426,25 +2930,37 @@ class ElasticsearchTestCase(TestCase): # We're assuming that _get_instance is properly tested # These tests are very simple in nature, mostly checking default arguments. def test_flush_synced_succeess(self): - ''' + """ Test if flush_synced succeeds - ''' + """ - expected_return = {'_shards': {'failed': 0, 'successful': 0, 'total': 0}} + expected_return = {"_shards": {"failed": 0, "successful": 0, "total": 0}} fake_es = MagicMock() fake_es.indices = MagicMock() fake_es.indices.flush_synced = MagicMock(return_value=expected_return) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): - output = elasticsearch.flush_synced(index='_all', ignore_unavailable=True, allow_no_indices=True, expand_wildcards='all') - fake_es.indices.flush_synced.assert_called_with({'index': '_all', 'ignore_unavailable': True, 'allow_no_indices': True, 'expand_wildcards': 'all'}) + with patch.object(elasticsearch, "_get_instance", fake_instance): + output = elasticsearch.flush_synced( + index="_all", + ignore_unavailable=True, + allow_no_indices=True, + expand_wildcards="all", + ) + fake_es.indices.flush_synced.assert_called_with( + { + "index": "_all", + "ignore_unavailable": True, + "allow_no_indices": True, + "expand_wildcards": "all", + } + ) assert output == expected_return def test_flush_synced_failure(self): - ''' + """ Test if flush_synced fails with CommandExecutionError - ''' + """ fake_es = MagicMock() fake_es.indices = MagicMock() @@ -2452,5 +2968,5 @@ class ElasticsearchTestCase(TestCase): fake_es.indices.flush_synced.side_effect = TransportError("custom error", 123) fake_instance = MagicMock(return_value=fake_es) - with patch.object(elasticsearch, '_get_instance', fake_instance): + with patch.object(elasticsearch, "_get_instance", fake_instance): self.assertRaises(CommandExecutionError, elasticsearch.flush_synced) diff --git a/tests/unit/modules/test_environ.py b/tests/unit/modules/test_environ.py index 3bfe1c60df1..6c17eb34a50 100644 --- a/tests/unit/modules/test_environ.py +++ b/tests/unit/modules/test_environ.py @@ -1,126 +1,127 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.environ as environ +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class EnvironTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.environ - ''' + """ + def setup_loader_modules(self): return {environ: {}} def test_setval(self): - ''' + """ Test for set a single salt process environment variable. Returns True on success. - ''' + """ mock = MagicMock(return_value=None) with patch.dict(os.environ, {}): - self.assertEqual(environ.setval('key', False, True), None) + self.assertEqual(environ.setval("key", False, True), None) mock = MagicMock(side_effect=Exception()) with patch.dict(os.environ, {}): - self.assertFalse(environ.setval('key', False, True)) + self.assertFalse(environ.setval("key", False, True)) mock_environ = {} with patch.dict(os.environ, mock_environ): - self.assertEqual(environ.setval('key', False), '') + self.assertEqual(environ.setval("key", False), "") with patch.dict(os.environ, mock_environ): - self.assertFalse(environ.setval('key', True)) + self.assertFalse(environ.setval("key", True)) def test_set_val_permanent(self): - with patch.dict(os.environ, {}), \ - patch.dict(environ.__salt__, {'reg.set_value': MagicMock(), - 'reg.delete_value': MagicMock()}), \ - patch('salt.utils.platform.is_windows', MagicMock(return_value=True)): + with patch.dict(os.environ, {}), patch.dict( + environ.__salt__, + {"reg.set_value": MagicMock(), "reg.delete_value": MagicMock()}, + ), patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): - environ.setval('key', 'Test', permanent=True) - environ.__salt__['reg.set_value'].assert_called_with('HKCU', 'Environment', 'key', 'Test') + environ.setval("key", "Test", permanent=True) + environ.__salt__["reg.set_value"].assert_called_with( + "HKCU", "Environment", "key", "Test" + ) - environ.setval('key', False, false_unsets=True, permanent=True) - environ.__salt__['reg.set_value'].asset_not_called() - environ.__salt__['reg.delete_value'].assert_called_with('HKCU', 'Environment', 'key') + environ.setval("key", False, false_unsets=True, permanent=True) + environ.__salt__["reg.set_value"].asset_not_called() + environ.__salt__["reg.delete_value"].assert_called_with( + "HKCU", "Environment", "key" + ) - key = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment' - environ.setval('key', 'Test', permanent='HKLM') - environ.__salt__['reg.set_value'].assert_called_with('HKLM', key, 'key', 'Test') + key = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" + environ.setval("key", "Test", permanent="HKLM") + environ.__salt__["reg.set_value"].assert_called_with( + "HKLM", key, "key", "Test" + ) def test_setenv(self): - ''' + """ Set multiple salt process environment variables from a dict. Returns a dict. - ''' - mock_environ = {'KEY': 'value'} + """ + mock_environ = {"KEY": "value"} with patch.dict(os.environ, mock_environ): - self.assertFalse(environ.setenv('environ')) + self.assertFalse(environ.setenv("environ")) with patch.dict(os.environ, mock_environ): - self.assertFalse(environ.setenv({'A': True}, - False, - True, - False)) + self.assertFalse(environ.setenv({"A": True}, False, True, False)) with patch.dict(os.environ, mock_environ): mock_setval = MagicMock(return_value=None) - with patch.object(environ, 'setval', mock_setval): - self.assertEqual(environ.setenv({}, False, True, False)['KEY'], - None) + with patch.object(environ, "setval", mock_setval): + self.assertEqual(environ.setenv({}, False, True, False)["KEY"], None) def test_get(self): - ''' + """ Get a single salt process environment variable. - ''' + """ self.assertFalse(environ.get(True)) - self.assertEqual(environ.get('key'), '') + self.assertEqual(environ.get("key"), "") def test_has_value(self): - ''' + """ Determine whether the key exists in the current salt process environment dictionary. Optionally compare the current value of the environment against the supplied value string. - ''' + """ mock_environ = {} with patch.dict(os.environ, mock_environ): self.assertFalse(environ.has_value(True)) - os.environ['salty'] = 'yes' - self.assertTrue(environ.has_value('salty', 'yes')) + os.environ["salty"] = "yes" + self.assertTrue(environ.has_value("salty", "yes")) - os.environ['too_salty'] = 'no' - self.assertFalse(environ.has_value('too_salty', 'yes')) + os.environ["too_salty"] = "no" + self.assertFalse(environ.has_value("too_salty", "yes")) - self.assertFalse(environ.has_value('key', 'value')) + self.assertFalse(environ.has_value("key", "value")) - os.environ['key'] = 'value' - self.assertTrue(environ.has_value('key')) + os.environ["key"] = "value" + self.assertTrue(environ.has_value("key")) def test_item(self): - ''' + """ Get one or more salt process environment variables. Returns a dict. - ''' + """ self.assertEqual(environ.item(None), {}) def test_items(self): - ''' + """ Return a dict of the entire environment set for the salt process - ''' + """ self.assertNotEqual(list(environ.items()), []) diff --git a/tests/unit/modules/test_esxcluster.py b/tests/unit/modules/test_esxcluster.py index 39c0117ec91..135ee459cf4 100644 --- a/tests/unit/modules/test_esxcluster.py +++ b/tests/unit/modules/test_esxcluster.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstanley.com>` Tests for functions in salt.modules.esxcluster -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -13,23 +13,25 @@ import salt.modules.esxcluster as esxcluster # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class GetDetailsTestCase(TestCase, LoaderModuleMockMixin): - '''Tests for salt.modules.esxcluster.get_details''' + """Tests for salt.modules.esxcluster.get_details""" + def setup_loader_modules(self): - return {esxcluster: {'__virtual__': - MagicMock(return_value='esxcluster'), - '__proxy__': {}}} + return { + esxcluster: { + "__virtual__": MagicMock(return_value="esxcluster"), + "__proxy__": {}, + } + } def test_get_details(self): mock_get_details = MagicMock() - with patch.dict(esxcluster.__proxy__, - {'esxcluster.get_details': mock_get_details}): + with patch.dict( + esxcluster.__proxy__, {"esxcluster.get_details": mock_get_details} + ): esxcluster.get_details() mock_get_details.assert_called_once_with() diff --git a/tests/unit/modules/test_esxdatacenter.py b/tests/unit/modules/test_esxdatacenter.py index 4a5de7bbb2a..e15c2a1507b 100644 --- a/tests/unit/modules/test_esxdatacenter.py +++ b/tests/unit/modules/test_esxdatacenter.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstanley.com>` Tests for functions in salt.modules.esxdatacenter -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -13,23 +13,25 @@ import salt.modules.esxdatacenter as esxdatacenter # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class GetDetailsTestCase(TestCase, LoaderModuleMockMixin): - '''Tests for salt.modules.esxdatacenter.get_details''' + """Tests for salt.modules.esxdatacenter.get_details""" + def setup_loader_modules(self): - return {esxdatacenter: {'__virtual__': - MagicMock(return_value='esxdatacenter'), - '__proxy__': {}}} + return { + esxdatacenter: { + "__virtual__": MagicMock(return_value="esxdatacenter"), + "__proxy__": {}, + } + } def test_get_details(self): mock_get_details = MagicMock() - with patch.dict(esxdatacenter.__proxy__, - {'esxdatacenter.get_details': mock_get_details}): + with patch.dict( + esxdatacenter.__proxy__, {"esxdatacenter.get_details": mock_get_details} + ): esxdatacenter.get_details() mock_get_details.assert_called_once_with() diff --git a/tests/unit/modules/test_etcd_mod.py b/tests/unit/modules/test_etcd_mod.py index 6f1bb139825..202412c9662 100644 --- a/tests/unit/modules/test_etcd_mod.py +++ b/tests/unit/modules/test_etcd_mod.py @@ -1,29 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - create_autospec, - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.etcd_mod as etcd_mod import salt.utils.etcd_util as etcd_util +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, create_autospec, patch +from tests.support.unit import TestCase + class EtcdModTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.etcd_mod - ''' + """ def setup_loader_modules(self): return {etcd_mod: {}} @@ -40,150 +36,171 @@ class EtcdModTestCase(TestCase, LoaderModuleMockMixin): # 'get_' function tests: 1 def test_get(self): - ''' + """ Test if it get a value from etcd, by direct path - ''' - with patch.dict(etcd_mod.__utils__, {'etcd_util.get_conn': self.EtcdClientMock}): - self.instance.get.return_value = 'stack' - self.assertEqual(etcd_mod.get_('salt'), 'stack') - self.instance.get.assert_called_with('salt', recurse=False) + """ + with patch.dict( + etcd_mod.__utils__, {"etcd_util.get_conn": self.EtcdClientMock} + ): + self.instance.get.return_value = "stack" + self.assertEqual(etcd_mod.get_("salt"), "stack") + self.instance.get.assert_called_with("salt", recurse=False) self.instance.tree.return_value = {} - self.assertEqual(etcd_mod.get_('salt', recurse=True), {}) - self.instance.tree.assert_called_with('salt') + self.assertEqual(etcd_mod.get_("salt", recurse=True), {}) + self.instance.tree.assert_called_with("salt") self.instance.get.side_effect = Exception - self.assertRaises(Exception, etcd_mod.get_, 'err') + self.assertRaises(Exception, etcd_mod.get_, "err") # 'set_' function tests: 1 def test_set(self): - ''' + """ Test if it set a key in etcd, by direct path - ''' - with patch.dict(etcd_mod.__utils__, {'etcd_util.get_conn': self.EtcdClientMock}): - self.instance.set.return_value = 'stack' - self.assertEqual(etcd_mod.set_('salt', 'stack'), 'stack') - self.instance.set.assert_called_with('salt', 'stack', directory=False, ttl=None) + """ + with patch.dict( + etcd_mod.__utils__, {"etcd_util.get_conn": self.EtcdClientMock} + ): + self.instance.set.return_value = "stack" + self.assertEqual(etcd_mod.set_("salt", "stack"), "stack") + self.instance.set.assert_called_with( + "salt", "stack", directory=False, ttl=None + ) self.instance.set.return_value = True - self.assertEqual(etcd_mod.set_('salt', '', directory=True), True) - self.instance.set.assert_called_with('salt', '', directory=True, ttl=None) + self.assertEqual(etcd_mod.set_("salt", "", directory=True), True) + self.instance.set.assert_called_with("salt", "", directory=True, ttl=None) - self.assertEqual(etcd_mod.set_('salt', '', directory=True, ttl=5), True) - self.instance.set.assert_called_with('salt', '', directory=True, ttl=5) + self.assertEqual(etcd_mod.set_("salt", "", directory=True, ttl=5), True) + self.instance.set.assert_called_with("salt", "", directory=True, ttl=5) - self.assertEqual(etcd_mod.set_('salt', '', None, 10, True), True) - self.instance.set.assert_called_with('salt', '', directory=True, ttl=10) + self.assertEqual(etcd_mod.set_("salt", "", None, 10, True), True) + self.instance.set.assert_called_with("salt", "", directory=True, ttl=10) self.instance.set.side_effect = Exception - self.assertRaises(Exception, etcd_mod.set_, 'err', 'stack') + self.assertRaises(Exception, etcd_mod.set_, "err", "stack") # 'update' function tests: 1 def test_update(self): - ''' + """ Test if can set multiple keys in etcd - ''' - with patch.dict(etcd_mod.__utils__, {'etcd_util.get_conn': self.EtcdClientMock}): + """ + with patch.dict( + etcd_mod.__utils__, {"etcd_util.get_conn": self.EtcdClientMock} + ): args = { - 'x': { - 'y': { - 'a': '1', - 'b': '2', - } - }, - 'z': '4', - 'd': {}, + "x": {"y": {"a": "1", "b": "2"}}, + "z": "4", + "d": {}, } result = { - '/some/path/x/y/a': '1', - '/some/path/x/y/b': '2', - '/some/path/z': '4', - '/some/path/d': {}, + "/some/path/x/y/a": "1", + "/some/path/x/y/b": "2", + "/some/path/z": "4", + "/some/path/d": {}, } self.instance.update.return_value = result - self.assertDictEqual(etcd_mod.update(args, path='/some/path'), result) - self.instance.update.assert_called_with(args, '/some/path') + self.assertDictEqual(etcd_mod.update(args, path="/some/path"), result) + self.instance.update.assert_called_with(args, "/some/path") self.assertDictEqual(etcd_mod.update(args), result) - self.instance.update.assert_called_with(args, '') + self.instance.update.assert_called_with(args, "") # 'ls_' function tests: 1 def test_ls(self): - ''' + """ Test if it return all keys and dirs inside a specific path - ''' - with patch.dict(etcd_mod.__utils__, {'etcd_util.get_conn': self.EtcdClientMock}): - self.instance.ls.return_value = {'/some-dir': {}} - self.assertDictEqual(etcd_mod.ls_('/some-dir'), {'/some-dir': {}}) - self.instance.ls.assert_called_with('/some-dir') + """ + with patch.dict( + etcd_mod.__utils__, {"etcd_util.get_conn": self.EtcdClientMock} + ): + self.instance.ls.return_value = {"/some-dir": {}} + self.assertDictEqual(etcd_mod.ls_("/some-dir"), {"/some-dir": {}}) + self.instance.ls.assert_called_with("/some-dir") - self.instance.ls.return_value = {'/': {}} - self.assertDictEqual(etcd_mod.ls_(), {'/': {}}) - self.instance.ls.assert_called_with('/') + self.instance.ls.return_value = {"/": {}} + self.assertDictEqual(etcd_mod.ls_(), {"/": {}}) + self.instance.ls.assert_called_with("/") self.instance.ls.side_effect = Exception - self.assertRaises(Exception, etcd_mod.ls_, 'err') + self.assertRaises(Exception, etcd_mod.ls_, "err") # 'rm_' function tests: 1 def test_rm(self): - ''' + """ Test if it delete a key from etcd - ''' - with patch.dict(etcd_mod.__utils__, {'etcd_util.get_conn': self.EtcdClientMock}): + """ + with patch.dict( + etcd_mod.__utils__, {"etcd_util.get_conn": self.EtcdClientMock} + ): self.instance.rm.return_value = False - self.assertFalse(etcd_mod.rm_('dir')) - self.instance.rm.assert_called_with('dir', recurse=False) + self.assertFalse(etcd_mod.rm_("dir")) + self.instance.rm.assert_called_with("dir", recurse=False) self.instance.rm.return_value = True - self.assertTrue(etcd_mod.rm_('dir', recurse=True)) - self.instance.rm.assert_called_with('dir', recurse=True) + self.assertTrue(etcd_mod.rm_("dir", recurse=True)) + self.instance.rm.assert_called_with("dir", recurse=True) self.instance.rm.side_effect = Exception - self.assertRaises(Exception, etcd_mod.rm_, 'err') + self.assertRaises(Exception, etcd_mod.rm_, "err") # 'tree' function tests: 1 def test_tree(self): - ''' + """ Test if it recurses through etcd and return all values - ''' - with patch.dict(etcd_mod.__utils__, {'etcd_util.get_conn': self.EtcdClientMock}): + """ + with patch.dict( + etcd_mod.__utils__, {"etcd_util.get_conn": self.EtcdClientMock} + ): self.instance.tree.return_value = {} - self.assertDictEqual(etcd_mod.tree('/some-dir'), {}) - self.instance.tree.assert_called_with('/some-dir') + self.assertDictEqual(etcd_mod.tree("/some-dir"), {}) + self.instance.tree.assert_called_with("/some-dir") self.assertDictEqual(etcd_mod.tree(), {}) - self.instance.tree.assert_called_with('/') + self.instance.tree.assert_called_with("/") self.instance.tree.side_effect = Exception - self.assertRaises(Exception, etcd_mod.tree, 'err') + self.assertRaises(Exception, etcd_mod.tree, "err") # 'watch' function tests: 1 def test_watch(self): - ''' + """ Test if watch returns the right tuples - ''' - with patch.dict(etcd_mod.__utils__, {'etcd_util.get_conn': self.EtcdClientMock}): + """ + with patch.dict( + etcd_mod.__utils__, {"etcd_util.get_conn": self.EtcdClientMock} + ): self.instance.watch.return_value = { - 'value': 'stack', - 'changed': True, - 'dir': False, - 'mIndex': 1, - 'key': '/salt' + "value": "stack", + "changed": True, + "dir": False, + "mIndex": 1, + "key": "/salt", } - self.assertEqual(etcd_mod.watch('/salt'), self.instance.watch.return_value) - self.instance.watch.assert_called_with('/salt', recurse=False, timeout=0, index=None) + self.assertEqual(etcd_mod.watch("/salt"), self.instance.watch.return_value) + self.instance.watch.assert_called_with( + "/salt", recurse=False, timeout=0, index=None + ) - self.instance.watch.return_value['dir'] = True - self.assertEqual(etcd_mod.watch('/some-dir', recurse=True, timeout=5, index=10), - self.instance.watch.return_value) - self.instance.watch.assert_called_with('/some-dir', recurse=True, timeout=5, index=10) + self.instance.watch.return_value["dir"] = True + self.assertEqual( + etcd_mod.watch("/some-dir", recurse=True, timeout=5, index=10), + self.instance.watch.return_value, + ) + self.instance.watch.assert_called_with( + "/some-dir", recurse=True, timeout=5, index=10 + ) - self.assertEqual(etcd_mod.watch('/some-dir', True, None, 5, 10), - self.instance.watch.return_value) - self.instance.watch.assert_called_with('/some-dir', recurse=True, timeout=5, index=10) + self.assertEqual( + etcd_mod.watch("/some-dir", True, None, 5, 10), + self.instance.watch.return_value, + ) + self.instance.watch.assert_called_with( + "/some-dir", recurse=True, timeout=5, index=10 + ) diff --git a/tests/unit/modules/test_event.py b/tests/unit/modules/test_event.py index 157c662a74f..47f805774eb 100644 --- a/tests/unit/modules/test_event.py +++ b/tests/unit/modules/test_event.py @@ -1,76 +1,84 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.event as event import salt.utils.event +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class EventTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.event - ''' + """ + def setup_loader_modules(self): return { event: { - '__opts__': { - 'id': 'id', - 'sock_dir': RUNTIME_VARS.TMP, - 'transport': 'zeromq' + "__opts__": { + "id": "id", + "sock_dir": RUNTIME_VARS.TMP, + "transport": "zeromq", } } } def test_fire_master(self): - ''' + """ Test for Fire an event off up to the master server - ''' - with patch('salt.crypt.SAuth') as salt_crypt_sauth, \ - patch('salt.transport.client.ReqChannel.factory') as salt_transport_channel_factory: + """ + with patch("salt.crypt.SAuth") as salt_crypt_sauth, patch( + "salt.transport.client.ReqChannel.factory" + ) as salt_transport_channel_factory: - preload = {'id': 'id', 'tag': 'tag', 'data': 'data', - 'tok': 'salt', 'cmd': '_minion_event'} + preload = { + "id": "id", + "tag": "tag", + "data": "data", + "tok": "salt", + "cmd": "_minion_event", + } - with patch.dict(event.__opts__, {'transport': 'A', - 'master_uri': 'localhost', - 'local': False}): - with patch.object(salt_crypt_sauth, 'gen_token', - return_value='tok'): - with patch.object(salt_transport_channel_factory, 'send', - return_value=None): - self.assertTrue(event.fire_master('data', 'tag', preload)) + with patch.dict( + event.__opts__, + {"transport": "A", "master_uri": "localhost", "local": False}, + ): + with patch.object(salt_crypt_sauth, "gen_token", return_value="tok"): + with patch.object( + salt_transport_channel_factory, "send", return_value=None + ): + self.assertTrue(event.fire_master("data", "tag", preload)) - with patch.dict(event.__opts__, {'transport': 'A', 'local': False}): - with patch.object(salt.utils.event.MinionEvent, 'fire_event', - side_effect=Exception('foo')): - self.assertFalse(event.fire_master('data', 'tag')) + with patch.dict(event.__opts__, {"transport": "A", "local": False}): + with patch.object( + salt.utils.event.MinionEvent, + "fire_event", + side_effect=Exception("foo"), + ): + self.assertFalse(event.fire_master("data", "tag")) def test_fire(self): - ''' + """ Test to fire an event on the local minion event bus. Data must be formed as a dict. - ''' - with patch('salt.utils.event') as salt_utils_event: - with patch.object(salt_utils_event, 'get_event') as mock: + """ + with patch("salt.utils.event") as salt_utils_event: + with patch.object(salt_utils_event, "get_event") as mock: mock.fire_event = MagicMock(return_value=True) - self.assertTrue(event.fire('data', 'tag')) + self.assertTrue(event.fire("data", "tag")) def test_send(self): - ''' + """ Test for Send an event to the Salt Master - ''' - with patch.object(event, 'fire_master', return_value='B'): - self.assertEqual(event.send('tag'), 'B') + """ + with patch.object(event, "fire_master", return_value="B"): + self.assertEqual(event.send("tag"), "B") diff --git a/tests/unit/modules/test_extfs.py b/tests/unit/modules/test_extfs.py index 98e2f132c7c..aca666f6754 100644 --- a/tests/unit/modules/test_extfs.py +++ b/tests/unit/modules/test_extfs.py @@ -1,75 +1,80 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.modules.extfs as extfs +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ExtfsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.extfs - ''' + """ + def setup_loader_modules(self): return {extfs: {}} # 'mkfs' function tests: 1 def test_mkfs(self): - ''' + """ Tests if a file system created on the specified device - ''' + """ mock = MagicMock() - with patch.dict(extfs.__salt__, {'cmd.run': mock}): - self.assertListEqual([], extfs.mkfs('/dev/sda1', 'ext4')) + with patch.dict(extfs.__salt__, {"cmd.run": mock}): + self.assertListEqual([], extfs.mkfs("/dev/sda1", "ext4")) # 'tune' function tests: 1 def test_tune(self): - ''' + """ Tests if specified group was added - ''' + """ mock = MagicMock() - with patch.dict(extfs.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.extfs.tune', MagicMock(return_value='')): - self.assertEqual('', extfs.tune('/dev/sda1')) + with patch.dict(extfs.__salt__, {"cmd.run": mock}), patch( + "salt.modules.extfs.tune", MagicMock(return_value="") + ): + self.assertEqual("", extfs.tune("/dev/sda1")) # 'dump' function tests: 1 def test_dump(self): - ''' + """ Tests if specified group was added - ''' + """ mock = MagicMock() - with patch.dict(extfs.__salt__, {'cmd.run': mock}): - self.assertEqual({'attributes': {}, 'blocks': {}}, - extfs.dump('/dev/sda1')) + with patch.dict(extfs.__salt__, {"cmd.run": mock}): + self.assertEqual({"attributes": {}, "blocks": {}}, extfs.dump("/dev/sda1")) # 'attributes' function tests: 1 def test_attributes(self): - ''' + """ Tests if specified group was added - ''' - with patch('salt.modules.extfs.dump', - MagicMock(return_value={'attributes': {}, 'blocks': {}})): - self.assertEqual({}, extfs.attributes('/dev/sda1')) + """ + with patch( + "salt.modules.extfs.dump", + MagicMock(return_value={"attributes": {}, "blocks": {}}), + ): + self.assertEqual({}, extfs.attributes("/dev/sda1")) # 'blocks' function tests: 1 def test_blocks(self): - ''' + """ Tests if specified group was added - ''' - with patch('salt.modules.extfs.dump', - MagicMock(return_value={'attributes': {}, 'blocks': {}})): - self.assertEqual({}, extfs.blocks('/dev/sda1')) + """ + with patch( + "salt.modules.extfs.dump", + MagicMock(return_value={"attributes": {}, "blocks": {}}), + ): + self.assertEqual({}, extfs.blocks("/dev/sda1")) diff --git a/tests/unit/modules/test_file.py b/tests/unit/modules/test_file.py index b9a78a24385..a3935d8d721 100644 --- a/tests/unit/modules/test_file.py +++ b/tests/unit/modules/test_file.py @@ -2,47 +2,50 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import tempfile import textwrap -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS +import salt.config +import salt.loader +import salt.modules.cmdmod as cmdmod +import salt.modules.config as configmod +import salt.modules.file as filemod +import salt.utils.data +import salt.utils.files +import salt.utils.platform +import salt.utils.stringutils +from salt.exceptions import CommandExecutionError, SaltInvocationError + +# Import Salt libs +from salt.ext import six +from salt.utils.jinja import SaltCacheLoader from tests.support.helpers import with_tempfile from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import DEFAULT, MagicMock, Mock, mock_open, patch + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, Mock, patch, mock_open, DEFAULT try: import pytest except ImportError: pytest = None -# Import Salt libs -from salt.ext import six -import salt.config -import salt.loader -import salt.utils.data -import salt.utils.files -import salt.utils.platform -import salt.utils.stringutils -import salt.modules.file as filemod -import salt.modules.config as configmod -import salt.modules.cmdmod as cmdmod -from salt.exceptions import CommandExecutionError, SaltInvocationError -from salt.utils.jinja import SaltCacheLoader if salt.utils.platform.is_windows(): import salt.modules.win_file as win_file import salt.utils.win_dacl as win_dacl -SED_CONTENT = '''test +SED_CONTENT = """test some content /var/lib/foo/app/test here -''' +""" class DummyStat(object): @@ -59,31 +62,31 @@ class DummyStat(object): class FileReplaceTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { filemod: { - '__salt__': { - 'config.manage_mode': configmod.manage_mode, - 'cmd.run': cmdmod.run, - 'cmd.run_all': cmdmod.run_all + "__salt__": { + "config.manage_mode": configmod.manage_mode, + "cmd.run": cmdmod.run, + "cmd.run_all": cmdmod.run_all, }, - '__opts__': { - 'test': False, - 'file_roots': {'base': 'tmp'}, - 'pillar_roots': {'base': 'tmp'}, - 'cachedir': 'tmp', - 'grains': {}, + "__opts__": { + "test": False, + "file_roots": {"base": "tmp"}, + "pillar_roots": {"base": "tmp"}, + "cachedir": "tmp", + "grains": {}, }, - '__grains__': {'kernel': 'Linux'}, - '__utils__': { - 'files.is_text': MagicMock(return_value=True), - 'stringutils.get_diff': salt.utils.stringutils.get_diff, + "__grains__": {"kernel": "Linux"}, + "__utils__": { + "files.is_text": MagicMock(return_value=True), + "stringutils.get_diff": salt.utils.stringutils.get_diff, }, } } - MULTILINE_STRING = textwrap.dedent('''\ + MULTILINE_STRING = textwrap.dedent( + """\ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam rhoncus enim ac bibendum vulputate. Etiam nibh velit, placerat ac auctor in, lacinia a turpis. Nulla elit elit, ornare in sodales eu, aliquam sit @@ -97,10 +100,11 @@ class FileReplaceTestCase(TestCase, LoaderModuleMockMixin): venenatis tellus eget massa facilisis, in auctor ante aliquet. Sed nec cursus metus. Curabitur massa urna, vehicula id porttitor sed, lobortis quis leo. - ''') + """ + ) def setUp(self): - self.tfile = tempfile.NamedTemporaryFile(delete=False, mode='w+') + self.tfile = tempfile.NamedTemporaryFile(delete=False, mode="w+") self.tfile.write(self.MULTILINE_STRING) self.tfile.close() @@ -109,148 +113,140 @@ class FileReplaceTestCase(TestCase, LoaderModuleMockMixin): del self.tfile def test_replace(self): - filemod.replace(self.tfile.name, r'Etiam', 'Salticus', backup=False) + filemod.replace(self.tfile.name, r"Etiam", "Salticus", backup=False) - with salt.utils.files.fopen(self.tfile.name, 'r') as fp: - self.assertIn( - 'Salticus', - salt.utils.stringutils.to_unicode(fp.read()) - ) + with salt.utils.files.fopen(self.tfile.name, "r") as fp: + self.assertIn("Salticus", salt.utils.stringutils.to_unicode(fp.read())) + + def test_replace_idempotency(self): + os.utime(self.tfile.name, (1552661253, 1552661253)) + mtime = os.stat(self.tfile.name).st_mtime + filemod.replace(self.tfile.name, r"Etia.", "Etiam", backup=False) + nmtime = os.stat(self.tfile.name).st_mtime + + self.assertEqual(mtime, nmtime) def test_replace_append_if_not_found(self): - ''' + """ Check that file.replace append_if_not_found works - ''' + """ args = { - 'pattern': '#*baz=(?P<value>.*)', - 'repl': 'baz=\\g<value>', - 'append_if_not_found': True, + "pattern": "#*baz=(?P<value>.*)", + "repl": "baz=\\g<value>", + "append_if_not_found": True, } - base = os.linesep.join(['foo=1', 'bar=2']) + base = os.linesep.join(["foo=1", "bar=2"]) # File ending with a newline, no match - with tempfile.NamedTemporaryFile('w+b', delete=False) as tfile: + with tempfile.NamedTemporaryFile("w+b", delete=False) as tfile: tfile.write(salt.utils.stringutils.to_bytes(base + os.linesep)) tfile.flush() filemod.replace(tfile.name, **args) - expected = os.linesep.join([base, 'baz=\\g<value>']) + os.linesep + expected = os.linesep.join([base, "baz=\\g<value>"]) + os.linesep with salt.utils.files.fopen(tfile.name) as tfile2: - self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), expected) + self.assertEqual(salt.utils.stringutils.to_unicode(tfile2.read()), expected) os.remove(tfile.name) # File not ending with a newline, no match - with tempfile.NamedTemporaryFile('w+b', delete=False) as tfile: + with tempfile.NamedTemporaryFile("w+b", delete=False) as tfile: tfile.write(salt.utils.stringutils.to_bytes(base)) tfile.flush() filemod.replace(tfile.name, **args) with salt.utils.files.fopen(tfile.name) as tfile2: - self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), expected) + self.assertEqual(salt.utils.stringutils.to_unicode(tfile2.read()), expected) os.remove(tfile.name) # A newline should not be added in empty files - with tempfile.NamedTemporaryFile('w+b', delete=False) as tfile: + with tempfile.NamedTemporaryFile("w+b", delete=False) as tfile: pass filemod.replace(tfile.name, **args) - expected = args['repl'] + os.linesep + expected = args["repl"] + os.linesep with salt.utils.files.fopen(tfile.name) as tfile2: - self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), expected) + self.assertEqual(salt.utils.stringutils.to_unicode(tfile2.read()), expected) os.remove(tfile.name) # Using not_found_content, rather than repl - with tempfile.NamedTemporaryFile('w+b', delete=False) as tfile: + with tempfile.NamedTemporaryFile("w+b", delete=False) as tfile: tfile.write(salt.utils.stringutils.to_bytes(base)) tfile.flush() - args['not_found_content'] = 'baz=3' - expected = os.linesep.join([base, 'baz=3']) + os.linesep + args["not_found_content"] = "baz=3" + expected = os.linesep.join([base, "baz=3"]) + os.linesep filemod.replace(tfile.name, **args) with salt.utils.files.fopen(tfile.name) as tfile2: - self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), expected) + self.assertEqual(salt.utils.stringutils.to_unicode(tfile2.read()), expected) os.remove(tfile.name) # not appending if matches - with tempfile.NamedTemporaryFile('w+b', delete=False) as tfile: - base = os.linesep.join(['foo=1', 'baz=42', 'bar=2']) + with tempfile.NamedTemporaryFile("w+b", delete=False) as tfile: + base = os.linesep.join(["foo=1", "baz=42", "bar=2"]) tfile.write(salt.utils.stringutils.to_bytes(base)) tfile.flush() expected = base filemod.replace(tfile.name, **args) with salt.utils.files.fopen(tfile.name) as tfile2: - self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), expected) + self.assertEqual(salt.utils.stringutils.to_unicode(tfile2.read()), expected) def test_backup(self): - fext = '.bak' - bak_file = '{0}{1}'.format(self.tfile.name, fext) + fext = ".bak" + bak_file = "{0}{1}".format(self.tfile.name, fext) - filemod.replace(self.tfile.name, r'Etiam', 'Salticus', backup=fext) + filemod.replace(self.tfile.name, r"Etiam", "Salticus", backup=fext) self.assertTrue(os.path.exists(bak_file)) os.unlink(bak_file) def test_nobackup(self): - fext = '.bak' - bak_file = '{0}{1}'.format(self.tfile.name, fext) + fext = ".bak" + bak_file = "{0}{1}".format(self.tfile.name, fext) - filemod.replace(self.tfile.name, r'Etiam', 'Salticus', backup=False) + filemod.replace(self.tfile.name, r"Etiam", "Salticus", backup=False) self.assertFalse(os.path.exists(bak_file)) def test_dry_run(self): before_ctime = os.stat(self.tfile.name).st_mtime - filemod.replace(self.tfile.name, r'Etiam', 'Salticus', dry_run=True) + filemod.replace(self.tfile.name, r"Etiam", "Salticus", dry_run=True) after_ctime = os.stat(self.tfile.name).st_mtime self.assertEqual(before_ctime, after_ctime) def test_show_changes(self): - ret = filemod.replace(self.tfile.name, - r'Etiam', 'Salticus', - show_changes=True) + ret = filemod.replace(self.tfile.name, r"Etiam", "Salticus", show_changes=True) - self.assertTrue(ret.startswith('---')) # looks like a diff + self.assertTrue(ret.startswith("---")) # looks like a diff def test_noshow_changes(self): - ret = filemod.replace(self.tfile.name, - r'Etiam', 'Salticus', - show_changes=False) + ret = filemod.replace(self.tfile.name, r"Etiam", "Salticus", show_changes=False) self.assertIsInstance(ret, bool) def test_re_str_flags(self): # upper- & lower-case - filemod.replace(self.tfile.name, - r'Etiam', 'Salticus', - flags=['MULTILINE', 'ignorecase']) + filemod.replace( + self.tfile.name, r"Etiam", "Salticus", flags=["MULTILINE", "ignorecase"] + ) def test_re_int_flags(self): - filemod.replace(self.tfile.name, r'Etiam', 'Salticus', flags=10) + filemod.replace(self.tfile.name, r"Etiam", "Salticus", flags=10) def test_numeric_repl(self): - ''' + """ This test covers cases where the replacement string is numeric, and the CLI parser yamlifies it into a numeric type. If not converted back to a string type in file.replace, a TypeError occurs when the replacemen is attempted. See https://github.com/saltstack/salt/issues/9097 for more information. - ''' - filemod.replace(self.tfile.name, r'Etiam', 123) + """ + filemod.replace(self.tfile.name, r"Etiam", 123) def test_search_only_return_true(self): - ret = filemod.replace(self.tfile.name, - r'Etiam', 'Salticus', - search_only=True) + ret = filemod.replace(self.tfile.name, r"Etiam", "Salticus", search_only=True) self.assertIsInstance(ret, bool) self.assertEqual(ret, True) def test_search_only_return_false(self): - ret = filemod.replace(self.tfile.name, - r'Etian', 'Salticus', - search_only=True) + ret = filemod.replace(self.tfile.name, r"Etian", "Salticus", search_only=True) self.assertIsInstance(ret, bool) self.assertEqual(ret, False) @@ -260,36 +256,38 @@ class FileCommentLineTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { filemod: { - '__salt__': { - 'config.manage_mode': configmod.manage_mode, - 'cmd.run': cmdmod.run, - 'cmd.run_all': cmdmod.run_all + "__salt__": { + "config.manage_mode": configmod.manage_mode, + "cmd.run": cmdmod.run, + "cmd.run_all": cmdmod.run_all, }, - '__opts__': { - 'test': False, - 'file_roots': {'base': 'tmp'}, - 'pillar_roots': {'base': 'tmp'}, - 'cachedir': 'tmp', - 'grains': {}, + "__opts__": { + "test": False, + "file_roots": {"base": "tmp"}, + "pillar_roots": {"base": "tmp"}, + "cachedir": "tmp", + "grains": {}, }, - '__grains__': {'kernel': 'Linux'}, - '__utils__': { - 'files.is_text': MagicMock(return_value=True), - 'stringutils.get_diff': salt.utils.stringutils.get_diff, + "__grains__": {"kernel": "Linux"}, + "__utils__": { + "files.is_text": MagicMock(return_value=True), + "stringutils.get_diff": salt.utils.stringutils.get_diff, }, } } - MULTILINE_STRING = textwrap.dedent('''\ + MULTILINE_STRING = textwrap.dedent( + """\ Lorem ipsum #dolor - ''') + """ + ) MULTILINE_STRING = os.linesep.join(MULTILINE_STRING.splitlines()) def setUp(self): - self.tfile = tempfile.NamedTemporaryFile(delete=False, mode='w+') + self.tfile = tempfile.NamedTemporaryFile(delete=False, mode="w+") self.tfile.write(self.MULTILINE_STRING) self.tfile.close() @@ -298,89 +296,85 @@ class FileCommentLineTestCase(TestCase, LoaderModuleMockMixin): del self.tfile def test_comment_line(self): - filemod.comment_line(self.tfile.name, - '^ipsum') + filemod.comment_line(self.tfile.name, "^ipsum") - with salt.utils.files.fopen(self.tfile.name, 'r') as fp: + with salt.utils.files.fopen(self.tfile.name, "r") as fp: filecontent = fp.read() - self.assertIn('#ipsum', filecontent) + self.assertIn("#ipsum", filecontent) def test_comment(self): - filemod.comment(self.tfile.name, - '^ipsum') + filemod.comment(self.tfile.name, "^ipsum") - with salt.utils.files.fopen(self.tfile.name, 'r') as fp: + with salt.utils.files.fopen(self.tfile.name, "r") as fp: filecontent = fp.read() - self.assertIn('#ipsum', filecontent) + self.assertIn("#ipsum", filecontent) def test_comment_different_character(self): - filemod.comment_line(self.tfile.name, - '^ipsum', - '//') + filemod.comment_line(self.tfile.name, "^ipsum", "//") - with salt.utils.files.fopen(self.tfile.name, 'r') as fp: + with salt.utils.files.fopen(self.tfile.name, "r") as fp: filecontent = fp.read() - self.assertIn('//ipsum', filecontent) + self.assertIn("//ipsum", filecontent) def test_comment_not_found(self): - filemod.comment_line(self.tfile.name, - '^sit') + filemod.comment_line(self.tfile.name, "^sit") - with salt.utils.files.fopen(self.tfile.name, 'r') as fp: + with salt.utils.files.fopen(self.tfile.name, "r") as fp: filecontent = fp.read() - self.assertNotIn('#sit', filecontent) - self.assertNotIn('sit', filecontent) + self.assertNotIn("#sit", filecontent) + self.assertNotIn("sit", filecontent) def test_uncomment(self): - filemod.uncomment(self.tfile.name, - 'dolor') + filemod.uncomment(self.tfile.name, "dolor") - with salt.utils.files.fopen(self.tfile.name, 'r') as fp: + with salt.utils.files.fopen(self.tfile.name, "r") as fp: filecontent = fp.read() - self.assertIn('dolor', filecontent) - self.assertNotIn('#dolor', filecontent) + self.assertIn("dolor", filecontent) + self.assertNotIn("#dolor", filecontent) class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): if salt.utils.platform.is_windows(): - grains = {'kernel': 'Windows'} + grains = {"kernel": "Windows"} else: - grains = {'kernel': 'Linux'} - opts = {'test': False, - 'file_roots': {'base': 'tmp'}, - 'pillar_roots': {'base': 'tmp'}, - 'cachedir': 'tmp', - 'grains': grains} + grains = {"kernel": "Linux"} + opts = { + "test": False, + "file_roots": {"base": "tmp"}, + "pillar_roots": {"base": "tmp"}, + "cachedir": "tmp", + "grains": grains, + } ret = { filemod: { - '__salt__': { - 'config.manage_mode': MagicMock(), - 'cmd.run': cmdmod.run, - 'cmd.run_all': cmdmod.run_all + "__salt__": { + "config.manage_mode": MagicMock(), + "cmd.run": cmdmod.run, + "cmd.run_all": cmdmod.run_all, }, - '__opts__': opts, - '__grains__': grains, - '__utils__': { - 'files.is_binary': MagicMock(return_value=False), - 'files.get_encoding': MagicMock(return_value='utf-8'), - 'stringutils.get_diff': salt.utils.stringutils.get_diff, + "__opts__": opts, + "__grains__": grains, + "__utils__": { + "files.is_binary": MagicMock(return_value=False), + "files.get_encoding": MagicMock(return_value="utf-8"), + "stringutils.get_diff": salt.utils.stringutils.get_diff, }, } } if salt.utils.platform.is_windows(): - ret.update({ - win_dacl: {'__opts__': opts}, - win_file: { - '__utils__': { - 'dacl.check_perms': win_dacl.check_perms - }}}) + ret.update( + { + win_dacl: {"__opts__": opts}, + win_file: {"__utils__": {"dacl.check_perms": win_dacl.check_perms}}, + } + ) return ret - MULTILINE_STRING = textwrap.dedent('''\ + MULTILINE_STRING = textwrap.dedent( + """\ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam rhoncus enim ac bibendum vulputate. Etiam nibh velit, placerat ac auctor in, lacinia a turpis. Nulla elit elit, ornare in sodales eu, aliquam sit @@ -405,14 +399,15 @@ class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin): venenatis tellus eget massa facilisis, in auctor ante aliquet. Sed nec cursus metus. Curabitur massa urna, vehicula id porttitor sed, lobortis quis leo. - ''') + """ + ) MULTILINE_STRING = os.linesep.join(MULTILINE_STRING.splitlines()) def setUp(self): - self.tfile = tempfile.NamedTemporaryFile(delete=False, - prefix='blockrepltmp', - mode='w+b') + self.tfile = tempfile.NamedTemporaryFile( + delete=False, prefix="blockrepltmp", mode="w+b" + ) self.tfile.write(salt.utils.stringutils.to_bytes(self.MULTILINE_STRING)) self.tfile.close() @@ -421,32 +416,40 @@ class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin): del self.tfile def test_replace_multiline(self): - new_multiline_content = os.linesep.join([ - "Who's that then?", - "Well, how'd you become king, then?", - "We found them. I'm not a witch.", - "We shall say 'Ni' again to you, if you do not appease us." - ]) + new_multiline_content = os.linesep.join( + [ + "Who's that then?", + "Well, how'd you become king, then?", + "We found them. I'm not a witch.", + "We shall say 'Ni' again to you, if you do not appease us.", + ] + ) if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): - filemod.blockreplace(self.tfile.name, - marker_start='#-- START BLOCK 1', - marker_end='#-- END BLOCK 1', - content=new_multiline_content, - backup=False, - append_newline=None) + with patch.object(filemod, "check_perms", check_perms_patch): + filemod.blockreplace( + self.tfile.name, + marker_start="#-- START BLOCK 1", + marker_end="#-- END BLOCK 1", + content=new_multiline_content, + backup=False, + append_newline=None, + ) - with salt.utils.files.fopen(self.tfile.name, 'rb') as fp: + with salt.utils.files.fopen(self.tfile.name, "rb") as fp: filecontent = fp.read() - self.assertIn(salt.utils.stringutils.to_bytes( - os.linesep.join([ - '#-- START BLOCK 1', new_multiline_content, '#-- END BLOCK 1'])), - filecontent) - self.assertNotIn(b'old content part 1', filecontent) - self.assertNotIn(b'old content part 2', filecontent) + self.assertIn( + salt.utils.stringutils.to_bytes( + os.linesep.join( + ["#-- START BLOCK 1", new_multiline_content, "#-- END BLOCK 1"] + ) + ), + filecontent, + ) + self.assertNotIn(b"old content part 1", filecontent) + self.assertNotIn(b"old content part 2", filecontent) def test_replace_append(self): new_content = "Well, I didn't vote for you." @@ -455,93 +458,95 @@ class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin): CommandExecutionError, filemod.blockreplace, self.tfile.name, - marker_start='#-- START BLOCK 2', - marker_end='#-- END BLOCK 2', + marker_start="#-- START BLOCK 2", + marker_end="#-- END BLOCK 2", content=new_content, append_if_not_found=False, - backup=False + backup=False, ) - with salt.utils.files.fopen(self.tfile.name, 'r') as fp: + with salt.utils.files.fopen(self.tfile.name, "r") as fp: self.assertNotIn( - '#-- START BLOCK 2' + "\n" + new_content + '#-- END BLOCK 2', - salt.utils.stringutils.to_unicode(fp.read()) + "#-- START BLOCK 2" + "\n" + new_content + "#-- END BLOCK 2", + salt.utils.stringutils.to_unicode(fp.read()), ) if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): - filemod.blockreplace(self.tfile.name, - marker_start='#-- START BLOCK 2', - marker_end='#-- END BLOCK 2', - content=new_content, - backup=False, - append_if_not_found=True) + with patch.object(filemod, "check_perms", check_perms_patch): + filemod.blockreplace( + self.tfile.name, + marker_start="#-- START BLOCK 2", + marker_end="#-- END BLOCK 2", + content=new_content, + backup=False, + append_if_not_found=True, + ) - with salt.utils.files.fopen(self.tfile.name, 'rb') as fp: - self.assertIn(salt.utils.stringutils.to_bytes( - os.linesep.join([ - '#-- START BLOCK 2', - '{0}#-- END BLOCK 2'.format(new_content)])), - fp.read()) + with salt.utils.files.fopen(self.tfile.name, "rb") as fp: + self.assertIn( + salt.utils.stringutils.to_bytes( + os.linesep.join( + ["#-- START BLOCK 2", "{0}#-- END BLOCK 2".format(new_content)] + ) + ), + fp.read(), + ) def test_replace_append_newline_at_eof(self): - ''' + """ Check that file.blockreplace works consistently on files with and without newlines at end of file. - ''' - base = 'bar' + """ + base = "bar" args = { - 'marker_start': '#start', - 'marker_end': '#stop', - 'content': 'baz', - 'append_if_not_found': True, + "marker_start": "#start", + "marker_end": "#stop", + "content": "baz", + "append_if_not_found": True, } - block = os.linesep.join(['#start', 'baz#stop']) + os.linesep + block = os.linesep.join(["#start", "baz#stop"]) + os.linesep # File ending with a newline - with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as tfile: + with tempfile.NamedTemporaryFile(mode="w+b", delete=False) as tfile: tfile.write(salt.utils.stringutils.to_bytes(base + os.linesep)) tfile.flush() if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): + with patch.object(filemod, "check_perms", check_perms_patch): filemod.blockreplace(tfile.name, **args) expected = os.linesep.join([base, block]) with salt.utils.files.fopen(tfile.name) as tfile2: - self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), expected) + self.assertEqual(salt.utils.stringutils.to_unicode(tfile2.read()), expected) os.remove(tfile.name) # File not ending with a newline - with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as tfile: + with tempfile.NamedTemporaryFile(mode="w+b", delete=False) as tfile: tfile.write(salt.utils.stringutils.to_bytes(base)) tfile.flush() if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): + with patch.object(filemod, "check_perms", check_perms_patch): filemod.blockreplace(tfile.name, **args) with salt.utils.files.fopen(tfile.name) as tfile2: - self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), expected) + self.assertEqual(salt.utils.stringutils.to_unicode(tfile2.read()), expected) os.remove(tfile.name) # A newline should not be added in empty files - with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as tfile: + with tempfile.NamedTemporaryFile(mode="w+b", delete=False) as tfile: pass if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): + with patch.object(filemod, "check_perms", check_perms_patch): filemod.blockreplace(tfile.name, **args) with salt.utils.files.fopen(tfile.name) as tfile2: - self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), block) + self.assertEqual(salt.utils.stringutils.to_unicode(tfile2.read()), block) os.remove(tfile.name) def test_replace_prepend(self): @@ -551,92 +556,109 @@ class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin): CommandExecutionError, filemod.blockreplace, self.tfile.name, - marker_start='#-- START BLOCK 2', - marker_end='#-- END BLOCK 2', + marker_start="#-- START BLOCK 2", + marker_end="#-- END BLOCK 2", content=new_content, prepend_if_not_found=False, - backup=False + backup=False, ) - with salt.utils.files.fopen(self.tfile.name, 'rb') as fp: - self.assertNotIn(salt.utils.stringutils.to_bytes( - os.linesep.join([ - '#-- START BLOCK 2', - '{0}#-- END BLOCK 2'.format(new_content)])), - fp.read()) + with salt.utils.files.fopen(self.tfile.name, "rb") as fp: + self.assertNotIn( + salt.utils.stringutils.to_bytes( + os.linesep.join( + ["#-- START BLOCK 2", "{0}#-- END BLOCK 2".format(new_content)] + ) + ), + fp.read(), + ) if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): - filemod.blockreplace(self.tfile.name, - marker_start='#-- START BLOCK 2', - marker_end='#-- END BLOCK 2', - content=new_content, - backup=False, - prepend_if_not_found=True) + with patch.object(filemod, "check_perms", check_perms_patch): + filemod.blockreplace( + self.tfile.name, + marker_start="#-- START BLOCK 2", + marker_end="#-- END BLOCK 2", + content=new_content, + backup=False, + prepend_if_not_found=True, + ) - with salt.utils.files.fopen(self.tfile.name, 'rb') as fp: + with salt.utils.files.fopen(self.tfile.name, "rb") as fp: self.assertTrue( - fp.read().startswith(salt.utils.stringutils.to_bytes( - os.linesep.join([ - '#-- START BLOCK 2', - '{0}#-- END BLOCK 2'.format(new_content)])))) + fp.read().startswith( + salt.utils.stringutils.to_bytes( + os.linesep.join( + [ + "#-- START BLOCK 2", + "{0}#-- END BLOCK 2".format(new_content), + ] + ) + ) + ) + ) def test_replace_partial_marked_lines(self): if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): - filemod.blockreplace(self.tfile.name, - marker_start='// START BLOCK', - marker_end='// END BLOCK', - content='new content 1', - backup=False) + with patch.object(filemod, "check_perms", check_perms_patch): + filemod.blockreplace( + self.tfile.name, + marker_start="// START BLOCK", + marker_end="// END BLOCK", + content="new content 1", + backup=False, + ) - with salt.utils.files.fopen(self.tfile.name, 'r') as fp: + with salt.utils.files.fopen(self.tfile.name, "r") as fp: filecontent = salt.utils.stringutils.to_unicode(fp.read()) - self.assertIn('new content 1', filecontent) - self.assertNotIn('to be removed', filecontent) - self.assertIn('first part of start line', filecontent) - self.assertNotIn('first part of end line', filecontent) - self.assertIn('part of start line not removed', filecontent) - self.assertIn('part of end line not removed', filecontent) + self.assertIn("new content 1", filecontent) + self.assertNotIn("to be removed", filecontent) + self.assertIn("first part of start line", filecontent) + self.assertNotIn("first part of end line", filecontent) + self.assertIn("part of start line not removed", filecontent) + self.assertIn("part of end line not removed", filecontent) def test_backup(self): - fext = '.bak' - bak_file = '{0}{1}'.format(self.tfile.name, fext) + fext = ".bak" + bak_file = "{0}{1}".format(self.tfile.name, fext) if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): + with patch.object(filemod, "check_perms", check_perms_patch): filemod.blockreplace( self.tfile.name, - marker_start='// START BLOCK', - marker_end='// END BLOCK', - content='new content 2', - backup=fext) + marker_start="// START BLOCK", + marker_end="// END BLOCK", + content="new content 2", + backup=fext, + ) self.assertTrue(os.path.exists(bak_file)) os.unlink(bak_file) self.assertFalse(os.path.exists(bak_file)) - fext = '.bak' - bak_file = '{0}{1}'.format(self.tfile.name, fext) + fext = ".bak" + bak_file = "{0}{1}".format(self.tfile.name, fext) if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): - filemod.blockreplace(self.tfile.name, - marker_start='// START BLOCK', - marker_end='// END BLOCK', - content='new content 3', - backup=False) + with patch.object(filemod, "check_perms", check_perms_patch): + filemod.blockreplace( + self.tfile.name, + marker_start="// START BLOCK", + marker_end="// END BLOCK", + content="new content 3", + backup=False, + ) self.assertFalse(os.path.exists(bak_file)) @@ -645,36 +667,42 @@ class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): - filemod.blockreplace(self.tfile.name, - marker_start='#-- START BLOCK 1', - marker_end='#-- END BLOCK 1', - content='new content 4', - backup=False, - append_newline=None) + with patch.object(filemod, "check_perms", check_perms_patch): + filemod.blockreplace( + self.tfile.name, + marker_start="#-- START BLOCK 1", + marker_end="#-- END BLOCK 1", + content="new content 4", + backup=False, + append_newline=None, + ) before_ctime = os.stat(self.tfile.name).st_mtime if salt.utils.platform.is_windows(): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): - filemod.blockreplace(self.tfile.name, - marker_start='#-- START BLOCK 1', - marker_end='#-- END BLOCK 1', - content='new content 4', - backup=False, - append_newline=None) + with patch.object(filemod, "check_perms", check_perms_patch): + filemod.blockreplace( + self.tfile.name, + marker_start="#-- START BLOCK 1", + marker_end="#-- END BLOCK 1", + content="new content 4", + backup=False, + append_newline=None, + ) after_ctime = os.stat(self.tfile.name).st_mtime self.assertEqual(before_ctime, after_ctime) def test_dry_run(self): before_ctime = os.stat(self.tfile.name).st_mtime - filemod.blockreplace(self.tfile.name, - marker_start='// START BLOCK', - marker_end='// END BLOCK', - content='new content 5', - dry_run=True) + filemod.blockreplace( + self.tfile.name, + marker_start="// START BLOCK", + marker_end="// END BLOCK", + content="new content 5", + dry_run=True, + ) after_ctime = os.stat(self.tfile.name).st_mtime self.assertEqual(before_ctime, after_ctime) @@ -684,22 +712,26 @@ class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin): check_perms_patch = win_file.check_perms else: check_perms_patch = filemod.check_perms - with patch.object(filemod, 'check_perms', check_perms_patch): - ret = filemod.blockreplace(self.tfile.name, - marker_start='// START BLOCK', - marker_end='// END BLOCK', - content='new content 6', - backup=False, - show_changes=True) + with patch.object(filemod, "check_perms", check_perms_patch): + ret = filemod.blockreplace( + self.tfile.name, + marker_start="// START BLOCK", + marker_end="// END BLOCK", + content="new content 6", + backup=False, + show_changes=True, + ) - self.assertTrue(ret.startswith('---')) # looks like a diff + self.assertTrue(ret.startswith("---")) # looks like a diff - ret = filemod.blockreplace(self.tfile.name, - marker_start='// START BLOCK', - marker_end='// END BLOCK', - content='new content 7', - backup=False, - show_changes=False) + ret = filemod.blockreplace( + self.tfile.name, + marker_start="// START BLOCK", + marker_end="// END BLOCK", + content="new content 7", + backup=False, + show_changes=False, + ) self.assertIsInstance(ret, bool) @@ -708,48 +740,50 @@ class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin): CommandExecutionError, filemod.blockreplace, self.tfile.name, - marker_start='#-- START BLOCK UNFINISHED', - marker_end='#-- END BLOCK UNFINISHED', - content='foobar', - backup=False + marker_start="#-- START BLOCK UNFINISHED", + marker_end="#-- END BLOCK UNFINISHED", + content="foobar", + backup=False, ) -@skipIf(salt.utils.platform.is_windows(), 'Skip on windows') +@skipIf(salt.utils.platform.is_windows(), "Skip on windows") class FileGrepTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { filemod: { - '__salt__': { - 'config.manage_mode': configmod.manage_mode, - 'cmd.run': cmdmod.run, - 'cmd.run_all': cmdmod.run_all + "__salt__": { + "config.manage_mode": configmod.manage_mode, + "cmd.run": cmdmod.run, + "cmd.run_all": cmdmod.run_all, }, - '__opts__': { - 'test': False, - 'file_roots': {'base': 'tmp'}, - 'pillar_roots': {'base': 'tmp'}, - 'cachedir': 'tmp', - 'grains': {}, + "__opts__": { + "test": False, + "file_roots": {"base": "tmp"}, + "pillar_roots": {"base": "tmp"}, + "cachedir": "tmp", + "grains": {}, }, - '__grains__': {'kernel': 'Linux'}, - '__utils__': { - 'files.is_text': MagicMock(return_value=True), - 'stringutils.get_diff': salt.utils.stringutils.get_diff, + "__grains__": {"kernel": "Linux"}, + "__utils__": { + "files.is_text": MagicMock(return_value=True), + "stringutils.get_diff": salt.utils.stringutils.get_diff, }, } } - MULTILINE_STRING = textwrap.dedent('''\ + MULTILINE_STRING = textwrap.dedent( + """\ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam rhoncus enim ac bibendum vulputate. - ''') + """ + ) MULTILINE_STRING = os.linesep.join(MULTILINE_STRING.splitlines()) def setUp(self): - self.tfile = tempfile.NamedTemporaryFile(delete=False, mode='w+') + self.tfile = tempfile.NamedTemporaryFile(delete=False, mode="w+") self.tfile.write(self.MULTILINE_STRING) self.tfile.close() @@ -758,348 +792,389 @@ class FileGrepTestCase(TestCase, LoaderModuleMockMixin): del self.tfile def test_grep_query_exists(self): - result = filemod.grep(self.tfile.name, - 'Lorem ipsum') + result = filemod.grep(self.tfile.name, "Lorem ipsum") self.assertTrue(result, None) - self.assertTrue(result['retcode'] == 0) - self.assertTrue(result['stdout'] == 'Lorem ipsum dolor sit amet, consectetur') - self.assertTrue(result['stderr'] == '') + self.assertTrue(result["retcode"] == 0) + self.assertTrue(result["stdout"] == "Lorem ipsum dolor sit amet, consectetur") + self.assertTrue(result["stderr"] == "") def test_grep_query_not_exists(self): - result = filemod.grep(self.tfile.name, - 'Lorem Lorem') + result = filemod.grep(self.tfile.name, "Lorem Lorem") - self.assertTrue(result['retcode'] == 1) - self.assertTrue(result['stdout'] == '') - self.assertTrue(result['stderr'] == '') + self.assertTrue(result["retcode"] == 1) + self.assertTrue(result["stdout"] == "") + self.assertTrue(result["stderr"] == "") def test_grep_query_exists_with_opt(self): - result = filemod.grep(self.tfile.name, - 'Lorem ipsum', - '-i') + result = filemod.grep(self.tfile.name, "Lorem ipsum", "-i") self.assertTrue(result, None) - self.assertTrue(result['retcode'] == 0) - self.assertTrue(result['stdout'] == 'Lorem ipsum dolor sit amet, consectetur') - self.assertTrue(result['stderr'] == '') + self.assertTrue(result["retcode"] == 0) + self.assertTrue(result["stdout"] == "Lorem ipsum dolor sit amet, consectetur") + self.assertTrue(result["stderr"] == "") def test_grep_query_not_exists_opt(self): - result = filemod.grep(self.tfile.name, - 'Lorem Lorem', - '-v') + result = filemod.grep(self.tfile.name, "Lorem Lorem", "-v") - self.assertTrue(result['retcode'] == 0) - self.assertTrue(result['stdout'] == FileGrepTestCase.MULTILINE_STRING) - self.assertTrue(result['stderr'] == '') + self.assertTrue(result["retcode"] == 0) + self.assertTrue(result["stdout"] == FileGrepTestCase.MULTILINE_STRING) + self.assertTrue(result["stderr"] == "") def test_grep_query_too_many_opts(self): - with self.assertRaisesRegex(SaltInvocationError, '^Passing multiple command line arg') as cm: - result = filemod.grep(self.tfile.name, - 'Lorem Lorem', - '-i -b2') + with self.assertRaisesRegex( + SaltInvocationError, "^Passing multiple command line arg" + ) as cm: + result = filemod.grep(self.tfile.name, "Lorem Lorem", "-i -b2") def test_grep_query_exists_wildcard(self): - _file = '{0}*'.format(self.tfile.name) - result = filemod.grep(_file, - 'Lorem ipsum') + _file = "{0}*".format(self.tfile.name) + result = filemod.grep(_file, "Lorem ipsum") self.assertTrue(result, None) - self.assertTrue(result['retcode'] == 0) - self.assertTrue(result['stdout'] == 'Lorem ipsum dolor sit amet, consectetur') - self.assertTrue(result['stderr'] == '') + self.assertTrue(result["retcode"] == 0) + self.assertTrue(result["stdout"] == "Lorem ipsum dolor sit amet, consectetur") + self.assertTrue(result["stderr"] == "") def test_grep_file_not_exists_wildcard(self): - _file = '{0}-junk*'.format(self.tfile.name) - result = filemod.grep(_file, - 'Lorem ipsum') + _file = "{0}-junk*".format(self.tfile.name) + result = filemod.grep(_file, "Lorem ipsum") self.assertTrue(result, None) - self.assertFalse(result['retcode'] == 0) - self.assertFalse(result['stdout'] == 'Lorem ipsum dolor sit amet, consectetur') - _expected_stderr = 'grep: {0}-junk*: No such file or directory'.format(self.tfile.name) - self.assertTrue(result['stderr'] == _expected_stderr) + self.assertFalse(result["retcode"] == 0) + self.assertFalse(result["stdout"] == "Lorem ipsum dolor sit amet, consectetur") + _expected_stderr = "grep: {0}-junk*: No such file or directory".format( + self.tfile.name + ) + self.assertTrue(result["stderr"] == _expected_stderr) class FileModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { filemod: { - '__salt__': { - 'config.manage_mode': configmod.manage_mode, - 'cmd.run': cmdmod.run, - 'cmd.run_all': cmdmod.run_all + "__salt__": { + "config.manage_mode": configmod.manage_mode, + "cmd.run": cmdmod.run, + "cmd.run_all": cmdmod.run_all, }, - '__opts__': { - 'test': False, - 'file_roots': {'base': 'tmp'}, - 'pillar_roots': {'base': 'tmp'}, - 'cachedir': 'tmp', - 'grains': {}, - }, - '__grains__': {'kernel': 'Linux'}, - '__utils__': { - 'stringutils.get_diff': salt.utils.stringutils.get_diff, + "__opts__": { + "test": False, + "file_roots": {"base": "tmp"}, + "pillar_roots": {"base": "tmp"}, + "cachedir": "tmp", + "grains": {}, }, + "__grains__": {"kernel": "Linux"}, + "__utils__": {"stringutils.get_diff": salt.utils.stringutils.get_diff}, } } - @skipIf(salt.utils.platform.is_windows(), 'lsattr is not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "lsattr is not available on Windows") def test_check_file_meta_no_lsattr(self): - ''' + """ Ensure that we skip attribute comparison if lsattr(1) is not found - ''' + """ source = "salt:///README.md" name = "/home/git/proj/a/README.md" source_sum = {} - stats_result = {'size': 22, 'group': 'wheel', 'uid': 0, 'type': 'file', - 'mode': '0600', 'gid': 0, 'target': name, 'user': - 'root', 'mtime': 1508356390, 'atime': 1508356390, - 'inode': 447, 'ctime': 1508356390} - with patch('salt.modules.file.stats') as m_stats: + stats_result = { + "size": 22, + "group": "wheel", + "uid": 0, + "type": "file", + "mode": "0600", + "gid": 0, + "target": name, + "user": "root", + "mtime": 1508356390, + "atime": 1508356390, + "inode": 447, + "ctime": 1508356390, + } + with patch("salt.modules.file.stats") as m_stats: m_stats.return_value = stats_result - with patch('salt.utils.path.which') as m_which: + with patch("salt.utils.path.which") as m_which: m_which.return_value = None - result = filemod.check_file_meta(name, name, source, source_sum, - 'root', 'root', '755', None, - 'base') + result = filemod.check_file_meta( + name, name, source, source_sum, "root", "root", "755", None, "base" + ) self.assertTrue(result, None) - @skipIf(salt.utils.platform.is_windows(), 'SED is not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "SED is not available on Windows") def test_sed_limit_escaped(self): - with tempfile.NamedTemporaryFile(mode='w+') as tfile: + with tempfile.NamedTemporaryFile(mode="w+") as tfile: tfile.write(SED_CONTENT) tfile.seek(0, 0) path = tfile.name - before = '/var/lib/foo' - after = '' - limit = '^{0}'.format(before) + before = "/var/lib/foo" + after = "" + limit = "^{0}".format(before) filemod.sed(path, before, after, limit=limit) - with salt.utils.files.fopen(path, 'r') as newfile: + with salt.utils.files.fopen(path, "r") as newfile: self.assertEqual( - SED_CONTENT.replace(before, ''), - salt.utils.stringutils.to_unicode(newfile.read()) + SED_CONTENT.replace(before, ""), + salt.utils.stringutils.to_unicode(newfile.read()), ) def test_append_newline_at_eof(self): - ''' + """ Check that file.append works consistently on files with and without newlines at end of file. - ''' + """ # File ending with a newline - with tempfile.NamedTemporaryFile(mode='wb', delete=False) as tfile: - tfile.write(salt.utils.stringutils.to_bytes('foo' + os.linesep)) + with tempfile.NamedTemporaryFile(mode="wb", delete=False) as tfile: + tfile.write(salt.utils.stringutils.to_bytes("foo" + os.linesep)) tfile.flush() - filemod.append(tfile.name, 'bar') - expected = os.linesep.join(['foo', 'bar', '']) + filemod.append(tfile.name, "bar") + expected = os.linesep.join(["foo", "bar", ""]) with salt.utils.files.fopen(tfile.name) as tfile2: new_file = salt.utils.stringutils.to_unicode(tfile2.read()) self.assertEqual(new_file, expected) # File not ending with a newline - with tempfile.NamedTemporaryFile(mode='wb', delete=False) as tfile: - tfile.write(salt.utils.stringutils.to_bytes('foo')) + with tempfile.NamedTemporaryFile(mode="wb", delete=False) as tfile: + tfile.write(salt.utils.stringutils.to_bytes("foo")) tfile.flush() - filemod.append(tfile.name, 'bar') + filemod.append(tfile.name, "bar") with salt.utils.files.fopen(tfile.name) as tfile2: - self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), expected) + self.assertEqual(salt.utils.stringutils.to_unicode(tfile2.read()), expected) # A newline should be added in empty files - with tempfile.NamedTemporaryFile(mode='wb', delete=False) as tfile: - filemod.append(tfile.name, salt.utils.stringutils.to_str('bar')) + with tempfile.NamedTemporaryFile(mode="wb", delete=False) as tfile: + filemod.append(tfile.name, salt.utils.stringutils.to_str("bar")) with salt.utils.files.fopen(tfile.name) as tfile2: self.assertEqual( - salt.utils.stringutils.to_unicode(tfile2.read()), - 'bar' + os.linesep + salt.utils.stringutils.to_unicode(tfile2.read()), "bar" + os.linesep ) def test_extract_hash(self): - ''' + """ Check various hash file formats. - ''' + """ # With file name - with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as tfile: - tfile.write(salt.utils.stringutils.to_bytes( - 'rc.conf ef6e82e4006dee563d98ada2a2a80a27\n' - 'ead48423703509d37c4a90e6a0d53e143b6fc268 example.tar.gz\n' - 'fe05bcdcdc4928012781a5f1a2a77cbb5398e106 ./subdir/example.tar.gz\n' - 'ad782ecdac770fc6eb9a62e44f90873fb97fb26b foo.tar.bz2\n' - )) + with tempfile.NamedTemporaryFile(mode="w+b", delete=False) as tfile: + tfile.write( + salt.utils.stringutils.to_bytes( + "rc.conf ef6e82e4006dee563d98ada2a2a80a27\n" + "ead48423703509d37c4a90e6a0d53e143b6fc268 example.tar.gz\n" + "fe05bcdcdc4928012781a5f1a2a77cbb5398e106 ./subdir/example.tar.gz\n" + "ad782ecdac770fc6eb9a62e44f90873fb97fb26b foo.tar.bz2\n" + ) + ) tfile.flush() - result = filemod.extract_hash(tfile.name, '', '/rc.conf') - self.assertEqual(result, { - 'hsum': 'ef6e82e4006dee563d98ada2a2a80a27', - 'hash_type': 'md5' - }) + result = filemod.extract_hash(tfile.name, "", "/rc.conf") + self.assertEqual( + result, {"hsum": "ef6e82e4006dee563d98ada2a2a80a27", "hash_type": "md5"} + ) - result = filemod.extract_hash(tfile.name, '', '/example.tar.gz') - self.assertEqual(result, { - 'hsum': 'ead48423703509d37c4a90e6a0d53e143b6fc268', - 'hash_type': 'sha1' - }) + result = filemod.extract_hash(tfile.name, "", "/example.tar.gz") + self.assertEqual( + result, + {"hsum": "ead48423703509d37c4a90e6a0d53e143b6fc268", "hash_type": "sha1"}, + ) # All the checksums in this test file are sha1 sums. We run this # loop three times. The first pass tests auto-detection of hash # type by length of the hash. The second tests matching a specific # type. The third tests a failed attempt to match a specific type, # since sha256 was requested but sha1 is what is in the file. - for hash_type in ('', 'sha1', 'sha256'): + for hash_type in ("", "sha1", "sha256"): # Test the source_hash_name argument. Even though there are # matches in the source_hash file for both the file_name and # source params, they should be ignored in favor of the # source_hash_name. - file_name = '/example.tar.gz' - source = 'https://mydomain.tld/foo.tar.bz2?key1=val1&key2=val2' - source_hash_name = './subdir/example.tar.gz' + file_name = "/example.tar.gz" + source = "https://mydomain.tld/foo.tar.bz2?key1=val1&key2=val2" + source_hash_name = "./subdir/example.tar.gz" result = filemod.extract_hash( - tfile.name, - hash_type, - file_name, - source, - source_hash_name) - expected = { - 'hsum': 'fe05bcdcdc4928012781a5f1a2a77cbb5398e106', - 'hash_type': 'sha1' - } if hash_type != 'sha256' else None + tfile.name, hash_type, file_name, source, source_hash_name + ) + expected = ( + { + "hsum": "fe05bcdcdc4928012781a5f1a2a77cbb5398e106", + "hash_type": "sha1", + } + if hash_type != "sha256" + else None + ) self.assertEqual(result, expected) # Test both a file_name and source but no source_hash_name. # Even though there are matches for both file_name and # source_hash_name, file_name should be preferred. - file_name = '/example.tar.gz' - source = 'https://mydomain.tld/foo.tar.bz2?key1=val1&key2=val2' + file_name = "/example.tar.gz" + source = "https://mydomain.tld/foo.tar.bz2?key1=val1&key2=val2" source_hash_name = None result = filemod.extract_hash( - tfile.name, - hash_type, - file_name, - source, - source_hash_name) - expected = { - 'hsum': 'ead48423703509d37c4a90e6a0d53e143b6fc268', - 'hash_type': 'sha1' - } if hash_type != 'sha256' else None + tfile.name, hash_type, file_name, source, source_hash_name + ) + expected = ( + { + "hsum": "ead48423703509d37c4a90e6a0d53e143b6fc268", + "hash_type": "sha1", + } + if hash_type != "sha256" + else None + ) self.assertEqual(result, expected) # Test both a file_name and source but no source_hash_name. # Since there is no match for the file_name, the source is # matched. - file_name = '/somefile.tar.gz' - source = 'https://mydomain.tld/foo.tar.bz2?key1=val1&key2=val2' + file_name = "/somefile.tar.gz" + source = "https://mydomain.tld/foo.tar.bz2?key1=val1&key2=val2" source_hash_name = None result = filemod.extract_hash( - tfile.name, - hash_type, - file_name, - source, - source_hash_name) - expected = { - 'hsum': 'ad782ecdac770fc6eb9a62e44f90873fb97fb26b', - 'hash_type': 'sha1' - } if hash_type != 'sha256' else None + tfile.name, hash_type, file_name, source, source_hash_name + ) + expected = ( + { + "hsum": "ad782ecdac770fc6eb9a62e44f90873fb97fb26b", + "hash_type": "sha1", + } + if hash_type != "sha256" + else None + ) self.assertEqual(result, expected) # Hash only, no file name (Maven repo checksum format) # Since there is no name match, the first checksum in the file will # always be returned, never the second. - with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as tfile: - tfile.write(salt.utils.stringutils.to_bytes( - 'ead48423703509d37c4a90e6a0d53e143b6fc268\n' - 'ad782ecdac770fc6eb9a62e44f90873fb97fb26b\n')) + with tempfile.NamedTemporaryFile(mode="w+b", delete=False) as tfile: + tfile.write( + salt.utils.stringutils.to_bytes( + "ead48423703509d37c4a90e6a0d53e143b6fc268\n" + "ad782ecdac770fc6eb9a62e44f90873fb97fb26b\n" + ) + ) tfile.flush() - for hash_type in ('', 'sha1', 'sha256'): - result = filemod.extract_hash(tfile.name, hash_type, '/testfile') - expected = { - 'hsum': 'ead48423703509d37c4a90e6a0d53e143b6fc268', - 'hash_type': 'sha1' - } if hash_type != 'sha256' else None + for hash_type in ("", "sha1", "sha256"): + result = filemod.extract_hash(tfile.name, hash_type, "/testfile") + expected = ( + { + "hsum": "ead48423703509d37c4a90e6a0d53e143b6fc268", + "hash_type": "sha1", + } + if hash_type != "sha256" + else None + ) self.assertEqual(result, expected) def test_user_to_uid_int(self): - ''' + """ Tests if user is passed as an integer - ''' + """ user = 5034 ret = filemod.user_to_uid(user) self.assertEqual(ret, user) def test_group_to_gid_int(self): - ''' + """ Tests if group is passed as an integer - ''' + """ group = 5034 ret = filemod.group_to_gid(group) self.assertEqual(ret, group) def test_patch(self): - with patch('os.path.isdir', return_value=False) as mock_isdir, \ - patch('salt.utils.path.which', return_value='/bin/patch') as mock_which: - cmd_mock = MagicMock(return_value='test_retval') - with patch.dict(filemod.__salt__, {'cmd.run_all': cmd_mock}): - ret = filemod.patch('/path/to/file', '/path/to/patch') - cmd = ['/bin/patch', '--forward', '--reject-file=-', - '-i', '/path/to/patch', '/path/to/file'] + with patch("os.path.isdir", return_value=False) as mock_isdir, patch( + "salt.utils.path.which", return_value="/bin/patch" + ) as mock_which: + cmd_mock = MagicMock(return_value="test_retval") + with patch.dict(filemod.__salt__, {"cmd.run_all": cmd_mock}): + ret = filemod.patch("/path/to/file", "/path/to/patch") + cmd = [ + "/bin/patch", + "--forward", + "--reject-file=-", + "-i", + "/path/to/patch", + "/path/to/file", + ] cmd_mock.assert_called_once_with(cmd, python_shell=False) - self.assertEqual('test_retval', ret) + self.assertEqual("test_retval", ret) def test_patch_dry_run(self): - with patch('os.path.isdir', return_value=False) as mock_isdir, \ - patch('salt.utils.path.which', return_value='/bin/patch') as mock_which: - cmd_mock = MagicMock(return_value='test_retval') - with patch.dict(filemod.__salt__, {'cmd.run_all': cmd_mock}): - ret = filemod.patch('/path/to/file', '/path/to/patch', dry_run=True) - cmd = ['/bin/patch', '--dry-run', '--forward', '--reject-file=-', - '-i', '/path/to/patch', '/path/to/file'] + with patch("os.path.isdir", return_value=False) as mock_isdir, patch( + "salt.utils.path.which", return_value="/bin/patch" + ) as mock_which: + cmd_mock = MagicMock(return_value="test_retval") + with patch.dict(filemod.__salt__, {"cmd.run_all": cmd_mock}): + ret = filemod.patch("/path/to/file", "/path/to/patch", dry_run=True) + cmd = [ + "/bin/patch", + "--dry-run", + "--forward", + "--reject-file=-", + "-i", + "/path/to/patch", + "/path/to/file", + ] cmd_mock.assert_called_once_with(cmd, python_shell=False) - self.assertEqual('test_retval', ret) + self.assertEqual("test_retval", ret) def test_patch_dir(self): - with patch('os.path.isdir', return_value=True) as mock_isdir, \ - patch('salt.utils.path.which', return_value='/bin/patch') as mock_which: - cmd_mock = MagicMock(return_value='test_retval') - with patch.dict(filemod.__salt__, {'cmd.run_all': cmd_mock}): - ret = filemod.patch('/path/to/dir', '/path/to/patch') - cmd = ['/bin/patch', '--forward', '--reject-file=-', - '-i', '/path/to/patch', '-d', '/path/to/dir', '--strip=0'] + with patch("os.path.isdir", return_value=True) as mock_isdir, patch( + "salt.utils.path.which", return_value="/bin/patch" + ) as mock_which: + cmd_mock = MagicMock(return_value="test_retval") + with patch.dict(filemod.__salt__, {"cmd.run_all": cmd_mock}): + ret = filemod.patch("/path/to/dir", "/path/to/patch") + cmd = [ + "/bin/patch", + "--forward", + "--reject-file=-", + "-i", + "/path/to/patch", + "-d", + "/path/to/dir", + "--strip=0", + ] cmd_mock.assert_called_once_with(cmd, python_shell=False) - self.assertEqual('test_retval', ret) + self.assertEqual("test_retval", ret) def test_apply_template_on_contents(self): - ''' + """ Tests that the templating engine works on string contents - ''' - contents = 'This is a {{ template }}.' - defaults = {'template': 'templated file'} - with patch.object(SaltCacheLoader, 'file_client', Mock()): + """ + contents = "This is a {{ template }}." + defaults = {"template": "templated file"} + with patch.object(SaltCacheLoader, "file_client", Mock()): ret = filemod.apply_template_on_contents( contents, - template='jinja', - context={'opts': filemod.__opts__}, + template="jinja", + context={"opts": filemod.__opts__}, defaults=defaults, - saltenv='base') - self.assertEqual(ret, 'This is a templated file.') + saltenv="base", + ) + self.assertEqual(ret, "This is a templated file.") def test_get_diff(self): - text1 = textwrap.dedent('''\ + text1 = textwrap.dedent( + """\ foo bar baz спам - ''') - text2 = textwrap.dedent('''\ + """ + ) + text2 = textwrap.dedent( + """\ foo bar baz яйца - ''') - diff_result = textwrap.dedent('''\ + """ + ) + diff_result = textwrap.dedent( + """\ --- text1 +++ text2 @@ -1,4 +1,4 @@ @@ -1108,28 +1183,32 @@ class FileModuleTestCase(TestCase, LoaderModuleMockMixin): baz -спам +яйца - ''') + """ + ) # The below two variables are 8 bytes of data pulled from /dev/urandom - binary1 = b'\xd4\xb2\xa6W\xc6\x8e\xf5\x0f' - binary2 = b',\x13\x04\xa5\xb0\x12\xdf%' + binary1 = b"\xd4\xb2\xa6W\xc6\x8e\xf5\x0f" + binary2 = b",\x13\x04\xa5\xb0\x12\xdf%" # pylint: disable=no-self-argument class MockFopen(object): - ''' + """ Provides a fake filehandle object that has just enough to run readlines() as file.get_diff does. Any significant changes to file.get_diff may require this class to be modified. - ''' - def __init__(mockself, path, *args, **kwargs): # pylint: disable=unused-argument + """ + + def __init__( + mockself, path, *args, **kwargs + ): # pylint: disable=unused-argument mockself.path = path def readlines(mockself): # pylint: disable=unused-argument return { - 'text1': text1.encode('utf8'), - 'text2': text2.encode('utf8'), - 'binary1': binary1, - 'binary2': binary2, + "text1": text1.encode("utf8"), + "text2": text2.encode("utf8"), + "binary1": binary1, + "binary2": binary2, }[mockself.path].splitlines(True) def __enter__(mockself): @@ -1137,10 +1216,11 @@ class FileModuleTestCase(TestCase, LoaderModuleMockMixin): def __exit__(mockself, *args): # pylint: disable=unused-argument pass + # pylint: enable=no-self-argument fopen = MagicMock(side_effect=lambda x, *args, **kwargs: MockFopen(x)) - cache_file = MagicMock(side_effect=lambda x, *args, **kwargs: x.split('/')[-1]) + cache_file = MagicMock(side_effect=lambda x, *args, **kwargs: x.split("/")[-1]) # Mocks for __utils__['files.is_text'] mock_text_text = MagicMock(side_effect=[True, True]) @@ -1148,68 +1228,73 @@ class FileModuleTestCase(TestCase, LoaderModuleMockMixin): mock_text_bin = MagicMock(side_effect=[True, False]) mock_bin_text = MagicMock(side_effect=[False, True]) - with patch.dict(filemod.__salt__, {'cp.cache_file': cache_file}), \ - patch.object(salt.utils.files, 'fopen', fopen): + with patch.dict(filemod.__salt__, {"cp.cache_file": cache_file}), patch.object( + salt.utils.files, "fopen", fopen + ): # Test diffing two text files - with patch.dict(filemod.__utils__, {'files.is_text': mock_text_text}): + with patch.dict(filemod.__utils__, {"files.is_text": mock_text_text}): # Identical files - ret = filemod.get_diff('text1', 'text1') - self.assertEqual(ret, '') + ret = filemod.get_diff("text1", "text1") + self.assertEqual(ret, "") # Non-identical files - ret = filemod.get_diff('text1', 'text2') + ret = filemod.get_diff("text1", "text2") self.assertEqual(ret, diff_result) # Repeat the above test with remote file paths. The expectation # is that the cp.cache_file mock will ensure that we are not # trying to do an fopen on the salt:// URL, but rather the # "cached" file path we've mocked. - with patch.object(filemod, '_binary_replace', - MagicMock(return_value='')): - ret = filemod.get_diff('salt://text1', 'salt://text1') - self.assertEqual(ret, '') - ret = filemod.get_diff('salt://text1', 'salt://text2') + with patch.object( + filemod, "_binary_replace", MagicMock(return_value="") + ): + ret = filemod.get_diff("salt://text1", "salt://text1") + self.assertEqual(ret, "") + ret = filemod.get_diff("salt://text1", "salt://text2") self.assertEqual(ret, diff_result) # Test diffing two binary files - with patch.dict(filemod.__utils__, {'files.is_text': mock_bin_bin}): + with patch.dict(filemod.__utils__, {"files.is_text": mock_bin_bin}): # Identical files - ret = filemod.get_diff('binary1', 'binary1') - self.assertEqual(ret, '') + ret = filemod.get_diff("binary1", "binary1") + self.assertEqual(ret, "") # Non-identical files - ret = filemod.get_diff('binary1', 'binary2') - self.assertEqual(ret, 'Replace binary file') + ret = filemod.get_diff("binary1", "binary2") + self.assertEqual(ret, "Replace binary file") # Test diffing a text file with a binary file - with patch.dict(filemod.__utils__, {'files.is_text': mock_text_bin}): + with patch.dict(filemod.__utils__, {"files.is_text": mock_text_bin}): - ret = filemod.get_diff('text1', 'binary1') - self.assertEqual(ret, 'Replace text file with binary file') + ret = filemod.get_diff("text1", "binary1") + self.assertEqual(ret, "Replace text file with binary file") # Test diffing a binary file with a text file - with patch.dict(filemod.__utils__, {'files.is_text': mock_bin_text}): + with patch.dict(filemod.__utils__, {"files.is_text": mock_bin_text}): - ret = filemod.get_diff('binary1', 'text1') - self.assertEqual(ret, 'Replace binary file with text file') + ret = filemod.get_diff("binary1", "text1") + self.assertEqual(ret, "Replace binary file with text file") def test_stats(self): - with patch('os.path.expanduser', MagicMock(side_effect=lambda path: path)), \ - patch('os.path.exists', MagicMock(return_value=True)), \ - patch('os.stat', MagicMock(return_value=DummyStat())): - ret = filemod.stats('dummy', None, True) - self.assertEqual(ret['mode'], '0644') - self.assertEqual(ret['type'], 'file') + with patch( + "os.path.expanduser", MagicMock(side_effect=lambda path: path) + ), patch("os.path.exists", MagicMock(return_value=True)), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ): + ret = filemod.stats("dummy", None, True) + self.assertEqual(ret["mode"], "0644") + self.assertEqual(ret["type"], "file") -@skipIf(pytest is None, 'PyTest required for this set of tests') +@skipIf(pytest is None, "PyTest required for this set of tests") class FilemodLineTests(TestCase, LoaderModuleMockMixin): - ''' + """ Unit tests for file.line - ''' + """ + def setUp(self): class AnyAttr(object): def __getattr__(self, item): @@ -1217,6 +1302,7 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): def __call__(self, *args, **kwargs): return self + self._anyattr = AnyAttr() def tearDown(self): @@ -1225,162 +1311,162 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { filemod: { - '__salt__': { - 'config.manage_mode': configmod.manage_mode, - 'cmd.run': cmdmod.run, - 'cmd.run_all': cmdmod.run_all + "__salt__": { + "config.manage_mode": configmod.manage_mode, + "cmd.run": cmdmod.run, + "cmd.run_all": cmdmod.run_all, }, - '__opts__': { - 'test': False, - 'file_roots': {'base': 'tmp'}, - 'pillar_roots': {'base': 'tmp'}, - 'cachedir': 'tmp', - 'grains': {}, - }, - '__grains__': {'kernel': 'Linux'}, - '__utils__': { - 'stringutils.get_diff': salt.utils.stringutils.get_diff, + "__opts__": { + "test": False, + "file_roots": {"base": "tmp"}, + "pillar_roots": {"base": "tmp"}, + "cachedir": "tmp", + "grains": {}, }, + "__grains__": {"kernel": "Linux"}, + "__utils__": {"stringutils.get_diff": salt.utils.stringutils.get_diff}, } } @staticmethod def _get_body(content): - ''' + """ The body is written as bytestrings or strings depending on platform. This func accepts a string of content and returns the appropriate list of strings back. - ''' + """ ret = content.splitlines(True) if six.PY2 and salt.utils.platform.is_windows(): return salt.utils.data.encode_list(ret) else: return salt.utils.data.decode_list(ret, to_str=True) - @patch('os.path.realpath', MagicMock(wraps=lambda x: x)) - @patch('os.path.isfile', MagicMock(return_value=True)) + @patch("os.path.realpath", MagicMock(wraps=lambda x: x)) + @patch("os.path.isfile", MagicMock(return_value=True)) def test_delete_line_in_empty_file(self): - ''' + """ Tests that when calling file.line with ``mode=delete``, the function doesn't stack trace if the file is empty. Should return ``False``. See Issue #38438. - ''' - for mode in ['delete', 'replace']: + """ + for mode in ["delete", "replace"]: _log = MagicMock() - with patch('salt.utils.files.fopen', mock_open(read_data='')), \ - patch('os.stat', self._anyattr), \ - patch('salt.modules.file.log', _log): - self.assertFalse(filemod.line('/dummy/path', content='foo', match='bar', mode=mode)) - self.assertIn('Cannot find text to {0}'.format(mode), - _log.warning.call_args_list[0][0][0]) + with patch("salt.utils.files.fopen", mock_open(read_data="")), patch( + "os.stat", self._anyattr + ), patch("salt.modules.file.log", _log): + self.assertFalse( + filemod.line("/dummy/path", content="foo", match="bar", mode=mode) + ) + self.assertIn( + "Cannot find text to {0}".format(mode), + _log.warning.call_args_list[0][0][0], + ) - @patch('os.path.realpath', MagicMock()) - @patch('os.path.isfile', MagicMock(return_value=True)) - @patch('os.stat', MagicMock()) + @patch("os.path.realpath", MagicMock()) + @patch("os.path.isfile", MagicMock(return_value=True)) + @patch("os.stat", MagicMock()) def test_line_delete_no_match(self): - ''' + """ Tests that when calling file.line with ``mode=delete``, with not matching pattern to delete returns False :return: - ''' - file_content = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/custom' - ]) - match = 'not matching' - for mode in ['delete', 'replace']: + """ + file_content = os.linesep.join( + ["file_roots:", " base:", " - /srv/salt", " - /srv/custom"] + ) + match = "not matching" + for mode in ["delete", "replace"]: files_fopen = mock_open(read_data=file_content) - with patch('salt.utils.files.fopen', files_fopen): + with patch("salt.utils.files.fopen", files_fopen): atomic_opener = mock_open() - with patch('salt.utils.atomicfile.atomic_open', atomic_opener): - self.assertFalse(filemod.line('foo', content='foo', match=match, mode=mode)) + with patch("salt.utils.atomicfile.atomic_open", atomic_opener): + self.assertFalse( + filemod.line("foo", content="foo", match=match, mode=mode) + ) - @patch('os.path.realpath', MagicMock(wraps=lambda x: x)) - @patch('os.path.isfile', MagicMock(return_value=True)) + @patch("os.path.realpath", MagicMock(wraps=lambda x: x)) + @patch("os.path.isfile", MagicMock(return_value=True)) def test_line_modecheck_failure(self): - ''' + """ Test for file.line for empty or wrong mode. Calls unknown or empty mode and expects failure. :return: - ''' - for mode, err_msg in [(None, 'How to process the file'), ('nonsense', 'Unknown mode')]: + """ + for mode, err_msg in [ + (None, "How to process the file"), + ("nonsense", "Unknown mode"), + ]: with pytest.raises(CommandExecutionError) as exc_info: - filemod.line('foo', mode=mode) + filemod.line("foo", mode=mode) self.assertIn(err_msg, six.text_type(exc_info.value)) - @patch('os.path.realpath', MagicMock(wraps=lambda x: x)) - @patch('os.path.isfile', MagicMock(return_value=True)) + @patch("os.path.realpath", MagicMock(wraps=lambda x: x)) + @patch("os.path.isfile", MagicMock(return_value=True)) def test_line_no_content(self): - ''' + """ Test for file.line for an empty content when not deleting anything. :return: - ''' - for mode in ['insert', 'ensure', 'replace']: + """ + for mode in ["insert", "ensure", "replace"]: with pytest.raises(CommandExecutionError) as exc_info: - filemod.line('foo', mode=mode) - self.assertIn('Content can only be empty if mode is "delete"', - six.text_type(exc_info.value)) + filemod.line("foo", mode=mode) + self.assertIn( + 'Content can only be empty if mode is "delete"', + six.text_type(exc_info.value), + ) - @patch('os.path.realpath', MagicMock(wraps=lambda x: x)) - @patch('os.path.isfile', MagicMock(return_value=True)) - @patch('os.stat', MagicMock()) + @patch("os.path.realpath", MagicMock(wraps=lambda x: x)) + @patch("os.path.isfile", MagicMock(return_value=True)) + @patch("os.stat", MagicMock()) def test_line_insert_no_location_no_before_no_after(self): - ''' + """ Test for file.line for insertion but define no location/before/after. :return: - ''' - files_fopen = mock_open(read_data='test data') - with patch('salt.utils.files.fopen', files_fopen): + """ + files_fopen = mock_open(read_data="test data") + with patch("salt.utils.files.fopen", files_fopen): with pytest.raises(CommandExecutionError) as exc_info: - filemod.line('foo', content='test content', mode='insert') - self.assertIn('"location" or "before/after"', - six.text_type(exc_info.value)) + filemod.line("foo", content="test content", mode="insert") + self.assertIn('"location" or "before/after"', six.text_type(exc_info.value)) def test_util_starts_till(self): - ''' + """ Test for file._starts_till function. :return: - ''' - src = 'here is something' + """ + src = "here is something" self.assertEqual( - filemod._starts_till(src=src, probe='here quite something else'), 1) + filemod._starts_till(src=src, probe="here quite something else"), 1 + ) + self.assertEqual(filemod._starts_till(src=src, probe="here is something"), 0) self.assertEqual( - filemod._starts_till(src=src, probe='here is something'), 0) - self.assertEqual( - filemod._starts_till(src=src, probe='and here is something'), -1) + filemod._starts_till(src=src, probe="and here is something"), -1 + ) @with_tempfile() def test_line_insert_after_no_pattern(self, name): - ''' + """ Test for file.line for insertion after specific line, using no pattern. See issue #38670 :return: - ''' - file_content = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt' - ]) - file_modified = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/custom' - ]) - cfg_content = '- /srv/custom' + """ + file_content = os.linesep.join(["file_roots:", " base:", " - /srv/salt"]) + file_modified = os.linesep.join( + ["file_roots:", " base:", " - /srv/salt", " - /srv/custom"] + ) + cfg_content = "- /srv/custom" isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, after='- /srv/salt', mode='insert') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", mock_open(read_data=file_content)), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line(name, content=cfg_content, after="- /srv/salt", mode="insert") handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1395,43 +1481,51 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): @with_tempfile() def test_line_insert_after_pattern(self, name): - ''' + """ Test for file.line for insertion after specific line, using pattern. See issue #38670 :return: - ''' - file_content = os.linesep.join([ - 'file_boots:', - ' - /rusty', - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/sugar' - ]) - file_modified = os.linesep.join([ - 'file_boots:', - ' - /rusty', - 'file_roots:', - ' custom:', - ' - /srv/custom', - ' base:', - ' - /srv/salt', - ' - /srv/sugar' - ]) - cfg_content = os.linesep.join([ - ' custom:', - ' - /srv/custom' - ]) + """ + file_content = os.linesep.join( + [ + "file_boots:", + " - /rusty", + "file_roots:", + " base:", + " - /srv/salt", + " - /srv/sugar", + ] + ) + file_modified = os.linesep.join( + [ + "file_boots:", + " - /rusty", + "file_roots:", + " custom:", + " - /srv/custom", + " base:", + " - /srv/salt", + " - /srv/sugar", + ] + ) + cfg_content = os.linesep.join([" custom:", " - /srv/custom"]) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - for after_line in ['file_r.*', '.*roots']: - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, after=after_line, mode='insert', indent=False) + for after_line in ["file_r.*", ".*roots"]: + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch( + "salt.utils.files.fopen", mock_open(read_data=file_content) + ), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line( + name, + content=cfg_content, + after=after_line, + mode="insert", + indent=False, + ) handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1446,33 +1540,42 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): # will be written as two lines in the same element of the list # passed to .writelines() expected[3] = expected[3] + expected.pop(4) - assert writelines_content[0] == expected, (writelines_content[0], expected) + assert writelines_content[0] == expected, ( + writelines_content[0], + expected, + ) @with_tempfile() def test_line_insert_multi_line_content_after_unicode(self, name): - ''' + """ Test for file.line for insertion after specific line with Unicode See issue #48113 :return: - ''' - file_content = 'This is a line{}This is another line'.format(os.linesep) - file_modified = salt.utils.stringutils.to_str('This is a line{}' - 'This is another line{}' - 'This is a line with unicode Ŷ'.format( - os.linesep, os.linesep - ) - ) + """ + file_content = "This is a line{}This is another line".format(os.linesep) + file_modified = salt.utils.stringutils.to_str( + "This is a line{}" + "This is another line{}" + "This is a line with unicode Ŷ".format(os.linesep, os.linesep) + ) cfg_content = "This is a line with unicode Ŷ" isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - for after_line in ['This is another line']: - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, after=after_line, mode='insert', indent=False) + for after_line in ["This is another line"]: + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch( + "salt.utils.files.fopen", mock_open(read_data=file_content) + ), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line( + name, + content=cfg_content, + after=after_line, + mode="insert", + indent=False, + ) handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1483,40 +1586,45 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): assert writelines_count == 1, writelines_count # ... with the updated content expected = self._get_body(file_modified) - assert writelines_content[0] == expected, (writelines_content[0], expected) + assert writelines_content[0] == expected, ( + writelines_content[0], + expected, + ) @with_tempfile() def test_line_insert_before(self, name): - ''' + """ Test for file.line for insertion before specific line, using pattern and no patterns. See issue #38670 :return: - ''' - file_content = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/sugar' - ]) - file_modified = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/custom', - ' - /srv/salt', - ' - /srv/sugar' - ]) - cfg_content = '- /srv/custom' + """ + file_content = os.linesep.join( + ["file_roots:", " base:", " - /srv/salt", " - /srv/sugar"] + ) + file_modified = os.linesep.join( + [ + "file_roots:", + " base:", + " - /srv/custom", + " - /srv/salt", + " - /srv/sugar", + ] + ) + cfg_content = "- /srv/custom" isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - for before_line in ['/srv/salt', '/srv/sa.*t']: - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, before=before_line, mode='insert') + for before_line in ["/srv/salt", "/srv/sa.*t"]: + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch( + "salt.utils.files.fopen", mock_open(read_data=file_content) + ), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line( + name, content=cfg_content, before=before_line, mode="insert" + ) handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1527,68 +1635,86 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): assert writelines_count == 1, writelines_count # ... with the updated content expected = self._get_body(file_modified) - assert writelines_content[0] == expected, (writelines_content[0], expected) + assert writelines_content[0] == expected, ( + writelines_content[0], + expected, + ) - @patch('os.path.realpath', MagicMock(wraps=lambda x: x)) - @patch('os.path.isfile', MagicMock(return_value=True)) - @patch('os.stat', MagicMock()) + @patch("os.path.realpath", MagicMock(wraps=lambda x: x)) + @patch("os.path.isfile", MagicMock(return_value=True)) + @patch("os.stat", MagicMock()) def test_line_assert_exception_pattern(self): - ''' + """ Test for file.line for exception on insert with too general pattern. :return: - ''' - file_content = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/sugar' - ]) - cfg_content = '- /srv/custom' - for before_line in ['/sr.*']: + """ + file_content = os.linesep.join( + ["file_roots:", " base:", " - /srv/salt", " - /srv/sugar"] + ) + cfg_content = "- /srv/custom" + for before_line in ["/sr.*"]: files_fopen = mock_open(read_data=file_content) - with patch('salt.utils.files.fopen', files_fopen): + with patch("salt.utils.files.fopen", files_fopen): atomic_opener = mock_open() - with patch('salt.utils.atomicfile.atomic_open', atomic_opener): + with patch("salt.utils.atomicfile.atomic_open", atomic_opener): with self.assertRaises(CommandExecutionError) as cm: - filemod.line('foo', content=cfg_content, before=before_line, mode='insert') - self.assertEqual(cm.exception.strerror, - 'Found more than expected occurrences in "before" expression') + filemod.line( + "foo", + content=cfg_content, + before=before_line, + mode="insert", + ) + self.assertEqual( + cm.exception.strerror, + 'Found more than expected occurrences in "before" expression', + ) @with_tempfile() def test_line_insert_before_after(self, name): - ''' + """ Test for file.line for insertion before specific line, using pattern and no patterns. See issue #38670 :return: - ''' - file_content = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/pepper', - ' - /srv/sugar' - ]) - file_modified = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/pepper', - ' - /srv/coriander', - ' - /srv/sugar' - ]) - cfg_content = '- /srv/coriander' + """ + file_content = os.linesep.join( + [ + "file_roots:", + " base:", + " - /srv/salt", + " - /srv/pepper", + " - /srv/sugar", + ] + ) + file_modified = os.linesep.join( + [ + "file_roots:", + " base:", + " - /srv/salt", + " - /srv/pepper", + " - /srv/coriander", + " - /srv/sugar", + ] + ) + cfg_content = "- /srv/coriander" isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - for b_line, a_line in [('/srv/sugar', '/srv/salt')]: - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, before=b_line, after=a_line, mode='insert') + for b_line, a_line in [("/srv/sugar", "/srv/salt")]: + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch( + "salt.utils.files.fopen", mock_open(read_data=file_content) + ), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line( + name, + content=cfg_content, + before=b_line, + after=a_line, + mode="insert", + ) handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1599,37 +1725,38 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): assert writelines_count == 1, writelines_count # ... with the updated content expected = self._get_body(file_modified) - assert writelines_content[0] == expected, (writelines_content[0], expected) + assert writelines_content[0] == expected, ( + writelines_content[0], + expected, + ) @with_tempfile() def test_line_insert_start(self, name): - ''' + """ Test for file.line for insertion at the beginning of the file :return: - ''' - cfg_content = 'everything: fantastic' - file_content = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/sugar' - ]) - file_modified = os.linesep.join([ - cfg_content, - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/sugar' - ]) + """ + cfg_content = "everything: fantastic" + file_content = os.linesep.join( + ["file_roots:", " base:", " - /srv/salt", " - /srv/sugar"] + ) + file_modified = os.linesep.join( + [ + cfg_content, + "file_roots:", + " base:", + " - /srv/salt", + " - /srv/sugar", + ] + ) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, location='start', mode='insert') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", mock_open(read_data=file_content)), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line(name, content=cfg_content, location="start", mode="insert") handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1644,33 +1771,31 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): @with_tempfile() def test_line_insert_end(self, name): - ''' + """ Test for file.line for insertion at the end of the file (append) :return: - ''' - cfg_content = 'everything: fantastic' - file_content = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/sugar' - ]) - file_modified = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/sugar', - ' ' + cfg_content - ]) + """ + cfg_content = "everything: fantastic" + file_content = os.linesep.join( + ["file_roots:", " base:", " - /srv/salt", " - /srv/sugar"] + ) + file_modified = os.linesep.join( + [ + "file_roots:", + " base:", + " - /srv/salt", + " - /srv/sugar", + " " + cfg_content, + ] + ) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, location='end', mode='insert') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", mock_open(read_data=file_content)), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line(name, content=cfg_content, location="end", mode="insert") handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1685,31 +1810,21 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): @with_tempfile() def test_line_insert_ensure_before(self, name): - ''' + """ Test for file.line for insertion ensuring the line is before :return: - ''' - cfg_content = '/etc/init.d/someservice restart' - file_content = os.linesep.join([ - '#!/bin/bash', - '', - 'exit 0' - ]) - file_modified = os.linesep.join([ - '#!/bin/bash', - '', - cfg_content, - 'exit 0' - ]) + """ + cfg_content = "/etc/init.d/someservice restart" + file_content = os.linesep.join(["#!/bin/bash", "", "exit 0"]) + file_modified = os.linesep.join(["#!/bin/bash", "", cfg_content, "exit 0"]) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, before='exit 0', mode='ensure') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", mock_open(read_data=file_content)), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line(name, content=cfg_content, before="exit 0", mode="ensure") handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1724,60 +1839,48 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): @with_tempfile() def test_line_insert_duplicate_ensure_before(self, name): - ''' + """ Test for file.line for insertion ensuring the line is before :return: - ''' - cfg_content = '/etc/init.d/someservice restart' - file_content = os.linesep.join([ - '#!/bin/bash', - '', - cfg_content, - 'exit 0' - ]) - file_modified = os.linesep.join([ - '#!/bin/bash', - '', - cfg_content, - 'exit 0' - ]) + """ + cfg_content = "/etc/init.d/someservice restart" + file_content = os.linesep.join(["#!/bin/bash", "", cfg_content, "exit 0"]) + file_modified = os.linesep.join(["#!/bin/bash", "", cfg_content, "exit 0"]) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, before='exit 0', mode='ensure') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", mock_open(read_data=file_content)), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line(name, content=cfg_content, before="exit 0", mode="ensure") # If file not modified no handlers in dict assert atomic_open_mock.filehandles.get(name) is None @with_tempfile() def test_line_insert_ensure_before_first_line(self, name): - ''' + """ Test for file.line for insertion ensuring the line is before first line :return: - ''' - cfg_content = '#!/bin/bash' - file_content = os.linesep.join([ - '/etc/init.d/someservice restart', - 'exit 0' - ]) - file_modified = os.linesep.join([ - cfg_content, - '/etc/init.d/someservice restart', - 'exit 0' - ]) + """ + cfg_content = "#!/bin/bash" + file_content = os.linesep.join(["/etc/init.d/someservice restart", "exit 0"]) + file_modified = os.linesep.join( + [cfg_content, "/etc/init.d/someservice restart", "exit 0"] + ) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, before='/etc/init.d/someservice restart', mode='ensure') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", mock_open(read_data=file_content)), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line( + name, + content=cfg_content, + before="/etc/init.d/someservice restart", + mode="ensure", + ) handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1792,29 +1895,30 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): @with_tempfile() def test_line_insert_ensure_after(self, name): - ''' + """ Test for file.line for insertion ensuring the line is after :return: - ''' - cfg_content = 'exit 0' - file_content = os.linesep.join([ - '#!/bin/bash', - '/etc/init.d/someservice restart' - ]) - file_modified = os.linesep.join([ - '#!/bin/bash', - '/etc/init.d/someservice restart', - cfg_content - ]) + """ + cfg_content = "exit 0" + file_content = os.linesep.join( + ["#!/bin/bash", "/etc/init.d/someservice restart"] + ) + file_modified = os.linesep.join( + ["#!/bin/bash", "/etc/init.d/someservice restart", cfg_content] + ) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, after='/etc/init.d/someservice restart', mode='ensure') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", mock_open(read_data=file_content)), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line( + name, + content=cfg_content, + after="/etc/init.d/someservice restart", + mode="ensure", + ) handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1829,58 +1933,67 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): @with_tempfile() def test_line_insert_duplicate_ensure_after(self, name): - ''' + """ Test for file.line for insertion ensuring the line is after :return: - ''' - cfg_content = 'exit 0' - file_content = os.linesep.join([ - '#!/bin/bash', - '/etc/init.d/someservice restart', - cfg_content - ]) - file_modified = os.linesep.join([ - '#!/bin/bash', - '/etc/init.d/someservice restart', - cfg_content - ]) + """ + cfg_content = "exit 0" + file_content = os.linesep.join( + ["#!/bin/bash", "/etc/init.d/someservice restart", cfg_content] + ) + file_modified = os.linesep.join( + ["#!/bin/bash", "/etc/init.d/someservice restart", cfg_content] + ) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, after='/etc/init.d/someservice restart', mode='ensure') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", mock_open(read_data=file_content)), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line( + name, + content=cfg_content, + after="/etc/init.d/someservice restart", + mode="ensure", + ) # If file not modified no handlers in dict assert atomic_open_mock.filehandles.get(name) is None @with_tempfile() def test_line_insert_ensure_beforeafter_twolines(self, name): - ''' + """ Test for file.line for insertion ensuring the line is between two lines :return: - ''' + """ cfg_content = 'EXTRA_GROUPS="dialout cdrom floppy audio video plugdev users"' # pylint: disable=W1401 - file_content = os.linesep.join([ - 'NAME_REGEX="^[a-z][-a-z0-9_]*\$"', - 'SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)"' - ]) + file_content = os.linesep.join( + [ + 'NAME_REGEX="^[a-z][-a-z0-9_]*\$"', + 'SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)"', + ] + ) # pylint: enable=W1401 after, before = file_content.split(os.linesep) file_modified = os.linesep.join([after, cfg_content, before]) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - for (_after, _before) in [(after, before), ('NAME_.*', 'SKEL_.*')]: - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - filemod.line(name, content=cfg_content, after=_after, before=_before, mode='ensure') + for (_after, _before) in [(after, before), ("NAME_.*", "SKEL_.*")]: + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch( + "salt.utils.files.fopen", mock_open(read_data=file_content) + ), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line( + name, + content=cfg_content, + after=_after, + before=_before, + mode="ensure", + ) handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1891,93 +2004,120 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): assert writelines_count == 1, writelines_count # ... with the updated content expected = self._get_body(file_modified) - assert writelines_content[0] == expected, (writelines_content[0], expected) + assert writelines_content[0] == expected, ( + writelines_content[0], + expected, + ) @with_tempfile() def test_line_insert_ensure_beforeafter_twolines_exists(self, name): - ''' + """ Test for file.line for insertion ensuring the line is between two lines where content already exists - ''' + """ cfg_content = 'EXTRA_GROUPS="dialout"' # pylint: disable=W1401 - file_content = os.linesep.join([ - 'NAME_REGEX="^[a-z][-a-z0-9_]*\$"', - 'EXTRA_GROUPS="dialout"', - 'SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)"' - ]) + file_content = os.linesep.join( + [ + 'NAME_REGEX="^[a-z][-a-z0-9_]*\$"', + 'EXTRA_GROUPS="dialout"', + 'SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)"', + ] + ) # pylint: enable=W1401 - after, before = file_content.split(os.linesep)[0], file_content.split(os.linesep)[2] + after, before = ( + file_content.split(os.linesep)[0], + file_content.split(os.linesep)[2], + ) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - for (_after, _before) in [(after, before), ('NAME_.*', 'SKEL_.*')]: - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', - mock_open(read_data=file_content)), \ - patch('salt.utils.atomicfile.atomic_open', - mock_open()) as atomic_open_mock: - result = filemod.line('foo', content=cfg_content, after=_after, before=_before, mode='ensure') + for (_after, _before) in [(after, before), ("NAME_.*", "SKEL_.*")]: + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch( + "salt.utils.files.fopen", mock_open(read_data=file_content) + ), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + result = filemod.line( + "foo", + content=cfg_content, + after=_after, + before=_before, + mode="ensure", + ) # We should not have opened the file assert not atomic_open_mock.filehandles # No changes should have been made assert result is False - @patch('os.path.realpath', MagicMock(wraps=lambda x: x)) - @patch('os.path.isfile', MagicMock(return_value=True)) - @patch('os.stat', MagicMock()) + @patch("os.path.realpath", MagicMock(wraps=lambda x: x)) + @patch("os.path.isfile", MagicMock(return_value=True)) + @patch("os.stat", MagicMock()) def test_line_insert_ensure_beforeafter_rangelines(self): - ''' + """ Test for file.line for insertion ensuring the line is between two lines within the range. This expected to bring no changes. - ''' + """ cfg_content = 'EXTRA_GROUPS="dialout cdrom floppy audio video plugdev users"' # pylint: disable=W1401 - file_content = 'NAME_REGEX="^[a-z][-a-z0-9_]*\$"{}SETGID_HOME=no{}ADD_EXTRA_GROUPS=1{}' \ - 'SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)"'.format( - os.linesep, os.linesep, os.linesep - ) + file_content = ( + 'NAME_REGEX="^[a-z][-a-z0-9_]*\$"{}SETGID_HOME=no{}ADD_EXTRA_GROUPS=1{}' + 'SKEL_IGNORE_REGEX="dpkg-(old|new|dist|save)"'.format( + os.linesep, os.linesep, os.linesep + ) + ) # pylint: enable=W1401 - after, before = file_content.split(os.linesep)[0], file_content.split(os.linesep)[-1] - for (_after, _before) in [(after, before), ('NAME_.*', 'SKEL_.*')]: + after, before = ( + file_content.split(os.linesep)[0], + file_content.split(os.linesep)[-1], + ) + for (_after, _before) in [(after, before), ("NAME_.*", "SKEL_.*")]: files_fopen = mock_open(read_data=file_content) - with patch('salt.utils.files.fopen', files_fopen): + with patch("salt.utils.files.fopen", files_fopen): atomic_opener = mock_open() - with patch('salt.utils.atomicfile.atomic_open', atomic_opener): + with patch("salt.utils.atomicfile.atomic_open", atomic_opener): with pytest.raises(CommandExecutionError) as exc_info: - filemod.line('foo', content=cfg_content, after=_after, before=_before, mode='ensure') + filemod.line( + "foo", + content=cfg_content, + after=_after, + before=_before, + mode="ensure", + ) self.assertIn( 'Found more than one line between boundaries "before" and "after"', - six.text_type(exc_info.value)) + six.text_type(exc_info.value), + ) @with_tempfile() def test_line_delete(self, name): - ''' + """ Test for file.line for deletion of specific line :return: - ''' - file_content = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/pepper', - ' - /srv/sugar' - ]) - file_modified = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/sugar' - ]) + """ + file_content = os.linesep.join( + [ + "file_roots:", + " base:", + " - /srv/salt", + " - /srv/pepper", + " - /srv/sugar", + ] + ) + file_modified = os.linesep.join( + ["file_roots:", " base:", " - /srv/salt", " - /srv/sugar"] + ) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - for content in ['/srv/pepper', '/srv/pepp*', '/srv/p.*', '/sr.*pe.*']: + for content in ["/srv/pepper", "/srv/pepp*", "/srv/p.*", "/sr.*pe.*"]: files_fopen = mock_open(read_data=file_content) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', files_fopen), \ - patch('salt.utils.atomicfile.atomic_open', mock_open()) as atomic_open_mock: - filemod.line(name, content=content, mode='delete') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", files_fopen), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line(name, content=content, mode="delete") handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -1988,37 +2128,47 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): assert writelines_count == 1, writelines_count # ... with the updated content expected = self._get_body(file_modified) - assert writelines_content[0] == expected, (writelines_content[0], expected) + assert writelines_content[0] == expected, ( + writelines_content[0], + expected, + ) @with_tempfile() def test_line_replace(self, name): - ''' + """ Test for file.line for replacement of specific line :return: - ''' - file_content = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/pepper', - ' - /srv/sugar' - ]) - file_modified = os.linesep.join([ - 'file_roots:', - ' base:', - ' - /srv/salt', - ' - /srv/natrium-chloride', - ' - /srv/sugar' - ]) + """ + file_content = os.linesep.join( + [ + "file_roots:", + " base:", + " - /srv/salt", + " - /srv/pepper", + " - /srv/sugar", + ] + ) + file_modified = os.linesep.join( + [ + "file_roots:", + " base:", + " - /srv/salt", + " - /srv/natrium-chloride", + " - /srv/sugar", + ] + ) isfile_mock = MagicMock(side_effect=lambda x: True if x == name else DEFAULT) - for match in ['/srv/pepper', '/srv/pepp*', '/srv/p.*', '/sr.*pe.*']: + for match in ["/srv/pepper", "/srv/pepp*", "/srv/p.*", "/sr.*pe.*"]: files_fopen = mock_open(read_data=file_content) - with patch('os.path.isfile', isfile_mock), \ - patch('os.stat', MagicMock(return_value=DummyStat())), \ - patch('salt.utils.files.fopen', files_fopen), \ - patch('salt.utils.atomicfile.atomic_open', mock_open()) as atomic_open_mock: - filemod.line(name, content='- /srv/natrium-chloride', match=match, mode='replace') + with patch("os.path.isfile", isfile_mock), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ), patch("salt.utils.files.fopen", files_fopen), patch( + "salt.utils.atomicfile.atomic_open", mock_open() + ) as atomic_open_mock: + filemod.line( + name, content="- /srv/natrium-chloride", match=match, mode="replace" + ) handles = atomic_open_mock.filehandles[name] # We should only have opened the file once open_count = len(handles) @@ -2029,69 +2179,72 @@ class FilemodLineTests(TestCase, LoaderModuleMockMixin): assert writelines_count == 1, writelines_count # ... with the updated content expected = self._get_body(file_modified) - assert writelines_content[0] == expected, (writelines_content[0], expected) + assert writelines_content[0] == expected, ( + writelines_content[0], + expected, + ) class FileBasicsTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { filemod: { - '__salt__': { - 'config.manage_mode': configmod.manage_mode, - 'cmd.run': cmdmod.run, - 'cmd.run_all': cmdmod.run_all + "__salt__": { + "config.manage_mode": configmod.manage_mode, + "cmd.run": cmdmod.run, + "cmd.run_all": cmdmod.run_all, }, - '__opts__': { - 'test': False, - 'file_roots': {'base': 'tmp'}, - 'pillar_roots': {'base': 'tmp'}, - 'cachedir': 'tmp', - 'grains': {}, + "__opts__": { + "test": False, + "file_roots": {"base": "tmp"}, + "pillar_roots": {"base": "tmp"}, + "cachedir": "tmp", + "grains": {}, }, - '__grains__': {'kernel': 'Linux'} + "__grains__": {"kernel": "Linux"}, } } def setUp(self): self.directory = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, self.directory) - self.addCleanup(delattr, self, 'directory') - with tempfile.NamedTemporaryFile(delete=False, mode='w+') as self.tfile: - self.tfile.write('Hi hello! I am a file.') + self.addCleanup(delattr, self, "directory") + with tempfile.NamedTemporaryFile(delete=False, mode="w+") as self.tfile: + self.tfile.write("Hi hello! I am a file.") self.tfile.close() self.addCleanup(os.remove, self.tfile.name) - self.addCleanup(delattr, self, 'tfile') - self.myfile = os.path.join(RUNTIME_VARS.TMP, 'myfile') - with salt.utils.files.fopen(self.myfile, 'w+') as fp: - fp.write(salt.utils.stringutils.to_str('Hello\n')) + self.addCleanup(delattr, self, "tfile") + self.myfile = os.path.join(RUNTIME_VARS.TMP, "myfile") + with salt.utils.files.fopen(self.myfile, "w+") as fp: + fp.write(salt.utils.stringutils.to_str("Hello\n")) self.addCleanup(os.remove, self.myfile) - self.addCleanup(delattr, self, 'myfile') + self.addCleanup(delattr, self, "myfile") - @skipIf(salt.utils.platform.is_windows(), 'os.symlink is not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "os.symlink is not available on Windows") def test_symlink_already_in_desired_state(self): - os.symlink(self.tfile.name, self.directory + '/a_link') - self.addCleanup(os.remove, self.directory + '/a_link') - result = filemod.symlink(self.tfile.name, self.directory + '/a_link') + os.symlink(self.tfile.name, self.directory + "/a_link") + self.addCleanup(os.remove, self.directory + "/a_link") + result = filemod.symlink(self.tfile.name, self.directory + "/a_link") self.assertTrue(result) - @skipIf(salt.utils.platform.is_windows(), 'os.link is not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "os.link is not available on Windows") def test_hardlink_sanity(self): - target = os.path.join(self.directory, 'a_hardlink') + target = os.path.join(self.directory, "a_hardlink") self.addCleanup(os.remove, target) result = filemod.link(self.tfile.name, target) self.assertTrue(result) - @skipIf(salt.utils.platform.is_windows(), 'os.link is not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "os.link is not available on Windows") def test_hardlink_numlinks(self): - target = os.path.join(self.directory, 'a_hardlink') + target = os.path.join(self.directory, "a_hardlink") self.addCleanup(os.remove, target) result = filemod.link(self.tfile.name, target) name_i = os.stat(self.tfile.name).st_nlink self.assertTrue(name_i > 1) - @skipIf(salt.utils.platform.is_windows(), 'os.link is not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "os.link is not available on Windows") def test_hardlink_working(self): - target = os.path.join(self.directory, 'a_hardlink') + target = os.path.join(self.directory, "a_hardlink") self.addCleanup(os.remove, target) result = filemod.link(self.tfile.name, target) name_i = os.stat(self.tfile.name).st_ino @@ -2099,188 +2252,233 @@ class FileBasicsTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(name_i == target_i) def test_source_list_for_list_returns_file_from_dict_via_http(self): - with patch('salt.modules.file.os.remove') as remove: + with patch("salt.modules.file.os.remove") as remove: remove.return_value = None - with patch.dict(filemod.__salt__, {'cp.list_master': MagicMock(return_value=[]), - 'cp.list_master_dirs': MagicMock(return_value=[]), - 'cp.cache_file': MagicMock(return_value='/tmp/http.conf')}): + with patch.dict( + filemod.__salt__, + { + "cp.list_master": MagicMock(return_value=[]), + "cp.list_master_dirs": MagicMock(return_value=[]), + "cp.cache_file": MagicMock(return_value="/tmp/http.conf"), + }, + ): ret = filemod.source_list( - [{'http://t.est.com/http/httpd.conf': 'filehash'}], '', 'base') - self.assertEqual(list(ret), ['http://t.est.com/http/httpd.conf', 'filehash']) + [{"http://t.est.com/http/httpd.conf": "filehash"}], "", "base" + ) + self.assertEqual( + list(ret), ["http://t.est.com/http/httpd.conf", "filehash"] + ) def test_source_list_for_list_returns_existing_file(self): - with patch.dict(filemod.__salt__, {'cp.list_master': MagicMock(return_value=['http/httpd.conf.fallback']), - 'cp.list_master_dirs': MagicMock(return_value=[])}): - ret = filemod.source_list(['salt://http/httpd.conf', - 'salt://http/httpd.conf.fallback'], - 'filehash', 'base') - self.assertEqual(list(ret), ['salt://http/httpd.conf.fallback', 'filehash']) + with patch.dict( + filemod.__salt__, + { + "cp.list_master": MagicMock(return_value=["http/httpd.conf.fallback"]), + "cp.list_master_dirs": MagicMock(return_value=[]), + }, + ): + ret = filemod.source_list( + ["salt://http/httpd.conf", "salt://http/httpd.conf.fallback"], + "filehash", + "base", + ) + self.assertEqual(list(ret), ["salt://http/httpd.conf.fallback", "filehash"]) def test_source_list_for_list_returns_file_from_other_env(self): def list_master(env): - dct = {'base': [], 'dev': ['http/httpd.conf']} + dct = {"base": [], "dev": ["http/httpd.conf"]} return dct[env] - with patch.dict(filemod.__salt__, {'cp.list_master': MagicMock(side_effect=list_master), - 'cp.list_master_dirs': MagicMock(return_value=[])}): - ret = filemod.source_list(['salt://http/httpd.conf?saltenv=dev', - 'salt://http/httpd.conf.fallback'], - 'filehash', 'base') - self.assertEqual(list(ret), ['salt://http/httpd.conf?saltenv=dev', 'filehash']) + with patch.dict( + filemod.__salt__, + { + "cp.list_master": MagicMock(side_effect=list_master), + "cp.list_master_dirs": MagicMock(return_value=[]), + }, + ): + ret = filemod.source_list( + [ + "salt://http/httpd.conf?saltenv=dev", + "salt://http/httpd.conf.fallback", + ], + "filehash", + "base", + ) + self.assertEqual( + list(ret), ["salt://http/httpd.conf?saltenv=dev", "filehash"] + ) def test_source_list_for_list_returns_file_from_dict(self): - with patch.dict(filemod.__salt__, {'cp.list_master': MagicMock(return_value=['http/httpd.conf']), - 'cp.list_master_dirs': MagicMock(return_value=[])}): + with patch.dict( + filemod.__salt__, + { + "cp.list_master": MagicMock(return_value=["http/httpd.conf"]), + "cp.list_master_dirs": MagicMock(return_value=[]), + }, + ): ret = filemod.source_list( - [{'salt://http/httpd.conf': ''}], 'filehash', 'base') - self.assertEqual(list(ret), ['salt://http/httpd.conf', 'filehash']) + [{"salt://http/httpd.conf": ""}], "filehash", "base" + ) + self.assertEqual(list(ret), ["salt://http/httpd.conf", "filehash"]) def test_source_list_for_list_returns_existing_local_file_slash(self): - with patch.dict(filemod.__salt__, {'cp.list_master': MagicMock(return_value=[]), - 'cp.list_master_dirs': MagicMock(return_value=[])}): - ret = filemod.source_list([self.myfile + '-foo', - self.myfile], - 'filehash', 'base') - self.assertEqual(list(ret), [self.myfile, 'filehash']) + with patch.dict( + filemod.__salt__, + { + "cp.list_master": MagicMock(return_value=[]), + "cp.list_master_dirs": MagicMock(return_value=[]), + }, + ): + ret = filemod.source_list( + [self.myfile + "-foo", self.myfile], "filehash", "base" + ) + self.assertEqual(list(ret), [self.myfile, "filehash"]) def test_source_list_for_list_returns_existing_local_file_proto(self): - with patch.dict(filemod.__salt__, {'cp.list_master': MagicMock(return_value=[]), - 'cp.list_master_dirs': MagicMock(return_value=[])}): - ret = filemod.source_list(['file://' + self.myfile + '-foo', - 'file://' + self.myfile], - 'filehash', 'base') - self.assertEqual(list(ret), ['file://' + self.myfile, 'filehash']) + with patch.dict( + filemod.__salt__, + { + "cp.list_master": MagicMock(return_value=[]), + "cp.list_master_dirs": MagicMock(return_value=[]), + }, + ): + ret = filemod.source_list( + ["file://" + self.myfile + "-foo", "file://" + self.myfile], + "filehash", + "base", + ) + self.assertEqual(list(ret), ["file://" + self.myfile, "filehash"]) def test_source_list_for_list_returns_local_file_slash_from_dict(self): - with patch.dict(filemod.__salt__, {'cp.list_master': MagicMock(return_value=[]), - 'cp.list_master_dirs': MagicMock(return_value=[])}): - ret = filemod.source_list( - [{self.myfile: ''}], 'filehash', 'base') - self.assertEqual(list(ret), [self.myfile, 'filehash']) + with patch.dict( + filemod.__salt__, + { + "cp.list_master": MagicMock(return_value=[]), + "cp.list_master_dirs": MagicMock(return_value=[]), + }, + ): + ret = filemod.source_list([{self.myfile: ""}], "filehash", "base") + self.assertEqual(list(ret), [self.myfile, "filehash"]) def test_source_list_for_list_returns_local_file_proto_from_dict(self): - with patch.dict(filemod.__salt__, {'cp.list_master': MagicMock(return_value=[]), - 'cp.list_master_dirs': MagicMock(return_value=[])}): + with patch.dict( + filemod.__salt__, + { + "cp.list_master": MagicMock(return_value=[]), + "cp.list_master_dirs": MagicMock(return_value=[]), + }, + ): ret = filemod.source_list( - [{'file://' + self.myfile: ''}], 'filehash', 'base') - self.assertEqual(list(ret), ['file://' + self.myfile, 'filehash']) + [{"file://" + self.myfile: ""}], "filehash", "base" + ) + self.assertEqual(list(ret), ["file://" + self.myfile, "filehash"]) class LsattrTests(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { - filemod: { - '__salt__': { - 'cmd.run': cmdmod.run, - }, - }, + filemod: {"__salt__": {"cmd.run": cmdmod.run}}, } def run(self, result=None): - patch_aix = patch( - 'salt.utils.platform.is_aix', - Mock(return_value=False), - ) - patch_exists = patch( - 'os.path.exists', - Mock(return_value=True), - ) - patch_which = patch( - 'salt.utils.path.which', - Mock(return_value='fnord'), - ) + patch_aix = patch("salt.utils.platform.is_aix", Mock(return_value=False),) + patch_exists = patch("os.path.exists", Mock(return_value=True),) + patch_which = patch("salt.utils.path.which", Mock(return_value="fnord"),) with patch_aix, patch_exists, patch_which: super(LsattrTests, self).run(result) def test_if_lsattr_is_missing_it_should_return_None(self): - patch_which = patch( - 'salt.utils.path.which', - Mock(return_value=None), - ) + patch_which = patch("salt.utils.path.which", Mock(return_value=None),) with patch_which: - actual = filemod.lsattr('foo') + actual = filemod.lsattr("foo") assert actual is None, actual def test_on_aix_lsattr_should_be_None(self): - patch_aix = patch( - 'salt.utils.platform.is_aix', - Mock(return_value=True), - ) + patch_aix = patch("salt.utils.platform.is_aix", Mock(return_value=True),) with patch_aix: # SaltInvocationError will be raised if filemod.lsattr # doesn't early exit - actual = filemod.lsattr('foo') + actual = filemod.lsattr("foo") self.assertIsNone(actual) def test_SaltInvocationError_should_be_raised_when_file_is_missing(self): - patch_exists = patch( - 'os.path.exists', - Mock(return_value=False), - ) + patch_exists = patch("os.path.exists", Mock(return_value=False),) with patch_exists, self.assertRaises(SaltInvocationError): - filemod.lsattr('foo') + filemod.lsattr("foo") def test_if_chattr_version_is_less_than_required_flags_should_ignore_extended(self): - fname = '/path/to/fnord' - with_extended = textwrap.dedent( - ''' + fname = "/path/to/fnord" + with_extended = ( + textwrap.dedent( + """ aAcCdDeijPsStTu---- {} - ''' - ).strip().format(fname) - expected = set('acdijstuADST') + """ + ) + .strip() + .format(fname) + ) + expected = set("acdijstuADST") patch_has_ext = patch( - 'salt.modules.file._chattr_has_extended_attrs', - Mock(return_value=False), + "salt.modules.file._chattr_has_extended_attrs", Mock(return_value=False), ) patch_run = patch.dict( - filemod.__salt__, - {'cmd.run': Mock(return_value=with_extended)}, + filemod.__salt__, {"cmd.run": Mock(return_value=with_extended)}, ) with patch_has_ext, patch_run: actual = set(filemod.lsattr(fname)[fname]) - msg = 'Actual: {!r} Expected: {!r}'.format(actual, expected) # pylint: disable=E1322 + # pylint: disable=E1322 + msg = "Actual: {!r} Expected: {!r}".format(actual, expected) + # pylint: enable=E1322 assert actual == expected, msg - def test_if_chattr_version_is_high_enough_then_extended_flags_should_be_returned(self): - fname = '/path/to/fnord' - with_extended = textwrap.dedent( - ''' + def test_if_chattr_version_is_high_enough_then_extended_flags_should_be_returned( + self, + ): + fname = "/path/to/fnord" + with_extended = ( + textwrap.dedent( + """ aAcCdDeijPsStTu---- {} - ''' - ).strip().format(fname) - expected = set('aAcCdDeijPsStTu') + """ + ) + .strip() + .format(fname) + ) + expected = set("aAcCdDeijPsStTu") patch_has_ext = patch( - 'salt.modules.file._chattr_has_extended_attrs', - Mock(return_value=True), + "salt.modules.file._chattr_has_extended_attrs", Mock(return_value=True), ) patch_run = patch.dict( - filemod.__salt__, - {'cmd.run': Mock(return_value=with_extended)}, + filemod.__salt__, {"cmd.run": Mock(return_value=with_extended)}, ) with patch_has_ext, patch_run: actual = set(filemod.lsattr(fname)[fname]) - msg = 'Actual: {!r} Expected: {!r}'.format(actual, expected) # pylint: disable=E1322 + msg = "Actual: {!r} Expected: {!r}".format(actual, expected) assert actual == expected, msg - def test_if_supports_extended_but_there_are_no_flags_then_none_should_be_returned(self): - fname = '/path/to/fnord' - with_extended = textwrap.dedent( - ''' + def test_if_supports_extended_but_there_are_no_flags_then_none_should_be_returned( + self, + ): + fname = "/path/to/fnord" + with_extended = ( + textwrap.dedent( + """ ------------------- {} - ''' - ).strip().format(fname) - expected = set('') + """ + ) + .strip() + .format(fname) + ) + expected = set("") patch_has_ext = patch( - 'salt.modules.file._chattr_has_extended_attrs', - Mock(return_value=True), + "salt.modules.file._chattr_has_extended_attrs", Mock(return_value=True), ) patch_run = patch.dict( - filemod.__salt__, - {'cmd.run': Mock(return_value=with_extended)}, + filemod.__salt__, {"cmd.run": Mock(return_value=with_extended)}, ) with patch_has_ext, patch_run: actual = set(filemod.lsattr(fname)[fname]) - msg = 'Actual: {!r} Expected: {!r}'.format(actual, expected) # pylint: disable=E1322 + msg = "Actual: {!r} Expected: {!r}".format(actual, expected) assert actual == expected, msg @@ -2292,60 +2490,38 @@ class ChattrTests(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { filemod: { - '__salt__': { - 'cmd.run': cmdmod.run, - }, - '__opts__': { - 'test': False, - }, + "__salt__": {"cmd.run": cmdmod.run}, + "__opts__": {"test": False}, }, } def run(self, result=None): - patch_aix = patch( - 'salt.utils.platform.is_aix', - Mock(return_value=False), - ) - patch_exists = patch( - 'os.path.exists', - Mock(return_value=True), - ) - patch_which = patch( - 'salt.utils.path.which', - Mock(return_value='some/tune2fs'), - ) + patch_aix = patch("salt.utils.platform.is_aix", Mock(return_value=False),) + patch_exists = patch("os.path.exists", Mock(return_value=True),) + patch_which = patch("salt.utils.path.which", Mock(return_value="some/tune2fs"),) with patch_aix, patch_exists, patch_which: super(ChattrTests, self).run(result) def test_chattr_version_returns_None_if_no_tune2fs_exists(self): - patch_which = patch( - 'salt.utils.path.which', - Mock(return_value=''), - ) + patch_which = patch("salt.utils.path.which", Mock(return_value=""),) with patch_which: actual = filemod._chattr_version() self.assertIsNone(actual) def test_on_aix_chattr_version_should_be_None_even_if_tune2fs_exists(self): - patch_which = patch( - 'salt.utils.path.which', - Mock(return_value='fnord'), - ) - patch_aix = patch( - 'salt.utils.platform.is_aix', - Mock(return_value=True), - ) - mock_run = MagicMock(return_value='fnord') - patch_run = patch.dict(filemod.__salt__, {'cmd.run': mock_run}) + patch_which = patch("salt.utils.path.which", Mock(return_value="fnord"),) + patch_aix = patch("salt.utils.platform.is_aix", Mock(return_value=True),) + mock_run = MagicMock(return_value="fnord") + patch_run = patch.dict(filemod.__salt__, {"cmd.run": mock_run}) with patch_which, patch_aix, patch_run: actual = filemod._chattr_version() self.assertIsNone(actual) mock_run.assert_not_called() def test_chattr_version_should_return_version_from_tune2fs(self): - expected = '1.43.4' + expected = "1.43.4" sample_output = textwrap.dedent( - ''' + """ tune2fs 1.43.4 (31-Jan-2017) Usage: tune2fs [-c max_mounts_count] [-e errors_behavior] [-f] [-g group] [-i interval[d|m|w]] [-j] [-J journal_options] [-l] @@ -2355,67 +2531,61 @@ class ChattrTests(TestCase, LoaderModuleMockMixin): [-O [^]feature[,...]] [-Q quota_options] [-E extended-option[,...]] [-T last_check_time] [-U UUID] [-I new_inode_size] [-z undo_file] device - ''' - ) - patch_which = patch( - 'salt.utils.path.which', - Mock(return_value='fnord'), + """ ) + patch_which = patch("salt.utils.path.which", Mock(return_value="fnord"),) patch_run = patch.dict( - filemod.__salt__, - {'cmd.run': MagicMock(return_value=sample_output)}, + filemod.__salt__, {"cmd.run": MagicMock(return_value=sample_output)}, ) with patch_which, patch_run: actual = filemod._chattr_version() self.assertEqual(actual, expected) def test_if_tune2fs_has_no_version_version_should_be_None(self): - patch_which = patch( - 'salt.utils.path.which', - Mock(return_value='fnord'), - ) + patch_which = patch("salt.utils.path.which", Mock(return_value="fnord"),) patch_run = patch.dict( - filemod.__salt__, - {'cmd.run': MagicMock(return_value='fnord')}, + filemod.__salt__, {"cmd.run": MagicMock(return_value="fnord")}, ) with patch_which, patch_run: actual = filemod._chattr_version() self.assertIsNone(actual) - def test_chattr_has_extended_attrs_should_return_False_if_chattr_version_is_None(self): + def test_chattr_has_extended_attrs_should_return_False_if_chattr_version_is_None( + self, + ): patch_chattr = patch( - 'salt.modules.file._chattr_version', - Mock(return_value=None), + "salt.modules.file._chattr_version", Mock(return_value=None), ) with patch_chattr: actual = filemod._chattr_has_extended_attrs() assert not actual, actual def test_chattr_has_extended_attrs_should_return_False_if_version_is_too_low(self): - below_expected = '0.1.1' + below_expected = "0.1.1" patch_chattr = patch( - 'salt.modules.file._chattr_version', - Mock(return_value=below_expected), + "salt.modules.file._chattr_version", Mock(return_value=below_expected), ) with patch_chattr: actual = filemod._chattr_has_extended_attrs() assert not actual, actual - def test_chattr_has_extended_attrs_should_return_False_if_version_is_equal_threshold(self): - threshold = '1.41.12' + def test_chattr_has_extended_attrs_should_return_False_if_version_is_equal_threshold( + self, + ): + threshold = "1.41.12" patch_chattr = patch( - 'salt.modules.file._chattr_version', - Mock(return_value=threshold), + "salt.modules.file._chattr_version", Mock(return_value=threshold), ) with patch_chattr: actual = filemod._chattr_has_extended_attrs() assert not actual, actual - def test_chattr_has_extended_attrs_should_return_True_if_version_is_above_threshold(self): - higher_than = '1.41.13' + def test_chattr_has_extended_attrs_should_return_True_if_version_is_above_threshold( + self, + ): + higher_than = "1.41.13" patch_chattr = patch( - 'salt.modules.file._chattr_version', - Mock(return_value=higher_than), + "salt.modules.file._chattr_version", Mock(return_value=higher_than), ) with patch_chattr: actual = filemod._chattr_has_extended_attrs() @@ -2424,116 +2594,94 @@ class ChattrTests(TestCase, LoaderModuleMockMixin): # We're skipping this on Windows as it tests the check_perms function in # file.py which is specifically for Linux. The Windows version resides in # win_file.py - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows") def test_check_perms_should_report_no_attr_changes_if_there_are_none(self): - filename = '/path/to/fnord' - attrs = 'aAcCdDeijPsStTu' + filename = "/path/to/fnord" + attrs = "aAcCdDeijPsStTu" - higher_than = '1.41.13' + higher_than = "1.41.13" patch_chattr = patch( - 'salt.modules.file._chattr_version', - Mock(return_value=higher_than), - ) - patch_exists = patch( - 'os.path.exists', - Mock(return_value=True), + "salt.modules.file._chattr_version", Mock(return_value=higher_than), ) + patch_exists = patch("os.path.exists", Mock(return_value=True),) patch_stats = patch( - 'salt.modules.file.stats', - Mock(return_value={ - 'user': 'foo', - 'group': 'bar', - 'mode': '123', - }), + "salt.modules.file.stats", + Mock(return_value={"user": "foo", "group": "bar", "mode": "123"}), ) patch_run = patch.dict( filemod.__salt__, - {'cmd.run': MagicMock(return_value='--------- '+filename)}, + {"cmd.run": MagicMock(return_value="--------- " + filename)}, ) with patch_chattr, patch_exists, patch_stats, patch_run: actual_ret, actual_perms = filemod.check_perms( name=filename, ret=None, - user='foo', - group='bar', - mode='123', + user="foo", + group="bar", + mode="123", attrs=attrs, follow_symlinks=False, ) - assert actual_ret.get('changes', {}).get('attrs')is None, actual_ret + assert actual_ret.get("changes", {}).get("attrs") is None, actual_ret # We're skipping this on Windows as it tests the check_perms function in # file.py which is specifically for Linux. The Windows version resides in # win_file.py - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows") def test_check_perms_should_report_attrs_new_and_old_if_they_changed(self): - filename = '/path/to/fnord' - attrs = 'aAcCdDeijPsStTu' - existing_attrs = 'aeiu' + filename = "/path/to/fnord" + attrs = "aAcCdDeijPsStTu" + existing_attrs = "aeiu" expected = { - 'attrs': { - 'old': existing_attrs, - 'new': attrs, - }, + "attrs": {"old": existing_attrs, "new": attrs}, } - higher_than = '1.41.13' + higher_than = "1.41.13" patch_chattr = patch( - 'salt.modules.file._chattr_version', - Mock(return_value=higher_than), + "salt.modules.file._chattr_version", Mock(return_value=higher_than), ) patch_stats = patch( - 'salt.modules.file.stats', - Mock(return_value={ - 'user': 'foo', - 'group': 'bar', - 'mode': '123', - }), + "salt.modules.file.stats", + Mock(return_value={"user": "foo", "group": "bar", "mode": "123"}), ) patch_cmp = patch( - 'salt.modules.file._cmp_attrs', - MagicMock(side_effect=[ - filemod.AttrChanges( - added='aAcCdDeijPsStTu', - removed='', - ), - filemod.AttrChanges( - None, - None, - ), - ]), - ) - patch_chattr = patch( - 'salt.modules.file.chattr', - MagicMock(), + "salt.modules.file._cmp_attrs", + MagicMock( + side_effect=[ + filemod.AttrChanges(added="aAcCdDeijPsStTu", removed="",), + filemod.AttrChanges(None, None,), + ] + ), ) + patch_chattr = patch("salt.modules.file.chattr", MagicMock(),) def fake_cmd(cmd, *args, **kwargs): - if cmd == ['lsattr', '/path/to/fnord']: + if cmd == ["lsattr", "/path/to/fnord"]: return textwrap.dedent( - ''' + """ {}---- {} - '''.format(existing_attrs, filename) + """.format( + existing_attrs, filename + ) ).strip() else: assert False, "not sure how to handle {}".format(cmd) patch_run = patch.dict( - filemod.__salt__, - {'cmd.run': MagicMock(side_effect=fake_cmd)}, + filemod.__salt__, {"cmd.run": MagicMock(side_effect=fake_cmd)}, ) patch_ver = patch( - 'salt.modules.file._chattr_has_extended_attrs', + "salt.modules.file._chattr_has_extended_attrs", MagicMock(return_value=True), ) with patch_chattr, patch_stats, patch_cmp, patch_run, patch_ver: actual_ret, actual_perms = filemod.check_perms( name=filename, ret=None, - user='foo', - group='bar', - mode='123', + user="foo", + group="bar", + mode="123", attrs=attrs, follow_symlinks=False, ) - self.assertDictEqual(actual_ret['changes'], expected) + self.assertDictEqual(actual_ret["changes"], expected) diff --git a/tests/unit/modules/test_firewalld.py b/tests/unit/modules/test_firewalld.py index 0a8a9623d97..5e033cab755 100644 --- a/tests/unit/modules/test_firewalld.py +++ b/tests/unit/modules/test_firewalld.py @@ -1,284 +1,302 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.firewalld as firewalld +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class FirewalldTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.firewalld - ''' + """ + def setup_loader_modules(self): return {firewalld: {}} def test_version(self): - ''' + """ Test for Return version from firewall-cmd - ''' - with patch.object(firewalld, '__firewall_cmd', return_value=2): + """ + with patch.object(firewalld, "__firewall_cmd", return_value=2): self.assertEqual(firewalld.version(), 2) def test_default_zone(self): - ''' + """ Test for Print default zone for connections and interfaces - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='A'): - self.assertEqual(firewalld.default_zone(), 'A') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="A"): + self.assertEqual(firewalld.default_zone(), "A") def test_list_zones(self): - ''' + """ Test for List everything added for or enabled in all zones - ''' - with patch.object(firewalld, '__firewall_cmd', return_value=[]): + """ + with patch.object(firewalld, "__firewall_cmd", return_value=[]): self.assertEqual(firewalld.default_zone(), []) def test_get_zones(self): - ''' + """ Test for Print predefined zones - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='A'): - self.assertEqual(firewalld.get_zones(), ['A']) + """ + with patch.object(firewalld, "__firewall_cmd", return_value="A"): + self.assertEqual(firewalld.get_zones(), ["A"]) def test_get_services(self): - ''' + """ Test for Print predefined services - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='A'): - self.assertEqual(firewalld.get_services(), ['A']) + """ + with patch.object(firewalld, "__firewall_cmd", return_value="A"): + self.assertEqual(firewalld.get_services(), ["A"]) def test_get_icmp_types(self): - ''' + """ Test for Print predefined icmptypes - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='A'): - self.assertEqual(firewalld.get_icmp_types(), ['A']) + """ + with patch.object(firewalld, "__firewall_cmd", return_value="A"): + self.assertEqual(firewalld.get_icmp_types(), ["A"]) def test_new_zone(self): - ''' + """ Test for Add a new zone - ''' - with patch.object(firewalld, '__mgmt', return_value='success'): - mock = MagicMock(return_value='A') - with patch.object(firewalld, '__firewall_cmd', mock): - self.assertEqual(firewalld.new_zone('zone'), 'A') + """ + with patch.object(firewalld, "__mgmt", return_value="success"): + mock = MagicMock(return_value="A") + with patch.object(firewalld, "__firewall_cmd", mock): + self.assertEqual(firewalld.new_zone("zone"), "A") - with patch.object(firewalld, '__mgmt', return_value='A'): - self.assertEqual(firewalld.new_zone('zone'), 'A') + with patch.object(firewalld, "__mgmt", return_value="A"): + self.assertEqual(firewalld.new_zone("zone"), "A") - with patch.object(firewalld, '__mgmt', return_value='A'): - self.assertEqual(firewalld.new_zone('zone', False), 'A') + with patch.object(firewalld, "__mgmt", return_value="A"): + self.assertEqual(firewalld.new_zone("zone", False), "A") def test_delete_zone(self): - ''' + """ Test for Delete an existing zone - ''' - with patch.object(firewalld, '__mgmt', return_value='success'): - with patch.object(firewalld, '__firewall_cmd', return_value='A'): - self.assertEqual(firewalld.delete_zone('zone'), 'A') + """ + with patch.object(firewalld, "__mgmt", return_value="success"): + with patch.object(firewalld, "__firewall_cmd", return_value="A"): + self.assertEqual(firewalld.delete_zone("zone"), "A") - with patch.object(firewalld, '__mgmt', return_value='A'): - self.assertEqual(firewalld.delete_zone('zone'), 'A') + with patch.object(firewalld, "__mgmt", return_value="A"): + self.assertEqual(firewalld.delete_zone("zone"), "A") - mock = MagicMock(return_value='A') - with patch.object(firewalld, '__mgmt', return_value='A'): - self.assertEqual(firewalld.delete_zone('zone', False), 'A') + mock = MagicMock(return_value="A") + with patch.object(firewalld, "__mgmt", return_value="A"): + self.assertEqual(firewalld.delete_zone("zone", False), "A") def test_set_default_zone(self): - ''' + """ Test for Set default zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='A'): - self.assertEqual(firewalld.set_default_zone('zone'), 'A') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="A"): + self.assertEqual(firewalld.set_default_zone("zone"), "A") def test_new_service(self): - ''' + """ Test for Add a new service - ''' - with patch.object(firewalld, '__mgmt', return_value='success'): - mock = MagicMock(return_value='A') - with patch.object(firewalld, '__firewall_cmd', return_value='A'): - self.assertEqual(firewalld.new_service('zone'), 'A') + """ + with patch.object(firewalld, "__mgmt", return_value="success"): + mock = MagicMock(return_value="A") + with patch.object(firewalld, "__firewall_cmd", return_value="A"): + self.assertEqual(firewalld.new_service("zone"), "A") - with patch.object(firewalld, '__mgmt', return_value='A'): - self.assertEqual(firewalld.new_service('zone'), 'A') + with patch.object(firewalld, "__mgmt", return_value="A"): + self.assertEqual(firewalld.new_service("zone"), "A") - with patch.object(firewalld, '__mgmt', return_value='A'): - self.assertEqual(firewalld.new_service('zone', False), 'A') + with patch.object(firewalld, "__mgmt", return_value="A"): + self.assertEqual(firewalld.new_service("zone", False), "A") def test_delete_service(self): - ''' + """ Test for Delete an existing service - ''' - with patch.object(firewalld, '__mgmt', return_value='success'): - mock = MagicMock(return_value='A') - with patch.object(firewalld, '__firewall_cmd', return_value='A'): - self.assertEqual(firewalld.delete_service('name'), 'A') + """ + with patch.object(firewalld, "__mgmt", return_value="success"): + mock = MagicMock(return_value="A") + with patch.object(firewalld, "__firewall_cmd", return_value="A"): + self.assertEqual(firewalld.delete_service("name"), "A") - with patch.object(firewalld, '__mgmt', return_value='A'): - self.assertEqual(firewalld.delete_service('name'), 'A') + with patch.object(firewalld, "__mgmt", return_value="A"): + self.assertEqual(firewalld.delete_service("name"), "A") - with patch.object(firewalld, '__mgmt', return_value='A'): - self.assertEqual(firewalld.delete_service('name', False), 'A') + with patch.object(firewalld, "__mgmt", return_value="A"): + self.assertEqual(firewalld.delete_service("name", False), "A") def test_list_all(self): - ''' + """ Test for List everything added for or enabled in a zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value=''): + """ + with patch.object(firewalld, "__firewall_cmd", return_value=""): self.assertEqual(firewalld.list_all(), {}) def test_list_services(self): - ''' + """ Test for List services added for zone as a space separated list. - ''' - with patch.object(firewalld, '__firewall_cmd', return_value=''): + """ + with patch.object(firewalld, "__firewall_cmd", return_value=""): self.assertEqual(firewalld.list_services(), []) def test_add_service(self): - ''' + """ Test for Add a service for zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value=''): - self.assertEqual(firewalld.add_service('name'), '') + """ + with patch.object(firewalld, "__firewall_cmd", return_value=""): + self.assertEqual(firewalld.add_service("name"), "") def test_remove_service(self): - ''' + """ Test for Remove a service from zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value=''): - self.assertEqual(firewalld.remove_service('name'), '') + """ + with patch.object(firewalld, "__firewall_cmd", return_value=""): + self.assertEqual(firewalld.remove_service("name"), "") def test_add_masquerade(self): - ''' + """ Test for adding masquerade - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - self.assertEqual(firewalld.add_masquerade('name'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + self.assertEqual(firewalld.add_masquerade("name"), "success") def test_remove_masquerade(self): - ''' + """ Test for removing masquerade - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - self.assertEqual(firewalld.remove_masquerade('name'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + self.assertEqual(firewalld.remove_masquerade("name"), "success") def test_add_port(self): - ''' + """ Test adding a port to a specific zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - self.assertEqual(firewalld.add_port('zone', '80/tcp'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + self.assertEqual(firewalld.add_port("zone", "80/tcp"), "success") def test_remove_port(self): - ''' + """ Test removing a port from a specific zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - self.assertEqual(firewalld.remove_port('zone', '80/tcp'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + self.assertEqual(firewalld.remove_port("zone", "80/tcp"), "success") def test_list_ports(self): - ''' + """ Test listing ports within a zone - ''' - ret = '22/tcp 53/udp 53/tcp' - exp = ['22/tcp', '53/udp', '53/tcp'] + """ + ret = "22/tcp 53/udp 53/tcp" + exp = ["22/tcp", "53/udp", "53/tcp"] - with patch.object(firewalld, '__firewall_cmd', return_value=ret): - self.assertEqual(firewalld.list_ports('zone'), exp) + with patch.object(firewalld, "__firewall_cmd", return_value=ret): + self.assertEqual(firewalld.list_ports("zone"), exp) def test_add_port_fwd(self): - ''' + """ Test adding port forwarding on a zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - self.assertEqual(firewalld.add_port_fwd('zone', '22', '2222', 'tcp'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + self.assertEqual( + firewalld.add_port_fwd("zone", "22", "2222", "tcp"), "success" + ) def test_remove_port_fwd(self): - ''' + """ Test removing port forwarding on a zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - self.assertEqual(firewalld.remove_port_fwd('zone', '22', '2222', 'tcp'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + self.assertEqual( + firewalld.remove_port_fwd("zone", "22", "2222", "tcp"), "success" + ) def test_list_port_fwd(self): - ''' + """ Test listing all port forwarding for a zone - ''' - ret = 'port=23:proto=tcp:toport=8080:toaddr=\nport=80:proto=tcp:toport=443:toaddr=' - exp = [{'Destination address': '', - 'Destination port': '8080', - 'Protocol': 'tcp', - 'Source port': '23'}, - {'Destination address': '', - 'Destination port': '443', - 'Protocol': 'tcp', - 'Source port': '80'}] + """ + ret = "port=23:proto=tcp:toport=8080:toaddr=\nport=80:proto=tcp:toport=443:toaddr=" + exp = [ + { + "Destination address": "", + "Destination port": "8080", + "Protocol": "tcp", + "Source port": "23", + }, + { + "Destination address": "", + "Destination port": "443", + "Protocol": "tcp", + "Source port": "80", + }, + ] - with patch.object(firewalld, '__firewall_cmd', return_value=ret): - self.assertEqual(firewalld.list_port_fwd('zone'), exp) + with patch.object(firewalld, "__firewall_cmd", return_value=ret): + self.assertEqual(firewalld.list_port_fwd("zone"), exp) def test_block_icmp(self): - ''' + """ Test ICMP block - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - with patch.object(firewalld, 'get_icmp_types', return_value='echo-reply'): - self.assertEqual(firewalld.block_icmp('zone', 'echo-reply'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + with patch.object(firewalld, "get_icmp_types", return_value="echo-reply"): + self.assertEqual(firewalld.block_icmp("zone", "echo-reply"), "success") - with patch.object(firewalld, '__firewall_cmd'): - self.assertFalse(firewalld.block_icmp('zone', 'echo-reply')) + with patch.object(firewalld, "__firewall_cmd"): + self.assertFalse(firewalld.block_icmp("zone", "echo-reply")) def test_allow_icmp(self): - ''' + """ Test ICMP allow - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - with patch.object(firewalld, 'get_icmp_types', return_value='echo-reply'): - self.assertEqual(firewalld.allow_icmp('zone', 'echo-reply'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + with patch.object(firewalld, "get_icmp_types", return_value="echo-reply"): + self.assertEqual(firewalld.allow_icmp("zone", "echo-reply"), "success") - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - self.assertFalse(firewalld.allow_icmp('zone', 'echo-reply')) + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + self.assertFalse(firewalld.allow_icmp("zone", "echo-reply")) def test_list_icmp_block(self): - ''' + """ Test ICMP block list - ''' - ret = 'echo-reply echo-request' - exp = ['echo-reply', 'echo-request'] + """ + ret = "echo-reply echo-request" + exp = ["echo-reply", "echo-request"] - with patch.object(firewalld, '__firewall_cmd', return_value=ret): - self.assertEqual(firewalld.list_icmp_block('zone'), exp) + with patch.object(firewalld, "__firewall_cmd", return_value=ret): + self.assertEqual(firewalld.list_icmp_block("zone"), exp) def test_get_rich_rules(self): - ''' + """ Test listing rich rules bound to a zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value=''): - self.assertEqual(firewalld.get_rich_rules('zone'), []) + """ + with patch.object(firewalld, "__firewall_cmd", return_value=""): + self.assertEqual(firewalld.get_rich_rules("zone"), []) def test_add_rich_rule(self): - ''' + """ Test adding a rich rule to a zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - self.assertEqual(firewalld.add_rich_rule('zone', 'rule family="ipv4" source address="1.2.3.4" accept'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + self.assertEqual( + firewalld.add_rich_rule( + "zone", 'rule family="ipv4" source address="1.2.3.4" accept' + ), + "success", + ) def test_remove_rich_rule(self): - ''' + """ Test removing a rich rule to a zone - ''' - with patch.object(firewalld, '__firewall_cmd', return_value='success'): - self.assertEqual(firewalld.remove_rich_rule('zone', 'rule family="ipv4" source address="1.2.3.4" accept'), 'success') + """ + with patch.object(firewalld, "__firewall_cmd", return_value="success"): + self.assertEqual( + firewalld.remove_rich_rule( + "zone", 'rule family="ipv4" source address="1.2.3.4" accept' + ), + "success", + ) diff --git a/tests/unit/modules/test_freezer.py b/tests/unit/modules/test_freezer.py index 434ddec3ec9..f4fcbfdd7ab 100644 --- a/tests/unit/modules/test_freezer.py +++ b/tests/unit/modules/test_freezer.py @@ -1,52 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" :maintainer: Alberto Planas <aplanas@suse.com> :platform: Linux -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import salt.modules.freezer as freezer +from salt.exceptions import CommandExecutionError + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import MagicMock, patch - -from salt.exceptions import CommandExecutionError -import salt.modules.freezer as freezer +from tests.support.unit import TestCase class FreezerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.freezer - ''' + """ def setup_loader_modules(self): - return { - freezer: { - '__salt__': {}, - '__opts__': {'cachedir': ''}, - } - } + return {freezer: {"__salt__": {}, "__opts__": {"cachedir": ""}}} - @patch('os.path.isfile') + @patch("os.path.isfile") def test_status(self, isfile): - ''' + """ Test if a frozen state exist. - ''' + """ isfile.side_effect = (True, True) self.assertTrue(freezer.status()) isfile.side_effect = (True, False) self.assertFalse(freezer.status()) - @patch('os.listdir') - @patch('os.path.isdir') + @patch("os.listdir") + @patch("os.path.isdir") def test_list(self, isdir, listdir): - ''' + """ Test the listing of all frozen states. - ''' + """ # There is no freezer directory isdir.return_value = False self.assertEqual(freezer.list_(), []) @@ -59,239 +54,256 @@ class FreezerTestCase(TestCase, LoaderModuleMockMixin): # There is freezer directory with states isdir.return_value = True listdir.return_value = [ - 'freezer-pkgs.yml', 'freezer-reps.yml', - 'state-pkgs.yml', 'state-reps.yml', - 'random-file' + "freezer-pkgs.yml", + "freezer-reps.yml", + "state-pkgs.yml", + "state-reps.yml", + "random-file", ] - self.assertEqual(freezer.list_(), ['freezer', 'state']) + self.assertEqual(freezer.list_(), ["freezer", "state"]) - @patch('os.makedirs') + @patch("os.makedirs") def test_freeze_fails_cache(self, makedirs): - ''' + """ Test to freeze a current installation - ''' + """ # Fails when creating the freeze cache directory makedirs.side_effect = OSError() self.assertRaises(CommandExecutionError, freezer.freeze) - @patch('salt.modules.freezer.status') - @patch('os.makedirs') + @patch("salt.modules.freezer.status") + @patch("os.makedirs") def test_freeze_fails_already_frozen(self, makedirs, status): - ''' + """ Test to freeze a current installation - ''' + """ # Fails when there is already a frozen state status.return_value = True self.assertRaises(CommandExecutionError, freezer.freeze) makedirs.assert_called_once() - @patch('salt.utils.json.dump') - @patch('salt.modules.freezer.fopen') - @patch('salt.modules.freezer.status') - @patch('os.makedirs') + @patch("salt.utils.json.dump") + @patch("salt.modules.freezer.fopen") + @patch("salt.modules.freezer.status") + @patch("os.makedirs") def test_freeze_success_two_freeze(self, makedirs, status, fopen, dump): - ''' + """ Test to freeze a current installation - ''' + """ # Freeze the current new state status.return_value = False salt_mock = { - 'pkg.list_pkgs': MagicMock(return_value={}), - 'pkg.list_repos': MagicMock(return_value={}), + "pkg.list_pkgs": MagicMock(return_value={}), + "pkg.list_repos": MagicMock(return_value={}), } with patch.dict(freezer.__salt__, salt_mock): - self.assertTrue(freezer.freeze('one')) - self.assertTrue(freezer.freeze('two')) + self.assertTrue(freezer.freeze("one")) + self.assertTrue(freezer.freeze("two")) self.assertEqual(makedirs.call_count, 2) - self.assertEqual(salt_mock['pkg.list_pkgs'].call_count, 2) - self.assertEqual(salt_mock['pkg.list_repos'].call_count, 2) + self.assertEqual(salt_mock["pkg.list_pkgs"].call_count, 2) + self.assertEqual(salt_mock["pkg.list_repos"].call_count, 2) fopen.assert_called() dump.assert_called() - @patch('salt.utils.json.dump') - @patch('salt.modules.freezer.fopen') - @patch('salt.modules.freezer.status') - @patch('os.makedirs') + @patch("salt.utils.json.dump") + @patch("salt.modules.freezer.fopen") + @patch("salt.modules.freezer.status") + @patch("os.makedirs") def test_freeze_success_new_state(self, makedirs, status, fopen, dump): - ''' + """ Test to freeze a current installation - ''' + """ # Freeze the current new state status.return_value = False salt_mock = { - 'pkg.list_pkgs': MagicMock(return_value={}), - 'pkg.list_repos': MagicMock(return_value={}), + "pkg.list_pkgs": MagicMock(return_value={}), + "pkg.list_repos": MagicMock(return_value={}), } with patch.dict(freezer.__salt__, salt_mock): self.assertTrue(freezer.freeze()) makedirs.assert_called_once() - salt_mock['pkg.list_pkgs'].assert_called_once() - salt_mock['pkg.list_repos'].assert_called_once() + salt_mock["pkg.list_pkgs"].assert_called_once() + salt_mock["pkg.list_repos"].assert_called_once() fopen.assert_called() dump.assert_called() - @patch('salt.utils.json.dump') - @patch('salt.modules.freezer.fopen') - @patch('salt.modules.freezer.status') - @patch('os.makedirs') + @patch("salt.utils.json.dump") + @patch("salt.modules.freezer.fopen") + @patch("salt.modules.freezer.status") + @patch("os.makedirs") def test_freeze_success_force(self, makedirs, status, fopen, dump): - ''' + """ Test to freeze a current installation - ''' + """ # Freeze the current old state status.return_value = True salt_mock = { - 'pkg.list_pkgs': MagicMock(return_value={}), - 'pkg.list_repos': MagicMock(return_value={}), + "pkg.list_pkgs": MagicMock(return_value={}), + "pkg.list_repos": MagicMock(return_value={}), } with patch.dict(freezer.__salt__, salt_mock): self.assertTrue(freezer.freeze(force=True)) makedirs.assert_called_once() - salt_mock['pkg.list_pkgs'].assert_called_once() - salt_mock['pkg.list_repos'].assert_called_once() + salt_mock["pkg.list_pkgs"].assert_called_once() + salt_mock["pkg.list_repos"].assert_called_once() fopen.assert_called() dump.assert_called() - @patch('salt.modules.freezer.status') + @patch("salt.modules.freezer.status") def test_restore_fails_missing_state(self, status): - ''' + """ Test to restore an old state - ''' + """ # Fails if the state is not found status.return_value = False self.assertRaises(CommandExecutionError, freezer.restore) - @patch('salt.utils.json.load') - @patch('salt.modules.freezer.fopen') - @patch('salt.modules.freezer.status') + @patch("salt.utils.json.load") + @patch("salt.modules.freezer.fopen") + @patch("salt.modules.freezer.status") def test_restore_add_missing_repo(self, status, fopen, load): - ''' + """ Test to restore an old state - ''' + """ # Only a missing repo is installed status.return_value = True - load.side_effect = ({}, {'missing-repo': {}}) + load.side_effect = ({}, {"missing-repo": {}}) salt_mock = { - 'pkg.list_pkgs': MagicMock(return_value={}), - 'pkg.list_repos': MagicMock(return_value={}), - 'pkg.mod_repo': MagicMock(), + "pkg.list_pkgs": MagicMock(return_value={}), + "pkg.list_repos": MagicMock(return_value={}), + "pkg.mod_repo": MagicMock(), } with patch.dict(freezer.__salt__, salt_mock): - self.assertEqual(freezer.restore(), { - 'pkgs': {'add': [], 'remove': []}, - 'repos': {'add': ['missing-repo'], 'remove': []}, - 'comment': [], - }) - salt_mock['pkg.list_pkgs'].assert_called() - salt_mock['pkg.list_repos'].assert_called() - salt_mock['pkg.mod_repo'].assert_called_once() + self.assertEqual( + freezer.restore(), + { + "pkgs": {"add": [], "remove": []}, + "repos": {"add": ["missing-repo"], "remove": []}, + "comment": [], + }, + ) + salt_mock["pkg.list_pkgs"].assert_called() + salt_mock["pkg.list_repos"].assert_called() + salt_mock["pkg.mod_repo"].assert_called_once() fopen.assert_called() load.assert_called() - @patch('salt.utils.json.load') - @patch('salt.modules.freezer.fopen') - @patch('salt.modules.freezer.status') + @patch("salt.utils.json.load") + @patch("salt.modules.freezer.fopen") + @patch("salt.modules.freezer.status") def test_restore_add_missing_package(self, status, fopen, load): - ''' + """ Test to restore an old state - ''' + """ # Only a missing package is installed status.return_value = True - load.side_effect = ({'missing-package': {}}, {}) + load.side_effect = ({"missing-package": {}}, {}) salt_mock = { - 'pkg.list_pkgs': MagicMock(return_value={}), - 'pkg.list_repos': MagicMock(return_value={}), - 'pkg.install': MagicMock(), + "pkg.list_pkgs": MagicMock(return_value={}), + "pkg.list_repos": MagicMock(return_value={}), + "pkg.install": MagicMock(), } with patch.dict(freezer.__salt__, salt_mock): - self.assertEqual(freezer.restore(), { - 'pkgs': {'add': ['missing-package'], 'remove': []}, - 'repos': {'add': [], 'remove': []}, - 'comment': [], - }) - salt_mock['pkg.list_pkgs'].assert_called() - salt_mock['pkg.list_repos'].assert_called() - salt_mock['pkg.install'].assert_called_once() + self.assertEqual( + freezer.restore(), + { + "pkgs": {"add": ["missing-package"], "remove": []}, + "repos": {"add": [], "remove": []}, + "comment": [], + }, + ) + salt_mock["pkg.list_pkgs"].assert_called() + salt_mock["pkg.list_repos"].assert_called() + salt_mock["pkg.install"].assert_called_once() fopen.assert_called() load.assert_called() - @patch('salt.utils.json.load') - @patch('salt.modules.freezer.fopen') - @patch('salt.modules.freezer.status') + @patch("salt.utils.json.load") + @patch("salt.modules.freezer.fopen") + @patch("salt.modules.freezer.status") def test_restore_remove_extra_package(self, status, fopen, load): - ''' + """ Test to restore an old state - ''' + """ # Only an extra package is removed status.return_value = True load.side_effect = ({}, {}) salt_mock = { - 'pkg.list_pkgs': MagicMock(return_value={'extra-package': {}}), - 'pkg.list_repos': MagicMock(return_value={}), - 'pkg.remove': MagicMock(), + "pkg.list_pkgs": MagicMock(return_value={"extra-package": {}}), + "pkg.list_repos": MagicMock(return_value={}), + "pkg.remove": MagicMock(), } with patch.dict(freezer.__salt__, salt_mock): - self.assertEqual(freezer.restore(), { - 'pkgs': {'add': [], 'remove': ['extra-package']}, - 'repos': {'add': [], 'remove': []}, - 'comment': [], - }) - salt_mock['pkg.list_pkgs'].assert_called() - salt_mock['pkg.list_repos'].assert_called() - salt_mock['pkg.remove'].assert_called_once() + self.assertEqual( + freezer.restore(), + { + "pkgs": {"add": [], "remove": ["extra-package"]}, + "repos": {"add": [], "remove": []}, + "comment": [], + }, + ) + salt_mock["pkg.list_pkgs"].assert_called() + salt_mock["pkg.list_repos"].assert_called() + salt_mock["pkg.remove"].assert_called_once() fopen.assert_called() load.assert_called() - @patch('salt.utils.json.load') - @patch('salt.modules.freezer.fopen') - @patch('salt.modules.freezer.status') + @patch("salt.utils.json.load") + @patch("salt.modules.freezer.fopen") + @patch("salt.modules.freezer.status") def test_restore_remove_extra_repo(self, status, fopen, load): - ''' + """ Test to restore an old state - ''' + """ # Only an extra repository is removed status.return_value = True load.side_effect = ({}, {}) salt_mock = { - 'pkg.list_pkgs': MagicMock(return_value={}), - 'pkg.list_repos': MagicMock(return_value={'extra-repo': {}}), - 'pkg.del_repo': MagicMock(), + "pkg.list_pkgs": MagicMock(return_value={}), + "pkg.list_repos": MagicMock(return_value={"extra-repo": {}}), + "pkg.del_repo": MagicMock(), } with patch.dict(freezer.__salt__, salt_mock): - self.assertEqual(freezer.restore(), { - 'pkgs': {'add': [], 'remove': []}, - 'repos': {'add': [], 'remove': ['extra-repo']}, - 'comment': [], - }) - salt_mock['pkg.list_pkgs'].assert_called() - salt_mock['pkg.list_repos'].assert_called() - salt_mock['pkg.del_repo'].assert_called_once() + self.assertEqual( + freezer.restore(), + { + "pkgs": {"add": [], "remove": []}, + "repos": {"add": [], "remove": ["extra-repo"]}, + "comment": [], + }, + ) + salt_mock["pkg.list_pkgs"].assert_called() + salt_mock["pkg.list_repos"].assert_called() + salt_mock["pkg.del_repo"].assert_called_once() fopen.assert_called() load.assert_called() - @patch('os.remove') - @patch('salt.utils.json.load') - @patch('salt.modules.freezer.fopen') - @patch('salt.modules.freezer.status') + @patch("os.remove") + @patch("salt.utils.json.load") + @patch("salt.modules.freezer.fopen") + @patch("salt.modules.freezer.status") def test_restore_clean_yml(self, status, fopen, load, remove): - ''' + """ Test to restore an old state - ''' + """ status.return_value = True salt_mock = { - 'pkg.list_pkgs': MagicMock(return_value={}), - 'pkg.list_repos': MagicMock(return_value={}), - 'pkg.install': MagicMock(), + "pkg.list_pkgs": MagicMock(return_value={}), + "pkg.list_repos": MagicMock(return_value={}), + "pkg.install": MagicMock(), } with patch.dict(freezer.__salt__, salt_mock): - self.assertEqual(freezer.restore(clean=True), { - 'pkgs': {'add': [], 'remove': []}, - 'repos': {'add': [], 'remove': []}, - 'comment': [], - }) - salt_mock['pkg.list_pkgs'].assert_called() - salt_mock['pkg.list_repos'].assert_called() + self.assertEqual( + freezer.restore(clean=True), + { + "pkgs": {"add": [], "remove": []}, + "repos": {"add": [], "remove": []}, + "comment": [], + }, + ) + salt_mock["pkg.list_pkgs"].assert_called() + salt_mock["pkg.list_repos"].assert_called() fopen.assert_called() load.assert_called() self.assertEqual(remove.call_count, 2) diff --git a/tests/unit/modules/test_gem.py b/tests/unit/modules/test_gem.py index 0d4d8f9e654..990fb24aba7 100644 --- a/tests/unit/modules/test_gem.py +++ b/tests/unit/modules/test_gem.py @@ -1,109 +1,120 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.modules.gem as gem +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class TestGemModule(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {gem: {}} def test_gem(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(gem.__salt__, - {'rvm.is_installed': MagicMock(return_value=False), - 'rbenv.is_installed': MagicMock(return_value=False), - 'cmd.run_all': mock}): - gem._gem(['install', 'rails']) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + gem.__salt__, + { + "rvm.is_installed": MagicMock(return_value=False), + "rbenv.is_installed": MagicMock(return_value=False), + "cmd.run_all": mock, + }, + ): + gem._gem(["install", "rails"]) mock.assert_called_once_with( - ['gem', 'install', 'rails'], - runas=None, - python_shell=False + ["gem", "install", "rails"], runas=None, python_shell=False ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) rvm_mock = MagicMock() - with patch.dict(gem.__salt__, - {'rvm.is_installed': rvm_mock, - 'rbenv.is_installed': rvm_mock, - 'cmd.run_all': mock}): - gem._gem(['install', 'rails'], gem_bin='/usr/local/bin/gem') + with patch.dict( + gem.__salt__, + { + "rvm.is_installed": rvm_mock, + "rbenv.is_installed": rvm_mock, + "cmd.run_all": mock, + }, + ): + gem._gem(["install", "rails"], gem_bin="/usr/local/bin/gem") self.assertEqual( False, rvm_mock.called, - 'Should never call rvm.is_installed if gem_bin provided' + "Should never call rvm.is_installed if gem_bin provided", ) mock.assert_called_once_with( - ['/usr/local/bin/gem', 'install', 'rails'], + ["/usr/local/bin/gem", "install", "rails"], runas=None, - python_shell=False + python_shell=False, ) mock = MagicMock(return_value=None) - with patch.dict(gem.__salt__, - {'rvm.is_installed': MagicMock(return_value=True), - 'rbenv.is_installed': MagicMock(return_value=False), - 'rvm.do': mock}): - gem._gem(['install', 'rails'], ruby='1.9.3') + with patch.dict( + gem.__salt__, + { + "rvm.is_installed": MagicMock(return_value=True), + "rbenv.is_installed": MagicMock(return_value=False), + "rvm.do": mock, + }, + ): + gem._gem(["install", "rails"], ruby="1.9.3") mock.assert_called_once_with( - '1.9.3', - ['gem', 'install', 'rails'], - runas=None + "1.9.3", ["gem", "install", "rails"], runas=None ) mock = MagicMock(return_value=None) - with patch.dict(gem.__salt__, - {'rvm.is_installed': MagicMock(return_value=False), - 'rbenv.is_installed': MagicMock(return_value=True), - 'rbenv.do': mock}),\ - patch('salt.utils.platform.is_windows', return_value=False): - gem._gem(['install', 'rails']) - mock.assert_called_once_with( - ['gem', 'install', 'rails'], - runas=None - ) + with patch.dict( + gem.__salt__, + { + "rvm.is_installed": MagicMock(return_value=False), + "rbenv.is_installed": MagicMock(return_value=True), + "rbenv.do": mock, + }, + ), patch("salt.utils.platform.is_windows", return_value=False): + gem._gem(["install", "rails"]) + mock.assert_called_once_with(["gem", "install", "rails"], runas=None) def test_install_pre_rubygems_3(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(gem.__salt__, - {'rvm.is_installed': MagicMock(return_value=False), - 'rbenv.is_installed': MagicMock(return_value=False), - 'cmd.run_all': mock}),\ - patch.object( - gem, '_has_rubygems_3', MagicMock(return_value=True)): - gem.install('rails', pre_releases=True) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + gem.__salt__, + { + "rvm.is_installed": MagicMock(return_value=False), + "rbenv.is_installed": MagicMock(return_value=False), + "cmd.run_all": mock, + }, + ), patch.object(gem, "_has_rubygems_3", MagicMock(return_value=True)): + gem.install("rails", pre_releases=True) mock.assert_called_once_with( - ['gem', 'install', 'rails', '--no-document', '--prerelease'], + ["gem", "install", "rails", "--no-document", "--prerelease"], runas=None, - python_shell=False + python_shell=False, ) def test_install_pre(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(gem.__salt__, - {'rvm.is_installed': MagicMock(return_value=False), - 'rbenv.is_installed': MagicMock(return_value=False), - 'cmd.run_all': mock}),\ - patch.object( - gem, '_has_rubygems_3', MagicMock(return_value=False)): - gem.install('rails', pre_releases=True) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + gem.__salt__, + { + "rvm.is_installed": MagicMock(return_value=False), + "rbenv.is_installed": MagicMock(return_value=False), + "cmd.run_all": mock, + }, + ), patch.object(gem, "_has_rubygems_3", MagicMock(return_value=False)): + gem.install("rails", pre_releases=True) mock.assert_called_once_with( - ['gem', 'install', 'rails', '--no-rdoc', '--no-ri', '--pre'], + ["gem", "install", "rails", "--no-rdoc", "--no-ri", "--pre"], runas=None, - python_shell=False + python_shell=False, ) def test_list(self): - output = ''' + output = """ actionmailer (2.3.14) actionpack (2.3.14) activerecord (2.3.14) @@ -112,40 +123,41 @@ activesupport (3.0.5, 2.3.14) rake (0.9.2, 0.8.7) responds_to_parent (1.0.20091013) sass (3.1.15, 3.1.7) -''' +""" mock = MagicMock(return_value=output) - with patch.object(gem, '_gem', new=mock): + with patch.object(gem, "_gem", new=mock): self.assertEqual( - {'actionmailer': ['2.3.14'], - 'actionpack': ['2.3.14'], - 'activerecord': ['2.3.14'], - 'activeresource': ['2.3.14'], - 'activesupport': ['3.0.5', '2.3.14'], - 'rake': ['0.9.2', '0.8.7'], - 'responds_to_parent': ['1.0.20091013'], - 'sass': ['3.1.15', '3.1.7']}, - gem.list_()) + { + "actionmailer": ["2.3.14"], + "actionpack": ["2.3.14"], + "activerecord": ["2.3.14"], + "activeresource": ["2.3.14"], + "activesupport": ["3.0.5", "2.3.14"], + "rake": ["0.9.2", "0.8.7"], + "responds_to_parent": ["1.0.20091013"], + "sass": ["3.1.15", "3.1.7"], + }, + gem.list_(), + ) def test_list_upgrades(self): - output = ''' + output = """ arel (5.0.1.20140414130214 < 6.0.0) rails (4.1.9 < 4.2.0) rake (10.3.2 < 10.4.2) -''' +""" mock = MagicMock(return_value=output) - with patch.object(gem, '_gem', new=mock): + with patch.object(gem, "_gem", new=mock): self.assertEqual( - {'arel': '6.0.0', - 'rails': '4.2.0', - 'rake': '10.4.2'}, gem.list_upgrades() + {"arel": "6.0.0", "rails": "4.2.0", "rake": "10.4.2"}, + gem.list_upgrades(), ) def test_sources_list(self): - output = '''*** CURRENT SOURCES *** + output = """*** CURRENT SOURCES *** http://rubygems.org/ -''' +""" mock = MagicMock(return_value=output) - with patch.object(gem, '_gem', new=mock): - self.assertEqual( - ['http://rubygems.org/'], gem.sources_list()) + with patch.object(gem, "_gem", new=mock): + self.assertEqual(["http://rubygems.org/"], gem.sources_list()) diff --git a/tests/unit/modules/test_genesis.py b/tests/unit/modules/test_genesis.py index 305ec097d04..a4f7a134376 100644 --- a/tests/unit/modules/test_genesis.py +++ b/tests/unit/modules/test_genesis.py @@ -1,134 +1,202 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals import sys -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.genesis as genesis +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class GenesisTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.genesis - ''' + """ + def setup_loader_modules(self): return {genesis: {}} def test_bootstrap(self): - ''' + """ Test for Create an image for a specific platform. - ''' + """ # Changed in 3.7.0 pformat no longer includes the comma if sys.version_info >= (3, 7): - exception_string = 'Exception({0})'.format(repr('foo')) + exception_string = "Exception({0})".format(repr("foo")) else: - exception_string = 'Exception({0},)'.format(repr('foo')) + exception_string = "Exception({0},)".format(repr("foo")) mock = MagicMock(return_value=False) - with patch.dict(genesis.__salt__, {'file.directory_exists': mock}): - mock = MagicMock(side_effect=Exception('foo')) - with patch.dict(genesis.__salt__, {'file.mkdir': mock}): - self.assertEqual(genesis.bootstrap('platform', 'root'), - {'Error': exception_string}) + with patch.dict(genesis.__salt__, {"file.directory_exists": mock}): + mock = MagicMock(side_effect=Exception("foo")) + with patch.dict(genesis.__salt__, {"file.mkdir": mock}): + self.assertEqual( + genesis.bootstrap("platform", "root"), {"Error": exception_string} + ) - with patch.object(genesis, '_bootstrap_yum', return_value='A'): - with patch.dict(genesis.__salt__, {'mount.umount': MagicMock(), - 'file.rmdir': MagicMock(), - 'file.directory_exists': MagicMock()}): - with patch.dict(genesis.__salt__, {'disk.blkid': MagicMock(return_value={})}): - self.assertEqual(genesis.bootstrap('rpm', 'root', 'dir'), None) + with patch.object(genesis, "_bootstrap_yum", return_value="A"): + with patch.dict( + genesis.__salt__, + { + "mount.umount": MagicMock(), + "file.rmdir": MagicMock(), + "file.directory_exists": MagicMock(), + }, + ): + with patch.dict( + genesis.__salt__, {"disk.blkid": MagicMock(return_value={})} + ): + self.assertEqual(genesis.bootstrap("rpm", "root", "dir"), None) - common_parms = {'platform': 'deb', - 'root': 'root', - 'img_format': 'dir', - 'arch': 'amd64', - 'flavor': 'stable', - 'static_qemu': 'qemu'} + common_parms = { + "platform": "deb", + "root": "root", + "img_format": "dir", + "arch": "amd64", + "flavor": "stable", + "static_qemu": "qemu", + } param_sets = [ - - {'params': {}, - 'cmd': ['debootstrap', '--foreign', '--arch', 'amd64', - 'stable', 'root', 'http://ftp.debian.org/debian/'] - }, - - {'params': {'pkgs': 'vim'}, - 'cmd': ['debootstrap', '--foreign', '--arch', 'amd64', - '--include', 'vim', - 'stable', 'root', 'http://ftp.debian.org/debian/'] - }, - - {'params': {'pkgs': 'vim,emacs'}, - 'cmd': ['debootstrap', '--foreign', '--arch', 'amd64', - '--include', 'vim,emacs', - 'stable', 'root', 'http://ftp.debian.org/debian/'] - }, - - {'params': {'pkgs': ['vim', 'emacs']}, - 'cmd': ['debootstrap', '--foreign', '--arch', 'amd64', - '--include', 'vim,emacs', - 'stable', 'root', 'http://ftp.debian.org/debian/'] - }, - - {'params': {'pkgs': ['vim', 'emacs'], 'exclude_pkgs': ['vim', 'foo']}, - 'cmd': ['debootstrap', '--foreign', '--arch', 'amd64', - '--include', 'vim,emacs', '--exclude', 'vim,foo', - 'stable', 'root', 'http://ftp.debian.org/debian/'] - }, - + { + "params": {}, + "cmd": [ + "debootstrap", + "--foreign", + "--arch", + "amd64", + "stable", + "root", + "http://ftp.debian.org/debian/", + ], + }, + { + "params": {"pkgs": "vim"}, + "cmd": [ + "debootstrap", + "--foreign", + "--arch", + "amd64", + "--include", + "vim", + "stable", + "root", + "http://ftp.debian.org/debian/", + ], + }, + { + "params": {"pkgs": "vim,emacs"}, + "cmd": [ + "debootstrap", + "--foreign", + "--arch", + "amd64", + "--include", + "vim,emacs", + "stable", + "root", + "http://ftp.debian.org/debian/", + ], + }, + { + "params": {"pkgs": ["vim", "emacs"]}, + "cmd": [ + "debootstrap", + "--foreign", + "--arch", + "amd64", + "--include", + "vim,emacs", + "stable", + "root", + "http://ftp.debian.org/debian/", + ], + }, + { + "params": {"pkgs": ["vim", "emacs"], "exclude_pkgs": ["vim", "foo"]}, + "cmd": [ + "debootstrap", + "--foreign", + "--arch", + "amd64", + "--include", + "vim,emacs", + "--exclude", + "vim,foo", + "stable", + "root", + "http://ftp.debian.org/debian/", + ], + }, ] for param_set in param_sets: - with patch.dict(genesis.__salt__, {'mount.umount': MagicMock(), - 'file.rmdir': MagicMock(), - 'file.directory_exists': MagicMock(), - 'cmd.run': MagicMock(), - 'disk.blkid': MagicMock(return_value={})}): - with patch('salt.modules.genesis.salt.utils.path.which', return_value=True): - with patch('salt.modules.genesis.salt.utils.validate.path.is_executable', - return_value=True): - param_set['params'].update(common_parms) - self.assertEqual(genesis.bootstrap(**param_set['params']), None) - genesis.__salt__['cmd.run'].assert_any_call(param_set['cmd'], python_shell=False) + with patch.dict( + genesis.__salt__, + { + "mount.umount": MagicMock(), + "file.rmdir": MagicMock(), + "file.directory_exists": MagicMock(), + "cmd.run": MagicMock(), + "disk.blkid": MagicMock(return_value={}), + }, + ): + with patch( + "salt.modules.genesis.salt.utils.path.which", return_value=True + ): + with patch( + "salt.modules.genesis.salt.utils.validate.path.is_executable", + return_value=True, + ): + param_set["params"].update(common_parms) + self.assertEqual(genesis.bootstrap(**param_set["params"]), None) + genesis.__salt__["cmd.run"].assert_any_call( + param_set["cmd"], python_shell=False + ) - with patch.object(genesis, '_bootstrap_pacman', return_value='A') as pacman_patch: - with patch.dict(genesis.__salt__, {'mount.umount': MagicMock(), - 'file.rmdir': MagicMock(), - 'file.directory_exists': MagicMock(), - 'disk.blkid': MagicMock(return_value={})}): - genesis.bootstrap('pacman', 'root', 'dir') - pacman_patch.assert_called_with('root', img_format='dir', exclude_pkgs=[], pkgs=[]) + with patch.object( + genesis, "_bootstrap_pacman", return_value="A" + ) as pacman_patch: + with patch.dict( + genesis.__salt__, + { + "mount.umount": MagicMock(), + "file.rmdir": MagicMock(), + "file.directory_exists": MagicMock(), + "disk.blkid": MagicMock(return_value={}), + }, + ): + genesis.bootstrap("pacman", "root", "dir") + pacman_patch.assert_called_with( + "root", img_format="dir", exclude_pkgs=[], pkgs=[] + ) def test_avail_platforms(self): - ''' + """ Test for Return which platforms are available - ''' - with patch('salt.utils.path.which', MagicMock(return_value=False)): - self.assertFalse(genesis.avail_platforms()['deb']) + """ + with patch("salt.utils.path.which", MagicMock(return_value=False)): + self.assertFalse(genesis.avail_platforms()["deb"]) def test_pack(self): - ''' + """ Test for Pack up a directory structure, into a specific format - ''' - with patch.object(genesis, '_tar', return_value='tar'): - self.assertEqual(genesis.pack('name', 'root'), None) + """ + with patch.object(genesis, "_tar", return_value="tar"): + self.assertEqual(genesis.pack("name", "root"), None) def test_unpack(self): - ''' + """ Test for Unpack an image into a directory structure - ''' - with patch.object(genesis, '_untar', return_value='untar'): - self.assertEqual(genesis.unpack('name', 'root'), None) + """ + with patch.object(genesis, "_untar", return_value="untar"): + self.assertEqual(genesis.unpack("name", "root"), None) diff --git a/tests/unit/modules/test_gentoo_service.py b/tests/unit/modules/test_gentoo_service.py index c986a7482ee..2861960a55c 100644 --- a/tests/unit/modules/test_gentoo_service.py +++ b/tests/unit/modules/test_gentoo_service.py @@ -3,109 +3,110 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - call -) - # Import Salt Libs import salt.modules.gentoo_service as gentoo_service +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase + class GentooServicesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.gentoo_service - ''' + """ def setup_loader_modules(self): return {gentoo_service: {}} def test_service_list_parser(self): - ''' + """ Test for parser of rc-status results - ''' + """ # no services is enabled - mock = MagicMock(return_value='') - with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}): + mock = MagicMock(return_value="") + with patch.dict(gentoo_service.__salt__, {"cmd.run": mock}): self.assertFalse(gentoo_service.get_enabled()) - mock.assert_called_once_with('rc-update -v show') + mock.assert_called_once_with("rc-update -v show") def test_get_enabled_single_runlevel(self): - ''' + """ Test for Return a list of service that are enabled on boot - ''' - service_name = 'name' - runlevels = ['default'] + """ + service_name = "name" + runlevels = ["default"] mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.run": mock}): enabled_services = gentoo_service.get_enabled() self.assertTrue(service_name in enabled_services) self.assertEqual(enabled_services[service_name], runlevels) def test_get_enabled_filters_out_disabled_services(self): - ''' + """ Test for Return a list of service that are enabled on boot - ''' - service_name = 'name' - runlevels = ['default'] - disabled_service = 'disabled' + """ + service_name = "name" + runlevels = ["default"] + disabled_service = "disabled" service_list = self.__services({service_name: runlevels, disabled_service: []}) mock = MagicMock(return_value=service_list) - with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.run": mock}): enabled_services = gentoo_service.get_enabled() self.assertEqual(len(enabled_services), 1) self.assertTrue(service_name in enabled_services) self.assertEqual(enabled_services[service_name], runlevels) def test_get_enabled_with_multiple_runlevels(self): - ''' + """ Test for Return a list of service that are enabled on boot at more than one runlevel - ''' - service_name = 'name' - runlevels = ['non-default', 'default'] + """ + service_name = "name" + runlevels = ["non-default", "default"] mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.run": mock}): enabled_services = gentoo_service.get_enabled() self.assertTrue(service_name in enabled_services) self.assertEqual(enabled_services[service_name][0], runlevels[1]) self.assertEqual(enabled_services[service_name][1], runlevels[0]) def test_get_disabled(self): - ''' + """ Test for Return a list of service that are installed but disabled - ''' - disabled_service = 'disabled' - enabled_service = 'enabled' - service_list = self.__services({disabled_service: [], - enabled_service: ['default']}) + """ + disabled_service = "disabled" + enabled_service = "enabled" + service_list = self.__services( + {disabled_service: [], enabled_service: ["default"]} + ) mock = MagicMock(return_value=service_list) - with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.run": mock}): disabled_services = gentoo_service.get_disabled() self.assertTrue(len(disabled_services), 1) self.assertTrue(disabled_service in disabled_services) def test_available(self): - ''' + """ Test for Returns ``True`` if the specified service is available, otherwise returns ``False``. - ''' - disabled_service = 'disabled' - enabled_service = 'enabled' - multilevel_service = 'multilevel' - missing_service = 'missing' - shutdown_service = 'shutdown' - service_list = self.__services({disabled_service: [], - enabled_service: ['default'], - multilevel_service: ['default', 'shutdown'], - shutdown_service: ['shutdown']}) + """ + disabled_service = "disabled" + enabled_service = "enabled" + multilevel_service = "multilevel" + missing_service = "missing" + shutdown_service = "shutdown" + service_list = self.__services( + { + disabled_service: [], + enabled_service: ["default"], + multilevel_service: ["default", "shutdown"], + shutdown_service: ["shutdown"], + } + ) mock = MagicMock(return_value=service_list) - with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.run": mock}): self.assertTrue(gentoo_service.available(enabled_service)) self.assertTrue(gentoo_service.available(multilevel_service)) self.assertTrue(gentoo_service.available(disabled_service)) @@ -113,380 +114,426 @@ class GentooServicesTestCase(TestCase, LoaderModuleMockMixin): self.assertFalse(gentoo_service.available(missing_service)) def test_missing(self): - ''' + """ Test for The inverse of service.available. - ''' - disabled_service = 'disabled' - enabled_service = 'enabled' - service_list = self.__services({disabled_service: [], - enabled_service: ['default']}) + """ + disabled_service = "disabled" + enabled_service = "enabled" + service_list = self.__services( + {disabled_service: [], enabled_service: ["default"]} + ) mock = MagicMock(return_value=service_list) - with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.run": mock}): self.assertFalse(gentoo_service.missing(enabled_service)) self.assertFalse(gentoo_service.missing(disabled_service)) - self.assertTrue(gentoo_service.missing('missing')) + self.assertTrue(gentoo_service.missing("missing")) def test_getall(self): - ''' + """ Test for Return all available boot services - ''' - disabled_service = 'disabled' - enabled_service = 'enabled' - service_list = self.__services({disabled_service: [], - enabled_service: ['default']}) + """ + disabled_service = "disabled" + enabled_service = "enabled" + service_list = self.__services( + {disabled_service: [], enabled_service: ["default"]} + ) mock = MagicMock(return_value=service_list) - with patch.dict(gentoo_service.__salt__, {'cmd.run': mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.run": mock}): all_services = gentoo_service.get_all() self.assertEqual(len(all_services), 2) self.assertTrue(disabled_service in all_services) self.assertTrue(enabled_service in all_services) def test_start(self): - ''' + """ Test for Start the specified service - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(gentoo_service.start('name')) - mock.assert_called_once_with('/etc/init.d/name start', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(gentoo_service.start("name")) + mock.assert_called_once_with( + "/etc/init.d/name start", ignore_retcode=False, python_shell=False + ) def test_stop(self): - ''' + """ Test for Stop the specified service - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(gentoo_service.stop('name')) - mock.assert_called_once_with('/etc/init.d/name stop', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(gentoo_service.stop("name")) + mock.assert_called_once_with( + "/etc/init.d/name stop", ignore_retcode=False, python_shell=False + ) def test_restart(self): - ''' + """ Test for Restart the named service - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(gentoo_service.restart('name')) - mock.assert_called_once_with('/etc/init.d/name restart', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(gentoo_service.restart("name")) + mock.assert_called_once_with( + "/etc/init.d/name restart", ignore_retcode=False, python_shell=False + ) def test_reload_(self): - ''' + """ Test for Reload the named service - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(gentoo_service.reload_('name')) - mock.assert_called_once_with('/etc/init.d/name reload', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(gentoo_service.reload_("name")) + mock.assert_called_once_with( + "/etc/init.d/name reload", ignore_retcode=False, python_shell=False + ) def test_zap(self): - ''' + """ Test for Reload the named service - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(gentoo_service.zap('name')) - mock.assert_called_once_with('/etc/init.d/name zap', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(gentoo_service.zap("name")) + mock.assert_called_once_with( + "/etc/init.d/name zap", ignore_retcode=False, python_shell=False + ) def test_status(self): - ''' + """ Test for Return the status for a service - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(gentoo_service.__salt__, {'status.pid': mock}): - self.assertTrue(gentoo_service.status('name', 1)) + with patch.dict(gentoo_service.__salt__, {"status.pid": mock}): + self.assertTrue(gentoo_service.status("name", 1)) # service is running mock = MagicMock(return_value=0) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}): - self.assertTrue(gentoo_service.status('name')) - mock.assert_called_once_with('/etc/init.d/name status', - ignore_retcode=True, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": mock}): + self.assertTrue(gentoo_service.status("name")) + mock.assert_called_once_with( + "/etc/init.d/name status", ignore_retcode=True, python_shell=False + ) # service is not running mock = MagicMock(return_value=1) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(gentoo_service.status('name')) - mock.assert_called_once_with('/etc/init.d/name status', - ignore_retcode=True, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(gentoo_service.status("name")) + mock.assert_called_once_with( + "/etc/init.d/name status", ignore_retcode=True, python_shell=False + ) # service is stopped mock = MagicMock(return_value=3) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(gentoo_service.status('name')) - mock.assert_called_once_with('/etc/init.d/name status', - ignore_retcode=True, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(gentoo_service.status("name")) + mock.assert_called_once_with( + "/etc/init.d/name status", ignore_retcode=True, python_shell=False + ) # service has crashed mock = MagicMock(return_value=32) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': mock}): - self.assertFalse(gentoo_service.status('name')) - mock.assert_called_once_with('/etc/init.d/name status', - ignore_retcode=True, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": mock}): + self.assertFalse(gentoo_service.status("name")) + mock.assert_called_once_with( + "/etc/init.d/name status", ignore_retcode=True, python_shell=False + ) def test_enable(self): - ''' + """ Test for Enable the named service to start at boot - ''' + """ rc_update_mock = MagicMock(return_value=0) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.enable('name')) - rc_update_mock.assert_called_once_with('rc-update add name', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.enable("name")) + rc_update_mock.assert_called_once_with( + "rc-update add name", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # move service from 'l1' to 'l2' runlevel - service_name = 'name' - runlevels = ['l1'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.enable('name', runlevels='l2')) - rc_update_mock.assert_has_calls([call('rc-update delete name l1', - ignore_retcode=False, - python_shell=False), - call('rc-update add name l2', - ignore_retcode=False, - python_shell=False)]) + service_name = "name" + runlevels = ["l1"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.enable("name", runlevels="l2")) + rc_update_mock.assert_has_calls( + [ + call( + "rc-update delete name l1", ignore_retcode=False, python_shell=False + ), + call("rc-update add name l2", ignore_retcode=False, python_shell=False), + ] + ) rc_update_mock.reset_mock() # requested levels are the same as the current ones - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.enable('name', runlevels='l1')) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.enable("name", runlevels="l1")) self.assertTrue(rc_update_mock.call_count == 0) rc_update_mock.reset_mock() # same as above with the list instead of the string - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.enable('name', runlevels=['l1'])) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.enable("name", runlevels=["l1"])) self.assertTrue(rc_update_mock.call_count == 0) rc_update_mock.reset_mock() # add service to 'l2' runlevel - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.enable('name', runlevels=['l2', 'l1'])) - rc_update_mock.assert_called_once_with('rc-update add name l2', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.enable("name", runlevels=["l2", "l1"])) + rc_update_mock.assert_called_once_with( + "rc-update add name l2", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # remove service from 'l1' runlevel - runlevels = ['l1', 'l2'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.enable('name', runlevels=['l2'])) - rc_update_mock.assert_called_once_with('rc-update delete name l1', - ignore_retcode=False, - python_shell=False) + runlevels = ["l1", "l2"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.enable("name", runlevels=["l2"])) + rc_update_mock.assert_called_once_with( + "rc-update delete name l1", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # move service from 'l2' add to 'l3', leaving at l1 - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.enable('name', runlevels=['l1', 'l3'])) - rc_update_mock.assert_has_calls([call('rc-update delete name l2', - ignore_retcode=False, - python_shell=False), - call('rc-update add name l3', - ignore_retcode=False, - python_shell=False)]) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.enable("name", runlevels=["l1", "l3"])) + rc_update_mock.assert_has_calls( + [ + call( + "rc-update delete name l2", ignore_retcode=False, python_shell=False + ), + call("rc-update add name l3", ignore_retcode=False, python_shell=False), + ] + ) rc_update_mock.reset_mock() # remove from l1, l3, and add to l2, l4, and leave at l5 - runlevels = ['l1', 'l3', 'l5'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.enable('name', runlevels=['l2', 'l4', 'l5'])) - rc_update_mock.assert_has_calls([call('rc-update delete name l1 l3', - ignore_retcode=False, - python_shell=False), - call('rc-update add name l2 l4', - ignore_retcode=False, - python_shell=False)]) + runlevels = ["l1", "l3", "l5"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue( + gentoo_service.enable("name", runlevels=["l2", "l4", "l5"]) + ) + rc_update_mock.assert_has_calls( + [ + call( + "rc-update delete name l1 l3", + ignore_retcode=False, + python_shell=False, + ), + call( + "rc-update add name l2 l4", ignore_retcode=False, python_shell=False + ), + ] + ) rc_update_mock.reset_mock() # rc-update failed rc_update_mock = MagicMock(return_value=1) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertFalse(gentoo_service.enable('name')) - rc_update_mock.assert_called_once_with('rc-update add name', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertFalse(gentoo_service.enable("name")) + rc_update_mock.assert_called_once_with( + "rc-update add name", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # move service delete failed - runlevels = ['l1'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertFalse(gentoo_service.enable('name', runlevels='l2')) - rc_update_mock.assert_called_once_with('rc-update delete name l1', - ignore_retcode=False, - python_shell=False) + runlevels = ["l1"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertFalse(gentoo_service.enable("name", runlevels="l2")) + rc_update_mock.assert_called_once_with( + "rc-update delete name l1", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # move service delete succeeds. add fails rc_update_mock = MagicMock() rc_update_mock.side_effect = [0, 1] - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertFalse(gentoo_service.enable('name', runlevels='l2')) - rc_update_mock.assert_has_calls([call('rc-update delete name l1', - ignore_retcode=False, - python_shell=False), - call('rc-update add name l2', - ignore_retcode=False, - python_shell=False)]) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertFalse(gentoo_service.enable("name", runlevels="l2")) + rc_update_mock.assert_has_calls( + [ + call( + "rc-update delete name l1", ignore_retcode=False, python_shell=False + ), + call("rc-update add name l2", ignore_retcode=False, python_shell=False), + ] + ) rc_update_mock.reset_mock() def test_disable(self): - ''' + """ Test for Disable the named service to start at boot - ''' + """ rc_update_mock = MagicMock(return_value=0) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.disable('name')) - rc_update_mock.assert_called_once_with('rc-update delete name', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.disable("name")) + rc_update_mock.assert_called_once_with( + "rc-update delete name", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # disable service - service_name = 'name' - runlevels = ['l1'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.disable('name', runlevels='l1')) - rc_update_mock.assert_called_once_with('rc-update delete name l1', - ignore_retcode=False, - python_shell=False) + service_name = "name" + runlevels = ["l1"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.disable("name", runlevels="l1")) + rc_update_mock.assert_called_once_with( + "rc-update delete name l1", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # same as above with list - runlevels = ['l1'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.disable('name', runlevels=['l1'])) - rc_update_mock.assert_called_once_with('rc-update delete name l1', - ignore_retcode=False, - python_shell=False) + runlevels = ["l1"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.disable("name", runlevels=["l1"])) + rc_update_mock.assert_called_once_with( + "rc-update delete name l1", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # remove from 'l1', and leave at 'l2' - runlevels = ['l1', 'l2'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.disable('name', runlevels=['l1'])) - rc_update_mock.assert_called_once_with('rc-update delete name l1', - ignore_retcode=False, - python_shell=False) + runlevels = ["l1", "l2"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.disable("name", runlevels=["l1"])) + rc_update_mock.assert_called_once_with( + "rc-update delete name l1", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # remove from non-enabled level - runlevels = ['l2'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.disable('name', runlevels=['l1'])) + runlevels = ["l2"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.disable("name", runlevels=["l1"])) self.assertTrue(rc_update_mock.call_count == 0) rc_update_mock.reset_mock() # remove from 'l1' and 'l3', leave at 'l2' - runlevels = ['l1', 'l2', 'l3'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertTrue(gentoo_service.disable('name', runlevels=['l1', 'l3'])) - rc_update_mock.assert_called_once_with('rc-update delete name l1 l3', - ignore_retcode=False, - python_shell=False) + runlevels = ["l1", "l2", "l3"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertTrue(gentoo_service.disable("name", runlevels=["l1", "l3"])) + rc_update_mock.assert_called_once_with( + "rc-update delete name l1 l3", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # rc-update failed rc_update_mock = MagicMock(return_value=1) - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertFalse(gentoo_service.disable('name')) - rc_update_mock.assert_called_once_with('rc-update delete name', - ignore_retcode=False, - python_shell=False) + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertFalse(gentoo_service.disable("name")) + rc_update_mock.assert_called_once_with( + "rc-update delete name", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # move service delete failed - runlevels = ['l1'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertFalse(gentoo_service.disable('name', runlevels='l1')) - rc_update_mock.assert_called_once_with('rc-update delete name l1', - ignore_retcode=False, - python_shell=False) + runlevels = ["l1"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertFalse(gentoo_service.disable("name", runlevels="l1")) + rc_update_mock.assert_called_once_with( + "rc-update delete name l1", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() # move service delete succeeds. add fails - runlevels = ['l1', 'l2', 'l3'] - level_list_mock = MagicMock(return_value=self.__services({service_name: runlevels})) - with patch.dict(gentoo_service.__salt__, {'cmd.run': level_list_mock}): - with patch.dict(gentoo_service.__salt__, {'cmd.retcode': rc_update_mock}): - self.assertFalse(gentoo_service.disable('name', runlevels=['l1', 'l3'])) - rc_update_mock.assert_called_once_with('rc-update delete name l1 l3', - ignore_retcode=False, - python_shell=False) + runlevels = ["l1", "l2", "l3"] + level_list_mock = MagicMock( + return_value=self.__services({service_name: runlevels}) + ) + with patch.dict(gentoo_service.__salt__, {"cmd.run": level_list_mock}): + with patch.dict(gentoo_service.__salt__, {"cmd.retcode": rc_update_mock}): + self.assertFalse(gentoo_service.disable("name", runlevels=["l1", "l3"])) + rc_update_mock.assert_called_once_with( + "rc-update delete name l1 l3", ignore_retcode=False, python_shell=False + ) rc_update_mock.reset_mock() def test_enabled(self): - ''' + """ Test for Return True if the named service is enabled, false otherwise - ''' - mock = MagicMock(return_value={'name': ['default']}) - with patch.object(gentoo_service, 'get_enabled', mock): + """ + mock = MagicMock(return_value={"name": ["default"]}) + with patch.object(gentoo_service, "get_enabled", mock): # service is enabled at any level - self.assertTrue(gentoo_service.enabled('name')) + self.assertTrue(gentoo_service.enabled("name")) # service is enabled at the requested runlevels - self.assertTrue(gentoo_service.enabled('name', runlevels='default')) + self.assertTrue(gentoo_service.enabled("name", runlevels="default")) # service is enabled at a different runlevels - self.assertFalse(gentoo_service.enabled('name', runlevels='boot')) + self.assertFalse(gentoo_service.enabled("name", runlevels="boot")) - mock = MagicMock(return_value={'name': ['boot', 'default']}) - with patch.object(gentoo_service, 'get_enabled', mock): + mock = MagicMock(return_value={"name": ["boot", "default"]}) + with patch.object(gentoo_service, "get_enabled", mock): # service is enabled at any level - self.assertTrue(gentoo_service.enabled('name')) + self.assertTrue(gentoo_service.enabled("name")) # service is enabled at the requested runlevels - self.assertTrue(gentoo_service.enabled('name', runlevels='default')) + self.assertTrue(gentoo_service.enabled("name", runlevels="default")) # service is enabled at all levels - self.assertTrue(gentoo_service.enabled('name', runlevels=['boot', 'default'])) + self.assertTrue( + gentoo_service.enabled("name", runlevels=["boot", "default"]) + ) # service is enabled at a different runlevels - self.assertFalse(gentoo_service.enabled('name', runlevels='some-other-level')) + self.assertFalse( + gentoo_service.enabled("name", runlevels="some-other-level") + ) # service is enabled at a different runlevels - self.assertFalse(gentoo_service.enabled('name', runlevels=['boot', 'some-other-level'])) + self.assertFalse( + gentoo_service.enabled("name", runlevels=["boot", "some-other-level"]) + ) def test_disabled(self): - ''' + """ Test for Return True if the named service is disabled, false otherwise - ''' - mock = MagicMock(return_value=['name']) - with patch.object(gentoo_service, 'get_disabled', mock): - self.assertTrue(gentoo_service.disabled('name')) + """ + mock = MagicMock(return_value=["name"]) + with patch.object(gentoo_service, "get_disabled", mock): + self.assertTrue(gentoo_service.disabled("name")) def __services(self, services): - return '\n'.join([' | '.join([svc, ' '.join(services[svc])]) for svc in services]) + return "\n".join( + [" | ".join([svc, " ".join(services[svc])]) for svc in services] + ) diff --git a/tests/unit/modules/test_git.py b/tests/unit/modules/test_git.py index 62ead906e4d..83af5e1b4b9 100644 --- a/tests/unit/modules/test_git.py +++ b/tests/unit/modules/test_git.py @@ -1,217 +1,221 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Erik Johnson <erik@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import logging import os import subprocess -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch, -) +import salt.modules.git as git_mod # Don't potentially shadow GitPython # Import Salt Libs from salt.utils.versions import LooseVersion -import salt.modules.git as git_mod # Don't potentially shadow GitPython + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase log = logging.getLogger(__name__) -WORKTREE_ROOT = '/tmp/salt-tests-tmpdir/main' +WORKTREE_ROOT = "/tmp/salt-tests-tmpdir/main" WORKTREE_INFO = { WORKTREE_ROOT: { - 'HEAD': '119f025073875a938f2456f5ffd7d04e79e5a427', - 'branch': 'refs/heads/master', - 'stale': False, + "HEAD": "119f025073875a938f2456f5ffd7d04e79e5a427", + "branch": "refs/heads/master", + "stale": False, }, - '/tmp/salt-tests-tmpdir/worktree1': { - 'HEAD': 'd8d19cf75d7cc3bdc598dc2d472881d26b51a6bf', - 'branch': 'refs/heads/worktree1', - 'stale': False, + "/tmp/salt-tests-tmpdir/worktree1": { + "HEAD": "d8d19cf75d7cc3bdc598dc2d472881d26b51a6bf", + "branch": "refs/heads/worktree1", + "stale": False, }, - '/tmp/salt-tests-tmpdir/worktree2': { - 'HEAD': '56332ca504aa8b37bb62b54272d52b1d6d832629', - 'branch': 'refs/heads/worktree2', - 'stale': True, + "/tmp/salt-tests-tmpdir/worktree2": { + "HEAD": "56332ca504aa8b37bb62b54272d52b1d6d832629", + "branch": "refs/heads/worktree2", + "stale": True, }, - '/tmp/salt-tests-tmpdir/worktree3': { - 'HEAD': 'e148ea2d521313579f661373fbb93a48a5a6d40d', - 'branch': 'detached', - 'tags': ['v1.1'], - 'stale': False, + "/tmp/salt-tests-tmpdir/worktree3": { + "HEAD": "e148ea2d521313579f661373fbb93a48a5a6d40d", + "branch": "detached", + "tags": ["v1.1"], + "stale": False, }, - '/tmp/salt-tests-tmpdir/worktree4': { - 'HEAD': '6bbac64d3ad5582b3147088a708952df185db020', - 'branch': 'detached', - 'stale': True, + "/tmp/salt-tests-tmpdir/worktree4": { + "HEAD": "6bbac64d3ad5582b3147088a708952df185db020", + "branch": "detached", + "stale": True, }, } def _git_version(): git_version = subprocess.Popen( - ['git', '--version'], + ["git", "--version"], shell=False, close_fds=True, stdout=subprocess.PIPE, - stderr=subprocess.PIPE).communicate()[0] + stderr=subprocess.PIPE, + ).communicate()[0] if not git_version: - log.error('Git not installed') + log.error("Git not installed") return False - log.debug('Detected git version %s', git_version) + log.debug("Detected git version %s", git_version) return LooseVersion(git_version.split()[-1]) class GitTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.git - ''' + """ + def setup_loader_modules(self): return { - git_mod: { - '__utils__': { - 'ssh.key_is_encrypted': Mock(return_value=False) - }, - } + git_mod: {"__utils__": {"ssh.key_is_encrypted": Mock(return_value=False)}} } def test_list_worktrees(self): - ''' + """ This tests git.list_worktrees - ''' + """ + def _build_worktree_output(path): - ''' + """ Build 'git worktree list' output for a given path - ''' - return 'worktree {0}\nHEAD {1}\n{2}\n'.format( + """ + return "worktree {0}\nHEAD {1}\n{2}\n".format( path, - WORKTREE_INFO[path]['HEAD'], - 'branch {0}'.format(WORKTREE_INFO[path]['branch']) - if WORKTREE_INFO[path]['branch'] != 'detached' - else 'detached' + WORKTREE_INFO[path]["HEAD"], + "branch {0}".format(WORKTREE_INFO[path]["branch"]) + if WORKTREE_INFO[path]["branch"] != "detached" + else "detached", ) # Build dict for _cmd_run_side_effect below. Start with the output from # 'git worktree list'. _cmd_run_values = { - 'git worktree list --porcelain': '\n'.join( + "git worktree list --porcelain": "\n".join( [_build_worktree_output(x) for x in WORKTREE_INFO] ), - 'git --version': 'git version 2.7.0', + "git --version": "git version 2.7.0", } # Add 'git tag --points-at' output for detached HEAD worktrees with # tags pointing at HEAD. for path in WORKTREE_INFO: - if WORKTREE_INFO[path]['branch'] != 'detached': + if WORKTREE_INFO[path]["branch"] != "detached": continue - key = 'git tag --points-at ' + WORKTREE_INFO[path]['HEAD'] - _cmd_run_values[key] = '\n'.join( - WORKTREE_INFO[path].get('tags', []) - ) + key = "git tag --points-at " + WORKTREE_INFO[path]["HEAD"] + _cmd_run_values[key] = "\n".join(WORKTREE_INFO[path].get("tags", [])) def _cmd_run_side_effect(key, **kwargs): # Not using dict.get() here because we want to know if # _cmd_run_values doesn't account for all uses of cmd.run_all. - return {'stdout': _cmd_run_values[' '.join(key)], - 'stderr': '', - 'retcode': 0, - 'pid': 12345} + return { + "stdout": _cmd_run_values[" ".join(key)], + "stderr": "", + "retcode": 0, + "pid": 12345, + } def _isdir_side_effect(key): # os.path.isdir() would return True on a non-stale worktree - return not WORKTREE_INFO[key].get('stale', False) + return not WORKTREE_INFO[key].get("stale", False) # Build return dict for comparison worktree_ret = copy.deepcopy(WORKTREE_INFO) for key in worktree_ret: ptr = worktree_ret.get(key) - ptr['detached'] = ptr['branch'] == 'detached' - ptr['branch'] = None \ - if ptr['detached'] \ - else ptr['branch'].replace('refs/heads/', '', 1) + ptr["detached"] = ptr["branch"] == "detached" + ptr["branch"] = ( + None if ptr["detached"] else ptr["branch"].replace("refs/heads/", "", 1) + ) cmd_run_mock = MagicMock(side_effect=_cmd_run_side_effect) isdir_mock = MagicMock(side_effect=_isdir_side_effect) - with patch.dict(git_mod.__salt__, {'cmd.run_all': cmd_run_mock}): - with patch.object(os.path, 'isdir', isdir_mock): + with patch.dict(git_mod.__salt__, {"cmd.run_all": cmd_run_mock}): + with patch.object(os.path, "isdir", isdir_mock): # Test all=True. Include all return data. self.maxDiff = None self.assertEqual( - git_mod.list_worktrees( - WORKTREE_ROOT, all=True, stale=False - ), - worktree_ret + git_mod.list_worktrees(WORKTREE_ROOT, all=True, stale=False), + worktree_ret, ) # Test all=False and stale=False. Exclude stale worktrees from # return data. self.assertEqual( - git_mod.list_worktrees( - WORKTREE_ROOT, all=False, stale=False + git_mod.list_worktrees(WORKTREE_ROOT, all=False, stale=False), + dict( + [ + (x, worktree_ret[x]) + for x in WORKTREE_INFO + if not WORKTREE_INFO[x].get("stale", False) + ] ), - dict([(x, worktree_ret[x]) for x in WORKTREE_INFO - if not WORKTREE_INFO[x].get('stale', False)]) ) # Test stale=True. Exclude non-stale worktrees from return # data. self.assertEqual( - git_mod.list_worktrees( - WORKTREE_ROOT, all=False, stale=True + git_mod.list_worktrees(WORKTREE_ROOT, all=False, stale=True), + dict( + [ + (x, worktree_ret[x]) + for x in WORKTREE_INFO + if WORKTREE_INFO[x].get("stale", False) + ] ), - dict([(x, worktree_ret[x]) for x in WORKTREE_INFO - if WORKTREE_INFO[x].get('stale', False)]) ) def test__git_run_tmp_wrapper(self): - ''' + """ When an identity file is specified, make sure we don't attempt to remove a temp wrapper that wasn't created. Windows doesn't use temp wrappers, and *NIX won't unless no username was specified and the path is not executable. - ''' + """ file_remove_mock = Mock() mock_true = Mock(return_value=True) mock_false = Mock(return_value=False) - cmd_mock = MagicMock(return_value={ - 'retcode': 0, - 'stdout': '', - 'stderr': '', - }) - with patch.dict(git_mod.__salt__, {'file.file_exists': mock_true, - 'file.remove': file_remove_mock, - 'cmd.run_all': cmd_mock, - 'ssh.key_is_encrypted': mock_false}): + cmd_mock = MagicMock(return_value={"retcode": 0, "stdout": "", "stderr": ""}) + with patch.dict( + git_mod.__salt__, + { + "file.file_exists": mock_true, + "file.remove": file_remove_mock, + "cmd.run_all": cmd_mock, + "ssh.key_is_encrypted": mock_false, + }, + ): # Non-windows - with patch('salt.utils.platform.is_windows', mock_false), \ - patch.object(git_mod, '_path_is_executable_others', - mock_true): + with patch("salt.utils.platform.is_windows", mock_false), patch.object( + git_mod, "_path_is_executable_others", mock_true + ): # Command doesn't really matter here since we're mocking git_mod._git_run( - ['git', 'rev-parse', 'HEAD'], - cwd='/some/path', + ["git", "rev-parse", "HEAD"], + cwd="/some/path", user=None, - identity='/root/.ssh/id_rsa') + identity="/root/.ssh/id_rsa", + ) file_remove_mock.assert_not_called() file_remove_mock.reset_mock() - with patch('salt.utils.platform.is_windows', mock_true), \ - patch.object(git_mod, '_find_ssh_exe', - MagicMock(return_value=r'C:\Git\ssh.exe')): + with patch("salt.utils.platform.is_windows", mock_true), patch.object( + git_mod, "_find_ssh_exe", MagicMock(return_value=r"C:\Git\ssh.exe") + ): # Command doesn't really matter here since we're mocking git_mod._git_run( - ['git', 'rev-parse', 'HEAD'], - cwd=r'C:\some\path', + ["git", "rev-parse", "HEAD"], + cwd=r"C:\some\path", user=None, - identity=r'C:\ssh\id_rsa') + identity=r"C:\ssh\id_rsa", + ) file_remove_mock.assert_not_called() diff --git a/tests/unit/modules/test_glance.py b/tests/unit/modules/test_glance.py index f6216c63187..8d2226358b9 100644 --- a/tests/unit/modules/test_glance.py +++ b/tests/unit/modules/test_glance.py @@ -1,36 +1,41 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch -from tests.support.mixins import LoaderModuleMockMixin +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.modules.glance as glance import salt.modules.salt_version +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase class GlanceTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - return {glance: - {'__salt__': {'salt_version.less_than': - salt.modules.salt_version.less_than}}, + return { + glance: { + "__salt__": { + "salt_version.less_than": salt.modules.salt_version.less_than } + }, + } def test_image_list(self): - ''' + """ test salt.modles.glance - ''' - name = 'test' + """ + name = "test" image = MagicMock() image.name = name - attrs = {'images.list.return_value': [image], } + attrs = { + "images.list.return_value": [image], + } mock_auth = MagicMock(**attrs) - patch_auth = patch('salt.modules.glance._auth', return_value=mock_auth) + patch_auth = patch("salt.modules.glance._auth", return_value=mock_auth) with patch_auth: - ret = glance.image_list(id='test_id', name=name) - assert ret[0]['name'] == name + ret = glance.image_list(id="test_id", name=name) + assert ret[0]["name"] == name diff --git a/tests/unit/modules/test_glusterfs.py b/tests/unit/modules/test_glusterfs.py index 4baf4ed203c..1b97749b1d1 100644 --- a/tests/unit/modules/test_glusterfs.py +++ b/tests/unit/modules/test_glusterfs.py @@ -1,182 +1,226 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> :codeauthor: Joe Julian <me@joejulian.name> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.glusterfs as glusterfs from salt.exceptions import SaltInvocationError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class GlusterResults(object): - ''' This class holds the xml results from gluster cli transactions ''' + """ This class holds the xml results from gluster cli transactions """ class v34(object): - ''' This is for version 3.4 results ''' + """ This is for version 3.4 results """ class list_peers(object): - ''' results from "peer status" ''' + """ results from "peer status" """ class peer_probe(object): - fail_cant_connect = fail_bad_hostname = '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>', - '<cliOutput>', - ' <opRet>-1</opRet>', - ' <opErrno>107</opErrno>', - ' <opErrstr>Probe returned with unknown errno 107</opErrstr>', - '</cliOutput>', - '']) + fail_cant_connect = fail_bad_hostname = "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>', + "<cliOutput>", + " <opRet>-1</opRet>", + " <opErrno>107</opErrno>", + " <opErrstr>Probe returned with unknown errno 107</opErrstr>", + "</cliOutput>", + "", + ] + ) - success_self = '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>1</opErrno>', - ' <opErrstr>(null)</opErrstr>', - ' <output>success: on localhost not needed</output>', - '</cliOutput>', - '']) - success_other = '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>0</opErrno>', - ' <opErrstr>(null)</opErrstr>', - ' <output>success</output>', - '</cliOutput>', - '']) + success_self = "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>1</opErrno>", + " <opErrstr>(null)</opErrstr>", + " <output>success: on localhost not needed</output>", + "</cliOutput>", + "", + ] + ) + success_other = "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>0</opErrno>", + " <opErrstr>(null)</opErrstr>", + " <output>success</output>", + "</cliOutput>", + "", + ] + ) success_hostname_after_ip = success_other success_ip_after_hostname = success_other success_already_peer = { - 'ip': '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>2</opErrno>', - ' <opErrstr>(null)</opErrstr>', - ' <output>success: host 10.0.0.2 port 24007 already in peer list</output>', - '</cliOutput>', - '']), - 'hostname': '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>2</opErrno>', - ' <opErrstr>(null)</opErrstr>', - ' <output>success: host server2 port 24007 already in peer list</output>', - '</cliOutput>', - ''])} + "ip": "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>2</opErrno>", + " <opErrstr>(null)</opErrstr>", + " <output>success: host 10.0.0.2 port 24007 already in peer list</output>", + "</cliOutput>", + "", + ] + ), + "hostname": "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>2</opErrno>", + " <opErrstr>(null)</opErrstr>", + " <output>success: host server2 port 24007 already in peer list</output>", + "</cliOutput>", + "", + ] + ), + } success_reverse_already_peer = { - 'ip': '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>2</opErrno>', - ' <opErrstr>(null)</opErrstr>', - ' <output>success: host 10.0.0.1 port 24007 already in peer list</output>', - '</cliOutput>', - '']), - 'hostname': '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>2</opErrno>', - ' <opErrstr>(null)</opErrstr>', - ' <output>success: host server1 port 24007 already in peer list</output>', - '</cliOutput>', - ''])} + "ip": "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>2</opErrno>", + " <opErrstr>(null)</opErrstr>", + " <output>success: host 10.0.0.1 port 24007 already in peer list</output>", + "</cliOutput>", + "", + ] + ), + "hostname": "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>2</opErrno>", + " <opErrstr>(null)</opErrstr>", + " <output>success: host server1 port 24007 already in peer list</output>", + "</cliOutput>", + "", + ] + ), + } success_first_hostname_from_second_first_time = success_other success_first_hostname_from_second_second_time = success_reverse_already_peer[ - 'hostname'] - success_first_ip_from_second_first_time = success_reverse_already_peer[ - 'ip'] + "hostname" + ] + success_first_ip_from_second_first_time = success_reverse_already_peer["ip"] class v37(object): - class peer_probe(object): - fail_cant_connect = fail_bad_hostname = '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>', - '<cliOutput>', - ' <opRet>-1</opRet>', - ' <opErrno>107</opErrno>', - ' <opErrstr>Probe returned with Transport endpoint is not connected</opErrstr>', - '</cliOutput>', - '']) - success_self = '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>1</opErrno>', - ' <opErrstr/>', - ' <output>Probe on localhost not needed</output>', - '</cliOutput>', - '']) - success_other = '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>0</opErrno>', - ' <opErrstr/>', - ' <output/>', - '</cliOutput>', - '']) + fail_cant_connect = fail_bad_hostname = "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>', + "<cliOutput>", + " <opRet>-1</opRet>", + " <opErrno>107</opErrno>", + " <opErrstr>Probe returned with Transport endpoint is not connected</opErrstr>", + "</cliOutput>", + "", + ] + ) + success_self = "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>1</opErrno>", + " <opErrstr/>", + " <output>Probe on localhost not needed</output>", + "</cliOutput>", + "", + ] + ) + success_other = "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>0</opErrno>", + " <opErrstr/>", + " <output/>", + "</cliOutput>", + "", + ] + ) success_hostname_after_ip = success_other success_ip_after_hostname = success_other success_already_peer = { - 'ip': '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>2</opErrno>', - ' <opErrstr/>', - ' <output>Host 10.0.0.2 port 24007 already in peer list</output>', - '</cliOutput>', - '']), - 'hostname': '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>2</opErrno>', - ' <opErrstr/>', - ' <output>Host server2 port 24007 already in peer list</output>', - '</cliOutput>', - ''])} + "ip": "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>2</opErrno>", + " <opErrstr/>", + " <output>Host 10.0.0.2 port 24007 already in peer list</output>", + "</cliOutput>", + "", + ] + ), + "hostname": "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>2</opErrno>", + " <opErrstr/>", + " <output>Host server2 port 24007 already in peer list</output>", + "</cliOutput>", + "", + ] + ), + } success_reverse_already_peer = { - 'ip': '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>2</opErrno>', - ' <opErrstr/>', - ' <output>Host 10.0.0.1 port 24007 already in peer list</output>', - '</cliOutput>', - '']), - 'hostname': '\n'.join([ - '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' - ' <cliOutput>', - ' <opRet>0</opRet>', - ' <opErrno>2</opErrno>', - ' <opErrstr/>', - ' <output>Host server1 port 24007 already in peer list</output>', - '</cliOutput>', - ''])} + "ip": "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>2</opErrno>", + " <opErrstr/>", + " <output>Host 10.0.0.1 port 24007 already in peer list</output>", + "</cliOutput>", + "", + ] + ), + "hostname": "\n".join( + [ + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + " <cliOutput>", + " <opRet>0</opRet>", + " <opErrno>2</opErrno>", + " <opErrstr/>", + " <output>Host server1 port 24007 already in peer list</output>", + "</cliOutput>", + "", + ] + ), + } success_first_hostname_from_second_first_time = success_reverse_already_peer[ - 'hostname'] + "hostname" + ] success_first_ip_from_second_first_time = success_other success_first_ip_from_second_second_time = success_reverse_already_peer[ - 'ip'] + "ip" + ] xml_peer_present = """ @@ -433,9 +477,10 @@ xml_set_op_version_success = """ class GlusterfsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.glusterfs - ''' + """ + def setup_loader_modules(self): return {glusterfs: {}} @@ -444,351 +489,354 @@ class GlusterfsTestCase(TestCase, LoaderModuleMockMixin): # 'peer_status' function tests: 1 def test_peer_status(self): - ''' + """ Test gluster peer status - ''' + """ mock_run = MagicMock(return_value=xml_peer_present) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): self.assertDictEqual( glusterfs.peer_status(), - {'uuid1': { - 'hostnames': ['node02', 'node02.domain.dom', '10.0.0.2']}}) + {"uuid1": {"hostnames": ["node02", "node02.domain.dom", "10.0.0.2"]}}, + ) mock_run = MagicMock(return_value=xml_command_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): self.assertDictEqual(glusterfs.peer_status(), {}) # 'peer' function tests: 1 def test_peer(self): - ''' + """ Test if gluster peer call is successful. - ''' + """ mock_run = MagicMock() - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): mock_run.return_value = xml_peer_probe_already_member - self.assertTrue(glusterfs.peer('salt')) + self.assertTrue(glusterfs.peer("salt")) mock_run.return_value = xml_peer_probe_localhost - self.assertTrue(glusterfs.peer('salt')) + self.assertTrue(glusterfs.peer("salt")) mock_run.return_value = xml_peer_probe_fail_cant_connect - self.assertFalse(glusterfs.peer('salt')) + self.assertFalse(glusterfs.peer("salt")) # 'create_volume' function tests: 1 def test_create_volume(self): - ''' + """ Test if it creates a glusterfs volume. - ''' + """ mock_run = MagicMock(return_value=xml_command_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): self.assertRaises( - SaltInvocationError, - glusterfs.create_volume, - 'newvolume', - 'host1:brick') + SaltInvocationError, glusterfs.create_volume, "newvolume", "host1:brick" + ) self.assertRaises( - SaltInvocationError, - glusterfs.create_volume, - 'newvolume', - 'host1/brick') + SaltInvocationError, glusterfs.create_volume, "newvolume", "host1/brick" + ) self.assertFalse(mock_run.called) mock_start_volume = MagicMock(return_value=True) - with patch.object(glusterfs, 'start_volume', mock_start_volume): + with patch.object(glusterfs, "start_volume", mock_start_volume): # Create, do not start - self.assertTrue(glusterfs.create_volume('newvolume', - 'host1:/brick')) + self.assertTrue(glusterfs.create_volume("newvolume", "host1:/brick")) self.assertFalse(mock_start_volume.called) # Create and start - self.assertTrue(glusterfs.create_volume('newvolume', - 'host1:/brick', - start=True)) + self.assertTrue( + glusterfs.create_volume("newvolume", "host1:/brick", start=True) + ) self.assertTrue(mock_start_volume.called) mock_start_volume.return_value = False # Create and fail start - self.assertFalse(glusterfs.create_volume('newvolume', - 'host1:/brick', - start=True)) + self.assertFalse( + glusterfs.create_volume("newvolume", "host1:/brick", start=True) + ) mock_run.return_value = xml_command_fail - self.assertFalse(glusterfs.create_volume('newvolume', 'host1:/brick', - True, True, True, 'tcp', True)) + self.assertFalse( + glusterfs.create_volume( + "newvolume", "host1:/brick", True, True, True, "tcp", True + ) + ) # 'list_volumes' function tests: 1 def test_list_volumes(self): - ''' + """ Test if it list configured volumes - ''' + """ mock = MagicMock(return_value=xml_volume_absent) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock}): self.assertListEqual(glusterfs.list_volumes(), []) mock = MagicMock(return_value=xml_volume_present) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock}): - self.assertListEqual(glusterfs.list_volumes(), - ['Newvolume1', 'Newvolume2']) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock}): + self.assertListEqual(glusterfs.list_volumes(), ["Newvolume1", "Newvolume2"]) # 'status' function tests: 1 def test_status(self): - ''' + """ Test if it check the status of a gluster volume. - ''' + """ mock_run = MagicMock(return_value=xml_command_fail) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertIsNone(glusterfs.status('myvol1')) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertIsNone(glusterfs.status("myvol1")) - res = {'bricks': { - 'node01:/tmp/foo': { - 'host': 'node01', - 'hostname': 'node01', - 'online': True, - 'path': '/tmp/foo', - 'peerid': '830700d7-0684-497c-a12c-c02e365fb90b', - 'pid': '2470', - 'port': '49155', - 'ports': { - 'rdma': 'N/A', - 'tcp': '49155'}, - 'status': '1'}}, - 'healers': {}, - 'nfs': { - 'node01': { - 'host': 'NFS Server', - 'hostname': 'NFS Server', - 'online': False, - 'path': 'localhost', - 'peerid': '830700d7-0684-497c-a12c-c02e365fb90b', - 'pid': '-1', - 'port': 'N/A', - 'ports': { - 'rdma': 'N/A', - 'tcp': 'N/A'}, - 'status': '0'}}} + res = { + "bricks": { + "node01:/tmp/foo": { + "host": "node01", + "hostname": "node01", + "online": True, + "path": "/tmp/foo", + "peerid": "830700d7-0684-497c-a12c-c02e365fb90b", + "pid": "2470", + "port": "49155", + "ports": {"rdma": "N/A", "tcp": "49155"}, + "status": "1", + } + }, + "healers": {}, + "nfs": { + "node01": { + "host": "NFS Server", + "hostname": "NFS Server", + "online": False, + "path": "localhost", + "peerid": "830700d7-0684-497c-a12c-c02e365fb90b", + "pid": "-1", + "port": "N/A", + "ports": {"rdma": "N/A", "tcp": "N/A"}, + "status": "0", + } + }, + } mock = MagicMock(return_value=xml_volume_status) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock}): - self.assertDictEqual(glusterfs.status('myvol1'), res) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock}): + self.assertDictEqual(glusterfs.status("myvol1"), res) # 'start_volume' function tests: 1 def test_volume_info(self): - ''' + """ Test if it returns the volume info. - ''' - res = {'myvol1': { - 'brickCount': '1', - 'bricks': { - 'brick1': { - 'hostUuid': '830700d7-0684-497c-a12c-c02e365fb90b', - 'path': 'node01:/tmp/foo', - 'uuid': '830700d7-0684-497c-a12c-c02e365fb90b'}}, - 'disperseCount': '0', - 'distCount': '1', - 'id': 'f03c2180-cf55-4f77-ae0b-3650f57c82a1', - 'name': 'myvol1', - 'optCount': '1', - 'options': { - 'performance.readdir-ahead': 'on'}, - 'redundancyCount': '0', - 'replicaCount': '1', - 'status': '1', - 'statusStr': 'Started', - 'stripeCount': '1', - 'transport': '0', - 'type': '0', - 'typeStr': 'Distribute'}} + """ + res = { + "myvol1": { + "brickCount": "1", + "bricks": { + "brick1": { + "hostUuid": "830700d7-0684-497c-a12c-c02e365fb90b", + "path": "node01:/tmp/foo", + "uuid": "830700d7-0684-497c-a12c-c02e365fb90b", + } + }, + "disperseCount": "0", + "distCount": "1", + "id": "f03c2180-cf55-4f77-ae0b-3650f57c82a1", + "name": "myvol1", + "optCount": "1", + "options": {"performance.readdir-ahead": "on"}, + "redundancyCount": "0", + "replicaCount": "1", + "status": "1", + "statusStr": "Started", + "stripeCount": "1", + "transport": "0", + "type": "0", + "typeStr": "Distribute", + } + } mock = MagicMock(return_value=xml_volume_info_running) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock}): - self.assertDictEqual(glusterfs.info('myvol1'), res) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock}): + self.assertDictEqual(glusterfs.info("myvol1"), res) def test_start_volume(self): - ''' + """ Test if it start a gluster volume. - ''' + """ # Stopped volume - mock_info = MagicMock(return_value={'Newvolume1': {'status': '0'}}) - with patch.object(glusterfs, 'info', mock_info): + mock_info = MagicMock(return_value={"Newvolume1": {"status": "0"}}) + with patch.object(glusterfs, "info", mock_info): mock_run = MagicMock(return_value=xml_command_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertEqual(glusterfs.start_volume('Newvolume1'), True) - self.assertEqual(glusterfs.start_volume('nonExisting'), False) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertEqual(glusterfs.start_volume("Newvolume1"), True) + self.assertEqual(glusterfs.start_volume("nonExisting"), False) mock_run = MagicMock(return_value=xml_command_fail) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertEqual(glusterfs.start_volume('Newvolume1'), False) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertEqual(glusterfs.start_volume("Newvolume1"), False) # Started volume - mock_info = MagicMock(return_value={'Newvolume1': {'status': '1'}}) - with patch.object(glusterfs, 'info', mock_info): + mock_info = MagicMock(return_value={"Newvolume1": {"status": "1"}}) + with patch.object(glusterfs, "info", mock_info): mock_run = MagicMock(return_value=xml_command_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertEqual( - glusterfs.start_volume('Newvolume1', force=True), - True - ) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertEqual(glusterfs.start_volume("Newvolume1", force=True), True) mock_run = MagicMock(return_value=xml_command_fail) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): # cmd.run should not be called for already running volume: - self.assertEqual(glusterfs.start_volume('Newvolume1'), True) + self.assertEqual(glusterfs.start_volume("Newvolume1"), True) # except when forcing: self.assertEqual( - glusterfs.start_volume('Newvolume1', force=True), - False + glusterfs.start_volume("Newvolume1", force=True), False ) # 'stop_volume' function tests: 1 def test_stop_volume(self): - ''' + """ Test if it stop a gluster volume. - ''' + """ # Stopped volume - mock_info = MagicMock(return_value={'Newvolume1': {'status': '0'}}) - with patch.object(glusterfs, 'info', mock_info): + mock_info = MagicMock(return_value={"Newvolume1": {"status": "0"}}) + with patch.object(glusterfs, "info", mock_info): mock_run = MagicMock(return_value=xml_command_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertEqual(glusterfs.stop_volume('Newvolume1'), True) - self.assertEqual(glusterfs.stop_volume('nonExisting'), False) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertEqual(glusterfs.stop_volume("Newvolume1"), True) + self.assertEqual(glusterfs.stop_volume("nonExisting"), False) mock_run = MagicMock(return_value=xml_command_fail) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): # cmd.run should not be called for already stopped volume: - self.assertEqual(glusterfs.stop_volume('Newvolume1'), True) + self.assertEqual(glusterfs.stop_volume("Newvolume1"), True) # Started volume - mock_info = MagicMock(return_value={'Newvolume1': {'status': '1'}}) - with patch.object(glusterfs, 'info', mock_info): + mock_info = MagicMock(return_value={"Newvolume1": {"status": "1"}}) + with patch.object(glusterfs, "info", mock_info): mock_run = MagicMock(return_value=xml_command_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertEqual(glusterfs.stop_volume('Newvolume1'), True) - self.assertEqual(glusterfs.stop_volume('nonExisting'), False) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertEqual(glusterfs.stop_volume("Newvolume1"), True) + self.assertEqual(glusterfs.stop_volume("nonExisting"), False) mock_run = MagicMock(return_value=xml_command_fail) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertEqual(glusterfs.stop_volume('Newvolume1'), False) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertEqual(glusterfs.stop_volume("Newvolume1"), False) # 'delete_volume' function tests: 1 def test_delete_volume(self): - ''' + """ Test if it deletes a gluster volume. - ''' - mock_info = MagicMock(return_value={'Newvolume1': {'status': '1'}}) - with patch.object(glusterfs, 'info', mock_info): + """ + mock_info = MagicMock(return_value={"Newvolume1": {"status": "1"}}) + with patch.object(glusterfs, "info", mock_info): # volume doesn't exist - self.assertFalse(glusterfs.delete_volume('Newvolume3')) + self.assertFalse(glusterfs.delete_volume("Newvolume3")) mock_stop_volume = MagicMock(return_value=True) mock_run = MagicMock(return_value=xml_command_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - with patch.object(glusterfs, 'stop_volume', mock_stop_volume): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + with patch.object(glusterfs, "stop_volume", mock_stop_volume): # volume exists, should not be stopped, and is started - self.assertFalse(glusterfs.delete_volume('Newvolume1', - False)) + self.assertFalse(glusterfs.delete_volume("Newvolume1", False)) self.assertFalse(mock_run.called) self.assertFalse(mock_stop_volume.called) # volume exists, should be stopped, and is started - self.assertTrue(glusterfs.delete_volume('Newvolume1')) + self.assertTrue(glusterfs.delete_volume("Newvolume1")) self.assertTrue(mock_run.called) self.assertTrue(mock_stop_volume.called) # volume exists and isn't started - mock_info = MagicMock(return_value={'Newvolume1': {'status': '2'}}) - with patch.object(glusterfs, 'info', mock_info): + mock_info = MagicMock(return_value={"Newvolume1": {"status": "2"}}) + with patch.object(glusterfs, "info", mock_info): mock_run = MagicMock(return_value=xml_command_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertTrue(glusterfs.delete_volume('Newvolume1')) + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertTrue(glusterfs.delete_volume("Newvolume1")) mock_run.return_value = xml_command_fail - self.assertFalse(glusterfs.delete_volume('Newvolume1')) + self.assertFalse(glusterfs.delete_volume("Newvolume1")) # 'add_volume_bricks' function tests: 1 def test_add_volume_bricks(self): - ''' + """ Test if it add brick(s) to an existing volume - ''' - mock_info = MagicMock(return_value={ - 'Newvolume1': { - 'status': '1', - 'bricks': { - 'brick1': {'path': 'host:/path1'}, - 'brick2': {'path': 'host:/path2'} + """ + mock_info = MagicMock( + return_value={ + "Newvolume1": { + "status": "1", + "bricks": { + "brick1": {"path": "host:/path1"}, + "brick2": {"path": "host:/path2"}, + }, } } - }) - with patch.object(glusterfs, 'info', mock_info): + ) + with patch.object(glusterfs, "info", mock_info): mock_run = MagicMock(return_value=xml_command_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): # Volume does not exist - self.assertFalse(glusterfs.add_volume_bricks('nonExisting', - ['bricks'])) + self.assertFalse(glusterfs.add_volume_bricks("nonExisting", ["bricks"])) # Brick already exists - self.assertTrue(glusterfs.add_volume_bricks('Newvolume1', - ['host:/path2'])) + self.assertTrue( + glusterfs.add_volume_bricks("Newvolume1", ["host:/path2"]) + ) # Already existing brick as a string - self.assertTrue(glusterfs.add_volume_bricks('Newvolume1', - 'host:/path2')) + self.assertTrue( + glusterfs.add_volume_bricks("Newvolume1", "host:/path2") + ) self.assertFalse(mock_run.called) # A new brick: - self.assertTrue(glusterfs.add_volume_bricks('Newvolume1', - ['host:/new1'])) + self.assertTrue( + glusterfs.add_volume_bricks("Newvolume1", ["host:/new1"]) + ) self.assertTrue(mock_run.called) # Gluster call fails mock_run.return_value = xml_command_fail - self.assertFalse(glusterfs.add_volume_bricks('Newvolume1', - ['new:/path'])) + self.assertFalse( + glusterfs.add_volume_bricks("Newvolume1", ["new:/path"]) + ) # 'get_op_version' function tests: 1 def test_get_op_version(self): - ''' + """ Test retrieving the glusterfs op-version - ''' + """ # Test with xml output structure from v3.7 mock_run = MagicMock(return_value=xml_op_version_37) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertEqual(glusterfs.get_op_version('test'), '30707') + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertEqual(glusterfs.get_op_version("test"), "30707") # Test with xml output structure from v3.12 mock_run = MagicMock(return_value=xml_op_version_312) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_run}): - self.assertEqual(glusterfs.get_op_version('test'), '30707') + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_run}): + self.assertEqual(glusterfs.get_op_version("test"), "30707") # 'get_max_op_version' function tests: 1 def test_get_max_op_version(self): - ''' + """ Test retrieving the glusterfs max-op-version. - ''' + """ mock_xml = MagicMock(return_value=xml_max_op_version) - mock_version = MagicMock(return_value='glusterfs 3.9.1') + mock_version = MagicMock(return_value="glusterfs 3.9.1") - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_version}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_version}): self.assertFalse(glusterfs.get_max_op_version()[0]) - with patch.object(glusterfs, '_get_version', return_value=(3, 12, 0)): - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_xml}): - self.assertEqual(glusterfs.get_max_op_version(), '31200') + with patch.object(glusterfs, "_get_version", return_value=(3, 12, 0)): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_xml}): + self.assertEqual(glusterfs.get_max_op_version(), "31200") # 'set_op_version' function tests: 1 def test_set_op_version(self): - ''' + """ Test setting the glusterfs op-version - ''' + """ mock_failure = MagicMock(return_value=xml_set_op_version_failure) mock_success = MagicMock(return_value=xml_set_op_version_success) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_failure}): + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_failure}): self.assertFalse(glusterfs.set_op_version(30707)[0]) - with patch.dict(glusterfs.__salt__, {'cmd.run': mock_success}): - self.assertEqual(glusterfs.set_op_version(31200), 'Set volume successful') + with patch.dict(glusterfs.__salt__, {"cmd.run": mock_success}): + self.assertEqual(glusterfs.set_op_version(31200), "Set volume successful") diff --git a/tests/unit/modules/test_gnomedesktop.py b/tests/unit/modules/test_gnomedesktop.py index 90ee6ec5be3..8ccebcef32b 100644 --- a/tests/unit/modules/test_gnomedesktop.py +++ b/tests/unit/modules/test_gnomedesktop.py @@ -1,116 +1,115 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, -) - # Import Salt Libs import salt.modules.gnomedesktop as gnomedesktop +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class GnomedesktopTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.gnomedesktop - ''' + """ + def setup_loader_modules(self): return {gnomedesktop: {}} def test_ping(self): - ''' + """ Test for A test to ensure the GNOME module is loaded - ''' + """ self.assertTrue(gnomedesktop.ping()) def test_getidledelay(self): - ''' + """ Test for Return the current idle delay setting in seconds - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - with patch.object(gsettings_mock, '_get', return_value=True): + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + with patch.object(gsettings_mock, "_get", return_value=True): self.assertTrue(gnomedesktop.getIdleDelay()) def test_setidledelay(self): - ''' + """ Test for Set the current idle delay setting in seconds - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - with patch.object(gsettings_mock, '_set', return_value=True): + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + with patch.object(gsettings_mock, "_set", return_value=True): self.assertTrue(gnomedesktop.setIdleDelay(5)) def test_getclockformat(self): - ''' + """ Test for Return the current clock format, either 12h or 24h format. - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - with patch.object(gsettings_mock, '_get', return_value=True): + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + with patch.object(gsettings_mock, "_get", return_value=True): self.assertTrue(gnomedesktop.getClockFormat()) def test_setclockformat(self): - ''' + """ Test for Set the clock format, either 12h or 24h format.. - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - with patch.object(gsettings_mock, '_set', return_value=True): - self.assertTrue(gnomedesktop.setClockFormat('12h')) + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + with patch.object(gsettings_mock, "_set", return_value=True): + self.assertTrue(gnomedesktop.setClockFormat("12h")) - self.assertFalse(gnomedesktop.setClockFormat('a')) + self.assertFalse(gnomedesktop.setClockFormat("a")) def test_getclockshowdate(self): - ''' + """ Test for Return the current setting, if the date is shown in the clock - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - with patch.object(gsettings_mock, '_get', return_value=True): + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + with patch.object(gsettings_mock, "_get", return_value=True): self.assertTrue(gnomedesktop.getClockShowDate()) def test_setclockshowdate(self): - ''' + """ Test for Set whether the date is visible in the clock - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - self.assertFalse(gnomedesktop.setClockShowDate('kvalue')) + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + self.assertFalse(gnomedesktop.setClockShowDate("kvalue")) - with patch.object(gsettings_mock, '_get', return_value=True): + with patch.object(gsettings_mock, "_get", return_value=True): self.assertTrue(gnomedesktop.setClockShowDate(True)) def test_getidleactivation(self): - ''' + """ Test for Get whether the idle activation is enabled - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - with patch.object(gsettings_mock, '_get', return_value=True): + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + with patch.object(gsettings_mock, "_get", return_value=True): self.assertTrue(gnomedesktop.getIdleActivation()) def test_setidleactivation(self): - ''' + """ Test for Set whether the idle activation is enabled - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - self.assertFalse(gnomedesktop.setIdleActivation('kvalue')) + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + self.assertFalse(gnomedesktop.setIdleActivation("kvalue")) - with patch.object(gsettings_mock, '_set', return_value=True): + with patch.object(gsettings_mock, "_set", return_value=True): self.assertTrue(gnomedesktop.setIdleActivation(True)) def test_get(self): - ''' + """ Test for Get key in a particular GNOME schema - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - with patch.object(gsettings_mock, '_get', return_value=True): + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + with patch.object(gsettings_mock, "_get", return_value=True): self.assertTrue(gnomedesktop.get()) def test_set_(self): - ''' + """ Test for Set key in a particular GNOME schema. - ''' - with patch('salt.modules.gnomedesktop._GSettings') as gsettings_mock: - with patch.object(gsettings_mock, '_get', return_value=True): + """ + with patch("salt.modules.gnomedesktop._GSettings") as gsettings_mock: + with patch.object(gsettings_mock, "_get", return_value=True): self.assertTrue(gnomedesktop.set_()) diff --git a/tests/unit/modules/test_google_chat.py b/tests/unit/modules/test_google_chat.py index a3f88c471d2..20370834706 100644 --- a/tests/unit/modules/test_google_chat.py +++ b/tests/unit/modules/test_google_chat.py @@ -1,51 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" Test the Google Chat Execution module. -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch - # Import Salt Libs import salt.modules.google_chat as gchat +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + def mocked_http_query(url, method, **kwargs): # pylint: disable=unused-argument - ''' + """ Mocked data for test_send_message_success - ''' - return {'status': 200, - 'dict': None} + """ + return {"status": 200, "dict": None} def mocked_http_query_failure(url, method, **kwargs): # pylint: disable=unused-argument - ''' + """ Mocked data for test_send_message_failure - ''' - return {'status': 522, - 'dict': None} + """ + return {"status": 522, "dict": None} class TestModulesCfutils(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.google_chat - ''' + """ + def setup_loader_modules(self): return {gchat: {}} def test_send_message_success(self): - ''' + """ Testing a successful message - ''' - with patch.dict(gchat.__utils__, {'http.query': mocked_http_query}): # pylint: disable=no-member - self.assertTrue(gchat.send_message('https://example.com', 'Yupiii')) + """ + with patch.dict( + gchat.__utils__, {"http.query": mocked_http_query} + ): # pylint: disable=no-member + self.assertTrue(gchat.send_message("https://example.com", "Yupiii")) def test_send_message_failure(self): - ''' + """ Testing a failed message - ''' - with patch.dict(gchat.__utils__, {'http.query': mocked_http_query_failure}): # pylint: disable=no-member - self.assertFalse(gchat.send_message('https://example.com', 'Yupiii')) + """ + with patch.dict( + gchat.__utils__, {"http.query": mocked_http_query_failure} + ): # pylint: disable=no-member + self.assertFalse(gchat.send_message("https://example.com", "Yupiii")) diff --git a/tests/unit/modules/test_gpg.py b/tests/unit/modules/test_gpg.py index dddc6e213c1..fbc55fb7849 100644 --- a/tests/unit/modules/test_gpg.py +++ b/tests/unit/modules/test_gpg.py @@ -1,31 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Gareth J. Greenaway <gareth@saltstack.com>` :codeauthor: :email:`David Murphy <dmurphy@saltstack.com>` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime import os import shutil -import datetime import time -# Import Salt Testing Libs -from tests.support.helpers import destructiveTest -from tests.support.unit import TestCase, skipIf -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.runtests import RUNTIME_VARS -from tests.support.mock import MagicMock, patch +import salt.modules.gpg as gpg +import salt.utils.files # Import Salt libs import salt.utils.platform -import salt.utils.files -import salt.modules.gpg as gpg +# Import Salt Testing Libs +from tests.support.helpers import destructiveTest +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, skipIf -GPG_TEST_KEY_PASSPHRASE = 'testkeypassphrase' -GPG_TEST_KEY_ID = '7416F045' +GPG_TEST_KEY_PASSPHRASE = "testkeypassphrase" +GPG_TEST_KEY_ID = "7416F045" GPG_TEST_PUB_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK----- mQGNBFz1dx4BDACph7J5nuWE+zb9rZqTaL8akAnPAli2j6Qtk7BTDzTM9Kq80U2P @@ -156,259 +157,334 @@ OZV2Hg+93dg3Wi6g/JW4OuTKWKuHRqpRB1J4i4lO try: import gnupg # pylint: disable=import-error,unused-import + HAS_GPG = True except ImportError: HAS_GPG = False @destructiveTest -@skipIf(not salt.utils.platform.is_linux(), 'These tests can only be run on linux') +@skipIf(not salt.utils.platform.is_linux(), "These tests can only be run on linux") class GpgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the gpg module - ''' - def setup_loader_modules(self): - return {gpg: {'__salt__': {}}} + """ - @skipIf(not HAS_GPG, 'GPG Module Unavailable') + def setup_loader_modules(self): + return {gpg: {"__salt__": {}}} + + @skipIf(not HAS_GPG, "GPG Module Unavailable") def setUp(self): super(GpgTestCase, self).setUp() - self.gpghome = os.path.join(RUNTIME_VARS.TMP, 'gpghome') + self.gpghome = os.path.join(RUNTIME_VARS.TMP, "gpghome") if not os.path.isdir(self.gpghome): # left behind... Don't fail because of this! os.makedirs(self.gpghome) - self.gpgfile_pub = os.path.join(RUNTIME_VARS.TMP, 'gpgfile.pub') - with salt.utils.files.fopen(self.gpgfile_pub, 'wb') as fp: + self.gpgfile_pub = os.path.join(RUNTIME_VARS.TMP, "gpgfile.pub") + with salt.utils.files.fopen(self.gpgfile_pub, "wb") as fp: fp.write(salt.utils.stringutils.to_bytes(GPG_TEST_PUB_KEY)) - self.gpgfile_priv = os.path.join(RUNTIME_VARS.TMP, 'gpgfile.priv') - with salt.utils.files.fopen(self.gpgfile_priv, 'wb') as fp: + self.gpgfile_priv = os.path.join(RUNTIME_VARS.TMP, "gpgfile.priv") + with salt.utils.files.fopen(self.gpgfile_priv, "wb") as fp: fp.write(salt.utils.stringutils.to_bytes(GPG_TEST_PRIV_KEY)) - self.user = 'salt' + self.user = "salt" - @skipIf(not HAS_GPG, 'GPG Module Unavailable') + @skipIf(not HAS_GPG, "GPG Module Unavailable") def tearDown(self): if os.path.isfile(self.gpgfile_pub): os.remove(self.gpgfile_pub) shutil.rmtree(self.gpghome, ignore_errors=True) super(GpgTestCase, self).tearDown() - @skipIf(not HAS_GPG, 'GPG Module Unavailable') + @skipIf(not HAS_GPG, "GPG Module Unavailable") def test_list_keys(self): - ''' + """ Test gpg.list_keys - ''' + """ - _user_mock = {u'shell': u'/bin/bash', - u'workphone': u'', - u'uid': 0, - u'passwd': u'x', - u'roomnumber': u'', - u'gid': 0, - u'groups': [ - u'root' - ], - u'home': u'/root', - u'fullname': u'root', - u'homephone': u'', - u'name': u'root'} + _user_mock = { + "shell": "/bin/bash", + "workphone": "", + "uid": 0, + "passwd": "x", + "roomnumber": "", + "gid": 0, + "groups": ["root"], + "home": "/root", + "fullname": "root", + "homephone": "", + "name": "root", + } - _list_result = [{u'dummy': u'', - u'keyid': u'xxxxxxxxxxxxxxxx', - u'expires': u'2011188692', - u'sigs': [], - u'subkeys': [[u'xxxxxxxxxxxxxxxx', u'e', u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx']], - u'length': u'4096', - u'ownertrust': u'-', - u'sig': u'', - u'algo': u'1', - u'fingerprint': u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - u'date': u'1506612692', - u'trust': u'-', - u'type': u'pub', - u'uids': [u'GPG Person <person@example.com>']}] + _list_result = [ + { + "dummy": "", + "keyid": "xxxxxxxxxxxxxxxx", + "expires": "2011188692", + "sigs": [], + "subkeys": [ + [ + "xxxxxxxxxxxxxxxx", + "e", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + ] + ], + "length": "4096", + "ownertrust": "-", + "sig": "", + "algo": "1", + "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "date": "1506612692", + "trust": "-", + "type": "pub", + "uids": ["GPG Person <person@example.com>"], + } + ] - _expected_result = [{u'keyid': u'xxxxxxxxxxxxxxxx', - u'uids': [u'GPG Person <person@example.com>'], - u'created': '2017-09-28', - u'expires': '2033-09-24', - u'keyLength': u'4096', - u'ownerTrust': u'Unknown', - u'fingerprint': u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - u'trust': u'Unknown'}] + _expected_result = [ + { + "keyid": "xxxxxxxxxxxxxxxx", + "uids": ["GPG Person <person@example.com>"], + "created": "2017-09-28", + "expires": "2033-09-24", + "keyLength": "4096", + "ownerTrust": "Unknown", + "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "trust": "Unknown", + } + ] - mock_opt = MagicMock(return_value='root') - with patch.dict(gpg.__salt__, {'user.info': MagicMock(return_value=_user_mock)}): - with patch.dict(gpg.__salt__, {'config.option': mock_opt}): - with patch.object(gpg, '_list_keys', return_value=_list_result): + mock_opt = MagicMock(return_value="root") + with patch.dict( + gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)} + ): + with patch.dict(gpg.__salt__, {"config.option": mock_opt}): + with patch.object(gpg, "_list_keys", return_value=_list_result): self.assertEqual(gpg.list_keys(), _expected_result) - @skipIf(not HAS_GPG, 'GPG Module Unavailable') + @skipIf(not HAS_GPG, "GPG Module Unavailable") def test_get_key(self): - ''' + """ Test gpg.get_key - ''' + """ - _user_mock = {u'shell': u'/bin/bash', - u'workphone': u'', - u'uid': 0, - u'passwd': u'x', - u'roomnumber': u'', - u'gid': 0, - u'groups': [ - u'root' - ], - u'home': u'/root', - u'fullname': u'root', - u'homephone': u'', - u'name': u'root'} + _user_mock = { + "shell": "/bin/bash", + "workphone": "", + "uid": 0, + "passwd": "x", + "roomnumber": "", + "gid": 0, + "groups": ["root"], + "home": "/root", + "fullname": "root", + "homephone": "", + "name": "root", + } - _list_result = [{u'dummy': u'', - u'keyid': u'xxxxxxxxxxxxxxxx', - u'expires': u'2011188692', - u'sigs': [], - u'subkeys': [[u'xxxxxxxxxxxxxxxx', u'e', u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx']], - u'length': u'4096', - u'ownertrust': u'-', - u'sig': u'', - u'algo': u'1', - u'fingerprint': u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - u'date': u'1506612692', - u'trust': u'-', - u'type': u'pub', - u'uids': [u'GPG Person <person@example.com>']}] + _list_result = [ + { + "dummy": "", + "keyid": "xxxxxxxxxxxxxxxx", + "expires": "2011188692", + "sigs": [], + "subkeys": [ + [ + "xxxxxxxxxxxxxxxx", + "e", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + ] + ], + "length": "4096", + "ownertrust": "-", + "sig": "", + "algo": "1", + "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "date": "1506612692", + "trust": "-", + "type": "pub", + "uids": ["GPG Person <person@example.com>"], + } + ] - _expected_result = {u'fingerprint': u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - u'keyid': u'xxxxxxxxxxxxxxxx', - u'uids': [u'GPG Person <person@example.com>'], - u'created': u'2017-09-28', - u'trust': u'Unknown', - u'ownerTrust': u'Unknown', - u'expires': u'2033-09-24', - u'keyLength': u'4096'} + _expected_result = { + "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "keyid": "xxxxxxxxxxxxxxxx", + "uids": ["GPG Person <person@example.com>"], + "created": "2017-09-28", + "trust": "Unknown", + "ownerTrust": "Unknown", + "expires": "2033-09-24", + "keyLength": "4096", + } - mock_opt = MagicMock(return_value='root') - with patch.dict(gpg.__salt__, {'user.info': MagicMock(return_value=_user_mock)}): - with patch.dict(gpg.__salt__, {'config.option': mock_opt}): - with patch.object(gpg, '_list_keys', return_value=_list_result): - ret = gpg.get_key('xxxxxxxxxxxxxxxx') + mock_opt = MagicMock(return_value="root") + with patch.dict( + gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)} + ): + with patch.dict(gpg.__salt__, {"config.option": mock_opt}): + with patch.object(gpg, "_list_keys", return_value=_list_result): + ret = gpg.get_key("xxxxxxxxxxxxxxxx") self.assertEqual(ret, _expected_result) @destructiveTest # Need to run as root!? - @skipIf(not HAS_GPG, 'GPG Module Unavailable') + @skipIf(not HAS_GPG, "GPG Module Unavailable") def test_delete_key(self): - ''' + """ Test gpg.delete_key - ''' + """ - _user_mock = {u'shell': u'/bin/bash', - u'workphone': u'', - u'uid': 0, - u'passwd': u'x', - u'roomnumber': u'', - u'gid': 0, - u'groups': [ - u'root' - ], - u'home': self.gpghome, - u'fullname': u'root', - u'homephone': u'', - u'name': u'root'} + _user_mock = { + "shell": "/bin/bash", + "workphone": "", + "uid": 0, + "passwd": "x", + "roomnumber": "", + "gid": 0, + "groups": ["root"], + "home": self.gpghome, + "fullname": "root", + "homephone": "", + "name": "root", + } - _list_result = [{'dummy': u'', - 'keyid': u'xxxxxxxxxxxxxxxx', - 'expires': u'2011188692', - 'sigs': [], - 'subkeys': [[u'xxxxxxxxxxxxxxxx', u'e', u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx']], - 'length': u'4096', - 'ownertrust': u'-', - 'sig': u'', - 'algo': u'1', - 'fingerprint': u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - 'date': u'1506612692', - 'trust': u'-', - 'type': u'pub', - 'uids': [u'GPG Person <person@example.com>']}] + _list_result = [ + { + "dummy": "", + "keyid": "xxxxxxxxxxxxxxxx", + "expires": "2011188692", + "sigs": [], + "subkeys": [ + [ + "xxxxxxxxxxxxxxxx", + "e", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + ] + ], + "length": "4096", + "ownertrust": "-", + "sig": "", + "algo": "1", + "fingerprint": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "date": "1506612692", + "trust": "-", + "type": "pub", + "uids": ["GPG Person <person@example.com>"], + } + ] - _expected_result = {u'res': True, - u'message': u'Secret key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted\nPublic key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted'} + _expected_result = { + "res": True, + "message": "Secret key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted\nPublic key for xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx deleted", + } - mock_opt = MagicMock(return_value='root') - with patch.dict(gpg.__salt__, {'user.info': MagicMock(return_value=_user_mock)}): - with patch.dict(gpg.__salt__, {'config.option': mock_opt}): - with patch.object(gpg, '_list_keys', return_value=_list_result): - with patch('salt.modules.gpg.gnupg.GPG.delete_keys', MagicMock(return_value='ok')): - ret = gpg.delete_key('xxxxxxxxxxxxxxxx', delete_secret=True) + mock_opt = MagicMock(return_value="root") + with patch.dict( + gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)} + ): + with patch.dict(gpg.__salt__, {"config.option": mock_opt}): + with patch.object(gpg, "_list_keys", return_value=_list_result): + with patch( + "salt.modules.gpg.gnupg.GPG.delete_keys", + MagicMock(return_value="ok"), + ): + ret = gpg.delete_key("xxxxxxxxxxxxxxxx", delete_secret=True) self.assertEqual(ret, _expected_result) - @skipIf(not HAS_GPG, 'GPG Module Unavailable') + @skipIf(not HAS_GPG, "GPG Module Unavailable") def test_search_keys(self): - ''' + """ Test gpg.search_keys - ''' + """ - _user_mock = {'shell': '/bin/bash', - 'workphone': '', - 'uid': 0, - 'passwd': 'x', - 'roomnumber': '', - 'gid': 0, - 'groups': [ - 'root' - ], - 'home': self.gpghome, - 'fullname': 'root', - 'homephone': '', - 'name': 'root'} + _user_mock = { + "shell": "/bin/bash", + "workphone": "", + "uid": 0, + "passwd": "x", + "roomnumber": "", + "gid": 0, + "groups": ["root"], + "home": self.gpghome, + "fullname": "root", + "homephone": "", + "name": "root", + } - _search_result = [{u'keyid': u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', - u'uids': [u'GPG Person <person@example.com>'], - u'expires': u'', - u'sigs': [], - u'length': u'1024', - u'algo': u'17', - u'date': int(time.mktime(datetime.datetime(2004, 11, 13).timetuple())), - u'type': u'pub'}] + _search_result = [ + { + "keyid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "uids": ["GPG Person <person@example.com>"], + "expires": "", + "sigs": [], + "length": "1024", + "algo": "17", + "date": int(time.mktime(datetime.datetime(2004, 11, 13).timetuple())), + "type": "pub", + } + ] - _expected_result = [{u'uids': [u'GPG Person <person@example.com>'], - 'created': '2004-11-13', - u'keyLength': u'1024', - u'keyid': u'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}] + _expected_result = [ + { + "uids": ["GPG Person <person@example.com>"], + "created": "2004-11-13", + "keyLength": "1024", + "keyid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + } + ] - mock_opt = MagicMock(return_value='root') - with patch.dict(gpg.__salt__, {'user.info': MagicMock(return_value=_user_mock)}): - with patch.dict(gpg.__salt__, {'config.option': mock_opt}): - with patch.object(gpg, '_search_keys', return_value=_search_result): - ret = gpg.search_keys('person@example.com') + mock_opt = MagicMock(return_value="root") + with patch.dict( + gpg.__salt__, {"user.info": MagicMock(return_value=_user_mock)} + ): + with patch.dict(gpg.__salt__, {"config.option": mock_opt}): + with patch.object(gpg, "_search_keys", return_value=_search_result): + ret = gpg.search_keys("person@example.com") self.assertEqual(ret, _expected_result) - @skipIf(not HAS_GPG, 'GPG Module Unavailable') + @skipIf(not HAS_GPG, "GPG Module Unavailable") def test_gpg_import_pub_key(self): - config_user = MagicMock(return_value='salt') - user_info = MagicMock(return_value={'name': 'salt', 'home': self.gpghome, 'uid': 1000}) - with patch.dict(gpg.__salt__, {'config.option': config_user}): - with patch.dict(gpg.__salt__, {'user.info': user_info}): - ret = gpg.import_key(None, self.gpgfile_pub, 'salt', self.gpghome) - self.assertEqual(ret['res'], True) + config_user = MagicMock(return_value="salt") + user_info = MagicMock( + return_value={"name": "salt", "home": self.gpghome, "uid": 1000} + ) + with patch.dict(gpg.__salt__, {"config.option": config_user}): + with patch.dict(gpg.__salt__, {"user.info": user_info}): + ret = gpg.import_key(None, self.gpgfile_pub, "salt", self.gpghome) + self.assertEqual(ret["res"], True) - @skipIf(not HAS_GPG, 'GPG Module Unavailable') + @skipIf(not HAS_GPG, "GPG Module Unavailable") def test_gpg_import_priv_key(self): - config_user = MagicMock(return_value='salt') - user_info = MagicMock(return_value={'name': 'salt', 'home': self.gpghome, 'uid': 1000}) - with patch.dict(gpg.__salt__, {'config.option': config_user}): - with patch.dict(gpg.__salt__, {'user.info': user_info}): - ret = gpg.import_key(None, self.gpgfile_priv, 'salt', self.gpghome) - self.assertEqual(ret['res'], True) + config_user = MagicMock(return_value="salt") + user_info = MagicMock( + return_value={"name": "salt", "home": self.gpghome, "uid": 1000} + ) + with patch.dict(gpg.__salt__, {"config.option": config_user}): + with patch.dict(gpg.__salt__, {"user.info": user_info}): + ret = gpg.import_key(None, self.gpgfile_priv, "salt", self.gpghome) + self.assertEqual(ret["res"], True) - @skipIf(not HAS_GPG, 'GPG Module Unavailable') + @skipIf(not HAS_GPG, "GPG Module Unavailable") def test_gpg_sign(self): - config_user = MagicMock(return_value='salt') - user_info = MagicMock(return_value={'name': 'salt', 'home': self.gpghome, 'uid': 1000}) - pillar_mock = MagicMock(return_value={'gpg_passphrase': GPG_TEST_KEY_PASSPHRASE}) - with patch.dict(gpg.__salt__, {'config.option': config_user}): - with patch.dict(gpg.__salt__, {'user.info': user_info}): - with patch.dict(gpg.__salt__, {'pillar.get': pillar_mock}): - ret = gpg.import_key(None, self.gpgfile_priv, 'salt', self.gpghome) - self.assertEqual(ret['res'], True) - gpg_text_input = 'The quick brown fox jumped over the lazy dog' - gpg_sign_output = gpg.sign(config_user, GPG_TEST_KEY_ID, gpg_text_input, None, None, True, self.gpghome) + config_user = MagicMock(return_value="salt") + user_info = MagicMock( + return_value={"name": "salt", "home": self.gpghome, "uid": 1000} + ) + pillar_mock = MagicMock( + return_value={"gpg_passphrase": GPG_TEST_KEY_PASSPHRASE} + ) + with patch.dict(gpg.__salt__, {"config.option": config_user}): + with patch.dict(gpg.__salt__, {"user.info": user_info}): + with patch.dict(gpg.__salt__, {"pillar.get": pillar_mock}): + ret = gpg.import_key(None, self.gpgfile_priv, "salt", self.gpghome) + self.assertEqual(ret["res"], True) + gpg_text_input = "The quick brown fox jumped over the lazy dog" + gpg_sign_output = gpg.sign( + config_user, + GPG_TEST_KEY_ID, + gpg_text_input, + None, + None, + True, + self.gpghome, + ) self.assertIsNotNone(gpg_sign_output) diff --git a/tests/unit/modules/test_grains.py b/tests/unit/modules/test_grains.py index 0b6d264147c..4397d7c287b 100644 --- a/tests/unit/modules/test_grains.py +++ b/tests/unit/modules/test_grains.py @@ -2,77 +2,59 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import copy +import os -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - -# Import Salt libs -from salt.exceptions import SaltException import salt.modules.grains as grainsmod import salt.utils.dictupdate as dictupdate +# Import Salt libs +from salt.exceptions import SaltException + # Import 3rd-party libs from salt.utils.odict import OrderedDict +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class GrainsModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - conf_file = os.path.join(RUNTIME_VARS.TMP, '__salt_test_grains') - cachedir = os.path.join(RUNTIME_VARS.TMP, '__salt_test_grains_cache_dir') + conf_file = os.path.join(RUNTIME_VARS.TMP, "__salt_test_grains") + cachedir = os.path.join(RUNTIME_VARS.TMP, "__salt_test_grains_cache_dir") if not os.path.isdir(cachedir): os.makedirs(cachedir) return { grainsmod: { - '__opts__': { - 'conf_file': conf_file, - 'cachedir': cachedir - }, - '__salt__': {'saltutil.refresh_grains': MagicMock()} + "__opts__": {"conf_file": conf_file, "cachedir": cachedir}, + "__salt__": {"saltutil.refresh_grains": MagicMock()}, } } def test_filter_by(self): - with patch.dict(grainsmod.__grains__, {'os_family': 'MockedOS', - '1': '1', - '2': '2', - 'roles': ['A', 'B']}): + with patch.dict( + grainsmod.__grains__, + {"os_family": "MockedOS", "1": "1", "2": "2", "roles": ["A", "B"]}, + ): - dict1 = {'A': 'B', 'C': {'D': {'E': 'F', 'G': 'H'}}} + dict1 = {"A": "B", "C": {"D": {"E": "F", "G": "H"}}} dict2 = { - 'default': { - 'A': 'B', - 'C': { - 'D': 'E' - }, - }, - '1': { - 'A': 'X', - }, - '2': { - 'C': { - 'D': 'H', - }, - }, - 'MockedOS': { - 'A': 'Z', - }, + "default": {"A": "B", "C": {"D": "E"}}, + "1": {"A": "X"}, + "2": {"C": {"D": "H"}}, + "MockedOS": {"A": "Z"}, } - mdict1 = {'D': {'E': 'I'}, 'J': 'K'} - mdict2 = {'A': 'Z'} - mdict3 = {'C': {'D': 'J'}} + mdict1 = {"D": {"E": "I"}, "J": "K"} + mdict2 = {"A": "Z"} + mdict3 = {"C": {"D": "J"}} # test None result with non existent grain and no default - res = grainsmod.filter_by(dict1, grain='xxx') + res = grainsmod.filter_by(dict1, grain="xxx") self.assertIs(res, None) # test None result with os_family grain and no matching result @@ -80,94 +62,85 @@ class GrainsModuleTestCase(TestCase, LoaderModuleMockMixin): self.assertIs(res, None) # test with non existent grain, and a given default key - res = grainsmod.filter_by(dict1, grain='xxx', default='C') - self.assertEqual(res, {'D': {'E': 'F', 'G': 'H'}}) + res = grainsmod.filter_by(dict1, grain="xxx", default="C") + self.assertEqual(res, {"D": {"E": "F", "G": "H"}}) # add a merge dictionary, F disappears - res = grainsmod.filter_by(dict1, grain='xxx', merge=mdict1, default='C') - self.assertEqual(res, {'D': {'E': 'I', 'G': 'H'}, 'J': 'K'}) + res = grainsmod.filter_by(dict1, grain="xxx", merge=mdict1, default="C") + self.assertEqual(res, {"D": {"E": "I", "G": "H"}, "J": "K"}) # dict1 was altered, reestablish - dict1 = {'A': 'B', 'C': {'D': {'E': 'F', 'G': 'H'}}} + dict1 = {"A": "B", "C": {"D": {"E": "F", "G": "H"}}} # default is not present in dict1, check we only have merge in result - res = grainsmod.filter_by(dict1, grain='xxx', merge=mdict1, default='Z') + res = grainsmod.filter_by(dict1, grain="xxx", merge=mdict1, default="Z") self.assertEqual(res, mdict1) # default is not present in dict1, and no merge, should get None - res = grainsmod.filter_by(dict1, grain='xxx', default='Z') + res = grainsmod.filter_by(dict1, grain="xxx", default="Z") self.assertIs(res, None) - #test giving a list as merge argument raise exception + # test giving a list as merge argument raise exception self.assertRaises( - SaltException, - grainsmod.filter_by, - dict1, - 'xxx', - ['foo'], - 'C' + SaltException, grainsmod.filter_by, dict1, "xxx", ["foo"], "C" ) - #Now, re-test with an existing grain (os_family), but with no match. + # Now, re-test with an existing grain (os_family), but with no match. res = grainsmod.filter_by(dict1) self.assertIs(res, None) - res = grainsmod.filter_by(dict1, default='C') - self.assertEqual(res, {'D': {'E': 'F', 'G': 'H'}}) - res = grainsmod.filter_by(dict1, merge=mdict1, default='C') - self.assertEqual(res, {'D': {'E': 'I', 'G': 'H'}, 'J': 'K'}) + res = grainsmod.filter_by(dict1, default="C") + self.assertEqual(res, {"D": {"E": "F", "G": "H"}}) + res = grainsmod.filter_by(dict1, merge=mdict1, default="C") + self.assertEqual(res, {"D": {"E": "I", "G": "H"}, "J": "K"}) # dict1 was altered, reestablish - dict1 = {'A': 'B', 'C': {'D': {'E': 'F', 'G': 'H'}}} - res = grainsmod.filter_by(dict1, merge=mdict1, default='Z') + dict1 = {"A": "B", "C": {"D": {"E": "F", "G": "H"}}} + res = grainsmod.filter_by(dict1, merge=mdict1, default="Z") self.assertEqual(res, mdict1) - res = grainsmod.filter_by(dict1, default='Z') + res = grainsmod.filter_by(dict1, default="Z") self.assertIs(res, None) # this one is in fact a traceback in updatedict, merging a string with a dictionary self.assertRaises( - TypeError, - grainsmod.filter_by, - dict1, - merge=mdict1, - default='A' + TypeError, grainsmod.filter_by, dict1, merge=mdict1, default="A" ) - #Now, re-test with a matching grain. - dict1 = {'A': 'B', 'MockedOS': {'D': {'E': 'F', 'G': 'H'}}} + # Now, re-test with a matching grain. + dict1 = {"A": "B", "MockedOS": {"D": {"E": "F", "G": "H"}}} res = grainsmod.filter_by(dict1) - self.assertEqual(res, {'D': {'E': 'F', 'G': 'H'}}) - res = grainsmod.filter_by(dict1, default='A') - self.assertEqual(res, {'D': {'E': 'F', 'G': 'H'}}) - res = grainsmod.filter_by(dict1, merge=mdict1, default='A') - self.assertEqual(res, {'D': {'E': 'I', 'G': 'H'}, 'J': 'K'}) + self.assertEqual(res, {"D": {"E": "F", "G": "H"}}) + res = grainsmod.filter_by(dict1, default="A") + self.assertEqual(res, {"D": {"E": "F", "G": "H"}}) + res = grainsmod.filter_by(dict1, merge=mdict1, default="A") + self.assertEqual(res, {"D": {"E": "I", "G": "H"}, "J": "K"}) # dict1 was altered, reestablish - dict1 = {'A': 'B', 'MockedOS': {'D': {'E': 'F', 'G': 'H'}}} - res = grainsmod.filter_by(dict1, merge=mdict1, default='Z') - self.assertEqual(res, {'D': {'E': 'I', 'G': 'H'}, 'J': 'K'}) + dict1 = {"A": "B", "MockedOS": {"D": {"E": "F", "G": "H"}}} + res = grainsmod.filter_by(dict1, merge=mdict1, default="Z") + self.assertEqual(res, {"D": {"E": "I", "G": "H"}, "J": "K"}) # dict1 was altered, reestablish - dict1 = {'A': 'B', 'MockedOS': {'D': {'E': 'F', 'G': 'H'}}} - res = grainsmod.filter_by(dict1, default='Z') - self.assertEqual(res, {'D': {'E': 'F', 'G': 'H'}}) + dict1 = {"A": "B", "MockedOS": {"D": {"E": "F", "G": "H"}}} + res = grainsmod.filter_by(dict1, default="Z") + self.assertEqual(res, {"D": {"E": "F", "G": "H"}}) # Test when grain value is a list - dict1 = {'A': 'B', 'C': {'D': {'E': 'F', 'G': 'H'}}} - res = grainsmod.filter_by(dict1, grain='roles', default='C') - self.assertEqual(res, 'B') + dict1 = {"A": "B", "C": {"D": {"E": "F", "G": "H"}}} + res = grainsmod.filter_by(dict1, grain="roles", default="C") + self.assertEqual(res, "B") # Test default when grain value is a list - dict1 = {'Z': 'B', 'C': {'D': {'E': 'F', 'G': 'H'}}} - res = grainsmod.filter_by(dict1, grain='roles', default='C') - self.assertEqual(res, {'D': {'E': 'F', 'G': 'H'}}) + dict1 = {"Z": "B", "C": {"D": {"E": "F", "G": "H"}}} + res = grainsmod.filter_by(dict1, grain="roles", default="C") + self.assertEqual(res, {"D": {"E": "F", "G": "H"}}) # Test with wildcard pattern in the lookup_dict keys - dict1 = {'*OS': 'B', 'C': {'D': {'E': 'F', 'G': 'H'}}} + dict1 = {"*OS": "B", "C": {"D": {"E": "F", "G": "H"}}} res = grainsmod.filter_by(dict1) - self.assertEqual(res, 'B') + self.assertEqual(res, "B") # Test with non-strings in lookup_dict keys # Issue #38094 - dict1 = {1: 2, 3: {4: 5}, '*OS': 'B'} + dict1 = {1: 2, 3: {4: 5}, "*OS": "B"} res = grainsmod.filter_by(dict1) - self.assertEqual(res, 'B') + self.assertEqual(res, "B") # Test with sequence pattern with roles - dict1 = {'Z': 'B', '[BC]': {'D': {'E': 'F', 'G': 'H'}}} - res = grainsmod.filter_by(dict1, grain='roles', default='Z') - self.assertEqual(res, {'D': {'E': 'F', 'G': 'H'}}) + dict1 = {"Z": "B", "[BC]": {"D": {"E": "F", "G": "H"}}} + res = grainsmod.filter_by(dict1, grain="roles", default="Z") + self.assertEqual(res, {"D": {"E": "F", "G": "H"}}) # Base tests # NOTE: these may fail to detect errors if dictupdate.update() is broken @@ -176,454 +149,560 @@ class GrainsModuleTestCase(TestCase, LoaderModuleMockMixin): # in filter_by() processes its arguments. # Test with just the base - res = grainsmod.filter_by(dict2, grain='xxx', default='xxx', base='default') - self.assertEqual(res, dict2['default']) + res = grainsmod.filter_by(dict2, grain="xxx", default="xxx", base="default") + self.assertEqual(res, dict2["default"]) # Test the base with the OS grain look-up - res = grainsmod.filter_by(dict2, default='xxx', base='default') + res = grainsmod.filter_by(dict2, default="xxx", base="default") self.assertEqual( res, - dictupdate.update(copy.deepcopy(dict2['default']), dict2['MockedOS']) + dictupdate.update(copy.deepcopy(dict2["default"]), dict2["MockedOS"]), ) # Test the base with default - res = grainsmod.filter_by(dict2, grain='xxx', base='default') - self.assertEqual(res, dict2['default']) + res = grainsmod.filter_by(dict2, grain="xxx", base="default") + self.assertEqual(res, dict2["default"]) - res = grainsmod.filter_by(dict2, grain='1', base='default') + res = grainsmod.filter_by(dict2, grain="1", base="default") self.assertEqual( - res, - dictupdate.update(copy.deepcopy(dict2['default']), dict2['1']) + res, dictupdate.update(copy.deepcopy(dict2["default"]), dict2["1"]) ) - res = grainsmod.filter_by(dict2, base='default', merge=mdict2) + res = grainsmod.filter_by(dict2, base="default", merge=mdict2) self.assertEqual( res, dictupdate.update( dictupdate.update( - copy.deepcopy(dict2['default']), - dict2['MockedOS']), - mdict2 - ) + copy.deepcopy(dict2["default"]), dict2["MockedOS"] + ), + mdict2, + ), ) - res = grainsmod.filter_by(dict2, base='default', merge=mdict3) + res = grainsmod.filter_by(dict2, base="default", merge=mdict3) self.assertEqual( res, dictupdate.update( dictupdate.update( - copy.deepcopy(dict2['default']), - dict2['MockedOS']), - mdict3 - ) + copy.deepcopy(dict2["default"]), dict2["MockedOS"] + ), + mdict3, + ), ) def test_append_not_a_list(self): # Failing append to an existing string, without convert - with patch.dict(grainsmod.__grains__, {'b': 'bval'}): - res = grainsmod.append('b', 'd') - self.assertEqual(res, 'The key b is not a valid list') - self.assertEqual(grainsmod.__grains__, {'b': 'bval'}) + with patch.dict(grainsmod.__grains__, {"b": "bval"}): + res = grainsmod.append("b", "d") + self.assertEqual(res, "The key b is not a valid list") + self.assertEqual(grainsmod.__grains__, {"b": "bval"}) # Failing append to an existing dict - with patch.dict(grainsmod.__grains__, {'b': {'b1': 'bval1'}}): - res = grainsmod.append('b', 'd') - self.assertEqual(res, 'The key b is not a valid list') - self.assertEqual(grainsmod.__grains__, {'b': {'b1': 'bval1'}}) + with patch.dict(grainsmod.__grains__, {"b": {"b1": "bval1"}}): + res = grainsmod.append("b", "d") + self.assertEqual(res, "The key b is not a valid list") + self.assertEqual(grainsmod.__grains__, {"b": {"b1": "bval1"}}) def test_append_already_in_list(self): # Append an existing value - with patch.dict(grainsmod.__grains__, {'a_list': ['a', 'b', 'c'], 'b': 'bval'}): - res = grainsmod.append('a_list', 'b') - self.assertEqual(res, 'The val b was already in the list a_list') - self.assertEqual(grainsmod.__grains__, {'a_list': ['a', 'b', 'c'], 'b': 'bval'}) + with patch.dict(grainsmod.__grains__, {"a_list": ["a", "b", "c"], "b": "bval"}): + res = grainsmod.append("a_list", "b") + self.assertEqual(res, "The val b was already in the list a_list") + self.assertEqual( + grainsmod.__grains__, {"a_list": ["a", "b", "c"], "b": "bval"} + ) def test_append_ok(self): # Append to an existing list - with patch.dict(grainsmod.__grains__, {'a_list': ['a', 'b', 'c'], 'b': 'bval'}): - res = grainsmod.append('a_list', 'd') - self.assertEqual(res, {'a_list': ['a', 'b', 'c', 'd']}) - self.assertEqual(grainsmod.__grains__, {'a_list': ['a', 'b', 'c', 'd'], 'b': 'bval'}) + with patch.dict(grainsmod.__grains__, {"a_list": ["a", "b", "c"], "b": "bval"}): + res = grainsmod.append("a_list", "d") + self.assertEqual(res, {"a_list": ["a", "b", "c", "d"]}) + self.assertEqual( + grainsmod.__grains__, {"a_list": ["a", "b", "c", "d"], "b": "bval"} + ) # Append to an non existing list - with patch.dict(grainsmod.__grains__, {'b': 'bval'}): - res = grainsmod.append('a_list', 'd') - self.assertEqual(res, {'a_list': ['d']}) - self.assertEqual(grainsmod.__grains__, {'a_list': ['d'], 'b': 'bval'}) + with patch.dict(grainsmod.__grains__, {"b": "bval"}): + res = grainsmod.append("a_list", "d") + self.assertEqual(res, {"a_list": ["d"]}) + self.assertEqual(grainsmod.__grains__, {"a_list": ["d"], "b": "bval"}) # Append to an existing string, with convert - with patch.dict(grainsmod.__grains__, {'b': 'bval'}): - res = grainsmod.append('b', 'd', convert=True) - self.assertEqual(res, {'b': ['bval', 'd']}) - self.assertEqual(grainsmod.__grains__, {'b': ['bval', 'd']}) + with patch.dict(grainsmod.__grains__, {"b": "bval"}): + res = grainsmod.append("b", "d", convert=True) + self.assertEqual(res, {"b": ["bval", "d"]}) + self.assertEqual(grainsmod.__grains__, {"b": ["bval", "d"]}) # Append to an existing dict, with convert - with patch.dict(grainsmod.__grains__, {'b': {'b1': 'bval1'}}): - res = grainsmod.append('b', 'd', convert=True) - self.assertEqual(res, {'b': [{'b1': 'bval1'}, 'd']}) - self.assertEqual(grainsmod.__grains__, {'b': [{'b1': 'bval1'}, 'd']}) + with patch.dict(grainsmod.__grains__, {"b": {"b1": "bval1"}}): + res = grainsmod.append("b", "d", convert=True) + self.assertEqual(res, {"b": [{"b1": "bval1"}, "d"]}) + self.assertEqual(grainsmod.__grains__, {"b": [{"b1": "bval1"}, "d"]}) def test_append_nested_not_a_list(self): # Failing append to an existing string, without convert - with patch.dict(grainsmod.__grains__, {'a': {'b': 'bval'}}): - res = grainsmod.append('a:b', 'd') - self.assertEqual(res, 'The key a:b is not a valid list') - self.assertEqual(grainsmod.__grains__, {'a': {'b': 'bval'}}) + with patch.dict(grainsmod.__grains__, {"a": {"b": "bval"}}): + res = grainsmod.append("a:b", "d") + self.assertEqual(res, "The key a:b is not a valid list") + self.assertEqual(grainsmod.__grains__, {"a": {"b": "bval"}}) # Failing append to an existing dict - with patch.dict(grainsmod.__grains__, {'a': {'b': {'b1': 'bval1'}}}): - res = grainsmod.append('a:b', 'd') - self.assertEqual(res, 'The key a:b is not a valid list') - self.assertEqual(grainsmod.__grains__, {'a': {'b': {'b1': 'bval1'}}}) + with patch.dict(grainsmod.__grains__, {"a": {"b": {"b1": "bval1"}}}): + res = grainsmod.append("a:b", "d") + self.assertEqual(res, "The key a:b is not a valid list") + self.assertEqual(grainsmod.__grains__, {"a": {"b": {"b1": "bval1"}}}) def test_append_nested_already_in_list(self): # Append an existing value - with patch.dict(grainsmod.__grains__, {'a': {'a_list': ['a', 'b', 'c'], 'b': 'bval'}}): - res = grainsmod.append('a:a_list', 'b') - self.assertEqual(res, 'The val b was already in the list a:a_list') - self.assertEqual(grainsmod.__grains__, {'a': {'a_list': ['a', 'b', 'c'], 'b': 'bval'}}) + with patch.dict( + grainsmod.__grains__, {"a": {"a_list": ["a", "b", "c"], "b": "bval"}} + ): + res = grainsmod.append("a:a_list", "b") + self.assertEqual(res, "The val b was already in the list a:a_list") + self.assertEqual( + grainsmod.__grains__, {"a": {"a_list": ["a", "b", "c"], "b": "bval"}} + ) def test_append_nested_ok(self): # Append to an existing list - with patch.dict(grainsmod.__grains__, {'a': {'a_list': ['a', 'b', 'c'], 'b': 'bval'}}): - res = grainsmod.append('a:a_list', 'd') - self.assertEqual(res, {'a': {'a_list': ['a', 'b', 'c', 'd'], 'b': 'bval'}}) - self.assertEqual(grainsmod.__grains__, {'a': {'a_list': ['a', 'b', 'c', 'd'], 'b': 'bval'}}) + with patch.dict( + grainsmod.__grains__, {"a": {"a_list": ["a", "b", "c"], "b": "bval"}} + ): + res = grainsmod.append("a:a_list", "d") + self.assertEqual(res, {"a": {"a_list": ["a", "b", "c", "d"], "b": "bval"}}) + self.assertEqual( + grainsmod.__grains__, + {"a": {"a_list": ["a", "b", "c", "d"], "b": "bval"}}, + ) # Append to an non existing list - with patch.dict(grainsmod.__grains__, {'a': {'b': 'bval'}}): - res = grainsmod.append('a:a_list', 'd') - self.assertEqual(res, {'a': {'a_list': ['d'], 'b': 'bval'}}) - self.assertEqual(grainsmod.__grains__, {'a': {'a_list': ['d'], 'b': 'bval'}}) + with patch.dict(grainsmod.__grains__, {"a": {"b": "bval"}}): + res = grainsmod.append("a:a_list", "d") + self.assertEqual(res, {"a": {"a_list": ["d"], "b": "bval"}}) + self.assertEqual( + grainsmod.__grains__, {"a": {"a_list": ["d"], "b": "bval"}} + ) # Append to an existing string, with convert - with patch.dict(grainsmod.__grains__, {'a': {'b': 'bval'}}): - res = grainsmod.append('a:b', 'd', convert=True) - self.assertEqual(res, {'a': {'b': ['bval', 'd']}}) - self.assertEqual(grainsmod.__grains__, {'a': {'b': ['bval', 'd']}}) + with patch.dict(grainsmod.__grains__, {"a": {"b": "bval"}}): + res = grainsmod.append("a:b", "d", convert=True) + self.assertEqual(res, {"a": {"b": ["bval", "d"]}}) + self.assertEqual(grainsmod.__grains__, {"a": {"b": ["bval", "d"]}}) # Append to an existing dict, with convert - with patch.dict(grainsmod.__grains__, {'a': {'b': {'b1': 'bval1'}}}): - res = grainsmod.append('a:b', 'd', convert=True) - self.assertEqual(res, {'a': {'b': [{'b1': 'bval1'}, 'd']}}) - self.assertEqual(grainsmod.__grains__, {'a': {'b': [{'b1': 'bval1'}, 'd']}}) + with patch.dict(grainsmod.__grains__, {"a": {"b": {"b1": "bval1"}}}): + res = grainsmod.append("a:b", "d", convert=True) + self.assertEqual(res, {"a": {"b": [{"b1": "bval1"}, "d"]}}) + self.assertEqual(grainsmod.__grains__, {"a": {"b": [{"b1": "bval1"}, "d"]}}) def test_append_to_an_element_of_a_list(self): # Append to an element in a list # It currently fails silently - with patch.dict(grainsmod.__grains__, {'a': ['b', 'c']}): - res = grainsmod.append('a:b', 'd') - self.assertEqual(res, {'a': ['b', 'c']}) - self.assertEqual(grainsmod.__grains__, {'a': ['b', 'c']}) + with patch.dict(grainsmod.__grains__, {"a": ["b", "c"]}): + res = grainsmod.append("a:b", "d") + self.assertEqual(res, {"a": ["b", "c"]}) + self.assertEqual(grainsmod.__grains__, {"a": ["b", "c"]}) def test_set_value_already_set(self): # Set a grain to the same simple value - with patch.dict(grainsmod.__grains__, {'a': 12, 'c': 8}): - res = grainsmod.set('a', 12) - self.assertTrue(res['result']) - self.assertEqual(res['comment'], 'Grain is already set') - self.assertEqual(grainsmod.__grains__, {'a': 12, 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": 12, "c": 8}): + res = grainsmod.set("a", 12) + self.assertTrue(res["result"]) + self.assertEqual(res["comment"], "Grain is already set") + self.assertEqual(grainsmod.__grains__, {"a": 12, "c": 8}) # Set a grain to the same complex value - with patch.dict(grainsmod.__grains__, {'a': ['item', 12], 'c': 8}): - res = grainsmod.set('a', ['item', 12]) - self.assertTrue(res['result']) - self.assertEqual(res['comment'], 'Grain is already set') - self.assertEqual(grainsmod.__grains__, {'a': ['item', 12], 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": ["item", 12], "c": 8}): + res = grainsmod.set("a", ["item", 12]) + self.assertTrue(res["result"]) + self.assertEqual(res["comment"], "Grain is already set") + self.assertEqual(grainsmod.__grains__, {"a": ["item", 12], "c": 8}) # Set a key to the same simple value in a nested grain - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'b': {'nested': 'val'}, 'c': 8}): - res = grainsmod.set('b,nested', 'val', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['comment'], 'Grain is already set') - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': {'nested': 'val'}, - 'c': 8}) + with patch.dict( + grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8} + ): + res = grainsmod.set("b,nested", "val", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual(res["comment"], "Grain is already set") + self.assertEqual( + grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8} + ) def test_set_fail_replacing_existing_complex_key(self): # Fails to set a complex value without 'force' - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'c': 8}): - res = grainsmod.set('a', ['item', 12]) - self.assertFalse(res['result']) - self.assertEqual(res['comment'], 'The key \'a\' exists and the given value is a ' - 'dict or a list. Use \'force=True\' to overwrite.') - self.assertEqual(grainsmod.__grains__, {'a': 'aval', 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}): + res = grainsmod.set("a", ["item", 12]) + self.assertFalse(res["result"]) + self.assertEqual( + res["comment"], + "The key 'a' exists and the given value is a " + "dict or a list. Use 'force=True' to overwrite.", + ) + self.assertEqual(grainsmod.__grains__, {"a": "aval", "c": 8}) # Fails to overwrite a complex value without 'force' - with patch.dict(grainsmod.__grains__, {'a': ['item', 12], 'c': 8}): - res = grainsmod.set('a', ['item', 14]) - self.assertFalse(res['result']) - self.assertEqual(res['comment'], 'The key \'a\' exists but is a dict or a list. ' - + 'Use \'force=True\' to overwrite.') - self.assertEqual(grainsmod.__grains__, {'a': ['item', 12], 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": ["item", 12], "c": 8}): + res = grainsmod.set("a", ["item", 14]) + self.assertFalse(res["result"]) + self.assertEqual( + res["comment"], + "The key 'a' exists but is a dict or a list. " + + "Use 'force=True' to overwrite.", + ) + self.assertEqual(grainsmod.__grains__, {"a": ["item", 12], "c": 8}) # Fails to overwrite a complex value without 'force' in a nested grain - with patch.dict(grainsmod.__grains__, {'a': 'aval', - 'b': ['l1', {'l2': ['val1']}], - 'c': 8}): - res = grainsmod.set('b,l2', 'val2', delimiter=',') - self.assertFalse(res['result']) - self.assertEqual(res['comment'], 'The key \'b,l2\' exists but is a dict or a ' - + 'list. Use \'force=True\' to overwrite.') - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': ['l1', {'l2': ['val1']}], - 'c': 8}) + with patch.dict( + grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": ["val1"]}], "c": 8} + ): + res = grainsmod.set("b,l2", "val2", delimiter=",") + self.assertFalse(res["result"]) + self.assertEqual( + res["comment"], + "The key 'b,l2' exists but is a dict or a " + + "list. Use 'force=True' to overwrite.", + ) + self.assertEqual( + grainsmod.__grains__, + {"a": "aval", "b": ["l1", {"l2": ["val1"]}], "c": 8}, + ) def test_set_nested_fails_replace_simple_value(self): # Fails to replace a simple value with a new dictionary consisting # of the specified key and value - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'b': 'l1', 'c': 8}): - res = grainsmod.set('b,l3', 'val3', delimiter=',') - self.assertFalse(res['result']) - self.assertEqual(res['comment'], 'The key \'b\' value is \'l1\', which is ' - + 'different from the provided key \'l3\'. ' - + 'Use \'force=True\' to overwrite.') - self.assertEqual(grainsmod.__grains__, {'a': 'aval', 'b': 'l1', 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "b": "l1", "c": 8}): + res = grainsmod.set("b,l3", "val3", delimiter=",") + self.assertFalse(res["result"]) + self.assertEqual( + res["comment"], + "The key 'b' value is 'l1', which is " + + "different from the provided key 'l3'. " + + "Use 'force=True' to overwrite.", + ) + self.assertEqual(grainsmod.__grains__, {"a": "aval", "b": "l1", "c": 8}) def test_set_simple_value(self): - with patch.dict(grainsmod.__grains__, {'a': ['b', 'c'], 'c': 8}): - res = grainsmod.set('b', 'bval') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': 'bval'}) - self.assertEqual(grainsmod.__grains__, {'a': ['b', 'c'], - 'b': 'bval', - 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": ["b", "c"], "c": 8}): + res = grainsmod.set("b", "bval") + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": "bval"}) + self.assertEqual( + grainsmod.__grains__, {"a": ["b", "c"], "b": "bval", "c": 8} + ) def test_set_replace_value(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'c': 8}): - res = grainsmod.set('a', 12) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'a': 12}) - self.assertEqual(grainsmod.__grains__, {'a': 12, 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}): + res = grainsmod.set("a", 12) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"a": 12}) + self.assertEqual(grainsmod.__grains__, {"a": 12, "c": 8}) def test_set_None_ok(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'c': 8}): - res = grainsmod.set('b', None) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': None}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', 'b': None, 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}): + res = grainsmod.set("b", None) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": None}) + self.assertEqual(grainsmod.__grains__, {"a": "aval", "b": None, "c": 8}) def test_set_None_ok_destructive(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'c': 8}): - res = grainsmod.set('b', None, destructive=True) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': None}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}): + res = grainsmod.set("b", None, destructive=True) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": None}) + self.assertEqual(grainsmod.__grains__, {"a": "aval", "c": 8}) def test_set_None_replace_ok(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'c': 8}): - res = grainsmod.set('a', None) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'a': None}) - self.assertEqual(grainsmod.__grains__, {'a': None, 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}): + res = grainsmod.set("a", None) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"a": None}) + self.assertEqual(grainsmod.__grains__, {"a": None, "c": 8}) def test_set_None_force_destructive(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'c': 8}): - res = grainsmod.set('a', None, force=True, destructive=True) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'a': None}) - self.assertEqual(grainsmod.__grains__, {'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}): + res = grainsmod.set("a", None, force=True, destructive=True) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"a": None}) + self.assertEqual(grainsmod.__grains__, {"c": 8}) def test_set_replace_value_was_complex_force(self): - with patch.dict(grainsmod.__grains__, {'a': ['item', 12], 'c': 8}): - res = grainsmod.set('a', 'aval', force=True) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'a': 'aval'}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": ["item", 12], "c": 8}): + res = grainsmod.set("a", "aval", force=True) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"a": "aval"}) + self.assertEqual(grainsmod.__grains__, {"a": "aval", "c": 8}) def test_set_complex_value_force(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'c': 8}): - res = grainsmod.set('a', ['item', 12], force=True) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'a': ['item', 12]}) - self.assertEqual(grainsmod.__grains__, {'a': ['item', 12], 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}): + res = grainsmod.set("a", ["item", 12], force=True) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"a": ["item", 12]}) + self.assertEqual(grainsmod.__grains__, {"a": ["item", 12], "c": 8}) def test_set_nested_create(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'c': 8}): - res = grainsmod.set('b,nested', 'val', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': {'nested': 'val'}}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': {'nested': 'val'}, - 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "c": 8}): + res = grainsmod.set("b,nested", "val", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": {"nested": "val"}}) + self.assertEqual( + grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8} + ) def test_set_nested_update_dict(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'b': {'nested': 'val'}, 'c': 8}): - res = grainsmod.set('b,nested', 'val2', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': {'nested': 'val2'}}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': {'nested': 'val2'}, - 'c': 8}) + with patch.dict( + grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8} + ): + res = grainsmod.set("b,nested", "val2", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": {"nested": "val2"}}) + self.assertEqual( + grainsmod.__grains__, {"a": "aval", "b": {"nested": "val2"}, "c": 8} + ) def test_set_nested_update_dict_remove_key(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'b': {'nested': 'val'}, 'c': 8}): - res = grainsmod.set('b,nested', None, delimiter=',', destructive=True) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': {}}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', 'b': {}, 'c': 8}) + with patch.dict( + grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8} + ): + res = grainsmod.set("b,nested", None, delimiter=",", destructive=True) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": {}}) + self.assertEqual(grainsmod.__grains__, {"a": "aval", "b": {}, "c": 8}) def test_set_nested_update_dict_new_key(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'b': {'nested': 'val'}, 'c': 8}): - res = grainsmod.set('b,b2', 'val2', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': {'b2': 'val2', 'nested': 'val'}}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': {'b2': 'val2', - 'nested': 'val'}, - 'c': 8}) + with patch.dict( + grainsmod.__grains__, {"a": "aval", "b": {"nested": "val"}, "c": 8} + ): + res = grainsmod.set("b,b2", "val2", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": {"b2": "val2", "nested": "val"}}) + self.assertEqual( + grainsmod.__grains__, + {"a": "aval", "b": {"b2": "val2", "nested": "val"}, "c": 8}, + ) def test_set_nested_list_replace_key(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'b': ['l1', 'l2', 'l3'], 'c': 8}): - res = grainsmod.set('b,l2', 'val2', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': ['l1', {'l2': 'val2'}, 'l3']}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': ['l1', {'l2': 'val2'}, 'l3'], - 'c': 8}) + with patch.dict( + grainsmod.__grains__, {"a": "aval", "b": ["l1", "l2", "l3"], "c": 8} + ): + res = grainsmod.set("b,l2", "val2", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": ["l1", {"l2": "val2"}, "l3"]}) + self.assertEqual( + grainsmod.__grains__, + {"a": "aval", "b": ["l1", {"l2": "val2"}, "l3"], "c": 8}, + ) def test_set_nested_list_update_dict_key(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'b': ['l1', {'l2': 'val1'}], 'c': 8}): - res = grainsmod.set('b,l2', 'val2', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': ['l1', {'l2': 'val2'}]}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': ['l1', {'l2': 'val2'}], - 'c': 8}) + with patch.dict( + grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": "val1"}], "c": 8} + ): + res = grainsmod.set("b,l2", "val2", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": ["l1", {"l2": "val2"}]}) + self.assertEqual( + grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": "val2"}], "c": 8} + ) def test_set_nested_list_update_dict_key_overwrite(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', - 'b': ['l1', {'l2': ['val1']}], - 'c': 8}): - res = grainsmod.set('b,l2', 'val2', delimiter=',', force=True) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': ['l1', {'l2': 'val2'}]}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': ['l1', {'l2': 'val2'}], - 'c': 8}) + with patch.dict( + grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": ["val1"]}], "c": 8} + ): + res = grainsmod.set("b,l2", "val2", delimiter=",", force=True) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": ["l1", {"l2": "val2"}]}) + self.assertEqual( + grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": "val2"}], "c": 8} + ) def test_set_nested_list_append_dict_key(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', - 'b': ['l1', {'l2': 'val2'}], - 'c': 8}): - res = grainsmod.set('b,l3', 'val3', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': ['l1', {'l2': 'val2'}, {'l3': 'val3'}]}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': ['l1', - {'l2': 'val2'}, - {'l3': 'val3'}], - 'c': 8}) + with patch.dict( + grainsmod.__grains__, {"a": "aval", "b": ["l1", {"l2": "val2"}], "c": 8} + ): + res = grainsmod.set("b,l3", "val3", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual( + res["changes"], {"b": ["l1", {"l2": "val2"}, {"l3": "val3"}]} + ) + self.assertEqual( + grainsmod.__grains__, + {"a": "aval", "b": ["l1", {"l2": "val2"}, {"l3": "val3"}], "c": 8}, + ) def test_set_nested_existing_value_is_the_key(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'b': 'l3', 'c': 8}): - res = grainsmod.set('b,l3', 'val3', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': {'l3': 'val3'}}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': {'l3': 'val3'}, - 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "b": "l3", "c": 8}): + res = grainsmod.set("b,l3", "val3", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": {"l3": "val3"}}) + self.assertEqual( + grainsmod.__grains__, {"a": "aval", "b": {"l3": "val3"}, "c": 8} + ) def test_set_nested_existing_value_overwrite(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', 'b': 'l1', 'c': 8}): - res = grainsmod.set('b,l3', 'val3', delimiter=',', force=True) - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': {'l3': 'val3'}}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': {'l3': 'val3'}, - 'c': 8}) + with patch.dict(grainsmod.__grains__, {"a": "aval", "b": "l1", "c": 8}): + res = grainsmod.set("b,l3", "val3", delimiter=",", force=True) + self.assertTrue(res["result"]) + self.assertEqual(res["changes"], {"b": {"l3": "val3"}}) + self.assertEqual( + grainsmod.__grains__, {"a": "aval", "b": {"l3": "val3"}, "c": 8} + ) def test_set_deeply_nested_update(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', - 'b': {'l1': ['l21', 'l22', {'l23': 'l23val'}]}, - 'c': 8}): - res = grainsmod.set('b,l1,l23', 'val', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': {'l1': ['l21', 'l22', {'l23': 'val'}]}}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': {'l1': ['l21', - 'l22', - {'l23': 'val'}]}, - 'c': 8}) + with patch.dict( + grainsmod.__grains__, + {"a": "aval", "b": {"l1": ["l21", "l22", {"l23": "l23val"}]}, "c": 8}, + ): + res = grainsmod.set("b,l1,l23", "val", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual( + res["changes"], {"b": {"l1": ["l21", "l22", {"l23": "val"}]}} + ) + self.assertEqual( + grainsmod.__grains__, + {"a": "aval", "b": {"l1": ["l21", "l22", {"l23": "val"}]}, "c": 8}, + ) def test_set_deeply_nested_create(self): - with patch.dict(grainsmod.__grains__, {'a': 'aval', - 'b': {'l1': ['l21', 'l22', {'l23': 'l23val'}]}, - 'c': 8}): - res = grainsmod.set('b,l1,l24,l241', 'val', delimiter=',') - self.assertTrue(res['result']) - self.assertEqual(res['changes'], {'b': {'l1': ['l21', - 'l22', - {'l23': 'l23val'}, - {'l24': {'l241': 'val'}}]}}) - self.assertEqual(grainsmod.__grains__, {'a': 'aval', - 'b': {'l1': [ - 'l21', - 'l22', - {'l23': 'l23val'}, - {'l24': {'l241': 'val'}}]}, - 'c': 8}) + with patch.dict( + grainsmod.__grains__, + {"a": "aval", "b": {"l1": ["l21", "l22", {"l23": "l23val"}]}, "c": 8}, + ): + res = grainsmod.set("b,l1,l24,l241", "val", delimiter=",") + self.assertTrue(res["result"]) + self.assertEqual( + res["changes"], + { + "b": { + "l1": [ + "l21", + "l22", + {"l23": "l23val"}, + {"l24": {"l241": "val"}}, + ] + } + }, + ) + self.assertEqual( + grainsmod.__grains__, + { + "a": "aval", + "b": { + "l1": [ + "l21", + "l22", + {"l23": "l23val"}, + {"l24": {"l241": "val"}}, + ] + }, + "c": 8, + }, + ) def test_get_ordered(self): - with patch.dict(grainsmod.__grains__, OrderedDict([ - ('a', 'aval'), - ('b', OrderedDict([ - ('z', 'zval'), - ('l1', ['l21', - 'l22', - OrderedDict([('l23', 'l23val')])]) - ])), - ('c', 8)])): - res = grainsmod.get('b') + with patch.dict( + grainsmod.__grains__, + OrderedDict( + [ + ("a", "aval"), + ( + "b", + OrderedDict( + [ + ("z", "zval"), + ( + "l1", + ["l21", "l22", OrderedDict([("l23", "l23val")])], + ), + ] + ), + ), + ("c", 8), + ] + ), + ): + res = grainsmod.get("b") self.assertEqual(type(res), OrderedDict) # Check that order really matters - self.assertTrue(res == OrderedDict([ - ('z', 'zval'), - ('l1', ['l21', - 'l22', - OrderedDict([('l23', 'l23val')])]), - ])) - self.assertFalse(res == OrderedDict([ - ('l1', ['l21', - 'l22', - OrderedDict([('l23', 'l23val')])]), - ('z', 'zval'), - ])) + self.assertTrue( + res + == OrderedDict( + [ + ("z", "zval"), + ("l1", ["l21", "l22", OrderedDict([("l23", "l23val")])]), + ] + ) + ) + self.assertFalse( + res + == OrderedDict( + [ + ("l1", ["l21", "l22", OrderedDict([("l23", "l23val")])]), + ("z", "zval"), + ] + ) + ) def test_get_unordered(self): - with patch.dict(grainsmod.__grains__, OrderedDict([ - ('a', 'aval'), - ('b', OrderedDict([ - ('z', 'zval'), - ('l1', ['l21', - 'l22', - OrderedDict([('l23', 'l23val')])]) - ])), - ('c', 8)])): - res = grainsmod.get('b', ordered=False) + with patch.dict( + grainsmod.__grains__, + OrderedDict( + [ + ("a", "aval"), + ( + "b", + OrderedDict( + [ + ("z", "zval"), + ( + "l1", + ["l21", "l22", OrderedDict([("l23", "l23val")])], + ), + ] + ), + ), + ("c", 8), + ] + ), + ): + res = grainsmod.get("b", ordered=False) self.assertEqual(type(res), dict) # Check that order doesn't matter - self.assertTrue(res == OrderedDict([ - ('l1', ['l21', - 'l22', - OrderedDict([('l23', 'l23val')])]), - ('z', 'zval'), - ])) + self.assertTrue( + res + == OrderedDict( + [ + ("l1", ["l21", "l22", OrderedDict([("l23", "l23val")])]), + ("z", "zval"), + ] + ) + ) def test_equals(self): - with patch.dict(grainsmod.__grains__, OrderedDict([ - ('a', 'aval'), - ('b', OrderedDict([ - ('z', 'zval'), - ('l1', ['l21', - 'l22', - OrderedDict([('l23', 'l23val')])]) - ])), - ('c', 8)])): - res = grainsmod.equals('a', 'aval') + with patch.dict( + grainsmod.__grains__, + OrderedDict( + [ + ("a", "aval"), + ( + "b", + OrderedDict( + [ + ("z", "zval"), + ( + "l1", + ["l21", "l22", OrderedDict([("l23", "l23val")])], + ), + ] + ), + ), + ("c", 8), + ] + ), + ): + res = grainsmod.equals("a", "aval") self.assertEqual(type(res), bool) self.assertTrue(res) - res = grainsmod.equals('b:z', 'zval') + res = grainsmod.equals("b:z", "zval") self.assertTrue(res) - res = grainsmod.equals('b:z', 'aval') + res = grainsmod.equals("b:z", "aval") self.assertFalse(res) diff --git a/tests/unit/modules/test_groupadd.py b/tests/unit/modules/test_groupadd.py index a8ee5ebd6e1..f36b3b8f12c 100644 --- a/tests/unit/modules/test_groupadd.py +++ b/tests/unit/modules/test_groupadd.py @@ -1,30 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -try: - import grp -except ImportError: - pass - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.modules.groupadd as groupadd import salt.utils.platform +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + +try: + import grp +except ImportError: + pass + @skipIf(salt.utils.platform.is_windows(), "Module not available on Windows") class GroupAddTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.groupadd - ''' + """ def setup_loader_modules(self): return {groupadd: {}} @@ -32,176 +33,233 @@ class GroupAddTestCase(TestCase, LoaderModuleMockMixin): # 'add' function tests: 1 def test_add(self): - ''' + """ Tests if specified group was added - ''' - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(groupadd.__salt__, {'cmd.run_all': mock}): - self.assertTrue(groupadd.add('test', 100)) + """ + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(groupadd.__salt__, {"cmd.run_all": mock}): + self.assertTrue(groupadd.add("test", 100)) - with patch.dict(groupadd.__grains__, {'kernel': 'Linux'}): - with patch.dict(groupadd.__salt__, {'cmd.run_all': mock}): - self.assertTrue(groupadd.add('test', 100, True)) + with patch.dict(groupadd.__grains__, {"kernel": "Linux"}): + with patch.dict(groupadd.__salt__, {"cmd.run_all": mock}): + self.assertTrue(groupadd.add("test", 100, True)) # 'info' function tests: 1 def test_info(self): - ''' + """ Tests the return of group information - ''' - getgrnam = grp.struct_group(('foo', '*', 20, ['test'])) - with patch('grp.getgrnam', MagicMock(return_value=getgrnam)): - ret = {'passwd': '*', 'gid': 20, 'name': 'foo', 'members': ['test']} - self.assertEqual(groupadd.info('foo'), ret) + """ + getgrnam = grp.struct_group(("foo", "*", 20, ["test"])) + with patch("grp.getgrnam", MagicMock(return_value=getgrnam)): + ret = {"passwd": "*", "gid": 20, "name": "foo", "members": ["test"]} + self.assertEqual(groupadd.info("foo"), ret) # '_format_info' function tests: 1 def test_format_info(self): - ''' + """ Tests the formatting of returned group information - ''' - group = {'passwd': '*', 'gid': 0, 'name': 'test', 'members': ['root']} - with patch('salt.modules.groupadd._format_info', MagicMock(return_value=group)): - data = grp.struct_group(('wheel', '*', 0, ['root'])) - ret = {'passwd': '*', 'gid': 0, 'name': 'test', 'members': ['root']} + """ + group = {"passwd": "*", "gid": 0, "name": "test", "members": ["root"]} + with patch("salt.modules.groupadd._format_info", MagicMock(return_value=group)): + data = grp.struct_group(("wheel", "*", 0, ["root"])) + ret = {"passwd": "*", "gid": 0, "name": "test", "members": ["root"]} self.assertDictEqual(groupadd._format_info(data), ret) # 'getent' function tests: 1 def test_getent(self): - ''' + """ Tests the return of information on all groups - ''' - getgrnam = grp.struct_group(('foo', '*', 20, ['test'])) - with patch('grp.getgrall', MagicMock(return_value=[getgrnam])): - ret = [{'passwd': '*', 'gid': 20, 'name': 'foo', 'members': ['test']}] + """ + getgrnam = grp.struct_group(("foo", "*", 20, ["test"])) + with patch("grp.getgrall", MagicMock(return_value=[getgrnam])): + ret = [{"passwd": "*", "gid": 20, "name": "foo", "members": ["test"]}] self.assertEqual(groupadd.getent(), ret) # 'chgid' function tests: 2 def test_chgid_gid_same(self): - ''' + """ Tests if the group id is the same as argument - ''' - mock = MagicMock(return_value={'gid': 10}) - with patch.object(groupadd, 'info', mock): - self.assertTrue(groupadd.chgid('test', 10)) + """ + mock = MagicMock(return_value={"gid": 10}) + with patch.object(groupadd, "info", mock): + self.assertTrue(groupadd.chgid("test", 10)) def test_chgid(self): - ''' + """ Tests the gid for a named group was changed - ''' + """ mock = MagicMock(return_value=None) - with patch.dict(groupadd.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'gid': 10}, {'gid': 500}]) - with patch.object(groupadd, 'info', mock): - self.assertTrue(groupadd.chgid('test', 500)) + with patch.dict(groupadd.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"gid": 10}, {"gid": 500}]) + with patch.object(groupadd, "info", mock): + self.assertTrue(groupadd.chgid("test", 500)) # 'delete' function tests: 1 def test_delete(self): - ''' + """ Tests if the specified group was deleted - ''' - mock_ret = MagicMock(return_value={'retcode': 0}) - with patch.dict(groupadd.__salt__, {'cmd.run_all': mock_ret}): - self.assertTrue(groupadd.delete('test')) + """ + mock_ret = MagicMock(return_value={"retcode": 0}) + with patch.dict(groupadd.__salt__, {"cmd.run_all": mock_ret}): + self.assertTrue(groupadd.delete("test")) # 'adduser' function tests: 1 def test_adduser(self): - ''' + """ Tests if specified user gets added in the group. - ''' + """ os_version_list = [ - {'grains': {'kernel': 'Linux', 'os_family': 'RedHat', 'osmajorrelease': '5'}, - 'cmd': ['gpasswd', '-a', 'root', 'test']}, - - {'grains': {'kernel': 'Linux', 'os_family': 'Suse', 'osmajorrelease': '11'}, - 'cmd': ['usermod', '-A', 'test', 'root']}, - - {'grains': {'kernel': 'Linux'}, - 'cmd': ['gpasswd', '--add', 'root', 'test']}, - - {'grains': {'kernel': 'OTHERKERNEL'}, - 'cmd': ['usermod', '-G', 'test', 'root']}, + { + "grains": { + "kernel": "Linux", + "os_family": "RedHat", + "osmajorrelease": "5", + }, + "cmd": ["gpasswd", "-a", "root", "test"], + }, + { + "grains": { + "kernel": "Linux", + "os_family": "Suse", + "osmajorrelease": "11", + }, + "cmd": ["usermod", "-A", "test", "root"], + }, + { + "grains": {"kernel": "Linux"}, + "cmd": ["gpasswd", "--add", "root", "test"], + }, + { + "grains": {"kernel": "OTHERKERNEL"}, + "cmd": ["usermod", "-G", "test", "root"], + }, ] for os_version in os_version_list: - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(groupadd.__grains__, os_version['grains']): - with patch.dict(groupadd.__salt__, {'cmd.retcode': mock}): - self.assertFalse(groupadd.adduser('test', 'root')) - groupadd.__salt__['cmd.retcode'].assert_called_once_with(os_version['cmd'], python_shell=False) + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(groupadd.__grains__, os_version["grains"]): + with patch.dict(groupadd.__salt__, {"cmd.retcode": mock}): + self.assertFalse(groupadd.adduser("test", "root")) + groupadd.__salt__["cmd.retcode"].assert_called_once_with( + os_version["cmd"], python_shell=False + ) # 'deluser' function tests: 1 def test_deluser(self): - ''' + """ Tests if specified user gets deleted from the group. - ''' + """ os_version_list = [ - {'grains': {'kernel': 'Linux', 'os_family': 'RedHat', 'osmajorrelease': '5'}, - 'cmd': ['gpasswd', '-d', 'root', 'test']}, - - {'grains': {'kernel': 'Linux', 'os_family': 'Suse', 'osmajorrelease': '11'}, - 'cmd': ['usermod', '-R', 'test', 'root']}, - - {'grains': {'kernel': 'Linux'}, - 'cmd': ['gpasswd', '--del', 'root', 'test']}, - - {'grains': {'kernel': 'OpenBSD'}, - 'cmd': ['usermod', '-S', 'foo', 'root']}, + { + "grains": { + "kernel": "Linux", + "os_family": "RedHat", + "osmajorrelease": "5", + }, + "cmd": ["gpasswd", "-d", "root", "test"], + }, + { + "grains": { + "kernel": "Linux", + "os_family": "Suse", + "osmajorrelease": "11", + }, + "cmd": ["usermod", "-R", "test", "root"], + }, + { + "grains": {"kernel": "Linux"}, + "cmd": ["gpasswd", "--del", "root", "test"], + }, + {"grains": {"kernel": "OpenBSD"}, "cmd": ["usermod", "-S", "foo", "root"]}, ] for os_version in os_version_list: mock_retcode = MagicMock(return_value=0) - mock_stdout = MagicMock(return_value='test foo') - mock_info = MagicMock(return_value={'passwd': '*', - 'gid': 0, - 'name': 'test', - 'members': ['root']}) + mock_stdout = MagicMock(return_value="test foo") + mock_info = MagicMock( + return_value={ + "passwd": "*", + "gid": 0, + "name": "test", + "members": ["root"], + } + ) - with patch.dict(groupadd.__grains__, os_version['grains']): - with patch.dict(groupadd.__salt__, {'cmd.retcode': mock_retcode, - 'group.info': mock_info, - 'cmd.run_stdout': mock_stdout}): - self.assertTrue(groupadd.deluser('test', 'root')) - groupadd.__salt__['cmd.retcode'].assert_called_once_with(os_version['cmd'], python_shell=False) + with patch.dict(groupadd.__grains__, os_version["grains"]): + with patch.dict( + groupadd.__salt__, + { + "cmd.retcode": mock_retcode, + "group.info": mock_info, + "cmd.run_stdout": mock_stdout, + }, + ): + self.assertTrue(groupadd.deluser("test", "root")) + groupadd.__salt__["cmd.retcode"].assert_called_once_with( + os_version["cmd"], python_shell=False + ) # 'deluser' function tests: 1 def test_members(self): - ''' + """ Tests if members of the group, get replaced with a provided list. - ''' + """ os_version_list = [ - {'grains': {'kernel': 'Linux', 'os_family': 'RedHat', 'osmajorrelease': '5'}, - 'cmd': ['gpasswd', '-M', 'foo', 'test']}, - - {'grains': {'kernel': 'Linux', 'os_family': 'Suse', 'osmajorrelease': '11'}, - 'cmd': ['groupmod', '-A', 'foo', 'test']}, - - {'grains': {'kernel': 'Linux'}, - 'cmd': ['gpasswd', '--members', 'foo', 'test']}, - - {'grains': {'kernel': 'OpenBSD'}, - 'cmd': ['usermod', '-G', 'test', 'foo']}, + { + "grains": { + "kernel": "Linux", + "os_family": "RedHat", + "osmajorrelease": "5", + }, + "cmd": ["gpasswd", "-M", "foo", "test"], + }, + { + "grains": { + "kernel": "Linux", + "os_family": "Suse", + "osmajorrelease": "11", + }, + "cmd": ["groupmod", "-A", "foo", "test"], + }, + { + "grains": {"kernel": "Linux"}, + "cmd": ["gpasswd", "--members", "foo", "test"], + }, + {"grains": {"kernel": "OpenBSD"}, "cmd": ["usermod", "-G", "test", "foo"]}, ] for os_version in os_version_list: - mock_ret = MagicMock(return_value={'retcode': 0}) - mock_stdout = MagicMock(return_value={'cmd.run_stdout': 1}) - mock_info = MagicMock(return_value={'passwd': '*', - 'gid': 0, - 'name': 'test', - 'members': ['root']}) + mock_ret = MagicMock(return_value={"retcode": 0}) + mock_stdout = MagicMock(return_value={"cmd.run_stdout": 1}) + mock_info = MagicMock( + return_value={ + "passwd": "*", + "gid": 0, + "name": "test", + "members": ["root"], + } + ) mock = MagicMock(return_value=True) - with patch.dict(groupadd.__grains__, os_version['grains']): - with patch.dict(groupadd.__salt__, {'cmd.retcode': mock_ret, - 'group.info': mock_info, - 'cmd.run_stdout': mock_stdout, - 'cmd.run': mock}): - self.assertFalse(groupadd.members('test', 'foo')) - groupadd.__salt__['cmd.retcode'].assert_called_once_with(os_version['cmd'], python_shell=False) + with patch.dict(groupadd.__grains__, os_version["grains"]): + with patch.dict( + groupadd.__salt__, + { + "cmd.retcode": mock_ret, + "group.info": mock_info, + "cmd.run_stdout": mock_stdout, + "cmd.run": mock, + }, + ): + self.assertFalse(groupadd.members("test", "foo")) + groupadd.__salt__["cmd.retcode"].assert_called_once_with( + os_version["cmd"], python_shell=False + ) diff --git a/tests/unit/modules/test_grub_legacy.py b/tests/unit/modules/test_grub_legacy.py index 26365ee241b..1a9b268c3b2 100644 --- a/tests/unit/modules/test_grub_legacy.py +++ b/tests/unit/modules/test_grub_legacy.py @@ -1,53 +1,53 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import errno -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - mock_open, - MagicMock, - patch, -) +import errno # Import Salt Libs import salt.modules.grub_legacy as grub_legacy import salt.utils.stringutils from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class GrublegacyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.grub_legacy - ''' + """ + def setup_loader_modules(self): return {grub_legacy: {}} def test_version(self): - ''' + """ Test for Return server version from grub --version - ''' - mock = MagicMock(return_value='out') - with patch.dict(grub_legacy.__salt__, {'cmd.run': mock}): - self.assertEqual(grub_legacy.version(), 'out') + """ + mock = MagicMock(return_value="out") + with patch.dict(grub_legacy.__salt__, {"cmd.run": mock}): + self.assertEqual(grub_legacy.version(), "out") def test_conf(self): - ''' + """ Test for Parse GRUB conf file - ''' - file_data = IOError(errno.EACCES, 'Permission denied') - with patch('salt.utils.files.fopen', mock_open(read_data=file_data)), \ - patch.object(grub_legacy, '_detect_conf', return_value='A'): + """ + file_data = IOError(errno.EACCES, "Permission denied") + with patch( + "salt.utils.files.fopen", mock_open(read_data=file_data) + ), patch.object(grub_legacy, "_detect_conf", return_value="A"): self.assertRaises(CommandExecutionError, grub_legacy.conf) - file_data = salt.utils.stringutils.to_str('\n'.join(['#', 'A B C D,E,F G H'])) - with patch('salt.utils.files.fopen', mock_open(read_data=file_data)), \ - patch.object(grub_legacy, '_detect_conf', return_value='A'): + file_data = salt.utils.stringutils.to_str("\n".join(["#", "A B C D,E,F G H"])) + with patch( + "salt.utils.files.fopen", mock_open(read_data=file_data) + ), patch.object(grub_legacy, "_detect_conf", return_value="A"): conf = grub_legacy.conf() - assert conf == {'A': 'B C D,E,F G H', 'stanzas': []}, conf + assert conf == {"A": "B C D,E,F G H", "stanzas": []}, conf diff --git a/tests/unit/modules/test_guestfs.py b/tests/unit/modules/test_guestfs.py index c5f29d64a5b..9cbebf81404 100644 --- a/tests/unit/modules/test_guestfs.py +++ b/tests/unit/modules/test_guestfs.py @@ -1,67 +1,107 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.guestfs as guestfs +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase + class GuestfsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.guestfs - ''' + """ + def setup_loader_modules(self): return {guestfs: {}} # 'mount' function tests: 1 def test_mount(self): - ''' + """ Test if it mounts an image - ''' + """ # Test case with non-existing mount folder - run_mock = MagicMock(return_value='') - with patch('os.path.join', MagicMock(return_value='/tmp/guest/fedora.qcow')), \ - patch('os.path.isdir', MagicMock(return_value=False)), \ - patch('os.makedirs', MagicMock()) as makedirs_mock, \ - patch('os.listdir', MagicMock(return_value=False)), \ - patch.dict(guestfs.__salt__, {'cmd.run': run_mock}): - self.assertTrue(guestfs.mount('/srv/images/fedora.qcow')) - run_mock.assert_called_once_with('guestmount -i -a /srv/images/fedora.qcow --rw /tmp/guest/fedora.qcow', - python_shell=False) + run_mock = MagicMock(return_value="") + with patch( + "os.path.join", MagicMock(return_value="/tmp/guest/fedora.qcow") + ), patch("os.path.isdir", MagicMock(return_value=False)), patch( + "os.makedirs", MagicMock() + ) as makedirs_mock, patch( + "os.listdir", MagicMock(return_value=False) + ), patch.dict( + guestfs.__salt__, {"cmd.run": run_mock} + ): + self.assertTrue(guestfs.mount("/srv/images/fedora.qcow")) + run_mock.assert_called_once_with( + "guestmount -i -a /srv/images/fedora.qcow --rw /tmp/guest/fedora.qcow", + python_shell=False, + ) makedirs_mock.assert_called_once() # Test case with existing but empty mount folder run_mock.reset_mock() - with patch('os.path.join', MagicMock(return_value='/tmp/guest/fedora.qcow')), \ - patch('os.path.isdir', MagicMock(return_value=True)), \ - patch('os.makedirs', MagicMock()) as makedirs_mock, \ - patch('os.listdir', MagicMock(return_value=False)), \ - patch.dict(guestfs.__salt__, {'cmd.run': run_mock}): - self.assertTrue(guestfs.mount('/srv/images/fedora.qcow')) - run_mock.assert_called_once_with('guestmount -i -a /srv/images/fedora.qcow --rw /tmp/guest/fedora.qcow', - python_shell=False) + with patch( + "os.path.join", MagicMock(return_value="/tmp/guest/fedora.qcow") + ), patch("os.path.isdir", MagicMock(return_value=True)), patch( + "os.makedirs", MagicMock() + ) as makedirs_mock, patch( + "os.listdir", MagicMock(return_value=False) + ), patch.dict( + guestfs.__salt__, {"cmd.run": run_mock} + ): + self.assertTrue(guestfs.mount("/srv/images/fedora.qcow")) + run_mock.assert_called_once_with( + "guestmount -i -a /srv/images/fedora.qcow --rw /tmp/guest/fedora.qcow", + python_shell=False, + ) makedirs_mock.assert_not_called() # Test case with existing but not empty mount folder run_mock.reset_mock() - with patch('os.path.join', MagicMock(side_effect=['/tmp/guest/fedora.qcow', '/tmp/guest/fedora.qcowabc'])), \ - patch('os.path.isdir', MagicMock(side_effect=[True, False])), \ - patch('os.makedirs', MagicMock()) as makedirs_mock, \ - patch('os.listdir', MagicMock(side_effect=[True, False])), \ - patch.dict(guestfs.__salt__, {'cmd.run': run_mock}): - self.assertTrue(guestfs.mount('/srv/images/fedora.qcow')) - run_mock.assert_called_once_with('guestmount -i -a /srv/images/fedora.qcow --rw /tmp/guest/fedora.qcowabc', - python_shell=False) + with patch( + "os.path.join", + MagicMock( + side_effect=["/tmp/guest/fedora.qcow", "/tmp/guest/fedora.qcowabc"] + ), + ), patch("os.path.isdir", MagicMock(side_effect=[True, False])), patch( + "os.makedirs", MagicMock() + ) as makedirs_mock, patch( + "os.listdir", MagicMock(side_effect=[True, False]) + ), patch.dict( + guestfs.__salt__, {"cmd.run": run_mock} + ): + self.assertTrue(guestfs.mount("/srv/images/fedora.qcow")) + run_mock.assert_called_once_with( + "guestmount -i -a /srv/images/fedora.qcow --rw /tmp/guest/fedora.qcowabc", + python_shell=False, + ) makedirs_mock.assert_called_once() + + def test_umount(self): + """ + Test the guestfs.unmount function + """ + run_mock = MagicMock(side_effect=["", "lsof output line", ""]) + with patch.dict(guestfs.__salt__, {"cmd.run": run_mock}): + guestfs.umount("/tmp/mnt/opensuse.qcow", disk="/path/to/opensuse.qcow") + expected = [ + call("guestunmount -q /tmp/mnt/opensuse.qcow"), + call("lsof /path/to/opensuse.qcow"), + call("lsof /path/to/opensuse.qcow"), + ] + self.assertEqual(expected, run_mock.call_args_list) + + # Test without the disk image path + run_mock = MagicMock(side_effect=[""]) + with patch.dict(guestfs.__salt__, {"cmd.run": run_mock}): + guestfs.umount("/tmp/mnt/opensuse.qcow") + expected = [call("guestunmount -q /tmp/mnt/opensuse.qcow")] + self.assertEqual(expected, run_mock.call_args_list) diff --git a/tests/unit/modules/test_hadoop.py b/tests/unit/modules/test_hadoop.py index 9f693d93614..359546664fe 100644 --- a/tests/unit/modules/test_hadoop.py +++ b/tests/unit/modules/test_hadoop.py @@ -1,68 +1,68 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.hadoop as hadoop +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class HadoopTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.hadoop - ''' + """ + def setup_loader_modules(self): return {hadoop: {}} def test_version(self): - ''' + """ Test for Return version from hadoop version - ''' + """ mock = MagicMock(return_value="A \nB \n") - with patch.dict(hadoop.__salt__, {'cmd.run': mock}): - self.assertEqual(hadoop.version(), 'B') + with patch.dict(hadoop.__salt__, {"cmd.run": mock}): + self.assertEqual(hadoop.version(), "B") def test_dfs(self): - ''' + """ Test for Execute a command on DFS - ''' - with patch.object(hadoop, '_hadoop_cmd', return_value='A'): - self.assertEqual(hadoop.dfs('command'), 'A') + """ + with patch.object(hadoop, "_hadoop_cmd", return_value="A"): + self.assertEqual(hadoop.dfs("command"), "A") - self.assertEqual(hadoop.dfs(), 'Error: command must be provided') + self.assertEqual(hadoop.dfs(), "Error: command must be provided") def test_dfs_present(self): - ''' + """ Test for Check if a file or directory is present on the distributed FS. - ''' - with patch.object(hadoop, '_hadoop_cmd', - side_effect=['No such file or directory', 'A']): - self.assertFalse(hadoop.dfs_present('path')) - self.assertTrue(hadoop.dfs_present('path')) + """ + with patch.object( + hadoop, "_hadoop_cmd", side_effect=["No such file or directory", "A"] + ): + self.assertFalse(hadoop.dfs_present("path")) + self.assertTrue(hadoop.dfs_present("path")) def test_dfs_absent(self): - ''' + """ Test for Check if a file or directory is absent on the distributed FS. - ''' - with patch.object(hadoop, '_hadoop_cmd', - side_effect=['No such file or directory', 'A']): - self.assertTrue(hadoop.dfs_absent('path')) - self.assertFalse(hadoop.dfs_absent('path')) + """ + with patch.object( + hadoop, "_hadoop_cmd", side_effect=["No such file or directory", "A"] + ): + self.assertTrue(hadoop.dfs_absent("path")) + self.assertFalse(hadoop.dfs_absent("path")) def test_namenode_format(self): - ''' + """ Test for Format a name node - ''' - with patch.object(hadoop, '_hadoop_cmd', return_value='A'): - self.assertEqual(hadoop.namenode_format('force'), 'A') + """ + with patch.object(hadoop, "_hadoop_cmd", return_value="A"): + self.assertEqual(hadoop.namenode_format("force"), "A") diff --git a/tests/unit/modules/test_haproxyconn.py b/tests/unit/modules/test_haproxyconn.py index 678fc5f971b..c40b00e45ac 100644 --- a/tests/unit/modules/test_haproxyconn.py +++ b/tests/unit/modules/test_haproxyconn.py @@ -1,214 +1,205 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +# Import Salt Libs +import salt.modules.haproxyconn as haproxyconn + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -# Import Salt Libs -import salt.modules.haproxyconn as haproxyconn - class Mockcmds(object): - ''' + """ Mock of cmds - ''' + """ + def __init__(self): self.backend = None self.server = None self.weight = None def listServers(self, backend): - ''' + """ Mock of listServers method - ''' + """ self.backend = backend - return 'Name: server01 Status: UP Weight: 1 bIn: 22 bOut: 12\n' \ - 'Name: server02 Status: MAINT Weight: 2 bIn: 0 bOut: 0' + return ( + "Name: server01 Status: UP Weight: 1 bIn: 22 bOut: 12\n" + "Name: server02 Status: MAINT Weight: 2 bIn: 0 bOut: 0" + ) def enableServer(self, server, backend): - ''' + """ Mock of enableServer method - ''' + """ self.backend = backend self.server = server - return 'server enabled' + return "server enabled" def disableServer(self, server, backend): - ''' + """ Mock of disableServer method - ''' + """ self.backend = backend self.server = server - return 'server disabled' + return "server disabled" def getWeight(self, server, backend, weight=0): - ''' + """ Mock of getWeight method - ''' + """ self.backend = backend self.server = server self.weight = weight - return 'server weight' + return "server weight" @staticmethod def showFrontends(): - ''' + """ Mock of showFrontends method - ''' - return 'frontend-alpha\n' \ - 'frontend-beta\n' \ - 'frontend-gamma' + """ + return "frontend-alpha\n" "frontend-beta\n" "frontend-gamma" @staticmethod def showBackends(): - ''' + """ Mock of showBackends method - ''' - return 'backend-alpha\n' \ - 'backend-beta\n' \ - 'backend-gamma' + """ + return "backend-alpha\n" "backend-beta\n" "backend-gamma" class Mockhaproxy(object): - ''' + """ Mock of haproxy - ''' + """ + def __init__(self): self.cmds = Mockcmds() class MockHaConn(object): - ''' + """ Mock of HaConn - ''' + """ + def __init__(self, socket=None): self.ha_cmd = None def sendCmd(self, ha_cmd, objectify=False): - ''' + """ Mock of sendCmd method - ''' + """ self.ha_cmd = ha_cmd self.objectify = objectify return ha_cmd class HaproxyConnTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.haproxyconn - ''' + """ + def setup_loader_modules(self): - return {haproxyconn: {'haproxy': Mockhaproxy(), '_get_conn': MockHaConn}} + return {haproxyconn: {"haproxy": Mockhaproxy(), "_get_conn": MockHaConn}} # 'list_servers' function tests: 1 def test_list_servers(self): - ''' + """ Test list_servers - ''' - self.assertTrue(haproxyconn.list_servers('mysql')) + """ + self.assertTrue(haproxyconn.list_servers("mysql")) # 'enable_server' function tests: 1 def test_enable_server(self): - ''' + """ Test enable_server - ''' - self.assertTrue(haproxyconn.enable_server('web1.salt.com', 'www')) + """ + self.assertTrue(haproxyconn.enable_server("web1.salt.com", "www")) # 'disable_server' function tests: 1 def test_disable_server(self): - ''' + """ Test disable_server - ''' - self.assertTrue(haproxyconn.disable_server('db1.salt.com', 'mysql')) + """ + self.assertTrue(haproxyconn.disable_server("db1.salt.com", "mysql")) # 'get_weight' function tests: 1 def test_get_weight(self): - ''' + """ Test get the weight of a server - ''' - self.assertTrue(haproxyconn.get_weight('db1.salt.com', 'mysql')) + """ + self.assertTrue(haproxyconn.get_weight("db1.salt.com", "mysql")) # 'set_weight' function tests: 1 def test_set_weight(self): - ''' + """ Test setting the weight of a given server - ''' - self.assertTrue(haproxyconn.set_weight('db1.salt.com', 'mysql', - weight=11)) + """ + self.assertTrue(haproxyconn.set_weight("db1.salt.com", "mysql", weight=11)) # 'show_frontends' function tests: 1 def test_show_frontends(self): - ''' + """ Test print all frontends received from the HAProxy socket - ''' + """ self.assertTrue(haproxyconn.show_frontends()) def test_list_frontends(self): - ''' + """ Test listing all frontends - ''' + """ self.assertEqual( sorted(haproxyconn.list_frontends()), - sorted(['frontend-alpha', 'frontend-beta', 'frontend-gamma']) + sorted(["frontend-alpha", "frontend-beta", "frontend-gamma"]), ) # 'show_backends' function tests: 1 def test_show_backends(self): - ''' + """ Test print all backends received from the HAProxy socket - ''' + """ self.assertTrue(haproxyconn.show_backends()) def test_list_backends(self): - ''' + """ Test listing of all backends - ''' + """ self.assertEqual( sorted(haproxyconn.list_backends()), - sorted(['backend-alpha', 'backend-beta', 'backend-gamma']) + sorted(["backend-alpha", "backend-beta", "backend-gamma"]), ) def test_get_backend(self): - ''' + """ Test get_backend and compare returned value - ''' + """ expected_data = { - 'server01': { - 'status': 'UP', - 'weight': 1, - 'bin': 22, - 'bout': 12 - }, - 'server02': { - 'status': 'MAINT', - 'weight': 2, - 'bin': 0, - 'bout': 0 - } + "server01": {"status": "UP", "weight": 1, "bin": 22, "bout": 12}, + "server02": {"status": "MAINT", "weight": 2, "bin": 0, "bout": 0}, } - self.assertDictEqual(haproxyconn.get_backend('test'), expected_data) + self.assertDictEqual(haproxyconn.get_backend("test"), expected_data) def test_wait_state_true(self): - ''' + """ Test a successful wait for state - ''' - self.assertTrue(haproxyconn.wait_state('test', 'server01')) + """ + self.assertTrue(haproxyconn.wait_state("test", "server01")) def test_wait_state_false(self): - ''' + """ Test a failed wait for state, with a timeout of 0 - ''' - self.assertFalse(haproxyconn.wait_state('test', 'server02', 'up', 0)) + """ + self.assertFalse(haproxyconn.wait_state("test", "server02", "up", 0)) diff --git a/tests/unit/modules/test_hashutil.py b/tests/unit/modules/test_hashutil.py index 17955cd0d8c..4c17e2909cb 100644 --- a/tests/unit/modules/test_hashutil.py +++ b/tests/unit/modules/test_hashutil.py @@ -2,65 +2,70 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import salt testing libs -from tests.support.case import ModuleCase -from tests.support.mixins import RUNTIME_VARS +import os # Import Salt libs import salt.config import salt.loader +# Import salt testing libs +from tests.support.case import ModuleCase +from tests.support.mixins import RUNTIME_VARS + class HashutilTestCase(ModuleCase): - the_string = 'get salted' - the_string_base64 = 'Z2V0IHNhbHRlZA==\n' - the_string_md5 = '2aacf29e92feaf528fb738bcf9d647ac' - the_string_sha256 = 'd49859ccbc854fa68d800b5734efc70d72383e6479d545468bc300263164ff33' - the_string_sha512 = 'a8c174a7941c64a068e686812a2fafd7624c840fde800f5965fbeca675f2f6e37061ffe41e17728c919bdea290eab7a21e13c04ae71661955a87f2e0e04bb045' - the_string_hmac = 'eBWf9bstXg+NiP5AOwppB5HMvZiYMPzEM9W5YMm/AmQ=' - the_string_hmac_compute = '78159ff5bb2d5e0f8d88fe403b0a690791ccbd989830fcc433d5b960c9bf0264' - the_string_github = 'sha1=b06aa56bdf4935eec82c4e53e83ed03f03fdb32d' + the_string = "get salted" + the_string_base64 = "Z2V0IHNhbHRlZA==\n" + the_string_md5 = "2aacf29e92feaf528fb738bcf9d647ac" + the_string_sha256 = ( + "d49859ccbc854fa68d800b5734efc70d72383e6479d545468bc300263164ff33" + ) + the_string_sha512 = "a8c174a7941c64a068e686812a2fafd7624c840fde800f5965fbeca675f2f6e37061ffe41e17728c919bdea290eab7a21e13c04ae71661955a87f2e0e04bb045" + the_string_hmac = "eBWf9bstXg+NiP5AOwppB5HMvZiYMPzEM9W5YMm/AmQ=" + the_string_hmac_compute = ( + "78159ff5bb2d5e0f8d88fe403b0a690791ccbd989830fcc433d5b960c9bf0264" + ) + the_string_github = "sha1=b06aa56bdf4935eec82c4e53e83ed03f03fdb32d" def setUp(self): - minion_opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) - self.hashutil = salt.loader.raw_mod(minion_opts, 'hashutil', None) + minion_opts = salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion") + ) + self.hashutil = salt.loader.raw_mod(minion_opts, "hashutil", None) def test_base64_encodestring(self): - ret = self.hashutil['hashutil.base64_encodestring'](self.the_string) + ret = self.hashutil["hashutil.base64_encodestring"](self.the_string) self.assertEqual(ret, self.the_string_base64) def test_base64_decodestring(self): - ret = self.hashutil['hashutil.base64_decodestring'](self.the_string_base64) + ret = self.hashutil["hashutil.base64_decodestring"](self.the_string_base64) self.assertEqual(ret, self.the_string) def test_md5_digest(self): - ret = self.hashutil['hashutil.md5_digest'](self.the_string) + ret = self.hashutil["hashutil.md5_digest"](self.the_string) self.assertEqual(ret, self.the_string_md5) def test_sha256_digest(self): - ret = self.hashutil['hashutil.sha256_digest'](self.the_string) + ret = self.hashutil["hashutil.sha256_digest"](self.the_string) self.assertEqual(ret, self.the_string_sha256) def test_sha512_digest(self): - ret = self.hashutil['hashutil.sha512_digest'](self.the_string) + ret = self.hashutil["hashutil.sha512_digest"](self.the_string) self.assertEqual(ret, self.the_string_sha512) def test_hmac_signature(self): - ret = self.hashutil['hashutil.hmac_signature']( - self.the_string, - 'shared secret', - self.the_string_hmac) + ret = self.hashutil["hashutil.hmac_signature"]( + self.the_string, "shared secret", self.the_string_hmac + ) self.assertTrue(ret) def test_hmac_compute(self): - ret = self.hashutil['hashutil.hmac_compute'](self.the_string, 'shared secret') + ret = self.hashutil["hashutil.hmac_compute"](self.the_string, "shared secret") self.assertEqual(ret, self.the_string_hmac_compute) def test_github_signature(self): - ret = self.hashutil['hashutil.github_signature']( - self.the_string, - 'shared secret', - self.the_string_github) + ret = self.hashutil["hashutil.github_signature"]( + self.the_string, "shared secret", self.the_string_github + ) self.assertTrue(ret) diff --git a/tests/unit/modules/test_heat.py b/tests/unit/modules/test_heat.py index 7516381eda5..60eb6ccdebd 100644 --- a/tests/unit/modules/test_heat.py +++ b/tests/unit/modules/test_heat.py @@ -1,53 +1,57 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.runtests import RUNTIME_VARS -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.file as file_ +import salt.modules.heat as heat +import salt.modules.win_file as win_file # Import Salt Libs import salt.utils.platform -import salt.modules.win_file -import salt.modules.heat as heat -import salt.modules.file as file_ -import salt.modules.win_file as win_file import salt.utils.win_dacl as dacl +# Import Salt Testing Libs +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 + class MockStacks(object): - ''' + """ Mock stacks.StackManager - ''' + """ + def validate(self, **kwargs): - ''' + """ Mock of stacks.StackManager.validate method - ''' + """ self.mock_val_ret = MagicMock() - self.mock_val_ret.json.return_value = {'result': 'mocked response'} + self.mock_val_ret.json.return_value = {"result": "mocked response"} self.mock_validate = MagicMock() self.mock_validate.post.return_value = self.mock_val_ret return self.mock_validate def create(self, **kwargs): self.mock_create_ret = MagicMock() - self.mock_create_ret.json.return_value = {'result': 'mocked create', - 'fields': kwargs} + self.mock_create_ret.json.return_value = { + "result": "mocked create", + "fields": kwargs, + } self.mock_create = MagicMock() self.mock_create.post.return_value = self.mock_create_ret return self.mock_create def update(self, name, **kwargs): self.mock_update_ret = MagicMock() - self.mock_update_ret.json.return_value = {'result': 'mocked update', - 'fields': kwargs, 'name': name} + self.mock_update_ret.json.return_value = { + "result": "mocked update", + "fields": kwargs, + "name": name, + } self.mock_update = MagicMock() self.mock_update.post.return_value = self.mock_update_ret return self.mock_update @@ -57,145 +61,196 @@ class MockClient(object): """ Mock of Client class """ + def __init__(self, profile=None, **conn_args): self.stacks = MockStacks() class HeatTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.heat - ''' + """ def setup_loader_modules(self): return { - heat: { - '_auth': MockClient, - }, + heat: {"_auth": MockClient}, file_: { - '__opts__': {'hash_type': 'sha256', - 'cachedir': os.path.join(RUNTIME_VARS.TMP, - 'rootdir', 'cache'), - 'test': False}, - '__salt__': {'config.option': - MagicMock(return_value={'obfuscate_templates': - False}), - 'config.backup_mode': - MagicMock(return_value=False)} + "__opts__": { + "hash_type": "sha256", + "cachedir": os.path.join(RUNTIME_VARS.TMP, "rootdir", "cache"), + "test": False, + }, + "__salt__": { + "config.option": MagicMock( + return_value={"obfuscate_templates": False} + ), + "config.backup_mode": MagicMock(return_value=False), + }, }, win_file: { - '__utils__': {'dacl.check_perms': salt.utils.win_dacl.check_perms} - }, - dacl: {'__opts__': {'test': False}}, + "__utils__": {"dacl.check_perms": salt.utils.win_dacl.check_perms} + }, + dacl: {"__opts__": {"test": False}}, } def setUp(self): - self.patch_check = patch('salt.modules.file.check_perms', file_.check_perms) + self.patch_check = patch("salt.modules.file.check_perms", file_.check_perms) if salt.utils.platform.is_windows(): - self.patch_check = patch('salt.modules.file.check_perms', win_file.check_perms) + self.patch_check = patch( + "salt.modules.file.check_perms", win_file.check_perms + ) def test_heat_create_stack(self): - ''' + """ Test salt.modules.heat.create_stack method - ''' - patch_file = patch.dict(heat.__salt__, - {'file.get_managed': file_.get_managed, - 'file.manage_file': file_.manage_file, }, - ) + """ + patch_file = patch.dict( + heat.__salt__, + { + "file.get_managed": file_.get_managed, + "file.manage_file": file_.manage_file, + }, + ) with patch_file, self.patch_check: - ret = heat.create_stack(name='mystack', - profile='openstack1', - template_file=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-template.yml')) - assert ret == {'result': True, 'comment': "Created stack 'mystack'."} + ret = heat.create_stack( + name="mystack", + profile="openstack1", + template_file=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-template.yml" + ), + ) + assert ret == {"result": True, "comment": "Created stack 'mystack'."} def test_heat_create_stack_environment(self): - ''' + """ Test salt.modules.heat.create_stack method with environment set - ''' - patch_file = patch.dict('salt.modules.heat.__salt__', - {'file.get_managed': file_.get_managed, - 'file.manage_file': file_.manage_file, }, - ) + """ + patch_file = patch.dict( + "salt.modules.heat.__salt__", + { + "file.get_managed": file_.get_managed, + "file.manage_file": file_.manage_file, + }, + ) with patch_file, self.patch_check: - ret = heat.create_stack(name='mystack', - profile='openstack1', - environment=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-env.yml'), - template_file=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-template.yml')) - assert ret == {'result': True, 'comment': "Created stack 'mystack'."} + ret = heat.create_stack( + name="mystack", + profile="openstack1", + environment=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-env.yml" + ), + template_file=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-template.yml" + ), + ) + assert ret == {"result": True, "comment": "Created stack 'mystack'."} def test_heat_create_stack_environment_err(self): - ''' + """ Test salt.modules.heat.create_stack method with environment set and there is an error reading the environment file - ''' - patch_file = patch.dict('salt.modules.heat.__salt__', - {'file.get_managed': file_.get_managed, - 'file.manage_file': MagicMock(side_effect=[{'result': True}, {'result': False}]), }, - ) - patch_template = patch('salt.modules.heat._parse_template', MagicMock(return_value=True)) - env_file = os.path.join(RUNTIME_VARS.BASE_FILES, 'templates', 'heat-env.yml') + """ + patch_file = patch.dict( + "salt.modules.heat.__salt__", + { + "file.get_managed": file_.get_managed, + "file.manage_file": MagicMock( + side_effect=[{"result": True}, {"result": False}] + ), + }, + ) + patch_template = patch( + "salt.modules.heat._parse_template", MagicMock(return_value=True) + ) + env_file = os.path.join(RUNTIME_VARS.BASE_FILES, "templates", "heat-env.yml") with patch_file, patch_template, self.patch_check: - ret = heat.create_stack(name='mystack', - profile='openstack1', - environment=env_file, - template_file=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-template.yml')) - assert ret == {'result': False, 'comment': 'Can not open environment: {0}, '.format(env_file)} + ret = heat.create_stack( + name="mystack", + profile="openstack1", + environment=env_file, + template_file=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-template.yml" + ), + ) + assert ret == { + "result": False, + "comment": "Can not open environment: {0}, ".format(env_file), + } def test_heat_update_stack(self): - ''' + """ Test salt.modules.heat.update_method method - ''' - patch_file = patch.dict(heat.__salt__, - {'file.get_managed': file_.get_managed, - 'file.manage_file': file_.manage_file, }, - ) + """ + patch_file = patch.dict( + heat.__salt__, + { + "file.get_managed": file_.get_managed, + "file.manage_file": file_.manage_file, + }, + ) with patch_file, self.patch_check: - ret = heat.update_stack(name='mystack', - profile='openstack1', - template_file=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-template.yml')) - assert ret == {'result': True, 'comment': ("Updated stack 'mystack'.",)} + ret = heat.update_stack( + name="mystack", + profile="openstack1", + template_file=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-template.yml" + ), + ) + assert ret == {"result": True, "comment": ("Updated stack 'mystack'.",)} def test_heat_update_stack_env(self): - ''' + """ Test salt.modules.heat.update_method method with environment set - ''' - patch_file = patch.dict(heat.__salt__, - {'file.get_managed': file_.get_managed, - 'file.manage_file': file_.manage_file, }, - ) + """ + patch_file = patch.dict( + heat.__salt__, + { + "file.get_managed": file_.get_managed, + "file.manage_file": file_.manage_file, + }, + ) with patch_file, self.patch_check: - ret = heat.update_stack(name='mystack', - profile='openstack1', - template_file=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-template.yml'), - environment=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-env.yml')) - assert ret == {'result': True, 'comment': ("Updated stack 'mystack'.",)} + ret = heat.update_stack( + name="mystack", + profile="openstack1", + template_file=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-template.yml" + ), + environment=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-env.yml" + ), + ) + assert ret == {"result": True, "comment": ("Updated stack 'mystack'.",)} def test_heat_update_stack_env_err(self): - ''' + """ Test salt.modules.heat.update_method method with environment set and there is an error reading the environment file - ''' - patch_file = patch.dict(heat.__salt__, - {'file.get_managed': file_.get_managed, - 'file.manage_file': - MagicMock(side_effect=[{'result': True}, - {'result': False}]), } - ) + """ + patch_file = patch.dict( + heat.__salt__, + { + "file.get_managed": file_.get_managed, + "file.manage_file": MagicMock( + side_effect=[{"result": True}, {"result": False}] + ), + }, + ) with patch_file, self.patch_check: - ret = heat.update_stack(name='mystack', - profile='openstack1', - template_file=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-template.yml'), - environment=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-env.yml')) - assert ret == {'result': False, - 'comment': 'Error parsing template Template format version not found.'} + ret = heat.update_stack( + name="mystack", + profile="openstack1", + template_file=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-template.yml" + ), + environment=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-env.yml" + ), + ) + assert ret == { + "result": False, + "comment": "Error parsing template Template format version not found.", + } diff --git a/tests/unit/modules/test_helm.py b/tests/unit/modules/test_helm.py new file mode 100644 index 00000000000..ac41128852c --- /dev/null +++ b/tests/unit/modules/test_helm.py @@ -0,0 +1,677 @@ +# -*- coding: utf-8 -*- + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.modules.helm as helm + +# Import Exception +from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase + + +class HelmTestCase(TestCase, LoaderModuleMockMixin): + """ + Test cases for salt.modules.helm + """ + + def setup_loader_modules(self): + return {helm: {}} + + def test__prepare_cmd(self): + self.assertEqual(helm._prepare_cmd(), ("helm",)) + + def test__prepare_cmd_binary(self): + self.assertEqual(helm._prepare_cmd(binary="binary"), ("binary",)) + + def test__prepare_cmd_commands(self): + self.assertEqual( + helm._prepare_cmd(commands=["com1", "com2"]), ("helm", "com1", "com2",) + ) + + def test__prepare_cmd_flags(self): + self.assertEqual( + helm._prepare_cmd(flags=["flag1", "--flag2"]), + ("helm", "--flag1", "--flag2",), + ) + + def test__prepare_cmd_kvflags(self): + result_tuple = helm._prepare_cmd( + kvflags={"kflag1": "vflag1", "--kflag2": "vflag2"} + ) + tuple_valide_1 = ( + "helm", + "--kflag1", + "vflag1", + "--kflag2", + "vflag2", + ) + tuple_valide_2 = ( + "helm", + "--kflag2", + "vflag2", + "--kflag1", + "vflag1", + ) + + # + self.assertEqual( + True, result_tuple == tuple_valide_1 or result_tuple == tuple_valide_2 + ) + + def test__exec_cmd(self): + cmd_prepare = helm._prepare_cmd() + cmd_prepare_str = " ".join(cmd_prepare) + cmd_return = { + "stdout": "succes", + "stderr": "", + "retcode": 0, + } + result = cmd_return + result.update({"cmd": cmd_prepare_str}) + with patch.dict( + helm.__salt__, + { # pylint: disable=no-member + "cmd.run_all": MagicMock(return_value=cmd_return) + }, + ): + self.assertEqual(helm._exec_cmd(), result) + + def test__exec_true_return_valid(self): + _exec_cmd_return = {"retcode": 0} + with patch( + "salt.modules.helm._exec_cmd", MagicMock(return_value=_exec_cmd_return) + ): + self.assertEqual(True, helm._exec_true_return()) + + def test__exec_true_return_not_valid(self): + _exec_cmd_return = {"retcode": -1, "stderr": "test"} + with patch( + "salt.modules.helm._exec_cmd", MagicMock(return_value=_exec_cmd_return) + ): + self.assertEqual("test", helm._exec_true_return()) + + def test__exec_string_return_valid(self): + _exec_cmd_return = {"retcode": 0, "stdout": "test"} + with patch( + "salt.modules.helm._exec_cmd", MagicMock(return_value=_exec_cmd_return) + ): + self.assertEqual("test", helm._exec_string_return()) + + def test__exec_string_return_not_valid(self): + _exec_cmd_return = {"retcode": -1, "stderr": "test"} + with patch( + "salt.modules.helm._exec_cmd", MagicMock(return_value=_exec_cmd_return) + ): + self.assertEqual("test", helm._exec_string_return()) + + def test__exec_dict_return_valide(self): + _exec_cmd_return = {"retcode": 0, "stdout": '{"test": true}'} + with patch( + "salt.modules.helm._exec_cmd", MagicMock(return_value=_exec_cmd_return) + ): + self.assertEqual({"test": True}, helm._exec_dict_return()) + + def test__exec_dict_return_valide_no_json(self): + _exec_cmd_return = {"retcode": 0, "stdout": '{"test": true}'} + with patch( + "salt.modules.helm._exec_cmd", MagicMock(return_value=_exec_cmd_return) + ): + self.assertEqual( + '{"test": true}', helm._exec_dict_return(kvflags={"output": "table"}) + ) + + def test__exec_dict_return_not_valid(self): + _exec_cmd_return = {"retcode": -1, "stderr": "test"} + with patch( + "salt.modules.helm._exec_cmd", MagicMock(return_value=_exec_cmd_return) + ): + self.assertEqual("test", helm._exec_dict_return()) + + def test_completion(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.completion("bash")) + self.assertEqual( + [call(commands=["completion", "bash"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_create(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.create("name")) + self.assertEqual( + [call(commands=["create", "name"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_dependency_build(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.dependency_build("chart")) + self.assertEqual( + [ + call( + commands=["dependency", "build", "chart"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_dependency_list(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.dependency_list("chart")) + self.assertEqual( + [ + call( + commands=["dependency", "list", "chart"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_dependency_update(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.dependency_update("chart")) + self.assertEqual( + [ + call( + commands=["dependency", "update", "chart"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_env(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.env()) + self.assertEqual( + [call(commands=["env"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_get_all(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.get_all("release")) + self.assertEqual( + [call(commands=["get", "all", "release"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_get_hooks(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.get_hooks("release")) + self.assertEqual( + [call(commands=["get", "hooks", "release"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_get_manifest(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.get_manifest("release")) + self.assertEqual( + [ + call( + commands=["get", "manifest", "release"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_get_notes(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.get_notes("release")) + self.assertEqual( + [call(commands=["get", "notes", "release"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_get_values(self): + magic_mock = MagicMock(return_value={"test": True}) + with patch("salt.modules.helm._exec_dict_return", magic_mock): + self.assertEqual({"test": True}, helm.get_values("release")) + self.assertEqual( + [call(commands=["get", "values", "release"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_help_(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.help_("command")) + self.assertEqual( + [call(commands=["help", "command"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_history(self): + magic_mock = MagicMock(return_value={"test": True}) + with patch("salt.modules.helm._exec_dict_return", magic_mock): + self.assertEqual({"test": True}, helm.history("release")) + self.assertEqual( + [call(commands=["history", "release"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_install(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.install("release", "chart")) + self.assertEqual( + [ + call( + commands=["install", "release", "chart"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_lint(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.lint("path")) + self.assertEqual( + [call(commands=["lint", "path"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_list_(self): + magic_mock = MagicMock(return_value={"test": True}) + with patch("salt.modules.helm._exec_dict_return", magic_mock): + self.assertEqual({"test": True}, helm.list_()) + self.assertEqual( + [call(commands=["list"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_package(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.package("chart")) + self.assertEqual( + [call(commands=["package", "chart"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_plugin_install(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.plugin_install("path")) + self.assertEqual( + [ + call( + commands=["plugin", "install", "path"], flags=None, kvflags=None + ) + ], + magic_mock.mock_calls, + ) + + def test_plugin_list(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.plugin_list()) + self.assertEqual( + [call(commands=["plugin", "list"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_plugin_uninstall(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.plugin_uninstall("plugin")) + self.assertEqual( + [ + call( + commands=["plugin", "uninstall", "plugin"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_plugin_update(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.plugin_update("plugin")) + self.assertEqual( + [ + call( + commands=["plugin", "update", "plugin"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_pull(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.pull("pkg")) + self.assertEqual( + [call(commands=["pull", "pkg"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_repo_add(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.repo_add("name", "url")) + self.assertEqual( + [ + call( + commands=["repo", "add", "name", "url"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_repo_index(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.repo_index("directory")) + self.assertEqual( + [ + call( + commands=["repo", "index", "directory"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_repo_list(self): + magic_mock = MagicMock(return_value={"test": True}) + with patch("salt.modules.helm._exec_dict_return", magic_mock): + self.assertEqual({"test": True}, helm.repo_list()) + self.assertEqual( + [call(commands=["repo", "list"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_repo_remove(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.repo_remove("name")) + self.assertEqual( + [call(commands=["repo", "remove", "name"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_repo_update(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.repo_update()) + self.assertEqual( + [call(commands=["repo", "update"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_repo_manage_present_bad_list(self): + with patch("salt.modules.helm.repo_list", MagicMock(return_value=None)): + with self.assertRaises(CommandExecutionError): + helm.repo_manage(present=["test"]) + + def test_repo_manage_present_bad_format(self): + with patch("salt.modules.helm.repo_list", MagicMock(return_value=None)): + with self.assertRaises(CommandExecutionError): + helm.repo_manage(present=[{"test": True}]) + + def test_repo_manage_present_failed(self): + result_wanted = { + "present": [], + "added": [], + "absent": [], + "removed": [], + "failed": [{"name": "myname", "url": "myurl"}], + } + with patch("salt.modules.helm.repo_list", MagicMock(return_value=None)): + with patch("salt.modules.helm.repo_add", MagicMock(return_value="failed")): + self.assertEqual( + helm.repo_manage(present=[{"name": "myname", "url": "myurl"}]), + result_wanted, + ) + + def test_repo_manage_present_succeed(self): + result_wanted = { + "present": [], + "added": [{"name": "myname", "url": "myurl"}], + "absent": [], + "removed": [], + "failed": [], + } + with patch("salt.modules.helm.repo_list", MagicMock(return_value=None)): + with patch("salt.modules.helm.repo_add", MagicMock(return_value=True)): + self.assertEqual( + helm.repo_manage(present=[{"name": "myname", "url": "myurl"}]), + result_wanted, + ) + + def test_repo_manage_present_already_present(self): + result_wanted = { + "present": [{"name": "myname", "url": "myurl"}], + "added": [], + "absent": [], + "removed": [], + "failed": [], + } + with patch( + "salt.modules.helm.repo_list", + MagicMock(return_value=[{"name": "myname", "url": "myurl"}]), + ): + self.assertEqual( + helm.repo_manage(present=[{"name": "myname", "url": "myurl"}]), + result_wanted, + ) + + def test_repo_manage_prune(self): + result_wanted = { + "present": [], + "added": [], + "absent": [], + "removed": ["myname"], + "failed": [], + } + with patch( + "salt.modules.helm.repo_list", + MagicMock(return_value=[{"name": "myname", "url": "myurl"}]), + ): + with patch("salt.modules.helm.repo_remove", MagicMock(return_value=True)): + self.assertEqual(helm.repo_manage(prune=True), result_wanted) + + def test_repo_manage_absent(self): + result_wanted = { + "present": [], + "added": [], + "absent": ["myname"], + "removed": [], + "failed": [], + } + with patch("salt.modules.helm.repo_list", MagicMock(return_value=None)): + with patch("salt.modules.helm.repo_remove", MagicMock(return_value=False)): + self.assertEqual(helm.repo_manage(absent=["myname"]), result_wanted) + + def test_repo_manage_removed(self): + result_wanted = { + "present": [], + "added": [], + "absent": [], + "removed": ["myname"], + "failed": [], + } + with patch("salt.modules.helm.repo_list", MagicMock(return_value=None)): + with patch("salt.modules.helm.repo_remove", MagicMock(return_value=True)): + self.assertEqual(helm.repo_manage(absent=["myname"]), result_wanted) + + def test_rollback(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.rollback("release", "revision")) + self.assertEqual( + [ + call( + commands=["rollback", "release", "revision"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_search_hub(self): + magic_mock = MagicMock(return_value={"test": True}) + with patch("salt.modules.helm._exec_dict_return", magic_mock): + self.assertEqual({"test": True}, helm.search_hub("keyword")) + self.assertEqual( + [call(commands=["search", "hub", "keyword"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_search_repo(self): + magic_mock = MagicMock(return_value={"test": True}) + with patch("salt.modules.helm._exec_dict_return", magic_mock): + self.assertEqual({"test": True}, helm.search_repo("keyword")) + self.assertEqual( + [ + call( + commands=["search", "repo", "keyword"], flags=None, kvflags=None + ) + ], + magic_mock.mock_calls, + ) + + def test_show_all(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.show_all("chart")) + self.assertEqual( + [call(commands=["show", "all", "chart"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_show_chart(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.show_chart("chart")) + self.assertEqual( + [call(commands=["show", "chart", "chart"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_show_readme(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.show_readme("chart")) + self.assertEqual( + [call(commands=["show", "readme", "chart"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_show_values(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.show_values("chart")) + self.assertEqual( + [call(commands=["show", "values", "chart"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_status(self): + magic_mock = MagicMock(return_value={"test": True}) + with patch("salt.modules.helm._exec_dict_return", magic_mock): + self.assertEqual({"test": True}, helm.status("release")) + self.assertEqual( + [call(commands=["status", "release"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_template(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.template("name", "chart")) + self.assertEqual( + [ + call( + commands=["template", "name", "chart"], flags=None, kvflags=None + ) + ], + magic_mock.mock_calls, + ) + + def test_test(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.test("release")) + self.assertEqual( + [call(commands=["test", "release"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_uninstall(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.uninstall("release")) + self.assertEqual( + [call(commands=["uninstall", "release"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_upgrade(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.upgrade("release", "chart")) + self.assertEqual( + [ + call( + commands=["upgrade", "release", "chart"], + flags=None, + kvflags=None, + ) + ], + magic_mock.mock_calls, + ) + + def test_verify(self): + magic_mock = MagicMock(return_value=True) + with patch("salt.modules.helm._exec_true_return", magic_mock): + self.assertEqual(True, helm.verify("path")) + self.assertEqual( + [call(commands=["verify", "path"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) + + def test_version(self): + magic_mock = MagicMock(return_value="the_return") + with patch("salt.modules.helm._exec_string_return", magic_mock): + self.assertEqual("the_return", helm.version()) + self.assertEqual( + [call(commands=["version"], flags=None, kvflags=None)], + magic_mock.mock_calls, + ) diff --git a/tests/unit/modules/test_hg.py b/tests/unit/modules/test_hg.py index d62090daaa8..f5fb2b2d9d4 100644 --- a/tests/unit/modules/test_hg.py +++ b/tests/unit/modules/test_hg.py @@ -1,110 +1,120 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.hg as hg +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class HgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.hg - ''' + """ + def setup_loader_modules(self): return {hg: {}} def test_revision(self): - ''' + """ Test for Returns the long hash of a given identifier - ''' - mock = MagicMock(side_effect=[{'retcode': 0, 'stdout': 'A'}, - {'retcode': 1, 'stdout': 'A'}]) - with patch.dict(hg.__salt__, {'cmd.run_all': mock}): - self.assertEqual(hg.revision('cwd'), 'A') + """ + mock = MagicMock( + side_effect=[{"retcode": 0, "stdout": "A"}, {"retcode": 1, "stdout": "A"}] + ) + with patch.dict(hg.__salt__, {"cmd.run_all": mock}): + self.assertEqual(hg.revision("cwd"), "A") - self.assertEqual(hg.revision('cwd'), '') + self.assertEqual(hg.revision("cwd"), "") def test_describe(self): - ''' + """ Test for Mimic git describe. - ''' - with patch.dict(hg.__salt__, {'cmd.run_stdout': - MagicMock(return_value='A')}): - with patch.object(hg, 'revision', return_value=False): - self.assertEqual(hg.describe('cwd'), 'A') + """ + with patch.dict(hg.__salt__, {"cmd.run_stdout": MagicMock(return_value="A")}): + with patch.object(hg, "revision", return_value=False): + self.assertEqual(hg.describe("cwd"), "A") def test_archive(self): - ''' + """ Test for Export a tarball from the repository - ''' - with patch.dict(hg.__salt__, {'cmd.run': - MagicMock(return_value='A')}): - self.assertEqual(hg.archive('cwd', 'output'), 'A') + """ + with patch.dict(hg.__salt__, {"cmd.run": MagicMock(return_value="A")}): + self.assertEqual(hg.archive("cwd", "output"), "A") def test_pull(self): - ''' + """ Test for Perform a pull on the given repository - ''' - with patch.dict(hg.__salt__, {'cmd.run_all': - MagicMock(return_value={'retcode': 0, - 'stdout': 'A'})}): - self.assertEqual(hg.pull('cwd'), 'A') + """ + with patch.dict( + hg.__salt__, + {"cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": "A"})}, + ): + self.assertEqual(hg.pull("cwd"), "A") def test_update(self): - ''' + """ Test for Update to a given revision - ''' - with patch.dict(hg.__salt__, {'cmd.run_all': - MagicMock(return_value={'retcode': 0, - 'stdout': 'A'})}): - self.assertEqual(hg.update('cwd', 'rev'), 'A') + """ + with patch.dict( + hg.__salt__, + {"cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": "A"})}, + ): + self.assertEqual(hg.update("cwd", "rev"), "A") def test_clone(self): - ''' + """ Test for Clone a new repository - ''' - with patch.dict(hg.__salt__, {'cmd.run_all': - MagicMock(return_value={'retcode': 0, - 'stdout': 'A'})}): - self.assertEqual(hg.clone('cwd', 'repository'), 'A') + """ + with patch.dict( + hg.__salt__, + {"cmd.run_all": MagicMock(return_value={"retcode": 0, "stdout": "A"})}, + ): + self.assertEqual(hg.clone("cwd", "repository"), "A") def test_status_single(self): - ''' + """ Test for Status to a given repository - ''' - with patch.dict(hg.__salt__, {'cmd.run_stdout': - MagicMock(return_value=( - 'A added 0\n' - 'A added 1\n' - 'M modified'))}): - self.assertEqual(hg.status('cwd'), { - 'added': ['added 0', 'added 1'], - 'modified': ['modified'], - }) + """ + with patch.dict( + hg.__salt__, + { + "cmd.run_stdout": MagicMock( + return_value=("A added 0\n" "A added 1\n" "M modified") + ) + }, + ): + self.assertEqual( + hg.status("cwd"), + {"added": ["added 0", "added 1"], "modified": ["modified"]}, + ) def test_status_multiple(self): - ''' + """ Test for Status to a given repository (cwd is list) - ''' - with patch.dict(hg.__salt__, { - 'cmd.run_stdout': MagicMock(side_effect=( - lambda *args, **kwargs: { - 'dir 0': 'A file 0\n', - 'dir 1': 'M file 1' - }[kwargs['cwd']]))}): - self.assertEqual(hg.status(['dir 0', 'dir 1']), { - 'dir 0': {'added': ['file 0']}, - 'dir 1': {'modified': ['file 1']}, - }) + """ + with patch.dict( + hg.__salt__, + { + "cmd.run_stdout": MagicMock( + side_effect=( + lambda *args, **kwargs: { + "dir 0": "A file 0\n", + "dir 1": "M file 1", + }[kwargs["cwd"]] + ) + ) + }, + ): + self.assertEqual( + hg.status(["dir 0", "dir 1"]), + {"dir 0": {"added": ["file 0"]}, "dir 1": {"modified": ["file 1"]}}, + ) diff --git a/tests/unit/modules/test_hosts.py b/tests/unit/modules/test_hosts.py index 6517ddc32ec..3c8b249f246 100644 --- a/tests/unit/modules/test_hosts.py +++ b/tests/unit/modules/test_hosts.py @@ -1,144 +1,161 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - mock_open, - patch, -) # Import Salt Libs import salt.modules.hosts as hosts import salt.utils.data import salt.utils.platform import salt.utils.stringutils -from salt.ext.six.moves import StringIO from salt.ext import six +from salt.ext.six.moves import StringIO + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase class HostsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.hosts - ''' + """ + def setup_loader_modules(self): return {hosts: {}} # 'list_hosts' function tests: 1 def test_list_hosts(self): - ''' + """ Tests return the hosts found in the hosts file - ''' - with patch('salt.modules.hosts._list_hosts', - MagicMock(return_value={'10.10.10.10': ['Salt1', 'Salt2']})): - self.assertDictEqual({'10.10.10.10': ['Salt1', 'Salt2']}, - hosts.list_hosts()) + """ + with patch( + "salt.modules.hosts._list_hosts", + MagicMock(return_value={"10.10.10.10": {"aliases": ["Salt1", "Salt2"]}}), + ): + self.assertDictEqual( + {"10.10.10.10": {"aliases": ["Salt1", "Salt2"]}}, hosts.list_hosts() + ) # 'get_ip' function tests: 3 def test_get_ip(self): - ''' + """ Tests return ip associated with the named host - ''' - with patch('salt.modules.hosts._list_hosts', - MagicMock(return_value={'10.10.10.10': ['Salt1', 'Salt2']})): - self.assertEqual('10.10.10.10', hosts.get_ip('Salt1')) + """ + with patch( + "salt.modules.hosts._list_hosts", + MagicMock(return_value={"10.10.10.10": {"aliases": ["Salt1", "Salt2"]}}), + ): + self.assertEqual("10.10.10.10", hosts.get_ip("Salt1")) - self.assertEqual('', hosts.get_ip('Salt3')) + self.assertEqual("", hosts.get_ip("Salt3")) def test_get_ip_none(self): - ''' + """ Tests return ip associated with the named host - ''' - with patch('salt.modules.hosts._list_hosts', MagicMock(return_value='')): - self.assertEqual('', hosts.get_ip('Salt1')) + """ + with patch("salt.modules.hosts._list_hosts", MagicMock(return_value="")): + self.assertEqual("", hosts.get_ip("Salt1")) # 'get_alias' function tests: 2 def test_get_alias(self): - ''' + """ Tests return the list of aliases associated with an ip - ''' - with patch('salt.modules.hosts._list_hosts', - MagicMock(return_value={'10.10.10.10': ['Salt1', 'Salt2']})): - self.assertListEqual(['Salt1', 'Salt2'], hosts.get_alias('10.10.10.10')) + """ + with patch( + "salt.modules.hosts._list_hosts", + MagicMock(return_value={"10.10.10.10": {"aliases": ["Salt1", "Salt2"]}}), + ): + self.assertListEqual(["Salt1", "Salt2"], hosts.get_alias("10.10.10.10")) def test_get_alias_none(self): - ''' + """ Tests return the list of aliases associated with an ip - ''' - with patch('salt.modules.hosts._list_hosts', - MagicMock(return_value={'10.10.10.10': ['Salt1', 'Salt2']})): - self.assertListEqual([], hosts.get_alias('10.10.10.11')) + """ + with patch( + "salt.modules.hosts._list_hosts", + MagicMock(return_value={"10.10.10.10": {"aliases": ["Salt1", "Salt2"]}}), + ): + self.assertListEqual([], hosts.get_alias("10.10.10.11")) # 'has_pair' function tests: 1 def test_has_pair(self): - ''' + """ Tests return True / False if the alias is set - ''' - with patch('salt.modules.hosts._list_hosts', - MagicMock(return_value={'10.10.10.10': ['Salt1', 'Salt2']})): - self.assertTrue(hosts.has_pair('10.10.10.10', 'Salt1')) + """ + with patch( + "salt.modules.hosts._list_hosts", + MagicMock(return_value={"10.10.10.10": {"aliases": ["Salt1", "Salt2"]}}), + ): + self.assertTrue(hosts.has_pair("10.10.10.10", "Salt1")) - self.assertFalse(hosts.has_pair('10.10.10.10', 'Salt3')) + self.assertFalse(hosts.has_pair("10.10.10.10", "Salt3")) # 'set_host' function tests: 3 def test_set_host(self): - ''' + """ Tests true if the alias is set - ''' - hosts_file = '/etc/hosts' + """ + hosts_file = "/etc/hosts" if salt.utils.platform.is_windows(): - hosts_file = r'C:\Windows\System32\Drivers\etc\hosts' + hosts_file = r"C:\Windows\System32\Drivers\etc\hosts" - with patch('salt.modules.hosts.__get_hosts_filename', - MagicMock(return_value=hosts_file)), \ - patch('os.path.isfile', MagicMock(return_value=False)), \ - patch.dict(hosts.__salt__, - {'config.option': MagicMock(return_value=None)}): - self.assertFalse(hosts.set_host('10.10.10.10', 'Salt1')) + with patch( + "salt.modules.hosts.__get_hosts_filename", + MagicMock(return_value=hosts_file), + ), patch("os.path.isfile", MagicMock(return_value=False)), patch.dict( + hosts.__salt__, {"config.option": MagicMock(return_value=None)} + ): + self.assertFalse(hosts.set_host("10.10.10.10", "Salt1")) def test_set_host_true(self): - ''' + """ Tests true if the alias is set - ''' - with patch('salt.modules.hosts.__get_hosts_filename', - MagicMock(return_value='/etc/hosts')), \ - patch('os.path.isfile', MagicMock(return_value=True)), \ - patch('salt.utils.files.fopen', mock_open(b'')): + """ + with patch( + "salt.modules.hosts.__get_hosts_filename", + MagicMock(return_value="/etc/hosts"), + ), patch("os.path.isfile", MagicMock(return_value=True)), patch( + "salt.utils.files.fopen", mock_open(b"") + ): mock_opt = MagicMock(return_value=None) - with patch.dict(hosts.__salt__, {'config.option': mock_opt}): - self.assertTrue(hosts.set_host('10.10.10.10', 'Salt1')) + with patch.dict(hosts.__salt__, {"config.option": mock_opt}): + self.assertTrue(hosts.set_host("10.10.10.10", "Salt1")) def test_set_host_true_remove(self): - ''' + """ Test if an empty hosts value removes existing entries - ''' - with patch('salt.modules.hosts.__get_hosts_filename', - MagicMock(return_value='/etc/hosts')), \ - patch('os.path.isfile', MagicMock(return_value=True)): - data = ['\n'.join(( - '1.1.1.1 foo.foofoo foo', - '2.2.2.2 bar.barbar bar', - '3.3.3.3 asdf.asdfadsf asdf', - '1.1.1.1 foofoo.foofoo foofoo', - ))] + """ + with patch( + "salt.modules.hosts.__get_hosts_filename", + MagicMock(return_value="/etc/hosts"), + ), patch("os.path.isfile", MagicMock(return_value=True)): + data = [ + "\n".join( + ( + "1.1.1.1 foo.foofoo foo", + "2.2.2.2 bar.barbar bar", + "3.3.3.3 asdf.asdfadsf asdf", + "1.1.1.1 foofoo.foofoo foofoo", + ) + ) + ] class TmpStringIO(StringIO, object): - def __init__(self, fn, mode='r'): + def __init__(self, fn, mode="r"): self.mode = mode initial_value = data[0] - if 'w' in self.mode: - initial_value = '' + if "w" in self.mode: + initial_value = "" super(TmpStringIO, self).__init__(initial_value) def __enter__(self): @@ -162,30 +179,35 @@ class HostsTestCase(TestCase, LoaderModuleMockMixin): def read(self, *args): ret = super(TmpStringIO, self).read(*args) - if six.PY3 and 'b' in self.mode: + if six.PY3 and "b" in self.mode: return salt.utils.stringutils.to_bytes(ret) else: return ret def write(self, s, *args): if six.PY3: - if 'b' in self.mode: + if "b" in self.mode: if not isinstance(s, bytes): # Make this act like a binary filehandle - raise TypeError("a bytes-like object is required, not 'str'") + raise TypeError( + "a bytes-like object is required, not 'str'" + ) # The StringIO wants a str type, it won't take # bytes. Convert before writing to it. return super(TmpStringIO, self).write( - salt.utils.stringutils.to_str(s), *args) + salt.utils.stringutils.to_str(s), *args + ) else: if not isinstance(s, str): # Make this act like a non-binary filehandle - raise TypeError("write() argument must be str, not bytes") + raise TypeError( + "write() argument must be str, not bytes" + ) return super(TmpStringIO, self).write(s, *args) def readlines(self): ret = super(TmpStringIO, self).readlines() - if six.PY3 and 'b' in self.mode: + if six.PY3 and "b" in self.mode: return salt.utils.data.encode(ret) else: return ret @@ -194,74 +216,101 @@ class HostsTestCase(TestCase, LoaderModuleMockMixin): for line in lines: self.write(line) - expected = '\n'.join(( - '2.2.2.2 bar.barbar bar', - '3.3.3.3 asdf.asdfadsf asdf', - )) + '\n' + expected = ( + "\n".join(("2.2.2.2 bar.barbar bar", "3.3.3.3 asdf.asdfadsf asdf",)) + + "\n" + ) - with patch('salt.utils.files.fopen', TmpStringIO): + with patch("salt.utils.files.fopen", TmpStringIO): mock_opt = MagicMock(return_value=None) - with patch.dict(hosts.__salt__, {'config.option': mock_opt}): - self.assertTrue(hosts.set_host('1.1.1.1', ' ')) + with patch.dict(hosts.__salt__, {"config.option": mock_opt}): + self.assertTrue(hosts.set_host("1.1.1.1", " ")) self.assertEqual(data[0], expected) # 'rm_host' function tests: 2 def test_rm_host(self): - ''' + """ Tests if specified host entry gets removed from the hosts file - ''' - with patch('salt.utils.files.fopen', mock_open(b'')), \ - patch('salt.modules.hosts.__get_hosts_filename', - MagicMock(return_value='/etc/hosts')), \ - patch('salt.modules.hosts.has_pair', - MagicMock(return_value=True)), \ - patch('os.path.isfile', MagicMock(return_value=True)): + """ + with patch("salt.utils.files.fopen", mock_open(b"")), patch( + "salt.modules.hosts.__get_hosts_filename", + MagicMock(return_value="/etc/hosts"), + ), patch("salt.modules.hosts.has_pair", MagicMock(return_value=True)), patch( + "os.path.isfile", MagicMock(return_value=True) + ): mock_opt = MagicMock(return_value=None) - with patch.dict(hosts.__salt__, {'config.option': mock_opt}): - self.assertTrue(hosts.rm_host('10.10.10.10', 'Salt1')) + with patch.dict(hosts.__salt__, {"config.option": mock_opt}): + self.assertTrue(hosts.rm_host("10.10.10.10", "Salt1")) def test_rm_host_false(self): - ''' + """ Tests if specified host entry gets removed from the hosts file - ''' - with patch('salt.modules.hosts.has_pair', MagicMock(return_value=False)): - self.assertTrue(hosts.rm_host('10.10.10.10', 'Salt1')) + """ + with patch("salt.modules.hosts.has_pair", MagicMock(return_value=False)): + self.assertTrue(hosts.rm_host("10.10.10.10", "Salt1")) # 'add_host' function tests: 3 def test_add_host(self): - ''' + """ Tests if specified host entry gets added from the hosts file - ''' - hosts_file = '/etc/hosts' + """ + hosts_file = "/etc/hosts" if salt.utils.platform.is_windows(): - hosts_file = r'C:\Windows\System32\Drivers\etc\hosts' + hosts_file = r"C:\Windows\System32\Drivers\etc\hosts" - with patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.modules.hosts.__get_hosts_filename', - MagicMock(return_value=hosts_file)): + with patch("salt.utils.files.fopen", mock_open()), patch( + "salt.modules.hosts.__get_hosts_filename", + MagicMock(return_value=hosts_file), + ): mock_opt = MagicMock(return_value=None) - with patch.dict(hosts.__salt__, {'config.option': mock_opt}): - self.assertTrue(hosts.add_host('10.10.10.10', 'Salt1')) + with patch.dict(hosts.__salt__, {"config.option": mock_opt}): + self.assertTrue(hosts.add_host("10.10.10.10", "Salt1")) def test_add_host_no_file(self): - ''' + """ Tests if specified host entry gets added from the hosts file - ''' - with patch('salt.utils.files.fopen', mock_open()), \ - patch('os.path.isfile', MagicMock(return_value=False)): + """ + with patch("salt.utils.files.fopen", mock_open()), patch( + "os.path.isfile", MagicMock(return_value=False) + ): mock_opt = MagicMock(return_value=None) - with patch.dict(hosts.__salt__, {'config.option': mock_opt}): - self.assertFalse(hosts.add_host('10.10.10.10', 'Salt1')) + with patch.dict(hosts.__salt__, {"config.option": mock_opt}): + self.assertFalse(hosts.add_host("10.10.10.10", "Salt1")) def test_add_host_create_entry(self): - ''' + """ Tests if specified host entry gets added from the hosts file - ''' - with patch('salt.utils.files.fopen', mock_open()), \ - patch('os.path.isfile', MagicMock(return_value=True)): + """ + with patch("salt.utils.files.fopen", mock_open()), patch( + "os.path.isfile", MagicMock(return_value=True) + ): mock_opt = MagicMock(return_value=None) - with patch.dict(hosts.__salt__, {'config.option': mock_opt}): - self.assertTrue(hosts.add_host('10.10.10.10', 'Salt1')) + with patch.dict(hosts.__salt__, {"config.option": mock_opt}): + self.assertTrue(hosts.add_host("10.10.10.10", "Salt1")) + + def test_set_comment(self): + """ + Tests return True / False when setting a comment + """ + hosts_file = "/etc/hosts" + if salt.utils.platform.is_windows(): + hosts_file = r"C:\Windows\System32\Drivers\etc\hosts" + + with patch("salt.utils.files.fopen", mock_open()), patch( + "salt.modules.hosts.__get_hosts_filename", + MagicMock(return_value=hosts_file), + ): + mock_opt = MagicMock(return_value=None) + with patch.dict(hosts.__salt__, {"config.option": mock_opt}): + with patch( + "salt.modules.hosts._list_hosts", + MagicMock( + return_value={"10.10.10.10": {"aliases": ["Salt1", "Salt2"]}} + ), + ): + self.assertTrue(hosts.set_comment("10.10.10.10", "A comment")) + + self.assertFalse(hosts.set_comment("10.10.10.11", "A comment")) diff --git a/tests/unit/modules/test_http.py b/tests/unit/modules/test_http.py index e3ea75a30c4..957e7025bb3 100644 --- a/tests/unit/modules/test_http.py +++ b/tests/unit/modules/test_http.py @@ -1,58 +1,58 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.http as http import salt.utils.http +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class HttpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.http - ''' + """ + def setup_loader_modules(self): return {http: {}} def test_query(self): - ''' + """ Test for Query a resource, and decode the return data - ''' - with patch.object(salt.utils.http, 'query', return_value='A'): - self.assertEqual(http.query('url'), 'A') + """ + with patch.object(salt.utils.http, "query", return_value="A"): + self.assertEqual(http.query("url"), "A") def test_wait_for_with_interval(self): - ''' + """ Test for wait_for_successful_query waits for request_interval - ''' + """ - query_mock = MagicMock(side_effect=[{'error': 'error'}, {}]) + query_mock = MagicMock(side_effect=[{"error": "error"}, {}]) - with patch.object(salt.utils.http, 'query', query_mock): - with patch('time.sleep', MagicMock()) as sleep_mock: - self.assertEqual(http.wait_for_successful_query('url', request_interval=1), {}) + with patch.object(salt.utils.http, "query", query_mock): + with patch("time.sleep", MagicMock()) as sleep_mock: + self.assertEqual( + http.wait_for_successful_query("url", request_interval=1), {} + ) sleep_mock.assert_called_once_with(1) def test_wait_for_without_interval(self): - ''' + """ Test for wait_for_successful_query waits for request_interval - ''' + """ - query_mock = MagicMock(side_effect=[{'error': 'error'}, {}]) + query_mock = MagicMock(side_effect=[{"error": "error"}, {}]) - with patch.object(salt.utils.http, 'query', query_mock): - with patch('time.sleep', MagicMock()) as sleep_mock: - self.assertEqual(http.wait_for_successful_query('url'), {}) + with patch.object(salt.utils.http, "query", query_mock): + with patch("time.sleep", MagicMock()) as sleep_mock: + self.assertEqual(http.wait_for_successful_query("url"), {}) sleep_mock.assert_not_called() diff --git a/tests/unit/modules/test_ilo.py b/tests/unit/modules/test_ilo.py index 7e28815b6f7..a7d679b3b99 100644 --- a/tests/unit/modules/test_ilo.py +++ b/tests/unit/modules/test_ilo.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -9,318 +9,377 @@ from __future__ import absolute_import, print_function, unicode_literals import logging import tempfile -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.file # Import Salt Libs import salt.modules.ilo as ilo -import salt.modules.file + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase log = logging.getLogger(__name__) class IloTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.ilo - ''' + """ + def setup_loader_modules(self): - return {ilo: - {'__opts__': {'cachedir': tempfile.gettempdir()}, - '__salt__': {'file.remove': salt.modules.file.remove}} - } + return { + ilo: { + "__opts__": {"cachedir": tempfile.gettempdir()}, + "__salt__": {"file.remove": salt.modules.file.remove}, + } + } # '__execute_cmd' function tests: 1 def test_execute_cmd(self): - ''' + """ Test if __execute_command opens the temporary file properly when calling global_settings. - ''' - mock_cmd_run = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(ilo.__salt__, {'cmd.run_all': mock_cmd_run}): + """ + mock_cmd_run = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(ilo.__salt__, {"cmd.run_all": mock_cmd_run}): ret = ilo.global_settings() self.assertEqual(ret, True) # 'global_settings' function tests: 1 def test_global_settings(self): - ''' + """ Test if it shows global_settings - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Global Settings': {}})): - self.assertDictEqual(ilo.global_settings(), {'Global Settings': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Global Settings": {}}), + ): + self.assertDictEqual(ilo.global_settings(), {"Global Settings": {}}) # 'set_http_port' function tests: 1 def test_set_http_port(self): - ''' + """ Test if it configure the port HTTP should listen on - ''' - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'HTTP_PORT': - {'VALUE': 80}}}): + """ + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"HTTP_PORT": {"VALUE": 80}}}, + ): self.assertTrue(ilo.set_http_port()) - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'HTTP_PORT': - {'VALUE': 40}}}): - with patch.object(ilo, '__execute_cmd', - return_value={'Set HTTP Port': {}}): - self.assertDictEqual(ilo.set_http_port(), - {'Set HTTP Port': {}}) + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"HTTP_PORT": {"VALUE": 40}}}, + ): + with patch.object(ilo, "__execute_cmd", return_value={"Set HTTP Port": {}}): + self.assertDictEqual(ilo.set_http_port(), {"Set HTTP Port": {}}) # 'set_https_port' function tests: 1 def test_set_https_port(self): - ''' + """ Test if it configure the port HTTPS should listen on - ''' - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'HTTP_PORT': - {'VALUE': 443}}}): + """ + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"HTTP_PORT": {"VALUE": 443}}}, + ): self.assertTrue(ilo.set_https_port()) - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'HTTP_PORT': - {'VALUE': 80}}}): - with patch.object(ilo, '__execute_cmd', - return_value={'Set HTTPS Port': {}}): - self.assertDictEqual(ilo.set_https_port(), - {'Set HTTPS Port': {}}) + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"HTTP_PORT": {"VALUE": 80}}}, + ): + with patch.object( + ilo, "__execute_cmd", return_value={"Set HTTPS Port": {}} + ): + self.assertDictEqual(ilo.set_https_port(), {"Set HTTPS Port": {}}) # 'enable_ssh' function tests: 1 def test_enable_ssh(self): - ''' + """ Test if it enable the SSH daemon - ''' - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'SSH_STATUS': - {'VALUE': 'Y'}}}): + """ + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"SSH_STATUS": {"VALUE": "Y"}}}, + ): self.assertTrue(ilo.enable_ssh()) - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'SSH_STATUS': - {'VALUE': 'N'}}}): - with patch.object(ilo, '__execute_cmd', - return_value={'Enable SSH': {}}): - self.assertDictEqual(ilo.enable_ssh(), {'Enable SSH': {}}) + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"SSH_STATUS": {"VALUE": "N"}}}, + ): + with patch.object(ilo, "__execute_cmd", return_value={"Enable SSH": {}}): + self.assertDictEqual(ilo.enable_ssh(), {"Enable SSH": {}}) # 'disable_ssh' function tests: 1 def test_disable_ssh(self): - ''' + """ Test if it disable the SSH daemon - ''' - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'SSH_STATUS': - {'VALUE': 'N'}}}): + """ + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"SSH_STATUS": {"VALUE": "N"}}}, + ): self.assertTrue(ilo.disable_ssh()) - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'SSH_STATUS': - {'VALUE': 'Y'}}}): - with patch.object(ilo, '__execute_cmd', - return_value={'Disable SSH': {}}): - self.assertDictEqual(ilo.disable_ssh(), {'Disable SSH': {}}) + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"SSH_STATUS": {"VALUE": "Y"}}}, + ): + with patch.object(ilo, "__execute_cmd", return_value={"Disable SSH": {}}): + self.assertDictEqual(ilo.disable_ssh(), {"Disable SSH": {}}) # 'set_ssh_port' function tests: 1 def test_set_ssh_port(self): - ''' + """ Test if it enable SSH on a user defined port - ''' - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'SSH_PORT': - {'VALUE': 22}}}): + """ + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"SSH_PORT": {"VALUE": 22}}}, + ): self.assertTrue(ilo.set_ssh_port()) - with patch.object(ilo, 'global_settings', - return_value={'Global Settings': {'SSH_PORT': - {'VALUE': 20}}}): - with patch.object(ilo, '__execute_cmd', - return_value={'Configure SSH Port': {}}): - self.assertDictEqual(ilo.set_ssh_port(), - {'Configure SSH Port': {}}) + with patch.object( + ilo, + "global_settings", + return_value={"Global Settings": {"SSH_PORT": {"VALUE": 20}}}, + ): + with patch.object( + ilo, "__execute_cmd", return_value={"Configure SSH Port": {}} + ): + self.assertDictEqual(ilo.set_ssh_port(), {"Configure SSH Port": {}}) # 'set_ssh_key' function tests: 1 def test_set_ssh_key(self): - ''' + """ Test if it configure SSH public keys for specific users - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Import SSH Publickey': {}})): - self.assertDictEqual(ilo.set_ssh_key('ssh-rsa AAAAB3Nza Salt'), - {'Import SSH Publickey': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Import SSH Publickey": {}}), + ): + self.assertDictEqual( + ilo.set_ssh_key("ssh-rsa AAAAB3Nza Salt"), {"Import SSH Publickey": {}} + ) # 'delete_ssh_key' function tests: 1 def test_delete_ssh_key(self): - ''' + """ Test if it delete a users SSH key from the ILO - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Delete user SSH key': {}})): - self.assertDictEqual(ilo.delete_ssh_key('Salt'), - {'Delete user SSH key': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Delete user SSH key": {}}), + ): + self.assertDictEqual( + ilo.delete_ssh_key("Salt"), {"Delete user SSH key": {}} + ) # 'list_users' function tests: 1 def test_list_users(self): - ''' + """ Test if it list all users - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'All users': {}})): - self.assertDictEqual(ilo.list_users(), {'All users': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", MagicMock(return_value={"All users": {}}) + ): + self.assertDictEqual(ilo.list_users(), {"All users": {}}) # 'list_users_info' function tests: 1 def test_list_users_info(self): - ''' + """ Test if it List all users in detail - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'All users info': {}})): - self.assertDictEqual(ilo.list_users_info(), {'All users info': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"All users info": {}}), + ): + self.assertDictEqual(ilo.list_users_info(), {"All users info": {}}) # 'create_user' function tests: 1 def test_create_user(self): - ''' + """ Test if it create user - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Create user': {}})): - self.assertDictEqual(ilo.create_user('Salt', 'secretagent', - 'VIRTUAL_MEDIA_PRIV'), - {'Create user': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Create user": {}}), + ): + self.assertDictEqual( + ilo.create_user("Salt", "secretagent", "VIRTUAL_MEDIA_PRIV"), + {"Create user": {}}, + ) # 'delete_user' function tests: 1 def test_delete_user(self): - ''' + """ Test if it delete a user - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Delete user': {}})): - self.assertDictEqual(ilo.delete_user('Salt'), {'Delete user': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Delete user": {}}), + ): + self.assertDictEqual(ilo.delete_user("Salt"), {"Delete user": {}}) # 'get_user' function tests: 1 def test_get_user(self): - ''' + """ Test if it returns local user information, excluding the password - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'User Info': {}})): - self.assertDictEqual(ilo.get_user('Salt'), {'User Info': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", MagicMock(return_value={"User Info": {}}) + ): + self.assertDictEqual(ilo.get_user("Salt"), {"User Info": {}}) # 'change_username' function tests: 1 def test_change_username(self): - ''' + """ Test if it change a username - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Change username': {}})): - self.assertDictEqual(ilo.change_username('Salt', 'SALT'), - {'Change username': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Change username": {}}), + ): + self.assertDictEqual( + ilo.change_username("Salt", "SALT"), {"Change username": {}} + ) # 'change_password' function tests: 1 def test_change_password(self): - ''' + """ Test if it reset a users password - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Change password': {}})): - self.assertDictEqual(ilo.change_password('Salt', 'saltpasswd'), - {'Change password': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Change password": {}}), + ): + self.assertDictEqual( + ilo.change_password("Salt", "saltpasswd"), {"Change password": {}} + ) # 'network' function tests: 1 def test_network(self): - ''' + """ Test if it grab the current network settings - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Network Settings': {}})): - self.assertDictEqual(ilo.network(), {'Network Settings': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Network Settings": {}}), + ): + self.assertDictEqual(ilo.network(), {"Network Settings": {}}) # 'configure_network' function tests: 1 def test_configure_network(self): - ''' + """ Test if it configure Network Interface - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Configure_Network': {}})): - ret = {'Network Settings': - {'IP_ADDRESS': {'VALUE': '10.0.0.10'}, - 'SUBNET_MASK': {'VALUE': '255.255.255.0'}, - 'GATEWAY_IP_ADDRESS': {'VALUE': '10.0.0.1'}}} - with patch.object(ilo, 'network', return_value=ret): - self.assertTrue(ilo.configure_network('10.0.0.10', - '255.255.255.0', '10.0.0.1')) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Configure_Network": {}}), + ): + ret = { + "Network Settings": { + "IP_ADDRESS": {"VALUE": "10.0.0.10"}, + "SUBNET_MASK": {"VALUE": "255.255.255.0"}, + "GATEWAY_IP_ADDRESS": {"VALUE": "10.0.0.1"}, + } + } + with patch.object(ilo, "network", return_value=ret): + self.assertTrue( + ilo.configure_network("10.0.0.10", "255.255.255.0", "10.0.0.1") + ) - with patch.object(ilo, 'network', return_value=ret): - with patch.object(ilo, '__execute_cmd', - return_value={'Network Settings': {}}): - self.assertDictEqual(ilo.configure_network('10.0.0.100', - '255.255.255.10', - '10.0.0.10'), - {'Network Settings': {}}) + with patch.object(ilo, "network", return_value=ret): + with patch.object( + ilo, "__execute_cmd", return_value={"Network Settings": {}} + ): + self.assertDictEqual( + ilo.configure_network( + "10.0.0.100", "255.255.255.10", "10.0.0.10" + ), + {"Network Settings": {}}, + ) # 'enable_dhcp' function tests: 1 def test_enable_dhcp(self): - ''' + """ Test if it enable DHCP - ''' - with patch.object(ilo, 'network', - return_value={'Network Settings': {'DHCP_ENABLE': - {'VALUE': 'Y'}}}): + """ + with patch.object( + ilo, + "network", + return_value={"Network Settings": {"DHCP_ENABLE": {"VALUE": "Y"}}}, + ): self.assertTrue(ilo.enable_dhcp()) - with patch.object(ilo, 'network', - return_value={'Network Settings': {'DHCP_ENABLE': - {'VALUE': 'N'}}}): - with patch.object(ilo, '__execute_cmd', - return_value={'Enable DHCP': {}}): - self.assertDictEqual(ilo.enable_dhcp(), {'Enable DHCP': {}}) + with patch.object( + ilo, + "network", + return_value={"Network Settings": {"DHCP_ENABLE": {"VALUE": "N"}}}, + ): + with patch.object(ilo, "__execute_cmd", return_value={"Enable DHCP": {}}): + self.assertDictEqual(ilo.enable_dhcp(), {"Enable DHCP": {}}) # 'disable_dhcp' function tests: 1 def test_disable_dhcp(self): - ''' + """ Test if it disable DHCP - ''' - with patch.object(ilo, 'network', - return_value={'Network Settings': {'DHCP_ENABLE': - {'VALUE': 'N'}}}): + """ + with patch.object( + ilo, + "network", + return_value={"Network Settings": {"DHCP_ENABLE": {"VALUE": "N"}}}, + ): self.assertTrue(ilo.disable_dhcp()) - with patch.object(ilo, 'network', - return_value={'Network Settings': {'DHCP_ENABLE': - {'VALUE': 'Y'}}}): - with patch.object(ilo, '__execute_cmd', - return_value={'Disable DHCP': {}}): - self.assertDictEqual(ilo.disable_dhcp(), {'Disable DHCP': {}}) + with patch.object( + ilo, + "network", + return_value={"Network Settings": {"DHCP_ENABLE": {"VALUE": "Y"}}}, + ): + with patch.object(ilo, "__execute_cmd", return_value={"Disable DHCP": {}}): + self.assertDictEqual(ilo.disable_dhcp(), {"Disable DHCP": {}}) # 'configure_snmp' function tests: 1 def test_configure_snmp(self): - ''' + """ Test if it configure SNMP - ''' - with patch('salt.modules.ilo.__execute_cmd', - MagicMock(return_value={'Configure SNMP': {}})): - self.assertDictEqual(ilo.configure_snmp('Salt'), {'Configure SNMP': {}}) + """ + with patch( + "salt.modules.ilo.__execute_cmd", + MagicMock(return_value={"Configure SNMP": {}}), + ): + self.assertDictEqual(ilo.configure_snmp("Salt"), {"Configure SNMP": {}}) diff --git a/tests/unit/modules/test_incron.py b/tests/unit/modules/test_incron.py index 76aa4aa463c..0fda351b88d 100644 --- a/tests/unit/modules/test_incron.py +++ b/tests/unit/modules/test_incron.py @@ -1,181 +1,221 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.incron as incron +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class IncronTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.incron - ''' + """ + def setup_loader_modules(self): return {incron: {}} # 'write_incron_file' function tests: 1 def test_write_incron_file(self): - ''' + """ Test if it writes the contents of a file to a user's crontab - ''' + """ mock = MagicMock(return_value=0) - with patch.dict(incron.__salt__, {'cmd.retcode': mock}), \ - patch('salt.modules.incron._get_incron_cmdstr', - MagicMock(return_value='incrontab')): - self.assertTrue(incron.write_incron_file('cybage', - '/home/cybage/new_cron')) + with patch.dict(incron.__salt__, {"cmd.retcode": mock}), patch( + "salt.modules.incron._get_incron_cmdstr", + MagicMock(return_value="incrontab"), + ): + self.assertTrue(incron.write_incron_file("cybage", "/home/cybage/new_cron")) # 'write_cron_file_verbose' function tests: 1 def test_write_cron_file_verbose(self): - ''' + """ Test if it writes the contents of a file to a user's crontab and return error message on error - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(incron.__salt__, {'cmd.run_all': mock}), \ - patch('salt.modules.incron._get_incron_cmdstr', - MagicMock(return_value='incrontab')): - self.assertTrue(incron.write_incron_file_verbose - ('cybage', '/home/cybage/new_cron')) + with patch.dict(incron.__salt__, {"cmd.run_all": mock}), patch( + "salt.modules.incron._get_incron_cmdstr", + MagicMock(return_value="incrontab"), + ): + self.assertTrue( + incron.write_incron_file_verbose("cybage", "/home/cybage/new_cron") + ) # 'raw_system_incron' function tests: 1 def test_raw_system_incron(self): - ''' + """ Test if it return the contents of the system wide incrontab - ''' - with patch('salt.modules.incron._read_file', - MagicMock(return_value='salt')): - self.assertEqual(incron.raw_system_incron(), 'salt') + """ + with patch("salt.modules.incron._read_file", MagicMock(return_value="salt")): + self.assertEqual(incron.raw_system_incron(), "salt") # 'raw_incron' function tests: 1 def test_raw_incron(self): - ''' + """ Test if it return the contents of the user's incrontab - ''' - mock = MagicMock(return_value='incrontab') - with patch.dict(incron.__grains__, {'os_family': mock}): - mock = MagicMock(return_value='salt') - with patch.dict(incron.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(incron.raw_incron('cybage'), 'salt') + """ + mock = MagicMock(return_value="incrontab") + with patch.dict(incron.__grains__, {"os_family": mock}): + mock = MagicMock(return_value="salt") + with patch.dict(incron.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual(incron.raw_incron("cybage"), "salt") # 'list_tab' function tests: 1 def test_list_tab(self): - ''' + """ Test if it return the contents of the specified user's incrontab - ''' - mock = MagicMock(return_value='incrontab') - with patch.dict(incron.__grains__, {'os_family': mock}): - mock = MagicMock(return_value='salt') - with patch.dict(incron.__salt__, {'cmd.run_stdout': mock}): - self.assertDictEqual(incron.list_tab('cybage'), - {'pre': ['salt'], 'crons': []}) + """ + mock = MagicMock(return_value="incrontab") + with patch.dict(incron.__grains__, {"os_family": mock}): + mock = MagicMock(return_value="salt") + with patch.dict(incron.__salt__, {"cmd.run_stdout": mock}): + self.assertDictEqual( + incron.list_tab("cybage"), {"pre": ["salt"], "crons": []} + ) # 'set_job' function tests: 1 def test_set_job(self): - ''' + """ Test if it sets a cron job up for a specified user. - ''' - self.assertEqual(incron.set_job('cybage', '/home/cybage', 'TO_MODIFY', - 'echo "$$ $@ $# $% $&"'), - 'Invalid mask type: TO_MODIFY') + """ + self.assertEqual( + incron.set_job( + "cybage", "/home/cybage", "TO_MODIFY", 'echo "$$ $@ $# $% $&"' + ), + "Invalid mask type: TO_MODIFY", + ) - val = {'pre': [], 'crons': [{'path': '/home/cybage', - 'mask': 'IN_MODIFY', - 'cmd': 'echo "SALT"'}]} - with patch.object(incron, 'list_tab', - MagicMock(return_value=val)): - self.assertEqual(incron.set_job('cybage', '/home/cybage', - 'IN_MODIFY', - 'echo "SALT"'), 'present') + val = { + "pre": [], + "crons": [ + {"path": "/home/cybage", "mask": "IN_MODIFY", "cmd": 'echo "SALT"'} + ], + } + with patch.object(incron, "list_tab", MagicMock(return_value=val)): + self.assertEqual( + incron.set_job("cybage", "/home/cybage", "IN_MODIFY", 'echo "SALT"'), + "present", + ) - with patch.object(incron, 'list_tab', - MagicMock(return_value={'pre': ['salt'], - 'crons': []})): - mock = MagicMock(return_value='incrontab') - with patch.dict(incron.__grains__, {'os_family': mock}): - with patch.object(incron, '_write_incron_lines', - MagicMock(return_value={'retcode': True, - 'stderr': 'error'})): - self.assertEqual(incron.set_job('cybage', '/home/cybage', - 'IN_MODIFY', - 'echo "SALT"'), 'error') + with patch.object( + incron, "list_tab", MagicMock(return_value={"pre": ["salt"], "crons": []}) + ): + mock = MagicMock(return_value="incrontab") + with patch.dict(incron.__grains__, {"os_family": mock}): + with patch.object( + incron, + "_write_incron_lines", + MagicMock(return_value={"retcode": True, "stderr": "error"}), + ): + self.assertEqual( + incron.set_job( + "cybage", "/home/cybage", "IN_MODIFY", 'echo "SALT"' + ), + "error", + ) - with patch.object(incron, 'list_tab', - MagicMock(return_value={'pre': ['salt'], - 'crons': []})): - mock = MagicMock(return_value='incrontab') - with patch.dict(incron.__grains__, {'os_family': mock}): - with patch.object(incron, '_write_incron_lines', - MagicMock(return_value={'retcode': False, - 'stderr': 'error'})): - self.assertEqual(incron.set_job('cybage', '/home/cybage', - 'IN_MODIFY', - 'echo "SALT"'), 'new') + with patch.object( + incron, "list_tab", MagicMock(return_value={"pre": ["salt"], "crons": []}) + ): + mock = MagicMock(return_value="incrontab") + with patch.dict(incron.__grains__, {"os_family": mock}): + with patch.object( + incron, + "_write_incron_lines", + MagicMock(return_value={"retcode": False, "stderr": "error"}), + ): + self.assertEqual( + incron.set_job( + "cybage", "/home/cybage", "IN_MODIFY", 'echo "SALT"' + ), + "new", + ) - val = {'pre': [], 'crons': [{'path': '/home/cybage', - 'mask': 'IN_MODIFY,IN_DELETE', - 'cmd': 'echo "SALT"'}]} - with patch.object(incron, 'list_tab', - MagicMock(return_value=val)): - mock = MagicMock(return_value='incrontab') - with patch.dict(incron.__grains__, {'os_family': mock}): - with patch.object(incron, '_write_incron_lines', - MagicMock(return_value={'retcode': False, - 'stderr': 'error'})): - self.assertEqual(incron.set_job('cybage', '/home/cybage', - 'IN_DELETE', - 'echo "SALT"'), 'updated') + val = { + "pre": [], + "crons": [ + { + "path": "/home/cybage", + "mask": "IN_MODIFY,IN_DELETE", + "cmd": 'echo "SALT"', + } + ], + } + with patch.object(incron, "list_tab", MagicMock(return_value=val)): + mock = MagicMock(return_value="incrontab") + with patch.dict(incron.__grains__, {"os_family": mock}): + with patch.object( + incron, + "_write_incron_lines", + MagicMock(return_value={"retcode": False, "stderr": "error"}), + ): + self.assertEqual( + incron.set_job( + "cybage", "/home/cybage", "IN_DELETE", 'echo "SALT"' + ), + "updated", + ) # 'rm_job' function tests: 1 def test_rm_job(self): - ''' + """ Test if it remove a cron job for a specified user. If any of the day/time params are specified, the job will only be removed if the specified params match. - ''' - self.assertEqual(incron.rm_job('cybage', '/home/cybage', 'TO_MODIFY', - 'echo "$$ $@ $# $% $&"'), - 'Invalid mask type: TO_MODIFY') + """ + self.assertEqual( + incron.rm_job( + "cybage", "/home/cybage", "TO_MODIFY", 'echo "$$ $@ $# $% $&"' + ), + "Invalid mask type: TO_MODIFY", + ) - with patch.object(incron, 'list_tab', - MagicMock(return_value={'pre': ['salt'], - 'crons': []})): - mock = MagicMock(return_value='incrontab') - with patch.dict(incron.__grains__, {'os_family': mock}): - with patch.object(incron, '_write_incron_lines', - MagicMock(return_value={'retcode': True, - 'stderr': 'error'})): - self.assertEqual(incron.rm_job('cybage', '/home/cybage', - 'IN_MODIFY', - 'echo "SALT"'), 'error') + with patch.object( + incron, "list_tab", MagicMock(return_value={"pre": ["salt"], "crons": []}) + ): + mock = MagicMock(return_value="incrontab") + with patch.dict(incron.__grains__, {"os_family": mock}): + with patch.object( + incron, + "_write_incron_lines", + MagicMock(return_value={"retcode": True, "stderr": "error"}), + ): + self.assertEqual( + incron.rm_job( + "cybage", "/home/cybage", "IN_MODIFY", 'echo "SALT"' + ), + "error", + ) - with patch.object(incron, 'list_tab', - MagicMock(return_value={'pre': ['salt'], - 'crons': []})): - mock = MagicMock(return_value='incrontab') - with patch.dict(incron.__grains__, {'os_family': mock}): - with patch.object(incron, '_write_incron_lines', - MagicMock(return_value={'retcode': False, - 'stderr': 'error'})): - self.assertEqual(incron.rm_job('cybage', '/home/cybage', - 'IN_MODIFY', - 'echo "SALT"'), 'absent') + with patch.object( + incron, "list_tab", MagicMock(return_value={"pre": ["salt"], "crons": []}) + ): + mock = MagicMock(return_value="incrontab") + with patch.dict(incron.__grains__, {"os_family": mock}): + with patch.object( + incron, + "_write_incron_lines", + MagicMock(return_value={"retcode": False, "stderr": "error"}), + ): + self.assertEqual( + incron.rm_job( + "cybage", "/home/cybage", "IN_MODIFY", 'echo "SALT"' + ), + "absent", + ) diff --git a/tests/unit/modules/test_influxdb08mod.py b/tests/unit/modules/test_influxdb08mod.py index 39aae8758f3..311abdfa54f 100644 --- a/tests/unit/modules/test_influxdb08mod.py +++ b/tests/unit/modules/test_influxdb08mod.py @@ -1,23 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.influxdb08mod as influx08 +from tests.support.mock import MagicMock, patch -DB_LIST = ['A', 'B', 'C'] -USER_LIST = [{'name': 'A'}, {'name': 'B'}] +# Import Salt Testing Libs +from tests.support.unit import TestCase + +DB_LIST = ["A", "B", "C"] +USER_LIST = [{"name": "A"}, {"name": "B"}] class MockInfluxDBClient(object): @@ -56,228 +53,275 @@ class MockInfluxDBClient(object): class InfluxTestCase(TestCase): - ''' + """ TestCase for the salt.modules.at module - ''' + """ + def test_db_list(self): """ Test to list all InfluxDB databases """ mock_inf_db_client = MagicMock(return_value=MockInfluxDBClient()) - with patch.object(influx08, '_client', mock_inf_db_client): - self.assertEqual(influx08.db_list(user='root', - password='root', - host='localhost', - port=8086), DB_LIST) + with patch.object(influx08, "_client", mock_inf_db_client): + self.assertEqual( + influx08.db_list( + user="root", password="root", host="localhost", port=8086 + ), + DB_LIST, + ) def test_db_exists(self): - ''' + """ Tests for checks if a database exists in InfluxDB - ''' - with patch.object(influx08, 'db_list', side_effect=[[{'name': 'A'}], - None]): - self.assertTrue(influx08.db_exists(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + """ + with patch.object(influx08, "db_list", side_effect=[[{"name": "A"}], None]): + self.assertTrue( + influx08.db_exists( + name="A", user="root", password="root", host="localhost", port=8000 + ) + ) - self.assertFalse(influx08.db_exists(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + self.assertFalse( + influx08.db_exists( + name="A", user="root", password="root", host="localhost", port=8000 + ) + ) def test_db_create(self): - ''' + """ Test to create a database - ''' - with patch.object(influx08, 'db_exists', side_effect=[True, False]): - self.assertFalse(influx08.db_create(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + """ + with patch.object(influx08, "db_exists", side_effect=[True, False]): + self.assertFalse( + influx08.db_create( + name="A", user="root", password="root", host="localhost", port=8000 + ) + ) mock_inf_db_client = MagicMock(return_value=MockInfluxDBClient()) - with patch.object(influx08, '_client', mock_inf_db_client): - self.assertTrue(influx08.db_create(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + with patch.object(influx08, "_client", mock_inf_db_client): + self.assertTrue( + influx08.db_create( + name="A", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) def test_db_remove(self): - ''' + """ Test to remove a database - ''' - with patch.object(influx08, 'db_exists', side_effect=[False, True]): - self.assertFalse(influx08.db_remove(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + """ + with patch.object(influx08, "db_exists", side_effect=[False, True]): + self.assertFalse( + influx08.db_remove( + name="A", user="root", password="root", host="localhost", port=8000 + ) + ) mock_inf_db_client = MagicMock(return_value=MockInfluxDBClient()) - with patch.object(influx08, '_client', mock_inf_db_client): - self.assertTrue(influx08.db_remove(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + with patch.object(influx08, "_client", mock_inf_db_client): + self.assertTrue( + influx08.db_remove( + name="A", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) def test_user_list(self): - ''' + """ Tests for list cluster admins or database users. - ''' + """ mock_inf_db_client = MagicMock(return_value=MockInfluxDBClient()) - with patch.object(influx08, '_client', mock_inf_db_client): - self.assertListEqual(influx08.user_list(database='A', - user='root', - password='root', - host='localhost', - port=8086), USER_LIST) + with patch.object(influx08, "_client", mock_inf_db_client): + self.assertListEqual( + influx08.user_list( + database="A", + user="root", + password="root", + host="localhost", + port=8086, + ), + USER_LIST, + ) - self.assertListEqual(influx08.user_list(user='root', - password='root', - host='localhost', - port=8086), USER_LIST) + self.assertListEqual( + influx08.user_list( + user="root", password="root", host="localhost", port=8086 + ), + USER_LIST, + ) def test_user_exists(self): - ''' + """ Test to checks if a cluster admin or database user exists. - ''' - with patch.object(influx08, 'user_list', side_effect=[[{'name': 'A'}], - None]): - self.assertTrue(influx08.user_exists(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + """ + with patch.object(influx08, "user_list", side_effect=[[{"name": "A"}], None]): + self.assertTrue( + influx08.user_exists( + name="A", user="root", password="root", host="localhost", port=8000 + ) + ) - self.assertFalse(influx08.user_exists(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + self.assertFalse( + influx08.user_exists( + name="A", user="root", password="root", host="localhost", port=8000 + ) + ) def test_user_chpass(self): - ''' + """ Tests to change password for a cluster admin or a database user. - ''' - with patch.object(influx08, 'user_exists', return_value=False): - self.assertFalse(influx08.user_chpass(name='A', - passwd='*', - user='root', - password='root', - host='localhost', - port=8000)) + """ + with patch.object(influx08, "user_exists", return_value=False): + self.assertFalse( + influx08.user_chpass( + name="A", + passwd="*", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) - self.assertFalse(influx08.user_chpass(name='A', - passwd='*', - database='test', - user='root', - password='root', - host='localhost', - port=8000)) + self.assertFalse( + influx08.user_chpass( + name="A", + passwd="*", + database="test", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) mock_inf_db_client = MagicMock(return_value=MockInfluxDBClient()) - with patch.object(influx08, '_client', mock_inf_db_client): - with patch.object(influx08, 'user_exists', return_value=True): - self.assertTrue(influx08.user_chpass(name='A', - passwd='*', - user='root', - password='root', - host='localhost', - port=8000)) + with patch.object(influx08, "_client", mock_inf_db_client): + with patch.object(influx08, "user_exists", return_value=True): + self.assertTrue( + influx08.user_chpass( + name="A", + passwd="*", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) - self.assertTrue(influx08.user_chpass(name='A', - passwd='*', - database='test', - user='root', - password='root', - host='localhost', - port=8000)) + self.assertTrue( + influx08.user_chpass( + name="A", + passwd="*", + database="test", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) def test_user_remove(self): - ''' + """ Tests to remove a cluster admin or a database user. - ''' - with patch.object(influx08, 'user_exists', return_value=False): - self.assertFalse(influx08.user_remove(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + """ + with patch.object(influx08, "user_exists", return_value=False): + self.assertFalse( + influx08.user_remove( + name="A", user="root", password="root", host="localhost", port=8000 + ) + ) - self.assertFalse(influx08.user_remove(name='A', - database='test', - user='root', - password='root', - host='localhost', - port=8000)) + self.assertFalse( + influx08.user_remove( + name="A", + database="test", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) mock_inf_db_client = MagicMock(return_value=MockInfluxDBClient()) - with patch.object(influx08, '_client', mock_inf_db_client): - with patch.object(influx08, 'user_exists', return_value=True): - self.assertTrue(influx08.user_remove(name='A', - user='root', - password='root', - host='localhost', - port=8000)) + with patch.object(influx08, "_client", mock_inf_db_client): + with patch.object(influx08, "user_exists", return_value=True): + self.assertTrue( + influx08.user_remove( + name="A", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) - self.assertTrue(influx08.user_remove(name='A', - database='test', - user='root', - password='root', - host='localhost', - port=8000)) + self.assertTrue( + influx08.user_remove( + name="A", + database="test", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) def test_query(self): - ''' + """ Test for querying data - ''' + """ mock_inf_db_client = MagicMock(return_value=MockInfluxDBClient()) - with patch.object(influx08, '_client', mock_inf_db_client): - self.assertTrue(influx08.query(database='db', - query='q', - user='root', - password='root', - host='localhost', - port=8000)) + with patch.object(influx08, "_client", mock_inf_db_client): + self.assertTrue( + influx08.query( + database="db", + query="q", + user="root", + password="root", + host="localhost", + port=8000, + ) + ) def test_retention_policy_get(self): client = MockInfluxDBClient() - policy = {'name': 'foo'} - with patch.object(influx08, '_client', MagicMock(return_value=client)): + policy = {"name": "foo"} + with patch.object(influx08, "_client", MagicMock(return_value=client)): client.get_list_retention_policies = MagicMock(return_value=[policy]) self.assertEqual( - policy, - influx08.retention_policy_get(database='db', name='foo') + policy, influx08.retention_policy_get(database="db", name="foo") ) def test_retention_policy_add(self): client = MockInfluxDBClient() - with patch.object(influx08, '_client', MagicMock(return_value=client)): + with patch.object(influx08, "_client", MagicMock(return_value=client)): client.create_retention_policy = MagicMock() - self.assertTrue(influx08.retention_policy_add( - database='db', - name='name', - duration='30d', - replication=1, - )) + self.assertTrue( + influx08.retention_policy_add( + database="db", name="name", duration="30d", replication=1, + ) + ) client.create_retention_policy.assert_called_once_with( - 'name', '30d', 1, 'db', False) + "name", "30d", 1, "db", False + ) def test_retention_policy_modify(self): client = MockInfluxDBClient() - with patch.object(influx08, '_client', MagicMock(return_value=client)): + with patch.object(influx08, "_client", MagicMock(return_value=client)): client.alter_retention_policy = MagicMock() - self.assertTrue(influx08.retention_policy_alter( - database='db', - name='name', - duration='30d', - replication=1, - )) + self.assertTrue( + influx08.retention_policy_alter( + database="db", name="name", duration="30d", replication=1, + ) + ) client.alter_retention_policy.assert_called_once_with( - 'name', 'db', '30d', 1, False) + "name", "db", "30d", 1, False + ) diff --git a/tests/unit/modules/test_ini_manage.py b/tests/unit/modules/test_ini_manage.py index 0c372baf215..b96325bc87e 100644 --- a/tests/unit/modules/test_ini_manage.py +++ b/tests/unit/modules/test_ini_manage.py @@ -1,57 +1,61 @@ # -*- coding: utf-8 -*- -''' +""" Testing ini_manage exec module. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os import tempfile -# Import Salt Testing libs -from tests.support.unit import TestCase +import salt.modules.ini_manage as ini # Import Salt libs import salt.utils.files import salt.utils.stringutils -import salt.modules.ini_manage as ini + +# Import Salt Testing libs +from tests.support.unit import TestCase class IniManageTestCase(TestCase): - ''' + """ Testing ini_manage exec module. - ''' + """ - TEST_FILE_CONTENT = os.linesep.join([ - '# Comment on the first line', - '', - '# First main option', - 'option1=main1', - '', - '# Second main option', - 'option2=main2', - '', - '', - '[main]', - '# Another comment', - 'test1=value 1', - '', - 'test2=value 2', - '', - '[SectionB]', - 'test1=value 1B', - '', - '# Blank line should be above', - 'test3 = value 3B', - '', - '[SectionC]', - '# The following option is empty', - 'empty_option=' - ]) + TEST_FILE_CONTENT = os.linesep.join( + [ + "# Comment on the first line", + "", + "# First main option", + "option1=main1", + "", + "# Second main option", + "option2=main2", + "", + "", + "[main]", + "# Another comment", + "test1=value 1", + "", + "test2=value 2", + "", + "[SectionB]", + "test1=value 1B", + "", + "# Blank line should be above", + "test3 = value 3B", + "", + "[SectionC]", + "# The following option is empty", + "empty_option=", + ] + ) maxDiff = None def setUp(self): - self.tfile = tempfile.NamedTemporaryFile(delete=False, mode='w+b') + self.tfile = tempfile.NamedTemporaryFile(delete=False, mode="w+b") self.tfile.write(salt.utils.stringutils.to_bytes(self.TEST_FILE_CONTENT)) self.tfile.close() @@ -59,145 +63,154 @@ class IniManageTestCase(TestCase): os.remove(self.tfile.name) def test_get_option(self): - ''' + """ Test get_option method. - ''' + """ + self.assertEqual(ini.get_option(self.tfile.name, "main", "test1"), "value 1") + self.assertEqual(ini.get_option(self.tfile.name, "main", "test2"), "value 2") self.assertEqual( - ini.get_option(self.tfile.name, 'main', 'test1'), - 'value 1') + ini.get_option(self.tfile.name, "SectionB", "test1"), "value 1B" + ) self.assertEqual( - ini.get_option(self.tfile.name, 'main', 'test2'), - 'value 2') + ini.get_option(self.tfile.name, "SectionB", "test3"), "value 3B" + ) self.assertEqual( - ini.get_option(self.tfile.name, 'SectionB', 'test1'), - 'value 1B') - self.assertEqual( - ini.get_option(self.tfile.name, 'SectionB', 'test3'), - 'value 3B') - self.assertEqual( - ini.get_option(self.tfile.name, 'SectionC', 'empty_option'), - '') + ini.get_option(self.tfile.name, "SectionC", "empty_option"), "" + ) def test_get_section(self): - ''' + """ Test get_section method. - ''' + """ self.assertEqual( - ini.get_section(self.tfile.name, 'SectionB'), - {'test1': 'value 1B', 'test3': 'value 3B'}) + ini.get_section(self.tfile.name, "SectionB"), + {"test1": "value 1B", "test3": "value 3B"}, + ) def test_remove_option(self): - ''' + """ Test remove_option method. - ''' + """ self.assertEqual( - ini.remove_option(self.tfile.name, 'SectionB', 'test1'), - 'value 1B') - self.assertIsNone(ini.get_option(self.tfile.name, 'SectionB', 'test1')) + ini.remove_option(self.tfile.name, "SectionB", "test1"), "value 1B" + ) + self.assertIsNone(ini.get_option(self.tfile.name, "SectionB", "test1")) def test_remove_section(self): - ''' + """ Test remove_section method. - ''' + """ self.assertEqual( - ini.remove_section(self.tfile.name, 'SectionB'), - {'test1': 'value 1B', 'test3': 'value 3B'}) - self.assertEqual(ini.get_section(self.tfile.name, 'SectionB'), {}) + ini.remove_section(self.tfile.name, "SectionB"), + {"test1": "value 1B", "test3": "value 3B"}, + ) + self.assertEqual(ini.get_section(self.tfile.name, "SectionB"), {}) def test_get_ini(self): - ''' + """ Test get_ini method. - ''' + """ self.assertEqual( - dict(ini.get_ini(self.tfile.name)), { - 'SectionC': {'empty_option': ''}, - 'SectionB': {'test1': 'value 1B', 'test3': 'value 3B'}, - 'main': {'test1': 'value 1', 'test2': 'value 2'}, - 'option2': 'main2', 'option1': 'main1'}) + dict(ini.get_ini(self.tfile.name)), + { + "SectionC": {"empty_option": ""}, + "SectionB": {"test1": "value 1B", "test3": "value 3B"}, + "main": {"test1": "value 1", "test2": "value 2"}, + "option2": "main2", + "option1": "main1", + }, + ) def test_set_option(self): - ''' + """ Test set_option method. - ''' - result = ini.set_option(self.tfile.name, { - 'SectionB': { - 'test3': 'new value 3B', - 'test_set_option': 'test_set_value' + """ + result = ini.set_option( + self.tfile.name, + { + "SectionB": { + "test3": "new value 3B", + "test_set_option": "test_set_value", + }, + "SectionD": {"test_set_option2": "test_set_value1"}, }, - 'SectionD': { - 'test_set_option2': 'test_set_value1' - } - }) - self.assertEqual(result, { - 'SectionB': {'test3': {'after': 'new value 3B', - 'before': 'value 3B'}, - 'test_set_option': {'after': 'test_set_value', - 'before': None}}, - 'SectionD': {'after': {'test_set_option2': 'test_set_value1'}, - 'before': None}}) + ) + self.assertEqual( + result, + { + "SectionB": { + "test3": {"after": "new value 3B", "before": "value 3B"}, + "test_set_option": {"after": "test_set_value", "before": None}, + }, + "SectionD": { + "after": {"test_set_option2": "test_set_value1"}, + "before": None, + }, + }, + ) # Check existing option updated self.assertEqual( - ini.get_option(self.tfile.name, 'SectionB', 'test3'), - 'new value 3B') + ini.get_option(self.tfile.name, "SectionB", "test3"), "new value 3B" + ) # Check new section and option added self.assertEqual( - ini.get_option(self.tfile.name, 'SectionD', 'test_set_option2'), - 'test_set_value1') + ini.get_option(self.tfile.name, "SectionD", "test_set_option2"), + "test_set_value1", + ) def test_empty_value(self): - ''' + """ Test empty value preserved after edit - ''' - ini.set_option(self.tfile.name, { - 'SectionB': {'test3': 'new value 3B'}, - }) - with salt.utils.files.fopen(self.tfile.name, 'r') as fp_: + """ + ini.set_option(self.tfile.name, {"SectionB": {"test3": "new value 3B"}}) + with salt.utils.files.fopen(self.tfile.name, "r") as fp_: file_content = salt.utils.stringutils.to_unicode(fp_.read()) - expected = '{0}{1}{0}'.format(os.linesep, 'empty_option = ') - self.assertIn(expected, file_content, 'empty_option was not preserved') + expected = "{0}{1}{0}".format(os.linesep, "empty_option = ") + self.assertIn(expected, file_content, "empty_option was not preserved") def test_empty_lines(self): - ''' + """ Test empty lines preserved after edit - ''' - ini.set_option(self.tfile.name, { - 'SectionB': {'test3': 'new value 3B'}, - }) - expected = os.linesep.join([ - '# Comment on the first line', - '', - '# First main option', - 'option1 = main1', - '', - '# Second main option', - 'option2 = main2', - '', - '[main]', - '# Another comment', - 'test1 = value 1', - '', - 'test2 = value 2', - '', - '[SectionB]', - 'test1 = value 1B', - '', - '# Blank line should be above', - 'test3 = new value 3B', - '', - '[SectionC]', - '# The following option is empty', - 'empty_option = ', - '' - ]) - with salt.utils.files.fopen(self.tfile.name, 'r') as fp_: + """ + ini.set_option(self.tfile.name, {"SectionB": {"test3": "new value 3B"}}) + expected = os.linesep.join( + [ + "# Comment on the first line", + "", + "# First main option", + "option1 = main1", + "", + "# Second main option", + "option2 = main2", + "", + "[main]", + "# Another comment", + "test1 = value 1", + "", + "test2 = value 2", + "", + "[SectionB]", + "test1 = value 1B", + "", + "# Blank line should be above", + "test3 = new value 3B", + "", + "[SectionC]", + "# The following option is empty", + "empty_option = ", + "", + ] + ) + with salt.utils.files.fopen(self.tfile.name, "r") as fp_: file_content = salt.utils.stringutils.to_unicode(fp_.read()) self.assertEqual(expected, file_content) def test_empty_lines_multiple_edits(self): - ''' + """ Test empty lines preserved after multiple edits - ''' - ini.set_option(self.tfile.name, { - 'SectionB': {'test3': 'this value will be edited two times'}, - }) + """ + ini.set_option( + self.tfile.name, + {"SectionB": {"test3": "this value will be edited two times"}}, + ) self.test_empty_lines() diff --git a/tests/unit/modules/test_introspect.py b/tests/unit/modules/test_introspect.py index 1c79f6a0ac4..1fdf0e68d48 100644 --- a/tests/unit/modules/test_introspect.py +++ b/tests/unit/modules/test_introspect.py @@ -1,77 +1,96 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.introspect as introspect +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class IntrospectTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.introspect - ''' + """ + def setup_loader_modules(self): return {introspect: {}} # 'running_service_owners' function tests: 1 def test_running_service_owners(self): - ''' + """ Test if it determine which packages own the currently running services. - ''' - err1 = ('The module for the package manager on this system does not' - ' support looking up which package(s) owns which file(s)') - err2 = ('The file module on this system does not ' - 'support looking up open files on the system') - ret = {'Error': {'Unsupported File Module': '{0}'.format(err2), - 'Unsupported Package Manager': '{0}'.format(err1)}} + """ + err1 = ( + "The module for the package manager on this system does not" + " support looking up which package(s) owns which file(s)" + ) + err2 = ( + "The file module on this system does not " + "support looking up open files on the system" + ) + ret = { + "Error": { + "Unsupported File Module": "{0}".format(err2), + "Unsupported Package Manager": "{0}".format(err1), + } + } self.assertDictEqual(introspect.running_service_owners(), ret) mock = MagicMock(return_value={}) - with patch.dict(introspect.__salt__, {'pkg.owner': mock, - 'file.open_files': mock, 'service.execs': mock}): + with patch.dict( + introspect.__salt__, + {"pkg.owner": mock, "file.open_files": mock, "service.execs": mock}, + ): self.assertDictEqual(introspect.running_service_owners(), {}) # 'enabled_service_owners' function tests: 1 def test_enabled_service_owners(self): - ''' + """ Test if it return which packages own each of the services that are currently enabled. - ''' - err1 = ('The module for the package manager on this system does not' - ' support looking up which package(s) owns which file(s)') - err2 = ('The module for the service manager on this system does not' - ' support showing descriptive service data') - ret = {'Error': {'Unsupported Service Manager': '{0}'.format(err2), - 'Unsupported Package Manager': '{0}'.format(err1)}} + """ + err1 = ( + "The module for the package manager on this system does not" + " support looking up which package(s) owns which file(s)" + ) + err2 = ( + "The module for the service manager on this system does not" + " support showing descriptive service data" + ) + ret = { + "Error": { + "Unsupported Service Manager": "{0}".format(err2), + "Unsupported Package Manager": "{0}".format(err1), + } + } self.assertDictEqual(introspect.enabled_service_owners(), ret) mock = MagicMock(return_value={}) - with patch.dict(introspect.__salt__, {'pkg.owner': mock, - 'service.show': mock, 'service.get_enabled': mock}): + with patch.dict( + introspect.__salt__, + {"pkg.owner": mock, "service.show": mock, "service.get_enabled": mock}, + ): self.assertDictEqual(introspect.enabled_service_owners(), {}) # 'service_highstate' function tests: 1 def test_service_highstate(self): - ''' + """ Test if it return running and enabled services in a highstate structure. - ''' - with patch('salt.modules.introspect.running_service_owners', - MagicMock(return_value={})), \ - patch('salt.modules.introspect.enabled_service_owners', - MagicMock(return_value={})): + """ + with patch( + "salt.modules.introspect.running_service_owners", MagicMock(return_value={}) + ), patch( + "salt.modules.introspect.enabled_service_owners", MagicMock(return_value={}) + ): self.assertDictEqual(introspect.service_highstate(), {}) diff --git a/tests/unit/modules/test_iosconfig.py b/tests/unit/modules/test_iosconfig.py index 90b528e5e09..ce053f434e0 100644 --- a/tests/unit/modules/test_iosconfig.py +++ b/tests/unit/modules/test_iosconfig.py @@ -1,24 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" Test the iosconfig Execution module. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import textwrap +import salt.modules.iosconfig as iosconfig + +# Import Salt modules +from salt.utils.odict import OrderedDict + # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -# Import Salt modules -from salt.utils.odict import OrderedDict -import salt.modules.iosconfig as iosconfig - class TestModulesIOSConfig(TestCase, LoaderModuleMockMixin): - running_config = textwrap.dedent('''\ + running_config = textwrap.dedent( + """\ interface GigabitEthernet1 ip address dhcp negotiation auto @@ -33,9 +35,11 @@ class TestModulesIOSConfig(TestCase, LoaderModuleMockMixin): no ip address shutdown negotiation auto - !''') + !""" + ) - candidate_config = textwrap.dedent('''\ + candidate_config = textwrap.dedent( + """\ interface GigabitEthernet1 ip address dhcp negotiation auto @@ -54,9 +58,11 @@ class TestModulesIOSConfig(TestCase, LoaderModuleMockMixin): bgp log-neighbor-changes neighbor 1.1.1.1 remote-as 12345 ! - !''') + !""" + ) - merge_config = textwrap.dedent('''\ + merge_config = textwrap.dedent( + """\ router bgp 65000 bgp log-neighbor-changes neighbor 1.1.1.1 remote-as 12345 @@ -65,34 +71,53 @@ class TestModulesIOSConfig(TestCase, LoaderModuleMockMixin): virtual-service csr_mgmt ! ip forward-protocol nd - !''') + !""" + ) def setup_loader_modules(self): return {} def test_tree(self): - running_config_tree = OrderedDict([ - (u'interface GigabitEthernet1', OrderedDict([ - (u'ip address dhcp', OrderedDict()), - (u'negotiation auto', OrderedDict()), - (u'no mop enabled', OrderedDict()) - ])), - (u'interface GigabitEthernet2', OrderedDict([ - (u'ip address 172.20.0.1 255.255.255.0', OrderedDict()), - (u'shutdown', OrderedDict()), - (u'negotiation auto', OrderedDict()) - ])), - (u'interface GigabitEthernet3', OrderedDict([ - (u'no ip address', OrderedDict()), - (u'shutdown', OrderedDict()), - (u'negotiation auto', OrderedDict()) - ])) - ]) + running_config_tree = OrderedDict( + [ + ( + "interface GigabitEthernet1", + OrderedDict( + [ + ("ip address dhcp", OrderedDict()), + ("negotiation auto", OrderedDict()), + ("no mop enabled", OrderedDict()), + ] + ), + ), + ( + "interface GigabitEthernet2", + OrderedDict( + [ + ("ip address 172.20.0.1 255.255.255.0", OrderedDict()), + ("shutdown", OrderedDict()), + ("negotiation auto", OrderedDict()), + ] + ), + ), + ( + "interface GigabitEthernet3", + OrderedDict( + [ + ("no ip address", OrderedDict()), + ("shutdown", OrderedDict()), + ("negotiation auto", OrderedDict()), + ] + ), + ), + ] + ) tree = iosconfig.tree(config=self.running_config) self.assertEqual(tree, running_config_tree) def test_clean(self): - clean_running_config = textwrap.dedent('''\ + clean_running_config = textwrap.dedent( + """\ interface GigabitEthernet1 ip address dhcp negotiation auto @@ -105,40 +130,65 @@ class TestModulesIOSConfig(TestCase, LoaderModuleMockMixin): no ip address shutdown negotiation auto - ''') + """ + ) clean = iosconfig.clean(config=self.running_config) self.assertEqual(clean, clean_running_config) def test_merge_tree(self): - expected_merge_tree = OrderedDict([ - (u'interface GigabitEthernet1', OrderedDict([ - (u'ip address dhcp', OrderedDict()), - (u'negotiation auto', OrderedDict()), - (u'no mop enabled', OrderedDict()) - ])), - (u'interface GigabitEthernet2', OrderedDict([ - (u'ip address 172.20.0.1 255.255.255.0', OrderedDict()), - (u'shutdown', OrderedDict()), - (u'negotiation auto', OrderedDict()) - ])), - (u'interface GigabitEthernet3', OrderedDict([ - (u'no ip address', OrderedDict()), - (u'shutdown', OrderedDict()), - (u'negotiation auto', OrderedDict()) - ])), - (u'router bgp 65000', OrderedDict([ - (u'bgp log-neighbor-changes', OrderedDict()), - (u'neighbor 1.1.1.1 remote-as 12345', OrderedDict()) - ])), - (u'virtual-service csr_mgmt', OrderedDict()), - (u'ip forward-protocol nd', OrderedDict()) - ]) - merge_tree = iosconfig.merge_tree(initial_config=self.running_config, - merge_config=self.merge_config) + expected_merge_tree = OrderedDict( + [ + ( + "interface GigabitEthernet1", + OrderedDict( + [ + ("ip address dhcp", OrderedDict()), + ("negotiation auto", OrderedDict()), + ("no mop enabled", OrderedDict()), + ] + ), + ), + ( + "interface GigabitEthernet2", + OrderedDict( + [ + ("ip address 172.20.0.1 255.255.255.0", OrderedDict()), + ("shutdown", OrderedDict()), + ("negotiation auto", OrderedDict()), + ] + ), + ), + ( + "interface GigabitEthernet3", + OrderedDict( + [ + ("no ip address", OrderedDict()), + ("shutdown", OrderedDict()), + ("negotiation auto", OrderedDict()), + ] + ), + ), + ( + "router bgp 65000", + OrderedDict( + [ + ("bgp log-neighbor-changes", OrderedDict()), + ("neighbor 1.1.1.1 remote-as 12345", OrderedDict()), + ] + ), + ), + ("virtual-service csr_mgmt", OrderedDict()), + ("ip forward-protocol nd", OrderedDict()), + ] + ) + merge_tree = iosconfig.merge_tree( + initial_config=self.running_config, merge_config=self.merge_config + ) self.assertEqual(merge_tree, expected_merge_tree) def test_merge_text(self): - extected_merge_text = textwrap.dedent('''\ + extected_merge_text = textwrap.dedent( + """\ interface GigabitEthernet1 ip address dhcp negotiation auto @@ -156,13 +206,16 @@ class TestModulesIOSConfig(TestCase, LoaderModuleMockMixin): neighbor 1.1.1.1 remote-as 12345 virtual-service csr_mgmt ip forward-protocol nd - ''') - merge_text = iosconfig.merge_text(initial_config=self.running_config, - merge_config=self.merge_config) + """ + ) + merge_text = iosconfig.merge_text( + initial_config=self.running_config, merge_config=self.merge_config + ) self.assertEqual(merge_text, extected_merge_text) def test_merge_diff(self): - expected_diff = textwrap.dedent('''\ + expected_diff = textwrap.dedent( + """\ @@ -10,3 +10,8 @@ no ip address shutdown @@ -172,13 +225,16 @@ class TestModulesIOSConfig(TestCase, LoaderModuleMockMixin): + neighbor 1.1.1.1 remote-as 12345 +virtual-service csr_mgmt +ip forward-protocol nd - ''') - diff = iosconfig.merge_diff(initial_config=self.running_config, - merge_config=self.merge_config) + """ + ) + diff = iosconfig.merge_diff( + initial_config=self.running_config, merge_config=self.merge_config + ) self.assertEqual(diff.splitlines()[2:], expected_diff.splitlines()) def test_diff_text(self): - expected_diff = textwrap.dedent('''\ + expected_diff = textwrap.dedent( + """\ @@ -3,10 +3,12 @@ negotiation auto no mop enabled @@ -194,7 +250,9 @@ class TestModulesIOSConfig(TestCase, LoaderModuleMockMixin): +router bgp 65000 + bgp log-neighbor-changes + neighbor 1.1.1.1 remote-as 12345 - ''') - diff = iosconfig.diff_text(candidate_config=self.candidate_config, - running_config=self.running_config) + """ + ) + diff = iosconfig.diff_text( + candidate_config=self.candidate_config, running_config=self.running_config + ) self.assertEqual(diff.splitlines()[2:], expected_diff.splitlines()) diff --git a/tests/unit/modules/test_ipset.py b/tests/unit/modules/test_ipset.py index d43cbc98c6a..e7879bdcfb6 100644 --- a/tests/unit/modules/test_ipset.py +++ b/tests/unit/modules/test_ipset.py @@ -1,247 +1,261 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.ipset as ipset +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class IpsetTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.aptpkg - ''' + """ + def setup_loader_modules(self): return {ipset: {}} def test_version(self): - ''' + """ Test for Return version from ipset --version - ''' - with patch.object(ipset, '_ipset_cmd', return_value='A'): + """ + with patch.object(ipset, "_ipset_cmd", return_value="A"): mock = MagicMock(return_value="A\nB\nC") - with patch.dict(ipset.__salt__, {'cmd.run': mock}): - self.assertEqual(ipset.version(), 'B') + with patch.dict(ipset.__salt__, {"cmd.run": mock}): + self.assertEqual(ipset.version(), "B") def test_new_set(self): - ''' + """ Test for Create new custom set - ''' - self.assertEqual(ipset.new_set(), 'Error: Set needs to be specified') + """ + self.assertEqual(ipset.new_set(), "Error: Set needs to be specified") - self.assertEqual(ipset.new_set('s'), - 'Error: Set Type needs to be specified') + self.assertEqual(ipset.new_set("s"), "Error: Set Type needs to be specified") - self.assertEqual(ipset.new_set('s', 'd'), 'Error: Set Type is invalid') + self.assertEqual(ipset.new_set("s", "d"), "Error: Set Type is invalid") - self.assertEqual(ipset.new_set('s', 'bitmap:ip'), - 'Error: range is a required argument') + self.assertEqual( + ipset.new_set("s", "bitmap:ip"), "Error: range is a required argument" + ) mock = MagicMock(return_value=False) - with patch.dict(ipset.__salt__, {'cmd.run': mock}): - self.assertTrue(ipset.new_set('s', 'bitmap:ip', range='range')) + with patch.dict(ipset.__salt__, {"cmd.run": mock}): + self.assertTrue(ipset.new_set("s", "bitmap:ip", range="range")) def test_delete_set(self): - ''' + """ Test for Delete ipset set. - ''' - self.assertEqual(ipset.delete_set(), - 'Error: Set needs to be specified') + """ + self.assertEqual(ipset.delete_set(), "Error: Set needs to be specified") - with patch.object(ipset, '_ipset_cmd', return_value='A'): + with patch.object(ipset, "_ipset_cmd", return_value="A"): mock = MagicMock(return_value=True) - with patch.dict(ipset.__salt__, {'cmd.run': mock}): - self.assertTrue(ipset.delete_set('set', 'family')) + with patch.dict(ipset.__salt__, {"cmd.run": mock}): + self.assertTrue(ipset.delete_set("set", "family")) def test_rename_set(self): - ''' + """ Test for Delete ipset set. - ''' - self.assertEqual(ipset.rename_set(), - 'Error: Set needs to be specified') + """ + self.assertEqual(ipset.rename_set(), "Error: Set needs to be specified") - self.assertEqual(ipset.rename_set('s'), - 'Error: New name for set needs to be specified') + self.assertEqual( + ipset.rename_set("s"), "Error: New name for set needs to be specified" + ) - with patch.object(ipset, '_find_set_type', return_value=False): - self.assertEqual(ipset.rename_set('s', 'd'), - 'Error: Set does not exist') + with patch.object(ipset, "_find_set_type", return_value=False): + self.assertEqual(ipset.rename_set("s", "d"), "Error: Set does not exist") - with patch.object(ipset, '_find_set_type', return_value=True): - self.assertEqual(ipset.rename_set('s', 'd'), - 'Error: New Set already exists') + with patch.object(ipset, "_find_set_type", return_value=True): + self.assertEqual( + ipset.rename_set("s", "d"), "Error: New Set already exists" + ) - with patch.object(ipset, '_find_set_type', side_effect=[True, False]): - with patch.object(ipset, '_ipset_cmd', return_value='A'): + with patch.object(ipset, "_find_set_type", side_effect=[True, False]): + with patch.object(ipset, "_ipset_cmd", return_value="A"): mock = MagicMock(return_value=True) - with patch.dict(ipset.__salt__, {'cmd.run': mock}): - self.assertTrue(ipset.rename_set('set', 'new_set')) + with patch.dict(ipset.__salt__, {"cmd.run": mock}): + self.assertTrue(ipset.rename_set("set", "new_set")) def test_list_sets(self): - ''' + """ Test for List all ipset sets. - ''' - with patch.object(ipset, '_ipset_cmd', return_value='A'): + """ + with patch.object(ipset, "_ipset_cmd", return_value="A"): mock = MagicMock(return_value="A:a") - with patch.dict(ipset.__salt__, {'cmd.run': mock}): - self.assertEqual(ipset.list_sets(), [{'A': ''}]) + with patch.dict(ipset.__salt__, {"cmd.run": mock}): + self.assertEqual(ipset.list_sets(), [{"A": ""}]) def test_check_set(self): - ''' + """ Test for Check that given ipset set exists. - ''' - self.assertEqual(ipset.check_set(), 'Error: Set needs to be specified') + """ + self.assertEqual(ipset.check_set(), "Error: Set needs to be specified") - with patch.object(ipset, '_find_set_info', side_effect=[False, True]): - self.assertFalse(ipset.check_set('set')) - self.assertTrue(ipset.check_set('set')) + with patch.object(ipset, "_find_set_info", side_effect=[False, True]): + self.assertFalse(ipset.check_set("set")) + self.assertTrue(ipset.check_set("set")) def test_add(self): - ''' + """ Test for Append an entry to the specified set. - ''' - self.assertEqual(ipset.add(), 'Error: Set needs to be specified') + """ + self.assertEqual(ipset.add(), "Error: Set needs to be specified") - self.assertEqual(ipset.add('set'), - 'Error: Entry needs to be specified') + self.assertEqual(ipset.add("set"), "Error: Entry needs to be specified") - with patch.object(ipset, '_find_set_info', return_value=None): - self.assertEqual(ipset.add('set', 'entry'), - 'Error: Set set does not exist') + with patch.object(ipset, "_find_set_info", return_value=None): + self.assertEqual(ipset.add("set", "entry"), "Error: Set set does not exist") - mock = MagicMock(return_value={'Type': 'type', - 'Header': 'Header'}) - with patch.object(ipset, '_find_set_info', mock): - self.assertEqual(ipset.add('set', 'entry', timeout=0), - 'Error: Set set not created with timeout support') + mock = MagicMock(return_value={"Type": "type", "Header": "Header"}) + with patch.object(ipset, "_find_set_info", mock): + self.assertEqual( + ipset.add("set", "entry", timeout=0), + "Error: Set set not created with timeout support", + ) - self.assertEqual(ipset.add('set', 'entry', packets=0), - 'Error: Set set not created with \ -counters support') + self.assertEqual( + ipset.add("set", "entry", packets=0), + "Error: Set set not created with \ +counters support", + ) - self.assertEqual(ipset.add('set', 'entry', comment=0), - 'Error: Set set not created with \ -comment support') + self.assertEqual( + ipset.add("set", "entry", comment=0), + "Error: Set set not created with \ +comment support", + ) - mock = MagicMock(return_value={'Type': 'bitmap:ip', - 'Header': 'Header'}) - with patch.object(ipset, '_find_set_info', mock): - with patch.object(ipset, '_find_set_members', return_value='entry'): - self.assertEqual(ipset.add('set', 'entry'), - 'Warn: Entry entry already exists in set set') + mock = MagicMock(return_value={"Type": "bitmap:ip", "Header": "Header"}) + with patch.object(ipset, "_find_set_info", mock): + with patch.object(ipset, "_find_set_members", return_value="entry"): + self.assertEqual( + ipset.add("set", "entry"), + "Warn: Entry entry already exists in set set", + ) - with patch.object(ipset, '_find_set_members', return_value='A'): - mock = MagicMock(return_value='') - with patch.dict(ipset.__salt__, {'cmd.run': mock}): - self.assertEqual(ipset.add('set', 'entry'), 'Success') + with patch.object(ipset, "_find_set_members", return_value="A"): + mock = MagicMock(return_value="") + with patch.dict(ipset.__salt__, {"cmd.run": mock}): + self.assertEqual(ipset.add("set", "entry"), "Success") - mock = MagicMock(return_value='out') - with patch.dict(ipset.__salt__, {'cmd.run': mock}): - self.assertEqual(ipset.add('set', 'entry'), 'Error: out') + mock = MagicMock(return_value="out") + with patch.dict(ipset.__salt__, {"cmd.run": mock}): + self.assertEqual(ipset.add("set", "entry"), "Error: out") def test_delete(self): - ''' + """ Test for Delete an entry from the specified set. - ''' - self.assertEqual(ipset.delete(), 'Error: Set needs to be specified') + """ + self.assertEqual(ipset.delete(), "Error: Set needs to be specified") - self.assertEqual(ipset.delete('s'), - 'Error: Entry needs to be specified') + self.assertEqual(ipset.delete("s"), "Error: Entry needs to be specified") - with patch.object(ipset, '_find_set_type', return_value=None): - self.assertEqual(ipset.delete('set', 'entry'), - 'Error: Set set does not exist') + with patch.object(ipset, "_find_set_type", return_value=None): + self.assertEqual( + ipset.delete("set", "entry"), "Error: Set set does not exist" + ) - with patch.object(ipset, '_find_set_type', return_value=True): - with patch.object(ipset, '_ipset_cmd', return_value='A'): - mock = MagicMock(side_effect=['', 'A']) - with patch.dict(ipset.__salt__, {'cmd.run': mock}): - self.assertEqual(ipset.delete('set', 'entry'), 'Success') - self.assertEqual(ipset.delete('set', 'entry'), 'Error: A') + with patch.object(ipset, "_find_set_type", return_value=True): + with patch.object(ipset, "_ipset_cmd", return_value="A"): + mock = MagicMock(side_effect=["", "A"]) + with patch.dict(ipset.__salt__, {"cmd.run": mock}): + self.assertEqual(ipset.delete("set", "entry"), "Success") + self.assertEqual(ipset.delete("set", "entry"), "Error: A") def test_check(self): - ''' + """ Test for Check that an entry exists in the specified set. - ''' - self.assertEqual(ipset.check(), 'Error: Set needs to be specified') + """ + self.assertEqual(ipset.check(), "Error: Set needs to be specified") - self.assertEqual(ipset.check('s'), - 'Error: Entry needs to be specified') + self.assertEqual(ipset.check("s"), "Error: Entry needs to be specified") - with patch.object(ipset, '_find_set_type', return_value=None): - self.assertEqual(ipset.check('set', 'entry'), - 'Error: Set set does not exist') + with patch.object(ipset, "_find_set_type", return_value=None): + self.assertEqual( + ipset.check("set", "entry"), "Error: Set set does not exist" + ) - with patch.object(ipset, '_find_set_type', return_value='hash:ip'): - with patch.object(ipset, '_find_set_members', - side_effect=['entry', '', - ['192.168.0.4', '192.168.0.5'], - ['192.168.0.3'], ['192.168.0.6'], - ['192.168.0.4', '192.168.0.5'], - ['192.168.0.3'], ['192.168.0.6'], - ]): - self.assertTrue(ipset.check('set', 'entry')) - self.assertFalse(ipset.check('set', 'entry')) - self.assertTrue(ipset.check('set', '192.168.0.4/31')) - self.assertFalse(ipset.check('set', '192.168.0.4/31')) - self.assertFalse(ipset.check('set', '192.168.0.4/31')) - self.assertTrue(ipset.check('set', '192.168.0.4-192.168.0.5')) - self.assertFalse(ipset.check('set', '192.168.0.4-192.168.0.5')) - self.assertFalse(ipset.check('set', '192.168.0.4-192.168.0.5')) + with patch.object(ipset, "_find_set_type", return_value="hash:ip"): + with patch.object( + ipset, + "_find_set_members", + side_effect=[ + "entry", + "", + ["192.168.0.4", "192.168.0.5"], + ["192.168.0.3"], + ["192.168.0.6"], + ["192.168.0.4", "192.168.0.5"], + ["192.168.0.3"], + ["192.168.0.6"], + ], + ): + self.assertTrue(ipset.check("set", "entry")) + self.assertFalse(ipset.check("set", "entry")) + self.assertTrue(ipset.check("set", "192.168.0.4/31")) + self.assertFalse(ipset.check("set", "192.168.0.4/31")) + self.assertFalse(ipset.check("set", "192.168.0.4/31")) + self.assertTrue(ipset.check("set", "192.168.0.4-192.168.0.5")) + self.assertFalse(ipset.check("set", "192.168.0.4-192.168.0.5")) + self.assertFalse(ipset.check("set", "192.168.0.4-192.168.0.5")) - with patch.object(ipset, '_find_set_type', return_value='hash:net'): - with patch.object(ipset, '_find_set_members', - side_effect=['entry', '', - '192.168.0.4/31', '192.168.0.4/30', - '192.168.0.4/31', '192.168.0.4/30', - ]): - self.assertTrue(ipset.check('set', 'entry')) - self.assertFalse(ipset.check('set', 'entry')) - self.assertTrue(ipset.check('set', '192.168.0.4/31')) - self.assertFalse(ipset.check('set', '192.168.0.4/31')) - self.assertTrue(ipset.check('set', '192.168.0.4-192.168.0.5')) - self.assertFalse(ipset.check('set', '192.168.0.4-192.168.0.5')) + with patch.object(ipset, "_find_set_type", return_value="hash:net"): + with patch.object( + ipset, + "_find_set_members", + side_effect=[ + "entry", + "", + "192.168.0.4/31", + "192.168.0.4/30", + "192.168.0.4/31", + "192.168.0.4/30", + ], + ): + self.assertTrue(ipset.check("set", "entry")) + self.assertFalse(ipset.check("set", "entry")) + self.assertTrue(ipset.check("set", "192.168.0.4/31")) + self.assertFalse(ipset.check("set", "192.168.0.4/31")) + self.assertTrue(ipset.check("set", "192.168.0.4-192.168.0.5")) + self.assertFalse(ipset.check("set", "192.168.0.4-192.168.0.5")) def test_test(self): - ''' + """ Test for Test if an entry is in the specified set. - ''' - self.assertEqual(ipset.test(), 'Error: Set needs to be specified') + """ + self.assertEqual(ipset.test(), "Error: Set needs to be specified") - self.assertEqual(ipset.test('s'), - 'Error: Entry needs to be specified') + self.assertEqual(ipset.test("s"), "Error: Entry needs to be specified") - with patch.object(ipset, '_find_set_type', return_value=None): - self.assertEqual(ipset.test('set', 'entry'), - 'Error: Set set does not exist') + with patch.object(ipset, "_find_set_type", return_value=None): + self.assertEqual( + ipset.test("set", "entry"), "Error: Set set does not exist" + ) - with patch.object(ipset, '_find_set_type', return_value=True): - mock = MagicMock(side_effect=[{'retcode': 1}, {'retcode': -1}]) - with patch.dict(ipset.__salt__, {'cmd.run_all': mock}): - self.assertFalse(ipset.test('set', 'entry')) - self.assertTrue(ipset.test('set', 'entry')) + with patch.object(ipset, "_find_set_type", return_value=True): + mock = MagicMock(side_effect=[{"retcode": 1}, {"retcode": -1}]) + with patch.dict(ipset.__salt__, {"cmd.run_all": mock}): + self.assertFalse(ipset.test("set", "entry")) + self.assertTrue(ipset.test("set", "entry")) def test_flush(self): - ''' + """ Test for Flush entries in the specified set - ''' - with patch.object(ipset, '_find_set_type', return_value=None): - self.assertEqual(ipset.flush('set'), - 'Error: Set set does not exist') + """ + with patch.object(ipset, "_find_set_type", return_value=None): + self.assertEqual(ipset.flush("set"), "Error: Set set does not exist") - with patch.object(ipset, '_find_set_type', return_value=True): - mock = MagicMock(side_effect=['', 'A']) - with patch.dict(ipset.__salt__, {'cmd.run': mock}): - self.assertTrue(ipset.flush('set')) - self.assertFalse(ipset.flush('set')) + with patch.object(ipset, "_find_set_type", return_value=True): + mock = MagicMock(side_effect=["", "A"]) + with patch.dict(ipset.__salt__, {"cmd.run": mock}): + self.assertTrue(ipset.flush("set")) + self.assertFalse(ipset.flush("set")) diff --git a/tests/unit/modules/test_iptables.py b/tests/unit/modules/test_iptables.py index 870bfb92912..723d20ce691 100644 --- a/tests/unit/modules/test_iptables.py +++ b/tests/unit/modules/test_iptables.py @@ -1,480 +1,635 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import uuid +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import uuid # Import Salt Libs import salt.modules.iptables as iptables +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class IptablesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.iptables - ''' + """ + def setup_loader_modules(self): return {iptables: {}} # 'version' function tests: 1 def test_version(self): - ''' + """ Test if it return version from iptables --version - ''' - mock = MagicMock(return_value='iptables v1.4.21') - with patch.dict(iptables.__salt__, {'cmd.run': mock}): - self.assertEqual(iptables.version(), 'v1.4.21') + """ + mock = MagicMock(return_value="iptables v1.4.21") + with patch.dict(iptables.__salt__, {"cmd.run": mock}): + self.assertEqual(iptables.version(), "v1.4.21") # 'build_rule' function tests: 1 def test_build_rule(self): - ''' + """ Test if it build a well-formatted iptables rule based on kwargs. - ''' - with patch.object(iptables, '_has_option', MagicMock(return_value=True)): - self.assertEqual(iptables.build_rule(), '') + """ + with patch.object(iptables, "_has_option", MagicMock(return_value=True)): + self.assertEqual(iptables.build_rule(), "") - self.assertEqual(iptables.build_rule(name='ignored', state='ignored'), - '', - 'build_rule should ignore name and state') + self.assertEqual( + iptables.build_rule(name="ignored", state="ignored"), + "", + "build_rule should ignore name and state", + ) # Should properly negate bang-prefixed values - self.assertEqual(iptables.build_rule(**{'if': '!eth0'}), - '! -i eth0') + self.assertEqual(iptables.build_rule(**{"if": "!eth0"}), "! -i eth0") # Should properly negate "not"-prefixed values - self.assertEqual(iptables.build_rule(**{'if': 'not eth0'}), - '! -i eth0') + self.assertEqual(iptables.build_rule(**{"if": "not eth0"}), "! -i eth0") - self.assertEqual(iptables.build_rule(**{'protocol': 'tcp', 'syn': '!'}), - '-p tcp ! --syn') + self.assertEqual( + iptables.build_rule(**{"protocol": "tcp", "syn": "!"}), "-p tcp ! --syn" + ) - self.assertEqual(iptables.build_rule(dports=[80, 443], protocol='tcp'), - '-p tcp -m multiport --dports 80,443') + self.assertEqual( + iptables.build_rule(dports=[80, 443], protocol="tcp"), + "-p tcp -m multiport --dports 80,443", + ) - self.assertEqual(iptables.build_rule(dports='80,443', protocol='tcp'), - '-p tcp -m multiport --dports 80,443') + self.assertEqual( + iptables.build_rule(dports="80,443", protocol="tcp"), + "-p tcp -m multiport --dports 80,443", + ) # Should it really behave this way? - self.assertEqual(iptables.build_rule(dports=['!80', 443], - protocol='tcp'), - '-p tcp -m multiport ! --dports 80,443') + self.assertEqual( + iptables.build_rule(dports=["!80", 443], protocol="tcp"), + "-p tcp -m multiport ! --dports 80,443", + ) - self.assertEqual(iptables.build_rule(dports='!80,443', protocol='tcp'), - '-p tcp -m multiport ! --dports 80,443') + self.assertEqual( + iptables.build_rule(dports="!80,443", protocol="tcp"), + "-p tcp -m multiport ! --dports 80,443", + ) - self.assertEqual(iptables.build_rule(sports=[80, 443], protocol='tcp'), - '-p tcp -m multiport --sports 80,443') + self.assertEqual( + iptables.build_rule(sports=[80, 443], protocol="tcp"), + "-p tcp -m multiport --sports 80,443", + ) - self.assertEqual(iptables.build_rule(sports='80,443', protocol='tcp'), - '-p tcp -m multiport --sports 80,443') + self.assertEqual( + iptables.build_rule(sports="80,443", protocol="tcp"), + "-p tcp -m multiport --sports 80,443", + ) - self.assertEqual(iptables.build_rule('filter', 'INPUT', command='I', - position='3', full=True, - dports='protocol', jump='ACCEPT'), - 'Error: protocol must be specified') + self.assertEqual( + iptables.build_rule( + "filter", + "INPUT", + command="I", + position="3", + full=True, + dports="protocol", + jump="ACCEPT", + ), + "Error: protocol must be specified", + ) - self.assertEqual(iptables.build_rule('filter', 'INPUT', command='I', - position='3', full=True, - sports='protocol', jump='ACCEPT'), - 'Error: protocol must be specified') + self.assertEqual( + iptables.build_rule( + "filter", + "INPUT", + command="I", + position="3", + full=True, + sports="protocol", + jump="ACCEPT", + ), + "Error: protocol must be specified", + ) - self.assertEqual(iptables.build_rule('', 'INPUT', command='I', - position='3', full='True', - match='state', jump='ACCEPT'), - 'Error: Table needs to be specified') + self.assertEqual( + iptables.build_rule( + "", + "INPUT", + command="I", + position="3", + full="True", + match="state", + jump="ACCEPT", + ), + "Error: Table needs to be specified", + ) - self.assertEqual(iptables.build_rule('filter', '', command='I', - position='3', full='True', - match='state', jump='ACCEPT'), - 'Error: Chain needs to be specified') + self.assertEqual( + iptables.build_rule( + "filter", + "", + command="I", + position="3", + full="True", + match="state", + jump="ACCEPT", + ), + "Error: Chain needs to be specified", + ) - self.assertEqual(iptables.build_rule('filter', 'INPUT', command='', - position='3', full='True', - match='state', jump='ACCEPT'), - 'Error: Command needs to be specified') + self.assertEqual( + iptables.build_rule( + "filter", + "INPUT", + command="", + position="3", + full="True", + match="state", + jump="ACCEPT", + ), + "Error: Command needs to be specified", + ) # Test arguments that should appear after the --jump - self.assertEqual(iptables.build_rule(jump='REDIRECT', - **{'to-port': 8080}), - '--jump REDIRECT --to-port 8080') + self.assertEqual( + iptables.build_rule(jump="REDIRECT", **{"to-port": 8080}), + "--jump REDIRECT --to-port 8080", + ) # Should quote arguments with spaces, like log-prefix often has - self.assertEqual(iptables.build_rule(jump='LOG', - **{'log-prefix': 'long prefix'}), - '--jump LOG --log-prefix "long prefix"') + self.assertEqual( + iptables.build_rule(jump="LOG", **{"log-prefix": "long prefix"}), + '--jump LOG --log-prefix "long prefix"', + ) # Should quote arguments with leading or trailing spaces - self.assertEqual(iptables.build_rule(jump='LOG', - **{'log-prefix': 'spam: '}), - '--jump LOG --log-prefix "spam: "') + self.assertEqual( + iptables.build_rule(jump="LOG", **{"log-prefix": "spam: "}), + '--jump LOG --log-prefix "spam: "', + ) # Should allow no-arg jump options - self.assertEqual(iptables.build_rule(jump='CLUSTERIP', - **{'new': ''}), - '--jump CLUSTERIP --new') + self.assertEqual( + iptables.build_rule(jump="CLUSTERIP", **{"new": ""}), + "--jump CLUSTERIP --new", + ) # Should allow no-arg jump options as None - self.assertEqual(iptables.build_rule(jump='CT', - **{'notrack': None}), - '--jump CT --notrack') + self.assertEqual( + iptables.build_rule(jump="CT", **{"notrack": None}), + "--jump CT --notrack", + ) # should build match-sets with single string - self.assertEqual(iptables.build_rule(**{'match-set': 'src flag1,flag2'}), - '-m set --match-set src flag1,flag2') + self.assertEqual( + iptables.build_rule(**{"match-set": "src flag1,flag2"}), + "-m set --match-set src flag1,flag2", + ) # should build match-sets as list - match_sets = ['src1 flag1', - 'src2 flag2,flag3', - ] - self.assertEqual(iptables.build_rule(**{'match-set': match_sets}), - '-m set --match-set src1 flag1 -m set --match-set src2 flag2,flag3') + match_sets = [ + "src1 flag1", + "src2 flag2,flag3", + ] + self.assertEqual( + iptables.build_rule(**{"match-set": match_sets}), + "-m set --match-set src1 flag1 -m set --match-set src2 flag2,flag3", + ) # should handle negations for string match-sets - self.assertEqual(iptables.build_rule(**{'match-set': '!src flag'}), - '-m set ! --match-set src flag') + self.assertEqual( + iptables.build_rule(**{"match-set": "!src flag"}), + "-m set ! --match-set src flag", + ) # should handle negations for list match-sets - match_sets = ['src1 flag', - 'not src2 flag2'] - self.assertEqual(iptables.build_rule(**{'match-set': match_sets}), - '-m set --match-set src1 flag -m set ! --match-set src2 flag2') + match_sets = ["src1 flag", "not src2 flag2"] + self.assertEqual( + iptables.build_rule(**{"match-set": match_sets}), + "-m set --match-set src1 flag -m set ! --match-set src2 flag2", + ) # should allow escaped name - self.assertEqual(iptables.build_rule(**{'match': 'recent', 'name_': 'SSH'}), - '-m recent --name SSH') + self.assertEqual( + iptables.build_rule(**{"match": "recent", "name_": "SSH"}), + "-m recent --name SSH", + ) # should allow empty arguments - self.assertEqual(iptables.build_rule(**{'match': 'recent', 'update': None}), - '-m recent --update') + self.assertEqual( + iptables.build_rule(**{"match": "recent", "update": None}), + "-m recent --update", + ) # Should allow the --save jump option to CONNSECMARK - #self.assertEqual(iptables.build_rule(jump='CONNSECMARK', + # self.assertEqual(iptables.build_rule(jump='CONNSECMARK', # **{'save': ''}), # '--jump CONNSECMARK --save ') - ret = '/sbin/iptables --wait -t salt -I INPUT 3 -m state --jump ACCEPT' - with patch.object(iptables, '_iptables_cmd', - MagicMock(return_value='/sbin/iptables')): - self.assertEqual(iptables.build_rule('salt', 'INPUT', command='I', - position='3', full='True', - match='state', jump='ACCEPT'), - ret) + ret = "/sbin/iptables --wait -t salt -I INPUT 3 -m state --jump ACCEPT" + with patch.object( + iptables, "_iptables_cmd", MagicMock(return_value="/sbin/iptables") + ): + self.assertEqual( + iptables.build_rule( + "salt", + "INPUT", + command="I", + position="3", + full="True", + match="state", + jump="ACCEPT", + ), + ret, + ) # 'get_saved_rules' function tests: 1 def test_get_saved_rules(self): - ''' + """ Test if it return a data structure of the rules in the conf file - ''' + """ mock = MagicMock(return_value=False) - with patch.object(iptables, '_parse_conf', mock): + with patch.object(iptables, "_parse_conf", mock): self.assertFalse(iptables.get_saved_rules()) - mock.assert_called_with(conf_file=None, family='ipv4') + mock.assert_called_with(conf_file=None, family="ipv4") # 'get_rules' function tests: 1 def test_get_rules(self): - ''' + """ Test if it return a data structure of the current, in-memory rules - ''' + """ mock = MagicMock(return_value=False) - with patch.object(iptables, '_parse_conf', mock): + with patch.object(iptables, "_parse_conf", mock): self.assertFalse(iptables.get_rules()) - mock.assert_called_with(in_mem=True, family='ipv4') + mock.assert_called_with(in_mem=True, family="ipv4") # 'get_saved_policy' function tests: 1 def test_get_saved_policy(self): - ''' + """ Test if it return the current policy for the specified table/chain - ''' - self.assertEqual(iptables.get_saved_policy(table='filter', chain=None, - conf_file=None, - family='ipv4'), - 'Error: Chain needs to be specified') + """ + self.assertEqual( + iptables.get_saved_policy( + table="filter", chain=None, conf_file=None, family="ipv4" + ), + "Error: Chain needs to be specified", + ) - with patch.object(iptables, '_parse_conf', - MagicMock(return_value={'filter': - {'INPUT': - {'policy': True}}})): - self.assertTrue(iptables.get_saved_policy(table='filter', - chain='INPUT', - conf_file=None, - family='ipv4')) + with patch.object( + iptables, + "_parse_conf", + MagicMock(return_value={"filter": {"INPUT": {"policy": True}}}), + ): + self.assertTrue( + iptables.get_saved_policy( + table="filter", chain="INPUT", conf_file=None, family="ipv4" + ) + ) - with patch.object(iptables, '_parse_conf', - MagicMock(return_value={'filter': - {'INPUT': - {'policy1': True}}})): - self.assertIsNone(iptables.get_saved_policy(table='filter', - chain='INPUT', - conf_file=None, - family='ipv4')) + with patch.object( + iptables, + "_parse_conf", + MagicMock(return_value={"filter": {"INPUT": {"policy1": True}}}), + ): + self.assertIsNone( + iptables.get_saved_policy( + table="filter", chain="INPUT", conf_file=None, family="ipv4" + ) + ) # 'get_policy' function tests: 1 def test_get_policy(self): - ''' + """ Test if it return the current policy for the specified table/chain - ''' - self.assertEqual(iptables.get_policy(table='filter', chain=None, - family='ipv4'), - 'Error: Chain needs to be specified') + """ + self.assertEqual( + iptables.get_policy(table="filter", chain=None, family="ipv4"), + "Error: Chain needs to be specified", + ) - with patch.object(iptables, '_parse_conf', - MagicMock(return_value={'filter': - {'INPUT': - {'policy': True}}})): - self.assertTrue(iptables.get_policy(table='filter', - chain='INPUT', - family='ipv4')) + with patch.object( + iptables, + "_parse_conf", + MagicMock(return_value={"filter": {"INPUT": {"policy": True}}}), + ): + self.assertTrue( + iptables.get_policy(table="filter", chain="INPUT", family="ipv4") + ) - with patch.object(iptables, '_parse_conf', - MagicMock(return_value={'filter': - {'INPUT': - {'policy1': True}}})): - self.assertIsNone(iptables.get_policy(table='filter', - chain='INPUT', - family='ipv4')) + with patch.object( + iptables, + "_parse_conf", + MagicMock(return_value={"filter": {"INPUT": {"policy1": True}}}), + ): + self.assertIsNone( + iptables.get_policy(table="filter", chain="INPUT", family="ipv4") + ) # 'set_policy' function tests: 1 def test_set_policy(self): - ''' + """ Test if it set the current policy for the specified table/chain - ''' - with patch.object(iptables, '_has_option', MagicMock(return_value=True)): - self.assertEqual(iptables.set_policy(table='filter', chain=None, - policy=None, - family='ipv4'), - 'Error: Chain needs to be specified') + """ + with patch.object(iptables, "_has_option", MagicMock(return_value=True)): + self.assertEqual( + iptables.set_policy( + table="filter", chain=None, policy=None, family="ipv4" + ), + "Error: Chain needs to be specified", + ) - self.assertEqual(iptables.set_policy(table='filter', chain='INPUT', - policy=None, - family='ipv4'), - 'Error: Policy needs to be specified') + self.assertEqual( + iptables.set_policy( + table="filter", chain="INPUT", policy=None, family="ipv4" + ), + "Error: Policy needs to be specified", + ) mock = MagicMock(return_value=True) - with patch.dict(iptables.__salt__, {'cmd.run': mock}): - self.assertTrue(iptables.set_policy(table='filter', - chain='INPUT', - policy='ACCEPT', - family='ipv4')) + with patch.dict(iptables.__salt__, {"cmd.run": mock}): + self.assertTrue( + iptables.set_policy( + table="filter", chain="INPUT", policy="ACCEPT", family="ipv4" + ) + ) # 'save' function tests: 1 def test_save(self): - ''' + """ Test if it save the current in-memory rules to disk - ''' - with patch('salt.modules.iptables._conf', MagicMock(return_value=False)), \ - patch('os.path.isdir', MagicMock(return_value=True)): + """ + with patch("salt.modules.iptables._conf", MagicMock(return_value=False)), patch( + "os.path.isdir", MagicMock(return_value=True) + ): mock = MagicMock(return_value=True) - with patch.dict(iptables.__salt__, {'cmd.run': mock, - 'file.write': mock, - 'config.option': MagicMock(return_value=[])}): - self.assertTrue(iptables.save(filename='/xyz', family='ipv4')) + with patch.dict( + iptables.__salt__, + { + "cmd.run": mock, + "file.write": mock, + "config.option": MagicMock(return_value=[]), + }, + ): + self.assertTrue(iptables.save(filename="/xyz", family="ipv4")) # 'check' function tests: 1 def test_check(self): - ''' + """ Test if it check for the existence of a rule in the table and chain - ''' - self.assertEqual(iptables.check(table='filter', chain=None, - rule=None, - family='ipv4'), - 'Error: Chain needs to be specified') + """ + self.assertEqual( + iptables.check(table="filter", chain=None, rule=None, family="ipv4"), + "Error: Chain needs to be specified", + ) - self.assertEqual(iptables.check(table='filter', chain='INPUT', - rule=None, - family='ipv4'), - 'Error: Rule needs to be specified') + self.assertEqual( + iptables.check(table="filter", chain="INPUT", rule=None, family="ipv4"), + "Error: Rule needs to be specified", + ) - mock_rule = 'm state --state RELATED,ESTABLISHED -j ACCEPT' - mock_chain = 'INPUT' + mock_rule = "m state --state RELATED,ESTABLISHED -j ACCEPT" + mock_chain = "INPUT" mock_uuid = 31337 - mock_cmd = MagicMock(return_value='-A {0}\n-A {1}'.format(mock_chain, - hex(mock_uuid))) + mock_cmd = MagicMock( + return_value="-A {0}\n-A {1}".format(mock_chain, hex(mock_uuid)) + ) mock_has = MagicMock(return_value=True) mock_not = MagicMock(return_value=False) - with patch.object(iptables, '_has_option', mock_not): - with patch.object(uuid, 'getnode', MagicMock(return_value=mock_uuid)): - with patch.dict(iptables.__salt__, {'cmd.run': mock_cmd}): - self.assertTrue(iptables.check(table='filter', chain=mock_chain, - rule=mock_rule, family='ipv4')) + with patch.object(iptables, "_has_option", mock_not): + with patch.object(uuid, "getnode", MagicMock(return_value=mock_uuid)): + with patch.dict(iptables.__salt__, {"cmd.run": mock_cmd}): + self.assertTrue( + iptables.check( + table="filter", + chain=mock_chain, + rule=mock_rule, + family="ipv4", + ) + ) - mock_cmd = MagicMock(return_value='') + mock_cmd = MagicMock(return_value="") - with patch.object(iptables, '_has_option', mock_not): - with patch.object(uuid, 'getnode', MagicMock(return_value=mock_uuid)): - with patch.dict(iptables.__salt__, {'cmd.run': MagicMock(return_value='')}): - self.assertFalse(iptables.check(table='filter', chain=mock_chain, - rule=mock_rule, family='ipv4')) + with patch.object(iptables, "_has_option", mock_not): + with patch.object(uuid, "getnode", MagicMock(return_value=mock_uuid)): + with patch.dict( + iptables.__salt__, {"cmd.run": MagicMock(return_value="")} + ): + self.assertFalse( + iptables.check( + table="filter", + chain=mock_chain, + rule=mock_rule, + family="ipv4", + ) + ) - with patch.object(iptables, '_has_option', mock_has): - with patch.dict(iptables.__salt__, {'cmd.run': mock_cmd}): - self.assertTrue(iptables.check(table='filter', chain='INPUT', - rule=mock_rule, family='ipv4')) + with patch.object(iptables, "_has_option", mock_has): + with patch.dict(iptables.__salt__, {"cmd.run": mock_cmd}): + self.assertTrue( + iptables.check( + table="filter", chain="INPUT", rule=mock_rule, family="ipv4" + ) + ) - mock_cmd = MagicMock(return_value='-A 0x4d2') + mock_cmd = MagicMock(return_value="-A 0x4d2") mock_uuid = MagicMock(return_value=1234) - with patch.object(iptables, '_has_option', mock_has): - with patch.object(uuid, 'getnode', mock_uuid): - with patch.dict(iptables.__salt__, {'cmd.run': mock_cmd}): - self.assertTrue(iptables.check(table='filter', - chain='0x4d2', - rule=mock_rule, family='ipv4')) + with patch.object(iptables, "_has_option", mock_has): + with patch.object(uuid, "getnode", mock_uuid): + with patch.dict(iptables.__salt__, {"cmd.run": mock_cmd}): + self.assertTrue( + iptables.check( + table="filter", chain="0x4d2", rule=mock_rule, family="ipv4" + ) + ) # 'check_chain' function tests: 1 def test_check_chain(self): - ''' + """ Test if it check for the existence of a chain in the table - ''' - self.assertEqual(iptables.check_chain(table='filter', chain=None, - family='ipv4'), - 'Error: Chain needs to be specified') + """ + self.assertEqual( + iptables.check_chain(table="filter", chain=None, family="ipv4"), + "Error: Chain needs to be specified", + ) - mock_cmd = MagicMock(return_value='') - with patch.dict(iptables.__salt__, {'cmd.run': mock_cmd}): - self.assertFalse(iptables.check_chain(table='filter', - chain='INPUT', - family='ipv4')) + mock_cmd = MagicMock(return_value="") + with patch.dict(iptables.__salt__, {"cmd.run": mock_cmd}): + self.assertFalse( + iptables.check_chain(table="filter", chain="INPUT", family="ipv4") + ) # 'new_chain' function tests: 1 def test_new_chain(self): - ''' + """ Test if it create new custom chain to the specified table. - ''' - self.assertEqual(iptables.new_chain(table='filter', chain=None, - family='ipv4'), - 'Error: Chain needs to be specified') + """ + self.assertEqual( + iptables.new_chain(table="filter", chain=None, family="ipv4"), + "Error: Chain needs to be specified", + ) - mock_cmd = MagicMock(return_value='') - with patch.dict(iptables.__salt__, {'cmd.run': mock_cmd}): - self.assertTrue(iptables.new_chain(table='filter', - chain='INPUT', - family='ipv4')) + mock_cmd = MagicMock(return_value="") + with patch.dict(iptables.__salt__, {"cmd.run": mock_cmd}): + self.assertTrue( + iptables.new_chain(table="filter", chain="INPUT", family="ipv4") + ) # 'delete_chain' function tests: 1 def test_delete_chain(self): - ''' + """ Test if it delete custom chain to the specified table. - ''' - self.assertEqual(iptables.delete_chain(table='filter', chain=None, - family='ipv4'), - 'Error: Chain needs to be specified') + """ + self.assertEqual( + iptables.delete_chain(table="filter", chain=None, family="ipv4"), + "Error: Chain needs to be specified", + ) - mock_cmd = MagicMock(return_value='') - with patch.dict(iptables.__salt__, {'cmd.run': mock_cmd}): - self.assertTrue(iptables.delete_chain(table='filter', - chain='INPUT', - family='ipv4')) + mock_cmd = MagicMock(return_value="") + with patch.dict(iptables.__salt__, {"cmd.run": mock_cmd}): + self.assertTrue( + iptables.delete_chain(table="filter", chain="INPUT", family="ipv4") + ) # 'append' function tests: 1 def test_append(self): - ''' + """ Test if it append a rule to the specified table/chain. - ''' - with patch.object(iptables, '_has_option', MagicMock(return_value=True)), \ - patch.object(iptables, 'check', MagicMock(return_value=False)): - self.assertEqual(iptables.append(table='filter', chain=None, - rule=None, - family='ipv4'), - 'Error: Chain needs to be specified') + """ + with patch.object( + iptables, "_has_option", MagicMock(return_value=True) + ), patch.object(iptables, "check", MagicMock(return_value=False)): + self.assertEqual( + iptables.append(table="filter", chain=None, rule=None, family="ipv4"), + "Error: Chain needs to be specified", + ) - self.assertEqual(iptables.append(table='filter', chain='INPUT', - rule=None, - family='ipv4'), - 'Error: Rule needs to be specified') + self.assertEqual( + iptables.append( + table="filter", chain="INPUT", rule=None, family="ipv4" + ), + "Error: Rule needs to be specified", + ) - _rule = 'm state --state RELATED,ESTABLISHED -j ACCEPT' - mock = MagicMock(side_effect=['', 'SALT']) - with patch.dict(iptables.__salt__, {'cmd.run': mock}): - self.assertTrue(iptables.append(table='filter', chain='INPUT', - rule=_rule, family='ipv4')) + _rule = "m state --state RELATED,ESTABLISHED -j ACCEPT" + mock = MagicMock(side_effect=["", "SALT"]) + with patch.dict(iptables.__salt__, {"cmd.run": mock}): + self.assertTrue( + iptables.append( + table="filter", chain="INPUT", rule=_rule, family="ipv4" + ) + ) - self.assertFalse(iptables.append(table='filter', chain='INPUT', - rule=_rule, family='ipv4')) + self.assertFalse( + iptables.append( + table="filter", chain="INPUT", rule=_rule, family="ipv4" + ) + ) # 'insert' function tests: 1 def test_insert(self): - ''' + """ Test if it insert a rule into the specified table/chain, at the specified position. - ''' - with patch.object(iptables, '_has_option', MagicMock(return_value=True)), \ - patch.object(iptables, 'check', MagicMock(return_value=False)): - self.assertEqual(iptables.insert(table='filter', chain=None, - position=None, rule=None, - family='ipv4'), - 'Error: Chain needs to be specified') + """ + with patch.object( + iptables, "_has_option", MagicMock(return_value=True) + ), patch.object(iptables, "check", MagicMock(return_value=False)): + self.assertEqual( + iptables.insert( + table="filter", chain=None, position=None, rule=None, family="ipv4" + ), + "Error: Chain needs to be specified", + ) - pos_err = 'Error: Position needs to be specified or use append (-A)' - self.assertEqual(iptables.insert(table='filter', chain='INPUT', - position=None, rule=None, - family='ipv4'), pos_err) + pos_err = "Error: Position needs to be specified or use append (-A)" + self.assertEqual( + iptables.insert( + table="filter", + chain="INPUT", + position=None, + rule=None, + family="ipv4", + ), + pos_err, + ) - self.assertEqual(iptables.insert(table='filter', chain='INPUT', - position=3, rule=None, - family='ipv4'), - 'Error: Rule needs to be specified') + self.assertEqual( + iptables.insert( + table="filter", chain="INPUT", position=3, rule=None, family="ipv4" + ), + "Error: Rule needs to be specified", + ) - _rule = 'm state --state RELATED,ESTABLISHED -j ACCEPT' + _rule = "m state --state RELATED,ESTABLISHED -j ACCEPT" mock = MagicMock(return_value=True) - with patch.dict(iptables.__salt__, {'cmd.run': mock}): - self.assertTrue(iptables.insert(table='filter', chain='INPUT', - position=3, rule=_rule, - family='ipv4')) + with patch.dict(iptables.__salt__, {"cmd.run": mock}): + self.assertTrue( + iptables.insert( + table="filter", + chain="INPUT", + position=3, + rule=_rule, + family="ipv4", + ) + ) # 'delete' function tests: 1 def test_delete(self): - ''' + """ Test if it delete a rule from the specified table/chain - ''' - with patch.object(iptables, '_has_option', MagicMock(return_value=True)): - _rule = 'm state --state RELATED,ESTABLISHED -j ACCEPT' - self.assertEqual(iptables.delete(table='filter', chain=None, - position=3, rule=_rule, - family='ipv4'), - 'Error: Only specify a position or a rule, not both') + """ + with patch.object(iptables, "_has_option", MagicMock(return_value=True)): + _rule = "m state --state RELATED,ESTABLISHED -j ACCEPT" + self.assertEqual( + iptables.delete( + table="filter", chain=None, position=3, rule=_rule, family="ipv4" + ), + "Error: Only specify a position or a rule, not both", + ) mock = MagicMock(return_value=True) - with patch.dict(iptables.__salt__, {'cmd.run': mock}): - self.assertTrue(iptables.delete(table='filter', chain='INPUT', - position=3, rule='', - family='ipv4')) + with patch.dict(iptables.__salt__, {"cmd.run": mock}): + self.assertTrue( + iptables.delete( + table="filter", + chain="INPUT", + position=3, + rule="", + family="ipv4", + ) + ) # 'flush' function tests: 1 def test_flush(self): - ''' + """ Test if it flush the chain in the specified table, flush all chains in the specified table if not specified chain. - ''' - with patch.object(iptables, '_has_option', MagicMock(return_value=True)): + """ + with patch.object(iptables, "_has_option", MagicMock(return_value=True)): mock_cmd = MagicMock(return_value=True) - with patch.dict(iptables.__salt__, {'cmd.run': mock_cmd}): - self.assertTrue(iptables.flush(table='filter', - chain='INPUT', - family='ipv4')) + with patch.dict(iptables.__salt__, {"cmd.run": mock_cmd}): + self.assertTrue( + iptables.flush(table="filter", chain="INPUT", family="ipv4") + ) diff --git a/tests/unit/modules/test_jboss7.py b/tests/unit/modules/test_jboss7.py index 8e794359933..098096182a3 100644 --- a/tests/unit/modules/test_jboss7.py +++ b/tests/unit/modules/test_jboss7.py @@ -3,14 +3,15 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import salt testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock +import salt.modules.jboss7 as jboss7 # Import salt libs from salt.utils.odict import OrderedDict -import salt.modules.jboss7 as jboss7 + +# Import salt testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock +from tests.support.unit import TestCase class JBoss7TestCase(TestCase, LoaderModuleMockMixin): @@ -20,221 +21,272 @@ class JBoss7TestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): self.org_run_operation = MagicMock() - self.addCleanup(delattr, self, 'org_run_operation') + self.addCleanup(delattr, self, "org_run_operation") return { - jboss7: { - '__salt__': { - 'jboss7_cli.run_operation': self.org_run_operation - } - } + jboss7: {"__salt__": {"jboss7_cli.run_operation": self.org_run_operation}} } def test_create_simple_binding(self): - jboss7.create_simple_binding(self.jboss_config, 'java:global/env', 'DEV') + jboss7.create_simple_binding(self.jboss_config, "java:global/env", "DEV") - self.org_run_operation.assert_called_with(self.jboss_config, '/subsystem=naming/binding="java:global/env":add(binding-type=simple, value="DEV")') + self.org_run_operation.assert_called_with( + self.jboss_config, + '/subsystem=naming/binding="java:global/env":add(binding-type=simple, value="DEV")', + ) def test_create_simple_binding_with_backslash(self): - jboss7.create_simple_binding(self.jboss_config, 'java:global/env', r'DEV\2') + jboss7.create_simple_binding(self.jboss_config, "java:global/env", r"DEV\2") - self.org_run_operation.assert_called_with(self.jboss_config, r'/subsystem=naming/binding="java:global/env":add(binding-type=simple, value="DEV\\\\2")') + self.org_run_operation.assert_called_with( + self.jboss_config, + r'/subsystem=naming/binding="java:global/env":add(binding-type=simple, value="DEV\\\\2")', + ) def test_update_binding(self): - jboss7.update_simple_binding(self.jboss_config, 'java:global/env', 'INT') + jboss7.update_simple_binding(self.jboss_config, "java:global/env", "INT") - self.org_run_operation.assert_called_with(self.jboss_config, '/subsystem=naming/binding="java:global/env":write-attribute(name=value, value="INT")') + self.org_run_operation.assert_called_with( + self.jboss_config, + '/subsystem=naming/binding="java:global/env":write-attribute(name=value, value="INT")', + ) def test_update_binding_with_backslash(self): - jboss7.update_simple_binding(self.jboss_config, 'java:global/env', r'INT\2') + jboss7.update_simple_binding(self.jboss_config, "java:global/env", r"INT\2") - self.org_run_operation.assert_called_with(self.jboss_config, r'/subsystem=naming/binding="java:global/env":write-attribute(name=value, value="INT\\\\2")') + self.org_run_operation.assert_called_with( + self.jboss_config, + r'/subsystem=naming/binding="java:global/env":write-attribute(name=value, value="INT\\\\2")', + ) def test_read_binding(self): def cli_command_response(jboss_config, cli_command): - if cli_command == '/subsystem=naming/binding="java:global/env":read-resource': - return {'outcome': 'success', - 'result': { - 'binding-type': 'simple', - 'value': 'DEV' - } + if ( + cli_command + == '/subsystem=naming/binding="java:global/env":read-resource' + ): + return { + "outcome": "success", + "result": {"binding-type": "simple", "value": "DEV"}, } self.org_run_operation.side_effect = cli_command_response - result = jboss7.read_simple_binding(self.jboss_config, 'java:global/env') - self.assertEqual(result['outcome'], 'success') - self.assertEqual(result['result']['value'], 'DEV') + result = jboss7.read_simple_binding(self.jboss_config, "java:global/env") + self.assertEqual(result["outcome"], "success") + self.assertEqual(result["result"]["value"], "DEV") def test_create_datasource_all_properties_included(self): def cli_command_response(jboss_config, cli_command, fail_on_error=False): - if cli_command == '/subsystem=datasources/data-source="appDS":read-resource-description': - return {'outcome': 'success', - 'result': { - 'attributes': { - 'driver-name': {'type': 'STRING'}, - 'connection-url': {'type': 'STRING'}, - 'jndi-name': {'type': 'STRING'}, - 'user-name': {'type': 'STRING'}, - 'password': {'type': 'STRING'} - } + if ( + cli_command + == '/subsystem=datasources/data-source="appDS":read-resource-description' + ): + return { + "outcome": "success", + "result": { + "attributes": { + "driver-name": {"type": "STRING"}, + "connection-url": {"type": "STRING"}, + "jndi-name": {"type": "STRING"}, + "user-name": {"type": "STRING"}, + "password": {"type": "STRING"}, } + }, } self.org_run_operation.side_effect = cli_command_response datasource_properties = OrderedDict() - datasource_properties['driver-name'] = 'mysql' - datasource_properties['connection-url'] = 'jdbc:mysql://localhost:3306/app' - datasource_properties['jndi-name'] = 'java:jboss/datasources/appDS' - datasource_properties['user-name'] = 'app' - datasource_properties['password'] = 'app_password' + datasource_properties["driver-name"] = "mysql" + datasource_properties["connection-url"] = "jdbc:mysql://localhost:3306/app" + datasource_properties["jndi-name"] = "java:jboss/datasources/appDS" + datasource_properties["user-name"] = "app" + datasource_properties["password"] = "app_password" - jboss7.create_datasource(self.jboss_config, 'appDS', datasource_properties) + jboss7.create_datasource(self.jboss_config, "appDS", datasource_properties) - self.org_run_operation.assert_called_with(self.jboss_config, '/subsystem=datasources/data-source="appDS":add(driver-name="mysql",connection-url="jdbc:mysql://localhost:3306/app",jndi-name="java:jboss/datasources/appDS",user-name="app",password="app_password")', fail_on_error=False) + self.org_run_operation.assert_called_with( + self.jboss_config, + '/subsystem=datasources/data-source="appDS":add(driver-name="mysql",connection-url="jdbc:mysql://localhost:3306/app",jndi-name="java:jboss/datasources/appDS",user-name="app",password="app_password")', + fail_on_error=False, + ) def test_create_datasource_format_boolean_value_when_string(self): def cli_command_response(jboss_config, cli_command, fail_on_error=False): - if cli_command == '/subsystem=datasources/data-source="appDS":read-resource-description': - return {'outcome': 'success', - 'result': { - 'attributes': { - 'use-ccm': {'type': 'BOOLEAN'} - } - } + if ( + cli_command + == '/subsystem=datasources/data-source="appDS":read-resource-description' + ): + return { + "outcome": "success", + "result": {"attributes": {"use-ccm": {"type": "BOOLEAN"}}}, } self.org_run_operation.side_effect = cli_command_response datasource_properties = OrderedDict() - datasource_properties['use-ccm'] = 'true' + datasource_properties["use-ccm"] = "true" - jboss7.create_datasource(self.jboss_config, 'appDS', datasource_properties) + jboss7.create_datasource(self.jboss_config, "appDS", datasource_properties) - self.org_run_operation.assert_called_with(self.jboss_config, '/subsystem=datasources/data-source="appDS":add(use-ccm=true)', fail_on_error=False) + self.org_run_operation.assert_called_with( + self.jboss_config, + '/subsystem=datasources/data-source="appDS":add(use-ccm=true)', + fail_on_error=False, + ) def test_create_datasource_format_boolean_value_when_boolean(self): def cli_command_response(jboss_config, cli_command, fail_on_error=False): - if cli_command == '/subsystem=datasources/data-source="appDS":read-resource-description': - return {'outcome': 'success', - 'result': { - 'attributes': { - 'use-ccm': {'type': 'BOOLEAN'} - } - } + if ( + cli_command + == '/subsystem=datasources/data-source="appDS":read-resource-description' + ): + return { + "outcome": "success", + "result": {"attributes": {"use-ccm": {"type": "BOOLEAN"}}}, } self.org_run_operation.side_effect = cli_command_response datasource_properties = OrderedDict() - datasource_properties['use-ccm'] = True + datasource_properties["use-ccm"] = True - jboss7.create_datasource(self.jboss_config, 'appDS', datasource_properties) + jboss7.create_datasource(self.jboss_config, "appDS", datasource_properties) - self.org_run_operation.assert_called_with(self.jboss_config, '/subsystem=datasources/data-source="appDS":add(use-ccm=true)', fail_on_error=False) + self.org_run_operation.assert_called_with( + self.jboss_config, + '/subsystem=datasources/data-source="appDS":add(use-ccm=true)', + fail_on_error=False, + ) def test_create_datasource_format_int_value_when_int(self): def cli_command_response(jboss_config, cli_command, fail_on_error=False): - if cli_command == '/subsystem=datasources/data-source="appDS":read-resource-description': - return {'outcome': 'success', - 'result': { - 'attributes': { - 'min-pool-size': {'type': 'INT'} - } - } + if ( + cli_command + == '/subsystem=datasources/data-source="appDS":read-resource-description' + ): + return { + "outcome": "success", + "result": {"attributes": {"min-pool-size": {"type": "INT"}}}, } self.org_run_operation.side_effect = cli_command_response datasource_properties = OrderedDict() - datasource_properties['min-pool-size'] = 15 + datasource_properties["min-pool-size"] = 15 - jboss7.create_datasource(self.jboss_config, 'appDS', datasource_properties) + jboss7.create_datasource(self.jboss_config, "appDS", datasource_properties) - self.org_run_operation.assert_called_with(self.jboss_config, '/subsystem=datasources/data-source="appDS":add(min-pool-size=15)', fail_on_error=False) + self.org_run_operation.assert_called_with( + self.jboss_config, + '/subsystem=datasources/data-source="appDS":add(min-pool-size=15)', + fail_on_error=False, + ) def test_create_datasource_format_int_value_when_string(self): def cli_command_response(jboss_config, cli_command, fail_on_error=False): - if cli_command == '/subsystem=datasources/data-source="appDS":read-resource-description': - return {'outcome': 'success', - 'result': { - 'attributes': { - 'min-pool-size': {'type': 'INT'} - } - } + if ( + cli_command + == '/subsystem=datasources/data-source="appDS":read-resource-description' + ): + return { + "outcome": "success", + "result": {"attributes": {"min-pool-size": {"type": "INT"}}}, } self.org_run_operation.side_effect = cli_command_response datasource_properties = OrderedDict() - datasource_properties['min-pool-size'] = '15' + datasource_properties["min-pool-size"] = "15" - jboss7.create_datasource(self.jboss_config, 'appDS', datasource_properties) + jboss7.create_datasource(self.jboss_config, "appDS", datasource_properties) - self.org_run_operation.assert_called_with(self.jboss_config, '/subsystem=datasources/data-source="appDS":add(min-pool-size=15)', fail_on_error=False) + self.org_run_operation.assert_called_with( + self.jboss_config, + '/subsystem=datasources/data-source="appDS":add(min-pool-size=15)', + fail_on_error=False, + ) def test_read_datasource(self): def cli_command_response(jboss_config, cli_command): - if cli_command == '/subsystem=datasources/data-source="appDS":read-resource': + if ( + cli_command + == '/subsystem=datasources/data-source="appDS":read-resource' + ): return { - 'outcome': 'success', - 'result': { - 'driver-name': 'mysql', - 'connection-url': 'jdbc:mysql://localhost:3306/app', - 'jndi-name': 'java:jboss/datasources/appDS', - 'user-name': 'app', - 'password': 'app_password' - } + "outcome": "success", + "result": { + "driver-name": "mysql", + "connection-url": "jdbc:mysql://localhost:3306/app", + "jndi-name": "java:jboss/datasources/appDS", + "user-name": "app", + "password": "app_password", + }, } self.org_run_operation.side_effect = cli_command_response - ds_result = jboss7.read_datasource(self.jboss_config, 'appDS') - ds_properties = ds_result['result'] + ds_result = jboss7.read_datasource(self.jboss_config, "appDS") + ds_properties = ds_result["result"] - self.assertEqual(ds_properties['driver-name'], 'mysql') - self.assertEqual(ds_properties['connection-url'], 'jdbc:mysql://localhost:3306/app') - self.assertEqual(ds_properties['jndi-name'], 'java:jboss/datasources/appDS') - self.assertEqual(ds_properties['user-name'], 'app') - self.assertEqual(ds_properties['password'], 'app_password') + self.assertEqual(ds_properties["driver-name"], "mysql") + self.assertEqual( + ds_properties["connection-url"], "jdbc:mysql://localhost:3306/app" + ) + self.assertEqual(ds_properties["jndi-name"], "java:jboss/datasources/appDS") + self.assertEqual(ds_properties["user-name"], "app") + self.assertEqual(ds_properties["password"], "app_password") def test_update_datasource(self): - datasource_properties = {'driver-name': 'mysql', - 'connection-url': 'jdbc:mysql://localhost:3306/app', - 'jndi-name': 'java:jboss/datasources/appDS', - 'user-name': 'newuser', - 'password': 'app_password'} + datasource_properties = { + "driver-name": "mysql", + "connection-url": "jdbc:mysql://localhost:3306/app", + "jndi-name": "java:jboss/datasources/appDS", + "user-name": "newuser", + "password": "app_password", + } def cli_command_response(jboss_config, cli_command, fail_on_error=False): - if cli_command == '/subsystem=datasources/data-source="appDS":read-resource-description': - return {'outcome': 'success', - 'result': { - 'attributes': { - 'driver-name': {'type': 'STRING'}, - 'connection-url': {'type': 'STRING'}, - 'jndi-name': {'type': 'STRING'}, - 'user-name': {'type': 'STRING'}, - 'password': {'type': 'STRING'} - } + if ( + cli_command + == '/subsystem=datasources/data-source="appDS":read-resource-description' + ): + return { + "outcome": "success", + "result": { + "attributes": { + "driver-name": {"type": "STRING"}, + "connection-url": {"type": "STRING"}, + "jndi-name": {"type": "STRING"}, + "user-name": {"type": "STRING"}, + "password": {"type": "STRING"}, } + }, } - elif cli_command == '/subsystem=datasources/data-source="appDS":read-resource': + elif ( + cli_command + == '/subsystem=datasources/data-source="appDS":read-resource' + ): return { - 'outcome': 'success', - 'result': { - 'driver-name': 'mysql', - 'connection-url': 'jdbc:mysql://localhost:3306/app', - 'jndi-name': 'java:jboss/datasources/appDS', - 'user-name': 'app', - 'password': 'app_password' - } + "outcome": "success", + "result": { + "driver-name": "mysql", + "connection-url": "jdbc:mysql://localhost:3306/app", + "jndi-name": "java:jboss/datasources/appDS", + "user-name": "app", + "password": "app_password", + }, } - elif cli_command == '/subsystem=datasources/data-source="appDS":write-attribute(name="user-name",value="newuser")': - return { - 'outcome': 'success', - 'success': True + elif ( + cli_command + == '/subsystem=datasources/data-source="appDS":write-attribute(name="user-name",value="newuser")' + ): + return {"outcome": "success", "success": True} - } self.org_run_operation.side_effect = cli_command_response - jboss7.update_datasource(self.jboss_config, 'appDS', datasource_properties) + jboss7.update_datasource(self.jboss_config, "appDS", datasource_properties) - self.org_run_operation.assert_any_call(self.jboss_config, '/subsystem=datasources/data-source="appDS":write-attribute(name="user-name",value="newuser")', fail_on_error=False) + self.org_run_operation.assert_any_call( + self.jboss_config, + '/subsystem=datasources/data-source="appDS":write-attribute(name="user-name",value="newuser")', + fail_on_error=False, + ) diff --git a/tests/unit/modules/test_jboss7_cli.py b/tests/unit/modules/test_jboss7_cli.py index 5af2b3d85bd..699337bbf4f 100644 --- a/tests/unit/modules/test_jboss7_cli.py +++ b/tests/unit/modules/test_jboss7_cli.py @@ -2,28 +2,33 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import re -# Import Salt testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase - -# Import Salt libs -from salt.ext import six import salt.modules.jboss7_cli as jboss7_cli from salt.exceptions import CommandExecutionError +# Import Salt libs +from salt.ext import six + +# Import Salt testing libs +from tests.support.mixins import LoaderModuleMockMixin from tests.support.mock import patch +from tests.support.unit import TestCase class CmdMock(object): commands = [] - command_response_func = None # if you want to test complete response object (with retcode, stdout and stderr) + command_response_func = None # if you want to test complete response object (with retcode, stdout and stderr) cli_commands = [] - default_response = {'retcode': 0, 'stdout': ''' { + default_response = { + "retcode": 0, + "stdout": """ { "outcome" => "success" - }''', 'stderr': ''} + }""", + "stderr": "", + } def __init__(self, command_response_func=None): self.command_response_func = command_response_func @@ -39,7 +44,7 @@ class CmdMock(object): @staticmethod def __get_cli_command(command): - command_re = re.compile(r'--command=\"\s*(.+?)\s*\"$', re.DOTALL) + command_re = re.compile(r"--command=\"\s*(.+?)\s*\"$", re.DOTALL) m = command_re.search(command) # --command has to be the last argument if m: cli_command = m.group(1) @@ -67,49 +72,53 @@ class CmdMock(object): class JBoss7CliTestCase(TestCase, LoaderModuleMockMixin): cmd = CmdMock() jboss_config = { - 'cli_path': '/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh', - 'controller': '123.234.345.456:9999', - 'instance_name': 'Instance1', - 'cli_user': 'jbossadm', - 'cli_password': 'jbossadm', - 'status_url': 'http://sampleapp.example.com:8080/' + "cli_path": "/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh", + "controller": "123.234.345.456:9999", + "instance_name": "Instance1", + "cli_user": "jbossadm", + "cli_password": "jbossadm", + "status_url": "http://sampleapp.example.com:8080/", } def setup_loader_modules(self): self.cmd = CmdMock() - self.addCleanup(delattr, self, 'cmd') - return { - jboss7_cli: { - '__salt__': { - 'cmd.run_all': self.cmd.run_all - } - } - } + self.addCleanup(delattr, self, "cmd") + return {jboss7_cli: {"__salt__": {"cmd.run_all": self.cmd.run_all}}} def test_controller_authentication(self): - jboss7_cli.run_operation(self.jboss_config, 'some cli operation') + jboss7_cli.run_operation(self.jboss_config, "some cli operation") - self.assertEqual(self.cmd.get_last_command(), '/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh --connect --controller="123.234.345.456:9999" --user="jbossadm" --password="jbossadm" --command="some cli operation"') + self.assertEqual( + self.cmd.get_last_command(), + '/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh --connect --controller="123.234.345.456:9999" --user="jbossadm" --password="jbossadm" --command="some cli operation"', + ) def test_controller_without_authentication(self): jboss_config = { - 'cli_path': '/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh', - 'controller': '123.234.345.456:9999' + "cli_path": "/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh", + "controller": "123.234.345.456:9999", } - jboss7_cli.run_operation(jboss_config, 'some cli operation') + jboss7_cli.run_operation(jboss_config, "some cli operation") - self.assertEqual(self.cmd.get_last_command(), '/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh --connect --controller="123.234.345.456:9999" --command="some cli operation"') + self.assertEqual( + self.cmd.get_last_command(), + '/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh --connect --controller="123.234.345.456:9999" --command="some cli operation"', + ) def test_operation_execution(self): - operation = r'sample_operation' + operation = r"sample_operation" jboss7_cli.run_operation(self.jboss_config, operation) - self.assertEqual(self.cmd.get_last_command(), r'/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh --connect --controller="123.234.345.456:9999" --user="jbossadm" --password="jbossadm" --command="sample_operation"') + self.assertEqual( + self.cmd.get_last_command(), + r'/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh --connect --controller="123.234.345.456:9999" --user="jbossadm" --password="jbossadm" --command="sample_operation"', + ) def test_handling_jboss_error(self): def command_response(command): - return {'retcode': 1, - 'stdout': r'''{ + return { + "retcode": 1, + "stdout": r"""{ "outcome" => "failed", "failure-description" => "JBAS014807: Management resource '[ (\"subsystem\" => \"datasources\"), @@ -118,99 +127,109 @@ class JBoss7CliTestCase(TestCase, LoaderModuleMockMixin): "rolled-back" => true, "response-headers" => {"process-state" => "reload-required"} } - ''', - 'stderr': 'some err'} + """, + "stderr": "some err", + } + self.cmd.command_response_func = command_response - result = jboss7_cli.run_operation(self.jboss_config, 'some cli command') + result = jboss7_cli.run_operation(self.jboss_config, "some cli command") - self.assertFalse(result['success']) - self.assertEqual(result['err_code'], 'JBAS014807') + self.assertFalse(result["success"]) + self.assertEqual(result["err_code"], "JBAS014807") def test_handling_cmd_not_exists(self): def command_response(command): - return {'retcode': 127, - 'stdout': '''Command not exists''', - 'stderr': 'some err'} + return { + "retcode": 127, + "stdout": """Command not exists""", + "stderr": "some err", + } + self.cmd.command_response_func = command_response try: - jboss7_cli.run_operation(self.jboss_config, 'some cli command') + jboss7_cli.run_operation(self.jboss_config, "some cli command") # should throw an exception assert False except CommandExecutionError as err: - self.assertTrue(six.text_type(err).startswith('Could not execute jboss-cli.sh script')) + self.assertTrue( + six.text_type(err).startswith("Could not execute jboss-cli.sh script") + ) def test_handling_other_cmd_error(self): def command_response(command): - return {'retcode': 1, - 'stdout': '''Command not exists''', - 'stderr': 'some err'} + return { + "retcode": 1, + "stdout": """Command not exists""", + "stderr": "some err", + } + self.cmd.command_response_func = command_response try: - jboss7_cli.run_command(self.jboss_config, 'some cli command') + jboss7_cli.run_command(self.jboss_config, "some cli command") # should throw an exception - self.fail('An exception should be thrown') + self.fail("An exception should be thrown") except CommandExecutionError as err: - self.assertTrue(six.text_type(err).startswith('Command execution failed')) + self.assertTrue(six.text_type(err).startswith("Command execution failed")) def test_matches_cli_output(self): - text = '''{ + text = """{ "key1" => "value1" "key2" => "value2" } - ''' + """ self.assertTrue(jboss7_cli._is_cli_output(text)) def test_not_matches_cli_output(self): - text = '''Some error ''' + text = """Some error """ self.assertFalse(jboss7_cli._is_cli_output(text)) def test_parse_flat_dictionary(self): - text = '''{ + text = """{ "key1" => "value1" "key2" => "value2" - }''' + }""" result = jboss7_cli._parse(text) self.assertEqual(len(result), 2) - self.assertEqual(result['key1'], 'value1') - self.assertEqual(result['key2'], 'value2') + self.assertEqual(result["key1"], "value1") + self.assertEqual(result["key2"], "value2") def test_parse_nested_dictionary(self): - text = '''{ + text = """{ "key1" => "value1", "key2" => { "nested_key1" => "nested_value1" } - }''' + }""" result = jboss7_cli._parse(text) self.assertEqual(len(result), 2) - self.assertEqual(result['key1'], 'value1') - self.assertEqual(len(result['key2']), 1) - self.assertEqual(result['key2']['nested_key1'], 'nested_value1') + self.assertEqual(result["key1"], "value1") + self.assertEqual(len(result["key2"]), 1) + self.assertEqual(result["key2"]["nested_key1"], "nested_value1") def test_parse_string_after_dict(self): - text = '''{ + text = """{ "result" => { "jta" => true }, "response-headers" => {"process-state" => "reload-required"} - }''' + }""" result = jboss7_cli._parse(text) - self.assertTrue(result['result']['jta']) - self.assertEqual(result['response-headers']['process-state'], 'reload-required') + self.assertTrue(result["result"]["jta"]) + self.assertEqual(result["response-headers"]["process-state"], "reload-required") def test_parse_all_datatypes(self): - text = '''{ + text = """{ "outcome" => "success", "result" => { "allocation-retry" => undefined, @@ -220,20 +239,22 @@ class JBoss7CliTestCase(TestCase, LoaderModuleMockMixin): "jta" => true }, "response-headers" => {"process-state" => "reload-required"} - }''' + }""" result = jboss7_cli._parse(text) - self.assertEqual(result['outcome'], 'success') - self.assertIsNone(result['result']['allocation-retry']) - self.assertEqual(result['result']['connection-url'], 'jdbc:mysql://localhost:3306/appdb') - self.assertEqual(result['result']['driver-name'], 'mysql') - self.assertEqual(result['result']['enabled'], False) - self.assertTrue(result['result']['jta']) - self.assertEqual(result['response-headers']['process-state'], 'reload-required') + self.assertEqual(result["outcome"], "success") + self.assertIsNone(result["result"]["allocation-retry"]) + self.assertEqual( + result["result"]["connection-url"], "jdbc:mysql://localhost:3306/appdb" + ) + self.assertEqual(result["result"]["driver-name"], "mysql") + self.assertEqual(result["result"]["enabled"], False) + self.assertTrue(result["result"]["jta"]) + self.assertEqual(result["response-headers"]["process-state"], "reload-required") def test_multiline_strings_with_escaped_quotes(self): - text = r'''{ + text = r"""{ "outcome" => "failed", "failure-description" => "JBAS014807: Management resource '[ (\"subsystem\" => \"datasources\"), @@ -241,50 +262,53 @@ class JBoss7CliTestCase(TestCase, LoaderModuleMockMixin): ]' not found", "rolled-back" => true, "response-headers" => {"process-state" => "reload-required"} - }''' + }""" result = jboss7_cli._parse(text) - self.assertEqual(result['outcome'], 'failed') - self.assertTrue(result['rolled-back']) - self.assertEqual(result['response-headers']['process-state'], 'reload-required') - self.assertEqual(result['failure-description'], r'''JBAS014807: Management resource '[ + self.assertEqual(result["outcome"], "failed") + self.assertTrue(result["rolled-back"]) + self.assertEqual(result["response-headers"]["process-state"], "reload-required") + self.assertEqual( + result["failure-description"], + r"""JBAS014807: Management resource '[ (\"subsystem\" => \"datasources\"), (\"data-source\" => \"asc\") - ]' not found''') + ]' not found""", + ) def test_handling_double_backslash_in_return_values(self): - text = r'''{ + text = r"""{ "outcome" => "success", "result" => { "binding-type" => "simple", "value" => "DOMAIN\\foo" } - }''' + }""" result = jboss7_cli._parse(text) - self.assertEqual(result['outcome'], 'success') - self.assertEqual(result['result']['binding-type'], 'simple') - self.assertEqual(result['result']['value'], r'DOMAIN\foo') + self.assertEqual(result["outcome"], "success") + self.assertEqual(result["result"]["binding-type"], "simple") + self.assertEqual(result["result"]["value"], r"DOMAIN\foo") def test_numbers_without_quotes(self): - text = r'''{ + text = r"""{ "outcome" => "success", "result" => { "min-pool-size" => 1233, "new-connection-sql" => undefined } - }''' + }""" result = jboss7_cli._parse(text) - self.assertEqual(result['outcome'], 'success') - self.assertEqual(result['result']['min-pool-size'], 1233) - self.assertIsNone(result['result']['new-connection-sql']) + self.assertEqual(result["outcome"], "success") + self.assertEqual(result["result"]["min-pool-size"], 1233) + self.assertIsNone(result["result"]["new-connection-sql"]) def test_all_datasource_properties(self): - text = r'''{ + text = r"""{ "outcome" => "success", "result" => { "allocation-retry" => undefined, @@ -340,18 +364,18 @@ class JBoss7CliTestCase(TestCase, LoaderModuleMockMixin): } }, "response-headers" => {"process-state" => "reload-required"} - }''' + }""" result = jboss7_cli._parse(text) - self.assertEqual(result['outcome'], 'success') - self.assertEqual(result['result']['max-pool-size'], 20) - self.assertIsNone(result['result']['new-connection-sql']) - self.assertIsNone(result['result']['url-delimiter']) - self.assertFalse(result['result']['validate-on-match']) + self.assertEqual(result["outcome"], "success") + self.assertEqual(result["result"]["max-pool-size"], 20) + self.assertIsNone(result["result"]["new-connection-sql"]) + self.assertIsNone(result["result"]["url-delimiter"]) + self.assertFalse(result["result"]["validate-on-match"]) def test_datasource_resource_one_attribute_description(self): - cli_output = '''{ + cli_output = """{ "outcome" => "success", "result" => { "description" => "A JDBC data-source configuration", @@ -373,23 +397,25 @@ class JBoss7CliTestCase(TestCase, LoaderModuleMockMixin): "children" => {"connection-properties" => {"description" => "The connection-properties element allows you to pass in arbitrary connection properties to the Driver.connect(url, props) method"}} } } - ''' + """ result = jboss7_cli._parse(cli_output) - self.assertEqual(result['outcome'], 'success') - conn_url_attributes = result['result']['attributes']['connection-url'] - self.assertEqual(conn_url_attributes['type'], 'STRING') - self.assertEqual(conn_url_attributes['description'], 'The JDBC driver connection URL') - self.assertTrue(conn_url_attributes['expressions-allowed']) - self.assertFalse(conn_url_attributes['nillable']) - self.assertEqual(conn_url_attributes['min-length'], 1) - self.assertEqual(conn_url_attributes['max-length'], 2147483647) - self.assertEqual(conn_url_attributes['access-type'], 'read-write') - self.assertEqual(conn_url_attributes['storage'], 'configuration') - self.assertEqual(conn_url_attributes['restart-required'], 'no-services') + self.assertEqual(result["outcome"], "success") + conn_url_attributes = result["result"]["attributes"]["connection-url"] + self.assertEqual(conn_url_attributes["type"], "STRING") + self.assertEqual( + conn_url_attributes["description"], "The JDBC driver connection URL" + ) + self.assertTrue(conn_url_attributes["expressions-allowed"]) + self.assertFalse(conn_url_attributes["nillable"]) + self.assertEqual(conn_url_attributes["min-length"], 1) + self.assertEqual(conn_url_attributes["max-length"], 2147483647) + self.assertEqual(conn_url_attributes["access-type"], "read-write") + self.assertEqual(conn_url_attributes["storage"], "configuration") + self.assertEqual(conn_url_attributes["restart-required"], "no-services") def test_datasource_complete_resource_description(self): - cli_output = '''{ + cli_output = """{ "outcome" => "success", "result" => { "description" => "A JDBC data-source configuration", @@ -411,38 +437,51 @@ class JBoss7CliTestCase(TestCase, LoaderModuleMockMixin): "children" => {"connection-properties" => {"description" => "The connection-properties element allows you to pass in arbitrary connection properties to the Driver.connect(url, props) method"}} } } - ''' + """ result = jboss7_cli._parse(cli_output) - self.assertEqual(result['outcome'], 'success') - conn_url_attributes = result['result']['attributes']['connection-url'] - self.assertEqual(conn_url_attributes['type'], 'STRING') - self.assertEqual(conn_url_attributes['description'], 'The JDBC driver connection URL') - self.assertTrue(conn_url_attributes['expressions-allowed']) - self.assertFalse(conn_url_attributes['nillable']) - self.assertEqual(conn_url_attributes['min-length'], 1) - self.assertEqual(conn_url_attributes['max-length'], 2147483647) - self.assertEqual(conn_url_attributes['access-type'], 'read-write') - self.assertEqual(conn_url_attributes['storage'], 'configuration') - self.assertEqual(conn_url_attributes['restart-required'], 'no-services') + self.assertEqual(result["outcome"], "success") + conn_url_attributes = result["result"]["attributes"]["connection-url"] + self.assertEqual(conn_url_attributes["type"], "STRING") + self.assertEqual( + conn_url_attributes["description"], "The JDBC driver connection URL" + ) + self.assertTrue(conn_url_attributes["expressions-allowed"]) + self.assertFalse(conn_url_attributes["nillable"]) + self.assertEqual(conn_url_attributes["min-length"], 1) + self.assertEqual(conn_url_attributes["max-length"], 2147483647) + self.assertEqual(conn_url_attributes["access-type"], "read-write") + self.assertEqual(conn_url_attributes["storage"], "configuration") + self.assertEqual(conn_url_attributes["restart-required"], "no-services") def test_escaping_operation_with_backslashes_and_quotes(self): operation = r'/subsystem=naming/binding="java:/sampleapp/web-module/ldap/username":add(binding-type=simple, value="DOMAIN\\\\user")' jboss7_cli.run_operation(self.jboss_config, operation) - self.assertEqual(self.cmd.get_last_command(), r'/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh --connect --controller="123.234.345.456:9999" --user="jbossadm" --password="jbossadm" --command="/subsystem=naming/binding=\"java:/sampleapp/web-module/ldap/username\":add(binding-type=simple, value=\"DOMAIN\\\\\\\\user\")"') + self.assertEqual( + self.cmd.get_last_command(), + r'/opt/jboss/jboss-eap-6.0.1/bin/jboss-cli.sh --connect --controller="123.234.345.456:9999" --user="jbossadm" --password="jbossadm" --command="/subsystem=naming/binding=\"java:/sampleapp/web-module/ldap/username\":add(binding-type=simple, value=\"DOMAIN\\\\\\\\user\")"', + ) def test_run_operation_wflyctl_error(self): - call_cli_ret = {'retcode': 1, - 'stdout': '{"failure-description" => "WFLYCTL0234523: ops"}'} - with patch('salt.modules.jboss7_cli._call_cli', return_value=call_cli_ret) as _call_cli: + call_cli_ret = { + "retcode": 1, + "stdout": '{"failure-description" => "WFLYCTL0234523: ops"}', + } + with patch( + "salt.modules.jboss7_cli._call_cli", return_value=call_cli_ret + ) as _call_cli: ret = jboss7_cli.run_operation(None, "ls", False) - self.assertEqual(ret['err_code'], "WFLYCTL0234523") + self.assertEqual(ret["err_code"], "WFLYCTL0234523") def test_run_operation_no_code_error(self): - call_cli_ret = {'retcode': 1, - 'stdout': '{"failure-description" => "ERROR234523: ops"}'} - with patch('salt.modules.jboss7_cli._call_cli', return_value=call_cli_ret) as _call_cli: + call_cli_ret = { + "retcode": 1, + "stdout": '{"failure-description" => "ERROR234523: ops"}', + } + with patch( + "salt.modules.jboss7_cli._call_cli", return_value=call_cli_ret + ) as _call_cli: ret = jboss7_cli.run_operation(None, "ls", False) - self.assertEqual(ret['err_code'], "-1") + self.assertEqual(ret["err_code"], "-1") diff --git a/tests/unit/modules/test_junos.py b/tests/unit/modules/test_junos.py index 7736497ec2b..0e72802e468 100644 --- a/tests/unit/modules/test_junos.py +++ b/tests/unit/modules/test_junos.py @@ -1,14 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rajvi Dhimar <rajvidhimar95@gmail.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals +# Import salt modules +import salt.modules.junos as junos + # Import test libs from tests.support.mixins import LoaderModuleMockMixin, XMLEqualityMixin -from tests.support.mock import patch, mock_open, PropertyMock, call, ANY -from tests.support.unit import skipIf, TestCase +from tests.support.mock import ANY, PropertyMock, call, mock_open, patch +from tests.support.unit import TestCase, skipIf # Import 3rd-party libs try: @@ -22,26 +25,25 @@ try: from jnpr.junos.device import Device import jxmlease # pylint: disable=unused-import from jnpr.junos.exception import LockError, UnlockError + HAS_JUNOS = True except ImportError: HAS_JUNOS = False -# Import salt modules -import salt.modules.junos as junos - -@skipIf(not HAS_JUNOS, 'The junos-eznc and jxmlease modules are required') +@skipIf(not HAS_JUNOS, "The junos-eznc and jxmlease modules are required") class Test_Junos_Module(TestCase, LoaderModuleMockMixin, XMLEqualityMixin): - def setup_loader_modules(self): return { junos: { - '__proxy__': { - 'junos.conn': self.make_connect, - 'junos.get_serialized_facts': self.get_facts + "__proxy__": { + "junos.conn": self.make_connect, + "junos.get_serialized_facts": self.get_facts, + }, + "__salt__": { + "cp.get_template": self.mock_cp, + "cp.get_file": self.mock_cp, }, - '__salt__': {'cp.get_template': self.mock_cp, - 'cp.get_file': self.mock_cp} } } @@ -49,1366 +51,1710 @@ class Test_Junos_Module(TestCase, LoaderModuleMockMixin, XMLEqualityMixin): pass def make_connect(self): - with patch('ncclient.manager.connect') as mock_connect: + with patch("ncclient.manager.connect") as mock_connect: self.dev = Device( - host='1.1.1.1', - user='test', - password='test123', - fact_style='old', - gather_facts=False) + host="1.1.1.1", + user="test", + password="test123", + fact_style="old", + gather_facts=False, + ) self.dev.open() self.dev.timeout = 30 self.dev.bind(cu=Config) self.dev.bind(sw=SW) - self.addCleanup(delattr, self, 'dev') + self.addCleanup(delattr, self, "dev") return self.dev def raise_exception(self, *args, **kwargs): - raise Exception('Test exception') + raise Exception("Test exception") def get_facts(self): - facts = {'2RE': True, - 'HOME': '/var/home/regress', - 'RE0': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'master', - 'model': 'RE-VMX', - 'status': 'OK', - 'up_time': '11 days, 23 hours, 16 minutes, 54 seconds'}, - 'RE1': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'backup', - 'model': 'RE-VMX', - 'status': 'OK', - 'up_time': '11 days, 23 hours, 16 minutes, 41 seconds'}, - 'RE_hw_mi': False, - 'current_re': ['re0', 'master', 'node', 'fwdd', 'member', 'pfem'], - 'domain': 'englab.juniper.net', - 'fqdn': 'R1_re0.englab.juniper.net', - 'hostname': 'R1_re0', - 'hostname_info': {'re0': 'R1_re0', 're1': 'R1_re01'}, - 'ifd_style': 'CLASSIC', - 'junos_info': {'re0': {'object': {'build': None, - 'major': (16, 1), - 'minor': '20160413_0837_aamish', - 'type': 'I'}, - 'text': '16.1I20160413_0837_aamish'}, - 're1': {'object': {'build': None, - 'major': (16, 1), - 'minor': '20160413_0837_aamish', - 'type': 'I'}, - 'text': '16.1I20160413_0837_aamish'}}, - 'master': 'RE0', - 'model': 'MX240', - 'model_info': {'re0': 'MX240', 're1': 'MX240'}, - 'personality': 'MX', - 're_info': {'default': {'0': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'master', - 'model': 'RE-VMX', - 'status': 'OK'}, - '1': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'backup', - 'model': 'RE-VMX', - 'status': 'OK'}, - 'default': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'master', - 'model': 'RE-VMX', - 'status': 'OK'}}}, - 're_master': {'default': '0'}, - 'serialnumber': 'VMX4eaf', - 'srx_cluster': None, - 'switch_style': 'BRIDGE_DOMAIN', - 'vc_capable': False, - 'vc_fabric': None, - 'vc_master': None, - 'vc_mode': None, - 'version': '16.1I20160413_0837_aamish', - 'version_RE0': '16.1I20160413_0837_aamish', - 'version_RE1': '16.1I20160413_0837_aamish', - 'version_info': {'build': None, - 'major': (16, 1), - 'minor': '20160413_0837_aamish', - 'type': 'I'}, - 'virtual': True} + facts = { + "2RE": True, + "HOME": "/var/home/regress", + "RE0": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "master", + "model": "RE-VMX", + "status": "OK", + "up_time": "11 days, 23 hours, 16 minutes, 54 seconds", + }, + "RE1": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "backup", + "model": "RE-VMX", + "status": "OK", + "up_time": "11 days, 23 hours, 16 minutes, 41 seconds", + }, + "RE_hw_mi": False, + "current_re": ["re0", "master", "node", "fwdd", "member", "pfem"], + "domain": "englab.juniper.net", + "fqdn": "R1_re0.englab.juniper.net", + "hostname": "R1_re0", + "hostname_info": {"re0": "R1_re0", "re1": "R1_re01"}, + "ifd_style": "CLASSIC", + "junos_info": { + "re0": { + "object": { + "build": None, + "major": (16, 1), + "minor": "20160413_0837_aamish", + "type": "I", + }, + "text": "16.1I20160413_0837_aamish", + }, + "re1": { + "object": { + "build": None, + "major": (16, 1), + "minor": "20160413_0837_aamish", + "type": "I", + }, + "text": "16.1I20160413_0837_aamish", + }, + }, + "master": "RE0", + "model": "MX240", + "model_info": {"re0": "MX240", "re1": "MX240"}, + "personality": "MX", + "re_info": { + "default": { + "0": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "master", + "model": "RE-VMX", + "status": "OK", + }, + "1": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "backup", + "model": "RE-VMX", + "status": "OK", + }, + "default": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "master", + "model": "RE-VMX", + "status": "OK", + }, + } + }, + "re_master": {"default": "0"}, + "serialnumber": "VMX4eaf", + "srx_cluster": None, + "switch_style": "BRIDGE_DOMAIN", + "vc_capable": False, + "vc_fabric": None, + "vc_master": None, + "vc_mode": None, + "version": "16.1I20160413_0837_aamish", + "version_RE0": "16.1I20160413_0837_aamish", + "version_RE1": "16.1I20160413_0837_aamish", + "version_info": { + "build": None, + "major": (16, 1), + "minor": "20160413_0837_aamish", + "type": "I", + }, + "virtual": True, + } return facts def test_timeout_decorator(self): - with patch('jnpr.junos.Device.timeout', - new_callable=PropertyMock) as mock_timeout: + with patch( + "jnpr.junos.Device.timeout", new_callable=PropertyMock + ) as mock_timeout: mock_timeout.return_value = 30 def function(x): return x + decorator = junos.timeoutDecorator(function) decorator("Test Mock", dev_timeout=10) calls = [call(), call(10), call(30)] mock_timeout.assert_has_calls(calls) def test_facts_refresh(self): - with patch('salt.modules.saltutil.sync_grains') as mock_sync_grains: + with patch("salt.modules.saltutil.sync_grains") as mock_sync_grains: ret = dict() - ret['facts'] = {'2RE': True, - 'HOME': '/var/home/regress', - 'RE0': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'master', - 'model': 'RE-VMX', - 'status': 'OK', - 'up_time': '11 days, 23 hours, 16 minutes, 54 seconds'}, - 'RE1': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'backup', - 'model': 'RE-VMX', - 'status': 'OK', - 'up_time': '11 days, 23 hours, 16 minutes, 41 seconds'}, - 'RE_hw_mi': False, - 'current_re': ['re0', 'master', 'node', 'fwdd', 'member', 'pfem'], - 'domain': 'englab.juniper.net', - 'fqdn': 'R1_re0.englab.juniper.net', - 'hostname': 'R1_re0', - 'hostname_info': {'re0': 'R1_re0', 're1': 'R1_re01'}, - 'ifd_style': 'CLASSIC', - 'junos_info': {'re0': {'object': {'build': None, - 'major': (16, 1), - 'minor': '20160413_0837_aamish', - 'type': 'I'}, - 'text': '16.1I20160413_0837_aamish'}, - 're1': {'object': {'build': None, - 'major': (16, 1), - 'minor': '20160413_0837_aamish', - 'type': 'I'}, - 'text': '16.1I20160413_0837_aamish'}}, - 'master': 'RE0', - 'model': 'MX240', - 'model_info': {'re0': 'MX240', 're1': 'MX240'}, - 'personality': 'MX', - 're_info': {'default': {'0': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'master', - 'model': 'RE-VMX', - 'status': 'OK'}, - '1': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'backup', - 'model': 'RE-VMX', - 'status': 'OK'}, - 'default': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'master', - 'model': 'RE-VMX', - 'status': 'OK'}}}, - 're_master': {'default': '0'}, - 'serialnumber': 'VMX4eaf', - 'srx_cluster': None, - 'switch_style': 'BRIDGE_DOMAIN', - 'vc_capable': False, - 'vc_fabric': None, - 'vc_master': None, - 'vc_mode': None, - 'version': '16.1I20160413_0837_aamish', - 'version_RE0': '16.1I20160413_0837_aamish', - 'version_RE1': '16.1I20160413_0837_aamish', - 'version_info': {'build': None, - 'major': (16, 1), - 'minor': '20160413_0837_aamish', - 'type': 'I'}, - 'virtual': True} - ret['out'] = True + ret["facts"] = { + "2RE": True, + "HOME": "/var/home/regress", + "RE0": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "master", + "model": "RE-VMX", + "status": "OK", + "up_time": "11 days, 23 hours, 16 minutes, 54 seconds", + }, + "RE1": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "backup", + "model": "RE-VMX", + "status": "OK", + "up_time": "11 days, 23 hours, 16 minutes, 41 seconds", + }, + "RE_hw_mi": False, + "current_re": ["re0", "master", "node", "fwdd", "member", "pfem"], + "domain": "englab.juniper.net", + "fqdn": "R1_re0.englab.juniper.net", + "hostname": "R1_re0", + "hostname_info": {"re0": "R1_re0", "re1": "R1_re01"}, + "ifd_style": "CLASSIC", + "junos_info": { + "re0": { + "object": { + "build": None, + "major": (16, 1), + "minor": "20160413_0837_aamish", + "type": "I", + }, + "text": "16.1I20160413_0837_aamish", + }, + "re1": { + "object": { + "build": None, + "major": (16, 1), + "minor": "20160413_0837_aamish", + "type": "I", + }, + "text": "16.1I20160413_0837_aamish", + }, + }, + "master": "RE0", + "model": "MX240", + "model_info": {"re0": "MX240", "re1": "MX240"}, + "personality": "MX", + "re_info": { + "default": { + "0": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "master", + "model": "RE-VMX", + "status": "OK", + }, + "1": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "backup", + "model": "RE-VMX", + "status": "OK", + }, + "default": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "master", + "model": "RE-VMX", + "status": "OK", + }, + } + }, + "re_master": {"default": "0"}, + "serialnumber": "VMX4eaf", + "srx_cluster": None, + "switch_style": "BRIDGE_DOMAIN", + "vc_capable": False, + "vc_fabric": None, + "vc_master": None, + "vc_mode": None, + "version": "16.1I20160413_0837_aamish", + "version_RE0": "16.1I20160413_0837_aamish", + "version_RE1": "16.1I20160413_0837_aamish", + "version_info": { + "build": None, + "major": (16, 1), + "minor": "20160413_0837_aamish", + "type": "I", + }, + "virtual": True, + } + ret["out"] = True self.assertEqual(junos.facts_refresh(), ret) def test_facts_refresh_exception(self): - with patch('jnpr.junos.device.Device.facts_refresh') as mock_facts_refresh: + with patch("jnpr.junos.device.Device.facts_refresh") as mock_facts_refresh: mock_facts_refresh.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Execution failed due to "Test exception"' - ret['out'] = False + ret["message"] = 'Execution failed due to "Test exception"' + ret["out"] = False self.assertEqual(junos.facts_refresh(), ret) def test_facts(self): ret = dict() - ret['facts'] = {'2RE': True, - 'HOME': '/var/home/regress', - 'RE0': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'master', - 'model': 'RE-VMX', - 'status': 'OK', - 'up_time': '11 days, 23 hours, 16 minutes, 54 seconds'}, - 'RE1': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'backup', - 'model': 'RE-VMX', - 'status': 'OK', - 'up_time': '11 days, 23 hours, 16 minutes, 41 seconds'}, - 'RE_hw_mi': False, - 'current_re': ['re0', 'master', 'node', 'fwdd', 'member', 'pfem'], - 'domain': 'englab.juniper.net', - 'fqdn': 'R1_re0.englab.juniper.net', - 'hostname': 'R1_re0', - 'hostname_info': {'re0': 'R1_re0', 're1': 'R1_re01'}, - 'ifd_style': 'CLASSIC', - 'junos_info': {'re0': {'object': {'build': None, - 'major': (16, 1), - 'minor': '20160413_0837_aamish', - 'type': 'I'}, - 'text': '16.1I20160413_0837_aamish'}, - 're1': {'object': {'build': None, - 'major': (16, 1), - 'minor': '20160413_0837_aamish', - 'type': 'I'}, - 'text': '16.1I20160413_0837_aamish'}}, - 'master': 'RE0', - 'model': 'MX240', - 'model_info': {'re0': 'MX240', 're1': 'MX240'}, - 'personality': 'MX', - 're_info': {'default': {'0': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'master', - 'model': 'RE-VMX', - 'status': 'OK'}, - '1': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'backup', - 'model': 'RE-VMX', - 'status': 'OK'}, - 'default': {'last_reboot_reason': '0x200:normal shutdown', - 'mastership_state': 'master', - 'model': 'RE-VMX', - 'status': 'OK'}}}, - 're_master': {'default': '0'}, - 'serialnumber': 'VMX4eaf', - 'srx_cluster': None, - 'switch_style': 'BRIDGE_DOMAIN', - 'vc_capable': False, - 'vc_fabric': None, - 'vc_master': None, - 'vc_mode': None, - 'version': '16.1I20160413_0837_aamish', - 'version_RE0': '16.1I20160413_0837_aamish', - 'version_RE1': '16.1I20160413_0837_aamish', - 'version_info': {'build': None, - 'major': (16, 1), - 'minor': '20160413_0837_aamish', - 'type': 'I'}, - 'virtual': True} - ret['out'] = True + ret["facts"] = { + "2RE": True, + "HOME": "/var/home/regress", + "RE0": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "master", + "model": "RE-VMX", + "status": "OK", + "up_time": "11 days, 23 hours, 16 minutes, 54 seconds", + }, + "RE1": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "backup", + "model": "RE-VMX", + "status": "OK", + "up_time": "11 days, 23 hours, 16 minutes, 41 seconds", + }, + "RE_hw_mi": False, + "current_re": ["re0", "master", "node", "fwdd", "member", "pfem"], + "domain": "englab.juniper.net", + "fqdn": "R1_re0.englab.juniper.net", + "hostname": "R1_re0", + "hostname_info": {"re0": "R1_re0", "re1": "R1_re01"}, + "ifd_style": "CLASSIC", + "junos_info": { + "re0": { + "object": { + "build": None, + "major": (16, 1), + "minor": "20160413_0837_aamish", + "type": "I", + }, + "text": "16.1I20160413_0837_aamish", + }, + "re1": { + "object": { + "build": None, + "major": (16, 1), + "minor": "20160413_0837_aamish", + "type": "I", + }, + "text": "16.1I20160413_0837_aamish", + }, + }, + "master": "RE0", + "model": "MX240", + "model_info": {"re0": "MX240", "re1": "MX240"}, + "personality": "MX", + "re_info": { + "default": { + "0": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "master", + "model": "RE-VMX", + "status": "OK", + }, + "1": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "backup", + "model": "RE-VMX", + "status": "OK", + }, + "default": { + "last_reboot_reason": "0x200:normal shutdown", + "mastership_state": "master", + "model": "RE-VMX", + "status": "OK", + }, + } + }, + "re_master": {"default": "0"}, + "serialnumber": "VMX4eaf", + "srx_cluster": None, + "switch_style": "BRIDGE_DOMAIN", + "vc_capable": False, + "vc_fabric": None, + "vc_master": None, + "vc_mode": None, + "version": "16.1I20160413_0837_aamish", + "version_RE0": "16.1I20160413_0837_aamish", + "version_RE1": "16.1I20160413_0837_aamish", + "version_info": { + "build": None, + "major": (16, 1), + "minor": "20160413_0837_aamish", + "type": "I", + }, + "virtual": True, + } + ret["out"] = True self.assertEqual(junos.facts(), ret) def test_facts_exception(self): - with patch.dict(junos.__proxy__, {'junos.get_serialized_facts': self.raise_exception}): + with patch.dict( + junos.__proxy__, {"junos.get_serialized_facts": self.raise_exception} + ): ret = dict() - ret['message'] = 'Could not display facts due to "Test exception"' - ret['out'] = False + ret["message"] = 'Could not display facts due to "Test exception"' + ret["out"] = False self.assertEqual(junos.facts(), ret) def test_set_hostname_without_args(self): ret = dict() - ret['message'] = 'Please provide the hostname.' - ret['out'] = False + ret["message"] = "Please provide the hostname." + ret["out"] = False self.assertEqual(junos.set_hostname(), ret) def test_set_hostname_load_called_with_valid_name(self): - with patch('jnpr.junos.utils.config.Config.load') as mock_load: - junos.set_hostname('test-name') - mock_load.assert_called_with( - 'set system host-name test-name', format='set') + with patch("jnpr.junos.utils.config.Config.load") as mock_load: + junos.set_hostname("test-name") + mock_load.assert_called_with("set system host-name test-name", format="set") def test_set_hostname_raise_exception_for_load(self): - with patch('jnpr.junos.utils.config.Config.load') as mock_load: + with patch("jnpr.junos.utils.config.Config.load") as mock_load: mock_load.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Could not load configuration due to error "Test exception"' - ret['out'] = False - self.assertEqual(junos.set_hostname('Test-name'), ret) + ret[ + "message" + ] = 'Could not load configuration due to error "Test exception"' + ret["out"] = False + self.assertEqual(junos.set_hostname("Test-name"), ret) def test_set_hostname_raise_exception_for_commit_check(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check: + with patch("jnpr.junos.utils.config.Config.commit_check") as mock_commit_check: mock_commit_check.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Could not commit check due to error "Test exception"' - ret['out'] = False - self.assertEqual(junos.set_hostname('test-name'), ret) + ret["message"] = 'Could not commit check due to error "Test exception"' + ret["out"] = False + self.assertEqual(junos.set_hostname("test-name"), ret) def test_set_hostname_one_arg_parsed_correctly(self): - with patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit: + with patch("jnpr.junos.utils.config.Config.load") as mock_load, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit: mock_commit_check.return_value = True - args = {'comment': 'Committed via salt', '__pub_user': 'root', - '__pub_arg': ['test-name', {'comment': 'Committed via salt'}], - '__pub_fun': 'junos.set_hostname', '__pub_jid': - '20170220210915624885', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + args = { + "comment": "Committed via salt", + "__pub_user": "root", + "__pub_arg": ["test-name", {"comment": "Committed via salt"}], + "__pub_fun": "junos.set_hostname", + "__pub_jid": "20170220210915624885", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } - junos.set_hostname('test-name', **args) - mock_commit.assert_called_with(comment='Committed via salt') + junos.set_hostname("test-name", **args) + mock_commit.assert_called_with(comment="Committed via salt") def test_set_hostname_more_than_one_args_parsed_correctly(self): - with patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit: + with patch("jnpr.junos.utils.config.Config.load") as mock_load, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit: mock_commit_check.return_value = True - args = {'comment': 'Committed via salt', - '__pub_user': 'root', - '__pub_arg': ['test-name', - {'comment': 'Committed via salt', - 'confirm': 5}], - '__pub_fun': 'junos.set_hostname', - '__pub_jid': '20170220210915624885', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "comment": "Committed via salt", + "__pub_user": "root", + "__pub_arg": [ + "test-name", + {"comment": "Committed via salt", "confirm": 5}, + ], + "__pub_fun": "junos.set_hostname", + "__pub_jid": "20170220210915624885", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } - junos.set_hostname('test-name', **args) - mock_commit.assert_called_with(comment='Committed via salt', confirm=5) + junos.set_hostname("test-name", **args) + mock_commit.assert_called_with(comment="Committed via salt", confirm=5) def test_set_hostname_successful_return_message(self): - with patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit: + with patch("jnpr.junos.utils.config.Config.load") as mock_load, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit: mock_commit_check.return_value = True - args = {'comment': 'Committed via salt', - '__pub_user': 'root', - '__pub_arg': ['test-name', - {'comment': 'Committed via salt'}], - '__pub_fun': 'junos.set_hostname', - '__pub_jid': '20170220210915624885', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "comment": "Committed via salt", + "__pub_user": "root", + "__pub_arg": ["test-name", {"comment": "Committed via salt"}], + "__pub_fun": "junos.set_hostname", + "__pub_jid": "20170220210915624885", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = 'Successfully changed hostname.' - ret['out'] = True - self.assertEqual(junos.set_hostname('test-name', **args), ret) + ret["message"] = "Successfully changed hostname." + ret["out"] = True + self.assertEqual(junos.set_hostname("test-name", **args), ret) def test_set_hostname_raise_exception_for_commit(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit: mock_commit.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Successfully loaded host-name but commit failed with "Test exception"' - ret['out'] = False - self.assertEqual(junos.set_hostname('test-name'), ret) + ret[ + "message" + ] = 'Successfully loaded host-name but commit failed with "Test exception"' + ret["out"] = False + self.assertEqual(junos.set_hostname("test-name"), ret) def test_set_hostname_fail_commit_check(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('salt.modules.junos.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch("salt.modules.junos.rollback") as mock_rollback: mock_commit_check.return_value = False ret = dict() - ret['out'] = False - ret['message'] = 'Successfully loaded host-name but pre-commit check failed.' - self.assertEqual(junos.set_hostname('test'), ret) + ret["out"] = False + ret[ + "message" + ] = "Successfully loaded host-name but pre-commit check failed." + self.assertEqual(junos.set_hostname("test"), ret) def test_commit_without_args(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit: mock_commit.return_value = True mock_commit_check.return_value = True ret = dict() - ret['message'] = 'Commit Successful.' - ret['out'] = True + ret["message"] = "Commit Successful." + ret["out"] = True self.assertEqual(junos.commit(), ret) def test_commit_raise_commit_check_exception(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check: + with patch("jnpr.junos.utils.config.Config.commit_check") as mock_commit_check: mock_commit_check.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Could not perform commit check due to "Test exception"' - ret['out'] = False + ret["message"] = 'Could not perform commit check due to "Test exception"' + ret["out"] = False self.assertEqual(junos.commit(), ret) def test_commit_raise_commit_exception(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit: mock_commit_check.return_value = True mock_commit.side_effect = self.raise_exception ret = dict() - ret['out'] = False - ret['message'] = \ - 'Commit check succeeded but actual commit failed with "Test exception"' + ret["out"] = False + ret[ + "message" + ] = 'Commit check succeeded but actual commit failed with "Test exception"' self.assertEqual(junos.commit(), ret) def test_commit_with_single_argument(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit: mock_commit_check.return_value = True - args = {'__pub_user': 'root', - '__pub_arg': [{'sync': True}], - 'sync': True, - '__pub_fun': 'junos.commit', - '__pub_jid': '20170221182531323467', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"sync": True}], + "sync": True, + "__pub_fun": "junos.commit", + "__pub_jid": "20170221182531323467", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } junos.commit(**args) mock_commit.assert_called_with(detail=False, sync=True) - def test_commit_with_multiple_arguments( - self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit: + def test_commit_with_multiple_arguments(self): + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit: mock_commit_check.return_value = True - args = {'comment': 'comitted via salt', - '__pub_user': 'root', - '__pub_arg': [{'comment': 'comitted via salt', - 'confirm': 3, - 'detail': True}], - 'confirm': 3, - 'detail': True, - '__pub_fun': 'junos.commit', - '__pub_jid': '20170221182856987820', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "comment": "comitted via salt", + "__pub_user": "root", + "__pub_arg": [ + {"comment": "comitted via salt", "confirm": 3, "detail": True} + ], + "confirm": 3, + "detail": True, + "__pub_fun": "junos.commit", + "__pub_jid": "20170221182856987820", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } junos.commit(**args) mock_commit.assert_called_with( - comment='comitted via salt', detail=True, confirm=3) + comment="comitted via salt", detail=True, confirm=3 + ) - def test_commit_pyez_commit_returning_false( - self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit: + def test_commit_pyez_commit_returning_false(self): + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit: mock_commit.return_value = False mock_commit_check.return_value = True ret = dict() - ret['message'] = 'Commit failed.' - ret['out'] = False + ret["message"] = "Commit failed." + ret["out"] = False self.assertEqual(junos.commit(), ret) def test_commit_pyez_commit_check_returns_false(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check: + with patch("jnpr.junos.utils.config.Config.commit_check") as mock_commit_check: mock_commit_check.return_value = False ret = dict() - ret['out'] = False - ret['message'] = 'Pre-commit check failed.' + ret["out"] = False + ret["message"] = "Pre-commit check failed." self.assertEqual(junos.commit(), ret) def test_rollback_exception(self): - with patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch("jnpr.junos.utils.config.Config.rollback") as mock_rollback: mock_rollback.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Rollback failed due to "Test exception"' - ret['out'] = False + ret["message"] = 'Rollback failed due to "Test exception"' + ret["out"] = False self.assertEqual(junos.rollback(), ret) def test_rollback_without_args_success(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback: mock_commit_check.return_value = True mock_rollback.return_value = True ret = dict() - ret['message'] = 'Rollback successful' - ret['out'] = True + ret["message"] = "Rollback successful" + ret["out"] = True self.assertEqual(junos.rollback(), ret) def test_rollback_without_args_fail(self): - with patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch("jnpr.junos.utils.config.Config.rollback") as mock_rollback: mock_rollback.return_value = False ret = dict() - ret['message'] = 'Rollback failed' - ret['out'] = False + ret["message"] = "Rollback failed" + ret["out"] = False self.assertEqual(junos.rollback(), ret) def test_rollback_with_id(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback: mock_commit_check.return_value = True junos.rollback(id=5) mock_rollback.assert_called_with(5) def test_rollback_with_id_and_single_arg(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback: mock_commit_check.return_value = True - args = {'__pub_user': 'root', '__pub_arg': [2, {'confirm': 2}], - 'confirm': 2, '__pub_fun': 'junos.rollback', - '__pub_jid': '20170221184518526067', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [2, {"confirm": 2}], + "confirm": 2, + "__pub_fun": "junos.rollback", + "__pub_jid": "20170221184518526067", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } junos.rollback(id=2, **args) mock_rollback.assert_called_with(2) mock_commit.assert_called_with(confirm=2) def test_rollback_with_id_and_multiple_args(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback: mock_commit_check.return_value = True - args = {'comment': 'Comitted via salt', - '__pub_user': 'root', - '__pub_arg': [2, - {'comment': 'Comitted via salt', - 'dev_timeout': 40, - 'confirm': 1}], - 'confirm': 1, - '__pub_fun': 'junos.rollback', - '__pub_jid': '20170221192708251721', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "comment": "Comitted via salt", + "__pub_user": "root", + "__pub_arg": [ + 2, + {"comment": "Comitted via salt", "dev_timeout": 40, "confirm": 1}, + ], + "confirm": 1, + "__pub_fun": "junos.rollback", + "__pub_jid": "20170221192708251721", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } junos.rollback(id=2, **args) mock_rollback.assert_called_with(2) mock_commit.assert_called_with( - comment='Comitted via salt', confirm=1, dev_timeout=40) + comment="Comitted via salt", confirm=1, dev_timeout=40 + ) def test_rollback_with_only_single_arg(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback: mock_commit_check.return_value = True - args = {'__pub_user': 'root', - '__pub_arg': [{'sync': True}], - 'sync': True, - '__pub_fun': 'junos.rollback', - '__pub_jid': '20170221193615696475', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"sync": True}], + "sync": True, + "__pub_fun": "junos.rollback", + "__pub_jid": "20170221193615696475", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } junos.rollback(**args) mock_rollback.assert_called_once_with(0) mock_commit.assert_called_once_with(sync=True) def test_rollback_with_only_multiple_args_no_id(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback: mock_commit_check.return_value = True - args = {'comment': 'Comitted via salt', - '__pub_user': 'root', - '__pub_arg': [{'comment': 'Comitted via salt', - 'confirm': 3, - 'sync': True}], - 'confirm': 3, - 'sync': True, - '__pub_fun': 'junos.rollback', - '__pub_jid': '20170221193945996362', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "comment": "Comitted via salt", + "__pub_user": "root", + "__pub_arg": [ + {"comment": "Comitted via salt", "confirm": 3, "sync": True} + ], + "confirm": 3, + "sync": True, + "__pub_fun": "junos.rollback", + "__pub_jid": "20170221193945996362", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } junos.rollback(**args) mock_rollback.assert_called_with(0) mock_commit.assert_called_once_with( - sync=True, confirm=3, comment='Comitted via salt') + sync=True, confirm=3, comment="Comitted via salt" + ) def test_rollback_with_diffs_file_option_when_diff_is_None(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback, \ - patch('salt.utils.files.fopen') as mock_fopen, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback, patch( + "salt.utils.files.fopen" + ) as mock_fopen, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff: mock_commit_check.return_value = True - mock_diff.return_value = 'diff' - args = {'__pub_user': 'root', - '__pub_arg': [{'diffs_file': '/home/regress/diff', - 'confirm': 2}], - 'confirm': 2, - '__pub_fun': 'junos.rollback', - '__pub_jid': '20170221205153884009', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': '', - 'diffs_file': '/home/regress/diff'} + mock_diff.return_value = "diff" + args = { + "__pub_user": "root", + "__pub_arg": [{"diffs_file": "/home/regress/diff", "confirm": 2}], + "confirm": 2, + "__pub_fun": "junos.rollback", + "__pub_jid": "20170221205153884009", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + "diffs_file": "/home/regress/diff", + } junos.rollback(**args) - mock_fopen.assert_called_with('/home/regress/diff', 'w') + mock_fopen.assert_called_with("/home/regress/diff", "w") def test_rollback_with_diffs_file_option(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback, \ - patch('salt.utils.files.fopen') as mock_fopen, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback, patch( + "salt.utils.files.fopen" + ) as mock_fopen, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff: mock_commit_check.return_value = True mock_diff.return_value = None - args = {'__pub_user': 'root', - '__pub_arg': [{'diffs_file': '/home/regress/diff', - 'confirm': 2}], - 'confirm': 2, - '__pub_fun': 'junos.rollback', - '__pub_jid': '20170221205153884009', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': '', - 'diffs_file': '/home/regress/diff'} + args = { + "__pub_user": "root", + "__pub_arg": [{"diffs_file": "/home/regress/diff", "confirm": 2}], + "confirm": 2, + "__pub_fun": "junos.rollback", + "__pub_jid": "20170221205153884009", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + "diffs_file": "/home/regress/diff", + } junos.rollback(**args) assert not mock_fopen.called def test_rollback_commit_check_exception(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback: mock_commit_check.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Could not commit check due to "Test exception"' - ret['out'] = False + ret["message"] = 'Could not commit check due to "Test exception"' + ret["out"] = False self.assertEqual(junos.rollback(), ret) def test_rollback_commit_exception(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.commit" + ) as mock_commit, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback: mock_commit_check.return_value = True mock_commit.side_effect = self.raise_exception ret = dict() - ret['message'] = \ - 'Rollback successful but commit failed with error "Test exception"' - ret['out'] = False + ret[ + "message" + ] = 'Rollback successful but commit failed with error "Test exception"' + ret["out"] = False self.assertEqual(junos.rollback(), ret) def test_rollback_commit_check_fails(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.rollback') as mock_rollback: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.rollback" + ) as mock_rollback: mock_commit_check.return_value = False ret = dict() - ret['message'] = 'Rollback succesfull but pre-commit check failed.' - ret['out'] = False + ret["message"] = "Rollback succesfull but pre-commit check failed." + ret["out"] = False self.assertEqual(junos.rollback(), ret) def test_diff_without_args(self): - with patch('jnpr.junos.utils.config.Config.diff') as mock_diff: + with patch("jnpr.junos.utils.config.Config.diff") as mock_diff: junos.diff() mock_diff.assert_called_with(rb_id=0) def test_diff_with_arg(self): - with patch('jnpr.junos.utils.config.Config.diff') as mock_diff: + with patch("jnpr.junos.utils.config.Config.diff") as mock_diff: junos.diff(id=2) mock_diff.assert_called_with(rb_id=2) def test_diff_exception(self): - with patch('jnpr.junos.utils.config.Config.diff') as mock_diff: + with patch("jnpr.junos.utils.config.Config.diff") as mock_diff: mock_diff.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Could not get diff with error "Test exception"' - ret['out'] = False + ret["message"] = 'Could not get diff with error "Test exception"' + ret["out"] = False self.assertEqual(junos.diff(), ret) def test_ping_without_args(self): ret = dict() - ret['message'] = 'Please specify the destination ip to ping.' - ret['out'] = False + ret["message"] = "Please specify the destination ip to ping." + ret["out"] = False self.assertEqual(junos.ping(), ret) def test_ping(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute: - junos.ping('1.1.1.1') + with patch("jnpr.junos.device.Device.execute") as mock_execute: + junos.ping("1.1.1.1") args = mock_execute.call_args - rpc = '<ping><count>5</count><host>1.1.1.1</host></ping>' + rpc = "<ping><count>5</count><host>1.1.1.1</host></ping>" self.assertEqualXML(args[0][0], rpc) def test_ping_ttl(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute: - args = {'__pub_user': 'sudo_drajvi', - '__pub_arg': ['1.1.1.1', - {'ttl': 3}], - '__pub_fun': 'junos.ping', - '__pub_jid': '20170306165237683279', - '__pub_tgt': 'mac_min', - 'ttl': 3, - '__pub_tgt_type': 'glob', - '__pub_ret': ''} - junos.ping('1.1.1.1', **args) + with patch("jnpr.junos.device.Device.execute") as mock_execute: + args = { + "__pub_user": "sudo_drajvi", + "__pub_arg": ["1.1.1.1", {"ttl": 3}], + "__pub_fun": "junos.ping", + "__pub_jid": "20170306165237683279", + "__pub_tgt": "mac_min", + "ttl": 3, + "__pub_tgt_type": "glob", + "__pub_ret": "", + } + junos.ping("1.1.1.1", **args) exec_args = mock_execute.call_args - rpc = '<ping><count>5</count><host>1.1.1.1</host><ttl>3</ttl></ping>' + rpc = "<ping><count>5</count><host>1.1.1.1</host><ttl>3</ttl></ping>" self.assertEqualXML(exec_args[0][0], rpc) def test_ping_exception(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute: + with patch("jnpr.junos.device.Device.execute") as mock_execute: mock_execute.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Execution failed due to "Test exception"' - ret['out'] = False - self.assertEqual(junos.ping('1.1.1.1'), ret) + ret["message"] = 'Execution failed due to "Test exception"' + ret["out"] = False + self.assertEqual(junos.ping("1.1.1.1"), ret) def test_cli_without_args(self): ret = dict() - ret['message'] = 'Please provide the CLI command to be executed.' - ret['out'] = False + ret["message"] = "Please provide the CLI command to be executed." + ret["out"] = False self.assertEqual(junos.cli(), ret) def test_cli_with_format_as_empty_string(self): - with patch('jnpr.junos.device.Device.cli') as mock_cli: - junos.cli('show version', format='') - mock_cli.assert_called_with('show version', 'text', warning=False) + with patch("jnpr.junos.device.Device.cli") as mock_cli: + junos.cli("show version", format="") + mock_cli.assert_called_with("show version", "text", warning=False) def test_cli(self): - with patch('jnpr.junos.device.Device.cli') as mock_cli: - mock_cli.return_vale = 'CLI result' + with patch("jnpr.junos.device.Device.cli") as mock_cli: + mock_cli.return_vale = "CLI result" ret = dict() - ret['message'] = 'CLI result' - ret['out'] = True - junos.cli('show version') - mock_cli.assert_called_with('show version', 'text', warning=False) + ret["message"] = "CLI result" + ret["out"] = True + junos.cli("show version") + mock_cli.assert_called_with("show version", "text", warning=False) def test_cli_format_xml(self): - with patch('salt.modules.junos.jxmlease.parse') as mock_jxml, \ - patch('salt.modules.junos.etree.tostring') as mock_to_string, \ - patch('jnpr.junos.device.Device.cli') as mock_cli: - mock_cli.return_value = '<root><a>test</a></root>' - mock_jxml.return_value = '<root><a>test</a></root>' - args = {'__pub_user': 'root', - '__pub_arg': [{'format': 'xml'}], - 'format': 'xml', - '__pub_fun': 'junos.cli', - '__pub_jid': '20170221182531323467', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + with patch("salt.modules.junos.jxmlease.parse") as mock_jxml, patch( + "salt.modules.junos.etree.tostring" + ) as mock_to_string, patch("jnpr.junos.device.Device.cli") as mock_cli: + mock_cli.return_value = "<root><a>test</a></root>" + mock_jxml.return_value = "<root><a>test</a></root>" + args = { + "__pub_user": "root", + "__pub_arg": [{"format": "xml"}], + "format": "xml", + "__pub_fun": "junos.cli", + "__pub_jid": "20170221182531323467", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = '<root><a>test</a></root>' - ret['out'] = True - self.assertEqual(junos.cli('show version', **args), ret) - mock_cli.assert_called_with('show version', 'xml', warning=False) - mock_to_string.assert_called_once_with('<root><a>test</a></root>') + ret["message"] = "<root><a>test</a></root>" + ret["out"] = True + self.assertEqual(junos.cli("show version", **args), ret) + mock_cli.assert_called_with("show version", "xml", warning=False) + mock_to_string.assert_called_once_with("<root><a>test</a></root>") assert mock_jxml.called def test_cli_exception_in_cli(self): - with patch('jnpr.junos.device.Device.cli') as mock_cli: + with patch("jnpr.junos.device.Device.cli") as mock_cli: mock_cli.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Execution failed due to "Test exception"' - ret['out'] = False - self.assertEqual(junos.cli('show version'), ret) + ret["message"] = 'Execution failed due to "Test exception"' + ret["out"] = False + self.assertEqual(junos.cli("show version"), ret) def test_shutdown_without_args(self): ret = dict() - ret['message'] = \ - 'Provide either one of the arguments: shutdown or reboot.' - ret['out'] = False + ret["message"] = "Provide either one of the arguments: shutdown or reboot." + ret["out"] = False self.assertEqual(junos.shutdown(), ret) def test_shutdown_with_reboot_args(self): - with patch('salt.modules.junos.SW.reboot') as mock_reboot: + with patch("salt.modules.junos.SW.reboot") as mock_reboot: ret = dict() - ret['message'] = 'Successfully powered off/rebooted.' - ret['out'] = True - args = {'__pub_user': 'root', '__pub_arg': [{'reboot': True}], - 'reboot': True, '__pub_fun': 'junos.shutdown', - '__pub_jid': '20170222213858582619', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + ret["message"] = "Successfully powered off/rebooted." + ret["out"] = True + args = { + "__pub_user": "root", + "__pub_arg": [{"reboot": True}], + "reboot": True, + "__pub_fun": "junos.shutdown", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } self.assertEqual(junos.shutdown(**args), ret) assert mock_reboot.called def test_shutdown_with_poweroff_args(self): - with patch('salt.modules.junos.SW.poweroff') as mock_poweroff: + with patch("salt.modules.junos.SW.poweroff") as mock_poweroff: ret = dict() - ret['message'] = 'Successfully powered off/rebooted.' - ret['out'] = True - args = {'__pub_user': 'root', '__pub_arg': [{'shutdown': True}], - 'reboot': True, '__pub_fun': 'junos.shutdown', - '__pub_jid': '20170222213858582619', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + ret["message"] = "Successfully powered off/rebooted." + ret["out"] = True + args = { + "__pub_user": "root", + "__pub_arg": [{"shutdown": True}], + "reboot": True, + "__pub_fun": "junos.shutdown", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } self.assertEqual(junos.shutdown(**args), ret) assert mock_poweroff.called def test_shutdown_with_shutdown_as_false(self): ret = dict() - ret['message'] = 'Nothing to be done.' - ret['out'] = False - args = {'__pub_user': 'root', '__pub_arg': [{'shutdown': False}], - 'reboot': True, '__pub_fun': 'junos.shutdown', - '__pub_jid': '20170222213858582619', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + ret["message"] = "Nothing to be done." + ret["out"] = False + args = { + "__pub_user": "root", + "__pub_arg": [{"shutdown": False}], + "reboot": True, + "__pub_fun": "junos.shutdown", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } self.assertEqual(junos.shutdown(**args), ret) def test_shutdown_with_in_min_arg(self): - with patch('salt.modules.junos.SW.poweroff') as mock_poweroff: - args = {'__pub_user': 'root', - 'in_min': 10, - '__pub_arg': [{'in_min': 10, - 'shutdown': True}], - 'reboot': True, - '__pub_fun': 'junos.shutdown', - '__pub_jid': '20170222231445709212', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + with patch("salt.modules.junos.SW.poweroff") as mock_poweroff: + args = { + "__pub_user": "root", + "in_min": 10, + "__pub_arg": [{"in_min": 10, "shutdown": True}], + "reboot": True, + "__pub_fun": "junos.shutdown", + "__pub_jid": "20170222231445709212", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } junos.shutdown(**args) mock_poweroff.assert_called_with(in_min=10) def test_shutdown_with_at_arg(self): - with patch('salt.modules.junos.SW.reboot') as mock_reboot: - args = {'__pub_user': 'root', - '__pub_arg': [{'at': '12:00 pm', - 'reboot': True}], - 'reboot': True, - '__pub_fun': 'junos.shutdown', - '__pub_jid': '201702276857', - 'at': '12:00 pm', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + with patch("salt.modules.junos.SW.reboot") as mock_reboot: + args = { + "__pub_user": "root", + "__pub_arg": [{"at": "12:00 pm", "reboot": True}], + "reboot": True, + "__pub_fun": "junos.shutdown", + "__pub_jid": "201702276857", + "at": "12:00 pm", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } junos.shutdown(**args) - mock_reboot.assert_called_with(at='12:00 pm') + mock_reboot.assert_called_with(at="12:00 pm") def test_shutdown_fail_with_exception(self): - with patch('salt.modules.junos.SW.poweroff') as mock_poweroff: + with patch("salt.modules.junos.SW.poweroff") as mock_poweroff: mock_poweroff.side_effect = self.raise_exception - args = {'__pub_user': 'root', '__pub_arg': [{'shutdown': True}], - 'shutdown': True, '__pub_fun': 'junos.shutdown', - '__pub_jid': '20170222213858582619', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"shutdown": True}], + "shutdown": True, + "__pub_fun": "junos.shutdown", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = 'Could not poweroff/reboot beacause "Test exception"' - ret['out'] = False + ret["message"] = 'Could not poweroff/reboot beacause "Test exception"' + ret["out"] = False self.assertEqual(junos.shutdown(**args), ret) def test_install_config_without_args(self): ret = dict() - ret['message'] = \ - 'Please provide the salt path where the configuration is present' - ret['out'] = False + ret[ + "message" + ] = "Please provide the salt path where the configuration is present" + ret["out"] = False self.assertEqual(junos.install_config(), ret) def test_install_config_cp_fails(self): - with patch('os.path.isfile') as mock_isfile: + with patch("os.path.isfile") as mock_isfile: mock_isfile.return_value = False ret = dict() - ret['message'] = 'Invalid file path.' - ret['out'] = False - self.assertEqual(junos.install_config('path'), ret) + ret["message"] = "Invalid file path." + ret["out"] = False + self.assertEqual(junos.install_config("path"), ret) def test_install_config_file_cp_fails(self): - with patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("os.path.isfile") as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 0 ret = dict() - ret['message'] = 'Template failed to render' - ret['out'] = False - self.assertEqual(junos.install_config('path'), ret) + ret["message"] = "Template failed to render" + ret["out"] = False + self.assertEqual(junos.install_config("path"), ret) def test_install_config(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True ret = dict() - ret['message'] = 'Successfully loaded and committed!' - ret['out'] = True - self.assertEqual(junos.install_config('actual/path/config.set'), ret) - mock_load.assert_called_with(path='test/path/config', format='set') + ret["message"] = "Successfully loaded and committed!" + ret["out"] = True + self.assertEqual(junos.install_config("actual/path/config.set"), ret) + mock_load.assert_called_with(path="test/path/config", format="set") def test_install_config_xml_file(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True ret = dict() - ret['message'] = 'Successfully loaded and committed!' - ret['out'] = True - self.assertEqual(junos.install_config('actual/path/config.xml'), ret) - mock_load.assert_called_with(path='test/path/config', format='xml') + ret["message"] = "Successfully loaded and committed!" + ret["out"] = True + self.assertEqual(junos.install_config("actual/path/config.xml"), ret) + mock_load.assert_called_with(path="test/path/config", format="xml") def test_install_config_text_file(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True ret = dict() - ret['message'] = 'Successfully loaded and committed!' - ret['out'] = True - self.assertEqual(junos.install_config('actual/path/config'), ret) - mock_load.assert_called_with(path='test/path/config', format='text') + ret["message"] = "Successfully loaded and committed!" + ret["out"] = True + self.assertEqual(junos.install_config("actual/path/config"), ret) + mock_load.assert_called_with(path="test/path/config", format="text") def test_install_config_replace(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True - args = {'__pub_user': 'root', '__pub_arg': [{'replace': True}], - 'replace': True, '__pub_fun': 'junos.install_config', - '__pub_jid': '20170222213858582619', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"replace": True}], + "replace": True, + "__pub_fun": "junos.install_config", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = 'Successfully loaded and committed!' - ret['out'] = True + ret["message"] = "Successfully loaded and committed!" + ret["out"] = True self.assertEqual( - junos.install_config( - 'actual/path/config.set', - **args), - ret) + junos.install_config("actual/path/config.set", **args), ret + ) mock_load.assert_called_with( - path='test/path/config', - format='set', - merge=False) + path="test/path/config", format="set", merge=False + ) def test_install_config_overwrite(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True - args = {'__pub_user': 'root', '__pub_arg': [{'overwrite': True}], - 'overwrite': True, '__pub_fun': 'junos.install_config', - '__pub_jid': '20170222213858582619', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"overwrite": True}], + "overwrite": True, + "__pub_fun": "junos.install_config", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = 'Successfully loaded and committed!' - ret['out'] = True + ret["message"] = "Successfully loaded and committed!" + ret["out"] = True self.assertEqual( - junos.install_config( - 'actual/path/config.xml', - **args), - ret) + junos.install_config("actual/path/config.xml", **args), ret + ) mock_load.assert_called_with( - path='test/path/config', - format='xml', - overwrite=True) + path="test/path/config", format="xml", overwrite=True + ) def test_install_config_overwrite_false(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True - args = {'__pub_user': 'root', '__pub_arg': [{'overwrite': False}], - 'overwrite': False, '__pub_fun': 'junos.install_config', - '__pub_jid': '20170222213858582619', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"overwrite": False}], + "overwrite": False, + "__pub_fun": "junos.install_config", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = 'Successfully loaded and committed!' - ret['out'] = True - self.assertEqual( - junos.install_config( - 'actual/path/config', - **args), - ret) + ret["message"] = "Successfully loaded and committed!" + ret["out"] = True + self.assertEqual(junos.install_config("actual/path/config", **args), ret) mock_load.assert_called_with( - path='test/path/config', format='text', merge=True) + path="test/path/config", format="text", merge=True + ) def test_install_config_load_causes_exception(self): - with patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.diff") as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.safe_rm") as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' + mock_mkstemp.return_value = "test/path/config" mock_load.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Could not load configuration due to : "Test exception"' - ret['format'] = 'set' - ret['out'] = False - self.assertEqual( - junos.install_config( - path='actual/path/config.set'), ret) + ret["message"] = 'Could not load configuration due to : "Test exception"' + ret["format"] = "set" + ret["out"] = False + self.assertEqual(junos.install_config(path="actual/path/config.set"), ret) def test_install_config_no_diff(self): - with patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.diff") as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.safe_rm") as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' + mock_mkstemp.return_value = "test/path/config" mock_diff.return_value = None ret = dict() - ret['message'] = 'Configuration already applied!' - ret['out'] = True - self.assertEqual(junos.install_config('actual/path/config'), ret) + ret["message"] = "Configuration already applied!" + ret["out"] = True + self.assertEqual(junos.install_config("actual/path/config"), ret) def test_install_config_write_diff(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('salt.utils.files.fopen') as mock_fopen, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "salt.utils.files.fopen" + ) as mock_fopen, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True - args = {'__pub_user': 'root', - '__pub_arg': [{'diffs_file': 'copy/config/here'}], - 'diffs_file': 'copy/config/here', - '__pub_fun': 'junos.install_config', - '__pub_jid': '20170222213858582619', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"diffs_file": "copy/config/here"}], + "diffs_file": "copy/config/here", + "__pub_fun": "junos.install_config", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = 'Successfully loaded and committed!' - ret['out'] = True - self.assertEqual( - junos.install_config( - 'actual/path/config', - **args), - ret) - mock_fopen.assert_called_with('copy/config/here', 'w') + ret["message"] = "Successfully loaded and committed!" + ret["out"] = True + self.assertEqual(junos.install_config("actual/path/config", **args), ret) + mock_fopen.assert_called_with("copy/config/here", "w") def test_install_config_write_diff_exception(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('salt.utils.files.fopen') as mock_fopen, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "salt.utils.files.fopen" + ) as mock_fopen, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True mock_fopen.side_effect = self.raise_exception - args = {'__pub_user': 'root', - '__pub_arg': [{'diffs_file': 'copy/config/here'}], - 'diffs_file': 'copy/config/here', - '__pub_fun': 'junos.install_config', - '__pub_jid': '20170222213858582619', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"diffs_file": "copy/config/here"}], + "diffs_file": "copy/config/here", + "__pub_fun": "junos.install_config", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = 'Could not write into diffs_file due to: "Test exception"' - ret['out'] = False - self.assertEqual( - junos.install_config( - 'actual/path/config', - **args), - ret) - mock_fopen.assert_called_with('copy/config/here', 'w') + ret["message"] = 'Could not write into diffs_file due to: "Test exception"' + ret["out"] = False + self.assertEqual(junos.install_config("actual/path/config", **args), ret) + mock_fopen.assert_called_with("copy/config/here", "w") def test_install_config_commit_params(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True - args = {'comment': 'comitted via salt', - '__pub_user': 'root', - '__pub_arg': [{'comment': 'comitted via salt', - 'confirm': 3}], - 'confirm': 3, - '__pub_fun': 'junos.commit', - '__pub_jid': '20170221182856987820', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} + args = { + "comment": "comitted via salt", + "__pub_user": "root", + "__pub_arg": [{"comment": "comitted via salt", "confirm": 3}], + "confirm": 3, + "__pub_fun": "junos.commit", + "__pub_jid": "20170221182856987820", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = 'Successfully loaded and committed!' - ret['out'] = True - self.assertEqual( - junos.install_config( - 'actual/path/config', - **args), - ret) - mock_commit.assert_called_with(comment='comitted via salt', confirm=3) + ret["message"] = "Successfully loaded and committed!" + ret["out"] = True + self.assertEqual(junos.install_config("actual/path/config", **args), ret) + mock_commit.assert_called_with(comment="comitted via salt", confirm=3) def test_install_config_commit_check_fails(self): - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = False ret = dict() - ret['message'] = 'Loaded configuration but commit check failed, hence rolling back configuration.' - ret['out'] = False - self.assertEqual(junos.install_config('actual/path/config.xml'), ret) + ret[ + "message" + ] = "Loaded configuration but commit check failed, hence rolling back configuration." + ret["out"] = False + self.assertEqual(junos.install_config("actual/path/config.xml"), ret) def test_install_config_commit_exception(self): - with patch('jnpr.junos.utils.config.Config.commit') as mock_commit, \ - patch('jnpr.junos.utils.config.Config.commit_check') as mock_commit_check, \ - patch('jnpr.junos.utils.config.Config.diff') as mock_diff, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.config.Config.commit") as mock_commit, patch( + "jnpr.junos.utils.config.Config.commit_check" + ) as mock_commit_check, patch( + "jnpr.junos.utils.config.Config.diff" + ) as mock_diff, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_isfile.return_value = True mock_getsize.return_value = 10 - mock_mkstemp.return_value = 'test/path/config' - mock_diff.return_value = 'diff' + mock_mkstemp.return_value = "test/path/config" + mock_diff.return_value = "diff" mock_commit_check.return_value = True mock_commit.side_effect = self.raise_exception ret = dict() - ret['message'] = \ - 'Commit check successful but commit failed with "Test exception"' - ret['out'] = False - self.assertEqual(junos.install_config('actual/path/config'), ret) + ret[ + "message" + ] = 'Commit check successful but commit failed with "Test exception"' + ret["out"] = False + self.assertEqual(junos.install_config("actual/path/config"), ret) def test_zeroize(self): - with patch('jnpr.junos.device.Device.cli') as mock_cli: + with patch("jnpr.junos.device.Device.cli") as mock_cli: result = junos.zeroize() ret = dict() - ret['out'] = True - ret['message'] = 'Completed zeroize and rebooted' - mock_cli.assert_called_once_with('request system zeroize') + ret["out"] = True + ret["message"] = "Completed zeroize and rebooted" + mock_cli.assert_called_once_with("request system zeroize") self.assertEqual(result, ret) def test_zeroize_throw_exception(self): - with patch('jnpr.junos.device.Device.cli') as mock_cli: + with patch("jnpr.junos.device.Device.cli") as mock_cli: mock_cli.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Could not zeroize due to : "Test exception"' - ret['out'] = False + ret["message"] = 'Could not zeroize due to : "Test exception"' + ret["out"] = False self.assertEqual(junos.zeroize(), ret) def test_install_os_without_args(self): ret = dict() - ret['message'] = \ - 'Please provide the salt path where the junos image is present.' - ret['out'] = False + ret[ + "message" + ] = "Please provide the salt path where the junos image is present." + ret["out"] = False self.assertEqual(junos.install_os(), ret) def test_install_os_cp_fails(self): - with patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("os.path.isfile") as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_getsize.return_value = 10 mock_isfile.return_value = False ret = dict() - ret['message'] = 'Invalid image path.' - ret['out'] = False - self.assertEqual(junos.install_os('/image/path/'), ret) + ret["message"] = "Invalid image path." + ret["out"] = False + self.assertEqual(junos.install_os("/image/path/"), ret) def test_install_os_image_cp_fails(self): - with patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("os.path.isfile") as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_getsize.return_value = 0 mock_isfile.return_value = True ret = dict() - ret['message'] = 'Failed to copy image' - ret['out'] = False - self.assertEqual(junos.install_os('/image/path/'), ret) + ret["message"] = "Failed to copy image" + ret["out"] = False + self.assertEqual(junos.install_os("/image/path/"), ret) def test_install_os(self): - with patch('jnpr.junos.utils.sw.SW.install') as mock_install, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.sw.SW.install") as mock_install, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch("salt.utils.files.mkstemp") as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_getsize.return_value = 10 mock_isfile.return_value = True ret = dict() - ret['out'] = True - ret['message'] = 'Installed the os.' - self.assertEqual(junos.install_os('path'), ret) + ret["out"] = True + ret["message"] = "Installed the os." + self.assertEqual(junos.install_os("path"), ret) def test_install_os_with_reboot_arg(self): - with patch('jnpr.junos.utils.sw.SW.install') as mock_install, \ - patch('jnpr.junos.utils.sw.SW.reboot') as mock_reboot, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.sw.SW.install") as mock_install, patch( + "jnpr.junos.utils.sw.SW.reboot" + ) as mock_reboot, patch("salt.utils.files.safe_rm") as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_getsize.return_value = 10 mock_isfile.return_value = True - args = {'__pub_user': 'root', '__pub_arg': [{'reboot': True}], - 'reboot': True, '__pub_fun': 'junos.install_os', - '__pub_jid': '20170222213858582619', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"reboot": True}], + "reboot": True, + "__pub_fun": "junos.install_os", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = 'Successfully installed and rebooted!' - ret['out'] = True - self.assertEqual(junos.install_os('path', **args), ret) + ret["message"] = "Successfully installed and rebooted!" + ret["out"] = True + self.assertEqual(junos.install_os("path", **args), ret) def test_install_os_pyez_install_throws_exception(self): - with patch('jnpr.junos.utils.sw.SW.install') as mock_install, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.sw.SW.install") as mock_install, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch("salt.utils.files.mkstemp") as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_getsize.return_value = 10 mock_isfile.return_value = True mock_install.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Installation failed due to: "Test exception"' - ret['out'] = False - self.assertEqual(junos.install_os('path'), ret) + ret["message"] = 'Installation failed due to: "Test exception"' + ret["out"] = False + self.assertEqual(junos.install_os("path"), ret) def test_install_os_with_reboot_raises_exception(self): - with patch('jnpr.junos.utils.sw.SW.install') as mock_install, \ - patch('jnpr.junos.utils.sw.SW.reboot') as mock_reboot, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.sw.SW.install") as mock_install, patch( + "jnpr.junos.utils.sw.SW.reboot" + ) as mock_reboot, patch("salt.utils.files.safe_rm") as mock_safe_rm, patch( + "salt.utils.files.mkstemp" + ) as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_getsize.return_value = 10 mock_isfile.return_value = True mock_reboot.side_effect = self.raise_exception - args = {'__pub_user': 'root', '__pub_arg': [{'reboot': True}], - 'reboot': True, '__pub_fun': 'junos.install_os', - '__pub_jid': '20170222213858582619', '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', '__pub_ret': ''} + args = { + "__pub_user": "root", + "__pub_arg": [{"reboot": True}], + "reboot": True, + "__pub_fun": "junos.install_os", + "__pub_jid": "20170222213858582619", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } ret = dict() - ret['message'] = \ - 'Installation successful but reboot failed due to : "Test exception"' - ret['out'] = False - self.assertEqual(junos.install_os('path', **args), ret) + ret[ + "message" + ] = 'Installation successful but reboot failed due to : "Test exception"' + ret["out"] = False + self.assertEqual(junos.install_os("path", **args), ret) def test_install_os_no_copy(self): - with patch('jnpr.junos.utils.sw.SW.install') as mock_install, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.sw.SW.install") as mock_install, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch("salt.utils.files.mkstemp") as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_getsize.return_value = 10 mock_isfile.return_value = True ret = dict() - ret['out'] = True - ret['message'] = 'Installed the os.' - self.assertEqual(junos.install_os('path', no_copy=True), ret) - mock_install.assert_called_with(u'path', no_copy=True, progress=True) + ret["out"] = True + ret["message"] = "Installed the os." + self.assertEqual(junos.install_os("path", no_copy=True), ret) + mock_install.assert_called_with("path", no_copy=True, progress=True) mock_mkstemp.assert_not_called() mock_safe_rm.assert_not_called() def test_install_os_issu(self): - with patch('jnpr.junos.utils.sw.SW.install') as mock_install, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.sw.SW.install") as mock_install, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch("salt.utils.files.mkstemp") as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_getsize.return_value = 10 mock_isfile.return_value = True ret = dict() - ret['out'] = True - ret['message'] = 'Installed the os.' - self.assertEqual(junos.install_os('path', issu=True), ret) + ret["out"] = True + ret["message"] = "Installed the os." + self.assertEqual(junos.install_os("path", issu=True), ret) mock_install.assert_called_with(ANY, issu=True, progress=True) def test_install_os_add_params(self): - with patch('jnpr.junos.utils.sw.SW.install') as mock_install, \ - patch('salt.utils.files.safe_rm') as mock_safe_rm, \ - patch('salt.utils.files.mkstemp') as mock_mkstemp, \ - patch('os.path.isfile') as mock_isfile, \ - patch('os.path.getsize') as mock_getsize: + with patch("jnpr.junos.utils.sw.SW.install") as mock_install, patch( + "salt.utils.files.safe_rm" + ) as mock_safe_rm, patch("salt.utils.files.mkstemp") as mock_mkstemp, patch( + "os.path.isfile" + ) as mock_isfile, patch( + "os.path.getsize" + ) as mock_getsize: mock_getsize.return_value = 10 mock_isfile.return_value = True ret = dict() - ret['out'] = True - ret['message'] = 'Installed the os.' - remote_path = '/path/to/file' - self.assertEqual(junos.install_os('path', remote_path=remote_path, nssu=True, validate=True), ret) - mock_install.assert_called_with(ANY, nssu=True, remote_path=remote_path, progress=True, validate=True) + ret["out"] = True + ret["message"] = "Installed the os." + remote_path = "/path/to/file" + self.assertEqual( + junos.install_os( + "path", remote_path=remote_path, nssu=True, validate=True + ), + ret, + ) + mock_install.assert_called_with( + ANY, nssu=True, remote_path=remote_path, progress=True, validate=True + ) def test_file_copy_without_args(self): ret = dict() - ret['message'] = \ - 'Please provide the absolute path of the file to be copied.' - ret['out'] = False + ret["message"] = "Please provide the absolute path of the file to be copied." + ret["out"] = False self.assertEqual(junos.file_copy(), ret) def test_file_copy_invalid_src(self): - with patch('os.path.isfile') as mock_isfile: + with patch("os.path.isfile") as mock_isfile: mock_isfile.return_value = False ret = dict() - ret['message'] = 'Invalid source file path' - ret['out'] = False - self.assertEqual(junos.file_copy('invalid/file/path', 'file'), ret) + ret["message"] = "Invalid source file path" + ret["out"] = False + self.assertEqual(junos.file_copy("invalid/file/path", "file"), ret) def test_file_copy_without_dest(self): ret = dict() - ret['message'] = \ - 'Please provide the absolute path of the destination where the file is to be copied.' - ret['out'] = False - with patch('salt.modules.junos.os.path.isfile') as mck: + ret[ + "message" + ] = "Please provide the absolute path of the destination where the file is to be copied." + ret["out"] = False + with patch("salt.modules.junos.os.path.isfile") as mck: mck.return_value = True - self.assertEqual(junos.file_copy('/home/user/config.set'), ret) + self.assertEqual(junos.file_copy("/home/user/config.set"), ret) def test_file_copy(self): - with patch('salt.modules.junos.SCP') as mock_scp, \ - patch('os.path.isfile') as mock_isfile: + with patch("salt.modules.junos.SCP") as mock_scp, patch( + "os.path.isfile" + ) as mock_isfile: mock_isfile.return_value = True ret = dict() - ret['message'] = 'Successfully copied file from test/src/file to file' - ret['out'] = True - self.assertEqual( - junos.file_copy( - dest='file', - src='test/src/file'), - ret) + ret["message"] = "Successfully copied file from test/src/file to file" + ret["out"] = True + self.assertEqual(junos.file_copy(dest="file", src="test/src/file"), ret) def test_file_copy_exception(self): - with patch('salt.modules.junos.SCP') as mock_scp, \ - patch('os.path.isfile') as mock_isfile: + with patch("salt.modules.junos.SCP") as mock_scp, patch( + "os.path.isfile" + ) as mock_isfile: mock_isfile.return_value = True mock_scp.side_effect = self.raise_exception ret = dict() - ret['message'] = 'Could not copy file : "Test exception"' - ret['out'] = False - self.assertEqual( - junos.file_copy( - dest='file', - src='test/src/file'), - ret) + ret["message"] = 'Could not copy file : "Test exception"' + ret["out"] = False + self.assertEqual(junos.file_copy(dest="file", src="test/src/file"), ret) # These test cases test the __virtual__ function, used internally by salt # to check if the given module is loadable. This function is not used by @@ -1416,279 +1762,321 @@ class Test_Junos_Module(TestCase, LoaderModuleMockMixin, XMLEqualityMixin): def test_virtual_proxy_unavailable(self): with patch.dict(junos.__opts__, {}): - res = (False, 'The junos module could not be ' - 'loaded: junos-eznc or jxmlease or proxy could not be loaded.') + res = ( + False, + "The junos module could not be " + "loaded: junos-eznc or jxmlease or proxy could not be loaded.", + ) self.assertEqual(junos.__virtual__(), res) def test_virtual_all_true(self): - with patch.dict(junos.__opts__, {'proxy': 'test'}): - self.assertEqual(junos.__virtual__(), 'junos') + with patch.dict(junos.__opts__, {"proxy": "test"}): + self.assertEqual(junos.__virtual__(), "junos") def test_rpc_without_args(self): ret = dict() - ret['message'] = 'Please provide the rpc to execute.' - ret['out'] = False + ret["message"] = "Please provide the rpc to execute." + ret["out"] = False self.assertEqual(junos.rpc(), ret) def test_rpc_get_config_exception(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute: + with patch("jnpr.junos.device.Device.execute") as mock_execute: mock_execute.side_effect = self.raise_exception ret = dict() - ret['message'] = 'RPC execution failed due to "Test exception"' - ret['out'] = False - self.assertEqual(junos.rpc('get_config'), ret) + ret["message"] = 'RPC execution failed due to "Test exception"' + ret["out"] = False + self.assertEqual(junos.rpc("get_config"), ret) def test_rpc_get_config_filter(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute: - mock_execute.return_value = etree.XML('<reply><rpc/></reply>') - args = {'__pub_user': 'root', - '__pub_arg': ['get-config', - {'filter': '<configuration><system/></configuration>'}], - '__pub_fun': 'junos.rpc', - '__pub_jid': '20170314162715866528', - '__pub_tgt': 'mac_min', - '__pub_tgt_type': 'glob', - 'filter': '<configuration><system/></configuration>', - '__pub_ret': ''} - junos.rpc('get-config', **args) + with patch("jnpr.junos.device.Device.execute") as mock_execute: + mock_execute.return_value = etree.XML("<reply><rpc/></reply>") + args = { + "__pub_user": "root", + "__pub_arg": [ + "get-config", + {"filter": "<configuration><system/></configuration>"}, + ], + "__pub_fun": "junos.rpc", + "__pub_jid": "20170314162715866528", + "__pub_tgt": "mac_min", + "__pub_tgt_type": "glob", + "filter": "<configuration><system/></configuration>", + "__pub_ret": "", + } + junos.rpc("get-config", **args) exec_args = mock_execute.call_args - expected_rpc = '<get-configuration dev_timeout="30" ' \ - 'format="xml"><configuration><system/></configuration></get-configuration>' + expected_rpc = ( + '<get-configuration dev_timeout="30" ' + 'format="xml"><configuration><system/></configuration></get-configuration>' + ) self.assertEqualXML(exec_args[0][0], expected_rpc) def test_rpc_get_interface_information(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute: - junos.rpc('get-interface-information', format='json') + with patch("jnpr.junos.device.Device.execute") as mock_execute: + junos.rpc("get-interface-information", format="json") args = mock_execute.call_args expected_rpc = '<get-interface-information format="json"/>' self.assertEqualXML(args[0][0], expected_rpc) def test_rpc_get_interface_information_with_kwargs(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute: - args = {'__pub_user': 'root', - '__pub_arg': ['get-interface-information', - '', - 'text', - {'terse': True, - 'interface_name': 'lo0'}], - 'terse': True, - '__pub_fun': 'junos.rpc', - '__pub_jid': '20170314160943363563', - '__pub_tgt': 'mac_min', - 'interface_name': 'lo0', - '__pub_tgt_type': 'glob', - '__pub_ret': ''} - junos.rpc('get-interface-information', format='text', **args) + with patch("jnpr.junos.device.Device.execute") as mock_execute: + args = { + "__pub_user": "root", + "__pub_arg": [ + "get-interface-information", + "", + "text", + {"terse": True, "interface_name": "lo0"}, + ], + "terse": True, + "__pub_fun": "junos.rpc", + "__pub_jid": "20170314160943363563", + "__pub_tgt": "mac_min", + "interface_name": "lo0", + "__pub_tgt_type": "glob", + "__pub_ret": "", + } + junos.rpc("get-interface-information", format="text", **args) args = mock_execute.call_args expected_rpc = ( - '<get-interface-information format="text">' - '<terse/><interface-name>lo0</interface-name></get-interface-information>' + '<get-interface-information format="text">' + "<terse/><interface-name>lo0</interface-name></get-interface-information>" ) self.assertEqualXML(etree.tostring(args[0][0]), expected_rpc) def test_rpc_get_chassis_inventory_filter_as_arg(self): - with patch('salt.modules.junos.jxmlease.parse') as mock_jxmlease, \ - patch('salt.modules.junos.etree.tostring') as mock_tostring, \ - patch('salt.modules.junos.logging.Logger.warning') as mock_warning, \ - patch('jnpr.junos.device.Device.execute') as mock_execute: + with patch("salt.modules.junos.jxmlease.parse") as mock_jxmlease, patch( + "salt.modules.junos.etree.tostring" + ) as mock_tostring, patch( + "salt.modules.junos.logging.Logger.warning" + ) as mock_warning, patch( + "jnpr.junos.device.Device.execute" + ) as mock_execute: junos.rpc( - 'get-chassis-inventory', - filter='<configuration><system/></configuration>') + "get-chassis-inventory", + filter="<configuration><system/></configuration>", + ) mock_warning.assert_called_with( - 'Filter ignored as it is only used with "get-config" rpc') + 'Filter ignored as it is only used with "get-config" rpc' + ) def test_rpc_get_interface_information_exception(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute: + with patch("jnpr.junos.device.Device.execute") as mock_execute: mock_execute.side_effect = self.raise_exception ret = dict() - ret['message'] = 'RPC execution failed due to "Test exception"' - ret['out'] = False - self.assertEqual(junos.rpc('get_interface_information'), ret) + ret["message"] = 'RPC execution failed due to "Test exception"' + ret["out"] = False + self.assertEqual(junos.rpc("get_interface_information"), ret) def test_rpc_write_file_format_text(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute: + with patch("jnpr.junos.device.Device.execute") as mock_execute: mock_execute.return_value = etree.XML( - '<rpc-reply>text rpc reply</rpc-reply>') - with patch('salt.utils.files.fopen', mock_open(), create=True) as m_open: - junos.rpc('get-chassis-inventory', '/path/to/file', format='text') + "<rpc-reply>text rpc reply</rpc-reply>" + ) + with patch("salt.utils.files.fopen", mock_open(), create=True) as m_open: + junos.rpc("get-chassis-inventory", "/path/to/file", format="text") writes = m_open.write_calls() - assert writes == ['text rpc reply'], writes + assert writes == ["text rpc reply"], writes def test_rpc_write_file_format_json(self): - with patch('jnpr.junos.device.Device.execute') as mock_execute, \ - patch('salt.utils.json.dumps') as mock_dumps: - mock_dumps.return_value = 'json rpc reply' - with patch('salt.utils.files.fopen', mock_open(), create=True) as m_open: - junos.rpc('get-chassis-inventory', '/path/to/file', format='json') + with patch("jnpr.junos.device.Device.execute") as mock_execute, patch( + "salt.utils.json.dumps" + ) as mock_dumps: + mock_dumps.return_value = "json rpc reply" + with patch("salt.utils.files.fopen", mock_open(), create=True) as m_open: + junos.rpc("get-chassis-inventory", "/path/to/file", format="json") writes = m_open.write_calls() - assert writes == ['json rpc reply'], writes + assert writes == ["json rpc reply"], writes def test_rpc_write_file(self): - with patch('salt.modules.junos.jxmlease.parse') as mock_parse, \ - patch('salt.modules.junos.etree.tostring') as mock_tostring, \ - patch('jnpr.junos.device.Device.execute') as mock_execute: - mock_tostring.return_value = 'xml rpc reply' - with patch('salt.utils.files.fopen', mock_open(), create=True) as m_open: - junos.rpc('get-chassis-inventory', '/path/to/file') + with patch("salt.modules.junos.jxmlease.parse") as mock_parse, patch( + "salt.modules.junos.etree.tostring" + ) as mock_tostring, patch("jnpr.junos.device.Device.execute") as mock_execute: + mock_tostring.return_value = "xml rpc reply" + with patch("salt.utils.files.fopen", mock_open(), create=True) as m_open: + junos.rpc("get-chassis-inventory", "/path/to/file") writes = m_open.write_calls() - assert writes == ['xml rpc reply'], writes + assert writes == ["xml rpc reply"], writes def test_lock_success(self): - ret_exp = {'out': True, 'message': 'Successfully locked the configuration.'} + ret_exp = {"out": True, "message": "Successfully locked the configuration."} ret = junos.lock() self.assertEqual(ret, ret_exp) def test_lock_error(self): - ret_exp = {'out': False, 'message': 'Could not gain lock due to : "LockError"'} - with patch('jnpr.junos.utils.config.Config.lock') as mock_lock: + ret_exp = {"out": False, "message": 'Could not gain lock due to : "LockError"'} + with patch("jnpr.junos.utils.config.Config.lock") as mock_lock: mock_lock.side_effect = LockError(None) ret = junos.lock() self.assertEqual(ret, ret_exp) def test_unlock_success(self): - ret_exp = {'out': True, 'message': 'Successfully unlocked the configuration.'} + ret_exp = {"out": True, "message": "Successfully unlocked the configuration."} ret = junos.unlock() self.assertEqual(ret, ret_exp) def test_unlock_error(self): - ret_exp = {'out': False, 'message': 'Could not unlock configuration due to : "UnlockError"'} - with patch('jnpr.junos.utils.config.Config.unlock') as mock_unlock: + ret_exp = { + "out": False, + "message": 'Could not unlock configuration due to : "UnlockError"', + } + with patch("jnpr.junos.utils.config.Config.unlock") as mock_unlock: mock_unlock.side_effect = UnlockError(None) ret = junos.unlock() self.assertEqual(ret, ret_exp) def test_load_none_path(self): - ret_exp = {'out': False, - 'message': 'Please provide the salt path where the configuration is present'} + ret_exp = { + "out": False, + "message": "Please provide the salt path where the configuration is present", + } ret = junos.load() self.assertEqual(ret, ret_exp) def test_load_wrong_tmp_file(self): - ret_exp = {'out': False, 'message': 'Invalid file path.'} - with patch('salt.utils.files.mkstemp') as mock_mkstemp: - mock_mkstemp.return_value = '/pat/to/tmp/file' - ret = junos.load('/path/to/file') + ret_exp = {"out": False, "message": "Invalid file path."} + with patch("salt.utils.files.mkstemp") as mock_mkstemp: + mock_mkstemp.return_value = "/pat/to/tmp/file" + ret = junos.load("/path/to/file") self.assertEqual(ret, ret_exp) def test_load_invalid_path(self): - ret_exp = {'out': False, 'message': 'Template failed to render'} - ret = junos.load('/path/to/file') + ret_exp = {"out": False, "message": "Template failed to render"} + ret = junos.load("/path/to/file") self.assertEqual(ret, ret_exp) def test_load_no_extension(self): - ret_exp = {'out': True, 'message': 'Successfully loaded the configuration.'} - with patch('os.path.getsize') as mock_getsize, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.mkstemp') as mock_mkstmp, \ - patch('os.path.isfile') as mock_isfile: + ret_exp = {"out": True, "message": "Successfully loaded the configuration."} + with patch("os.path.getsize") as mock_getsize, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.mkstemp") as mock_mkstmp, patch( + "os.path.isfile" + ) as mock_isfile: mock_getsize.return_value = 1000 - mock_mkstmp.return_value = '/path/to/file' + mock_mkstmp.return_value = "/path/to/file" mock_isfile.return_value = True - ret = junos.load('/path/to/file') - mock_load.assert_called_with(format='text', path='/path/to/file') + ret = junos.load("/path/to/file") + mock_load.assert_called_with(format="text", path="/path/to/file") self.assertEqual(ret, ret_exp) def test_load_xml_extension(self): - ret_exp = {'out': True, 'message': 'Successfully loaded the configuration.'} - with patch('os.path.getsize') as mock_getsize, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.mkstemp') as mock_mkstmp, \ - patch('os.path.isfile') as mock_isfile: + ret_exp = {"out": True, "message": "Successfully loaded the configuration."} + with patch("os.path.getsize") as mock_getsize, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.mkstemp") as mock_mkstmp, patch( + "os.path.isfile" + ) as mock_isfile: mock_getsize.return_value = 1000 - mock_mkstmp.return_value = '/path/to/file' + mock_mkstmp.return_value = "/path/to/file" mock_isfile.return_value = True - ret = junos.load('/path/to/file.xml') - mock_load.assert_called_with(format='xml', path='/path/to/file') + ret = junos.load("/path/to/file.xml") + mock_load.assert_called_with(format="xml", path="/path/to/file") self.assertEqual(ret, ret_exp) def test_load_set_extension(self): - ret_exp = {'out': True, 'message': 'Successfully loaded the configuration.'} - with patch('os.path.getsize') as mock_getsize, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.mkstemp') as mock_mkstmp, \ - patch('os.path.isfile') as mock_isfile: + ret_exp = {"out": True, "message": "Successfully loaded the configuration."} + with patch("os.path.getsize") as mock_getsize, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.mkstemp") as mock_mkstmp, patch( + "os.path.isfile" + ) as mock_isfile: mock_getsize.return_value = 1000 - mock_mkstmp.return_value = '/path/to/file' + mock_mkstmp.return_value = "/path/to/file" mock_isfile.return_value = True - ret = junos.load('/path/to/file.set') - mock_load.assert_called_with(format='set', path='/path/to/file') + ret = junos.load("/path/to/file.set") + mock_load.assert_called_with(format="set", path="/path/to/file") self.assertEqual(ret, ret_exp) def test_load_replace_true(self): - ret_exp = {'out': True, 'message': 'Successfully loaded the configuration.'} - with patch('os.path.getsize') as mock_getsize, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.mkstemp') as mock_mkstmp, \ - patch('os.path.isfile') as mock_isfile: + ret_exp = {"out": True, "message": "Successfully loaded the configuration."} + with patch("os.path.getsize") as mock_getsize, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.mkstemp") as mock_mkstmp, patch( + "os.path.isfile" + ) as mock_isfile: mock_getsize.return_value = 1000 - mock_mkstmp.return_value = '/path/to/file' + mock_mkstmp.return_value = "/path/to/file" mock_isfile.return_value = True - ret = junos.load('/path/to/file', replace=True) - mock_load.assert_called_with(format='text', merge=False, path='/path/to/file') + ret = junos.load("/path/to/file", replace=True) + mock_load.assert_called_with( + format="text", merge=False, path="/path/to/file" + ) self.assertEqual(ret, ret_exp) def test_load_replace_false(self): - ret_exp = {'out': True, 'message': 'Successfully loaded the configuration.'} - with patch('os.path.getsize') as mock_getsize, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.mkstemp') as mock_mkstmp, \ - patch('os.path.isfile') as mock_isfile: + ret_exp = {"out": True, "message": "Successfully loaded the configuration."} + with patch("os.path.getsize") as mock_getsize, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.mkstemp") as mock_mkstmp, patch( + "os.path.isfile" + ) as mock_isfile: mock_getsize.return_value = 1000 - mock_mkstmp.return_value = '/path/to/file' + mock_mkstmp.return_value = "/path/to/file" mock_isfile.return_value = True - ret = junos.load('/path/to/file', replace=False) - mock_load.assert_called_with(format='text', replace=False, path='/path/to/file') + ret = junos.load("/path/to/file", replace=False) + mock_load.assert_called_with( + format="text", replace=False, path="/path/to/file" + ) self.assertEqual(ret, ret_exp) def test_load_overwrite_true(self): - ret_exp = {'out': True, 'message': 'Successfully loaded the configuration.'} - with patch('os.path.getsize') as mock_getsize, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.mkstemp') as mock_mkstmp, \ - patch('os.path.isfile') as mock_isfile: + ret_exp = {"out": True, "message": "Successfully loaded the configuration."} + with patch("os.path.getsize") as mock_getsize, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.mkstemp") as mock_mkstmp, patch( + "os.path.isfile" + ) as mock_isfile: mock_getsize.return_value = 1000 - mock_mkstmp.return_value = '/path/to/file' + mock_mkstmp.return_value = "/path/to/file" mock_isfile.return_value = True - ret = junos.load('/path/to/file', overwrite=True) - mock_load.assert_called_with(format='text', overwrite=True, path='/path/to/file') + ret = junos.load("/path/to/file", overwrite=True) + mock_load.assert_called_with( + format="text", overwrite=True, path="/path/to/file" + ) self.assertEqual(ret, ret_exp) def test_load_overwrite_false(self): - ret_exp = {'out': True, 'message': 'Successfully loaded the configuration.'} - with patch('os.path.getsize') as mock_getsize, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.mkstemp') as mock_mkstmp, \ - patch('os.path.isfile') as mock_isfile: + ret_exp = {"out": True, "message": "Successfully loaded the configuration."} + with patch("os.path.getsize") as mock_getsize, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.mkstemp") as mock_mkstmp, patch( + "os.path.isfile" + ) as mock_isfile: mock_getsize.return_value = 1000 - mock_mkstmp.return_value = '/path/to/file' + mock_mkstmp.return_value = "/path/to/file" mock_isfile.return_value = True - ret = junos.load('/path/to/file', overwrite=False) - mock_load.assert_called_with(format='text', merge=True, path='/path/to/file') + ret = junos.load("/path/to/file", overwrite=False) + mock_load.assert_called_with( + format="text", merge=True, path="/path/to/file" + ) self.assertEqual(ret, ret_exp) def test_load_error(self): - ret_exp = {'out': False, - 'format': 'text', - 'message': 'Could not load configuration due to : "Test Error"'} - with patch('os.path.getsize') as mock_getsize, \ - patch('jnpr.junos.utils.config.Config.load') as mock_load, \ - patch('salt.utils.files.mkstemp') as mock_mkstmp, \ - patch('os.path.isfile') as mock_isfile: + ret_exp = { + "out": False, + "format": "text", + "message": 'Could not load configuration due to : "Test Error"', + } + with patch("os.path.getsize") as mock_getsize, patch( + "jnpr.junos.utils.config.Config.load" + ) as mock_load, patch("salt.utils.files.mkstemp") as mock_mkstmp, patch( + "os.path.isfile" + ) as mock_isfile: mock_getsize.return_value = 1000 - mock_mkstmp.return_value = '/path/to/file' + mock_mkstmp.return_value = "/path/to/file" mock_isfile.return_value = True - mock_load.side_effect = Exception('Test Error') - ret = junos.load('/path/to/file') + mock_load.side_effect = Exception("Test Error") + ret = junos.load("/path/to/file") self.assertEqual(ret, ret_exp) def test_commit_check_success(self): - ret_exp = {'out': True, 'message': 'Commit check succeeded.'} + ret_exp = {"out": True, "message": "Commit check succeeded."} ret = junos.commit_check() self.assertEqual(ret, ret_exp) def test_commit_check_error(self): - ret_exp = {'out': False, 'message': 'Commit check failed with '} - with patch('jnpr.junos.utils.config.Config.commit_check') as mock_check: + ret_exp = {"out": False, "message": "Commit check failed with "} + with patch("jnpr.junos.utils.config.Config.commit_check") as mock_check: mock_check.side_effect = Exception ret = junos.commit_check() self.assertEqual(ret, ret_exp) diff --git a/tests/unit/modules/test_k8s.py b/tests/unit/modules/test_k8s.py index c2cf96b7a5d..ed5637c2dc3 100644 --- a/tests/unit/modules/test_k8s.py +++ b/tests/unit/modules/test_k8s.py @@ -1,30 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" Unit Tests for the k8s execution module. -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import hashlib -import base64 -import time -from subprocess import Popen, PIPE +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.helpers import skip_if_binaries_missing +import base64 +import hashlib +import time +from subprocess import PIPE, Popen + +import salt.modules.k8s as k8s # Import Salt libs import salt.utils.files import salt.utils.json -import salt.modules.k8s as k8s +from salt.ext import six # Import 3rd-party libs from salt.ext.six.moves import range # pylint: disable=import-error -from salt.ext import six +from tests.support.helpers import skip_if_binaries_missing + +# Import Salt Testing libs +from tests.support.unit import TestCase -@skip_if_binaries_missing(['kubectl']) +@skip_if_binaries_missing(["kubectl"]) class TestK8SNamespace(TestCase): maxDiff = None @@ -40,7 +42,9 @@ class TestK8SNamespace(TestCase): def test_get_one_namespace(self): res = k8s.get_namespaces("default", apiserver_url="http://127.0.0.1:8080") a = res.get("metadata", {}).get("name", "a") - proc = Popen(["kubectl", "get", "namespaces", "default", "-o", "json"], stdout=PIPE) + proc = Popen( + ["kubectl", "get", "namespaces", "default", "-o", "json"], stdout=PIPE + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) b = kubectl_out.get("metadata", {}).get("name", "b") self.assertEqual(a, b) @@ -50,13 +54,15 @@ class TestK8SNamespace(TestCase): hash.update(six.text_type(time.time())) nsname = hash.hexdigest()[:16] res = k8s.create_namespace(nsname, apiserver_url="http://127.0.0.1:8080") - proc = Popen(["kubectl", "get", "namespaces", nsname, "-o", "json"], stdout=PIPE) + proc = Popen( + ["kubectl", "get", "namespaces", nsname, "-o", "json"], stdout=PIPE + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) # if creation is failed, kubernetes return non json error message self.assertTrue(isinstance(kubectl_out, dict)) -@skip_if_binaries_missing(['kubectl']) +@skip_if_binaries_missing(["kubectl"]) class TestK8SSecrets(TestCase): maxDiff = None @@ -69,17 +75,17 @@ class TestK8SSecrets(TestCase): self.request = { "apiVersion": "v1", "kind": "Secret", - "metadata": { - "name": self.name, - "namespace": "default", - }, + "metadata": {"name": self.name, "namespace": "default"}, "data": data, } def test_get_secrets(self): res = k8s.get_secrets("default", apiserver_url="http://127.0.0.1:8080") a = len(res.get("items", [])) - proc = Popen(["kubectl", "--namespace=default", "get", "secrets", "-o", "json"], stdout=PIPE) + proc = Popen( + ["kubectl", "--namespace=default", "get", "secrets", "-o", "json"], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) b = len(kubectl_out.get("items", [])) self.assertEqual(a, b) @@ -87,15 +93,20 @@ class TestK8SSecrets(TestCase): def test_get_one_secret(self): name = self.name filename = "/tmp/{0}.json".format(name) - with salt.utils.files.fopen(filename, 'w') as f: + with salt.utils.files.fopen(filename, "w") as f: salt.utils.json.dump(self.request, f) - create = Popen(["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE) + create = Popen( + ["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE + ) # wee need to give kubernetes time save data in etcd time.sleep(0.1) res = k8s.get_secrets("default", name, apiserver_url="http://127.0.0.1:8080") a = res.get("metadata", {}).get("name", "a") - proc = Popen(["kubectl", "--namespace=default", "get", "secrets", name, "-o", "json"], stdout=PIPE) + proc = Popen( + ["kubectl", "--namespace=default", "get", "secrets", name, "-o", "json"], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) b = kubectl_out.get("metadata", {}).get("name", "b") self.assertEqual(a, b) @@ -103,14 +114,18 @@ class TestK8SSecrets(TestCase): def test_get_decoded_secret(self): name = self.name filename = "/tmp/{0}.json".format(name) - with salt.utils.files.fopen(filename, 'w') as f: + with salt.utils.files.fopen(filename, "w") as f: salt.utils.json.dump(self.request, f) - create = Popen(["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE) + create = Popen( + ["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE + ) # wee need to give etcd to populate data on all nodes time.sleep(0.1) - res = k8s.get_secrets("default", name, apiserver_url="http://127.0.0.1:8080", decode=True) - a = res.get("data", {}).get("testsecret", ) + res = k8s.get_secrets( + "default", name, apiserver_url="http://127.0.0.1:8080", decode=True + ) + a = res.get("data", {}).get("testsecret",) self.assertEqual(a, "teststring") def test_create_secret(self): @@ -119,11 +134,18 @@ class TestK8SSecrets(TestCase): expected_data = {} for i in range(2): names.append("/tmp/{0}-{1}".format(name, i)) - with salt.utils.files.fopen("/tmp/{0}-{1}".format(name, i), 'w') as f: - expected_data["{0}-{1}".format(name, i)] = base64.b64encode("{0}{1}".format(name, i)) + with salt.utils.files.fopen("/tmp/{0}-{1}".format(name, i), "w") as f: + expected_data["{0}-{1}".format(name, i)] = base64.b64encode( + "{0}{1}".format(name, i) + ) f.write(salt.utils.stringutils.to_str("{0}{1}".format(name, i))) - res = k8s.create_secret("default", name, names, apiserver_url="http://127.0.0.1:8080") - proc = Popen(["kubectl", "--namespace=default", "get", "secrets", name, "-o", "json"], stdout=PIPE) + res = k8s.create_secret( + "default", name, names, apiserver_url="http://127.0.0.1:8080" + ) + proc = Popen( + ["kubectl", "--namespace=default", "get", "secrets", name, "-o", "json"], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) # if creation is failed, kubernetes return non json error message b = kubectl_out.get("data", {}) @@ -133,23 +155,34 @@ class TestK8SSecrets(TestCase): def test_update_secret(self): name = self.name filename = "/tmp/{0}.json".format(name) - with salt.utils.files.fopen(filename, 'w') as f: + with salt.utils.files.fopen(filename, "w") as f: salt.utils.json.dump(self.request, f) - create = Popen(["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE) + create = Popen( + ["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE + ) # wee need to give kubernetes time save data in etcd time.sleep(0.1) expected_data = {} names = [] for i in range(3): names.append("/tmp/{0}-{1}-updated".format(name, i)) - with salt.utils.files.fopen("/tmp/{0}-{1}-updated".format(name, i), 'w') as f: - expected_data["{0}-{1}-updated".format(name, i)] = base64.b64encode("{0}{1}-updated".format(name, i)) + with salt.utils.files.fopen( + "/tmp/{0}-{1}-updated".format(name, i), "w" + ) as f: + expected_data["{0}-{1}-updated".format(name, i)] = base64.b64encode( + "{0}{1}-updated".format(name, i) + ) f.write("{0}{1}-updated".format(name, i)) - res = k8s.update_secret("default", name, names, apiserver_url="http://127.0.0.1:8080") + res = k8s.update_secret( + "default", name, names, apiserver_url="http://127.0.0.1:8080" + ) # if creation is failed, kubernetes return non json error message - proc = Popen(["kubectl", "--namespace=default", "get", "secrets", name, "-o", "json"], stdout=PIPE) + proc = Popen( + ["kubectl", "--namespace=default", "get", "secrets", name, "-o", "json"], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) # if creation is failed, kubernetes return non json error message b = kubectl_out.get("data", {}) @@ -159,22 +192,30 @@ class TestK8SSecrets(TestCase): def test_delete_secret(self): name = self.name filename = "/tmp/{0}.json".format(name) - with salt.utils.files.fopen(filename, 'w') as f: + with salt.utils.files.fopen(filename, "w") as f: salt.utils.json.dump(self.request, f) - create = Popen(["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE) + create = Popen( + ["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE + ) # wee need to give kubernetes time save data in etcd time.sleep(0.1) res = k8s.delete_secret("default", name, apiserver_url="http://127.0.0.1:8080") time.sleep(0.1) - proc = Popen(["kubectl", "--namespace=default", "get", "secrets", name, "-o", "json"], stdout=PIPE, stderr=PIPE) + proc = Popen( + ["kubectl", "--namespace=default", "get", "secrets", name, "-o", "json"], + stdout=PIPE, + stderr=PIPE, + ) kubectl_out, err = proc.communicate() # stdout is empty, stderr is showing something like "not found" - self.assertEqual('', kubectl_out) - self.assertEqual('Error from server: secrets "{0}" not found\n'.format(name), err) + self.assertEqual("", kubectl_out) + self.assertEqual( + 'Error from server: secrets "{0}" not found\n'.format(name), err + ) -@skip_if_binaries_missing(['kubectl']) +@skip_if_binaries_missing(["kubectl"]) class TestK8SResourceQuotas(TestCase): maxDiff = None @@ -187,8 +228,12 @@ class TestK8SResourceQuotas(TestCase): def test_get_resource_quotas(self): name = self.name namespace = self.name - create_namespace = Popen(["kubectl", "create", "namespace", namespace], stdout=PIPE) - create_namespace = Popen(["kubectl", "create", "namespace", namespace], stdout=PIPE) + create_namespace = Popen( + ["kubectl", "create", "namespace", namespace], stdout=PIPE + ) + create_namespace = Popen( + ["kubectl", "create", "namespace", namespace], stdout=PIPE + ) request = """ apiVersion: v1 kind: ResourceQuota @@ -204,17 +249,32 @@ spec: resourcequotas: "1" secrets: "10" services: "5" -""".format(name) +""".format( + name + ) filename = "/tmp/{0}.yaml".format(name) - with salt.utils.files.fopen(filename, 'w') as f: + with salt.utils.files.fopen(filename, "w") as f: f.write(salt.utils.stringutils.to_str(request)) - create = Popen(["kubectl", "--namespace={0}".format(namespace), "create", "-f", filename], stdout=PIPE) + create = Popen( + ["kubectl", "--namespace={0}".format(namespace), "create", "-f", filename], + stdout=PIPE, + ) # wee need to give kubernetes time save data in etcd time.sleep(0.2) res = k8s.get_resource_quotas(namespace, apiserver_url="http://127.0.0.1:8080") a = len(res.get("items", [])) - proc = Popen(["kubectl", "--namespace={0}".format(namespace), "get", "quota", "-o", "json"], stdout=PIPE) + proc = Popen( + [ + "kubectl", + "--namespace={0}".format(namespace), + "get", + "quota", + "-o", + "json", + ], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) b = len(kubectl_out.get("items", [])) self.assertEqual(a, b) @@ -222,7 +282,9 @@ spec: def test_get_one_resource_quota(self): name = self.name namespace = self.name - create_namespace = Popen(["kubectl", "create", "namespace", namespace], stdout=PIPE) + create_namespace = Popen( + ["kubectl", "create", "namespace", namespace], stdout=PIPE + ) request = """ apiVersion: v1 kind: ResourceQuota @@ -238,17 +300,35 @@ spec: resourcequotas: "1" secrets: "10" services: "5" -""".format(name) +""".format( + name + ) filename = "/tmp/{0}.yaml".format(name) - with salt.utils.files.fopen(filename, 'w') as f: + with salt.utils.files.fopen(filename, "w") as f: f.write(salt.utils.stringutils.to_str(request)) - create = Popen(["kubectl", "--namespace={0}".format(namespace), "create", "-f", filename], stdout=PIPE) + create = Popen( + ["kubectl", "--namespace={0}".format(namespace), "create", "-f", filename], + stdout=PIPE, + ) # wee need to give kubernetes time save data in etcd time.sleep(0.2) - res = k8s.get_resource_quotas(namespace, name, apiserver_url="http://127.0.0.1:8080") + res = k8s.get_resource_quotas( + namespace, name, apiserver_url="http://127.0.0.1:8080" + ) a = res.get("metadata", {}).get("name", "a") - proc = Popen(["kubectl", "--namespace={0}".format(namespace), "get", "quota", name, "-o", "json"], stdout=PIPE) + proc = Popen( + [ + "kubectl", + "--namespace={0}".format(namespace), + "get", + "quota", + name, + "-o", + "json", + ], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) b = kubectl_out.get("metadata", {}).get("name", "b") self.assertEqual(a, b) @@ -256,20 +336,34 @@ spec: def test_create_resource_quota(self): name = self.name namespace = self.name - create_namespace = Popen(["kubectl", "create", "namespace", namespace], stdout=PIPE) - quota = { - "cpu": "20", - "memory": "1Gi" - } - res = k8s.create_resource_quota(namespace, quota, name=name, apiserver_url="http://127.0.0.1:8080") - proc = Popen(["kubectl", "--namespace={0}".format(namespace), "get", "quota", name, "-o", "json"], stdout=PIPE) + create_namespace = Popen( + ["kubectl", "create", "namespace", namespace], stdout=PIPE + ) + quota = {"cpu": "20", "memory": "1Gi"} + res = k8s.create_resource_quota( + namespace, quota, name=name, apiserver_url="http://127.0.0.1:8080" + ) + proc = Popen( + [ + "kubectl", + "--namespace={0}".format(namespace), + "get", + "quota", + name, + "-o", + "json", + ], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) self.assertTrue(isinstance(kubectl_out, dict)) def test_update_resource_quota(self): name = self.name namespace = self.name - create_namespace = Popen(["kubectl", "create", "namespace", namespace], stdout=PIPE) + create_namespace = Popen( + ["kubectl", "create", "namespace", namespace], stdout=PIPE + ) request = """ apiVersion: v1 kind: ResourceQuota @@ -285,26 +379,45 @@ spec: resourcequotas: "1" secrets: "10" services: "5" -""".format(name) +""".format( + name + ) filename = "/tmp/{0}.yaml".format(name) - with salt.utils.files.fopen(filename, 'w') as f: + with salt.utils.files.fopen(filename, "w") as f: f.write(salt.utils.stringutils.to_str(request)) - create = Popen(["kubectl", "--namespace={0}".format(namespace), "create", "-f", filename], stdout=PIPE) + create = Popen( + ["kubectl", "--namespace={0}".format(namespace), "create", "-f", filename], + stdout=PIPE, + ) # wee need to give kubernetes time save data in etcd time.sleep(0.2) - quota = { - "cpu": "10", - "memory": "2Gi" - } - res = k8s.create_resource_quota(namespace, quota, name=name, apiserver_url="http://127.0.0.1:8080", update=True) - proc = Popen(["kubectl", "--namespace={0}".format(namespace), "get", "quota", name, "-o", "json"], stdout=PIPE) + quota = {"cpu": "10", "memory": "2Gi"} + res = k8s.create_resource_quota( + namespace, + quota, + name=name, + apiserver_url="http://127.0.0.1:8080", + update=True, + ) + proc = Popen( + [ + "kubectl", + "--namespace={0}".format(namespace), + "get", + "quota", + name, + "-o", + "json", + ], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) limit = kubectl_out.get("spec").get("hard").get("memory") self.assertEqual("2Gi", limit) -@skip_if_binaries_missing(['kubectl']) +@skip_if_binaries_missing(["kubectl"]) class TestK8SLimitRange(TestCase): maxDiff = None @@ -316,15 +429,14 @@ class TestK8SLimitRange(TestCase): def test_create_limit_range(self): name = self.name - limits = { - "Container": { - "defaultRequest": { - "cpu": "100m" - } - } - } - res = k8s.create_limit_range("default", limits, name=name, apiserver_url="http://127.0.0.1:8080") - proc = Popen(["kubectl", "--namespace=default", "get", "limits", name, "-o", "json"], stdout=PIPE) + limits = {"Container": {"defaultRequest": {"cpu": "100m"}}} + res = k8s.create_limit_range( + "default", limits, name=name, apiserver_url="http://127.0.0.1:8080" + ) + proc = Popen( + ["kubectl", "--namespace=default", "get", "limits", name, "-o", "json"], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) self.assertTrue(isinstance(kubectl_out, dict)) @@ -344,31 +456,43 @@ spec: cpu: 100m memory: 256Mi type: Container -""".format(name) - limits = { - "Container": { - "defaultRequest": { - "cpu": "100m" - } - } - } +""".format( + name + ) + limits = {"Container": {"defaultRequest": {"cpu": "100m"}}} filename = "/tmp/{0}.yaml".format(name) - with salt.utils.files.fopen(filename, 'w') as f: + with salt.utils.files.fopen(filename, "w") as f: f.write(salt.utils.stringutils.to_str(request)) - create = Popen(["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE) + create = Popen( + ["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE + ) # wee need to give kubernetes time save data in etcd time.sleep(0.1) - res = k8s.create_limit_range("default", limits, name=name, apiserver_url="http://127.0.0.1:8080", update=True) - proc = Popen(["kubectl", "--namespace=default", "get", "limits", name, "-o", "json"], stdout=PIPE) + res = k8s.create_limit_range( + "default", + limits, + name=name, + apiserver_url="http://127.0.0.1:8080", + update=True, + ) + proc = Popen( + ["kubectl", "--namespace=default", "get", "limits", name, "-o", "json"], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) - limit = kubectl_out.get("spec").get("limits")[0].get("defaultRequest").get("cpu") + limit = ( + kubectl_out.get("spec").get("limits")[0].get("defaultRequest").get("cpu") + ) self.assertEqual("100m", limit) def test_get_limit_ranges(self): res = k8s.get_limit_ranges("default", apiserver_url="http://127.0.0.1:8080") a = len(res.get("items", [])) - proc = Popen(["kubectl", "--namespace=default", "get", "limits", "-o", "json"], stdout=PIPE) + proc = Popen( + ["kubectl", "--namespace=default", "get", "limits", "-o", "json"], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) b = len(kubectl_out.get("items", [])) self.assertEqual(a, b) @@ -389,17 +513,26 @@ spec: cpu: 100m memory: 256Mi type: Container -""".format(name) +""".format( + name + ) filename = "/tmp/{0}.yaml".format(name) - with salt.utils.files.fopen(filename, 'w') as f: + with salt.utils.files.fopen(filename, "w") as f: f.write(salt.utils.stringutils.to_str(request)) - create = Popen(["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE) + create = Popen( + ["kubectl", "--namespace=default", "create", "-f", filename], stdout=PIPE + ) # wee need to give kubernetes time save data in etcd time.sleep(0.1) - res = k8s.get_limit_ranges("default", name, apiserver_url="http://127.0.0.1:8080") + res = k8s.get_limit_ranges( + "default", name, apiserver_url="http://127.0.0.1:8080" + ) a = res.get("metadata", {}).get("name", "a") - proc = Popen(["kubectl", "--namespace=default", "get", "limits", name, "-o", "json"], stdout=PIPE) + proc = Popen( + ["kubectl", "--namespace=default", "get", "limits", name, "-o", "json"], + stdout=PIPE, + ) kubectl_out = salt.utils.json.loads(proc.communicate()[0]) b = kubectl_out.get("metadata", {}).get("name", "b") self.assertEqual(a, b) diff --git a/tests/unit/modules/test_kapacitor.py b/tests/unit/modules/test_kapacitor.py index b68cf3aa830..871a3dd0006 100644 --- a/tests/unit/modules/test_kapacitor.py +++ b/tests/unit/modules/test_kapacitor.py @@ -3,74 +3,89 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.modules.kapacitor as kapacitor + # Import Salt libs import salt.utils.json -import salt.modules.kapacitor as kapacitor # Import Salt testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import Mock, patch +from tests.support.unit import TestCase class KapacitorTestCase(TestCase, LoaderModuleMockMixin): - env = { - 'KAPACITOR_UNSAFE_SSL': 'false', - 'KAPACITOR_URL': 'http://localhost:9092' - } + env = {"KAPACITOR_UNSAFE_SSL": "false", "KAPACITOR_URL": "http://localhost:9092"} def setup_loader_modules(self): return { kapacitor: { - '__env__': 'test', - '__salt__': { - 'pkg.version': Mock(return_value='9999'), - 'config.option': Mock(side_effect=lambda key, default: default) - } + "__env__": "test", + "__salt__": { + "pkg.version": Mock(return_value="9999"), + "config.option": Mock(side_effect=lambda key, default: default), + }, } } def test_get_task_success(self): - http_body = salt.utils.json.dumps({ - 'script': 'test', - 'type': 'stream', - 'dbrps': [{'db': 'db', 'rp': 'rp'}], - 'status': 'enabled', - }) - query_ret = {'body': http_body, 'status': 200} - with patch('salt.utils.http.query', return_value=query_ret) as http_mock: - task = kapacitor.get_task('taskname') - http_mock.assert_called_once_with('http://localhost:9092/kapacitor/v1/tasks/taskname?skip-format=true', status=True) - self.assertEqual('test', task['script']) + http_body = salt.utils.json.dumps( + { + "script": "test", + "type": "stream", + "dbrps": [{"db": "db", "rp": "rp"}], + "status": "enabled", + } + ) + query_ret = {"body": http_body, "status": 200} + with patch("salt.utils.http.query", return_value=query_ret) as http_mock: + task = kapacitor.get_task("taskname") + http_mock.assert_called_once_with( + "http://localhost:9092/kapacitor/v1/tasks/taskname?skip-format=true", + status=True, + ) + self.assertEqual("test", task["script"]) def test_get_task_not_found(self): - query_ret = {'body': '{"Error":"unknown task taskname"}', 'status': 404} - with patch('salt.utils.http.query', return_value=query_ret) as http_mock: - task = kapacitor.get_task('taskname') - http_mock.assert_called_once_with('http://localhost:9092/kapacitor/v1/tasks/taskname?skip-format=true', status=True) + query_ret = {"body": '{"Error":"unknown task taskname"}', "status": 404} + with patch("salt.utils.http.query", return_value=query_ret) as http_mock: + task = kapacitor.get_task("taskname") + http_mock.assert_called_once_with( + "http://localhost:9092/kapacitor/v1/tasks/taskname?skip-format=true", + status=True, + ) self.assertEqual(None, task) def test_define_task(self): - cmd_mock = Mock(return_value={'retcode': 0}) - with patch.dict(kapacitor.__salt__, {'cmd.run_all': cmd_mock}): - kapacitor.define_task('taskname', '/tmp/script.tick', dbrps=['db.rp']) - cmd_mock.assert_called_once_with('kapacitor define taskname ' - '-tick /tmp/script.tick -type stream -dbrp db.rp', env=self.__class__.env) + cmd_mock = Mock(return_value={"retcode": 0}) + with patch.dict(kapacitor.__salt__, {"cmd.run_all": cmd_mock}): + kapacitor.define_task("taskname", "/tmp/script.tick", dbrps=["db.rp"]) + cmd_mock.assert_called_once_with( + "kapacitor define taskname " + "-tick /tmp/script.tick -type stream -dbrp db.rp", + env=self.__class__.env, + ) def test_enable_task(self): - cmd_mock = Mock(return_value={'retcode': 0}) - with patch.dict(kapacitor.__salt__, {'cmd.run_all': cmd_mock}): - kapacitor.enable_task('taskname') - cmd_mock.assert_called_once_with('kapacitor enable taskname', env=self.__class__.env) + cmd_mock = Mock(return_value={"retcode": 0}) + with patch.dict(kapacitor.__salt__, {"cmd.run_all": cmd_mock}): + kapacitor.enable_task("taskname") + cmd_mock.assert_called_once_with( + "kapacitor enable taskname", env=self.__class__.env + ) def test_disable_task(self): - cmd_mock = Mock(return_value={'retcode': 0}) - with patch.dict(kapacitor.__salt__, {'cmd.run_all': cmd_mock}): - kapacitor.disable_task('taskname') - cmd_mock.assert_called_once_with('kapacitor disable taskname', env=self.__class__.env) + cmd_mock = Mock(return_value={"retcode": 0}) + with patch.dict(kapacitor.__salt__, {"cmd.run_all": cmd_mock}): + kapacitor.disable_task("taskname") + cmd_mock.assert_called_once_with( + "kapacitor disable taskname", env=self.__class__.env + ) def test_delete_task(self): - cmd_mock = Mock(return_value={'retcode': 0}) - with patch.dict(kapacitor.__salt__, {'cmd.run_all': cmd_mock}): - kapacitor.delete_task('taskname') - cmd_mock.assert_called_once_with('kapacitor delete tasks taskname', env=self.__class__.env) + cmd_mock = Mock(return_value={"retcode": 0}) + with patch.dict(kapacitor.__salt__, {"cmd.run_all": cmd_mock}): + kapacitor.delete_task("taskname") + cmd_mock.assert_called_once_with( + "kapacitor delete tasks taskname", env=self.__class__.env + ) diff --git a/tests/unit/modules/test_kernelpkg_linux_apt.py b/tests/unit/modules/test_kernelpkg_linux_apt.py index 6eb5e154088..788b08d184c 100644 --- a/tests/unit/modules/test_kernelpkg_linux_apt.py +++ b/tests/unit/modules/test_kernelpkg_linux_apt.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for 'module.aptkernelpkg' :platform: Linux :maturity: develop .. versionadded:: 2018.3.0 -''' +""" # pylint: disable=invalid-name,no-member # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import re try: @@ -21,81 +22,97 @@ try: from tests.support.kernelpkg import KernelPkgTestCase import salt.modules.kernelpkg_linux_apt as kernelpkg from salt.exceptions import CommandExecutionError + HAS_MODULES = True except ImportError: HAS_MODULES = False -@skipIf(not HAS_MODULES, 'Salt modules could not be loaded') +@skipIf(not HAS_MODULES, "Salt modules could not be loaded") class AptKernelPkgTestCase(KernelPkgTestCase, TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.kernelpkg_linux_apt - ''' + """ _kernelpkg = kernelpkg - KERNEL_LIST = ['4.4.0-70-generic', '4.4.0-71-generic', '4.5.1-14-generic'] + KERNEL_LIST = ["4.4.0-70-generic", "4.4.0-71-generic", "4.5.1-14-generic"] PACKAGE_DICT = {} @classmethod def setUpClass(cls): - version = re.match(r'^(\d+\.\d+\.\d+)-(\d+)', cls.KERNEL_LIST[-1]) - cls.LATEST = '{0}.{1}'.format(version.group(1), version.group(2)) + version = re.match(r"^(\d+\.\d+\.\d+)-(\d+)", cls.KERNEL_LIST[-1]) + cls.LATEST = "{0}.{1}".format(version.group(1), version.group(2)) for kernel in cls.KERNEL_LIST: - pkg = '{0}-{1}'.format(kernelpkg._package_prefix(), kernel) # pylint: disable=protected-access + pkg = "{0}-{1}".format( + kernelpkg._package_prefix(), kernel + ) # pylint: disable=protected-access cls.PACKAGE_DICT[pkg] = pkg def setup_loader_modules(self): return { kernelpkg: { - '__grains__': { - 'kernelrelease': self.KERNEL_LIST[0] + "__grains__": {"kernelrelease": self.KERNEL_LIST[0]}, + "__salt__": { + "pkg.install": MagicMock(return_value={}), + "pkg.latest_version": MagicMock(return_value=self.LATEST), + "pkg.list_pkgs": MagicMock(return_value=self.PACKAGE_DICT), + "pkg.purge": MagicMock(return_value=None), + "system.reboot": MagicMock(return_value=None), }, - '__salt__': { - 'pkg.install': MagicMock(return_value={}), - 'pkg.latest_version': MagicMock(return_value=self.LATEST), - 'pkg.list_pkgs': MagicMock(return_value=self.PACKAGE_DICT), - 'pkg.purge': MagicMock(return_value=None), - 'system.reboot': MagicMock(return_value=None) - } } } def test_list_installed(self): - ''' + """ Test - Return return the latest installed kernel version - ''' - PACKAGE_LIST = ['{0}-{1}'.format(kernelpkg._package_prefix(), kernel) for kernel in self.KERNEL_LIST] # pylint: disable=protected-access + """ + PACKAGE_LIST = [ + "{0}-{1}".format(kernelpkg._package_prefix(), kernel) + for kernel in self.KERNEL_LIST + ] # pylint: disable=protected-access mock = MagicMock(return_value=PACKAGE_LIST) - with patch.dict(self._kernelpkg.__salt__, {'pkg.list_pkgs': mock}): + with patch.dict(self._kernelpkg.__salt__, {"pkg.list_pkgs": mock}): self.assertListEqual(self._kernelpkg.list_installed(), self.KERNEL_LIST) def test_list_installed_none(self): - ''' + """ Test - Return return the latest installed kernel version - ''' + """ mock = MagicMock(return_value=None) - with patch.dict(self._kernelpkg.__salt__, {'pkg.list_pkgs': mock}): + with patch.dict(self._kernelpkg.__salt__, {"pkg.list_pkgs": mock}): self.assertListEqual(self._kernelpkg.list_installed(), []) def test_remove_success(self): - ''' + """ Test - remove kernel package - ''' - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): + """ + with patch.object(self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1]): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): result = self._kernelpkg.remove(release=self.KERNEL_LIST[0]) - self.assertIn('removed', result) - target = '{0}-{1}'.format(self._kernelpkg._package_prefix(), self.KERNEL_LIST[0]) # pylint: disable=protected-access - self.assertListEqual(result['removed'], [target]) + self.assertIn("removed", result) + target = "{0}-{1}".format( + self._kernelpkg._package_prefix(), self.KERNEL_LIST[0] + ) # pylint: disable=protected-access + self.assertListEqual(result["removed"], [target]) def test_remove_error(self): - ''' + """ Test - remove kernel package - ''' + """ mock = MagicMock(side_effect=CommandExecutionError()) - with patch.dict(self._kernelpkg.__salt__, {'pkg.purge': mock}): - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): - self.assertRaises(CommandExecutionError, self._kernelpkg.remove, release=self.KERNEL_LIST[0]) + with patch.dict(self._kernelpkg.__salt__, {"pkg.purge": mock}): + with patch.object( + self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1] + ): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): + self.assertRaises( + CommandExecutionError, + self._kernelpkg.remove, + release=self.KERNEL_LIST[0], + ) diff --git a/tests/unit/modules/test_kernelpkg_linux_yum.py b/tests/unit/modules/test_kernelpkg_linux_yum.py index 5d55fafd479..1b2a9144fb0 100644 --- a/tests/unit/modules/test_kernelpkg_linux_yum.py +++ b/tests/unit/modules/test_kernelpkg_linux_yum.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for 'module.yumkernelpkg' :platform: Linux :maturity: develop .. versionadded:: 2018.3.0 -''' +""" # pylint: disable=invalid-name,no-member # Import Python Libs @@ -21,81 +21,96 @@ try: import salt.modules.kernelpkg_linux_yum as kernelpkg import salt.modules.yumpkg as pkg from salt.exceptions import CommandExecutionError + HAS_MODULES = True except ImportError: HAS_MODULES = False -@skipIf(not HAS_MODULES, 'Salt modules could not be loaded') +@skipIf(not HAS_MODULES, "Salt modules could not be loaded") class YumKernelPkgTestCase(KernelPkgTestCase, TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.kernelpkg_linux_yum - ''' + """ _kernelpkg = kernelpkg - KERNEL_LIST = ['3.10.0-327.el7', '3.11.0-327.el7', '4.9.1-100.el7'] + KERNEL_LIST = ["3.10.0-327.el7", "3.11.0-327.el7", "4.9.1-100.el7"] LATEST = KERNEL_LIST[-1] - OS_ARCH = 'x86_64' - OS_NAME = 'RedHat' + OS_ARCH = "x86_64" + OS_NAME = "RedHat" + OS_MAJORRELEASE = "7" def setup_loader_modules(self): return { kernelpkg: { - '__grains__': { - 'os': self.OS_NAME, - 'kernelrelease': '{0}.{1}'.format(self.KERNEL_LIST[0], self.OS_ARCH) + "__grains__": { + "os": self.OS_NAME, + "osmajorrelease": self.OS_MAJORRELEASE, + "kernelrelease": "{0}.{1}".format( + self.KERNEL_LIST[0], self.OS_ARCH + ), + }, + "__salt__": { + "pkg.normalize_name": pkg.normalize_name, + "pkg.upgrade": MagicMock(return_value={}), + "pkg.list_pkgs": MagicMock(return_value={}), + "pkg.version": MagicMock(return_value=self.KERNEL_LIST), + "system.reboot": MagicMock(return_value=None), + "config.get": MagicMock(return_value=True), }, - '__salt__': { - 'pkg.normalize_name': pkg.normalize_name, - 'pkg.upgrade': MagicMock(return_value={}), - 'pkg.list_pkgs': MagicMock(return_value={}), - 'pkg.version': MagicMock(return_value=self.KERNEL_LIST), - 'system.reboot': MagicMock(return_value=None), - 'config.get': MagicMock(return_value=True) - } }, - pkg: { - '__grains__': { - 'osarch': self.OS_ARCH - } - } + pkg: {"__grains__": {"osarch": self.OS_ARCH}}, } def test_list_installed(self): - ''' + """ Test - Return the latest installed kernel version - ''' + """ mock = MagicMock(return_value=self.KERNEL_LIST) - with patch.dict(self._kernelpkg.__salt__, {'pkg.version': mock}): + with patch.dict(self._kernelpkg.__salt__, {"pkg.version": mock}): self.assertListEqual(self._kernelpkg.list_installed(), self.KERNEL_LIST) def test_list_installed_none(self): - ''' + """ Test - Return the latest installed kernel version - ''' + """ mock = MagicMock(return_value=None) - with patch.dict(self._kernelpkg.__salt__, {'pkg.version': mock}): + with patch.dict(self._kernelpkg.__salt__, {"pkg.version": mock}): self.assertListEqual(self._kernelpkg.list_installed(), []) def test_remove_success(self): - ''' + """ Test - remove kernel package - ''' - mock = MagicMock(return_value={'retcode': 0, 'stderr': []}) - with patch.dict(self._kernelpkg.__salt__, {'cmd.run_all': mock}): - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): + """ + mock = MagicMock(return_value={"retcode": 0, "stderr": []}) + with patch.dict(self._kernelpkg.__salt__, {"cmd.run_all": mock}): + with patch.object( + self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1] + ): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): result = self._kernelpkg.remove(release=self.KERNEL_LIST[0]) - self.assertIn('removed', result) - target = '{0}-{1}'.format(self._kernelpkg._package_name(), self.KERNEL_LIST[0]) # pylint: disable=protected-access - self.assertListEqual(result['removed'], [target]) + self.assertIn("removed", result) + target = "{0}-{1}".format( + self._kernelpkg._package_name(), self.KERNEL_LIST[0] + ) # pylint: disable=protected-access + self.assertListEqual(result["removed"], [target]) def test_remove_error(self): - ''' + """ Test - remove kernel package - ''' - mock = MagicMock(return_value={'retcode': -1, 'stderr': []}) - with patch.dict(self._kernelpkg.__salt__, {'cmd.run_all': mock}): - with patch.object(self._kernelpkg, 'active', return_value=self.KERNEL_LIST[-1]): - with patch.object(self._kernelpkg, 'list_installed', return_value=self.KERNEL_LIST): - self.assertRaises(CommandExecutionError, self._kernelpkg.remove, release=self.KERNEL_LIST[0]) + """ + mock = MagicMock(return_value={"retcode": -1, "stderr": []}) + with patch.dict(self._kernelpkg.__salt__, {"cmd.run_all": mock}): + with patch.object( + self._kernelpkg, "active", return_value=self.KERNEL_LIST[-1] + ): + with patch.object( + self._kernelpkg, "list_installed", return_value=self.KERNEL_LIST + ): + self.assertRaises( + CommandExecutionError, + self._kernelpkg.remove, + release=self.KERNEL_LIST[0], + ) diff --git a/tests/unit/modules/test_key.py b/tests/unit/modules/test_key.py index 30a18b0c3b5..41ba49edd26 100644 --- a/tests/unit/modules/test_key.py +++ b/tests/unit/modules/test_key.py @@ -1,50 +1,49 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os.path -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.key as key # Import Salt Libs import salt.utils.crypt -import salt.modules.key as key + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class KeyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.key - ''' + """ + def setup_loader_modules(self): return {key: {}} def test_finger(self): - ''' + """ Test for finger - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(salt.utils.crypt, - 'pem_finger', return_value='A'): - with patch.dict(key.__opts__, - {'pki_dir': MagicMock(return_value='A'), 'hash_type': 'sha256'}): - self.assertEqual(key.finger(), 'A') + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(salt.utils.crypt, "pem_finger", return_value="A"): + with patch.dict( + key.__opts__, + {"pki_dir": MagicMock(return_value="A"), "hash_type": "sha256"}, + ): + self.assertEqual(key.finger(), "A") def test_finger_master(self): - ''' + """ Test for finger - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(salt.utils.crypt, - 'pem_finger', return_value='A'): - with patch.dict(key.__opts__, - {'pki_dir': 'A', 'hash_type': 'sha256'}): - self.assertEqual(key.finger_master(), 'A') + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(salt.utils.crypt, "pem_finger", return_value="A"): + with patch.dict(key.__opts__, {"pki_dir": "A", "hash_type": "sha256"}): + self.assertEqual(key.finger_master(), "A") diff --git a/tests/unit/modules/test_keyboard.py b/tests/unit/modules/test_keyboard.py index 5182549cf09..dee22be4318 100644 --- a/tests/unit/modules/test_keyboard.py +++ b/tests/unit/modules/test_keyboard.py @@ -1,69 +1,67 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.keyboard as keyboard +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class KeyboardTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.keyboard - ''' + """ + def setup_loader_modules(self): return {keyboard: {}} # 'get_sys' function tests: 1 def test_get_sys(self): - ''' + """ Test if it get current system keyboard setting - ''' - mock = MagicMock(return_value='X11 Layout=us') - with patch.dict(keyboard.__grains__, {'os_family': 'RedHat'}): - with patch.dict(keyboard.__salt__, {'cmd.run': mock}): - self.assertEqual(keyboard.get_sys(), 'us') + """ + mock = MagicMock(return_value="X11 Layout=us") + with patch.dict(keyboard.__grains__, {"os_family": "RedHat"}): + with patch.dict(keyboard.__salt__, {"cmd.run": mock}): + self.assertEqual(keyboard.get_sys(), "us") # 'set_sys' function tests: 1 def test_set_sys(self): - ''' + """ Test if it set current system keyboard setting - ''' - mock = MagicMock(return_value='us') - with patch.dict(keyboard.__grains__, {'os_family': 'RedHat'}): - with patch.dict(keyboard.__salt__, {'cmd.run': mock}): - with patch.dict(keyboard.__salt__, {'file.sed': MagicMock()}): - self.assertEqual(keyboard.set_sys('us'), 'us') + """ + mock = MagicMock(return_value="us") + with patch.dict(keyboard.__grains__, {"os_family": "RedHat"}): + with patch.dict(keyboard.__salt__, {"cmd.run": mock}): + with patch.dict(keyboard.__salt__, {"file.sed": MagicMock()}): + self.assertEqual(keyboard.set_sys("us"), "us") # 'get_x' function tests: 1 def test_get_x(self): - ''' + """ Test if it get current X keyboard setting - ''' - mock = MagicMock(return_value='layout: us') - with patch.dict(keyboard.__salt__, {'cmd.run': mock}): - self.assertEqual(keyboard.get_x(), 'us') + """ + mock = MagicMock(return_value="layout: us") + with patch.dict(keyboard.__salt__, {"cmd.run": mock}): + self.assertEqual(keyboard.get_x(), "us") # 'set_x' function tests: 1 def test_set_x(self): - ''' + """ Test if it set current X keyboard setting - ''' - mock = MagicMock(return_value='us') - with patch.dict(keyboard.__salt__, {'cmd.run': mock}): - self.assertEqual(keyboard.set_x('us'), 'us') + """ + mock = MagicMock(return_value="us") + with patch.dict(keyboard.__salt__, {"cmd.run": mock}): + self.assertEqual(keyboard.set_x("us"), "us") diff --git a/tests/unit/modules/test_keystone.py b/tests/unit/modules/test_keystone.py index ce72ee171ba..018c21bfc68 100644 --- a/tests/unit/modules/test_keystone.py +++ b/tests/unit/modules/test_keystone.py @@ -1,34 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs 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.unit import TestCase + class MockEC2(object): """ Mock of EC2 class """ + def __init__(self): - self.access = '' - self.secret = '' - self.tenant_id = '' - self.user_id = '' - self.connection_args = '' - self.profile = '' + self.access = "" + self.secret = "" + self.tenant_id = "" + self.user_id = "" + self.connection_args = "" + self.profile = "" @staticmethod def create(userid, tenantid): @@ -74,13 +72,14 @@ class MockEndpoints(object): """ Mock of Endpoints class """ + def __init__(self): - self.id = '007' - self.region = 'RegionOne' - self.adminurl = 'adminurl' - self.internalurl = 'internalurl' - self.publicurl = 'publicurl' - self.service_id = '117' + self.id = "007" + self.region = "RegionOne" + self.adminurl = "adminurl" + self.internalurl = "internalurl" + self.publicurl = "publicurl" + self.service_id = "117" @staticmethod def list(): @@ -108,13 +107,14 @@ class MockServices(object): """ Mock of Services class """ + flag = None def __init__(self): - self.id = '117' - self.name = 'iptables' - self.description = 'description' - self.type = 'type' + self.id = "117" + self.name = "iptables" + self.description = "description" + self.type = "type" @staticmethod def create(name, service_type, description): @@ -122,7 +122,7 @@ class MockServices(object): Mock of create method """ service = MockServices() - service.id = '005' + service.id = "005" service.name = name service.description = description service.type = service_type @@ -134,7 +134,7 @@ class MockServices(object): """ service = MockServices() if self.flag == 1: - service.id = 'asd' + service.id = "asd" return [service] elif self.flag == 2: service.id = service_id @@ -147,7 +147,7 @@ class MockServices(object): """ service = MockServices() if self.flag == 1: - service.id = 'asd' + service.id = "asd" return [service] return [service] @@ -163,13 +163,14 @@ class MockRoles(object): """ Mock of Roles class """ + flag = None def __init__(self): - self.id = '113' - self.name = 'nova' - self.user_id = '446' - self.tenant_id = 'a1a1' + self.id = "113" + self.name = "nova" + self.user_id = "446" + self.tenant_id = "a1a1" @staticmethod def create(name): @@ -232,13 +233,14 @@ class MockTenants(object): """ Mock of Tenants class """ + flag = None def __init__(self): - self.id = '446' - self.name = 'nova' - self.description = 'description' - self.enabled = 'True' + self.id = "446" + self.name = "nova" + self.description = "description" + self.enabled = "True" @staticmethod def create(name, description, enabled): @@ -281,33 +283,39 @@ class MockServiceCatalog(object): """ Mock of ServiceCatalog class """ + def __init__(self): - self.id = '446' - self.expires = 'No' - self.user_id = 'admin' - self.tenant_id = 'ae04' + self.id = "446" + self.expires = "No" + self.user_id = "admin" + self.tenant_id = "ae04" def get_token(self): """ Mock of get_token method """ - return {'id': self.id, 'expires': self.expires, 'user_id': self.user_id, - 'tenant_id': self.tenant_id} + return { + "id": self.id, + "expires": self.expires, + "user_id": self.user_id, + "tenant_id": self.tenant_id, + } class MockUsers(object): """ Mock of Users class """ + flag = None def __init__(self): - self.id = '446' - self.name = 'nova' - self.email = 'salt@saltstack.com' - self.enabled = 'True' - self.tenant_id = 'a1a1' - self.password = 'salt' + self.id = "446" + self.name = "nova" + self.email = "salt@saltstack.com" + self.enabled = "True" + self.tenant_id = "a1a1" + self.password = "salt" def create(self, name, password, email, tenant_id, enabled): """ @@ -365,16 +373,18 @@ class Unauthorized(Exception): """ The base exception class for all exceptions. """ - def __init__(self, message='Test'): + + def __init__(self, message="Test"): super(Unauthorized, self).__init__(message) self.msg = message class AuthorizationFailure(Exception): - ''' + """ Additional exception class to Unauthorized. - ''' - def __init__(self, message='Test'): + """ + + def __init__(self, message="Test"): super(AuthorizationFailure, self).__init__(message) self.msg = message @@ -383,6 +393,7 @@ class MockExceptions(object): """ Mock of exceptions class """ + def __init__(self): self.Unauthorized = Unauthorized self.AuthorizationFailure = AuthorizationFailure @@ -392,6 +403,7 @@ class MockKeystoneClient(object): """ Mock of keystoneclient module """ + def __init__(self): self.exceptions = MockExceptions() @@ -400,6 +412,7 @@ class MockClient(object): """ Mock of Client class """ + flag = None def __init__(self, profile=None, **conn_args): @@ -421,485 +434,622 @@ class MockClient(object): class KeystoneTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.keystone - ''' + """ def setup_loader_modules(self): return { keystone: { - 'auth': MockClient, - 'client': MockClient(), - 'keystoneclient': MockKeystoneClient() + "auth": MockClient, + "client": MockClient(), + "keystoneclient": MockKeystoneClient(), } } # 'ec2_credentials_create' function tests: 1 def test_ec2_credentials_create(self): - ''' + """ Test if it create EC2-compatible credentials for user per tenant - ''' - self.assertDictEqual(keystone.ec2_credentials_create(), - {'Error': 'Could not resolve User ID'}) + """ + self.assertDictEqual( + keystone.ec2_credentials_create(), {"Error": "Could not resolve User ID"} + ) - self.assertDictEqual(keystone.ec2_credentials_create(user_id='salt'), - {'Error': 'Could not resolve Tenant ID'}) + self.assertDictEqual( + keystone.ec2_credentials_create(user_id="salt"), + {"Error": "Could not resolve Tenant ID"}, + ) - self.assertDictEqual(keystone.ec2_credentials_create(user_id='salt', - tenant_id='72278'), - {'access': '', 'tenant_id': '72278', 'secret': '', - 'user_id': 'salt'}) + self.assertDictEqual( + keystone.ec2_credentials_create(user_id="salt", tenant_id="72278"), + {"access": "", "tenant_id": "72278", "secret": "", "user_id": "salt"}, + ) # 'ec2_credentials_delete' function tests: 1 def test_ec2_credentials_delete(self): - ''' + """ Test if it delete EC2-compatible credentials - ''' - self.assertDictEqual(keystone.ec2_credentials_delete(), - {'Error': 'Could not resolve User ID'}) + """ + self.assertDictEqual( + keystone.ec2_credentials_delete(), {"Error": "Could not resolve User ID"} + ) - self.assertEqual(keystone.ec2_credentials_delete(user_id='salt', - access_key='72278'), - 'ec2 key "72278" deleted under user id "salt"') + self.assertEqual( + keystone.ec2_credentials_delete(user_id="salt", access_key="72278"), + 'ec2 key "72278" deleted under user id "salt"', + ) # 'ec2_credentials_get' function tests: 1 def test_ec2_credentials_get(self): - ''' + """ Test if it return ec2_credentials for a user (keystone ec2-credentials-get) - ''' - self.assertDictEqual(keystone.ec2_credentials_get(), - {'Error': 'Unable to resolve user id'}) + """ + self.assertDictEqual( + keystone.ec2_credentials_get(), {"Error": "Unable to resolve user id"} + ) - self.assertDictEqual(keystone.ec2_credentials_get(user_id='salt'), - {'Error': 'Access key is required'}) + self.assertDictEqual( + keystone.ec2_credentials_get(user_id="salt"), + {"Error": "Access key is required"}, + ) - self.assertDictEqual(keystone.ec2_credentials_get(user_id='salt', - access='72278', - profile='openstack1'), - {'salt': {'access': '72278', 'secret': '', - 'tenant': '', 'user_id': 'salt'}}) + self.assertDictEqual( + keystone.ec2_credentials_get( + user_id="salt", access="72278", profile="openstack1" + ), + { + "salt": { + "access": "72278", + "secret": "", + "tenant": "", + "user_id": "salt", + } + }, + ) # 'ec2_credentials_list' function tests: 1 def test_ec2_credentials_list(self): - ''' + """ Test if it return a list of ec2_credentials for a specific user (keystone ec2-credentials-list) - ''' - self.assertDictEqual(keystone.ec2_credentials_list(), - {'Error': 'Unable to resolve user id'}) + """ + self.assertDictEqual( + keystone.ec2_credentials_list(), {"Error": "Unable to resolve user id"} + ) - self.assertDictEqual(keystone.ec2_credentials_list - (user_id='salt', profile='openstack1'), - {'salt': {'access': '', 'secret': '', - 'tenant_id': '', 'user_id': 'salt'}}) + self.assertDictEqual( + keystone.ec2_credentials_list(user_id="salt", profile="openstack1"), + {"salt": {"access": "", "secret": "", "tenant_id": "", "user_id": "salt"}}, + ) # 'endpoint_get' function tests: 1 def test_endpoint_get(self): - ''' + """ Test if it return a specific endpoint (keystone endpoint-get) - ''' - self.assertDictEqual(keystone.endpoint_get('nova', - 'RegionOne', - profile='openstack'), - {'Error': 'Could not find the specified service'}) + """ + self.assertDictEqual( + keystone.endpoint_get("nova", "RegionOne", profile="openstack"), + {"Error": "Could not find the specified service"}, + ) - ret = {'Error': 'Could not find endpoint for the specified service'} + ret = {"Error": "Could not find endpoint for the specified service"} MockServices.flag = 1 - self.assertDictEqual(keystone.endpoint_get('iptables', - 'RegionOne', - profile='openstack'), ret) + self.assertDictEqual( + keystone.endpoint_get("iptables", "RegionOne", profile="openstack"), ret + ) MockServices.flag = 0 - self.assertDictEqual(keystone.endpoint_get('iptables', - 'RegionOne', - profile='openstack'), - {'adminurl': 'adminurl', - 'id': '007', - 'internalurl': 'internalurl', - 'publicurl': 'publicurl', - 'region': 'RegionOne', - 'service_id': '117'}) + self.assertDictEqual( + keystone.endpoint_get("iptables", "RegionOne", profile="openstack"), + { + "adminurl": "adminurl", + "id": "007", + "internalurl": "internalurl", + "publicurl": "publicurl", + "region": "RegionOne", + "service_id": "117", + }, + ) # 'endpoint_list' function tests: 1 def test_endpoint_list(self): - ''' + """ Test if it return a list of available endpoints (keystone endpoints-list) - ''' - self.assertDictEqual(keystone.endpoint_list(profile='openstack1'), - {'007': {'adminurl': 'adminurl', - 'id': '007', - 'internalurl': 'internalurl', - 'publicurl': 'publicurl', - 'region': 'RegionOne', - 'service_id': '117'}}) + """ + self.assertDictEqual( + keystone.endpoint_list(profile="openstack1"), + { + "007": { + "adminurl": "adminurl", + "id": "007", + "internalurl": "internalurl", + "publicurl": "publicurl", + "region": "RegionOne", + "service_id": "117", + } + }, + ) # 'endpoint_create' function tests: 1 def test_endpoint_create(self): - ''' + """ Test if it create an endpoint for an Openstack service - ''' - self.assertDictEqual(keystone.endpoint_create('nova'), - {'Error': 'Could not find the specified service'}) + """ + self.assertDictEqual( + keystone.endpoint_create("nova"), + {"Error": "Could not find the specified service"}, + ) MockServices.flag = 2 - self.assertDictEqual(keystone.endpoint_create('iptables', - 'http://public/url', - 'http://internal/url', - 'http://adminurl/url', - 'RegionOne'), - {'adminurl': 'adminurl', - 'id': '007', - 'internalurl': 'internalurl', - 'publicurl': 'publicurl', - 'region': 'RegionOne', - 'service_id': '117'}) + self.assertDictEqual( + keystone.endpoint_create( + "iptables", + "http://public/url", + "http://internal/url", + "http://adminurl/url", + "RegionOne", + ), + { + "adminurl": "adminurl", + "id": "007", + "internalurl": "internalurl", + "publicurl": "publicurl", + "region": "RegionOne", + "service_id": "117", + }, + ) # 'endpoint_delete' function tests: 1 def test_endpoint_delete(self): - ''' + """ Test if it delete an endpoint for an Openstack service - ''' - ret = {'Error': 'Could not find any endpoints for the service'} - self.assertDictEqual(keystone.endpoint_delete('nova', 'RegionOne'), ret) + """ + ret = {"Error": "Could not find any endpoints for the service"} + self.assertDictEqual(keystone.endpoint_delete("nova", "RegionOne"), ret) - with patch.object(keystone, 'endpoint_get', - MagicMock(side_effect=[{'id': '117'}, None])): - self.assertTrue(keystone.endpoint_delete('iptables', 'RegionOne')) + with patch.object( + keystone, "endpoint_get", MagicMock(side_effect=[{"id": "117"}, None]) + ): + self.assertTrue(keystone.endpoint_delete("iptables", "RegionOne")) # 'role_create' function tests: 1 def test_role_create(self): - ''' + """ Test if it create named role - ''' - self.assertDictEqual(keystone.role_create('nova'), - {'Error': 'Role "nova" already exists'}) + """ + self.assertDictEqual( + keystone.role_create("nova"), {"Error": 'Role "nova" already exists'} + ) - self.assertDictEqual(keystone.role_create('iptables'), - {'Error': 'Unable to resolve role id'}) + self.assertDictEqual( + keystone.role_create("iptables"), {"Error": "Unable to resolve role id"} + ) # 'role_delete' function tests: 1 def test_role_delete(self): - ''' + """ Test if it delete a role (keystone role-delete) - ''' - self.assertDictEqual(keystone.role_delete(), - {'Error': 'Unable to resolve role id'}) + """ + self.assertDictEqual( + keystone.role_delete(), {"Error": "Unable to resolve role id"} + ) - self.assertEqual(keystone.role_delete('iptables'), - 'Role ID iptables deleted') + self.assertEqual(keystone.role_delete("iptables"), "Role ID iptables deleted") # 'role_get' function tests: 1 def test_role_get(self): - ''' + """ Test if it return a specific roles (keystone role-get) - ''' - self.assertDictEqual(keystone.role_get(), - {'Error': 'Unable to resolve role id'}) + """ + self.assertDictEqual( + keystone.role_get(), {"Error": "Unable to resolve role id"} + ) - self.assertDictEqual(keystone.role_get(name='nova'), - {'nova': {'id': '113', 'name': 'nova'}}) + self.assertDictEqual( + keystone.role_get(name="nova"), {"nova": {"id": "113", "name": "nova"}} + ) # 'role_list' function tests: 1 def test_role_list(self): - ''' + """ Test if it return a list of available roles (keystone role-list) - ''' - self.assertDictEqual(keystone.role_list(), - {'nova': {'id': '113', 'name': 'nova', 'tenant_id': - 'a1a1', 'user_id': '446'}}) + """ + self.assertDictEqual( + keystone.role_list(), + { + "nova": { + "id": "113", + "name": "nova", + "tenant_id": "a1a1", + "user_id": "446", + } + }, + ) # 'service_create' function tests: 1 def test_service_create(self): - ''' + """ Test if it add service to Keystone service catalog - ''' + """ MockServices.flag = 2 - self.assertDictEqual(keystone.service_create('nova', 'compute', - 'OpenStack Service'), - {'iptables': {'description': 'description', - 'id': '005', - 'name': 'iptables', - 'type': 'type'}}) + self.assertDictEqual( + keystone.service_create("nova", "compute", "OpenStack Service"), + { + "iptables": { + "description": "description", + "id": "005", + "name": "iptables", + "type": "type", + } + }, + ) # 'service_delete' function tests: 1 def test_service_delete(self): - ''' + """ Test if it delete a service from Keystone service catalog - ''' - self.assertEqual(keystone.service_delete('iptables'), - 'Keystone service ID "iptables" deleted') + """ + self.assertEqual( + keystone.service_delete("iptables"), + 'Keystone service ID "iptables" deleted', + ) # 'service_get' function tests: 1 def test_service_get(self): - ''' + """ Test if it return a list of available services (keystone services-list) - ''' + """ MockServices.flag = 0 - self.assertDictEqual(keystone.service_get(), - {'Error': 'Unable to resolve service id'}) + self.assertDictEqual( + keystone.service_get(), {"Error": "Unable to resolve service id"} + ) MockServices.flag = 2 - self.assertDictEqual(keystone.service_get(service_id='c965'), - {'iptables': {'description': 'description', - 'id': 'c965', - 'name': 'iptables', - 'type': 'type'}}) + self.assertDictEqual( + keystone.service_get(service_id="c965"), + { + "iptables": { + "description": "description", + "id": "c965", + "name": "iptables", + "type": "type", + } + }, + ) # 'service_list' function tests: 1 def test_service_list(self): - ''' + """ Test if it return a list of available services (keystone services-list) - ''' + """ MockServices.flag = 0 - self.assertDictEqual(keystone.service_list(profile='openstack1'), - {'iptables': {'description': 'description', - 'id': '117', 'name': 'iptables', - 'type': 'type'}}) + self.assertDictEqual( + keystone.service_list(profile="openstack1"), + { + "iptables": { + "description": "description", + "id": "117", + "name": "iptables", + "type": "type", + } + }, + ) # 'tenant_create' function tests: 1 def test_tenant_create(self): - ''' + """ Test if it create a keystone tenant - ''' - self.assertDictEqual(keystone.tenant_create('nova'), - {'nova': {'description': 'description', - 'id': '446', 'name': 'nova', - 'enabled': 'True'}}) + """ + self.assertDictEqual( + keystone.tenant_create("nova"), + { + "nova": { + "description": "description", + "id": "446", + "name": "nova", + "enabled": "True", + } + }, + ) # 'tenant_delete' function tests: 1 def test_tenant_delete(self): - ''' + """ Test if it delete a tenant (keystone tenant-delete) - ''' - self.assertDictEqual(keystone.tenant_delete(), - {'Error': 'Unable to resolve tenant id'}) + """ + self.assertDictEqual( + keystone.tenant_delete(), {"Error": "Unable to resolve tenant id"} + ) - self.assertEqual(keystone.tenant_delete('nova'), - 'Tenant ID nova deleted') + self.assertEqual(keystone.tenant_delete("nova"), "Tenant ID nova deleted") # 'tenant_get' function tests: 1 def test_tenant_get(self): - ''' + """ Test if it return a specific tenants (keystone tenant-get) - ''' - self.assertDictEqual(keystone.tenant_get(), - {'Error': 'Unable to resolve tenant id'}) + """ + self.assertDictEqual( + keystone.tenant_get(), {"Error": "Unable to resolve tenant id"} + ) - self.assertDictEqual(keystone.tenant_get(tenant_id='446'), - {'nova': {'description': 'description', - 'id': '446', 'name': 'nova', - 'enabled': 'True'}}) + self.assertDictEqual( + keystone.tenant_get(tenant_id="446"), + { + "nova": { + "description": "description", + "id": "446", + "name": "nova", + "enabled": "True", + } + }, + ) # 'tenant_list' function tests: 1 def test_tenant_list(self): - ''' + """ Test if it return a list of available tenants (keystone tenants-list) - ''' - self.assertDictEqual(keystone.tenant_list(), - {'nova': {'description': 'description', - 'id': '446', 'name': 'nova', - 'enabled': 'True'}}) + """ + self.assertDictEqual( + keystone.tenant_list(), + { + "nova": { + "description": "description", + "id": "446", + "name": "nova", + "enabled": "True", + } + }, + ) # 'tenant_update' function tests: 1 def test_tenant_update(self): - ''' + """ Test if it update a tenant's information (keystone tenant-update) - ''' - self.assertDictEqual(keystone.tenant_update(), - {'Error': 'Unable to resolve tenant id'}) + """ + self.assertDictEqual( + keystone.tenant_update(), {"Error": "Unable to resolve tenant id"} + ) # 'token_get' function tests: 1 def test_token_get(self): - ''' + """ Test if it return the configured tokens (keystone token-get) - ''' - self.assertDictEqual(keystone.token_get(), {'expires': 'No', - 'id': '446', - 'tenant_id': 'ae04', - 'user_id': 'admin'}) + """ + self.assertDictEqual( + keystone.token_get(), + {"expires": "No", "id": "446", "tenant_id": "ae04", "user_id": "admin"}, + ) # 'user_list' function tests: 1 def test_user_list(self): - ''' + """ Test if it return a list of available users (keystone user-list) - ''' - self.assertDictEqual(keystone.user_list(), - {'nova': {'name': 'nova', - 'tenant_id': 'a1a1', - 'enabled': 'True', - 'id': '446', - 'password': 'salt', - 'email': 'salt@saltstack.com'}}) + """ + self.assertDictEqual( + keystone.user_list(), + { + "nova": { + "name": "nova", + "tenant_id": "a1a1", + "enabled": "True", + "id": "446", + "password": "salt", + "email": "salt@saltstack.com", + } + }, + ) # 'user_get' function tests: 1 def test_user_get(self): - ''' + """ Test if it return a specific users (keystone user-get) - ''' - self.assertDictEqual(keystone.user_get(), - {'Error': 'Unable to resolve user id'}) + """ + self.assertDictEqual( + keystone.user_get(), {"Error": "Unable to resolve user id"} + ) + + self.assertDictEqual( + keystone.user_get(user_id="446"), + { + "nova": { + "name": "nova", + "tenant_id": "a1a1", + "enabled": "True", + "id": "446", + "password": "salt", + "email": "salt@saltstack.com", + } + }, + ) - self.assertDictEqual(keystone.user_get(user_id='446'), - {'nova': {'name': 'nova', 'tenant_id': 'a1a1', - 'enabled': 'True', 'id': '446', - 'password': 'salt', - 'email': 'salt@saltstack.com'}}) # 'user_create' function tests: 1 def test_user_create(self): - ''' + """ Test if it create a user (keystone user-create) - ''' - self.assertDictEqual(keystone.user_create(name='nova', password='salt', - email='salt@saltstack.com', - tenant_id='a1a1'), - {'nova': {'name': 'nova', 'tenant_id': 'a1a1', - 'enabled': 'True', 'id': '446', - 'password': 'salt', - 'email': 'salt@saltstack.com'}}) + """ + self.assertDictEqual( + keystone.user_create( + name="nova", + password="salt", + email="salt@saltstack.com", + tenant_id="a1a1", + ), + { + "nova": { + "name": "nova", + "tenant_id": "a1a1", + "enabled": "True", + "id": "446", + "password": "salt", + "email": "salt@saltstack.com", + } + }, + ) # 'user_delete' function tests: 1 def test_user_delete(self): - ''' + """ Test if it delete a user (keystone user-delete) - ''' - self.assertDictEqual(keystone.user_delete(), - {'Error': 'Unable to resolve user id'}) + """ + self.assertDictEqual( + keystone.user_delete(), {"Error": "Unable to resolve user id"} + ) - self.assertEqual(keystone.user_delete('nova'), - 'User ID nova deleted') + self.assertEqual(keystone.user_delete("nova"), "User ID nova deleted") # 'user_update' function tests: 1 def test_user_update(self): - ''' + """ Test if it update a user's information (keystone user-update) - ''' - self.assertDictEqual(keystone.user_update(), - {'Error': 'Unable to resolve user id'}) + """ + self.assertDictEqual( + keystone.user_update(), {"Error": "Unable to resolve user id"} + ) - self.assertEqual(keystone.user_update('nova'), - 'Info updated for user ID nova') + self.assertEqual(keystone.user_update("nova"), "Info updated for user ID nova") # 'user_verify_password' function tests: 1 def test_user_verify_password(self): - ''' + """ Test if it verify a user's password - ''' - mock = MagicMock(return_value='http://127.0.0.1:35357/v2.0') - with patch.dict(keystone.__salt__, {'config.option': mock}): - self.assertDictEqual(keystone.user_verify_password(), - {'Error': 'Unable to resolve user name'}) + """ + mock = MagicMock(return_value="http://127.0.0.1:35357/v2.0") + with patch.dict(keystone.__salt__, {"config.option": mock}): + self.assertDictEqual( + keystone.user_verify_password(), + {"Error": "Unable to resolve user name"}, + ) - self.assertTrue(keystone.user_verify_password(user_id='446', - name='nova')) + self.assertTrue(keystone.user_verify_password(user_id="446", name="nova")) MockClient.flag = 1 - self.assertFalse(keystone.user_verify_password(user_id='446', - name='nova')) + self.assertFalse(keystone.user_verify_password(user_id="446", name="nova")) # 'user_password_update' function tests: 1 def test_user_password_update(self): - ''' + """ Test if it update a user's password (keystone user-password-update) - ''' - self.assertDictEqual(keystone.user_password_update(), - {'Error': 'Unable to resolve user id'}) + """ + self.assertDictEqual( + keystone.user_password_update(), {"Error": "Unable to resolve user id"} + ) - self.assertEqual(keystone.user_password_update('nova'), - 'Password updated for user ID nova') + self.assertEqual( + keystone.user_password_update("nova"), "Password updated for user ID nova" + ) # 'user_role_add' function tests: 1 def test_user_role_add(self): - ''' + """ Test if it add role for user in tenant (keystone user-role-add) - ''' - self.assertEqual(keystone.user_role_add(user='nova', tenant='nova', - role='nova'), - '"nova" role added for user "nova" for "nova" tenant/project') + """ + self.assertEqual( + keystone.user_role_add(user="nova", tenant="nova", role="nova"), + '"nova" role added for user "nova" for "nova" tenant/project', + ) MockRoles.flag = 1 - self.assertDictEqual(keystone.user_role_add(user='nova', tenant='nova', - role='nova'), - {'Error': 'Unable to resolve role id'}) + self.assertDictEqual( + keystone.user_role_add(user="nova", tenant="nova", role="nova"), + {"Error": "Unable to resolve role id"}, + ) MockTenants.flag = 1 - self.assertDictEqual(keystone.user_role_add(user='nova', tenant='nova'), - {'Error': 'Unable to resolve tenant/project id'}) + self.assertDictEqual( + keystone.user_role_add(user="nova", tenant="nova"), + {"Error": "Unable to resolve tenant/project id"}, + ) MockUsers.flag = 1 - self.assertDictEqual(keystone.user_role_add(user='nova'), - {'Error': 'Unable to resolve user id'}) + self.assertDictEqual( + keystone.user_role_add(user="nova"), {"Error": "Unable to resolve user id"} + ) # 'user_role_remove' function tests: 1 def test_user_role_remove(self): - ''' + """ Test if it add role for user in tenant (keystone user-role-add) - ''' + """ MockUsers.flag = 1 - self.assertDictEqual(keystone.user_role_remove(user='nova'), - {'Error': 'Unable to resolve user id'}) + self.assertDictEqual( + keystone.user_role_remove(user="nova"), + {"Error": "Unable to resolve user id"}, + ) MockUsers.flag = 0 MockTenants.flag = 1 - self.assertDictEqual(keystone.user_role_remove(user='nova', - tenant='nova'), - {'Error': 'Unable to resolve tenant/project id'}) + self.assertDictEqual( + keystone.user_role_remove(user="nova", tenant="nova"), + {"Error": "Unable to resolve tenant/project id"}, + ) MockTenants.flag = 0 MockRoles.flag = 1 - self.assertDictEqual(keystone.user_role_remove(user='nova', - tenant='nova', - role='nova'), - {'Error': 'Unable to resolve role id'}) + self.assertDictEqual( + keystone.user_role_remove(user="nova", tenant="nova", role="nova"), + {"Error": "Unable to resolve role id"}, + ) ret = '"nova" role removed for user "nova" under "nova" tenant' MockRoles.flag = 0 - self.assertEqual(keystone.user_role_remove(user='nova', tenant='nova', - role='nova'), ret) + self.assertEqual( + keystone.user_role_remove(user="nova", tenant="nova", role="nova"), ret + ) # 'user_role_list' function tests: 1 def test_user_role_list(self): - ''' + """ Test if it return a list of available user_roles (keystone user-roles-list) - ''' - self.assertDictEqual(keystone.user_role_list(user='nova'), - {'Error': 'Unable to resolve user or tenant/project id'}) + """ + self.assertDictEqual( + keystone.user_role_list(user="nova"), + {"Error": "Unable to resolve user or tenant/project id"}, + ) - self.assertDictEqual(keystone.user_role_list(user_name='nova', - tenant_name='nova'), - {'nova': {'id': '113', 'name': 'nova', - 'tenant_id': '446', 'user_id': '446'}}) + self.assertDictEqual( + keystone.user_role_list(user_name="nova", tenant_name="nova"), + { + "nova": { + "id": "113", + "name": "nova", + "tenant_id": "446", + "user_id": "446", + } + }, + ) diff --git a/tests/unit/modules/test_kmod.py b/tests/unit/modules/test_kmod.py index 4bb799539b4..091627fd256 100644 --- a/tests/unit/modules/test_kmod.py +++ b/tests/unit/modules/test_kmod.py @@ -1,25 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch +import os # Import Salt Libs import salt.modules.kmod as kmod +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + class KmodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.kmod - ''' + """ def setup_loader_modules(self): return {kmod: {}} @@ -27,109 +28,112 @@ class KmodTestCase(TestCase, LoaderModuleMockMixin): # 'available' function tests: 1 def test_available(self): - ''' + """ Tests return a list of all available kernel modules - ''' - with patch('salt.modules.kmod.available', MagicMock(return_value=['kvm'])): - self.assertEqual(['kvm'], kmod.available()) + """ + with patch("salt.modules.kmod.available", MagicMock(return_value=["kvm"])): + self.assertEqual(["kvm"], kmod.available()) # 'check_available' function tests: 1 def test_check_available(self): - ''' + """ Tests if the specified kernel module is available - ''' - with patch('salt.modules.kmod.available', MagicMock(return_value=['kvm'])): - self.assertTrue(kmod.check_available('kvm')) + """ + with patch("salt.modules.kmod.available", MagicMock(return_value=["kvm"])): + self.assertTrue(kmod.check_available("kvm")) # 'lsmod' function tests: 1 def test_lsmod(self): - ''' + """ Tests return information about currently loaded modules - ''' - mock_ret = [{'size': 100, 'module': None, 'depcount': 10, 'deps': None}] - with patch('salt.modules.kmod.lsmod', MagicMock(return_value=mock_ret)): + """ + mock_ret = [{"size": 100, "module": None, "depcount": 10, "deps": None}] + with patch("salt.modules.kmod.lsmod", MagicMock(return_value=mock_ret)): mock_cmd = MagicMock(return_value=1) - with patch.dict(kmod.__salt__, {'cmd.run': mock_cmd}): + with patch.dict(kmod.__salt__, {"cmd.run": mock_cmd}): self.assertListEqual(mock_ret, kmod.lsmod()) # 'mod_list' function tests: 1 - @skipIf(not os.path.isfile('/etc/modules'), '/etc/modules not present') + @skipIf(not os.path.isfile("/etc/modules"), "/etc/modules not present") def test_mod_list(self): - ''' + """ Tests return a list of the loaded module names - ''' - with patch('salt.modules.kmod._get_modules_conf', - MagicMock(return_value='/etc/modules')): - with patch('salt.modules.kmod._strip_module_name', - MagicMock(return_value='lp')): - self.assertListEqual(['lp'], kmod.mod_list(True)) + """ + with patch( + "salt.modules.kmod._get_modules_conf", + MagicMock(return_value="/etc/modules"), + ): + with patch( + "salt.modules.kmod._strip_module_name", MagicMock(return_value="lp") + ): + self.assertListEqual(["lp"], kmod.mod_list(True)) - mock_ret = [{'size': 100, 'module': None, 'depcount': 10, 'deps': None}] - with patch('salt.modules.kmod.lsmod', MagicMock(return_value=mock_ret)): + mock_ret = [{"size": 100, "module": None, "depcount": 10, "deps": None}] + with patch("salt.modules.kmod.lsmod", MagicMock(return_value=mock_ret)): self.assertListEqual([None], kmod.mod_list(False)) # 'load' function tests: 1 def test_load(self): - ''' + """ Tests to loads specified kernel module. - ''' - mod = 'cheese' - err_msg = 'Module too moldy, refusing to load' + """ + mod = "cheese" + err_msg = "Module too moldy, refusing to load" mock_persist = MagicMock(return_value=set([mod])) - mock_lsmod = MagicMock(return_value=[{'size': 100, - 'module': None, - 'depcount': 10, - 'deps': None}]) - mock_run_all_0 = MagicMock(return_value={'retcode': 0}) - mock_run_all_1 = MagicMock(return_value={'retcode': 1, - 'stderr': err_msg}) + mock_lsmod = MagicMock( + return_value=[{"size": 100, "module": None, "depcount": 10, "deps": None}] + ) + mock_run_all_0 = MagicMock(return_value={"retcode": 0}) + mock_run_all_1 = MagicMock(return_value={"retcode": 1, "stderr": err_msg}) - with patch('salt.modules.kmod._set_persistent_module', mock_persist): - with patch('salt.modules.kmod.lsmod', mock_lsmod): - with patch.dict(kmod.__salt__, {'cmd.run_all': mock_run_all_0}): + with patch("salt.modules.kmod._set_persistent_module", mock_persist): + with patch("salt.modules.kmod.lsmod", mock_lsmod): + with patch.dict(kmod.__salt__, {"cmd.run_all": mock_run_all_0}): self.assertEqual([mod], kmod.load(mod, True)) - with patch.dict(kmod.__salt__, {'cmd.run_all': mock_run_all_1}): - self.assertEqual('Error loading module {0}: {1}'.format(mod, err_msg), - kmod.load(mod)) + with patch.dict(kmod.__salt__, {"cmd.run_all": mock_run_all_1}): + self.assertEqual( + "Error loading module {0}: {1}".format(mod, err_msg), + kmod.load(mod), + ) # 'is_loaded' function tests: 1 def test_is_loaded(self): - ''' + """ Tests if specified kernel module is loaded. - ''' - with patch('salt.modules.kmod.mod_list', MagicMock(return_value=set(['lp']))): - self.assertTrue(kmod.is_loaded('lp')) + """ + with patch("salt.modules.kmod.mod_list", MagicMock(return_value=set(["lp"]))): + self.assertTrue(kmod.is_loaded("lp")) # 'remove' function tests: 1 def test_remove(self): - ''' + """ Tests to remove the specified kernel module - ''' - mod = 'cheese' - err_msg = 'Cannot find module: it has been eaten' + """ + mod = "cheese" + err_msg = "Cannot find module: it has been eaten" mock_persist = MagicMock(return_value=set([mod])) - mock_lsmod = MagicMock(return_value=[{'size': 100, - 'module': None, - 'depcount': 10, - 'deps': None}]) - mock_run_all_0 = MagicMock(return_value={'retcode': 0}) - mock_run_all_1 = MagicMock(return_value={'retcode': 1, - 'stderr': err_msg}) + mock_lsmod = MagicMock( + return_value=[{"size": 100, "module": None, "depcount": 10, "deps": None}] + ) + mock_run_all_0 = MagicMock(return_value={"retcode": 0}) + mock_run_all_1 = MagicMock(return_value={"retcode": 1, "stderr": err_msg}) - with patch('salt.modules.kmod._remove_persistent_module', mock_persist): - with patch('salt.modules.kmod.lsmod', mock_lsmod): - with patch.dict(kmod.__salt__, {'cmd.run_all': mock_run_all_0}): + with patch("salt.modules.kmod._remove_persistent_module", mock_persist): + with patch("salt.modules.kmod.lsmod", mock_lsmod): + with patch.dict(kmod.__salt__, {"cmd.run_all": mock_run_all_0}): self.assertEqual([mod], kmod.remove(mod, True)) self.assertEqual([], kmod.remove(mod)) - with patch.dict(kmod.__salt__, {'cmd.run_all': mock_run_all_1}): - self.assertEqual('Error removing module {0}: {1}'.format(mod, err_msg), - kmod.remove(mod, True)) + with patch.dict(kmod.__salt__, {"cmd.run_all": mock_run_all_1}): + self.assertEqual( + "Error removing module {0}: {1}".format(mod, err_msg), + kmod.remove(mod, True), + ) diff --git a/tests/unit/modules/test_kubernetesmod.py b/tests/unit/modules/test_kubernetesmod.py index f3dcbfcd069..a4b479a92a3 100644 --- a/tests/unit/modules/test_kubernetesmod.py +++ b/tests/unit/modules/test_kubernetesmod.py @@ -1,27 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jochen Breuer <jbreuer@suse.de> -''' +""" # pylint: disable=no-value-for-parameter # Import Python Libs from __future__ import absolute_import + import os - from contextlib import contextmanager -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - Mock, - patch, -) - import salt.utils.files import salt.utils.platform from salt.modules import kubernetesmod as kubernetes +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock, patch +from tests.support.unit import TestCase, skipIf + @contextmanager def mock_kubernetes_library(): @@ -30,224 +27,284 @@ def mock_kubernetes_library(): it caused kubernetes._cleanup() to get called for virtually every test, which blows up. This prevents that specific blow-up once """ - with patch('salt.modules.kubernetesmod.kubernetes') as mock_kubernetes_lib: + with patch("salt.modules.kubernetesmod.kubernetes") as mock_kubernetes_lib: yield mock_kubernetes_lib -@skipIf(not kubernetes.HAS_LIBS, "Kubernetes client lib is not installed. " - "Skipping test_kubernetes.py") +@skipIf( + not kubernetes.HAS_LIBS, + "Kubernetes client lib is not installed. " "Skipping test_kubernetes.py", +) class KubernetesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.kubernetesmod - ''' + """ def setup_loader_modules(self): - return { - kubernetes: { - '__salt__': {}, - } - } + return {kubernetes: {"__salt__": {}}} def test_nodes(self): - ''' + """ Test node listing. :return: - ''' + """ with mock_kubernetes_library() as mock_kubernetes_lib: - with patch.dict(kubernetes.__salt__, {'config.option': Mock(side_effect=self.settings)}): + with patch.dict( + kubernetes.__salt__, {"config.option": Mock(side_effect=self.settings)} + ): mock_kubernetes_lib.client.CoreV1Api.return_value = Mock( - **{"list_node.return_value.to_dict.return_value": - {'items': [{'metadata': {'name': 'mock_node_name'}}]}} + **{ + "list_node.return_value.to_dict.return_value": { + "items": [{"metadata": {"name": "mock_node_name"}}] + } + } + ) + self.assertEqual(kubernetes.nodes(), ["mock_node_name"]) + self.assertTrue( + kubernetes.kubernetes.client.CoreV1Api().list_node().to_dict.called ) - self.assertEqual(kubernetes.nodes(), ['mock_node_name']) - self.assertTrue(kubernetes.kubernetes.client.CoreV1Api().list_node().to_dict.called) def test_deployments(self): - ''' + """ Tests deployment listing. :return: - ''' + """ with mock_kubernetes_library() as mock_kubernetes_lib: - with patch.dict(kubernetes.__salt__, {'config.option': Mock(side_effect=self.settings)}): + with patch.dict( + kubernetes.__salt__, {"config.option": Mock(side_effect=self.settings)} + ): mock_kubernetes_lib.client.ExtensionsV1beta1Api.return_value = Mock( - **{"list_namespaced_deployment.return_value.to_dict.return_value": - {'items': [{'metadata': {'name': 'mock_deployment_name'}}]}} + **{ + "list_namespaced_deployment.return_value.to_dict.return_value": { + "items": [{"metadata": {"name": "mock_deployment_name"}}] + } + } ) - self.assertEqual(kubernetes.deployments(), ['mock_deployment_name']) + self.assertEqual(kubernetes.deployments(), ["mock_deployment_name"]) # pylint: disable=E1120 self.assertTrue( - kubernetes.kubernetes.client.ExtensionsV1beta1Api().list_namespaced_deployment().to_dict.called) + kubernetes.kubernetes.client.ExtensionsV1beta1Api() + .list_namespaced_deployment() + .to_dict.called + ) # pylint: enable=E1120 def test_services(self): - ''' + """ Tests services listing. :return: - ''' + """ with mock_kubernetes_library() as mock_kubernetes_lib: - with patch.dict(kubernetes.__salt__, {'config.option': Mock(side_effect=self.settings)}): + with patch.dict( + kubernetes.__salt__, {"config.option": Mock(side_effect=self.settings)} + ): mock_kubernetes_lib.client.CoreV1Api.return_value = Mock( - **{"list_namespaced_service.return_value.to_dict.return_value": - {'items': [{'metadata': {'name': 'mock_service_name'}}]}} + **{ + "list_namespaced_service.return_value.to_dict.return_value": { + "items": [{"metadata": {"name": "mock_service_name"}}] + } + } ) - self.assertEqual(kubernetes.services(), ['mock_service_name']) + self.assertEqual(kubernetes.services(), ["mock_service_name"]) # pylint: disable=E1120 - self.assertTrue(kubernetes.kubernetes.client.CoreV1Api().list_namespaced_service().to_dict.called) + self.assertTrue( + kubernetes.kubernetes.client.CoreV1Api() + .list_namespaced_service() + .to_dict.called + ) # pylint: enable=E1120 def test_pods(self): - ''' + """ Tests pods listing. :return: - ''' + """ with mock_kubernetes_library() as mock_kubernetes_lib: - with patch.dict(kubernetes.__salt__, {'config.option': Mock(side_effect=self.settings)}): + with patch.dict( + kubernetes.__salt__, {"config.option": Mock(side_effect=self.settings)} + ): mock_kubernetes_lib.client.CoreV1Api.return_value = Mock( - **{"list_namespaced_pod.return_value.to_dict.return_value": - {'items': [{'metadata': {'name': 'mock_pod_name'}}]}} + **{ + "list_namespaced_pod.return_value.to_dict.return_value": { + "items": [{"metadata": {"name": "mock_pod_name"}}] + } + } ) - self.assertEqual(kubernetes.pods(), ['mock_pod_name']) + self.assertEqual(kubernetes.pods(), ["mock_pod_name"]) # pylint: disable=E1120 - self.assertTrue(kubernetes.kubernetes.client.CoreV1Api(). - list_namespaced_pod().to_dict.called) + self.assertTrue( + kubernetes.kubernetes.client.CoreV1Api() + .list_namespaced_pod() + .to_dict.called + ) # pylint: enable=E1120 def test_delete_deployments(self): - ''' + """ Tests deployment deletion :return: - ''' + """ with mock_kubernetes_library() as mock_kubernetes_lib: - with patch('salt.modules.kubernetesmod.show_deployment', Mock(return_value=None)): - with patch.dict(kubernetes.__salt__, {'config.option': Mock(side_effect=self.settings)}): + with patch( + "salt.modules.kubernetesmod.show_deployment", Mock(return_value=None) + ): + with patch.dict( + kubernetes.__salt__, + {"config.option": Mock(side_effect=self.settings)}, + ): mock_kubernetes_lib.client.V1DeleteOptions = Mock(return_value="") mock_kubernetes_lib.client.ExtensionsV1beta1Api.return_value = Mock( - **{"delete_namespaced_deployment.return_value.to_dict.return_value": {'code': ''}} + **{ + "delete_namespaced_deployment.return_value.to_dict.return_value": { + "code": "" + } + } + ) + self.assertEqual( + kubernetes.delete_deployment("test"), {"code": 200} ) - self.assertEqual(kubernetes.delete_deployment("test"), {'code': 200}) # pylint: disable=E1120 self.assertTrue( - kubernetes.kubernetes.client.ExtensionsV1beta1Api(). - delete_namespaced_deployment().to_dict.called) + kubernetes.kubernetes.client.ExtensionsV1beta1Api() + .delete_namespaced_deployment() + .to_dict.called + ) # pylint: enable=E1120 def test_create_deployments(self): - ''' + """ Tests deployment creation. :return: - ''' + """ with mock_kubernetes_library() as mock_kubernetes_lib: - with patch.dict(kubernetes.__salt__, {'config.option': Mock(side_effect=self.settings)}): + with patch.dict( + kubernetes.__salt__, {"config.option": Mock(side_effect=self.settings)} + ): mock_kubernetes_lib.client.ExtensionsV1beta1Api.return_value = Mock( - **{"create_namespaced_deployment.return_value.to_dict.return_value": {}} + **{ + "create_namespaced_deployment.return_value.to_dict.return_value": {} + } + ) + self.assertEqual( + kubernetes.create_deployment( + "test", "default", {}, {}, None, None, None + ), + {}, ) - self.assertEqual(kubernetes.create_deployment("test", "default", {}, {}, - None, None, None), {}) # pylint: disable=E1120 self.assertTrue( - kubernetes.kubernetes.client.ExtensionsV1beta1Api(). - create_namespaced_deployment().to_dict.called) + kubernetes.kubernetes.client.ExtensionsV1beta1Api() + .create_namespaced_deployment() + .to_dict.called + ) # pylint: enable=E1120 @staticmethod def settings(name, value=None): - ''' + """ Test helper :return: settings or default - ''' + """ data = { - 'kubernetes.kubeconfig': '/home/testuser/.minikube/kubeconfig.cfg', - 'kubernetes.context': 'minikube' + "kubernetes.kubeconfig": "/home/testuser/.minikube/kubeconfig.cfg", + "kubernetes.context": "minikube", } return data.get(name, value) def test_setup_kubeconfig_file(self): - ''' + """ Test that the `kubernetes.kubeconfig` configuration isn't overwritten :return: - ''' + """ with mock_kubernetes_library() as mock_kubernetes_lib: - with patch.dict(kubernetes.__salt__, {'config.option': Mock(side_effect=self.settings)}): + with patch.dict( + kubernetes.__salt__, {"config.option": Mock(side_effect=self.settings)} + ): mock_kubernetes_lib.config.load_kube_config = Mock() config = kubernetes._setup_conn() self.assertEqual( - self.settings('kubernetes.kubeconfig'), - config['kubeconfig'], + self.settings("kubernetes.kubeconfig"), config["kubeconfig"], ) def test_setup_kubeconfig_data_overwrite(self): - ''' + """ Test that provided `kubernetes.kubeconfig` configuration is overwritten by provided kubeconfig_data in the command :return: - ''' + """ with mock_kubernetes_library() as mock_kubernetes_lib: - with patch.dict(kubernetes.__salt__, {'config.option': Mock(side_effect=self.settings)}): + with patch.dict( + kubernetes.__salt__, {"config.option": Mock(side_effect=self.settings)} + ): mock_kubernetes_lib.config.load_kube_config = Mock() - config = kubernetes._setup_conn(kubeconfig_data='MTIzNDU2Nzg5MAo=', context='newcontext') - check_path = os.path.join('/tmp', 'salt-kubeconfig-') + config = kubernetes._setup_conn( + kubeconfig_data="MTIzNDU2Nzg5MAo=", context="newcontext" + ) + check_path = os.path.join("/tmp", "salt-kubeconfig-") if salt.utils.platform.is_windows(): - check_path = os.path.join(os.environ.get('TMP'), 'salt-kubeconfig-') + check_path = os.path.join(os.environ.get("TMP"), "salt-kubeconfig-") elif salt.utils.platform.is_darwin(): - check_path = os.path.join(os.environ.get('TMPDIR', '/tmp'), 'salt-kubeconfig-') - self.assertTrue(config['kubeconfig'].lower().startswith(check_path.lower())) - self.assertTrue(os.path.exists(config['kubeconfig'])) - with salt.utils.files.fopen(config['kubeconfig'], 'r') as kcfg: - self.assertEqual('1234567890\n', kcfg.read()) + check_path = os.path.join( + os.environ.get("TMPDIR", "/tmp"), "salt-kubeconfig-" + ) + self.assertTrue( + config["kubeconfig"].lower().startswith(check_path.lower()) + ) + self.assertTrue(os.path.exists(config["kubeconfig"])) + with salt.utils.files.fopen(config["kubeconfig"], "r") as kcfg: + self.assertEqual("1234567890\n", kcfg.read()) kubernetes._cleanup(**config) def test_node_labels(self): - ''' + """ Test kubernetes.node_labels :return: - ''' - with patch('salt.modules.kubernetesmod.node') as mock_node: + """ + with patch("salt.modules.kubernetesmod.node") as mock_node: mock_node.return_value = { - 'metadata': { - 'labels': { - 'kubernetes.io/hostname': 'minikube', - 'kubernetes.io/os': 'linux', + "metadata": { + "labels": { + "kubernetes.io/hostname": "minikube", + "kubernetes.io/os": "linux", } } } self.assertEqual( - kubernetes.node_labels('minikube'), - {'kubernetes.io/hostname': 'minikube', 'kubernetes.io/os': 'linux'}, + kubernetes.node_labels("minikube"), + {"kubernetes.io/hostname": "minikube", "kubernetes.io/os": "linux"}, ) def test_adding_change_cause_annotation(self): - ''' + """ Tests adding a `kubernetes.io/change-cause` annotation just like kubectl [apply|create|replace] --record does :return: - ''' - with patch('salt.modules.kubernetesmod.sys.argv', ['/usr/bin/salt-call', 'state.apply']) as mock_sys: - func = getattr(kubernetes, '__dict_to_object_meta') - data = func(name='test-pod', namespace='test', metadata={}) + """ + with patch( + "salt.modules.kubernetesmod.sys.argv", ["/usr/bin/salt-call", "state.apply"] + ) as mock_sys: + func = getattr(kubernetes, "__dict_to_object_meta") + data = func(name="test-pod", namespace="test", metadata={}) - self.assertEqual(data.name, 'test-pod') - self.assertEqual(data.namespace, 'test') + self.assertEqual(data.name, "test-pod") + self.assertEqual(data.namespace, "test") self.assertEqual( data.annotations, - {'kubernetes.io/change-cause': '/usr/bin/salt-call state.apply'} + {"kubernetes.io/change-cause": "/usr/bin/salt-call state.apply"}, ) # Ensure any specified annotations aren't overwritten - test_metadata = {'annotations': {'kubernetes.io/change-cause': 'NOPE'}} - data = func(name='test-pod', namespace='test', metadata=test_metadata) + test_metadata = {"annotations": {"kubernetes.io/change-cause": "NOPE"}} + data = func(name="test-pod", namespace="test", metadata=test_metadata) - self.assertEqual( - data.annotations, - {'kubernetes.io/change-cause': 'NOPE'} - ) + self.assertEqual(data.annotations, {"kubernetes.io/change-cause": "NOPE"}) def test_enforce_only_strings_dict(self): - func = getattr(kubernetes, '__enforce_only_strings_dict') + func = getattr(kubernetes, "__enforce_only_strings_dict") data = { - 'unicode': 1, + "unicode": 1, 2: 2, } self.assertEqual( - {'unicode': '1', '2': '2'}, - func(data), + {"unicode": "1", "2": "2"}, func(data), ) diff --git a/tests/unit/modules/test_launchctl_service.py b/tests/unit/modules/test_launchctl_service.py index 306f48b29be..b57455e51dd 100644 --- a/tests/unit/modules/test_launchctl_service.py +++ b/tests/unit/modules/test_launchctl_service.py @@ -1,64 +1,62 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.launchctl_service as launchctl +import salt.utils.stringutils # Import Salt Libs from salt.ext import six -import salt.utils.stringutils -import salt.modules.launchctl_service as launchctl + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class LaunchctlTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.launchctl - ''' + """ + def setup_loader_modules(self): return {launchctl: {}} def test_get_all(self): - ''' + """ Test for Return all installed services - ''' - with patch.dict(launchctl.__salt__, - {'cmd.run': MagicMock(return_value='A\tB\tC\t\n')}): - with patch.object(launchctl, - '_available_services', - return_value={'A': 'a', 'B': 'b'}): - self.assertEqual(launchctl.get_all(), ['A', 'B', 'C']) + """ + with patch.dict( + launchctl.__salt__, {"cmd.run": MagicMock(return_value="A\tB\tC\t\n")} + ): + with patch.object( + launchctl, "_available_services", return_value={"A": "a", "B": "b"} + ): + self.assertEqual(launchctl.get_all(), ["A", "B", "C"]) def test_available(self): - ''' + """ Test for Check that the given service is available. - ''' - with patch.object(launchctl, - '_service_by_name', return_value=True): - self.assertTrue(launchctl.available('job_label')) + """ + with patch.object(launchctl, "_service_by_name", return_value=True): + self.assertTrue(launchctl.available("job_label")) def test_missing(self): - ''' + """ Test for The inverse of service.available - ''' - with patch.object(launchctl, - '_service_by_name', return_value=True): - self.assertFalse(launchctl.missing('job_label')) + """ + with patch.object(launchctl, "_service_by_name", return_value=True): + self.assertFalse(launchctl.missing("job_label")) def test_status(self): - ''' + """ Test for Return the status for a service - ''' - launchctl_data = '''<?xml version="1.0" encoding="UTF-8"?> + """ + launchctl_data = """<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> @@ -79,53 +77,51 @@ class LaunchctlTestCase(TestCase, LoaderModuleMockMixin): <key>TimeOut</key> <integer>30</integer> </dict> -</plist>''' - with patch.object(launchctl, - '_service_by_name', - return_value={'plist': - {'Label': 'A'}}): +</plist>""" + with patch.object( + launchctl, "_service_by_name", return_value={"plist": {"Label": "A"}} + ): if six.PY3: launchctl_data = salt.utils.stringutils.to_bytes(launchctl_data) - with patch.object(launchctl, '_get_launchctl_data', - return_value=launchctl_data): - self.assertTrue(launchctl.status('job_label')) + with patch.object( + launchctl, "_get_launchctl_data", return_value=launchctl_data + ): + self.assertTrue(launchctl.status("job_label")) def test_stop(self): - ''' + """ Test for Stop the specified service - ''' - with patch.object(launchctl, - '_service_by_name', - return_value={'file_path': 'A'}): - with patch.dict(launchctl.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(launchctl.stop('job_label')) + """ + with patch.object( + launchctl, "_service_by_name", return_value={"file_path": "A"} + ): + with patch.dict( + launchctl.__salt__, {"cmd.retcode": MagicMock(return_value=False)} + ): + self.assertTrue(launchctl.stop("job_label")) - with patch.object(launchctl, - '_service_by_name', - return_value=None): - self.assertFalse(launchctl.stop('job_label')) + with patch.object(launchctl, "_service_by_name", return_value=None): + self.assertFalse(launchctl.stop("job_label")) def test_start(self): - ''' + """ Test for Start the specified service - ''' - with patch.object(launchctl, - '_service_by_name', - return_value={'file_path': 'A'}): - with patch.dict(launchctl.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(launchctl.start('job_label')) + """ + with patch.object( + launchctl, "_service_by_name", return_value={"file_path": "A"} + ): + with patch.dict( + launchctl.__salt__, {"cmd.retcode": MagicMock(return_value=False)} + ): + self.assertTrue(launchctl.start("job_label")) - with patch.object(launchctl, - '_service_by_name', - return_value=None): - self.assertFalse(launchctl.start('job_label')) + with patch.object(launchctl, "_service_by_name", return_value=None): + self.assertFalse(launchctl.start("job_label")) def test_restart(self): - ''' + """ Test for Restart the named service - ''' - with patch.object(launchctl, 'stop', return_value=None): - with patch.object(launchctl, 'start', return_value=True): - self.assertTrue(launchctl.restart('job_label')) + """ + with patch.object(launchctl, "stop", return_value=None): + with patch.object(launchctl, "start", return_value=True): + self.assertTrue(launchctl.restart("job_label")) diff --git a/tests/unit/modules/test_ldapmod.py b/tests/unit/modules/test_ldapmod.py index e9c83dca93d..a8b4f2cd34c 100644 --- a/tests/unit/modules/test_ldapmod.py +++ b/tests/unit/modules/test_ldapmod.py @@ -1,41 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import time # Import Salt Libs import salt.modules.ldapmod as ldapmod +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LdapmodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.ldapmod - ''' + """ + def setup_loader_modules(self): return {ldapmod: {}} # 'search' function tests: 1 def test_search(self): - ''' + """ Test if it run an arbitrary LDAP query and return the results. - ''' + """ + class MockConnect(object): - ''' + """ Mocking _connect method - ''' + """ + def __init__(self): self.bdn = None self.scope = None @@ -43,21 +44,26 @@ class LdapmodTestCase(TestCase, LoaderModuleMockMixin): self.attrs = None def search_s(self, bdn, scope, _filter, attrs): - ''' + """ Mock function for search_s - ''' + """ self.bdn = bdn self.scope = scope self._filter = _filter self.attrs = attrs - return 'SALT' + return "SALT" mock = MagicMock(return_value=True) - with patch.dict(ldapmod.__salt__, {'config.option': mock}): - with patch.object(ldapmod, '_connect', - MagicMock(return_value=MockConnect())): - with patch.object(time, 'time', MagicMock(return_value=8e-04)): - self.assertDictEqual(ldapmod.search(filter='myhost'), - {'count': 4, 'results': 'SALT', - 'time': {'raw': '0.0', - 'human': '0.0ms'}}) + with patch.dict(ldapmod.__salt__, {"config.option": mock}): + with patch.object( + ldapmod, "_connect", MagicMock(return_value=MockConnect()) + ): + with patch.object(time, "time", MagicMock(return_value=8e-04)): + self.assertDictEqual( + ldapmod.search(filter="myhost"), + { + "count": 4, + "results": "SALT", + "time": {"raw": "0.0", "human": "0.0ms"}, + }, + ) diff --git a/tests/unit/modules/test_libcloud_compute.py b/tests/unit/modules/test_libcloud_compute.py index ee9e0a53523..90b5c9ed593 100644 --- a/tests/unit/modules/test_libcloud_compute.py +++ b/tests/unit/modules/test_libcloud_compute.py @@ -1,90 +1,97 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.modules.libcloud_compute as libcloud_compute from salt.utils.versions import LooseVersion as _LooseVersion # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - MagicMock, -) -import salt.modules.libcloud_compute as libcloud_compute -REQUIRED_LIBCLOUD_VERSION = '2.0.0' +REQUIRED_LIBCLOUD_VERSION = "2.0.0" try: import libcloud from libcloud.compute.base import ( - BaseDriver, Node, - NodeSize, NodeState, NodeLocation, - StorageVolume, StorageVolumeState, - VolumeSnapshot, NodeImage, KeyPair) - if hasattr(libcloud, '__version__') and _LooseVersion(libcloud.__version__) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): + BaseDriver, + Node, + NodeSize, + NodeState, + NodeLocation, + StorageVolume, + StorageVolumeState, + VolumeSnapshot, + NodeImage, + KeyPair, + ) + + if hasattr(libcloud, "__version__") and _LooseVersion( + libcloud.__version__ + ) < _LooseVersion(REQUIRED_LIBCLOUD_VERSION): raise ImportError() - logging.getLogger('libcloud').setLevel(logging.CRITICAL) + logging.getLogger("libcloud").setLevel(logging.CRITICAL) HAS_LIBCLOUD = True except ImportError: HAS_LIBCLOUD = False if HAS_LIBCLOUD: + class MockComputeDriver(BaseDriver): def __init__(self): # pylint: disable=W0231 self._TEST_SIZE = NodeSize( - id='test_id', name='test_size', - ram=4096, disk=10240, bandwidth=100000, price=0, - driver=self) + id="test_id", + name="test_size", + ram=4096, + disk=10240, + bandwidth=100000, + price=0, + driver=self, + ) self._TEST_NODE = Node( - id='test_id', name='test_node', - state=NodeState.RUNNING, public_ips=['1.2.3.4'], - private_ips=['2.3.4.5'], driver=self, - size=self._TEST_SIZE, extra={ - 'ex_key': 'ex_value' - }) + id="test_id", + name="test_node", + state=NodeState.RUNNING, + public_ips=["1.2.3.4"], + private_ips=["2.3.4.5"], + driver=self, + size=self._TEST_SIZE, + extra={"ex_key": "ex_value"}, + ) self._TEST_LOCATION = NodeLocation( - id='test_location', - name='location1', - country='Australia', - driver=self) + id="test_location", name="location1", country="Australia", driver=self + ) self._TEST_VOLUME = StorageVolume( - id='vol1', - name='vol_name', + id="vol1", + name="vol_name", size=40960, driver=self, state=StorageVolumeState.AVAILABLE, - extra={ - 'ex_key': 'ex_value' - } + extra={"ex_key": "ex_value"}, ) self._TEST_VOLUME_SNAPSHOT = VolumeSnapshot( - id='snap1', - size=80960, - driver=self + id="snap1", size=80960, driver=self ) self._TEST_IMAGE = NodeImage( - id='image1', - name='test_image', - extra={ - 'ex_key': 'ex_value' - }, - driver=self + id="image1", + name="test_image", + extra={"ex_key": "ex_value"}, + driver=self, ) self._TEST_KEY_PAIR = KeyPair( - name='test_key', - fingerprint='abc123', - public_key='pub123', - private_key='priv123', + name="test_key", + fingerprint="abc123", + public_key="pub123", + private_key="priv123", driver=self, - extra={ - 'ex_key': 'ex_value' - } + extra={"ex_key": "ex_value"}, ) def list_nodes(self): @@ -92,104 +99,104 @@ if HAS_LIBCLOUD: def list_sizes(self, location=None): if location: - assert location.id == 'test_location' + assert location.id == "test_location" return [self._TEST_SIZE] def list_locations(self): return [self._TEST_LOCATION] def reboot_node(self, node): - assert node.id == 'test_id' + assert node.id == "test_id" return True def destroy_node(self, node): - assert node.id == 'test_id' + assert node.id == "test_id" return True def list_volumes(self): return [self._TEST_VOLUME] def list_volume_snapshots(self, volume): - assert volume.id == 'vol1' + assert volume.id == "vol1" return [self._TEST_VOLUME_SNAPSHOT] def create_volume(self, size, name, location=None, snapshot=None): assert size == 9000 - assert name == 'test_new_volume' + assert name == "test_new_volume" if location: - assert location.country == 'Australia' + assert location.country == "Australia" return self._TEST_VOLUME def create_volume_snapshot(self, volume, name=None): - assert volume.id == 'vol1' + assert volume.id == "vol1" if name: - assert name == 'test_snapshot' + assert name == "test_snapshot" return self._TEST_VOLUME_SNAPSHOT def attach_volume(self, node, volume, device=None): - assert node.id == 'test_id' - assert volume.id == 'vol1' + assert node.id == "test_id" + assert volume.id == "vol1" if device: - assert device == '/dev/sdc' + assert device == "/dev/sdc" return True def detach_volume(self, volume): - assert volume.id == 'vol1' + assert volume.id == "vol1" return True def destroy_volume(self, volume): - assert volume.id == 'vol1' + assert volume.id == "vol1" return True def destroy_volume_snapshot(self, snapshot): - assert snapshot.id == 'snap1' + assert snapshot.id == "snap1" return True def list_images(self, location=None): if location: - assert location.id == 'test_location' + assert location.id == "test_location" return [self._TEST_IMAGE] def create_image(self, node, name, description=None): - assert node.id == 'test_id' + assert node.id == "test_id" return self._TEST_IMAGE def delete_image(self, node_image): return True def get_image(self, image_id): - assert image_id == 'image1' + assert image_id == "image1" return self._TEST_IMAGE def copy_image(self, source_region, node_image, name, description=None): - assert source_region == 'us-east1' - assert node_image.id == 'image1' - assert name == 'copy_test' + assert source_region == "us-east1" + assert node_image.id == "image1" + assert name == "copy_test" return self._TEST_IMAGE def list_key_pairs(self): return [self._TEST_KEY_PAIR] def get_key_pair(self, name): - assert name == 'test_key' + assert name == "test_key" return self._TEST_KEY_PAIR def create_key_pair(self, name): - assert name == 'test_key' + assert name == "test_key" return self._TEST_KEY_PAIR def import_key_pair_from_string(self, name, key_material): - assert name == 'test_key' - assert key_material == 'test_key_value' + assert name == "test_key" + assert key_material == "test_key_value" return self._TEST_KEY_PAIR def import_key_pair_from_file(self, name, key_file_path): - assert name == 'test_key' - assert key_file_path == '/path/to/key' + assert name == "test_key" + assert key_file_path == "/path/to/key" return self._TEST_KEY_PAIR def delete_key_pair(self, key_pair): - assert key_pair.name == 'test_key' + assert key_pair.name == "test_key" return True @@ -197,199 +204,205 @@ else: MockComputeDriver = object -@skipIf(not HAS_LIBCLOUD, 'No libcloud installed') -@patch('salt.modules.libcloud_compute._get_driver', - MagicMock(return_value=MockComputeDriver())) +@skipIf(not HAS_LIBCLOUD, "No libcloud installed") +@patch( + "salt.modules.libcloud_compute._get_driver", + MagicMock(return_value=MockComputeDriver()), +) class LibcloudComputeModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }) + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ) } } if libcloud_compute.HAS_LIBCLOUD is False: - module_globals['sys.modules'] = {'libcloud': MagicMock()} + module_globals["sys.modules"] = {"libcloud": MagicMock()} return {libcloud_compute: module_globals} def test_module_creation(self): - client = libcloud_compute._get_driver('test') + client = libcloud_compute._get_driver("test") self.assertFalse(client is None) def test_init(self): - with patch('salt.utils.compat.pack_dunder', return_value=False) as dunder: + with patch("salt.utils.compat.pack_dunder", return_value=False) as dunder: libcloud_compute.__init__(None) - dunder.assert_called_with('salt.modules.libcloud_compute') + dunder.assert_called_with("salt.modules.libcloud_compute") def _validate_node(self, node): - self.assertEqual(node['name'], 'test_node') - self.assertEqual(node['id'], 'test_id') - self.assertEqual(node['private_ips'], ['2.3.4.5']) - self.assertEqual(node['public_ips'], ['1.2.3.4']) - self.assertEqual(node['size']['name'], 'test_size') + self.assertEqual(node["name"], "test_node") + self.assertEqual(node["id"], "test_id") + self.assertEqual(node["private_ips"], ["2.3.4.5"]) + self.assertEqual(node["public_ips"], ["1.2.3.4"]) + self.assertEqual(node["size"]["name"], "test_size") def _validate_size(self, size): - self.assertEqual(size['id'], 'test_id') - self.assertEqual(size['name'], 'test_size') - self.assertEqual(size['ram'], 4096) + self.assertEqual(size["id"], "test_id") + self.assertEqual(size["name"], "test_size") + self.assertEqual(size["ram"], 4096) def _validate_location(self, location): - self.assertEqual(location['id'], 'test_location') - self.assertEqual(location['name'], 'location1') - self.assertEqual(location['country'], 'Australia') + self.assertEqual(location["id"], "test_location") + self.assertEqual(location["name"], "location1") + self.assertEqual(location["country"], "Australia") def _validate_volume(self, volume): - self.assertEqual(volume['id'], 'vol1') - self.assertEqual(volume['name'], 'vol_name') - self.assertEqual(volume['size'], 40960) - self.assertEqual(volume['state'], 'available') - self.assertEqual(volume['extra'], {'ex_key': 'ex_value'}) + self.assertEqual(volume["id"], "vol1") + self.assertEqual(volume["name"], "vol_name") + self.assertEqual(volume["size"], 40960) + self.assertEqual(volume["state"], "available") + self.assertEqual(volume["extra"], {"ex_key": "ex_value"}) def _validate_volume_snapshot(self, volume): - self.assertEqual(volume['id'], 'snap1') - self.assertEqual(volume['size'], 80960) + self.assertEqual(volume["id"], "snap1") + self.assertEqual(volume["size"], 80960) def _validate_image(self, image): - self.assertEqual(image['id'], 'image1') - self.assertEqual(image['name'], 'test_image') - self.assertEqual(image['extra'], {'ex_key': 'ex_value'}) + self.assertEqual(image["id"], "image1") + self.assertEqual(image["name"], "test_image") + self.assertEqual(image["extra"], {"ex_key": "ex_value"}) def _validate_key_pair(self, key): - self.assertEqual(key['name'], 'test_key') - self.assertEqual(key['fingerprint'], 'abc123') - self.assertEqual(key['extra'], {'ex_key': 'ex_value'}) + self.assertEqual(key["name"], "test_key") + self.assertEqual(key["fingerprint"], "abc123") + self.assertEqual(key["extra"], {"ex_key": "ex_value"}) def test_list_nodes(self): - nodes = libcloud_compute.list_nodes('test') + nodes = libcloud_compute.list_nodes("test") self.assertEqual(len(nodes), 1) self._validate_node(nodes[0]) def test_list_sizes(self): - sizes = libcloud_compute.list_sizes('test') + sizes = libcloud_compute.list_sizes("test") self.assertEqual(len(sizes), 1) self._validate_size(sizes[0]) def test_list_sizes_location(self): - sizes = libcloud_compute.list_sizes('test', location_id='test_location') + sizes = libcloud_compute.list_sizes("test", location_id="test_location") self.assertEqual(len(sizes), 1) self._validate_size(sizes[0]) def test_list_locations(self): - locations = libcloud_compute.list_locations('test') + locations = libcloud_compute.list_locations("test") self.assertEqual(len(locations), 1) self._validate_location(locations[0]) def test_reboot_node(self): - result = libcloud_compute.reboot_node('test_id', 'test') + result = libcloud_compute.reboot_node("test_id", "test") self.assertTrue(result) def test_reboot_node_invalid(self): with self.assertRaises(ValueError): - libcloud_compute.reboot_node('foo_node', 'test') + libcloud_compute.reboot_node("foo_node", "test") def test_destroy_node(self): - result = libcloud_compute.destroy_node('test_id', 'test') + result = libcloud_compute.destroy_node("test_id", "test") self.assertTrue(result) def test_destroy_node_invalid(self): with self.assertRaises(ValueError): - libcloud_compute.destroy_node('foo_node', 'test') + libcloud_compute.destroy_node("foo_node", "test") def test_list_volumes(self): - volumes = libcloud_compute.list_volumes('test') + volumes = libcloud_compute.list_volumes("test") self.assertEqual(len(volumes), 1) self._validate_volume(volumes[0]) def test_list_volume_snapshots(self): - volumes = libcloud_compute.list_volume_snapshots('vol1', 'test') + volumes = libcloud_compute.list_volume_snapshots("vol1", "test") self.assertEqual(len(volumes), 1) self._validate_volume_snapshot(volumes[0]) def test_create_volume(self): - volume = libcloud_compute.create_volume(9000, 'test_new_volume', 'test') + volume = libcloud_compute.create_volume(9000, "test_new_volume", "test") self._validate_volume(volume) def test_create_volume_in_location(self): - volume = libcloud_compute.create_volume(9000, 'test_new_volume', 'test', location_id='test_location') + volume = libcloud_compute.create_volume( + 9000, "test_new_volume", "test", location_id="test_location" + ) self._validate_volume(volume) def test_create_volume_snapshot(self): - snapshot = libcloud_compute.create_volume_snapshot('vol1', 'test') + snapshot = libcloud_compute.create_volume_snapshot("vol1", "test") self._validate_volume_snapshot(snapshot) def test_create_volume_snapshot_named(self): - snapshot = libcloud_compute.create_volume_snapshot('vol1', 'test', name='test_snapshot') + snapshot = libcloud_compute.create_volume_snapshot( + "vol1", "test", name="test_snapshot" + ) self._validate_volume_snapshot(snapshot) def test_attach_volume(self): - result = libcloud_compute.attach_volume('test_id', 'vol1', 'test') + result = libcloud_compute.attach_volume("test_id", "vol1", "test") self.assertTrue(result) def test_detatch_volume(self): - result = libcloud_compute.detach_volume('vol1', 'test') + result = libcloud_compute.detach_volume("vol1", "test") self.assertTrue(result) def test_destroy_volume(self): - result = libcloud_compute.destroy_volume('vol1', 'test') + result = libcloud_compute.destroy_volume("vol1", "test") self.assertTrue(result) def test_destroy_volume_snapshot(self): - result = libcloud_compute.destroy_volume_snapshot('vol1', 'snap1', 'test') + result = libcloud_compute.destroy_volume_snapshot("vol1", "snap1", "test") self.assertTrue(result) def test_list_images(self): - images = libcloud_compute.list_images('test') + images = libcloud_compute.list_images("test") self.assertEqual(len(images), 1) self._validate_image(images[0]) def test_list_images_in_location(self): - images = libcloud_compute.list_images('test', location_id='test_location') + images = libcloud_compute.list_images("test", location_id="test_location") self.assertEqual(len(images), 1) self._validate_image(images[0]) def test_create_image(self): - image = libcloud_compute.create_image('test_id', 'new_image', 'test') + image = libcloud_compute.create_image("test_id", "new_image", "test") self._validate_image(image) def test_delete_image(self): - result = libcloud_compute.delete_image('image1', 'test') + result = libcloud_compute.delete_image("image1", "test") self.assertTrue(result) def test_get_image(self): - image = libcloud_compute.get_image('image1', 'test') + image = libcloud_compute.get_image("image1", "test") self._validate_image(image) def test_copy_image(self): - new_image = libcloud_compute.copy_image('us-east1', 'image1', 'copy_test', 'test') + new_image = libcloud_compute.copy_image( + "us-east1", "image1", "copy_test", "test" + ) self._validate_image(new_image) def test_list_key_pairs(self): - keys = libcloud_compute.list_key_pairs('test') + keys = libcloud_compute.list_key_pairs("test") self.assertEqual(len(keys), 1) self._validate_key_pair(keys[0]) def test_get_key_pair(self): - key = libcloud_compute.get_key_pair('test_key', 'test') + key = libcloud_compute.get_key_pair("test_key", "test") self._validate_key_pair(key) def test_create_key_pair(self): - key = libcloud_compute.create_key_pair('test_key', 'test') + key = libcloud_compute.create_key_pair("test_key", "test") self._validate_key_pair(key) def test_import_key_string(self): - key = libcloud_compute.import_key_pair('test_key', 'test_key_value', 'test') + key = libcloud_compute.import_key_pair("test_key", "test_key_value", "test") self._validate_key_pair(key) def test_import_key_file(self): - key = libcloud_compute.import_key_pair('test_key', '/path/to/key', 'test', key_type='FILE') + key = libcloud_compute.import_key_pair( + "test_key", "/path/to/key", "test", key_type="FILE" + ) self._validate_key_pair(key) def test_delete_key_pair(self): - result = libcloud_compute.delete_key_pair('test_key', 'test') + result = libcloud_compute.delete_key_pair("test_key", "test") self.assertTrue(result) diff --git a/tests/unit/modules/test_libcloud_dns.py b/tests/unit/modules/test_libcloud_dns.py index e0158f57dd6..2aeace4df81 100644 --- a/tests/unit/modules/test_libcloud_dns.py +++ b/tests/unit/modules/test_libcloud_dns.py @@ -1,19 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Anthony Shaw <anthonyshaw@apache.org> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import salt.modules.libcloud_dns as libcloud_dns # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - MagicMock, -) -import salt.modules.libcloud_dns as libcloud_dns class MockDNSDriver(object): @@ -25,31 +23,27 @@ def get_mock_driver(): return MockDNSDriver() -@skipIf(not libcloud_dns.HAS_LIBCLOUD, 'No libcloud installed') +@skipIf(not libcloud_dns.HAS_LIBCLOUD, "No libcloud installed") class LibcloudDnsModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '_get_driver': MagicMock(return_value=MockDNSDriver()), - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }) - } + "_get_driver": MagicMock(return_value=MockDNSDriver()), + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ) + }, } if libcloud_dns.HAS_LIBCLOUD is False: - module_globals['sys.modules'] = {'libcloud': MagicMock()} + module_globals["sys.modules"] = {"libcloud": MagicMock()} return {libcloud_dns: module_globals} def test_module_creation(self): - client = libcloud_dns._get_driver('test') + client = libcloud_dns._get_driver("test") self.assertFalse(client is None) def test_init(self): - with patch('salt.utils.compat.pack_dunder', return_value=False) as dunder: + with patch("salt.utils.compat.pack_dunder", return_value=False) as dunder: libcloud_dns.__init__(None) - dunder.assert_called_with('salt.modules.libcloud_dns') + dunder.assert_called_with("salt.modules.libcloud_dns") diff --git a/tests/unit/modules/test_libcloud_loadbalancer.py b/tests/unit/modules/test_libcloud_loadbalancer.py index 6b8b6e72ceb..5c092674c0c 100644 --- a/tests/unit/modules/test_libcloud_loadbalancer.py +++ b/tests/unit/modules/test_libcloud_loadbalancer.py @@ -1,54 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import salt.modules.libcloud_loadbalancer as libcloud_loadbalancer # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - MagicMock, -) -import salt.modules.libcloud_loadbalancer as libcloud_loadbalancer try: from libcloud.loadbalancer.base import BaseDriver, LoadBalancer, Algorithm, Member + HAS_LIBCLOUD = True except ImportError: HAS_LIBCLOUD = False if HAS_LIBCLOUD: + class MockLBDriver(BaseDriver): def __init__(self): # pylint: disable=W0231 self._TEST_BALANCER = LoadBalancer( - id='test_id', name='test_balancer', + id="test_id", + name="test_balancer", state=0, # RUNNING - ip='1.2.3.4', - port=80, driver=self, - extra={}) + ip="1.2.3.4", + port=80, + driver=self, + extra={}, + ) self._TEST_MEMBER = Member( - id='member_id', ip='12.3.4.5', - port=443, balancer=self._TEST_BALANCER, - extra=None) + id="member_id", + ip="12.3.4.5", + port=443, + balancer=self._TEST_BALANCER, + extra=None, + ) def get_balancer(self, balancer_id): - assert balancer_id == 'test_id' + assert balancer_id == "test_id" return self._TEST_BALANCER def list_balancers(self): return [self._TEST_BALANCER] def list_protocols(self): - return ['http', 'https'] + return ["http", "https"] def create_balancer(self, name, port, protocol, algorithm, members): - assert name == 'new_test_balancer' + assert name == "new_test_balancer" assert port == 80 - assert protocol == 'http' + assert protocol == "http" assert isinstance(algorithm, (Algorithm, int)) assert isinstance(members, list) return self._TEST_BALANCER @@ -61,19 +67,19 @@ if HAS_LIBCLOUD: assert isinstance(balancer, LoadBalancer) assert isinstance(member, Member) assert member.id is None - assert balancer.id == 'test_id' + assert balancer.id == "test_id" return self._TEST_MEMBER def balancer_detach_member(self, balancer, member): assert isinstance(balancer, LoadBalancer) assert isinstance(member, Member) - assert member.id == 'member_id' - assert balancer.id == 'test_id' + assert member.id == "member_id" + assert balancer.id == "test_id" return True def balancer_list_members(self, balancer): assert isinstance(balancer, LoadBalancer) - assert balancer.id == 'test_id' + assert balancer.id == "test_id" return [self._TEST_MEMBER] @@ -85,81 +91,87 @@ def get_mock_driver(): return MockLBDriver() -@skipIf(not HAS_LIBCLOUD, 'No libcloud package') -@patch('salt.modules.libcloud_loadbalancer._get_driver', - MagicMock(return_value=MockLBDriver())) +@skipIf(not HAS_LIBCLOUD, "No libcloud package") +@patch( + "salt.modules.libcloud_loadbalancer._get_driver", + MagicMock(return_value=MockLBDriver()), +) class LibcloudLoadBalancerModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }) + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ) } } if libcloud_loadbalancer.HAS_LIBCLOUD is False: - module_globals['sys.modules'] = {'libcloud': MagicMock()} + module_globals["sys.modules"] = {"libcloud": MagicMock()} return {libcloud_loadbalancer: module_globals} def test_module_creation(self): - client = libcloud_loadbalancer._get_driver('test') + client = libcloud_loadbalancer._get_driver("test") self.assertFalse(client is None) def test_init(self): - with patch('salt.utils.compat.pack_dunder', return_value=False) as dunder: + with patch("salt.utils.compat.pack_dunder", return_value=False) as dunder: libcloud_loadbalancer.__init__(None) - dunder.assert_called_with('salt.modules.libcloud_loadbalancer') + dunder.assert_called_with("salt.modules.libcloud_loadbalancer") def _validate_balancer(self, balancer): - self.assertEqual(balancer['name'], 'test_balancer') + self.assertEqual(balancer["name"], "test_balancer") def _validate_member(self, member): - self.assertEqual(member['id'], 'member_id') - self.assertEqual(member['ip'], '12.3.4.5') + self.assertEqual(member["id"], "member_id") + self.assertEqual(member["ip"], "12.3.4.5") def test_list_balancers(self): - balancers = libcloud_loadbalancer.list_balancers('test') + balancers = libcloud_loadbalancer.list_balancers("test") self.assertEqual(len(balancers), 1) self._validate_balancer(balancers[0]) def test_list_protocols(self): - protocols = libcloud_loadbalancer.list_protocols('test') + protocols = libcloud_loadbalancer.list_protocols("test") self.assertEqual(len(protocols), 2) - self.assertTrue('http' in protocols) + self.assertTrue("http" in protocols) def test_create_balancer(self): - balancer = libcloud_loadbalancer.create_balancer('new_test_balancer', 80, 'http', 'test') + balancer = libcloud_loadbalancer.create_balancer( + "new_test_balancer", 80, "http", "test" + ) self._validate_balancer(balancer) def test_create_balancer_custom_algorithm(self): - balancer = libcloud_loadbalancer.create_balancer('new_test_balancer', 80, 'http', 'test', algorithm='LEAST_CONNECTIONS') + balancer = libcloud_loadbalancer.create_balancer( + "new_test_balancer", 80, "http", "test", algorithm="LEAST_CONNECTIONS" + ) self._validate_balancer(balancer) def test_destroy_balancer(self): - result = libcloud_loadbalancer.destroy_balancer('test_id', 'test') + result = libcloud_loadbalancer.destroy_balancer("test_id", "test") self.assertTrue(result) def test_get_balancer_by_name(self): - balancer = libcloud_loadbalancer.get_balancer_by_name('test_balancer', 'test') + balancer = libcloud_loadbalancer.get_balancer_by_name("test_balancer", "test") self._validate_balancer(balancer) def test_get_balancer(self): - balancer = libcloud_loadbalancer.get_balancer('test_id', 'test') + balancer = libcloud_loadbalancer.get_balancer("test_id", "test") self._validate_balancer(balancer) def test_balancer_attach_member(self): - member = libcloud_loadbalancer.balancer_attach_member('test_id', '12.3.4.5', 443, 'test') + member = libcloud_loadbalancer.balancer_attach_member( + "test_id", "12.3.4.5", 443, "test" + ) self._validate_member(member) def test_balancer_detach_member(self): - result = libcloud_loadbalancer.balancer_detach_member('test_id', 'member_id', 'test') + result = libcloud_loadbalancer.balancer_detach_member( + "test_id", "member_id", "test" + ) self.assertTrue(result) def test_list_balancer_members(self): - members = libcloud_loadbalancer.list_balancer_members('test_id', 'test') + members = libcloud_loadbalancer.list_balancer_members("test_id", "test") self._validate_member(members[0]) diff --git a/tests/unit/modules/test_libcloud_storage.py b/tests/unit/modules/test_libcloud_storage.py index 8c4786e5b8b..fbe8d19f073 100644 --- a/tests/unit/modules/test_libcloud_storage.py +++ b/tests/unit/modules/test_libcloud_storage.py @@ -1,58 +1,64 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import salt.modules.libcloud_storage as libcloud_storage # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - MagicMock, -) -import salt.modules.libcloud_storage as libcloud_storage try: from libcloud.storage.base import Container, BaseDriver, Object + HAS_LIBCLOUD = True except ImportError: HAS_LIBCLOUD = False if HAS_LIBCLOUD: + class MockStorageDriver(BaseDriver): def __init__(self): # pylint: disable=W0231 - self._TEST_CONTAINER = Container(name='test_container', extra={}, driver=self) - self._TEST_OBJECT = Object(name='test_obj', - size=1234, - hash='123sdfsdf', - extra={}, - meta_data={'key': 'value'}, - container=self._TEST_CONTAINER, - driver=self) + self._TEST_CONTAINER = Container( + name="test_container", extra={}, driver=self + ) + self._TEST_OBJECT = Object( + name="test_obj", + size=1234, + hash="123sdfsdf", + extra={}, + meta_data={"key": "value"}, + container=self._TEST_CONTAINER, + driver=self, + ) def list_containers(self): return [self._TEST_CONTAINER] def get_container(self, container_name): - assert container_name == 'test_container' + assert container_name == "test_container" return self._TEST_CONTAINER def list_container_objects(self, container): - assert container.name == 'test_container' + assert container.name == "test_container" return [self._TEST_OBJECT] def create_container(self, container_name): - assert container_name == 'new_test_container' + assert container_name == "new_test_container" return self._TEST_CONTAINER def get_container_object(self, container_name, object_name): - assert container_name == 'test_container' - assert object_name == 'test_obj' + assert container_name == "test_container" + assert object_name == "test_obj" return self._TEST_OBJECT + + else: MockStorageDriver = object @@ -61,56 +67,56 @@ def get_mock_driver(): return MockStorageDriver() -@skipIf(not HAS_LIBCLOUD, 'No libcloud installed') -@patch('salt.modules.libcloud_storage._get_driver', - MagicMock(return_value=MockStorageDriver())) +@skipIf(not HAS_LIBCLOUD, "No libcloud installed") +@patch( + "salt.modules.libcloud_storage._get_driver", + MagicMock(return_value=MockStorageDriver()), +) class LibcloudStorageModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }) + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ) } } if libcloud_storage.HAS_LIBCLOUD is False: - module_globals['sys.modules'] = {'libcloud': MagicMock()} + module_globals["sys.modules"] = {"libcloud": MagicMock()} return {libcloud_storage: module_globals} def test_module_creation(self): - client = libcloud_storage._get_driver('test') + client = libcloud_storage._get_driver("test") self.assertFalse(client is None) def test_init(self): - with patch('salt.utils.compat.pack_dunder', return_value=False) as dunder: + with patch("salt.utils.compat.pack_dunder", return_value=False) as dunder: libcloud_storage.__init__(None) - dunder.assert_called_with('salt.modules.libcloud_storage') + dunder.assert_called_with("salt.modules.libcloud_storage") def test_list_containers(self): - containers = libcloud_storage.list_containers('test') + containers = libcloud_storage.list_containers("test") self.assertEqual(len(containers), 1) - self.assertEqual(containers[0]['name'], 'test_container') + self.assertEqual(containers[0]["name"], "test_container") def test_list_container_objects(self): - objects = libcloud_storage.list_container_objects('test_container', 'test') + objects = libcloud_storage.list_container_objects("test_container", "test") self.assertEqual(len(objects), 1) - self.assertEqual(objects[0]['name'], 'test_obj') - self.assertEqual(objects[0]['size'], 1234) + self.assertEqual(objects[0]["name"], "test_obj") + self.assertEqual(objects[0]["size"], 1234) def test_create_container(self): - container = libcloud_storage.create_container('new_test_container', 'test') - self.assertEqual(container['name'], 'test_container') + container = libcloud_storage.create_container("new_test_container", "test") + self.assertEqual(container["name"], "test_container") def test_get_container(self): - container = libcloud_storage.get_container('test_container', 'test') - self.assertEqual(container['name'], 'test_container') + container = libcloud_storage.get_container("test_container", "test") + self.assertEqual(container["name"], "test_container") def test_get_container_object(self): - obj = libcloud_storage.get_container_object('test_container', 'test_obj', 'test') - self.assertEqual(obj['name'], 'test_obj') - self.assertEqual(obj['size'], 1234) + obj = libcloud_storage.get_container_object( + "test_container", "test_obj", "test" + ) + self.assertEqual(obj["name"], "test_obj") + self.assertEqual(obj["size"], 1234) diff --git a/tests/unit/modules/test_linux_acl.py b/tests/unit/modules/test_linux_acl.py index ec594d71827..7ed937f2f31 100644 --- a/tests/unit/modules/test_linux_acl.py +++ b/tests/unit/modules/test_linux_acl.py @@ -3,44 +3,55 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import salt libs import salt.modules.linux_acl as linux_acl from salt.exceptions import CommandExecutionError +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LinuxAclTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): self.cmdrun = MagicMock() - self.addCleanup(delattr, self, 'cmdrun') - return {linux_acl: {'__salt__': {'cmd.run': self.cmdrun}}} + self.addCleanup(delattr, self, "cmdrun") + return {linux_acl: {"__salt__": {"cmd.run": self.cmdrun}}} def setUp(self): - self.file = '/tmp/file' + self.file = "/tmp/file" self.quoted_file = '"/tmp/file"' - self.files = ['/tmp/file1', '/tmp/file2', '/tmp/file3 with whitespaces'] + self.files = ["/tmp/file1", "/tmp/file2", "/tmp/file3 with whitespaces"] self.quoted_files = ['"{0}"'.format(f) for f in self.files] - self.u_acl = ['u', 'myuser', 'rwx'] - self.user_acl = ['user', 'myuser', 'rwx'] - self.user_acl_cmd = 'u:myuser:rwx' - self.g_acl = ['g', 'mygroup', 'rwx'] - self.group_acl = ['group', 'mygroup', 'rwx'] - self.group_acl_cmd = 'g:mygroup:rwx' - self.d_u_acl = ['d:u', 'myuser', 'rwx'] - self.d_user_acl = ['d:user', 'myuser', 'rwx'] - self.default_user_acl = ['d:user', 'myuser', 'rwx'] - self.default_user_acl_cmd = 'd:u:myuser:rwx' + self.u_acl = ["u", "myuser", "rwx"] + self.user_acl = ["user", "myuser", "rwx"] + self.user_acl_cmd = "u:myuser:rwx" + self.g_acl = ["g", "mygroup", "rwx"] + self.group_acl = ["group", "mygroup", "rwx"] + self.group_acl_cmd = "g:mygroup:rwx" + self.d_u_acl = ["d:u", "myuser", "rwx"] + self.d_user_acl = ["d:user", "myuser", "rwx"] + self.default_user_acl = ["d:user", "myuser", "rwx"] + self.default_user_acl_cmd = "d:u:myuser:rwx" def tearDown(self): - for attrname in ('file', 'quoted_file', 'files', 'quoted_files', - 'u_acl', 'user_acl', 'user_acl_cmd', 'g_acl', - 'group_acl', 'group_acl_cmd', 'd_u_acl', - 'd_user_acl', 'default_user_acl', 'default_user_acl_cmd'): + for attrname in ( + "file", + "quoted_file", + "files", + "quoted_files", + "u_acl", + "user_acl", + "user_acl_cmd", + "g_acl", + "group_acl", + "group_acl_cmd", + "d_u_acl", + "d_user_acl", + "default_user_acl", + "default_user_acl_cmd", + ): delattr(self, attrname) # too easy to test (DRY) @@ -52,29 +63,33 @@ class LinuxAclTestCase(TestCase, LoaderModuleMockMixin): def test_getfacl_w_single_arg(self): linux_acl.getfacl(self.file) - self.cmdrun.assert_called_once_with('getfacl --absolute-names ' + self.quoted_file, python_shell=False) + self.cmdrun.assert_called_once_with( + "getfacl --absolute-names " + self.quoted_file, python_shell=False + ) def test_getfacl_w_multiple_args(self): linux_acl.getfacl(*self.files) - self.cmdrun.assert_called_once_with('getfacl --absolute-names ' + ' '.join(self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "getfacl --absolute-names " + " ".join(self.quoted_files), + python_shell=False, + ) def test_getfacl__recursive_w_multiple_args(self): linux_acl.getfacl(*self.files, recursive=True) - self.cmdrun.assert_called_once_with('getfacl --absolute-names -R ' + ' '.join(self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "getfacl --absolute-names -R " + " ".join(self.quoted_files), + python_shell=False, + ) def test_getfacl__effective_acls(self): - line = 'group:webmaster:r-x #effective:---' - user = 'root' - group = 'root' + line = "group:webmaster:r-x #effective:---" + user = "root" + group = "root" expected = { - 'type': 'acl', - 'group': 'webmaster', - 'permissions': { - 'read': False, - 'write': False, - 'execute': False - }, - 'octal': 0, + "type": "acl", + "group": "webmaster", + "permissions": {"read": False, "write": False, "execute": False}, + "octal": 0, } self.assertEqual(linux_acl._parse_acl(line, user, group), expected) @@ -83,15 +98,21 @@ class LinuxAclTestCase(TestCase, LoaderModuleMockMixin): def test_wipefacls_w_single_arg(self): linux_acl.wipefacls(self.file) - self.cmdrun.assert_called_once_with('setfacl -b ' + self.quoted_file, python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -b " + self.quoted_file, python_shell=False + ) def test_wipefacls_w_multiple_args(self): linux_acl.wipefacls(*self.files) - self.cmdrun.assert_called_once_with('setfacl -b ' + ' '.join(self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -b " + " ".join(self.quoted_files), python_shell=False + ) def test_wipefacls__recursive_w_multiple_args(self): linux_acl.wipefacls(*self.files, recursive=True) - self.cmdrun.assert_called_once_with('setfacl -b -R ' + ' '.join(self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -b -R " + " ".join(self.quoted_files), python_shell=False + ) def test_modfacl_wo_args(self): for acl in [self.u_acl, self.user_acl, self.g_acl, self.group_acl]: @@ -99,70 +120,130 @@ class LinuxAclTestCase(TestCase, LoaderModuleMockMixin): def test_modfacl__u_w_single_arg(self): linux_acl.modfacl(*(self.u_acl + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.user_acl_cmd, self.quoted_file]), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.user_acl_cmd, self.quoted_file]), + python_shell=False, + raise_err=False, + ) def test_modfacl__u_w_multiple_args(self): linux_acl.modfacl(*(self.u_acl + self.files)) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.user_acl_cmd] + self.quoted_files), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.user_acl_cmd] + self.quoted_files), + python_shell=False, + raise_err=False, + ) def test_modfacl__user_w_single_arg(self): linux_acl.modfacl(*(self.user_acl + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.user_acl_cmd, self.quoted_file]), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.user_acl_cmd, self.quoted_file]), + python_shell=False, + raise_err=False, + ) def test_modfacl__user_w_multiple_args(self): linux_acl.modfacl(*(self.user_acl + self.files)) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.user_acl_cmd] + self.quoted_files), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.user_acl_cmd] + self.quoted_files), + python_shell=False, + raise_err=False, + ) def test_modfacl__g_w_single_arg(self): linux_acl.modfacl(*(self.g_acl + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.group_acl_cmd, self.quoted_file]), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.group_acl_cmd, self.quoted_file]), + python_shell=False, + raise_err=False, + ) def test_modfacl__g_w_multiple_args(self): linux_acl.modfacl(*(self.g_acl + self.files)) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.group_acl_cmd] + self.quoted_files), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.group_acl_cmd] + self.quoted_files), + python_shell=False, + raise_err=False, + ) def test_modfacl__group_w_single_arg(self): linux_acl.modfacl(*(self.group_acl + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.group_acl_cmd, self.quoted_file]), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.group_acl_cmd, self.quoted_file]), + python_shell=False, + raise_err=False, + ) def test_modfacl__group_w_multiple_args(self): linux_acl.modfacl(*(self.group_acl + self.files)) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.group_acl_cmd] + self.quoted_files), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.group_acl_cmd] + self.quoted_files), + python_shell=False, + raise_err=False, + ) def test_modfacl__d_u_w_single_arg(self): linux_acl.modfacl(*(self.d_u_acl + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.default_user_acl_cmd, self.quoted_file]), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.default_user_acl_cmd, self.quoted_file]), + python_shell=False, + raise_err=False, + ) def test_modfacl__d_u_w_multiple_args(self): linux_acl.modfacl(*(self.d_u_acl + self.files)) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.default_user_acl_cmd] + self.quoted_files), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.default_user_acl_cmd] + self.quoted_files), + python_shell=False, + raise_err=False, + ) def test_modfacl__d_user_w_single_arg(self): linux_acl.modfacl(*(self.d_user_acl + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.default_user_acl_cmd, self.quoted_file]), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.default_user_acl_cmd, self.quoted_file]), + python_shell=False, + raise_err=False, + ) def test_modfacl__d_user_w_multiple_args(self): linux_acl.modfacl(*(self.d_user_acl + self.files)) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.default_user_acl_cmd] + self.quoted_files), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.default_user_acl_cmd] + self.quoted_files), + python_shell=False, + raise_err=False, + ) def test_modfacl__default_user_w_single_arg(self): linux_acl.modfacl(*(self.default_user_acl + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.default_user_acl_cmd, self.quoted_file]), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.default_user_acl_cmd, self.quoted_file]), + python_shell=False, + raise_err=False, + ) def test_modfacl__default_user_w_multiple_args(self): linux_acl.modfacl(*(self.default_user_acl + self.files)) - self.cmdrun.assert_called_once_with('setfacl -m ' + ' '.join([self.default_user_acl_cmd] + self.quoted_files), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -m " + " ".join([self.default_user_acl_cmd] + self.quoted_files), + python_shell=False, + raise_err=False, + ) def test_modfacl__recursive_w_multiple_args(self): linux_acl.modfacl(*(self.user_acl + self.files), recursive=True) - self.cmdrun.assert_called_once_with('setfacl -R -m ' + ' '.join([self.user_acl_cmd] + self.quoted_files), python_shell=False, raise_err=False) + self.cmdrun.assert_called_once_with( + "setfacl -R -m " + " ".join([self.user_acl_cmd] + self.quoted_files), + python_shell=False, + raise_err=False, + ) def test_modfacl_raise_err(self): - mock = MagicMock(side_effect=CommandExecutionError('Custom err')) - with patch.dict(linux_acl.__salt__, {'cmd.run': mock}): + mock = MagicMock(side_effect=CommandExecutionError("Custom err")) + with patch.dict(linux_acl.__salt__, {"cmd.run": mock}): with self.assertRaises(CommandExecutionError) as excinfo: linux_acl.modfacl(*(self.user_acl + self.files), raise_err=True) - self.assertEqual(excinfo.exception.strerror, 'Custom err') + self.assertEqual(excinfo.exception.strerror, "Custom err") def test_delfacl_wo_args(self): for acl in [self.u_acl, self.user_acl, self.g_acl, self.group_acl]: @@ -170,60 +251,134 @@ class LinuxAclTestCase(TestCase, LoaderModuleMockMixin): def test_delfacl__u_w_single_arg(self): linux_acl.delfacl(*(self.u_acl[:-1] + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.user_acl_cmd.rpartition(':')[0], self.quoted_file]), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join([self.user_acl_cmd.rpartition(":")[0], self.quoted_file]), + python_shell=False, + ) def test_delfacl__u_w_multiple_args(self): linux_acl.delfacl(*(self.u_acl[:-1] + self.files)) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.user_acl_cmd.rpartition(':')[0]] + self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join([self.user_acl_cmd.rpartition(":")[0]] + self.quoted_files), + python_shell=False, + ) def test_delfacl__user_w_single_arg(self): linux_acl.delfacl(*(self.user_acl[:-1] + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.user_acl_cmd.rpartition(':')[0], self.quoted_file]), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join([self.user_acl_cmd.rpartition(":")[0], self.quoted_file]), + python_shell=False, + ) def test_delfacl__user_w_multiple_args(self): linux_acl.delfacl(*(self.user_acl[:-1] + self.files)) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.user_acl_cmd.rpartition(':')[0]] + self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join([self.user_acl_cmd.rpartition(":")[0]] + self.quoted_files), + python_shell=False, + ) def test_delfacl__g_w_single_arg(self): linux_acl.delfacl(*(self.g_acl[:-1] + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.group_acl_cmd.rpartition(':')[0], self.quoted_file]), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join([self.group_acl_cmd.rpartition(":")[0], self.quoted_file]), + python_shell=False, + ) def test_delfacl__g_w_multiple_args(self): linux_acl.delfacl(*(self.g_acl[:-1] + self.files)) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.group_acl_cmd.rpartition(':')[0]] + self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join([self.group_acl_cmd.rpartition(":")[0]] + self.quoted_files), + python_shell=False, + ) def test_delfacl__group_w_single_arg(self): linux_acl.delfacl(*(self.group_acl[:-1] + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.group_acl_cmd.rpartition(':')[0], self.quoted_file]), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join([self.group_acl_cmd.rpartition(":")[0], self.quoted_file]), + python_shell=False, + ) def test_delfacl__group_w_multiple_args(self): linux_acl.delfacl(*(self.group_acl[:-1] + self.files)) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.group_acl_cmd.rpartition(':')[0]] + self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join([self.group_acl_cmd.rpartition(":")[0]] + self.quoted_files), + python_shell=False, + ) def test_delfacl__d_u_w_single_arg(self): linux_acl.delfacl(*(self.d_u_acl[:-1] + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.default_user_acl_cmd.rpartition(':')[0], self.quoted_file]), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join( + [self.default_user_acl_cmd.rpartition(":")[0], self.quoted_file] + ), + python_shell=False, + ) def test_delfacl__d_u_w_multiple_args(self): linux_acl.delfacl(*(self.d_u_acl[:-1] + self.files)) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.default_user_acl_cmd.rpartition(':')[0]] + self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join( + [self.default_user_acl_cmd.rpartition(":")[0]] + self.quoted_files + ), + python_shell=False, + ) def test_delfacl__d_user_w_single_arg(self): linux_acl.delfacl(*(self.d_user_acl[:-1] + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.default_user_acl_cmd.rpartition(':')[0], self.quoted_file]), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join( + [self.default_user_acl_cmd.rpartition(":")[0], self.quoted_file] + ), + python_shell=False, + ) def test_delfacl__d_user_w_multiple_args(self): linux_acl.delfacl(*(self.d_user_acl[:-1] + self.files)) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.default_user_acl_cmd.rpartition(':')[0]] + self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join( + [self.default_user_acl_cmd.rpartition(":")[0]] + self.quoted_files + ), + python_shell=False, + ) def test_delfacl__default_user_w_single_arg(self): linux_acl.delfacl(*(self.default_user_acl[:-1] + [self.file])) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.default_user_acl_cmd.rpartition(':')[0], self.quoted_file]), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join( + [self.default_user_acl_cmd.rpartition(":")[0], self.quoted_file] + ), + python_shell=False, + ) def test_delfacl__default_user_w_multiple_args(self): linux_acl.delfacl(*(self.default_user_acl[:-1] + self.files)) - self.cmdrun.assert_called_once_with('setfacl -x ' + ' '.join([self.default_user_acl_cmd.rpartition(':')[0]] + self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -x " + + " ".join( + [self.default_user_acl_cmd.rpartition(":")[0]] + self.quoted_files + ), + python_shell=False, + ) def test_delfacl__recursive_w_multiple_args(self): linux_acl.delfacl(*(self.default_user_acl[:-1] + self.files), recursive=True) - self.cmdrun.assert_called_once_with('setfacl -R -x ' + ' '.join([self.default_user_acl_cmd.rpartition(':')[0]] + self.quoted_files), python_shell=False) + self.cmdrun.assert_called_once_with( + "setfacl -R -x " + + " ".join( + [self.default_user_acl_cmd.rpartition(":")[0]] + self.quoted_files + ), + python_shell=False, + ) diff --git a/tests/unit/modules/test_linux_lvm.py b/tests/unit/modules/test_linux_lvm.py index 77ebf63720f..6d274b5e3ec 100644 --- a/tests/unit/modules/test_linux_lvm.py +++ b/tests/unit/modules/test_linux_lvm.py @@ -1,311 +1,368 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os.path -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os.path # Import Salt Libs import salt.modules.linux_lvm as linux_lvm from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LinuxLVMTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for the salt.modules.linux_lvm module - ''' + """ + def setup_loader_modules(self): return {linux_lvm: {}} def test_version(self): - ''' + """ Tests LVM version info from lvm version - ''' - mock = MagicMock(return_value= - ' LVM version: 2.02.168(2) (2016-11-30)\n' - ' Library version: 1.03.01 (2016-11-30)\n' - ' Driver version: 4.35.0\n' - ) - with patch.dict(linux_lvm.__salt__, {'cmd.run': mock}): - self.assertEqual(linux_lvm.version(), '2.02.168(2) (2016-11-30)') + """ + mock = MagicMock( + return_value=" LVM version: 2.02.168(2) (2016-11-30)\n" + " Library version: 1.03.01 (2016-11-30)\n" + " Driver version: 4.35.0\n" + ) + with patch.dict(linux_lvm.__salt__, {"cmd.run": mock}): + self.assertEqual(linux_lvm.version(), "2.02.168(2) (2016-11-30)") def test_fullversion(self): - ''' + """ Tests all version info from lvm version - ''' - mock = MagicMock(return_value= - ' LVM version: 2.02.168(2) (2016-11-30)\n' - ' Library version: 1.03.01 (2016-11-30)\n' - ' Driver version: 4.35.0\n' - ) - with patch.dict(linux_lvm.__salt__, {'cmd.run': mock}): - self.assertDictEqual(linux_lvm.fullversion(), - {'LVM version': '2.02.168(2) (2016-11-30)', - 'Library version': '1.03.01 (2016-11-30)', - 'Driver version': '4.35.0', - }) + """ + mock = MagicMock( + return_value=" LVM version: 2.02.168(2) (2016-11-30)\n" + " Library version: 1.03.01 (2016-11-30)\n" + " Driver version: 4.35.0\n" + ) + with patch.dict(linux_lvm.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + linux_lvm.fullversion(), + { + "LVM version": "2.02.168(2) (2016-11-30)", + "Library version": "1.03.01 (2016-11-30)", + "Driver version": "4.35.0", + }, + ) def test_pvdisplay(self): - ''' + """ Tests information about the physical volume(s) - ''' - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): self.assertDictEqual(linux_lvm.pvdisplay(), {}) - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): self.assertDictEqual(linux_lvm.pvdisplay(quiet=True), {}) - mock.assert_called_with(['pvdisplay', '-c'], ignore_retcode=True, - python_shell=False) + mock.assert_called_with( + ["pvdisplay", "-c"], ignore_retcode=True, python_shell=False + ) - mock = MagicMock(return_value={'retcode': 0, - 'stdout': 'A:B:C:D:E:F:G:H:I:J:K'}) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(linux_lvm.pvdisplay(), - {'A': {'Allocated Physical Extents': 'K', - 'Current Logical Volumes Here': 'G', - 'Free Physical Extents': 'J', - 'Internal Physical Volume Number': 'D', - 'Physical Extent Size (kB)': 'H', - 'Physical Volume (not) Allocatable': 'F', - 'Physical Volume Device': 'A', - 'Physical Volume Size (kB)': 'C', - 'Physical Volume Status': 'E', - 'Total Physical Extents': 'I', - 'Volume Group Name': 'B'}}) + mock = MagicMock(return_value={"retcode": 0, "stdout": "A:B:C:D:E:F:G:H:I:J:K"}) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual( + linux_lvm.pvdisplay(), + { + "A": { + "Allocated Physical Extents": "K", + "Current Logical Volumes Here": "G", + "Free Physical Extents": "J", + "Internal Physical Volume Number": "D", + "Physical Extent Size (kB)": "H", + "Physical Volume (not) Allocatable": "F", + "Physical Volume Device": "A", + "Physical Volume Size (kB)": "C", + "Physical Volume Status": "E", + "Total Physical Extents": "I", + "Volume Group Name": "B", + } + }, + ) - mockpath = MagicMock(return_value='Z') - with patch.object(os.path, 'realpath', mockpath): - self.assertDictEqual(linux_lvm.pvdisplay(real=True), - {'Z': {'Allocated Physical Extents': 'K', - 'Current Logical Volumes Here': 'G', - 'Free Physical Extents': 'J', - 'Internal Physical Volume Number': 'D', - 'Physical Extent Size (kB)': 'H', - 'Physical Volume (not) Allocatable': 'F', - 'Physical Volume Device': 'A', - 'Physical Volume Size (kB)': 'C', - 'Physical Volume Status': 'E', - 'Real Physical Volume Device': 'Z', - 'Total Physical Extents': 'I', - 'Volume Group Name': 'B'}}) + mockpath = MagicMock(return_value="Z") + with patch.object(os.path, "realpath", mockpath): + self.assertDictEqual( + linux_lvm.pvdisplay(real=True), + { + "Z": { + "Allocated Physical Extents": "K", + "Current Logical Volumes Here": "G", + "Free Physical Extents": "J", + "Internal Physical Volume Number": "D", + "Physical Extent Size (kB)": "H", + "Physical Volume (not) Allocatable": "F", + "Physical Volume Device": "A", + "Physical Volume Size (kB)": "C", + "Physical Volume Status": "E", + "Real Physical Volume Device": "Z", + "Total Physical Extents": "I", + "Volume Group Name": "B", + } + }, + ) def test_vgdisplay(self): - ''' + """ Tests information about the volume group(s) - ''' - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): self.assertDictEqual(linux_lvm.vgdisplay(), {}) - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): self.assertDictEqual(linux_lvm.vgdisplay(quiet=True), {}) - mock.assert_called_with(['vgdisplay', '-c'], ignore_retcode=True, - python_shell=False) + mock.assert_called_with( + ["vgdisplay", "-c"], ignore_retcode=True, python_shell=False + ) - mock = MagicMock(return_value={'retcode': 0, - 'stdout': 'A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q'}) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(linux_lvm.vgdisplay(), - {'A': {'Actual Physical Volumes': 'K', - 'Allocated Physical Extents': 'O', - 'Current Logical Volumes': 'F', - 'Current Physical Volumes': 'J', - 'Free Physical Extents': 'P', - 'Internal Volume Group Number': 'D', - 'Maximum Logical Volume Size': 'H', - 'Maximum Logical Volumes': 'E', - 'Maximum Physical Volumes': 'I', - 'Open Logical Volumes': 'G', - 'Physical Extent Size (kB)': 'M', - 'Total Physical Extents': 'N', - 'UUID': 'Q', - 'Volume Group Access': 'B', - 'Volume Group Name': 'A', - 'Volume Group Size (kB)': 'L', - 'Volume Group Status': 'C'}}) + mock = MagicMock( + return_value={"retcode": 0, "stdout": "A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q"} + ) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual( + linux_lvm.vgdisplay(), + { + "A": { + "Actual Physical Volumes": "K", + "Allocated Physical Extents": "O", + "Current Logical Volumes": "F", + "Current Physical Volumes": "J", + "Free Physical Extents": "P", + "Internal Volume Group Number": "D", + "Maximum Logical Volume Size": "H", + "Maximum Logical Volumes": "E", + "Maximum Physical Volumes": "I", + "Open Logical Volumes": "G", + "Physical Extent Size (kB)": "M", + "Total Physical Extents": "N", + "UUID": "Q", + "Volume Group Access": "B", + "Volume Group Name": "A", + "Volume Group Size (kB)": "L", + "Volume Group Status": "C", + } + }, + ) def test_lvdisplay(self): - ''' + """ Return information about the logical volume(s) - ''' - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): self.assertDictEqual(linux_lvm.lvdisplay(), {}) - mock = MagicMock(return_value={'retcode': 0, - 'stdout': 'A:B:C:D:E:F:G:H:I:J:K:L:M'}) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(linux_lvm.lvdisplay(), - {'A': {'Allocated Logical Extents': 'I', - 'Allocation Policy': 'J', - 'Current Logical Extents Associated': 'H', - 'Internal Logical Volume Number': 'E', - 'Logical Volume Access': 'C', - 'Logical Volume Name': 'A', - 'Logical Volume Size': 'G', - 'Logical Volume Status': 'D', - 'Major Device Number': 'L', - 'Minor Device Number': 'M', - 'Open Logical Volumes': 'F', - 'Read Ahead Sectors': 'K', - 'Volume Group Name': 'B'}}) + mock = MagicMock( + return_value={"retcode": 0, "stdout": "A:B:C:D:E:F:G:H:I:J:K:L:M"} + ) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual( + linux_lvm.lvdisplay(), + { + "A": { + "Allocated Logical Extents": "I", + "Allocation Policy": "J", + "Current Logical Extents Associated": "H", + "Internal Logical Volume Number": "E", + "Logical Volume Access": "C", + "Logical Volume Name": "A", + "Logical Volume Size": "G", + "Logical Volume Status": "D", + "Major Device Number": "L", + "Minor Device Number": "M", + "Open Logical Volumes": "F", + "Read Ahead Sectors": "K", + "Volume Group Name": "B", + } + }, + ) def test_pvcreate(self): - ''' + """ Tests for set a physical device to be used as an LVM physical volume - ''' - self.assertEqual(linux_lvm.pvcreate(''), - 'Error: at least one device is required') + """ + self.assertEqual( + linux_lvm.pvcreate(""), "Error: at least one device is required" + ) - self.assertRaises(CommandExecutionError, linux_lvm.pvcreate, 'A') + self.assertRaises(CommandExecutionError, linux_lvm.pvcreate, "A") # pvdisplay() would be called by pvcreate() twice: firstly to check # whether a device is already initialized for use by LVM and then to # ensure that the pvcreate executable did its job correctly. pvdisplay = MagicMock(side_effect=[False, True]) - with patch('salt.modules.linux_lvm.pvdisplay', pvdisplay): - with patch.object(os.path, 'exists', return_value=True): - ret = {'stdout': 'saltines', 'stderr': 'cheese', 'retcode': 0, 'pid': '1337'} + with patch("salt.modules.linux_lvm.pvdisplay", pvdisplay): + with patch.object(os.path, "exists", return_value=True): + ret = { + "stdout": "saltines", + "stderr": "cheese", + "retcode": 0, + "pid": "1337", + } mock = MagicMock(return_value=ret) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): - self.assertEqual(linux_lvm.pvcreate('A', metadatasize=1000), True) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): + self.assertEqual(linux_lvm.pvcreate("A", metadatasize=1000), True) def test_pvcreate_existing_pvs(self): - ''' + """ Test a scenario when all the submitted devices are already LVM PVs. - ''' + """ pvdisplay = MagicMock(return_value=True) - with patch('salt.modules.linux_lvm.pvdisplay', pvdisplay): - with patch.object(os.path, 'exists', return_value=True): - ret = {'stdout': 'saltines', 'stderr': 'cheese', 'retcode': 0, 'pid': '1337'} + with patch("salt.modules.linux_lvm.pvdisplay", pvdisplay): + with patch.object(os.path, "exists", return_value=True): + ret = { + "stdout": "saltines", + "stderr": "cheese", + "retcode": 0, + "pid": "1337", + } cmd_mock = MagicMock(return_value=ret) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': cmd_mock}): - self.assertEqual(linux_lvm.pvcreate('A', metadatasize=1000), - True) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": cmd_mock}): + self.assertEqual(linux_lvm.pvcreate("A", metadatasize=1000), True) self.assertTrue(cmd_mock.call_count == 0) def test_pvremove(self): - ''' + """ Tests for remove a physical device being used as an LVM physical volume - ''' + """ pvdisplay = MagicMock(return_value=False) - with patch('salt.modules.linux_lvm.pvdisplay', pvdisplay): - self.assertRaises(CommandExecutionError, linux_lvm.pvremove, 'A', override=False) + with patch("salt.modules.linux_lvm.pvdisplay", pvdisplay): + self.assertRaises( + CommandExecutionError, linux_lvm.pvremove, "A", override=False + ) pvdisplay = MagicMock(return_value=False) - with patch('salt.modules.linux_lvm.pvdisplay', pvdisplay): + with patch("salt.modules.linux_lvm.pvdisplay", pvdisplay): mock = MagicMock(return_value=True) - with patch.dict(linux_lvm.__salt__, {'lvm.pvdisplay': mock}): - ret = {'stdout': 'saltines', 'stderr': 'cheese', 'retcode': 0, 'pid': '1337'} + with patch.dict(linux_lvm.__salt__, {"lvm.pvdisplay": mock}): + ret = { + "stdout": "saltines", + "stderr": "cheese", + "retcode": 0, + "pid": "1337", + } mock = MagicMock(return_value=ret) - with patch.dict(linux_lvm.__salt__, {'cmd.run_all': mock}): - self.assertEqual(linux_lvm.pvremove('A'), True) + with patch.dict(linux_lvm.__salt__, {"cmd.run_all": mock}): + self.assertEqual(linux_lvm.pvremove("A"), True) def test_vgcreate(self): - ''' + """ Tests create an LVM volume group - ''' - self.assertEqual(linux_lvm.vgcreate('', ''), - 'Error: vgname and device(s) are both required') + """ + self.assertEqual( + linux_lvm.vgcreate("", ""), "Error: vgname and device(s) are both required" + ) - mock = MagicMock(return_value='A\nB') - with patch.dict(linux_lvm.__salt__, {'cmd.run': mock}): - with patch.object(linux_lvm, 'vgdisplay', return_value={}): - self.assertDictEqual(linux_lvm.vgcreate('A', 'B'), - {'Output from vgcreate': 'A'}) + mock = MagicMock(return_value="A\nB") + with patch.dict(linux_lvm.__salt__, {"cmd.run": mock}): + with patch.object(linux_lvm, "vgdisplay", return_value={}): + self.assertDictEqual( + linux_lvm.vgcreate("A", "B"), {"Output from vgcreate": "A"} + ) def test_vgextend(self): - ''' + """ Tests add physical volumes to an LVM volume group - ''' - self.assertEqual(linux_lvm.vgextend('', ''), - 'Error: vgname and device(s) are both required') + """ + self.assertEqual( + linux_lvm.vgextend("", ""), "Error: vgname and device(s) are both required" + ) - mock = MagicMock(return_value='A\nB') - with patch.dict(linux_lvm.__salt__, {'cmd.run': mock}): - with patch.object(linux_lvm, 'vgdisplay', return_value={}): - self.assertDictEqual(linux_lvm.vgextend('A', 'B'), - {'Output from vgextend': 'A'}) + mock = MagicMock(return_value="A\nB") + with patch.dict(linux_lvm.__salt__, {"cmd.run": mock}): + with patch.object(linux_lvm, "vgdisplay", return_value={}): + self.assertDictEqual( + linux_lvm.vgextend("A", "B"), {"Output from vgextend": "A"} + ) def test_lvcreate(self): - ''' + """ Test create a new logical volume, with option for which physical volume to be used - ''' - self.assertEqual(linux_lvm.lvcreate(None, None, 1, 1), - 'Error: Please specify only one of size or extents') + """ + self.assertEqual( + linux_lvm.lvcreate(None, None, 1, 1), + "Error: Please specify only one of size or extents", + ) - self.assertEqual(linux_lvm.lvcreate(None, None, None, None), - 'Error: Either size or extents must be specified') + self.assertEqual( + linux_lvm.lvcreate(None, None, None, None), + "Error: Either size or extents must be specified", + ) - self.assertEqual(linux_lvm.lvcreate(None, None, thinvolume=True, thinpool=True), - 'Error: Please set only one of thinvolume or thinpool to True') + self.assertEqual( + linux_lvm.lvcreate(None, None, thinvolume=True, thinpool=True), + "Error: Please set only one of thinvolume or thinpool to True", + ) - self.assertEqual(linux_lvm.lvcreate(None, None, thinvolume=True, extents=1), - 'Error: Thin volume size cannot be specified as extents') + self.assertEqual( + linux_lvm.lvcreate(None, None, thinvolume=True, extents=1), + "Error: Thin volume size cannot be specified as extents", + ) - mock = MagicMock(return_value='A\nB') - with patch.dict(linux_lvm.__salt__, {'cmd.run': mock}): - with patch.object(linux_lvm, 'lvdisplay', return_value={}): - self.assertDictEqual(linux_lvm.lvcreate(None, None, None, 1), - {'Output from lvcreate': 'A'}) + mock = MagicMock(return_value="A\nB") + with patch.dict(linux_lvm.__salt__, {"cmd.run": mock}): + with patch.object(linux_lvm, "lvdisplay", return_value={}): + self.assertDictEqual( + linux_lvm.lvcreate(None, None, None, 1), + {"Output from lvcreate": "A"}, + ) def test_lvcreate_with_force(self): - ''' + """ Test create a new logical volume, with option for which physical volume to be used - ''' - mock = MagicMock(return_value='A\nB') - with patch.dict(linux_lvm.__salt__, {'cmd.run': mock}): - with patch.object(linux_lvm, 'lvdisplay', return_value={}): - self.assertDictEqual(linux_lvm.lvcreate(None, - None, - None, - 1, - force=True), - {'Output from lvcreate': 'A'}) + """ + mock = MagicMock(return_value="A\nB") + with patch.dict(linux_lvm.__salt__, {"cmd.run": mock}): + with patch.object(linux_lvm, "lvdisplay", return_value={}): + self.assertDictEqual( + linux_lvm.lvcreate(None, None, None, 1, force=True), + {"Output from lvcreate": "A"}, + ) def test_vgremove(self): - ''' + """ Tests to remove an LVM volume group - ''' - mock = MagicMock(return_value='A') - with patch.dict(linux_lvm.__salt__, {'cmd.run': mock}): - self.assertEqual(linux_lvm.vgremove('A'), 'A') + """ + mock = MagicMock(return_value="A") + with patch.dict(linux_lvm.__salt__, {"cmd.run": mock}): + self.assertEqual(linux_lvm.vgremove("A"), "A") def test_lvremove(self): - ''' + """ Test to remove a given existing logical volume from a named existing volume group - ''' - mock = MagicMock(return_value='A') - with patch.dict(linux_lvm.__salt__, {'cmd.run': mock}): - self.assertEqual(linux_lvm.lvremove('', ''), 'A') + """ + mock = MagicMock(return_value="A") + with patch.dict(linux_lvm.__salt__, {"cmd.run": mock}): + self.assertEqual(linux_lvm.lvremove("", ""), "A") def test_lvresize(self): - ''' + """ Test to return information about the logical volume(s) - ''' - self.assertEqual(linux_lvm.lvresize(1, None, 1), - {}) + """ + self.assertEqual(linux_lvm.lvresize(1, None, 1), {}) - self.assertEqual(linux_lvm.lvresize(None, None, None), - {}) + self.assertEqual(linux_lvm.lvresize(None, None, None), {}) - mock = MagicMock(return_value='A') - with patch.dict(linux_lvm.__salt__, {'cmd.run': mock}): - self.assertDictEqual(linux_lvm.lvresize('A', 1), - {'Output from lvresize': 'A'}) + mock = MagicMock(return_value="A") + with patch.dict(linux_lvm.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + linux_lvm.lvresize("A", 1), {"Output from lvresize": "A"} + ) diff --git a/tests/unit/modules/test_linux_service.py b/tests/unit/modules/test_linux_service.py new file mode 100644 index 00000000000..fed84575875 --- /dev/null +++ b/tests/unit/modules/test_linux_service.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" + :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> +""" +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +import os + +# Import salt libs +import salt.modules.linux_service as service + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + + +class ServiceTestCase(TestCase, LoaderModuleMockMixin): + """ + Test cases for salt.modules.linux_service + """ + + def setup_loader_modules(self): + return {service: {}} + + def test_start(self): + """ + Test to start the specified service + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(service, "run", MagicMock(return_value=True)): + self.assertTrue(service.start("name")) + + def test_stop(self): + """ + Test to stop the specified service + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(service, "run", MagicMock(return_value=True)): + self.assertTrue(service.stop("name")) + + def test_restart(self): + """ + Test to restart the specified service + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(service, "run", MagicMock(return_value=True)): + self.assertTrue(service.restart("name")) + + def test_status(self): + """ + Test to return the status for a service, returns the PID or an empty + string if the service is running or not, pass a signature to use to + find the service via ps + """ + with patch.dict(service.__salt__, {"status.pid": MagicMock(return_value=True)}): + self.assertTrue(service.status("name")) + + def test_reload_(self): + """ + Test to restart the specified service + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(service, "run", MagicMock(return_value=True)): + self.assertTrue(service.reload_("name")) + + def test_run(self): + """ + Test to run the specified service + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(service, "run", MagicMock(return_value=True)): + self.assertTrue(service.run("name", "action")) + + def test_available(self): + """ + Test to returns ``True`` if the specified service is available, + otherwise returns ``False``. + """ + with patch.object(service, "get_all", return_value=["name", "A"]): + self.assertTrue(service.available("name")) + + def test_missing(self): + """ + Test to inverse of service.available. + """ + with patch.object(service, "get_all", return_value=["name1", "A"]): + self.assertTrue(service.missing("name")) + + def test_get_all(self): + """ + Test to return a list of all available services + """ + with patch.object(os.path, "isdir", side_effect=[False, True]): + + self.assertEqual(service.get_all(), []) + + with patch.object(os, "listdir", return_value=["A", "B"]): + self.assertListEqual(service.get_all(), ["A", "B"]) diff --git a/tests/unit/modules/test_shadow.py b/tests/unit/modules/test_linux_shadow.py similarity index 57% rename from tests/unit/modules/test_shadow.py rename to tests/unit/modules/test_linux_shadow.py index edb255f5931..40acc802269 100644 --- a/tests/unit/modules/test_shadow.py +++ b/tests/unit/modules/test_linux_shadow.py @@ -1,72 +1,67 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Erik Johnson <erik@saltstack.com> -''' +""" # Import Pytohn libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Testing libs import salt.utils.platform + +# Import 3rd-party libs +from salt.ext import six +from tests.support.helpers import skip_if_not_root from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase, skipIf -from tests.support.helpers import skip_if_not_root # Import salt libs try: - import salt.modules.shadow as shadow + import salt.modules.linux_shadow as shadow + HAS_SHADOW = True except ImportError: HAS_SHADOW = False -# Import 3rd-party libs -from salt.ext import six - -_PASSWORD = 'lamepassword' +_PASSWORD = "lamepassword" # Not testing blowfish as it is not available on most Linux distros _HASHES = dict( - md5=dict( - pw_salt='TgIp9OTu', - pw_hash='$1$TgIp9OTu$.d0FFP6jVi5ANoQmk6GpM1' - ), + md5=dict(pw_salt="TgIp9OTu", pw_hash="$1$TgIp9OTu$.d0FFP6jVi5ANoQmk6GpM1"), sha256=dict( - pw_salt='3vINbSrC', - pw_hash='$5$3vINbSrC$hH8A04jAY3bG123yU4FQ0wvP678QDTvWBhHHFbz6j0D' + pw_salt="3vINbSrC", + pw_hash="$5$3vINbSrC$hH8A04jAY3bG123yU4FQ0wvP678QDTvWBhHHFbz6j0D", ), sha512=dict( - pw_salt='PiGA3V2o', - pw_hash='$6$PiGA3V2o$/PrntRYufz49bRV/V5Eb1V6DdHaS65LB0fu73Tp/xxmDFr6HWJKptY2TvHRDViXZugWpnAcOnrbORpOgZUGTn.' + pw_salt="PiGA3V2o", + pw_hash="$6$PiGA3V2o$/PrntRYufz49bRV/V5Eb1V6DdHaS65LB0fu73Tp/xxmDFr6HWJKptY2TvHRDViXZugWpnAcOnrbORpOgZUGTn.", ), ) -@skipIf(not salt.utils.platform.is_linux(), 'minion is not Linux') -@skipIf(not HAS_SHADOW, 'shadow module is not available') +@skipIf(not salt.utils.platform.is_linux(), "minion is not Linux") +@skipIf(not HAS_SHADOW, "shadow module is not available") class LinuxShadowTest(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {shadow: {}} def test_gen_password(self): - ''' + """ Test shadow.gen_password - ''' + """ self.assertTrue(HAS_SHADOW) for algorithm, hash_info in six.iteritems(_HASHES): self.assertEqual( shadow.gen_password( - _PASSWORD, - crypt_salt=hash_info['pw_salt'], - algorithm=algorithm + _PASSWORD, crypt_salt=hash_info["pw_salt"], algorithm=algorithm ), - hash_info['pw_hash'] + hash_info["pw_hash"], ) @skip_if_not_root def test_list_users(self): - ''' + """ Test if it returns a list of all users - ''' + """ self.assertTrue(shadow.list_users()) diff --git a/tests/unit/modules/test_linux_sysctl.py b/tests/unit/modules/test_linux_sysctl.py index 49cfb0a22fe..90db8e36918 100644 --- a/tests/unit/modules/test_linux_sysctl.py +++ b/tests/unit/modules/test_linux_sysctl.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: jmoney <justin@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -13,135 +13,188 @@ from salt.exceptions import CommandExecutionError # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - mock_open, - patch, -) class LinuxSysctlTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.linux_sysctl module - ''' + """ def setup_loader_modules(self): return {linux_sysctl: {}, systemd: {}} def test_get(self): - ''' + """ Tests the return of get function - ''' + """ mock_cmd = MagicMock(return_value=1) - with patch.dict(linux_sysctl.__salt__, {'cmd.run': mock_cmd}): - self.assertEqual(linux_sysctl.get('net.ipv4.ip_forward'), 1) + with patch.dict(linux_sysctl.__salt__, {"cmd.run": mock_cmd}): + self.assertEqual(linux_sysctl.get("net.ipv4.ip_forward"), 1) + + def test_get_ignore(self): + """ + Tests the return of get function with ignore + """ + mock_cmd = MagicMock(return_value="") + with patch.dict(linux_sysctl.__salt__, {"cmd.run": mock_cmd}): + self.assertEqual(linux_sysctl.get("net.ipv4.ip_forward", ignore=True), "") def test_assign_proc_sys_failed(self): - ''' + """ Tests if /proc/sys/<kernel-subsystem> exists or not - ''' - with patch('os.path.exists', MagicMock(return_value=False)): - cmd = {'pid': 1337, 'retcode': 0, 'stderr': '', - 'stdout': 'net.ipv4.ip_forward = 1'} + """ + with patch("os.path.exists", MagicMock(return_value=False)): + cmd = { + "pid": 1337, + "retcode": 0, + "stderr": "", + "stdout": "net.ipv4.ip_forward = 1", + } mock_cmd = MagicMock(return_value=cmd) - with patch.dict(linux_sysctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertRaises(CommandExecutionError, - linux_sysctl.assign, - 'net.ipv4.ip_forward', 1) + with patch.dict(linux_sysctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertRaises( + CommandExecutionError, linux_sysctl.assign, "net.ipv4.ip_forward", 1 + ) def test_assign_cmd_failed(self): - ''' + """ Tests if the assignment was successful or not - ''' - with patch('os.path.exists', MagicMock(return_value=True)): - cmd = {'pid': 1337, 'retcode': 0, 'stderr': - 'sysctl: setting key "net.ipv4.ip_forward": Invalid argument', - 'stdout': 'net.ipv4.ip_forward = backward'} + """ + with patch("os.path.exists", MagicMock(return_value=True)): + cmd = { + "pid": 1337, + "retcode": 0, + "stderr": 'sysctl: setting key "net.ipv4.ip_forward": Invalid argument', + "stdout": "net.ipv4.ip_forward = backward", + } mock_cmd = MagicMock(return_value=cmd) - with patch.dict(linux_sysctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertRaises(CommandExecutionError, - linux_sysctl.assign, - 'net.ipv4.ip_forward', 'backward') + with patch.dict(linux_sysctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertRaises( + CommandExecutionError, + linux_sysctl.assign, + "net.ipv4.ip_forward", + "backward", + ) def test_assign_success(self): - ''' + """ Tests the return of successful assign function - ''' - with patch('os.path.exists', MagicMock(return_value=True)): - cmd = {'pid': 1337, 'retcode': 0, 'stderr': '', - 'stdout': 'net.ipv4.ip_forward = 1'} - ret = {'net.ipv4.ip_forward': '1'} + """ + with patch("os.path.exists", MagicMock(return_value=True)): + cmd = { + "pid": 1337, + "retcode": 0, + "stderr": "", + "stdout": "net.ipv4.ip_forward = 1", + } + ret = {"net.ipv4.ip_forward": "1"} mock_cmd = MagicMock(return_value=cmd) - with patch.dict(linux_sysctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertEqual(linux_sysctl.assign( - 'net.ipv4.ip_forward', 1), ret) + with patch.dict(linux_sysctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertEqual(linux_sysctl.assign("net.ipv4.ip_forward", 1), ret) + + def test_assign_ignore(self): + """ + Tests the ignore assign function + """ + with patch("os.path.exists", MagicMock(return_value=True)): + cmd = {"pid": 1337, "retcode": 0, "stderr": "", "stdout": ""} + ret = {"net.ipv4.ip_forward": "ignored"} + mock_cmd = MagicMock(return_value=cmd) + with patch.dict(linux_sysctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertEqual( + linux_sysctl.assign("net.ipv4.ip_forward", 1, ignore=True), ret + ) def test_persist_no_conf_failure(self): - ''' + """ Tests adding of config file failure - ''' - asn_cmd = {'pid': 1337, 'retcode': 0, - 'stderr': "sysctl: permission denied", 'stdout': ''} + """ + asn_cmd = { + "pid": 1337, + "retcode": 0, + "stderr": "sysctl: permission denied", + "stdout": "", + } mock_asn_cmd = MagicMock(return_value=asn_cmd) cmd = "sysctl -w net.ipv4.ip_forward=1" mock_cmd = MagicMock(return_value=cmd) - with patch.dict(linux_sysctl.__salt__, {'cmd.run_stdout': mock_cmd, - 'cmd.run_all': mock_asn_cmd}): - with patch('salt.utils.files.fopen', mock_open()) as m_open: - self.assertRaises(CommandExecutionError, - linux_sysctl.persist, - 'net.ipv4.ip_forward', - 1, config=None) + with patch.dict( + linux_sysctl.__salt__, + {"cmd.run_stdout": mock_cmd, "cmd.run_all": mock_asn_cmd}, + ): + with patch("salt.utils.files.fopen", mock_open()) as m_open: + self.assertRaises( + CommandExecutionError, + linux_sysctl.persist, + "net.ipv4.ip_forward", + 1, + config=None, + ) def test_persist_no_conf_success(self): - ''' + """ Tests successful add of config file when previously not one - ''' - config = '/etc/sysctl.conf' - with patch('os.path.isfile', MagicMock(return_value=False)), \ - patch('os.path.exists', MagicMock(return_value=True)): - asn_cmd = {'pid': 1337, 'retcode': 0, 'stderr': '', - 'stdout': 'net.ipv4.ip_forward = 1'} + """ + config = "/etc/sysctl.conf" + with patch("os.path.isfile", MagicMock(return_value=False)), patch( + "os.path.exists", MagicMock(return_value=True) + ): + asn_cmd = { + "pid": 1337, + "retcode": 0, + "stderr": "", + "stdout": "net.ipv4.ip_forward = 1", + } mock_asn_cmd = MagicMock(return_value=asn_cmd) - sys_cmd = 'systemd 208\n+PAM +LIBWRAP' + sys_cmd = "systemd 208\n+PAM +LIBWRAP" mock_sys_cmd = MagicMock(return_value=sys_cmd) - with patch('salt.utils.files.fopen', mock_open()) as m_open, \ - patch.dict(linux_sysctl.__context__, - {'salt.utils.systemd.version': 232}), \ - patch.dict(linux_sysctl.__salt__, - {'cmd.run_stdout': mock_sys_cmd, - 'cmd.run_all': mock_asn_cmd}), \ - patch.dict(systemd.__context__, - {'salt.utils.systemd.booted': True, - 'salt.utils.systemd.version': 232}): - linux_sysctl.persist('net.ipv4.ip_forward', 1, config=config) + with patch("salt.utils.files.fopen", mock_open()) as m_open, patch.dict( + linux_sysctl.__context__, {"salt.utils.systemd.version": 232} + ), patch.dict( + linux_sysctl.__salt__, + {"cmd.run_stdout": mock_sys_cmd, "cmd.run_all": mock_asn_cmd}, + ), patch.dict( + systemd.__context__, + {"salt.utils.systemd.booted": True, "salt.utils.systemd.version": 232}, + ): + linux_sysctl.persist("net.ipv4.ip_forward", 1, config=config) writes = m_open.write_calls() - assert writes == [ - '#\n# Kernel sysctl configuration\n#\n' - ], writes + assert writes == ["#\n# Kernel sysctl configuration\n#\n"], writes def test_persist_read_conf_success(self): - ''' + """ Tests sysctl.conf read success - ''' - with patch('os.path.isfile', MagicMock(return_value=True)), \ - patch('os.path.exists', MagicMock(return_value=True)): - asn_cmd = {'pid': 1337, 'retcode': 0, 'stderr': '', - 'stdout': 'net.ipv4.ip_forward = 1'} + """ + with patch("os.path.isfile", MagicMock(return_value=True)), patch( + "os.path.exists", MagicMock(return_value=True) + ): + asn_cmd = { + "pid": 1337, + "retcode": 0, + "stderr": "", + "stdout": "net.ipv4.ip_forward = 1", + } mock_asn_cmd = MagicMock(return_value=asn_cmd) - sys_cmd = 'systemd 208\n+PAM +LIBWRAP' + sys_cmd = "systemd 208\n+PAM +LIBWRAP" mock_sys_cmd = MagicMock(return_value=sys_cmd) - with patch('salt.utils.files.fopen', mock_open()): - with patch.dict(linux_sysctl.__context__, {'salt.utils.systemd.version': 232}): - with patch.dict(linux_sysctl.__salt__, - {'cmd.run_stdout': mock_sys_cmd, - 'cmd.run_all': mock_asn_cmd}): - with patch.dict(systemd.__context__, - {'salt.utils.systemd.booted': True}): - self.assertEqual(linux_sysctl.persist( - 'net.ipv4.ip_forward', 1), 'Updated') + with patch("salt.utils.files.fopen", mock_open()): + with patch.dict( + linux_sysctl.__context__, {"salt.utils.systemd.version": 232} + ): + with patch.dict( + linux_sysctl.__salt__, + {"cmd.run_stdout": mock_sys_cmd, "cmd.run_all": mock_asn_cmd}, + ): + with patch.dict( + systemd.__context__, {"salt.utils.systemd.booted": True} + ): + self.assertEqual( + linux_sysctl.persist("net.ipv4.ip_forward", 1), + "Updated", + ) diff --git a/tests/unit/modules/test_localemod.py b/tests/unit/modules/test_localemod.py index af77394bafe..b7a5955db63 100644 --- a/tests/unit/modules/test_localemod.py +++ b/tests/unit/modules/test_localemod.py @@ -1,651 +1,857 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - Mock, - patch, -) -try: - import pytest -except ImportError as import_error: - pytest = None - # Import Salt Libs import salt.modules.localemod as localemod from salt.exceptions import CommandExecutionError from salt.ext import six +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase, skipIf + +try: + import pytest +except ImportError as import_error: + pytest = None + @skipIf(not pytest, False) class LocalemodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.localemod - ''' - locale_ctl_out = ''' + """ + + locale_ctl_out = """ System Locale: LANG=de_DE.utf8 LANGUAGE=de_DE.utf8 VC Keymap: n/a X11 Layout: us X11 Model: pc105 - ''' - locale_ctl_notset = ''' + """ + locale_ctl_notset = """ System Locale: n/a VC Keymap: n/a X11 Layout: n/a X11 Model: n/a - ''' - locale_ctl_out_empty = '' - locale_ctl_out_broken = ''' + """ + locale_ctl_out_empty = "" + locale_ctl_out_broken = """ System error:Recursive traversal of loopback mount points - ''' - locale_ctl_out_structure = ''' + """ + locale_ctl_out_structure = """ Main: printers=We're upgrading /dev/null racks=hardware stress fractures failure=Ionisation from the air-conditioning Cow say: errors=We're out of slots on the server hardware=high pressure system failure Reason: The vendor put the bug there. - ''' + """ def setup_loader_modules(self): return {localemod: {}} def test_list_avail(self): - ''' + """ Test for Lists available (compiled) locales - ''' - with patch.dict(localemod.__salt__, - {'cmd.run': MagicMock(return_value='A\nB')}): - assert localemod.list_avail() == ['A', 'B'] + """ + with patch.dict( + localemod.__salt__, {"cmd.run": MagicMock(return_value="A\nB")} + ): + assert localemod.list_avail() == ["A", "B"] - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock(return_value=locale_ctl_out)}) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__salt__", + {"cmd.run": MagicMock(return_value=locale_ctl_out)}, + ) def test_localectl_status_parser(self): - ''' + """ Test localectl status parser. :return: - ''' + """ out = localemod._localectl_status() assert isinstance(out, dict) - for key in ['system_locale', 'vc_keymap', 'x11_layout', 'x11_model']: + for key in ["system_locale", "vc_keymap", "x11_layout", "x11_model"]: assert key in out - assert isinstance(out['system_locale'], dict) - assert 'LANG' in out['system_locale'] - assert 'LANGUAGE' in out['system_locale'] - assert out['system_locale']['LANG'] == out['system_locale']['LANGUAGE'] == 'de_DE.utf8' - assert isinstance(out['vc_keymap'], dict) - assert 'data' in out['vc_keymap'] - assert out['vc_keymap']['data'] is None - assert isinstance(out['x11_layout'], dict) - assert 'data' in out['x11_layout'] - assert out['x11_layout']['data'] == 'us' - assert isinstance(out['x11_model'], dict) - assert 'data' in out['x11_model'] - assert out['x11_model']['data'] == 'pc105' + assert isinstance(out["system_locale"], dict) + assert "LANG" in out["system_locale"] + assert "LANGUAGE" in out["system_locale"] + assert ( + out["system_locale"]["LANG"] + == out["system_locale"]["LANGUAGE"] + == "de_DE.utf8" + ) + assert isinstance(out["vc_keymap"], dict) + assert "data" in out["vc_keymap"] + assert out["vc_keymap"]["data"] is None + assert isinstance(out["x11_layout"], dict) + assert "data" in out["x11_layout"] + assert out["x11_layout"]["data"] == "us" + assert isinstance(out["x11_model"], dict) + assert "data" in out["x11_model"] + assert out["x11_model"]["data"] == "pc105" - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock(return_value=locale_ctl_notset)}) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__salt__", + {"cmd.run": MagicMock(return_value=locale_ctl_notset)}, + ) def test_localectl_status_parser_notset(self): - ''' + """ Test localectl status parser. :return: - ''' + """ out = localemod._localectl_status() assert isinstance(out, dict) - for key in ['system_locale', 'vc_keymap', 'x11_layout']: + for key in ["system_locale", "vc_keymap", "x11_layout"]: assert key in out - assert isinstance(out['system_locale'], dict) - assert 'data' in out['system_locale'] - assert out['system_locale']['data'] is None - assert isinstance(out['vc_keymap'], dict) - assert 'data' in out['vc_keymap'] - assert out['vc_keymap']['data'] is None - assert isinstance(out['x11_layout'], dict) - assert 'data' in out['x11_layout'] - assert out['x11_layout']['data'] is None + assert isinstance(out["system_locale"], dict) + assert "data" in out["system_locale"] + assert out["system_locale"]["data"] is None + assert isinstance(out["vc_keymap"], dict) + assert "data" in out["vc_keymap"] + assert out["vc_keymap"]["data"] is None + assert isinstance(out["x11_layout"], dict) + assert "data" in out["x11_layout"] + assert out["x11_layout"]["data"] is None - @patch('salt.modules.localemod.dbus', MagicMock()) + @patch("salt.modules.localemod.dbus", MagicMock()) def test_dbus_locale_parser_matches(self): - ''' + """ Test dbus locale status parser matching the results. :return: - ''' + """ i_dbus = MagicMock() - i_dbus.Get = MagicMock(return_value=['LANG=de_DE.utf8']) + i_dbus.Get = MagicMock(return_value=["LANG=de_DE.utf8"]) dbus = MagicMock(return_value=i_dbus) - with patch('salt.modules.localemod.dbus.Interface', dbus): + with patch("salt.modules.localemod.dbus.Interface", dbus): out = localemod._parse_dbus_locale() assert isinstance(out, dict) - assert 'LANG' in out - assert out['LANG'] == 'de_DE.utf8' + assert "LANG" in out + assert out["LANG"] == "de_DE.utf8" - @patch('salt.modules.localemod.dbus', MagicMock()) - @patch('salt.modules.localemod.log', MagicMock()) + @patch("salt.modules.localemod.dbus", MagicMock()) + @patch("salt.modules.localemod.log", MagicMock()) def test_dbus_locale_parser_doesnot_matches(self): - ''' + """ Test dbus locale status parser does not matching the results. :return: - ''' + """ i_dbus = MagicMock() - i_dbus.Get = MagicMock(return_value=['Fatal error right in front of screen']) + i_dbus.Get = MagicMock(return_value=["Fatal error right in front of screen"]) dbus = MagicMock(return_value=i_dbus) - with patch('salt.modules.localemod.dbus.Interface', dbus): + with patch("salt.modules.localemod.dbus.Interface", dbus): out = localemod._parse_dbus_locale() assert isinstance(out, dict) - assert 'LANG' not in out + assert "LANG" not in out assert localemod.log.error.called - msg = localemod.log.error.call_args[0][0] % localemod.log.error.call_args[0][1] - assert msg == ('Odd locale parameter "Fatal error right in front of screen" detected in dbus locale output.' - ' This should not happen. You should probably investigate what caused this.') + msg = ( + localemod.log.error.call_args[0][0] + % localemod.log.error.call_args[0][1] + ) + assert msg == ( + 'Odd locale parameter "Fatal error right in front of screen" detected in dbus locale output.' + " This should not happen. You should probably investigate what caused this." + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.log', MagicMock()) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch("salt.modules.localemod.log", MagicMock()) def test_localectl_status_parser_no_systemd(self): - ''' + """ Test localectl status parser raises an exception if no systemd installed. :return: - ''' + """ with pytest.raises(CommandExecutionError) as exc_info: localemod._localectl_status() assert 'Unable to find "localectl"' in six.text_type(exc_info.value) assert not localemod.log.debug.called - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock(return_value=locale_ctl_out_empty)}) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__salt__", + {"cmd.run": MagicMock(return_value=locale_ctl_out_empty)}, + ) def test_localectl_status_parser_empty(self): with pytest.raises(CommandExecutionError) as exc_info: localemod._localectl_status() assert 'Unable to parse result of "localectl"' in six.text_type(exc_info.value) - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock(return_value=locale_ctl_out_broken)}) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__salt__", + {"cmd.run": MagicMock(return_value=locale_ctl_out_broken)}, + ) def test_localectl_status_parser_broken(self): with pytest.raises(CommandExecutionError) as exc_info: localemod._localectl_status() assert 'Unable to parse result of "localectl"' in six.text_type(exc_info.value) - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock(return_value=locale_ctl_out_structure)}) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__salt__", + {"cmd.run": MagicMock(return_value=locale_ctl_out_structure)}, + ) def test_localectl_status_parser_structure(self): out = localemod._localectl_status() assert isinstance(out, dict) - for key in ['main', 'cow_say']: + for key in ["main", "cow_say"]: assert isinstance(out[key], dict) for in_key in out[key]: assert isinstance(out[key][in_key], six.text_type) - assert isinstance(out['reason']['data'], six.text_type) + assert isinstance(out["reason"]["data"], six.text_type) - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Ubuntu', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod._parse_dbus_locale', MagicMock(return_value={'LANG': 'en_US.utf8'})) - @patch('salt.modules.localemod._localectl_status', MagicMock(return_value={'system_locale': {'LANG': 'de_DE.utf8'}})) - @patch('salt.utils.systemd.booted', MagicMock(return_value=True)) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Ubuntu", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch( + "salt.modules.localemod._parse_dbus_locale", + MagicMock(return_value={"LANG": "en_US.utf8"}), + ) + @patch( + "salt.modules.localemod._localectl_status", + MagicMock(return_value={"system_locale": {"LANG": "de_DE.utf8"}}), + ) + @patch("salt.utils.systemd.booted", MagicMock(return_value=True)) def test_get_locale_with_systemd_nodbus(self): - ''' + """ Test getting current system locale with systemd but no dbus available. :return: - ''' - assert localemod.get_locale() == 'de_DE.utf8' + """ + assert localemod.get_locale() == "de_DE.utf8" - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Ubuntu', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', True) - @patch('salt.modules.localemod._parse_dbus_locale', MagicMock(return_value={'LANG': 'en_US.utf8'})) - @patch('salt.modules.localemod._localectl_status', MagicMock(return_value={'system_locale': {'LANG': 'de_DE.utf8'}})) - @patch('salt.utils.systemd.booted', MagicMock(return_value=True)) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Ubuntu", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", True) + @patch( + "salt.modules.localemod._parse_dbus_locale", + MagicMock(return_value={"LANG": "en_US.utf8"}), + ) + @patch( + "salt.modules.localemod._localectl_status", + MagicMock(return_value={"system_locale": {"LANG": "de_DE.utf8"}}), + ) + @patch("salt.utils.systemd.booted", MagicMock(return_value=True)) def test_get_locale_with_systemd_and_dbus(self): - ''' + """ Test getting current system locale with systemd and dbus available. :return: - ''' - assert localemod.get_locale() == 'en_US.utf8' + """ + assert localemod.get_locale() == "en_US.utf8" - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Suse', 'osmajorrelease': 12}) - @patch('salt.modules.localemod.dbus', True) - @patch('salt.modules.localemod._parse_dbus_locale', MagicMock(return_value={'LANG': 'en_US.utf8'})) - @patch('salt.modules.localemod._localectl_status', MagicMock(return_value={'system_locale': {'LANG': 'de_DE.utf8'}})) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock()}) - @patch('salt.utils.systemd.booted', MagicMock(return_value=True)) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__grains__", {"os_family": "Suse", "osmajorrelease": 12} + ) + @patch("salt.modules.localemod.dbus", True) + @patch( + "salt.modules.localemod._parse_dbus_locale", + MagicMock(return_value={"LANG": "en_US.utf8"}), + ) + @patch( + "salt.modules.localemod._localectl_status", + MagicMock(return_value={"system_locale": {"LANG": "de_DE.utf8"}}), + ) + @patch("salt.modules.localemod.__salt__", {"cmd.run": MagicMock()}) + @patch("salt.utils.systemd.booted", MagicMock(return_value=True)) def test_get_locale_with_systemd_and_dbus_sle12(self): - ''' + """ Test getting current system locale with systemd and dbus available on SLE12. :return: - ''' + """ localemod.get_locale() - assert localemod.__salt__['cmd.run'].call_args[0][0] == 'grep "^RC_LANG" /etc/sysconfig/language' + assert ( + localemod.__salt__["cmd.run"].call_args[0][0] + == 'grep "^RC_LANG" /etc/sysconfig/language' + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'RedHat', 'osmajorrelease': 12}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock()}) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "RedHat", "osmajorrelease": 12}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.modules.localemod.__salt__", {"cmd.run": MagicMock()}) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_get_locale_with_no_systemd_redhat(self): - ''' + """ Test getting current system locale with systemd and dbus available on RedHat. :return: - ''' + """ localemod.get_locale() - assert localemod.__salt__['cmd.run'].call_args[0][0] == 'grep "^LANG=" /etc/sysconfig/i18n' + assert ( + localemod.__salt__["cmd.run"].call_args[0][0] + == 'grep "^LANG=" /etc/sysconfig/i18n' + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Debian', 'osmajorrelease': 12}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock()}) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Debian", "osmajorrelease": 12}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.modules.localemod.__salt__", {"cmd.run": MagicMock()}) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_get_locale_with_no_systemd_debian(self): - ''' + """ Test getting current system locale with systemd and dbus available on Debian. :return: - ''' + """ localemod.get_locale() - assert localemod.__salt__['cmd.run'].call_args[0][0] == 'grep "^LANG=" /etc/default/locale' + assert ( + localemod.__salt__["cmd.run"].call_args[0][0] + == 'grep "^LANG=" /etc/default/locale' + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Gentoo', 'osmajorrelease': 12}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock()}) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Gentoo", "osmajorrelease": 12}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.modules.localemod.__salt__", {"cmd.run": MagicMock()}) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_get_locale_with_no_systemd_gentoo(self): - ''' + """ Test getting current system locale with systemd and dbus available on Gentoo. :return: - ''' + """ localemod.get_locale() - assert localemod.__salt__['cmd.run'].call_args[0][0] == 'eselect --brief locale show' + assert ( + localemod.__salt__["cmd.run"].call_args[0][0] + == "eselect --brief locale show" + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Solaris', 'osmajorrelease': 12}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock()}) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Solaris", "osmajorrelease": 12}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.modules.localemod.__salt__", {"cmd.run": MagicMock()}) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_get_locale_with_no_systemd_solaris(self): - ''' + """ Test getting current system locale with systemd and dbus available on Solaris. :return: - ''' + """ localemod.get_locale() - assert localemod.__salt__['cmd.run'].call_args[0][0] == 'grep "^LANG=" /etc/default/init' + assert ( + localemod.__salt__["cmd.run"].call_args[0][0] + == 'grep "^LANG=" /etc/default/init' + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'BSD', 'osmajorrelease': 8, 'oscodename': 'DrunkDragon'}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', {'cmd.run': MagicMock()}) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "BSD", "osmajorrelease": 8, "oscodename": "DrunkDragon"}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.modules.localemod.__salt__", {"cmd.run": MagicMock()}) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_get_locale_with_no_systemd_unknown(self): - ''' + """ Test getting current system locale with systemd and dbus available on Gentoo. :return: - ''' + """ with pytest.raises(CommandExecutionError) as exc_info: localemod.get_locale() assert '"DrunkDragon" is unsupported' in six.text_type(exc_info.value) - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Ubuntu', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.utils.systemd.booted', MagicMock(return_value=True)) - @patch('salt.modules.localemod._localectl_set', MagicMock()) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Ubuntu", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.utils.systemd.booted", MagicMock(return_value=True)) + @patch("salt.modules.localemod._localectl_set", MagicMock()) def test_set_locale_with_systemd_nodbus(self): - ''' + """ Test setting current system locale with systemd but no dbus available. :return: - ''' - loc = 'de_DE.utf8' + """ + loc = "de_DE.utf8" localemod.set_locale(loc) - assert localemod._localectl_set.call_args[0][0] == 'de_DE.utf8' + assert localemod._localectl_set.call_args[0][0] == "de_DE.utf8" - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Ubuntu', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', True) - @patch('salt.utils.systemd.booted', MagicMock(return_value=True)) - @patch('salt.modules.localemod._localectl_set', MagicMock()) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Ubuntu", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", True) + @patch("salt.utils.systemd.booted", MagicMock(return_value=True)) + @patch("salt.modules.localemod._localectl_set", MagicMock()) def test_set_locale_with_systemd_and_dbus(self): - ''' + """ Test setting current system locale with systemd and dbus available. :return: - ''' - loc = 'de_DE.utf8' + """ + loc = "de_DE.utf8" localemod.set_locale(loc) - assert localemod._localectl_set.call_args[0][0] == 'de_DE.utf8' + assert localemod._localectl_set.call_args[0][0] == "de_DE.utf8" - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Suse', 'osmajorrelease': 12}) - @patch('salt.modules.localemod.dbus', True) - @patch('salt.modules.localemod.__salt__', MagicMock()) - @patch('salt.modules.localemod._localectl_set', MagicMock()) - @patch('salt.utils.systemd.booted', MagicMock(return_value=True)) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) + @patch( + "salt.modules.localemod.__grains__", {"os_family": "Suse", "osmajorrelease": 12} + ) + @patch("salt.modules.localemod.dbus", True) + @patch("salt.modules.localemod.__salt__", MagicMock()) + @patch("salt.modules.localemod._localectl_set", MagicMock()) + @patch("salt.utils.systemd.booted", MagicMock(return_value=True)) def test_set_locale_with_systemd_and_dbus_sle12(self): - ''' + """ Test setting current system locale with systemd and dbus available on SLE12. :return: - ''' - loc = 'de_DE.utf8' + """ + loc = "de_DE.utf8" localemod.set_locale(loc) assert not localemod._localectl_set.called - assert localemod.__salt__['file.replace'].called - assert localemod.__salt__['file.replace'].call_args[0][0] == '/etc/sysconfig/language' - assert localemod.__salt__['file.replace'].call_args[0][1] == '^RC_LANG=.*' - assert localemod.__salt__['file.replace'].call_args[0][2] == 'RC_LANG="{}"'.format(loc) + assert localemod.__salt__["file.replace"].called + assert ( + localemod.__salt__["file.replace"].call_args[0][0] + == "/etc/sysconfig/language" + ) + assert localemod.__salt__["file.replace"].call_args[0][1] == "^RC_LANG=.*" + assert localemod.__salt__["file.replace"].call_args[0][ + 2 + ] == 'RC_LANG="{}"'.format(loc) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'RedHat', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', MagicMock()) - @patch('salt.modules.localemod._localectl_set', MagicMock()) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "RedHat", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.modules.localemod.__salt__", MagicMock()) + @patch("salt.modules.localemod._localectl_set", MagicMock()) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_set_locale_with_no_systemd_redhat(self): - ''' + """ Test setting current system locale with systemd and dbus available on RedHat. :return: - ''' - loc = 'de_DE.utf8' + """ + loc = "de_DE.utf8" localemod.set_locale(loc) assert not localemod._localectl_set.called - assert localemod.__salt__['file.replace'].called - assert localemod.__salt__['file.replace'].call_args[0][0] == '/etc/sysconfig/i18n' - assert localemod.__salt__['file.replace'].call_args[0][1] == '^LANG=.*' - assert localemod.__salt__['file.replace'].call_args[0][2] == 'LANG="{}"'.format(loc) + assert localemod.__salt__["file.replace"].called + assert ( + localemod.__salt__["file.replace"].call_args[0][0] == "/etc/sysconfig/i18n" + ) + assert localemod.__salt__["file.replace"].call_args[0][1] == "^LANG=.*" + assert localemod.__salt__["file.replace"].call_args[0][2] == 'LANG="{}"'.format( + loc + ) - @patch('salt.utils.path.which', MagicMock(return_value='/usr/sbin/update-locale')) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Debian', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', MagicMock()) - @patch('salt.modules.localemod._localectl_set', MagicMock()) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/sbin/update-locale")) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Debian", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.modules.localemod.__salt__", MagicMock()) + @patch("salt.modules.localemod._localectl_set", MagicMock()) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_set_locale_with_no_systemd_debian(self): - ''' + """ Test setting current system locale with systemd and dbus available on Debian. :return: - ''' - loc = 'de_DE.utf8' + """ + loc = "de_DE.utf8" localemod.set_locale(loc) assert not localemod._localectl_set.called - assert localemod.__salt__['file.replace'].called - assert localemod.__salt__['file.replace'].call_args[0][0] == '/etc/default/locale' - assert localemod.__salt__['file.replace'].call_args[0][1] == '^LANG=.*' - assert localemod.__salt__['file.replace'].call_args[0][2] == 'LANG="{}"'.format(loc) + assert localemod.__salt__["file.replace"].called + assert ( + localemod.__salt__["file.replace"].call_args[0][0] == "/etc/default/locale" + ) + assert localemod.__salt__["file.replace"].call_args[0][1] == "^LANG=.*" + assert localemod.__salt__["file.replace"].call_args[0][2] == 'LANG="{}"'.format( + loc + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Debian', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', MagicMock()) - @patch('salt.modules.localemod._localectl_set', MagicMock()) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Debian", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.modules.localemod.__salt__", MagicMock()) + @patch("salt.modules.localemod._localectl_set", MagicMock()) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_set_locale_with_no_systemd_debian_no_update_locale(self): - ''' + """ Test setting current system locale with systemd and dbus available on Debian but update-locale is not installed. :return: - ''' - loc = 'de_DE.utf8' + """ + loc = "de_DE.utf8" with pytest.raises(CommandExecutionError) as exc_info: localemod.set_locale(loc) assert not localemod._localectl_set.called - assert 'Cannot set locale: "update-locale" was not found.' in six.text_type(exc_info.value) + assert 'Cannot set locale: "update-locale" was not found.' in six.text_type( + exc_info.value + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Gentoo', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', MagicMock()) - @patch('salt.modules.localemod._localectl_set', MagicMock()) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Gentoo", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch("salt.modules.localemod.__salt__", MagicMock()) + @patch("salt.modules.localemod._localectl_set", MagicMock()) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_set_locale_with_no_systemd_gentoo(self): - ''' + """ Test setting current system locale with systemd and dbus available on Gentoo. :return: - ''' - loc = 'de_DE.utf8' + """ + loc = "de_DE.utf8" localemod.set_locale(loc) assert not localemod._localectl_set.called - assert localemod.__salt__['cmd.retcode'].call_args[0][0] == 'eselect --brief locale set de_DE.utf8' + assert ( + localemod.__salt__["cmd.retcode"].call_args[0][0] + == "eselect --brief locale set de_DE.utf8" + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Solaris', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', {'locale.list_avail': MagicMock(return_value=['de_DE.utf8']), - 'file.replace': MagicMock()}) - @patch('salt.modules.localemod._localectl_set', MagicMock()) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Solaris", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch( + "salt.modules.localemod.__salt__", + { + "locale.list_avail": MagicMock(return_value=["de_DE.utf8"]), + "file.replace": MagicMock(), + }, + ) + @patch("salt.modules.localemod._localectl_set", MagicMock()) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_set_locale_with_no_systemd_solaris_with_list_avail(self): - ''' + """ Test setting current system locale with systemd and dbus available on Solaris. The list_avail returns the proper locale. :return: - ''' - loc = 'de_DE.utf8' + """ + loc = "de_DE.utf8" localemod.set_locale(loc) assert not localemod._localectl_set.called - assert localemod.__salt__['file.replace'].called - assert localemod.__salt__['file.replace'].call_args[0][0] == '/etc/default/init' - assert localemod.__salt__['file.replace'].call_args[0][1] == '^LANG=.*' - assert localemod.__salt__['file.replace'].call_args[0][2] == 'LANG="{}"'.format(loc) + assert localemod.__salt__["file.replace"].called + assert localemod.__salt__["file.replace"].call_args[0][0] == "/etc/default/init" + assert localemod.__salt__["file.replace"].call_args[0][1] == "^LANG=.*" + assert localemod.__salt__["file.replace"].call_args[0][2] == 'LANG="{}"'.format( + loc + ) - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Solaris', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', {'locale.list_avail': MagicMock(return_value=['en_GB.utf8']), - 'file.replace': MagicMock()}) - @patch('salt.modules.localemod._localectl_set', MagicMock()) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", + {"os_family": "Solaris", "osmajorrelease": 42}, + ) + @patch("salt.modules.localemod.dbus", None) + @patch( + "salt.modules.localemod.__salt__", + { + "locale.list_avail": MagicMock(return_value=["en_GB.utf8"]), + "file.replace": MagicMock(), + }, + ) + @patch("salt.modules.localemod._localectl_set", MagicMock()) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_set_locale_with_no_systemd_solaris_without_list_avail(self): - ''' + """ Test setting current system locale with systemd and dbus is not available on Solaris. The list_avail does not return the proper locale. :return: - ''' - loc = 'de_DE.utf8' + """ + loc = "de_DE.utf8" assert not localemod.set_locale(loc) assert not localemod._localectl_set.called - assert not localemod.__salt__['file.replace'].called + assert not localemod.__salt__["file.replace"].called - @patch('salt.utils.path.which', MagicMock(return_value=None)) - @patch('salt.modules.localemod.__grains__', {'os_family': 'BSD', 'osmajorrelease': 42}) - @patch('salt.modules.localemod.dbus', None) - @patch('salt.modules.localemod.__salt__', {'locale.list_avail': MagicMock(return_value=['en_GB.utf8']), - 'file.replace': MagicMock()}) - @patch('salt.modules.localemod._localectl_set', MagicMock()) - @patch('salt.utils.systemd.booted', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=None)) + @patch( + "salt.modules.localemod.__grains__", {"os_family": "BSD", "osmajorrelease": 42} + ) + @patch("salt.modules.localemod.dbus", None) + @patch( + "salt.modules.localemod.__salt__", + { + "locale.list_avail": MagicMock(return_value=["en_GB.utf8"]), + "file.replace": MagicMock(), + }, + ) + @patch("salt.modules.localemod._localectl_set", MagicMock()) + @patch("salt.utils.systemd.booted", MagicMock(return_value=False)) def test_set_locale_with_no_systemd_unknown(self): - ''' + """ Test setting current system locale without systemd on unknown system. :return: - ''' + """ with pytest.raises(CommandExecutionError) as exc_info: - localemod.set_locale('de_DE.utf8') - assert 'Unsupported platform' in six.text_type(exc_info.value) + localemod.set_locale("de_DE.utf8") + assert "Unsupported platform" in six.text_type(exc_info.value) - @patch('salt.utils.locales.normalize_locale', MagicMock(return_value='en_US.UTF-8 UTF-8')) - @patch('salt.modules.localemod.__salt__', {'locale.list_avail': MagicMock(return_value=['A', 'B'])}) + @patch( + "salt.utils.locales.normalize_locale", + MagicMock(return_value="en_US.UTF-8 UTF-8"), + ) + @patch( + "salt.modules.localemod.__salt__", + {"locale.list_avail": MagicMock(return_value=["A", "B"])}, + ) def test_avail(self): - ''' + """ Test for Check if a locale is available - ''' - assert localemod.avail('locale') + """ + assert localemod.avail("locale") - @patch('salt.modules.localemod.log', MagicMock()) - @patch('salt.utils.path.which', MagicMock(return_value='/some/dir/path')) - @patch('salt.modules.localemod.__grains__', {'os': 'Debian'}) - @patch('salt.modules.localemod.__salt__', {'file.search': MagicMock(return_value=False)}) + @patch("salt.modules.localemod.log", MagicMock()) + @patch("salt.utils.path.which", MagicMock(return_value="/some/dir/path")) + @patch("salt.modules.localemod.__grains__", {"os": "Debian"}) + @patch( + "salt.modules.localemod.__salt__", + {"file.search": MagicMock(return_value=False)}, + ) def test_gen_locale_not_valid(self): - ''' + """ Tests the return of gen_locale when the provided locale is not found - ''' - assert not localemod.gen_locale('foo') + """ + assert not localemod.gen_locale("foo") assert localemod.log.error.called - msg = localemod.log.error.call_args[0][0] % (localemod.log.error.call_args[0][1], - localemod.log.error.call_args[0][2]) - assert msg == 'The provided locale "foo" is not found in /usr/share/i18n/SUPPORTED' + msg = localemod.log.error.call_args[0][0] % ( + localemod.log.error.call_args[0][1], + localemod.log.error.call_args[0][2], + ) + assert ( + msg == 'The provided locale "foo" is not found in /usr/share/i18n/SUPPORTED' + ) - @patch('salt.modules.localemod.log', MagicMock()) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Suse'}) - @patch('os.listdir', MagicMock(return_value=[])) - @patch('salt.utils.locales.join_locale', MagicMock(return_value='en_GB.utf8')) + @patch("salt.modules.localemod.log", MagicMock()) + @patch("salt.modules.localemod.__grains__", {"os_family": "Suse"}) + @patch("os.listdir", MagicMock(return_value=[])) + @patch("salt.utils.locales.join_locale", MagicMock(return_value="en_GB.utf8")) def test_gen_locale_suse_invalid(self): - ''' + """ Tests the location where gen_locale is searching for generated paths. :return: - ''' - assert not localemod.gen_locale('de_DE.utf8') + """ + assert not localemod.gen_locale("de_DE.utf8") assert localemod.log.error.called - msg = localemod.log.error.call_args[0][0] % (localemod.log.error.call_args[0][1], - localemod.log.error.call_args[0][2]) - assert localemod.os.listdir.call_args[0][0] == '/usr/share/locale' - assert msg == 'The provided locale "en_GB.utf8" is not found in /usr/share/locale' + msg = localemod.log.error.call_args[0][0] % ( + localemod.log.error.call_args[0][1], + localemod.log.error.call_args[0][2], + ) + assert localemod.os.listdir.call_args[0][0] == "/usr/share/locale" + assert ( + msg == 'The provided locale "en_GB.utf8" is not found in /usr/share/locale' + ) - @patch('salt.modules.localemod.log', MagicMock()) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Suse'}) - @patch('salt.modules.localemod.__salt__', {'cmd.run_all': MagicMock(return_value={'retcode': 0})}) - @patch('os.listdir', MagicMock(return_value=['de_DE'])) - @patch('os.path.exists', MagicMock(return_value=False)) - @patch('salt.utils.locales.join_locale', MagicMock(return_value='de_DE.utf8')) - @patch('salt.utils.path.which', MagicMock(side_effect=[None, '/usr/bin/localedef'])) + @patch("salt.modules.localemod.log", MagicMock()) + @patch("salt.modules.localemod.__grains__", {"os_family": "Suse"}) + @patch( + "salt.modules.localemod.__salt__", + {"cmd.run_all": MagicMock(return_value={"retcode": 0})}, + ) + @patch("os.listdir", MagicMock(return_value=["de_DE"])) + @patch("os.path.exists", MagicMock(return_value=False)) + @patch("salt.utils.locales.join_locale", MagicMock(return_value="de_DE.utf8")) + @patch("salt.utils.path.which", MagicMock(side_effect=[None, "/usr/bin/localedef"])) def test_gen_locale_suse_valid(self): - ''' + """ Tests the location where gen_locale is calling localedef on Suse os-family. :return: - ''' - localemod.gen_locale('de_DE.utf8') - assert localemod.__salt__['cmd.run_all'].call_args[0][0] == ['localedef', '--force', '-i', 'de_DE', - '-f', 'utf8', 'de_DE.utf8', '--quiet'] + """ + localemod.gen_locale("de_DE.utf8") + assert localemod.__salt__["cmd.run_all"].call_args[0][0] == [ + "localedef", + "--force", + "-i", + "de_DE", + "-f", + "utf8", + "de_DE.utf8", + "--quiet", + ] - @patch('salt.modules.localemod.log', MagicMock()) - @patch('salt.modules.localemod.__grains__', {'os_family': 'Suse'}) - @patch('salt.modules.localemod.__salt__', {'cmd.run_all': MagicMock(return_value={'retcode': 0})}) - @patch('os.listdir', MagicMock(return_value=['de_DE'])) - @patch('os.path.exists', MagicMock(return_value=False)) - @patch('salt.utils.locales.join_locale', MagicMock(return_value='de_DE.utf8')) - @patch('salt.utils.path.which', MagicMock(return_value=None)) + @patch("salt.modules.localemod.log", MagicMock()) + @patch("salt.modules.localemod.__grains__", {"os_family": "Suse"}) + @patch( + "salt.modules.localemod.__salt__", + {"cmd.run_all": MagicMock(return_value={"retcode": 0})}, + ) + @patch("os.listdir", MagicMock(return_value=["de_DE"])) + @patch("os.path.exists", MagicMock(return_value=False)) + @patch("salt.utils.locales.join_locale", MagicMock(return_value="de_DE.utf8")) + @patch("salt.utils.path.which", MagicMock(return_value=None)) def test_gen_locale_suse_localedef_error_handling(self): - ''' + """ Tests the location where gen_locale is handling error while calling not installed localedef on Suse os-family. :return: - ''' + """ with pytest.raises(CommandExecutionError) as exc_info: - localemod.gen_locale('de_DE.utf8') - assert 'Command "locale-gen" or "localedef" was not found on this system.' in six.text_type(exc_info.value) + localemod.gen_locale("de_DE.utf8") + assert ( + 'Command "locale-gen" or "localedef" was not found on this system.' + in six.text_type(exc_info.value) + ) def test_gen_locale_debian(self): - ''' + """ Tests the return of successful gen_locale on Debian system - ''' - ret = {'stdout': 'saltines', 'stderr': 'biscuits', 'retcode': 0, 'pid': 1337} - with patch.dict(localemod.__grains__, {'os': 'Debian'}), \ - patch('salt.utils.path.which', MagicMock(return_value='/some/dir/path')), \ - patch.dict(localemod.__salt__, - {'file.search': MagicMock(return_value=True), - 'file.replace': MagicMock(return_value=True), - 'cmd.run_all': MagicMock(return_value=ret)}): - assert localemod.gen_locale('en_US.UTF-8 UTF-8') + """ + ret = {"stdout": "saltines", "stderr": "biscuits", "retcode": 0, "pid": 1337} + with patch.dict(localemod.__grains__, {"os": "Debian"}), patch( + "salt.utils.path.which", MagicMock(return_value="/some/dir/path") + ), patch.dict( + localemod.__salt__, + { + "file.search": MagicMock(return_value=True), + "file.replace": MagicMock(return_value=True), + "cmd.run_all": MagicMock(return_value=ret), + }, + ): + assert localemod.gen_locale("en_US.UTF-8 UTF-8") def test_gen_locale_debian_no_charmap(self): - ''' + """ Tests the return of successful gen_locale on Debian system without a charmap - ''' - ret = {'stdout': 'saltines', 'stderr': 'biscuits', 'retcode': 0, 'pid': 1337} - with patch.dict(localemod.__grains__, {'os': 'Debian'}), \ - patch('salt.utils.path.which', MagicMock(return_value='/some/dir/path')), \ - patch.dict(localemod.__salt__, - {'file.search': lambda s, p, flags: not len(p.split()) == 1, - 'file.replace': MagicMock(return_value=True), - 'cmd.run_all': MagicMock(return_value=ret)}): - assert localemod.gen_locale('en_US.UTF-8') + """ + ret = {"stdout": "saltines", "stderr": "biscuits", "retcode": 0, "pid": 1337} + with patch.dict(localemod.__grains__, {"os": "Debian"}), patch( + "salt.utils.path.which", MagicMock(return_value="/some/dir/path") + ), patch.dict( + localemod.__salt__, + { + "file.search": lambda s, p, flags: not len(p.split()) == 1, + "file.replace": MagicMock(return_value=True), + "cmd.run_all": MagicMock(return_value=ret), + }, + ): + assert localemod.gen_locale("en_US.UTF-8") def test_gen_locale_ubuntu(self): - ''' + """ Test the return of successful gen_locale on Ubuntu system - ''' - ret = {'stdout': 'saltines', 'stderr': 'biscuits', 'retcode': 0, 'pid': 1337} - with patch.dict(localemod.__salt__, - {'file.replace': MagicMock(return_value=True), - 'file.touch': MagicMock(return_value=None), - 'file.append': MagicMock(return_value=None), - 'cmd.run_all': MagicMock(return_value=ret)}), \ - patch('salt.utils.path.which', MagicMock(return_value='/some/dir/path')), \ - patch('os.listdir', MagicMock(return_value=['en_US'])), \ - patch.dict(localemod.__grains__, {'os': 'Ubuntu'}): - assert localemod.gen_locale('en_US.UTF-8') + """ + ret = {"stdout": "saltines", "stderr": "biscuits", "retcode": 0, "pid": 1337} + with patch.dict( + localemod.__salt__, + { + "file.replace": MagicMock(return_value=True), + "file.touch": MagicMock(return_value=None), + "file.append": MagicMock(return_value=None), + "cmd.run_all": MagicMock(return_value=ret), + }, + ), patch( + "salt.utils.path.which", MagicMock(return_value="/some/dir/path") + ), patch( + "os.listdir", MagicMock(return_value=["en_US"]) + ), patch.dict( + localemod.__grains__, {"os": "Ubuntu"} + ): + assert localemod.gen_locale("en_US.UTF-8") def test_gen_locale_gentoo(self): - ''' + """ Tests the return of successful gen_locale on Gentoo system - ''' - ret = {'stdout': 'saltines', 'stderr': 'biscuits', 'retcode': 0, 'pid': 1337} - with patch.dict(localemod.__grains__, {'os_family': 'Gentoo'}), \ - patch('salt.utils.path.which', MagicMock(return_value='/some/dir/path')), \ - patch('os.listdir', MagicMock(return_value=['en_US.UTF-8'])), \ - patch.dict(localemod.__salt__, - {'file.search': MagicMock(return_value=True), - 'file.replace': MagicMock(return_value=True), - 'cmd.run_all': MagicMock(return_value=ret)}): - assert localemod.gen_locale('en_US.UTF-8 UTF-8') + """ + ret = {"stdout": "saltines", "stderr": "biscuits", "retcode": 0, "pid": 1337} + with patch.dict(localemod.__grains__, {"os_family": "Gentoo"}), patch( + "salt.utils.path.which", MagicMock(return_value="/some/dir/path") + ), patch("os.listdir", MagicMock(return_value=["en_US.UTF-8"])), patch.dict( + localemod.__salt__, + { + "file.search": MagicMock(return_value=True), + "file.replace": MagicMock(return_value=True), + "cmd.run_all": MagicMock(return_value=ret), + }, + ): + assert localemod.gen_locale("en_US.UTF-8 UTF-8") def test_gen_locale_gentoo_no_charmap(self): - ''' + """ Tests the return of successful gen_locale on Gentoo system without a charmap - ''' + """ + def file_search(search, pattern, flags): - ''' + """ mock file.search - ''' + """ if len(pattern.split()) == 1: return False else: # charmap was supplied return True - ret = {'stdout': 'saltines', 'stderr': 'biscuits', 'retcode': 0, 'pid': 1337} - with patch.dict(localemod.__grains__, {'os_family': 'Gentoo'}), \ - patch('salt.utils.path.which', MagicMock(return_value='/some/dir/path')), \ - patch('os.listdir', MagicMock(return_value=['en_US.UTF-8'])), \ - patch.dict(localemod.__salt__, - {'file.search': file_search, - 'file.replace': MagicMock(return_value=True), - 'cmd.run_all': MagicMock(return_value=ret)}): - assert localemod.gen_locale('en_US.UTF-8') + ret = {"stdout": "saltines", "stderr": "biscuits", "retcode": 0, "pid": 1337} + with patch.dict(localemod.__grains__, {"os_family": "Gentoo"}), patch( + "salt.utils.path.which", MagicMock(return_value="/some/dir/path") + ), patch("os.listdir", MagicMock(return_value=["en_US.UTF-8"])), patch.dict( + localemod.__salt__, + { + "file.search": file_search, + "file.replace": MagicMock(return_value=True), + "cmd.run_all": MagicMock(return_value=ret), + }, + ): + assert localemod.gen_locale("en_US.UTF-8") def test_gen_locale(self): - ''' + """ Tests the return of successful gen_locale - ''' - ret = {'stdout': 'saltines', 'stderr': 'biscuits', 'retcode': 0, 'pid': 1337} - with patch.dict(localemod.__salt__, - {'cmd.run_all': MagicMock(return_value=ret), - 'file.replace': MagicMock()}), \ - patch('salt.utils.path.which', MagicMock(return_value='/some/dir/path')), \ - patch('os.listdir', MagicMock(return_value=['en_US'])): - assert localemod.gen_locale('en_US.UTF-8') + """ + ret = {"stdout": "saltines", "stderr": "biscuits", "retcode": 0, "pid": 1337} + with patch.dict( + localemod.__salt__, + {"cmd.run_all": MagicMock(return_value=ret), "file.replace": MagicMock()}, + ), patch( + "salt.utils.path.which", MagicMock(return_value="/some/dir/path") + ), patch( + "os.listdir", MagicMock(return_value=["en_US"]) + ): + assert localemod.gen_locale("en_US.UTF-8") def test_gen_locale_verbose(self): - ''' + """ Tests the return of successful gen_locale - ''' - ret = {'stdout': 'saltines', 'stderr': 'biscuits', 'retcode': 0, 'pid': 1337} - with patch.dict(localemod.__salt__, - {'cmd.run_all': MagicMock(return_value=ret), - 'file.replace': MagicMock()}), \ - patch('salt.utils.path.which', MagicMock(return_value='/some/dir/path')), \ - patch('os.listdir', MagicMock(return_value=['en_US'])): - assert localemod.gen_locale('en_US.UTF-8', verbose=True) == ret + """ + ret = {"stdout": "saltines", "stderr": "biscuits", "retcode": 0, "pid": 1337} + with patch.dict( + localemod.__salt__, + {"cmd.run_all": MagicMock(return_value=ret), "file.replace": MagicMock()}, + ), patch( + "salt.utils.path.which", MagicMock(return_value="/some/dir/path") + ), patch( + "os.listdir", MagicMock(return_value=["en_US"]) + ): + assert localemod.gen_locale("en_US.UTF-8", verbose=True) == ret - @patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/localctl")) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/localctl")) def test_parse_localectl(self): - localectl_out = (' System Locale: LANG=en_US.UTF-8\n' - ' LANGUAGE=en_US:en\n' - ' VC Keymap: n/a') + localectl_out = ( + " System Locale: LANG=en_US.UTF-8\n" + " LANGUAGE=en_US:en\n" + " VC Keymap: n/a" + ) mock_cmd = Mock(return_value=localectl_out) - with patch.dict(localemod.__salt__, {'cmd.run': mock_cmd}): - ret = localemod._localectl_status()['system_locale'] - assert {'LANG': 'en_US.UTF-8', 'LANGUAGE': 'en_US:en'} == ret + with patch.dict(localemod.__salt__, {"cmd.run": mock_cmd}): + ret = localemod._localectl_status()["system_locale"] + assert {"LANG": "en_US.UTF-8", "LANGUAGE": "en_US:en"} == ret diff --git a/tests/unit/modules/test_locate.py b/tests/unit/modules/test_locate.py index a7c6e48882f..072b3e61204 100644 --- a/tests/unit/modules/test_locate.py +++ b/tests/unit/modules/test_locate.py @@ -1,78 +1,77 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.locate as locate +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LocateTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.locate - ''' + """ + def setup_loader_modules(self): return {locate: {}} # 'version' function tests: 1 def test_version(self): - ''' + """ Test if it returns the version of locate - ''' - mock = MagicMock(return_value='mlocate 0.26') - with patch.dict(locate.__salt__, {'cmd.run': mock}): - self.assertListEqual(locate.version(), ['mlocate 0.26']) + """ + mock = MagicMock(return_value="mlocate 0.26") + with patch.dict(locate.__salt__, {"cmd.run": mock}): + self.assertListEqual(locate.version(), ["mlocate 0.26"]) # 'stats' function tests: 1 def test_stats(self): - ''' + """ Test if it returns statistics about the locate database - ''' - ret = {'files': '75,253', - 'directories': '49,252', - 'bytes in file names': '93,214', - 'bytes used to store database': '29,165', - 'database': '/var/lib/mlocate/mlocate.db'} + """ + ret = { + "files": "75,253", + "directories": "49,252", + "bytes in file names": "93,214", + "bytes used to store database": "29,165", + "database": "/var/lib/mlocate/mlocate.db", + } - mock_ret = '''Database /var/lib/mlocate/mlocate.db: + mock_ret = """Database /var/lib/mlocate/mlocate.db: 49,252 directories 75,253 files 93,214 bytes in file names - 29,165 bytes used to store database''' + 29,165 bytes used to store database""" - with patch.dict(locate.__salt__, - {'cmd.run': MagicMock(return_value=mock_ret)}): + with patch.dict(locate.__salt__, {"cmd.run": MagicMock(return_value=mock_ret)}): self.assertDictEqual(locate.stats(), ret) # 'updatedb' function tests: 1 def test_updatedb(self): - ''' + """ Test if it updates the locate database - ''' - mock = MagicMock(return_value='') - with patch.dict(locate.__salt__, {'cmd.run': mock}): + """ + mock = MagicMock(return_value="") + with patch.dict(locate.__salt__, {"cmd.run": mock}): self.assertListEqual(locate.updatedb(), []) # 'locate' function tests: 1 def test_locate(self): - ''' + """ Test if it performs a file lookup. - ''' - mock = MagicMock(return_value='') - with patch.dict(locate.__salt__, {'cmd.run': mock}): - self.assertListEqual(locate.locate('wholename', database='myfile'), []) + """ + mock = MagicMock(return_value="") + with patch.dict(locate.__salt__, {"cmd.run": mock}): + self.assertListEqual(locate.locate("wholename", database="myfile"), []) diff --git a/tests/unit/modules/test_logadm.py b/tests/unit/modules/test_logadm.py index 0329ddcd77f..765a5168992 100644 --- a/tests/unit/modules/test_logadm.py +++ b/tests/unit/modules/test_logadm.py @@ -1,70 +1,73 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.logadm as logadm +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LogadmTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.logadm - ''' + """ + def setup_loader_modules(self): return {logadm: {}} def test_show_conf(self): - ''' + """ Test for Show parsed configuration - ''' - with patch.object(logadm, '_parse_conf', return_value=True): - self.assertTrue(logadm.show_conf('conf_file')) + """ + with patch.object(logadm, "_parse_conf", return_value=True): + self.assertTrue(logadm.show_conf("conf_file")) def test_rotate(self): - ''' + """ Test for Set up pattern for logging. - ''' - with patch.dict(logadm.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 1, - 'stderr': 'stderr'})}): - self.assertEqual(logadm.rotate('name'), - {'Output': 'stderr', - 'Error': 'Failed in adding log'}) + """ + with patch.dict( + logadm.__salt__, + {"cmd.run_all": MagicMock(return_value={"retcode": 1, "stderr": "stderr"})}, + ): + self.assertEqual( + logadm.rotate("name"), + {"Output": "stderr", "Error": "Failed in adding log"}, + ) - with patch.dict(logadm.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 0, - 'stderr': 'stderr'})}): - self.assertEqual(logadm.rotate('name'), {'Result': 'Success'}) + with patch.dict( + logadm.__salt__, + {"cmd.run_all": MagicMock(return_value={"retcode": 0, "stderr": "stderr"})}, + ): + self.assertEqual(logadm.rotate("name"), {"Result": "Success"}) def test_remove(self): - ''' + """ Test for Remove log pattern from logadm - ''' - with patch.dict(logadm.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 1, - 'stderr': 'stderr'})}): - self.assertEqual(logadm.remove('name'), - {'Output': 'stderr', - 'Error': 'Failure in removing log. Possibly\ - already removed?'}) + """ + with patch.dict( + logadm.__salt__, + {"cmd.run_all": MagicMock(return_value={"retcode": 1, "stderr": "stderr"})}, + ): + self.assertEqual( + logadm.remove("name"), + { + "Output": "stderr", + "Error": "Failure in removing log. Possibly\ + already removed?", + }, + ) - with patch.dict(logadm.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 0, - 'stderr': 'stderr'})}): - self.assertEqual(logadm.remove('name'), {'Result': 'Success'}) + with patch.dict( + logadm.__salt__, + {"cmd.run_all": MagicMock(return_value={"retcode": 0, "stderr": "stderr"})}, + ): + self.assertEqual(logadm.remove("name"), {"Result": "Success"}) diff --git a/tests/unit/modules/test_logrotate.py b/tests/unit/modules/test_logrotate.py index c38b4ca7634..88338fa2124 100644 --- a/tests/unit/modules/test_logrotate.py +++ b/tests/unit/modules/test_logrotate.py @@ -1,87 +1,85 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import salt.modules.logrotate as logrotate + # Import Salt Libs from salt.exceptions import SaltInvocationError -import salt.modules.logrotate as logrotate # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) PARSE_CONF = { - 'include files': { - 'rsyslog': ['/var/log/syslog'] - }, - 'rotate': 1, - '/var/log/wtmp': { - 'rotate': 1 - } + "include files": {"rsyslog": ["/var/log/syslog"]}, + "rotate": 1, + "/var/log/wtmp": {"rotate": 1}, } class LogrotateTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.logrotate - ''' + """ + def setup_loader_modules(self): return {logrotate: {}} # 'show_conf' function tests: 1 def test_show_conf(self): - ''' + """ Test if it show parsed configuration - ''' - with patch('salt.modules.logrotate._parse_conf', - MagicMock(return_value=True)): + """ + with patch("salt.modules.logrotate._parse_conf", MagicMock(return_value=True)): self.assertTrue(logrotate.show_conf()) # 'set_' function tests: 4 def test_set(self): - ''' + """ Test if it set a new value for a specific configuration line - ''' - with patch('salt.modules.logrotate._parse_conf', - MagicMock(return_value=PARSE_CONF)), \ - patch.dict(logrotate.__salt__, - {'file.replace': MagicMock(return_value=True)}): - self.assertTrue(logrotate.set_('rotate', '2')) + """ + with patch( + "salt.modules.logrotate._parse_conf", MagicMock(return_value=PARSE_CONF) + ), patch.dict( + logrotate.__salt__, {"file.replace": MagicMock(return_value=True)} + ): + self.assertTrue(logrotate.set_("rotate", "2")) def test_set_failed(self): - ''' + """ Test if it fails to set a new value for a specific configuration line - ''' - with patch('salt.modules.logrotate._parse_conf', MagicMock(return_value=PARSE_CONF)): - kwargs = {'key': '/var/log/wtmp', 'value': 2} + """ + with patch( + "salt.modules.logrotate._parse_conf", MagicMock(return_value=PARSE_CONF) + ): + kwargs = {"key": "/var/log/wtmp", "value": 2} self.assertRaises(SaltInvocationError, logrotate.set_, **kwargs) def test_set_setting(self): - ''' + """ Test if it set a new value for a specific configuration line - ''' - with patch.dict(logrotate.__salt__, - {'file.replace': MagicMock(return_value=True)}), \ - patch('salt.modules.logrotate._parse_conf', - MagicMock(return_value=PARSE_CONF)): - self.assertTrue(logrotate.set_('/var/log/wtmp', 'rotate', '2')) + """ + with patch.dict( + logrotate.__salt__, {"file.replace": MagicMock(return_value=True)} + ), patch( + "salt.modules.logrotate._parse_conf", MagicMock(return_value=PARSE_CONF) + ): + self.assertTrue(logrotate.set_("/var/log/wtmp", "rotate", "2")) def test_set_setting_failed(self): - ''' + """ Test if it fails to set a new value for a specific configuration line - ''' - with patch('salt.modules.logrotate._parse_conf', MagicMock(return_value=PARSE_CONF)): - kwargs = {'key': 'rotate', - 'value': '/var/log/wtmp', - 'setting': '2'} + """ + with patch( + "salt.modules.logrotate._parse_conf", MagicMock(return_value=PARSE_CONF) + ): + kwargs = {"key": "rotate", "value": "/var/log/wtmp", "setting": "2"} self.assertRaises(SaltInvocationError, logrotate.set_, **kwargs) diff --git a/tests/unit/modules/test_lvs.py b/tests/unit/modules/test_lvs.py index 7c14b5cea4f..cb72b0c9668 100644 --- a/tests/unit/modules/test_lvs.py +++ b/tests/unit/modules/test_lvs.py @@ -1,194 +1,214 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.lvs as lvs +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LvsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.lvs - ''' + """ + def setup_loader_modules(self): return {lvs: {}} def test_add_service(self): - ''' + """ Test for Add a virtual service. - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - self.assertEqual(lvs.add_service(), 'stderr') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + self.assertEqual(lvs.add_service(), "stderr") def test_edit_service(self): - ''' + """ Test for Edit the virtual service. - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - self.assertEqual(lvs.edit_service(), 'stderr') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + self.assertEqual(lvs.edit_service(), "stderr") def test_delete_service(self): - ''' + """ Test for Delete the virtual service. - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - self.assertEqual(lvs.delete_service(), 'stderr') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + self.assertEqual(lvs.delete_service(), "stderr") def test_add_server(self): - ''' + """ Test for Add a real server to a virtual service. - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - self.assertEqual(lvs.add_server(), 'stderr') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + self.assertEqual(lvs.add_server(), "stderr") def test_edit_server(self): - ''' + """ Test for Edit a real server to a virtual service. - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - self.assertEqual(lvs.edit_server(), 'stderr') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + self.assertEqual(lvs.edit_server(), "stderr") def test_delete_server(self): - ''' + """ Test for Delete the realserver from the virtual service. - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - self.assertEqual(lvs.delete_server(), 'stderr') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + self.assertEqual(lvs.delete_server(), "stderr") def test_clear(self): - ''' + """ Test for Clear the virtual server table - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - self.assertEqual(lvs.clear(), 'stderr') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + self.assertEqual(lvs.clear(), "stderr") def test_get_rules(self): - ''' + """ Test for Get the virtual server rules - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.dict(lvs.__salt__, - {'cmd.run': - MagicMock(return_value='A')}): - self.assertEqual(lvs.get_rules(), 'A') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.dict(lvs.__salt__, {"cmd.run": MagicMock(return_value="A")}): + self.assertEqual(lvs.get_rules(), "A") def test_list_(self): - ''' + """ Test for List the virtual server table - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - self.assertEqual(lvs.list_('p', 's'), 'stderr') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + self.assertEqual(lvs.list_("p", "s"), "stderr") def test_zero(self): - ''' + """ Test for Zero the packet, byte and rate counters in a service or all services. - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - self.assertEqual(lvs.zero('p', 's'), 'stderr') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + self.assertEqual(lvs.zero("p", "s"), "stderr") def test_check_service(self): - ''' + """ Test for Check the virtual service exists. - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - with patch.object(lvs, 'get_rules', return_value='C'): - self.assertEqual(lvs.check_service('p', 's'), - 'Error: service not exists') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + with patch.object(lvs, "get_rules", return_value="C"): + self.assertEqual( + lvs.check_service("p", "s"), "Error: service not exists" + ) def test_check_server(self): - ''' + """ Test for Check the real server exists in the specified service. - ''' - with patch.object(lvs, '__detect_os', return_value='C'): - with patch.object(lvs, '_build_cmd', return_value='B'): - with patch.dict(lvs.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': - 'ret', - 'stderr': - 'stderr'})}): - with patch.object(lvs, 'get_rules', return_value='C'): - self.assertEqual(lvs.check_server('p', 's'), - 'Error: server not exists') + """ + with patch.object(lvs, "__detect_os", return_value="C"): + with patch.object(lvs, "_build_cmd", return_value="B"): + with patch.dict( + lvs.__salt__, + { + "cmd.run_all": MagicMock( + return_value={"retcode": "ret", "stderr": "stderr"} + ) + }, + ): + with patch.object(lvs, "get_rules", return_value="C"): + self.assertEqual( + lvs.check_server("p", "s"), "Error: server not exists" + ) diff --git a/tests/unit/modules/test_mac_assistive.py b/tests/unit/modules/test_mac_assistive.py index 131bb42bf6a..512152cf90e 100644 --- a/tests/unit/modules/test_mac_assistive.py +++ b/tests/unit/modules/test_mac_assistive.py @@ -1,140 +1,146 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import salt.modules.mac_assistive as assistive # Import Salt Libs from salt.exceptions import CommandExecutionError -import salt.modules.mac_assistive as assistive + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class AssistiveTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {assistive: {}} def test_install_assistive_bundle(self): - ''' + """ Test installing a bundle ID as being allowed to run with assistive access - ''' - mock_ret = MagicMock(return_value={'retcode': 0}) - with patch.dict(assistive.__salt__, {'cmd.run_all': mock_ret}): - with patch.dict(assistive.__grains__, {'osrelease': '10.11.3'}): - self.assertTrue(assistive.install('foo')) + """ + mock_ret = MagicMock(return_value={"retcode": 0}) + with patch.dict(assistive.__salt__, {"cmd.run_all": mock_ret}): + with patch.dict(assistive.__grains__, {"osrelease": "10.11.3"}): + self.assertTrue(assistive.install("foo")) def test_install_assistive_error(self): - ''' + """ Test installing a bundle ID as being allowed to run with assistive access - ''' - mock_ret = MagicMock(return_value={'retcode': 1}) - with patch.dict(assistive.__salt__, {'cmd.run_all': mock_ret}): - with patch.dict(assistive.__grains__, {'osrelease': '10.11.3'}): - self.assertRaises(CommandExecutionError, assistive.install, 'foo') + """ + mock_ret = MagicMock(return_value={"retcode": 1}) + with patch.dict(assistive.__salt__, {"cmd.run_all": mock_ret}): + with patch.dict(assistive.__grains__, {"osrelease": "10.11.3"}): + self.assertRaises(CommandExecutionError, assistive.install, "foo") def test_installed_bundle(self): - ''' + """ Test checking to see if a bundle id is installed as being able to use assistive access - ''' - with patch('salt.modules.mac_assistive._get_assistive_access', - MagicMock(return_value=[('foo', 0)])): - self.assertTrue(assistive.installed('foo')) + """ + with patch( + "salt.modules.mac_assistive._get_assistive_access", + MagicMock(return_value=[("foo", 0)]), + ): + self.assertTrue(assistive.installed("foo")) def test_installed_bundle_not(self): - ''' + """ Test checking to see if a bundle id is installed as being able to use assistive access - ''' - with patch('salt.modules.mac_assistive._get_assistive_access', - MagicMock(return_value=[])): - self.assertFalse(assistive.installed('foo')) + """ + with patch( + "salt.modules.mac_assistive._get_assistive_access", + MagicMock(return_value=[]), + ): + self.assertFalse(assistive.installed("foo")) def test_enable_assistive(self): - ''' + """ Test enabling a bundle ID as being allowed to run with assistive access - ''' - mock_ret = MagicMock(return_value={'retcode': 0}) - with patch.dict(assistive.__salt__, {'cmd.run_all': mock_ret}), \ - patch('salt.modules.mac_assistive._get_assistive_access', - MagicMock(return_value=[('foo', 0)])): - self.assertTrue(assistive.enable('foo', True)) + """ + mock_ret = MagicMock(return_value={"retcode": 0}) + with patch.dict(assistive.__salt__, {"cmd.run_all": mock_ret}), patch( + "salt.modules.mac_assistive._get_assistive_access", + MagicMock(return_value=[("foo", 0)]), + ): + self.assertTrue(assistive.enable("foo", True)) def test_enable_error(self): - ''' + """ Test enabled a bundle ID that throws a command error - ''' - mock_ret = MagicMock(return_value={'retcode': 1}) - with patch.dict(assistive.__salt__, {'cmd.run_all': mock_ret}), \ - patch('salt.modules.mac_assistive._get_assistive_access', - MagicMock(return_value=[('foo', 0)])): - self.assertRaises(CommandExecutionError, - assistive.enable, - 'foo') + """ + mock_ret = MagicMock(return_value={"retcode": 1}) + with patch.dict(assistive.__salt__, {"cmd.run_all": mock_ret}), patch( + "salt.modules.mac_assistive._get_assistive_access", + MagicMock(return_value=[("foo", 0)]), + ): + self.assertRaises(CommandExecutionError, assistive.enable, "foo") def test_enable_false(self): - ''' + """ Test return of enable function when app isn't found. - ''' - with patch('salt.modules.mac_assistive._get_assistive_access', - MagicMock(return_value=[])): - self.assertFalse(assistive.enable('foo')) + """ + with patch( + "salt.modules.mac_assistive._get_assistive_access", + MagicMock(return_value=[]), + ): + self.assertFalse(assistive.enable("foo")) def test_enabled_assistive(self): - ''' + """ Test enabling a bundle ID as being allowed to run with assistive access - ''' - with patch('salt.modules.mac_assistive._get_assistive_access', - MagicMock(return_value=[('foo', '1')])): - self.assertTrue(assistive.enabled('foo')) + """ + with patch( + "salt.modules.mac_assistive._get_assistive_access", + MagicMock(return_value=[("foo", "1")]), + ): + self.assertTrue(assistive.enabled("foo")) def test_enabled_assistive_false(self): - ''' + """ Test if a bundle ID is disabled for assistive access - ''' - with patch('salt.modules.mac_assistive._get_assistive_access', - MagicMock(return_value=[])): - self.assertFalse(assistive.enabled('foo')) + """ + with patch( + "salt.modules.mac_assistive._get_assistive_access", + MagicMock(return_value=[]), + ): + self.assertFalse(assistive.enabled("foo")) def test_remove_assistive(self): - ''' + """ Test removing an assitive bundle. - ''' - mock_ret = MagicMock(return_value={'retcode': 0}) - with patch.dict(assistive.__salt__, {'cmd.run_all': mock_ret}): - self.assertTrue(assistive.remove('foo')) + """ + mock_ret = MagicMock(return_value={"retcode": 0}) + with patch.dict(assistive.__salt__, {"cmd.run_all": mock_ret}): + self.assertTrue(assistive.remove("foo")) def test_remove_assistive_error(self): - ''' + """ Test removing an assitive bundle. - ''' - mock_ret = MagicMock(return_value={'retcode': 1}) - with patch.dict(assistive.__salt__, {'cmd.run_all': mock_ret}): - self.assertRaises(CommandExecutionError, - assistive.remove, - 'foo') + """ + mock_ret = MagicMock(return_value={"retcode": 1}) + with patch.dict(assistive.__salt__, {"cmd.run_all": mock_ret}): + self.assertRaises(CommandExecutionError, assistive.remove, "foo") def test_get_assistive_access(self): - ''' + """ Test if a bundle ID is enabled for assistive access - ''' - mock_out = 'kTCCServiceAccessibility|/bin/bash|1|1|1|\n' \ - 'kTCCServiceAccessibility|/usr/bin/osascript|1|1|1|' - mock_ret = MagicMock(return_value={'retcode': 0, 'stdout': mock_out}) - expected = [('/bin/bash', '1'), ('/usr/bin/osascript', '1')] - with patch.dict(assistive.__salt__, {'cmd.run_all': mock_ret}): + """ + mock_out = ( + "kTCCServiceAccessibility|/bin/bash|1|1|1|\n" + "kTCCServiceAccessibility|/usr/bin/osascript|1|1|1|" + ) + mock_ret = MagicMock(return_value={"retcode": 0, "stdout": mock_out}) + expected = [("/bin/bash", "1"), ("/usr/bin/osascript", "1")] + with patch.dict(assistive.__salt__, {"cmd.run_all": mock_ret}): self.assertEqual(assistive._get_assistive_access(), expected) def test_get_assistive_access_error(self): - ''' + """ Test a CommandExecutionError is raised when something goes wrong. - ''' - mock_ret = MagicMock(return_value={'retcode': 1}) - with patch.dict(assistive.__salt__, {'cmd.run_all': mock_ret}): - self.assertRaises(CommandExecutionError, - assistive._get_assistive_access) + """ + mock_ret = MagicMock(return_value={"retcode": 1}) + with patch.dict(assistive.__salt__, {"cmd.run_all": mock_ret}): + self.assertRaises(CommandExecutionError, assistive._get_assistive_access) diff --git a/tests/unit/modules/test_mac_brew_pkg.py b/tests/unit/modules/test_mac_brew_pkg.py index 45ab5649ef8..d596d575862 100644 --- a/tests/unit/modules/test_mac_brew_pkg.py +++ b/tests/unit/modules/test_mac_brew_pkg.py @@ -1,155 +1,174 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.mac_brew_pkg as mac_brew -from salt.exceptions import CommandExecutionError import salt.utils.pkg +from salt.exceptions import CommandExecutionError # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase -TAPS_STRING = 'homebrew/dupes\nhomebrew/science\nhomebrew/x11' -TAPS_LIST = ['homebrew/dupes', 'homebrew/science', 'homebrew/x11'] -HOMEBREW_BIN = '/usr/local/bin/brew' +TAPS_STRING = "homebrew/dupes\nhomebrew/science\nhomebrew/x11" +TAPS_LIST = ["homebrew/dupes", "homebrew/science", "homebrew/x11"] +HOMEBREW_BIN = "/usr/local/bin/brew" class BrewTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.mac_brew module - ''' + """ def setup_loader_modules(self): - return {mac_brew: {'__opts__': {'user': MagicMock(return_value='bar')}}} + return {mac_brew: {"__opts__": {"user": MagicMock(return_value="bar")}}} # '_list_taps' function tests: 1 def test_list_taps(self): - ''' + """ Tests the return of the list of taps - ''' - mock_taps = MagicMock(return_value={'stdout': TAPS_STRING, - 'retcode': 0}) - mock_user = MagicMock(return_value='foo') - mock_cmd = MagicMock(return_value='') - with patch.dict(mac_brew.__salt__, {'file.get_user': mock_user, - 'cmd.run_all': mock_taps, - 'cmd.run': mock_cmd}): + """ + mock_taps = MagicMock(return_value={"stdout": TAPS_STRING, "retcode": 0}) + mock_user = MagicMock(return_value="foo") + mock_cmd = MagicMock(return_value="") + with patch.dict( + mac_brew.__salt__, + {"file.get_user": mock_user, "cmd.run_all": mock_taps, "cmd.run": mock_cmd}, + ): self.assertEqual(mac_brew._list_taps(), TAPS_LIST) # '_tap' function tests: 3 def test_tap_installed(self): - ''' + """ Tests if tap argument is already installed or not - ''' - with patch('salt.modules.mac_brew_pkg._list_taps', MagicMock(return_value=TAPS_LIST)): - self.assertTrue(mac_brew._tap('homebrew/science')) + """ + with patch( + "salt.modules.mac_brew_pkg._list_taps", MagicMock(return_value=TAPS_LIST) + ): + self.assertTrue(mac_brew._tap("homebrew/science")) def test_tap_failure(self): - ''' + """ Tests if the tap installation failed - ''' - mock_failure = MagicMock(return_value={'stdout': '', - 'stderr': '', - 'retcode': 1}) - mock_user = MagicMock(return_value='foo') - mock_cmd = MagicMock(return_value='') - with patch.dict(mac_brew.__salt__, {'cmd.run_all': mock_failure, - 'file.get_user': mock_user, - 'cmd.run': mock_cmd}), \ - patch('salt.modules.mac_brew_pkg._list_taps', MagicMock(return_value={})): - self.assertFalse(mac_brew._tap('homebrew/test')) + """ + mock_failure = MagicMock( + return_value={"stdout": "", "stderr": "", "retcode": 1} + ) + mock_user = MagicMock(return_value="foo") + mock_cmd = MagicMock(return_value="") + with patch.dict( + mac_brew.__salt__, + { + "cmd.run_all": mock_failure, + "file.get_user": mock_user, + "cmd.run": mock_cmd, + }, + ), patch("salt.modules.mac_brew_pkg._list_taps", MagicMock(return_value={})): + self.assertFalse(mac_brew._tap("homebrew/test")) def test_tap(self): - ''' + """ Tests adding unofficial GitHub repos to the list of brew taps - ''' - mock_failure = MagicMock(return_value={'retcode': 0}) - mock_user = MagicMock(return_value='foo') - mock_cmd = MagicMock(return_value='') - with patch.dict(mac_brew.__salt__, {'cmd.run_all': mock_failure, - 'file.get_user': mock_user, - 'cmd.run': mock_cmd}), \ - patch('salt.modules.mac_brew_pkg._list_taps', MagicMock(return_value=TAPS_LIST)): - self.assertTrue(mac_brew._tap('homebrew/test')) + """ + mock_failure = MagicMock(return_value={"retcode": 0}) + mock_user = MagicMock(return_value="foo") + mock_cmd = MagicMock(return_value="") + with patch.dict( + mac_brew.__salt__, + { + "cmd.run_all": mock_failure, + "file.get_user": mock_user, + "cmd.run": mock_cmd, + }, + ), patch( + "salt.modules.mac_brew_pkg._list_taps", MagicMock(return_value=TAPS_LIST) + ): + self.assertTrue(mac_brew._tap("homebrew/test")) # '_homebrew_bin' function tests: 1 def test_homebrew_bin(self): - ''' + """ Tests the path to the homebrew binary - ''' - mock_path = MagicMock(return_value='/usr/local') - with patch.dict(mac_brew.__salt__, {'cmd.run': mock_path}): - self.assertEqual(mac_brew._homebrew_bin(), '/usr/local/bin/brew') + """ + mock_path = MagicMock(return_value="/usr/local") + with patch.dict(mac_brew.__salt__, {"cmd.run": mock_path}): + self.assertEqual(mac_brew._homebrew_bin(), "/usr/local/bin/brew") # 'list_pkgs' function tests: 2 # Only tested a few basics # Full functionality should be tested in integration phase def test_list_pkgs_removed(self): - ''' + """ Tests removed implementation - ''' + """ self.assertEqual(mac_brew.list_pkgs(removed=True), {}) def test_list_pkgs_versions_true(self): - ''' + """ Tests if pkg.list_pkgs is already in context and is a list - ''' - mock_context = {'foo': ['bar']} - with patch.dict(mac_brew.__context__, {'pkg.list_pkgs': mock_context}): - self.assertEqual(mac_brew.list_pkgs(versions_as_list=True), - mock_context) + """ + mock_context = {"foo": ["bar"]} + with patch.dict(mac_brew.__context__, {"pkg.list_pkgs": mock_context}): + self.assertEqual(mac_brew.list_pkgs(versions_as_list=True), mock_context) def test_list_pkgs_homebrew_cask_pakages(self): - ''' + """ Tests if pkg.list_pkgs list properly homebrew cask packages - ''' + """ def custom_call_brew(cmd, failhard=True): result = dict() - if cmd == 'info --json=v1 --installed': - result = {'stdout': '[{"name":"zsh","full_name":"zsh","oldname":null,' - '"aliases":[],"homepage":"https://www.zsh.org/",' - '"versions":{"stable":"5.7.1","devel":null,"head":"HEAD","bottle":true},' - '"installed":[{"version":"5.7.1","used_options":[],' - '"built_as_bottle":true,"poured_from_bottle":true,' - '"runtime_dependencies":[{"full_name":"ncurses","version":"6.1"},' - '{"full_name":"pcre","version":"8.42"}],' - '"installed_as_dependency":false,"installed_on_request":true}]}]', - 'stderr': '', - 'retcode': 0} - elif cmd == 'cask list --versions': - result = {'stdout': 'macvim 8.1.151\nfont-firacode-nerd-font 2.0.0', - 'stderr': '', - 'retcode': 0} - elif cmd == 'cask info macvim': - result = {'stdout': 'macvim: 8.1.1517,156 (auto_updates)\n' - 'https://github.com/macvim-dev/macvim\n' - '/usr/local/Caskroom/macvim/8.1.151 (64B)\n' - 'From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/macvim.rb\n' - '==> Name\n' - 'MacVim', - 'stderr': '', - 'retcode': 0} - elif cmd == 'cask info font-firacode-nerd-font': - result = {'stdout': 'font-firacode-nerd-font: 2.0.0\n' - 'https://github.com/ryanoasis/nerd-fonts\n' - '/usr/local/Caskroom/font-firacode-nerd-font/2.0.0 (35 files, 64.8MB)\n' - 'From: https://github.com/Homebrew/homebrew-cask-fonts/blob/master/Casks/font-firacode-nerd-font.rb\n' - '==> Name\n' - 'FuraCode Nerd Font (FiraCode)', - 'stderr': '', - 'retcode': ''} + if cmd == "info --json=v1 --installed": + result = { + "stdout": '[{"name":"zsh","full_name":"zsh","oldname":null,' + '"aliases":[],"homepage":"https://www.zsh.org/",' + '"versions":{"stable":"5.7.1","devel":null,"head":"HEAD","bottle":true},' + '"installed":[{"version":"5.7.1","used_options":[],' + '"built_as_bottle":true,"poured_from_bottle":true,' + '"runtime_dependencies":[{"full_name":"ncurses","version":"6.1"},' + '{"full_name":"pcre","version":"8.42"}],' + '"installed_as_dependency":false,"installed_on_request":true}]}]', + "stderr": "", + "retcode": 0, + } + elif cmd == "cask list --versions": + result = { + "stdout": "macvim 8.1.151\nfont-firacode-nerd-font 2.0.0", + "stderr": "", + "retcode": 0, + } + elif cmd == "cask info macvim": + result = { + "stdout": "macvim: 8.1.1517,156 (auto_updates)\n" + "https://github.com/macvim-dev/macvim\n" + "/usr/local/Caskroom/macvim/8.1.151 (64B)\n" + "From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/macvim.rb\n" + "==> Name\n" + "MacVim", + "stderr": "", + "retcode": 0, + } + elif cmd == "cask info font-firacode-nerd-font": + result = { + "stdout": "font-firacode-nerd-font: 2.0.0\n" + "https://github.com/ryanoasis/nerd-fonts\n" + "/usr/local/Caskroom/font-firacode-nerd-font/2.0.0 (35 files, 64.8MB)\n" + "From: https://github.com/Homebrew/homebrew-cask-fonts/blob/master/Casks/font-firacode-nerd-font.rb\n" + "==> Name\n" + "FuraCode Nerd Font (FiraCode)", + "stderr": "", + "retcode": "", + } return result @@ -157,25 +176,32 @@ class BrewTestCase(TestCase, LoaderModuleMockMixin): ret[name] = newest_version return ret - expected_pkgs = {'zsh': '5.7.1', - 'homebrew/cask/macvim': '8.1.151', - 'homebrew/cask-fonts/font-firacode-nerd-font': '2.0.0'} + expected_pkgs = { + "zsh": "5.7.1", + "homebrew/cask/macvim": "8.1.151", + "homebrew/cask-fonts/font-firacode-nerd-font": "2.0.0", + } - with patch('salt.modules.mac_brew_pkg._call_brew', custom_call_brew),\ - patch.dict(mac_brew.__salt__, {'pkg_resource.add_pkg': custom_add_pkg, - 'pkg_resource.sort_pkglist': MagicMock()}): - self.assertEqual(mac_brew.list_pkgs(versions_as_list=True), - expected_pkgs) + with patch( + "salt.modules.mac_brew_pkg._call_brew", custom_call_brew + ), patch.dict( + mac_brew.__salt__, + { + "pkg_resource.add_pkg": custom_add_pkg, + "pkg_resource.sort_pkglist": MagicMock(), + }, + ): + self.assertEqual(mac_brew.list_pkgs(versions_as_list=True), expected_pkgs) # 'version' function tests: 1 def test_version(self): - ''' + """ Tests version name returned - ''' - mock_version = MagicMock(return_value='0.1.5') - with patch.dict(mac_brew.__salt__, {'pkg_resource.version': mock_version}): - self.assertEqual(mac_brew.version('foo'), '0.1.5') + """ + mock_version = MagicMock(return_value="0.1.5") + with patch.dict(mac_brew.__salt__, {"pkg_resource.version": mock_version}): + self.assertEqual(mac_brew.version("foo"), "0.1.5") # 'latest_version' function tests: 0 # It has not been fully implemented @@ -185,42 +211,47 @@ class BrewTestCase(TestCase, LoaderModuleMockMixin): # Full functionality should be tested in integration phase def test_remove(self): - ''' + """ Tests if package to be removed exists - ''' - mock_params = MagicMock(return_value=({'foo': None}, 'repository')) - with patch('salt.modules.mac_brew_pkg.list_pkgs', return_value={'test': '0.1.5'}), \ - patch.dict(mac_brew.__salt__, {'pkg_resource.parse_targets': mock_params}): - self.assertEqual(mac_brew.remove('foo'), {}) + """ + mock_params = MagicMock(return_value=({"foo": None}, "repository")) + with patch( + "salt.modules.mac_brew_pkg.list_pkgs", return_value={"test": "0.1.5"} + ), patch.dict(mac_brew.__salt__, {"pkg_resource.parse_targets": mock_params}): + self.assertEqual(mac_brew.remove("foo"), {}) # 'refresh_db' function tests: 2 def test_refresh_db_failure(self): - ''' + """ Tests an update of homebrew package repository failure - ''' - mock_user = MagicMock(return_value='foo') - mock_failure = MagicMock(return_value={'stdout': '', - 'stderr': '', - 'retcode': 1}) - with patch.dict(mac_brew.__salt__, {'file.get_user': mock_user, - 'cmd.run_all': mock_failure}), \ - patch('salt.modules.mac_brew_pkg._homebrew_bin', - MagicMock(return_value=HOMEBREW_BIN)): - with patch.object(salt.utils.pkg, 'clear_rtag', Mock()): + """ + mock_user = MagicMock(return_value="foo") + mock_failure = MagicMock( + return_value={"stdout": "", "stderr": "", "retcode": 1} + ) + with patch.dict( + mac_brew.__salt__, {"file.get_user": mock_user, "cmd.run_all": mock_failure} + ), patch( + "salt.modules.mac_brew_pkg._homebrew_bin", + MagicMock(return_value=HOMEBREW_BIN), + ): + with patch.object(salt.utils.pkg, "clear_rtag", Mock()): self.assertRaises(CommandExecutionError, mac_brew.refresh_db) def test_refresh_db(self): - ''' + """ Tests a successful update of homebrew package repository - ''' - mock_user = MagicMock(return_value='foo') - mock_success = MagicMock(return_value={'retcode': 0}) - with patch.dict(mac_brew.__salt__, {'file.get_user': mock_user, - 'cmd.run_all': mock_success}), \ - patch('salt.modules.mac_brew_pkg._homebrew_bin', - MagicMock(return_value=HOMEBREW_BIN)): - with patch.object(salt.utils.pkg, 'clear_rtag', Mock()): + """ + mock_user = MagicMock(return_value="foo") + mock_success = MagicMock(return_value={"retcode": 0}) + with patch.dict( + mac_brew.__salt__, {"file.get_user": mock_user, "cmd.run_all": mock_success} + ), patch( + "salt.modules.mac_brew_pkg._homebrew_bin", + MagicMock(return_value=HOMEBREW_BIN), + ): + with patch.object(salt.utils.pkg, "clear_rtag", Mock()): self.assertTrue(mac_brew.refresh_db()) # 'install' function tests: 1 @@ -228,10 +259,9 @@ class BrewTestCase(TestCase, LoaderModuleMockMixin): # Full functionality should be tested in integration phase def test_install(self): - ''' + """ Tests if package to be installed exists - ''' + """ mock_params = MagicMock(return_value=[None, None]) - with patch.dict(mac_brew.__salt__, - {'pkg_resource.parse_targets': mock_params}): - self.assertEqual(mac_brew.install('name=foo'), {}) + with patch.dict(mac_brew.__salt__, {"pkg_resource.parse_targets": mock_params}): + self.assertEqual(mac_brew.install("name=foo"), {}) diff --git a/tests/unit/modules/test_mac_desktop.py b/tests/unit/modules/test_mac_desktop.py index 3b1c71ffc78..d1ddaf40745 100644 --- a/tests/unit/modules/test_mac_desktop.py +++ b/tests/unit/modules/test_mac_desktop.py @@ -1,125 +1,119 @@ # -*- coding: utf-8 -*- -''' +""" Unit Tests for the mac_desktop execution module. -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.mac_desktop as mac_desktop from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MacDesktopTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.mac_desktop - ''' + """ + def setup_loader_modules(self): return {mac_desktop: {}} # 'get_output_volume' function tests: 2 def test_get_output_volume(self): - ''' + """ Test if it get the output volume (range 0 to 100) - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': '25'}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}): - self.assertEqual(mac_desktop.get_output_volume(), '25') + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": "25"}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}): + self.assertEqual(mac_desktop.get_output_volume(), "25") def test_get_output_volume_error(self): - ''' + """ Tests that an error is raised when cmd.run_all errors - ''' - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, - mac_desktop.get_output_volume) + """ + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}): + self.assertRaises(CommandExecutionError, mac_desktop.get_output_volume) # 'set_output_volume' function tests: 2 def test_set_output_volume(self): - ''' + """ Test if it set the volume of sound (range 0 to 100) - ''' - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}), \ - patch('salt.modules.mac_desktop.get_output_volume', - MagicMock(return_value='25')): - self.assertTrue(mac_desktop.set_output_volume('25')) + """ + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}), patch( + "salt.modules.mac_desktop.get_output_volume", MagicMock(return_value="25") + ): + self.assertTrue(mac_desktop.set_output_volume("25")) def test_set_output_volume_error(self): - ''' + """ Tests that an error is raised when cmd.run_all errors - ''' - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, - mac_desktop.set_output_volume, - '25') + """ + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}): + self.assertRaises( + CommandExecutionError, mac_desktop.set_output_volume, "25" + ) # 'screensaver' function tests: 2 def test_screensaver(self): - ''' + """ Test if it launch the screensaver - ''' - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}): self.assertTrue(mac_desktop.screensaver()) def test_screensaver_error(self): - ''' + """ Tests that an error is raised when cmd.run_all errors - ''' - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, - mac_desktop.screensaver) + """ + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}): + self.assertRaises(CommandExecutionError, mac_desktop.screensaver) # 'lock' function tests: 2 def test_lock(self): - ''' + """ Test if it lock the desktop session - ''' - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}): self.assertTrue(mac_desktop.lock()) def test_lock_error(self): - ''' + """ Tests that an error is raised when cmd.run_all errors - ''' - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, - mac_desktop.lock) + """ + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}): + self.assertRaises(CommandExecutionError, mac_desktop.lock) # 'say' function tests: 2 def test_say(self): - ''' + """ Test if it says some words. - ''' - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}): self.assertTrue(mac_desktop.say()) def test_say_error(self): - ''' + """ Tests that an error is raised when cmd.run_all errors - ''' - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(mac_desktop.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, - mac_desktop.say) + """ + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(mac_desktop.__salt__, {"cmd.run_all": mock}): + self.assertRaises(CommandExecutionError, mac_desktop.say) diff --git a/tests/unit/modules/test_mac_group.py b/tests/unit/modules/test_mac_group.py index 853adaa189d..be212354a5c 100644 --- a/tests/unit/modules/test_mac_group.py +++ b/tests/unit/modules/test_mac_group.py @@ -1,31 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.modules.mac_group as mac_group +from salt.exceptions import CommandExecutionError, SaltInvocationError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + HAS_GRP = True try: import grp except ImportError: HAS_GRP = False -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch - -# Import Salt Libs -import salt.modules.mac_group as mac_group -from salt.exceptions import SaltInvocationError, CommandExecutionError - @skipIf(not HAS_GRP, "Missing required library 'grp'") class MacGroupTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for the salt.modules.mac_group module - ''' + """ def setup_loader_modules(self): return {mac_group: {}} @@ -33,160 +34,162 @@ class MacGroupTestCase(TestCase, LoaderModuleMockMixin): # 'add' function tests: 6 def test_add_group_exists(self): - ''' + """ Tests if the group already exists or not - ''' - mock_group = {'passwd': '*', 'gid': 0, 'name': 'test', 'members': ['root']} - with patch('salt.modules.mac_group.info', MagicMock(return_value=mock_group)): - self.assertRaises(CommandExecutionError, mac_group.add, 'test') + """ + mock_group = {"passwd": "*", "gid": 0, "name": "test", "members": ["root"]} + with patch("salt.modules.mac_group.info", MagicMock(return_value=mock_group)): + self.assertRaises(CommandExecutionError, mac_group.add, "test") def test_add_whitespace(self): - ''' + """ Tests if there is whitespace in the group name - ''' - with patch('salt.modules.mac_group.info', MagicMock(return_value={})): - self.assertRaises(SaltInvocationError, mac_group.add, 'white space') + """ + with patch("salt.modules.mac_group.info", MagicMock(return_value={})): + self.assertRaises(SaltInvocationError, mac_group.add, "white space") def test_add_underscore(self): - ''' + """ Tests if the group name starts with an underscore or not - ''' - with patch('salt.modules.mac_group.info', MagicMock(return_value={})): - self.assertRaises(SaltInvocationError, mac_group.add, '_Test') + """ + with patch("salt.modules.mac_group.info", MagicMock(return_value={})): + self.assertRaises(SaltInvocationError, mac_group.add, "_Test") def test_add_gid_int(self): - ''' + """ Tests if the gid is an int or not - ''' - with patch('salt.modules.mac_group.info', MagicMock(return_value={})): - self.assertRaises(SaltInvocationError, mac_group.add, 'foo', 'foo') + """ + with patch("salt.modules.mac_group.info", MagicMock(return_value={})): + self.assertRaises(SaltInvocationError, mac_group.add, "foo", "foo") def test_add_gid_exists(self): - ''' + """ Tests if the gid is already in use or not - ''' - with patch('salt.modules.mac_group.info', MagicMock(return_value={})), \ - patch('salt.modules.mac_group._list_gids', MagicMock(return_value=['3456'])): - self.assertRaises(CommandExecutionError, mac_group.add, 'foo', 3456) + """ + with patch("salt.modules.mac_group.info", MagicMock(return_value={})), patch( + "salt.modules.mac_group._list_gids", MagicMock(return_value=["3456"]) + ): + self.assertRaises(CommandExecutionError, mac_group.add, "foo", 3456) def test_add(self): - ''' + """ Tests if specified group was added - ''' + """ mock_ret = MagicMock(return_value=0) - with patch.dict(mac_group.__salt__, {'cmd.retcode': mock_ret}), \ - patch('salt.modules.mac_group.info', MagicMock(return_value={})), \ - patch('salt.modules.mac_group._list_gids', MagicMock(return_value=[])): - self.assertTrue(mac_group.add('test', 500)) + with patch.dict(mac_group.__salt__, {"cmd.retcode": mock_ret}), patch( + "salt.modules.mac_group.info", MagicMock(return_value={}) + ), patch("salt.modules.mac_group._list_gids", MagicMock(return_value=[])): + self.assertTrue(mac_group.add("test", 500)) # 'delete' function tests: 4 def test_delete_whitespace(self): - ''' + """ Tests if there is whitespace in the group name - ''' - self.assertRaises(SaltInvocationError, mac_group.delete, 'white space') + """ + self.assertRaises(SaltInvocationError, mac_group.delete, "white space") def test_delete_underscore(self): - ''' + """ Tests if the group name starts with an underscore or not - ''' - self.assertRaises(SaltInvocationError, mac_group.delete, '_Test') + """ + self.assertRaises(SaltInvocationError, mac_group.delete, "_Test") def test_delete_group_exists(self): - ''' + """ Tests if the group to be deleted exists or not - ''' - with patch('salt.modules.mac_group.info', MagicMock(return_value={})): - self.assertTrue(mac_group.delete('test')) + """ + with patch("salt.modules.mac_group.info", MagicMock(return_value={})): + self.assertTrue(mac_group.delete("test")) def test_delete(self): - ''' + """ Tests if the specified group was deleted - ''' + """ mock_ret = MagicMock(return_value=0) - mock_group = {'passwd': '*', 'gid': 0, 'name': 'test', 'members': ['root']} - with patch.dict(mac_group.__salt__, {'cmd.retcode': mock_ret}), \ - patch('salt.modules.mac_group.info', MagicMock(return_value=mock_group)): - self.assertTrue(mac_group.delete('test')) + mock_group = {"passwd": "*", "gid": 0, "name": "test", "members": ["root"]} + with patch.dict(mac_group.__salt__, {"cmd.retcode": mock_ret}), patch( + "salt.modules.mac_group.info", MagicMock(return_value=mock_group) + ): + self.assertTrue(mac_group.delete("test")) # 'info' function tests: 2 def test_info_whitespace(self): - ''' + """ Tests if there is whitespace in the group name - ''' - self.assertRaises(SaltInvocationError, mac_group.info, 'white space') + """ + self.assertRaises(SaltInvocationError, mac_group.info, "white space") def test_info(self): - ''' + """ Tests the return of group information - ''' - mock_getgrall = [grp.struct_group(('foo', '*', 20, ['test']))] - with patch('grp.getgrall', MagicMock(return_value=mock_getgrall)): - ret = {'passwd': '*', 'gid': 20, 'name': 'foo', 'members': ['test']} - self.assertEqual(mac_group.info('foo'), ret) + """ + mock_getgrall = [grp.struct_group(("foo", "*", 20, ["test"]))] + with patch("grp.getgrall", MagicMock(return_value=mock_getgrall)): + ret = {"passwd": "*", "gid": 20, "name": "foo", "members": ["test"]} + self.assertEqual(mac_group.info("foo"), ret) # '_format_info' function tests: 1 def test_format_info(self): - ''' + """ Tests the formatting of returned group information - ''' - data = grp.struct_group(('wheel', '*', 0, ['root'])) - ret = {'passwd': '*', 'gid': 0, 'name': 'wheel', 'members': ['root']} + """ + data = grp.struct_group(("wheel", "*", 0, ["root"])) + ret = {"passwd": "*", "gid": 0, "name": "wheel", "members": ["root"]} self.assertEqual(mac_group._format_info(data), ret) # 'getent' function tests: 1 def test_getent(self): - ''' + """ Tests the return of information on all groups - ''' - mock_getgrall = [grp.struct_group(('foo', '*', 20, ['test']))] - with patch('grp.getgrall', MagicMock(return_value=mock_getgrall)): - ret = [{'passwd': '*', 'gid': 20, 'name': 'foo', 'members': ['test']}] + """ + mock_getgrall = [grp.struct_group(("foo", "*", 20, ["test"]))] + with patch("grp.getgrall", MagicMock(return_value=mock_getgrall)): + ret = [{"passwd": "*", "gid": 20, "name": "foo", "members": ["test"]}] self.assertEqual(mac_group.getent(), ret) # 'chgid' function tests: 4 def test_chgid_gid_int(self): - ''' + """ Tests if gid is an integer or not - ''' - self.assertRaises(SaltInvocationError, mac_group.chgid, 'foo', 'foo') + """ + self.assertRaises(SaltInvocationError, mac_group.chgid, "foo", "foo") def test_chgid_group_exists(self): - ''' + """ Tests if the group id exists or not - ''' - mock_pre_gid = MagicMock(return_value='') - with patch.dict(mac_group.__salt__, - {'file.group_to_gid': mock_pre_gid}), \ - patch('salt.modules.mac_group.info', MagicMock(return_value={})): - self.assertRaises(CommandExecutionError, - mac_group.chgid, 'foo', 4376) + """ + mock_pre_gid = MagicMock(return_value="") + with patch.dict(mac_group.__salt__, {"file.group_to_gid": mock_pre_gid}), patch( + "salt.modules.mac_group.info", MagicMock(return_value={}) + ): + self.assertRaises(CommandExecutionError, mac_group.chgid, "foo", 4376) def test_chgid_gid_same(self): - ''' + """ Tests if the group id is the same as argument - ''' - mock_group = {'passwd': '*', 'gid': 0, 'name': 'test', 'members': ['root']} + """ + mock_group = {"passwd": "*", "gid": 0, "name": "test", "members": ["root"]} mock_pre_gid = MagicMock(return_value=0) - with patch.dict(mac_group.__salt__, - {'file.group_to_gid': mock_pre_gid}), \ - patch('salt.modules.mac_group.info', MagicMock(return_value=mock_group)): - self.assertTrue(mac_group.chgid('test', 0)) + with patch.dict(mac_group.__salt__, {"file.group_to_gid": mock_pre_gid}), patch( + "salt.modules.mac_group.info", MagicMock(return_value=mock_group) + ): + self.assertTrue(mac_group.chgid("test", 0)) def test_chgid(self): - ''' + """ Tests the gid for a named group was changed - ''' - mock_group = {'passwd': '*', 'gid': 0, 'name': 'test', 'members': ['root']} + """ + mock_group = {"passwd": "*", "gid": 0, "name": "test", "members": ["root"]} mock_pre_gid = MagicMock(return_value=0) mock_ret = MagicMock(return_value=0) - with patch.dict(mac_group.__salt__, - {'file.group_to_gid': mock_pre_gid}), \ - patch.dict(mac_group.__salt__, {'cmd.retcode': mock_ret}), \ - patch('salt.modules.mac_group.info', MagicMock(return_value=mock_group)): - self.assertTrue(mac_group.chgid('test', 500)) + with patch.dict( + mac_group.__salt__, {"file.group_to_gid": mock_pre_gid} + ), patch.dict(mac_group.__salt__, {"cmd.retcode": mock_ret}), patch( + "salt.modules.mac_group.info", MagicMock(return_value=mock_group) + ): + self.assertTrue(mac_group.chgid("test", 500)) diff --git a/tests/unit/modules/test_mac_keychain.py b/tests/unit/modules/test_mac_keychain.py index 5411d539928..c53850daaf0 100644 --- a/tests/unit/modules/test_mac_keychain.py +++ b/tests/unit/modules/test_mac_keychain.py @@ -1,108 +1,128 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.mac_keychain as keychain # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class KeychainTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {keychain: {}} def test_install_cert(self): - ''' + """ Test installing a certificate into the macOS keychain - ''' + """ mock = MagicMock() - with patch.dict(keychain.__salt__, {'cmd.run': mock}): - keychain.install('/path/to/cert.p12', 'passw0rd') - mock.assert_called_once_with('security import /path/to/cert.p12 -P passw0rd ' - '-k /Library/Keychains/System.keychain') + with patch.dict(keychain.__salt__, {"cmd.run": mock}): + keychain.install("/path/to/cert.p12", "passw0rd") + mock.assert_called_once_with( + "security import /path/to/cert.p12 -P passw0rd " + "-k /Library/Keychains/System.keychain" + ) def test_install_cert_extras(self): - ''' + """ Test installing a certificate into the macOS keychain with extras - ''' + """ mock = MagicMock() - with patch.dict(keychain.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.mac_keychain.unlock_keychain') as unlock_mock: - keychain.install('/path/to/cert.p12', 'passw0rd', '/path/to/chain', allow_any=True, - keychain_password='passw0rd1') - unlock_mock.assert_called_once_with('/path/to/chain', 'passw0rd1') - mock.assert_called_once_with('security import /path/to/cert.p12 -P passw0rd -k /path/to/chain -A') + with patch.dict(keychain.__salt__, {"cmd.run": mock}), patch( + "salt.modules.mac_keychain.unlock_keychain" + ) as unlock_mock: + keychain.install( + "/path/to/cert.p12", + "passw0rd", + "/path/to/chain", + allow_any=True, + keychain_password="passw0rd1", + ) + unlock_mock.assert_called_once_with("/path/to/chain", "passw0rd1") + mock.assert_called_once_with( + "security import /path/to/cert.p12 -P passw0rd -k /path/to/chain -A" + ) def test_uninstall_cert(self): - ''' + """ Test uninstalling a certificate from the macOS keychain - ''' + """ mock = MagicMock() - with patch.dict(keychain.__salt__, {'cmd.run': mock}): - keychain.uninstall('/path/to/cert.p12', 'passw0rd') - mock.assert_called_once_with('security delete-certificate -c "/path/to/cert.p12" passw0rd') + with patch.dict(keychain.__salt__, {"cmd.run": mock}): + keychain.uninstall("/path/to/cert.p12", "passw0rd") + mock.assert_called_once_with( + 'security delete-certificate -c "/path/to/cert.p12" passw0rd' + ) def test_list_certs(self): - ''' + """ Test listing available certificates in a keychain - ''' + """ expected = ["com.apple.systemdefault", "com.apple.kerberos.kdc"] - mock = MagicMock(return_value='"com.apple.systemdefault"\n"com.apple.kerberos.kdc"') - with patch.dict(keychain.__salt__, {'cmd.run': mock}): - out = keychain.list_certs('/path/to/cert.p12') - mock.assert_called_once_with('security find-certificate -a /path/to/cert.p12 | ' - 'grep -o "alis".*\\" | grep -o \'\\"[-A-Za-z0-9.:() ]*\\"\'', - python_shell=True) + mock = MagicMock( + return_value='"com.apple.systemdefault"\n"com.apple.kerberos.kdc"' + ) + with patch.dict(keychain.__salt__, {"cmd.run": mock}): + out = keychain.list_certs("/path/to/cert.p12") + mock.assert_called_once_with( + "security find-certificate -a /path/to/cert.p12 | " + 'grep -o "alis".*\\" | grep -o \'\\"[-A-Za-z0-9.:() ]*\\"\'', + python_shell=True, + ) self.assertEqual(out, expected) def test_get_friendly_name(self): - ''' + """ Test getting the friendly name of a certificate - ''' + """ expected = "ID Installer Salt" mock = MagicMock(return_value="friendlyName: ID Installer Salt") - with patch.dict(keychain.__salt__, {'cmd.run': mock}): - out = keychain.get_friendly_name('/path/to/cert.p12', 'passw0rd') - mock.assert_called_once_with('openssl pkcs12 -in /path/to/cert.p12 -passin pass:passw0rd -info ' - '-nodes -nokeys 2> /dev/null | grep friendlyName:', - python_shell=True) + with patch.dict(keychain.__salt__, {"cmd.run": mock}): + out = keychain.get_friendly_name("/path/to/cert.p12", "passw0rd") + mock.assert_called_once_with( + "openssl pkcs12 -in /path/to/cert.p12 -passin pass:passw0rd -info " + "-nodes -nokeys 2> /dev/null | grep friendlyName:", + python_shell=True, + ) self.assertEqual(out, expected) def test_get_default_keychain(self): - ''' + """ Test getting the default keychain - ''' + """ mock = MagicMock() - with patch.dict(keychain.__salt__, {'cmd.run': mock}): - keychain.get_default_keychain('frank', 'system') - mock.assert_called_once_with('security default-keychain -d system', runas='frank') + with patch.dict(keychain.__salt__, {"cmd.run": mock}): + keychain.get_default_keychain("frank", "system") + mock.assert_called_once_with( + "security default-keychain -d system", runas="frank" + ) def test_set_default_keychain(self): - ''' + """ Test setting the default keychain - ''' + """ mock = MagicMock() - with patch.dict(keychain.__salt__, {'cmd.run': mock}): - keychain.set_default_keychain('/path/to/chain.keychain', 'system', 'frank') - mock.assert_called_once_with('security default-keychain -d system -s /path/to/chain.keychain', - runas='frank') + with patch.dict(keychain.__salt__, {"cmd.run": mock}): + keychain.set_default_keychain("/path/to/chain.keychain", "system", "frank") + mock.assert_called_once_with( + "security default-keychain -d system -s /path/to/chain.keychain", + runas="frank", + ) def test_unlock_keychain(self): - ''' + """ Test unlocking the keychain - ''' + """ mock = MagicMock() - with patch.dict(keychain.__salt__, {'cmd.run': mock}): - keychain.unlock_keychain('/path/to/chain.keychain', 'passw0rd') - mock.assert_called_once_with('security unlock-keychain -p passw0rd /path/to/chain.keychain') + with patch.dict(keychain.__salt__, {"cmd.run": mock}): + keychain.unlock_keychain("/path/to/chain.keychain", "passw0rd") + mock.assert_called_once_with( + "security unlock-keychain -p passw0rd /path/to/chain.keychain" + ) diff --git a/tests/unit/modules/test_mac_pkgutil.py b/tests/unit/modules/test_mac_pkgutil.py index 29b5926b610..9247e357d55 100644 --- a/tests/unit/modules/test_mac_pkgutil.py +++ b/tests/unit/modules/test_mac_pkgutil.py @@ -8,25 +8,24 @@ import salt.modules.mac_pkgutil as mac_pkgutil # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import patch +from tests.support.unit import TestCase class MacPkgutilTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {mac_pkgutil: {}} def test_install(self): # Given - source = '/foo/bar/fubar.pkg' - package_id = 'com.foo.fubar.pkg' + source = "/foo/bar/fubar.pkg" + package_id = "com.foo.fubar.pkg" # When - with patch('salt.modules.mac_pkgutil.is_installed', - return_value=False): - with patch('salt.modules.mac_pkgutil._install_from_path', - return_value=True) as _install_from_path: + with patch("salt.modules.mac_pkgutil.is_installed", return_value=False): + with patch( + "salt.modules.mac_pkgutil._install_from_path", return_value=True + ) as _install_from_path: mac_pkgutil.install(source, package_id) # Then @@ -34,14 +33,14 @@ class MacPkgutilTestCase(TestCase, LoaderModuleMockMixin): def test_install_already_there(self): # Given - source = '/foo/bar/fubar.pkg' - package_id = 'com.foo.fubar.pkg' + source = "/foo/bar/fubar.pkg" + package_id = "com.foo.fubar.pkg" # When - with patch('salt.modules.mac_pkgutil.is_installed', - return_value=True): - with patch('salt.modules.mac_pkgutil._install_from_path', - return_value=True) as _install_from_path: + with patch("salt.modules.mac_pkgutil.is_installed", return_value=True): + with patch( + "salt.modules.mac_pkgutil._install_from_path", return_value=True + ) as _install_from_path: mac_pkgutil.install(source, package_id) # Then diff --git a/tests/unit/modules/test_mac_power.py b/tests/unit/modules/test_mac_power.py index 3c2d990de31..83e2264c995 100644 --- a/tests/unit/modules/test_mac_power.py +++ b/tests/unit/modules/test_mac_power.py @@ -1,74 +1,63 @@ # -*- coding: utf-8 -*- -''' +""" mac_power tests -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.unit import TestCase +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.mac_power as mac_power from salt.exceptions import SaltInvocationError +# Import Salt Testing Libs +from tests.support.unit import TestCase + class MacPowerTestCase(TestCase): - ''' + """ test mac_power execution module - ''' + """ + def test_validate_sleep_valid_number(self): - ''' + """ test _validate_sleep function with valid number - ''' - self.assertEqual(mac_power._validate_sleep(179), - 179) + """ + self.assertEqual(mac_power._validate_sleep(179), 179) def test_validate_sleep_invalid_number(self): - ''' + """ test _validate_sleep function with invalid number - ''' - self.assertRaises(SaltInvocationError, - mac_power._validate_sleep, - 181) + """ + self.assertRaises(SaltInvocationError, mac_power._validate_sleep, 181) def test_validate_sleep_valid_string(self): - ''' + """ test _validate_sleep function with valid string - ''' - self.assertEqual(mac_power._validate_sleep('never'), - 'Never') - self.assertEqual(mac_power._validate_sleep('off'), - 'Never') + """ + self.assertEqual(mac_power._validate_sleep("never"), "Never") + self.assertEqual(mac_power._validate_sleep("off"), "Never") def test_validate_sleep_invalid_string(self): - ''' + """ test _validate_sleep function with invalid string - ''' - self.assertRaises(SaltInvocationError, - mac_power._validate_sleep, - 'bob') + """ + self.assertRaises(SaltInvocationError, mac_power._validate_sleep, "bob") def test_validate_sleep_bool_true(self): - ''' + """ test _validate_sleep function with True - ''' - self.assertRaises(SaltInvocationError, - mac_power._validate_sleep, - True) + """ + self.assertRaises(SaltInvocationError, mac_power._validate_sleep, True) def test_validate_sleep_bool_false(self): - ''' + """ test _validate_sleep function with False - ''' - self.assertEqual(mac_power._validate_sleep(False), - 'Never') + """ + self.assertEqual(mac_power._validate_sleep(False), "Never") def test_validate_sleep_unexpected(self): - ''' + """ test _validate_sleep function with True - ''' - self.assertRaises(SaltInvocationError, - mac_power._validate_sleep, - 172.7) + """ + self.assertRaises(SaltInvocationError, mac_power._validate_sleep, 172.7) diff --git a/tests/unit/modules/test_mac_service.py b/tests/unit/modules/test_mac_service.py index 04392c42777..da3b9147eaa 100644 --- a/tests/unit/modules/test_mac_service.py +++ b/tests/unit/modules/test_mac_service.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Megan Wilhite<mwilhite@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import @@ -11,168 +11,198 @@ import salt.modules.mac_service as mac_service # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class MacServiceTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.mac_service module - ''' + """ + def setup_loader_modules(self): return {mac_service: {}} def test_service_disabled_when_enabled(self): - ''' + """ test service.disabled when service is enabled - ''' - srv_name = 'com.apple.atrun' + """ + srv_name = "com.apple.atrun" cmd = 'disabled services = {\n\t"com.saltstack.salt.minion" => false\n\t"com.apple.atrun" => false\n{' - with patch.object(mac_service, 'launchctl', MagicMock(return_value=cmd)): + with patch.object(mac_service, "launchctl", MagicMock(return_value=cmd)): self.assertFalse(mac_service.disabled(srv_name)) def test_service_disabled_when_disabled(self): - ''' + """ test service.disabled when service is disabled - ''' - srv_name = 'com.apple.atrun' + """ + srv_name = "com.apple.atrun" cmd = 'disabled services = {\n\t"com.saltstack.salt.minion" => false\n\t"com.apple.atrun" => true\n{' - with patch.object(mac_service, 'launchctl', MagicMock(return_value=cmd)): + with patch.object(mac_service, "launchctl", MagicMock(return_value=cmd)): self.assertTrue(mac_service.disabled(srv_name)) def test_service_disabled_srvname_wrong(self): - ''' + """ test service.disabled when service is just slightly wrong - ''' - srv_names = ['com.apple.atru', 'com', 'apple'] + """ + srv_names = ["com.apple.atru", "com", "apple"] cmd = 'disabled services = {\n\t"com.saltstack.salt.minion" => false\n\t"com.apple.atrun" => true\n}' for name in srv_names: - with patch.object(mac_service, 'launchctl', MagicMock(return_value=cmd)): + with patch.object(mac_service, "launchctl", MagicMock(return_value=cmd)): self.assertFalse(mac_service.disabled(name)) def test_service_disabled_status_upper_case(self): - ''' + """ test service.disabled when disabled status is uppercase - ''' - srv_name = 'com.apple.atrun' + """ + srv_name = "com.apple.atrun" cmd = 'disabled services = {\n\t"com.saltstack.salt.minion" => false\n\t"com.apple.atrun" => True\n{' - with patch.object(mac_service, 'launchctl', MagicMock(return_value=cmd)): + with patch.object(mac_service, "launchctl", MagicMock(return_value=cmd)): self.assertTrue(mac_service.disabled(srv_name)) def test_service_keep_alive_pathstate_file_rm(self): - ''' + """ test _always_running_service when keep_alive has pathstate set in plist file and file doesn't exist - ''' - srv_name = 'com.apple.atrun' - info = {'plist': {'EnableTransactions': True, - 'ProgramArguments': ['/usr/libexec/ntpd-wrapper'], - 'Label': 'org.ntp.ntpd', - 'KeepAlive': {'PathState': {'/private/etc/ntp.conf': True}}}} + """ + srv_name = "com.apple.atrun" + info = { + "plist": { + "EnableTransactions": True, + "ProgramArguments": ["/usr/libexec/ntpd-wrapper"], + "Label": "org.ntp.ntpd", + "KeepAlive": {"PathState": {"/private/etc/ntp.conf": True}}, + } + } - with patch.object(mac_service, 'show', MagicMock(return_value=info)): - with patch('os.path.exists', MagicMock(return_value=False)): + with patch.object(mac_service, "show", MagicMock(return_value=info)): + with patch("os.path.exists", MagicMock(return_value=False)): assert mac_service._always_running_service(srv_name) is False def test_service_keep_alive_empty(self): - ''' + """ test _always_running_service when keep_alive is empty - ''' - srv_name = 'com.apple.atrun' - info = {'plist': {'EnableTransactions': True, - 'ProgramArguments': ['/usr/libexec/ntpd-wrapper'], - 'Label': 'org.ntp.ntpd', - 'KeepAlive': {}}} + """ + srv_name = "com.apple.atrun" + info = { + "plist": { + "EnableTransactions": True, + "ProgramArguments": ["/usr/libexec/ntpd-wrapper"], + "Label": "org.ntp.ntpd", + "KeepAlive": {}, + } + } - with patch.object(mac_service, 'show', MagicMock(return_value=info)): - with patch('os.path.exists', MagicMock(return_value=False)): + with patch.object(mac_service, "show", MagicMock(return_value=info)): + with patch("os.path.exists", MagicMock(return_value=False)): assert mac_service._always_running_service(srv_name) is False def test_service_keep_alive_pathstate_false(self): - ''' + """ test _always_running_service when keep_alive has pathstate set in plist file and file is false - ''' - srv_name = 'com.apple.atrun' - info = {'plist': {'EnableTransactions': True, - 'ProgramArguments': ['/usr/libexec/ntpd-wrapper'], - 'Label': 'org.ntp.ntpd', - 'KeepAlive': {'PathState': {'/private/etc/ntp.conf': False}}}} + """ + srv_name = "com.apple.atrun" + info = { + "plist": { + "EnableTransactions": True, + "ProgramArguments": ["/usr/libexec/ntpd-wrapper"], + "Label": "org.ntp.ntpd", + "KeepAlive": {"PathState": {"/private/etc/ntp.conf": False}}, + } + } - with patch.object(mac_service, 'show', MagicMock(return_value=info)): - with patch('os.path.exists', MagicMock(return_value=False)): + with patch.object(mac_service, "show", MagicMock(return_value=info)): + with patch("os.path.exists", MagicMock(return_value=False)): assert mac_service._always_running_service(srv_name) is True def test_service_keep_alive_pathstate(self): - ''' + """ test _always_running_service when keep_alive has pathstate set in plist file - ''' - srv_name = 'com.apple.atrun' - info = {'plist': {'EnableTransactions': True, - 'ProgramArguments': ['/usr/libexec/ntpd-wrapper'], - 'Label': 'org.ntp.ntpd', - 'KeepAlive': {'PathState': {'/private/etc/ntp.conf': True}}}} + """ + srv_name = "com.apple.atrun" + info = { + "plist": { + "EnableTransactions": True, + "ProgramArguments": ["/usr/libexec/ntpd-wrapper"], + "Label": "org.ntp.ntpd", + "KeepAlive": {"PathState": {"/private/etc/ntp.conf": True}}, + } + } - with patch.object(mac_service, 'show', MagicMock(return_value=info)): - with patch('os.path.exists', MagicMock(return_value=True)): + with patch.object(mac_service, "show", MagicMock(return_value=info)): + with patch("os.path.exists", MagicMock(return_value=True)): assert mac_service._always_running_service(srv_name) is True def test_service_keep_alive(self): - ''' + """ test _always_running_service when keep_alive set - ''' - srv_name = 'com.apple.atrun' - info = {'plist': {'EnableTransactions': True, - 'ProgramArguments': ['/usr/libexec/ntpd-wrapper'], - 'Label': 'org.ntp.ntpd', - 'KeepAlive': True}} + """ + srv_name = "com.apple.atrun" + info = { + "plist": { + "EnableTransactions": True, + "ProgramArguments": ["/usr/libexec/ntpd-wrapper"], + "Label": "org.ntp.ntpd", + "KeepAlive": True, + } + } - with patch.object(mac_service, 'show', MagicMock(return_value=info)): + with patch.object(mac_service, "show", MagicMock(return_value=info)): assert mac_service._always_running_service(srv_name) is True def test_service_keep_alive_false(self): - ''' + """ test _always_running_service when keep_alive False - ''' - srv_name = 'com.apple.atrun' - info = {'plist': {'EnableTransactions': True, - 'ProgramArguments': ['/usr/libexec/ntpd-wrapper'], - 'Label': 'org.ntp.ntpd', - 'KeepAlive': False}} + """ + srv_name = "com.apple.atrun" + info = { + "plist": { + "EnableTransactions": True, + "ProgramArguments": ["/usr/libexec/ntpd-wrapper"], + "Label": "org.ntp.ntpd", + "KeepAlive": False, + } + } - with patch.object(mac_service, 'show', MagicMock(return_value=info)): + with patch.object(mac_service, "show", MagicMock(return_value=info)): assert mac_service._always_running_service(srv_name) is False def test_service_keep_alive_missing(self): - ''' + """ test _always_running_service when keep_alive not in dict - ''' - srv_name = 'com.apple.atrun' - info = {'plist': {'EnableTransactions': True, - 'ProgramArguments': ['/usr/libexec/ntpd-wrapper'], - 'Label': 'org.ntp.ntpd'}} + """ + srv_name = "com.apple.atrun" + info = { + "plist": { + "EnableTransactions": True, + "ProgramArguments": ["/usr/libexec/ntpd-wrapper"], + "Label": "org.ntp.ntpd", + } + } - with patch.object(mac_service, 'show', MagicMock(return_value=info)): + with patch.object(mac_service, "show", MagicMock(return_value=info)): assert mac_service._always_running_service(srv_name) is False def test_service_keep_alive_wrong_setting(self): - ''' + """ test _always_running_service when keep_alive has pathstate set in plist file - ''' - srv_name = 'com.apple.atrun' - info = {'plist': {'EnableTransactions': True, - 'ProgramArguments': ['/usr/libexec/ntpd-wrapper'], - 'Label': 'org.ntp.ntpd', - 'KeepAlive': {'Doesnotexist': {'doesnt_exist': True}}}} + """ + srv_name = "com.apple.atrun" + info = { + "plist": { + "EnableTransactions": True, + "ProgramArguments": ["/usr/libexec/ntpd-wrapper"], + "Label": "org.ntp.ntpd", + "KeepAlive": {"Doesnotexist": {"doesnt_exist": True}}, + } + } - with patch.object(mac_service, 'show', MagicMock(return_value=info)): + with patch.object(mac_service, "show", MagicMock(return_value=info)): assert mac_service._always_running_service(srv_name) is False diff --git a/tests/unit/modules/test_mac_sysctl.py b/tests/unit/modules/test_mac_sysctl.py index 35cc00f1b82..275f0bf03e8 100644 --- a/tests/unit/modules/test_mac_sysctl.py +++ b/tests/unit/modules/test_mac_sysctl.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.mac_sysctl as mac_sysctl @@ -12,77 +12,83 @@ from salt.exceptions import CommandExecutionError # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import DEFAULT, MagicMock, mock_open, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - mock_open, - patch, - DEFAULT -) class DarwinSysctlTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.mac_sysctl module - ''' + """ + def setup_loader_modules(self): return {mac_sysctl: {}} def test_get(self): - ''' + """ Tests the return of get function - ''' - mock_cmd = MagicMock(return_value='foo') - with patch.dict(mac_sysctl.__salt__, {'cmd.run': mock_cmd}): - self.assertEqual(mac_sysctl.get('kern.ostype'), 'foo') + """ + mock_cmd = MagicMock(return_value="foo") + with patch.dict(mac_sysctl.__salt__, {"cmd.run": mock_cmd}): + self.assertEqual(mac_sysctl.get("kern.ostype"), "foo") def test_assign_cmd_failed(self): - ''' + """ Tests if the assignment was successful or not - ''' - cmd = {'pid': 3548, 'retcode': 1, 'stderr': '', - 'stdout': 'net.inet.icmp.icmplim: 250 -> 50'} + """ + cmd = { + "pid": 3548, + "retcode": 1, + "stderr": "", + "stdout": "net.inet.icmp.icmplim: 250 -> 50", + } mock_cmd = MagicMock(return_value=cmd) - with patch.dict(mac_sysctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertRaises(CommandExecutionError, - mac_sysctl.assign, - 'net.inet.icmp.icmplim', 50) + with patch.dict(mac_sysctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertRaises( + CommandExecutionError, mac_sysctl.assign, "net.inet.icmp.icmplim", 50 + ) def test_assign(self): - ''' + """ Tests the return of successful assign function - ''' - cmd = {'pid': 3548, 'retcode': 0, 'stderr': '', - 'stdout': 'net.inet.icmp.icmplim: 250 -> 50'} - ret = {'net.inet.icmp.icmplim': '50'} + """ + cmd = { + "pid": 3548, + "retcode": 0, + "stderr": "", + "stdout": "net.inet.icmp.icmplim: 250 -> 50", + } + ret = {"net.inet.icmp.icmplim": "50"} mock_cmd = MagicMock(return_value=cmd) - with patch.dict(mac_sysctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertEqual(mac_sysctl.assign( - 'net.inet.icmp.icmplim', 50), ret) + with patch.dict(mac_sysctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertEqual(mac_sysctl.assign("net.inet.icmp.icmplim", 50), ret) def test_persist_no_conf_failure(self): - ''' + """ Tests adding of config file failure - ''' - read_data = IOError(13, 'Permission denied', '/file') - with patch('salt.utils.files.fopen', mock_open(read_data=read_data)), \ - patch('os.path.isfile', MagicMock(return_value=False)): - self.assertRaises(CommandExecutionError, - mac_sysctl.persist, - 'net.inet.icmp.icmplim', - 50, config=None) + """ + read_data = IOError(13, "Permission denied", "/file") + with patch("salt.utils.files.fopen", mock_open(read_data=read_data)), patch( + "os.path.isfile", MagicMock(return_value=False) + ): + self.assertRaises( + CommandExecutionError, + mac_sysctl.persist, + "net.inet.icmp.icmplim", + 50, + config=None, + ) def test_persist_no_conf_success(self): - ''' + """ Tests successful add of config file when it did not already exist - ''' - config = '/etc/sysctl.conf' - isfile_mock = MagicMock( - side_effect=lambda x: False if x == config else DEFAULT - ) - with patch('salt.utils.files.fopen', mock_open()) as m_open, \ - patch('os.path.isfile', isfile_mock): - mac_sysctl.persist('net.inet.icmp.icmplim', 50, config=config) + """ + config = "/etc/sysctl.conf" + isfile_mock = MagicMock(side_effect=lambda x: False if x == config else DEFAULT) + with patch("salt.utils.files.fopen", mock_open()) as m_open, patch( + "os.path.isfile", isfile_mock + ): + mac_sysctl.persist("net.inet.icmp.icmplim", 50, config=config) # We only should have opened the one file num_handles = len(m_open.filehandles) assert num_handles == 1, num_handles @@ -91,26 +97,27 @@ class DarwinSysctlTestCase(TestCase, LoaderModuleMockMixin): # content num_writes = len(writes) assert num_writes == 1, num_writes - assert writes[0] == '#\n# Kernel sysctl configuration\n#\n', writes[0] + assert writes[0] == "#\n# Kernel sysctl configuration\n#\n", writes[0] def test_persist_success(self): - ''' + """ Tests successful write to existing sysctl file - ''' - config = '/etc/sysctl.conf' - to_write = '#\n# Kernel sysctl configuration\n#\n' - writelines_calls = [[ - '#\n', - '# Kernel sysctl configuration\n', - '#\n', - 'net.inet.icmp.icmplim=50\n', - ]] - isfile_mock = MagicMock( - side_effect=lambda x: True if x == config else DEFAULT - ) - with patch('salt.utils.files.fopen', mock_open(read_data=to_write)) as m_open, \ - patch('os.path.isfile', isfile_mock): - mac_sysctl.persist('net.inet.icmp.icmplim', 50, config=config) + """ + config = "/etc/sysctl.conf" + to_write = "#\n# Kernel sysctl configuration\n#\n" + writelines_calls = [ + [ + "#\n", + "# Kernel sysctl configuration\n", + "#\n", + "net.inet.icmp.icmplim=50\n", + ] + ] + isfile_mock = MagicMock(side_effect=lambda x: True if x == config else DEFAULT) + with patch( + "salt.utils.files.fopen", mock_open(read_data=to_write) + ) as m_open, patch("os.path.isfile", isfile_mock): + mac_sysctl.persist("net.inet.icmp.icmplim", 50, config=config) # We only should have opened the one file num_handles = len(m_open.filehandles) assert num_handles == 1, num_handles diff --git a/tests/unit/modules/test_mac_user.py b/tests/unit/modules/test_mac_user.py index a3ebf3832f4..d71cda32c33 100644 --- a/tests/unit/modules/test_mac_user.py +++ b/tests/unit/modules/test_mac_user.py @@ -1,80 +1,117 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.modules.mac_user as mac_user +from salt.exceptions import CommandExecutionError, SaltInvocationError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + HAS_PWD = True try: import pwd except ImportError: HAS_PWD = False -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch - -# Import Salt Libs -import salt.modules.mac_user as mac_user -from salt.exceptions import SaltInvocationError, CommandExecutionError - @skipIf(not HAS_PWD, "Missing required library 'pwd'") class MacUserTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for the salt.modules.mac_user modules - ''' + """ def setup_loader_modules(self): return {mac_user: {}} if HAS_PWD: - mock_pwall = [pwd.struct_passwd(('_amavisd', '*', 83, 83, 'AMaViS Daemon', - '/var/virusmails', '/usr/bin/false')), - pwd.struct_passwd(('_appleevents', '*', 55, 55, - 'AppleEvents Daemon', - '/var/empty', '/usr/bin/false')), - pwd.struct_passwd(('_appowner', '*', 87, 87, - 'Application Owner', - '/var/empty', '/usr/bin/false'))] - mock_info_ret = {'shell': '/bin/bash', 'name': 'test', 'gid': 4376, - 'groups': ['TEST_GROUP'], 'home': '/Users/foo', - 'fullname': 'TEST USER', 'uid': 4376} + mock_pwall = [ + pwd.struct_passwd( + ( + "_amavisd", + "*", + 83, + 83, + "AMaViS Daemon", + "/var/virusmails", + "/usr/bin/false", + ) + ), + pwd.struct_passwd( + ( + "_appleevents", + "*", + 55, + 55, + "AppleEvents Daemon", + "/var/empty", + "/usr/bin/false", + ) + ), + pwd.struct_passwd( + ( + "_appowner", + "*", + 87, + 87, + "Application Owner", + "/var/empty", + "/usr/bin/false", + ) + ), + ] + mock_info_ret = { + "shell": "/bin/bash", + "name": "test", + "gid": 4376, + "groups": ["TEST_GROUP"], + "home": "/Users/foo", + "fullname": "TEST USER", + "uid": 4376, + } @classmethod def tearDownClass(cls): - for attrname in ('mock_pwall', 'mock_info_ret'): + for attrname in ("mock_pwall", "mock_info_ret"): delattr(cls, attrname) - @skipIf(True, 'Waiting on some clarifications from bug report #10594') + @skipIf(True, "Waiting on some clarifications from bug report #10594") def _test_flush_dscl_cache(self): # TODO: Implement tests after clarifications come in pass def test_dscl(self): - ''' + """ Tests the creation of a dscl node - ''' - mac_mock = MagicMock(return_value={'pid': 4948, - 'retcode': 0, - 'stderr': '', - 'stdout': ''}) - with patch.dict(mac_user.__salt__, {'cmd.run_all': mac_mock}): - with patch.dict(mac_user.__grains__, - {'kernel': 'Darwin', 'osrelease': '10.9.1', - 'osrelease_info': (10, 9, 1)}): - self.assertEqual(mac_user._dscl(['username', 'UniqueID', 501]), - {'pid': 4948, - 'retcode': 0, - 'stderr': '', - 'stdout': ''}) + """ + mac_mock = MagicMock( + return_value={"pid": 4948, "retcode": 0, "stderr": "", "stdout": ""} + ) + with patch.dict(mac_user.__salt__, {"cmd.run_all": mac_mock}): + with patch.dict( + mac_user.__grains__, + { + "kernel": "Darwin", + "osrelease": "10.9.1", + "osrelease_info": (10, 9, 1), + }, + ): + self.assertEqual( + mac_user._dscl(["username", "UniqueID", 501]), + {"pid": 4948, "retcode": 0, "stderr": "", "stdout": ""}, + ) def test_first_avail_uid(self): - ''' + """ Tests the availability of the next uid - ''' - with patch('pwd.getpwall', MagicMock(return_value=self.mock_pwall)): + """ + with patch("pwd.getpwall", MagicMock(return_value=self.mock_pwall)): self.assertEqual(mac_user._first_avail_uid(), 501) # 'add' function tests: 4 @@ -82,66 +119,88 @@ class MacUserTestCase(TestCase, LoaderModuleMockMixin): # Full functionality tests covered in integration testing def test_add_user_exists(self): - ''' + """ Tests if the user exists or not - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value=self.mock_info_ret)): - self.assertRaises(CommandExecutionError, mac_user.add, 'test') + """ + with patch( + "salt.modules.mac_user.info", MagicMock(return_value=self.mock_info_ret) + ): + self.assertRaises(CommandExecutionError, mac_user.add, "test") def test_add_whitespace(self): - ''' + """ Tests if there is whitespace in the user name - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertRaises(SaltInvocationError, mac_user.add, 'foo bar') + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertRaises(SaltInvocationError, mac_user.add, "foo bar") def test_add_uid_int(self): - ''' + """ Tests if the uid is an int - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertRaises(SaltInvocationError, mac_user.add, 'foo', 'foo') + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertRaises(SaltInvocationError, mac_user.add, "foo", "foo") def test_add_gid_int(self): - ''' + """ Tests if the gid is an int - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertRaises(SaltInvocationError, mac_user.add, 'foo', 20, 'foo') + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertRaises(SaltInvocationError, mac_user.add, "foo", 20, "foo") # 'delete' function tests: 2 # Only tested pure logic of function # Full functionality tests covered in integration testing def test_delete_whitespace(self): - ''' + """ Tests if there is whitespace in the user name - ''' - self.assertRaises(SaltInvocationError, mac_user.delete, 'foo bar') + """ + self.assertRaises(SaltInvocationError, mac_user.delete, "foo bar") def test_delete_user_exists(self): - ''' + """ Tests if the user exists or not - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertTrue(mac_user.delete('foo')) + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertTrue(mac_user.delete("foo")) def test_getent(self): - ''' + """ Tests the list of information for all users - ''' - with patch('pwd.getpwall', MagicMock(return_value=self.mock_pwall)), \ - patch('salt.modules.mac_user.list_groups', - MagicMock(return_value=['TEST_GROUP'])): - ret = [{'shell': '/usr/bin/false', 'name': '_amavisd', 'gid': 83, - 'groups': ['TEST_GROUP'], 'home': '/var/virusmails', - 'fullname': 'AMaViS Daemon', 'uid': 83}, - {'shell': '/usr/bin/false', 'name': '_appleevents', 'gid': 55, - 'groups': ['TEST_GROUP'], 'home': '/var/empty', - 'fullname': 'AppleEvents Daemon', 'uid': 55}, - {'shell': '/usr/bin/false', 'name': '_appowner', 'gid': 87, - 'groups': ['TEST_GROUP'], 'home': '/var/empty', - 'fullname': 'Application Owner', 'uid': 87}] + """ + with patch("pwd.getpwall", MagicMock(return_value=self.mock_pwall)), patch( + "salt.modules.mac_user.list_groups", MagicMock(return_value=["TEST_GROUP"]) + ): + ret = [ + { + "shell": "/usr/bin/false", + "name": "_amavisd", + "gid": 83, + "groups": ["TEST_GROUP"], + "home": "/var/virusmails", + "fullname": "AMaViS Daemon", + "uid": 83, + }, + { + "shell": "/usr/bin/false", + "name": "_appleevents", + "gid": 55, + "groups": ["TEST_GROUP"], + "home": "/var/empty", + "fullname": "AppleEvents Daemon", + "uid": 55, + }, + { + "shell": "/usr/bin/false", + "name": "_appowner", + "gid": 87, + "groups": ["TEST_GROUP"], + "home": "/var/empty", + "fullname": "Application Owner", + "uid": 87, + }, + ] self.assertEqual(mac_user.getent(), ret) # 'chuid' function tests: 3 @@ -149,173 +208,220 @@ class MacUserTestCase(TestCase, LoaderModuleMockMixin): # Full functionality tests covered in integration testing def test_chuid_int(self): - ''' + """ Tests if the uid is an int - ''' - self.assertRaises(SaltInvocationError, mac_user.chuid, 'foo', 'foo') + """ + self.assertRaises(SaltInvocationError, mac_user.chuid, "foo", "foo") def test_chuid_user_exists(self): - ''' + """ Tests if the user exists or not - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertRaises(CommandExecutionError, mac_user.chuid, 'foo', 4376) + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertRaises(CommandExecutionError, mac_user.chuid, "foo", 4376) def test_chuid_same_uid(self): - ''' + """ Tests if the user's uid is the same as as the argument - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value=self.mock_info_ret)): - self.assertTrue(mac_user.chuid('foo', 4376)) + """ + with patch( + "salt.modules.mac_user.info", MagicMock(return_value=self.mock_info_ret) + ): + self.assertTrue(mac_user.chuid("foo", 4376)) # 'chgid' function tests: 3 # Only tested pure logic of function # Full functionality tests covered in integration testing def test_chgid_int(self): - ''' + """ Tests if the gid is an int - ''' - self.assertRaises(SaltInvocationError, mac_user.chgid, 'foo', 'foo') + """ + self.assertRaises(SaltInvocationError, mac_user.chgid, "foo", "foo") def test_chgid_user_exists(self): - ''' + """ Tests if the user exists or not - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertRaises(CommandExecutionError, mac_user.chgid, 'foo', 4376) + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertRaises(CommandExecutionError, mac_user.chgid, "foo", 4376) def test_chgid_same_gid(self): - ''' + """ Tests if the user's gid is the same as as the argument - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value=self.mock_info_ret)): - self.assertTrue(mac_user.chgid('foo', 4376)) + """ + with patch( + "salt.modules.mac_user.info", MagicMock(return_value=self.mock_info_ret) + ): + self.assertTrue(mac_user.chgid("foo", 4376)) # 'chshell' function tests: 2 # Only tested pure logic of function # Full functionality tests covered in integration testing def test_chshell_user_exists(self): - ''' + """ Tests if the user exists or not - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertRaises(CommandExecutionError, mac_user.chshell, - 'foo', '/bin/bash') + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertRaises( + CommandExecutionError, mac_user.chshell, "foo", "/bin/bash" + ) def test_chshell_same_shell(self): - ''' + """ Tests if the user's shell is the same as the argument - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value=self.mock_info_ret)): - self.assertTrue(mac_user.chshell('foo', '/bin/bash')) + """ + with patch( + "salt.modules.mac_user.info", MagicMock(return_value=self.mock_info_ret) + ): + self.assertTrue(mac_user.chshell("foo", "/bin/bash")) # 'chhome' function tests: 2 # Only tested pure logic of function # Full functionality tests covered in integration testing def test_chhome_user_exists(self): - ''' + """ Test if the user exists or not - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertRaises(CommandExecutionError, mac_user.chhome, - 'foo', '/Users/foo') + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertRaises( + CommandExecutionError, mac_user.chhome, "foo", "/Users/foo" + ) def test_chhome_same_home(self): - ''' + """ Tests if the user's home is the same as the argument - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value=self.mock_info_ret)): - self.assertTrue(mac_user.chhome('foo', '/Users/foo')) + """ + with patch( + "salt.modules.mac_user.info", MagicMock(return_value=self.mock_info_ret) + ): + self.assertTrue(mac_user.chhome("foo", "/Users/foo")) # 'chfullname' function tests: 2 # Only tested pure logic of function # Full functionality tests covered in integration testing def test_chfullname_user_exists(self): - ''' + """ Tests if the user exists or not - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertRaises(CommandExecutionError, mac_user.chfullname, - 'test', 'TEST USER') + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertRaises( + CommandExecutionError, mac_user.chfullname, "test", "TEST USER" + ) def test_chfullname_same_name(self): - ''' + """ Tests if the user's full name is the same as the argument - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value=self.mock_info_ret)): - self.assertTrue(mac_user.chfullname('test', 'TEST USER')) + """ + with patch( + "salt.modules.mac_user.info", MagicMock(return_value=self.mock_info_ret) + ): + self.assertTrue(mac_user.chfullname("test", "TEST USER")) # 'chgroups' function tests: 3 # Only tested pure logic of function # Full functionality tests covered in integration testing def test_chgroups_user_exists(self): - ''' + """ Tests if the user exists or not - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value={})): - self.assertRaises(CommandExecutionError, mac_user.chgroups, - 'foo', 'wheel,root') + """ + with patch("salt.modules.mac_user.info", MagicMock(return_value={})): + self.assertRaises( + CommandExecutionError, mac_user.chgroups, "foo", "wheel,root" + ) def test_chgroups_bad_groups(self): - ''' + """ Test if there is white space in groups argument - ''' - with patch('salt.modules.mac_user.info', MagicMock(return_value=self.mock_info_ret)): - self.assertRaises(SaltInvocationError, mac_user.chgroups, - 'test', 'bad group') + """ + with patch( + "salt.modules.mac_user.info", MagicMock(return_value=self.mock_info_ret) + ): + self.assertRaises( + SaltInvocationError, mac_user.chgroups, "test", "bad group" + ) def test_chgroups_same_desired(self): - ''' + """ Tests if the user's list of groups is the same as the arguments - ''' - mock_primary = MagicMock(return_value='wheel') - with patch.dict(mac_user.__salt__, {'file.gid_to_group': mock_primary}), \ - patch('salt.modules.mac_user.info', MagicMock(return_value=self.mock_info_ret)), \ - patch('salt.modules.mac_user.list_groups', - MagicMock(return_value=('wheel', 'root'))): - self.assertTrue(mac_user.chgroups('test', 'wheel,root')) + """ + mock_primary = MagicMock(return_value="wheel") + with patch.dict(mac_user.__salt__, {"file.gid_to_group": mock_primary}), patch( + "salt.modules.mac_user.info", MagicMock(return_value=self.mock_info_ret) + ), patch( + "salt.modules.mac_user.list_groups", + MagicMock(return_value=("wheel", "root")), + ): + self.assertTrue(mac_user.chgroups("test", "wheel,root")) def test_info(self): - ''' + """ Tests the return of user information - ''' - mock_pwnam = pwd.struct_passwd(('test', '*', 0, 0, 'TEST USER', - '/var/test', '/bin/bash')) - ret = {'shell': '/bin/bash', 'name': 'test', 'gid': 0, - 'groups': ['_TEST_GROUP'], 'home': '/var/test', - 'fullname': 'TEST USER', 'uid': 0} - with patch('pwd.getpwnam', MagicMock(return_value=mock_pwnam)), \ - patch('salt.modules.mac_user.list_groups', - MagicMock(return_value=['_TEST_GROUP'])): - self.assertEqual(mac_user.info('root'), ret) + """ + mock_pwnam = pwd.struct_passwd( + ("test", "*", 0, 0, "TEST USER", "/var/test", "/bin/bash") + ) + ret = { + "shell": "/bin/bash", + "name": "test", + "gid": 0, + "groups": ["_TEST_GROUP"], + "home": "/var/test", + "fullname": "TEST USER", + "uid": 0, + } + with patch("pwd.getpwnam", MagicMock(return_value=mock_pwnam)), patch( + "salt.modules.mac_user.list_groups", MagicMock(return_value=["_TEST_GROUP"]) + ): + self.assertEqual(mac_user.info("root"), ret) def test_format_info(self): - ''' + """ Tests the formatting of returned user information - ''' - data = pwd.struct_passwd(('_TEST_GROUP', '*', 83, 83, 'AMaViS Daemon', - '/var/virusmails', '/usr/bin/false')) - ret = {'shell': '/usr/bin/false', 'name': '_TEST_GROUP', 'gid': 83, - 'groups': ['_TEST_GROUP'], 'home': '/var/virusmails', - 'fullname': 'AMaViS Daemon', 'uid': 83} - with patch('salt.modules.mac_user.list_groups', - MagicMock(return_value=['_TEST_GROUP'])): + """ + data = pwd.struct_passwd( + ( + "_TEST_GROUP", + "*", + 83, + 83, + "AMaViS Daemon", + "/var/virusmails", + "/usr/bin/false", + ) + ) + ret = { + "shell": "/usr/bin/false", + "name": "_TEST_GROUP", + "gid": 83, + "groups": ["_TEST_GROUP"], + "home": "/var/virusmails", + "fullname": "AMaViS Daemon", + "uid": 83, + } + with patch( + "salt.modules.mac_user.list_groups", MagicMock(return_value=["_TEST_GROUP"]) + ): self.assertEqual(mac_user._format_info(data), ret) def test_list_users(self): - ''' + """ Tests the list of all users - ''' - expected = ['spongebob', 'patrick', 'squidward'] - mock_run = MagicMock(return_value={'pid': 4948, - 'retcode': 0, - 'stderr': '', - 'stdout': '\n'.join(expected)}) - with patch.dict(mac_user.__grains__, {'osrelease_info': (10, 9, 1)}), \ - patch.dict(mac_user.__salt__, {'cmd.run_all': mock_run}): + """ + expected = ["spongebob", "patrick", "squidward"] + mock_run = MagicMock( + return_value={ + "pid": 4948, + "retcode": 0, + "stderr": "", + "stdout": "\n".join(expected), + } + ) + with patch.dict( + mac_user.__grains__, {"osrelease_info": (10, 9, 1)} + ), patch.dict(mac_user.__salt__, {"cmd.run_all": mock_run}): self.assertEqual(mac_user.list_users(), expected) diff --git a/tests/unit/modules/test_mac_xattr.py b/tests/unit/modules/test_mac_xattr.py index b2fd28caf3b..121e687f1f1 100644 --- a/tests/unit/modules/test_mac_xattr.py +++ b/tests/unit/modules/test_mac_xattr.py @@ -1,136 +1,152 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - -# Import Salt Libs -from salt.exceptions import CommandExecutionError import salt.modules.mac_xattr as xattr import salt.utils.mac_utils +# Import Salt Libs +from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class XAttrTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {xattr: {}} def test_list(self): - ''' + """ Test xattr.list - ''' - expected = {'spongebob': 'squarepants', - 'squidward': 'patrick'} - with patch.object(xattr, 'read', MagicMock(side_effect=['squarepants', - 'patrick'])), \ - patch('salt.utils.mac_utils.execute_return_result', - MagicMock(return_value='spongebob\nsquidward')): - self.assertEqual(xattr.list_('path/to/file'), expected) + """ + expected = {"spongebob": "squarepants", "squidward": "patrick"} + with patch.object( + xattr, "read", MagicMock(side_effect=["squarepants", "patrick"]) + ), patch( + "salt.utils.mac_utils.execute_return_result", + MagicMock(return_value="spongebob\nsquidward"), + ): + self.assertEqual(xattr.list_("path/to/file"), expected) def test_list_missing(self): - ''' + """ Test listing attributes of a missing file - ''' - with patch('salt.utils.mac_utils.execute_return_result', - MagicMock(side_effect=CommandExecutionError('No such file'))): - self.assertRaises(CommandExecutionError, xattr.list_, '/path/to/file') + """ + with patch( + "salt.utils.mac_utils.execute_return_result", + MagicMock(side_effect=CommandExecutionError("No such file")), + ): + self.assertRaises(CommandExecutionError, xattr.list_, "/path/to/file") def test_read(self): - ''' + """ Test reading a specific attribute from a file - ''' - with patch('salt.utils.mac_utils.execute_return_result', - MagicMock(return_value='expected results')): - self.assertEqual(xattr.read('/path/to/file', 'com.attr'), - 'expected results') + """ + with patch( + "salt.utils.mac_utils.execute_return_result", + MagicMock(return_value="expected results"), + ): + self.assertEqual( + xattr.read("/path/to/file", "com.attr"), "expected results" + ) def test_read_hex(self): - ''' + """ Test reading a specific attribute from a file - ''' - with patch.object(salt.utils.mac_utils, 'execute_return_result', - MagicMock(return_value='expected results')) as mock: + """ + with patch.object( + salt.utils.mac_utils, + "execute_return_result", + MagicMock(return_value="expected results"), + ) as mock: self.assertEqual( - xattr.read('/path/to/file', 'com.attr', **{'hex': True}), - 'expected results' + xattr.read("/path/to/file", "com.attr", **{"hex": True}), + "expected results", ) mock.assert_called_once_with( - ['xattr', '-p', '-x', 'com.attr', '/path/to/file']) + ["xattr", "-p", "-x", "com.attr", "/path/to/file"] + ) def test_read_missing(self): - ''' + """ Test reading a specific attribute from a file - ''' - with patch('salt.utils.mac_utils.execute_return_result', - MagicMock(side_effect=CommandExecutionError('No such file'))): - self.assertRaises(CommandExecutionError, - xattr.read, - '/path/to/file', - 'attribute') + """ + with patch( + "salt.utils.mac_utils.execute_return_result", + MagicMock(side_effect=CommandExecutionError("No such file")), + ): + self.assertRaises( + CommandExecutionError, xattr.read, "/path/to/file", "attribute" + ) def test_write(self): - ''' + """ Test writing a specific attribute to a file - ''' - mock_cmd = MagicMock(return_value='squarepants') - with patch.object(xattr, 'read', mock_cmd), \ - patch('salt.utils.mac_utils.execute_return_success', - MagicMock(return_value=True)): - self.assertTrue(xattr.write('/path/to/file', - 'spongebob', - 'squarepants')) + """ + mock_cmd = MagicMock(return_value="squarepants") + with patch.object(xattr, "read", mock_cmd), patch( + "salt.utils.mac_utils.execute_return_success", MagicMock(return_value=True) + ): + self.assertTrue(xattr.write("/path/to/file", "spongebob", "squarepants")) def test_write_missing(self): - ''' + """ Test writing a specific attribute to a file - ''' - with patch('salt.utils.mac_utils.execute_return_success', - MagicMock(side_effect=CommandExecutionError('No such file'))): - self.assertRaises(CommandExecutionError, - xattr.write, - '/path/to/file', - 'attribute', - 'value') + """ + with patch( + "salt.utils.mac_utils.execute_return_success", + MagicMock(side_effect=CommandExecutionError("No such file")), + ): + self.assertRaises( + CommandExecutionError, + xattr.write, + "/path/to/file", + "attribute", + "value", + ) def test_delete(self): - ''' + """ Test deleting a specific attribute from a file - ''' - mock_cmd = MagicMock(return_value={'spongebob': 'squarepants'}) - with patch.object(xattr, 'list_', mock_cmd), \ - patch('salt.utils.mac_utils.execute_return_success', - MagicMock(return_value=True)): - self.assertTrue(xattr.delete('/path/to/file', 'attribute')) + """ + mock_cmd = MagicMock(return_value={"spongebob": "squarepants"}) + with patch.object(xattr, "list_", mock_cmd), patch( + "salt.utils.mac_utils.execute_return_success", MagicMock(return_value=True) + ): + self.assertTrue(xattr.delete("/path/to/file", "attribute")) def test_delete_missing(self): - ''' + """ Test deleting a specific attribute from a file - ''' - with patch('salt.utils.mac_utils.execute_return_success', - MagicMock(side_effect=CommandExecutionError('No such file'))): - self.assertRaises(CommandExecutionError, - xattr.delete, - '/path/to/file', - 'attribute') + """ + with patch( + "salt.utils.mac_utils.execute_return_success", + MagicMock(side_effect=CommandExecutionError("No such file")), + ): + self.assertRaises( + CommandExecutionError, xattr.delete, "/path/to/file", "attribute" + ) def test_clear(self): - ''' + """ Test clearing all attributes on a file - ''' + """ mock_cmd = MagicMock(return_value={}) - with patch.object(xattr, 'list_', mock_cmd), \ - patch('salt.utils.mac_utils.execute_return_success', - MagicMock(return_value=True)): - self.assertTrue(xattr.clear('/path/to/file')) + with patch.object(xattr, "list_", mock_cmd), patch( + "salt.utils.mac_utils.execute_return_success", MagicMock(return_value=True) + ): + self.assertTrue(xattr.clear("/path/to/file")) def test_clear_missing(self): - ''' + """ Test clearing all attributes on a file - ''' - with patch('salt.utils.mac_utils.execute_return_success', - MagicMock(side_effect=CommandExecutionError('No such file'))): - self.assertRaises(CommandExecutionError, xattr.clear, '/path/to/file') + """ + with patch( + "salt.utils.mac_utils.execute_return_success", + MagicMock(side_effect=CommandExecutionError("No such file")), + ): + self.assertRaises(CommandExecutionError, xattr.clear, "/path/to/file") diff --git a/tests/unit/modules/test_macdefaults.py b/tests/unit/modules/test_macdefaults.py index 66ffa5ec277..542b2f08993 100644 --- a/tests/unit/modules/test_macdefaults.py +++ b/tests/unit/modules/test_macdefaults.py @@ -1,87 +1,103 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.macdefaults as macdefaults # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class MacDefaultsTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {macdefaults: {}} def test_write_default(self): - ''' + """ Test writing a default setting - ''' + """ mock = MagicMock() - with patch.dict(macdefaults.__salt__, {'cmd.run_all': mock}): - macdefaults.write('com.apple.CrashReporter', 'DialogType', 'Server') - mock.assert_called_once_with('defaults write "com.apple.CrashReporter" "DialogType" -string "Server"', - runas=None) + with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}): + macdefaults.write("com.apple.CrashReporter", "DialogType", "Server") + mock.assert_called_once_with( + 'defaults write "com.apple.CrashReporter" "DialogType" -string "Server"', + runas=None, + ) def test_write_with_user(self): - ''' + """ Test writing a default setting with a specific user - ''' + """ mock = MagicMock() - with patch.dict(macdefaults.__salt__, {'cmd.run_all': mock}): - macdefaults.write('com.apple.CrashReporter', 'DialogType', 'Server', user="frank") - mock.assert_called_once_with('defaults write "com.apple.CrashReporter" "DialogType" -string "Server"', - runas="frank") + with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}): + macdefaults.write( + "com.apple.CrashReporter", "DialogType", "Server", user="frank" + ) + mock.assert_called_once_with( + 'defaults write "com.apple.CrashReporter" "DialogType" -string "Server"', + runas="frank", + ) def test_write_default_boolean(self): - ''' + """ Test writing a default setting - ''' + """ mock = MagicMock() - with patch.dict(macdefaults.__salt__, {'cmd.run_all': mock}): - macdefaults.write('com.apple.CrashReporter', 'Crash', True, type="boolean") - mock.assert_called_once_with('defaults write "com.apple.CrashReporter" "Crash" -boolean "TRUE"', - runas=None) + with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}): + macdefaults.write("com.apple.CrashReporter", "Crash", True, type="boolean") + mock.assert_called_once_with( + 'defaults write "com.apple.CrashReporter" "Crash" -boolean "TRUE"', + runas=None, + ) def test_read_default(self): - ''' + """ Test reading a default setting - ''' + """ mock = MagicMock() - with patch.dict(macdefaults.__salt__, {'cmd.run': mock}): - macdefaults.read('com.apple.CrashReporter', 'Crash') - mock.assert_called_once_with('defaults read "com.apple.CrashReporter" "Crash"', runas=None) + with patch.dict(macdefaults.__salt__, {"cmd.run": mock}): + macdefaults.read("com.apple.CrashReporter", "Crash") + mock.assert_called_once_with( + 'defaults read "com.apple.CrashReporter" "Crash"', runas=None + ) def test_read_default_with_user(self): - ''' + """ Test reading a default setting as a specific user - ''' + """ mock = MagicMock() - with patch.dict(macdefaults.__salt__, {'cmd.run': mock}): - macdefaults.read('com.apple.CrashReporter', 'Crash', user="frank") - mock.assert_called_once_with('defaults read "com.apple.CrashReporter" "Crash"', runas="frank") + with patch.dict(macdefaults.__salt__, {"cmd.run": mock}): + macdefaults.read("com.apple.CrashReporter", "Crash", user="frank") + mock.assert_called_once_with( + 'defaults read "com.apple.CrashReporter" "Crash"', runas="frank" + ) def test_delete_default(self): - ''' + """ Test delete a default setting - ''' + """ mock = MagicMock() - with patch.dict(macdefaults.__salt__, {'cmd.run_all': mock}): - macdefaults.delete('com.apple.CrashReporter', 'Crash') - mock.assert_called_once_with('defaults delete "com.apple.CrashReporter" "Crash"', output_loglevel='debug', runas=None) + with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}): + macdefaults.delete("com.apple.CrashReporter", "Crash") + mock.assert_called_once_with( + 'defaults delete "com.apple.CrashReporter" "Crash"', + output_loglevel="debug", + runas=None, + ) def test_delete_default_with_user(self): - ''' + """ Test delete a default setting as a specific user - ''' + """ mock = MagicMock() - with patch.dict(macdefaults.__salt__, {'cmd.run_all': mock}): - macdefaults.delete('com.apple.CrashReporter', 'Crash', user="frank") - mock.assert_called_once_with('defaults delete "com.apple.CrashReporter" "Crash"', output_loglevel='debug', runas="frank") + with patch.dict(macdefaults.__salt__, {"cmd.run_all": mock}): + macdefaults.delete("com.apple.CrashReporter", "Crash", user="frank") + mock.assert_called_once_with( + 'defaults delete "com.apple.CrashReporter" "Crash"', + output_loglevel="debug", + runas="frank", + ) diff --git a/tests/unit/modules/test_macpackage.py b/tests/unit/modules/test_macpackage.py index be286a8d9cd..39455281142 100644 --- a/tests/unit/modules/test_macpackage.py +++ b/tests/unit/modules/test_macpackage.py @@ -1,255 +1,290 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.macpackage as macpackage # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - call -) class MacPackageTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {macpackage: {}} def test_install(self): - ''' + """ Test installing a PKG file - ''' + """ mock = MagicMock() - with patch.dict(macpackage.__salt__, {'cmd.run_all': mock}): - macpackage.install('/path/to/file.pkg') - mock.assert_called_once_with('installer -pkg /path/to/file.pkg -target LocalSystem', python_shell=False) + with patch.dict(macpackage.__salt__, {"cmd.run_all": mock}): + macpackage.install("/path/to/file.pkg") + mock.assert_called_once_with( + "installer -pkg /path/to/file.pkg -target LocalSystem", + python_shell=False, + ) def test_install_wildcard(self): - ''' + """ Test installing a PKG file with a wildcard - ''' + """ mock = MagicMock() - with patch.dict(macpackage.__salt__, {'cmd.run_all': mock}): - macpackage.install('/path/to/*.pkg') - mock.assert_called_once_with('installer -pkg /path/to/*.pkg -target LocalSystem', python_shell=True) + with patch.dict(macpackage.__salt__, {"cmd.run_all": mock}): + macpackage.install("/path/to/*.pkg") + mock.assert_called_once_with( + "installer -pkg /path/to/*.pkg -target LocalSystem", python_shell=True + ) def test_install_with_extras(self): - ''' + """ Test installing a PKG file with extra options - ''' + """ mock = MagicMock() - with patch.dict(macpackage.__salt__, {'cmd.run_all': mock}): - macpackage.install('/path/to/file.pkg', store=True, allow_untrusted=True) - mock.assert_called_once_with('installer -pkg /path/to/file.pkg -target LocalSystem -store -allowUntrusted', - python_shell=False) + with patch.dict(macpackage.__salt__, {"cmd.run_all": mock}): + macpackage.install("/path/to/file.pkg", store=True, allow_untrusted=True) + mock.assert_called_once_with( + "installer -pkg /path/to/file.pkg -target LocalSystem -store -allowUntrusted", + python_shell=False, + ) def test_install_app(self): - ''' + """ Test installing an APP package - ''' + """ mock = MagicMock() - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): - macpackage.install_app('/path/to/file.app') - mock.assert_called_once_with('rsync -a --delete "/path/to/file.app/" ' - '"/Applications/file.app"') + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): + macpackage.install_app("/path/to/file.app") + mock.assert_called_once_with( + 'rsync -a --delete "/path/to/file.app/" ' '"/Applications/file.app"' + ) def test_install_app_specify_target(self): - ''' + """ Test installing an APP package with a specific target - ''' + """ mock = MagicMock() - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): - macpackage.install_app('/path/to/file.app', '/Applications/new.app') - mock.assert_called_once_with('rsync -a --delete "/path/to/file.app/" ' - '"/Applications/new.app"') + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): + macpackage.install_app("/path/to/file.app", "/Applications/new.app") + mock.assert_called_once_with( + 'rsync -a --delete "/path/to/file.app/" ' '"/Applications/new.app"' + ) def test_install_app_with_slash(self): - ''' + """ Test installing an APP package with a specific target - ''' + """ mock = MagicMock() - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): - macpackage.install_app('/path/to/file.app/') - mock.assert_called_once_with('rsync -a --delete "/path/to/file.app/" ' - '"/Applications/file.app"') + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): + macpackage.install_app("/path/to/file.app/") + mock.assert_called_once_with( + 'rsync -a --delete "/path/to/file.app/" ' '"/Applications/file.app"' + ) def test_uninstall(self): - ''' + """ Test Uninstalling an APP package with a specific target - ''' + """ mock = MagicMock() - with patch.dict(macpackage.__salt__, {'file.remove': mock}): - macpackage.uninstall_app('/path/to/file.app') - mock.assert_called_once_with('/path/to/file.app') + with patch.dict(macpackage.__salt__, {"file.remove": mock}): + macpackage.uninstall_app("/path/to/file.app") + mock.assert_called_once_with("/path/to/file.app") def test_mount(self): - ''' + """ Test mounting an dmg file to a temporary location - ''' + """ cmd_mock = MagicMock() - temp_mock = MagicMock(return_value='dmg-ABCDEF') - with patch.dict(macpackage.__salt__, {'cmd.run': cmd_mock, - 'temp.dir': temp_mock}): - macpackage.mount('/path/to/file.dmg') - temp_mock.assert_called_once_with(prefix='dmg-') - cmd_mock.assert_called_once_with('hdiutil attach -readonly -nobrowse -mountpoint ' - 'dmg-ABCDEF "/path/to/file.dmg"') + temp_mock = MagicMock(return_value="dmg-ABCDEF") + with patch.dict( + macpackage.__salt__, {"cmd.run": cmd_mock, "temp.dir": temp_mock} + ): + macpackage.mount("/path/to/file.dmg") + temp_mock.assert_called_once_with(prefix="dmg-") + cmd_mock.assert_called_once_with( + "hdiutil attach -readonly -nobrowse -mountpoint " + 'dmg-ABCDEF "/path/to/file.dmg"' + ) def test_unmount(self): - ''' + """ Test Unmounting an dmg file to a temporary location - ''' + """ mock = MagicMock() - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): - macpackage.unmount('/path/to/file.dmg') + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): + macpackage.unmount("/path/to/file.dmg") mock.assert_called_once_with('hdiutil detach "/path/to/file.dmg"') def test_installed_pkgs(self): - ''' + """ Test getting a list of the installed packages - ''' - expected = ['com.apple.this', 'com.salt.that'] - mock = MagicMock(return_value='com.apple.this\ncom.salt.that') - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): + """ + expected = ["com.apple.this", "com.salt.that"] + mock = MagicMock(return_value="com.apple.this\ncom.salt.that") + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): out = macpackage.installed_pkgs() - mock.assert_called_once_with('pkgutil --pkgs') + mock.assert_called_once_with("pkgutil --pkgs") self.assertEqual(out, expected) def test_get_pkg_id_with_files(self): - ''' + """ Test getting a the id for a package - ''' - with patch('salt.modules.macpackage._get_pkg_id_from_pkginfo') as pkg_id_pkginfo_mock: - expected = ['com.apple.this'] - cmd_mock = MagicMock(side_effect=[ - '/path/to/PackageInfo\n/path/to/some/other/fake/PackageInfo', - '', - '' - ]) - pkg_id_pkginfo_mock.side_effect = [['com.apple.this'], []] - temp_mock = MagicMock(return_value='/tmp/dmg-ABCDEF') + """ + with patch( + "salt.modules.macpackage._get_pkg_id_from_pkginfo" + ) as pkg_id_pkginfo_mock: + expected = ["com.apple.this"] + cmd_mock = MagicMock( + side_effect=[ + "/path/to/PackageInfo\n/path/to/some/other/fake/PackageInfo", + "", + "", + ] + ) + pkg_id_pkginfo_mock.side_effect = [["com.apple.this"], []] + temp_mock = MagicMock(return_value="/tmp/dmg-ABCDEF") remove_mock = MagicMock() - with patch.dict(macpackage.__salt__, {'cmd.run': cmd_mock, - 'temp.dir': temp_mock, - 'file.remove': remove_mock}): - out = macpackage.get_pkg_id('/path/to/file.pkg') + with patch.dict( + macpackage.__salt__, + { + "cmd.run": cmd_mock, + "temp.dir": temp_mock, + "file.remove": remove_mock, + }, + ): + out = macpackage.get_pkg_id("/path/to/file.pkg") - temp_mock.assert_called_once_with(prefix='pkg-') + temp_mock.assert_called_once_with(prefix="pkg-") cmd_calls = [ - call('xar -t -f /path/to/file.pkg | grep PackageInfo', python_shell=True, output_loglevel='quiet'), - call('xar -x -f /path/to/file.pkg /path/to/PackageInfo /path/to/some/other/fake/PackageInfo', - cwd='/tmp/dmg-ABCDEF', output_loglevel='quiet') + call( + "xar -t -f /path/to/file.pkg | grep PackageInfo", + python_shell=True, + output_loglevel="quiet", + ), + call( + "xar -x -f /path/to/file.pkg /path/to/PackageInfo /path/to/some/other/fake/PackageInfo", + cwd="/tmp/dmg-ABCDEF", + output_loglevel="quiet", + ), ] cmd_mock.assert_has_calls(cmd_calls) pkg_id_pkginfo_calls = [ - call('/path/to/PackageInfo'), - call('/path/to/some/other/fake/PackageInfo') + call("/path/to/PackageInfo"), + call("/path/to/some/other/fake/PackageInfo"), ] pkg_id_pkginfo_mock.assert_has_calls(pkg_id_pkginfo_calls) - remove_mock.assert_called_once_with('/tmp/dmg-ABCDEF') + remove_mock.assert_called_once_with("/tmp/dmg-ABCDEF") self.assertEqual(out, expected) def test_get_pkg_id_with_dir(self): - ''' + """ Test getting a the id for a package with a directory - ''' - with patch('salt.modules.macpackage._get_pkg_id_dir') as pkg_id_dir_mock: - expected = ['com.apple.this'] - pkg_id_dir_mock.return_value = ['com.apple.this'] - cmd_mock = MagicMock(return_value='Error opening /path/to/file.pkg') - temp_mock = MagicMock(return_value='/tmp/dmg-ABCDEF') + """ + with patch("salt.modules.macpackage._get_pkg_id_dir") as pkg_id_dir_mock: + expected = ["com.apple.this"] + pkg_id_dir_mock.return_value = ["com.apple.this"] + cmd_mock = MagicMock(return_value="Error opening /path/to/file.pkg") + temp_mock = MagicMock(return_value="/tmp/dmg-ABCDEF") remove_mock = MagicMock() - with patch.dict(macpackage.__salt__, {'cmd.run': cmd_mock, - 'temp.dir': temp_mock, - 'file.remove': remove_mock}): - out = macpackage.get_pkg_id('/path/to/file.pkg') + with patch.dict( + macpackage.__salt__, + { + "cmd.run": cmd_mock, + "temp.dir": temp_mock, + "file.remove": remove_mock, + }, + ): + out = macpackage.get_pkg_id("/path/to/file.pkg") - temp_mock.assert_called_once_with(prefix='pkg-') - cmd_mock.assert_called_once_with('xar -t -f /path/to/file.pkg | grep PackageInfo', - python_shell=True, output_loglevel='quiet') - pkg_id_dir_mock.assert_called_once_with('/path/to/file.pkg') - remove_mock.assert_called_once_with('/tmp/dmg-ABCDEF') + temp_mock.assert_called_once_with(prefix="pkg-") + cmd_mock.assert_called_once_with( + "xar -t -f /path/to/file.pkg | grep PackageInfo", + python_shell=True, + output_loglevel="quiet", + ) + pkg_id_dir_mock.assert_called_once_with("/path/to/file.pkg") + remove_mock.assert_called_once_with("/tmp/dmg-ABCDEF") self.assertEqual(out, expected) def test_get_mpkg_ids(self): - ''' + """ Test getting the ids of a mpkg file - ''' - with patch('salt.modules.macpackage.get_pkg_id') as get_pkg_id_mock: - expected = ['com.apple.this', 'com.salt.other'] - mock = MagicMock(return_value='/tmp/dmg-X/file.pkg\n/tmp/dmg-X/other.pkg') - get_pkg_id_mock.side_effect = [['com.apple.this'], ['com.salt.other']] + """ + with patch("salt.modules.macpackage.get_pkg_id") as get_pkg_id_mock: + expected = ["com.apple.this", "com.salt.other"] + mock = MagicMock(return_value="/tmp/dmg-X/file.pkg\n/tmp/dmg-X/other.pkg") + get_pkg_id_mock.side_effect = [["com.apple.this"], ["com.salt.other"]] - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): - out = macpackage.get_mpkg_ids('/path/to/file.mpkg') + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): + out = macpackage.get_mpkg_ids("/path/to/file.mpkg") - mock.assert_called_once_with('find /path/to -name *.pkg', python_shell=True) + mock.assert_called_once_with( + "find /path/to -name *.pkg", python_shell=True + ) - calls = [ - call('/tmp/dmg-X/file.pkg'), - call('/tmp/dmg-X/other.pkg') - ] + calls = [call("/tmp/dmg-X/file.pkg"), call("/tmp/dmg-X/other.pkg")] get_pkg_id_mock.assert_has_calls(calls) self.assertEqual(out, expected) def test_get_pkg_id_from_pkginfo(self): - ''' + """ Test getting a package id from pkginfo files - ''' - expected = ['com.apple.this', 'com.apple.that'] - mock = MagicMock(return_value='com.apple.this\ncom.apple.that') - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): - out = macpackage._get_pkg_id_from_pkginfo('/tmp/dmg-X/PackageInfo') - cmd = 'cat /tmp/dmg-X/PackageInfo | grep -Eo \'identifier="[a-zA-Z.0-9\\-]*"\' | ' \ - 'cut -c 13- | tr -d \'"\'' + """ + expected = ["com.apple.this", "com.apple.that"] + mock = MagicMock(return_value="com.apple.this\ncom.apple.that") + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): + out = macpackage._get_pkg_id_from_pkginfo("/tmp/dmg-X/PackageInfo") + cmd = ( + "cat /tmp/dmg-X/PackageInfo | grep -Eo 'identifier=\"[a-zA-Z.0-9\\-]*\"' | " + "cut -c 13- | tr -d '\"'" + ) mock.assert_called_once_with(cmd, python_shell=True) self.assertEqual(out, expected) def test_get_pkg_id_from_pkginfo_no_file(self): - ''' + """ Test getting a package id from pkginfo file when it doesn't exist - ''' + """ expected = [] - mock = MagicMock(return_value='No such file') - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): - out = macpackage._get_pkg_id_from_pkginfo('/tmp/dmg-X/PackageInfo') - cmd = 'cat /tmp/dmg-X/PackageInfo | grep -Eo \'identifier="[a-zA-Z.0-9\\-]*"\' | ' \ - 'cut -c 13- | tr -d \'"\'' + mock = MagicMock(return_value="No such file") + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): + out = macpackage._get_pkg_id_from_pkginfo("/tmp/dmg-X/PackageInfo") + cmd = ( + "cat /tmp/dmg-X/PackageInfo | grep -Eo 'identifier=\"[a-zA-Z.0-9\\-]*\"' | " + "cut -c 13- | tr -d '\"'" + ) mock.assert_called_once_with(cmd, python_shell=True) self.assertEqual(out, expected) def test_get_pkg_id_dir(self): - ''' + """ Test getting a package id from a directory - ''' - expected = ['com.apple.this'] - mock = MagicMock(return_value='com.apple.this') - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): - out = macpackage._get_pkg_id_dir('/tmp/dmg-X/') + """ + expected = ["com.apple.this"] + mock = MagicMock(return_value="com.apple.this") + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): + out = macpackage._get_pkg_id_dir("/tmp/dmg-X/") cmd = '/usr/libexec/PlistBuddy -c "print :CFBundleIdentifier" /tmp/dmg-X/Contents/Info.plist' mock.assert_called_once_with(cmd, python_shell=False) self.assertEqual(out, expected) def test_get_pkg_id_dir_wildcard(self): - ''' + """ Test getting a package id from a directory with a wildcard - ''' - expected = ['com.apple.this'] - mock = MagicMock(return_value='com.apple.this') - with patch.dict(macpackage.__salt__, {'cmd.run': mock}): - out = macpackage._get_pkg_id_dir('/tmp/dmg-X/*.pkg/') - cmd = '/usr/libexec/PlistBuddy -c "print :CFBundleIdentifier" \'/tmp/dmg-X/*.pkg/Contents/Info.plist\'' + """ + expected = ["com.apple.this"] + mock = MagicMock(return_value="com.apple.this") + with patch.dict(macpackage.__salt__, {"cmd.run": mock}): + out = macpackage._get_pkg_id_dir("/tmp/dmg-X/*.pkg/") + cmd = "/usr/libexec/PlistBuddy -c \"print :CFBundleIdentifier\" '/tmp/dmg-X/*.pkg/Contents/Info.plist'" mock.assert_called_once_with(cmd, python_shell=True) self.assertEqual(out, expected) diff --git a/tests/unit/modules/test_mandrill.py b/tests/unit/modules/test_mandrill.py index 368d2b8e068..39b3b580c05 100644 --- a/tests/unit/modules/test_mandrill.py +++ b/tests/unit/modules/test_mandrill.py @@ -1,71 +1,66 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the Mandrill execution module. -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, - MagicMock, -) - # Import Salt Libs import salt.modules.mandrill as mandrill +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + # Test data TEST_SEND = { - 'result': True, - 'comment': '', - 'out': [ + "result": True, + "comment": "", + "out": [ { - 'status': 'sent', - '_id': 'c4353540a3c123eca112bbdd704ab6', - 'email': 'recv@example.com', - 'reject_reason': None + "status": "sent", + "_id": "c4353540a3c123eca112bbdd704ab6", + "email": "recv@example.com", + "reject_reason": None, } - ] + ], } class MandrillModuleTest(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.mandrill. - ''' + """ + def setup_loader_modules(self): module_globals = { mandrill: { - '__salt__': { - 'config.merge': MagicMock(return_value={ - 'mandrill': { - 'key': '2orgk34kgk34g' - } - }) + "__salt__": { + "config.merge": MagicMock( + return_value={"mandrill": {"key": "2orgk34kgk34g"}} + ) } } } if mandrill.HAS_REQUESTS is False: - module_globals['sys.modules'] = {'requests': MagicMock()} + module_globals["sys.modules"] = {"requests": MagicMock()} return module_globals def test_send(self): - ''' + """ Test the send function. - ''' + """ mock_cmd = MagicMock(return_value=TEST_SEND) - with patch.object(mandrill, 'send', mock_cmd) as send: + with patch.object(mandrill, "send", mock_cmd) as send: self.assertEqual( - send(message={ - 'subject': 'Hi', - 'from_email': 'test@example.com', - 'to': [ - {'email': 'recv@example.com', 'type': 'to'} - ] + send( + message={ + "subject": "Hi", + "from_email": "test@example.com", + "to": [{"email": "recv@example.com", "type": "to"}], } ), - TEST_SEND + TEST_SEND, ) diff --git a/tests/unit/modules/test_match.py b/tests/unit/modules/test_match.py index 3e62f4828a4..c2307babb3c 100644 --- a/tests/unit/modules/test_match.py +++ b/tests/unit/modules/test_match.py @@ -1,119 +1,106 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Oleg Lipovchenko <oleg.lipovchenko@gmail.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.loader -import salt.modules.match as match import salt.matchers.compound_match as compound_match import salt.matchers.glob_match as glob_match import salt.matchers.list_match as list_match +import salt.modules.match as match + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase MATCHERS_DICT = { - 'compound_match.match': compound_match.match, - 'glob_match.match': glob_match.match, - 'list_match.match': list_match.match - } + "compound_match.match": compound_match.match, + "glob_match.match": glob_match.match, + "list_match.match": list_match.match, +} # the name of the minion to be used for tests -MINION_ID = 'bar03' +MINION_ID = "bar03" -@patch('salt.loader.matchers', MagicMock(return_value=MATCHERS_DICT)) +@patch("salt.loader.matchers", MagicMock(return_value=MATCHERS_DICT)) class MatchTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ This class contains a set of functions that test salt.modules.match. - ''' + """ def setup_loader_modules(self): return { - match: { - '__opts__': { - 'extension_modules': '', - 'id': MINION_ID - } - }, - compound_match: { - '__opts__': { - 'id': MINION_ID - } - }, - glob_match: { - '__opts__': {'id': MINION_ID} - }, - list_match: { - '__opts__': {'id': MINION_ID} - } + match: {"__opts__": {"extension_modules": "", "id": MINION_ID}}, + compound_match: {"__opts__": {"id": MINION_ID}}, + glob_match: {"__opts__": {"id": MINION_ID}}, + list_match: {"__opts__": {"id": MINION_ID}}, } def test_compound_with_minion_id(self): - ''' + """ Make sure that when a minion_id IS past, that it is contained in opts - ''' + """ mock_compound_match = MagicMock() - target = 'bar04' - new_minion_id = 'new_minion_id' + target = "bar04" + new_minion_id = "new_minion_id" - with patch.object(salt.loader, 'matchers', return_value={'compound_match.match': mock_compound_match}) as matchers: + with patch.object( + salt.loader, + "matchers", + return_value={"compound_match.match": mock_compound_match}, + ) as matchers: match.compound(target, minion_id=new_minion_id) # The matcher should get called with MINION_ID matchers.assert_called_once() matchers_opts = matchers.call_args[0][0] - self.assertEqual(matchers_opts.get('id'), new_minion_id) + self.assertEqual(matchers_opts.get("id"), new_minion_id) # The compound matcher should not get MINION_ID, no opts should be passed mock_compound_match.assert_called_once_with(target) def test_compound(self): - ''' + """ Test issue #55149 - ''' + """ mock_compound_match = MagicMock() - target = 'bar04' + target = "bar04" - with patch.object(salt.loader, 'matchers', return_value={'compound_match.match': mock_compound_match}) as matchers: + with patch.object( + salt.loader, + "matchers", + return_value={"compound_match.match": mock_compound_match}, + ) as matchers: match.compound(target) # The matcher should get called with MINION_ID matchers.assert_called_once() self.assertEqual(len(matchers.call_args[0]), 1) - self.assertEqual(matchers.call_args[0][0].get('id'), MINION_ID) + self.assertEqual(matchers.call_args[0][0].get("id"), MINION_ID) # The compound matcher should not get MINION_ID, no opts should be passed mock_compound_match.assert_called_once_with(target) def test_filter_by(self): - ''' + """ Tests if filter_by returns the correct dictionary. - ''' + """ lookup = { - 'foo*': { - 'key1': 'fooval1', 'key2': 'fooval2' - }, - 'bar*': { - 'key1': 'barval1', 'key2': 'barval2' - } + "foo*": {"key1": "fooval1", "key2": "fooval2"}, + "bar*": {"key1": "barval1", "key2": "barval2"}, } - result = {'key1': 'barval1', 'key2': 'barval2'} + result = {"key1": "barval1", "key2": "barval2"} self.assertDictEqual(match.filter_by(lookup), result) def test_watch_for_opts_mismatch_glob_match(self): - ''' + """ Tests for situations where the glob matcher might reference __opts__ directly instead of the local opts variable. @@ -121,13 +108,13 @@ class MatchTestCase(TestCase, LoaderModuleMockMixin): dictionary. Inside the matchers we check to see if `opts` was passed and use it instead of `__opts__`. If sometime in the future we update the matchers and use `__opts__` directly this breaks proxy matching. - ''' - self.assertTrue(glob_match.match('bar03')) - self.assertTrue(glob_match.match('rest03', {'id': 'rest03'})) - self.assertFalse(glob_match.match('rest03')) + """ + self.assertTrue(glob_match.match("bar03")) + self.assertTrue(glob_match.match("rest03", {"id": "rest03"})) + self.assertFalse(glob_match.match("rest03")) def test_watch_for_opts_mismatch_list_match(self): - ''' + """ Tests for situations where the list matcher might reference __opts__ directly instead of the local opts variable @@ -135,13 +122,13 @@ class MatchTestCase(TestCase, LoaderModuleMockMixin): dictionary. Inside the matchers we check to see if `opts` was passed and use it instead of `__opts__`. If sometime in the future we update the matchers and use `__opts__` directly this breaks proxy matching. - ''' - self.assertTrue(list_match.match('bar03')) - self.assertTrue(list_match.match('rest03', {'id': 'rest03'})) - self.assertFalse(list_match.match('rest03')) + """ + self.assertTrue(list_match.match("bar03")) + self.assertTrue(list_match.match("rest03", {"id": "rest03"})) + self.assertFalse(list_match.match("rest03")) def test_watch_for_opts_mismatch_compound_match(self): - ''' + """ Tests for situations where the compound matcher might reference __opts__ directly instead of the local opts variable @@ -149,7 +136,7 @@ class MatchTestCase(TestCase, LoaderModuleMockMixin): dictionary. Inside the matchers we check to see if `opts` was passed and use it instead of `__opts__`. If sometime in the future we update the matchers and use `__opts__` directly this breaks proxy matching. - ''' - self.assertTrue(compound_match.match('L@bar03')) - self.assertTrue(compound_match.match('L@rest03', {'id': 'rest03'})) - self.assertFalse(compound_match.match('L@rest03')) + """ + self.assertTrue(compound_match.match("L@bar03")) + self.assertTrue(compound_match.match("L@rest03", {"id": "rest03"})) + self.assertFalse(compound_match.match("L@rest03")) diff --git a/tests/unit/modules/test_mdadm_raid.py b/tests/unit/modules/test_mdadm_raid.py index 0dbf5db8717..af7ea325d2e 100644 --- a/tests/unit/modules/test_mdadm_raid.py +++ b/tests/unit/modules/test_mdadm_raid.py @@ -1,41 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Ted Strzalkowski (tedski@gmail.com) tests.unit.modules.mdadm_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import salt libs import salt.modules.mdadm_raid as mdadm +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MdadmTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {mdadm: {}} def test_create(self): - mock = MagicMock(return_value='salt') - with patch.dict(mdadm.__salt__, {'cmd.run': mock}), \ - patch('salt.utils.path.which', lambda exe: exe): + mock = MagicMock(return_value="salt") + with patch.dict(mdadm.__salt__, {"cmd.run": mock}), patch( + "salt.utils.path.which", lambda exe: exe + ): ret = mdadm.create( - '/dev/md0', 5, - devices=['/dev/sdb1', '/dev/sdc1', '/dev/sdd1'], - test_mode=False, - force=True, - chunk=256 + "/dev/md0", + 5, + devices=["/dev/sdb1", "/dev/sdc1", "/dev/sdd1"], + test_mode=False, + force=True, + chunk=256, ) - self.assertEqual('salt', ret) + self.assertEqual("salt", ret) self.assert_called_once(mock) @@ -45,85 +46,95 @@ class MdadmTestCase(TestCase, LoaderModuleMockMixin): # where args between -v and -l could be in any order self.assertEqual(len(args), 1) self.assertEqual(len(args[0]), 17) - self.assertEqual(args[0][:7], [ - 'mdadm', - '-C', '/dev/md0', - '-R', - '-v', - '-l', '5']) - self.assertEqual(args[0][10:], [ - '-e', 'default', - '-n', '3', - '/dev/sdb1', '/dev/sdc1', '/dev/sdd1']) - self.assertEqual(sorted(args[0][7:10]), - sorted(['--chunk', '256', '--force'])) - self.assertEqual(kwargs, {'python_shell': False}) + self.assertEqual( + args[0][:7], ["mdadm", "-C", "/dev/md0", "-R", "-v", "-l", "5"] + ) + self.assertEqual( + args[0][10:], + ["-e", "default", "-n", "3", "/dev/sdb1", "/dev/sdc1", "/dev/sdd1"], + ) + self.assertEqual( + sorted(args[0][7:10]), sorted(["--chunk", "256", "--force"]) + ) + self.assertEqual(kwargs, {"python_shell": False}) def test_create_metadata(self): - mock = MagicMock(return_value='salt') - with patch.dict(mdadm.__salt__, {'cmd.run': mock}), \ - patch('salt.utils.path.which', lambda exe: exe): + mock = MagicMock(return_value="salt") + with patch.dict(mdadm.__salt__, {"cmd.run": mock}), patch( + "salt.utils.path.which", lambda exe: exe + ): ret = mdadm.create( - '/dev/md0', 5, - devices=['/dev/sdb1', '/dev/sdc1', '/dev/sdd1'], - metadata=0.9, - test_mode=False, - force=True, - chunk=256 + "/dev/md0", + 5, + devices=["/dev/sdb1", "/dev/sdc1", "/dev/sdd1"], + metadata=0.9, + test_mode=False, + force=True, + chunk=256, ) - self.assertEqual('salt', ret) + self.assertEqual("salt", ret) self.assert_called_once(mock) args, kwargs = mock.call_args - self.assertEqual(args[0][:7], [ - 'mdadm', - '-C', '/dev/md0', - '-R', - '-v', - '-l', '5']) - self.assertEqual(args[0][10:], [ - '-e', '0.9', - '-n', '3', - '/dev/sdb1', '/dev/sdc1', '/dev/sdd1']) - self.assertEqual(sorted(args[0][7:10]), - sorted(['--chunk', '256', '--force'])) - self.assertEqual(kwargs, {'python_shell': False}) + self.assertEqual( + args[0][:7], ["mdadm", "-C", "/dev/md0", "-R", "-v", "-l", "5"] + ) + self.assertEqual( + args[0][10:], + ["-e", "0.9", "-n", "3", "/dev/sdb1", "/dev/sdc1", "/dev/sdd1"], + ) + self.assertEqual( + sorted(args[0][7:10]), sorted(["--chunk", "256", "--force"]) + ) + self.assertEqual(kwargs, {"python_shell": False}) def test_create_test_mode(self): mock = MagicMock() - with patch.dict(mdadm.__salt__, {'cmd.run': mock}): + with patch.dict(mdadm.__salt__, {"cmd.run": mock}): ret = mdadm.create( - '/dev/md0', 5, - devices=['/dev/sdb1', '/dev/sdc1', '/dev/sdd1'], - force=True, - chunk=256, - test_mode=True + "/dev/md0", + 5, + devices=["/dev/sdb1", "/dev/sdc1", "/dev/sdd1"], + force=True, + chunk=256, + test_mode=True, ) - self.assertEqual(sorted('mdadm -C /dev/md0 -R -v --chunk 256 ' - '--force -l 5 -e default -n 3 ' - '/dev/sdb1 /dev/sdc1 /dev/sdd1'.split()), sorted(ret.split())) - assert not mock.called, 'test mode failed, cmd.run called' + self.assertEqual( + sorted( + "mdadm -C /dev/md0 -R -v --chunk 256 " + "--force -l 5 -e default -n 3 " + "/dev/sdb1 /dev/sdc1 /dev/sdd1".split() + ), + sorted(ret.split()), + ) + assert not mock.called, "test mode failed, cmd.run called" def test_examine(self): - ''' + """ Test for mdadm_raid.examine - ''' - mock = MagicMock(return_value='ARRAY /dev/md/pool metadata=1.2 UUID=567da122:fb8e445e:55b853e0:81bd0a3e name=positron:pool') - with patch.dict(mdadm.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(mdadm.examine('/dev/md0'), - { - 'ARRAY /dev/md/pool metadata': '1.2 UUID=567da122:fb8e445e:55b853e0:81bd0a3e name=positron:pool' - }) - mock.assert_called_with('mdadm -Y -E /dev/md0', ignore_retcode=False, - python_shell=False) + """ + mock = MagicMock( + return_value="ARRAY /dev/md/pool metadata=1.2 UUID=567da122:fb8e445e:55b853e0:81bd0a3e name=positron:pool" + ) + with patch.dict(mdadm.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual( + mdadm.examine("/dev/md0"), + { + "ARRAY /dev/md/pool metadata": "1.2 UUID=567da122:fb8e445e:55b853e0:81bd0a3e name=positron:pool" + }, + ) + mock.assert_called_with( + "mdadm -Y -E /dev/md0", ignore_retcode=False, python_shell=False + ) def test_examine_quiet(self): - ''' + """ Test for mdadm_raid.examine - ''' - mock = MagicMock(return_value='') - with patch.dict(mdadm.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(mdadm.examine('/dev/md0', quiet=True), {}) - mock.assert_called_with('mdadm -Y -E /dev/md0', ignore_retcode=True, - python_shell=False) + """ + mock = MagicMock(return_value="") + with patch.dict(mdadm.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual(mdadm.examine("/dev/md0", quiet=True), {}) + mock.assert_called_with( + "mdadm -Y -E /dev/md0", ignore_retcode=True, python_shell=False + ) diff --git a/tests/unit/modules/test_memcached.py b/tests/unit/modules/test_memcached.py index 3c0d84fb08e..203b0d0446f 100644 --- a/tests/unit/modules/test_memcached.py +++ b/tests/unit/modules/test_memcached.py @@ -1,58 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.memcached as memcached from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext.six import integer_types +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase class MemcachedTestCase(TestCase): - ''' + """ Test cases for salt.modules.memcached - ''' + """ + # 'status' function tests: 2 def test_status(self): - ''' + """ Test if it gets memcached status - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + @staticmethod def get_stats(): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertDictEqual(memcached.status(), - {'127.0.0.1:11211 (1)': {}}) + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertDictEqual(memcached.status(), {"127.0.0.1:11211 (1)": {}}) def test_status_false(self): - ''' + """ Test if it gets memcached status - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + @staticmethod def get_stats(): """ @@ -60,26 +62,29 @@ class MemcachedTestCase(TestCase): """ return [] - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): self.assertFalse(memcached.status()) # 'get' function tests: 1 def test_get(self): - ''' + """ Test if it retrieve value for a key - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + @staticmethod def get_stats(): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] @staticmethod def get(key): @@ -88,20 +93,23 @@ class MemcachedTestCase(TestCase): """ return key - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertEqual(memcached.get('salt'), 'salt') + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertEqual(memcached.get("salt"), "salt") # 'set_' function tests: 1 def test_set(self): - ''' + """ Test if it set a key on the memcached server - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None self.value = None @@ -113,7 +121,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def set(self, key, value, time, min_compress_len): """ @@ -125,26 +133,35 @@ class MemcachedTestCase(TestCase): self.min_compress_len = min_compress_len return True - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertTrue(memcached.set_('salt', '1111')) + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertTrue(memcached.set_("salt", "1111")) - self.assertRaises(SaltInvocationError, memcached.set_, - 'salt', '1111', time='0.1') + self.assertRaises( + SaltInvocationError, memcached.set_, "salt", "1111", time="0.1" + ) - self.assertRaises(SaltInvocationError, memcached.set_, - 'salt', '1111', min_compress_len='0.1') + self.assertRaises( + SaltInvocationError, + memcached.set_, + "salt", + "1111", + min_compress_len="0.1", + ) # 'delete' function tests: 1 def test_delete(self): - ''' + """ Test if it delete a key from memcache server - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None self.time = None @@ -154,7 +171,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def delete(self, key, time): """ @@ -164,23 +181,27 @@ class MemcachedTestCase(TestCase): self.time = time return True - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertTrue(memcached.delete('salt')) + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertTrue(memcached.delete("salt")) - self.assertRaises(SaltInvocationError, memcached.delete, - 'salt', '1111', time='0.1') + self.assertRaises( + SaltInvocationError, memcached.delete, "salt", "1111", time="0.1" + ) # 'add' function tests: 1 def test_add(self): - ''' + """ Test if it add a key from memcache server - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None self.value = None @@ -192,7 +213,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def add(self, key, value, time, min_compress_len): """ @@ -204,26 +225,35 @@ class MemcachedTestCase(TestCase): self.min_compress_len = min_compress_len return True - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertTrue(memcached.add('salt', '1111')) + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertTrue(memcached.add("salt", "1111")) - self.assertRaises(SaltInvocationError, memcached.add, - 'salt', '1111', time='0.1') + self.assertRaises( + SaltInvocationError, memcached.add, "salt", "1111", time="0.1" + ) - self.assertRaises(SaltInvocationError, memcached.add, - 'salt', '1111', min_compress_len='0.1') + self.assertRaises( + SaltInvocationError, + memcached.add, + "salt", + "1111", + min_compress_len="0.1", + ) # 'replace' function tests: 1 def test_replace(self): - ''' + """ Test if it replace a key from memcache server - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None self.value = None @@ -235,7 +265,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def replace(self, key, value, time, min_compress_len): """ @@ -247,26 +277,35 @@ class MemcachedTestCase(TestCase): self.min_compress_len = min_compress_len return True - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertTrue(memcached.replace('salt', '1111')) + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertTrue(memcached.replace("salt", "1111")) - self.assertRaises(SaltInvocationError, memcached.replace, - 'salt', '1111', time='0.1') + self.assertRaises( + SaltInvocationError, memcached.replace, "salt", "1111", time="0.1" + ) - self.assertRaises(SaltInvocationError, memcached.replace, - 'salt', '1111', min_compress_len='0.1') + self.assertRaises( + SaltInvocationError, + memcached.replace, + "salt", + "1111", + min_compress_len="0.1", + ) # 'increment' function tests: 3 def test_increment(self): - ''' + """ Test if it increment the value of a key - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None @@ -275,7 +314,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def get(self, key): """ @@ -290,24 +329,28 @@ class MemcachedTestCase(TestCase): """ self.key = key if not isinstance(delta, integer_types): - raise SaltInvocationError('Delta value must be an integer') + raise SaltInvocationError("Delta value must be an integer") return key - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertEqual(memcached.increment('salt'), 'salt') + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertEqual(memcached.increment("salt"), "salt") - self.assertRaises(SaltInvocationError, memcached.increment, - 'salt', delta='sa') + self.assertRaises( + SaltInvocationError, memcached.increment, "salt", delta="sa" + ) def test_increment_exist(self): - ''' + """ Test if it increment the value of a key - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None @@ -316,7 +359,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def get(self, key): """ @@ -325,19 +368,21 @@ class MemcachedTestCase(TestCase): self.key = key return key - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertRaises(CommandExecutionError, memcached.increment, - 'salt') + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertRaises(CommandExecutionError, memcached.increment, "salt") def test_increment_none(self): - ''' + """ Test if it increment the value of a key - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None @@ -346,7 +391,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def get(self, key): """ @@ -355,21 +400,23 @@ class MemcachedTestCase(TestCase): self.key = key return None - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertRaises(CommandExecutionError, memcached.increment, - 'salt') + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertRaises(CommandExecutionError, memcached.increment, "salt") # 'decrement' function tests: 3 def test_decrement(self): - ''' + """ Test if it decrement the value of a key - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None @@ -378,7 +425,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def get(self, key): """ @@ -393,24 +440,28 @@ class MemcachedTestCase(TestCase): """ self.key = key if not isinstance(delta, integer_types): - raise SaltInvocationError('Delta value must be an integer') + raise SaltInvocationError("Delta value must be an integer") return key - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertEqual(memcached.decrement('salt'), 'salt') + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertEqual(memcached.decrement("salt"), "salt") - self.assertRaises(SaltInvocationError, memcached.decrement, - 'salt', delta='sa') + self.assertRaises( + SaltInvocationError, memcached.decrement, "salt", delta="sa" + ) def test_decrement_exist(self): - ''' + """ Test if it decrement the value of a key - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None @@ -419,7 +470,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def get(self, key): """ @@ -428,19 +479,21 @@ class MemcachedTestCase(TestCase): self.key = key return key - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertRaises(CommandExecutionError, memcached.decrement, - 'salt') + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertRaises(CommandExecutionError, memcached.decrement, "salt") def test_decrement_none(self): - ''' + """ Test if it decrement the value of a key - ''' + """ + class MockMemcache(object): """ Mock of memcache """ + def __init__(self): self.key = None @@ -449,7 +502,7 @@ class MemcachedTestCase(TestCase): """ Mock of stats method """ - return [('127.0.0.1:11211 (1)', {})] + return [("127.0.0.1:11211 (1)", {})] def get(self, key): """ @@ -458,7 +511,7 @@ class MemcachedTestCase(TestCase): self.key = key return None - with patch.object(memcached, '_connect', - MagicMock(return_value=MockMemcache())): - self.assertRaises(CommandExecutionError, memcached.decrement, - 'salt') + with patch.object( + memcached, "_connect", MagicMock(return_value=MockMemcache()) + ): + self.assertRaises(CommandExecutionError, memcached.decrement, "salt") diff --git a/tests/unit/modules/test_mine.py b/tests/unit/modules/test_mine.py index 30de6f4b2ff..d429d32a768 100644 --- a/tests/unit/modules/test_mine.py +++ b/tests/unit/modules/test_mine.py @@ -1,587 +1,613 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> :codeauthor: Herbert Buurman <herbert.buurman@ogd.nl> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.mine as mine import salt.utils.mine from salt.utils.odict import OrderedDict +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class FakeCache(object): - def __init__(self): self.data = {} def store(self, bank, key, value): self.data[bank, key] = value - return 'FakeCache:StoreSuccess!' + return "FakeCache:StoreSuccess!" def fetch(self, bank, key): return self.data.get((bank, key), {}) def debug(self): - print(__name__ + ':FakeCache dump:\n' - '{}'.format(self.data)) + print(__name__ + ":FakeCache dump:\n" "{}".format(self.data)) class MineTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.mine - ''' + """ + def setUp(self): - self.kernel_ret = 'Linux!' - self.foo_ret = 'baz' - self.ip_ret = '2001:db8::1:3' + self.kernel_ret = "Linux!" + self.foo_ret = "baz" + self.ip_ret = "2001:db8::1:3" self.cache = FakeCache() def setup_loader_modules(self): - mock_match = MagicMock(return_value='webserver') - return {mine: {'__salt__': { - 'match.glob': mock_match, - 'match.pcre': mock_match, - 'match.list': mock_match, - 'match.grain': mock_match, - 'match.grain_pcre': mock_match, - 'match.ipcidr': mock_match, - 'match.compound': mock_match, - 'match.pillar': mock_match, - 'match.pillar_pcre': mock_match, - 'data.get': lambda key: self.cache.fetch('minions/webserver', key), - 'data.update': lambda key, value: self.cache.store('minions/webserver', key, value), - }}} + mock_match = MagicMock(return_value="webserver") + return { + mine: { + "__salt__": { + "match.glob": mock_match, + "match.pcre": mock_match, + "match.list": mock_match, + "match.grain": mock_match, + "match.grain_pcre": mock_match, + "match.ipcidr": mock_match, + "match.compound": mock_match, + "match.pillar": mock_match, + "match.pillar_pcre": mock_match, + "data.get": lambda key: self.cache.fetch("minions/webserver", key), + "data.update": lambda key, value: self.cache.store( + "minions/webserver", key, value + ), + } + } + } def test_get_local_empty(self): - ''' + """ Tests getting function data from the local mine that does not exist. - ''' - with patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }): - ret_classic = mine.get('*', 'funky.doodle') - ret_dict = mine.get('*', ['funky.doodle']) + """ + with patch.dict(mine.__opts__, {"file_client": "local", "id": "webserver"}): + ret_classic = mine.get("*", "funky.doodle") + ret_dict = mine.get("*", ["funky.doodle"]) self.assertEqual(ret_classic, {}) self.assertEqual(ret_dict, {}) def test_get_local_classic(self): - ''' + """ Tests getting function data from the local mine that was stored without minion-side ACL. This verifies backwards compatible reads from a salt mine. - ''' + """ # Prefill minion cache with a non-ACL value - self.cache.store('minions/webserver', 'mine_cache', {'foobard': 'barfood'}) - with patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }): - ret_classic = mine.get('*', 'foobard') - ret_dict = mine.get('*', ['foobard']) - self.assertEqual(ret_classic, {'webserver': 'barfood'}) - self.assertEqual(ret_dict, {'foobard': {'webserver': 'barfood'}}) + self.cache.store("minions/webserver", "mine_cache", {"foobard": "barfood"}) + with patch.dict(mine.__opts__, {"file_client": "local", "id": "webserver"}): + ret_classic = mine.get("*", "foobard") + ret_dict = mine.get("*", ["foobard"]) + self.assertEqual(ret_classic, {"webserver": "barfood"}) + self.assertEqual(ret_dict, {"foobard": {"webserver": "barfood"}}) def test_send_get_local(self): - ''' + """ Tests sending an item to the mine in the minion's local cache, and then immediately fetching it again (since tests are executed unordered). Also verify that the stored mine cache does not use ACL data structure without allow_tgt passed. - ''' - with patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }), \ - patch.dict(mine.__salt__, { - 'network.ip_addrs': MagicMock(return_value=self.ip_ret), - 'foo.bar': MagicMock(return_value=self.foo_ret), - }): - ret = mine.send('ip_addr', mine_function='network.ip_addrs') - mine.send('foo.bar') - self.assertEqual(ret, 'FakeCache:StoreSuccess!') - self.assertEqual( - self.cache.fetch('minions/webserver', 'mine_cache'), + """ + with patch.dict( + mine.__opts__, {"file_client": "local", "id": "webserver"} + ), patch.dict( + mine.__salt__, { - 'ip_addr': self.ip_ret, - 'foo.bar': self.foo_ret, - } + "network.ip_addrs": MagicMock(return_value=self.ip_ret), + "foo.bar": MagicMock(return_value=self.foo_ret), + }, + ): + ret = mine.send("ip_addr", mine_function="network.ip_addrs") + mine.send("foo.bar") + self.assertEqual(ret, "FakeCache:StoreSuccess!") + self.assertEqual( + self.cache.fetch("minions/webserver", "mine_cache"), + {"ip_addr": self.ip_ret, "foo.bar": self.foo_ret}, + ) + with patch.dict(mine.__opts__, {"file_client": "local", "id": "webserver"}): + ret_single = mine.get("*", "ip_addr") + ret_single_dict = mine.get("*", ["ip_addr"]) + ret_multi = mine.get("*", "ip_addr,foo.bar") + ret_multi2 = mine.get("*", ["ip_addr", "foo.bar"]) + self.assertEqual(ret_single, {"webserver": self.ip_ret}) + self.assertEqual(ret_single_dict, {"ip_addr": {"webserver": self.ip_ret}}) + self.assertEqual( + ret_multi, + { + "ip_addr": {"webserver": self.ip_ret}, + "foo.bar": {"webserver": self.foo_ret}, + }, ) - with patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }): - ret_single = mine.get('*', 'ip_addr') - ret_single_dict = mine.get('*', ['ip_addr']) - ret_multi = mine.get('*', 'ip_addr,foo.bar') - ret_multi2 = mine.get('*', ['ip_addr', 'foo.bar']) - self.assertEqual(ret_single, {'webserver': self.ip_ret}) - self.assertEqual(ret_single_dict, {'ip_addr': {'webserver': self.ip_ret}}) - self.assertEqual(ret_multi, {'ip_addr': {'webserver': self.ip_ret}, 'foo.bar': {'webserver': self.foo_ret}}) self.assertEqual(ret_multi, ret_multi2) def test_send_get_acl_local(self): - ''' + """ Tests sending an item to the mine in the minion's local cache, including ACL information (useless when only working locally, but hey), and then immediately fetching it again (since tests are executed unordered). Also verify that the stored mine cache has the correct structure (with ACL) when using allow_tgt and no ACL without allow_tgt. - ''' - with patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }), \ - patch.dict(mine.__salt__, { - 'network.ip_addrs': MagicMock(return_value=self.ip_ret), - 'foo.bar': MagicMock(return_value=self.foo_ret), - }): - ret = mine.send('ip_addr', mine_function='network.ip_addrs', allow_tgt='web*', allow_tgt_type='glob') - mine.send('foo.bar') - self.assertEqual(ret, 'FakeCache:StoreSuccess!') - self.assertEqual( - self.cache.fetch('minions/webserver', 'mine_cache'), + """ + with patch.dict( + mine.__opts__, {"file_client": "local", "id": "webserver"} + ), patch.dict( + mine.__salt__, { - 'ip_addr': { + "network.ip_addrs": MagicMock(return_value=self.ip_ret), + "foo.bar": MagicMock(return_value=self.foo_ret), + }, + ): + ret = mine.send( + "ip_addr", + mine_function="network.ip_addrs", + allow_tgt="web*", + allow_tgt_type="glob", + ) + mine.send("foo.bar") + self.assertEqual(ret, "FakeCache:StoreSuccess!") + self.assertEqual( + self.cache.fetch("minions/webserver", "mine_cache"), + { + "ip_addr": { salt.utils.mine.MINE_ITEM_ACL_DATA: self.ip_ret, salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION, - 'allow_tgt': 'web*', - 'allow_tgt_type': 'glob', + "allow_tgt": "web*", + "allow_tgt_type": "glob", }, - 'foo.bar': self.foo_ret, - } + "foo.bar": self.foo_ret, + }, ) - with patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }): - ret_single = mine.get('*', 'ip_addr') - self.assertEqual(ret_single, {'webserver': self.ip_ret}) + with patch.dict(mine.__opts__, {"file_client": "local", "id": "webserver"}): + ret_single = mine.get("*", "ip_addr") + self.assertEqual(ret_single, {"webserver": self.ip_ret}) def test_send_master(self): - ''' + """ Tests sending an item to the mine stored on the master. This is done by capturing the load that is sent to the master. - ''' - with patch.object(mine, '_mine_send', MagicMock(side_effect=lambda x, y: x)),\ - patch.dict(mine.__salt__, { - 'foo.bar': MagicMock(return_value=self.foo_ret), - }), \ - patch.dict(mine.__opts__, { - 'file_client': 'remote', - 'id': 'foo', - }): - ret = mine.send('foo.bar') + """ + with patch.object( + mine, "_mine_send", MagicMock(side_effect=lambda x, y: x) + ), patch.dict( + mine.__salt__, {"foo.bar": MagicMock(return_value=self.foo_ret)} + ), patch.dict( + mine.__opts__, {"file_client": "remote", "id": "foo"} + ): + ret = mine.send("foo.bar") self.assertEqual( ret, { - 'id': 'foo', - 'cmd': '_mine', - 'data': {'foo.bar': self.foo_ret}, - 'clear': False, - } + "id": "foo", + "cmd": "_mine", + "data": {"foo.bar": self.foo_ret}, + "clear": False, + }, ) def test_send_master_acl(self): - ''' + """ Tests sending an item to the mine stored on the master. Now with ACL. This is done by capturing the load that is sent to the master. - ''' - with patch.object(mine, '_mine_send', MagicMock(side_effect=lambda x, y: x)),\ - patch.dict(mine.__salt__, { - 'foo.bar': MagicMock(return_value=self.foo_ret), - }), \ - patch.dict(mine.__opts__, { - 'file_client': 'remote', - 'id': 'foo', - }): - ret = mine.send('foo.bar', allow_tgt='roles:web', allow_tgt_type='grains') + """ + with patch.object( + mine, "_mine_send", MagicMock(side_effect=lambda x, y: x) + ), patch.dict( + mine.__salt__, {"foo.bar": MagicMock(return_value=self.foo_ret)} + ), patch.dict( + mine.__opts__, {"file_client": "remote", "id": "foo"} + ): + ret = mine.send("foo.bar", allow_tgt="roles:web", allow_tgt_type="grains") self.assertEqual( ret, { - 'id': 'foo', - 'cmd': '_mine', - 'data': { - 'foo.bar': { + "id": "foo", + "cmd": "_mine", + "data": { + "foo.bar": { salt.utils.mine.MINE_ITEM_ACL_DATA: self.foo_ret, salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION, - 'allow_tgt': 'roles:web', - 'allow_tgt_type': 'grains', + "allow_tgt": "roles:web", + "allow_tgt_type": "grains", }, }, - 'clear': False, - } + "clear": False, + }, ) def test_get_master(self): - ''' + """ Tests loading a mine item from the mine stored on the master. - ''' + """ mock_load = { - 'tgt_type': 'qux', - 'tgt': self.foo_ret, - 'cmd': '_mine_get', - 'fun': 'foo.bar', - 'id': 'foo' + "tgt_type": "qux", + "tgt": self.foo_ret, + "cmd": "_mine_get", + "fun": "foo.bar", + "id": "foo", } - with patch.object(mine, '_mine_get', MagicMock(return_value=mock_load)),\ - patch.dict(mine.__opts__, { - 'file_client': 'remote', - 'id': 'foo', - }): + with patch.object( + mine, "_mine_get", MagicMock(return_value=mock_load) + ), patch.dict(mine.__opts__, {"file_client": "remote", "id": "foo"}): # Verify the correct load - self.assertEqual( - mine.get('*', 'foo.bar'), - mock_load - ) + self.assertEqual(mine.get("*", "foo.bar"), mock_load) def test_get_master_exclude_minion(self): - ''' + """ Tests the exclude_minion-parameter for mine.get - ''' - _mine_get_ret = OrderedDict([('webserver', 'value')]) - with patch.object(mine, '_mine_get', MagicMock(return_value=_mine_get_ret)),\ - patch.dict(mine.__opts__, { - 'file_client': 'remote', - 'id': 'webserver', - }): + """ + _mine_get_ret = OrderedDict([("webserver", "value")]) + with patch.object( + mine, "_mine_get", MagicMock(return_value=_mine_get_ret) + ), patch.dict(mine.__opts__, {"file_client": "remote", "id": "webserver"}): self.assertEqual( - mine.get('*', 'foo.bar', exclude_minion=False), - {'webserver': 'value'} - ) - self.assertEqual( - mine.get('*', 'foo.bar', exclude_minion=True), - {} + mine.get("*", "foo.bar", exclude_minion=False), {"webserver": "value"} ) + self.assertEqual(mine.get("*", "foo.bar", exclude_minion=True), {}) def test_update_local(self): - ''' + """ Tests the ``update``-function on the minion's local cache. Updates mine functions from pillar+config only. - ''' + """ config_mine_functions = { - 'ip_addr': {'mine_function': 'network.ip_addrs'}, - 'network.ip_addrs': [], - 'kernel': [{'mine_function': 'grains.get'}, 'kernel', {'allow_tgt': 'web*'}], - 'foo.bar': {'allow_tgt': 'G@roles:webserver', 'allow_tgt_type': 'compound'}, + "ip_addr": {"mine_function": "network.ip_addrs"}, + "network.ip_addrs": [], + "kernel": [ + {"mine_function": "grains.get"}, + "kernel", + {"allow_tgt": "web*"}, + ], + "foo.bar": {"allow_tgt": "G@roles:webserver", "allow_tgt_type": "compound"}, } - with \ - patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }), \ - patch.dict(mine.__salt__, { - 'config.merge': MagicMock(return_value=config_mine_functions), - 'grains.get': lambda x: self.kernel_ret, - 'network.ip_addrs': MagicMock(return_value=self.ip_ret), - 'foo.bar': MagicMock(return_value=self.foo_ret), - }): + with patch.dict( + mine.__opts__, {"file_client": "local", "id": "webserver"} + ), patch.dict( + mine.__salt__, + { + "config.merge": MagicMock(return_value=config_mine_functions), + "grains.get": lambda x: self.kernel_ret, + "network.ip_addrs": MagicMock(return_value=self.ip_ret), + "foo.bar": MagicMock(return_value=self.foo_ret), + }, + ): ret = mine.update() - self.assertEqual(ret, 'FakeCache:StoreSuccess!') + self.assertEqual(ret, "FakeCache:StoreSuccess!") # Check if the mine entries have been stored properly in the FakeCache. self.assertEqual( - self.cache.fetch('minions/webserver', 'mine_cache'), + self.cache.fetch("minions/webserver", "mine_cache"), { - 'ip_addr': self.ip_ret, - 'network.ip_addrs': self.ip_ret, - 'foo.bar': { + "ip_addr": self.ip_ret, + "network.ip_addrs": self.ip_ret, + "foo.bar": { salt.utils.mine.MINE_ITEM_ACL_DATA: self.foo_ret, salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION, - 'allow_tgt': 'G@roles:webserver', - 'allow_tgt_type': 'compound', + "allow_tgt": "G@roles:webserver", + "allow_tgt_type": "compound", }, - 'kernel': { + "kernel": { salt.utils.mine.MINE_ITEM_ACL_DATA: self.kernel_ret, salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION, - 'allow_tgt': 'web*', + "allow_tgt": "web*", }, - } + }, ) def test_update_local_specific(self): - ''' + """ Tests the ``update``-function on the minion's local cache. Updates mine functions from kwargs only. - ''' + """ manual_mine_functions = { - 'ip_addr': {'mine_function': 'network.ip_addrs'}, - 'network.ip_addrs': [], - 'kernel': [{'mine_function': 'grains.get'}, 'kernel', {'allow_tgt': 'web*'}], - 'foo.bar': {'allow_tgt': 'G@roles:webserver', 'allow_tgt_type': 'compound'}, + "ip_addr": {"mine_function": "network.ip_addrs"}, + "network.ip_addrs": [], + "kernel": [ + {"mine_function": "grains.get"}, + "kernel", + {"allow_tgt": "web*"}, + ], + "foo.bar": {"allow_tgt": "G@roles:webserver", "allow_tgt_type": "compound"}, } - with \ - patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }), \ - patch.dict(mine.__salt__, { - 'config.merge': MagicMock(return_value={}), - 'grains.get': lambda x: 'Linux!!', - 'network.ip_addrs': MagicMock(return_value=self.ip_ret), - 'foo.bar': MagicMock(return_value=self.foo_ret), - }): + with patch.dict( + mine.__opts__, {"file_client": "local", "id": "webserver"} + ), patch.dict( + mine.__salt__, + { + "config.merge": MagicMock(return_value={}), + "grains.get": lambda x: "Linux!!", + "network.ip_addrs": MagicMock(return_value=self.ip_ret), + "foo.bar": MagicMock(return_value=self.foo_ret), + }, + ): ret = mine.update(mine_functions=manual_mine_functions) - self.assertEqual(ret, 'FakeCache:StoreSuccess!') + self.assertEqual(ret, "FakeCache:StoreSuccess!") # Check if the mine entries have been stored properly in the FakeCache. self.assertEqual( - self.cache.fetch('minions/webserver', 'mine_cache'), + self.cache.fetch("minions/webserver", "mine_cache"), { - 'ip_addr': self.ip_ret, - 'network.ip_addrs': self.ip_ret, - 'foo.bar': { + "ip_addr": self.ip_ret, + "network.ip_addrs": self.ip_ret, + "foo.bar": { salt.utils.mine.MINE_ITEM_ACL_DATA: self.foo_ret, salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION, - 'allow_tgt': 'G@roles:webserver', - 'allow_tgt_type': 'compound', + "allow_tgt": "G@roles:webserver", + "allow_tgt_type": "compound", }, - 'kernel': { - salt.utils.mine.MINE_ITEM_ACL_DATA: 'Linux!!', + "kernel": { + salt.utils.mine.MINE_ITEM_ACL_DATA: "Linux!!", salt.utils.mine.MINE_ITEM_ACL_ID: salt.utils.mine.MINE_ITEM_ACL_VERSION, - 'allow_tgt': 'web*', + "allow_tgt": "web*", }, - } + }, ) def test_update_master(self): - ''' + """ Tests whether the ``update``-function sends the correct data to the master. - ''' + """ config_mine_functions = { - 'ip_addr': {'mine_function': 'network.ip_addrs'}, - 'network.ip_addrs': [], - 'kernel': [{'mine_function': 'grains.get'}, 'kernel'], - 'foo.bar': {}, + "ip_addr": {"mine_function": "network.ip_addrs"}, + "network.ip_addrs": [], + "kernel": [{"mine_function": "grains.get"}, "kernel"], + "foo.bar": {}, } mock_load = { - 'id': 'webserver', - 'cmd': '_mine', - 'data': { - 'ip_addr': self.ip_ret, - 'network.ip_addrs': self.ip_ret, - 'foo.bar': self.foo_ret, - 'kernel': self.kernel_ret, + "id": "webserver", + "cmd": "_mine", + "data": { + "ip_addr": self.ip_ret, + "network.ip_addrs": self.ip_ret, + "foo.bar": self.foo_ret, + "kernel": self.kernel_ret, }, - 'clear': False, + "clear": False, } - with \ - patch.object(mine, '_mine_send', MagicMock(side_effect=lambda x, y: x)),\ - patch.dict(mine.__opts__, { - 'file_client': 'remote', - 'id': 'webserver', - }), \ - patch.dict(mine.__salt__, { - 'config.merge': MagicMock(return_value=config_mine_functions), - 'grains.get': lambda x: self.kernel_ret, - 'network.ip_addrs': MagicMock(return_value=self.ip_ret), - 'foo.bar': MagicMock(return_value=self.foo_ret), - }): + with patch.object( + mine, "_mine_send", MagicMock(side_effect=lambda x, y: x) + ), patch.dict( + mine.__opts__, {"file_client": "remote", "id": "webserver"} + ), patch.dict( + mine.__salt__, + { + "config.merge": MagicMock(return_value=config_mine_functions), + "grains.get": lambda x: self.kernel_ret, + "network.ip_addrs": MagicMock(return_value=self.ip_ret), + "foo.bar": MagicMock(return_value=self.foo_ret), + }, + ): # Verify the correct load - self.assertEqual( - mine.update(), - mock_load - ) + self.assertEqual(mine.update(), mock_load) def test_delete_local(self): - ''' + """ Tests the ``delete``-function on the minion's local cache. - ''' + """ # Prefill minion cache with a non-ACL value - self.cache.store('minions/webserver', 'mine_cache', {'foobard': 'barfood'}) - with patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }): - ret = mine.delete('foobard') - self.assertEqual( - self.cache.fetch('minions/webserver', 'mine_cache'), - {} - ) + self.cache.store("minions/webserver", "mine_cache", {"foobard": "barfood"}) + with patch.dict(mine.__opts__, {"file_client": "local", "id": "webserver"}): + ret = mine.delete("foobard") + self.assertEqual(self.cache.fetch("minions/webserver", "mine_cache"), {}) def test_delete_master(self): - ''' + """ Tests whether the ``delete``-function sends the correct data to the master. - ''' + """ # Prefill minion cache with a non-ACL value - self.cache.store('minions/webserver', 'mine_cache', {'foobard': 'barfood'}) + self.cache.store("minions/webserver", "mine_cache", {"foobard": "barfood"}) mock_load = { - 'cmd': '_mine_delete', - 'fun': 'foobard', - 'id': 'foo', + "cmd": "_mine_delete", + "fun": "foobard", + "id": "foo", } - with patch.object(mine, '_mine_send', MagicMock(side_effect=lambda x, y: x)),\ - patch.dict(mine.__opts__, { - 'file_client': 'remote', - 'id': 'foo', - }): + with patch.object( + mine, "_mine_send", MagicMock(side_effect=lambda x, y: x) + ), patch.dict(mine.__opts__, {"file_client": "remote", "id": "foo"}): # Verify the correct load - self.assertEqual( - mine.delete('foobard'), - mock_load - ) + self.assertEqual(mine.delete("foobard"), mock_load) def test_flush_local(self): - ''' + """ Tests the ``flush``-function on the minion's local cache. - ''' + """ # Prefill minion cache with a non-ACL value - self.cache.store('minions/webserver', 'mine_cache', {'foobard': 'barfood'}) - with patch.dict(mine.__opts__, { - 'file_client': 'local', - 'id': 'webserver', - }): + self.cache.store("minions/webserver", "mine_cache", {"foobard": "barfood"}) + with patch.dict(mine.__opts__, {"file_client": "local", "id": "webserver"}): ret = mine.flush() - self.assertEqual( - self.cache.fetch('minions/webserver', 'mine_cache'), - {} - ) + self.assertEqual(self.cache.fetch("minions/webserver", "mine_cache"), {}) def test_flush_master(self): - ''' + """ Tests whether the ``flush``-function sends the correct data to the master. - ''' - mock_load = { - 'cmd': '_mine_flush', - 'id': 'foo' - } - with patch.object(mine, '_mine_send', MagicMock(side_effect=lambda x, y: x)),\ - patch.dict(mine.__opts__, { - 'file_client': 'remote', - 'id': 'foo', - }): + """ + mock_load = {"cmd": "_mine_flush", "id": "foo"} + with patch.object( + mine, "_mine_send", MagicMock(side_effect=lambda x, y: x) + ), patch.dict(mine.__opts__, {"file_client": "remote", "id": "foo"}): # Verify the correct load - self.assertEqual( - mine.flush(), - mock_load - ) + self.assertEqual(mine.flush(), mock_load) def test_valid(self): - ''' + """ Tests the ``valid``-function. Note that mine functions defined as list are returned in dict format. Mine functions that do not exist in __salt__ are not returned. - ''' + """ config_mine_functions = { - 'network.ip_addrs': [], - 'kernel': [{'mine_function': 'grains.get'}, 'kernel'], - 'fubar': [{'mine_function': 'does.not_exist'}], + "network.ip_addrs": [], + "kernel": [{"mine_function": "grains.get"}, "kernel"], + "fubar": [{"mine_function": "does.not_exist"}], } - with \ - patch.dict(mine.__salt__, { - 'config.merge': MagicMock(return_value=config_mine_functions), - 'network.ip_addrs': lambda: True, - 'grains.get': lambda: True, - }): + with patch.dict( + mine.__salt__, + { + "config.merge": MagicMock(return_value=config_mine_functions), + "network.ip_addrs": lambda: True, + "grains.get": lambda: True, + }, + ): self.assertEqual( mine.valid(), - { - 'network.ip_addrs': [], - 'kernel': {'grains.get': ['kernel']}, - } + {"network.ip_addrs": [], "kernel": {"grains.get": ["kernel"]}}, ) def test_get_docker(self): - ''' + """ Test for Get all mine data for 'docker.ps' and run an aggregation. - ''' + """ ps_response = { - 'localhost': { - 'host': { - 'interfaces': { - 'docker0': { - 'hwaddr': '88:99:00:00:99:99', - 'inet': [{'address': '172.17.42.1', - 'broadcast': None, - 'label': 'docker0', - 'netmask': '255.255.0.0'}], - 'inet6': [{'address': 'ffff::eeee:aaaa:bbbb:8888', - 'prefixlen': '64'}], - 'up': True}, - 'eth0': {'hwaddr': '88:99:00:99:99:99', - 'inet': [{'address': '192.168.0.1', - 'broadcast': '192.168.0.255', - 'label': 'eth0', - 'netmask': '255.255.255.0'}], - 'inet6': [{'address': - 'ffff::aaaa:aaaa:bbbb:8888', - 'prefixlen': '64'}], - 'up': True}, - }}, - 'abcdefhjhi1234567899': { # container Id - 'Ports': [{'IP': '0.0.0.0', # we bind on every interfaces - 'PrivatePort': 80, - 'PublicPort': 80, - 'Type': 'tcp'}], - 'Image': 'image:latest', - 'Info': {'Id': 'abcdefhjhi1234567899'}, + "localhost": { + "host": { + "interfaces": { + "docker0": { + "hwaddr": "88:99:00:00:99:99", + "inet": [ + { + "address": "172.17.42.1", + "broadcast": None, + "label": "docker0", + "netmask": "255.255.0.0", + } + ], + "inet6": [ + { + "address": "ffff::eeee:aaaa:bbbb:8888", + "prefixlen": "64", + } + ], + "up": True, + }, + "eth0": { + "hwaddr": "88:99:00:99:99:99", + "inet": [ + { + "address": "192.168.0.1", + "broadcast": "192.168.0.255", + "label": "eth0", + "netmask": "255.255.255.0", + } + ], + "inet6": [ + { + "address": "ffff::aaaa:aaaa:bbbb:8888", + "prefixlen": "64", + } + ], + "up": True, + }, + } }, - }} - with patch.object(mine, 'get', return_value=ps_response): + "abcdefhjhi1234567899": { # container Id + "Ports": [ + { + "IP": "0.0.0.0", # we bind on every interfaces + "PrivatePort": 80, + "PublicPort": 80, + "Type": "tcp", + } + ], + "Image": "image:latest", + "Info": {"Id": "abcdefhjhi1234567899"}, + }, + } + } + with patch.object(mine, "get", return_value=ps_response): ret = mine.get_docker() # Sort ifaces since that will change between py2 and py3 - ret['image:latest']['ipv4'][80] = sorted(ret['image:latest']['ipv4'][80]) - self.assertEqual(ret, - {'image:latest': { - 'ipv4': {80: sorted([ - '172.17.42.1:80', - '192.168.0.1:80', - ])}}}) + ret["image:latest"]["ipv4"][80] = sorted(ret["image:latest"]["ipv4"][80]) + self.assertEqual( + ret, + { + "image:latest": { + "ipv4": {80: sorted(["172.17.42.1:80", "192.168.0.1:80"])} + } + }, + ) def test_get_docker_with_container_id(self): - ''' + """ Test for Get all mine data for 'docker.ps' and run an aggregation. - ''' + """ ps_response = { - 'localhost': { - 'host': { - 'interfaces': { - 'docker0': { - 'hwaddr': '88:99:00:00:99:99', - 'inet': [{'address': '172.17.42.1', - 'broadcast': None, - 'label': 'docker0', - 'netmask': '255.255.0.0'}], - 'inet6': [{'address': 'ffff::eeee:aaaa:bbbb:8888', - 'prefixlen': '64'}], - 'up': True}, - 'eth0': {'hwaddr': '88:99:00:99:99:99', - 'inet': [{'address': '192.168.0.1', - 'broadcast': '192.168.0.255', - 'label': 'eth0', - 'netmask': '255.255.255.0'}], - 'inet6': [{'address': - 'ffff::aaaa:aaaa:bbbb:8888', - 'prefixlen': '64'}], - 'up': True}, - }}, - 'abcdefhjhi1234567899': { # container Id - 'Ports': [{'IP': '0.0.0.0', # we bind on every interfaces - 'PrivatePort': 80, - 'PublicPort': 80, - 'Type': 'tcp'}], - 'Image': 'image:latest', - 'Info': {'Id': 'abcdefhjhi1234567899'}, - }, - }} - with patch.object(mine, 'get', return_value=ps_response): + "localhost": { + "host": { + "interfaces": { + "docker0": { + "hwaddr": "88:99:00:00:99:99", + "inet": [ + { + "address": "172.17.42.1", + "broadcast": None, + "label": "docker0", + "netmask": "255.255.0.0", + } + ], + "inet6": [ + { + "address": "ffff::eeee:aaaa:bbbb:8888", + "prefixlen": "64", + } + ], + "up": True, + }, + "eth0": { + "hwaddr": "88:99:00:99:99:99", + "inet": [ + { + "address": "192.168.0.1", + "broadcast": "192.168.0.255", + "label": "eth0", + "netmask": "255.255.255.0", + } + ], + "inet6": [ + { + "address": "ffff::aaaa:aaaa:bbbb:8888", + "prefixlen": "64", + } + ], + "up": True, + }, + } + }, + "abcdefhjhi1234567899": { # container Id + "Ports": [ + { + "IP": "0.0.0.0", # we bind on every interfaces + "PrivatePort": 80, + "PublicPort": 80, + "Type": "tcp", + } + ], + "Image": "image:latest", + "Info": {"Id": "abcdefhjhi1234567899"}, + }, + } + } + with patch.object(mine, "get", return_value=ps_response): ret = mine.get_docker(with_container_id=True) # Sort ifaces since that will change between py2 and py3 - ret['image:latest']['ipv4'][80] = sorted(ret['image:latest']['ipv4'][80]) - self.assertEqual(ret, - {'image:latest': { - 'ipv4': {80: sorted([ - ('172.17.42.1:80', 'abcdefhjhi1234567899'), - ('192.168.0.1:80', 'abcdefhjhi1234567899'), - ])}}}) + ret["image:latest"]["ipv4"][80] = sorted(ret["image:latest"]["ipv4"][80]) + self.assertEqual( + ret, + { + "image:latest": { + "ipv4": { + 80: sorted( + [ + ("172.17.42.1:80", "abcdefhjhi1234567899"), + ("192.168.0.1:80", "abcdefhjhi1234567899"), + ] + ) + } + } + }, + ) diff --git a/tests/unit/modules/test_mod_random.py b/tests/unit/modules/test_mod_random.py index b1fbec69ff9..5edf09f33f9 100644 --- a/tests/unit/modules/test_mod_random.py +++ b/tests/unit/modules/test_mod_random.py @@ -1,18 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, -) - # Import Salt Libs import salt.modules.mod_random as mod_random import salt.utils.pycrypto @@ -21,6 +14,11 @@ from salt.exceptions import SaltInvocationError # Import 3rd-party libs from salt.ext import six +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase, skipIf + def _test_hashlib(): try: @@ -29,9 +27,9 @@ def _test_hashlib(): return False if six.PY2: - algorithms_attr_name = 'algorithms' + algorithms_attr_name = "algorithms" else: - algorithms_attr_name = 'algorithms_guaranteed' + algorithms_attr_name = "algorithms_guaranteed" if not hasattr(hashlib, algorithms_attr_name): return False @@ -42,51 +40,47 @@ def _test_hashlib(): SUPPORTED_HASHLIB = _test_hashlib() -@skipIf(not SUPPORTED_HASHLIB, 'Hashlib does not contain needed functionality') +@skipIf(not SUPPORTED_HASHLIB, "Hashlib does not contain needed functionality") class ModrandomTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.mod_random - ''' + """ + def setup_loader_modules(self): return {mod_random: {}} def test_hash(self): - ''' + """ Test for Encodes a value with the specified encoder. - ''' - self.assertEqual(mod_random.hash('value')[0:4], 'ec2c') + """ + self.assertEqual(mod_random.hash("value")[0:4], "ec2c") - self.assertRaises(SaltInvocationError, - mod_random.hash, 'value', 'algorithm') + self.assertRaises(SaltInvocationError, mod_random.hash, "value", "algorithm") def test_str_encode(self): - ''' + """ Test for The value to be encoded. - ''' - self.assertRaises(SaltInvocationError, - mod_random.str_encode, 'None', 'abc') + """ + self.assertRaises(SaltInvocationError, mod_random.str_encode, "None", "abc") - self.assertRaises(SaltInvocationError, - mod_random.str_encode, None) + self.assertRaises(SaltInvocationError, mod_random.str_encode, None) if six.PY2: - self.assertEqual(mod_random.str_encode('A'), 'QQ==\n') + self.assertEqual(mod_random.str_encode("A"), "QQ==\n") else: # We're using the base64 module which does not include the trailing new line - self.assertEqual(mod_random.str_encode('A'), 'QQ==') + self.assertEqual(mod_random.str_encode("A"), "QQ==") def test_get_str(self): - ''' + """ Test for Returns a random string of the specified length. - ''' - with patch.object(salt.utils.pycrypto, - 'secure_password', return_value='A'): - self.assertEqual(mod_random.get_str(), 'A') + """ + with patch.object(salt.utils.pycrypto, "secure_password", return_value="A"): + self.assertEqual(mod_random.get_str(), "A") def test_shadow_hash(self): - ''' + """ Test for Generates a salted hash suitable for /etc/shadow. - ''' - with patch.object(salt.utils.pycrypto, - 'gen_hash', return_value='A'): - self.assertEqual(mod_random.shadow_hash(), 'A') + """ + with patch.object(salt.utils.pycrypto, "gen_hash", return_value="A"): + self.assertEqual(mod_random.shadow_hash(), "A") diff --git a/tests/unit/modules/test_modjk.py b/tests/unit/modules/test_modjk.py index e23d91fd35d..726328e3c37 100644 --- a/tests/unit/modules/test_modjk.py +++ b/tests/unit/modules/test_modjk.py @@ -1,225 +1,231 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, -) - # Import Salt Libs import salt.modules.modjk as modjk +from tests.support.mock import patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase class ModjkTestCase(TestCase): - ''' + """ Test cases for salt.modules.modjk - ''' + """ + # 'version' function tests: 1 def test_version(self): - ''' + """ Test for return the modjk version - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.jk_version': 'mod_jk/1.2.37'}): - self.assertEqual(modjk.version(), '1.2.37') + """ + with patch.object( + modjk, "_do_http", return_value={"worker.jk_version": "mod_jk/1.2.37"} + ): + self.assertEqual(modjk.version(), "1.2.37") # 'get_running' function tests: 1 def test_get_running(self): - ''' + """ Test for get the current running config (not from disk) - ''' - with patch.object(modjk, '_do_http', return_value={}): + """ + with patch.object(modjk, "_do_http", return_value={}): self.assertDictEqual(modjk.get_running(), {}) # 'dump_config' function tests: 1 def test_dump_config(self): - ''' + """ Test for dump the original configuration that was loaded from disk - ''' - with patch.object(modjk, '_do_http', return_value={}): + """ + with patch.object(modjk, "_do_http", return_value={}): self.assertDictEqual(modjk.dump_config(), {}) # 'list_configured_members' function tests: 1 def test_list_configured_members(self): - ''' + """ Test for return a list of member workers from the configuration files - ''' - with patch.object(modjk, '_do_http', return_value={}): - self.assertListEqual(modjk.list_configured_members('loadbalancer1'), - []) + """ + with patch.object(modjk, "_do_http", return_value={}): + self.assertListEqual(modjk.list_configured_members("loadbalancer1"), []) - with patch.object(modjk, '_do_http', return_value= - {'worker.loadbalancer1.balance_workers': 'SALT'}): - self.assertListEqual(modjk.list_configured_members('loadbalancer1'), - ['SALT']) + with patch.object( + modjk, + "_do_http", + return_value={"worker.loadbalancer1.balance_workers": "SALT"}, + ): + self.assertListEqual( + modjk.list_configured_members("loadbalancer1"), ["SALT"] + ) # 'workers' function tests: 1 def test_workers(self): - ''' + """ Test for return a list of member workers and their status - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.list': 'Salt1,Salt2'}): + """ + with patch.object( + modjk, "_do_http", return_value={"worker.list": "Salt1,Salt2"} + ): self.assertDictEqual(modjk.workers(), {}) # 'recover_all' function tests: 1 def test_recover_all(self): - ''' + """ Test for set the all the workers in lbn to recover and activate them if they are not - ''' - with patch.object(modjk, '_do_http', return_value={}): - self.assertDictEqual(modjk.recover_all('loadbalancer1'), {}) + """ + with patch.object(modjk, "_do_http", return_value={}): + self.assertDictEqual(modjk.recover_all("loadbalancer1"), {}) - with patch.object(modjk, '_do_http', return_value= - {'worker.loadbalancer1.balance_workers': 'SALT'}): - with patch.object(modjk, 'worker_status', - return_value={'activation': 'ACT', - 'state': 'OK'}): - self.assertDictEqual(modjk.recover_all('loadbalancer1'), - {'SALT': {'activation': 'ACT', - 'state': 'OK'}}) + with patch.object( + modjk, + "_do_http", + return_value={"worker.loadbalancer1.balance_workers": "SALT"}, + ): + with patch.object( + modjk, + "worker_status", + return_value={"activation": "ACT", "state": "OK"}, + ): + self.assertDictEqual( + modjk.recover_all("loadbalancer1"), + {"SALT": {"activation": "ACT", "state": "OK"}}, + ) # 'reset_stats' function tests: 1 def test_reset_stats(self): - ''' + """ Test for reset all runtime statistics for the load balancer - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.reset_stats('loadbalancer1')) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue(modjk.reset_stats("loadbalancer1")) # 'lb_edit' function tests: 1 def test_lb_edit(self): - ''' + """ Test for edit the loadbalancer settings - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.lb_edit('loadbalancer1', {'vlr': 1, - 'vlt': 60})) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue(modjk.lb_edit("loadbalancer1", {"vlr": 1, "vlt": 60})) # 'bulk_stop' function tests: 1 def test_bulk_stop(self): - ''' + """ Test for stop all the given workers in the specific load balancer - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.bulk_stop(["node1", "node2", "node3"], - 'loadbalancer1')) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue( + modjk.bulk_stop(["node1", "node2", "node3"], "loadbalancer1") + ) # 'bulk_activate' function tests: 1 def test_bulk_activate(self): - ''' + """ Test for activate all the given workers in the specific load balancer - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.bulk_activate(["node1", "node2", "node3"], - 'loadbalancer1')) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue( + modjk.bulk_activate(["node1", "node2", "node3"], "loadbalancer1") + ) # 'bulk_disable' function tests: 1 def test_bulk_disable(self): - ''' + """ Test for disable all the given workers in the specific load balancer - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.bulk_disable(["node1", "node2", "node3"], - 'loadbalancer1')) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue( + modjk.bulk_disable(["node1", "node2", "node3"], "loadbalancer1") + ) # 'bulk_recover' function tests: 1 def test_bulk_recover(self): - ''' + """ Test for recover all the given workers in the specific load balancer - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.bulk_recover(["node1", "node2", "node3"], - 'loadbalancer1')) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue( + modjk.bulk_recover(["node1", "node2", "node3"], "loadbalancer1") + ) # 'worker_status' function tests: 1 def test_worker_status(self): - ''' + """ Test for return the state of the worker - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.node1.activation': 'ACT', - 'worker.node1.state': 'OK'}): - self.assertDictEqual(modjk.worker_status("node1"), - {'activation': 'ACT', 'state': 'OK'}) + """ + with patch.object( + modjk, + "_do_http", + return_value={"worker.node1.activation": "ACT", "worker.node1.state": "OK"}, + ): + self.assertDictEqual( + modjk.worker_status("node1"), {"activation": "ACT", "state": "OK"} + ) - with patch.object(modjk, '_do_http', return_value={}): + with patch.object(modjk, "_do_http", return_value={}): self.assertFalse(modjk.worker_status("node1")) # 'worker_recover' function tests: 1 def test_worker_recover(self): - ''' + """ Test for set the worker to recover this module will fail if it is in OK state - ''' - with patch.object(modjk, '_do_http', return_value={}): - self.assertDictEqual(modjk.worker_recover("node1", 'loadbalancer1'), - {}) + """ + with patch.object(modjk, "_do_http", return_value={}): + self.assertDictEqual(modjk.worker_recover("node1", "loadbalancer1"), {}) # 'worker_disable' function tests: 1 def test_worker_disable(self): - ''' + """ Test for set the worker to disable state in the lbn load balancer - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.worker_disable('node1', 'loadbalancer1')) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue(modjk.worker_disable("node1", "loadbalancer1")) # 'worker_activate' function tests: 1 def test_worker_activate(self): - ''' + """ Test for set the worker to activate state in the lbn load balancer - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.worker_activate('node1', 'loadbalancer1')) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue(modjk.worker_activate("node1", "loadbalancer1")) # 'worker_stop' function tests: 1 def test_worker_stop(self): - ''' + """ Test for set the worker to stopped state in the lbn load balancer - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.worker_stop('node1', 'loadbalancer1')) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue(modjk.worker_stop("node1", "loadbalancer1")) # 'worker_edit' function tests: 1 def test_worker_edit(self): - ''' + """ Test for edit the worker settings - ''' - with patch.object(modjk, '_do_http', return_value= - {'worker.result.type': 'OK'}): - self.assertTrue(modjk.worker_edit('node1', 'loadbalancer1', - {'vwf': 500, 'vwd': 60})) + """ + with patch.object(modjk, "_do_http", return_value={"worker.result.type": "OK"}): + self.assertTrue( + modjk.worker_edit("node1", "loadbalancer1", {"vwf": 500, "vwd": 60}) + ) diff --git a/tests/unit/modules/test_monit.py b/tests/unit/modules/test_monit.py index 9eb3f5a2005..973b5afa6de 100644 --- a/tests/unit/modules/test_monit.py +++ b/tests/unit/modules/test_monit.py @@ -1,147 +1,129 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.monit as monit +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MonitTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.aptpkg - ''' + """ + def setup_loader_modules(self): return {monit: {}} def test_start(self): - ''' + """ Test for start - ''' - with patch.dict(monit.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(monit.start('name')) + """ + with patch.dict(monit.__salt__, {"cmd.retcode": MagicMock(return_value=False)}): + self.assertTrue(monit.start("name")) def test_stop(self): - ''' + """ Test for Stops service via monit - ''' - with patch.dict(monit.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(monit.stop('name')) + """ + with patch.dict(monit.__salt__, {"cmd.retcode": MagicMock(return_value=False)}): + self.assertTrue(monit.stop("name")) def test_restart(self): - ''' + """ Test for Restart service via monit - ''' - with patch.dict(monit.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(monit.restart('name')) + """ + with patch.dict(monit.__salt__, {"cmd.retcode": MagicMock(return_value=False)}): + self.assertTrue(monit.restart("name")) def test_unmonitor(self): - ''' + """ Test for Unmonitor service via monit - ''' - with patch.dict(monit.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(monit.unmonitor('name')) + """ + with patch.dict(monit.__salt__, {"cmd.retcode": MagicMock(return_value=False)}): + self.assertTrue(monit.unmonitor("name")) def test_monitor(self): - ''' + """ Test for monitor service via monit - ''' - with patch.dict(monit.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(monit.monitor('name')) + """ + with patch.dict(monit.__salt__, {"cmd.retcode": MagicMock(return_value=False)}): + self.assertTrue(monit.monitor("name")) def test_summary(self): - ''' + """ Test for Display a summary from monit - ''' - mock = MagicMock(side_effect=['daemon is not running', - 'A\nB\nC\nD\nE']) - with patch.dict(monit.__salt__, {'cmd.run': mock}): - self.assertEqual(monit.summary(), - {'monit': 'daemon is not running', - 'result': False}) + """ + mock = MagicMock(side_effect=["daemon is not running", "A\nB\nC\nD\nE"]) + with patch.dict(monit.__salt__, {"cmd.run": mock}): + self.assertEqual( + monit.summary(), {"monit": "daemon is not running", "result": False} + ) self.assertEqual(monit.summary(), {}) def test_status(self): - ''' + """ Test for Display a process status from monit - ''' - with patch.dict(monit.__salt__, - {'cmd.run': - MagicMock(return_value='Process')}): - self.assertEqual(monit.status('service'), 'No such service') + """ + with patch.dict(monit.__salt__, {"cmd.run": MagicMock(return_value="Process")}): + self.assertEqual(monit.status("service"), "No such service") def test_reload(self): - ''' + """ Test for Reload configuration - ''' + """ mock = MagicMock(return_value=0) - with patch.dict(monit.__salt__, {'cmd.retcode': mock}): + with patch.dict(monit.__salt__, {"cmd.retcode": mock}): self.assertTrue(monit.reload_()) def test_version(self): - ''' + """ Test for Display version from monit -V - ''' + """ mock = MagicMock(return_value="This is Monit version 5.14\nA\nB") - with patch.dict(monit.__salt__, {'cmd.run': mock}): - self.assertEqual(monit.version(), '5.14') + with patch.dict(monit.__salt__, {"cmd.run": mock}): + self.assertEqual(monit.version(), "5.14") def test_id(self): - ''' + """ Test for Display unique id - ''' - mock = MagicMock( - return_value='Monit ID: d3b1aba48527dd599db0e86f5ad97120') - with patch.dict(monit.__salt__, {'cmd.run': mock}): - self.assertEqual(monit.id_(), 'd3b1aba48527dd599db0e86f5ad97120') + """ + mock = MagicMock(return_value="Monit ID: d3b1aba48527dd599db0e86f5ad97120") + with patch.dict(monit.__salt__, {"cmd.run": mock}): + self.assertEqual(monit.id_(), "d3b1aba48527dd599db0e86f5ad97120") def test_reset_id(self): - ''' + """ Test for Regenerate a unique id - ''' - expected = { - 'stdout': 'Monit id d3b1aba48527dd599db0e86f5ad97120 and ...' - } + """ + expected = {"stdout": "Monit id d3b1aba48527dd599db0e86f5ad97120 and ..."} mock = MagicMock(return_value=expected) - with patch.dict(monit.__salt__, {'cmd.run_all': mock}): - self.assertEqual(monit.id_(reset=True), - 'd3b1aba48527dd599db0e86f5ad97120') + with patch.dict(monit.__salt__, {"cmd.run_all": mock}): + self.assertEqual(monit.id_(reset=True), "d3b1aba48527dd599db0e86f5ad97120") def test_configtest(self): - ''' + """ Test for Check configuration syntax - ''' - excepted = { - 'stdout': 'Control file syntax OK', - 'retcode': 0, - 'stderr': '' - } + """ + excepted = {"stdout": "Control file syntax OK", "retcode": 0, "stderr": ""} mock = MagicMock(return_value=excepted) - with patch.dict(monit.__salt__, {'cmd.run_all': mock}): - self.assertTrue(monit.configtest()['result']) - self.assertEqual(monit.configtest()['comment'], 'Syntax OK') + with patch.dict(monit.__salt__, {"cmd.run_all": mock}): + self.assertTrue(monit.configtest()["result"]) + self.assertEqual(monit.configtest()["comment"], "Syntax OK") def test_validate(self): - ''' + """ Test for Check all services are monitored - ''' + """ mock = MagicMock(return_value=0) - with patch.dict(monit.__salt__, {'cmd.retcode': mock}): + with patch.dict(monit.__salt__, {"cmd.retcode": mock}): self.assertTrue(monit.validate()) diff --git a/tests/unit/modules/test_moosefs.py b/tests/unit/modules/test_moosefs.py index 2fbb1b0bcbe..4bfb4d97e58 100644 --- a/tests/unit/modules/test_moosefs.py +++ b/tests/unit/modules/test_moosefs.py @@ -1,27 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.moosefs as moosefs +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MoosefsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.moosefs - ''' + """ def setup_loader_modules(self): return {moosefs: {}} @@ -29,39 +26,39 @@ class MoosefsTestCase(TestCase, LoaderModuleMockMixin): # 'dirinfo' function tests: 1i def test_dirinfo(self): - ''' + """ Test if it return information on a directory located on the Moose - ''' - mock = MagicMock(return_value={'stdout': 'Salt:salt'}) - with patch.dict(moosefs.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(moosefs.dirinfo('/tmp/salt'), {'Salt': 'salt'}) + """ + mock = MagicMock(return_value={"stdout": "Salt:salt"}) + with patch.dict(moosefs.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual(moosefs.dirinfo("/tmp/salt"), {"Salt": "salt"}) # 'fileinfo' function tests: 1 def test_fileinfo(self): - ''' + """ Test if it returns information on a file located on the Moose - ''' - mock = MagicMock(return_value={'stdout': ''}) - with patch.dict(moosefs.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(moosefs.fileinfo('/tmp/salt'), {}) + """ + mock = MagicMock(return_value={"stdout": ""}) + with patch.dict(moosefs.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual(moosefs.fileinfo("/tmp/salt"), {}) # 'mounts' function tests: 1 def test_mounts(self): - ''' + """ Test if it returns a list of current MooseFS mounts - ''' - mock = MagicMock(return_value={'stdout': ''}) - with patch.dict(moosefs.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"stdout": ""}) + with patch.dict(moosefs.__salt__, {"cmd.run_all": mock}): self.assertDictEqual(moosefs.mounts(), {}) # 'getgoal' function tests: 1 def test_getgoal(self): - ''' + """ Test if it returns goal(s) for a file or directory - ''' - mock = MagicMock(return_value={'stdout': 'Salt: salt'}) - with patch.dict(moosefs.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(moosefs.getgoal('/tmp/salt'), {'goal': 'salt'}) + """ + mock = MagicMock(return_value={"stdout": "Salt: salt"}) + with patch.dict(moosefs.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual(moosefs.getgoal("/tmp/salt"), {"goal": "salt"}) diff --git a/tests/unit/modules/test_mount.py b/tests/unit/modules/test_mount.py index 7703e19ce9d..5116849d972 100644 --- a/tests/unit/modules/test_mount.py +++ b/tests/unit/modules/test_mount.py @@ -1,157 +1,173 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import textwrap -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - mock_open, - MagicMock, - patch, -) +import salt.modules.mount as mount # Import Salt Libs import salt.utils.files import salt.utils.path -import salt.modules.mount as mount from salt.exceptions import CommandExecutionError -MOCK_SHELL_FILE = 'A B C D F G\n' +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + +MOCK_SHELL_FILE = "A B C D F G\n" class MountTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.mount - ''' + """ + def setup_loader_modules(self): return {mount: {}} def test_active(self): - ''' + """ List the active mounts. - ''' - with patch.dict(mount.__grains__, {'os': 'FreeBSD', 'kernel': 'FreeBSD'}): + """ + with patch.dict(mount.__grains__, {"os": "FreeBSD", "kernel": "FreeBSD"}): # uid=user1 tests the improbable case where a OS returns a name # instead of a numeric id, for #25293 - mock = MagicMock(return_value='A B C D,E,F,uid=user1,gid=grp1') - mock_user = MagicMock(return_value={'uid': '100'}) - mock_group = MagicMock(return_value={'gid': '100'}) - with patch.dict(mount.__salt__, {'cmd.run_stdout': mock, - 'user.info': mock_user, - 'group.info': mock_group}): - self.assertEqual(mount.active(), {'B': - {'device': 'A', - 'opts': ['D', 'E', 'F', - 'uid=100', - 'gid=100'], - 'fstype': 'C'}}) + mock = MagicMock(return_value="A B C D,E,F,uid=user1,gid=grp1") + mock_user = MagicMock(return_value={"uid": "100"}) + mock_group = MagicMock(return_value={"gid": "100"}) + with patch.dict( + mount.__salt__, + { + "cmd.run_stdout": mock, + "user.info": mock_user, + "group.info": mock_group, + }, + ): + self.assertEqual( + mount.active(), + { + "B": { + "device": "A", + "opts": ["D", "E", "F", "uid=100", "gid=100"], + "fstype": "C", + } + }, + ) - with patch.dict(mount.__grains__, {'os': 'Solaris', 'kernel': 'SunOS'}): - mock = MagicMock(return_value='A * B * C D/E/F') - with patch.dict(mount.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(mount.active(), {'B': - {'device': 'A', - 'opts': ['D', 'E', 'F'], - 'fstype': 'C'}}) + with patch.dict(mount.__grains__, {"os": "Solaris", "kernel": "SunOS"}): + mock = MagicMock(return_value="A * B * C D/E/F") + with patch.dict(mount.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual( + mount.active(), + {"B": {"device": "A", "opts": ["D", "E", "F"], "fstype": "C"}}, + ) - with patch.dict(mount.__grains__, {'os': 'AIX', 'kernel': 'AIX'}): - mock = MagicMock(return_value='A * B * C D/E/F') - with patch.dict(mount.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(mount.active(), - {'B': {'node': 'A', - 'device': '*', - 'fstype': '*'}}) + with patch.dict(mount.__grains__, {"os": "AIX", "kernel": "AIX"}): + mock = MagicMock(return_value="A * B * C D/E/F") + with patch.dict(mount.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual( + mount.active(), {"B": {"node": "A", "device": "*", "fstype": "*"}} + ) - with patch.dict(mount.__grains__, {'os': 'OpenBSD', 'kernel': 'OpenBSD'}): + with patch.dict(mount.__grains__, {"os": "OpenBSD", "kernel": "OpenBSD"}): mock = MagicMock(return_value={}) - with patch.object(mount, '_active_mounts_openbsd', mock): + with patch.object(mount, "_active_mounts_openbsd", mock): self.assertEqual(mount.active(), {}) - with patch.dict(mount.__grains__, {'os': 'MacOS', 'kernel': 'Darwin'}): + with patch.dict(mount.__grains__, {"os": "MacOS", "kernel": "Darwin"}): mock = MagicMock(return_value={}) - with patch.object(mount, '_active_mounts_darwin', mock): + with patch.object(mount, "_active_mounts_darwin", mock): self.assertEqual(mount.active(), {}) - with patch.dict(mount.__grains__, {'os': 'MacOS', 'kernel': 'Darwin'}): + with patch.dict(mount.__grains__, {"os": "MacOS", "kernel": "Darwin"}): mock = MagicMock(return_value={}) - with patch.object(mount, '_active_mountinfo', mock): - with patch.object(mount, '_active_mounts_darwin', mock): + with patch.object(mount, "_active_mountinfo", mock): + with patch.object(mount, "_active_mounts_darwin", mock): self.assertEqual(mount.active(extended=True), {}) - with patch.dict(mount.__grains__, {'os': 'AIX', 'kernel': 'AIX'}): + with patch.dict(mount.__grains__, {"os": "AIX", "kernel": "AIX"}): mock = MagicMock(return_value={}) - with patch.object(mount, '_active_mounts_aix', mock): + with patch.object(mount, "_active_mounts_aix", mock): self.assertEqual(mount.active(), {}) def test_fstab(self): - ''' + """ List the content of the fstab - ''' + """ mock = MagicMock(return_value=False) - with patch.object(os.path, 'isfile', mock): + with patch.object(os.path, "isfile", mock): self.assertEqual(mount.fstab(), {}) - file_data = '\n'.join(['#', 'A B C D,E,F G H']) + file_data = "\n".join(["#", "A B C D,E,F G H"]) mock = MagicMock(return_value=True) - with patch.dict(mount.__grains__, {'kernel': ''}), \ - patch.object(os.path, 'isfile', mock), \ - patch('salt.utils.files.fopen', mock_open(read_data=file_data)): + with patch.dict(mount.__grains__, {"kernel": ""}), patch.object( + os.path, "isfile", mock + ), patch("salt.utils.files.fopen", mock_open(read_data=file_data)): fstab = mount.fstab() assert fstab == { - 'B': {'device': 'A', - 'dump': 'G', - 'fstype': 'C', - 'opts': ['D', 'E', 'F'], - 'pass': 'H'} + "B": { + "device": "A", + "dump": "G", + "fstype": "C", + "opts": ["D", "E", "F"], + "pass": "H", + } }, fstab def test_vfstab(self): - ''' + """ List the content of the vfstab - ''' + """ mock = MagicMock(return_value=False) - with patch.object(os.path, 'isfile', mock): + with patch.object(os.path, "isfile", mock): self.assertEqual(mount.vfstab(), {}) - file_data = textwrap.dedent('''\ + file_data = textwrap.dedent( + """\ # swap - /tmp tmpfs - yes size=2048m - ''') + """ + ) mock = MagicMock(return_value=True) - with patch.dict(mount.__grains__, {'kernel': 'SunOS'}), \ - patch.object(os.path, 'isfile', mock), \ - patch('salt.utils.files.fopen', mock_open(read_data=file_data)): + with patch.dict(mount.__grains__, {"kernel": "SunOS"}), patch.object( + os.path, "isfile", mock + ), patch("salt.utils.files.fopen", mock_open(read_data=file_data)): vfstab = mount.vfstab() assert vfstab == { - '/tmp': {'device': 'swap', - 'device_fsck': '-', - 'fstype': 'tmpfs', - 'mount_at_boot': 'yes', - 'opts': ['size=2048m'], - 'pass_fsck': '-'} + "/tmp": { + "device": "swap", + "device_fsck": "-", + "fstype": "tmpfs", + "mount_at_boot": "yes", + "opts": ["size=2048m"], + "pass_fsck": "-", + } }, vfstab def test_filesystems(self): - ''' + """ List the content of the filesystems - ''' - file_data = textwrap.dedent('''\ + """ + file_data = textwrap.dedent( + """\ # - ''') + """ + ) mock = MagicMock(return_value=True) - with patch.dict(mount.__grains__, {'os': 'AIX', 'kernel': 'AIX'}), \ - patch.object(os.path, 'isfile', mock), \ - patch('salt.utils.files.fopen', mock_open(read_data=file_data)): + with patch.dict(mount.__grains__, {"os": "AIX", "kernel": "AIX"}), patch.object( + os.path, "isfile", mock + ), patch("salt.utils.files.fopen", mock_open(read_data=file_data)): self.assertEqual(mount.filesystems(), {}) - file_data = textwrap.dedent('''\ + file_data = textwrap.dedent( + """\ # /home: dev = /dev/hd1 @@ -163,538 +179,595 @@ class MountTestCase(TestCase, LoaderModuleMockMixin): free = false quota = no - ''') + """ + ) mock = MagicMock(return_value=True) - with patch.dict(mount.__grains__, {'os': 'AIX', 'kernel': 'AIX'}), \ - patch.object(os.path, 'isfile', mock), \ - patch('salt.utils.files.fopen', mock_open(read_data=file_data)): + with patch.dict(mount.__grains__, {"os": "AIX", "kernel": "AIX"}), patch.object( + os.path, "isfile", mock + ), patch("salt.utils.files.fopen", mock_open(read_data=file_data)): fsyst = mount.filesystems() - test_fsyst = {'/home': {'dev': '/dev/hd1', - 'vfs': 'jfs2', - 'log': '/dev/hd8', - 'mount': 'true', - 'check': 'true', - 'vol': '/home', - 'free': 'false', - 'quota': 'no'}} + test_fsyst = { + "/home": { + "dev": "/dev/hd1", + "vfs": "jfs2", + "log": "/dev/hd8", + "mount": "true", + "check": "true", + "vol": "/home", + "free": "false", + "quota": "no", + } + } self.assertEqual(test_fsyst, fsyst) def test_rm_fstab(self): - ''' + """ Remove the mount point from the fstab - ''' + """ mock_fstab = MagicMock(return_value={}) - with patch.dict(mount.__grains__, {'kernel': ''}): - with patch.object(mount, 'fstab', mock_fstab): - with patch('salt.utils.files.fopen', mock_open()): - self.assertTrue(mount.rm_fstab('name', 'device')) + with patch.dict(mount.__grains__, {"kernel": ""}): + with patch.object(mount, "fstab", mock_fstab): + with patch("salt.utils.files.fopen", mock_open()): + self.assertTrue(mount.rm_fstab("name", "device")) def test_set_fstab(self): - ''' + """ Tests to verify that this mount is represented in the fstab, change the mount to match the data passed, or add the mount if it is not present. - ''' + """ mock = MagicMock(return_value=False) - with patch.object(os.path, 'isfile', mock): - self.assertRaises(CommandExecutionError, - mount.set_fstab, 'A', 'B', 'C') + with patch.object(os.path, "isfile", mock): + self.assertRaises(CommandExecutionError, mount.set_fstab, "A", "B", "C") mock = MagicMock(return_value=True) mock_read = MagicMock(side_effect=OSError) - with patch.object(os.path, 'isfile', mock): - with patch.object(salt.utils.files, 'fopen', mock_read): - self.assertRaises(CommandExecutionError, - mount.set_fstab, 'A', 'B', 'C') + with patch.object(os.path, "isfile", mock): + with patch.object(salt.utils.files, "fopen", mock_read): + self.assertRaises(CommandExecutionError, mount.set_fstab, "A", "B", "C") mock = MagicMock(return_value=True) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', - mock_open(read_data=MOCK_SHELL_FILE)): - self.assertEqual(mount.set_fstab('A', 'B', 'C'), 'new') + with patch.object(os.path, "isfile", mock): + with patch("salt.utils.files.fopen", mock_open(read_data=MOCK_SHELL_FILE)): + self.assertEqual(mount.set_fstab("A", "B", "C"), "new") mock = MagicMock(return_value=True) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', - mock_open(read_data=MOCK_SHELL_FILE)): - self.assertEqual(mount.set_fstab('B', 'A', 'C', 'D', 'F', 'G'), - 'present') + with patch.object(os.path, "isfile", mock): + with patch("salt.utils.files.fopen", mock_open(read_data=MOCK_SHELL_FILE)): + self.assertEqual( + mount.set_fstab("B", "A", "C", "D", "F", "G"), "present" + ) mock = MagicMock(return_value=True) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', - mock_open(read_data=MOCK_SHELL_FILE)): - self.assertEqual(mount.set_fstab('B', 'A', 'C', - not_change=True), - 'present') + with patch.object(os.path, "isfile", mock): + with patch("salt.utils.files.fopen", mock_open(read_data=MOCK_SHELL_FILE)): + self.assertEqual( + mount.set_fstab("B", "A", "C", not_change=True), "present" + ) def test_rm_automaster(self): - ''' + """ Remove the mount point from the auto_master - ''' + """ mock = MagicMock(return_value={}) - with patch.object(mount, 'automaster', mock): - self.assertTrue(mount.rm_automaster('name', 'device')) + with patch.object(mount, "automaster", mock): + self.assertTrue(mount.rm_automaster("name", "device")) - mock = MagicMock(return_value={'name': 'name'}) - with patch.object(mount, 'fstab', mock): - self.assertTrue(mount.rm_automaster('name', 'device')) + mock = MagicMock(return_value={"name": "name"}) + with patch.object(mount, "fstab", mock): + self.assertTrue(mount.rm_automaster("name", "device")) def test_set_automaster(self): - ''' + """ Verify that this mount is represented in the auto_salt, change the mount to match the data passed, or add the mount if it is not present. - ''' + """ mock = MagicMock(return_value=True) - with patch.object(os.path, 'isfile', mock): - self.assertRaises(CommandExecutionError, - mount.set_automaster, - 'A', 'B', 'C') + with patch.object(os.path, "isfile", mock): + self.assertRaises( + CommandExecutionError, mount.set_automaster, "A", "B", "C" + ) mock = MagicMock(return_value=True) mock_read = MagicMock(side_effect=OSError) - with patch.object(os.path, 'isfile', mock): - with patch.object(salt.utils.files, 'fopen', mock_read): - self.assertRaises(CommandExecutionError, - mount.set_automaster, 'A', 'B', 'C') + with patch.object(os.path, "isfile", mock): + with patch.object(salt.utils.files, "fopen", mock_read): + self.assertRaises( + CommandExecutionError, mount.set_automaster, "A", "B", "C" + ) mock = MagicMock(return_value=True) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', - mock_open(read_data=MOCK_SHELL_FILE)): - self.assertEqual(mount.set_automaster('A', 'B', 'C'), 'new') + with patch.object(os.path, "isfile", mock): + with patch("salt.utils.files.fopen", mock_open(read_data=MOCK_SHELL_FILE)): + self.assertEqual(mount.set_automaster("A", "B", "C"), "new") mock = MagicMock(return_value=True) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', - mock_open(read_data='/..A -fstype=C,D C:B')): - self.assertEqual(mount.set_automaster('A', 'B', 'C', 'D'), - 'present') + with patch.object(os.path, "isfile", mock): + with patch( + "salt.utils.files.fopen", mock_open(read_data="/..A -fstype=C,D C:B") + ): + self.assertEqual(mount.set_automaster("A", "B", "C", "D"), "present") mock = MagicMock(return_value=True) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', - mock_open(read_data='/..A -fstype=XX C:B')): - self.assertEqual(mount.set_automaster('A', 'B', 'C', 'D', - not_change=True), - 'present') + with patch.object(os.path, "isfile", mock): + with patch( + "salt.utils.files.fopen", mock_open(read_data="/..A -fstype=XX C:B") + ): + self.assertEqual( + mount.set_automaster("A", "B", "C", "D", not_change=True), "present" + ) def test_automaster(self): - ''' + """ Test the list the contents of the fstab - ''' + """ self.assertDictEqual(mount.automaster(), {}) def test_rm_filesystems(self): - ''' + """ Remove the mount point from the filesystems - ''' - file_data = textwrap.dedent('''\ + """ + file_data = textwrap.dedent( + """\ # - ''') + """ + ) mock = MagicMock(return_value=True) - with patch.dict(mount.__grains__, {'os': 'AIX', 'kernel': 'AIX'}), \ - patch.object(os.path, 'isfile', mock), \ - patch('salt.utils.files.fopen', mock_open(read_data=file_data)): - self.assertFalse(mount.rm_filesystems('name', 'device')) + with patch.dict(mount.__grains__, {"os": "AIX", "kernel": "AIX"}), patch.object( + os.path, "isfile", mock + ), patch("salt.utils.files.fopen", mock_open(read_data=file_data)): + self.assertFalse(mount.rm_filesystems("name", "device")) - file_data = textwrap.dedent('''\ + file_data = textwrap.dedent( + """\ # /name: dev = device vol = /name - ''') + """ + ) mock = MagicMock(return_value=True) mock_fsyst = MagicMock(return_value=True) - with patch.dict(mount.__grains__, {'os': 'AIX', 'kernel': 'AIX'}), \ - patch.object(os.path, 'isfile', mock), \ - patch('salt.utils.files.fopen', mock_open(read_data=file_data)): - self.assertTrue(mount.rm_filesystems('/name', 'device')) + with patch.dict(mount.__grains__, {"os": "AIX", "kernel": "AIX"}), patch.object( + os.path, "isfile", mock + ), patch("salt.utils.files.fopen", mock_open(read_data=file_data)): + self.assertTrue(mount.rm_filesystems("/name", "device")) def test_set_filesystems(self): - ''' + """ Tests to verify that this mount is represented in the filesystems, change the mount to match the data passed, or add the mount if it is not present. - ''' + """ mock = MagicMock(return_value=False) - with patch.dict(mount.__grains__, {'os': 'AIX', 'kernel': 'AIX'}): - with patch.object(os.path, 'isfile', mock): - self.assertRaises(CommandExecutionError, - mount.set_filesystems, 'A', 'B', 'C') + with patch.dict(mount.__grains__, {"os": "AIX", "kernel": "AIX"}): + with patch.object(os.path, "isfile", mock): + self.assertRaises( + CommandExecutionError, mount.set_filesystems, "A", "B", "C" + ) mock_read = MagicMock(side_effect=OSError) - with patch.object(os.path, 'isfile', mock): - with patch.object(salt.utils.files, 'fopen', mock_read): - self.assertRaises(CommandExecutionError, - mount.set_filesystems, 'A', 'B', 'C') + with patch.object(os.path, "isfile", mock): + with patch.object(salt.utils.files, "fopen", mock_read): + self.assertRaises( + CommandExecutionError, mount.set_filesystems, "A", "B", "C" + ) def test_mount(self): - ''' + """ Mount a device - ''' - with patch.dict(mount.__grains__, {'os': 'MacOS'}): + """ + with patch.dict(mount.__grains__, {"os": "MacOS"}): mock = MagicMock(return_value=True) - with patch.object(os.path, 'exists', mock): + with patch.object(os.path, "exists", mock): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'file.mkdir': None}): - mock = MagicMock(return_value={'retcode': True, - 'stderr': True}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.mount('name', 'device')) - mock.assert_called_with('mount device name ', - python_shell=False, runas=None) + with patch.dict(mount.__salt__, {"file.mkdir": None}): + mock = MagicMock(return_value={"retcode": True, "stderr": True}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.mount("name", "device")) + mock.assert_called_with( + "mount device name ", python_shell=False, runas=None + ) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.mount('name', 'device', fstype='fstype')) - mock.assert_called_with('mount -t fstype device name ', - python_shell=False, runas=None) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.mount("name", "device", fstype="fstype")) + mock.assert_called_with( + "mount -t fstype device name ", + python_shell=False, + runas=None, + ) - mock = MagicMock(return_value={'retcode': False, - 'stderr': False}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.mount('name', 'device')) + mock = MagicMock(return_value={"retcode": False, "stderr": False}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.mount("name", "device")) - with patch.dict(mount.__grains__, {'os': 'AIX'}): + with patch.dict(mount.__grains__, {"os": "AIX"}): mock = MagicMock(return_value=True) - with patch.object(os.path, 'exists', mock): + with patch.object(os.path, "exists", mock): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'file.mkdir': None}): - mock = MagicMock(return_value={'retcode': True, - 'stderr': True}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.mount('name', 'device')) - mock.assert_called_with('mount device name ', - python_shell=False, runas=None) + with patch.dict(mount.__salt__, {"file.mkdir": None}): + mock = MagicMock(return_value={"retcode": True, "stderr": True}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.mount("name", "device")) + mock.assert_called_with( + "mount device name ", python_shell=False, runas=None + ) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.mount('name', 'device', fstype='fstype')) - mock.assert_called_with('mount -v fstype device name ', - python_shell=False, runas=None) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.mount("name", "device", fstype="fstype")) + mock.assert_called_with( + "mount -v fstype device name ", + python_shell=False, + runas=None, + ) - mock = MagicMock(return_value={'retcode': False, - 'stderr': False}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.mount('name', 'device')) + mock = MagicMock(return_value={"retcode": False, "stderr": False}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.mount("name", "device")) - with patch.dict(mount.__grains__, {'os': 'Linux'}): + with patch.dict(mount.__grains__, {"os": "Linux"}): mock = MagicMock(return_value=True) - with patch.object(os.path, 'exists', mock): + with patch.object(os.path, "exists", mock): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'file.mkdir': None}): - mock = MagicMock(return_value={'retcode': True, - 'stderr': True}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.mount('name', 'device')) - mock.assert_called_with('mount -o defaults device name ', - python_shell=False, runas=None) + with patch.dict(mount.__salt__, {"file.mkdir": None}): + mock = MagicMock(return_value={"retcode": True, "stderr": True}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.mount("name", "device")) + mock.assert_called_with( + "mount -o defaults device name ", + python_shell=False, + runas=None, + ) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.mount('name', 'device', fstype='fstype')) - mock.assert_called_with('mount -o defaults -t fstype device name ', - python_shell=False, runas=None) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.mount("name", "device", fstype="fstype")) + mock.assert_called_with( + "mount -o defaults -t fstype device name ", + python_shell=False, + runas=None, + ) - mock = MagicMock(return_value={'retcode': False, - 'stderr': False}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.mount('name', 'device')) + mock = MagicMock(return_value={"retcode": False, "stderr": False}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.mount("name", "device")) def test_remount_non_mounted(self): - ''' + """ Attempt to remount a device, if the device is not already mounted, mount is called - ''' - with patch.dict(mount.__grains__, {'os': 'MacOS'}): + """ + with patch.dict(mount.__grains__, {"os": "MacOS"}): mock = MagicMock(return_value=[]) - with patch.object(mount, 'active', mock): + with patch.object(mount, "active", mock): mock = MagicMock(return_value=True) - with patch.object(mount, 'mount', mock): - self.assertTrue(mount.remount('name', 'device')) + with patch.object(mount, "mount", mock): + self.assertTrue(mount.remount("name", "device")) - with patch.dict(mount.__grains__, {'os': 'AIX'}): + with patch.dict(mount.__grains__, {"os": "AIX"}): mock = MagicMock(return_value=[]) - with patch.object(mount, 'active', mock): + with patch.object(mount, "active", mock): mock = MagicMock(return_value=True) - with patch.object(mount, 'mount', mock): - self.assertTrue(mount.remount('name', 'device')) + with patch.object(mount, "mount", mock): + self.assertTrue(mount.remount("name", "device")) - with patch.dict(mount.__grains__, {'os': 'Linux'}): + with patch.dict(mount.__grains__, {"os": "Linux"}): mock = MagicMock(return_value=[]) - with patch.object(mount, 'active', mock): + with patch.object(mount, "active", mock): mock = MagicMock(return_value=True) - with patch.object(mount, 'mount', mock): - self.assertTrue(mount.remount('name', 'device')) + with patch.object(mount, "mount", mock): + self.assertTrue(mount.remount("name", "device")) def test_remount_already_mounted_no_fstype(self): - ''' + """ Attempt to remount a device already mounted that do not provides fstype - ''' - with patch.dict(mount.__grains__, {'os': 'MacOS'}): - mock = MagicMock(return_value=['name']) - with patch.object(mount, 'active', mock): - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.remount('name', 'device')) - mock.assert_called_with('mount -u -o noowners device name ', - python_shell=False, runas=None) + """ + with patch.dict(mount.__grains__, {"os": "MacOS"}): + mock = MagicMock(return_value=["name"]) + with patch.object(mount, "active", mock): + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.remount("name", "device")) + mock.assert_called_with( + "mount -u -o noowners device name ", + python_shell=False, + runas=None, + ) - with patch.dict(mount.__grains__, {'os': 'AIX'}): - mock = MagicMock(return_value=['name']) - with patch.object(mount, 'active', mock): - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.remount('name', 'device')) - mock.assert_called_with('mount -o remount device name ', - python_shell=False, runas=None) + with patch.dict(mount.__grains__, {"os": "AIX"}): + mock = MagicMock(return_value=["name"]) + with patch.object(mount, "active", mock): + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.remount("name", "device")) + mock.assert_called_with( + "mount -o remount device name ", python_shell=False, runas=None + ) - with patch.dict(mount.__grains__, {'os': 'Linux'}): - mock = MagicMock(return_value=['name']) - with patch.object(mount, 'active', mock): - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.remount('name', 'device')) - mock.assert_called_with('mount -o defaults,remount device name ', - python_shell=False, runas=None) + with patch.dict(mount.__grains__, {"os": "Linux"}): + mock = MagicMock(return_value=["name"]) + with patch.object(mount, "active", mock): + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.remount("name", "device")) + mock.assert_called_with( + "mount -o defaults,remount device name ", + python_shell=False, + runas=None, + ) def test_remount_already_mounted_with_fstype(self): - ''' + """ Attempt to remount a device already mounted that do not provides fstype - ''' - with patch.dict(mount.__grains__, {'os': 'MacOS'}): - mock = MagicMock(return_value=['name']) - with patch.object(mount, 'active', mock): - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.remount('name', 'device', fstype='type')) - mock.assert_called_with('mount -u -o noowners -t type device name ', - python_shell=False, runas=None) + """ + with patch.dict(mount.__grains__, {"os": "MacOS"}): + mock = MagicMock(return_value=["name"]) + with patch.object(mount, "active", mock): + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.remount("name", "device", fstype="type")) + mock.assert_called_with( + "mount -u -o noowners -t type device name ", + python_shell=False, + runas=None, + ) - with patch.dict(mount.__grains__, {'os': 'AIX'}): - mock = MagicMock(return_value=['name']) - with patch.object(mount, 'active', mock): - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.remount('name', 'device', fstype='type')) - mock.assert_called_with('mount -o remount -v type device name ', - python_shell=False, runas=None) + with patch.dict(mount.__grains__, {"os": "AIX"}): + mock = MagicMock(return_value=["name"]) + with patch.object(mount, "active", mock): + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.remount("name", "device", fstype="type")) + mock.assert_called_with( + "mount -o remount -v type device name ", + python_shell=False, + runas=None, + ) - with patch.dict(mount.__grains__, {'os': 'Linux'}): - mock = MagicMock(return_value=['name']) - with patch.object(mount, 'active', mock): - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.remount('name', 'device', fstype='type')) - mock.assert_called_with('mount -o defaults,remount -t type device name ', - python_shell=False, runas=None) + with patch.dict(mount.__grains__, {"os": "Linux"}): + mock = MagicMock(return_value=["name"]) + with patch.object(mount, "active", mock): + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.remount("name", "device", fstype="type")) + mock.assert_called_with( + "mount -o defaults,remount -t type device name ", + python_shell=False, + runas=None, + ) def test_umount(self): - ''' + """ Attempt to unmount a device by specifying the directory it is mounted on - ''' + """ mock = MagicMock(return_value={}) - with patch.object(mount, 'active', mock): - self.assertEqual(mount.umount('name'), - 'name does not have anything mounted') + with patch.object(mount, "active", mock): + self.assertEqual( + mount.umount("name"), "name does not have anything mounted" + ) - mock = MagicMock(return_value={'name': 'name'}) - with patch.object(mount, 'active', mock): - mock = MagicMock(return_value={'retcode': True, 'stderr': True}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.umount('name')) + mock = MagicMock(return_value={"name": "name"}) + with patch.object(mount, "active", mock): + mock = MagicMock(return_value={"retcode": True, "stderr": True}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.umount("name")) - mock = MagicMock(return_value={'retcode': False}) - with patch.dict(mount.__salt__, {'cmd.run_all': mock}): - self.assertTrue(mount.umount('name')) + mock = MagicMock(return_value={"retcode": False}) + with patch.dict(mount.__salt__, {"cmd.run_all": mock}): + self.assertTrue(mount.umount("name")) + + # Test unmounting with guestfs util + mock = MagicMock() + with patch.dict(mount.__salt__, {"guestfs.umount": mock}): + mount.umount("/mountpoint", device="/path/to/my.qcow", util="guestfs") + mock.assert_called_once_with("/mountpoint", disk="/path/to/my.qcow") def test_is_fuse_exec(self): - ''' + """ Returns true if the command passed is a fuse mountable application - ''' - with patch.object(salt.utils.path, 'which', return_value=None): - self.assertFalse(mount.is_fuse_exec('cmd')) + """ + with patch.object(salt.utils.path, "which", return_value=None): + self.assertFalse(mount.is_fuse_exec("cmd")) def _ldd_side_effect(cmd, *args, **kwargs): - ''' + """ Neither of these are full ldd output, but what is_fuse_exec is looking for is 'libfuse' in the ldd output, so these examples should be sufficient enough to test both the True and False cases. - ''' + """ return { - 'ldd cmd1': textwrap.dedent('''\ + "ldd cmd1": textwrap.dedent( + """\ linux-vdso.so.1 (0x00007ffeaf5fb000) libfuse3.so.3 => /usr/lib/libfuse3.so.3 (0x00007f91e66ac000) - '''), - 'ldd cmd2': textwrap.dedent('''\ + """ + ), + "ldd cmd2": textwrap.dedent( + """\ linux-vdso.so.1 (0x00007ffeaf5fb000) - ''') + """ + ), }[cmd] + which_mock = MagicMock(side_effect=lambda x: x) ldd_mock = MagicMock(side_effect=_ldd_side_effect) - with patch.object(salt.utils.path, 'which', which_mock): - with patch.dict(mount.__salt__, {'cmd.run': _ldd_side_effect}): - self.assertTrue(mount.is_fuse_exec('cmd1')) - self.assertFalse(mount.is_fuse_exec('cmd2')) + with patch.object(salt.utils.path, "which", which_mock): + with patch.dict(mount.__salt__, {"cmd.run": _ldd_side_effect}): + self.assertTrue(mount.is_fuse_exec("cmd1")) + self.assertFalse(mount.is_fuse_exec("cmd2")) def test_swaps(self): - ''' + """ Return a dict containing information on active swap - ''' - file_data = textwrap.dedent('''\ + """ + file_data = textwrap.dedent( + """\ Filename Type Size Used Priority /dev/sda1 partition 31249404 4100 -1 - ''') - with patch.dict(mount.__grains__, {'os': '', 'kernel': ''}): - with patch('salt.utils.files.fopen', mock_open(read_data=file_data)): + """ + ) + with patch.dict(mount.__grains__, {"os": "", "kernel": ""}): + with patch("salt.utils.files.fopen", mock_open(read_data=file_data)): swaps = mount.swaps() assert swaps == { - '/dev/sda1': { - 'priority': '-1', - 'size': '31249404', - 'type': 'partition', - 'used': '4100'} + "/dev/sda1": { + "priority": "-1", + "size": "31249404", + "type": "partition", + "used": "4100", + } }, swaps - file_data = textwrap.dedent('''\ + file_data = textwrap.dedent( + """\ Device Size Used Unknown Unknown Priority /dev/sda1 31249404 4100 unknown unknown -1 - ''') + """ + ) mock = MagicMock(return_value=file_data) - with patch.dict(mount.__grains__, {'os': 'OpenBSD', 'kernel': 'OpenBSD'}), \ - patch.dict(mount.__salt__, {'cmd.run_stdout': mock}): + with patch.dict( + mount.__grains__, {"os": "OpenBSD", "kernel": "OpenBSD"} + ), patch.dict(mount.__salt__, {"cmd.run_stdout": mock}): swaps = mount.swaps() assert swaps == { - '/dev/sda1': { - 'priority': '-1', - 'size': '31249404', - 'type': 'partition', - 'used': '4100'} + "/dev/sda1": { + "priority": "-1", + "size": "31249404", + "type": "partition", + "used": "4100", + } }, swaps - file_data = textwrap.dedent('''\ + file_data = textwrap.dedent( + """\ device maj,min total free /dev/hd6 10, 2 11776MB 11765MB - ''') + """ + ) mock = MagicMock(return_value=file_data) - with patch.dict(mount.__grains__, {'os': 'AIX', 'kernel': 'AIX'}), \ - patch.dict(mount.__salt__, {'cmd.run_stdout': mock}): + with patch.dict(mount.__grains__, {"os": "AIX", "kernel": "AIX"}), patch.dict( + mount.__salt__, {"cmd.run_stdout": mock} + ): swaps = mount.swaps() assert swaps == { - '/dev/hd6': { - 'priority': '-', - 'size': 12058624, - 'type': 'device', - 'used': 11264} + "/dev/hd6": { + "priority": "-", + "size": 12058624, + "type": "device", + "used": 11264, + } }, swaps def test_swapon(self): - ''' + """ Activate a swap disk - ''' - mock = MagicMock(return_value={'name': 'name'}) - with patch.dict(mount.__grains__, {'kernel': ''}): - with patch.object(mount, 'swaps', mock): - self.assertEqual(mount.swapon('name'), - {'stats': 'name', 'new': False}) + """ + mock = MagicMock(return_value={"name": "name"}) + with patch.dict(mount.__grains__, {"kernel": ""}): + with patch.object(mount, "swaps", mock): + self.assertEqual(mount.swapon("name"), {"stats": "name", "new": False}) mock = MagicMock(return_value={}) - with patch.dict(mount.__grains__, {'kernel': ''}): - with patch.object(mount, 'swaps', mock): + with patch.dict(mount.__grains__, {"kernel": ""}): + with patch.object(mount, "swaps", mock): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'cmd.run': mock}): - self.assertEqual(mount.swapon('name', False), {}) + with patch.dict(mount.__salt__, {"cmd.run": mock}): + self.assertEqual(mount.swapon("name", False), {}) - mock = MagicMock(side_effect=[{}, {'name': 'name'}]) - with patch.dict(mount.__grains__, {'kernel': ''}): - with patch.object(mount, 'swaps', mock): + mock = MagicMock(side_effect=[{}, {"name": "name"}]) + with patch.dict(mount.__grains__, {"kernel": ""}): + with patch.object(mount, "swaps", mock): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'cmd.run': mock}): - self.assertEqual(mount.swapon('name'), {'stats': 'name', - 'new': True}) + with patch.dict(mount.__salt__, {"cmd.run": mock}): + self.assertEqual( + mount.swapon("name"), {"stats": "name", "new": True} + ) ## effects of AIX - mock = MagicMock(return_value={'name': 'name'}) - with patch.dict(mount.__grains__, {'kernel': 'AIX'}): - with patch.object(mount, 'swaps', mock): - self.assertEqual(mount.swapon('name'), - {'stats': 'name', 'new': False}) + mock = MagicMock(return_value={"name": "name"}) + with patch.dict(mount.__grains__, {"kernel": "AIX"}): + with patch.object(mount, "swaps", mock): + self.assertEqual(mount.swapon("name"), {"stats": "name", "new": False}) mock = MagicMock(return_value={}) - with patch.dict(mount.__grains__, {'kernel': 'AIX'}): - with patch.object(mount, 'swaps', mock): + with patch.dict(mount.__grains__, {"kernel": "AIX"}): + with patch.object(mount, "swaps", mock): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'cmd.run': mock}): - self.assertEqual(mount.swapon('name', False), {}) + with patch.dict(mount.__salt__, {"cmd.run": mock}): + self.assertEqual(mount.swapon("name", False), {}) - mock = MagicMock(side_effect=[{}, {'name': 'name'}]) - with patch.dict(mount.__grains__, {'kernel': 'AIX'}): - with patch.object(mount, 'swaps', mock): + mock = MagicMock(side_effect=[{}, {"name": "name"}]) + with patch.dict(mount.__grains__, {"kernel": "AIX"}): + with patch.object(mount, "swaps", mock): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'cmd.run': mock}): - self.assertEqual(mount.swapon('name'), {'stats': 'name', - 'new': True}) + with patch.dict(mount.__salt__, {"cmd.run": mock}): + self.assertEqual( + mount.swapon("name"), {"stats": "name", "new": True} + ) def test_swapoff(self): - ''' + """ Deactivate a named swap mount - ''' + """ mock = MagicMock(return_value={}) - with patch.dict(mount.__grains__, {'kernel': ''}): - with patch.object(mount, 'swaps', mock): - self.assertEqual(mount.swapoff('name'), None) + with patch.dict(mount.__grains__, {"kernel": ""}): + with patch.object(mount, "swaps", mock): + self.assertEqual(mount.swapoff("name"), None) - mock = MagicMock(return_value={'name': 'name'}) - with patch.dict(mount.__grains__, {'kernel': ''}): - with patch.object(mount, 'swaps', mock): - with patch.dict(mount.__grains__, {'os': 'test'}): + mock = MagicMock(return_value={"name": "name"}) + with patch.dict(mount.__grains__, {"kernel": ""}): + with patch.object(mount, "swaps", mock): + with patch.dict(mount.__grains__, {"os": "test"}): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'cmd.run': mock}): - self.assertFalse(mount.swapoff('name')) + with patch.dict(mount.__salt__, {"cmd.run": mock}): + self.assertFalse(mount.swapoff("name")) - mock = MagicMock(side_effect=[{'name': 'name'}, {}]) - with patch.dict(mount.__grains__, {'kernel': ''}): - with patch.object(mount, 'swaps', mock): - with patch.dict(mount.__grains__, {'os': 'test'}): + mock = MagicMock(side_effect=[{"name": "name"}, {}]) + with patch.dict(mount.__grains__, {"kernel": ""}): + with patch.object(mount, "swaps", mock): + with patch.dict(mount.__grains__, {"os": "test"}): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'cmd.run': mock}): - self.assertTrue(mount.swapoff('name')) + with patch.dict(mount.__salt__, {"cmd.run": mock}): + self.assertTrue(mount.swapoff("name")) # check on AIX mock = MagicMock(return_value={}) - with patch.dict(mount.__grains__, {'kernel': 'AIX'}): - with patch.object(mount, 'swaps', mock): - self.assertEqual(mount.swapoff('name'), None) + with patch.dict(mount.__grains__, {"kernel": "AIX"}): + with patch.object(mount, "swaps", mock): + self.assertEqual(mount.swapoff("name"), None) - mock = MagicMock(return_value={'name': 'name'}) - with patch.dict(mount.__grains__, {'kernel': 'AIX'}): - with patch.object(mount, 'swaps', mock): - with patch.dict(mount.__grains__, {'os': 'test'}): + mock = MagicMock(return_value={"name": "name"}) + with patch.dict(mount.__grains__, {"kernel": "AIX"}): + with patch.object(mount, "swaps", mock): + with patch.dict(mount.__grains__, {"os": "test"}): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'cmd.run': mock}): - self.assertFalse(mount.swapoff('name')) + with patch.dict(mount.__salt__, {"cmd.run": mock}): + self.assertFalse(mount.swapoff("name")) - mock = MagicMock(side_effect=[{'name': 'name'}, {}]) - with patch.dict(mount.__grains__, {'kernel': 'AIX'}): - with patch.object(mount, 'swaps', mock): - with patch.dict(mount.__grains__, {'os': 'test'}): + mock = MagicMock(side_effect=[{"name": "name"}, {}]) + with patch.dict(mount.__grains__, {"kernel": "AIX"}): + with patch.object(mount, "swaps", mock): + with patch.dict(mount.__grains__, {"os": "test"}): mock = MagicMock(return_value=None) - with patch.dict(mount.__salt__, {'cmd.run': mock}): - self.assertTrue(mount.swapoff('name')) + with patch.dict(mount.__salt__, {"cmd.run": mock}): + self.assertTrue(mount.swapoff("name")) def test_is_mounted(self): - ''' + """ Provide information if the path is mounted - ''' + """ mock = MagicMock(return_value={}) - with patch.object(mount, 'active', mock), \ - patch.dict(mount.__grains__, {'kernel': ''}): - self.assertFalse(mount.is_mounted('name')) + with patch.object(mount, "active", mock), patch.dict( + mount.__grains__, {"kernel": ""} + ): + self.assertFalse(mount.is_mounted("name")) - mock = MagicMock(return_value={'name': 'name'}) - with patch.object(mount, 'active', mock), \ - patch.dict(mount.__grains__, {'kernel': ''}): - self.assertTrue(mount.is_mounted('name')) + mock = MagicMock(return_value={"name": "name"}) + with patch.object(mount, "active", mock), patch.dict( + mount.__grains__, {"kernel": ""} + ): + self.assertTrue(mount.is_mounted("name")) diff --git a/tests/unit/modules/test_munin.py b/tests/unit/modules/test_munin.py index eba929efd8e..b2c76e4dc75 100644 --- a/tests/unit/modules/test_munin.py +++ b/tests/unit/modules/test_munin.py @@ -1,61 +1,59 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.munin as munin +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MuninTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.munin - ''' + """ + def setup_loader_modules(self): return {munin: {}} # 'run' function tests: 1 def test_run(self): - ''' + """ Test if it runs one or more named munin plugins - ''' - mock = MagicMock(return_value='uptime.value 0.01') - with patch.dict(munin.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.munin.list_plugins', - MagicMock(return_value=['uptime'])): - self.assertDictEqual(munin.run('uptime'), - {'uptime': {'uptime': 0.01}}) + """ + mock = MagicMock(return_value="uptime.value 0.01") + with patch.dict(munin.__salt__, {"cmd.run": mock}), patch( + "salt.modules.munin.list_plugins", MagicMock(return_value=["uptime"]) + ): + self.assertDictEqual(munin.run("uptime"), {"uptime": {"uptime": 0.01}}) # 'run_all' function tests: 1 def test_run_all(self): - ''' + """ Test if it runs all the munin plugins - ''' - mock = MagicMock(return_value='uptime.value 0.01') - with patch.dict(munin.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.munin.list_plugins', - MagicMock(return_value=['uptime'])): - self.assertDictEqual(munin.run_all(), {'uptime': {'uptime': 0.01}}) + """ + mock = MagicMock(return_value="uptime.value 0.01") + with patch.dict(munin.__salt__, {"cmd.run": mock}), patch( + "salt.modules.munin.list_plugins", MagicMock(return_value=["uptime"]) + ): + self.assertDictEqual(munin.run_all(), {"uptime": {"uptime": 0.01}}) # 'list_plugins' function tests: 1 def test_list_plugins(self): - ''' + """ Test if it list all the munin plugins - ''' - with patch('salt.modules.munin.list_plugins', - MagicMock(return_value=['uptime'])): - self.assertListEqual(munin.list_plugins(), ['uptime']) + """ + with patch( + "salt.modules.munin.list_plugins", MagicMock(return_value=["uptime"]) + ): + self.assertListEqual(munin.list_plugins(), ["uptime"]) diff --git a/tests/unit/modules/test_mysql.py b/tests/unit/modules/test_mysql.py index 4529336220d..cb72389ef7a 100644 --- a/tests/unit/modules/test_mysql.py +++ b/tests/unit/modules/test_mysql.py @@ -1,551 +1,731 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place (mp@saltstack.com) tests.unit.modules.mysql ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch, call +import logging # Import salt libs import salt.modules.mysql as mysql +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase, skipIf + +log = logging.getLogger(__name__) NO_MYSQL = False +NO_PyMYSQL = False try: import MySQLdb # pylint: disable=W0611 -except Exception: # pylint: disable=broad-except +except ImportError: NO_MYSQL = True +try: + # MySQLdb import failed, try to import PyMySQL + import pymysql +except ImportError: + NO_PyMYSQL = True + __all_privileges__ = [ - 'ALTER', - 'ALTER ROUTINE', - 'BACKUP_ADMIN', - 'BINLOG_ADMIN', - 'CONNECTION_ADMIN', - 'CREATE', - 'CREATE ROLE', - 'CREATE ROUTINE', - 'CREATE TABLESPACE', - 'CREATE TEMPORARY TABLES', - 'CREATE USER', - 'CREATE VIEW', - 'DELETE', - 'DROP', - 'DROP ROLE', - 'ENCRYPTION_KEY_ADMIN', - 'EVENT', - 'EXECUTE', - 'FILE', - 'GROUP_REPLICATION_ADMIN', - 'INDEX', - 'INSERT', - 'LOCK TABLES', - 'PERSIST_RO_VARIABLES_ADMIN', - 'PROCESS', - 'REFERENCES', - 'RELOAD', - 'REPLICATION CLIENT', - 'REPLICATION SLAVE', - 'REPLICATION_SLAVE_ADMIN', - 'RESOURCE_GROUP_ADMIN', - 'RESOURCE_GROUP_USER', - 'ROLE_ADMIN', - 'SELECT', - 'SET_USER_ID', - 'SHOW DATABASES', - 'SHOW VIEW', - 'SHUTDOWN', - 'SUPER', - 'SYSTEM_VARIABLES_ADMIN', - 'TRIGGER', - 'UPDATE', - 'XA_RECOVER_ADMIN' + "ALTER", + "ALTER ROUTINE", + "BACKUP_ADMIN", + "BINLOG_ADMIN", + "CONNECTION_ADMIN", + "CREATE", + "CREATE ROLE", + "CREATE ROUTINE", + "CREATE TABLESPACE", + "CREATE TEMPORARY TABLES", + "CREATE USER", + "CREATE VIEW", + "DELETE", + "DROP", + "DROP ROLE", + "ENCRYPTION_KEY_ADMIN", + "EVENT", + "EXECUTE", + "FILE", + "GROUP_REPLICATION_ADMIN", + "INDEX", + "INSERT", + "LOCK TABLES", + "PERSIST_RO_VARIABLES_ADMIN", + "PROCESS", + "REFERENCES", + "RELOAD", + "REPLICATION CLIENT", + "REPLICATION SLAVE", + "REPLICATION_SLAVE_ADMIN", + "RESOURCE_GROUP_ADMIN", + "RESOURCE_GROUP_USER", + "ROLE_ADMIN", + "SELECT", + "SET_USER_ID", + "SHOW DATABASES", + "SHOW VIEW", + "SHUTDOWN", + "SUPER", + "SYSTEM_VARIABLES_ADMIN", + "TRIGGER", + "UPDATE", + "XA_RECOVER_ADMIN", ] -@skipIf(NO_MYSQL, 'Install MySQL bindings before running MySQL unit tests.') -class MySQLTestCase(TestCase, LoaderModuleMockMixin): +class MockMySQLConnect(object): + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + def autocommit(self, *args, **kwards): + return True + + +@skipIf(NO_MYSQL, "Install MySQL bindings before running MySQL unit tests.") +class MySQLTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return {mysql: {}} def test_user_exists(self): - ''' + """ Test to see if mysql module properly forms the MySQL query to see if a user exists Do it before test_user_create_when_user_exists mocks the user_exists call - ''' - with patch.object(mysql, 'version', return_value='8.0.10'): - self._test_call(mysql.user_exists, - {'sql': ('SELECT User,Host FROM mysql.user WHERE ' - 'User = %(user)s AND Host = %(host)s AND ' - 'Password = PASSWORD(%(password)s)'), - 'sql_args': {'host': 'localhost', - 'password': 'BLUECOW', - 'user': 'mytestuser' - } - }, - user='mytestuser', - host='localhost', - password='BLUECOW' + """ + with patch.object(mysql, "version", return_value="8.0.10"): + self._test_call( + mysql.user_exists, + { + "sql": ( + "SELECT User,Host FROM mysql.user WHERE " + "User = %(user)s AND Host = %(host)s AND " + "Password = PASSWORD(%(password)s)" + ), + "sql_args": { + "host": "localhost", + "password": "BLUECOW", + "user": "mytestuser", + }, + }, + user="mytestuser", + host="localhost", + password="BLUECOW", ) - with patch.object(mysql, 'version', return_value='10.1.38-MariaDB'): - self._test_call(mysql.user_exists, - {'sql': ('SELECT User,Host FROM mysql.user WHERE ' - 'User = %(user)s AND Host = %(host)s AND ' - 'Password = PASSWORD(%(password)s)'), - 'sql_args': {'host': 'localhost', - 'password': 'BLUECOW', - 'user': 'mytestuser' - } - }, - user='mytestuser', - host='localhost', - password='BLUECOW' + with patch.object(mysql, "version", return_value="10.1.38-MariaDB"): + self._test_call( + mysql.user_exists, + { + "sql": ( + "SELECT User,Host FROM mysql.user WHERE " + "User = %(user)s AND Host = %(host)s AND " + "Password = PASSWORD(%(password)s)" + ), + "sql_args": { + "host": "localhost", + "password": "BLUECOW", + "user": "mytestuser", + }, + }, + user="mytestuser", + host="localhost", + password="BLUECOW", ) - with patch.object(mysql, 'version', return_value='8.0.11'): - self._test_call(mysql.user_exists, - {'sql': ('SELECT User,Host FROM mysql.user WHERE ' - 'User = %(user)s AND Host = %(host)s'), - 'sql_args': {'host': 'localhost', - 'user': 'mytestuser' - } - }, - user='mytestuser', - host='localhost', - password='BLUECOW' + with patch.object(mysql, "version", return_value="8.0.11"): + self._test_call( + mysql.user_exists, + { + "sql": ( + "SELECT User,Host FROM mysql.user WHERE " + "User = %(user)s AND Host = %(host)s" + ), + "sql_args": {"host": "localhost", "user": "mytestuser"}, + }, + user="mytestuser", + host="localhost", + password="BLUECOW", ) - with patch.object(mysql, 'version', return_value='8.0.11'): - with patch.object(mysql, '__get_auth_plugin', MagicMock(return_value='mysql_native_password')): - self._test_call(mysql.user_exists, - {'sql': ('SELECT User,Host FROM mysql.user WHERE ' - 'User = %(user)s AND Host = %(host)s AND ' - 'Password = %(password)s'), - 'sql_args': {'host': '%', - 'password': '*1A01CF8FBE6425398935FB90359AD8B817399102', - 'user': 'mytestuser' - } - }, - user='mytestuser', - host='%', - password='BLUECOW' + with patch.object(mysql, "version", return_value="8.0.11"): + with patch.object( + mysql, + "__get_auth_plugin", + MagicMock(return_value="mysql_native_password"), + ): + self._test_call( + mysql.user_exists, + { + "sql": ( + "SELECT User,Host FROM mysql.user WHERE " + "User = %(user)s AND Host = %(host)s AND " + "Password = %(password)s" + ), + "sql_args": { + "host": "%", + "password": "*1A01CF8FBE6425398935FB90359AD8B817399102", + "user": "mytestuser", + }, + }, + user="mytestuser", + host="%", + password="BLUECOW", ) - with patch.object(mysql, 'version', return_value='10.2.21-MariaDB'): - self._test_call(mysql.user_exists, - {'sql': ('SELECT User,Host FROM mysql.user WHERE ' - 'User = %(user)s AND Host = %(host)s AND ' - 'Password = PASSWORD(%(password)s)'), - 'sql_args': {'host': 'localhost', - 'password': 'BLUECOW', - 'user': 'mytestuser' - } - }, - user='mytestuser', - host='localhost', - password='BLUECOW' + with patch.object(mysql, "version", return_value="10.2.21-MariaDB"): + self._test_call( + mysql.user_exists, + { + "sql": ( + "SELECT User,Host FROM mysql.user WHERE " + "User = %(user)s AND Host = %(host)s AND " + "Password = PASSWORD(%(password)s)" + ), + "sql_args": { + "host": "localhost", + "password": "BLUECOW", + "user": "mytestuser", + }, + }, + user="mytestuser", + host="localhost", + password="BLUECOW", ) # test_user_create_when_user_exists(self): # ensure we don't try to create a user when one already exists # mock the version of MySQL - with patch.object(mysql, 'version', return_value='8.0.10'): - with patch.object(mysql, 'user_exists', MagicMock(return_value=True)): - with patch.dict(mysql.__salt__, {'config.option': MagicMock()}): - ret = mysql.user_create('testuser') + with patch.object(mysql, "version", return_value="8.0.10"): + with patch.object(mysql, "user_exists", MagicMock(return_value=True)): + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): + ret = mysql.user_create("testuser") self.assertEqual(False, ret) # test_user_create_when_user_exists(self): # ensure we don't try to create a user when one already exists # mock the version of MySQL - with patch.object(mysql, 'version', return_value='8.0.11'): - with patch.object(mysql, 'user_exists', MagicMock(return_value=True)): - with patch.object(mysql, 'verify_login', MagicMock(return_value=True)): - with patch.dict(mysql.__salt__, {'config.option': MagicMock()}): - ret = mysql.user_create('testuser') + with patch.object(mysql, "version", return_value="8.0.11"): + with patch.object(mysql, "user_exists", MagicMock(return_value=True)): + with patch.object(mysql, "verify_login", MagicMock(return_value=True)): + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): + ret = mysql.user_create("testuser") self.assertEqual(False, ret) def test_user_create(self): - ''' + """ Test the creation of a MySQL user in mysql exec module - ''' - with patch.object(mysql, 'version', return_value='8.0.10'): - with patch.object(mysql, '__get_auth_plugin', MagicMock(return_value='mysql_native_password')): - self._test_call(mysql.user_create, - {'sql': 'CREATE USER %(user)s@%(host)s IDENTIFIED BY %(password)s', - 'sql_args': {'password': 'BLUECOW', - 'user': 'testuser', - 'host': 'localhost', - } - }, - 'testuser', - password='BLUECOW' + """ + with patch.object(mysql, "version", return_value="8.0.10"): + with patch.object( + mysql, + "__get_auth_plugin", + MagicMock(return_value="mysql_native_password"), + ): + self._test_call( + mysql.user_create, + { + "sql": "CREATE USER %(user)s@%(host)s IDENTIFIED BY %(password)s", + "sql_args": { + "password": "BLUECOW", + "user": "testuser", + "host": "localhost", + }, + }, + "testuser", + password="BLUECOW", ) - with patch.object(mysql, 'version', return_value='8.0.11'): - with patch.object(mysql, '__get_auth_plugin', MagicMock(return_value='mysql_native_password')): - self._test_call(mysql.user_create, - {'sql': 'CREATE USER %(user)s@%(host)s IDENTIFIED WITH %(auth_plugin)s BY %(password)s', - 'sql_args': {'password': 'BLUECOW', - 'auth_plugin': 'mysql_native_password', - 'user': 'testuser', - 'host': 'localhost', - } - }, - 'testuser', - password='BLUECOW' + with patch.object(mysql, "version", return_value="8.0.11"): + with patch.object( + mysql, + "__get_auth_plugin", + MagicMock(return_value="mysql_native_password"), + ): + self._test_call( + mysql.user_create, + { + "sql": "CREATE USER %(user)s@%(host)s IDENTIFIED WITH %(auth_plugin)s BY %(password)s", + "sql_args": { + "password": "BLUECOW", + "auth_plugin": "mysql_native_password", + "user": "testuser", + "host": "localhost", + }, + }, + "testuser", + password="BLUECOW", ) # Test creating a user with passwordless=True and unix_socket=True - with patch.object(mysql, 'version', return_value='8.0.10'): - with patch.object(mysql, 'plugin_status', MagicMock(return_value='ACTIVE')): - self._test_call(mysql.user_create, - {'sql': 'CREATE USER %(user)s@%(host)s IDENTIFIED WITH auth_socket', - 'sql_args': {'user': 'testuser', - 'host': 'localhost', - } - }, - 'testuser', - allow_passwordless=True, - unix_socket=True, + with patch.object(mysql, "version", return_value="8.0.10"): + with patch.object(mysql, "plugin_status", MagicMock(return_value="ACTIVE")): + self._test_call( + mysql.user_create, + { + "sql": "CREATE USER %(user)s@%(host)s IDENTIFIED WITH auth_socket", + "sql_args": {"user": "testuser", "host": "localhost"}, + }, + "testuser", + allow_passwordless=True, + unix_socket=True, ) - with patch.object(mysql, 'version', return_value='10.2.21-MariaDB'): - with patch.object(mysql, 'plugin_status', MagicMock(return_value='ACTIVE')): - self._test_call(mysql.user_create, - {'sql': 'CREATE USER %(user)s@%(host)s IDENTIFIED VIA unix_socket', - 'sql_args': {'user': 'testuser', - 'host': 'localhost', - } - }, - 'testuser', - allow_passwordless=True, - unix_socket=True, + with patch.object(mysql, "version", return_value="10.2.21-MariaDB"): + with patch.object(mysql, "plugin_status", MagicMock(return_value="ACTIVE")): + self._test_call( + mysql.user_create, + { + "sql": "CREATE USER %(user)s@%(host)s IDENTIFIED VIA unix_socket", + "sql_args": {"user": "testuser", "host": "localhost"}, + }, + "testuser", + allow_passwordless=True, + unix_socket=True, ) def test_user_chpass(self): - ''' + """ Test changing a MySQL user password in mysql exec module - ''' + """ connect_mock = MagicMock() - with patch.object(mysql, '_connect', connect_mock): - with patch.object(mysql, 'version', return_value='8.0.10'): - with patch.object(mysql, 'user_exists', MagicMock(return_value=True)): - with patch.dict(mysql.__salt__, {'config.option': MagicMock()}): - mysql.user_chpass('testuser', password='BLUECOW') + with patch.object(mysql, "_connect", connect_mock): + with patch.object(mysql, "version", return_value="8.0.10"): + with patch.object(mysql, "user_exists", MagicMock(return_value=True)): + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): + mysql.user_chpass("testuser", password="BLUECOW") calls = ( - call().cursor().execute( - 'UPDATE mysql.user SET Password=PASSWORD(%(password)s) WHERE User=%(user)s AND Host = %(host)s;', - {'password': 'BLUECOW', - 'user': 'testuser', - 'host': 'localhost', - } + call() + .cursor() + .execute( + "UPDATE mysql.user SET Password=PASSWORD(%(password)s) WHERE User=%(user)s AND Host = %(host)s;", + { + "password": "BLUECOW", + "user": "testuser", + "host": "localhost", + }, ), - call().cursor().execute('FLUSH PRIVILEGES;'), + call().cursor().execute("FLUSH PRIVILEGES;"), ) connect_mock.assert_has_calls(calls, any_order=True) connect_mock = MagicMock() - with patch.object(mysql, '_connect', connect_mock): - with patch.object(mysql, 'version', return_value='8.0.11'): - with patch.object(mysql, 'user_exists', MagicMock(return_value=True)): - with patch.dict(mysql.__salt__, {'config.option': MagicMock()}): - mysql.user_chpass('testuser', password='BLUECOW') + with patch.object(mysql, "_connect", connect_mock): + with patch.object(mysql, "version", return_value="8.0.11"): + with patch.object(mysql, "user_exists", MagicMock(return_value=True)): + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): + mysql.user_chpass("testuser", password="BLUECOW") calls = ( - call().cursor().execute( + call() + .cursor() + .execute( "ALTER USER %(user)s@%(host)s IDENTIFIED BY %(password)s;", - {'password': 'BLUECOW', - 'user': 'testuser', - 'host': 'localhost', - } + { + "password": "BLUECOW", + "user": "testuser", + "host": "localhost", + }, ), - call().cursor().execute('FLUSH PRIVILEGES;'), + call().cursor().execute("FLUSH PRIVILEGES;"), ) connect_mock.assert_has_calls(calls, any_order=True) def test_user_remove(self): - ''' + """ Test the removal of a MySQL user in mysql exec module - ''' - with patch.object(mysql, 'user_exists', MagicMock(return_value=True)): - self._test_call(mysql.user_remove, - {'sql': 'DROP USER %(user)s@%(host)s', - 'sql_args': {'user': 'testuser', - 'host': 'localhost', - } - }, - 'testuser' + """ + with patch.object(mysql, "user_exists", MagicMock(return_value=True)): + self._test_call( + mysql.user_remove, + { + "sql": "DROP USER %(user)s@%(host)s", + "sql_args": {"user": "testuser", "host": "localhost"}, + }, + "testuser", ) def test_db_check(self): - ''' + """ Test MySQL db check function in mysql exec module - ''' - self._test_call(mysql.db_check, 'CHECK TABLE `test``\'" db`.`my``\'" table`', 'test`\'" db', 'my`\'" table') + """ + self._test_call( + mysql.db_check, + "CHECK TABLE `test``'\" db`.`my``'\" table`", + "test`'\" db", + "my`'\" table", + ) def test_db_repair(self): - ''' + """ Test MySQL db repair function in mysql exec module - ''' - self._test_call(mysql.db_repair, 'REPAIR TABLE `test``\'" db`.`my``\'" table`', 'test`\'" db', 'my`\'" table') + """ + self._test_call( + mysql.db_repair, + "REPAIR TABLE `test``'\" db`.`my``'\" table`", + "test`'\" db", + "my`'\" table", + ) def test_db_optimize(self): - ''' + """ Test MySQL db optimize function in mysql exec module - ''' - self._test_call(mysql.db_optimize, 'OPTIMIZE TABLE `test``\'" db`.`my``\'" table`', 'test`\'" db', 'my`\'" table') + """ + self._test_call( + mysql.db_optimize, + "OPTIMIZE TABLE `test``'\" db`.`my``'\" table`", + "test`'\" db", + "my`'\" table", + ) def test_db_remove(self): - ''' + """ Test MySQL db remove function in mysql exec module - ''' - with patch.object(mysql, 'db_exists', MagicMock(return_value=True)): - self._test_call(mysql.db_remove, 'DROP DATABASE `test``\'" db`;', 'test`\'" db') + """ + with patch.object(mysql, "db_exists", MagicMock(return_value=True)): + self._test_call( + mysql.db_remove, "DROP DATABASE `test``'\" db`;", "test`'\" db" + ) def test_db_tables(self): - ''' + """ Test MySQL db_tables function in mysql exec module - ''' - with patch.object(mysql, 'db_exists', MagicMock(return_value=True)): - self._test_call(mysql.db_tables, 'SHOW TABLES IN `test``\'" db`', 'test`\'" db') + """ + with patch.object(mysql, "db_exists", MagicMock(return_value=True)): + self._test_call( + mysql.db_tables, "SHOW TABLES IN `test``'\" db`", "test`'\" db" + ) def test_db_exists(self): - ''' + """ Test MySQL db_exists function in mysql exec module - ''' + """ self._test_call( mysql.db_exists, - {'sql': 'SHOW DATABASES LIKE %(dbname)s;', - 'sql_args': {'dbname': r'''test%_`" db'''} - }, - 'test%_`" db' + { + "sql": "SHOW DATABASES LIKE %(dbname)s;", + "sql_args": {"dbname": r"""test%_`" db"""}, + }, + 'test%_`" db', ) def test_db_create(self): - ''' + """ Test MySQL db_create function in mysql exec module - ''' + """ self._test_call( mysql.db_create, - 'CREATE DATABASE IF NOT EXISTS `test``\'" db`;', - 'test`\'" db' + "CREATE DATABASE IF NOT EXISTS `test``'\" db`;", + "test`'\" db", ) def test_user_list(self): - ''' + """ Test MySQL user_list function in mysql exec module - ''' - self._test_call(mysql.user_list, 'SELECT User,Host FROM mysql.user') + """ + self._test_call(mysql.user_list, "SELECT User,Host FROM mysql.user") def test_user_info(self): - ''' + """ Test to see if the mysql execution module correctly forms the SQL for information on a MySQL user. - ''' - self._test_call(mysql.user_info, - {'sql': 'SELECT * FROM mysql.user WHERE User = %(user)s AND Host = %(host)s', - 'sql_args': {'host': 'localhost', - 'user': 'mytestuser', - } - }, - 'mytestuser' + """ + self._test_call( + mysql.user_info, + { + "sql": "SELECT * FROM mysql.user WHERE User = %(user)s AND Host = %(host)s", + "sql_args": {"host": "localhost", "user": "mytestuser"}, + }, + "mytestuser", ) def test_user_grants(self): - ''' + """ Test to ensure the mysql user_grants function returns properly formed SQL for a basic query - ''' - with patch.object(mysql, 'user_exists', MagicMock(return_value=True)): - self._test_call(mysql.user_grants, - {'sql': 'SHOW GRANTS FOR %(user)s@%(host)s', - 'sql_args': {'host': 'localhost', - 'user': 'testuser', - } - }, - 'testuser') + """ + with patch.object(mysql, "user_exists", MagicMock(return_value=True)): + self._test_call( + mysql.user_grants, + { + "sql": "SHOW GRANTS FOR %(user)s@%(host)s", + "sql_args": {"host": "localhost", "user": "testuser"}, + }, + "testuser", + ) def test_grant_exists_true(self): - ''' + """ Test to ensure that we can find a grant that exists - ''' + """ mock_grants = [ "GRANT USAGE ON *.* TO 'testuser'@'%'", "GRANT SELECT, INSERT, UPDATE ON `testdb`.`testtableone` TO 'testuser'@'%'", "GRANT SELECT ON `testdb`.`testtabletwo` TO 'testuer'@'%'", "GRANT SELECT ON `testdb`.`testtablethree` TO 'testuser'@'%'", ] - with patch.object(mysql, 'version', return_value='5.6.41'): + with patch.object(mysql, "version", return_value="5.6.41"): mock = MagicMock(return_value=mock_grants) - with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants: + with patch.object( + mysql, "user_grants", return_value=mock_grants + ) as mock_user_grants: ret = mysql.grant_exists( - 'SELECT, INSERT, UPDATE', - 'testdb.testtableone', - 'testuser', - '%' + "SELECT, INSERT, UPDATE", "testdb.testtableone", "testuser", "%" ) self.assertEqual(ret, True) def test_grant_exists_false(self): - ''' + """ Test to ensure that we don't find a grant that doesn't exist - ''' + """ mock_grants = [ "GRANT USAGE ON *.* TO 'testuser'@'%'", "GRANT SELECT, INSERT, UPDATE ON `testdb`.`testtableone` TO 'testuser'@'%'", "GRANT SELECT ON `testdb`.`testtablethree` TO 'testuser'@'%'", ] - with patch.object(mysql, 'version', return_value='5.6.41'): + with patch.object(mysql, "version", return_value="5.6.41"): mock = MagicMock(return_value=mock_grants) - with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants: + with patch.object( + mysql, "user_grants", return_value=mock_grants + ) as mock_user_grants: ret = mysql.grant_exists( - 'SELECT', - 'testdb.testtabletwo', - 'testuser', - '%' + "SELECT", "testdb.testtabletwo", "testuser", "%" ) self.assertEqual(ret, False) def test_grant_exists_all(self): - ''' + """ Test to ensure that we can find a grant that exists - ''' + """ mock_grants = [ "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON testdb.testtableone TO `testuser`@`%`", - "GRANT BACKUP_ADMIN,BINLOG_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,GROUP_REPLICATION_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SET_USER_ID,SYSTEM_VARIABLES_ADMIN,XA_RECOVER_ADMIN ON testdb.testtableone TO `testuser`@`%`" + "GRANT BACKUP_ADMIN,BINLOG_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,GROUP_REPLICATION_ADMIN,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SET_USER_ID,SYSTEM_VARIABLES_ADMIN,XA_RECOVER_ADMIN ON testdb.testtableone TO `testuser`@`%`", ] - with patch.object(mysql, 'version', return_value='8.0.10'): + with patch.object(mysql, "version", return_value="8.0.10"): mock = MagicMock(return_value=mock_grants) - with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants: - ret = mysql.grant_exists( - 'ALL', - 'testdb.testtableone', - 'testuser', - '%' - ) + with patch.object( + mysql, "user_grants", return_value=mock_grants + ) as mock_user_grants: + ret = mysql.grant_exists("ALL", "testdb.testtableone", "testuser", "%") self.assertEqual(ret, True) mock_grants = ["GRANT ALL PRIVILEGES ON testdb.testtableone TO `testuser`@`%`"] - with patch.object(mysql, 'version', return_value='5.6.41'): + with patch.object(mysql, "version", return_value="5.6.41"): mock = MagicMock(return_value=mock_grants) - with patch.object(mysql, 'user_grants', return_value=mock_grants) as mock_user_grants: + with patch.object( + mysql, "user_grants", return_value=mock_grants + ) as mock_user_grants: ret = mysql.grant_exists( - 'ALL PRIVILEGES', - 'testdb.testtableone', - 'testuser', - '%' + "ALL PRIVILEGES", "testdb.testtableone", "testuser", "%" ) self.assertEqual(ret, True) - @skipIf(True, 'TODO: Mock up user_grants()') + @skipIf(True, "TODO: Mock up user_grants()") def test_grant_add(self): - ''' + """ Test grant_add function in mysql exec module - ''' - self._test_call(mysql.grant_add, '', 'SELECT,INSERT,UPDATE', 'database.*', 'frank', 'localhost') + """ + self._test_call( + mysql.grant_add, + "", + "SELECT,INSERT,UPDATE", + "database.*", + "frank", + "localhost", + ) - @skipIf(True, 'TODO: Mock up user_grants()') + @skipIf(True, "TODO: Mock up user_grants()") def test_grant_revoke(self): - ''' + """ Test grant revoke in mysql exec module - ''' - self._test_call(mysql.grant_revoke, '', 'SELECT,INSERT,UPDATE', 'database.*', 'frank', 'localhost') + """ + self._test_call( + mysql.grant_revoke, + "", + "SELECT,INSERT,UPDATE", + "database.*", + "frank", + "localhost", + ) def test_processlist(self): - ''' + """ Test processlist function in mysql exec module - ''' - self._test_call(mysql.processlist, 'SHOW FULL PROCESSLIST') + """ + self._test_call(mysql.processlist, "SHOW FULL PROCESSLIST") def test_get_master_status(self): - ''' + """ Test get_master_status in the mysql execution module - ''' - self._test_call(mysql.get_master_status, 'SHOW MASTER STATUS') + """ + self._test_call(mysql.get_master_status, "SHOW MASTER STATUS") def test_get_slave_status(self): - ''' + """ Test get_slave_status in the mysql execution module - ''' - self._test_call(mysql.get_slave_status, 'SHOW SLAVE STATUS') + """ + self._test_call(mysql.get_slave_status, "SHOW SLAVE STATUS") def test_get_slave_status_bad_server(self): - ''' + """ Test get_slave_status in the mysql execution module, simulating a broken server - ''' + """ connect_mock = MagicMock(return_value=None) - with patch.object(mysql, '_connect', connect_mock): - with patch.dict(mysql.__salt__, {'config.option': MagicMock()}): + with patch.object(mysql, "_connect", connect_mock): + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): rslt = mysql.get_slave_status() connect_mock.assert_has_calls([call()]) self.assertEqual(rslt, []) - @skipIf(True, 'MySQL module claims this function is not ready for production') + @skipIf(True, "MySQL module claims this function is not ready for production") def test_free_slave(self): pass def test_query(self): - self._test_call(mysql.query, 'SELECT * FROM testdb', 'testdb', 'SELECT * FROM testdb') + self._test_call( + mysql.query, "SELECT * FROM testdb", "testdb", "SELECT * FROM testdb" + ) def test_query_error(self): connect_mock = MagicMock() - with patch.object(mysql, '_connect', connect_mock): - with patch.dict(mysql.__salt__, {'config.option': MagicMock()}): + with patch.object(mysql, "_connect", connect_mock): + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): # Use the OperationalError from the salt mysql module because that # exception can come from either MySQLdb or pymysql - side_effect = mysql.OperationalError(9999, 'Something Went Wrong') - with patch.object(mysql, '_execute', MagicMock(side_effect=side_effect)): - mysql.query('testdb', 'SELECT * FROM testdb') - self.assertIn('mysql.error', mysql.__context__) - expected = 'MySQL Error 9999: Something Went Wrong' - self.assertEqual(mysql.__context__['mysql.error'], expected) + side_effect = mysql.OperationalError(9999, "Something Went Wrong") + with patch.object( + mysql, "_execute", MagicMock(side_effect=side_effect) + ): + mysql.query("testdb", "SELECT * FROM testdb") + self.assertIn("mysql.error", mysql.__context__) + expected = "MySQL Error 9999: Something Went Wrong" + self.assertEqual(mysql.__context__["mysql.error"], expected) def test_plugin_add(self): - ''' + """ Test the adding/installing a MySQL / MariaDB plugin - ''' - with patch.object(mysql, 'plugin_status', MagicMock(return_value='')): - self._test_call(mysql.plugin_add, - 'INSTALL PLUGIN auth_socket SONAME "auth_socket.so"', - 'auth_socket', + """ + with patch.object(mysql, "plugin_status", MagicMock(return_value="")): + self._test_call( + mysql.plugin_add, + 'INSTALL PLUGIN auth_socket SONAME "auth_socket.so"', + "auth_socket", ) def test_plugin_remove(self): - ''' + """ Test the removing/uninstalling a MySQL / MariaDB plugin - ''' - with patch.object(mysql, 'plugin_status', MagicMock(return_value='ACTIVE')): - self._test_call(mysql.plugin_remove, - 'UNINSTALL PLUGIN auth_socket', - 'auth_socket', + """ + with patch.object(mysql, "plugin_status", MagicMock(return_value="ACTIVE")): + self._test_call( + mysql.plugin_remove, "UNINSTALL PLUGIN auth_socket", "auth_socket", ) def test_plugin_status(self): - ''' + """ Test checking the status of a MySQL / MariaDB plugin - ''' - self._test_call(mysql.plugin_status, - {'sql': 'SELECT PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = %(name)s', - 'sql_args': {'name': 'auth_socket'} - }, - 'auth_socket') + """ + self._test_call( + mysql.plugin_status, + { + "sql": "SELECT PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME = %(name)s", + "sql_args": {"name": "auth_socket"}, + }, + "auth_socket", + ) + + def test_sanitize_comment(self): + """ + Test comment sanitization + """ + input_data = """/* + multiline + comment + */ + CREATE TABLE test_update (a VARCHAR(25)); # end of line comment + # example comment + insert into test_update values ("some #hash value"); -- ending comment + insert into test_update values ("crazy -- not comment"); -- another ending comment + -- another comment type + """ + expected_response = """/* +multiline +comment +*/ +CREATE TABLE test_update (a VARCHAR(25)); +insert into test_update values ("some #hash value"); +insert into test_update values ("crazy -- not comment"); +""" + output = mysql._sanitize_comments(input_data) + self.assertEqual(output, expected_response) def _test_call(self, function, expected_sql, *args, **kwargs): connect_mock = MagicMock() - with patch.object(mysql, '_connect', connect_mock): - with patch.dict(mysql.__salt__, {'config.option': MagicMock()}): + with patch.object(mysql, "_connect", connect_mock): + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): function(*args, **kwargs) if isinstance(expected_sql, dict): - calls = call().cursor().execute('{0}'.format(expected_sql['sql']), expected_sql['sql_args']) + calls = ( + call() + .cursor() + .execute( + "{0}".format(expected_sql["sql"]), expected_sql["sql_args"] + ) + ) else: - calls = call().cursor().execute('{0}'.format(expected_sql)) + calls = call().cursor().execute("{0}".format(expected_sql)) connect_mock.assert_has_calls((calls,), True) + + @skipIf( + NO_PyMYSQL, "Install pymysql bindings before running test__connect_pymysql." + ) + def test__connect_pymysql_exception(self): + """ + Test the _connect function in the MySQL module + """ + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): + with patch( + "MySQLdb.connect", + side_effect=pymysql.err.InternalError( + 1698, "Access denied for user 'root'@'localhost'" + ), + ): + ret = mysql._connect() + self.assertIn("mysql.error", mysql.__context__) + self.assertEqual( + mysql.__context__["mysql.error"], + "MySQL Error 1698: Access denied for user 'root'@'localhost'", + ) + + @skipIf(not NO_PyMYSQL, "With pymysql installed use test__connect_pymysql.") + def test__connect_mysqldb_exception(self): + """ + Test the _connect function in the MySQL module + """ + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): + with patch( + "MySQLdb.connect", + side_effect=mysql.OperationalError( + 1698, "Access denied for user 'root'@'localhost'" + ), + ): + ret = mysql._connect() + self.assertIn("mysql.error", mysql.__context__) + self.assertEqual( + mysql.__context__["mysql.error"], + "MySQL Error 1698: Access denied for user 'root'@'localhost'", + ) + + def test__connect_mysqldb(self): + """ + Test the _connect function in the MySQL module + """ + with patch.dict(mysql.__salt__, {"config.option": MagicMock()}): + with patch("MySQLdb.connect", return_value=MockMySQLConnect()): + ret = mysql._connect() + self.assertNotIn("mysql.error", mysql.__context__) diff --git a/tests/unit/modules/test_nacl.py b/tests/unit/modules/test_nacl.py index 230fa34b0c7..df1f09a77ef 100644 --- a/tests/unit/modules/test_nacl.py +++ b/tests/unit/modules/test_nacl.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the nacl execution module -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,57 +10,53 @@ import salt.utils.stringutils # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.unit import skipIf - +from tests.support.unit import TestCase, skipIf try: import libnacl.secret # pylint: disable=unused-import import libnacl.sealed # pylint: disable=unused-import import salt.modules.nacl as nacl + HAS_LIBNACL = True except (ImportError, OSError, AttributeError): HAS_LIBNACL = False -@skipIf(not HAS_LIBNACL, 'skipping test_nacl, libnacl is unavailable') +@skipIf(not HAS_LIBNACL, "skipping test_nacl, libnacl is unavailable") class NaclTest(TestCase, LoaderModuleMockMixin): - ''' + """ Test the nacl runner - ''' + """ + def setup_loader_modules(self): - self.unencrypted_data = salt.utils.stringutils.to_bytes('hello') + self.unencrypted_data = salt.utils.stringutils.to_bytes("hello") self.opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils(self.opts) - funcs = salt.loader.minion_mods(self.opts, utils=utils, whitelist=['nacl']) + funcs = salt.loader.minion_mods(self.opts, utils=utils, whitelist=["nacl"]) return { - nacl: { - '__opts__': self.opts, - '__utils__': utils, - '__salt__': funcs - }, + nacl: {"__opts__": self.opts, "__utils__": utils, "__salt__": funcs}, } def setUp(self): # Generate the keys ret = nacl.keygen() - self.assertIn('pk', ret) - self.assertIn('sk', ret) - self.pk = ret['pk'] - self.sk = ret['sk'] + self.assertIn("pk", ret) + self.assertIn("sk", ret) + self.pk = ret["pk"] + self.sk = ret["sk"] def test_keygen(self): - ''' + """ Test keygen - ''' + """ self.assertEqual(len(self.pk), 44) self.assertEqual(len(self.sk), 44) def test_enc_dec(self): - ''' + """ Generate keys, encrypt, then decrypt. - ''' + """ # Encrypt with pk encrypted_data = nacl.enc(data=self.unencrypted_data, pk=self.pk) @@ -69,29 +65,25 @@ class NaclTest(TestCase, LoaderModuleMockMixin): self.assertEqual(self.unencrypted_data, decrypted_data) def test_sealedbox_enc_dec(self): - ''' + """ Generate keys, encrypt, then decrypt. - ''' + """ # Encrypt with pk - encrypted_data = nacl.sealedbox_encrypt( - data=self.unencrypted_data, pk=self.pk) + encrypted_data = nacl.sealedbox_encrypt(data=self.unencrypted_data, pk=self.pk) # Decrypt with sk - decrypted_data = nacl.sealedbox_decrypt( - data=encrypted_data, sk=self.sk) + decrypted_data = nacl.sealedbox_decrypt(data=encrypted_data, sk=self.sk) self.assertEqual(self.unencrypted_data, decrypted_data) def test_secretbox_enc_dec(self): - ''' + """ Generate keys, encrypt, then decrypt. - ''' + """ # Encrypt with sk - encrypted_data = nacl.secretbox_encrypt( - data=self.unencrypted_data, sk=self.sk) + encrypted_data = nacl.secretbox_encrypt(data=self.unencrypted_data, sk=self.sk) # Decrypt with sk - decrypted_data = nacl.secretbox_decrypt( - data=encrypted_data, sk=self.sk) + decrypted_data = nacl.secretbox_decrypt(data=encrypted_data, sk=self.sk) self.assertEqual(self.unencrypted_data, decrypted_data) diff --git a/tests/unit/modules/test_nagios.py b/tests/unit/modules/test_nagios.py index ca2d9b4ce4b..52689eade66 100644 --- a/tests/unit/modules/test_nagios.py +++ b/tests/unit/modules/test_nagios.py @@ -1,82 +1,81 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.nagios as nagios +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class NagiosTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.nagios - ''' + """ + def setup_loader_modules(self): return {nagios: {}} def test_run(self): - ''' + """ Test for Run nagios plugin and return all the data execution with cmd.run - ''' - with patch.object(nagios, '_execute_cmd', return_value='A'): - self.assertEqual(nagios.run('plugin'), 'A') + """ + with patch.object(nagios, "_execute_cmd", return_value="A"): + self.assertEqual(nagios.run("plugin"), "A") def test_retcode(self): - ''' + """ Test for Run one nagios plugin and return retcode of the execution - ''' - with patch.object(nagios, '_execute_cmd', return_value='A'): - self.assertEqual(nagios.retcode('plugin', key_name='key'), - {'key': {'status': 'A'}}) + """ + with patch.object(nagios, "_execute_cmd", return_value="A"): + self.assertEqual( + nagios.retcode("plugin", key_name="key"), {"key": {"status": "A"}} + ) def test_run_all(self): - ''' + """ Test for Run nagios plugin and return all the data execution with cmd.run_all - ''' - with patch.object(nagios, '_execute_cmd', return_value='A'): - self.assertEqual(nagios.run_all('plugin'), 'A') + """ + with patch.object(nagios, "_execute_cmd", return_value="A"): + self.assertEqual(nagios.run_all("plugin"), "A") def test_retcode_pillar(self): - ''' + """ Test for Run one or more nagios plugins from pillar data and get the result of cmd.retcode - ''' - with patch.dict(nagios.__salt__, - {'pillar.get': MagicMock(return_value={})}): - self.assertEqual(nagios.retcode_pillar('pillar_name'), {}) + """ + with patch.dict(nagios.__salt__, {"pillar.get": MagicMock(return_value={})}): + self.assertEqual(nagios.retcode_pillar("pillar_name"), {}) def test_run_pillar(self): - ''' + """ Test for Run one or more nagios plugins from pillar data and get the result of cmd.run - ''' - with patch.object(nagios, '_execute_pillar', return_value='A'): - self.assertEqual(nagios.run_pillar('pillar'), 'A') + """ + with patch.object(nagios, "_execute_pillar", return_value="A"): + self.assertEqual(nagios.run_pillar("pillar"), "A") def test_run_all_pillar(self): - ''' + """ Test for Run one or more nagios plugins from pillar data and get the result of cmd.run - ''' - with patch.object(nagios, '_execute_pillar', return_value='A'): - self.assertEqual(nagios.run_all_pillar('pillar'), 'A') + """ + with patch.object(nagios, "_execute_pillar", return_value="A"): + self.assertEqual(nagios.run_all_pillar("pillar"), "A") def test_list_plugins(self): - ''' + """ Test for List all the nagios plugins - ''' - with patch.object(os, 'listdir', return_value=[]): + """ + with patch.object(os, "listdir", return_value=[]): self.assertEqual(nagios.list_plugins(), []) diff --git a/tests/unit/modules/test_napalm_bgp.py b/tests/unit/modules/test_napalm_bgp.py index 8d92b0afc27..3f98b50b295 100644 --- a/tests/unit/modules/test_napalm_bgp.py +++ b/tests/unit/modules/test_napalm_bgp.py @@ -1,37 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import tests.support.napalm as napalm_test_support # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import tests.support.napalm as napalm_test_support import salt.modules.napalm_bgp as napalm_bgp # NOQA class NapalmBgpModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, } } @@ -39,8 +34,8 @@ class NapalmBgpModuleTestCase(TestCase, LoaderModuleMockMixin): def test_config(self): ret = napalm_bgp.config("test_group") - assert ret['out'] is napalm_test_support.TEST_BGP_CONFIG + assert ret["out"] is napalm_test_support.TEST_BGP_CONFIG def test_neighbors(self): ret = napalm_bgp.neighbors("test_address") - assert ret['out'] is napalm_test_support.TEST_BGP_NEIGHBORS + assert ret["out"] is napalm_test_support.TEST_BGP_NEIGHBORS diff --git a/tests/unit/modules/test_napalm_formula.py b/tests/unit/modules/test_napalm_formula.py index e1799a4f0a5..090a5b6da2e 100644 --- a/tests/unit/modules/test_napalm_formula.py +++ b/tests/unit/modules/test_napalm_formula.py @@ -1,177 +1,162 @@ # -*- coding: utf-8 -*- -''' +""" Test the napalm_formula execution module. -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import textwrap -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch - # Import Salt modules import salt.modules.napalm_formula as napalm_formula +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class TestModulesNAPALMFormula(TestCase, LoaderModuleMockMixin): model = { - 'interfaces': { - 'interface': { - 'Ethernet1': { - 'config': { - 'name': 'Ethernet1', - 'description': 'Interface Ethernet1' + "interfaces": { + "interface": { + "Ethernet1": { + "config": { + "name": "Ethernet1", + "description": "Interface Ethernet1", }, - 'subinterfaces': { - 'subinterface': { - '0': { - 'config': { - 'index': 0, - 'description': 'Subinterface Ethernet1.0' + "subinterfaces": { + "subinterface": { + "0": { + "config": { + "index": 0, + "description": "Subinterface Ethernet1.0", } }, - '100': { - 'config': { - 'index': 100, - 'description': 'Subinterface Ethernet1.100' + "100": { + "config": { + "index": 100, + "description": "Subinterface Ethernet1.100", } }, - '900': { - 'config': { - 'index': 900, - 'description': 'Subinterface Ethernet1.900' + "900": { + "config": { + "index": 900, + "description": "Subinterface Ethernet1.900", } - } + }, } - } + }, }, - 'Ethernet2': { - 'config': { - 'name': 'Ethernet2', - 'description': 'Interface Ethernet2' + "Ethernet2": { + "config": { + "name": "Ethernet2", + "description": "Interface Ethernet2", }, - 'subinterfaces': { - 'subinterface': { - '400': { - 'config': { - 'index': 400, - 'description': 'Subinterface Ethernet2.400' + "subinterfaces": { + "subinterface": { + "400": { + "config": { + "index": 400, + "description": "Subinterface Ethernet2.400", } } } - } - } + }, + }, } } } defaults = { - 'interfaces': { - 'interface': { - '*': { - 'config': { - 'mtu': 2048, - 'enabled': True + "interfaces": { + "interface": { + "*": { + "config": {"mtu": 2048, "enabled": True}, + "subinterfaces": { + "subinterface": {"*": {"config": {"enabled": True}}} }, - 'subinterfaces': { - 'subinterface': { - '*': { - 'config': { - 'enabled': True - } - } - } - } } } } } def setup_loader_modules(self): - return { - napalm_formula: { - '__grains__': { - 'os': 'eos' - } - } - } + return {napalm_formula: {"__grains__": {"os": "eos"}}} def test_container_path(self): paths = [ - 'interfaces:interface:Ethernet1:config', - 'interfaces:interface:Ethernet1:subinterfaces:subinterface:0:config', - 'interfaces:interface:Ethernet1:subinterfaces:subinterface:100:config', - 'interfaces:interface:Ethernet2:subinterfaces:subinterface:400:config', - 'interfaces:interface:Ethernet1:subinterfaces:subinterface:900:config', - 'interfaces:interface:Ethernet2:config' + "interfaces:interface:Ethernet1:config", + "interfaces:interface:Ethernet1:subinterfaces:subinterface:0:config", + "interfaces:interface:Ethernet1:subinterfaces:subinterface:100:config", + "interfaces:interface:Ethernet2:subinterfaces:subinterface:400:config", + "interfaces:interface:Ethernet1:subinterfaces:subinterface:900:config", + "interfaces:interface:Ethernet2:config", ] ret = napalm_formula.container_path(self.model) self.assertEqual(set(ret), set(paths)) def test_setval(self): - dict_ = {'foo': {'bar': {'baz': True}}} - self.assertEqual(dict_, napalm_formula.setval('foo:bar:baz', True)) + dict_ = {"foo": {"bar": {"baz": True}}} + self.assertEqual(dict_, napalm_formula.setval("foo:bar:baz", True)) def test_defaults(self): expected_result = { - 'interfaces': { - 'interface': { - 'Ethernet1': { - 'config': { - 'name': 'Ethernet1', - 'description': 'Interface Ethernet1', - 'mtu': 2048, - 'enabled': True + "interfaces": { + "interface": { + "Ethernet1": { + "config": { + "name": "Ethernet1", + "description": "Interface Ethernet1", + "mtu": 2048, + "enabled": True, }, - 'subinterfaces': { - 'subinterface': { - '0': { - 'config': { - 'index': 0, - 'description': 'Subinterface Ethernet1.0', - 'enabled': True + "subinterfaces": { + "subinterface": { + "0": { + "config": { + "index": 0, + "description": "Subinterface Ethernet1.0", + "enabled": True, } }, - '100': { - 'config': { - 'index': 100, - 'description': 'Subinterface Ethernet1.100', - 'enabled': True + "100": { + "config": { + "index": 100, + "description": "Subinterface Ethernet1.100", + "enabled": True, } }, - '900': { - 'config': { - 'index': 900, - 'description': 'Subinterface Ethernet1.900', - 'enabled': True + "900": { + "config": { + "index": 900, + "description": "Subinterface Ethernet1.900", + "enabled": True, } - } + }, } - } + }, }, - 'Ethernet2': { - 'config': { - 'name': 'Ethernet2', - 'description': 'Interface Ethernet2', - 'mtu': 2048, - 'enabled': True + "Ethernet2": { + "config": { + "name": "Ethernet2", + "description": "Interface Ethernet2", + "mtu": 2048, + "enabled": True, }, - 'subinterfaces': { - 'subinterface': { - '400': { - 'config': { - 'index': 400, - 'description': 'Subinterface Ethernet2.400', - 'enabled': True + "subinterfaces": { + "subinterface": { + "400": { + "config": { + "index": 400, + "description": "Subinterface Ethernet2.400", + "enabled": True, } } } - } - } + }, + }, } } } @@ -179,27 +164,22 @@ class TestModulesNAPALMFormula(TestCase, LoaderModuleMockMixin): self.assertEqual(ret, expected_result) def test_render_field(self): - config = { - 'description': 'Interface description' - } - ret = napalm_formula.render_field(config, 'description', quotes=True) + config = {"description": "Interface description"} + ret = napalm_formula.render_field(config, "description", quotes=True) self.assertEqual(ret, 'description "Interface description"') def test_render_field_junos(self): - config = { - 'description': 'Interface description' - } - with patch.dict(napalm_formula.__grains__, {'os': 'junos'}): - ret = napalm_formula.render_field(config, 'description', quotes=True) + config = {"description": "Interface description"} + with patch.dict(napalm_formula.__grains__, {"os": "junos"}): + ret = napalm_formula.render_field(config, "description", quotes=True) self.assertEqual(ret, 'description "Interface description";') def test_render_fields(self): - config = { - 'mtu': 2048, - 'description': 'Interface description' - } - expected_render = textwrap.dedent('''\ + config = {"mtu": 2048, "description": "Interface description"} + expected_render = textwrap.dedent( + '''\ mtu "2048" - description "Interface description"''') - ret = napalm_formula.render_fields(config, 'mtu', 'description', quotes=True) + description "Interface description"''' + ) + ret = napalm_formula.render_fields(config, "mtu", "description", quotes=True) self.assertEqual(ret, expected_render) diff --git a/tests/unit/modules/test_napalm_netacl.py b/tests/unit/modules/test_napalm_netacl.py index c73048d1326..cdca54e05a9 100644 --- a/tests/unit/modules/test_napalm_netacl.py +++ b/tests/unit/modules/test_napalm_netacl.py @@ -1,103 +1,94 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import tests.support.napalm as napalm_test_support # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import tests.support.napalm as napalm_test_support import salt.modules.napalm_netacl as napalm_acl # NOQA def mock_capirca_term_config(platform, filter_name, term_name, *args, **kwargs): - assert platform == 'cisco' - assert filter_name == 'test_filter' - assert term_name == 'test_term' - return 'test_config' + assert platform == "cisco" + assert filter_name == "test_filter" + assert term_name == "test_term" + return "test_config" def mock_capirca_filter_config(platform, filter_name, *args, **kwargs): - assert platform == 'cisco' - assert filter_name == 'test_filter' - return 'test_config' + assert platform == "cisco" + assert filter_name == "test_filter" + return "test_config" def mock_capirca_policy_config(platform, *args, **kwargs): - assert platform == 'cisco' - return 'test_config' + assert platform == "cisco" + return "test_config" def mock_net_load_config(text, *args, **kwargs): - assert text == 'test_config' + assert text == "test_config" return napalm_test_support.TEST_TERM_CONFIG def mock_capirca_get_filter_pillar(filter_, *args, **kwargs): - assert filter_ == 'test_filter' - return {'test': 'value'} + assert filter_ == "test_filter" + return {"test": "value"} def mock_capirca_get_term_pillar(filter_, term, *args, **kwargs): - assert filter_ == 'test_filter' - assert term == 'test_term' - return {'test': 'value'} + assert filter_ == "test_filter" + assert term == "test_term" + return {"test": "value"} class NapalmAclModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash, - 'capirca.get_term_config': mock_capirca_term_config, - 'capirca.get_policy_config': mock_capirca_policy_config, - 'capirca.get_filter_config': mock_capirca_filter_config, - 'capirca.get_filter_pillar': mock_capirca_get_filter_pillar, - 'capirca.get_term_pillar': mock_capirca_get_term_pillar, - 'net.load_config': mock_net_load_config + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, + "capirca.get_term_config": mock_capirca_term_config, + "capirca.get_policy_config": mock_capirca_policy_config, + "capirca.get_filter_config": mock_capirca_filter_config, + "capirca.get_filter_pillar": mock_capirca_get_filter_pillar, + "capirca.get_term_pillar": mock_capirca_get_term_pillar, + "net.load_config": mock_net_load_config, }, - '__grains__': { - 'os': 'ios', - 'vendor': 'cisco', - 'model': '3750X' - } + "__grains__": {"os": "ios", "vendor": "cisco", "model": "3750X"}, } return {napalm_acl: module_globals} def test_load_term_config(self): - ret = napalm_acl.load_term_config('test_filter', 'test_term') - assert ret['already_configured'] is False + ret = napalm_acl.load_term_config("test_filter", "test_term") + assert ret["already_configured"] is False def test_load_filter_config(self): - ret = napalm_acl.load_filter_config('test_filter', 'test_term') - assert ret['already_configured'] is False + ret = napalm_acl.load_filter_config("test_filter", "test_term") + assert ret["already_configured"] is False def test_load_policy_config(self): - ret = napalm_acl.load_policy_config('test_filter', 'test_term') - assert ret['already_configured'] is False + ret = napalm_acl.load_policy_config("test_filter", "test_term") + assert ret["already_configured"] is False def test_get_filter_pillar(self): - ret = napalm_acl.get_filter_pillar('test_filter') - assert ret['test'] == 'value' + ret = napalm_acl.get_filter_pillar("test_filter") + assert ret["test"] == "value" def test_get_term_pillar(self): - ret = napalm_acl.get_term_pillar('test_filter', 'test_term') - assert ret['test'] == 'value' + ret = napalm_acl.get_term_pillar("test_filter", "test_term") + assert ret["test"] == "value" diff --git a/tests/unit/modules/test_napalm_network.py b/tests/unit/modules/test_napalm_network.py index cf7ea26f239..5f3f6ebbbeb 100644 --- a/tests/unit/modules/test_napalm_network.py +++ b/tests/unit/modules/test_napalm_network.py @@ -1,37 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Anthony Shaw <anthonyshaw@apache.org> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import tests.support.napalm as napalm_test_support # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import tests.support.napalm as napalm_test_support import salt.modules.napalm_network as napalm_network # NOQA class NapalmNetworkModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, } } @@ -39,99 +34,98 @@ class NapalmNetworkModuleTestCase(TestCase, LoaderModuleMockMixin): def test_connected_pass(self): ret = napalm_network.connected() - assert ret['out'] is True + assert ret["out"] is True def test_facts(self): ret = napalm_network.facts() - assert ret['out'] == napalm_test_support.TEST_FACTS + assert ret["out"] == napalm_test_support.TEST_FACTS def test_environment(self): ret = napalm_network.environment() - assert ret['out'] == napalm_test_support.TEST_ENVIRONMENT + assert ret["out"] == napalm_test_support.TEST_ENVIRONMENT def test_cli_single_command(self): - ''' + """ Test that CLI works with 1 arg - ''' + """ ret = napalm_network.cli("show run") - assert ret['out'] == napalm_test_support.TEST_COMMAND_RESPONSE + assert ret["out"] == napalm_test_support.TEST_COMMAND_RESPONSE def test_cli_multi_command(self): - ''' + """ Test that CLI works with 2 arg - ''' + """ ret = napalm_network.cli("show run", "show run") - assert ret['out'] == napalm_test_support.TEST_COMMAND_RESPONSE + assert ret["out"] == napalm_test_support.TEST_COMMAND_RESPONSE def test_traceroute(self): - ret = napalm_network.traceroute('destination.com') - assert list(ret['out'].keys())[0] == 'success' + ret = napalm_network.traceroute("destination.com") + assert list(ret["out"].keys())[0] == "success" def test_ping(self): - ret = napalm_network.ping('destination.com') - assert list(ret['out'].keys())[0] == 'success' + ret = napalm_network.ping("destination.com") + assert list(ret["out"].keys())[0] == "success" def test_arp(self): ret = napalm_network.arp() - assert ret['out'] == napalm_test_support.TEST_ARP_TABLE + assert ret["out"] == napalm_test_support.TEST_ARP_TABLE def test_ipaddrs(self): ret = napalm_network.ipaddrs() - assert ret['out'] == napalm_test_support.TEST_IPADDRS + assert ret["out"] == napalm_test_support.TEST_IPADDRS def test_interfaces(self): ret = napalm_network.interfaces() - assert ret['out'] == napalm_test_support.TEST_INTERFACES + assert ret["out"] == napalm_test_support.TEST_INTERFACES def test_lldp(self): ret = napalm_network.lldp() - assert ret['out'] == napalm_test_support.TEST_LLDP_NEIGHBORS + assert ret["out"] == napalm_test_support.TEST_LLDP_NEIGHBORS def test_mac(self): ret = napalm_network.mac() - assert ret['out'] == napalm_test_support.TEST_MAC_TABLE + assert ret["out"] == napalm_test_support.TEST_MAC_TABLE def test_config(self): - ret = napalm_network.config('running') - assert ret['out'] == napalm_test_support.TEST_RUNNING_CONFIG + ret = napalm_network.config("running") + assert ret["out"] == napalm_test_support.TEST_RUNNING_CONFIG def test_optics(self): ret = napalm_network.optics() - assert ret['out'] == napalm_test_support.TEST_OPTICS + assert ret["out"] == napalm_test_support.TEST_OPTICS def test_load_config(self): - ret = napalm_network.load_config(text='new config') - assert ret['result'] + ret = napalm_network.load_config(text="new config") + assert ret["result"] def test_load_config_replace(self): - ret = napalm_network.load_config(text='new config', replace=True) - assert ret['result'] + ret = napalm_network.load_config(text="new config", replace=True) + assert ret["result"] def test_load_template(self): - ret = napalm_network.load_template('set_ntp_peers', - peers=['192.168.0.1']) - assert ret['out'] is None + ret = napalm_network.load_template("set_ntp_peers", peers=["192.168.0.1"]) + assert ret["out"] is None def test_commit(self): ret = napalm_network.commit() - assert ret['out'] == napalm_test_support.TEST_RUNNING_CONFIG + assert ret["out"] == napalm_test_support.TEST_RUNNING_CONFIG def test_discard_config(self): ret = napalm_network.discard_config() - assert ret['out'] == napalm_test_support.TEST_RUNNING_CONFIG + assert ret["out"] == napalm_test_support.TEST_RUNNING_CONFIG def test_compare_config(self): ret = napalm_network.compare_config() - assert ret['out'] == napalm_test_support.TEST_RUNNING_CONFIG + assert ret["out"] == napalm_test_support.TEST_RUNNING_CONFIG def test_rollback(self): ret = napalm_network.rollback() - assert ret['out'] == napalm_test_support.TEST_RUNNING_CONFIG + assert ret["out"] == napalm_test_support.TEST_RUNNING_CONFIG def test_config_changed(self): ret = napalm_network.config_changed() - assert ret == (True, '') + assert ret == (True, "") def test_config_control(self): ret = napalm_network.config_control() - assert ret == (True, '') + assert ret == (True, "") diff --git a/tests/unit/modules/test_napalm_ntp.py b/tests/unit/modules/test_napalm_ntp.py index 62140a4810e..1356acd9927 100644 --- a/tests/unit/modules/test_napalm_ntp.py +++ b/tests/unit/modules/test_napalm_ntp.py @@ -1,45 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import tests.support.napalm as napalm_test_support # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import tests.support.napalm as napalm_test_support import salt.modules.napalm_ntp as napalm_ntp # NOQA def mock_net_load_template(template, *args, **kwargs): - if template == 'set_ntp_peers' or template == 'delete_ntp_peers': - assert '1.2.3.4' in kwargs['peers'] - if template == 'set_ntp_servers' or template == 'delete_ntp_servers': - assert '2.2.3.4' in kwargs['servers'] + if template == "set_ntp_peers" or template == "delete_ntp_peers": + assert "1.2.3.4" in kwargs["peers"] + if template == "set_ntp_servers" or template == "delete_ntp_servers": + assert "2.2.3.4" in kwargs["servers"] class NapalmNtpModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash, - 'net.load_template': mock_net_load_template + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, + "net.load_template": mock_net_load_template, } } @@ -47,28 +42,28 @@ class NapalmNtpModuleTestCase(TestCase, LoaderModuleMockMixin): def test_peers(self): ret = napalm_ntp.peers() - assert '172.17.17.1' in ret['out'] + assert "172.17.17.1" in ret["out"] def test_servers(self): ret = napalm_ntp.servers() - assert '172.17.17.1' in ret['out'] + assert "172.17.17.1" in ret["out"] def test_stats(self): ret = napalm_ntp.stats() - assert ret['out'][0]['reachability'] == 377 + assert ret["out"][0]["reachability"] == 377 def test_set_peers(self): - ret = napalm_ntp.set_peers('1.2.3.4', '5.6.7.8') + ret = napalm_ntp.set_peers("1.2.3.4", "5.6.7.8") assert ret is None def test_set_servers(self): - ret = napalm_ntp.set_servers('2.2.3.4', '6.6.7.8') + ret = napalm_ntp.set_servers("2.2.3.4", "6.6.7.8") assert ret is None def test_delete_servers(self): - ret = napalm_ntp.delete_servers('2.2.3.4', '6.6.7.8') + ret = napalm_ntp.delete_servers("2.2.3.4", "6.6.7.8") assert ret is None def test_delete_peers(self): - ret = napalm_ntp.delete_peers('1.2.3.4', '5.6.7.8') + ret = napalm_ntp.delete_peers("1.2.3.4", "5.6.7.8") assert ret is None diff --git a/tests/unit/modules/test_napalm_probes.py b/tests/unit/modules/test_napalm_probes.py index 3b8aacb959a..9c373c0984c 100644 --- a/tests/unit/modules/test_napalm_probes.py +++ b/tests/unit/modules/test_napalm_probes.py @@ -1,80 +1,65 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import tests.support.napalm as napalm_test_support # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import tests.support.napalm as napalm_test_support import salt.modules.napalm_probes as napalm_probes # NOQA TEST_PROBES = { - 'new_probe': { - 'new_test1': { - 'probe_type': 'icmp-ping', - 'target': '192.168.0.1', - 'source': '192.168.0.2', - 'probe_count': 13, - 'test_interval': 3 + "new_probe": { + "new_test1": { + "probe_type": "icmp-ping", + "target": "192.168.0.1", + "source": "192.168.0.2", + "probe_count": 13, + "test_interval": 3, } } } -TEST_DELETE_PROBES = { - 'existing_probe': { - 'existing_test1': {}, - 'existing_test2': {} - } -} +TEST_DELETE_PROBES = {"existing_probe": {"existing_test1": {}, "existing_test2": {}}} -TEST_SCHEDULE_PROBES = { - 'test_probe': { - 'existing_test1': {}, - 'existing_test2': {} - } -} +TEST_SCHEDULE_PROBES = {"test_probe": {"existing_test1": {}, "existing_test2": {}}} def mock_net_load(template, *args, **kwargs): - if template == 'set_probes': - assert kwargs['probes'] == TEST_PROBES + if template == "set_probes": + assert kwargs["probes"] == TEST_PROBES return napalm_test_support.TEST_TERM_CONFIG - if template == 'delete_probes': - assert kwargs['probes'] == TEST_DELETE_PROBES + if template == "delete_probes": + assert kwargs["probes"] == TEST_DELETE_PROBES return napalm_test_support.TEST_TERM_CONFIG - if template == 'schedule_probes': - assert kwargs['probes'] == TEST_SCHEDULE_PROBES + if template == "schedule_probes": + assert kwargs["probes"] == TEST_SCHEDULE_PROBES return napalm_test_support.TEST_TERM_CONFIG raise ValueError("incorrect template {0}".format(template)) class NapalmProbesModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash, - 'net.load_template': mock_net_load + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, + "net.load_template": mock_net_load, } } @@ -82,20 +67,20 @@ class NapalmProbesModuleTestCase(TestCase, LoaderModuleMockMixin): def test_probes_config(self): ret = napalm_probes.config() - assert ret['out'] == napalm_test_support.TEST_PROBES_CONFIG + assert ret["out"] == napalm_test_support.TEST_PROBES_CONFIG def test_probes_results(self): ret = napalm_probes.results() - assert ret['out'] == napalm_test_support.TEST_PROBES_RESULTS + assert ret["out"] == napalm_test_support.TEST_PROBES_RESULTS def test_set_probes(self): ret = napalm_probes.set_probes(TEST_PROBES) - assert ret['result'] is True + assert ret["result"] is True def test_delete_probes(self): ret = napalm_probes.delete_probes(TEST_DELETE_PROBES) - assert ret['result'] is True + assert ret["result"] is True def test_schedule_probes(self): ret = napalm_probes.schedule_probes(TEST_SCHEDULE_PROBES) - assert ret['result'] is True + assert ret["result"] is True diff --git a/tests/unit/modules/test_napalm_route.py b/tests/unit/modules/test_napalm_route.py index c459ac818f9..5cd435340d1 100644 --- a/tests/unit/modules/test_napalm_route.py +++ b/tests/unit/modules/test_napalm_route.py @@ -1,19 +1,18 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import tests.support.napalm as napalm_test_support # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import tests.support.napalm as napalm_test_support import salt.modules.napalm_route as napalm_route # NOQA @@ -22,26 +21,22 @@ def mock_net_load(template, *args, **kwargs): class NapalmRouteModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash, - 'net.load_template': mock_net_load + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, + "net.load_template": mock_net_load, } } return {napalm_route: module_globals} def test_show(self): - ret = napalm_route.show('1.2.3.4') - assert ret['out'] == napalm_test_support.TEST_ROUTE + ret = napalm_route.show("1.2.3.4") + assert ret["out"] == napalm_test_support.TEST_ROUTE diff --git a/tests/unit/modules/test_napalm_snmp.py b/tests/unit/modules/test_napalm_snmp.py index d5f82f38a92..2e5b791f0bd 100644 --- a/tests/unit/modules/test_napalm_snmp.py +++ b/tests/unit/modules/test_napalm_snmp.py @@ -1,39 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import tests.support.napalm as napalm_test_support # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import tests.support.napalm as napalm_test_support import salt.modules.napalm_snmp as napalm_snmp # NOQA import salt.modules.napalm_network as napalm_network # NOQA class NapalmSnmpModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash, - 'net.load_template': napalm_network.load_template + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, + "net.load_template": napalm_network.load_template, } } @@ -41,12 +36,12 @@ class NapalmSnmpModuleTestCase(TestCase, LoaderModuleMockMixin): def test_config(self): ret = napalm_snmp.config() - assert ret['out'] == napalm_test_support.TEST_SNMP_INFO + assert ret["out"] == napalm_test_support.TEST_SNMP_INFO def test_remove_config(self): - ret = napalm_snmp.remove_config('1.2.3.4') - assert ret['result'] is False + ret = napalm_snmp.remove_config("1.2.3.4") + assert ret["result"] is False def test_update_config(self): - ret = napalm_snmp.update_config('1.2.3.4') - assert ret['result'] is False + ret = napalm_snmp.update_config("1.2.3.4") + assert ret["result"] is False diff --git a/tests/unit/modules/test_napalm_users.py b/tests/unit/modules/test_napalm_users.py index f8214ea7609..3816abb6a4a 100644 --- a/tests/unit/modules/test_napalm_users.py +++ b/tests/unit/modules/test_napalm_users.py @@ -1,39 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import tests.support.napalm as napalm_test_support # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import tests.support.napalm as napalm_test_support import salt.modules.napalm_users as napalm_users # NOQA import salt.modules.napalm_network as napalm_network # NOQA class NapalmUsersModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash, - 'net.load_template': napalm_network.load_template + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, + "net.load_template": napalm_network.load_template, } } @@ -41,12 +36,12 @@ class NapalmUsersModuleTestCase(TestCase, LoaderModuleMockMixin): def test_config(self): ret = napalm_users.config() - assert ret['out'] == napalm_test_support.TEST_USERS + assert ret["out"] == napalm_test_support.TEST_USERS def test_set_users(self): - ret = napalm_users.set_users({'mircea': {}}) - assert ret['result'] is False + ret = napalm_users.set_users({"mircea": {}}) + assert ret["result"] is False def test_delete_users(self): - ret = napalm_users.delete_users({'mircea': {}}) - assert ret['result'] is False + ret = napalm_users.delete_users({"mircea": {}}) + assert ret["result"] is False diff --git a/tests/unit/modules/test_napalm_yang_mod.py b/tests/unit/modules/test_napalm_yang_mod.py index 2be6d203bd6..a5ba5b576ae 100644 --- a/tests/unit/modules/test_napalm_yang_mod.py +++ b/tests/unit/modules/test_napalm_yang_mod.py @@ -1,26 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import tests.support.napalm as napalm_test_support # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import tests.support.napalm as napalm_test_support import salt.modules.napalm_yang_mod as napalm_yang_mod # NOQA import salt.modules.napalm_network as napalm_network # NOQA -TEST_DIFF = { - 'diff1': 'value' -} +TEST_DIFF = {"diff1": "value"} class MockNapalmYangModel(object): @@ -44,10 +41,10 @@ class MockNapalmYangModule(object): TEST_CONFIG = { - 'comment': 'Configuration discarded.', - 'already_configured': False, - 'result': True, - 'diff': '[edit interfaces xe-0/0/5]+ description "Adding a description";' + "comment": "Configuration discarded.", + "already_configured": False, + "result": True, + "diff": '[edit interfaces xe-0/0/5]+ description "Adding a description";', } @@ -56,51 +53,47 @@ def mock_net_load_config(**kwargs): class NapalmYangModModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }), - 'file.file_exists': napalm_test_support.true, - 'file.join': napalm_test_support.join, - 'file.get_managed': napalm_test_support.get_managed_file, - 'random.hash': napalm_test_support.random_hash, - 'net.load_template': napalm_network.load_template, - 'net.load_config': mock_net_load_config + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ), + "file.file_exists": napalm_test_support.true, + "file.join": napalm_test_support.join, + "file.get_managed": napalm_test_support.get_managed_file, + "random.hash": napalm_test_support.random_hash, + "net.load_template": napalm_network.load_template, + "net.load_config": mock_net_load_config, } } - module_globals['napalm_yang'] = MockNapalmYangModule() + module_globals["napalm_yang"] = MockNapalmYangModule() return {napalm_yang_mod: module_globals, napalm_network: module_globals} def test_diff(self): - ret = napalm_yang_mod.diff({}, {'test': True}, 'models.openconfig_interfaces') + ret = napalm_yang_mod.diff({}, {"test": True}, "models.openconfig_interfaces") assert ret == TEST_DIFF def test_diff_list(self): - ''' + """ Test it with an actual list - ''' - ret = napalm_yang_mod.diff({}, {'test': True}, ['models.openconfig_interfaces']) + """ + ret = napalm_yang_mod.diff({}, {"test": True}, ["models.openconfig_interfaces"]) assert ret == TEST_DIFF def test_parse(self): - ret = napalm_yang_mod.parse('models.openconfig_interfaces') + ret = napalm_yang_mod.parse("models.openconfig_interfaces") assert ret is not None def test_get_config(self): - ret = napalm_yang_mod.get_config({}, 'models.openconfig_interfaces') + ret = napalm_yang_mod.get_config({}, "models.openconfig_interfaces") assert ret is not None def test_load_config(self): - ret = napalm_yang_mod.load_config({}, 'models.openconfig_interfaces') + ret = napalm_yang_mod.load_config({}, "models.openconfig_interfaces") assert ret is TEST_CONFIG def test_compliance_report(self): - ret = napalm_yang_mod.compliance_report({}, 'models.openconfig_interfaces') + ret = napalm_yang_mod.compliance_report({}, "models.openconfig_interfaces") assert ret is not None diff --git a/tests/unit/modules/test_netbox.py b/tests/unit/modules/test_netbox.py index d6c005f6399..9654fefec13 100644 --- a/tests/unit/modules/test_netbox.py +++ b/tests/unit/modules/test_netbox.py @@ -1,105 +1,101 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Zach Moody <zmoody@do.co>` -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import salt.modules.netbox as netbox + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase, skipIf + try: import pynetbox # pylint: disable=unused-import + HAS_PYNETBOX = True except ImportError: HAS_PYNETBOX = False -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - MagicMock, - call, -) -import salt.modules.netbox as netbox NETBOX_RESPONSE_STUB = { - 'device_name': 'test1-router1', - 'url': 'http://test/', - 'device_role': { - 'name': 'router', - 'url': 'http://test/' - } + "device_name": "test1-router1", + "url": "http://test/", + "device_role": {"name": "router", "url": "http://test/"}, } def mocked_clean_kwargs_filter(**kwargs): - ''' + """ Mocked args.clean_kwargs for filter tests - ''' - return {'site': u'test'} + """ + return {"site": "test"} def mocked_clean_kwargs_get(**kwargs): - ''' + """ Mocked args.clean_kwargs for get tests - ''' - return {'name': u'test'} + """ + return {"name": "test"} -@skipIf(HAS_PYNETBOX is False, 'pynetbox lib not installed') -@patch('salt.modules.netbox._config', MagicMock()) +@skipIf(HAS_PYNETBOX is False, "pynetbox lib not installed") +@patch("salt.modules.netbox._config", MagicMock()) class NetBoxTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { netbox: {}, } def test_get_by_id(self): - with patch('pynetbox.api', MagicMock()) as mock: - with patch.dict(netbox.__utils__, {'args.clean_kwargs': mocked_clean_kwargs_get}): - netbox.get_('dcim', 'devices', id=1) - self.assertEqual( - mock.mock_calls[1], - call().dcim.devices.get(1) - ) + with patch("pynetbox.api", MagicMock()) as mock: + with patch.dict( + netbox.__utils__, {"args.clean_kwargs": mocked_clean_kwargs_get} + ): + netbox.get_("dcim", "devices", id=1) + self.assertEqual(mock.mock_calls[1], call().dcim.devices.get(1)) def test_get_by_name(self): - with patch('pynetbox.api', MagicMock()) as mock: - with patch.dict(netbox.__utils__, {'args.clean_kwargs': mocked_clean_kwargs_get}): - netbox.get_('dcim', 'devices', name='test') + with patch("pynetbox.api", MagicMock()) as mock: + with patch.dict( + netbox.__utils__, {"args.clean_kwargs": mocked_clean_kwargs_get} + ): + netbox.get_("dcim", "devices", name="test") self.assertEqual( - mock.mock_calls[1], - call().dcim.devices.get(name='test') + mock.mock_calls[1], call().dcim.devices.get(name="test") ) def test_filter_by_site(self): - with patch('pynetbox.api', MagicMock()) as mock: - with patch.dict(netbox.__utils__, {'args.clean_kwargs': mocked_clean_kwargs_filter}): - netbox.filter_('dcim', 'devices', site='test') + with patch("pynetbox.api", MagicMock()) as mock: + with patch.dict( + netbox.__utils__, {"args.clean_kwargs": mocked_clean_kwargs_filter} + ): + netbox.filter_("dcim", "devices", site="test") self.assertEqual( - mock.mock_calls[1], - call().dcim.devices.filter(site='test') + mock.mock_calls[1], call().dcim.devices.filter(site="test") ) def test_filter_url(self): strip_url = netbox._strip_url_field(NETBOX_RESPONSE_STUB) self.assertTrue( - 'url' not in strip_url and 'url' not in strip_url['device_role'] + "url" not in strip_url and "url" not in strip_url["device_role"] ) def test_get_secret(self): - with patch('pynetbox.api', MagicMock()) as mock: - with patch.dict(netbox.__utils__, {'args.clean_kwargs': mocked_clean_kwargs_get}): - netbox.get_('secrets', 'secrets', name='test') - self.assertTrue( - 'token' and 'private_key_file' in mock.call_args[1] - ) + with patch("pynetbox.api", MagicMock()) as mock: + with patch.dict( + netbox.__utils__, {"args.clean_kwargs": mocked_clean_kwargs_get} + ): + netbox.get_("secrets", "secrets", name="test") + self.assertTrue("token" and "private_key_file" in mock.call_args[1]) def test_token_present(self): - with patch('pynetbox.api', MagicMock()) as mock: - with patch.dict(netbox.__utils__, {'args.clean_kwargs': mocked_clean_kwargs_get}): - netbox.get_('dcim', 'devices', name='test') - self.assertTrue( - 'token' in mock.call_args[1] - ) + with patch("pynetbox.api", MagicMock()) as mock: + with patch.dict( + netbox.__utils__, {"args.clean_kwargs": mocked_clean_kwargs_get} + ): + netbox.get_("dcim", "devices", name="test") + self.assertTrue("token" in mock.call_args[1]) diff --git a/tests/unit/modules/test_netscaler.py b/tests/unit/modules/test_netscaler.py index 1c026d64719..a7b0bf04e39 100644 --- a/tests/unit/modules/test_netscaler.py +++ b/tests/unit/modules/test_netscaler.py @@ -1,68 +1,69 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.netscaler as netscaler +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MockJson(Exception): - ''' + """ Mock Json class - ''' + """ + @staticmethod def loads(content): - ''' + """ Mock loads method - ''' + """ return content @staticmethod def dumps(dumps): - ''' + """ Mock dumps method - ''' + """ return dumps class MockNSNitroError(Exception): - ''' + """ Mock NSNitroError class - ''' - def __init__(self, message='error'): + """ + + def __init__(self, message="error"): self._message = message super(MockNSNitroError, self).__init__(self.message) def _get_message(self): - ''' + """ get_message method - ''' + """ return self._message def _set_message(self, message): - ''' + """ set_message method - ''' + """ self._message = message + message = property(_get_message, _set_message) class MockNSNitro(object): - ''' + """ Mock NSNitro class - ''' + """ + flag = None def __init__(self, host, user, passwd, bol): @@ -70,409 +71,415 @@ class MockNSNitro(object): @staticmethod def login(): - ''' + """ Mock login method - ''' + """ return True @staticmethod def logout(): - ''' + """ Mock logout method - ''' + """ return True class MockNSServiceGroup(object): - ''' + """ Mock NSServiceGroup class - ''' + """ + def __init__(self): self.sg_name = None def set_servicegroupname(self, sg_name): - ''' + """ Mock set_servicegroupname method - ''' + """ self.sg_name = sg_name return MockNSServiceGroup() @staticmethod def get(obj, servicegroup): - ''' + """ Mock get method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSServiceGroup() @staticmethod def add(obj, servicegroup): - ''' + """ Mock add method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSServiceGroup() @staticmethod def delete(obj, servicegroup): - ''' + """ Mock delete method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSServiceGroup() @staticmethod def get_servers(obj, servicegroup): - ''' + """ Mock get_servers method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return [MockNSServiceGroup()] @staticmethod def enable_server(obj, servicegroup): - ''' + """ Mock enable_server method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSServiceGroup() @staticmethod def disable_server(obj, servicegroup): - ''' + """ Mock disable_server method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSServiceGroup() @staticmethod def get_servername(): - ''' + """ Mock get_servername method - ''' - return 'serviceGroupName' + """ + return "serviceGroupName" @staticmethod def get_state(): - ''' + """ Mock get_state method - ''' - return 'ENABLED' + """ + return "ENABLED" @staticmethod def get_servicetype(): - ''' + """ Mock get_servicetype method - ''' - return '' + """ + return "" @staticmethod def set_servicetype(bol): - ''' + """ Mock set_servicetype method - ''' + """ return bol class MockNSServiceGroupServerBinding(object): - ''' + """ Mock NSServiceGroupServerBinding class - ''' + """ + def __init__(self): self.sg_name = None def set_servername(self, sg_name): - ''' + """ Mock set_servername method - ''' + """ self.sg_name = sg_name return MockNSServiceGroupServerBinding() def set_servicegroupname(self, sg_name): - ''' + """ Mock set_servicegroupname method - ''' + """ self.sg_name = sg_name return MockNSServiceGroupServerBinding() def set_port(self, sg_name): - ''' + """ Mock set_port method - ''' + """ self.sg_name = sg_name return MockNSServiceGroupServerBinding() @staticmethod def add(obj, servicegroup): - ''' + """ Mock add method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSServiceGroupServerBinding() @staticmethod def delete(obj, servicegroup): - ''' + """ Mock delete method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSServiceGroupServerBinding() class MockNSService(object): - ''' + """ Mock NSService class - ''' + """ + def __init__(self): self.sg_name = None def set_name(self, sg_name): - ''' + """ Mock set_name method - ''' + """ self.sg_name = sg_name return MockNSService() @staticmethod def get(obj, servicegroup): - ''' + """ Mock get method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSService() @staticmethod def enable(obj, servicegroup): - ''' + """ Mock enable method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSService() @staticmethod def disable(obj, servicegroup): - ''' + """ Mock disable method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSService() @staticmethod def get_svrstate(): - ''' + """ Mock get_svrstate method - ''' - return 'UP' + """ + return "UP" class MockNSServer(object): - ''' + """ Mock NSServer class - ''' + """ + flag = None def __init__(self): self.sg_name = None def set_name(self, sg_name): - ''' + """ Mock set_name method - ''' + """ self.sg_name = sg_name return MockNSServer() @staticmethod def get(obj, servicegroup): - ''' + """ Mock get method - ''' + """ return MockNSServer() @staticmethod def add(obj, servicegroup): - ''' + """ Mock add method - ''' + """ return MockNSServer() @staticmethod def delete(obj, servicegroup): - ''' + """ Mock delete method - ''' + """ return MockNSServer() @staticmethod def update(obj, servicegroup): - ''' + """ Mock update method - ''' + """ return MockNSServer() @staticmethod def enable(obj, servicegroup): - ''' + """ Mock enable method - ''' + """ return MockNSServer() @staticmethod def disable(obj, servicegroup): - ''' + """ Mock disable method - ''' + """ return MockNSServer() @staticmethod def get_ipaddress(): - ''' + """ Mock get_ipaddress method - ''' - return '' + """ + return "" @staticmethod def set_ipaddress(s_ip): - ''' + """ Mock set_ipaddress method - ''' + """ return s_ip def get_state(self): - ''' + """ Mock get_state method - ''' + """ if self.flag == 1: - return '' + return "" elif self.flag == 2: - return 'DISABLED' - return 'ENABLED' + return "DISABLED" + return "ENABLED" class MockNSLBVServer(object): - ''' + """ Mock NSLBVServer class - ''' + """ + def __init__(self): self.sg_name = None def set_name(self, sg_name): - ''' + """ Mock set_name method - ''' + """ self.sg_name = sg_name return MockNSLBVServer() @staticmethod def get(obj, servicegroup): - ''' + """ Mock get method - ''' + """ return MockNSLBVServer() @staticmethod def set_ipv46(v_ip): - ''' + """ Mock set_ipv46 method - ''' + """ return v_ip @staticmethod def set_port(v_port): - ''' + """ Mock set_port method - ''' + """ return v_port @staticmethod def set_servicetype(v_type): - ''' + """ Mock set_servicetype method - ''' + """ return v_type @staticmethod def get_ipv46(): - ''' + """ Mock get_ipv46 method - ''' - return '' + """ + return "" @staticmethod def get_port(): - ''' + """ Mock get_port method - ''' - return '' + """ + return "" @staticmethod def get_servicetype(): - ''' + """ Mock get_servicetype method - ''' - return '' + """ + return "" @staticmethod def add(obj, servicegroup): - ''' + """ Mock add method - ''' + """ return MockNSLBVServer() @staticmethod def delete(obj, servicegroup): - ''' + """ Mock delete method - ''' + """ return MockNSLBVServer() class MockNSLBVServerServiceGroupBinding(object): - ''' + """ Mock NSLBVServerServiceGroupBinding class - ''' + """ + flag = None def __init__(self): self.sg_name = None def set_name(self, sg_name): - ''' + """ Mock set_name method - ''' + """ self.sg_name = sg_name return MockNSLBVServerServiceGroupBinding() @staticmethod def get(obj, servicegroup): - ''' + """ Mock get method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return [MockNSLBVServerServiceGroupBinding()] @staticmethod def get_servicegroupname(): - ''' + """ Mock get_servicegroupname method - ''' - return 'serviceGroupName' + """ + return "serviceGroupName" def set_servicegroupname(self, sg_name): - ''' + """ Mock set_servicegroupname method - ''' + """ self.sg_name = sg_name if self.flag: return None @@ -480,642 +487,677 @@ class MockNSLBVServerServiceGroupBinding(object): @staticmethod def add(obj, servicegroup): - ''' + """ Mock add method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSLBVServerServiceGroupBinding() @staticmethod def delete(obj, servicegroup): - ''' + """ Mock delete method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSLBVServerServiceGroupBinding() class MockNSSSLVServerSSLCertKeyBinding(object): - ''' + """ Mock NSSSLVServerSSLCertKeyBinding class - ''' + """ + def __init__(self): self.sg_name = None def set_vservername(self, sg_name): - ''' + """ Mock set_vservername method - ''' + """ self.sg_name = sg_name return MockNSSSLVServerSSLCertKeyBinding() @staticmethod def get(obj, servicegroup): - ''' + """ Mock get method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return [MockNSSSLVServerSSLCertKeyBinding()] @staticmethod def get_certkeyname(): - ''' + """ Mock get_certkeyname method - ''' - return 'serviceGroupName' + """ + return "serviceGroupName" def set_certkeyname(self, sg_name): - ''' + """ Mock set_certkeyname method - ''' + """ self.sg_name = sg_name return MockNSSSLVServerSSLCertKeyBinding() @staticmethod def add(obj, servicegroup): - ''' + """ Mock add method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSSSLVServerSSLCertKeyBinding() @staticmethod def delete(obj, servicegroup): - ''' + """ Mock delete method - ''' + """ if MockNSNitro.flag: raise MockNSNitroError return MockNSSSLVServerSSLCertKeyBinding() class NetscalerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.netscaler - ''' + """ + def setup_loader_modules(self): return { netscaler: { - 'NSNitro': MockNSNitro, - 'NSServiceGroup': MockNSServiceGroup, - 'NSServiceGroupServerBinding': MockNSServiceGroupServerBinding, - 'NSLBVServerServiceGroupBinding': MockNSLBVServerServiceGroupBinding, - 'NSService': MockNSService, - 'NSServer': MockNSServer, - 'NSLBVServer': MockNSLBVServer, - 'NSNitroError': MockNSNitroError, - 'NSSSLVServerSSLCertKeyBinding': MockNSSSLVServerSSLCertKeyBinding, + "NSNitro": MockNSNitro, + "NSServiceGroup": MockNSServiceGroup, + "NSServiceGroupServerBinding": MockNSServiceGroupServerBinding, + "NSLBVServerServiceGroupBinding": MockNSLBVServerServiceGroupBinding, + "NSService": MockNSService, + "NSServer": MockNSServer, + "NSLBVServer": MockNSLBVServer, + "NSNitroError": MockNSNitroError, + "NSSSLVServerSSLCertKeyBinding": MockNSSSLVServerSSLCertKeyBinding, } } + # 'servicegroup_exists' function tests: 1 def test_servicegroup_exists(self): - ''' + """ Tests if it checks if a service group exists - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): MockNSNitro.flag = None - self.assertTrue(netscaler.servicegroup_exists('serviceGrpName')) + self.assertTrue(netscaler.servicegroup_exists("serviceGrpName")) - self.assertFalse(netscaler.servicegroup_exists('serviceGrpName', - sg_type='HTTP')) + self.assertFalse( + netscaler.servicegroup_exists("serviceGrpName", sg_type="HTTP") + ) MockNSNitro.flag = True - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.servicegroup_exists('serGrpNme')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.servicegroup_exists("serGrpNme")) # 'servicegroup_add' function tests: 1 def test_servicegroup_add(self): - ''' + """ Tests if it add a new service group - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertFalse(netscaler.servicegroup_add('serviceGroupName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertFalse(netscaler.servicegroup_add("serviceGroupName")) MockNSNitro.flag = True - self.assertFalse(netscaler.servicegroup_add('serviceGroupName')) + self.assertFalse(netscaler.servicegroup_add("serviceGroupName")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.servicegroup_add('serveGrpName')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.servicegroup_add("serveGrpName")) # 'servicegroup_delete' function tests: 1 def test_servicegroup_delete(self): - ''' + """ Tests if it delete a new service group - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): MockNSNitro.flag = None - self.assertTrue(netscaler.servicegroup_delete('serviceGrpName')) + self.assertTrue(netscaler.servicegroup_delete("serviceGrpName")) mock = MagicMock(side_effect=[None, MockNSServiceGroup()]) - with patch.object(netscaler, '_servicegroup_get', mock): + with patch.object(netscaler, "_servicegroup_get", mock): MockNSNitro.flag = True - self.assertFalse(netscaler.servicegroup_delete('srGrpName')) + self.assertFalse(netscaler.servicegroup_delete("srGrpName")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.servicegroup_delete('sGNam')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.servicegroup_delete("sGNam")) # 'servicegroup_server_exists' function tests: 1 def test_servicegroup_server_exists(self): - ''' + """ Tests if it check if a server:port combination is a member of a servicegroup - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertFalse(netscaler.servicegroup_server_exists - ('serviceGrpName', 'serverName', 'serverPort')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertFalse( + netscaler.servicegroup_server_exists( + "serviceGrpName", "serverName", "serverPort" + ) + ) # 'servicegroup_server_up' function tests: 1 def test_servicegroup_server_up(self): - ''' + """ Tests if it check if a server:port combination is a member of a servicegroup - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertFalse(netscaler.servicegroup_server_up - ('serviceGrpName', 'serverName', 'serverPort')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertFalse( + netscaler.servicegroup_server_up( + "serviceGrpName", "serverName", "serverPort" + ) + ) # 'servicegroup_server_enable' function tests: 1 def test_servicegroup_server_enable(self): - ''' + """ Tests if it enable a server:port member of a servicegroup - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertFalse(netscaler.servicegroup_server_enable - ('serviceGrpName', 'serverName', 'serverPort')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertFalse( + netscaler.servicegroup_server_enable( + "serviceGrpName", "serverName", "serverPort" + ) + ) - with patch.object(netscaler, '_servicegroup_get_server', - MagicMock(return_value=MockNSServiceGroup())): + with patch.object( + netscaler, + "_servicegroup_get_server", + MagicMock(return_value=MockNSServiceGroup()), + ): MockNSNitro.flag = None - self.assertTrue(netscaler.servicegroup_server_enable - ('servGrpName', 'serverName', 'serPort')) + self.assertTrue( + netscaler.servicegroup_server_enable( + "servGrpName", "serverName", "serPort" + ) + ) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.servicegroup_server_enable - ('serGrpName', 'serverName', 'sPort')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse( + netscaler.servicegroup_server_enable( + "serGrpName", "serverName", "sPort" + ) + ) # 'servicegroup_server_disable' function tests: 1 def test_sergrp_server_disable(self): - ''' + """ Tests if it disable a server:port member of a servicegroup - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertFalse(netscaler.servicegroup_server_disable - ('serviceGrpName', 'serverName', 'serverPort')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertFalse( + netscaler.servicegroup_server_disable( + "serviceGrpName", "serverName", "serverPort" + ) + ) - with patch.object(netscaler, '_servicegroup_get_server', - MagicMock(return_value=MockNSServiceGroup())): + with patch.object( + netscaler, + "_servicegroup_get_server", + MagicMock(return_value=MockNSServiceGroup()), + ): MockNSNitro.flag = None - self.assertTrue(netscaler.servicegroup_server_disable - ('serveGrpName', 'serverName', 'serPort')) + self.assertTrue( + netscaler.servicegroup_server_disable( + "serveGrpName", "serverName", "serPort" + ) + ) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.servicegroup_server_disable - ('servGrpName', 'serverName', 'sPort')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse( + netscaler.servicegroup_server_disable( + "servGrpName", "serverName", "sPort" + ) + ) # 'servicegroup_server_add' function tests: 1 def test_servicegroup_server_add(self): - ''' + """ Tests if it add a server:port member to a servicegroup - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.servicegroup_server_add - ('serGrpName', 'serverName', 'sPort')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse( + netscaler.servicegroup_server_add( + "serGrpName", "serverName", "sPort" + ) + ) MockNSNitro.flag = None - self.assertTrue(netscaler.servicegroup_server_add - ('serGrpName', 'serverName', 'serverPort')) + self.assertTrue( + netscaler.servicegroup_server_add( + "serGrpName", "serverName", "serverPort" + ) + ) - mock = MagicMock(return_value= - MockNSServiceGroupServerBinding()) - with patch.object(netscaler, '_servicegroup_get_server', - mock): + mock = MagicMock(return_value=MockNSServiceGroupServerBinding()) + with patch.object(netscaler, "_servicegroup_get_server", mock): MockNSNitro.flag = True - self.assertFalse(netscaler.servicegroup_server_add - ('serviceGroupName', 'serverName', - 'serPort')) + self.assertFalse( + netscaler.servicegroup_server_add( + "serviceGroupName", "serverName", "serPort" + ) + ) # 'servicegroup_server_delete' function tests: 1 def test_servicegroup_server_delete(self): - ''' + """ Tests if it remove a server:port member to a servicegroup - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.servicegroup_server_delete - ('servGrpName', 'serverName', 'sPort')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse( + netscaler.servicegroup_server_delete( + "servGrpName", "serverName", "sPort" + ) + ) - self.assertFalse(netscaler.servicegroup_server_delete - ('serviceGroupName', 'serverName', - 'serverPort')) + self.assertFalse( + netscaler.servicegroup_server_delete( + "serviceGroupName", "serverName", "serverPort" + ) + ) - mock = MagicMock(return_value= - MockNSServiceGroupServerBinding()) - with patch.object(netscaler, '_servicegroup_get_server', - mock): + mock = MagicMock(return_value=MockNSServiceGroupServerBinding()) + with patch.object(netscaler, "_servicegroup_get_server", mock): MockNSNitro.flag = None - self.assertTrue(netscaler.servicegroup_server_delete - ('serviceGroupName', 'serverName', - 'serPort')) + self.assertTrue( + netscaler.servicegroup_server_delete( + "serviceGroupName", "serverName", "serPort" + ) + ) # 'service_up' function tests: 1 def test_service_up(self): - ''' + """ Tests if it checks if a service is UP - ''' + """ mock = MagicMock(return_value=MockNSService()) - with patch.object(netscaler, '_service_get', mock): - self.assertTrue(netscaler.service_up('serviceGrpName')) + with patch.object(netscaler, "_service_get", mock): + self.assertTrue(netscaler.service_up("serviceGrpName")) # 'service_exists' function tests: 1 def test_service_exists(self): - ''' + """ Tests if it checks if a service is UP - ''' + """ mock = MagicMock(return_value=MockNSService()) - with patch.object(netscaler, '_service_get', mock): - self.assertTrue(netscaler.service_exists('serviceGrpName')) + with patch.object(netscaler, "_service_get", mock): + self.assertTrue(netscaler.service_exists("serviceGrpName")) # 'service_enable' function tests: 1 def test_service_enable(self): - ''' + """ Tests if it enable a service - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.service_enable('serviceGrpName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue(netscaler.service_enable("serviceGrpName")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.service_enable('serviceGrpName')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.service_enable("serviceGrpName")) mock = MagicMock(return_value=MockNSService()) - with patch.object(netscaler, '_service_get', mock): - self.assertFalse(netscaler.service_enable('serGrpName')) + with patch.object(netscaler, "_service_get", mock): + self.assertFalse(netscaler.service_enable("serGrpName")) # 'service_disable' function tests: 1 def test_service_disable(self): - ''' + """ Tests if it disable a service - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.service_disable('serviceGrpName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue(netscaler.service_disable("serviceGrpName")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.service_disable('serceGrpName')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.service_disable("serceGrpName")) mock = MagicMock(return_value=MockNSService()) - with patch.object(netscaler, '_service_get', mock): - self.assertFalse(netscaler.service_disable('seGrpName')) + with patch.object(netscaler, "_service_get", mock): + self.assertFalse(netscaler.service_disable("seGrpName")) # 'server_exists' function tests: 1 def test_server_exists(self): - ''' + """ Tests if it checks if a server exists - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.server_exists('serviceGrpName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue(netscaler.server_exists("serviceGrpName")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.server_exists('serviceGrpName')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.server_exists("serviceGrpName")) - self.assertFalse(netscaler.server_exists('serviceGrpName', - ip='1.0.0.1')) + self.assertFalse(netscaler.server_exists("serviceGrpName", ip="1.0.0.1")) - self.assertFalse(netscaler.server_exists('serviceGrpName', - s_state='serverName')) + self.assertFalse( + netscaler.server_exists("serviceGrpName", s_state="serverName") + ) # 'server_add' function tests: 1 def test_server_add(self): - ''' + """ Tests if it add a server - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertFalse(netscaler.server_add('servGrpName', '1.0.0.1')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertFalse(netscaler.server_add("servGrpName", "1.0.0.1")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.server_add('serviceGrpName', - '1.0.0.1')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.server_add("serviceGrpName", "1.0.0.1")) mock = MagicMock(return_value=False) - with patch.object(netscaler, 'server_exists', mock): - self.assertTrue(netscaler.server_add('serviceGrpName', - '1.0.0.1')) + with patch.object(netscaler, "server_exists", mock): + self.assertTrue(netscaler.server_add("serviceGrpName", "1.0.0.1")) # 'server_delete' function tests: 1 def test_server_delete(self): - ''' + """ Tests if it delete a server - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.server_delete('serviceGrpName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue(netscaler.server_delete("serviceGrpName")) mock = MagicMock(side_effect=[MockNSServer(), None]) - with patch.object(netscaler, '_server_get', mock): - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.server_delete('serGrpName')) + with patch.object(netscaler, "_server_get", mock): + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.server_delete("serGrpName")) - self.assertFalse(netscaler.server_delete('serviceGrpName')) + self.assertFalse(netscaler.server_delete("serviceGrpName")) # 'server_update' function tests: 1 def test_server_update(self): - ''' + """ Tests if it update a server's attributes - ''' - mock = MagicMock(side_effect=[None, MockNSServer(), MockNSServer(), - MockNSServer()]) - with patch.object(netscaler, '_server_get', mock): - self.assertFalse(netscaler.server_update('seGrName', '1.0.0.1')) + """ + mock = MagicMock( + side_effect=[None, MockNSServer(), MockNSServer(), MockNSServer()] + ) + with patch.object(netscaler, "_server_get", mock): + self.assertFalse(netscaler.server_update("seGrName", "1.0.0.1")) - self.assertFalse(netscaler.server_update('serGrpName', '')) + self.assertFalse(netscaler.server_update("serGrpName", "")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.server_update('serGrpName', - '1.0.0.1')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.server_update("serGrpName", "1.0.0.1")) - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.server_update('serGrpName', - '1.0.0.1')) + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue(netscaler.server_update("serGrpName", "1.0.0.1")) # 'server_enabled' function tests: 1 def test_server_enabled(self): - ''' + """ Tests if it check if a server is enabled globally - ''' + """ mock = MagicMock(return_value=MockNSServer()) - with patch.object(netscaler, '_server_get', mock): + with patch.object(netscaler, "_server_get", mock): MockNSServer.flag = None - self.assertTrue(netscaler.server_enabled('serGrpName')) + self.assertTrue(netscaler.server_enabled("serGrpName")) # 'server_enable' function tests: 1 def test_server_enable(self): - ''' + """ Tests if it enables a server globally - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.server_enable('serGrpName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue(netscaler.server_enable("serGrpName")) MockNSServer.flag = 1 - self.assertTrue(netscaler.server_enable('serGrpName')) + self.assertTrue(netscaler.server_enable("serGrpName")) mock = MagicMock(side_effect=[MockNSServer(), None]) - with patch.object(netscaler, '_server_get', mock): - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.server_enable('serGrpName')) + with patch.object(netscaler, "_server_get", mock): + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.server_enable("serGrpName")) - self.assertFalse(netscaler.server_enable('serGrpName')) + self.assertFalse(netscaler.server_enable("serGrpName")) # 'server_disable' function tests: 1 def test_server_disable(self): - ''' + """ Tests if it disable a server globally - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.server_disable('serGrpName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue(netscaler.server_disable("serGrpName")) MockNSServer.flag = 2 - self.assertTrue(netscaler.server_disable('serGrpName')) + self.assertTrue(netscaler.server_disable("serGrpName")) MockNSServer.flag = None mock = MagicMock(side_effect=[None, MockNSServer()]) - with patch.object(netscaler, '_server_get', mock): - self.assertFalse(netscaler.server_disable('serGrpName')) + with patch.object(netscaler, "_server_get", mock): + self.assertFalse(netscaler.server_disable("serGrpName")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.server_disable('serGrpName')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.server_disable("serGrpName")) # 'vserver_exists' function tests: 1 def test_vserver_exists(self): - ''' + """ Tests if it checks if a vserver exists - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.vserver_exists('vserverName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue(netscaler.vserver_exists("vserverName")) - self.assertFalse(netscaler.vserver_exists('vserverName', - v_ip='1.0.0.1')) + self.assertFalse(netscaler.vserver_exists("vserverName", v_ip="1.0.0.1")) - self.assertFalse(netscaler.vserver_exists('vserrName', v_ip='', - v_port='vserverPort')) + self.assertFalse( + netscaler.vserver_exists("vserrName", v_ip="", v_port="vserverPort") + ) - self.assertFalse(netscaler.vserver_exists('vserrName', v_ip='', - v_port='', - v_type='vserverType')) + self.assertFalse( + netscaler.vserver_exists( + "vserrName", v_ip="", v_port="", v_type="vserverType" + ) + ) mock = MagicMock(return_value=None) - with patch.object(netscaler, '_vserver_get', mock): - self.assertFalse(netscaler.vserver_exists('vserverName')) + with patch.object(netscaler, "_vserver_get", mock): + self.assertFalse(netscaler.vserver_exists("vserverName")) # 'vserver_add' function tests: 1 def test_vserver_add(self): - ''' + """ Tests if it add a new lb vserver - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertFalse(netscaler.vserver_add('alex.patate.chaude.443', - '1.2.3.4', '443', 'SSL')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertFalse( + netscaler.vserver_add("alex.patate.chaude.443", "1.2.3.4", "443", "SSL") + ) mock = MagicMock(return_value=False) - with patch.object(netscaler, 'vserver_exists', mock): - self.assertTrue(netscaler.vserver_add('alex.pae.chaude.443', - '1.2.3.4', '443', - 'SSL')) + with patch.object(netscaler, "vserver_exists", mock): + self.assertTrue( + netscaler.vserver_add( + "alex.pae.chaude.443", "1.2.3.4", "443", "SSL" + ) + ) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.vserver_add('alex.chde.443', - '1.2.3.4', '443', - 'SSL')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse( + netscaler.vserver_add("alex.chde.443", "1.2.3.4", "443", "SSL") + ) # 'vserver_delete' function tests: 1 def test_vserver_delete(self): - ''' + """ Tests if it delete a new lb vserver - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.vserver_delete('alex.pe.chaude.443')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue(netscaler.vserver_delete("alex.pe.chaude.443")) mock = MagicMock(side_effect=[None, MockNSLBVServer()]) - with patch.object(netscaler, '_vserver_get', mock): - self.assertFalse(netscaler.vserver_delete('alex.chade.443')) + with patch.object(netscaler, "_vserver_get", mock): + self.assertFalse(netscaler.vserver_delete("alex.chade.443")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.vserver_delete('al.cha.443')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.vserver_delete("al.cha.443")) # 'vserver_servicegroup_exists' function tests: 1 def test_vser_sergrp_exists(self): - ''' + """ Tests if it checks if a servicegroup is tied to a vserver - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.vserver_servicegroup_exists - ('vserverName', 'serviceGroupName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue( + netscaler.vserver_servicegroup_exists("vserverName", "serviceGroupName") + ) # 'vserver_servicegroup_add' function tests: 1 def test_vserver_servicegroup_add(self): - ''' + """ Tests if it bind a servicegroup to a vserver - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): MockNSNitro.flag = None - self.assertTrue(netscaler.vserver_servicegroup_add - ('vserverName', 'serGroupName')) + self.assertTrue( + netscaler.vserver_servicegroup_add("vserverName", "serGroupName") + ) - mock = MagicMock(side_effect= - [MockNSLBVServerServiceGroupBinding(), None]) - with patch.object(netscaler, 'vserver_servicegroup_exists', - mock): - self.assertFalse(netscaler.vserver_servicegroup_add - ('vserName', 'serGroupName')) + mock = MagicMock(side_effect=[MockNSLBVServerServiceGroupBinding(), None]) + with patch.object(netscaler, "vserver_servicegroup_exists", mock): + self.assertFalse( + netscaler.vserver_servicegroup_add("vserName", "serGroupName") + ) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.vserver_servicegroup_add - ('vName', 'serGroupName')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse( + netscaler.vserver_servicegroup_add("vName", "serGroupName") + ) # 'vserver_servicegroup_delete' function tests: 1 def test_vser_sergrp_delete(self): - ''' + """ Tests if it unbind a servicegroup from a vserver - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertFalse(netscaler.vserver_servicegroup_delete - ('vservName', 'serGroupName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertFalse( + netscaler.vserver_servicegroup_delete("vservName", "serGroupName") + ) - mock = MagicMock(return_value= - MockNSLBVServerServiceGroupBinding()) - with patch.object(netscaler, 'vserver_servicegroup_exists', - mock): + mock = MagicMock(return_value=MockNSLBVServerServiceGroupBinding()) + with patch.object(netscaler, "vserver_servicegroup_exists", mock): MockNSNitro.flag = None - self.assertTrue(netscaler.vserver_servicegroup_delete - ('vName', 'serGroupName')) + self.assertTrue( + netscaler.vserver_servicegroup_delete("vName", "serGroupName") + ) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.vserver_servicegroup_delete - ('vserverName', 'serGroupName')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse( + netscaler.vserver_servicegroup_delete( + "vserverName", "serGroupName" + ) + ) # 'vserver_sslcert_exists' function tests: 1 def test_vserver_sslcert_exists(self): - ''' + """ Tests if it checks if a SSL certificate is tied to a vserver - ''' - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): - self.assertTrue(netscaler.vserver_sslcert_exists - ('vserverName', 'serviceGroupName')) + """ + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): + self.assertTrue( + netscaler.vserver_sslcert_exists("vserverName", "serviceGroupName") + ) # 'vserver_sslcert_add' function tests: 1 def test_vserver_sslcert_add(self): - ''' + """ Tests if it binds a SSL certificate to a vserver - ''' - mock = MagicMock(side_effect=[MockNSSSLVServerSSLCertKeyBinding(), - None, None]) - with patch.object(netscaler, 'vserver_sslcert_exists', mock): - self.assertFalse(netscaler.vserver_sslcert_add - ('vserName', 'serGroupName')) + """ + mock = MagicMock(side_effect=[MockNSSSLVServerSSLCertKeyBinding(), None, None]) + with patch.object(netscaler, "vserver_sslcert_exists", mock): + self.assertFalse(netscaler.vserver_sslcert_add("vserName", "serGroupName")) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.vserver_sslcert_add - ('vName', 'serGrName')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse(netscaler.vserver_sslcert_add("vName", "serGrName")) - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): MockNSNitro.flag = None - self.assertTrue(netscaler.vserver_sslcert_add - ('vserverName', 'serGroupName')) + self.assertTrue( + netscaler.vserver_sslcert_add("vserverName", "serGroupName") + ) # 'vserver_sslcert_delete' function tests: 1 def test_vserver_sslcert_delete(self): - ''' + """ Tests if it unbinds a SSL certificate from a vserver - ''' - mock = MagicMock(side_effect=[None, - MockNSSSLVServerSSLCertKeyBinding(), - MockNSSSLVServerSSLCertKeyBinding()]) - with patch.object(netscaler, 'vserver_sslcert_exists', mock): - self.assertFalse(netscaler.vserver_sslcert_delete('vName', - 'serGrpName')) + """ + mock = MagicMock( + side_effect=[ + None, + MockNSSSLVServerSSLCertKeyBinding(), + MockNSSSLVServerSSLCertKeyBinding(), + ] + ) + with patch.object(netscaler, "vserver_sslcert_exists", mock): + self.assertFalse(netscaler.vserver_sslcert_delete("vName", "serGrpName")) - mock = MagicMock(return_value='') - with patch.dict(netscaler.__salt__, {'config.option': mock}): + mock = MagicMock(return_value="") + with patch.dict(netscaler.__salt__, {"config.option": mock}): MockNSNitro.flag = None - self.assertTrue(netscaler.vserver_sslcert_delete - ('vservName', 'serGroupName')) + self.assertTrue( + netscaler.vserver_sslcert_delete("vservName", "serGroupName") + ) - with patch.object(netscaler, '_connect', - MagicMock(return_value=None)): - self.assertFalse(netscaler.vserver_sslcert_delete - ('vserverName', 'serGroupName')) + with patch.object(netscaler, "_connect", MagicMock(return_value=None)): + self.assertFalse( + netscaler.vserver_sslcert_delete("vserverName", "serGroupName") + ) diff --git a/tests/unit/modules/test_network.py b/tests/unit/modules/test_network.py index 7b36f965f5b..5b2ed7686e0 100644 --- a/tests/unit/modules/test_network.py +++ b/tests/unit/modules/test_network.py @@ -1,53 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import logging -import socket -import os.path +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - mock_open, - MagicMock, - patch, -) +import logging +import os.path +import socket + +import salt.modules.network as network # Import Salt Libs import salt.utils.network import salt.utils.path -import salt.modules.network as network -from salt.exceptions import CommandExecutionError from salt._compat import ipaddress +from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase, skipIf log = logging.getLogger(__name__) class NetworkTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.network - ''' + """ + def setup_loader_modules(self): return {network: {}} def test_wol_bad_mac(self): - ''' + """ tests network.wol with bad mac - ''' - bad_mac = '31337' + """ + bad_mac = "31337" self.assertRaises(ValueError, network.wol, bad_mac) def test_wol_success(self): - ''' + """ tests network.wol success - ''' - mac = '080027136977' - bcast = '255.255.255.255 7' + """ + mac = "080027136977" + bcast = "255.255.255.255 7" class MockSocket(object): def __init__(self, *args, **kwargs): @@ -62,341 +61,370 @@ class NetworkTestCase(TestCase, LoaderModuleMockMixin): def sendto(self, *args, **kwargs): pass - with patch('socket.socket', MockSocket): + with patch("socket.socket", MockSocket): self.assertTrue(network.wol(mac, bcast)) def test_ping(self): - ''' + """ Test for Performs a ping to a host - ''' - with patch.object(salt.utils.network, 'sanitize_host', - return_value='A'): - mock_all = MagicMock(side_effect=[{'retcode': 1}, {'retcode': 0}]) - with patch.dict(network.__salt__, {'cmd.run_all': mock_all}): - self.assertFalse(network.ping('host', return_boolean=True)) + """ + with patch.object(salt.utils.network, "sanitize_host", return_value="A"): + mock_all = MagicMock(side_effect=[{"retcode": 1}, {"retcode": 0}]) + with patch.dict(network.__salt__, {"cmd.run_all": mock_all}): + self.assertFalse(network.ping("host", return_boolean=True)) - self.assertTrue(network.ping('host', return_boolean=True)) + self.assertTrue(network.ping("host", return_boolean=True)) - with patch.dict(network.__salt__, {'cmd.run': - MagicMock(return_value='A')}): - self.assertEqual(network.ping('host'), 'A') + with patch.dict(network.__salt__, {"cmd.run": MagicMock(return_value="A")}): + self.assertEqual(network.ping("host"), "A") def test_netstat(self): - ''' + """ Test for return information on open ports and states - ''' - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - with patch.object(network, '_netstat_linux', return_value='A'): - with patch.object(network, '_ss_linux', return_value='A'): - self.assertEqual(network.netstat(), 'A') + """ + with patch.dict(network.__grains__, {"kernel": "Linux"}): + with patch.object(network, "_netstat_linux", return_value="A"): + with patch.object(network, "_ss_linux", return_value="A"): + self.assertEqual(network.netstat(), "A") - with patch.dict(network.__grains__, {'kernel': 'OpenBSD'}): - with patch.object(network, '_netstat_bsd', return_value='A'): - self.assertEqual(network.netstat(), 'A') + with patch.dict(network.__grains__, {"kernel": "OpenBSD"}): + with patch.object(network, "_netstat_bsd", return_value="A"): + self.assertEqual(network.netstat(), "A") - with patch.dict(network.__grains__, {'kernel': 'A'}): + with patch.dict(network.__grains__, {"kernel": "A"}): self.assertRaises(CommandExecutionError, network.netstat) def test_active_tcp(self): - ''' + """ Test for return a dict containing information on all of the running TCP connections - ''' - with patch.object(salt.utils.network, 'active_tcp', return_value='A'): - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - self.assertEqual(network.active_tcp(), 'A') + """ + with patch.object(salt.utils.network, "active_tcp", return_value="A"): + with patch.dict(network.__grains__, {"kernel": "Linux"}): + self.assertEqual(network.active_tcp(), "A") def test_traceroute(self): - ''' + """ Test for Performs a traceroute to a 3rd party host - ''' - with patch('salt.utils.path.which', MagicMock(return_value='traceroute')): - with patch.dict(network.__salt__, {'cmd.run': MagicMock(return_value='')}): - self.assertListEqual(network.traceroute('gentoo.org'), []) + """ + with patch("salt.utils.path.which", MagicMock(return_value="traceroute")): + with patch.dict(network.__salt__, {"cmd.run": MagicMock(return_value="")}): + self.assertListEqual(network.traceroute("gentoo.org"), []) - with patch.object(salt.utils.network, 'sanitize_host', - return_value='gentoo.org'): - with patch.dict(network.__salt__, {'cmd.run': - MagicMock(return_value='')}): - self.assertListEqual(network.traceroute('gentoo.org'), []) + with patch.object( + salt.utils.network, "sanitize_host", return_value="gentoo.org" + ): + with patch.dict( + network.__salt__, {"cmd.run": MagicMock(return_value="")} + ): + self.assertListEqual(network.traceroute("gentoo.org"), []) def test_dig(self): - ''' + """ Test for Performs a DNS lookup with dig - ''' - with patch('salt.utils.path.which', MagicMock(return_value='dig')), \ - patch.object(salt.utils.network, 'sanitize_host', - return_value='A'), \ - patch.dict(network.__salt__, {'cmd.run': - MagicMock(return_value='A')}): - self.assertEqual(network.dig('host'), 'A') + """ + with patch( + "salt.utils.path.which", MagicMock(return_value="dig") + ), patch.object( + salt.utils.network, "sanitize_host", return_value="A" + ), patch.dict( + network.__salt__, {"cmd.run": MagicMock(return_value="A")} + ): + self.assertEqual(network.dig("host"), "A") def test_arp(self): - ''' + """ Test for return the arp table from the minion - ''' - with patch.dict(network.__salt__, - {'cmd.run': - MagicMock(return_value='A,B,C,D\nE,F,G,H\n')}), \ - patch('salt.utils.path.which', MagicMock(return_value='')): + """ + with patch.dict( + network.__salt__, {"cmd.run": MagicMock(return_value="A,B,C,D\nE,F,G,H\n")} + ), patch("salt.utils.path.which", MagicMock(return_value="")): self.assertDictEqual(network.arp(), {}) def test_interfaces(self): - ''' + """ Test for return a dictionary of information about all the interfaces on the minion - ''' - with patch.object(salt.utils.network, 'interfaces', return_value={}): + """ + with patch.object(salt.utils.network, "interfaces", return_value={}): self.assertDictEqual(network.interfaces(), {}) def test_hw_addr(self): - ''' + """ Test for return the hardware address (a.k.a. MAC address) for a given interface - ''' - with patch.object(salt.utils.network, 'hw_addr', return_value={}): - self.assertDictEqual(network.hw_addr('iface'), {}) + """ + with patch.object(salt.utils.network, "hw_addr", return_value={}): + self.assertDictEqual(network.hw_addr("iface"), {}) def test_interface(self): - ''' + """ Test for return the inet address for a given interface - ''' - with patch.object(salt.utils.network, 'interface', return_value={}): - self.assertDictEqual(network.interface('iface'), {}) + """ + with patch.object(salt.utils.network, "interface", return_value={}): + self.assertDictEqual(network.interface("iface"), {}) def test_interface_ip(self): - ''' + """ Test for return the inet address for a given interface - ''' - with patch.object(salt.utils.network, 'interface_ip', return_value={}): - self.assertDictEqual(network.interface_ip('iface'), {}) + """ + with patch.object(salt.utils.network, "interface_ip", return_value={}): + self.assertDictEqual(network.interface_ip("iface"), {}) def test_subnets(self): - ''' + """ Test for returns a list of subnets to which the host belongs - ''' - with patch.object(salt.utils.network, 'subnets', return_value={}): + """ + with patch.object(salt.utils.network, "subnets", return_value={}): self.assertDictEqual(network.subnets(), {}) def test_in_subnet(self): - ''' + """ Test for returns True if host is within specified subnet, otherwise False. - ''' - with patch.object(salt.utils.network, 'in_subnet', return_value={}): - self.assertDictEqual(network.in_subnet('iface'), {}) + """ + with patch.object(salt.utils.network, "in_subnet", return_value={}): + self.assertDictEqual(network.in_subnet("iface"), {}) def test_ip_addrs(self): - ''' + """ Test for returns a list of IPv4 addresses assigned to the host. - ''' - with patch.object(salt.utils.network, 'ip_addrs', - return_value=['0.0.0.0']): - with patch.object(salt.utils.network, 'in_subnet', - return_value=True): - self.assertListEqual(network.ip_addrs('interface', - 'include_loopback', - 'cidr'), ['0.0.0.0']) + """ + with patch.object(salt.utils.network, "ip_addrs", return_value=["0.0.0.0"]): + with patch.object(salt.utils.network, "in_subnet", return_value=True): + self.assertListEqual( + network.ip_addrs("interface", "include_loopback", "cidr"), + ["0.0.0.0"], + ) - self.assertListEqual(network.ip_addrs('interface', - 'include_loopback'), - ['0.0.0.0']) + self.assertListEqual( + network.ip_addrs("interface", "include_loopback"), ["0.0.0.0"] + ) def test_ip_addrs6(self): - ''' + """ Test for returns a list of IPv6 addresses assigned to the host. - ''' - with patch.object(salt.utils.network, 'ip_addrs6', - return_value=['A']): - self.assertListEqual(network.ip_addrs6('int', 'include'), ['A']) + """ + with patch.object(salt.utils.network, "ip_addrs6", return_value=["A"]): + self.assertListEqual(network.ip_addrs6("int", "include"), ["A"]) def test_get_hostname(self): - ''' + """ Test for Get hostname - ''' - with patch.object(network.socket, 'gethostname', return_value='A'): - self.assertEqual(network.get_hostname(), 'A') + """ + with patch.object(network.socket, "gethostname", return_value="A"): + self.assertEqual(network.get_hostname(), "A") def test_mod_hostname(self): - ''' + """ Test for Modify hostname - ''' + """ self.assertFalse(network.mod_hostname(None)) - file_d = '\n'.join(['#', 'A B C D,E,F G H']) + file_d = "\n".join(["#", "A B C D,E,F G H"]) - with patch.object(salt.utils.path, 'which', return_value='hostname'), \ - patch.dict(network.__salt__, - {'cmd.run': MagicMock(return_value=None)}), \ - patch.dict(network.__grains__, {'os_family': 'A'}), \ - patch('salt.utils.files.fopen', mock_open(read_data=file_d)): - self.assertTrue(network.mod_hostname('hostname')) + with patch.object( + salt.utils.path, "which", return_value="hostname" + ), patch.dict( + network.__salt__, {"cmd.run": MagicMock(return_value=None)} + ), patch.dict( + network.__grains__, {"os_family": "A"} + ), patch( + "salt.utils.files.fopen", mock_open(read_data=file_d) + ): + self.assertTrue(network.mod_hostname("hostname")) def test_connect(self): - ''' + """ Test for Test connectivity to a host using a particular port from the minion. - ''' - with patch('socket.socket') as mock_socket: - self.assertDictEqual(network.connect(False, 'port'), - {'comment': 'Required argument, host, is missing.', - 'result': False}) + """ + with patch("socket.socket") as mock_socket: + self.assertDictEqual( + network.connect(False, "port"), + {"comment": "Required argument, host, is missing.", "result": False}, + ) - self.assertDictEqual(network.connect('host', False), - {'comment': 'Required argument, port, is missing.', - 'result': False}) + self.assertDictEqual( + network.connect("host", False), + {"comment": "Required argument, port, is missing.", "result": False}, + ) - ret = 'Unable to connect to host (0) on tcp port port' - mock_socket.side_effect = Exception('foo') - with patch.object(salt.utils.network, 'sanitize_host', - return_value='A'): - with patch.object(socket, 'getaddrinfo', - return_value=[['ipv4', 'A', 6, 'B', '0.0.0.0']]): - self.assertDictEqual(network.connect('host', 'port'), - {'comment': ret, 'result': False}) + ret = "Unable to connect to host (0) on tcp port port" + mock_socket.side_effect = Exception("foo") + with patch.object(salt.utils.network, "sanitize_host", return_value="A"): + with patch.object( + socket, + "getaddrinfo", + return_value=[["ipv4", "A", 6, "B", "0.0.0.0"]], + ): + self.assertDictEqual( + network.connect("host", "port"), + {"comment": ret, "result": False}, + ) - ret = 'Successfully connected to host (0) on tcp port port' + ret = "Successfully connected to host (0) on tcp port port" mock_socket.side_effect = MagicMock() mock_socket.settimeout().return_value = None mock_socket.connect().return_value = None mock_socket.shutdown().return_value = None - with patch.object(salt.utils.network, 'sanitize_host', - return_value='A'): - with patch.object(socket, - 'getaddrinfo', - return_value=[['ipv4', - 'A', 6, 'B', '0.0.0.0']]): - self.assertDictEqual(network.connect('host', 'port'), - {'comment': ret, 'result': True}) + with patch.object(salt.utils.network, "sanitize_host", return_value="A"): + with patch.object( + socket, + "getaddrinfo", + return_value=[["ipv4", "A", 6, "B", "0.0.0.0"]], + ): + self.assertDictEqual( + network.connect("host", "port"), + {"comment": ret, "result": True}, + ) - @skipIf(not bool(ipaddress), 'unable to import \'ipaddress\'') + @skipIf(not bool(ipaddress), "unable to import 'ipaddress'") def test_is_private(self): - ''' + """ Test for Check if the given IP address is a private address - ''' - with patch.object(ipaddress.IPv4Address, 'is_private', - return_value=True): - self.assertTrue(network.is_private('0.0.0.0')) - with patch.object(ipaddress.IPv6Address, 'is_private', - return_value=True): - self.assertTrue(network.is_private('::1')) + """ + with patch.object(ipaddress.IPv4Address, "is_private", return_value=True): + self.assertTrue(network.is_private("0.0.0.0")) + with patch.object(ipaddress.IPv6Address, "is_private", return_value=True): + self.assertTrue(network.is_private("::1")) - @skipIf(not bool(ipaddress), 'unable to import \'ipaddress\'') + @skipIf(not bool(ipaddress), "unable to import 'ipaddress'") def test_is_loopback(self): - ''' + """ Test for Check if the given IP address is a loopback address - ''' - with patch.object(ipaddress.IPv4Address, 'is_loopback', - return_value=True): - self.assertTrue(network.is_loopback('127.0.0.1')) - with patch.object(ipaddress.IPv6Address, 'is_loopback', - return_value=True): - self.assertTrue(network.is_loopback('::1')) + """ + with patch.object(ipaddress.IPv4Address, "is_loopback", return_value=True): + self.assertTrue(network.is_loopback("127.0.0.1")) + with patch.object(ipaddress.IPv6Address, "is_loopback", return_value=True): + self.assertTrue(network.is_loopback("::1")) def test_get_bufsize(self): - ''' + """ Test for return network buffer sizes as a dict - ''' - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - with patch.object(os.path, 'exists', return_value=True): - with patch.object(network, '_get_bufsize_linux', - return_value={'size': 1}): - self.assertDictEqual(network.get_bufsize('iface'), - {'size': 1}) + """ + with patch.dict(network.__grains__, {"kernel": "Linux"}): + with patch.object(os.path, "exists", return_value=True): + with patch.object( + network, "_get_bufsize_linux", return_value={"size": 1} + ): + self.assertDictEqual(network.get_bufsize("iface"), {"size": 1}) - with patch.dict(network.__grains__, {'kernel': 'A'}): - self.assertDictEqual(network.get_bufsize('iface'), {}) + with patch.dict(network.__grains__, {"kernel": "A"}): + self.assertDictEqual(network.get_bufsize("iface"), {}) def test_mod_bufsize(self): - ''' + """ Test for Modify network interface buffers (currently linux only) - ''' - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - with patch.object(os.path, 'exists', return_value=True): - with patch.object(network, '_mod_bufsize_linux', - return_value={'size': 1}): - self.assertDictEqual(network.mod_bufsize('iface'), - {'size': 1}) + """ + with patch.dict(network.__grains__, {"kernel": "Linux"}): + with patch.object(os.path, "exists", return_value=True): + with patch.object( + network, "_mod_bufsize_linux", return_value={"size": 1} + ): + self.assertDictEqual(network.mod_bufsize("iface"), {"size": 1}) - with patch.dict(network.__grains__, {'kernel': 'A'}): - self.assertFalse(network.mod_bufsize('iface')) + with patch.dict(network.__grains__, {"kernel": "A"}): + self.assertFalse(network.mod_bufsize("iface")) def test_routes(self): - ''' + """ Test for return currently configured routes from routing table - ''' - self.assertRaises(CommandExecutionError, network.routes, 'family') + """ + self.assertRaises(CommandExecutionError, network.routes, "family") - with patch.dict(network.__grains__, {'kernel': 'A', 'os': 'B'}): - self.assertRaises(CommandExecutionError, network.routes, 'inet') + with patch.dict(network.__grains__, {"kernel": "A", "os": "B"}): + self.assertRaises(CommandExecutionError, network.routes, "inet") - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - with patch.object(network, '_netstat_route_linux', - side_effect=['A', [{'addr_family': 'inet'}]]): - with patch.object(network, '_ip_route_linux', - side_effect=['A', [{'addr_family': 'inet'}]]): - self.assertEqual(network.routes(None), 'A') + with patch.dict(network.__grains__, {"kernel": "Linux"}): + with patch.object( + network, + "_netstat_route_linux", + side_effect=["A", [{"addr_family": "inet"}]], + ): + with patch.object( + network, + "_ip_route_linux", + side_effect=["A", [{"addr_family": "inet"}]], + ): + self.assertEqual(network.routes(None), "A") - self.assertListEqual(network.routes('inet'), - [{'addr_family': 'inet'}]) + self.assertListEqual( + network.routes("inet"), [{"addr_family": "inet"}] + ) def test_default_route(self): - ''' + """ Test for return default route(s) from routing table - ''' - self.assertRaises(CommandExecutionError, network.default_route, - 'family') + """ + self.assertRaises(CommandExecutionError, network.default_route, "family") - with patch.object(network, 'routes', - side_effect=[[{'addr_family': 'inet'}, - {'destination': 'A'}], []]): - with patch.dict(network.__grains__, {'kernel': 'A', - 'os': 'B'}): - self.assertRaises(CommandExecutionError, - network.default_route, 'inet') + with patch.object( + network, + "routes", + side_effect=[[{"addr_family": "inet"}, {"destination": "A"}], []], + ): + with patch.dict(network.__grains__, {"kernel": "A", "os": "B"}): + self.assertRaises(CommandExecutionError, network.default_route, "inet") - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - self.assertListEqual(network.default_route('inet'), []) + with patch.dict(network.__grains__, {"kernel": "Linux"}): + self.assertListEqual(network.default_route("inet"), []) def test_get_route(self): - ''' + """ Test for return output from get_route - ''' - mock_iproute = MagicMock(return_value='8.8.8.8 via 10.10.10.1 dev eth0 src 10.10.10.10 uid 0\ncache') - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - with patch.dict(network.__salt__, {'cmd.run': mock_iproute}): - expected = {'interface': 'eth0', - 'source': '10.10.10.10', - 'destination': '8.8.8.8', - 'gateway': '10.10.10.1'} - ret = network.get_route('8.8.8.8') + """ + mock_iproute = MagicMock( + return_value="8.8.8.8 via 10.10.10.1 dev eth0 src 10.10.10.10 uid 0\ncache" + ) + with patch.dict(network.__grains__, {"kernel": "Linux"}): + with patch.dict(network.__salt__, {"cmd.run": mock_iproute}): + expected = { + "interface": "eth0", + "source": "10.10.10.10", + "destination": "8.8.8.8", + "gateway": "10.10.10.1", + } + ret = network.get_route("8.8.8.8") self.assertEqual(ret, expected) - mock_iproute = MagicMock(return_value='8.8.8.8 via 10.10.10.1 dev eth0.1 src 10.10.10.10 uid 0\ncache') - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - with patch.dict(network.__salt__, {'cmd.run': mock_iproute}): - expected = {'interface': 'eth0.1', - 'source': '10.10.10.10', - 'destination': '8.8.8.8', - 'gateway': '10.10.10.1'} - ret = network.get_route('8.8.8.8') + mock_iproute = MagicMock( + return_value="8.8.8.8 via 10.10.10.1 dev eth0.1 src 10.10.10.10 uid 0\ncache" + ) + with patch.dict(network.__grains__, {"kernel": "Linux"}): + with patch.dict(network.__salt__, {"cmd.run": mock_iproute}): + expected = { + "interface": "eth0.1", + "source": "10.10.10.10", + "destination": "8.8.8.8", + "gateway": "10.10.10.1", + } + ret = network.get_route("8.8.8.8") self.assertEqual(ret, expected) - mock_iproute = MagicMock(return_value='8.8.8.8 via 10.10.10.1 dev eth0:1 src 10.10.10.10 uid 0\ncache') - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - with patch.dict(network.__salt__, {'cmd.run': mock_iproute}): - expected = {'interface': 'eth0:1', - 'source': '10.10.10.10', - 'destination': '8.8.8.8', - 'gateway': '10.10.10.1'} - ret = network.get_route('8.8.8.8') + mock_iproute = MagicMock( + return_value="8.8.8.8 via 10.10.10.1 dev eth0:1 src 10.10.10.10 uid 0\ncache" + ) + with patch.dict(network.__grains__, {"kernel": "Linux"}): + with patch.dict(network.__salt__, {"cmd.run": mock_iproute}): + expected = { + "interface": "eth0:1", + "source": "10.10.10.10", + "destination": "8.8.8.8", + "gateway": "10.10.10.1", + } + ret = network.get_route("8.8.8.8") self.assertEqual(ret, expected) - mock_iproute = MagicMock(return_value='8.8.8.8 via 10.10.10.1 dev lan-br0 src 10.10.10.10 uid 0\ncache') - with patch.dict(network.__grains__, {'kernel': 'Linux'}): - with patch.dict(network.__salt__, {'cmd.run': mock_iproute}): - expected = {'interface': 'lan-br0', - 'source': '10.10.10.10', - 'destination': '8.8.8.8', - 'gateway': '10.10.10.1'} - ret = network.get_route('8.8.8.8') + mock_iproute = MagicMock( + return_value="8.8.8.8 via 10.10.10.1 dev lan-br0 src 10.10.10.10 uid 0\ncache" + ) + with patch.dict(network.__grains__, {"kernel": "Linux"}): + with patch.dict(network.__salt__, {"cmd.run": mock_iproute}): + expected = { + "interface": "lan-br0", + "source": "10.10.10.10", + "destination": "8.8.8.8", + "gateway": "10.10.10.1", + } + ret = network.get_route("8.8.8.8") self.assertEqual(ret, expected) diff --git a/tests/unit/modules/test_neutron.py b/tests/unit/modules/test_neutron.py index 423b6f26aa8..da174fdfb91 100644 --- a/tests/unit/modules/test_neutron.py +++ b/tests/unit/modules/test_neutron.py @@ -1,26 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) - # Import Salt Libs import salt.modules.neutron as neutron +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock +from tests.support.unit import TestCase + class MockNeutron(object): """ Mock of neutron """ + @staticmethod def get_quotas_tenant(): """ @@ -43,14 +42,29 @@ class MockNeutron(object): return tenant_id @staticmethod - def update_quota(tenant_id, subnet, router, network, - floatingip, port, security_group, - security_group_rule): + def update_quota( + tenant_id, + subnet, + router, + network, + floatingip, + port, + security_group, + security_group_rule, + ): """ Mock of update_quota method """ - return (tenant_id, subnet, router, network, floatingip, port, - security_group, security_group_rule) + return ( + tenant_id, + subnet, + router, + network, + floatingip, + port, + security_group, + security_group_rule, + ) @staticmethod def delete_quota(tenant_id): @@ -116,11 +130,27 @@ class MockNeutron(object): return network @staticmethod - def create_network(name, admin_state_up, router_ext, network_type, physical_network, segmentation_id, shared): + def create_network( + name, + admin_state_up, + router_ext, + network_type, + physical_network, + segmentation_id, + shared, + ): """ Mock of create_network method """ - return (name, admin_state_up, router_ext, network_type, physical_network, segmentation_id, shared) + return ( + name, + admin_state_up, + router_ext, + network_type, + physical_network, + segmentation_id, + shared, + ) @staticmethod def update_network(network, name): @@ -319,18 +349,27 @@ class MockNeutron(object): return security_group_rule_id @staticmethod - def create_security_group_rule(security_group, - remote_group_id, - direction, - protocol, - port_range_min, - port_range_max, - ethertype): + def create_security_group_rule( + security_group, + remote_group_id, + direction, + protocol, + port_range_min, + port_range_max, + ethertype, + ): """ Mock of create_security_group_rule method """ - return (security_group, remote_group_id, direction, protocol, - port_range_min, port_range_max, ethertype) + return ( + security_group, + remote_group_id, + direction, + protocol, + port_range_min, + port_range_max, + ethertype, + ) @staticmethod def delete_security_group_rule(security_group_rule_id): @@ -389,21 +428,33 @@ class MockNeutron(object): return ipsec_site_connection @staticmethod - def create_ipsec_site_connection(name, - ipsecpolicy, - ikepolicy, - vpnservice, - peer_cidrs, - peer_address, - peer_id, - psk, - admin_state_up, - **kwargs): + def create_ipsec_site_connection( + name, + ipsecpolicy, + ikepolicy, + vpnservice, + peer_cidrs, + peer_address, + peer_id, + psk, + admin_state_up, + **kwargs + ): """ Mock of create_ipsec_site_connection method """ - return (name, ipsecpolicy, ikepolicy, vpnservice, peer_cidrs, - peer_address, peer_id, psk, admin_state_up, kwargs) + return ( + name, + ipsecpolicy, + ikepolicy, + vpnservice, + peer_cidrs, + peer_address, + peer_id, + psk, + admin_state_up, + kwargs, + ) @staticmethod def delete_ipsec_site_connection(ipsec_site_connection): @@ -470,564 +521,595 @@ class MockNeutron(object): class NeutronTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.neutron - ''' + """ + def setup_loader_modules(self): - return {neutron: {'_auth': MagicMock(return_value=MockNeutron())}} + return {neutron: {"_auth": MagicMock(return_value=MockNeutron())}} # 'get_quotas_tenant' function tests: 1 def test_get_quotas_tenant(self): - ''' + """ Test if it fetches tenant info in server's context for following quota operation - ''' - self.assertTrue(neutron.get_quotas_tenant(profile='openstack1')) + """ + self.assertTrue(neutron.get_quotas_tenant(profile="openstack1")) # 'list_quotas' function tests: 1 def test_list_quotas(self): - ''' + """ Test if it fetches all tenants quotas - ''' - self.assertTrue(neutron.list_quotas(profile='openstack1')) + """ + self.assertTrue(neutron.list_quotas(profile="openstack1")) # 'show_quota' function tests: 1 def test_show_quota(self): - ''' + """ Test if it fetches information of a certain tenant's quotas - ''' - self.assertTrue(neutron.show_quota('Salt', profile='openstack1')) + """ + self.assertTrue(neutron.show_quota("Salt", profile="openstack1")) # 'update_quota' function tests: 1 def test_update_quota(self): - ''' + """ Test if it update a tenant's quota - ''' - self.assertTrue(neutron.update_quota('Salt', subnet='40', - router='50', network='10', - floatingip='30', port='30', - security_group='10', - security_group_rule='SS')) + """ + self.assertTrue( + neutron.update_quota( + "Salt", + subnet="40", + router="50", + network="10", + floatingip="30", + port="30", + security_group="10", + security_group_rule="SS", + ) + ) # 'delete_quota' function tests: 1 def test_delete_quota(self): - ''' + """ Test if it delete the specified tenant's quota value - ''' - self.assertTrue(neutron.delete_quota('Salt', profile='openstack1')) + """ + self.assertTrue(neutron.delete_quota("Salt", profile="openstack1")) # 'list_extensions' function tests: 1 def test_list_extensions(self): - ''' + """ Test if it fetches a list of all extensions on server side - ''' - self.assertTrue(neutron.list_extensions(profile='openstack1')) + """ + self.assertTrue(neutron.list_extensions(profile="openstack1")) # 'list_ports' function tests: 1 def test_list_ports(self): - ''' + """ Test if it fetches a list of all networks for a tenant - ''' - self.assertTrue(neutron.list_ports(profile='openstack1')) + """ + self.assertTrue(neutron.list_ports(profile="openstack1")) # 'show_port' function tests: 1 def test_show_port(self): - ''' + """ Test if it fetches information of a certain port - ''' - self.assertTrue(neutron.show_port('1080', profile='openstack1')) + """ + self.assertTrue(neutron.show_port("1080", profile="openstack1")) # 'create_port' function tests: 1 def test_create_port(self): - ''' + """ Test if it creates a new port - ''' - self.assertTrue(neutron.create_port('Salt', 'SALTSTACK', - device_id='800', - admin_state_up=True, - profile='openstack1')) + """ + self.assertTrue( + neutron.create_port( + "Salt", + "SALTSTACK", + device_id="800", + admin_state_up=True, + profile="openstack1", + ) + ) # 'update_port' function tests: 1 def test_update_port(self): - ''' + """ Test if it updates a port - ''' - self.assertTrue(neutron.update_port('800', 'SALTSTACK', - admin_state_up=True, - profile='openstack1')) + """ + self.assertTrue( + neutron.update_port( + "800", "SALTSTACK", admin_state_up=True, profile="openstack1" + ) + ) # 'delete_port' function tests: 1 def test_delete_port(self): - ''' + """ Test if it deletes the specified port - ''' - self.assertTrue(neutron.delete_port('1080', profile='openstack1')) + """ + self.assertTrue(neutron.delete_port("1080", profile="openstack1")) # 'list_networks' function tests: 1 def test_list_networks(self): - ''' + """ Test if it fetches a list of all networks for a tenant - ''' - self.assertTrue(neutron.list_networks(profile='openstack1')) + """ + self.assertTrue(neutron.list_networks(profile="openstack1")) # 'show_network' function tests: 1 def test_show_network(self): - ''' + """ Test if it fetches information of a certain network - ''' - self.assertTrue(neutron.show_network('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.show_network("SALTSTACK", profile="openstack1")) # 'create_network' function tests: 1 def test_create_network(self): - ''' + """ Test if it creates a new network - ''' - self.assertTrue(neutron.create_network('SALT', - profile='openstack1')) + """ + self.assertTrue(neutron.create_network("SALT", profile="openstack1")) # 'update_network' function tests: 1 def test_update_network(self): - ''' + """ Test if it updates a network - ''' - self.assertTrue(neutron.update_network('SALT', 'SLATSTACK', - profile='openstack1')) + """ + self.assertTrue( + neutron.update_network("SALT", "SLATSTACK", profile="openstack1") + ) # 'delete_network' function tests: 1 def test_delete_network(self): - ''' + """ Test if it deletes the specified network - ''' - self.assertTrue(neutron.delete_network('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.delete_network("SALTSTACK", profile="openstack1")) # 'list_subnets' function tests: 1 def test_list_subnets(self): - ''' + """ Test if it fetches a list of all networks for a tenant - ''' - self.assertTrue(neutron.list_subnets(profile='openstack1')) + """ + self.assertTrue(neutron.list_subnets(profile="openstack1")) # 'show_subnet' function tests: 1 def test_show_subnet(self): - ''' + """ Test if it fetches information of a certain subnet - ''' - self.assertTrue(neutron.show_subnet('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.show_subnet("SALTSTACK", profile="openstack1")) # 'create_subnet' function tests: 1 def test_create_subnet(self): - ''' + """ Test if it creates a new subnet - ''' - self.assertTrue(neutron.create_subnet('192.168.1.0', - '192.168.1.0/24', - name='Salt', - ip_version=4, - profile='openstack1')) + """ + self.assertTrue( + neutron.create_subnet( + "192.168.1.0", + "192.168.1.0/24", + name="Salt", + ip_version=4, + profile="openstack1", + ) + ) # 'update_subnet' function tests: 1 def test_update_subnet(self): - ''' + """ Test if it updates a subnet - ''' - self.assertTrue(neutron.update_subnet('255.255.255.0', - name='Salt', - profile='openstack1')) + """ + self.assertTrue( + neutron.update_subnet("255.255.255.0", name="Salt", profile="openstack1") + ) # 'delete_subnet' function tests: 1 def test_delete_subnet(self): - ''' + """ Test if it deletes the specified subnet - ''' - self.assertTrue(neutron.delete_subnet('255.255.255.0', - profile='openstack1')) + """ + self.assertTrue(neutron.delete_subnet("255.255.255.0", profile="openstack1")) # 'list_routers' function tests: 1 def test_list_routers(self): - ''' + """ Test if it fetches a list of all routers for a tenant - ''' - self.assertTrue(neutron.list_routers(profile='openstack1')) + """ + self.assertTrue(neutron.list_routers(profile="openstack1")) # 'show_router' function tests: 1 def test_show_router(self): - ''' + """ Test if it fetches information of a certain router - ''' - self.assertTrue(neutron.show_router('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.show_router("SALTSTACK", profile="openstack1")) # 'create_router' function tests: 1 def test_create_router(self): - ''' + """ Test if it creates a new router - ''' - self.assertTrue(neutron.create_router('SALT', - '192.168.1.0', - admin_state_up=True, - profile='openstack1')) + """ + self.assertTrue( + neutron.create_router( + "SALT", "192.168.1.0", admin_state_up=True, profile="openstack1" + ) + ) # 'update_router' function tests: 1 def test_update_router(self): - ''' + """ Test if it updates a router - ''' - self.assertTrue(neutron.update_router('255.255.255.0', - name='Salt', - profile='openstack1')) + """ + self.assertTrue( + neutron.update_router("255.255.255.0", name="Salt", profile="openstack1") + ) # 'delete_router' function tests: 1 def test_delete_router(self): - ''' + """ Test if it delete the specified router - ''' - self.assertTrue(neutron.delete_router('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.delete_router("SALTSTACK", profile="openstack1")) # 'add_interface_router' function tests: 1 def test_add_interface_router(self): - ''' + """ Test if it adds an internal network interface to the specified router - ''' - self.assertTrue(neutron.add_interface_router('Salt', - '255.255.255.0', - profile='openstack1')) + """ + self.assertTrue( + neutron.add_interface_router("Salt", "255.255.255.0", profile="openstack1") + ) # 'remove_interface_router' function tests: 1 def test_remove_interface_router(self): - ''' + """ Test if it removes an internal network interface from the specified router - ''' - self.assertTrue(neutron.remove_interface_router - ('Salt', '255.255.255.0', profile='openstack1')) + """ + self.assertTrue( + neutron.remove_interface_router( + "Salt", "255.255.255.0", profile="openstack1" + ) + ) # 'add_gateway_router' function tests: 1 def test_add_gateway_router(self): - ''' + """ Test if it adds an external network gateway to the specified router - ''' - self.assertTrue(neutron.add_gateway_router - ('Salt', 'SALTSTACK', profile='openstack1')) + """ + self.assertTrue( + neutron.add_gateway_router("Salt", "SALTSTACK", profile="openstack1") + ) # 'remove_gateway_router' function tests: 1 def test_remove_gateway_router(self): - ''' + """ Test if it removes an external network gateway from the specified router - ''' + """ - self.assertTrue(neutron.remove_gateway_router('SALTSTACK', - profile='openstack1')) + self.assertTrue( + neutron.remove_gateway_router("SALTSTACK", profile="openstack1") + ) # 'list_floatingips' function tests: 1 def test_list_floatingips(self): - ''' + """ Test if it fetch a list of all floatingIPs for a tenant - ''' - self.assertTrue(neutron.list_floatingips(profile='openstack1')) + """ + self.assertTrue(neutron.list_floatingips(profile="openstack1")) # 'show_floatingip' function tests: 1 def test_show_floatingip(self): - ''' + """ Test if it fetches information of a certain floatingIP - ''' - self.assertTrue(neutron.show_floatingip('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.show_floatingip("SALTSTACK", profile="openstack1")) # 'create_floatingip' function tests: 1 def test_create_floatingip(self): - ''' + """ Test if it creates a new floatingIP - ''' - self.assertTrue(neutron.create_floatingip('SALTSTACK', port='800', - profile='openstack1')) + """ + self.assertTrue( + neutron.create_floatingip("SALTSTACK", port="800", profile="openstack1") + ) # 'update_floatingip' function tests: 1 def test_update_floatingip(self): - ''' + """ Test if it updates a floatingIP - ''' - self.assertTrue(neutron.update_floatingip('SALTSTACK', port='800', - profile='openstack1')) + """ + self.assertTrue( + neutron.update_floatingip("SALTSTACK", port="800", profile="openstack1") + ) # 'delete_floatingip' function tests: 1 def test_delete_floatingip(self): - ''' + """ Test if it deletes the specified floating IP - ''' - self.assertTrue(neutron.delete_floatingip('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.delete_floatingip("SALTSTACK", profile="openstack1")) # 'list_security_groups' function tests: 1 def test_list_security_groups(self): - ''' + """ Test if it fetches a list of all security groups for a tenant - ''' - self.assertTrue(neutron.list_security_groups(profile='openstack1')) + """ + self.assertTrue(neutron.list_security_groups(profile="openstack1")) # 'show_security_group' function tests: 1 def test_show_security_group(self): - ''' + """ Test if it fetches information of a certain security group - ''' - self.assertTrue(neutron.show_security_group('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.show_security_group("SALTSTACK", profile="openstack1")) # 'create_security_group' function tests: 1 def test_create_security_group(self): - ''' + """ Test if it creates a new security group - ''' - self.assertTrue(neutron.create_security_group('SALTSTACK', - 'Security group', - profile='openstack1')) + """ + self.assertTrue( + neutron.create_security_group( + "SALTSTACK", "Security group", profile="openstack1" + ) + ) # 'update_security_group' function tests: 1 def test_update_security_group(self): - ''' + """ Test if it updates a security group - ''' - self.assertTrue(neutron.update_security_group('SALT', 'SALTSTACK', - 'Security group', - profile='openstack1')) + """ + self.assertTrue( + neutron.update_security_group( + "SALT", "SALTSTACK", "Security group", profile="openstack1" + ) + ) # 'delete_security_group' function tests: 1 def test_delete_security_group(self): - ''' + """ Test if it deletes the specified security group - ''' - self.assertTrue(neutron.delete_security_group('SALT', - profile='openstack1')) + """ + self.assertTrue(neutron.delete_security_group("SALT", profile="openstack1")) # 'list_security_group_rules' function tests: 1 def test_list_security_group_rules(self): - ''' + """ Test if it fetches a list of all security group rules for a tenant - ''' - self.assertTrue(neutron.list_security_group_rules - (profile='openstack1')) + """ + self.assertTrue(neutron.list_security_group_rules(profile="openstack1")) # 'show_security_group_rule' function tests: 1 def test_show_security_group_rule(self): - ''' + """ Test if it fetches information of a certain security group rule - ''' - self.assertTrue(neutron.show_security_group_rule - ('SALTSTACK', profile='openstack1')) + """ + self.assertTrue( + neutron.show_security_group_rule("SALTSTACK", profile="openstack1") + ) # 'create_security_group_rule' function tests: 1 def test_create_security_group_rule(self): - ''' + """ Test if it creates a new security group rule - ''' - self.assertTrue(neutron.create_security_group_rule - ('SALTSTACK', profile='openstack1')) + """ + self.assertTrue( + neutron.create_security_group_rule("SALTSTACK", profile="openstack1") + ) # 'delete_security_group_rule' function tests: 1 def test_delete_security_group_rule(self): - ''' + """ Test if it deletes the specified security group rule - ''' - self.assertTrue(neutron.delete_security_group_rule - ('SALTSTACK', profile='openstack1')) + """ + self.assertTrue( + neutron.delete_security_group_rule("SALTSTACK", profile="openstack1") + ) # 'list_vpnservices' function tests: 1 def test_list_vpnservices(self): - ''' + """ Test if it fetches a list of all configured VPN services for a tenant - ''' - self.assertTrue(neutron.list_vpnservices(True, - profile='openstack1')) + """ + self.assertTrue(neutron.list_vpnservices(True, profile="openstack1")) # 'show_vpnservice' function tests: 1 def test_show_vpnservice(self): - ''' + """ Test if it fetches information of a specific VPN service - ''' - self.assertTrue(neutron.show_vpnservice('SALT', - profile='openstack1')) + """ + self.assertTrue(neutron.show_vpnservice("SALT", profile="openstack1")) # 'create_vpnservice' function tests: 1 def test_create_vpnservice(self): - ''' + """ Test if it creates a new VPN service - ''' - self.assertTrue(neutron.create_vpnservice('255.255.255.0', 'SALT', - 'SALTSTACK', True, - profile='openstack1')) + """ + self.assertTrue( + neutron.create_vpnservice( + "255.255.255.0", "SALT", "SALTSTACK", True, profile="openstack1" + ) + ) # 'update_vpnservice' function tests: 1 def test_update_vpnservice(self): - ''' + """ Test if it updates a VPN service - ''' - self.assertTrue(neutron.update_vpnservice('SALT', 'VPN Service1', - profile='openstack1')) + """ + self.assertTrue( + neutron.update_vpnservice("SALT", "VPN Service1", profile="openstack1") + ) # 'delete_vpnservice' function tests: 1 def test_delete_vpnservice(self): - ''' + """ Test if it deletes the specified VPN service - ''' - self.assertTrue(neutron.delete_vpnservice('SALT VPN Service1', - profile='openstack1')) + """ + self.assertTrue( + neutron.delete_vpnservice("SALT VPN Service1", profile="openstack1") + ) # 'list_ipsec_site_connections' function tests: 1 def test_list_ipsec_site(self): - ''' + """ Test if it fetches all configured IPsec Site Connections for a tenant - ''' - self.assertTrue(neutron.list_ipsec_site_connections - (profile='openstack1')) + """ + self.assertTrue(neutron.list_ipsec_site_connections(profile="openstack1")) # 'show_ipsec_site_connection' function tests: 1 def test_show_ipsec_site_connection(self): - ''' + """ Test if it fetches information of a specific IPsecSiteConnection - ''' - self.assertTrue(neutron.show_ipsec_site_connection - ('SALT', profile='openstack1')) + """ + self.assertTrue( + neutron.show_ipsec_site_connection("SALT", profile="openstack1") + ) # 'create_ipsec_site_connection' function tests: 1 def test_create_ipsec_site(self): - ''' + """ Test if it creates a new IPsecSiteConnection - ''' - self.assertTrue(neutron.create_ipsec_site_connection - ('SALTSTACK', 'A', 'B', 'C', '192.168.1.0/24', - '192.168.1.11', '192.168.1.10', 'secret', - profile='openstack1')) + """ + self.assertTrue( + neutron.create_ipsec_site_connection( + "SALTSTACK", + "A", + "B", + "C", + "192.168.1.0/24", + "192.168.1.11", + "192.168.1.10", + "secret", + profile="openstack1", + ) + ) # 'delete_ipsec_site_connection' function tests: 1 def test_delete_ipsec_site(self): - ''' + """ Test if it deletes the specified IPsecSiteConnection - ''' - self.assertTrue(neutron.delete_ipsec_site_connection - ('SALT VPN Service1', profile='openstack1')) + """ + self.assertTrue( + neutron.delete_ipsec_site_connection( + "SALT VPN Service1", profile="openstack1" + ) + ) # 'list_ikepolicies' function tests: 1 def test_list_ikepolicies(self): - ''' + """ Test if it fetches a list of all configured IKEPolicies for a tenant - ''' - self.assertTrue(neutron.list_ikepolicies(profile='openstack1')) + """ + self.assertTrue(neutron.list_ikepolicies(profile="openstack1")) # 'show_ikepolicy' function tests: 1 def test_show_ikepolicy(self): - ''' + """ Test if it fetches information of a specific IKEPolicy - ''' - self.assertTrue(neutron.show_ikepolicy('SALT', - profile='openstack1')) + """ + self.assertTrue(neutron.show_ikepolicy("SALT", profile="openstack1")) # 'create_ikepolicy' function tests: 1 def test_create_ikepolicy(self): - ''' + """ Test if it creates a new IKEPolicy - ''' - self.assertTrue(neutron.create_ikepolicy('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.create_ikepolicy("SALTSTACK", profile="openstack1")) # 'delete_ikepolicy' function tests: 1 def test_delete_ikepolicy(self): - ''' + """ Test if it deletes the specified IKEPolicy - ''' - self.assertTrue(neutron.delete_ikepolicy('SALT', - profile='openstack1')) + """ + self.assertTrue(neutron.delete_ikepolicy("SALT", profile="openstack1")) # 'list_ipsecpolicies' function tests: 1 def test_list_ipsecpolicies(self): - ''' + """ Test if it fetches a list of all configured IPsecPolicies for a tenant - ''' - self.assertTrue(neutron.list_ipsecpolicies(profile='openstack1')) + """ + self.assertTrue(neutron.list_ipsecpolicies(profile="openstack1")) # 'show_ipsecpolicy' function tests: 1 def test_show_ipsecpolicy(self): - ''' + """ Test if it fetches information of a specific IPsecPolicy - ''' - self.assertTrue(neutron.show_ipsecpolicy('SALT', - profile='openstack1')) + """ + self.assertTrue(neutron.show_ipsecpolicy("SALT", profile="openstack1")) # 'create_ipsecpolicy' function tests: 1 def test_create_ipsecpolicy(self): - ''' + """ Test if it creates a new IPsecPolicy - ''' - self.assertTrue(neutron.create_ipsecpolicy('SALTSTACK', - profile='openstack1')) + """ + self.assertTrue(neutron.create_ipsecpolicy("SALTSTACK", profile="openstack1")) # 'delete_ipsecpolicy' function tests: 1 def test_delete_ipsecpolicy(self): - ''' + """ Test if it deletes the specified IPsecPolicy - ''' - self.assertTrue(neutron.delete_ipsecpolicy('SALT', - profile='openstack1')) + """ + self.assertTrue(neutron.delete_ipsecpolicy("SALT", profile="openstack1")) diff --git a/tests/unit/modules/test_nexus.py b/tests/unit/modules/test_nexus.py index 3315b0867e4..2300cb17a57 100644 --- a/tests/unit/modules/test_nexus.py +++ b/tests/unit/modules/test_nexus.py @@ -3,22 +3,24 @@ # Import pytohn libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt libs import salt.modules.nexus as nexus +# Import Salt testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class nexusTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {nexus: {}} def test_artifact_get_metadata(self): - with patch('salt.modules.nexus._get_artifact_metadata_xml', MagicMock(return_value='''<?xml version="1.0" encoding="UTF-8"?> + with patch( + "salt.modules.nexus._get_artifact_metadata_xml", + MagicMock( + return_value="""<?xml version="1.0" encoding="UTF-8"?> <metadata> <groupId>com.company.sampleapp.web-module</groupId> <artifactId>web</artifactId> @@ -32,16 +34,23 @@ class nexusTestCase(TestCase, LoaderModuleMockMixin): </versions> <lastUpdated>20171010143552</lastUpdated> </versioning> -</metadata>''')): - metadata = nexus._get_artifact_metadata(nexus_url='http://nexus.example.com/repository', - repository='libs-releases', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - headers={}) - self.assertEqual(metadata['latest_version'], '0.1.0') +</metadata>""" + ), + ): + metadata = nexus._get_artifact_metadata( + nexus_url="http://nexus.example.com/repository", + repository="libs-releases", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + headers={}, + ) + self.assertEqual(metadata["latest_version"], "0.1.0") def test_snapshot_version_get_metadata(self): - with patch('salt.modules.nexus._get_snapshot_version_metadata_xml', MagicMock(return_value='''<?xml version="1.0" encoding="UTF-8"?> + with patch( + "salt.modules.nexus._get_snapshot_version_metadata_xml", + MagicMock( + return_value="""<?xml version="1.0" encoding="UTF-8"?> <metadata modelVersion="1.1.0"> <groupId>com.company.sampleapp.web-module</groupId> <artifactId>web</artifactId> @@ -67,53 +76,84 @@ class nexusTestCase(TestCase, LoaderModuleMockMixin): </snapshotVersion> </snapshotVersions> </versioning> -</metadata>''')): - metadata = nexus._get_snapshot_version_metadata(nexus_url='http://nexus.example.com/repository', - repository='libs-releases', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - version='0.0.2-SNAPSHOT', - headers={}) - self.assertEqual(metadata['snapshot_versions']['dist'], '0.0.2-20170920.212353-3') +</metadata>""" + ), + ): + metadata = nexus._get_snapshot_version_metadata( + nexus_url="http://nexus.example.com/repository", + repository="libs-releases", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + version="0.0.2-SNAPSHOT", + headers={}, + ) + self.assertEqual( + metadata["snapshot_versions"]["dist"], "0.0.2-20170920.212353-3" + ) def test_artifact_metadata_url(self): - metadata_url = nexus._get_artifact_metadata_url(nexus_url='http://nexus.example.com/repository', - repository='libs-releases', - group_id='com.company.sampleapp.web-module', - artifact_id='web') + metadata_url = nexus._get_artifact_metadata_url( + nexus_url="http://nexus.example.com/repository", + repository="libs-releases", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + ) - self.assertEqual(metadata_url, "http://nexus.example.com/repository/libs-releases/com/company/sampleapp/web-module/web/maven-metadata.xml") + self.assertEqual( + metadata_url, + "http://nexus.example.com/repository/libs-releases/com/company/sampleapp/web-module/web/maven-metadata.xml", + ) def test_snapshot_version_metadata_url(self): - metadata_url = nexus._get_snapshot_version_metadata_url(nexus_url='http://nexus.example.com/repository', - repository='libs-snapshots', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - version='0.0.2-SNAPSHOT') + metadata_url = nexus._get_snapshot_version_metadata_url( + nexus_url="http://nexus.example.com/repository", + repository="libs-snapshots", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + version="0.0.2-SNAPSHOT", + ) - self.assertEqual(metadata_url, "http://nexus.example.com/repository/libs-snapshots/com/company/sampleapp/web-module/web/0.0.2-SNAPSHOT/maven-metadata.xml") + self.assertEqual( + metadata_url, + "http://nexus.example.com/repository/libs-snapshots/com/company/sampleapp/web-module/web/0.0.2-SNAPSHOT/maven-metadata.xml", + ) def test_construct_url_for_released_version(self): - artifact_url, file_name = nexus._get_release_url(repository='libs-releases', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - packaging='zip', - nexus_url='http://nexus.example.com/repository', - version='0.1.0') + artifact_url, file_name = nexus._get_release_url( + repository="libs-releases", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + packaging="zip", + nexus_url="http://nexus.example.com/repository", + version="0.1.0", + ) - self.assertEqual(artifact_url, "http://nexus.example.com/repository/libs-releases/com/company/sampleapp/web-module/web/0.1.0/web-0.1.0.zip") + self.assertEqual( + artifact_url, + "http://nexus.example.com/repository/libs-releases/com/company/sampleapp/web-module/web/0.1.0/web-0.1.0.zip", + ) self.assertEqual(file_name, "web-0.1.0.zip") def test_construct_url_for_snapshot_version(self): - with patch('salt.modules.nexus._get_snapshot_version_metadata', MagicMock(return_value={'snapshot_versions': {'zip': '0.0.2-20170920.212353-3'}})): + with patch( + "salt.modules.nexus._get_snapshot_version_metadata", + MagicMock( + return_value={"snapshot_versions": {"zip": "0.0.2-20170920.212353-3"}} + ), + ): - artifact_url, file_name = nexus._get_snapshot_url(nexus_url='http://nexus.example.com/repository', - repository='libs-snapshots', - group_id='com.company.sampleapp.web-module', - artifact_id='web', - version='0.2.0-SNAPSHOT', - packaging='zip', - headers={}) + artifact_url, file_name = nexus._get_snapshot_url( + nexus_url="http://nexus.example.com/repository", + repository="libs-snapshots", + group_id="com.company.sampleapp.web-module", + artifact_id="web", + version="0.2.0-SNAPSHOT", + packaging="zip", + headers={}, + ) - self.assertEqual(artifact_url, "http://nexus.example.com/repository/libs-snapshots/com/company/sampleapp/web-module/web/0.2.0-SNAPSHOT/web-0.0.2-20170920.212353-3.zip") + self.assertEqual( + artifact_url, + "http://nexus.example.com/repository/libs-snapshots/com/company/sampleapp/web-module/web/0.2.0-SNAPSHOT/web-0.0.2-20170920.212353-3.zip", + ) self.assertEqual(file_name, "web-0.0.2-20170920.212353-3.zip") diff --git a/tests/unit/modules/test_nfs3.py b/tests/unit/modules/test_nfs3.py index 557dbac5dac..64cb854c2ac 100644 --- a/tests/unit/modules/test_nfs3.py +++ b/tests/unit/modules/test_nfs3.py @@ -1,50 +1,45 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - mock_open, - patch, -) - # Import Salt Libs import salt.modules.nfs3 as nfs3 +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class NfsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.nfs3 - ''' + """ + def setup_loader_modules(self): return {nfs3: {}} def test_list_exports(self): - ''' + """ Test for List configured exports - ''' - with patch('salt.utils.files.fopen', mock_open(read_data='A B1(23')): + """ + with patch("salt.utils.files.fopen", mock_open(read_data="A B1(23")): exports = nfs3.list_exports() - assert exports == {'A': [{'hosts': 'B1', 'options': ['23']}]}, exports + assert exports == {"A": [{"hosts": "B1", "options": ["23"]}]}, exports def test_del_export(self): - ''' + """ Test for Remove an export - ''' - list_exports_mock = MagicMock(return_value={ - 'A': [ - {'hosts': ['B1'], - 'options': ['23']}, - ], - }) - with patch.object(nfs3, 'list_exports', list_exports_mock), \ - patch.object(nfs3, '_write_exports', MagicMock(return_value=None)): - result = nfs3.del_export(path='A') + """ + list_exports_mock = MagicMock( + return_value={"A": [{"hosts": ["B1"], "options": ["23"]}]} + ) + with patch.object(nfs3, "list_exports", list_exports_mock), patch.object( + nfs3, "_write_exports", MagicMock(return_value=None) + ): + result = nfs3.del_export(path="A") assert result == {}, result diff --git a/tests/unit/modules/test_nftables.py b/tests/unit/modules/test_nftables.py index e6e83c2759a..c4369ecc462 100644 --- a/tests/unit/modules/test_nftables.py +++ b/tests/unit/modules/test_nftables.py @@ -1,122 +1,137 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - mock_open, - patch, -) - # Import Salt Libs import salt.modules.nftables as nftables import salt.utils.files from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class NftablesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.nftables - ''' + """ + def setup_loader_modules(self): return {nftables: {}} # 'version' function tests: 1 def test_version(self): - ''' + """ Test if it return version from nftables --version - ''' - mock = MagicMock(return_value='nf_tables 0.3-1') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.version(), '0.3-1') + """ + mock = MagicMock(return_value="nf_tables 0.3-1") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.version(), "0.3-1") # 'build_rule' function tests: 1 def test_build_rule(self): - ''' + """ Test if it build a well-formatted nftables rule based on kwargs. - ''' - self.assertEqual(nftables.build_rule(full='True'), - {'result': False, - 'rule': '', - 'comment': 'Table needs to be specified'}) + """ + self.assertEqual( + nftables.build_rule(full="True"), + {"result": False, "rule": "", "comment": "Table needs to be specified"}, + ) - self.assertEqual(nftables.build_rule(table='filter', full='True'), - {'result': False, - 'rule': '', - 'comment': 'Chain needs to be specified'}) + self.assertEqual( + nftables.build_rule(table="filter", full="True"), + {"result": False, "rule": "", "comment": "Chain needs to be specified"}, + ) - self.assertEqual(nftables.build_rule(table='filter', chain='input', - full='True'), - {'result': False, - 'rule': '', - 'comment': 'Command needs to be specified'}) + self.assertEqual( + nftables.build_rule(table="filter", chain="input", full="True"), + {"result": False, "rule": "", "comment": "Command needs to be specified"}, + ) - self.assertEqual(nftables.build_rule(table='filter', chain='input', - command='insert', position='3', - full='True'), - {'result': True, - 'rule': 'nft insert rule ip filter input position 3 ', - 'comment': 'Successfully built rule'}) + self.assertEqual( + nftables.build_rule( + table="filter", + chain="input", + command="insert", + position="3", + full="True", + ), + { + "result": True, + "rule": "nft insert rule ip filter input position 3 ", + "comment": "Successfully built rule", + }, + ) - self.assertEqual(nftables.build_rule(table='filter', chain='input', - command='insert', full='True'), - {'result': True, - 'rule': 'nft insert rule ip filter input ', - 'comment': 'Successfully built rule'}) + self.assertEqual( + nftables.build_rule( + table="filter", chain="input", command="insert", full="True" + ), + { + "result": True, + "rule": "nft insert rule ip filter input ", + "comment": "Successfully built rule", + }, + ) - self.assertEqual(nftables.build_rule(table='filter', chain='input', - command='halt', full='True'), - {'result': True, - 'rule': 'nft halt rule ip filter input ', - 'comment': 'Successfully built rule'}) + self.assertEqual( + nftables.build_rule( + table="filter", chain="input", command="halt", full="True" + ), + { + "result": True, + "rule": "nft halt rule ip filter input ", + "comment": "Successfully built rule", + }, + ) - self.assertEqual(nftables.build_rule(), - {'result': True, - 'rule': '', - 'comment': ''}) + self.assertEqual( + nftables.build_rule(), {"result": True, "rule": "", "comment": ""} + ) # 'get_saved_rules' function tests: 1 def test_get_saved_rules(self): - ''' + """ Test if it return a data structure of the rules in the conf file - ''' - with patch.dict(nftables.__grains__, {'os_family': 'Debian'}): - with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())): + """ + with patch.dict(nftables.__grains__, {"os_family": "Debian"}): + with patch.object(salt.utils.files, "fopen", MagicMock(mock_open())): self.assertListEqual(nftables.get_saved_rules(), []) # 'list_tables' function tests: 1 def test_list_tables(self): - ''' + """ Test if it return a data structure of the current, in-memory tables - ''' - list_tables = [{'family': 'inet', 'name': 'filter', 'handle': 2}] + """ + list_tables = [{"family": "inet", "name": "filter", "handle": 2}] list_tables_mock = MagicMock(return_value=list_tables) - with patch.object(nftables, 'list_tables', list_tables_mock): + with patch.object(nftables, "list_tables", list_tables_mock): self.assertListEqual(nftables.list_tables(), list_tables) list_tables_mock = MagicMock(return_value=[]) - with patch.object(nftables, 'list_tables', list_tables_mock): + with patch.object(nftables, "list_tables", list_tables_mock): self.assertListEqual(nftables.list_tables(), []) # 'get_rules' function tests: 1 def test_get_rules(self): - ''' + """ Test if it return a data structure of the current, in-memory rules - ''' - list_tables_mock = MagicMock(return_value=[{'family': 'inet', 'name': 'filter', 'handle': 2}]) + """ + list_tables_mock = MagicMock( + return_value=[{"family": "inet", "name": "filter", "handle": 2}] + ) list_rules_return = """table inet filter { chain input { type filter hook input priority 0; policy accept; @@ -133,550 +148,633 @@ class NftablesTestCase(TestCase, LoaderModuleMockMixin): list_rules_mock = MagicMock(return_value=list_rules_return) expected = [list_rules_return] - with patch.object(nftables, 'list_tables', list_tables_mock): - with patch.dict(nftables.__salt__, {'cmd.run': list_rules_mock}): + with patch.object(nftables, "list_tables", list_tables_mock): + with patch.dict(nftables.__salt__, {"cmd.run": list_rules_mock}): self.assertListEqual(nftables.get_rules(), expected) list_tables_mock = MagicMock(return_value=[]) - with patch.object(nftables, 'list_tables', list_tables_mock): + with patch.object(nftables, "list_tables", list_tables_mock): self.assertListEqual(nftables.get_rules(), []) # 'save' function tests: 1 def test_save(self): - ''' + """ Test if it save the current in-memory rules to disk - ''' - with patch.dict(nftables.__grains__, {'os_family': 'Debian'}): + """ + with patch.dict(nftables.__grains__, {"os_family": "Debian"}): mock = MagicMock(return_value=False) - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())): - self.assertEqual(nftables.save(), '#! nft -f\n\n') + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + with patch.object(salt.utils.files, "fopen", MagicMock(mock_open())): + self.assertEqual(nftables.save(), "#! nft -f\n\n") - with patch.object(salt.utils.files, 'fopen', - MagicMock(side_effect=IOError)): + with patch.object( + salt.utils.files, "fopen", MagicMock(side_effect=IOError) + ): self.assertRaises(CommandExecutionError, nftables.save) # 'get_rule_handle' function tests: 1 def test_get_rule_handle(self): - ''' + """ Test if it get the handle for a particular rule - ''' - self.assertEqual(nftables.get_rule_handle(), - {'result': False, - 'comment': 'Chain needs to be specified'}) + """ + self.assertEqual( + nftables.get_rule_handle(), + {"result": False, "comment": "Chain needs to be specified"}, + ) - self.assertEqual(nftables.get_rule_handle(chain='input'), - {'result': False, - 'comment': 'Rule needs to be specified'}) + self.assertEqual( + nftables.get_rule_handle(chain="input"), + {"result": False, "comment": "Rule needs to be specified"}, + ) - _ru = 'input tcp dport 22 log accept' - ret = {'result': False, - 'comment': 'Table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.get_rule_handle(chain='input', rule=_ru), - ret) + _ru = "input tcp dport 22 log accept" + ret = {"result": False, "comment": "Table filter in family ipv4 does not exist"} + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.get_rule_handle(chain="input", rule=_ru), ret) - ret = {'result': False, - 'comment': 'Chain input in table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='table ip filter') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.get_rule_handle(chain='input', rule=_ru), - ret) + ret = { + "result": False, + "comment": "Chain input in table filter in family ipv4 does not exist", + } + mock = MagicMock(return_value="table ip filter") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.get_rule_handle(chain="input", rule=_ru), ret) - ret = {'result': False, - 'comment': ('Rule input tcp dport 22 log accept chain' - ' input in table filter in family ipv4 does not exist')} - ret1 = {'result': False, - 'comment': 'Could not find rule input tcp dport 22 log accept'} - with patch.object(nftables, 'check_table', - MagicMock(return_value={'result': True, - 'comment': ''})): - with patch.object(nftables, 'check_chain', - MagicMock(return_value={'result': True, - 'comment': ''})): - _ret1 = {'result': False, - 'comment': ('Rule input tcp dport 22 log accept' - ' chain input in table filter in' - ' family ipv4 does not exist')} - _ret2 = {'result': True, 'comment': ''} - with patch.object(nftables, 'check', - MagicMock(side_effect=[_ret1, _ret2])): - self.assertEqual(nftables.get_rule_handle(chain='input', - rule=_ru), ret) + ret = { + "result": False, + "comment": ( + "Rule input tcp dport 22 log accept chain" + " input in table filter in family ipv4 does not exist" + ), + } + ret1 = { + "result": False, + "comment": "Could not find rule input tcp dport 22 log accept", + } + with patch.object( + nftables, + "check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ): + with patch.object( + nftables, + "check_chain", + MagicMock(return_value={"result": True, "comment": ""}), + ): + _ret1 = { + "result": False, + "comment": ( + "Rule input tcp dport 22 log accept" + " chain input in table filter in" + " family ipv4 does not exist" + ), + } + _ret2 = {"result": True, "comment": ""} + with patch.object( + nftables, "check", MagicMock(side_effect=[_ret1, _ret2]) + ): + self.assertEqual( + nftables.get_rule_handle(chain="input", rule=_ru), ret + ) - _ru = 'input tcp dport 22 log accept' - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.get_rule_handle(chain='input', - rule=_ru), - ret1) + _ru = "input tcp dport 22 log accept" + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual( + nftables.get_rule_handle(chain="input", rule=_ru), ret1 + ) # 'check' function tests: 1 def test_check(self): - ''' + """ Test if it check for the existence of a rule in the table and chain - ''' - self.assertEqual(nftables.check(), - {'result': False, - 'comment': 'Chain needs to be specified'}) + """ + self.assertEqual( + nftables.check(), + {"result": False, "comment": "Chain needs to be specified"}, + ) - self.assertEqual(nftables.check(chain='input'), - {'result': False, - 'comment': 'Rule needs to be specified'}) + self.assertEqual( + nftables.check(chain="input"), + {"result": False, "comment": "Rule needs to be specified"}, + ) - _ru = 'tcp dport 22 log accept' - ret = {'result': False, - 'comment': 'Table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.check(chain='input', rule=_ru), ret) + _ru = "tcp dport 22 log accept" + ret = {"result": False, "comment": "Table filter in family ipv4 does not exist"} + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.check(chain="input", rule=_ru), ret) - mock = MagicMock(return_value='table ip filter') - ret = {'result': False, - 'comment': 'Chain input in table filter in family ipv4 does not exist'} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.check(chain='input', rule=_ru), ret) + mock = MagicMock(return_value="table ip filter") + ret = { + "result": False, + "comment": "Chain input in table filter in family ipv4 does not exist", + } + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.check(chain="input", rule=_ru), ret) - mock = MagicMock(return_value='table ip filter chain input {{') - ret = {'result': False, 'comment': - 'Rule tcp dport 22 log accept in chain input in table filter in family ipv4 does not exist'} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.check(chain='input', rule=_ru), ret) + mock = MagicMock(return_value="table ip filter chain input {{") + ret = { + "result": False, + "comment": "Rule tcp dport 22 log accept in chain input in table filter in family ipv4 does not exist", + } + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.check(chain="input", rule=_ru), ret) - r_val = 'table ip filter chain input {{ input tcp dport 22 log accept #' + r_val = "table ip filter chain input {{ input tcp dport 22 log accept #" mock = MagicMock(return_value=r_val) - ret = {'result': True, - 'comment': 'Rule tcp dport 22 log accept in chain input in table filter in family ipv4 exists'} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.check(chain='input', rule=_ru), ret) + ret = { + "result": True, + "comment": "Rule tcp dport 22 log accept in chain input in table filter in family ipv4 exists", + } + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.check(chain="input", rule=_ru), ret) # 'check_chain' function tests: 1 def test_check_chain(self): - ''' + """ Test if it check for the existence of a chain in the table - ''' - self.assertEqual(nftables.check_chain(), - {'result': False, - 'comment': 'Chain needs to be specified'}) + """ + self.assertEqual( + nftables.check_chain(), + {"result": False, "comment": "Chain needs to be specified"}, + ) - mock = MagicMock(return_value='') - ret = {'comment': 'Chain input in table filter in family ipv4 does not exist', - 'result': False} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.check_chain(chain='input'), ret) + mock = MagicMock(return_value="") + ret = { + "comment": "Chain input in table filter in family ipv4 does not exist", + "result": False, + } + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.check_chain(chain="input"), ret) - mock = MagicMock(return_value='chain input {{') - ret = {'comment': 'Chain input in table filter in family ipv4 exists', - 'result': True} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.check_chain(chain='input'), ret) + mock = MagicMock(return_value="chain input {{") + ret = { + "comment": "Chain input in table filter in family ipv4 exists", + "result": True, + } + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.check_chain(chain="input"), ret) # 'check_table' function tests: 1 def test_check_table(self): - ''' + """ Test if it check for the existence of a table - ''' - self.assertEqual(nftables.check_table(), - {'result': False, - 'comment': 'Table needs to be specified'}) + """ + self.assertEqual( + nftables.check_table(), + {"result": False, "comment": "Table needs to be specified"}, + ) - mock = MagicMock(return_value='') - ret = {'comment': 'Table nat in family ipv4 does not exist', - 'result': False} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.check_table(table='nat'), ret) + mock = MagicMock(return_value="") + ret = {"comment": "Table nat in family ipv4 does not exist", "result": False} + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.check_table(table="nat"), ret) - mock = MagicMock(return_value='table ip nat') - ret = {'comment': 'Table nat in family ipv4 exists', - 'result': True} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.check_table(table='nat'), ret) + mock = MagicMock(return_value="table ip nat") + ret = {"comment": "Table nat in family ipv4 exists", "result": True} + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.check_table(table="nat"), ret) # 'new_table' function tests: 1 def test_new_table(self): - ''' + """ Test if it create new custom table. - ''' - self.assertEqual(nftables.new_table(table=None), - {'result': False, - 'comment': 'Table needs to be specified'}) + """ + self.assertEqual( + nftables.new_table(table=None), + {"result": False, "comment": "Table needs to be specified"}, + ) - mock = MagicMock(return_value='') - ret = {'comment': 'Table nat in family ipv4 created', 'result': True} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.new_table(table='nat'), ret) + mock = MagicMock(return_value="") + ret = {"comment": "Table nat in family ipv4 created", "result": True} + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.new_table(table="nat"), ret) - mock = MagicMock(return_value='table ip nat') - ret = {'comment': 'Table nat in family ipv4 exists', 'result': True} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.new_table(table='nat'), - ret) + mock = MagicMock(return_value="table ip nat") + ret = {"comment": "Table nat in family ipv4 exists", "result": True} + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.new_table(table="nat"), ret) # 'delete_table' function tests: 1 def test_delete_table(self): - ''' + """ Test if it delete custom table. - ''' - self.assertEqual(nftables.delete_table(table=None), - {'result': False, - 'comment': 'Table needs to be specified'}) + """ + self.assertEqual( + nftables.delete_table(table=None), + {"result": False, "comment": "Table needs to be specified"}, + ) - mock_ret = {'result': False, - 'comment': 'Table nat in family ipv4 does not exist'} - with patch('salt.modules.nftables.check_table', - MagicMock(return_value=mock_ret)): - ret = nftables.delete_table(table='nat') - self.assertEqual(ret, - {'result': False, - 'comment': 'Table nat in family ipv4 does not exist'}) + mock_ret = { + "result": False, + "comment": "Table nat in family ipv4 does not exist", + } + with patch( + "salt.modules.nftables.check_table", MagicMock(return_value=mock_ret) + ): + ret = nftables.delete_table(table="nat") + self.assertEqual( + ret, + {"result": False, "comment": "Table nat in family ipv4 does not exist"}, + ) - mock = MagicMock(return_value='table ip nat') - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})): - self.assertEqual(nftables.delete_table(table='nat'), - {'comment': 'Table nat in family ipv4 could not be deleted', - 'result': False}) + mock = MagicMock(return_value="table ip nat") + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ): + self.assertEqual( + nftables.delete_table(table="nat"), + { + "comment": "Table nat in family ipv4 could not be deleted", + "result": False, + }, + ) - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})): - self.assertEqual(nftables.delete_table(table='nat'), - {'comment': 'Table nat in family ipv4 deleted', - 'result': True}) + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ): + self.assertEqual( + nftables.delete_table(table="nat"), + {"comment": "Table nat in family ipv4 deleted", "result": True}, + ) # 'new_chain' function tests: 2 def test_new_chain(self): - ''' + """ Test if it create new chain to the specified table. - ''' - self.assertEqual(nftables.new_chain(), - {'result': False, - 'comment': 'Chain needs to be specified'}) + """ + self.assertEqual( + nftables.new_chain(), + {"result": False, "comment": "Chain needs to be specified"}, + ) - ret = {'result': False, - 'comment': 'Table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.new_chain(chain='input'), ret) + ret = {"result": False, "comment": "Table filter in family ipv4 does not exist"} + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.new_chain(chain="input"), ret) - ret = {'result': False, - 'comment': 'Chain input in table filter in family ipv4 already exists'} - mock = MagicMock(return_value='table ip filter chain input {{') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.new_chain(chain='input'), ret) + ret = { + "result": False, + "comment": "Chain input in table filter in family ipv4 already exists", + } + mock = MagicMock(return_value="table ip filter chain input {{") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.new_chain(chain="input"), ret) def test_new_chain_variable(self): - ''' + """ Test if it create new chain to the specified table. - ''' - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check_chain', - MagicMock(return_value={'result': False, - 'comment': ''})), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})): - self.assertEqual(nftables.new_chain(chain='input', - table_type='filter'), - {'result': False, - 'comment': 'Table_type, hook, and priority required.'}) + """ + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check_chain", + MagicMock(return_value={"result": False, "comment": ""}), + ), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ): + self.assertEqual( + nftables.new_chain(chain="input", table_type="filter"), + { + "result": False, + "comment": "Table_type, hook, and priority required.", + }, + ) - self.assertTrue(nftables.new_chain(chain='input', - table_type='filter', - hook='input', priority=0)) + self.assertTrue( + nftables.new_chain( + chain="input", table_type="filter", hook="input", priority=0 + ) + ) # 'delete_chain' function tests: 1 def test_delete_chain(self): - ''' + """ Test if it delete the chain from the specified table. - ''' - self.assertEqual(nftables.delete_chain(), - {'result': False, - 'comment': 'Chain needs to be specified'}) + """ + self.assertEqual( + nftables.delete_chain(), + {"result": False, "comment": "Chain needs to be specified"}, + ) - ret = {'result': False, - 'comment': 'Table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.delete_chain(chain='input'), ret) + ret = {"result": False, "comment": "Table filter in family ipv4 does not exist"} + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.delete_chain(chain="input"), ret) - ret = {'result': False, - 'comment': 'Chain input in table filter in family ipv4 could not be deleted'} - mock = MagicMock(return_value='table ip filter') - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})), \ - patch('salt.modules.nftables.check_chain', - MagicMock(return_value={'result': True, - 'comment': ''})): - self.assertEqual(nftables.delete_chain(chain='input'), ret) + ret = { + "result": False, + "comment": "Chain input in table filter in family ipv4 could not be deleted", + } + mock = MagicMock(return_value="table ip filter") + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ), patch( + "salt.modules.nftables.check_chain", + MagicMock(return_value={"result": True, "comment": ""}), + ): + self.assertEqual(nftables.delete_chain(chain="input"), ret) - ret = {'result': True, - 'comment': 'Chain input in table filter in family ipv4 deleted'} - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})), \ - patch('salt.modules.nftables.check_chain', - MagicMock(return_value={'result': True, - 'comment': ''})): - self.assertEqual(nftables.delete_chain(chain='input'), ret) + ret = { + "result": True, + "comment": "Chain input in table filter in family ipv4 deleted", + } + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ), patch( + "salt.modules.nftables.check_chain", + MagicMock(return_value={"result": True, "comment": ""}), + ): + self.assertEqual(nftables.delete_chain(chain="input"), ret) def test_delete_chain_variables(self): - ''' + """ Test if it delete the chain from the specified table. - ''' - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check_chain', - MagicMock(return_value={'result': True, - 'comment': ''})), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})): - _expected = {'comment': 'Chain input in table filter in family ipv4 deleted', - 'result': True} - self.assertEqual(nftables.delete_chain(chain='input'), _expected) + """ + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check_chain", + MagicMock(return_value={"result": True, "comment": ""}), + ), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ): + _expected = { + "comment": "Chain input in table filter in family ipv4 deleted", + "result": True, + } + self.assertEqual(nftables.delete_chain(chain="input"), _expected) # 'append' function tests: 2 def test_append(self): - ''' + """ Test if it append a rule to the specified table & chain. - ''' - self.assertEqual(nftables.append(), - {'result': False, - 'comment': 'Chain needs to be specified'}) + """ + self.assertEqual( + nftables.append(), + {"result": False, "comment": "Chain needs to be specified"}, + ) - self.assertEqual(nftables.append(chain='input'), - {'result': False, - 'comment': 'Rule needs to be specified'}) + self.assertEqual( + nftables.append(chain="input"), + {"result": False, "comment": "Rule needs to be specified"}, + ) - _ru = 'input tcp dport 22 log accept' - ret = {'comment': 'Table filter in family ipv4 does not exist', - 'result': False} - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.append(chain='input', rule=_ru), ret) + _ru = "input tcp dport 22 log accept" + ret = {"comment": "Table filter in family ipv4 does not exist", "result": False} + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.append(chain="input", rule=_ru), ret) - ret = {'comment': 'Chain input in table filter in family ipv4 does not exist', - 'result': False} - mock = MagicMock(return_value='table ip filter') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.append(chain='input', rule=_ru), ret) + ret = { + "comment": "Chain input in table filter in family ipv4 does not exist", + "result": False, + } + mock = MagicMock(return_value="table ip filter") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.append(chain="input", rule=_ru), ret) - r_val = 'table ip filter chain input {{ input tcp dport 22 log accept #' + r_val = "table ip filter chain input {{ input tcp dport 22 log accept #" mock = MagicMock(return_value=r_val) - _expected = {'comment': 'Rule input tcp dport 22 log accept chain input in table filter in family ipv4 already exists', - 'result': False} - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.append(chain='input', - rule=_ru), - _expected) + _expected = { + "comment": "Rule input tcp dport 22 log accept chain input in table filter in family ipv4 already exists", + "result": False, + } + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.append(chain="input", rule=_ru), _expected) def test_append_rule(self): - ''' + """ Test if it append a rule to the specified table & chain. - ''' - _ru = 'input tcp dport 22 log accept' - mock = MagicMock(side_effect=['1', '']) - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check', - MagicMock(return_value={'result': False, - 'comment': ''})), \ - patch('salt.modules.nftables.check_chain', - MagicMock(return_value={'result': True, - 'comment': ''})), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})): - _expected = {'comment': 'Failed to add rule "{0}" chain input in table filter in family ipv4.'.format(_ru), 'result': False} - self.assertEqual(nftables.append(chain='input', rule=_ru), _expected) - _expected = {'comment': 'Added rule "{0}" chain input in table filter in family ipv4.'.format(_ru), 'result': True} - self.assertEqual(nftables.append(chain='input', rule=_ru), _expected) + """ + _ru = "input tcp dport 22 log accept" + mock = MagicMock(side_effect=["1", ""]) + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check", + MagicMock(return_value={"result": False, "comment": ""}), + ), patch( + "salt.modules.nftables.check_chain", + MagicMock(return_value={"result": True, "comment": ""}), + ), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ): + _expected = { + "comment": 'Failed to add rule "{0}" chain input in table filter in family ipv4.'.format( + _ru + ), + "result": False, + } + self.assertEqual(nftables.append(chain="input", rule=_ru), _expected) + _expected = { + "comment": 'Added rule "{0}" chain input in table filter in family ipv4.'.format( + _ru + ), + "result": True, + } + self.assertEqual(nftables.append(chain="input", rule=_ru), _expected) # 'insert' function tests: 2 def test_insert(self): - ''' + """ Test if it insert a rule into the specified table & chain, at the specified position. - ''' - self.assertEqual(nftables.insert(), - {'result': False, - 'comment': 'Chain needs to be specified'}) + """ + self.assertEqual( + nftables.insert(), + {"result": False, "comment": "Chain needs to be specified"}, + ) - self.assertEqual(nftables.insert(chain='input'), - {'result': False, - 'comment': 'Rule needs to be specified'}) + self.assertEqual( + nftables.insert(chain="input"), + {"result": False, "comment": "Rule needs to be specified"}, + ) - _ru = 'input tcp dport 22 log accept' - ret = {'result': False, - 'comment': 'Table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.insert(chain='input', rule=_ru), ret) + _ru = "input tcp dport 22 log accept" + ret = {"result": False, "comment": "Table filter in family ipv4 does not exist"} + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.insert(chain="input", rule=_ru), ret) - ret = {'result': False, - 'comment': 'Chain input in table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='table ip filter') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.insert(chain='input', rule=_ru), ret) + ret = { + "result": False, + "comment": "Chain input in table filter in family ipv4 does not exist", + } + mock = MagicMock(return_value="table ip filter") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.insert(chain="input", rule=_ru), ret) - r_val = 'table ip filter chain input {{ input tcp dport 22 log accept #' + r_val = "table ip filter chain input {{ input tcp dport 22 log accept #" mock = MagicMock(return_value=r_val) - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - res = nftables.insert(chain='input', rule=_ru) + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + res = nftables.insert(chain="input", rule=_ru) import logging + log = logging.getLogger(__name__) - log.debug('=== res %s ===', res) - self.assertTrue(nftables.insert(chain='input', rule=_ru)) + log.debug("=== res %s ===", res) + self.assertTrue(nftables.insert(chain="input", rule=_ru)) def test_insert_rule(self): - ''' + """ Test if it insert a rule into the specified table & chain, at the specified position. - ''' - _ru = 'input tcp dport 22 log accept' - mock = MagicMock(side_effect=['1', '']) - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check', - MagicMock(return_value={'result': False, - 'comment': ''})), \ - patch('salt.modules.nftables.check_chain', - MagicMock(return_value={'result': True, - 'comment': ''})), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})): - _expected = {'result': False, - 'comment': 'Failed to add rule "{0}" chain input in table filter in family ipv4.'.format(_ru)} - self.assertEqual(nftables.insert(chain='input', rule=_ru), - _expected) - _expected = {'result': True, - 'comment': 'Added rule "{0}" chain input in table filter in family ipv4.'.format(_ru)} - self.assertEqual(nftables.insert(chain='input', rule=_ru), - _expected) + """ + _ru = "input tcp dport 22 log accept" + mock = MagicMock(side_effect=["1", ""]) + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check", + MagicMock(return_value={"result": False, "comment": ""}), + ), patch( + "salt.modules.nftables.check_chain", + MagicMock(return_value={"result": True, "comment": ""}), + ), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ): + _expected = { + "result": False, + "comment": 'Failed to add rule "{0}" chain input in table filter in family ipv4.'.format( + _ru + ), + } + self.assertEqual(nftables.insert(chain="input", rule=_ru), _expected) + _expected = { + "result": True, + "comment": 'Added rule "{0}" chain input in table filter in family ipv4.'.format( + _ru + ), + } + self.assertEqual(nftables.insert(chain="input", rule=_ru), _expected) # 'delete' function tests: 2 def test_delete(self): - ''' + """ Test if it delete a rule from the specified table & chain, specifying either the rule in its entirety, or the rule's position in the chain. - ''' - _ru = 'input tcp dport 22 log accept' - ret = {'result': False, - 'comment': 'Only specify a position or a rule, not both'} - self.assertEqual(nftables.delete(table='filter', chain='input', - position='3', rule=_ru), - ret) + """ + _ru = "input tcp dport 22 log accept" + ret = { + "result": False, + "comment": "Only specify a position or a rule, not both", + } + self.assertEqual( + nftables.delete(table="filter", chain="input", position="3", rule=_ru), ret + ) - ret = {'result': False, - 'comment': 'Table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.delete(table='filter', chain='input', - rule=_ru), ret) + ret = {"result": False, "comment": "Table filter in family ipv4 does not exist"} + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual( + nftables.delete(table="filter", chain="input", rule=_ru), ret + ) - ret = {'result': False, - 'comment': 'Chain input in table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='table ip filter') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.delete(table='filter', chain='input', - rule=_ru), ret) + ret = { + "result": False, + "comment": "Chain input in table filter in family ipv4 does not exist", + } + mock = MagicMock(return_value="table ip filter") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual( + nftables.delete(table="filter", chain="input", rule=_ru), ret + ) - mock = MagicMock(return_value='table ip filter chain input {{') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertTrue(nftables.delete(table='filter', chain='input', - rule=_ru)) + mock = MagicMock(return_value="table ip filter chain input {{") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertTrue(nftables.delete(table="filter", chain="input", rule=_ru)) def test_delete_rule(self): - ''' + """ Test if it delete a rule from the specified table & chain, specifying either the rule in its entirety, or the rule's position in the chain. - ''' - mock = MagicMock(side_effect=['1', '']) - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check', - MagicMock(return_value={'result': True, - 'comment': ''})), \ - patch('salt.modules.nftables.check_chain', - MagicMock(return_value={'result': True, - 'comment': ''})), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})): - _expected = {'result': False, - 'comment': 'Failed to delete rule "None" in chain input table filter in family ipv4'} - self.assertEqual(nftables.delete(table='filter', - chain='input', - position='3'), - _expected) - _expected = {'result': True, - 'comment': 'Deleted rule "None" in chain input in table filter in family ipv4.'} - self.assertEqual(nftables.delete(table='filter', - chain='input', - position='3'), - _expected) + """ + mock = MagicMock(side_effect=["1", ""]) + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check", + MagicMock(return_value={"result": True, "comment": ""}), + ), patch( + "salt.modules.nftables.check_chain", + MagicMock(return_value={"result": True, "comment": ""}), + ), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ): + _expected = { + "result": False, + "comment": 'Failed to delete rule "None" in chain input table filter in family ipv4', + } + self.assertEqual( + nftables.delete(table="filter", chain="input", position="3"), _expected + ) + _expected = { + "result": True, + "comment": 'Deleted rule "None" in chain input in table filter in family ipv4.', + } + self.assertEqual( + nftables.delete(table="filter", chain="input", position="3"), _expected + ) + # 'flush' function tests: 2 def test_flush(self): - ''' + """ Test if it flush the chain in the specified table, flush all chains in the specified table if chain is not specified. - ''' - ret = {'result': False, - 'comment': 'Table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.flush(table='filter', chain='input'), ret) + """ + ret = {"result": False, "comment": "Table filter in family ipv4 does not exist"} + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.flush(table="filter", chain="input"), ret) - ret = {'result': False, - 'comment': 'Chain input in table filter in family ipv4 does not exist'} - mock = MagicMock(return_value='table ip filter') - with patch.dict(nftables.__salt__, {'cmd.run': mock}): - self.assertEqual(nftables.flush(table='filter', chain='input'), ret) + ret = { + "result": False, + "comment": "Chain input in table filter in family ipv4 does not exist", + } + mock = MagicMock(return_value="table ip filter") + with patch.dict(nftables.__salt__, {"cmd.run": mock}): + self.assertEqual(nftables.flush(table="filter", chain="input"), ret) def test_flush_chain(self): - ''' + """ Test if it flush the chain in the specified table, flush all chains in the specified table if chain is not specified. - ''' - mock = MagicMock(side_effect=['1', '']) - with patch.dict(nftables.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.nftables.check_chain', - MagicMock(return_value={'result': True, - 'comment': ''})), \ - patch('salt.modules.nftables.check_table', - MagicMock(return_value={'result': True, - 'comment': ''})): - _expected = {'result': False, - 'comment': 'Failed to flush rules from chain input in table filter in family ipv4.'} - self.assertEqual(nftables.flush(table='filter', - chain='input'), - _expected) - _expected = {'result': True, - 'comment': 'Flushed rules from chain input in table filter in family ipv4.'} - self.assertEqual(nftables.flush(table='filter', - chain='input'), - _expected) + """ + mock = MagicMock(side_effect=["1", ""]) + with patch.dict(nftables.__salt__, {"cmd.run": mock}), patch( + "salt.modules.nftables.check_chain", + MagicMock(return_value={"result": True, "comment": ""}), + ), patch( + "salt.modules.nftables.check_table", + MagicMock(return_value={"result": True, "comment": ""}), + ): + _expected = { + "result": False, + "comment": "Failed to flush rules from chain input in table filter in family ipv4.", + } + self.assertEqual(nftables.flush(table="filter", chain="input"), _expected) + _expected = { + "result": True, + "comment": "Flushed rules from chain input in table filter in family ipv4.", + } + self.assertEqual(nftables.flush(table="filter", chain="input"), _expected) diff --git a/tests/unit/modules/test_nginx.py b/tests/unit/modules/test_nginx.py index c634d2aa49a..d6b359425b9 100644 --- a/tests/unit/modules/test_nginx.py +++ b/tests/unit/modules/test_nginx.py @@ -3,14 +3,14 @@ # Import Pytohn libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import Mock, patch - # Import Salt Module import salt.modules.nginx as nginx +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock, patch +from tests.support.unit import TestCase + MOCK_STATUS_OUTPUT = """Active connections: 7 server accepts handled requests 46756 46756 89318 @@ -19,6 +19,7 @@ Reading: 0 Writing: 7 Waiting: 0""" class MockUrllibStatus(object): """Mock of urllib2 call for Nginx status""" + def read(self): return MOCK_STATUS_OUTPUT @@ -27,27 +28,29 @@ class MockUrllibStatus(object): class NginxTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/nginx')) + patcher = patch("salt.utils.path.which", Mock(return_value="/usr/bin/nginx")) patcher.start() self.addCleanup(patcher.stop) - return {nginx: {'_urlopen': Mock(return_value=MockUrllibStatus())}} + return {nginx: {"_urlopen": Mock(return_value=MockUrllibStatus())}} def test_nginx_status(self): result = nginx.status() - nginx._urlopen.assert_called_once_with('http://127.0.0.1/status') - self.assertEqual(result, { - 'active connections': 7, - 'accepted': 46756, - 'handled': 46756, - 'requests': 89318, - 'reading': 0, - 'writing': 7, - 'waiting': 0, - }) + nginx._urlopen.assert_called_once_with("http://127.0.0.1/status") + self.assertEqual( + result, + { + "active connections": 7, + "accepted": 46756, + "handled": 46756, + "requests": 89318, + "reading": 0, + "writing": 7, + "waiting": 0, + }, + ) def test_nginx_status_with_arg(self): - other_path = 'http://localhost/path' + other_path = "http://localhost/path" result = nginx.status(other_path) nginx._urlopen.assert_called_once_with(other_path) diff --git a/tests/unit/modules/test_nova.py b/tests/unit/modules/test_nova.py index 4e3cc9e02e9..d21e173cbe6 100644 --- a/tests/unit/modules/test_nova.py +++ b/tests/unit/modules/test_nova.py @@ -1,294 +1,279 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.nova as nova +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class NovaTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.nova - ''' + """ + def setup_loader_modules(self): - patcher = patch('salt.modules.nova._auth') + patcher = patch("salt.modules.nova._auth") self.mock_auth = patcher.start() self.addCleanup(patcher.stop) - self.addCleanup(delattr, self, 'mock_auth') + self.addCleanup(delattr, self, "mock_auth") return {nova: {}} def test_boot(self): - ''' + """ Test for Boot (create) a new instance - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'boot', MagicMock(return_value='A')): - self.assertTrue(nova.boot('name')) + with patch.object(self.mock_auth, "boot", MagicMock(return_value="A")): + self.assertTrue(nova.boot("name")) def test_volume_list(self): - ''' + """ Test for List storage volumes - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'volume_list', MagicMock(return_value='A')): + with patch.object(self.mock_auth, "volume_list", MagicMock(return_value="A")): self.assertTrue(nova.volume_list()) def test_volume_show(self): - ''' + """ Test for Create a block storage volume - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'volume_show', MagicMock(return_value='A')): - self.assertTrue(nova.volume_show('name')) + with patch.object(self.mock_auth, "volume_show", MagicMock(return_value="A")): + self.assertTrue(nova.volume_show("name")) def test_volume_create(self): - ''' + """ Test for Create a block storage volume - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'volume_create', MagicMock(return_value='A')): - self.assertTrue(nova.volume_create('name')) + with patch.object(self.mock_auth, "volume_create", MagicMock(return_value="A")): + self.assertTrue(nova.volume_create("name")) def test_volume_delete(self): - ''' + """ Test for Destroy the volume - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'volume_delete', MagicMock(return_value='A')): - self.assertTrue(nova.volume_delete('name')) + with patch.object(self.mock_auth, "volume_delete", MagicMock(return_value="A")): + self.assertTrue(nova.volume_delete("name")) def test_volume_detach(self): - ''' + """ Test for Attach a block storage volume - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'volume_detach', MagicMock(return_value='A')): - self.assertTrue(nova.volume_detach('name')) + with patch.object(self.mock_auth, "volume_detach", MagicMock(return_value="A")): + self.assertTrue(nova.volume_detach("name")) def test_volume_attach(self): - ''' + """ Test for Attach a block storage volume - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'volume_attach', MagicMock(return_value='A')): - self.assertTrue(nova.volume_attach('name', 'serv_name')) + with patch.object(self.mock_auth, "volume_attach", MagicMock(return_value="A")): + self.assertTrue(nova.volume_attach("name", "serv_name")) def test_suspend(self): - ''' + """ Test for Suspend an instance - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'suspend', MagicMock(return_value='A')): - self.assertTrue(nova.suspend('instance_id')) + with patch.object(self.mock_auth, "suspend", MagicMock(return_value="A")): + self.assertTrue(nova.suspend("instance_id")) def test_resume(self): - ''' + """ Test for Resume an instance - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'resume', MagicMock(return_value='A')): - self.assertTrue(nova.resume('instance_id')) + with patch.object(self.mock_auth, "resume", MagicMock(return_value="A")): + self.assertTrue(nova.resume("instance_id")) def test_lock(self): - ''' + """ Test for Lock an instance - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'lock', MagicMock(return_value='A')): - self.assertTrue(nova.lock('instance_id')) + with patch.object(self.mock_auth, "lock", MagicMock(return_value="A")): + self.assertTrue(nova.lock("instance_id")) def test_delete(self): - ''' + """ Test for Delete an instance - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'delete', MagicMock(return_value='A')): - self.assertTrue(nova.delete('instance_id')) + with patch.object(self.mock_auth, "delete", MagicMock(return_value="A")): + self.assertTrue(nova.delete("instance_id")) def test_flavor_list(self): - ''' + """ Test for Return a list of available flavors (nova flavor-list) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'flavor_list', MagicMock(return_value='A')): + with patch.object(self.mock_auth, "flavor_list", MagicMock(return_value="A")): self.assertTrue(nova.flavor_list()) def test_flavor_create(self): - ''' + """ Test for Add a flavor to nova (nova flavor-create) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'flavor_create', MagicMock(return_value='A')): - self.assertTrue(nova.flavor_create('name')) + with patch.object(self.mock_auth, "flavor_create", MagicMock(return_value="A")): + self.assertTrue(nova.flavor_create("name")) def test_flavor_delete(self): - ''' + """ Test for Delete a flavor from nova by id (nova flavor-delete) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'flavor_delete', MagicMock(return_value='A')): - self.assertTrue(nova.flavor_delete('flavor_id')) + with patch.object(self.mock_auth, "flavor_delete", MagicMock(return_value="A")): + self.assertTrue(nova.flavor_delete("flavor_id")) def test_keypair_list(self): - ''' + """ Test for Return a list of available keypairs (nova keypair-list) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'keypair_list', MagicMock(return_value='A')): + with patch.object(self.mock_auth, "keypair_list", MagicMock(return_value="A")): self.assertTrue(nova.keypair_list()) def test_keypair_add(self): - ''' + """ Test for Add a keypair to nova (nova keypair-add) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'keypair_add', MagicMock(return_value='A')): - self.assertTrue(nova.keypair_add('name')) + with patch.object(self.mock_auth, "keypair_add", MagicMock(return_value="A")): + self.assertTrue(nova.keypair_add("name")) def test_keypair_delete(self): - ''' + """ Test for Add a keypair to nova (nova keypair-delete) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'keypair_delete', MagicMock(return_value='A')): - self.assertTrue(nova.keypair_delete('name')) + with patch.object( + self.mock_auth, "keypair_delete", MagicMock(return_value="A") + ): + self.assertTrue(nova.keypair_delete("name")) def test_image_list(self): - ''' + """ Test for Return a list of available images (nova images-list + nova image-show) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'image_list', MagicMock(return_value='A')): + with patch.object(self.mock_auth, "image_list", MagicMock(return_value="A")): self.assertTrue(nova.image_list()) def test_image_meta_set(self): - ''' + """ Test for Sets a key=value pair in the metadata for an image (nova image-meta set) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'image_meta_set', MagicMock(return_value='A')): + with patch.object( + self.mock_auth, "image_meta_set", MagicMock(return_value="A") + ): self.assertTrue(nova.image_meta_set()) def test_image_meta_delete(self): - ''' + """ Test for Delete a key=value pair from the metadata for an image (nova image-meta set) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'image_meta_delete', MagicMock(return_value='A')): + with patch.object( + self.mock_auth, "image_meta_delete", MagicMock(return_value="A") + ): self.assertTrue(nova.image_meta_delete()) def test_list_(self): - ''' + """ Test for To maintain the feel of the nova command line, this function simply calls the server_list function. - ''' - with patch.object(nova, 'server_list', return_value=['A']): - self.assertEqual(nova.list_(), ['A']) + """ + with patch.object(nova, "server_list", return_value=["A"]): + self.assertEqual(nova.list_(), ["A"]) def test_server_list(self): - ''' + """ Test for Return list of active servers - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'server_list', MagicMock(return_value='A')): + with patch.object(self.mock_auth, "server_list", MagicMock(return_value="A")): self.assertTrue(nova.server_list()) def test_show(self): - ''' + """ Test for To maintain the feel of the nova command line, this function simply calls the server_show function. - ''' - with patch.object(nova, 'server_show', return_value=['A']): - self.assertEqual(nova.show('server_id'), ['A']) + """ + with patch.object(nova, "server_show", return_value=["A"]): + self.assertEqual(nova.show("server_id"), ["A"]) def test_server_list_detailed(self): - ''' + """ Test for Return detailed list of active servers - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'server_list_detailed', MagicMock(return_value='A')): + with patch.object( + self.mock_auth, "server_list_detailed", MagicMock(return_value="A") + ): self.assertTrue(nova.server_list_detailed()) def test_server_show(self): - ''' + """ Test for Return detailed information for an active server - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'server_show', MagicMock(return_value='A')): - self.assertTrue(nova.server_show('serv_id')) + with patch.object(self.mock_auth, "server_show", MagicMock(return_value="A")): + self.assertTrue(nova.server_show("serv_id")) def test_secgroup_create(self): - ''' + """ Test for Add a secgroup to nova (nova secgroup-create) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'server_list_detailed', MagicMock(return_value='A')): - self.assertTrue(nova.secgroup_create('name', 'desc')) + with patch.object( + self.mock_auth, "server_list_detailed", MagicMock(return_value="A") + ): + self.assertTrue(nova.secgroup_create("name", "desc")) def test_secgroup_delete(self): - ''' + """ Test for Delete a secgroup to nova (nova secgroup-delete) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'secgroup_delete', MagicMock(return_value='A')): - self.assertTrue(nova.secgroup_delete('name')) + with patch.object( + self.mock_auth, "secgroup_delete", MagicMock(return_value="A") + ): + self.assertTrue(nova.secgroup_delete("name")) def test_secgroup_list(self): - ''' + """ Test for Return a list of available security groups (nova items-list) - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'secgroup_list', MagicMock(return_value='A')): + with patch.object(self.mock_auth, "secgroup_list", MagicMock(return_value="A")): self.assertTrue(nova.secgroup_list()) def test_server_by_name(self): - ''' + """ Test for Return information about a server - ''' + """ self.mock_auth.side_effect = MagicMock() - with patch.object(self.mock_auth, - 'server_by_name', MagicMock(return_value='A')): - self.assertTrue(nova.server_by_name('name')) + with patch.object( + self.mock_auth, "server_by_name", MagicMock(return_value="A") + ): + self.assertTrue(nova.server_by_name("name")) diff --git a/tests/unit/modules/test_npm.py b/tests/unit/modules/test_npm.py index a2c87d76563..c5b6f4f8cc1 100644 --- a/tests/unit/modules/test_npm.py +++ b/tests/unit/modules/test_npm.py @@ -1,33 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import textwrap -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.npm as npm # Import Salt Libs import salt.utils.json -import salt.modules.npm as npm from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class NpmTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.npm - ''' + """ + def setup_loader_modules(self): - patcher = patch('salt.modules.npm._check_valid_version', - MagicMock(return_value=True)) + patcher = patch( + "salt.modules.npm._check_valid_version", MagicMock(return_value=True) + ) patcher.start() self.addCleanup(patcher.stop) return {npm: {}} @@ -35,30 +36,32 @@ class NpmTestCase(TestCase, LoaderModuleMockMixin): # 'install' function tests: 4 def test_install(self): - ''' + """ Test if it installs an NPM package. - ''' - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, npm.install, - 'coffee-script') + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "error"}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertRaises(CommandExecutionError, npm.install, "coffee-script") # This is at least somewhat closer to the actual output format. - mock_json_out = textwrap.dedent('''\ + mock_json_out = textwrap.dedent( + """\ [ { "salt": "SALT" } - ]''') + ]""" + ) # Successful run, expected output format - mock = MagicMock(return_value={'retcode': 0, 'stderr': '', - 'stdout': mock_json_out}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertEqual(npm.install('coffee-script'), - [{u'salt': u'SALT'}]) + mock = MagicMock( + return_value={"retcode": 0, "stderr": "", "stdout": mock_json_out} + ) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertEqual(npm.install("coffee-script"), [{"salt": "SALT"}]) - mock_json_out_extra = textwrap.dedent('''\ + mock_json_out_extra = textwrap.dedent( + """\ Compilation output here [bcrypt] Success: "/tmp/node_modules/bcrypt/foo" is installed via remote" @@ -76,112 +79,124 @@ class NpmTestCase(TestCase, LoaderModuleMockMixin): }, "version" : "4.16.3" } - ]''') - extra_expected = [{u'dependencies': - {u'escape-html': { - u'dependencies': {}, - u'from': u'escape-html@~1.0.3', - u'version': u'1.0.3'} - }, - u'from': u'express@', - u'name': u'express', - u'version': u'4.16.3'}] + ]""" + ) + extra_expected = [ + { + "dependencies": { + "escape-html": { + "dependencies": {}, + "from": "escape-html@~1.0.3", + "version": "1.0.3", + } + }, + "from": "express@", + "name": "express", + "version": "4.16.3", + } + ] # Successful run, expected output format with additional leading text - mock = MagicMock(return_value={'retcode': 0, 'stderr': '', - 'stdout': mock_json_out_extra}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertEqual(npm.install('coffee-script'), extra_expected) + mock = MagicMock( + return_value={"retcode": 0, "stderr": "", "stdout": mock_json_out_extra} + ) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertEqual(npm.install("coffee-script"), extra_expected) # Successful run, unexpected output format - mock = MagicMock(return_value={'retcode': 0, 'stderr': '', - 'stdout': 'SALT'}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stderr": "", "stdout": "SALT"}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): mock_err = MagicMock(side_effect=ValueError()) # When JSON isn't successfully parsed, return should equal input - with patch.object(salt.utils.json, 'loads', mock_err): - self.assertEqual(npm.install('coffee-script'), 'SALT') + with patch.object(salt.utils.json, "loads", mock_err): + self.assertEqual(npm.install("coffee-script"), "SALT") # 'uninstall' function tests: 1 def test_uninstall(self): - ''' + """ Test if it uninstalls an NPM package. - ''' - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertFalse(npm.uninstall('coffee-script')) + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "error"}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertFalse(npm.uninstall("coffee-script")) - mock = MagicMock(return_value={'retcode': 0, 'stderr': ''}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertTrue(npm.uninstall('coffee-script')) + mock = MagicMock(return_value={"retcode": 0, "stderr": ""}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertTrue(npm.uninstall("coffee-script")) # 'list_' function tests: 1 def test_list(self): - ''' + """ Test if it list installed NPM packages. - ''' - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, npm.list_, 'coffee-script') + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "error"}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertRaises(CommandExecutionError, npm.list_, "coffee-script") - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error', - 'stdout': '{"salt": ["SALT"]}'}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - mock_err = MagicMock(return_value={'dependencies': 'SALT'}) - with patch.object(salt.utils.json, 'loads', mock_err): - self.assertEqual(npm.list_('coffee-script'), 'SALT') + mock = MagicMock( + return_value={ + "retcode": 0, + "stderr": "error", + "stdout": '{"salt": ["SALT"]}', + } + ) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + mock_err = MagicMock(return_value={"dependencies": "SALT"}) + with patch.object(salt.utils.json, "loads", mock_err): + self.assertEqual(npm.list_("coffee-script"), "SALT") # 'cache_clean' function tests: 1 def test_cache_clean(self): - ''' + """ Test if it cleans the cached NPM packages. - ''' - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "error"}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): self.assertFalse(npm.cache_clean()) - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): self.assertTrue(npm.cache_clean()) - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertTrue(npm.cache_clean('coffee-script')) + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertTrue(npm.cache_clean("coffee-script")) # 'cache_list' function tests: 1 def test_cache_list(self): - ''' + """ Test if it lists the NPM cache. - ''' - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "error"}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): self.assertRaises(CommandExecutionError, npm.cache_list) - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error', - 'stdout': ['~/.npm']}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertEqual(npm.cache_list(), ['~/.npm']) + mock = MagicMock( + return_value={"retcode": 0, "stderr": "error", "stdout": ["~/.npm"]} + ) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertEqual(npm.cache_list(), ["~/.npm"]) - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error', - 'stdout': ''}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertEqual(npm.cache_list('coffee-script'), '') + mock = MagicMock(return_value={"retcode": 0, "stderr": "error", "stdout": ""}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertEqual(npm.cache_list("coffee-script"), "") # 'cache_path' function tests: 1 def test_cache_path(self): - ''' + """ Test if it prints the NPM cache path. - ''' - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error'}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertEqual(npm.cache_path(), 'error') + """ + mock = MagicMock(return_value={"retcode": 1, "stderr": "error"}) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertEqual(npm.cache_path(), "error") - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error', - 'stdout': '/User/salt/.npm'}) - with patch.dict(npm.__salt__, {'cmd.run_all': mock}): - self.assertEqual(npm.cache_path(), '/User/salt/.npm') + mock = MagicMock( + return_value={"retcode": 0, "stderr": "error", "stdout": "/User/salt/.npm"} + ) + with patch.dict(npm.__salt__, {"cmd.run_all": mock}): + self.assertEqual(npm.cache_path(), "/User/salt/.npm") diff --git a/tests/unit/modules/test_openbsd_sysctl.py b/tests/unit/modules/test_openbsd_sysctl.py index 2a92e067952..1c7f338f486 100644 --- a/tests/unit/modules/test_openbsd_sysctl.py +++ b/tests/unit/modules/test_openbsd_sysctl.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Jasper Lievisse Adriaanse <j@jasper.la>` -''' +""" # Import Python libs from __future__ import absolute_import @@ -12,60 +12,69 @@ from salt.exceptions import CommandExecutionError # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class OpenBSDSysctlTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.openbsd_sysctl module - ''' + """ + def setup_loader_modules(self): return {openbsd_sysctl: {}} def test_get(self): - ''' + """ Tests the return of get function - ''' - mock_cmd = MagicMock(return_value='OpenBSD') - with patch.dict(openbsd_sysctl.__salt__, {'cmd.run': mock_cmd}): - self.assertEqual(openbsd_sysctl.get('kern.ostype'), 'OpenBSD') + """ + mock_cmd = MagicMock(return_value="OpenBSD") + with patch.dict(openbsd_sysctl.__salt__, {"cmd.run": mock_cmd}): + self.assertEqual(openbsd_sysctl.get("kern.ostype"), "OpenBSD") def test_assign_cmd_failed(self): - ''' + """ Tests if the assignment was successful or not - ''' - cmd = {'pid': 1234, 'retcode': 1, 'stderr': '', - 'stdout': 'kern.securelevel: 1 -> 9000'} + """ + cmd = { + "pid": 1234, + "retcode": 1, + "stderr": "", + "stdout": "kern.securelevel: 1 -> 9000", + } mock_cmd = MagicMock(return_value=cmd) - with patch.dict(openbsd_sysctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertRaises(CommandExecutionError, - openbsd_sysctl.assign, - 'kern.securelevel', 9000) + with patch.dict(openbsd_sysctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertRaises( + CommandExecutionError, openbsd_sysctl.assign, "kern.securelevel", 9000 + ) def test_assign_cmd_eperm(self): - ''' + """ Tests if the assignment was not permitted. - ''' - cmd = {'pid': 1234, 'retcode': 0, 'stdout': '', - 'stderr': 'sysctl: ddb.console: Operation not permitted'} + """ + cmd = { + "pid": 1234, + "retcode": 0, + "stdout": "", + "stderr": "sysctl: ddb.console: Operation not permitted", + } mock_cmd = MagicMock(return_value=cmd) - with patch.dict(openbsd_sysctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertRaises(CommandExecutionError, - openbsd_sysctl.assign, - 'ddb.console', 1) + with patch.dict(openbsd_sysctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertRaises( + CommandExecutionError, openbsd_sysctl.assign, "ddb.console", 1 + ) def test_assign(self): - ''' + """ Tests the return of successful assign function - ''' - cmd = {'pid': 1234, 'retcode': 0, 'stderr': '', - 'stdout': 'net.inet.ip.forwarding: 0 -> 1'} - ret = {'net.inet.ip.forwarding': '1'} + """ + cmd = { + "pid": 1234, + "retcode": 0, + "stderr": "", + "stdout": "net.inet.ip.forwarding: 0 -> 1", + } + ret = {"net.inet.ip.forwarding": "1"} mock_cmd = MagicMock(return_value=cmd) - with patch.dict(openbsd_sysctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertEqual(openbsd_sysctl.assign( - 'net.inet.ip.forwarding', 1), ret) + with patch.dict(openbsd_sysctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertEqual(openbsd_sysctl.assign("net.inet.ip.forwarding", 1), ret) diff --git a/tests/unit/modules/test_openbsdpkg.py b/tests/unit/modules/test_openbsdpkg.py index f5666745696..72d53a6b738 100644 --- a/tests/unit/modules/test_openbsdpkg.py +++ b/tests/unit/modules/test_openbsdpkg.py @@ -1,23 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Eric Radman <ericshane@eradman.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - call, -) - # Import Salt Libs import salt.modules.openbsdpkg as openbsdpkg +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase + class ListPackages(object): def __init__(self): @@ -25,8 +21,8 @@ class ListPackages(object): def __call__(self): pkg_lists = [ - {'vim': '7.4.1467p1-gtk2'}, - {'png': '1.6.23', 'vim': '7.4.1467p1-gtk2', 'ruby': '2.3.1p1'} + {"vim": "7.4.1467p1-gtk2"}, + {"png": "1.6.23", "vim": "7.4.1467p1-gtk2", "ruby": "2.3.1p1"}, ] pkgs = pkg_lists[self._iteration] self._iteration += 1 @@ -34,114 +30,120 @@ class ListPackages(object): class OpenbsdpkgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.openbsdpkg - ''' + """ + def setup_loader_modules(self): return {openbsdpkg: {}} def test_list_pkgs(self): - ''' + """ Test for listing installed packages. - ''' + """ + def _add_data(data, key, value): data[key] = value pkg_info_out = [ - 'png-1.6.23', - 'vim-7.4.1467p1-gtk2', # vim--gtk2 - 'ruby-2.3.1p1' # ruby%2.3 + "png-1.6.23", + "vim-7.4.1467p1-gtk2", # vim--gtk2 + "ruby-2.3.1p1", # ruby%2.3 ] - run_stdout_mock = MagicMock(return_value='\n'.join(pkg_info_out)) + run_stdout_mock = MagicMock(return_value="\n".join(pkg_info_out)) patches = { - 'cmd.run_stdout': run_stdout_mock, - 'pkg_resource.add_pkg': _add_data, - 'pkg_resource.sort_pkglist': MagicMock(), - 'pkg_resource.stringify': MagicMock(), + "cmd.run_stdout": run_stdout_mock, + "pkg_resource.add_pkg": _add_data, + "pkg_resource.sort_pkglist": MagicMock(), + "pkg_resource.stringify": MagicMock(), } with patch.dict(openbsdpkg.__salt__, patches): pkgs = openbsdpkg.list_pkgs() - self.assertDictEqual(pkgs, { - 'png': '1.6.23', - 'vim--gtk2': '7.4.1467p1', - 'ruby': '2.3.1p1'}) - run_stdout_mock.assert_called_once_with('pkg_info -q -a', - output_loglevel='trace') + self.assertDictEqual( + pkgs, {"png": "1.6.23", "vim--gtk2": "7.4.1467p1", "ruby": "2.3.1p1"} + ) + run_stdout_mock.assert_called_once_with( + "pkg_info -q -a", output_loglevel="trace" + ) def test_install_pkgs(self): - ''' + """ Test package install behavior for the following conditions: - only base package name is given ('png') - a flavor is specified ('vim--gtk2') - a branch is specified ('ruby%2.3') - ''' + """ parsed_targets = ( - {'vim--gtk2': None, 'png': None, 'ruby%2.3': None}, - "repository" + {"vim--gtk2": None, "png": None, "ruby%2.3": None}, + "repository", ) cmd_out = { - 'retcode': 0, - 'stdout': 'quirks-2.241 signed on 2016-07-26T16:56:10Z', - 'stderr': '' + "retcode": 0, + "stdout": "quirks-2.241 signed on 2016-07-26T16:56:10Z", + "stderr": "", } run_all_mock = MagicMock(return_value=cmd_out) patches = { - 'cmd.run_all': run_all_mock, - 'pkg_resource.parse_targets': MagicMock(return_value=parsed_targets), - 'pkg_resource.stringify': MagicMock(), - 'pkg_resource.sort_pkglist': MagicMock(), + "cmd.run_all": run_all_mock, + "pkg_resource.parse_targets": MagicMock(return_value=parsed_targets), + "pkg_resource.stringify": MagicMock(), + "pkg_resource.sort_pkglist": MagicMock(), } with patch.dict(openbsdpkg.__salt__, patches): - with patch('salt.modules.openbsdpkg.list_pkgs', ListPackages()): + with patch("salt.modules.openbsdpkg.list_pkgs", ListPackages()): added = openbsdpkg.install() expected = { - 'png': {'new': '1.6.23', 'old': ''}, - 'ruby': {'new': '2.3.1p1', 'old': ''} + "png": {"new": "1.6.23", "old": ""}, + "ruby": {"new": "2.3.1p1", "old": ""}, } self.assertDictEqual(added, expected) expected_calls = [ - call('pkg_add -x -I png--%', output_loglevel='trace', python_shell=False), - call('pkg_add -x -I ruby--%2.3', output_loglevel='trace', python_shell=False), - call('pkg_add -x -I vim--gtk2%', output_loglevel='trace', python_shell=False), + call("pkg_add -x -I png--%", output_loglevel="trace", python_shell=False), + call( + "pkg_add -x -I ruby--%2.3", output_loglevel="trace", python_shell=False + ), + call( + "pkg_add -x -I vim--gtk2%", output_loglevel="trace", python_shell=False + ), ] run_all_mock.assert_has_calls(expected_calls, any_order=True) self.assertEqual(run_all_mock.call_count, 3) def test_upgrade_available(self): - ''' + """ Test upgrade_available when an update is available. - ''' - ret = MagicMock(return_value='5.4.2p0') - with patch('salt.modules.openbsdpkg.latest_version', ret): - self.assertTrue(openbsdpkg.upgrade_available('zsh')) + """ + ret = MagicMock(return_value="5.4.2p0") + with patch("salt.modules.openbsdpkg.latest_version", ret): + self.assertTrue(openbsdpkg.upgrade_available("zsh")) def test_upgrade_not_available(self): - ''' + """ Test upgrade_available when an update is not available. - ''' - ret = MagicMock(return_value='') - with patch('salt.modules.openbsdpkg.latest_version', ret): - self.assertFalse(openbsdpkg.upgrade_available('zsh')) + """ + ret = MagicMock(return_value="") + with patch("salt.modules.openbsdpkg.latest_version", ret): + self.assertFalse(openbsdpkg.upgrade_available("zsh")) def test_upgrade(self): - ''' + """ Test upgrading packages. - ''' + """ ret = {} pkg_add_u_stdout = [ - 'quirks-2.402 signed on 2018-01-02T16:30:59Z', - 'Read shared items: ok' + "quirks-2.402 signed on 2018-01-02T16:30:59Z", + "Read shared items: ok", ] - ret['stdout'] = '\n'.join(pkg_add_u_stdout) - ret['retcode'] = 0 + ret["stdout"] = "\n".join(pkg_add_u_stdout) + ret["retcode"] = 0 run_all_mock = MagicMock(return_value=ret) - with patch.dict(openbsdpkg.__salt__, {'cmd.run_all': run_all_mock}): - with patch('salt.modules.openbsdpkg.list_pkgs', ListPackages()): + with patch.dict(openbsdpkg.__salt__, {"cmd.run_all": run_all_mock}): + with patch("salt.modules.openbsdpkg.list_pkgs", ListPackages()): upgraded = openbsdpkg.upgrade() expected = { - 'png': {'new': '1.6.23', 'old': ''}, - 'ruby': {'new': '2.3.1p1', 'old': ''} + "png": {"new": "1.6.23", "old": ""}, + "ruby": {"new": "2.3.1p1", "old": ""}, } self.assertDictEqual(upgraded, expected) diff --git a/tests/unit/modules/test_openscap.py b/tests/unit/modules/test_openscap.py index 516b639ed2f..04cf00a1d38 100644 --- a/tests/unit/modules/test_openscap.py +++ b/tests/unit/modules/test_openscap.py @@ -2,37 +2,35 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + from subprocess import PIPE # Import salt libs import salt.modules.openscap as openscap -# Import salt test libs -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch, -) - # Import 3rd-party libs from salt.ext import six +from tests.support.mock import MagicMock, Mock, patch + +# Import salt test libs +from tests.support.unit import TestCase class OpenscapTestCase(TestCase): - random_temp_dir = '/tmp/unique-name' - policy_file = '/usr/share/openscap/policy-file-xccdf.xml' + random_temp_dir = "/tmp/unique-name" + policy_file = "/usr/share/openscap/policy-file-xccdf.xml" def setUp(self): import salt.modules.openscap + salt.modules.openscap.__salt__ = MagicMock() patchers = [ - patch('salt.modules.openscap.__salt__', MagicMock()), - patch('salt.modules.openscap.shutil.rmtree', Mock()), + patch("salt.modules.openscap.__salt__", MagicMock()), + patch("salt.modules.openscap.shutil.rmtree", Mock()), patch( - 'salt.modules.openscap.tempfile.mkdtemp', - Mock(return_value=self.random_temp_dir) + "salt.modules.openscap.tempfile.mkdtemp", + Mock(return_value=self.random_temp_dir), ), ] for patcher in patchers: @@ -43,150 +41,179 @@ class OpenscapTestCase(TestCase): self.addCleanup(patcher.stop) def test_openscap_xccdf_eval_success(self): - with patch('salt.modules.openscap.Popen', - MagicMock( - return_value=Mock( - **{'returncode': 0, 'communicate.return_value': ('', '')} - ))): + with patch( + "salt.modules.openscap.Popen", + MagicMock( + return_value=Mock( + **{"returncode": 0, "communicate.return_value": ("", "")} + ) + ), + ): response = openscap.xccdf( - 'eval --profile Default {0}'.format(self.policy_file)) + "eval --profile Default {0}".format(self.policy_file) + ) self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1) expected_cmd = [ - 'oscap', - 'xccdf', - 'eval', - '--oval-results', - '--results', 'results.xml', - '--report', 'report.html', - '--profile', 'Default', - self.policy_file + "oscap", + "xccdf", + "eval", + "--oval-results", + "--results", + "results.xml", + "--report", + "report.html", + "--profile", + "Default", + self.policy_file, ] openscap.Popen.assert_called_once_with( expected_cmd, cwd=openscap.tempfile.mkdtemp.return_value, stderr=PIPE, - stdout=PIPE) - openscap.__salt__['cp.push_dir'].assert_called_once_with(self.random_temp_dir) + stdout=PIPE, + ) + openscap.__salt__["cp.push_dir"].assert_called_once_with( + self.random_temp_dir + ) self.assertEqual(openscap.shutil.rmtree.call_count, 1) self.assertEqual( response, { - 'upload_dir': self.random_temp_dir, - 'error': '', - 'success': True, - 'returncode': 0 - } + "upload_dir": self.random_temp_dir, + "error": "", + "success": True, + "returncode": 0, + }, ) def test_openscap_xccdf_eval_success_with_failing_rules(self): - with patch('salt.modules.openscap.Popen', - MagicMock( - return_value=Mock( - **{'returncode': 2, 'communicate.return_value': ('', 'some error')} - ))): + with patch( + "salt.modules.openscap.Popen", + MagicMock( + return_value=Mock( + **{"returncode": 2, "communicate.return_value": ("", "some error")} + ) + ), + ): response = openscap.xccdf( - 'eval --profile Default {0}'.format(self.policy_file)) + "eval --profile Default {0}".format(self.policy_file) + ) self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1) expected_cmd = [ - 'oscap', - 'xccdf', - 'eval', - '--oval-results', - '--results', 'results.xml', - '--report', 'report.html', - '--profile', 'Default', - self.policy_file + "oscap", + "xccdf", + "eval", + "--oval-results", + "--results", + "results.xml", + "--report", + "report.html", + "--profile", + "Default", + self.policy_file, ] openscap.Popen.assert_called_once_with( expected_cmd, cwd=openscap.tempfile.mkdtemp.return_value, stderr=PIPE, - stdout=PIPE) - openscap.__salt__['cp.push_dir'].assert_called_once_with(self.random_temp_dir) + stdout=PIPE, + ) + openscap.__salt__["cp.push_dir"].assert_called_once_with( + self.random_temp_dir + ) self.assertEqual(openscap.shutil.rmtree.call_count, 1) self.assertEqual( response, { - 'upload_dir': self.random_temp_dir, - 'error': 'some error', - 'success': True, - 'returncode': 2 - } + "upload_dir": self.random_temp_dir, + "error": "some error", + "success": True, + "returncode": 2, + }, ) def test_openscap_xccdf_eval_fail_no_profile(self): - response = openscap.xccdf('eval --param Default /unknown/param') + response = openscap.xccdf("eval --param Default /unknown/param") if six.PY2: - error = 'argument --profile is required' + error = "argument --profile is required" else: - error = 'the following arguments are required: --profile' + error = "the following arguments are required: --profile" self.assertEqual( response, - { - 'error': error, - 'upload_dir': None, - 'success': False, - 'returncode': None - } + {"error": error, "upload_dir": None, "success": False, "returncode": None}, ) def test_openscap_xccdf_eval_success_ignore_unknown_params(self): - with patch('salt.modules.openscap.Popen', - MagicMock( - return_value=Mock( - **{'returncode': 2, 'communicate.return_value': ('', 'some error')} - ))): + with patch( + "salt.modules.openscap.Popen", + MagicMock( + return_value=Mock( + **{"returncode": 2, "communicate.return_value": ("", "some error")} + ) + ), + ): response = openscap.xccdf( - 'eval --profile Default --param Default /policy/file') + "eval --profile Default --param Default /policy/file" + ) self.assertEqual( response, { - 'upload_dir': self.random_temp_dir, - 'error': 'some error', - 'success': True, - 'returncode': 2 - } + "upload_dir": self.random_temp_dir, + "error": "some error", + "success": True, + "returncode": 2, + }, ) expected_cmd = [ - 'oscap', - 'xccdf', - 'eval', - '--oval-results', - '--results', 'results.xml', - '--report', 'report.html', - '--profile', 'Default', - '/policy/file' + "oscap", + "xccdf", + "eval", + "--oval-results", + "--results", + "results.xml", + "--report", + "report.html", + "--profile", + "Default", + "/policy/file", ] openscap.Popen.assert_called_once_with( expected_cmd, cwd=openscap.tempfile.mkdtemp.return_value, stderr=PIPE, - stdout=PIPE) + stdout=PIPE, + ) def test_openscap_xccdf_eval_evaluation_error(self): - with patch('salt.modules.openscap.Popen', - MagicMock( - return_value=Mock(**{ - 'returncode': 1, - 'communicate.return_value': ('', 'evaluation error') - }))): + with patch( + "salt.modules.openscap.Popen", + MagicMock( + return_value=Mock( + **{ + "returncode": 1, + "communicate.return_value": ("", "evaluation error"), + } + ) + ), + ): response = openscap.xccdf( - 'eval --profile Default {0}'.format(self.policy_file)) + "eval --profile Default {0}".format(self.policy_file) + ) self.assertEqual( response, { - 'upload_dir': None, - 'error': 'evaluation error', - 'success': False, - 'returncode': 1 - } + "upload_dir": None, + "error": "evaluation error", + "success": False, + "returncode": 1, + }, ) def test_openscap_xccdf_eval_fail_not_implemented_action(self): - response = openscap.xccdf('info {0}'.format(self.policy_file)) + response = openscap.xccdf("info {0}".format(self.policy_file)) if six.PY2: mock_err = "argument action: invalid choice: 'info' (choose from u'eval')" else: @@ -195,9 +222,9 @@ class OpenscapTestCase(TestCase): self.assertEqual( response, { - 'upload_dir': None, - 'error': mock_err, - 'success': False, - 'returncode': None - } + "upload_dir": None, + "error": mock_err, + "success": False, + "returncode": None, + }, ) diff --git a/tests/unit/modules/test_openstack_config.py b/tests/unit/modules/test_openstack_config.py index 6f40412233a..d0d1cb5a408 100644 --- a/tests/unit/modules/test_openstack_config.py +++ b/tests/unit/modules/test_openstack_config.py @@ -1,30 +1,28 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.openstack_config as openstack_config from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class OpenstackConfigTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.openstack_config - ''' + """ + def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', MagicMock(return_value=True)) + patcher = patch("salt.utils.path.which", MagicMock(return_value=True)) patcher.start() self.addCleanup(patcher.stop) return {openstack_config: {}} @@ -32,56 +30,83 @@ class OpenstackConfigTestCase(TestCase, LoaderModuleMockMixin): # 'set_' function tests: 1 def test_set(self): - ''' + """ Test if it set a value in an OpenStack configuration file. - ''' - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error', - 'stdout': 'salt'}) - with patch.dict(openstack_config.__salt__, {'cmd.run_all': mock}): - self.assertEqual(openstack_config.set_('/etc/keystone/keys.conf', - 'sql', 'connection', 'foo'), - 'salt') + """ + mock = MagicMock( + return_value={"retcode": 0, "stderr": "error", "stdout": "salt"} + ) + with patch.dict(openstack_config.__salt__, {"cmd.run_all": mock}): + self.assertEqual( + openstack_config.set_( + "/etc/keystone/keys.conf", "sql", "connection", "foo" + ), + "salt", + ) - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error', - 'stdout': 'salt'}) - with patch.dict(openstack_config.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, openstack_config.set_, - '/etc/keystone/keystone.conf', 'sql', - 'connection', 'foo') + mock = MagicMock( + return_value={"retcode": 1, "stderr": "error", "stdout": "salt"} + ) + with patch.dict(openstack_config.__salt__, {"cmd.run_all": mock}): + self.assertRaises( + CommandExecutionError, + openstack_config.set_, + "/etc/keystone/keystone.conf", + "sql", + "connection", + "foo", + ) # 'get' function tests: 1 def test_get(self): - ''' + """ Test if it get a value from an OpenStack configuration file. - ''' - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error', - 'stdout': 'salt'}) - with patch.dict(openstack_config.__salt__, {'cmd.run_all': mock}): - self.assertEqual(openstack_config.get('/etc/keystone/keys.conf', - 'sql', 'connection'), 'salt') + """ + mock = MagicMock( + return_value={"retcode": 0, "stderr": "error", "stdout": "salt"} + ) + with patch.dict(openstack_config.__salt__, {"cmd.run_all": mock}): + self.assertEqual( + openstack_config.get("/etc/keystone/keys.conf", "sql", "connection"), + "salt", + ) - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error', - 'stdout': 'salt'}) - with patch.dict(openstack_config.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, openstack_config.get, - '/etc/key/keystone.conf', 'sql', 'connection') + mock = MagicMock( + return_value={"retcode": 1, "stderr": "error", "stdout": "salt"} + ) + with patch.dict(openstack_config.__salt__, {"cmd.run_all": mock}): + self.assertRaises( + CommandExecutionError, + openstack_config.get, + "/etc/key/keystone.conf", + "sql", + "connection", + ) # 'delete' function tests: 1 def test_delete(self): - ''' + """ Test if it delete a value from an OpenStack configuration file. - ''' - mock = MagicMock(return_value={'retcode': 0, 'stderr': 'error', - 'stdout': 'salt'}) - with patch.dict(openstack_config.__salt__, {'cmd.run_all': mock}): - self.assertEqual(openstack_config.delete('/etc/keystone/keys.conf', - 'sql', 'connection'), - 'salt') + """ + mock = MagicMock( + return_value={"retcode": 0, "stderr": "error", "stdout": "salt"} + ) + with patch.dict(openstack_config.__salt__, {"cmd.run_all": mock}): + self.assertEqual( + openstack_config.delete("/etc/keystone/keys.conf", "sql", "connection"), + "salt", + ) - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'error', - 'stdout': 'salt'}) - with patch.dict(openstack_config.__salt__, {'cmd.run_all': mock}): - self.assertRaises(CommandExecutionError, openstack_config.delete, - '/etc/key/keystone.conf', 'sql', 'connection') + mock = MagicMock( + return_value={"retcode": 1, "stderr": "error", "stdout": "salt"} + ) + with patch.dict(openstack_config.__salt__, {"cmd.run_all": mock}): + self.assertRaises( + CommandExecutionError, + openstack_config.delete, + "/etc/key/keystone.conf", + "sql", + "connection", + ) diff --git a/tests/unit/modules/test_opkg.py b/tests/unit/modules/test_opkg.py index 55371a45c42..b295abfe061 100644 --- a/tests/unit/modules/test_opkg.py +++ b/tests/unit/modules/test_opkg.py @@ -1,221 +1,226 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for Package Management module 'module.opkg' :platform: Linux -''' +""" # pylint: disable=import-error,3rd-party-module-not-gated # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import collections import copy -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.opkg as opkg # Import Salt Libs from salt.ext import six -import salt.modules.opkg as opkg + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + # pylint: disable=import-error,3rd-party-module-not-gated OPKG_VIM_INFO = { - 'vim': { - 'Package': 'vim', - 'Version': '7.4.769-r0.31', - 'Status': 'install ok installed' + "vim": { + "Package": "vim", + "Version": "7.4.769-r0.31", + "Status": "install ok installed", } } OPKG_VIM_FILES = { - 'errors': [], - 'packages': { - 'vim': [ - '/usr/bin/view', - '/usr/bin/vim.vim', - '/usr/bin/xxd', - '/usr/bin/vimdiff', - '/usr/bin/rview', - '/usr/bin/rvim', - '/usr/bin/ex' + "errors": [], + "packages": { + "vim": [ + "/usr/bin/view", + "/usr/bin/vim.vim", + "/usr/bin/xxd", + "/usr/bin/vimdiff", + "/usr/bin/rview", + "/usr/bin/rvim", + "/usr/bin/ex", ] - } + }, } -INSTALLED = { - 'vim': { - 'new': '7.4', - 'old': six.text_type() - } -} +INSTALLED = {"vim": {"new": "7.4", "old": six.text_type()}} -REMOVED = { - 'vim': { - 'new': six.text_type(), - 'old': '7.4' - } -} -PACKAGES = { - 'vim': '7.4' -} +REMOVED = {"vim": {"new": six.text_type(), "old": "7.4"}} +PACKAGES = {"vim": "7.4"} class OpkgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.opkg - ''' + """ + def setup_loader_modules(self): # pylint: disable=no-self-use - ''' + """ Tested modules - ''' + """ return {opkg: {}} def test_version(self): - ''' + """ Test - Returns a string representing the package version or an empty string if not installed. - ''' - version = OPKG_VIM_INFO['vim']['Version'] + """ + version = OPKG_VIM_INFO["vim"]["Version"] mock = MagicMock(return_value=version) - with patch.dict(opkg.__salt__, {'pkg_resource.version': mock}): - self.assertEqual(opkg.version(*['vim']), version) + with patch.dict(opkg.__salt__, {"pkg_resource.version": mock}): + self.assertEqual(opkg.version(*["vim"]), version) def test_upgrade_available(self): - ''' + """ Test - Check whether or not an upgrade is available for a given package. - ''' - with patch('salt.modules.opkg.latest_version', - MagicMock(return_value='')): - self.assertFalse(opkg.upgrade_available('vim')) + """ + with patch("salt.modules.opkg.latest_version", MagicMock(return_value="")): + self.assertFalse(opkg.upgrade_available("vim")) def test_file_dict(self): - ''' + """ Test - List the files that belong to a package, grouped by package. - ''' - std_out = '\n'.join(OPKG_VIM_FILES['packages']['vim']) - ret_value = {'stdout': std_out} + """ + std_out = "\n".join(OPKG_VIM_FILES["packages"]["vim"]) + ret_value = {"stdout": std_out} mock = MagicMock(return_value=ret_value) - with patch.dict(opkg.__salt__, {'cmd.run_all': mock}): - self.assertEqual(opkg.file_dict('vim'), OPKG_VIM_FILES) + with patch.dict(opkg.__salt__, {"cmd.run_all": mock}): + self.assertEqual(opkg.file_dict("vim"), OPKG_VIM_FILES) def test_file_list(self): - ''' + """ Test - List the files that belong to a package. - ''' - std_out = '\n'.join(OPKG_VIM_FILES['packages']['vim']) - ret_value = {'stdout': std_out} + """ + std_out = "\n".join(OPKG_VIM_FILES["packages"]["vim"]) + ret_value = {"stdout": std_out} mock = MagicMock(return_value=ret_value) files = { - 'errors': OPKG_VIM_FILES['errors'], - 'files': OPKG_VIM_FILES['packages']['vim'], + "errors": OPKG_VIM_FILES["errors"], + "files": OPKG_VIM_FILES["packages"]["vim"], } - with patch.dict(opkg.__salt__, {'cmd.run_all': mock}): - self.assertEqual(opkg.file_list('vim'), files) + with patch.dict(opkg.__salt__, {"cmd.run_all": mock}): + self.assertEqual(opkg.file_list("vim"), files) def test_owner(self): - ''' + """ Test - Return the name of the package that owns the file. - ''' - paths = ['/usr/bin/vimdiff'] - mock = MagicMock(return_value='vim - version - info') - with patch.dict(opkg.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(opkg.owner(*paths), 'vim') + """ + paths = ["/usr/bin/vimdiff"] + mock = MagicMock(return_value="vim - version - info") + with patch.dict(opkg.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual(opkg.owner(*paths), "vim") def test_install(self): - ''' + """ Test - Install packages. - ''' - with patch('salt.modules.opkg.list_pkgs', MagicMock(side_effect=[{}, PACKAGES])): - ret_value = {'retcode': 0} + """ + with patch( + "salt.modules.opkg.list_pkgs", MagicMock(side_effect=[{}, PACKAGES]) + ): + ret_value = {"retcode": 0} mock = MagicMock(return_value=ret_value) patch_kwargs = { - '__salt__': { - 'cmd.run_all': mock, - 'pkg_resource.parse_targets': MagicMock(return_value=({'vim': '7.4'}, 'repository')), - 'restartcheck.restartcheck': MagicMock(return_value='No packages seem to need to be restarted') + "__salt__": { + "cmd.run_all": mock, + "pkg_resource.parse_targets": MagicMock( + return_value=({"vim": "7.4"}, "repository") + ), + "restartcheck.restartcheck": MagicMock( + return_value="No packages seem to need to be restarted" + ), } } with patch.multiple(opkg, **patch_kwargs): - self.assertEqual(opkg.install('vim:7.4'), INSTALLED) + self.assertEqual(opkg.install("vim:7.4"), INSTALLED) def test_install_noaction(self): - ''' + """ Test - Install packages. - ''' - with patch('salt.modules.opkg.list_pkgs', MagicMock(return_value=({}))): - ret_value = {'retcode': 0} + """ + with patch("salt.modules.opkg.list_pkgs", MagicMock(return_value=({}))): + ret_value = {"retcode": 0} mock = MagicMock(return_value=ret_value) patch_kwargs = { - '__salt__': { - 'cmd.run_all': mock, - 'pkg_resource.parse_targets': MagicMock(return_value=({'vim': '7.4'}, 'repository')), - 'restartcheck.restartcheck': MagicMock(return_value='No packages seem to need to be restarted') + "__salt__": { + "cmd.run_all": mock, + "pkg_resource.parse_targets": MagicMock( + return_value=({"vim": "7.4"}, "repository") + ), + "restartcheck.restartcheck": MagicMock( + return_value="No packages seem to need to be restarted" + ), } } with patch.multiple(opkg, **patch_kwargs): - self.assertEqual(opkg.install('vim:7.4', test=True), {}) + self.assertEqual(opkg.install("vim:7.4", test=True), {}) def test_remove(self): - ''' + """ Test - Remove packages. - ''' - with patch('salt.modules.opkg.list_pkgs', MagicMock(side_effect=[PACKAGES, {}])): - ret_value = {'retcode': 0} + """ + with patch( + "salt.modules.opkg.list_pkgs", MagicMock(side_effect=[PACKAGES, {}]) + ): + ret_value = {"retcode": 0} mock = MagicMock(return_value=ret_value) patch_kwargs = { - '__salt__': { - 'cmd.run_all': mock, - 'pkg_resource.parse_targets': MagicMock(return_value=({'vim': '7.4'}, 'repository')), - 'restartcheck.restartcheck': MagicMock(return_value='No packages seem to need to be restarted') + "__salt__": { + "cmd.run_all": mock, + "pkg_resource.parse_targets": MagicMock( + return_value=({"vim": "7.4"}, "repository") + ), + "restartcheck.restartcheck": MagicMock( + return_value="No packages seem to need to be restarted" + ), } } with patch.multiple(opkg, **patch_kwargs): - self.assertEqual(opkg.remove('vim'), REMOVED) + self.assertEqual(opkg.remove("vim"), REMOVED) def test_remove_noaction(self): - ''' + """ Test - Remove packages. - ''' - with patch('salt.modules.opkg.list_pkgs', MagicMock(return_value=({}))): - ret_value = {'retcode': 0} + """ + with patch("salt.modules.opkg.list_pkgs", MagicMock(return_value=({}))): + ret_value = {"retcode": 0} mock = MagicMock(return_value=ret_value) patch_kwargs = { - '__salt__': { - 'cmd.run_all': mock, - 'pkg_resource.parse_targets': MagicMock(return_value=({'vim': '7.4'}, 'repository')), - 'restartcheck.restartcheck': MagicMock(return_value='No packages seem to need to be restarted') + "__salt__": { + "cmd.run_all": mock, + "pkg_resource.parse_targets": MagicMock( + return_value=({"vim": "7.4"}, "repository") + ), + "restartcheck.restartcheck": MagicMock( + return_value="No packages seem to need to be restarted" + ), } } with patch.multiple(opkg, **patch_kwargs): - self.assertEqual(opkg.remove('vim:7.4', test=True), {}) + self.assertEqual(opkg.remove("vim:7.4", test=True), {}) def test_info_installed(self): - ''' + """ Test - Return the information of the named package(s) installed on the system. - ''' - installed = copy.deepcopy(OPKG_VIM_INFO['vim']) - del installed['Package'] + """ + installed = copy.deepcopy(OPKG_VIM_INFO["vim"]) + del installed["Package"] ordered_info = collections.OrderedDict(sorted(installed.items())) - expected_dict = {'vim': {k.lower(): v for k, v in ordered_info.items()}} - std_out = '\n'.join([k + ': ' + v for k, v in OPKG_VIM_INFO['vim'].items()]) - ret_value = { - 'stdout': std_out, - 'retcode': 0 - } + expected_dict = {"vim": {k.lower(): v for k, v in ordered_info.items()}} + std_out = "\n".join([k + ": " + v for k, v in OPKG_VIM_INFO["vim"].items()]) + ret_value = {"stdout": std_out, "retcode": 0} mock = MagicMock(return_value=ret_value) - with patch.dict(opkg.__salt__, {'cmd.run_all': mock}): - self.assertEqual(opkg.info_installed('vim'), expected_dict) + with patch.dict(opkg.__salt__, {"cmd.run_all": mock}): + self.assertEqual(opkg.info_installed("vim"), expected_dict) def test_version_clean(self): - ''' + """ Test - Return the information of version_clean - ''' - self.assertEqual(opkg.version_clean('1.0.1'), '1.0.1') + """ + self.assertEqual(opkg.version_clean("1.0.1"), "1.0.1") def test_check_extra_requirements(self): - ''' + """ Test - Return the information of check_extra_requirements - ''' - self.assertEqual(opkg.check_extra_requirements('vim', '1.0.1'), True) + """ + self.assertEqual(opkg.check_extra_requirements("vim", "1.0.1"), True) diff --git a/tests/unit/modules/test_oracle.py b/tests/unit/modules/test_oracle.py index b85dbe24d00..70eed54fd19 100644 --- a/tests/unit/modules/test_oracle.py +++ b/tests/unit/modules/test_oracle.py @@ -1,83 +1,82 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.oracle as oracle +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class OracleTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.oracle - ''' + """ + def setup_loader_modules(self): - return {oracle: {'cx_Oracle': object()}} + return {oracle: {"cx_Oracle": object()}} def test_run_query(self): - ''' + """ Test for Run SQL query and return result - ''' - with patch.object(oracle, '_connect', MagicMock()) as mock_connect: + """ + with patch.object(oracle, "_connect", MagicMock()) as mock_connect: mock_connect.cursor.execute.fetchall.return_value = True - with patch.object(oracle, 'show_dbs', MagicMock()): - self.assertTrue(oracle.run_query('db', 'query')) + with patch.object(oracle, "show_dbs", MagicMock()): + self.assertTrue(oracle.run_query("db", "query")) def test_show_dbs(self): - ''' + """ Test for Show databases configuration from pillar. Filter by `*args` - ''' - with patch.dict(oracle.__salt__, {'pillar.get': - MagicMock(return_value='a')}): - self.assertDictEqual(oracle.show_dbs('A', 'B'), - {'A': 'a', 'B': 'a'}) + """ + with patch.dict(oracle.__salt__, {"pillar.get": MagicMock(return_value="a")}): + self.assertDictEqual(oracle.show_dbs("A", "B"), {"A": "a", "B": "a"}) - self.assertEqual(oracle.show_dbs(), 'a') + self.assertEqual(oracle.show_dbs(), "a") def test_version(self): - ''' + """ Test for Server Version (select banner from v$version) - ''' - with patch.dict(oracle.__salt__, {'pillar.get': - MagicMock(return_value='a')}): - with patch.object(oracle, 'run_query', return_value='A'): + """ + with patch.dict(oracle.__salt__, {"pillar.get": MagicMock(return_value="a")}): + with patch.object(oracle, "run_query", return_value="A"): self.assertDictEqual(oracle.version(), {}) def test_client_version(self): - ''' + """ Test for Oracle Client Version - ''' - with patch.object(oracle, 'cx_Oracle', - MagicMock(side_effect=MagicMock())): - self.assertEqual(oracle.client_version(), '') + """ + with patch.object(oracle, "cx_Oracle", MagicMock(side_effect=MagicMock())): + self.assertEqual(oracle.client_version(), "") def test_show_pillar(self): - ''' + """ Test for Show Pillar segment oracle.* - ''' - with patch.dict(oracle.__salt__, {'pillar.get': - MagicMock(return_value='a')}): - self.assertEqual(oracle.show_pillar('item'), 'a') + """ + with patch.dict(oracle.__salt__, {"pillar.get": MagicMock(return_value="a")}): + self.assertEqual(oracle.show_pillar("item"), "a") def test_show_env(self): - ''' + """ Test for Show Environment used by Oracle Client - ''' - with patch.object(os, 'environ', - return_value={'PATH': 'PATH', - 'ORACLE_HOME': 'ORACLE_HOME', - 'TNS_ADMIN': 'TNS_ADMIN', - 'NLS_LANG': 'NLS_LANG'}): + """ + with patch.object( + os, + "environ", + return_value={ + "PATH": "PATH", + "ORACLE_HOME": "ORACLE_HOME", + "TNS_ADMIN": "TNS_ADMIN", + "NLS_LANG": "NLS_LANG", + }, + ): self.assertDictEqual(oracle.show_env(), {}) diff --git a/tests/unit/modules/test_osquery.py b/tests/unit/modules/test_osquery.py index a727c89f0b7..7a11fd7ca40 100644 --- a/tests/unit/modules/test_osquery.py +++ b/tests/unit/modules/test_osquery.py @@ -1,213 +1,274 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Gareth J. Greenaway <gareth@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.osquery as osquery +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class OSQueryTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.iptables - ''' + """ + def setup_loader_modules(self): return {osquery: {}} def test_version(self): - ''' + """ Test the version returned from OSQuery - ''' - _table_attrs_results = ['pid', - 'uuid', - 'instance_id', - 'version', - 'config_hash', - 'config_valid', - 'extensions', - 'build_platform', - 'build_distro', - 'start_time', - 'watcher'] + """ + _table_attrs_results = [ + "pid", + "uuid", + "instance_id", + "version", + "config_hash", + "config_valid", + "extensions", + "build_platform", + "build_distro", + "start_time", + "watcher", + ] - _os_query_results = {'data': [{'version': '2.6.1'}], 'result': True} + _os_query_results = {"data": [{"version": "2.6.1"}], "result": True} - with patch.object(osquery, '_table_attrs', - MagicMock(return_value=_table_attrs_results)): - with patch.object(osquery, '_osquery', - MagicMock(return_value=_os_query_results)): - self.assertEqual(osquery.version(), '2.6.1') + with patch.object( + osquery, "_table_attrs", MagicMock(return_value=_table_attrs_results) + ): + with patch.object( + osquery, "_osquery", MagicMock(return_value=_os_query_results) + ): + self.assertEqual(osquery.version(), "2.6.1") def test_deb_packages(self): - ''' + """ Test the results returned from the deb_packages function - ''' - _os_query_results = {'data': [ - {'arch': 'amd64', 'name': 'accountsservice', 'revision': '1', - 'size': '451', 'source': '', 'version': '0.6.45-1'}, - {'arch': 'amd64', 'name': 'acetoneiso', 'revision': '2+b2', - 'size': '1820', 'source': 'acetoneiso (2.4-2)', - 'version': '2.4-2+b2'}, - {'arch': 'amd64', 'name': 'acl', 'revision': '3+b1', - 'size': '200', 'source': 'acl (2.2.52-3)', - 'version': '2.2.52-3+b1'}, - {'arch': 'amd64', 'name': 'adb', 'revision': '2', 'size': '189', - 'source': 'android-platform-system-core', - 'version': '1: 7.0.0+r33-2'}], - 'result': True + """ + _os_query_results = { + "data": [ + { + "arch": "amd64", + "name": "accountsservice", + "revision": "1", + "size": "451", + "source": "", + "version": "0.6.45-1", + }, + { + "arch": "amd64", + "name": "acetoneiso", + "revision": "2+b2", + "size": "1820", + "source": "acetoneiso (2.4-2)", + "version": "2.4-2+b2", + }, + { + "arch": "amd64", + "name": "acl", + "revision": "3+b1", + "size": "200", + "source": "acl (2.2.52-3)", + "version": "2.2.52-3+b1", + }, + { + "arch": "amd64", + "name": "adb", + "revision": "2", + "size": "189", + "source": "android-platform-system-core", + "version": "1: 7.0.0+r33-2", + }, + ], + "result": True, } - with patch.object(osquery, '_osquery', - MagicMock(return_value=_os_query_results)): - with patch.dict(osquery.__grains__, {'os_family': 'Debian'}): + with patch.object( + osquery, "_osquery", MagicMock(return_value=_os_query_results) + ): + with patch.dict(osquery.__grains__, {"os_family": "Debian"}): self.assertEqual(osquery.deb_packages(), _os_query_results) def test_deb_packages_with_attrs(self): - ''' + """ Test the results returned from the deb_packages function with attributes - ''' - _table_attrs_results = ['name', - 'version', - 'source', - 'size', - 'arch', - 'revision'] + """ + _table_attrs_results = ["name", "version", "source", "size", "arch", "revision"] - _os_query_results = {'data': [ - {'name': 'accountsservice', 'version': '0.6.45-1'}, - {'name': 'acetoneiso', 'version': '2.4-2+b2'}, - {'name': 'acl', 'version': '2.2.52-3+b1'}, - {'name': 'adb', 'version': '1: 7.0.0+r33-2'}], - 'result': True} + _os_query_results = { + "data": [ + {"name": "accountsservice", "version": "0.6.45-1"}, + {"name": "acetoneiso", "version": "2.4-2+b2"}, + {"name": "acl", "version": "2.2.52-3+b1"}, + {"name": "adb", "version": "1: 7.0.0+r33-2"}, + ], + "result": True, + } - with patch.object(osquery, '_table_attrs', - MagicMock(return_value=_table_attrs_results)): - with patch.object(osquery, '_osquery', - MagicMock(return_value=_os_query_results)): - with patch.dict(osquery.__grains__, {'os_family': 'Debian'}): - self.assertEqual(osquery.deb_packages(attrs=['name', - 'version']), - _os_query_results) + with patch.object( + osquery, "_table_attrs", MagicMock(return_value=_table_attrs_results) + ): + with patch.object( + osquery, "_osquery", MagicMock(return_value=_os_query_results) + ): + with patch.dict(osquery.__grains__, {"os_family": "Debian"}): + self.assertEqual( + osquery.deb_packages(attrs=["name", "version"]), + _os_query_results, + ) def test_kernel_modules(self): - ''' + """ Test the results returned from the kernel_modules function - ''' - _os_query_results = {'data': [ - {'address': '0xffffffffc14f2000', 'name': 'nls_utf8', - 'size': '16384', 'status': 'Live', 'used_by': '-'}, - {'address': '0xffffffffc1599000', 'name': 'udf', - 'size': '90112', 'status': 'Live', 'used_by': '-'}, - {'address': '0xffffffffc14b5000', 'name': 'crc_itu_t', - 'size': '16384', 'status': 'Live', 'used_by': 'udf'}], - 'result': True + """ + _os_query_results = { + "data": [ + { + "address": "0xffffffffc14f2000", + "name": "nls_utf8", + "size": "16384", + "status": "Live", + "used_by": "-", + }, + { + "address": "0xffffffffc1599000", + "name": "udf", + "size": "90112", + "status": "Live", + "used_by": "-", + }, + { + "address": "0xffffffffc14b5000", + "name": "crc_itu_t", + "size": "16384", + "status": "Live", + "used_by": "udf", + }, + ], + "result": True, } - with patch.object(osquery, '_osquery', - MagicMock(return_value=_os_query_results)): - with patch.dict(osquery.__grains__, {'os_family': 'Debian'}): - self.assertEqual(osquery.kernel_modules(), - _os_query_results) + with patch.object( + osquery, "_osquery", MagicMock(return_value=_os_query_results) + ): + with patch.dict(osquery.__grains__, {"os_family": "Debian"}): + self.assertEqual(osquery.kernel_modules(), _os_query_results) def test_kernel_modules_with_attrs(self): - ''' + """ Test the results returned from the kernel_modules function with attributes - ''' - _table_attrs_results = ['address', - 'name', - 'size', - 'status', - 'used_by'] + """ + _table_attrs_results = ["address", "name", "size", "status", "used_by"] - _os_query_results = {'data': [ - {'name': 'nls_utf8', 'status': 'Live'}, - {'name': 'udf', 'status': 'Live'}, - {'name': 'crc_itu_t', 'status': 'Live'}], - 'result': True + _os_query_results = { + "data": [ + {"name": "nls_utf8", "status": "Live"}, + {"name": "udf", "status": "Live"}, + {"name": "crc_itu_t", "status": "Live"}, + ], + "result": True, } - with patch.object(osquery, '_table_attrs', - MagicMock(return_value=_table_attrs_results)): - with patch.object(osquery, '_osquery', - MagicMock(return_value=_os_query_results)): - with patch.dict(osquery.__grains__, {'os_family': 'Debian'}): - self.assertEqual(osquery.kernel_modules(attrs=['name', - 'status']), - _os_query_results) + with patch.object( + osquery, "_table_attrs", MagicMock(return_value=_table_attrs_results) + ): + with patch.object( + osquery, "_osquery", MagicMock(return_value=_os_query_results) + ): + with patch.dict(osquery.__grains__, {"os_family": "Debian"}): + self.assertEqual( + osquery.kernel_modules(attrs=["name", "status"]), + _os_query_results, + ) def test_osquery_info(self): - ''' + """ Test the results returned from the kernel_modules function with attributes - ''' - _table_attrs_results = ['pid', - 'uuid', - 'instance_id', - 'version', - 'config_hash', - 'config_valid', - 'extensions', - 'build_platform', - 'build_distro', - 'start_time', - 'watcher'] + """ + _table_attrs_results = [ + "pid", + "uuid", + "instance_id", + "version", + "config_hash", + "config_valid", + "extensions", + "build_platform", + "build_distro", + "start_time", + "watcher", + ] - _os_query_results = {'data': [ - {'build_platform': 'ubuntu', 'start_time': '1514484833', - 'uuid': 'D31FD400-7277-11E3-ABA6-B8AEED7E173B', - 'build_distro': 'xenial', - 'pid': '24288', - 'watcher': '-1', - 'instance_id': 'dff196b0-5c91-4105-962b-28660d7aa282', - 'version': '2.6.1', - 'extensions': 'inactive', - 'config_valid': '0', - 'config_hash': ''}], - 'result': True} + _os_query_results = { + "data": [ + { + "build_platform": "ubuntu", + "start_time": "1514484833", + "uuid": "D31FD400-7277-11E3-ABA6-B8AEED7E173B", + "build_distro": "xenial", + "pid": "24288", + "watcher": "-1", + "instance_id": "dff196b0-5c91-4105-962b-28660d7aa282", + "version": "2.6.1", + "extensions": "inactive", + "config_valid": "0", + "config_hash": "", + } + ], + "result": True, + } - with patch.object(osquery, '_table_attrs', - MagicMock(return_value=_table_attrs_results)): - with patch.object(osquery, '_osquery', - MagicMock(return_value=_os_query_results)): - with patch.dict(osquery.__grains__, {'os_family': 'Debian'}): - self.assertEqual(osquery.osquery_info(), - _os_query_results) + with patch.object( + osquery, "_table_attrs", MagicMock(return_value=_table_attrs_results) + ): + with patch.object( + osquery, "_osquery", MagicMock(return_value=_os_query_results) + ): + with patch.dict(osquery.__grains__, {"os_family": "Debian"}): + self.assertEqual(osquery.osquery_info(), _os_query_results) def test_osquery_info_with_attrs(self): - ''' + """ Test the results returned from the kernel_modules function with attributes - ''' - _table_attrs_results = ['pid', - 'uuid', - 'instance_id', - 'version', - 'config_hash', - 'config_valid', - 'extensions', - 'build_platform', - 'build_distro', - 'start_time', - 'watcher'] + """ + _table_attrs_results = [ + "pid", + "uuid", + "instance_id", + "version", + "config_hash", + "config_valid", + "extensions", + "build_platform", + "build_distro", + "start_time", + "watcher", + ] - _os_query_results = {'data': [ - {'build_platform': 'ubuntu', 'start_time': '1514484833'}], - 'result': True} + _os_query_results = { + "data": [{"build_platform": "ubuntu", "start_time": "1514484833"}], + "result": True, + } - with patch.object(osquery, '_table_attrs', - MagicMock(return_value=_table_attrs_results)): - with patch.object(osquery, '_osquery', - MagicMock(return_value=_os_query_results)): - with patch.dict(osquery.__grains__, {'os_family': 'Debian'}): - self.assertEqual(osquery.osquery_info(attrs=['build_platform', 'start_time']), - _os_query_results) + with patch.object( + osquery, "_table_attrs", MagicMock(return_value=_table_attrs_results) + ): + with patch.object( + osquery, "_osquery", MagicMock(return_value=_os_query_results) + ): + with patch.dict(osquery.__grains__, {"os_family": "Debian"}): + self.assertEqual( + osquery.osquery_info(attrs=["build_platform", "start_time"]), + _os_query_results, + ) diff --git a/tests/unit/modules/test_pacmanpkg.py b/tests/unit/modules/test_pacmanpkg.py index dc6b9c0cbe4..458d8460f18 100644 --- a/tests/unit/modules/test_pacmanpkg.py +++ b/tests/unit/modules/test_pacmanpkg.py @@ -1,130 +1,149 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Eric Vz <eric@base10.org> -''' +""" # Import Python Libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.pacmanpkg as pacman +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PacmanTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.pacman - ''' + """ + def setup_loader_modules(self): return {pacman: {}} def test_list_pkgs(self): - ''' + """ Test if it list the packages currently installed in a dict - ''' - cmdmock = MagicMock(return_value='A 1.0\nB 2.0') + """ + cmdmock = MagicMock(return_value="A 1.0\nB 2.0") sortmock = MagicMock() stringifymock = MagicMock() - mock_ret = {'A': ['1.0'], 'B': ['2.0']} - with patch.dict(pacman.__salt__, { - 'cmd.run': cmdmock, - 'pkg_resource.add_pkg': lambda pkgs, name, version: pkgs.setdefault(name, []).append(version), - 'pkg_resource.sort_pkglist': sortmock, - 'pkg_resource.stringify': stringifymock - }): + mock_ret = {"A": ["1.0"], "B": ["2.0"]} + with patch.dict( + pacman.__salt__, + { + "cmd.run": cmdmock, + "pkg_resource.add_pkg": lambda pkgs, name, version: pkgs.setdefault( + name, [] + ).append(version), + "pkg_resource.sort_pkglist": sortmock, + "pkg_resource.stringify": stringifymock, + }, + ): self.assertDictEqual(pacman.list_pkgs(), mock_ret) sortmock.assert_called_with(mock_ret) stringifymock.assert_called_with(mock_ret) def test_list_pkgs_as_list(self): - ''' + """ Test if it lists the packages currently installed in a dict - ''' - cmdmock = MagicMock(return_value='A 1.0\nB 2.0') + """ + cmdmock = MagicMock(return_value="A 1.0\nB 2.0") sortmock = MagicMock() stringifymock = MagicMock() - mock_ret = {'A': ['1.0'], 'B': ['2.0']} - with patch.dict(pacman.__salt__, { - 'cmd.run': cmdmock, - 'pkg_resource.add_pkg': lambda pkgs, name, version: pkgs.setdefault(name, []).append(version), - 'pkg_resource.sort_pkglist': sortmock, - 'pkg_resource.stringify': stringifymock - }): + mock_ret = {"A": ["1.0"], "B": ["2.0"]} + with patch.dict( + pacman.__salt__, + { + "cmd.run": cmdmock, + "pkg_resource.add_pkg": lambda pkgs, name, version: pkgs.setdefault( + name, [] + ).append(version), + "pkg_resource.sort_pkglist": sortmock, + "pkg_resource.stringify": stringifymock, + }, + ): self.assertDictEqual(pacman.list_pkgs(True), mock_ret) sortmock.assert_called_with(mock_ret) self.assertTrue(stringifymock.call_count == 0) def test_group_list(self): - ''' + """ Test if it lists the available groups - ''' + """ def cmdlist(cmd, **kwargs): - ''' + """ Handle several different commands being run - ''' - if cmd == ['pacman', '-Sgg']: - return 'group-a pkg1\ngroup-a pkg2\ngroup-f pkg9\ngroup-c pkg3\ngroup-b pkg4' - elif cmd == ['pacman', '-Qg']: - return 'group-a pkg1\ngroup-b pkg4' + """ + if cmd == ["pacman", "-Sgg"]: + return "group-a pkg1\ngroup-a pkg2\ngroup-f pkg9\ngroup-c pkg3\ngroup-b pkg4" + elif cmd == ["pacman", "-Qg"]: + return "group-a pkg1\ngroup-b pkg4" else: - return 'Untested command ({0}, {1})!'.format(cmd, kwargs) + return "Untested command ({0}, {1})!".format(cmd, kwargs) cmdmock = MagicMock(side_effect=cmdlist) sortmock = MagicMock() - with patch.dict(pacman.__salt__, { - 'cmd.run': cmdmock, - 'pkg_resource.sort_pkglist': sortmock - }): - self.assertDictEqual(pacman.group_list(), {'available': ['group-c', 'group-f'], 'installed': ['group-b'], 'partially_installed': ['group-a']}) + with patch.dict( + pacman.__salt__, {"cmd.run": cmdmock, "pkg_resource.sort_pkglist": sortmock} + ): + self.assertDictEqual( + pacman.group_list(), + { + "available": ["group-c", "group-f"], + "installed": ["group-b"], + "partially_installed": ["group-a"], + }, + ) def test_group_info(self): - ''' + """ Test if it shows the packages in a group - ''' + """ def cmdlist(cmd, **kwargs): - ''' + """ Handle several different commands being run - ''' - if cmd == ['pacman', '-Sgg', 'testgroup']: - return 'testgroup pkg1\ntestgroup pkg2' + """ + if cmd == ["pacman", "-Sgg", "testgroup"]: + return "testgroup pkg1\ntestgroup pkg2" else: - return 'Untested command ({0}, {1})!'.format(cmd, kwargs) + return "Untested command ({0}, {1})!".format(cmd, kwargs) cmdmock = MagicMock(side_effect=cmdlist) sortmock = MagicMock() - with patch.dict(pacman.__salt__, { - 'cmd.run': cmdmock, - 'pkg_resource.sort_pkglist': sortmock - }): - self.assertEqual(pacman.group_info('testgroup')['default'], ['pkg1', 'pkg2']) + with patch.dict( + pacman.__salt__, {"cmd.run": cmdmock, "pkg_resource.sort_pkglist": sortmock} + ): + self.assertEqual( + pacman.group_info("testgroup")["default"], ["pkg1", "pkg2"] + ) def test_group_diff(self): - ''' + """ Test if it shows the difference between installed and target group contents - ''' + """ - listmock = MagicMock(return_value={'A': ['1.0'], 'B': ['2.0']}) - groupmock = MagicMock(return_value={ - 'mandatory': [], - 'optional': [], - 'default': ['A', 'C'], - 'conditional': []}) - with patch.dict(pacman.__salt__, { - 'pkg.list_pkgs': listmock, - 'pkg.group_info': groupmock - }): - results = pacman.group_diff('testgroup') - self.assertEqual(results['default'], {'installed': ['A'], 'not installed': ['C']}) + listmock = MagicMock(return_value={"A": ["1.0"], "B": ["2.0"]}) + groupmock = MagicMock( + return_value={ + "mandatory": [], + "optional": [], + "default": ["A", "C"], + "conditional": [], + } + ) + with patch.dict( + pacman.__salt__, {"pkg.list_pkgs": listmock, "pkg.group_info": groupmock} + ): + results = pacman.group_diff("testgroup") + self.assertEqual( + results["default"], {"installed": ["A"], "not installed": ["C"]} + ) diff --git a/tests/unit/modules/test_pagerduty.py b/tests/unit/modules/test_pagerduty.py index ad702864d8c..e8004caccc3 100644 --- a/tests/unit/modules/test_pagerduty.py +++ b/tests/unit/modules/test_pagerduty.py @@ -1,85 +1,79 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, - MagicMock, -) +import salt.modules.pagerduty as pagerduty # Import Salt Libs import salt.utils.json import salt.utils.pagerduty -import salt.modules.pagerduty as pagerduty + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class PagerdutyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.pagerduty - ''' + """ + def setup_loader_modules(self): - return {pagerduty: {'__salt__': {'config.option': MagicMock(return_value=None)}}} + return { + pagerduty: {"__salt__": {"config.option": MagicMock(return_value=None)}} + } def test_list_services(self): - ''' + """ Test for List services belonging to this account - ''' - with patch.object(salt.utils.pagerduty, - 'list_items', return_value='A'): - self.assertEqual(pagerduty.list_services(), 'A') + """ + with patch.object(salt.utils.pagerduty, "list_items", return_value="A"): + self.assertEqual(pagerduty.list_services(), "A") def test_list_incidents(self): - ''' + """ Test for List incidents belonging to this account - ''' - with patch.object(salt.utils.pagerduty, - 'list_items', return_value='A'): - self.assertEqual(pagerduty.list_incidents(), 'A') + """ + with patch.object(salt.utils.pagerduty, "list_items", return_value="A"): + self.assertEqual(pagerduty.list_incidents(), "A") def test_list_users(self): - ''' + """ Test for List users belonging to this account - ''' - with patch.object(salt.utils.pagerduty, - 'list_items', return_value='A'): - self.assertEqual(pagerduty.list_users(), 'A') + """ + with patch.object(salt.utils.pagerduty, "list_items", return_value="A"): + self.assertEqual(pagerduty.list_users(), "A") def test_list_schedules(self): - ''' + """ Test for List schedules belonging to this account - ''' - with patch.object(salt.utils.pagerduty, - 'list_items', return_value='A'): - self.assertEqual(pagerduty.list_schedules(), 'A') + """ + with patch.object(salt.utils.pagerduty, "list_items", return_value="A"): + self.assertEqual(pagerduty.list_schedules(), "A") def test_list_windows(self): - ''' + """ Test for List maintenance windows belonging to this account - ''' - with patch.object(salt.utils.pagerduty, - 'list_items', return_value='A'): - self.assertEqual(pagerduty.list_windows(), 'A') + """ + with patch.object(salt.utils.pagerduty, "list_items", return_value="A"): + self.assertEqual(pagerduty.list_windows(), "A") def test_list_policies(self): - ''' + """ Test for List escalation policies belonging to this account - ''' - with patch.object(salt.utils.pagerduty, - 'list_items', return_value='A'): - self.assertEqual(pagerduty.list_policies(), 'A') + """ + with patch.object(salt.utils.pagerduty, "list_items", return_value="A"): + self.assertEqual(pagerduty.list_policies(), "A") def test_create_event(self): - ''' + """ Test for Create an event in PagerDuty. Designed for use in states. - ''' - with patch.object(salt.utils.json, 'loads', return_value=['A']): - with patch.object(salt.utils.pagerduty, 'query', - return_value='A'): - self.assertListEqual(pagerduty.create_event(), ['A']) + """ + with patch.object(salt.utils.json, "loads", return_value=["A"]): + with patch.object(salt.utils.pagerduty, "query", return_value="A"): + self.assertListEqual(pagerduty.create_event(), ["A"]) diff --git a/tests/unit/modules/test_pam.py b/tests/unit/modules/test_pam.py index 0ea0ac910bf..afea86a7a11 100644 --- a/tests/unit/modules/test_pam.py +++ b/tests/unit/modules/test_pam.py @@ -1,38 +1,46 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import sys +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - mock_open, -) +import sys # Import Salt Libs import salt.modules.pam as pam +from tests.support.mock import mock_open, patch -MOCK_FILE = 'ok ok ignore ' +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf + +MOCK_FILE = "ok ok ignore " -@skipIf(sys.platform.startswith('openbsd'), 'OpenBSD does not use PAM') +@skipIf(sys.platform.startswith("openbsd"), "OpenBSD does not use PAM") class PamTestCase(TestCase): - ''' + """ Test cases for salt.modules.pam - ''' + """ + # 'read_file' function tests: 1 def test_read_file(self): - ''' + """ Test if the parsing function works - ''' - with patch('os.path.exists', return_value=True), \ - patch('salt.utils.files.fopen', mock_open(read_data=MOCK_FILE)): - self.assertListEqual(pam.read_file('/etc/pam.d/login'), - [{'arguments': [], 'control_flag': 'ok', - 'interface': 'ok', 'module': 'ignore'}]) + """ + with patch("os.path.exists", return_value=True), patch( + "salt.utils.files.fopen", mock_open(read_data=MOCK_FILE) + ): + self.assertListEqual( + pam.read_file("/etc/pam.d/login"), + [ + { + "arguments": [], + "control_flag": "ok", + "interface": "ok", + "module": "ignore", + } + ], + ) diff --git a/tests/unit/modules/test_parallels.py b/tests/unit/modules/test_parallels.py index 694a741243b..9e503afc5f8 100644 --- a/tests/unit/modules/test_parallels.py +++ b/tests/unit/modules/test_parallels.py @@ -2,46 +2,49 @@ # Import python libs from __future__ import absolute_import + import textwrap # Import Salt Libs import salt.modules.parallels as parallels -from salt.exceptions import SaltInvocationError, CommandExecutionError - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import third party libs from salt.ext import six +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ParallelsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test parallels desktop execution module functions - ''' + """ + def setup_loader_modules(self): return {parallels: {}} def test__normalize_args(self): - ''' + """ Test parallels._normalize_args - ''' + """ + def _validate_ret(ret): - ''' + """ Assert that the returned data is a list of strings - ''' + """ self.assertTrue(isinstance(ret, list)) for arg in ret: self.assertTrue(isinstance(arg, six.string_types)) # Validate string arguments - str_args = 'electrolytes --aqueous --anion hydroxide --cation=ammonium free radicals -- hydrogen' + str_args = "electrolytes --aqueous --anion hydroxide --cation=ammonium free radicals -- hydrogen" _validate_ret(parallels._normalize_args(str_args)) # Validate list arguments - list_args = ' '.join(str_args) + list_args = " ".join(str_args) _validate_ret(parallels._normalize_args(list_args)) # Validate tuple arguments @@ -49,550 +52,576 @@ class ParallelsTestCase(TestCase, LoaderModuleMockMixin): _validate_ret(parallels._normalize_args(tuple_args)) # Validate dictionary arguments - other_args = {'anion': 'hydroxide', 'cation': 'ammonium'} + other_args = {"anion": "hydroxide", "cation": "ammonium"} _validate_ret(parallels._normalize_args(other_args)) def test__find_guids(self): - ''' + """ Test parallels._find_guids - ''' - guid_str = textwrap.dedent(''' + """ + guid_str = textwrap.dedent( + """ PARENT_SNAPSHOT_ID SNAPSHOT_ID {a5b8999f-5d95-4aff-82de-e515b0101b66} {a5b8999f-5d95-4aff-82de-e515b0101b66} *{a7345be5-ab66-478c-946e-a6c2caf14909} - ''') - guids = ['a5b8999f-5d95-4aff-82de-e515b0101b66', - 'a7345be5-ab66-478c-946e-a6c2caf14909'] + """ + ) + guids = [ + "a5b8999f-5d95-4aff-82de-e515b0101b66", + "a7345be5-ab66-478c-946e-a6c2caf14909", + ] self.assertEqual(parallels._find_guids(guid_str), guids) def test_prlsrvctl(self): - ''' + """ Test parallels.prlsrvctl - ''' - runas = 'macdev' + """ + runas = "macdev" # Test missing prlsrvctl binary - with patch('salt.utils.path.which', MagicMock(return_value=False)): + with patch("salt.utils.path.which", MagicMock(return_value=False)): with self.assertRaises(CommandExecutionError): - parallels.prlsrvctl('info', runas=runas) + parallels.prlsrvctl("info", runas=runas) # Simulate the existence of prlsrvctl - with patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/prlsrvctl")): + with patch( + "salt.utils.path.which", MagicMock(return_value="/usr/bin/prlsrvctl") + ): # Validate 'prlsrvctl info' - info_cmd = ['prlsrvctl', 'info'] + info_cmd = ["prlsrvctl", "info"] info_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': info_fcn}): - parallels.prlsrvctl('info', runas=runas) + with patch.dict(parallels.__salt__, {"cmd.run": info_fcn}): + parallels.prlsrvctl("info", runas=runas) info_fcn.assert_called_once_with(info_cmd, runas=runas) # Validate 'prlsrvctl usb list' - usb_cmd = ['prlsrvctl', 'usb', 'list'] + usb_cmd = ["prlsrvctl", "usb", "list"] usb_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': usb_fcn}): - parallels.prlsrvctl('usb', 'list', runas=runas) + with patch.dict(parallels.__salt__, {"cmd.run": usb_fcn}): + parallels.prlsrvctl("usb", "list", runas=runas) usb_fcn.assert_called_once_with(usb_cmd, runas=runas) # Validate 'prlsrvctl set "--mem-limit auto"' - set_cmd = ['prlsrvctl', 'set', '--mem-limit', 'auto'] + set_cmd = ["prlsrvctl", "set", "--mem-limit", "auto"] set_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': set_fcn}): - parallels.prlsrvctl('set', '--mem-limit auto', runas=runas) + with patch.dict(parallels.__salt__, {"cmd.run": set_fcn}): + parallels.prlsrvctl("set", "--mem-limit auto", runas=runas) set_fcn.assert_called_once_with(set_cmd, runas=runas) def test_prlctl(self): - ''' + """ Test parallels.prlctl - ''' - runas = 'macdev' + """ + runas = "macdev" # Test missing prlctl binary - with patch('salt.utils.path.which', MagicMock(return_value=False)): + with patch("salt.utils.path.which", MagicMock(return_value=False)): with self.assertRaises(CommandExecutionError): - parallels.prlctl('info', runas=runas) + parallels.prlctl("info", runas=runas) # Simulate the existence of prlctl - with patch('salt.utils.path.which', MagicMock(return_value="/usr/bin/prlctl")): + with patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/prlctl")): # Validate 'prlctl user list' - user_cmd = ['prlctl', 'user', 'list'] + user_cmd = ["prlctl", "user", "list"] user_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': user_fcn}): - parallels.prlctl('user', 'list', runas=runas) + with patch.dict(parallels.__salt__, {"cmd.run": user_fcn}): + parallels.prlctl("user", "list", runas=runas) user_fcn.assert_called_once_with(user_cmd, runas=runas) # Validate 'prlctl exec "macvm uname"' - exec_cmd = ['prlctl', 'exec', 'macvm', 'uname'] + exec_cmd = ["prlctl", "exec", "macvm", "uname"] exec_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': exec_fcn}): - parallels.prlctl('exec', 'macvm uname', runas=runas) + with patch.dict(parallels.__salt__, {"cmd.run": exec_fcn}): + parallels.prlctl("exec", "macvm uname", runas=runas) exec_fcn.assert_called_once_with(exec_cmd, runas=runas) # Validate 'prlctl capture "macvm --file macvm.display.png"' - cap_cmd = ['prlctl', 'capture', 'macvm', '--file', 'macvm.display.png'] + cap_cmd = ["prlctl", "capture", "macvm", "--file", "macvm.display.png"] cap_fcn = MagicMock() - with patch.dict(parallels.__salt__, {'cmd.run': cap_fcn}): - parallels.prlctl('capture', 'macvm --file macvm.display.png', runas=runas) + with patch.dict(parallels.__salt__, {"cmd.run": cap_fcn}): + parallels.prlctl( + "capture", "macvm --file macvm.display.png", runas=runas + ) cap_fcn.assert_called_once_with(cap_cmd, runas=runas) def test_list_vms(self): - ''' + """ Test parallels.list_vms - ''' - runas = 'macdev' + """ + runas = "macdev" # Validate a simple list mock_plain = MagicMock() - with patch.object(parallels, 'prlctl', mock_plain): + with patch.object(parallels, "prlctl", mock_plain): parallels.list_vms(runas=runas) - mock_plain.assert_called_once_with('list', [], runas=runas) + mock_plain.assert_called_once_with("list", [], runas=runas) # Validate listing a single VM mock_name = MagicMock() - with patch.object(parallels, 'prlctl', mock_name): - parallels.list_vms(name='macvm', runas=runas) - mock_name.assert_called_once_with('list', - ['macvm'], - runas=runas) + with patch.object(parallels, "prlctl", mock_name): + parallels.list_vms(name="macvm", runas=runas) + mock_name.assert_called_once_with("list", ["macvm"], runas=runas) # Validate listing templates mock_templ = MagicMock() - with patch.object(parallels, 'prlctl', mock_templ): + with patch.object(parallels, "prlctl", mock_templ): parallels.list_vms(template=True, runas=runas) - mock_templ.assert_called_once_with('list', - ['--template'], - runas=runas) + mock_templ.assert_called_once_with("list", ["--template"], runas=runas) # Validate listing extra info mock_info = MagicMock() - with patch.object(parallels, 'prlctl', mock_info): + with patch.object(parallels, "prlctl", mock_info): parallels.list_vms(info=True, runas=runas) - mock_info.assert_called_once_with('list', - ['--info'], - runas=runas) + mock_info.assert_called_once_with("list", ["--info"], runas=runas) # Validate listing with extra options mock_complex = MagicMock() - with patch.object(parallels, 'prlctl', mock_complex): - parallels.list_vms(args=' -o uuid,status', all=True, runas=runas) - mock_complex.assert_called_once_with('list', - ['-o', 'uuid,status', '--all'], - runas=runas) + with patch.object(parallels, "prlctl", mock_complex): + parallels.list_vms(args=" -o uuid,status", all=True, runas=runas) + mock_complex.assert_called_once_with( + "list", ["-o", "uuid,status", "--all"], runas=runas + ) def test_clone(self): - ''' + """ Test parallels.clone - ''' - name = 'macvm' - runas = 'macdev' + """ + name = "macvm" + runas = "macdev" # Validate clone mock_clone = MagicMock() - with patch.object(parallels, 'prlctl', mock_clone): - parallels.clone(name, 'macvm_new', runas=runas) - mock_clone.assert_called_once_with('clone', - [name, '--name', 'macvm_new'], - runas=runas) + with patch.object(parallels, "prlctl", mock_clone): + parallels.clone(name, "macvm_new", runas=runas) + mock_clone.assert_called_once_with( + "clone", [name, "--name", "macvm_new"], runas=runas + ) # Validate linked clone mock_linked = MagicMock() - with patch.object(parallels, 'prlctl', mock_linked): - parallels.clone(name, 'macvm_link', linked=True, runas=runas) - mock_linked.assert_called_once_with('clone', - [name, '--name', 'macvm_link', '--linked'], - runas=runas) + with patch.object(parallels, "prlctl", mock_linked): + parallels.clone(name, "macvm_link", linked=True, runas=runas) + mock_linked.assert_called_once_with( + "clone", [name, "--name", "macvm_link", "--linked"], runas=runas + ) # Validate template clone mock_template = MagicMock() - with patch.object(parallels, 'prlctl', mock_template): - parallels.clone(name, 'macvm_templ', template=True, runas=runas) - mock_template.assert_called_once_with('clone', - [name, '--name', 'macvm_templ', '--template'], - runas=runas) + with patch.object(parallels, "prlctl", mock_template): + parallels.clone(name, "macvm_templ", template=True, runas=runas) + mock_template.assert_called_once_with( + "clone", [name, "--name", "macvm_templ", "--template"], runas=runas + ) def test_delete(self): - ''' + """ Test parallels.delete - ''' - name = 'macvm' - runas = 'macdev' + """ + name = "macvm" + runas = "macdev" # Validate delete mock_delete = MagicMock() - with patch.object(parallels, 'prlctl', mock_delete): + with patch.object(parallels, "prlctl", mock_delete): parallels.delete(name, runas=runas) - mock_delete.assert_called_once_with('delete', name, runas=runas) + mock_delete.assert_called_once_with("delete", name, runas=runas) def test_exists(self): - ''' + """ Test parallels.exists - ''' - name = 'macvm' - runas = 'macdev' + """ + name = "macvm" + runas = "macdev" # Validate exists - mock_list = MagicMock(return_value='Name: {0}\nState: running'.format(name)) - with patch.object(parallels, 'list_vms', mock_list): + mock_list = MagicMock(return_value="Name: {0}\nState: running".format(name)) + with patch.object(parallels, "list_vms", mock_list): self.assertTrue(parallels.exists(name, runas=runas)) # Validate not exists - mock_list = MagicMock(return_value='Name: {0}\nState: running'.format(name)) - with patch.object(parallels, 'list_vms', mock_list): - self.assertFalse(parallels.exists('winvm', runas=runas)) + mock_list = MagicMock(return_value="Name: {0}\nState: running".format(name)) + with patch.object(parallels, "list_vms", mock_list): + self.assertFalse(parallels.exists("winvm", runas=runas)) def test_start(self): - ''' + """ Test parallels.start - ''' - name = 'macvm' - runas = 'macdev' + """ + name = "macvm" + runas = "macdev" mock_start = MagicMock() - with patch.object(parallels, 'prlctl', mock_start): + with patch.object(parallels, "prlctl", mock_start): parallels.start(name, runas=runas) - mock_start.assert_called_once_with('start', name, runas=runas) + mock_start.assert_called_once_with("start", name, runas=runas) def test_stop(self): - ''' + """ Test parallels.stop - ''' - name = 'macvm' - runas = 'macdev' + """ + name = "macvm" + runas = "macdev" # Validate stop mock_stop = MagicMock() - with patch.object(parallels, 'prlctl', mock_stop): + with patch.object(parallels, "prlctl", mock_stop): parallels.stop(name, runas=runas) - mock_stop.assert_called_once_with('stop', [name], runas=runas) + mock_stop.assert_called_once_with("stop", [name], runas=runas) # Validate immediate stop mock_kill = MagicMock() - with patch.object(parallels, 'prlctl', mock_kill): + with patch.object(parallels, "prlctl", mock_kill): parallels.stop(name, kill=True, runas=runas) - mock_kill.assert_called_once_with('stop', [name, '--kill'], runas=runas) + mock_kill.assert_called_once_with("stop", [name, "--kill"], runas=runas) def test_restart(self): - ''' + """ Test parallels.restart - ''' - name = 'macvm' - runas = 'macdev' + """ + name = "macvm" + runas = "macdev" mock_start = MagicMock() - with patch.object(parallels, 'prlctl', mock_start): + with patch.object(parallels, "prlctl", mock_start): parallels.restart(name, runas=runas) - mock_start.assert_called_once_with('restart', name, runas=runas) + mock_start.assert_called_once_with("restart", name, runas=runas) def test_reset(self): - ''' + """ Test parallels.reset - ''' - name = 'macvm' - runas = 'macdev' + """ + name = "macvm" + runas = "macdev" mock_start = MagicMock() - with patch.object(parallels, 'prlctl', mock_start): + with patch.object(parallels, "prlctl", mock_start): parallels.reset(name, runas=runas) - mock_start.assert_called_once_with('reset', name, runas=runas) + mock_start.assert_called_once_with("reset", name, runas=runas) def test_status(self): - ''' + """ Test parallels.status - ''' - name = 'macvm' - runas = 'macdev' + """ + name = "macvm" + runas = "macdev" mock_start = MagicMock() - with patch.object(parallels, 'prlctl', mock_start): + with patch.object(parallels, "prlctl", mock_start): parallels.status(name, runas=runas) - mock_start.assert_called_once_with('status', name, runas=runas) + mock_start.assert_called_once_with("status", name, runas=runas) def test_exec_(self): - ''' + """ Test parallels.exec_ - ''' - name = 'macvm' - runas = 'macdev' + """ + name = "macvm" + runas = "macdev" mock_start = MagicMock() - with patch.object(parallels, 'prlctl', mock_start): - parallels.exec_(name, 'find /etc/paths.d', runas=runas) - mock_start.assert_called_once_with('exec', - [name, 'find', '/etc/paths.d'], - runas=runas) + with patch.object(parallels, "prlctl", mock_start): + parallels.exec_(name, "find /etc/paths.d", runas=runas) + mock_start.assert_called_once_with( + "exec", [name, "find", "/etc/paths.d"], runas=runas + ) def test_snapshot_id_to_name(self): - ''' + """ Test parallels.snapshot_id_to_name - ''' - name = 'macvm' - snap_id = 'a5b8999f-5d95-4aff-82de-e515b0101b66' + """ + name = "macvm" + snap_id = "a5b8999f-5d95-4aff-82de-e515b0101b66" # Invalid GUID raises error - self.assertRaises(SaltInvocationError, - parallels.snapshot_id_to_name, - name, - '{8-4-4-4-12}') + self.assertRaises( + SaltInvocationError, parallels.snapshot_id_to_name, name, "{8-4-4-4-12}" + ) # Empty return from prlctl raises error (name/snap_id mismatch?) - mock_no_data = MagicMock(return_value='') - with patch.object(parallels, 'prlctl', mock_no_data): - self.assertRaises(SaltInvocationError, - parallels.snapshot_id_to_name, - name, - snap_id) + mock_no_data = MagicMock(return_value="") + with patch.object(parallels, "prlctl", mock_no_data): + self.assertRaises( + SaltInvocationError, parallels.snapshot_id_to_name, name, snap_id + ) # Data returned from prlctl is invalid YAML - mock_invalid_data = MagicMock(return_value='[string theory is falsifiable}') - with patch.object(parallels, 'prlctl', mock_invalid_data): + mock_invalid_data = MagicMock(return_value="[string theory is falsifiable}") + with patch.object(parallels, "prlctl", mock_invalid_data): snap_name = parallels.snapshot_id_to_name(name, snap_id) - self.assertEqual(snap_name, '') + self.assertEqual(snap_name, "") # Data returned from prlctl does not render as a dictionary mock_unknown_data = MagicMock(return_value="['sfermions', 'bosinos']") - with patch.object(parallels, 'prlctl', mock_unknown_data): + with patch.object(parallels, "prlctl", mock_unknown_data): snap_name = parallels.snapshot_id_to_name(name, snap_id) - self.assertEqual(snap_name, '') + self.assertEqual(snap_name, "") # Snapshot is unnamed - mock_no_name = MagicMock(return_value='Name:') - with patch.object(parallels, 'prlctl', mock_no_name): + mock_no_name = MagicMock(return_value="Name:") + with patch.object(parallels, "prlctl", mock_no_name): snap_name = parallels.snapshot_id_to_name(name, snap_id) - self.assertEqual(snap_name, '') + self.assertEqual(snap_name, "") # If strict, then raise an error when name is not found - mock_no_name = MagicMock(return_value='Name:') - with patch.object(parallels, 'prlctl', mock_no_name): - self.assertRaises(SaltInvocationError, - parallels.snapshot_id_to_name, - name, - snap_id, - strict=True) + mock_no_name = MagicMock(return_value="Name:") + with patch.object(parallels, "prlctl", mock_no_name): + self.assertRaises( + SaltInvocationError, + parallels.snapshot_id_to_name, + name, + snap_id, + strict=True, + ) # Return name when found - mock_yes_name = MagicMock(return_value='Name: top') - with patch.object(parallels, 'prlctl', mock_yes_name): + mock_yes_name = MagicMock(return_value="Name: top") + with patch.object(parallels, "prlctl", mock_yes_name): snap_name = parallels.snapshot_id_to_name(name, snap_id) - self.assertEqual(snap_name, 'top') + self.assertEqual(snap_name, "top") def test_snapshot_name_to_id(self): - ''' + """ Test parallels.snapshot_name_to_id - ''' - name = 'macvm' - snap_ids = ['a5b8999f-5d95-4aff-82de-e515b0101b66', - 'a7345be5-ab66-478c-946e-a6c2caf14909'] + """ + name = "macvm" + snap_ids = [ + "a5b8999f-5d95-4aff-82de-e515b0101b66", + "a7345be5-ab66-478c-946e-a6c2caf14909", + ] snap_id = snap_ids[0] - guid_str = textwrap.dedent(''' + guid_str = textwrap.dedent( + """ PARENT_SNAPSHOT_ID SNAPSHOT_ID {a5b8999f-5d95-4aff-82de-e515b0101b66} {a5b8999f-5d95-4aff-82de-e515b0101b66} *{a7345be5-ab66-478c-946e-a6c2caf14909} - ''') + """ + ) mock_guids = MagicMock(return_value=guid_str) # Raise error when no IDs found for snapshot name - with patch.object(parallels, 'prlctl', mock_guids): + with patch.object(parallels, "prlctl", mock_guids): mock_no_names = MagicMock(return_value=[]) - with patch.object(parallels, 'snapshot_id_to_name', mock_no_names): - self.assertRaises(SaltInvocationError, - parallels.snapshot_name_to_id, - name, - 'graviton') + with patch.object(parallels, "snapshot_id_to_name", mock_no_names): + self.assertRaises( + SaltInvocationError, parallels.snapshot_name_to_id, name, "graviton" + ) # Validate singly-valued name - with patch.object(parallels, 'prlctl', mock_guids): - mock_one_name = MagicMock(side_effect=[u'', u'ν_e']) - with patch.object(parallels, 'snapshot_id_to_name', mock_one_name): - self.assertEqual(parallels.snapshot_name_to_id(name, u'ν_e'), snap_ids[1]) + with patch.object(parallels, "prlctl", mock_guids): + mock_one_name = MagicMock(side_effect=[u"", u"ν_e"]) + with patch.object(parallels, "snapshot_id_to_name", mock_one_name): + self.assertEqual( + parallels.snapshot_name_to_id(name, u"ν_e"), snap_ids[1] + ) # Validate multiply-valued name - with patch.object(parallels, 'prlctl', mock_guids): - mock_many_names = MagicMock(side_effect=[u'J/Ψ', u'J/Ψ']) - with patch.object(parallels, 'snapshot_id_to_name', mock_many_names): - self.assertEqual(sorted(parallels.snapshot_name_to_id(name, u'J/Ψ')), - sorted(snap_ids)) + with patch.object(parallels, "prlctl", mock_guids): + mock_many_names = MagicMock(side_effect=[u"J/Ψ", u"J/Ψ"]) + with patch.object(parallels, "snapshot_id_to_name", mock_many_names): + self.assertEqual( + sorted(parallels.snapshot_name_to_id(name, u"J/Ψ")), + sorted(snap_ids), + ) # Raise error for multiply-valued name - with patch.object(parallels, 'prlctl', mock_guids): - mock_many_names = MagicMock(side_effect=[u'J/Ψ', u'J/Ψ']) - with patch.object(parallels, 'snapshot_id_to_name', mock_many_names): - self.assertRaises(SaltInvocationError, - parallels.snapshot_name_to_id, - name, - u'J/Ψ', - strict=True) + with patch.object(parallels, "prlctl", mock_guids): + mock_many_names = MagicMock(side_effect=[u"J/Ψ", u"J/Ψ"]) + with patch.object(parallels, "snapshot_id_to_name", mock_many_names): + self.assertRaises( + SaltInvocationError, + parallels.snapshot_name_to_id, + name, + u"J/Ψ", + strict=True, + ) def test__validate_snap_name(self): - ''' + """ Test parallels._validate_snap_name - ''' - name = 'macvm' - snap_id = 'a5b8999f-5d95-4aff-82de-e515b0101b66' + """ + name = "macvm" + snap_id = "a5b8999f-5d95-4aff-82de-e515b0101b66" # Validate a GUID passthrough self.assertEqual(parallels._validate_snap_name(name, snap_id), snap_id) # Validate an unicode name mock_snap_symb = MagicMock(return_value=snap_id) - with patch.object(parallels, 'snapshot_name_to_id', mock_snap_symb): - self.assertEqual(parallels._validate_snap_name(name, u'π'), snap_id) - mock_snap_symb.assert_called_once_with(name, u'π', strict=True, runas=None) + with patch.object(parallels, "snapshot_name_to_id", mock_snap_symb): + self.assertEqual(parallels._validate_snap_name(name, u"π"), snap_id) + mock_snap_symb.assert_called_once_with(name, u"π", strict=True, runas=None) # Validate an ascii name mock_snap_name = MagicMock(return_value=snap_id) - with patch.object(parallels, 'snapshot_name_to_id', mock_snap_name): - self.assertEqual(parallels._validate_snap_name(name, 'pion'), snap_id) - mock_snap_name.assert_called_once_with(name, 'pion', strict=True, runas=None) + with patch.object(parallels, "snapshot_name_to_id", mock_snap_name): + self.assertEqual(parallels._validate_snap_name(name, "pion"), snap_id) + mock_snap_name.assert_called_once_with( + name, "pion", strict=True, runas=None + ) # Validate a numerical name mock_snap_numb = MagicMock(return_value=snap_id) - with patch.object(parallels, 'snapshot_name_to_id', mock_snap_numb): - self.assertEqual(parallels._validate_snap_name(name, '3.14159'), snap_id) - mock_snap_numb.assert_called_once_with(name, u'3.14159', strict=True, runas=None) + with patch.object(parallels, "snapshot_name_to_id", mock_snap_numb): + self.assertEqual(parallels._validate_snap_name(name, "3.14159"), snap_id) + mock_snap_numb.assert_called_once_with( + name, u"3.14159", strict=True, runas=None + ) # Validate not strict (think neutrino oscillation) mock_snap_non_strict = MagicMock(return_value=snap_id) - with patch.object(parallels, 'snapshot_name_to_id', mock_snap_non_strict): - self.assertEqual(parallels._validate_snap_name(name, u'e_ν', strict=False), snap_id) - mock_snap_non_strict.assert_called_once_with(name, u'e_ν', strict=False, runas=None) + with patch.object(parallels, "snapshot_name_to_id", mock_snap_non_strict): + self.assertEqual( + parallels._validate_snap_name(name, u"e_ν", strict=False), snap_id + ) + mock_snap_non_strict.assert_called_once_with( + name, u"e_ν", strict=False, runas=None + ) def test_list_snapshots(self): - ''' + """ Test parallels.list_snapshots - ''' - name = 'macvm' - guid_str = textwrap.dedent(''' + """ + name = "macvm" + guid_str = textwrap.dedent( + """ PARENT_SNAPSHOT_ID SNAPSHOT_ID {a5b8999f-5d95-4aff-82de-e515b0101b66} {a5b8999f-5d95-4aff-82de-e515b0101b66} *{a7345be5-ab66-478c-946e-a6c2caf14909} {a5b8999f-5d95-4aff-82de-e515b0101b66} {5da9faef-cb0e-466d-9b41-e5571b62ac2a} - ''') + """ + ) # Validate listing all snapshots for the VM mock_prlctl = MagicMock(return_value=guid_str) - with patch.object(parallels, 'prlctl', mock_prlctl): + with patch.object(parallels, "prlctl", mock_prlctl): parallels.list_snapshots(name) - mock_prlctl.assert_called_once_with('snapshot-list', [name], runas=None) + mock_prlctl.assert_called_once_with("snapshot-list", [name], runas=None) # Validate listing all snapshots in tree mode mock_prlctl = MagicMock(return_value=guid_str) - with patch.object(parallels, 'prlctl', mock_prlctl): + with patch.object(parallels, "prlctl", mock_prlctl): parallels.list_snapshots(name, tree=True) - mock_prlctl.assert_called_once_with('snapshot-list', [name, '--tree'], runas=None) + mock_prlctl.assert_called_once_with( + "snapshot-list", [name, "--tree"], runas=None + ) # Validate listing a single snapshot - snap_name = 'muon' + snap_name = "muon" mock_snap_name = MagicMock(return_value=snap_name) - with patch.object(parallels, '_validate_snap_name', mock_snap_name): + with patch.object(parallels, "_validate_snap_name", mock_snap_name): mock_prlctl = MagicMock(return_value=guid_str) - with patch.object(parallels, 'prlctl', mock_prlctl): + with patch.object(parallels, "prlctl", mock_prlctl): parallels.list_snapshots(name, snap_name) - mock_prlctl.assert_called_once_with('snapshot-list', - [name, '--id', snap_name], - runas=None) + mock_prlctl.assert_called_once_with( + "snapshot-list", [name, "--id", snap_name], runas=None + ) # Validate listing snapshot ID and name pairs - snap_names = ['electron', 'muon', 'tauon'] + snap_names = ["electron", "muon", "tauon"] mock_snap_name = MagicMock(side_effect=snap_names) - with patch.object(parallels, 'snapshot_id_to_name', mock_snap_name): + with patch.object(parallels, "snapshot_id_to_name", mock_snap_name): mock_prlctl = MagicMock(return_value=guid_str) - with patch.object(parallels, 'prlctl', mock_prlctl): + with patch.object(parallels, "prlctl", mock_prlctl): ret = parallels.list_snapshots(name, names=True) for snap_name in snap_names: self.assertIn(snap_name, ret) - mock_prlctl.assert_called_once_with('snapshot-list', [name], runas=None) + mock_prlctl.assert_called_once_with("snapshot-list", [name], runas=None) def test_snapshot(self): - ''' + """ Test parallels.snapshot - ''' - name = 'macvm' + """ + name = "macvm" # Validate creating a snapshot - mock_snap = MagicMock(return_value='') - with patch.object(parallels, 'prlctl', mock_snap): + mock_snap = MagicMock(return_value="") + with patch.object(parallels, "prlctl", mock_snap): parallels.snapshot(name) - mock_snap.assert_called_once_with('snapshot', [name], runas=None) + mock_snap.assert_called_once_with("snapshot", [name], runas=None) # Validate creating a snapshot with a name - snap_name = 'h_0' - mock_snap_name = MagicMock(return_value='') - with patch.object(parallels, 'prlctl', mock_snap_name): + snap_name = "h_0" + mock_snap_name = MagicMock(return_value="") + with patch.object(parallels, "prlctl", mock_snap_name): parallels.snapshot(name, snap_name) - mock_snap_name.assert_called_once_with('snapshot', - [name, '--name', snap_name], - runas=None) + mock_snap_name.assert_called_once_with( + "snapshot", [name, "--name", snap_name], runas=None + ) # Validate creating a snapshot with a name and a description - snap_name = 'h_0' - snap_desc = textwrap.dedent('The ground state particle of the higgs ' - 'multiplet family of bosons') - mock_snap_name = MagicMock(return_value='') - with patch.object(parallels, 'prlctl', mock_snap_name): + snap_name = "h_0" + snap_desc = textwrap.dedent( + "The ground state particle of the higgs " "multiplet family of bosons" + ) + mock_snap_name = MagicMock(return_value="") + with patch.object(parallels, "prlctl", mock_snap_name): parallels.snapshot(name, snap_name, snap_desc) - mock_snap_name.assert_called_once_with('snapshot', - [name, - '--name', snap_name, - '--description', snap_desc], - runas=None) + mock_snap_name.assert_called_once_with( + "snapshot", + [name, "--name", snap_name, "--description", snap_desc], + runas=None, + ) def test_delete_snapshot(self): - ''' + """ Test parallels.delete_snapshot - ''' - delete_message = ('Delete the snapshot...\n' - 'The snapshot has been successfully deleted.') + """ + delete_message = ( + "Delete the snapshot...\n" "The snapshot has been successfully deleted." + ) # Validate single ID - name = 'macvm' - snap_name = 'kaon' - snap_id = 'c2eab062-a635-4ccd-b9ae-998370f898b5' + name = "macvm" + snap_name = "kaon" + snap_id = "c2eab062-a635-4ccd-b9ae-998370f898b5" mock_snap_name = MagicMock(return_value=snap_id) - with patch.object(parallels, '_validate_snap_name', mock_snap_name): + with patch.object(parallels, "_validate_snap_name", mock_snap_name): mock_delete = MagicMock(return_value=delete_message) - with patch.object(parallels, 'prlctl', mock_delete): + with patch.object(parallels, "prlctl", mock_delete): ret = parallels.delete_snapshot(name, snap_name) self.assertEqual(ret, delete_message) - mock_delete.assert_called_once_with('snapshot-delete', - [name, '--id', snap_id], - runas=None) + mock_delete.assert_called_once_with( + "snapshot-delete", [name, "--id", snap_id], runas=None + ) # Validate multiple IDs - name = 'macvm' - snap_name = 'higgs doublet' - snap_ids = ['c2eab062-a635-4ccd-b9ae-998370f898b5', - '8aca07c5-a0e1-4dcb-ba75-cb154d46d516'] + name = "macvm" + snap_name = "higgs doublet" + snap_ids = [ + "c2eab062-a635-4ccd-b9ae-998370f898b5", + "8aca07c5-a0e1-4dcb-ba75-cb154d46d516", + ] mock_snap_ids = MagicMock(return_value=snap_ids) - with patch.object(parallels, '_validate_snap_name', mock_snap_ids): + with patch.object(parallels, "_validate_snap_name", mock_snap_ids): mock_delete = MagicMock(return_value=delete_message) - with patch.object(parallels, 'prlctl', mock_delete): + with patch.object(parallels, "prlctl", mock_delete): ret = parallels.delete_snapshot(name, snap_name, all=True) - mock_ret = {snap_ids[0]: delete_message, - snap_ids[1]: delete_message} + mock_ret = {snap_ids[0]: delete_message, snap_ids[1]: delete_message} self.assertDictEqual(ret, mock_ret) - mock_delete.assert_any_call('snapshot-delete', - [name, '--id', snap_ids[0]], - runas=None) - mock_delete.assert_any_call('snapshot-delete', - [name, '--id', snap_ids[1]], - runas=None) + mock_delete.assert_any_call( + "snapshot-delete", [name, "--id", snap_ids[0]], runas=None + ) + mock_delete.assert_any_call( + "snapshot-delete", [name, "--id", snap_ids[1]], runas=None + ) def test_revert_snapshot(self): - ''' + """ Test parallels.revert_snapshot - ''' - name = 'macvm' - snap_name = 'k-bar' - snap_id = 'c2eab062-a635-4ccd-b9ae-998370f898b5' + """ + name = "macvm" + snap_name = "k-bar" + snap_id = "c2eab062-a635-4ccd-b9ae-998370f898b5" mock_snap_name = MagicMock(return_value=snap_id) - with patch.object(parallels, '_validate_snap_name', mock_snap_name): - mock_delete = MagicMock(return_value='') - with patch.object(parallels, 'prlctl', mock_delete): + with patch.object(parallels, "_validate_snap_name", mock_snap_name): + mock_delete = MagicMock(return_value="") + with patch.object(parallels, "prlctl", mock_delete): parallels.revert_snapshot(name, snap_name) - mock_delete.assert_called_once_with('snapshot-switch', - [name, '--id', snap_id], - runas=None) + mock_delete.assert_called_once_with( + "snapshot-switch", [name, "--id", snap_id], runas=None + ) diff --git a/tests/unit/modules/test_parted_partition.py b/tests/unit/modules/test_parted_partition.py index aad28298674..f9d95c8c7cf 100644 --- a/tests/unit/modules/test_parted_partition.py +++ b/tests/unit/modules/test_parted_partition.py @@ -1,38 +1,37 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Dave Rawks (dave@pandora.com) tests.unit.modules.parted_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin - -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +import salt.modules.parted_partition as parted # Import Salt libs from salt.exceptions import CommandExecutionError -import salt.modules.parted_partition as parted + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class PartedTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): self.cmdrun = MagicMock() self.cmdrun_stdout = MagicMock() - self.addCleanup(delattr, self, 'cmdrun') - self.addCleanup(delattr, self, 'cmdrun_stdout') + self.addCleanup(delattr, self, "cmdrun") + self.addCleanup(delattr, self, "cmdrun_stdout") return { parted: { - '__salt__': { - 'cmd.run': self.cmdrun, - 'cmd.run_stdout': self.cmdrun_stdout + "__salt__": { + "cmd.run": self.cmdrun, + "cmd.run_stdout": self.cmdrun_stdout, } } } @@ -40,69 +39,85 @@ class PartedTestCase(TestCase, LoaderModuleMockMixin): # Test __virtual__ function for module registration def test_virtual_bails_on_windows(self): - ''' + """ If running windows, __virtual__ shouldn't register module - ''' - with patch('salt.utils.platform.is_windows', lambda: True): + """ + with patch("salt.utils.platform.is_windows", lambda: True): ret = parted.__virtual__() - err = (False, 'The parted execution module failed to load Windows systems are not supported.') + err = ( + False, + "The parted execution module failed to load Windows systems are not supported.", + ) self.assertEqual(err, ret) def test_virtual_bails_without_parted(self): - ''' + """ If parted not in PATH, __virtual__ shouldn't register module - ''' - with patch('salt.utils.path.which', lambda exe: not exe == "parted"),\ - patch('salt.utils.platform.is_windows', return_value=False): + """ + with patch("salt.utils.path.which", lambda exe: not exe == "parted"), patch( + "salt.utils.platform.is_windows", return_value=False + ): ret = parted.__virtual__() - err = (False, 'The parted execution module failed to load parted binary is not in the path.') + err = ( + False, + "The parted execution module failed to load parted binary is not in the path.", + ) self.assertEqual(err, ret) def test_virtual_bails_without_lsblk(self): - ''' + """ If lsblk not in PATH, __virtual__ shouldn't register module - ''' - with patch('salt.utils.path.which', lambda exe: not exe == "lsblk"),\ - patch('salt.utils.platform.is_windows', return_value=False): + """ + with patch("salt.utils.path.which", lambda exe: not exe == "lsblk"), patch( + "salt.utils.platform.is_windows", return_value=False + ): ret = parted.__virtual__() - err = (False, 'The parted execution module failed to load lsblk binary is not in the path.') + err = ( + False, + "The parted execution module failed to load lsblk binary is not in the path.", + ) self.assertEqual(err, ret) def test_virtual_bails_without_partprobe(self): - ''' + """ If partprobe not in PATH, __virtual__ shouldn't register module - ''' - with patch('salt.utils.path.which', lambda exe: not exe == "partprobe"),\ - patch('salt.utils.platform.is_windows', return_value=False): + """ + with patch("salt.utils.path.which", lambda exe: not exe == "partprobe"), patch( + "salt.utils.platform.is_windows", return_value=False + ): ret = parted.__virtual__() - err = (False, 'The parted execution module failed to load partprobe binary is not in the path.') + err = ( + False, + "The parted execution module failed to load partprobe binary is not in the path.", + ) self.assertEqual(err, ret) def test_virtual(self): - ''' + """ On expected platform with correct utils in PATH, register "partition" module - ''' - with patch('salt.utils.platform.is_windows', lambda: False), \ - patch('salt.utils.path.which', lambda exe: exe in ('parted', 'lsblk', 'partprobe')): + """ + with patch("salt.utils.platform.is_windows", lambda: False), patch( + "salt.utils.path.which", lambda exe: exe in ("parted", "lsblk", "partprobe") + ): ret = parted.__virtual__() - expect = 'partition' + expect = "partition" self.assertEqual(ret, expect) # Test probe function def test_probe_wo_args(self): parted.probe() - self.cmdrun.assert_called_once_with('partprobe -- ') + self.cmdrun.assert_called_once_with("partprobe -- ") def test_probe_w_single_arg(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - parted.probe('/dev/sda') - self.cmdrun.assert_called_once_with('partprobe -- /dev/sda') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + parted.probe("/dev/sda") + self.cmdrun.assert_called_once_with("partprobe -- /dev/sda") def test_probe_w_multiple_args(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - parted.probe('/dev/sda', '/dev/sdb') - self.cmdrun.assert_called_once_with('partprobe -- /dev/sda /dev/sdb') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + parted.probe("/dev/sda", "/dev/sdb") + self.cmdrun.assert_called_once_with("partprobe -- /dev/sda /dev/sdb") # Test _list function @@ -110,41 +125,41 @@ class PartedTestCase(TestCase, LoaderModuleMockMixin): def parted_print_output(k): output = { "valid": ( - '''BYT;\n''' - '''/dev/sda:4000GB:scsi:512:512:gpt:AMCC 9650SE-24M DISK:;\n''' - '''1:17.4kB:150MB:150MB:ext3::boot;\n''' - '''2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n''' + """BYT;\n""" + """/dev/sda:4000GB:scsi:512:512:gpt:AMCC 9650SE-24M DISK:;\n""" + """1:17.4kB:150MB:150MB:ext3::boot;\n""" + """2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n""" ), "valid chs": ( - '''CHS;\n''' - '''/dev/sda:3133,0,2:scsi:512:512:gpt:AMCC 9650SE-24M DISK:;\n''' - '''1:0,0,34:2431,134,43:ext3::boot;\n''' - '''2:2431,134,44:2492,80,42:linux-swap(v1)::;\n''' + """CHS;\n""" + """/dev/sda:3133,0,2:scsi:512:512:gpt:AMCC 9650SE-24M DISK:;\n""" + """1:0,0,34:2431,134,43:ext3::boot;\n""" + """2:2431,134,44:2492,80,42:linux-swap(v1)::;\n""" ), "valid_legacy": ( - '''BYT;\n''' - '''/dev/sda:4000GB:scsi:512:512:gpt:AMCC 9650SE-24M DISK;\n''' - '''1:17.4kB:150MB:150MB:ext3::boot;\n''' - '''2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n''' + """BYT;\n""" + """/dev/sda:4000GB:scsi:512:512:gpt:AMCC 9650SE-24M DISK;\n""" + """1:17.4kB:150MB:150MB:ext3::boot;\n""" + """2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n""" ), - "empty": '', + "empty": "", "bad_label_info": ( - '''BYT;\n''' - '''badbadbadbad\n''' - '''1:17.4kB:150MB:150MB:ext3::boot;\n''' - '''2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n''' + """BYT;\n""" + """badbadbadbad\n""" + """1:17.4kB:150MB:150MB:ext3::boot;\n""" + """2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n""" ), "bad_header": ( - '''badbadbadbad\n''' - '''/dev/sda:4000GB:scsi:512:512:gpt:AMCC 9650SE-24M DISK:;\n''' - '''1:17.4kB:150MB:150MB:ext3::boot;\n''' - '''2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n''' + """badbadbadbad\n""" + """/dev/sda:4000GB:scsi:512:512:gpt:AMCC 9650SE-24M DISK:;\n""" + """1:17.4kB:150MB:150MB:ext3::boot;\n""" + """2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n""" ), "bad_partition": ( - '''BYT;\n''' - '''/dev/sda:4000GB:scsi:512:512:gpt:AMCC 9650SE-24M DISK:;\n''' - '''badbadbadbad\n''' - '''2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n''' + """BYT;\n""" + """/dev/sda:4000GB:scsi:512:512:gpt:AMCC 9650SE-24M DISK:;\n""" + """badbadbadbad\n""" + """2:3921GB:4000GB:79.3GB:linux-swap(v1)::;\n""" ), } return output[k] @@ -153,242 +168,274 @@ class PartedTestCase(TestCase, LoaderModuleMockMixin): self.assertRaises(TypeError, parted.list_) def test_list__empty_cmd_output(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('empty') - output = parted.list_('/dev/sda') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda print') - expected = {'info': {}, 'partitions': {}} + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("empty") + output = parted.list_("/dev/sda") + self.cmdrun_stdout.assert_called_once_with("parted -m -s /dev/sda print") + expected = {"info": {}, "partitions": {}} self.assertEqual(output, expected) def test_list__valid_unit_empty_cmd_output(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('empty') - output = parted.list_('/dev/sda', unit='s') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda unit s print') - expected = {'info': {}, 'partitions': {}} + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("empty") + output = parted.list_("/dev/sda", unit="s") + self.cmdrun_stdout.assert_called_once_with( + "parted -m -s /dev/sda unit s print" + ) + expected = {"info": {}, "partitions": {}} self.assertEqual(output, expected) def test_list__invalid_unit(self): - self.assertRaises(CommandExecutionError, parted.list_, '/dev/sda', - unit='badbadbad') + self.assertRaises( + CommandExecutionError, parted.list_, "/dev/sda", unit="badbadbad" + ) self.assertFalse(self.cmdrun.called) def test_list__bad_header(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('bad_header') - self.assertRaises(CommandExecutionError, parted.list_, '/dev/sda') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda print') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("bad_header") + self.assertRaises(CommandExecutionError, parted.list_, "/dev/sda") + self.cmdrun_stdout.assert_called_once_with("parted -m -s /dev/sda print") def test_list__bad_label_info(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('bad_label_info') - self.assertRaises(CommandExecutionError, parted.list_, '/dev/sda') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda print') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("bad_label_info") + self.assertRaises(CommandExecutionError, parted.list_, "/dev/sda") + self.cmdrun_stdout.assert_called_once_with("parted -m -s /dev/sda print") def test_list__bad_partition(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('bad_partition') - self.assertRaises(CommandExecutionError, parted.list_, '/dev/sda') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda print') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("bad_partition") + self.assertRaises(CommandExecutionError, parted.list_, "/dev/sda") + self.cmdrun_stdout.assert_called_once_with("parted -m -s /dev/sda print") def test_list__valid_cmd_output(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('valid') - output = parted.list_('/dev/sda') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda print') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("valid") + output = parted.list_("/dev/sda") + self.cmdrun_stdout.assert_called_once_with("parted -m -s /dev/sda print") expected = { - 'info': { - 'logical sector': '512', - 'physical sector': '512', - 'interface': 'scsi', - 'model': 'AMCC 9650SE-24M DISK', - 'disk': '/dev/sda', - 'disk flags': '', - 'partition table': 'gpt', - 'size': '4000GB' + "info": { + "logical sector": "512", + "physical sector": "512", + "interface": "scsi", + "model": "AMCC 9650SE-24M DISK", + "disk": "/dev/sda", + "disk flags": "", + "partition table": "gpt", + "size": "4000GB", + }, + "partitions": { + "1": { + "end": "150MB", + "number": "1", + "start": "17.4kB", + "file system": "ext3", + "flags": "boot", + "name": "", + "size": "150MB", + }, + "2": { + "end": "4000GB", + "number": "2", + "start": "3921GB", + "file system": "linux-swap(v1)", + "flags": "", + "name": "", + "size": "79.3GB", + }, }, - 'partitions': { - '1': { - 'end': '150MB', - 'number': '1', - 'start': '17.4kB', - 'file system': 'ext3', - 'flags': 'boot', - 'name': '', - 'size': '150MB'}, - '2': { - 'end': '4000GB', - 'number': '2', - 'start': '3921GB', - 'file system': 'linux-swap(v1)', - 'flags': '', - 'name': '', - 'size': '79.3GB' - } - } } self.assertEqual(output, expected) def test_list__valid_unit_valid_cmd_output(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('valid') - output = parted.list_('/dev/sda', unit='s') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda unit s print') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("valid") + output = parted.list_("/dev/sda", unit="s") + self.cmdrun_stdout.assert_called_once_with( + "parted -m -s /dev/sda unit s print" + ) expected = { - 'info': { - 'logical sector': '512', - 'physical sector': '512', - 'interface': 'scsi', - 'model': 'AMCC 9650SE-24M DISK', - 'disk': '/dev/sda', - 'disk flags': '', - 'partition table': 'gpt', - 'size': '4000GB' + "info": { + "logical sector": "512", + "physical sector": "512", + "interface": "scsi", + "model": "AMCC 9650SE-24M DISK", + "disk": "/dev/sda", + "disk flags": "", + "partition table": "gpt", + "size": "4000GB", + }, + "partitions": { + "1": { + "end": "150MB", + "number": "1", + "start": "17.4kB", + "file system": "ext3", + "flags": "boot", + "name": "", + "size": "150MB", + }, + "2": { + "end": "4000GB", + "number": "2", + "start": "3921GB", + "file system": "linux-swap(v1)", + "flags": "", + "name": "", + "size": "79.3GB", + }, }, - 'partitions': { - '1': { - 'end': '150MB', - 'number': '1', - 'start': '17.4kB', - 'file system': 'ext3', - 'flags': 'boot', - 'name': '', - 'size': '150MB'}, - '2': { - 'end': '4000GB', - 'number': '2', - 'start': '3921GB', - 'file system': 'linux-swap(v1)', - 'flags': '', - 'name': '', - 'size': '79.3GB' - } - } } self.assertEqual(output, expected) def test_list__valid_unit_chs_valid_cmd_output(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('valid chs') - output = parted.list_('/dev/sda', unit='chs') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda unit chs print') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("valid chs") + output = parted.list_("/dev/sda", unit="chs") + self.cmdrun_stdout.assert_called_once_with( + "parted -m -s /dev/sda unit chs print" + ) expected = { - 'info': { - 'logical sector': '512', - 'physical sector': '512', - 'interface': 'scsi', - 'model': 'AMCC 9650SE-24M DISK', - 'disk': '/dev/sda', - 'disk flags': '', - 'partition table': 'gpt', - 'size': '3133,0,2' + "info": { + "logical sector": "512", + "physical sector": "512", + "interface": "scsi", + "model": "AMCC 9650SE-24M DISK", + "disk": "/dev/sda", + "disk flags": "", + "partition table": "gpt", + "size": "3133,0,2", + }, + "partitions": { + "1": { + "end": "2431,134,43", + "number": "1", + "start": "0,0,34", + "file system": "ext3", + "flags": "boot", + "name": "", + }, + "2": { + "end": "2492,80,42", + "number": "2", + "start": "2431,134,44", + "file system": "linux-swap(v1)", + "flags": "", + "name": "", + }, }, - 'partitions': { - '1': { - 'end': '2431,134,43', - 'number': '1', - 'start': '0,0,34', - 'file system': 'ext3', - 'flags': 'boot', - 'name': ''}, - '2': { - 'end': '2492,80,42', - 'number': '2', - 'start': '2431,134,44', - 'file system': 'linux-swap(v1)', - 'flags': '', - 'name': ''} - } } self.assertEqual(output, expected) def test_list__valid_legacy_cmd_output(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('valid_legacy') - output = parted.list_('/dev/sda') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda print') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("valid_legacy") + output = parted.list_("/dev/sda") + self.cmdrun_stdout.assert_called_once_with("parted -m -s /dev/sda print") expected = { - 'info': { - 'logical sector': '512', - 'physical sector': '512', - 'interface': 'scsi', - 'model': 'AMCC 9650SE-24M DISK', - 'disk': '/dev/sda', - 'partition table': 'gpt', - 'size': '4000GB' + "info": { + "logical sector": "512", + "physical sector": "512", + "interface": "scsi", + "model": "AMCC 9650SE-24M DISK", + "disk": "/dev/sda", + "partition table": "gpt", + "size": "4000GB", + }, + "partitions": { + "1": { + "end": "150MB", + "number": "1", + "start": "17.4kB", + "file system": "ext3", + "flags": "boot", + "name": "", + "size": "150MB", + }, + "2": { + "end": "4000GB", + "number": "2", + "start": "3921GB", + "file system": "linux-swap(v1)", + "flags": "", + "name": "", + "size": "79.3GB", + }, }, - 'partitions': { - '1': { - 'end': '150MB', - 'number': '1', - 'start': '17.4kB', - 'file system': 'ext3', - 'flags': 'boot', - 'name': '', - 'size': '150MB'}, - '2': { - 'end': '4000GB', - 'number': '2', - 'start': '3921GB', - 'file system': 'linux-swap(v1)', - 'flags': '', - 'name': '', - 'size': '79.3GB' - } - } } self.assertEqual(output, expected) def test_list__valid_unit_valid_legacy_cmd_output(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun_stdout.return_value = self.parted_print_output('valid_legacy') - output = parted.list_('/dev/sda', unit='s') - self.cmdrun_stdout.assert_called_once_with('parted -m -s /dev/sda unit s print') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun_stdout.return_value = self.parted_print_output("valid_legacy") + output = parted.list_("/dev/sda", unit="s") + self.cmdrun_stdout.assert_called_once_with( + "parted -m -s /dev/sda unit s print" + ) expected = { - 'info': { - 'logical sector': '512', - 'physical sector': '512', - 'interface': 'scsi', - 'model': 'AMCC 9650SE-24M DISK', - 'disk': '/dev/sda', - 'partition table': 'gpt', - 'size': '4000GB' + "info": { + "logical sector": "512", + "physical sector": "512", + "interface": "scsi", + "model": "AMCC 9650SE-24M DISK", + "disk": "/dev/sda", + "partition table": "gpt", + "size": "4000GB", + }, + "partitions": { + "1": { + "end": "150MB", + "number": "1", + "start": "17.4kB", + "file system": "ext3", + "flags": "boot", + "name": "", + "size": "150MB", + }, + "2": { + "end": "4000GB", + "number": "2", + "start": "3921GB", + "file system": "linux-swap(v1)", + "flags": "", + "name": "", + "size": "79.3GB", + }, }, - 'partitions': { - '1': { - 'end': '150MB', - 'number': '1', - 'start': '17.4kB', - 'file system': 'ext3', - 'flags': 'boot', - 'name': '', - 'size': '150MB'}, - '2': { - 'end': '4000GB', - 'number': '2', - 'start': '3921GB', - 'file system': 'linux-swap(v1)', - 'flags': '', - 'name': '', - 'size': '79.3GB' - } - } } self.assertEqual(output, expected) + def test_btrfs_fstypes(self): + """Tests if we see btrfs as valid fs type""" + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + try: + parted.mkfs("/dev/foo", "btrfs") + except CommandExecutionError: + self.fail("Btrfs is not in the supported fstypes") + + def test_xfs_fstypes(self): + """Tests if we see xfs as valid fs type""" + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + try: + parted.mkfs("/dev/foo", "xfs") + except CommandExecutionError: + self.fail("XFS is not in the supported fstypes") + def test_disk_set(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun.return_value = '' - output = parted.disk_set('/dev/sda', 'pmbr_boot', 'on') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun.return_value = "" + output = parted.disk_set("/dev/sda", "pmbr_boot", "on") self.cmdrun.assert_called_once_with( - ['parted', '-m', '-s', '/dev/sda', 'disk_set', - 'pmbr_boot', 'on']) + ["parted", "-m", "-s", "/dev/sda", "disk_set", "pmbr_boot", "on"] + ) assert output == [] def test_disk_toggle(self): - with patch('salt.modules.parted_partition._validate_device', MagicMock()): - self.cmdrun.return_value = '' - output = parted.disk_toggle('/dev/sda', 'pmbr_boot') + with patch("salt.modules.parted_partition._validate_device", MagicMock()): + self.cmdrun.return_value = "" + output = parted.disk_toggle("/dev/sda", "pmbr_boot") self.cmdrun.assert_called_once_with( - ['parted', '-m', '-s', '/dev/sda', 'disk_toggle', 'pmbr_boot']) + ["parted", "-m", "-s", "/dev/sda", "disk_toggle", "pmbr_boot"] + ) assert output == [] diff --git a/tests/unit/modules/test_pdbedit.py b/tests/unit/modules/test_pdbedit.py index 07dedc2f5dd..90e2d7f4fcd 100644 --- a/tests/unit/modules/test_pdbedit.py +++ b/tests/unit/modules/test_pdbedit.py @@ -8,62 +8,67 @@ import salt.modules.pdbedit as pdbedit # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class PdbeditTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.pdbedit module - ''' + """ def setup_loader_modules(self): return {pdbedit: {}} def test_version(self): - ''' + """ Test salt.modules.__virtual__'s handling of pdbedit versions - ''' - mock_bad_ver = MagicMock(return_value='Ver 1.1a') - mock_old_ver = MagicMock(return_value='Version 1.0.0') - mock_exa_ver = MagicMock(return_value='Version 4.8.0') - mock_new_ver = MagicMock(return_value='Version 4.9.2') + """ + mock_bad_ver = MagicMock(return_value="Ver 1.1a") + mock_old_ver = MagicMock(return_value="Version 1.0.0") + mock_exa_ver = MagicMock(return_value="Version 4.8.0") + mock_new_ver = MagicMock(return_value="Version 4.9.2") # NOTE: no pdbedit installed - with patch('salt.utils.path.which', MagicMock(return_value=None)): + with patch("salt.utils.path.which", MagicMock(return_value=None)): ret = pdbedit.__virtual__() - self.assertEqual(ret, (False, 'pdbedit command is not available')) + self.assertEqual(ret, (False, "pdbedit command is not available")) # NOTE: pdbedit is not returning a valid version - with patch('salt.utils.path.which', MagicMock(return_value='/opt/local/bin/pdbedit')), \ - patch('salt.modules.cmdmod.run', mock_bad_ver): + with patch( + "salt.utils.path.which", MagicMock(return_value="/opt/local/bin/pdbedit") + ), patch("salt.modules.cmdmod.run", mock_bad_ver): ret = pdbedit.__virtual__() - self.assertEqual(ret, (False, 'pdbedit -V returned an unknown version format')) + self.assertEqual( + ret, (False, "pdbedit -V returned an unknown version format") + ) # NOTE: pdbedit is too old - with patch('salt.utils.path.which', MagicMock(return_value='/opt/local/bin/pdbedit')), \ - patch('salt.modules.cmdmod.run', mock_old_ver): + with patch( + "salt.utils.path.which", MagicMock(return_value="/opt/local/bin/pdbedit") + ), patch("salt.modules.cmdmod.run", mock_old_ver): ret = pdbedit.__virtual__() - self.assertEqual(ret, (False, 'pdbedit is to old, 4.8.0 or newer is required')) + self.assertEqual( + ret, (False, "pdbedit is to old, 4.8.0 or newer is required") + ) # NOTE: pdbedit is exactly 4.8.0 - with patch('salt.utils.path.which', MagicMock(return_value='/opt/local/bin/pdbedit')), \ - patch('salt.modules.cmdmod.run', mock_exa_ver): + with patch( + "salt.utils.path.which", MagicMock(return_value="/opt/local/bin/pdbedit") + ), patch("salt.modules.cmdmod.run", mock_exa_ver): ret = pdbedit.__virtual__() - self.assertEqual(ret, 'pdbedit') + self.assertEqual(ret, "pdbedit") # NOTE: pdbedit is newer than 4.8.0 - with patch('salt.utils.path.which', MagicMock(return_value='/opt/local/bin/pdbedit')), \ - patch('salt.modules.cmdmod.run', mock_new_ver): + with patch( + "salt.utils.path.which", MagicMock(return_value="/opt/local/bin/pdbedit") + ), patch("salt.modules.cmdmod.run", mock_new_ver): ret = pdbedit.__virtual__() - self.assertEqual(ret, 'pdbedit') + self.assertEqual(ret, "pdbedit") def test_generate_nt_hash(self): - ''' + """ Test salt.modules.pdbedit.generate_nt_hash - ''' - ret = pdbedit.generate_nt_hash('supersecret') - assert b'43239E3A0AF748020D5B426A4977D7E5' == ret + """ + ret = pdbedit.generate_nt_hash("supersecret") + assert b"43239E3A0AF748020D5B426A4977D7E5" == ret diff --git a/tests/unit/modules/test_pecl.py b/tests/unit/modules/test_pecl.py index d7b0e66865e..bbda947710d 100644 --- a/tests/unit/modules/test_pecl.py +++ b/tests/unit/modules/test_pecl.py @@ -1,54 +1,53 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, -) - # Import Salt Libs import salt.modules.pecl as pecl +from tests.support.mock import patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase class PeclTestCase(TestCase): - ''' + """ Test cases for salt.modules.pecl - ''' + """ + def test_install(self): - ''' + """ Test to installs one or several pecl extensions. - ''' - with patch.object(pecl, '_pecl', return_value='A'): - self.assertEqual(pecl.install('fuse', force=True), 'A') + """ + with patch.object(pecl, "_pecl", return_value="A"): + self.assertEqual(pecl.install("fuse", force=True), "A") - self.assertFalse(pecl.install('fuse')) + self.assertFalse(pecl.install("fuse")) - with patch.object(pecl, 'list_', return_value={'A': ['A', 'B']}): - self.assertTrue(pecl.install(['A', 'B'])) + with patch.object(pecl, "list_", return_value={"A": ["A", "B"]}): + self.assertTrue(pecl.install(["A", "B"])) def test_uninstall(self): - ''' + """ Test to uninstall one or several pecl extensions. - ''' - with patch.object(pecl, '_pecl', return_value='A'): - self.assertEqual(pecl.uninstall('fuse'), 'A') + """ + with patch.object(pecl, "_pecl", return_value="A"): + self.assertEqual(pecl.uninstall("fuse"), "A") def test_update(self): - ''' + """ Test to update one or several pecl extensions. - ''' - with patch.object(pecl, '_pecl', return_value='A'): - self.assertEqual(pecl.update('fuse'), 'A') + """ + with patch.object(pecl, "_pecl", return_value="A"): + self.assertEqual(pecl.update("fuse"), "A") def test_list_(self): - ''' + """ Test to list installed pecl extensions. - ''' - with patch.object(pecl, '_pecl', return_value='A\nB'): - self.assertDictEqual(pecl.list_('channel'), {}) + """ + with patch.object(pecl, "_pecl", return_value="A\nB"): + self.assertDictEqual(pecl.list_("channel"), {}) diff --git a/tests/unit/modules/test_pf.py b/tests/unit/modules/test_pf.py index 3b2306ac270..e6cebdf54ab 100644 --- a/tests/unit/modules/test_pf.py +++ b/tests/unit/modules/test_pf.py @@ -8,231 +8,251 @@ import salt.modules.pf as pf # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class PfTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ test modules.pf functions - ''' + """ + def setup_loader_modules(self): return {pf: {}} def test_enable_when_disabled(self): - ''' + """ Tests enabling pf when it's not enabled yet. - ''' + """ ret = {} - ret['stderr'] = 'pf enabled' - ret['retcode'] = 0 + ret["stderr"] = "pf enabled" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertTrue(pf.enable()['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertTrue(pf.enable()["changes"]) def test_enable_when_enabled(self): - ''' + """ Tests enabling pf when it already enabled. - ''' + """ ret = {} - ret['stderr'] = 'pfctl: pf already enabled' - ret['retcode'] = 1 + ret["stderr"] = "pfctl: pf already enabled" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertFalse(pf.enable()['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertFalse(pf.enable()["changes"]) def test_disable_when_enabled(self): - ''' + """ Tests disabling pf when it's enabled. - ''' + """ ret = {} - ret['stderr'] = 'pf disabled' - ret['retcode'] = 0 + ret["stderr"] = "pf disabled" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertTrue(pf.disable()['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertTrue(pf.disable()["changes"]) def test_disable_when_disabled(self): - ''' + """ Tests disabling pf when it already disabled. - ''' + """ ret = {} - ret['stderr'] = 'pfctl: pf not enabled' - ret['retcode'] = 1 + ret["stderr"] = "pfctl: pf not enabled" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertFalse(pf.disable()['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertFalse(pf.disable()["changes"]) def test_loglevel(self): - ''' + """ Tests setting a loglevel. - ''' + """ ret = {} - ret['retcode'] = 0 + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - res = pf.loglevel('crit') - mock_cmd.assert_called_once_with('pfctl -x crit', - output_loglevel='trace', python_shell=False) - self.assertTrue(res['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + res = pf.loglevel("crit") + mock_cmd.assert_called_once_with( + "pfctl -x crit", output_loglevel="trace", python_shell=False + ) + self.assertTrue(res["changes"]) def test_load(self): - ''' + """ Tests loading ruleset. - ''' + """ ret = {} - ret['retcode'] = 0 + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): res = pf.load() - mock_cmd.assert_called_once_with(['pfctl', '-f', '/etc/pf.conf'], - output_loglevel='trace', python_shell=False) - self.assertTrue(res['changes']) + mock_cmd.assert_called_once_with( + ["pfctl", "-f", "/etc/pf.conf"], + output_loglevel="trace", + python_shell=False, + ) + self.assertTrue(res["changes"]) def test_load_noop(self): - ''' + """ Tests evaluating but not actually loading ruleset. - ''' + """ ret = {} - ret['retcode'] = 0 + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): res = pf.load(noop=True) - mock_cmd.assert_called_once_with(['pfctl', '-f', '/etc/pf.conf', '-n'], - output_loglevel='trace', python_shell=False) - self.assertFalse(res['changes']) + mock_cmd.assert_called_once_with( + ["pfctl", "-f", "/etc/pf.conf", "-n"], + output_loglevel="trace", + python_shell=False, + ) + self.assertFalse(res["changes"]) def test_flush(self): - ''' + """ Tests a regular flush command. - ''' + """ ret = {} - ret['stderr'] = 'pf: statistics cleared' - ret['retcode'] = 0 + ret["stderr"] = "pf: statistics cleared" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - res = pf.flush('info') - mock_cmd.assert_called_once_with('pfctl -v -F info', - output_loglevel='trace', python_shell=False) - self.assertTrue(res['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + res = pf.flush("info") + mock_cmd.assert_called_once_with( + "pfctl -v -F info", output_loglevel="trace", python_shell=False + ) + self.assertTrue(res["changes"]) def test_flush_capital(self): - ''' + """ Tests a flush command starting with a capital letter. - ''' + """ ret = {} - ret['stderr'] = '2 tables cleared' - ret['retcode'] = 0 + ret["stderr"] = "2 tables cleared" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - res = pf.flush('tables') - mock_cmd.assert_called_once_with('pfctl -v -F Tables', - output_loglevel='trace', python_shell=False) - self.assertTrue(res['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + res = pf.flush("tables") + mock_cmd.assert_called_once_with( + "pfctl -v -F Tables", output_loglevel="trace", python_shell=False + ) + self.assertTrue(res["changes"]) def test_flush_without_changes(self): - ''' + """ Tests a flush command that has no changes. - ''' + """ ret = {} - ret['stderr'] = '0 tables cleared' - ret['retcode'] = 0 + ret["stderr"] = "0 tables cleared" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertFalse(pf.flush('tables')['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertFalse(pf.flush("tables")["changes"]) def test_table(self): - ''' + """ Tests a regular table command. - ''' + """ ret = {} - ret['stderr'] = '42 addresses deleted' - ret['retcode'] = 0 + ret["stderr"] = "42 addresses deleted" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertTrue(pf.table('flush', table='bad_hosts')['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertTrue(pf.table("flush", table="bad_hosts")["changes"]) def test_table_expire(self): - ''' + """ Tests the table expire command. - ''' + """ ret = {} - ret['stderr'] = '1/1 addresses expired.' - ret['retcode'] = 0 + ret["stderr"] = "1/1 addresses expired." + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertTrue(pf.table('expire', table='bad_hosts', number=300)['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertTrue( + pf.table("expire", table="bad_hosts", number=300)["changes"] + ) def test_table_add_addresses(self): - ''' + """ Tests adding addresses to a table. - ''' + """ ret = {} - ret['stderr'] = '2/2 addressess added.' - ret['retcode'] = 0 + ret["stderr"] = "2/2 addressess added." + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertTrue(pf.table('add', table='bad_hosts', addresses=['1.2.3.4', '5.6.7.8'])['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertTrue( + pf.table("add", table="bad_hosts", addresses=["1.2.3.4", "5.6.7.8"])[ + "changes" + ] + ) def test_table_test_address(self): - ''' + """ Tests testing addresses in a table. - ''' + """ ret = {} - ret['stderr'] = '1/2 addressess match.' - ret['retcode'] = 0 + ret["stderr"] = "1/2 addressess match." + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertTrue(pf.table('test', table='bad_hosts', addresses=['1.2.3.4'])['matches']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertTrue( + pf.table("test", table="bad_hosts", addresses=["1.2.3.4"])["matches"] + ) def test_table_no_changes(self): - ''' + """ Tests a table command that has no changes. - ''' + """ ret = {} - ret['stderr'] = '0/1 addresses expired.' - ret['retcode'] = 0 + ret["stderr"] = "0/1 addresses expired." + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertFalse(pf.table('expire', table='bad_hosts', number=300)['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertFalse( + pf.table("expire", table="bad_hosts", number=300)["changes"] + ) def test_table_show(self): - ''' + """ Tests showing table contents. - ''' + """ ret = {} - ret['stdout'] = '1.2.3.4\n5.6.7.8' - ret['retcode'] = 0 - expected = ['1.2.3.4', '5.6.7.8'] + ret["stdout"] = "1.2.3.4\n5.6.7.8" + ret["retcode"] = 0 + expected = ["1.2.3.4", "5.6.7.8"] mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertListEqual(pf.table('show', table='bad_hosts')['comment'], expected) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertListEqual( + pf.table("show", table="bad_hosts")["comment"], expected + ) def test_show(self): - ''' + """ Tests a regular show command. - ''' + """ ret = {} - ret['stdout'] = 'block return\npass' - ret['retcode'] = 0 - expected = ['block return', 'pass'] + ret["stdout"] = "block return\npass" + ret["retcode"] = 0 + expected = ["block return", "pass"] mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - self.assertListEqual(pf.show('rules')['comment'], expected) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + self.assertListEqual(pf.show("rules")["comment"], expected) def test_show_capital(self): - ''' + """ Tests a show command starting with a capital letter. - ''' + """ ret = {} - ret['stdout'] = 'bad_hosts' - ret['retcode'] = 0 + ret["stdout"] = "bad_hosts" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(pf.__salt__, {'cmd.run_all': mock_cmd}): - res = pf.show('tables') - mock_cmd.assert_called_once_with('pfctl -s Tables', - output_loglevel='trace', python_shell=False) - self.assertFalse(res['changes']) + with patch.dict(pf.__salt__, {"cmd.run_all": mock_cmd}): + res = pf.show("tables") + mock_cmd.assert_called_once_with( + "pfctl -s Tables", output_loglevel="trace", python_shell=False + ) + self.assertFalse(res["changes"]) diff --git a/tests/unit/modules/test_pillar.py b/tests/unit/modules/test_pillar.py index ef39b953ba0..1b89970f344 100644 --- a/tests/unit/modules/test_pillar.py +++ b/tests/unit/modules/test_pillar.py @@ -3,76 +3,75 @@ # Import Python libs from __future__ import absolute_import -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.pillar as pillarmod # Import Salt libs from salt.ext import six from salt.utils.odict import OrderedDict -import salt.modules.pillar as pillarmod +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase -pillar_value_1 = dict(a=1, b='very secret') +pillar_value_1 = dict(a=1, b="very secret") class PillarModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {pillarmod: {}} def test_obfuscate_inner_recursion(self): self.assertEqual( - pillarmod._obfuscate_inner(dict(a=[1, 2], - b=dict(pwd='secret', deeper=('a', 1)))), - dict(a=['<int>', '<int>'], - b=dict(pwd='<str>', deeper=('<str>', '<int>'))) + pillarmod._obfuscate_inner( + dict(a=[1, 2], b=dict(pwd="secret", deeper=("a", 1))) + ), + dict(a=["<int>", "<int>"], b=dict(pwd="<str>", deeper=("<str>", "<int>"))), ) def test_obfuscate_inner_more_types(self): - self.assertEqual(pillarmod._obfuscate_inner(OrderedDict([('key', 'value')])), - OrderedDict([('key', '<str>')])) + self.assertEqual( + pillarmod._obfuscate_inner(OrderedDict([("key", "value")])), + OrderedDict([("key", "<str>")]), + ) - self.assertEqual(pillarmod._obfuscate_inner(set((1, 2))), - set(['<int>'])) + self.assertEqual(pillarmod._obfuscate_inner(set((1, 2))), set(["<int>"])) - self.assertEqual(pillarmod._obfuscate_inner((1, 2)), - ('<int>', '<int>')) + self.assertEqual(pillarmod._obfuscate_inner((1, 2)), ("<int>", "<int>")) def test_obfuscate(self): - with patch('salt.modules.pillar.items', MagicMock(return_value=pillar_value_1)): - self.assertEqual(pillarmod.obfuscate(), - dict(a='<int>', b='<str>')) + with patch("salt.modules.pillar.items", MagicMock(return_value=pillar_value_1)): + self.assertEqual(pillarmod.obfuscate(), dict(a="<int>", b="<str>")) def test_ls(self): - with patch('salt.modules.pillar.items', MagicMock(return_value=pillar_value_1)): + with patch("salt.modules.pillar.items", MagicMock(return_value=pillar_value_1)): ls = sorted(pillarmod.ls()) if six.PY3: - self.assertCountEqual(ls, ['a', 'b']) + self.assertCountEqual(ls, ["a", "b"]) else: - self.assertEqual(ls, ['a', 'b']) + self.assertEqual(ls, ["a", "b"]) def test_pillar_get_default_merge(self): - defaults = {'int': 1, - 'string': 'foo', - 'list': ['foo'], - 'dict': {'foo': 'bar', 'subkey': {'foo': 'bar'}}} + defaults = { + "int": 1, + "string": "foo", + "list": ["foo"], + "dict": {"foo": "bar", "subkey": {"foo": "bar"}}, + } - pillar_mock = {'int': 2, - 'string': 'bar', - 'list': ['bar', 'baz'], - 'dict': {'baz': 'qux', 'subkey': {'baz': 'qux'}}} + pillar_mock = { + "int": 2, + "string": "bar", + "list": ["bar", "baz"], + "dict": {"baz": "qux", "subkey": {"baz": "qux"}}, + } # Test that we raise a KeyError when pillar_raise_on_missing is True - with patch.dict(pillarmod.__opts__, {'pillar_raise_on_missing': True}): - self.assertRaises(KeyError, pillarmod.get, 'missing') + with patch.dict(pillarmod.__opts__, {"pillar_raise_on_missing": True}): + self.assertRaises(KeyError, pillarmod.get, "missing") # Test that we return an empty string when it is not with patch.dict(pillarmod.__opts__, {}): - self.assertEqual(pillarmod.get('missing'), '') + self.assertEqual(pillarmod.get("missing"), "") with patch.dict(pillarmod.__pillar__, pillar_mock): # Test with no default passed (it should be KeyError) and merge=True. @@ -80,32 +79,33 @@ class PillarModuleTestCase(TestCase, LoaderModuleMockMixin): # should be returned. for item in pillarmod.__pillar__: self.assertEqual( - pillarmod.get(item, merge=True), - pillarmod.__pillar__[item] + pillarmod.get(item, merge=True), pillarmod.__pillar__[item] ) # Test merging when the type of the default value is not the same as # what was returned. Merging should be skipped and the value returned # from __pillar__ should be returned. for default_type in defaults: - for data_type in ('dict', 'list'): + for data_type in ("dict", "list"): if default_type == data_type: continue self.assertEqual( - pillarmod.get(data_type, default=defaults[default_type], merge=True), - pillarmod.__pillar__[data_type] + pillarmod.get( + data_type, default=defaults[default_type], merge=True + ), + pillarmod.__pillar__[data_type], ) # Test recursive dict merging self.assertEqual( - pillarmod.get('dict', default=defaults['dict'], merge=True), - {'foo': 'bar', 'baz': 'qux', 'subkey': {'foo': 'bar', 'baz': 'qux'}} + pillarmod.get("dict", default=defaults["dict"], merge=True), + {"foo": "bar", "baz": "qux", "subkey": {"foo": "bar", "baz": "qux"}}, ) # Test list merging self.assertEqual( - pillarmod.get('list', default=defaults['list'], merge=True), - ['foo', 'bar', 'baz'] + pillarmod.get("list", default=defaults["list"], merge=True), + ["foo", "bar", "baz"], ) def test_pillar_get_default_merge_regression_38558(self): @@ -115,31 +115,30 @@ class PillarModuleTestCase(TestCase, LoaderModuleMockMixin): See: https://github.com/saltstack/salt/issues/38558 """ - with patch.dict(pillarmod.__pillar__, {'l1': {'l2': {'l3': 42}}}): + with patch.dict(pillarmod.__pillar__, {"l1": {"l2": {"l3": 42}}}): - res = pillarmod.get(key='l1') - self.assertEqual({'l2': {'l3': 42}}, res) + res = pillarmod.get(key="l1") + self.assertEqual({"l2": {"l3": 42}}, res) - default = {'l2': {'l3': 43}} + default = {"l2": {"l3": 43}} - res = pillarmod.get(key='l1', default=default) - self.assertEqual({'l2': {'l3': 42}}, res) - self.assertEqual({'l2': {'l3': 43}}, default) + res = pillarmod.get(key="l1", default=default) + self.assertEqual({"l2": {"l3": 42}}, res) + self.assertEqual({"l2": {"l3": 43}}, default) - res = pillarmod.get(key='l1', default=default, merge=True) - self.assertEqual({'l2': {'l3': 42}}, res) - self.assertEqual({'l2': {'l3': 43}}, default) + res = pillarmod.get(key="l1", default=default, merge=True) + self.assertEqual({"l2": {"l3": 42}}, res) + self.assertEqual({"l2": {"l3": 43}}, default) def test_pillar_get_default_merge_regression_39062(self): - ''' + """ Confirm that we do not raise an exception if default is None and merge=True. See https://github.com/saltstack/salt/issues/39062 for more info. - ''' - with patch.dict(pillarmod.__pillar__, {'foo': 'bar'}): + """ + with patch.dict(pillarmod.__pillar__, {"foo": "bar"}): self.assertEqual( - pillarmod.get(key='foo', default=None, merge=True), - 'bar', + pillarmod.get(key="foo", default=None, merge=True), "bar", ) diff --git a/tests/unit/modules/test_pip.py b/tests/unit/modules/test_pip.py index d04885ae51e..818283c2c38 100644 --- a/tests/unit/modules/test_pip.py +++ b/tests/unit/modules/test_pip.py @@ -2,195 +2,200 @@ # Import python libs from __future__ import absolute_import + import os import sys -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +import salt.modules.pip as pip # Import salt libs import salt.utils.platform -import salt.modules.pip as pip from salt.exceptions import CommandExecutionError -import salt.utils.platform + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class PipTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - return {pip: {'__salt__': {'cmd.which_bin': lambda _: 'pip'}}} + return {pip: {"__salt__": {"cmd.which_bin": lambda _: "pip"}}} def test__pip_bin_env(self): - ret = pip._pip_bin_env(None, 'C:/Users/ch44d/Documents/salt/tests/pip.exe') + ret = pip._pip_bin_env(None, "C:/Users/ch44d/Documents/salt/tests/pip.exe") if salt.utils.platform.is_windows(): - self.assertEqual(ret, 'C:/Users/ch44d/Documents/salt/tests') + self.assertEqual(ret, "C:/Users/ch44d/Documents/salt/tests") else: - self.assertEqual(ret, None) + self.assertIsNone(ret) def test__pip_bin_env_no_change(self): - cwd = 'C:/Users/ch44d/Desktop' - ret = pip._pip_bin_env(cwd, 'C:/Users/ch44d/Documents/salt/tests/pip.exe') + cwd = "C:/Users/ch44d/Desktop" + ret = pip._pip_bin_env(cwd, "C:/Users/ch44d/Documents/salt/tests/pip.exe") self.assertEqual(ret, cwd) def test__pip_bin_env_no_bin_env(self): ret = pip._pip_bin_env(None, None) - self.assertEqual(None, None) + self.assertIsNone(ret) def test_fix4361(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(requirements='requirements.txt') - expected_cmd = [sys.executable, '-m', 'pip', 'install', '--requirement', 'requirements.txt'] + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(requirements="requirements.txt") + expected_cmd = [ + sys.executable, + "-m", + "pip", + "install", + "--requirement", + "requirements.txt", + ] mock.assert_called_with( expected_cmd, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_editable_without_egg_fails(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, pip.install, - editable='git+https://github.com/saltstack/salt-testing.git' + editable="git+https://github.com/saltstack/salt-testing.git", ) def test_install_multiple_editable(self): editables = [ - 'git+https://github.com/jek/blinker.git#egg=Blinker', - 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting' + "git+https://github.com/jek/blinker.git#egg=Blinker", + "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting", ] - expected = [sys.executable, '-m', 'pip', 'install'] + expected = [sys.executable, "-m", "pip", "install"] for item in editables: - expected.extend(['--editable', item]) + expected.extend(["--editable", item]) # Passing editables as a list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(editable=editables) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing editables as a comma separated list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(editable=','.join(editables)) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(editable=",".join(editables)) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_multiple_pkgs_and_editables(self): - pkgs = ['pep8', 'salt'] + pkgs = ["pep8", "salt"] editables = [ - 'git+https://github.com/jek/blinker.git#egg=Blinker', - 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting' + "git+https://github.com/jek/blinker.git#egg=Blinker", + "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting", ] - expected = [sys.executable, '-m', 'pip', 'install'] + expected = [sys.executable, "-m", "pip", "install"] expected.extend(pkgs) for item in editables: - expected.extend(['--editable', item]) + expected.extend(["--editable", item]) # Passing editables as a list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkgs=pkgs, editable=editables) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing editables as a comma separated list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(pkgs=','.join(pkgs), editable=','.join(editables)) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(pkgs=",".join(pkgs), editable=",".join(editables)) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # As single string (just use the first element from pkgs and editables) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkgs=pkgs[0], editable=editables[0]) - expected = [sys.executable, '-m', 'pip', 'install', pkgs[0], '--editable', editables[0]] + expected = [ + sys.executable, + "-m", + "pip", + "install", + pkgs[0], + "--editable", + editables[0], + ] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_issue5940_install_multiple_pip_mirrors(self): - ''' + """ test multiple pip mirrors. This test only works with pip < 7.0.0 - ''' - with patch.object(pip, 'version', MagicMock(return_value='1.4')): + """ + with patch.object(pip, "version", MagicMock(return_value="1.4")): mirrors = [ - 'http://g.pypi.python.org', - 'http://c.pypi.python.org', - 'http://pypi.crate.io' + "http://g.pypi.python.org", + "http://c.pypi.python.org", + "http://pypi.crate.io", ] - expected = [sys.executable, '-m', 'pip', 'install', '--use-mirrors'] + expected = [sys.executable, "-m", "pip", "install", "--use-mirrors"] for item in mirrors: - expected.extend(['--mirrors', item]) - expected.append('pep8') + expected.extend(["--mirrors", item]) + expected.append("pep8") # Passing mirrors as a list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(pkgs=['pep8'], mirrors=mirrors) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(pkgs=["pep8"], mirrors=mirrors) mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing mirrors as a comma separated list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(pkgs=['pep8'], mirrors=','.join(mirrors)) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(pkgs=["pep8"], mirrors=",".join(mirrors)) mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) - expected = [sys.executable, '-m', 'pip', 'install', '--use-mirrors', '--mirrors', mirrors[0], 'pep8'] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--use-mirrors", + "--mirrors", + mirrors[0], + "pep8", + ] # As single string (just use the first element from mirrors) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(pkgs=['pep8'], mirrors=mirrors[0]) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(pkgs=["pep8"], mirrors=mirrors[0]) mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, @@ -198,116 +203,123 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): def test_install_with_multiple_find_links(self): find_links = [ - 'http://g.pypi.python.org', - 'http://c.pypi.python.org', - 'http://pypi.crate.io' + "http://g.pypi.python.org", + "http://c.pypi.python.org", + "http://pypi.crate.io", ] - pkg = 'pep8' + pkg = "pep8" - expected = [sys.executable, '-m', 'pip', 'install'] + expected = [sys.executable, "-m", "pip", "install"] for item in find_links: - expected.extend(['--find-links', item]) + expected.extend(["--find-links", item]) expected.append(pkg) # Passing mirrors as a list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, find_links=find_links) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing mirrors as a comma separated list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(pkg, find_links=','.join(find_links)) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(pkg, find_links=",".join(find_links)) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Valid protos work? - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, find_links=find_links) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) - expected = [sys.executable, '-m', 'pip', 'install', '--find-links', find_links[0], pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--find-links", + find_links[0], + pkg, + ] # As single string (just use the first element from find_links) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, find_links=find_links[0]) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Invalid proto raises exception - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, pip.install, - '\'' + pkg + '\'', - find_links='sftp://pypi.crate.io' + "'" + pkg + "'", + find_links="sftp://pypi.crate.io", ) def test_install_no_index_with_index_url_or_extra_index_url_raises(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, - pip.install, no_index=True, index_url='http://foo.tld' + pip.install, + no_index=True, + index_url="http://foo.tld", ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, - pip.install, no_index=True, extra_index_url='http://foo.tld' + pip.install, + no_index=True, + extra_index_url="http://foo.tld", ) def test_install_failed_cached_requirements(self): - with patch('salt.modules.pip._get_cached_requirements') as get_cached_requirements: + with patch( + "salt.modules.pip._get_cached_requirements" + ) as get_cached_requirements: get_cached_requirements.return_value = False - ret = pip.install(requirements='salt://my_test_reqs') - self.assertEqual(False, ret['result']) - self.assertIn('my_test_reqs', ret['comment']) + ret = pip.install(requirements="salt://my_test_reqs") + self.assertEqual(False, ret["result"]) + self.assertIn("my_test_reqs", ret["comment"]) def test_install_cached_requirements_used(self): - with patch('salt.modules.pip._get_cached_requirements') as get_cached_requirements: - get_cached_requirements.return_value = 'my_cached_reqs' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(requirements='salt://requirements.txt') - expected = [sys.executable, '-m', 'pip', 'install', '--requirement', 'my_cached_reqs'] + with patch( + "salt.modules.pip._get_cached_requirements" + ) as get_cached_requirements: + get_cached_requirements.return_value = "my_cached_reqs" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(requirements="salt://requirements.txt") + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--requirement", + "my_cached_reqs", + ] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_venv(self): - with patch('os.path') as mock_path: + with patch("os.path") as mock_path: def join(*args): return os.path.normpath(os.sep.join(args)) @@ -317,523 +329,514 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): mock_path.join = join if salt.utils.platform.is_windows(): - venv_path = 'C:\\test_env' - bin_path = os.path.join(venv_path, 'python.exe') + venv_path = "C:\\test_env" + bin_path = os.path.join(venv_path, "python.exe") else: - venv_path = '/test_env' - bin_path = os.path.join(venv_path, 'python') + venv_path = "/test_env" + bin_path = os.path.join(venv_path, "python") - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_bin = MagicMock(return_value=[bin_path, '-m', 'pip']) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_bin = MagicMock(return_value=[bin_path, "-m", "pip"]) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}), \ - patch.object(pip, '_get_pip_bin', pip_bin): - pip.install('mock', bin_env=venv_path) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}), patch.object( + pip, "_get_pip_bin", pip_bin + ): + pip.install("mock", bin_env=venv_path) mock.assert_called_with( - [bin_path, '-m', 'pip', 'install', 'mock'], - env={'VIRTUAL_ENV': venv_path}, - saltenv='base', + [bin_path, "-m", "pip", "install", "mock"], + env={"VIRTUAL_ENV": venv_path}, + saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_log_argument_in_resulting_command(self): - with patch('os.access') as mock_path: - pkg = 'pep8' - log_path = '/tmp/pip-install.log' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + with patch("os.access") as mock_path: + pkg = "pep8" + log_path = "/tmp/pip-install.log" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, log=log_path) - expected = [sys.executable, '-m', 'pip', 'install', '--log', log_path, pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--log", + log_path, + pkg, + ] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_non_writeable_log(self): - with patch('os.path') as mock_path: + with patch("os.path") as mock_path: # Let's fake a non-writable log file - pkg = 'pep8' - log_path = '/tmp/pip-install.log' - mock_path.exists.side_effect = IOError('Fooo!') - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - self.assertRaises( - IOError, - pip.install, - pkg, - log=log_path - ) + pkg = "pep8" + log_path = "/tmp/pip-install.log" + mock_path.exists.side_effect = IOError("Fooo!") + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + self.assertRaises(IOError, pip.install, pkg, log=log_path) def test_install_timeout_argument_in_resulting_command(self): # Passing an int - pkg = 'pep8' - expected = [sys.executable, '-m', 'pip', 'install', '--timeout'] - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + expected = [sys.executable, "-m", "pip", "install", "--timeout"] + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, timeout=10) mock.assert_called_with( expected + [10, pkg], - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing an int as a string - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(pkg, timeout='10') + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(pkg, timeout="10") mock.assert_called_with( - expected + ['10', pkg], - saltenv='base', + expected + ["10", pkg], + saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing a non-int to timeout - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - self.assertRaises( - ValueError, - pip.install, - pkg, - timeout='a' - ) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + self.assertRaises(ValueError, pip.install, pkg, timeout="a") def test_install_index_url_argument_in_resulting_command(self): - pkg = 'pep8' - index_url = 'http://foo.tld' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + index_url = "http://foo.tld" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, index_url=index_url) - expected = [sys.executable, '-m', 'pip', 'install', '--index-url', index_url, pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--index-url", + index_url, + pkg, + ] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_extra_index_url_argument_in_resulting_command(self): - pkg = 'pep8' - extra_index_url = 'http://foo.tld' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + extra_index_url = "http://foo.tld" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, extra_index_url=extra_index_url) - expected = [sys.executable, '-m', 'pip', 'install', '--extra-index-url', extra_index_url, pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--extra-index-url", + extra_index_url, + pkg, + ] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_no_index_argument_in_resulting_command(self): - pkg = 'pep8' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, no_index=True) - expected = [sys.executable, '-m', 'pip', 'install', '--no-index', pkg] + expected = [sys.executable, "-m", "pip", "install", "--no-index", pkg] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_build_argument_in_resulting_command(self): - pkg = 'pep8' - build = '/tmp/foo' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + build = "/tmp/foo" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, build=build) - expected = [sys.executable, '-m', 'pip', 'install', '--build', build, pkg] + expected = [sys.executable, "-m", "pip", "install", "--build", build, pkg] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_target_argument_in_resulting_command(self): - pkg = 'pep8' - target = '/tmp/foo' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + target = "/tmp/foo" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, target=target) - expected = [sys.executable, '-m', 'pip', 'install', '--target', target, pkg] + expected = [sys.executable, "-m", "pip", "install", "--target", target, pkg] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_download_argument_in_resulting_command(self): - pkg = 'pep8' - download = '/tmp/foo' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + download = "/tmp/foo" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, download=download) - expected = [sys.executable, '-m', 'pip', 'install', '--download', download, pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--download", + download, + pkg, + ] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_no_download_argument_in_resulting_command(self): - pkg = 'pep8' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, no_download=True) - expected = [sys.executable, '-m', 'pip', 'install', '--no-download', pkg] + expected = [sys.executable, "-m", "pip", "install", "--no-download", pkg] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_download_cache_dir_arguments_in_resulting_command(self): - pkg = 'pep8' + pkg = "pep8" cache_dir_arg_mapping = { - '1.5.6': '--download-cache', - '6.0': '--cache-dir', + "1.5.6": "--download-cache", + "6.0": "--cache-dir", } - download_cache = '/tmp/foo' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + download_cache = "/tmp/foo" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): for pip_version, cmd_arg in cache_dir_arg_mapping.items(): - with patch('salt.modules.pip.version', - MagicMock(return_value=pip_version)): + with patch( + "salt.modules.pip.version", MagicMock(return_value=pip_version) + ): # test `download_cache` kwarg - pip.install(pkg, download_cache='/tmp/foo') - expected = [sys.executable, '-m', 'pip', 'install', cmd_arg, download_cache, pkg] + pip.install(pkg, download_cache="/tmp/foo") + expected = [ + sys.executable, + "-m", + "pip", + "install", + cmd_arg, + download_cache, + pkg, + ] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) # test `cache_dir` kwarg - pip.install(pkg, cache_dir='/tmp/foo') + pip.install(pkg, cache_dir="/tmp/foo") mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_source_argument_in_resulting_command(self): - pkg = 'pep8' - source = '/tmp/foo' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + source = "/tmp/foo" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, source=source) - expected = [sys.executable, '-m', 'pip', 'install', '--source', source, pkg] + expected = [sys.executable, "-m", "pip", "install", "--source", source, pkg] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_exists_action_argument_in_resulting_command(self): - pkg = 'pep8' - for action in ('s', 'i', 'w', 'b'): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + for action in ("s", "i", "w", "b"): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, exists_action=action) - expected = [sys.executable, '-m', 'pip', 'install', '--exists-action', action, pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--exists-action", + action, + pkg, + ] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Test for invalid action - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): self.assertRaises( - CommandExecutionError, - pip.install, - pkg, - exists_action='d' + CommandExecutionError, pip.install, pkg, exists_action="d" ) def test_install_install_options_argument_in_resulting_command(self): - install_options = [ - '--exec-prefix=/foo/bar', - '--install-scripts=/foo/bar/bin' - ] - pkg = 'pep8' + install_options = ["--exec-prefix=/foo/bar", "--install-scripts=/foo/bar/bin"] + pkg = "pep8" - expected = [sys.executable, '-m', 'pip', 'install'] + expected = [sys.executable, "-m", "pip", "install"] for item in install_options: - expected.extend(['--install-option', item]) + expected.extend(["--install-option", item]) expected.append(pkg) # Passing options as a list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, install_options=install_options) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing mirrors as a comma separated list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(pkg, install_options=','.join(install_options)) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(pkg, install_options=",".join(install_options)) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing mirrors as a single string entry - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, install_options=install_options[0]) - expected = [sys.executable, '-m', 'pip', 'install', '--install-option', install_options[0], pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--install-option", + install_options[0], + pkg, + ] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_global_options_argument_in_resulting_command(self): - global_options = [ - '--quiet', - '--no-user-cfg' - ] - pkg = 'pep8' + global_options = ["--quiet", "--no-user-cfg"] + pkg = "pep8" - expected = [sys.executable, '-m', 'pip', 'install'] + expected = [sys.executable, "-m", "pip", "install"] for item in global_options: - expected.extend(['--global-option', item]) + expected.extend(["--global-option", item]) expected.append(pkg) # Passing options as a list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, global_options=global_options) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing mirrors as a comma separated list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(pkg, global_options=','.join(global_options)) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(pkg, global_options=",".join(global_options)) mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing mirrors as a single string entry - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, global_options=global_options[0]) - expected = [sys.executable, '-m', 'pip', 'install', '--global-option', global_options[0], pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--global-option", + global_options[0], + pkg, + ] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_upgrade_argument_in_resulting_command(self): - pkg = 'pep8' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, upgrade=True) - expected = [sys.executable, '-m', 'pip', 'install', '--upgrade', pkg] + expected = [sys.executable, "-m", "pip", "install", "--upgrade", pkg] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_force_reinstall_argument_in_resulting_command(self): - pkg = 'pep8' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, force_reinstall=True) - expected = [sys.executable, '-m', 'pip', 'install', '--force-reinstall', pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--force-reinstall", + pkg, + ] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_ignore_installed_argument_in_resulting_command(self): - pkg = 'pep8' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, ignore_installed=True) - expected = [sys.executable, '-m', 'pip', 'install', '--ignore-installed', pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--ignore-installed", + pkg, + ] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_no_deps_argument_in_resulting_command(self): - pkg = 'pep8' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, no_deps=True) - expected = [sys.executable, '-m', 'pip', 'install', '--no-deps', pkg] + expected = [sys.executable, "-m", "pip", "install", "--no-deps", pkg] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_no_install_argument_in_resulting_command(self): - pkg = 'pep8' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, no_install=True) - expected = [sys.executable, '-m', 'pip', 'install', '--no-install', pkg] + expected = [sys.executable, "-m", "pip", "install", "--no-install", pkg] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_proxy_argument_in_resulting_command(self): - pkg = 'pep8' - proxy = 'salt-user:salt-passwd@salt-proxy:3128' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + proxy = "salt-user:salt-passwd@salt-proxy:3128" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, proxy=proxy) - expected = [sys.executable, '-m', 'pip', 'install', '--proxy', proxy, pkg] + expected = [sys.executable, "-m", "pip", "install", "--proxy", proxy, pkg] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_proxy_false_argument_in_resulting_command(self): - ''' + """ Checking that there is no proxy set if proxy arg is set to False even if the global proxy is set. - ''' - pkg = 'pep8' + """ + pkg = "pep8" proxy = False - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - config_mock = {'proxy_host': 'salt-proxy', - 'proxy_port': '3128', - 'proxy_username': 'salt-user', - 'proxy_password': 'salt-passwd'} - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + config_mock = { + "proxy_host": "salt-proxy", + "proxy_port": "3128", + "proxy_username": "salt-user", + "proxy_password": "salt-passwd", + } + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): with patch.dict(pip.__opts__, config_mock): pip.install(pkg, proxy=proxy) - expected = [sys.executable, '-m', 'pip', 'install', pkg] + expected = [sys.executable, "-m", "pip", "install", pkg] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_global_proxy_in_resulting_command(self): - ''' + """ Checking that there is proxy set if global proxy is set. - ''' - pkg = 'pep8' - proxy = 'http://salt-user:salt-passwd@salt-proxy:3128' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - config_mock = {'proxy_host': 'salt-proxy', - 'proxy_port': '3128', - 'proxy_username': 'salt-user', - 'proxy_password': 'salt-passwd'} - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + """ + pkg = "pep8" + proxy = "http://salt-user:salt-passwd@salt-proxy:3128" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + config_mock = { + "proxy_host": "salt-proxy", + "proxy_port": "3128", + "proxy_username": "salt-user", + "proxy_password": "salt-passwd", + } + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): with patch.dict(pip.__opts__, config_mock): pip.install(pkg) - expected = [sys.executable, '-m', 'pip', 'install', '--proxy', proxy, pkg] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--proxy", + proxy, + pkg, + ] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_multiple_requirements_arguments_in_resulting_command(self): - with patch('salt.modules.pip._get_cached_requirements') as get_cached_requirements: - cached_reqs = [ - 'my_cached_reqs-1', 'my_cached_reqs-2' - ] + with patch( + "salt.modules.pip._get_cached_requirements" + ) as get_cached_requirements: + cached_reqs = ["my_cached_reqs-1", "my_cached_reqs-2"] get_cached_requirements.side_effect = cached_reqs - requirements = [ - 'salt://requirements-1.txt', 'salt://requirements-2.txt' - ] + requirements = ["salt://requirements-1.txt", "salt://requirements-2.txt"] - expected = [sys.executable, '-m', 'pip', 'install'] + expected = [sys.executable, "-m", "pip", "install"] for item in cached_reqs: - expected.extend(['--requirement', item]) + expected.extend(["--requirement", item]) # Passing option as a list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(requirements=requirements) mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, @@ -841,12 +844,12 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): # Passing option as a comma separated list get_cached_requirements.side_effect = cached_reqs - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(requirements=','.join(requirements)) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install(requirements=",".join(requirements)) mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, @@ -854,75 +857,85 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): # Passing option as a single string entry get_cached_requirements.side_effect = [cached_reqs[0]] - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(requirements=requirements[0]) - expected = [sys.executable, '-m', 'pip', 'install', '--requirement', cached_reqs[0]] + expected = [ + sys.executable, + "-m", + "pip", + "install", + "--requirement", + cached_reqs[0], + ] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_extra_args_arguments_in_resulting_command(self): - pkg = 'pep8' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.install(pkg, extra_args=[ - {"--latest-pip-kwarg": "param"}, - "--latest-pip-arg" - ]) + pkg = "pep8" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.install( + pkg, extra_args=[{"--latest-pip-kwarg": "param"}, "--latest-pip-arg"] + ) expected = [ - sys.executable, '-m', 'pip', 'install', pkg, - "--latest-pip-kwarg", "param", "--latest-pip-arg" + sys.executable, + "-m", + "pip", + "install", + pkg, + "--latest-pip-kwarg", + "param", + "--latest-pip-arg", ] mock.assert_called_with( - expected, - saltenv='base', - runas=None, - use_vt=False, - python_shell=False, + expected, saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_install_extra_args_arguments_recursion_error(self): - pkg = 'pep8' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + pkg = "pep8" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): - self.assertRaises(TypeError, lambda: pip.install( - pkg, extra_args=[ - {"--latest-pip-kwarg": ["param1", "param2"]}, - ])) + self.assertRaises( + TypeError, + lambda: pip.install( + pkg, extra_args=[{"--latest-pip-kwarg": ["param1", "param2"]}] + ), + ) - self.assertRaises(TypeError, lambda: pip.install( - pkg, extra_args=[ - {"--latest-pip-kwarg": [{"--too-deep": dict()}]}, - ])) + self.assertRaises( + TypeError, + lambda: pip.install( + pkg, extra_args=[{"--latest-pip-kwarg": [{"--too-deep": dict()}]}] + ), + ) def test_uninstall_multiple_requirements_arguments_in_resulting_command(self): - with patch('salt.modules.pip._get_cached_requirements') as get_cached_requirements: - cached_reqs = [ - 'my_cached_reqs-1', 'my_cached_reqs-2' - ] + with patch( + "salt.modules.pip._get_cached_requirements" + ) as get_cached_requirements: + cached_reqs = ["my_cached_reqs-1", "my_cached_reqs-2"] get_cached_requirements.side_effect = cached_reqs - requirements = [ - 'salt://requirements-1.txt', 'salt://requirements-2.txt' - ] + requirements = ["salt://requirements-1.txt", "salt://requirements-2.txt"] - expected = [sys.executable, '-m', 'pip', 'uninstall', '-y'] + expected = [sys.executable, "-m", "pip", "uninstall", "-y"] for item in cached_reqs: - expected.extend(['--requirement', item]) + expected.extend(["--requirement", item]) # Passing option as a list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.uninstall(requirements=requirements) mock.assert_called_with( expected, cwd=None, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, @@ -930,13 +943,13 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): # Passing option as a comma separated list get_cached_requirements.side_effect = cached_reqs - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.uninstall(requirements=','.join(requirements)) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.uninstall(requirements=",".join(requirements)) mock.assert_called_with( expected, cwd=None, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, @@ -944,37 +957,56 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): # Passing option as a single string entry get_cached_requirements.side_effect = [cached_reqs[0]] - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.uninstall(requirements=requirements[0]) - expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--requirement', cached_reqs[0]] + expected = [ + sys.executable, + "-m", + "pip", + "uninstall", + "-y", + "--requirement", + cached_reqs[0], + ] mock.assert_called_with( expected, cwd=None, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) def test_uninstall_global_proxy_in_resulting_command(self): - ''' + """ Checking that there is proxy set if global proxy is set. - ''' - pkg = 'pep8' - proxy = 'http://salt-user:salt-passwd@salt-proxy:3128' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - config_mock = {'proxy_host': 'salt-proxy', - 'proxy_port': '3128', - 'proxy_username': 'salt-user', - 'proxy_password': 'salt-passwd'} - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + """ + pkg = "pep8" + proxy = "http://salt-user:salt-passwd@salt-proxy:3128" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + config_mock = { + "proxy_host": "salt-proxy", + "proxy_port": "3128", + "proxy_username": "salt-user", + "proxy_password": "salt-passwd", + } + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): with patch.dict(pip.__opts__, config_mock): pip.uninstall(pkg) - expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--proxy', proxy, pkg] + expected = [ + sys.executable, + "-m", + "pip", + "uninstall", + "-y", + "--proxy", + proxy, + pkg, + ] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", cwd=None, runas=None, use_vt=False, @@ -982,24 +1014,26 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): ) def test_uninstall_proxy_false_argument_in_resulting_command(self): - ''' + """ Checking that there is no proxy set if proxy arg is set to False even if the global proxy is set. - ''' - pkg = 'pep8' + """ + pkg = "pep8" proxy = False - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - config_mock = {'proxy_host': 'salt-proxy', - 'proxy_port': '3128', - 'proxy_username': 'salt-user', - 'proxy_password': 'salt-passwd'} - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + config_mock = { + "proxy_host": "salt-proxy", + "proxy_port": "3128", + "proxy_username": "salt-user", + "proxy_password": "salt-passwd", + } + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): with patch.dict(pip.__opts__, config_mock): pip.uninstall(pkg, proxy=proxy) - expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', pkg] + expected = [sys.executable, "-m", "pip", "uninstall", "-y", pkg] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", cwd=None, runas=None, use_vt=False, @@ -1007,16 +1041,25 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): ) def test_uninstall_log_argument_in_resulting_command(self): - pkg = 'pep8' - log_path = '/tmp/pip-install.log' + pkg = "pep8" + log_path = "/tmp/pip-install.log" - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.uninstall(pkg, log=log_path) - expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--log', log_path, pkg] + expected = [ + sys.executable, + "-m", + "pip", + "uninstall", + "-y", + "--log", + log_path, + pkg, + ] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", cwd=None, runas=None, use_vt=False, @@ -1024,94 +1067,68 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): ) # Let's fake a non-writable log file - with patch('os.path') as mock_path: - mock_path.exists.side_effect = IOError('Fooo!') - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - self.assertRaises( - IOError, - pip.uninstall, - pkg, - log=log_path - ) + with patch("os.path") as mock_path: + mock_path.exists.side_effect = IOError("Fooo!") + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + self.assertRaises(IOError, pip.uninstall, pkg, log=log_path) def test_uninstall_timeout_argument_in_resulting_command(self): - pkg = 'pep8' - expected = [sys.executable, '-m', 'pip', 'uninstall', '-y', '--timeout'] + pkg = "pep8" + expected = [sys.executable, "-m", "pip", "uninstall", "-y", "--timeout"] # Passing an int - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.uninstall(pkg, timeout=10) mock.assert_called_with( expected + [10, pkg], cwd=None, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing an int as a string - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - pip.uninstall(pkg, timeout='10') + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + pip.uninstall(pkg, timeout="10") mock.assert_called_with( - expected + ['10', pkg], + expected + ["10", pkg], cwd=None, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) # Passing a non-int to timeout - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - self.assertRaises( - ValueError, - pip.uninstall, - pkg, - timeout='a' - ) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + self.assertRaises(ValueError, pip.uninstall, pkg, timeout="a") def test_freeze_command(self): - expected = [sys.executable, '-m', 'pip', 'freeze'] + expected = [sys.executable, "-m", "pip", "freeze"] eggs = [ - 'M2Crypto==0.21.1', - '-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev', - 'bbfreeze==1.1.0', - 'bbfreeze-loader==1.1.0', - 'pycrypto==2.6' + "M2Crypto==0.21.1", + "-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev", + "bbfreeze==1.1.0", + "bbfreeze-loader==1.1.0", + "pycrypto==2.6", ] - mock = MagicMock( - return_value={ - 'retcode': 0, - 'stdout': '\n'.join(eggs) - } - ) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='6.1.1')): + mock = MagicMock(return_value={"retcode": 0, "stdout": "\n".join(eggs)}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="6.1.1")): ret = pip.freeze() mock.assert_called_with( - expected, - cwd=None, - runas=None, - use_vt=False, - python_shell=False, + expected, cwd=None, runas=None, use_vt=False, python_shell=False, ) self.assertEqual(ret, eggs) - mock = MagicMock( - return_value={ - 'retcode': 0, - 'stdout': '\n'.join(eggs) - } - ) + mock = MagicMock(return_value={"retcode": 0, "stdout": "\n".join(eggs)}) # Passing env_vars passes them to underlying command? - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='6.1.1')): + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="6.1.1")): ret = pip.freeze(env_vars={"foo": "bar"}) mock.assert_called_with( expected, @@ -1119,262 +1136,216 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): runas=None, use_vt=False, python_shell=False, - env={"foo": "bar"} + env={"foo": "bar"}, ) self.assertEqual(ret, eggs) # Non zero returncode raises exception? - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'CABOOOOMMM!'}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='6.1.1')): + mock = MagicMock(return_value={"retcode": 1, "stderr": "CABOOOOMMM!"}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="6.1.1")): self.assertRaises( - CommandExecutionError, - pip.freeze, + CommandExecutionError, pip.freeze, ) def test_freeze_command_with_all(self): eggs = [ - 'M2Crypto==0.21.1', - '-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev', - 'bbfreeze==1.1.0', - 'bbfreeze-loader==1.1.0', - 'pip==0.9.1', - 'pycrypto==2.6', - 'setuptools==20.10.1' + "M2Crypto==0.21.1", + "-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev", + "bbfreeze==1.1.0", + "bbfreeze-loader==1.1.0", + "pip==0.9.1", + "pycrypto==2.6", + "setuptools==20.10.1", ] - mock = MagicMock( - return_value={ - 'retcode': 0, - 'stdout': '\n'.join(eggs) - } - ) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='9.0.1')): + mock = MagicMock(return_value={"retcode": 0, "stdout": "\n".join(eggs)}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="9.0.1")): ret = pip.freeze() - expected = [sys.executable, '-m', 'pip', 'freeze', '--all'] + expected = [sys.executable, "-m", "pip", "freeze", "--all"] mock.assert_called_with( - expected, - cwd=None, - runas=None, - use_vt=False, - python_shell=False, + expected, cwd=None, runas=None, use_vt=False, python_shell=False, ) self.assertEqual(ret, eggs) # Non zero returncode raises exception? - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'CABOOOOMMM!'}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='9.0.1')): + mock = MagicMock(return_value={"retcode": 1, "stderr": "CABOOOOMMM!"}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="9.0.1")): self.assertRaises( - CommandExecutionError, - pip.freeze, + CommandExecutionError, pip.freeze, ) def test_list_command(self): eggs = [ - 'M2Crypto==0.21.1', - '-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev', - 'bbfreeze==1.1.0', - 'bbfreeze-loader==1.1.0', - 'pycrypto==2.6' + "M2Crypto==0.21.1", + "-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev", + "bbfreeze==1.1.0", + "bbfreeze-loader==1.1.0", + "pycrypto==2.6", ] - mock_version = '6.1.1' - mock = MagicMock(return_value={'retcode': 0, 'stdout': '\n'.join(eggs)}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value=mock_version)): + mock_version = "6.1.1" + mock = MagicMock(return_value={"retcode": 0, "stdout": "\n".join(eggs)}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch( + "salt.modules.pip.version", MagicMock(return_value=mock_version) + ): ret = pip.list_() - expected = [sys.executable, '-m', 'pip', 'freeze'] + expected = [sys.executable, "-m", "pip", "freeze"] mock.assert_called_with( - expected, - cwd=None, - runas=None, - python_shell=False, - use_vt=False, + expected, cwd=None, runas=None, python_shell=False, use_vt=False, ) self.assertEqual( - ret, { - 'SaltTesting-dev': 'git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8', - 'M2Crypto': '0.21.1', - 'bbfreeze-loader': '1.1.0', - 'bbfreeze': '1.1.0', - 'pip': mock_version, - 'pycrypto': '2.6' - } + ret, + { + "SaltTesting-dev": "git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8", + "M2Crypto": "0.21.1", + "bbfreeze-loader": "1.1.0", + "bbfreeze": "1.1.0", + "pip": mock_version, + "pycrypto": "2.6", + }, ) # Non zero returncode raises exception? - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'CABOOOOMMM!'}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='6.1.1')): + mock = MagicMock(return_value={"retcode": 1, "stderr": "CABOOOOMMM!"}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="6.1.1")): self.assertRaises( - CommandExecutionError, - pip.list_, + CommandExecutionError, pip.list_, ) def test_list_command_with_all(self): eggs = [ - 'M2Crypto==0.21.1', - '-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev', - 'bbfreeze==1.1.0', - 'bbfreeze-loader==1.1.0', - 'pip==9.0.1', - 'pycrypto==2.6', - 'setuptools==20.10.1' + "M2Crypto==0.21.1", + "-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev", + "bbfreeze==1.1.0", + "bbfreeze-loader==1.1.0", + "pip==9.0.1", + "pycrypto==2.6", + "setuptools==20.10.1", ] # N.B.: this is deliberately different from the "output" of pip freeze. # This is to demonstrate that the version reported comes from freeze # instead of from the pip.version function. - mock_version = '9.0.0' - mock = MagicMock(return_value={'retcode': 0, 'stdout': '\n'.join(eggs)}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value=mock_version)): + mock_version = "9.0.0" + mock = MagicMock(return_value={"retcode": 0, "stdout": "\n".join(eggs)}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch( + "salt.modules.pip.version", MagicMock(return_value=mock_version) + ): ret = pip.list_() - expected = [sys.executable, '-m', 'pip', 'freeze', '--all'] + expected = [sys.executable, "-m", "pip", "freeze", "--all"] mock.assert_called_with( - expected, - cwd=None, - runas=None, - python_shell=False, - use_vt=False, + expected, cwd=None, runas=None, python_shell=False, use_vt=False, ) self.assertEqual( - ret, { - 'SaltTesting-dev': 'git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8', - 'M2Crypto': '0.21.1', - 'bbfreeze-loader': '1.1.0', - 'bbfreeze': '1.1.0', - 'pip': '9.0.1', - 'pycrypto': '2.6', - 'setuptools': '20.10.1' - } + ret, + { + "SaltTesting-dev": "git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8", + "M2Crypto": "0.21.1", + "bbfreeze-loader": "1.1.0", + "bbfreeze": "1.1.0", + "pip": "9.0.1", + "pycrypto": "2.6", + "setuptools": "20.10.1", + }, ) # Non zero returncode raises exception? - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'CABOOOOMMM!'}) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='6.1.1')): + mock = MagicMock(return_value={"retcode": 1, "stderr": "CABOOOOMMM!"}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="6.1.1")): self.assertRaises( - CommandExecutionError, - pip.list_, + CommandExecutionError, pip.list_, ) def test_list_command_with_prefix(self): eggs = [ - 'M2Crypto==0.21.1', - '-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev', - 'bbfreeze==1.1.0', - 'bbfreeze-loader==1.1.0', - 'pycrypto==2.6' + "M2Crypto==0.21.1", + "-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev", + "bbfreeze==1.1.0", + "bbfreeze-loader==1.1.0", + "pycrypto==2.6", ] - mock = MagicMock( - return_value={ - 'retcode': 0, - 'stdout': '\n'.join(eggs) - } - ) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='6.1.1')): - ret = pip.list_(prefix='bb') - expected = [sys.executable, '-m', 'pip', 'freeze'] + mock = MagicMock(return_value={"retcode": 0, "stdout": "\n".join(eggs)}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="6.1.1")): + ret = pip.list_(prefix="bb") + expected = [sys.executable, "-m", "pip", "freeze"] mock.assert_called_with( - expected, - cwd=None, - runas=None, - python_shell=False, - use_vt=False, - ) - self.assertEqual( - ret, { - 'bbfreeze-loader': '1.1.0', - 'bbfreeze': '1.1.0', - } + expected, cwd=None, runas=None, python_shell=False, use_vt=False, ) + self.assertEqual(ret, {"bbfreeze-loader": "1.1.0", "bbfreeze": "1.1.0"}) def test_list_upgrades_legacy(self): eggs = [ - 'apache-libcloud (Current: 1.1.0 Latest: 2.2.1 [wheel])', - 'appdirs (Current: 1.4.1 Latest: 1.4.3 [wheel])', - 'awscli (Current: 1.11.63 Latest: 1.12.1 [sdist])' + "apache-libcloud (Current: 1.1.0 Latest: 2.2.1 [wheel])", + "appdirs (Current: 1.4.1 Latest: 1.4.3 [wheel])", + "awscli (Current: 1.11.63 Latest: 1.12.1 [sdist])", ] - mock = MagicMock( - return_value={ - 'retcode': 0, - 'stdout': '\n'.join(eggs) - } - ) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='6.1.1')): + mock = MagicMock(return_value={"retcode": 0, "stdout": "\n".join(eggs)}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="6.1.1")): ret = pip.list_upgrades() mock.assert_called_with( - [sys.executable, '-m', 'pip', 'list', '--outdated'], + [sys.executable, "-m", "pip", "list", "--outdated"], cwd=None, runas=None, ) self.assertEqual( - ret, { - 'apache-libcloud': '2.2.1 [wheel]', - 'appdirs': '1.4.3 [wheel]', - 'awscli': '1.12.1 [sdist]' - } + ret, + { + "apache-libcloud": "2.2.1 [wheel]", + "appdirs": "1.4.3 [wheel]", + "awscli": "1.12.1 [sdist]", + }, ) def test_list_upgrades_gt9(self): - eggs = '''[{"latest_filetype": "wheel", "version": "1.1.0", "name": "apache-libcloud", "latest_version": "2.2.1"}, + eggs = """[{"latest_filetype": "wheel", "version": "1.1.0", "name": "apache-libcloud", "latest_version": "2.2.1"}, {"latest_filetype": "wheel", "version": "1.4.1", "name": "appdirs", "latest_version": "1.4.3"}, {"latest_filetype": "sdist", "version": "1.11.63", "name": "awscli", "latest_version": "1.12.1"} - ]''' - mock = MagicMock( - return_value={ - 'retcode': 0, - 'stdout': '{0}'.format(eggs) - } - ) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='9.1.1')): + ]""" + mock = MagicMock(return_value={"retcode": 0, "stdout": "{0}".format(eggs)}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="9.1.1")): ret = pip.list_upgrades() mock.assert_called_with( - [sys.executable, '-m', 'pip', 'list', '--outdated', '--format=json'], + [ + sys.executable, + "-m", + "pip", + "list", + "--outdated", + "--format=json", + ], cwd=None, runas=None, ) self.assertEqual( - ret, { - 'apache-libcloud': '2.2.1 [wheel]', - 'appdirs': '1.4.3 [wheel]', - 'awscli': '1.12.1 [sdist]' - } + ret, + { + "apache-libcloud": "2.2.1 [wheel]", + "appdirs": "1.4.3 [wheel]", + "awscli": "1.12.1 [sdist]", + }, ) def test_is_installed_true(self): eggs = [ - 'M2Crypto==0.21.1', - '-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev', - 'bbfreeze==1.1.0', - 'bbfreeze-loader==1.1.0', - 'pycrypto==2.6' + "M2Crypto==0.21.1", + "-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev", + "bbfreeze==1.1.0", + "bbfreeze-loader==1.1.0", + "pycrypto==2.6", ] - mock = MagicMock( - return_value={ - 'retcode': 0, - 'stdout': '\n'.join(eggs) - } - ) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='6.1.1')): - ret = pip.is_installed(pkgname='bbfreeze') + mock = MagicMock(return_value={"retcode": 0, "stdout": "\n".join(eggs)}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="6.1.1")): + ret = pip.is_installed(pkgname="bbfreeze") mock.assert_called_with( - [sys.executable, '-m', 'pip', 'freeze'], + [sys.executable, "-m", "pip", "freeze"], cwd=None, runas=None, python_shell=False, @@ -1384,24 +1355,18 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): def test_is_installed_false(self): eggs = [ - 'M2Crypto==0.21.1', - '-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev', - 'bbfreeze==1.1.0', - 'bbfreeze-loader==1.1.0', - 'pycrypto==2.6' + "M2Crypto==0.21.1", + "-e git+git@github.com:s0undt3ch/salt-testing.git@9ed81aa2f918d59d3706e56b18f0782d1ea43bf8#egg=SaltTesting-dev", + "bbfreeze==1.1.0", + "bbfreeze-loader==1.1.0", + "pycrypto==2.6", ] - mock = MagicMock( - return_value={ - 'retcode': 0, - 'stdout': '\n'.join(eggs) - } - ) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='6.1.1')): - ret = pip.is_installed(pkgname='notexist') + mock = MagicMock(return_value={"retcode": 0, "stdout": "\n".join(eggs)}) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="6.1.1")): + ret = pip.is_installed(pkgname="notexist") mock.assert_called_with( - [sys.executable, '-m', 'pip', 'freeze'], + [sys.executable, "-m", "pip", "freeze"], cwd=None, runas=None, python_shell=False, @@ -1410,36 +1375,39 @@ class PipTestCase(TestCase, LoaderModuleMockMixin): self.assertFalse(ret) def test_install_pre_argument_in_resulting_command(self): - pkg = 'pep8' + pkg = "pep8" # Lower than 1.4 versions don't end up with `--pre` in the resulting output - mock = MagicMock(side_effect=[ - {'retcode': 0, 'stdout': 'pip 1.2.0 /path/to/site-packages/pip'}, - {'retcode': 0, 'stdout': ''} - ]) - with patch.dict(pip.__salt__, {'cmd.run_all': mock}): - with patch('salt.modules.pip.version', - MagicMock(return_value='1.3')): + mock = MagicMock( + side_effect=[ + {"retcode": 0, "stdout": "pip 1.2.0 /path/to/site-packages/pip"}, + {"retcode": 0, "stdout": ""}, + ] + ) + with patch.dict(pip.__salt__, {"cmd.run_all": mock}): + with patch("salt.modules.pip.version", MagicMock(return_value="1.3")): pip.install(pkg, pre_releases=True) - expected = [sys.executable, '-m', 'pip', 'install', pkg] + expected = [sys.executable, "-m", "pip", "install", pkg] mock.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, ) - mock_run = MagicMock(return_value='pip 1.4.1 /path/to/site-packages/pip') - mock_run_all = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(pip.__salt__, {'cmd.run_stdout': mock_run, - 'cmd.run_all': mock_run_all}): - with patch('salt.modules.pip._get_pip_bin', - MagicMock(return_value=['pip'])): + mock_run = MagicMock(return_value="pip 1.4.1 /path/to/site-packages/pip") + mock_run_all = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + pip.__salt__, {"cmd.run_stdout": mock_run, "cmd.run_all": mock_run_all} + ): + with patch( + "salt.modules.pip._get_pip_bin", MagicMock(return_value=["pip"]) + ): pip.install(pkg, pre_releases=True) - expected = ['pip', 'install', '--pre', pkg] + expected = ["pip", "install", "--pre", pkg] mock_run_all.assert_called_with( expected, - saltenv='base', + saltenv="base", runas=None, use_vt=False, python_shell=False, diff --git a/tests/unit/modules/test_pkg_resource.py b/tests/unit/modules/test_pkg_resource.py index 6bb647082ce..bc2a16d8f2b 100644 --- a/tests/unit/modules/test_pkg_resource.py +++ b/tests/unit/modules/test_pkg_resource.py @@ -1,273 +1,304 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import yaml -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.pkg_resource as pkg_resource # Import Salt Libs import salt.utils.data import salt.utils.yaml -import salt.modules.pkg_resource as pkg_resource +import yaml from salt.ext import six +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PkgresTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.pkg_resource - ''' + """ + def setup_loader_modules(self): return {pkg_resource: {}} def test_pack_sources(self): - ''' + """ Test to accepts list of dicts (or a string representing a list of dicts) and packs the key/value pairs into a single dict. - ''' - with patch.object(salt.utils.yaml, - 'safe_load', - MagicMock(side_effect=yaml.parser.ParserError('f'))): - with patch.dict(pkg_resource.__salt__, - {'pkg.normalize_name': MagicMock()}): - self.assertDictEqual(pkg_resource.pack_sources('sources'), {}) + """ + with patch.object( + salt.utils.yaml, + "safe_load", + MagicMock(side_effect=yaml.parser.ParserError("f")), + ): + with patch.dict(pkg_resource.__salt__, {"pkg.normalize_name": MagicMock()}): + self.assertDictEqual(pkg_resource.pack_sources("sources"), {}) - self.assertDictEqual(pkg_resource.pack_sources(['A', 'a']), {}) + self.assertDictEqual(pkg_resource.pack_sources(["A", "a"]), {}) - self.assertTrue(pkg_resource.pack_sources([{'A': 'a'}])) + self.assertTrue(pkg_resource.pack_sources([{"A": "a"}])) def test_parse_targets(self): - ''' + """ Test to parses the input to pkg.install and returns back the package(s) to be installed. Returns a list of packages, as well as a string noting whether the packages are to come from a repository or a binary package. - ''' - with patch.dict(pkg_resource.__grains__, {'os': 'A'}): - self.assertEqual(pkg_resource.parse_targets(pkgs='a', - sources='a'), - (None, None)) + """ + with patch.dict(pkg_resource.__grains__, {"os": "A"}): + self.assertEqual( + pkg_resource.parse_targets(pkgs="a", sources="a"), (None, None) + ) - with patch.object(pkg_resource, '_repack_pkgs', - return_value=False): - self.assertEqual(pkg_resource.parse_targets(pkgs='a'), - (None, None)) + with patch.object(pkg_resource, "_repack_pkgs", return_value=False): + self.assertEqual(pkg_resource.parse_targets(pkgs="a"), (None, None)) - with patch.object(pkg_resource, '_repack_pkgs', - return_value='A'): - self.assertEqual(pkg_resource.parse_targets(pkgs='a'), - ('A', 'repository')) + with patch.object(pkg_resource, "_repack_pkgs", return_value="A"): + self.assertEqual( + pkg_resource.parse_targets(pkgs="a"), ("A", "repository") + ) - with patch.dict(pkg_resource.__grains__, {'os': 'MacOS1'}): - with patch.object(pkg_resource, 'pack_sources', - return_value=False): - self.assertEqual(pkg_resource.parse_targets(sources='s'), - (None, None)) + with patch.dict(pkg_resource.__grains__, {"os": "MacOS1"}): + with patch.object(pkg_resource, "pack_sources", return_value=False): + self.assertEqual(pkg_resource.parse_targets(sources="s"), (None, None)) - with patch.object(pkg_resource, 'pack_sources', - return_value={'A': '/a'}): - with patch.dict(pkg_resource.__salt__, - {'config.valid_fileproto': - MagicMock(return_value=False)}): - self.assertEqual(pkg_resource.parse_targets(sources='s'), - (['/a'], 'file')) + with patch.object(pkg_resource, "pack_sources", return_value={"A": "/a"}): + with patch.dict( + pkg_resource.__salt__, + {"config.valid_fileproto": MagicMock(return_value=False)}, + ): + self.assertEqual( + pkg_resource.parse_targets(sources="s"), (["/a"], "file") + ) - with patch.object(pkg_resource, 'pack_sources', - return_value={'A': 'a'}): - with patch.dict(pkg_resource.__salt__, - {'config.valid_fileproto': - MagicMock(return_value=False)}): - self.assertEqual(pkg_resource.parse_targets(name='n'), - ({'n': None}, 'repository')) + with patch.object(pkg_resource, "pack_sources", return_value={"A": "a"}): + with patch.dict( + pkg_resource.__salt__, + {"config.valid_fileproto": MagicMock(return_value=False)}, + ): + self.assertEqual( + pkg_resource.parse_targets(name="n"), + ({"n": None}, "repository"), + ) - self.assertEqual(pkg_resource.parse_targets(), - (None, None)) + self.assertEqual(pkg_resource.parse_targets(), (None, None)) def test_version(self): - ''' + """ Test to Common interface for obtaining the version of installed packages. - ''' - with patch.object(salt.utils.data, 'is_true', return_value=True): - mock = MagicMock(return_value={'A': 'B'}) - with patch.dict(pkg_resource.__salt__, - {'pkg.list_pkgs': mock}): - self.assertEqual(pkg_resource.version('A'), 'B') + """ + with patch.object(salt.utils.data, "is_true", return_value=True): + mock = MagicMock(return_value={"A": "B"}) + with patch.dict(pkg_resource.__salt__, {"pkg.list_pkgs": mock}): + self.assertEqual(pkg_resource.version("A"), "B") self.assertDictEqual(pkg_resource.version(), {}) mock = MagicMock(return_value={}) - with patch.dict(pkg_resource.__salt__, {'pkg.list_pkgs': mock}): - with patch('builtins.next' if six.PY3 else '__builtin__.next') as mock_next: + with patch.dict(pkg_resource.__salt__, {"pkg.list_pkgs": mock}): + with patch( + "builtins.next" if six.PY3 else "__builtin__.next" + ) as mock_next: mock_next.side_effect = StopIteration() - self.assertEqual(pkg_resource.version('A'), '') + self.assertEqual(pkg_resource.version("A"), "") def test_add_pkg(self): - ''' + """ Test to add a package to a dict of installed packages. - ''' - self.assertIsNone(pkg_resource.add_pkg({'pkgs': []}, 'name', 'version')) + """ + self.assertIsNone(pkg_resource.add_pkg({"pkgs": []}, "name", "version")) def test_sort_pkglist(self): - ''' + """ Test to accepts a dict obtained from pkg.list_pkgs() and sorts in place the list of versions for any packages that have multiple versions installed, so that two package lists can be compared to one another. - ''' + """ self.assertIsNone(pkg_resource.sort_pkglist({})) def test_format_pkg_list_no_attr(self): - ''' + """ Test to output format of the package list with no attr parameter. - ''' + """ packages = { - 'glibc': [{'version': '2.12', 'epoch': None, 'release': '1.212.el6', 'arch': 'x86_64'}], - 'glibc.i686': [{'version': '2.12', 'epoch': None, 'release': '1.212.el6', 'arch': 'i686'}], - 'foobar': [ - {'version': '1.2.0', 'epoch': '2', 'release': '7', 'arch': 'x86_64'}, - {'version': '1.2.3', 'epoch': '2', 'release': '27', 'arch': 'x86_64'}, + "glibc": [ + { + "version": "2.12", + "epoch": None, + "release": "1.212.el6", + "arch": "x86_64", + } + ], + "glibc.i686": [ + { + "version": "2.12", + "epoch": None, + "release": "1.212.el6", + "arch": "i686", + } + ], + "foobar": [ + {"version": "1.2.0", "epoch": "2", "release": "7", "arch": "x86_64"}, + {"version": "1.2.3", "epoch": "2", "release": "27", "arch": "x86_64"}, + ], + "foobar.something": [ + {"version": "1.1", "epoch": "3", "release": "23.1", "arch": "i686"} + ], + "foobar.": [ + {"version": "1.1", "epoch": "3", "release": "23.1", "arch": "i686"} ], - 'foobar.something': [{'version': '1.1', 'epoch': '3', 'release': '23.1', 'arch': 'i686'}], - 'foobar.': [{'version': '1.1', 'epoch': '3', 'release': '23.1', 'arch': 'i686'}] } expected_pkg_list = { - 'glibc': '2.12-1.212.el6', - 'glibc.i686': '2.12-1.212.el6', - 'foobar': '2:1.2.0-7,2:1.2.3-27', - 'foobar.something': '3:1.1-23.1', - 'foobar.': '3:1.1-23.1', + "glibc": "2.12-1.212.el6", + "glibc.i686": "2.12-1.212.el6", + "foobar": "2:1.2.0-7,2:1.2.3-27", + "foobar.something": "3:1.1-23.1", + "foobar.": "3:1.1-23.1", } if six.PY3: - self.assertCountEqual(pkg_resource.format_pkg_list(packages, False, None), expected_pkg_list) + self.assertCountEqual( + pkg_resource.format_pkg_list(packages, False, None), expected_pkg_list + ) else: - self.assertItemsEqual(pkg_resource.format_pkg_list(packages, False, None), expected_pkg_list) + self.assertItemsEqual( + pkg_resource.format_pkg_list(packages, False, None), expected_pkg_list + ) def test_format_pkg_list_with_attr(self): - ''' + """ Test to output format of the package list with attr parameter. In this case, any redundant "arch" reference will be removed from the package name since it's include as part of the requested attr. - ''' + """ NAME_ARCH_MAPPING = { - 'glibc': { - 'name': 'glibc', - 'arch': None - }, - 'glibc.i686': { - 'name': 'glibc', - 'arch': 'i686' - }, - 'foobar': { - 'name': 'foobar', - 'arch': None - }, - 'foobar.something': { - 'name': 'foobar.something', - 'arch': None - }, - 'foobar.': { - 'name': 'foobar.', - 'arch': None - } + "glibc": {"name": "glibc", "arch": None}, + "glibc.i686": {"name": "glibc", "arch": "i686"}, + "foobar": {"name": "foobar", "arch": None}, + "foobar.something": {"name": "foobar.something", "arch": None}, + "foobar.": {"name": "foobar.", "arch": None}, } packages = { - 'glibc': [{'version': '2.12', 'epoch': None, 'release': '1.212.el6', 'arch': 'x86_64'}], - 'glibc.i686': [{'version': '2.12', 'epoch': None, 'release': '1.212.el6', 'arch': 'i686'}], - 'foobar': [ - {'version': '1.2.0', 'epoch': '2', 'release': '7', 'arch': 'x86_64'}, - {'version': '1.2.3', 'epoch': '2', 'release': '27', 'arch': 'x86_64'}, + "glibc": [ + { + "version": "2.12", + "epoch": None, + "release": "1.212.el6", + "arch": "x86_64", + } + ], + "glibc.i686": [ + { + "version": "2.12", + "epoch": None, + "release": "1.212.el6", + "arch": "i686", + } + ], + "foobar": [ + {"version": "1.2.0", "epoch": "2", "release": "7", "arch": "x86_64"}, + {"version": "1.2.3", "epoch": "2", "release": "27", "arch": "x86_64"}, + ], + "foobar.something": [ + {"version": "1.1", "epoch": "3", "release": "23.1", "arch": "i686"} + ], + "foobar.": [ + {"version": "1.1", "epoch": "3", "release": "23.1", "arch": "i686"} ], - 'foobar.something': [{'version': '1.1', 'epoch': '3', 'release': '23.1', 'arch': 'i686'}], - 'foobar.': [{'version': '1.1', 'epoch': '3', 'release': '23.1', 'arch': 'i686'}] } expected_pkg_list = { - 'glibc': [ + "glibc": [ { - 'arch': 'x86_64', - 'release': '1.212.el6', - 'epoch': None, - 'version': '2.12' + "arch": "x86_64", + "release": "1.212.el6", + "epoch": None, + "version": "2.12", }, { - 'arch': 'i686', - 'release': '1.212.el6', - 'epoch': None, - 'version': '2.12' - } - ], - 'foobar': [ - { - 'arch': 'x86_64', - 'release': '7', - 'epoch': '2', - 'version': '1.2.0' + "arch": "i686", + "release": "1.212.el6", + "epoch": None, + "version": "2.12", }, - { - 'arch': 'x86_64', - 'release': '27', - 'epoch': '2', - 'version': '1.2.3' - } ], - 'foobar.': [ - { - 'arch': 'i686', - 'release': '23.1', - 'epoch': '3', - 'version': '1.1' - } + "foobar": [ + {"arch": "x86_64", "release": "7", "epoch": "2", "version": "1.2.0"}, + {"arch": "x86_64", "release": "27", "epoch": "2", "version": "1.2.3"}, + ], + "foobar.": [ + {"arch": "i686", "release": "23.1", "epoch": "3", "version": "1.1"} + ], + "foobar.something": [ + {"arch": "i686", "release": "23.1", "epoch": "3", "version": "1.1"} ], - 'foobar.something': [ - { - 'arch': 'i686', - 'release': '23.1', - 'epoch': '3', - 'version': '1.1' - } - ] } - with patch.dict(pkg_resource.__salt__, {'pkg.parse_arch': NAME_ARCH_MAPPING.get}): + with patch.dict( + pkg_resource.__salt__, {"pkg.parse_arch": NAME_ARCH_MAPPING.get} + ): if six.PY3: - self.assertCountEqual(pkg_resource.format_pkg_list(packages, False, attr=['epoch', 'release']), expected_pkg_list) + self.assertCountEqual( + pkg_resource.format_pkg_list( + packages, False, attr=["epoch", "release"] + ), + expected_pkg_list, + ) else: - self.assertItemsEqual(pkg_resource.format_pkg_list(packages, False, attr=['epoch', 'release']), expected_pkg_list) + self.assertItemsEqual( + pkg_resource.format_pkg_list( + packages, False, attr=["epoch", "release"] + ), + expected_pkg_list, + ) def test_stringify(self): - ''' + """ Test to takes a dict of package name/version information and joins each list of installed versions into a string. - ''' + """ self.assertIsNone(pkg_resource.stringify({})) def test_version_clean(self): - ''' + """ Test to clean the version string removing extra data. - ''' - with patch.dict(pkg_resource.__salt__, {'pkg.version_clean': - MagicMock(return_value='A')}): - self.assertEqual(pkg_resource.version_clean('version'), 'A') + """ + with patch.dict( + pkg_resource.__salt__, {"pkg.version_clean": MagicMock(return_value="A")} + ): + self.assertEqual(pkg_resource.version_clean("version"), "A") - self.assertEqual(pkg_resource.version_clean('v'), 'v') + self.assertEqual(pkg_resource.version_clean("v"), "v") def test_check_extra_requirements(self): - ''' + """ Test to check if the installed package already has the given requirements. - ''' - with patch.dict(pkg_resource.__salt__, {'pkg.check_extra_requirements': - MagicMock(return_value='A')}): - self.assertEqual(pkg_resource.check_extra_requirements('a', 'b'), - 'A') + """ + with patch.dict( + pkg_resource.__salt__, + {"pkg.check_extra_requirements": MagicMock(return_value="A")}, + ): + self.assertEqual(pkg_resource.check_extra_requirements("a", "b"), "A") - self.assertTrue(pkg_resource.check_extra_requirements('a', False)) + self.assertTrue(pkg_resource.check_extra_requirements("a", False)) + + def test_version_compare(self): + """ + Test the version_compare function + + TODO: Come up with a good way to test epoch handling across different + platforms. This function will look in the ``__salt__`` dunder for a + version_cmp function (which not all pkg modules implement) and use that + to perform platform-specific handling (including interpretation of + epochs), but even an integration test would need to take into account + the fact that not all package managers grok epochs. + """ + assert pkg_resource.version_compare("2.0", "<", "3.0") is True diff --git a/tests/unit/modules/test_pkgin.py b/tests/unit/modules/test_pkgin.py index 729870dbae0..0b30aa50342 100644 --- a/tests/unit/modules/test_pkgin.py +++ b/tests/unit/modules/test_pkgin.py @@ -2,174 +2,182 @@ # Import Python Libs from __future__ import absolute_import -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.pkgin as pkgin +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PkginTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.pkgin - ''' + """ + def setup_loader_modules(self): - return { - pkgin: { - '__opts__': { - 'cachedir': '/tmp' - } - } - } + return {pkgin: {"__opts__": {"cachedir": "/tmp"}}} def test_search(self): - ''' + """ Test searching for a package - ''' + """ # Test searching for an available and uninstalled package pkgin_out = [ - 'somepkg-1.0 Some package description here', - '', - '=: package is installed and up-to-date', - '<: package is installed but newer version is available', - '>: installed package has a greater version than available package' + "somepkg-1.0 Some package description here", + "", + "=: package is installed and up-to-date", + "<: package is installed but newer version is available", + ">: installed package has a greater version than available package", ] - pkgin__get_version_mock = MagicMock(return_value=['0', '9', '0']) - pkgin__check_pkgin_mock = MagicMock(return_value='/opt/pkg/bin/pkgin') + pkgin__get_version_mock = MagicMock(return_value=["0", "9", "0"]) + pkgin__check_pkgin_mock = MagicMock(return_value="/opt/pkg/bin/pkgin") pkgin_search_cmd = MagicMock(return_value=os.linesep.join(pkgin_out)) - with patch('salt.modules.pkgin._get_version', pkgin__get_version_mock), \ - patch('salt.modules.pkgin._check_pkgin', pkgin__check_pkgin_mock), \ - patch.dict(pkgin.__salt__, {'cmd.run': pkgin_search_cmd}): - self.assertDictEqual(pkgin.search('somepkg'), {'somepkg': '1.0'}) + with patch("salt.modules.pkgin._get_version", pkgin__get_version_mock), patch( + "salt.modules.pkgin._check_pkgin", pkgin__check_pkgin_mock + ), patch.dict(pkgin.__salt__, {"cmd.run": pkgin_search_cmd}): + self.assertDictEqual(pkgin.search("somepkg"), {"somepkg": "1.0"}) # Test searching for an available and installed package pkgin_out = [ - 'somepkg-1.0 = Some package description here', - '', - '=: package is installed and up-to-date', - '<: package is installed but newer version is available', - '>: installed package has a greater version than available package' + "somepkg-1.0 = Some package description here", + "", + "=: package is installed and up-to-date", + "<: package is installed but newer version is available", + ">: installed package has a greater version than available package", ] pkgin_search_cmd = MagicMock(return_value=os.linesep.join(pkgin_out)) - with patch('salt.modules.pkgin._get_version', pkgin__get_version_mock), \ - patch('salt.modules.pkgin._check_pkgin', pkgin__check_pkgin_mock), \ - patch.dict(pkgin.__salt__, {'cmd.run': pkgin_search_cmd}): - self.assertDictEqual(pkgin.search('somepkg'), {'somepkg': '1.0'}) + with patch("salt.modules.pkgin._get_version", pkgin__get_version_mock), patch( + "salt.modules.pkgin._check_pkgin", pkgin__check_pkgin_mock + ), patch.dict(pkgin.__salt__, {"cmd.run": pkgin_search_cmd}): + self.assertDictEqual(pkgin.search("somepkg"), {"somepkg": "1.0"}) def test_latest_version(self): - ''' + """ Test getting the latest version of a package - ''' + """ # Test getting the latest version of an uninstalled package pkgin_out = [ - 'somepkg-1.0;;Some package description here', - '', - '=: package is installed and up-to-date', - '<: package is installed but newer version is available', - '>: installed package has a greater version than available package' + "somepkg-1.0;;Some package description here", + "", + "=: package is installed and up-to-date", + "<: package is installed but newer version is available", + ">: installed package has a greater version than available package", ] - pkgin__get_version_mock = MagicMock(return_value=['0', '9', '0']) - pkgin__check_pkgin_mock = MagicMock(return_value='/opt/pkg/bin/pkgin') + pkgin__get_version_mock = MagicMock(return_value=["0", "9", "0"]) + pkgin__check_pkgin_mock = MagicMock(return_value="/opt/pkg/bin/pkgin") pkgin_refresh_db_mock = MagicMock(return_value=True) pkgin_search_cmd = MagicMock(return_value=os.linesep.join(pkgin_out)) - with patch('salt.modules.pkgin.refresh_db', pkgin_refresh_db_mock), \ - patch('salt.modules.pkgin._get_version', pkgin__get_version_mock), \ - patch('salt.modules.pkgin._check_pkgin', pkgin__check_pkgin_mock), \ - patch.dict(pkgin.__salt__, {'cmd.run': pkgin_search_cmd}): - self.assertEqual(pkgin.latest_version('somepkg'), '1.0') + with patch("salt.modules.pkgin.refresh_db", pkgin_refresh_db_mock), patch( + "salt.modules.pkgin._get_version", pkgin__get_version_mock + ), patch( + "salt.modules.pkgin._check_pkgin", pkgin__check_pkgin_mock + ), patch.dict( + pkgin.__salt__, {"cmd.run": pkgin_search_cmd} + ): + self.assertEqual(pkgin.latest_version("somepkg"), "1.0") # Test getting the latest version of an installed package pkgin_out = [ - 'somepkg-1.1;<;Some package description here', - '', - '=: package is installed and up-to-date', - '<: package is installed but newer version is available', - '>: installed package has a greater version than available package' + "somepkg-1.1;<;Some package description here", + "", + "=: package is installed and up-to-date", + "<: package is installed but newer version is available", + ">: installed package has a greater version than available package", ] pkgin_refresh_db_mock = MagicMock(return_value=True) pkgin_search_cmd = MagicMock(return_value=os.linesep.join(pkgin_out)) - with patch('salt.modules.pkgin.refresh_db', pkgin_refresh_db_mock), \ - patch('salt.modules.pkgin._get_version', pkgin__get_version_mock), \ - patch('salt.modules.pkgin._check_pkgin', pkgin__check_pkgin_mock), \ - patch.dict(pkgin.__salt__, {'cmd.run': pkgin_search_cmd}): - self.assertEqual(pkgin.latest_version('somepkg'), '1.1') + with patch("salt.modules.pkgin.refresh_db", pkgin_refresh_db_mock), patch( + "salt.modules.pkgin._get_version", pkgin__get_version_mock + ), patch( + "salt.modules.pkgin._check_pkgin", pkgin__check_pkgin_mock + ), patch.dict( + pkgin.__salt__, {"cmd.run": pkgin_search_cmd} + ): + self.assertEqual(pkgin.latest_version("somepkg"), "1.1") # Test getting the latest version of a package that is already installed # and is already at the latest version pkgin_out = [ - 'somepkg-1.2;=;Some package description here', - '', - '=: package is installed and up-to-date', - '<: package is installed but newer version is available', - '>: installed package has a greater version than available package' + "somepkg-1.2;=;Some package description here", + "", + "=: package is installed and up-to-date", + "<: package is installed but newer version is available", + ">: installed package has a greater version than available package", ] pkgin_refresh_db_mock = MagicMock(return_value=True) pkgin_search_cmd = MagicMock(return_value=os.linesep.join(pkgin_out)) - with patch('salt.modules.pkgin.refresh_db', pkgin_refresh_db_mock), \ - patch('salt.modules.pkgin._get_version', pkgin__get_version_mock), \ - patch('salt.modules.pkgin._check_pkgin', pkgin__check_pkgin_mock), \ - patch.dict(pkgin.__salt__, {'cmd.run': pkgin_search_cmd}): - self.assertEqual(pkgin.latest_version('somepkg'), '1.2') + with patch("salt.modules.pkgin.refresh_db", pkgin_refresh_db_mock), patch( + "salt.modules.pkgin._get_version", pkgin__get_version_mock + ), patch( + "salt.modules.pkgin._check_pkgin", pkgin__check_pkgin_mock + ), patch.dict( + pkgin.__salt__, {"cmd.run": pkgin_search_cmd} + ): + self.assertEqual(pkgin.latest_version("somepkg"), "1.2") # Test getting the latest version of a bogus package - pkgin_out = 'No results found for ^boguspkg$' + pkgin_out = "No results found for ^boguspkg$" pkgin_refresh_db_mock = MagicMock(return_value=True) pkgin_search_cmd = MagicMock(return_value=pkgin_out) - with patch('salt.modules.pkgin.refresh_db', pkgin_refresh_db_mock), \ - patch('salt.modules.pkgin._get_version', pkgin__get_version_mock), \ - patch('salt.modules.pkgin._check_pkgin', pkgin__check_pkgin_mock), \ - patch.dict(pkgin.__salt__, {'cmd.run': pkgin_search_cmd}): - self.assertEqual(pkgin.latest_version('boguspkg'), {}) + with patch("salt.modules.pkgin.refresh_db", pkgin_refresh_db_mock), patch( + "salt.modules.pkgin._get_version", pkgin__get_version_mock + ), patch( + "salt.modules.pkgin._check_pkgin", pkgin__check_pkgin_mock + ), patch.dict( + pkgin.__salt__, {"cmd.run": pkgin_search_cmd} + ): + self.assertEqual(pkgin.latest_version("boguspkg"), {}) def test_file_dict(self): - ''' + """ Test that file_dict doesn't crash - ''' + """ pkg_info_stdout = [ - '/opt/pkg/bin/pkgin', - '/opt/pkg/man/man1/pkgin.1', - '/opt/pkg/share/examples/pkgin/preferred.conf.example', - '/opt/pkg/share/examples/pkgin/repositories.conf.example' + "/opt/pkg/bin/pkgin", + "/opt/pkg/man/man1/pkgin.1", + "/opt/pkg/share/examples/pkgin/preferred.conf.example", + "/opt/pkg/share/examples/pkgin/repositories.conf.example", ] pkg_info_out = { - 'pid': 1234, - 'retcode': 0, - 'stderr': '', - 'stdout': os.linesep.join(pkg_info_stdout) + "pid": 1234, + "retcode": 0, + "stderr": "", + "stdout": os.linesep.join(pkg_info_stdout), } pkg_info_cmd = MagicMock(return_value=pkg_info_out) - with patch.dict(pkgin.__salt__, {'cmd.run_all': pkg_info_cmd}): - self.assertDictEqual(pkgin.file_dict('pkgin'), { - 'files': { - 'pkgin': [ - '/opt/pkg/bin/pkgin', - '/opt/pkg/man/man1/pkgin.1', - '/opt/pkg/share/examples/pkgin/preferred.conf.example', - '/opt/pkg/share/examples/pkgin/repositories.conf.example' - ] - } - }) + with patch.dict(pkgin.__salt__, {"cmd.run_all": pkg_info_cmd}): + self.assertDictEqual( + pkgin.file_dict("pkgin"), + { + "files": { + "pkgin": [ + "/opt/pkg/bin/pkgin", + "/opt/pkg/man/man1/pkgin.1", + "/opt/pkg/share/examples/pkgin/preferred.conf.example", + "/opt/pkg/share/examples/pkgin/repositories.conf.example", + ] + } + }, + ) diff --git a/tests/unit/modules/test_pkgng.py b/tests/unit/modules/test_pkgng.py index 2a95b0a9ac5..7fa79ea7e99 100644 --- a/tests/unit/modules/test_pkgng.py +++ b/tests/unit/modules/test_pkgng.py @@ -2,113 +2,109 @@ # Import Python libs from __future__ import absolute_import -import textwrap -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +import textwrap # Import Salt Libs import salt.modules.pkgng as pkgng +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PkgNgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.pkgng - ''' + """ @classmethod def setup_loader_modules(cls): - return { - pkgng: { - '__salt__': {} - } - } + return {pkgng: {"__salt__": {}}} def test_lock(self): - ''' + """ Test pkgng.lock - ''' - lock_cmd = MagicMock(return_value={ - 'stdout': ('pkga-1.0\n' - 'pkgb-2.0\n'), - 'retcode': 0 - }) - with patch.dict(pkgng.__salt__, {'cmd.run_all': lock_cmd}): + """ + lock_cmd = MagicMock( + return_value={"stdout": ("pkga-1.0\n" "pkgb-2.0\n"), "retcode": 0} + ) + with patch.dict(pkgng.__salt__, {"cmd.run_all": lock_cmd}): - result = pkgng.lock('pkga') + result = pkgng.lock("pkga") self.assertTrue(result) lock_cmd.assert_called_with( - ['pkg', 'lock', '-y', '--quiet', '--show-locked', 'pkga'], - output_loglevel='trace', python_shell=False + ["pkg", "lock", "-y", "--quiet", "--show-locked", "pkga"], + output_loglevel="trace", + python_shell=False, ) - result = pkgng.lock('dummy') + result = pkgng.lock("dummy") self.assertFalse(result) lock_cmd.assert_called_with( - ['pkg', 'lock', '-y', '--quiet', '--show-locked', 'dummy'], - output_loglevel='trace', python_shell=False + ["pkg", "lock", "-y", "--quiet", "--show-locked", "dummy"], + output_loglevel="trace", + python_shell=False, ) def test_unlock(self): - ''' + """ Test pkgng.unlock - ''' - unlock_cmd = MagicMock(return_value={ - 'stdout': ('pkga-1.0\n' - 'pkgb-2.0\n'), - 'retcode': 0 - }) - with patch.dict(pkgng.__salt__, {'cmd.run_all': unlock_cmd}): + """ + unlock_cmd = MagicMock( + return_value={"stdout": ("pkga-1.0\n" "pkgb-2.0\n"), "retcode": 0} + ) + with patch.dict(pkgng.__salt__, {"cmd.run_all": unlock_cmd}): - result = pkgng.unlock('pkga') + result = pkgng.unlock("pkga") self.assertFalse(result) unlock_cmd.assert_called_with( - ['pkg', 'unlock', '-y', '--quiet', '--show-locked', 'pkga'], - output_loglevel='trace', python_shell=False + ["pkg", "unlock", "-y", "--quiet", "--show-locked", "pkga"], + output_loglevel="trace", + python_shell=False, ) - result = pkgng.unlock('dummy') + result = pkgng.unlock("dummy") self.assertTrue(result) unlock_cmd.assert_called_with( - ['pkg', 'unlock', '-y', '--quiet', '--show-locked', 'dummy'], - output_loglevel='trace', python_shell=False + ["pkg", "unlock", "-y", "--quiet", "--show-locked", "dummy"], + output_loglevel="trace", + python_shell=False, ) def test_locked(self): - ''' + """ Test pkgng.unlock - ''' - lock_cmd = MagicMock(return_value={ - 'stdout': ('pkga-1.0\n' - 'pkgb-2.0\n'), - 'retcode': 0 - }) - with patch.dict(pkgng.__salt__, {'cmd.run_all': lock_cmd}): + """ + lock_cmd = MagicMock( + return_value={"stdout": ("pkga-1.0\n" "pkgb-2.0\n"), "retcode": 0} + ) + with patch.dict(pkgng.__salt__, {"cmd.run_all": lock_cmd}): - result = pkgng.locked('pkga') + result = pkgng.locked("pkga") self.assertTrue(result) lock_cmd.assert_called_with( - ['pkg', 'lock', '-y', '--quiet', '--show-locked'], - output_loglevel='trace', python_shell=False + ["pkg", "lock", "-y", "--quiet", "--show-locked"], + output_loglevel="trace", + python_shell=False, ) - result = pkgng.locked('dummy') + result = pkgng.locked("dummy") self.assertFalse(result) lock_cmd.assert_called_with( - ['pkg', 'lock', '-y', '--quiet', '--show-locked'], - output_loglevel='trace', python_shell=False + ["pkg", "lock", "-y", "--quiet", "--show-locked"], + output_loglevel="trace", + python_shell=False, ) def test_list_upgrades_present(self): - ''' + """ Test pkgng.list_upgrades with upgrades available - ''' - pkg_cmd = MagicMock(return_value=textwrap.dedent( - """ + """ + pkg_cmd = MagicMock( + return_value=textwrap.dedent( + """ The following 6 package(s) will be affected (of 0 checked): Installed packages to be UPGRADED: @@ -147,27 +143,34 @@ class PkgNgTestCase(TestCase, LoaderModuleMockMixin): The process will require 14 MiB more space. 22 MiB to be downloaded. """ - )) + ) + ) - with patch.dict(pkgng.__salt__, {'cmd.run_stdout': pkg_cmd}): + with patch.dict(pkgng.__salt__, {"cmd.run_stdout": pkg_cmd}): result = pkgng.list_upgrades(refresh=False) - self.assertDictEqual(result, {'pkga': '1.1', 'pkgb': '2.1', 'pkgc': '3.1', 'pkgd': '4.1'}) + self.assertDictEqual( + result, {"pkga": "1.1", "pkgb": "2.1", "pkgc": "3.1", "pkgd": "4.1"} + ) pkg_cmd.assert_called_with( - ['pkg', 'upgrade', '--dry-run', '--quiet', '--no-repo-update'], - output_loglevel='trace', python_shell=False, ignore_retcode=True + ["pkg", "upgrade", "--dry-run", "--quiet", "--no-repo-update"], + output_loglevel="trace", + python_shell=False, + ignore_retcode=True, ) def test_list_upgrades_absent(self): - ''' + """ Test pkgng.list_upgrades with no upgrades available - ''' - pkg_cmd = MagicMock(return_value='') + """ + pkg_cmd = MagicMock(return_value="") - with patch.dict(pkgng.__salt__, {'cmd.run_stdout': pkg_cmd}): + with patch.dict(pkgng.__salt__, {"cmd.run_stdout": pkg_cmd}): result = pkgng.list_upgrades(refresh=False) self.assertDictEqual(result, {}) pkg_cmd.assert_called_with( - ['pkg', 'upgrade', '--dry-run', '--quiet', '--no-repo-update'], - output_loglevel='trace', python_shell=False, ignore_retcode=True + ["pkg", "upgrade", "--dry-run", "--quiet", "--no-repo-update"], + output_loglevel="trace", + python_shell=False, + ignore_retcode=True, ) diff --git a/tests/unit/modules/test_pkgutil.py b/tests/unit/modules/test_pkgutil.py index 68e736b2fbd..61efaf8061c 100644 --- a/tests/unit/modules/test_pkgutil.py +++ b/tests/unit/modules/test_pkgutil.py @@ -1,229 +1,253 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.pkgutil as pkgutil -from salt.exceptions import CommandExecutionError, MinionError import salt.utils.pkg +from salt.exceptions import CommandExecutionError, MinionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase class PkgutilTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.pkgutil - ''' + """ + def setup_loader_modules(self): return {pkgutil: {}} # 'refresh_db' function tests: 1 def test_refresh_db(self): - ''' + """ Test if it updates the pkgutil repo database (pkgutil -U). - ''' + """ mock = MagicMock(return_value=0) - with patch.dict(pkgutil.__salt__, {'cmd.retcode': mock}): - with patch.object(salt.utils.pkg, 'clear_rtag', Mock()): + with patch.dict(pkgutil.__salt__, {"cmd.retcode": mock}): + with patch.object(salt.utils.pkg, "clear_rtag", Mock()): self.assertTrue(pkgutil.refresh_db()) # 'upgrade_available' function tests: 1 def test_upgrade_available(self): - ''' + """ Test if there is an upgrade available for a certain package. - ''' - mock = MagicMock(return_value='A\n B\n SAME') - with patch.dict(pkgutil.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(pkgutil.upgrade_available('CSWpython'), '') + """ + mock = MagicMock(return_value="A\n B\n SAME") + with patch.dict(pkgutil.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual(pkgutil.upgrade_available("CSWpython"), "") - mock = MagicMock(side_effect=['A\n B\n SALT', None]) - with patch.dict(pkgutil.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(pkgutil.upgrade_available('CSWpython'), 'SALT') + mock = MagicMock(side_effect=["A\n B\n SALT", None]) + with patch.dict(pkgutil.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual(pkgutil.upgrade_available("CSWpython"), "SALT") - self.assertEqual(pkgutil.upgrade_available('CSWpython'), '') + self.assertEqual(pkgutil.upgrade_available("CSWpython"), "") # 'list_upgrades' function tests: 1 def test_list_upgrades(self): - ''' + """ Test if it list all available package upgrades on this system. - ''' - mock_run = MagicMock(return_value='A\t B\t SAME') + """ + mock_run = MagicMock(return_value="A\t B\t SAME") mock_ret = MagicMock(return_value=0) - with patch.dict(pkgutil.__salt__, {'cmd.run_stdout': mock_run, - 'cmd.retcode': mock_ret}): - with patch.object(salt.utils.pkg, 'clear_rtag', Mock()): - self.assertDictEqual(pkgutil.list_upgrades(), {'A': ' B'}) + with patch.dict( + pkgutil.__salt__, {"cmd.run_stdout": mock_run, "cmd.retcode": mock_ret} + ): + with patch.object(salt.utils.pkg, "clear_rtag", Mock()): + self.assertDictEqual(pkgutil.list_upgrades(), {"A": " B"}) # 'upgrade' function tests: 1 def test_upgrade(self): - ''' + """ Test if it upgrade all of the packages to the latest available version. - ''' - mock_run = MagicMock(return_value='A\t B\t SAME') + """ + mock_run = MagicMock(return_value="A\t B\t SAME") mock_ret = MagicMock(return_value=0) - mock_pkg = MagicMock(return_value='') - with patch.dict(pkgutil.__salt__, - {'cmd.run_stdout': mock_run, - 'cmd.retcode': mock_ret, - 'pkg_resource.stringify': mock_pkg, - 'pkg_resource.sort_pkglist': mock_pkg, - 'cmd.run_all': mock_ret, 'cmd.run': mock_run}): - with patch.dict(pkgutil.__context__, {'pkg.list_pkgs': mock_ret}): - with patch.object(salt.utils.pkg, 'clear_rtag', Mock()): + mock_pkg = MagicMock(return_value="") + with patch.dict( + pkgutil.__salt__, + { + "cmd.run_stdout": mock_run, + "cmd.retcode": mock_ret, + "pkg_resource.stringify": mock_pkg, + "pkg_resource.sort_pkglist": mock_pkg, + "cmd.run_all": mock_ret, + "cmd.run": mock_run, + }, + ): + with patch.dict(pkgutil.__context__, {"pkg.list_pkgs": mock_ret}): + with patch.object(salt.utils.pkg, "clear_rtag", Mock()): self.assertDictEqual(pkgutil.upgrade(), {}) # 'list_pkgs' function tests: 1 def test_list_pkgs(self): - ''' + """ Test if it list the packages currently installed as a dict. - ''' - mock_run = MagicMock(return_value='A\t B\t SAME') + """ + mock_run = MagicMock(return_value="A\t B\t SAME") mock_ret = MagicMock(return_value=True) - mock_pkg = MagicMock(return_value='') - with patch.dict(pkgutil.__salt__, - {'cmd.run_stdout': mock_run, - 'cmd.retcode': mock_ret, - 'pkg_resource.stringify': mock_pkg, - 'pkg_resource.sort_pkglist': mock_pkg, - 'cmd.run': mock_run}): - with patch.dict(pkgutil.__context__, {'pkg.list_pkgs': mock_ret}): - self.assertDictEqual(pkgutil.list_pkgs(versions_as_list=True, - removed=True), {}) + mock_pkg = MagicMock(return_value="") + with patch.dict( + pkgutil.__salt__, + { + "cmd.run_stdout": mock_run, + "cmd.retcode": mock_ret, + "pkg_resource.stringify": mock_pkg, + "pkg_resource.sort_pkglist": mock_pkg, + "cmd.run": mock_run, + }, + ): + with patch.dict(pkgutil.__context__, {"pkg.list_pkgs": mock_ret}): + self.assertDictEqual( + pkgutil.list_pkgs(versions_as_list=True, removed=True), {} + ) self.assertDictEqual(pkgutil.list_pkgs(), {}) - with patch.dict(pkgutil.__context__, {'pkg.list_pkgs': True}): + with patch.dict(pkgutil.__context__, {"pkg.list_pkgs": True}): self.assertTrue(pkgutil.list_pkgs(versions_as_list=True)) mock_pkg = MagicMock(return_value=True) - with patch.dict(pkgutil.__salt__, - {'pkg_resource.stringify': mock_pkg}): + with patch.dict(pkgutil.__salt__, {"pkg_resource.stringify": mock_pkg}): self.assertTrue(pkgutil.list_pkgs()) # 'version' function tests: 1 def test_version(self): - ''' + """ Test if it returns a version if the package is installed. - ''' + """ mock_ret = MagicMock(return_value=True) - with patch.dict(pkgutil.__salt__, {'pkg_resource.version': mock_ret}): - self.assertTrue(pkgutil.version('CSWpython')) + with patch.dict(pkgutil.__salt__, {"pkg_resource.version": mock_ret}): + self.assertTrue(pkgutil.version("CSWpython")) # 'latest_version' function tests: 1 def test_latest_version(self): - ''' + """ Test if it return the latest version of the named package available for upgrade or installation. - ''' - self.assertEqual(pkgutil.latest_version(), '') + """ + self.assertEqual(pkgutil.latest_version(), "") - mock_run_all = MagicMock(return_value='A\t B\t SAME') - mock_run = MagicMock(return_value={'stdout': ''}) + mock_run_all = MagicMock(return_value="A\t B\t SAME") + mock_run = MagicMock(return_value={"stdout": ""}) mock_ret = MagicMock(return_value=True) - mock_pkg = MagicMock(return_value='') - with patch.dict(pkgutil.__salt__, - {'cmd.retcode': mock_ret, - 'pkg_resource.stringify': mock_pkg, - 'pkg_resource.sort_pkglist': mock_pkg, - 'cmd.run_all': mock_run, 'cmd.run': mock_run_all}): - with patch.object(salt.utils.pkg, 'clear_rtag', Mock()): - self.assertEqual(pkgutil.latest_version('CSWpython'), '') + mock_pkg = MagicMock(return_value="") + with patch.dict( + pkgutil.__salt__, + { + "cmd.retcode": mock_ret, + "pkg_resource.stringify": mock_pkg, + "pkg_resource.sort_pkglist": mock_pkg, + "cmd.run_all": mock_run, + "cmd.run": mock_run_all, + }, + ): + with patch.object(salt.utils.pkg, "clear_rtag", Mock()): + self.assertEqual(pkgutil.latest_version("CSWpython"), "") - self.assertDictEqual(pkgutil.latest_version('CSWpython', 'Python'), - {'Python': '', 'CSWpython': ''}) + self.assertDictEqual( + pkgutil.latest_version("CSWpython", "Python"), + {"Python": "", "CSWpython": ""}, + ) # 'install' function tests: 1 def test_install(self): - ''' + """ Test if it install packages using the pkgutil tool. - ''' + """ mock_pkg = MagicMock(side_effect=MinionError) - with patch.dict(pkgutil.__salt__, - {'pkg_resource.parse_targets': mock_pkg}): + with patch.dict(pkgutil.__salt__, {"pkg_resource.parse_targets": mock_pkg}): self.assertRaises(CommandExecutionError, pkgutil.install) mock_ret = MagicMock(return_value=True) - mock_pkg = MagicMock(return_value=['']) - with patch.dict(pkgutil.__salt__, - {'pkg_resource.parse_targets': mock_pkg}): - with patch.dict(pkgutil.__context__, {'pkg.list_pkgs': mock_ret}): + mock_pkg = MagicMock(return_value=[""]) + with patch.dict(pkgutil.__salt__, {"pkg_resource.parse_targets": mock_pkg}): + with patch.dict(pkgutil.__context__, {"pkg.list_pkgs": mock_ret}): self.assertDictEqual(pkgutil.install(), {}) - mock_run = MagicMock(return_value='A\t B\t SAME') - mock_run_all = MagicMock(return_value={'stdout': ''}) + mock_run = MagicMock(return_value="A\t B\t SAME") + mock_run_all = MagicMock(return_value={"stdout": ""}) mock_pkg = MagicMock(return_value=[{"bar": "1.2.3"}]) - with patch.dict(pkgutil.__salt__, - {'pkg_resource.parse_targets': mock_pkg, - 'pkg_resource.stringify': mock_pkg, - 'pkg_resource.sort_pkglist': mock_pkg, - 'cmd.run_all': mock_run_all, 'cmd.run': mock_run}): - with patch.dict(pkgutil.__context__, {'pkg.list_pkgs': mock_ret}): - self.assertDictEqual(pkgutil.install - (pkgs='["foo", {"bar": "1.2.3"}]'), {}) + with patch.dict( + pkgutil.__salt__, + { + "pkg_resource.parse_targets": mock_pkg, + "pkg_resource.stringify": mock_pkg, + "pkg_resource.sort_pkglist": mock_pkg, + "cmd.run_all": mock_run_all, + "cmd.run": mock_run, + }, + ): + with patch.dict(pkgutil.__context__, {"pkg.list_pkgs": mock_ret}): + self.assertDictEqual( + pkgutil.install(pkgs='["foo", {"bar": "1.2.3"}]'), {} + ) # 'remove' function tests: 1 def test_remove(self): - ''' + """ Test if it remove a package and all its dependencies which are not in use by other packages. - ''' + """ mock_pkg = MagicMock(side_effect=MinionError) - with patch.dict(pkgutil.__salt__, - {'pkg_resource.parse_targets': mock_pkg}): + with patch.dict(pkgutil.__salt__, {"pkg_resource.parse_targets": mock_pkg}): self.assertRaises(CommandExecutionError, pkgutil.remove) mock_ret = MagicMock(return_value=True) - mock_run = MagicMock(return_value='A\t B\t SAME') - mock_run_all = MagicMock(return_value={'stdout': ''}) - mock_pkg = MagicMock(return_value=['']) - with patch.dict(pkgutil.__salt__, - {'pkg_resource.parse_targets': mock_pkg, - 'pkg_resource.stringify': mock_pkg, - 'pkg_resource.sort_pkglist': mock_pkg, - 'cmd.run_all': mock_run_all, 'cmd.run': mock_run}): - with patch.dict(pkgutil.__context__, {'pkg.list_pkgs': mock_ret}): + mock_run = MagicMock(return_value="A\t B\t SAME") + mock_run_all = MagicMock(return_value={"stdout": ""}) + mock_pkg = MagicMock(return_value=[""]) + with patch.dict( + pkgutil.__salt__, + { + "pkg_resource.parse_targets": mock_pkg, + "pkg_resource.stringify": mock_pkg, + "pkg_resource.sort_pkglist": mock_pkg, + "cmd.run_all": mock_run_all, + "cmd.run": mock_run, + }, + ): + with patch.dict(pkgutil.__context__, {"pkg.list_pkgs": mock_ret}): self.assertDictEqual(pkgutil.remove(), {}) mock_pkg = MagicMock(return_value=[{"bar": "1.2.3"}]) - with patch.dict(pkgutil.__salt__, - {'pkg_resource.parse_targets': mock_pkg, - 'pkg_resource.stringify': mock_pkg, - 'pkg_resource.sort_pkglist': mock_pkg, - 'cmd.run_all': mock_run_all, 'cmd.run': mock_run}): - with patch.dict(pkgutil.__context__, {'pkg.list_pkgs': mock_ret}): - with patch.object(pkgutil, 'list_pkgs', - return_value={"bar": "1.2.3"}): - self.assertDictEqual(pkgutil.remove(pkgs='["foo", "bar"]'), - {}) + with patch.dict( + pkgutil.__salt__, + { + "pkg_resource.parse_targets": mock_pkg, + "pkg_resource.stringify": mock_pkg, + "pkg_resource.sort_pkglist": mock_pkg, + "cmd.run_all": mock_run_all, + "cmd.run": mock_run, + }, + ): + with patch.dict(pkgutil.__context__, {"pkg.list_pkgs": mock_ret}): + with patch.object(pkgutil, "list_pkgs", return_value={"bar": "1.2.3"}): + self.assertDictEqual(pkgutil.remove(pkgs='["foo", "bar"]'), {}) # 'purge' function tests: 1 def test_purge(self): - ''' + """ Test if it package purges are not supported, this function is identical to ``remove()``. - ''' + """ mock_pkg = MagicMock(side_effect=MinionError) - with patch.dict(pkgutil.__salt__, - {'pkg_resource.parse_targets': mock_pkg}): + with patch.dict(pkgutil.__salt__, {"pkg_resource.parse_targets": mock_pkg}): self.assertRaises(CommandExecutionError, pkgutil.purge) diff --git a/tests/unit/modules/test_portage_config.py b/tests/unit/modules/test_portage_config.py index acca447ac44..3e5fc88162d 100644 --- a/tests/unit/modules/test_portage_config.py +++ b/tests/unit/modules/test_portage_config.py @@ -1,23 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Ryan Lewis (ryansname@gmail.com) tests.unit.modules.portage_flags ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import re -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch -import salt.utils.files +import re # Import salt libs import salt.modules.portage_config as portage_config +import salt.utils.files +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class PortageConfigTestCase(TestCase, LoaderModuleMockMixin): @@ -27,20 +28,20 @@ class PortageConfigTestCase(TestCase, LoaderModuleMockMixin): self.repo = None def __call__(self, atom, *_, **__): - if atom == '#' or isinstance(atom, MagicMock): + if atom == "#" or isinstance(atom, MagicMock): self.repo = None self.cp = None return self # extract (and remove) repo - atom, self.repo = atom.split('::') if '::' in atom else (atom, None) + atom, self.repo = atom.split("::") if "::" in atom else (atom, None) # remove '>, >=, <=, =, ~' etc. - atom = re.sub(r'[<>~+=]', '', atom) + atom = re.sub(r"[<>~+=]", "", atom) # remove slots - atom = re.sub(r':[0-9][^:]*', '', atom) + atom = re.sub(r":[0-9][^:]*", "", atom) # remove version - atom = re.sub(r'-[0-9][\.0-9]*', '', atom) + atom = re.sub(r"-[0-9][\.0-9]*", "", atom) self.cp = atom return self @@ -48,6 +49,7 @@ class PortageConfigTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): try: import portage # pylint: disable=unused-import + return {} except ImportError: dummy_atom = self.DummyAtom() @@ -55,67 +57,76 @@ class PortageConfigTestCase(TestCase, LoaderModuleMockMixin): self.portage.dep.Atom = MagicMock(side_effect=dummy_atom) self.portage.dep_getkey = MagicMock(side_effect=lambda x: dummy_atom(x).cp) self.portage.exception.InvalidAtom = Exception - self.addCleanup(delattr, self, 'portage') - return {portage_config: {'portage': self.portage}} + self.addCleanup(delattr, self, "portage") + return {portage_config: {"portage": self.portage}} def test_get_config_file_wildcards(self): pairs = [ - ('*/*::repo', '/etc/portage/package.mask/repo'), - ('*/pkg::repo', '/etc/portage/package.mask/pkg'), - ('cat/*', '/etc/portage/package.mask/cat_'), - ('cat/pkg', '/etc/portage/package.mask/cat/pkg'), - ('cat/pkg::repo', '/etc/portage/package.mask/cat/pkg'), + ("*/*::repo", "/etc/portage/package.mask/repo"), + ("*/pkg::repo", "/etc/portage/package.mask/pkg"), + ("cat/*", "/etc/portage/package.mask/cat_"), + ("cat/pkg", "/etc/portage/package.mask/cat/pkg"), + ("cat/pkg::repo", "/etc/portage/package.mask/cat/pkg"), ] for (atom, expected) in pairs: - self.assertEqual(portage_config._get_config_file('mask', atom), expected) + self.assertEqual(portage_config._get_config_file("mask", atom), expected) def test_enforce_nice_config(self): atoms = [ - ('*/*::repo', 'repo'), - ('*/pkg1::repo', 'pkg1'), - ('cat/*', 'cat_'), - ('cat/pkg2', 'cat/pkg2'), - ('cat/pkg3::repo', 'cat/pkg3'), - ('<cat/pkg4-0.0.0.0', 'cat/pkg4'), - ('>cat/pkg5-0.0.0.0:0', 'cat/pkg5'), - ('>cat/pkg6-0.0.0.0:0::repo', 'cat/pkg6'), - ('<=cat/pkg7-0.0.0.0', 'cat/pkg7'), - ('=cat/pkg8-0.0.0.0', 'cat/pkg8'), + ("*/*::repo", "repo"), + ("*/pkg1::repo", "pkg1"), + ("cat/*", "cat_"), + ("cat/pkg2", "cat/pkg2"), + ("cat/pkg3::repo", "cat/pkg3"), + ("<cat/pkg4-0.0.0.0", "cat/pkg4"), + (">cat/pkg5-0.0.0.0:0", "cat/pkg5"), + (">cat/pkg6-0.0.0.0:0::repo", "cat/pkg6"), + ("<=cat/pkg7-0.0.0.0", "cat/pkg7"), + ("=cat/pkg8-0.0.0.0", "cat/pkg8"), ] supported = [ - ('accept_keywords', ['~amd64']), - ('env', ['glibc.conf']), - ('license', ['LICENCE1', 'LICENCE2']), - ('mask', ['']), - ('properties', ['* -interactive']), - ('unmask', ['']), - ('use', ['apple', '-banana', 'ananas', 'orange']), + ("accept_keywords", ["~amd64"]), + ("env", ["glibc.conf"]), + ("license", ["LICENCE1", "LICENCE2"]), + ("mask", [""]), + ("properties", ["* -interactive"]), + ("unmask", [""]), + ("use", ["apple", "-banana", "ananas", "orange"]), ] - base_path = RUNTIME_VARS.TMP + '/package.{0}' + base_path = RUNTIME_VARS.TMP + "/package.{0}" def make_line(atom, addition): - return atom + (' ' + addition if addition != '' else '') + '\n' + return atom + (" " + addition if addition != "" else "") + "\n" for typ, additions in supported: path = base_path.format(typ) - with salt.utils.files.fopen(path, 'a') as fh: + with salt.utils.files.fopen(path, "a") as fh: for atom, _ in atoms: for addition in additions: line = make_line(atom, addition) - fh.write('# comment for: ' + line) + fh.write("# comment for: " + line) fh.write(line) - with patch.object(portage_config, 'BASE_PATH', base_path): - with patch.object(portage_config, '_merge_flags', lambda l1, l2, _: list(set(l1 + l2))): + with patch.object(portage_config, "BASE_PATH", base_path): + with patch.object( + portage_config, "_merge_flags", lambda l1, l2, _: list(set(l1 + l2)) + ): portage_config.enforce_nice_config() for typ, additions in supported: for atom, file_name in atoms: - with salt.utils.files.fopen(base_path.format(typ) + "/" + file_name, 'r') as fh: + with salt.utils.files.fopen( + base_path.format(typ) + "/" + file_name, "r" + ) as fh: for line in fh: - self.assertTrue(atom in line, msg="'{}' not in '{}'".format(addition, line)) + self.assertTrue( + atom in line, msg="'{}' not in '{}'".format(addition, line) + ) for addition in additions: - self.assertTrue(addition in line, msg="'{}' not in '{}'".format(addition, line)) + self.assertTrue( + addition in line, + msg="'{}' not in '{}'".format(addition, line), + ) diff --git a/tests/unit/modules/test_postfix.py b/tests/unit/modules/test_postfix.py index d4e261483fe..4fe3f550e98 100644 --- a/tests/unit/modules/test_postfix.py +++ b/tests/unit/modules/test_postfix.py @@ -1,131 +1,133 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.modules.postfix as postfix +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PostfixTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.postfix - ''' + """ + def setup_loader_modules(self): return {postfix: {}} def test_show_master(self): - ''' + """ Test for return a dict of active config values - ''' - with patch.object(postfix, '_parse_master', - return_value=({'A': 'a'}, ['b'])): - self.assertDictEqual(postfix.show_master('path'), {'A': 'a'}) + """ + with patch.object(postfix, "_parse_master", return_value=({"A": "a"}, ["b"])): + self.assertDictEqual(postfix.show_master("path"), {"A": "a"}) def test_set_master(self): - ''' + """ Test for set a single config value in the master.cf file - ''' - with patch.object(postfix, '_parse_master', - return_value=({'A': 'a'}, ['b'])): - with patch.object(postfix, '_write_conf', return_value=None): - self.assertTrue(postfix.set_master('a', 'b')) + """ + with patch.object(postfix, "_parse_master", return_value=({"A": "a"}, ["b"])): + with patch.object(postfix, "_write_conf", return_value=None): + self.assertTrue(postfix.set_master("a", "b")) def test_show_main(self): - ''' + """ Test for return a dict of active config values - ''' - with patch.object(postfix, '_parse_main', - return_value=({'A': 'a'}, ['b'])): - self.assertDictEqual(postfix.show_main('path'), {'A': 'a'}) + """ + with patch.object(postfix, "_parse_main", return_value=({"A": "a"}, ["b"])): + self.assertDictEqual(postfix.show_main("path"), {"A": "a"}) def test_set_main(self): - ''' + """ Test for set a single config value in the master.cf file - ''' - with patch.object(postfix, '_parse_main', - return_value=({'A': 'a'}, ['b'])): - with patch.object(postfix, '_write_conf', return_value=None): - self.assertTrue(postfix.set_main('key', 'value')) + """ + with patch.object(postfix, "_parse_main", return_value=({"A": "a"}, ["b"])): + with patch.object(postfix, "_write_conf", return_value=None): + self.assertTrue(postfix.set_main("key", "value")) def test_show_queue(self): - ''' + """ Test for show contents of the mail queue - ''' - with patch.dict(postfix.__salt__, {'cmd.run': - MagicMock(return_value='A\nB')}): + """ + with patch.dict(postfix.__salt__, {"cmd.run": MagicMock(return_value="A\nB")}): self.assertEqual(postfix.show_queue(), []) def test_delete(self): - ''' + """ Test for delete message(s) from the mail queue - ''' - with patch.object(postfix, 'show_queue', return_value={}): - self.assertDictEqual(postfix.delete('queue_id'), - {'result': False, 'message': - 'No message in queue with ID queue_id'}) + """ + with patch.object(postfix, "show_queue", return_value={}): + self.assertDictEqual( + postfix.delete("queue_id"), + {"result": False, "message": "No message in queue with ID queue_id"}, + ) - with patch.dict(postfix.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 0})}): - self.assertDictEqual(postfix.delete('ALL'), - {'result': True, 'message': - 'Successfully removed all messages'}) + with patch.dict( + postfix.__salt__, {"cmd.run_all": MagicMock(return_value={"retcode": 0})} + ): + self.assertDictEqual( + postfix.delete("ALL"), + {"result": True, "message": "Successfully removed all messages"}, + ) def test_hold(self): - ''' + """ Test for set held message(s) in the mail queue to unheld - ''' - with patch.object(postfix, 'show_queue', return_value={}): - self.assertDictEqual(postfix.hold('queue_id'), - {'result': False, 'message': - 'No message in queue with ID queue_id'}) + """ + with patch.object(postfix, "show_queue", return_value={}): + self.assertDictEqual( + postfix.hold("queue_id"), + {"result": False, "message": "No message in queue with ID queue_id"}, + ) - with patch.dict(postfix.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 0})}): - self.assertDictEqual(postfix.hold('ALL'), - {'result': True, 'message': - 'Successfully placed all messages on hold'}) + with patch.dict( + postfix.__salt__, {"cmd.run_all": MagicMock(return_value={"retcode": 0})} + ): + self.assertDictEqual( + postfix.hold("ALL"), + {"result": True, "message": "Successfully placed all messages on hold"}, + ) def test_unhold(self): - ''' + """ Test for put message(s) on hold from the mail queue - ''' - with patch.object(postfix, 'show_queue', return_value={}): - self.assertDictEqual(postfix.unhold('queue_id'), - {'result': False, 'message': - 'No message in queue with ID queue_id'}) + """ + with patch.object(postfix, "show_queue", return_value={}): + self.assertDictEqual( + postfix.unhold("queue_id"), + {"result": False, "message": "No message in queue with ID queue_id"}, + ) - with patch.dict(postfix.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 0})}): - self.assertDictEqual(postfix.unhold('ALL'), - {'result': True, 'message': - 'Successfully set all message as unheld'}) + with patch.dict( + postfix.__salt__, {"cmd.run_all": MagicMock(return_value={"retcode": 0})} + ): + self.assertDictEqual( + postfix.unhold("ALL"), + {"result": True, "message": "Successfully set all message as unheld"}, + ) def test_requeue(self): - ''' + """ Test for requeue message(s) in the mail queue - ''' - with patch.object(postfix, 'show_queue', return_value={}): - self.assertDictEqual(postfix.requeue('queue_id'), - {'result': False, 'message': - 'No message in queue with ID queue_id'}) + """ + with patch.object(postfix, "show_queue", return_value={}): + self.assertDictEqual( + postfix.requeue("queue_id"), + {"result": False, "message": "No message in queue with ID queue_id"}, + ) - with patch.dict(postfix.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 0})}): - self.assertDictEqual(postfix.requeue('ALL'), - {'result': True, 'message': - 'Successfully requeued all messages'}) + with patch.dict( + postfix.__salt__, {"cmd.run_all": MagicMock(return_value={"retcode": 0})} + ): + self.assertDictEqual( + postfix.requeue("ALL"), + {"result": True, "message": "Successfully requeued all messages"}, + ) diff --git a/tests/unit/modules/test_postgres.py b/tests/unit/modules/test_postgres.py index 9e177a8df97..116b528ea21 100644 --- a/tests/unit/modules/test_postgres.py +++ b/tests/unit/modules/test_postgres.py @@ -2,306 +2,507 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import datetime -import re -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import Mock, patch, call +import datetime +import logging +import re # Import salt libs import salt.modules.postgres as postgres from salt.exceptions import SaltInvocationError +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock, call, patch +from tests.support.unit import TestCase + test_list_db_csv = ( - 'Name,Owner,Encoding,Collate,Ctype,Access privileges,Tablespace\n' - 'template1,postgres,LATIN1,en_US,en_US' + "Name,Owner,Encoding,Collate,Ctype,Access privileges,Tablespace\n" + "template1,postgres,LATIN1,en_US,en_US" ',"{=c/postgres,postgres=CTc/postgres}",pg_default\n' - 'template0,postgres,LATIN1,en_US,en_US' + "template0,postgres,LATIN1,en_US,en_US" ',"{=c/postgres,postgres=CTc/postgres}",pg_default\n' - 'postgres,postgres,LATIN1,en_US,en_US,,pg_default\n' - 'test_db,postgres,LATIN1,en_US,en_US,,pg_default' + "postgres,postgres,LATIN1,en_US,en_US,,pg_default\n" + "test_db,postgres,LATIN1,en_US,en_US,,pg_default" ) test_list_schema_csv = ( - 'name,owner,acl\n' + "name,owner,acl\n" 'public,postgres,"{postgres=UC/postgres,=UC/postgres}"\n' 'pg_toast,postgres,""' ) -test_list_language_csv = ( - 'Name\n' - 'internal\n' - 'c\n' - 'sql\n' - 'plpgsql\n' -) +test_list_language_csv = "Name\n" "internal\n" "c\n" "sql\n" "plpgsql\n" test_privileges_list_table_csv = ( - 'name\n' + "name\n" '"{baruwatest=arwdDxt/baruwatest,bayestest=arwd/baruwatest,baruwa=a*r*w*d*D*x*t*/baruwatest}"\n' ) test_privileges_list_group_csv = ( - 'rolname,admin_option\n' - 'baruwa,f\n' - 'baruwatest2,t\n' - 'baruwatest,f\n' + "rolname,admin_option\n" "baruwa,f\n" "baruwatest2,t\n" "baruwatest,f\n" ) +log = logging.getLogger(__name__) + class PostgresTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/pgsql')) + patcher = patch("salt.utils.path.which", Mock(return_value="/usr/bin/pgsql")) patcher.start() self.addCleanup(patcher.stop) return { postgres: { - '__grains__': {'os_family': 'Linux'}, - '__salt__': { - 'config.option': Mock(), - 'cmd.run_all': Mock(), - 'file.chown': Mock(), - 'file.remove': Mock(), - } + "__grains__": {"os_family": "Linux"}, + "__salt__": { + "config.option": Mock(), + "cmd.run_all": Mock(), + "file.chown": Mock(), + "file.remove": Mock(), + }, } } def test_run_psql(self): postgres._run_psql('echo "hi"') - cmd = postgres.__salt__['cmd.run_all'] + cmd = postgres.__salt__["cmd.run_all"] - self.assertEqual('postgres', cmd.call_args[1]['runas']) + self.assertEqual("postgres", cmd.call_args[1]["runas"]) def test_db_alter(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - postgres.db_alter('dbname', - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='foo', - tablespace='testspace', - owner='otheruser', - runas='foo') - postgres._run_psql.assert_has_calls([ - call(['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'maint_db', - '-c', 'ALTER DATABASE "dbname" OWNER TO "otheruser"'], - host='testhost', user='testuser', - password='foo', runas='foo', port='testport'), - call(['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'maint_db', - '-c', 'ALTER DATABASE "dbname" SET TABLESPACE "testspace"'], - host='testhost', user='testuser', - password='foo', runas='foo', port='testport') - ]) + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + postgres.db_alter( + "dbname", + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_db", + password="foo", + tablespace="testspace", + owner="otheruser", + runas="foo", + ) + postgres._run_psql.assert_has_calls( + [ + call( + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + 'ALTER DATABASE "dbname" OWNER TO "otheruser"', + ], + host="testhost", + user="testuser", + password="foo", + runas="foo", + port="testport", + ), + call( + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + 'ALTER DATABASE "dbname" SET TABLESPACE "testspace"', + ], + host="testhost", + user="testuser", + password="foo", + runas="foo", + port="testport", + ), + ] + ) def test_db_alter_owner_recurse(self): - with patch('salt.modules.postgres.owner_to', - Mock(return_value={'retcode': None})): - postgres.db_alter('dbname', - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='foo', - tablespace='testspace', - owner='otheruser', - owner_recurse=True, - runas='foo') - postgres.owner_to.assert_called_once_with('dbname', - 'otheruser', - user='testuser', - host='testhost', - port='testport', - password='foo', - runas='foo') + with patch( + "salt.modules.postgres.owner_to", Mock(return_value={"retcode": None}) + ): + postgres.db_alter( + "dbname", + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_db", + password="foo", + tablespace="testspace", + owner="otheruser", + owner_recurse=True, + runas="foo", + ) + postgres.owner_to.assert_called_once_with( + "dbname", + "otheruser", + user="testuser", + host="testhost", + port="testport", + password="foo", + runas="foo", + ) def test_db_create(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): postgres.db_create( - 'dbname', - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='foo', - tablespace='testspace', - owner='otheruser', - runas='foo' + "dbname", + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_db", + password="foo", + tablespace="testspace", + owner="otheruser", + runas="foo", ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'maint_db', - '-c', 'CREATE DATABASE "dbname" WITH TABLESPACE = "testspace" ' - 'OWNER = "otheruser"'], - host='testhost', user='testuser', - password='foo', runas='foo', port='testport') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + 'CREATE DATABASE "dbname" WITH TABLESPACE = "testspace" ' + 'OWNER = "otheruser"', + ], + host="testhost", + user="testuser", + password="foo", + runas="foo", + port="testport", + ) def test_db_create_empty_string_param(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - postgres.db_create('dbname', lc_collate='', encoding='utf8', - user='testuser', host='testhost', port=1234, - maintenance_db='maint_db', password='foo') + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + postgres.db_create( + "dbname", + lc_collate="", + encoding="utf8", + user="testuser", + host="testhost", + port=1234, + maintenance_db="maint_db", + password="foo", + ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', '1234', '--dbname', 'maint_db', '-c', - 'CREATE DATABASE "dbname" WITH ENCODING = \'utf8\' ' - 'LC_COLLATE = \'\''], host='testhost', password='foo', - port=1234, runas=None, user='testuser') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "1234", + "--dbname", + "maint_db", + "-c", + "CREATE DATABASE \"dbname\" WITH ENCODING = 'utf8' " + "LC_COLLATE = ''", + ], + host="testhost", + password="foo", + port=1234, + runas=None, + user="testuser", + ) def test_db_create_with_trivial_sql_injection(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): self.assertRaises( - SaltInvocationError, - postgres.db_create, - 'dbname', lc_collate="foo' ENCODING='utf8") + SaltInvocationError, + postgres.db_create, + "dbname", + lc_collate="foo' ENCODING='utf8", + ) def test_db_exists(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0, - 'stdout': test_list_db_csv})): + with patch( + "salt.modules.postgres._run_psql", + Mock(return_value={"retcode": 0, "stdout": test_list_db_csv}), + ): ret = postgres.db_exists( - 'test_db', - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='foo', - runas='foo' + "test_db", + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_db", + password="foo", + runas="foo", ) self.assertTrue(ret) def test_db_list(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0, - 'stdout': test_list_db_csv})): + with patch( + "salt.modules.postgres._run_psql", + Mock(return_value={"retcode": 0, "stdout": test_list_db_csv}), + ): ret = postgres.db_list( - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='foo', - runas='foo' + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_db", + password="foo", + runas="foo", + ) + self.assertDictEqual( + ret, + { + "test_db": { + "Encoding": "LATIN1", + "Ctype": "en_US", + "Tablespace": "pg_default", + "Collate": "en_US", + "Owner": "postgres", + "Access privileges": "", + }, + "template1": { + "Encoding": "LATIN1", + "Ctype": "en_US", + "Tablespace": "pg_default", + "Collate": "en_US", + "Owner": "postgres", + "Access privileges": ("{=c/postgres,postgres=CTc/postgres}"), + }, + "template0": { + "Encoding": "LATIN1", + "Ctype": "en_US", + "Tablespace": "pg_default", + "Collate": "en_US", + "Owner": "postgres", + "Access privileges": ("{=c/postgres,postgres=CTc/postgres}"), + }, + "postgres": { + "Encoding": "LATIN1", + "Ctype": "en_US", + "Tablespace": "pg_default", + "Collate": "en_US", + "Owner": "postgres", + "Access privileges": "", + }, + }, ) - self.assertDictEqual(ret, { - 'test_db': {'Encoding': 'LATIN1', 'Ctype': 'en_US', - 'Tablespace': 'pg_default', 'Collate': 'en_US', - 'Owner': 'postgres', 'Access privileges': ''}, - 'template1': {'Encoding': 'LATIN1', 'Ctype': 'en_US', - 'Tablespace': 'pg_default', 'Collate': 'en_US', - 'Owner': 'postgres', - 'Access privileges': ( - '{=c/postgres,postgres=CTc/postgres}' - )}, - 'template0': {'Encoding': 'LATIN1', 'Ctype': 'en_US', - 'Tablespace': 'pg_default', 'Collate': 'en_US', - 'Owner': 'postgres', - 'Access privileges': ( - '{=c/postgres,postgres=CTc/postgres}' - )}, - 'postgres': {'Encoding': 'LATIN1', 'Ctype': 'en_US', - 'Tablespace': 'pg_default', 'Collate': 'en_US', - 'Owner': 'postgres', 'Access privileges': ''}}) def test_db_remove(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): postgres.db_remove( - 'test_db', - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='foo', - runas='foo' + "test_db", + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_db", + password="foo", + runas="foo", ) - postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'maint_db', - '-c', 'DROP DATABASE "test_db"'], - host='testhost', user='testuser', - password='foo', runas='foo', port='testport') + + calls = ( + call( + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + 'REVOKE CONNECT ON DATABASE "test_db" FROM public;', + ], + host="testhost", + password="foo", + port="testport", + runas="foo", + user="testuser", + ), + call( + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + "SELECT pid, pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'test_db' AND pid <> pg_backend_pid();", + ], + host="testhost", + password="foo", + port="testport", + runas="foo", + user="testuser", + ), + call( + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + 'DROP DATABASE "test_db";', + ], + host="testhost", + password="foo", + port="testport", + runas="foo", + user="testuser", + ), + ) + + postgres._run_psql.assert_has_calls(calls, any_order=True) def test_group_create(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.user_exists', Mock(return_value=False)): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.user_exists", Mock(return_value=False)): postgres.group_create( - 'testgroup', - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='foo', + "testgroup", + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_db", + password="foo", createdb=False, encrypted=False, superuser=False, replication=False, - rolepassword='testrolepass', - groups='testgroup', - runas='foo' + rolepassword="testrolepass", + groups="testgroup", + runas="foo", ) # postgres._run_psql.call_args[0][0] will contain the list of CLI args. # The first 14 elements of this list are initial args used in all (or # virtually all) commands run through _run_psql(), so the actual SQL # query will be in the 15th argument. self.assertTrue( - postgres._run_psql.call_args[0][0][14].startswith('CREATE ROLE') + postgres._run_psql.call_args[0][0][14].startswith("CREATE ROLE") ) def test_group_remove(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.user_exists', Mock(return_value=True)): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.user_exists", Mock(return_value=True)): postgres.group_remove( - 'testgroup', - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='foo', - runas='foo' + "testgroup", + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_db", + password="foo", + runas="foo", ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'maint_db', - '-c', 'DROP ROLE "testgroup"'], - host='testhost', user='testuser', - password='foo', runas='foo', port='testport') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + 'DROP ROLE "testgroup"', + ], + host="testhost", + user="testuser", + password="foo", + runas="foo", + port="testport", + ) def test_group_update(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.role_get', - Mock(return_value={'superuser': False})): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.role_get", + Mock(return_value={"superuser": False}), + ): postgres.group_update( - 'testgroup', + "testgroup", user='"testuser"', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='foo', + host="testhost", + port="testport", + maintenance_db="maint_db", + password="foo", createdb=False, encrypted=False, replication=False, - rolepassword='test_role_pass', - groups='testgroup', - runas='foo' + rolepassword="test_role_pass", + groups="testgroup", + runas="foo", ) # postgres._run_psql.call_args[0][0] will contain the list of CLI args. # The first 14 elements of this list are initial args used in all (or @@ -310,31 +511,32 @@ class PostgresTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue( re.match( 'ALTER.* "testgroup" .* UNENCRYPTED PASSWORD', - postgres._run_psql.call_args[0][0][14] + postgres._run_psql.call_args[0][0][14], ) ) def test_user_create(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.user_exists', Mock(return_value=False)): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.user_exists", Mock(return_value=False)): postgres.user_create( - 'testuser', - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_test', - password='test_pass', + "testuser", + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_test", + password="test_pass", login=True, createdb=False, createroles=False, encrypted=False, superuser=False, replication=False, - rolepassword='test_role_pass', - valid_until='2042-07-01', - groups='test_groups', - runas='foo' + rolepassword="test_role_pass", + valid_until="2042-07-01", + groups="test_groups", + runas="foo", ) # postgres._run_psql.call_args[0][0] will contain the list of CLI args. # The first 14 elements of this list are initial args used in all (or @@ -343,133 +545,179 @@ class PostgresTestCase(TestCase, LoaderModuleMockMixin): call = postgres._run_psql.call_args[0][0][14] self.assertTrue(re.match('CREATE ROLE "testuser"', call)) for i in ( - 'INHERIT', 'NOCREATEDB', 'NOCREATEROLE', 'NOSUPERUSER', - 'NOREPLICATION', 'LOGIN', 'UNENCRYPTED', 'PASSWORD', - 'VALID UNTIL', + "INHERIT", + "NOCREATEDB", + "NOCREATEROLE", + "NOSUPERUSER", + "NOREPLICATION", + "LOGIN", + "UNENCRYPTED", + "PASSWORD", + "VALID UNTIL", ): - self.assertTrue(i in call, '{0} not in {1}'.format(i, call)) + self.assertTrue(i in call, "{0} not in {1}".format(i, call)) def test_user_exists(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.version', - Mock(return_value='9.1')): - with patch('salt.modules.postgres.psql_query', - Mock(return_value=[ - { - 'name': 'test_user', - 'superuser': 't', - 'inherits privileges': 't', - 'can create roles': 't', - 'can create databases': 't', - 'can update system catalogs': 't', - 'can login': 't', - 'replication': None, - 'password': 'test_password', - 'connections': '-1', - 'groups': '', - 'expiry time': '', - 'defaults variables': None - }])): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.version", Mock(return_value="9.1")): + with patch( + "salt.modules.postgres.psql_query", + Mock( + return_value=[ + { + "name": "test_user", + "superuser": "t", + "inherits privileges": "t", + "can create roles": "t", + "can create databases": "t", + "can update system catalogs": "t", + "can login": "t", + "replication": None, + "password": "test_password", + "connections": "-1", + "groups": "", + "expiry time": "", + "defaults variables": None, + } + ] + ), + ): ret = postgres.user_exists( - 'test_user', - user='test_user', - host='test_host', - port='test_port', - maintenance_db='maint_db', - password='test_password', - runas='foo' + "test_user", + user="test_user", + host="test_host", + port="test_port", + maintenance_db="maint_db", + password="test_password", + runas="foo", ) self.assertTrue(ret) def test_user_list(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.version', - Mock(return_value='9.1')): - with patch('salt.modules.postgres.psql_query', - Mock(return_value=[ - { - 'name': 'test_user', - 'superuser': 't', - 'inherits privileges': 't', - 'can create roles': 't', - 'can create databases': 't', - 'can update system catalogs': 't', - 'can login': 't', - 'replication': None, - 'connections': '-1', - 'groups': '', - 'expiry time': '2017-08-16 08:57:46', - 'defaults variables': None - }])): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.version", Mock(return_value="9.1")): + with patch( + "salt.modules.postgres.psql_query", + Mock( + return_value=[ + { + "name": "test_user", + "superuser": "t", + "inherits privileges": "t", + "can create roles": "t", + "can create databases": "t", + "can update system catalogs": "t", + "can login": "t", + "replication": None, + "connections": "-1", + "groups": "", + "expiry time": "2017-08-16 08:57:46", + "defaults variables": None, + } + ] + ), + ): ret = postgres.user_list( - 'test_user', - host='test_host', - port='test_port', - maintenance_db='maint_db', - password='test_password', - runas='foo' + "test_user", + host="test_host", + port="test_port", + maintenance_db="maint_db", + password="test_password", + runas="foo", ) - self.assertDictEqual(ret, { - 'test_user': {'superuser': True, - 'defaults variables': None, - 'can create databases': True, - 'can create roles': True, - 'connections': None, - 'replication': None, - 'expiry time': datetime.datetime( - 2017, 8, 16, 8, 57, 46), - 'can login': True, - 'can update system catalogs': True, - 'groups': [], - 'inherits privileges': True}}) + self.assertDictEqual( + ret, + { + "test_user": { + "superuser": True, + "defaults variables": None, + "can create databases": True, + "can create roles": True, + "connections": None, + "replication": None, + "expiry time": datetime.datetime( + 2017, 8, 16, 8, 57, 46 + ), + "can login": True, + "can update system catalogs": True, + "groups": [], + "inherits privileges": True, + } + }, + ) def test_user_remove(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.version', Mock(return_value='9.1')): - with patch('salt.modules.postgres.user_exists', Mock(return_value=True)): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.version", Mock(return_value="9.1")): + with patch( + "salt.modules.postgres.user_exists", Mock(return_value=True) + ): postgres.user_remove( - 'testuser', - user='testuser', - host='testhost', - port='testport', - maintenance_db='maint_db', - password='testpassword', - runas='foo' + "testuser", + user="testuser", + host="testhost", + port="testport", + maintenance_db="maint_db", + password="testpassword", + runas="foo", ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'maint_db', - '-c', 'DROP ROLE "testuser"'], - host='testhost', port='testport', user='testuser', - password='testpassword', runas='foo') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + 'DROP ROLE "testuser"', + ], + host="testhost", + port="testport", + user="testuser", + password="testpassword", + runas="foo", + ) def test_user_update(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.role_get', - Mock(return_value={'superuser': False})): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.role_get", + Mock(return_value={"superuser": False}), + ): postgres.user_update( - 'test_username', - user='test_user', - host='test_host', - port='test_port', - maintenance_db='test_maint', - password='test_pass', + "test_username", + user="test_user", + host="test_host", + port="test_port", + maintenance_db="test_maint", + password="test_pass", createdb=False, createroles=False, encrypted=False, inherit=True, login=True, replication=False, - rolepassword='test_role_pass', - valid_until='2017-07-01', - groups='test_groups', - runas='foo' + rolepassword="test_role_pass", + valid_until="2017-07-01", + groups="test_groups", + runas="foo", ) # postgres._run_psql.call_args[0][0] will contain the list of CLI args. # The first 14 elements of this list are initial args used in all (or @@ -478,34 +726,37 @@ class PostgresTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue( re.match( 'ALTER ROLE "test_username" WITH INHERIT NOCREATEDB ' - 'NOCREATEROLE NOREPLICATION LOGIN ' - 'UNENCRYPTED PASSWORD [\'"]{0,5}test_role_pass[\'"]{0,5} ' - 'VALID UNTIL \'2017-07-01\';' + "NOCREATEROLE NOREPLICATION LOGIN " + "UNENCRYPTED PASSWORD ['\"]{0,5}test_role_pass['\"]{0,5} " + "VALID UNTIL '2017-07-01';" ' GRANT "test_groups" TO "test_username"', - postgres._run_psql.call_args[0][0][14] + postgres._run_psql.call_args[0][0][14], ) ) def test_user_update2(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.role_get', - Mock(return_value={'superuser': False})): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.role_get", + Mock(return_value={"superuser": False}), + ): postgres.user_update( - 'test_username', - user='test_user', - host='test_host', - port='test_port', - maintenance_db='test_maint', - password='test_pass', + "test_username", + user="test_user", + host="test_host", + port="test_port", + maintenance_db="test_maint", + password="test_pass", createdb=False, createroles=True, encrypted=False, inherit=True, login=True, replication=False, - groups='test_groups', - runas='foo' + groups="test_groups", + runas="foo", ) # postgres._run_psql.call_args[0][0] will contain the list of CLI args. # The first 14 elements of this list are initial args used in all (or @@ -514,24 +765,27 @@ class PostgresTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue( re.match( 'ALTER ROLE "test_username" WITH INHERIT NOCREATEDB ' - 'CREATEROLE NOREPLICATION LOGIN;' + "CREATEROLE NOREPLICATION LOGIN;" ' GRANT "test_groups" TO "test_username"', - postgres._run_psql.call_args[0][0][14] + postgres._run_psql.call_args[0][0][14], ) ) def test_user_update3(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.role_get', - Mock(return_value={'superuser': False})): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.role_get", + Mock(return_value={"superuser": False}), + ): postgres.user_update( - 'test_username', - user='test_user', - host='test_host', - port='test_port', - maintenance_db='test_maint', - password='test_pass', + "test_username", + user="test_user", + host="test_host", + port="test_port", + maintenance_db="test_maint", + password="test_pass", createdb=False, createroles=True, encrypted=False, @@ -539,8 +793,8 @@ class PostgresTestCase(TestCase, LoaderModuleMockMixin): login=True, rolepassword=False, replication=False, - groups='test_groups', - runas='foo' + groups="test_groups", + runas="foo", ) # postgres._run_psql.call_args[0][0] will contain the list of CLI args. # The first 14 elements of this list are initial args used in all (or @@ -549,33 +803,36 @@ class PostgresTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue( re.match( 'ALTER ROLE "test_username" WITH INHERIT NOCREATEDB ' - 'CREATEROLE NOREPLICATION LOGIN NOPASSWORD;' + "CREATEROLE NOREPLICATION LOGIN NOPASSWORD;" ' GRANT "test_groups" TO "test_username"', - postgres._run_psql.call_args[0][0][14] + postgres._run_psql.call_args[0][0][14], ) ) def test_user_update_encrypted_passwd(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.role_get', - Mock(return_value={'superuser': False})): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.role_get", + Mock(return_value={"superuser": False}), + ): postgres.user_update( - 'test_username', - user='test_user', - host='test_host', - port='test_port', - maintenance_db='test_maint', - password='test_pass', + "test_username", + user="test_user", + host="test_host", + port="test_port", + maintenance_db="test_maint", + password="test_pass", createdb=False, createroles=True, encrypted=True, inherit=True, login=True, - rolepassword='foobar', + rolepassword="foobar", replication=False, - groups='test_groups', - runas='foo' + groups="test_groups", + runas="foo", ) # postgres._run_psql.call_args[0][0] will contain the list of CLI args. # The first 14 elements of this list are initial args used in all (or @@ -584,24 +841,26 @@ class PostgresTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue( re.match( 'ALTER ROLE "test_username" WITH INHERIT NOCREATEDB ' - 'CREATEROLE NOREPLICATION LOGIN ' - 'ENCRYPTED PASSWORD ' - '[\'"]{0,5}md531c27e68d3771c392b52102c01be1da1[\'"]{0,5}' + "CREATEROLE NOREPLICATION LOGIN " + "ENCRYPTED PASSWORD " + "['\"]{0,5}md531c27e68d3771c392b52102c01be1da1['\"]{0,5}" '; GRANT "test_groups" TO "test_username"', - postgres._run_psql.call_args[0][0][14] + postgres._run_psql.call_args[0][0][14], ) ) def test_version(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0, 'stdout': '9.1.9'})): + with patch( + "salt.modules.postgres._run_psql", + Mock(return_value={"retcode": 0, "stdout": "9.1.9"}), + ): postgres.version( - user='test_user', - host='test_host', - port='test_port', - maintenance_db='test_maint', - password='test_pass', - runas='foo' + user="test_user", + host="test_host", + port="test_port", + maintenance_db="test_maint", + password="test_pass", + runas="foo", ) # postgres._run_psql.call_args[0][0] will contain the list of CLI args. # The first 14 elements of this list are initial args used in all (or @@ -609,436 +868,581 @@ class PostgresTestCase(TestCase, LoaderModuleMockMixin): # query will be in the 15th argument. self.assertTrue( re.match( - 'SELECT setting FROM pg_catalog.pg_settings', - postgres._run_psql.call_args[0][0][14] + "SELECT setting FROM pg_catalog.pg_settings", + postgres._run_psql.call_args[0][0][14], ) ) def test_installed_extensions(self): - with patch('salt.modules.postgres.psql_query', - Mock(return_value=[{'extname': "foo", 'extversion': "1"}])): + with patch( + "salt.modules.postgres.psql_query", + Mock(return_value=[{"extname": "foo", "extversion": "1"}]), + ): exts = postgres.installed_extensions() - self.assertEqual( - exts, - {'foo': {'extversion': '1', 'extname': 'foo'}} - ) + self.assertEqual(exts, {"foo": {"extversion": "1", "extname": "foo"}}) def test_available_extensions(self): - with patch('salt.modules.postgres.psql_query', - Mock(return_value=[{'name': "foo", 'default_version': "1"}])): + with patch( + "salt.modules.postgres.psql_query", + Mock(return_value=[{"name": "foo", "default_version": "1"}]), + ): exts = postgres.available_extensions() - self.assertEqual( - exts, - {'foo': {'default_version': '1', 'name': 'foo'}} - ) + self.assertEqual(exts, {"foo": {"default_version": "1", "name": "foo"}}) def test_drop_extension2(self): - with patch('salt.modules.postgres.installed_extensions', - Mock(side_effect=[{}, {}])): - with patch('salt.modules.postgres._psql_prepare_and_run', - Mock(return_value=None)): - with patch('salt.modules.postgres.available_extensions', - Mock(return_value={'foo': {'default_version': '1', 'name': 'foo'}})): - self.assertEqual(postgres.drop_extension('foo'), True) + with patch( + "salt.modules.postgres.installed_extensions", Mock(side_effect=[{}, {}]) + ): + with patch( + "salt.modules.postgres._psql_prepare_and_run", Mock(return_value=None) + ): + with patch( + "salt.modules.postgres.available_extensions", + Mock(return_value={"foo": {"default_version": "1", "name": "foo"}}), + ): + self.assertEqual(postgres.drop_extension("foo"), True) def test_drop_extension3(self): - with patch('salt.modules.postgres.installed_extensions', - Mock(side_effect=[{'foo': {'extversion': '1', 'extname': 'foo'}}, {}])): - with patch('salt.modules.postgres._psql_prepare_and_run', - Mock(return_value=None)): - with patch('salt.modules.postgres.available_extensions', - Mock(return_value={'foo': {'default_version': '1', 'name': 'foo'}})): - self.assertEqual(postgres.drop_extension('foo'), True) + with patch( + "salt.modules.postgres.installed_extensions", + Mock(side_effect=[{"foo": {"extversion": "1", "extname": "foo"}}, {}]), + ): + with patch( + "salt.modules.postgres._psql_prepare_and_run", Mock(return_value=None) + ): + with patch( + "salt.modules.postgres.available_extensions", + Mock(return_value={"foo": {"default_version": "1", "name": "foo"}}), + ): + self.assertEqual(postgres.drop_extension("foo"), True) def test_drop_extension1(self): - with patch('salt.modules.postgres.installed_extensions', - Mock(side_effect=[{'foo': {'extversion': '1', 'extname': 'foo'}}, - {'foo': {'extversion': '1', 'extname': 'foo'}}])): - with patch('salt.modules.postgres._psql_prepare_and_run', - Mock(return_value=None)): - with patch('salt.modules.postgres.available_extensions', - Mock(return_value={'foo': {'default_version': '1', 'name': 'foo'}})): - self.assertEqual(postgres.drop_extension('foo'), False) + with patch( + "salt.modules.postgres.installed_extensions", + Mock( + side_effect=[ + {"foo": {"extversion": "1", "extname": "foo"}}, + {"foo": {"extversion": "1", "extname": "foo"}}, + ] + ), + ): + with patch( + "salt.modules.postgres._psql_prepare_and_run", Mock(return_value=None) + ): + with patch( + "salt.modules.postgres.available_extensions", + Mock(return_value={"foo": {"default_version": "1", "name": "foo"}}), + ): + self.assertEqual(postgres.drop_extension("foo"), False) def test_create_mtdata(self): - with patch('salt.modules.postgres.installed_extensions', - Mock(return_value={ - 'foo': {'extversion': '0.8', - 'extrelocatable': 't', - 'schema_name': 'foo', - 'extname': 'foo'}}, - )): - with patch('salt.modules.postgres.available_extensions', - Mock(return_value={'foo': {'default_version': '1.4', - 'name': 'foo'}})): - ret = postgres.create_metadata('foo', schema='bar', ext_version='1.4') + with patch( + "salt.modules.postgres.installed_extensions", + Mock( + return_value={ + "foo": { + "extversion": "0.8", + "extrelocatable": "t", + "schema_name": "foo", + "extname": "foo", + } + }, + ), + ): + with patch( + "salt.modules.postgres.available_extensions", + Mock(return_value={"foo": {"default_version": "1.4", "name": "foo"}}), + ): + ret = postgres.create_metadata("foo", schema="bar", ext_version="1.4") self.assertTrue(postgres._EXTENSION_INSTALLED in ret) self.assertTrue(postgres._EXTENSION_TO_UPGRADE in ret) self.assertTrue(postgres._EXTENSION_TO_MOVE in ret) - ret = postgres.create_metadata('foo', schema='foo', ext_version='0.4') + ret = postgres.create_metadata("foo", schema="foo", ext_version="0.4") self.assertTrue(postgres._EXTENSION_INSTALLED in ret) self.assertFalse(postgres._EXTENSION_TO_UPGRADE in ret) self.assertFalse(postgres._EXTENSION_TO_MOVE in ret) - ret = postgres.create_metadata('foo') + ret = postgres.create_metadata("foo") self.assertTrue(postgres._EXTENSION_INSTALLED in ret) self.assertFalse(postgres._EXTENSION_TO_UPGRADE in ret) self.assertFalse(postgres._EXTENSION_TO_MOVE in ret) - ret = postgres.create_metadata('foobar') + ret = postgres.create_metadata("foobar") self.assertTrue(postgres._EXTENSION_NOT_INSTALLED in ret) self.assertFalse(postgres._EXTENSION_INSTALLED in ret) self.assertFalse(postgres._EXTENSION_TO_UPGRADE in ret) self.assertFalse(postgres._EXTENSION_TO_MOVE in ret) def test_create_extension_newerthan(self): - ''' + """ scenario of creating upgrading extensions with possible schema and version specifications - ''' - with patch('salt.modules.postgres.create_metadata', - Mock(side_effect=[ - # create succeeded - [postgres._EXTENSION_NOT_INSTALLED], - [postgres._EXTENSION_INSTALLED], - [postgres._EXTENSION_NOT_INSTALLED], - [postgres._EXTENSION_INSTALLED], - # create failed - [postgres._EXTENSION_NOT_INSTALLED], - [postgres._EXTENSION_NOT_INSTALLED], - # move+upgrade succeeded - [postgres._EXTENSION_TO_MOVE, + """ + with patch( + "salt.modules.postgres.create_metadata", + Mock( + side_effect=[ + # create succeeded + [postgres._EXTENSION_NOT_INSTALLED], + [postgres._EXTENSION_INSTALLED], + [postgres._EXTENSION_NOT_INSTALLED], + [postgres._EXTENSION_INSTALLED], + # create failed + [postgres._EXTENSION_NOT_INSTALLED], + [postgres._EXTENSION_NOT_INSTALLED], + # move+upgrade succeeded + [ + postgres._EXTENSION_TO_MOVE, postgres._EXTENSION_TO_UPGRADE, - postgres._EXTENSION_INSTALLED], - [postgres._EXTENSION_INSTALLED], - # move succeeded - [postgres._EXTENSION_TO_MOVE, - postgres._EXTENSION_INSTALLED], - [postgres._EXTENSION_INSTALLED], - # upgrade succeeded - [postgres._EXTENSION_TO_UPGRADE, - postgres._EXTENSION_INSTALLED], - [postgres._EXTENSION_INSTALLED], - # upgrade failed - [postgres._EXTENSION_TO_UPGRADE, postgres._EXTENSION_INSTALLED], - [postgres._EXTENSION_TO_UPGRADE, postgres._EXTENSION_INSTALLED], - # move failed - [postgres._EXTENSION_TO_MOVE, postgres._EXTENSION_INSTALLED], - [postgres._EXTENSION_TO_MOVE, postgres._EXTENSION_INSTALLED]])): - with patch('salt.modules.postgres._psql_prepare_and_run', - Mock(return_value=None)): - with patch('salt.modules.postgres.available_extensions', - Mock(return_value={'foo': {'default_version': '1.4', - 'name': 'foo'}})): - self.assertTrue(postgres.create_extension('foo')) - self.assertTrue(re.match( - 'CREATE EXTENSION IF NOT EXISTS "foo" ;', - postgres._psql_prepare_and_run.call_args[0][0][1])) - self.assertTrue(postgres.create_extension( - 'foo', schema='a', ext_version='b', from_version='c')) - self.assertTrue(re.match( - 'CREATE EXTENSION IF NOT EXISTS "foo" ' - 'WITH SCHEMA "a" VERSION b FROM c ;', - postgres._psql_prepare_and_run.call_args[0][0][1])) - self.assertFalse(postgres.create_extension('foo')) - ret = postgres.create_extension('foo', ext_version='a', schema='b') + postgres._EXTENSION_INSTALLED, + ], + [postgres._EXTENSION_INSTALLED], + # move succeeded + [postgres._EXTENSION_TO_MOVE, postgres._EXTENSION_INSTALLED], + [postgres._EXTENSION_INSTALLED], + # upgrade succeeded + [postgres._EXTENSION_TO_UPGRADE, postgres._EXTENSION_INSTALLED], + [postgres._EXTENSION_INSTALLED], + # upgrade failed + [postgres._EXTENSION_TO_UPGRADE, postgres._EXTENSION_INSTALLED], + [postgres._EXTENSION_TO_UPGRADE, postgres._EXTENSION_INSTALLED], + # move failed + [postgres._EXTENSION_TO_MOVE, postgres._EXTENSION_INSTALLED], + [postgres._EXTENSION_TO_MOVE, postgres._EXTENSION_INSTALLED], + ] + ), + ): + with patch( + "salt.modules.postgres._psql_prepare_and_run", Mock(return_value=None) + ): + with patch( + "salt.modules.postgres.available_extensions", + Mock( + return_value={"foo": {"default_version": "1.4", "name": "foo"}} + ), + ): + self.assertTrue(postgres.create_extension("foo")) + self.assertTrue( + re.match( + 'CREATE EXTENSION IF NOT EXISTS "foo" ;', + postgres._psql_prepare_and_run.call_args[0][0][1], + ) + ) + self.assertTrue( + postgres.create_extension( + "foo", schema="a", ext_version="b", from_version="c" + ) + ) + self.assertTrue( + re.match( + 'CREATE EXTENSION IF NOT EXISTS "foo" ' + 'WITH SCHEMA "a" VERSION b FROM c ;', + postgres._psql_prepare_and_run.call_args[0][0][1], + ) + ) + self.assertFalse(postgres.create_extension("foo")) + ret = postgres.create_extension("foo", ext_version="a", schema="b") self.assertTrue(ret) - self.assertTrue(re.match( - 'ALTER EXTENSION "foo" SET SCHEMA "b";' - ' ALTER EXTENSION "foo" UPDATE TO a;', - postgres._psql_prepare_and_run.call_args[0][0][1])) - ret = postgres.create_extension('foo', ext_version='a', schema='b') + self.assertTrue( + re.match( + 'ALTER EXTENSION "foo" SET SCHEMA "b";' + ' ALTER EXTENSION "foo" UPDATE TO a;', + postgres._psql_prepare_and_run.call_args[0][0][1], + ) + ) + ret = postgres.create_extension("foo", ext_version="a", schema="b") self.assertTrue(ret) - self.assertTrue(re.match( - 'ALTER EXTENSION "foo" SET SCHEMA "b";', - postgres._psql_prepare_and_run.call_args[0][0][1])) - ret = postgres.create_extension('foo', ext_version='a', schema='b') + self.assertTrue( + re.match( + 'ALTER EXTENSION "foo" SET SCHEMA "b";', + postgres._psql_prepare_and_run.call_args[0][0][1], + ) + ) + ret = postgres.create_extension("foo", ext_version="a", schema="b") self.assertTrue(ret) - self.assertTrue(re.match( - 'ALTER EXTENSION "foo" UPDATE TO a;', - postgres._psql_prepare_and_run.call_args[0][0][1])) - self.assertFalse(postgres.create_extension( - 'foo', ext_version='a', schema='b')) - self.assertFalse(postgres.create_extension( - 'foo', ext_version='a', schema='b')) + self.assertTrue( + re.match( + 'ALTER EXTENSION "foo" UPDATE TO a;', + postgres._psql_prepare_and_run.call_args[0][0][1], + ) + ) + self.assertFalse( + postgres.create_extension("foo", ext_version="a", schema="b") + ) + self.assertFalse( + postgres.create_extension("foo", ext_version="a", schema="b") + ) def test_encrypt_passwords(self): + self.assertEqual(postgres._maybe_encrypt_password("foo", "bar", False), "bar") self.assertEqual( - postgres._maybe_encrypt_password( - 'foo', 'bar', False), - 'bar') - self.assertEqual( - postgres._maybe_encrypt_password( - 'foo', 'bar', True), - 'md596948aad3fcae80c08a35c9b5958cd89') + postgres._maybe_encrypt_password("foo", "bar", True), + "md596948aad3fcae80c08a35c9b5958cd89", + ) def test_schema_list(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0, - 'stdout': test_list_schema_csv})): + with patch( + "salt.modules.postgres._run_psql", + Mock(return_value={"retcode": 0, "stdout": test_list_schema_csv}), + ): ret = postgres.schema_list( - 'maint_db', - db_user='testuser', - db_host='testhost', - db_port='testport', - db_password='foo' + "maint_db", + db_user="testuser", + db_host="testhost", + db_port="testport", + db_password="foo", + ) + self.assertDictEqual( + ret, + { + "public": { + "acl": "{postgres=UC/postgres,=UC/postgres}", + "owner": "postgres", + }, + "pg_toast": {"acl": "", "owner": "postgres"}, + }, ) - self.assertDictEqual(ret, { - 'public': {'acl': '{postgres=UC/postgres,=UC/postgres}', - 'owner': 'postgres'}, - 'pg_toast': {'acl': '', 'owner': 'postgres'} - }) def test_schema_exists(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.psql_query', - Mock(return_value=[ - { - 'name': 'public', - 'acl': '{postgres=UC/postgres,=UC/postgres}', - 'owner': 'postgres' - }])): - ret = postgres.schema_exists( - 'template1', - 'public' - ) + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.psql_query", + Mock( + return_value=[ + { + "name": "public", + "acl": "{postgres=UC/postgres,=UC/postgres}", + "owner": "postgres", + } + ] + ), + ): + ret = postgres.schema_exists("template1", "public") self.assertTrue(ret) def test_schema_get(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.psql_query', - Mock(return_value=[ - { - 'name': 'public', - 'acl': '{postgres=UC/postgres,=UC/postgres}', - 'owner': 'postgres' - }])): - ret = postgres.schema_get( - 'template1', - 'public' - ) + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.psql_query", + Mock( + return_value=[ + { + "name": "public", + "acl": "{postgres=UC/postgres,=UC/postgres}", + "owner": "postgres", + } + ] + ), + ): + ret = postgres.schema_get("template1", "public") self.assertTrue(ret) def test_schema_get_again(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.psql_query', - Mock(return_value=[ - { - 'name': 'public', - 'acl': '{postgres=UC/postgres,=UC/postgres}', - 'owner': 'postgres' - }])): - ret = postgres.schema_get( - 'template1', - 'pg_toast' - ) + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.psql_query", + Mock( + return_value=[ + { + "name": "public", + "acl": "{postgres=UC/postgres,=UC/postgres}", + "owner": "postgres", + } + ] + ), + ): + ret = postgres.schema_get("template1", "pg_toast") self.assertFalse(ret) def test_schema_create(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.schema_exists', Mock(return_value=False)): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.schema_exists", Mock(return_value=False)): postgres.schema_create( - 'maint_db', - 'testschema', - user='user', - db_host='testhost', - db_port='testport', - db_user='testuser', - db_password='testpassword' + "maint_db", + "testschema", + user="user", + db_host="testhost", + db_port="testport", + db_user="testuser", + db_password="testpassword", ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'maint_db', - '-c', 'CREATE SCHEMA "testschema"'], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + 'CREATE SCHEMA "testschema"', + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_schema_create2(self): - with patch('salt.modules.postgres.schema_exists', Mock(return_value=True)): - ret = postgres.schema_create('test_db', - 'test_schema', - user='user', - db_host='test_host', - db_port='test_port', - db_user='test_user', - db_password='test_password' - ) + with patch("salt.modules.postgres.schema_exists", Mock(return_value=True)): + ret = postgres.schema_create( + "test_db", + "test_schema", + user="user", + db_host="test_host", + db_port="test_port", + db_user="test_user", + db_password="test_password", + ) self.assertFalse(ret) def test_schema_remove(self): - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.schema_exists', Mock(return_value=True)): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.schema_exists", Mock(return_value=True)): postgres.schema_remove( - 'maint_db', - 'testschema', - user='user', - db_host='testhost', - db_port='testport', - db_user='testuser', - db_password='testpassword' + "maint_db", + "testschema", + user="user", + db_host="testhost", + db_port="testport", + db_user="testuser", + db_password="testpassword", ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'maint_db', - '-c', 'DROP SCHEMA "testschema"'], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "maint_db", + "-c", + 'DROP SCHEMA "testschema"', + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_schema_remove2(self): - with patch('salt.modules.postgres.schema_exists', Mock(return_value=False)): - ret = postgres.schema_remove('test_db', - 'test_schema', - user='user', - db_host='test_host', - db_port='test_port', - db_user='test_user', - db_password='test_password' - ) + with patch("salt.modules.postgres.schema_exists", Mock(return_value=False)): + ret = postgres.schema_remove( + "test_db", + "test_schema", + user="user", + db_host="test_host", + db_port="test_port", + db_user="test_user", + db_password="test_password", + ) self.assertFalse(ret) def test_language_list(self): - ''' + """ Test language listing - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0, - 'stdout': test_list_language_csv})): + """ + with patch( + "salt.modules.postgres._run_psql", + Mock(return_value={"retcode": 0, "stdout": test_list_language_csv}), + ): ret = postgres.language_list( - 'testdb', - user='testuser', - host='testhost', - port='testport', - password='foo' + "testdb", + user="testuser", + host="testhost", + port="testport", + password="foo", + ) + self.assertDictEqual( + ret, + {"c": "c", "internal": "internal", "plpgsql": "plpgsql", "sql": "sql"}, ) - self.assertDictEqual(ret, - {'c': 'c', - 'internal': 'internal', - 'plpgsql': 'plpgsql', - 'sql': 'sql'}) def test_language_exists(self): - ''' + """ Test language existence check - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.psql_query', - Mock(return_value=[ - {'Name': 'internal'}, - {'Name': 'c'}, - {'Name': 'sql'}, - {'Name': 'plpgsql'}])): - with patch('salt.modules.postgres.language_exists', - Mock(return_value=True)): - ret = postgres.language_exists( - 'sql', - 'testdb' - ) + """ + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.psql_query", + Mock( + return_value=[ + {"Name": "internal"}, + {"Name": "c"}, + {"Name": "sql"}, + {"Name": "plpgsql"}, + ] + ), + ): + with patch( + "salt.modules.postgres.language_exists", Mock(return_value=True) + ): + ret = postgres.language_exists("sql", "testdb") self.assertTrue(ret) def test_language_create(self): - ''' + """ Test language creation - does not exist in db - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.language_exists', Mock(return_value=False)): + """ + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.language_exists", Mock(return_value=False) + ): postgres.language_create( - 'plpythonu', - 'testdb', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "plpythonu", + "testdb", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'testdb', - '-c', 'CREATE LANGUAGE plpythonu'], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "testdb", + "-c", + "CREATE LANGUAGE plpythonu", + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_language_create_exists(self): - ''' + """ Test language creation - already exists in db - ''' - with patch('salt.modules.postgres.language_exists', Mock(return_value=True)): + """ + with patch("salt.modules.postgres.language_exists", Mock(return_value=True)): ret = postgres.language_create( - 'plpythonu', - 'testdb', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "plpythonu", + "testdb", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) self.assertFalse(ret) def test_language_remove(self): - ''' + """ Test language removal - exists in db - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.language_exists', - Mock(return_value=True)): + """ + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.language_exists", Mock(return_value=True) + ): postgres.language_remove( - 'plpgsql', - 'testdb', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "plpgsql", + "testdb", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'testdb', - '-c', 'DROP LANGUAGE plpgsql'], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "testdb", + "-c", + "DROP LANGUAGE plpgsql", + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_language_remove_non_exist(self): - ''' + """ Test language removal - does not exist in db - ''' - with patch('salt.modules.postgres.language_exists', - Mock(return_value=False)): + """ + with patch("salt.modules.postgres.language_exists", Mock(return_value=False)): ret = postgres.language_remove( - 'plpgsql', - 'testdb', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "plpgsql", + "testdb", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) self.assertFalse(ret) def test_privileges_list_table(self): - ''' + """ Test privilege listing on a table - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0, - 'stdout': test_privileges_list_table_csv})): + """ + with patch( + "salt.modules.postgres._run_psql", + Mock(return_value={"retcode": 0, "stdout": test_privileges_list_table_csv}), + ): ret = postgres.privileges_list( - 'awl', - 'table', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "awl", + "table", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) expected = { "bayestest": { @@ -1069,431 +1473,605 @@ class PostgresTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual(ret, expected) - query = ("COPY (SELECT relacl AS name FROM pg_catalog.pg_class c " - "JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " - "WHERE nspname = 'public' AND relname = 'awl' AND relkind = 'r' " - "ORDER BY relname) TO STDOUT WITH CSV HEADER") + query = ( + "COPY (SELECT relacl AS name FROM pg_catalog.pg_class c " + "JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace " + "WHERE nspname = 'public' AND relname = 'awl' AND relkind = 'r' " + "ORDER BY relname) TO STDOUT WITH CSV HEADER" + ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'db_name', - '-v', 'datestyle=ISO,MDY', '-c', query], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "db_name", + "-v", + "datestyle=ISO,MDY", + "-c", + query, + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_privileges_list_group(self): - ''' + """ Test privilege listing on a group - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0, - 'stdout': test_privileges_list_group_csv})): + """ + with patch( + "salt.modules.postgres._run_psql", + Mock(return_value={"retcode": 0, "stdout": test_privileges_list_group_csv}), + ): ret = postgres.privileges_list( - 'admin', - 'group', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "admin", + "group", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) expected = { - 'baruwa': False, - 'baruwatest': False, - 'baruwatest2': True, + "baruwa": False, + "baruwatest": False, + "baruwatest2": True, } self.assertDictEqual(ret, expected) - query = ("COPY (SELECT rolname, admin_option " - "FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles r " - "ON m.member=r.oid WHERE m.roleid IN (SELECT oid FROM " - "pg_catalog.pg_roles WHERE rolname='admin') ORDER BY rolname) " - "TO STDOUT WITH CSV HEADER") + query = ( + "COPY (SELECT rolname, admin_option " + "FROM pg_catalog.pg_auth_members m JOIN pg_catalog.pg_roles r " + "ON m.member=r.oid WHERE m.roleid IN (SELECT oid FROM " + "pg_catalog.pg_roles WHERE rolname='admin') ORDER BY rolname) " + "TO STDOUT WITH CSV HEADER" + ) postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'db_name', - '-v', 'datestyle=ISO,MDY', '-c', query], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "db_name", + "-v", + "datestyle=ISO,MDY", + "-c", + query, + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_has_privileges_on_table(self): - ''' + """ Test privilege checks on table - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0, - 'stdout': test_privileges_list_table_csv})): + """ + with patch( + "salt.modules.postgres._run_psql", + Mock(return_value={"retcode": 0, "stdout": test_privileges_list_table_csv}), + ): ret = postgres.has_privileges( - 'baruwa', - 'awl', - 'table', - 'SELECT,INSERT', + "baruwa", + "awl", + "table", + "SELECT,INSERT", grant_option=True, - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) self.assertTrue(ret) ret = postgres.has_privileges( - 'baruwa', - 'awl', - 'table', - 'ALL', + "baruwa", + "awl", + "table", + "ALL", grant_option=True, - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) self.assertTrue(ret) ret = postgres.has_privileges( - 'bayestest', - 'awl', - 'table', - 'SELECT,INSERT,TRUNCATE', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "bayestest", + "awl", + "table", + "SELECT,INSERT,TRUNCATE", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) self.assertFalse(ret) ret = postgres.has_privileges( - 'bayestest', - 'awl', - 'table', - 'SELECT,INSERT', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "bayestest", + "awl", + "table", + "SELECT,INSERT", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) self.assertTrue(ret) def test_has_privileges_on_group(self): - ''' + """ Test privilege checks on group - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0, - 'stdout': test_privileges_list_group_csv})): + """ + with patch( + "salt.modules.postgres._run_psql", + Mock(return_value={"retcode": 0, "stdout": test_privileges_list_group_csv}), + ): ret = postgres.has_privileges( - 'baruwa', - 'admin', - 'group', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "baruwa", + "admin", + "group", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) self.assertTrue(ret) ret = postgres.has_privileges( - 'baruwa', - 'admin', - 'group', + "baruwa", + "admin", + "group", grant_option=True, - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) self.assertFalse(ret) ret = postgres.has_privileges( - 'tony', - 'admin', - 'group', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "tony", + "admin", + "group", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) self.assertFalse(ret) def test_privileges_grant_table(self): - ''' + """ Test granting privileges on table - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.has_privileges', - Mock(return_value=False)): + """ + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.has_privileges", Mock(return_value=False) + ): ret = postgres.privileges_grant( - 'baruwa', - 'awl', - 'table', - 'ALL', - grant_option=True, - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "baruwa", + "awl", + "table", + "ALL", + grant_option=True, + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) query = 'GRANT ALL ON TABLE public."awl" TO "baruwa" WITH GRANT OPTION' postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'db_name', - '-c', query], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "db_name", + "-c", + query, + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.has_privileges', - Mock(return_value=False)): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.has_privileges", Mock(return_value=False) + ): ret = postgres.privileges_grant( - 'baruwa', - 'awl', - 'table', - 'ALL', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "baruwa", + "awl", + "table", + "ALL", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) query = 'GRANT ALL ON TABLE public."awl" TO "baruwa"' postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'db_name', - '-c', query], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "db_name", + "-c", + query, + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) # Test grant on all tables - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.has_privileges', - Mock(return_value=False)): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.has_privileges", Mock(return_value=False) + ): ret = postgres.privileges_grant( - 'baruwa', - 'ALL', - 'table', - 'SELECT', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "baruwa", + "ALL", + "table", + "SELECT", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) query = 'GRANT SELECT ON ALL TABLES IN SCHEMA public TO "baruwa"' postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'db_name', - '-c', query], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "db_name", + "-c", + query, + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_privileges_grant_group(self): - ''' + """ Test granting privileges on group - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.has_privileges', - Mock(return_value=False)): + """ + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.has_privileges", Mock(return_value=False) + ): ret = postgres.privileges_grant( - 'baruwa', - 'admins', - 'group', - grant_option=True, - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "baruwa", + "admins", + "group", + grant_option=True, + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) query = 'GRANT admins TO "baruwa" WITH ADMIN OPTION' postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'db_name', - '-c', query], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "db_name", + "-c", + query, + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.has_privileges', - Mock(return_value=False)): + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.has_privileges", Mock(return_value=False) + ): ret = postgres.privileges_grant( - 'baruwa', - 'admins', - 'group', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "baruwa", + "admins", + "group", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) query = 'GRANT admins TO "baruwa"' postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'db_name', - '-c', query], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "db_name", + "-c", + query, + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_privileges_revoke_table(self): - ''' + """ Test revoking privileges on table - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.has_privileges', - Mock(return_value=True)): + """ + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.has_privileges", Mock(return_value=True)): ret = postgres.privileges_revoke( - 'baruwa', - 'awl', - 'table', - 'ALL', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "baruwa", + "awl", + "table", + "ALL", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) - query = 'REVOKE ALL ON TABLE public.awl FROM baruwa' + query = "REVOKE ALL ON TABLE public.awl FROM baruwa" postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'db_name', - '-c', query], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "db_name", + "-c", + query, + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_privileges_revoke_group(self): - ''' + """ Test revoking privileges on group - ''' - with patch('salt.modules.postgres._run_psql', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.has_privileges', - Mock(return_value=True)): + """ + with patch( + "salt.modules.postgres._run_psql", Mock(return_value={"retcode": 0}) + ): + with patch("salt.modules.postgres.has_privileges", Mock(return_value=True)): ret = postgres.privileges_revoke( - 'baruwa', - 'admins', - 'group', - maintenance_db='db_name', - runas='user', - host='testhost', - port='testport', - user='testuser', - password='testpassword' + "baruwa", + "admins", + "group", + maintenance_db="db_name", + runas="user", + host="testhost", + port="testport", + user="testuser", + password="testpassword", ) - query = 'REVOKE admins FROM baruwa' + query = "REVOKE admins FROM baruwa" postgres._run_psql.assert_called_once_with( - ['/usr/bin/pgsql', '--no-align', '--no-readline', '--no-psqlrc', - '--no-password', '--username', 'testuser', '--host', - 'testhost', '--port', 'testport', '--dbname', 'db_name', - '-c', query], - host='testhost', port='testport', - password='testpassword', user='testuser', runas='user') + [ + "/usr/bin/pgsql", + "--no-align", + "--no-readline", + "--no-psqlrc", + "--no-password", + "--username", + "testuser", + "--host", + "testhost", + "--port", + "testport", + "--dbname", + "db_name", + "-c", + query, + ], + host="testhost", + port="testport", + password="testpassword", + user="testuser", + runas="user", + ) def test_datadir_init(self): - ''' + """ Test Initializing a postgres data directory - ''' - with patch('salt.modules.postgres._run_initdb', - Mock(return_value={'retcode': 0})): - with patch('salt.modules.postgres.datadir_exists', - Mock(return_value=False)): - name = '/var/lib/pgsql/data' + """ + with patch( + "salt.modules.postgres._run_initdb", Mock(return_value={"retcode": 0}) + ): + with patch( + "salt.modules.postgres.datadir_exists", Mock(return_value=False) + ): + name = "/var/lib/pgsql/data" ret = postgres.datadir_init( - name, - user='postgres', - password='test', - runas='postgres') + name, user="postgres", password="test", runas="postgres" + ) postgres._run_initdb.assert_called_once_with( name, - auth='password', - encoding='UTF8', + auth="password", + encoding="UTF8", locale=None, - password='test', - runas='postgres', + password="test", + runas="postgres", checksums=False, - user='postgres', + user="postgres", ) self.assertTrue(ret) def test_datadir_exists(self): - ''' + """ Test Checks if postgres data directory has been initialized - ''' - with patch('os.path.isfile', Mock(return_value=True)): - name = '/var/lib/pgsql/data' + """ + with patch("os.path.isfile", Mock(return_value=True)): + name = "/var/lib/pgsql/data" ret = postgres.datadir_exists(name) self.assertTrue(ret) def test_pg_is_older_ext_ver(self): - ''' + """ Test Checks if postgres extension version string is older - ''' - self.assertTrue(postgres._pg_is_older_ext_ver('8.5', '9.5')) - self.assertTrue(postgres._pg_is_older_ext_ver('8.5', '8.6')) - self.assertTrue(postgres._pg_is_older_ext_ver('8.5.2', '8.5.3')) - self.assertFalse(postgres._pg_is_older_ext_ver('9.5', '8.5')) - self.assertTrue(postgres._pg_is_older_ext_ver('9.5', '9.6')) - self.assertTrue(postgres._pg_is_older_ext_ver('9.5.0', '9.5.1')) - self.assertTrue(postgres._pg_is_older_ext_ver('9.5', '9.5.1')) - self.assertFalse(postgres._pg_is_older_ext_ver('9.5.1', '9.5')) - self.assertFalse(postgres._pg_is_older_ext_ver('9.5b', '9.5a')) - self.assertTrue(postgres._pg_is_older_ext_ver('10a', '10b')) - self.assertTrue(postgres._pg_is_older_ext_ver('1.2.3.4', '1.2.3.5')) - self.assertTrue(postgres._pg_is_older_ext_ver('10dev', '10next')) - self.assertFalse(postgres._pg_is_older_ext_ver('10next', '10dev')) + """ + self.assertTrue(postgres._pg_is_older_ext_ver("8.5", "9.5")) + self.assertTrue(postgres._pg_is_older_ext_ver("8.5", "8.6")) + self.assertTrue(postgres._pg_is_older_ext_ver("8.5.2", "8.5.3")) + self.assertFalse(postgres._pg_is_older_ext_ver("9.5", "8.5")) + self.assertTrue(postgres._pg_is_older_ext_ver("9.5", "9.6")) + self.assertTrue(postgres._pg_is_older_ext_ver("9.5.0", "9.5.1")) + self.assertTrue(postgres._pg_is_older_ext_ver("9.5", "9.5.1")) + self.assertFalse(postgres._pg_is_older_ext_ver("9.5.1", "9.5")) + self.assertFalse(postgres._pg_is_older_ext_ver("9.5b", "9.5a")) + self.assertTrue(postgres._pg_is_older_ext_ver("10a", "10b")) + self.assertTrue(postgres._pg_is_older_ext_ver("1.2.3.4", "1.2.3.5")) + self.assertTrue(postgres._pg_is_older_ext_ver("10dev", "10next")) + self.assertFalse(postgres._pg_is_older_ext_ver("10next", "10dev")) diff --git a/tests/unit/modules/test_poudriere.py b/tests/unit/modules/test_poudriere.py index 9873ced17b0..59e1c8a8999 100644 --- a/tests/unit/modules/test_poudriere.py +++ b/tests/unit/modules/test_poudriere.py @@ -1,250 +1,262 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - mock_open, -) +import os # Import Salt Libs import salt.modules.poudriere as poudriere +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class PoudriereTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.poudriere - ''' + """ + def setup_loader_modules(self): return {poudriere: {}} # 'is_jail' function tests: 1 def test_is_jail(self): - ''' + """ Test if it return True if jail exists False if not. - ''' - mock = MagicMock(return_value='salt stack') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - self.assertTrue(poudriere.is_jail('salt')) + """ + mock = MagicMock(return_value="salt stack") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + self.assertTrue(poudriere.is_jail("salt")) - self.assertFalse(poudriere.is_jail('SALT')) + self.assertFalse(poudriere.is_jail("SALT")) # 'make_pkgng_aware' function tests: 1 def test_make_pkgng_aware(self): - ''' + """ Test if it make jail ``jname`` pkgng aware. - ''' - temp_dir = os.path.join('tmp', 'salt') - conf_file = os.path.join('tmp', 'salt', 'salt-make.conf') - ret1 = 'Could not create or find required directory {0}'.format(temp_dir) - ret2 = 'Looks like file {0} could not be created'.format(conf_file) - ret3 = {'changes': 'Created {0}'.format(conf_file)} + """ + temp_dir = os.path.join("tmp", "salt") + conf_file = os.path.join("tmp", "salt", "salt-make.conf") + ret1 = "Could not create or find required directory {0}".format(temp_dir) + ret2 = "Looks like file {0} could not be created".format(conf_file) + ret3 = {"changes": "Created {0}".format(conf_file)} mock = MagicMock(return_value=temp_dir) mock_true = MagicMock(return_value=True) - with patch.dict(poudriere.__salt__, {'config.option': mock, - 'file.write': mock_true}): - with patch.object(os.path, 'isdir', MagicMock(return_value=False)): - with patch.object(os, 'makedirs', mock_true): - self.assertEqual(poudriere.make_pkgng_aware('salt'), ret1) + with patch.dict( + poudriere.__salt__, {"config.option": mock, "file.write": mock_true} + ): + with patch.object(os.path, "isdir", MagicMock(return_value=False)): + with patch.object(os, "makedirs", mock_true): + self.assertEqual(poudriere.make_pkgng_aware("salt"), ret1) - with patch.object(os.path, 'isdir', mock_true): - self.assertEqual(poudriere.make_pkgng_aware('salt'), ret2) + with patch.object(os.path, "isdir", mock_true): + self.assertEqual(poudriere.make_pkgng_aware("salt"), ret2) - with patch.object(os.path, 'isfile', mock_true): - self.assertDictEqual(poudriere.make_pkgng_aware('salt'), - ret3) + with patch.object(os.path, "isfile", mock_true): + self.assertDictEqual(poudriere.make_pkgng_aware("salt"), ret3) # 'parse_config' function tests: 1 def test_parse_config(self): - ''' + """ Test if it returns a dict of poudriere main configuration definitions. - ''' - mock = MagicMock(return_value='/tmp/salt') - with patch.dict(poudriere.__salt__, {'config.option': mock}), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch.object(poudriere, '_check_config_exists', - MagicMock(side_effect=[True, False])): + """ + mock = MagicMock(return_value="/tmp/salt") + with patch.dict(poudriere.__salt__, {"config.option": mock}), patch( + "salt.utils.files.fopen", mock_open() + ), patch.object( + poudriere, "_check_config_exists", MagicMock(side_effect=[True, False]) + ): self.assertDictEqual(poudriere.parse_config(), {}) - self.assertEqual(poudriere.parse_config(), - 'Could not find /tmp/salt on file system') + self.assertEqual( + poudriere.parse_config(), "Could not find /tmp/salt on file system" + ) # 'version' function tests: 1 def test_version(self): - ''' + """ Test if it return poudriere version. - ''' - mock = MagicMock(return_value='9.0-RELEASE') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}): - self.assertEqual(poudriere.version(), '9.0-RELEASE') + """ + mock = MagicMock(return_value="9.0-RELEASE") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}): + self.assertEqual(poudriere.version(), "9.0-RELEASE") # 'list_jails' function tests: 1 def test_list_jails(self): - ''' + """ Test if it return a list of current jails managed by poudriere. - ''' - mock = MagicMock(return_value='salt stack') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - self.assertListEqual(poudriere.list_jails(), ['salt stack']) + """ + mock = MagicMock(return_value="salt stack") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + self.assertListEqual(poudriere.list_jails(), ["salt stack"]) # 'list_ports' function tests: 1 def test_list_ports(self): - ''' + """ Test if it return a list of current port trees managed by poudriere. - ''' - mock = MagicMock(return_value='salt stack') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - self.assertListEqual(poudriere.list_ports(), ['salt stack']) + """ + mock = MagicMock(return_value="salt stack") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + self.assertListEqual(poudriere.list_ports(), ["salt stack"]) # 'create_jail' function tests: 1 def test_create_jail(self): - ''' + """ Test if it creates a new poudriere jail if one does not exist. - ''' - mock_stack = MagicMock(return_value='90amd64 stack') + """ + mock_stack = MagicMock(return_value="90amd64 stack") mock_true = MagicMock(return_value=True) - with patch.dict(poudriere.__salt__, {'cmd.run': mock_stack}), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - self.assertEqual(poudriere.create_jail('90amd64', 'amd64'), - '90amd64 already exists') + with patch.dict(poudriere.__salt__, {"cmd.run": mock_stack}), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + self.assertEqual( + poudriere.create_jail("90amd64", "amd64"), "90amd64 already exists" + ) - with patch.object(poudriere, 'make_pkgng_aware', mock_true): - self.assertEqual(poudriere.create_jail('80amd64', 'amd64'), - 'Issue creating jail 80amd64') + with patch.object(poudriere, "make_pkgng_aware", mock_true): + self.assertEqual( + poudriere.create_jail("80amd64", "amd64"), + "Issue creating jail 80amd64", + ) - with patch.object(poudriere, 'make_pkgng_aware', mock_true), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - with patch.object(poudriere, 'is_jail', - MagicMock(side_effect=[False, True])): - with patch.dict(poudriere.__salt__, {'cmd.run': mock_stack}): - self.assertEqual(poudriere.create_jail('80amd64', 'amd64'), - 'Created jail 80amd64') + with patch.object(poudriere, "make_pkgng_aware", mock_true), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + with patch.object( + poudriere, "is_jail", MagicMock(side_effect=[False, True]) + ): + with patch.dict(poudriere.__salt__, {"cmd.run": mock_stack}): + self.assertEqual( + poudriere.create_jail("80amd64", "amd64"), + "Created jail 80amd64", + ) # 'update_jail' function tests: 1 def test_update_jail(self): - ''' + """ Test if it run freebsd-update on `name` poudriere jail. - ''' - mock = MagicMock(return_value='90amd64 stack') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - self.assertEqual(poudriere.update_jail('90amd64'), '90amd64 stack') + """ + mock = MagicMock(return_value="90amd64 stack") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + self.assertEqual(poudriere.update_jail("90amd64"), "90amd64 stack") - self.assertEqual(poudriere.update_jail('80amd64'), - 'Could not find jail 80amd64') + self.assertEqual( + poudriere.update_jail("80amd64"), "Could not find jail 80amd64" + ) # 'delete_jail' function tests: 1 def test_delete_jail(self): - ''' + """ Test if it deletes poudriere jail with `name`. - ''' - ret = 'Looks like there was an issue deleteing jail 90amd64' - mock_stack = MagicMock(return_value='90amd64 stack') - with patch.dict(poudriere.__salt__, {'cmd.run': mock_stack}), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - self.assertEqual(poudriere.delete_jail('90amd64'), ret) + """ + ret = "Looks like there was an issue deleteing jail 90amd64" + mock_stack = MagicMock(return_value="90amd64 stack") + with patch.dict(poudriere.__salt__, {"cmd.run": mock_stack}), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + self.assertEqual(poudriere.delete_jail("90amd64"), ret) - self.assertEqual(poudriere.delete_jail('80amd64'), - 'Looks like jail 80amd64 has not been created') + self.assertEqual( + poudriere.delete_jail("80amd64"), + "Looks like jail 80amd64 has not been created", + ) ret1 = 'Deleted jail "80amd64" but was unable to remove jail make file' - with patch.object(poudriere, 'is_jail', - MagicMock(side_effect=[True, False, True, False])): - with patch.dict(poudriere.__salt__, {'cmd.run': mock_stack}): - with patch.object(poudriere, '_config_dir', - MagicMock(return_value='/tmp/salt')): - self.assertEqual(poudriere.delete_jail('80amd64'), - 'Deleted jail 80amd64') + with patch.object( + poudriere, "is_jail", MagicMock(side_effect=[True, False, True, False]) + ): + with patch.dict(poudriere.__salt__, {"cmd.run": mock_stack}): + with patch.object( + poudriere, "_config_dir", MagicMock(return_value="/tmp/salt") + ): + self.assertEqual( + poudriere.delete_jail("80amd64"), "Deleted jail 80amd64" + ) - with patch.object(os.path, 'isfile', - MagicMock(return_value=True)): - self.assertEqual(poudriere.delete_jail('80amd64'), ret1) + with patch.object(os.path, "isfile", MagicMock(return_value=True)): + self.assertEqual(poudriere.delete_jail("80amd64"), ret1) # 'create_ports_tree' function tests: 1 def test_create_ports_tree(self): - ''' + """ Test if it not working need to run portfetch non interactive. - ''' - mock = MagicMock(return_value='salt stack') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - self.assertEqual(poudriere.create_ports_tree(), 'salt stack') + """ + mock = MagicMock(return_value="salt stack") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + self.assertEqual(poudriere.create_ports_tree(), "salt stack") # 'update_ports_tree' function tests: 1 def test_update_ports_tree(self): - ''' + """ Test if it updates the ports tree, either the default or the `ports_tree` specified. - ''' - mock = MagicMock(return_value='salt stack') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - self.assertEqual(poudriere.update_ports_tree('staging'), - 'salt stack') + """ + mock = MagicMock(return_value="salt stack") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + self.assertEqual(poudriere.update_ports_tree("staging"), "salt stack") # 'bulk_build' function tests: 1 def test_bulk_build(self): - ''' + """ Test if it run bulk build on poudriere server. - ''' - ret = 'Could not find file /root/pkg_list on filesystem' - mock = MagicMock(return_value='salt stack') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - self.assertEqual(poudriere.bulk_build('90amd64', '/root/pkg_list'), - ret) + """ + ret = "Could not find file /root/pkg_list on filesystem" + mock = MagicMock(return_value="salt stack") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + self.assertEqual(poudriere.bulk_build("90amd64", "/root/pkg_list"), ret) - with patch.object(os.path, 'isfile', MagicMock(return_value=True)): - self.assertEqual(poudriere.bulk_build('90amd64', - '/root/pkg_list'), - 'Could not find jail 90amd64') + with patch.object(os.path, "isfile", MagicMock(return_value=True)): + self.assertEqual( + poudriere.bulk_build("90amd64", "/root/pkg_list"), + "Could not find jail 90amd64", + ) - ret = ('There may have been an issue building ' - 'packages dumping output: 90amd64 stack') - with patch.object(os.path, 'isfile', MagicMock(return_value=True)), \ - patch('salt.modules.poudriere._check_config_exists', - MagicMock(return_value=True)): - mock = MagicMock(return_value='90amd64 stack packages built') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}): - self.assertEqual(poudriere.bulk_build('90amd64', - '/root/pkg_list'), - '90amd64 stack packages built') + ret = ( + "There may have been an issue building " + "packages dumping output: 90amd64 stack" + ) + with patch.object(os.path, "isfile", MagicMock(return_value=True)), patch( + "salt.modules.poudriere._check_config_exists", MagicMock(return_value=True) + ): + mock = MagicMock(return_value="90amd64 stack packages built") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}): + self.assertEqual( + poudriere.bulk_build("90amd64", "/root/pkg_list"), + "90amd64 stack packages built", + ) - mock = MagicMock(return_value='90amd64 stack') - with patch.dict(poudriere.__salt__, {'cmd.run': mock}): - self.assertEqual(poudriere.bulk_build('90amd64', - '/root/pkg_list'), ret) + mock = MagicMock(return_value="90amd64 stack") + with patch.dict(poudriere.__salt__, {"cmd.run": mock}): + self.assertEqual(poudriere.bulk_build("90amd64", "/root/pkg_list"), ret) diff --git a/tests/unit/modules/test_powerpath.py b/tests/unit/modules/test_powerpath.py index 6636cf65a3b..7bd6cf46569 100644 --- a/tests/unit/modules/test_powerpath.py +++ b/tests/unit/modules/test_powerpath.py @@ -1,33 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.modules.powerpath as powerpath +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PowerpathTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.powerpath - ''' + """ + def setup_loader_modules(self): return {powerpath: {}} def test_has_powerpath(self): - ''' + """ Test for powerpath - ''' - with patch('os.path.exists') as mock_exists: + """ + with patch("os.path.exists") as mock_exists: mock_exists.return_value = True self.assertTrue(powerpath.has_powerpath()) @@ -35,41 +34,54 @@ class PowerpathTestCase(TestCase, LoaderModuleMockMixin): self.assertFalse(powerpath.has_powerpath()) def test_list_licenses(self): - ''' + """ Test to returns a list of applied powerpath license keys - ''' - with patch.dict(powerpath.__salt__, - {'cmd.run': MagicMock(return_value='A\nB')}): + """ + with patch.dict( + powerpath.__salt__, {"cmd.run": MagicMock(return_value="A\nB")} + ): self.assertListEqual(powerpath.list_licenses(), []) def test_add_license(self): - ''' + """ Test to add a license - ''' - with patch.object(powerpath, 'has_powerpath', return_value=False): - self.assertDictEqual(powerpath.add_license('key'), - {'output': 'PowerPath is not installed', - 'result': False, 'retcode': -1}) + """ + with patch.object(powerpath, "has_powerpath", return_value=False): + self.assertDictEqual( + powerpath.add_license("key"), + { + "output": "PowerPath is not installed", + "result": False, + "retcode": -1, + }, + ) - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'stderr'}) - with patch.object(powerpath, 'has_powerpath', return_value=True): - with patch.dict(powerpath.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(powerpath.add_license('key'), - {'output': 'stderr', 'result': False, - 'retcode': 1}) + mock = MagicMock(return_value={"retcode": 1, "stderr": "stderr"}) + with patch.object(powerpath, "has_powerpath", return_value=True): + with patch.dict(powerpath.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual( + powerpath.add_license("key"), + {"output": "stderr", "result": False, "retcode": 1}, + ) def test_remove_license(self): - ''' + """ Test to remove a license - ''' - with patch.object(powerpath, 'has_powerpath', return_value=False): - self.assertDictEqual(powerpath.remove_license('key'), - {'output': 'PowerPath is not installed', - 'result': False, 'retcode': -1}) + """ + with patch.object(powerpath, "has_powerpath", return_value=False): + self.assertDictEqual( + powerpath.remove_license("key"), + { + "output": "PowerPath is not installed", + "result": False, + "retcode": -1, + }, + ) - mock = MagicMock(return_value={'retcode': 1, 'stderr': 'stderr'}) - with patch.object(powerpath, 'has_powerpath', return_value=True): - with patch.dict(powerpath.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(powerpath.remove_license('key'), - {'output': 'stderr', 'result': False, - 'retcode': 1}) + mock = MagicMock(return_value={"retcode": 1, "stderr": "stderr"}) + with patch.object(powerpath, "has_powerpath", return_value=True): + with patch.dict(powerpath.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual( + powerpath.remove_license("key"), + {"output": "stderr", "result": False, "retcode": 1}, + ) diff --git a/tests/unit/modules/test_proxy.py b/tests/unit/modules/test_proxy.py index b2fa4203592..191e96f03f1 100644 --- a/tests/unit/modules/test_proxy.py +++ b/tests/unit/modules/test_proxy.py @@ -1,362 +1,443 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.proxy as proxy # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - call -) class ProxyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.proxy - ''' + """ + def setup_loader_modules(self): - return {proxy: {'__grains__': {'os': 'Darwin'}}} + return {proxy: {"__grains__": {"os": "Darwin"}}} def test_get_http_proxy_macos(self): - ''' + """ Test to make sure that we correctly get the current proxy info on macOS - ''' - mock = MagicMock(return_value='Enabled: Yes\nServer: 192.168.0.1\nPort: 3128\nAuthenticated Proxy Enabled: 0') - expected = { - 'enabled': True, - 'server': '192.168.0.1', - 'port': '3128' - } + """ + mock = MagicMock( + return_value="Enabled: Yes\nServer: 192.168.0.1\nPort: 3128\nAuthenticated Proxy Enabled: 0" + ) + expected = {"enabled": True, "server": "192.168.0.1", "port": "3128"} - with patch.dict(proxy.__salt__, {'cmd.run': mock}): + with patch.dict(proxy.__salt__, {"cmd.run": mock}): out = proxy.get_http_proxy() - mock.assert_called_once_with('networksetup -getwebproxy Ethernet') + mock.assert_called_once_with("networksetup -getwebproxy Ethernet") self.assertEqual(expected, out) def test_get_https_proxy_macos(self): - ''' + """ Test to make sure that we correctly get the current proxy info on macOS - ''' - mock = MagicMock(return_value='Enabled: Yes\nServer: 192.168.0.1\nPort: 3128\nAuthenticated Proxy Enabled: 0') - expected = { - 'enabled': True, - 'server': '192.168.0.1', - 'port': '3128' - } + """ + mock = MagicMock( + return_value="Enabled: Yes\nServer: 192.168.0.1\nPort: 3128\nAuthenticated Proxy Enabled: 0" + ) + expected = {"enabled": True, "server": "192.168.0.1", "port": "3128"} - with patch.dict(proxy.__salt__, {'cmd.run': mock}): + with patch.dict(proxy.__salt__, {"cmd.run": mock}): out = proxy.get_https_proxy() - mock.assert_called_once_with('networksetup -getsecurewebproxy Ethernet') + mock.assert_called_once_with("networksetup -getsecurewebproxy Ethernet") self.assertEqual(expected, out) def test_get_ftp_proxy_macos(self): - ''' + """ Test to make sure that we correctly get the current proxy info on macOS - ''' - mock = MagicMock(return_value='Enabled: Yes\nServer: 192.168.0.1\nPort: 3128\nAuthenticated Proxy Enabled: 0') - expected = { - 'enabled': True, - 'server': '192.168.0.1', - 'port': '3128' - } + """ + mock = MagicMock( + return_value="Enabled: Yes\nServer: 192.168.0.1\nPort: 3128\nAuthenticated Proxy Enabled: 0" + ) + expected = {"enabled": True, "server": "192.168.0.1", "port": "3128"} - with patch.dict(proxy.__salt__, {'cmd.run': mock}): + with patch.dict(proxy.__salt__, {"cmd.run": mock}): out = proxy.get_ftp_proxy() - mock.assert_called_once_with('networksetup -getftpproxy Ethernet') + mock.assert_called_once_with("networksetup -getftpproxy Ethernet") self.assertEqual(expected, out) def test_get_http_proxy_macos_none(self): - ''' + """ Test to make sure that we correctly return when theres no proxy set - ''' - mock = MagicMock(return_value='Enabled: No\nServer:\nPort: 0\nAuthenticated Proxy Enabled: 0') + """ + mock = MagicMock( + return_value="Enabled: No\nServer:\nPort: 0\nAuthenticated Proxy Enabled: 0" + ) - with patch.dict(proxy.__salt__, {'cmd.run': mock}): + with patch.dict(proxy.__salt__, {"cmd.run": mock}): out = proxy.get_http_proxy() - mock.assert_called_once_with('networksetup -getwebproxy Ethernet') + mock.assert_called_once_with("networksetup -getwebproxy Ethernet") self.assertEqual({}, out) def test_set_http_proxy_macos(self): - ''' + """ Test to make sure that we correctly set the proxy info on macOS - ''' + """ mock = MagicMock() - with patch.dict(proxy.__salt__, {'cmd.run': mock}): - out = proxy.set_http_proxy('192.168.0.1', 3128, 'frank', 'badpassw0rd', bypass_hosts='.moo.com,.salt.com') - mock.assert_called_once_with('networksetup -setwebproxy Ethernet 192.168.0.1 3128 On frank badpassw0rd') + with patch.dict(proxy.__salt__, {"cmd.run": mock}): + out = proxy.set_http_proxy( + "192.168.0.1", + 3128, + "frank", + "badpassw0rd", + bypass_hosts=".moo.com,.salt.com", + ) + mock.assert_called_once_with( + "networksetup -setwebproxy Ethernet 192.168.0.1 3128 On frank badpassw0rd" + ) self.assertTrue(out) def test_set_https_proxy_macos(self): - ''' + """ Test to make sure that we correctly set the proxy info on macOS - ''' + """ mock = MagicMock() - with patch.dict(proxy.__salt__, {'cmd.run': mock}): - out = proxy.set_https_proxy('192.168.0.1', 3128, 'frank', 'passw0rd', bypass_hosts='.moo.com,.salt.com') - mock.assert_called_once_with('networksetup -setsecurewebproxy Ethernet 192.168.0.1 3128 On frank passw0rd') + with patch.dict(proxy.__salt__, {"cmd.run": mock}): + out = proxy.set_https_proxy( + "192.168.0.1", + 3128, + "frank", + "passw0rd", + bypass_hosts=".moo.com,.salt.com", + ) + mock.assert_called_once_with( + "networksetup -setsecurewebproxy Ethernet 192.168.0.1 3128 On frank passw0rd" + ) self.assertTrue(out) def test_set_ftp_proxy_macos(self): - ''' + """ Test to make sure that we correctly set the proxy info on macOS - ''' + """ mock = MagicMock() - with patch.dict(proxy.__salt__, {'cmd.run': mock}): - out = proxy.set_ftp_proxy('192.168.0.1', 3128, 'frank', 'badpassw0rd', bypass_hosts='.moo.com,.salt.com') - mock.assert_called_once_with('networksetup -setftpproxy Ethernet 192.168.0.1 3128 On frank badpassw0rd') + with patch.dict(proxy.__salt__, {"cmd.run": mock}): + out = proxy.set_ftp_proxy( + "192.168.0.1", + 3128, + "frank", + "badpassw0rd", + bypass_hosts=".moo.com,.salt.com", + ) + mock.assert_called_once_with( + "networksetup -setftpproxy Ethernet 192.168.0.1 3128 On frank badpassw0rd" + ) self.assertTrue(out) def test_get_http_proxy_windows(self): - ''' + """ Test to make sure that we correctly get the current proxy info on Windows - ''' - result = {'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'} + """ + result = { + "vdata": "http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128" + } mock = MagicMock(return_value=result) - expected = {'server': '192.168.0.1', - 'port': '3128'} - with patch.dict(proxy.__grains__, {'os': 'Windows'}): - with patch.dict(proxy.__salt__, {'reg.read_value': mock}): + expected = {"server": "192.168.0.1", "port": "3128"} + with patch.dict(proxy.__grains__, {"os": "Windows"}): + with patch.dict(proxy.__salt__, {"reg.read_value": mock}): out = proxy.get_http_proxy() mock.assert_called_once_with( - hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyServer') + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyServer", + ) self.assertEqual(expected, out) def test_get_https_proxy_windows(self): - ''' + """ Test to make sure that we correctly get the current proxy info on Windows - ''' - result = {'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'} + """ + result = { + "vdata": "http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128" + } mock = MagicMock(return_value=result) - expected = {'server': '192.168.0.2', - 'port': '3128'} - with patch.dict(proxy.__grains__, {'os': 'Windows'}): - with patch.dict(proxy.__salt__, {'reg.read_value': mock}): + expected = {"server": "192.168.0.2", "port": "3128"} + with patch.dict(proxy.__grains__, {"os": "Windows"}): + with patch.dict(proxy.__salt__, {"reg.read_value": mock}): out = proxy.get_https_proxy() mock.assert_called_once_with( - hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyServer') + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyServer", + ) self.assertEqual(expected, out) def test_get_ftp_proxy_windows(self): - ''' + """ Test to make sure that we correctly get the current proxy info on Windows - ''' - result = {'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'} + """ + result = { + "vdata": "http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128" + } mock = MagicMock(return_value=result) - expected = {'server': '192.168.0.3', - 'port': '3128'} - with patch.dict(proxy.__grains__, {'os': 'Windows'}): - with patch.dict(proxy.__salt__, {'reg.read_value': mock}): + expected = {"server": "192.168.0.3", "port": "3128"} + with patch.dict(proxy.__grains__, {"os": "Windows"}): + with patch.dict(proxy.__salt__, {"reg.read_value": mock}): out = proxy.get_ftp_proxy() mock.assert_called_once_with( - hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyServer') + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyServer", + ) self.assertEqual(expected, out) def test_get_all_proxies_macos_fails(self): mock = MagicMock() - with patch.dict(proxy.__salt__, {'reg.read_value': mock}): + with patch.dict(proxy.__salt__, {"reg.read_value": mock}): out = proxy.get_proxy_win() assert not mock.called self.assertEqual(out, None) def test_get_all_proxies_windows(self): - ''' + """ Test to make sure that we correctly get the current proxy info on Windows - ''' - results = [{'vdata': 'http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128'}, - {'vdata': 1}] + """ + results = [ + { + "vdata": "http=192.168.0.1:3128;https=192.168.0.2:3128;ftp=192.168.0.3:3128" + }, + {"vdata": 1}, + ] mock = MagicMock(side_effect=results) - expected = {'enabled': True, - 'http': {'server': '192.168.0.1', - 'port': '3128'}, - 'https': {'server': '192.168.0.2', - 'port': '3128'}, - 'ftp': {'server': '192.168.0.3', - 'port': '3128'}} + expected = { + "enabled": True, + "http": {"server": "192.168.0.1", "port": "3128"}, + "https": {"server": "192.168.0.2", "port": "3128"}, + "ftp": {"server": "192.168.0.3", "port": "3128"}, + } calls = [ - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyServer'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyEnable')] - with patch.dict(proxy.__grains__, {'os': 'Windows'}): - with patch.dict(proxy.__salt__, {'reg.read_value': mock}): + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyServer", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyEnable", + ), + ] + with patch.dict(proxy.__grains__, {"os": "Windows"}): + with patch.dict(proxy.__salt__, {"reg.read_value": mock}): out = proxy.get_proxy_win() mock.assert_has_calls(calls) self.assertEqual(expected, out) def test_set_http_proxy_windows(self): - ''' + """ Test to make sure that we correctly set the proxy info on Windows - ''' + """ calls = [ - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyServer', - vdata='http=192.168.0.1:3128;'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyEnable', - vdata=1, - vtype='REG_DWORD'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyOverride', - vdata='<local>;.moo.com;.salt.com')] + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyServer", + vdata="http=192.168.0.1:3128;", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyEnable", + vdata=1, + vtype="REG_DWORD", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyOverride", + vdata="<local>;.moo.com;.salt.com", + ), + ] mock_reg = MagicMock() mock_cmd = MagicMock() - with patch.dict(proxy.__grains__, {'os': 'Windows'}): - with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, - 'cmd.run': mock_cmd}): - out = proxy.set_http_proxy(server='192.168.0.1', - port=3128, - bypass_hosts=['.moo.com', '.salt.com']) + with patch.dict(proxy.__grains__, {"os": "Windows"}): + with patch.dict( + proxy.__salt__, {"reg.set_value": mock_reg, "cmd.run": mock_cmd} + ): + out = proxy.set_http_proxy( + server="192.168.0.1", + port=3128, + bypass_hosts=[".moo.com", ".salt.com"], + ) mock_reg.assert_has_calls(calls) - mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie') + mock_cmd.assert_called_once_with("netsh winhttp import proxy source=ie") self.assertTrue(out) def test_set_https_proxy_windows(self): - ''' + """ Test to make sure that we correctly set the proxy info on Windows - ''' + """ calls = [ - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyServer', - vdata='https=192.168.0.1:3128;'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyEnable', - vdata=1, - vtype='REG_DWORD'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyOverride', - vdata='<local>;.moo.com;.salt.com')] + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyServer", + vdata="https=192.168.0.1:3128;", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyEnable", + vdata=1, + vtype="REG_DWORD", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyOverride", + vdata="<local>;.moo.com;.salt.com", + ), + ] mock_reg = MagicMock() mock_cmd = MagicMock() - with patch.dict(proxy.__grains__, {'os': 'Windows'}): - with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, - 'cmd.run': mock_cmd}): - out = proxy.set_https_proxy(server='192.168.0.1', - port=3128, - bypass_hosts=['.moo.com', '.salt.com']) + with patch.dict(proxy.__grains__, {"os": "Windows"}): + with patch.dict( + proxy.__salt__, {"reg.set_value": mock_reg, "cmd.run": mock_cmd} + ): + out = proxy.set_https_proxy( + server="192.168.0.1", + port=3128, + bypass_hosts=[".moo.com", ".salt.com"], + ) mock_reg.assert_has_calls(calls) - mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie') + mock_cmd.assert_called_once_with("netsh winhttp import proxy source=ie") self.assertTrue(out) def test_set_ftp_proxy_windows(self): - ''' + """ Test to make sure that we correctly set the proxy info on Windows - ''' + """ calls = [ - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyServer', - vdata='ftp=192.168.0.1:3128;'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyEnable', - vdata=1, - vtype='REG_DWORD'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyOverride', - vdata='<local>;.moo.com;.salt.com')] + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyServer", + vdata="ftp=192.168.0.1:3128;", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyEnable", + vdata=1, + vtype="REG_DWORD", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyOverride", + vdata="<local>;.moo.com;.salt.com", + ), + ] mock_reg = MagicMock() mock_cmd = MagicMock() - with patch.dict(proxy.__grains__, {'os': 'Windows'}): - with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, - 'cmd.run': mock_cmd}): - out = proxy.set_ftp_proxy(server='192.168.0.1', - port=3128, - bypass_hosts=['.moo.com', '.salt.com']) + with patch.dict(proxy.__grains__, {"os": "Windows"}): + with patch.dict( + proxy.__salt__, {"reg.set_value": mock_reg, "cmd.run": mock_cmd} + ): + out = proxy.set_ftp_proxy( + server="192.168.0.1", + port=3128, + bypass_hosts=[".moo.com", ".salt.com"], + ) mock_reg.assert_has_calls(calls) - mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie') + mock_cmd.assert_called_once_with("netsh winhttp import proxy source=ie") self.assertTrue(out) def test_set_proxy_windows(self): - ''' + """ Test to make sure that we correctly set the proxy info on Windows - ''' + """ calls = [ - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyServer', - vdata='http=192.168.0.1:3128;https=192.168.0.1:3128;ftp=192.168.0.1:3128;'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyEnable', - vdata=1, - vtype='REG_DWORD'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyOverride', - vdata='<local>;.moo.com;.salt.com')] + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyServer", + vdata="http=192.168.0.1:3128;https=192.168.0.1:3128;ftp=192.168.0.1:3128;", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyEnable", + vdata=1, + vtype="REG_DWORD", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyOverride", + vdata="<local>;.moo.com;.salt.com", + ), + ] mock_reg = MagicMock() mock_cmd = MagicMock() - with patch.dict(proxy.__grains__, {'os': 'Windows'}): - with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, - 'cmd.run': mock_cmd}): - out = proxy.set_proxy_win(server='192.168.0.1', - port=3128, - bypass_hosts=['.moo.com', '.salt.com']) + with patch.dict(proxy.__grains__, {"os": "Windows"}): + with patch.dict( + proxy.__salt__, {"reg.set_value": mock_reg, "cmd.run": mock_cmd} + ): + out = proxy.set_proxy_win( + server="192.168.0.1", + port=3128, + bypass_hosts=[".moo.com", ".salt.com"], + ) mock_reg.assert_has_calls(calls) - mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie') + mock_cmd.assert_called_once_with("netsh winhttp import proxy source=ie") self.assertTrue(out) def test_set_proxy_windows_no_ftp(self): - ''' + """ Test to make sure that we correctly set the proxy info on Windows - ''' + """ calls = [ - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyServer', - vdata='http=192.168.0.1:3128;https=192.168.0.1:3128;'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyEnable', - vdata=1, - vtype='REG_DWORD'), - call(hive='HKEY_CURRENT_USER', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings', - vname='ProxyOverride', - vdata='<local>;.moo.com;.salt.com')] + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyServer", + vdata="http=192.168.0.1:3128;https=192.168.0.1:3128;", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyEnable", + vdata=1, + vtype="REG_DWORD", + ), + call( + hive="HKEY_CURRENT_USER", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", + vname="ProxyOverride", + vdata="<local>;.moo.com;.salt.com", + ), + ] mock_reg = MagicMock() mock_cmd = MagicMock() - with patch.dict(proxy.__grains__, {'os': 'Windows'}): - with patch.dict(proxy.__salt__, {'reg.set_value': mock_reg, - 'cmd.run': mock_cmd}): - out = proxy.set_proxy_win(server='192.168.0.1', - port=3128, - types=['http', 'https'], - bypass_hosts=['.moo.com', '.salt.com']) + with patch.dict(proxy.__grains__, {"os": "Windows"}): + with patch.dict( + proxy.__salt__, {"reg.set_value": mock_reg, "cmd.run": mock_cmd} + ): + out = proxy.set_proxy_win( + server="192.168.0.1", + port=3128, + types=["http", "https"], + bypass_hosts=[".moo.com", ".salt.com"], + ) mock_reg.assert_has_calls(calls) - mock_cmd.assert_called_once_with('netsh winhttp import proxy source=ie') + mock_cmd.assert_called_once_with("netsh winhttp import proxy source=ie") self.assertTrue(out) diff --git a/tests/unit/modules/test_ps.py b/tests/unit/modules/test_ps.py index 139634cb3be..e1e6efda9b7 100644 --- a/tests/unit/modules/test_ps.py +++ b/tests/unit/modules/test_ps.py @@ -1,48 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place <mp@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -from collections import namedtuple -import time +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, call, Mock +import time +from collections import namedtuple + +import salt.modules.ps as ps # Import Salt libs import salt.utils.data -import salt.modules.ps as ps - -HAS_PSUTIL_VERSION = False +import salt.utils.psutil_compat as psutil # Import 3rd-party libs # pylint: disable=import-error,unused-import from salt.ext.six.moves import range # pylint: disable=redefined-builtin -import salt.utils.psutil_compat as psutil +from tests.support.mock import MagicMock, Mock, call, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf + +HAS_PSUTIL_VERSION = False + PSUTIL2 = psutil.version_info >= (2, 0) -STUB_CPU_TIMES = namedtuple('cputimes', 'user nice system idle')(1, 2, 3, 4) -STUB_VIRT_MEM = namedtuple('vmem', 'total available percent used free')(1000, 500, 50, 500, 500) -STUB_SWAP_MEM = namedtuple('swap', 'total used free percent sin sout')(1000, 500, 500, 50, 0, 0) -STUB_PHY_MEM_USAGE = namedtuple('usage', 'total used free percent')(1000, 500, 500, 50) -STUB_DISK_PARTITION = namedtuple( - 'partition', - 'device mountpoint fstype, opts')( - '/dev/disk0s2', '/', 'hfs', 'rw,local,rootfs,dovolfs,journaled,multilabel') -STUB_DISK_USAGE = namedtuple('usage', 'total used free percent')(1000, 500, 500, 50) +STUB_CPU_TIMES = namedtuple("cputimes", "user nice system idle")(1, 2, 3, 4) +STUB_VIRT_MEM = namedtuple("vmem", "total available percent used free")( + 1000, 500, 50, 500, 500 +) +STUB_SWAP_MEM = namedtuple("swap", "total used free percent sin sout")( + 1000, 500, 500, 50, 0, 0 +) +STUB_PHY_MEM_USAGE = namedtuple("usage", "total used free percent")(1000, 500, 500, 50) +STUB_DISK_PARTITION = namedtuple("partition", "device mountpoint fstype, opts")( + "/dev/disk0s2", "/", "hfs", "rw,local,rootfs,dovolfs,journaled,multilabel" +) +STUB_DISK_USAGE = namedtuple("usage", "total used free percent")(1000, 500, 500, 50) STUB_NETWORK_IO = namedtuple( - 'iostat', - 'bytes_sent, bytes_recv, packets_sent, packets_recv, errin errout dropin dropout')( - 1000, 2000, 500, 600, 1, 2, 3, 4) + "iostat", + "bytes_sent, bytes_recv, packets_sent, packets_recv, errin errout dropin dropout", +)(1000, 2000, 500, 600, 1, 2, 3, 4) STUB_DISK_IO = namedtuple( - 'iostat', - 'read_count, write_count, read_bytes, write_bytes, read_time, write_time')( - 1000, 2000, 500, 600, 2000, 3000) -STUB_USER = namedtuple('user', 'name, terminal, host, started')('bdobbs', 'ttys000', 'localhost', 0.0) + "iostat", "read_count, write_count, read_bytes, write_bytes, read_time, write_time" +)(1000, 2000, 500, 600, 2000, 3000) +STUB_USER = namedtuple("user", "name, terminal, host, started")( + "bdobbs", "ttys000", "localhost", 0.0 +) if psutil.version_info >= (0, 6, 0): HAS_PSUTIL_VERSION = True @@ -66,27 +73,35 @@ def _get_proc_pid(proc): class DummyProcess(object): - ''' + """ Dummy class to emulate psutil.Process. This ensures that _any_ string values used for any of the options passed in are converted to str types on both Python 2 and Python 3. - ''' - def __init__(self, cmdline=None, create_time=None, name=None, status=None, - username=None, pid=None): + """ + + def __init__( + self, + cmdline=None, + create_time=None, + name=None, + status=None, + username=None, + pid=None, + ): self._cmdline = salt.utils.data.decode( - cmdline if cmdline is not None else [], - to_str=True) + cmdline if cmdline is not None else [], to_str=True + ) self._create_time = salt.utils.data.decode( - create_time if create_time is not None else time.time(), - to_str=True) + create_time if create_time is not None else time.time(), to_str=True + ) self._name = salt.utils.data.decode( - name if name is not None else [], - to_str=True) + name if name is not None else [], to_str=True + ) self._status = salt.utils.data.decode(status, to_str=True) self._username = salt.utils.data.decode(username, to_str=True) self._pid = salt.utils.data.decode( - pid if pid is not None else 12345, - to_str=True) + pid if pid is not None else 12345, to_str=True + ) def cmdline(self): return self._cmdline @@ -109,108 +124,191 @@ class DummyProcess(object): class PsTestCase(TestCase): def setUp(self): - self.mocked_proc = mocked_proc = MagicMock('salt.utils.psutil_compat.Process') + self.mocked_proc = mocked_proc = MagicMock("salt.utils.psutil_compat.Process") if PSUTIL2: self.mocked_proc.name = Mock(return_value="test_mock_proc") self.mocked_proc.pid = Mock(return_value=9999999999) else: - self.mocked_proc.name = 'test_mock_proc' + self.mocked_proc.name = "test_mock_proc" self.mocked_proc.pid = 9999999999 - @skipIf(not ps.PSUTIL2, 'Only run for psutil 2.x') + @skipIf(not ps.PSUTIL2, "Only run for psutil 2.x") def test__get_proc_cmdline(self): - cmdline = ['echo', 'питон'] + cmdline = ["echo", "питон"] ret = ps._get_proc_cmdline(DummyProcess(cmdline=cmdline)) assert ret == cmdline, ret def test_get_pid_list(self): - with patch('salt.utils.psutil_compat.pids', - MagicMock(return_value=STUB_PID_LIST)): + with patch( + "salt.utils.psutil_compat.pids", MagicMock(return_value=STUB_PID_LIST) + ): self.assertListEqual(STUB_PID_LIST, ps.get_pid_list()) def test_kill_pid(self): - with patch('salt.utils.psutil_compat.Process') as send_signal_mock: + with patch("salt.utils.psutil_compat.Process") as send_signal_mock: ps.kill_pid(0, signal=999) self.assertEqual(send_signal_mock.call_args, call(0)) def test_pkill(self): - with patch('salt.utils.psutil_compat.Process.send_signal'), \ - patch('salt.utils.psutil_compat.process_iter', - MagicMock(return_value=[self.mocked_proc])): + with patch("salt.utils.psutil_compat.Process.send_signal"), patch( + "salt.utils.psutil_compat.process_iter", + MagicMock(return_value=[self.mocked_proc]), + ): self.mocked_proc.send_signal = MagicMock() test_signal = 1234 ps.pkill(_get_proc_name(self.mocked_proc), signal=test_signal) self.assertEqual(self.mocked_proc.send_signal.call_args, call(test_signal)) def test_pgrep(self): - with patch('salt.utils.psutil_compat.process_iter', - MagicMock(return_value=[self.mocked_proc])): - self.assertIn(_get_proc_pid(self.mocked_proc), ps.pgrep(_get_proc_name(self.mocked_proc))) + with patch( + "salt.utils.psutil_compat.process_iter", + MagicMock(return_value=[self.mocked_proc]), + ): + self.assertIn( + _get_proc_pid(self.mocked_proc), + ps.pgrep(_get_proc_name(self.mocked_proc)), + ) def test_cpu_percent(self): - with patch('salt.utils.psutil_compat.cpu_percent', - MagicMock(return_value=1)): + with patch("salt.utils.psutil_compat.cpu_percent", MagicMock(return_value=1)): self.assertEqual(ps.cpu_percent(), 1) def test_cpu_times(self): - with patch('salt.utils.psutil_compat.cpu_times', - MagicMock(return_value=STUB_CPU_TIMES)): - self.assertDictEqual({'idle': 4, 'nice': 2, 'system': 3, 'user': 1}, ps.cpu_times()) + with patch( + "salt.utils.psutil_compat.cpu_times", MagicMock(return_value=STUB_CPU_TIMES) + ): + self.assertDictEqual( + {"idle": 4, "nice": 2, "system": 3, "user": 1}, ps.cpu_times() + ) - @skipIf(HAS_PSUTIL_VERSION is False, 'psutil 0.6.0 or greater is required for this test') + @skipIf( + HAS_PSUTIL_VERSION is False, "psutil 0.6.0 or greater is required for this test" + ) def test_virtual_memory(self): - with patch('salt.utils.psutil_compat.virtual_memory', - MagicMock(return_value=STUB_VIRT_MEM)): - self.assertDictEqual({'used': 500, 'total': 1000, 'available': 500, 'percent': 50, 'free': 500}, - ps.virtual_memory()) + with patch( + "salt.utils.psutil_compat.virtual_memory", + MagicMock(return_value=STUB_VIRT_MEM), + ): + self.assertDictEqual( + { + "used": 500, + "total": 1000, + "available": 500, + "percent": 50, + "free": 500, + }, + ps.virtual_memory(), + ) - @skipIf(HAS_PSUTIL_VERSION is False, 'psutil 0.6.0 or greater is required for this test') + @skipIf( + HAS_PSUTIL_VERSION is False, "psutil 0.6.0 or greater is required for this test" + ) def test_swap_memory(self): - with patch('salt.utils.psutil_compat.swap_memory', - MagicMock(return_value=STUB_SWAP_MEM)): - self.assertDictEqual({'used': 500, 'total': 1000, 'percent': 50, 'free': 500, 'sin': 0, 'sout': 0}, - ps.swap_memory()) + with patch( + "salt.utils.psutil_compat.swap_memory", + MagicMock(return_value=STUB_SWAP_MEM), + ): + self.assertDictEqual( + { + "used": 500, + "total": 1000, + "percent": 50, + "free": 500, + "sin": 0, + "sout": 0, + }, + ps.swap_memory(), + ) def test_disk_partitions(self): - with patch('salt.utils.psutil_compat.disk_partitions', - MagicMock(return_value=[STUB_DISK_PARTITION])): + with patch( + "salt.utils.psutil_compat.disk_partitions", + MagicMock(return_value=[STUB_DISK_PARTITION]), + ): self.assertDictEqual( - {'device': '/dev/disk0s2', 'mountpoint': '/', 'opts': 'rw,local,rootfs,dovolfs,journaled,multilabel', - 'fstype': 'hfs'}, - ps.disk_partitions()[0]) + { + "device": "/dev/disk0s2", + "mountpoint": "/", + "opts": "rw,local,rootfs,dovolfs,journaled,multilabel", + "fstype": "hfs", + }, + ps.disk_partitions()[0], + ) def test_disk_usage(self): - with patch('salt.utils.psutil_compat.disk_usage', - MagicMock(return_value=STUB_DISK_USAGE)): - self.assertDictEqual({'used': 500, 'total': 1000, 'percent': 50, 'free': 500}, ps.disk_usage('DUMMY_PATH')) + with patch( + "salt.utils.psutil_compat.disk_usage", + MagicMock(return_value=STUB_DISK_USAGE), + ): + self.assertDictEqual( + {"used": 500, "total": 1000, "percent": 50, "free": 500}, + ps.disk_usage("DUMMY_PATH"), + ) def test_disk_partition_usage(self): - with patch('salt.utils.psutil_compat.disk_partitions', - MagicMock(return_value=[STUB_DISK_PARTITION])): + with patch( + "salt.utils.psutil_compat.disk_partitions", + MagicMock(return_value=[STUB_DISK_PARTITION]), + ): self.assertDictEqual( - {'device': '/dev/disk0s2', 'mountpoint': '/', 'opts': 'rw,local,rootfs,dovolfs,journaled,multilabel', - 'fstype': 'hfs'}, - ps.disk_partitions()[0]) + { + "device": "/dev/disk0s2", + "mountpoint": "/", + "opts": "rw,local,rootfs,dovolfs,journaled,multilabel", + "fstype": "hfs", + }, + ps.disk_partitions()[0], + ) def test_network_io_counters(self): - with patch('salt.utils.psutil_compat.net_io_counters', - MagicMock(return_value=STUB_NETWORK_IO)): + with patch( + "salt.utils.psutil_compat.net_io_counters", + MagicMock(return_value=STUB_NETWORK_IO), + ): self.assertDictEqual( - {'packets_sent': 500, 'packets_recv': 600, 'bytes_recv': 2000, 'dropout': 4, 'bytes_sent': 1000, - 'errout': 2, 'errin': 1, 'dropin': 3}, ps.network_io_counters()) + { + "packets_sent": 500, + "packets_recv": 600, + "bytes_recv": 2000, + "dropout": 4, + "bytes_sent": 1000, + "errout": 2, + "errin": 1, + "dropin": 3, + }, + ps.network_io_counters(), + ) def test_disk_io_counters(self): - with patch('salt.utils.psutil_compat.disk_io_counters', - MagicMock(return_value=STUB_DISK_IO)): + with patch( + "salt.utils.psutil_compat.disk_io_counters", + MagicMock(return_value=STUB_DISK_IO), + ): self.assertDictEqual( - {'read_time': 2000, 'write_bytes': 600, 'read_bytes': 500, 'write_time': 3000, 'read_count': 1000, - 'write_count': 2000}, ps.disk_io_counters()) + { + "read_time": 2000, + "write_bytes": 600, + "read_bytes": 500, + "write_time": 3000, + "read_count": 1000, + "write_count": 2000, + }, + ps.disk_io_counters(), + ) def test_get_users(self): - with patch('salt.utils.psutil_compat.users', - MagicMock(return_value=[STUB_USER])): - self.assertDictEqual({'terminal': 'ttys000', 'started': 0.0, 'host': 'localhost', 'name': 'bdobbs'}, - ps.get_users()[0]) + with patch( + "salt.utils.psutil_compat.users", MagicMock(return_value=[STUB_USER]) + ): + self.assertDictEqual( + { + "terminal": "ttys000", + "started": 0.0, + "host": "localhost", + "name": "bdobbs", + }, + ps.get_users()[0], + ) ## This is commented out pending discussion on https://github.com/saltstack/salt/commit/2e5c3162ef87cca8a2c7b12ade7c7e1b32028f0a # @skipIf(not HAS_UTMP, "The utmp module must be installed to run test_get_users_utmp()") diff --git a/tests/unit/modules/test_publish.py b/tests/unit/modules/test_publish.py index 82055757672..dc523737986 100644 --- a/tests/unit/modules/test_publish.py +++ b/tests/unit/modules/test_publish.py @@ -1,43 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.publish as publish from salt.exceptions import SaltReqTimeoutError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SAuth(object): - ''' + """ Mock SAuth class - ''' + """ + def __init__(self, __opts__): self.tok = None def gen_token(self, tok): - ''' + """ Mock gen_token method - ''' + """ self.tok = tok - return 'salt_tok' + return "salt_tok" class Channel(object): - ''' + """ Mock Channel class - ''' + """ + flag = None def __init__(self): @@ -45,16 +44,16 @@ class Channel(object): self.load = None def factory(self, tok): - ''' + """ Mock factory method - ''' + """ self.tok = tok return Channel() def send(self, load): - ''' + """ Mock send method - ''' + """ self.load = load if self.flag == 1: raise SaltReqTimeoutError @@ -71,15 +70,16 @@ class Channel(object): class PublishTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.publish - ''' + """ + def setup_loader_modules(self): return {publish: {}} @classmethod def setUpClass(cls): - cls.channel_patcher = patch('salt.transport.client.ReqChannel', Channel()) + cls.channel_patcher = patch("salt.transport.client.ReqChannel", Channel()) cls.channel_patcher.start() @classmethod @@ -88,44 +88,43 @@ class PublishTestCase(TestCase, LoaderModuleMockMixin): del cls.channel_patcher def setUp(self): - patcher = patch('salt.crypt.SAuth', return_value=SAuth(publish.__opts__)) + patcher = patch("salt.crypt.SAuth", return_value=SAuth(publish.__opts__)) patcher.start() self.addCleanup(patcher.stop) # 'publish' function tests: 1 def test_publish(self): - ''' + """ Test if it publish a command from the minion out to other minions. - ''' - self.assertDictEqual(publish.publish('os:Fedora', 'publish.salt'), {}) + """ + self.assertDictEqual(publish.publish("os:Fedora", "publish.salt"), {}) # 'full_data' function tests: 1 def test_full_data(self): - ''' + """ Test if it return the full data about the publication - ''' - self.assertDictEqual(publish.publish('*', 'publish.salt'), {}) + """ + self.assertDictEqual(publish.publish("*", "publish.salt"), {}) # 'runner' function tests: 1 def test_runner(self): - ''' + """ Test if it execute a runner on the master and return the data from the runner function - ''' - ret = ('No access to master. If using salt-call with --local,' - ' please remove.') - self.assertEqual(publish.runner('manage.down'), ret) + """ + ret = "No access to master. If using salt-call with --local," " please remove." + self.assertEqual(publish.runner("manage.down"), ret) mock = MagicMock(return_value=True) - mock_id = MagicMock(return_value='salt_id') - with patch.dict(publish.__opts__, {'master_uri': mock, - 'id': mock_id}): + mock_id = MagicMock(return_value="salt_id") + with patch.dict(publish.__opts__, {"master_uri": mock, "id": mock_id}): Channel.flag = 0 - self.assertTrue(publish.runner('manage.down')) + self.assertTrue(publish.runner("manage.down")) Channel.flag = 1 - self.assertEqual(publish.runner('manage.down'), - "'manage.down' runner publish timed out") + self.assertEqual( + publish.runner("manage.down"), "'manage.down' runner publish timed out" + ) diff --git a/tests/unit/modules/test_puppet.py b/tests/unit/modules/test_puppet.py index 9ee4e921ba1..15d8c5e09db 100644 --- a/tests/unit/modules/test_puppet.py +++ b/tests/unit/modules/test_puppet.py @@ -1,185 +1,182 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - mock_open, - MagicMock, - patch, -) +import salt.modules.puppet as puppet # Import Salt Libs import salt.utils.args import salt.utils.files -import salt.modules.puppet as puppet from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class PuppetTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.puppet - ''' + """ + def setup_loader_modules(self): return {puppet: {}} def test_run(self): - ''' + """ Test to execute a puppet run - ''' + """ mock = MagicMock(return_value={"A": "B"}) - with patch.object(salt.utils.args, 'clean_kwargs', mock): - mock = MagicMock(return_value={'retcode': 0}) + with patch.object(salt.utils.args, "clean_kwargs", mock): + mock = MagicMock(return_value={"retcode": 0}) mock_lst = MagicMock(return_value=[]) - with patch.dict(puppet.__salt__, {'cmd.run_all': mock, 'cmd.run': mock_lst}): + with patch.dict( + puppet.__salt__, {"cmd.run_all": mock, "cmd.run": mock_lst} + ): self.assertTrue(puppet.run()) def test_noop(self): - ''' + """ Test to execute a puppet noop run - ''' + """ mock = MagicMock(return_value={"stderr": "A", "stdout": "B"}) - with patch.object(puppet, 'run', mock): - self.assertDictEqual(puppet.noop(), {'stderr': 'A', 'stdout': 'B'}) + with patch.object(puppet, "run", mock): + self.assertDictEqual(puppet.noop(), {"stderr": "A", "stdout": "B"}) def test_enable(self): - ''' + """ Test to enable the puppet agent - ''' + """ mock_lst = MagicMock(return_value=[]) - with patch.dict(puppet.__salt__, {'cmd.run': mock_lst}): + with patch.dict(puppet.__salt__, {"cmd.run": mock_lst}): mock = MagicMock(return_value=True) - with patch.object(os.path, 'isfile', mock): + with patch.object(os.path, "isfile", mock): mock = MagicMock(return_value=True) - with patch.object(os, 'remove', mock): + with patch.object(os, "remove", mock): self.assertTrue(puppet.enable()) - with patch.object(os, 'remove', MagicMock(side_effect=IOError)): + with patch.object(os, "remove", MagicMock(side_effect=IOError)): self.assertRaises(CommandExecutionError, puppet.enable) self.assertFalse(puppet.enable()) def test_disable(self): - ''' + """ Test to disable the puppet agent - ''' + """ mock_lst = MagicMock(return_value=[]) - with patch.dict(puppet.__salt__, {'cmd.run': mock_lst}): + with patch.dict(puppet.__salt__, {"cmd.run": mock_lst}): mock = MagicMock(side_effect=[True, False]) - with patch.object(os.path, 'isfile', mock): + with patch.object(os.path, "isfile", mock): self.assertFalse(puppet.disable()) - with patch('salt.utils.files.fopen', mock_open()): + with patch("salt.utils.files.fopen", mock_open()): self.assertTrue(puppet.disable()) try: - with patch('salt.utils.files.fopen', mock_open()) as m_open: - m_open.side_effect = IOError(13, 'Permission denied:', '/file') + with patch("salt.utils.files.fopen", mock_open()) as m_open: + m_open.side_effect = IOError(13, "Permission denied:", "/file") self.assertRaises(CommandExecutionError, puppet.disable) except StopIteration: pass def test_status(self): - ''' + """ Test to display puppet agent status - ''' + """ mock_lst = MagicMock(return_value=[]) - with patch.dict(puppet.__salt__, {'cmd.run': mock_lst}): + with patch.dict(puppet.__salt__, {"cmd.run": mock_lst}): mock = MagicMock(side_effect=[True]) - with patch.object(os.path, 'isfile', mock): + with patch.object(os.path, "isfile", mock): self.assertEqual(puppet.status(), "Administratively disabled") mock = MagicMock(side_effect=[False, True]) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', mock_open(read_data="1")): + with patch.object(os.path, "isfile", mock): + with patch("salt.utils.files.fopen", mock_open(read_data="1")): mock = MagicMock(return_value=True) - with patch.object(os, 'kill', mock): + with patch.object(os, "kill", mock): self.assertEqual(puppet.status(), "Applying a catalog") mock = MagicMock(side_effect=[False, True]) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', mock_open()): + with patch.object(os.path, "isfile", mock): + with patch("salt.utils.files.fopen", mock_open()): mock = MagicMock(return_value=True) - with patch.object(os, 'kill', mock): + with patch.object(os, "kill", mock): self.assertEqual(puppet.status(), "Stale lockfile") mock = MagicMock(side_effect=[False, False, True]) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', mock_open(read_data="1")): + with patch.object(os.path, "isfile", mock): + with patch("salt.utils.files.fopen", mock_open(read_data="1")): mock = MagicMock(return_value=True) - with patch.object(os, 'kill', mock): + with patch.object(os, "kill", mock): self.assertEqual(puppet.status(), "Idle daemon") mock = MagicMock(side_effect=[False, False, True]) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', mock_open()): + with patch.object(os.path, "isfile", mock): + with patch("salt.utils.files.fopen", mock_open()): mock = MagicMock(return_value=True) - with patch.object(os, 'kill', mock): + with patch.object(os, "kill", mock): self.assertEqual(puppet.status(), "Stale pidfile") mock = MagicMock(side_effect=[False, False, False]) - with patch.object(os.path, 'isfile', mock): + with patch.object(os.path, "isfile", mock): self.assertEqual(puppet.status(), "Stopped") def test_summary(self): - ''' + """ Test to show a summary of the last puppet agent run - ''' + """ mock_lst = MagicMock(return_value=[]) - with patch.dict(puppet.__salt__, {'cmd.run': mock_lst}): - with patch('salt.utils.files.fopen', - mock_open(read_data="resources: 1")): - self.assertDictEqual(puppet.summary(), {'resources': 1}) + with patch.dict(puppet.__salt__, {"cmd.run": mock_lst}): + with patch("salt.utils.files.fopen", mock_open(read_data="resources: 1")): + self.assertDictEqual(puppet.summary(), {"resources": 1}) - permission_error = IOError(errno.EACCES, 'Permission denied:', '/file') - with patch('salt.utils.files.fopen', - mock_open(read_data=permission_error)) as m_open: + permission_error = IOError(errno.EACCES, "Permission denied:", "/file") + with patch( + "salt.utils.files.fopen", mock_open(read_data=permission_error) + ) as m_open: self.assertRaises(CommandExecutionError, puppet.summary) def test_plugin_sync(self): - ''' + """ Test to runs a plugin synch between the puppet master and agent - ''' + """ mock_lst = MagicMock(return_value=[]) - with patch.dict(puppet.__salt__, {'cmd.run': mock_lst}): + with patch.dict(puppet.__salt__, {"cmd.run": mock_lst}): mock_lst = MagicMock(side_effect=[False, True]) - with patch.dict(puppet.__salt__, {'cmd.run': mock_lst}): + with patch.dict(puppet.__salt__, {"cmd.run": mock_lst}): self.assertEqual(puppet.plugin_sync(), "") self.assertTrue(puppet.plugin_sync()) def test_facts(self): - ''' + """ Test to run facter and return the results - ''' - mock = MagicMock(return_value={ - 'retcode': 0, - 'stdout': "1\n2" - }) - with patch.dict(puppet.__salt__, {'cmd.run_all': mock}): - mock = MagicMock(side_effect=[ - ['a', 'b'], - ['c', 'd'], - ]) - with patch.object(puppet, '_format_fact', mock): - self.assertDictEqual(puppet.facts(), {'a': 'b', 'c': 'd'}) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": "1\n2"}) + with patch.dict(puppet.__salt__, {"cmd.run_all": mock}): + mock = MagicMock(side_effect=[["a", "b"], ["c", "d"]]) + with patch.object(puppet, "_format_fact", mock): + self.assertDictEqual(puppet.facts(), {"a": "b", "c": "d"}) def test_fact(self): - ''' + """ Test to run facter for a specific fact - ''' - mock = MagicMock(side_effect=[ - {'retcode': 0, 'stdout': False}, - {'retcode': 0, 'stdout': True}, - ]) - with patch.dict(puppet.__salt__, {'cmd.run_all': mock}): - self.assertEqual(puppet.fact('salt'), '') + """ + mock = MagicMock( + side_effect=[ + {"retcode": 0, "stdout": False}, + {"retcode": 0, "stdout": True}, + ] + ) + with patch.dict(puppet.__salt__, {"cmd.run_all": mock}): + self.assertEqual(puppet.fact("salt"), "") - self.assertTrue(puppet.fact('salt')) + self.assertTrue(puppet.fact("salt")) diff --git a/tests/unit/modules/test_purefb.py b/tests/unit/modules/test_purefb.py index 5a201f818cf..1af1b7eb430 100644 --- a/tests/unit/modules/test_purefb.py +++ b/tests/unit/modules/test_purefb.py @@ -1,75 +1,73 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Simon Dodsley <simon@purestorage.com>` -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, -) - # Import Salt Libs import salt.modules.purefb as purefb +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class PureFBTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.purefb - ''' + """ + def setup_loader_modules(self): return {purefb: {}} def test_fs_create(self): - ''' + """ Test for creation of a filesystem - ''' - with patch.object(purefb, 'fs_create', return_value=True): - self.assertEqual(purefb.fs_create('test'), True) + """ + with patch.object(purefb, "fs_create", return_value=True): + self.assertEqual(purefb.fs_create("test"), True) def test_fs_delete(self): - ''' + """ Test for deletion of a filesystem - ''' - with patch.object(purefb, 'fs_delete', return_value=True): - self.assertEqual(purefb.fs_delete('test'), True) + """ + with patch.object(purefb, "fs_delete", return_value=True): + self.assertEqual(purefb.fs_delete("test"), True) def test_fs_eradicate(self): - ''' + """ Test for eradication of a filesystem - ''' - with patch.object(purefb, 'fs_eradicate', return_value=True): - self.assertEqual(purefb.fs_eradicate('test'), True) + """ + with patch.object(purefb, "fs_eradicate", return_value=True): + self.assertEqual(purefb.fs_eradicate("test"), True) def test_fs_extend(self): - ''' + """ Test for size extention of a filesystem - ''' - with patch.object(purefb, 'fs_extend', return_value=True): - self.assertEqual(purefb.fs_extend('test', '33G'), True) + """ + with patch.object(purefb, "fs_extend", return_value=True): + self.assertEqual(purefb.fs_extend("test", "33G"), True) def test_snap_create(self): - ''' + """ Test for creation of a filesystem snapshot - ''' - with patch.object(purefb, 'snap_create', return_value=True): - self.assertEqual(purefb.snap_create('test', suffix='suffix'), True) + """ + with patch.object(purefb, "snap_create", return_value=True): + self.assertEqual(purefb.snap_create("test", suffix="suffix"), True) def test_snap_delete(self): - ''' + """ Test for deletion of a filesystem snapshot - ''' - with patch.object(purefb, 'snap_delete', return_value=True): - self.assertEqual(purefb.snap_delete('test', suffix='suffix'), True) + """ + with patch.object(purefb, "snap_delete", return_value=True): + self.assertEqual(purefb.snap_delete("test", suffix="suffix"), True) def test_snap_eradicate(self): - ''' + """ Test for eradication of a deleted filesystem snapshot - ''' - with patch.object(purefb, 'snap_eradicate', return_value=True): - self.assertEqual(purefb.snap_eradicate('test', - suffix='suffix'), True) + """ + with patch.object(purefb, "snap_eradicate", return_value=True): + self.assertEqual(purefb.snap_eradicate("test", suffix="suffix"), True) diff --git a/tests/unit/modules/test_pw_group.py b/tests/unit/modules/test_pw_group.py index b65bc7d66a5..dc3315cb97a 100644 --- a/tests/unit/modules/test_pw_group.py +++ b/tests/unit/modules/test_pw_group.py @@ -1,94 +1,94 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.pw_group as pw_group import salt.utils.platform +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + class PwGroupTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test for salt.module.pw_group - ''' + """ + def setup_loader_modules(self): - return {pw_group: {'grinfo': {}}} + return {pw_group: {"grinfo": {}}} def test_add(self): - ''' + """ Tests to add the specified group - ''' - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(pw_group.__salt__, {'cmd.run_all': mock}): - self.assertTrue(pw_group.add('a')) + """ + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(pw_group.__salt__, {"cmd.run_all": mock}): + self.assertTrue(pw_group.add("a")) def test_delete(self): - ''' + """ Tests to remove the named group - ''' - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(pw_group.__salt__, {'cmd.run_all': mock}): - self.assertTrue(pw_group.delete('a')) + """ + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(pw_group.__salt__, {"cmd.run_all": mock}): + self.assertTrue(pw_group.delete("a")) - @skipIf(salt.utils.platform.is_windows(), 'grp not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "grp not available on Windows") def test_info(self): - ''' + """ Tests to return information about a group - ''' - self.assertDictEqual(pw_group.info('name'), {}) + """ + self.assertDictEqual(pw_group.info("name"), {}) - mock = MagicMock(return_value={'gr_name': 'A', - 'gr_passwd': 'B', - 'gr_gid': 1, - 'gr_mem': ['C', 'D']}) + mock = MagicMock( + return_value={ + "gr_name": "A", + "gr_passwd": "B", + "gr_gid": 1, + "gr_mem": ["C", "D"], + } + ) with patch.dict(pw_group.grinfo, mock): - self.assertDictEqual(pw_group.info('name'), {}) + self.assertDictEqual(pw_group.info("name"), {}) - @skipIf(salt.utils.platform.is_windows(), 'grp not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "grp not available on Windows") def test_getent(self): - ''' + """ Tests for return info on all groups - ''' - mock_getent = [{'passwd': 'x', - 'gid': 0, - 'name': 'root'}] - with patch.dict(pw_group.__context__, {'group.getent': mock_getent}): - self.assertDictContainsSubset({'passwd': 'x', - 'gid': 0, - 'name': 'root'}, pw_group.getent()[0]) + """ + mock_getent = [{"passwd": "x", "gid": 0, "name": "root"}] + with patch.dict(pw_group.__context__, {"group.getent": mock_getent}): + self.assertDictContainsSubset( + {"passwd": "x", "gid": 0, "name": "root"}, pw_group.getent()[0] + ) - mock = MagicMock(return_value='A') - with patch.object(pw_group, 'info', mock): - self.assertEqual(pw_group.getent(True)[0], 'A') + mock = MagicMock(return_value="A") + with patch.object(pw_group, "info", mock): + self.assertEqual(pw_group.getent(True)[0], "A") def test_chgid(self): - ''' + """ tests to change the gid for a named group - ''' + """ mock = MagicMock(return_value=1) - with patch.dict(pw_group.__salt__, {'file.group_to_gid': mock}): - self.assertTrue(pw_group.chgid('name', 1)) + with patch.dict(pw_group.__salt__, {"file.group_to_gid": mock}): + self.assertTrue(pw_group.chgid("name", 1)) mock = MagicMock(side_effect=[1, 0]) - with patch.dict(pw_group.__salt__, {'file.group_to_gid': mock}): + with patch.dict(pw_group.__salt__, {"file.group_to_gid": mock}): mock = MagicMock(return_value=None) - with patch.dict(pw_group.__salt__, {'cmd.run': mock}): - self.assertTrue(pw_group.chgid('name', 0)) + with patch.dict(pw_group.__salt__, {"cmd.run": mock}): + self.assertTrue(pw_group.chgid("name", 0)) mock = MagicMock(side_effect=[1, 1]) - with patch.dict(pw_group.__salt__, {'file.group_to_gid': mock}): + with patch.dict(pw_group.__salt__, {"file.group_to_gid": mock}): mock = MagicMock(return_value=None) - with patch.dict(pw_group.__salt__, {'cmd.run': mock}): - self.assertFalse(pw_group.chgid('name', 0)) + with patch.dict(pw_group.__salt__, {"cmd.run": mock}): + self.assertFalse(pw_group.chgid("name", 0)) diff --git a/tests/unit/modules/test_pw_user.py b/tests/unit/modules/test_pw_user.py index 38ac4dcc461..cb9cd85a342 100644 --- a/tests/unit/modules/test_pw_user.py +++ b/tests/unit/modules/test_pw_user.py @@ -1,354 +1,361 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.pw_user as pw_user from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + try: import pwd + HAS_PWD = True except ImportError: HAS_PWD = False -@skipIf(not HAS_PWD, 'These tests can only run on systems with the python pwd module') +@skipIf(not HAS_PWD, "These tests can only run on systems with the python pwd module") class PwUserTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.pw_user - ''' + """ + def setup_loader_modules(self): return {pw_user: {}} def test_add(self): - ''' + """ Test for adding a user - ''' - with patch.dict(pw_user.__grains__, {'os_family': 'RedHat'}): + """ + with patch.dict(pw_user.__grains__, {"os_family": "RedHat"}): mock = MagicMock(return_value=0) - with patch.dict(pw_user.__salt__, {'cmd.retcode': mock}): - self.assertTrue(pw_user.add('a')) + with patch.dict(pw_user.__salt__, {"cmd.retcode": mock}): + self.assertTrue(pw_user.add("a")) def test_delete(self): - ''' + """ Test for deleting a user - ''' + """ mock = MagicMock(return_value=0) - with patch.dict(pw_user.__salt__, {'cmd.retcode': mock}): - self.assertTrue(pw_user.delete('A')) + with patch.dict(pw_user.__salt__, {"cmd.retcode": mock}): + self.assertTrue(pw_user.delete("A")) def test_getent(self): - ''' + """ Test if user.getent already have a value - ''' - mock_user = 'saltdude' + """ + mock_user = "saltdude" class MockData(object): pw_name = mock_user - with patch('pwd.getpwall', MagicMock(return_value=[MockData()])): - with patch.dict(pw_user.__context__, {'user.getent': mock_user}): + with patch("pwd.getpwall", MagicMock(return_value=[MockData()])): + with patch.dict(pw_user.__context__, {"user.getent": mock_user}): self.assertEqual(pw_user.getent(), mock_user) - with patch.object(pw_user, 'info', MagicMock(return_value=mock_user)): + with patch.object(pw_user, "info", MagicMock(return_value=mock_user)): self.assertEqual(pw_user.getent(True)[0], mock_user) def test_chuid(self): - ''' + """ Test if user id given is same as previous id - ''' - mock = MagicMock(return_value={'uid': 'A'}) - with patch.object(pw_user, 'info', mock): - self.assertTrue(pw_user.chuid('name', 'A')) + """ + mock = MagicMock(return_value={"uid": "A"}) + with patch.object(pw_user, "info", mock): + self.assertTrue(pw_user.chuid("name", "A")) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'uid': 'A'}, {'uid': 'A'}]) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chuid('name', 'B')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"uid": "A"}, {"uid": "A"}]) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chuid("name", "B")) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'uid': 'A'}, {'uid': 'B'}]) - with patch.object(pw_user, 'info', mock): - self.assertTrue(pw_user.chuid('name', 'A')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"uid": "A"}, {"uid": "B"}]) + with patch.object(pw_user, "info", mock): + self.assertTrue(pw_user.chuid("name", "A")) def test_chgid(self): - ''' + """ Test if group id given is same as previous id - ''' - mock = MagicMock(return_value={'gid': 1}) - with patch.object(pw_user, 'info', mock): - self.assertTrue(pw_user.chgid('name', 1)) + """ + mock = MagicMock(return_value={"gid": 1}) + with patch.object(pw_user, "info", mock): + self.assertTrue(pw_user.chgid("name", 1)) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'gid': 2}, {'gid': 2}]) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chgid('name', 1)) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"gid": 2}, {"gid": 2}]) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chgid("name", 1)) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'gid': 1}, {'gid': 2}]) - with patch.object(pw_user, 'info', mock): - self.assertTrue(pw_user.chgid('name', 1)) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"gid": 1}, {"gid": 2}]) + with patch.object(pw_user, "info", mock): + self.assertTrue(pw_user.chgid("name", 1)) def test_chshell(self): - ''' + """ Test if shell given is same as previous shell - ''' - mock = MagicMock(return_value={'shell': 'A'}) - with patch.object(pw_user, 'info', mock): - self.assertTrue(pw_user.chshell('name', 'A')) + """ + mock = MagicMock(return_value={"shell": "A"}) + with patch.object(pw_user, "info", mock): + self.assertTrue(pw_user.chshell("name", "A")) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'shell': 'B'}, {'shell': 'B'}]) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chshell('name', 'A')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"shell": "B"}, {"shell": "B"}]) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chshell("name", "A")) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'shell': 'A'}, {'shell': 'B'}]) - with patch.object(pw_user, 'info', mock): - self.assertTrue(pw_user.chshell('name', 'A')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"shell": "A"}, {"shell": "B"}]) + with patch.object(pw_user, "info", mock): + self.assertTrue(pw_user.chshell("name", "A")) def test_chhome(self): - ''' + """ Test if home directory given is same as previous home directory - ''' - mock = MagicMock(return_value={'home': 'A'}) - with patch.object(pw_user, 'info', mock): - self.assertTrue(pw_user.chhome('name', 'A')) + """ + mock = MagicMock(return_value={"home": "A"}) + with patch.object(pw_user, "info", mock): + self.assertTrue(pw_user.chhome("name", "A")) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'home': 'B'}, {'home': 'B'}]) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chhome('name', 'A')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"home": "B"}, {"home": "B"}]) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chhome("name", "A")) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'home': 'A'}, {'home': 'B'}]) - with patch.object(pw_user, 'info', mock): - self.assertTrue(pw_user.chhome('name', 'A')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"home": "A"}, {"home": "B"}]) + with patch.object(pw_user, "info", mock): + self.assertTrue(pw_user.chhome("name", "A")) def test_chgroups(self): - ''' + """ Test if no group needs to be added - ''' + """ mock = MagicMock(return_value=False) - with patch.dict(pw_user.__salt__, {'cmd.retcode': mock}): - mock = MagicMock(return_value=['a', 'b', 'c', 'd']) - with patch.object(pw_user, 'list_groups', mock): - self.assertTrue(pw_user.chgroups('name', 'a, b, c, d')) + with patch.dict(pw_user.__salt__, {"cmd.retcode": mock}): + mock = MagicMock(return_value=["a", "b", "c", "d"]) + with patch.object(pw_user, "list_groups", mock): + self.assertTrue(pw_user.chgroups("name", "a, b, c, d")) mock = MagicMock(return_value=False) - with patch.dict(pw_user.__salt__, {'cmd.retcode': mock}): - mock = MagicMock(return_value=['a', 'b']) - with patch.object(pw_user, 'list_groups', mock): - self.assertTrue(pw_user.chgroups('name', 'a, b, c')) + with patch.dict(pw_user.__salt__, {"cmd.retcode": mock}): + mock = MagicMock(return_value=["a", "b"]) + with patch.object(pw_user, "list_groups", mock): + self.assertTrue(pw_user.chgroups("name", "a, b, c")) def test_chfullname(self): - ''' + """ Change the user's Full Name - ''' + """ mock = MagicMock(return_value=False) - with patch.object(pw_user, '_get_gecos', mock): - self.assertFalse(pw_user.chfullname('name', 'fullname')) + with patch.object(pw_user, "_get_gecos", mock): + self.assertFalse(pw_user.chfullname("name", "fullname")) - mock = MagicMock(return_value={'fullname': 'fullname'}) - with patch.object(pw_user, '_get_gecos', mock): - self.assertTrue(pw_user.chfullname('name', 'fullname')) + mock = MagicMock(return_value={"fullname": "fullname"}) + with patch.object(pw_user, "_get_gecos", mock): + self.assertTrue(pw_user.chfullname("name", "fullname")) - mock = MagicMock(return_value={'fullname': u'Unicøde name ①③②'}) - with patch.object(pw_user, '_get_gecos', mock): - self.assertTrue(pw_user.chfullname('name', u'Unicøde name ①③②')) + mock = MagicMock(return_value={"fullname": "Unicøde name ①③②"}) + with patch.object(pw_user, "_get_gecos", mock): + self.assertTrue(pw_user.chfullname("name", "Unicøde name ①③②")) - mock = MagicMock(return_value={'fullname': 'fullname'}) - with patch.object(pw_user, '_get_gecos', mock): + mock = MagicMock(return_value={"fullname": "fullname"}) + with patch.object(pw_user, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'fullname': 'fullname2'}) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chfullname('name', 'fullname1')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"fullname": "fullname2"}) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chfullname("name", "fullname1")) - mock = MagicMock(return_value={'fullname': 'fullname2'}) - with patch.object(pw_user, '_get_gecos', mock): + mock = MagicMock(return_value={"fullname": "fullname2"}) + with patch.object(pw_user, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'fullname': 'fullname2'}) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chfullname('name', 'fullname1')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"fullname": "fullname2"}) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chfullname("name", "fullname1")) def test_chroomnumber(self): - ''' + """ Change the user's Room Number - ''' + """ mock = MagicMock(return_value=False) - with patch.object(pw_user, '_get_gecos', mock): - self.assertFalse(pw_user.chroomnumber('name', 1)) + with patch.object(pw_user, "_get_gecos", mock): + self.assertFalse(pw_user.chroomnumber("name", 1)) - mock = MagicMock(return_value={'roomnumber': u'Unicøde room ①③②'}) - with patch.object(pw_user, '_get_gecos', mock): - self.assertTrue(pw_user.chroomnumber('name', u'Unicøde room ①③②')) + mock = MagicMock(return_value={"roomnumber": "Unicøde room ①③②"}) + with patch.object(pw_user, "_get_gecos", mock): + self.assertTrue(pw_user.chroomnumber("name", "Unicøde room ①③②")) - mock = MagicMock(return_value={'roomnumber': '1'}) - with patch.object(pw_user, '_get_gecos', mock): - self.assertTrue(pw_user.chroomnumber('name', 1)) + mock = MagicMock(return_value={"roomnumber": "1"}) + with patch.object(pw_user, "_get_gecos", mock): + self.assertTrue(pw_user.chroomnumber("name", 1)) - mock = MagicMock(return_value={'roomnumber': '2'}) - with patch.object(pw_user, '_get_gecos', mock): + mock = MagicMock(return_value={"roomnumber": "2"}) + with patch.object(pw_user, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'roomnumber': '3'}) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chroomnumber('name', 1)) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"roomnumber": "3"}) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chroomnumber("name", 1)) - mock = MagicMock(return_value={'roomnumber': '3'}) - with patch.object(pw_user, '_get_gecos', mock): + mock = MagicMock(return_value={"roomnumber": "3"}) + with patch.object(pw_user, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'roomnumber': '3'}) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chroomnumber('name', 1)) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"roomnumber": "3"}) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chroomnumber("name", 1)) def test_chworkphone(self): - ''' + """ Change the user's Work Phone - ''' + """ mock = MagicMock(return_value=False) - with patch.object(pw_user, '_get_gecos', mock): - self.assertFalse(pw_user.chworkphone('name', 1)) + with patch.object(pw_user, "_get_gecos", mock): + self.assertFalse(pw_user.chworkphone("name", 1)) - mock = MagicMock(return_value={'workphone': '1'}) - with patch.object(pw_user, '_get_gecos', mock): - self.assertTrue(pw_user.chworkphone('name', 1)) + mock = MagicMock(return_value={"workphone": "1"}) + with patch.object(pw_user, "_get_gecos", mock): + self.assertTrue(pw_user.chworkphone("name", 1)) - mock = MagicMock(return_value={'workphone': u'Unicøde phone number ①③②'}) - with patch.object(pw_user, '_get_gecos', mock): - self.assertTrue(pw_user.chworkphone('name', u'Unicøde phone number ①③②')) + mock = MagicMock(return_value={"workphone": "Unicøde phone number ①③②"}) + with patch.object(pw_user, "_get_gecos", mock): + self.assertTrue(pw_user.chworkphone("name", "Unicøde phone number ①③②")) - mock = MagicMock(return_value={'workphone': '2'}) - with patch.object(pw_user, '_get_gecos', mock): + mock = MagicMock(return_value={"workphone": "2"}) + with patch.object(pw_user, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'workphone': '3'}) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chworkphone('name', 1)) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"workphone": "3"}) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chworkphone("name", 1)) - mock = MagicMock(return_value={'workphone': '3'}) - with patch.object(pw_user, '_get_gecos', mock): + mock = MagicMock(return_value={"workphone": "3"}) + with patch.object(pw_user, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'workphone': '3'}) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chworkphone('name', 1)) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"workphone": "3"}) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chworkphone("name", 1)) def test_chhomephone(self): - ''' + """ Change the user's Home Phone - ''' + """ mock = MagicMock(return_value=False) - with patch.object(pw_user, '_get_gecos', mock): - self.assertFalse(pw_user.chhomephone('name', 1)) + with patch.object(pw_user, "_get_gecos", mock): + self.assertFalse(pw_user.chhomephone("name", 1)) - mock = MagicMock(return_value={'homephone': '1'}) - with patch.object(pw_user, '_get_gecos', mock): - self.assertTrue(pw_user.chhomephone('name', 1)) + mock = MagicMock(return_value={"homephone": "1"}) + with patch.object(pw_user, "_get_gecos", mock): + self.assertTrue(pw_user.chhomephone("name", 1)) - mock = MagicMock(return_value={'homephone': u'Unicøde phone number ①③②'}) - with patch.object(pw_user, '_get_gecos', mock): - self.assertTrue(pw_user.chhomephone('name', u'Unicøde phone number ①③②')) + mock = MagicMock(return_value={"homephone": "Unicøde phone number ①③②"}) + with patch.object(pw_user, "_get_gecos", mock): + self.assertTrue(pw_user.chhomephone("name", "Unicøde phone number ①③②")) - mock = MagicMock(return_value={'homephone': '2'}) - with patch.object(pw_user, '_get_gecos', mock): + mock = MagicMock(return_value={"homephone": "2"}) + with patch.object(pw_user, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'homephone': '3'}) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chhomephone('name', 1)) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"homephone": "3"}) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chhomephone("name", 1)) - mock = MagicMock(return_value={'homephone': '3'}) - with patch.object(pw_user, '_get_gecos', mock): + mock = MagicMock(return_value={"homephone": "3"}) + with patch.object(pw_user, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'homephone': '3'}) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.chhomephone('name', 1)) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"homephone": "3"}) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.chhomephone("name", 1)) def test_info(self): - ''' + """ Return user information - ''' - self.assertEqual(pw_user.info('name'), {}) + """ + self.assertEqual(pw_user.info("name"), {}) - mock = MagicMock(return_value=pwd.struct_passwd(('_TEST_GROUP', - '*', - 83, - 83, - 'AMaViS Daemon', - '/var/virusmails', - '/usr/bin/false'))) - with patch.object(pwd, 'getpwnam', mock): - mock = MagicMock(return_value='Group Name') - with patch.object(pw_user, 'list_groups', mock): - self.assertEqual(pw_user.info('name')['name'], '_TEST_GROUP') + mock = MagicMock( + return_value=pwd.struct_passwd( + ( + "_TEST_GROUP", + "*", + 83, + 83, + "AMaViS Daemon", + "/var/virusmails", + "/usr/bin/false", + ) + ) + ) + with patch.object(pwd, "getpwnam", mock): + mock = MagicMock(return_value="Group Name") + with patch.object(pw_user, "list_groups", mock): + self.assertEqual(pw_user.info("name")["name"], "_TEST_GROUP") def test_list_groups(self): - ''' + """ Return a list of groups the named user belongs to - ''' - mock_group = 'saltgroup' + """ + mock_group = "saltgroup" - with patch('salt.utils.user.get_group_list', MagicMock(return_value=[mock_group])): - self.assertEqual(pw_user.list_groups('name'), [mock_group]) + with patch( + "salt.utils.user.get_group_list", MagicMock(return_value=[mock_group]) + ): + self.assertEqual(pw_user.list_groups("name"), [mock_group]) def test_list_users(self): - ''' + """ Return a list of all users - ''' - mock_user = 'saltdude' + """ + mock_user = "saltdude" class MockData(object): pw_name = mock_user - with patch('pwd.getpwall', MagicMock(return_value=[MockData()])): + with patch("pwd.getpwall", MagicMock(return_value=[MockData()])): self.assertEqual(pw_user.list_users(), [mock_user]) def test_rename(self): - ''' + """ Change the username for a named user - ''' + """ mock = MagicMock(return_value=False) - with patch.object(pw_user, 'info', mock): - self.assertRaises(CommandExecutionError, pw_user.rename, 'name', 1) + with patch.object(pw_user, "info", mock): + self.assertRaises(CommandExecutionError, pw_user.rename, "name", 1) mock = MagicMock(return_value=True) - with patch.object(pw_user, 'info', mock): - self.assertRaises(CommandExecutionError, pw_user.rename, 'name', 1) + with patch.object(pw_user, "info", mock): + self.assertRaises(CommandExecutionError, pw_user.rename, "name", 1) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'name': ''}, - False, {'name': 'name'}]) - with patch.object(pw_user, 'info', mock): - self.assertTrue(pw_user.rename('name', 'name')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"name": ""}, False, {"name": "name"}]) + with patch.object(pw_user, "info", mock): + self.assertTrue(pw_user.rename("name", "name")) mock = MagicMock(return_value=None) - with patch.dict(pw_user.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'name': ''}, False, {'name': ''}]) - with patch.object(pw_user, 'info', mock): - self.assertFalse(pw_user.rename('name', 'name')) + with patch.dict(pw_user.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"name": ""}, False, {"name": ""}]) + with patch.object(pw_user, "info", mock): + self.assertFalse(pw_user.rename("name", "name")) diff --git a/tests/unit/modules/test_pyenv.py b/tests/unit/modules/test_pyenv.py index 8cb003c8d6a..6136ad7f73e 100644 --- a/tests/unit/modules/test_pyenv.py +++ b/tests/unit/modules/test_pyenv.py @@ -1,207 +1,261 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.pyenv as pyenv +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PyenvTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.pyenv - ''' + """ + def setup_loader_modules(self): return {pyenv: {}} # 'install' function tests: 1 def test_install(self): - ''' + """ Test if it install pyenv systemwide - ''' - mock_opt = MagicMock(return_value='salt stack') + """ + mock_opt = MagicMock(return_value="salt stack") mock_ret = MagicMock(return_value=0) - with patch.dict(pyenv.__salt__, {'config.option': mock_opt, - 'cmd.retcode': mock_ret}): + with patch.dict( + pyenv.__salt__, {"config.option": mock_opt, "cmd.retcode": mock_ret} + ): self.assertTrue(pyenv.install()) # 'update' function tests: 1 def test_update(self): - ''' + """ Test if it updates the current versions of pyenv and python-Build - ''' - mock_opt = MagicMock(return_value='salt stack') - with patch.dict(pyenv.__salt__, {'config.option': mock_opt}): + """ + mock_opt = MagicMock(return_value="salt stack") + with patch.dict(pyenv.__salt__, {"config.option": mock_opt}): self.assertFalse(pyenv.update()) # 'is_installed' function tests: 1 def test_is_installed(self): - ''' + """ Test if it check if pyenv is installed. - ''' + """ mock_cmd = MagicMock(return_value=True) - mock_opt = MagicMock(return_value='salt stack') - with patch.dict(pyenv.__salt__, {'config.option': mock_opt, - 'cmd.has_exec': mock_cmd}): + mock_opt = MagicMock(return_value="salt stack") + with patch.dict( + pyenv.__salt__, {"config.option": mock_opt, "cmd.has_exec": mock_cmd} + ): self.assertTrue(pyenv.is_installed()) # 'install_python' function tests: 1 def test_install_python(self): - ''' + """ Test if it install a python implementation. - ''' - mock_opt = MagicMock(return_value='salt stack') + """ + mock_opt = MagicMock(return_value="salt stack") mock_cmd = MagicMock(return_value=True) - mock_all = MagicMock(return_value={'retcode': 0, 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__grains__, {'os': 'Linux'}): - mock_all = MagicMock(return_value={'retcode': 0, 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'config.option': mock_opt, - 'cmd.has_exec': mock_cmd, - 'cmd.run_all': mock_all}): - self.assertEqual(pyenv.install_python('2.0.0-p0'), 'error') + mock_all = MagicMock( + return_value={"retcode": 0, "stdout": "salt", "stderr": "error"} + ) + with patch.dict(pyenv.__grains__, {"os": "Linux"}): + mock_all = MagicMock( + return_value={"retcode": 0, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "config.option": mock_opt, + "cmd.has_exec": mock_cmd, + "cmd.run_all": mock_all, + }, + ): + self.assertEqual(pyenv.install_python("2.0.0-p0"), "error") - mock_all = MagicMock(return_value={'retcode': True, - 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'config.option': mock_opt, - 'cmd.has_exec': mock_cmd, - 'cmd.run_all': mock_all}): - self.assertFalse(pyenv.install_python('2.0.0-p0')) + mock_all = MagicMock( + return_value={"retcode": True, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "config.option": mock_opt, + "cmd.has_exec": mock_cmd, + "cmd.run_all": mock_all, + }, + ): + self.assertFalse(pyenv.install_python("2.0.0-p0")) # 'uninstall_python' function tests: 1 def test_uninstall_python(self): - ''' + """ Test if it uninstall a python implementation. - ''' - mock_opt = MagicMock(return_value='salt stack') + """ + mock_opt = MagicMock(return_value="salt stack") mock_cmd = MagicMock(return_value=True) - mock_all = MagicMock(return_value={'retcode': True, - 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'cmd.has_exec': mock_cmd, - 'config.option': mock_opt, - 'cmd.run_all': mock_all}): - self.assertTrue(pyenv.uninstall_python('2.0.0-p0')) + mock_all = MagicMock( + return_value={"retcode": True, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "cmd.has_exec": mock_cmd, + "config.option": mock_opt, + "cmd.run_all": mock_all, + }, + ): + self.assertTrue(pyenv.uninstall_python("2.0.0-p0")) # 'versions' function tests: 1 def test_versions(self): - ''' + """ Test if it list the installed versions of python. - ''' - mock_opt = MagicMock(return_value='salt stack') + """ + mock_opt = MagicMock(return_value="salt stack") mock_cmd = MagicMock(return_value=True) - mock_all = MagicMock(return_value={'retcode': True, - 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'cmd.has_exec': mock_cmd, - 'config.option': mock_opt, - 'cmd.run_all': mock_all}): + mock_all = MagicMock( + return_value={"retcode": True, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "cmd.has_exec": mock_cmd, + "config.option": mock_opt, + "cmd.run_all": mock_all, + }, + ): self.assertListEqual(pyenv.versions(), []) # 'default' function tests: 1 def test_default(self): - ''' + """ Test if it returns or sets the currently defined default python. - ''' - mock_opt = MagicMock(return_value='salt stack') + """ + mock_opt = MagicMock(return_value="salt stack") mock_cmd = MagicMock(return_value=True) - mock_all = MagicMock(return_value={'retcode': True, - 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'cmd.has_exec': mock_cmd, - 'config.option': mock_opt, - 'cmd.run_all': mock_all}): - self.assertEqual(pyenv.default(), '') - self.assertTrue(pyenv.default('2.0.0-p0')) + mock_all = MagicMock( + return_value={"retcode": True, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "cmd.has_exec": mock_cmd, + "config.option": mock_opt, + "cmd.run_all": mock_all, + }, + ): + self.assertEqual(pyenv.default(), "") + self.assertTrue(pyenv.default("2.0.0-p0")) # 'list_' function tests: 1 def test_list(self): - ''' + """ Test if it list the installable versions of python. - ''' - mock_opt = MagicMock(return_value='salt stack') + """ + mock_opt = MagicMock(return_value="salt stack") mock_cmd = MagicMock(return_value=True) - mock_all = MagicMock(return_value={'retcode': True, - 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'cmd.has_exec': mock_cmd, - 'config.option': mock_opt, - 'cmd.run_all': mock_all}): + mock_all = MagicMock( + return_value={"retcode": True, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "cmd.has_exec": mock_cmd, + "config.option": mock_opt, + "cmd.run_all": mock_all, + }, + ): self.assertListEqual(pyenv.list_(), []) # 'rehash' function tests: 1 def test_rehash(self): - ''' + """ Test if it run pyenv rehash to update the installed shims. - ''' - mock_opt = MagicMock(return_value='salt stack') + """ + mock_opt = MagicMock(return_value="salt stack") mock_cmd = MagicMock(return_value=True) - mock_all = MagicMock(return_value={'retcode': True, - 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'cmd.has_exec': mock_cmd, - 'config.option': mock_opt, - 'cmd.run_all': mock_all}): + mock_all = MagicMock( + return_value={"retcode": True, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "cmd.has_exec": mock_cmd, + "config.option": mock_opt, + "cmd.run_all": mock_all, + }, + ): self.assertTrue(pyenv.rehash()) # 'do' function tests: 1 def test_do(self): - ''' + """ Test if it execute a python command with pyenv's shims from the user or the system. - ''' - mock_opt = MagicMock(return_value='salt stack') + """ + mock_opt = MagicMock(return_value="salt stack") mock_cmd = MagicMock(return_value=True) - mock_all = MagicMock(return_value={'retcode': True, 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'cmd.has_exec': mock_cmd, - 'config.option': mock_opt, - 'cmd.run_all': mock_all}): - self.assertFalse(pyenv.do('gem list bundler')) + mock_all = MagicMock( + return_value={"retcode": True, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "cmd.has_exec": mock_cmd, + "config.option": mock_opt, + "cmd.run_all": mock_all, + }, + ): + self.assertFalse(pyenv.do("gem list bundler")) - mock_all = MagicMock(return_value={'retcode': 0, 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'config.option': mock_opt, - 'cmd.has_exec': mock_cmd, - 'cmd.run_all': mock_all}): - self.assertEqual(pyenv.do('gem list bundler'), 'salt') + mock_all = MagicMock( + return_value={"retcode": 0, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "config.option": mock_opt, + "cmd.has_exec": mock_cmd, + "cmd.run_all": mock_all, + }, + ): + self.assertEqual(pyenv.do("gem list bundler"), "salt") # 'do_with_python' function tests: 1 def test_do_with_python(self): - ''' + """ Test if it execute a python command with pyenv's shims using a specific python version. - ''' - mock_opt = MagicMock(return_value='salt stack') + """ + mock_opt = MagicMock(return_value="salt stack") mock_cmd = MagicMock(return_value=True) - mock_all = MagicMock(return_value={'retcode': True, 'stdout': 'salt', - 'stderr': 'error'}) - with patch.dict(pyenv.__salt__, {'cmd.has_exec': mock_cmd, - 'config.option': mock_opt, - 'cmd.run_all': mock_all}): - self.assertFalse(pyenv.do_with_python('2.0.0-p0', - 'gem list bundler')) + mock_all = MagicMock( + return_value={"retcode": True, "stdout": "salt", "stderr": "error"} + ) + with patch.dict( + pyenv.__salt__, + { + "cmd.has_exec": mock_cmd, + "config.option": mock_opt, + "cmd.run_all": mock_all, + }, + ): + self.assertFalse(pyenv.do_with_python("2.0.0-p0", "gem list bundler")) diff --git a/tests/unit/modules/test_qemu_img.py b/tests/unit/modules/test_qemu_img.py index bc7e5ee9034..c54b44840ea 100644 --- a/tests/unit/modules/test_qemu_img.py +++ b/tests/unit/modules/test_qemu_img.py @@ -1,45 +1,50 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rupesh Tare <rupesht@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.qemu_img as qemu_img +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class QemuimgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.qemu_img - ''' + """ + def setup_loader_modules(self): return {qemu_img: {}} def test_make_image(self): - ''' + """ Test for create a blank virtual machine image file of the specified size in megabytes - ''' - with patch.object(os.path, 'isabs', - MagicMock(side_effect=[False, True, True, True])): - self.assertEqual(qemu_img.make_image('location', 'size', 'fmt'), '') + """ + with patch.object( + os.path, "isabs", MagicMock(side_effect=[False, True, True, True]) + ): + self.assertEqual(qemu_img.make_image("location", "size", "fmt"), "") - with patch.object(os.path, 'isdir', - MagicMock(side_effect=[False, True, True])): - self.assertEqual(qemu_img.make_image('location', 'size', 'fmt'), '') + with patch.object( + os.path, "isdir", MagicMock(side_effect=[False, True, True]) + ): + self.assertEqual(qemu_img.make_image("location", "size", "fmt"), "") - with patch.dict(qemu_img.__salt__, - {'cmd.retcode': MagicMock(side_effect=[False, True])}): - self.assertEqual(qemu_img.make_image('location', 'size', 'fmt'), 'location') - self.assertEqual(qemu_img.make_image('location', 'size', 'fmt'), '') + with patch.dict( + qemu_img.__salt__, + {"cmd.retcode": MagicMock(side_effect=[False, True])}, + ): + self.assertEqual( + qemu_img.make_image("location", "size", "fmt"), "location" + ) + self.assertEqual(qemu_img.make_image("location", "size", "fmt"), "") diff --git a/tests/unit/modules/test_qemu_nbd.py b/tests/unit/modules/test_qemu_nbd.py index 7bd07c1d6fd..54830abbd77 100644 --- a/tests/unit/modules/test_qemu_nbd.py +++ b/tests/unit/modules/test_qemu_nbd.py @@ -1,103 +1,108 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os.path -import glob -import tempfile -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import glob +import os.path +import tempfile # Import Salt Libs import salt.modules.qemu_nbd as qemu_nbd +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class QemuNbdTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.qemu_nbd - ''' + """ + def setup_loader_modules(self): return {qemu_nbd: {}} # 'connect' function tests: 1 def test_connect(self): - ''' + """ Test if it activate nbd for an image file. - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(qemu_nbd.__salt__, {'cmd.run': mock}): - with patch.object(os.path, 'isfile', - MagicMock(return_value=False)): - self.assertEqual(qemu_nbd.connect('/tmp/image.raw'), '') - self.assertEqual(qemu_nbd.connect('/tmp/image.raw'), '') + with patch.dict(qemu_nbd.__salt__, {"cmd.run": mock}): + with patch.object(os.path, "isfile", MagicMock(return_value=False)): + self.assertEqual(qemu_nbd.connect("/tmp/image.raw"), "") + self.assertEqual(qemu_nbd.connect("/tmp/image.raw"), "") - with patch.object(os.path, 'isfile', mock): - with patch.object(glob, 'glob', - MagicMock(return_value=['/dev/nbd0'])): - with patch.dict(qemu_nbd.__salt__, - {'cmd.run': mock, - 'cmd.retcode': MagicMock(side_effect=[1, 0])}): - self.assertEqual(qemu_nbd.connect('/tmp/image.raw'), - '/dev/nbd0') + with patch.object(os.path, "isfile", mock): + with patch.object(glob, "glob", MagicMock(return_value=["/dev/nbd0"])): + with patch.dict( + qemu_nbd.__salt__, + {"cmd.run": mock, "cmd.retcode": MagicMock(side_effect=[1, 0])}, + ): + self.assertEqual(qemu_nbd.connect("/tmp/image.raw"), "/dev/nbd0") - with patch.dict(qemu_nbd.__salt__, - {'cmd.run': mock, - 'cmd.retcode': MagicMock(return_value=False)}): - self.assertEqual(qemu_nbd.connect('/tmp/image.raw'), '') + with patch.dict( + qemu_nbd.__salt__, + {"cmd.run": mock, "cmd.retcode": MagicMock(return_value=False)}, + ): + self.assertEqual(qemu_nbd.connect("/tmp/image.raw"), "") # 'mount' function tests: 1 def test_mount(self): - ''' + """ Test if it pass in the nbd connection device location, mount all partitions and return a dict of mount points. - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(qemu_nbd.__salt__, {'cmd.run': mock}): - self.assertDictEqual(qemu_nbd.mount('/dev/nbd0'), {}) + with patch.dict(qemu_nbd.__salt__, {"cmd.run": mock}): + self.assertDictEqual(qemu_nbd.mount("/dev/nbd0"), {}) # 'init' function tests: 1 def test_init(self): - ''' + """ Test if it mount the named image via qemu-nbd and return the mounted roots - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(qemu_nbd.__salt__, {'cmd.run': mock}): - self.assertEqual(qemu_nbd.init('/srv/image.qcow2'), '') + with patch.dict(qemu_nbd.__salt__, {"cmd.run": mock}): + self.assertEqual(qemu_nbd.init("/srv/image.qcow2"), "") - with patch.object(os.path, 'isfile', mock),\ - patch.object(glob, 'glob', MagicMock(return_value=['/dev/nbd0'])),\ - patch.dict(qemu_nbd.__salt__, - {'cmd.run': mock, - 'mount.mount': mock, - 'cmd.retcode': MagicMock(side_effect=[1, 0])}): - expected = {os.sep.join([tempfile.gettempdir(), 'nbd', 'nbd0', 'nbd0']): '/dev/nbd0'} - self.assertDictEqual(qemu_nbd.init('/srv/image.qcow2'), expected) + with patch.object(os.path, "isfile", mock), patch.object( + glob, "glob", MagicMock(return_value=["/dev/nbd0"]) + ), patch.dict( + qemu_nbd.__salt__, + { + "cmd.run": mock, + "mount.mount": mock, + "cmd.retcode": MagicMock(side_effect=[1, 0]), + }, + ): + expected = { + os.sep.join([tempfile.gettempdir(), "nbd", "nbd0", "nbd0"]): "/dev/nbd0" + } + self.assertDictEqual(qemu_nbd.init("/srv/image.qcow2"), expected) # 'clear' function tests: 1 def test_clear(self): - ''' + """ Test if it pass in the mnt dict returned from nbd_mount to unmount and disconnect the image from nbd. - ''' + """ mock_run = MagicMock(return_value=True) - with patch.dict(qemu_nbd.__salt__, - {'cmd.run': mock_run, - 'mount.umount': MagicMock(side_effect=[False, True])}): - self.assertDictEqual(qemu_nbd.clear({"/mnt/foo": "/dev/nbd0p1"}), - {'/mnt/foo': '/dev/nbd0p1'}) - self.assertDictEqual(qemu_nbd.clear({"/mnt/foo": "/dev/nbd0p1"}), - {}) + with patch.dict( + qemu_nbd.__salt__, + {"cmd.run": mock_run, "mount.umount": MagicMock(side_effect=[False, True])}, + ): + self.assertDictEqual( + qemu_nbd.clear({"/mnt/foo": "/dev/nbd0p1"}), {"/mnt/foo": "/dev/nbd0p1"} + ) + self.assertDictEqual(qemu_nbd.clear({"/mnt/foo": "/dev/nbd0p1"}), {}) diff --git a/tests/unit/modules/test_rabbitmq.py b/tests/unit/modules/test_rabbitmq.py index b9467aaeafa..edadf2ef6ec 100644 --- a/tests/unit/modules/test_rabbitmq.py +++ b/tests/unit/modules/test_rabbitmq.py @@ -1,644 +1,888 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.rabbitmq as rabbitmq from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RabbitmqTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.rabbitmq - ''' + """ + def setup_loader_modules(self): - return {rabbitmq: {'__context__': {'rabbitmqctl': None, 'rabbitmq-plugins': None}}} + return { + rabbitmq: {"__context__": {"rabbitmqctl": None, "rabbitmq-plugins": None}} + } # 'list_users_rabbitmq2' function tests: 1 def test_list_users_rabbitmq2(self): - ''' + """ Test if it return a list of users based off of rabbitmqctl user_list. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 0, - 'stdout': 'Listing users ...\nguest\t[administrator, user]\njustAnAdmin\t[administrator]\n', - 'stderr': '', - }) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.list_users(), - {'guest': ['administrator', 'user'], 'justAnAdmin': ['administrator']}) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "Listing users ...\nguest\t[administrator, user]\njustAnAdmin\t[administrator]\n", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.list_users(), + {"guest": ["administrator", "user"], "justAnAdmin": ["administrator"]}, + ) # 'list_users_rabbitmq3' function tests: 1 def test_list_users_rabbitmq3(self): - ''' + """ Test if it return a list of users based off of rabbitmqctl user_list. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 0, - 'stdout': 'guest\t[administrator user]\r\nother\t[a b]\r\n', - 'stderr': '' - }) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.list_users(), {'guest': ['administrator', 'user'], 'other': ['a', 'b']}) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "guest\t[administrator user]\r\nother\t[a b]\r\n", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.list_users(), + {"guest": ["administrator", "user"], "other": ["a", "b"]}, + ) # 'list_users_with_warning_rabbitmq2' function tests: 1 def test_list_users_with_warning_rabbitmq2(self): - ''' + """ Test if having a leading WARNING returns the user_list anyway. - ''' - rtn_stdout = '\n'.join([ - 'WARNING: ignoring /etc/rabbitmq/rabbitmq.conf -- location has moved to /etc/rabbitmq/rabbitmq-env.conf', - 'Listing users ...', - 'guest\t[administrator, user]\n', - ]) - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': rtn_stdout, 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.list_users(), {'guest': ['administrator', 'user']}) + """ + rtn_stdout = "\n".join( + [ + "WARNING: ignoring /etc/rabbitmq/rabbitmq.conf -- location has moved to /etc/rabbitmq/rabbitmq-env.conf", + "Listing users ...", + "guest\t[administrator, user]\n", + ] + ) + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": rtn_stdout, "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.list_users(), {"guest": ["administrator", "user"]} + ) # 'list_users_with_warning_rabbitmq3' function tests: 1 def test_list_users_with_warning_rabbitmq3(self): - ''' + """ Test if having a leading WARNING returns the user_list anyway. - ''' - rtn_stdout = '\n'.join([ - 'WARNING: ignoring /etc/rabbitmq/rabbitmq.conf -- location has moved to /etc/rabbitmq/rabbitmq-env.conf', - 'Listing users ...', - 'guest\t[administrator user]\n', - ]) - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': rtn_stdout, 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.list_users(), {'guest': ['administrator', 'user']}) + """ + rtn_stdout = "\n".join( + [ + "WARNING: ignoring /etc/rabbitmq/rabbitmq.conf -- location has moved to /etc/rabbitmq/rabbitmq-env.conf", + "Listing users ...", + "guest\t[administrator user]\n", + ] + ) + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": rtn_stdout, "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.list_users(), {"guest": ["administrator", "user"]} + ) # 'list_vhosts' function tests: 2 def test_list_vhosts(self): - ''' + """ Test if it return a list of vhost based on rabbitmqctl list_vhosts. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': '/\nsaltstack\n...', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertListEqual(rabbitmq.list_vhosts(), ['/', 'saltstack', '...']) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "/\nsaltstack\n...", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertListEqual(rabbitmq.list_vhosts(), ["/", "saltstack", "..."]) def test_list_vhosts_with_warning(self): - ''' + """ Test if it return a list of vhost based on rabbitmqctl list_vhosts even with a leading WARNING. - ''' - rtn_stdout = '\n'.join([ - 'WARNING: ignoring /etc/rabbitmq/rabbitmq.conf -- location has moved to /etc/rabbitmq/rabbitmq-env.conf', - 'Listing users ...', - '/', - 'saltstack', - '...\n', - ]) - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': rtn_stdout, 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertListEqual(rabbitmq.list_vhosts(), ['/', 'saltstack', '...']) + """ + rtn_stdout = "\n".join( + [ + "WARNING: ignoring /etc/rabbitmq/rabbitmq.conf -- location has moved to /etc/rabbitmq/rabbitmq-env.conf", + "Listing users ...", + "/", + "saltstack", + "...\n", + ] + ) + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": rtn_stdout, "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertListEqual(rabbitmq.list_vhosts(), ["/", "saltstack", "..."]) # 'user_exists' function tests: 2 def test_user_exists(self): - ''' + """ Test whether a given rabbitmq-internal user exists based on rabbitmqctl list_users. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'Listing users ...\n' - 'saltstack\t[administrator]\n...done', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertTrue(rabbitmq.user_exists('saltstack')) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "Listing users ...\n" "saltstack\t[administrator]\n...done", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertTrue(rabbitmq.user_exists("saltstack")) def test_user_exists_negative(self): - ''' + """ Negative test of whether rabbitmq-internal user exists based on rabbitmqctl list_users. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'Listing users ...\n' - 'saltstack\t[administrator]\n...done', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertFalse(rabbitmq.user_exists('salt')) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "Listing users ...\n" "saltstack\t[administrator]\n...done", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertFalse(rabbitmq.user_exists("salt")) # 'vhost_exists' function tests: 2 def test_vhost_exists(self): - ''' + """ Test if it return whether the vhost exists based on rabbitmqctl list_vhosts. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'Listing vhosts ...\nsaltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertTrue(rabbitmq.vhost_exists('saltstack')) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "Listing vhosts ...\nsaltstack", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertTrue(rabbitmq.vhost_exists("saltstack")) def test_vhost_exists_negative(self): - ''' + """ Test if it return whether the vhost exists based on rabbitmqctl list_vhosts. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'Listing vhosts ...\nsaltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertFalse(rabbitmq.vhost_exists('salt')) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "Listing vhosts ...\nsaltstack", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertFalse(rabbitmq.vhost_exists("salt")) # 'add_user' function tests: 1 def test_add_user(self): - ''' + """ Test if it add a rabbitMQ user via rabbitmqctl user_add <user> <password> - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.add_user('saltstack'), - {'Added': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual(rabbitmq.add_user("saltstack"), {"Added": "saltstack"}) - mock_run = MagicMock(return_value='Error') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - with patch.object(rabbitmq, 'clear_password', - return_value={'Error': 'Error', 'retcode': 1}): - self.assertRaises(CommandExecutionError, rabbitmq.add_user, 'saltstack') + mock_run = MagicMock(return_value="Error") + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + with patch.object( + rabbitmq, + "clear_password", + return_value={"Error": "Error", "retcode": 1}, + ): + self.assertRaises(CommandExecutionError, rabbitmq.add_user, "saltstack") # 'delete_user' function tests: 1 def test_delete_user(self): - ''' + """ Test if it deletes a user via rabbitmqctl delete_user. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.delete_user('saltstack'), - {'Deleted': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.delete_user("saltstack"), {"Deleted": "saltstack"} + ) + + # 'check_password' function tests: 2 + + def test_check_password_lt_38(self): + """ + Test if it checks a user's password for RabbitMQ less than v3.8. + """ + mock_run = MagicMock(return_value='{rabbit,"RabbitMQ","3.5.7"}') + mock_run2 = MagicMock( + return_value={ + "retcode": 0, + "stdout": 'Authenticating user "saltstack" ...\nSuccess', + "stderr": "", + } + ) + with patch.dict( + rabbitmq.__salt__, {"cmd.run": mock_run, "cmd.run_all": mock_run2} + ): + self.assertEqual(rabbitmq.check_password("saltstack", "salt@123"), True) + + def test_check_password_gt_38(self): + """ + Test if it checks a user's password for RabbitMQ greater than v3.8. + """ + mock_run = MagicMock(return_value="RabbitMQ version: 3.8.3") + mock_run2 = MagicMock( + return_value={ + "retcode": 0, + "stdout": 'Authenticating user "saltstack" ...\nSuccess', + "stderr": "", + } + ) + with patch.dict( + rabbitmq.__salt__, {"cmd.run": mock_run, "cmd.run_all": mock_run2} + ): + self.assertEqual(rabbitmq.check_password("saltstack", "salt@123"), True) # 'change_password' function tests: 1 def test_change_password(self): - ''' + """ Test if it changes a user's password. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.change_password('saltstack', - 'salt@123'), - {'Password Changed': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.change_password("saltstack", "salt@123"), + {"Password Changed": "saltstack"}, + ) # 'clear_password' function tests: 1 def test_clear_password(self): - ''' + """ Test if it removes a user's password. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.clear_password('saltstack'), - {'Password Cleared': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.clear_password("saltstack"), {"Password Cleared": "saltstack"} + ) # 'add_vhost' function tests: 1 def test_add_vhost(self): - ''' + """ Test if it adds a vhost via rabbitmqctl add_vhost. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.add_vhost('saltstack'), - {'Added': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.add_vhost("saltstack"), {"Added": "saltstack"} + ) # 'delete_vhost' function tests: 1 def test_delete_vhost(self): - ''' + """ Test if it deletes a vhost rabbitmqctl delete_vhost. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.delete_vhost('saltstack'), - {'Deleted': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.delete_vhost("saltstack"), {"Deleted": "saltstack"} + ) # 'set_permissions' function tests: 1 def test_set_permissions(self): - ''' + """ Test if it sets permissions for vhost via rabbitmqctl set_permissions. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.set_permissions('myvhost', 'myuser'), - {'Permissions Set': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.set_permissions("myvhost", "myuser"), + {"Permissions Set": "saltstack"}, + ) # 'list_permissions' function tests: 1 def test_list_permissions(self): - ''' + """ Test if it lists permissions for a vhost via rabbitmqctl list_permissions. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 0, - 'stdout': 'Listing stuff ...\nsaltstack\tsaltstack\t.*\t1\nguest\t0\tone\n...done', - 'stderr': '', - }) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.list_user_permissions('myuser'), - {'saltstack': ['saltstack', '.*', '1'], 'guest': ['0', 'one']}) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "Listing stuff ...\nsaltstack\tsaltstack\t.*\t1\nguest\t0\tone\n...done", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.list_user_permissions("myuser"), + {"saltstack": ["saltstack", ".*", "1"], "guest": ["0", "one"]}, + ) # 'list_user_permissions' function tests: 1 def test_list_user_permissions(self): - ''' + """ Test if it list permissions for a user via rabbitmqctl list_user_permissions. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 0, - 'stdout': 'Listing stuff ...\nsaltstack\tsaltstack\t0\t1\nguest\t0\tone\n...done', - 'stderr': '', - }) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.list_user_permissions('myuser'), - {'saltstack': ['saltstack', '0', '1'], 'guest': ['0', 'one']}) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "Listing stuff ...\nsaltstack\tsaltstack\t0\t1\nguest\t0\tone\n...done", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.list_user_permissions("myuser"), + {"saltstack": ["saltstack", "0", "1"], "guest": ["0", "one"]}, + ) # 'set_user_tags' function tests: 1 def test_set_user_tags(self): - ''' + """ Test if it add user tags via rabbitmqctl set_user_tags. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.set_user_tags('myadmin', 'admin'), - {'Tag(s) set': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.set_user_tags("myadmin", "admin"), {"Tag(s) set": "saltstack"} + ) # 'status' function tests: 1 def test_status(self): - ''' + """ Test if it return rabbitmq status. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertEqual(rabbitmq.status(), 'saltstack') + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertEqual(rabbitmq.status(), "saltstack") # 'cluster_status' function tests: 1 def test_cluster_status(self): - ''' + """ Test if it return rabbitmq cluster_status. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertEqual(rabbitmq.cluster_status(), 'saltstack') + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertEqual(rabbitmq.cluster_status(), "saltstack") # 'join_cluster' function tests: 1 def test_join_cluster(self): - ''' + """ Test if it join a rabbit cluster. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.join_cluster('rabbit.example.com'), - {'Join': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.join_cluster("rabbit.example.com"), {"Join": "saltstack"} + ) # 'stop_app' function tests: 1 def test_stop_app(self): - ''' + """ Test if it stops the RabbitMQ application, leaving the Erlang node running. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertEqual(rabbitmq.stop_app(), 'saltstack') + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertEqual(rabbitmq.stop_app(), "saltstack") # 'start_app' function tests: 1 def test_start_app(self): - ''' + """ Test if it start the RabbitMQ application. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertEqual(rabbitmq.start_app(), 'saltstack') + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertEqual(rabbitmq.start_app(), "saltstack") # 'reset' function tests: 1 def test_reset(self): - ''' + """ Test if it return a RabbitMQ node to its virgin state - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertEqual(rabbitmq.reset(), 'saltstack') + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertEqual(rabbitmq.reset(), "saltstack") # 'force_reset' function tests: 1 def test_force_reset(self): - ''' + """ Test if it forcefully Return a RabbitMQ node to its virgin state - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertEqual(rabbitmq.force_reset(), 'saltstack') + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertEqual(rabbitmq.force_reset(), "saltstack") # 'list_queues' function tests: 1 def test_list_queues(self): - ''' + """ Test if it returns queue details of the / virtual host - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack\t0\nceleryev.234-234\t10', - 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.list_queues(), {'saltstack': ['0'], 'celeryev.234-234': ['10']}) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "saltstack\t0\nceleryev.234-234\t10", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.list_queues(), {"saltstack": ["0"], "celeryev.234-234": ["10"]} + ) # 'list_queues_vhost' function tests: 1 def test_list_queues_vhost(self): - ''' + """ Test if it returns queue details of specified virtual host. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack\t0\nceleryev.234-234\t10', - 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.list_queues_vhost('consumers'), {'saltstack': ['0'], - 'celeryev.234-234': ['10']}) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "saltstack\t0\nceleryev.234-234\t10", + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.list_queues_vhost("consumers"), + {"saltstack": ["0"], "celeryev.234-234": ["10"]}, + ) # 'list_policies' function tests: 3 def test_list_policies(self): - ''' + """ Test if it return a dictionary of policies nested by vhost and name based on the data returned from rabbitmqctl list_policies. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - mock_pkg = MagicMock(return_value='3.7') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, 'pkg.version': mock_pkg}), \ - patch.dict(rabbitmq.__grains__, {'os_family': ''}): + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + mock_pkg = MagicMock(return_value="3.7") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ), patch.dict(rabbitmq.__grains__, {"os_family": ""}): self.assertDictEqual(rabbitmq.list_policies(), {}) def test_list_policies_freebsd(self): - ''' + """ Test if it return a dictionary of policies nested by vhost and name based on the data returned from rabbitmqctl list_policies. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - mock_pkg = MagicMock(return_value='3.7') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, 'pkg.version': mock_pkg}), \ - patch.dict(rabbitmq.__grains__, {'os_family': 'FreeBSD'}): + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + mock_pkg = MagicMock(return_value="3.7") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ), patch.dict(rabbitmq.__grains__, {"os_family": "FreeBSD"}): self.assertDictEqual(rabbitmq.list_policies(), {}) def test_list_policies_old_version(self): - ''' + """ Test if it return a dictionary of policies nested by vhost and name based on the data returned from rabbitmqctl list_policies. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - mock_pkg = MagicMock(return_value='3.0') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, 'pkg.version': mock_pkg}), \ - patch.dict(rabbitmq.__grains__, {'os_family': ''}): + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + mock_pkg = MagicMock(return_value="3.0") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ), patch.dict(rabbitmq.__grains__, {"os_family": ""}): self.assertDictEqual(rabbitmq.list_policies(), {}) # 'set_policy' function tests: 1 def test_set_policy(self): - ''' + """ Test if it set a policy based on rabbitmqctl set_policy. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.set_policy('/', 'HA', '.*', - '{"ha-mode": "all"}'), - {'Set': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.set_policy("/", "HA", ".*", '{"ha-mode": "all"}'), + {"Set": "saltstack"}, + ) # 'delete_policy' function tests: 1 def test_delete_policy(self): - ''' + """ Test if it delete a policy based on rabbitmqctl clear_policy. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertDictEqual(rabbitmq.delete_policy('/', 'HA'), - {'Deleted': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertDictEqual( + rabbitmq.delete_policy("/", "HA"), {"Deleted": "saltstack"} + ) # 'policy_exists' function tests: 1 def test_policy_exists(self): - ''' + """ Test if it return whether the policy exists based on rabbitmqctl list_policies. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - mock_pkg = MagicMock(return_value='3.0') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, 'pkg.version': mock_pkg}), \ - patch.dict(rabbitmq.__grains__, {'os_family': ''}): - self.assertFalse(rabbitmq.policy_exists('/', 'HA')) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + mock_pkg = MagicMock(return_value="3.0") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ), patch.dict(rabbitmq.__grains__, {"os_family": ""}): + self.assertFalse(rabbitmq.policy_exists("/", "HA")) # 'list_available_plugins' function tests: 2 def test_list_available_plugins(self): - ''' + """ Test if it returns a list of plugins. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack\nsalt\nother', 'stderr': ''}) - mock_pkg = MagicMock(return_value='') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, - 'pkg.version': mock_pkg}): - self.assertListEqual(rabbitmq.list_available_plugins(), ['saltstack', 'salt', 'other']) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "saltstack\nsalt\nother", + "stderr": "", + } + ) + mock_pkg = MagicMock(return_value="") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ): + self.assertListEqual( + rabbitmq.list_available_plugins(), ["saltstack", "salt", "other"] + ) def test_list_available_plugins_space_delimited(self): - ''' + """ Test if it returns a list of plugins. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack salt other', 'stderr': ''}) - mock_pkg = MagicMock(return_value='') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, - 'pkg.version': mock_pkg}): - self.assertListEqual(rabbitmq.list_available_plugins(), ['saltstack', 'salt', 'other']) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack salt other", "stderr": ""} + ) + mock_pkg = MagicMock(return_value="") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ): + self.assertListEqual( + rabbitmq.list_available_plugins(), ["saltstack", "salt", "other"] + ) # 'list_enabled_plugins' function tests: 2 def test_list_enabled_plugins(self): - ''' + """ Test if it returns a list of plugins. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack\nsalt\nother', 'stderr': ''}) - mock_pkg = MagicMock(return_value='') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, - 'pkg.version': mock_pkg}): - self.assertListEqual(rabbitmq.list_enabled_plugins(), ['saltstack', 'salt', 'other']) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "saltstack\nsalt\nother", + "stderr": "", + } + ) + mock_pkg = MagicMock(return_value="") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ): + self.assertListEqual( + rabbitmq.list_enabled_plugins(), ["saltstack", "salt", "other"] + ) def test_list_enabled_plugins_space_delimited(self): - ''' + """ Test if it returns a list of plugins. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack salt other', 'stderr': ''}) - mock_pkg = MagicMock(return_value='') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, - 'pkg.version': mock_pkg}): - self.assertListEqual(rabbitmq.list_enabled_plugins(), ['saltstack', 'salt', 'other']) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack salt other", "stderr": ""} + ) + mock_pkg = MagicMock(return_value="") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ): + self.assertListEqual( + rabbitmq.list_enabled_plugins(), ["saltstack", "salt", "other"] + ) # 'plugin_is_enabled' function tests: 2 def test_plugin_is_enabled(self): - ''' + """ Test if it returns true for an enabled plugin. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack\nsalt\nother', 'stderr': ''}) - mock_pkg = MagicMock(return_value='') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, - 'pkg.version': mock_pkg}): - self.assertTrue(rabbitmq.plugin_is_enabled('saltstack')) - self.assertTrue(rabbitmq.plugin_is_enabled('salt')) - self.assertTrue(rabbitmq.plugin_is_enabled('other')) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "saltstack\nsalt\nother", + "stderr": "", + } + ) + mock_pkg = MagicMock(return_value="") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ): + self.assertTrue(rabbitmq.plugin_is_enabled("saltstack")) + self.assertTrue(rabbitmq.plugin_is_enabled("salt")) + self.assertTrue(rabbitmq.plugin_is_enabled("other")) def test_plugin_is_enabled_negative(self): - ''' + """ Test if it returns false for a disabled plugin. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack\nother', 'stderr': ''}) - mock_pkg = MagicMock(return_value='') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, - 'pkg.version': mock_pkg}): - self.assertFalse(rabbitmq.plugin_is_enabled('salt')) - self.assertFalse(rabbitmq.plugin_is_enabled('stack')) - self.assertFalse(rabbitmq.plugin_is_enabled('random')) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack\nother", "stderr": ""} + ) + mock_pkg = MagicMock(return_value="") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ): + self.assertFalse(rabbitmq.plugin_is_enabled("salt")) + self.assertFalse(rabbitmq.plugin_is_enabled("stack")) + self.assertFalse(rabbitmq.plugin_is_enabled("random")) # 'enable_plugin' function tests: 1 def test_enable_plugin(self): - ''' + """ Test if it enable a RabbitMQ plugin via the rabbitmq-plugins command. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - mock_pkg = MagicMock(return_value='') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, - 'pkg.version': mock_pkg}): - self.assertDictEqual(rabbitmq.enable_plugin('salt'), - {'Enabled': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + mock_pkg = MagicMock(return_value="") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ): + self.assertDictEqual( + rabbitmq.enable_plugin("salt"), {"Enabled": "saltstack"} + ) # 'disable_plugin' function tests: 1 def test_disable_plugin(self): - ''' + """ Test if it disable a RabbitMQ plugin via the rabbitmq-plugins command. - ''' - mock_run = MagicMock(return_value={'retcode': 0, 'stdout': 'saltstack', 'stderr': ''}) - mock_pkg = MagicMock(return_value='') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, - 'pkg.version': mock_pkg}): - self.assertDictEqual(rabbitmq.disable_plugin('salt'), - {'Disabled': 'saltstack'}) + """ + mock_run = MagicMock( + return_value={"retcode": 0, "stdout": "saltstack", "stderr": ""} + ) + mock_pkg = MagicMock(return_value="") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ): + self.assertDictEqual( + rabbitmq.disable_plugin("salt"), {"Disabled": "saltstack"} + ) # 'list_upstreams' function tests: 1 def test_list_upstreams(self): - ''' + """ Test if it returns a list of upstreams. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 0, - 'stdout': 'federation-upstream\tremote-name\t{"ack-mode":"on-confirm"' - ',"max-hops":1,"trust-user-id":true,"uri":"amqp://username:' - 'password@remote.fqdn"}', - 'stderr': ''}) - mock_pkg = MagicMock(return_value='') - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run, - 'pkg.version': mock_pkg}): + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": 'federation-upstream\tremote-name\t{"ack-mode":"on-confirm"' + ',"max-hops":1,"trust-user-id":true,"uri":"amqp://username:' + 'password@remote.fqdn"}', + "stderr": "", + } + ) + mock_pkg = MagicMock(return_value="") + with patch.dict( + rabbitmq.__salt__, {"cmd.run_all": mock_run, "pkg.version": mock_pkg} + ): self.assertDictEqual( rabbitmq.list_upstreams(), - {'remote-name': ('{"ack-mode":"on-confirm","max-hops":1,' - '"trust-user-id":true,"uri":"amqp://username:' - 'password@remote.fqdn"}')} + { + "remote-name": ( + '{"ack-mode":"on-confirm","max-hops":1,' + '"trust-user-id":true,"uri":"amqp://username:' + 'password@remote.fqdn"}' + ) + }, ) # 'upstream_exists' function tests: 2 def test_upstream_exists(self): - ''' + """ Test whether a given rabbitmq-internal upstream exists based on rabbitmqctl list_upstream. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 0, - 'stdout': 'federation-upstream\tremote-name\t{"ack-mode":"on-confirm"' - ',"max-hops":1,"trust-user-id":true,"uri":"amqp://username:' - 'password@remote.fqdn"}', - 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertTrue(rabbitmq.upstream_exists('remote-name')) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": 'federation-upstream\tremote-name\t{"ack-mode":"on-confirm"' + ',"max-hops":1,"trust-user-id":true,"uri":"amqp://username:' + 'password@remote.fqdn"}', + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertTrue(rabbitmq.upstream_exists("remote-name")) def test_upstream_exists_negative(self): - ''' + """ Negative test of whether rabbitmq-internal upstream exists based on rabbitmqctl list_upstream. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 0, - 'stdout': 'federation-upstream\tremote-name\t{"ack-mode":"on-confirm"' - ',"max-hops":1,"trust-user-id":true,"uri":"amqp://username:' - 'password@remote.fqdn"}', - 'stderr': ''}) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertFalse(rabbitmq.upstream_exists('does-not-exist')) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": 'federation-upstream\tremote-name\t{"ack-mode":"on-confirm"' + ',"max-hops":1,"trust-user-id":true,"uri":"amqp://username:' + 'password@remote.fqdn"}', + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertFalse(rabbitmq.upstream_exists("does-not-exist")) # 'add_upstream' function tests: 1 def test_set_upstream(self): - ''' + """ Test if a rabbitMQ upstream gets configured properly. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 0, - 'stdout': ('Setting runtime parameter "federation-upstream" for component ' - '"remote-name" to "{"trust-user-id": true, "uri": ' - '"amqp://username:password@remote.fqdn", "ack-mode": "on-confirm", ' - '"max-hops": 1}" in vhost "/" ...'), - 'stderr': '' - }) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertTrue(rabbitmq.set_upstream('remote-name', - 'amqp://username:password@remote.fqdn', - ack_mode='on-confirm', - max_hops=1, - trust_user_id=True)) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": ( + 'Setting runtime parameter "federation-upstream" for component ' + '"remote-name" to "{"trust-user-id": true, "uri": ' + '"amqp://username:password@remote.fqdn", "ack-mode": "on-confirm", ' + '"max-hops": 1}" in vhost "/" ...' + ), + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertTrue( + rabbitmq.set_upstream( + "remote-name", + "amqp://username:password@remote.fqdn", + ack_mode="on-confirm", + max_hops=1, + trust_user_id=True, + ) + ) # 'delete_upstream' function tests: 2 def test_delete_upstream(self): - ''' + """ Test if an upstream gets deleted properly using rabbitmqctl delete_upstream. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 0, - 'stdout': ('Clearing runtime parameter "remote-name" for component ' - '"federation-upstream" on vhost "/" ...'), - 'stderr': '' - }) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertTrue(rabbitmq.delete_upstream('remote-name')) + """ + mock_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": ( + 'Clearing runtime parameter "remote-name" for component ' + '"federation-upstream" on vhost "/" ...' + ), + "stderr": "", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertTrue(rabbitmq.delete_upstream("remote-name")) def test_delete_upstream_negative(self): - ''' + """ Negative test trying to delete a non-existant upstream. - ''' - mock_run = MagicMock(return_value={ - 'retcode': 70, - 'stdout': ('Clearing runtime parameter "remote-name" for component ' - '"federation-upstream" on vhost "/" ...'), - 'stderr': 'Error:\nParameter does not exist' - }) - with patch.dict(rabbitmq.__salt__, {'cmd.run_all': mock_run}): - self.assertRaises(CommandExecutionError, rabbitmq.delete_upstream, 'remote-name') + """ + mock_run = MagicMock( + return_value={ + "retcode": 70, + "stdout": ( + 'Clearing runtime parameter "remote-name" for component ' + '"federation-upstream" on vhost "/" ...' + ), + "stderr": "Error:\nParameter does not exist", + } + ) + with patch.dict(rabbitmq.__salt__, {"cmd.run_all": mock_run}): + self.assertRaises( + CommandExecutionError, rabbitmq.delete_upstream, "remote-name" + ) diff --git a/tests/unit/modules/test_random_org.py b/tests/unit/modules/test_random_org.py index 0b5f72cd6f5..28159cfe5ce 100644 --- a/tests/unit/modules/test_random_org.py +++ b/tests/unit/modules/test_random_org.py @@ -1,38 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.helpers import flaky -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase - # Import Salt Libs import salt.modules.random_org as random_org # Import 3rd-party libs from salt.ext.tornado.httpclient import HTTPClient +# Import Salt Testing Libs +from tests.support.helpers import flaky +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase, skipIf + def check_status(): - ''' + """ Check the status of random.org - ''' + """ try: - return HTTPClient().fetch('https://api.random.org/').code == 200 + return HTTPClient().fetch("https://api.random.org/").code == 200 except Exception: # pylint: disable=broad-except return False -@skipIf(True, 'WAR ROOM 7/31/2019, test needs to allow for quotas of random website') +@skipIf(True, "WAR ROOM 7/31/2019, test needs to allow for quotas of random website") class RandomOrgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.random_org - ''' + """ + def setup_loader_modules(self): return {random_org: {}} @@ -43,268 +44,359 @@ class RandomOrgTestCase(TestCase, LoaderModuleMockMixin): # 'getUsage' function tests: 1 def test_getusage(self): - ''' + """ Test if it show current usages statistics. - ''' - ret = {'message': 'No Random.org api key or api version found.', - 'res': False} + """ + ret = {"message": "No Random.org api key or api version found.", "res": False} self.assertDictEqual(random_org.getUsage(), ret) - self.assertDictEqual(random_org.getUsage(api_key='peW', - api_version='1'), - {'bitsLeft': None, 'requestsLeft': None, - 'res': True, 'totalBits': None, - 'totalRequests': None}) + self.assertDictEqual( + random_org.getUsage(api_key="peW", api_version="1"), + { + "bitsLeft": None, + "requestsLeft": None, + "res": True, + "totalBits": None, + "totalRequests": None, + }, + ) # 'generateIntegers' function tests: 1 def test_generateintegers(self): - ''' + """ Test if it generate random integers. - ''' - ret1 = {'message': 'No Random.org api key or api version found.', - 'res': False} + """ + ret1 = {"message": "No Random.org api key or api version found.", "res": False} self.assertDictEqual(random_org.generateIntegers(), ret1) - ret2 = {'message': 'Rquired argument, number is missing.', 'res': False} - self.assertDictEqual(random_org.generateIntegers(api_key='peW', - api_version='1'), ret2) + ret2 = {"message": "Rquired argument, number is missing.", "res": False} + self.assertDictEqual( + random_org.generateIntegers(api_key="peW", api_version="1"), ret2 + ) - ret3 = {'message': 'Number of integers must be between 1 and 10000', - 'res': False} - self.assertDictEqual(random_org.generateIntegers(api_key='peW', - api_version='1', - number='5', - minimum='1', - maximum='6'), ret3) + ret3 = { + "message": "Number of integers must be between 1 and 10000", + "res": False, + } + self.assertDictEqual( + random_org.generateIntegers( + api_key="peW", api_version="1", number="5", minimum="1", maximum="6" + ), + ret3, + ) - ret4 = {'message': ('Minimum argument must be between -1,000,000,000' - ' and 1,000,000,000'), 'res': False} - self.assertDictEqual(random_org.generateIntegers(api_key='peW', - api_version='1', - number=5, minimum='1', - maximum='6'), ret4) + ret4 = { + "message": ( + "Minimum argument must be between -1,000,000,000" " and 1,000,000,000" + ), + "res": False, + } + self.assertDictEqual( + random_org.generateIntegers( + api_key="peW", api_version="1", number=5, minimum="1", maximum="6" + ), + ret4, + ) - ret5 = {'message': ('Maximum argument must be between -1,000,000,000' - ' and 1,000,000,000'), 'res': False} - self.assertDictEqual(random_org.generateIntegers(api_key='peW', - api_version='1', - number=5, minimum=1, - maximum='6'), ret5) + ret5 = { + "message": ( + "Maximum argument must be between -1,000,000,000" " and 1,000,000,000" + ), + "res": False, + } + self.assertDictEqual( + random_org.generateIntegers( + api_key="peW", api_version="1", number=5, minimum=1, maximum="6" + ), + ret5, + ) - ret6 = {'message': 'Base must be either 2, 8, 10 or 16.', 'res': False} - self.assertDictEqual(random_org.generateIntegers(api_key='peW', - api_version='1', - number=5, minimum=1, - maximum=6, base='2'), - ret6) + ret6 = {"message": "Base must be either 2, 8, 10 or 16.", "res": False} + self.assertDictEqual( + random_org.generateIntegers( + api_key="peW", api_version="1", number=5, minimum=1, maximum=6, base="2" + ), + ret6, + ) - ret7 = {'message': "Parameter 'apiKey' is malformed", 'res': False} - self.assertDictEqual(random_org.generateIntegers(api_key='peW', - api_version='1', - number=5, minimum=1, - maximum=6, base=2), - ret7) + ret7 = {"message": "Parameter 'apiKey' is malformed", "res": False} + self.assertDictEqual( + random_org.generateIntegers( + api_key="peW", api_version="1", number=5, minimum=1, maximum=6, base=2 + ), + ret7, + ) # 'generateStrings' function tests: 1 def test_generatestrings(self): - ''' + """ Test if it generate random strings. - ''' - ret1 = {'message': 'No Random.org api key or api version found.', - 'res': False} + """ + ret1 = {"message": "No Random.org api key or api version found.", "res": False} self.assertDictEqual(random_org.generateStrings(), ret1) - ret2 = {'message': 'Required argument, number is missing.', - 'res': False} - self.assertDictEqual(random_org.generateStrings(api_key='peW', - api_version='1'), ret2) + ret2 = {"message": "Required argument, number is missing.", "res": False} + self.assertDictEqual( + random_org.generateStrings(api_key="peW", api_version="1"), ret2 + ) - ret3 = {'message': 'Number of strings must be between 1 and 10000', - 'res': False} - char = 'abcdefghijklmnopqrstuvwxyz' - self.assertDictEqual(random_org.generateStrings(api_key='peW', - api_version='1', - number='5', - length='8', - characters=char), ret3) + ret3 = { + "message": "Number of strings must be between 1 and 10000", + "res": False, + } + char = "abcdefghijklmnopqrstuvwxyz" + self.assertDictEqual( + random_org.generateStrings( + api_key="peW", api_version="1", number="5", length="8", characters=char + ), + ret3, + ) - ret3 = {'message': 'Length of strings must be between 1 and 20', - 'res': False} - self.assertDictEqual(random_org.generateStrings(api_key='peW', - api_version='1', - number=5, - length='8', - characters=char), ret3) + ret3 = {"message": "Length of strings must be between 1 and 20", "res": False} + self.assertDictEqual( + random_org.generateStrings( + api_key="peW", api_version="1", number=5, length="8", characters=char + ), + ret3, + ) - ret3 = {'message': 'Length of characters must be less than 80.', - 'res': False} - self.assertDictEqual(random_org.generateStrings(api_key='peW', - api_version='1', - number=5, - length=8, - characters=char*4), - ret3) + ret3 = {"message": "Length of characters must be less than 80.", "res": False} + self.assertDictEqual( + random_org.generateStrings( + api_key="peW", api_version="1", number=5, length=8, characters=char * 4 + ), + ret3, + ) - ret3 = {'message': "Parameter 'apiKey' is malformed", - 'res': False} - self.assertDictEqual(random_org.generateStrings(api_key='peW', - api_version='1', - number=5, - length=8, - characters=char), ret3) + ret3 = {"message": "Parameter 'apiKey' is malformed", "res": False} + self.assertDictEqual( + random_org.generateStrings( + api_key="peW", api_version="1", number=5, length=8, characters=char + ), + ret3, + ) # 'generateUUIDs' function tests: 1 def test_generateuuids(self): - ''' + """ Test if it generate a list of random UUIDs. - ''' - ret1 = {'message': 'No Random.org api key or api version found.', - 'res': False} + """ + ret1 = {"message": "No Random.org api key or api version found.", "res": False} self.assertDictEqual(random_org.generateUUIDs(), ret1) - ret2 = {'message': 'Required argument, number is missing.', - 'res': False} - self.assertDictEqual(random_org.generateUUIDs(api_key='peW', - api_version='1'), ret2) + ret2 = {"message": "Required argument, number is missing.", "res": False} + self.assertDictEqual( + random_org.generateUUIDs(api_key="peW", api_version="1"), ret2 + ) - ret3 = {'message': 'Number of UUIDs must be between 1 and 1000', - 'res': False} - self.assertDictEqual(random_org.generateUUIDs(api_key='peW', - api_version='1', - number='5'), ret3) + ret3 = {"message": "Number of UUIDs must be between 1 and 1000", "res": False} + self.assertDictEqual( + random_org.generateUUIDs(api_key="peW", api_version="1", number="5"), ret3 + ) - ret3 = {'message': "Parameter 'apiKey' is malformed", - 'res': False} - self.assertDictEqual(random_org.generateUUIDs(api_key='peW', - api_version='1', - number=5), ret3) + ret3 = {"message": "Parameter 'apiKey' is malformed", "res": False} + self.assertDictEqual( + random_org.generateUUIDs(api_key="peW", api_version="1", number=5), ret3 + ) # 'generateDecimalFractions' function tests: 1 @flaky def test_generatedecimalfractions(self): - ''' + """ Test if it generates true random decimal fractions. - ''' - ret1 = {'message': 'No Random.org api key or api version found.', - 'res': False} + """ + ret1 = {"message": "No Random.org api key or api version found.", "res": False} self.assertDictEqual(random_org.generateDecimalFractions(), ret1) - ret2 = {'message': 'Required argument, number is missing.', - 'res': False} - self.assertDictEqual(random_org.generateDecimalFractions - (api_key='peW', api_version='1'), ret2) + ret2 = {"message": "Required argument, number is missing.", "res": False} + self.assertDictEqual( + random_org.generateDecimalFractions(api_key="peW", api_version="1"), ret2 + ) - ret3 = {'message': ('Number of decimal fractions must be' - ' between 1 and 10000'), 'res': False} - self.assertDictEqual(random_org.generateDecimalFractions - (api_key='peW', api_version='1', number='5', - decimalPlaces='4', replacement=True), ret3) + ret3 = { + "message": ("Number of decimal fractions must be" " between 1 and 10000"), + "res": False, + } + self.assertDictEqual( + random_org.generateDecimalFractions( + api_key="peW", + api_version="1", + number="5", + decimalPlaces="4", + replacement=True, + ), + ret3, + ) - ret4 = {'message': 'Number of decimal places must be between 1 and 20', - 'res': False} - self.assertDictEqual(random_org.generateDecimalFractions - (api_key='peW', api_version='1', number=5, - decimalPlaces='4', replacement=True), ret4) + ret4 = { + "message": "Number of decimal places must be between 1 and 20", + "res": False, + } + self.assertDictEqual( + random_org.generateDecimalFractions( + api_key="peW", + api_version="1", + number=5, + decimalPlaces="4", + replacement=True, + ), + ret4, + ) - ret5 = {'message': "Parameter 'apiKey' is malformed", 'res': False} - self.assertDictEqual(random_org.generateDecimalFractions - (api_key='peW', api_version='1', number=5, - decimalPlaces=4, replacement=True), ret5) + ret5 = {"message": "Parameter 'apiKey' is malformed", "res": False} + self.assertDictEqual( + random_org.generateDecimalFractions( + api_key="peW", + api_version="1", + number=5, + decimalPlaces=4, + replacement=True, + ), + ret5, + ) # 'generateGaussians' function tests: 1 @flaky def test_generategaussians(self): - ''' + """ Test if it generates true random numbers from a Gaussian distribution (also known as a normal distribution). - ''' - ret1 = {'message': 'No Random.org api key or api version found.', - 'res': False} + """ + ret1 = {"message": "No Random.org api key or api version found.", "res": False} self.assertDictEqual(random_org.generateGaussians(), ret1) - ret2 = {'message': 'Required argument, number is missing.', - 'res': False} - self.assertDictEqual(random_org.generateGaussians(api_key='peW', - api_version='1'), - ret2) + ret2 = {"message": "Required argument, number is missing.", "res": False} + self.assertDictEqual( + random_org.generateGaussians(api_key="peW", api_version="1"), ret2 + ) - ret3 = {'message': ('Number of decimal fractions must be' - ' between 1 and 10000'), 'res': False} - self.assertDictEqual(random_org.generateGaussians - (api_key='peW', api_version='1', - number='5', mean='0.0', standardDeviation='1.0', - significantDigits='8'), ret3) + ret3 = { + "message": ("Number of decimal fractions must be" " between 1 and 10000"), + "res": False, + } + self.assertDictEqual( + random_org.generateGaussians( + api_key="peW", + api_version="1", + number="5", + mean="0.0", + standardDeviation="1.0", + significantDigits="8", + ), + ret3, + ) - ret4 = {'message': ("The distribution's mean must be between" - " -1000000 and 1000000"), 'res': False} - self.assertDictEqual(random_org.generateGaussians - (api_key='peW', api_version='1', number=5, - mean='0.0', standardDeviation='1.0', - significantDigits='8'), ret4) + ret4 = { + "message": ( + "The distribution's mean must be between" " -1000000 and 1000000" + ), + "res": False, + } + self.assertDictEqual( + random_org.generateGaussians( + api_key="peW", + api_version="1", + number=5, + mean="0.0", + standardDeviation="1.0", + significantDigits="8", + ), + ret4, + ) - ret5 = {'message': ("The distribution's standard deviation must be" - " between -1000000 and 1000000"), 'res': False} - self.assertDictEqual(random_org.generateGaussians - (api_key='peW', api_version='1', number=5, - mean=0.0, standardDeviation='1.0', - significantDigits='8'), ret5) + ret5 = { + "message": ( + "The distribution's standard deviation must be" + " between -1000000 and 1000000" + ), + "res": False, + } + self.assertDictEqual( + random_org.generateGaussians( + api_key="peW", + api_version="1", + number=5, + mean=0.0, + standardDeviation="1.0", + significantDigits="8", + ), + ret5, + ) - ret6 = {'message': ('The number of significant digits must be' - ' between 2 and 20'), 'res': False} - self.assertDictEqual(random_org.generateGaussians - (api_key='peW', api_version='1', number=5, - mean=0.0, standardDeviation=1.0, - significantDigits='8'), ret6) + ret6 = { + "message": ("The number of significant digits must be" " between 2 and 20"), + "res": False, + } + self.assertDictEqual( + random_org.generateGaussians( + api_key="peW", + api_version="1", + number=5, + mean=0.0, + standardDeviation=1.0, + significantDigits="8", + ), + ret6, + ) - ret7 = {'message': "Parameter 'apiKey' is malformed", 'res': False} - self.assertDictEqual(random_org.generateGaussians(api_key='peW', - api_version='1', - number=5, mean=0.0, - standardDeviation=1.0, - significantDigits=8), - ret7) + ret7 = {"message": "Parameter 'apiKey' is malformed", "res": False} + self.assertDictEqual( + random_org.generateGaussians( + api_key="peW", + api_version="1", + number=5, + mean=0.0, + standardDeviation=1.0, + significantDigits=8, + ), + ret7, + ) # 'generateBlobs' function tests: 1 def test_generateblobs(self): - ''' + """ Test if it list all Slack users. - ''' - ret1 = {'message': 'No Random.org api key or api version found.', - 'res': False} + """ + ret1 = {"message": "No Random.org api key or api version found.", "res": False} self.assertDictEqual(random_org.generateBlobs(), ret1) - ret2 = {'message': 'Required argument, number is missing.', - 'res': False} - self.assertDictEqual(random_org.generateBlobs(api_key='peW', - api_version='1'), ret2) + ret2 = {"message": "Required argument, number is missing.", "res": False} + self.assertDictEqual( + random_org.generateBlobs(api_key="peW", api_version="1"), ret2 + ) - ret3 = {'message': 'Number of blobs must be between 1 and 100', - 'res': False} - self.assertDictEqual(random_org.generateBlobs(api_key='peW', - api_version='1', - number='5', size='1'), - ret3) + ret3 = {"message": "Number of blobs must be between 1 and 100", "res": False} + self.assertDictEqual( + random_org.generateBlobs( + api_key="peW", api_version="1", number="5", size="1" + ), + ret3, + ) - ret4 = {'message': 'Number of blobs must be between 1 and 100', - 'res': False} - self.assertDictEqual(random_org.generateBlobs(api_key='peW', - api_version='1', number=5, - size=1), ret4) + ret4 = {"message": "Number of blobs must be between 1 and 100", "res": False} + self.assertDictEqual( + random_org.generateBlobs(api_key="peW", api_version="1", number=5, size=1), + ret4, + ) - ret5 = {'message': 'Format must be either base64 or hex.', 'res': False} - self.assertDictEqual(random_org.generateBlobs(api_key='peW', - api_version='1', number=5, - size=8, format='oct'), - ret5) + ret5 = {"message": "Format must be either base64 or hex.", "res": False} + self.assertDictEqual( + random_org.generateBlobs( + api_key="peW", api_version="1", number=5, size=8, format="oct" + ), + ret5, + ) - ret6 = {'message': "Parameter 'apiKey' is malformed", 'res': False} - self.assertDictEqual(random_org.generateBlobs(api_key='peW', - api_version='1', - number=5, size=8, - format='hex'), ret6) + ret6 = {"message": "Parameter 'apiKey' is malformed", "res": False} + self.assertDictEqual( + random_org.generateBlobs( + api_key="peW", api_version="1", number=5, size=8, format="hex" + ), + ret6, + ) diff --git a/tests/unit/modules/test_rbenv.py b/tests/unit/modules/test_rbenv.py index 528925198c5..7c072d106db 100644 --- a/tests/unit/modules/test_rbenv.py +++ b/tests/unit/modules/test_rbenv.py @@ -1,123 +1,125 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +import os # Import Salt Libs import salt.modules.rbenv as rbenv +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RbenvTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.rbenv - ''' + """ + def setup_loader_modules(self): return {rbenv: {}} def test_install(self): - ''' + """ Test for install Rbenv systemwide - ''' - with patch.object(rbenv, '_rbenv_path', return_value=True): - with patch.object(rbenv, '_install_rbenv', return_value=True): - with patch.object(rbenv, '_install_ruby_build', - return_value=True): - with patch.object(os.path, 'expanduser', return_value='A'): + """ + with patch.object(rbenv, "_rbenv_path", return_value=True): + with patch.object(rbenv, "_install_rbenv", return_value=True): + with patch.object(rbenv, "_install_ruby_build", return_value=True): + with patch.object(os.path, "expanduser", return_value="A"): self.assertTrue(rbenv.install()) def test_update(self): - ''' + """ Test for updates the current versions of Rbenv and Ruby-Build - ''' - with patch.object(rbenv, '_rbenv_path', return_value=True): - with patch.object(rbenv, '_update_rbenv', return_value=True): - with patch.object(rbenv, '_update_ruby_build', - return_value=True): - with patch.object(os.path, 'expanduser', return_value='A'): + """ + with patch.object(rbenv, "_rbenv_path", return_value=True): + with patch.object(rbenv, "_update_rbenv", return_value=True): + with patch.object(rbenv, "_update_ruby_build", return_value=True): + with patch.object(os.path, "expanduser", return_value="A"): self.assertTrue(rbenv.update()) def test_is_installed(self): - ''' + """ Test for check if Rbenv is installed. - ''' - with patch.object(rbenv, '_rbenv_bin', return_value='A'): - with patch.dict(rbenv.__salt__, - {'cmd.has_exec': MagicMock(return_value=True)}): + """ + with patch.object(rbenv, "_rbenv_bin", return_value="A"): + with patch.dict( + rbenv.__salt__, {"cmd.has_exec": MagicMock(return_value=True)} + ): self.assertTrue(rbenv.is_installed()) def test_install_ruby(self): - ''' + """ Test for install a ruby implementation. - ''' - with patch.dict(rbenv.__grains__, {'os': 'FreeBSD'}): - with patch.dict(rbenv.__salt__, - {'config.get': MagicMock(return_value='True')}): - with patch.object(rbenv, '_rbenv_exec', - return_value={'retcode': 0, - 'stderr': 'stderr'}): - with patch.object(rbenv, 'rehash', return_value=None): - self.assertEqual(rbenv.install_ruby('ruby'), 'stderr') + """ + with patch.dict(rbenv.__grains__, {"os": "FreeBSD"}): + with patch.dict( + rbenv.__salt__, {"config.get": MagicMock(return_value="True")} + ): + with patch.object( + rbenv, + "_rbenv_exec", + return_value={"retcode": 0, "stderr": "stderr"}, + ): + with patch.object(rbenv, "rehash", return_value=None): + self.assertEqual(rbenv.install_ruby("ruby"), "stderr") - with patch.object(rbenv, '_rbenv_exec', - return_value={'retcode': 1, - 'stderr': 'stderr'}): - with patch.object(rbenv, 'uninstall_ruby', - return_value=None): - self.assertFalse(rbenv.install_ruby('ruby')) + with patch.object( + rbenv, + "_rbenv_exec", + return_value={"retcode": 1, "stderr": "stderr"}, + ): + with patch.object(rbenv, "uninstall_ruby", return_value=None): + self.assertFalse(rbenv.install_ruby("ruby")) def test_uninstall_ruby(self): - ''' + """ Test for uninstall a ruby implementation. - ''' - with patch.object(rbenv, '_rbenv_exec', return_value=None): - self.assertTrue(rbenv.uninstall_ruby('ruby', 'runas')) + """ + with patch.object(rbenv, "_rbenv_exec", return_value=None): + self.assertTrue(rbenv.uninstall_ruby("ruby", "runas")) def test_versions(self): - ''' + """ Test for list the installed versions of ruby. - ''' - with patch.object(rbenv, '_rbenv_exec', return_value='A\nBC\nD'): - self.assertListEqual(rbenv.versions(), ['A', 'BC', 'D']) + """ + with patch.object(rbenv, "_rbenv_exec", return_value="A\nBC\nD"): + self.assertListEqual(rbenv.versions(), ["A", "BC", "D"]) def test_default(self): - ''' + """ Test for returns or sets the currently defined default ruby. - ''' - with patch.object(rbenv, '_rbenv_exec', - MagicMock(side_effect=[None, False])): - self.assertTrue(rbenv.default('ruby', 'runas')) + """ + with patch.object(rbenv, "_rbenv_exec", MagicMock(side_effect=[None, False])): + self.assertTrue(rbenv.default("ruby", "runas")) - self.assertEqual(rbenv.default(), '') + self.assertEqual(rbenv.default(), "") def test_list_(self): - ''' + """ Test for list the installable versions of ruby. - ''' - with patch.object(rbenv, '_rbenv_exec', return_value='A\nB\nCD\n'): - self.assertListEqual(rbenv.list_(), ['A', 'B', 'CD']) + """ + with patch.object(rbenv, "_rbenv_exec", return_value="A\nB\nCD\n"): + self.assertListEqual(rbenv.list_(), ["A", "B", "CD"]) def test_rehash(self): - ''' + """ Test for run rbenv rehash to update the installed shims. - ''' - with patch.object(rbenv, '_rbenv_exec', return_value=None): + """ + with patch.object(rbenv, "_rbenv_exec", return_value=None): self.assertTrue(rbenv.rehash()) def test_do_with_ruby(self): - ''' + """ Test for execute a ruby command with rbenv's shims using a specific ruby version. - ''' - with patch.object(rbenv, 'do', return_value='A'): - self.assertEqual(rbenv.do_with_ruby('ruby', 'cmdline'), 'A') + """ + with patch.object(rbenv, "do", return_value="A"): + self.assertEqual(rbenv.do_with_ruby("ruby", "cmdline"), "A") diff --git a/tests/unit/modules/test_rdp.py b/tests/unit/modules/test_rdp.py index 40c68e711c2..0fc7be25ec1 100644 --- a/tests/unit/modules/test_rdp.py +++ b/tests/unit/modules/test_rdp.py @@ -1,60 +1,58 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.modules.rdp as rdp +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RdpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.rdp - ''' + """ + def setup_loader_modules(self): return {rdp: {}} # 'enable' function tests: 1 def test_enable(self): - ''' + """ Test if it enables RDP the service on the server - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(rdp.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.rdp._parse_return_code_powershell', - MagicMock(return_value=0)): + with patch.dict(rdp.__salt__, {"cmd.run": mock}), patch( + "salt.modules.rdp._parse_return_code_powershell", MagicMock(return_value=0) + ): self.assertTrue(rdp.enable()) # 'disable' function tests: 1 def test_disable(self): - ''' + """ Test if it disables RDP the service on the server - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(rdp.__salt__, {'cmd.run': mock}), \ - patch('salt.modules.rdp._parse_return_code_powershell', - MagicMock(return_value=0)): + with patch.dict(rdp.__salt__, {"cmd.run": mock}), patch( + "salt.modules.rdp._parse_return_code_powershell", MagicMock(return_value=0) + ): self.assertTrue(rdp.disable()) # 'status' function tests: 1 def test_status(self): - ''' + """ Test if it shows rdp is enabled on the server - ''' - mock = MagicMock(return_value='1') - with patch.dict(rdp.__salt__, {'cmd.run': mock}): + """ + mock = MagicMock(return_value="1") + with patch.dict(rdp.__salt__, {"cmd.run": mock}): self.assertTrue(rdp.status()) diff --git a/tests/unit/modules/test_redismod.py b/tests/unit/modules/test_redismod.py index 4acb8ed01e5..50cef009560 100644 --- a/tests/unit/modules/test_redismod.py +++ b/tests/unit/modules/test_redismod.py @@ -1,35 +1,37 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -from datetime import datetime -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock) +from datetime import datetime # Import Salt Libs import salt.modules.redismod as redismod +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock +from tests.support.unit import TestCase + class Mockredis(object): - ''' + """ Mock redis class - ''' + """ + class ConnectionError(Exception): - ''' + """ Mock ConnectionError class - ''' + """ class MockConnect(object): - ''' + """ Mock Connect class - ''' + """ + counter = 0 def __init__(self): @@ -47,380 +49,381 @@ class MockConnect(object): @staticmethod def bgrewriteaof(): - ''' + """ Mock bgrewriteaof method - ''' - return 'A' + """ + return "A" @staticmethod def bgsave(): - ''' + """ Mock bgsave method - ''' - return 'A' + """ + return "A" def config_get(self, pattern): - ''' + """ Mock config_get method - ''' + """ self.pattern = pattern - return 'A' + return "A" def config_set(self, name, value): - ''' + """ Mock config_set method - ''' + """ self.name = name self.value = value - return 'A' + return "A" @staticmethod def dbsize(): - ''' + """ Mock dbsize method - ''' - return 'A' + """ + return "A" @staticmethod def delete(): - ''' + """ Mock delete method - ''' - return 'A' + """ + return "A" def exists(self, key): - ''' + """ Mock exists method - ''' + """ self.key = key - return 'A' + return "A" def expire(self, key, seconds): - ''' + """ Mock expire method - ''' + """ self.key = key self.seconds = seconds - return 'A' + return "A" def expireat(self, key, timestamp): - ''' + """ Mock expireat method - ''' + """ self.key = key self.timestamp = timestamp - return 'A' + return "A" @staticmethod def flushall(): - ''' + """ Mock flushall method - ''' - return 'A' + """ + return "A" @staticmethod def flushdb(): - ''' + """ Mock flushdb method - ''' - return 'A' + """ + return "A" def get(self, key): - ''' + """ Mock get method - ''' + """ self.key = key - return 'A' + return "A" def hget(self, key, field): - ''' + """ Mock hget method - ''' + """ self.key = key self.field = field - return 'A' + return "A" def hgetall(self, key): - ''' + """ Mock hgetall method - ''' + """ self.key = key - return 'A' + return "A" @staticmethod def info(): - ''' + """ Mock info method - ''' - return 'A' + """ + return "A" def keys(self, pattern): - ''' + """ Mock keys method - ''' + """ self.pattern = pattern - return 'A' + return "A" def type(self, key): - ''' + """ Mock type method - ''' + """ self.key = key - return 'A' + return "A" @staticmethod def lastsave(): - ''' + """ Mock lastsave method - ''' + """ return datetime.now() def llen(self, key): - ''' + """ Mock llen method - ''' + """ self.key = key - return 'A' + return "A" def lrange(self, key, start, stop): - ''' + """ Mock lrange method - ''' + """ self.key = key self.start = start self.stop = stop - return 'A' + return "A" @staticmethod def ping(): - ''' + """ Mock ping method - ''' + """ MockConnect.counter = MockConnect.counter + 1 if MockConnect.counter == 1: - return 'A' + return "A" elif MockConnect.counter in (2, 3, 5): - raise Mockredis.ConnectionError('foo') + raise Mockredis.ConnectionError("foo") @staticmethod def save(): - ''' + """ Mock save method - ''' - return 'A' + """ + return "A" def set(self, key, value): - ''' + """ Mock set method - ''' + """ self.key = key self.value = value - return 'A' + return "A" @staticmethod def shutdown(): - ''' + """ Mock shutdown method - ''' - return 'A' + """ + return "A" def slaveof(self, master_host, master_port): - ''' + """ Mock slaveof method - ''' + """ self.master_host = master_host self.master_port = master_port - return 'A' + return "A" def smembers(self, key): - ''' + """ Mock smembers method - ''' + """ self.key = key - return 'A' + return "A" @staticmethod def time(): - ''' + """ Mock time method - ''' - return 'A' + """ + return "A" def zcard(self, key): - ''' + """ Mock zcard method - ''' + """ self.key = key - return 'A' + return "A" def zrange(self, key, start, stop): - ''' + """ Mock zrange method - ''' + """ self.key = key self.start = start self.stop = stop - return 'A' + return "A" class RedismodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.redismod - ''' + """ + def setup_loader_modules(self): return { redismod: { - 'redis': Mockredis, - '_connect': MagicMock(return_value=MockConnect()) + "redis": Mockredis, + "_connect": MagicMock(return_value=MockConnect()), } } def test_bgrewriteaof(self): - ''' + """ Test to asynchronously rewrite the append-only file - ''' - self.assertEqual(redismod.bgrewriteaof(), 'A') + """ + self.assertEqual(redismod.bgrewriteaof(), "A") def test_bgsave(self): - ''' + """ Test to asynchronously save the dataset to disk - ''' - self.assertEqual(redismod.bgsave(), 'A') + """ + self.assertEqual(redismod.bgsave(), "A") def test_config_get(self): - ''' + """ Test to get redis server configuration values - ''' - self.assertEqual(redismod.config_get('*'), 'A') + """ + self.assertEqual(redismod.config_get("*"), "A") def test_config_set(self): - ''' + """ Test to set redis server configuration values - ''' - self.assertEqual(redismod.config_set('name', 'value'), 'A') + """ + self.assertEqual(redismod.config_set("name", "value"), "A") def test_dbsize(self): - ''' + """ Test to return the number of keys in the selected database - ''' - self.assertEqual(redismod.dbsize(), 'A') + """ + self.assertEqual(redismod.dbsize(), "A") def test_delete(self): - ''' + """ Test to deletes the keys from redis, returns number of keys deleted - ''' - self.assertEqual(redismod.delete(), 'A') + """ + self.assertEqual(redismod.delete(), "A") def test_exists(self): - ''' + """ Test to return true if the key exists in redis - ''' - self.assertEqual(redismod.exists('key'), 'A') + """ + self.assertEqual(redismod.exists("key"), "A") def test_expire(self): - ''' + """ Test to set a keys time to live in seconds - ''' - self.assertEqual(redismod.expire('key', 'seconds'), 'A') + """ + self.assertEqual(redismod.expire("key", "seconds"), "A") def test_expireat(self): - ''' + """ Test to set a keys expire at given UNIX time - ''' - self.assertEqual(redismod.expireat('key', 'timestamp'), 'A') + """ + self.assertEqual(redismod.expireat("key", "timestamp"), "A") def test_flushall(self): - ''' + """ Test to remove all keys from all databases - ''' - self.assertEqual(redismod.flushall(), 'A') + """ + self.assertEqual(redismod.flushall(), "A") def test_flushdb(self): - ''' + """ Test to remove all keys from the selected database - ''' - self.assertEqual(redismod.flushdb(), 'A') + """ + self.assertEqual(redismod.flushdb(), "A") def test_get_key(self): - ''' + """ Test to get redis key value - ''' - self.assertEqual(redismod.get_key('key'), 'A') + """ + self.assertEqual(redismod.get_key("key"), "A") def test_hget(self): - ''' + """ Test to get specific field value from a redis hash, returns dict - ''' - self.assertEqual(redismod.hget('key', 'field'), 'A') + """ + self.assertEqual(redismod.hget("key", "field"), "A") def test_hgetall(self): - ''' + """ Test to get all fields and values from a redis hash, returns dict - ''' - self.assertEqual(redismod.hgetall('key'), 'A') + """ + self.assertEqual(redismod.hgetall("key"), "A") def test_info(self): - ''' + """ Test to get information and statistics about the server - ''' - self.assertEqual(redismod.info(), 'A') + """ + self.assertEqual(redismod.info(), "A") def test_keys(self): - ''' + """ Test to get redis keys, supports glob style patterns - ''' - self.assertEqual(redismod.keys('pattern'), 'A') + """ + self.assertEqual(redismod.keys("pattern"), "A") def test_key_type(self): - ''' + """ Test to get redis key type - ''' - self.assertEqual(redismod.key_type('key'), 'A') + """ + self.assertEqual(redismod.key_type("key"), "A") def test_lastsave(self): - ''' + """ Test to get the UNIX time in seconds of the last successful save to disk - ''' + """ self.assertTrue(redismod.lastsave()) def test_llen(self): - ''' + """ Test to get the length of a list in Redis - ''' - self.assertEqual(redismod.llen('key'), 'A') + """ + self.assertEqual(redismod.llen("key"), "A") def test_lrange(self): - ''' + """ Test to get a range of values from a list in Redis - ''' - self.assertEqual(redismod.lrange('key', 'start', 'stop'), 'A') + """ + self.assertEqual(redismod.lrange("key", "start", "stop"), "A") def test_ping(self): - ''' + """ Test to ping the server, returns False on connection errors - ''' - self.assertEqual(redismod.ping(), 'A') + """ + self.assertEqual(redismod.ping(), "A") self.assertFalse(redismod.ping()) def test_save(self): - ''' + """ Test to synchronously save the dataset to disk - ''' - self.assertEqual(redismod.save(), 'A') + """ + self.assertEqual(redismod.save(), "A") def test_set_key(self): - ''' + """ Test to set redis key value - ''' - self.assertEqual(redismod.set_key('key', 'value'), 'A') + """ + self.assertEqual(redismod.set_key("key", "value"), "A") def test_shutdown(self): - ''' + """ Test to synchronously save the dataset to disk and then shut down the server - ''' + """ self.assertFalse(redismod.shutdown()) self.assertTrue(redismod.shutdown()) @@ -428,32 +431,32 @@ class RedismodTestCase(TestCase, LoaderModuleMockMixin): self.assertFalse(redismod.shutdown()) def test_slaveof(self): - ''' + """ Test to make the server a slave of another instance, or promote it as master - ''' - self.assertEqual(redismod.slaveof('master_host', 'master_port'), 'A') + """ + self.assertEqual(redismod.slaveof("master_host", "master_port"), "A") def test_smembers(self): - ''' + """ Test to get members in a Redis set - ''' - self.assertListEqual(redismod.smembers('key'), ['A']) + """ + self.assertListEqual(redismod.smembers("key"), ["A"]) def test_time(self): - ''' + """ Test to return the current server UNIX time in seconds - ''' - self.assertEqual(redismod.time(), 'A') + """ + self.assertEqual(redismod.time(), "A") def test_zcard(self): - ''' + """ Test to get the length of a sorted set in Redis - ''' - self.assertEqual(redismod.zcard('key'), 'A') + """ + self.assertEqual(redismod.zcard("key"), "A") def test_zrange(self): - ''' + """ Test to get a range of values from a sorted set in Redis by index - ''' - self.assertEqual(redismod.zrange('key', 'start', 'stop'), 'A') + """ + self.assertEqual(redismod.zrange("key", "start", "stop"), "A") diff --git a/tests/unit/modules/test_reg.py b/tests/unit/modules/test_reg.py index 87dcbc01f84..04502a78faa 100644 --- a/tests/unit/modules/test_reg.py +++ b/tests/unit/modules/test_reg.py @@ -1,13 +1,7 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.helpers import destructiveTest, generate_random_name -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import patch, MagicMock -from tests.support.unit import TestCase, skipIf +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.reg as reg @@ -15,846 +9,863 @@ import salt.utils.stringutils import salt.utils.win_reg from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.helpers import destructiveTest, generate_random_name +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + try: import win32api + HAS_WIN32 = True except ImportError: HAS_WIN32 = False -UNICODE_KEY = 'Unicode Key \N{TRADE MARK SIGN}' -UNICODE_VALUE = 'Unicode Value ' \ - '\N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN}' -FAKE_KEY = '\\'.join(['SOFTWARE', generate_random_name('SaltTesting-')]) +UNICODE_KEY = "Unicode Key \N{TRADE MARK SIGN}" +UNICODE_VALUE = ( + "Unicode Value " "\N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN}" +) +FAKE_KEY = "\\".join(["SOFTWARE", generate_random_name("SaltTesting-")]) -@skipIf(not HAS_WIN32, 'Tests require win32 libraries') +@skipIf(not HAS_WIN32, "Tests require win32 libraries") class WinFunctionsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.reg - ''' + """ + def setup_loader_modules(self): return { reg: { - '__utils__': { - 'reg.delete_value': salt.utils.win_reg.delete_value, - 'reg.delete_key_recursive': salt.utils.win_reg.delete_key_recursive, - 'reg.key_exists': salt.utils.win_reg.key_exists, - 'reg.list_keys': salt.utils.win_reg.list_keys, - 'reg.list_values': salt.utils.win_reg.list_values, - 'reg.read_value': salt.utils.win_reg.read_value, - 'reg.set_value': salt.utils.win_reg.set_value, - 'reg.value_exists': salt.utils.win_reg.value_exists, + "__utils__": { + "reg.delete_value": salt.utils.win_reg.delete_value, + "reg.delete_key_recursive": salt.utils.win_reg.delete_key_recursive, + "reg.key_exists": salt.utils.win_reg.key_exists, + "reg.list_keys": salt.utils.win_reg.list_keys, + "reg.list_values": salt.utils.win_reg.list_values, + "reg.read_value": salt.utils.win_reg.read_value, + "reg.set_value": salt.utils.win_reg.set_value, + "reg.value_exists": salt.utils.win_reg.value_exists, } } } def test_key_exists_existing(self): - ''' + """ Tests the key_exists function using a well known registry key - ''' - self.assertTrue( - reg.key_exists(hive='HKLM', key='SOFTWARE\\Microsoft')) + """ + self.assertTrue(reg.key_exists(hive="HKLM", key="SOFTWARE\\Microsoft")) def test_key_exists_non_existing(self): - ''' + """ Tests the key_exists function using a non existing registry key - ''' - self.assertFalse(reg.key_exists(hive='HKLM', key=FAKE_KEY)) + """ + self.assertFalse(reg.key_exists(hive="HKLM", key=FAKE_KEY)) def test_key_exists_invalid_hive(self): - ''' + """ Tests the key_exists function using an invalid hive - ''' - self.assertRaises(CommandExecutionError, - reg.key_exists, - hive='BADHIVE', - key='SOFTWARE\\Microsoft') + """ + self.assertRaises( + CommandExecutionError, + reg.key_exists, + hive="BADHIVE", + key="SOFTWARE\\Microsoft", + ) def test_key_exists_unknown_key_error(self): - ''' + """ Tests the key_exists function with an unknown key error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): - self.assertRaises(win32api.error, - reg.key_exists, - hive='HKLM', - key='SOFTWARE\\Microsoft') + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): + self.assertRaises( + win32api.error, reg.key_exists, hive="HKLM", key="SOFTWARE\\Microsoft" + ) def test_value_exists_existing(self): - ''' + """ Tests the value_exists function using a well known registry key - ''' + """ self.assertTrue( reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='CommonFilesDir')) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="CommonFilesDir", + ) + ) def test_value_exists_non_existing(self): - ''' + """ Tests the value_exists function using a non existing registry key - ''' + """ self.assertFalse( reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='NonExistingValueName')) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="NonExistingValueName", + ) + ) def test_value_exists_invalid_hive(self): - ''' + """ Tests the value_exists function using an invalid hive - ''' - self.assertRaises(CommandExecutionError, - reg.value_exists, - hive='BADHIVE', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='CommonFilesDir') + """ + self.assertRaises( + CommandExecutionError, + reg.value_exists, + hive="BADHIVE", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="CommonFilesDir", + ) def test_value_exists_key_not_exist(self): - ''' + """ Tests the value_exists function when the key does not exist - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(2, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + side_effect=win32api.error(2, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): self.assertFalse( reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='CommonFilesDir')) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="CommonFilesDir", + ) + ) def test_value_exists_unknown_key_error(self): - ''' + """ Tests the value_exists function with an unknown error when opening the key - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): self.assertRaises( win32api.error, reg.value_exists, - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='CommonFilesDir') + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="CommonFilesDir", + ) def test_value_exists_empty_default_value(self): - ''' + """ Tests the value_exists function when querying the default value - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(2, 'RegQueryValueEx', 'Empty Value')) - with patch('salt.utils.win_reg.win32api.RegQueryValueEx', mock_error): + side_effect=win32api.error(2, "RegQueryValueEx", "Empty Value") + ) + with patch("salt.utils.win_reg.win32api.RegQueryValueEx", mock_error): self.assertTrue( reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname=None)) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname=None, + ) + ) def test_value_exists_no_vname(self): - ''' + """ Tests the value_exists function when the vname does not exist - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegQueryValueEx', 'Empty Value')) - with patch('salt.utils.win_reg.win32api.RegQueryValueEx', mock_error): + side_effect=win32api.error(123, "RegQueryValueEx", "Empty Value") + ) + with patch("salt.utils.win_reg.win32api.RegQueryValueEx", mock_error): self.assertFalse( reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='NonExistingValuePair')) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="NonExistingValuePair", + ) + ) def test_list_keys_existing(self): - ''' + """ Test the list_keys function using a well known registry key - ''' - self.assertIn( - 'Microsoft', - reg.list_keys(hive='HKLM', key='SOFTWARE')) + """ + self.assertIn("Microsoft", reg.list_keys(hive="HKLM", key="SOFTWARE")) def test_list_keys_non_existing(self): - ''' + """ Test the list_keys function using a non existing registry key - ''' - expected = (False, 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY)) - self.assertEqual(reg.list_keys(hive='HKLM', key=FAKE_KEY), expected) + """ + expected = (False, "Cannot find key: HKLM\\{0}".format(FAKE_KEY)) + self.assertEqual(reg.list_keys(hive="HKLM", key=FAKE_KEY), expected) def test_list_keys_invalid_hive(self): - ''' + """ Test the list_keys function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - reg.list_keys, - hive='BADHIVE', - key='SOFTWARE\\Microsoft') + """ + self.assertRaises( + CommandExecutionError, + reg.list_keys, + hive="BADHIVE", + key="SOFTWARE\\Microsoft", + ) def test_list_keys_unknown_key_error(self): - ''' + """ Tests the list_keys function with an unknown key error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): - self.assertRaises(win32api.error, - reg.list_keys, - hive='HKLM', - key='SOFTWARE\\Microsoft') + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): + self.assertRaises( + win32api.error, reg.list_keys, hive="HKLM", key="SOFTWARE\\Microsoft" + ) def test_list_values_existing(self): - ''' + """ Test the list_values function using a well known registry key - ''' + """ values = reg.list_values( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion') + hive="HKLM", key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion" + ) keys = [] for value in values: - keys.append(value['vname']) - self.assertIn('ProgramFilesDir', keys) + keys.append(value["vname"]) + self.assertIn("ProgramFilesDir", keys) def test_list_values_non_existing(self): - ''' + """ Test the list_values function using a non existing registry key - ''' - expected = (False, 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY)) - self.assertEqual(reg.list_values(hive='HKLM', key=FAKE_KEY), - expected) + """ + expected = (False, "Cannot find key: HKLM\\{0}".format(FAKE_KEY)) + self.assertEqual(reg.list_values(hive="HKLM", key=FAKE_KEY), expected) def test_list_values_invalid_hive(self): - ''' + """ Test the list_values function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - reg.list_values, - hive='BADHIVE', - key='SOFTWARE\\Microsoft') + """ + self.assertRaises( + CommandExecutionError, + reg.list_values, + hive="BADHIVE", + key="SOFTWARE\\Microsoft", + ) def test_list_values_unknown_key_error(self): - ''' + """ Tests the list_values function with an unknown key error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): - self.assertRaises(win32api.error, - reg.list_values, - hive='HKLM', - key='SOFTWARE\\Microsoft') + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): + self.assertRaises( + win32api.error, reg.list_values, hive="HKLM", key="SOFTWARE\\Microsoft" + ) def test_read_value_existing(self): - ''' + """ Test the read_value function using a well known registry value - ''' + """ ret = reg.read_value( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='ProgramFilesPath') - self.assertEqual(ret['vdata'], '%ProgramFiles%') + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="ProgramFilesPath", + ) + self.assertEqual(ret["vdata"], "%ProgramFiles%") def test_read_value_default(self): - ''' + """ Test the read_value function reading the default value using a well known registry key - ''' + """ ret = reg.read_value( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion') - self.assertEqual(ret['vdata'], '(value not set)') + hive="HKLM", key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion" + ) + self.assertEqual(ret["vdata"], "(value not set)") def test_read_value_non_existing(self): - ''' + """ Test the read_value function using a non existing value pair - ''' + """ expected = { - 'comment': 'Cannot find fake_name in HKLM\\SOFTWARE\\Microsoft\\' - 'Windows\\CurrentVersion', - 'vdata': None, - 'vname': 'fake_name', - 'success': False, - 'hive': 'HKLM', - 'key': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'} + "comment": "Cannot find fake_name in HKLM\\SOFTWARE\\Microsoft\\" + "Windows\\CurrentVersion", + "vdata": None, + "vname": "fake_name", + "success": False, + "hive": "HKLM", + "key": "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + } self.assertDictEqual( reg.read_value( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='fake_name'), - expected) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="fake_name", + ), + expected, + ) def test_read_value_non_existing_key(self): - ''' + """ Test the read_value function using a non existing registry key - ''' + """ expected = { - 'comment': 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY), - 'vdata': None, - 'vname': 'fake_name', - 'success': False, - 'hive': 'HKLM', - 'key': FAKE_KEY} + "comment": "Cannot find key: HKLM\\{0}".format(FAKE_KEY), + "vdata": None, + "vname": "fake_name", + "success": False, + "hive": "HKLM", + "key": FAKE_KEY, + } self.assertDictEqual( - reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name'), - expected) + reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_name"), expected + ) def test_read_value_invalid_hive(self): - ''' + """ Test the read_value function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - reg.read_value, - hive='BADHIVE', - key='SOFTWARE\\Microsoft', - vname='ProgramFilesPath') + """ + self.assertRaises( + CommandExecutionError, + reg.read_value, + hive="BADHIVE", + key="SOFTWARE\\Microsoft", + vname="ProgramFilesPath", + ) def test_read_value_unknown_key_error(self): - ''' + """ Tests the read_value function with an unknown key error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): self.assertRaises( win32api.error, reg.read_value, - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='ProgramFilesPath') + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="ProgramFilesPath", + ) def test_read_value_unknown_value_error(self): - ''' + """ Tests the read_value function with an unknown value error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegQueryValueEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegQueryValueEx', mock_error): + side_effect=win32api.error(123, "RegQueryValueEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegQueryValueEx", mock_error): self.assertRaises( win32api.error, reg.read_value, - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='ProgramFilesPath') + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="ProgramFilesPath", + ) @destructiveTest def test_read_value_multi_sz_empty_list(self): - ''' + """ An empty REG_MULTI_SZ value should return an empty list, not None - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', + hive="HKLM", key=FAKE_KEY, - vname='empty_list', + vname="empty_list", vdata=[], - vtype='REG_MULTI_SZ' + vtype="REG_MULTI_SZ", ) ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': [], - 'vname': 'empty_list', - 'vtype': 'REG_MULTI_SZ' + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": [], + "vname": "empty_list", + "vtype": "REG_MULTI_SZ", } self.assertEqual( - reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='empty_list', - ), - expected + reg.read_value(hive="HKLM", key=FAKE_KEY, vname="empty_list",), expected ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value(self): - ''' + """ Test the set_value function - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': 'fake_data', - 'vname': 'fake_name', - 'vtype': 'REG_SZ'} + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": "fake_data", + "vname": "fake_name", + "vtype": "REG_SZ", + } self.assertEqual( - reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name'), - expected) + reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_name"), expected + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_default(self): - ''' + """ Test the set_value function on the default value - ''' + """ try: self.assertTrue( - reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vdata='fake_default_data')) + reg.set_value(hive="HKLM", key=FAKE_KEY, vdata="fake_default_data") + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': 'fake_default_data', - 'vname': '(Default)', - 'vtype': 'REG_SZ'} - self.assertEqual( - reg.read_value( - hive='HKLM', - key=FAKE_KEY), - expected) + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": "fake_default_data", + "vname": "(Default)", + "vtype": "REG_SZ", + } + self.assertEqual(reg.read_value(hive="HKLM", key=FAKE_KEY), expected) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_unicode_key(self): - ''' + """ Test the set_value function on a unicode key - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name', - vdata='fake_value')) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + vdata="fake_value", + ) + ) expected = { - 'hive': 'HKLM', - 'key': '\\'.join([FAKE_KEY, UNICODE_KEY]), - 'success': True, - 'vdata': 'fake_value', - 'vname': 'fake_name', - 'vtype': 'REG_SZ'} + "hive": "HKLM", + "key": "\\".join([FAKE_KEY, UNICODE_KEY]), + "success": True, + "vdata": "fake_value", + "vname": "fake_name", + "vtype": "REG_SZ", + } self.assertEqual( reg.read_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name'), - expected) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + ), + expected, + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_unicode_value(self): - ''' + """ Test the set_value function on a unicode value - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_unicode', - vdata=UNICODE_VALUE)) + hive="HKLM", key=FAKE_KEY, vname="fake_unicode", vdata=UNICODE_VALUE + ) + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': UNICODE_VALUE, - 'vname': 'fake_unicode', - 'vtype': 'REG_SZ'} + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": UNICODE_VALUE, + "vname": "fake_unicode", + "vtype": "REG_SZ", + } self.assertEqual( - reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_unicode'), - expected) + reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_unicode"), + expected, + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_reg_dword(self): - ''' + """ Test the set_value function on a REG_DWORD value - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', + hive="HKLM", key=FAKE_KEY, - vname='dword_value', + vname="dword_value", vdata=123, - vtype='REG_DWORD')) + vtype="REG_DWORD", + ) + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': 123, - 'vname': 'dword_value', - 'vtype': 'REG_DWORD'} + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": 123, + "vname": "dword_value", + "vtype": "REG_DWORD", + } self.assertEqual( - reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='dword_value'), - expected) + reg.read_value(hive="HKLM", key=FAKE_KEY, vname="dword_value"), expected + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_reg_qword(self): - ''' + """ Test the set_value function on a REG_QWORD value - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', + hive="HKLM", key=FAKE_KEY, - vname='qword_value', + vname="qword_value", vdata=123, - vtype='REG_QWORD')) + vtype="REG_QWORD", + ) + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': 123, - 'vname': 'qword_value', - 'vtype': 'REG_QWORD'} + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": 123, + "vname": "qword_value", + "vtype": "REG_QWORD", + } self.assertEqual( - reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='qword_value'), - expected) + reg.read_value(hive="HKLM", key=FAKE_KEY, vname="qword_value"), expected + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) def test_set_value_invalid_hive(self): - ''' + """ Test the set_value function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - reg.set_value, - hive='BADHIVE', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data') + """ + self.assertRaises( + CommandExecutionError, + reg.set_value, + hive="BADHIVE", + key=FAKE_KEY, + vname="fake_name", + vdata="fake_data", + ) def test_set_value_open_create_failure(self): - ''' + """ Test the set_value function when there is a problem opening/creating the key - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegCreateKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegCreateKeyEx', mock_error): + side_effect=win32api.error(123, "RegCreateKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegCreateKeyEx", mock_error): self.assertFalse( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) def test_set_value_type_error(self): - ''' + """ Test the set_value function when the wrong type of data is passed - ''' - mock_error = MagicMock( - side_effect=TypeError('Mocked TypeError')) - with patch('salt.utils.win_reg.win32api.RegSetValueEx', mock_error): + """ + mock_error = MagicMock(side_effect=TypeError("Mocked TypeError")) + with patch("salt.utils.win_reg.win32api.RegSetValueEx", mock_error): self.assertFalse( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) def test_set_value_system_error(self): - ''' + """ Test the set_value function when a SystemError occurs while setting the value - ''' - mock_error = MagicMock( - side_effect=SystemError('Mocked SystemError')) - with patch('salt.utils.win_reg.win32api.RegSetValueEx', mock_error): + """ + mock_error = MagicMock(side_effect=SystemError("Mocked SystemError")) + with patch("salt.utils.win_reg.win32api.RegSetValueEx", mock_error): self.assertFalse( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) def test_set_value_value_error(self): - ''' + """ Test the set_value function when a ValueError occurs while setting the value - ''' - mock_error = MagicMock( - side_effect=ValueError('Mocked ValueError')) - with patch('salt.utils.win_reg.win32api.RegSetValueEx', mock_error): + """ + mock_error = MagicMock(side_effect=ValueError("Mocked ValueError")) + with patch("salt.utils.win_reg.win32api.RegSetValueEx", mock_error): self.assertFalse( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) @destructiveTest def test_delete_value(self): - ''' + """ Test the delete_value function - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) self.assertTrue( - reg.delete_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name')) + reg.delete_value(hive="HKLM", key=FAKE_KEY, vname="fake_name") + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) def test_delete_value_non_existing(self): - ''' + """ Test the delete_value function on non existing value - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(2, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + side_effect=win32api.error(2, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): self.assertIsNone( - reg.delete_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name')) + reg.delete_value(hive="HKLM", key=FAKE_KEY, vname="fake_name") + ) def test_delete_value_invalid_hive(self): - ''' + """ Test the delete_value function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - reg.delete_value, - hive='BADHIVE', - key=FAKE_KEY, - vname='fake_name') + """ + self.assertRaises( + CommandExecutionError, + reg.delete_value, + hive="BADHIVE", + key=FAKE_KEY, + vname="fake_name", + ) def test_delete_value_unknown_error(self): - ''' + """ Test the delete_value function when there is a problem opening the key - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): - self.assertRaises(win32api.error, - reg.delete_value, - hive='HKLM', - key=FAKE_KEY, - vname='fake_name') + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): + self.assertRaises( + win32api.error, + reg.delete_value, + hive="HKLM", + key=FAKE_KEY, + vname="fake_name", + ) @destructiveTest def test_delete_value_unicode(self): - ''' + """ Test the delete_value function on a unicode value - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_unicode', - vdata=UNICODE_VALUE)) + hive="HKLM", key=FAKE_KEY, vname="fake_unicode", vdata=UNICODE_VALUE + ) + ) self.assertTrue( - reg.delete_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_unicode')) + reg.delete_value(hive="HKLM", key=FAKE_KEY, vname="fake_unicode") + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_value_unicode_vname(self): - ''' + """ Test the delete_value function on a unicode vname - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname=UNICODE_KEY, - vdata='junk data')) + hive="HKLM", key=FAKE_KEY, vname=UNICODE_KEY, vdata="junk data" + ) + ) self.assertTrue( - reg.delete_value( - hive='HKLM', - key=FAKE_KEY, - vname=UNICODE_KEY)) + reg.delete_value(hive="HKLM", key=FAKE_KEY, vname=UNICODE_KEY) + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_value_unicode_key(self): - ''' + """ Test the delete_value function on a unicode key - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name', - vdata='junk data')) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + vdata="junk data", + ) + ) self.assertTrue( reg.delete_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name')) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + ) + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) def test_delete_key_recursive_invalid_hive(self): - ''' + """ Test the delete_key_recursive function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - reg.delete_key_recursive, - hive='BADHIVE', - key=FAKE_KEY) + """ + self.assertRaises( + CommandExecutionError, + reg.delete_key_recursive, + hive="BADHIVE", + key=FAKE_KEY, + ) def test_delete_key_recursive_key_not_found(self): - ''' + """ Test the delete_key_recursive function when the passed key to delete is not found. - ''' - self.assertFalse( - reg.key_exists(hive='HKLM', key=FAKE_KEY)) - self.assertFalse( - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY)) + """ + self.assertFalse(reg.key_exists(hive="HKLM", key=FAKE_KEY)) + self.assertFalse(reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)) def test_delete_key_recursive_too_close(self): - ''' + """ Test the delete_key_recursive function when the passed key to delete is too close to root, such as - ''' + """ mock_true = MagicMock(return_value=True) - with patch('salt.utils.win_reg.key_exists', mock_true): - self.assertFalse( - reg.delete_key_recursive(hive='HKLM', key='FAKE_KEY')) + with patch("salt.utils.win_reg.key_exists", mock_true): + self.assertFalse(reg.delete_key_recursive(hive="HKLM", key="FAKE_KEY")) @destructiveTest def test_delete_key_recursive(self): - ''' + """ Test the delete_key_recursive function - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_value')) - expected = { - 'Deleted': ['\\'.join(['HKLM', FAKE_KEY])], - 'Failed': []} + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_value" + ) + ) + expected = {"Deleted": ["\\".join(["HKLM", FAKE_KEY])], "Failed": []} self.assertDictEqual( - reg.delete_key_recursive( - hive='HKLM', - key=FAKE_KEY), - expected) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY), expected + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_key_recursive_failed_to_open_key(self): - ''' + """ Test the delete_key_recursive function on failure to open the key - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_value')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_value" + ) + ) expected = { - 'Deleted': [], - 'Failed': ['\\'.join(['HKLM', FAKE_KEY]) + - ' Failed to connect to key']} + "Deleted": [], + "Failed": ["\\".join(["HKLM", FAKE_KEY]) + " Failed to connect to key"], + } mock_true = MagicMock(return_value=True) mock_error = MagicMock( side_effect=[ 1, - win32api.error(3, 'RegOpenKeyEx', - 'Failed to connect to key') - ]) - with patch('salt.utils.win_reg.key_exists', mock_true), \ - patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + win32api.error(3, "RegOpenKeyEx", "Failed to connect to key"), + ] + ) + with patch("salt.utils.win_reg.key_exists", mock_true), patch( + "salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error + ): self.assertDictEqual( - reg.delete_key_recursive( - hive='HKLM', - key=FAKE_KEY), - expected) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY), expected + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_key_recursive_failed_to_delete(self): - ''' + """ Test the delete_key_recursive function on failure to delete a key - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_value')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_value" + ) + ) expected = { - 'Deleted': [], - 'Failed': ['\\'.join(['HKLM', FAKE_KEY]) + ' Unknown error']} - mock_error = MagicMock(side_effect=WindowsError('Unknown error')) # pylint: disable=undefined-variable - with patch('salt.utils.win_reg.win32api.RegDeleteKey', mock_error): + "Deleted": [], + "Failed": ["\\".join(["HKLM", FAKE_KEY]) + " Unknown error"], + } + # pylint: disable=undefined-variable + mock_error = MagicMock(side_effect=WindowsError("Unknown error")) + # pylint: enable=undefined-variable + with patch("salt.utils.win_reg.win32api.RegDeleteKey", mock_error): self.assertDictEqual( - reg.delete_key_recursive( - hive='HKLM', - key=FAKE_KEY), - expected) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY), expected + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_key_recursive_unicode(self): - ''' + """ Test the delete_key_recursive function on value within a unicode key - ''' + """ try: self.assertTrue( reg.set_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name', - vdata='fake_value')) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + vdata="fake_value", + ) + ) expected = { - 'Deleted': ['\\'.join(['HKLM', FAKE_KEY, UNICODE_KEY])], - 'Failed': []} + "Deleted": ["\\".join(["HKLM", FAKE_KEY, UNICODE_KEY])], + "Failed": [], + } self.assertDictEqual( reg.delete_key_recursive( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY])), - expected) + hive="HKLM", key="\\".join([FAKE_KEY, UNICODE_KEY]) + ), + expected, + ) finally: - reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) diff --git a/tests/unit/modules/test_restartcheck.py b/tests/unit/modules/test_restartcheck.py index f7b4417f72c..c42150583e3 100644 --- a/tests/unit/modules/test_restartcheck.py +++ b/tests/unit/modules/test_restartcheck.py @@ -1,252 +1,331 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`David Homolka <david.homolka@ultimum.io>` -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libsrestartcheck import salt.modules.restartcheck as restartcheck + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + # import salt.utils.files # from salt.exceptions import CommandExecutionError class RestartcheckTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.restartcheck - ''' + """ + def setup_loader_modules(self): return {restartcheck: {}} def test_kernel_versions_debian(self): - ''' + """ Test kernel version debian - ''' - mock = MagicMock(return_value=' Installed: 4.9.82-1+deb9u3') - with patch.dict(restartcheck.__grains__, {'os': 'Debian'}): - with patch.dict(restartcheck.__salt__, {'cmd.run': mock}): - self.assertListEqual(restartcheck._kernel_versions_debian(), ['4.9.82-1+deb9u3']) + """ + mock = MagicMock(return_value=" Installed: 4.9.82-1+deb9u3") + with patch.dict(restartcheck.__grains__, {"os": "Debian"}): + with patch.dict(restartcheck.__salt__, {"cmd.run": mock}): + self.assertListEqual( + restartcheck._kernel_versions_debian(), ["4.9.82-1+deb9u3"] + ) def test_kernel_versions_ubuntu(self): - ''' + """ Test kernel version ubuntu - ''' - mock = MagicMock(return_value=' Installed: 4.10.0-42.46') - with patch.dict(restartcheck.__grains__, {'os': 'Ubuntu'}): - with patch.dict(restartcheck.__salt__, {'cmd.run': mock}): - self.assertListEqual(restartcheck._kernel_versions_debian(), - ['4.10.0-42.46', '4.10.0-42-generic #46', '4.10.0-42-lowlatency #46']) + """ + mock = MagicMock(return_value=" Installed: 4.10.0-42.46") + with patch.dict(restartcheck.__grains__, {"os": "Ubuntu"}): + with patch.dict(restartcheck.__salt__, {"cmd.run": mock}): + self.assertListEqual( + restartcheck._kernel_versions_debian(), + [ + "4.10.0-42.46", + "4.10.0-42-generic #46", + "4.10.0-42-lowlatency #46", + ], + ) def test_kernel_versions_redhat(self): - ''' + """ Test if it return a data structure of the current, in-memory rules - ''' - mock = MagicMock(return_value='kernel-3.10.0-862.el7.x86_64 Thu Apr 5 00:40:00 2018') - with patch.dict(restartcheck.__salt__, {'cmd.run': mock}): - self.assertListEqual(restartcheck._kernel_versions_redhat(), ['3.10.0-862.el7.x86_64']) + """ + mock = MagicMock( + return_value="kernel-3.10.0-862.el7.x86_64 Thu Apr 5 00:40:00 2018" + ) + with patch.dict(restartcheck.__salt__, {"cmd.run": mock}): + self.assertListEqual( + restartcheck._kernel_versions_redhat(), ["3.10.0-862.el7.x86_64"] + ) def test_valid_deleted_file_deleted(self): - ''' + """ Test (deleted) file - ''' - self.assertTrue(restartcheck._valid_deleted_file('/usr/lib/test (deleted)')) + """ + self.assertTrue(restartcheck._valid_deleted_file("/usr/lib/test (deleted)")) def test_valid_deleted_file_psth_inode(self): - ''' + """ Test (path inode=1) file - ''' - self.assertTrue(restartcheck._valid_deleted_file('/usr/lib/test (path inode=1)')) + """ + self.assertTrue( + restartcheck._valid_deleted_file("/usr/lib/test (path inode=1)") + ) def test_valid_deleted_file_var_log(self): - ''' + """ Test /var/log/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/log/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/log/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/log/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/var/log/test")) + self.assertFalse(restartcheck._valid_deleted_file("/var/log/test (deleted)")) + self.assertFalse( + restartcheck._valid_deleted_file("/var/log/test (path inode=1)") + ) def test_valid_deleted_file_var_local_log(self): - ''' + """ Test /var/local/log/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/local/log/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/local/log/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/local/log/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/var/local/log/test")) + self.assertFalse( + restartcheck._valid_deleted_file("/var/local/log/test (deleted)") + ) + self.assertFalse( + restartcheck._valid_deleted_file("/var/local/log/test (path inode=1)") + ) def test_valid_deleted_file_var_run(self): - ''' + """ Test /var/run/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/run/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/run/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/run/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/var/run/test")) + self.assertFalse(restartcheck._valid_deleted_file("/var/run/test (deleted)")) + self.assertFalse( + restartcheck._valid_deleted_file("/var/run/test (path inode=1)") + ) def test_valid_deleted_file_var_local_run(self): - ''' + """ Test /var/local/run/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/local/run/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/local/run/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/local/run/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/var/local/run/test")) + self.assertFalse( + restartcheck._valid_deleted_file("/var/local/run/test (deleted)") + ) + self.assertFalse( + restartcheck._valid_deleted_file("/var/local/run/test (path inode=1)") + ) def test_valid_deleted_file_tmp(self): - ''' + """ Test /tmp/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/tmp/test')) - self.assertFalse(restartcheck._valid_deleted_file('/tmp/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/tmp/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/tmp/test")) + self.assertFalse(restartcheck._valid_deleted_file("/tmp/test (deleted)")) + self.assertFalse(restartcheck._valid_deleted_file("/tmp/test (path inode=1)")) def test_valid_deleted_file_dev_shm(self): - ''' + """ Test /dev/shm/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/dev/shm/test')) - self.assertFalse(restartcheck._valid_deleted_file('/dev/shm/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/dev/shm/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/dev/shm/test")) + self.assertFalse(restartcheck._valid_deleted_file("/dev/shm/test (deleted)")) + self.assertFalse( + restartcheck._valid_deleted_file("/dev/shm/test (path inode=1)") + ) def test_valid_deleted_file_run(self): - ''' + """ Test /run/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/run/test')) - self.assertFalse(restartcheck._valid_deleted_file('/run/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/run/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/run/test")) + self.assertFalse(restartcheck._valid_deleted_file("/run/test (deleted)")) + self.assertFalse(restartcheck._valid_deleted_file("/run/test (path inode=1)")) def test_valid_deleted_file_drm(self): - ''' + """ Test /drm/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/drm/test')) - self.assertFalse(restartcheck._valid_deleted_file('/drm/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/drm/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/drm/test")) + self.assertFalse(restartcheck._valid_deleted_file("/drm/test (deleted)")) + self.assertFalse(restartcheck._valid_deleted_file("/drm/test (path inode=1)")) def test_valid_deleted_file_var_tmp(self): - ''' + """ Test /var/tmp/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/tmp/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/tmp/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/tmp/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/var/tmp/test")) + self.assertFalse(restartcheck._valid_deleted_file("/var/tmp/test (deleted)")) + self.assertFalse( + restartcheck._valid_deleted_file("/var/tmp/test (path inode=1)") + ) def test_valid_deleted_file_var_local_tmp(self): - ''' + """ Test /var/local/tmp/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/local/tmp/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/local/tmp/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/local/tmp/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/var/local/tmp/test")) + self.assertFalse( + restartcheck._valid_deleted_file("/var/local/tmp/test (deleted)") + ) + self.assertFalse( + restartcheck._valid_deleted_file("/var/local/tmp/test (path inode=1)") + ) def test_valid_deleted_file_dev_zero(self): - ''' + """ Test /dev/zero/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/dev/zero/test')) - self.assertFalse(restartcheck._valid_deleted_file('/dev/zero/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/dev/zero/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/dev/zero/test")) + self.assertFalse(restartcheck._valid_deleted_file("/dev/zero/test (deleted)")) + self.assertFalse( + restartcheck._valid_deleted_file("/dev/zero/test (path inode=1)") + ) def test_valid_deleted_file_dev_pts(self): - ''' + """ Test /dev/pts/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/dev/pts/test')) - self.assertFalse(restartcheck._valid_deleted_file('/dev/pts/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/dev/pts/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/dev/pts/test")) + self.assertFalse(restartcheck._valid_deleted_file("/dev/pts/test (deleted)")) + self.assertFalse( + restartcheck._valid_deleted_file("/dev/pts/test (path inode=1)") + ) def test_valid_deleted_file_usr_lib_locale(self): - ''' + """ Test /usr/lib/locale/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/usr/lib/locale/test')) - self.assertFalse(restartcheck._valid_deleted_file('/usr/lib/locale/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/usr/lib/locale/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/usr/lib/locale/test")) + self.assertFalse( + restartcheck._valid_deleted_file("/usr/lib/locale/test (deleted)") + ) + self.assertFalse( + restartcheck._valid_deleted_file("/usr/lib/locale/test (path inode=1)") + ) def test_valid_deleted_file_home(self): - ''' + """ Test /home/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/home/test')) - self.assertFalse(restartcheck._valid_deleted_file('/home/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/home/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/home/test")) + self.assertFalse(restartcheck._valid_deleted_file("/home/test (deleted)")) + self.assertFalse(restartcheck._valid_deleted_file("/home/test (path inode=1)")) def test_valid_deleted_file_icon_theme_cache(self): - ''' + """ Test /test.icon-theme.cache - ''' - self.assertFalse(restartcheck._valid_deleted_file('/dev/test.icon-theme.cache')) - self.assertFalse(restartcheck._valid_deleted_file('/dev/test.icon-theme.cache (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/dev/test.icon-theme.cache (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/dev/test.icon-theme.cache")) + self.assertFalse( + restartcheck._valid_deleted_file("/dev/test.icon-theme.cache (deleted)") + ) + self.assertFalse( + restartcheck._valid_deleted_file( + "/dev/test.icon-theme.cache (path inode=1)" + ) + ) def test_valid_deleted_file_var_cache_fontconfig(self): - ''' + """ Test /var/cache/fontconfig/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/cache/fontconfig/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/cache/fontconfig/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/cache/fontconfig/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/var/cache/fontconfig/test")) + self.assertFalse( + restartcheck._valid_deleted_file("/var/cache/fontconfig/test (deleted)") + ) + self.assertFalse( + restartcheck._valid_deleted_file( + "/var/cache/fontconfig/test (path inode=1)" + ) + ) def test_valid_deleted_file_var_lib_nagios3_spool(self): - ''' + """ Test /var/lib/nagios3/spool/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/nagios3/spool/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/nagios3/spool/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/nagios3/spool/test (path inode=1)')) + """ + self.assertFalse( + restartcheck._valid_deleted_file("/var/lib/nagios3/spool/test") + ) + self.assertFalse( + restartcheck._valid_deleted_file("/var/lib/nagios3/spool/test (deleted)") + ) + self.assertFalse( + restartcheck._valid_deleted_file( + "/var/lib/nagios3/spool/test (path inode=1)" + ) + ) def test_valid_deleted_file_var_lib_nagios3_spool_checkresults(self): - ''' + """ Test /var/lib/nagios3/spool/checkresults/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/nagios3/spool/checkresults/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/nagios3/spool/checkresults/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/nagios3/spool/checkresults/test (path inode=1)')) + """ + self.assertFalse( + restartcheck._valid_deleted_file("/var/lib/nagios3/spool/checkresults/test") + ) + self.assertFalse( + restartcheck._valid_deleted_file( + "/var/lib/nagios3/spool/checkresults/test (deleted)" + ) + ) + self.assertFalse( + restartcheck._valid_deleted_file( + "/var/lib/nagios3/spool/checkresults/test (path inode=1)" + ) + ) def test_valid_deleted_file_var_lib_postgresql(self): - ''' + """ Test /var/lib/postgresql/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/postgresql/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/postgresql/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/postgresql/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/var/lib/postgresql/test")) + self.assertFalse( + restartcheck._valid_deleted_file("/var/lib/postgresql/test (deleted)") + ) + self.assertFalse( + restartcheck._valid_deleted_file("/var/lib/postgresql/test (path inode=1)") + ) def test_valid_deleted_file_var_lib_vdr(self): - ''' + """ Test /var/lib/vdr/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/vdr/test')) - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/vdr/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/var/lib/vdr/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/var/lib/vdr/test")) + self.assertFalse( + restartcheck._valid_deleted_file("/var/lib/vdr/test (deleted)") + ) + self.assertFalse( + restartcheck._valid_deleted_file("/var/lib/vdr/test (path inode=1)") + ) def test_valid_deleted_file_aio(self): - ''' + """ Test /[aio]/ - ''' - self.assertFalse(restartcheck._valid_deleted_file('/opt/test')) - self.assertFalse(restartcheck._valid_deleted_file('/opt/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/opt/test (path inode=1)')) - self.assertFalse(restartcheck._valid_deleted_file('/apt/test')) - self.assertFalse(restartcheck._valid_deleted_file('/apt/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/apt/test (path inode=1)')) - self.assertFalse(restartcheck._valid_deleted_file('/ipt/test')) - self.assertFalse(restartcheck._valid_deleted_file('/ipt/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/ipt/test (path inode=1)')) - self.assertFalse(restartcheck._valid_deleted_file('/aio/test')) - self.assertFalse(restartcheck._valid_deleted_file('/aio/test (deleted)')) - self.assertFalse(restartcheck._valid_deleted_file('/aio/test (path inode=1)')) + """ + self.assertFalse(restartcheck._valid_deleted_file("/opt/test")) + self.assertFalse(restartcheck._valid_deleted_file("/opt/test (deleted)")) + self.assertFalse(restartcheck._valid_deleted_file("/opt/test (path inode=1)")) + self.assertFalse(restartcheck._valid_deleted_file("/apt/test")) + self.assertFalse(restartcheck._valid_deleted_file("/apt/test (deleted)")) + self.assertFalse(restartcheck._valid_deleted_file("/apt/test (path inode=1)")) + self.assertFalse(restartcheck._valid_deleted_file("/ipt/test")) + self.assertFalse(restartcheck._valid_deleted_file("/ipt/test (deleted)")) + self.assertFalse(restartcheck._valid_deleted_file("/ipt/test (path inode=1)")) + self.assertFalse(restartcheck._valid_deleted_file("/aio/test")) + self.assertFalse(restartcheck._valid_deleted_file("/aio/test (deleted)")) + self.assertFalse(restartcheck._valid_deleted_file("/aio/test (path inode=1)")) def test_valid_deleted_file_sysv(self): - ''' + """ Test /SYSV/ - ''' - 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)')) + """ + 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)")) diff --git a/tests/unit/modules/test_ret.py b/tests/unit/modules/test_ret.py index 7103ca04593..4e9aa46ad70 100644 --- a/tests/unit/modules/test_ret.py +++ b/tests/unit/modules/test_ret.py @@ -1,72 +1,82 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.loader # Import Salt Libs import salt.modules.ret as ret -import salt.loader + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class RetTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.ret - ''' + """ + def setup_loader_modules(self): return {ret: {}} # 'get_jid' function tests: 1 def test_get_jid(self): - ''' + """ Test if it return the information for a specified job id - ''' - mock_ret = MagicMock(return_value='DB') - with patch.object(salt.loader, 'returners', - MagicMock(return_value={'redis.get_jid': mock_ret})): - self.assertEqual(ret.get_jid('redis', 'net'), 'DB') + """ + mock_ret = MagicMock(return_value="DB") + with patch.object( + salt.loader, + "returners", + MagicMock(return_value={"redis.get_jid": mock_ret}), + ): + self.assertEqual(ret.get_jid("redis", "net"), "DB") # 'get_fun' function tests: 1 def test_get_fun(self): - ''' + """ Test if it return info about last time fun was called on each minion - ''' - mock_ret = MagicMock(return_value='DB') - with patch.object(salt.loader, 'returners', - MagicMock(return_value={'mysql.get_fun': mock_ret})): - self.assertEqual(ret.get_fun('mysql', 'net'), 'DB') + """ + mock_ret = MagicMock(return_value="DB") + with patch.object( + salt.loader, + "returners", + MagicMock(return_value={"mysql.get_fun": mock_ret}), + ): + self.assertEqual(ret.get_fun("mysql", "net"), "DB") # 'get_jids' function tests: 1 def test_get_jids(self): - ''' + """ Test if it return a list of all job ids - ''' - mock_ret = MagicMock(return_value='DB') - with patch.object(salt.loader, 'returners', - MagicMock(return_value={'mysql.get_jids': mock_ret})): - self.assertEqual(ret.get_jids('mysql'), 'DB') + """ + mock_ret = MagicMock(return_value="DB") + with patch.object( + salt.loader, + "returners", + MagicMock(return_value={"mysql.get_jids": mock_ret}), + ): + self.assertEqual(ret.get_jids("mysql"), "DB") # 'get_minions' function tests: 1 def test_get_minions(self): - ''' + """ Test if it return a list of all minions - ''' - mock_ret = MagicMock(return_value='DB') - with patch.object(salt.loader, 'returners', - MagicMock(return_value= - {'mysql.get_minions': mock_ret})): - self.assertEqual(ret.get_minions('mysql'), 'DB') + """ + mock_ret = MagicMock(return_value="DB") + with patch.object( + salt.loader, + "returners", + MagicMock(return_value={"mysql.get_minions": mock_ret}), + ): + self.assertEqual(ret.get_minions("mysql"), "DB") diff --git a/tests/unit/modules/test_rh_ip.py b/tests/unit/modules/test_rh_ip.py index b1c6583cff5..d65e4c3028e 100644 --- a/tests/unit/modules/test_rh_ip.py +++ b/tests/unit/modules/test_rh_ip.py @@ -1,237 +1,286 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -# Import Salt Libs -from salt.ext.six.moves import range -import salt.modules.rh_ip as rh_ip - # Import 3rd-party libs import jinja2.exceptions +import salt.modules.rh_ip as rh_ip from salt.ext import six +# Import Salt Libs +from salt.ext.six.moves import range + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RhipTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.rh_ip - ''' + """ + def setup_loader_modules(self): return {rh_ip: {}} def test_error_message_iface_should_process_non_str_expected(self): - values = [1, True, False, 'no-kaboom'] - iface = 'ethtest' - option = 'test' + values = [1, True, False, "no-kaboom"] + iface = "ethtest" + option = "test" msg = rh_ip._error_msg_iface(iface, option, values) - self.assertTrue(msg.endswith('[1|True|False|no-kaboom]'), msg) + self.assertTrue(msg.endswith("[1|True|False|no-kaboom]"), msg) def test_error_message_network_should_process_non_str_expected(self): - values = [1, True, False, 'no-kaboom'] - msg = rh_ip._error_msg_network('fnord', values) - self.assertTrue(msg.endswith('[1|True|False|no-kaboom]'), msg) + values = [1, True, False, "no-kaboom"] + msg = rh_ip._error_msg_network("fnord", values) + self.assertTrue(msg.endswith("[1|True|False|no-kaboom]"), msg) def test_build_bond(self): - ''' + """ Test to create a bond script in /etc/modprobe.d with the passed settings and load the bonding kernel module. - ''' - with patch.dict(rh_ip.__grains__, {'osrelease': 'osrelease'}): - with patch.object(rh_ip, '_parse_settings_bond', MagicMock()): - mock = jinja2.exceptions.TemplateNotFound('foo') - with patch.object(jinja2.Environment, 'get_template', - MagicMock(side_effect=mock)): - self.assertEqual(rh_ip.build_bond('iface'), '') + """ + with patch.dict(rh_ip.__grains__, {"osrelease": "osrelease"}): + with patch.object(rh_ip, "_parse_settings_bond", MagicMock()): + mock = jinja2.exceptions.TemplateNotFound("foo") + with patch.object( + jinja2.Environment, "get_template", MagicMock(side_effect=mock) + ): + self.assertEqual(rh_ip.build_bond("iface"), "") - with patch.dict(rh_ip.__salt__, {'kmod.load': - MagicMock(return_value=None)}): - with patch.object(rh_ip, '_write_file_iface', - return_value=None): - with patch.object(rh_ip, '_read_temp', return_value='A'): - self.assertEqual(rh_ip.build_bond('iface', test='A'), - 'A') + with patch.dict( + rh_ip.__salt__, {"kmod.load": MagicMock(return_value=None)} + ): + with patch.object(rh_ip, "_write_file_iface", return_value=None): + with patch.object(rh_ip, "_read_temp", return_value="A"): + self.assertEqual(rh_ip.build_bond("iface", test="A"), "A") - with patch.object(rh_ip, '_read_file', return_value='A'): - self.assertEqual(rh_ip.build_bond('iface', test=None), - 'A') + with patch.object(rh_ip, "_read_file", return_value="A"): + self.assertEqual(rh_ip.build_bond("iface", test=None), "A") def test_build_interface(self): - ''' + """ Test to build an interface script for a network interface. - ''' - with patch.dict(rh_ip.__grains__, {'os': 'Fedora', 'osmajorrelease': 26}): - with patch.object(rh_ip, '_raise_error_iface', return_value=None): - self.assertRaises(AttributeError, - rh_ip.build_interface, - 'iface', 'slave', True) + """ + with patch.dict(rh_ip.__grains__, {"os": "Fedora", "osmajorrelease": 26}): + with patch.object(rh_ip, "_raise_error_iface", return_value=None): + self.assertRaises( + AttributeError, rh_ip.build_interface, "iface", "slave", True + ) - with patch.dict(rh_ip.__salt__, {'network.interfaces': lambda: {'eth': True}}): - self.assertRaises(AttributeError, - rh_ip.build_interface, - 'iface', 'eth', True, netmask='255.255.255.255', prefix=32, - test=True) - self.assertRaises(AttributeError, - rh_ip.build_interface, - 'iface', 'eth', True, ipaddrs=['A'], - test=True) - self.assertRaises(AttributeError, - rh_ip.build_interface, - 'iface', 'eth', True, ipv6addrs=['A'], - test=True) + with patch.dict( + rh_ip.__salt__, {"network.interfaces": lambda: {"eth": True}} + ): + self.assertRaises( + AttributeError, + rh_ip.build_interface, + "iface", + "eth", + True, + netmask="255.255.255.255", + prefix=32, + test=True, + ) + self.assertRaises( + AttributeError, + rh_ip.build_interface, + "iface", + "eth", + True, + ipaddrs=["A"], + test=True, + ) + self.assertRaises( + AttributeError, + rh_ip.build_interface, + "iface", + "eth", + True, + ipv6addrs=["A"], + test=True, + ) for osrelease in range(5, 8): - with patch.dict(rh_ip.__grains__, {'os': 'RedHat', 'osrelease': six.text_type(osrelease)}): - with patch.object(rh_ip, '_raise_error_iface', return_value=None): - with patch.object(rh_ip, '_parse_settings_bond', MagicMock()): - mock = jinja2.exceptions.TemplateNotFound('foo') - with patch.object(jinja2.Environment, - 'get_template', - MagicMock(side_effect=mock)): - self.assertEqual(rh_ip.build_interface('iface', - 'vlan', - True), '') + with patch.dict( + rh_ip.__grains__, + {"os": "RedHat", "osrelease": six.text_type(osrelease)}, + ): + with patch.object(rh_ip, "_raise_error_iface", return_value=None): + with patch.object(rh_ip, "_parse_settings_bond", MagicMock()): + mock = jinja2.exceptions.TemplateNotFound("foo") + with patch.object( + jinja2.Environment, + "get_template", + MagicMock(side_effect=mock), + ): + self.assertEqual( + rh_ip.build_interface("iface", "vlan", True), "" + ) - with patch.object(rh_ip, '_read_temp', return_value='A'): - with patch.object(jinja2.Environment, - 'get_template', MagicMock()): - self.assertEqual(rh_ip.build_interface('iface', - 'vlan', - True, - test='A'), - 'A') + with patch.object(rh_ip, "_read_temp", return_value="A"): + with patch.object( + jinja2.Environment, "get_template", MagicMock() + ): + self.assertEqual( + rh_ip.build_interface( + "iface", "vlan", True, test="A" + ), + "A", + ) - with patch.object(rh_ip, '_write_file_iface', - return_value=None): - with patch.object(os.path, 'join', - return_value='A'): - with patch.object(rh_ip, '_read_file', - return_value='A'): - self.assertEqual(rh_ip.build_interface - ('iface', 'vlan', - True), 'A') + with patch.object( + rh_ip, "_write_file_iface", return_value=None + ): + with patch.object( + os.path, "join", return_value="A" + ): + with patch.object( + rh_ip, "_read_file", return_value="A" + ): + self.assertEqual( + rh_ip.build_interface( + "iface", "vlan", True + ), + "A", + ) if osrelease > 6: - with patch.dict(rh_ip.__salt__, {'network.interfaces': lambda: {'eth': True}}): - self.assertEqual(rh_ip.build_interface - ('iface', 'eth', True, - ipaddrs=['127.0.0.1/8']), 'A') - self.assertEqual(rh_ip.build_interface - ('iface', 'eth', True, - ipv6addrs=['fc00::1/128']), 'A') + with patch.dict( + rh_ip.__salt__, + { + "network.interfaces": lambda: { + "eth": True + } + }, + ): + self.assertEqual( + rh_ip.build_interface( + "iface", + "eth", + True, + ipaddrs=["127.0.0.1/8"], + ), + "A", + ) + self.assertEqual( + rh_ip.build_interface( + "iface", + "eth", + True, + ipv6addrs=["fc00::1/128"], + ), + "A", + ) def test_build_routes(self): - ''' + """ Test to build a route script for a network interface. - ''' - with patch.dict(rh_ip.__grains__, {'osrelease': '5.0'}): - with patch.object(rh_ip, '_parse_routes', MagicMock()): - mock = jinja2.exceptions.TemplateNotFound('foo') - with patch.object(jinja2.Environment, - 'get_template', MagicMock(side_effect=mock)): - self.assertEqual(rh_ip.build_routes('iface'), '') + """ + with patch.dict(rh_ip.__grains__, {"osrelease": "5.0"}): + with patch.object(rh_ip, "_parse_routes", MagicMock()): + mock = jinja2.exceptions.TemplateNotFound("foo") + with patch.object( + jinja2.Environment, "get_template", MagicMock(side_effect=mock) + ): + self.assertEqual(rh_ip.build_routes("iface"), "") - with patch.object(jinja2.Environment, - 'get_template', MagicMock()): - with patch.object(rh_ip, '_read_temp', return_value=['A']): - self.assertEqual(rh_ip.build_routes('i', test='t'), ['A', 'A']) + with patch.object(jinja2.Environment, "get_template", MagicMock()): + with patch.object(rh_ip, "_read_temp", return_value=["A"]): + self.assertEqual(rh_ip.build_routes("i", test="t"), ["A", "A"]) - with patch.object(rh_ip, '_read_file', return_value=['A']): - with patch.object(os.path, 'join', return_value='A'): - with patch.object(rh_ip, '_write_file_iface', - return_value=None): - self.assertEqual(rh_ip.build_routes('i', - test=None), - ['A', 'A']) + with patch.object(rh_ip, "_read_file", return_value=["A"]): + with patch.object(os.path, "join", return_value="A"): + with patch.object( + rh_ip, "_write_file_iface", return_value=None + ): + self.assertEqual( + rh_ip.build_routes("i", test=None), ["A", "A"] + ) def test_down(self): - ''' + """ Test to shutdown a network interface - ''' - with patch.dict(rh_ip.__salt__, {'cmd.run': - MagicMock(return_value='A')}): - self.assertEqual(rh_ip.down('iface', 'iface_type'), 'A') + """ + with patch.dict(rh_ip.__salt__, {"cmd.run": MagicMock(return_value="A")}): + self.assertEqual(rh_ip.down("iface", "iface_type"), "A") - self.assertEqual(rh_ip.down('iface', 'slave'), None) + self.assertEqual(rh_ip.down("iface", "slave"), None) def test_get_bond(self): - ''' + """ Test to return the content of a bond script - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(rh_ip, '_read_file', return_value='A'): - self.assertEqual(rh_ip.get_bond('iface'), 'A') + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(rh_ip, "_read_file", return_value="A"): + self.assertEqual(rh_ip.get_bond("iface"), "A") def test_get_interface(self): - ''' + """ Test to return the contents of an interface script - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(rh_ip, '_read_file', return_value='A'): - self.assertEqual(rh_ip.get_interface('iface'), 'A') + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(rh_ip, "_read_file", return_value="A"): + self.assertEqual(rh_ip.get_interface("iface"), "A") def test_up(self): - ''' + """ Test to start up a network interface - ''' - with patch.dict(rh_ip.__salt__, {'cmd.run': - MagicMock(return_value='A')}): - self.assertEqual(rh_ip.up('iface', 'iface_type'), 'A') + """ + with patch.dict(rh_ip.__salt__, {"cmd.run": MagicMock(return_value="A")}): + self.assertEqual(rh_ip.up("iface", "iface_type"), "A") - self.assertEqual(rh_ip.up('iface', 'slave'), None) + self.assertEqual(rh_ip.up("iface", "slave"), None) def test_get_routes(self): - ''' + """ Test to return the contents of the interface routes script. - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(rh_ip, '_read_file', return_value=['A']): - self.assertEqual(rh_ip.get_routes('iface'), ['A', 'A']) + """ + with patch.object(os.path, "join", return_value="A"): + with patch.object(rh_ip, "_read_file", return_value=["A"]): + self.assertEqual(rh_ip.get_routes("iface"), ["A", "A"]) def test_get_network_settings(self): - ''' + """ Test to return the contents of the global network script. - ''' - with patch.object(rh_ip, '_read_file', return_value='A'): - self.assertEqual(rh_ip.get_network_settings(), 'A') + """ + with patch.object(rh_ip, "_read_file", return_value="A"): + self.assertEqual(rh_ip.get_network_settings(), "A") def test_apply_network_settings(self): - ''' + """ Test to apply global network configuration. - ''' - with patch.dict(rh_ip.__salt__, {'service.restart': - MagicMock(return_value=True)}): + """ + with patch.dict( + rh_ip.__salt__, {"service.restart": MagicMock(return_value=True)} + ): self.assertTrue(rh_ip.apply_network_settings()) def test_build_network_settings(self): - ''' + """ Test to build the global network script. - ''' - with patch.object(rh_ip, '_parse_rh_config', MagicMock()): - with patch.object(rh_ip, '_parse_network_settings', MagicMock()): + """ + with patch.object(rh_ip, "_parse_rh_config", MagicMock()): + with patch.object(rh_ip, "_parse_network_settings", MagicMock()): - mock = jinja2.exceptions.TemplateNotFound('foo') - with patch.object(jinja2.Environment, - 'get_template', MagicMock(side_effect=mock)): - self.assertEqual(rh_ip.build_network_settings(), '') + mock = jinja2.exceptions.TemplateNotFound("foo") + with patch.object( + jinja2.Environment, "get_template", MagicMock(side_effect=mock) + ): + self.assertEqual(rh_ip.build_network_settings(), "") - with patch.object(jinja2.Environment, - 'get_template', MagicMock()): - with patch.object(rh_ip, '_read_temp', return_value='A'): - self.assertEqual(rh_ip.build_network_settings - (test='t'), 'A') + with patch.object(jinja2.Environment, "get_template", MagicMock()): + with patch.object(rh_ip, "_read_temp", return_value="A"): + self.assertEqual(rh_ip.build_network_settings(test="t"), "A") - with patch.object(rh_ip, '_write_file_network', - return_value=None): - with patch.object(rh_ip, '_read_file', - return_value='A'): - self.assertEqual(rh_ip.build_network_settings - (test=None), 'A') + with patch.object( + rh_ip, "_write_file_network", return_value=None + ): + with patch.object(rh_ip, "_read_file", return_value="A"): + self.assertEqual( + rh_ip.build_network_settings(test=None), "A" + ) diff --git a/tests/unit/modules/test_rh_service.py b/tests/unit/modules/test_rh_service.py index dab0e9d9e2c..5870357b985 100644 --- a/tests/unit/modules/test_rh_service.py +++ b/tests/unit/modules/test_rh_service.py @@ -1,87 +1,98 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import textwrap +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import textwrap # Import Salt Libs import salt.modules.rh_service as rh_service -RET = ['hostname', 'mountall', 'network-interface', 'network-manager', - 'salt-api', 'salt-master', 'salt-minion'] +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +RET = [ + "hostname", + "mountall", + "network-interface", + "network-manager", + "salt-api", + "salt-master", + "salt-minion", +] class RhServiceTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.rh_service - ''' + """ def setup_loader_modules(self): return { rh_service: { - '_upstart_disable': None, - '_upstart_enable': None, - '_upstart_is_enabled': None + "_upstart_disable": None, + "_upstart_enable": None, + "_upstart_is_enabled": None, } } @staticmethod def _m_lst(): - ''' + """ Return value for []. - ''' + """ return MagicMock(return_value=[]) @staticmethod def _m_ret(): - ''' + """ Return value for RET. - ''' + """ return MagicMock(return_value=RET) @staticmethod def _m_bool(bol=True): - ''' + """ Return Bool value. - ''' + """ return MagicMock(return_value=bol) def test__chkconfig_is_enabled(self): - ''' + """ test _chkconfig_is_enabled function - ''' - name = 'atd' - chkconfig_out = textwrap.dedent('''\ + """ + name = "atd" + chkconfig_out = textwrap.dedent( + """\ {0} 0:off 1:off 2:off 3:on 4:on 5:on 6:off - '''.format(name)) - xinetd_out = textwrap.dedent('''\ + """.format( + name + ) + ) + xinetd_out = textwrap.dedent( + """\ xinetd based services: {0} on - '''.format(name)) + """.format( + name + ) + ) - with patch.object(rh_service, '_runlevel', MagicMock(return_value=3)): - mock_run = MagicMock(return_value={'retcode': 0, - 'stdout': chkconfig_out}) - with patch.dict(rh_service.__salt__, {'cmd.run_all': mock_run}): + with patch.object(rh_service, "_runlevel", MagicMock(return_value=3)): + mock_run = MagicMock(return_value={"retcode": 0, "stdout": chkconfig_out}) + with patch.dict(rh_service.__salt__, {"cmd.run_all": mock_run}): self.assertTrue(rh_service._chkconfig_is_enabled(name)) self.assertFalse(rh_service._chkconfig_is_enabled(name, 2)) self.assertTrue(rh_service._chkconfig_is_enabled(name, 3)) - mock_run = MagicMock(return_value={'retcode': 0, - 'stdout': xinetd_out}) - with patch.dict(rh_service.__salt__, {'cmd.run_all': mock_run}): + mock_run = MagicMock(return_value={"retcode": 0, "stdout": xinetd_out}) + with patch.dict(rh_service.__salt__, {"cmd.run_all": mock_run}): self.assertTrue(rh_service._chkconfig_is_enabled(name)) self.assertTrue(rh_service._chkconfig_is_enabled(name, 2)) self.assertTrue(rh_service._chkconfig_is_enabled(name, 3)) @@ -89,230 +100,242 @@ class RhServiceTestCase(TestCase, LoaderModuleMockMixin): # 'get_enabled' function tests: 1 def test_get_enabled(self): - ''' + """ Test if it return the enabled services. Use the ``limit`` param to restrict results to services of that type. - ''' - with patch.object(rh_service, '_upstart_services', self._m_ret()): - with patch.object(rh_service, '_upstart_is_enabled', MagicMock(return_value=False)): - self.assertListEqual(rh_service.get_enabled('upstart'), []) + """ + with patch.object(rh_service, "_upstart_services", self._m_ret()): + with patch.object( + rh_service, "_upstart_is_enabled", MagicMock(return_value=False) + ): + self.assertListEqual(rh_service.get_enabled("upstart"), []) - mock_run = MagicMock(return_value='salt stack') - with patch.dict(rh_service.__salt__, {'cmd.run': mock_run}): - with patch.object(rh_service, '_sysv_services', self._m_ret()): - with patch.object(rh_service, '_sysv_is_enabled', - self._m_bool()): - self.assertListEqual(rh_service.get_enabled('sysvinit'), - RET) + mock_run = MagicMock(return_value="salt stack") + with patch.dict(rh_service.__salt__, {"cmd.run": mock_run}): + with patch.object(rh_service, "_sysv_services", self._m_ret()): + with patch.object(rh_service, "_sysv_is_enabled", self._m_bool()): + self.assertListEqual(rh_service.get_enabled("sysvinit"), RET) - with patch.object(rh_service, '_upstart_services', self._m_lst()): - with patch.object(rh_service, '_upstart_is_enabled', MagicMock(return_value=True)): + with patch.object(rh_service, "_upstart_services", self._m_lst()): + with patch.object( + rh_service, + "_upstart_is_enabled", + MagicMock(return_value=True), + ): self.assertListEqual(rh_service.get_enabled(), RET) # 'get_disabled' function tests: 1 def test_get_disabled(self): - ''' + """ Test if it return the disabled services. Use the ``limit`` param to restrict results to services of that type. - ''' - with patch.object(rh_service, '_upstart_services', self._m_ret()): - with patch.object(rh_service, '_upstart_is_enabled', MagicMock(return_value=False)): - self.assertListEqual(rh_service.get_disabled('upstart'), RET) + """ + with patch.object(rh_service, "_upstart_services", self._m_ret()): + with patch.object( + rh_service, "_upstart_is_enabled", MagicMock(return_value=False) + ): + self.assertListEqual(rh_service.get_disabled("upstart"), RET) - mock_run = MagicMock(return_value='salt stack') - with patch.dict(rh_service.__salt__, {'cmd.run': mock_run}): - with patch.object(rh_service, '_sysv_services', self._m_ret()): - with patch.object(rh_service, '_sysv_is_enabled', - self._m_bool(False)): - self.assertListEqual(rh_service.get_disabled('sysvinit'), - RET) + mock_run = MagicMock(return_value="salt stack") + with patch.dict(rh_service.__salt__, {"cmd.run": mock_run}): + with patch.object(rh_service, "_sysv_services", self._m_ret()): + with patch.object(rh_service, "_sysv_is_enabled", self._m_bool(False)): + self.assertListEqual(rh_service.get_disabled("sysvinit"), RET) - with patch.object(rh_service, '_upstart_services', - self._m_lst()): - with patch.object(rh_service, '_upstart_is_enabled', MagicMock(return_value=False)): + with patch.object(rh_service, "_upstart_services", self._m_lst()): + with patch.object( + rh_service, + "_upstart_is_enabled", + MagicMock(return_value=False), + ): self.assertListEqual(rh_service.get_disabled(), RET) # 'get_all' function tests: 1 def test_get_all(self): - ''' + """ Test if it return all installed services. Use the ``limit`` param to restrict results to services of that type. - ''' - with patch.object(rh_service, '_upstart_services', self._m_ret()): - self.assertListEqual(rh_service.get_all('upstart'), RET) + """ + with patch.object(rh_service, "_upstart_services", self._m_ret()): + self.assertListEqual(rh_service.get_all("upstart"), RET) - with patch.object(rh_service, '_sysv_services', self._m_ret()): - self.assertListEqual(rh_service.get_all('sysvinit'), RET) + with patch.object(rh_service, "_sysv_services", self._m_ret()): + self.assertListEqual(rh_service.get_all("sysvinit"), RET) - with patch.object(rh_service, '_upstart_services', self._m_lst()): + with patch.object(rh_service, "_upstart_services", self._m_lst()): self.assertListEqual(rh_service.get_all(), RET) # 'available' function tests: 1 def test_available(self): - ''' + """ Test if it return True if the named service is available. - ''' - with patch.object(rh_service, '_service_is_upstart', self._m_bool()): - self.assertTrue(rh_service.available('salt-api', 'upstart')) + """ + with patch.object(rh_service, "_service_is_upstart", self._m_bool()): + self.assertTrue(rh_service.available("salt-api", "upstart")) - with patch.object(rh_service, '_service_is_sysv', self._m_bool()): - self.assertTrue(rh_service.available('salt-api', 'sysvinit')) + with patch.object(rh_service, "_service_is_sysv", self._m_bool()): + self.assertTrue(rh_service.available("salt-api", "sysvinit")) - with patch.object(rh_service, '_service_is_upstart', - self._m_bool()): - self.assertTrue(rh_service.available('salt-api')) + with patch.object(rh_service, "_service_is_upstart", self._m_bool()): + self.assertTrue(rh_service.available("salt-api")) # 'missing' function tests: 1 def test_missing(self): - ''' + """ Test if it return True if the named service is not available. - ''' - with patch.object(rh_service, '_service_is_upstart', - self._m_bool(False)): - self.assertTrue(rh_service.missing('sshd', 'upstart')) + """ + with patch.object(rh_service, "_service_is_upstart", self._m_bool(False)): + self.assertTrue(rh_service.missing("sshd", "upstart")) - with patch.object(rh_service, '_service_is_sysv', - self._m_bool(False)): - self.assertTrue(rh_service.missing('sshd')) + with patch.object(rh_service, "_service_is_sysv", self._m_bool(False)): + self.assertTrue(rh_service.missing("sshd")) - with patch.object(rh_service, '_service_is_sysv', self._m_bool()): - self.assertFalse(rh_service.missing('sshd', 'sysvinit')) + with patch.object(rh_service, "_service_is_sysv", self._m_bool()): + self.assertFalse(rh_service.missing("sshd", "sysvinit")) - with patch.object(rh_service, '_service_is_upstart', - self._m_bool()): - self.assertFalse(rh_service.missing('sshd')) + with patch.object(rh_service, "_service_is_upstart", self._m_bool()): + self.assertFalse(rh_service.missing("sshd")) # 'start' function tests: 1 def test_start(self): - ''' + """ Test if it start the specified service. - ''' - with patch.object(rh_service, '_service_is_upstart', self._m_bool()): - with patch.dict(rh_service.__salt__, {'cmd.retcode': - self._m_bool(False)}): - self.assertTrue(rh_service.start('salt-api')) + """ + with patch.object(rh_service, "_service_is_upstart", self._m_bool()): + with patch.dict(rh_service.__salt__, {"cmd.retcode": self._m_bool(False)}): + self.assertTrue(rh_service.start("salt-api")) # 'stop' function tests: 1 def test_stop(self): - ''' + """ Test if it stop the specified service. - ''' - with patch.object(rh_service, '_service_is_upstart', self._m_bool()): - with patch.dict(rh_service.__salt__, {'cmd.retcode': - self._m_bool(False)}): - self.assertTrue(rh_service.stop('salt-api')) + """ + with patch.object(rh_service, "_service_is_upstart", self._m_bool()): + with patch.dict(rh_service.__salt__, {"cmd.retcode": self._m_bool(False)}): + self.assertTrue(rh_service.stop("salt-api")) # 'restart' function tests: 1 def test_restart(self): - ''' + """ Test if it restart the specified service. - ''' - with patch.object(rh_service, '_service_is_upstart', self._m_bool()): - with patch.dict(rh_service.__salt__, {'cmd.retcode': - self._m_bool(False)}): - self.assertTrue(rh_service.restart('salt-api')) + """ + with patch.object(rh_service, "_service_is_upstart", self._m_bool()): + with patch.dict(rh_service.__salt__, {"cmd.retcode": self._m_bool(False)}): + self.assertTrue(rh_service.restart("salt-api")) # 'reload_' function tests: 1 def test_reload(self): - ''' + """ Test if it reload the specified service. - ''' - with patch.object(rh_service, '_service_is_upstart', self._m_bool()): - with patch.dict(rh_service.__salt__, {'cmd.retcode': - self._m_bool(False)}): - self.assertTrue(rh_service.reload_('salt-api')) + """ + with patch.object(rh_service, "_service_is_upstart", self._m_bool()): + with patch.dict(rh_service.__salt__, {"cmd.retcode": self._m_bool(False)}): + self.assertTrue(rh_service.reload_("salt-api")) # 'status' function tests: 1 def test_status(self): - ''' + """ Test if it return the status for a service, returns a bool whether the service is running. - ''' - with patch.object(rh_service, '_service_is_upstart', self._m_bool()): - mock_run = MagicMock(return_value='start/running') - with patch.dict(rh_service.__salt__, {'cmd.run': mock_run}): - self.assertTrue(rh_service.status('salt-api')) + """ + with patch.object(rh_service, "_service_is_upstart", self._m_bool()): + mock_run = MagicMock(return_value="start/running") + with patch.dict(rh_service.__salt__, {"cmd.run": mock_run}): + self.assertTrue(rh_service.status("salt-api")) - with patch.object(rh_service, '_service_is_upstart', - self._m_bool(False)): - with patch.dict(rh_service.__salt__, {'status.pid': - self._m_bool()}): - self.assertTrue(rh_service.status('salt-api', sig=True)) + with patch.object(rh_service, "_service_is_upstart", self._m_bool(False)): + with patch.dict(rh_service.__salt__, {"status.pid": self._m_bool()}): + self.assertTrue(rh_service.status("salt-api", sig=True)) mock_ret = MagicMock(return_value=0) - with patch.dict(rh_service.__salt__, {'cmd.retcode': mock_ret}): - self.assertTrue(rh_service.status('salt-api')) + with patch.dict(rh_service.__salt__, {"cmd.retcode": mock_ret}): + self.assertTrue(rh_service.status("salt-api")) # 'enable' function tests: 1 def test_enable(self): - ''' + """ Test if it enable the named service to start at boot. - ''' + """ mock_bool = MagicMock(side_effect=[True, True, False]) - with patch.object(rh_service, '_service_is_upstart', mock_bool): - with patch.object(rh_service, '_upstart_is_enabled', MagicMock(return_value=True)): - with patch.object(rh_service, '_upstart_enable', MagicMock(return_value=False)): - self.assertFalse(rh_service.enable('salt-api')) - with patch.object(rh_service, '_upstart_enable', MagicMock(return_value=True)): - self.assertTrue(rh_service.enable('salt-api')) + with patch.object(rh_service, "_service_is_upstart", mock_bool): + with patch.object( + rh_service, "_upstart_is_enabled", MagicMock(return_value=True) + ): + with patch.object( + rh_service, "_upstart_enable", MagicMock(return_value=False) + ): + self.assertFalse(rh_service.enable("salt-api")) + with patch.object( + rh_service, "_upstart_enable", MagicMock(return_value=True) + ): + self.assertTrue(rh_service.enable("salt-api")) - with patch.object(rh_service, '_sysv_enable', self._m_bool()): - self.assertTrue(rh_service.enable('salt-api')) + with patch.object(rh_service, "_sysv_enable", self._m_bool()): + self.assertTrue(rh_service.enable("salt-api")) # 'disable' function tests: 1 def test_disable(self): - ''' + """ Test if it disable the named service to start at boot. - ''' + """ mock_bool = MagicMock(side_effect=[True, True, False]) - with patch.object(rh_service, '_service_is_upstart', mock_bool): - with patch.object(rh_service, '_upstart_is_enabled', MagicMock(return_value=True)): - with patch.object(rh_service, '_upstart_disable', MagicMock(return_value=False)): - self.assertFalse(rh_service.disable('salt-api')) - with patch.object(rh_service, '_upstart_disable', MagicMock(return_value=True)): - self.assertTrue(rh_service.disable('salt-api')) + with patch.object(rh_service, "_service_is_upstart", mock_bool): + with patch.object( + rh_service, "_upstart_is_enabled", MagicMock(return_value=True) + ): + with patch.object( + rh_service, "_upstart_disable", MagicMock(return_value=False) + ): + self.assertFalse(rh_service.disable("salt-api")) + with patch.object( + rh_service, "_upstart_disable", MagicMock(return_value=True) + ): + self.assertTrue(rh_service.disable("salt-api")) - with patch.object(rh_service, '_sysv_disable', self._m_bool()): - self.assertTrue(rh_service.disable('salt-api')) + with patch.object(rh_service, "_sysv_disable", self._m_bool()): + self.assertTrue(rh_service.disable("salt-api")) # 'enabled' function tests: 1 def test_enabled(self): - ''' + """ Test if it check to see if the named service is enabled to start on boot. - ''' + """ mock_bool = MagicMock(side_effect=[True, False]) - with patch.object(rh_service, '_service_is_upstart', mock_bool): - with patch.object(rh_service, '_upstart_is_enabled', MagicMock(return_value=False)): - self.assertFalse(rh_service.enabled('salt-api')) + with patch.object(rh_service, "_service_is_upstart", mock_bool): + with patch.object( + rh_service, "_upstart_is_enabled", MagicMock(return_value=False) + ): + self.assertFalse(rh_service.enabled("salt-api")) - with patch.object(rh_service, '_sysv_is_enabled', self._m_bool()): - self.assertTrue(rh_service.enabled('salt-api')) + with patch.object(rh_service, "_sysv_is_enabled", self._m_bool()): + self.assertTrue(rh_service.enabled("salt-api")) # 'disabled' function tests: 1 def test_disabled(self): - ''' + """ Test if it check to see if the named service is disabled to start on boot. - ''' + """ mock_bool = MagicMock(side_effect=[True, False]) - with patch.object(rh_service, '_service_is_upstart', mock_bool): - with patch.object(rh_service, '_upstart_is_enabled', MagicMock(return_value=False)): - self.assertTrue(rh_service.disabled('salt-api')) + with patch.object(rh_service, "_service_is_upstart", mock_bool): + with patch.object( + rh_service, "_upstart_is_enabled", MagicMock(return_value=False) + ): + self.assertTrue(rh_service.disabled("salt-api")) - with patch.object(rh_service, '_sysv_is_enabled', - self._m_bool(False)): - self.assertTrue(rh_service.disabled('salt-api')) + with patch.object(rh_service, "_sysv_is_enabled", self._m_bool(False)): + self.assertTrue(rh_service.disabled("salt-api")) diff --git a/tests/unit/modules/test_riak.py b/tests/unit/modules/test_riak.py index b56448d2d8e..31febf5ad2b 100644 --- a/tests/unit/modules/test_riak.py +++ b/tests/unit/modules/test_riak.py @@ -1,114 +1,136 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch) - # Import Salt Libs import salt.modules.riak as riak +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class RiakTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.riak - ''' + """ + def setup_loader_modules(self): return {riak: {}} def test_start(self): - ''' + """ Test for start Riak - ''' - with patch.object(riak, '__execute_cmd', return_value={'retcode': 0, 'stdout': 'success'}): - self.assertEqual( - riak.start(), {'success': True, 'comment': 'success'} - ) + """ + with patch.object( + riak, "__execute_cmd", return_value={"retcode": 0, "stdout": "success"} + ): + self.assertEqual(riak.start(), {"success": True, "comment": "success"}) def test_stop(self): - ''' + """ Test for stop Riak - ''' - with patch.object(riak, '__execute_cmd', return_value={'retcode': 0, 'stdout': 'success'}): - self.assertEqual( - riak.stop(), {'success': True, 'comment': 'success'} - ) + """ + with patch.object( + riak, "__execute_cmd", return_value={"retcode": 0, "stdout": "success"} + ): + self.assertEqual(riak.stop(), {"success": True, "comment": "success"}) def test_cluster_join(self): - ''' + """ Test for Join a Riak cluster - ''' - with patch.object(riak, '__execute_cmd', return_value={'retcode': 0, 'stdout': 'success'}): + """ + with patch.object( + riak, "__execute_cmd", return_value={"retcode": 0, "stdout": "success"} + ): self.assertEqual( - riak.cluster_join('A', 'B'), {'success': True, 'comment': 'success'} + riak.cluster_join("A", "B"), {"success": True, "comment": "success"} ) def test_cluster_leave(self): - ''' + """ Test for leaving a Riak cluster - ''' - with patch.object(riak, '__execute_cmd', return_value={'retcode': 0, 'stdout': 'success'}): + """ + with patch.object( + riak, "__execute_cmd", return_value={"retcode": 0, "stdout": "success"} + ): self.assertEqual( - riak.cluster_leave('A', 'B'), {'success': True, 'comment': 'success'} + riak.cluster_leave("A", "B"), {"success": True, "comment": "success"} ) def test_cluster_plan(self): - ''' + """ Test for Review Cluster Plan - ''' - with patch.object(riak, '__execute_cmd', return_value={'retcode': 0, 'stdout': 'success'}): + """ + with patch.object( + riak, "__execute_cmd", return_value={"retcode": 0, "stdout": "success"} + ): self.assertTrue(riak.cluster_plan()) def test_cluster_commit(self): - ''' + """ Test for Commit Cluster Changes - ''' - with patch.object(riak, '__execute_cmd', return_value={'retcode': 0, 'stdout': 'success'}): + """ + with patch.object( + riak, "__execute_cmd", return_value={"retcode": 0, "stdout": "success"} + ): self.assertEqual( - riak.cluster_commit(), {'success': True, 'comment': 'success'} + riak.cluster_commit(), {"success": True, "comment": "success"} ) def test_member_status(self): - ''' + """ Test for Get cluster member status - ''' - with patch.object(riak, '__execute_cmd', return_value={'stdout': 'A:a/B:b\nC:c/D:d'}): - self.assertDictEqual(riak.member_status(), - {'membership': {}, - 'summary': {'A': 'a', 'C': 'c', 'B': 'b', - 'D': 'd', 'Exiting': 0, 'Down': 0, - 'Valid': 0, 'Leaving': 0, - 'Joining': 0}}) + """ + with patch.object( + riak, "__execute_cmd", return_value={"stdout": "A:a/B:b\nC:c/D:d"} + ): + self.assertDictEqual( + riak.member_status(), + { + "membership": {}, + "summary": { + "A": "a", + "C": "c", + "B": "b", + "D": "d", + "Exiting": 0, + "Down": 0, + "Valid": 0, + "Leaving": 0, + "Joining": 0, + }, + }, + ) def test_status(self): - ''' + """ Test status information - ''' - ret = {'stdout': 'vnode_map_update_time_95 : 0\nvnode_map_update_time_99 : 0'} + """ + ret = {"stdout": "vnode_map_update_time_95 : 0\nvnode_map_update_time_99 : 0"} - with patch.object(riak, '__execute_cmd', return_value=ret): + with patch.object(riak, "__execute_cmd", return_value=ret): self.assertEqual( - riak.status(), {'vnode_map_update_time_95': '0', 'vnode_map_update_time_99': '0'} + riak.status(), + {"vnode_map_update_time_95": "0", "vnode_map_update_time_99": "0"}, ) def test_test(self): - ''' + """ Test the Riak test - ''' - with patch.object(riak, '__execute_cmd', return_value={'retcode': 0, 'stdout': 'success'}): - self.assertEqual( - riak.test(), {'success': True, 'comment': 'success'} - ) + """ + with patch.object( + riak, "__execute_cmd", return_value={"retcode": 0, "stdout": "success"} + ): + self.assertEqual(riak.test(), {"success": True, "comment": "success"}) def test_services(self): - ''' + """ Test Riak Service List - ''' - with patch.object(riak, '__execute_cmd', return_value={'stdout': '[a,b,c]'}): - self.assertEqual(riak.services(), ['a', 'b', 'c']) + """ + with patch.object(riak, "__execute_cmd", return_value={"stdout": "[a,b,c]"}): + self.assertEqual(riak.services(), ["a", "b", "c"]) diff --git a/tests/unit/modules/test_rpm_lowpkg.py b/tests/unit/modules/test_rpm_lowpkg.py index 527c8d3bf8c..dda70a6e940 100644 --- a/tests/unit/modules/test_rpm_lowpkg.py +++ b/tests/unit/modules/test_rpm_lowpkg.py @@ -1,103 +1,103 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.rpm_lowpkg as rpm +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RpmTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.rpm - ''' + """ + def setup_loader_modules(self): - return {rpm: {'rpm': MagicMock(return_value=MagicMock)}} + return {rpm: {"rpm": MagicMock(return_value=MagicMock)}} # 'list_pkgs' function tests: 1 def test_list_pkgs(self): - ''' + """ Test if it list the packages currently installed in a dict - ''' - mock = MagicMock(return_value='') - with patch.dict(rpm.__salt__, {'cmd.run': mock}): + """ + mock = MagicMock(return_value="") + with patch.dict(rpm.__salt__, {"cmd.run": mock}): self.assertDictEqual(rpm.list_pkgs(), {}) # 'verify' function tests: 1 def test_verify(self): - ''' + """ Test if it runs an rpm -Va on a system, and returns the results in a dict - ''' - mock = MagicMock(return_value={'stdout': '', - 'stderr': '', - 'retcode': 0, - 'pid': 12345}) - with patch.dict(rpm.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(rpm.verify('httpd'), {}) + """ + mock = MagicMock( + return_value={"stdout": "", "stderr": "", "retcode": 0, "pid": 12345} + ) + with patch.dict(rpm.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual(rpm.verify("httpd"), {}) # 'file_list' function tests: 1 def test_file_list(self): - ''' + """ Test if it list the files that belong to a package. - ''' - mock = MagicMock(return_value='') - with patch.dict(rpm.__salt__, {'cmd.run': mock}): - self.assertDictEqual(rpm.file_list('httpd'), - {'errors': [], 'files': []}) + """ + mock = MagicMock(return_value="") + with patch.dict(rpm.__salt__, {"cmd.run": mock}): + self.assertDictEqual(rpm.file_list("httpd"), {"errors": [], "files": []}) # 'file_dict' function tests: 1 def test_file_dict(self): - ''' + """ Test if it list the files that belong to a package - ''' - mock = MagicMock(return_value='') - with patch.dict(rpm.__salt__, {'cmd.run': mock}): - self.assertDictEqual(rpm.file_dict('httpd'), - {'errors': [], 'packages': {}}) + """ + mock = MagicMock(return_value="") + with patch.dict(rpm.__salt__, {"cmd.run": mock}): + self.assertDictEqual(rpm.file_dict("httpd"), {"errors": [], "packages": {}}) # 'owner' function tests: 1 def test_owner(self): - ''' + """ Test if it return the name of the package that owns the file. - ''' - self.assertEqual(rpm.owner(), '') + """ + self.assertEqual(rpm.owner(), "") - ret = 'file /usr/bin/salt-jenkins-build is not owned by any package' + ret = "file /usr/bin/salt-jenkins-build is not owned by any package" mock = MagicMock(return_value=ret) - with patch.dict(rpm.__salt__, {'cmd.run_stdout': mock}): - self.assertEqual(rpm.owner('/usr/bin/salt-jenkins-build'), '') + with patch.dict(rpm.__salt__, {"cmd.run_stdout": mock}): + self.assertEqual(rpm.owner("/usr/bin/salt-jenkins-build"), "") - ret = {'/usr/bin/vim': 'vim-enhanced-7.4.160-1.e17.x86_64', - '/usr/bin/python': 'python-2.7.5-16.e17.x86_64'} - mock = MagicMock(side_effect=['python-2.7.5-16.e17.x86_64', - 'vim-enhanced-7.4.160-1.e17.x86_64']) - with patch.dict(rpm.__salt__, {'cmd.run_stdout': mock}): - self.assertDictEqual(rpm.owner('/usr/bin/python', '/usr/bin/vim'), - ret) + ret = { + "/usr/bin/vim": "vim-enhanced-7.4.160-1.e17.x86_64", + "/usr/bin/python": "python-2.7.5-16.e17.x86_64", + } + mock = MagicMock( + side_effect=[ + "python-2.7.5-16.e17.x86_64", + "vim-enhanced-7.4.160-1.e17.x86_64", + ] + ) + with patch.dict(rpm.__salt__, {"cmd.run_stdout": mock}): + self.assertDictEqual(rpm.owner("/usr/bin/python", "/usr/bin/vim"), ret) # 'checksum' function tests: 1 def test_checksum(self): - ''' + """ Test if checksum validate as expected - ''' + """ ret = { "file1.rpm": True, "file2.rpm": False, @@ -105,25 +105,33 @@ class RpmTestCase(TestCase, LoaderModuleMockMixin): } mock = MagicMock(side_effect=[True, 0, True, 1, False, 0]) - with patch.dict(rpm.__salt__, {'file.file_exists': mock, 'cmd.retcode': mock}): - self.assertDictEqual(rpm.checksum("file1.rpm", "file2.rpm", "file3.rpm"), ret) + with patch.dict(rpm.__salt__, {"file.file_exists": mock, "cmd.retcode": mock}): + self.assertDictEqual( + rpm.checksum("file1.rpm", "file2.rpm", "file3.rpm"), ret + ) def test_version_cmp_rpm(self): - ''' + """ Test package version is called RPM version if RPM-Python is installed :return: - ''' - with patch('salt.modules.rpm_lowpkg.rpm.labelCompare', MagicMock(return_value=0)), \ - patch('salt.modules.rpm_lowpkg.HAS_RPM', True): - self.assertEqual(0, rpm.version_cmp('1', '2')) # mock returns 0, which means RPM was called + """ + with patch( + "salt.modules.rpm_lowpkg.rpm.labelCompare", MagicMock(return_value=0) + ), patch("salt.modules.rpm_lowpkg.HAS_RPM", True): + self.assertEqual( + 0, rpm.version_cmp("1", "2") + ) # mock returns 0, which means RPM was called def test_version_cmp_fallback(self): - ''' + """ Test package version is called RPM version if RPM-Python is installed :return: - ''' - with patch('salt.modules.rpm_lowpkg.rpm.labelCompare', MagicMock(return_value=0)), \ - patch('salt.modules.rpm_lowpkg.HAS_RPM', False): - self.assertEqual(-1, rpm.version_cmp('1', '2')) # mock returns -1, a python implementation was called + """ + with patch( + "salt.modules.rpm_lowpkg.rpm.labelCompare", MagicMock(return_value=0) + ), patch("salt.modules.rpm_lowpkg.HAS_RPM", False): + self.assertEqual( + -1, rpm.version_cmp("1", "2") + ) # mock returns -1, a python implementation was called diff --git a/tests/unit/modules/test_rsync.py b/tests/unit/modules/test_rsync.py index 73527369344..630277e6218 100644 --- a/tests/unit/modules/test_rsync.py +++ b/tests/unit/modules/test_rsync.py @@ -1,82 +1,94 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.modules.rsync as rsync from salt.exceptions import CommandExecutionError, SaltInvocationError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RsyncTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.rsync - ''' + """ + def setup_loader_modules(self): return {rsync: {}} def test_rsync(self): - ''' + """ Test for rsync files from src to dst - ''' - with patch.dict(rsync.__salt__, {'config.option': - MagicMock(return_value=False)}): - self.assertRaises(SaltInvocationError, rsync.rsync, '', '') + """ + with patch.dict( + rsync.__salt__, {"config.option": MagicMock(return_value=False)} + ): + self.assertRaises(SaltInvocationError, rsync.rsync, "", "") - with patch.dict(rsync.__salt__, - {'config.option': MagicMock(return_value='A'), - 'cmd.run_all': MagicMock(side_effect=[OSError(1, 'f'), - 'A'])}): - with patch.object(rsync, '_check', return_value=['A']): - self.assertRaises(CommandExecutionError, rsync.rsync, 'a', 'b') + with patch.dict( + rsync.__salt__, + { + "config.option": MagicMock(return_value="A"), + "cmd.run_all": MagicMock(side_effect=[OSError(1, "f"), "A"]), + }, + ): + with patch.object(rsync, "_check", return_value=["A"]): + self.assertRaises(CommandExecutionError, rsync.rsync, "a", "b") - self.assertEqual(rsync.rsync('src', 'dst'), 'A') + self.assertEqual(rsync.rsync("src", "dst"), "A") def test_version(self): - ''' + """ Test for return rsync version - ''' - mock = MagicMock(side_effect=[OSError(1, 'f'), 'A B C\n']) - with patch.dict(rsync.__salt__, {'cmd.run_stdout': mock}): + """ + mock = MagicMock(side_effect=[OSError(1, "f"), "A B C\n"]) + with patch.dict(rsync.__salt__, {"cmd.run_stdout": mock}): self.assertRaises(CommandExecutionError, rsync.version) - self.assertEqual(rsync.version(), 'C') + self.assertEqual(rsync.version(), "C") def test_rsync_excludes_list(self): - ''' + """ Test for rsync files from src to dst with a list of excludes - ''' + """ mock = { - 'config.option': MagicMock(return_value=False), - 'cmd.run_all': MagicMock() + "config.option": MagicMock(return_value=False), + "cmd.run_all": MagicMock(), } with patch.dict(rsync.__salt__, mock): - rsync.rsync('src', 'dst', exclude=['test/one', 'test/two']) - mock['cmd.run_all'].assert_called_once_with( - ['rsync', '-avz', '--exclude', 'test/one', '--exclude', 'test/two', 'src', 'dst'], + rsync.rsync("src", "dst", exclude=["test/one", "test/two"]) + mock["cmd.run_all"].assert_called_once_with( + [ + "rsync", + "-avz", + "--exclude", + "test/one", + "--exclude", + "test/two", + "src", + "dst", + ], python_shell=False, ) def test_rsync_excludes_str(self): - ''' + """ Test for rsync files from src to dst with one exclude - ''' + """ mock = { - 'config.option': MagicMock(return_value=False), - 'cmd.run_all': MagicMock() + "config.option": MagicMock(return_value=False), + "cmd.run_all": MagicMock(), } with patch.dict(rsync.__salt__, mock): - rsync.rsync('src', 'dst', exclude='test/one') - mock['cmd.run_all'].assert_called_once_with( - ['rsync', '-avz', '--exclude', 'test/one', 'src', 'dst'], + rsync.rsync("src", "dst", exclude="test/one") + mock["cmd.run_all"].assert_called_once_with( + ["rsync", "-avz", "--exclude", "test/one", "src", "dst"], python_shell=False, ) diff --git a/tests/unit/modules/test_rvm.py b/tests/unit/modules/test_rvm.py index 811db3a95de..ede1f953028 100644 --- a/tests/unit/modules/test_rvm.py +++ b/tests/unit/modules/test_rvm.py @@ -3,119 +3,126 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch, call - # Import salt libs import salt.modules.rvm as rvm +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase + class TestRvmModule(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { rvm: { - '__salt__': { - 'cmd.has_exec': MagicMock(return_value=True), - 'config.option': MagicMock(return_value=None) + "__salt__": { + "cmd.has_exec": MagicMock(return_value=True), + "config.option": MagicMock(return_value=None), } } } def test_rvm(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(rvm.__salt__, {'cmd.run_all': mock}): - rvm._rvm(['install', '1.9.3']) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(rvm.__salt__, {"cmd.run_all": mock}): + rvm._rvm(["install", "1.9.3"]) mock.assert_called_once_with( - ['/usr/local/rvm/bin/rvm', 'install', '1.9.3'], + ["/usr/local/rvm/bin/rvm", "install", "1.9.3"], runas=None, cwd=None, python_shell=False, - env=None + env=None, ) def test_rvm_do(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout'}) - with patch.dict(rvm.__salt__, {'cmd.run_all': mock}): - rvm._rvm_do('1.9.3', ['gemset', 'list']) + mock = MagicMock(return_value={"retcode": 0, "stdout": "stdout"}) + with patch.dict(rvm.__salt__, {"cmd.run_all": mock}): + rvm._rvm_do("1.9.3", ["gemset", "list"]) mock.assert_called_once_with( - ['/usr/local/rvm/bin/rvm', '1.9.3', 'do', 'gemset', 'list'], + ["/usr/local/rvm/bin/rvm", "1.9.3", "do", "gemset", "list"], runas=None, cwd=None, python_shell=False, - env=None + env=None, ) def test_install(self): - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(rvm.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(rvm.__salt__, {"cmd.run_all": mock}): rvm.install() - curl_cmd = 'curl -Ls https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer ' \ - '| bash -s stable' + curl_cmd = ( + "curl -Ls https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer " + "| bash -s stable" + ) mock.assert_called_once_with(curl_cmd, runas=None, python_shell=True) def test_install_ruby_nonroot(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout'}) + mock = MagicMock(return_value={"retcode": 0, "stdout": "stdout"}) expected = [ call( - ['/usr/local/rvm/bin/rvm', 'autolibs', 'disable', '2.0.0'], - runas='rvm', + ["/usr/local/rvm/bin/rvm", "autolibs", "disable", "2.0.0"], + runas="rvm", cwd=None, python_shell=False, - env=None + env=None, ), call( - ['/usr/local/rvm/bin/rvm', 'install', '2.0.0', - '--disable-binary'], - runas='rvm', + ["/usr/local/rvm/bin/rvm", "install", "2.0.0", "--disable-binary"], + runas="rvm", cwd=None, python_shell=False, - env=None - ) + env=None, + ), ] - with patch.dict(rvm.__salt__, {'cmd.run_all': mock}): - rvm.install_ruby('2.0.0', runas='rvm') + with patch.dict(rvm.__salt__, {"cmd.run_all": mock}): + rvm.install_ruby("2.0.0", runas="rvm") self.assertEqual(mock.call_args_list, expected) def test_install_with_env(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout'}) + mock = MagicMock(return_value={"retcode": 0, "stdout": "stdout"}) expected = [ call( - ['/usr/local/rvm/bin/rvm', 'install', '2.0.0'], + ["/usr/local/rvm/bin/rvm", "install", "2.0.0"], runas=None, cwd=None, python_shell=False, - env=[{'RUBY_CONFIGURE_OPTS': '--foobar'}] + env=[{"RUBY_CONFIGURE_OPTS": "--foobar"}], ) ] - with patch.dict(rvm.__salt__, {'cmd.run_all': mock}): - rvm.install_ruby('2.0.0', env=[{'RUBY_CONFIGURE_OPTS': '--foobar'}]) + with patch.dict(rvm.__salt__, {"cmd.run_all": mock}): + rvm.install_ruby("2.0.0", env=[{"RUBY_CONFIGURE_OPTS": "--foobar"}]) self.assertEqual(mock.call_args_list, expected) def test_install_with_opts(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout'}) + mock = MagicMock(return_value={"retcode": 0, "stdout": "stdout"}) expected = [ call( - ['/usr/local/rvm/bin/rvm', 'install', '2.0.0', - '-C --enable-shared,--with-readline-dir=$HOME/.rvm/usr', - '--patch /path/to/awesome.patch'], + [ + "/usr/local/rvm/bin/rvm", + "install", + "2.0.0", + "-C --enable-shared,--with-readline-dir=$HOME/.rvm/usr", + "--patch /path/to/awesome.patch", + ], runas=None, cwd=None, python_shell=False, - env=None + env=None, ) ] - with patch.dict(rvm.__salt__, {'cmd.run_all': mock}): - rvm.install_ruby('2.0.0', opts=[ - '-C --enable-shared,--with-readline-dir=$HOME/.rvm/usr', - '--patch /path/to/awesome.patch' - ]) + with patch.dict(rvm.__salt__, {"cmd.run_all": mock}): + rvm.install_ruby( + "2.0.0", + opts=[ + "-C --enable-shared,--with-readline-dir=$HOME/.rvm/usr", + "--patch /path/to/awesome.patch", + ], + ) self.assertEqual(mock.call_args_list, expected) def test_list(self): - list_output = ''' + list_output = """ rvm rubies jruby-1.6.5.1 [ amd64 ] @@ -130,35 +137,36 @@ rvm rubies # =* - current && default # * - default -''' - with patch.object(rvm, '_rvm') as mock_method: +""" + with patch.object(rvm, "_rvm") as mock_method: mock_method.return_value = list_output self.assertEqual( - [['jruby', '1.6.5.1', False], - ['ree', '1.8.7-2011.03', False], - ['ree', '1.8.7-2011.12', False], - ['ree', '1.8.7-2012.02', True], - ['ruby', '1.9.2-p180', False], - ['ruby', '1.9.3-p125', False], - ['ruby', 'head', False]], - rvm.list_()) + [ + ["jruby", "1.6.5.1", False], + ["ree", "1.8.7-2011.03", False], + ["ree", "1.8.7-2011.12", False], + ["ree", "1.8.7-2012.02", True], + ["ruby", "1.9.2-p180", False], + ["ruby", "1.9.3-p125", False], + ["ruby", "head", False], + ], + rvm.list_(), + ) def test_gemset_list(self): - output = ''' + output = """ gemsets for ree-1.8.7-2012.02 (found in /usr/local/rvm/gems/ree-1.8.7-2012.02) global bar foo -''' - with patch.object(rvm, '_rvm_do') as mock_method: +""" + with patch.object(rvm, "_rvm_do") as mock_method: mock_method.return_value = output - self.assertEqual( - ['global', 'bar', 'foo'], - rvm.gemset_list()) + self.assertEqual(["global", "bar", "foo"], rvm.gemset_list()) def test_gemset_list_all(self): - output = ''' + output = """ gemsets for ruby-1.9.3-p125 (found in /usr/local/rvm/gems/ruby-1.9.3-p125) 9bar @@ -182,12 +190,15 @@ gemsets for ruby-1.9.2-p180 (found in /usr/local/rvm/gems/ruby-1.9.2-p180) global -''' - with patch.object(rvm, '_rvm_do') as mock_method: +""" + with patch.object(rvm, "_rvm_do") as mock_method: mock_method.return_value = output self.assertEqual( - {'jruby-1.6.5.1': ['global', 'jbar', 'jfoo'], - 'ruby-1.9.2-p180': ['global'], - 'ruby-1.9.3-p125': ['9bar', '9foo', 'global'], - 'ruby-head': ['global', 'headbar', 'headfoo']}, - rvm.gemset_list_all()) + { + "jruby-1.6.5.1": ["global", "jbar", "jfoo"], + "ruby-1.9.2-p180": ["global"], + "ruby-1.9.3-p125": ["9bar", "9foo", "global"], + "ruby-head": ["global", "headbar", "headfoo"], + }, + rvm.gemset_list_all(), + ) diff --git a/tests/unit/modules/test_s3.py b/tests/unit/modules/test_s3.py index 83316075a77..2e660af0ed9 100644 --- a/tests/unit/modules/test_s3.py +++ b/tests/unit/modules/test_s3.py @@ -1,78 +1,127 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.s3 as s3 +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class S3TestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - return {s3: {'__utils__': {'s3.query': MagicMock(return_value='A')}}} + return {s3: {"__utils__": {"s3.query": MagicMock(return_value="A")}}} def test__get_key_defaults(self): - mock = MagicMock(return_value='') - with patch.dict(s3.__salt__, {'config.option': mock}): - key, keyid, service_url, verify_ssl, kms_keyid, location, role_arn, path_style, https_enable = ( - s3._get_key(None, None, None, None, None, None, None, None, None)) + mock = MagicMock(return_value="") + with patch.dict(s3.__salt__, {"config.option": mock}): + ( + key, + keyid, + service_url, + verify_ssl, + kms_keyid, + location, + role_arn, + path_style, + https_enable, + ) = s3._get_key(None, None, None, None, None, None, None, None, None) self.assertEqual(None, role_arn) self.assertEqual(None, key) self.assertEqual(None, keyid) - self.assertEqual('s3.amazonaws.com', service_url) - self.assertEqual('', verify_ssl) - self.assertEqual('', location) - self.assertEqual('', path_style) - self.assertEqual('', https_enable) + self.assertEqual("s3.amazonaws.com", service_url) + self.assertEqual("", verify_ssl) + self.assertEqual("", location) + self.assertEqual("", path_style) + self.assertEqual("", https_enable) def test_delete(self): - ''' + """ Test for delete a bucket, or delete an object from a bucket. - ''' - with patch.object(s3, '_get_key', - return_value=('key', 'keyid', 'service_url', - 'verify_ssl', 'kms_keyid', 'location', - 'role_arn', 'path_style', 'https_enable')): - self.assertEqual(s3.delete('bucket'), 'A') + """ + with patch.object( + s3, + "_get_key", + return_value=( + "key", + "keyid", + "service_url", + "verify_ssl", + "kms_keyid", + "location", + "role_arn", + "path_style", + "https_enable", + ), + ): + self.assertEqual(s3.delete("bucket"), "A") def test_get(self): - ''' + """ Test for list the contents of a bucket, or return an object from a bucket. - ''' - with patch.object(s3, '_get_key', - return_value=('key', 'keyid', 'service_url', - 'verify_ssl', 'kms_keyid', 'location', - 'role_arn', 'path_style', 'https_enable')): - self.assertEqual(s3.get(), 'A') + """ + with patch.object( + s3, + "_get_key", + return_value=( + "key", + "keyid", + "service_url", + "verify_ssl", + "kms_keyid", + "location", + "role_arn", + "path_style", + "https_enable", + ), + ): + self.assertEqual(s3.get(), "A") def test_head(self): - ''' + """ Test for return the metadata for a bucket, or an object in a bucket. - ''' - with patch.object(s3, '_get_key', - return_value=('key', 'keyid', 'service_url', - 'verify_ssl', 'kms_keyid', 'location', - 'role_arn', 'path_style', 'https_enable')): - self.assertEqual(s3.head('bucket'), 'A') + """ + with patch.object( + s3, + "_get_key", + return_value=( + "key", + "keyid", + "service_url", + "verify_ssl", + "kms_keyid", + "location", + "role_arn", + "path_style", + "https_enable", + ), + ): + self.assertEqual(s3.head("bucket"), "A") def test_put(self): - ''' + """ Test for create a new bucket, or upload an object to a bucket. - ''' - with patch.object(s3, '_get_key', - return_value=('key', 'keyid', 'service_url', - 'verify_ssl', 'kms_keyid', 'location', - 'role_arn', 'path_style', 'https_enable')): - self.assertEqual(s3.put('bucket'), 'A') + """ + with patch.object( + s3, + "_get_key", + return_value=( + "key", + "keyid", + "service_url", + "verify_ssl", + "kms_keyid", + "location", + "role_arn", + "path_style", + "https_enable", + ), + ): + self.assertEqual(s3.put("bucket"), "A") diff --git a/tests/unit/modules/test_s6.py b/tests/unit/modules/test_s6.py index fbd1fc937ae..2ecdc19e733 100644 --- a/tests/unit/modules/test_s6.py +++ b/tests/unit/modules/test_s6.py @@ -1,130 +1,126 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Marek Skrobacki <skrobul@skrobul.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.s6 as s6 +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class S6TestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.s6 - ''' + """ + def setup_loader_modules(self): - return {s6: {'SERVICE_DIR': '/etc/service'}} + return {s6: {"SERVICE_DIR": "/etc/service"}} # 'start' function tests: 1 def test_start(self): - ''' + """ Test if it starts service via s6-svc. - ''' + """ mock_ret = MagicMock(return_value=False) - with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}): - self.assertTrue(s6.start('ssh')) + with patch.dict(s6.__salt__, {"cmd.retcode": mock_ret}): + self.assertTrue(s6.start("ssh")) # 'stop' function tests: 1 def test_stop(self): - ''' + """ Test if it stops service via s6. - ''' + """ mock_ret = MagicMock(return_value=False) - with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}): - self.assertTrue(s6.stop('ssh')) + with patch.dict(s6.__salt__, {"cmd.retcode": mock_ret}): + self.assertTrue(s6.stop("ssh")) # 'term' function tests: 1 def test_term(self): - ''' + """ Test if it send a TERM to service via s6. - ''' + """ mock_ret = MagicMock(return_value=False) - with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}): - self.assertTrue(s6.term('ssh')) + with patch.dict(s6.__salt__, {"cmd.retcode": mock_ret}): + self.assertTrue(s6.term("ssh")) # 'reload_' function tests: 1 def test_reload(self): - ''' + """ Test if it send a HUP to service via s6. - ''' + """ mock_ret = MagicMock(return_value=False) - with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}): - self.assertTrue(s6.reload_('ssh')) + with patch.dict(s6.__salt__, {"cmd.retcode": mock_ret}): + self.assertTrue(s6.reload_("ssh")) # 'restart' function tests: 1 def test_restart(self): - ''' + """ Test if it restart service via s6. This will stop/start service. - ''' + """ mock_ret = MagicMock(return_value=False) - with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}): - self.assertTrue(s6.restart('ssh')) + with patch.dict(s6.__salt__, {"cmd.retcode": mock_ret}): + self.assertTrue(s6.restart("ssh")) # 'full_restart' function tests: 1 def test_full_restart(self): - ''' + """ Test if it calls s6.restart() function. - ''' + """ mock_ret = MagicMock(return_value=False) - with patch.dict(s6.__salt__, {'cmd.retcode': mock_ret}): - self.assertIsNone(s6.full_restart('ssh')) + with patch.dict(s6.__salt__, {"cmd.retcode": mock_ret}): + self.assertIsNone(s6.full_restart("ssh")) # 'status' function tests: 1 def test_status(self): - ''' + """ Test if it return the status for a service via s6, return pid if running. - ''' - mock_run = MagicMock(return_value='salt') - with patch.dict(s6.__salt__, {'cmd.run_stdout': mock_run}): - self.assertEqual(s6.status('ssh'), '') + """ + mock_run = MagicMock(return_value="salt") + with patch.dict(s6.__salt__, {"cmd.run_stdout": mock_run}): + self.assertEqual(s6.status("ssh"), "") # 'available' function tests: 1 def test_available(self): - ''' + """ Test if it returns ``True`` if the specified service is available, otherwise returns ``False``. - ''' - with patch.object(os, 'listdir', - MagicMock(return_value=['/etc/service'])): - self.assertTrue(s6.available('/etc/service')) + """ + with patch.object(os, "listdir", MagicMock(return_value=["/etc/service"])): + self.assertTrue(s6.available("/etc/service")) # 'missing' function tests: 1 def test_missing(self): - ''' + """ Test if it returns ``True`` if the specified service is not available, otherwise returns ``False``. - ''' - with patch.object(os, 'listdir', - MagicMock(return_value=['/etc/service'])): - self.assertTrue(s6.missing('foo')) + """ + with patch.object(os, "listdir", MagicMock(return_value=["/etc/service"])): + self.assertTrue(s6.missing("foo")) # 'get_all' function tests: 1 def test_get_all(self): - ''' + """ Test if it return a list of all available services. - ''' - with patch.object(os, 'listdir', - MagicMock(return_value=['/etc/service'])): - self.assertListEqual(s6.get_all(), ['/etc/service']) + """ + with patch.object(os, "listdir", MagicMock(return_value=["/etc/service"])): + self.assertListEqual(s6.get_all(), ["/etc/service"]) diff --git a/tests/unit/modules/test_salt_version.py b/tests/unit/modules/test_salt_version.py index 8ee69fce413..c0e1c737db4 100644 --- a/tests/unit/modules/test_salt_version.py +++ b/tests/unit/modules/test_salt_version.py @@ -1,33 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for salt/modules/salt_version.py -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - -# Import Salt libs -from salt.ext import six import salt.modules.salt_version as salt_version import salt.version +# Import Salt libs +from salt.ext import six +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase + class SaltVersionTestCase(TestCase): - ''' + """ Test cases for salt.modules.salt_version - ''' + """ def test_mocked_objects(self): - ''' + """ Test that the mocked objects actually have what we expect. For example, earlier tests incorrectly mocked the salt.version.SaltStackVersion.LNAMES dict using upper-case indexes - ''' + """ assert isinstance(salt.version.SaltStackVersion.LNAMES, dict) sv = salt.version.SaltStackVersion(*salt.version.__version_info__) for k, v in salt.version.SaltStackVersion.LNAMES.items(): @@ -41,180 +42,204 @@ class SaltVersionTestCase(TestCase): sv = sv.__str__() assert isinstance(sv, six.string_types) - with patch('salt.version.SaltStackVersion.LNAMES', {'neon': (2019, 8)}): - sv = salt.version.SaltStackVersion.from_name('Neon') - self.assertEqual(sv.string, '2019.8.0') + with patch("salt.version.SaltStackVersion.LNAMES", {"neon": (2019, 8)}): + sv = salt.version.SaltStackVersion.from_name("Neon") + self.assertEqual(sv.string, "2019.8.0") # get_release_number tests: 3 def test_get_release_number_no_codename(self): - ''' + """ Test that None is returned when the codename isn't found. - ''' - assert salt_version.get_release_number('foo') is None + """ + assert salt_version.get_release_number("foo") is None - @patch('salt.version.SaltStackVersion.LNAMES', {'foo': (12345, 0)}) + @patch("salt.version.SaltStackVersion.LNAMES", {"foo": (12345, 0)}) def test_get_release_number_unassigned(self): - ''' + """ Test that a string is returned when a version is found, but unassigned. - ''' - mock_str = 'No version assigned.' - assert salt_version.get_release_number('foo') == mock_str + """ + mock_str = "No version assigned." + assert salt_version.get_release_number("foo") == mock_str def test_get_release_number_success(self): - ''' + """ Test that a version is returned for a released codename - ''' - assert salt_version.get_release_number('Oxygen') == '2018.3' + """ + assert salt_version.get_release_number("Oxygen") == "2018.3" def test_get_release_number_success_new_version(self): - ''' + """ Test that a version is returned for new versioning (3000) - ''' - assert salt_version.get_release_number('Neon') == '3000' + """ + assert salt_version.get_release_number("Neon") == "3000" # equal tests: 3 - @patch('salt.version.SaltStackVersion.LNAMES', {'foo': (1900, 5)}) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='1900.5.0')) + @patch("salt.version.SaltStackVersion.LNAMES", {"foo": (1900, 5)}) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="1900.5.0")) def test_equal_success(self): - ''' + """ Test that the current version is equal to the codename - ''' - assert salt_version.equal('foo') is True + """ + assert salt_version.equal("foo") is True - @patch('salt.version.SaltStackVersion.LNAMES', {'foo': (3000, )}) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='3000.1')) + @patch("salt.version.SaltStackVersion.LNAMES", {"foo": (3000,)}) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="3000.1")) def test_equal_success_new_version(self): - ''' + """ Test that the current version is equal to the codename while using the new versioning - ''' - assert salt_version.equal('foo') is True + """ + assert salt_version.equal("foo") is True - @patch('salt.version.SaltStackVersion.LNAMES', {'oxygen': (2018, 3), - 'nitrogen': (2017, 7)}) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.version.SaltStackVersion.LNAMES", + {"oxygen": (2018, 3), "nitrogen": (2017, 7)}, + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_equal_older_codename(self): - ''' + """ Test that when an older codename is passed in, the function returns False. - ''' - assert salt_version.equal('Nitrogen') is False + """ + assert salt_version.equal("Nitrogen") is False - @patch('salt.version.SaltStackVersion.LNAMES', {'neon': (3000), - 'nitrogen': (2017, 7)}) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.version.SaltStackVersion.LNAMES", {"neon": (3000), "nitrogen": (2017, 7)} + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_equal_older_codename_new_version(self): - ''' + """ Test that when an older codename is passed in, the function returns False. while also testing with the new versioning. - ''' - assert salt_version.equal('Nitrogen') is False + """ + assert salt_version.equal("Nitrogen") is False - @patch('salt.version.SaltStackVersion.LNAMES', {'fluorine': (salt.version.MAX_SIZE - 100, 0)}) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.version.SaltStackVersion.LNAMES", + {"fluorine": (salt.version.MAX_SIZE - 100, 0)}, + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_equal_newer_codename(self): - ''' + """ Test that when a newer codename is passed in, the function returns False - ''' - assert salt_version.equal('Fluorine') is False + """ + assert salt_version.equal("Fluorine") is False # greater_than tests: 4 - @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='2017.7')) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.modules.salt_version.get_release_number", MagicMock(return_value="2017.7") + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_greater_than_success(self): - ''' + """ Test that the current version is newer than the codename - ''' - assert salt_version.greater_than('Nitrogen') is True + """ + assert salt_version.greater_than("Nitrogen") is True - @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='2017.7')) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='3000')) + @patch( + "salt.modules.salt_version.get_release_number", MagicMock(return_value="2017.7") + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="3000")) def test_greater_than_success_new_version(self): - ''' + """ Test that the current version is newer than the codename - ''' - assert salt_version.greater_than('Nitrogen') is True + """ + assert salt_version.greater_than("Nitrogen") is True - @patch('salt.version.SaltStackVersion.LNAMES', {'oxygen': (2018, 3)}) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch("salt.version.SaltStackVersion.LNAMES", {"oxygen": (2018, 3)}) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_greater_than_with_equal_codename(self): - ''' + """ Test that when an equal codename is passed in, the function returns False. - ''' - assert salt_version.greater_than('Oxygen') is False + """ + assert salt_version.greater_than("Oxygen") is False - @patch('salt.version.SaltStackVersion.LNAMES', {'fluorine': (2019, 2), - 'oxygen': (2018, 3)}) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.version.SaltStackVersion.LNAMES", + {"fluorine": (2019, 2), "oxygen": (2018, 3)}, + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_greater_than_with_newer_codename(self): - ''' + """ Test that when a newer codename is passed in, the function returns False. - ''' - assert salt_version.greater_than('Fluorine') is False + """ + assert salt_version.greater_than("Fluorine") is False - @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='No version assigned.')) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.modules.salt_version.get_release_number", + MagicMock(return_value="No version assigned."), + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_greater_than_unassigned(self): - ''' + """ Test that the unassigned codename is greater than the current version - ''' - assert salt_version.greater_than('Fluorine') is False + """ + assert salt_version.greater_than("Fluorine") is False # less_than tests: 4 - @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='2019.2')) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.modules.salt_version.get_release_number", MagicMock(return_value="2019.2") + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_less_than_success(self): - ''' + """ Test that when a newer codename is passed in, the function returns True. - ''' - assert salt_version.less_than('Fluorine') is True + """ + assert salt_version.less_than("Fluorine") is True - @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='3000')) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.modules.salt_version.get_release_number", MagicMock(return_value="3000") + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_less_than_success_new_version(self): - ''' + """ Test that when a newer codename is passed in, the function returns True using new version - ''' - assert salt_version.less_than('Fluorine') is True + """ + assert salt_version.less_than("Fluorine") is True - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) - @patch('salt.version.SaltStackVersion.LNAMES', {'oxygen': (2018, 3)}) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) + @patch("salt.version.SaltStackVersion.LNAMES", {"oxygen": (2018, 3)}) def test_less_than_with_equal_codename(self): - ''' + """ Test that when an equal codename is passed in, the function returns False. - ''' - assert salt_version.less_than('Oxygen') is False + """ + assert salt_version.less_than("Oxygen") is False - @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='2017.7')) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.modules.salt_version.get_release_number", MagicMock(return_value="2017.7") + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_less_than_with_older_codename(self): - ''' + """ Test that the current version is less than the codename. - ''' - assert salt_version.less_than('Nitrogen') is False + """ + assert salt_version.less_than("Nitrogen") is False - @patch('salt.modules.salt_version.get_release_number', MagicMock(return_value='No version assigned.')) - @patch('salt.version.SaltStackVersion', MagicMock(return_value='2018.3.2')) + @patch( + "salt.modules.salt_version.get_release_number", + MagicMock(return_value="No version assigned."), + ) + @patch("salt.version.SaltStackVersion", MagicMock(return_value="2018.3.2")) def test_less_than_with_unassigned_codename(self): - ''' + """ Test that when an unassigned codename greater than the current version. - ''' - assert salt_version.less_than('Fluorine') is True + """ + assert salt_version.less_than("Fluorine") is True # _check_release_cmp tests: 2 def test_check_release_cmp_no_codename(self): - ''' + """ Test that None is returned when the codename isn't found. - ''' - assert salt_version._check_release_cmp('foo') is None + """ + assert salt_version._check_release_cmp("foo") is None def test_check_release_cmp_success(self): - ''' + """ Test that an int is returned from the version compare - ''' - assert isinstance(salt_version._check_release_cmp('Oxygen'), int) + """ + assert isinstance(salt_version._check_release_cmp("Oxygen"), int) diff --git a/tests/unit/modules/test_saltcheck.py b/tests/unit/modules/test_saltcheck.py index 3b7f47fcbd2..7eae17a5dfb 100644 --- a/tests/unit/modules/test_saltcheck.py +++ b/tests/unit/modules/test_saltcheck.py @@ -1,8 +1,9 @@ # -*- coding: utf-8 -*- -'''Unit test for saltcheck execution module''' +"""Unit test for saltcheck execution module""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os.path import salt.config @@ -11,174 +12,238 @@ import salt.syspaths as syspaths # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.saltcheck module - ''' + """ def setup_loader_modules(self): # Setting the environment to be local local_opts = salt.config.minion_config( - os.path.join(syspaths.CONFIG_DIR, 'minion')) - local_opts['file_client'] = 'local' - local_opts['conf_file'] = '/etc/salt/minion' - patcher = patch('salt.config.minion_config', - MagicMock(return_value=local_opts)) + os.path.join(syspaths.CONFIG_DIR, "minion") + ) + local_opts["file_client"] = "local" + local_opts["conf_file"] = "/etc/salt/minion" + patcher = patch("salt.config.minion_config", MagicMock(return_value=local_opts)) patcher.start() self.addCleanup(patcher.stop) - return {saltcheck: {'__opts__': local_opts}} + return {saltcheck: {"__opts__": local_opts}} def test_call_salt_command(self): - '''test simple test.echo module''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'sys.list_modules': MagicMock(return_value=['module1']), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test simple test.echo module""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "sys.list_modules": MagicMock(return_value=["module1"]), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() - returned = sc_instance._call_salt_command(fun="test.echo", args=['hello'], kwargs=None) - self.assertEqual(returned, 'hello') + returned = sc_instance._call_salt_command( + fun="test.echo", args=["hello"], kwargs=None + ) + self.assertEqual(returned, "hello") def test_call_salt_command2(self): - '''test simple test.echo module again''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'sys.list_modules': MagicMock(return_value=['module1']), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test simple test.echo module again""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "sys.list_modules": MagicMock(return_value=["module1"]), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() - returned = sc_instance._call_salt_command(fun="test.echo", args=['hello'], kwargs=None) - self.assertNotEqual(returned, 'not-hello') + returned = sc_instance._call_salt_command( + fun="test.echo", args=["hello"], kwargs=None + ) + self.assertNotEqual(returned, "not-hello") def test__assert_equal1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() - aaa = {'a': 1, 'b': 2} - bbb = {'a': 1, 'b': 2} + aaa = {"a": 1, "b": 2} + bbb = {"a": 1, "b": 2} mybool = sc_instance._SaltCheck__assert_equal(aaa, bbb) self.assertTrue(mybool) def test__assert_equal2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() mybool = sc_instance._SaltCheck__assert_equal(False, True) self.assertNotEqual(mybool, True) def test__assert_not_equal1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() - aaa = {'a': 1, 'b': 2} - bbb = {'a': 1, 'b': 2, 'c': 3} + aaa = {"a": 1, "b": 2} + bbb = {"a": 1, "b": 2, "c": 3} mybool = sc_instance._SaltCheck__assert_not_equal(aaa, bbb) self.assertTrue(mybool) def test__assert_not_equal2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() - aaa = {'a': 1, 'b': 2} - bbb = {'a': 1, 'b': 2} + aaa = {"a": 1, "b": 2} + bbb = {"a": 1, "b": 2} mybool = sc_instance._SaltCheck__assert_not_equal(aaa, bbb) self.assertNotEqual(mybool, True) def test__assert_true1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() mybool = sc_instance._SaltCheck__assert_equal(True, True) self.assertTrue(mybool) def test__assert_true2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() mybool = sc_instance._SaltCheck__assert_equal(False, True) self.assertNotEqual(mybool, True) def test__assert_false1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() mybool = sc_instance._SaltCheck__assert_false(False) self.assertTrue(mybool) def test__assert_false2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() mybool = sc_instance._SaltCheck__assert_false(True) self.assertNotEqual(mybool, True) def test__assert_in1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = "bob" - mylist = ['alice', 'bob', 'charles', 'dana'] + mylist = ["alice", "bob", "charles", "dana"] mybool = sc_instance._SaltCheck__assert_in(aaa, mylist) self.assertTrue(mybool, True) def test__assert_in2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = "elaine" - mylist = ['alice', 'bob', 'charles', 'dana'] + mylist = ["alice", "bob", "charles", "dana"] mybool = sc_instance._SaltCheck__assert_in(aaa, mylist) self.assertNotEqual(mybool, True) def test__assert_not_in1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = "elaine" - mylist = ['alice', 'bob', 'charles', 'dana'] + mylist = ["alice", "bob", "charles", "dana"] mybool = sc_instance._SaltCheck__assert_not_in(aaa, mylist) self.assertTrue(mybool, True) def test__assert_not_in2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = "bob" - mylist = ['alice', 'bob', 'charles', 'dana'] + mylist = ["alice", "bob", "charles", "dana"] mybool = sc_instance._SaltCheck__assert_not_in(aaa, mylist) self.assertNotEqual(mybool, True) def test__assert_greater1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 110 bbb = 100 @@ -186,10 +251,14 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(mybool, True) def test__assert_greater2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 100 bbb = 110 @@ -197,10 +266,14 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertNotEqual(mybool, True) def test__assert_greater3(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 100 bbb = 100 @@ -208,10 +281,14 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertNotEqual(mybool, True) def test__assert_greater_equal1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 110 bbb = 100 @@ -219,10 +296,14 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(mybool, True) def test__assert_greater_equal2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 100 bbb = 110 @@ -230,21 +311,29 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertNotEqual(mybool, True) def test__assert_greater_equal3(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 100 bbb = 100 mybool = sc_instance._SaltCheck__assert_greater_equal(aaa, bbb) - self.assertEqual(mybool, 'Pass') + self.assertEqual(mybool, "Pass") def test__assert_less1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 99 bbb = 100 @@ -252,10 +341,14 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(mybool, True) def test__assert_less2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 110 bbb = 99 @@ -263,10 +356,14 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertNotEqual(mybool, True) def test__assert_less3(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 100 bbb = 100 @@ -274,10 +371,14 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertNotEqual(mybool, True) def test__assert_less_equal1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 99 bbb = 100 @@ -285,10 +386,14 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(mybool, True) def test__assert_less_equal2(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 110 bbb = 99 @@ -296,82 +401,112 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): self.assertNotEqual(mybool, True) def test__assert_less_equal3(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() aaa = 100 bbb = 100 mybool = sc_instance._SaltCheck__assert_less_equal(aaa, bbb) - self.assertEqual(mybool, 'Pass') + self.assertEqual(mybool, "Pass") def test__assert_empty(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() mybool = sc_instance._SaltCheck__assert_empty("") - self.assertEqual(mybool, 'Pass') + self.assertEqual(mybool, "Pass") def test__assert_empty_fail(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() mybool = sc_instance._SaltCheck__assert_empty("data") - self.assertNotEqual(mybool, 'Pass') + self.assertNotEqual(mybool, "Pass") def test__assert__not_empty(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() mybool = sc_instance._SaltCheck__assert_not_empty("data") - self.assertEqual(mybool, 'Pass') + self.assertEqual(mybool, "Pass") def test__assert__not_empty_fail(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'cp.cache_master': MagicMock(return_value=[True]) - }): + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): sc_instance = saltcheck.SaltCheck() mybool = sc_instance._SaltCheck__assert_not_empty("") - self.assertNotEqual(mybool, 'Pass') + self.assertNotEqual(mybool, "Pass") def test_run_test_1(self): - '''test''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value=True), - 'sys.list_modules': MagicMock(return_value=['test']), - 'sys.list_functions': MagicMock(return_value=['test.echo']), - 'cp.cache_master': MagicMock(return_value=[True])}): - returned = saltcheck.run_test(test={"module_and_function": "test.echo", - "assertion": "assertEqual", - "expected_return": "This works!", - "args": ["This works!"] - }) - self.assertEqual(returned['status'], 'Pass') + """test""" + with patch.dict( + saltcheck.__salt__, + { + "config.get": MagicMock(return_value=True), + "sys.list_modules": MagicMock(return_value=["test"]), + "sys.list_functions": MagicMock(return_value=["test.echo"]), + "cp.cache_master": MagicMock(return_value=[True]), + }, + ): + returned = saltcheck.run_test( + test={ + "module_and_function": "test.echo", + "assertion": "assertEqual", + "expected_return": "This works!", + "args": ["This works!"], + } + ) + self.assertEqual(returned["status"], "Pass") def test_report_highstate_tests(self): - '''test report_highstate_tests''' - expected_output = {'TEST REPORT RESULTS': { - 'States missing tests': ['state1'], - 'Missing Tests': 1, - 'States with tests': ['found'] - }} - with patch('salt.modules.saltcheck._get_top_states') as mocked_get_top: - mocked_get_top.return_value = ['state1', 'found'] - with patch('salt.modules.saltcheck.StateTestLoader') as mocked_stl: + """test report_highstate_tests""" + expected_output = { + "TEST REPORT RESULTS": { + "States missing tests": ["state1"], + "Missing Tests": 1, + "States with tests": ["found"], + } + } + with patch("salt.modules.saltcheck._get_top_states") as mocked_get_top: + mocked_get_top.return_value = ["state1", "found"] + with patch("salt.modules.saltcheck.StateTestLoader") as mocked_stl: instance = mocked_stl.return_value - instance.found_states = ['found'] + instance.found_states = ["found"] returned = saltcheck.report_highstate_tests() self.assertEqual(returned, expected_output) def test_validation(self): - '''test validation of tests''' + """test validation of tests""" sc_instance = saltcheck.SaltCheck() # Fail on empty test @@ -382,220 +517,287 @@ class SaltcheckTestCase(TestCase, LoaderModuleMockMixin): # Succeed on standard test test_dict = { - 'module_and_function': 'test.echo', - 'args': ["hello"], - 'kwargs': {}, - 'assertion': 'assertEqual', - 'expected_return': 'hello' - } + "module_and_function": "test.echo", + "args": ["hello"], + "kwargs": {}, + "assertion": "assertEqual", + "expected_return": "hello", + } expected_return = True - with patch.dict(saltcheck.__salt__, {'sys.list_modules': MagicMock(return_value=['test']), - 'sys.list_functions': MagicMock(return_value=['test.echo']) - }): + with patch.dict( + saltcheck.__salt__, + { + "sys.list_modules": MagicMock(return_value=["test"]), + "sys.list_functions": MagicMock(return_value=["test.echo"]), + }, + ): val_ret = sc_instance._SaltCheck__is_valid_test(test_dict) self.assertEqual(val_ret, expected_return) # Succeed on standard test with older expected-return syntax test_dict = { - 'module_and_function': 'test.echo', - 'args': ["hello"], - 'kwargs': {}, - 'assertion': 'assertEqual', - 'expected-return': 'hello' - } + "module_and_function": "test.echo", + "args": ["hello"], + "kwargs": {}, + "assertion": "assertEqual", + "expected-return": "hello", + } expected_return = True - with patch.dict(saltcheck.__salt__, {'sys.list_modules': MagicMock(return_value=['test']), - 'sys.list_functions': MagicMock(return_value=['test.echo']) - }): + with patch.dict( + saltcheck.__salt__, + { + "sys.list_modules": MagicMock(return_value=["test"]), + "sys.list_functions": MagicMock(return_value=["test.echo"]), + }, + ): val_ret = sc_instance._SaltCheck__is_valid_test(test_dict) self.assertEqual(val_ret, expected_return) # Do not require expected_return for some assertions - assertions = ["assertEmpty", - "assertNotEmpty", - "assertTrue", - "assertFalse"] + assertions = ["assertEmpty", "assertNotEmpty", "assertTrue", "assertFalse"] for assertion in assertions: - test_dict = { - 'module_and_function': 'test.echo', - 'args': ["hello"] - } - test_dict['assertion'] = assertion + test_dict = {"module_and_function": "test.echo", "args": ["hello"]} + test_dict["assertion"] = assertion expected_return = True - with patch.dict(saltcheck.__salt__, {'sys.list_modules': MagicMock(return_value=['test']), - 'sys.list_functions': MagicMock(return_value=['test.echo']) - }): + with patch.dict( + saltcheck.__salt__, + { + "sys.list_modules": MagicMock(return_value=["test"]), + "sys.list_functions": MagicMock(return_value=["test.echo"]), + }, + ): val_ret = sc_instance._SaltCheck__is_valid_test(test_dict) self.assertEqual(val_ret, expected_return) # Fail on invalid module test_dict = { - 'module_and_function': 'broken.echo', - 'args': ["hello"], - 'kwargs': {}, - 'assertion': 'assertEqual', - 'expected_return': 'hello' - } + "module_and_function": "broken.echo", + "args": ["hello"], + "kwargs": {}, + "assertion": "assertEqual", + "expected_return": "hello", + } expected_return = False - with patch.dict(saltcheck.__salt__, {'sys.list_modules': MagicMock(return_value=['test']), - 'sys.list_functions': MagicMock(return_value=['test.echo']) - }): + with patch.dict( + saltcheck.__salt__, + { + "sys.list_modules": MagicMock(return_value=["test"]), + "sys.list_functions": MagicMock(return_value=["test.echo"]), + }, + ): val_ret = sc_instance._SaltCheck__is_valid_test(test_dict) self.assertEqual(val_ret, expected_return) # Fail on invalid function test_dict = { - 'module_and_function': 'test.broken', - 'args': ["hello"], - 'kwargs': {}, - 'assertion': 'assertEqual', - 'expected_return': 'hello' - } + "module_and_function": "test.broken", + "args": ["hello"], + "kwargs": {}, + "assertion": "assertEqual", + "expected_return": "hello", + } expected_return = False - with patch.dict(saltcheck.__salt__, {'sys.list_modules': MagicMock(return_value=['test']), - 'sys.list_functions': MagicMock(return_value=['test.echo']) - }): + with patch.dict( + saltcheck.__salt__, + { + "sys.list_modules": MagicMock(return_value=["test"]), + "sys.list_functions": MagicMock(return_value=["test.echo"]), + }, + ): val_ret = sc_instance._SaltCheck__is_valid_test(test_dict) self.assertEqual(val_ret, expected_return) # Fail on missing expected_return test_dict = { - 'module_and_function': 'test.echo', - 'args': ["hello"], - 'kwargs': {}, - 'assertion': 'assertEqual' - } + "module_and_function": "test.echo", + "args": ["hello"], + "kwargs": {}, + "assertion": "assertEqual", + } expected_return = False - with patch.dict(saltcheck.__salt__, {'sys.list_modules': MagicMock(return_value=['test']), - 'sys.list_functions': MagicMock(return_value=['test.echo']) - }): + with patch.dict( + saltcheck.__salt__, + { + "sys.list_modules": MagicMock(return_value=["test"]), + "sys.list_functions": MagicMock(return_value=["test.echo"]), + }, + ): val_ret = sc_instance._SaltCheck__is_valid_test(test_dict) self.assertEqual(val_ret, expected_return) # Fail on empty expected_return test_dict = { - 'module_and_function': 'test.echo', - 'args': ["hello"], - 'kwargs': {}, - 'assertion': 'assertEqual', - 'expected_return': None - } + "module_and_function": "test.echo", + "args": ["hello"], + "kwargs": {}, + "assertion": "assertEqual", + "expected_return": None, + } expected_return = False - with patch.dict(saltcheck.__salt__, {'sys.list_modules': MagicMock(return_value=['test']), - 'sys.list_functions': MagicMock(return_value=['test.echo']) - }): + with patch.dict( + saltcheck.__salt__, + { + "sys.list_modules": MagicMock(return_value=["test"]), + "sys.list_functions": MagicMock(return_value=["test.echo"]), + }, + ): val_ret = sc_instance._SaltCheck__is_valid_test(test_dict) self.assertEqual(val_ret, expected_return) # Succeed on m_and_f saltcheck.state_apply with only args - test_dict = { - 'module_and_function': 'saltcheck.state_apply', - 'args': ["common"] - } + test_dict = {"module_and_function": "saltcheck.state_apply", "args": ["common"]} expected_return = True - with patch.dict(saltcheck.__salt__, {'sys.list_modules': MagicMock(return_value=['saltcheck']), - 'sys.list_functions': MagicMock(return_value=['saltcheck.state_apply']) - }): + with patch.dict( + saltcheck.__salt__, + { + "sys.list_modules": MagicMock(return_value=["saltcheck"]), + "sys.list_functions": MagicMock(return_value=["saltcheck.state_apply"]), + }, + ): val_ret = sc_instance._SaltCheck__is_valid_test(test_dict) self.assertEqual(val_ret, expected_return) def test_sls_path_generation(self): - '''test generation of sls paths''' - with patch.dict(saltcheck.__salt__, {'config.get': MagicMock(return_value='saltcheck-tests')}): + """test generation of sls paths""" + with patch.dict( + saltcheck.__salt__, + {"config.get": MagicMock(return_value="saltcheck-tests")}, + ): testLoader = saltcheck.StateTestLoader() - state_name = 'teststate' - expected_return = ['salt://teststate/saltcheck-tests', - 'salt:///saltcheck-tests'] + state_name = "teststate" + expected_return = [ + "salt://teststate/saltcheck-tests", + "salt:///saltcheck-tests", + ] ret = testLoader._generate_sls_path(state_name) self.assertEqual(ret, expected_return) - state_name = 'teststate.long.path' - expected_return = ['salt://teststate/long/path/saltcheck-tests', - 'salt://teststate/long/saltcheck-tests', - 'salt://teststate/saltcheck-tests'] + state_name = "teststate.long.path" + expected_return = [ + "salt://teststate/long/path/saltcheck-tests", + "salt://teststate/long/saltcheck-tests", + "salt://teststate/saltcheck-tests", + ] ret = testLoader._generate_sls_path(state_name) self.assertEqual(ret, expected_return) - state_name = 'teststate.really.long.path' - expected_return = ['salt://teststate/really/long/path/saltcheck-tests', - 'salt://teststate/really/long/saltcheck-tests', - 'salt://teststate/saltcheck-tests'] + state_name = "teststate.really.long.path" + expected_return = [ + "salt://teststate/really/long/path/saltcheck-tests", + "salt://teststate/really/long/saltcheck-tests", + "salt://teststate/saltcheck-tests", + ] ret = testLoader._generate_sls_path(state_name) self.assertEqual(ret, expected_return) def test_generate_output(self): # passing states - sc_results = {'a_state': - {'test_id1': {'status': 'Pass', 'duration': 1.987}, - 'test_id2': {'status': 'Pass', 'duration': 1.123}}} - expected_output = [{'a_state': - {'test_id1': {'status': 'Pass', 'duration': 1.987}, - 'test_id2': {'status': 'Pass', 'duration': 1.123}}}, - {'TEST RESULTS': - {'Execution Time': 3.11, - 'Passed': 2, - 'Failed': 0, - 'Skipped': 0, - 'Missing Tests': 0 - } - }] + sc_results = { + "a_state": { + "test_id1": {"status": "Pass", "duration": 1.987}, + "test_id2": {"status": "Pass", "duration": 1.123}, + } + } + expected_output = [ + { + "a_state": { + "test_id1": {"status": "Pass", "duration": 1.987}, + "test_id2": {"status": "Pass", "duration": 1.123}, + } + }, + { + "TEST RESULTS": { + "Execution Time": 3.11, + "Passed": 2, + "Failed": 0, + "Skipped": 0, + "Missing Tests": 0, + } + }, + ] ret = saltcheck._generate_out_list(sc_results) self.assertEqual(ret, expected_output) # Skipped - sc_results = {'a_state': - {'test_id1': {'status': 'Skip', 'duration': 1.987}, - 'test_id2': {'status': 'Pass', 'duration': 1.123}}} - expected_output = [{'a_state': - {'test_id1': {'status': 'Skip', 'duration': 1.987}, - 'test_id2': {'status': 'Pass', 'duration': 1.123}}}, - {'TEST RESULTS': - {'Execution Time': 3.11, - 'Passed': 1, - 'Failed': 0, - 'Skipped': 1, - 'Missing Tests': 0 - } - }] + sc_results = { + "a_state": { + "test_id1": {"status": "Skip", "duration": 1.987}, + "test_id2": {"status": "Pass", "duration": 1.123}, + } + } + expected_output = [ + { + "a_state": { + "test_id1": {"status": "Skip", "duration": 1.987}, + "test_id2": {"status": "Pass", "duration": 1.123}, + } + }, + { + "TEST RESULTS": { + "Execution Time": 3.11, + "Passed": 1, + "Failed": 0, + "Skipped": 1, + "Missing Tests": 0, + } + }, + ] ret = saltcheck._generate_out_list(sc_results) self.assertEqual(ret, expected_output) # Failed (does not test setting __context__) - sc_results = {'a_state': - {'test_id1': {'status': 'Failed', 'duration': 1.987}, - 'test_id2': {'status': 'Pass', 'duration': 1.123}}} - expected_output = [{'a_state': - {'test_id1': {'status': 'Failed', 'duration': 1.987}, - 'test_id2': {'status': 'Pass', 'duration': 1.123}}}, - {'TEST RESULTS': - {'Execution Time': 3.11, - 'Passed': 1, - 'Failed': 1, - 'Skipped': 0, - 'Missing Tests': 0 - } - }] + sc_results = { + "a_state": { + "test_id1": {"status": "Failed", "duration": 1.987}, + "test_id2": {"status": "Pass", "duration": 1.123}, + } + } + expected_output = [ + { + "a_state": { + "test_id1": {"status": "Failed", "duration": 1.987}, + "test_id2": {"status": "Pass", "duration": 1.123}, + } + }, + { + "TEST RESULTS": { + "Execution Time": 3.11, + "Passed": 1, + "Failed": 1, + "Skipped": 0, + "Missing Tests": 0, + } + }, + ] ret = saltcheck._generate_out_list(sc_results) self.assertEqual(ret, expected_output) # missing states - sc_results = {'a_state': - {'test_id1': {'status': 'Pass', 'duration': 1.987}, - 'test_id2': {'status': 'Pass', 'duration': 1.123}}, - 'b_state': {} - } - expected_output = [{'a_state': - {'test_id1': {'status': 'Pass', 'duration': 1.987}, - 'test_id2': {'status': 'Pass', 'duration': 1.123}}}, - {'b_state': {}}, - {'TEST RESULTS': - {'Execution Time': 3.11, - 'Passed': 2, - 'Failed': 0, - 'Skipped': 0, - 'Missing Tests': 1 - } - }] + sc_results = { + "a_state": { + "test_id1": {"status": "Pass", "duration": 1.987}, + "test_id2": {"status": "Pass", "duration": 1.123}, + }, + "b_state": {}, + } + expected_output = [ + { + "a_state": { + "test_id1": {"status": "Pass", "duration": 1.987}, + "test_id2": {"status": "Pass", "duration": 1.123}, + } + }, + {"b_state": {}}, + { + "TEST RESULTS": { + "Execution Time": 3.11, + "Passed": 2, + "Failed": 0, + "Skipped": 0, + "Missing Tests": 1, + } + }, + ] ret = saltcheck._generate_out_list(sc_results) self.assertEqual(ret, expected_output) diff --git a/tests/unit/modules/test_saltcloudmod.py b/tests/unit/modules/test_saltcloudmod.py index 823ba1c27d5..26c26d2e20f 100644 --- a/tests/unit/modules/test_saltcloudmod.py +++ b/tests/unit/modules/test_saltcloudmod.py @@ -1,28 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.saltcloudmod as saltcloudmod import salt.utils.json +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SaltcloudmodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.saltcloudmod - ''' + """ + def setup_loader_modules(self): return {saltcloudmod: {}} @@ -30,16 +28,14 @@ class SaltcloudmodTestCase(TestCase, LoaderModuleMockMixin): self.mock_json_loads = MagicMock(side_effect=ValueError()) def test_create(self): - ''' + """ Test if create the named vm - ''' - mock = MagicMock(return_value='''{"foo": "bar"}''') - with patch.dict(saltcloudmod.__salt__, {'cmd.run_stdout': mock}): - self.assertTrue( - saltcloudmod.create("webserver", "rackspace_centos_512")) + """ + mock = MagicMock(return_value="""{"foo": "bar"}""") + with patch.dict(saltcloudmod.__salt__, {"cmd.run_stdout": mock}): + self.assertTrue(saltcloudmod.create("webserver", "rackspace_centos_512")) - with patch.object(salt.utils.json, 'loads', self.mock_json_loads): + with patch.object(salt.utils.json, "loads", self.mock_json_loads): self.assertDictEqual( - saltcloudmod.create("webserver", "rackspace_centos_512"), - {} + saltcloudmod.create("webserver", "rackspace_centos_512"), {} ) diff --git a/tests/unit/modules/test_schedule.py b/tests/unit/modules/test_schedule.py index 2ac3a2d5c30..e82f2c5abd8 100644 --- a/tests/unit/modules/test_schedule.py +++ b/tests/unit/modules/test_schedule.py @@ -1,38 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.schedule as schedule from salt.utils.event import SaltEvent +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase -JOB1 = {'function': 'test.ping', 'maxrunning': 1, 'name': 'job1', - 'jid_include': True, 'enabled': True} +JOB1 = { + "function": "test.ping", + "maxrunning": 1, + "name": "job1", + "jid_include": True, + "enabled": True, +} class ScheduleTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.schedule - ''' + """ @classmethod def setUpClass(cls): - cls.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + cls.sock_dir = os.path.join(RUNTIME_VARS.TMP, "test-socks") def setup_loader_modules(self): return {schedule: {}} @@ -40,400 +42,487 @@ class ScheduleTestCase(TestCase, LoaderModuleMockMixin): # 'purge' function tests: 1 def test_purge(self): - ''' + """ Test if it purge all the jobs currently scheduled on the minion. - ''' - with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': self.sock_dir}): + """ + with patch.dict(schedule.__opts__, {"schedule": {}, "sock_dir": self.sock_dir}): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.purge(), {'comment': ['Deleted job: schedule from schedule.'], - 'result': True}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.purge(), + { + "comment": ["Deleted job: schedule from schedule."], + "result": True, + }, + ) # 'delete' function tests: 1 def test_delete(self): - ''' + """ Test if it delete a job from the minion's schedule. - ''' - with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': self.sock_dir}): + """ + with patch.dict(schedule.__opts__, {"schedule": {}, "sock_dir": self.sock_dir}): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.delete('job1'), - {'comment': 'Job job1 does not exist.', - 'result': False}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.delete("job1"), + {"comment": "Job job1 does not exist.", "result": False}, + ) # 'build_schedule_item' function tests: 1 def test_build_schedule_item(self): - ''' + """ Test if it build a schedule job. - ''' - comment = 'Unable to use "seconds", "minutes", "hours", ' \ - 'or "days" with "when" or "cron" options.' - comment1 = 'Unable to use "when" and "cron" ' \ - 'options together. Ignoring.' - with patch.dict(schedule.__opts__, {'job1': {}}): - self.assertDictEqual(schedule.build_schedule_item(''), - {'comment': 'Job name is required.', - 'result': False}) + """ + comment = ( + 'Unable to use "seconds", "minutes", "hours", ' + 'or "days" with "when" or "cron" options.' + ) + comment1 = 'Unable to use "when" and "cron" ' "options together. Ignoring." + with patch.dict(schedule.__opts__, {"job1": {}}): + self.assertDictEqual( + schedule.build_schedule_item(""), + {"comment": "Job name is required.", "result": False}, + ) - self.assertDictEqual(schedule.build_schedule_item - ('job1', function='test.ping'), - {'function': 'test.ping', 'maxrunning': 1, - 'name': 'job1', 'jid_include': True, - 'enabled': True}) + self.assertDictEqual( + schedule.build_schedule_item("job1", function="test.ping"), + { + "function": "test.ping", + "maxrunning": 1, + "name": "job1", + "jid_include": True, + "enabled": True, + }, + ) - self.assertDictEqual(schedule.build_schedule_item - ('job1', function='test.ping', seconds=3600, - when='2400'), - {'comment': comment, 'result': False}) + self.assertDictEqual( + schedule.build_schedule_item( + "job1", function="test.ping", seconds=3600, when="2400" + ), + {"comment": comment, "result": False}, + ) - self.assertDictEqual(schedule.build_schedule_item - ('job1', function='test.ping', when='2400', - cron='2'), - {'comment': comment1, 'result': False}) + self.assertDictEqual( + schedule.build_schedule_item( + "job1", function="test.ping", when="2400", cron="2" + ), + {"comment": comment1, "result": False}, + ) # 'build_schedule_item_invalid_when' function tests: 1 def test_build_schedule_item_invalid_when(self): - ''' + """ Test if it build a schedule job. - ''' + """ comment = 'Schedule item garbage for "when" in invalid.' - with patch.dict(schedule.__opts__, {'job1': {}}): - self.assertDictEqual(schedule.build_schedule_item - ('job1', function='test.ping', when='garbage'), - {'comment': comment, 'result': False}) + with patch.dict(schedule.__opts__, {"job1": {}}): + self.assertDictEqual( + schedule.build_schedule_item( + "job1", function="test.ping", when="garbage" + ), + {"comment": comment, "result": False}, + ) # 'add' function tests: 1 def test_add(self): - ''' + """ Test if it add a job to the schedule. - ''' - comm1 = 'Job job1 already exists in schedule.' - comm2 = 'Error: Unable to use "seconds", "minutes", "hours", ' \ - 'or "days" with "when" or "cron" options.' + """ + comm1 = "Job job1 already exists in schedule." + comm2 = ( + 'Error: Unable to use "seconds", "minutes", "hours", ' + 'or "days" with "when" or "cron" options.' + ) comm3 = 'Unable to use "when" and "cron" options together. Ignoring.' - comm4 = 'Job: job2 would be added to schedule.' - with patch.dict(schedule.__opts__, {'schedule': {'job1': 'salt'}, 'sock_dir': self.sock_dir}): + comm4 = "Job: job2 would be added to schedule." + with patch.dict( + schedule.__opts__, {"schedule": {"job1": "salt"}, "sock_dir": self.sock_dir} + ): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {'job1': {'salt': 'salt'}}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.add('job1'), - {'comment': comm1, 'result': False}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {"job1": {"salt": "salt"}}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.add("job1"), {"comment": comm1, "result": False} + ) - _ret_value = {'complete': True, 'schedule': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.add('job2', function='test.ping', - seconds=3600, when='2400'), - {'comment': comm2, 'result': False}) + _ret_value = {"complete": True, "schedule": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.add( + "job2", function="test.ping", seconds=3600, when="2400" + ), + {"comment": comm2, "result": False}, + ) - _ret_value = {'complete': True, 'schedule': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.add('job2', function='test.ping', - when='2400', cron='2'), - {'comment': comm3, 'result': False}) - _ret_value = {'complete': True, 'schedule': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.add('job2', function='test.ping', - test=True), - {'comment': comm4, 'result': True}) + _ret_value = {"complete": True, "schedule": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.add( + "job2", function="test.ping", when="2400", cron="2" + ), + {"comment": comm3, "result": False}, + ) + _ret_value = {"complete": True, "schedule": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.add("job2", function="test.ping", test=True), + {"comment": comm4, "result": True}, + ) # 'run_job' function tests: 1 def test_run_job(self): - ''' + """ Test if it run a scheduled job on the minion immediately. - ''' - with patch.dict(schedule.__opts__, {'schedule': {'job1': JOB1}, 'sock_dir': self.sock_dir}): + """ + with patch.dict( + schedule.__opts__, {"schedule": {"job1": JOB1}, "sock_dir": self.sock_dir} + ): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {'job1': JOB1}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.run_job('job1'), - {'comment': 'Scheduling Job job1 on minion.', - 'result': True}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {"job1": JOB1}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.run_job("job1"), + {"comment": "Scheduling Job job1 on minion.", "result": True}, + ) # 'enable_job' function tests: 1 def test_enable_job(self): - ''' + """ Test if it enable a job in the minion's schedule. - ''' - with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': self.sock_dir}): + """ + with patch.dict(schedule.__opts__, {"schedule": {}, "sock_dir": self.sock_dir}): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.enable_job('job1'), - {'comment': 'Job job1 does not exist.', - 'result': False}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.enable_job("job1"), + {"comment": "Job job1 does not exist.", "result": False}, + ) # 'disable_job' function tests: 1 def test_disable_job(self): - ''' + """ Test if it disable a job in the minion's schedule. - ''' - with patch.dict(schedule.__opts__, {'schedule': {}, 'sock_dir': self.sock_dir}): + """ + with patch.dict(schedule.__opts__, {"schedule": {}, "sock_dir": self.sock_dir}): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.disable_job('job1'), - {'comment': 'Job job1 does not exist.', - 'result': False}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.disable_job("job1"), + {"comment": "Job job1 does not exist.", "result": False}, + ) # 'save' function tests: 1 def test_save(self): - ''' + """ Test if it save all scheduled jobs on the minion. - ''' - comm1 = 'Schedule (non-pillar items) saved.' - with patch.dict(schedule.__opts__, {'schedule': {}, - 'default_include': '/tmp', - 'sock_dir': self.sock_dir}): + """ + comm1 = "Schedule (non-pillar items) saved." + with patch.dict( + schedule.__opts__, + {"schedule": {}, "default_include": "/tmp", "sock_dir": self.sock_dir}, + ): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - self.assertDictEqual(schedule.save(), - {'comment': comm1, 'result': True}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + self.assertDictEqual( + schedule.save(), {"comment": comm1, "result": True} + ) # 'enable' function tests: 1 def test_enable(self): - ''' + """ Test if it enable all scheduled jobs on the minion. - ''' - self.assertDictEqual(schedule.enable(test=True), - {'comment': 'Schedule would be enabled.', - 'result': True}) + """ + self.assertDictEqual( + schedule.enable(test=True), + {"comment": "Schedule would be enabled.", "result": True}, + ) # 'disable' function tests: 1 def test_disable(self): - ''' + """ Test if it disable all scheduled jobs on the minion. - ''' - self.assertDictEqual(schedule.disable(test=True), - {'comment': 'Schedule would be disabled.', - 'result': True}) + """ + self.assertDictEqual( + schedule.disable(test=True), + {"comment": "Schedule would be disabled.", "result": True}, + ) # 'move' function tests: 1 def test_move(self): - ''' + """ Test if it move scheduled job to another minion or minions. - ''' - comm1 = 'no servers answered the published schedule.add command' - comm2 = 'the following minions return False' - comm3 = 'Moved Job job1 from schedule.' - with patch.dict(schedule.__opts__, {'schedule': {'job1': JOB1}, 'sock_dir': self.sock_dir}): + """ + comm1 = "no servers answered the published schedule.add command" + comm2 = "the following minions return False" + comm3 = "Moved Job job1 from schedule." + with patch.dict( + schedule.__opts__, {"schedule": {"job1": JOB1}, "sock_dir": self.sock_dir} + ): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {'job1': JOB1}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {"job1": JOB1}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): mock = MagicMock(return_value={}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): - self.assertDictEqual(schedule.move('job1', 'minion1'), - {'comment': comm1, 'result': True}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): + self.assertDictEqual( + schedule.move("job1", "minion1"), + {"comment": comm1, "result": True}, + ) - mock = MagicMock(return_value={'minion1': ''}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): - self.assertDictEqual(schedule.move('job1', 'minion1'), - {'comment': comm2, 'minions': ['minion1'], - 'result': True}) + mock = MagicMock(return_value={"minion1": ""}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): + self.assertDictEqual( + schedule.move("job1", "minion1"), + {"comment": comm2, "minions": ["minion1"], "result": True}, + ) - mock = MagicMock(return_value={'minion1': 'job1'}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): + mock = MagicMock(return_value={"minion1": "job1"}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - self.assertDictEqual(schedule.move('job1', 'minion1'), - {'comment': comm3, - 'minions': ['minion1'], - 'result': True}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + self.assertDictEqual( + schedule.move("job1", "minion1"), + { + "comment": comm3, + "minions": ["minion1"], + "result": True, + }, + ) - self.assertDictEqual(schedule.move('job3', 'minion1'), - {'comment': 'Job job3 does not exist.', - 'result': False}) + self.assertDictEqual( + schedule.move("job3", "minion1"), + {"comment": "Job job3 does not exist.", "result": False}, + ) - mock = MagicMock(side_effect=[{}, {'job1': {}}]) - with patch.dict(schedule.__opts__, {'schedule': mock, 'sock_dir': self.sock_dir}): + mock = MagicMock(side_effect=[{}, {"job1": {}}]) + with patch.dict( + schedule.__opts__, {"schedule": mock, "sock_dir": self.sock_dir} + ): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {'job1': JOB1}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - with patch.dict(schedule.__pillar__, {'schedule': {'job1': JOB1}}): + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {"job1": JOB1}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + with patch.dict(schedule.__pillar__, {"schedule": {"job1": JOB1}}): mock = MagicMock(return_value={}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): - self.assertDictEqual(schedule.move('job1', 'minion1'), - {'comment': comm1, - 'result': True}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): + self.assertDictEqual( + schedule.move("job1", "minion1"), + {"comment": comm1, "result": True}, + ) - mock = MagicMock(return_value={'minion1': ''}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): - self.assertDictEqual(schedule.move('job1', 'minion1'), - {'comment': comm2, - 'minions': ['minion1'], - 'result': True}) + mock = MagicMock(return_value={"minion1": ""}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): + self.assertDictEqual( + schedule.move("job1", "minion1"), + { + "comment": comm2, + "minions": ["minion1"], + "result": True, + }, + ) - mock = MagicMock(return_value={'minion1': 'job1'}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): + mock = MagicMock(return_value={"minion1": "job1"}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - self.assertDictEqual(schedule.move('job1', 'minion1'), - {'comment': comm3, - 'minions': ['minion1'], - 'result': True}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + self.assertDictEqual( + schedule.move("job1", "minion1"), + { + "comment": comm3, + "minions": ["minion1"], + "result": True, + }, + ) # 'copy' function tests: 1 def test_copy(self): - ''' + """ Test if it copy scheduled job to another minion or minions. - ''' - comm1 = 'no servers answered the published schedule.add command' - comm2 = 'the following minions return False' - comm3 = 'Copied Job job1 from schedule to minion(s).' - with patch.dict(schedule.__opts__, {'schedule': {'job1': JOB1}, 'sock_dir': self.sock_dir}): + """ + comm1 = "no servers answered the published schedule.add command" + comm2 = "the following minions return False" + comm3 = "Copied Job job1 from schedule to minion(s)." + with patch.dict( + schedule.__opts__, {"schedule": {"job1": JOB1}, "sock_dir": self.sock_dir} + ): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {'job1': {'job1': JOB1}}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {"job1": {"job1": JOB1}}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): mock = MagicMock(return_value={}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): - self.assertDictEqual(schedule.copy('job1', 'minion1'), - {'comment': comm1, 'result': True}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): + self.assertDictEqual( + schedule.copy("job1", "minion1"), + {"comment": comm1, "result": True}, + ) - mock = MagicMock(return_value={'minion1': ''}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): - self.assertDictEqual(schedule.copy('job1', 'minion1'), - {'comment': comm2, 'minions': ['minion1'], - 'result': True}) + mock = MagicMock(return_value={"minion1": ""}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): + self.assertDictEqual( + schedule.copy("job1", "minion1"), + {"comment": comm2, "minions": ["minion1"], "result": True}, + ) - mock = MagicMock(return_value={'minion1': 'job1'}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): + mock = MagicMock(return_value={"minion1": "job1"}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - self.assertDictEqual(schedule.copy('job1', 'minion1'), - {'comment': comm3, - 'minions': ['minion1'], - 'result': True}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + self.assertDictEqual( + schedule.copy("job1", "minion1"), + { + "comment": comm3, + "minions": ["minion1"], + "result": True, + }, + ) - self.assertDictEqual(schedule.copy('job3', 'minion1'), - {'comment': 'Job job3 does not exist.', - 'result': False}) + self.assertDictEqual( + schedule.copy("job3", "minion1"), + {"comment": "Job job3 does not exist.", "result": False}, + ) - mock = MagicMock(side_effect=[{}, {'job1': {}}]) - with patch.dict(schedule.__opts__, {'schedule': mock, 'sock_dir': self.sock_dir}): - with patch.dict(schedule.__pillar__, {'schedule': {'job1': JOB1}}): + mock = MagicMock(side_effect=[{}, {"job1": {}}]) + with patch.dict( + schedule.__opts__, {"schedule": mock, "sock_dir": self.sock_dir} + ): + with patch.dict(schedule.__pillar__, {"schedule": {"job1": JOB1}}): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {'job1': {'job1': JOB1}}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = { + "complete": True, + "schedule": {"job1": {"job1": JOB1}}, + } + with patch.object(SaltEvent, "get_event", return_value=_ret_value): mock = MagicMock(return_value={}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): - self.assertDictEqual(schedule.copy('job1', 'minion1'), - {'comment': comm1, 'result': True}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): + self.assertDictEqual( + schedule.copy("job1", "minion1"), + {"comment": comm1, "result": True}, + ) - mock = MagicMock(return_value={'minion1': ''}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): - self.assertDictEqual(schedule.copy('job1', 'minion1'), - {'comment': comm2, - 'minions': ['minion1'], - 'result': True}) + mock = MagicMock(return_value={"minion1": ""}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): + self.assertDictEqual( + schedule.copy("job1", "minion1"), + { + "comment": comm2, + "minions": ["minion1"], + "result": True, + }, + ) - mock = MagicMock(return_value={'minion1': 'job1'}) - with patch.dict(schedule.__salt__, {'publish.publish': mock}): + mock = MagicMock(return_value={"minion1": "job1"}) + with patch.dict(schedule.__salt__, {"publish.publish": mock}): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - self.assertDictEqual(schedule.copy('job1', 'minion1'), - {'comment': comm3, - 'minions': ['minion1'], - 'result': True}) + with patch.dict(schedule.__salt__, {"event.fire": mock}): + self.assertDictEqual( + schedule.copy("job1", "minion1"), + { + "comment": comm3, + "minions": ["minion1"], + "result": True, + }, + ) # 'modify' function tests: 1 def test_modify(self): - ''' + """ Test if modifying job to the schedule. - ''' - job1 = {'function': 'salt', 'seconds': 3600} + """ + job1 = {"function": "salt", "seconds": 3600} - comm1 = 'Modified job: job1 in schedule.' - diff1 = ('--- \n+++ \n@@ -1,3 +1,6 @@\n ' - 'enabled:True\n function:salt\n' - '-seconds:3600\n+jid_include:True\n' - '+maxrunning:1\n+name:job1\n' - '+seconds:60\n') + comm1 = "Modified job: job1 in schedule." + diff1 = ( + "--- \n+++ \n@@ -1,3 +1,6 @@\n " + "enabled:True\n function:salt\n" + "-seconds:3600\n+jid_include:True\n" + "+maxrunning:1\n+name:job1\n" + "+seconds:60\n" + ) - diff4 = ('--- \n+++ \n@@ -1,3 +1,5 @@\n ' - 'enabled:True\n-function:salt\n' - '-seconds:3600\n+function:test.version\n' - '+jid_include:True\n+maxrunning:1\n' - '+name:job1\n') + diff4 = ( + "--- \n+++ \n@@ -1,3 +1,5 @@\n " + "enabled:True\n-function:salt\n" + "-seconds:3600\n+function:test.version\n" + "+jid_include:True\n+maxrunning:1\n" + "+name:job1\n" + ) - expected1 = {'comment': comm1, - 'changes': {'diff': diff1}, - 'result': True} + expected1 = {"comment": comm1, "changes": {"diff": diff1}, "result": True} - comm2 = 'Error: Unable to use "seconds", "minutes", "hours", ' \ - 'or "days" with "when" option.' - expected2 = {'comment': comm2, - 'changes': {}, - 'result': False} + comm2 = ( + 'Error: Unable to use "seconds", "minutes", "hours", ' + 'or "days" with "when" option.' + ) + expected2 = {"comment": comm2, "changes": {}, "result": False} comm3 = 'Unable to use "when" and "cron" options together. Ignoring.' - expected3 = {'comment': comm3, - 'changes': {}, - 'result': False} + expected3 = {"comment": comm3, "changes": {}, "result": False} - comm4 = 'Job: job1 would be modified in schedule.' - expected4 = {'comment': comm4, - 'changes': {'diff': diff4}, - 'result': True} + comm4 = "Job: job1 would be modified in schedule." + expected4 = {"comment": comm4, "changes": {"diff": diff4}, "result": True} - comm5 = 'Job job2 does not exist in schedule.' - expected5 = {'comment': comm5, - 'changes': {}, - 'result': False} + comm5 = "Job job2 does not exist in schedule." + expected5 = {"comment": comm5, "changes": {}, "result": False} - with patch.dict(schedule.__opts__, {'schedule': {'job1': job1}, 'sock_dir': self.sock_dir}): + with patch.dict( + schedule.__opts__, {"schedule": {"job1": job1}, "sock_dir": self.sock_dir} + ): mock = MagicMock(return_value=True) - with patch.dict(schedule.__salt__, {'event.fire': mock}): - _ret_value = {'complete': True, 'schedule': {'job1': job1}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - ret = schedule.modify('job1', seconds='60') + with patch.dict(schedule.__salt__, {"event.fire": mock}): + _ret_value = {"complete": True, "schedule": {"job1": job1}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + ret = schedule.modify("job1", seconds="60") self.assertDictEqual(ret, expected1) - _ret_value = {'complete': True, 'schedule': {'job1': job1}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - ret = schedule.modify('job1', function='test.ping', - seconds=3600, when='2400') + _ret_value = {"complete": True, "schedule": {"job1": job1}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + ret = schedule.modify( + "job1", function="test.ping", seconds=3600, when="2400" + ) self.assertDictEqual(ret, expected2) - _ret_value = {'complete': True, 'schedule': {'job1': job1}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - ret = schedule.modify('job1', function='test.ping', - when='2400', cron='2') + _ret_value = {"complete": True, "schedule": {"job1": job1}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + ret = schedule.modify( + "job1", function="test.ping", when="2400", cron="2" + ) self.assertDictEqual(ret, expected3) - _ret_value = {'complete': True, 'schedule': {'job1': job1}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - ret = schedule.modify('job1', function='test.version', test=True) + _ret_value = {"complete": True, "schedule": {"job1": job1}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + ret = schedule.modify("job1", function="test.version", test=True) self.assertDictEqual(ret, expected4) - _ret_value = {'complete': True, 'schedule': {}} - with patch.object(SaltEvent, 'get_event', return_value=_ret_value): - ret = schedule.modify('job2', function='test.version', test=True) + _ret_value = {"complete": True, "schedule": {}} + with patch.object(SaltEvent, "get_event", return_value=_ret_value): + ret = schedule.modify("job2", function="test.version", test=True) self.assertDictEqual(ret, expected5) diff --git a/tests/unit/modules/test_scsi.py b/tests/unit/modules/test_scsi.py index 8f2186b57b0..bdba0887e84 100644 --- a/tests/unit/modules/test_scsi.py +++ b/tests/unit/modules/test_scsi.py @@ -1,91 +1,97 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import copy -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import copy +import os # Import Salt Libs import salt.modules.scsi as scsi import salt.utils.path +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ScsiTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.scsi - ''' + """ + def setup_loader_modules(self): return {scsi: {}} def test_ls_(self): - ''' + """ Test for list SCSI devices, with details - ''' + """ lsscsi = { - 'stdout': '[0:0:0:0] disk HP LOGICAL VOLUME 6.68 /dev/sda [8:0]', - 'stderr': '', - 'retcode': 0 + "stdout": "[0:0:0:0] disk HP LOGICAL VOLUME 6.68 /dev/sda [8:0]", + "stderr": "", + "retcode": 0, } lsscsi_size = { - 'stdout': '[0:0:0:0] disk HP LOGICAL VOLUME 6.68 /dev/sda [8:0] 1.20TB', - 'stderr': '', - 'retcode': 0 + "stdout": "[0:0:0:0] disk HP LOGICAL VOLUME 6.68 /dev/sda [8:0] 1.20TB", + "stderr": "", + "retcode": 0, } result = { - '[0:0:0:0]': { - 'major': '8', - 'lun': '0:0:0:0', - 'device': '/dev/sda', - 'model': 'LOGICAL VOLUME 6.68', - 'minor': '0', - 'size': None, + "[0:0:0:0]": { + "major": "8", + "lun": "0:0:0:0", + "device": "/dev/sda", + "model": "LOGICAL VOLUME 6.68", + "minor": "0", + "size": None, } } result_size = copy.deepcopy(result) - result_size['[0:0:0:0]']['size'] = '1.20TB' + result_size["[0:0:0:0]"]["size"] = "1.20TB" - mock = MagicMock(return_value='/usr/bin/lsscsi') - with patch.object(salt.utils.path, 'which', mock): + mock = MagicMock(return_value="/usr/bin/lsscsi") + with patch.object(salt.utils.path, "which", mock): # get_size = True cmd_mock = MagicMock(return_value=lsscsi_size) - with patch.dict(scsi.__salt__, {'cmd.run_all': cmd_mock}): + with patch.dict(scsi.__salt__, {"cmd.run_all": cmd_mock}): self.assertDictEqual(scsi.ls_(), result_size) - with patch.dict(lsscsi_size, {'retcode': 1, 'stderr': 'An error occurred'}): - self.assertEqual(scsi.ls_(), 'An error occurred') - with patch.dict(lsscsi_size, {'retcode': 1, 'stderr': "lsscsi: invalid option -- 's'\nUsage:"}): - self.assertEqual(scsi.ls_(), "lsscsi: invalid option -- 's' - try get_size=False") + with patch.dict( + lsscsi_size, {"retcode": 1, "stderr": "An error occurred"} + ): + self.assertEqual(scsi.ls_(), "An error occurred") + with patch.dict( + lsscsi_size, + {"retcode": 1, "stderr": "lsscsi: invalid option -- 's'\nUsage:"}, + ): + self.assertEqual( + scsi.ls_(), "lsscsi: invalid option -- 's' - try get_size=False" + ) # get_size = False cmd_mock = MagicMock(return_value=lsscsi) - with patch.dict(scsi.__salt__, {'cmd.run_all': cmd_mock}): + with patch.dict(scsi.__salt__, {"cmd.run_all": cmd_mock}): self.assertDictEqual(scsi.ls_(get_size=False), result) mock = MagicMock(return_value=None) - with patch.object(salt.utils.path, 'which', mock): - self.assertEqual(scsi.ls_(), 'scsi.ls not available - lsscsi command not found') + with patch.object(salt.utils.path, "which", mock): + self.assertEqual( + scsi.ls_(), "scsi.ls not available - lsscsi command not found" + ) def test_rescan_all(self): - ''' + """ Test for list scsi devices - ''' + """ mock = MagicMock(side_effect=[False, True]) - with patch.object(os.path, 'isdir', mock): - self.assertEqual(scsi.rescan_all('host'), - 'Host host does not exist') + with patch.object(os.path, "isdir", mock): + self.assertEqual(scsi.rescan_all("host"), "Host host does not exist") - with patch.dict(scsi.__salt__, - {'cmd.run': MagicMock(return_value='A')}): - self.assertListEqual(scsi.rescan_all('host'), ['A']) + with patch.dict(scsi.__salt__, {"cmd.run": MagicMock(return_value="A")}): + self.assertListEqual(scsi.rescan_all("host"), ["A"]) diff --git a/tests/unit/modules/test_sdb.py b/tests/unit/modules/test_sdb.py index 6f7bb2d530b..1a375047bb3 100644 --- a/tests/unit/modules/test_sdb.py +++ b/tests/unit/modules/test_sdb.py @@ -1,40 +1,41 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.modules.sdb as sdb # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -# Import Salt Libs -import salt.modules.sdb as sdb - class SdbTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.sdb - ''' + """ + def setup_loader_modules(self): return {sdb: {}} # 'get' function tests: 1 def test_get(self): - ''' + """ Test if it gets a value from a db, using a uri in the form of sdb://<profile>/<key> - ''' - self.assertEqual(sdb.get('sdb://salt/foo'), 'sdb://salt/foo') + """ + self.assertEqual(sdb.get("sdb://salt/foo"), "sdb://salt/foo") # 'set_' function tests: 1 def test_set(self): - ''' + """ Test if it sets a value from a db, using a uri in the form of sdb://<profile>/<key> - ''' - self.assertFalse(sdb.set_('sdb://mymemcached/foo', 'bar')) + """ + self.assertFalse(sdb.set_("sdb://mymemcached/foo", "bar")) diff --git a/tests/unit/modules/test_seed.py b/tests/unit/modules/test_seed.py index 9e66ae8ec18..90438f05ea8 100644 --- a/tests/unit/modules/test_seed.py +++ b/tests/unit/modules/test_seed.py @@ -1,93 +1,112 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import uuid -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - +import salt.modules.seed as seed # Import Salt Libs import salt.utils.files import salt.utils.odict -import salt.modules.seed as seed + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class SeedTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.seed - ''' + """ + def setup_loader_modules(self): return {seed: {}} def test_mkconfig_odict(self): - with patch.dict(seed.__opts__, - {'master': 'foo'}): + with patch.dict(seed.__opts__, {"master": "foo"}): ddd = salt.utils.odict.OrderedDict() - ddd['b'] = 'b' - ddd['a'] = 'b' + ddd["b"] = "b" + ddd["a"] = "b" data = seed.mkconfig(ddd, approve_key=False) - with salt.utils.files.fopen(data['config']) as fic: + with salt.utils.files.fopen(data["config"]) as fic: fdata = fic.read() - self.assertEqual(fdata, 'b: b\na: b\nmaster: foo\n') + self.assertEqual(fdata, "b: b\na: b\nmaster: foo\n") def test_prep_bootstrap(self): - ''' + """ Test to update and get the random script to a random place - ''' - with patch.dict(seed.__salt__, {'config.gather_bootstrap_script': MagicMock(return_value=os.path.join('BS_PATH', 'BS'))}),\ - patch.object(uuid, 'uuid4', return_value='UUID'),\ - patch.object(os.path, 'exists', return_value=True),\ - patch.object(os, 'chmod', return_value=None),\ - patch.object(shutil, 'copy', return_value=None): + """ + with patch.dict( + seed.__salt__, + { + "config.gather_bootstrap_script": MagicMock( + return_value=os.path.join("BS_PATH", "BS") + ) + }, + ), patch.object(uuid, "uuid4", return_value="UUID"), patch.object( + os.path, "exists", return_value=True + ), patch.object( + os, "chmod", return_value=None + ), patch.object( + shutil, "copy", return_value=None + ): - expect = (os.path.join('MPT', 'tmp', 'UUID', 'BS'), - os.sep + os.path.join('tmp', 'UUID')) - self.assertEqual(seed.prep_bootstrap('MPT'), expect) + expect = ( + os.path.join("MPT", "tmp", "UUID", "BS"), + os.sep + os.path.join("tmp", "UUID"), + ) + self.assertEqual(seed.prep_bootstrap("MPT"), expect) - expect = (os.sep + os.path.join('MPT', 'tmp', 'UUID', 'BS'), - os.sep + os.path.join('tmp', 'UUID')) - self.assertEqual(seed.prep_bootstrap(os.sep + 'MPT'), expect) + expect = ( + os.sep + os.path.join("MPT", "tmp", "UUID", "BS"), + os.sep + os.path.join("tmp", "UUID"), + ) + self.assertEqual(seed.prep_bootstrap(os.sep + "MPT"), expect) def test_apply_(self): - ''' + """ Test to seed a location (disk image, directory, or block device) with the minion config, approve the minion's key, and/or install salt-minion. - ''' - mock = MagicMock(side_effect=[False, {'type': 'type', - 'target': 'target'}, - {'type': 'type', 'target': 'target'}, - {'type': 'type', 'target': 'target'}]) - with patch.dict(seed.__salt__, {'file.stats': mock}): - self.assertEqual(seed.apply_('path'), 'path does not exist') + """ + mock = MagicMock( + side_effect=[ + False, + {"type": "type", "target": "target"}, + {"type": "type", "target": "target"}, + {"type": "type", "target": "target"}, + ] + ) + with patch.dict(seed.__salt__, {"file.stats": mock}): + self.assertEqual(seed.apply_("path"), "path does not exist") - with patch.object(seed, '_mount', return_value=False): - self.assertEqual(seed.apply_('path'), - 'target could not be mounted') + with patch.object(seed, "_mount", return_value=False): + self.assertEqual(seed.apply_("path"), "target could not be mounted") - with patch.object(seed, '_mount', return_value=True): - with patch.object(os.path, 'join', return_value='A'): - with patch.object(os, 'makedirs', - MagicMock(side_effect=OSError('f'))): - with patch.object(os.path, 'isdir', - return_value=False): - self.assertRaises(OSError, seed.apply_, 'p') + with patch.object(seed, "_mount", return_value="/mountpoint"): + with patch.object(os.path, "join", return_value="A"): + with patch.object( + os, "makedirs", MagicMock(side_effect=OSError("f")) + ): + with patch.object(os.path, "isdir", return_value=False): + self.assertRaises(OSError, seed.apply_, "p") - with patch.object(os, 'makedirs', MagicMock()): - with patch.object(seed, 'mkconfig', return_value='A'): - with patch.object(seed, '_check_install', - return_value=False): - with patch.object(seed, '_umount', - return_value=None): - self.assertFalse(seed.apply_('path', - install=False)) + with patch.object(os, "makedirs", MagicMock()): + with patch.object(seed, "mkconfig", return_value="A"): + with patch.object( + seed, "_check_install", return_value=False + ): + with patch.object( + seed, "_umount", return_value=None + ) as umount_mock: + self.assertFalse(seed.apply_("path", install=False)) + umount_mock.assert_called_once_with( + "/mountpoint", "target", "type" + ) diff --git a/tests/unit/modules/test_selinux.py b/tests/unit/modules/test_selinux.py index 00da3a648d6..0f17606d61f 100644 --- a/tests/unit/modules/test_selinux.py +++ b/tests/unit/modules/test_selinux.py @@ -2,197 +2,213 @@ # Import Salt Testing Libs from __future__ import absolute_import -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) + +import salt.modules.selinux as selinux # Import Salt libs from salt.exceptions import SaltInvocationError -import salt.modules.selinux as selinux +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class SelinuxModuleTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.selinux - ''' + """ + def setup_loader_modules(self): return {selinux: {}} def test_fcontext_get_policy_parsing(self): - ''' + """ Test to verify that the parsing of the semanage output into fields is correct. Added with #45784. - ''' + """ cases = [ { - 'semanage_out': '/var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0', - 'name': '/var/www(/.*)?', - 'filetype': 'all files', - 'sel_user': 'system_u', - 'sel_role': 'object_r', - 'sel_type': 'httpd_sys_content_t', - 'sel_level': 's0' + "semanage_out": "/var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0", + "name": "/var/www(/.*)?", + "filetype": "all files", + "sel_user": "system_u", + "sel_role": "object_r", + "sel_type": "httpd_sys_content_t", + "sel_level": "s0", }, { - 'semanage_out': '/var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0', - 'name': '/var/www(/.*)?', - 'filetype': 'all files', - 'sel_user': 'system_u', - 'sel_role': 'object_r', - 'sel_type': 'httpd_sys_content_t', - 'sel_level': 's0' + "semanage_out": "/var/www(/.*)? all files system_u:object_r:httpd_sys_content_t:s0", + "name": "/var/www(/.*)?", + "filetype": "all files", + "sel_user": "system_u", + "sel_role": "object_r", + "sel_type": "httpd_sys_content_t", + "sel_level": "s0", }, { - 'semanage_out': '/var/lib/dhcp3? directory system_u:object_r:dhcp_state_t:s0', - 'name': '/var/lib/dhcp3?', - 'filetype': 'directory', - 'sel_user': 'system_u', - 'sel_role': 'object_r', - 'sel_type': 'dhcp_state_t', - 'sel_level': 's0' + "semanage_out": "/var/lib/dhcp3? directory system_u:object_r:dhcp_state_t:s0", + "name": "/var/lib/dhcp3?", + "filetype": "directory", + "sel_user": "system_u", + "sel_role": "object_r", + "sel_type": "dhcp_state_t", + "sel_level": "s0", }, { - 'semanage_out': '/var/lib/dhcp3? directory system_u:object_r:dhcp_state_t:s0', - 'name': '/var/lib/dhcp3?', - 'filetype': 'directory', - 'sel_user': 'system_u', - 'sel_role': 'object_r', - 'sel_type': 'dhcp_state_t', - 'sel_level': 's0' + "semanage_out": "/var/lib/dhcp3? directory system_u:object_r:dhcp_state_t:s0", + "name": "/var/lib/dhcp3?", + "filetype": "directory", + "sel_user": "system_u", + "sel_role": "object_r", + "sel_type": "dhcp_state_t", + "sel_level": "s0", }, { - 'semanage_out': '/var/lib/dhcp3? directory system_u:object_r:dhcp_state_t:s0', - 'name': '/var/lib/dhcp3?', - 'filetype': 'directory', - 'sel_user': 'system_u', - 'sel_role': 'object_r', - 'sel_type': 'dhcp_state_t', - 'sel_level': 's0' - } + "semanage_out": "/var/lib/dhcp3? directory system_u:object_r:dhcp_state_t:s0", + "name": "/var/lib/dhcp3?", + "filetype": "directory", + "sel_user": "system_u", + "sel_role": "object_r", + "sel_type": "dhcp_state_t", + "sel_level": "s0", + }, ] for case in cases: - with patch.dict(selinux.__salt__, {'cmd.shell': MagicMock(return_value=case['semanage_out'])}): - ret = selinux.fcontext_get_policy(case['name']) - self.assertEqual(ret['filespec'], case['name']) - self.assertEqual(ret['filetype'], case['filetype']) - self.assertEqual(ret['sel_user'], case['sel_user']) - self.assertEqual(ret['sel_role'], case['sel_role']) - self.assertEqual(ret['sel_type'], case['sel_type']) - self.assertEqual(ret['sel_level'], case['sel_level']) + with patch.dict( + selinux.__salt__, + {"cmd.shell": MagicMock(return_value=case["semanage_out"])}, + ): + ret = selinux.fcontext_get_policy(case["name"]) + self.assertEqual(ret["filespec"], case["name"]) + self.assertEqual(ret["filetype"], case["filetype"]) + self.assertEqual(ret["sel_user"], case["sel_user"]) + self.assertEqual(ret["sel_role"], case["sel_role"]) + self.assertEqual(ret["sel_type"], case["sel_type"]) + self.assertEqual(ret["sel_level"], case["sel_level"]) def test_parse_protocol_port_positive(self): - ''' + """ Test to verify positive parsing name, protocol and port combinations - ''' + """ cases = [ { - 'name': 'tcp/80', - 'protocol': None, - 'port': None, - 'expected': ('tcp', '80') + "name": "tcp/80", + "protocol": None, + "port": None, + "expected": ("tcp", "80"), }, { - 'name': 'udp/53', - 'protocol': None, - 'port': None, - 'expected': ('udp', '53') + "name": "udp/53", + "protocol": None, + "port": None, + "expected": ("udp", "53"), }, { - 'name': 'tcp_test_dns', - 'protocol': 'tcp', - 'port': '53', - 'expected': ('tcp', '53') + "name": "tcp_test_dns", + "protocol": "tcp", + "port": "53", + "expected": ("tcp", "53"), }, { - 'name': 'udp_test/dns', - 'protocol': 'udp', - 'port': '53', - 'expected': ('udp', '53') + "name": "udp_test/dns", + "protocol": "udp", + "port": "53", + "expected": ("udp", "53"), }, ] for case in cases: - ret = selinux._parse_protocol_port(case['name'], case['protocol'], case['port']) - self.assertTupleEqual(ret, case['expected']) + ret = selinux._parse_protocol_port( + case["name"], case["protocol"], case["port"] + ) + self.assertTupleEqual(ret, case["expected"]) def test_parse_protocol_port_negative(self): - ''' + """ Test to verify negative parsing of name, protocol and port combinations - ''' + """ cases = [ - { - 'name': 'invalid_name_no_args', - 'protocol': None, - 'port': None, - }, - { - 'name': 'invalid_proto/80', - 'protocol': 'nottcp', - 'port': '80', - }, - { - 'name': 'invalid_port', - 'protocol': 'tcp', - 'port': 'notaport', - }, - { - 'name': 'missing_proto', - 'protocol': None, - 'port': '80', - }, - { - 'name': 'missing_port', - 'protocol': 'udp', - 'port': None, - }, + {"name": "invalid_name_no_args", "protocol": None, "port": None}, + {"name": "invalid_proto/80", "protocol": "nottcp", "port": "80"}, + {"name": "invalid_port", "protocol": "tcp", "port": "notaport"}, + {"name": "missing_proto", "protocol": None, "port": "80"}, + {"name": "missing_port", "protocol": "udp", "port": None}, ] for case in cases: - self.assertRaises(SaltInvocationError, selinux._parse_protocol_port, case['name'], case['protocol'], - case['port']) + self.assertRaises( + SaltInvocationError, + selinux._parse_protocol_port, + case["name"], + case["protocol"], + case["port"], + ) def test_port_get_policy_parsing(self): - ''' + """ Test to verify that the parsing of the semanage port output into fields is correct. - ''' + """ cases = [ { - 'semanage_out': 'cma_port_t tcp 1050', - 'name': 'tcp/1050', - 'expected': {'sel_type': 'cma_port_t', 'protocol': 'tcp', 'port': '1050'}, + "semanage_out": "cma_port_t tcp 1050", + "name": "tcp/1050", + "expected": { + "sel_type": "cma_port_t", + "protocol": "tcp", + "port": "1050", + }, }, { - 'semanage_out': 'cluster_port_t tcp 5149, 40040, 50006-50008', - 'name': 'tcp/40040', - 'expected': {'sel_type': 'cluster_port_t', 'protocol': 'tcp', 'port': '5149, 40040, 50006-50008'}, + "semanage_out": "cluster_port_t tcp 5149, 40040, 50006-50008", + "name": "tcp/40040", + "expected": { + "sel_type": "cluster_port_t", + "protocol": "tcp", + "port": "5149, 40040, 50006-50008", + }, }, { - 'semanage_out': 'http_port_t tcp 9008, 8010, 9002-9003, 80, 81, 443, 488, 8008, 8009, 8443, 9000', - 'name': 'tcp/9000', - 'expected': {'sel_type': 'http_port_t', 'protocol': 'tcp', 'port': '9008, 8010, 9002-9003, 80, 81, 443, 488, 8008, 8009, 8443, 9000'}, + "semanage_out": "http_port_t tcp 9008, 8010, 9002-9003, 80, 81, 443, 488, 8008, 8009, 8443, 9000", + "name": "tcp/9000", + "expected": { + "sel_type": "http_port_t", + "protocol": "tcp", + "port": "9008, 8010, 9002-9003, 80, 81, 443, 488, 8008, 8009, 8443, 9000", + }, }, { - 'semanage_out': 'vnc_port_t tcp 5985-5999, 5900-5983', - 'name': 'tcp/5985-5999', - 'expected': {'sel_type': 'vnc_port_t', 'protocol': 'tcp', 'port': '5985-5999, 5900-5983'}, + "semanage_out": "vnc_port_t tcp 5985-5999, 5900-5983", + "name": "tcp/5985-5999", + "expected": { + "sel_type": "vnc_port_t", + "protocol": "tcp", + "port": "5985-5999, 5900-5983", + }, }, { - 'semanage_out': 'zebra_port_t tcp 2606, 2608-2609, 2600-2604', - 'name': 'tcp/2608-2609', - 'expected': {'sel_type': 'zebra_port_t', 'protocol': 'tcp', 'port': '2606, 2608-2609, 2600-2604'}, + "semanage_out": "zebra_port_t tcp 2606, 2608-2609, 2600-2604", + "name": "tcp/2608-2609", + "expected": { + "sel_type": "zebra_port_t", + "protocol": "tcp", + "port": "2606, 2608-2609, 2600-2604", + }, }, { - 'semanage_out': 'radius_port_t udp 1645, 1812, 18120-18121', - 'name': 'tcp/18120-18121', - 'expected': {'sel_type': 'radius_port_t', 'protocol': 'udp', 'port': '1645, 1812, 18120-18121'}, + "semanage_out": "radius_port_t udp 1645, 1812, 18120-18121", + "name": "tcp/18120-18121", + "expected": { + "sel_type": "radius_port_t", + "protocol": "udp", + "port": "1645, 1812, 18120-18121", + }, }, ] for case in cases: - with patch.dict(selinux.__salt__, {'cmd.shell': MagicMock(return_value=case['semanage_out'])}): - ret = selinux.port_get_policy(case['name']) - self.assertDictEqual(ret, case['expected']) + with patch.dict( + selinux.__salt__, + {"cmd.shell": MagicMock(return_value=case["semanage_out"])}, + ): + ret = selinux.port_get_policy(case["name"]) + self.assertDictEqual(ret, case["expected"]) diff --git a/tests/unit/modules/test_sensors.py b/tests/unit/modules/test_sensors.py index 8d965b34263..60b6a7d9114 100644 --- a/tests/unit/modules/test_sensors.py +++ b/tests/unit/modules/test_sensors.py @@ -1,32 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.modules.sensors as sensors +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SensorTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.sensors - ''' + """ + def setup_loader_modules(self): return {sensors: {}} def test_sense(self): - ''' + """ Test to gather lm-sensors data from a given chip - ''' - with patch.dict(sensors.__salt__, - {'cmd.run': MagicMock(return_value='A:a B:b C:c D:d')}): - self.assertDictEqual(sensors.sense('chip'), {'A': 'a B'}) + """ + with patch.dict( + sensors.__salt__, {"cmd.run": MagicMock(return_value="A:a B:b C:c D:d")} + ): + self.assertDictEqual(sensors.sense("chip"), {"A": "a B"}) diff --git a/tests/unit/modules/test_serverdensity_device.py b/tests/unit/modules/test_serverdensity_device.py index edb9d90e9bd..922191c8487 100644 --- a/tests/unit/modules/test_serverdensity_device.py +++ b/tests/unit/modules/test_serverdensity_device.py @@ -1,31 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.serverdensity_device as serverdensity_device # Import Salt Libs import salt.utils.json -import salt.modules.serverdensity_device as serverdensity_device from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MockRequests(object): - ''' + """ Mock smtplib class - ''' + """ + flag = None - content = '''{"message": "Invalid token", "errors": [{"type": "invalid_token", "subject": "token"}]}''' + content = """{"message": "Invalid token", "errors": [{"type": "invalid_token", "subject": "token"}]}""" status_code = None def __init__(self): @@ -34,9 +33,9 @@ class MockRequests(object): self.kwargs = None def return_request(self, url, data=None, **kwargs): - ''' + """ Mock request method. - ''' + """ self.url = url self.data = data self.kwargs = kwargs @@ -48,40 +47,37 @@ class MockRequests(object): return requests def post(self, url, data=None, **kwargs): - ''' + """ Mock post method. - ''' + """ return self.return_request(url, data, **kwargs) def delete(self, url, **kwargs): - ''' + """ Mock delete method. - ''' + """ return self.return_request(url, **kwargs) def get(self, url, **kwargs): - ''' + """ Mock get method. - ''' + """ return self.return_request(url, **kwargs) def put(self, url, data=None, **kwargs): - ''' + """ Mock put method. - ''' + """ return self.return_request(url, data, **kwargs) class ServerdensityDeviceTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.serverdensity_device - ''' + """ + def setup_loader_modules(self): - return { - serverdensity_device: { - 'requests': MockRequests() - } - } + return {serverdensity_device: {"requests": MockRequests()}} def setUp(self): self.mock_json_loads = MagicMock(side_effect=ValueError()) @@ -89,126 +85,139 @@ class ServerdensityDeviceTestCase(TestCase, LoaderModuleMockMixin): # 'get_sd_auth' function tests: 1 def test_get_sd_auth(self): - ''' + """ Tests if it returns requested Server Density authentication value from pillar. - ''' - with patch.dict(serverdensity_device.__pillar__, {'serverdensity': - False}): - self.assertRaises(CommandExecutionError, - serverdensity_device.get_sd_auth, '1') + """ + with patch.dict(serverdensity_device.__pillar__, {"serverdensity": False}): + self.assertRaises( + CommandExecutionError, serverdensity_device.get_sd_auth, "1" + ) - with patch.dict(serverdensity_device.__pillar__, {'serverdensity': - {'1': 'salt'}}): - self.assertEqual(serverdensity_device.get_sd_auth('1'), 'salt') + with patch.dict( + serverdensity_device.__pillar__, {"serverdensity": {"1": "salt"}} + ): + self.assertEqual(serverdensity_device.get_sd_auth("1"), "salt") - self.assertRaises(CommandExecutionError, - serverdensity_device.get_sd_auth, '2') + self.assertRaises( + CommandExecutionError, serverdensity_device.get_sd_auth, "2" + ) # 'create' function tests: 1 def test_create(self): - ''' + """ Tests if it create device in Server Density. - ''' - with patch.dict(serverdensity_device.__pillar__, - {'serverdensity': {'api_token': 'salt'}}): - self.assertTrue(serverdensity_device.create('rich_lama', - group='lama_band')) + """ + with patch.dict( + serverdensity_device.__pillar__, {"serverdensity": {"api_token": "salt"}} + ): + self.assertTrue(serverdensity_device.create("rich_lama", group="lama_band")) - with patch.object(salt.utils.json, 'loads', self.mock_json_loads): - self.assertRaises(CommandExecutionError, - serverdensity_device.create, 'rich_lama', - group='lama_band') + with patch.object(salt.utils.json, "loads", self.mock_json_loads): + self.assertRaises( + CommandExecutionError, + serverdensity_device.create, + "rich_lama", + group="lama_band", + ) MockRequests.flag = 1 - self.assertIsNone(serverdensity_device.create('rich_lama', - group='lama_band')) + self.assertIsNone( + serverdensity_device.create("rich_lama", group="lama_band") + ) # 'delete' function tests: 1 def test_delete(self): - ''' + """ Tests if it delete a device from Server Density. - ''' - with patch.dict(serverdensity_device.__pillar__, - {'serverdensity': {'api_token': 'salt'}}): + """ + with patch.dict( + serverdensity_device.__pillar__, {"serverdensity": {"api_token": "salt"}} + ): MockRequests.flag = 0 - self.assertTrue(serverdensity_device.delete('51f7eaf')) + self.assertTrue(serverdensity_device.delete("51f7eaf")) - with patch.object(salt.utils.json, 'loads', self.mock_json_loads): - self.assertRaises(CommandExecutionError, - serverdensity_device.delete, '51f7eaf') + with patch.object(salt.utils.json, "loads", self.mock_json_loads): + self.assertRaises( + CommandExecutionError, serverdensity_device.delete, "51f7eaf" + ) MockRequests.flag = 1 - self.assertIsNone(serverdensity_device.delete('51f7eaf')) + self.assertIsNone(serverdensity_device.delete("51f7eaf")) # 'ls' function tests: 1 def test_ls(self): - ''' + """ Tests if it list devices in Server Density. - ''' - with patch.dict(serverdensity_device.__pillar__, - {'serverdensity': {'api_token': 'salt'}}): + """ + with patch.dict( + serverdensity_device.__pillar__, {"serverdensity": {"api_token": "salt"}} + ): MockRequests.flag = 0 - self.assertTrue(serverdensity_device.ls(name='lama')) + self.assertTrue(serverdensity_device.ls(name="lama")) - with patch.object(salt.utils.json, 'loads', self.mock_json_loads): - self.assertRaises(CommandExecutionError, - serverdensity_device.ls, name='lama') + with patch.object(salt.utils.json, "loads", self.mock_json_loads): + self.assertRaises( + CommandExecutionError, serverdensity_device.ls, name="lama" + ) MockRequests.flag = 1 - self.assertIsNone(serverdensity_device.ls(name='lama')) + self.assertIsNone(serverdensity_device.ls(name="lama")) # 'update' function tests: 1 def test_update(self): - ''' + """ Tests if it updates device information in Server Density. - ''' - with patch.dict(serverdensity_device.__pillar__, - {'serverdensity': {'api_token': 'salt'}}): + """ + with patch.dict( + serverdensity_device.__pillar__, {"serverdensity": {"api_token": "salt"}} + ): MockRequests.flag = 0 - self.assertTrue(serverdensity_device.update('51f7eaf', name='lama')) + self.assertTrue(serverdensity_device.update("51f7eaf", name="lama")) - with patch.object(salt.utils.json, 'loads', self.mock_json_loads): - self.assertRaises(CommandExecutionError, - serverdensity_device.update, '51f7eaf', - name='lama') + with patch.object(salt.utils.json, "loads", self.mock_json_loads): + self.assertRaises( + CommandExecutionError, + serverdensity_device.update, + "51f7eaf", + name="lama", + ) MockRequests.flag = 1 - self.assertIsNone(serverdensity_device.update('51f7eaf', - name='lama')) + self.assertIsNone(serverdensity_device.update("51f7eaf", name="lama")) # 'install_agent' function tests: 1 def test_install_agent(self): - ''' + """ Tests if it downloads Server Density installation agent, and installs sd-agent with agent_key. - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(serverdensity_device.__pillar__, - {'serverdensity': {'account_url': 'salt'}}): - with patch.dict(serverdensity_device.__salt__, {'cmd.run': mock}): - with patch.dict(serverdensity_device.__opts__, - {'cachedir': '/'}): - self.assertTrue(serverdensity_device.install_agent('51f7e')) + with patch.dict( + serverdensity_device.__pillar__, {"serverdensity": {"account_url": "salt"}} + ): + with patch.dict(serverdensity_device.__salt__, {"cmd.run": mock}): + with patch.dict(serverdensity_device.__opts__, {"cachedir": "/"}): + self.assertTrue(serverdensity_device.install_agent("51f7e")) # 'install_agent_v2' function tests: 1 def test_install_agent_v2(self): - ''' + """ Tests if it downloads Server Density installation agent, and installs sd-agent with agent_key. - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(serverdensity_device.__pillar__, - {'serverdensity': {'account_name': 'salt'}}): - with patch.dict(serverdensity_device.__salt__, {'cmd.run': mock}): - with patch.dict(serverdensity_device.__opts__, - {'cachedir': '/'}): + with patch.dict( + serverdensity_device.__pillar__, {"serverdensity": {"account_name": "salt"}} + ): + with patch.dict(serverdensity_device.__salt__, {"cmd.run": mock}): + with patch.dict(serverdensity_device.__opts__, {"cachedir": "/"}): self.assertTrue( - serverdensity_device.install_agent( - '51f7e', agent_version=2)) + serverdensity_device.install_agent("51f7e", agent_version=2) + ) diff --git a/tests/unit/modules/test_service.py b/tests/unit/modules/test_service.py deleted file mode 100644 index 5d46f361911..00000000000 --- a/tests/unit/modules/test_service.py +++ /dev/null @@ -1,101 +0,0 @@ -# -*- coding: utf-8 -*- -''' - :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals -import os - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -# Import Salt Libs -import salt.modules.service as service - - -class ServiceTestCase(TestCase, LoaderModuleMockMixin): - ''' - Test cases for salt.modules.service - ''' - def setup_loader_modules(self): - return {service: {}} - - def test_start(self): - ''' - Test to start the specified service - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(service, 'run', MagicMock(return_value=True)): - self.assertTrue(service.start('name')) - - def test_stop(self): - ''' - Test to stop the specified service - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(service, 'run', MagicMock(return_value=True)): - self.assertTrue(service.stop('name')) - - def test_restart(self): - ''' - Test to restart the specified service - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(service, 'run', MagicMock(return_value=True)): - self.assertTrue(service.restart('name')) - - def test_status(self): - ''' - Test to return the status for a service, returns the PID or an empty - string if the service is running or not, pass a signature to use to - find the service via ps - ''' - with patch.dict(service.__salt__, - {'status.pid': MagicMock(return_value=True)}): - self.assertTrue(service.status('name')) - - def test_reload_(self): - ''' - Test to restart the specified service - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(service, 'run', MagicMock(return_value=True)): - self.assertTrue(service.reload_('name')) - - def test_run(self): - ''' - Test to run the specified service - ''' - with patch.object(os.path, 'join', return_value='A'): - with patch.object(service, 'run', MagicMock(return_value=True)): - self.assertTrue(service.run('name', 'action')) - - def test_available(self): - ''' - Test to returns ``True`` if the specified service is available, - otherwise returns ``False``. - ''' - with patch.object(service, 'get_all', return_value=['name', 'A']): - self.assertTrue(service.available('name')) - - def test_missing(self): - ''' - Test to inverse of service.available. - ''' - with patch.object(service, 'get_all', return_value=['name1', 'A']): - self.assertTrue(service.missing('name')) - - def test_get_all(self): - ''' - Test to return a list of all available services - ''' - with patch.object(os.path, 'isdir', side_effect=[False, True]): - - self.assertEqual(service.get_all(), []) - - with patch.object(os, 'listdir', return_value=['A', 'B']): - self.assertListEqual(service.get_all(), ['A', 'B']) diff --git a/tests/unit/modules/test_servicenow.py b/tests/unit/modules/test_servicenow.py index 5bb8b4e2dce..c27efbaacc4 100644 --- a/tests/unit/modules/test_servicenow.py +++ b/tests/unit/modules/test_servicenow.py @@ -1,18 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Anthony Shaw <anthonyshaw@apache.org> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import salt.modules.servicenow as servicenow + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, -) -import salt.modules.servicenow as servicenow class MockServiceNowClient(object): @@ -20,25 +19,28 @@ class MockServiceNowClient(object): pass def get(self, query): - return [{'query_size': len(query), - 'query_value': query}] + return [{"query_size": len(query), "query_value": query}] class ServiceNowModuleTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): module_globals = { - 'Client': MockServiceNowClient, - '__salt__': { - 'config.option': MagicMock(return_value={ - 'instance_name': 'test', - 'username': 'mr_test', - 'password': 'test123' - }) - } + "Client": MockServiceNowClient, + "__salt__": { + "config.option": MagicMock( + return_value={ + "instance_name": "test", + "username": "mr_test", + "password": "test123", + } + ) + }, } if servicenow.HAS_LIBS is False: - module_globals['sys.modules'] = {'servicenow_rest': MagicMock()} - module_globals['sys.modules']['servicenow_rest'].api.Client = MockServiceNowClient + module_globals["sys.modules"] = {"servicenow_rest": MagicMock()} + module_globals["sys.modules"][ + "servicenow_rest" + ].api.Client = MockServiceNowClient return {servicenow: module_globals} def test_module_creation(self): @@ -46,19 +48,18 @@ class ServiceNowModuleTestCase(TestCase, LoaderModuleMockMixin): self.assertFalse(client is None) def test_non_structured_query(self): - result = servicenow.non_structured_query('tests', 'role=web') + result = servicenow.non_structured_query("tests", "role=web") self.assertFalse(result is None) - self.assertEqual(result[0]['query_size'], 8) - self.assertEqual(result[0]['query_value'], 'role=web') + self.assertEqual(result[0]["query_size"], 8) + self.assertEqual(result[0]["query_value"], "role=web") def test_non_structured_query_kwarg(self): - result = servicenow.non_structured_query('tests', role='web') + result = servicenow.non_structured_query("tests", role="web") self.assertFalse(result is None) - self.assertEqual(result[0]['query_size'], 8) - self.assertEqual(result[0]['query_value'], 'role=web') + self.assertEqual(result[0]["query_size"], 8) + self.assertEqual(result[0]["query_value"], "role=web") def test_non_structured_query_kwarg_multi(self): - result = servicenow.non_structured_query('tests', role='web', - type='computer') + result = servicenow.non_structured_query("tests", role="web", type="computer") self.assertFalse(result is None) - self.assertEqual(result[0]['query_size'], 22) + self.assertEqual(result[0]["query_size"], 22) diff --git a/tests/unit/modules/test_smartos_imgadm.py b/tests/unit/modules/test_smartos_imgadm.py index a2bbc183227..50455032f83 100644 --- a/tests/unit/modules/test_smartos_imgadm.py +++ b/tests/unit/modules/test_smartos_imgadm.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jorge Schrauwen <info@blackdot.be> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -15,12 +15,10 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase image_orphan = { - "manifest": { - "uuid": "07f360fd-12d5-e624-a279-eb8a15b630f6" - }, + "manifest": {"uuid": "07f360fd-12d5-e624-a279-eb8a15b630f6"}, "zpool": "zones", "cloneNames": [], - "clones": 0 + "clones": 0, } image_native = { "manifest": { @@ -39,34 +37,22 @@ image_native = { { "sha1": "5efaf95b7f226eb09c7d5e6c3734f8aa654b811d", "size": 465411979, - "compression": "gzip" + "compression": "gzip", } ], "description": "A SmartOS image pre-configured for building pkgsrc packages.", "homepage": "https://docs.joyent.com/images/smartos/pkgbuild", "urn": "sdc:sdc:pkgbuild:18.1.0", "requirements": { - "min_platform": { - "7.0": "20141030T081701Z" - }, - "networks": [ - { - "name": "net0", - "description": "public" - } - ] + "min_platform": {"7.0": "20141030T081701Z"}, + "networks": [{"name": "net0", "description": "public"}], }, - "tags": { - "role": "os", - "group": "pkgbuild" - } + "tags": {"role": "os", "group": "pkgbuild"}, }, "zpool": "zones", "source": "https://images.joyent.com", - "cloneNames": [ - "zones/dda70f61-70fe-65e7-cf70-d878d69442d4" - ], - "clones": 1 + "cloneNames": ["zones/dda70f61-70fe-65e7-cf70-d878d69442d4"], + "clones": 1, } image_lx = { "manifest": { @@ -85,34 +71,22 @@ image_lx = { { "sha1": "d342f137c5ccef0702ec479acb63c196cf81b38a", "size": 134969110, - "compression": "gzip" + "compression": "gzip", } ], "description": "Container-native Ubuntu 16.04 64-bit image. Built to run on containers with bare metal speed, while offering all the services of a typical unix host.", "homepage": "https://docs.joyent.com/images/container-native-linux", "requirements": { - "networks": [ - { - "name": "net0", - "description": "public" - } - ], - "min_platform": { - "7.0": "20160225T122859Z" - }, - "brand": "lx" + "networks": [{"name": "net0", "description": "public"}], + "min_platform": {"7.0": "20160225T122859Z"}, + "brand": "lx", }, - "tags": { - "role": "os", - "kernel_version": "4.3.0" - } - }, - "zpool": "zones", - "source": "https://images.joyent.com", - "cloneNames": [ - "zones/e4c1f6b5-4429-e6c2-ae2a-d6aa58bdeebb" - ], - "clones": 1 + "tags": {"role": "os", "kernel_version": "4.3.0"}, + }, + "zpool": "zones", + "source": "https://images.joyent.com", + "cloneNames": ["zones/e4c1f6b5-4429-e6c2-ae2a-d6aa58bdeebb"], + "clones": 1, } image_zvol = { "manifest": { @@ -131,36 +105,26 @@ image_zvol = { { "sha1": "9f7704969507bd97e160a8f42a3631487644e457", "size": 372276887, - "compression": "gzip" + "compression": "gzip", } ], "description": "Ubuntu 18.04 LTS (20180808 64-bit). Certified Ubuntu Server Cloud Image from Canonical. For kvm and bhyve.", "homepage": "https://docs.joyent.com/images/linux/ubuntu-certified", "requirements": { - "min_platform": { - "7.0": "20150929T232348Z" - }, - "networks": [ - { - "name": "net0", - "description": "public" - } - ], - "ssh_key": True + "min_platform": {"7.0": "20150929T232348Z"}, + "networks": [{"name": "net0", "description": "public"}], + "ssh_key": True, }, "nic_driver": "virtio", "disk_driver": "virtio", "cpu_type": "host", "image_size": 10240, - "tags": { - "default_user": "ubuntu", - "role": "os" - } + "tags": {"default_user": "ubuntu", "role": "os"}, }, "zpool": "zones", "source": "https://images.joyent.com", "cloneNames": [], - "clones": 0 + "clones": 0, } image_docker = { "manifest": { @@ -174,103 +138,107 @@ image_docker = { "published_at": "2019-03-23T01:32:25.320Z", "type": "docker", "os": "linux", - "description": "/bin/sh -c #(nop) CMD [\"/bin/bash\" \"/opt/start.sh\" \"-bash\"]", + "description": '/bin/sh -c #(nop) CMD ["/bin/bash" "/opt/start.sh" "-bash"]', "tags": { "docker:repo": "busybox42/zimbra-docker-centos", "docker:id": "sha256:62487cf6a7f698af4edc20707e14b1b3bba13b98bea3375f05af04859a30b222", "docker:architecture": "amd64", "docker:tag:latest": True, "docker:config": { - "Cmd": [ - "/bin/bash", - "/opt/start.sh", - "-bash" - ], + "Cmd": ["/bin/bash", "/opt/start.sh", "-bash"], "Entrypoint": None, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" ], - "WorkingDir": "" - } + "WorkingDir": "", + }, }, - "origin": "2f0c529b-7bab-28d1-ff34-bdc9281b7a4b" + "origin": "2f0c529b-7bab-28d1-ff34-bdc9281b7a4b", }, "zpool": "zones", "source": "https://docker.io", "cloneNames": [], - "clones": 0 + "clones": 0, } class ImgadmTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.smartos_imgadm module - ''' + """ def setup_loader_modules(self): return {imgadm: {}} def test_parse_image_meta_orphan(self): - ''' + """ Test the internal _parse_image_meta methode Feed it an 'orphan' image as we get it from from imgadm list -j - ''' - ret = {'Error': 'This looks like an orphaned image, image payload was invalid.'} + """ + ret = {"Error": "This looks like an orphaned image, image payload was invalid."} self.assertEqual(_parse_image_meta(image_orphan, True), ret) def test_parse_image_meta_native(self): - ''' + """ Test the internal _parse_image_meta methode Feed it an 'native' image as we get it from from imgadm list -j - ''' - ret = {'description': 'A SmartOS image pre-configured for building pkgsrc packages.', - 'name': 'pkgbuild', - 'os': 'smartos', - 'published': '2018-04-09T08:25:52Z', - 'source': 'https://images.joyent.com', - 'version': '18.1.0'} + """ + ret = { + "description": "A SmartOS image pre-configured for building pkgsrc packages.", + "name": "pkgbuild", + "os": "smartos", + "published": "2018-04-09T08:25:52Z", + "source": "https://images.joyent.com", + "version": "18.1.0", + } self.assertEqual(_parse_image_meta(image_native, True), ret) def test_parse_image_meta_lx(self): - ''' + """ Test the internal _parse_image_meta methode Feed it an 'lx' image as we get it from from imgadm list -j - ''' - ret = {'description': 'Container-native Ubuntu 16.04 64-bit image. Built to run on ' - 'containers with bare metal speed, while offering all the ' - 'services of a typical unix host.', - 'name': 'ubuntu-16.04', - 'os': 'linux', - 'published': '2016-06-01T02:17:41Z', - 'source': 'https://images.joyent.com', - 'version': '20160601'} + """ + ret = { + "description": "Container-native Ubuntu 16.04 64-bit image. Built to run on " + "containers with bare metal speed, while offering all the " + "services of a typical unix host.", + "name": "ubuntu-16.04", + "os": "linux", + "published": "2016-06-01T02:17:41Z", + "source": "https://images.joyent.com", + "version": "20160601", + } self.assertEqual(_parse_image_meta(image_lx, True), ret) def test_parse_image_meta_zvol(self): - ''' + """ Test the internal _parse_image_meta methode Feed it an 'zvol' image as we get it from from imgadm list -j - ''' - ret = {'description': 'Ubuntu 18.04 LTS (20180808 64-bit). Certified Ubuntu Server ' - 'Cloud Image from Canonical. For kvm and bhyve.', - 'name': 'ubuntu-certified-18.04', - 'os': 'linux', - 'published': '2018-10-11T12:45:24.804Z', - 'source': 'https://images.joyent.com', - 'version': '20180808'} + """ + ret = { + "description": "Ubuntu 18.04 LTS (20180808 64-bit). Certified Ubuntu Server " + "Cloud Image from Canonical. For kvm and bhyve.", + "name": "ubuntu-certified-18.04", + "os": "linux", + "published": "2018-10-11T12:45:24.804Z", + "source": "https://images.joyent.com", + "version": "20180808", + } self.assertEqual(_parse_image_meta(image_zvol, True), ret) def test_parse_image_meta_docker(self): - ''' + """ Test the internal _parse_image_meta methode Feed it an 'docker' image as we get it from from imgadm list -j - ''' - ret = {'description': 'Docker image imported from ' - 'busybox42/zimbra-docker-centos:latest on ' - '2019-03-23T01:32:25.320Z.', - 'name': 'busybox42/zimbra-docker-centos:latest', - 'os': 'linux', - 'published': '2019-03-23T01:32:25.320Z', - 'source': 'https://docker.io', - 'version': '62487cf6a7f6'} + """ + ret = { + "description": "Docker image imported from " + "busybox42/zimbra-docker-centos:latest on " + "2019-03-23T01:32:25.320Z.", + "name": "busybox42/zimbra-docker-centos:latest", + "os": "linux", + "published": "2019-03-23T01:32:25.320Z", + "source": "https://docker.io", + "version": "62487cf6a7f6", + } self.assertEqual(_parse_image_meta(image_docker, True), ret) diff --git a/tests/unit/modules/test_smf_service.py b/tests/unit/modules/test_smf_service.py index ceb41efe3d6..69b547278da 100644 --- a/tests/unit/modules/test_smf_service.py +++ b/tests/unit/modules/test_smf_service.py @@ -1,174 +1,175 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.modules.smf_service as smf +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SmfTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.smf - ''' + """ + def setup_loader_modules(self): return {smf: {}} def test_get_running(self): - ''' + """ Test to return the running services - ''' - with patch.dict(smf.__salt__, {'cmd.run': - MagicMock(return_value='A online\n')}): - self.assertEqual(smf.get_running(), ['A']) + """ + with patch.dict( + smf.__salt__, {"cmd.run": MagicMock(return_value="A online\n")} + ): + self.assertEqual(smf.get_running(), ["A"]) def test_get_stopped(self): - ''' + """ Test to return the stopped services - ''' - with patch.dict(smf.__salt__, - {'cmd.run': MagicMock(return_value='A\n')}): - self.assertListEqual(smf.get_stopped(), ['A']) + """ + with patch.dict(smf.__salt__, {"cmd.run": MagicMock(return_value="A\n")}): + self.assertListEqual(smf.get_stopped(), ["A"]) def test_available(self): - ''' + """ Test to returns ``True`` if the specified service is available, otherwise returns ``False``. - ''' - with patch.dict(smf.__salt__, {'cmd.run': MagicMock(return_value='A')}): - with patch.object(smf, 'get_all', return_value=('A')): - self.assertTrue(smf.available('A')) + """ + with patch.dict(smf.__salt__, {"cmd.run": MagicMock(return_value="A")}): + with patch.object(smf, "get_all", return_value=("A")): + self.assertTrue(smf.available("A")) def test_missing(self): - ''' + """ The inverse of service.available. Returns ``True`` if the specified service is not available, otherwise returns ``False``. - ''' - with patch.dict(smf.__salt__, {'cmd.run': MagicMock(return_value='A')}): - with patch.object(smf, 'get_all', return_value=('A')): - self.assertFalse(smf.missing('A')) + """ + with patch.dict(smf.__salt__, {"cmd.run": MagicMock(return_value="A")}): + with patch.object(smf, "get_all", return_value=("A")): + self.assertFalse(smf.missing("A")) def test_get_all(self): - ''' + """ Test to return all installed services - ''' - with patch.dict(smf.__salt__, - {'cmd.run': MagicMock(return_value='A\n')}): - self.assertListEqual(smf.get_all(), ['A']) + """ + with patch.dict(smf.__salt__, {"cmd.run": MagicMock(return_value="A\n")}): + self.assertListEqual(smf.get_all(), ["A"]) def test_start(self): - ''' + """ Test to start the specified service - ''' - with patch.dict(smf.__salt__, - {'cmd.retcode': MagicMock(side_effect=[False, 3, None, - False, 4])}): - self.assertTrue(smf.start('name')) + """ + with patch.dict( + smf.__salt__, + {"cmd.retcode": MagicMock(side_effect=[False, 3, None, False, 4])}, + ): + self.assertTrue(smf.start("name")) - self.assertTrue(smf.start('name')) + self.assertTrue(smf.start("name")) - self.assertFalse(smf.start('name')) + self.assertFalse(smf.start("name")) def test_stop(self): - ''' + """ Test to stop the specified service - ''' - with patch.dict(smf.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(smf.stop('name')) + """ + with patch.dict(smf.__salt__, {"cmd.retcode": MagicMock(return_value=False)}): + self.assertTrue(smf.stop("name")) def test_restart(self): - ''' + """ Test to restart the named service - ''' - with patch.dict(smf.__salt__, - {'cmd.retcode': MagicMock(side_effect=[False, True])}): + """ + with patch.dict( + smf.__salt__, {"cmd.retcode": MagicMock(side_effect=[False, True])} + ): - with patch.object(smf, 'start', return_value='A'): - self.assertEqual(smf.restart('name'), 'A') + with patch.object(smf, "start", return_value="A"): + self.assertEqual(smf.restart("name"), "A") - self.assertFalse(smf.restart('name')) + self.assertFalse(smf.restart("name")) def test_reload_(self): - ''' + """ Test to reload the named service - ''' - with patch.dict(smf.__salt__, - {'cmd.retcode': MagicMock(side_effect=[False, True])}): + """ + with patch.dict( + smf.__salt__, {"cmd.retcode": MagicMock(side_effect=[False, True])} + ): - with patch.object(smf, 'start', return_value='A'): - self.assertEqual(smf.reload_('name'), 'A') + with patch.object(smf, "start", return_value="A"): + self.assertEqual(smf.reload_("name"), "A") - self.assertFalse(smf.reload_('name')) + self.assertFalse(smf.reload_("name")) def test_status(self): - ''' + """ Test to return the status for a service, returns a bool whether the service is running. - ''' - with patch.dict(smf.__salt__, - {'cmd.run': MagicMock(side_effect=['online', - 'online1'])}): - self.assertTrue(smf.status('name')) + """ + with patch.dict( + smf.__salt__, {"cmd.run": MagicMock(side_effect=["online", "online1"])} + ): + self.assertTrue(smf.status("name")) - self.assertFalse(smf.status('name')) + self.assertFalse(smf.status("name")) def test_enable(self): - ''' + """ Test to enable the named service to start at boot - ''' - with patch.dict(smf.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(smf.enable('name')) + """ + with patch.dict(smf.__salt__, {"cmd.retcode": MagicMock(return_value=False)}): + self.assertTrue(smf.enable("name")) def test_disable(self): - ''' + """ Test to disable the named service to start at boot - ''' - with patch.dict(smf.__salt__, - {'cmd.retcode': MagicMock(return_value=False)}): - self.assertTrue(smf.disable('name')) + """ + with patch.dict(smf.__salt__, {"cmd.retcode": MagicMock(return_value=False)}): + self.assertTrue(smf.disable("name")) def test_enabled(self): - ''' + """ Test to check to see if the named service is enabled to start on boot - ''' - with patch.dict(smf.__salt__, - {'cmd.run': MagicMock(side_effect=['fmri', - 'A B true', - 'fmri', - 'A B false'])}): - self.assertTrue(smf.enabled('name')) + """ + with patch.dict( + smf.__salt__, + { + "cmd.run": MagicMock( + side_effect=["fmri", "A B true", "fmri", "A B false"] + ) + }, + ): + self.assertTrue(smf.enabled("name")) - self.assertFalse(smf.enabled('name')) + self.assertFalse(smf.enabled("name")) def test_disabled(self): - ''' + """ Test to check to see if the named service is disabled to start on boot - ''' - with patch.object(smf, 'enabled', return_value=False): - self.assertTrue(smf.disabled('name')) + """ + with patch.object(smf, "enabled", return_value=False): + self.assertTrue(smf.disabled("name")) def test_get_enabled(self): - ''' + """ Test to return the enabled services - ''' - with patch.object(smf, '_get_enabled_disabled', return_value=True): + """ + with patch.object(smf, "_get_enabled_disabled", return_value=True): self.assertTrue(smf.get_enabled()) def test_get_disabled(self): - ''' + """ Test to return the disabled services - ''' - with patch.object(smf, '_get_enabled_disabled', return_value=True): + """ + with patch.object(smf, "_get_enabled_disabled", return_value=True): self.assertTrue(smf.get_disabled()) diff --git a/tests/unit/modules/test_smtp.py b/tests/unit/modules/test_smtp.py index e49b0cb0620..898123b3e11 100644 --- a/tests/unit/modules/test_smtp.py +++ b/tests/unit/modules/test_smtp.py @@ -1,120 +1,125 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.smtp as smtp +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SMTPRecipientsRefused(Exception): - ''' + """ Mock SMTPRecipientsRefused class - ''' + """ + def __init__(self, msg): super(SMTPRecipientsRefused, self).__init__(msg) self.smtp_error = msg class SMTPHeloError(Exception): - ''' + """ Mock SMTPHeloError class - ''' + """ + def __init__(self, msg): super(SMTPHeloError, self).__init__(msg) self.smtp_error = msg class SMTPSenderRefused(Exception): - ''' + """ Mock SMTPSenderRefused class - ''' + """ + def __init__(self, msg): super(SMTPSenderRefused, self).__init__(msg) self.smtp_error = msg class SMTPDataError(Exception): - ''' + """ Mock SMTPDataError class - ''' + """ + def __init__(self, msg): super(SMTPDataError, self).__init__(msg) self.smtp_error = msg class SMTPException(Exception): - ''' + """ Mock SMTPException class - ''' + """ + def __init__(self, msg): super(SMTPException, self).__init__(msg) self.smtp_error = msg class SMTPAuthenticationError(Exception): - ''' + """ Mock SMTPAuthenticationError class - ''' + """ + def __init__(self, msg): super(SMTPAuthenticationError, self).__init__(msg) self.smtp_error = msg class MockSMTPSSL(object): - ''' + """ Mock SMTP_SSL class - ''' + """ + flag = None def __init__(self, server): pass def sendmail(self, sender, recipient, msg): - ''' + """ Mock sendmail method - ''' + """ if self.flag == 1: - raise SMTPRecipientsRefused('All recipients were refused.') + raise SMTPRecipientsRefused("All recipients were refused.") elif self.flag == 2: - raise SMTPHeloError('Helo error') + raise SMTPHeloError("Helo error") elif self.flag == 3: - raise SMTPSenderRefused('Sender Refused') + raise SMTPSenderRefused("Sender Refused") elif self.flag == 4: - raise SMTPDataError('Data error') + raise SMTPDataError("Data error") return (sender, recipient, msg) def login(self, username, password): - ''' + """ Mock login method - ''' + """ if self.flag == 5: - raise SMTPAuthenticationError('SMTP Authentication Failure') + raise SMTPAuthenticationError("SMTP Authentication Failure") return (username, password) @staticmethod def quit(): - ''' + """ Mock quit method - ''' + """ return True class MockSMTP(object): - ''' + """ Mock SMTP class - ''' + """ + flag = None def __init__(self, server): @@ -122,73 +127,76 @@ class MockSMTP(object): @staticmethod def ehlo(): - ''' + """ Mock ehlo method - ''' + """ return True @staticmethod def has_extn(name): - ''' + """ Mock has_extn method - ''' + """ return name def starttls(self): - ''' + """ Mock starttls method - ''' + """ if self.flag == 1: - raise SMTPHeloError('Helo error') + raise SMTPHeloError("Helo error") elif self.flag == 2: - raise SMTPException('Exception error') + raise SMTPException("Exception error") elif self.flag == 3: raise RuntimeError return True def sendmail(self, sender, recipient, msg): - ''' + """ Mock sendmail method - ''' + """ if self.flag == 1: - raise SMTPRecipientsRefused('All recipients were refused.') + raise SMTPRecipientsRefused("All recipients were refused.") elif self.flag == 2: - raise SMTPHeloError('Helo error') + raise SMTPHeloError("Helo error") elif self.flag == 3: - raise SMTPSenderRefused('Sender Refused') + raise SMTPSenderRefused("Sender Refused") elif self.flag == 4: - raise SMTPDataError('Data error') + raise SMTPDataError("Data error") return (sender, recipient, msg) @staticmethod def quit(): - ''' + """ Mock quit method - ''' + """ return True class MockGaierror(Exception): - ''' + """ Mock MockGaierror class - ''' + """ + def __init__(self, msg): super(MockGaierror, self).__init__(msg) self.smtp_error = msg class MockSocket(object): - ''' + """ Mock Socket class - ''' + """ + def __init__(self): self.gaierror = MockGaierror class MockSmtplib(object): - ''' + """ Mock smtplib class - ''' + """ + flag = None def __init__(self): @@ -201,98 +209,146 @@ class MockSmtplib(object): self.server = None def SMTP_SSL(self, server): - ''' + """ Mock SMTP_SSL method - ''' + """ self.server = server if self.flag == 1: - raise MockGaierror('gaierror') - return MockSMTPSSL('server') + raise MockGaierror("gaierror") + return MockSMTPSSL("server") def SMTP(self, server): - ''' + """ Mock SMTP method - ''' + """ self.server = server if self.flag == 1: - raise MockGaierror('gaierror') - return MockSMTP('server') + raise MockGaierror("gaierror") + return MockSMTP("server") class SmtpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.smtp - ''' + """ + def setup_loader_modules(self): - return { - smtp: { - 'socket': MockSocket(), - 'smtplib': MockSmtplib() - } - } + return {smtp: {"socket": MockSocket(), "smtplib": MockSmtplib()}} # 'send_msg' function tests: 1 def test_send_msg(self): - ''' + """ Tests if it send a message to an SMTP recipient. - ''' - mock = MagicMock(return_value={'smtp.server': '', 'smtp.tls': 'True', - 'smtp.sender': '', 'smtp.username': '', - 'smtp.password': ''}) - with patch.dict(smtp.__salt__, {'config.option': mock}): - self.assertTrue(smtp.send_msg('admin@example.com', - 'This is a salt module test', - profile='my-smtp-account')) + """ + mock = MagicMock( + return_value={ + "smtp.server": "", + "smtp.tls": "True", + "smtp.sender": "", + "smtp.username": "", + "smtp.password": "", + } + ) + with patch.dict(smtp.__salt__, {"config.option": mock}): + self.assertTrue( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + profile="my-smtp-account", + ) + ) MockSMTPSSL.flag = 1 - self.assertFalse(smtp.send_msg('admin@example.com', - 'This is a salt module test', - profile='my-smtp-account')) + self.assertFalse( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + profile="my-smtp-account", + ) + ) MockSMTPSSL.flag = 2 - self.assertFalse(smtp.send_msg('admin@example.com', - 'This is a salt module test', - profile='my-smtp-account')) + self.assertFalse( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + profile="my-smtp-account", + ) + ) MockSMTPSSL.flag = 3 - self.assertFalse(smtp.send_msg('admin@example.com', - 'This is a salt module test', - profile='my-smtp-account')) + self.assertFalse( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + profile="my-smtp-account", + ) + ) MockSMTPSSL.flag = 4 - self.assertFalse(smtp.send_msg('admin@example.com', - 'This is a salt module test', - profile='my-smtp-account')) + self.assertFalse( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + profile="my-smtp-account", + ) + ) - mock = MagicMock(return_value={'smtp.server': '', 'smtp.tls': '', - 'smtp.sender': '', 'smtp.username': '', - 'smtp.password': ''}) - with patch.dict(smtp.__salt__, {'config.option': mock}): + mock = MagicMock( + return_value={ + "smtp.server": "", + "smtp.tls": "", + "smtp.sender": "", + "smtp.username": "", + "smtp.password": "", + } + ) + with patch.dict(smtp.__salt__, {"config.option": mock}): MockSMTPSSL.flag = 5 - self.assertFalse(smtp.send_msg('admin@example.com', - 'This is a salt module test', - username='myuser', - password='verybadpass', - sender='admin@example.com', - server='smtp.domain.com')) + self.assertFalse( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + username="myuser", + password="verybadpass", + sender="admin@example.com", + server="smtp.domain.com", + ) + ) MockSMTP.flag = 1 - self.assertFalse(smtp.send_msg('admin@example.com', - 'This is a salt module test', - profile='my-smtp-account')) + self.assertFalse( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + profile="my-smtp-account", + ) + ) MockSMTP.flag = 2 - self.assertFalse(smtp.send_msg('admin@example.com', - 'This is a salt module test', - profile='my-smtp-account')) + self.assertFalse( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + profile="my-smtp-account", + ) + ) MockSMTP.flag = 3 - self.assertFalse(smtp.send_msg('admin@example.com', - 'This is a salt module test', - profile='my-smtp-account')) + self.assertFalse( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + profile="my-smtp-account", + ) + ) MockSmtplib.flag = 1 - self.assertFalse(smtp.send_msg('admin@example.com', - 'This is a salt module test', - profile='my-smtp-account')) + self.assertFalse( + smtp.send_msg( + "admin@example.com", + "This is a salt module test", + profile="my-smtp-account", + ) + ) diff --git a/tests/unit/modules/test_snapper.py b/tests/unit/modules/test_snapper.py index 5bbe62f576e..b5d6d27eff8 100644 --- a/tests/unit/modules/test_snapper.py +++ b/tests/unit/modules/test_snapper.py @@ -1,188 +1,245 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for the Snapper module :codeauthor: Duncan Mac-Vicar P. <dmacvicar@suse.de> :codeauthor: Pablo Suárez Hernández <psuarezhernandez@suse.de> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import sys -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, - mock_open, -) +import salt.modules.snapper as snapper +from salt.exceptions import CommandExecutionError # Import Salt libs from salt.ext import six -from salt.exceptions import CommandExecutionError -import salt.modules.snapper as snapper + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase, skipIf DBUS_RET = { - 'ListSnapshots': [ - [42, 1, 0, 1457006571, - 0, 'Some description', '', - {'userdata1': 'userval1', 'salt_jid': '20160607130930720112'}], - [43, 2, 42, 1457006572, - 0, 'Blah Blah', '', - {'userdata2': 'userval2', 'salt_jid': '20160607130930720112'}] + "ListSnapshots": [ + [ + 42, + 1, + 0, + 1457006571, + 0, + "Some description", + "", + {"userdata1": "userval1", "salt_jid": "20160607130930720112"}, + ], + [ + 43, + 2, + 42, + 1457006572, + 0, + "Blah Blah", + "", + {"userdata2": "userval2", "salt_jid": "20160607130930720112"}, + ], ], - 'ListConfigs': [ - ['root', '/', { - 'SUBVOLUME': '/', 'NUMBER_MIN_AGE': '1800', - 'TIMELINE_LIMIT_YEARLY': '4-10', 'NUMBER_LIMIT_IMPORTANT': '10', - 'FSTYPE': 'btrfs', 'TIMELINE_LIMIT_MONTHLY': '4-10', - 'ALLOW_GROUPS': '', 'EMPTY_PRE_POST_MIN_AGE': '1800', - 'EMPTY_PRE_POST_CLEANUP': 'yes', 'BACKGROUND_COMPARISON': 'yes', - 'TIMELINE_LIMIT_HOURLY': '4-10', 'ALLOW_USERS': '', - 'TIMELINE_LIMIT_WEEKLY': '0', 'TIMELINE_CREATE': 'no', - 'NUMBER_CLEANUP': 'yes', 'TIMELINE_CLEANUP': 'yes', - 'SPACE_LIMIT': '0.5', 'NUMBER_LIMIT': '10', - 'TIMELINE_MIN_AGE': '1800', 'TIMELINE_LIMIT_DAILY': '4-10', - 'SYNC_ACL': 'no', 'QGROUP': '1/0'} + "ListConfigs": [ + [ + "root", + "/", + { + "SUBVOLUME": "/", + "NUMBER_MIN_AGE": "1800", + "TIMELINE_LIMIT_YEARLY": "4-10", + "NUMBER_LIMIT_IMPORTANT": "10", + "FSTYPE": "btrfs", + "TIMELINE_LIMIT_MONTHLY": "4-10", + "ALLOW_GROUPS": "", + "EMPTY_PRE_POST_MIN_AGE": "1800", + "EMPTY_PRE_POST_CLEANUP": "yes", + "BACKGROUND_COMPARISON": "yes", + "TIMELINE_LIMIT_HOURLY": "4-10", + "ALLOW_USERS": "", + "TIMELINE_LIMIT_WEEKLY": "0", + "TIMELINE_CREATE": "no", + "NUMBER_CLEANUP": "yes", + "TIMELINE_CLEANUP": "yes", + "SPACE_LIMIT": "0.5", + "NUMBER_LIMIT": "10", + "TIMELINE_MIN_AGE": "1800", + "TIMELINE_LIMIT_DAILY": "4-10", + "SYNC_ACL": "no", + "QGROUP": "1/0", + }, ] ], - 'GetFiles': [ - ['/root/.viminfo', 8], - ['/tmp/foo', 52], - ['/tmp/foo2', 1], - ['/tmp/foo3', 2], - ['/var/log/snapper.log', 8], - ['/var/cache/salt/minion/extmods/modules/snapper.py', 8], - ['/var/cache/salt/minion/extmods/modules/snapper.pyc', 8], + "GetFiles": [ + ["/root/.viminfo", 8], + ["/tmp/foo", 52], + ["/tmp/foo2", 1], + ["/tmp/foo3", 2], + ["/var/log/snapper.log", 8], + ["/var/cache/salt/minion/extmods/modules/snapper.py", 8], + ["/var/cache/salt/minion/extmods/modules/snapper.pyc", 8], ], } FILE_CONTENT = { - '/tmp/foo': { - "pre": "dummy text", - "post": "another foobar" - }, - '/tmp/foo2': { - "post": "another foobar" - } + "/tmp/foo": {"pre": "dummy text", "post": "another foobar"}, + "/tmp/foo2": {"post": "another foobar"}, } MODULE_RET = { - 'SNAPSHOTS': [ + "SNAPSHOTS": [ { - 'userdata': {'userdata1': 'userval1', 'salt_jid': '20160607130930720112'}, - 'description': 'Some description', 'timestamp': 1457006571, - 'cleanup': '', 'user': 'root', 'type': 'pre', 'id': 42 + "userdata": {"userdata1": "userval1", "salt_jid": "20160607130930720112"}, + "description": "Some description", + "timestamp": 1457006571, + "cleanup": "", + "user": "root", + "type": "pre", + "id": 42, }, { - 'pre': 42, - 'userdata': {'userdata2': 'userval2', 'salt_jid': '20160607130930720112'}, - 'description': 'Blah Blah', 'timestamp': 1457006572, - 'cleanup': '', 'user': 'root', 'type': 'post', 'id': 43 - } + "pre": 42, + "userdata": {"userdata2": "userval2", "salt_jid": "20160607130930720112"}, + "description": "Blah Blah", + "timestamp": 1457006572, + "cleanup": "", + "user": "root", + "type": "post", + "id": 43, + }, ], - 'LISTCONFIGS': { - 'root': { - 'SUBVOLUME': '/', 'NUMBER_MIN_AGE': '1800', - 'TIMELINE_LIMIT_YEARLY': '4-10', 'NUMBER_LIMIT_IMPORTANT': '10', - 'FSTYPE': 'btrfs', 'TIMELINE_LIMIT_MONTHLY': '4-10', - 'ALLOW_GROUPS': '', 'EMPTY_PRE_POST_MIN_AGE': '1800', - 'EMPTY_PRE_POST_CLEANUP': 'yes', 'BACKGROUND_COMPARISON': 'yes', - 'TIMELINE_LIMIT_HOURLY': '4-10', 'ALLOW_USERS': '', - 'TIMELINE_LIMIT_WEEKLY': '0', 'TIMELINE_CREATE': 'no', - 'NUMBER_CLEANUP': 'yes', 'TIMELINE_CLEANUP': 'yes', - 'SPACE_LIMIT': '0.5', 'NUMBER_LIMIT': '10', - 'TIMELINE_MIN_AGE': '1800', 'TIMELINE_LIMIT_DAILY': '4-10', - 'SYNC_ACL': 'no', 'QGROUP': '1/0' + "LISTCONFIGS": { + "root": { + "SUBVOLUME": "/", + "NUMBER_MIN_AGE": "1800", + "TIMELINE_LIMIT_YEARLY": "4-10", + "NUMBER_LIMIT_IMPORTANT": "10", + "FSTYPE": "btrfs", + "TIMELINE_LIMIT_MONTHLY": "4-10", + "ALLOW_GROUPS": "", + "EMPTY_PRE_POST_MIN_AGE": "1800", + "EMPTY_PRE_POST_CLEANUP": "yes", + "BACKGROUND_COMPARISON": "yes", + "TIMELINE_LIMIT_HOURLY": "4-10", + "ALLOW_USERS": "", + "TIMELINE_LIMIT_WEEKLY": "0", + "TIMELINE_CREATE": "no", + "NUMBER_CLEANUP": "yes", + "TIMELINE_CLEANUP": "yes", + "SPACE_LIMIT": "0.5", + "NUMBER_LIMIT": "10", + "TIMELINE_MIN_AGE": "1800", + "TIMELINE_LIMIT_DAILY": "4-10", + "SYNC_ACL": "no", + "QGROUP": "1/0", } }, - 'GETFILES': { - '/root/.viminfo': {'status': ['modified']}, - '/tmp/foo': {'status': ['type changed', 'permission changed', 'owner changed']}, - '/tmp/foo2': {'status': ['created']}, - '/tmp/foo3': {'status': ['deleted']}, - '/var/log/snapper.log': {'status': ['modified']}, - '/var/cache/salt/minion/extmods/modules/snapper.py': {'status': ['modified']}, - '/var/cache/salt/minion/extmods/modules/snapper.pyc': {'status': ['modified']}, + "GETFILES": { + "/root/.viminfo": {"status": ["modified"]}, + "/tmp/foo": {"status": ["type changed", "permission changed", "owner changed"]}, + "/tmp/foo2": {"status": ["created"]}, + "/tmp/foo3": {"status": ["deleted"]}, + "/var/log/snapper.log": {"status": ["modified"]}, + "/var/cache/salt/minion/extmods/modules/snapper.py": {"status": ["modified"]}, + "/var/cache/salt/minion/extmods/modules/snapper.pyc": {"status": ["modified"]}, }, - 'DIFF': { - '/tmp/foo': { - 'comment': 'text file changed', - 'diff': "--- /.snapshots/55/snapshot/tmp/foo\n" - "+++ /tmp/foo\n" - "@@ -1 +1 @@\n" - "-dummy text" - "+another foobar" + "DIFF": { + "/tmp/foo": { + "comment": "text file changed", + "diff": "--- /.snapshots/55/snapshot/tmp/foo\n" + "+++ /tmp/foo\n" + "@@ -1 +1 @@\n" + "-dummy text" + "+another foobar", }, - '/tmp/foo2': { - 'comment': 'text file created', - 'diff': "--- /.snapshots/55/snapshot/tmp/foo2\n" - "+++ /tmp/foo2\n" - "@@ -0,0 +1 @@\n" - "+another foobar", + "/tmp/foo2": { + "comment": "text file created", + "diff": "--- /.snapshots/55/snapshot/tmp/foo2\n" + "+++ /tmp/foo2\n" + "@@ -0,0 +1 @@\n" + "+another foobar", }, - '/tmp/foo26': { - 'comment': 'text file created', - 'diff': "--- /.snapshots/55/snapshot/tmp/foo2 \n" - "+++ /tmp/foo2 \n" - "@@ -1,0 +1,1 @@\n" - "+another foobar", + "/tmp/foo26": { + "comment": "text file created", + "diff": "--- /.snapshots/55/snapshot/tmp/foo2 \n" + "+++ /tmp/foo2 \n" + "@@ -1,0 +1,1 @@\n" + "+another foobar", }, - '/tmp/foo3': { - 'comment': 'binary file changed', - 'old_sha256_digest': 'e61f8b762d83f3b4aeb3689564b0ffbe54fa731a69a1e208dc9440ce0f69d19b', - 'new_sha256_digest': 'f18f971f1517449208a66589085ddd3723f7f6cefb56c141e3d97ae49e1d87fa', - } - } + "/tmp/foo3": { + "comment": "binary file changed", + "old_sha256_digest": "e61f8b762d83f3b4aeb3689564b0ffbe54fa731a69a1e208dc9440ce0f69d19b", + "new_sha256_digest": "f18f971f1517449208a66589085ddd3723f7f6cefb56c141e3d97ae49e1d87fa", + }, + }, } -@skipIf(sys.platform.startswith('win'), 'Snapper not available on Windows') +@skipIf(sys.platform.startswith("win"), "Snapper not available on Windows") class SnapperTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - class DBusException(BaseException): - get_dbus_name = 'foo' + get_dbus_name = "foo" dbus_mock = MagicMock() dbus_mock.configure_mock(DBusException=DBusException) - return {snapper: {'dbus': dbus_mock, 'snapper': MagicMock()}} + return {snapper: {"dbus": dbus_mock, "snapper": MagicMock()}} def test__snapshot_to_data(self): - data = snapper._snapshot_to_data(DBUS_RET['ListSnapshots'][0]) # pylint: disable=protected-access - self.assertEqual(data['id'], 42) - self.assertNotIn('pre', data) - self.assertEqual(data['type'], 'pre') - self.assertEqual(data['user'], 'root') - self.assertEqual(data['timestamp'], 1457006571) - self.assertEqual(data['description'], 'Some description') - self.assertEqual(data['cleanup'], '') - self.assertEqual(data['userdata']['userdata1'], 'userval1') + data = snapper._snapshot_to_data( + DBUS_RET["ListSnapshots"][0] + ) # pylint: disable=protected-access + self.assertEqual(data["id"], 42) + self.assertNotIn("pre", data) + self.assertEqual(data["type"], "pre") + self.assertEqual(data["user"], "root") + self.assertEqual(data["timestamp"], 1457006571) + self.assertEqual(data["description"], "Some description") + self.assertEqual(data["cleanup"], "") + self.assertEqual(data["userdata"]["userdata1"], "userval1") def test_list_snapshots(self): - with patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])): + with patch( + "salt.modules.snapper.snapper.ListSnapshots", + MagicMock(return_value=DBUS_RET["ListSnapshots"]), + ): self.assertEqual(snapper.list_snapshots(), MODULE_RET["SNAPSHOTS"]) def test_get_snapshot(self): - with patch('salt.modules.snapper.snapper.GetSnapshot', MagicMock(return_value=DBUS_RET['ListSnapshots'][0])): + with patch( + "salt.modules.snapper.snapper.GetSnapshot", + MagicMock(return_value=DBUS_RET["ListSnapshots"][0]), + ): self.assertEqual(snapper.get_snapshot(), MODULE_RET["SNAPSHOTS"][0]) - self.assertEqual(snapper.get_snapshot(number=42), MODULE_RET["SNAPSHOTS"][0]) - self.assertNotEqual(snapper.get_snapshot(number=42), MODULE_RET["SNAPSHOTS"][1]) + self.assertEqual( + snapper.get_snapshot(number=42), MODULE_RET["SNAPSHOTS"][0] + ) + self.assertNotEqual( + snapper.get_snapshot(number=42), MODULE_RET["SNAPSHOTS"][1] + ) def test_list_configs(self): - with patch('salt.modules.snapper.snapper.ListConfigs', MagicMock(return_value=DBUS_RET['ListConfigs'])): + with patch( + "salt.modules.snapper.snapper.ListConfigs", + MagicMock(return_value=DBUS_RET["ListConfigs"]), + ): self.assertEqual(snapper.list_configs(), MODULE_RET["LISTCONFIGS"]) def test_get_config(self): - with patch('salt.modules.snapper.snapper.GetConfig', MagicMock(return_value=DBUS_RET['ListConfigs'][0])): + with patch( + "salt.modules.snapper.snapper.GetConfig", + MagicMock(return_value=DBUS_RET["ListConfigs"][0]), + ): self.assertEqual(snapper.get_config(), DBUS_RET["ListConfigs"][0]) def test_set_config(self): - with patch('salt.modules.snapper.snapper.SetConfig', MagicMock()): - opts = {'sync_acl': True, 'dummy': False, 'foobar': 1234} + with patch("salt.modules.snapper.snapper.SetConfig", MagicMock()): + opts = {"sync_acl": True, "dummy": False, "foobar": 1234} self.assertEqual(snapper.set_config(opts), True) def test_status_to_string(self): @@ -191,202 +248,356 @@ class SnapperTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(snapper.status_to_string(4), ["type changed"]) self.assertEqual(snapper.status_to_string(8), ["modified"]) self.assertEqual(snapper.status_to_string(16), ["permission changed"]) - self.assertListEqual(snapper.status_to_string(24), ["modified", "permission changed"]) + self.assertListEqual( + snapper.status_to_string(24), ["modified", "permission changed"] + ) self.assertEqual(snapper.status_to_string(32), ["owner changed"]) self.assertEqual(snapper.status_to_string(64), ["group changed"]) - self.assertListEqual(snapper.status_to_string(97), ["created", "owner changed", "group changed"]) + self.assertListEqual( + snapper.status_to_string(97), ["created", "owner changed", "group changed"] + ) self.assertEqual(snapper.status_to_string(128), ["extended attributes changed"]) self.assertEqual(snapper.status_to_string(256), ["ACL info changed"]) def test_create_config(self): - with patch('salt.modules.snapper.snapper.CreateConfig', MagicMock()), \ - patch('salt.modules.snapper.snapper.GetConfig', MagicMock(return_value=DBUS_RET['ListConfigs'][0])): + with patch("salt.modules.snapper.snapper.CreateConfig", MagicMock()), patch( + "salt.modules.snapper.snapper.GetConfig", + MagicMock(return_value=DBUS_RET["ListConfigs"][0]), + ): opts = { - 'name': 'testconfig', - 'subvolume': '/foo/bar/', - 'fstype': 'btrfs', - 'template': 'mytemplate', - 'extra_opts': {"NUMBER_CLEANUP": False}, + "name": "testconfig", + "subvolume": "/foo/bar/", + "fstype": "btrfs", + "template": "mytemplate", + "extra_opts": {"NUMBER_CLEANUP": False}, } - with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock: - self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0]) - set_config_mock.assert_called_with("testconfig", **opts['extra_opts']) + with patch( + "salt.modules.snapper.set_config", MagicMock() + ) as set_config_mock: + self.assertEqual( + snapper.create_config(**opts), DBUS_RET["ListConfigs"][0] + ) + set_config_mock.assert_called_with("testconfig", **opts["extra_opts"]) - with patch('salt.modules.snapper.set_config', MagicMock()) as set_config_mock: - del opts['extra_opts'] - self.assertEqual(snapper.create_config(**opts), DBUS_RET['ListConfigs'][0]) + with patch( + "salt.modules.snapper.set_config", MagicMock() + ) as set_config_mock: + del opts["extra_opts"] + self.assertEqual( + snapper.create_config(**opts), DBUS_RET["ListConfigs"][0] + ) assert not set_config_mock.called self.assertRaises(CommandExecutionError, snapper.create_config) def test_create_snapshot(self): - with patch('salt.modules.snapper.snapper.CreateSingleSnapshot', MagicMock(return_value=1234)), \ - patch('salt.modules.snapper.snapper.CreatePreSnapshot', MagicMock(return_value=1234)), \ - patch('salt.modules.snapper.snapper.CreatePostSnapshot', MagicMock(return_value=1234)): - for snapshot_type in ['pre', 'post', 'single']: + with patch( + "salt.modules.snapper.snapper.CreateSingleSnapshot", + MagicMock(return_value=1234), + ), patch( + "salt.modules.snapper.snapper.CreatePreSnapshot", + MagicMock(return_value=1234), + ), patch( + "salt.modules.snapper.snapper.CreatePostSnapshot", + MagicMock(return_value=1234), + ): + for snapshot_type in ["pre", "post", "single"]: opts = { - '__pub_jid': 20160607130930720112, - 'type': snapshot_type, - 'description': 'Test description', - 'cleanup_algorithm': 'number', - 'pre_number': 23, + "__pub_jid": 20160607130930720112, + "type": snapshot_type, + "description": "Test description", + "cleanup_algorithm": "number", + "pre_number": 23, } self.assertEqual(snapper.create_snapshot(**opts), 1234) def test_delete_snapshot_id_success(self): - with patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock()), \ - patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])): - self.assertEqual(snapper.delete_snapshot(snapshots_ids=43), {"root": {"ids": [43], "status": "deleted"}}) - self.assertEqual(snapper.delete_snapshot(snapshots_ids=[42, 43]), - {"root": {"ids": [42, 43], "status": "deleted"}}) + with patch("salt.modules.snapper.snapper.DeleteSnapshots", MagicMock()), patch( + "salt.modules.snapper.snapper.ListSnapshots", + MagicMock(return_value=DBUS_RET["ListSnapshots"]), + ): + self.assertEqual( + snapper.delete_snapshot(snapshots_ids=43), + {"root": {"ids": [43], "status": "deleted"}}, + ) + self.assertEqual( + snapper.delete_snapshot(snapshots_ids=[42, 43]), + {"root": {"ids": [42, 43], "status": "deleted"}}, + ) def test_delete_snapshot_id_fail(self): - with patch('salt.modules.snapper.snapper.DeleteSnapshots', MagicMock()), \ - patch('salt.modules.snapper.snapper.ListSnapshots', MagicMock(return_value=DBUS_RET['ListSnapshots'])): + with patch("salt.modules.snapper.snapper.DeleteSnapshots", MagicMock()), patch( + "salt.modules.snapper.snapper.ListSnapshots", + MagicMock(return_value=DBUS_RET["ListSnapshots"]), + ): self.assertRaises(CommandExecutionError, snapper.delete_snapshot) - self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=1) - self.assertRaises(CommandExecutionError, snapper.delete_snapshot, snapshots_ids=[1, 2]) + self.assertRaises( + CommandExecutionError, snapper.delete_snapshot, snapshots_ids=1 + ) + self.assertRaises( + CommandExecutionError, snapper.delete_snapshot, snapshots_ids=[1, 2] + ) def test_modify_snapshot(self): - with patch('salt.modules.snapper.snapper.SetSnapshot', MagicMock()): + with patch("salt.modules.snapper.snapper.SetSnapshot", MagicMock()): _ret = { - 'userdata': {'userdata2': 'uservalue2'}, - 'description': 'UPDATED DESCRIPTION', 'timestamp': 1457006571, - 'cleanup': 'number', 'user': 'root', 'type': 'pre', 'id': 42 + "userdata": {"userdata2": "uservalue2"}, + "description": "UPDATED DESCRIPTION", + "timestamp": 1457006571, + "cleanup": "number", + "user": "root", + "type": "pre", + "id": 42, } _opts = { - 'config': 'root', - 'snapshot_id': 42, - 'cleanup': 'number', - 'description': 'UPDATED DESCRIPTION', - 'userdata': {'userdata2': 'uservalue2'}, + "config": "root", + "snapshot_id": 42, + "cleanup": "number", + "description": "UPDATED DESCRIPTION", + "userdata": {"userdata2": "uservalue2"}, } - with patch('salt.modules.snapper.get_snapshot', MagicMock(side_effect=[DBUS_RET['ListSnapshots'][0], _ret])): + with patch( + "salt.modules.snapper.get_snapshot", + MagicMock(side_effect=[DBUS_RET["ListSnapshots"][0], _ret]), + ): self.assertDictEqual(snapper.modify_snapshot(**_opts), _ret) def test__get_num_interval(self): - with patch('salt.modules.snapper._get_last_snapshot', MagicMock(return_value={'id': 42})): - self.assertEqual(snapper._get_num_interval(config=None, num_pre=None, num_post=None), (42, 0)) # pylint: disable=protected-access - self.assertEqual(snapper._get_num_interval(config=None, num_pre=None, num_post=50), (42, 50)) # pylint: disable=protected-access - self.assertEqual(snapper._get_num_interval(config=None, num_pre=42, num_post=50), (42, 50)) # pylint: disable=protected-access + with patch( + "salt.modules.snapper._get_last_snapshot", + MagicMock(return_value={"id": 42}), + ): + self.assertEqual( + snapper._get_num_interval(config=None, num_pre=None, num_post=None), + (42, 0), + ) # pylint: disable=protected-access + self.assertEqual( + snapper._get_num_interval(config=None, num_pre=None, num_post=50), + (42, 50), + ) # pylint: disable=protected-access + self.assertEqual( + snapper._get_num_interval(config=None, num_pre=42, num_post=50), + (42, 50), + ) # pylint: disable=protected-access def test_run(self): patch_dict = { - 'snapper.create_snapshot': MagicMock(return_value=43), - 'test.ping': MagicMock(return_value=True), + "snapper.create_snapshot": MagicMock(return_value=43), + "test.ping": MagicMock(return_value=True), } with patch.dict(snapper.__salt__, patch_dict): self.assertEqual(snapper.run("test.ping"), True) self.assertRaises(CommandExecutionError, snapper.run, "unknown.func") def test_status(self): - with patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(42, 43))), \ - patch('salt.modules.snapper.snapper.GetComparison', MagicMock()), \ - patch('salt.modules.snapper.snapper.GetFiles', MagicMock(return_value=DBUS_RET['GetFiles'])), \ - patch('salt.modules.snapper.snapper.ListConfigs', MagicMock(return_value=DBUS_RET['ListConfigs'])): + with patch( + "salt.modules.snapper._get_num_interval", MagicMock(return_value=(42, 43)) + ), patch("salt.modules.snapper.snapper.GetComparison", MagicMock()), patch( + "salt.modules.snapper.snapper.GetFiles", + MagicMock(return_value=DBUS_RET["GetFiles"]), + ), patch( + "salt.modules.snapper.snapper.ListConfigs", + MagicMock(return_value=DBUS_RET["ListConfigs"]), + ): if six.PY3: - self.assertCountEqual(snapper.status(), MODULE_RET['GETFILES']) - self.assertCountEqual(snapper.status(num_pre="42", num_post=43), MODULE_RET['GETFILES']) - self.assertCountEqual(snapper.status(num_pre=42), MODULE_RET['GETFILES']) - self.assertCountEqual(snapper.status(num_post=43), MODULE_RET['GETFILES']) + self.assertCountEqual(snapper.status(), MODULE_RET["GETFILES"]) + self.assertCountEqual( + snapper.status(num_pre="42", num_post=43), MODULE_RET["GETFILES"] + ) + self.assertCountEqual( + snapper.status(num_pre=42), MODULE_RET["GETFILES"] + ) + self.assertCountEqual( + snapper.status(num_post=43), MODULE_RET["GETFILES"] + ) else: - self.assertItemsEqual(snapper.status(), MODULE_RET['GETFILES']) - self.assertItemsEqual(snapper.status(num_pre="42", num_post=43), MODULE_RET['GETFILES']) - self.assertItemsEqual(snapper.status(num_pre=42), MODULE_RET['GETFILES']) - self.assertItemsEqual(snapper.status(num_post=43), MODULE_RET['GETFILES']) + self.assertItemsEqual(snapper.status(), MODULE_RET["GETFILES"]) + self.assertItemsEqual( + snapper.status(num_pre="42", num_post=43), MODULE_RET["GETFILES"] + ) + self.assertItemsEqual( + snapper.status(num_pre=42), MODULE_RET["GETFILES"] + ) + self.assertItemsEqual( + snapper.status(num_post=43), MODULE_RET["GETFILES"] + ) def test_changed_files(self): - with patch('salt.modules.snapper.status', MagicMock(return_value=MODULE_RET['GETFILES'])): - self.assertEqual(snapper.changed_files(), MODULE_RET['GETFILES'].keys()) + with patch( + "salt.modules.snapper.status", + MagicMock(return_value=MODULE_RET["GETFILES"]), + ): + self.assertEqual(snapper.changed_files(), MODULE_RET["GETFILES"].keys()) def test_undo(self): - with patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(42, 43))), \ - patch('salt.modules.snapper.status', MagicMock(return_value=MODULE_RET['GETFILES'])): - cmd_ret = 'create:0 modify:1 delete:0' - with patch.dict(snapper.__salt__, {'cmd.run': MagicMock(return_value=cmd_ret)}): - module_ret = {'create': '0', 'delete': '0', 'modify': '1'} - self.assertEqual(snapper.undo(files=['/tmp/foo']), module_ret) + with patch( + "salt.modules.snapper._get_num_interval", MagicMock(return_value=(42, 43)) + ), patch( + "salt.modules.snapper.status", + MagicMock(return_value=MODULE_RET["GETFILES"]), + ): + cmd_ret = "create:0 modify:1 delete:0" + with patch.dict( + snapper.__salt__, {"cmd.run": MagicMock(return_value=cmd_ret)} + ): + module_ret = {"create": "0", "delete": "0", "modify": "1"} + self.assertEqual(snapper.undo(files=["/tmp/foo"]), module_ret) - cmd_ret = 'create:1 modify:1 delete:0' - with patch.dict(snapper.__salt__, {'cmd.run': MagicMock(return_value=cmd_ret)}): - module_ret = {'create': '1', 'delete': '0', 'modify': '1'} - self.assertEqual(snapper.undo(files=['/tmp/foo', '/tmp/foo2']), module_ret) + cmd_ret = "create:1 modify:1 delete:0" + with patch.dict( + snapper.__salt__, {"cmd.run": MagicMock(return_value=cmd_ret)} + ): + module_ret = {"create": "1", "delete": "0", "modify": "1"} + self.assertEqual( + snapper.undo(files=["/tmp/foo", "/tmp/foo2"]), module_ret + ) - cmd_ret = 'create:1 modify:1 delete:1' - with patch.dict(snapper.__salt__, {'cmd.run': MagicMock(return_value=cmd_ret)}): - module_ret = {'create': '1', 'delete': '1', 'modify': '1'} - self.assertEqual(snapper.undo(files=['/tmp/foo', '/tmp/foo2', '/tmp/foo3']), module_ret) + cmd_ret = "create:1 modify:1 delete:1" + with patch.dict( + snapper.__salt__, {"cmd.run": MagicMock(return_value=cmd_ret)} + ): + module_ret = {"create": "1", "delete": "1", "modify": "1"} + self.assertEqual( + snapper.undo(files=["/tmp/foo", "/tmp/foo2", "/tmp/foo3"]), + module_ret, + ) def test__get_jid_snapshots(self): - with patch('salt.modules.snapper.list_snapshots', MagicMock(return_value=MODULE_RET['SNAPSHOTS'])): + with patch( + "salt.modules.snapper.list_snapshots", + MagicMock(return_value=MODULE_RET["SNAPSHOTS"]), + ): self.assertEqual( - snapper._get_jid_snapshots("20160607130930720112"), # pylint: disable=protected-access - (MODULE_RET['SNAPSHOTS'][0]['id'], MODULE_RET['SNAPSHOTS'][1]['id']) + snapper._get_jid_snapshots( + "20160607130930720112" + ), # pylint: disable=protected-access + (MODULE_RET["SNAPSHOTS"][0]["id"], MODULE_RET["SNAPSHOTS"][1]["id"]), ) def test_undo_jid(self): - with patch('salt.modules.snapper._get_jid_snapshots', MagicMock(return_value=(42, 43))), \ - patch('salt.modules.snapper.undo', MagicMock(return_value='create:1 modify:1 delete:1')): - self.assertEqual(snapper.undo_jid(20160607130930720112), 'create:1 modify:1 delete:1') + with patch( + "salt.modules.snapper._get_jid_snapshots", MagicMock(return_value=(42, 43)) + ), patch( + "salt.modules.snapper.undo", + MagicMock(return_value="create:1 modify:1 delete:1"), + ): + self.assertEqual( + snapper.undo_jid(20160607130930720112), "create:1 modify:1 delete:1" + ) def test_diff_text_file(self): - with patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(42, 43))), \ - patch('salt.modules.snapper.snapper.MountSnapshot', - MagicMock(side_effect=["/.snapshots/55/snapshot", ""])), \ - patch('salt.modules.snapper.snapper.UmountSnapshot', MagicMock(return_value="")), \ - patch('os.path.isdir', MagicMock(return_value=False)), \ - patch('salt.modules.snapper.changed_files', MagicMock(return_value=["/tmp/foo2"])), \ - patch('salt.modules.snapper._is_text_file', MagicMock(return_value=True)), \ - patch('os.path.isfile', MagicMock(side_effect=[False, True])), \ - patch('salt.utils.files.fopen', mock_open(read_data=FILE_CONTENT["/tmp/foo2"]['post'])), \ - patch('salt.modules.snapper.snapper.ListConfigs', MagicMock(return_value=DBUS_RET['ListConfigs'])): + with patch( + "salt.modules.snapper._get_num_interval", MagicMock(return_value=(42, 43)) + ), patch( + "salt.modules.snapper.snapper.MountSnapshot", + MagicMock(side_effect=["/.snapshots/55/snapshot", ""]), + ), patch( + "salt.modules.snapper.snapper.UmountSnapshot", MagicMock(return_value="") + ), patch( + "os.path.isdir", MagicMock(return_value=False) + ), patch( + "salt.modules.snapper.changed_files", MagicMock(return_value=["/tmp/foo2"]) + ), patch( + "salt.modules.snapper._is_text_file", MagicMock(return_value=True) + ), patch( + "os.path.isfile", MagicMock(side_effect=[False, True]) + ), patch( + "salt.utils.files.fopen", + mock_open(read_data=FILE_CONTENT["/tmp/foo2"]["post"]), + ), patch( + "salt.modules.snapper.snapper.ListConfigs", + MagicMock(return_value=DBUS_RET["ListConfigs"]), + ): if sys.version_info < (2, 7): - self.assertEqual(snapper.diff(), {"/tmp/foo2": MODULE_RET['DIFF']['/tmp/foo26']}) + self.assertEqual( + snapper.diff(), {"/tmp/foo2": MODULE_RET["DIFF"]["/tmp/foo26"]} + ) else: - self.assertEqual(snapper.diff(), {"/tmp/foo2": MODULE_RET['DIFF']['/tmp/foo2']}) + self.assertEqual( + snapper.diff(), {"/tmp/foo2": MODULE_RET["DIFF"]["/tmp/foo2"]} + ) - @skipIf(sys.version_info < (2, 7), 'Python 2.7 required to compare diff properly') + @skipIf(sys.version_info < (2, 7), "Python 2.7 required to compare diff properly") def test_diff_text_files(self): - with patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(55, 0))), \ - patch('salt.modules.snapper.snapper.MountSnapshot', - MagicMock(side_effect=["/.snapshots/55/snapshot", "", "/.snapshots/55/snapshot", ""])), \ - patch('salt.modules.snapper.snapper.UmountSnapshot', MagicMock(return_value="")), \ - patch('salt.modules.snapper.changed_files', MagicMock(return_value=["/tmp/foo", "/tmp/foo2"])), \ - patch('salt.modules.snapper._is_text_file', MagicMock(return_value=True)), \ - patch('os.path.isfile', MagicMock(side_effect=[True, True, False, True])), \ - patch('os.path.isdir', MagicMock(return_value=False)), \ - patch('salt.modules.snapper.snapper.ListConfigs', MagicMock(return_value=DBUS_RET['ListConfigs'])): + with patch( + "salt.modules.snapper._get_num_interval", MagicMock(return_value=(55, 0)) + ), patch( + "salt.modules.snapper.snapper.MountSnapshot", + MagicMock( + side_effect=[ + "/.snapshots/55/snapshot", + "", + "/.snapshots/55/snapshot", + "", + ] + ), + ), patch( + "salt.modules.snapper.snapper.UmountSnapshot", MagicMock(return_value="") + ), patch( + "salt.modules.snapper.changed_files", + MagicMock(return_value=["/tmp/foo", "/tmp/foo2"]), + ), patch( + "salt.modules.snapper._is_text_file", MagicMock(return_value=True) + ), patch( + "os.path.isfile", MagicMock(side_effect=[True, True, False, True]) + ), patch( + "os.path.isdir", MagicMock(return_value=False) + ), patch( + "salt.modules.snapper.snapper.ListConfigs", + MagicMock(return_value=DBUS_RET["ListConfigs"]), + ): contents = { - '*/tmp/foo': [ - FILE_CONTENT['/tmp/foo']['pre'], - FILE_CONTENT['/tmp/foo']['post'], + "*/tmp/foo": [ + FILE_CONTENT["/tmp/foo"]["pre"], + FILE_CONTENT["/tmp/foo"]["post"], ], - '*/tmp/foo2': FILE_CONTENT['/tmp/foo2']['post'], + "*/tmp/foo2": FILE_CONTENT["/tmp/foo2"]["post"], } - with patch('salt.utils.files.fopen', mock_open(read_data=contents)): + with patch("salt.utils.files.fopen", mock_open(read_data=contents)): module_ret = { - "/tmp/foo": MODULE_RET['DIFF']["/tmp/foo"], - "/tmp/foo2": MODULE_RET['DIFF']["/tmp/foo2"], + "/tmp/foo": MODULE_RET["DIFF"]["/tmp/foo"], + "/tmp/foo2": MODULE_RET["DIFF"]["/tmp/foo2"], } self.assertEqual(snapper.diff(), module_ret) def test_diff_binary_files(self): - with patch('salt.modules.snapper._get_num_interval', MagicMock(return_value=(55, 0))), \ - patch('salt.modules.snapper.snapper.MountSnapshot', - MagicMock(side_effect=["/.snapshots/55/snapshot", "", "/.snapshots/55/snapshot", ""])), \ - patch('salt.modules.snapper.snapper.UmountSnapshot', MagicMock(return_value="")), \ - patch('salt.modules.snapper.changed_files', MagicMock(return_value=["/tmp/foo3"])), \ - patch('salt.modules.snapper._is_text_file', MagicMock(return_value=False)), \ - patch('os.path.isfile', MagicMock(side_effect=[True, True])), \ - patch('os.path.isdir', MagicMock(return_value=False)), \ - patch('salt.modules.snapper.snapper.ListConfigs', MagicMock(return_value=DBUS_RET['ListConfigs'])), \ - patch.dict(snapper.__salt__, { - 'hashutil.sha256_digest': MagicMock(side_effect=[ - "e61f8b762d83f3b4aeb3689564b0ffbe54fa731a69a1e208dc9440ce0f69d19b", - "f18f971f1517449208a66589085ddd3723f7f6cefb56c141e3d97ae49e1d87fa", - ]) - }): - with patch('salt.utils.files.fopen', mock_open(read_data='dummy binary')): + with patch( + "salt.modules.snapper._get_num_interval", MagicMock(return_value=(55, 0)) + ), patch( + "salt.modules.snapper.snapper.MountSnapshot", + MagicMock( + side_effect=[ + "/.snapshots/55/snapshot", + "", + "/.snapshots/55/snapshot", + "", + ] + ), + ), patch( + "salt.modules.snapper.snapper.UmountSnapshot", MagicMock(return_value="") + ), patch( + "salt.modules.snapper.changed_files", MagicMock(return_value=["/tmp/foo3"]) + ), patch( + "salt.modules.snapper._is_text_file", MagicMock(return_value=False) + ), patch( + "os.path.isfile", MagicMock(side_effect=[True, True]) + ), patch( + "os.path.isdir", MagicMock(return_value=False) + ), patch( + "salt.modules.snapper.snapper.ListConfigs", + MagicMock(return_value=DBUS_RET["ListConfigs"]), + ), patch.dict( + snapper.__salt__, + { + "hashutil.sha256_digest": MagicMock( + side_effect=[ + "e61f8b762d83f3b4aeb3689564b0ffbe54fa731a69a1e208dc9440ce0f69d19b", + "f18f971f1517449208a66589085ddd3723f7f6cefb56c141e3d97ae49e1d87fa", + ] + ) + }, + ): + with patch("salt.utils.files.fopen", mock_open(read_data="dummy binary")): module_ret = { - "/tmp/foo3": MODULE_RET['DIFF']["/tmp/foo3"], + "/tmp/foo3": MODULE_RET["DIFF"]["/tmp/foo3"], } self.assertEqual(snapper.diff(), module_ret) diff --git a/tests/unit/modules/test_solarisipspkg.py b/tests/unit/modules/test_solarisipspkg.py index 5bb6d601661..320db023605 100644 --- a/tests/unit/modules/test_solarisipspkg.py +++ b/tests/unit/modules/test_solarisipspkg.py @@ -2,295 +2,307 @@ # Import Python Libs from __future__ import absolute_import + import sys -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.modules.pkg_resource as pkg_resource # Import Salt libs import salt.modules.solarisipspkg as solarisips -import salt.modules.pkg_resource as pkg_resource import salt.utils.data +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -@skipIf(sys.platform != 'solaris', 'Skip when not running on Solaris') + +@skipIf(sys.platform != "solaris", "Skip when not running on Solaris") class IpsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.solarisips - ''' + """ + def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() - utils = salt.loader.utils( - opts, - whitelist=['pkg', 'path', 'platform']) + utils = salt.loader.utils(opts, whitelist=["pkg", "path", "platform"]) return { pkg_resource: { - '__grains__': { - 'osarch': 'sparcv9', - 'os_family': 'Solaris', - 'osmajorrelease': 11, - 'kernelrelease': 5.11, + "__grains__": { + "osarch": "sparcv9", + "os_family": "Solaris", + "osmajorrelease": 11, + "kernelrelease": 5.11, }, }, - solarisips: { - '__opts__': opts, - '__utils__': utils, - } + solarisips: {"__opts__": opts, "__utils__": utils}, } def test_install_single_package(self): - ''' + """ Test installing a single package - ''' + """ pkg_list_pre = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", } pkg_list_post = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', - 'pkg://solaris/text/less': '458,5.11-0.175.3.0.0.30.0:20150821T172730Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", + "pkg://solaris/text/less": "458,5.11-0.175.3.0.0.30.0:20150821T172730Z", } install_cmd = { - 'pid': 1234, - 'retcode': 0, - 'stderr': '', - 'stdout': '', + "pid": 1234, + "retcode": 0, + "stderr": "", + "stdout": "", } mock_install_cmd = MagicMock(return_value=install_cmd) list_pkgs_responses = [pkg_list_pre, pkg_list_post] - with patch.object(solarisips, 'is_installed', return_value=False), \ - patch.object(solarisips, 'list_pkgs', side_effect=list_pkgs_responses), \ - patch.dict(solarisips.__salt__, {'cmd.run_all': mock_install_cmd}): - result = solarisips.install(name='less', refresh=False) - self.assertEqual(result, salt.utils.data.compare_dicts(pkg_list_pre, pkg_list_post)) + with patch.object(solarisips, "is_installed", return_value=False), patch.object( + solarisips, "list_pkgs", side_effect=list_pkgs_responses + ), patch.dict(solarisips.__salt__, {"cmd.run_all": mock_install_cmd}): + result = solarisips.install(name="less", refresh=False) + self.assertEqual( + result, salt.utils.data.compare_dicts(pkg_list_pre, pkg_list_post) + ) def test_install_list_pkgs(self): - ''' + """ Test installing a list of packages - ''' + """ pkg_list_pre = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", } pkg_list_post = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', - 'pkg://solaris/text/less': '458,5.11-0.175.3.0.0.30.0:20150821T172730Z', - 'pkg://solaris/system/library/security/libsasl': '0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", + "pkg://solaris/text/less": "458,5.11-0.175.3.0.0.30.0:20150821T172730Z", + "pkg://solaris/system/library/security/libsasl": "0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z", } install_cmd = { - 'pid': 1234, - 'retcode': 0, - 'stderr': '', - 'stdout': '', + "pid": 1234, + "retcode": 0, + "stderr": "", + "stdout": "", } mock_install_cmd = MagicMock(return_value=install_cmd) list_pkgs_responses = [pkg_list_pre, pkg_list_post] - with patch.object(solarisips, 'is_installed', return_value=False), \ - patch.object(solarisips, 'list_pkgs', side_effect=list_pkgs_responses), \ - patch.dict(solarisips.__salt__, {'cmd.run_all': mock_install_cmd}): - result = solarisips.install(pkgs=['less', 'libsasl'], refresh=False) - self.assertEqual(result, salt.utils.data.compare_dicts(pkg_list_pre, pkg_list_post)) + with patch.object(solarisips, "is_installed", return_value=False), patch.object( + solarisips, "list_pkgs", side_effect=list_pkgs_responses + ), patch.dict(solarisips.__salt__, {"cmd.run_all": mock_install_cmd}): + result = solarisips.install(pkgs=["less", "libsasl"], refresh=False) + self.assertEqual( + result, salt.utils.data.compare_dicts(pkg_list_pre, pkg_list_post) + ) def test_install_dict_pkgs_no_version(self): - ''' + """ Test installing a list of packages - ''' + """ pkg_list_pre = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", } pkg_list_post = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', - 'pkg://solaris/text/less': '458,5.11-0.175.3.0.0.30.0:20150821T172730Z', - 'pkg://solaris/system/library/security/libsasl': '0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", + "pkg://solaris/text/less": "458,5.11-0.175.3.0.0.30.0:20150821T172730Z", + "pkg://solaris/system/library/security/libsasl": "0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z", } install_cmd = { - 'pid': 1234, - 'retcode': 0, - 'stderr': '', - 'stdout': '', + "pid": 1234, + "retcode": 0, + "stderr": "", + "stdout": "", } mock_install_cmd = MagicMock(return_value=install_cmd) list_pkgs_responses = [pkg_list_pre, pkg_list_post] - with patch.object(solarisips, 'is_installed', return_value=False), \ - patch.object(solarisips, 'list_pkgs', side_effect=list_pkgs_responses), \ - patch.dict(solarisips.__salt__, {'cmd.run_all': mock_install_cmd}): - result = solarisips.install(pkgs=[{'less': ''}, {'libsasl': ''}], refresh=False) - self.assertEqual(result, salt.utils.data.compare_dicts(pkg_list_pre, pkg_list_post)) + with patch.object(solarisips, "is_installed", return_value=False), patch.object( + solarisips, "list_pkgs", side_effect=list_pkgs_responses + ), patch.dict(solarisips.__salt__, {"cmd.run_all": mock_install_cmd}): + result = solarisips.install( + pkgs=[{"less": ""}, {"libsasl": ""}], refresh=False + ) + self.assertEqual( + result, salt.utils.data.compare_dicts(pkg_list_pre, pkg_list_post) + ) def test_install_dict_pkgs_with_version(self): - ''' + """ Test installing a list of packages - ''' + """ pkg_list_pre = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", } pkg_list_post = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', - 'pkg://solaris/text/less': '458,5.11-0.175.3.0.0.30.0:20150821T172730Z', - 'pkg://solaris/system/library/security/libsasl': '0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", + "pkg://solaris/text/less": "458,5.11-0.175.3.0.0.30.0:20150821T172730Z", + "pkg://solaris/system/library/security/libsasl": "0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z", } install_cmd = { - 'pid': 1234, - 'retcode': 0, - 'stderr': '', - 'stdout': '', + "pid": 1234, + "retcode": 0, + "stderr": "", + "stdout": "", } mock_install_cmd = MagicMock(return_value=install_cmd) list_pkgs_responses = [pkg_list_pre, pkg_list_post] - with patch.object(solarisips, 'is_installed', return_value=False), \ - patch.object(solarisips, 'list_pkgs', side_effect=list_pkgs_responses), \ - patch.dict(solarisips.__salt__, {'cmd.run_all': mock_install_cmd}): - result = solarisips.install(pkgs=[ - {'less': '458,5.11-0.175.3.0.0.30.0:20150821T172730Z'}, - {'libsasl': '0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z'}], refresh=False) - self.assertEqual(result, salt.utils.data.compare_dicts(pkg_list_pre, pkg_list_post)) + with patch.object(solarisips, "is_installed", return_value=False), patch.object( + solarisips, "list_pkgs", side_effect=list_pkgs_responses + ), patch.dict(solarisips.__salt__, {"cmd.run_all": mock_install_cmd}): + result = solarisips.install( + pkgs=[ + {"less": "458,5.11-0.175.3.0.0.30.0:20150821T172730Z"}, + {"libsasl": "0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z"}, + ], + refresh=False, + ) + self.assertEqual( + result, salt.utils.data.compare_dicts(pkg_list_pre, pkg_list_post) + ) def test_install_already_installed_single_pkg(self): - ''' + """ Test installing a package that is already installed - ''' + """ result = None expected_result = {} - with patch.object(solarisips, 'is_installed', return_value=True): - result = solarisips.install(name='less') + with patch.object(solarisips, "is_installed", return_value=True): + result = solarisips.install(name="less") self.assertEqual(result, expected_result) def test_install_dict_pkgs_with_version_validate_cmd(self): - ''' + """ Test installing a list of packages - ''' + """ + def check_param(arg, **kwargs): - self.assertEqual(arg, [ - 'pkg', - 'install', - '-v', - '--accept', - 'less@458,5.11-0.175.3.0.0.30.0:20150821T172730Z', - 'libsasl@0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z' - ]) + self.assertEqual( + arg, + [ + "pkg", + "install", + "-v", + "--accept", + "less@458,5.11-0.175.3.0.0.30.0:20150821T172730Z", + "libsasl@0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z", + ], + ) return { - 'pid': 1234, - 'retcode': 0, - 'stderr': '', - 'stdout': '', + "pid": 1234, + "retcode": 0, + "stderr": "", + "stdout": "", } pkg_list_pre = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", } pkg_list_post = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', - 'pkg://solaris/text/less': '458,5.11-0.175.3.0.0.30.0:20150821T172730Z', - 'pkg://solaris/system/library/security/libsasl': '0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", + "pkg://solaris/text/less": "458,5.11-0.175.3.0.0.30.0:20150821T172730Z", + "pkg://solaris/system/library/security/libsasl": "0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z", } mock_install_cmd = MagicMock(side_effect=check_param) list_pkgs_responses = [pkg_list_pre, pkg_list_post] - with patch.object(solarisips, 'is_installed', return_value=False), \ - patch.object(solarisips, 'list_pkgs', side_effect=list_pkgs_responses): - with patch.dict(solarisips.__salt__, {'cmd.run_all': mock_install_cmd}): - result = solarisips.install(pkgs=[ - {'less': '458,5.11-0.175.3.0.0.30.0:20150821T172730Z'}, - {'libsasl': '0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z'}], refresh=False) + with patch.object(solarisips, "is_installed", return_value=False), patch.object( + solarisips, "list_pkgs", side_effect=list_pkgs_responses + ): + with patch.dict(solarisips.__salt__, {"cmd.run_all": mock_install_cmd}): + result = solarisips.install( + pkgs=[ + {"less": "458,5.11-0.175.3.0.0.30.0:20150821T172730Z"}, + {"libsasl": "0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z"}, + ], + refresh=False, + ) def test_install_dict_pkgs_no_version_validate_cmd(self): - ''' + """ Test installing a list of packages - ''' + """ + def check_param(arg, **kwargs): - self.assertEqual(arg, [ - 'pkg', - 'install', - '-v', - '--accept', - 'less', - 'libsasl' - ]) + self.assertEqual( + arg, ["pkg", "install", "-v", "--accept", "less", "libsasl"] + ) return { - 'pid': 1234, - 'retcode': 0, - 'stderr': '', - 'stdout': '', + "pid": 1234, + "retcode": 0, + "stderr": "", + "stdout": "", } pkg_list_pre = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", } pkg_list_post = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', - 'pkg://solaris/text/less': '458,5.11-0.175.3.0.0.30.0:20150821T172730Z', - 'pkg://solaris/system/library/security/libsasl': '0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", + "pkg://solaris/text/less": "458,5.11-0.175.3.0.0.30.0:20150821T172730Z", + "pkg://solaris/system/library/security/libsasl": "0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z", } mock_install_cmd = MagicMock(side_effect=check_param) list_pkgs_responses = [pkg_list_pre, pkg_list_post] - with patch.object(solarisips, 'is_installed', return_value=False), \ - patch.object(solarisips, 'list_pkgs', side_effect=list_pkgs_responses): - with patch.dict(solarisips.__salt__, {'cmd.run_all': mock_install_cmd}): - result = solarisips.install(pkgs=[ - {'less': ''}, - {'libsasl': ''}], refresh=False) + with patch.object(solarisips, "is_installed", return_value=False), patch.object( + solarisips, "list_pkgs", side_effect=list_pkgs_responses + ): + with patch.dict(solarisips.__salt__, {"cmd.run_all": mock_install_cmd}): + result = solarisips.install( + pkgs=[{"less": ""}, {"libsasl": ""}], refresh=False + ) def test_install_list_pkgs_validate_cmd(self): - ''' + """ Test installing a list of packages - ''' + """ + def check_param(arg, **kwargs): - self.assertEqual(arg, [ - 'pkg', - 'install', - '-v', - '--accept', - 'less', - 'libsasl' - ]) + self.assertEqual( + arg, ["pkg", "install", "-v", "--accept", "less", "libsasl"] + ) return { - 'pid': 1234, - 'retcode': 0, - 'stderr': '', - 'stdout': '', + "pid": 1234, + "retcode": 0, + "stderr": "", + "stdout": "", } pkg_list_pre = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", } pkg_list_post = { - 'pkg://solaris/compress/bzip2': '1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z', - 'pkg://solaris/compress/gzip': '1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z', - 'pkg://solaris/compress/p7zip': '16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z', - 'pkg://solaris/text/less': '458,5.11-0.175.3.0.0.30.0:20150821T172730Z', - 'pkg://solaris/system/library/security/libsasl': '0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z', + "pkg://solaris/compress/bzip2": "1.0.6,5.11-0.175.3.10.0.4.0:20160630T215500Z", + "pkg://solaris/compress/gzip": "1.5,5.11-0.175.3.0.0.30.0:20150821T161446Z", + "pkg://solaris/compress/p7zip": "16.2.3,5.11-0.175.3.34.0.2.0:20180614T204908Z", + "pkg://solaris/text/less": "458,5.11-0.175.3.0.0.30.0:20150821T172730Z", + "pkg://solaris/system/library/security/libsasl": "0.5.11,5.11-0.175.3.32.0.1.0:20180406T191209Z", } mock_install_cmd = MagicMock(side_effect=check_param) list_pkgs_responses = [pkg_list_pre, pkg_list_post] - with patch.object(solarisips, 'is_installed', return_value=False), \ - patch.object(solarisips, 'list_pkgs', side_effect=list_pkgs_responses): - with patch.dict(solarisips.__salt__, {'cmd.run_all': mock_install_cmd}): - result = solarisips.install(pkgs=['less', 'libsasl'], refresh=False) + with patch.object(solarisips, "is_installed", return_value=False), patch.object( + solarisips, "list_pkgs", side_effect=list_pkgs_responses + ): + with patch.dict(solarisips.__salt__, {"cmd.run_all": mock_install_cmd}): + result = solarisips.install(pkgs=["less", "libsasl"], refresh=False) diff --git a/tests/unit/modules/test_solr.py b/tests/unit/modules/test_solr.py index 003cdd97638..b4f64aa3136 100644 --- a/tests/unit/modules/test_solr.py +++ b/tests/unit/modules/test_solr.py @@ -1,439 +1,522 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +import os # Import Salt Libs import salt.modules.solr as solr +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SolrTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.solr - ''' + """ + def setup_loader_modules(self): return {solr: {}} def test_lucene_version(self): - ''' + """ Test to get the lucene version that solr is using. - ''' - with patch.object(solr, '_get_return_dict', return_value={'A': 'a'}): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, True, True]): - with patch.object(solr, '_check_for_cores', - side_effect=[True, False, False]): - tempdict = {'success': 'success', 'errors': 'errors', - 'data': {'lucene': {'lucene-spec-version': 1}}} - with patch.object(solr, '_get_admin_info', - side_effect=[tempdict, tempdict, - {'success': None}]): - with patch.dict(solr.__salt__, - {'config.option': - MagicMock(return_value=['A'])}): + """ + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object( + solr, "_get_none_or_value", side_effect=[None, True, True] + ): + with patch.object( + solr, "_check_for_cores", side_effect=[True, False, False] + ): + tempdict = { + "success": "success", + "errors": "errors", + "data": {"lucene": {"lucene-spec-version": 1}}, + } + with patch.object( + solr, + "_get_admin_info", + side_effect=[tempdict, tempdict, {"success": None}], + ): + with patch.dict( + solr.__salt__, + {"config.option": MagicMock(return_value=["A"])}, + ): - with patch.object(solr, '_update_return_dict', - return_value={'A': 'a'}): - self.assertDictEqual(solr.lucene_version('c'), - {'A': 'a'}) + with patch.object( + solr, "_update_return_dict", return_value={"A": "a"} + ): + self.assertDictEqual( + solr.lucene_version("c"), {"A": "a"} + ) - self.assertDictEqual(solr.lucene_version('c'), - {'A': 'a'}) + self.assertDictEqual(solr.lucene_version("c"), {"A": "a"}) - self.assertDictEqual(solr.lucene_version('c'), - {'success': None}) + self.assertDictEqual( + solr.lucene_version("c"), {"success": None} + ) def test_version(self): - ''' + """ Test to get the solr version for the core specified - ''' - with patch.object(solr, '_get_return_dict', return_value={'A': 'a'}): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, True, True]): - with patch.object(solr, '_check_for_cores', - side_effect=[True, False, False]): + """ + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object( + solr, "_get_none_or_value", side_effect=[None, True, True] + ): + with patch.object( + solr, "_check_for_cores", side_effect=[True, False, False] + ): - tempdict = {'success': 'success', 'errors': 'errors', - 'warnings': 'warnings', - 'data': {'lucene': {'solr-spec-version': 1}}} - with patch.object(solr, '_get_admin_info', - side_effect=[tempdict, tempdict]): - with patch.dict(solr.__opts__, - {'solr.cores': - MagicMock(return_value=['A'])}): - with patch.object(solr, '_update_return_dict', - return_value={'A': 'a'}): - self.assertDictEqual(solr.version(), - {'A': 'a'}) + tempdict = { + "success": "success", + "errors": "errors", + "warnings": "warnings", + "data": {"lucene": {"solr-spec-version": 1}}, + } + with patch.object( + solr, "_get_admin_info", side_effect=[tempdict, tempdict] + ): + with patch.dict( + solr.__opts__, {"solr.cores": MagicMock(return_value=["A"])} + ): + with patch.object( + solr, "_update_return_dict", return_value={"A": "a"} + ): + self.assertDictEqual(solr.version(), {"A": "a"}) - self.assertDictEqual(solr.version(), - {'A': 'a'}) + self.assertDictEqual(solr.version(), {"A": "a"}) - with patch.object(solr, '_get_admin_info', - return_value={'success': None}): - self.assertDictEqual(solr.version(), {'success': None}) + with patch.object( + solr, "_get_admin_info", return_value={"success": None} + ): + self.assertDictEqual(solr.version(), {"success": None}) def test_optimize(self): - ''' + """ Test to search queries fast, but it is a very expensive operation. - ''' - with patch.object(solr, '_get_return_dict', return_value={'A': 'a'}): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, True]): - with patch.object(solr, '_check_for_cores', - side_effect=[True, False]): - tempdict = {'success': 'success', 'errors': 'errors', - 'warnings': 'warnings', - 'data': {'lucene': {'solr-spec-version': 1}}} - with patch.object(solr, '_format_url', - return_value='A'): - with patch.dict(solr.__salt__, - {'config.option': - MagicMock(return_value=['A'])}): - with patch.object(solr, '_http_request', return_value=tempdict): - with patch.object(solr, '_update_return_dict', - return_value={'A': 'a'}): - self.assertDictEqual(solr.optimize(), - {'A': 'a'}) + """ + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object(solr, "_get_none_or_value", side_effect=[None, True]): + with patch.object(solr, "_check_for_cores", side_effect=[True, False]): + tempdict = { + "success": "success", + "errors": "errors", + "warnings": "warnings", + "data": {"lucene": {"solr-spec-version": 1}}, + } + with patch.object(solr, "_format_url", return_value="A"): + with patch.dict( + solr.__salt__, + {"config.option": MagicMock(return_value=["A"])}, + ): + with patch.object( + solr, "_http_request", return_value=tempdict + ): + with patch.object( + solr, "_update_return_dict", return_value={"A": "a"} + ): + self.assertDictEqual(solr.optimize(), {"A": "a"}) - with patch.object(solr, '_http_request', return_value='A'): - self.assertEqual(solr.optimize(), 'A') + with patch.object(solr, "_http_request", return_value="A"): + self.assertEqual(solr.optimize(), "A") def test_ping(self): - ''' + """ Test to check on solr, makes sure solr can talk to the indexes. - ''' - with patch.object(solr, '_get_return_dict', return_value={'A': 'a'}): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, True]): - with patch.object(solr, '_check_for_cores', - side_effect=[True, False]): + """ + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object(solr, "_get_none_or_value", side_effect=[None, True]): + with patch.object(solr, "_check_for_cores", side_effect=[True, False]): - tempdict = {'success': 'success', 'errors': 'errors', - 'warnings': 'warnings', - 'data': {'lucene': {'solr-spec-version': 1}}} + tempdict = { + "success": "success", + "errors": "errors", + "warnings": "warnings", + "data": {"lucene": {"solr-spec-version": 1}}, + } - with patch.dict(solr.__opts__, - {'solr.cores': - MagicMock(return_value=['A'])}): - with patch.object(solr, '_get_admin_info', - return_value=tempdict): - with patch.object(solr, '_update_return_dict', - return_value={'A': 'a'}): - self.assertDictEqual(solr.ping(), {'A': 'a'}) + with patch.dict( + solr.__opts__, {"solr.cores": MagicMock(return_value=["A"])} + ): + with patch.object( + solr, "_get_admin_info", return_value=tempdict + ): + with patch.object( + solr, "_update_return_dict", return_value={"A": "a"} + ): + self.assertDictEqual(solr.ping(), {"A": "a"}) - with patch.object(solr, '_get_admin_info', - return_value='A'): - self.assertEqual(solr.ping(), 'A') + with patch.object(solr, "_get_admin_info", return_value="A"): + self.assertEqual(solr.ping(), "A") def test_is_replication_enabled(self): - ''' + """ Test to check for errors, and determine if a slave is replicating or not. - ''' + """ error = 'Only "slave" minions can run "is_replication_enabled"' - with patch.object(solr, '_get_return_dict', return_value={'A': 'a'}): - with patch.object(solr, '_is_master', side_effect=[True, False]): + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object(solr, "_is_master", side_effect=[True, False]): self.assertIsNone(solr.is_replication_enabled()) - with patch.object(solr, '_get_none_or_value', return_value=None): - with patch.object(solr, '_check_for_cores', return_value=True): - with patch.dict(solr.__opts__, {'solr.cores': MagicMock(return_value='A')}): - with patch.object(solr, '_replication_request', return_value='A'): - self.assertDictEqual(solr.is_replication_enabled - (), {'A': 'a', - 'errors': [error], - 'success': False}) + with patch.object(solr, "_get_none_or_value", return_value=None): + with patch.object(solr, "_check_for_cores", return_value=True): + with patch.dict( + solr.__opts__, {"solr.cores": MagicMock(return_value="A")} + ): + with patch.object( + solr, "_replication_request", return_value="A" + ): + self.assertDictEqual( + solr.is_replication_enabled(), + {"A": "a", "errors": [error], "success": False}, + ) def test_match_index_versions(self): - ''' + """ Test to verifies that the master and the slave versions are in sync by comparing the index version. - ''' + """ err = 'solr.match_index_versions can only be called by "slave" minions' - with patch.object(solr, '_get_return_dict', return_value={'A': 'a'}): - with patch.object(solr, '_is_master', side_effect=[True, False]): + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object(solr, "_is_master", side_effect=[True, False]): self.assertIsNone(solr.match_index_versions()) - with patch.object(solr, '_get_none_or_value', - return_value=None): - with patch.object(solr, '_check_for_cores', - return_value=True): - with patch.dict(solr.__opts__, - {'solr.cores': - MagicMock(return_value='A')}): - with patch.object(solr, '_replication_request', - return_value='A'): - self.assertDictEqual(solr.match_index_versions - (), {'A': 'a', - 'errors': [err], - 'success': False}) + with patch.object(solr, "_get_none_or_value", return_value=None): + with patch.object(solr, "_check_for_cores", return_value=True): + with patch.dict( + solr.__opts__, {"solr.cores": MagicMock(return_value="A")} + ): + with patch.object( + solr, "_replication_request", return_value="A" + ): + self.assertDictEqual( + solr.match_index_versions(), + {"A": "a", "errors": [err], "success": False}, + ) def test_replication_details(self): - ''' + """ Test to get the full replication details. - ''' - tempdict1 = {'success': 'success', 'errors': 'errors', - 'warnings': 'warnings', 'data': 'data'} + """ + tempdict1 = { + "success": "success", + "errors": "errors", + "warnings": "warnings", + "data": "data", + } - tempdict2 = {'success': None, 'errors': 'errors', - 'warnings': 'warnings', 'data': 'data'} + tempdict2 = { + "success": None, + "errors": "errors", + "warnings": "warnings", + "data": "data", + } - with patch.object(solr, '_get_return_dict', return_value={'A': 'a'}): - with patch.object(solr, '_get_none_or_value', return_value=True): - with patch.object(solr, '_replication_request', - side_effect=[tempdict2, - tempdict1]): + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object(solr, "_get_none_or_value", return_value=True): + with patch.object( + solr, "_replication_request", side_effect=[tempdict2, tempdict1] + ): self.assertDictEqual(solr.replication_details(), tempdict2) - with patch.object(solr, '_update_return_dict', - return_value=tempdict1): - self.assertDictEqual(solr.replication_details(), - tempdict1) + with patch.object( + solr, "_update_return_dict", return_value=tempdict1 + ): + self.assertDictEqual(solr.replication_details(), tempdict1) def test_backup(self): - ''' + """ Test to tell solr make a backup. - ''' - tempdict = {'success': 'success', 'errors': 'errors', - 'warnings': 'warnings', 'data': 'data'} + """ + tempdict = { + "success": "success", + "errors": "errors", + "warnings": "warnings", + "data": "data", + } - with patch.object(solr, '_get_return_dict', return_value={'A': 'a'}): - with patch.dict(solr.__opts__, {'solr.backup_path': MagicMock(return_value='A'), - 'solr.num_backups': MagicMock(return_value='B'), - 'solr.cores': MagicMock(return_value=['A'])}): - with patch.object(os.path, 'sep', return_value='B'): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, True]): - with patch.object(solr, '_check_for_cores', - side_effect=[True, False]): - with patch.object(solr, '_replication_request', - return_value=tempdict): - with patch.dict(solr.__opts__, {'solr.cores': MagicMock(return_value=['A'])}): - with patch.object(solr, '_update_return_dict', return_value='A'): - self.assertDictEqual(solr.backup(), {'A': 'a'}) + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.dict( + solr.__opts__, + { + "solr.backup_path": MagicMock(return_value="A"), + "solr.num_backups": MagicMock(return_value="B"), + "solr.cores": MagicMock(return_value=["A"]), + }, + ): + with patch.object(os.path, "sep", return_value="B"): + with patch.object( + solr, "_get_none_or_value", side_effect=[None, True] + ): + with patch.object( + solr, "_check_for_cores", side_effect=[True, False] + ): + with patch.object( + solr, "_replication_request", return_value=tempdict + ): + with patch.dict( + solr.__opts__, + {"solr.cores": MagicMock(return_value=["A"])}, + ): + with patch.object( + solr, "_update_return_dict", return_value="A" + ): + self.assertDictEqual(solr.backup(), {"A": "a"}) self.assertDictEqual(solr.backup(), tempdict) def test_set_is_polling(self): - ''' + """ Test to prevent the slaves from polling the master for updates. - ''' - tempdict = {'success': 'success', 'errors': 'errors', - 'warnings': 'warnings', 'data': 'data'} + """ + tempdict = { + "success": "success", + "errors": "errors", + "warnings": "warnings", + "data": "data", + } err = 'solr.set_is_polling can only be called by "slave" minions' - with patch.object(solr, '_get_return_dict', return_value={'A': 'a'}): - with patch.object(solr, '_is_master', side_effect=[True, False, - False]): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, None, True]): - with patch.object(solr, '_check_for_cores', - side_effect=[True, False]): + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object(solr, "_is_master", side_effect=[True, False, False]): + with patch.object( + solr, "_get_none_or_value", side_effect=[None, None, True] + ): + with patch.object( + solr, "_check_for_cores", side_effect=[True, False] + ): - self.assertIsNone(solr.set_is_polling('p')) + self.assertIsNone(solr.set_is_polling("p")) - with patch.dict(solr.__opts__, - {'solr.cores': - MagicMock(return_value='A')}): - with patch.object(solr, '_update_return_dict', - return_value=tempdict): - self.assertDictEqual(solr.set_is_polling('p'), - {'A': 'a', 'errors': [err], - 'success': False}) + with patch.dict( + solr.__opts__, {"solr.cores": MagicMock(return_value="A")} + ): + with patch.object( + solr, "_update_return_dict", return_value=tempdict + ): + self.assertDictEqual( + solr.set_is_polling("p"), + {"A": "a", "errors": [err], "success": False}, + ) - with patch.object(solr, '_replication_request', - return_value='A'): - self.assertEqual(solr.set_is_polling('p'), 'A') + with patch.object( + solr, "_replication_request", return_value="A" + ): + self.assertEqual(solr.set_is_polling("p"), "A") def test_set_replication_enabled(self): - ''' + """ Test to sets the master to ignore poll requests from the slaves. - ''' - with patch.object(solr, '_is_master', - side_effect=[False, True, True, True]): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, None, True, True, True]): - with patch.object(solr, '_get_return_dict', - side_effect=[{'A': 'a'}, {}]): - with patch.object(solr, '_replication_request', - return_value='A'): - self.assertDictEqual(solr.set_replication_enabled('s'), {'A': 'a'}) - with patch.object(solr, '_check_for_cores', - return_value=True): - with patch.dict(solr.__opts__, {'solr.cores': MagicMock(return_value='n')}): - self.assertEqual(solr.set_replication_enabled('s'), {}) - self.assertEqual(solr.set_replication_enabled('s'), 'A') - self.assertEqual(solr.set_replication_enabled(False), 'A') + """ + with patch.object(solr, "_is_master", side_effect=[False, True, True, True]): + with patch.object( + solr, "_get_none_or_value", side_effect=[None, None, True, True, True] + ): + with patch.object( + solr, "_get_return_dict", side_effect=[{"A": "a"}, {}] + ): + with patch.object(solr, "_replication_request", return_value="A"): + self.assertDictEqual( + solr.set_replication_enabled("s"), {"A": "a"} + ) + with patch.object(solr, "_check_for_cores", return_value=True): + with patch.dict( + solr.__opts__, + {"solr.cores": MagicMock(return_value="n")}, + ): + self.assertEqual(solr.set_replication_enabled("s"), {}) + self.assertEqual(solr.set_replication_enabled("s"), "A") + self.assertEqual(solr.set_replication_enabled(False), "A") def test_signal(self): - ''' + """ Test to signals Apache Solr to start, stop, or restart. - ''' - self.assertEqual(solr.signal('signal'), - ('signal is an invalid signal. Try: one of: start,' - ' stop, or restart')) + """ + self.assertEqual( + solr.signal("signal"), + ("signal is an invalid signal. Try: one of: start," " stop, or restart"), + ) def test_reload_core(self): - ''' + """ Test to load a new core from the same configuration as an existing registered core. - ''' + """ error = ['solr.reload_core can only be called by "multi-core" minions'] - with patch.object(solr, '_check_for_cores', - side_effect=[False, True, True, True]): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, True]): - with patch.object(solr, '_get_return_dict', - return_value={'A': 'a'}): - with patch.object(solr, '_format_url', - return_value='A'): - with patch.object(solr, '_http_request', - return_value='A'): - with patch.dict(solr.__opts__, {'solr.cores': MagicMock(return_value='n')}): + with patch.object( + solr, "_check_for_cores", side_effect=[False, True, True, True] + ): + with patch.object(solr, "_get_none_or_value", side_effect=[None, True]): + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object(solr, "_format_url", return_value="A"): + with patch.object(solr, "_http_request", return_value="A"): + with patch.dict( + solr.__opts__, + {"solr.cores": MagicMock(return_value="n")}, + ): self.assertIsNone(solr.reload_core()) - self.assertDictEqual(solr.reload_core(), - {'A': 'a', 'errors': error, - 'success': False}) - self.assertEqual(solr.reload_core(), 'A') + self.assertDictEqual( + solr.reload_core(), + {"A": "a", "errors": error, "success": False}, + ) + self.assertEqual(solr.reload_core(), "A") def test_core_status(self): - ''' + """ Test to get the status for a given core or all cores if no core is specified - ''' + """ error = ['solr.reload_core can only be called by "multi-core" minions'] - with patch.object(solr, '_check_for_cores', - side_effect=[False, True, True, True]): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, True]): - with patch.object(solr, '_get_return_dict', - return_value={'A': 'a'}): - with patch.object(solr, '_format_url', - return_value='A'): - with patch.object(solr, '_http_request', - return_value='A'): - with patch.dict(solr.__opts__, - {'solr.cores': MagicMock(return_value='n')}): + with patch.object( + solr, "_check_for_cores", side_effect=[False, True, True, True] + ): + with patch.object(solr, "_get_none_or_value", side_effect=[None, True]): + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object(solr, "_format_url", return_value="A"): + with patch.object(solr, "_http_request", return_value="A"): + with patch.dict( + solr.__opts__, + {"solr.cores": MagicMock(return_value="n")}, + ): self.assertIsNone(solr.core_status()) - self.assertDictEqual(solr.core_status(), - {'A': 'a', 'errors': error, - 'success': False}) + self.assertDictEqual( + solr.core_status(), + {"A": "a", "errors": error, "success": False}, + ) - self.assertEqual(solr.core_status(), 'A') + self.assertEqual(solr.core_status(), "A") def test_reload_import_config(self): - ''' + """ Test to re-loads the handler config XML file. - ''' - with patch.object(solr, '_is_master', side_effect=[False, True, True]): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, None, None, True, True]): - with patch.object(solr, '_get_return_dict', - return_value={'A': 'a'}): - with patch.object(solr, '_check_for_cores', - side_effect=[True, False]): - with patch.object(solr, '_format_url', - return_value='A'): - with patch.object(solr, '_http_request', - return_value='A'): - self.assertDictEqual(solr.reload_import_config('h'), {'A': 'a'}) - self.assertDictEqual(solr.reload_import_config('h'), {'A': 'a'}) - self.assertEqual(solr.reload_import_config('h'), 'A') + """ + with patch.object(solr, "_is_master", side_effect=[False, True, True]): + with patch.object( + solr, "_get_none_or_value", side_effect=[None, None, None, True, True] + ): + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object( + solr, "_check_for_cores", side_effect=[True, False] + ): + with patch.object(solr, "_format_url", return_value="A"): + with patch.object(solr, "_http_request", return_value="A"): + self.assertDictEqual( + solr.reload_import_config("h"), {"A": "a"} + ) + self.assertDictEqual( + solr.reload_import_config("h"), {"A": "a"} + ) + self.assertEqual(solr.reload_import_config("h"), "A") def test_abort_import(self): - ''' + """ Test to aborts an existing import command to the specified handler. - ''' - with patch.object(solr, '_is_master', side_effect=[False, True, True]): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, None, None, True, True]): - with patch.object(solr, '_get_return_dict', - return_value={'A': 'a'}): - with patch.object(solr, '_check_for_cores', - side_effect=[True, False]): - with patch.object(solr, '_format_url', - return_value='A'): - with patch.object(solr, '_http_request', - return_value='A'): - self.assertDictEqual(solr.abort_import('h'), {'A': 'a'}) - self.assertDictEqual(solr.abort_import('h'), {'A': 'a'}) - self.assertEqual(solr.abort_import('h'), 'A') + """ + with patch.object(solr, "_is_master", side_effect=[False, True, True]): + with patch.object( + solr, "_get_none_or_value", side_effect=[None, None, None, True, True] + ): + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object( + solr, "_check_for_cores", side_effect=[True, False] + ): + with patch.object(solr, "_format_url", return_value="A"): + with patch.object(solr, "_http_request", return_value="A"): + self.assertDictEqual(solr.abort_import("h"), {"A": "a"}) + self.assertDictEqual(solr.abort_import("h"), {"A": "a"}) + self.assertEqual(solr.abort_import("h"), "A") def test_full_import(self): - ''' + """ Test to submits an import command to the specified handler using specified options. - ''' - with patch('salt.modules.solr._format_url', MagicMock(return_value='A')), \ - patch.object(solr, '_is_master', side_effect=[False, True, True, True, True, True]), \ - patch.object(solr, '_get_return_dict', return_value={'A': 'a'}), \ - patch.object(solr, '_get_none_or_value', side_effect=[None, True, True, True, True]), \ - patch.object(solr, '_check_for_cores', side_effect=[True, False, False, False, False]), \ - patch.object(solr, '_pre_index_check', side_effect=[{'success': False}, - {'success': True}, - {'success': True}]), \ - patch.object(solr, '_merge_options', side_effect=[{'clean': True}, - {'clean': False}]), \ - patch.object(solr, 'set_replication_enabled', return_value={'success': False}), \ - patch.object(solr, '_http_request', return_value='A'): + """ + with patch( + "salt.modules.solr._format_url", MagicMock(return_value="A") + ), patch.object( + solr, "_is_master", side_effect=[False, True, True, True, True, True] + ), patch.object( + solr, "_get_return_dict", return_value={"A": "a"} + ), patch.object( + solr, "_get_none_or_value", side_effect=[None, True, True, True, True] + ), patch.object( + solr, "_check_for_cores", side_effect=[True, False, False, False, False] + ), patch.object( + solr, + "_pre_index_check", + side_effect=[{"success": False}, {"success": True}, {"success": True}], + ), patch.object( + solr, "_merge_options", side_effect=[{"clean": True}, {"clean": False}] + ), patch.object( + solr, "set_replication_enabled", return_value={"success": False} + ), patch.object( + solr, "_http_request", return_value="A" + ): - self.assertDictEqual(solr.full_import('h'), {'A': 'a'}) - self.assertDictEqual(solr.full_import('h'), {'A': 'a'}) - self.assertDictEqual(solr.full_import('h'), {'success': False}) - self.assertDictEqual(solr.full_import('h'), {'A': 'a'}) - self.assertEqual(solr.full_import('h'), 'A') + self.assertDictEqual(solr.full_import("h"), {"A": "a"}) + self.assertDictEqual(solr.full_import("h"), {"A": "a"}) + self.assertDictEqual(solr.full_import("h"), {"success": False}) + self.assertDictEqual(solr.full_import("h"), {"A": "a"}) + self.assertEqual(solr.full_import("h"), "A") def test_delta_import(self): - ''' + """ Test to submits an import command to the specified handler using specified options. - ''' - with patch('salt.modules.solr._format_url', MagicMock(return_value='A')), \ - patch.object(solr, '_is_master', side_effect=[False, True, True, True, True]), \ - patch.object(solr, '_get_none_or_value', side_effect=[None, True, True, True, True]), \ - patch.object(solr, '_get_return_dict', return_value={'A': 'a'}), \ - patch.object(solr, '_pre_index_check', side_effect=[{'success': False}, - {'success': True}, - {'success': True}, - {'success': True}]), \ - patch.object(solr, '_merge_options', side_effect=[{'clean': True}, - {'clean': False}]), \ - patch.object(solr, '_check_for_cores', side_effect=[True, False]), \ - patch.object(solr, 'set_replication_enabled', return_value={'success': False}), \ - patch.object(solr, '_http_request', return_value='A'): + """ + with patch( + "salt.modules.solr._format_url", MagicMock(return_value="A") + ), patch.object( + solr, "_is_master", side_effect=[False, True, True, True, True] + ), patch.object( + solr, "_get_none_or_value", side_effect=[None, True, True, True, True] + ), patch.object( + solr, "_get_return_dict", return_value={"A": "a"} + ), patch.object( + solr, + "_pre_index_check", + side_effect=[ + {"success": False}, + {"success": True}, + {"success": True}, + {"success": True}, + ], + ), patch.object( + solr, "_merge_options", side_effect=[{"clean": True}, {"clean": False}] + ), patch.object( + solr, "_check_for_cores", side_effect=[True, False] + ), patch.object( + solr, "set_replication_enabled", return_value={"success": False} + ), patch.object( + solr, "_http_request", return_value="A" + ): - self.assertDictEqual(solr.delta_import('h'), {'A': 'a'}) - self.assertDictEqual(solr.delta_import('h'), {'success': False}) - self.assertDictEqual(solr.delta_import('h'), {'A': 'a'}) - self.assertEqual(solr.delta_import('h'), 'A') + self.assertDictEqual(solr.delta_import("h"), {"A": "a"}) + self.assertDictEqual(solr.delta_import("h"), {"success": False}) + self.assertDictEqual(solr.delta_import("h"), {"A": "a"}) + self.assertEqual(solr.delta_import("h"), "A") def test_import_status(self): - ''' + """ Test to submits an import command to the specified handler using specified options. - ''' - with patch.object(solr, '_is_master', side_effect=[False, True]): - with patch.object(solr, '_get_none_or_value', - side_effect=[None, True]): - with patch.object(solr, '_get_return_dict', - return_value={'A': 'a'}): - with patch.object(solr, '_format_url', - return_value='A'): - with patch.object(solr, '_http_request', - return_value='A'): - self.assertDictEqual(solr.import_status('h'), {'A': 'a'}) - self.assertEqual(solr.import_status('h'), 'A') + """ + with patch.object(solr, "_is_master", side_effect=[False, True]): + with patch.object(solr, "_get_none_or_value", side_effect=[None, True]): + with patch.object(solr, "_get_return_dict", return_value={"A": "a"}): + with patch.object(solr, "_format_url", return_value="A"): + with patch.object(solr, "_http_request", return_value="A"): + self.assertDictEqual(solr.import_status("h"), {"A": "a"}) + self.assertEqual(solr.import_status("h"), "A") diff --git a/tests/unit/modules/test_sqlite3.py b/tests/unit/modules/test_sqlite3.py index c9743e7d539..5d84ba8fc01 100644 --- a/tests/unit/modules/test_sqlite3.py +++ b/tests/unit/modules/test_sqlite3.py @@ -1,133 +1,135 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.modules.sqlite3 as sqlite3 # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -# Import Salt Libs -import salt.modules.sqlite3 as sqlite3 - class MockSqlite3(object): - ''' + """ Mock sqlite3 class - ''' - version = '2.6.0' - sqlite_version = '3.8.2' + """ + + version = "2.6.0" + sqlite_version = "3.8.2" def __init__(self): self.dbase = None self.isolation_level = None def connect(self, dbase, isolation_level=None): - ''' + """ Mock connect method - ''' + """ self.dbase = dbase self.isolation_level = isolation_level return MockSqlite3() @staticmethod def cursor(): - ''' + """ Mock connect method - ''' + """ return MockSqlite3() @staticmethod def execute(sql): - ''' + """ Mock connect method - ''' + """ return sql @staticmethod def fetchall(): - ''' + """ Mock connect method - ''' + """ return True class Sqlite3TestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.sqlite3 - ''' + """ + def setup_loader_modules(self): - return {sqlite3: {'sqlite3': MockSqlite3()}} + return {sqlite3: {"sqlite3": MockSqlite3()}} # 'version' function tests: 1 def test_version(self): - ''' + """ Tests if it return version of pysqlite. - ''' - self.assertEqual(sqlite3.version(), '2.6.0') + """ + self.assertEqual(sqlite3.version(), "2.6.0") # 'sqlite_version' function tests: 1 def test_sqlite_version(self): - ''' + """ Tests if it return version of sqlite. - ''' - self.assertEqual(sqlite3.sqlite_version(), '3.8.2') + """ + self.assertEqual(sqlite3.sqlite_version(), "3.8.2") # 'modify' function tests: 1 def test_modify(self): - ''' + """ Tests if it issue an SQL query to sqlite3 (with no return data). - ''' + """ self.assertFalse(sqlite3.modify()) - self.assertTrue(sqlite3.modify - ('/root/test.db', - 'CREATE TABLE test(id INT, testdata TEXT);')) + self.assertTrue( + sqlite3.modify("/root/test.db", "CREATE TABLE test(id INT, testdata TEXT);") + ) # 'fetch' function tests: 1 def test_fetch(self): - ''' + """ Tests if it retrieve data from an sqlite3 db (returns all rows, be careful!) - ''' + """ self.assertFalse(sqlite3.fetch()) - self.assertTrue(sqlite3.fetch - ('/root/test.db', - 'CREATE TABLE test(id INT, testdata TEXT);')) + self.assertTrue( + sqlite3.fetch("/root/test.db", "CREATE TABLE test(id INT, testdata TEXT);") + ) # 'tables' function tests: 1 def test_tables(self): - ''' + """ Tests if it show all tables in the database. - ''' + """ self.assertFalse(sqlite3.tables()) - self.assertTrue(sqlite3.tables('/root/test.db')) + self.assertTrue(sqlite3.tables("/root/test.db")) # 'indices' function tests: 1 def test_indices(self): - ''' + """ Tests if it show all indices in the database. - ''' + """ self.assertFalse(sqlite3.indices()) - self.assertTrue(sqlite3.indices('/root/test.db')) + self.assertTrue(sqlite3.indices("/root/test.db")) # 'indexes' function tests: 1 def test_indexes(self): - ''' + """ Tests if it show all indices in the database, for people with poor spelling skills - ''' - self.assertTrue(sqlite3.indexes('/root/test.db')) + """ + self.assertTrue(sqlite3.indexes("/root/test.db")) diff --git a/tests/unit/modules/test_ssh.py b/tests/unit/modules/test_ssh.py index 1bbe7f6d608..42c07d5d5f0 100644 --- a/tests/unit/modules/test_ssh.py +++ b/tests/unit/modules/test_ssh.py @@ -1,109 +1,116 @@ # -*- coding: utf-8 -*- # import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import tempfile -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import salt.modules.ssh as ssh # Import Salt Libs import salt.utils.files import salt.utils.platform -import salt.modules.ssh as ssh from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SSHAuthKeyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.ssh - ''' + """ + def setup_loader_modules(self): return { ssh: { - '__salt__': { - 'user.info': lambda u: getattr(self, 'user_info_mock', None), + "__salt__": { + "user.info": lambda u: getattr(self, "user_info_mock", None), } } } def tearDown(self): try: - delattr(self, 'user_info_mock') + delattr(self, "user_info_mock") except AttributeError: pass def test_expand_user_token(self): - ''' + """ Test if the %u, %h, and %% tokens are correctly expanded - ''' - output = ssh._expand_authorized_keys_path('/home/%u', 'user', - '/home/user') - self.assertEqual(output, '/home/user') + """ + output = ssh._expand_authorized_keys_path("/home/%u", "user", "/home/user") + self.assertEqual(output, "/home/user") - output = ssh._expand_authorized_keys_path('/home/%h', 'user', - '/home/user') - self.assertEqual(output, '/home//home/user') + output = ssh._expand_authorized_keys_path("/home/%h", "user", "/home/user") + self.assertEqual(output, "/home//home/user") - output = ssh._expand_authorized_keys_path('%h/foo', 'user', - '/home/user') - self.assertEqual(output, '/home/user/foo') + output = ssh._expand_authorized_keys_path("%h/foo", "user", "/home/user") + self.assertEqual(output, "/home/user/foo") - output = ssh._expand_authorized_keys_path('/srv/%h/aaa/%u%%', 'user', - '/home/user') - self.assertEqual(output, '/srv//home/user/aaa/user%') + output = ssh._expand_authorized_keys_path( + "/srv/%h/aaa/%u%%", "user", "/home/user" + ) + self.assertEqual(output, "/srv//home/user/aaa/user%") - user = 'dude' - home = '/home/dude' - path = '/home/dude%' - self.assertRaises(CommandExecutionError, ssh._expand_authorized_keys_path, path, user, home) + user = "dude" + home = "/home/dude" + path = "/home/dude%" + self.assertRaises( + CommandExecutionError, ssh._expand_authorized_keys_path, path, user, home + ) - path = '/home/%dude' - self.assertRaises(CommandExecutionError, ssh._expand_authorized_keys_path, path, user, home) + path = "/home/%dude" + self.assertRaises( + CommandExecutionError, ssh._expand_authorized_keys_path, path, user, home + ) def test_set_auth_key_invalid(self): - self.user_info_mock = {'home': '/dev/null'} + self.user_info_mock = {"home": "/dev/null"} # Inserting invalid public key should be rejected - invalid_key = 'AAAAB3NzaC1kc3MAAACBAL0sQ9fJ5bYTEyY' # missing padding - self.assertEqual(ssh.set_auth_key('user', invalid_key), 'Invalid public key') + invalid_key = "AAAAB3NzaC1kc3MAAACBAL0sQ9fJ5bYTEyY" # missing padding + self.assertEqual(ssh.set_auth_key("user", invalid_key), "Invalid public key") def test_replace_auth_key(self): - ''' + """ Test the _replace_auth_key with some different authorized_keys examples - ''' + """ # First test a known working example, gathered from the authorized_keys file # in the integration test files. - enc = 'ssh-rsa' - key = 'AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+' \ - 'PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNl' \ - 'GEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWp' \ - 'XLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal' \ - '72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi' \ - '/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' + enc = "ssh-rsa" + key = ( + "AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+" + "PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNl" + "GEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWp" + "XLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal" + "72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi" + "/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==" + ) options = 'command="/usr/local/lib/ssh-helper"' - email = 'github.com' - empty_line = '\n' - comment_line = '# this is a comment\n' + email = "github.com" + empty_line = "\n" + comment_line = "# this is a comment\n" # Write out the authorized key to a temporary file - temp_file = tempfile.NamedTemporaryFile(delete=False, mode='w+') + temp_file = tempfile.NamedTemporaryFile(delete=False, mode="w+") temp_file.close() - with salt.utils.files.fopen(temp_file.name, 'w') as _fh: + with salt.utils.files.fopen(temp_file.name, "w") as _fh: # Add comment _fh.write(comment_line) # Add empty line for #41335 _fh.write(empty_line) - _fh.write('{0} {1} {2} {3}'.format(options, enc, key, email)) + _fh.write("{0} {1} {2} {3}".format(options, enc, key, email)) - with patch.dict(ssh.__salt__, {'user.info': MagicMock(return_value={})}): - with patch('salt.modules.ssh._get_config_file', MagicMock(return_value=temp_file.name)): - ssh._replace_auth_key('foo', key, config=temp_file.name) + with patch.dict(ssh.__salt__, {"user.info": MagicMock(return_value={})}): + with patch( + "salt.modules.ssh._get_config_file", + MagicMock(return_value=temp_file.name), + ): + ssh._replace_auth_key("foo", key, config=temp_file.name) # The previous authorized key should have been replaced by the simpler one with salt.utils.files.fopen(temp_file.name) as _fh: @@ -114,28 +121,42 @@ class SSHAuthKeyTestCase(TestCase, LoaderModuleMockMixin): self.assertNotIn(email, file_txt) # Now test a very simple key using ecdsa instead of ssh-rsa and with multiple options - enc = 'ecdsa-sha2-nistp256' - key = 'abcxyz' + enc = "ecdsa-sha2-nistp256" + key = "abcxyz" - with salt.utils.files.fopen(temp_file.name, 'a') as _fh: - _fh.write(salt.utils.stringutils.to_str('{0} {1}'.format(enc, key))) + with salt.utils.files.fopen(temp_file.name, "a") as _fh: + _fh.write(salt.utils.stringutils.to_str("{0} {1}".format(enc, key))) # Replace the simple key from before with the more complicated options + new email # Option example is taken from Pull Request #39855 - options = ['no-port-forwarding', 'no-agent-forwarding', 'no-X11-forwarding', - 'command="echo \'Please login as the user \"ubuntu\" rather than the user \"root\".\''] - email = 'foo@example.com' + options = [ + "no-port-forwarding", + "no-agent-forwarding", + "no-X11-forwarding", + 'command="echo \'Please login as the user "ubuntu" rather than the user "root".\'', + ] + email = "foo@example.com" - with patch.dict(ssh.__salt__, {'user.info': MagicMock(return_value={})}): - with patch('salt.modules.ssh._get_config_file', MagicMock(return_value=temp_file.name)): - ssh._replace_auth_key('foo', key, enc=enc, comment=email, options=options, config=temp_file.name) + with patch.dict(ssh.__salt__, {"user.info": MagicMock(return_value={})}): + with patch( + "salt.modules.ssh._get_config_file", + MagicMock(return_value=temp_file.name), + ): + ssh._replace_auth_key( + "foo", + key, + enc=enc, + comment=email, + options=options, + config=temp_file.name, + ) # Assert that the new line was added as-is to the file with salt.utils.files.fopen(temp_file.name) as _fh: file_txt = salt.utils.stringutils.to_unicode(_fh.read()) self.assertIn(enc, file_txt) self.assertIn(key, file_txt) - self.assertIn('{0} '.format(','.join(options)), file_txt) + self.assertIn("{0} ".format(",".join(options)), file_txt) self.assertIn(email, file_txt) self.assertIn(empty_line, file_txt) self.assertIn(comment_line, file_txt) diff --git a/tests/unit/modules/test_state.py b/tests/unit/modules/test_state.py index e3c3dc8fc62..bc72b23bfdb 100644 --- a/tests/unit/modules/test_state.py +++ b/tests/unit/modules/test_state.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import os import shutil @@ -12,58 +13,53 @@ import tempfile import textwrap import time -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch, - mock_open, -) - # Import Salt Libs import salt.config import salt.loader +import salt.modules.config as config +import salt.modules.state as state import salt.state import salt.utils.args import salt.utils.files -import salt.utils.json import salt.utils.hashutils +import salt.utils.json import salt.utils.odict import salt.utils.platform import salt.utils.state -import salt.modules.state as state from salt.exceptions import CommandExecutionError, SaltInvocationError -import salt.modules.config as config from salt.ext import six +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, mock_open, patch + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class MockState(object): - ''' + """ Mock class - ''' + """ + def __init__(self): pass class State(object): - ''' + """ Mock state class - ''' + """ + flag = None - def __init__(self, - opts, - pillar_override=False, - pillar_enc=None, - initial_pillar=None): + def __init__( + self, opts, pillar_override=False, pillar_enc=None, initial_pillar=None + ): pass def verify_data(self, data): - ''' + """ Mock verify_data method - ''' + """ if self.flag: return True else: @@ -71,36 +67,36 @@ class MockState(object): @staticmethod def call(data): - ''' + """ Mock call method - ''' + """ return list @staticmethod def call_high(data, orchestration_jid=None): - ''' + """ Mock call_high method - ''' + """ return True @staticmethod def call_template_str(data): - ''' + """ Mock call_template_str method - ''' + """ return True @staticmethod def _mod_init(data): - ''' + """ Mock _mod_init method - ''' + """ return True def verify_high(self, data): - ''' + """ Mock verify_high method - ''' + """ if self.flag: return True else: @@ -108,52 +104,51 @@ class MockState(object): @staticmethod def compile_high_data(data): - ''' + """ Mock compile_high_data - ''' + """ return [{"__id__": "ABC"}] @staticmethod def call_chunk(data, data1, data2): - ''' + """ Mock call_chunk method - ''' - return {'': 'ABC'} + """ + return {"": "ABC"} @staticmethod def call_chunks(data): - ''' + """ Mock call_chunks method - ''' + """ return True @staticmethod def call_listen(data, ret): - ''' + """ Mock call_listen method - ''' + """ return True def requisite_in(self, data): # pylint: disable=unused-argument return data, [] class HighState(object): - ''' + """ Mock HighState class - ''' + """ + flag = False - opts = {'state_top': '', - 'pillar': {}} + opts = {"state_top": "", "pillar": {}} def __init__(self, opts, pillar_override=None, *args, **kwargs): self.building_highstate = salt.utils.odict.OrderedDict - self.state = MockState.State(opts, - pillar_override=pillar_override) + self.state = MockState.State(opts, pillar_override=pillar_override) def render_state(self, sls, saltenv, mods, matches, local=False): - ''' + """ Mock render_state method - ''' + """ if self.flag: return {}, True else: @@ -161,15 +156,15 @@ class MockState(object): @staticmethod def get_top(): - ''' + """ Mock get_top method - ''' + """ return "_top" def verify_tops(self, data): - ''' + """ Mock verify_tops method - ''' + """ if self.flag: return ["a", "b"] else: @@ -177,97 +172,106 @@ class MockState(object): @staticmethod def top_matches(data): - ''' + """ Mock top_matches method - ''' + """ return ["a", "b", "c"] @staticmethod def push_active(): - ''' + """ Mock push_active method - ''' + """ return True @staticmethod def compile_highstate(): - ''' + """ Mock compile_highstate method - ''' + """ return "A" @staticmethod def compile_state_usage(): - ''' + """ Mock compile_state_usage method - ''' + """ return "A" @staticmethod def pop_active(): - ''' + """ Mock pop_active method - ''' + """ return True @staticmethod def compile_low_chunks(): - ''' + """ Mock compile_low_chunks method - ''' + """ return [{"__id__": "ABC", "__sls__": "abc"}] def render_highstate(self, data): - ''' + """ Mock render_highstate method - ''' + """ if self.flag: return ["a", "b"], True else: return ["a", "b"], False @staticmethod - def call_highstate(exclude, cache, cache_name, force=None, - whitelist=None, orchestration_jid=None): - ''' + def call_highstate( + exclude, + cache, + cache_name, + force=None, + whitelist=None, + orchestration_jid=None, + ): + """ Mock call_highstate method - ''' + """ return True class MockSerial(object): - ''' + """ Mock Class - ''' + """ + def __init__(self): pass class Serial(object): - ''' + """ Mock Serial class - ''' + """ + def __init__(self, data): pass @staticmethod def load(data): - ''' + """ Mock load method - ''' + """ return {"A": "B"} @staticmethod def dump(data, data1): - ''' + """ Mock dump method - ''' + """ return True class MockTarFile(object): - ''' + """ Mock tarfile class - ''' + """ + path = os.sep + "tmp" def __init__(self): @@ -275,852 +279,868 @@ class MockTarFile(object): @staticmethod def open(data, data1): - ''' + """ Mock open method - ''' + """ return MockTarFile @staticmethod def getmembers(): - ''' + """ Mock getmembers method - ''' + """ return [MockTarFile] @staticmethod def extractall(data): - ''' + """ Mock extractall method - ''' + """ return True @staticmethod def close(): - ''' + """ Mock close method - ''' + """ return True class StateTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.modules.state - ''' + """ def setup_loader_modules(self): utils = salt.loader.utils( salt.config.DEFAULT_MINION_OPTS.copy(), - whitelist=['state', 'args', 'systemd', 'path', 'platform'] + whitelist=["state", "args", "systemd", "path", "platform"], ) utils.keys() - patcher = patch('salt.modules.state.salt.state', MockState()) + patcher = patch("salt.modules.state.salt.state", MockState()) patcher.start() self.addCleanup(patcher.stop) return { state: { - '__opts__': { - 'cachedir': '/D', - 'saltenv': None, - '__cli': 'salt', + "__opts__": {"cachedir": "/D", "saltenv": None, "__cli": "salt"}, + "__utils__": utils, + "__salt__": { + "config.get": config.get, + "config.option": MagicMock(return_value=""), }, - '__utils__': utils, - '__salt__': { - 'config.get': config.get, - 'config.option': MagicMock(return_value=''), - } }, - config: { - '__opts__': {}, - '__pillar__': {}, - }, - + config: {"__opts__": {}, "__pillar__": {}}, } def test_running(self): - ''' + """ Test of checking i fthe state function is already running - ''' + """ self.assertEqual(state.running(True), []) - mock = MagicMock(side_effect=[[{"fun": "state.running", "pid": "4126", - "jid": "20150325123407204096"}], []]) - with patch.dict(state.__salt__, - {'saltutil.is_running': mock} - ): - self.assertListEqual(state.running(), - ['The function "state.running"' - ' is running as PID 4126 and ' - 'was started at 2015, Mar 25 12:34:07.' - '204096 with jid 20150325123407204096']) + mock = MagicMock( + side_effect=[ + [ + { + "fun": "state.running", + "pid": "4126", + "jid": "20150325123407204096", + } + ], + [], + ] + ) + with patch.dict(state.__salt__, {"saltutil.is_running": mock}): + self.assertListEqual( + state.running(), + [ + 'The function "state.running"' + " is running as PID 4126 and " + "was started at 2015, Mar 25 12:34:07." + "204096 with jid 20150325123407204096" + ], + ) self.assertListEqual(state.running(), []) def test_low(self): - ''' + """ Test of executing a single low data call - ''' + """ mock = MagicMock(side_effect=[False, None, None]) - with patch.object(state, '_check_queue', mock): - self.assertFalse(state.low({"state": "pkg", "fun": "installed", - "name": "vi"})) + with patch.object(state, "_check_queue", mock): + self.assertFalse( + state.low({"state": "pkg", "fun": "installed", "name": "vi"}) + ) MockState.State.flag = False - self.assertEqual(state.low({"state": "pkg", "fun": "installed", - "name": "vi"}), list) + self.assertEqual( + state.low({"state": "pkg", "fun": "installed", "name": "vi"}), list + ) MockState.State.flag = True - self.assertTrue(state.low({"state": "pkg", "fun": "installed", - "name": "vi"})) + self.assertTrue( + state.low({"state": "pkg", "fun": "installed", "name": "vi"}) + ) def test_high(self): - ''' + """ Test for checking the state system - ''' + """ mock = MagicMock(side_effect=[False, None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertFalse(state.high({"vim": {"pkg": ["installed"]}})) mock = MagicMock(return_value={"test": True}) - with patch.object(salt.utils.state, 'get_sls_opts', mock): + with patch.object(salt.utils.state, "get_sls_opts", mock): self.assertTrue(state.high({"vim": {"pkg": ["installed"]}})) def test_template(self): - ''' + """ Test of executing the information stored in a template file on the minion - ''' + """ mock = MagicMock(side_effect=[False, None, None]) - with patch.object(state, '_check_queue', mock): - self.assertFalse(state.template('/home/salt/salt.sls')) + with patch.object(state, "_check_queue", mock): + self.assertFalse(state.template("/home/salt/salt.sls")) MockState.HighState.flag = True - self.assertTrue(state.template('/home/salt/salt.sls')) + self.assertTrue(state.template("/home/salt/salt.sls")) MockState.HighState.flag = False - self.assertTrue(state.template('/home/salt/salt.sls')) + self.assertTrue(state.template("/home/salt/salt.sls")) def test_template_str(self): - ''' + """ Test for Executing the information stored in a string from an sls template - ''' + """ mock = MagicMock(side_effect=[False, None]) - with patch.object(state, '_check_queue', mock): - self.assertFalse(state.template_str('Template String')) + with patch.object(state, "_check_queue", mock): + self.assertFalse(state.template_str("Template String")) - self.assertTrue(state.template_str('Template String')) + self.assertTrue(state.template_str("Template String")) def test_apply_(self): - ''' + """ Test to apply states - ''' + """ mock = MagicMock(return_value=True) - with patch.object(state, 'sls', mock): + with patch.object(state, "sls", mock): self.assertTrue(state.apply_(True)) - with patch.object(state, 'highstate', mock): + with patch.object(state, "highstate", mock): self.assertTrue(state.apply_(None)) def test_list_disabled(self): - ''' + """ Test to list disabled states - ''' + """ mock = MagicMock(return_value=["A", "B", "C"]) - with patch.dict(state.__salt__, {'grains.get': mock}): + with patch.dict(state.__salt__, {"grains.get": mock}): self.assertListEqual(state.list_disabled(), ["A", "B", "C"]) def test_enable(self): - ''' + """ Test to Enable state function or sls run - ''' + """ mock = MagicMock(return_value=["A", "B"]) - with patch.dict(state.__salt__, {'grains.get': mock}): + with patch.dict(state.__salt__, {"grains.get": mock}): mock = MagicMock(return_value=[]) - with patch.dict(state.__salt__, {'grains.setval': mock}): + with patch.dict(state.__salt__, {"grains.setval": mock}): mock = MagicMock(return_value=[]) - with patch.dict(state.__salt__, {'saltutil.refresh_modules': - mock}): - self.assertDictEqual(state.enable("A"), - {'msg': 'Info: A state enabled.', - 'res': True}) + with patch.dict(state.__salt__, {"saltutil.refresh_modules": mock}): + self.assertDictEqual( + state.enable("A"), + {"msg": "Info: A state enabled.", "res": True}, + ) - self.assertDictEqual(state.enable("Z"), - {'msg': 'Info: Z state already ' - 'enabled.', 'res': True}) + self.assertDictEqual( + state.enable("Z"), + {"msg": "Info: Z state already " "enabled.", "res": True}, + ) def test_disable(self): - ''' + """ Test to disable state run - ''' + """ mock = MagicMock(return_value=["C", "D"]) - with patch.dict(state.__salt__, {'grains.get': mock}): + with patch.dict(state.__salt__, {"grains.get": mock}): mock = MagicMock(return_value=[]) - with patch.dict(state.__salt__, {'grains.setval': mock}): + with patch.dict(state.__salt__, {"grains.setval": mock}): mock = MagicMock(return_value=[]) - with patch.dict(state.__salt__, {'saltutil.refresh_modules': - mock}): - self.assertDictEqual(state.disable("C"), - {'msg': 'Info: C state ' - 'already disabled.', - 'res': True}) + with patch.dict(state.__salt__, {"saltutil.refresh_modules": mock}): + self.assertDictEqual( + state.disable("C"), + {"msg": "Info: C state " "already disabled.", "res": True}, + ) - self.assertDictEqual(state.disable("Z"), - {'msg': 'Info: Z state ' - 'disabled.', 'res': True}) + self.assertDictEqual( + state.disable("Z"), + {"msg": "Info: Z state " "disabled.", "res": True}, + ) def test_clear_cache(self): - ''' + """ Test to clear out cached state file - ''' + """ mock = MagicMock(return_value=["A.cache.p", "B.cache.p", "C"]) - with patch.object(os, 'listdir', mock): + with patch.object(os, "listdir", mock): mock = MagicMock(return_value=True) - with patch.object(os.path, 'isfile', mock): + with patch.object(os.path, "isfile", mock): mock = MagicMock(return_value=True) - with patch.object(os, 'remove', mock): - self.assertEqual(state.clear_cache(), - ['A.cache.p', - 'B.cache.p']) + with patch.object(os, "remove", mock): + self.assertEqual(state.clear_cache(), ["A.cache.p", "B.cache.p"]) def test_single(self): - ''' + """ Test to execute single state function - ''' - ret = {'pkg_|-name=vim_|-name=vim_|-installed': list} + """ + ret = {"pkg_|-name=vim_|-name=vim_|-installed": list} mock = MagicMock(side_effect=["A", None, None, None, None]) - with patch.object(state, '_check_queue', mock): - self.assertEqual(state.single("pkg.installed", - " name=vim"), "A") + with patch.object(state, "_check_queue", mock): + self.assertEqual(state.single("pkg.installed", " name=vim"), "A") - self.assertEqual(state.single("pk", "name=vim"), - "Invalid function passed") + self.assertEqual(state.single("pk", "name=vim"), "Invalid function passed") with patch.dict(state.__opts__, {"test": "install"}): mock = MagicMock(return_value={"test": ""}) - with patch.object(salt.utils.state, 'get_sls_opts', mock): + with patch.object(salt.utils.state, "get_sls_opts", mock): mock = MagicMock(return_value=True) - with patch.object(salt.utils.args, 'test_mode', mock): - self.assertRaises(SaltInvocationError, - state.single, - "pkg.installed", - "name=vim", - pillar="A") + with patch.object(salt.utils.args, "test_mode", mock): + self.assertRaises( + SaltInvocationError, + state.single, + "pkg.installed", + "name=vim", + pillar="A", + ) MockState.State.flag = True - self.assertTrue(state.single("pkg.installed", - "name=vim")) + self.assertTrue(state.single("pkg.installed", "name=vim")) MockState.State.flag = False - self.assertDictEqual(state.single("pkg.installed", - "name=vim"), ret) + self.assertDictEqual( + state.single("pkg.installed", "name=vim"), ret + ) def test_show_top(self): - ''' + """ Test to return the top data that the minion will use for a highstate - ''' + """ mock = MagicMock(side_effect=["A", None, None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertEqual(state.show_top(), "A") MockState.HighState.flag = True - self.assertListEqual(state.show_top(), ['a', 'b']) + self.assertListEqual(state.show_top(), ["a", "b"]) MockState.HighState.flag = False - self.assertListEqual(state.show_top(), ['a', 'b', 'c']) + self.assertListEqual(state.show_top(), ["a", "b", "c"]) def test_run_request(self): - ''' + """ Test to Execute the pending state request - ''' - mock = MagicMock(side_effect=[{}, - {"name": "A"}, - {"name": {'mods': "A", - 'kwargs': {}}}]) - with patch.object(state, 'check_request', mock): + """ + mock = MagicMock( + side_effect=[{}, {"name": "A"}, {"name": {"mods": "A", "kwargs": {}}}] + ) + with patch.object(state, "check_request", mock): self.assertDictEqual(state.run_request("A"), {}) self.assertDictEqual(state.run_request("A"), {}) mock = MagicMock(return_value=["True"]) - with patch.object(state, 'apply_', mock): + with patch.object(state, "apply_", mock): mock = MagicMock(return_value="") - with patch.object(os, 'remove', mock): - self.assertListEqual(state.run_request("name"), - ["True"]) + with patch.object(os, "remove", mock): + self.assertListEqual(state.run_request("name"), ["True"]) def test_show_highstate(self): - ''' + """ Test to retrieve the highstate data from the salt master - ''' + """ mock = MagicMock(side_effect=["A", None, None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertEqual(state.show_highstate(), "A") - self.assertRaises(SaltInvocationError, - state.show_highstate, - pillar="A") + self.assertRaises(SaltInvocationError, state.show_highstate, pillar="A") self.assertEqual(state.show_highstate(), "A") def test_show_lowstate(self): - ''' + """ Test to list out the low data that will be applied to this minion - ''' + """ mock = MagicMock(side_effect=["A", None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertRaises(AssertionError, state.show_lowstate) self.assertTrue(state.show_lowstate()) def test_show_state_usage(self): - ''' + """ Test to list out the state usage that will be applied to this minion - ''' + """ mock = MagicMock(side_effect=["A", None, None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertEqual(state.show_state_usage(), "A") - self.assertRaises(SaltInvocationError, - state.show_state_usage, - pillar="A") + self.assertRaises(SaltInvocationError, state.show_state_usage, pillar="A") self.assertEqual(state.show_state_usage(), "A") def test_show_states(self): - ''' + """ Test to display the low data from a specific sls - ''' + """ mock = MagicMock(side_effect=["A", None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertEqual(state.show_low_sls("foo"), "A") - self.assertListEqual(state.show_states("foo"), ['abc']) + self.assertListEqual(state.show_states("foo"), ["abc"]) def test_show_states_missing_sls(self): - ''' + """ Test state.show_states when a sls file defined in a top.sls file is missing - ''' + """ msg = ["No matching sls found for 'cloud' in evn 'base'"] chunks_mock = MagicMock(side_effect=[msg]) mock = MagicMock(side_effect=["A", None]) - with patch.object(state, '_check_queue', mock),\ - patch('salt.state.HighState.compile_low_chunks', chunks_mock): + with patch.object(state, "_check_queue", mock), patch( + "salt.state.HighState.compile_low_chunks", chunks_mock + ): self.assertEqual(state.show_low_sls("foo"), "A") self.assertListEqual(state.show_states("foo"), [msg[0]]) def test_sls_id(self): - ''' + """ Test to call a single ID from the named module(s) and handle all requisites - ''' + """ mock = MagicMock(side_effect=["A", None, None, None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertEqual(state.sls_id("apache", "http"), "A") with patch.dict(state.__opts__, {"test": "A"}): - mock = MagicMock( - return_value={'test': True, - 'saltenv': None} - ) - with patch.object(salt.utils.state, 'get_sls_opts', mock): + mock = MagicMock(return_value={"test": True, "saltenv": None}) + with patch.object(salt.utils.state, "get_sls_opts", mock): mock = MagicMock(return_value=True) - with patch.object(salt.utils.args, 'test_mode', mock): + with patch.object(salt.utils.args, "test_mode", mock): MockState.State.flag = True MockState.HighState.flag = True self.assertEqual(state.sls_id("apache", "http"), 2) MockState.State.flag = False - self.assertDictEqual(state.sls_id("ABC", "http"), - {'': 'ABC'}) - self.assertRaises(SaltInvocationError, - state.sls_id, - "DEF", "http") + self.assertDictEqual(state.sls_id("ABC", "http"), {"": "ABC"}) + self.assertRaises( + SaltInvocationError, state.sls_id, "DEF", "http" + ) def test_show_low_sls(self): - ''' + """ Test to display the low data from a specific sls - ''' + """ mock = MagicMock(side_effect=["A", None, None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertEqual(state.show_low_sls("foo"), "A") with patch.dict(state.__opts__, {"test": "A"}): - mock = MagicMock( - return_value={'test': True, - 'saltenv': None} - ) - with patch.object(salt.utils.state, 'get_sls_opts', mock): + mock = MagicMock(return_value={"test": True, "saltenv": None}) + with patch.object(salt.utils.state, "get_sls_opts", mock): MockState.State.flag = True MockState.HighState.flag = True self.assertEqual(state.show_low_sls("foo"), 2) MockState.State.flag = False - self.assertListEqual(state.show_low_sls("foo"), - [{'__id__': 'ABC'}]) + self.assertListEqual(state.show_low_sls("foo"), [{"__id__": "ABC"}]) def test_show_sls(self): - ''' + """ Test to display the state data from a specific sls - ''' + """ mock = MagicMock(side_effect=["A", None, None, None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertEqual(state.show_sls("foo"), "A") with patch.dict(state.__opts__, {"test": "A"}): - mock = MagicMock( - return_value={'test': True, - 'saltenv': None} - ) - with patch.object(salt.utils.state, 'get_sls_opts', mock): + mock = MagicMock(return_value={"test": True, "saltenv": None}) + with patch.object(salt.utils.state, "get_sls_opts", mock): mock = MagicMock(return_value=True) - with patch.object(salt.utils.args, 'test_mode', mock): - self.assertRaises(SaltInvocationError, - state.show_sls, - "foo", - pillar="A") + with patch.object(salt.utils.args, "test_mode", mock): + self.assertRaises( + SaltInvocationError, state.show_sls, "foo", pillar="A" + ) MockState.State.flag = True self.assertEqual(state.show_sls("foo"), 2) MockState.State.flag = False - self.assertListEqual(state.show_sls("foo"), - ['a', 'b']) + self.assertListEqual(state.show_sls("foo"), ["a", "b"]) def test_sls_exists(self): - ''' + """ Test of sls_exists - ''' + """ test_state = {} test_missing_state = [] mock = MagicMock(return_value=test_state) - with patch.object(state, 'show_sls', mock): + with patch.object(state, "show_sls", mock): self.assertTrue(state.sls_exists("state_name")) mock = MagicMock(return_value=test_missing_state) - with patch.object(state, 'show_sls', mock): + with patch.object(state, "show_sls", mock): self.assertFalse(state.sls_exists("missing_state")) def test_id_exists(self): - ''' + """ Test of id_exists - ''' - test_state = [{ - "key1": "value1", - "name": "value1", - "state": "file", - "fun": "test", - "__env__": "base", - "__sls__": "test-sls", - "order": 10000, - "__id__": "state_id1" - }, - { - "key2": "value2", - "name": "value2", - "state": "file", - "fun": "directory", - "__env__": "base", - "__sls__": "test-sls", - "order": 10001, - "__id__": "state_id2" - }] + """ + test_state = [ + { + "key1": "value1", + "name": "value1", + "state": "file", + "fun": "test", + "__env__": "base", + "__sls__": "test-sls", + "order": 10000, + "__id__": "state_id1", + }, + { + "key2": "value2", + "name": "value2", + "state": "file", + "fun": "directory", + "__env__": "base", + "__sls__": "test-sls", + "order": 10001, + "__id__": "state_id2", + }, + ] mock = MagicMock(return_value=test_state) - with patch.object(state, 'show_low_sls', mock): + with patch.object(state, "show_low_sls", mock): self.assertTrue(state.id_exists("state_id1,state_id2", "test-sls")) self.assertFalse(state.id_exists("invalid", "state_name")) def test_top(self): - ''' + """ Test to execute a specific top file - ''' - ret = ['Pillar failed to render with the following messages:', 'E'] + """ + ret = ["Pillar failed to render with the following messages:", "E"] mock = MagicMock(side_effect=["A", None, None, None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertEqual(state.top("reverse_top.sls"), "A") - mock = MagicMock(side_effect=[['E'], None, None]) - with patch.object(state, '_get_pillar_errors', mock): - with patch.dict(state.__pillar__, {"_errors": ['E']}): + mock = MagicMock(side_effect=[["E"], None, None]) + with patch.object(state, "_get_pillar_errors", mock): + with patch.dict(state.__pillar__, {"_errors": ["E"]}): self.assertListEqual(state.top("reverse_top.sls"), ret) with patch.dict(state.__opts__, {"test": "A"}): - mock = MagicMock(return_value={'test': True}) - with patch.object(salt.utils.state, 'get_sls_opts', mock): + mock = MagicMock(return_value={"test": True}) + with patch.object(salt.utils.state, "get_sls_opts", mock): mock = MagicMock(return_value=True) - with patch.object(salt.utils.args, 'test_mode', mock): - self.assertRaises(SaltInvocationError, - state.top, - "reverse_top.sls", - pillar="A") + with patch.object(salt.utils.args, "test_mode", mock): + self.assertRaises( + SaltInvocationError, + state.top, + "reverse_top.sls", + pillar="A", + ) - mock = MagicMock( - return_value - = - 'salt://reverse_top.sls') - with patch.object(os.path, 'join', mock): + mock = MagicMock(return_value="salt://reverse_top.sls") + with patch.object(os.path, "join", mock): mock = MagicMock(return_value=True) - with patch.object(state, '_set_retcode', - mock): + with patch.object(state, "_set_retcode", mock): self.assertTrue( - state. - top("reverse_top.sls " - "exclude=exclude.sls")) + state.top( + "reverse_top.sls " "exclude=exclude.sls" + ) + ) def test_highstate(self): - ''' + """ Test to retrieve the state data from the salt master for the minion and execute it - ''' + """ arg = "whitelist=sls1.sls" mock = MagicMock(side_effect=[True, False, False, False]) - with patch.object(state, '_disabled', mock): - self.assertDictEqual(state.highstate("whitelist=sls1.sls"), - {'comment': 'Disabled', - 'name': 'Salt highstate run is disabled. ' - 'To re-enable, run state.enable highstate', - 'result': 'False'}) + with patch.object(state, "_disabled", mock): + self.assertDictEqual( + state.highstate("whitelist=sls1.sls"), + { + "comment": "Disabled", + "name": "Salt highstate run is disabled. " + "To re-enable, run state.enable highstate", + "result": "False", + }, + ) mock = MagicMock(side_effect=["A", None, None]) - with patch.object(state, '_check_queue', mock): + with patch.object(state, "_check_queue", mock): self.assertEqual(state.highstate("whitelist=sls1.sls"), "A") with patch.dict(state.__opts__, {"test": "A"}): - mock = MagicMock(return_value={'test': True}) - with patch.object(salt.utils.state, 'get_sls_opts', mock): - self.assertRaises(SaltInvocationError, - state.highstate, - "whitelist=sls1.sls", - pillar="A") + mock = MagicMock(return_value={"test": True}) + with patch.object(salt.utils.state, "get_sls_opts", mock): + self.assertRaises( + SaltInvocationError, + state.highstate, + "whitelist=sls1.sls", + pillar="A", + ) mock = MagicMock(return_value="A") - with patch.object(state, '_filter_running', - mock): + with patch.object(state, "_filter_running", mock): mock = MagicMock(return_value=True) - with patch.object(state, '_filter_running', - mock): + with patch.object(state, "_filter_running", mock): mock = MagicMock(return_value=True) - with patch.object(salt.payload, 'Serial', - mock): - with patch.object(os.path, - 'join', mock): + with patch.object(salt.payload, "Serial", mock): + with patch.object(os.path, "join", mock): with patch.object( - state, - '_set' - '_retcode', - mock): - self.assertTrue(state. - highstate - (arg)) + state, "_set" "_retcode", mock + ): + self.assertTrue(state.highstate(arg)) def test_clear_request(self): - ''' + """ Test to clear out the state execution request without executing it - ''' + """ mock = MagicMock(return_value=True) - with patch.object(salt.payload, 'Serial', mock): + with patch.object(salt.payload, "Serial", mock): mock = MagicMock(side_effect=[False, True, True]) - with patch.object(os.path, 'isfile', mock): + with patch.object(os.path, "isfile", mock): self.assertTrue(state.clear_request("A")) mock = MagicMock(return_value=True) - with patch.object(os, 'remove', mock): + with patch.object(os, "remove", mock): self.assertTrue(state.clear_request()) mock = MagicMock(return_value={}) - with patch.object(state, 'check_request', mock): + with patch.object(state, "check_request", mock): self.assertFalse(state.clear_request("A")) def test_check_request(self): - ''' + """ Test to return the state request information - ''' - with patch('salt.modules.state.salt.payload', MockSerial): + """ + with patch("salt.modules.state.salt.payload", MockSerial): mock = MagicMock(side_effect=[True, True, False]) - with patch.object(os.path, 'isfile', mock): - with patch('salt.utils.files.fopen', mock_open()): - self.assertDictEqual(state.check_request(), {'A': 'B'}) + with patch.object(os.path, "isfile", mock): + with patch("salt.utils.files.fopen", mock_open()): + self.assertDictEqual(state.check_request(), {"A": "B"}) - with patch('salt.utils.files.fopen', mock_open()): - self.assertEqual(state.check_request("A"), 'B') + with patch("salt.utils.files.fopen", mock_open()): + self.assertEqual(state.check_request("A"), "B") self.assertDictEqual(state.check_request(), {}) def test_request(self): - ''' + """ Test to request the local admin execute a state run - ''' + """ mock = MagicMock(return_value=True) - with patch.object(state, 'apply_', mock): + with patch.object(state, "apply_", mock): mock = MagicMock(return_value=True) - with patch.object(os.path, 'join', mock): - mock = MagicMock(return_value= - {"test_run": "", - "mods": "", - "kwargs": ""}) - with patch.object(state, 'check_request', mock): + with patch.object(os.path, "join", mock): + mock = MagicMock( + return_value={"test_run": "", "mods": "", "kwargs": ""} + ) + with patch.object(state, "check_request", mock): mock = MagicMock(return_value=True) - with patch.object(os, 'umask', mock): - with patch.object(salt.utils.platform, 'is_windows', mock): - with patch.dict(state.__salt__, {'cmd.run': mock}): - with patch('salt.utils.files.fopen', mock_open()): + with patch.object(os, "umask", mock): + with patch.object(salt.utils.platform, "is_windows", mock): + with patch.dict(state.__salt__, {"cmd.run": mock}): + with patch("salt.utils.files.fopen", mock_open()): mock = MagicMock(return_value=True) - with patch.object(os, 'umask', mock): + with patch.object(os, "umask", mock): self.assertTrue(state.request("A")) def test_sls(self): - ''' + """ Test to execute a set list of state files from an environment - ''' + """ arg = "core,edit.vim dev" - ret = ['Pillar failed to render with the following messages:', 'E', '1'] + ret = ["Pillar failed to render with the following messages:", "E", "1"] mock = MagicMock(return_value=True) - with patch.object(state, 'running', mock): + with patch.object(state, "running", mock): with patch.dict(state.__context__, {"retcode": 1}): self.assertEqual(state.sls("core,edit.vim dev"), True) mock = MagicMock(side_effect=[True, True, True, True, True, True]) - with patch.object(state, '_wait', mock): + with patch.object(state, "_wait", mock): mock = MagicMock(side_effect=[["A"], [], [], [], [], []]) - with patch.object(state, '_disabled', mock): + with patch.object(state, "_disabled", mock): with patch.dict(state.__context__, {"retcode": 1}): self.assertEqual( - state.sls("core,edit.vim dev", - None, - None, - True), - ["A"]) + state.sls("core,edit.vim dev", None, None, True), ["A"] + ) - mock = MagicMock(side_effect=[['E', '1'], None, None, None, None]) - with patch.object(state, '_get_pillar_errors', mock): + mock = MagicMock(side_effect=[["E", "1"], None, None, None, None]) + with patch.object(state, "_get_pillar_errors", mock): with patch.dict(state.__context__, {"retcode": 5}): - with patch.dict(state.__pillar__, {"_errors": ['E', '1']}): - self.assertListEqual(state.sls("core,edit.vim dev", - None, - None, - True), ret) + with patch.dict(state.__pillar__, {"_errors": ["E", "1"]}): + self.assertListEqual( + state.sls("core,edit.vim dev", None, None, True), ret + ) with patch.dict(state.__opts__, {"test": None}): - mock = MagicMock(return_value={"test": "", - "saltenv": None}) - with patch.object(salt.utils.state, 'get_sls_opts', mock): + mock = MagicMock(return_value={"test": "", "saltenv": None}) + with patch.object(salt.utils.state, "get_sls_opts", mock): mock = MagicMock(return_value=True) - with patch.object(salt.utils.args, - 'test_mode', - mock): + with patch.object(salt.utils.args, "test_mode", mock): self.assertRaises( - SaltInvocationError, - state.sls, - "core,edit.vim dev", - None, - None, - True, - pillar="A") + SaltInvocationError, + state.sls, + "core,edit.vim dev", + None, + None, + True, + pillar="A", + ) mock = MagicMock(return_value="/D/cache.cache.p") - with patch.object(os.path, - 'join', - mock): + with patch.object(os.path, "join", mock): mock = MagicMock(return_value=True) - with patch.object(os.path, - 'isfile', - mock): + with patch.object(os.path, "isfile", mock): with patch( - 'salt.utils.files.fopen', - mock_open(b'')): + "salt.utils.files.fopen", mock_open(b"") + ): self.assertTrue( - state.sls(arg, - None, - None, - True, - cache - =True - ) - ) + state.sls( + arg, None, None, True, cache=True + ) + ) MockState.HighState.flag = True - self.assertTrue(state.sls("core,edit" - ".vim dev", - None, - None, - True) - ) + self.assertTrue( + state.sls( + "core,edit" ".vim dev", None, None, True + ) + ) MockState.HighState.flag = False mock = MagicMock(return_value=True) - with patch.object(state, - '_filter_' - 'running', - mock): + with patch.object( + state, "_filter_" "running", mock + ): self.sub_test_sls() def test_get_test_value(self): - ''' + """ Test _get_test_value when opts contains different values - ''' - test_arg = 'test' + """ + test_arg = "test" with patch.dict(state.__opts__, {test_arg: True}): - self.assertTrue(state._get_test_value(test=None), - msg='Failure when {0} is True in __opts__'.format(test_arg)) + self.assertTrue( + state._get_test_value(test=None), + msg="Failure when {0} is True in __opts__".format(test_arg), + ) - with patch.dict(config.__pillar__, {test_arg: 'blah'}): - self.assertFalse(state._get_test_value(test=None), - msg='Failure when {0} is blah in __opts__'.format(test_arg)) + with patch.dict(config.__pillar__, {test_arg: "blah"}): + self.assertFalse( + state._get_test_value(test=None), + msg="Failure when {0} is blah in __opts__".format(test_arg), + ) - with patch.dict(config.__pillar__, {test_arg: 'true'}): - self.assertFalse(state._get_test_value(test=None), - msg='Failure when {0} is true in __opts__'.format(test_arg)) + with patch.dict(config.__pillar__, {test_arg: "true"}): + self.assertFalse( + state._get_test_value(test=None), + msg="Failure when {0} is true in __opts__".format(test_arg), + ) with patch.dict(config.__opts__, {test_arg: False}): - self.assertFalse(state._get_test_value(test=None), - msg='Failure when {0} is False in __opts__'.format(test_arg)) + self.assertFalse( + state._get_test_value(test=None), + msg="Failure when {0} is False in __opts__".format(test_arg), + ) with patch.dict(config.__opts__, {}): - self.assertFalse(state._get_test_value(test=None), - msg='Failure when {0} does not exist in __opts__'.format(test_arg)) + self.assertFalse( + state._get_test_value(test=None), + msg="Failure when {0} does not exist in __opts__".format(test_arg), + ) with patch.dict(config.__pillar__, {test_arg: None}): - self.assertEqual(state._get_test_value(test=None), None, - msg='Failure when {0} is None in __opts__'.format(test_arg)) + self.assertEqual( + state._get_test_value(test=None), + None, + msg="Failure when {0} is None in __opts__".format(test_arg), + ) with patch.dict(config.__pillar__, {test_arg: True}): - self.assertTrue(state._get_test_value(test=None), - msg='Failure when {0} is True in __pillar__'.format(test_arg)) + self.assertTrue( + state._get_test_value(test=None), + msg="Failure when {0} is True in __pillar__".format(test_arg), + ) - with patch.dict(config.__pillar__, {'master': {test_arg: True}}): - self.assertTrue(state._get_test_value(test=None), - msg='Failure when {0} is True in master __pillar__'.format(test_arg)) + with patch.dict(config.__pillar__, {"master": {test_arg: True}}): + self.assertTrue( + state._get_test_value(test=None), + msg="Failure when {0} is True in master __pillar__".format(test_arg), + ) - with patch.dict(config.__pillar__, {'master': {test_arg: False}}): + with patch.dict(config.__pillar__, {"master": {test_arg: False}}): with patch.dict(config.__pillar__, {test_arg: True}): - self.assertTrue(state._get_test_value(test=None), - msg='Failure when {0} is False in master __pillar__ and True in pillar'.format(test_arg)) + self.assertTrue( + state._get_test_value(test=None), + msg="Failure when {0} is False in master __pillar__ and True in pillar".format( + test_arg + ), + ) - with patch.dict(config.__pillar__, {'master': {test_arg: True}}): + with patch.dict(config.__pillar__, {"master": {test_arg: True}}): with patch.dict(config.__pillar__, {test_arg: False}): - self.assertFalse(state._get_test_value(test=None), - msg='Failure when {0} is True in master __pillar__ and False in pillar'.format(test_arg)) + self.assertFalse( + state._get_test_value(test=None), + msg="Failure when {0} is True in master __pillar__ and False in pillar".format( + test_arg + ), + ) - with patch.dict(state.__opts__, {'test': False}): - self.assertFalse(state._get_test_value(test=None), - msg='Failure when {0} is False in __opts__'.format(test_arg)) + with patch.dict(state.__opts__, {"test": False}): + self.assertFalse( + state._get_test_value(test=None), + msg="Failure when {0} is False in __opts__".format(test_arg), + ) - with patch.dict(state.__opts__, {'test': False}): - with patch.dict(config.__pillar__, {'master': {test_arg: True}}): - self.assertTrue(state._get_test_value(test=None), - msg='Failure when {0} is False in __opts__'.format(test_arg)) + with patch.dict(state.__opts__, {"test": False}): + with patch.dict(config.__pillar__, {"master": {test_arg: True}}): + self.assertTrue( + state._get_test_value(test=None), + msg="Failure when {0} is False in __opts__".format(test_arg), + ) with patch.dict(state.__opts__, {}): - self.assertTrue(state._get_test_value(test=True), - msg='Failure when test is True as arg') + self.assertTrue( + state._get_test_value(test=True), msg="Failure when test is True as arg" + ) def sub_test_sls(self): - ''' + """ Sub function of test_sls - ''' + """ mock = MagicMock(return_value=True) - with patch.object(os.path, 'join', mock): - with patch.object(os, 'umask', mock): + with patch.object(os.path, "join", mock): + with patch.object(os, "umask", mock): mock = MagicMock(return_value=False) - with patch.object(salt.utils.platform, 'is_windows', mock): + with patch.object(salt.utils.platform, "is_windows", mock): mock = MagicMock(return_value=True) - with patch.object(os, 'umask', mock): - with patch.object(state, '_set_retcode', mock): - with patch.dict(state.__opts__, - {"test": True}): - with patch('salt.utils.files.fopen', mock_open()): - self.assertTrue(state.sls("core,edit" - ".vim dev", - None, - None, - True)) + with patch.object(os, "umask", mock): + with patch.object(state, "_set_retcode", mock): + with patch.dict(state.__opts__, {"test": True}): + with patch("salt.utils.files.fopen", mock_open()): + self.assertTrue( + state.sls( + "core,edit" ".vim dev", None, None, True + ) + ) def test_sls_sync(self): - ''' + """ Test test.sls with the sync argument We're only mocking the sync functions we expect to sync. If any other sync functions are run then they will raise a KeyError, which we want as it will tell us that we are syncing things we shouldn't. - ''' + """ mock_empty_list = MagicMock(return_value=[]) - with patch.object(state, 'running', mock_empty_list), \ - patch.object(state, '_disabled', mock_empty_list), \ - patch.object(state, '_get_pillar_errors', mock_empty_list): + with patch.object(state, "running", mock_empty_list), patch.object( + state, "_disabled", mock_empty_list + ), patch.object(state, "_get_pillar_errors", mock_empty_list): sync_mocks = { - 'saltutil.sync_modules': Mock(), - 'saltutil.sync_states': Mock(), + "saltutil.sync_modules": Mock(), + "saltutil.sync_states": Mock(), } with patch.dict(state.__salt__, sync_mocks): - state.sls('foo', sync_mods='modules,states') + state.sls("foo", sync_mods="modules,states") for key in sync_mocks: call_count = sync_mocks[key].call_count expected = 1 - assert call_count == expected, \ - '{0} called {1} time(s) (expected: {2})'.format( - key, call_count, expected - ) + assert ( + call_count == expected + ), "{0} called {1} time(s) (expected: {2})".format( + key, call_count, expected + ) # Test syncing all - sync_mocks = {'saltutil.sync_all': Mock()} + sync_mocks = {"saltutil.sync_all": Mock()} with patch.dict(state.__salt__, sync_mocks): - state.sls('foo', sync_mods='all') + state.sls("foo", sync_mods="all") for key in sync_mocks: call_count = sync_mocks[key].call_count expected = 1 - assert call_count == expected, \ - '{0} called {1} time(s) (expected: {2})'.format( - key, call_count, expected - ) + assert ( + call_count == expected + ), "{0} called {1} time(s) (expected: {2})".format( + key, call_count, expected + ) # sync_mods=True should be interpreted as sync_mods=all - sync_mocks = {'saltutil.sync_all': Mock()} + sync_mocks = {"saltutil.sync_all": Mock()} with patch.dict(state.__salt__, sync_mocks): - state.sls('foo', sync_mods=True) + state.sls("foo", sync_mods=True) for key in sync_mocks: call_count = sync_mocks[key].call_count expected = 1 - assert call_count == expected, \ - '{0} called {1} time(s) (expected: {2})'.format( - key, call_count, expected - ) + assert ( + call_count == expected + ), "{0} called {1} time(s) (expected: {2})".format( + key, call_count, expected + ) # Test syncing all when "all" is passed along with module types. # This tests that we *only* run a sync_all and avoid unnecessary # extra syncing. - sync_mocks = {'saltutil.sync_all': Mock()} + sync_mocks = {"saltutil.sync_all": Mock()} with patch.dict(state.__salt__, sync_mocks): - state.sls('foo', sync_mods='modules,all') + state.sls("foo", sync_mods="modules,all") for key in sync_mocks: call_count = sync_mocks[key].call_count expected = 1 - assert call_count == expected, \ - '{0} called {1} time(s) (expected: {2})'.format( - key, call_count, expected - ) + assert ( + call_count == expected + ), "{0} called {1} time(s) (expected: {2})".format( + key, call_count, expected + ) def test_pkg(self): - ''' + """ Test to execute a packaged state run - ''' - tar_file = os.sep + os.path.join('tmp', 'state_pkg.tgz') - mock = MagicMock(side_effect=[False, True, True, True, True, True, - True, True, True, True, True]) + """ + tar_file = os.sep + os.path.join("tmp", "state_pkg.tgz") + mock = MagicMock( + side_effect=[ + False, + True, + True, + True, + True, + True, + True, + True, + True, + True, + True, + ] + ) mock_json_loads_true = MagicMock(return_value=[True]) mock_json_loads_dictlist = MagicMock(return_value=[{"test": ""}]) - with patch.object(os.path, 'isfile', mock), \ - patch('salt.modules.state.tarfile', MockTarFile), \ - patch.object(salt.utils, 'json', mock_json_loads_dictlist): + with patch.object(os.path, "isfile", mock), patch( + "salt.modules.state.tarfile", MockTarFile + ), patch.object(salt.utils, "json", mock_json_loads_dictlist): self.assertEqual(state.pkg(tar_file, "", "md5"), {}) mock = MagicMock(side_effect=[False, 0, 0, 0, 0]) - with patch.object(salt.utils.hashutils, 'get_hash', mock): + with patch.object(salt.utils.hashutils, "get_hash", mock): # Verify hash self.assertDictEqual(state.pkg(tar_file, "", "md5"), {}) @@ -1128,169 +1148,193 @@ class StateTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual(state.pkg(tar_file, 0, "md5"), {}) MockTarFile.path = "" - with patch('salt.utils.files.fopen', mock_open()), \ - patch.object(salt.utils.json, 'loads', mock_json_loads_true): + with patch("salt.utils.files.fopen", mock_open()), patch.object( + salt.utils.json, "loads", mock_json_loads_true + ): self.assertEqual(state.pkg(tar_file, 0, "md5"), True) MockTarFile.path = "" if six.PY2: - with patch('salt.utils.files.fopen', mock_open()), \ - patch.dict(state.__utils__, {'state.check_result': MagicMock(return_value=True)}): + with patch("salt.utils.files.fopen", mock_open()), patch.dict( + state.__utils__, + {"state.check_result": MagicMock(return_value=True)}, + ): self.assertTrue(state.pkg(tar_file, 0, "md5")) else: - with patch('salt.utils.files.fopen', mock_open()): + with patch("salt.utils.files.fopen", mock_open()): self.assertTrue(state.pkg(tar_file, 0, "md5")) def test_lock_saltenv(self): - ''' + """ Tests lock_saltenv in each function which accepts saltenv on the CLI - ''' - lock_msg = 'lock_saltenv is enabled, saltenv cannot be changed' + """ + lock_msg = "lock_saltenv is enabled, saltenv cannot be changed" empty_list_mock = MagicMock(return_value=[]) - with patch.dict(state.__opts__, {'lock_saltenv': True}), \ - patch.dict(state.__salt__, {'grains.get': empty_list_mock}), \ - patch.object(state, 'running', empty_list_mock): + with patch.dict(state.__opts__, {"lock_saltenv": True}), patch.dict( + state.__salt__, {"grains.get": empty_list_mock} + ), patch.object(state, "running", empty_list_mock): # Test high with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.high( - [{"vim": {"pkg": ["installed"]}}], saltenv='base') + state.high([{"vim": {"pkg": ["installed"]}}], saltenv="base") # Test template with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.template('foo', saltenv='base') + state.template("foo", saltenv="base") # Test template_str with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.template_str('foo', saltenv='base') + state.template_str("foo", saltenv="base") # Test apply_ with SLS with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.apply_('foo', saltenv='base') + state.apply_("foo", saltenv="base") # Test apply_ with Highstate with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.apply_(saltenv='base') + state.apply_(saltenv="base") # Test highstate with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.highstate(saltenv='base') + state.highstate(saltenv="base") # Test sls with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.sls('foo', saltenv='base') + state.sls("foo", saltenv="base") # Test top with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.top('foo.sls', saltenv='base') + state.top("foo.sls", saltenv="base") # Test show_highstate with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.show_highstate(saltenv='base') + state.show_highstate(saltenv="base") # Test show_lowstate with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.show_lowstate(saltenv='base') + state.show_lowstate(saltenv="base") # Test sls_id with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.sls_id('foo', 'bar', saltenv='base') + state.sls_id("foo", "bar", saltenv="base") # Test show_low_sls with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.show_low_sls('foo', saltenv='base') + state.show_low_sls("foo", saltenv="base") # Test show_sls with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.show_sls('foo', saltenv='base') + state.show_sls("foo", saltenv="base") # Test show_top with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.show_top(saltenv='base') + state.show_top(saltenv="base") # Test single with self.assertRaisesRegex(CommandExecutionError, lock_msg): - state.single('foo.bar', name='baz', saltenv='base') + state.single("foo.bar", name="baz", saltenv="base") # Test pkg with self.assertRaisesRegex(CommandExecutionError, lock_msg): state.pkg( - '/tmp/salt_state.tgz', - '760a9353810e36f6d81416366fc426dc', - 'md5', - saltenv='base') + "/tmp/salt_state.tgz", + "760a9353810e36f6d81416366fc426dc", + "md5", + saltenv="base", + ) def test_get_pillar_errors_CC(self): - ''' + """ Test _get_pillar_errors function. CC: External clean, Internal clean :return: - ''' - for int_pillar, ext_pillar in [({'foo': 'bar'}, {'fred': 'baz'}), - ({'foo': 'bar'}, None), - ({}, {'fred': 'baz'})]: - with patch('salt.modules.state.__pillar__', int_pillar): - for opts, res in [({'force': True}, None), - ({'force': False}, None), - ({}, None)]: - assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar) + """ + for int_pillar, ext_pillar in [ + ({"foo": "bar"}, {"fred": "baz"}), + ({"foo": "bar"}, None), + ({}, {"fred": "baz"}), + ]: + with patch("salt.modules.state.__pillar__", int_pillar): + for opts, res in [ + ({"force": True}, None), + ({"force": False}, None), + ({}, None), + ]: + assert res == state._get_pillar_errors( + kwargs=opts, pillar=ext_pillar + ) def test_get_pillar_errors_EC(self): - ''' + """ Test _get_pillar_errors function. EC: External erroneous, Internal clean :return: - ''' - errors = ['failure', 'everywhere'] - for int_pillar, ext_pillar in [({'foo': 'bar'}, {'fred': 'baz', '_errors': errors}), - ({}, {'fred': 'baz', '_errors': errors})]: - with patch('salt.modules.state.__pillar__', int_pillar): - for opts, res in [({'force': True}, None), - ({'force': False}, errors), - ({}, errors)]: - assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar) + """ + errors = ["failure", "everywhere"] + for int_pillar, ext_pillar in [ + ({"foo": "bar"}, {"fred": "baz", "_errors": errors}), + ({}, {"fred": "baz", "_errors": errors}), + ]: + with patch("salt.modules.state.__pillar__", int_pillar): + for opts, res in [ + ({"force": True}, None), + ({"force": False}, errors), + ({}, errors), + ]: + assert res == state._get_pillar_errors( + kwargs=opts, pillar=ext_pillar + ) def test_get_pillar_errors_EE(self): - ''' + """ Test _get_pillar_errors function. CC: External erroneous, Internal erroneous :return: - ''' - errors = ['failure', 'everywhere'] - for int_pillar, ext_pillar in [({'foo': 'bar', '_errors': errors}, {'fred': 'baz', '_errors': errors})]: - with patch('salt.modules.state.__pillar__', int_pillar): - for opts, res in [({'force': True}, None), - ({'force': False}, errors), - ({}, errors)]: - assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar) + """ + errors = ["failure", "everywhere"] + for int_pillar, ext_pillar in [ + ({"foo": "bar", "_errors": errors}, {"fred": "baz", "_errors": errors}) + ]: + with patch("salt.modules.state.__pillar__", int_pillar): + for opts, res in [ + ({"force": True}, None), + ({"force": False}, errors), + ({}, errors), + ]: + assert res == state._get_pillar_errors( + kwargs=opts, pillar=ext_pillar + ) def test_get_pillar_errors_CE(self): - ''' + """ Test _get_pillar_errors function. CC: External clean, Internal erroneous :return: - ''' - errors = ['failure', 'everywhere'] - for int_pillar, ext_pillar in [({'foo': 'bar', '_errors': errors}, {'fred': 'baz'}), - ({'foo': 'bar', '_errors': errors}, None)]: - with patch('salt.modules.state.__pillar__', int_pillar): - for opts, res in [({'force': True}, None), - ({'force': False}, errors), - ({}, errors)]: - assert res == state._get_pillar_errors(kwargs=opts, pillar=ext_pillar) + """ + errors = ["failure", "everywhere"] + for int_pillar, ext_pillar in [ + ({"foo": "bar", "_errors": errors}, {"fred": "baz"}), + ({"foo": "bar", "_errors": errors}, None), + ]: + with patch("salt.modules.state.__pillar__", int_pillar): + for opts, res in [ + ({"force": True}, None), + ({"force": False}, errors), + ({}, errors), + ]: + assert res == state._get_pillar_errors( + kwargs=opts, pillar=ext_pillar + ) class TopFileMergingCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { state: { - '__opts__': salt.config.minion_config( - os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion') + "__opts__": salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion") ), - '__salt__': { - 'saltutil.is_running': MagicMock(return_value=[]), - }, + "__salt__": {"saltutil.is_running": MagicMock(return_value=[])}, }, } @@ -1300,14 +1344,15 @@ class TopFileMergingCase(TestCase, LoaderModuleMockMixin): self.addCleanup(shutil.rmtree, self.cachedir, ignore_errors=True) self.addCleanup(shutil.rmtree, self.fileserver_root, ignore_errors=True) - self.saltenvs = ['base', 'foo', 'bar', 'baz'] + self.saltenvs = ["base", "foo", "bar", "baz"] self.saltenv_roots = { x: os.path.join(self.fileserver_root, x) - for x in ('base', 'foo', 'bar', 'baz') + for x in ("base", "foo", "bar", "baz") } - self.base_top_file = os.path.join(self.saltenv_roots['base'], 'top.sls') + self.base_top_file = os.path.join(self.saltenv_roots["base"], "top.sls") self.dunder_opts = salt.utils.yaml.safe_load( - textwrap.dedent('''\ + textwrap.dedent( + """\ file_client: local default_top: base @@ -1320,26 +1365,34 @@ class TopFileMergingCase(TestCase, LoaderModuleMockMixin): - {bar} baz: - {baz} - '''.format(**self.saltenv_roots) + """.format( + **self.saltenv_roots + ) ) ) - self.dunder_opts['env_order'] = self.saltenvs + self.dunder_opts["env_order"] = self.saltenvs # Write top files for all but the "baz" environment for saltenv in self.saltenv_roots: os.makedirs(self.saltenv_roots[saltenv]) - if saltenv == 'baz': + if saltenv == "baz": continue - top_file = os.path.join(self.saltenv_roots[saltenv], 'top.sls') - with salt.utils.files.fopen(top_file, 'w') as fp_: + top_file = os.path.join(self.saltenv_roots[saltenv], "top.sls") + with salt.utils.files.fopen(top_file, "w") as fp_: # Add a section for every environment to each top file, with # the SLS target prefixed with the current saltenv. for env_name in self.saltenvs: - fp_.write(textwrap.dedent('''\ + fp_.write( + textwrap.dedent( + """\ {env_name}: '*': - {saltenv}_{env_name} - '''.format(env_name=env_name, saltenv=saltenv))) + """.format( + env_name=env_name, saltenv=saltenv + ) + ) + ) def tearDown(self): time.sleep(1) @@ -1348,41 +1401,45 @@ class TopFileMergingCase(TestCase, LoaderModuleMockMixin): def show_top(self, **kwargs): local_opts = copy.deepcopy(self.dunder_opts) local_opts.update(kwargs) - with patch.dict(state.__opts__, local_opts), \ - patch.object(salt.state.State, '_gather_pillar', - MagicMock(return_value={})): + with patch.dict(state.__opts__, local_opts), patch.object( + salt.state.State, "_gather_pillar", MagicMock(return_value={}) + ): ret = state.show_top() # Lazy way of converting ordered dicts to regular dicts. We don't # care about dict ordering for these tests. return salt.utils.json.loads(salt.utils.json.dumps(ret)) def use_limited_base_top_file(self): - ''' + """ Overwrites the base top file so that it only contains sections for its own saltenv. - ''' - with salt.utils.files.fopen(self.base_top_file, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + """ + with salt.utils.files.fopen(self.base_top_file, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ base: '*': - base_base - ''')) + """ + ) + ) time.sleep(1) def test_merge_strategy_merge(self): - ''' + """ Base overrides everything - ''' - ret = self.show_top(top_file_merging_strategy='merge') + """ + ret = self.show_top(top_file_merging_strategy="merge") assert ret == { - 'base': ['base_base'], - 'foo': ['base_foo'], - 'bar': ['base_bar'], - 'baz': ['base_baz'], + "base": ["base_base"], + "foo": ["base_foo"], + "bar": ["base_bar"], + "baz": ["base_baz"], }, ret def test_merge_strategy_merge_limited_base(self): - ''' + """ Test with a "base" top file containing only a "base" section. The "baz" saltenv should not be in the return data because that env doesn't have its own top file and there will be no "baz" section in the "base" env's @@ -1390,193 +1447,185 @@ class TopFileMergingCase(TestCase, LoaderModuleMockMixin): Next, append a "baz" section to the rewritten top file and we should get results for that saltenv in the return data. - ''' + """ self.use_limited_base_top_file() - ret = self.show_top(top_file_merging_strategy='merge') + ret = self.show_top(top_file_merging_strategy="merge") assert ret == { - 'base': ['base_base'], - 'foo': ['foo_foo'], - 'bar': ['bar_bar'], + "base": ["base_base"], + "foo": ["foo_foo"], + "bar": ["bar_bar"], }, ret # Add a "baz" section - with salt.utils.files.fopen(self.base_top_file, 'a') as fp_: - fp_.write(textwrap.dedent('''\ + with salt.utils.files.fopen(self.base_top_file, "a") as fp_: + fp_.write( + textwrap.dedent( + """\ baz: '*': - base_baz - ''')) + """ + ) + ) - ret = self.show_top(top_file_merging_strategy='merge') + ret = self.show_top(top_file_merging_strategy="merge") assert ret == { - 'base': ['base_base'], - 'foo': ['foo_foo'], - 'bar': ['bar_bar'], - 'baz': ['base_baz'], + "base": ["base_base"], + "foo": ["foo_foo"], + "bar": ["bar_bar"], + "baz": ["base_baz"], }, ret def test_merge_strategy_merge_state_top_saltenv_base(self): - ''' + """ This tests with state_top_saltenv=base, which should pull states *only* from the base saltenv. - ''' - ret = self.show_top( - top_file_merging_strategy='merge', - state_top_saltenv='base') + """ + ret = self.show_top(top_file_merging_strategy="merge", state_top_saltenv="base") assert ret == { - 'base': ['base_base'], - 'foo': ['base_foo'], - 'bar': ['base_bar'], - 'baz': ['base_baz'], + "base": ["base_base"], + "foo": ["base_foo"], + "bar": ["base_bar"], + "baz": ["base_baz"], }, ret def test_merge_strategy_merge_state_top_saltenv_foo(self): - ''' + """ This tests with state_top_saltenv=foo, which should pull states *only* from the foo saltenv. Since that top file is only authoritative for its own saltenv, *only* the foo saltenv's matches from the foo top file should be in the return data. - ''' - ret = self.show_top( - top_file_merging_strategy='merge', - state_top_saltenv='foo') - assert ret == {'foo': ['foo_foo']}, ret + """ + ret = self.show_top(top_file_merging_strategy="merge", state_top_saltenv="foo") + assert ret == {"foo": ["foo_foo"]}, ret def test_merge_strategy_merge_all(self): - ''' + """ Include everything in every top file - ''' - ret = self.show_top(top_file_merging_strategy='merge_all') + """ + ret = self.show_top(top_file_merging_strategy="merge_all") assert ret == { - 'base': ['base_base', 'foo_base', 'bar_base'], - 'foo': ['base_foo', 'foo_foo', 'bar_foo'], - 'bar': ['base_bar', 'foo_bar', 'bar_bar'], - 'baz': ['base_baz', 'foo_baz', 'bar_baz'], + "base": ["base_base", "foo_base", "bar_base"], + "foo": ["base_foo", "foo_foo", "bar_foo"], + "bar": ["base_bar", "foo_bar", "bar_bar"], + "baz": ["base_baz", "foo_baz", "bar_baz"], }, ret def test_merge_strategy_merge_all_alternate_env_order(self): - ''' + """ Use an alternate env_order. This should change the order in which the SLS targets appear in the result. - ''' + """ ret = self.show_top( - top_file_merging_strategy='merge_all', - env_order=['bar', 'foo', 'base']) + top_file_merging_strategy="merge_all", env_order=["bar", "foo", "base"] + ) assert ret == { - 'base': ['bar_base', 'foo_base', 'base_base'], - 'foo': ['bar_foo', 'foo_foo', 'base_foo'], - 'bar': ['bar_bar', 'foo_bar', 'base_bar'], - 'baz': ['bar_baz', 'foo_baz', 'base_baz'], + "base": ["bar_base", "foo_base", "base_base"], + "foo": ["bar_foo", "foo_foo", "base_foo"], + "bar": ["bar_bar", "foo_bar", "base_bar"], + "baz": ["bar_baz", "foo_baz", "base_baz"], }, ret def test_merge_strategy_merge_all_state_top_saltenv_base(self): - ''' + """ This tests with state_top_saltenv=base, which should pull states *only* from the base saltenv. Since we are using the "merge_all" strategy, all the states from that top file should be in the return data. - ''' + """ ret = self.show_top( - top_file_merging_strategy='merge_all', - state_top_saltenv='base') + top_file_merging_strategy="merge_all", state_top_saltenv="base" + ) assert ret == { - 'base': ['base_base'], - 'foo': ['base_foo'], - 'bar': ['base_bar'], - 'baz': ['base_baz'], + "base": ["base_base"], + "foo": ["base_foo"], + "bar": ["base_bar"], + "baz": ["base_baz"], }, ret def test_merge_strategy_merge_all_state_top_saltenv_foo(self): - ''' + """ This tests with state_top_saltenv=foo, which should pull states *only* from the foo saltenv. Since we are using the "merge_all" strategy, all the states from that top file should be in the return data. - ''' + """ ret = self.show_top( - top_file_merging_strategy='merge_all', - state_top_saltenv='foo') + top_file_merging_strategy="merge_all", state_top_saltenv="foo" + ) assert ret == { - 'base': ['foo_base'], - 'foo': ['foo_foo'], - 'bar': ['foo_bar'], - 'baz': ['foo_baz'], + "base": ["foo_base"], + "foo": ["foo_foo"], + "bar": ["foo_bar"], + "baz": ["foo_baz"], }, ret def test_merge_strategy_same(self): - ''' + """ Each env should get its SLS targets from its own top file, with the "baz" env pulling from "base" since default_top=base and there is no top file in the "baz" saltenv. - ''' - ret = self.show_top(top_file_merging_strategy='same') + """ + ret = self.show_top(top_file_merging_strategy="same") assert ret == { - 'base': ['base_base'], - 'foo': ['foo_foo'], - 'bar': ['bar_bar'], - 'baz': ['base_baz'], + "base": ["base_base"], + "foo": ["foo_foo"], + "bar": ["bar_bar"], + "baz": ["base_baz"], }, ret def test_merge_strategy_same_limited_base(self): - ''' + """ Each env should get its SLS targets from its own top file, with the "baz" env pulling from "base" since default_top=base and there is no top file in the "baz" saltenv. - ''' + """ self.use_limited_base_top_file() - ret = self.show_top(top_file_merging_strategy='same') + ret = self.show_top(top_file_merging_strategy="same") assert ret == { - 'base': ['base_base'], - 'foo': ['foo_foo'], - 'bar': ['bar_bar'], + "base": ["base_base"], + "foo": ["foo_foo"], + "bar": ["bar_bar"], }, ret def test_merge_strategy_same_default_top_foo(self): - ''' + """ Each env should get its SLS targets from its own top file, with the "baz" env pulling from "foo" since default_top=foo and there is no top file in the "baz" saltenv. - ''' - ret = self.show_top( - top_file_merging_strategy='same', - default_top='foo') + """ + ret = self.show_top(top_file_merging_strategy="same", default_top="foo") assert ret == { - 'base': ['base_base'], - 'foo': ['foo_foo'], - 'bar': ['bar_bar'], - 'baz': ['foo_baz'], + "base": ["base_base"], + "foo": ["foo_foo"], + "bar": ["bar_bar"], + "baz": ["foo_baz"], }, ret def test_merge_strategy_same_state_top_saltenv_base(self): - ''' + """ Test the state_top_saltenv parameter to load states exclusively from the base saltenv, with the "same" merging strategy. This should result in just the base environment's states from the base top file being in the merged result. - ''' - ret = self.show_top( - top_file_merging_strategy='same', - state_top_saltenv='base') - assert ret == {'base': ['base_base']}, ret + """ + ret = self.show_top(top_file_merging_strategy="same", state_top_saltenv="base") + assert ret == {"base": ["base_base"]}, ret def test_merge_strategy_same_state_top_saltenv_foo(self): - ''' + """ Test the state_top_saltenv parameter to load states exclusively from the foo saltenv, with the "same" merging strategy. This should result in just the foo environment's states from the foo top file being in the merged result. - ''' - ret = self.show_top( - top_file_merging_strategy='same', - state_top_saltenv='foo') - assert ret == {'foo': ['foo_foo']}, ret + """ + ret = self.show_top(top_file_merging_strategy="same", state_top_saltenv="foo") + assert ret == {"foo": ["foo_foo"]}, ret def test_merge_strategy_same_state_top_saltenv_baz(self): - ''' + """ Test the state_top_saltenv parameter to load states exclusively from the baz saltenv, with the "same" merging strategy. This should result in an empty dictionary since there is no top file in that environment. - ''' - ret = self.show_top( - top_file_merging_strategy='same', - state_top_saltenv='baz') + """ + ret = self.show_top(top_file_merging_strategy="same", state_top_saltenv="baz") assert ret == {}, ret diff --git a/tests/unit/modules/test_status.py b/tests/unit/modules/test_status.py index 8f568d8e9a2..91c43948b2c 100644 --- a/tests/unit/modules/test_status.py +++ b/tests/unit/modules/test_status.py @@ -2,330 +2,378 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os +import salt.modules.status as status + # Import Salt Libs import salt.utils.platform -import salt.modules.status as status from salt.exceptions import CommandExecutionError from salt.ext import six # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - mock_open, -) class StatusTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ test modules.status functions - ''' + """ + def setup_loader_modules(self): return {status: {}} def _set_up_test_uptime(self): - ''' + """ Define common mock data for status.uptime tests - ''' + """ + class MockData(object): - ''' + """ Store mock data - ''' + """ m = MockData() m.now = 1477004312 m.ut = 1540154.00 m.idle = 3047777.32 m.ret = { - 'users': 3, - 'seconds': 1540154, - 'since_t': 1475464158, - 'days': 17, - 'since_iso': '2016-10-03T03:09:18', - 'time': '19:49', + "users": 3, + "seconds": 1540154, + "since_t": 1475464158, + "days": 17, + "since_iso": "2016-10-03T03:09:18", + "time": "19:49", } return m def _set_up_test_uptime_sunos(self): - ''' + """ Define common mock data for cmd.run_all for status.uptime on SunOS - ''' + """ + class MockData(object): - ''' + """ Store mock data - ''' + """ m = MockData() m.ret = { - 'retcode': 0, - 'stdout': 'unix:0:system_misc:boot_time 1475464158', + "retcode": 0, + "stdout": "unix:0:system_misc:boot_time 1475464158", } return m def test_uptime_linux(self): - ''' + """ Test modules.status.uptime function for Linux - ''' + """ m = self._set_up_test_uptime() - with patch.multiple(salt.utils.platform, - is_linux=MagicMock(return_value=True), - is_sunos=MagicMock(return_value=False), - is_darwin=MagicMock(return_value=False), - is_freebsd=MagicMock(return_value=False), - is_openbsd=MagicMock(return_value=False), - is_netbsd=MagicMock(return_value=False)), \ - patch('salt.utils.path.which', MagicMock(return_value=True)), \ - patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(['1', '2', '3']))}), \ - patch('time.time', MagicMock(return_value=m.now)), \ - patch('os.path.exists', MagicMock(return_value=True)): - proc_uptime = salt.utils.stringutils.to_str('{0} {1}'.format(m.ut, m.idle)) + with patch.multiple( + salt.utils.platform, + is_linux=MagicMock(return_value=True), + is_sunos=MagicMock(return_value=False), + is_darwin=MagicMock(return_value=False), + is_freebsd=MagicMock(return_value=False), + is_openbsd=MagicMock(return_value=False), + is_netbsd=MagicMock(return_value=False), + ), patch("salt.utils.path.which", MagicMock(return_value=True)), patch.dict( + status.__salt__, + {"cmd.run": MagicMock(return_value=os.linesep.join(["1", "2", "3"]))}, + ), patch( + "time.time", MagicMock(return_value=m.now) + ), patch( + "os.path.exists", MagicMock(return_value=True) + ): + proc_uptime = salt.utils.stringutils.to_str("{0} {1}".format(m.ut, m.idle)) - with patch('salt.utils.files.fopen', mock_open(read_data=proc_uptime)): + with patch("salt.utils.files.fopen", mock_open(read_data=proc_uptime)): ret = status.uptime() self.assertDictEqual(ret, m.ret) - with patch('os.path.exists', MagicMock(return_value=False)): + with patch("os.path.exists", MagicMock(return_value=False)): with self.assertRaises(CommandExecutionError): status.uptime() def test_uptime_sunos(self): - ''' + """ Test modules.status.uptime function for SunOS - ''' + """ m = self._set_up_test_uptime() m2 = self._set_up_test_uptime_sunos() - with patch.multiple(salt.utils.platform, - is_linux=MagicMock(return_value=False), - is_sunos=MagicMock(return_value=True), - is_darwin=MagicMock(return_value=False), - is_freebsd=MagicMock(return_value=False), - is_openbsd=MagicMock(return_value=False), - is_netbsd=MagicMock(return_value=False)), \ - patch('salt.utils.path.which', MagicMock(return_value=True)), \ - patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(['1', '2', '3'])), - 'cmd.run_all': MagicMock(return_value=m2.ret)}), \ - patch('time.time', MagicMock(return_value=m.now)): + with patch.multiple( + salt.utils.platform, + is_linux=MagicMock(return_value=False), + is_sunos=MagicMock(return_value=True), + is_darwin=MagicMock(return_value=False), + is_freebsd=MagicMock(return_value=False), + is_openbsd=MagicMock(return_value=False), + is_netbsd=MagicMock(return_value=False), + ), patch("salt.utils.path.which", MagicMock(return_value=True)), patch.dict( + status.__salt__, + { + "cmd.run": MagicMock(return_value=os.linesep.join(["1", "2", "3"])), + "cmd.run_all": MagicMock(return_value=m2.ret), + }, + ), patch( + "time.time", MagicMock(return_value=m.now) + ): ret = status.uptime() self.assertDictEqual(ret, m.ret) def test_uptime_macos(self): - ''' + """ Test modules.status.uptime function for macOS - ''' + """ m = self._set_up_test_uptime() - kern_boottime = ('{{ sec = {0}, usec = {1:0<6} }} Mon Oct 03 03:09:18.23 2016' - ''.format(*six.text_type(m.now - m.ut).split('.'))) - with patch.multiple(salt.utils.platform, - is_linux=MagicMock(return_value=False), - is_sunos=MagicMock(return_value=False), - is_darwin=MagicMock(return_value=True), - is_freebsd=MagicMock(return_value=False), - is_openbsd=MagicMock(return_value=False), - is_netbsd=MagicMock(return_value=False)), \ - patch('salt.utils.path.which', MagicMock(return_value=True)), \ - patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(['1', '2', '3'])), - 'sysctl.get': MagicMock(return_value=kern_boottime)}), \ - patch('time.time', MagicMock(return_value=m.now)): + kern_boottime = ( + "{{ sec = {0}, usec = {1:0<6} }} Mon Oct 03 03:09:18.23 2016" + "".format(*six.text_type(m.now - m.ut).split(".")) + ) + with patch.multiple( + salt.utils.platform, + is_linux=MagicMock(return_value=False), + is_sunos=MagicMock(return_value=False), + is_darwin=MagicMock(return_value=True), + is_freebsd=MagicMock(return_value=False), + is_openbsd=MagicMock(return_value=False), + is_netbsd=MagicMock(return_value=False), + ), patch("salt.utils.path.which", MagicMock(return_value=True)), patch.dict( + status.__salt__, + { + "cmd.run": MagicMock(return_value=os.linesep.join(["1", "2", "3"])), + "sysctl.get": MagicMock(return_value=kern_boottime), + }, + ), patch( + "time.time", MagicMock(return_value=m.now) + ): ret = status.uptime() self.assertDictEqual(ret, m.ret) - with patch.dict(status.__salt__, {'sysctl.get': MagicMock(return_value='')}): + with patch.dict( + status.__salt__, {"sysctl.get": MagicMock(return_value="")} + ): with self.assertRaises(CommandExecutionError): status.uptime() def test_uptime_return_success_not_supported(self): - ''' + """ Test modules.status.uptime function for other platforms - ''' - with patch.multiple(salt.utils.platform, - is_linux=MagicMock(return_value=False), - is_sunos=MagicMock(return_value=False), - is_darwin=MagicMock(return_value=False), - is_freebsd=MagicMock(return_value=False), - is_openbsd=MagicMock(return_value=False), - is_netbsd=MagicMock(return_value=False)): + """ + with patch.multiple( + salt.utils.platform, + is_linux=MagicMock(return_value=False), + is_sunos=MagicMock(return_value=False), + is_darwin=MagicMock(return_value=False), + is_freebsd=MagicMock(return_value=False), + is_openbsd=MagicMock(return_value=False), + is_netbsd=MagicMock(return_value=False), + ): exc_mock = MagicMock(side_effect=CommandExecutionError) with self.assertRaises(CommandExecutionError): - with patch.dict(status.__salt__, {'cmd.run': exc_mock}): + with patch.dict(status.__salt__, {"cmd.run": exc_mock}): status.uptime() def _set_up_test_cpustats_openbsd(self): - ''' + """ Define mock data for status.cpustats on OpenBSD - ''' + """ + class MockData(object): - ''' + """ Store mock data - ''' + """ m = MockData() m.ret = { - '0': { - 'User': '0.0%', - 'Nice': '0.0%', - 'System': '4.5%', - 'Interrupt': '0.5%', - 'Idle': '95.0%', + "0": { + "User": "0.0%", + "Nice": "0.0%", + "System": "4.5%", + "Interrupt": "0.5%", + "Idle": "95.0%", } } return m def test_cpustats_openbsd(self): - ''' + """ Test modules.status.cpustats function for OpenBSD - ''' + """ m = self._set_up_test_cpustats_openbsd() - systat = '\n' \ - '\n' \ - ' 1 users Load 0.20 0.07 0.05 salt.localdomain 09:42:42\n' \ - 'CPU User Nice System Interrupt Idle\n' \ - '0 0.0% 0.0% 4.5% 0.5% 95.0%\n' + systat = ( + "\n" + "\n" + " 1 users Load 0.20 0.07 0.05 salt.localdomain 09:42:42\n" + "CPU User Nice System Interrupt Idle\n" + "0 0.0% 0.0% 4.5% 0.5% 95.0%\n" + ) - with patch.multiple(salt.utils.platform, - is_linux=MagicMock(return_value=False), - is_sunos=MagicMock(return_value=False), - is_darwin=MagicMock(return_value=False), - is_freebsd=MagicMock(return_value=False), - is_openbsd=MagicMock(return_value=True), - is_netbsd=MagicMock(return_value=False)), \ - patch('salt.utils.path.which', MagicMock(return_value=True)), \ - patch.dict(status.__grains__, {'kernel': 'OpenBSD'}), \ - patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value=systat)}): + with patch.multiple( + salt.utils.platform, + is_linux=MagicMock(return_value=False), + is_sunos=MagicMock(return_value=False), + is_darwin=MagicMock(return_value=False), + is_freebsd=MagicMock(return_value=False), + is_openbsd=MagicMock(return_value=True), + is_netbsd=MagicMock(return_value=False), + ), patch("salt.utils.path.which", MagicMock(return_value=True)), patch.dict( + status.__grains__, {"kernel": "OpenBSD"} + ), patch.dict( + status.__salt__, {"cmd.run": MagicMock(return_value=systat)} + ): ret = status.cpustats() self.assertDictEqual(ret, m.ret) def _set_up_test_cpuinfo_bsd(self): class MockData(object): - ''' + """ Store mock data - ''' + """ m = MockData() m.ret = { - 'hw.model': 'Intel(R) Core(TM) i5-7287U CPU @ 3.30GHz', - 'hw.ncpu': '4', + "hw.model": "Intel(R) Core(TM) i5-7287U CPU @ 3.30GHz", + "hw.ncpu": "4", } return m def test_cpuinfo_freebsd(self): m = self._set_up_test_cpuinfo_bsd() - sysctl = 'hw.model:Intel(R) Core(TM) i5-7287U CPU @ 3.30GHz\nhw.ncpu:4' + sysctl = "hw.model:Intel(R) Core(TM) i5-7287U CPU @ 3.30GHz\nhw.ncpu:4" - with patch.dict(status.__grains__, {'kernel': 'FreeBSD'}): - with patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value=sysctl)}): + with patch.dict(status.__grains__, {"kernel": "FreeBSD"}): + with patch.dict( + status.__salt__, {"cmd.run": MagicMock(return_value=sysctl)} + ): ret = status.cpuinfo() self.assertDictEqual(ret, m.ret) def test_cpuinfo_openbsd(self): m = self._set_up_test_cpuinfo_bsd() - sysctl = 'hw.model=Intel(R) Core(TM) i5-7287U CPU @ 3.30GHz\nhw.ncpu=4' + sysctl = "hw.model=Intel(R) Core(TM) i5-7287U CPU @ 3.30GHz\nhw.ncpu=4" - for bsd in ['NetBSD', 'OpenBSD']: - with patch.dict(status.__grains__, {'kernel': bsd}): - with patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value=sysctl)}): + for bsd in ["NetBSD", "OpenBSD"]: + with patch.dict(status.__grains__, {"kernel": bsd}): + with patch.dict( + status.__salt__, {"cmd.run": MagicMock(return_value=sysctl)} + ): ret = status.cpuinfo() self.assertDictEqual(ret, m.ret) def _set_up_test_meminfo_openbsd(self): class MockData(object): - ''' + """ Store mock data - ''' + """ m = MockData() m.ret = { - 'active virtual pages': '355M', - 'free list size': '305M', - 'page faults': '845', - 'pages reclaimed': '1', - 'pages paged in': '2', - 'pages paged out': '3', - 'pages freed': '4', - 'pages scanned': '5' + "active virtual pages": "355M", + "free list size": "305M", + "page faults": "845", + "pages reclaimed": "1", + "pages paged in": "2", + "pages paged out": "3", + "pages freed": "4", + "pages scanned": "5", } return m def test_meminfo_openbsd(self): m = self._set_up_test_meminfo_openbsd() - vmstat = ' procs memory page disks traps cpu\n' \ - ' r s avm fre flt re pi po fr sr cd0 sd0 int sys cs us sy id\n' \ - ' 2 103 355M 305M 845 1 2 3 4 5 0 1 21 682 86 1 1 98' + vmstat = ( + " procs memory page disks traps cpu\n" + " r s avm fre flt re pi po fr sr cd0 sd0 int sys cs us sy id\n" + " 2 103 355M 305M 845 1 2 3 4 5 0 1 21 682 86 1 1 98" + ) - with patch.dict(status.__grains__, {'kernel': 'OpenBSD'}): - with patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value=vmstat)}): + with patch.dict(status.__grains__, {"kernel": "OpenBSD"}): + with patch.dict( + status.__salt__, {"cmd.run": MagicMock(return_value=vmstat)} + ): ret = status.meminfo() self.assertDictEqual(ret, m.ret) def _set_up_test_w_linux(self): - ''' + """ Define mock data for status.w on Linux - ''' + """ + class MockData(object): - ''' + """ Store mock data - ''' + """ m = MockData() - m.ret = [{ - 'idle': '0s', - 'jcpu': '0.24s', - 'login': '13:42', - 'pcpu': '0.16s', - 'tty': 'pts/1', - 'user': 'root', - 'what': 'nmap -sV 10.2.2.2', - }] + m.ret = [ + { + "idle": "0s", + "jcpu": "0.24s", + "login": "13:42", + "pcpu": "0.16s", + "tty": "pts/1", + "user": "root", + "what": "nmap -sV 10.2.2.2", + } + ] return m def _set_up_test_w_bsd(self): - ''' + """ Define mock data for status.w on Linux - ''' + """ + class MockData(object): - ''' + """ Store mock data - ''' + """ m = MockData() - m.ret = [{ - 'idle': '0', - 'from': '10.2.2.1', - 'login': '1:42PM', - 'tty': 'p1', - 'user': 'root', - 'what': 'nmap -sV 10.2.2.2', - }] + m.ret = [ + { + "idle": "0", + "from": "10.2.2.1", + "login": "1:42PM", + "tty": "p1", + "user": "root", + "what": "nmap -sV 10.2.2.2", + } + ] return m def test_w_linux(self): m = self._set_up_test_w_linux() - w_output = 'root pts/1 13:42 0s 0.24s 0.16s nmap -sV 10.2.2.2' + w_output = "root pts/1 13:42 0s 0.24s 0.16s nmap -sV 10.2.2.2" - with patch.dict(status.__grains__, {'kernel': 'Linux'}): - with patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value=w_output)}): + with patch.dict(status.__grains__, {"kernel": "Linux"}): + with patch.dict( + status.__salt__, {"cmd.run": MagicMock(return_value=w_output)} + ): ret = status.w() self.assertListEqual(ret, m.ret) def test_w_bsd(self): m = self._set_up_test_w_bsd() - w_output = 'root p1 10.2.2.1 1:42PM 0 nmap -sV 10.2.2.2' + w_output = "root p1 10.2.2.1 1:42PM 0 nmap -sV 10.2.2.2" - for bsd in ['Darwin', 'FreeBSD', 'OpenBSD']: - with patch.dict(status.__grains__, {'kernel': bsd}): - with patch.dict(status.__salt__, {'cmd.run': MagicMock(return_value=w_output)}): + for bsd in ["Darwin", "FreeBSD", "OpenBSD"]: + with patch.dict(status.__grains__, {"kernel": bsd}): + with patch.dict( + status.__salt__, {"cmd.run": MagicMock(return_value=w_output)} + ): ret = status.w() self.assertListEqual(ret, m.ret) diff --git a/tests/unit/modules/test_supervisord.py b/tests/unit/modules/test_supervisord.py index 05a1bdc5048..f590c6214cb 100644 --- a/tests/unit/modules/test_supervisord.py +++ b/tests/unit/modules/test_supervisord.py @@ -1,183 +1,206 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.supervisord as supervisord from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SupervisordTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.supervisord - ''' + """ + def setup_loader_modules(self): return {supervisord: {}} @staticmethod def _m_all(stdout=True): - ''' + """ Return value for cmd.run_all. - ''' - return MagicMock(return_value={'retcode': 0, 'stdout': stdout}) + """ + return MagicMock(return_value={"retcode": 0, "stdout": stdout}) @staticmethod def _m_bin(): - ''' + """ Return value for cmd.which_bin. - ''' - return MagicMock(return_value='/tmp/bin_env') + """ + return MagicMock(return_value="/tmp/bin_env") # 'start' function tests: 1 def test_start(self): - ''' + """ Tests if it start the named service. - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all(), - 'cmd.which_bin': self._m_bin()}): + """ + with patch.dict( + supervisord.__salt__, + {"cmd.run_all": self._m_all(), "cmd.which_bin": self._m_bin()}, + ): self.assertTrue(supervisord.start()) # 'restart' function tests: 1 def test_restart(self): - ''' + """ Tests if it restart the named service. - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all(), - 'cmd.which_bin': self._m_bin()}): + """ + with patch.dict( + supervisord.__salt__, + {"cmd.run_all": self._m_all(), "cmd.which_bin": self._m_bin()}, + ): self.assertTrue(supervisord.restart()) # 'stop' function tests: 1 def test_stop(self): - ''' + """ Tests if it stop the named service. - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all(), - 'cmd.which_bin': self._m_bin()}): + """ + with patch.dict( + supervisord.__salt__, + {"cmd.run_all": self._m_all(), "cmd.which_bin": self._m_bin()}, + ): self.assertTrue(supervisord.stop()) # 'add' function tests: 1 def test_add(self): - ''' + """ Tests if it activates any updates in config for process/group. - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all(), - 'cmd.which_bin': self._m_bin()}): - self.assertTrue(supervisord.add('salt')) + """ + with patch.dict( + supervisord.__salt__, + {"cmd.run_all": self._m_all(), "cmd.which_bin": self._m_bin()}, + ): + self.assertTrue(supervisord.add("salt")) # 'remove' function tests: 1 def test_remove(self): - ''' + """ Tests if it removes process/group from active config - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all(), - 'cmd.which_bin': self._m_bin()}): - self.assertTrue(supervisord.remove('salt')) + """ + with patch.dict( + supervisord.__salt__, + {"cmd.run_all": self._m_all(), "cmd.which_bin": self._m_bin()}, + ): + self.assertTrue(supervisord.remove("salt")) # 'reread' function tests: 1 def test_reread(self): - ''' + """ Tests if it reload the daemon's configuration files - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all(), - 'cmd.which_bin': self._m_bin()}): + """ + with patch.dict( + supervisord.__salt__, + {"cmd.run_all": self._m_all(), "cmd.which_bin": self._m_bin()}, + ): self.assertTrue(supervisord.reread()) # 'update' function tests: 1 def test_update(self): - ''' + """ Tests if it reload config and add/remove as necessary - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all(), - 'cmd.which_bin': self._m_bin()}): + """ + with patch.dict( + supervisord.__salt__, + {"cmd.run_all": self._m_all(), "cmd.which_bin": self._m_bin()}, + ): self.assertTrue(supervisord.update()) # 'status' function tests: 1 def test_status(self): - ''' + """ Tests if it list programs and its state - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all - ('salt running'), - 'cmd.which_bin': self._m_bin()}): - self.assertDictEqual(supervisord.status(), - {'salt': {'state': 'running', 'reason': ''}}) + """ + with patch.dict( + supervisord.__salt__, + { + "cmd.run_all": self._m_all("salt running"), + "cmd.which_bin": self._m_bin(), + }, + ): + self.assertDictEqual( + supervisord.status(), {"salt": {"state": "running", "reason": ""}} + ) # 'status_raw' function tests: 1 def test_status_raw(self): - ''' + """ Tests if it display the raw output of status - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all(), - 'cmd.which_bin': self._m_bin()}): + """ + with patch.dict( + supervisord.__salt__, + {"cmd.run_all": self._m_all(), "cmd.which_bin": self._m_bin()}, + ): self.assertTrue(supervisord.status_raw()) # 'custom' function tests: 1 def test_custom(self): - ''' + """ Tests if it run any custom supervisord command - ''' - with patch.dict(supervisord.__salt__, {'cmd.run_all': self._m_all(), - 'cmd.which_bin': self._m_bin()}): + """ + with patch.dict( + supervisord.__salt__, + {"cmd.run_all": self._m_all(), "cmd.which_bin": self._m_bin()}, + ): self.assertTrue(supervisord.custom("mstop '*gunicorn*'")) # 'options' function tests: 1 def test_options(self): - ''' + """ Tests if it read the config file and return the config options for a given process - ''' + """ + class MockConfig(object): - ''' + """ Mock Config class - ''' + """ + flag = None def __init__(self): self.name = None def sections(self): - ''' + """ Mock sections methos - ''' + """ if self.flag == 1: return [] - return ['program:salt'] + return ["program:salt"] def items(self, name): - ''' + """ Mock sections methos - ''' + """ self.name = name - return [('salt', 'True')] + return [("salt", "True")] - with patch.object(supervisord, '_read_config', - MagicMock(return_value=MockConfig())): + with patch.object( + supervisord, "_read_config", MagicMock(return_value=MockConfig()) + ): MockConfig.flag = 1 - self.assertRaises(CommandExecutionError, supervisord.options, - 'salt') + self.assertRaises(CommandExecutionError, supervisord.options, "salt") MockConfig.flag = 0 - self.assertDictEqual(supervisord.options('salt'), {'salt': True}) + self.assertDictEqual(supervisord.options("salt"), {"salt": True}) diff --git a/tests/unit/modules/test_svn.py b/tests/unit/modules/test_svn.py index a9c1796f81d..1878d89fdc0 100644 --- a/tests/unit/modules/test_svn.py +++ b/tests/unit/modules/test_svn.py @@ -1,117 +1,120 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.svn as svn +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SvnTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.svn - ''' + """ + def setup_loader_modules(self): return {svn: {}} def test_info(self): - ''' + """ Test to display the Subversion information from the checkout. - ''' - mock = MagicMock(side_effect=[{'retcode': 0, 'stdout': True}, - {'retcode': 0, 'stdout': 'A\n\nB'}, - {'retcode': 0, 'stdout': 'A\n\nB'}]) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.info('cwd', fmt='xml')) + """ + mock = MagicMock( + side_effect=[ + {"retcode": 0, "stdout": True}, + {"retcode": 0, "stdout": "A\n\nB"}, + {"retcode": 0, "stdout": "A\n\nB"}, + ] + ) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.info("cwd", fmt="xml")) - self.assertListEqual(svn.info('cwd', fmt='list'), [[], []]) + self.assertListEqual(svn.info("cwd", fmt="list"), [[], []]) - self.assertListEqual(svn.info('cwd', fmt='dict'), [{}, {}]) + self.assertListEqual(svn.info("cwd", fmt="dict"), [{}, {}]) def test_checkout(self): - ''' + """ Test to download a working copy of the remote Subversion repository directory or file - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': True}) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.checkout('cwd', 'remote')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": True}) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.checkout("cwd", "remote")) def test_switch(self): - ''' + """ Test to switch a working copy of a remote Subversion repository directory - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': True}) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.switch('cwd', 'remote')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": True}) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.switch("cwd", "remote")) def test_update(self): - ''' + """ Test to update the current directory, files, or directories from the remote Subversion repository - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': True}) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.update('cwd')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": True}) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.update("cwd")) def test_diff(self): - ''' + """ Test to return the diff of the current directory, files, or directories from the remote Subversion repository - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': True}) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.diff('cwd')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": True}) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.diff("cwd")) def test_commit(self): - ''' + """ Test to commit the current directory, files, or directories to the remote Subversion repository - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': True}) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.commit('cwd')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": True}) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.commit("cwd")) def test_add(self): - ''' + """ Test to add files to be tracked by the Subversion working-copy checkout - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': True}) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.add('cwd', False)) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": True}) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.add("cwd", False)) def test_remove(self): - ''' + """ Test to remove files and directories from the Subversion repository - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': True}) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.remove('cwd', False)) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": True}) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.remove("cwd", False)) def test_status(self): - ''' + """ Test to display the status of the current directory, files, or directories in the Subversion repository - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': True}) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.status('cwd')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": True}) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.status("cwd")) def test_export(self): - ''' + """ Test to create an unversioned copy of a tree. - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': True}) - with patch.dict(svn.__salt__, {'cmd.run_all': mock}): - self.assertTrue(svn.export('cwd', 'remote')) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": True}) + with patch.dict(svn.__salt__, {"cmd.run_all": mock}): + self.assertTrue(svn.export("cwd", "remote")) diff --git a/tests/unit/modules/test_swift.py b/tests/unit/modules/test_swift.py index 10b944555ea..550bcd946c0 100644 --- a/tests/unit/modules/test_swift.py +++ b/tests/unit/modules/test_swift.py @@ -1,67 +1,72 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.swift as swift +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase class SwiftTestCase(TestCase): - ''' + """ Test cases for salt.modules.swift - ''' + """ + # 'delete' function tests: 1 def test_delete(self): - ''' + """ Test for delete a container, or delete an object from a container. - ''' - with patch.object(swift, '_auth', MagicMock()): - self.assertTrue(swift.delete('mycontainer')) + """ + with patch.object(swift, "_auth", MagicMock()): + self.assertTrue(swift.delete("mycontainer")) - self.assertTrue(swift.delete('mycontainer', path='myfile.png')) + self.assertTrue(swift.delete("mycontainer", path="myfile.png")) # 'get' function tests: 1 def test_get(self): - ''' + """ Test for list the contents of a container, or return an object from a container. - ''' - with patch.object(swift, '_auth', MagicMock()): + """ + with patch.object(swift, "_auth", MagicMock()): self.assertTrue(swift.get()) - self.assertTrue(swift.get('mycontainer')) + self.assertTrue(swift.get("mycontainer")) - self.assertTrue(swift.get('mycontainer', path='myfile.png', - return_bin=True)) + self.assertTrue( + swift.get("mycontainer", path="myfile.png", return_bin=True) + ) - self.assertTrue(swift.get('mycontainer', path='myfile.png', - local_file='/tmp/myfile.png')) + self.assertTrue( + swift.get( + "mycontainer", path="myfile.png", local_file="/tmp/myfile.png" + ) + ) - self.assertFalse(swift.get('mycontainer', path='myfile.png')) + self.assertFalse(swift.get("mycontainer", path="myfile.png")) # 'put' function tests: 1 def test_put(self): - ''' + """ Test for create a new container, or upload an object to a container. - ''' - with patch.object(swift, '_auth', MagicMock()): - self.assertTrue(swift.put('mycontainer')) + """ + with patch.object(swift, "_auth", MagicMock()): + self.assertTrue(swift.put("mycontainer")) - self.assertTrue(swift.put('mycontainer', path='myfile.png', - local_file='/tmp/myfile.png')) + self.assertTrue( + swift.put( + "mycontainer", path="myfile.png", local_file="/tmp/myfile.png" + ) + ) - self.assertFalse(swift.put('mycontainer', path='myfile.png')) + self.assertFalse(swift.put("mycontainer", path="myfile.png")) diff --git a/tests/unit/modules/test_sysbench.py b/tests/unit/modules/test_sysbench.py index 8d6184de886..5994843db0a 100644 --- a/tests/unit/modules/test_sysbench.py +++ b/tests/unit/modules/test_sysbench.py @@ -1,116 +1,126 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.modules.sysbench as sysbench +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SysbenchTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases to salt.modules.sysbench - ''' + """ + def setup_loader_modules(self): return {sysbench: {}} def test_cpu(self): - ''' + """ Test to tests to the CPU performance of minions. - ''' - with patch.dict(sysbench.__salt__, - {'cmd.run': MagicMock(return_value={'A': 'a'})}): - with patch.object(sysbench, '_parser', return_value={'A': 'a'}): - self.assertEqual(sysbench.cpu(), - {'Prime numbers limit: 500': - {'A': 'a'}, 'Prime numbers limit: 5000': - {'A': 'a'}, 'Prime numbers limit: 2500': - {'A': 'a'}, 'Prime numbers limit: 1000': - {'A': 'a'}}) + """ + with patch.dict( + sysbench.__salt__, {"cmd.run": MagicMock(return_value={"A": "a"})} + ): + with patch.object(sysbench, "_parser", return_value={"A": "a"}): + self.assertEqual( + sysbench.cpu(), + { + "Prime numbers limit: 500": {"A": "a"}, + "Prime numbers limit: 5000": {"A": "a"}, + "Prime numbers limit: 2500": {"A": "a"}, + "Prime numbers limit: 1000": {"A": "a"}, + }, + ) def test_threads(self): - ''' + """ Test to this tests the performance of the processor's scheduler - ''' - with patch.dict(sysbench.__salt__, - {'cmd.run': MagicMock(return_value={'A': 'a'})}): - with patch.object(sysbench, '_parser', return_value={'A': 'a'}): - self.assertEqual(sysbench.threads(), - {'Yields: 500 Locks: 8': {'A': 'a'}, - 'Yields: 200 Locks: 4': {'A': 'a'}, - 'Yields: 1000 Locks: 16': {'A': 'a'}, - 'Yields: 100 Locks: 2': {'A': 'a'}}) + """ + with patch.dict( + sysbench.__salt__, {"cmd.run": MagicMock(return_value={"A": "a"})} + ): + with patch.object(sysbench, "_parser", return_value={"A": "a"}): + self.assertEqual( + sysbench.threads(), + { + "Yields: 500 Locks: 8": {"A": "a"}, + "Yields: 200 Locks: 4": {"A": "a"}, + "Yields: 1000 Locks: 16": {"A": "a"}, + "Yields: 100 Locks: 2": {"A": "a"}, + }, + ) def test_mutex(self): - ''' + """ Test to tests the implementation of mutex - ''' - with patch.dict(sysbench.__salt__, - {'cmd.run': MagicMock(return_value={'A': 'a'})}): - with patch.object(sysbench, '_parser', return_value={'A': 'a'}): - self.assertEqual(sysbench.mutex(), - {'Mutex: 1000 Locks: 25000 Loops: 10000': - {'A': 'a'}, - 'Mutex: 50 Locks: 10000 Loops: 2500': - {'A': 'a'}, - 'Mutex: 1000 Locks: 10000 Loops: 5000': - {'A': 'a'}, - 'Mutex: 500 Locks: 50000 Loops: 5000': - {'A': 'a'}, - 'Mutex: 500 Locks: 25000 Loops: 2500': - {'A': 'a'}, - 'Mutex: 500 Locks: 10000 Loops: 10000': - {'A': 'a'}, - 'Mutex: 50 Locks: 50000 Loops: 10000': - {'A': 'a'}, - 'Mutex: 1000 Locks: 50000 Loops: 2500': - {'A': 'a'}, - 'Mutex: 50 Locks: 25000 Loops: 5000': - {'A': 'a'}}) + """ + with patch.dict( + sysbench.__salt__, {"cmd.run": MagicMock(return_value={"A": "a"})} + ): + with patch.object(sysbench, "_parser", return_value={"A": "a"}): + self.assertEqual( + sysbench.mutex(), + { + "Mutex: 1000 Locks: 25000 Loops: 10000": {"A": "a"}, + "Mutex: 50 Locks: 10000 Loops: 2500": {"A": "a"}, + "Mutex: 1000 Locks: 10000 Loops: 5000": {"A": "a"}, + "Mutex: 500 Locks: 50000 Loops: 5000": {"A": "a"}, + "Mutex: 500 Locks: 25000 Loops: 2500": {"A": "a"}, + "Mutex: 500 Locks: 10000 Loops: 10000": {"A": "a"}, + "Mutex: 50 Locks: 50000 Loops: 10000": {"A": "a"}, + "Mutex: 1000 Locks: 50000 Loops: 2500": {"A": "a"}, + "Mutex: 50 Locks: 25000 Loops: 5000": {"A": "a"}, + }, + ) def test_memory(self): - ''' + """ Test to this tests the memory for read and write operations. - ''' - with patch.dict(sysbench.__salt__, - {'cmd.run': MagicMock(return_value={'A': 'a'})}): - with patch.object(sysbench, '_parser', return_value={'A': 'a'}): - self.assertEqual(sysbench.memory(), - {'Operation: read Scope: local': - {'A': 'a'}, - 'Operation: write Scope: local': - {'A': 'a'}, - 'Operation: read Scope: global': - {'A': 'a'}, - 'Operation: write Scope: global': - {'A': 'a'}}) + """ + with patch.dict( + sysbench.__salt__, {"cmd.run": MagicMock(return_value={"A": "a"})} + ): + with patch.object(sysbench, "_parser", return_value={"A": "a"}): + self.assertEqual( + sysbench.memory(), + { + "Operation: read Scope: local": {"A": "a"}, + "Operation: write Scope: local": {"A": "a"}, + "Operation: read Scope: global": {"A": "a"}, + "Operation: write Scope: global": {"A": "a"}, + }, + ) def test_fileio(self): - ''' + """ Test to this tests for the file read and write operations - ''' - with patch.dict(sysbench.__salt__, - {'cmd.run': MagicMock(return_value={'A': 'a'})}): - with patch.object(sysbench, '_parser', return_value={'A': 'a'}): - self.assertEqual(sysbench.fileio(), - {'Mode: seqrd': {'A': 'a'}, - 'Mode: seqwr': {'A': 'a'}, - 'Mode: rndrd': {'A': 'a'}, - 'Mode: rndwr': {'A': 'a'}, - 'Mode: seqrewr': {'A': 'a'}, - 'Mode: rndrw': {'A': 'a'}}) + """ + with patch.dict( + sysbench.__salt__, {"cmd.run": MagicMock(return_value={"A": "a"})} + ): + with patch.object(sysbench, "_parser", return_value={"A": "a"}): + self.assertEqual( + sysbench.fileio(), + { + "Mode: seqrd": {"A": "a"}, + "Mode: seqwr": {"A": "a"}, + "Mode: rndrd": {"A": "a"}, + "Mode: rndwr": {"A": "a"}, + "Mode: seqrewr": {"A": "a"}, + "Mode: rndrw": {"A": "a"}, + }, + ) def test_ping(self): - ''' + """ Test to ping - ''' + """ self.assertTrue(sysbench.ping()) diff --git a/tests/unit/modules/test_syslog_ng.py b/tests/unit/modules/test_syslog_ng.py index a68a32546b7..4bf669fdbdb 100644 --- a/tests/unit/modules/test_syslog_ng.py +++ b/tests/unit/modules/test_syslog_ng.py @@ -1,26 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" Test module for syslog_ng -''' +""" # Import Python modules -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os from textwrap import dedent -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch +import salt.modules.syslog_ng as syslog_ng # Import Salt libs import salt.utils.platform -import salt.modules.syslog_ng as syslog_ng + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf _VERSION = "3.6.0alpha0" -_MODULES = ("syslogformat,json-plugin,basicfuncs,afstomp,afsocket,cryptofuncs," - "afmongodb,dbparser,system-source,affile,pseudofile,afamqp," - "afsocket-notls,csvparser,linux-kmsg-format,afuser,confgen,afprog") +_MODULES = ( + "syslogformat,json-plugin,basicfuncs,afstomp,afsocket,cryptofuncs," + "afmongodb,dbparser,system-source,affile,pseudofile,afamqp," + "afsocket-notls,csvparser,linux-kmsg-format,afuser,confgen,afprog" +) VERSION_OUTPUT = """syslog-ng {0} Installer-Version: {0} @@ -34,7 +38,9 @@ Enable-Memtrace: off Enable-IPv6: on Enable-Spoof-Source: off Enable-TCP-Wrapper: off -Enable-Linux-Caps: off""".format(_VERSION, _MODULES) +Enable-Linux-Caps: off""".format( + _VERSION, _MODULES +) STATS_OUTPUT = """SourceName;SourceId;SourceInstance;State;Type;Number center;;received;a;processed;0 @@ -47,21 +53,21 @@ global;sdata_updates;;a;processed;0 global;msg_clones;;a;processed;0""" _SYSLOG_NG_NOT_INSTALLED_RETURN_VALUE = { - "retcode": -1, "stderr": - "Unable to execute the command 'syslog-ng'. It is not in the PATH." + "retcode": -1, + "stderr": "Unable to execute the command 'syslog-ng'. It is not in the PATH.", } _SYSLOG_NG_CTL_NOT_INSTALLED_RETURN_VALUE = { - "retcode": -1, "stderr": - "Unable to execute the command 'syslog-ng-ctl'. It is not in the PATH." + "retcode": -1, + "stderr": "Unable to execute the command 'syslog-ng-ctl'. It is not in the PATH.", } class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): # pylint: disable=blacklisted-function - orig_env = {str('PATH'): str('/foo:/bar')} - bin_dir = str('/baz') - mocked_env = {str('PATH'): str('/foo:/bar:/baz')} + orig_env = {str("PATH"): str("/foo:/bar")} + bin_dir = str("/baz") + mocked_env = {str("PATH"): str("/foo:/bar:/baz")} # pylint: enable=blacklisted-function def setup_loader_modules(self): @@ -70,26 +76,34 @@ class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): def test_statement_without_options(self): s = syslog_ng.Statement("source", "s_local", options=[]) b = s.build() - self.assertEqual(dedent( - """\ + self.assertEqual( + dedent( + """\ source s_local { }; - """), b) + """ + ), + b, + ) def test_non_empty_statement(self): o1 = syslog_ng.Option("file") o2 = syslog_ng.Option("tcp") s = syslog_ng.Statement("source", "s_local", options=[o1, o2]) b = s.build() - self.assertEqual(dedent( - """\ + self.assertEqual( + dedent( + """\ source s_local { file( ); tcp( ); }; - """), b) + """ + ), + b, + ) def test_option_with_parameters(self): o1 = syslog_ng.Option("file") @@ -102,41 +116,49 @@ class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): o1.add_parameter(p2) o1.add_parameter(p3) b = o1.build() - self.assertEqual(dedent( - """\ + self.assertEqual( + dedent( + """\ file( "/var/log/messages", "/var/log/syslog", tls( ) ); - """), b) + """ + ), + b, + ) def test_parameter_with_values(self): p = syslog_ng.TypedParameter() p.type = "tls" v1 = syslog_ng.TypedParameterValue() - v1.type = 'key_file' + v1.type = "key_file" v2 = syslog_ng.TypedParameterValue() - v2.type = 'cert_file' + v2.type = "cert_file" p.add_value(v1) p.add_value(v2) b = p.build() - self.assertEqual(dedent( - """\ + self.assertEqual( + dedent( + """\ tls( key_file( ), cert_file( ) - )"""), b) + )""" + ), + b, + ) def test_value_with_arguments(self): t = syslog_ng.TypedParameterValue() - t.type = 'key_file' + t.type = "key_file" a1 = syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"') a2 = syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"') @@ -145,33 +167,41 @@ class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): t.add_argument(a2) b = t.build() - self.assertEqual(dedent( - '''\ + self.assertEqual( + dedent( + """\ key_file( "/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key" "/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key" - )'''), b) + )""" + ), + b, + ) def test_end_to_end_statement_generation(self): - s = syslog_ng.Statement('source', 's_tls') + s = syslog_ng.Statement("source", "s_tls") - o = syslog_ng.Option('tcp') + o = syslog_ng.Option("tcp") - ip = syslog_ng.TypedParameter('ip') + ip = syslog_ng.TypedParameter("ip") ip.add_value(syslog_ng.SimpleParameterValue("'192.168.42.2'")) o.add_parameter(ip) - port = syslog_ng.TypedParameter('port') + port = syslog_ng.TypedParameter("port") port.add_value(syslog_ng.SimpleParameterValue(514)) o.add_parameter(port) - tls = syslog_ng.TypedParameter('tls') - key_file = syslog_ng.TypedParameterValue('key_file') - key_file.add_argument(syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"')) - cert_file = syslog_ng.TypedParameterValue('cert_file') - cert_file.add_argument(syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/cert.d/syslog-ng.cert"')) - peer_verify = syslog_ng.TypedParameterValue('peer_verify') - peer_verify.add_argument(syslog_ng.Argument('optional-untrusted')) + tls = syslog_ng.TypedParameter("tls") + key_file = syslog_ng.TypedParameterValue("key_file") + key_file.add_argument( + syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/key.d/syslog-ng.key"') + ) + cert_file = syslog_ng.TypedParameterValue("cert_file") + cert_file.add_argument( + syslog_ng.Argument('"/opt/syslog-ng/etc/syslog-ng/cert.d/syslog-ng.cert"') + ) + peer_verify = syslog_ng.TypedParameterValue("peer_verify") + peer_verify.add_argument(syslog_ng.Argument("optional-untrusted")) tls.add_value(key_file) tls.add_value(cert_file) tls.add_value(peer_verify) @@ -179,8 +209,9 @@ class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): s.add_child(o) b = s.build() - self.assertEqual(dedent( - '''\ + self.assertEqual( + dedent( + """\ source s_tls { tcp( ip( @@ -202,147 +233,126 @@ class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): ) ); }; - '''), b) + """ + ), + b, + ) - @skipIf(salt.utils.platform.is_windows(), 'Module not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "Module not available on Windows") def test_version(self): - cmd_ret = {'retcode': 0, 'stdout': VERSION_OUTPUT} - expected_output = {'retcode': 0, 'stdout': _VERSION} - cmd_args = ['syslog-ng', '-V'] + cmd_ret = {"retcode": 0, "stdout": VERSION_OUTPUT} + expected_output = {"retcode": 0, "stdout": _VERSION} + cmd_args = ["syslog-ng", "-V"] cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): result = syslog_ng.version() self.assertEqual(result, expected_output) - cmd_mock.assert_called_once_with( - cmd_args, - env=None, - python_shell=False - ) + cmd_mock.assert_called_once_with(cmd_args, env=None, python_shell=False) cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): result = syslog_ng.version(syslog_ng_sbin_dir=self.bin_dir) self.assertEqual(result, expected_output) cmd_mock.assert_called_once_with( - cmd_args, - env=self.mocked_env, - python_shell=False + cmd_args, env=self.mocked_env, python_shell=False ) - @skipIf(salt.utils.platform.is_windows(), 'Module not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "Module not available on Windows") def test_stats(self): - cmd_ret = {'retcode': 0, 'stdout': STATS_OUTPUT} - cmd_args = ['syslog-ng-ctl', 'stats'] + cmd_ret = {"retcode": 0, "stdout": STATS_OUTPUT} + cmd_args = ["syslog-ng-ctl", "stats"] cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): result = syslog_ng.stats() self.assertEqual(result, cmd_ret) - cmd_mock.assert_called_once_with( - cmd_args, - env=None, - python_shell=False - ) + cmd_mock.assert_called_once_with(cmd_args, env=None, python_shell=False) cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): result = syslog_ng.stats(syslog_ng_sbin_dir=self.bin_dir) self.assertEqual(result, cmd_ret) cmd_mock.assert_called_once_with( - cmd_args, - env=self.mocked_env, - python_shell=False + cmd_args, env=self.mocked_env, python_shell=False ) - @skipIf(salt.utils.platform.is_windows(), 'Module not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "Module not available on Windows") def test_modules(self): - cmd_ret = {'retcode': 0, 'stdout': VERSION_OUTPUT} - expected_output = {'retcode': 0, 'stdout': _MODULES} - cmd_args = ['syslog-ng', '-V'] + cmd_ret = {"retcode": 0, "stdout": VERSION_OUTPUT} + expected_output = {"retcode": 0, "stdout": _MODULES} + cmd_args = ["syslog-ng", "-V"] cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): result = syslog_ng.modules() self.assertEqual(result, expected_output) - cmd_mock.assert_called_once_with( - cmd_args, - env=None, - python_shell=False - ) + cmd_mock.assert_called_once_with(cmd_args, env=None, python_shell=False) cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): result = syslog_ng.modules(syslog_ng_sbin_dir=self.bin_dir) self.assertEqual(result, expected_output) cmd_mock.assert_called_once_with( - cmd_args, - env=self.mocked_env, - python_shell=False + cmd_args, env=self.mocked_env, python_shell=False ) - @skipIf(salt.utils.platform.is_windows(), 'Module not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "Module not available on Windows") def test_config_test(self): - cmd_ret = {'retcode': 0, 'stderr': '', 'stdout': 'Foo'} - cmd_args = ['syslog-ng', '--syntax-only'] + cmd_ret = {"retcode": 0, "stderr": "", "stdout": "Foo"} + cmd_args = ["syslog-ng", "--syntax-only"] cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): result = syslog_ng.config_test() self.assertEqual(result, cmd_ret) - cmd_mock.assert_called_once_with( - cmd_args, - env=None, - python_shell=False - ) + cmd_mock.assert_called_once_with(cmd_args, env=None, python_shell=False) cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): result = syslog_ng.config_test(syslog_ng_sbin_dir=self.bin_dir) self.assertEqual(result, cmd_ret) cmd_mock.assert_called_once_with( - cmd_args, - env=self.mocked_env, - python_shell=False + cmd_args, env=self.mocked_env, python_shell=False ) - @skipIf(salt.utils.platform.is_windows(), 'Module not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "Module not available on Windows") def test_config_test_cfgfile(self): - cfgfile = '/path/to/syslog-ng.conf' - cmd_ret = {'retcode': 1, 'stderr': 'Syntax error...', 'stdout': ''} - cmd_args = ['syslog-ng', '--syntax-only', - '--cfgfile={0}'.format(cfgfile)] + cfgfile = "/path/to/syslog-ng.conf" + cmd_ret = {"retcode": 1, "stderr": "Syntax error...", "stdout": ""} + cmd_args = ["syslog-ng", "--syntax-only", "--cfgfile={0}".format(cfgfile)] cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): self.assertEqual(syslog_ng.config_test(cfgfile=cfgfile), cmd_ret) - cmd_mock.assert_called_once_with( - cmd_args, - env=None, - python_shell=False - ) + cmd_mock.assert_called_once_with(cmd_args, env=None, python_shell=False) cmd_mock = MagicMock(return_value=cmd_ret) - with patch.dict(syslog_ng.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.dict(os.environ, self.orig_env): + with patch.dict(syslog_ng.__salt__, {"cmd.run_all": cmd_mock}), patch.dict( + os.environ, self.orig_env + ): self.assertEqual( - syslog_ng.config_test( - syslog_ng_sbin_dir=self.bin_dir, - cfgfile=cfgfile - ), - cmd_ret + syslog_ng.config_test(syslog_ng_sbin_dir=self.bin_dir, cfgfile=cfgfile), + cmd_ret, ) cmd_mock.assert_called_once_with( - cmd_args, - env=self.mocked_env, - python_shell=False + cmd_args, env=self.mocked_env, python_shell=False ) diff --git a/tests/unit/modules/test_sysmod.py b/tests/unit/modules/test_sysmod.py index 0b02ca49072..257bca27ff8 100644 --- a/tests/unit/modules/test_sysmod.py +++ b/tests/unit/modules/test_sysmod.py @@ -1,38 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.sysmod as sysmod +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class MockDocstringable(object): def __init__(self, docstr): self.__doc__ = docstr def set_module_docstring(self, docstr): - self.__globals__ = {'__doc__': docstr} + self.__globals__ = {"__doc__": docstr} class Mockstate(object): """ Mock of State """ + class State(object): """ Mock state functions """ + states = {} def __init__(self, opts): @@ -43,6 +43,7 @@ class Mockrunner(object): """ Mock of runner """ + class Runner(object): """ Mock runner functions @@ -60,6 +61,7 @@ class Mockloader(object): """ Mock of loader """ + functions = [] # ? does not have any effect on existing tests def __init__(self): @@ -75,28 +77,36 @@ class Mockloader(object): """ Mock renderers """ - return sysmod.__salt__ # renderers do not have '.'s; but whatever. This is for convenience + return ( + sysmod.__salt__ + ) # renderers do not have '.'s; but whatever. This is for convenience class SysmodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.sysmod - ''' + """ + def setup_loader_modules(self): - return {sysmod: {'__salt__': self.salt_dunder}} + return {sysmod: {"__salt__": self.salt_dunder}} @classmethod def setUpClass(cls): cls._modules = set() cls._functions = [ - 'exist.exist', - - 'sys.doc', 'sys.list_functions', 'sys.list_modules', - 'sysctl.get', 'sysctl.show', - 'system.halt', 'system.reboot', - - 'udev.name', 'udev.path', - 'user.add', 'user.info', 'user.rename', + "exist.exist", + "sys.doc", + "sys.list_functions", + "sys.list_modules", + "sysctl.get", + "sysctl.show", + "system.halt", + "system.reboot", + "udev.name", + "udev.path", + "user.add", + "user.info", + "user.rename", ] cls._docstrings = {} cls._statedocstrings = {} @@ -104,30 +114,31 @@ class SysmodTestCase(TestCase, LoaderModuleMockMixin): cls.salt_dunder = {} for func in cls._functions: - docstring = 'docstring for {0}'.format(func) + docstring = "docstring for {0}".format(func) cls.salt_dunder[func] = MockDocstringable(docstring) cls._docstrings[func] = docstring - module = func.split('.')[0] + module = func.split(".")[0] cls._statedocstrings[func] = docstring - cls._statedocstrings[module] = 'docstring for {0}'.format(module) + cls._statedocstrings[module] = "docstring for {0}".format(module) - cls._modules.add(func.split('.')[0]) + cls._modules.add(func.split(".")[0]) - docstring = 'docstring for {0}'.format(func) + docstring = "docstring for {0}".format(func) mock = MockDocstringable(docstring) - mock.set_module_docstring('docstring for {0}'.format(func.split('.')[0])) + mock.set_module_docstring("docstring for {0}".format(func.split(".")[0])) Mockstate.State.states[func] = mock cls._modules = sorted(list(cls._modules)) - cls.state_patcher = patch('salt.state', Mockstate()) + cls.state_patcher = patch("salt.state", Mockstate()) cls.state_patcher.start() - cls.runner_patcher = patch('salt.runner', Mockrunner()) + cls.runner_patcher = patch("salt.runner", Mockrunner()) cls.runner_patcher.start() - cls.loader_patcher = patch('salt.loader', Mockloader()) + cls.loader_patcher = patch("salt.loader", Mockloader()) cls.loader_patcher.start() + # 'doc' function tests: 2 @classmethod @@ -135,255 +146,352 @@ class SysmodTestCase(TestCase, LoaderModuleMockMixin): cls.runner_patcher.stop() cls.state_patcher.stop() cls.loader_patcher.stop() - for attrname in ('_modules', '_functions', '_docstrings', '_statedocstrings', 'salt_dunder', - 'runner_patcher', 'state_patcher', 'loader_patcher'): + for attrname in ( + "_modules", + "_functions", + "_docstrings", + "_statedocstrings", + "salt_dunder", + "runner_patcher", + "state_patcher", + "loader_patcher", + ): try: delattr(cls, attrname) except AttributeError: continue def test_doc(self): - ''' + """ Test if it returns the docstrings for all modules. - ''' + """ self.assertDictEqual(sysmod.doc(), self._docstrings) - self.assertDictEqual(sysmod.doc('sys.doc'), {'sys.doc': 'docstring for sys.doc'}) + self.assertDictEqual( + sysmod.doc("sys.doc"), {"sys.doc": "docstring for sys.doc"} + ) # 'state_doc' function tests: 2 def test_state_doc(self): - ''' + """ Test if it returns the docstrings for all states. - ''' + """ self.assertDictEqual(sysmod.state_doc(), self._statedocstrings) - self.assertDictEqual(sysmod.state_doc('sys.doc'), {'sys': 'docstring for sys', 'sys.doc': 'docstring for sys.doc'}) + self.assertDictEqual( + sysmod.state_doc("sys.doc"), + {"sys": "docstring for sys", "sys.doc": "docstring for sys.doc"}, + ) # 'runner_doc' function tests: 2 def test_runner_doc(self): - ''' + """ Test if it returns the docstrings for all runners. - ''' + """ self.assertDictEqual(sysmod.runner_doc(), self._docstrings) - self.assertDictEqual(sysmod.runner_doc('sys.doc'), {'sys.doc': 'docstring for sys.doc'}) + self.assertDictEqual( + sysmod.runner_doc("sys.doc"), {"sys.doc": "docstring for sys.doc"} + ) # 'returner_doc' function tests: 2 def test_returner_doc(self): - ''' + """ Test if it returns the docstrings for all returners. - ''' + """ self.assertDictEqual(sysmod.returner_doc(), self._docstrings) - self.assertDictEqual(sysmod.returner_doc('sys.doc'), {'sys.doc': 'docstring for sys.doc'}) + self.assertDictEqual( + sysmod.returner_doc("sys.doc"), {"sys.doc": "docstring for sys.doc"} + ) # 'renderer_doc' function tests: 2 def test_renderer_doc(self): - ''' + """ Test if it returns the docstrings for all renderers. - ''' + """ self.assertDictEqual(sysmod.renderer_doc(), self._docstrings) - self.assertDictEqual(sysmod.renderer_doc('sys.doc'), {'sys.doc': 'docstring for sys.doc'}) + self.assertDictEqual( + sysmod.renderer_doc("sys.doc"), {"sys.doc": "docstring for sys.doc"} + ) # 'list_functions' function tests: 7 def test_list_functions(self): - ''' + """ Test if it lists the functions for all modules. - ''' + """ self.assertListEqual(sysmod.list_functions(), self._functions) - self.assertListEqual(sysmod.list_functions('nonexist'), []) + self.assertListEqual(sysmod.list_functions("nonexist"), []) # list all functions in/given a specific module - self.assertListEqual(sysmod.list_functions('sys'), ['sys.doc', 'sys.list_functions', 'sys.list_modules']) + self.assertListEqual( + sysmod.list_functions("sys"), + ["sys.doc", "sys.list_functions", "sys.list_modules"], + ) # globs can be used for both module names and function names: - self.assertListEqual(sysmod.list_functions('sys*'), ['sys.doc', 'sys.list_functions', 'sys.list_modules', 'sysctl.get', 'sysctl.show', 'system.halt', 'system.reboot']) - self.assertListEqual(sysmod.list_functions('sys.list*'), ['sys.list_functions', 'sys.list_modules']) + self.assertListEqual( + sysmod.list_functions("sys*"), + [ + "sys.doc", + "sys.list_functions", + "sys.list_modules", + "sysctl.get", + "sysctl.show", + "system.halt", + "system.reboot", + ], + ) + self.assertListEqual( + sysmod.list_functions("sys.list*"), + ["sys.list_functions", "sys.list_modules"], + ) # "list", or check for a specific function: - self.assertListEqual(sysmod.list_functions('sys.list'), []) - self.assertListEqual(sysmod.list_functions('exist.exist'), ['exist.exist']) + self.assertListEqual(sysmod.list_functions("sys.list"), []) + self.assertListEqual(sysmod.list_functions("exist.exist"), ["exist.exist"]) # 'list_modules' function tests: 4 def test_list_modules(self): - ''' + """ Test if it lists the modules loaded on the minion - ''' + """ self.assertListEqual(sysmod.list_modules(), self._modules) - self.assertListEqual(sysmod.list_modules('nonexist'), []) + self.assertListEqual(sysmod.list_modules("nonexist"), []) - self.assertListEqual(sysmod.list_modules('user'), ['user']) + self.assertListEqual(sysmod.list_modules("user"), ["user"]) - self.assertListEqual(sysmod.list_modules('s*'), ['sys', 'sysctl', 'system']) + self.assertListEqual(sysmod.list_modules("s*"), ["sys", "sysctl", "system"]) # 'reload_modules' function tests: 1 def test_reload_modules(self): - ''' + """ Test if it tell the minion to reload the execution modules - ''' + """ self.assertTrue(sysmod.reload_modules()) # 'argspec' function tests: 1 def test_argspec(self): - ''' + """ Test if it return the argument specification of functions in Salt execution modules. - ''' + """ self.assertDictEqual(sysmod.argspec(), {}) # 'state_argspec' function tests: 1 def test_state_argspec(self): - ''' + """ Test if it return the argument specification of functions in Salt state modules. - ''' + """ self.assertDictEqual(sysmod.state_argspec(), {}) # 'returner_argspec' function tests: 1 def test_returner_argspec(self): - ''' + """ Test if it return the argument specification of functions in Salt returner modules. - ''' + """ self.assertDictEqual(sysmod.returner_argspec(), {}) # 'runner_argspec' function tests: 1 def test_runner_argspec(self): - ''' + """ Test if it return the argument specification of functions in Salt runner modules. - ''' + """ self.assertDictEqual(sysmod.runner_argspec(), {}) # 'list_state_functions' function tests: 7 def test_list_state_functions(self): - ''' + """ Test if it lists the functions for all state modules. - ''' + """ self.assertListEqual(sysmod.list_state_functions(), self._functions) - self.assertListEqual(sysmod.list_state_functions('nonexist'), []) + self.assertListEqual(sysmod.list_state_functions("nonexist"), []) # list all functions in/given a specific module - self.assertListEqual(sysmod.list_state_functions('sys'), ['sys.doc', 'sys.list_functions', 'sys.list_modules']) + self.assertListEqual( + sysmod.list_state_functions("sys"), + ["sys.doc", "sys.list_functions", "sys.list_modules"], + ) # globs can be used for both module names and function names: - self.assertListEqual(sysmod.list_state_functions('sys*'), ['sys.doc', 'sys.list_functions', 'sys.list_modules', 'sysctl.get', 'sysctl.show', 'system.halt', 'system.reboot']) - self.assertListEqual(sysmod.list_state_functions('sys.list*'), ['sys.list_functions', 'sys.list_modules']) + self.assertListEqual( + sysmod.list_state_functions("sys*"), + [ + "sys.doc", + "sys.list_functions", + "sys.list_modules", + "sysctl.get", + "sysctl.show", + "system.halt", + "system.reboot", + ], + ) + self.assertListEqual( + sysmod.list_state_functions("sys.list*"), + ["sys.list_functions", "sys.list_modules"], + ) # "list", or check for a specific function: - self.assertListEqual(sysmod.list_state_functions('sys.list'), []) - self.assertListEqual(sysmod.list_state_functions('exist.exist'), ['exist.exist']) + self.assertListEqual(sysmod.list_state_functions("sys.list"), []) + self.assertListEqual( + sysmod.list_state_functions("exist.exist"), ["exist.exist"] + ) # 'list_state_modules' function tests: 4 def test_list_state_modules(self): - ''' + """ Test if it lists the modules loaded on the minion. - ''' + """ self.assertListEqual(sysmod.list_state_modules(), self._modules) - self.assertListEqual(sysmod.list_state_modules('nonexist'), []) + self.assertListEqual(sysmod.list_state_modules("nonexist"), []) - self.assertListEqual(sysmod.list_state_modules('user'), ['user']) + self.assertListEqual(sysmod.list_state_modules("user"), ["user"]) - self.assertListEqual(sysmod.list_state_modules('s*'), ['sys', 'sysctl', 'system']) + self.assertListEqual( + sysmod.list_state_modules("s*"), ["sys", "sysctl", "system"] + ) # 'list_runners' function tests: 4 def test_list_runners(self): - ''' + """ Test if it list the runners loaded on the minion. - ''' + """ self.assertListEqual(sysmod.list_runners(), self._modules) - self.assertListEqual(sysmod.list_runners('nonexist'), []) + self.assertListEqual(sysmod.list_runners("nonexist"), []) - self.assertListEqual(sysmod.list_runners('user'), ['user']) + self.assertListEqual(sysmod.list_runners("user"), ["user"]) - self.assertListEqual(sysmod.list_runners('s*'), ['sys', 'sysctl', 'system']) + self.assertListEqual(sysmod.list_runners("s*"), ["sys", "sysctl", "system"]) # 'list_runner_functions' function tests: 7 def test_list_runner_functions(self): - ''' + """ Test if it lists the functions for all runner modules. - ''' + """ self.assertListEqual(sysmod.list_runner_functions(), self._functions) - self.assertListEqual(sysmod.list_runner_functions('nonexist'), []) + self.assertListEqual(sysmod.list_runner_functions("nonexist"), []) # list all functions in/given a specific module - self.assertListEqual(sysmod.list_runner_functions('sys'), ['sys.doc', 'sys.list_functions', 'sys.list_modules']) + self.assertListEqual( + sysmod.list_runner_functions("sys"), + ["sys.doc", "sys.list_functions", "sys.list_modules"], + ) # globs can be used for both module names and function names: - self.assertListEqual(sysmod.list_runner_functions('sys*'), ['sys.doc', 'sys.list_functions', 'sys.list_modules', 'sysctl.get', 'sysctl.show', 'system.halt', 'system.reboot']) - self.assertListEqual(sysmod.list_runner_functions('sys.list*'), ['sys.list_functions', 'sys.list_modules']) + self.assertListEqual( + sysmod.list_runner_functions("sys*"), + [ + "sys.doc", + "sys.list_functions", + "sys.list_modules", + "sysctl.get", + "sysctl.show", + "system.halt", + "system.reboot", + ], + ) + self.assertListEqual( + sysmod.list_runner_functions("sys.list*"), + ["sys.list_functions", "sys.list_modules"], + ) # "list", or check for a specific function: - self.assertListEqual(sysmod.list_runner_functions('sys.list'), []) - self.assertListEqual(sysmod.list_runner_functions('exist.exist'), ['exist.exist']) + self.assertListEqual(sysmod.list_runner_functions("sys.list"), []) + self.assertListEqual( + sysmod.list_runner_functions("exist.exist"), ["exist.exist"] + ) # 'list_returners' function tests: 4 def test_list_returners(self): - ''' + """ Test if it lists the returners loaded on the minion - ''' + """ self.assertListEqual(sysmod.list_returners(), self._modules) - self.assertListEqual(sysmod.list_returners('nonexist'), []) + self.assertListEqual(sysmod.list_returners("nonexist"), []) - self.assertListEqual(sysmod.list_returners('user'), ['user']) + self.assertListEqual(sysmod.list_returners("user"), ["user"]) - self.assertListEqual(sysmod.list_returners('s*'), ['sys', 'sysctl', 'system']) + self.assertListEqual(sysmod.list_returners("s*"), ["sys", "sysctl", "system"]) # 'list_returner_functions' function tests: 7 def test_list_returner_functions(self): - ''' + """ Test if it lists the functions for all returner modules. - ''' + """ self.assertListEqual(sysmod.list_returner_functions(), self._functions) - self.assertListEqual(sysmod.list_returner_functions('nonexist'), []) + self.assertListEqual(sysmod.list_returner_functions("nonexist"), []) # list all functions in/given a specific module - self.assertListEqual(sysmod.list_returner_functions('sys'), ['sys.doc', 'sys.list_functions', 'sys.list_modules']) + self.assertListEqual( + sysmod.list_returner_functions("sys"), + ["sys.doc", "sys.list_functions", "sys.list_modules"], + ) # globs can be used for both module names and function names: - self.assertListEqual(sysmod.list_returner_functions('sys*'), ['sys.doc', 'sys.list_functions', 'sys.list_modules', 'sysctl.get', 'sysctl.show', 'system.halt', 'system.reboot']) - self.assertListEqual(sysmod.list_returner_functions('sys.list*'), ['sys.list_functions', 'sys.list_modules']) + self.assertListEqual( + sysmod.list_returner_functions("sys*"), + [ + "sys.doc", + "sys.list_functions", + "sys.list_modules", + "sysctl.get", + "sysctl.show", + "system.halt", + "system.reboot", + ], + ) + self.assertListEqual( + sysmod.list_returner_functions("sys.list*"), + ["sys.list_functions", "sys.list_modules"], + ) # "list", or check for a specific function: - self.assertListEqual(sysmod.list_returner_functions('sys.list'), []) - self.assertListEqual(sysmod.list_returner_functions('exist.exist'), ['exist.exist']) + self.assertListEqual(sysmod.list_returner_functions("sys.list"), []) + self.assertListEqual( + sysmod.list_returner_functions("exist.exist"), ["exist.exist"] + ) # 'list_renderers' function tests: 4 def test_list_renderers(self): - ''' + """ Test if it list the renderers loaded on the minion. - ''' + """ self.assertListEqual(sysmod.list_renderers(), self._functions) - self.assertListEqual(sysmod.list_renderers('nonexist'), []) + self.assertListEqual(sysmod.list_renderers("nonexist"), []) - self.assertListEqual(sysmod.list_renderers('user.info'), ['user.info']) + self.assertListEqual(sysmod.list_renderers("user.info"), ["user.info"]) - self.assertListEqual(sysmod.list_renderers('syst*'), ['system.halt', 'system.reboot']) + self.assertListEqual( + sysmod.list_renderers("syst*"), ["system.halt", "system.reboot"] + ) diff --git a/tests/unit/modules/test_system.py b/tests/unit/modules/test_system.py index 3c21c125a59..1b4879c014f 100644 --- a/tests/unit/modules/test_system.py +++ b/tests/unit/modules/test_system.py @@ -1,114 +1,114 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.system as system +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SystemTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.system - ''' + """ + def setup_loader_modules(self): return {system: {}} def test_halt(self): - ''' + """ Test to halt a running system - ''' - with patch.dict(system.__salt__, - {'cmd.run': MagicMock(return_value='A')}): - self.assertEqual(system.halt(), 'A') + """ + with patch.dict(system.__salt__, {"cmd.run": MagicMock(return_value="A")}): + self.assertEqual(system.halt(), "A") def test_init(self): - ''' + """ Test to change the system runlevel on sysV compatible systems - ''' - with patch.dict(system.__salt__, - {'cmd.run': MagicMock(return_value='A')}): - self.assertEqual(system.init('r'), 'A') + """ + with patch.dict(system.__salt__, {"cmd.run": MagicMock(return_value="A")}): + self.assertEqual(system.init("r"), "A") def test_poweroff(self): - ''' + """ Test to poweroff a running system - ''' - with patch.dict(system.__salt__, - {'cmd.run': MagicMock(return_value='A')}): - self.assertEqual(system.poweroff(), 'A') + """ + with patch.dict(system.__salt__, {"cmd.run": MagicMock(return_value="A")}): + self.assertEqual(system.poweroff(), "A") def test_reboot(self): - ''' + """ Test to reboot the system with shutdown -r - ''' - cmd_mock = MagicMock(return_value='A') - with patch.dict(system.__salt__, {'cmd.run': cmd_mock}): - self.assertEqual(system.reboot(), 'A') - cmd_mock.assert_called_with(['shutdown', '-r', 'now'], python_shell=False) + """ + cmd_mock = MagicMock(return_value="A") + with patch.dict(system.__salt__, {"cmd.run": cmd_mock}): + self.assertEqual(system.reboot(), "A") + cmd_mock.assert_called_with(["shutdown", "-r", "now"], python_shell=False) def test_reboot_with_delay(self): - ''' + """ Test to reboot the system using shutdown -r with a delay - ''' - cmd_mock = MagicMock(return_value='A') - with patch.dict(system.__salt__, {'cmd.run': cmd_mock}): - self.assertEqual(system.reboot(at_time=5), 'A') - cmd_mock.assert_called_with(['shutdown', '-r', '5'], python_shell=False) + """ + cmd_mock = MagicMock(return_value="A") + with patch.dict(system.__salt__, {"cmd.run": cmd_mock}): + self.assertEqual(system.reboot(at_time=5), "A") + cmd_mock.assert_called_with(["shutdown", "-r", "5"], python_shell=False) def test_shutdown(self): - ''' + """ Test to shutdown a running system - ''' - cmd_mock = MagicMock(return_value='A') - with patch.dict(system.__salt__, {'cmd.run': cmd_mock}), \ - patch('salt.utils.platform.is_freebsd', MagicMock(return_value=False)), \ - patch('salt.utils.platform.is_netbsd', MagicMock(return_value=False)), \ - patch('salt.utils.platform.is_openbsd', MagicMock(return_value=False)): - self.assertEqual(system.shutdown(), 'A') - cmd_mock.assert_called_with(['shutdown', '-h', 'now'], python_shell=False) + """ + cmd_mock = MagicMock(return_value="A") + with patch.dict(system.__salt__, {"cmd.run": cmd_mock}), patch( + "salt.utils.platform.is_freebsd", MagicMock(return_value=False) + ), patch("salt.utils.platform.is_netbsd", MagicMock(return_value=False)), patch( + "salt.utils.platform.is_openbsd", MagicMock(return_value=False) + ): + self.assertEqual(system.shutdown(), "A") + cmd_mock.assert_called_with(["shutdown", "-h", "now"], python_shell=False) def test_shutdown_freebsd(self): - ''' + """ Test to shutdown a running FreeBSD system - ''' - cmd_mock = MagicMock(return_value='A') - with patch.dict(system.__salt__, {'cmd.run': cmd_mock}), \ - patch('salt.utils.platform.is_freebsd', MagicMock(return_value=True)), \ - patch('salt.utils.platform.is_netbsd', MagicMock(return_value=False)), \ - patch('salt.utils.platform.is_openbsd', MagicMock(return_value=False)): - self.assertEqual(system.shutdown(), 'A') - cmd_mock.assert_called_with(['shutdown', '-p', 'now'], python_shell=False) + """ + cmd_mock = MagicMock(return_value="A") + with patch.dict(system.__salt__, {"cmd.run": cmd_mock}), patch( + "salt.utils.platform.is_freebsd", MagicMock(return_value=True) + ), patch("salt.utils.platform.is_netbsd", MagicMock(return_value=False)), patch( + "salt.utils.platform.is_openbsd", MagicMock(return_value=False) + ): + self.assertEqual(system.shutdown(), "A") + cmd_mock.assert_called_with(["shutdown", "-p", "now"], python_shell=False) def test_shutdown_netbsd(self): - ''' + """ Test to shutdown a running NetBSD system - ''' - cmd_mock = MagicMock(return_value='A') - with patch.dict(system.__salt__, {'cmd.run': cmd_mock}), \ - patch('salt.utils.platform.is_freebsd', MagicMock(return_value=False)), \ - patch('salt.utils.platform.is_netbsd', MagicMock(return_value=True)), \ - patch('salt.utils.platform.is_openbsd', MagicMock(return_value=False)): - self.assertEqual(system.shutdown(), 'A') - cmd_mock.assert_called_with(['shutdown', '-p', 'now'], python_shell=False) + """ + cmd_mock = MagicMock(return_value="A") + with patch.dict(system.__salt__, {"cmd.run": cmd_mock}), patch( + "salt.utils.platform.is_freebsd", MagicMock(return_value=False) + ), patch("salt.utils.platform.is_netbsd", MagicMock(return_value=True)), patch( + "salt.utils.platform.is_openbsd", MagicMock(return_value=False) + ): + self.assertEqual(system.shutdown(), "A") + cmd_mock.assert_called_with(["shutdown", "-p", "now"], python_shell=False) def test_shutdown_openbsd(self): - ''' + """ Test to shutdown a running OpenBSD system - ''' - cmd_mock = MagicMock(return_value='A') - with patch.dict(system.__salt__, {'cmd.run': cmd_mock}), \ - patch('salt.utils.platform.is_freebsd', MagicMock(return_value=False)), \ - patch('salt.utils.platform.is_netbsd', MagicMock(return_value=False)), \ - patch('salt.utils.platform.is_openbsd', MagicMock(return_value=True)): - self.assertEqual(system.shutdown(), 'A') - cmd_mock.assert_called_with(['shutdown', '-p', 'now'], python_shell=False) + """ + cmd_mock = MagicMock(return_value="A") + with patch.dict(system.__salt__, {"cmd.run": cmd_mock}), patch( + "salt.utils.platform.is_freebsd", MagicMock(return_value=False) + ), patch("salt.utils.platform.is_netbsd", MagicMock(return_value=False)), patch( + "salt.utils.platform.is_openbsd", MagicMock(return_value=True) + ): + self.assertEqual(system.shutdown(), "A") + cmd_mock.assert_called_with(["shutdown", "-p", "now"], python_shell=False) diff --git a/tests/unit/modules/test_systemd_service.py b/tests/unit/modules/test_systemd_service.py index 13ddc394bed..9aef59084ec 100644 --- a/tests/unit/modules/test_systemd_service.py +++ b/tests/unit/modules/test_systemd_service.py @@ -1,130 +1,120 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch, -) +import pytest # Import Salt Libs import salt.modules.systemd_service as systemd import salt.utils.systemd from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase + _SYSTEMCTL_STATUS = { - 'sshd.service': { - 'stdout': '''\ + "sshd.service": { + "stdout": """\ * sshd.service - OpenSSH Daemon Loaded: loaded (/usr/lib/systemd/system/sshd.service; disabled; vendor preset: disabled) - Active: inactive (dead)''', - 'stderr': '', - 'retcode': 3, - 'pid': 12345, + Active: inactive (dead)""", + "stderr": "", + "retcode": 3, + "pid": 12345, }, - - 'foo.service': { - 'stdout': '''\ + "foo.service": { + "stdout": """\ * foo.service Loaded: not-found (Reason: No such file or directory) - Active: inactive (dead)''', - 'stderr': '', - 'retcode': 3, - 'pid': 12345, + Active: inactive (dead)""", + "stderr": "", + "retcode": 3, + "pid": 12345, }, } # This reflects systemd >= 231 behavior _SYSTEMCTL_STATUS_GTE_231 = { - 'bar.service': { - 'stdout': 'Unit bar.service could not be found.', - 'stderr': '', - 'retcode': 4, - 'pid': 12345, + "bar.service": { + "stdout": "Unit bar.service could not be found.", + "stderr": "", + "retcode": 4, + "pid": 12345, }, } -_LIST_UNIT_FILES = '''\ +_LIST_UNIT_FILES = """\ service1.service enabled service2.service disabled service3.service static timer1.timer enabled timer2.timer disabled -timer3.timer static''' +timer3.timer static""" class SystemdTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.modules.systemd - ''' + """ + def setup_loader_modules(self): return {systemd: {}} def test_systemctl_reload(self): - ''' + """ Test to Reloads systemctl - ''' - mock = MagicMock(side_effect=[ - {'stdout': 'Who knows why?', - 'stderr': '', - 'retcode': 1, - 'pid': 12345}, - {'stdout': '', - 'stderr': '', - 'retcode': 0, - 'pid': 54321}, - ]) - with patch.dict(systemd.__salt__, {'cmd.run_all': mock}): + """ + mock = MagicMock( + side_effect=[ + {"stdout": "Who knows why?", "stderr": "", "retcode": 1, "pid": 12345}, + {"stdout": "", "stderr": "", "retcode": 0, "pid": 54321}, + ] + ) + with patch.dict(systemd.__salt__, {"cmd.run_all": mock}): self.assertRaisesRegex( CommandExecutionError, - 'Problem performing systemctl daemon-reload: Who knows why?', - systemd.systemctl_reload + "Problem performing systemctl daemon-reload: Who knows why?", + systemd.systemctl_reload, ) self.assertTrue(systemd.systemctl_reload()) def test_get_enabled(self): - ''' + """ Test to return a list of all enabled services - ''' + """ cmd_mock = MagicMock(return_value=_LIST_UNIT_FILES) - listdir_mock = MagicMock(return_value=['foo', 'bar', 'baz', 'README']) + listdir_mock = MagicMock(return_value=["foo", "bar", "baz", "README"]) sd_mock = MagicMock( - return_value=set( - [x.replace('.service', '') for x in _SYSTEMCTL_STATUS] - ) + return_value=set([x.replace(".service", "") for x in _SYSTEMCTL_STATUS]) ) access_mock = MagicMock( - side_effect=lambda x, y: x != os.path.join( - systemd.INITSCRIPT_PATH, - 'README' - ) + side_effect=lambda x, y: x + != os.path.join(systemd.INITSCRIPT_PATH, "README") ) - sysv_enabled_mock = MagicMock(side_effect=lambda x, _: x == 'baz') + sysv_enabled_mock = MagicMock(side_effect=lambda x, _: x == "baz") - with patch.dict(systemd.__salt__, {'cmd.run': cmd_mock}): - with patch.object(os, 'listdir', listdir_mock): - with patch.object(systemd, '_get_systemd_services', sd_mock): - with patch.object(os, 'access', side_effect=access_mock): - with patch.object(systemd, '_sysv_enabled', - sysv_enabled_mock): + with patch.dict(systemd.__salt__, {"cmd.run": cmd_mock}): + with patch.object(os, "listdir", listdir_mock): + with patch.object(systemd, "_get_systemd_services", sd_mock): + with patch.object(os, "access", side_effect=access_mock): + with patch.object(systemd, "_sysv_enabled", sysv_enabled_mock): self.assertListEqual( systemd.get_enabled(), - ['baz', 'service1', 'timer1.timer'] + ["baz", "service1", "timer1.timer"], ) def test_get_disabled(self): - ''' + """ Test to return a list of all disabled services - ''' + """ cmd_mock = MagicMock(return_value=_LIST_UNIT_FILES) # 'foo' should collide with the systemd services (as returned by # sd_mock) and thus not be returned by _get_sysv_services(). It doesn't @@ -132,164 +122,150 @@ class SystemdTestCase(TestCase, LoaderModuleMockMixin): # want to ensure that 'foo' isn't identified as a disabled initscript # even though below we are mocking it to show as not enabled (since # only 'baz' will be considered an enabled sysv service). - listdir_mock = MagicMock(return_value=['foo', 'bar', 'baz', 'README']) + listdir_mock = MagicMock(return_value=["foo", "bar", "baz", "README"]) sd_mock = MagicMock( - return_value=set( - [x.replace('.service', '') for x in _SYSTEMCTL_STATUS] - ) + return_value=set([x.replace(".service", "") for x in _SYSTEMCTL_STATUS]) ) access_mock = MagicMock( - side_effect=lambda x, y: x != os.path.join( - systemd.INITSCRIPT_PATH, - 'README' - ) + side_effect=lambda x, y: x + != os.path.join(systemd.INITSCRIPT_PATH, "README") ) - sysv_enabled_mock = MagicMock(side_effect=lambda x, _: x == 'baz') + sysv_enabled_mock = MagicMock(side_effect=lambda x, _: x == "baz") - with patch.dict(systemd.__salt__, {'cmd.run': cmd_mock}): - with patch.object(os, 'listdir', listdir_mock): - with patch.object(systemd, '_get_systemd_services', sd_mock): - with patch.object(os, 'access', side_effect=access_mock): - with patch.object(systemd, '_sysv_enabled', - sysv_enabled_mock): + with patch.dict(systemd.__salt__, {"cmd.run": cmd_mock}): + with patch.object(os, "listdir", listdir_mock): + with patch.object(systemd, "_get_systemd_services", sd_mock): + with patch.object(os, "access", side_effect=access_mock): + with patch.object(systemd, "_sysv_enabled", sysv_enabled_mock): self.assertListEqual( systemd.get_disabled(), - ['bar', 'service2', 'timer2.timer'] + ["bar", "service2", "timer2.timer"], ) def test_get_all(self): - ''' + """ Test to return a list of all available services - ''' - listdir_mock = MagicMock(side_effect=[ - ['foo.service', 'multi-user.target.wants', 'mytimer.timer'], - [], - ['foo.service', 'multi-user.target.wants', 'bar.service'], - ['mysql', 'nginx', 'README'] - ]) - access_mock = MagicMock( - side_effect=lambda x, y: x != os.path.join( - systemd.INITSCRIPT_PATH, - 'README' - ) + """ + listdir_mock = MagicMock( + side_effect=[ + ["foo.service", "multi-user.target.wants", "mytimer.timer"], + [], + ["foo.service", "multi-user.target.wants", "bar.service"], + ["mysql", "nginx", "README"], + ] ) - with patch.object(os, 'listdir', listdir_mock): - with patch.object(os, 'access', side_effect=access_mock): + access_mock = MagicMock( + side_effect=lambda x, y: x + != os.path.join(systemd.INITSCRIPT_PATH, "README") + ) + with patch.object(os, "listdir", listdir_mock): + with patch.object(os, "access", side_effect=access_mock): self.assertListEqual( - systemd.get_all(), - ['bar', 'foo', 'mysql', 'mytimer.timer', 'nginx'] + systemd.get_all(), ["bar", "foo", "mysql", "mytimer.timer", "nginx"] ) def test_available(self): - ''' + """ Test to check that the given service is available - ''' + """ mock = MagicMock(side_effect=lambda x: _SYSTEMCTL_STATUS[x]) # systemd < 231 - with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 230}): - with patch.object(systemd, '_systemctl_status', mock): - self.assertTrue(systemd.available('sshd.service')) - self.assertFalse(systemd.available('foo.service')) + with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 230}): + with patch.object(systemd, "_systemctl_status", mock): + self.assertTrue(systemd.available("sshd.service")) + self.assertFalse(systemd.available("foo.service")) # systemd >= 231 - with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 231}): + with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 231}): with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231): - with patch.object(systemd, '_systemctl_status', mock): - self.assertTrue(systemd.available('sshd.service')) - self.assertFalse(systemd.available('bar.service')) + with patch.object(systemd, "_systemctl_status", mock): + self.assertTrue(systemd.available("sshd.service")) + self.assertFalse(systemd.available("bar.service")) # systemd < 231 with retcode/output changes backported (e.g. RHEL 7.3) - with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 219}): + with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 219}): with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231): - with patch.object(systemd, '_systemctl_status', mock): - self.assertTrue(systemd.available('sshd.service')) - self.assertFalse(systemd.available('bar.service')) + with patch.object(systemd, "_systemctl_status", mock): + self.assertTrue(systemd.available("sshd.service")) + self.assertFalse(systemd.available("bar.service")) def test_missing(self): - ''' + """ Test to the inverse of service.available. - ''' + """ mock = MagicMock(side_effect=lambda x: _SYSTEMCTL_STATUS[x]) # systemd < 231 - with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 230}): - with patch.object(systemd, '_systemctl_status', mock): - self.assertFalse(systemd.missing('sshd.service')) - self.assertTrue(systemd.missing('foo.service')) + with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 230}): + with patch.object(systemd, "_systemctl_status", mock): + self.assertFalse(systemd.missing("sshd.service")) + self.assertTrue(systemd.missing("foo.service")) # systemd >= 231 - with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 231}): + with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 231}): with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231): - with patch.object(systemd, '_systemctl_status', mock): - self.assertFalse(systemd.missing('sshd.service')) - self.assertTrue(systemd.missing('bar.service')) + with patch.object(systemd, "_systemctl_status", mock): + self.assertFalse(systemd.missing("sshd.service")) + self.assertTrue(systemd.missing("bar.service")) # systemd < 231 with retcode/output changes backported (e.g. RHEL 7.3) - with patch.dict(systemd.__context__, {'salt.utils.systemd.version': 219}): + with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 219}): with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231): - with patch.object(systemd, '_systemctl_status', mock): - self.assertFalse(systemd.missing('sshd.service')) - self.assertTrue(systemd.missing('bar.service')) + with patch.object(systemd, "_systemctl_status", mock): + self.assertFalse(systemd.missing("sshd.service")) + self.assertTrue(systemd.missing("bar.service")) def test_show(self): - ''' + """ Test to show properties of one or more units/jobs or the manager - ''' - show_output = 'a=b\nc=d\ne={ f=g ; h=i }\nWants=foo.service bar.service\n' + """ + show_output = "a=b\nc=d\ne={ f=g ; h=i }\nWants=foo.service bar.service\n" mock = MagicMock(return_value=show_output) - with patch.dict(systemd.__salt__, {'cmd.run': mock}): + with patch.dict(systemd.__salt__, {"cmd.run": mock}): self.assertDictEqual( - systemd.show('sshd'), - {'a': 'b', - 'c': 'd', - 'e': {'f': 'g', 'h': 'i'}, - 'Wants': ['foo.service', 'bar.service']} + systemd.show("sshd"), + { + "a": "b", + "c": "d", + "e": {"f": "g", "h": "i"}, + "Wants": ["foo.service", "bar.service"], + }, ) def test_execs(self): - ''' + """ Test to return a list of all files specified as ``ExecStart`` for all services - ''' - mock = MagicMock(return_value=['a', 'b']) - with patch.object(systemd, 'get_all', mock): - mock = MagicMock(return_value={'ExecStart': {'path': 'c'}}) - with patch.object(systemd, 'show', mock): - self.assertDictEqual(systemd.execs(), {'a': 'c', 'b': 'c'}) + """ + mock = MagicMock(return_value=["a", "b"]) + with patch.object(systemd, "get_all", mock): + mock = MagicMock(return_value={"ExecStart": {"path": "c"}}) + with patch.object(systemd, "show", mock): + self.assertDictEqual(systemd.execs(), {"a": "c", "b": "c"}) def test_status(self): - ''' + """ Test to confirm that the function retries when the service is in the activating/deactivating state. - ''' - active = { - 'stdout': 'active', - 'stderr': '', - 'retcode': 0, - 'pid': 12345} - inactive = { - 'stdout': 'inactive', - 'stderr': '', - 'retcode': 3, - 'pid': 12345} - activating = { - 'stdout': 'activating', - 'stderr': '', - 'retcode': 3, - 'pid': 12345} + """ + active = {"stdout": "active", "stderr": "", "retcode": 0, "pid": 12345} + inactive = {"stdout": "inactive", "stderr": "", "retcode": 3, "pid": 12345} + activating = {"stdout": "activating", "stderr": "", "retcode": 3, "pid": 12345} deactivating = { - 'stdout': 'deactivating', - 'stderr': '', - 'retcode': 3, - 'pid': 12345} + "stdout": "deactivating", + "stderr": "", + "retcode": 3, + "pid": 12345, + } check_mock = Mock() cmd_mock = MagicMock(side_effect=[activating, activating, active, inactive]) - with patch.dict(systemd.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.object(systemd, '_check_for_unit_changes', check_mock): - ret = systemd.status('foo') + with patch.dict(systemd.__salt__, {"cmd.run_all": cmd_mock}), patch.object( + systemd, "_check_for_unit_changes", check_mock + ): + ret = systemd.status("foo") assert ret is True # We should only have had three calls, since the third was not # either in the activating or deactivating state and we should not @@ -297,9 +273,10 @@ class SystemdTestCase(TestCase, LoaderModuleMockMixin): assert cmd_mock.call_count == 3 cmd_mock = MagicMock(side_effect=[deactivating, deactivating, inactive, active]) - with patch.dict(systemd.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.object(systemd, '_check_for_unit_changes', check_mock): - ret = systemd.status('foo') + with patch.dict(systemd.__salt__, {"cmd.run_all": cmd_mock}), patch.object( + systemd, "_check_for_unit_changes", check_mock + ): + ret = systemd.status("foo") assert ret is False # We should only have had three calls, since the third was not # either in the activating or deactivating state and we should not @@ -307,27 +284,30 @@ class SystemdTestCase(TestCase, LoaderModuleMockMixin): assert cmd_mock.call_count == 3 cmd_mock = MagicMock(side_effect=[activating, activating, active, inactive]) - with patch.dict(systemd.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.object(systemd, '_check_for_unit_changes', check_mock): - ret = systemd.status('foo', wait=0.25) + with patch.dict(systemd.__salt__, {"cmd.run_all": cmd_mock}), patch.object( + systemd, "_check_for_unit_changes", check_mock + ): + ret = systemd.status("foo", wait=0.25) assert ret is False # We should only have had two calls, because "wait" was set too low # to allow for more than one retry. assert cmd_mock.call_count == 2 cmd_mock = MagicMock(side_effect=[active, inactive]) - with patch.dict(systemd.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.object(systemd, '_check_for_unit_changes', check_mock): - ret = systemd.status('foo') + with patch.dict(systemd.__salt__, {"cmd.run_all": cmd_mock}), patch.object( + systemd, "_check_for_unit_changes", check_mock + ): + ret = systemd.status("foo") assert ret is True # We should only have a single call, because the first call was in # the active state. assert cmd_mock.call_count == 1 cmd_mock = MagicMock(side_effect=[inactive, active]) - with patch.dict(systemd.__salt__, {'cmd.run_all': cmd_mock}), \ - patch.object(systemd, '_check_for_unit_changes', check_mock): - ret = systemd.status('foo') + with patch.dict(systemd.__salt__, {"cmd.run_all": cmd_mock}), patch.object( + systemd, "_check_for_unit_changes", check_mock + ): + ret = systemd.status("foo") assert ret is False # We should only have a single call, because the first call was in # the inactive state. @@ -335,73 +315,82 @@ class SystemdTestCase(TestCase, LoaderModuleMockMixin): class SystemdScopeTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test case for salt.modules.systemd, for functions which use systemd scopes - ''' + """ + def setup_loader_modules(self): return {systemd: {}} - unit_name = 'foo' + unit_name = "foo" mock_none = MagicMock(return_value=None) mock_success = MagicMock(return_value=0) mock_failure = MagicMock(return_value=1) mock_true = MagicMock(return_value=True) mock_false = MagicMock(return_value=False) mock_empty_list = MagicMock(return_value=[]) - mock_run_all_success = MagicMock(return_value={'retcode': 0, - 'stdout': '', - 'stderr': '', - 'pid': 12345}) - mock_run_all_failure = MagicMock(return_value={'retcode': 1, - 'stdout': '', - 'stderr': '', - 'pid': 12345}) + mock_run_all_success = MagicMock( + return_value={"retcode": 0, "stdout": "", "stderr": "", "pid": 12345} + ) + mock_run_all_failure = MagicMock( + return_value={"retcode": 1, "stdout": "", "stderr": "", "pid": 12345} + ) def _change_state(self, action, no_block=False): - ''' + """ Common code for start/stop/restart/reload/force_reload tests - ''' + """ # We want the traceback if the function name can't be found in the # systemd execution module. func = getattr(systemd, action) # Remove trailing _ in "reload_" - action = action.rstrip('_').replace('_', '-') - systemctl_command = ['systemctl'] + action = action.rstrip("_").replace("_", "-") + systemctl_command = ["systemctl"] if no_block: - systemctl_command.append('--no-block') - systemctl_command.extend([action, self.unit_name + '.service']) - scope_prefix = ['systemd-run', '--scope'] + systemctl_command.append("--no-block") + systemctl_command.extend([action, self.unit_name + ".service"]) + scope_prefix = ["systemd-run", "--scope"] - assert_kwargs = {'python_shell': False} - if action in ('enable', 'disable'): - assert_kwargs['ignore_retcode'] = True + assert_kwargs = {"python_shell": False} + if action in ("enable", "disable"): + assert_kwargs["ignore_retcode"] = True - with patch.object(systemd, '_check_for_unit_changes', self.mock_none): - with patch.object(systemd, '_unit_file_changed', self.mock_none): - with patch.object(systemd, '_check_unmask', self.mock_none): - with patch.object(systemd, '_get_sysv_services', self.mock_empty_list): + with patch.object(systemd, "_check_for_unit_changes", self.mock_none): + with patch.object(systemd, "_unit_file_changed", self.mock_none): + with patch.object(systemd, "_check_unmask", self.mock_none): + with patch.object( + systemd, "_get_sysv_services", self.mock_empty_list + ): # Has scopes available - with patch.object(salt.utils.systemd, 'has_scope', self.mock_true): + with patch.object( + salt.utils.systemd, "has_scope", self.mock_true + ): # Scope enabled, successful with patch.dict( - systemd.__salt__, - {'config.get': self.mock_true, - 'cmd.run_all': self.mock_run_all_success}): + systemd.__salt__, + { + "config.get": self.mock_true, + "cmd.run_all": self.mock_run_all_success, + }, + ): ret = func(self.unit_name, no_block=no_block) self.assertTrue(ret) self.mock_run_all_success.assert_called_with( - scope_prefix + systemctl_command, - **assert_kwargs) + scope_prefix + systemctl_command, **assert_kwargs + ) # Scope enabled, failed with patch.dict( - systemd.__salt__, - {'config.get': self.mock_true, - 'cmd.run_all': self.mock_run_all_failure}): - if action in ('stop', 'disable'): + systemd.__salt__, + { + "config.get": self.mock_true, + "cmd.run_all": self.mock_run_all_failure, + }, + ): + if action in ("stop", "disable"): ret = func(self.unit_name, no_block=no_block) self.assertFalse(ret) else: @@ -409,28 +398,35 @@ class SystemdScopeTestCase(TestCase, LoaderModuleMockMixin): CommandExecutionError, func, self.unit_name, - no_block=no_block) + no_block=no_block, + ) self.mock_run_all_failure.assert_called_with( - scope_prefix + systemctl_command, - **assert_kwargs) + scope_prefix + systemctl_command, **assert_kwargs + ) # Scope disabled, successful with patch.dict( - systemd.__salt__, - {'config.get': self.mock_false, - 'cmd.run_all': self.mock_run_all_success}): + systemd.__salt__, + { + "config.get": self.mock_false, + "cmd.run_all": self.mock_run_all_success, + }, + ): ret = func(self.unit_name, no_block=no_block) self.assertTrue(ret) self.mock_run_all_success.assert_called_with( - systemctl_command, - **assert_kwargs) + systemctl_command, **assert_kwargs + ) # Scope disabled, failed with patch.dict( - systemd.__salt__, - {'config.get': self.mock_false, - 'cmd.run_all': self.mock_run_all_failure}): - if action in ('stop', 'disable'): + systemd.__salt__, + { + "config.get": self.mock_false, + "cmd.run_all": self.mock_run_all_failure, + }, + ): + if action in ("stop", "disable"): ret = func(self.unit_name, no_block=no_block) self.assertFalse(ret) else: @@ -438,13 +434,16 @@ class SystemdScopeTestCase(TestCase, LoaderModuleMockMixin): CommandExecutionError, func, self.unit_name, - no_block=no_block) + no_block=no_block, + ) self.mock_run_all_failure.assert_called_with( - systemctl_command, - **assert_kwargs) + systemctl_command, **assert_kwargs + ) # Does not have scopes available - with patch.object(salt.utils.systemd, 'has_scope', self.mock_false): + with patch.object( + salt.utils.systemd, "has_scope", self.mock_false + ): # The results should be the same irrespective of # whether or not scope is enabled, since scope is not @@ -454,124 +453,142 @@ class SystemdScopeTestCase(TestCase, LoaderModuleMockMixin): # Successful with patch.dict( - systemd.__salt__, - {'config.get': scope_mock, - 'cmd.run_all': self.mock_run_all_success}): + systemd.__salt__, + { + "config.get": scope_mock, + "cmd.run_all": self.mock_run_all_success, + }, + ): ret = func(self.unit_name, no_block=no_block) self.assertTrue(ret) self.mock_run_all_success.assert_called_with( - systemctl_command, - **assert_kwargs) + systemctl_command, **assert_kwargs + ) # Failed with patch.dict( - systemd.__salt__, - {'config.get': scope_mock, - 'cmd.run_all': self.mock_run_all_failure}): - if action in ('stop', 'disable'): - ret = func(self.unit_name, - no_block=no_block) + systemd.__salt__, + { + "config.get": scope_mock, + "cmd.run_all": self.mock_run_all_failure, + }, + ): + if action in ("stop", "disable"): + ret = func(self.unit_name, no_block=no_block) self.assertFalse(ret) else: self.assertRaises( CommandExecutionError, func, self.unit_name, - no_block=no_block) + no_block=no_block, + ) self.mock_run_all_failure.assert_called_with( - systemctl_command, - **assert_kwargs) + systemctl_command, **assert_kwargs + ) def _mask_unmask(self, action, runtime): - ''' + """ Common code for mask/unmask tests - ''' + """ # We want the traceback if the function name can't be found in the # systemd execution module, so don't provide a fallback value for the # call to getattr() here. func = getattr(systemd, action) # Remove trailing _ in "unmask_" - action = action.rstrip('_').replace('_', '-') - systemctl_command = ['systemctl', action] + action = action.rstrip("_").replace("_", "-") + systemctl_command = ["systemctl", action] if runtime: - systemctl_command.append('--runtime') - systemctl_command.append(self.unit_name + '.service') - scope_prefix = ['systemd-run', '--scope'] + systemctl_command.append("--runtime") + systemctl_command.append(self.unit_name + ".service") + scope_prefix = ["systemd-run", "--scope"] args = [self.unit_name, runtime] - masked_mock = self.mock_true if action == 'unmask' else self.mock_false + masked_mock = self.mock_true if action == "unmask" else self.mock_false - with patch.object(systemd, '_check_for_unit_changes', self.mock_none): - if action == 'unmask': - mock_not_run = MagicMock(return_value={'retcode': 0, - 'stdout': '', - 'stderr': '', - 'pid': 12345}) - with patch.dict(systemd.__salt__, {'cmd.run_all': mock_not_run}): - with patch.object(systemd, 'masked', self.mock_false): + with patch.object(systemd, "_check_for_unit_changes", self.mock_none): + if action == "unmask": + mock_not_run = MagicMock( + return_value={ + "retcode": 0, + "stdout": "", + "stderr": "", + "pid": 12345, + } + ) + with patch.dict(systemd.__salt__, {"cmd.run_all": mock_not_run}): + with patch.object(systemd, "masked", self.mock_false): # Test not masked (should take no action and return True) self.assertTrue(systemd.unmask_(self.unit_name)) # Also should not have called cmd.run_all self.assertTrue(mock_not_run.call_count == 0) - with patch.object(systemd, 'masked', masked_mock): + with patch.object(systemd, "masked", masked_mock): # Has scopes available - with patch.object(salt.utils.systemd, 'has_scope', self.mock_true): + with patch.object(salt.utils.systemd, "has_scope", self.mock_true): # Scope enabled, successful with patch.dict( - systemd.__salt__, - {'config.get': self.mock_true, - 'cmd.run_all': self.mock_run_all_success}): + systemd.__salt__, + { + "config.get": self.mock_true, + "cmd.run_all": self.mock_run_all_success, + }, + ): ret = func(*args) self.assertTrue(ret) self.mock_run_all_success.assert_called_with( scope_prefix + systemctl_command, python_shell=False, - redirect_stderr=True) + redirect_stderr=True, + ) # Scope enabled, failed with patch.dict( - systemd.__salt__, - {'config.get': self.mock_true, - 'cmd.run_all': self.mock_run_all_failure}): - self.assertRaises( - CommandExecutionError, - func, *args) + systemd.__salt__, + { + "config.get": self.mock_true, + "cmd.run_all": self.mock_run_all_failure, + }, + ): + self.assertRaises(CommandExecutionError, func, *args) self.mock_run_all_failure.assert_called_with( scope_prefix + systemctl_command, python_shell=False, - redirect_stderr=True) + redirect_stderr=True, + ) # Scope disabled, successful with patch.dict( - systemd.__salt__, - {'config.get': self.mock_false, - 'cmd.run_all': self.mock_run_all_success}): + systemd.__salt__, + { + "config.get": self.mock_false, + "cmd.run_all": self.mock_run_all_success, + }, + ): ret = func(*args) self.assertTrue(ret) self.mock_run_all_success.assert_called_with( - systemctl_command, - python_shell=False, - redirect_stderr=True) + systemctl_command, python_shell=False, redirect_stderr=True + ) # Scope disabled, failed with patch.dict( - systemd.__salt__, - {'config.get': self.mock_false, - 'cmd.run_all': self.mock_run_all_failure}): - self.assertRaises( - CommandExecutionError, - func, *args) + systemd.__salt__, + { + "config.get": self.mock_false, + "cmd.run_all": self.mock_run_all_failure, + }, + ): + self.assertRaises(CommandExecutionError, func, *args) self.mock_run_all_failure.assert_called_with( - systemctl_command, - python_shell=False, - redirect_stderr=True) + systemctl_command, python_shell=False, redirect_stderr=True + ) # Does not have scopes available - with patch.object(salt.utils.systemd, 'has_scope', self.mock_false): + with patch.object(salt.utils.systemd, "has_scope", self.mock_false): # The results should be the same irrespective of # whether or not scope is enabled, since scope is not @@ -581,65 +598,133 @@ class SystemdScopeTestCase(TestCase, LoaderModuleMockMixin): # Successful with patch.dict( - systemd.__salt__, - {'config.get': scope_mock, - 'cmd.run_all': self.mock_run_all_success}): + systemd.__salt__, + { + "config.get": scope_mock, + "cmd.run_all": self.mock_run_all_success, + }, + ): ret = func(*args) self.assertTrue(ret) self.mock_run_all_success.assert_called_with( systemctl_command, python_shell=False, - redirect_stderr=True) + redirect_stderr=True, + ) # Failed with patch.dict( - systemd.__salt__, - {'config.get': scope_mock, - 'cmd.run_all': self.mock_run_all_failure}): - self.assertRaises( - CommandExecutionError, - func, *args) + systemd.__salt__, + { + "config.get": scope_mock, + "cmd.run_all": self.mock_run_all_failure, + }, + ): + self.assertRaises(CommandExecutionError, func, *args) self.mock_run_all_failure.assert_called_with( systemctl_command, python_shell=False, - redirect_stderr=True) + redirect_stderr=True, + ) def test_start(self): - self._change_state('start', no_block=False) - self._change_state('start', no_block=True) + self._change_state("start", no_block=False) + self._change_state("start", no_block=True) def test_stop(self): - self._change_state('stop', no_block=False) - self._change_state('stop', no_block=True) + self._change_state("stop", no_block=False) + self._change_state("stop", no_block=True) def test_restart(self): - self._change_state('restart', no_block=False) - self._change_state('restart', no_block=True) + self._change_state("restart", no_block=False) + self._change_state("restart", no_block=True) def test_reload(self): - self._change_state('reload_', no_block=False) - self._change_state('reload_', no_block=True) + self._change_state("reload_", no_block=False) + self._change_state("reload_", no_block=True) def test_force_reload(self): - self._change_state('force_reload', no_block=False) - self._change_state('force_reload', no_block=True) + self._change_state("force_reload", no_block=False) + self._change_state("force_reload", no_block=True) def test_enable(self): - self._change_state('enable', no_block=False) - self._change_state('enable', no_block=True) + self._change_state("enable", no_block=False) + self._change_state("enable", no_block=True) def test_disable(self): - self._change_state('disable', no_block=False) - self._change_state('disable', no_block=True) + self._change_state("disable", no_block=False) + self._change_state("disable", no_block=True) def test_mask(self): - self._mask_unmask('mask', False) + self._mask_unmask("mask", False) def test_mask_runtime(self): - self._mask_unmask('mask', True) + self._mask_unmask("mask", True) def test_unmask(self): - self._mask_unmask('unmask_', False) + self._mask_unmask("unmask_", False) def test_unmask_runtime(self): - self._mask_unmask('unmask_', True) + self._mask_unmask("unmask_", True) + + def test_firstboot(self): + """ + Test service.firstboot without parameters + """ + result = {"retcode": 0, "stdout": "stdout"} + salt_mock = { + "cmd.run_all": MagicMock(return_value=result), + } + with patch.dict(systemd.__salt__, salt_mock): + assert systemd.firstboot() + salt_mock["cmd.run_all"].assert_called_with(["systemd-firstboot"]) + + def test_firstboot_params(self): + """ + Test service.firstboot with parameters + """ + result = {"retcode": 0, "stdout": "stdout"} + salt_mock = { + "cmd.run_all": MagicMock(return_value=result), + } + with patch.dict(systemd.__salt__, salt_mock): + assert systemd.firstboot( + locale="en_US.UTF-8", + locale_message="en_US.UTF-8", + keymap="jp", + timezone="Europe/Berlin", + hostname="node-001", + machine_id="1234567890abcdef", + root="/mnt", + ) + salt_mock["cmd.run_all"].assert_called_with( + [ + "systemd-firstboot", + "--locale", + "en_US.UTF-8", + "--locale-message", + "en_US.UTF-8", + "--keymap", + "jp", + "--timezone", + "Europe/Berlin", + "--hostname", + "node-001", + "--machine-ID", + "1234567890abcdef", + "--root", + "/mnt", + ] + ) + + def test_firstboot_error(self): + """ + Test service.firstboot error + """ + result = {"retcode": 1, "stderr": "error"} + salt_mock = { + "cmd.run_all": MagicMock(return_value=result), + } + with patch.dict(systemd.__salt__, salt_mock): + with pytest.raises(CommandExecutionError): + assert systemd.firstboot() diff --git a/tests/unit/modules/test_telegram.py b/tests/unit/modules/test_telegram.py index 3a7eca9483c..425850ab8cb 100644 --- a/tests/unit/modules/test_telegram.py +++ b/tests/unit/modules/test_telegram.py @@ -1,34 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the Telegram execution module. :codeauthor: :email:`Roald Nefs (info@roaldnefs.com)` -''' +""" # Import Python Libs from __future__ import absolute_import import logging -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, -) - # Import Salt Libs import salt.modules.telegram as telegram +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock +from tests.support.unit import TestCase log = logging.getLogger(__name__) class RequestMock(Mock): - ''' + """ Request Mock - ''' + """ def get(self, *args, **kwargs): return RequestResponseMock() @@ -45,46 +41,52 @@ class RequestMock(Mock): class RequestResponseMock(Mock): - ''' + """ Request Response Mock - ''' + """ + def json(self): - return [{'url': 'http://example.org', - '_id': 1234}, ] + return [ + {"url": "http://example.org", "_id": 1234}, + ] class RequestPutResponseMock(Mock): - ''' + """ Request Put Response Mock - ''' + """ + ok = True def json(self): - return {'_id': 4321} + return {"_id": 4321} class TelegramModuleTest(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.telegram. - ''' + """ + def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.get': MagicMock(return_value={ - 'telegram': { - 'chat_id': '123456789', - 'token': '000000000:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' + "__salt__": { + "config.get": MagicMock( + return_value={ + "telegram": { + "chat_id": "123456789", + "token": "000000000:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + } } - }), - 'requests.put': Mock(), + ), + "requests.put": Mock(), }, - 'requests': RequestMock() + "requests": RequestMock(), } return {telegram: module_globals} def test_post_message(self): - ''' + """ Test the post_message function. - ''' - message = 'Hello World!' + """ + message = "Hello World!" self.assertTrue(telegram.post_message(message)) diff --git a/tests/unit/modules/test_timezone.py b/tests/unit/modules/test_timezone.py index 1aa2a766c83..9ea7a7070ae 100644 --- a/tests/unit/modules/test_timezone.py +++ b/tests/unit/modules/test_timezone.py @@ -2,33 +2,30 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -from tempfile import NamedTemporaryFile + import os +from tempfile import NamedTemporaryFile -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, - mock_open -) - -# Import Salt Libs -from salt.exceptions import CommandExecutionError, SaltInvocationError import salt.modules.timezone as timezone -from salt.ext import six import salt.utils.platform import salt.utils.stringutils -GET_ZONE_FILE = 'salt.modules.timezone._get_zone_file' -GET_LOCALTIME_PATH = 'salt.modules.timezone._get_localtime_path' +# Import Salt Libs +from salt.exceptions import CommandExecutionError, SaltInvocationError +from salt.ext import six + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase, skipIf + +GET_ZONE_FILE = "salt.modules.timezone._get_zone_file" +GET_LOCALTIME_PATH = "salt.modules.timezone._get_localtime_path" class TimezoneTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - return {timezone: {'__grains__': {'os_family': 'Ubuntu'}}} + return {timezone: {"__grains__": {"os_family": "Ubuntu"}}} def setUp(self): self.tempfiles = [] @@ -42,35 +39,35 @@ class TimezoneTestCase(TestCase, LoaderModuleMockMixin): del self.tempfiles def test_zone_compare_equal(self): - etc_localtime = self.create_tempfile_with_contents('a') - zone_path = self.create_tempfile_with_contents('a') + etc_localtime = self.create_tempfile_with_contents("a") + zone_path = self.create_tempfile_with_contents("a") with patch(GET_ZONE_FILE, lambda p: zone_path.name): with patch(GET_LOCALTIME_PATH, lambda: etc_localtime.name): - self.assertTrue(timezone.zone_compare('foo')) + self.assertTrue(timezone.zone_compare("foo")) def test_zone_compare_nonexistent(self): - etc_localtime = self.create_tempfile_with_contents('a') + etc_localtime = self.create_tempfile_with_contents("a") - with patch(GET_ZONE_FILE, lambda p: '/foopath/nonexistent'): + with patch(GET_ZONE_FILE, lambda p: "/foopath/nonexistent"): with patch(GET_LOCALTIME_PATH, lambda: etc_localtime.name): - self.assertRaises(SaltInvocationError, timezone.zone_compare, 'foo') + self.assertRaises(SaltInvocationError, timezone.zone_compare, "foo") def test_zone_compare_unequal(self): - etc_localtime = self.create_tempfile_with_contents('a') - zone_path = self.create_tempfile_with_contents('b') + etc_localtime = self.create_tempfile_with_contents("a") + zone_path = self.create_tempfile_with_contents("b") with patch(GET_ZONE_FILE, lambda p: zone_path.name): with patch(GET_LOCALTIME_PATH, lambda: etc_localtime.name): - self.assertFalse(timezone.zone_compare('foo')) + self.assertFalse(timezone.zone_compare("foo")) def test_missing_localtime(self): - with patch(GET_ZONE_FILE, lambda p: '/nonexisting'): - with patch(GET_LOCALTIME_PATH, lambda: '/also-missing'): - self.assertRaises(CommandExecutionError, timezone.zone_compare, 'foo') + with patch(GET_ZONE_FILE, lambda p: "/nonexisting"): + with patch(GET_LOCALTIME_PATH, lambda: "/also-missing"): + self.assertRaises(CommandExecutionError, timezone.zone_compare, "foo") def create_tempfile_with_contents(self, contents): temp = NamedTemporaryFile(delete=False) @@ -84,378 +81,418 @@ class TimezoneTestCase(TestCase, LoaderModuleMockMixin): class TimezoneModuleTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Timezone test case - ''' - TEST_TZ = 'UTC' + """ + + TEST_TZ = "UTC" def setup_loader_modules(self): - return {timezone: {'__grains__': {'os': ''}, - '__salt__': {'file.sed': MagicMock(), - 'cmd.run': MagicMock(), - 'cmd.retcode': MagicMock(return_value=0)}}} + return { + timezone: { + "__grains__": {"os": ""}, + "__salt__": { + "file.sed": MagicMock(), + "cmd.run": MagicMock(), + "cmd.retcode": MagicMock(return_value=0), + }, + } + } - @patch('salt.utils.path.which', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=False)) def test_get_zone_centos(self): - ''' + """ Test CentOS is recognized :return: - ''' - with patch.dict(timezone.__grains__, {'os': 'centos'}): - with patch('salt.modules.timezone._get_zone_etc_localtime', MagicMock(return_value=self.TEST_TZ)): + """ + with patch.dict(timezone.__grains__, {"os": "centos"}): + with patch( + "salt.modules.timezone._get_zone_etc_localtime", + MagicMock(return_value=self.TEST_TZ), + ): assert timezone.get_zone() == self.TEST_TZ - @patch('salt.utils.path.which', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=False)) def test_get_zone_os_family_rh_suse(self): - ''' + """ Test RedHat and Suse are recognized :return: - ''' - for osfamily in ['RedHat', 'Suse']: - with patch.dict(timezone.__grains__, {'os_family': [osfamily]}): - with patch('salt.modules.timezone._get_zone_sysconfig', MagicMock(return_value=self.TEST_TZ)): + """ + for osfamily in ["RedHat", "Suse"]: + with patch.dict(timezone.__grains__, {"os_family": [osfamily]}): + with patch( + "salt.modules.timezone._get_zone_sysconfig", + MagicMock(return_value=self.TEST_TZ), + ): assert timezone.get_zone() == self.TEST_TZ - @patch('salt.utils.path.which', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=False)) def test_get_zone_os_family_debian_gentoo(self): - ''' + """ Test Debian and Gentoo are recognized :return: - ''' - for osfamily in ['Debian', 'Gentoo']: - with patch.dict(timezone.__grains__, {'os_family': [osfamily]}): - with patch('salt.modules.timezone._get_zone_etc_timezone', MagicMock(return_value=self.TEST_TZ)): + """ + for osfamily in ["Debian", "Gentoo"]: + with patch.dict(timezone.__grains__, {"os_family": [osfamily]}): + with patch( + "salt.modules.timezone._get_zone_etc_timezone", + MagicMock(return_value=self.TEST_TZ), + ): assert timezone.get_zone() == self.TEST_TZ - @patch('salt.utils.path.which', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=False)) def test_get_zone_os_family_allbsd_nilinuxrt(self): - ''' + """ Test *BSD and NILinuxRT are recognized :return: - ''' - for osfamily in ['FreeBSD', 'OpenBSD', 'NetBSD', 'NILinuxRT']: - with patch.dict(timezone.__grains__, {'os_family': osfamily}): - with patch('salt.modules.timezone._get_zone_etc_localtime', MagicMock(return_value=self.TEST_TZ)): + """ + for osfamily in ["FreeBSD", "OpenBSD", "NetBSD", "NILinuxRT"]: + with patch.dict(timezone.__grains__, {"os_family": osfamily}): + with patch( + "salt.modules.timezone._get_zone_etc_localtime", + MagicMock(return_value=self.TEST_TZ), + ): assert timezone.get_zone() == self.TEST_TZ - @patch('salt.utils.path.which', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=False)) def test_get_zone_os_family_slowlaris(self): - ''' + """ Test Slowlaris is recognized :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Solaris']}): - with patch('salt.modules.timezone._get_zone_solaris', MagicMock(return_value=self.TEST_TZ)): + """ + with patch.dict(timezone.__grains__, {"os_family": ["Solaris"]}): + with patch( + "salt.modules.timezone._get_zone_solaris", + MagicMock(return_value=self.TEST_TZ), + ): assert timezone.get_zone() == self.TEST_TZ - @patch('salt.utils.path.which', MagicMock(return_value=False)) + @patch("salt.utils.path.which", MagicMock(return_value=False)) def test_get_zone_os_family_aix(self): - ''' + """ Test IBM AIX is recognized :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['AIX']}): - with patch('salt.modules.timezone._get_zone_aix', MagicMock(return_value=self.TEST_TZ)): + """ + with patch.dict(timezone.__grains__, {"os_family": ["AIX"]}): + with patch( + "salt.modules.timezone._get_zone_aix", + MagicMock(return_value=self.TEST_TZ), + ): assert timezone.get_zone() == self.TEST_TZ - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_set_zone_redhat(self): - ''' + """ Test zone set on RH series :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['RedHat']}): + """ + with patch.dict(timezone.__grains__, {"os_family": ["RedHat"]}): assert timezone.set_zone(self.TEST_TZ) - name, args, kwargs = timezone.__salt__['file.sed'].mock_calls[0] - assert args == ('/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="UTC"') + name, args, kwargs = timezone.__salt__["file.sed"].mock_calls[0] + assert args == ("/etc/sysconfig/clock", "^ZONE=.*", 'ZONE="UTC"') - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_set_zone_suse(self): - ''' + """ Test zone set on SUSE series :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Suse']}): + """ + with patch.dict(timezone.__grains__, {"os_family": ["Suse"]}): assert timezone.set_zone(self.TEST_TZ) - name, args, kwargs = timezone.__salt__['file.sed'].mock_calls[0] - assert args == ('/etc/sysconfig/clock', '^TIMEZONE=.*', 'TIMEZONE="UTC"') + name, args, kwargs = timezone.__salt__["file.sed"].mock_calls[0] + assert args == ("/etc/sysconfig/clock", "^TIMEZONE=.*", 'TIMEZONE="UTC"') - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_set_zone_gentoo(self): - ''' + """ Test zone set on Gentoo series :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Gentoo']}): - with patch('salt.utils.files.fopen', mock_open()) as m_open: + """ + with patch.dict(timezone.__grains__, {"os_family": ["Gentoo"]}): + with patch("salt.utils.files.fopen", mock_open()) as m_open: assert timezone.set_zone(self.TEST_TZ) - fh_ = m_open.filehandles['/etc/timezone'][0] - assert fh_.call.args == ('/etc/timezone', 'w'), fh_.call.args - assert fh_.write_calls == ['UTC', '\n'], fh_.write_calls + fh_ = m_open.filehandles["/etc/timezone"][0] + assert fh_.call.args == ("/etc/timezone", "w"), fh_.call.args + assert fh_.write_calls == ["UTC", "\n"], fh_.write_calls - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_set_zone_debian(self): - ''' + """ Test zone set on Debian series :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Debian']}): - with patch('salt.utils.files.fopen', mock_open()) as m_open: + """ + with patch.dict(timezone.__grains__, {"os_family": ["Debian"]}): + with patch("salt.utils.files.fopen", mock_open()) as m_open: assert timezone.set_zone(self.TEST_TZ) - fh_ = m_open.filehandles['/etc/timezone'][0] - assert fh_.call.args == ('/etc/timezone', 'w'), fh_.call.args - assert fh_.write_calls == ['UTC', '\n'], fh_.write_calls + fh_ = m_open.filehandles["/etc/timezone"][0] + assert fh_.call.args == ("/etc/timezone", "w"), fh_.call.args + assert fh_.write_calls == ["UTC", "\n"], fh_.write_calls - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=True)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=True)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_get_hwclock_timedate_utc(self): - ''' + """ Test get hwclock UTC/localtime :return: - ''' - with patch('salt.modules.timezone._timedatectl', MagicMock(return_value={'stdout': 'rtc in local tz'})): - assert timezone.get_hwclock() == 'UTC' - with patch('salt.modules.timezone._timedatectl', MagicMock(return_value={'stdout': 'rtc in local tz:yes'})): - assert timezone.get_hwclock() == 'localtime' + """ + with patch( + "salt.modules.timezone._timedatectl", + MagicMock(return_value={"stdout": "rtc in local tz"}), + ): + assert timezone.get_hwclock() == "UTC" + with patch( + "salt.modules.timezone._timedatectl", + MagicMock(return_value={"stdout": "rtc in local tz:yes"}), + ): + assert timezone.get_hwclock() == "localtime" - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_get_hwclock_suse(self): - ''' + """ Test get hwclock on SUSE :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Suse']}): + """ + with patch.dict(timezone.__grains__, {"os_family": ["Suse"]}): timezone.get_hwclock() - name, args, kwarg = timezone.__salt__['cmd.run'].mock_calls[0] - assert args == (['tail', '-n', '1', '/etc/adjtime'],) - assert kwarg == {'python_shell': False} + name, args, kwarg = timezone.__salt__["cmd.run"].mock_calls[0] + assert args == (["tail", "-n", "1", "/etc/adjtime"],) + assert kwarg == {"python_shell": False} - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_get_hwclock_redhat(self): - ''' + """ Test get hwclock on RedHat :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['RedHat']}): + """ + with patch.dict(timezone.__grains__, {"os_family": ["RedHat"]}): timezone.get_hwclock() - name, args, kwarg = timezone.__salt__['cmd.run'].mock_calls[0] - assert args == (['tail', '-n', '1', '/etc/adjtime'],) - assert kwarg == {'python_shell': False} + name, args, kwarg = timezone.__salt__["cmd.run"].mock_calls[0] + assert args == (["tail", "-n", "1", "/etc/adjtime"],) + assert kwarg == {"python_shell": False} - def _test_get_hwclock_debian(self): # TODO: Enable this when testing environment is working properly - ''' + def _test_get_hwclock_debian( + self, + ): # TODO: Enable this when testing environment is working properly + """ Test get hwclock on Debian :return: - ''' - with patch('salt.utils.path.which', MagicMock(return_value=False)): - with patch('os.path.exists', MagicMock(return_value=True)): - with patch('os.unlink', MagicMock()): - with patch('os.symlink', MagicMock()): - with patch.dict(timezone.__grains__, {'os_family': ['Debian']}): + """ + with patch("salt.utils.path.which", MagicMock(return_value=False)): + with patch("os.path.exists", MagicMock(return_value=True)): + with patch("os.unlink", MagicMock()): + with patch("os.symlink", MagicMock()): + with patch.dict(timezone.__grains__, {"os_family": ["Debian"]}): timezone.get_hwclock() - name, args, kwarg = timezone.__salt__['cmd.run'].mock_calls[0] - assert args == (['tail', '-n', '1', '/etc/adjtime'],) - assert kwarg == {'python_shell': False} + name, args, kwarg = timezone.__salt__["cmd.run"].mock_calls[ + 0 + ] + assert args == (["tail", "-n", "1", "/etc/adjtime"],) + assert kwarg == {"python_shell": False} - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_get_hwclock_solaris(self): - ''' + """ Test get hwclock on Solaris :return: - ''' + """ # Incomplete - with patch.dict(timezone.__grains__, {'os_family': ['Solaris']}): - assert timezone.get_hwclock() == 'UTC' - with patch('salt.utils.files.fopen', mock_open()): - assert timezone.get_hwclock() == 'localtime' + with patch.dict(timezone.__grains__, {"os_family": ["Solaris"]}): + assert timezone.get_hwclock() == "UTC" + with patch("salt.utils.files.fopen", mock_open()): + assert timezone.get_hwclock() == "localtime" - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_get_hwclock_aix(self): - ''' + """ Test get hwclock on AIX :return: - ''' + """ # Incomplete - hwclock = 'localtime' - if not os.path.isfile('/etc/environment'): - hwclock = 'UTC' - with patch.dict(timezone.__grains__, {'os_family': ['AIX']}): + hwclock = "localtime" + if not os.path.isfile("/etc/environment"): + hwclock = "UTC" + with patch.dict(timezone.__grains__, {"os_family": ["AIX"]}): assert timezone.get_hwclock() == hwclock - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=True)) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=True)) def test_set_hwclock_timedatectl(self): - ''' + """ Test set hwclock with timedatectl :return: - ''' - timezone.set_hwclock('UTC') - name, args, kwargs = timezone.__salt__['cmd.retcode'].mock_calls[0] - assert args == (['timedatectl', 'set-local-rtc', 'false'],) + """ + timezone.set_hwclock("UTC") + name, args, kwargs = timezone.__salt__["cmd.retcode"].mock_calls[0] + assert args == (["timedatectl", "set-local-rtc", "false"],) - timezone.set_hwclock('localtime') - name, args, kwargs = timezone.__salt__['cmd.retcode'].mock_calls[1] - assert args == (['timedatectl', 'set-local-rtc', 'true'],) + timezone.set_hwclock("localtime") + name, args, kwargs = timezone.__salt__["cmd.retcode"].mock_calls[1] + assert args == (["timedatectl", "set-local-rtc", "true"],) - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) def test_set_hwclock_aix_nilinuxrt(self): - ''' + """ Test set hwclock on AIX and NILinuxRT :return: - ''' - for osfamily in ['AIX', 'NILinuxRT']: - with patch.dict(timezone.__grains__, {'os_family': osfamily}): + """ + for osfamily in ["AIX", "NILinuxRT"]: + with patch.dict(timezone.__grains__, {"os_family": osfamily}): with self.assertRaises(SaltInvocationError): - assert timezone.set_hwclock('forty two') - assert timezone.set_hwclock('UTC') + assert timezone.set_hwclock("forty two") + assert timezone.set_hwclock("UTC") - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) - @patch('salt.modules.timezone.get_zone', MagicMock(return_value='TEST_TIMEZONE')) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) + @patch("salt.modules.timezone.get_zone", MagicMock(return_value="TEST_TIMEZONE")) def test_set_hwclock_solaris(self): - ''' + """ Test set hwclock on Solaris :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Solaris'], - 'cpuarch': 'x86'}): + """ + with patch.dict( + timezone.__grains__, {"os_family": ["Solaris"], "cpuarch": "x86"} + ): with self.assertRaises(SaltInvocationError): - assert timezone.set_hwclock('forty two') - assert timezone.set_hwclock('UTC') - name, args, kwargs = timezone.__salt__['cmd.retcode'].mock_calls[0] - assert args == (['rtc', '-z', 'GMT'],) - assert kwargs == {'python_shell': False} + assert timezone.set_hwclock("forty two") + assert timezone.set_hwclock("UTC") + name, args, kwargs = timezone.__salt__["cmd.retcode"].mock_calls[0] + assert args == (["rtc", "-z", "GMT"],) + assert kwargs == {"python_shell": False} - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) - @patch('salt.modules.timezone.get_zone', MagicMock(return_value='TEST_TIMEZONE')) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) + @patch("salt.modules.timezone.get_zone", MagicMock(return_value="TEST_TIMEZONE")) def test_set_hwclock_arch(self): - ''' + """ Test set hwclock on arch :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Arch']}): - assert timezone.set_hwclock('UTC') - name, args, kwargs = timezone.__salt__['cmd.retcode'].mock_calls[0] - assert args == (['timezonectl', 'set-local-rtc', 'false'],) - assert kwargs == {'python_shell': False} + """ + with patch.dict(timezone.__grains__, {"os_family": ["Arch"]}): + assert timezone.set_hwclock("UTC") + name, args, kwargs = timezone.__salt__["cmd.retcode"].mock_calls[0] + assert args == (["timezonectl", "set-local-rtc", "false"],) + assert kwargs == {"python_shell": False} - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) - @patch('salt.modules.timezone.get_zone', MagicMock(return_value='TEST_TIMEZONE')) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) + @patch("salt.modules.timezone.get_zone", MagicMock(return_value="TEST_TIMEZONE")) def test_set_hwclock_redhat(self): - ''' + """ Test set hwclock on RedHat :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['RedHat']}): - assert timezone.set_hwclock('UTC') - name, args, kwargs = timezone.__salt__['file.sed'].mock_calls[0] - assert args == ('/etc/sysconfig/clock', '^ZONE=.*', 'ZONE="TEST_TIMEZONE"') + """ + with patch.dict(timezone.__grains__, {"os_family": ["RedHat"]}): + assert timezone.set_hwclock("UTC") + name, args, kwargs = timezone.__salt__["file.sed"].mock_calls[0] + assert args == ("/etc/sysconfig/clock", "^ZONE=.*", 'ZONE="TEST_TIMEZONE"') - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) - @patch('salt.modules.timezone.get_zone', MagicMock(return_value='TEST_TIMEZONE')) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) + @patch("salt.modules.timezone.get_zone", MagicMock(return_value="TEST_TIMEZONE")) def test_set_hwclock_suse(self): - ''' + """ Test set hwclock on SUSE :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Suse']}): - assert timezone.set_hwclock('UTC') - name, args, kwargs = timezone.__salt__['file.sed'].mock_calls[0] - assert args == ('/etc/sysconfig/clock', '^TIMEZONE=.*', 'TIMEZONE="TEST_TIMEZONE"') + """ + with patch.dict(timezone.__grains__, {"os_family": ["Suse"]}): + assert timezone.set_hwclock("UTC") + name, args, kwargs = timezone.__salt__["file.sed"].mock_calls[0] + assert args == ( + "/etc/sysconfig/clock", + "^TIMEZONE=.*", + 'TIMEZONE="TEST_TIMEZONE"', + ) - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) - @patch('salt.modules.timezone.get_zone', MagicMock(return_value='TEST_TIMEZONE')) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) + @patch("salt.modules.timezone.get_zone", MagicMock(return_value="TEST_TIMEZONE")) def test_set_hwclock_debian(self): - ''' + """ Test set hwclock on Debian :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Debian']}): - assert timezone.set_hwclock('UTC') - name, args, kwargs = timezone.__salt__['file.sed'].mock_calls[0] - assert args == ('/etc/default/rcS', '^UTC=.*', 'UTC=yes') + """ + with patch.dict(timezone.__grains__, {"os_family": ["Debian"]}): + assert timezone.set_hwclock("UTC") + name, args, kwargs = timezone.__salt__["file.sed"].mock_calls[0] + assert args == ("/etc/default/rcS", "^UTC=.*", "UTC=yes") - assert timezone.set_hwclock('localtime') - name, args, kwargs = timezone.__salt__['file.sed'].mock_calls[1] - assert args == ('/etc/default/rcS', '^UTC=.*', 'UTC=no') + assert timezone.set_hwclock("localtime") + name, args, kwargs = timezone.__salt__["file.sed"].mock_calls[1] + assert args == ("/etc/default/rcS", "^UTC=.*", "UTC=no") - @skipIf(salt.utils.platform.is_windows(), 'os.symlink not available in Windows') - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('os.path.exists', MagicMock(return_value=True)) - @patch('os.unlink', MagicMock()) - @patch('os.symlink', MagicMock()) - @patch('salt.modules.timezone.get_zone', MagicMock(return_value='TEST_TIMEZONE')) + @skipIf(salt.utils.platform.is_windows(), "os.symlink not available in Windows") + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=True)) + @patch("os.unlink", MagicMock()) + @patch("os.symlink", MagicMock()) + @patch("salt.modules.timezone.get_zone", MagicMock(return_value="TEST_TIMEZONE")) def test_set_hwclock_gentoo(self): - ''' + """ Test set hwclock on Gentoo :return: - ''' - with patch.dict(timezone.__grains__, {'os_family': ['Gentoo']}): + """ + with patch.dict(timezone.__grains__, {"os_family": ["Gentoo"]}): with self.assertRaises(SaltInvocationError): - timezone.set_hwclock('forty two') + timezone.set_hwclock("forty two") - timezone.set_hwclock('UTC') - name, args, kwargs = timezone.__salt__['file.sed'].mock_calls[0] - assert args == ('/etc/conf.d/hwclock', '^clock=.*', 'clock="UTC"') + timezone.set_hwclock("UTC") + name, args, kwargs = timezone.__salt__["file.sed"].mock_calls[0] + assert args == ("/etc/conf.d/hwclock", "^clock=.*", 'clock="UTC"') - timezone.set_hwclock('localtime') - name, args, kwargs = timezone.__salt__['file.sed'].mock_calls[1] - assert args == ('/etc/conf.d/hwclock', '^clock=.*', 'clock="local"') + timezone.set_hwclock("localtime") + name, args, kwargs = timezone.__salt__["file.sed"].mock_calls[1] + assert args == ("/etc/conf.d/hwclock", "^clock=.*", 'clock="local"') diff --git a/tests/unit/modules/test_tls.py b/tests/unit/modules/test_tls.py index e22d6ecea4b..419cddd056b 100644 --- a/tests/unit/modules/test_tls.py +++ b/tests/unit/modules/test_tls.py @@ -1,42 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Joe Julian <me@joejulian.name> -''' +""" # Import the future -from __future__ import absolute_import, unicode_literals, print_function - +from __future__ import absolute_import, print_function, unicode_literals # Import Python libs import logging -try: - # We're not going to actually use OpenSSL, we just want to check that - # it's installed. - import OpenSSL # pylint: disable=unused-import - NO_PYOPENSSL = False -except Exception: # pylint: disable=broad-except - NO_PYOPENSSL = True -# Import Salt Testing Libs -from tests.support.helpers import with_tempdir -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - mock_open, - MagicMock, - patch, -) +# Import 3rd party Libs +import salt.ext as six # Import Salt Libs import salt.modules.tls as tls from salt.utils.versions import LooseVersion -# Import 3rd party Libs -import salt.ext as six +# Import Salt Testing Libs +from tests.support.helpers import with_tempdir +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase, skipIf + +try: + # We're not going to actually use OpenSSL, we just want to check that + # it's installed. + import OpenSSL # pylint: disable=unused-import + + NO_PYOPENSSL = False +except Exception: # pylint: disable=broad-except + NO_PYOPENSSL = True + log = logging.getLogger(__name__) _TLS_TEST_DATA = { - 'ca_cert': '''-----BEGIN CERTIFICATE----- + "ca_cert": """-----BEGIN CERTIFICATE----- MIIEejCCA2KgAwIBAgIRANW6IUG5rMez0vPi3cSmS3QwDQYJKoZIhvcNAQELBQAw eTELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFV0YWgxFzAVBgNVBAcMDlNhbHQgTGFr ZSBDaXR5MRIwEAYDVQQKDAlTYWx0U3RhY2sxEjAQBgNVBAMMCWxvY2FsaG9zdDEa @@ -61,8 +59,8 @@ CxGFTRqnQ+8bdhZA6LYQPXieGikjTy+P2oiKOvPnYsATUXLbZ3ee+zEgBFGbbxNX Argd3Vahg7Onu3ynsJz9a+hmwVqTX70Ykrrm+b/YtwKPfHeXTMxkX23jc4R7D+ED VvROFJ27hFPLVaJrsq3EHb8ZkQHmRCzK2sMIPyJb2e0BOKDEZEphNxiIjerDnH7n xDSMW0jK9FEv/W/sSBwoEolh3Q2e0gfd2vo7bEGvNF4eTqFsoAfeiAjWk65q0Q== ------END CERTIFICATE-----''', - 'ca_cert_key': '''-----BEGIN PRIVATE KEY----- +-----END CERTIFICATE-----""", + "ca_cert_key": """-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDbx3PC8KSOfzY sEU1mBt+FkOb2rvIJI/vKeSnoVwzZg3AmPEFvcTWgsWOgLYNg8IebxGnLmykXJ0C /UhSmB2uHXbZ5CEs0pHWTSJmO2VULXgyFTSZsuw62eEb1k69ns12C7/KDZKqyocp @@ -89,179 +87,190 @@ rF63gdoBlL5C3zXURaX1mFM7jG1E0wI22lDL4u8FAoGAaeghxaW4V0keS0KpdPvL kmEhQ5VDAG92fmzQa0wkBoTbsS4kitHj5QTfNJgdCDJbZ7srkMbQR7oVGeZKIDBk L7lyhyrtHaR/r/fdUNEJAVsQt3Shaa5c5srLh6vzGcBsV7/nQqrEP+GadvGSbHNT bymYbi0l2pWqQLA2sPoRHNw= ------END PRIVATE KEY-----''', - 'ca_signed_cert': ''' - ''', - 'ca_signed_key': ''' - ''', - 'create_ca': { - 'bits': 2048, - 'CN': 'localhost', - 'C': 'US', - 'ST': 'Utah', - 'L': 'Salt Lake City', - 'O': 'SaltStack', - 'OU': 'Test Unit', - 'emailAddress': 'xyz@pdq.net', - 'digest': 'sha256', - 'replace': False - } +-----END PRIVATE KEY-----""", + "ca_signed_cert": """ + """, + "ca_signed_key": """ + """, + "create_ca": { + "bits": 2048, + "CN": "localhost", + "C": "US", + "ST": "Utah", + "L": "Salt Lake City", + "O": "SaltStack", + "OU": "Test Unit", + "emailAddress": "xyz@pdq.net", + "digest": "sha256", + "replace": False, + }, } # Skip this test case if we don't have access to mock or PyOpenSSL. -@skipIf(NO_PYOPENSSL, 'PyOpenSSL must be installed to run these tests.') +@skipIf(NO_PYOPENSSL, "PyOpenSSL must be installed to run these tests.") class TLSAddTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.tls - ''' + """ + def setup_loader_modules(self): return {tls: {}} def test_cert_base_path(self): - ''' + """ Test for retrieving cert base path - ''' - ca_path = '/etc/tls' + """ + ca_path = "/etc/tls" mock_opt = MagicMock(return_value=ca_path) - with patch.dict(tls.__salt__, {'config.option': mock_opt}): + with patch.dict(tls.__salt__, {"config.option": mock_opt}): self.assertEqual(tls.cert_base_path(), ca_path) def test_set_ca_cert_path(self): - ''' + """ Test for setting the cert base path - ''' - ca_path = '/tmp/ca_cert_test_path' - mock_opt = MagicMock(return_value='/etc/tls') - ret = {'ca.contextual_cert_base_path': '/tmp/ca_cert_test_path'} - with patch.dict(tls.__salt__, {'config.option': mock_opt}): + """ + ca_path = "/tmp/ca_cert_test_path" + mock_opt = MagicMock(return_value="/etc/tls") + ret = {"ca.contextual_cert_base_path": "/tmp/ca_cert_test_path"} + with patch.dict(tls.__salt__, {"config.option": mock_opt}): tls.set_ca_path(ca_path) self.assertEqual(tls.__context__, ret) def test_ca_exists(self): - ''' + """ Test to see if ca does not exist - ''' - ca_path = '/tmp/test_tls' - ca_name = 'test_ca' + """ + ca_path = "/tmp/test_tls" + ca_name = "test_ca" mock_opt = MagicMock(return_value=ca_path) - with patch.dict(tls.__salt__, {'config.option': mock_opt}), \ - patch('os.path.exists', MagicMock(return_value=False)), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict(tls.__salt__, {"config.option": mock_opt}), patch( + "os.path.exists", MagicMock(return_value=False) + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): self.assertFalse(tls.ca_exists(ca_name)) def test_ca_exists_true(self): - ''' + """ Test to see if ca exists - ''' - ca_path = '/tmp/test_tls' - ca_name = 'test_ca' + """ + ca_path = "/tmp/test_tls" + ca_name = "test_ca" mock_opt = MagicMock(return_value=ca_path) - with patch.dict(tls.__salt__, {'config.option': mock_opt}), \ - patch('os.path.exists', MagicMock(return_value=True)), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict(tls.__salt__, {"config.option": mock_opt}), patch( + "os.path.exists", MagicMock(return_value=True) + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): self.assertTrue(tls.ca_exists(ca_name)) def test_get_ca_fail(self): - ''' + """ Test get_ca failure - ''' - ca_path = '/tmp/test_tls' - ca_name = 'test_ca' + """ + ca_path = "/tmp/test_tls" + ca_name = "test_ca" mock_opt = MagicMock(return_value=ca_path) - with patch.dict(tls.__salt__, {'config.option': mock_opt}), \ - patch('os.path.exists', MagicMock(return_value=False)), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict(tls.__salt__, {"config.option": mock_opt}), patch( + "os.path.exists", MagicMock(return_value=False) + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): self.assertRaises(ValueError, tls.get_ca, ca_name) def test_get_ca_text(self): - ''' + """ Test get_ca text - ''' - ca_path = '/tmp/test_tls' - ca_name = 'test_ca' + """ + ca_path = "/tmp/test_tls" + ca_name = "test_ca" mock_opt = MagicMock(return_value=ca_path) - with patch('salt.utils.files.fopen', - mock_open(read_data=_TLS_TEST_DATA['ca_cert'])), \ - patch.dict(tls.__salt__, {'config.option': mock_opt}), \ - patch('os.path.exists', MagicMock(return_value=True)), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): - self.assertEqual(tls.get_ca(ca_name, as_text=True), - _TLS_TEST_DATA['ca_cert']) + with patch( + "salt.utils.files.fopen", mock_open(read_data=_TLS_TEST_DATA["ca_cert"]) + ), patch.dict(tls.__salt__, {"config.option": mock_opt}), patch( + "os.path.exists", MagicMock(return_value=True) + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): + self.assertEqual( + tls.get_ca(ca_name, as_text=True), _TLS_TEST_DATA["ca_cert"] + ) def test_get_ca(self): - ''' + """ Test get_ca - ''' - ca_path = '/tmp/test_tls' - ca_name = 'test_ca' - certp = '{0}/{1}/{1}_ca_cert.crt'.format(ca_path, ca_name) + """ + ca_path = "/tmp/test_tls" + ca_name = "test_ca" + certp = "{0}/{1}/{1}_ca_cert.crt".format(ca_path, ca_name) mock_opt = MagicMock(return_value=ca_path) - with patch.dict(tls.__salt__, {'config.option': mock_opt}), \ - patch('os.path.exists', MagicMock(return_value=True)), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict(tls.__salt__, {"config.option": mock_opt}), patch( + "os.path.exists", MagicMock(return_value=True) + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): self.assertEqual(tls.get_ca(ca_name), certp) def test_cert_info(self): - ''' + """ Test cert info - ''' + """ self.maxDiff = None - with patch('os.path.exists', MagicMock(return_value=True)), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): - ca_path = '/tmp/test_tls' - ca_name = 'test_ca' - certp = '{0}/{1}/{1}_ca_cert.crt'.format(ca_path, ca_name) + with patch("os.path.exists", MagicMock(return_value=True)), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): + ca_path = "/tmp/test_tls" + ca_name = "test_ca" + certp = "{0}/{1}/{1}_ca_cert.crt".format(ca_path, ca_name) ret = { - 'not_after': 1462379961, - 'signature_algorithm': 'sha256WithRSAEncryption', - 'extensions': None, - 'fingerprint': ('96:72:B3:0A:1D:34:37:05:75:57:44:7E:08:81:A7:09:' - '0C:E1:8F:5F:4D:0C:49:CE:5B:D2:6B:45:D3:4D:FF:31'), - 'serial_number': 284092004844685647925744086791559203700, - 'subject': { - 'C': 'US', - 'CN': 'localhost', - 'L': 'Salt Lake City', - 'O': 'SaltStack', - 'ST': 'Utah', - 'emailAddress': - 'xyz@pdq.net'}, - 'not_before': 1430843961, - 'issuer': { - 'C': 'US', - 'CN': 'localhost', - 'L': 'Salt Lake City', - 'O': 'SaltStack', - 'ST': 'Utah', - 'emailAddress': 'xyz@pdq.net'} + "not_after": 1462379961, + "signature_algorithm": "sha256WithRSAEncryption", + "extensions": None, + "fingerprint": ( + "96:72:B3:0A:1D:34:37:05:75:57:44:7E:08:81:A7:09:" + "0C:E1:8F:5F:4D:0C:49:CE:5B:D2:6B:45:D3:4D:FF:31" + ), + "serial_number": 284092004844685647925744086791559203700, + "subject": { + "C": "US", + "CN": "localhost", + "L": "Salt Lake City", + "O": "SaltStack", + "ST": "Utah", + "emailAddress": "xyz@pdq.net", + }, + "not_before": 1430843961, + "issuer": { + "C": "US", + "CN": "localhost", + "L": "Salt Lake City", + "O": "SaltStack", + "ST": "Utah", + "emailAddress": "xyz@pdq.net", + }, } def ignore_extensions(data): - ''' + """ Ignore extensions pending a resolution of issue 24338 - ''' - if 'extensions' in data.keys(): - data['extensions'] = None + """ + if "extensions" in data.keys(): + data["extensions"] = None return data # older pyopenssl versions don't have extensions or # signature_algorithms def remove_not_in_result(source, reference): - if 'signature_algorithm' not in reference: - del source['signature_algorithm'] - if 'extensions' not in reference: - del source['extensions'] + if "signature_algorithm" not in reference: + del source["signature_algorithm"] + if "extensions" not in reference: + del source["extensions"] - with patch('salt.utils.files.fopen', - mock_open(read_data=_TLS_TEST_DATA['ca_cert'])): + with patch( + "salt.utils.files.fopen", mock_open(read_data=_TLS_TEST_DATA["ca_cert"]) + ): try: result = ignore_extensions(tls.cert_info(certp)) except AttributeError as err: @@ -269,27 +278,33 @@ class TLSAddTestCase(TestCase, LoaderModuleMockMixin): # in OpenSSL/crypto.py in the get_signature_algorithm function referencing # the cert_info attribute, which doesn't exist. This was fixed in subsequent # releases of PyOpenSSL with https://github.com/pyca/pyopenssl/pull/476 - if '\'_cffi_backend.CDataGCP\' object has no attribute \'cert_info\'' == six.text_type(err): + if ( + "'_cffi_backend.CDataGCP' object has no attribute 'cert_info'" + == six.text_type(err) + ): log.exception(err) self.skipTest( - 'Encountered an upstream error with PyOpenSSL: {0}'.format( + "Encountered an upstream error with PyOpenSSL: {0}".format( err ) ) - if '\'_cffi_backend.CDataGCP\' object has no attribute \'object\'' == str(err): + if ( + "'_cffi_backend.CDataGCP' object has no attribute 'object'" + == str(err) + ): log.exception(err) self.skipTest( - 'Encountered an upstream error with PyOpenSSL: {0}'.format( + "Encountered an upstream error with PyOpenSSL: {0}".format( err ) ) # python-openssl version 0.14, when installed with the "junos-eznc" pip # package, causes an error on this test. Newer versions of PyOpenSSL do not have # this issue. If 0.14 is installed and we hit this error, skip the test. - if LooseVersion(OpenSSL.__version__) == LooseVersion('0.14'): + if LooseVersion(OpenSSL.__version__) == LooseVersion("0.14"): log.exception(err) self.skipTest( - 'Encountered a package conflict. OpenSSL version 0.14 cannot be used with ' + "Encountered a package conflict. OpenSSL version 0.14 cannot be used with " 'the "junos-eznc" pip package on this test. Skipping.' ) result = {} @@ -299,463 +314,516 @@ class TLSAddTestCase(TestCase, LoaderModuleMockMixin): @with_tempdir() def test_create_ca(self, ca_path): - ''' + """ Test creating CA cert - ''' - ca_name = 'test_ca' - certp = '{0}/{1}/{1}_ca_cert.crt'.format(ca_path, ca_name) - certk = '{0}/{1}/{1}_ca_cert.key'.format(ca_path, ca_name) + """ + ca_name = "test_ca" + certp = "{0}/{1}/{1}_ca_cert.crt".format(ca_path, ca_name) + certk = "{0}/{1}/{1}_ca_cert.key".format(ca_path, ca_name) ret = 'Created Private Key: "{0}." Created CA "{1}": "{2}."'.format( - certk, ca_name, certp) + certk, ca_name, certp + ) mock_opt = MagicMock(return_value=ca_path) mock_ret = MagicMock(return_value=0) - with patch.dict(tls.__salt__, {'config.option': mock_opt, 'cmd.retcode': mock_ret}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', 'cachedir': ca_path}), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict( + tls.__salt__, {"config.option": mock_opt, "cmd.retcode": mock_ret} + ), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): self.assertEqual( tls.create_ca( - ca_name, - days=365, - fixmode=False, - **_TLS_TEST_DATA['create_ca']), - ret) + ca_name, days=365, fixmode=False, **_TLS_TEST_DATA["create_ca"] + ), + ret, + ) @with_tempdir() def test_recreate_ca(self, ca_path): - ''' + """ Test creating CA cert when one already exists - ''' - ca_name = 'test_ca' - certp = '{0}/{1}/{1}_ca_cert.crt'.format(ca_path, ca_name) - certk = '{0}/{1}/{1}_ca_cert.key'.format(ca_path, ca_name) + """ + ca_name = "test_ca" + certp = "{0}/{1}/{1}_ca_cert.crt".format(ca_path, ca_name) + certk = "{0}/{1}/{1}_ca_cert.key".format(ca_path, ca_name) ret = 'Created Private Key: "{0}." Created CA "{1}": "{2}."'.format( - certk, ca_name, certp) + certk, ca_name, certp + ) mock_opt = MagicMock(return_value=ca_path) mock_ret = MagicMock(return_value=0) - with patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)), \ - patch.dict(tls.__salt__, {'config.option': mock_opt, 'cmd.retcode': mock_ret}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', 'cachedir': ca_path}), \ - patch.dict(_TLS_TEST_DATA['create_ca'], {'replace': True}): + with patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ), patch.dict( + tls.__salt__, {"config.option": mock_opt, "cmd.retcode": mock_ret} + ), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch.dict( + _TLS_TEST_DATA["create_ca"], {"replace": True} + ): tls.create_ca(ca_name) self.assertEqual( tls.create_ca( - ca_name, - days=365, - fixmode=False, - **_TLS_TEST_DATA['create_ca']), - ret) + ca_name, days=365, fixmode=False, **_TLS_TEST_DATA["create_ca"] + ), + ret, + ) @with_tempdir() def test_create_csr(self, ca_path): - ''' + """ Test creating certificate signing request - ''' - ca_name = 'test_ca' - certp = '{0}/{1}/certs/{2}.csr'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) - certk = '{0}/{1}/certs/{2}.key'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) - ret = ('Created Private Key: "{0}." ' - 'Created CSR for "{1}": "{2}."').format( - certk, _TLS_TEST_DATA['create_ca']['CN'], certp) + """ + ca_name = "test_ca" + certp = "{0}/{1}/certs/{2}.csr".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) + certk = "{0}/{1}/certs/{2}.key".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) + ret = ('Created Private Key: "{0}." ' 'Created CSR for "{1}": "{2}."').format( + certk, _TLS_TEST_DATA["create_ca"]["CN"], certp + ) mock_opt = MagicMock(return_value=ca_path) mock_ret = MagicMock(return_value=0) mock_pgt = MagicMock(return_value=False) - with patch.dict(tls.__salt__, {'config.option': mock_opt, 'cmd.retcode': mock_ret, 'pillar.get': mock_pgt}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', 'cachedir': ca_path}), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict( + tls.__salt__, + { + "config.option": mock_opt, + "cmd.retcode": mock_ret, + "pillar.get": mock_pgt, + }, + ), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): tls.create_ca(ca_name) self.assertEqual( - tls.create_csr( - ca_name, - **_TLS_TEST_DATA['create_ca']), - ret) + tls.create_csr(ca_name, **_TLS_TEST_DATA["create_ca"]), ret + ) @with_tempdir() def test_recreate_csr(self, ca_path): - ''' + """ Test creating certificate signing request when one already exists - ''' - ca_name = 'test_ca' - certp = '{0}/{1}/certs/{2}.csr'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) - certk = '{0}/{1}/certs/{2}.key'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) - ret = ('Created Private Key: "{0}." ' - 'Created CSR for "{1}": "{2}."').format( - certk, _TLS_TEST_DATA['create_ca']['CN'], certp) + """ + ca_name = "test_ca" + certp = "{0}/{1}/certs/{2}.csr".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) + certk = "{0}/{1}/certs/{2}.key".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) + ret = ('Created Private Key: "{0}." ' 'Created CSR for "{1}": "{2}."').format( + certk, _TLS_TEST_DATA["create_ca"]["CN"], certp + ) mock_opt = MagicMock(return_value=ca_path) mock_ret = MagicMock(return_value=0) mock_pgt = MagicMock(return_value=False) - with patch.dict(tls.__salt__, {'config.option': mock_opt, - 'cmd.retcode': mock_ret, - 'pillar.get': mock_pgt}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', - 'cachedir': ca_path}), \ - patch.dict(_TLS_TEST_DATA['create_ca'], {'replace': True}), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict( + tls.__salt__, + { + "config.option": mock_opt, + "cmd.retcode": mock_ret, + "pillar.get": mock_pgt, + }, + ), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch.dict( + _TLS_TEST_DATA["create_ca"], {"replace": True} + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): tls.create_ca(ca_name) tls.create_csr(ca_name) self.assertEqual( - tls.create_csr( - ca_name, - **_TLS_TEST_DATA['create_ca']), - ret) + tls.create_csr(ca_name, **_TLS_TEST_DATA["create_ca"]), ret + ) @with_tempdir() def test_create_self_signed_cert(self, ca_path): - ''' + """ Test creating self signed certificate - ''' - tls_dir = 'test_tls' - certp = '{0}/{1}/certs/{2}.crt'.format( - ca_path, - tls_dir, - _TLS_TEST_DATA['create_ca']['CN']) - certk = '{0}/{1}/certs/{2}.key'.format( - ca_path, - tls_dir, - _TLS_TEST_DATA['create_ca']['CN']) - ret = ('Created Private Key: "{0}." ' - 'Created Certificate: "{1}."').format( - certk, certp) + """ + tls_dir = "test_tls" + certp = "{0}/{1}/certs/{2}.crt".format( + ca_path, tls_dir, _TLS_TEST_DATA["create_ca"]["CN"] + ) + certk = "{0}/{1}/certs/{2}.key".format( + ca_path, tls_dir, _TLS_TEST_DATA["create_ca"]["CN"] + ) + ret = ('Created Private Key: "{0}." ' 'Created Certificate: "{1}."').format( + certk, certp + ) mock_opt = MagicMock(return_value=ca_path) - with patch.dict(tls.__salt__, {'config.option': mock_opt}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', - 'cachedir': ca_path}), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict(tls.__salt__, {"config.option": mock_opt}), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): self.assertEqual( tls.create_self_signed_cert( - tls_dir=tls_dir, - days=365, - **_TLS_TEST_DATA['create_ca']), - ret) + tls_dir=tls_dir, days=365, **_TLS_TEST_DATA["create_ca"] + ), + ret, + ) @with_tempdir() def test_recreate_self_signed_cert(self, ca_path): - ''' + """ Test creating self signed certificate when one already exists - ''' - tls_dir = 'test_tls' - certp = '{0}/{1}/certs/{2}.crt'.format( - ca_path, - tls_dir, - _TLS_TEST_DATA['create_ca']['CN']) - certk = '{0}/{1}/certs/{2}.key'.format( - ca_path, - tls_dir, - _TLS_TEST_DATA['create_ca']['CN']) - ret = ('Created Private Key: "{0}." ' - 'Created Certificate: "{1}."').format( - certk, certp) + """ + tls_dir = "test_tls" + certp = "{0}/{1}/certs/{2}.crt".format( + ca_path, tls_dir, _TLS_TEST_DATA["create_ca"]["CN"] + ) + certk = "{0}/{1}/certs/{2}.key".format( + ca_path, tls_dir, _TLS_TEST_DATA["create_ca"]["CN"] + ) + ret = ('Created Private Key: "{0}." ' 'Created Certificate: "{1}."').format( + certk, certp + ) mock_opt = MagicMock(return_value=ca_path) - with patch.dict(tls.__salt__, {'config.option': mock_opt}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', - 'cachedir': ca_path}), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict(tls.__salt__, {"config.option": mock_opt}), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): self.assertEqual( tls.create_self_signed_cert( - tls_dir=tls_dir, - days=365, - **_TLS_TEST_DATA['create_ca']), - ret) + tls_dir=tls_dir, days=365, **_TLS_TEST_DATA["create_ca"] + ), + ret, + ) @with_tempdir() def test_create_ca_signed_cert(self, ca_path): - ''' + """ Test signing certificate from request - ''' - ca_name = 'test_ca' - certp = '{0}/{1}/certs/{2}.crt'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) + """ + ca_name = "test_ca" + certp = "{0}/{1}/certs/{2}.crt".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) ret = 'Created Certificate for "{0}": "{1}"'.format( - _TLS_TEST_DATA['create_ca']['CN'], certp) + _TLS_TEST_DATA["create_ca"]["CN"], certp + ) mock_opt = MagicMock(return_value=ca_path) mock_ret = MagicMock(return_value=0) mock_pgt = MagicMock(return_value=False) - with patch.dict(tls.__salt__, {'config.option': mock_opt, - 'cmd.retcode': mock_ret, - 'pillar.get': mock_pgt}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', - 'cachedir': ca_path}), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict( + tls.__salt__, + { + "config.option": mock_opt, + "cmd.retcode": mock_ret, + "pillar.get": mock_pgt, + }, + ), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): tls.create_ca(ca_name) - tls.create_csr(ca_name, **_TLS_TEST_DATA['create_ca']) + tls.create_csr(ca_name, **_TLS_TEST_DATA["create_ca"]) self.assertEqual( - tls.create_ca_signed_cert( - ca_name, - _TLS_TEST_DATA['create_ca']['CN']), - ret) + tls.create_ca_signed_cert(ca_name, _TLS_TEST_DATA["create_ca"]["CN"]), + ret, + ) @with_tempdir() def test_recreate_ca_signed_cert(self, ca_path): - ''' + """ Test signing certificate from request when certificate exists - ''' - ca_name = 'test_ca' - certp = '{0}/{1}/certs/{2}.crt'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) + """ + ca_name = "test_ca" + certp = "{0}/{1}/certs/{2}.crt".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) ret = 'Created Certificate for "{0}": "{1}"'.format( - _TLS_TEST_DATA['create_ca']['CN'], certp) + _TLS_TEST_DATA["create_ca"]["CN"], certp + ) mock_opt = MagicMock(return_value=ca_path) mock_ret = MagicMock(return_value=0) mock_pgt = MagicMock(return_value=False) - with patch.dict(tls.__salt__, {'config.option': mock_opt, - 'cmd.retcode': mock_ret, - 'pillar.get': mock_pgt}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', - 'cachedir': ca_path}), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict( + tls.__salt__, + { + "config.option": mock_opt, + "cmd.retcode": mock_ret, + "pillar.get": mock_pgt, + }, + ), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): tls.create_ca(ca_name) tls.create_csr(ca_name) - tls.create_ca_signed_cert(ca_name, - _TLS_TEST_DATA['create_ca']['CN']) + tls.create_ca_signed_cert(ca_name, _TLS_TEST_DATA["create_ca"]["CN"]) self.assertEqual( tls.create_ca_signed_cert( - ca_name, - _TLS_TEST_DATA['create_ca']['CN'], - replace=True), - ret) + ca_name, _TLS_TEST_DATA["create_ca"]["CN"], replace=True + ), + ret, + ) @with_tempdir() def test_create_pkcs12(self, ca_path): - ''' + """ Test creating pkcs12 - ''' - ca_name = 'test_ca' - certp = '{0}/{1}/certs/{2}.p12'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) + """ + ca_name = "test_ca" + certp = "{0}/{1}/certs/{2}.p12".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) ret = 'Created PKCS#12 Certificate for "{0}": "{1}"'.format( - _TLS_TEST_DATA['create_ca']['CN'], certp) + _TLS_TEST_DATA["create_ca"]["CN"], certp + ) mock_opt = MagicMock(return_value=ca_path) mock_ret = MagicMock(return_value=0) mock_pgt = MagicMock(return_value=False) - with patch.dict(tls.__salt__, {'config.option': mock_opt, - 'cmd.retcode': mock_ret, - 'pillar.get': mock_pgt}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', - 'cachedir': ca_path}), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict( + tls.__salt__, + { + "config.option": mock_opt, + "cmd.retcode": mock_ret, + "pillar.get": mock_pgt, + }, + ), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): tls.create_ca(ca_name) - tls.create_csr(ca_name, **_TLS_TEST_DATA['create_ca']) - tls.create_ca_signed_cert(ca_name, - _TLS_TEST_DATA['create_ca']['CN']) + tls.create_csr(ca_name, **_TLS_TEST_DATA["create_ca"]) + tls.create_ca_signed_cert(ca_name, _TLS_TEST_DATA["create_ca"]["CN"]) self.assertEqual( - tls.create_pkcs12(ca_name, - _TLS_TEST_DATA['create_ca']['CN'], - 'password'), - ret) + tls.create_pkcs12( + ca_name, _TLS_TEST_DATA["create_ca"]["CN"], "password" + ), + ret, + ) @with_tempdir() def test_recreate_pkcs12(self, ca_path): - ''' + """ Test creating pkcs12 when it already exists - ''' - ca_name = 'test_ca' - certp = '{0}/{1}/certs/{2}.p12'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) + """ + ca_name = "test_ca" + certp = "{0}/{1}/certs/{2}.p12".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) ret = 'Created PKCS#12 Certificate for "{0}": "{1}"'.format( - _TLS_TEST_DATA['create_ca']['CN'], certp) + _TLS_TEST_DATA["create_ca"]["CN"], certp + ) mock_opt = MagicMock(return_value=ca_path) mock_ret = MagicMock(return_value=0) mock_pgt = MagicMock(return_value=False) - with patch.dict(tls.__salt__, {'config.option': mock_opt, - 'cmd.retcode': mock_ret, - 'pillar.get': mock_pgt}), \ - patch.dict(tls.__opts__, {'hash_type': 'sha256', - 'cachedir': ca_path}), \ - patch.dict(_TLS_TEST_DATA['create_ca'], {'replace': True}), \ - patch('salt.modules.tls.maybe_fix_ssl_version', - MagicMock(return_value=True)): + with patch.dict( + tls.__salt__, + { + "config.option": mock_opt, + "cmd.retcode": mock_ret, + "pillar.get": mock_pgt, + }, + ), patch.dict( + tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path} + ), patch.dict( + _TLS_TEST_DATA["create_ca"], {"replace": True} + ), patch( + "salt.modules.tls.maybe_fix_ssl_version", MagicMock(return_value=True) + ): tls.create_ca(ca_name) tls.create_csr(ca_name) - tls.create_ca_signed_cert(ca_name, - _TLS_TEST_DATA['create_ca']['CN']) - tls.create_pkcs12(ca_name, - _TLS_TEST_DATA['create_ca']['CN'], - 'password') + tls.create_ca_signed_cert(ca_name, _TLS_TEST_DATA["create_ca"]["CN"]) + tls.create_pkcs12(ca_name, _TLS_TEST_DATA["create_ca"]["CN"], "password") self.assertEqual( - tls.create_pkcs12(ca_name, - _TLS_TEST_DATA[ - 'create_ca']['CN'], - 'password', - replace=True), - ret) + tls.create_pkcs12( + ca_name, _TLS_TEST_DATA["create_ca"]["CN"], "password", replace=True + ), + ret, + ) def test_pyOpenSSL_version(self): - ''' + """ Test extension logic with different pyOpenSSL versions - ''' - pillarval = {'csr': {'extendedKeyUsage': 'serverAuth'}} + """ + pillarval = {"csr": {"extendedKeyUsage": "serverAuth"}} mock_pgt = MagicMock(return_value=pillarval) - with patch.dict(tls.__dict__, { - 'OpenSSL_version': LooseVersion('0.1.1'), - 'X509_EXT_ENABLED': False}): - self.assertEqual(tls.__virtual__(), - (False, 'PyOpenSSL version 0.10 or later must be installed ' - 'before this module can be used.')) - with patch.dict(tls.__salt__, {'pillar.get': mock_pgt}): - self.assertRaises(AssertionError, tls.get_extensions, 'server') - self.assertRaises(AssertionError, tls.get_extensions, 'client') - with patch.dict(tls.__dict__, { - 'OpenSSL_version': LooseVersion('0.14.1'), - 'X509_EXT_ENABLED': True}): + with patch.dict( + tls.__dict__, + {"OpenSSL_version": LooseVersion("0.1.1"), "X509_EXT_ENABLED": False}, + ): + self.assertEqual( + tls.__virtual__(), + ( + False, + "PyOpenSSL version 0.10 or later must be installed " + "before this module can be used.", + ), + ) + with patch.dict(tls.__salt__, {"pillar.get": mock_pgt}): + self.assertRaises(AssertionError, tls.get_extensions, "server") + self.assertRaises(AssertionError, tls.get_extensions, "client") + with patch.dict( + tls.__dict__, + {"OpenSSL_version": LooseVersion("0.14.1"), "X509_EXT_ENABLED": True}, + ): self.assertTrue(tls.__virtual__()) - with patch.dict(tls.__salt__, {'pillar.get': mock_pgt}): - self.assertEqual(tls.get_extensions('server'), pillarval) - self.assertEqual(tls.get_extensions('client'), pillarval) - with patch.dict(tls.__dict__, { - 'OpenSSL_version': LooseVersion('0.15.1'), - 'X509_EXT_ENABLED': True}): + with patch.dict(tls.__salt__, {"pillar.get": mock_pgt}): + self.assertEqual(tls.get_extensions("server"), pillarval) + self.assertEqual(tls.get_extensions("client"), pillarval) + with patch.dict( + tls.__dict__, + {"OpenSSL_version": LooseVersion("0.15.1"), "X509_EXT_ENABLED": True}, + ): self.assertTrue(tls.__virtual__()) - with patch.dict(tls.__salt__, {'pillar.get': mock_pgt}): - self.assertEqual(tls.get_extensions('server'), pillarval) - self.assertEqual(tls.get_extensions('client'), pillarval) + with patch.dict(tls.__salt__, {"pillar.get": mock_pgt}): + self.assertEqual(tls.get_extensions("server"), pillarval) + self.assertEqual(tls.get_extensions("client"), pillarval) @with_tempdir() def test_pyOpenSSL_version_destructive(self, ca_path): - ''' + """ Test extension logic with different pyOpenSSL versions - ''' - pillarval = {'csr': {'extendedKeyUsage': 'serverAuth'}} + """ + pillarval = {"csr": {"extendedKeyUsage": "serverAuth"}} mock_pgt = MagicMock(return_value=pillarval) - ca_name = 'test_ca' - certp = '{0}/{1}/{1}_ca_cert.crt'.format(ca_path, ca_name) - certk = '{0}/{1}/{1}_ca_cert.key'.format(ca_path, ca_name) + ca_name = "test_ca" + certp = "{0}/{1}/{1}_ca_cert.crt".format(ca_path, ca_name) + certk = "{0}/{1}/{1}_ca_cert.key".format(ca_path, ca_name) ret = 'Created Private Key: "{0}." Created CA "{1}": "{2}."'.format( - certk, ca_name, certp) + certk, ca_name, certp + ) mock_opt = MagicMock(return_value=ca_path) mock_ret = MagicMock(return_value=0) - with patch.dict(tls.__salt__, { - 'config.option': mock_opt, - 'cmd.retcode': mock_ret}): - with patch.dict(tls.__opts__, { - 'hash_type': 'sha256', - 'cachedir': ca_path}): - with patch.dict(_TLS_TEST_DATA['create_ca'], - {'replace': True}): - with patch.dict(tls.__dict__, { - 'OpenSSL_version': - LooseVersion('0.1.1'), - 'X509_EXT_ENABLED': False}): + with patch.dict( + tls.__salt__, {"config.option": mock_opt, "cmd.retcode": mock_ret} + ): + with patch.dict(tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path}): + with patch.dict(_TLS_TEST_DATA["create_ca"], {"replace": True}): + with patch.dict( + tls.__dict__, + { + "OpenSSL_version": LooseVersion("0.1.1"), + "X509_EXT_ENABLED": False, + }, + ): self.assertEqual( tls.create_ca( ca_name, days=365, fixmode=False, - **_TLS_TEST_DATA['create_ca']), - ret) - with patch.dict(tls.__dict__, { - 'OpenSSL_version': - LooseVersion('0.14.1'), - 'X509_EXT_ENABLED': True}): + **_TLS_TEST_DATA["create_ca"] + ), + ret, + ) + with patch.dict( + tls.__dict__, + { + "OpenSSL_version": LooseVersion("0.14.1"), + "X509_EXT_ENABLED": True, + }, + ): self.assertEqual( tls.create_ca( ca_name, days=365, fixmode=False, - **_TLS_TEST_DATA['create_ca']), - ret) - with patch.dict(tls.__dict__, { - 'OpenSSL_version': - LooseVersion('0.15.1'), - 'X509_EXT_ENABLED': True}): + **_TLS_TEST_DATA["create_ca"] + ), + ret, + ) + with patch.dict( + tls.__dict__, + { + "OpenSSL_version": LooseVersion("0.15.1"), + "X509_EXT_ENABLED": True, + }, + ): self.assertEqual( tls.create_ca( ca_name, days=365, fixmode=False, - **_TLS_TEST_DATA['create_ca']), - ret) + **_TLS_TEST_DATA["create_ca"] + ), + ret, + ) - certp = '{0}/{1}/certs/{2}.csr'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) - certk = '{0}/{1}/certs/{2}.key'.format( - ca_path, - ca_name, - _TLS_TEST_DATA['create_ca']['CN']) - ret = ('Created Private Key: "{0}." ' - 'Created CSR for "{1}": "{2}."').format( - certk, _TLS_TEST_DATA['create_ca']['CN'], certp) - with patch.dict(tls.__salt__, { - 'config.option': mock_opt, - 'cmd.retcode': mock_ret, - 'pillar.get': mock_pgt}): - with patch.dict(tls.__opts__, {'hash_type': 'sha256', - 'cachedir': ca_path}): - with patch.dict(_TLS_TEST_DATA['create_ca'], { - 'subjectAltName': 'DNS:foo.bar', - 'replace': True}): - with patch.dict(tls.__dict__, { - 'OpenSSL_version': - LooseVersion('0.1.1'), - 'X509_EXT_ENABLED': False}): + certp = "{0}/{1}/certs/{2}.csr".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) + certk = "{0}/{1}/certs/{2}.key".format( + ca_path, ca_name, _TLS_TEST_DATA["create_ca"]["CN"] + ) + ret = ('Created Private Key: "{0}." ' 'Created CSR for "{1}": "{2}."').format( + certk, _TLS_TEST_DATA["create_ca"]["CN"], certp + ) + with patch.dict( + tls.__salt__, + { + "config.option": mock_opt, + "cmd.retcode": mock_ret, + "pillar.get": mock_pgt, + }, + ): + with patch.dict(tls.__opts__, {"hash_type": "sha256", "cachedir": ca_path}): + with patch.dict( + _TLS_TEST_DATA["create_ca"], + {"subjectAltName": "DNS:foo.bar", "replace": True}, + ): + with patch.dict( + tls.__dict__, + { + "OpenSSL_version": LooseVersion("0.1.1"), + "X509_EXT_ENABLED": False, + }, + ): tls.create_ca(ca_name) tls.create_csr(ca_name) - self.assertRaises(ValueError, - tls.create_csr, - ca_name, - **_TLS_TEST_DATA['create_ca']) - with patch.dict(tls.__dict__, { - 'OpenSSL_version': - LooseVersion('0.14.1'), - 'X509_EXT_ENABLED': True}): + self.assertRaises( + ValueError, + tls.create_csr, + ca_name, + **_TLS_TEST_DATA["create_ca"] + ) + with patch.dict( + tls.__dict__, + { + "OpenSSL_version": LooseVersion("0.14.1"), + "X509_EXT_ENABLED": True, + }, + ): tls.create_ca(ca_name) tls.create_csr(ca_name) self.assertEqual( - tls.create_csr( - ca_name, - **_TLS_TEST_DATA['create_ca']), - ret) - with patch.dict(tls.__dict__, { - 'OpenSSL_version': - LooseVersion('0.15.1'), - 'X509_EXT_ENABLED': True}): + tls.create_csr(ca_name, **_TLS_TEST_DATA["create_ca"]), ret + ) + with patch.dict( + tls.__dict__, + { + "OpenSSL_version": LooseVersion("0.15.1"), + "X509_EXT_ENABLED": True, + }, + ): tls.create_ca(ca_name) tls.create_csr(ca_name) self.assertEqual( - tls.create_csr( - ca_name, - **_TLS_TEST_DATA['create_ca']), - ret) + tls.create_csr(ca_name, **_TLS_TEST_DATA["create_ca"]), ret + ) def test_get_expiration_date(self): - with patch('salt.utils.files.fopen', - mock_open(read_data=_TLS_TEST_DATA['ca_cert'])): + with patch( + "salt.utils.files.fopen", mock_open(read_data=_TLS_TEST_DATA["ca_cert"]) + ): + self.assertEqual(tls.get_expiration_date("/path/to/cert"), "2016-05-04") + with patch( + "salt.utils.files.fopen", mock_open(read_data=_TLS_TEST_DATA["ca_cert"]) + ): self.assertEqual( - tls.get_expiration_date('/path/to/cert'), - '2016-05-04' - ) - with patch('salt.utils.files.fopen', - mock_open(read_data=_TLS_TEST_DATA['ca_cert'])): - self.assertEqual( - tls.get_expiration_date('/path/to/cert', date_format='%d/%m/%y'), - '04/05/16' + tls.get_expiration_date("/path/to/cert", date_format="%d/%m/%y"), + "04/05/16", ) diff --git a/tests/unit/modules/test_tuned.py b/tests/unit/modules/test_tuned.py index b71077476b7..f5a18693bef 100644 --- a/tests/unit/modules/test_tuned.py +++ b/tests/unit/modules/test_tuned.py @@ -2,28 +2,25 @@ from __future__ import absolute_import from salt.modules import tuned - from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class TunedListTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test the tuned.list_() method for different versions of tuned-adm - ''' + """ + def setup_loader_modules(self): return {tuned: {}} def test_v_241(self): - ''' + """ Test the list_ function for older tuned-adm (v2.4.1) as shipped with CentOS-6 - ''' - tuned_list = '''Available profiles: + """ + tuned_list = """Available profiles: - throughput-performance - virtual-guest - latency-performance @@ -36,23 +33,33 @@ class TunedListTestCase(TestCase, LoaderModuleMockMixin): - sap - enterprise-storage - default -Current active profile: throughput-performance''' +Current active profile: throughput-performance""" mock_cmd = MagicMock(return_value=tuned_list) - with patch.dict(tuned.__salt__, {'cmd.run': mock_cmd}): + with patch.dict(tuned.__salt__, {"cmd.run": mock_cmd}): self.assertEqual( tuned.list_(), - ['throughput-performance', 'virtual-guest', - 'latency-performance', 'laptop-battery-powersave', - 'laptop-ac-powersave', 'virtual-host', - 'desktop-powersave', 'server-powersave', - 'spindown-disk', 'sap', 'enterprise-storage', 'default']) + [ + "throughput-performance", + "virtual-guest", + "latency-performance", + "laptop-battery-powersave", + "laptop-ac-powersave", + "virtual-host", + "desktop-powersave", + "server-powersave", + "spindown-disk", + "sap", + "enterprise-storage", + "default", + ], + ) def test_v_271(self): - ''' + """ Test the list_ function for newer tuned-adm (v2.7.1) as shipped with CentOS-7 - ''' - tuned_list = '''Available profiles: + """ + tuned_list = """Available profiles: - balanced - General non-specialized tuned profile - desktop - Optmize for the desktop use-case - latency-performance - Optimize for deterministic performance @@ -63,20 +70,33 @@ Current active profile: throughput-performance''' - virtual-guest - Optimize for running inside a virtual-guest. - virtual-host - Optimize for running KVM guests Current active profile: virtual-guest -''' +""" mock_cmd = MagicMock(return_value=tuned_list) - with patch.dict(tuned.__salt__, {'cmd.run': mock_cmd}): + with patch.dict(tuned.__salt__, {"cmd.run": mock_cmd}): self.assertEqual( tuned.list_(), - ['balanced', 'desktop', 'latency-performance', - 'network-latency', 'network-throughput', 'powersave', - 'throughput-performance', 'virtual-guest', - 'virtual-host']) + [ + "balanced", + "desktop", + "latency-performance", + "network-latency", + "network-throughput", + "powersave", + "throughput-performance", + "virtual-guest", + "virtual-host", + ], + ) def test_none(self): - ''' - ''' - ret = {'pid': 12345, 'retcode': 1, 'stderr': 'stderr: Cannot talk to Tuned daemon via DBus. Is Tuned daemon running?', 'stdout': 'No current active profile.'} + """ + """ + ret = { + "pid": 12345, + "retcode": 1, + "stderr": "stderr: Cannot talk to Tuned daemon via DBus. Is Tuned daemon running?", + "stdout": "No current active profile.", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(tuned.__salt__, {'cmd.run_all': mock_cmd}): - self.assertEqual(tuned.active(), 'none') + with patch.dict(tuned.__salt__, {"cmd.run_all": mock_cmd}): + self.assertEqual(tuned.active(), "none") diff --git a/tests/unit/modules/test_twilio_notify.py b/tests/unit/modules/test_twilio_notify.py index 91d8f41ce53..a1e966b86c0 100644 --- a/tests/unit/modules/test_twilio_notify.py +++ b/tests/unit/modules/test_twilio_notify.py @@ -1,28 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.twilio_notify as twilio_notify +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + HAS_LIBS = False try: import twilio + # Grab version, ensure elements are ints twilio_version = tuple([int(x) for x in twilio.__version_info__]) - if twilio_version > (5, ): + if twilio_version > (5,): TWILIO_5 = False else: TWILIO_5 = True @@ -32,38 +30,40 @@ except ImportError: class MockTwilioRestException(Exception): - ''' + """ Mock TwilioRestException class - ''' + """ + def __init__(self): - self.code = 'error code' - self.msg = 'Exception error' - self.status = 'Not send' + self.code = "error code" + self.msg = "Exception error" + self.status = "Not send" super(MockTwilioRestException, self).__init__(self.msg) class MockMessages(object): - ''' + """ Mock SMS class - ''' + """ + flag = None def __init__(self): - self.sid = '011' - self.price = '200' - self.price_unit = '1' - self.status = 'Sent' - self.num_segments = '2' + self.sid = "011" + self.price = "200" + self.price_unit = "1" + self.status = "Sent" + self.num_segments = "2" self.body = None - self.date_sent = '01-01-2015' - self.date_created = '01-01-2015' + self.date_sent = "01-01-2015" + self.date_created = "01-01-2015" self.to = None self.from_ = None def create(self, body, to, from_): - ''' + """ Mock create method - ''' + """ msg = MockMessages() if self.flag == 1: raise MockTwilioRestException() @@ -74,17 +74,19 @@ class MockMessages(object): class MockSMS(object): - ''' + """ Mock SMS class - ''' + """ + def __init__(self): self.messages = MockMessages() class MockTwilioRestClient(object): - ''' + """ Mock TwilioRestClient class - ''' + """ + def __init__(self): if TWILIO_5: self.sms = MockSMS() @@ -92,44 +94,57 @@ class MockTwilioRestClient(object): self.messages = MockMessages() -@skipIf(not HAS_LIBS, 'twilio.rest is not available') +@skipIf(not HAS_LIBS, "twilio.rest is not available") class TwilioNotifyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.twilio_notify - ''' + """ + def setup_loader_modules(self): return { twilio_notify: { - 'TwilioRestClient': MockTwilioRestClient, - 'TwilioRestException': MockTwilioRestException + "TwilioRestClient": MockTwilioRestClient, + "TwilioRestException": MockTwilioRestException, } } # 'send_sms' function tests: 1 def test_send_sms(self): - ''' + """ Test if it send an sms. - ''' + """ mock = MagicMock(return_value=MockTwilioRestClient()) - with patch.object(twilio_notify, '_get_twilio', mock): - self.assertDictEqual(twilio_notify.send_sms('twilio-account', - 'SALTSTACK', - '+18019999999', - '+18011111111'), - {'message': {'status': 'Sent', - 'num_segments': '2', - 'price': '200', - 'body': 'SALTSTACK', 'sid': '011', - 'date_sent': '01-01-2015', - 'date_created': '01-01-2015', - 'price_unit': '1'}}) + with patch.object(twilio_notify, "_get_twilio", mock): + self.assertDictEqual( + twilio_notify.send_sms( + "twilio-account", "SALTSTACK", "+18019999999", "+18011111111" + ), + { + "message": { + "status": "Sent", + "num_segments": "2", + "price": "200", + "body": "SALTSTACK", + "sid": "011", + "date_sent": "01-01-2015", + "date_created": "01-01-2015", + "price_unit": "1", + } + }, + ) MockMessages.flag = 1 - self.assertDictEqual(twilio_notify.send_sms('twilio-account', - 'SALTSTACK', - '+18019999999', - '+18011111111'), - {'message': {'sid': None}, '_error': - {'msg': 'Exception error', - 'status': 'Not send', 'code': 'error code'}}) + self.assertDictEqual( + twilio_notify.send_sms( + "twilio-account", "SALTSTACK", "+18019999999", "+18011111111" + ), + { + "message": {"sid": None}, + "_error": { + "msg": "Exception error", + "status": "Not send", + "code": "error code", + }, + }, + ) diff --git a/tests/unit/modules/test_udev.py b/tests/unit/modules/test_udev.py index c643ce6c6c3..ad0b7abcc78 100644 --- a/tests/unit/modules/test_udev.py +++ b/tests/unit/modules/test_udev.py @@ -1,74 +1,75 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pablo Suárez Hdez. <psuarezhernandez@suse.de> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.udev as udev from salt.ext import six +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class UdevTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.udev - ''' + """ + def setup_loader_modules(self): return {udev: {}} # 'info' function tests: 1 def test_info(self): - ''' + """ Test if it returns the info of udev-created node in a dict - ''' + """ cmd_out = { - 'retcode': 0, - 'stdout': "P: /devices/virtual/vc/vcsa7\n" - "N: vcsa7\n" - "E: DEVNAME=/dev/vcsa7\n" - "E: DEVPATH=/devices/virtual/vc/vcsa7\n" - "E: MAJOR=7\n" - "E: MINOR=135\n" - "E: SUBSYSTEM=vc\n" - "\n", - 'stderr': '' + "retcode": 0, + "stdout": "P: /devices/virtual/vc/vcsa7\n" + "N: vcsa7\n" + "E: DEVNAME=/dev/vcsa7\n" + "E: DEVPATH=/devices/virtual/vc/vcsa7\n" + "E: MAJOR=7\n" + "E: MINOR=135\n" + "E: SUBSYSTEM=vc\n" + "\n", + "stderr": "", } ret = { - "E": {"DEVNAME": "/dev/vcsa7", - "DEVPATH": "/devices/virtual/vc/vcsa7", - "MAJOR": 7, - "MINOR": 135, - "SUBSYSTEM": "vc"}, + "E": { + "DEVNAME": "/dev/vcsa7", + "DEVPATH": "/devices/virtual/vc/vcsa7", + "MAJOR": 7, + "MINOR": 135, + "SUBSYSTEM": "vc", + }, "N": "vcsa7", - "P": "/devices/virtual/vc/vcsa7"} + "P": "/devices/virtual/vc/vcsa7", + } mock = MagicMock(return_value=cmd_out) - with patch.dict(udev.__salt__, {'cmd.run_all': mock}): + with patch.dict(udev.__salt__, {"cmd.run_all": mock}): data = udev.info("/dev/vcsa7") - assert ret['P'] == data['P'] - assert ret.get('N') == data.get('N') - for key, value in six.iteritems(data['E']): - assert ret['E'][key] == value + assert ret["P"] == data["P"] + assert ret.get("N") == data.get("N") + for key, value in six.iteritems(data["E"]): + assert ret["E"][key] == value # 'exportdb' function tests: 1 def test_exportdb(self): - ''' + """ Test if it returns the all the udev database into a dict - ''' + """ udev_data = """ P: /devices/LNXSYSTM:00/LNXPWRBN:00 E: DEVPATH=/devices/LNXSYSTM:00/LNXPWRBN:00 @@ -112,61 +113,79 @@ E: XKBLAYOUT=us E: XKBMODEL=pc105 """ - out = [{'P': '/devices/LNXSYSTM:00/LNXPWRBN:00', - 'E': {'MODALIAS': 'acpi:LNXPWRBN:', - 'SUBSYSTEM': 'acpi', - 'DRIVER': 'button', - 'DEVPATH': '/devices/LNXSYSTM:00/LNXPWRBN:00'}}, - {'P': '/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2', - 'E': {'SUBSYSTEM': 'input', - 'PRODUCT': '19/0/1/0', - 'PHYS': '"LNXPWRBN/button/input0"', - 'NAME': '"Power Button"', - 'ID_INPUT': 1, - 'DEVPATH': '/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2', - 'MODALIAS': 'input:b0019v0000p0001e0000-e0,1,k74,ramlsfw', - 'ID_PATH_TAG': 'acpi-LNXPWRBN_00', - 'TAGS': ':seat:', - 'PROP': 0, - 'ID_FOR_SEAT': 'input-acpi-LNXPWRBN_00', - 'KEY': '10000000000000 0', - 'USEC_INITIALIZED': 2010022, - 'ID_PATH': 'acpi-LNXPWRBN:00', - 'EV': 3, - 'ID_INPUT_KEY': 1}}, - {'P': '/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2/event2', - 'E': {'SUBSYSTEM': 'input', - 'XKBLAYOUT': 'us', - 'MAJOR': 13, - 'ID_INPUT': 1, - 'DEVPATH': '/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2/event2', - 'ID_PATH_TAG': 'acpi-LNXPWRBN_00', - 'DEVNAME': '/dev/input/event2', - 'TAGS': ':power-switch:', - 'BACKSPACE': 'guess', - 'MINOR': 66, - 'USEC_INITIALIZED': 2076101, - 'ID_PATH': 'acpi-LNXPWRBN:00', - 'XKBMODEL': 'pc105', - 'ID_INPUT_KEY': 1}, - 'N': 'input/event2'}] + out = [ + { + "P": "/devices/LNXSYSTM:00/LNXPWRBN:00", + "E": { + "MODALIAS": "acpi:LNXPWRBN:", + "SUBSYSTEM": "acpi", + "DRIVER": "button", + "DEVPATH": "/devices/LNXSYSTM:00/LNXPWRBN:00", + }, + }, + { + "P": "/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2", + "E": { + "SUBSYSTEM": "input", + "PRODUCT": "19/0/1/0", + "PHYS": '"LNXPWRBN/button/input0"', + "NAME": '"Power Button"', + "ID_INPUT": 1, + "DEVPATH": "/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2", + "MODALIAS": "input:b0019v0000p0001e0000-e0,1,k74,ramlsfw", + "ID_PATH_TAG": "acpi-LNXPWRBN_00", + "TAGS": ":seat:", + "PROP": 0, + "ID_FOR_SEAT": "input-acpi-LNXPWRBN_00", + "KEY": "10000000000000 0", + "USEC_INITIALIZED": 2010022, + "ID_PATH": "acpi-LNXPWRBN:00", + "EV": 3, + "ID_INPUT_KEY": 1, + }, + }, + { + "P": "/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2/event2", + "E": { + "SUBSYSTEM": "input", + "XKBLAYOUT": "us", + "MAJOR": 13, + "ID_INPUT": 1, + "DEVPATH": "/devices/LNXSYSTM:00/LNXPWRBN:00/input/input2/event2", + "ID_PATH_TAG": "acpi-LNXPWRBN_00", + "DEVNAME": "/dev/input/event2", + "TAGS": ":power-switch:", + "BACKSPACE": "guess", + "MINOR": 66, + "USEC_INITIALIZED": 2076101, + "ID_PATH": "acpi-LNXPWRBN:00", + "XKBMODEL": "pc105", + "ID_INPUT_KEY": 1, + }, + "N": "input/event2", + }, + ] - mock = MagicMock(return_value={'retcode': 0, 'stdout': udev_data}) - with patch.dict(udev.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": udev_data}) + with patch.dict(udev.__salt__, {"cmd.run_all": mock}): data = udev.exportdb() assert data == [x for x in data if x] for d_idx, d_section in enumerate(data): - assert out[d_idx]['P'] == d_section['P'] - assert out[d_idx].get('N') == d_section.get('N') - for key, value in six.iteritems(d_section['E']): - assert out[d_idx]['E'][key] == value + assert out[d_idx]["P"] == d_section["P"] + assert out[d_idx].get("N") == d_section.get("N") + for key, value in six.iteritems(d_section["E"]): + assert out[d_idx]["E"][key] == value def test_normalize_info(self): - ''' + """ Test if udevdb._normalize_info does not returns nested lists that contains only one item. :return: - ''' - data = {'key': ['value', 'here'], 'foo': ['bar'], 'some': 'data'} - assert udev._normalize_info(data) == {'foo': 'bar', 'some': 'data', 'key': ['value', 'here']} + """ + data = {"key": ["value", "here"], "foo": ["bar"], "some": "data"} + assert udev._normalize_info(data) == { + "foo": "bar", + "some": "data", + "key": ["value", "here"], + } diff --git a/tests/unit/modules/test_uptime.py b/tests/unit/modules/test_uptime.py index a58982efff8..f914809215f 100644 --- a/tests/unit/modules/test_uptime.py +++ b/tests/unit/modules/test_uptime.py @@ -3,20 +3,21 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import Mock +import salt.modules.uptime as uptime # Import salt libs from salt.exceptions import CommandExecutionError -import salt.modules.uptime as uptime + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock +from tests.support.unit import TestCase class RequestMock(Mock): - ''' + """ Request Mock - ''' + """ def get(self, *args, **kwargs): return RequestResponseMock() @@ -33,10 +34,10 @@ class RequestMock(Mock): class RequestResponseMock(Mock): - def json(self): - return [{'url': 'http://example.org', - '_id': 1234}, ] + return [ + {"url": "http://example.org", "_id": 1234}, + ] class RequestPutResponseMock(Mock): @@ -44,45 +45,41 @@ class RequestPutResponseMock(Mock): ok = True def json(self): - return {'_id': 4321} + return {"_id": 4321} REQUEST_MOCK = RequestMock() class UptimeTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ UptimeTestCase - ''' + """ def setup_loader_modules(self): return { uptime: { - '__salt__': { - 'pillar.get': Mock(return_value='http://localhost:5000'), - 'requests.put': Mock(), + "__salt__": { + "pillar.get": Mock(return_value="http://localhost:5000"), + "requests.put": Mock(), }, - 'requests': REQUEST_MOCK + "requests": REQUEST_MOCK, } } def test_checks_list(self): ret = uptime.checks_list() - self.assertListEqual(ret, ['http://example.org']) + self.assertListEqual(ret, ["http://example.org"]) def test_checks_exists(self): - self.assertTrue(uptime.check_exists('http://example.org') is True) + self.assertTrue(uptime.check_exists("http://example.org") is True) def test_checks_create(self): - self.assertRaises(CommandExecutionError, uptime.create, - 'http://example.org') - self.assertEqual(4321, uptime.create('http://example.com')) - self.assertEqual(('http://localhost:5000/api/checks',), - REQUEST_MOCK.args) + self.assertRaises(CommandExecutionError, uptime.create, "http://example.org") + self.assertEqual(4321, uptime.create("http://example.com")) + self.assertEqual(("http://localhost:5000/api/checks",), REQUEST_MOCK.args) def test_checks_delete(self): - self.assertRaises(CommandExecutionError, uptime.delete, - 'http://example.com') - self.assertTrue(uptime.delete('http://example.org') is True) - self.assertEqual(('http://localhost:5000/api/checks/1234',), - REQUEST_MOCK.args) + self.assertRaises(CommandExecutionError, uptime.delete, "http://example.com") + self.assertTrue(uptime.delete("http://example.org") is True) + self.assertEqual(("http://localhost:5000/api/checks/1234",), REQUEST_MOCK.args) diff --git a/tests/unit/modules/test_useradd.py b/tests/unit/modules/test_useradd.py index 814146e699d..6428a8918c8 100644 --- a/tests/unit/modules/test_useradd.py +++ b/tests/unit/modules/test_useradd.py @@ -1,50 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -try: - import pwd - HAS_PWD = True -except ImportError: - HAS_PWD = False - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) # Import Salt Libs import salt.modules.useradd as useradd from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + +try: + import pwd + + HAS_PWD = True +except ImportError: + HAS_PWD = False + class UserAddTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.useradd - ''' + """ + def setup_loader_modules(self): return {useradd: {}} @classmethod def setUpClass(cls): - cls.mock_pwall = {'gid': 0, - 'groups': ['root'], - 'home': '/root', - 'name': 'root', - 'passwd': 'x', - 'shell': '/bin/bash', - 'uid': 0, - 'fullname': 'root', - 'roomnumber': '', - 'workphone': '', - 'homephone': '', - 'other': ''} + cls.mock_pwall = { + "gid": 0, + "groups": ["root"], + "home": "/root", + "name": "root", + "passwd": "x", + "shell": "/bin/bash", + "uid": 0, + "fullname": "root", + "roomnumber": "", + "workphone": "", + "homephone": "", + "other": "", + } @classmethod def tearDownClass(cls): @@ -53,391 +55,405 @@ class UserAddTestCase(TestCase, LoaderModuleMockMixin): # 'add' function tests: 1 def test_add(self): - ''' + """ Test for adding a user - ''' - with patch.dict(useradd.__grains__, {'kernel': 'OpenBSD'}): - mock_primary = MagicMock(return_value='Salt') - with patch.dict(useradd.__salt__, - {'file.gid_to_group': mock_primary}): - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(useradd.__salt__, {'cmd.run_all': mock}): - self.assertTrue(useradd.add('Salt')) + """ + with patch.dict(useradd.__grains__, {"kernel": "OpenBSD"}): + mock_primary = MagicMock(return_value="Salt") + with patch.dict(useradd.__salt__, {"file.gid_to_group": mock_primary}): + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(useradd.__salt__, {"cmd.run_all": mock}): + self.assertTrue(useradd.add("Salt")) - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(useradd.__salt__, {'cmd.run_all': mock}): - self.assertFalse(useradd.add('Salt')) + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(useradd.__salt__, {"cmd.run_all": mock}): + self.assertFalse(useradd.add("Salt")) # 'getent' function tests: 2 - @skipIf(HAS_PWD is False, 'The pwd module is not available') + @skipIf(HAS_PWD is False, "The pwd module is not available") def test_getent(self): - ''' + """ Test if user.getent already have a value - ''' - with patch('salt.modules.useradd.__context__', MagicMock(return_value='Salt')): + """ + with patch("salt.modules.useradd.__context__", MagicMock(return_value="Salt")): self.assertTrue(useradd.getent()) - @skipIf(HAS_PWD is False, 'The pwd module is not available') + @skipIf(HAS_PWD is False, "The pwd module is not available") def test_getent_user(self): - ''' + """ Tests the return information on all users - ''' - with patch('pwd.getpwall', MagicMock(return_value=[''])): - ret = [{'gid': 0, - 'groups': ['root'], - 'home': '/root', - 'name': 'root', - 'passwd': 'x', - 'shell': '/bin/bash', - 'uid': 0, - 'fullname': 'root', - 'roomnumber': '', - 'workphone': '', - 'homephone': '', - 'other': ''}] - with patch('salt.modules.useradd._format_info', MagicMock(return_value=self.mock_pwall)): + """ + with patch("pwd.getpwall", MagicMock(return_value=[""])): + ret = [ + { + "gid": 0, + "groups": ["root"], + "home": "/root", + "name": "root", + "passwd": "x", + "shell": "/bin/bash", + "uid": 0, + "fullname": "root", + "roomnumber": "", + "workphone": "", + "homephone": "", + "other": "", + } + ] + with patch( + "salt.modules.useradd._format_info", + MagicMock(return_value=self.mock_pwall), + ): self.assertEqual(useradd.getent(), ret) # 'chuid' function tests: 1 def test_chuid(self): - ''' + """ Test if the uid of a user change - ''' - mock = MagicMock(return_value={'uid': 11}) - with patch.object(useradd, 'info', mock): - self.assertTrue(useradd.chuid('name', 11)) + """ + mock = MagicMock(return_value={"uid": 11}) + with patch.object(useradd, "info", mock): + self.assertTrue(useradd.chuid("name", 11)) mock_run = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock_run}): - mock = MagicMock(side_effect=[{'uid': 11}, {'uid': 11}]) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chuid('name', 22)) + with patch.dict(useradd.__salt__, {"cmd.run": mock_run}): + mock = MagicMock(side_effect=[{"uid": 11}, {"uid": 11}]) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chuid("name", 22)) - with patch.dict(useradd.__salt__, {'cmd.run': mock_run}): - mock = MagicMock(side_effect=[{'uid': 11}, {'uid': 22}]) - with patch.object(useradd, 'info', mock): - self.assertTrue(useradd.chuid('name', 11)) + with patch.dict(useradd.__salt__, {"cmd.run": mock_run}): + mock = MagicMock(side_effect=[{"uid": 11}, {"uid": 22}]) + with patch.object(useradd, "info", mock): + self.assertTrue(useradd.chuid("name", 11)) # 'chgid' function tests: 1 def test_chgid(self): - ''' + """ Test the default group of the user - ''' - mock = MagicMock(return_value={'gid': 11}) - with patch.object(useradd, 'info', mock): - self.assertTrue(useradd.chgid('name', 11)) + """ + mock = MagicMock(return_value={"gid": 11}) + with patch.object(useradd, "info", mock): + self.assertTrue(useradd.chgid("name", 11)) mock_run = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock_run}): - mock = MagicMock(side_effect=[{'gid': 22}, {'gid': 22}]) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chgid('name', 11)) + with patch.dict(useradd.__salt__, {"cmd.run": mock_run}): + mock = MagicMock(side_effect=[{"gid": 22}, {"gid": 22}]) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chgid("name", 11)) - with patch.dict(useradd.__salt__, {'cmd.run': mock_run}): - mock = MagicMock(side_effect=[{'gid': 11}, {'gid': 22}]) - with patch.object(useradd, 'info', mock): - self.assertTrue(useradd.chgid('name', 11)) + with patch.dict(useradd.__salt__, {"cmd.run": mock_run}): + mock = MagicMock(side_effect=[{"gid": 11}, {"gid": 22}]) + with patch.object(useradd, "info", mock): + self.assertTrue(useradd.chgid("name", 11)) # 'chshell' function tests: 1 def test_chshell(self): - ''' + """ Test the default shell of user - ''' - mock = MagicMock(return_value={'shell': '/bin/bash'}) - with patch.object(useradd, 'info', mock): - self.assertTrue(useradd.chshell('name', '/bin/bash')) + """ + mock = MagicMock(return_value={"shell": "/bin/bash"}) + with patch.object(useradd, "info", mock): + self.assertTrue(useradd.chshell("name", "/bin/bash")) mock_run = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock_run}): - mock = MagicMock(side_effect=[{'shell': '/bin/bash'}, - {'shell': '/bin/bash'}]) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chshell('name', '/usr/bash')) + with patch.dict(useradd.__salt__, {"cmd.run": mock_run}): + mock = MagicMock( + side_effect=[{"shell": "/bin/bash"}, {"shell": "/bin/bash"}] + ) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chshell("name", "/usr/bash")) - with patch.dict(useradd.__salt__, {'cmd.run': mock_run}): - mock = MagicMock(side_effect=[{'shell': '/bin/bash'}, - {'shell': '/usr/bash'}]) - with patch.object(useradd, 'info', mock): - self.assertTrue(useradd.chshell('name', '/bin/bash')) + with patch.dict(useradd.__salt__, {"cmd.run": mock_run}): + mock = MagicMock( + side_effect=[{"shell": "/bin/bash"}, {"shell": "/usr/bash"}] + ) + with patch.object(useradd, "info", mock): + self.assertTrue(useradd.chshell("name", "/bin/bash")) # 'chhome' function tests: 1 def test_chhome(self): - ''' + """ Test if home directory given is same as previous home directory - ''' - mock = MagicMock(return_value={'home': '/root'}) - with patch.object(useradd, 'info', mock): - self.assertTrue(useradd.chhome('name', '/root')) + """ + mock = MagicMock(return_value={"home": "/root"}) + with patch.object(useradd, "info", mock): + self.assertTrue(useradd.chhome("name", "/root")) mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'home': '/root'}, {'home': '/root'}]) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chhome('name', '/user')) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"home": "/root"}, {"home": "/root"}]) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chhome("name", "/user")) mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[{'home': '/root'}, {'home': '/root'}]) - with patch.object(useradd, 'info', mock): - self.assertTrue(useradd.chhome('name', '/root')) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[{"home": "/root"}, {"home": "/root"}]) + with patch.object(useradd, "info", mock): + self.assertTrue(useradd.chhome("name", "/root")) # 'chgroups' function tests: 1 def test_chgroups(self): - ''' + """ Test if user groups changed - ''' - mock = MagicMock(return_value=['wheel', 'root']) - with patch.object(useradd, 'list_groups', mock): - self.assertTrue(useradd.chgroups('foo', 'wheel,root')) + """ + mock = MagicMock(return_value=["wheel", "root"]) + with patch.object(useradd, "list_groups", mock): + self.assertTrue(useradd.chgroups("foo", "wheel,root")) - mock = MagicMock(return_value=['wheel', 'root']) - with patch.object(useradd, 'list_groups', mock): - with patch.dict(useradd.__grains__, {'kernel': 'OpenBSD'}): - mock_runall = MagicMock(return_value={'retcode': False, - 'stderr': ''}) - with patch.dict(useradd.__salt__, {'cmd.run_all': mock_runall}): - self.assertTrue(useradd.chgroups('foo', 'wheel,test,root')) + mock = MagicMock(return_value=["wheel", "root"]) + with patch.object(useradd, "list_groups", mock): + with patch.dict(useradd.__grains__, {"kernel": "OpenBSD"}): + mock_runall = MagicMock(return_value={"retcode": False, "stderr": ""}) + with patch.dict(useradd.__salt__, {"cmd.run_all": mock_runall}): + self.assertTrue(useradd.chgroups("foo", "wheel,test,root")) - mock_runall = MagicMock(return_value={'retcode': True, - 'stderr': ''}) - with patch.dict(useradd.__salt__, {'cmd.run_all': mock_runall}): - self.assertFalse(useradd.chgroups('foo', 'wheel,test,root')) + mock_runall = MagicMock(return_value={"retcode": True, "stderr": ""}) + with patch.dict(useradd.__salt__, {"cmd.run_all": mock_runall}): + self.assertFalse(useradd.chgroups("foo", "wheel,test,root")) # 'chfullname' function tests: 1 def test_chfullname(self): - ''' + """ Test if the user's Full Name is changed - ''' + """ mock = MagicMock(return_value=False) - with patch.object(useradd, '_get_gecos', mock): - self.assertFalse(useradd.chfullname('Salt', 'SaltStack')) + with patch.object(useradd, "_get_gecos", mock): + self.assertFalse(useradd.chfullname("Salt", "SaltStack")) - mock = MagicMock(return_value={'fullname': 'SaltStack'}) - with patch.object(useradd, '_get_gecos', mock): - self.assertTrue(useradd.chfullname('Salt', 'SaltStack')) + mock = MagicMock(return_value={"fullname": "SaltStack"}) + with patch.object(useradd, "_get_gecos", mock): + self.assertTrue(useradd.chfullname("Salt", "SaltStack")) - mock = MagicMock(return_value={'fullname': 'SaltStack'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"fullname": "SaltStack"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'fullname': 'SaltStack2'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chfullname('Salt', 'SaltStack1')) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"fullname": "SaltStack2"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chfullname("Salt", "SaltStack1")) - mock = MagicMock(return_value={'fullname': 'SaltStack2'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"fullname": "SaltStack2"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'fullname': 'SaltStack2'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chfullname('Salt', 'SaltStack1')) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"fullname": "SaltStack2"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chfullname("Salt", "SaltStack1")) # 'chroomnumber' function tests: 1 def test_chroomnumber(self): - ''' + """ Test if the user's Room Number is changed - ''' + """ mock = MagicMock(return_value=False) - with patch.object(useradd, '_get_gecos', mock): - self.assertFalse(useradd.chroomnumber('salt', 1)) + with patch.object(useradd, "_get_gecos", mock): + self.assertFalse(useradd.chroomnumber("salt", 1)) - mock = MagicMock(return_value={'roomnumber': '1'}) - with patch.object(useradd, '_get_gecos', mock): - self.assertTrue(useradd.chroomnumber('salt', 1)) + mock = MagicMock(return_value={"roomnumber": "1"}) + with patch.object(useradd, "_get_gecos", mock): + self.assertTrue(useradd.chroomnumber("salt", 1)) - mock = MagicMock(return_value={'roomnumber': '2'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"roomnumber": "2"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'roomnumber': '3'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chroomnumber('salt', 1)) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"roomnumber": "3"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chroomnumber("salt", 1)) - mock = MagicMock(return_value={'roomnumber': '3'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"roomnumber": "3"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'roomnumber': '3'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chroomnumber('salt', 1)) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"roomnumber": "3"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chroomnumber("salt", 1)) # 'chworkphone' function tests: 1 def test_chworkphone(self): - ''' + """ Test if the user's Work Phone is changed - ''' + """ mock = MagicMock(return_value=False) - with patch.object(useradd, '_get_gecos', mock): - self.assertFalse(useradd.chworkphone('salt', 1)) + with patch.object(useradd, "_get_gecos", mock): + self.assertFalse(useradd.chworkphone("salt", 1)) - mock = MagicMock(return_value={'workphone': '1'}) - with patch.object(useradd, '_get_gecos', mock): - self.assertTrue(useradd.chworkphone('salt', 1)) + mock = MagicMock(return_value={"workphone": "1"}) + with patch.object(useradd, "_get_gecos", mock): + self.assertTrue(useradd.chworkphone("salt", 1)) - mock = MagicMock(return_value={'workphone': '2'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"workphone": "2"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'workphone': '3'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chworkphone('salt', 1)) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"workphone": "3"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chworkphone("salt", 1)) - mock = MagicMock(return_value={'workphone': '3'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"workphone": "3"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'workphone': '3'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chworkphone('salt', 1)) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"workphone": "3"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chworkphone("salt", 1)) # 'chhomephone' function tests: 1 def test_chhomephone(self): - ''' + """ Test if the user's Home Phone is changed - ''' + """ mock = MagicMock(return_value=False) - with patch.object(useradd, '_get_gecos', mock): - self.assertFalse(useradd.chhomephone('salt', 1)) + with patch.object(useradd, "_get_gecos", mock): + self.assertFalse(useradd.chhomephone("salt", 1)) - mock = MagicMock(return_value={'homephone': '1'}) - with patch.object(useradd, '_get_gecos', mock): - self.assertTrue(useradd.chhomephone('salt', 1)) + mock = MagicMock(return_value={"homephone": "1"}) + with patch.object(useradd, "_get_gecos", mock): + self.assertTrue(useradd.chhomephone("salt", 1)) - mock = MagicMock(return_value={'homephone': '2'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"homephone": "2"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'homephone': '3'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chhomephone('salt', 1)) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"homephone": "3"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chhomephone("salt", 1)) - mock = MagicMock(return_value={'homephone': '3'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"homephone": "3"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'homephone': '3'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chhomephone('salt', 1)) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"homephone": "3"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chhomephone("salt", 1)) # 'chother' function tests: 1 def test_chother(self): - ''' + """ Test if the user's other GECOS attribute is changed - ''' + """ mock = MagicMock(return_value=False) - with patch.object(useradd, '_get_gecos', mock): - self.assertFalse(useradd.chother('salt', 1)) + with patch.object(useradd, "_get_gecos", mock): + self.assertFalse(useradd.chother("salt", 1)) - mock = MagicMock(return_value={'other': 'foobar'}) - with patch.object(useradd, '_get_gecos', mock): - self.assertTrue(useradd.chother('salt', 'foobar')) + mock = MagicMock(return_value={"other": "foobar"}) + with patch.object(useradd, "_get_gecos", mock): + self.assertTrue(useradd.chother("salt", "foobar")) - mock = MagicMock(return_value={'other': 'foobar2'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"other": "foobar2"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'other': 'foobar3'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chother('salt', 'foobar')) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"other": "foobar3"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chother("salt", "foobar")) - mock = MagicMock(return_value={'other': 'foobar3'}) - with patch.object(useradd, '_get_gecos', mock): + mock = MagicMock(return_value={"other": "foobar3"}) + with patch.object(useradd, "_get_gecos", mock): mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(return_value={'other': 'foobar3'}) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.chother('salt', 'foobar')) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(return_value={"other": "foobar3"}) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.chother("salt", "foobar")) # 'info' function tests: 1 - @skipIf(HAS_PWD is False, 'The pwd module is not available') + @skipIf(HAS_PWD is False, "The pwd module is not available") def test_info(self): - ''' + """ Test the user information - ''' - self.assertEqual(useradd.info('username-that-doesnt-exist'), {}) + """ + self.assertEqual(useradd.info("username-that-doesnt-exist"), {}) - mock = MagicMock(return_value=pwd.struct_passwd(('_TEST_GROUP', - '*', - 83, - 83, - 'AMaViS Daemon', - '/var/virusmails', - '/usr/bin/false'))) - with patch.object(pwd, 'getpwnam', mock): - self.assertEqual(useradd.info('username-that-doesnt-exist')['name'], '_TEST_GROUP') + mock = MagicMock( + return_value=pwd.struct_passwd( + ( + "_TEST_GROUP", + "*", + 83, + 83, + "AMaViS Daemon", + "/var/virusmails", + "/usr/bin/false", + ) + ) + ) + with patch.object(pwd, "getpwnam", mock): + self.assertEqual( + useradd.info("username-that-doesnt-exist")["name"], "_TEST_GROUP" + ) # 'list_groups' function tests: 1 def test_list_groups(self): - ''' + """ Test if it return a list of groups the named user belongs to - ''' - with patch('salt.utils.user.get_group_list', MagicMock(return_value='Salt')): - self.assertEqual(useradd.list_groups('name'), 'Salt') + """ + with patch("salt.utils.user.get_group_list", MagicMock(return_value="Salt")): + self.assertEqual(useradd.list_groups("name"), "Salt") # 'list_users' function tests: 1 - @skipIf(HAS_PWD is False, 'The pwd module is not available') + @skipIf(HAS_PWD is False, "The pwd module is not available") def test_list_users(self): - ''' + """ Test if it returns a list of all users - ''' + """ self.assertTrue(useradd.list_users()) # 'list_users' function tests: 1 def test_rename(self): - ''' + """ Test if the username for a named user changed - ''' + """ mock = MagicMock(return_value=False) - with patch.object(useradd, 'info', mock): - self.assertRaises(CommandExecutionError, useradd.rename, 'salt', 1) + with patch.object(useradd, "info", mock): + self.assertRaises(CommandExecutionError, useradd.rename, "salt", 1) mock = MagicMock(return_value=True) - with patch.object(useradd, 'info', mock): - self.assertRaises(CommandExecutionError, useradd.rename, 'salt', 1) + with patch.object(useradd, "info", mock): + self.assertRaises(CommandExecutionError, useradd.rename, "salt", 1) mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[False, {'name': ''}, - {'name': 'salt'}]) - with patch.object(useradd, 'info', mock): - self.assertTrue(useradd.rename('name', 'salt')) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[False, {"name": ""}, {"name": "salt"}]) + with patch.object(useradd, "info", mock): + self.assertTrue(useradd.rename("name", "salt")) mock = MagicMock(return_value=None) - with patch.dict(useradd.__salt__, {'cmd.run': mock}): - mock = MagicMock(side_effect=[False, {'name': ''}, - {'name': ''}]) - with patch.object(useradd, 'info', mock): - self.assertFalse(useradd.rename('salt', 'salt')) + with patch.dict(useradd.__salt__, {"cmd.run": mock}): + mock = MagicMock(side_effect=[False, {"name": ""}, {"name": ""}]) + with patch.object(useradd, "info", mock): + self.assertFalse(useradd.rename("salt", "salt")) def test_build_gecos_field(self): - ''' + """ Test if gecos fields are built correctly (removing trailing commas) - ''' - test_gecos = {'fullname': 'Testing', - 'roomnumber': 1234, - 'workphone': 22222, - 'homephone': 99999} - expected_gecos_fields = 'Testing,1234,22222,99999' + """ + test_gecos = { + "fullname": "Testing", + "roomnumber": 1234, + "workphone": 22222, + "homephone": 99999, + } + expected_gecos_fields = "Testing,1234,22222,99999" self.assertEqual(useradd._build_gecos(test_gecos), expected_gecos_fields) - test_gecos.pop('roomnumber') - test_gecos.pop('workphone') - expected_gecos_fields = 'Testing,,,99999' + test_gecos.pop("roomnumber") + test_gecos.pop("workphone") + expected_gecos_fields = "Testing,,,99999" self.assertEqual(useradd._build_gecos(test_gecos), expected_gecos_fields) - test_gecos.pop('homephone') - expected_gecos_fields = 'Testing' + test_gecos.pop("homephone") + expected_gecos_fields = "Testing" self.assertEqual(useradd._build_gecos(test_gecos), expected_gecos_fields) diff --git a/tests/unit/modules/test_uwsgi.py b/tests/unit/modules/test_uwsgi.py index 2eb3cef3c14..c4b30daa2f0 100644 --- a/tests/unit/modules/test_uwsgi.py +++ b/tests/unit/modules/test_uwsgi.py @@ -3,19 +3,18 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, Mock, patch - # Import salt libs import salt.modules.uwsgi as uwsgi +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase + class UwsgiTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/uwsgi')) + patcher = patch("salt.utils.path.which", Mock(return_value="/usr/bin/uwsgi")) patcher.start() self.addCleanup(patcher.stop) return {uwsgi: {}} @@ -23,9 +22,10 @@ class UwsgiTestCase(TestCase, LoaderModuleMockMixin): def test_uwsgi_stats(self): socket = "127.0.0.1:5050" mock = MagicMock(return_value='{"a": 1, "b": 2}') - with patch.dict(uwsgi.__salt__, {'cmd.run': mock}): + with patch.dict(uwsgi.__salt__, {"cmd.run": mock}): result = uwsgi.stats(socket) mock.assert_called_once_with( - ['uwsgi', '--connect-and-read', '{0}'.format(socket)], - python_shell=False) - self.assertEqual(result, {'a': 1, 'b': 2}) + ["uwsgi", "--connect-and-read", "{0}".format(socket)], + python_shell=False, + ) + self.assertEqual(result, {"a": 1, "b": 2}) diff --git a/tests/unit/modules/test_vagrant.py b/tests/unit/modules/test_vagrant.py index d91fdb6bef8..67c52c29e44 100644 --- a/tests/unit/modules/test_vagrant.py +++ b/tests/unit/modules/test_vagrant.py @@ -2,167 +2,182 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +import salt.exceptions # Import salt libs import salt.modules.vagrant as vagrant -import salt.exceptions import salt.utils.platform -TEMP_DATABASE_FILE = '/tmp/salt-tests-tmpdir/test_vagrant.sqlite' +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +TEMP_DATABASE_FILE = "/tmp/salt-tests-tmpdir/test_vagrant.sqlite" class VagrantTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Unit TestCase for the salt.modules.vagrant module. - ''' + """ + LOCAL_OPTS = { - 'extension_modules': '', - 'vagrant_sdb_data': { - 'driver': 'sqlite3', - 'database': TEMP_DATABASE_FILE, - 'table': 'sdb', - 'create_table': True - } - } + "extension_modules": "", + "vagrant_sdb_data": { + "driver": "sqlite3", + "database": TEMP_DATABASE_FILE, + "table": "sdb", + "create_table": True, + }, + } def setup_loader_modules(self): vagrant_globals = { - '__opts__': self.LOCAL_OPTS, - } + "__opts__": self.LOCAL_OPTS, + } return {vagrant: vagrant_globals} def test_vagrant_get_vm_info_not_found(self): mock_sdb = MagicMock(return_value=None) - with patch.dict(vagrant.__utils__, {'sdb.sdb_get': mock_sdb}): + with patch.dict(vagrant.__utils__, {"sdb.sdb_get": mock_sdb}): with self.assertRaises(salt.exceptions.SaltInvocationError): - vagrant.get_vm_info('thisNameDoesNotExist') + vagrant.get_vm_info("thisNameDoesNotExist") def test_vagrant_init_positional(self): - path_nowhere = os.path.join(os.sep, 'tmp', 'nowhere') + path_nowhere = os.path.join(os.sep, "tmp", "nowhere") if salt.utils.platform.is_windows(): - path_nowhere = 'c:{0}'.format(path_nowhere) + path_nowhere = "c:{0}".format(path_nowhere) mock_sdb = MagicMock(return_value=None) - with patch.dict(vagrant.__utils__, {'sdb.sdb_set': mock_sdb}): + with patch.dict(vagrant.__utils__, {"sdb.sdb_set": mock_sdb}): resp = vagrant.init( - 'test1', + "test1", path_nowhere, - 'onetest', - 'nobody', + "onetest", + "nobody", False, - 'french', - {'different': 'very'} - ) - self.assertTrue(resp.startswith('Name test1 defined')) - expected = dict(name='test1', - cwd=path_nowhere, - machine='onetest', - runas='nobody', - vagrant_provider='french', - different='very' - ) + "french", + {"different": "very"}, + ) + self.assertTrue(resp.startswith("Name test1 defined")) + expected = dict( + name="test1", + cwd=path_nowhere, + machine="onetest", + runas="nobody", + vagrant_provider="french", + different="very", + ) mock_sdb.assert_called_with( - 'sdb://vagrant_sdb_data/onetest?{0}'.format(path_nowhere), - 'test1', - self.LOCAL_OPTS) + "sdb://vagrant_sdb_data/onetest?{0}".format(path_nowhere), + "test1", + self.LOCAL_OPTS, + ) mock_sdb.assert_any_call( - 'sdb://vagrant_sdb_data/test1', - expected, - self.LOCAL_OPTS) + "sdb://vagrant_sdb_data/test1", expected, self.LOCAL_OPTS + ) def test_vagrant_get_vm_info(self): - testdict = {'testone': 'one', 'machine': 'two'} + testdict = {"testone": "one", "machine": "two"} mock_sdb = MagicMock(return_value=testdict) - with patch.dict(vagrant.__utils__, {'sdb.sdb_get': mock_sdb}): - resp = vagrant.get_vm_info('test1') + with patch.dict(vagrant.__utils__, {"sdb.sdb_get": mock_sdb}): + resp = vagrant.get_vm_info("test1") self.assertEqual(resp, testdict) def test_vagrant_init_dict(self): - testdict = dict(cwd='/tmp/anywhere', - machine='twotest', - runas='somebody', - vagrant_provider='english') + testdict = dict( + cwd="/tmp/anywhere", + machine="twotest", + runas="somebody", + vagrant_provider="english", + ) expected = testdict.copy() - expected['name'] = 'test2' + expected["name"] = "test2" mock_sdb = MagicMock(return_value=None) - with patch.dict(vagrant.__utils__, {'sdb.sdb_set': mock_sdb}): - vagrant.init('test2', vm=testdict) + with patch.dict(vagrant.__utils__, {"sdb.sdb_set": mock_sdb}): + vagrant.init("test2", vm=testdict) mock_sdb.assert_any_call( - 'sdb://vagrant_sdb_data/test2', - expected, - self.LOCAL_OPTS) + "sdb://vagrant_sdb_data/test2", expected, self.LOCAL_OPTS + ) def test_vagrant_init_arg_override(self): - testdict = dict(cwd='/tmp/there', - machine='treetest', - runas='anybody', - vagrant_provider='spansh') + testdict = dict( + cwd="/tmp/there", + machine="treetest", + runas="anybody", + vagrant_provider="spansh", + ) mock_sdb = MagicMock(return_value=None) - with patch.dict(vagrant.__utils__, {'sdb.sdb_set': mock_sdb}): - vagrant.init('test3', - cwd='/tmp', - machine='threetest', - runas='him', - vagrant_provider='polish', - vm=testdict) - expected = dict(name='test3', - cwd='/tmp', - machine='threetest', - runas='him', - vagrant_provider='polish') + with patch.dict(vagrant.__utils__, {"sdb.sdb_set": mock_sdb}): + vagrant.init( + "test3", + cwd="/tmp", + machine="threetest", + runas="him", + vagrant_provider="polish", + vm=testdict, + ) + expected = dict( + name="test3", + cwd="/tmp", + machine="threetest", + runas="him", + vagrant_provider="polish", + ) mock_sdb.assert_any_call( - 'sdb://vagrant_sdb_data/test3', - expected, - self.LOCAL_OPTS) + "sdb://vagrant_sdb_data/test3", expected, self.LOCAL_OPTS + ) def test_vagrant_get_ssh_config_fails(self): mock_sdb = MagicMock(return_value=None) - with patch.dict(vagrant.__utils__, {'sdb.sdb_set': mock_sdb}): + with patch.dict(vagrant.__utils__, {"sdb.sdb_set": mock_sdb}): mock_sdb = MagicMock(return_value={}) - with patch.dict(vagrant.__utils__, {'sdb.sdb_get': mock_sdb}): - vagrant.init('test3', cwd='/tmp') + with patch.dict(vagrant.__utils__, {"sdb.sdb_get": mock_sdb}): + vagrant.init("test3", cwd="/tmp") with self.assertRaises(salt.exceptions.SaltInvocationError): - vagrant.get_ssh_config('test3') # has not been started + vagrant.get_ssh_config("test3") # has not been started def test_vagrant_destroy(self): - path_mydir = os.path.join(os.sep, 'my', 'dir') + path_mydir = os.path.join(os.sep, "my", "dir") if salt.utils.platform.is_windows(): - path_mydir = 'c:{0}'.format(path_mydir) - mock_cmd = MagicMock(return_value={'retcode': 0}) - with patch.dict(vagrant.__salt__, {'cmd.run_all': mock_cmd}): + path_mydir = "c:{0}".format(path_mydir) + mock_cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict(vagrant.__salt__, {"cmd.run_all": mock_cmd}): mock_sdb = MagicMock(return_value=None) - with patch.dict(vagrant.__utils__, {'sdb.sdb_delete': mock_sdb}): - mock_sdb_get = MagicMock(return_value={ - 'machine': 'macfour', 'cwd': path_mydir}) - with patch.dict(vagrant.__utils__, {'sdb.sdb_get': mock_sdb_get}): - self.assertTrue(vagrant.destroy('test4')) + with patch.dict(vagrant.__utils__, {"sdb.sdb_delete": mock_sdb}): + mock_sdb_get = MagicMock( + return_value={"machine": "macfour", "cwd": path_mydir} + ) + with patch.dict(vagrant.__utils__, {"sdb.sdb_get": mock_sdb_get}): + self.assertTrue(vagrant.destroy("test4")) mock_sdb.assert_any_call( - 'sdb://vagrant_sdb_data/macfour?{0}'.format(path_mydir), - self.LOCAL_OPTS) + "sdb://vagrant_sdb_data/macfour?{0}".format(path_mydir), + self.LOCAL_OPTS, + ) mock_sdb.assert_any_call( - 'sdb://vagrant_sdb_data/test4', - self.LOCAL_OPTS) - cmd = 'vagrant destroy -f macfour' - mock_cmd.assert_called_with(cmd, - runas=None, - cwd=path_mydir, - output_loglevel='info') + "sdb://vagrant_sdb_data/test4", self.LOCAL_OPTS + ) + cmd = "vagrant destroy -f macfour" + mock_cmd.assert_called_with( + cmd, runas=None, cwd=path_mydir, output_loglevel="info" + ) def test_vagrant_start(self): - mock_cmd = MagicMock(return_value={'retcode': 0}) - with patch.dict(vagrant.__salt__, {'cmd.run_all': mock_cmd}): - mock_sdb_get = MagicMock(return_value={ - 'machine': 'five', 'cwd': '/the/dir', 'runas': 'me', - 'vagrant_provider': 'him'}) - with patch.dict(vagrant.__utils__, {'sdb.sdb_get': mock_sdb_get}): - self.assertTrue(vagrant.start('test5')) - cmd = 'vagrant up five --provider=him' - mock_cmd.assert_called_with(cmd, - runas='me', - cwd='/the/dir', - output_loglevel='info') + mock_cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict(vagrant.__salt__, {"cmd.run_all": mock_cmd}): + mock_sdb_get = MagicMock( + return_value={ + "machine": "five", + "cwd": "/the/dir", + "runas": "me", + "vagrant_provider": "him", + } + ) + with patch.dict(vagrant.__utils__, {"sdb.sdb_get": mock_sdb_get}): + self.assertTrue(vagrant.start("test5")) + cmd = "vagrant up five --provider=him" + mock_cmd.assert_called_with( + cmd, runas="me", cwd="/the/dir", output_loglevel="info" + ) diff --git a/tests/unit/modules/test_varnish.py b/tests/unit/modules/test_varnish.py index df4d9f9033f..afaaa788706 100644 --- a/tests/unit/modules/test_varnish.py +++ b/tests/unit/modules/test_varnish.py @@ -1,82 +1,85 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.modules.varnish as varnish +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class VarnishTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.varnish - ''' + """ + def setup_loader_modules(self): return {varnish: {}} def test_version(self): - ''' + """ Test to return server version from varnishd -V - ''' - with patch.dict(varnish.__salt__, - {'cmd.run': MagicMock(return_value='(varnish-2.0)')}): - self.assertEqual(varnish.version(), '2.0') + """ + with patch.dict( + varnish.__salt__, {"cmd.run": MagicMock(return_value="(varnish-2.0)")} + ): + self.assertEqual(varnish.version(), "2.0") def test_ban(self): - ''' + """ Test to add ban to the varnish cache - ''' - with patch.object(varnish, '_run_varnishadm', - return_value={'retcode': 0}): - self.assertTrue(varnish.ban('ban_expression')) + """ + with patch.object(varnish, "_run_varnishadm", return_value={"retcode": 0}): + self.assertTrue(varnish.ban("ban_expression")) def test_ban_list(self): - ''' + """ Test to list varnish cache current bans - ''' - with patch.object(varnish, '_run_varnishadm', - return_value={'retcode': True}): + """ + with patch.object(varnish, "_run_varnishadm", return_value={"retcode": True}): self.assertFalse(varnish.ban_list()) - with patch.object(varnish, '_run_varnishadm', - return_value={'retcode': False, - 'stdout': 'A\nB\nC'}): - self.assertEqual(varnish.ban_list(), ['B', 'C']) + with patch.object( + varnish, + "_run_varnishadm", + return_value={"retcode": False, "stdout": "A\nB\nC"}, + ): + self.assertEqual(varnish.ban_list(), ["B", "C"]) def test_purge(self): - ''' + """ Test to purge the varnish cache - ''' - with patch.object(varnish, 'ban', return_value=True): + """ + with patch.object(varnish, "ban", return_value=True): self.assertTrue(varnish.purge()) def test_param_set(self): - ''' + """ Test to set a param in varnish cache - ''' - with patch.object(varnish, '_run_varnishadm', - return_value={'retcode': 0}): - self.assertTrue(varnish.param_set('param', 'value')) + """ + with patch.object(varnish, "_run_varnishadm", return_value={"retcode": 0}): + self.assertTrue(varnish.param_set("param", "value")) def test_param_show(self): - ''' + """ Test to show params of varnish cache - ''' - with patch.object(varnish, '_run_varnishadm', - return_value={'retcode': True, - 'stdout': 'A\nB\nC'}): - self.assertFalse(varnish.param_show('param')) + """ + with patch.object( + varnish, + "_run_varnishadm", + return_value={"retcode": True, "stdout": "A\nB\nC"}, + ): + self.assertFalse(varnish.param_show("param")) - with patch.object(varnish, '_run_varnishadm', - return_value={'retcode': False, - 'stdout': 'A .1\nB .2\n'}): - self.assertEqual(varnish.param_show('param'), {'A': '.1'}) + with patch.object( + varnish, + "_run_varnishadm", + return_value={"retcode": False, "stdout": "A .1\nB .2\n"}, + ): + self.assertEqual(varnish.param_show("param"), {"A": ".1"}) diff --git a/tests/unit/modules/test_vault.py b/tests/unit/modules/test_vault.py new file mode 100644 index 00000000000..29fa55e7090 --- /dev/null +++ b/tests/unit/modules/test_vault.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +""" +Test case for the vault execution module +""" + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt libs +import salt.modules.vault as vault +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase + + +class TestVaultModule(LoaderModuleMockMixin, TestCase): + """ + Test case for the vault execution module + """ + + def setup_loader_modules(self): + return { + vault: { + "__opts__": { + "vault": { + "url": "http://127.0.0.1", + "auth": {"token": "test", "method": "token"}, + } + }, + "__grains__": {"id": "test-minion"}, + } + } + + def test_read_secret_v1(self): + """ + Test salt.modules.vault.read_secret function + """ + version = {"v2": False, "data": None, "metadata": None, "type": None} + mock_version = MagicMock(return_value=version) + mock_vault = MagicMock() + mock_vault.return_value.status_code = 200 + mock_vault.return_value.json.return_value = {"data": {"key": "test"}} + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + vault_return = vault.read_secret("/secret/my/secret") + + self.assertDictEqual(vault_return, {"key": "test"}) + + def test_read_secret_v1_key(self): + """ + Test salt.modules.vault.read_secret function specifying key + """ + version = {"v2": False, "data": None, "metadata": None, "type": None} + mock_version = MagicMock(return_value=version) + mock_vault = MagicMock() + mock_vault.return_value.status_code = 200 + mock_vault.return_value.json.return_value = {"data": {"key": "somevalue"}} + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + vault_return = vault.read_secret("/secret/my/secret", "key") + + self.assertEqual(vault_return, "somevalue") + + def test_read_secret_v2(self): + """ + Test salt.modules.vault.read_secret function for v2 of kv secret backend + """ + # given path secrets/mysecret generate v2 output + version = { + "v2": True, + "data": "secrets/data/mysecret", + "metadata": "secrets/metadata/mysecret", + "type": "kv", + } + mock_version = MagicMock(return_value=version) + mock_vault = MagicMock() + mock_vault.return_value.status_code = 200 + v2_return = { + "data": { + "data": {"akey": "avalue"}, + "metadata": { + "created_time": "2018-10-23T20:21:55.042755098Z", + "destroyed": False, + "version": 13, + "deletion_time": "", + }, + } + } + + mock_vault.return_value.json.return_value = v2_return + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + # Validate metadata returned + vault_return = vault.read_secret("/secret/my/secret", metadata=True) + self.assertDictContainsSubset({"data": {"akey": "avalue"}}, vault_return) + # Validate just data returned + vault_return = vault.read_secret("/secret/my/secret") + self.assertDictContainsSubset({"akey": "avalue"}, vault_return) + + def test_read_secret_v2_key(self): + """ + Test salt.modules.vault.read_secret function for v2 of kv secret backend + with specified key + """ + # given path secrets/mysecret generate v2 output + version = { + "v2": True, + "data": "secrets/data/mysecret", + "metadata": "secrets/metadata/mysecret", + "type": "kv", + } + mock_version = MagicMock(return_value=version) + mock_vault = MagicMock() + mock_vault.return_value.status_code = 200 + v2_return = { + "data": { + "data": {"akey": "avalue"}, + "metadata": { + "created_time": "2018-10-23T20:21:55.042755098Z", + "destroyed": False, + "version": 13, + "deletion_time": "", + }, + } + } + + mock_vault.return_value.json.return_value = v2_return + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + vault_return = vault.read_secret("/secret/my/secret", "akey") + + self.assertEqual(vault_return, "avalue") diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py index 32f4302e5f5..e175ea2be5d 100644 --- a/tests/unit/modules/test_virt.py +++ b/tests/unit/modules/test_virt.py @@ -1,865 +1,871 @@ # -*- coding: utf-8 -*- -''' +""" virt execution module unit tests -''' +""" # pylint: disable=3rd-party-module-not-gated # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime import os import re -import datetime import shutil +import tempfile -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +import salt.config +import salt.modules.config as config +import salt.modules.virt as virt +import salt.syspaths # Import salt libs import salt.utils.yaml -import salt.modules.virt as virt -import salt.modules.config as config from salt._compat import ElementTree as ET -import salt.config -import salt.syspaths -import tempfile -from salt.exceptions import CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError # Import third party libs from salt.ext import six + # pylint: disable=import-error from salt.ext.six.moves import range # pylint: disable=redefined-builtin +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + # pylint: disable=invalid-name,protected-access,attribute-defined-outside-init,too-many-public-methods,unused-argument class LibvirtMock(MagicMock): # pylint: disable=too-many-ancestors - ''' + """ Libvirt library mock - ''' + """ + class virDomain(MagicMock): - ''' + """ virDomain mock - ''' + """ class libvirtError(Exception): - ''' + """ libvirtError mock - ''' + """ class VirtTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.module.virt - ''' + """ def setup_loader_modules(self): self.mock_libvirt = LibvirtMock() self.mock_conn = MagicMock() self.mock_libvirt.openAuth.return_value = self.mock_conn self.mock_popen = MagicMock() - self.addCleanup(delattr, self, 'mock_libvirt') - self.addCleanup(delattr, self, 'mock_conn') - self.addCleanup(delattr, self, 'mock_popen') + self.addCleanup(delattr, self, "mock_libvirt") + self.addCleanup(delattr, self, "mock_conn") + self.addCleanup(delattr, self, "mock_popen") self.mock_subprocess = MagicMock() - self.mock_subprocess.return_value = self.mock_subprocess # pylint: disable=no-member - self.mock_subprocess.Popen.return_value = self.mock_popen # pylint: disable=no-member + self.mock_subprocess.return_value = ( + self.mock_subprocess + ) # pylint: disable=no-member + self.mock_subprocess.Popen.return_value = ( + self.mock_popen + ) # pylint: disable=no-member loader_globals = { - '__salt__': { - 'config.get': config.get, - 'config.option': config.option, - }, - 'libvirt': self.mock_libvirt, - 'subprocess': self.mock_subprocess + "__salt__": {"config.get": config.get, "config.option": config.option}, + "libvirt": self.mock_libvirt, + "subprocess": self.mock_subprocess, } return {virt: loader_globals, config: loader_globals} def set_mock_vm(self, name, xml): - ''' + """ Define VM to use in tests - ''' - self.mock_conn.listDefinedDomains.return_value = [name] # pylint: disable=no-member + """ + self.mock_conn.listDefinedDomains.return_value = [ + name + ] # pylint: disable=no-member mock_domain = self.mock_libvirt.virDomain() - self.mock_conn.lookupByName.return_value = mock_domain # pylint: disable=no-member + self.mock_conn.lookupByName.return_value = ( + mock_domain # pylint: disable=no-member + ) mock_domain.XMLDesc.return_value = xml # pylint: disable=no-member # Return state as shutdown - mock_domain.info.return_value = [4, 2048 * 1024, 1024 * 1024, 2, 1234] # pylint: disable=no-member + mock_domain.info.return_value = [ + 4, + 2048 * 1024, + 1024 * 1024, + 2, + 1234, + ] # pylint: disable=no-member mock_domain.ID.return_value = 1 mock_domain.name.return_value = name return mock_domain def test_disk_profile_merge(self): - ''' + """ Test virt._disk_profile() when merging with user-defined disks - ''' - root_dir = os.path.join(salt.syspaths.ROOT_DIR, 'srv', 'salt-images') - userdisks = [{'name': 'data', 'size': 16384, 'format': 'raw'}] + """ + root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images") + userdisks = [ + {"name": "system", "image": "/path/to/image"}, + {"name": "data", "size": 16384, "format": "raw"}, + ] - disks = virt._disk_profile('default', 'kvm', userdisks, 'myvm', image='/path/to/image') + disks = virt._disk_profile("default", "kvm", userdisks, "myvm") self.assertEqual( - [{'name': 'system', - 'device': 'disk', - 'size': 8192, - 'format': 'qcow2', - 'model': 'virtio', - 'filename': 'myvm_system.qcow2', - 'image': '/path/to/image', - 'source_file': '{0}{1}myvm_system.qcow2'.format(root_dir, os.sep)}, - {'name': 'data', - 'device': 'disk', - 'size': 16384, - 'format': 'raw', - 'model': 'virtio', - 'filename': 'myvm_data.raw', - 'source_file': '{0}{1}myvm_data.raw'.format(root_dir, os.sep)}], - disks + [ + { + "name": "system", + "device": "disk", + "size": 8192, + "format": "qcow2", + "model": "virtio", + "filename": "myvm_system.qcow2", + "image": "/path/to/image", + "source_file": "{0}{1}myvm_system.qcow2".format(root_dir, os.sep), + }, + { + "name": "data", + "device": "disk", + "size": 16384, + "format": "raw", + "model": "virtio", + "filename": "myvm_data.raw", + "source_file": "{0}{1}myvm_data.raw".format(root_dir, os.sep), + }, + ], + disks, ) def test_boot_default_dev(self): - ''' + """ Test virt._gen_xml() default boot device - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') - xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'kvm', - 'hvm', - 'x86_64' - ) + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") + xml_data = virt._gen_xml("hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64") root = ET.fromstring(xml_data) - self.assertEqual(root.find('os/boot').attrib['dev'], 'hd') - self.assertEqual(root.find('os/type').attrib['arch'], 'x86_64') - self.assertEqual(root.find('os/type').text, 'hvm') + self.assertEqual(root.find("os/boot").attrib["dev"], "hd") + self.assertEqual(root.find("os/type").attrib["arch"], "x86_64") + self.assertEqual(root.find("os/type").text, "hvm") def test_boot_custom_dev(self): - ''' + """ Test virt._gen_xml() custom boot device - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'kvm', - 'hvm', - 'x86_64', - boot_dev='cdrom' - ) + "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64", boot_dev="cdrom" + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('os/boot').attrib['dev'], 'cdrom') + self.assertEqual(root.find("os/boot").attrib["dev"], "cdrom") def test_boot_multiple_devs(self): - ''' + """ Test virt._gen_xml() multiple boot devices - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - boot_dev='cdrom network' - ) + "kvm", + "hvm", + "x86_64", + boot_dev="cdrom network", + ) root = ET.fromstring(xml_data) - devs = root.findall('.//boot') + devs = root.findall(".//boot") self.assertTrue(len(devs) == 2) def test_gen_xml_no_nic(self): - ''' + """ Test virt._gen_xml() serial console - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - serial_type='pty', - console=True - ) + "kvm", + "hvm", + "x86_64", + serial_type="pty", + console=True, + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('devices/serial').attrib['type'], 'pty') - self.assertEqual(root.find('devices/console').attrib['type'], 'pty') + self.assertEqual(root.find("devices/serial").attrib["type"], "pty") + self.assertEqual(root.find("devices/console").attrib["type"], "pty") def test_gen_xml_for_serial_console(self): - ''' + """ Test virt._gen_xml() serial console - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - serial_type='pty', - console=True - ) + "kvm", + "hvm", + "x86_64", + serial_type="pty", + console=True, + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('devices/serial').attrib['type'], 'pty') - self.assertEqual(root.find('devices/console').attrib['type'], 'pty') + self.assertEqual(root.find("devices/serial").attrib["type"], "pty") + self.assertEqual(root.find("devices/console").attrib["type"], "pty") def test_gen_xml_for_telnet_console(self): - ''' + """ Test virt._gen_xml() telnet console - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - serial_type='tcp', + "kvm", + "hvm", + "x86_64", + serial_type="tcp", console=True, - telnet_port=22223 - ) + telnet_port=22223, + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('devices/serial').attrib['type'], 'tcp') - self.assertEqual(root.find('devices/console').attrib['type'], 'tcp') - self.assertEqual(root.find('devices/console/source').attrib['service'], '22223') + self.assertEqual(root.find("devices/serial").attrib["type"], "tcp") + self.assertEqual(root.find("devices/console").attrib["type"], "tcp") + self.assertEqual(root.find("devices/console/source").attrib["service"], "22223") def test_gen_xml_for_telnet_console_unspecified_port(self): - ''' + """ Test virt._gen_xml() telnet console without any specified port - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - serial_type='tcp', - console=True - ) + "kvm", + "hvm", + "x86_64", + serial_type="tcp", + console=True, + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('devices/serial').attrib['type'], 'tcp') - self.assertEqual(root.find('devices/console').attrib['type'], 'tcp') - self.assertIsInstance(int(root.find('devices/console/source').attrib['service']), int) + self.assertEqual(root.find("devices/serial").attrib["type"], "tcp") + self.assertEqual(root.find("devices/console").attrib["type"], "tcp") + self.assertIsInstance( + int(root.find("devices/console/source").attrib["service"]), int + ) def test_gen_xml_for_serial_no_console(self): - ''' + """ Test virt._gen_xml() with no serial console - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - serial_type='pty', - console=False - ) + "kvm", + "hvm", + "x86_64", + serial_type="pty", + console=False, + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('devices/serial').attrib['type'], 'pty') - self.assertEqual(root.find('devices/console'), None) + self.assertEqual(root.find("devices/serial").attrib["type"], "pty") + self.assertEqual(root.find("devices/console"), None) def test_gen_xml_for_telnet_no_console(self): - ''' + """ Test virt._gen_xml() with no telnet console - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - serial_type='tcp', + "kvm", + "hvm", + "x86_64", + serial_type="tcp", console=False, - ) + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('devices/serial').attrib['type'], 'tcp') - self.assertEqual(root.find('devices/console'), None) + self.assertEqual(root.find("devices/serial").attrib["type"], "tcp") + self.assertEqual(root.find("devices/console"), None) def test_gen_xml_nographics_default(self): - ''' + """ Test virt._gen_xml() with default no graphics device - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') - xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'kvm', - 'hvm', - 'x86_64' - ) + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") + xml_data = virt._gen_xml("hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64") root = ET.fromstring(xml_data) - self.assertIsNone(root.find('devices/graphics')) + self.assertIsNone(root.find("devices/graphics")) def test_gen_xml_vnc_default(self): - ''' + """ Test virt._gen_xml() with default vnc graphics device - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - graphics={'type': 'vnc', 'port': 1234, 'tlsPort': 5678, - 'listen': {'type': 'address', 'address': 'myhost'}}, - ) + "kvm", + "hvm", + "x86_64", + graphics={ + "type": "vnc", + "port": 1234, + "tlsPort": 5678, + "listen": {"type": "address", "address": "myhost"}, + }, + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('devices/graphics').attrib['type'], 'vnc') - self.assertEqual(root.find('devices/graphics').attrib['autoport'], 'no') - self.assertEqual(root.find('devices/graphics').attrib['port'], '1234') - self.assertFalse('tlsPort' in root.find('devices/graphics').attrib) - self.assertEqual(root.find('devices/graphics').attrib['listen'], 'myhost') - self.assertEqual(root.find('devices/graphics/listen').attrib['type'], 'address') - self.assertEqual(root.find('devices/graphics/listen').attrib['address'], 'myhost') + self.assertEqual(root.find("devices/graphics").attrib["type"], "vnc") + self.assertEqual(root.find("devices/graphics").attrib["autoport"], "no") + self.assertEqual(root.find("devices/graphics").attrib["port"], "1234") + self.assertFalse("tlsPort" in root.find("devices/graphics").attrib) + self.assertEqual(root.find("devices/graphics").attrib["listen"], "myhost") + self.assertEqual(root.find("devices/graphics/listen").attrib["type"], "address") + self.assertEqual( + root.find("devices/graphics/listen").attrib["address"], "myhost" + ) def test_gen_xml_spice_default(self): - ''' + """ Test virt._gen_xml() with default spice graphics device - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - graphics={'type': 'spice'}, - ) + "kvm", + "hvm", + "x86_64", + graphics={"type": "spice"}, + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('devices/graphics').attrib['type'], 'spice') - self.assertEqual(root.find('devices/graphics').attrib['autoport'], 'yes') - self.assertEqual(root.find('devices/graphics').attrib['listen'], '0.0.0.0') - self.assertEqual(root.find('devices/graphics/listen').attrib['type'], 'address') - self.assertEqual(root.find('devices/graphics/listen').attrib['address'], '0.0.0.0') + self.assertEqual(root.find("devices/graphics").attrib["type"], "spice") + self.assertEqual(root.find("devices/graphics").attrib["autoport"], "yes") + self.assertEqual(root.find("devices/graphics").attrib["listen"], "0.0.0.0") + self.assertEqual(root.find("devices/graphics/listen").attrib["type"], "address") + self.assertEqual( + root.find("devices/graphics/listen").attrib["address"], "0.0.0.0" + ) def test_gen_xml_spice(self): - ''' + """ Test virt._gen_xml() with spice graphics device - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") xml_data = virt._gen_xml( - 'hello', + "hello", 1, 512, diskp, nicp, - 'kvm', - 'hvm', - 'x86_64', - graphics={'type': 'spice', 'port': 1234, 'tls_port': 5678, 'listen': {'type': 'none'}}, - ) + "kvm", + "hvm", + "x86_64", + graphics={ + "type": "spice", + "port": 1234, + "tls_port": 5678, + "listen": {"type": "none"}, + }, + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('devices/graphics').attrib['type'], 'spice') - self.assertEqual(root.find('devices/graphics').attrib['autoport'], 'no') - self.assertEqual(root.find('devices/graphics').attrib['port'], '1234') - self.assertEqual(root.find('devices/graphics').attrib['tlsPort'], '5678') - self.assertFalse('listen' in root.find('devices/graphics').attrib) - self.assertEqual(root.find('devices/graphics/listen').attrib['type'], 'none') - self.assertFalse('address' in root.find('devices/graphics/listen').attrib) + self.assertEqual(root.find("devices/graphics").attrib["type"], "spice") + self.assertEqual(root.find("devices/graphics").attrib["autoport"], "no") + self.assertEqual(root.find("devices/graphics").attrib["port"], "1234") + self.assertEqual(root.find("devices/graphics").attrib["tlsPort"], "5678") + self.assertFalse("listen" in root.find("devices/graphics").attrib) + self.assertEqual(root.find("devices/graphics/listen").attrib["type"], "none") + self.assertFalse("address" in root.find("devices/graphics/listen").attrib) def test_default_disk_profile_hypervisor_esxi(self): - ''' + """ Test virt._disk_profile() default ESXi profile - ''' + """ mock = MagicMock(return_value={}) - with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member - ret = virt._disk_profile('nonexistent', 'vmware') + with patch.dict( + virt.__salt__, {"config.get": mock} + ): # pylint: disable=no-member + ret = virt._disk_profile("nonexistent", "vmware") self.assertTrue(len(ret) == 1) - found = [disk for disk in ret if disk['name'] == 'system'] + found = [disk for disk in ret if disk["name"] == "system"] self.assertTrue(bool(found)) system = found[0] - self.assertEqual(system['format'], 'vmdk') - self.assertEqual(system['model'], 'scsi') - self.assertTrue(int(system['size']) >= 1) + self.assertEqual(system["format"], "vmdk") + self.assertEqual(system["model"], "scsi") + self.assertTrue(int(system["size"]) >= 1) def test_default_disk_profile_hypervisor_kvm(self): - ''' + """ Test virt._disk_profile() default KVM profile - ''' + """ mock = MagicMock(return_value={}) - with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member - ret = virt._disk_profile('nonexistent', 'kvm') + with patch.dict( + virt.__salt__, {"config.get": mock} + ): # pylint: disable=no-member + ret = virt._disk_profile("nonexistent", "kvm") self.assertTrue(len(ret) == 1) - found = [disk for disk in ret if disk['name'] == 'system'] + found = [disk for disk in ret if disk["name"] == "system"] self.assertTrue(bool(found)) system = found[0] - self.assertEqual(system['format'], 'qcow2') - self.assertEqual(system['model'], 'virtio') - self.assertTrue(int(system['size']) >= 1) + self.assertEqual(system["format"], "qcow2") + self.assertEqual(system["model"], "virtio") + self.assertTrue(int(system["size"]) >= 1) def test_default_disk_profile_hypervisor_xen(self): - ''' + """ Test virt._disk_profile() default XEN profile - ''' + """ mock = MagicMock(return_value={}) - with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member - ret = virt._disk_profile('nonexistent', 'xen') + with patch.dict( + virt.__salt__, {"config.get": mock} + ): # pylint: disable=no-member + ret = virt._disk_profile("nonexistent", "xen") self.assertTrue(len(ret) == 1) - found = [disk for disk in ret if disk['name'] == 'system'] + found = [disk for disk in ret if disk["name"] == "system"] self.assertTrue(bool(found)) system = found[0] - self.assertEqual(system['format'], 'qcow2') - self.assertEqual(system['model'], 'xen') - self.assertTrue(int(system['size']) >= 1) + self.assertEqual(system["format"], "qcow2") + self.assertEqual(system["model"], "xen") + self.assertTrue(int(system["size"]) >= 1) def test_default_nic_profile_hypervisor_esxi(self): - ''' + """ Test virt._nic_profile() default ESXi profile - ''' + """ mock = MagicMock(return_value={}) - with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member - ret = virt._nic_profile('nonexistent', 'vmware') + with patch.dict( + virt.__salt__, {"config.get": mock} + ): # pylint: disable=no-member + ret = virt._nic_profile("nonexistent", "vmware") self.assertTrue(len(ret) == 1) eth0 = ret[0] - self.assertEqual(eth0['name'], 'eth0') - self.assertEqual(eth0['type'], 'bridge') - self.assertEqual(eth0['source'], 'DEFAULT') - self.assertEqual(eth0['model'], 'e1000') + self.assertEqual(eth0["name"], "eth0") + self.assertEqual(eth0["type"], "bridge") + self.assertEqual(eth0["source"], "DEFAULT") + self.assertEqual(eth0["model"], "e1000") def test_default_nic_profile_hypervisor_kvm(self): - ''' + """ Test virt._nic_profile() default KVM profile - ''' + """ mock = MagicMock(return_value={}) - with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member - ret = virt._nic_profile('nonexistent', 'kvm') + with patch.dict( + virt.__salt__, {"config.get": mock} + ): # pylint: disable=no-member + ret = virt._nic_profile("nonexistent", "kvm") self.assertTrue(len(ret) == 1) eth0 = ret[0] - self.assertEqual(eth0['name'], 'eth0') - self.assertEqual(eth0['type'], 'bridge') - self.assertEqual(eth0['source'], 'br0') - self.assertEqual(eth0['model'], 'virtio') + self.assertEqual(eth0["name"], "eth0") + self.assertEqual(eth0["type"], "bridge") + self.assertEqual(eth0["source"], "br0") + self.assertEqual(eth0["model"], "virtio") def test_default_nic_profile_hypervisor_xen(self): - ''' + """ Test virt._nic_profile() default XEN profile - ''' + """ mock = MagicMock(return_value={}) - with patch.dict(virt.__salt__, {'config.get': mock}): # pylint: disable=no-member - ret = virt._nic_profile('nonexistent', 'xen') + with patch.dict( + virt.__salt__, {"config.get": mock} + ): # pylint: disable=no-member + ret = virt._nic_profile("nonexistent", "xen") self.assertTrue(len(ret) == 1) eth0 = ret[0] - self.assertEqual(eth0['name'], 'eth0') - self.assertEqual(eth0['type'], 'bridge') - self.assertEqual(eth0['source'], 'br0') - self.assertFalse(eth0['model']) + self.assertEqual(eth0["name"], "eth0") + self.assertEqual(eth0["type"], "bridge") + self.assertEqual(eth0["source"], "br0") + self.assertFalse(eth0["model"]) def test_gen_vol_xml(self): - ''' + """ Test virt._get_vol_xml() - ''' - xml_data = virt._gen_vol_xml('vmname', 'system', 'qcow2', 8192, '/path/to/image/') + """ + xml_data = virt._gen_vol_xml( + "vmname", "system", "qcow2", 8192, "/path/to/image/" + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'vmname/system.qcow2') - self.assertEqual(root.find('key').text, 'vmname/system') - self.assertEqual(root.find('capacity').attrib['unit'], 'KiB') - self.assertEqual(root.find('capacity').text, six.text_type(8192 * 1024)) + self.assertEqual(root.find("name").text, "vmname/system.qcow2") + self.assertEqual(root.find("key").text, "vmname/system") + self.assertEqual(root.find("capacity").attrib["unit"], "KiB") + self.assertEqual(root.find("capacity").text, six.text_type(8192 * 1024)) def test_gen_xml_for_kvm_default_profile(self): - ''' + """ Test virt._gen_xml(), KVM default profile case - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') - xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'kvm', - 'hvm', - 'x86_64', - ) + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") + xml_data = virt._gen_xml("hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",) root = ET.fromstring(xml_data) - self.assertEqual(root.attrib['type'], 'kvm') - self.assertEqual(root.find('vcpu').text, '1') - self.assertEqual(root.find('memory').text, six.text_type(512 * 1024)) - self.assertEqual(root.find('memory').attrib['unit'], 'KiB') + self.assertEqual(root.attrib["type"], "kvm") + self.assertEqual(root.find("vcpu").text, "1") + self.assertEqual(root.find("memory").text, six.text_type(512 * 1024)) + self.assertEqual(root.find("memory").attrib["unit"], "KiB") - disks = root.findall('.//disk') + disks = root.findall(".//disk") self.assertEqual(len(disks), 1) disk = disks[0] - root_dir = salt.config.DEFAULT_MINION_OPTS.get('root_dir') - self.assertTrue(disk.find('source').attrib['file'].startswith(root_dir)) - self.assertTrue('hello_system' in disk.find('source').attrib['file']) - self.assertEqual(disk.find('target').attrib['dev'], 'vda') - self.assertEqual(disk.find('target').attrib['bus'], 'virtio') - self.assertEqual(disk.find('driver').attrib['name'], 'qemu') - self.assertEqual(disk.find('driver').attrib['type'], 'qcow2') + root_dir = salt.config.DEFAULT_MINION_OPTS.get("root_dir") + self.assertTrue(disk.find("source").attrib["file"].startswith(root_dir)) + self.assertTrue("hello_system" in disk.find("source").attrib["file"]) + self.assertEqual(disk.find("target").attrib["dev"], "vda") + self.assertEqual(disk.find("target").attrib["bus"], "virtio") + self.assertEqual(disk.find("driver").attrib["name"], "qemu") + self.assertEqual(disk.find("driver").attrib["type"], "qcow2") - interfaces = root.findall('.//interface') + interfaces = root.findall(".//interface") self.assertEqual(len(interfaces), 1) iface = interfaces[0] - self.assertEqual(iface.attrib['type'], 'bridge') - self.assertEqual(iface.find('source').attrib['bridge'], 'br0') - self.assertEqual(iface.find('model').attrib['type'], 'virtio') + self.assertEqual(iface.attrib["type"], "bridge") + self.assertEqual(iface.find("source").attrib["bridge"], "br0") + self.assertEqual(iface.find("model").attrib["type"], "virtio") - mac = iface.find('mac').attrib['address'] - self.assertTrue( - re.match('^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$', mac, re.I)) + mac = iface.find("mac").attrib["address"] + self.assertTrue(re.match("^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$", mac, re.I)) def test_gen_xml_for_esxi_default_profile(self): - ''' + """ Test virt._gen_xml(), ESXi/vmware default profile case - ''' - diskp = virt._disk_profile('default', 'vmware', [], 'hello') - nicp = virt._nic_profile('default', 'vmware') + """ + diskp = virt._disk_profile("default", "vmware", [], "hello") + nicp = virt._nic_profile("default", "vmware") xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'vmware', - 'hvm', - 'x86_64', - ) + "hello", 1, 512, diskp, nicp, "vmware", "hvm", "x86_64", + ) root = ET.fromstring(xml_data) - self.assertEqual(root.attrib['type'], 'vmware') - self.assertEqual(root.find('vcpu').text, '1') - self.assertEqual(root.find('memory').text, six.text_type(512 * 1024)) - self.assertEqual(root.find('memory').attrib['unit'], 'KiB') + self.assertEqual(root.attrib["type"], "vmware") + self.assertEqual(root.find("vcpu").text, "1") + self.assertEqual(root.find("memory").text, six.text_type(512 * 1024)) + self.assertEqual(root.find("memory").attrib["unit"], "KiB") - disks = root.findall('.//disk') + disks = root.findall(".//disk") self.assertEqual(len(disks), 1) disk = disks[0] - self.assertTrue('[0]' in disk.find('source').attrib['file']) - self.assertTrue('hello_system' in disk.find('source').attrib['file']) - self.assertEqual(disk.find('target').attrib['dev'], 'sda') - self.assertEqual(disk.find('target').attrib['bus'], 'scsi') - self.assertEqual(disk.find('address').attrib['unit'], '0') + self.assertTrue("[0]" in disk.find("source").attrib["file"]) + self.assertTrue("hello_system" in disk.find("source").attrib["file"]) + self.assertEqual(disk.find("target").attrib["dev"], "sda") + self.assertEqual(disk.find("target").attrib["bus"], "scsi") + self.assertEqual(disk.find("address").attrib["unit"], "0") - interfaces = root.findall('.//interface') + interfaces = root.findall(".//interface") self.assertEqual(len(interfaces), 1) iface = interfaces[0] - self.assertEqual(iface.attrib['type'], 'bridge') - self.assertEqual(iface.find('source').attrib['bridge'], 'DEFAULT') - self.assertEqual(iface.find('model').attrib['type'], 'e1000') + self.assertEqual(iface.attrib["type"], "bridge") + self.assertEqual(iface.find("source").attrib["bridge"], "DEFAULT") + self.assertEqual(iface.find("model").attrib["type"], "e1000") - mac = iface.find('mac').attrib['address'] - self.assertTrue( - re.match('^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$', mac, re.I)) + mac = iface.find("mac").attrib["address"] + self.assertTrue(re.match("^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$", mac, re.I)) def test_gen_xml_for_xen_default_profile(self): - ''' + """ Test virt._gen_xml(), XEN PV default profile case - ''' - diskp = virt._disk_profile('default', 'xen', [], 'hello') - nicp = virt._nic_profile('default', 'xen') - with patch.dict(virt.__grains__, {'os_family': 'Suse'}): + """ + diskp = virt._disk_profile("default", "xen", [], "hello") + nicp = virt._nic_profile("default", "xen") + with patch.dict(virt.__grains__, {"os_family": "Suse"}): xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'xen', - 'xen', - 'x86_64', - boot=None - ) + "hello", 1, 512, diskp, nicp, "xen", "xen", "x86_64", boot=None + ) root = ET.fromstring(xml_data) - self.assertEqual(root.attrib['type'], 'xen') - self.assertEqual(root.find('vcpu').text, '1') - self.assertEqual(root.find('memory').text, six.text_type(512 * 1024)) - self.assertEqual(root.find('memory').attrib['unit'], 'KiB') - self.assertEqual(root.find('.//kernel').text, '/usr/lib/grub2/x86_64-xen/grub.xen') + self.assertEqual(root.attrib["type"], "xen") + self.assertEqual(root.find("vcpu").text, "1") + self.assertEqual(root.find("memory").text, six.text_type(512 * 1024)) + self.assertEqual(root.find("memory").attrib["unit"], "KiB") + self.assertEqual( + root.find(".//kernel").text, "/usr/lib/grub2/x86_64-xen/grub.xen" + ) - disks = root.findall('.//disk') + disks = root.findall(".//disk") self.assertEqual(len(disks), 1) disk = disks[0] - root_dir = salt.config.DEFAULT_MINION_OPTS.get('root_dir') - self.assertTrue(disk.find('source').attrib['file'].startswith(root_dir)) - self.assertTrue('hello_system' in disk.find('source').attrib['file']) - self.assertEqual(disk.find('target').attrib['dev'], 'xvda') - self.assertEqual(disk.find('target').attrib['bus'], 'xen') - self.assertEqual(disk.find('driver').attrib['name'], 'qemu') - self.assertEqual(disk.find('driver').attrib['type'], 'qcow2') + root_dir = salt.config.DEFAULT_MINION_OPTS.get("root_dir") + self.assertTrue(disk.find("source").attrib["file"].startswith(root_dir)) + self.assertTrue("hello_system" in disk.find("source").attrib["file"]) + self.assertEqual(disk.find("target").attrib["dev"], "xvda") + self.assertEqual(disk.find("target").attrib["bus"], "xen") + self.assertEqual(disk.find("driver").attrib["name"], "qemu") + self.assertEqual(disk.find("driver").attrib["type"], "qcow2") - interfaces = root.findall('.//interface') + interfaces = root.findall(".//interface") self.assertEqual(len(interfaces), 1) iface = interfaces[0] - self.assertEqual(iface.attrib['type'], 'bridge') - self.assertEqual(iface.find('source').attrib['bridge'], 'br0') - self.assertIsNone(iface.find('model')) + self.assertEqual(iface.attrib["type"], "bridge") + self.assertEqual(iface.find("source").attrib["bridge"], "br0") + self.assertIsNone(iface.find("model")) def test_gen_xml_for_esxi_custom_profile(self): - ''' + """ Test virt._gen_xml(), ESXi/vmware custom profile case - ''' + """ disks = { - 'noeffect': [ - {'first': {'size': 8192, 'pool': 'datastore1'}}, - {'second': {'size': 4096, 'pool': 'datastore2'}} + "noeffect": [ + {"first": {"size": 8192, "pool": "datastore1"}}, + {"second": {"size": 4096, "pool": "datastore2"}}, ] } nics = { - 'noeffect': [ - {'name': 'eth1', 'source': 'ONENET'}, - {'name': 'eth2', 'source': 'TWONET'} + "noeffect": [ + {"name": "eth1", "source": "ONENET"}, + {"name": "eth2", "source": "TWONET"}, ] } - with patch.dict(virt.__salt__, # pylint: disable=no-member - {'config.get': MagicMock(side_effect=[disks, nics])}): - diskp = virt._disk_profile('noeffect', 'vmware', [], 'hello') - nicp = virt._nic_profile('noeffect', 'vmware') + with patch.dict( + virt.__salt__, # pylint: disable=no-member + {"config.get": MagicMock(side_effect=[disks, nics])}, + ): + diskp = virt._disk_profile("noeffect", "vmware", [], "hello") + nicp = virt._nic_profile("noeffect", "vmware") xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'vmware', - 'hvm', - 'x86_64', - ) + "hello", 1, 512, diskp, nicp, "vmware", "hvm", "x86_64", + ) root = ET.fromstring(xml_data) - self.assertEqual(root.attrib['type'], 'vmware') - self.assertEqual(root.find('vcpu').text, '1') - self.assertEqual(root.find('memory').text, six.text_type(512 * 1024)) - self.assertEqual(root.find('memory').attrib['unit'], 'KiB') - self.assertTrue(len(root.findall('.//disk')) == 2) - self.assertTrue(len(root.findall('.//interface')) == 2) + self.assertEqual(root.attrib["type"], "vmware") + self.assertEqual(root.find("vcpu").text, "1") + self.assertEqual(root.find("memory").text, six.text_type(512 * 1024)) + self.assertEqual(root.find("memory").attrib["unit"], "KiB") + self.assertTrue(len(root.findall(".//disk")) == 2) + self.assertTrue(len(root.findall(".//interface")) == 2) def test_gen_xml_for_kvm_custom_profile(self): - ''' + """ Test virt._gen_xml(), KVM custom profile case - ''' + """ disks = { - 'noeffect': [ - {'first': {'size': 8192, 'pool': '/var/lib/images'}}, - {'second': {'size': 4096, 'pool': '/var/lib/images'}} + "noeffect": [ + {"first": {"size": 8192, "pool": "/var/lib/images"}}, + {"second": {"size": 4096, "pool": "/var/lib/images"}}, ] } nics = { - 'noeffect': [ - {'name': 'eth1', 'source': 'b2'}, - {'name': 'eth2', 'source': 'b2'} + "noeffect": [ + {"name": "eth1", "source": "b2"}, + {"name": "eth2", "source": "b2"}, ] } - with patch.dict(virt.__salt__, {'config.get': MagicMock(side_effect=[ # pylint: disable=no-member - disks, nics])}): - diskp = virt._disk_profile('noeffect', 'kvm', [], 'hello') - nicp = virt._nic_profile('noeffect', 'kvm') - xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'kvm', - 'hvm', - 'x86_64', + with patch.dict( + virt.__salt__, + { + "config.get": MagicMock( + side_effect=[disks, nics] # pylint: disable=no-member ) + }, + ): + diskp = virt._disk_profile("noeffect", "kvm", [], "hello") + nicp = virt._nic_profile("noeffect", "kvm") + xml_data = virt._gen_xml( + "hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64", + ) root = ET.fromstring(xml_data) - self.assertEqual(root.attrib['type'], 'kvm') - self.assertEqual(root.find('vcpu').text, '1') - self.assertEqual(root.find('memory').text, six.text_type(512 * 1024)) - self.assertEqual(root.find('memory').attrib['unit'], 'KiB') - self.assertTrue(len(root.findall('.//disk')) == 2) - self.assertTrue(len(root.findall('.//interface')) == 2) + self.assertEqual(root.attrib["type"], "kvm") + self.assertEqual(root.find("vcpu").text, "1") + self.assertEqual(root.find("memory").text, six.text_type(512 * 1024)) + self.assertEqual(root.find("memory").attrib["unit"], "KiB") + self.assertTrue(len(root.findall(".//disk")) == 2) + self.assertTrue(len(root.findall(".//interface")) == 2) - @patch('salt.modules.virt.pool_info', - return_value={'mypool': {'target_path': os.path.join(salt.syspaths.ROOT_DIR, 'pools', 'mypool')}}) + @patch( + "salt.modules.virt.pool_info", + return_value={ + "mypool": { + "target_path": os.path.join(salt.syspaths.ROOT_DIR, "pools", "mypool") + } + }, + ) def test_disk_profile_kvm_disk_pool(self, mock_poolinfo): - ''' + """ Test virt._gen_xml(), KVM case with pools defined. - ''' + """ disks = { - 'noeffect': [ - {'first': {'size': 8192, 'pool': 'mypool'}}, - {'second': {'size': 4096}} + "noeffect": [ + {"first": {"size": 8192, "pool": "mypool"}}, + {"second": {"size": 4096}}, ] } # pylint: disable=no-member - with patch.dict(virt.__salt__, {'config.get': MagicMock(side_effect=[ - disks, - os.path.join(salt.syspaths.ROOT_DIR, 'default', 'path')])}): + with patch.dict( + virt.__salt__, + { + "config.get": MagicMock( + side_effect=[ + disks, + os.path.join(salt.syspaths.ROOT_DIR, "default", "path"), + ] + ) + }, + ): - diskp = virt._disk_profile('noeffect', 'kvm', [], 'hello') + diskp = virt._disk_profile("noeffect", "kvm", [], "hello") - pools_path = os.path.join(salt.syspaths.ROOT_DIR, 'pools', 'mypool') + os.sep - default_path = os.path.join(salt.syspaths.ROOT_DIR, 'default', 'path') + os.sep + pools_path = ( + os.path.join(salt.syspaths.ROOT_DIR, "pools", "mypool") + os.sep + ) + default_path = ( + os.path.join(salt.syspaths.ROOT_DIR, "default", "path") + os.sep + ) self.assertEqual(len(diskp), 2) - self.assertTrue(diskp[0]['source_file'].startswith(pools_path)) - self.assertTrue(diskp[1]['source_file'].startswith(default_path)) + self.assertTrue(diskp[0]["source_file"].startswith(pools_path)) + self.assertTrue(diskp[1]["source_file"].startswith(default_path)) # pylint: enable=no-member def test_disk_profile_kvm_disk_external_image(self): - ''' + """ Test virt._gen_xml(), KVM case with an external image. - ''' - diskp = virt._disk_profile(None, 'kvm', [ - { - 'name': 'mydisk', - 'source_file': '/path/to/my/image.qcow2' - }], 'hello') + """ + diskp = virt._disk_profile( + None, + "kvm", + [{"name": "mydisk", "source_file": "/path/to/my/image.qcow2"}], + "hello", + ) self.assertEqual(len(diskp), 1) - self.assertEqual(diskp[0]['source_file'], ('/path/to/my/image.qcow2')) + self.assertEqual(diskp[0]["source_file"], ("/path/to/my/image.qcow2")) - @patch('salt.modules.virt.pool_info', return_value={}) + @patch("salt.modules.virt.pool_info", return_value={}) def test_disk_profile_kvm_disk_pool_notfound(self, mock_poolinfo): - ''' + """ Test virt._gen_xml(), KVM case with pools defined. - ''' - disks = { - 'noeffect': [ - {'first': {'size': 8192, 'pool': 'default'}}, - ] - } - with patch.dict(virt.__salt__, {'config.get': MagicMock(side_effect=[ # pylint: disable=no-member - disks, "/default/path/"])}): + """ + disks = {"noeffect": [{"first": {"size": 8192, "pool": "default"}}]} + with patch.dict( + virt.__salt__, + { + "config.get": MagicMock( + side_effect=[disks, "/default/path/"] # pylint: disable=no-member + ) + }, + ): with self.assertRaises(CommandExecutionError): - virt._disk_profile('noeffect', 'kvm', [], 'hello') + virt._disk_profile("noeffect", "kvm", [], "hello") - @patch('salt.modules.virt.pool_info', return_value={'target_path': '/dev/disk/by-path'}) + @patch( + "salt.modules.virt.pool_info", return_value={"target_path": "/dev/disk/by-path"} + ) def test_disk_profile_kvm_disk_pool_invalid(self, mock_poolinfo): - ''' + """ Test virt._gen_xml(), KVM case with pools defined. - ''' - disks = { - 'noeffect': [ - {'first': {'size': 8192, 'pool': 'default'}}, - ] - } - with patch.dict(virt.__salt__, {'config.get': MagicMock(side_effect=[ # pylint: disable=no-member - disks, "/default/path/"])}): + """ + disks = {"noeffect": [{"first": {"size": 8192, "pool": "default"}}]} + with patch.dict( + virt.__salt__, + { + "config.get": MagicMock( + side_effect=[disks, "/default/path/"] # pylint: disable=no-member + ) + }, + ): with self.assertRaises(CommandExecutionError): - virt._disk_profile('noeffect', 'kvm', [], 'hello') + virt._disk_profile("noeffect", "kvm", [], "hello") def test_gen_xml_cdrom(self): - ''' + """ Test virt._gen_xml(), generating a cdrom device (different disk type, no source) - ''' - diskp = virt._disk_profile(None, 'kvm', [{ - 'name': 'tested', - 'device': 'cdrom', - 'source_file': None, - 'model': 'ide'}], 'hello') - nicp = virt._nic_profile(None, 'kvm') - xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'kvm', - 'hvm', - 'x86_64', - ) + """ + diskp = virt._disk_profile( + None, + "kvm", + [ + { + "name": "tested", + "device": "cdrom", + "source_file": None, + "model": "ide", + } + ], + "hello", + ) + nicp = virt._nic_profile(None, "kvm") + xml_data = virt._gen_xml("hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",) root = ET.fromstring(xml_data) - disk = root.findall('.//disk')[0] - self.assertEqual(disk.attrib['device'], 'cdrom') - self.assertIsNone(disk.find('source')) + disk = root.findall(".//disk")[0] + self.assertEqual(disk.attrib["device"], "cdrom") + self.assertIsNone(disk.find("source")) def test_controller_for_esxi(self): - ''' + """ Test virt._gen_xml() generated device controller for ESXi/vmware - ''' - diskp = virt._disk_profile('default', 'vmware', [], 'hello') - nicp = virt._nic_profile('default', 'vmware') + """ + diskp = virt._disk_profile("default", "vmware", [], "hello") + nicp = virt._nic_profile("default", "vmware") xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'vmware', - 'hvm', - 'x86_64', - ) + "hello", 1, 512, diskp, nicp, "vmware", "hvm", "x86_64", + ) root = ET.fromstring(xml_data) - controllers = root.findall('.//devices/controller') + controllers = root.findall(".//devices/controller") self.assertTrue(len(controllers) == 1) controller = controllers[0] - self.assertEqual(controller.attrib['model'], 'lsilogic') + self.assertEqual(controller.attrib["model"], "lsilogic") def test_controller_for_kvm(self): - ''' + """ Test virt._gen_xml() generated device controller for KVM - ''' - diskp = virt._disk_profile('default', 'kvm', [], 'hello') - nicp = virt._nic_profile('default', 'kvm') - xml_data = virt._gen_xml( - 'hello', - 1, - 512, - diskp, - nicp, - 'kvm', - 'hvm', - 'x86_64', - ) + """ + diskp = virt._disk_profile("default", "kvm", [], "hello") + nicp = virt._nic_profile("default", "kvm") + xml_data = virt._gen_xml("hello", 1, 512, diskp, nicp, "kvm", "hvm", "x86_64",) root = ET.fromstring(xml_data) - controllers = root.findall('.//devices/controller') + controllers = root.findall(".//devices/controller") # There should be no controller self.assertTrue(len(controllers) == 0) # kvm mac address shoud start with 52:54:00 self.assertTrue("mac address='52:54:00" in xml_data) def test_diff_disks(self): - ''' + """ Test virt._diff_disks() - ''' - old_disks = ET.fromstring(''' + """ + old_disks = ET.fromstring( + """ <devices> <disk type='file' device='disk'> <source file='/path/to/img0.qcow2'/> @@ -881,9 +887,11 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <target dev='hdc' bus='ide'/> </disk> </devices> - ''').findall('disk') + """ + ).findall("disk") - new_disks = ET.fromstring(''' + new_disks = ET.fromstring( + """ <devices> <disk type='file' device='disk'> <source file='/path/to/img3.qcow2'/> @@ -901,35 +909,63 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <target dev='hda' bus='ide'/> </disk> </devices> - ''').findall('disk') + """ + ).findall("disk") ret = virt._diff_disk_lists(old_disks, new_disks) - self.assertEqual([disk.find('source').get('file') if disk.find('source') is not None else None - for disk in ret['unchanged']], []) - self.assertEqual([disk.find('source').get('file') if disk.find('source') is not None else None - for disk in ret['new']], - ['/path/to/img3.qcow2', '/path/to/img0.qcow2', '/path/to/img4.qcow2', None]) - self.assertEqual([disk.find('target').get('dev') for disk in ret['sorted']], - ['vda', 'vdb', 'vdc', 'hda']) - self.assertEqual([disk.find('source').get('file') if disk.find('source') is not None else None - for disk in ret['sorted']], - ['/path/to/img3.qcow2', - '/path/to/img0.qcow2', - '/path/to/img4.qcow2', - None]) - self.assertEqual(ret['new'][1].find('target').get('bus'), 'virtio') - self.assertEqual([disk.find('source').get('file') if disk.find('source') is not None else None - for disk in ret['deleted']], - ['/path/to/img0.qcow2', - '/path/to/img1.qcow2', - '/path/to/img2.qcow2', - '/path/to/img4.qcow2', - None]) + self.assertEqual( + [ + disk.find("source").get("file") + if disk.find("source") is not None + else None + for disk in ret["unchanged"] + ], + [], + ) + self.assertEqual( + [ + disk.find("source").get("file") + if disk.find("source") is not None + else None + for disk in ret["new"] + ], + ["/path/to/img3.qcow2", "/path/to/img0.qcow2", "/path/to/img4.qcow2", None], + ) + self.assertEqual( + [disk.find("target").get("dev") for disk in ret["sorted"]], + ["vda", "vdb", "vdc", "hda"], + ) + self.assertEqual( + [ + disk.find("source").get("file") + if disk.find("source") is not None + else None + for disk in ret["sorted"] + ], + ["/path/to/img3.qcow2", "/path/to/img0.qcow2", "/path/to/img4.qcow2", None], + ) + self.assertEqual(ret["new"][1].find("target").get("bus"), "virtio") + self.assertEqual( + [ + disk.find("source").get("file") + if disk.find("source") is not None + else None + for disk in ret["deleted"] + ], + [ + "/path/to/img0.qcow2", + "/path/to/img1.qcow2", + "/path/to/img2.qcow2", + "/path/to/img4.qcow2", + None, + ], + ) def test_diff_nics(self): - ''' + """ Test virt._diff_nics() - ''' - old_nics = ET.fromstring(''' + """ + old_nics = ET.fromstring( + """ <devices> <interface type='network'> <mac address='52:54:00:39:02:b1'/> @@ -950,9 +986,11 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> </interface> </devices> - ''').findall('interface') + """ + ).findall("interface") - new_nics = ET.fromstring(''' + new_nics = ET.fromstring( + """ <devices> <interface type='network'> <mac address='52:54:00:39:02:b1'/> @@ -970,20 +1008,27 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <model type='virtio'/> </interface> </devices> - ''').findall('interface') + """ + ).findall("interface") ret = virt._diff_interface_lists(old_nics, new_nics) - self.assertEqual([nic.find('mac').get('address') for nic in ret['unchanged']], - ['52:54:00:39:02:b1']) - self.assertEqual([nic.find('mac').get('address') for nic in ret['new']], - ['52:54:00:39:02:b2', '52:54:00:39:02:b4']) - self.assertEqual([nic.find('mac').get('address') for nic in ret['deleted']], - ['52:54:00:39:02:b2', '52:54:00:39:02:b3']) + self.assertEqual( + [nic.find("mac").get("address") for nic in ret["unchanged"]], + ["52:54:00:39:02:b1"], + ) + self.assertEqual( + [nic.find("mac").get("address") for nic in ret["new"]], + ["52:54:00:39:02:b2", "52:54:00:39:02:b4"], + ) + self.assertEqual( + [nic.find("mac").get("address") for nic in ret["deleted"]], + ["52:54:00:39:02:b2", "52:54:00:39:02:b3"], + ) def test_init(self): - ''' + """ Test init() function - ''' - xml = ''' + """ + xml = """ <capabilities> <host> <uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid> @@ -1101,29 +1146,29 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): </guest> </capabilities> - ''' + """ self.mock_conn.getCapabilities.return_value = xml # pylint: disable=no-member - root_dir = os.path.join(salt.syspaths.ROOT_DIR, 'srv', 'salt-images') + root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images") defineMock = MagicMock(return_value=1) self.mock_conn.defineXML = defineMock mock_chmod = MagicMock() mock_run = MagicMock() - with patch.dict(os.__dict__, {'chmod': mock_chmod, 'makedirs': MagicMock()}): # pylint: disable=no-member - with patch.dict(virt.__salt__, {'cmd.run': mock_run}): # pylint: disable=no-member + with patch.dict( + os.__dict__, {"chmod": mock_chmod, "makedirs": MagicMock()} + ): # pylint: disable=no-member + with patch.dict( + virt.__salt__, {"cmd.run": mock_run} + ): # pylint: disable=no-member # Ensure the init() function allows creating VM without NIC and disk - virt.init('test vm', - 2, - 1234, - nic=None, - disk=None, - seed=False, - start=False) + virt.init( + "test vm", 2, 1234, nic=None, disk=None, seed=False, start=False + ) definition = defineMock.call_args_list[0][0][0] - self.assertFalse('<interface' in definition) - self.assertFalse('<disk' in definition) + self.assertFalse("<interface" in definition) + self.assertFalse("<disk" in definition) # Ensure the init() function allows creating VM without NIC and # disk but with boot parameters. @@ -1131,56 +1176,60 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): defineMock.reset_mock() mock_run.reset_mock() boot = { - 'kernel': '/root/f8-i386-vmlinuz', - 'initrd': '/root/f8-i386-initrd', - 'cmdline': - 'console=ttyS0 ks=http://example.com/f8-i386/os/' + "kernel": "/root/f8-i386-vmlinuz", + "initrd": "/root/f8-i386-initrd", + "cmdline": "console=ttyS0 ks=http://example.com/f8-i386/os/", } - retval = virt.init('test vm boot params', - 2, - 1234, - nic=None, - disk=None, - seed=False, - start=False, - boot=boot) + retval = virt.init( + "test vm boot params", + 2, + 1234, + nic=None, + disk=None, + seed=False, + start=False, + boot=boot, + ) definition = defineMock.call_args_list[0][0][0] - self.assertEqual('<kernel' in definition, True) - self.assertEqual('<initrd' in definition, True) - self.assertEqual('<cmdline' in definition, True) + self.assertEqual("<kernel" in definition, True) + self.assertEqual("<initrd" in definition, True) + self.assertEqual("<cmdline" in definition, True) self.assertEqual(retval, True) # Verify that remote paths are downloaded and the xml has been # modified mock_response = MagicMock() - mock_response.read = MagicMock(return_value='filecontent') + mock_response.read = MagicMock(return_value="filecontent") cache_dir = tempfile.mkdtemp() - with patch.dict(virt.__dict__, {'CACHE_DIR': cache_dir}): - with patch('salt.ext.six.moves.urllib.request.urlopen', - MagicMock(return_value=mock_response)): - with patch('salt.utils.files.fopen', - return_value=mock_response): + with patch.dict(virt.__dict__, {"CACHE_DIR": cache_dir}): + with patch( + "salt.ext.six.moves.urllib.request.urlopen", + MagicMock(return_value=mock_response), + ): + with patch( + "salt.utils.files.fopen", return_value=mock_response + ): defineMock.reset_mock() mock_run.reset_mock() boot = { - 'kernel': - 'https://www.example.com/download/vmlinuz', - 'initrd': '', - 'cmdline': - 'console=ttyS0 ' - 'ks=http://example.com/f8-i386/os/' + "kernel": "https://www.example.com/download/vmlinuz", + "initrd": "", + "cmdline": "console=ttyS0 " + "ks=http://example.com/f8-i386/os/", } - retval = virt.init('test remote vm boot params', - 2, - 1234, - nic=None, - disk=None, - seed=False, - start=False, - boot=boot) + retval = virt.init( + "test remote vm boot params", + 2, + 1234, + nic=None, + disk=None, + seed=False, + start=False, + boot=boot, + ) definition = defineMock.call_args_list[0][0][0] self.assertEqual(cache_dir in definition, True) @@ -1189,34 +1238,47 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): # Test case creating disks defineMock.reset_mock() mock_run.reset_mock() - virt.init('test vm', - 2, - 1234, - nic=None, - disk=None, - disks=[ - {'name': 'system', 'size': 10240}, - {'name': 'cddrive', 'device': 'cdrom', 'source_file': None, 'model': 'ide'} - ], - seed=False, - start=False) + virt.init( + "test vm", + 2, + 1234, + nic=None, + disk=None, + disks=[ + {"name": "system", "size": 10240}, + { + "name": "cddrive", + "device": "cdrom", + "source_file": None, + "model": "ide", + }, + ], + seed=False, + start=False, + ) definition = ET.fromstring(defineMock.call_args_list[0][0][0]) - disk_sources = [disk.find('source').get('file') if disk.find('source') is not None else None - for disk in definition.findall('./devices/disk')] - expected_disk_path = os.path.join(root_dir, 'test vm_system.qcow2') + disk_sources = [ + disk.find("source").get("file") + if disk.find("source") is not None + else None + for disk in definition.findall("./devices/disk") + ] + expected_disk_path = os.path.join(root_dir, "test vm_system.qcow2") self.assertEqual(disk_sources, [expected_disk_path, None]) - self.assertEqual(mock_run.call_args[0][0], - 'qemu-img create -f qcow2 "{0}" 10240M'.format(expected_disk_path)) + self.assertEqual( + mock_run.call_args[0][0], + 'qemu-img create -f qcow2 "{0}" 10240M'.format(expected_disk_path), + ) self.assertEqual(mock_chmod.call_args[0][0], expected_disk_path) def test_update(self): - ''' + """ Test virt.update() - ''' - root_dir = os.path.join(salt.syspaths.ROOT_DIR, 'srv', 'salt-images') - xml = ''' + """ + root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images") + xml = """ <domain type='kvm' id='7'> - <name>my vm</name> + <name>my_vm</name> <memory unit='KiB'>1048576</memory> <currentMemory unit='KiB'>1048576</currentMemory> <vcpu placement='auto'>1</vcpu> @@ -1226,7 +1288,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <devices> <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> - <source file='{0}{1}my vm_system.qcow2'/> + <source file='{0}{1}my_vm_system.qcow2'/> <backingStore/> <target dev='vda' bus='virtio'/> <alias name='virtio-disk0'/> @@ -1234,7 +1296,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): </disk> <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> - <source file='{0}{1}my vm_data.qcow2'/> + <source file='{0}{1}my_vm_data.qcow2'/> <backingStore/> <target dev='vdb' bus='virtio'/> <alias name='virtio-disk1'/> @@ -1266,52 +1328,108 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): </video> </devices> </domain> - '''.format(root_dir, os.sep) - domain_mock = self.set_mock_vm('my vm', xml) - domain_mock.OSType = MagicMock(return_value='hvm') + """.format( + root_dir, os.sep + ) + domain_mock = self.set_mock_vm("my_vm", xml) + domain_mock.OSType = MagicMock(return_value="hvm") define_mock = MagicMock(return_value=True) self.mock_conn.defineXML = define_mock # Update vcpus case setvcpus_mock = MagicMock(return_value=0) domain_mock.setVcpusFlags = setvcpus_mock - self.assertEqual({ - 'definition': True, - 'cpu': True, - 'disk': {'attached': [], 'detached': []}, - 'interface': {'attached': [], 'detached': []} - }, virt.update('my vm', cpu=2)) + self.assertEqual( + { + "definition": True, + "cpu": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("my_vm", cpu=2), + ) setxml = ET.fromstring(define_mock.call_args[0][0]) - self.assertEqual(setxml.find('vcpu').text, '2') + self.assertEqual(setxml.find("vcpu").text, "2") self.assertEqual(setvcpus_mock.call_args[0][0], 2) boot = { - 'kernel': '/root/f8-i386-vmlinuz', - 'initrd': '/root/f8-i386-initrd', - 'cmdline': - 'console=ttyS0 ks=http://example.com/f8-i386/os/' + "kernel": "/root/f8-i386-vmlinuz", + "initrd": "/root/f8-i386-initrd", + "cmdline": "console=ttyS0 ks=http://example.com/f8-i386/os/", + } + + boot_uefi = { + "loader": "/usr/share/OVMF/OVMF_CODE.fd", + "nvram": "/usr/share/OVMF/OVMF_VARS.ms.fd", + } + + invalid_boot = { + "loader": "/usr/share/OVMF/OVMF_CODE.fd", + "initrd": "/root/f8-i386-initrd", } # Update with boot parameter case - self.assertEqual({ - 'definition': True, - 'disk': {'attached': [], 'detached': []}, - 'interface': {'attached': [], 'detached': []} - }, virt.update('my vm', boot=boot)) + self.assertEqual( + { + "definition": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("my_vm", boot=boot), + ) + setxml = ET.fromstring(define_mock.call_args[0][0]) + self.assertEqual(setxml.find("os").find("kernel").text, "/root/f8-i386-vmlinuz") + self.assertEqual(setxml.find("os").find("initrd").text, "/root/f8-i386-initrd") + self.assertEqual( + setxml.find("os").find("cmdline").text, + "console=ttyS0 ks=http://example.com/f8-i386/os/", + ) + setxml = ET.fromstring(define_mock.call_args[0][0]) + self.assertEqual(setxml.find("os").find("kernel").text, "/root/f8-i386-vmlinuz") + self.assertEqual(setxml.find("os").find("initrd").text, "/root/f8-i386-initrd") + self.assertEqual( + setxml.find("os").find("cmdline").text, + "console=ttyS0 ks=http://example.com/f8-i386/os/", + ) + + self.assertEqual( + { + "definition": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("my_vm", boot=boot_uefi), + ) + setxml = ET.fromstring(define_mock.call_args[0][0]) + self.assertEqual( + setxml.find("os").find("loader").text, "/usr/share/OVMF/OVMF_CODE.fd" + ) + self.assertEqual(setxml.find("os").find("loader").attrib.get("readonly"), "yes") + self.assertEqual(setxml.find("os").find("loader").attrib["type"], "pflash") + self.assertEqual( + setxml.find("os").find("nvram").attrib["template"], + "/usr/share/OVMF/OVMF_VARS.ms.fd", + ) + + with self.assertRaises(SaltInvocationError): + virt.update("my_vm", boot=invalid_boot) # Update memory case setmem_mock = MagicMock(return_value=0) domain_mock.setMemoryFlags = setmem_mock - self.assertEqual({ - 'definition': True, - 'mem': True, - 'disk': {'attached': [], 'detached': []}, - 'interface': {'attached': [], 'detached': []} - }, virt.update('my vm', mem=2048)) + self.assertEqual( + { + "definition": True, + "mem": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("my_vm", mem=2048), + ) setxml = ET.fromstring(define_mock.call_args[0][0]) - self.assertEqual(setxml.find('memory').text, '2048') - self.assertEqual(setxml.find('memory').get('unit'), 'MiB') + self.assertEqual(setxml.find("memory").text, "2048") + self.assertEqual(setxml.find("memory").get("unit"), "MiB") self.assertEqual(setmem_mock.call_args[0][0], 2048 * 1024) # Update disks case @@ -1321,122 +1439,355 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): domain_mock.detachDevice = devdetach_mock mock_chmod = MagicMock() mock_run = MagicMock() - with patch.dict(os.__dict__, {'chmod': mock_chmod, 'makedirs': MagicMock()}): # pylint: disable=no-member - with patch.dict(virt.__salt__, {'cmd.run': mock_run}): # pylint: disable=no-member - ret = virt.update('my vm', disk_profile='default', disks=[ - {'name': 'cddrive', 'device': 'cdrom', 'source_file': None, 'model': 'ide'}, - {'name': 'added', 'size': 2048}]) + with patch.dict( + os.__dict__, {"chmod": mock_chmod, "makedirs": MagicMock()} + ): # pylint: disable=no-member + with patch.dict( + virt.__salt__, {"cmd.run": mock_run} + ): # pylint: disable=no-member + ret = virt.update( + "my_vm", + disk_profile="default", + disks=[ + { + "name": "cddrive", + "device": "cdrom", + "source_file": None, + "model": "ide", + }, + {"name": "added", "size": 2048}, + ], + ) added_disk_path = os.path.join( - virt.__salt__['config.get']('virt:images'), 'my vm_added.qcow2') # pylint: disable=no-member - self.assertEqual(mock_run.call_args[0][0], - 'qemu-img create -f qcow2 "{0}" 2048M'.format(added_disk_path)) + virt.__salt__["config.get"]("virt:images"), "my_vm_added.qcow2" + ) # pylint: disable=no-member + self.assertEqual( + mock_run.call_args[0][0], + 'qemu-img create -f qcow2 "{0}" 2048M'.format(added_disk_path), + ) self.assertEqual(mock_chmod.call_args[0][0], added_disk_path) self.assertListEqual( - [None, os.path.join(root_dir, 'my vm_added.qcow2')], - [ET.fromstring(disk).find('source').get('file') if str(disk).find('<source') > -1 else None - for disk in ret['disk']['attached']]) + [None, os.path.join(root_dir, "my_vm_added.qcow2")], + [ + ET.fromstring(disk).find("source").get("file") + if str(disk).find("<source") > -1 + else None + for disk in ret["disk"]["attached"] + ], + ) self.assertListEqual( - [os.path.join(root_dir, 'my vm_data.qcow2')], - [ET.fromstring(disk).find('source').get('file') for disk in ret['disk']['detached']]) + [os.path.join(root_dir, "my_vm_data.qcow2")], + [ + ET.fromstring(disk).find("source").get("file") + for disk in ret["disk"]["detached"] + ], + ) self.assertEqual(devattach_mock.call_count, 2) devdetach_mock.assert_called_once() # Update nics case - yaml_config = ''' + yaml_config = """ virt: nic: myprofile: - network: default name: eth0 - ''' + """ mock_config = salt.utils.yaml.safe_load(yaml_config) devattach_mock.reset_mock() devdetach_mock.reset_mock() - with patch.dict(salt.modules.config.__opts__, mock_config): # pylint: disable=no-member - ret = virt.update('my vm', nic_profile='myprofile', - interfaces=[{'name': 'eth0', 'type': 'network', 'source': 'default', - 'mac': '52:54:00:39:02:b1'}, - {'name': 'eth1', 'type': 'network', 'source': 'newnet'}]) - self.assertEqual(['newnet'], - [ET.fromstring(nic).find('source').get('network') for nic in ret['interface']['attached']]) - self.assertEqual(['oldnet'], - [ET.fromstring(nic).find('source').get('network') for nic in ret['interface']['detached']]) + with patch.dict( + salt.modules.config.__opts__, mock_config + ): # pylint: disable=no-member + ret = virt.update( + "my_vm", + nic_profile="myprofile", + interfaces=[ + { + "name": "eth0", + "type": "network", + "source": "default", + "mac": "52:54:00:39:02:b1", + }, + {"name": "eth1", "type": "network", "source": "newnet"}, + ], + ) + self.assertEqual( + ["newnet"], + [ + ET.fromstring(nic).find("source").get("network") + for nic in ret["interface"]["attached"] + ], + ) + self.assertEqual( + ["oldnet"], + [ + ET.fromstring(nic).find("source").get("network") + for nic in ret["interface"]["detached"] + ], + ) devattach_mock.assert_called_once() devdetach_mock.assert_called_once() # Remove nics case devattach_mock.reset_mock() devdetach_mock.reset_mock() - ret = virt.update('my vm', nic_profile=None, interfaces=[]) - self.assertEqual([], ret['interface']['attached']) - self.assertEqual(2, len(ret['interface']['detached'])) + ret = virt.update("my_vm", nic_profile=None, interfaces=[]) + self.assertEqual([], ret["interface"]["attached"]) + self.assertEqual(2, len(ret["interface"]["detached"])) devattach_mock.assert_not_called() devdetach_mock.assert_called() # Remove disks case (yeah, it surely is silly) devattach_mock.reset_mock() devdetach_mock.reset_mock() - ret = virt.update('my vm', disk_profile=None, disks=[]) - self.assertEqual([], ret['disk']['attached']) - self.assertEqual(2, len(ret['disk']['detached'])) + ret = virt.update("my_vm", disk_profile=None, disks=[]) + self.assertEqual([], ret["disk"]["attached"]) + self.assertEqual(2, len(ret["disk"]["detached"])) devattach_mock.assert_not_called() devdetach_mock.assert_called() # Graphics change test case - self.assertEqual({ - 'definition': True, - 'disk': {'attached': [], 'detached': []}, - 'interface': {'attached': [], 'detached': []} - }, virt.update('my vm', graphics={'type': 'vnc'})) + self.assertEqual( + { + "definition": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("my_vm", graphics={"type": "vnc"}), + ) setxml = ET.fromstring(define_mock.call_args[0][0]) - self.assertEqual('vnc', setxml.find('devices/graphics').get('type')) + self.assertEqual("vnc", setxml.find("devices/graphics").get("type")) # Update with no diff case - self.assertEqual({ - 'definition': False, - 'disk': {'attached': [], 'detached': []}, - 'interface': {'attached': [], 'detached': []} - }, virt.update('my vm', cpu=1, mem=1024, - disk_profile='default', disks=[{'name': 'data', 'size': 2048}], - nic_profile='myprofile', - interfaces=[{'name': 'eth0', 'type': 'network', 'source': 'default', - 'mac': '52:54:00:39:02:b1'}, - {'name': 'eth1', 'type': 'network', 'source': 'oldnet', - 'mac': '52:54:00:39:02:b2'}], - graphics={'type': 'spice', - 'listen': {'type': 'address', 'address': '127.0.0.1'}})) + self.assertEqual( + { + "definition": False, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update( + "my_vm", + cpu=1, + mem=1024, + disk_profile="default", + disks=[{"name": "data", "size": 2048}], + nic_profile="myprofile", + interfaces=[ + { + "name": "eth0", + "type": "network", + "source": "default", + "mac": "52:54:00:39:02:b1", + }, + { + "name": "eth1", + "type": "network", + "source": "oldnet", + "mac": "52:54:00:39:02:b2", + }, + ], + graphics={ + "type": "spice", + "listen": {"type": "address", "address": "127.0.0.1"}, + }, + ), + ) # Failed XML description update case - self.mock_conn.defineXML.side_effect = self.mock_libvirt.libvirtError("Test error") + self.mock_conn.defineXML.side_effect = self.mock_libvirt.libvirtError( + "Test error" + ) setmem_mock.reset_mock() with self.assertRaises(self.mock_libvirt.libvirtError): - virt.update('my vm', mem=2048) + virt.update("my_vm", mem=2048) # Failed single update failure case self.mock_conn.defineXML = MagicMock(return_value=True) - setmem_mock.side_effect = self.mock_libvirt.libvirtError("Failed to live change memory") - self.assertEqual({ - 'definition': True, - 'errors': ['Failed to live change memory'], - 'disk': {'attached': [], 'detached': []}, - 'interface': {'attached': [], 'detached': []} - }, virt.update('my vm', mem=2048)) + setmem_mock.side_effect = self.mock_libvirt.libvirtError( + "Failed to live change memory" + ) + self.assertEqual( + { + "definition": True, + "errors": ["Failed to live change memory"], + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("my_vm", mem=2048), + ) # Failed multiple updates failure case - self.assertEqual({ - 'definition': True, - 'errors': ['Failed to live change memory'], - 'cpu': True, - 'disk': {'attached': [], 'detached': []}, - 'interface': {'attached': [], 'detached': []} - }, virt.update('my vm', cpu=4, mem=2048)) + self.assertEqual( + { + "definition": True, + "errors": ["Failed to live change memory"], + "cpu": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("my_vm", cpu=4, mem=2048), + ) + + def test_update_existing_boot_params(self): + """ + Test virt.update() with existing boot parameters. + """ + root_dir = os.path.join(salt.syspaths.ROOT_DIR, "srv", "salt-images") + xml_boot = """ + <domain type='kvm' id='8'> + <name>vm_with_boot_param</name> + <memory unit='KiB'>1048576</memory> + <currentMemory unit='KiB'>1048576</currentMemory> + <vcpu placement='auto'>1</vcpu> + <os> + <type arch='x86_64' machine='pc-i440fx-2.6'>hvm</type> + <kernel>/boot/oldkernel</kernel> + <initrd>/boot/initrdold.img</initrd> + <cmdline>console=ttyS0 ks=http://example.com/old/os/</cmdline> + <loader>/usr/share/old/OVMF_CODE.fd</loader> + <nvram>/usr/share/old/OVMF_VARS.ms.fd</nvram> + </os> + <devices> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='{0}{1}vm_with_boot_param_system.qcow2'/> + <backingStore/> + <target dev='vda' bus='virtio'/> + <alias name='virtio-disk0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='{0}{1}vm_with_boot_param_data.qcow2'/> + <backingStore/> + <target dev='vdb' bus='virtio'/> + <alias name='virtio-disk1'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x1'/> + </disk> + <interface type='network'> + <mac address='52:54:00:39:02:b1'/> + <source network='default' bridge='virbr0'/> + <target dev='vnet0'/> + <model type='virtio'/> + <alias name='net0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </interface> + <interface type='network'> + <mac address='52:54:00:39:02:b2'/> + <source network='oldnet' bridge='virbr1'/> + <target dev='vnet1'/> + <model type='virtio'/> + <alias name='net1'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/> + </interface> + <graphics type='spice' port='5900' autoport='yes' listen='127.0.0.1'> + <listen type='address' address='127.0.0.1'/> + </graphics> + <video> + <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/> + <alias name='video0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </video> + </devices> + </domain> + """.format( + root_dir, os.sep + ) + domain_mock_boot = self.set_mock_vm("vm_with_boot_param", xml_boot) + domain_mock_boot.OSType = MagicMock(return_value="hvm") + define_mock_boot = MagicMock(return_value=True) + self.mock_conn.defineXML = define_mock_boot + boot_new = { + "kernel": "/root/new-vmlinuz", + "initrd": "/root/new-initrd", + "cmdline": "console=ttyS0 ks=http://example.com/new/os/", + } + + uefi_boot_new = { + "loader": "/usr/share/new/OVMF_CODE.fd", + "nvram": "/usr/share/new/OVMF_VARS.ms.fd", + } + + self.assertEqual( + { + "definition": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("vm_with_boot_param", boot=boot_new), + ) + setxml_boot = ET.fromstring(define_mock_boot.call_args[0][0]) + self.assertEqual( + setxml_boot.find("os").find("kernel").text, "/root/new-vmlinuz" + ) + self.assertEqual(setxml_boot.find("os").find("initrd").text, "/root/new-initrd") + self.assertEqual( + setxml_boot.find("os").find("cmdline").text, + "console=ttyS0 ks=http://example.com/new/os/", + ) + + self.assertEqual( + { + "definition": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("vm_with_boot_param", boot=uefi_boot_new), + ) + + setxml = ET.fromstring(define_mock_boot.call_args[0][0]) + self.assertEqual( + setxml.find("os").find("loader").text, "/usr/share/new/OVMF_CODE.fd" + ) + self.assertEqual(setxml.find("os").find("loader").attrib.get("readonly"), "yes") + self.assertEqual(setxml.find("os").find("loader").attrib["type"], "pflash") + self.assertEqual( + setxml.find("os").find("nvram").attrib["template"], + "/usr/share/new/OVMF_VARS.ms.fd", + ) + + kernel_none = { + "kernel": None, + "initrd": None, + "cmdline": None, + } + + uefi_none = {"loader": None, "nvram": None} + + self.assertEqual( + { + "definition": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("vm_with_boot_param", boot=kernel_none), + ) + + setxml = ET.fromstring(define_mock_boot.call_args[0][0]) + self.assertEqual(setxml.find("os").find("kernel"), None) + self.assertEqual(setxml.find("os").find("initrd"), None) + self.assertEqual(setxml.find("os").find("cmdline"), None) + + self.assertEqual( + { + "definition": True, + "disk": {"attached": [], "detached": []}, + "interface": {"attached": [], "detached": []}, + }, + virt.update("vm_with_boot_param", boot=uefi_none), + ) + + setxml = ET.fromstring(define_mock_boot.call_args[0][0]) + self.assertEqual(setxml.find("os").find("loader"), None) + self.assertEqual(setxml.find("os").find("nvram"), None) def test_mixed_dict_and_list_as_profile_objects(self): - ''' + """ Test virt._nic_profile with mixed dictionaries and lists as input. - ''' - yaml_config = ''' + """ + yaml_config = """ virt: nic: new-listonly-profile: @@ -1458,30 +1809,36 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): eth1: bridge: br1 model: virtio - ''' + """ mock_config = salt.utils.yaml.safe_load(yaml_config) - with patch.dict(salt.modules.config.__opts__, mock_config): # pylint: disable=no-member + with patch.dict( + salt.modules.config.__opts__, mock_config + ): # pylint: disable=no-member - for name in six.iterkeys(mock_config['virt']['nic']): - profile = salt.modules.virt._nic_profile(name, 'kvm') + for name in six.iterkeys(mock_config["virt"]["nic"]): + profile = salt.modules.virt._nic_profile(name, "kvm") self.assertEqual(len(profile), 2) interface_attrs = profile[0] - self.assertIn('source', interface_attrs) - self.assertIn('type', interface_attrs) - self.assertIn('name', interface_attrs) - self.assertIn('model', interface_attrs) - self.assertEqual(interface_attrs['model'], 'virtio') - self.assertIn('mac', interface_attrs) + self.assertIn("source", interface_attrs) + self.assertIn("type", interface_attrs) + self.assertIn("name", interface_attrs) + self.assertIn("model", interface_attrs) + self.assertEqual(interface_attrs["model"], "virtio") + self.assertIn("mac", interface_attrs) self.assertTrue( - re.match('^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$', - interface_attrs['mac'], re.I)) + re.match( + "^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$", + interface_attrs["mac"], + re.I, + ) + ) def test_get_xml(self): - ''' + """ Test virt.get_xml() - ''' - xml = '''<domain type='kvm' id='7'> + """ + xml = """<domain type='kvm' id='7'> <name>test-vm</name> <devices> <graphics type='vnc' port='5900' autoport='yes' listen='0.0.0.0'> @@ -1489,16 +1846,16 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): </graphics> </devices> </domain> - ''' + """ domain = self.set_mock_vm("test-vm", xml) - self.assertEqual(xml, virt.get_xml('test-vm')) + self.assertEqual(xml, virt.get_xml("test-vm")) self.assertEqual(xml, virt.get_xml(domain)) def test_parse_qemu_img_info(self): - ''' + """ Make sure that qemu-img info output is properly parsed - ''' - qemu_infos = '''[{ + """ + qemu_infos = """[{ "snapshots": [ { "vm-clock-nsec": 0, @@ -1572,57 +1929,61 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): } }, "dirty-flag": false - }]''' + }]""" self.assertEqual( { - 'file': '/disks/test.qcow2', - 'file format': 'qcow2', - 'backing file': { - 'file': '/disks/mybacking.qcow2', - 'file format': 'qcow2', - 'disk size': 393744384, - 'virtual size': 25769803776, - 'cluster size': 65536, - 'backing file': { - 'file': '/disks/root.qcow2', - 'file format': 'qcow2', - 'disk size': 196872192, - 'virtual size': 25769803776, - 'cluster size': 65536, - } + "file": "/disks/test.qcow2", + "file format": "qcow2", + "backing file": { + "file": "/disks/mybacking.qcow2", + "file format": "qcow2", + "disk size": 393744384, + "virtual size": 25769803776, + "cluster size": 65536, + "backing file": { + "file": "/disks/root.qcow2", + "file format": "qcow2", + "disk size": 196872192, + "virtual size": 25769803776, + "cluster size": 65536, + }, }, - 'disk size': 217088, - 'virtual size': 25769803776, - 'cluster size': 65536, - 'snapshots': [ + "disk size": 217088, + "virtual size": 25769803776, + "cluster size": 65536, + "snapshots": [ { - 'id': '1', - 'tag': 'first-snap', - 'vmsize': 1234, - 'date': datetime.datetime.fromtimestamp( - float("{}.{}".format(1528877587, 380589000))).isoformat(), - 'vmclock': '00:00:00' + "id": "1", + "tag": "first-snap", + "vmsize": 1234, + "date": datetime.datetime.fromtimestamp( + float("{}.{}".format(1528877587, 380589000)) + ).isoformat(), + "vmclock": "00:00:00", }, { - 'id': '2', - 'tag': 'second snap', - 'vmsize': 4567, - 'date': datetime.datetime.fromtimestamp( - float("{}.{}".format(1528877592, 933509000))).isoformat(), - 'vmclock': '00:00:00' - } + "id": "2", + "tag": "second snap", + "vmsize": 4567, + "date": datetime.datetime.fromtimestamp( + float("{}.{}".format(1528877592, 933509000)) + ).isoformat(), + "vmclock": "00:00:00", + }, ], - }, virt._parse_qemu_img_info(qemu_infos)) + }, + virt._parse_qemu_img_info(qemu_infos), + ) - @patch('salt.modules.virt.stop', return_value=True) - @patch('salt.modules.virt.undefine') - @patch('os.remove') + @patch("salt.modules.virt.stop", return_value=True) + @patch("salt.modules.virt.undefine") + @patch("os.remove") def test_purge_default(self, mock_remove, mock_undefine, mock_stop): - ''' + """ Test virt.purge() with default parameters - ''' - xml = '''<domain type='kvm' id='7'> + """ + xml = """<domain type='kvm' id='7'> <name>test-vm</name> <devices> <disk type='file' device='disk'> @@ -1638,10 +1999,10 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): </disk> </devices> </domain> - ''' + """ self.set_mock_vm("test-vm", xml) - qemu_infos = '''[{ + qemu_infos = """[{ "virtual-size": 25769803776, "filename": "/disks/test.qcow2", "cluster-size": 65536, @@ -1657,23 +2018,25 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): } }, "dirty-flag": false - }]''' + }]""" - self.mock_popen.communicate.return_value = [qemu_infos] # pylint: disable=no-member + self.mock_popen.communicate.return_value = [ + qemu_infos + ] # pylint: disable=no-member - res = virt.purge('test-vm') + res = virt.purge("test-vm") self.assertTrue(res) - mock_remove.assert_any_call('/disks/test.qcow2') - mock_remove.assert_any_call('/disks/test-cdrom.iso') + mock_remove.assert_called_once() + mock_remove.assert_any_call("/disks/test.qcow2") - @patch('salt.modules.virt.stop', return_value=True) - @patch('salt.modules.virt.undefine') - @patch('os.remove') - def test_purge_noremovable(self, mock_remove, mock_undefine, mock_stop): - ''' - Test virt.purge(removables=False) - ''' - xml = '''<domain type='kvm' id='7'> + @patch("salt.modules.virt.stop", return_value=True) + @patch("salt.modules.virt.undefine") + @patch("os.remove") + def test_purge_removable(self, mock_remove, mock_undefine, mock_stop): + """ + Test virt.purge(removables=True) + """ + xml = """<domain type="kvm" id="7"> <name>test-vm</name> <devices> <disk type='file' device='disk'> @@ -1695,10 +2058,10 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): </disk> </devices> </domain> - ''' + """ self.set_mock_vm("test-vm", xml) - qemu_infos = '''[{ + qemu_infos = """[{ "virtual-size": 25769803776, "filename": "/disks/test.qcow2", "cluster-size": 65536, @@ -1714,20 +2077,22 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): } }, "dirty-flag": false - }]''' + }]""" - self.mock_popen.communicate.return_value = [qemu_infos] # pylint: disable=no-member + self.mock_popen.communicate.return_value = [ + qemu_infos + ] # pylint: disable=no-member - res = virt.purge('test-vm', removables=False) + res = virt.purge("test-vm", removables=True) self.assertTrue(res) - mock_remove.assert_called_once() - mock_remove.assert_any_call('/disks/test.qcow2') + mock_remove.assert_any_call("/disks/test.qcow2") + mock_remove.assert_any_call("/disks/test-cdrom.iso") def test_capabilities(self): - ''' + """ Test the virt.capabilities parsing - ''' - xml = ''' + """ + xml = """ <capabilities> <host> <uuid>44454c4c-3400-105a-8033-b3c04f4b344a</uuid> @@ -1855,198 +2220,258 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): </guest> </capabilities> - ''' + """ self.mock_conn.getCapabilities.return_value = xml # pylint: disable=no-member caps = virt.capabilities() expected = { - 'host': { - 'uuid': '44454c4c-3400-105a-8033-b3c04f4b344a', - 'cpu': { - 'arch': 'x86_64', - 'model': 'Nehalem', - 'vendor': 'Intel', - 'microcode': '25', - 'sockets': 1, - 'cores': 4, - 'threads': 2, - 'features': ['vme', 'ds', 'acpi'], - 'pages': [{'size': '4 KiB'}, {'size': '2048 KiB'}] + "host": { + "uuid": "44454c4c-3400-105a-8033-b3c04f4b344a", + "cpu": { + "arch": "x86_64", + "model": "Nehalem", + "vendor": "Intel", + "microcode": "25", + "sockets": 1, + "cores": 4, + "threads": 2, + "features": ["vme", "ds", "acpi"], + "pages": [{"size": "4 KiB"}, {"size": "2048 KiB"}], }, - 'power_management': ['suspend_mem', 'suspend_disk', 'suspend_hybrid'], - 'migration': { - 'live': True, - 'transports': ['tcp', 'rdma'] - }, - 'topology': { - 'cells': [ + "power_management": ["suspend_mem", "suspend_disk", "suspend_hybrid"], + "migration": {"live": True, "transports": ["tcp", "rdma"]}, + "topology": { + "cells": [ { - 'id': 0, - 'memory': '12367120 KiB', - 'pages': [ - {'size': '4 KiB', 'available': 3091780}, - {'size': '2048 KiB', 'available': 0} + "id": 0, + "memory": "12367120 KiB", + "pages": [ + {"size": "4 KiB", "available": 3091780}, + {"size": "2048 KiB", "available": 0}, + ], + "distances": {0: 10}, + "cpus": [ + { + "id": 0, + "socket_id": 0, + "core_id": 0, + "siblings": "0,4", + }, + { + "id": 1, + "socket_id": 0, + "core_id": 1, + "siblings": "1,5", + }, + { + "id": 2, + "socket_id": 0, + "core_id": 2, + "siblings": "2,6", + }, + { + "id": 3, + "socket_id": 0, + "core_id": 3, + "siblings": "3,7", + }, + { + "id": 4, + "socket_id": 0, + "core_id": 0, + "siblings": "0,4", + }, + { + "id": 5, + "socket_id": 0, + "core_id": 1, + "siblings": "1,5", + }, + { + "id": 6, + "socket_id": 0, + "core_id": 2, + "siblings": "2,6", + }, + { + "id": 7, + "socket_id": 0, + "core_id": 3, + "siblings": "3,7", + }, ], - 'distances': { - 0: 10, - }, - 'cpus': [ - {'id': 0, 'socket_id': 0, 'core_id': 0, 'siblings': '0,4'}, - {'id': 1, 'socket_id': 0, 'core_id': 1, 'siblings': '1,5'}, - {'id': 2, 'socket_id': 0, 'core_id': 2, 'siblings': '2,6'}, - {'id': 3, 'socket_id': 0, 'core_id': 3, 'siblings': '3,7'}, - {'id': 4, 'socket_id': 0, 'core_id': 0, 'siblings': '0,4'}, - {'id': 5, 'socket_id': 0, 'core_id': 1, 'siblings': '1,5'}, - {'id': 6, 'socket_id': 0, 'core_id': 2, 'siblings': '2,6'}, - {'id': 7, 'socket_id': 0, 'core_id': 3, 'siblings': '3,7'} - ] } ] }, - 'cache': { - 'banks': [ - {'id': 0, 'level': 3, 'type': 'both', 'size': '8 MiB', 'cpus': '0-7'} + "cache": { + "banks": [ + { + "id": 0, + "level": 3, + "type": "both", + "size": "8 MiB", + "cpus": "0-7", + } ] }, - 'security': [ - {'model': 'apparmor', 'doi': '0', 'baselabels': []}, - {'model': 'dac', 'doi': '0', 'baselabels': [ - {'type': 'kvm', 'label': '+487:+486'}, - {'type': 'qemu', 'label': '+487:+486'} - ]} - ] + "security": [ + {"model": "apparmor", "doi": "0", "baselabels": []}, + { + "model": "dac", + "doi": "0", + "baselabels": [ + {"type": "kvm", "label": "+487:+486"}, + {"type": "qemu", "label": "+487:+486"}, + ], + }, + ], }, - 'guests': [ + "guests": [ { - 'os_type': 'hvm', - 'arch': { - 'name': 'i686', - 'wordsize': 32, - 'emulator': '/usr/bin/qemu-system-i386', - 'machines': { - 'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']}, - 'pc-0.12': {'maxcpus': 255, 'alternate_names': []} - }, - 'domains': { - 'qemu': { - 'emulator': None, - 'machines': {} + "os_type": "hvm", + "arch": { + "name": "i686", + "wordsize": 32, + "emulator": "/usr/bin/qemu-system-i386", + "machines": { + "pc-i440fx-2.6": { + "maxcpus": 255, + "alternate_names": ["pc"], }, - 'kvm': { - 'emulator': '/usr/bin/qemu-kvm', - 'machines': { - 'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']}, - 'pc-0.12': {'maxcpus': 255, 'alternate_names': []} - } - } - } + "pc-0.12": {"maxcpus": 255, "alternate_names": []}, + }, + "domains": { + "qemu": {"emulator": None, "machines": {}}, + "kvm": { + "emulator": "/usr/bin/qemu-kvm", + "machines": { + "pc-i440fx-2.6": { + "maxcpus": 255, + "alternate_names": ["pc"], + }, + "pc-0.12": {"maxcpus": 255, "alternate_names": []}, + }, + }, + }, + }, + "features": { + "cpuselection": {"default": True, "toggle": False}, + "deviceboot": {"default": True, "toggle": False}, + "disksnapshot": {"default": True, "toggle": False}, + "acpi": {"default": True, "toggle": True}, + "apic": {"default": True, "toggle": False}, + "pae": {"default": True, "toggle": False}, + "nonpae": {"default": True, "toggle": False}, }, - 'features': { - 'cpuselection': {'default': True, 'toggle': False}, - 'deviceboot': {'default': True, 'toggle': False}, - 'disksnapshot': {'default': True, 'toggle': False}, - 'acpi': {'default': True, 'toggle': True}, - 'apic': {'default': True, 'toggle': False}, - 'pae': {'default': True, 'toggle': False}, - 'nonpae': {'default': True, 'toggle': False} - } }, { - 'os_type': 'hvm', - 'arch': { - 'name': 'x86_64', - 'wordsize': 64, - 'emulator': '/usr/bin/qemu-system-x86_64', - 'machines': { - 'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']}, - 'pc-0.12': {'maxcpus': 255, 'alternate_names': []} - }, - 'domains': { - 'qemu': { - 'emulator': None, - 'machines': {} + "os_type": "hvm", + "arch": { + "name": "x86_64", + "wordsize": 64, + "emulator": "/usr/bin/qemu-system-x86_64", + "machines": { + "pc-i440fx-2.6": { + "maxcpus": 255, + "alternate_names": ["pc"], }, - 'kvm': { - 'emulator': '/usr/bin/qemu-kvm', - 'machines': { - 'pc-i440fx-2.6': {'maxcpus': 255, 'alternate_names': ['pc']}, - 'pc-0.12': {'maxcpus': 255, 'alternate_names': []} - } - } - } + "pc-0.12": {"maxcpus": 255, "alternate_names": []}, + }, + "domains": { + "qemu": {"emulator": None, "machines": {}}, + "kvm": { + "emulator": "/usr/bin/qemu-kvm", + "machines": { + "pc-i440fx-2.6": { + "maxcpus": 255, + "alternate_names": ["pc"], + }, + "pc-0.12": {"maxcpus": 255, "alternate_names": []}, + }, + }, + }, + }, + "features": { + "cpuselection": {"default": True, "toggle": False}, + "deviceboot": {"default": True, "toggle": False}, + "disksnapshot": {"default": True, "toggle": False}, + "acpi": {"default": True, "toggle": True}, + "apic": {"default": True, "toggle": False}, }, - 'features': { - 'cpuselection': {'default': True, 'toggle': False}, - 'deviceboot': {'default': True, 'toggle': False}, - 'disksnapshot': {'default': True, 'toggle': False}, - 'acpi': {'default': True, 'toggle': True}, - 'apic': {'default': True, 'toggle': False} - } }, { - 'os_type': 'xen', - 'arch': { - 'name': 'x86_64', - 'wordsize': 64, - 'emulator': '/usr/bin/qemu-system-x86_64', - 'machines': { - 'xenpv': {'alternate_names': []} - }, - 'domains': { - 'xen': { - 'emulator': None, - 'machines': {} - } - } - } - } - ] + "os_type": "xen", + "arch": { + "name": "x86_64", + "wordsize": 64, + "emulator": "/usr/bin/qemu-system-x86_64", + "machines": {"xenpv": {"alternate_names": []}}, + "domains": {"xen": {"emulator": None, "machines": {}}}, + }, + }, + ], } self.assertEqual(expected, caps) def test_network(self): - ''' + """ Test virt._get_net_xml() - ''' - xml_data = virt._gen_net_xml('network', 'main', 'bridge', 'openvswitch') + """ + xml_data = virt._gen_net_xml("network", "main", "bridge", "openvswitch") root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'network') - self.assertEqual(root.find('bridge').attrib['name'], 'main') - self.assertEqual(root.find('forward').attrib['mode'], 'bridge') - self.assertEqual(root.find('virtualport').attrib['type'], 'openvswitch') + self.assertEqual(root.find("name").text, "network") + self.assertEqual(root.find("bridge").attrib["name"], "main") + self.assertEqual(root.find("forward").attrib["mode"], "bridge") + self.assertEqual(root.find("virtualport").attrib["type"], "openvswitch") def test_network_nat(self): - ''' + """ Test virt._get_net_xml() in a nat setup - ''' - xml_data = virt._gen_net_xml('network', 'main', 'nat', None, ip_configs=[ - { - 'cidr': '192.168.2.0/24', - 'dhcp_ranges': [ - {'start': '192.168.2.10', 'end': '192.168.2.25'}, - {'start': '192.168.2.110', 'end': '192.168.2.125'}, - ] - } - ]) + """ + xml_data = virt._gen_net_xml( + "network", + "main", + "nat", + None, + ip_configs=[ + { + "cidr": "192.168.2.0/24", + "dhcp_ranges": [ + {"start": "192.168.2.10", "end": "192.168.2.25"}, + {"start": "192.168.2.110", "end": "192.168.2.125"}, + ], + } + ], + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'network') - self.assertEqual(root.find('bridge').attrib['name'], 'main') - self.assertEqual(root.find('forward').attrib['mode'], 'nat') - self.assertEqual(root.find("./ip[@address='192.168.2.0']").attrib['prefix'], '24') - self.assertEqual(root.find("./ip[@address='192.168.2.0']").attrib['family'], 'ipv4') + self.assertEqual(root.find("name").text, "network") + self.assertEqual(root.find("bridge").attrib["name"], "main") + self.assertEqual(root.find("forward").attrib["mode"], "nat") self.assertEqual( - root.find("./ip[@address='192.168.2.0']/dhcp/range[@start='192.168.2.10']").attrib['end'], - '192.168.2.25') + root.find("./ip[@address='192.168.2.0']").attrib["prefix"], "24" + ) self.assertEqual( - root.find("./ip[@address='192.168.2.0']/dhcp/range[@start='192.168.2.110']").attrib['end'], - '192.168.2.125') + root.find("./ip[@address='192.168.2.0']").attrib["family"], "ipv4" + ) + self.assertEqual( + root.find( + "./ip[@address='192.168.2.0']/dhcp/range[@start='192.168.2.10']" + ).attrib["end"], + "192.168.2.25", + ) + self.assertEqual( + root.find( + "./ip[@address='192.168.2.0']/dhcp/range[@start='192.168.2.110']" + ).attrib["end"], + "192.168.2.125", + ) def test_domain_capabilities(self): - ''' + """ Test the virt.domain_capabilities parsing - ''' - xml = ''' + """ + xml = """ <domainCapabilities> <path>/usr/bin/qemu-system-aarch64</path> <domain>kvm</domain> @@ -2143,161 +2568,152 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <vmcoreinfo supported='yes'/> </features> </domainCapabilities> - ''' + """ - self.mock_conn.getDomainCapabilities.return_value = xml # pylint: disable=no-member + self.mock_conn.getDomainCapabilities.return_value = ( + xml # pylint: disable=no-member + ) caps = virt.domain_capabilities() expected = { - 'emulator': '/usr/bin/qemu-system-aarch64', - 'domain': 'kvm', - 'machine': 'virt-2.12', - 'arch': 'aarch64', - 'max_vcpus': 255, - 'iothreads': True, - 'os': { - 'loader': { - 'type': ['rom', 'pflash'], - 'readonly': ['yes', 'no'], - 'values': [ - '/usr/share/AAVMF/AAVMF_CODE.fd', - '/usr/share/AAVMF/AAVMF32_CODE.fd', - '/usr/share/OVMF/OVMF_CODE.fd' - ] + "emulator": "/usr/bin/qemu-system-aarch64", + "domain": "kvm", + "machine": "virt-2.12", + "arch": "aarch64", + "max_vcpus": 255, + "iothreads": True, + "os": { + "loader": { + "type": ["rom", "pflash"], + "readonly": ["yes", "no"], + "values": [ + "/usr/share/AAVMF/AAVMF_CODE.fd", + "/usr/share/AAVMF/AAVMF32_CODE.fd", + "/usr/share/OVMF/OVMF_CODE.fd", + ], } }, - 'cpu': { - 'host-passthrough': True, - 'host-model': { - 'model': { - 'name': 'sample-cpu', - 'fallback': 'forbid' - }, - 'vendor': 'ACME', - 'features': { - 'vme': 'require', - 'ss': 'require' - } + "cpu": { + "host-passthrough": True, + "host-model": { + "model": {"name": "sample-cpu", "fallback": "forbid"}, + "vendor": "ACME", + "features": {"vme": "require", "ss": "require"}, + }, + "custom": { + "models": {"pxa262": "unknown", "pxa270-a0": "yes", "arm1136": "no"} }, - 'custom': { - 'models': { - 'pxa262': 'unknown', - 'pxa270-a0': 'yes', - 'arm1136': 'no' - } - } }, - 'devices': { - 'disk': { - 'diskDevice': ['disk', 'cdrom', 'floppy', 'lun'], - 'bus': ['fdc', 'scsi', 'virtio', 'usb', 'sata'], + "devices": { + "disk": { + "diskDevice": ["disk", "cdrom", "floppy", "lun"], + "bus": ["fdc", "scsi", "virtio", "usb", "sata"], }, - 'graphics': { - 'type': ['sdl', 'vnc'] + "graphics": {"type": ["sdl", "vnc"]}, + "video": {"modelType": ["vga", "virtio"]}, + "hostdev": { + "mode": ["subsystem"], + "startupPolicy": ["default", "mandatory", "requisite", "optional"], + "subsysType": ["usb", "pci", "scsi"], + "capsType": [], + "pciBackend": ["default", "kvm", "vfio"], }, - 'video': { - 'modelType': ['vga', 'virtio'] - }, - 'hostdev': { - 'mode': ['subsystem'], - 'startupPolicy': ['default', 'mandatory', 'requisite', 'optional'], - 'subsysType': ['usb', 'pci', 'scsi'], - 'capsType': [], - 'pciBackend': ['default', 'kvm', 'vfio'] - } }, - 'features': { - 'gic': { - 'version': ['3'] - }, - 'vmcoreinfo': {} - } + "features": {"gic": {"version": ["3"]}, "vmcoreinfo": {}}, } self.assertEqual(expected, caps) def test_network_tag(self): - ''' + """ Test virt._get_net_xml() with VLAN tag - ''' - xml_data = virt._gen_net_xml('network', 'main', 'bridge', 'openvswitch', 1001) + """ + xml_data = virt._gen_net_xml("network", "main", "bridge", "openvswitch", 1001) root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'network') - self.assertEqual(root.find('bridge').attrib['name'], 'main') - self.assertEqual(root.find('forward').attrib['mode'], 'bridge') - self.assertEqual(root.find('virtualport').attrib['type'], 'openvswitch') - self.assertEqual(root.find('vlan/tag').attrib['id'], '1001') + self.assertEqual(root.find("name").text, "network") + self.assertEqual(root.find("bridge").attrib["name"], "main") + self.assertEqual(root.find("forward").attrib["mode"], "bridge") + self.assertEqual(root.find("virtualport").attrib["type"], "openvswitch") + self.assertEqual(root.find("vlan/tag").attrib["id"], "1001") def test_list_networks(self): - ''' + """ Test virt.list_networks() - ''' - names = ['net1', 'default', 'net2'] + """ + names = ["net1", "default", "net2"] net_mocks = [MagicMock(), MagicMock(), MagicMock()] for i, value in enumerate(names): net_mocks[i].name.return_value = value - self.mock_conn.listAllNetworks.return_value = net_mocks # pylint: disable=no-member + self.mock_conn.listAllNetworks.return_value = ( + net_mocks # pylint: disable=no-member + ) actual = virt.list_networks() self.assertEqual(names, actual) def test_network_info(self): - ''' + """ Test virt.network_info() - ''' + """ self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV4 = 0 self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV6 = 1 net_mock = MagicMock() # pylint: disable=no-member - net_mock.name.return_value = 'foo' - net_mock.UUIDString.return_value = 'some-uuid' - net_mock.bridgeName.return_value = 'br0' + net_mock.name.return_value = "foo" + net_mock.UUIDString.return_value = "some-uuid" + net_mock.bridgeName.return_value = "br0" net_mock.autostart.return_value = True net_mock.isActive.return_value = False net_mock.isPersistent.return_value = True net_mock.DHCPLeases.return_value = [ { - 'iface': 'virbr0', - 'expirytime': 1527757552, - 'type': 0, - 'mac': '52:54:00:01:71:bd', - 'ipaddr': '192.168.122.45', - 'prefix': 24, - 'hostname': 'py3-test', - 'clientid': '01:52:54:00:01:71:bd', - 'iaid': None + "iface": "virbr0", + "expirytime": 1527757552, + "type": 0, + "mac": "52:54:00:01:71:bd", + "ipaddr": "192.168.122.45", + "prefix": 24, + "hostname": "py3-test", + "clientid": "01:52:54:00:01:71:bd", + "iaid": None, } ] self.mock_conn.listAllNetworks.return_value = [net_mock] # pylint: enable=no-member - net = virt.network_info('foo') - self.assertEqual({'foo': { - 'uuid': 'some-uuid', - 'bridge': 'br0', - 'autostart': True, - 'active': False, - 'persistent': True, - 'leases': [ - { - 'iface': 'virbr0', - 'expirytime': 1527757552, - 'type': 'ipv4', - 'mac': '52:54:00:01:71:bd', - 'ipaddr': '192.168.122.45', - 'prefix': 24, - 'hostname': 'py3-test', - 'clientid': '01:52:54:00:01:71:bd', - 'iaid': None + net = virt.network_info("foo") + self.assertEqual( + { + "foo": { + "uuid": "some-uuid", + "bridge": "br0", + "autostart": True, + "active": False, + "persistent": True, + "leases": [ + { + "iface": "virbr0", + "expirytime": 1527757552, + "type": "ipv4", + "mac": "52:54:00:01:71:bd", + "ipaddr": "192.168.122.45", + "prefix": 24, + "hostname": "py3-test", + "clientid": "01:52:54:00:01:71:bd", + "iaid": None, + } + ], } - ]}}, net) + }, + net, + ) def test_network_info_all(self): - ''' + """ Test virt.network_info() - ''' + """ self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV4 = 0 self.mock_libvirt.VIR_IP_ADDR_TYPE_IPV6 = 1 @@ -2306,9 +2722,9 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): for i in range(2): net_mock = MagicMock() - net_mock.name.return_value = 'net{0}'.format(i) - net_mock.UUIDString.return_value = 'some-uuid' - net_mock.bridgeName.return_value = 'br{0}'.format(i) + net_mock.name.return_value = "net{0}".format(i) + net_mock.UUIDString.return_value = "some-uuid" + net_mock.bridgeName.return_value = "br{0}".format(i) net_mock.autostart.return_value = True net_mock.isActive.return_value = False net_mock.isPersistent.return_value = True @@ -2318,194 +2734,223 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): # pylint: enable=no-member net = virt.network_info() - self.assertEqual({ - 'net0': + self.assertEqual( { - 'uuid': 'some-uuid', - 'bridge': 'br0', - 'autostart': True, - 'active': False, - 'persistent': True, - 'leases': [] - }, 'net1': - { - 'uuid': 'some-uuid', - 'bridge': 'br1', - 'autostart': True, - 'active': False, - 'persistent': True, - 'leases': [] - } - }, net) + "net0": { + "uuid": "some-uuid", + "bridge": "br0", + "autostart": True, + "active": False, + "persistent": True, + "leases": [], + }, + "net1": { + "uuid": "some-uuid", + "bridge": "br1", + "autostart": True, + "active": False, + "persistent": True, + "leases": [], + }, + }, + net, + ) def test_network_info_notfound(self): - ''' + """ Test virt.network_info() when the network can't be found - ''' + """ # pylint: disable=no-member self.mock_conn.listAllNetworks.return_value = [] # pylint: enable=no-member - net = virt.network_info('foo') + net = virt.network_info("foo") self.assertEqual({}, net) def test_network_get_xml(self): - ''' + """ Test virt.network_get_xml - ''' + """ network_mock = MagicMock() - network_mock.XMLDesc.return_value = '<net>Raw XML</net>' + network_mock.XMLDesc.return_value = "<net>Raw XML</net>" self.mock_conn.networkLookupByName.return_value = network_mock - self.assertEqual('<net>Raw XML</net>', virt.network_get_xml('default')) + self.assertEqual("<net>Raw XML</net>", virt.network_get_xml("default")) def test_pool(self): - ''' + """ Test virt._gen_pool_xml() - ''' - xml_data = virt._gen_pool_xml('pool', 'logical', '/dev/base') + """ + xml_data = virt._gen_pool_xml("pool", "logical", "/dev/base") root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'pool') - self.assertEqual(root.attrib['type'], 'logical') - self.assertEqual(root.find('target/path').text, '/dev/base') + self.assertEqual(root.find("name").text, "pool") + self.assertEqual(root.attrib["type"], "logical") + self.assertEqual(root.find("target/path").text, "/dev/base") def test_pool_with_source(self): - ''' + """ Test virt._gen_pool_xml() with a source device - ''' - xml_data = virt._gen_pool_xml('pool', 'logical', '/dev/base', source_devices=[{'path': '/dev/sda'}]) + """ + xml_data = virt._gen_pool_xml( + "pool", "logical", "/dev/base", source_devices=[{"path": "/dev/sda"}] + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'pool') - self.assertEqual(root.attrib['type'], 'logical') - self.assertEqual(root.find('target/path').text, '/dev/base') - self.assertEqual(root.find('source/device').attrib['path'], '/dev/sda') + self.assertEqual(root.find("name").text, "pool") + self.assertEqual(root.attrib["type"], "logical") + self.assertEqual(root.find("target/path").text, "/dev/base") + self.assertEqual(root.find("source/device").attrib["path"], "/dev/sda") def test_pool_with_scsi(self): - ''' + """ Test virt._gen_pool_xml() with a SCSI source - ''' - xml_data = virt._gen_pool_xml('pool', - 'scsi', - '/dev/disk/by-path', - source_devices=[{'path': '/dev/sda'}], - source_adapter={ - 'type': 'scsi_host', - 'parent_address': { - 'unique_id': 5, - 'address': { - 'domain': '0x0000', - 'bus': '0x00', - 'slot': '0x1f', - 'function': '0x2' - } - } - }, - source_name='srcname') + """ + xml_data = virt._gen_pool_xml( + "pool", + "scsi", + "/dev/disk/by-path", + source_devices=[{"path": "/dev/sda"}], + source_adapter={ + "type": "scsi_host", + "parent_address": { + "unique_id": 5, + "address": { + "domain": "0x0000", + "bus": "0x00", + "slot": "0x1f", + "function": "0x2", + }, + }, + }, + source_name="srcname", + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'pool') - self.assertEqual(root.attrib['type'], 'scsi') - self.assertEqual(root.find('target/path').text, '/dev/disk/by-path') - self.assertEqual(root.find('source/device'), None) - self.assertEqual(root.find('source/name'), None) - self.assertEqual(root.find('source/adapter').attrib['type'], 'scsi_host') - self.assertEqual(root.find('source/adapter/parentaddr').attrib['unique_id'], '5') - self.assertEqual(root.find('source/adapter/parentaddr/address').attrib['domain'], '0x0000') - self.assertEqual(root.find('source/adapter/parentaddr/address').attrib['bus'], '0x00') - self.assertEqual(root.find('source/adapter/parentaddr/address').attrib['slot'], '0x1f') - self.assertEqual(root.find('source/adapter/parentaddr/address').attrib['function'], '0x2') + self.assertEqual(root.find("name").text, "pool") + self.assertEqual(root.attrib["type"], "scsi") + self.assertEqual(root.find("target/path").text, "/dev/disk/by-path") + self.assertEqual(root.find("source/device"), None) + self.assertEqual(root.find("source/name"), None) + self.assertEqual(root.find("source/adapter").attrib["type"], "scsi_host") + self.assertEqual( + root.find("source/adapter/parentaddr").attrib["unique_id"], "5" + ) + self.assertEqual( + root.find("source/adapter/parentaddr/address").attrib["domain"], "0x0000" + ) + self.assertEqual( + root.find("source/adapter/parentaddr/address").attrib["bus"], "0x00" + ) + self.assertEqual( + root.find("source/adapter/parentaddr/address").attrib["slot"], "0x1f" + ) + self.assertEqual( + root.find("source/adapter/parentaddr/address").attrib["function"], "0x2" + ) def test_pool_with_rbd(self): - ''' + """ Test virt._gen_pool_xml() with an RBD source - ''' - xml_data = virt._gen_pool_xml('pool', - 'rbd', - source_devices=[{'path': '/dev/sda'}], - source_hosts=['1.2.3.4', 'my.ceph.monitor:69'], - source_auth={ - 'type': 'ceph', - 'username': 'admin', - 'secret': { - 'type': 'uuid', - 'value': 'someuuid' - } - }, - source_name='srcname', - source_adapter={'type': 'scsi_host', 'name': 'host0'}, - source_dir='/some/dir', - source_format='fmt') + """ + xml_data = virt._gen_pool_xml( + "pool", + "rbd", + source_devices=[{"path": "/dev/sda"}], + source_hosts=["1.2.3.4", "my.ceph.monitor:69"], + source_auth={ + "type": "ceph", + "username": "admin", + "secret": {"type": "uuid", "value": "someuuid"}, + }, + source_name="srcname", + source_adapter={"type": "scsi_host", "name": "host0"}, + source_dir="/some/dir", + source_format="fmt", + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'pool') - self.assertEqual(root.attrib['type'], 'rbd') - self.assertEqual(root.find('target'), None) - self.assertEqual(root.find('source/device'), None) - self.assertEqual(root.find('source/name').text, 'srcname') - self.assertEqual(root.find('source/adapter'), None) - self.assertEqual(root.find('source/dir'), None) - self.assertEqual(root.find('source/format'), None) - self.assertEqual(root.findall('source/host')[0].attrib['name'], '1.2.3.4') - self.assertTrue('port' not in root.findall('source/host')[0].attrib) - self.assertEqual(root.findall('source/host')[1].attrib['name'], 'my.ceph.monitor') - self.assertEqual(root.findall('source/host')[1].attrib['port'], '69') - self.assertEqual(root.find('source/auth').attrib['type'], 'ceph') - self.assertEqual(root.find('source/auth').attrib['username'], 'admin') - self.assertEqual(root.find('source/auth/secret').attrib['uuid'], 'someuuid') + self.assertEqual(root.find("name").text, "pool") + self.assertEqual(root.attrib["type"], "rbd") + self.assertEqual(root.find("target"), None) + self.assertEqual(root.find("source/device"), None) + self.assertEqual(root.find("source/name").text, "srcname") + self.assertEqual(root.find("source/adapter"), None) + self.assertEqual(root.find("source/dir"), None) + self.assertEqual(root.find("source/format"), None) + self.assertEqual(root.findall("source/host")[0].attrib["name"], "1.2.3.4") + self.assertTrue("port" not in root.findall("source/host")[0].attrib) + self.assertEqual( + root.findall("source/host")[1].attrib["name"], "my.ceph.monitor" + ) + self.assertEqual(root.findall("source/host")[1].attrib["port"], "69") + self.assertEqual(root.find("source/auth").attrib["type"], "ceph") + self.assertEqual(root.find("source/auth").attrib["username"], "admin") + self.assertEqual(root.find("source/auth/secret").attrib["uuid"], "someuuid") def test_pool_with_netfs(self): - ''' + """ Test virt._gen_pool_xml() with a netfs source - ''' - xml_data = virt._gen_pool_xml('pool', - 'netfs', - target='/path/to/target', - permissions={ - 'mode': '0770', - 'owner': 1000, - 'group': 100, - 'label': 'seclabel' - }, - source_devices=[{'path': '/dev/sda'}], - source_hosts=['nfs.host'], - source_name='srcname', - source_adapter={'type': 'scsi_host', 'name': 'host0'}, - source_dir='/some/dir', - source_format='nfs') + """ + xml_data = virt._gen_pool_xml( + "pool", + "netfs", + target="/path/to/target", + permissions={ + "mode": "0770", + "owner": 1000, + "group": 100, + "label": "seclabel", + }, + source_devices=[{"path": "/dev/sda"}], + source_hosts=["nfs.host"], + source_name="srcname", + source_adapter={"type": "scsi_host", "name": "host0"}, + source_dir="/some/dir", + source_format="nfs", + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'pool') - self.assertEqual(root.attrib['type'], 'netfs') - self.assertEqual(root.find('target/path').text, '/path/to/target') - self.assertEqual(root.find('target/permissions/mode').text, '0770') - self.assertEqual(root.find('target/permissions/owner').text, '1000') - self.assertEqual(root.find('target/permissions/group').text, '100') - self.assertEqual(root.find('target/permissions/label').text, 'seclabel') - self.assertEqual(root.find('source/device'), None) - self.assertEqual(root.find('source/name'), None) - self.assertEqual(root.find('source/adapter'), None) - self.assertEqual(root.find('source/dir').attrib['path'], '/some/dir') - self.assertEqual(root.find('source/format').attrib['type'], 'nfs') - self.assertEqual(root.find('source/host').attrib['name'], 'nfs.host') - self.assertEqual(root.find('source/auth'), None) + self.assertEqual(root.find("name").text, "pool") + self.assertEqual(root.attrib["type"], "netfs") + self.assertEqual(root.find("target/path").text, "/path/to/target") + self.assertEqual(root.find("target/permissions/mode").text, "0770") + self.assertEqual(root.find("target/permissions/owner").text, "1000") + self.assertEqual(root.find("target/permissions/group").text, "100") + self.assertEqual(root.find("target/permissions/label").text, "seclabel") + self.assertEqual(root.find("source/device"), None) + self.assertEqual(root.find("source/name"), None) + self.assertEqual(root.find("source/adapter"), None) + self.assertEqual(root.find("source/dir").attrib["path"], "/some/dir") + self.assertEqual(root.find("source/format").attrib["type"], "nfs") + self.assertEqual(root.find("source/host").attrib["name"], "nfs.host") + self.assertEqual(root.find("source/auth"), None) def test_pool_with_iscsi_direct(self): - ''' + """ Test virt._gen_pool_xml() with a iscsi-direct source - ''' - xml_data = virt._gen_pool_xml('pool', - 'iscsi-direct', - source_hosts=['iscsi.example.com'], - source_devices=[{'path': 'iqn.2013-06.com.example:iscsi-pool'}], - source_initiator='iqn.2013-06.com.example:iscsi-initiator') + """ + xml_data = virt._gen_pool_xml( + "pool", + "iscsi-direct", + source_hosts=["iscsi.example.com"], + source_devices=[{"path": "iqn.2013-06.com.example:iscsi-pool"}], + source_initiator="iqn.2013-06.com.example:iscsi-initiator", + ) root = ET.fromstring(xml_data) - self.assertEqual(root.find('name').text, 'pool') - self.assertEqual(root.attrib['type'], 'iscsi-direct') - self.assertEqual(root.find('target'), None) - self.assertEqual(root.find('source/device').attrib['path'], 'iqn.2013-06.com.example:iscsi-pool') - self.assertEqual(root.findall('source/host')[0].attrib['name'], 'iscsi.example.com') - self.assertEqual(root.find('source/initiator/iqn').attrib['name'], 'iqn.2013-06.com.example:iscsi-initiator') + self.assertEqual(root.find("name").text, "pool") + self.assertEqual(root.attrib["type"], "iscsi-direct") + self.assertEqual(root.find("target"), None) + self.assertEqual( + root.find("source/device").attrib["path"], + "iqn.2013-06.com.example:iscsi-pool", + ) + self.assertEqual( + root.findall("source/host")[0].attrib["name"], "iscsi.example.com" + ) + self.assertEqual( + root.find("source/initiator/iqn").attrib["name"], + "iqn.2013-06.com.example:iscsi-initiator", + ) def test_pool_define(self): - ''' + """ Test virt.pool_define() - ''' + """ mock_pool = MagicMock() mock_secret = MagicMock() mock_secret_define = MagicMock(return_value=mock_secret) @@ -2513,22 +2958,29 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): self.mock_conn.storagePoolCreateXML = MagicMock(return_value=mock_pool) self.mock_conn.storagePoolDefineXML = MagicMock(return_value=mock_pool) - mocks = [mock_pool, mock_secret, mock_secret_define, self.mock_conn.storagePoolCreateXML, - self.mock_conn.secretDefineXML, self.mock_conn.storagePoolDefineXML] + mocks = [ + mock_pool, + mock_secret, + mock_secret_define, + self.mock_conn.storagePoolCreateXML, + self.mock_conn.secretDefineXML, + self.mock_conn.storagePoolDefineXML, + ] # Test case with already defined secret and permanent pool - self.assertTrue(virt.pool_define('default', - 'rbd', - source_hosts=['one.example.com', 'two.example.com'], - source_name='rbdvol', - source_auth={ - 'type': 'ceph', - 'username': 'admin', - 'secret': { - 'type': 'uuid', - 'value': 'someuuid' - } - })) + self.assertTrue( + virt.pool_define( + "default", + "rbd", + source_hosts=["one.example.com", "two.example.com"], + source_name="rbdvol", + source_auth={ + "type": "ceph", + "username": "admin", + "secret": {"type": "uuid", "value": "someuuid"}, + }, + ) + ) self.mock_conn.storagePoolDefineXML.assert_called_once() self.mock_conn.storagePoolCreateXML.assert_not_called() mock_pool.create.assert_called_once() @@ -2537,87 +2989,99 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): # Test case with Ceph secret to be defined and transient pool for mock in mocks: mock.reset_mock() - self.assertTrue(virt.pool_define('default', - 'rbd', - transient=True, - source_hosts=['one.example.com', 'two.example.com'], - source_name='rbdvol', - source_auth={ - 'username': 'admin', - 'password': 'c2VjcmV0' - })) + self.assertTrue( + virt.pool_define( + "default", + "rbd", + transient=True, + source_hosts=["one.example.com", "two.example.com"], + source_name="rbdvol", + source_auth={"username": "admin", "password": "c2VjcmV0"}, + ) + ) self.mock_conn.storagePoolDefineXML.assert_not_called() pool_xml = self.mock_conn.storagePoolCreateXML.call_args[0][0] root = ET.fromstring(pool_xml) - self.assertEqual(root.find('source/auth').attrib['type'], 'ceph') - self.assertEqual(root.find('source/auth').attrib['username'], 'admin') - self.assertEqual(root.find('source/auth/secret').attrib['usage'], 'pool_default') + self.assertEqual(root.find("source/auth").attrib["type"], "ceph") + self.assertEqual(root.find("source/auth").attrib["username"], "admin") + self.assertEqual( + root.find("source/auth/secret").attrib["usage"], "pool_default" + ) mock_pool.create.assert_not_called() - mock_secret.setValue.assert_called_once_with(b'secret') + mock_secret.setValue.assert_called_once_with(b"secret") secret_xml = mock_secret_define.call_args[0][0] root = ET.fromstring(secret_xml) - self.assertEqual(root.find('usage/name').text, 'pool_default') - self.assertEqual(root.find('usage').attrib['type'], 'ceph') - self.assertEqual(root.attrib['private'], 'yes') - self.assertEqual(root.find('description').text, 'Passphrase for default pool created by Salt') + self.assertEqual(root.find("usage/name").text, "pool_default") + self.assertEqual(root.find("usage").attrib["type"], "ceph") + self.assertEqual(root.attrib["private"], "yes") + self.assertEqual( + root.find("description").text, "Passphrase for default pool created by Salt" + ) # Test case with iscsi secret not starting for mock in mocks: mock.reset_mock() - self.assertTrue(virt.pool_define('default', - 'iscsi', - target='/dev/disk/by-path', - source_hosts=['iscsi.example.com'], - source_devices=[{'path': 'iqn.2013-06.com.example:iscsi-pool'}], - source_auth={ - 'username': 'admin', - 'password': 'secret' - }, - start=False)) + self.assertTrue( + virt.pool_define( + "default", + "iscsi", + target="/dev/disk/by-path", + source_hosts=["iscsi.example.com"], + source_devices=[{"path": "iqn.2013-06.com.example:iscsi-pool"}], + source_auth={"username": "admin", "password": "secret"}, + start=False, + ) + ) self.mock_conn.storagePoolCreateXML.assert_not_called() pool_xml = self.mock_conn.storagePoolDefineXML.call_args[0][0] root = ET.fromstring(pool_xml) - self.assertEqual(root.find('source/auth').attrib['type'], 'chap') - self.assertEqual(root.find('source/auth').attrib['username'], 'admin') - self.assertEqual(root.find('source/auth/secret').attrib['usage'], 'pool_default') + self.assertEqual(root.find("source/auth").attrib["type"], "chap") + self.assertEqual(root.find("source/auth").attrib["username"], "admin") + self.assertEqual( + root.find("source/auth/secret").attrib["usage"], "pool_default" + ) mock_pool.create.assert_not_called() - mock_secret.setValue.assert_called_once_with('secret') + mock_secret.setValue.assert_called_once_with("secret") secret_xml = mock_secret_define.call_args[0][0] root = ET.fromstring(secret_xml) - self.assertEqual(root.find('usage/target').text, 'pool_default') - self.assertEqual(root.find('usage').attrib['type'], 'iscsi') - self.assertEqual(root.attrib['private'], 'yes') - self.assertEqual(root.find('description').text, 'Passphrase for default pool created by Salt') + self.assertEqual(root.find("usage/target").text, "pool_default") + self.assertEqual(root.find("usage").attrib["type"], "iscsi") + self.assertEqual(root.attrib["private"], "yes") + self.assertEqual( + root.find("description").text, "Passphrase for default pool created by Salt" + ) def test_list_pools(self): - ''' + """ Test virt.list_pools() - ''' - names = ['pool1', 'default', 'pool2'] + """ + names = ["pool1", "default", "pool2"] pool_mocks = [MagicMock(), MagicMock(), MagicMock()] for i, value in enumerate(names): pool_mocks[i].name.return_value = value - self.mock_conn.listAllStoragePools.return_value = pool_mocks # pylint: disable=no-member + self.mock_conn.listAllStoragePools.return_value = ( + pool_mocks # pylint: disable=no-member + ) actual = virt.list_pools() self.assertEqual(names, actual) def test_pool_info(self): - ''' + """ Test virt.pool_info() - ''' + """ # pylint: disable=no-member pool_mock = MagicMock() - pool_mock.name.return_value = 'foo' - pool_mock.UUIDString.return_value = 'some-uuid' + pool_mock.name.return_value = "foo" + pool_mock.UUIDString.return_value = "some-uuid" pool_mock.info.return_value = [0, 1234, 5678, 123] pool_mock.autostart.return_value = True pool_mock.isPersistent.return_value = True - pool_mock.XMLDesc.return_value = '''<pool type='dir'> + pool_mock.XMLDesc.return_value = """<pool type='dir'> <name>default</name> <uuid>d92682d0-33cf-4e10-9837-a216c463e158</uuid> <capacity unit='bytes'>854374301696</capacity> @@ -2633,34 +3097,40 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <group>0</group> </permissions> </target> -</pool>''' +</pool>""" self.mock_conn.listAllStoragePools.return_value = [pool_mock] # pylint: enable=no-member - pool = virt.pool_info('foo') - self.assertEqual({'foo': { - 'uuid': 'some-uuid', - 'state': 'inactive', - 'capacity': 1234, - 'allocation': 5678, - 'free': 123, - 'autostart': True, - 'persistent': True, - 'type': 'dir', - 'target_path': '/srv/vms'}}, pool) + pool = virt.pool_info("foo") + self.assertEqual( + { + "foo": { + "uuid": "some-uuid", + "state": "inactive", + "capacity": 1234, + "allocation": 5678, + "free": 123, + "autostart": True, + "persistent": True, + "type": "dir", + "target_path": "/srv/vms", + } + }, + pool, + ) def test_pool_info_notarget(self): - ''' + """ Test virt.pool_info() - ''' + """ # pylint: disable=no-member pool_mock = MagicMock() - pool_mock.name.return_value = 'ceph' - pool_mock.UUIDString.return_value = 'some-uuid' + pool_mock.name.return_value = "ceph" + pool_mock.UUIDString.return_value = "some-uuid" pool_mock.info.return_value = [0, 0, 0, 0] pool_mock.autostart.return_value = True pool_mock.isPersistent.return_value = True - pool_mock.XMLDesc.return_value = '''<pool type='rbd'> + pool_mock.XMLDesc.return_value = """<pool type='rbd'> <name>ceph</name> <uuid>some-uuid</uuid> <capacity unit='bytes'>0</capacity> @@ -2674,46 +3144,52 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <secret uuid='2ec115d7-3a88-3ceb-bc12-0ac909a6fd87'/> </auth> </source> -</pool>''' +</pool>""" self.mock_conn.listAllStoragePools.return_value = [pool_mock] # pylint: enable=no-member - pool = virt.pool_info('ceph') - self.assertEqual({'ceph': { - 'uuid': 'some-uuid', - 'state': 'inactive', - 'capacity': 0, - 'allocation': 0, - 'free': 0, - 'autostart': True, - 'persistent': True, - 'type': 'rbd', - 'target_path': None}}, pool) + pool = virt.pool_info("ceph") + self.assertEqual( + { + "ceph": { + "uuid": "some-uuid", + "state": "inactive", + "capacity": 0, + "allocation": 0, + "free": 0, + "autostart": True, + "persistent": True, + "type": "rbd", + "target_path": None, + } + }, + pool, + ) def test_pool_info_notfound(self): - ''' + """ Test virt.pool_info() when the pool can't be found - ''' + """ # pylint: disable=no-member self.mock_conn.listAllStoragePools.return_value = [] # pylint: enable=no-member - pool = virt.pool_info('foo') + pool = virt.pool_info("foo") self.assertEqual({}, pool) def test_pool_info_all(self): - ''' + """ Test virt.pool_info() - ''' + """ # pylint: disable=no-member pool_mocks = [] for i in range(2): pool_mock = MagicMock() - pool_mock.name.return_value = 'pool{0}'.format(i) - pool_mock.UUIDString.return_value = 'some-uuid-{0}'.format(i) + pool_mock.name.return_value = "pool{0}".format(i) + pool_mock.UUIDString.return_value = "some-uuid-{0}".format(i) pool_mock.info.return_value = [0, 1234, 5678, 123] pool_mock.autostart.return_value = True pool_mock.isPersistent.return_value = True - pool_mock.XMLDesc.return_value = '''<pool type='dir'> + pool_mock.XMLDesc.return_value = """<pool type='dir'> <name>default</name> <uuid>d92682d0-33cf-4e10-9837-a216c463e158</uuid> <capacity unit='bytes'>854374301696</capacity> @@ -2729,95 +3205,100 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <group>0</group> </permissions> </target> -</pool>''' +</pool>""" pool_mocks.append(pool_mock) self.mock_conn.listAllStoragePools.return_value = pool_mocks # pylint: enable=no-member pool = virt.pool_info() - self.assertEqual({ - 'pool0': + self.assertEqual( { - 'uuid': 'some-uuid-0', - 'state': 'inactive', - 'capacity': 1234, - 'allocation': 5678, - 'free': 123, - 'autostart': True, - 'persistent': True, - 'type': 'dir', - 'target_path': '/srv/vms' - }, 'pool1': { - 'uuid': 'some-uuid-1', - 'state': 'inactive', - 'capacity': 1234, - 'allocation': 5678, - 'free': 123, - 'autostart': True, - 'persistent': True, - 'type': 'dir', - 'target_path': '/srv/vms' - } - }, pool) + "pool0": { + "uuid": "some-uuid-0", + "state": "inactive", + "capacity": 1234, + "allocation": 5678, + "free": 123, + "autostart": True, + "persistent": True, + "type": "dir", + "target_path": "/srv/vms", + }, + "pool1": { + "uuid": "some-uuid-1", + "state": "inactive", + "capacity": 1234, + "allocation": 5678, + "free": 123, + "autostart": True, + "persistent": True, + "type": "dir", + "target_path": "/srv/vms", + }, + }, + pool, + ) def test_pool_get_xml(self): - ''' + """ Test virt.pool_get_xml - ''' + """ pool_mock = MagicMock() - pool_mock.XMLDesc.return_value = '<pool>Raw XML</pool>' + pool_mock.XMLDesc.return_value = "<pool>Raw XML</pool>" self.mock_conn.storagePoolLookupByName.return_value = pool_mock - self.assertEqual('<pool>Raw XML</pool>', virt.pool_get_xml('default')) + self.assertEqual("<pool>Raw XML</pool>", virt.pool_get_xml("default")) def test_pool_list_volumes(self): - ''' + """ Test virt.pool_list_volumes - ''' - names = ['volume1', 'volume2'] + """ + names = ["volume1", "volume2"] mock_pool = MagicMock() # pylint: disable=no-member mock_pool.listVolumes.return_value = names self.mock_conn.storagePoolLookupByName.return_value = mock_pool # pylint: enable=no-member - self.assertEqual(names, virt.pool_list_volumes('default')) + self.assertEqual(names, virt.pool_list_volumes("default")) - @patch('salt.modules.virt._is_kvm_hyper', return_value=True) - @patch('salt.modules.virt._is_xen_hyper', return_value=False) + @patch("salt.modules.virt._is_kvm_hyper", return_value=True) + @patch("salt.modules.virt._is_xen_hyper", return_value=False) def test_get_hypervisor(self, isxen_mock, iskvm_mock): - ''' + """ test the virt.get_hypervisor() function - ''' - self.assertEqual('kvm', virt.get_hypervisor()) + """ + self.assertEqual("kvm", virt.get_hypervisor()) iskvm_mock.return_value = False self.assertIsNone(virt.get_hypervisor()) isxen_mock.return_value = True - self.assertEqual('xen', virt.get_hypervisor()) + self.assertEqual("xen", virt.get_hypervisor()) def test_pool_delete(self): - ''' + """ Test virt.pool_delete function - ''' + """ mock_pool = MagicMock() mock_pool.delete = MagicMock(return_value=0) self.mock_conn.storagePoolLookupByName = MagicMock(return_value=mock_pool) - res = virt.pool_delete('test-pool') + res = virt.pool_delete("test-pool") self.assertTrue(res) - self.mock_conn.storagePoolLookupByName.assert_called_once_with('test-pool') + self.mock_conn.storagePoolLookupByName.assert_called_once_with("test-pool") # Shouldn't be called with another parameter so far since those are not implemented # and thus throwing exceptions. - mock_pool.delete.assert_called_once_with(self.mock_libvirt.VIR_STORAGE_POOL_DELETE_NORMAL) + mock_pool.delete.assert_called_once_with( + self.mock_libvirt.VIR_STORAGE_POOL_DELETE_NORMAL + ) def test_full_info(self): - ''' + """ Test virt.full_info - ''' - xml = '''<domain type='kvm' id='7'> + """ + xml = """<domain type='kvm' id='7'> <uuid>28deee33-4859-4f23-891c-ee239cffec94</uuid> <name>test-vm</name> <on_poweroff>destroy</on_poweroff> @@ -2846,10 +3327,10 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): </graphics> </devices> </domain> - ''' + """ self.set_mock_vm("test-vm", xml) - qemu_infos = '''[{ + qemu_infos = """[{ "virtual-size": 25769803776, "filename": "/disks/test.qcow2", "cluster-size": 65536, @@ -2884,69 +3365,79 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): } }, "dirty-flag": false - }]''' + }]""" - self.mock_popen.communicate.return_value = [qemu_infos] # pylint: disable=no-member + self.mock_popen.communicate.return_value = [ + qemu_infos + ] # pylint: disable=no-member - self.mock_conn.getInfo = MagicMock(return_value=['x86_64', 4096, 8, 2712, 1, 2, 4, 2]) + self.mock_conn.getInfo = MagicMock( + return_value=["x86_64", 4096, 8, 2712, 1, 2, 4, 2] + ) actual = virt.full_info() # Check that qemu-img was called with the proper parameters - qemu_img_call = [call for call in self.mock_subprocess.Popen.call_args_list if 'qemu-img' in call[0][0]][0] - self.assertIn('info', qemu_img_call[0][0]) - self.assertIn('-U', qemu_img_call[0][0]) + qemu_img_call = [ + call + for call in self.mock_subprocess.Popen.call_args_list + if "qemu-img" in call[0][0] + ][0] + self.assertIn("info", qemu_img_call[0][0]) + self.assertIn("-U", qemu_img_call[0][0]) # Test the hypervisor infos - self.assertEqual(2816, actual['freemem']) - self.assertEqual(6, actual['freecpu']) - self.assertEqual(4, actual['node_info']['cpucores']) - self.assertEqual(2712, actual['node_info']['cpumhz']) - self.assertEqual('x86_64', actual['node_info']['cpumodel']) - self.assertEqual(8, actual['node_info']['cpus']) - self.assertEqual(2, actual['node_info']['cputhreads']) - self.assertEqual(1, actual['node_info']['numanodes']) - self.assertEqual(4096, actual['node_info']['phymemory']) - self.assertEqual(2, actual['node_info']['sockets']) + self.assertEqual(2816, actual["freemem"]) + self.assertEqual(6, actual["freecpu"]) + self.assertEqual(4, actual["node_info"]["cpucores"]) + self.assertEqual(2712, actual["node_info"]["cpumhz"]) + self.assertEqual("x86_64", actual["node_info"]["cpumodel"]) + self.assertEqual(8, actual["node_info"]["cpus"]) + self.assertEqual(2, actual["node_info"]["cputhreads"]) + self.assertEqual(1, actual["node_info"]["numanodes"]) + self.assertEqual(4096, actual["node_info"]["phymemory"]) + self.assertEqual(2, actual["node_info"]["sockets"]) # Test the vm_info output: - self.assertEqual(2, actual['vm_info']['test-vm']['cpu']) - self.assertEqual(1234, actual['vm_info']['test-vm']['cputime']) - self.assertEqual(1024 * 1024, actual['vm_info']['test-vm']['mem']) - self.assertEqual(2048 * 1024, actual['vm_info']['test-vm']['maxMem']) - self.assertEqual('shutdown', actual['vm_info']['test-vm']['state']) - self.assertEqual('28deee33-4859-4f23-891c-ee239cffec94', actual['vm_info']['test-vm']['uuid']) - self.assertEqual('destroy', actual['vm_info']['test-vm']['on_crash']) - self.assertEqual('restart', actual['vm_info']['test-vm']['on_reboot']) - self.assertEqual('destroy', actual['vm_info']['test-vm']['on_poweroff']) + self.assertEqual(2, actual["vm_info"]["test-vm"]["cpu"]) + self.assertEqual(1234, actual["vm_info"]["test-vm"]["cputime"]) + self.assertEqual(1024 * 1024, actual["vm_info"]["test-vm"]["mem"]) + self.assertEqual(2048 * 1024, actual["vm_info"]["test-vm"]["maxMem"]) + self.assertEqual("shutdown", actual["vm_info"]["test-vm"]["state"]) + self.assertEqual( + "28deee33-4859-4f23-891c-ee239cffec94", actual["vm_info"]["test-vm"]["uuid"] + ) + self.assertEqual("destroy", actual["vm_info"]["test-vm"]["on_crash"]) + self.assertEqual("restart", actual["vm_info"]["test-vm"]["on_reboot"]) + self.assertEqual("destroy", actual["vm_info"]["test-vm"]["on_poweroff"]) # Test the nics - nic = actual['vm_info']['test-vm']['nics']['ac:de:48:b6:8b:59'] - self.assertEqual('bridge', nic['type']) - self.assertEqual('ac:de:48:b6:8b:59', nic['mac']) + nic = actual["vm_info"]["test-vm"]["nics"]["ac:de:48:b6:8b:59"] + self.assertEqual("bridge", nic["type"]) + self.assertEqual("ac:de:48:b6:8b:59", nic["mac"]) # Test the disks - disks = actual['vm_info']['test-vm']['disks'] - disk = disks.get('vda') - self.assertEqual('/disks/test.qcow2', disk['file']) - self.assertEqual('disk', disk['type']) - self.assertEqual('/disks/mybacking.qcow2', disk['backing file']['file']) - cdrom = disks.get('hda') - self.assertEqual('/disks/test-cdrom.iso', cdrom['file']) - self.assertEqual('cdrom', cdrom['type']) - self.assertFalse('backing file' in cdrom.keys()) + disks = actual["vm_info"]["test-vm"]["disks"] + disk = disks.get("vda") + self.assertEqual("/disks/test.qcow2", disk["file"]) + self.assertEqual("disk", disk["type"]) + self.assertEqual("/disks/mybacking.qcow2", disk["backing file"]["file"]) + cdrom = disks.get("hda") + self.assertEqual("/disks/test-cdrom.iso", cdrom["file"]) + self.assertEqual("cdrom", cdrom["type"]) + self.assertFalse("backing file" in cdrom.keys()) # Test the graphics - graphics = actual['vm_info']['test-vm']['graphics'] - self.assertEqual('vnc', graphics['type']) - self.assertEqual('5900', graphics['port']) - self.assertEqual('0.0.0.0', graphics['listen']) + graphics = actual["vm_info"]["test-vm"]["graphics"] + self.assertEqual("vnc", graphics["type"]) + self.assertEqual("5900", graphics["port"]) + self.assertEqual("0.0.0.0", graphics["listen"]) def test_pool_update(self): - ''' + """ Test the pool_update function - ''' - current_xml = '''<pool type='dir'> + """ + current_xml = """<pool type='dir'> <name>default</name> <uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid> <capacity unit='bytes'>1999421108224</capacity> @@ -2962,29 +3453,31 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <group>100</group> </permissions> </target> - </pool>''' + </pool>""" - expected_xml = '<pool type="netfs">' \ - '<name>default</name>' \ - '<uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid>' \ - '<capacity unit="bytes">1999421108224</capacity>' \ - '<allocation unit="bytes">713207042048</allocation>' \ - '<available unit="bytes">1286214066176</available>' \ - '<target>' \ - '<path>/mnt/cifs</path>' \ - '<permissions>' \ - '<mode>0774</mode>' \ - '<owner>1234</owner>' \ - '<group>123</group>' \ - '</permissions>' \ - '</target>' \ - '<source>' \ - '<dir path="samba_share" />' \ - '<host name="one.example.com" />' \ - '<host name="two.example.com" />' \ - '<format type="cifs" />' \ - '</source>' \ - '</pool>' + expected_xml = ( + '<pool type="netfs">' + "<name>default</name>" + "<uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid>" + '<capacity unit="bytes">1999421108224</capacity>' + '<allocation unit="bytes">713207042048</allocation>' + '<available unit="bytes">1286214066176</available>' + "<target>" + "<path>/mnt/cifs</path>" + "<permissions>" + "<mode>0774</mode>" + "<owner>1234</owner>" + "<group>123</group>" + "</permissions>" + "</target>" + "<source>" + '<dir path="samba_share" />' + '<host name="one.example.com" />' + '<host name="two.example.com" />' + '<format type="cifs" />' + "</source>" + "</pool>" + ) mocked_pool = MagicMock() mocked_pool.XMLDesc = MagicMock(return_value=current_xml) @@ -2992,21 +3485,24 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): self.mock_conn.storagePoolDefineXML = MagicMock() self.assertTrue( - virt.pool_update('default', - 'netfs', - target='/mnt/cifs', - permissions={'mode': '0774', 'owner': '1234', 'group': '123'}, - source_format='cifs', - source_dir='samba_share', - source_hosts=['one.example.com', 'two.example.com'])) + virt.pool_update( + "default", + "netfs", + target="/mnt/cifs", + permissions={"mode": "0774", "owner": "1234", "group": "123"}, + source_format="cifs", + source_dir="samba_share", + source_hosts=["one.example.com", "two.example.com"], + ) + ) self.mock_conn.storagePoolDefineXML.assert_called_once_with(expected_xml) def test_pool_update_nochange(self): - ''' + """ Test the pool_update function when no change is needed - ''' + """ - current_xml = '''<pool type='dir'> + current_xml = """<pool type='dir'> <name>default</name> <uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid> <capacity unit='bytes'>1999421108224</capacity> @@ -3022,7 +3518,7 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <group>100</group> </permissions> </target> - </pool>''' + </pool>""" mocked_pool = MagicMock() mocked_pool.XMLDesc = MagicMock(return_value=current_xml) @@ -3030,18 +3526,21 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): self.mock_conn.storagePoolDefineXML = MagicMock() self.assertFalse( - virt.pool_update('default', - 'dir', - target='/path/to/pool', - permissions={'mode': '0775', 'owner': '0', 'group': '100'}, - test=True)) + virt.pool_update( + "default", + "dir", + target="/path/to/pool", + permissions={"mode": "0775", "owner": "0", "group": "100"}, + test=True, + ) + ) self.mock_conn.storagePoolDefineXML.assert_not_called() def test_pool_update_password(self): - ''' + """ Test the pool_update function, where the password only is changed - ''' - current_xml = '''<pool type='rbd'> + """ + current_xml = """<pool type='rbd'> <name>default</name> <uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid> <capacity unit='bytes'>1999421108224</capacity> @@ -3055,23 +3554,25 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <secret uuid='14e9a0f1-8fbf-4097-b816-5b094c182212'/> </auth> </source> - </pool>''' + </pool>""" - expected_xml = '<pool type="rbd">' \ - '<name>default</name>' \ - '<uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid>' \ - '<capacity unit="bytes">1999421108224</capacity>' \ - '<allocation unit="bytes">713207042048</allocation>' \ - '<available unit="bytes">1286214066176</available>' \ - '<source>' \ - '<host name="ses4.tf.local" />' \ - '<host name="ses5.tf.local" />' \ - '<auth type="ceph" username="libvirt">' \ - '<secret uuid="14e9a0f1-8fbf-4097-b816-5b094c182212" />' \ - '</auth>' \ - '<name>iscsi-images</name>' \ - '</source>' \ - '</pool>' + expected_xml = ( + '<pool type="rbd">' + "<name>default</name>" + "<uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid>" + '<capacity unit="bytes">1999421108224</capacity>' + '<allocation unit="bytes">713207042048</allocation>' + '<available unit="bytes">1286214066176</available>' + "<source>" + '<host name="ses4.tf.local" />' + '<host name="ses5.tf.local" />' + '<auth type="ceph" username="libvirt">' + '<secret uuid="14e9a0f1-8fbf-4097-b816-5b094c182212" />' + "</auth>" + "<name>iscsi-images</name>" + "</source>" + "</pool>" + ) mock_secret = MagicMock() self.mock_conn.secretLookupByUUIDString = MagicMock(return_value=mock_secret) @@ -3081,21 +3582,23 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): self.mock_conn.storagePoolLookupByName = MagicMock(return_value=mocked_pool) self.mock_conn.storagePoolDefineXML = MagicMock() - self.assertTrue( - virt.pool_update('default', - 'rbd', - source_name='iscsi-images', - source_hosts=['ses4.tf.local', 'ses5.tf.local'], - source_auth={'username': 'libvirt', - 'password': 'c2VjcmV0'})) - self.mock_conn.storagePoolDefineXML.assert_called_once_with(expected_xml) - mock_secret.setValue.assert_called_once_with(b'secret') + self.assertFalse( + virt.pool_update( + "default", + "rbd", + source_name="iscsi-images", + source_hosts=["ses4.tf.local", "ses5.tf.local"], + source_auth={"username": "libvirt", "password": "c2VjcmV0"}, + ) + ) + self.mock_conn.storagePoolDefineXML.assert_not_called() + mock_secret.setValue.assert_called_once_with(b"secret") def test_pool_update_password_create(self): - ''' + """ Test the pool_update function, where the password only is changed - ''' - current_xml = '''<pool type='rbd'> + """ + current_xml = """<pool type='rbd'> <name>default</name> <uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid> <capacity unit='bytes'>1999421108224</capacity> @@ -3106,23 +3609,25 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <host name='ses4.tf.local'/> <host name='ses5.tf.local'/> </source> - </pool>''' + </pool>""" - expected_xml = '<pool type="rbd">' \ - '<name>default</name>' \ - '<uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid>' \ - '<capacity unit="bytes">1999421108224</capacity>' \ - '<allocation unit="bytes">713207042048</allocation>' \ - '<available unit="bytes">1286214066176</available>' \ - '<source>' \ - '<host name="ses4.tf.local" />' \ - '<host name="ses5.tf.local" />' \ - '<auth type="ceph" username="libvirt">' \ - '<secret usage="pool_default" />' \ - '</auth>' \ - '<name>iscsi-images</name>' \ - '</source>' \ - '</pool>' + expected_xml = ( + '<pool type="rbd">' + "<name>default</name>" + "<uuid>20fbe05c-ab40-418a-9afa-136d512f0ede</uuid>" + '<capacity unit="bytes">1999421108224</capacity>' + '<allocation unit="bytes">713207042048</allocation>' + '<available unit="bytes">1286214066176</available>' + "<source>" + '<host name="ses4.tf.local" />' + '<host name="ses5.tf.local" />' + '<auth type="ceph" username="libvirt">' + '<secret usage="pool_default" />' + "</auth>" + "<name>iscsi-images</name>" + "</source>" + "</pool>" + ) mock_secret = MagicMock() self.mock_conn.secretDefineXML = MagicMock(return_value=mock_secret) @@ -3133,119 +3638,131 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): self.mock_conn.storagePoolDefineXML = MagicMock() self.assertTrue( - virt.pool_update('default', - 'rbd', - source_name='iscsi-images', - source_hosts=['ses4.tf.local', 'ses5.tf.local'], - source_auth={'username': 'libvirt', - 'password': 'c2VjcmV0'})) + virt.pool_update( + "default", + "rbd", + source_name="iscsi-images", + source_hosts=["ses4.tf.local", "ses5.tf.local"], + source_auth={"username": "libvirt", "password": "c2VjcmV0"}, + ) + ) self.mock_conn.storagePoolDefineXML.assert_called_once_with(expected_xml) - mock_secret.setValue.assert_called_once_with(b'secret') + mock_secret.setValue.assert_called_once_with(b"secret") def test_volume_infos(self): - ''' + """ Test virt.volume_infos - ''' + """ vms_disks = [ - ''' + """ <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> <source file='/path/to/vol0.qcow2'/> <target dev='vda' bus='virtio'/> </disk> - ''', - ''' + """, + """ <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> <source file='/path/to/vol3.qcow2'/> <target dev='vda' bus='virtio'/> </disk> - ''', - ''' + """, + """ <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> <source file='/path/to/vol2.qcow2'/> <target dev='vda' bus='virtio'/> </disk> - ''' + """, ] mock_vms = [] for idx, disk in enumerate(vms_disks): vm = MagicMock() # pylint: disable=no-member - vm.name.return_value = 'vm{0}'.format(idx) - vm.XMLDesc.return_value = ''' + vm.name.return_value = "vm{0}".format(idx) + vm.XMLDesc.return_value = """ <domain type='kvm' id='1'> <name>vm{0}</name> <devices>{1}</devices> </domain> - '''.format(idx, disk) + """.format( + idx, disk + ) # pylint: enable=no-member mock_vms.append(vm) mock_pool_data = [ { - 'name': 'pool0', - 'state': self.mock_libvirt.VIR_STORAGE_POOL_RUNNING, - 'volumes': [ + "name": "pool0", + "state": self.mock_libvirt.VIR_STORAGE_POOL_RUNNING, + "volumes": [ { - 'key': '/key/of/vol0', - 'name': 'vol0', - 'path': '/path/to/vol0.qcow2', - 'info': [0, 123456789, 123456], - 'backingStore': None + "key": "/key/of/vol0", + "name": "vol0", + "path": "/path/to/vol0.qcow2", + "info": [0, 123456789, 123456], + "backingStore": None, } - ] + ], }, { - 'name': 'pool1', - 'state': self.mock_libvirt.VIR_STORAGE_POOL_RUNNING, - 'volumes': [ + "name": "pool1", + "state": self.mock_libvirt.VIR_STORAGE_POOL_RUNNING, + "volumes": [ { - 'key': '/key/of/vol0bad', - 'name': 'vol0bad', - 'path': '/path/to/vol0bad.qcow2', - 'info': None, - 'backingStore': None + "key": "/key/of/vol0bad", + "name": "vol0bad", + "path": "/path/to/vol0bad.qcow2", + "info": None, + "backingStore": None, }, { - 'key': '/key/of/vol1', - 'name': 'vol1', - 'path': '/path/to/vol1.qcow2', - 'info': [0, 12345, 1234], - 'backingStore': None + "key": "/key/of/vol1", + "name": "vol1", + "path": "/path/to/vol1.qcow2", + "info": [0, 12345, 1234], + "backingStore": None, }, { - 'key': '/key/of/vol2', - 'name': 'vol2', - 'path': '/path/to/vol2.qcow2', - 'info': [0, 12345, 1234], - 'backingStore': '/path/to/vol0.qcow2' + "key": "/key/of/vol2", + "name": "vol2", + "path": "/path/to/vol2.qcow2", + "info": [0, 12345, 1234], + "backingStore": "/path/to/vol0.qcow2", }, ], - } + }, ] mock_pools = [] for pool_data in mock_pool_data: mock_pool = MagicMock() - mock_pool.name.return_value = pool_data['name'] # pylint: disable=no-member - mock_pool.info.return_value = [pool_data['state']] + mock_pool.name.return_value = pool_data["name"] # pylint: disable=no-member + mock_pool.info.return_value = [pool_data["state"]] mock_volumes = [] - for vol_data in pool_data['volumes']: + for vol_data in pool_data["volumes"]: mock_volume = MagicMock() # pylint: disable=no-member - mock_volume.name.return_value = vol_data['name'] - mock_volume.key.return_value = vol_data['key'] - mock_volume.path.return_value = '/path/to/{0}.qcow2'.format(vol_data['name']) - if vol_data['info']: - mock_volume.info.return_value = vol_data['info'] - backing_store = ''' + mock_volume.name.return_value = vol_data["name"] + mock_volume.key.return_value = vol_data["key"] + mock_volume.path.return_value = "/path/to/{0}.qcow2".format( + vol_data["name"] + ) + if vol_data["info"]: + mock_volume.info.return_value = vol_data["info"] + backing_store = ( + """ <backingStore> <format>qcow2</format> <path>{0}</path> </backingStore> - '''.format(vol_data['backingStore']) if vol_data['backingStore'] else '<backingStore/>' - mock_volume.XMLDesc.return_value = ''' + """.format( + vol_data["backingStore"] + ) + if vol_data["backingStore"] + else "<backingStore/>" + ) + mock_volume.XMLDesc.return_value = """ <volume type='file'> <name>{0}</name> <target> @@ -3254,195 +3771,230 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): </target> {1} </volume> - '''.format(vol_data['name'], backing_store) + """.format( + vol_data["name"], backing_store + ) else: - mock_volume.info.side_effect = self.mock_libvirt.libvirtError('No such volume') - mock_volume.XMLDesc.side_effect = self.mock_libvirt.libvirtError('No such volume') + mock_volume.info.side_effect = self.mock_libvirt.libvirtError( + "No such volume" + ) + mock_volume.XMLDesc.side_effect = self.mock_libvirt.libvirtError( + "No such volume" + ) mock_volumes.append(mock_volume) # pylint: enable=no-member - mock_pool.listAllVolumes.return_value = mock_volumes # pylint: disable=no-member + mock_pool.listAllVolumes.return_value = ( + mock_volumes # pylint: disable=no-member + ) mock_pools.append(mock_pool) inactive_pool = MagicMock() - inactive_pool.name.return_value = 'pool2' + inactive_pool.name.return_value = "pool2" inactive_pool.info.return_value = [self.mock_libvirt.VIR_STORAGE_POOL_INACTIVE] - inactive_pool.listAllVolumes.side_effect = self.mock_libvirt.libvirtError('pool is inactive') + inactive_pool.listAllVolumes.side_effect = self.mock_libvirt.libvirtError( + "pool is inactive" + ) mock_pools.append(inactive_pool) - self.mock_conn.listAllStoragePools.return_value = mock_pools # pylint: disable=no-member + self.mock_conn.listAllStoragePools.return_value = ( + mock_pools # pylint: disable=no-member + ) - with patch('salt.modules.virt._get_domain', MagicMock(return_value=mock_vms)): - actual = virt.volume_infos('pool0', 'vol0') + with patch("salt.modules.virt._get_domain", MagicMock(return_value=mock_vms)): + actual = virt.volume_infos("pool0", "vol0") self.assertEqual(1, len(actual.keys())) - self.assertEqual(1, len(actual['pool0'].keys())) - self.assertEqual(['vm0', 'vm2'], sorted(actual['pool0']['vol0']['used_by'])) - self.assertEqual('/path/to/vol0.qcow2', actual['pool0']['vol0']['path']) - self.assertEqual('file', actual['pool0']['vol0']['type']) - self.assertEqual('/key/of/vol0', actual['pool0']['vol0']['key']) - self.assertEqual(123456789, actual['pool0']['vol0']['capacity']) - self.assertEqual(123456, actual['pool0']['vol0']['allocation']) + self.assertEqual(1, len(actual["pool0"].keys())) + self.assertEqual(["vm0", "vm2"], sorted(actual["pool0"]["vol0"]["used_by"])) + self.assertEqual("/path/to/vol0.qcow2", actual["pool0"]["vol0"]["path"]) + self.assertEqual("file", actual["pool0"]["vol0"]["type"]) + self.assertEqual("/key/of/vol0", actual["pool0"]["vol0"]["key"]) + self.assertEqual(123456789, actual["pool0"]["vol0"]["capacity"]) + self.assertEqual(123456, actual["pool0"]["vol0"]["allocation"]) - self.assertEqual(virt.volume_infos('pool1', None), { - 'pool1': { - 'vol1': { - 'type': 'file', - 'key': '/key/of/vol1', - 'path': '/path/to/vol1.qcow2', - 'capacity': 12345, - 'allocation': 1234, - 'used_by': [], - }, - 'vol2': { - 'type': 'file', - 'key': '/key/of/vol2', - 'path': '/path/to/vol2.qcow2', - 'capacity': 12345, - 'allocation': 1234, - 'used_by': ['vm2'], + self.assertEqual( + virt.volume_infos("pool1", None), + { + "pool1": { + "vol1": { + "type": "file", + "key": "/key/of/vol1", + "path": "/path/to/vol1.qcow2", + "capacity": 12345, + "allocation": 1234, + "used_by": [], + }, + "vol2": { + "type": "file", + "key": "/key/of/vol2", + "path": "/path/to/vol2.qcow2", + "capacity": 12345, + "allocation": 1234, + "used_by": ["vm2"], + }, } - } - }) + }, + ) - self.assertEqual(virt.volume_infos(None, 'vol2'), { - 'pool1': { - 'vol2': { - 'type': 'file', - 'key': '/key/of/vol2', - 'path': '/path/to/vol2.qcow2', - 'capacity': 12345, - 'allocation': 1234, - 'used_by': ['vm2'], + self.assertEqual( + virt.volume_infos(None, "vol2"), + { + "pool1": { + "vol2": { + "type": "file", + "key": "/key/of/vol2", + "path": "/path/to/vol2.qcow2", + "capacity": 12345, + "allocation": 1234, + "used_by": ["vm2"], + } } - } - }) + }, + ) # Single VM test - with patch('salt.modules.virt._get_domain', MagicMock(return_value=mock_vms[0])): - actual = virt.volume_infos('pool0', 'vol0') + with patch( + "salt.modules.virt._get_domain", MagicMock(return_value=mock_vms[0]) + ): + actual = virt.volume_infos("pool0", "vol0") self.assertEqual(1, len(actual.keys())) - self.assertEqual(1, len(actual['pool0'].keys())) - self.assertEqual(['vm0'], sorted(actual['pool0']['vol0']['used_by'])) - self.assertEqual('/path/to/vol0.qcow2', actual['pool0']['vol0']['path']) - self.assertEqual('file', actual['pool0']['vol0']['type']) - self.assertEqual('/key/of/vol0', actual['pool0']['vol0']['key']) - self.assertEqual(123456789, actual['pool0']['vol0']['capacity']) - self.assertEqual(123456, actual['pool0']['vol0']['allocation']) + self.assertEqual(1, len(actual["pool0"].keys())) + self.assertEqual(["vm0"], sorted(actual["pool0"]["vol0"]["used_by"])) + self.assertEqual("/path/to/vol0.qcow2", actual["pool0"]["vol0"]["path"]) + self.assertEqual("file", actual["pool0"]["vol0"]["type"]) + self.assertEqual("/key/of/vol0", actual["pool0"]["vol0"]["key"]) + self.assertEqual(123456789, actual["pool0"]["vol0"]["capacity"]) + self.assertEqual(123456, actual["pool0"]["vol0"]["allocation"]) - self.assertEqual(virt.volume_infos('pool1', None), { - 'pool1': { - 'vol1': { - 'type': 'file', - 'key': '/key/of/vol1', - 'path': '/path/to/vol1.qcow2', - 'capacity': 12345, - 'allocation': 1234, - 'used_by': [], - }, - 'vol2': { - 'type': 'file', - 'key': '/key/of/vol2', - 'path': '/path/to/vol2.qcow2', - 'capacity': 12345, - 'allocation': 1234, - 'used_by': [], + self.assertEqual( + virt.volume_infos("pool1", None), + { + "pool1": { + "vol1": { + "type": "file", + "key": "/key/of/vol1", + "path": "/path/to/vol1.qcow2", + "capacity": 12345, + "allocation": 1234, + "used_by": [], + }, + "vol2": { + "type": "file", + "key": "/key/of/vol2", + "path": "/path/to/vol2.qcow2", + "capacity": 12345, + "allocation": 1234, + "used_by": [], + }, } - } - }) + }, + ) - self.assertEqual(virt.volume_infos(None, 'vol2'), { - 'pool1': { - 'vol2': { - 'type': 'file', - 'key': '/key/of/vol2', - 'path': '/path/to/vol2.qcow2', - 'capacity': 12345, - 'allocation': 1234, - 'used_by': [], + self.assertEqual( + virt.volume_infos(None, "vol2"), + { + "pool1": { + "vol2": { + "type": "file", + "key": "/key/of/vol2", + "path": "/path/to/vol2.qcow2", + "capacity": 12345, + "allocation": 1234, + "used_by": [], + } } - } - }) + }, + ) # No VM test - with patch('salt.modules.virt._get_domain', MagicMock(side_effect=CommandExecutionError('no VM'))): - actual = virt.volume_infos('pool0', 'vol0') + with patch( + "salt.modules.virt._get_domain", + MagicMock(side_effect=CommandExecutionError("no VM")), + ): + actual = virt.volume_infos("pool0", "vol0") self.assertEqual(1, len(actual.keys())) - self.assertEqual(1, len(actual['pool0'].keys())) - self.assertEqual([], sorted(actual['pool0']['vol0']['used_by'])) - self.assertEqual('/path/to/vol0.qcow2', actual['pool0']['vol0']['path']) - self.assertEqual('file', actual['pool0']['vol0']['type']) - self.assertEqual('/key/of/vol0', actual['pool0']['vol0']['key']) - self.assertEqual(123456789, actual['pool0']['vol0']['capacity']) - self.assertEqual(123456, actual['pool0']['vol0']['allocation']) + self.assertEqual(1, len(actual["pool0"].keys())) + self.assertEqual([], sorted(actual["pool0"]["vol0"]["used_by"])) + self.assertEqual("/path/to/vol0.qcow2", actual["pool0"]["vol0"]["path"]) + self.assertEqual("file", actual["pool0"]["vol0"]["type"]) + self.assertEqual("/key/of/vol0", actual["pool0"]["vol0"]["key"]) + self.assertEqual(123456789, actual["pool0"]["vol0"]["capacity"]) + self.assertEqual(123456, actual["pool0"]["vol0"]["allocation"]) - self.assertEqual(virt.volume_infos('pool1', None), { - 'pool1': { - 'vol1': { - 'type': 'file', - 'key': '/key/of/vol1', - 'path': '/path/to/vol1.qcow2', - 'capacity': 12345, - 'allocation': 1234, - 'used_by': [], - }, - 'vol2': { - 'type': 'file', - 'key': '/key/of/vol2', - 'path': '/path/to/vol2.qcow2', - 'capacity': 12345, - 'allocation': 1234, - 'used_by': [], + self.assertEqual( + virt.volume_infos("pool1", None), + { + "pool1": { + "vol1": { + "type": "file", + "key": "/key/of/vol1", + "path": "/path/to/vol1.qcow2", + "capacity": 12345, + "allocation": 1234, + "used_by": [], + }, + "vol2": { + "type": "file", + "key": "/key/of/vol2", + "path": "/path/to/vol2.qcow2", + "capacity": 12345, + "allocation": 1234, + "used_by": [], + }, } - } - }) + }, + ) - self.assertEqual(virt.volume_infos(None, 'vol2'), { - 'pool1': { - 'vol2': { - 'type': 'file', - 'key': '/key/of/vol2', - 'path': '/path/to/vol2.qcow2', - 'capacity': 12345, - 'allocation': 1234, - 'used_by': [], + self.assertEqual( + virt.volume_infos(None, "vol2"), + { + "pool1": { + "vol2": { + "type": "file", + "key": "/key/of/vol2", + "path": "/path/to/vol2.qcow2", + "capacity": 12345, + "allocation": 1234, + "used_by": [], + } } - } - }) + }, + ) def test_volume_delete(self): - ''' + """ Test virt.volume_delete - ''' + """ mock_delete = MagicMock(side_effect=[0, 1]) mock_volume = MagicMock() mock_volume.delete = mock_delete # pylint: disable=no-member mock_pool = MagicMock() # pylint: disable=no-member mock_pool.storageVolLookupByName.side_effect = [ - mock_volume, - mock_volume, - self.mock_libvirt.libvirtError("Missing volume"), - mock_volume, + mock_volume, + mock_volume, + self.mock_libvirt.libvirtError("Missing volume"), + mock_volume, ] self.mock_conn.storagePoolLookupByName.side_effect = [ - mock_pool, - mock_pool, - mock_pool, - self.mock_libvirt.libvirtError("Missing pool"), + mock_pool, + mock_pool, + mock_pool, + self.mock_libvirt.libvirtError("Missing pool"), ] # pylint: enable=no-member - self.assertTrue(virt.volume_delete('default', 'test_volume')) - self.assertFalse(virt.volume_delete('default', 'test_volume')) + self.assertTrue(virt.volume_delete("default", "test_volume")) + self.assertFalse(virt.volume_delete("default", "test_volume")) with self.assertRaises(self.mock_libvirt.libvirtError): - virt.volume_delete('default', 'missing') - virt.volume_delete('missing', 'test_volume') + virt.volume_delete("default", "missing") + virt.volume_delete("missing", "test_volume") self.assertEqual(mock_delete.call_count, 2) def test_pool_capabilities(self): - ''' + """ Test virt.pool_capabilities where libvirt has the pool-capabilities feature - ''' - xml_caps = ''' + """ + xml_caps = """ <storagepoolCapabilities> <pool type='disk' supported='yes'> <poolOptions> @@ -3473,87 +4025,195 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): <pool type='sheepdog' supported='no'> </pool> </storagepoolCapabilities> - ''' + """ self.mock_conn.getStoragePoolCapabilities = MagicMock(return_value=xml_caps) actual = virt.pool_capabilities() - self.assertEqual({ - 'computed': False, - 'pool_types': [{ - 'name': 'disk', - 'supported': True, - 'options': { - 'pool': { - 'default_format': 'unknown', - 'sourceFormatType': ['unknown', 'dos', 'dvh'] + self.assertEqual( + { + "computed": False, + "pool_types": [ + { + "name": "disk", + "supported": True, + "options": { + "pool": { + "default_format": "unknown", + "sourceFormatType": ["unknown", "dos", "dvh"], + }, + "volume": { + "default_format": "none", + "targetFormatType": ["none", "linux"], + }, + }, }, - 'volume': { - 'default_format': 'none', - 'targetFormatType': ['none', 'linux'] - } - } + {"name": "iscsi", "supported": True}, + { + "name": "rbd", + "supported": True, + "options": { + "volume": {"default_format": "raw", "targetFormatType": []} + }, + }, + {"name": "sheepdog", "supported": False}, + ], }, - { - 'name': 'iscsi', - 'supported': True, - }, - { - 'name': 'rbd', - 'supported': True, - 'options': { - 'volume': { - 'default_format': 'raw', - 'targetFormatType': [] - } - } - }, - { - 'name': 'sheepdog', - 'supported': False, - }, - ]}, actual) + actual, + ) - @patch('salt.modules.virt.get_hypervisor', return_value='kvm') + @patch("salt.modules.virt.get_hypervisor", return_value="kvm") def test_pool_capabilities_computed(self, mock_get_hypervisor): - ''' + """ Test virt.pool_capabilities where libvirt doesn't have the pool-capabilities feature - ''' + """ self.mock_conn.getLibVersion = MagicMock(return_value=4006000) del self.mock_conn.getStoragePoolCapabilities actual = virt.pool_capabilities() - self.assertTrue(actual['computed']) - backends = actual['pool_types'] + self.assertTrue(actual["computed"]) + backends = actual["pool_types"] # libvirt version matching check - self.assertFalse([backend for backend in backends if backend['name'] == 'iscsi-direct'][0]['supported']) - self.assertTrue([backend for backend in backends if backend['name'] == 'gluster'][0]['supported']) - self.assertFalse([backend for backend in backends if backend['name'] == 'zfs'][0]['supported']) + self.assertFalse( + [backend for backend in backends if backend["name"] == "iscsi-direct"][0][ + "supported" + ] + ) + self.assertTrue( + [backend for backend in backends if backend["name"] == "gluster"][0][ + "supported" + ] + ) + self.assertFalse( + [backend for backend in backends if backend["name"] == "zfs"][0][ + "supported" + ] + ) # test case matching other hypervisors - mock_get_hypervisor.return_value = 'xen' - backends = virt.pool_capabilities()['pool_types'] - self.assertFalse([backend for backend in backends if backend['name'] == 'gluster'][0]['supported']) + mock_get_hypervisor.return_value = "xen" + backends = virt.pool_capabilities()["pool_types"] + self.assertFalse( + [backend for backend in backends if backend["name"] == "gluster"][0][ + "supported" + ] + ) - mock_get_hypervisor.return_value = 'bhyve' - backends = virt.pool_capabilities()['pool_types'] - self.assertFalse([backend for backend in backends if backend['name'] == 'gluster'][0]['supported']) - self.assertTrue([backend for backend in backends if backend['name'] == 'zfs'][0]['supported']) + mock_get_hypervisor.return_value = "bhyve" + backends = virt.pool_capabilities()["pool_types"] + self.assertFalse( + [backend for backend in backends if backend["name"] == "gluster"][0][ + "supported" + ] + ) + self.assertTrue( + [backend for backend in backends if backend["name"] == "zfs"][0][ + "supported" + ] + ) # Test options output - self.assertNotIn('options', [backend for backend in backends if backend['name'] == 'iscsi'][0]) - self.assertNotIn('pool', [backend for backend in backends if backend['name'] == 'dir'][0]['options']) - self.assertNotIn('volume', [backend for backend in backends if backend['name'] == 'logical'][0]['options']) - self.assertEqual({ - 'pool': { - 'default_format': 'auto', - 'sourceFormatType': ['auto', 'nfs', 'glusterfs', 'cifs'] + self.assertNotIn( + "options", + [backend for backend in backends if backend["name"] == "iscsi"][0], + ) + self.assertNotIn( + "pool", + [backend for backend in backends if backend["name"] == "dir"][0]["options"], + ) + self.assertNotIn( + "volume", + [backend for backend in backends if backend["name"] == "logical"][0][ + "options" + ], + ) + self.assertEqual( + { + "pool": { + "default_format": "auto", + "sourceFormatType": ["auto", "nfs", "glusterfs", "cifs"], + }, + "volume": { + "default_format": "raw", + "targetFormatType": [ + "none", + "raw", + "dir", + "bochs", + "cloop", + "dmg", + "iso", + "vpc", + "vdi", + "fat", + "vhd", + "ploop", + "cow", + "qcow", + "qcow2", + "qed", + "vmdk", + ], }, - 'volume': { - 'default_format': 'raw', - 'targetFormatType': ['none', 'raw', 'dir', 'bochs', 'cloop', 'dmg', 'iso', 'vpc', 'vdi', - 'fat', 'vhd', 'ploop', 'cow', 'qcow', 'qcow2', 'qed', 'vmdk'] - } }, - [backend for backend in backends if backend['name'] == 'netfs'][0]['options']) + [backend for backend in backends if backend["name"] == "netfs"][0][ + "options" + ], + ) + + def test_get_domain(self): + """ + Test the virt._get_domain function + """ + # Tests with no VM + self.mock_conn.listDomainsID.return_value = [] + self.mock_conn.listDefinedDomains.return_value = [] + self.assertEqual([], virt._get_domain(self.mock_conn)) + self.assertRaisesRegex( + CommandExecutionError, + "No virtual machines found.", + virt._get_domain, + self.mock_conn, + "vm2", + ) + + # Test with active and inactive VMs + self.mock_conn.listDomainsID.return_value = [1] + + def create_mock_vm(idx): + mock_vm = MagicMock() + mock_vm.name.return_value = "vm{0}".format(idx) + return mock_vm + + mock_vms = [create_mock_vm(idx) for idx in range(3)] + self.mock_conn.lookupByID.return_value = mock_vms[0] + self.mock_conn.listDefinedDomains.return_value = ["vm1", "vm2"] + + self.mock_conn.lookupByName.side_effect = mock_vms + self.assertEqual(mock_vms, virt._get_domain(self.mock_conn)) + + self.mock_conn.lookupByName.side_effect = None + self.mock_conn.lookupByName.return_value = mock_vms[0] + self.assertEqual(mock_vms[0], virt._get_domain(self.mock_conn, inactive=False)) + + self.mock_conn.lookupByName.return_value = None + self.mock_conn.lookupByName.side_effect = [mock_vms[1], mock_vms[2]] + self.assertEqual( + [mock_vms[1], mock_vms[2]], virt._get_domain(self.mock_conn, active=False) + ) + + self.mock_conn.reset_mock() + self.mock_conn.lookupByName.return_value = None + self.mock_conn.lookupByName.side_effect = [mock_vms[1], mock_vms[2]] + self.assertEqual( + [mock_vms[1], mock_vms[2]], virt._get_domain(self.mock_conn, "vm1", "vm2") + ) + self.assertRaisesRegex( + CommandExecutionError, + 'The VM "vm2" is not present', + virt._get_domain, + self.mock_conn, + "vm2", + inactive=False, + ) diff --git a/tests/unit/modules/test_virtualenv_mod.py b/tests/unit/modules/test_virtualenv_mod.py index 8f04ca231eb..01204267707 100644 --- a/tests/unit/modules/test_virtualenv_mod.py +++ b/tests/unit/modules/test_virtualenv_mod.py @@ -1,380 +1,371 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.unit.modules.virtualenv_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals -import sys -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.helpers import TstSuiteLoggingHandler, ForceImportErrorOn -from tests.support.mock import MagicMock, patch +import sys # Import salt libs import salt.modules.virtualenv_mod as virtualenv_mod from salt.exceptions import CommandExecutionError +from tests.support.helpers import ForceImportErrorOn, TstSuiteLoggingHandler + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class VirtualenvTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): base_virtualenv_mock = MagicMock() - base_virtualenv_mock.__version__ = '1.9.1' - patcher = patch('salt.utils.path.which', lambda exe: exe) + base_virtualenv_mock.__version__ = "1.9.1" + patcher = patch("salt.utils.path.which", lambda exe: exe) patcher.start() self.addCleanup(patcher.stop) return { virtualenv_mod: { - '__opts__': {'venv_bin': 'virtualenv'}, - '_install_script': MagicMock(return_value={'retcode': 0, - 'stdout': 'Installed script!', - 'stderr': ''}), - 'sys.modules': {'virtualenv': base_virtualenv_mock} + "__opts__": {"venv_bin": "virtualenv"}, + "_install_script": MagicMock( + return_value={ + "retcode": 0, + "stdout": "Installed script!", + "stderr": "", + } + ), + "sys.modules": {"virtualenv": base_virtualenv_mock}, } } def test_issue_6029_deprecated_distribute(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): virtualenv_mod.create( - '/tmp/foo', system_site_packages=True, distribute=True + "/tmp/foo", system_site_packages=True, distribute=True ) mock.assert_called_once_with( - ['virtualenv', '--distribute', '--system-site-packages', '/tmp/foo'], + ["virtualenv", "--distribute", "--system-site-packages", "/tmp/foo"], runas=None, - python_shell=False + python_shell=False, ) with TstSuiteLoggingHandler() as handler: # Let's fake a higher virtualenv version virtualenv_mock = MagicMock() - virtualenv_mock.__version__ = '1.10rc1' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - with patch.dict('sys.modules', {'virtualenv': virtualenv_mock}): + virtualenv_mock.__version__ = "1.10rc1" + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + with patch.dict("sys.modules", {"virtualenv": virtualenv_mock}): virtualenv_mod.create( - '/tmp/foo', system_site_packages=True, distribute=True + "/tmp/foo", system_site_packages=True, distribute=True ) mock.assert_called_once_with( - ['virtualenv', '--system-site-packages', '/tmp/foo'], + ["virtualenv", "--system-site-packages", "/tmp/foo"], runas=None, - python_shell=False + python_shell=False, ) # Are we logging the deprecation information? self.assertIn( - 'INFO:The virtualenv \'--distribute\' option has been ' - 'deprecated in virtualenv(>=1.10), as such, the ' - '\'distribute\' option to `virtualenv.create()` has ' - 'also been deprecated and it\'s not necessary anymore.', - handler.messages + "INFO:The virtualenv '--distribute' option has been " + "deprecated in virtualenv(>=1.10), as such, the " + "'distribute' option to `virtualenv.create()` has " + "also been deprecated and it's not necessary anymore.", + handler.messages, ) def test_issue_6030_deprecated_never_download(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create( - '/tmp/foo', never_download=True - ) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", never_download=True) mock.assert_called_once_with( - ['virtualenv', '--never-download', '/tmp/foo'], + ["virtualenv", "--never-download", "/tmp/foo"], runas=None, - python_shell=False + python_shell=False, ) with TstSuiteLoggingHandler() as handler: - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) # Let's fake a higher virtualenv version virtualenv_mock = MagicMock() - virtualenv_mock.__version__ = '1.10rc1' - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - with patch.dict('sys.modules', - {'virtualenv': virtualenv_mock}): - virtualenv_mod.create( - '/tmp/foo', never_download=True + virtualenv_mock.__version__ = "1.10rc1" + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + with patch.dict("sys.modules", {"virtualenv": virtualenv_mock}): + virtualenv_mod.create("/tmp/foo", never_download=True) + mock.assert_called_once_with( + ["virtualenv", "/tmp/foo"], runas=None, python_shell=False ) - mock.assert_called_once_with(['virtualenv', '/tmp/foo'], - runas=None, - python_shell=False) # Are we logging the deprecation information? self.assertIn( - 'INFO:--never-download was deprecated in 1.10.0, ' - 'but reimplemented in 14.0.0. If this feature is needed, ' - 'please install a supported virtualenv version.', - handler.messages + "INFO:--never-download was deprecated in 1.10.0, " + "but reimplemented in 14.0.0. If this feature is needed, " + "please install a supported virtualenv version.", + handler.messages, ) def test_issue_6031_multiple_extra_search_dirs(self): - extra_search_dirs = [ - '/tmp/bar-1', - '/tmp/bar-2', - '/tmp/bar-3' - ] + extra_search_dirs = ["/tmp/bar-1", "/tmp/bar-2", "/tmp/bar-3"] # Passing extra_search_dirs as a list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create( - '/tmp/foo', extra_search_dir=extra_search_dirs - ) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", extra_search_dir=extra_search_dirs) mock.assert_called_once_with( - ['virtualenv', - '--extra-search-dir=/tmp/bar-1', - '--extra-search-dir=/tmp/bar-2', - '--extra-search-dir=/tmp/bar-3', - '/tmp/foo'], + [ + "virtualenv", + "--extra-search-dir=/tmp/bar-1", + "--extra-search-dir=/tmp/bar-2", + "--extra-search-dir=/tmp/bar-3", + "/tmp/foo", + ], runas=None, - python_shell=False + python_shell=False, ) # Passing extra_search_dirs as comma separated list - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): virtualenv_mod.create( - '/tmp/foo', extra_search_dir=','.join(extra_search_dirs) + "/tmp/foo", extra_search_dir=",".join(extra_search_dirs) ) mock.assert_called_once_with( - ['virtualenv', - '--extra-search-dir=/tmp/bar-1', - '--extra-search-dir=/tmp/bar-2', - '--extra-search-dir=/tmp/bar-3', - '/tmp/foo'], + [ + "virtualenv", + "--extra-search-dir=/tmp/bar-1", + "--extra-search-dir=/tmp/bar-2", + "--extra-search-dir=/tmp/bar-3", + "/tmp/foo", + ], runas=None, - python_shell=False + python_shell=False, ) def test_unapplicable_options(self): # ----- Virtualenv using pyvenv options -----------------------------> - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, virtualenv_mod.create, - '/tmp/foo', - venv_bin='virtualenv', - upgrade=True + "/tmp/foo", + venv_bin="virtualenv", + upgrade=True, ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, virtualenv_mod.create, - '/tmp/foo', - venv_bin='virtualenv', - symlinks=True + "/tmp/foo", + venv_bin="virtualenv", + symlinks=True, ) # <---- Virtualenv using pyvenv options ------------------------------ # ----- pyvenv using virtualenv options -----------------------------> - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock, - 'cmd.which_bin': lambda _: 'pyvenv'}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + virtualenv_mod.__salt__, + {"cmd.run_all": mock, "cmd.which_bin": lambda _: "pyvenv"}, + ): self.assertRaises( CommandExecutionError, virtualenv_mod.create, - '/tmp/foo', - venv_bin='pyvenv', - python='python2.7' + "/tmp/foo", + venv_bin="pyvenv", + python="python2.7", ) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, virtualenv_mod.create, - '/tmp/foo', - venv_bin='pyvenv', - prompt='PY Prompt' + "/tmp/foo", + venv_bin="pyvenv", + prompt="PY Prompt", ) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, virtualenv_mod.create, - '/tmp/foo', - venv_bin='pyvenv', - never_download=True + "/tmp/foo", + venv_bin="pyvenv", + never_download=True, ) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, virtualenv_mod.create, - '/tmp/foo', - venv_bin='pyvenv', - extra_search_dir='/tmp/bar' + "/tmp/foo", + venv_bin="pyvenv", + extra_search_dir="/tmp/bar", ) # <---- pyvenv using virtualenv options ------------------------------ def test_get_virtualenv_version_from_shell(self): - with ForceImportErrorOn('virtualenv'): + with ForceImportErrorOn("virtualenv"): # ----- virtualenv binary not available -------------------------> - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): self.assertRaises( - CommandExecutionError, - virtualenv_mod.create, - '/tmp/foo', + CommandExecutionError, virtualenv_mod.create, "/tmp/foo", ) # <---- virtualenv binary not available -------------------------- # ----- virtualenv binary present but > 0 exit code -------------> - mock = MagicMock(side_effect=[ - {'retcode': 1, 'stdout': '', 'stderr': 'This is an error'}, - {'retcode': 0, 'stdout': ''} - ]) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + mock = MagicMock( + side_effect=[ + {"retcode": 1, "stdout": "", "stderr": "This is an error"}, + {"retcode": 0, "stdout": ""}, + ] + ) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): self.assertRaises( CommandExecutionError, virtualenv_mod.create, - '/tmp/foo', - venv_bin='virtualenv', + "/tmp/foo", + venv_bin="virtualenv", ) # <---- virtualenv binary present but > 0 exit code -------------- # ----- virtualenv binary returns 1.9.1 as its version ---------> - mock = MagicMock(side_effect=[ - {'retcode': 0, 'stdout': '1.9.1'}, - {'retcode': 0, 'stdout': ''} - ]) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create( - '/tmp/foo', never_download=True - ) + mock = MagicMock( + side_effect=[ + {"retcode": 0, "stdout": "1.9.1"}, + {"retcode": 0, "stdout": ""}, + ] + ) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", never_download=True) mock.assert_called_with( - ['virtualenv', '--never-download', '/tmp/foo'], + ["virtualenv", "--never-download", "/tmp/foo"], runas=None, - python_shell=False + python_shell=False, ) # <---- virtualenv binary returns 1.9.1 as its version ---------- # ----- virtualenv binary returns 1.10rc1 as its version -------> - mock = MagicMock(side_effect=[ - {'retcode': 0, 'stdout': '1.10rc1'}, - {'retcode': 0, 'stdout': ''} - ]) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create( - '/tmp/foo', never_download=True - ) + mock = MagicMock( + side_effect=[ + {"retcode": 0, "stdout": "1.10rc1"}, + {"retcode": 0, "stdout": ""}, + ] + ) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", never_download=True) mock.assert_called_with( - ['virtualenv', '/tmp/foo'], - runas=None, - python_shell=False + ["virtualenv", "/tmp/foo"], runas=None, python_shell=False ) # <---- virtualenv binary returns 1.10rc1 as its version -------- def test_python_argument(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): virtualenv_mod.create( - '/tmp/foo', python=sys.executable, + "/tmp/foo", python=sys.executable, ) mock.assert_called_once_with( - ['virtualenv', '--python={0}'.format(sys.executable), '/tmp/foo'], + ["virtualenv", "--python={0}".format(sys.executable), "/tmp/foo"], runas=None, - python_shell=False + python_shell=False, ) def test_prompt_argument(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create('/tmp/foo', prompt='PY Prompt') + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", prompt="PY Prompt") mock.assert_called_once_with( - ['virtualenv', '--prompt=\'PY Prompt\'', '/tmp/foo'], + ["virtualenv", "--prompt='PY Prompt'", "/tmp/foo"], runas=None, - python_shell=False + python_shell=False, ) # Now with some quotes on the mix - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create('/tmp/foo', prompt='\'PY\' Prompt') + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", prompt="'PY' Prompt") mock.assert_called_once_with( - ['virtualenv', "--prompt=''PY' Prompt'", '/tmp/foo'], + ["virtualenv", "--prompt=''PY' Prompt'", "/tmp/foo"], runas=None, - python_shell=False + python_shell=False, ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create('/tmp/foo', prompt='"PY" Prompt') + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", prompt='"PY" Prompt') mock.assert_called_once_with( - ['virtualenv', '--prompt=\'"PY" Prompt\'', '/tmp/foo'], + ["virtualenv", "--prompt='\"PY\" Prompt'", "/tmp/foo"], runas=None, - python_shell=False + python_shell=False, ) def test_clear_argument(self): - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create('/tmp/foo', clear=True) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", clear=True) mock.assert_called_once_with( - ['virtualenv', '--clear', '/tmp/foo'], - runas=None, - python_shell=False + ["virtualenv", "--clear", "/tmp/foo"], runas=None, python_shell=False ) def test_upgrade_argument(self): # We test for pyvenv only because with virtualenv this is un # unsupported option. - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create('/tmp/foo', venv_bin='pyvenv', upgrade=True) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", venv_bin="pyvenv", upgrade=True) mock.assert_called_once_with( - ['pyvenv', '--upgrade', '/tmp/foo'], - runas=None, - python_shell=False + ["pyvenv", "--upgrade", "/tmp/foo"], runas=None, python_shell=False ) def test_symlinks_argument(self): # We test for pyvenv only because with virtualenv this is un # unsupported option. - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock}): - virtualenv_mod.create('/tmp/foo', venv_bin='pyvenv', symlinks=True) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock}): + virtualenv_mod.create("/tmp/foo", venv_bin="pyvenv", symlinks=True) mock.assert_called_once_with( - ['pyvenv', '--symlinks', '/tmp/foo'], - runas=None, - python_shell=False + ["pyvenv", "--symlinks", "/tmp/foo"], runas=None, python_shell=False ) def test_virtualenv_ver(self): - ''' + """ test virtualenv_ver when there is no ImportError - ''' - ret = virtualenv_mod.virtualenv_ver(venv_bin='pyvenv') + """ + ret = virtualenv_mod.virtualenv_ver(venv_bin="pyvenv") assert ret == (1, 9, 1) def test_virtualenv_ver_importerror(self): - ''' + """ test virtualenv_ver when there is an ImportError - ''' - with ForceImportErrorOn('virtualenv'): - mock_ver = MagicMock(return_value={'retcode': 0, 'stdout': '1.9.1'}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock_ver}): - ret = virtualenv_mod.virtualenv_ver(venv_bin='pyenv') + """ + with ForceImportErrorOn("virtualenv"): + mock_ver = MagicMock(return_value={"retcode": 0, "stdout": "1.9.1"}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock_ver}): + ret = virtualenv_mod.virtualenv_ver(venv_bin="pyenv") assert ret == (1, 9, 1) def test_virtualenv_ver_importerror_cmd_error(self): - ''' + """ test virtualenv_ver when there is an ImportError and virtualenv --version does not return anything - ''' - with ForceImportErrorOn('virtualenv'): - mock_ver = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(virtualenv_mod.__salt__, {'cmd.run_all': mock_ver}): + """ + with ForceImportErrorOn("virtualenv"): + mock_ver = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict(virtualenv_mod.__salt__, {"cmd.run_all": mock_ver}): with self.assertRaises(CommandExecutionError): - virtualenv_mod.virtualenv_ver(venv_bin='pyenv') + virtualenv_mod.virtualenv_ver(venv_bin="pyenv") diff --git a/tests/unit/modules/test_vmctl.py b/tests/unit/modules/test_vmctl.py index 6e54b160c2c..37b4e713726 100644 --- a/tests/unit/modules/test_vmctl.py +++ b/tests/unit/modules/test_vmctl.py @@ -8,230 +8,252 @@ import salt.modules.vmctl as vmctl # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class VmctlTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ test modules.vmctl functions - ''' + """ + def setup_loader_modules(self): return {vmctl: {}} def test_create_disk(self): - ''' + """ Tests creating a new disk image. - ''' + """ ret = {} - ret['stdout'] = 'vmctl: imagefile created' - ret['stderr'] = '' - ret['retcode'] = 0 + ret["stdout"] = "vmctl: imagefile created" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertTrue(vmctl.create_disk('/path/to/disk.img', '1G')) + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertTrue(vmctl.create_disk("/path/to/disk.img", "1G")) def test_load(self): - ''' + """ Tests loading a configuration file. - ''' + """ ret = {} - ret['retcode'] = 0 + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertTrue(vmctl.load('/etc/vm.switches.conf')) + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertTrue(vmctl.load("/etc/vm.switches.conf")) def test_reload(self): - ''' + """ Tests reloading the configuration. - ''' + """ ret = {} - ret['retcode'] = 0 + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): self.assertTrue(vmctl.reload()) def test_reset(self): - ''' + """ Tests resetting VMM. - ''' + """ ret = {} - ret['retcode'] = 0 + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): res = vmctl.reset() - mock_cmd.assert_called_once_with(['vmctl', 'reset'], - output_loglevel='trace', python_shell=False) + mock_cmd.assert_called_once_with( + ["vmctl", "reset"], output_loglevel="trace", python_shell=False + ) self.assertTrue(res) def test_reset_vms(self): - ''' + """ Tests resetting VMs. - ''' + """ ret = {} - ret['retcode'] = 0 + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): res = vmctl.reset(vms=True) - mock_cmd.assert_called_once_with(['vmctl', 'reset', 'vms'], - output_loglevel='trace', python_shell=False) + mock_cmd.assert_called_once_with( + ["vmctl", "reset", "vms"], output_loglevel="trace", python_shell=False + ) self.assertTrue(res) def test_reset_switches(self): - ''' + """ Tests resetting switches. - ''' + """ ret = {} - ret['retcode'] = 0 + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): res = vmctl.reset(switches=True) - mock_cmd.assert_called_once_with(['vmctl', 'reset', 'switches'], - output_loglevel='trace', python_shell=False) + mock_cmd.assert_called_once_with( + ["vmctl", "reset", "switches"], + output_loglevel="trace", + python_shell=False, + ) self.assertTrue(res) def test_reset_all(self): - ''' + """ Tests resetting all. - ''' + """ ret = {} - ret['retcode'] = 0 + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): res = vmctl.reset(all=True) - mock_cmd.assert_called_once_with(['vmctl', 'reset', 'all'], - output_loglevel='trace', python_shell=False) + mock_cmd.assert_called_once_with( + ["vmctl", "reset", "all"], output_loglevel="trace", python_shell=False + ) self.assertTrue(res) def test_start_existing_vm(self): - ''' + """ Tests starting a VM that is already defined. - ''' + """ ret = {} - ret['stderr'] = 'vmctl: started vm 4 successfully, tty /dev/ttyp4' - ret['retcode'] = 0 + ret["stderr"] = "vmctl: started vm 4 successfully, tty /dev/ttyp4" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - expected = {'changes': True, 'console': '/dev/ttyp4'} - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertDictEqual(expected, vmctl.start('4')) + expected = {"changes": True, "console": "/dev/ttyp4"} + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertDictEqual(expected, vmctl.start("4")) def test_start_new_vm(self): - ''' + """ Tests starting a new VM. - ''' + """ ret = {} - ret['stderr'] = 'vmctl: started vm 4 successfully, tty /dev/ttyp4' - ret['retcode'] = 0 + ret["stderr"] = "vmctl: started vm 4 successfully, tty /dev/ttyp4" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) mock_status = MagicMock(return_value={}) - expected = {'changes': True, 'console': '/dev/ttyp4'} - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): - with patch('salt.modules.vmctl.status', mock_status): - res = vmctl.start('web1', bootpath='/bsd.rd', nics=2, disk='/disk.img') - mock_cmd.assert_called_once_with(['vmctl', 'start', 'web1', '-i 2', '-b', '/bsd.rd', '-d', '/disk.img'], - output_loglevel='trace', python_shell=False) + expected = {"changes": True, "console": "/dev/ttyp4"} + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): + with patch("salt.modules.vmctl.status", mock_status): + res = vmctl.start("web1", bootpath="/bsd.rd", nics=2, disk="/disk.img") + mock_cmd.assert_called_once_with( + [ + "vmctl", + "start", + "web1", + "-i 2", + "-b", + "/bsd.rd", + "-d", + "/disk.img", + ], + output_loglevel="trace", + python_shell=False, + ) self.assertDictEqual(expected, res) def test_status(self): - ''' + """ Tests getting status for all VMs. - ''' + """ ret = {} - ret['stdout'] = ' ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME\n' \ - ' 1 123 1 2.9G 150M ttyp5 john web1 - stopping\n' \ - ' 2 456 1 512M 301M ttyp4 paul web2\n' \ - ' 3 - 1 512M - - george web3\n' - ret['retcode'] = 0 + ret["stdout"] = ( + " ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME\n" + " 1 123 1 2.9G 150M ttyp5 john web1 - stopping\n" + " 2 456 1 512M 301M ttyp4 paul web2\n" + " 3 - 1 512M - - george web3\n" + ) + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) expected = { - 'web1': { - 'curmem': '150M', - 'id': '1', - 'maxmem': '2.9G', - 'owner': 'john', - 'pid': '123', - 'state': 'stopping', - 'tty': 'ttyp5', - 'vcpus': '1' + "web1": { + "curmem": "150M", + "id": "1", + "maxmem": "2.9G", + "owner": "john", + "pid": "123", + "state": "stopping", + "tty": "ttyp5", + "vcpus": "1", }, - 'web2': { - 'curmem': '301M', - 'id': '2', - 'maxmem': '512M', - 'owner': 'paul', - 'pid': '456', - 'state': 'running', - 'tty': 'ttyp4', - 'vcpus': '1' + "web2": { + "curmem": "301M", + "id": "2", + "maxmem": "512M", + "owner": "paul", + "pid": "456", + "state": "running", + "tty": "ttyp4", + "vcpus": "1", }, - 'web3': { - 'curmem': '-', - 'id': '3', - 'maxmem': '512M', - 'owner': 'george', - 'pid': '-', - 'state': 'stopped', - 'tty': '-', - 'vcpus': '1' + "web3": { + "curmem": "-", + "id": "3", + "maxmem": "512M", + "owner": "george", + "pid": "-", + "state": "stopped", + "tty": "-", + "vcpus": "1", }, } - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): self.assertEqual(expected, vmctl.status()) def test_status_single(self): - ''' + """ Tests getting status for a single VM. - ''' + """ ret = {} - ret['stdout'] = ' ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME\n' \ - ' 1 123 1 2.9G 150M ttyp5 ringo web4\n' \ - ' 2 - 1 512M - - george web3\n' - ret['retcode'] = 0 + ret["stdout"] = ( + " ID PID VCPUS MAXMEM CURMEM TTY OWNER NAME\n" + " 1 123 1 2.9G 150M ttyp5 ringo web4\n" + " 2 - 1 512M - - george web3\n" + ) + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) expected = { - 'web4': { - 'curmem': '150M', - 'id': '1', - 'maxmem': '2.9G', - 'owner': 'ringo', - 'pid': '123', - 'state': 'running', - 'tty': 'ttyp5', - 'vcpus': '1' + "web4": { + "curmem": "150M", + "id": "1", + "maxmem": "2.9G", + "owner": "ringo", + "pid": "123", + "state": "running", + "tty": "ttyp5", + "vcpus": "1", }, } - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): - self.assertEqual(expected, vmctl.status('web4')) + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): + self.assertEqual(expected, vmctl.status("web4")) def test_stop_when_running(self): - ''' + """ Tests stopping a VM that is running. - ''' + """ ret = {} - ret['stdout'] = '' - ret['stderr'] = 'vmctl: sent request to terminate vm 14' - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "vmctl: sent request to terminate vm 14" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): - res = vmctl.stop('web1') - mock_cmd.assert_called_once_with(['vmctl', 'stop', 'web1'], - output_loglevel='trace', python_shell=False) - self.assertTrue(res['changes']) + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): + res = vmctl.stop("web1") + mock_cmd.assert_called_once_with( + ["vmctl", "stop", "web1"], output_loglevel="trace", python_shell=False + ) + self.assertTrue(res["changes"]) def test_stop_when_stopped(self): - ''' + """ Tests stopping a VM that is already stopped/stopping. - ''' + """ ret = {} - ret['stdout'] = '' - ret['stderr'] = 'vmctl: terminate vm command failed: Invalid argument' - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "vmctl: terminate vm command failed: Invalid argument" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(vmctl.__salt__, {'cmd.run_all': mock_cmd}): - res = vmctl.stop('web1') - mock_cmd.assert_called_once_with(['vmctl', 'stop', 'web1'], - output_loglevel='trace', python_shell=False) - self.assertFalse(res['changes']) + with patch.dict(vmctl.__salt__, {"cmd.run_all": mock_cmd}): + res = vmctl.stop("web1") + mock_cmd.assert_called_once_with( + ["vmctl", "stop", "web1"], output_loglevel="trace", python_shell=False + ) + self.assertFalse(res["changes"]) diff --git a/tests/unit/modules/test_vsphere.py b/tests/unit/modules/test_vsphere.py index 2e65241803a..ee20651f162 100644 --- a/tests/unit/modules/test_vsphere.py +++ b/tests/unit/modules/test_vsphere.py @@ -1,33 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> :codeauthor: Alexandru Bleotu <alexandru.bleotu@morganstanley.com> Tests for functions in salt.modules.vsphere -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import salt.modules.vsphere as vsphere +import salt.utils.args +import salt.utils.vmware +from salt.exceptions import ( + ArgumentValueError, + CommandExecutionError, + VMwareObjectRetrievalError, + VMwareSaltError, +) + # Import Salt Libs from salt.ext.six import text_type -import salt.modules.vsphere as vsphere -from salt.exceptions import ( - CommandExecutionError, - VMwareSaltError, - ArgumentValueError, - VMwareObjectRetrievalError -) # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, call, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - Mock, - MagicMock, - patch, - call -) # Import Third Party Libs try: @@ -40,732 +38,1097 @@ except ImportError: try: # pylint: disable=unused-import from com.vmware.vapi.std_client import DynamicID + HAS_VSPHERE_SDK = True except ImportError: HAS_VSPHERE_SDK = False -import salt.utils.args -import salt.utils.vmware # Globals -HOST = '1.2.3.4' -USER = 'root' -PASSWORD = 'SuperSecret!' -ERROR = 'Some Testing Error Message' +HOST = "1.2.3.4" +USER = "root" +PASSWORD = "SuperSecret!" +ERROR = "Some Testing Error Message" class VsphereTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Unit TestCase for the salt.modules.vsphere module. - ''' + """ def setup_loader_modules(self): - return {vsphere: {'__virtual__': MagicMock(return_value='vsphere')}} + return {vsphere: {"__virtual__": MagicMock(return_value="vsphere")}} # Tests for get_coredump_network_config function def test_get_coredump_network_config_esxi_hosts_not_list(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list. - ''' - self.assertRaises(CommandExecutionError, - vsphere.get_coredump_network_config, - HOST, USER, PASSWORD, esxi_hosts='foo') + """ + self.assertRaises( + CommandExecutionError, + vsphere.get_coredump_network_config, + HOST, + USER, + PASSWORD, + esxi_hosts="foo", + ) def test_get_coredump_network_config_host_list_bad_retcode(self): - ''' + """ Tests error message returned with list of esxi_hosts. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'Error': ERROR}}, - vsphere.get_coredump_network_config(HOST, USER, PASSWORD, esxi_hosts=[host_1])) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"Error": ERROR}}, + vsphere.get_coredump_network_config( + HOST, USER, PASSWORD, esxi_hosts=[host_1] + ), + ) def test_get_coredump_network_config_host_list_success(self): - ''' + """ Tests successful function return when an esxi_host is provided. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - with patch('salt.modules.vsphere._format_coredump_stdout', MagicMock(return_value={})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'Coredump Config': {}}}, - vsphere.get_coredump_network_config(HOST, USER, PASSWORD, esxi_hosts=[host_1])) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + with patch( + "salt.modules.vsphere._format_coredump_stdout", + MagicMock(return_value={}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"Coredump Config": {}}}, + vsphere.get_coredump_network_config( + HOST, USER, PASSWORD, esxi_hosts=[host_1] + ), + ) def test_get_coredump_network_config_bad_retcode(self): - ''' + """ Tests error message given for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - self.assertEqual({HOST: {'Error': ERROR}}, - vsphere.get_coredump_network_config(HOST, USER, PASSWORD)) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + self.assertEqual( + {HOST: {"Error": ERROR}}, + vsphere.get_coredump_network_config(HOST, USER, PASSWORD), + ) def test_get_coredump_network_config_success(self): - ''' + """ Tests successful function return for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - with patch('salt.modules.vsphere._format_coredump_stdout', MagicMock(return_value={})): - self.assertEqual({HOST: {'Coredump Config': {}}}, - vsphere.get_coredump_network_config(HOST, USER, PASSWORD)) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + with patch( + "salt.modules.vsphere._format_coredump_stdout", + MagicMock(return_value={}), + ): + self.assertEqual( + {HOST: {"Coredump Config": {}}}, + vsphere.get_coredump_network_config(HOST, USER, PASSWORD), + ) # Tests for coredump_network_enable function def test_coredump_network_enable_esxi_hosts_not_list(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list. - ''' - self.assertRaises(CommandExecutionError, - vsphere.coredump_network_enable, - HOST, USER, PASSWORD, True, esxi_hosts='foo') + """ + self.assertRaises( + CommandExecutionError, + vsphere.coredump_network_enable, + HOST, + USER, + PASSWORD, + True, + esxi_hosts="foo", + ) def test_coredump_network_enable_host_list_bad_retcode(self): - ''' + """ Tests error message returned with list of esxi_hosts. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'Error': ERROR}}, - vsphere.coredump_network_enable(HOST, USER, PASSWORD, True, esxi_hosts=[host_1])) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"Error": ERROR}}, + vsphere.coredump_network_enable( + HOST, USER, PASSWORD, True, esxi_hosts=[host_1] + ), + ) def test_coredump_network_enable_host_list_success(self): - ''' + """ Tests successful function return when an esxi_host is provided. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - with patch('salt.modules.vsphere._format_coredump_stdout', MagicMock(return_value={})): + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + with patch( + "salt.modules.vsphere._format_coredump_stdout", + MagicMock(return_value={}), + ): enabled = True - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'Coredump Enabled': enabled}}, - vsphere.coredump_network_enable(HOST, USER, PASSWORD, enabled, esxi_hosts=[host_1])) + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"Coredump Enabled": enabled}}, + vsphere.coredump_network_enable( + HOST, USER, PASSWORD, enabled, esxi_hosts=[host_1] + ), + ) def test_coredump_network_enable_bad_retcode(self): - ''' + """ Tests error message given for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - self.assertEqual({HOST: {'Error': ERROR}}, - vsphere.coredump_network_enable(HOST, USER, PASSWORD, True)) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + self.assertEqual( + {HOST: {"Error": ERROR}}, + vsphere.coredump_network_enable(HOST, USER, PASSWORD, True), + ) def test_coredump_network_enable_success(self): - ''' + """ Tests successful function return for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - with patch('salt.modules.vsphere._format_coredump_stdout', MagicMock(return_value={})): + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + with patch( + "salt.modules.vsphere._format_coredump_stdout", + MagicMock(return_value={}), + ): enabled = True - self.assertEqual({HOST: {'Coredump Enabled': enabled}}, - vsphere.coredump_network_enable(HOST, USER, PASSWORD, enabled)) + self.assertEqual( + {HOST: {"Coredump Enabled": enabled}}, + vsphere.coredump_network_enable(HOST, USER, PASSWORD, enabled), + ) # Tests for set_coredump_network_config function def test_set_coredump_network_config_esxi_hosts_not_list(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list. - ''' - self.assertRaises(CommandExecutionError, - vsphere.set_coredump_network_config, - HOST, USER, PASSWORD, 'loghost', 'foo', esxi_hosts='bar') + """ + self.assertRaises( + CommandExecutionError, + vsphere.set_coredump_network_config, + HOST, + USER, + PASSWORD, + "loghost", + "foo", + esxi_hosts="bar", + ) def test_set_coredump_network_config_host_list_bad_retcode(self): - ''' + """ Tests error message returned with list of esxi_hosts. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'retcode': 1, 'success': False}}, - vsphere.set_coredump_network_config(HOST, - USER, - PASSWORD, - 'dump-ip.test.com', - esxi_hosts=[host_1])) + """ + with patch("salt.utils.vmware.esxcli", MagicMock(return_value={"retcode": 1})): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"retcode": 1, "success": False}}, + vsphere.set_coredump_network_config( + HOST, USER, PASSWORD, "dump-ip.test.com", esxi_hosts=[host_1] + ), + ) def test_set_coredump_network_config_host_list_success(self): - ''' + """ Tests successful function return when an esxi_host is provided. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'retcode': 0, 'success': True}}, - vsphere.set_coredump_network_config(HOST, - USER, - PASSWORD, - 'dump-ip.test.com', - esxi_hosts=[host_1])) + """ + with patch("salt.utils.vmware.esxcli", MagicMock(return_value={"retcode": 0})): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"retcode": 0, "success": True}}, + vsphere.set_coredump_network_config( + HOST, USER, PASSWORD, "dump-ip.test.com", esxi_hosts=[host_1] + ), + ) def test_set_coredump_network_config_bad_retcode(self): - ''' + """ Tests error message given for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1})): - self.assertEqual({HOST: {'retcode': 1, 'success': False}}, - vsphere.set_coredump_network_config(HOST, - USER, - PASSWORD, - 'dump-ip.test.com')) + """ + with patch("salt.utils.vmware.esxcli", MagicMock(return_value={"retcode": 1})): + self.assertEqual( + {HOST: {"retcode": 1, "success": False}}, + vsphere.set_coredump_network_config( + HOST, USER, PASSWORD, "dump-ip.test.com" + ), + ) def test_set_coredump_network_config_success(self): - ''' + """ Tests successful function return for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0})): - self.assertEqual({HOST: {'retcode': 0, 'success': True}}, - vsphere.set_coredump_network_config(HOST, - USER, - PASSWORD, - 'dump-ip.test.com')) + """ + with patch("salt.utils.vmware.esxcli", MagicMock(return_value={"retcode": 0})): + self.assertEqual( + {HOST: {"retcode": 0, "success": True}}, + vsphere.set_coredump_network_config( + HOST, USER, PASSWORD, "dump-ip.test.com" + ), + ) # Tests for get_firewall_status function def test_get_firewall_status_esxi_hosts_not_list(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list. - ''' - self.assertRaises(CommandExecutionError, - vsphere.get_firewall_status, - HOST, USER, PASSWORD, esxi_hosts='foo') + """ + self.assertRaises( + CommandExecutionError, + vsphere.get_firewall_status, + HOST, + USER, + PASSWORD, + esxi_hosts="foo", + ) def test_get_firewall_status_host_list_bad_retcode(self): - ''' + """ Tests error message returned with list of esxi_hosts. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'success': False, 'Error': ERROR, 'rulesets': None}}, - vsphere.get_firewall_status(HOST, USER, PASSWORD, esxi_hosts=[host_1])) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"success": False, "Error": ERROR, "rulesets": None}}, + vsphere.get_firewall_status(HOST, USER, PASSWORD, esxi_hosts=[host_1]), + ) def test_get_firewall_status_host_list_success(self): - ''' + """ Tests successful function return when an esxi_host is provided. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'rulesets': {}, 'success': True}}, - vsphere.get_firewall_status(HOST, USER, PASSWORD, esxi_hosts=[host_1])) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"rulesets": {}, "success": True}}, + vsphere.get_firewall_status(HOST, USER, PASSWORD, esxi_hosts=[host_1]), + ) def test_get_firewall_status_bad_retcode(self): - ''' + """ Tests error message given for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - self.assertEqual({HOST: {'success': False, 'Error': ERROR, 'rulesets': None}}, - vsphere.get_firewall_status(HOST, USER, PASSWORD)) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + self.assertEqual( + {HOST: {"success": False, "Error": ERROR, "rulesets": None}}, + vsphere.get_firewall_status(HOST, USER, PASSWORD), + ) def test_get_firewall_status_success(self): - ''' + """ Tests successful function return for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - self.assertEqual({HOST: {'rulesets': {}, 'success': True}}, - vsphere.get_firewall_status(HOST, USER, PASSWORD)) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + self.assertEqual( + {HOST: {"rulesets": {}, "success": True}}, + vsphere.get_firewall_status(HOST, USER, PASSWORD), + ) # Tests for enable_firewall_ruleset function def test_enable_firewall_ruleset_esxi_hosts_not_list(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list. - ''' - self.assertRaises(CommandExecutionError, - vsphere.enable_firewall_ruleset, - HOST, USER, PASSWORD, 'foo', 'bar', esxi_hosts='baz') + """ + self.assertRaises( + CommandExecutionError, + vsphere.enable_firewall_ruleset, + HOST, + USER, + PASSWORD, + "foo", + "bar", + esxi_hosts="baz", + ) # Tests for syslog_service_reload function def test_syslog_service_reload_esxi_hosts_not_list(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list. - ''' - self.assertRaises(CommandExecutionError, - vsphere.syslog_service_reload, - HOST, USER, PASSWORD, esxi_hosts='foo') + """ + self.assertRaises( + CommandExecutionError, + vsphere.syslog_service_reload, + HOST, + USER, + PASSWORD, + esxi_hosts="foo", + ) # Tests for set_syslog_config function. # These tests only test the firewall=True and syslog_config == 'loghost' if block. # The rest of the function is tested in the _set_syslog_config_helper tests below. def test_set_syslog_config_esxi_hosts_not_list(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list, but we don't enter the 'loghost'/firewall loop. - ''' - self.assertRaises(CommandExecutionError, - vsphere.set_syslog_config, - HOST, USER, PASSWORD, 'foo', 'bar', esxi_hosts='baz') + """ + self.assertRaises( + CommandExecutionError, + vsphere.set_syslog_config, + HOST, + USER, + PASSWORD, + "foo", + "bar", + esxi_hosts="baz", + ) def test_set_syslog_config_esxi_hosts_not_list_firewall(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list, and we enter the 'loghost'/firewall loop. - ''' - self.assertRaises(CommandExecutionError, - vsphere.set_syslog_config, - HOST, USER, PASSWORD, 'loghost', 'foo', firewall=True, esxi_hosts='bar') + """ + self.assertRaises( + CommandExecutionError, + vsphere.set_syslog_config, + HOST, + USER, + PASSWORD, + "loghost", + "foo", + firewall=True, + esxi_hosts="bar", + ) def test_set_syslog_config_host_list_firewall_bad_retcode(self): - ''' + """ Tests error message returned with list of esxi_hosts with 'loghost' as syslog_config. - ''' - with patch('salt.modules.vsphere.enable_firewall_ruleset', - MagicMock(return_value={'host_1.foo.com': {'retcode': 1, 'stdout': ERROR}})): - with patch('salt.modules.vsphere._set_syslog_config_helper', - MagicMock(return_value={})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'enable_firewall': {'message': ERROR, 'success': False}}}, - vsphere.set_syslog_config(HOST, - USER, - PASSWORD, - 'loghost', - 'foo', - firewall=True, - esxi_hosts=[host_1])) + """ + with patch( + "salt.modules.vsphere.enable_firewall_ruleset", + MagicMock(return_value={"host_1.foo.com": {"retcode": 1, "stdout": ERROR}}), + ): + with patch( + "salt.modules.vsphere._set_syslog_config_helper", + MagicMock(return_value={}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"enable_firewall": {"message": ERROR, "success": False}}}, + vsphere.set_syslog_config( + HOST, + USER, + PASSWORD, + "loghost", + "foo", + firewall=True, + esxi_hosts=[host_1], + ), + ) def test_set_syslog_config_host_list_firewall_success(self): - ''' + """ Tests successful function return with list of esxi_hosts with 'loghost' as syslog_config. - ''' - with patch('salt.modules.vsphere.enable_firewall_ruleset', - MagicMock(return_value={'host_1.foo.com': {'retcode': 0}})): - with patch('salt.modules.vsphere._set_syslog_config_helper', - MagicMock(return_value={})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'enable_firewall': {'success': True}}}, - vsphere.set_syslog_config(HOST, - USER, - PASSWORD, - 'loghost', - 'foo', - firewall=True, - esxi_hosts=[host_1])) + """ + with patch( + "salt.modules.vsphere.enable_firewall_ruleset", + MagicMock(return_value={"host_1.foo.com": {"retcode": 0}}), + ): + with patch( + "salt.modules.vsphere._set_syslog_config_helper", + MagicMock(return_value={}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"enable_firewall": {"success": True}}}, + vsphere.set_syslog_config( + HOST, + USER, + PASSWORD, + "loghost", + "foo", + firewall=True, + esxi_hosts=[host_1], + ), + ) def test_set_syslog_config_firewall_bad_retcode(self): - ''' + """ Tests error message given for a single ESXi host with 'loghost' as syslog_config. - ''' - with patch('salt.modules.vsphere.enable_firewall_ruleset', - MagicMock(return_value={HOST: {'retcode': 1, 'stdout': ERROR}})): - with patch('salt.modules.vsphere._set_syslog_config_helper', - MagicMock(return_value={})): - self.assertEqual({HOST: {'enable_firewall': {'message': ERROR, 'success': False}}}, - vsphere.set_syslog_config(HOST, - USER, - PASSWORD, - 'loghost', - 'foo', - firewall=True)) + """ + with patch( + "salt.modules.vsphere.enable_firewall_ruleset", + MagicMock(return_value={HOST: {"retcode": 1, "stdout": ERROR}}), + ): + with patch( + "salt.modules.vsphere._set_syslog_config_helper", + MagicMock(return_value={}), + ): + self.assertEqual( + {HOST: {"enable_firewall": {"message": ERROR, "success": False}}}, + vsphere.set_syslog_config( + HOST, USER, PASSWORD, "loghost", "foo", firewall=True + ), + ) def test_set_syslog_config_firewall_success(self): - ''' + """ Tests successful function return for a single ESXi host with 'loghost' as syslog_config. - ''' - with patch('salt.modules.vsphere.enable_firewall_ruleset', - MagicMock(return_value={HOST: {'retcode': 0}})): - with patch('salt.modules.vsphere._set_syslog_config_helper', - MagicMock(return_value={})): - self.assertEqual({HOST: {'enable_firewall': {'success': True}}}, - vsphere.set_syslog_config(HOST, - USER, - PASSWORD, - 'loghost', - 'foo', - firewall=True)) + """ + with patch( + "salt.modules.vsphere.enable_firewall_ruleset", + MagicMock(return_value={HOST: {"retcode": 0}}), + ): + with patch( + "salt.modules.vsphere._set_syslog_config_helper", + MagicMock(return_value={}), + ): + self.assertEqual( + {HOST: {"enable_firewall": {"success": True}}}, + vsphere.set_syslog_config( + HOST, USER, PASSWORD, "loghost", "foo", firewall=True + ), + ) # Tests for get_syslog_config function def test_get_syslog_config_esxi_hosts_not_list(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list. - ''' - self.assertRaises(CommandExecutionError, - vsphere.get_syslog_config, - HOST, USER, PASSWORD, esxi_hosts='foo') + """ + self.assertRaises( + CommandExecutionError, + vsphere.get_syslog_config, + HOST, + USER, + PASSWORD, + esxi_hosts="foo", + ) def test_get_syslog_config_host_list_bad_retcode(self): - ''' + """ Tests error message returned with list of esxi_hosts. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'message': ERROR, 'success': False}}, - vsphere.get_syslog_config(HOST, USER, PASSWORD, esxi_hosts=[host_1])) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"message": ERROR, "success": False}}, + vsphere.get_syslog_config(HOST, USER, PASSWORD, esxi_hosts=[host_1]), + ) def test_get_syslog_config_host_list_success(self): - ''' + """ Tests successful function return when an esxi_host is provided. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'success': True}}, - vsphere.get_syslog_config(HOST, USER, PASSWORD, esxi_hosts=[host_1])) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"success": True}}, + vsphere.get_syslog_config(HOST, USER, PASSWORD, esxi_hosts=[host_1]), + ) def test_get_syslog_config_bad_retcode(self): - ''' + """ Tests error message given for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - self.assertEqual({HOST: {'message': ERROR, 'success': False}}, - vsphere.get_syslog_config(HOST, USER, PASSWORD)) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + self.assertEqual( + {HOST: {"message": ERROR, "success": False}}, + vsphere.get_syslog_config(HOST, USER, PASSWORD), + ) def test_get_syslog_config_success(self): - ''' + """ Tests successful function return for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - self.assertEqual({HOST: {'success': True}}, - vsphere.get_syslog_config(HOST, USER, PASSWORD)) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + self.assertEqual( + {HOST: {"success": True}}, + vsphere.get_syslog_config(HOST, USER, PASSWORD), + ) # Tests for reset_syslog_config function def test_reset_syslog_config_no_syslog_config(self): - ''' + """ Tests CommandExecutionError is raised when a syslog_config parameter is missing. - ''' - self.assertRaises(CommandExecutionError, - vsphere.reset_syslog_config, - HOST, USER, PASSWORD) + """ + self.assertRaises( + CommandExecutionError, vsphere.reset_syslog_config, HOST, USER, PASSWORD + ) def test_reset_syslog_config_esxi_hosts_not_list(self): - ''' + """ Tests CommandExecutionError is raised when esxi_hosts is provided, but is not a list. - ''' - self.assertRaises(CommandExecutionError, - vsphere.reset_syslog_config, - HOST, USER, PASSWORD, syslog_config='test', esxi_hosts='foo') + """ + self.assertRaises( + CommandExecutionError, + vsphere.reset_syslog_config, + HOST, + USER, + PASSWORD, + syslog_config="test", + esxi_hosts="foo", + ) def test_reset_syslog_config_invalid_config_param(self): - ''' + """ Tests error message returned when an invalid syslog_config parameter is provided. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={})): - error = 'Invalid syslog configuration parameter' - self.assertEqual({HOST: {'success': False, 'test': {'message': error, 'success': False}}}, - vsphere.reset_syslog_config(HOST, USER, PASSWORD, - syslog_config='test')) + """ + with patch("salt.utils.vmware.esxcli", MagicMock(return_value={})): + error = "Invalid syslog configuration parameter" + self.assertEqual( + { + HOST: { + "success": False, + "test": {"message": error, "success": False}, + } + }, + vsphere.reset_syslog_config(HOST, USER, PASSWORD, syslog_config="test"), + ) def test_reset_syslog_config_host_list_bad_retcode(self): - ''' + """ Tests error message returned with list of esxi_hosts. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'success': False, 'logdir': {'message': ERROR, 'success': False}}}, - vsphere.reset_syslog_config(HOST, USER, PASSWORD, - syslog_config='logdir', - esxi_hosts=[host_1])) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + { + host_1: { + "success": False, + "logdir": {"message": ERROR, "success": False}, + } + }, + vsphere.reset_syslog_config( + HOST, USER, PASSWORD, syslog_config="logdir", esxi_hosts=[host_1] + ), + ) def test_reset_syslog_config_host_list_success(self): - ''' + """ Tests successful function return when an esxi_host is provided. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - host_1 = 'host_1.foo.com' - self.assertEqual({host_1: {'success': True, 'loghost': {'success': True}}}, - vsphere.reset_syslog_config(HOST, USER, PASSWORD, - syslog_config='loghost', - esxi_hosts=[host_1])) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + host_1 = "host_1.foo.com" + self.assertEqual( + {host_1: {"success": True, "loghost": {"success": True}}}, + vsphere.reset_syslog_config( + HOST, USER, PASSWORD, syslog_config="loghost", esxi_hosts=[host_1] + ), + ) def test_reset_syslog_config_bad_retcode(self): - ''' + """ Tests error message given for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - self.assertEqual({HOST: {'success': False, 'logdir-unique': {'message': ERROR, 'success': False}}}, - vsphere.reset_syslog_config(HOST, USER, PASSWORD, - syslog_config='logdir-unique')) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + self.assertEqual( + { + HOST: { + "success": False, + "logdir-unique": {"message": ERROR, "success": False}, + } + }, + vsphere.reset_syslog_config( + HOST, USER, PASSWORD, syslog_config="logdir-unique" + ), + ) def test_reset_syslog_config_success(self): - ''' + """ Tests successful function return for a single ESXi host. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - self.assertEqual({HOST: {'success': True, 'default-rotate': {'success': True}}}, - vsphere.reset_syslog_config(HOST, USER, PASSWORD, - syslog_config='default-rotate')) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + self.assertEqual( + {HOST: {"success": True, "default-rotate": {"success": True}}}, + vsphere.reset_syslog_config( + HOST, USER, PASSWORD, syslog_config="default-rotate" + ), + ) def test_reset_syslog_config_success_multiple_configs(self): - ''' + """ Tests successful function return for a single ESXi host when passing in multiple syslog_config values. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - self.assertEqual({HOST: {'success': True, - 'default-size': {'success': True}, - 'default-timeout': {'success': True}}}, - vsphere.reset_syslog_config(HOST, USER, PASSWORD, - syslog_config='default-size,default-timeout')) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + self.assertEqual( + { + HOST: { + "success": True, + "default-size": {"success": True}, + "default-timeout": {"success": True}, + } + }, + vsphere.reset_syslog_config( + HOST, USER, PASSWORD, syslog_config="default-size,default-timeout" + ), + ) def test_reset_syslog_config_success_all_configs(self): - ''' + """ Tests successful function return for a single ESXi host when passing in multiple syslog_config values. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0, 'stdout': ''})): - self.assertEqual({HOST: {'success': True, - 'logdir': {'success': True}, - 'loghost': {'success': True}, - 'default-rotate': {'success': True}, - 'default-size': {'success': True}, - 'default-timeout': {'success': True}, - 'logdir-unique': {'success': True}}}, - vsphere.reset_syslog_config(HOST, USER, PASSWORD, - syslog_config='all')) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 0, "stdout": ""}), + ): + self.assertEqual( + { + HOST: { + "success": True, + "logdir": {"success": True}, + "loghost": {"success": True}, + "default-rotate": {"success": True}, + "default-size": {"success": True}, + "default-timeout": {"success": True}, + "logdir-unique": {"success": True}, + } + }, + vsphere.reset_syslog_config(HOST, USER, PASSWORD, syslog_config="all"), + ) # Tests for _reset_syslog_config_params function def test_reset_syslog_config_params_no_valid_reset(self): - ''' + """ Tests function returns False when an invalid syslog config is passed. - ''' - valid_resets = ['hello', 'world'] - config = 'foo' - ret = {'success': False, config: {'success': False, 'message': 'Invalid syslog configuration parameter'}} - self.assertEqual(ret, vsphere._reset_syslog_config_params(HOST, USER, PASSWORD, - 'cmd', config, valid_resets)) + """ + valid_resets = ["hello", "world"] + config = "foo" + ret = { + "success": False, + config: { + "success": False, + "message": "Invalid syslog configuration parameter", + }, + } + self.assertEqual( + ret, + vsphere._reset_syslog_config_params( + HOST, USER, PASSWORD, "cmd", config, valid_resets + ), + ) def test_reset_syslog_config_params_error(self): - ''' + """ Tests function returns False when the esxxli function returns an unsuccessful retcode. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - valid_resets = ['hello', 'world'] - error_dict = {'success': False, 'message': ERROR} - ret = {'success': False, 'hello': error_dict, 'world': error_dict} - self.assertDictEqual(ret, vsphere._reset_syslog_config_params(HOST, USER, PASSWORD, - 'cmd', valid_resets, valid_resets)) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + valid_resets = ["hello", "world"] + error_dict = {"success": False, "message": ERROR} + ret = {"success": False, "hello": error_dict, "world": error_dict} + self.assertDictEqual( + ret, + vsphere._reset_syslog_config_params( + HOST, USER, PASSWORD, "cmd", valid_resets, valid_resets + ), + ) def test_reset_syslog_config_params_success(self): - ''' + """ Tests function returns True when the esxxli function returns a successful retcode. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0})): - valid_resets = ['hello', 'world'] - ret = {'success': True, 'hello': {'success': True}, 'world': {'success': True}} - self.assertDictEqual(ret, vsphere._reset_syslog_config_params(HOST, USER, PASSWORD, - 'cmd', valid_resets, valid_resets)) + """ + with patch("salt.utils.vmware.esxcli", MagicMock(return_value={"retcode": 0})): + valid_resets = ["hello", "world"] + ret = { + "success": True, + "hello": {"success": True}, + "world": {"success": True}, + } + self.assertDictEqual( + ret, + vsphere._reset_syslog_config_params( + HOST, USER, PASSWORD, "cmd", valid_resets, valid_resets + ), + ) # Tests for _set_syslog_config_helper function def test_set_syslog_config_helper_no_valid_reset(self): - ''' + """ Tests function returns False when an invalid syslog config is passed. - ''' - config = 'foo' - ret = {'success': False, 'message': '\'{0}\' is not a valid config variable.'.format(config)} - self.assertEqual(ret, vsphere._set_syslog_config_helper(HOST, USER, PASSWORD, config, 'bar')) + """ + config = "foo" + ret = { + "success": False, + "message": "'{0}' is not a valid config variable.".format(config), + } + self.assertEqual( + ret, vsphere._set_syslog_config_helper(HOST, USER, PASSWORD, config, "bar") + ) def test_set_syslog_config_helper_bad_retcode(self): - ''' + """ Tests function returns False when the esxcli function returns an unsuccessful retcode. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 1, 'stdout': ERROR})): - config = 'default-rotate' - self.assertEqual({config: {'success': False, 'message': ERROR}}, - vsphere._set_syslog_config_helper(HOST, USER, PASSWORD, config, 'foo')) + """ + with patch( + "salt.utils.vmware.esxcli", + MagicMock(return_value={"retcode": 1, "stdout": ERROR}), + ): + config = "default-rotate" + self.assertEqual( + {config: {"success": False, "message": ERROR}}, + vsphere._set_syslog_config_helper(HOST, USER, PASSWORD, config, "foo"), + ) def test_set_syslog_config_helper_success(self): - ''' + """ Tests successful function return. - ''' - with patch('salt.utils.vmware.esxcli', MagicMock(return_value={'retcode': 0})): - config = 'logdir' - self.assertEqual({config: {'success': True}}, - vsphere._set_syslog_config_helper(HOST, USER, PASSWORD, config, 'foo')) + """ + with patch("salt.utils.vmware.esxcli", MagicMock(return_value={"retcode": 0})): + config = "logdir" + self.assertEqual( + {config: {"success": True}}, + vsphere._set_syslog_config_helper(HOST, USER, PASSWORD, config, "foo"), + ) class GetProxyTypeTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.get_proxy_type - ''' + """ def setup_loader_modules(self): - return {vsphere: {'__virtual__': MagicMock(return_value='vsphere')}} + return {vsphere: {"__virtual__": MagicMock(return_value="vsphere")}} def test_output(self): - with patch.dict(vsphere.__pillar__, - {'proxy': {'proxytype': 'fake_proxy_type'}}): + with patch.dict( + vsphere.__pillar__, {"proxy": {"proxytype": "fake_proxy_type"}} + ): ret = vsphere.get_proxy_type() - self.assertEqual('fake_proxy_type', ret) + self.assertEqual("fake_proxy_type", ret) class SupportsProxiesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.supports_proxies decorator - ''' + """ def setup_loader_modules(self): - return {vsphere: {'__virtual__': MagicMock(return_value='vsphere')}} + return {vsphere: {"__virtual__": MagicMock(return_value="vsphere")}} def test_supported_proxy(self): - @vsphere.supports_proxies('supported') + @vsphere.supports_proxies("supported") def mock_function(): - return 'fake_function' + return "fake_function" - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='supported')): + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="supported") + ): ret = mock_function() - self.assertEqual('fake_function', ret) + self.assertEqual("fake_function", ret) def test_unsupported_proxy(self): - @vsphere.supports_proxies('supported') + @vsphere.supports_proxies("supported") def mock_function(): - return 'fake_function' + return "fake_function" - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='unsupported')): + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="unsupported") + ): with self.assertRaises(CommandExecutionError) as excinfo: mock_function() - self.assertEqual('\'unsupported\' proxy is not supported by ' - 'function mock_function', - excinfo.exception.strerror) + self.assertEqual( + "'unsupported' proxy is not supported by " "function mock_function", + excinfo.exception.strerror, + ) class _GetProxyConnectionDetailsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere._get_proxy_connection_details - ''' + """ def setup_loader_modules(self): - return {vsphere: {'__virtual__': MagicMock(return_value='vsphere')}} + return {vsphere: {"__virtual__": MagicMock(return_value="vsphere")}} def setUp(self): - self.esxi_host_details = {'host': 'fake_host', - 'username': 'fake_username', - 'password': 'fake_password', - 'protocol': 'fake_protocol', - 'port': 'fake_port', - 'mechanism': 'fake_mechanism', - 'principal': 'fake_principal', - 'domain': 'fake_domain'} - self.esxi_vcenter_details = {'vcenter': 'fake_vcenter', - 'username': 'fake_username', - 'password': 'fake_password', - 'protocol': 'fake_protocol', - 'port': 'fake_port', - 'mechanism': 'fake_mechanism', - 'principal': 'fake_principal', - 'domain': 'fake_domain'} - self.esxdatacenter_details = {'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'username': 'fake_username', - 'password': 'fake_password', - 'protocol': 'fake_protocol', - 'port': 'fake_port', - 'mechanism': 'fake_mechanism', - 'principal': 'fake_principal', - 'domain': 'fake_domain'} - self.esxcluster_details = {'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'cluster': 'fake_cluster', - 'username': 'fake_username', - 'password': 'fake_password', - 'protocol': 'fake_protocol', - 'port': 'fake_port', - 'mechanism': 'fake_mechanism', - 'principal': 'fake_principal', - 'domain': 'fake_domain'} - self.vcenter_details = {'vcenter': 'fake_vcenter', - 'username': 'fake_username', - 'password': 'fake_password', - 'protocol': 'fake_protocol', - 'port': 'fake_port', - 'mechanism': 'fake_mechanism', - 'principal': 'fake_principal', - 'domain': 'fake_domain'} + self.esxi_host_details = { + "host": "fake_host", + "username": "fake_username", + "password": "fake_password", + "protocol": "fake_protocol", + "port": "fake_port", + "mechanism": "fake_mechanism", + "principal": "fake_principal", + "domain": "fake_domain", + } + self.esxi_vcenter_details = { + "vcenter": "fake_vcenter", + "username": "fake_username", + "password": "fake_password", + "protocol": "fake_protocol", + "port": "fake_port", + "mechanism": "fake_mechanism", + "principal": "fake_principal", + "domain": "fake_domain", + } + self.esxdatacenter_details = { + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "username": "fake_username", + "password": "fake_password", + "protocol": "fake_protocol", + "port": "fake_port", + "mechanism": "fake_mechanism", + "principal": "fake_principal", + "domain": "fake_domain", + } + self.esxcluster_details = { + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "cluster": "fake_cluster", + "username": "fake_username", + "password": "fake_password", + "protocol": "fake_protocol", + "port": "fake_port", + "mechanism": "fake_mechanism", + "principal": "fake_principal", + "domain": "fake_domain", + } + self.vcenter_details = { + "vcenter": "fake_vcenter", + "username": "fake_username", + "password": "fake_password", + "protocol": "fake_protocol", + "port": "fake_port", + "mechanism": "fake_mechanism", + "principal": "fake_principal", + "domain": "fake_domain", + } def tearDown(self): - for attrname in ('esxi_host_details', 'esxi_vcenter_details', - 'esxdatacenter_details', 'esxcluster_details'): + for attrname in ( + "esxi_host_details", + "esxi_vcenter_details", + "esxdatacenter_details", + "esxcluster_details", + ): try: delattr(self, attrname) except AttributeError: continue def test_esxi_proxy_host_details(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxi')): - with patch.dict(vsphere.__salt__, - {'esxi.get_details': - MagicMock(return_value=self.esxi_host_details)}): + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="esxi") + ): + with patch.dict( + vsphere.__salt__, + {"esxi.get_details": MagicMock(return_value=self.esxi_host_details)}, + ): ret = vsphere._get_proxy_connection_details() - self.assertEqual(('fake_host', 'fake_username', 'fake_password', - 'fake_protocol', 'fake_port', 'fake_mechanism', - 'fake_principal', 'fake_domain'), ret) + self.assertEqual( + ( + "fake_host", + "fake_username", + "fake_password", + "fake_protocol", + "fake_port", + "fake_mechanism", + "fake_principal", + "fake_domain", + ), + ret, + ) def test_esxdatacenter_proxy_details(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxdatacenter')): - with patch.dict(vsphere.__salt__, - {'esxdatacenter.get_details': MagicMock( - return_value=self.esxdatacenter_details)}): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value="esxdatacenter"), + ): + with patch.dict( + vsphere.__salt__, + { + "esxdatacenter.get_details": MagicMock( + return_value=self.esxdatacenter_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'), ret) + self.assertEqual( + ( + "fake_vcenter", + "fake_username", + "fake_password", + "fake_protocol", + "fake_port", + "fake_mechanism", + "fake_principal", + "fake_domain", + ), + ret, + ) def test_esxcluster_proxy_details(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxcluster')): - with patch.dict(vsphere.__salt__, - {'esxcluster.get_details': MagicMock( - return_value=self.esxcluster_details)}): + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="esxcluster") + ): + with patch.dict( + vsphere.__salt__, + { + "esxcluster.get_details": MagicMock( + return_value=self.esxcluster_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'), ret) + self.assertEqual( + ( + "fake_vcenter", + "fake_username", + "fake_password", + "fake_protocol", + "fake_port", + "fake_mechanism", + "fake_principal", + "fake_domain", + ), + ret, + ) def test_esxi_proxy_vcenter_details(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxi')): - with patch.dict(vsphere.__salt__, - {'esxi.get_details': - MagicMock( - return_value=self.esxi_vcenter_details)}): + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="esxi") + ): + with patch.dict( + vsphere.__salt__, + {"esxi.get_details": MagicMock(return_value=self.esxi_vcenter_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'), ret) + self.assertEqual( + ( + "fake_vcenter", + "fake_username", + "fake_password", + "fake_protocol", + "fake_port", + "fake_mechanism", + "fake_principal", + "fake_domain", + ), + ret, + ) def test_vcenter_proxy_details(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='vcenter')): - with patch.dict(vsphere.__salt__, - {'vcenter.get_details': MagicMock( - return_value=self.vcenter_details)}): + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="vcenter") + ): + with patch.dict( + vsphere.__salt__, + {"vcenter.get_details": MagicMock(return_value=self.vcenter_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'), ret) + self.assertEqual( + ( + "fake_vcenter", + "fake_username", + "fake_password", + "fake_protocol", + "fake_port", + "fake_mechanism", + "fake_principal", + "fake_domain", + ), + ret, + ) def test_unsupported_proxy_details(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='unsupported')): + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="unsupported") + ): with self.assertRaises(CommandExecutionError) as excinfo: ret = vsphere._get_proxy_connection_details() - self.assertEqual('\'unsupported\' proxy is not supported', - excinfo.exception.strerror) + self.assertEqual( + "'unsupported' proxy is not supported", excinfo.exception.strerror + ) class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.gets_service_instance_via_proxy decorator - ''' + """ def setup_loader_modules(self): - patcher = patch('salt.utils.vmware.get_service_instance', MagicMock()) + patcher = patch("salt.utils.vmware.get_service_instance", MagicMock()) patcher.start() self.addCleanup(patcher.stop) - patcher = patch('salt.utils.vmware.disconnect', MagicMock()) + patcher = patch("salt.utils.vmware.disconnect", MagicMock()) patcher.start() self.addCleanup(patcher.stop) return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), } } @@ -775,7 +1138,7 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): self.mock_details2 = MagicMock() def tearDown(self): - for attrname in ('mock_si', 'mock_details1', 'mock_details2'): + for attrname in ("mock_si", "mock_details1", "mock_details2"): try: delattr(self, attrname) except AttributeError: @@ -784,13 +1147,16 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): def test_no_service_instance_or_kwargs_parameters(self): @vsphere.gets_service_instance_via_proxy def mock_function(): - return 'fake_function' + return "fake_function" with self.assertRaises(CommandExecutionError) as excinfo: mock_function() - self.assertEqual('Function mock_function must have either a ' - '\'service_instance\', or a \'**kwargs\' type ' - 'parameter', excinfo.exception.strerror) + self.assertEqual( + "Function mock_function must have either a " + "'service_instance', or a '**kwargs' type " + "parameter", + excinfo.exception.strerror, + ) def test___get_proxy_connection_details_call(self): mock__get_proxy_connection_details = MagicMock() @@ -799,8 +1165,10 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): def mock_function(service_instance=None): return service_instance - with patch('salt.modules.vsphere._get_proxy_connection_details', - mock__get_proxy_connection_details): + with patch( + "salt.modules.vsphere._get_proxy_connection_details", + mock__get_proxy_connection_details, + ): mock_function() mock__get_proxy_connection_details.assert_called_once_with() @@ -812,15 +1180,18 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): def mock_function(service_instance=None): return service_instance - with patch('salt.modules.vsphere._get_proxy_connection_details', - MagicMock(return_value=(self.mock_details1, - self.mock_details2))): - with patch('salt.utils.vmware.get_service_instance', - mock_get_service_instance): - with patch('salt.utils.vmware.disconnect', mock_disconnect): + with patch( + "salt.modules.vsphere._get_proxy_connection_details", + MagicMock(return_value=(self.mock_details1, self.mock_details2)), + ): + with patch( + "salt.utils.vmware.get_service_instance", mock_get_service_instance + ): + with patch("salt.utils.vmware.disconnect", mock_disconnect): ret = mock_function() - mock_get_service_instance.assert_called_once_with(self.mock_details1, - self.mock_details2) + mock_get_service_instance.assert_called_once_with( + self.mock_details1, self.mock_details2 + ) mock_disconnect.assert_called_once_with(self.mock_si) self.assertEqual(ret, self.mock_si) @@ -830,17 +1201,20 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): @vsphere.gets_service_instance_via_proxy def mock_function(**kwargs): - return kwargs['service_instance'] + return kwargs["service_instance"] - with patch('salt.modules.vsphere._get_proxy_connection_details', - MagicMock(return_value=(self.mock_details1, - self.mock_details2))): - with patch('salt.utils.vmware.get_service_instance', - mock_get_service_instance): - with patch('salt.utils.vmware.disconnect', mock_disconnect): + with patch( + "salt.modules.vsphere._get_proxy_connection_details", + MagicMock(return_value=(self.mock_details1, self.mock_details2)), + ): + with patch( + "salt.utils.vmware.get_service_instance", mock_get_service_instance + ): + with patch("salt.utils.vmware.disconnect", mock_disconnect): ret = mock_function() - mock_get_service_instance.assert_called_once_with(self.mock_details1, - self.mock_details2) + mock_get_service_instance.assert_called_once_with( + self.mock_details1, self.mock_details2 + ) mock_disconnect.assert_called_once_with(self.mock_si) self.assertEqual(ret, self.mock_si) @@ -852,12 +1226,14 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): def mock_function(service_instance): return service_instance - with patch('salt.modules.vsphere._get_proxy_connection_details', - MagicMock(return_value=(self.mock_details1, - self.mock_details2))): - with patch('salt.utils.vmware.get_service_instance', - mock_get_service_instance): - with patch('salt.utils.vmware.disconnect', mock_disconnect): + with patch( + "salt.modules.vsphere._get_proxy_connection_details", + MagicMock(return_value=(self.mock_details1, self.mock_details2)), + ): + with patch( + "salt.utils.vmware.get_service_instance", mock_get_service_instance + ): + with patch("salt.utils.vmware.disconnect", mock_disconnect): ret = mock_function(self.mock_si) self.assertEqual(mock_get_service_instance.call_count, 0) self.assertEqual(mock_disconnect.call_count, 0) @@ -871,12 +1247,14 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): def mock_function(service_instance=None): return service_instance - with patch('salt.modules.vsphere._get_proxy_connection_details', - MagicMock(return_value=(self.mock_details1, - self.mock_details2))): - with patch('salt.utils.vmware.get_service_instance', - mock_get_service_instance): - with patch('salt.utils.vmware.disconnect', mock_disconnect): + with patch( + "salt.modules.vsphere._get_proxy_connection_details", + MagicMock(return_value=(self.mock_details1, self.mock_details2)), + ): + with patch( + "salt.utils.vmware.get_service_instance", mock_get_service_instance + ): + with patch("salt.utils.vmware.disconnect", mock_disconnect): ret = mock_function(self.mock_si) self.assertEqual(mock_get_service_instance.call_count, 0) self.assertEqual(mock_disconnect.call_count, 0) @@ -890,12 +1268,14 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): def mock_function(service_instance=None): return service_instance - with patch('salt.modules.vsphere._get_proxy_connection_details', - MagicMock(return_value=(self.mock_details1, - self.mock_details2))): - with patch('salt.utils.vmware.get_service_instance', - mock_get_service_instance): - with patch('salt.utils.vmware.disconnect', mock_disconnect): + with patch( + "salt.modules.vsphere._get_proxy_connection_details", + MagicMock(return_value=(self.mock_details1, self.mock_details2)), + ): + with patch( + "salt.utils.vmware.get_service_instance", mock_get_service_instance + ): + with patch("salt.utils.vmware.disconnect", mock_disconnect): ret = mock_function(service_instance=self.mock_si) self.assertEqual(mock_get_service_instance.call_count, 0) self.assertEqual(mock_disconnect.call_count, 0) @@ -907,14 +1287,16 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): @vsphere.gets_service_instance_via_proxy def mock_function(**kwargs): - return kwargs['service_instance'] + return kwargs["service_instance"] - with patch('salt.modules.vsphere._get_proxy_connection_details', - MagicMock(return_value=(self.mock_details1, - self.mock_details2))): - with patch('salt.utils.vmware.get_service_instance', - mock_get_service_instance): - with patch('salt.utils.vmware.disconnect', mock_disconnect): + with patch( + "salt.modules.vsphere._get_proxy_connection_details", + MagicMock(return_value=(self.mock_details1, self.mock_details2)), + ): + with patch( + "salt.utils.vmware.get_service_instance", mock_get_service_instance + ): + with patch("salt.utils.vmware.disconnect", mock_disconnect): ret = mock_function(service_instance=self.mock_si) self.assertEqual(mock_get_service_instance.call_count, 0) self.assertEqual(mock_disconnect.call_count, 0) @@ -922,77 +1304,84 @@ class GetsServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): class GetServiceInstanceViaProxyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.get_service_instance_via_proxy - ''' + """ def setup_loader_modules(self): - patcher = patch('salt.utils.vmware.get_service_instance', MagicMock()) + patcher = patch("salt.utils.vmware.get_service_instance", MagicMock()) patcher.start() self.addCleanup(patcher.stop) return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - 'get_proxy_type': MagicMock(return_value='esxi'), - '_get_proxy_connection_details': MagicMock() + "__virtual__": MagicMock(return_value="vsphere"), + "get_proxy_type": MagicMock(return_value="esxi"), + "_get_proxy_connection_details": MagicMock(), } } def test_supported_proxies(self): - supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter', 'vcenter', 'esxvm'] + supported_proxies = ["esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): vsphere.get_service_instance_via_proxy() def test_get_service_instance_call(self): mock_connection_details = [MagicMock(), MagicMock(), MagicMock()] mock_get_service_instance = MagicMock() - with patch('salt.modules.vsphere._get_proxy_connection_details', - MagicMock(return_value=mock_connection_details)): - with patch('salt.utils.vmware.get_service_instance', - mock_get_service_instance): + with patch( + "salt.modules.vsphere._get_proxy_connection_details", + MagicMock(return_value=mock_connection_details), + ): + with patch( + "salt.utils.vmware.get_service_instance", mock_get_service_instance + ): vsphere.get_service_instance_via_proxy() - mock_get_service_instance.assert_called_once_with( - *mock_connection_details) + mock_get_service_instance.assert_called_once_with(*mock_connection_details) def test_output(self): mock_si = MagicMock() - with patch('salt.utils.vmware.get_service_instance', - MagicMock(return_value=mock_si)): + with patch( + "salt.utils.vmware.get_service_instance", MagicMock(return_value=mock_si) + ): res = vsphere.get_service_instance_via_proxy() self.assertEqual(res, mock_si) class DisconnectTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.disconnect - ''' + """ def setup_loader_modules(self): self.mock_si = MagicMock() - self.addCleanup(delattr, self, 'mock_si') - patcher = patch('salt.utils.vmware.disconnect', MagicMock()) + self.addCleanup(delattr, self, "mock_si") + patcher = patch("salt.utils.vmware.disconnect", MagicMock()) patcher.start() self.addCleanup(patcher.stop) return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - 'get_proxy_type': MagicMock(return_value='esxi') + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "get_proxy_type": MagicMock(return_value="esxi"), } } def test_supported_proxies(self): - supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter', 'vcenter', 'esxvm'] + supported_proxies = ["esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): vsphere.disconnect(self.mock_si) def test_disconnect_call(self): mock_disconnect = MagicMock() - with patch('salt.utils.vmware.disconnect', mock_disconnect): + with patch("salt.utils.vmware.disconnect", mock_disconnect): vsphere.disconnect(self.mock_si) mock_disconnect.assert_called_once_with(self.mock_si) @@ -1002,536 +1391,616 @@ class DisconnectTestCase(TestCase, LoaderModuleMockMixin): class TestVcenterConnectionTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.test_vcenter_connection - ''' + """ def setup_loader_modules(self): self.mock_si = MagicMock() - self.addCleanup(delattr, self, 'mock_si') - patcher = patch('salt.utils.vmware.get_service_instance', MagicMock(return_value=self.mock_si)) + self.addCleanup(delattr, self, "mock_si") + patcher = patch( + "salt.utils.vmware.get_service_instance", + MagicMock(return_value=self.mock_si), + ) patcher.start() self.addCleanup(patcher.stop) - patcher = patch('salt.utils.vmware.disconnect', MagicMock()) + patcher = patch("salt.utils.vmware.disconnect", MagicMock()) patcher.start() self.addCleanup(patcher.stop) - patcher = patch('salt.utils.vmware.is_connection_to_a_vcenter', MagicMock()) + patcher = patch("salt.utils.vmware.is_connection_to_a_vcenter", MagicMock()) patcher.start() self.addCleanup(patcher.stop) return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - 'get_proxy_type': MagicMock(return_value='esxi') + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "get_proxy_type": MagicMock(return_value="esxi"), } } def test_supported_proxies(self): - supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter', 'vcenter', 'esxvm'] + supported_proxies = ["esxi", "esxcluster", "esxdatacenter", "vcenter", "esxvm"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): vsphere.test_vcenter_connection() def test_is_connection_to_a_vcenter_call_default_service_instance(self): mock_is_connection_to_a_vcenter = MagicMock() - with patch('salt.utils.vmware.is_connection_to_a_vcenter', - mock_is_connection_to_a_vcenter): + with patch( + "salt.utils.vmware.is_connection_to_a_vcenter", + mock_is_connection_to_a_vcenter, + ): vsphere.test_vcenter_connection() mock_is_connection_to_a_vcenter.assert_called_once_with(self.mock_si) def test_is_connection_to_a_vcenter_call_explicit_service_instance(self): expl_mock_si = MagicMock() mock_is_connection_to_a_vcenter = MagicMock() - with patch('salt.utils.vmware.is_connection_to_a_vcenter', - mock_is_connection_to_a_vcenter): + with patch( + "salt.utils.vmware.is_connection_to_a_vcenter", + mock_is_connection_to_a_vcenter, + ): vsphere.test_vcenter_connection(expl_mock_si) mock_is_connection_to_a_vcenter.assert_called_once_with(expl_mock_si) def test_is_connection_to_a_vcenter_raises_vmware_salt_error(self): - exc = VMwareSaltError('VMwareSaltError') - with patch('salt.utils.vmware.is_connection_to_a_vcenter', - MagicMock(side_effect=exc)): + exc = VMwareSaltError("VMwareSaltError") + with patch( + "salt.utils.vmware.is_connection_to_a_vcenter", MagicMock(side_effect=exc) + ): res = vsphere.test_vcenter_connection() self.assertEqual(res, False) def test_is_connection_to_a_vcenter_raises_non_vmware_salt_error(self): - exc = Exception('NonVMwareSaltError') - with patch('salt.utils.vmware.is_connection_to_a_vcenter', - MagicMock(side_effect=exc)): + exc = Exception("NonVMwareSaltError") + with patch( + "salt.utils.vmware.is_connection_to_a_vcenter", MagicMock(side_effect=exc) + ): with self.assertRaises(Exception) as excinfo: res = vsphere.test_vcenter_connection() - self.assertEqual('NonVMwareSaltError', text_type(excinfo.exception)) + self.assertEqual("NonVMwareSaltError", text_type(excinfo.exception)) def test_output_true(self): - with patch('salt.utils.vmware.is_connection_to_a_vcenter', - MagicMock(return_value=True)): + with patch( + "salt.utils.vmware.is_connection_to_a_vcenter", MagicMock(return_value=True) + ): res = vsphere.test_vcenter_connection() self.assertEqual(res, True) def test_output_false(self): - with patch('salt.utils.vmware.is_connection_to_a_vcenter', - MagicMock(return_value=False)): + with patch( + "salt.utils.vmware.is_connection_to_a_vcenter", + MagicMock(return_value=False), + ): res = vsphere.test_vcenter_connection() self.assertEqual(res, False) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class ListDatacentersViaProxyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.list_datacenters_via_proxy - ''' + """ def setup_loader_modules(self): self.mock_si = MagicMock() - self.addCleanup(delattr, self, 'mock_si') - patcher = patch('salt.utils.vmware.get_service_instance', - MagicMock(return_value=self.mock_si)) + self.addCleanup(delattr, self, "mock_si") + patcher = patch( + "salt.utils.vmware.get_service_instance", + MagicMock(return_value=self.mock_si), + ) patcher.start() self.addCleanup(patcher.stop) - patcher = patch('salt.utils.vmware.get_datacenters', MagicMock()) + patcher = patch("salt.utils.vmware.get_datacenters", MagicMock()) patcher.start() self.addCleanup(patcher.stop) - patcher = patch('salt.utils.vmware.get_managed_object_name', - MagicMock()) + patcher = patch("salt.utils.vmware.get_managed_object_name", MagicMock()) patcher.start() self.addCleanup(patcher.stop) return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - 'get_proxy_type': MagicMock(return_value='esxdatacenter') + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "get_proxy_type": MagicMock(return_value="esxdatacenter"), } } def test_supported_proxies(self): - supported_proxies = ['esxcluster', 'esxdatacenter', 'vcenter', 'esxvm'] + supported_proxies = ["esxcluster", "esxdatacenter", "vcenter", "esxvm"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): vsphere.list_datacenters_via_proxy() def test_default_params(self): mock_get_datacenters = MagicMock() - with patch('salt.utils.vmware.get_datacenters', - mock_get_datacenters): + with patch("salt.utils.vmware.get_datacenters", mock_get_datacenters): vsphere.list_datacenters_via_proxy() - mock_get_datacenters.assert_called_once_with(self.mock_si, - get_all_datacenters=True) + mock_get_datacenters.assert_called_once_with( + self.mock_si, get_all_datacenters=True + ) def test_defined_service_instance(self): mock_si = MagicMock() mock_get_datacenters = MagicMock() - with patch('salt.utils.vmware.get_datacenters', - mock_get_datacenters): + with patch("salt.utils.vmware.get_datacenters", mock_get_datacenters): vsphere.list_datacenters_via_proxy(service_instance=mock_si) - mock_get_datacenters.assert_called_once_with(mock_si, - get_all_datacenters=True) + mock_get_datacenters.assert_called_once_with(mock_si, get_all_datacenters=True) def test_defined_datacenter_names(self): mock_datacenters = MagicMock() mock_get_datacenters = MagicMock() - with patch('salt.utils.vmware.get_datacenters', - mock_get_datacenters): + with patch("salt.utils.vmware.get_datacenters", mock_get_datacenters): vsphere.list_datacenters_via_proxy(mock_datacenters) - mock_get_datacenters.assert_called_once_with(self.mock_si, - mock_datacenters) + mock_get_datacenters.assert_called_once_with(self.mock_si, mock_datacenters) def test_get_managed_object_name_calls(self): mock_get_managed_object_name = MagicMock() mock_dcs = [MagicMock(), MagicMock()] - with patch('salt.utils.vmware.get_datacenters', - MagicMock(return_value=mock_dcs)): - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_datacenters", MagicMock(return_value=mock_dcs) + ): + with patch( + "salt.utils.vmware.get_managed_object_name", + mock_get_managed_object_name, + ): vsphere.list_datacenters_via_proxy() - mock_get_managed_object_name.assert_has_calls([call(mock_dcs[0]), - call(mock_dcs[1])]) + mock_get_managed_object_name.assert_has_calls( + [call(mock_dcs[0]), call(mock_dcs[1])] + ) def test_returned_array(self): - with patch('salt.utils.vmware.get_datacenters', - MagicMock(return_value=[MagicMock(), MagicMock()])): + with patch( + "salt.utils.vmware.get_datacenters", + MagicMock(return_value=[MagicMock(), MagicMock()]), + ): # 2 datacenters - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(side_effect=['fake_dc1', 'fake_dc2', - 'fake_dc3'])): + with patch( + "salt.utils.vmware.get_managed_object_name", + MagicMock(side_effect=["fake_dc1", "fake_dc2", "fake_dc3"]), + ): # 3 possible names res = vsphere.list_datacenters_via_proxy() # Just the first two names are in the result - self.assertEqual(res, [{'name': 'fake_dc1'}, {'name': 'fake_dc2'}]) + self.assertEqual(res, [{"name": "fake_dc1"}, {"name": "fake_dc2"}]) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class CreateDatacenterTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.create_datacenter - ''' + """ def setup_loader_modules(self): self.mock_si = MagicMock() - self.addCleanup(delattr, self, 'mock_si') - patcher = patch('salt.utils.vmware.get_service_instance', MagicMock(return_value=self.mock_si)) + self.addCleanup(delattr, self, "mock_si") + patcher = patch( + "salt.utils.vmware.get_service_instance", + MagicMock(return_value=self.mock_si), + ) patcher.start() self.addCleanup(patcher.stop) - patcher = patch('salt.utils.vmware.create_datacenter', MagicMock()) + patcher = patch("salt.utils.vmware.create_datacenter", MagicMock()) patcher.start() self.addCleanup(patcher.stop) return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - 'get_proxy_type': MagicMock(return_value='esxdatacenter') + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "get_proxy_type": MagicMock(return_value="esxdatacenter"), } } def test_supported_proxies(self): - supported_proxies = ['esxdatacenter', 'vcenter'] + supported_proxies = ["esxdatacenter", "vcenter"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): - vsphere.create_datacenter('fake_dc1') + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): + vsphere.create_datacenter("fake_dc1") def test_default_service_instance(self): mock_create_datacenter = MagicMock() - with patch('salt.utils.vmware.create_datacenter', - mock_create_datacenter): - vsphere.create_datacenter('fake_dc1') - mock_create_datacenter.assert_called_once_with(self.mock_si, - 'fake_dc1') + with patch("salt.utils.vmware.create_datacenter", mock_create_datacenter): + vsphere.create_datacenter("fake_dc1") + mock_create_datacenter.assert_called_once_with(self.mock_si, "fake_dc1") def test_defined_service_instance(self): mock_si = MagicMock() mock_create_datacenter = MagicMock() - with patch('salt.utils.vmware.create_datacenter', - mock_create_datacenter): - vsphere.create_datacenter('fake_dc1', service_instance=mock_si) - mock_create_datacenter.assert_called_once_with(mock_si, 'fake_dc1') + with patch("salt.utils.vmware.create_datacenter", mock_create_datacenter): + vsphere.create_datacenter("fake_dc1", service_instance=mock_si) + mock_create_datacenter.assert_called_once_with(mock_si, "fake_dc1") def test_returned_value(self): - res = vsphere.create_datacenter('fake_dc1') - self.assertEqual(res, {'create_datacenter': True}) + res = vsphere.create_datacenter("fake_dc1") + self.assertEqual(res, {"create_datacenter": True}) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class EraseDiskPartitionsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.erase_disk_partitions - ''' + """ def setup_loader_modules(self): return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - '__proxy__': {'esxi.get_details': MagicMock( - return_value={'esxi_host': 'fake_host'})} + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "__proxy__": { + "esxi.get_details": MagicMock( + return_value={"esxi_host": "fake_host"} + ) + }, } } def setUp(self): - attrs = (('mock_si', MagicMock()), - ('mock_host', MagicMock())) + attrs = (("mock_si", MagicMock()), ("mock_host", MagicMock())) for attr, mock_obj in attrs: setattr(self, attr, mock_obj) self.addCleanup(delattr, self, attr) - attrs = (('mock_proxy_target', MagicMock(return_value=self.mock_host)), - ('mock_erase_disk_partitions', MagicMock())) + attrs = ( + ("mock_proxy_target", MagicMock(return_value=self.mock_host)), + ("mock_erase_disk_partitions", MagicMock()), + ) for attr, mock_obj in attrs: setattr(self, attr, mock_obj) self.addCleanup(delattr, self, attr) patches = ( - ('salt.utils.vmware.get_service_instance', - MagicMock(return_value=self.mock_si)), - ('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxi')), - ('salt.modules.vsphere._get_proxy_target', - MagicMock(return_value=self.mock_host)), - ('salt.utils.vmware.erase_disk_partitions', - self.mock_erase_disk_partitions)) + ( + "salt.utils.vmware.get_service_instance", + MagicMock(return_value=self.mock_si), + ), + ("salt.modules.vsphere.get_proxy_type", MagicMock(return_value="esxi")), + ( + "salt.modules.vsphere._get_proxy_target", + MagicMock(return_value=self.mock_host), + ), + ( + "salt.utils.vmware.erase_disk_partitions", + self.mock_erase_disk_partitions, + ), + ) for module, mock_obj in patches: patcher = patch(module, mock_obj) patcher.start() self.addCleanup(patcher.stop) def test_supported_proxies(self): - supported_proxies = ['esxi'] + supported_proxies = ["esxi"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): - vsphere.erase_disk_partitions(disk_id='fake_disk') + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): + vsphere.erase_disk_partitions(disk_id="fake_disk") def test_no_disk_id_or_scsi_address(self): with self.assertRaises(ArgumentValueError) as excinfo: vsphere.erase_disk_partitions() - self.assertEqual('Either \'disk_id\' or \'scsi_address\' needs to ' - 'be specified', excinfo.exception.strerror) + self.assertEqual( + "Either 'disk_id' or 'scsi_address' needs to " "be specified", + excinfo.exception.strerror, + ) def test_get_proxy_target(self): mock_test_proxy_target = MagicMock() - with patch('salt.modules.vsphere._get_proxy_target', - mock_test_proxy_target): - vsphere.erase_disk_partitions(disk_id='fake_disk') + with patch("salt.modules.vsphere._get_proxy_target", mock_test_proxy_target): + vsphere.erase_disk_partitions(disk_id="fake_disk") mock_test_proxy_target.assert_called_once_with(self.mock_si) def test_scsi_address_not_found(self): - mock = MagicMock(return_value={'bad_scsi_address': 'bad_disk_id'}) - with patch('salt.utils.vmware.get_scsi_address_to_lun_map', mock): + mock = MagicMock(return_value={"bad_scsi_address": "bad_disk_id"}) + with patch("salt.utils.vmware.get_scsi_address_to_lun_map", mock): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - vsphere.erase_disk_partitions(scsi_address='fake_scsi_address') - self.assertEqual('Scsi lun with address \'fake_scsi_address\' was ' - 'not found on host \'fake_host\'', - excinfo.exception.strerror) + vsphere.erase_disk_partitions(scsi_address="fake_scsi_address") + self.assertEqual( + "Scsi lun with address 'fake_scsi_address' was " + "not found on host 'fake_host'", + excinfo.exception.strerror, + ) def test_scsi_address_to_disk_id_map(self): - mock_disk_id = MagicMock(canonicalName='fake_scsi_disk_id') - mock_get_scsi_addr_to_lun = \ - MagicMock(return_value={'fake_scsi_address': mock_disk_id}) - with patch('salt.utils.vmware.get_scsi_address_to_lun_map', - mock_get_scsi_addr_to_lun): - vsphere.erase_disk_partitions(scsi_address='fake_scsi_address') + mock_disk_id = MagicMock(canonicalName="fake_scsi_disk_id") + mock_get_scsi_addr_to_lun = MagicMock( + return_value={"fake_scsi_address": mock_disk_id} + ) + with patch( + "salt.utils.vmware.get_scsi_address_to_lun_map", mock_get_scsi_addr_to_lun + ): + vsphere.erase_disk_partitions(scsi_address="fake_scsi_address") mock_get_scsi_addr_to_lun.assert_called_once_with(self.mock_host) self.mock_erase_disk_partitions.assert_called_once_with( - self.mock_si, self.mock_host, 'fake_scsi_disk_id', - hostname='fake_host') + self.mock_si, self.mock_host, "fake_scsi_disk_id", hostname="fake_host" + ) def test_erase_disk_partitions(self): - vsphere.erase_disk_partitions(disk_id='fake_disk_id') + vsphere.erase_disk_partitions(disk_id="fake_disk_id") self.mock_erase_disk_partitions.assert_called_once_with( - self.mock_si, self.mock_host, 'fake_disk_id', hostname='fake_host') + self.mock_si, self.mock_host, "fake_disk_id", hostname="fake_host" + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class RemoveDatastoreTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.remove_datastore - ''' + """ def setup_loader_modules(self): return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - 'get_proxy_type': MagicMock(return_value='esxdatacenter'), + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "get_proxy_type": MagicMock(return_value="esxdatacenter"), } } def setUp(self): - attrs = (('mock_si', MagicMock()), - ('mock_target', MagicMock()), - ('mock_ds', MagicMock())) + attrs = ( + ("mock_si", MagicMock()), + ("mock_target", MagicMock()), + ("mock_ds", MagicMock()), + ) for attr, mock_obj in attrs: setattr(self, attr, mock_obj) self.addCleanup(delattr, self, attr) patches = ( - ('salt.utils.vmware.get_service_instance', - MagicMock(return_value=self.mock_si)), - ('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxdatacenter')), - ('salt.modules.vsphere._get_proxy_target', - MagicMock(return_value=self.mock_target)), - ('salt.utils.vmware.get_datastores', - MagicMock(return_value=[self.mock_ds])), - ('salt.utils.vmware.remove_datastore', MagicMock())) + ( + "salt.utils.vmware.get_service_instance", + MagicMock(return_value=self.mock_si), + ), + ( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value="esxdatacenter"), + ), + ( + "salt.modules.vsphere._get_proxy_target", + MagicMock(return_value=self.mock_target), + ), + ( + "salt.utils.vmware.get_datastores", + MagicMock(return_value=[self.mock_ds]), + ), + ("salt.utils.vmware.remove_datastore", MagicMock()), + ) for module, mock_obj in patches: patcher = patch(module, mock_obj) patcher.start() self.addCleanup(patcher.stop) def test_supported_proxes(self): - supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter'] + supported_proxies = ["esxi", "esxcluster", "esxdatacenter"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): - vsphere.remove_datastore(datastore='fake_ds_name') + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): + vsphere.remove_datastore(datastore="fake_ds_name") def test__get_proxy_target_call(self): mock__get_proxy_target = MagicMock(return_value=self.mock_target) - with patch('salt.modules.vsphere._get_proxy_target', - mock__get_proxy_target): - vsphere.remove_datastore(datastore='fake_ds_name') + with patch("salt.modules.vsphere._get_proxy_target", mock__get_proxy_target): + vsphere.remove_datastore(datastore="fake_ds_name") mock__get_proxy_target.assert_called_once_with(self.mock_si) def test_get_datastores_call(self): mock_get_datastores = MagicMock() - with patch('salt.utils.vmware.get_datastores', - mock_get_datastores): - vsphere.remove_datastore(datastore='fake_ds') + with patch("salt.utils.vmware.get_datastores", mock_get_datastores): + vsphere.remove_datastore(datastore="fake_ds") mock_get_datastores.assert_called_once_with( - self.mock_si, reference=self.mock_target, - datastore_names=['fake_ds']) + self.mock_si, reference=self.mock_target, datastore_names=["fake_ds"] + ) def test_datastore_not_found(self): - with patch('salt.utils.vmware.get_datastores', - MagicMock(return_value=[])): + with patch("salt.utils.vmware.get_datastores", MagicMock(return_value=[])): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - vsphere.remove_datastore(datastore='fake_ds') - self.assertEqual('Datastore \'fake_ds\' was not found', - excinfo.exception.strerror) + vsphere.remove_datastore(datastore="fake_ds") + self.assertEqual( + "Datastore 'fake_ds' was not found", excinfo.exception.strerror + ) def test_multiple_datastores_found(self): - with patch('salt.utils.vmware.get_datastores', - MagicMock(return_value=[MagicMock(), MagicMock()])): + with patch( + "salt.utils.vmware.get_datastores", + MagicMock(return_value=[MagicMock(), MagicMock()]), + ): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - vsphere.remove_datastore(datastore='fake_ds') - self.assertEqual('Multiple datastores \'fake_ds\' were found', - excinfo.exception.strerror) + vsphere.remove_datastore(datastore="fake_ds") + self.assertEqual( + "Multiple datastores 'fake_ds' were found", excinfo.exception.strerror + ) def test_remove_datastore_call(self): mock_remove_datastore = MagicMock() - with patch('salt.utils.vmware.remove_datastore', - mock_remove_datastore): - vsphere.remove_datastore(datastore='fake_ds') - mock_remove_datastore.assert_called_once_with( - self.mock_si, self.mock_ds) + with patch("salt.utils.vmware.remove_datastore", mock_remove_datastore): + vsphere.remove_datastore(datastore="fake_ds") + mock_remove_datastore.assert_called_once_with(self.mock_si, self.mock_ds) def test_success_output(self): - res = vsphere.remove_datastore(datastore='fake_ds') + res = vsphere.remove_datastore(datastore="fake_ds") self.assertTrue(res) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class RemoveDiskgroupTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.remove_diskgroup - ''' + """ def setup_loader_modules(self): return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - '__proxy__': {'esxi.get_details': MagicMock( - return_value={'esxi_host': 'fake_host'})} + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "__proxy__": { + "esxi.get_details": MagicMock( + return_value={"esxi_host": "fake_host"} + ) + }, } } def setUp(self): - attrs = (('mock_si', MagicMock()), - ('mock_host', MagicMock()), - ('mock_diskgroup', MagicMock())) + attrs = ( + ("mock_si", MagicMock()), + ("mock_host", MagicMock()), + ("mock_diskgroup", MagicMock()), + ) for attr, mock_obj in attrs: setattr(self, attr, mock_obj) self.addCleanup(delattr, self, attr) patches = ( - ('salt.utils.vmware.get_service_instance', - MagicMock(return_value=self.mock_si)), - ('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxi')), - ('salt.modules.vsphere._get_proxy_target', - MagicMock(return_value=self.mock_host)), - ('salt.utils.vmware.get_diskgroups', - MagicMock(return_value=[self.mock_diskgroup])), - ('salt.utils.vsan.remove_diskgroup', MagicMock())) + ( + "salt.utils.vmware.get_service_instance", + MagicMock(return_value=self.mock_si), + ), + ("salt.modules.vsphere.get_proxy_type", MagicMock(return_value="esxi")), + ( + "salt.modules.vsphere._get_proxy_target", + MagicMock(return_value=self.mock_host), + ), + ( + "salt.utils.vmware.get_diskgroups", + MagicMock(return_value=[self.mock_diskgroup]), + ), + ("salt.utils.vsan.remove_diskgroup", MagicMock()), + ) for module, mock_obj in patches: patcher = patch(module, mock_obj) patcher.start() self.addCleanup(patcher.stop) def test_supported_proxes(self): - supported_proxies = ['esxi'] + supported_proxies = ["esxi"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): - vsphere.remove_diskgroup(cache_disk_id='fake_disk_id') + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): + vsphere.remove_diskgroup(cache_disk_id="fake_disk_id") def test__get_proxy_target_call(self): mock__get_proxy_target = MagicMock(return_value=self.mock_host) - with patch('salt.modules.vsphere._get_proxy_target', - mock__get_proxy_target): - vsphere.remove_diskgroup(cache_disk_id='fake_disk_id') + with patch("salt.modules.vsphere._get_proxy_target", mock__get_proxy_target): + vsphere.remove_diskgroup(cache_disk_id="fake_disk_id") mock__get_proxy_target.assert_called_once_with(self.mock_si) def test_get_disk_groups(self): mock_get_diskgroups = MagicMock(return_value=[self.mock_diskgroup]) - with patch('salt.utils.vmware.get_diskgroups', - mock_get_diskgroups): - vsphere.remove_diskgroup(cache_disk_id='fake_disk_id') + with patch("salt.utils.vmware.get_diskgroups", mock_get_diskgroups): + vsphere.remove_diskgroup(cache_disk_id="fake_disk_id") mock_get_diskgroups.assert_called_once_with( - self.mock_host, cache_disk_ids=['fake_disk_id']) + self.mock_host, cache_disk_ids=["fake_disk_id"] + ) def test_disk_group_not_found_safety_checks_set(self): - with patch('salt.utils.vmware.get_diskgroups', - MagicMock(return_value=[])): + with patch("salt.utils.vmware.get_diskgroups", MagicMock(return_value=[])): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - vsphere.remove_diskgroup(cache_disk_id='fake_disk_id') - self.assertEqual('No diskgroup with cache disk id ' - '\'fake_disk_id\' was found in ESXi host ' - '\'fake_host\'', - excinfo.exception.strerror) + vsphere.remove_diskgroup(cache_disk_id="fake_disk_id") + self.assertEqual( + "No diskgroup with cache disk id " + "'fake_disk_id' was found in ESXi host " + "'fake_host'", + excinfo.exception.strerror, + ) def test_remove_disk_group(self): mock_remove_diskgroup = MagicMock(return_value=None) - with patch('salt.utils.vsan.remove_diskgroup', - mock_remove_diskgroup): - vsphere.remove_diskgroup(cache_disk_id='fake_disk_id') + with patch("salt.utils.vsan.remove_diskgroup", mock_remove_diskgroup): + vsphere.remove_diskgroup(cache_disk_id="fake_disk_id") mock_remove_diskgroup.assert_called_once_with( - self.mock_si, self.mock_host, self.mock_diskgroup, - data_accessibility=True) + self.mock_si, self.mock_host, self.mock_diskgroup, data_accessibility=True + ) def test_remove_disk_group_data_accessibility_false(self): mock_remove_diskgroup = MagicMock(return_value=None) - with patch('salt.utils.vsan.remove_diskgroup', - mock_remove_diskgroup): - vsphere.remove_diskgroup(cache_disk_id='fake_disk_id', - data_accessibility=False) + with patch("salt.utils.vsan.remove_diskgroup", mock_remove_diskgroup): + vsphere.remove_diskgroup( + cache_disk_id="fake_disk_id", data_accessibility=False + ) mock_remove_diskgroup.assert_called_once_with( - self.mock_si, self.mock_host, self.mock_diskgroup, - data_accessibility=False) + self.mock_si, self.mock_host, self.mock_diskgroup, data_accessibility=False + ) def test_success_output(self): - res = vsphere.remove_diskgroup(cache_disk_id='fake_disk_id') + res = vsphere.remove_diskgroup(cache_disk_id="fake_disk_id") self.assertTrue(res) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not vsphere.HAS_JSONSCHEMA, 'The \'jsonschema\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not vsphere.HAS_JSONSCHEMA, "The 'jsonschema' library is missing") class RemoveCapacityFromDiskgroupTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.remove_capacity_from_diskgroup - ''' + """ def setup_loader_modules(self): return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - '__proxy__': {'esxi.get_details': MagicMock( - return_value={'esxi_host': 'fake_host'})} + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "__proxy__": { + "esxi.get_details": MagicMock( + return_value={"esxi_host": "fake_host"} + ) + }, } } def setUp(self): - attrs = (('mock_si', MagicMock()), - ('mock_schema', MagicMock()), - ('mock_host', MagicMock()), - ('mock_disk1', MagicMock(canonicalName='fake_disk1')), - ('mock_disk2', MagicMock(canonicalName='fake_disk2')), - ('mock_disk3', MagicMock(canonicalName='fake_disk3')), - ('mock_diskgroup', MagicMock())) + attrs = ( + ("mock_si", MagicMock()), + ("mock_schema", MagicMock()), + ("mock_host", MagicMock()), + ("mock_disk1", MagicMock(canonicalName="fake_disk1")), + ("mock_disk2", MagicMock(canonicalName="fake_disk2")), + ("mock_disk3", MagicMock(canonicalName="fake_disk3")), + ("mock_diskgroup", MagicMock()), + ) for attr, mock_obj in attrs: setattr(self, attr, mock_obj) self.addCleanup(delattr, self, attr) patches = ( - ('salt.utils.vmware.get_service_instance', - MagicMock(return_value=self.mock_si)), - ('salt.modules.vsphere.DiskGroupsDiskIdSchema.serialize', - MagicMock(return_value=self.mock_schema)), - ('salt.modules.vsphere.jsonschema.validate', MagicMock()), - ('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxi')), - ('salt.modules.vsphere._get_proxy_target', - MagicMock(return_value=self.mock_host)), - ('salt.utils.vmware.get_disks', - MagicMock(return_value=[self.mock_disk1, self.mock_disk2, - self.mock_disk3])), - ('salt.utils.vmware.get_diskgroups', - MagicMock(return_value=[self.mock_diskgroup])), - ('salt.utils.vsan.remove_capacity_from_diskgroup', MagicMock())) + ( + "salt.utils.vmware.get_service_instance", + MagicMock(return_value=self.mock_si), + ), + ( + "salt.modules.vsphere.DiskGroupsDiskIdSchema.serialize", + MagicMock(return_value=self.mock_schema), + ), + ("salt.modules.vsphere.jsonschema.validate", MagicMock()), + ("salt.modules.vsphere.get_proxy_type", MagicMock(return_value="esxi")), + ( + "salt.modules.vsphere._get_proxy_target", + MagicMock(return_value=self.mock_host), + ), + ( + "salt.utils.vmware.get_disks", + MagicMock( + return_value=[self.mock_disk1, self.mock_disk2, self.mock_disk3] + ), + ), + ( + "salt.utils.vmware.get_diskgroups", + MagicMock(return_value=[self.mock_diskgroup]), + ), + ("salt.utils.vsan.remove_capacity_from_diskgroup", MagicMock()), + ) for module, mock_obj in patches: patcher = patch(module, mock_obj) patcher.start() @@ -1539,227 +2008,273 @@ class RemoveCapacityFromDiskgroupTestCase(TestCase, LoaderModuleMockMixin): def test_validate(self): mock_schema_validate = MagicMock() - with patch('salt.modules.vsphere.jsonschema.validate', - mock_schema_validate): + with patch("salt.modules.vsphere.jsonschema.validate", mock_schema_validate): vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2']) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + ) mock_schema_validate.assert_called_once_with( - {'diskgroups': [{'cache_id': 'fake_cache_disk_id', - 'capacity_ids': ['fake_disk1', - 'fake_disk2']}]}, - self.mock_schema) + { + "diskgroups": [ + { + "cache_id": "fake_cache_disk_id", + "capacity_ids": ["fake_disk1", "fake_disk2"], + } + ] + }, + self.mock_schema, + ) def test_invalid_schema_validation(self): mock_schema_validate = MagicMock( - side_effect=vsphere.jsonschema.exceptions.ValidationError('err')) - with patch('salt.modules.vsphere.jsonschema.validate', - mock_schema_validate): + side_effect=vsphere.jsonschema.exceptions.ValidationError("err") + ) + with patch("salt.modules.vsphere.jsonschema.validate", mock_schema_validate): with self.assertRaises(ArgumentValueError) as excinfo: vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2']) - self.assertEqual('err', excinfo.exception.strerror) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + ) + self.assertEqual("err", excinfo.exception.strerror) def test_supported_proxes(self): - supported_proxies = ['esxi'] + supported_proxies = ["esxi"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2']) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + ) def test__get_proxy_target_call(self): mock__get_proxy_target = MagicMock(return_value=self.mock_host) - with patch('salt.modules.vsphere._get_proxy_target', - mock__get_proxy_target): + with patch("salt.modules.vsphere._get_proxy_target", mock__get_proxy_target): vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2']) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + ) mock__get_proxy_target.assert_called_once_with(self.mock_si) def test_get_disks(self): mock_get_disks = MagicMock( - return_value=[self.mock_disk1, self.mock_disk2, self.mock_disk3]) - with patch('salt.utils.vmware.get_disks', mock_get_disks): + return_value=[self.mock_disk1, self.mock_disk2, self.mock_disk3] + ) + with patch("salt.utils.vmware.get_disks", mock_get_disks): vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2']) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + ) mock_get_disks.assert_called_once_with( - self.mock_host, disk_ids=['fake_disk1', 'fake_disk2']) + self.mock_host, disk_ids=["fake_disk1", "fake_disk2"] + ) def test_disk_not_found_safety_checks_set(self): mock_get_disks = MagicMock( - return_value=[self.mock_disk1, self.mock_disk2, self.mock_disk3]) - with patch('salt.utils.vmware.get_disks', mock_get_disks): + return_value=[self.mock_disk1, self.mock_disk2, self.mock_disk3] + ) + with patch("salt.utils.vmware.get_disks", mock_get_disks): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk4'], - safety_checks=True) - self.assertEqual('No disk with id \'fake_disk4\' was found ' - 'in ESXi host \'fake_host\'', - excinfo.exception.strerror) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk4"], + safety_checks=True, + ) + self.assertEqual( + "No disk with id 'fake_disk4' was found " "in ESXi host 'fake_host'", + excinfo.exception.strerror, + ) def test_get_diskgroups(self): mock_get_diskgroups = MagicMock(return_value=[self.mock_diskgroup]) - with patch('salt.utils.vmware.get_diskgroups', - mock_get_diskgroups): + with patch("salt.utils.vmware.get_diskgroups", mock_get_diskgroups): vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2']) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + ) mock_get_diskgroups.assert_called_once_with( - self.mock_host, cache_disk_ids=['fake_cache_disk_id']) + self.mock_host, cache_disk_ids=["fake_cache_disk_id"] + ) def test_diskgroup_not_found(self): - with patch('salt.utils.vmware.get_diskgroups', - MagicMock(return_value=[])): + with patch("salt.utils.vmware.get_diskgroups", MagicMock(return_value=[])): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2']) - self.assertEqual('No diskgroup with cache disk id ' - '\'fake_cache_disk_id\' was found in ESXi host ' - '\'fake_host\'', - excinfo.exception.strerror) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + ) + self.assertEqual( + "No diskgroup with cache disk id " + "'fake_cache_disk_id' was found in ESXi host " + "'fake_host'", + excinfo.exception.strerror, + ) def test_remove_capacity_from_diskgroup(self): mock_remove_capacity_from_diskgroup = MagicMock() - with patch('salt.utils.vsan.remove_capacity_from_diskgroup', - mock_remove_capacity_from_diskgroup): + with patch( + "salt.utils.vsan.remove_capacity_from_diskgroup", + mock_remove_capacity_from_diskgroup, + ): vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2']) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + ) mock_remove_capacity_from_diskgroup.assert_called_once_with( - self.mock_si, self.mock_host, self.mock_diskgroup, + self.mock_si, + self.mock_host, + self.mock_diskgroup, capacity_disks=[self.mock_disk1, self.mock_disk2], - data_evacuation=True) + data_evacuation=True, + ) def test_remove_capacity_from_diskgroup_data_evacuation_false(self): mock_remove_capacity_from_diskgroup = MagicMock() - with patch('salt.utils.vsan.remove_capacity_from_diskgroup', - mock_remove_capacity_from_diskgroup): + with patch( + "salt.utils.vsan.remove_capacity_from_diskgroup", + mock_remove_capacity_from_diskgroup, + ): vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2'], - data_evacuation=False) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + data_evacuation=False, + ) mock_remove_capacity_from_diskgroup.assert_called_once_with( - self.mock_si, self.mock_host, self.mock_diskgroup, + self.mock_si, + self.mock_host, + self.mock_diskgroup, capacity_disks=[self.mock_disk1, self.mock_disk2], - data_evacuation=False) + data_evacuation=False, + ) def test_success_output(self): res = vsphere.remove_capacity_from_diskgroup( - cache_disk_id='fake_cache_disk_id', - capacity_disk_ids=['fake_disk1', 'fake_disk2']) + cache_disk_id="fake_cache_disk_id", + capacity_disk_ids=["fake_disk1", "fake_disk2"], + ) self.assertTrue(res) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class ListClusterTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.list_cluster - ''' + """ def setup_loader_modules(self): return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - '__salt__': {} + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "__salt__": {}, } } def setUp(self): - attrs = (('mock_si', MagicMock()), - ('mock_dc', MagicMock()), - ('mock_cl', MagicMock()), - ('mock__get_cluster_dict', MagicMock())) + attrs = ( + ("mock_si", MagicMock()), + ("mock_dc", MagicMock()), + ("mock_cl", MagicMock()), + ("mock__get_cluster_dict", MagicMock()), + ) for attr, mock_obj in attrs: setattr(self, attr, mock_obj) self.addCleanup(delattr, self, attr) - attrs = (('mock_get_cluster', MagicMock(return_value=self.mock_cl)),) + attrs = (("mock_get_cluster", MagicMock(return_value=self.mock_cl)),) for attr, mock_obj in attrs: setattr(self, attr, mock_obj) self.addCleanup(delattr, self, attr) patches = ( - ('salt.utils.vmware.get_service_instance', - MagicMock(return_value=self.mock_si)), - ('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxcluster')), - ('salt.modules.vsphere._get_proxy_target', - MagicMock(return_value=self.mock_cl)), - ('salt.utils.vmware.get_cluster', self.mock_get_cluster), - ('salt.modules.vsphere._get_cluster_dict', - self.mock__get_cluster_dict)) + ( + "salt.utils.vmware.get_service_instance", + MagicMock(return_value=self.mock_si), + ), + ( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value="esxcluster"), + ), + ( + "salt.modules.vsphere._get_proxy_target", + MagicMock(return_value=self.mock_cl), + ), + ("salt.utils.vmware.get_cluster", self.mock_get_cluster), + ("salt.modules.vsphere._get_cluster_dict", self.mock__get_cluster_dict), + ) for module, mock_obj in patches: patcher = patch(module, mock_obj) patcher.start() self.addCleanup(patcher.stop) # Patch __salt__ dunder - patcher = patch.dict(vsphere.__salt__, - {'esxcluster.get_details': - MagicMock(return_value={'cluster': 'cl'})}) + patcher = patch.dict( + vsphere.__salt__, + {"esxcluster.get_details": MagicMock(return_value={"cluster": "cl"})}, + ) patcher.start() self.addCleanup(patcher.stop) def test_supported_proxies(self): - supported_proxies = ['esxcluster', 'esxdatacenter'] + supported_proxies = ["esxcluster", "esxdatacenter"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): - vsphere.list_cluster(cluster='cl') + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): + vsphere.list_cluster(cluster="cl") def test_default_service_instance(self): mock__get_proxy_target = MagicMock() - with patch('salt.modules.vsphere._get_proxy_target', - mock__get_proxy_target): + with patch("salt.modules.vsphere._get_proxy_target", mock__get_proxy_target): vsphere.list_cluster() mock__get_proxy_target.assert_called_once_with(self.mock_si) def test_defined_service_instance(self): mock_si = MagicMock() mock__get_proxy_target = MagicMock() - with patch('salt.modules.vsphere._get_proxy_target', - mock__get_proxy_target): + with patch("salt.modules.vsphere._get_proxy_target", mock__get_proxy_target): vsphere.list_cluster(service_instance=mock_si) mock__get_proxy_target.assert_called_once_with(mock_si) def test_no_cluster_raises_argument_value_error(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxdatacenter')): - with patch('salt.modules.vsphere._get_proxy_target', MagicMock()): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value="esxdatacenter"), + ): + with patch("salt.modules.vsphere._get_proxy_target", MagicMock()): with self.assertRaises(ArgumentValueError) as excinfo: vsphere.list_cluster() - self.assertEqual(excinfo.exception.strerror, - '\'cluster\' needs to be specified') + self.assertEqual(excinfo.exception.strerror, "'cluster' needs to be specified") def test_get_cluster_call(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxdatacenter')): - with patch('salt.modules.vsphere._get_proxy_target', - MagicMock(return_value=self.mock_dc)): - vsphere.list_cluster(cluster='cl') - self.mock_get_cluster.assert_called_once_with(self.mock_dc, 'cl') + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value="esxdatacenter"), + ): + with patch( + "salt.modules.vsphere._get_proxy_target", + MagicMock(return_value=self.mock_dc), + ): + vsphere.list_cluster(cluster="cl") + self.mock_get_cluster.assert_called_once_with(self.mock_dc, "cl") def test__get_cluster_dict_call(self): vsphere.list_cluster() - self.mock__get_cluster_dict.assert_called_once_with('cl', self.mock_cl) + self.mock__get_cluster_dict.assert_called_once_with("cl", self.mock_cl) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class RenameDatastoreTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere.rename_datastore - ''' + """ def setup_loader_modules(self): return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - 'get_proxy_type': MagicMock(return_value='esxdatacenter') + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "get_proxy_type": MagicMock(return_value="esxdatacenter"), } } @@ -1770,169 +2285,223 @@ class RenameDatastoreTestCase(TestCase, LoaderModuleMockMixin): self.mock_get_datastores = MagicMock(return_value=[self.mock_ds_ref]) self.mock_rename_datastore = MagicMock() patches = ( - ('salt.utils.vmware.get_service_instance', - MagicMock(return_value=self.mock_si)), - ('salt.modules.vsphere._get_proxy_target', - MagicMock(return_value=self.mock_target)), - ('salt.utils.vmware.get_datastores', - self.mock_get_datastores), - ('salt.utils.vmware.rename_datastore', - self.mock_rename_datastore)) + ( + "salt.utils.vmware.get_service_instance", + MagicMock(return_value=self.mock_si), + ), + ( + "salt.modules.vsphere._get_proxy_target", + MagicMock(return_value=self.mock_target), + ), + ("salt.utils.vmware.get_datastores", self.mock_get_datastores), + ("salt.utils.vmware.rename_datastore", self.mock_rename_datastore), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_target', 'mock_ds_ref', - 'mock_get_datastores', 'mock_rename_datastore'): + for attr in ( + "mock_si", + "mock_target", + "mock_ds_ref", + "mock_get_datastores", + "mock_rename_datastore", + ): delattr(self, attr) def test_supported_proxes(self): - supported_proxies = ['esxi', 'esxcluster', 'esxdatacenter'] + supported_proxies = ["esxi", "esxcluster", "esxdatacenter"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): - vsphere.rename_datastore('current_ds_name', 'new_ds_name') + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): + vsphere.rename_datastore("current_ds_name", "new_ds_name") def test_default_service_instance(self): mock__get_proxy_target = MagicMock() - with patch('salt.modules.vsphere._get_proxy_target', - mock__get_proxy_target): - vsphere.rename_datastore('current_ds_name', 'new_ds_name') + with patch("salt.modules.vsphere._get_proxy_target", mock__get_proxy_target): + vsphere.rename_datastore("current_ds_name", "new_ds_name") mock__get_proxy_target.assert_called_once_with(self.mock_si) def test_defined_service_instance(self): mock_si = MagicMock() mock__get_proxy_target = MagicMock() - with patch('salt.modules.vsphere._get_proxy_target', - mock__get_proxy_target): - vsphere.rename_datastore('current_ds_name', 'new_ds_name', - service_instance=mock_si) + with patch("salt.modules.vsphere._get_proxy_target", mock__get_proxy_target): + vsphere.rename_datastore( + "current_ds_name", "new_ds_name", service_instance=mock_si + ) mock__get_proxy_target.assert_called_once_with(mock_si) def test_get_datastore_call(self): - vsphere.rename_datastore('current_ds_name', 'new_ds_name') + vsphere.rename_datastore("current_ds_name", "new_ds_name") self.mock_get_datastores.assert_called_once_with( - self.mock_si, self.mock_target, - datastore_names=['current_ds_name']) + self.mock_si, self.mock_target, datastore_names=["current_ds_name"] + ) def test_get_no_datastores(self): - with patch('salt.utils.vmware.get_datastores', - MagicMock(return_value=[])): + with patch("salt.utils.vmware.get_datastores", MagicMock(return_value=[])): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - vsphere.rename_datastore('current_ds_name', 'new_ds_name') - self.assertEqual(excinfo.exception.strerror, - 'Datastore \'current_ds_name\' was not found') + vsphere.rename_datastore("current_ds_name", "new_ds_name") + self.assertEqual( + excinfo.exception.strerror, "Datastore 'current_ds_name' was not found" + ) def test_rename_datastore_call(self): - vsphere.rename_datastore('current_ds_name', 'new_ds_name') + vsphere.rename_datastore("current_ds_name", "new_ds_name") self.mock_rename_datastore.assert_called_once_with( - self.mock_ds_ref, 'new_ds_name') + self.mock_ds_ref, "new_ds_name" + ) class _GetProxyTargetTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.modules.vsphere._get_proxy_target - ''' + """ def setup_loader_modules(self): return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - 'get_proxy_type': MagicMock(return_value='esxdatacenter') + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "get_proxy_type": MagicMock(return_value="esxdatacenter"), } } def setUp(self): - attrs = (('mock_si', MagicMock()), - ('mock_dc', MagicMock()), - ('mock_cl', MagicMock()), - ('mock_root', MagicMock())) + attrs = ( + ("mock_si", MagicMock()), + ("mock_dc", MagicMock()), + ("mock_cl", MagicMock()), + ("mock_root", MagicMock()), + ) for attr, mock_obj in attrs: setattr(self, attr, mock_obj) self.addCleanup(delattr, self, attr) - attrs = (('mock_get_datacenter', MagicMock(return_value=self.mock_dc)), - ('mock_get_cluster', MagicMock(return_value=self.mock_cl)), - ('mock_get_root_folder', - MagicMock(return_value=self.mock_root))) + attrs = ( + ("mock_get_datacenter", MagicMock(return_value=self.mock_dc)), + ("mock_get_cluster", MagicMock(return_value=self.mock_cl)), + ("mock_get_root_folder", MagicMock(return_value=self.mock_root)), + ) for attr, mock_obj in attrs: setattr(self, attr, mock_obj) self.addCleanup(delattr, self, attr) patches = ( - ('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxcluster')), - ('salt.utils.vmware.is_connection_to_a_vcenter', - MagicMock(return_value=True)), - ('salt.modules.vsphere._get_esxcluster_proxy_details', - MagicMock(return_value=(None, None, None, None, None, None, None, - None, 'datacenter', 'cluster'))), - ('salt.modules.vsphere._get_esxdatacenter_proxy_details', - MagicMock(return_value=(None, None, None, None, None, None, None, - None, 'datacenter'))), - ('salt.utils.vmware.get_datacenter', self.mock_get_datacenter), - ('salt.utils.vmware.get_cluster', self.mock_get_cluster), - ('salt.utils.vmware.get_root_folder', self.mock_get_root_folder)) + ( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value="esxcluster"), + ), + ( + "salt.utils.vmware.is_connection_to_a_vcenter", + MagicMock(return_value=True), + ), + ( + "salt.modules.vsphere._get_esxcluster_proxy_details", + MagicMock( + return_value=( + None, + None, + None, + None, + None, + None, + None, + None, + "datacenter", + "cluster", + ) + ), + ), + ( + "salt.modules.vsphere._get_esxdatacenter_proxy_details", + MagicMock( + return_value=( + None, + None, + None, + None, + None, + None, + None, + None, + "datacenter", + ) + ), + ), + ("salt.utils.vmware.get_datacenter", self.mock_get_datacenter), + ("salt.utils.vmware.get_cluster", self.mock_get_cluster), + ("salt.utils.vmware.get_root_folder", self.mock_get_root_folder), + ) for module, mock_obj in patches: patcher = patch(module, mock_obj) patcher.start() self.addCleanup(patcher.stop) def test_supported_proxies(self): - supported_proxies = ['esxcluster', 'esxdatacenter'] + supported_proxies = ["esxcluster", "esxdatacenter"] for proxy_type in supported_proxies: - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value=proxy_type)): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value=proxy_type), + ): vsphere._get_proxy_target(self.mock_si) def test_connected_to_esxi(self): - with patch('salt.utils.vmware.is_connection_to_a_vcenter', - MagicMock(return_value=False)): + with patch( + "salt.utils.vmware.is_connection_to_a_vcenter", + MagicMock(return_value=False), + ): with self.assertRaises(CommandExecutionError) as excinfo: vsphere._get_proxy_target(self.mock_si) - self.assertEqual(excinfo.exception.strerror, - '\'_get_proxy_target\' not supported when ' - 'connected via the ESXi host') + self.assertEqual( + excinfo.exception.strerror, + "'_get_proxy_target' not supported when " "connected via the ESXi host", + ) def test_get_cluster_call(self): vsphere._get_proxy_target(self.mock_si) - self.mock_get_datacenter.assert_called_once_with(self.mock_si, - 'datacenter') - self.mock_get_cluster.assert_called_once_with(self.mock_dc, 'cluster') + self.mock_get_datacenter.assert_called_once_with(self.mock_si, "datacenter") + self.mock_get_cluster.assert_called_once_with(self.mock_dc, "cluster") def test_esxcluster_proxy_return(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxcluster')): + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="esxcluster") + ): ret = vsphere._get_proxy_target(self.mock_si) self.assertEqual(ret, self.mock_cl) def test_get_datacenter_call(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxdatacenter')): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value="esxdatacenter"), + ): vsphere._get_proxy_target(self.mock_si) - self.mock_get_datacenter.assert_called_once_with(self.mock_si, - 'datacenter') + self.mock_get_datacenter.assert_called_once_with(self.mock_si, "datacenter") self.assertEqual(self.mock_get_cluster.call_count, 0) def test_esxdatacenter_proxy_return(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='esxdatacenter')): + with patch( + "salt.modules.vsphere.get_proxy_type", + MagicMock(return_value="esxdatacenter"), + ): ret = vsphere._get_proxy_target(self.mock_si) self.assertEqual(ret, self.mock_dc) def test_vcenter_proxy_return(self): - with patch('salt.modules.vsphere.get_proxy_type', - MagicMock(return_value='vcenter')): + with patch( + "salt.modules.vsphere.get_proxy_type", MagicMock(return_value="vcenter") + ): ret = vsphere._get_proxy_target(self.mock_si) self.mock_get_root_folder.assert_called_once_with(self.mock_si) self.assertEqual(ret, self.mock_root) -@skipIf(not HAS_VSPHERE_SDK, 'The \'vsphere-automation-sdk\' library is missing') +@skipIf(not HAS_VSPHERE_SDK, "The 'vsphere-automation-sdk' library is missing") class TestVSphereTagging(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for: - salt.modules.vsphere.create_tag_category - salt.modules.vsphere.create_tag @@ -1942,141 +2511,129 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): - salt.modules.vsphere.list_tags - salt.modules.vsphere.attach_tags - salt.modules.vsphere.list_attached_tags - ''' + """ def setup_loader_modules(self): return { vsphere: { - '__virtual__': MagicMock(return_value='vsphere'), - '_get_proxy_connection_details': MagicMock(), - 'get_proxy_type': MagicMock(return_value='vcenter') + "__virtual__": MagicMock(return_value="vsphere"), + "_get_proxy_connection_details": MagicMock(), + "get_proxy_type": MagicMock(return_value="vcenter"), } } # Grains for expected __salt__ calls - details = { - key: None for key in [ - 'vcenter', 'username', 'password'] - } + details = {key: None for key in ["vcenter", "username", "password"]} # Function attributes func_attrs = { - key: None for key in [ - 'category_id', 'object_id', 'tag_id', 'name', - 'description', 'cardinality'] + key: None + for key in [ + "category_id", + "object_id", + "tag_id", + "name", + "description", + "cardinality", + ] } # Expected returns create_tag_category = { - 'Category created': - 'urn:vmomi:InventoryServiceTag:' - 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + "Category created": "urn:vmomi:InventoryServiceTag:" + "bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL" } create_tag = { - 'Tag created': - 'urn:vmomi:InventoryServiceTag:' - 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + "Tag created": "urn:vmomi:InventoryServiceTag:" + "bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL" } delete_tag_category = { - 'Category deleted': - 'urn:vmomi:InventoryServiceTag:' - 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + "Category deleted": "urn:vmomi:InventoryServiceTag:" + "bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL" } delete_tag = { - 'Tag deleted': - 'urn:vmomi:InventoryServiceTag:' - 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + "Tag deleted": "urn:vmomi:InventoryServiceTag:" + "bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL" } list_tag_categories_return = [ - 'urn:vmomi:InventoryServiceCategory:' - 'b13f4959-a3f3-48d0-8080-15bb586b4355:GLOBAL', - 'urn:vmomi:InventoryServiceCategory:' - 'f4d41f02-c317-422d-9013-dcbebfcd54ad:GLOBAL', - 'urn:vmomi:InventoryServiceCategory:' - '2db5b00b-f211-4bba-ba42-e2658ebbb283:GLOBAL', - 'urn:vmomi:InventoryServiceCategory:' - 'cd847c3c-687c-4bd9-8e5a-0eb536f0a01d:GLOBAL', - 'urn:vmomi:InventoryServiceCategory:' - 'd51c24f9-cffb-4ce0-af56-7f18b6e649af:GLOBAL' + "urn:vmomi:InventoryServiceCategory:" + "b13f4959-a3f3-48d0-8080-15bb586b4355:GLOBAL", + "urn:vmomi:InventoryServiceCategory:" + "f4d41f02-c317-422d-9013-dcbebfcd54ad:GLOBAL", + "urn:vmomi:InventoryServiceCategory:" + "2db5b00b-f211-4bba-ba42-e2658ebbb283:GLOBAL", + "urn:vmomi:InventoryServiceCategory:" + "cd847c3c-687c-4bd9-8e5a-0eb536f0a01d:GLOBAL", + "urn:vmomi:InventoryServiceCategory:" + "d51c24f9-cffb-4ce0-af56-7f18b6e649af:GLOBAL", ] list_tags_return = [ - 'urn:vmomi:InventoryServiceTag:' - 'a584a83b-3015-45ad-8057-a3630613052f:GLOBAL', - 'urn:vmomi:InventoryServiceTag:' - 'db08019c-15de-4bbf-be46-d81aaf8d25c0:GLOBAL', - 'urn:vmomi:InventoryServiceTag:' - 'b55ecc77-f4a5-49f8-ab52-38865467cfbe:GLOBAL', - 'urn:vmomi:InventoryServiceTag:' - 'f009ab1b-e1b5-4c40-b8f7-951d9d716b39:GLOBAL', - 'urn:vmomi:InventoryServiceTag:' - '102bb4c5-9b76-4d6c-882a-76a91ee3edcc:GLOBAL', - 'urn:vmomi:InventoryServiceTag:' - 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL', - 'urn:vmomi:InventoryServiceTag:' - '71d30f2d-bb23-48e1-995f-630adfb0dc89:GLOBAL' + "urn:vmomi:InventoryServiceTag:a584a83b-3015-45ad-8057-a3630613052f:GLOBAL", + "urn:vmomi:InventoryServiceTag:db08019c-15de-4bbf-be46-d81aaf8d25c0:GLOBAL", + "urn:vmomi:InventoryServiceTag:b55ecc77-f4a5-49f8-ab52-38865467cfbe:GLOBAL", + "urn:vmomi:InventoryServiceTag:f009ab1b-e1b5-4c40-b8f7-951d9d716b39:GLOBAL", + "urn:vmomi:InventoryServiceTag:102bb4c5-9b76-4d6c-882a-76a91ee3edcc:GLOBAL", + "urn:vmomi:InventoryServiceTag:bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL", + "urn:vmomi:InventoryServiceTag:71d30f2d-bb23-48e1-995f-630adfb0dc89:GLOBAL", ] list_attached_tags_return = [ - 'urn:vmomi:InventoryServiceTag:' - 'b55ecc77-f4a5-49f8-ab52-38865467cfbe:GLOBAL', - 'urn:vmomi:InventoryServiceTag:' - 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + "urn:vmomi:InventoryServiceTag:b55ecc77-f4a5-49f8-ab52-38865467cfbe:GLOBAL", + "urn:vmomi:InventoryServiceTag:bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL", ] list_create_category_return = [ - 'urn:vmomi:InventoryServiceCategory:' - '0af54c2d-e8cd-4248-931e-2f5807d8c477:GLOBAL' + "urn:vmomi:InventoryServiceCategory:" + "0af54c2d-e8cd-4248-931e-2f5807d8c477:GLOBAL" ] list_create_tag_return = [ - 'urn:vmomi:InventoryServiceCategory:' - '0af54c2d-e8cd-4248-931e-2f5807d8c477:GLOBAL' + "urn:vmomi:InventoryServiceCategory:" + "0af54c2d-e8cd-4248-931e-2f5807d8c477:GLOBAL" ] attach_tags_return = { - 'Tag attached': - 'urn:vmomi:InventoryServiceTag:' - 'bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL' + "Tag attached": "urn:vmomi:InventoryServiceTag:" + "bb0350b4-85db-46b0-a726-e7c5989fc857:GLOBAL" } def test_create_tag_category_client_none(self): get_details = MagicMock(return_value=self.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=None - ) as get_vsphere_client: + 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=None + ) as get_vsphere_client: ret = vsphere.create_tag_category( - self.func_attrs['name'], - self.func_attrs['description'], - self.func_attrs['cardinality']) + 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, {'Category created': None}) + self.assertEqual(ret, {"Category created": None}) def test_create_tag_category_client(self): get_details = MagicMock(return_value=self.details) @@ -2087,34 +2644,37 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): Category=Mock( CreateSpec=Mock(return_value=Mock()), create=Mock( - return_value=self.create_tag_category[ - 'Category created'])))) + 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: - 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, "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: ret = vsphere.create_tag_category( - self.func_attrs['name'], - self.func_attrs['description'], - self.func_attrs['cardinality']) + self.func_attrs["name"], + self.func_attrs["description"], + self.func_attrs["cardinality"], + ) # Check function calls and return data get_proxy_type.assert_called_once() @@ -2127,36 +2687,34 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): get_details = MagicMock(return_value=self.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=None - ) as get_vsphere_client: + 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=None + ) as get_vsphere_client: ret = vsphere.create_tag( - self.func_attrs['name'], - self.func_attrs['description'], - self.func_attrs['cardinality']) + 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, {'Tag created': None}) + self.assertEqual(ret, {"Tag created": None}) def test_create_tag_client(self): get_details = MagicMock(return_value=self.details) @@ -2166,34 +2724,36 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): tagging=Mock( Tag=Mock( CreateSpec=Mock(return_value=Mock()), - create=Mock(return_value=self.create_tag[ - 'Tag created'])))) + create=Mock(return_value=self.create_tag["Tag created"]), + ) + ) + ) # 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, "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: ret = vsphere.create_tag( - self.func_attrs['name'], - self.func_attrs['description'], - self.func_attrs['cardinality']) + 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() @@ -2205,34 +2765,32 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): get_details = MagicMock(return_value=self.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=None - ) as get_vsphere_client: + 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=None + ) as get_vsphere_client: ret = vsphere.delete_tag_category( - self.func_attrs['category_id']) + 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, {'Category deleted': None}) + self.assertEqual(ret, {"Category deleted": None}) def test_delete_tag_category_client(self): get_details = MagicMock(return_value=self.details) @@ -2241,32 +2799,36 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): mock_client = Mock( tagging=Mock( Category=Mock( - delete=Mock(return_value=self.delete_tag_category[ - 'Category deleted'])))) + 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: - 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, "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: ret = vsphere.delete_tag_category( - self.func_attrs['category_id']) + self.func_attrs["category_id"] + ) # Check function calls and return data get_proxy_type.assert_called_once() @@ -2279,33 +2841,30 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): get_details = MagicMock(return_value=self.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=None - ) as get_vsphere_client: - ret = vsphere.delete_tag( - self.func_attrs['tag_id']) + 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=None + ) as get_vsphere_client: + ret = vsphere.delete_tag(self.func_attrs["tag_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, {'Tag deleted': None}) + self.assertEqual(ret, {"Tag deleted": None}) def test_delete_tag_client(self): get_details = MagicMock(return_value=self.details) @@ -2313,33 +2872,31 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): # Mock CreateSpec object and create objects mock_client = Mock( tagging=Mock( - Tag=Mock( - delete=Mock(return_value=self.delete_tag[ - 'Tag deleted'])))) + Tag=Mock(delete=Mock(return_value=self.delete_tag["Tag deleted"])) + ) + ) # 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: - ret = vsphere.delete_tag( - self.func_attrs['tag_id']) + 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: + ret = vsphere.delete_tag(self.func_attrs["tag_id"]) # Check function calls and return data get_proxy_type.assert_called_once() @@ -2352,33 +2909,30 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): get_details = MagicMock(return_value=self.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=None - ) as get_vsphere_client: + 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=None + ) as get_vsphere_client: ret = vsphere.list_tag_categories() # 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, {'Categories': None}) + self.assertEqual(ret, {"Categories": None}) def test_list_tag_categories_client(self): get_details = MagicMock(return_value=self.details) @@ -2386,31 +2940,30 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): # Mock CreateSpec object and create objects mock_client = Mock( tagging=Mock( - Category=Mock( - list=Mock(return_value=self.list_tag_categories_return - )))) + 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: - 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, "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: ret = vsphere.list_tag_categories() get_proxy_type.assert_called_once() @@ -2418,115 +2971,103 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): get_service_instance.assert_called_once() get_vsphere_client.assert_called_once() self.assertEqual( - ret, {'Categories': - self.list_tag_categories_return}) + ret, {"Categories": self.list_tag_categories_return} + ) def test_list_tags_client_none(self): get_details = MagicMock(return_value=self.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=None - ) as get_vsphere_client: + 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=None + ) as get_vsphere_client: # Check function calls and return ret = vsphere.list_tags() 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, {'Tags': None}) + self.assertEqual(ret, {"Tags": None}) def test_list_tags_client(self): get_details = MagicMock(return_value=self.details) # Mock CreateSpec object and create objects mock_client = Mock( - tagging=Mock( - Tag=Mock( - list=Mock(return_value=self.list_tags_return - )))) + 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: + 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() 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, - {'Tags': self.list_tags_return}) + self.assertEqual(ret, {"Tags": self.list_tags_return}) def test_list_attached_tags_client_none(self): get_details = MagicMock(return_value=self.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=None - ) as get_vsphere_client: - with patch.object(vsphere, - 'DynamicID' - ) as dynamic_id: + 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=None + ) as get_vsphere_client: + with patch.object(vsphere, "DynamicID") as dynamic_id: # Check function calls and return - ret = vsphere.list_attached_tags('object_id') + ret = vsphere.list_attached_tags("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': None}) + self.assertEqual(ret, {"Attached tags": None}) def test_list_attached_tags_client(self): get_details = MagicMock(return_value=self.details) @@ -2535,78 +3076,76 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): mock_client = Mock( tagging=Mock( TagAssociation=Mock( - list_attached_tags=Mock( - return_value=self.list_attached_tags_return - )))) + 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: - 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: + 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.list_attached_tags( - self.func_attrs['object_id']) + 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}) + ret, + {"Attached tags": self.list_attached_tags_return}, + ) def test_attach_tags_client_none(self): get_details = MagicMock(return_value=self.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=None - ) as get_vsphere_client: + 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=None + ) as get_vsphere_client: # Check function calls and return ret = vsphere.attach_tag( - object_id=self.func_attrs['object_id'], - tag_id=self.func_attrs['tag_id']) + 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': None}) + self.assertEqual(ret, {"Tag attached": None}) def test_attach_tags_client(self): get_details = MagicMock(return_value=self.details) @@ -2615,41 +3154,42 @@ class TestVSphereTagging(TestCase, LoaderModuleMockMixin): mock_client = Mock( tagging=Mock( TagAssociation=Mock( - attach=Mock(return_value=self.list_attached_tags_return - )))) + 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: + 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']) + 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}) + ret, + {"Tag attached": self.list_attached_tags_return}, + ) diff --git a/tests/unit/modules/test_webutil.py b/tests/unit/modules/test_webutil.py index eac01140075..b318e46db40 100644 --- a/tests/unit/modules/test_webutil.py +++ b/tests/unit/modules/test_webutil.py @@ -1,59 +1,61 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.webutil as htpasswd +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class HtpasswdTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.webutil - ''' + """ + def setup_loader_modules(self): return {htpasswd: {}} # 'useradd' function tests: 1 def test_useradd(self): - ''' + """ Test if it adds an HTTP user using the htpasswd command - ''' - mock = MagicMock(return_value={'out': 'Salt'}) - with patch.dict(htpasswd.__salt__, {'cmd.run_all': mock}), \ - patch('os.path.exists', MagicMock(return_value=True)): - self.assertDictEqual(htpasswd.useradd('/etc/httpd/htpasswd', - 'larry', 'badpassword'), - {'out': 'Salt'}) + """ + mock = MagicMock(return_value={"out": "Salt"}) + with patch.dict(htpasswd.__salt__, {"cmd.run_all": mock}), patch( + "os.path.exists", MagicMock(return_value=True) + ): + self.assertDictEqual( + htpasswd.useradd("/etc/httpd/htpasswd", "larry", "badpassword"), + {"out": "Salt"}, + ) # 'userdel' function tests: 2 def test_userdel(self): - ''' + """ Test if it delete an HTTP user from the specified htpasswd file. - ''' - mock = MagicMock(return_value='Salt') - with patch.dict(htpasswd.__salt__, {'cmd.run': mock}), \ - patch('os.path.exists', MagicMock(return_value=True)): - self.assertEqual(htpasswd.userdel('/etc/httpd/htpasswd', - 'larry'), ['Salt']) + """ + mock = MagicMock(return_value="Salt") + with patch.dict(htpasswd.__salt__, {"cmd.run": mock}), patch( + "os.path.exists", MagicMock(return_value=True) + ): + self.assertEqual(htpasswd.userdel("/etc/httpd/htpasswd", "larry"), ["Salt"]) def test_userdel_missing_htpasswd(self): - ''' + """ Test if it returns error when no htpasswd file exists - ''' - with patch('os.path.exists', MagicMock(return_value=False)): - self.assertEqual(htpasswd.userdel('/etc/httpd/htpasswd', 'larry'), - 'Error: The specified htpasswd file does not exist') + """ + with patch("os.path.exists", MagicMock(return_value=False)): + self.assertEqual( + htpasswd.userdel("/etc/httpd/htpasswd", "larry"), + "Error: The specified htpasswd file does not exist", + ) diff --git a/tests/unit/modules/test_win_autoruns.py b/tests/unit/modules/test_win_autoruns.py index 011647e3b60..ebe9eaeecaa 100644 --- a/tests/unit/modules/test_win_autoruns.py +++ b/tests/unit/modules/test_win_autoruns.py @@ -1,44 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_autoruns as win_autoruns -KEY = ['HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run', - 'HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run /reg:64', - 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run'] +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +KEY = [ + "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", + "HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run /reg:64", + "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run", +] class WinAutorunsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_autoruns - ''' + """ + def setup_loader_modules(self): return {win_autoruns: {}} # 'list_' function tests: 1 def test_list(self): - ''' + """ Test if it enables win_autoruns the service on the server - ''' - with patch('os.listdir', MagicMock(return_value=[])): - ret = {KEY[0]: ['SALT'], KEY[1]: ['SALT'], KEY[2]: ['SALT']} - mock = MagicMock(return_value='Windows 7') - with patch.dict(win_autoruns.__grains__, {'osfullname': mock}): - mock = MagicMock(return_value='SALT') - with patch.dict(win_autoruns.__salt__, {'cmd.run': mock}): + """ + with patch("os.listdir", MagicMock(return_value=[])): + ret = {KEY[0]: ["SALT"], KEY[1]: ["SALT"], KEY[2]: ["SALT"]} + mock = MagicMock(return_value="Windows 7") + with patch.dict(win_autoruns.__grains__, {"osfullname": mock}): + mock = MagicMock(return_value="SALT") + with patch.dict(win_autoruns.__salt__, {"cmd.run": mock}): self.assertDictEqual(win_autoruns.list_(), ret) diff --git a/tests/unit/modules/test_win_certutil.py b/tests/unit/modules/test_win_certutil.py index 213991f7757..9455120c1e6 100644 --- a/tests/unit/modules/test_win_certutil.py +++ b/tests/unit/modules/test_win_certutil.py @@ -1,94 +1,108 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_certutil as certutil # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class CertUtilTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {certutil: {}} def test_get_serial(self): - ''' + """ Test getting the serial number from a certificate - ''' - expected = '180720d39cd2db3244ba037417241e90' - mock = MagicMock(return_value=( - 'CertInfo\r\n' - 'Cert Serial Number: 180720d39cd2db3244ba037417241e90\r\n' - '\r\n' - 'OtherStuff')) - with patch.dict(certutil.__salt__, {'cmd.run': mock}): - out = certutil.get_cert_serial('/path/to/cert.cer') + """ + expected = "180720d39cd2db3244ba037417241e90" + mock = MagicMock( + return_value=( + "CertInfo\r\n" + "Cert Serial Number: 180720d39cd2db3244ba037417241e90\r\n" + "\r\n" + "OtherStuff" + ) + ) + with patch.dict(certutil.__salt__, {"cmd.run": mock}): + out = certutil.get_cert_serial("/path/to/cert.cer") mock.assert_called_once_with( - 'certutil.exe -silent -verify /path/to/cert.cer') + "certutil.exe -silent -verify /path/to/cert.cer" + ) self.assertEqual(expected, out) def test_get_serials(self): - ''' + """ Test getting all the serial numbers from a store - ''' - expected = ['180720d39cd2db3244ba037417241e90', - '1768ac4e5b72bf1d0df0df118b34b959'] - mock = MagicMock(return_value=( - 'CertInfo\r\n' - '================ Certificate 0 ================\r\n' - 'Serial Number: 180720d39cd2db3244ba037417241e90\r\n' - 'OtherStuff\r\n' - '\r\n' - '================ Certificate 1 ================\r\n' - 'Serial Number: 1768ac4e5b72bf1d0df0df118b34b959\r\n' - 'OtherStuff')) - with patch.dict(certutil.__salt__, {'cmd.run': mock}): - out = certutil.get_stored_cert_serials('TrustedPublisher') - mock.assert_called_once_with( - 'certutil.exe -store TrustedPublisher') + """ + expected = [ + "180720d39cd2db3244ba037417241e90", + "1768ac4e5b72bf1d0df0df118b34b959", + ] + mock = MagicMock( + return_value=( + "CertInfo\r\n" + "================ Certificate 0 ================\r\n" + "Serial Number: 180720d39cd2db3244ba037417241e90\r\n" + "OtherStuff\r\n" + "\r\n" + "================ Certificate 1 ================\r\n" + "Serial Number: 1768ac4e5b72bf1d0df0df118b34b959\r\n" + "OtherStuff" + ) + ) + with patch.dict(certutil.__salt__, {"cmd.run": mock}): + out = certutil.get_stored_cert_serials("TrustedPublisher") + mock.assert_called_once_with("certutil.exe -store TrustedPublisher") self.assertEqual(expected, out) def test_add_store(self): - ''' + """ Test adding a certificate to a specific store - ''' - cmd_mock = MagicMock(return_value=( - 'CertInfo\r\n' - '================ Certificate 0 ================\r\n' - 'Serial Number: 180720d39cd2db3244ba037417241e90\r\n' - 'OtherStuff')) - cache_mock = MagicMock(return_value='/tmp/cert.cer') - with patch.dict(certutil.__salt__, {'cmd.run': cmd_mock, - 'cp.cache_file': cache_mock}): - certutil.add_store('salt://path/to/file', 'TrustedPublisher') + """ + cmd_mock = MagicMock( + return_value=( + "CertInfo\r\n" + "================ Certificate 0 ================\r\n" + "Serial Number: 180720d39cd2db3244ba037417241e90\r\n" + "OtherStuff" + ) + ) + cache_mock = MagicMock(return_value="/tmp/cert.cer") + with patch.dict( + certutil.__salt__, {"cmd.run": cmd_mock, "cp.cache_file": cache_mock} + ): + certutil.add_store("salt://path/to/file", "TrustedPublisher") cmd_mock.assert_called_once_with( - 'certutil.exe -addstore TrustedPublisher /tmp/cert.cer') - cache_mock.assert_called_once_with('salt://path/to/file', 'base') + "certutil.exe -addstore TrustedPublisher /tmp/cert.cer" + ) + cache_mock.assert_called_once_with("salt://path/to/file", "base") def test_del_store(self): - ''' + """ Test removing a certificate to a specific store - ''' - with patch('salt.modules.win_certutil.get_cert_serial') as cert_serial_mock: - cmd_mock = MagicMock(return_value=( - 'CertInfo\r\n' - '================ Certificate 0 ================\r\n' - 'Serial Number: 180720d39cd2db3244ba037417241e90\r\n' - 'OtherStuff')) - cache_mock = MagicMock(return_value='/tmp/cert.cer') - cert_serial_mock.return_value = 'ABCDEF' - with patch.dict(certutil.__salt__, {'cmd.run': cmd_mock, - 'cp.cache_file': cache_mock}): - certutil.del_store('salt://path/to/file', 'TrustedPublisher') + """ + with patch("salt.modules.win_certutil.get_cert_serial") as cert_serial_mock: + cmd_mock = MagicMock( + return_value=( + "CertInfo\r\n" + "================ Certificate 0 ================\r\n" + "Serial Number: 180720d39cd2db3244ba037417241e90\r\n" + "OtherStuff" + ) + ) + cache_mock = MagicMock(return_value="/tmp/cert.cer") + cert_serial_mock.return_value = "ABCDEF" + with patch.dict( + certutil.__salt__, {"cmd.run": cmd_mock, "cp.cache_file": cache_mock} + ): + certutil.del_store("salt://path/to/file", "TrustedPublisher") cmd_mock.assert_called_once_with( - 'certutil.exe -delstore TrustedPublisher ABCDEF') - cache_mock.assert_called_once_with('salt://path/to/file', 'base') + "certutil.exe -delstore TrustedPublisher ABCDEF" + ) + cache_mock.assert_called_once_with("salt://path/to/file", "base") diff --git a/tests/unit/modules/test_win_disk.py b/tests/unit/modules/test_win_disk.py index 95d1ec47645..91f0c4b4911 100644 --- a/tests/unit/modules/test_win_disk.py +++ b/tests/unit/modules/test_win_disk.py @@ -1,66 +1,76 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.modules.win_disk as win_disk # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -# Import Salt Libs -import salt.modules.win_disk as win_disk - class MockKernel32(object): - ''' + """ Mock windll class - ''' + """ + def __init__(self): pass @staticmethod def GetLogicalDrives(): - ''' + """ Mock GetLogicalDrives method - ''' + """ return 1 class MockWindll(object): - ''' + """ Mock windll class - ''' + """ + def __init__(self): self.kernel32 = MockKernel32() class MockCtypes(object): - ''' + """ Mock ctypes class - ''' + """ + def __init__(self): self.windll = MockWindll() class WinDiskTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_disk - ''' + """ + def setup_loader_modules(self): - return {win_disk: {'ctypes': MockCtypes()}} + return {win_disk: {"ctypes": MockCtypes()}} # 'usage' function tests: 1 def test_usage(self): - ''' + """ Test if it return usage information for volumes mounted on this minion. - ''' - self.assertDictEqual(win_disk.usage(), - {'A:\\': {'available': None, - '1K-blocks': None, - 'used': None, - 'capacity': None, - 'filesystem': 'A:\\'}}) + """ + self.assertDictEqual( + win_disk.usage(), + { + "A:\\": { + "available": None, + "1K-blocks": None, + "used": None, + "capacity": None, + "filesystem": "A:\\", + } + }, + ) diff --git a/tests/unit/modules/test_win_dism.py b/tests/unit/modules/test_win_dism.py index dfd348f604e..a7e8683f504 100644 --- a/tests/unit/modules/test_win_dism.py +++ b/tests/unit/modules/test_win_dism.py @@ -1,235 +1,331 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_dism as dism # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class WinDismTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {dism: {}} def test_add_capability(self): - ''' + """ Test installing a capability with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): - with patch.dict(dism.__grains__, {'osversion': 10}): + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): + with patch.dict(dism.__grains__, {"osversion": 10}): dism.add_capability("test") mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Add-Capability', - '/CapabilityName:test', '/NoRestart']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Add-Capability", + "/CapabilityName:test", + "/NoRestart", + ] + ) def test_add_capability_with_extras(self): - ''' + """ Test installing a capability with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): - with patch.dict(dism.__grains__, {'osversion': 10}): + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): + with patch.dict(dism.__grains__, {"osversion": 10}): dism.add_capability("test", "life", True) mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Add-Capability', - '/CapabilityName:test', '/Source:life', '/LimitAccess', - '/NoRestart']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Add-Capability", + "/CapabilityName:test", + "/Source:life", + "/LimitAccess", + "/NoRestart", + ] + ) def test_remove_capability(self): - ''' + """ Test uninstalling a capability with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): - with patch.dict(dism.__grains__, {'osversion': 10}): + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): + with patch.dict(dism.__grains__, {"osversion": 10}): dism.remove_capability("test") mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Remove-Capability', - '/CapabilityName:test', '/NoRestart']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Remove-Capability", + "/CapabilityName:test", + "/NoRestart", + ] + ) def test_get_capabilities(self): - ''' + """ Test getting all the capabilities - ''' - capabilties = "Capability Identity : Capa1\r\n State : Installed\r\n" \ - "Capability Identity : Capa2\r\n State : Disabled\r\n" + """ + capabilties = ( + "Capability Identity : Capa1\r\n State : Installed\r\n" + "Capability Identity : Capa2\r\n State : Disabled\r\n" + ) mock = MagicMock(return_value=capabilties) - with patch.dict(dism.__salt__, {'cmd.run': mock}): - with patch.dict(dism.__grains__, {'osversion': 10}): + with patch.dict(dism.__salt__, {"cmd.run": mock}): + with patch.dict(dism.__grains__, {"osversion": 10}): out = dism.get_capabilities() mock.assert_called_once_with( - [dism.bin_dism, '/English', '/Online', '/Get-Capabilities']) - self.assertEqual(out, ['Capa1', 'Capa2']) + [dism.bin_dism, "/English", "/Online", "/Get-Capabilities"] + ) + self.assertEqual(out, ["Capa1", "Capa2"]) def test_installed_capabilities(self): - ''' + """ Test getting all the installed capabilities - ''' - capabilties = "Capability Identity : Capa1\r\n State : Installed\r\n" \ - "Capability Identity : Capa2\r\n State : Disabled\r\n" + """ + capabilties = ( + "Capability Identity : Capa1\r\n State : Installed\r\n" + "Capability Identity : Capa2\r\n State : Disabled\r\n" + ) mock = MagicMock(return_value=capabilties) - with patch.dict(dism.__salt__, {'cmd.run': mock}): - with patch.dict(dism.__grains__, {'osversion': 10}): + with patch.dict(dism.__salt__, {"cmd.run": mock}): + with patch.dict(dism.__grains__, {"osversion": 10}): out = dism.installed_capabilities() mock.assert_called_once_with( - [dism.bin_dism, '/English', '/Online', '/Get-Capabilities']) + [dism.bin_dism, "/English", "/Online", "/Get-Capabilities"] + ) self.assertEqual(out, ["Capa1"]) def test_available_capabilities(self): - ''' + """ Test getting all the available capabilities - ''' - capabilties = "Capability Identity : Capa1\r\n State : Installed\r\n" \ - "Capability Identity : Capa2\r\n State : Not Present\r\n" + """ + capabilties = ( + "Capability Identity : Capa1\r\n State : Installed\r\n" + "Capability Identity : Capa2\r\n State : Not Present\r\n" + ) mock = MagicMock(return_value=capabilties) - with patch.dict(dism.__salt__, {'cmd.run': mock}): - with patch.dict(dism.__grains__, {'osversion': 10}): + with patch.dict(dism.__salt__, {"cmd.run": mock}): + with patch.dict(dism.__grains__, {"osversion": 10}): out = dism.available_capabilities() mock.assert_called_once_with( - [dism.bin_dism, '/English', '/Online', '/Get-Capabilities']) + [dism.bin_dism, "/English", "/Online", "/Get-Capabilities"] + ) self.assertEqual(out, ["Capa2"]) def test_add_feature(self): - ''' + """ Test installing a feature with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): dism.add_feature("test") mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Enable-Feature', - '/FeatureName:test', '/NoRestart']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Enable-Feature", + "/FeatureName:test", + "/NoRestart", + ] + ) def test_add_feature_with_extras(self): - ''' + """ Test installing a feature with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): - dism.add_feature('sponge', 'bob', 'C:\\temp', True, True) + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): + dism.add_feature("sponge", "bob", "C:\\temp", True, True) mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Enable-Feature', - '/FeatureName:sponge', '/PackageName:bob', '/Source:C:\\temp', - '/LimitAccess', '/All', '/NoRestart']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Enable-Feature", + "/FeatureName:sponge", + "/PackageName:bob", + "/Source:C:\\temp", + "/LimitAccess", + "/All", + "/NoRestart", + ] + ) def test_remove_feature(self): - ''' + """ Test uninstalling a capability with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): dism.remove_feature("test") mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Disable-Feature', - '/FeatureName:test', '/NoRestart']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Disable-Feature", + "/FeatureName:test", + "/NoRestart", + ] + ) def test_remove_feature_with_extras(self): - ''' + """ Test uninstalling a capability with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): - dism.remove_feature('sponge', True) + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): + dism.remove_feature("sponge", True) mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Disable-Feature', - '/FeatureName:sponge', '/Remove', '/NoRestart']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Disable-Feature", + "/FeatureName:sponge", + "/Remove", + "/NoRestart", + ] + ) def test_get_features(self): - ''' + """ Test getting all the features - ''' - features = "Feature Name : Capa1\r\n State : Enabled\r\n" \ - "Feature Name : Capa2\r\n State : Disabled\r\n" + """ + features = ( + "Feature Name : Capa1\r\n State : Enabled\r\n" + "Feature Name : Capa2\r\n State : Disabled\r\n" + ) mock = MagicMock(return_value=features) - with patch.dict(dism.__salt__, {'cmd.run': mock}): + with patch.dict(dism.__salt__, {"cmd.run": mock}): out = dism.get_features() mock.assert_called_once_with( - [dism.bin_dism, '/English', '/Online', '/Get-Features']) - self.assertEqual(out, ['Capa1', 'Capa2']) + [dism.bin_dism, "/English", "/Online", "/Get-Features"] + ) + self.assertEqual(out, ["Capa1", "Capa2"]) def test_installed_features(self): - ''' + """ Test getting all the installed features - ''' - features = "Feature Name : Capa1\r\n State : Enabled\r\n" \ - "Feature Name : Capa2\r\n State : Disabled\r\n" + """ + features = ( + "Feature Name : Capa1\r\n State : Enabled\r\n" + "Feature Name : Capa2\r\n State : Disabled\r\n" + ) mock = MagicMock(return_value=features) - with patch.dict(dism.__salt__, {'cmd.run': mock}): + with patch.dict(dism.__salt__, {"cmd.run": mock}): out = dism.installed_features() mock.assert_called_once_with( - [dism.bin_dism, '/English', '/Online', '/Get-Features']) + [dism.bin_dism, "/English", "/Online", "/Get-Features"] + ) self.assertEqual(out, ["Capa1"]) def test_available_features(self): - ''' + """ Test getting all the available features - ''' - features = "Feature Name : Capa1\r\n State : Enabled\r\n" \ - "Feature Name : Capa2\r\n State : Disabled\r\n" + """ + features = ( + "Feature Name : Capa1\r\n State : Enabled\r\n" + "Feature Name : Capa2\r\n State : Disabled\r\n" + ) mock = MagicMock(return_value=features) - with patch.dict(dism.__salt__, {'cmd.run': mock}): + with patch.dict(dism.__salt__, {"cmd.run": mock}): out = dism.available_features() mock.assert_called_once_with( - [dism.bin_dism, '/English', '/Online', '/Get-Features']) + [dism.bin_dism, "/English", "/Online", "/Get-Features"] + ) self.assertEqual(out, ["Capa2"]) def test_add_package(self): - ''' + """ Test installing a package with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): dism.add_package("test") mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Add-Package', - '/PackagePath:test', '/NoRestart']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Add-Package", + "/PackagePath:test", + "/NoRestart", + ] + ) def test_add_package_with_extras(self): - ''' + """ Test installing a package with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): - dism.add_package('sponge', True, True) + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): + dism.add_package("sponge", True, True) mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Add-Package', - '/PackagePath:sponge', '/IgnoreCheck', '/PreventPending', - '/NoRestart']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Add-Package", + "/PackagePath:sponge", + "/IgnoreCheck", + "/PreventPending", + "/NoRestart", + ] + ) def test_remove_package(self): - ''' + """ Test uninstalling a package with DISM - ''' + """ mock = MagicMock() - with patch.dict(dism.__salt__, {'cmd.run_all': mock}): + with patch.dict(dism.__salt__, {"cmd.run_all": mock}): dism.remove_package("test") mock.assert_called_once_with( - [dism.bin_dism, '/Quiet', '/Online', '/Remove-Package', - '/NoRestart', '/PackagePath:test']) + [ + dism.bin_dism, + "/Quiet", + "/Online", + "/Remove-Package", + "/NoRestart", + "/PackagePath:test", + ] + ) def test_installed_packages(self): - ''' + """ Test getting all the installed features - ''' - features = "Package Identity : Capa1\r\n State : Installed\r\n" \ - "Package Identity : Capa2\r\n State : Installed\r\n" + """ + features = ( + "Package Identity : Capa1\r\n State : Installed\r\n" + "Package Identity : Capa2\r\n State : Installed\r\n" + ) mock = MagicMock(return_value=features) - with patch.dict(dism.__salt__, {'cmd.run': mock}): + with patch.dict(dism.__salt__, {"cmd.run": mock}): out = dism.installed_packages() mock.assert_called_once_with( - [dism.bin_dism, '/English', '/Online', '/Get-Packages']) - self.assertEqual(out, ['Capa1', 'Capa2']) + [dism.bin_dism, "/English", "/Online", "/Get-Packages"] + ) + self.assertEqual(out, ["Capa1", "Capa2"]) diff --git a/tests/unit/modules/test_win_dns_client.py b/tests/unit/modules/test_win_dns_client.py index 30025dfaca3..233c431ba3b 100644 --- a/tests/unit/modules/test_win_dns_client.py +++ b/tests/unit/modules/test_win_dns_client.py @@ -1,36 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import types +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch, Mock +import types # Import Salt Libs import salt.modules.win_dns_client as win_dns_client import salt.utils.stringutils +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase, skipIf + try: import wmi + HAS_WMI = True except ImportError: HAS_WMI = False class Mockwmi(object): - ''' + """ Mock wmi class - ''' - NetConnectionID = 'Local Area Connection' + """ + + NetConnectionID = "Local Area Connection" Index = 0 - DNSServerSearchOrder = ['10.1.1.10'] - Description = 'Local Area Connection' + DNSServerSearchOrder = ["10.1.1.10"] + Description = "Local Area Connection" DHCPEnabled = True def __init__(self): @@ -38,137 +41,148 @@ class Mockwmi(object): class Mockwinapi(object): - ''' + """ Mock winapi class - ''' + """ + def __init__(self): pass class winapi(object): - ''' + """ Mock winapi class - ''' + """ + def __init__(self): pass @staticmethod def Com(): - ''' + """ Mock Com method - ''' + """ return True -@skipIf(not HAS_WMI, 'WMI only available on Windows') +@skipIf(not HAS_WMI, "WMI only available on Windows") class WinDnsClientTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_dns_client - ''' + """ + def setup_loader_modules(self): # wmi and pythoncom modules are platform specific... - mock_pythoncom = types.ModuleType( - salt.utils.stringutils.to_str('pythoncom') - ) - sys_modules_patcher = patch.dict('sys.modules', - {'pythoncom': mock_pythoncom}) + mock_pythoncom = types.ModuleType(salt.utils.stringutils.to_str("pythoncom")) + sys_modules_patcher = patch.dict("sys.modules", {"pythoncom": mock_pythoncom}) sys_modules_patcher.start() self.addCleanup(sys_modules_patcher.stop) self.WMI = Mock() - self.addCleanup(delattr, self, 'WMI') - return {win_dns_client: {'wmi': wmi}} + self.addCleanup(delattr, self, "WMI") + return {win_dns_client: {"wmi": wmi}} # 'get_dns_servers' function tests: 1 def test_get_dns_servers(self): - ''' + """ Test if it return a list of the configured DNS servers of the specified interface. - ''' - with patch('salt.utils.winapi.Com', MagicMock()), \ - patch.object(self.WMI, 'Win32_NetworkAdapter', - return_value=[Mockwmi()]), \ - patch.object(self.WMI, 'Win32_NetworkAdapterConfiguration', - return_value=[Mockwmi()]), \ - patch.object(wmi, 'WMI', Mock(return_value=self.WMI)): - self.assertListEqual(win_dns_client.get_dns_servers - ('Local Area Connection'), - ['10.1.1.10']) + """ + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + self.WMI, "Win32_NetworkAdapter", return_value=[Mockwmi()] + ), patch.object( + self.WMI, "Win32_NetworkAdapterConfiguration", return_value=[Mockwmi()] + ), patch.object( + wmi, "WMI", Mock(return_value=self.WMI) + ): + self.assertListEqual( + win_dns_client.get_dns_servers("Local Area Connection"), ["10.1.1.10"] + ) - self.assertFalse(win_dns_client.get_dns_servers('Ethernet')) + self.assertFalse(win_dns_client.get_dns_servers("Ethernet")) # 'rm_dns' function tests: 1 def test_rm_dns(self): - ''' + """ Test if it remove the DNS server from the network interface. - ''' - with patch.dict(win_dns_client.__salt__, - {'cmd.retcode': MagicMock(return_value=0)}): - self.assertTrue(win_dns_client.rm_dns('10.1.1.10')) + """ + with patch.dict( + win_dns_client.__salt__, {"cmd.retcode": MagicMock(return_value=0)} + ): + self.assertTrue(win_dns_client.rm_dns("10.1.1.10")) # 'add_dns' function tests: 1 def test_add_dns(self): - ''' + """ Test if it add the DNS server to the network interface. - ''' - with patch('salt.utils.winapi.Com', MagicMock()), \ - patch.object(self.WMI, 'Win32_NetworkAdapter', - return_value=[Mockwmi()]), \ - patch.object(self.WMI, 'Win32_NetworkAdapterConfiguration', - return_value=[Mockwmi()]), \ - patch.object(wmi, 'WMI', Mock(return_value=self.WMI)): - self.assertFalse(win_dns_client.add_dns('10.1.1.10', 'Ethernet')) + """ + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + self.WMI, "Win32_NetworkAdapter", return_value=[Mockwmi()] + ), patch.object( + self.WMI, "Win32_NetworkAdapterConfiguration", return_value=[Mockwmi()] + ), patch.object( + wmi, "WMI", Mock(return_value=self.WMI) + ): + self.assertFalse(win_dns_client.add_dns("10.1.1.10", "Ethernet")) - self.assertTrue(win_dns_client.add_dns('10.1.1.10', 'Local Area Connection')) + self.assertTrue( + win_dns_client.add_dns("10.1.1.10", "Local Area Connection") + ) - with patch.object(win_dns_client, 'get_dns_servers', - MagicMock(return_value=['10.1.1.10'])), \ - patch.dict(win_dns_client.__salt__, - {'cmd.retcode': MagicMock(return_value=0)}), \ - patch.object(wmi, 'WMI', Mock(return_value=self.WMI)): - self.assertTrue(win_dns_client.add_dns('10.1.1.0', 'Local Area Connection')) + with patch.object( + win_dns_client, "get_dns_servers", MagicMock(return_value=["10.1.1.10"]) + ), patch.dict( + win_dns_client.__salt__, {"cmd.retcode": MagicMock(return_value=0)} + ), patch.object( + wmi, "WMI", Mock(return_value=self.WMI) + ): + self.assertTrue(win_dns_client.add_dns("10.1.1.0", "Local Area Connection")) # 'dns_dhcp' function tests: 1 def test_dns_dhcp(self): - ''' + """ Test if it configure the interface to get its DNS servers from the DHCP server - ''' - with patch.dict(win_dns_client.__salt__, - {'cmd.retcode': MagicMock(return_value=0)}): + """ + with patch.dict( + win_dns_client.__salt__, {"cmd.retcode": MagicMock(return_value=0)} + ): self.assertTrue(win_dns_client.dns_dhcp()) # 'get_dns_config' function tests: 1 def test_get_dns_config(self): - ''' + """ Test if it get the type of DNS configuration (dhcp / static) - ''' - with patch('salt.utils.winapi.Com', MagicMock()), \ - patch.object(self.WMI, 'Win32_NetworkAdapter', - return_value=[Mockwmi()]), \ - patch.object(self.WMI, 'Win32_NetworkAdapterConfiguration', - return_value=[Mockwmi()]), \ - patch.object(wmi, 'WMI', Mock(return_value=self.WMI)): + """ + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + self.WMI, "Win32_NetworkAdapter", return_value=[Mockwmi()] + ), patch.object( + self.WMI, "Win32_NetworkAdapterConfiguration", return_value=[Mockwmi()] + ), patch.object( + wmi, "WMI", Mock(return_value=self.WMI) + ): self.assertTrue(win_dns_client.get_dns_config()) - @patch('salt.utils.platform.is_windows') + @patch("salt.utils.platform.is_windows") def test___virtual__non_windows(self, mock): mock.return_value = False result = win_dns_client.__virtual__() - expected = (False, 'Module win_dns_client: module only works on ' - 'Windows systems') + expected = ( + False, + "Module win_dns_client: module only works on Windows systems", + ) self.assertEqual(result, expected) - @patch.object(win_dns_client, 'HAS_LIBS', False) + @patch.object(win_dns_client, "HAS_LIBS", False) def test___virtual__missing_libs(self): result = win_dns_client.__virtual__() - expected = (False, 'Module win_dns_client: missing required libraries') + expected = (False, "Module win_dns_client: missing required libraries") self.assertEqual(result, expected) def test___virtual__(self): result = win_dns_client.__virtual__() - expected = 'win_dns_client' + expected = "win_dns_client" self.assertEqual(result, expected) diff --git a/tests/unit/modules/test_win_file.py b/tests/unit/modules/test_win_file.py index 55957a9aaf6..959443802de 100644 --- a/tests/unit/modules/test_win_file.py +++ b/tests/unit/modules/test_win_file.py @@ -1,17 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Shane Lee <slee@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os import sys -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import MagicMock, patch -from tests.support.unit import TestCase, skipIf -from tests.support.runtests import RUNTIME_VARS +import salt.modules.cmdmod as cmdmod # Import Salt Libs import salt.modules.temp as temp @@ -19,9 +16,14 @@ import salt.modules.win_file as win_file import salt.utils.platform import salt.utils.win_dacl as win_dacl import salt.utils.win_functions -import salt.modules.cmdmod as cmdmod from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +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, skipIf + try: WIN_VER = sys.getwindowsversion().major except AttributeError: @@ -42,51 +44,51 @@ class DummyStat(object): class WinFileTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_file - ''' - FAKE_RET = {'fake': 'ret data'} + """ + + FAKE_RET = {"fake": "ret data"} if salt.utils.platform.is_windows(): - FAKE_PATH = os.sep.join(['C:', 'path', 'does', 'not', 'exist']) + FAKE_PATH = os.sep.join(["C:", "path", "does", "not", "exist"]) else: - FAKE_PATH = os.sep.join(['path', 'does', 'not', 'exist']) + FAKE_PATH = os.sep.join(["path", "does", "not", "exist"]) def setup_loader_modules(self): return { win_file: { - '__utils__': {'dacl.set_perms': win_dacl.set_perms}, - '__salt__': {'cmd.run_stdout': cmdmod.run_stdout} + "__utils__": {"dacl.set_perms": win_dacl.set_perms}, + "__salt__": {"cmd.run_stdout": cmdmod.run_stdout}, } } def test_issue_43328_stats(self): - ''' + """ Make sure that a CommandExecutionError is raised if the file does NOT exist - ''' - with patch('os.path.exists', return_value=False): - self.assertRaises(CommandExecutionError, - win_file.stats, - self.FAKE_PATH) + """ + with patch("os.path.exists", return_value=False): + self.assertRaises(CommandExecutionError, win_file.stats, self.FAKE_PATH) def test_issue_43328_check_perms_no_ret(self): - ''' + """ Make sure that a CommandExecutionError is raised if the file does NOT exist - ''' - with patch('os.path.exists', return_value=False): + """ + with patch("os.path.exists", return_value=False): self.assertRaises( - CommandExecutionError, win_file.check_perms, self.FAKE_PATH) + CommandExecutionError, win_file.check_perms, self.FAKE_PATH + ) - @skipIf(not salt.utils.platform.is_windows(), 'Skip on Non-Windows systems') - @skipIf(WIN_VER < 6, 'Symlinks not supported on Vista an lower') + @skipIf(not salt.utils.platform.is_windows(), "Skip on Non-Windows systems") + @skipIf(WIN_VER < 6, "Symlinks not supported on Vista an lower") def test_issue_52002_check_file_remove_symlink(self): - ''' + """ Make sure that directories including symlinks or symlinks can be removed - ''' - base = temp.dir(prefix='base-', parent=RUNTIME_VARS.TMP) - target = os.path.join(base, 'child 1', 'target\\') - symlink = os.path.join(base, 'child 2', 'link') + """ + base = temp.dir(prefix="base-", parent=RUNTIME_VARS.TMP) + target = os.path.join(base, "child 1", "target\\") + symlink = os.path.join(base, "child 2", "link") try: # Create environment self.assertFalse(win_file.directory_exists(target)) @@ -104,234 +106,275 @@ class WinFileTestCase(TestCase, LoaderModuleMockMixin): win_file.remove(base) -@skipIf(not salt.utils.platform.is_windows(), 'Skip on Non-Windows systems') +@skipIf(not salt.utils.platform.is_windows(), "Skip on Non-Windows systems") class WinFileCheckPermsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for the check_perms function in salt.modules.win_file - ''' - temp_file = '' - current_user = '' + """ + + temp_file = "" + current_user = "" def setup_loader_modules(self): self.current_user = salt.utils.win_functions.get_current_user(False) return { win_file: { - '__utils__': {'dacl.check_perms': win_dacl.check_perms, - 'dacl.set_perms': win_dacl.set_perms} + "__utils__": { + "dacl.check_perms": win_dacl.check_perms, + "dacl.set_perms": win_dacl.set_perms, + } }, - win_dacl: { - '__opts__': {'test': False}, - } + win_dacl: {"__opts__": {"test": False}}, } def setUp(self): self.temp_file = temp.file(parent=RUNTIME_VARS.TMP) - win_dacl.set_owner(obj_name=self.temp_file, - principal=self.current_user) - win_dacl.set_inheritance(obj_name=self.temp_file, - enabled=True) - self.assertEqual( - win_dacl.get_owner(obj_name=self.temp_file), - self.current_user) + win_dacl.set_owner(obj_name=self.temp_file, principal=self.current_user) + win_dacl.set_inheritance(obj_name=self.temp_file, enabled=True) + self.assertEqual(win_dacl.get_owner(obj_name=self.temp_file), self.current_user) def tearDown(self): os.remove(self.temp_file) def test_check_perms_set_owner_test_true(self): - ''' + """ Test setting the owner of a file with test=True - ''' - expected = {'comment': '', - 'changes': {'owner': 'Administrators'}, - 'name': self.temp_file, - 'result': None} - with patch.dict(win_dacl.__opts__, {'test': True}): - ret = win_file.check_perms(path=self.temp_file, - owner='Administrators', - inheritance=None) + """ + expected = { + "comment": "", + "changes": {"owner": "Administrators"}, + "name": self.temp_file, + "result": None, + } + with patch.dict(win_dacl.__opts__, {"test": True}): + ret = win_file.check_perms( + path=self.temp_file, owner="Administrators", inheritance=None + ) self.assertDictEqual(expected, ret) def test_check_perms_set_owner(self): - ''' + """ Test setting the owner of a file - ''' - expected = {'comment': '', - 'changes': {'owner': 'Administrators'}, - 'name': self.temp_file, - 'result': True} - ret = win_file.check_perms(path=self.temp_file, - owner='Administrators', - inheritance=None) + """ + expected = { + "comment": "", + "changes": {"owner": "Administrators"}, + "name": self.temp_file, + "result": True, + } + ret = win_file.check_perms( + path=self.temp_file, owner="Administrators", inheritance=None + ) self.assertDictEqual(expected, ret) def test_check_perms_deny_test_true(self): - ''' + """ Test setting deny perms on a file with test=True - ''' - expected = {'comment': '', - 'changes': {'perms': {'Users': {'deny': 'read_execute'}}}, - 'name': self.temp_file, - 'result': None} - with patch.dict(win_dacl.__opts__, {'test': True}): + """ + expected = { + "comment": "", + "changes": {"perms": {"Users": {"deny": "read_execute"}}}, + "name": self.temp_file, + "result": None, + } + with patch.dict(win_dacl.__opts__, {"test": True}): ret = win_file.check_perms( path=self.temp_file, - deny_perms={'Users': {'perms': 'read_execute'}}, - inheritance=None) + deny_perms={"Users": {"perms": "read_execute"}}, + inheritance=None, + ) self.assertDictEqual(expected, ret) def test_check_perms_deny(self): - ''' + """ Test setting deny perms on a file - ''' - expected = {'comment': '', - 'changes': { - 'perms': {'Users': {'deny': 'read_execute'}}}, - 'name': self.temp_file, - 'result': True} + """ + expected = { + "comment": "", + "changes": {"perms": {"Users": {"deny": "read_execute"}}}, + "name": self.temp_file, + "result": True, + } ret = win_file.check_perms( path=self.temp_file, - deny_perms={'Users': {'perms': 'read_execute'}}, - inheritance=None) + deny_perms={"Users": {"perms": "read_execute"}}, + inheritance=None, + ) self.assertDictEqual(expected, ret) def test_check_perms_grant_test_true(self): - ''' + """ Test setting grant perms on a file with test=True - ''' - expected = {'comment': '', - 'changes': {'perms': {'Users': {'grant': 'read_execute'}}}, - 'name': self.temp_file, - 'result': None} - with patch.dict(win_dacl.__opts__, {'test': True}): + """ + expected = { + "comment": "", + "changes": {"perms": {"Users": {"grant": "read_execute"}}}, + "name": self.temp_file, + "result": None, + } + with patch.dict(win_dacl.__opts__, {"test": True}): ret = win_file.check_perms( path=self.temp_file, - grant_perms={'Users': {'perms': 'read_execute'}}, - inheritance=None) + grant_perms={"Users": {"perms": "read_execute"}}, + inheritance=None, + ) self.assertDictEqual(expected, ret) def test_check_perms_grant(self): - ''' + """ Test setting grant perms on a file - ''' - expected = {'comment': '', - 'changes': { - 'perms': {'Users': {'grant': 'read_execute'}}}, - 'name': self.temp_file, - 'result': True} + """ + expected = { + "comment": "", + "changes": {"perms": {"Users": {"grant": "read_execute"}}}, + "name": self.temp_file, + "result": True, + } ret = win_file.check_perms( path=self.temp_file, - grant_perms={'Users': {'perms': 'read_execute'}}, - inheritance=None) + grant_perms={"Users": {"perms": "read_execute"}}, + inheritance=None, + ) self.assertDictEqual(expected, ret) def test_check_perms_inheritance_false_test_true(self): - ''' + """ Test setting inheritance to False with test=True - ''' - expected = {'comment': '', - 'changes': {'inheritance': False}, - 'name': self.temp_file, - 'result': None} - with patch.dict(win_dacl.__opts__, {'test': True}): - ret = win_file.check_perms(path=self.temp_file, - inheritance=False) + """ + expected = { + "comment": "", + "changes": {"inheritance": False}, + "name": self.temp_file, + "result": None, + } + with patch.dict(win_dacl.__opts__, {"test": True}): + ret = win_file.check_perms(path=self.temp_file, inheritance=False) self.assertDictEqual(expected, ret) def test_check_perms_inheritance_false(self): - ''' + """ Test setting inheritance to False - ''' - expected = {'comment': '', - 'changes': {'inheritance': False}, - 'name': self.temp_file, - 'result': True} - ret = win_file.check_perms(path=self.temp_file, - inheritance=False) + """ + expected = { + "comment": "", + "changes": {"inheritance": False}, + "name": self.temp_file, + "result": True, + } + ret = win_file.check_perms(path=self.temp_file, inheritance=False) self.assertDictEqual(expected, ret) def test_check_perms_inheritance_true(self): - ''' + """ Test setting inheritance to true when it's already true (default) - ''' - expected = {'comment': '', - 'changes': {}, - 'name': self.temp_file, - 'result': True} - ret = win_file.check_perms(path=self.temp_file, - inheritance=True) + """ + expected = { + "comment": "", + "changes": {}, + "name": self.temp_file, + "result": True, + } + ret = win_file.check_perms(path=self.temp_file, inheritance=True) self.assertDictEqual(expected, ret) def test_check_perms_reset_test_true(self): - ''' + """ Test resetting perms with test=True. This shows minimal changes - ''' + """ # Turn off inheritance - win_dacl.set_inheritance(obj_name=self.temp_file, - enabled=False, - clear=True) + win_dacl.set_inheritance(obj_name=self.temp_file, enabled=False, clear=True) # Set some permissions - win_dacl.set_permissions(obj_name=self.temp_file, - principal='Administrator', - permissions='full_control') - expected = {'comment': '', - 'changes': { - 'perms': { - 'Administrators': {'grant': 'full_control'}, - 'Users': {'grant': 'read_execute'}}, - 'remove_perms': { - 'Administrator': { - 'grant': {'applies to': 'Not Inherited (file)', - 'permissions': 'Full control'}}}}, - 'name': self.temp_file, - 'result': None} - with patch.dict(win_dacl.__opts__, {'test': True}): + win_dacl.set_permissions( + obj_name=self.temp_file, + principal="Administrator", + permissions="full_control", + ) + expected = { + "comment": "", + "changes": { + "perms": { + "Administrators": {"grant": "full_control"}, + "Users": {"grant": "read_execute"}, + }, + "remove_perms": { + "Administrator": { + "grant": { + "applies to": "Not Inherited (file)", + "permissions": "Full control", + } + } + }, + }, + "name": self.temp_file, + "result": None, + } + with patch.dict(win_dacl.__opts__, {"test": True}): ret = win_file.check_perms( path=self.temp_file, - grant_perms={'Users': {'perms': 'read_execute'}, - 'Administrators': {'perms': 'full_control'}}, + grant_perms={ + "Users": {"perms": "read_execute"}, + "Administrators": {"perms": "full_control"}, + }, inheritance=False, - reset=True) + reset=True, + ) self.assertDictEqual(expected, ret) def test_check_perms_reset(self): - ''' + """ Test resetting perms on a File - ''' + """ # Turn off inheritance - win_dacl.set_inheritance(obj_name=self.temp_file, - enabled=False, - clear=True) + win_dacl.set_inheritance(obj_name=self.temp_file, enabled=False, clear=True) # Set some permissions - win_dacl.set_permissions(obj_name=self.temp_file, - principal='Administrator', - permissions='full_control') - expected = {'comment': '', - 'changes': { - 'perms': { - 'Administrators': {'grant': 'full_control'}, - 'Users': {'grant': 'read_execute'}}, - 'remove_perms': { - 'Administrator': { - 'grant': {'applies to': 'Not Inherited (file)', - 'permissions': 'Full control'}}}}, - 'name': self.temp_file, - 'result': True} + win_dacl.set_permissions( + obj_name=self.temp_file, + principal="Administrator", + permissions="full_control", + ) + expected = { + "comment": "", + "changes": { + "perms": { + "Administrators": {"grant": "full_control"}, + "Users": {"grant": "read_execute"}, + }, + "remove_perms": { + "Administrator": { + "grant": { + "applies to": "Not Inherited (file)", + "permissions": "Full control", + } + } + }, + }, + "name": self.temp_file, + "result": True, + } ret = win_file.check_perms( path=self.temp_file, - grant_perms={'Users': {'perms': 'read_execute'}, - 'Administrators': {'perms': 'full_control'}}, + grant_perms={ + "Users": {"perms": "read_execute"}, + "Administrators": {"perms": "full_control"}, + }, inheritance=False, - reset=True) + reset=True, + ) self.assertDictEqual(expected, ret) def test_stat(self): - with patch('os.path.exists', MagicMock(return_value=True)), \ - patch('salt.modules.win_file._resolve_symlink', MagicMock(side_effect=lambda path: path)), \ - patch('salt.modules.win_file.get_uid', MagicMock(return_value=1)), \ - patch('salt.modules.win_file.uid_to_user', MagicMock(return_value='dummy')), \ - patch('salt.modules.win_file.get_pgid', MagicMock(return_value=1)), \ - patch('salt.modules.win_file.gid_to_group', MagicMock(return_value='dummy')), \ - patch('os.stat', MagicMock(return_value=DummyStat())): - ret = win_file.stats('dummy', None, True) - self.assertEqual(ret['mode'], '0644') - self.assertEqual(ret['type'], 'file') + with patch("os.path.exists", MagicMock(return_value=True)), patch( + "salt.modules.win_file._resolve_symlink", + MagicMock(side_effect=lambda path: path), + ), patch("salt.modules.win_file.get_uid", MagicMock(return_value=1)), patch( + "salt.modules.win_file.uid_to_user", MagicMock(return_value="dummy") + ), patch( + "salt.modules.win_file.get_pgid", MagicMock(return_value=1) + ), patch( + "salt.modules.win_file.gid_to_group", MagicMock(return_value="dummy") + ), patch( + "os.stat", MagicMock(return_value=DummyStat()) + ): + ret = win_file.stats("dummy", None, True) + self.assertEqual(ret["mode"], "0644") + self.assertEqual(ret["type"], "file") diff --git a/tests/unit/modules/test_win_groupadd.py b/tests/unit/modules/test_win_groupadd.py index 1c9dd851313..add84c0d659 100644 --- a/tests/unit/modules/test_win_groupadd.py +++ b/tests/unit/modules/test_win_groupadd.py @@ -1,32 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - Mock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_groupadd as win_groupadd import salt.utils.win_functions +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase, skipIf + # Import Other Libs # pylint: disable=unused-import try: import win32com import pythoncom import pywintypes + PYWINTYPES_ERROR = pywintypes.com_error( - -1234, 'Exception occurred.', (0, None, 'C', None, 0, -2147352567), None) + -1234, "Exception occurred.", (0, None, "C", None, 0, -2147352567), None + ) HAS_WIN_LIBS = True except ImportError: HAS_WIN_LIBS = False @@ -47,289 +45,364 @@ class MockGroupObj(object): return self._members def Add(self, name): - ''' + """ This should be a no-op unless we want to test raising an error, in which case this should be overridden in a subclass. - ''' + """ def Remove(self, name): - ''' + """ This should be a no-op unless we want to test raising an error, in which case this should be overridden in a subclass. - ''' + """ -sam_mock = MagicMock(side_effect=lambda x: 'HOST\\' + x) +sam_mock = MagicMock(side_effect=lambda x: "HOST\\" + x) -@skipIf(not HAS_WIN_LIBS, 'win_groupadd unit tests can only be run if win32com, pythoncom, and pywintypes are installed') +@skipIf( + not HAS_WIN_LIBS, + "win_groupadd unit tests can only be run if win32com, pythoncom, and pywintypes are installed", +) class WinGroupTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_groupadd - ''' + """ + def setup_loader_modules(self): - return { - win_groupadd: {'__opts__': {'test': False}} - } + return {win_groupadd: {"__opts__": {"test": False}}} def test_add(self): - ''' + """ Test adding a new group - ''' + """ info = MagicMock(return_value=False) - with patch.object(win_groupadd, 'info', info),\ - patch.object(win_groupadd, '_get_computer_object', Mock()): - self.assertTrue(win_groupadd.add('foo')) + with patch.object(win_groupadd, "info", info), patch.object( + win_groupadd, "_get_computer_object", Mock() + ): + self.assertTrue(win_groupadd.add("foo")) def test_add_group_exists(self): - ''' + """ Test adding a new group if the group already exists - ''' - info = MagicMock(return_value={'name': 'foo', - 'passwd': None, - 'gid': None, - 'members': ['HOST\\spongebob']}) - with patch.object(win_groupadd, 'info', info),\ - patch.object(win_groupadd, '_get_computer_object', Mock()): - self.assertFalse(win_groupadd.add('foo')) + """ + info = MagicMock( + return_value={ + "name": "foo", + "passwd": None, + "gid": None, + "members": ["HOST\\spongebob"], + } + ) + with patch.object(win_groupadd, "info", info), patch.object( + win_groupadd, "_get_computer_object", Mock() + ): + self.assertFalse(win_groupadd.add("foo")) def test_add_error(self): - ''' + """ Test adding a group and encountering an error - ''' + """ + class CompObj(object): def Create(self, type, name): raise PYWINTYPES_ERROR + obj_comp_mock = MagicMock(return_value=CompObj()) info = MagicMock(return_value=False) - with patch.object(win_groupadd, 'info', info),\ - patch.object(win_groupadd, '_get_computer_object', obj_comp_mock): - self.assertFalse(win_groupadd.add('foo')) + with patch.object(win_groupadd, "info", info), patch.object( + win_groupadd, "_get_computer_object", obj_comp_mock + ): + self.assertFalse(win_groupadd.add("foo")) def test_delete(self): - ''' + """ Test removing a group - ''' - info = MagicMock(return_value={'name': 'foo', - 'passwd': None, - 'gid': None, - 'members': ['HOST\\spongebob']}) - with patch.object(win_groupadd, 'info', info), \ - patch.object(win_groupadd, '_get_computer_object', Mock()): - self.assertTrue(win_groupadd.delete('foo')) + """ + info = MagicMock( + return_value={ + "name": "foo", + "passwd": None, + "gid": None, + "members": ["HOST\\spongebob"], + } + ) + with patch.object(win_groupadd, "info", info), patch.object( + win_groupadd, "_get_computer_object", Mock() + ): + self.assertTrue(win_groupadd.delete("foo")) def test_delete_no_group(self): - ''' + """ Test removing a group that doesn't exists - ''' + """ info = MagicMock(return_value=False) - with patch.object(win_groupadd, 'info', info), \ - patch.object(win_groupadd, '_get_computer_object', Mock()): - self.assertFalse(win_groupadd.delete('foo')) + with patch.object(win_groupadd, "info", info), patch.object( + win_groupadd, "_get_computer_object", Mock() + ): + self.assertFalse(win_groupadd.delete("foo")) def test_delete_error(self): - ''' + """ Test removing a group and encountering an error - ''' + """ + class CompObj(object): def Delete(self, type, name): raise PYWINTYPES_ERROR + obj_comp_mock = MagicMock(return_value=CompObj()) - info = MagicMock(return_value={'name': 'foo', - 'passwd': None, - 'gid': None, - 'members': ['HOST\\spongebob']}) - with patch.object(win_groupadd, 'info', info),\ - patch.object(win_groupadd, '_get_computer_object', obj_comp_mock): - self.assertFalse(win_groupadd.delete('foo')) + info = MagicMock( + return_value={ + "name": "foo", + "passwd": None, + "gid": None, + "members": ["HOST\\spongebob"], + } + ) + with patch.object(win_groupadd, "info", info), patch.object( + win_groupadd, "_get_computer_object", obj_comp_mock + ): + self.assertFalse(win_groupadd.delete("foo")) def test_info(self): - ''' + """ Test if it return information about a group. - ''' - obj_group_mock = MagicMock(return_value=MockGroupObj('salt', ['WinNT://HOST/steve'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock): - self.assertDictEqual(win_groupadd.info('salt'), - {'gid': None, - 'members': ['HOST\\steve'], - 'passwd': None, - 'name': 'salt'}) + """ + obj_group_mock = MagicMock( + return_value=MockGroupObj("salt", ["WinNT://HOST/steve"]) + ) + with patch.object(win_groupadd, "_get_group_object", obj_group_mock): + self.assertDictEqual( + win_groupadd.info("salt"), + { + "gid": None, + "members": ["HOST\\steve"], + "passwd": None, + "name": "salt", + }, + ) def test_getent(self): obj_group_mock = MagicMock( return_value=[ - MockGroupObj('salt', ['WinNT://HOST/steve']), - MockGroupObj('salty', ['WinNT://HOST/spongebob'])]) + MockGroupObj("salt", ["WinNT://HOST/steve"]), + MockGroupObj("salty", ["WinNT://HOST/spongebob"]), + ] + ) mock_g_to_g = MagicMock(side_effect=[1, 2]) - with patch.object(win_groupadd, '_get_all_groups', obj_group_mock),\ - patch.dict(win_groupadd.__salt__, {'file.group_to_gid': mock_g_to_g}): + with patch.object(win_groupadd, "_get_all_groups", obj_group_mock), patch.dict( + win_groupadd.__salt__, {"file.group_to_gid": mock_g_to_g} + ): self.assertListEqual( win_groupadd.getent(), [ - {'gid': 1, 'members': ['HOST\\steve'], 'name': 'salt', 'passwd': 'x'}, - {'gid': 2, 'members': ['HOST\\spongebob'], 'name': 'salty', 'passwd': 'x'} - ]) + { + "gid": 1, + "members": ["HOST\\steve"], + "name": "salt", + "passwd": "x", + }, + { + "gid": 2, + "members": ["HOST\\spongebob"], + "name": "salty", + "passwd": "x", + }, + ], + ) def test_getent_context(self): - ''' + """ Test group.getent is using the values in __context__ - ''' - with patch.dict(win_groupadd.__context__, {'group.getent': True}): + """ + with patch.dict(win_groupadd.__context__, {"group.getent": True}): self.assertTrue(win_groupadd.getent()) def test_adduser(self): - ''' + """ Test adding a user to a group - ''' - obj_group_mock = MagicMock(return_value=MockGroupObj('foo', ['WinNT://HOST/steve'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertTrue(win_groupadd.adduser('foo', 'spongebob')) + """ + obj_group_mock = MagicMock( + return_value=MockGroupObj("foo", ["WinNT://HOST/steve"]) + ) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertTrue(win_groupadd.adduser("foo", "spongebob")) def test_adduser_already_exists(self): - ''' + """ Test adding a user that already exists - ''' - obj_group_mock = MagicMock(return_value=MockGroupObj('foo', ['WinNT://HOST/steve'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertFalse(win_groupadd.adduser('foo', 'steve')) + """ + obj_group_mock = MagicMock( + return_value=MockGroupObj("foo", ["WinNT://HOST/steve"]) + ) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertFalse(win_groupadd.adduser("foo", "steve")) def test_adduser_error(self): - ''' + """ Test adding a user and encountering an error - ''' + """ # Create mock group object with mocked Add function which raises the # exception we need in order to test the error case. class GroupObj(MockGroupObj): def Add(self, name): raise PYWINTYPES_ERROR - obj_group_mock = MagicMock(return_value=GroupObj('foo', ['WinNT://HOST/steve'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertFalse(win_groupadd.adduser('foo', 'username')) + obj_group_mock = MagicMock(return_value=GroupObj("foo", ["WinNT://HOST/steve"])) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertFalse(win_groupadd.adduser("foo", "username")) def test_adduser_group_does_not_exist(self): obj_group_mock = MagicMock(side_effect=PYWINTYPES_ERROR) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock): - self.assertFalse(win_groupadd.adduser('foo', 'spongebob')) + with patch.object(win_groupadd, "_get_group_object", obj_group_mock): + self.assertFalse(win_groupadd.adduser("foo", "spongebob")) def test_deluser(self): - ''' + """ Test removing a user from a group - ''' + """ # Test removing a user - obj_group_mock = MagicMock(return_value=MockGroupObj('foo', ['WinNT://HOST/spongebob'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertTrue(win_groupadd.deluser('foo', 'spongebob')) + obj_group_mock = MagicMock( + return_value=MockGroupObj("foo", ["WinNT://HOST/spongebob"]) + ) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertTrue(win_groupadd.deluser("foo", "spongebob")) def test_deluser_no_user(self): - ''' + """ Test removing a user from a group and that user is not a member of the group - ''' - obj_group_mock = MagicMock(return_value=MockGroupObj('foo', ['WinNT://HOST/steve'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertFalse(win_groupadd.deluser('foo', 'spongebob')) + """ + obj_group_mock = MagicMock( + return_value=MockGroupObj("foo", ["WinNT://HOST/steve"]) + ) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertFalse(win_groupadd.deluser("foo", "spongebob")) def test_deluser_error(self): - ''' + """ Test removing a user and encountering an error - ''' + """ + class GroupObj(MockGroupObj): def Remove(self, name): raise PYWINTYPES_ERROR - obj_group_mock = MagicMock(return_value=GroupObj('foo', ['WinNT://HOST/spongebob'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertFalse(win_groupadd.deluser('foo', 'spongebob')) + obj_group_mock = MagicMock( + return_value=GroupObj("foo", ["WinNT://HOST/spongebob"]) + ) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertFalse(win_groupadd.deluser("foo", "spongebob")) def test_deluser_group_does_not_exist(self): obj_group_mock = MagicMock(side_effect=PYWINTYPES_ERROR) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock): - self.assertFalse(win_groupadd.deluser('foo', 'spongebob')) + with patch.object(win_groupadd, "_get_group_object", obj_group_mock): + self.assertFalse(win_groupadd.deluser("foo", "spongebob")) def test_members(self): - ''' + """ Test adding a list of members to a group, all existing users removed - ''' - obj_group_mock = MagicMock(return_value=MockGroupObj('foo', ['WinNT://HOST/steve'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertTrue( - win_groupadd.members('foo', 'spongebob,patrick,squidward')) - obj_group_mock.assert_called_once_with('foo') + """ + obj_group_mock = MagicMock( + return_value=MockGroupObj("foo", ["WinNT://HOST/steve"]) + ) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertTrue(win_groupadd.members("foo", "spongebob,patrick,squidward")) + obj_group_mock.assert_called_once_with("foo") def test_members_correct_membership(self): - ''' + """ Test adding a list of users where the list of users already exists - ''' - members_list = ['WinNT://HOST/spongebob', - 'WinNT://HOST/squidward', - 'WinNT://HOST/patrick'] - obj_group_mock = MagicMock(return_value=MockGroupObj('foo', members_list)) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertTrue( - win_groupadd.members('foo', 'spongebob,patrick,squidward')) - obj_group_mock.assert_called_once_with('foo') + """ + members_list = [ + "WinNT://HOST/spongebob", + "WinNT://HOST/squidward", + "WinNT://HOST/patrick", + ] + obj_group_mock = MagicMock(return_value=MockGroupObj("foo", members_list)) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertTrue(win_groupadd.members("foo", "spongebob,patrick,squidward")) + obj_group_mock.assert_called_once_with("foo") def test_members_group_does_not_exist(self): - ''' + """ Test adding a list of users where the group does not exist - ''' + """ obj_group_mock = MagicMock(side_effect=PYWINTYPES_ERROR) - with patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock),\ - patch.object(win_groupadd, '_get_group_object', obj_group_mock): - self.assertFalse(win_groupadd.members('foo', 'spongebob')) + with patch.object( + salt.utils.win_functions, "get_sam_name", sam_mock + ), patch.object(win_groupadd, "_get_group_object", obj_group_mock): + self.assertFalse(win_groupadd.members("foo", "spongebob")) def test_members_fail_to_remove(self): - ''' + """ Test adding a list of members and fail to remove members not in the list - ''' + """ + class GroupObj(MockGroupObj): def Remove(self, name): raise PYWINTYPES_ERROR - obj_group_mock = MagicMock(return_value=GroupObj('foo', ['WinNT://HOST/spongebob'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertFalse(win_groupadd.members('foo', 'patrick')) - obj_group_mock.assert_called_once_with('foo') + obj_group_mock = MagicMock( + return_value=GroupObj("foo", ["WinNT://HOST/spongebob"]) + ) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertFalse(win_groupadd.members("foo", "patrick")) + obj_group_mock.assert_called_once_with("foo") def test_members_fail_to_add(self): - ''' + """ Test adding a list of members and failing to add - ''' + """ + class GroupObj(MockGroupObj): def Add(self, name): raise PYWINTYPES_ERROR - obj_group_mock = MagicMock(return_value=GroupObj('foo', ['WinNT://HOST/spongebob'])) - with patch.object(win_groupadd, '_get_group_object', obj_group_mock), \ - patch.object(salt.utils.win_functions, 'get_sam_name', sam_mock): - self.assertFalse(win_groupadd.members('foo', 'patrick')) - obj_group_mock.assert_called_once_with('foo') + obj_group_mock = MagicMock( + return_value=GroupObj("foo", ["WinNT://HOST/spongebob"]) + ) + with patch.object( + win_groupadd, "_get_group_object", obj_group_mock + ), patch.object(salt.utils.win_functions, "get_sam_name", sam_mock): + self.assertFalse(win_groupadd.members("foo", "patrick")) + obj_group_mock.assert_called_once_with("foo") def test_list_groups(self): - ''' + """ Test that list groups returns a list of groups by name - ''' + """ obj_group_mock = MagicMock( return_value=[ - MockGroupObj('salt', ['WinNT://HOST/steve']), - MockGroupObj('salty', ['WinNT://HOST/Administrator'])]) - with patch.object(win_groupadd, '_get_all_groups', obj_group_mock): - self.assertListEqual(win_groupadd.list_groups(), - ['salt', 'salty']) + MockGroupObj("salt", ["WinNT://HOST/steve"]), + MockGroupObj("salty", ["WinNT://HOST/Administrator"]), + ] + ) + with patch.object(win_groupadd, "_get_all_groups", obj_group_mock): + self.assertListEqual(win_groupadd.list_groups(), ["salt", "salty"]) def test_list_groups_context(self): - ''' + """ Test group.list_groups is using the values in __context__ - ''' - with patch.dict(win_groupadd.__context__, {'group.list_groups': True}): + """ + with patch.dict(win_groupadd.__context__, {"group.list_groups": True}): self.assertTrue(win_groupadd.list_groups()) diff --git a/tests/unit/modules/test_win_iis.py b/tests/unit/modules/test_win_iis.py index ee7135b1ed8..b7449ae9c55 100644 --- a/tests/unit/modules/test_win_iis.py +++ b/tests/unit/modules/test_win_iis.py @@ -1,552 +1,640 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for Windows IIS Module 'module.win_iis' :platform: Windows :maturity: develop versionadded:: 2016.11.0 -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Libs -from salt.exceptions import SaltInvocationError import salt.modules.win_iis as win_iis import salt.utils.json +# Import Salt Libs +from salt.exceptions import SaltInvocationError + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) APP_LIST = { - 'testApp': { - 'apppool': 'MyTestPool', - 'path': '/testApp', - 'preload': False, - 'protocols': ['http'], - 'sourcepath': r'C:\inetpub\apps\testApp' + "testApp": { + "apppool": "MyTestPool", + "path": "/testApp", + "preload": False, + "protocols": ["http"], + "sourcepath": r"C:\inetpub\apps\testApp", } } -APPPOOL_LIST = { - 'MyTestPool': { - 'applications': ['MyTestSite'], - 'state': 'Started' - } -} +APPPOOL_LIST = {"MyTestPool": {"applications": ["MyTestSite"], "state": "Started"}} BINDING_LIST = { - '*:80:': { - 'certificatehash': None, - 'certificatestorename': None, - 'hostheader': None, - 'ipaddress': '*', 'port': 80, - 'protocol': 'http', - 'sslflags': 0 + "*:80:": { + "certificatehash": None, + "certificatestorename": None, + "hostheader": None, + "ipaddress": "*", + "port": 80, + "protocol": "http", + "sslflags": 0, + }, + "*:443:mytestsite.local": { + "certificatehash": "9988776655443322111000AAABBBCCCDDDEEEFFF", + "certificatestorename": "My", + "hostheader": "mytestsite.local", + "ipaddress": "*", + "port": 443, + "protocol": "https", + "sslflags": 0, }, - '*:443:mytestsite.local': { - 'certificatehash': '9988776655443322111000AAABBBCCCDDDEEEFFF', - 'certificatestorename': 'My', - 'hostheader': 'mytestsite.local', - 'ipaddress': '*', - 'port': 443, - 'protocol': 'https', - 'sslflags': 0 - } } SITE_LIST = { - 'MyTestSite': { - 'apppool': 'MyTestPool', - 'bindings': BINDING_LIST, - 'id': 1, 'sourcepath': r'C:\inetpub\wwwroot', - 'state': 'Started' + "MyTestSite": { + "apppool": "MyTestPool", + "bindings": BINDING_LIST, + "id": 1, + "sourcepath": r"C:\inetpub\wwwroot", + "state": "Started", } } -VDIR_LIST = { - 'TestVdir': { - 'sourcepath': r'C:\inetpub\vdirs\TestVdir' - } -} +VDIR_LIST = {"TestVdir": {"sourcepath": r"C:\inetpub\vdirs\TestVdir"}} LIST_APPS_SRVMGR = { - 'retcode': 0, - 'stdout': salt.utils.json.dumps([{ - 'applicationPool': 'MyTestPool', - 'name': 'testApp', 'path': '/testApp', - 'PhysicalPath': r'C:\inetpub\apps\testApp', - 'preloadEnabled': False, - 'protocols': 'http' - }]) + "retcode": 0, + "stdout": salt.utils.json.dumps( + [ + { + "applicationPool": "MyTestPool", + "name": "testApp", + "path": "/testApp", + "PhysicalPath": r"C:\inetpub\apps\testApp", + "preloadEnabled": False, + "protocols": "http", + } + ] + ), } LIST_APPPOOLS_SRVMGR = { - 'retcode': 0, - 'stdout': salt.utils.json.dumps([{ - 'name': 'MyTestPool', 'state': 'Started', - 'Applications': { - 'value': ['MyTestSite'], - 'Count': 1 - } - }]) + "retcode": 0, + "stdout": salt.utils.json.dumps( + [ + { + "name": "MyTestPool", + "state": "Started", + "Applications": {"value": ["MyTestSite"], "Count": 1}, + } + ] + ), } LIST_VDIRS_SRVMGR = { - 'retcode': 0, - 'stdout': salt.utils.json.dumps([{ - 'name': 'TestVdir', - 'physicalPath': r'C:\inetpub\vdirs\TestVdir' - }]) + "retcode": 0, + "stdout": salt.utils.json.dumps( + [{"name": "TestVdir", "physicalPath": r"C:\inetpub\vdirs\TestVdir"}] + ), } CONTAINER_SETTING = { - 'retcode': 0, - 'stdout': salt.utils.json.dumps([{ - 'managedPipelineMode': 'Integrated' - }]) + "retcode": 0, + "stdout": salt.utils.json.dumps([{"managedPipelineMode": "Integrated"}]), } -CERT_BINDING_INFO = '*:443:mytestsite.local' +CERT_BINDING_INFO = "*:443:mytestsite.local" class WinIisTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_iis - ''' + """ def setup_loader_modules(self): return {win_iis: {}} def test_create_apppool(self): - ''' + """ Test - Create an IIS application pool. - ''' - with patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_apppools', - MagicMock(return_value=dict())), \ - patch.dict(win_iis.__salt__): - self.assertTrue(win_iis.create_apppool('MyTestPool')) + """ + with patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_apppools", MagicMock(return_value=dict()) + ), patch.dict( + win_iis.__salt__ + ): + self.assertTrue(win_iis.create_apppool("MyTestPool")) def test_list_apppools(self): - ''' + """ Test - List all configured IIS application pools. - ''' - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value=LIST_APPPOOLS_SRVMGR)): + """ + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value=LIST_APPPOOLS_SRVMGR) + ): self.assertEqual(win_iis.list_apppools(), APPPOOL_LIST) def test_remove_apppool(self): - ''' + """ Test - Remove an IIS application pool. - ''' - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_apppools', - MagicMock(return_value={'MyTestPool': { - 'applications': list(), - 'state': 'Started'}})): - self.assertTrue(win_iis.remove_apppool('MyTestPool')) + """ + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_apppools", + MagicMock( + return_value={ + "MyTestPool": {"applications": list(), "state": "Started"} + } + ), + ): + self.assertTrue(win_iis.remove_apppool("MyTestPool")) def test_restart_apppool(self): - ''' + """ Test - Restart an IIS application pool. - ''' - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})): - self.assertTrue(win_iis.restart_apppool('MyTestPool')) + """ + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ): + self.assertTrue(win_iis.restart_apppool("MyTestPool")) def test_create_site(self): - ''' + """ Test - Create a basic website in IIS. - ''' - kwargs = {'name': 'MyTestSite', 'sourcepath': r'C:\inetpub\wwwroot', - 'apppool': 'MyTestPool', 'hostheader': 'mytestsite.local', - 'ipaddress': '*', 'port': 80, 'protocol': 'http'} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=dict())), \ - patch('salt.modules.win_iis.list_apppools', - MagicMock(return_value=dict())): + """ + kwargs = { + "name": "MyTestSite", + "sourcepath": r"C:\inetpub\wwwroot", + "apppool": "MyTestPool", + "hostheader": "mytestsite.local", + "ipaddress": "*", + "port": 80, + "protocol": "http", + } + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_sites", MagicMock(return_value=dict()) + ), patch( + "salt.modules.win_iis.list_apppools", MagicMock(return_value=dict()) + ): self.assertTrue(win_iis.create_site(**kwargs)) def test_create_site_failed(self): - ''' + """ Test - Create a basic website in IIS using invalid data. - ''' - kwargs = {'name': 'MyTestSite', 'sourcepath': r'C:\inetpub\wwwroot', - 'apppool': 'MyTestPool', 'hostheader': 'mytestsite.local', - 'ipaddress': '*', 'port': 80, 'protocol': 'invalid-protocol-name'} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=dict())), \ - patch('salt.modules.win_iis.list_apppools', - MagicMock(return_value=dict())): + """ + kwargs = { + "name": "MyTestSite", + "sourcepath": r"C:\inetpub\wwwroot", + "apppool": "MyTestPool", + "hostheader": "mytestsite.local", + "ipaddress": "*", + "port": 80, + "protocol": "invalid-protocol-name", + } + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_sites", MagicMock(return_value=dict()) + ), patch( + "salt.modules.win_iis.list_apppools", MagicMock(return_value=dict()) + ): self.assertRaises(SaltInvocationError, win_iis.create_site, **kwargs) def test_remove_site(self): - ''' + """ Test - Delete a website from IIS. - ''' - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=SITE_LIST)): - self.assertTrue(win_iis.remove_site('MyTestSite')) + """ + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch("salt.modules.win_iis.list_sites", MagicMock(return_value=SITE_LIST)): + self.assertTrue(win_iis.remove_site("MyTestSite")) def test_create_app(self): - ''' + """ Test - Create an IIS application. - ''' - kwargs = {'name': 'testApp', 'site': 'MyTestSite', - 'sourcepath': r'C:\inetpub\apps\testApp', 'apppool': 'MyTestPool'} - with patch.dict(win_iis.__salt__), \ - patch('os.path.isdir', MagicMock(return_value=True)), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_apps', - MagicMock(return_value=APP_LIST)): + """ + kwargs = { + "name": "testApp", + "site": "MyTestSite", + "sourcepath": r"C:\inetpub\apps\testApp", + "apppool": "MyTestPool", + } + with patch.dict(win_iis.__salt__), patch( + "os.path.isdir", MagicMock(return_value=True) + ), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_apps", MagicMock(return_value=APP_LIST) + ): self.assertTrue(win_iis.create_app(**kwargs)) def test_list_apps(self): - ''' + """ Test - Get all configured IIS applications for the specified site. - ''' - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value=LIST_APPS_SRVMGR)): - self.assertEqual(win_iis.list_apps('MyTestSite'), APP_LIST) + """ + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value=LIST_APPS_SRVMGR) + ): + self.assertEqual(win_iis.list_apps("MyTestSite"), APP_LIST) def test_remove_app(self): - ''' + """ Test - Remove an IIS application. - ''' - kwargs = {'name': 'otherApp', 'site': 'MyTestSite'} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_apps', - MagicMock(return_value=APP_LIST)): + """ + kwargs = {"name": "otherApp", "site": "MyTestSite"} + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch("salt.modules.win_iis.list_apps", MagicMock(return_value=APP_LIST)): self.assertTrue(win_iis.remove_app(**kwargs)) def test_create_binding(self): - ''' + """ Test - Create an IIS binding. - ''' - kwargs = {'site': 'MyTestSite', 'hostheader': '', 'ipaddress': '*', - 'port': 80, 'protocol': 'http', 'sslflags': 0} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_bindings', - MagicMock(return_value=BINDING_LIST)): + """ + kwargs = { + "site": "MyTestSite", + "hostheader": "", + "ipaddress": "*", + "port": 80, + "protocol": "http", + "sslflags": 0, + } + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_bindings", MagicMock(return_value=BINDING_LIST) + ): self.assertTrue(win_iis.create_binding(**kwargs)) def test_create_binding_failed(self): - ''' + """ Test - Create an IIS binding using invalid data. - ''' - kwargs = {'site': 'MyTestSite', 'hostheader': '', 'ipaddress': '*', - 'port': 80, 'protocol': 'invalid-protocol-name', 'sslflags': 999} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_bindings', - MagicMock(return_value=BINDING_LIST)): + """ + kwargs = { + "site": "MyTestSite", + "hostheader": "", + "ipaddress": "*", + "port": 80, + "protocol": "invalid-protocol-name", + "sslflags": 999, + } + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_bindings", MagicMock(return_value=BINDING_LIST) + ): self.assertRaises(SaltInvocationError, win_iis.create_binding, **kwargs) def test_list_bindings(self): - ''' + """ Test - Get all configured IIS bindings for the specified site. - ''' - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=SITE_LIST)): - self.assertEqual(win_iis.list_bindings('MyTestSite'), BINDING_LIST) + """ + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis.list_sites", MagicMock(return_value=SITE_LIST) + ): + self.assertEqual(win_iis.list_bindings("MyTestSite"), BINDING_LIST) def test_remove_binding(self): - ''' + """ Test - Remove an IIS binding. - ''' - kwargs = {'site': 'MyTestSite', 'hostheader': 'myothertestsite.local', - 'ipaddress': '*', 'port': 443} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_bindings', - MagicMock(return_value=BINDING_LIST)): + """ + kwargs = { + "site": "MyTestSite", + "hostheader": "myothertestsite.local", + "ipaddress": "*", + "port": 443, + } + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_bindings", MagicMock(return_value=BINDING_LIST) + ): self.assertTrue(win_iis.remove_binding(**kwargs)) def test_create_vdir(self): - ''' + """ Test - Create an IIS virtual directory. - ''' - kwargs = {'name': 'TestVdir', 'site': 'MyTestSite', - 'sourcepath': r'C:\inetpub\vdirs\TestVdir'} - with patch.dict(win_iis.__salt__), \ - patch('os.path.isdir', - MagicMock(return_value=True)), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_vdirs', - MagicMock(return_value=VDIR_LIST)): + """ + kwargs = { + "name": "TestVdir", + "site": "MyTestSite", + "sourcepath": r"C:\inetpub\vdirs\TestVdir", + } + with patch.dict(win_iis.__salt__), patch( + "os.path.isdir", MagicMock(return_value=True) + ), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_vdirs", MagicMock(return_value=VDIR_LIST) + ): self.assertTrue(win_iis.create_vdir(**kwargs)) def test_list_vdirs(self): - ''' + """ Test - Get configured IIS virtual directories. - ''' - vdirs = { - 'TestVdir': { - 'sourcepath': r'C:\inetpub\vdirs\TestVdir' - } - } - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value=LIST_VDIRS_SRVMGR)): - self.assertEqual(win_iis.list_vdirs('MyTestSite'), vdirs) + """ + vdirs = {"TestVdir": {"sourcepath": r"C:\inetpub\vdirs\TestVdir"}} + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value=LIST_VDIRS_SRVMGR) + ): + self.assertEqual(win_iis.list_vdirs("MyTestSite"), vdirs) def test_remove_vdir(self): - ''' + """ Test - Remove an IIS virtual directory. - ''' - kwargs = {'name': 'TestOtherVdir', 'site': 'MyTestSite'} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_vdirs', - MagicMock(return_value=VDIR_LIST)): + """ + kwargs = {"name": "TestOtherVdir", "site": "MyTestSite"} + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch("salt.modules.win_iis.list_vdirs", MagicMock(return_value=VDIR_LIST)): self.assertTrue(win_iis.remove_vdir(**kwargs)) def test_create_cert_binding(self): - ''' + """ Test - Assign a certificate to an IIS binding. - ''' - kwargs = {'name': '9988776655443322111000AAABBBCCCDDDEEEFFF', - 'site': 'MyTestSite', 'hostheader': 'mytestsite.local', - 'ipaddress': '*', 'port': 443} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._list_certs', - MagicMock(return_value={'9988776655443322111000AAABBBCCCDDDEEEFFF': None})), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0, 'stdout': 10})), \ - patch('salt.utils.json.loads', MagicMock(return_value=[{'MajorVersion': 10, 'MinorVersion': 0}])), \ - patch('salt.modules.win_iis.list_bindings', - MagicMock(return_value=BINDING_LIST)), \ - patch('salt.modules.win_iis.list_cert_bindings', - MagicMock(return_value={CERT_BINDING_INFO: BINDING_LIST[CERT_BINDING_INFO]})): + """ + kwargs = { + "name": "9988776655443322111000AAABBBCCCDDDEEEFFF", + "site": "MyTestSite", + "hostheader": "mytestsite.local", + "ipaddress": "*", + "port": 443, + } + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._list_certs", + MagicMock(return_value={"9988776655443322111000AAABBBCCCDDDEEEFFF": None}), + ), patch( + "salt.modules.win_iis._srvmgr", + MagicMock(return_value={"retcode": 0, "stdout": 10}), + ), patch( + "salt.utils.json.loads", + MagicMock(return_value=[{"MajorVersion": 10, "MinorVersion": 0}]), + ), patch( + "salt.modules.win_iis.list_bindings", MagicMock(return_value=BINDING_LIST) + ), patch( + "salt.modules.win_iis.list_cert_bindings", + MagicMock( + return_value={CERT_BINDING_INFO: BINDING_LIST[CERT_BINDING_INFO]} + ), + ): self.assertTrue(win_iis.create_cert_binding(**kwargs)) def test_list_cert_bindings(self): - ''' + """ Test - List certificate bindings for an IIS site. - ''' - key = '*:443:mytestsite.local' - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis.list_sites', - MagicMock(return_value=SITE_LIST)): - self.assertEqual(win_iis.list_cert_bindings('MyTestSite'), - {key: BINDING_LIST[key]}) + """ + key = "*:443:mytestsite.local" + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis.list_sites", MagicMock(return_value=SITE_LIST) + ): + self.assertEqual( + win_iis.list_cert_bindings("MyTestSite"), {key: BINDING_LIST[key]} + ) def test_remove_cert_binding(self): - ''' + """ Test - Remove a certificate from an IIS binding. - ''' - kwargs = {'name': 'FFFEEEDDDCCCBBBAAA0001112233445566778899', - 'site': 'MyOtherTestSite', 'hostheader': 'myothertestsite.local', - 'ipaddress': '*', 'port': 443} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.list_cert_bindings', - MagicMock(return_value={CERT_BINDING_INFO: BINDING_LIST[CERT_BINDING_INFO]})): + """ + kwargs = { + "name": "FFFEEEDDDCCCBBBAAA0001112233445566778899", + "site": "MyOtherTestSite", + "hostheader": "myothertestsite.local", + "ipaddress": "*", + "port": 443, + } + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.list_cert_bindings", + MagicMock( + return_value={CERT_BINDING_INFO: BINDING_LIST[CERT_BINDING_INFO]} + ), + ): self.assertTrue(win_iis.remove_cert_binding(**kwargs)) def test_get_container_setting(self): - ''' + """ Test - Get the value of the setting for the IIS container. - ''' - kwargs = {'name': 'MyTestSite', 'container': 'AppPools', - 'settings': ['managedPipelineMode']} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value=CONTAINER_SETTING)): - self.assertEqual(win_iis.get_container_setting(**kwargs), - {'managedPipelineMode': 'Integrated'}) + """ + kwargs = { + "name": "MyTestSite", + "container": "AppPools", + "settings": ["managedPipelineMode"], + } + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value=CONTAINER_SETTING) + ): + self.assertEqual( + win_iis.get_container_setting(**kwargs), + {"managedPipelineMode": "Integrated"}, + ) def test_set_container_setting(self): - ''' + """ Test - Set the value of the setting for an IIS container. - ''' - kwargs = {'name': 'MyTestSite', 'container': 'AppPools', - 'settings': {'managedPipelineMode': 'Integrated'}} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0})), \ - patch('salt.modules.win_iis.get_container_setting', - MagicMock(return_value={'managedPipelineMode': 'Integrated'})): + """ + kwargs = { + "name": "MyTestSite", + "container": "AppPools", + "settings": {"managedPipelineMode": "Integrated"}, + } + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._srvmgr", MagicMock(return_value={"retcode": 0}) + ), patch( + "salt.modules.win_iis.get_container_setting", + MagicMock(return_value={"managedPipelineMode": "Integrated"}), + ): self.assertTrue(win_iis.set_container_setting(**kwargs)) def test__collection_match_to_index(self): - bad_match = {'key_0': 'value'} - first_match = {'key_1': 'value'} - second_match = {'key_2': 'value'} + bad_match = {"key_0": "value"} + first_match = {"key_1": "value"} + second_match = {"key_2": "value"} collection = [first_match, second_match] - settings = [{'name': 'enabled', 'value': collection}] - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis.get_webconfiguration_settings', - MagicMock(return_value=settings)): - ret = win_iis._collection_match_to_index('pspath', 'colfilter', 'name', bad_match) + settings = [{"name": "enabled", "value": collection}] + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis.get_webconfiguration_settings", + MagicMock(return_value=settings), + ): + ret = win_iis._collection_match_to_index( + "pspath", "colfilter", "name", bad_match + ) self.assertEqual(ret, -1) - ret = win_iis._collection_match_to_index('pspath', 'colfilter', 'name', first_match) + ret = win_iis._collection_match_to_index( + "pspath", "colfilter", "name", first_match + ) self.assertEqual(ret, 0) - ret = win_iis._collection_match_to_index('pspath', 'colfilter', 'name', second_match) + ret = win_iis._collection_match_to_index( + "pspath", "colfilter", "name", second_match + ) self.assertEqual(ret, 1) def test__prepare_settings(self): - simple_setting = {'name': 'value', 'filter': 'value'} - collection_setting = {'name': 'Collection[{yaml:\n\tdata}]', 'filter': 'value'} - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._collection_match_to_index', - MagicMock(return_value=0)): - ret = win_iis._prepare_settings('pspath', [ - simple_setting, collection_setting, {'invalid': 'setting'}, {'name': 'filter-less_setting'} - ]) + simple_setting = {"name": "value", "filter": "value"} + collection_setting = {"name": "Collection[{yaml:\n\tdata}]", "filter": "value"} + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._collection_match_to_index", MagicMock(return_value=0) + ): + ret = win_iis._prepare_settings( + "pspath", + [ + simple_setting, + collection_setting, + {"invalid": "setting"}, + {"name": "filter-less_setting"}, + ], + ) self.assertEqual(ret, [simple_setting, collection_setting]) - @patch('salt.modules.win_iis.log') + @patch("salt.modules.win_iis.log") def test_get_webconfiguration_settings_empty(self, mock_log): - ret = win_iis.get_webconfiguration_settings('name', settings=[]) - mock_log.warning.assert_called_once_with('No settings provided') + ret = win_iis.get_webconfiguration_settings("name", settings=[]) + mock_log.warning.assert_called_once_with("No settings provided") self.assertEqual(ret, {}) def test_get_webconfiguration_settings(self): # Setup - name = 'IIS' - collection_setting = {'name': 'Collection[{yaml:\n\tdata}]', 'filter': 'value'} - filter_setting = {'name': 'enabled', - 'filter': 'system.webServer / security / authentication / anonymousAuthentication'} + name = "IIS" + collection_setting = {"name": "Collection[{yaml:\n\tdata}]", "filter": "value"} + filter_setting = { + "name": "enabled", + "filter": "system.webServer / security / authentication / anonymousAuthentication", + } settings = [collection_setting, filter_setting] - ps_cmd = ['$Settings = New-Object System.Collections.ArrayList;', ] + ps_cmd = [ + "$Settings = New-Object System.Collections.ArrayList;", + ] for setting in settings: - ps_cmd.extend([ - "$Property = Get-WebConfigurationProperty -PSPath '{}'".format(name), - "-Name '{name}' -Filter '{filter}' -ErrorAction Stop;".format( - filter=setting['filter'], name=setting['name']), - 'if (([String]::IsNullOrEmpty($Property) -eq $False) -and', - "($Property.GetType()).Name -eq 'ConfigurationAttribute') {", - '$Property = $Property | Select-Object', - '-ExpandProperty Value };', - "$Settings.add(@{{filter='{filter}';name='{name}';value=[String] $Property}})| Out-Null;".format( - filter=setting['filter'], name=setting['name']), - '$Property = $Null;', - ]) - ps_cmd.append('$Settings') + ps_cmd.extend( + [ + "$Property = Get-WebConfigurationProperty -PSPath '{}'".format( + name + ), + "-Name '{name}' -Filter '{filter}' -ErrorAction Stop;".format( + filter=setting["filter"], name=setting["name"] + ), + "if (([String]::IsNullOrEmpty($Property) -eq $False) -and", + "($Property.GetType()).Name -eq 'ConfigurationAttribute') {", + "$Property = $Property | Select-Object", + "-ExpandProperty Value };", + "$Settings.add(@{{filter='{filter}';name='{name}';value=[String] $Property}})| Out-Null;".format( + filter=setting["filter"], name=setting["name"] + ), + "$Property = $Null;", + ] + ) + ps_cmd.append("$Settings") # Execute - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._prepare_settings', - MagicMock(return_value=settings)), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0, 'stdout': '{}'})): + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._prepare_settings", MagicMock(return_value=settings) + ), patch( + "salt.modules.win_iis._srvmgr", + MagicMock(return_value={"retcode": 0, "stdout": "{}"}), + ): ret = win_iis.get_webconfiguration_settings(name, settings=settings) # Verify win_iis._srvmgr.assert_called_with(cmd=ps_cmd, return_json=True) self.assertEqual(ret, {}) - @patch('salt.modules.win_iis.log') + @patch("salt.modules.win_iis.log") def test_set_webconfiguration_settings_empty(self, mock_log): - ret = win_iis.set_webconfiguration_settings('name', settings=[]) - mock_log.warning.assert_called_once_with('No settings provided') + ret = win_iis.set_webconfiguration_settings("name", settings=[]) + mock_log.warning.assert_called_once_with("No settings provided") self.assertEqual(ret, False) - @patch('salt.modules.win_iis.log') + @patch("salt.modules.win_iis.log") def test_set_webconfiguration_settings_no_changes(self, mock_log): # Setup - name = 'IIS' + name = "IIS" setting = { - 'name': 'Collection[{yaml:\n\tdata}]', - 'filter': 'system.webServer / security / authentication / anonymousAuthentication', - 'value': [] + "name": "Collection[{yaml:\n\tdata}]", + "filter": "system.webServer / security / authentication / anonymousAuthentication", + "value": [], } settings = [setting] # Execute - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._prepare_settings', - MagicMock(return_value=settings)), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0, 'stdout': '{}'})), \ - patch('salt.modules.win_iis.get_webconfiguration_settings', - MagicMock(return_value=settings)): + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._prepare_settings", MagicMock(return_value=settings) + ), patch( + "salt.modules.win_iis._srvmgr", + MagicMock(return_value={"retcode": 0, "stdout": "{}"}), + ), patch( + "salt.modules.win_iis.get_webconfiguration_settings", + MagicMock(return_value=settings), + ): ret = win_iis.set_webconfiguration_settings(name, settings=settings) # Verify - mock_log.debug.assert_called_with('Settings already contain the provided values.') + mock_log.debug.assert_called_with( + "Settings already contain the provided values." + ) self.assertEqual(ret, True) - @patch('salt.modules.win_iis.log') + @patch("salt.modules.win_iis.log") def test_set_webconfiguration_settings_failed(self, mock_log): # Setup - name = 'IIS' + name = "IIS" setting = { - 'name': 'Collection[{yaml:\n\tdata}]', - 'filter': 'system.webServer / security / authentication / anonymousAuthentication', - 'value': [] + "name": "Collection[{yaml:\n\tdata}]", + "filter": "system.webServer / security / authentication / anonymousAuthentication", + "value": [], } settings = [setting] # Execute - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._prepare_settings', - MagicMock(return_value=settings)), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0, 'stdout': '{}'})), \ - patch('salt.modules.win_iis.get_webconfiguration_settings', - MagicMock(side_effect=[[], [{'value': 'unexpected_change!'}]])): + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._prepare_settings", MagicMock(return_value=settings) + ), patch( + "salt.modules.win_iis._srvmgr", + MagicMock(return_value={"retcode": 0, "stdout": "{}"}), + ), patch( + "salt.modules.win_iis.get_webconfiguration_settings", + MagicMock(side_effect=[[], [{"value": "unexpected_change!"}]]), + ): ret = win_iis.set_webconfiguration_settings(name, settings=settings) # Verify self.assertEqual(ret, False) - mock_log.error.assert_called_with('Failed to change settings: %s', settings) + mock_log.error.assert_called_with("Failed to change settings: %s", settings) - @patch('salt.modules.win_iis.log') + @patch("salt.modules.win_iis.log") def test_set_webconfiguration_settings(self, mock_log): # Setup - name = 'IIS' + name = "IIS" setting = { - 'name': 'Collection[{yaml:\n\tdata}]', - 'filter': 'system.webServer / security / authentication / anonymousAuthentication', - 'value': [] + "name": "Collection[{yaml:\n\tdata}]", + "filter": "system.webServer / security / authentication / anonymousAuthentication", + "value": [], } settings = [setting] # Execute - with patch.dict(win_iis.__salt__), \ - patch('salt.modules.win_iis._prepare_settings', - MagicMock(return_value=settings)), \ - patch('salt.modules.win_iis._srvmgr', - MagicMock(return_value={'retcode': 0, 'stdout': '{}'})), \ - patch('salt.modules.win_iis.get_webconfiguration_settings', - MagicMock(side_effect=[[], settings])): + with patch.dict(win_iis.__salt__), patch( + "salt.modules.win_iis._prepare_settings", MagicMock(return_value=settings) + ), patch( + "salt.modules.win_iis._srvmgr", + MagicMock(return_value={"retcode": 0, "stdout": "{}"}), + ), patch( + "salt.modules.win_iis.get_webconfiguration_settings", + MagicMock(side_effect=[[], settings]), + ): ret = win_iis.set_webconfiguration_settings(name, settings=settings) # Verify self.assertEqual(ret, True) - mock_log.debug.assert_called_with('Settings configured successfully: %s', settings) + mock_log.debug.assert_called_with( + "Settings configured successfully: %s", settings + ) diff --git a/tests/unit/modules/test_win_ip.py b/tests/unit/modules/test_win_ip.py index 9b135475fda..83f040e2d14 100644 --- a/tests/unit/modules/test_win_ip.py +++ b/tests/unit/modules/test_win_ip.py @@ -1,308 +1,384 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - call -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_ip as win_ip from salt.exceptions import CommandExecutionError, SaltInvocationError -ETHERNET_CONFIG = ('Configuration for interface "Ethernet"\n' - 'DHCP enabled: Yes\n' - 'IP Address: 1.2.3.74\n' - 'Subnet Prefix: 1.2.3.0/24 (mask 255.255.255.0)\n' - 'Default Gateway: 1.2.3.1\n' - 'Gateway Metric: 0\n' - 'InterfaceMetric: 20\n' - 'DNS servers configured through DHCP: 1.2.3.4\n' - 'Register with which suffix: Primary only\n' - 'WINS servers configured through DHCP: None\n') +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase -ETHERNET_ENABLE = ('Ethernet\n' - 'Type: Dedicated\n' - 'Administrative state: Enabled\n' - 'Connect state: Connected') +ETHERNET_CONFIG = ( + 'Configuration for interface "Ethernet"\n' + "DHCP enabled: Yes\n" + "IP Address: 1.2.3.74\n" + "Subnet Prefix: 1.2.3.0/24 (mask 255.255.255.0)\n" + "Default Gateway: 1.2.3.1\n" + "Gateway Metric: 0\n" + "InterfaceMetric: 20\n" + "DNS servers configured through DHCP: 1.2.3.4\n" + "Register with which suffix: Primary only\n" + "WINS servers configured through DHCP: None\n" +) + +ETHERNET_ENABLE = ( + "Ethernet\n" + "Type: Dedicated\n" + "Administrative state: Enabled\n" + "Connect state: Connected" +) class WinShadowTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_ip - ''' + """ + def setup_loader_modules(self): return {win_ip: {}} # 'raw_interface_configs' function tests: 1 def test_raw_interface_configs(self): - ''' + """ Test if it return raw configs for all interfaces. - ''' + """ mock_cmd = MagicMock(return_value=ETHERNET_CONFIG) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): self.assertEqual(win_ip.raw_interface_configs(), ETHERNET_CONFIG) # 'get_all_interfaces' function tests: 1 def test_get_all_interfaces(self): - ''' + """ Test if it return configs for all interfaces. - ''' - ret = {'Ethernet': {'DHCP enabled': 'Yes', - 'DNS servers configured through DHCP': ['1.2.3.4'], - 'Default Gateway': '1.2.3.1', - 'Gateway Metric': '0', - 'InterfaceMetric': '20', - 'Register with which suffix': 'Primary only', - 'WINS servers configured through DHCP': ['None'], - 'ip_addrs': [{'IP Address': '1.2.3.74', - 'Netmask': '255.255.255.0', - 'Subnet': '1.2.3.0/24'}]}} + """ + ret = { + "Ethernet": { + "DHCP enabled": "Yes", + "DNS servers configured through DHCP": ["1.2.3.4"], + "Default Gateway": "1.2.3.1", + "Gateway Metric": "0", + "InterfaceMetric": "20", + "Register with which suffix": "Primary only", + "WINS servers configured through DHCP": ["None"], + "ip_addrs": [ + { + "IP Address": "1.2.3.74", + "Netmask": "255.255.255.0", + "Subnet": "1.2.3.0/24", + } + ], + } + } mock_cmd = MagicMock(return_value=ETHERNET_CONFIG) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): self.assertDictEqual(win_ip.get_all_interfaces(), ret) # 'get_interface' function tests: 1 def test_get_interface(self): - ''' + """ Test if it return the configuration of a network interface. - ''' - ret = {'DHCP enabled': 'Yes', - 'DNS servers configured through DHCP': ['1.2.3.4'], - 'Default Gateway': '1.2.3.1', 'Gateway Metric': '0', - 'InterfaceMetric': '20', - 'Register with which suffix': 'Primary only', - 'WINS servers configured through DHCP': ['None'], - 'ip_addrs': [{'IP Address': '1.2.3.74', - 'Netmask': '255.255.255.0', - 'Subnet': '1.2.3.0/24'}]} + """ + ret = { + "DHCP enabled": "Yes", + "DNS servers configured through DHCP": ["1.2.3.4"], + "Default Gateway": "1.2.3.1", + "Gateway Metric": "0", + "InterfaceMetric": "20", + "Register with which suffix": "Primary only", + "WINS servers configured through DHCP": ["None"], + "ip_addrs": [ + { + "IP Address": "1.2.3.74", + "Netmask": "255.255.255.0", + "Subnet": "1.2.3.0/24", + } + ], + } mock_cmd = MagicMock(return_value=ETHERNET_CONFIG) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertDictEqual(win_ip.get_interface('Ethernet'), ret) + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): + self.assertDictEqual(win_ip.get_interface("Ethernet"), ret) # 'is_enabled' function tests: 1 def test_is_enabled(self): - ''' + """ Test if it returns `True` if interface is enabled, otherwise `False`. - ''' - mock_cmd = MagicMock(side_effect=[ETHERNET_ENABLE, '']) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertTrue(win_ip.is_enabled('Ethernet')) - self.assertRaises(CommandExecutionError, win_ip.is_enabled, - 'Ethernet') + """ + mock_cmd = MagicMock(side_effect=[ETHERNET_ENABLE, ""]) + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): + self.assertTrue(win_ip.is_enabled("Ethernet")) + self.assertRaises(CommandExecutionError, win_ip.is_enabled, "Ethernet") # 'is_disabled' function tests: 1 def test_is_disabled(self): - ''' + """ Test if it returns `True` if interface is disabled, otherwise `False`. - ''' + """ mock_cmd = MagicMock(return_value=ETHERNET_ENABLE) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertFalse(win_ip.is_disabled('Ethernet')) + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): + self.assertFalse(win_ip.is_disabled("Ethernet")) # 'enable' function tests: 1 def test_enable(self): - ''' + """ Test if it enable an interface. - ''' + """ # Test with enabled interface - with patch.object(win_ip, 'is_enabled', return_value=True): - self.assertTrue(win_ip.enable('Ethernet')) + with patch.object(win_ip, "is_enabled", return_value=True): + self.assertTrue(win_ip.enable("Ethernet")) mock_cmd = MagicMock() - with patch.object(win_ip, 'is_enabled', side_effect=[False, True]), \ - patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertTrue(win_ip.enable('Ethernet')) + with patch.object(win_ip, "is_enabled", side_effect=[False, True]), patch.dict( + win_ip.__salt__, {"cmd.run": mock_cmd} + ): + self.assertTrue(win_ip.enable("Ethernet")) mock_cmd.called_once_with( - ['netsh', 'interface', 'set', 'interface', - 'name=Ethernet', - 'admin=ENABLED'], - python_shell=False + [ + "netsh", + "interface", + "set", + "interface", + "name=Ethernet", + "admin=ENABLED", + ], + python_shell=False, ) # 'disable' function tests: 1 def test_disable(self): - ''' + """ Test if it disable an interface. - ''' - with patch.object(win_ip, 'is_disabled', return_value=True): - self.assertTrue(win_ip.disable('Ethernet')) + """ + with patch.object(win_ip, "is_disabled", return_value=True): + self.assertTrue(win_ip.disable("Ethernet")) mock_cmd = MagicMock() - with patch.object(win_ip, 'is_disabled', side_effect=[False, True]),\ - patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertTrue(win_ip.disable('Ethernet')) + with patch.object(win_ip, "is_disabled", side_effect=[False, True]), patch.dict( + win_ip.__salt__, {"cmd.run": mock_cmd} + ): + self.assertTrue(win_ip.disable("Ethernet")) mock_cmd.called_once_with( - ['netsh', 'interface', 'set', 'interface', - 'name=Ethernet', - 'admin=DISABLED'], - python_shell=False + [ + "netsh", + "interface", + "set", + "interface", + "name=Ethernet", + "admin=DISABLED", + ], + python_shell=False, ) # 'get_subnet_length' function tests: 1 def test_get_subnet_length(self): - ''' + """ Test if it disable an interface. - ''' - self.assertEqual(win_ip.get_subnet_length('255.255.255.0'), 24) - self.assertRaises(SaltInvocationError, win_ip.get_subnet_length, - '255.255.0') + """ + self.assertEqual(win_ip.get_subnet_length("255.255.255.0"), 24) + self.assertRaises(SaltInvocationError, win_ip.get_subnet_length, "255.255.0") # 'set_static_ip' function tests: 1 def test_set_static_ip(self): - ''' + """ Test if it set static IP configuration on a Windows NIC. - ''' - self.assertRaises(SaltInvocationError, win_ip.set_static_ip, - 'Local Area Connection', '10.1.2/24') + """ + self.assertRaises( + SaltInvocationError, + win_ip.set_static_ip, + "Local Area Connection", + "10.1.2/24", + ) mock_cmd = MagicMock(return_value=ETHERNET_CONFIG) - mock_all = MagicMock(return_value={'retcode': 1, 'stderr': 'Error'}) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd, - 'cmd.run_all': mock_all}): - self.assertRaises(CommandExecutionError, win_ip.set_static_ip, - 'Ethernet', '1.2.3.74/24', append=True) - self.assertRaises(CommandExecutionError, win_ip.set_static_ip, - 'Ethernet', '1.2.3.74/24') + mock_all = MagicMock(return_value={"retcode": 1, "stderr": "Error"}) + with patch.dict( + win_ip.__salt__, {"cmd.run": mock_cmd, "cmd.run_all": mock_all} + ): + self.assertRaises( + CommandExecutionError, + win_ip.set_static_ip, + "Ethernet", + "1.2.3.74/24", + append=True, + ) + self.assertRaises( + CommandExecutionError, win_ip.set_static_ip, "Ethernet", "1.2.3.74/24" + ) - mock_all = MagicMock(return_value={'retcode': 0}) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd, - 'cmd.run_all': mock_all}): - self.assertDictEqual(win_ip.set_static_ip('Local Area Connection', - '1.2.3.74/24'), {}) - self.assertDictEqual(win_ip.set_static_ip('Ethernet', - '1.2.3.74/24'), - {'Address Info': {'IP Address': '1.2.3.74', - 'Netmask': '255.255.255.0', - 'Subnet': '1.2.3.0/24'}}) + mock_all = MagicMock(return_value={"retcode": 0}) + with patch.dict( + win_ip.__salt__, {"cmd.run": mock_cmd, "cmd.run_all": mock_all} + ): + self.assertDictEqual( + win_ip.set_static_ip("Local Area Connection", "1.2.3.74/24"), {} + ) + self.assertDictEqual( + win_ip.set_static_ip("Ethernet", "1.2.3.74/24"), + { + "Address Info": { + "IP Address": "1.2.3.74", + "Netmask": "255.255.255.0", + "Subnet": "1.2.3.0/24", + } + }, + ) # 'set_dhcp_ip' function tests: 1 def test_set_dhcp_ip(self): - ''' + """ Test if it set Windows NIC to get IP from DHCP. - ''' + """ mock_cmd = MagicMock(return_value=ETHERNET_CONFIG) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertDictEqual(win_ip.set_dhcp_ip('Ethernet'), - {'DHCP enabled': 'Yes', - 'Interface': 'Ethernet'}) + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): + self.assertDictEqual( + win_ip.set_dhcp_ip("Ethernet"), + {"DHCP enabled": "Yes", "Interface": "Ethernet"}, + ) # 'set_static_dns' function tests: 1 def test_set_static_dns(self): - ''' + """ Test if it set static DNS configuration on a Windows NIC. - ''' + """ mock_cmd = MagicMock() - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertDictEqual(win_ip.set_static_dns('Ethernet', - '192.168.1.252', - '192.168.1.253'), - {'DNS Server': ('192.168.1.252', - '192.168.1.253'), - 'Interface': 'Ethernet'}) - mock_cmd.assert_has_calls([ - call(['netsh', 'interface', 'ip', 'set', 'dns', - 'name=Ethernet', - 'source=static', - 'address=192.168.1.252', - 'register=primary'], - python_shell=False), - call(['netsh', 'interface', 'ip', 'add', 'dns', - 'name=Ethernet', - 'address=192.168.1.253', - 'index=2'], - python_shell=False)] + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): + self.assertDictEqual( + win_ip.set_static_dns("Ethernet", "192.168.1.252", "192.168.1.253"), + { + "DNS Server": ("192.168.1.252", "192.168.1.253"), + "Interface": "Ethernet", + }, + ) + mock_cmd.assert_has_calls( + [ + call( + [ + "netsh", + "interface", + "ip", + "set", + "dns", + "name=Ethernet", + "source=static", + "address=192.168.1.252", + "register=primary", + ], + python_shell=False, + ), + call( + [ + "netsh", + "interface", + "ip", + "add", + "dns", + "name=Ethernet", + "address=192.168.1.253", + "index=2", + ], + python_shell=False, + ), + ] ) def test_set_static_dns_clear(self): - ''' + """ Test if it set static DNS configuration on a Windows NIC. - ''' + """ mock_cmd = MagicMock() - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertDictEqual(win_ip.set_static_dns('Ethernet', []), - {'DNS Server': [], - 'Interface': 'Ethernet'}) + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): + self.assertDictEqual( + win_ip.set_static_dns("Ethernet", []), + {"DNS Server": [], "Interface": "Ethernet"}, + ) mock_cmd.assert_called_once_with( - ['netsh', 'interface', 'ip', 'set', 'dns', - 'name=Ethernet', - 'source=static', - 'address=none'], - python_shell=False + [ + "netsh", + "interface", + "ip", + "set", + "dns", + "name=Ethernet", + "source=static", + "address=none", + ], + python_shell=False, ) def test_set_static_dns_no_action(self): - ''' + """ Test if it set static DNS configuration on a Windows NIC. - ''' + """ # Test passing nothing - self.assertDictEqual(win_ip.set_static_dns('Ethernet'), - {'DNS Server': 'No Changes', - 'Interface': 'Ethernet'}) + self.assertDictEqual( + win_ip.set_static_dns("Ethernet"), + {"DNS Server": "No Changes", "Interface": "Ethernet"}, + ) # Test passing None - self.assertDictEqual(win_ip.set_static_dns('Ethernet', None), - {'DNS Server': 'No Changes', - 'Interface': 'Ethernet'}) + self.assertDictEqual( + win_ip.set_static_dns("Ethernet", None), + {"DNS Server": "No Changes", "Interface": "Ethernet"}, + ) # Test passing string None - self.assertDictEqual(win_ip.set_static_dns('Ethernet', 'None'), - {'DNS Server': 'No Changes', - 'Interface': 'Ethernet'}) + self.assertDictEqual( + win_ip.set_static_dns("Ethernet", "None"), + {"DNS Server": "No Changes", "Interface": "Ethernet"}, + ) # 'set_dhcp_dns' function tests: 1 def test_set_dhcp_dns(self): - ''' + """ Test if it set DNS source to DHCP on Windows. - ''' + """ mock_cmd = MagicMock(return_value=ETHERNET_CONFIG) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertDictEqual(win_ip.set_dhcp_dns('Ethernet'), - {'DNS Server': 'DHCP', - 'Interface': 'Ethernet'}) + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): + self.assertDictEqual( + win_ip.set_dhcp_dns("Ethernet"), + {"DNS Server": "DHCP", "Interface": "Ethernet"}, + ) # 'set_dhcp_all' function tests: 1 def test_set_dhcp_all(self): - ''' + """ Test if it set both IP Address and DNS to DHCP. - ''' + """ mock_cmd = MagicMock(return_value=ETHERNET_CONFIG) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertDictEqual(win_ip.set_dhcp_all('Ethernet'), - {'Interface': 'Ethernet', 'DNS Server': 'DHCP', - 'DHCP enabled': 'Yes'}) + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): + self.assertDictEqual( + win_ip.set_dhcp_all("Ethernet"), + {"Interface": "Ethernet", "DNS Server": "DHCP", "DHCP enabled": "Yes"}, + ) # 'get_default_gateway' function tests: 1 def test_get_default_gateway(self): - ''' + """ Test if it set DNS source to DHCP on Windows. - ''' + """ mock_cmd = MagicMock(return_value=ETHERNET_CONFIG) - with patch.dict(win_ip.__salt__, {'cmd.run': mock_cmd}): - self.assertEqual(win_ip.get_default_gateway(), '1.2.3.1') + with patch.dict(win_ip.__salt__, {"cmd.run": mock_cmd}): + self.assertEqual(win_ip.get_default_gateway(), "1.2.3.1") diff --git a/tests/unit/modules/test_win_lgpo.py b/tests/unit/modules/test_win_lgpo.py index fbaaff827b1..61d0651057a 100644 --- a/tests/unit/modules/test_win_lgpo.py +++ b/tests/unit/modules/test_win_lgpo.py @@ -1,21 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Shane Lee <slee@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import glob import os -# Import Salt Testing Libs -from tests.support.helpers import destructiveTest -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import MagicMock, Mock, patch -from tests.support.unit import TestCase, skipIf - # Import Salt Libs import salt.config + +# Import 3rd Party Libs +import salt.ext.six as six import salt.loader import salt.modules.win_lgpo as win_lgpo import salt.states.win_lgpo @@ -23,338 +21,378 @@ import salt.utils.files import salt.utils.platform import salt.utils.stringutils -# Import 3rd Party Libs -import salt.ext.six as six +# Import Salt Testing Libs +from tests.support.helpers import destructiveTest +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase, skipIf # We're going to actually use the loader, without grains (slow) opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils(opts) modules = salt.loader.minion_mods(opts, utils=utils) -LOADER_DICTS = { - win_lgpo: { - '__opts__': opts, - '__salt__': modules, - '__utils__': utils, - } -} +LOADER_DICTS = {win_lgpo: {"__opts__": opts, "__salt__": modules, "__utils__": utils}} class WinLGPOTestCase(TestCase): - ''' + """ Test cases for salt.modules.win_lgpo - ''' - encoded_null = chr(0).encode('utf-16-le') + """ + + encoded_null = chr(0).encode("utf-16-le") def test__getAdmlDisplayName(self): - display_name = '$(string.KeepAliveTime1)' - adml_xml_data = 'junk, we are mocking the return' + display_name = "$(string.KeepAliveTime1)" + adml_xml_data = "junk, we are mocking the return" obj_xpath = Mock() - obj_xpath.text = '300000 or 5 minutes (recommended) ' + obj_xpath.text = "300000 or 5 minutes (recommended) " mock_xpath_obj = MagicMock(return_value=[obj_xpath]) - with patch.object(win_lgpo, 'ADML_DISPLAY_NAME_XPATH', mock_xpath_obj): - result = win_lgpo._getAdmlDisplayName(adml_xml_data=adml_xml_data, - display_name=display_name) - expected = '300000 or 5 minutes (recommended)' + with patch.object(win_lgpo, "ADML_DISPLAY_NAME_XPATH", mock_xpath_obj): + result = win_lgpo._getAdmlDisplayName( + adml_xml_data=adml_xml_data, display_name=display_name + ) + expected = "300000 or 5 minutes (recommended)" self.assertEqual(result, expected) def test__regexSearchKeyValueCombo_enabled(self): - ''' + """ Make sure - ''' - policy_data = b'[\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p' \ - b'\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i' \ - b'\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i' \ - b'\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d' \ - b'\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 ' \ - b'\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\' \ - b'\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00\x00;\x00D' \ - b'\x00e\x00f\x00a\x00u\x00l\x00t\x00C\x00o\x00n\x00s' \ - b'\x00e\x00n\x00t\x00\x00\x00;\x00\x01\x00\x00\x00;\x00' \ - b'\x04\x00\x00\x00;\x00\x02\x00\x00\x00]\x00' - policy_regpath = b'\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p' \ - b'\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i' \ - b'\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i' \ - b'\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d' \ - b'\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 ' \ - b'\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\' \ - b'\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00' - policy_regkey = b'\x00D\x00e\x00f\x00a\x00u\x00l\x00t\x00C\x00o\x00n' \ - b'\x00s\x00e\x00n\x00t\x00\x00' + """ + policy_data = ( + b"[\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p" + b"\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i" + b"\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i" + b"\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d" + b"\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 " + b"\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\" + b"\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00\x00;\x00D" + b"\x00e\x00f\x00a\x00u\x00l\x00t\x00C\x00o\x00n\x00s" + b"\x00e\x00n\x00t\x00\x00\x00;\x00\x01\x00\x00\x00;\x00" + b"\x04\x00\x00\x00;\x00\x02\x00\x00\x00]\x00" + ) + policy_regpath = ( + b"\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p" + b"\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i" + b"\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i" + b"\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d" + b"\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 " + b"\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\" + b"\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00" + ) + policy_regkey = ( + b"\x00D\x00e\x00f\x00a\x00u\x00l\x00t\x00C\x00o\x00n" + b"\x00s\x00e\x00n\x00t\x00\x00" + ) test = win_lgpo._regexSearchKeyValueCombo( policy_data=policy_data, policy_regpath=policy_regpath, - policy_regkey=policy_regkey + policy_regkey=policy_regkey, ) self.assertEqual(test, policy_data) def test__regexSearchKeyValueCombo_not_configured(self): - ''' + """ Make sure - ''' - policy_data = b'' - policy_regpath = b'\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p' \ - b'\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i' \ - b'\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i' \ - b'\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d' \ - b'\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 ' \ - b'\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\' \ - b'\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00' - policy_regkey = b'\x00D\x00e\x00f\x00a\x00u\x00l\x00t\x00C\x00o\x00n' \ - b'\x00s\x00e\x00n\x00t\x00\x00' + """ + policy_data = b"" + policy_regpath = ( + b"\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p" + b"\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i" + b"\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i" + b"\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d" + b"\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 " + b"\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\" + b"\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00" + ) + policy_regkey = ( + b"\x00D\x00e\x00f\x00a\x00u\x00l\x00t\x00C\x00o\x00n" + b"\x00s\x00e\x00n\x00t\x00\x00" + ) test = win_lgpo._regexSearchKeyValueCombo( policy_data=policy_data, policy_regpath=policy_regpath, - policy_regkey=policy_regkey + policy_regkey=policy_regkey, ) self.assertIsNone(test) def test__regexSearchKeyValueCombo_disabled(self): - ''' + """ Make sure - ''' - policy_data = b'[\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p' \ - b'\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i' \ - b'\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i' \ - b'\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d' \ - b'\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 ' \ - b'\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\' \ - b'\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00\x00;\x00*' \ - b'\x00*\x00d\x00e\x00l\x00.\x00D\x00e\x00f\x00a\x00u' \ - b'\x00l\x00t\x00C\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00' \ - b'\x00;\x00\x01\x00\x00\x00;\x00\x04\x00\x00\x00;\x00 ' \ - b'\x00\x00\x00]\x00' - policy_regpath = b'\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p' \ - b'\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i' \ - b'\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i' \ - b'\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d' \ - b'\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 ' \ - b'\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\' \ - b'\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00' - policy_regkey = b'\x00D\x00e\x00f\x00a\x00u\x00l\x00t\x00C\x00o\x00n' \ - b'\x00s\x00e\x00n\x00t\x00\x00' + """ + policy_data = ( + b"[\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p" + b"\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i" + b"\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i" + b"\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d" + b"\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 " + b"\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\" + b"\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00\x00;\x00*" + b"\x00*\x00d\x00e\x00l\x00.\x00D\x00e\x00f\x00a\x00u" + b"\x00l\x00t\x00C\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00" + b"\x00;\x00\x01\x00\x00\x00;\x00\x04\x00\x00\x00;\x00 " + b"\x00\x00\x00]\x00" + ) + policy_regpath = ( + b"\x00s\x00o\x00f\x00t\x00w\x00a\x00r\x00e\x00\\\x00p" + b"\x00o\x00l\x00i\x00c\x00i\x00e\x00s\x00\\\x00m\x00i" + b"\x00c\x00r\x00o\x00s\x00o\x00f\x00t\x00\\\x00w\x00i" + b"\x00n\x00d\x00o\x00w\x00s\x00\\\x00w\x00i\x00n\x00d" + b"\x00o\x00w\x00s\x00 \x00e\x00r\x00r\x00o\x00r\x00 " + b"\x00r\x00e\x00p\x00o\x00r\x00t\x00i\x00n\x00g\x00\\" + b"\x00c\x00o\x00n\x00s\x00e\x00n\x00t\x00\x00" + ) + policy_regkey = ( + b"\x00D\x00e\x00f\x00a\x00u\x00l\x00t\x00C\x00o\x00n" + b"\x00s\x00e\x00n\x00t\x00\x00" + ) test = win_lgpo._regexSearchKeyValueCombo( policy_data=policy_data, policy_regpath=policy_regpath, - policy_regkey=policy_regkey + policy_regkey=policy_regkey, ) self.assertEqual(test, policy_data) def test__encode_string(self): - ''' + """ ``_encode_string`` should return a null terminated ``utf-16-le`` encoded string when a string value is passed - ''' - encoded_value = b''.join(['Salt is awesome'.encode('utf-16-le'), - self.encoded_null]) - value = win_lgpo._encode_string('Salt is awesome') + """ + encoded_value = b"".join( + ["Salt is awesome".encode("utf-16-le"), self.encoded_null] + ) + value = win_lgpo._encode_string("Salt is awesome") self.assertEqual(value, encoded_value) def test__encode_string_empty_string(self): - ''' + """ ``_encode_string`` should return an encoded null when an empty string value is passed - ''' - value = win_lgpo._encode_string('') + """ + value = win_lgpo._encode_string("") self.assertEqual(value, self.encoded_null) def test__encode_string_error(self): - ''' + """ ``_encode_string`` should raise an error if a non-string value is passed - ''' + """ self.assertRaises(TypeError, win_lgpo._encode_string, [1]) - test_list = ['item1', 'item2'] + test_list = ["item1", "item2"] self.assertRaises(TypeError, win_lgpo._encode_string, [test_list]) - test_dict = {'key1': 'value1', 'key2': 'value2'} + test_dict = {"key1": "value1", "key2": "value2"} self.assertRaises(TypeError, win_lgpo._encode_string, [test_dict]) def test__encode_string_none(self): - ''' + """ ``_encode_string`` should return an encoded null when ``None`` is passed - ''' + """ value = win_lgpo._encode_string(None) self.assertEqual(value, self.encoded_null) def test__multi_string_get_transform_list(self): - ''' + """ ``_multi_string_get_transform`` should return the list when a list is passed - ''' - test_value = ['Spongebob', 'Squarepants'] + """ + test_value = ["Spongebob", "Squarepants"] value = win_lgpo._policy_info._multi_string_get_transform(item=test_value) self.assertEqual(value, test_value) def test__multi_string_get_transform_none(self): - ''' + """ ``_multi_string_get_transform`` should return "Not Defined" when ``None`` is passed - ''' + """ test_value = None value = win_lgpo._policy_info._multi_string_get_transform(item=test_value) - self.assertEqual(value, 'Not Defined') + self.assertEqual(value, "Not Defined") def test__multi_string_get_transform_invalid(self): - ''' + """ ``_multi_string_get_transform`` should return "Not Defined" when ``None`` is passed - ''' - test_value = 'Some String' + """ + test_value = "Some String" value = win_lgpo._policy_info._multi_string_get_transform(item=test_value) - self.assertEqual(value, 'Invalid Value') + self.assertEqual(value, "Invalid Value") def test__multi_string_put_transform_list(self): - ''' + """ ``_multi_string_put_transform`` should return the list when a list is passed - ''' - test_value = ['Spongebob', 'Squarepants'] + """ + test_value = ["Spongebob", "Squarepants"] value = win_lgpo._policy_info._multi_string_put_transform(item=test_value) self.assertEqual(value, test_value) def test__multi_string_put_transform_none(self): - ''' + """ ``_multi_string_put_transform`` should return ``None`` when "Not Defined" is passed - ''' + """ test_value = "Not Defined" value = win_lgpo._policy_info._multi_string_put_transform(item=test_value) self.assertEqual(value, None) def test__multi_string_put_transform_list_from_string(self): - ''' + """ ``_multi_string_put_transform`` should return a list when a comma delimited string is passed - ''' + """ test_value = "Spongebob,Squarepants" value = win_lgpo._policy_info._multi_string_put_transform(item=test_value) - self.assertEqual(value, ['Spongebob', 'Squarepants']) + self.assertEqual(value, ["Spongebob", "Squarepants"]) def test__multi_string_put_transform_invalid(self): - ''' + """ ``_multi_string_put_transform`` should return "Invalid" value if neither string nor list is passed - ''' + """ test_value = None value = win_lgpo._policy_info._multi_string_put_transform(item=test_value) self.assertEqual(value, "Invalid Value") -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLGPOGetPolicyADMXTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test functions related to the ``get_policy`` function using policy templates (admx/adml) - ''' + """ + def setup_loader_modules(self): return LOADER_DICTS def test_get_policy_name(self): - result = win_lgpo.get_policy(policy_name='Allow Telemetry', - policy_class='machine', - return_value_only=True, - return_full_policy_names=True, - hierarchical_return=False) - expected = 'Not Configured' + result = win_lgpo.get_policy( + policy_name="Allow Telemetry", + policy_class="machine", + return_value_only=True, + return_full_policy_names=True, + hierarchical_return=False, + ) + expected = "Not Configured" self.assertEqual(result, expected) def test_get_policy_id(self): - result = win_lgpo.get_policy(policy_name='AllowTelemetry', - policy_class='machine', - return_value_only=True, - return_full_policy_names=True, - hierarchical_return=False) - expected = 'Not Configured' + result = win_lgpo.get_policy( + policy_name="AllowTelemetry", + policy_class="machine", + return_value_only=True, + return_full_policy_names=True, + hierarchical_return=False, + ) + expected = "Not Configured" self.assertEqual(result, expected) def test_get_policy_name_full_return_full_names(self): - result = win_lgpo.get_policy(policy_name='Allow Telemetry', - policy_class='machine', - return_value_only=False, - return_full_policy_names=True, - hierarchical_return=False) + result = win_lgpo.get_policy( + policy_name="Allow Telemetry", + policy_class="machine", + return_value_only=False, + return_full_policy_names=True, + hierarchical_return=False, + ) expected = { - 'Windows Components\\Data Collection and Preview Builds\\' - 'Allow Telemetry': 'Not Configured'} + "Windows Components\\Data Collection and Preview Builds\\" + "Allow Telemetry": "Not Configured" + } self.assertDictEqual(result, expected) def test_get_policy_id_full_return_full_names(self): - result = win_lgpo.get_policy(policy_name='AllowTelemetry', - policy_class='machine', - return_value_only=False, - return_full_policy_names=True, - hierarchical_return=False) + result = win_lgpo.get_policy( + policy_name="AllowTelemetry", + policy_class="machine", + return_value_only=False, + return_full_policy_names=True, + hierarchical_return=False, + ) expected = { - 'Windows Components\\Data Collection and Preview Builds\\' - 'Allow Telemetry': 'Not Configured'} + "Windows Components\\Data Collection and Preview Builds\\" + "Allow Telemetry": "Not Configured" + } self.assertDictEqual(result, expected) def test_get_policy_name_full_return_ids(self): - result = win_lgpo.get_policy(policy_name='Allow Telemetry', - policy_class='machine', - return_value_only=False, - return_full_policy_names=False, - hierarchical_return=False) - expected = {'AllowTelemetry': 'Not Configured'} + result = win_lgpo.get_policy( + policy_name="Allow Telemetry", + policy_class="machine", + return_value_only=False, + return_full_policy_names=False, + hierarchical_return=False, + ) + expected = {"AllowTelemetry": "Not Configured"} self.assertDictEqual(result, expected) def test_get_policy_id_full_return_ids(self): - result = win_lgpo.get_policy(policy_name='AllowTelemetry', - policy_class='machine', - return_value_only=False, - return_full_policy_names=False, - hierarchical_return=False) - expected = {'AllowTelemetry': 'Not Configured'} + result = win_lgpo.get_policy( + policy_name="AllowTelemetry", + policy_class="machine", + return_value_only=False, + return_full_policy_names=False, + hierarchical_return=False, + ) + expected = {"AllowTelemetry": "Not Configured"} self.assertDictEqual(result, expected) def test_get_policy_id_full_return_ids_hierarchical(self): - result = win_lgpo.get_policy(policy_name='AllowTelemetry', - policy_class='machine', - return_value_only=False, - return_full_policy_names=False, - hierarchical_return=True) + result = win_lgpo.get_policy( + policy_name="AllowTelemetry", + policy_class="machine", + return_value_only=False, + return_full_policy_names=False, + hierarchical_return=True, + ) expected = { - 'Computer Configuration': { - 'Administrative Templates': { - 'WindowsComponents': { - 'DataCollectionAndPreviewBuilds': { - 'AllowTelemetry': 'Not Configured'}}}}} + "Computer Configuration": { + "Administrative Templates": { + "WindowsComponents": { + "DataCollectionAndPreviewBuilds": { + "AllowTelemetry": "Not Configured" + } + } + } + } + } self.assertDictEqual(result, expected) def test_get_policy_name_return_full_names_hierarchical(self): - result = win_lgpo.get_policy(policy_name='Allow Telemetry', - policy_class='machine', - return_value_only=False, - return_full_policy_names=True, - hierarchical_return=True) + result = win_lgpo.get_policy( + policy_name="Allow Telemetry", + policy_class="machine", + return_value_only=False, + return_full_policy_names=True, + hierarchical_return=True, + ) expected = { - 'Computer Configuration': { - 'Administrative Templates': { - 'Windows Components': { - 'Data Collection and Preview Builds': { - 'Allow Telemetry': 'Not Configured'}}}}} + "Computer Configuration": { + "Administrative Templates": { + "Windows Components": { + "Data Collection and Preview Builds": { + "Allow Telemetry": "Not Configured" + } + } + } + } + } self.assertDictEqual(result, expected) @destructiveTest def test__load_policy_definitions(self): - ''' + """ Test that unexpected files in the PolicyDefinitions directory won't cause the _load_policy_definitions function to explode https://gitlab.com/saltstack/enterprise/lock/issues/3826 - ''' + """ # The PolicyDefinitions directory should only contain ADMX files. We # want to make sure the `_load_policy_definitions` function skips non # ADMX files in this directory. # Create a bogus ADML file in PolicyDefinitions directory - bogus_fle = os.path.join( - 'c:\\Windows\\PolicyDefinitions', - '_bogus.adml') - cache_dir = os.path.join( - win_lgpo.__opts__['cachedir'], - 'lgpo', - 'policy_defs') + bogus_fle = os.path.join("c:\\Windows\\PolicyDefinitions", "_bogus.adml") + cache_dir = os.path.join(win_lgpo.__opts__["cachedir"], "lgpo", "policy_defs") try: - with salt.utils.files.fopen(bogus_fle, 'w+') as fh: - fh.write('<junk></junk>') + with salt.utils.files.fopen(bogus_fle, "w+") as fh: + fh.write("<junk></junk>") # This function doesn't return anything (None), it just loads # the XPath structures into __context__. We're just making sure it # doesn't stack trace here @@ -363,133 +401,150 @@ class WinLGPOGetPolicyADMXTestCase(TestCase, LoaderModuleMockMixin): # Remove source file os.remove(bogus_fle) # Remove cached file - search_string = '{0}\\_bogus*.adml'.format(cache_dir) + search_string = "{0}\\_bogus*.adml".format(cache_dir) for file_name in glob.glob(search_string): os.remove(file_name) -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLGPOGetPolicyFromPolicyInfoTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test functions related to the ``get_policy`` function using _policy_info object - ''' + """ + def setup_loader_modules(self): return LOADER_DICTS def test_get_policy_name(self): result = win_lgpo.get_policy( - policy_name='Network firewall: Public: Settings: Display a ' - 'notification', - policy_class='machine', + policy_name="Network firewall: Public: Settings: Display a " "notification", + policy_class="machine", return_value_only=True, return_full_policy_names=True, - hierarchical_return=False) - expected = 'Not configured' + hierarchical_return=False, + ) + expected = "Not configured" self.assertEqual(result, expected) def test_get_policy_id(self): result = win_lgpo.get_policy( - policy_name='WfwPublicSettingsNotification', - policy_class='machine', + policy_name="WfwPublicSettingsNotification", + policy_class="machine", return_value_only=True, return_full_policy_names=True, - hierarchical_return=False) - expected = 'Not configured' + hierarchical_return=False, + ) + expected = "Not configured" self.assertEqual(result, expected) def test_get_policy_name_full_return(self): result = win_lgpo.get_policy( - policy_name='Network firewall: Public: Settings: Display a ' - 'notification', - policy_class='machine', + policy_name="Network firewall: Public: Settings: Display a " "notification", + policy_class="machine", return_value_only=False, return_full_policy_names=True, - hierarchical_return=False) + hierarchical_return=False, + ) expected = { - 'Network firewall: Public: Settings: Display a notification': - 'Not configured'} + "Network firewall: Public: Settings: Display a notification": "Not configured" + } self.assertDictEqual(result, expected) def test_get_policy_id_full_return(self): result = win_lgpo.get_policy( - policy_name='WfwPublicSettingsNotification', - policy_class='machine', + policy_name="WfwPublicSettingsNotification", + policy_class="machine", return_value_only=False, return_full_policy_names=True, - hierarchical_return=False) + hierarchical_return=False, + ) expected = { - 'Network firewall: Public: Settings: Display a notification': - 'Not configured'} + "Network firewall: Public: Settings: Display a notification": "Not configured" + } self.assertDictEqual(result, expected) def test_get_policy_name_full_return_ids(self): result = win_lgpo.get_policy( - policy_name='Network firewall: Public: Settings: Display a ' - 'notification', - policy_class='machine', + policy_name="Network firewall: Public: Settings: Display a " "notification", + policy_class="machine", return_value_only=False, return_full_policy_names=False, - hierarchical_return=False) + hierarchical_return=False, + ) expected = { - 'Network firewall: Public: Settings: Display a notification': - 'Not configured'} + "Network firewall: Public: Settings: Display a notification": "Not configured" + } self.assertDictEqual(result, expected) def test_get_policy_id_full_return_ids(self): result = win_lgpo.get_policy( - policy_name='WfwPublicSettingsNotification', - policy_class='machine', + policy_name="WfwPublicSettingsNotification", + policy_class="machine", return_value_only=False, return_full_policy_names=False, - hierarchical_return=False) - expected = {'WfwPublicSettingsNotification': 'Not configured'} + hierarchical_return=False, + ) + expected = {"WfwPublicSettingsNotification": "Not configured"} self.assertDictEqual(result, expected) def test_get_policy_id_full_return_ids_hierarchical(self): result = win_lgpo.get_policy( - policy_name='WfwPublicSettingsNotification', - policy_class='machine', + policy_name="WfwPublicSettingsNotification", + policy_class="machine", return_value_only=False, return_full_policy_names=False, - hierarchical_return=True) + hierarchical_return=True, + ) expected = { - 'Computer Configuration': { - 'Windows Settings': { - 'Security Settings': { - 'Windows Firewall with Advanced Security': { - 'Windows Firewall with Advanced Security - Local ' - 'Group Policy Object': { - 'WfwPublicSettingsNotification': - 'Not configured'}}}}}} + "Computer Configuration": { + "Windows Settings": { + "Security Settings": { + "Windows Firewall with Advanced Security": { + "Windows Firewall with Advanced Security - Local " + "Group Policy Object": { + "WfwPublicSettingsNotification": "Not configured" + } + } + } + } + } + } self.assertDictEqual(result, expected) def test_get_policy_id_full_return_full_names_hierarchical(self): result = win_lgpo.get_policy( - policy_name='WfwPublicSettingsNotification', - policy_class='machine', + policy_name="WfwPublicSettingsNotification", + policy_class="machine", return_value_only=False, return_full_policy_names=True, - hierarchical_return=True) + hierarchical_return=True, + ) expected = { - 'Computer Configuration': { - 'Windows Settings': { - 'Security Settings': { - 'Windows Firewall with Advanced Security': { - 'Windows Firewall with Advanced Security - Local ' - 'Group Policy Object': { - 'Network firewall: Public: Settings: Display a ' - 'notification': - 'Not configured'}}}}}} + "Computer Configuration": { + "Windows Settings": { + "Security Settings": { + "Windows Firewall with Advanced Security": { + "Windows Firewall with Advanced Security - Local " + "Group Policy Object": { + "Network firewall: Public: Settings: Display a " + "notification": "Not configured" + } + } + } + } + } + } self.assertDictEqual(result, expected) -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLGPOPolicyInfoMechanismsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test getting local group policy settings defined in the _policy_info object Go through each mechanism - ''' + """ + def setup_loader_modules(self): return LOADER_DICTS @@ -498,97 +553,107 @@ class WinLGPOPolicyInfoMechanismsTestCase(TestCase, LoaderModuleMockMixin): cls.policy_data = salt.modules.win_lgpo._policy_info() def _test_policy(self, policy_name): - ''' + """ Helper function to get current setting - ''' - policy_definition = self.policy_data.policies['Machine']['policies'][policy_name] + """ + policy_definition = self.policy_data.policies["Machine"]["policies"][ + policy_name + ] return salt.modules.win_lgpo._get_policy_info_setting(policy_definition) def test_registry_mechanism(self): - ''' + """ Test getting policy value using the Registry mechanism - ''' - policy_name = 'RemoteRegistryExactPaths' + """ + policy_name = "RemoteRegistryExactPaths" result = self._test_policy(policy_name=policy_name) expected = [ - 'System\\CurrentControlSet\\Control\\ProductOptions', - 'System\\CurrentControlSet\\Control\\Server Applications', - 'Software\\Microsoft\\Windows NT\\CurrentVersion' + "System\\CurrentControlSet\\Control\\ProductOptions", + "System\\CurrentControlSet\\Control\\Server Applications", + "Software\\Microsoft\\Windows NT\\CurrentVersion", ] self.assertListEqual(result, expected) def test_secedit_mechanism(self): - ''' + """ Test getting policy value using the Secedit mechanism - ''' - policy_name = 'LSAAnonymousNameLookup' + """ + policy_name = "LSAAnonymousNameLookup" result = self._test_policy(policy_name=policy_name) - expected = 'Disabled' + expected = "Disabled" self.assertEqual(result, expected) def test_netsh_mechanism(self): - ''' + """ Test getting the policy value using the NetSH mechanism - ''' - policy_name = 'WfwDomainState' + """ + policy_name = "WfwDomainState" result = self._test_policy(policy_name=policy_name) - expected = 'Not configured' + expected = "Not configured" self.assertEqual(result, expected) @destructiveTest def test_adv_audit_mechanism(self): - ''' + """ Test getting the policy value using the AdvAudit mechanism - ''' - system_root = os.environ.get('SystemRoot', 'C:\\Windows') - f_audit = os.path.join(system_root, 'security', 'audit', 'audit.csv') - f_audit_gpo = os.path.join(system_root, 'System32', 'GroupPolicy', - 'Machine', 'Microsoft', 'Windows NT', - 'Audit', 'audit.csv') + """ + system_root = os.environ.get("SystemRoot", "C:\\Windows") + f_audit = os.path.join(system_root, "security", "audit", "audit.csv") + f_audit_gpo = os.path.join( + system_root, + "System32", + "GroupPolicy", + "Machine", + "Microsoft", + "Windows NT", + "Audit", + "audit.csv", + ) if os.path.exists(f_audit): os.remove(f_audit) if os.path.exists(f_audit_gpo): os.remove(f_audit_gpo) - policy_name = 'AuditCredentialValidation' + policy_name = "AuditCredentialValidation" result = self._test_policy(policy_name=policy_name) - expected = 'Not Configured' + expected = "Not Configured" self.assertEqual(result, expected) def test_net_user_modal_mechanism(self): - ''' + """ Test getting the policy value using the NetUserModal mechanism - ''' - policy_name = 'PasswordHistory' + """ + policy_name = "PasswordHistory" result = self._test_policy(policy_name=policy_name) expected = 0 self.assertEqual(result, expected) def test_lsa_rights_mechanism(self): - ''' + """ Test getting the policy value using the LsaRights mechanism - ''' - policy_name = 'SeTrustedCredManAccessPrivilege' + """ + policy_name = "SeTrustedCredManAccessPrivilege" result = self._test_policy(policy_name=policy_name) expected = [] self.assertEqual(result, expected) def test_script_ini_mechanism(self): - ''' + """ Test getting the policy value using the ScriptIni value - ''' - policy_name = 'StartupScripts' + """ + policy_name = "StartupScripts" result = self._test_policy(policy_name=policy_name) expected = None self.assertEqual(result, expected) @destructiveTest -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLGPOGetPointAndPrintNCTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test variations of the Point and Print Restrictions policy when Not Configured (NC) - ''' + """ + not_configured = False def setup_loader_modules(self): @@ -596,91 +661,90 @@ class WinLGPOGetPointAndPrintNCTestCase(TestCase, LoaderModuleMockMixin): def setUp(self): if not self.not_configured: - computer_policy = {'Point and Print Restrictions': 'Not Configured'} + computer_policy = {"Point and Print Restrictions": "Not Configured"} win_lgpo.set_(computer_policy=computer_policy) self.not_configured = True - def _get_policy_adm_setting(self, policy_name, policy_class, - return_full_policy_names, hierarchical_return): - ''' + def _get_policy_adm_setting( + self, policy_name, policy_class, return_full_policy_names, hierarchical_return + ): + """ Helper function to get current setting - ''' + """ # Get the policy success, policy_obj, _, _ = salt.modules.win_lgpo._lookup_admin_template( - policy_name=policy_name, - policy_class=policy_class, - adml_language='en-US') + policy_name=policy_name, policy_class=policy_class, adml_language="en-US" + ) if success: return salt.modules.win_lgpo._get_policy_adm_setting( admx_policy=policy_obj, policy_class=policy_class, - adml_language='en-US', + adml_language="en-US", return_full_policy_names=return_full_policy_names, - hierarchical_return=hierarchical_return + hierarchical_return=hierarchical_return, ) - return 'Policy Not Found' + return "Policy Not Found" def test_point_and_print_not_configured(self): result = self._get_policy_adm_setting( - policy_name='Point and Print Restrictions', - policy_class='Machine', + policy_name="Point and Print Restrictions", + policy_class="Machine", return_full_policy_names=False, - hierarchical_return=False + hierarchical_return=False, ) - expected = { - 'PointAndPrint_Restrictions_Win7': 'Not Configured' - } + expected = {"PointAndPrint_Restrictions_Win7": "Not Configured"} self.assertDictEqual(result, expected) def test_point_and_print_not_configured_hierarchical(self): result = self._get_policy_adm_setting( - policy_name='Point and Print Restrictions', - policy_class='Machine', + policy_name="Point and Print Restrictions", + policy_class="Machine", return_full_policy_names=False, - hierarchical_return=True + hierarchical_return=True, ) expected = { - 'Computer Configuration': { - 'Administrative Templates': { - 'Printers': { - 'PointAndPrint_Restrictions_Win7': - 'Not Configured'}}}} + "Computer Configuration": { + "Administrative Templates": { + "Printers": {"PointAndPrint_Restrictions_Win7": "Not Configured"} + } + } + } self.assertDictEqual(result, expected) def test_point_and_print_not_configured_full_names(self): result = self._get_policy_adm_setting( - policy_name='Point and Print Restrictions', - policy_class='Machine', + policy_name="Point and Print Restrictions", + policy_class="Machine", return_full_policy_names=True, - hierarchical_return=False + hierarchical_return=False, ) - expected = { - 'Printers\\Point and Print Restrictions': 'Not Configured' - } + expected = {"Printers\\Point and Print Restrictions": "Not Configured"} self.assertDictEqual(result, expected) def test_point_and_print_not_configured_full_names_hierarchical(self): result = self._get_policy_adm_setting( - policy_name='Point and Print Restrictions', - policy_class='Machine', + policy_name="Point and Print Restrictions", + policy_class="Machine", return_full_policy_names=True, - hierarchical_return=True + hierarchical_return=True, ) expected = { - 'Computer Configuration': { - 'Administrative Templates': { - 'Printers': { - 'Point and Print Restrictions': - 'Not Configured'}}}} + "Computer Configuration": { + "Administrative Templates": { + "Printers": {"Point and Print Restrictions": "Not Configured"} + } + } + } self.assertDictEqual(result, expected) @destructiveTest -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLGPOGetPointAndPrintENTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test variations of the Point and Print Restrictions policy when Enabled (EN) - ''' + """ + configured = False def setup_loader_modules(self): @@ -689,144 +753,135 @@ class WinLGPOGetPointAndPrintENTestCase(TestCase, LoaderModuleMockMixin): def setUp(self): if not self.configured: computer_policy = { - 'Point and Print Restrictions': { - 'Users can only point and print to these servers': - True, - 'Enter fully qualified server names separated by ' - 'semicolons': - 'fakeserver1;fakeserver2', - 'Users can only point and print to machines in their ' - 'forest': - True, - 'When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'When updating drivers for an existing connection': - 'Show warning only', + "Point and Print Restrictions": { + "Users can only point and print to these servers": True, + "Enter fully qualified server names separated by " + "semicolons": "fakeserver1;fakeserver2", + "Users can only point and print to machines in their " + "forest": True, + "When installing drivers for a new connection": "Show warning and elevation prompt", + "When updating drivers for an existing connection": "Show warning only", }, } win_lgpo.set_(computer_policy=computer_policy) self.configured = True - def _get_policy_adm_setting(self, policy_name, policy_class, - return_full_policy_names, hierarchical_return): - ''' + def _get_policy_adm_setting( + self, policy_name, policy_class, return_full_policy_names, hierarchical_return + ): + """ Helper function to get current setting - ''' + """ # Get the policy success, policy_obj, _, _ = salt.modules.win_lgpo._lookup_admin_template( - policy_name=policy_name, - policy_class=policy_class, - adml_language='en-US') + policy_name=policy_name, policy_class=policy_class, adml_language="en-US" + ) if success: results = salt.modules.win_lgpo._get_policy_adm_setting( admx_policy=policy_obj, policy_class=policy_class, - adml_language='en-US', + adml_language="en-US", return_full_policy_names=return_full_policy_names, - hierarchical_return=hierarchical_return + hierarchical_return=hierarchical_return, ) if six.PY2: results = salt.states.win_lgpo._convert_to_unicode(results) return results - return 'Policy Not Found' + return "Policy Not Found" def test_point_and_print_enabled(self): result = self._get_policy_adm_setting( - policy_name='Point and Print Restrictions', - policy_class='Machine', + policy_name="Point and Print Restrictions", + policy_class="Machine", return_full_policy_names=False, - hierarchical_return=False + hierarchical_return=False, ) expected = { - 'PointAndPrint_Restrictions_Win7': { - 'PointAndPrint_NoWarningNoElevationOnInstall_Enum': - 'Show warning and elevation prompt', - 'PointAndPrint_NoWarningNoElevationOnUpdate_Enum': - 'Show warning only', - 'PointAndPrint_TrustedForest_Chk': - True, - 'PointAndPrint_TrustedServers_Chk': - True, - 'PointAndPrint_TrustedServers_Edit': - 'fakeserver1;fakeserver2'}} + "PointAndPrint_Restrictions_Win7": { + "PointAndPrint_NoWarningNoElevationOnInstall_Enum": "Show warning and elevation prompt", + "PointAndPrint_NoWarningNoElevationOnUpdate_Enum": "Show warning only", + "PointAndPrint_TrustedForest_Chk": True, + "PointAndPrint_TrustedServers_Chk": True, + "PointAndPrint_TrustedServers_Edit": "fakeserver1;fakeserver2", + } + } self.assertDictEqual(result, expected) def test_point_and_print_enabled_hierarchical(self): result = self._get_policy_adm_setting( - policy_name='Point and Print Restrictions', - policy_class='Machine', + policy_name="Point and Print Restrictions", + policy_class="Machine", return_full_policy_names=False, - hierarchical_return=True + hierarchical_return=True, ) expected = { - 'Computer Configuration': { - 'Administrative Templates': { - 'Printers': { - 'PointAndPrint_Restrictions_Win7': { - 'PointAndPrint_NoWarningNoElevationOnInstall_Enum': - 'Show warning and elevation prompt', - 'PointAndPrint_NoWarningNoElevationOnUpdate_Enum': - 'Show warning only', - 'PointAndPrint_TrustedForest_Chk': - True, - 'PointAndPrint_TrustedServers_Chk': - True, - 'PointAndPrint_TrustedServers_Edit': - 'fakeserver1;fakeserver2'}}}}} + "Computer Configuration": { + "Administrative Templates": { + "Printers": { + "PointAndPrint_Restrictions_Win7": { + "PointAndPrint_NoWarningNoElevationOnInstall_Enum": "Show warning and elevation prompt", + "PointAndPrint_NoWarningNoElevationOnUpdate_Enum": "Show warning only", + "PointAndPrint_TrustedForest_Chk": True, + "PointAndPrint_TrustedServers_Chk": True, + "PointAndPrint_TrustedServers_Edit": "fakeserver1;fakeserver2", + } + } + } + } + } self.assertDictEqual(result, expected) def test_point_and_print_enabled_full_names(self): result = self._get_policy_adm_setting( - policy_name='Point and Print Restrictions', - policy_class='Machine', + policy_name="Point and Print Restrictions", + policy_class="Machine", return_full_policy_names=True, - hierarchical_return=False + hierarchical_return=False, ) expected = { - 'Printers\\Point and Print Restrictions': { - 'Enter fully qualified server names separated by semicolons': - 'fakeserver1;fakeserver2', - 'When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'Users can only point and print to machines in their forest': - True, - 'Users can only point and print to these servers': True, - 'When updating drivers for an existing connection': - 'Show warning only'}} + "Printers\\Point and Print Restrictions": { + "Enter fully qualified server names separated by semicolons": "fakeserver1;fakeserver2", + "When installing drivers for a new connection": "Show warning and elevation prompt", + "Users can only point and print to machines in their forest": True, + "Users can only point and print to these servers": True, + "When updating drivers for an existing connection": "Show warning only", + } + } self.assertDictEqual(result, expected) def test_point_and_print_enabled_full_names_hierarchical(self): result = self._get_policy_adm_setting( - policy_name='Point and Print Restrictions', - policy_class='Machine', + policy_name="Point and Print Restrictions", + policy_class="Machine", return_full_policy_names=True, - hierarchical_return=True + hierarchical_return=True, ) expected = { - 'Computer Configuration': { - 'Administrative Templates': { - 'Printers': { - 'Point and Print Restrictions': { - 'Enter fully qualified server names separated by ' - 'semicolons': - 'fakeserver1;fakeserver2', - 'When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'Users can only point and print to machines in ' - 'their forest': - True, - 'Users can only point and print to these servers': - True, - 'When updating drivers for an existing connection': - 'Show warning only'}}}}} + "Computer Configuration": { + "Administrative Templates": { + "Printers": { + "Point and Print Restrictions": { + "Enter fully qualified server names separated by " + "semicolons": "fakeserver1;fakeserver2", + "When installing drivers for a new connection": "Show warning and elevation prompt", + "Users can only point and print to machines in " + "their forest": True, + "Users can only point and print to these servers": True, + "When updating drivers for an existing connection": "Show warning only", + } + } + } + } + } self.assertDictEqual(result, expected) -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLGPOGetPolicyFromPolicyResources(TestCase, LoaderModuleMockMixin): - ''' + """ Test functions related to policy info gathered from ADMX/ADML files - ''' + """ + adml_data = None def setup_loader_modules(self): @@ -834,17 +889,18 @@ class WinLGPOGetPolicyFromPolicyResources(TestCase, LoaderModuleMockMixin): def setUp(self): if self.adml_data is None: - self.adml_data = win_lgpo._get_policy_resources('en-US') + self.adml_data = win_lgpo._get_policy_resources("en-US") def test__getAdmlPresentationRefId(self): - ref_id = 'LetAppsAccessAccountInfo_Enum' - expected = 'Default for all apps' + ref_id = "LetAppsAccessAccountInfo_Enum" + expected = "Default for all apps" result = win_lgpo._getAdmlPresentationRefId(self.adml_data, ref_id) self.assertEqual(result, expected) def test__getAdmlPresentationRefId_result_text_is_none(self): - ref_id = 'LetAppsAccessAccountInfo_UserInControlOfTheseApps_List' - expected = 'Put user in control of these specific apps (use Package ' \ - 'Family Names)' + ref_id = "LetAppsAccessAccountInfo_UserInControlOfTheseApps_List" + expected = ( + "Put user in control of these specific apps (use Package " "Family Names)" + ) result = win_lgpo._getAdmlPresentationRefId(self.adml_data, ref_id) self.assertEqual(result, expected) diff --git a/tests/unit/modules/test_win_license.py b/tests/unit/modules/test_win_license.py index 20d1698b552..e740d9379de 100644 --- a/tests/unit/modules/test_win_license.py +++ b/tests/unit/modules/test_win_license.py @@ -1,96 +1,96 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_license as win_license # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class LicenseTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {win_license: {}} def test_installed(self): - ''' + """ Test to see if the given license key is installed - ''' - mock = MagicMock(return_value='Partial Product Key: ABCDE') - with patch.dict(win_license.__salt__, {'cmd.run': mock}): - out = win_license.installed('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') - mock.assert_called_once_with(r'cscript C:\Windows\System32\slmgr.vbs /dli') + """ + mock = MagicMock(return_value="Partial Product Key: ABCDE") + with patch.dict(win_license.__salt__, {"cmd.run": mock}): + out = win_license.installed("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") + mock.assert_called_once_with(r"cscript C:\Windows\System32\slmgr.vbs /dli") self.assertTrue(out) def test_installed_diff(self): - ''' + """ Test to see if the given license key is installed when the key is different - ''' - mock = MagicMock(return_value='Partial Product Key: 12345') - with patch.dict(win_license.__salt__, {'cmd.run': mock}): - out = win_license.installed('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') - mock.assert_called_once_with(r'cscript C:\Windows\System32\slmgr.vbs /dli') + """ + mock = MagicMock(return_value="Partial Product Key: 12345") + with patch.dict(win_license.__salt__, {"cmd.run": mock}): + out = win_license.installed("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") + mock.assert_called_once_with(r"cscript C:\Windows\System32\slmgr.vbs /dli") self.assertFalse(out) def test_install(self): - ''' + """ Test installing the given product key - ''' + """ mock = MagicMock() - with patch.dict(win_license.__salt__, {'cmd.run': mock}): - win_license.install('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') - mock.assert_called_once_with(r'cscript C:\Windows\System32\slmgr.vbs /ipk ' - 'AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') + with patch.dict(win_license.__salt__, {"cmd.run": mock}): + win_license.install("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") + mock.assert_called_once_with( + r"cscript C:\Windows\System32\slmgr.vbs /ipk " + "AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE" + ) def test_uninstall(self): - ''' + """ Test uninstalling the given product key - ''' + """ mock = MagicMock() - with patch.dict(win_license.__salt__, {'cmd.run': mock}): + with patch.dict(win_license.__salt__, {"cmd.run": mock}): win_license.uninstall() - mock.assert_called_once_with(r'cscript C:\Windows\System32\slmgr.vbs /upk') + mock.assert_called_once_with(r"cscript C:\Windows\System32\slmgr.vbs /upk") def test_activate(self): - ''' + """ Test activating the current product key - ''' + """ mock = MagicMock() - with patch.dict(win_license.__salt__, {'cmd.run': mock}): + with patch.dict(win_license.__salt__, {"cmd.run": mock}): win_license.activate() - mock.assert_called_once_with(r'cscript C:\Windows\System32\slmgr.vbs /ato') + mock.assert_called_once_with(r"cscript C:\Windows\System32\slmgr.vbs /ato") def test_licensed(self): - ''' + """ Test checking if the minion is licensed - ''' - mock = MagicMock(return_value='License Status: Licensed') - with patch.dict(win_license.__salt__, {'cmd.run': mock}): + """ + mock = MagicMock(return_value="License Status: Licensed") + with patch.dict(win_license.__salt__, {"cmd.run": mock}): win_license.licensed() - mock.assert_called_once_with(r'cscript C:\Windows\System32\slmgr.vbs /dli') + mock.assert_called_once_with(r"cscript C:\Windows\System32\slmgr.vbs /dli") def test_info(self): - ''' + """ Test getting the info about the current license key - ''' + """ expected = { - 'description': 'Prof', - 'licensed': True, - 'name': 'Win7', - 'partial_key': '12345' + "description": "Prof", + "licensed": True, + "name": "Win7", + "partial_key": "12345", } - mock = MagicMock(return_value='Name: Win7\r\nDescription: Prof\r\nPartial Product Key: 12345\r\n' - 'License Status: Licensed') - with patch.dict(win_license.__salt__, {'cmd.run': mock}): + mock = MagicMock( + return_value="Name: Win7\r\nDescription: Prof\r\nPartial Product Key: 12345\r\n" + "License Status: Licensed" + ) + with patch.dict(win_license.__salt__, {"cmd.run": mock}): out = win_license.info() - mock.assert_called_once_with(r'cscript C:\Windows\System32\slmgr.vbs /dli') + mock.assert_called_once_with(r"cscript C:\Windows\System32\slmgr.vbs /dli") self.assertEqual(out, expected) diff --git a/tests/unit/modules/test_win_network.py b/tests/unit/modules/test_win_network.py index a527c0ddf62..7d3071480a7 100644 --- a/tests/unit/modules/test_win_network.py +++ b/tests/unit/modules/test_win_network.py @@ -1,59 +1,61 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, - Mock, -) +import salt.modules.win_network as win_network # Import Salt Libs import salt.utils.network -import salt.modules.win_network as win_network + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase, skipIf try: import wmi + HAS_WMI = True except ImportError: HAS_WMI = False class Mockwmi(object): - ''' + """ Mock wmi class - ''' - NetConnectionID = 'Ethernet' + """ + + NetConnectionID = "Ethernet" def __init__(self): pass class Mockwinapi(object): - ''' + """ Mock winapi class - ''' + """ + def __init__(self): pass class winapi(object): - ''' + """ Mock winapi class - ''' + """ + def __init__(self): pass class Com(object): - ''' + """ Mock Com method - ''' + """ + def __enter__(self): return self @@ -62,202 +64,251 @@ class Mockwinapi(object): class WinNetworkTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_network - ''' + """ + def setup_loader_modules(self): self.WMI = Mock() - self.addCleanup(delattr, self, 'WMI') + self.addCleanup(delattr, self, "WMI") return {win_network: {}} # 'ping' function tests: 1 def test_ping(self): - ''' + """ Test if it performs a ping to a host. - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(win_network.__salt__, {'cmd.run': mock}): - self.assertTrue(win_network.ping('127.0.0.1')) + with patch.dict(win_network.__salt__, {"cmd.run": mock}): + self.assertTrue(win_network.ping("127.0.0.1")) # 'netstat' function tests: 1 def test_netstat(self): - ''' + """ Test if it return information on open ports and states - ''' - ret = (' Proto Local Address Foreign Address State PID\n' - ' TCP 127.0.0.1:1434 0.0.0.0:0 LISTENING 1728\n' - ' UDP 127.0.0.1:1900 *:* 4240') + """ + ret = ( + " Proto Local Address Foreign Address State PID\n" + " TCP 127.0.0.1:1434 0.0.0.0:0 LISTENING 1728\n" + " UDP 127.0.0.1:1900 *:* 4240" + ) mock = MagicMock(return_value=ret) - with patch.dict(win_network.__salt__, {'cmd.run': mock}): - self.assertListEqual(win_network.netstat(), - [{'local-address': '127.0.0.1:1434', - 'program': '1728', 'proto': 'TCP', - 'remote-address': '0.0.0.0:0', - 'state': 'LISTENING'}, - {'local-address': '127.0.0.1:1900', - 'program': '4240', 'proto': 'UDP', - 'remote-address': '*:*', 'state': None}]) + with patch.dict(win_network.__salt__, {"cmd.run": mock}): + self.assertListEqual( + win_network.netstat(), + [ + { + "local-address": "127.0.0.1:1434", + "program": "1728", + "proto": "TCP", + "remote-address": "0.0.0.0:0", + "state": "LISTENING", + }, + { + "local-address": "127.0.0.1:1900", + "program": "4240", + "proto": "UDP", + "remote-address": "*:*", + "state": None, + }, + ], + ) # 'traceroute' function tests: 1 def test_traceroute(self): - ''' + """ Test if it performs a traceroute to a 3rd party host - ''' - ret = (' 1 1 ms <1 ms <1 ms 172.27.104.1\n' - ' 2 1 ms <1 ms 1 ms 121.242.35.1.s[121.242.35.1]\n' - ' 3 3 ms 2 ms 2 ms 121.242.4.53.s[121.242.4.53]\n') + """ + ret = ( + " 1 1 ms <1 ms <1 ms 172.27.104.1\n" + " 2 1 ms <1 ms 1 ms 121.242.35.1.s[121.242.35.1]\n" + " 3 3 ms 2 ms 2 ms 121.242.4.53.s[121.242.4.53]\n" + ) mock = MagicMock(return_value=ret) - with patch.dict(win_network.__salt__, {'cmd.run': mock}): - self.assertListEqual(win_network.traceroute('google.com'), - [{'count': '1', 'hostname': None, - 'ip': '172.27.104.1', 'ms1': '1', - 'ms2': '<1', 'ms3': '<1'}, - {'count': '2', 'hostname': None, - 'ip': '121.242.35.1.s[121.242.35.1]', - 'ms1': '1', 'ms2': '<1', 'ms3': '1'}, - {'count': '3', 'hostname': None, - 'ip': '121.242.4.53.s[121.242.4.53]', - 'ms1': '3', 'ms2': '2', 'ms3': '2'}]) + with patch.dict(win_network.__salt__, {"cmd.run": mock}): + self.assertListEqual( + win_network.traceroute("google.com"), + [ + { + "count": "1", + "hostname": None, + "ip": "172.27.104.1", + "ms1": "1", + "ms2": "<1", + "ms3": "<1", + }, + { + "count": "2", + "hostname": None, + "ip": "121.242.35.1.s[121.242.35.1]", + "ms1": "1", + "ms2": "<1", + "ms3": "1", + }, + { + "count": "3", + "hostname": None, + "ip": "121.242.4.53.s[121.242.4.53]", + "ms1": "3", + "ms2": "2", + "ms3": "2", + }, + ], + ) # 'nslookup' function tests: 1 def test_nslookup(self): - ''' + """ Test if it query DNS for information about a domain or ip address - ''' - ret = ('Server: ct-dc-3-2.cybage.com\n' - 'Address: 172.27.172.12\n' - 'Non-authoritative answer:\n' - 'Name: google.com\n' - 'Addresses: 2404:6800:4007:806::200e\n' - '216.58.196.110\n') + """ + ret = ( + "Server: ct-dc-3-2.cybage.com\n" + "Address: 172.27.172.12\n" + "Non-authoritative answer:\n" + "Name: google.com\n" + "Addresses: 2404:6800:4007:806::200e\n" + "216.58.196.110\n" + ) mock = MagicMock(return_value=ret) - with patch.dict(win_network.__salt__, {'cmd.run': mock}): - self.assertListEqual(win_network.nslookup('google.com'), - [{'Server': 'ct-dc-3-2.cybage.com'}, - {'Address': '172.27.172.12'}, - {'Name': 'google.com'}, - {'Addresses': ['2404:6800:4007:806::200e', - '216.58.196.110']}]) + with patch.dict(win_network.__salt__, {"cmd.run": mock}): + self.assertListEqual( + win_network.nslookup("google.com"), + [ + {"Server": "ct-dc-3-2.cybage.com"}, + {"Address": "172.27.172.12"}, + {"Name": "google.com"}, + {"Addresses": ["2404:6800:4007:806::200e", "216.58.196.110"]}, + ], + ) # 'dig' function tests: 1 def test_dig(self): - ''' + """ Test if it performs a DNS lookup with dig - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(win_network.__salt__, {'cmd.run': mock}): - self.assertTrue(win_network.dig('google.com')) + with patch.dict(win_network.__salt__, {"cmd.run": mock}): + self.assertTrue(win_network.dig("google.com")) # 'interfaces_names' function tests: 1 @skipIf(not HAS_WMI, "WMI only available on Windows") def test_interfaces_names(self): - ''' + """ Test if it return a list of all the interfaces names - ''' + """ self.WMI.Win32_NetworkAdapter = MagicMock(return_value=Mockwmi) - with patch('salt.utils.winapi.Com', MagicMock()), \ - patch.object(self.WMI, 'Win32_NetworkAdapter', - return_value=[Mockwmi()]), \ - patch('salt.utils', Mockwinapi), \ - patch.object(wmi, 'WMI', Mock(return_value=self.WMI)): - self.assertListEqual(win_network.interfaces_names(), - ['Ethernet']) + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + self.WMI, "Win32_NetworkAdapter", return_value=[Mockwmi()] + ), patch("salt.utils", Mockwinapi), patch.object( + wmi, "WMI", Mock(return_value=self.WMI) + ): + self.assertListEqual(win_network.interfaces_names(), ["Ethernet"]) # 'interfaces' function tests: 1 def test_interfaces(self): - ''' + """ Test if it return information about all the interfaces on the minion - ''' - with patch.object(salt.utils.network, 'win_interfaces', - MagicMock(return_value=True)): + """ + with patch.object( + salt.utils.network, "win_interfaces", MagicMock(return_value=True) + ): self.assertTrue(win_network.interfaces()) # 'hw_addr' function tests: 1 def test_hw_addr(self): - ''' + """ Test if it return the hardware address (a.k.a. MAC address) for a given interface - ''' - with patch.object(salt.utils.network, 'hw_addr', - MagicMock(return_value='Ethernet')): - self.assertEqual(win_network.hw_addr('Ethernet'), 'Ethernet') + """ + with patch.object( + salt.utils.network, "hw_addr", MagicMock(return_value="Ethernet") + ): + self.assertEqual(win_network.hw_addr("Ethernet"), "Ethernet") # 'subnets' function tests: 1 def test_subnets(self): - ''' + """ Test if it returns a list of subnets to which the host belongs - ''' - with patch.object(salt.utils.network, 'subnets', - MagicMock(return_value='10.1.1.0/24')): - self.assertEqual(win_network.subnets(), '10.1.1.0/24') + """ + with patch.object( + salt.utils.network, "subnets", MagicMock(return_value="10.1.1.0/24") + ): + self.assertEqual(win_network.subnets(), "10.1.1.0/24") # 'in_subnet' function tests: 1 def test_in_subnet(self): - ''' + """ Test if it returns True if host is within specified subnet, otherwise False - ''' - with patch.object(salt.utils.network, 'in_subnet', - MagicMock(return_value=True)): - self.assertTrue(win_network.in_subnet('10.1.1.0/16')) + """ + with patch.object( + salt.utils.network, "in_subnet", MagicMock(return_value=True) + ): + self.assertTrue(win_network.in_subnet("10.1.1.0/16")) # 'get_route' function tests: 1 def test_get_route(self): - ''' + """ Test if it return information on open ports and states - ''' - ret = ('\n\n' - 'IPAddress : 10.0.0.15\n' - 'InterfaceIndex : 3\n' - 'InterfaceAlias : Wi-Fi\n' - 'AddressFamily : IPv4\n' - 'Type : Unicast\n' - 'PrefixLength : 24\n' - 'PrefixOrigin : Dhcp\n' - 'SuffixOrigin : Dhcp\n' - 'AddressState : Preferred\n' - 'ValidLifetime : 6.17:52:39\n' - 'PreferredLifetime : 6.17:52:39\n' - 'SkipAsSource : False\n' - 'PolicyStore : ActiveStore\n' - '\n\n' - 'Caption :\n' - 'Description :\n' - 'ElementName :\n' - 'InstanceID : :8:8:8:9:55=55;:8;8;:8;55;\n' - 'AdminDistance :\n' - 'DestinationAddress :\n' - 'IsStatic :\n' - 'RouteMetric : 0\n' - 'TypeOfRoute : 3\n' - 'AddressFamily : IPv4\n' - 'CompartmentId : 1\n' - 'DestinationPrefix : 0.0.0.0/0\n' - 'InterfaceAlias : Wi-Fi\n' - 'InterfaceIndex : 3\n' - 'NextHop : 10.0.0.1\n' - 'PreferredLifetime : 6.23:14:43\n' - 'Protocol : NetMgmt\n' - 'Publish : No\n' - 'Store : ActiveStore\n' - 'ValidLifetime : 6.23:14:43\n' - 'PSComputerName :\n' - 'ifIndex : 3') + """ + ret = ( + "\n\n" + "IPAddress : 10.0.0.15\n" + "InterfaceIndex : 3\n" + "InterfaceAlias : Wi-Fi\n" + "AddressFamily : IPv4\n" + "Type : Unicast\n" + "PrefixLength : 24\n" + "PrefixOrigin : Dhcp\n" + "SuffixOrigin : Dhcp\n" + "AddressState : Preferred\n" + "ValidLifetime : 6.17:52:39\n" + "PreferredLifetime : 6.17:52:39\n" + "SkipAsSource : False\n" + "PolicyStore : ActiveStore\n" + "\n\n" + "Caption :\n" + "Description :\n" + "ElementName :\n" + "InstanceID : :8:8:8:9:55=55;:8;8;:8;55;\n" + "AdminDistance :\n" + "DestinationAddress :\n" + "IsStatic :\n" + "RouteMetric : 0\n" + "TypeOfRoute : 3\n" + "AddressFamily : IPv4\n" + "CompartmentId : 1\n" + "DestinationPrefix : 0.0.0.0/0\n" + "InterfaceAlias : Wi-Fi\n" + "InterfaceIndex : 3\n" + "NextHop : 10.0.0.1\n" + "PreferredLifetime : 6.23:14:43\n" + "Protocol : NetMgmt\n" + "Publish : No\n" + "Store : ActiveStore\n" + "ValidLifetime : 6.23:14:43\n" + "PSComputerName :\n" + "ifIndex : 3" + ) mock = MagicMock(return_value=ret) - with patch.dict(win_network.__salt__, {'cmd.run': mock}): - self.assertDictEqual(win_network.get_route('192.0.0.8'), - {'destination': '192.0.0.8', - 'gateway': '10.0.0.1', - 'interface': 'Wi-Fi', - 'source': '10.0.0.15'}) + with patch.dict(win_network.__salt__, {"cmd.run": mock}): + self.assertDictEqual( + win_network.get_route("192.0.0.8"), + { + "destination": "192.0.0.8", + "gateway": "10.0.0.1", + "interface": "Wi-Fi", + "source": "10.0.0.15", + }, + ) diff --git a/tests/unit/modules/test_win_ntp.py b/tests/unit/modules/test_win_ntp.py index e099008a58f..39ee4ae83e9 100644 --- a/tests/unit/modules/test_win_ntp.py +++ b/tests/unit/modules/test_win_ntp.py @@ -1,69 +1,84 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_ntp as win_ntp +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class WinNtpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_ntp - ''' + """ + def setup_loader_modules(self): return {win_ntp: {}} # 'set_servers' function tests: 1 def test_set_servers(self): - ''' + """ Test if it set Windows to use a list of NTP servers - ''' + """ # Windows Time (W32Time) service is not started # Windows Time (W32Time) service fails to start mock_service = MagicMock(return_value=False) - with patch.dict(win_ntp.__salt__, {'service.status': mock_service, - 'service.start': mock_service}): - self.assertFalse(win_ntp.set_servers('pool.ntp.org')) + with patch.dict( + win_ntp.__salt__, + {"service.status": mock_service, "service.start": mock_service}, + ): + self.assertFalse(win_ntp.set_servers("pool.ntp.org")) # Windows Time service is running # Fail to set NTP servers mock_service = MagicMock(return_value=True) - mock_cmd = MagicMock(side_effect=['Failure', 'Failure', 'Failure', 'NtpServer: time.windows.com,0x8']) - with patch.dict(win_ntp.__salt__, {'service.status': mock_service, - 'cmd.run': mock_cmd}): - self.assertFalse(win_ntp.set_servers('pool.ntp.org')) + mock_cmd = MagicMock( + side_effect=[ + "Failure", + "Failure", + "Failure", + "NtpServer: time.windows.com,0x8", + ] + ) + with patch.dict( + win_ntp.__salt__, {"service.status": mock_service, "cmd.run": mock_cmd} + ): + self.assertFalse(win_ntp.set_servers("pool.ntp.org")) # Windows Time service is running # Successfully set NTP servers - mock_cmd = MagicMock(side_effect=['Success', 'Success', 'Success', 'NtpServer: pool.ntp.org']) - with patch.dict(win_ntp.__salt__, {'service.status': mock_service, - 'service.restart': mock_service, - 'cmd.run': mock_cmd}): - self.assertTrue(win_ntp.set_servers('pool.ntp.org')) + mock_cmd = MagicMock( + side_effect=["Success", "Success", "Success", "NtpServer: pool.ntp.org"] + ) + with patch.dict( + win_ntp.__salt__, + { + "service.status": mock_service, + "service.restart": mock_service, + "cmd.run": mock_cmd, + }, + ): + self.assertTrue(win_ntp.set_servers("pool.ntp.org")) # 'get_servers' function tests: 1 def test_get_servers(self): - ''' + """ Test if it get list of configured NTP servers - ''' - mock_cmd = MagicMock(side_effect=['', 'NtpServer: SALT', 'NtpServer']) - with patch.dict(win_ntp.__salt__, {'cmd.run': mock_cmd}): + """ + mock_cmd = MagicMock(side_effect=["", "NtpServer: SALT", "NtpServer"]) + with patch.dict(win_ntp.__salt__, {"cmd.run": mock_cmd}): self.assertFalse(win_ntp.get_servers()) - self.assertListEqual(win_ntp.get_servers(), ['SALT']) + self.assertListEqual(win_ntp.get_servers(), ["SALT"]) self.assertFalse(win_ntp.get_servers()) diff --git a/tests/unit/modules/test_win_path.py b/tests/unit/modules/test_win_path.py index 19f05854cc8..936d0166488 100644 --- a/tests/unit/modules/test_win_path.py +++ b/tests/unit/modules/test_win_path.py @@ -1,35 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.modules.win_path as win_path import salt.utils.stringutils +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class WinPathTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_path - ''' + """ + def setup_loader_modules(self): return {win_path: {}} def __init__(self, *args, **kwargs): super(WinPathTestCase, self).__init__(*args, **kwargs) - self.pathsep = str(';') # future lint: disable=blacklisted-function + self.pathsep = str(";") # future lint: disable=blacklisted-function def assert_call_matches(self, mock_obj, new_path): mock_obj.assert_called_once_with( @@ -37,43 +36,44 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): win_path.KEY, win_path.VNAME, self.pathsep.join(new_path), - win_path.VTYPE + win_path.VTYPE, ) def assert_path_matches(self, env, new_path): self.assertEqual( - env['PATH'], - salt.utils.stringutils.to_str(self.pathsep.join(new_path)) + env["PATH"], salt.utils.stringutils.to_str(self.pathsep.join(new_path)) ) def test_get_path(self): - ''' + """ Test to Returns the system path - ''' - mock = MagicMock(return_value={'vdata': 'C:\\Salt'}) - with patch.dict(win_path.__salt__, {'reg.read_value': mock}): - self.assertListEqual(win_path.get_path(), ['C:\\Salt']) + """ + mock = MagicMock(return_value={"vdata": "C:\\Salt"}) + with patch.dict(win_path.__salt__, {"reg.read_value": mock}): + self.assertListEqual(win_path.get_path(), ["C:\\Salt"]) def test_exists(self): - ''' + """ Test to check if the directory is configured - ''' - get_mock = MagicMock(return_value=['C:\\Foo', 'C:\\Bar']) - with patch.object(win_path, 'get_path', get_mock): + """ + get_mock = MagicMock(return_value=["C:\\Foo", "C:\\Bar"]) + with patch.object(win_path, "get_path", get_mock): # Ensure case insensitivity respected - self.assertTrue(win_path.exists('C:\\FOO')) - self.assertTrue(win_path.exists('c:\\foo')) - self.assertFalse(win_path.exists('c:\\mystuff')) + self.assertTrue(win_path.exists("C:\\FOO")) + self.assertTrue(win_path.exists("c:\\foo")) + self.assertFalse(win_path.exists("c:\\mystuff")) def test_add(self): - ''' + """ Test to add the directory to the SYSTEM path - ''' - orig_path = ('C:\\Foo', 'C:\\Bar') + """ + orig_path = ("C:\\Foo", "C:\\Bar") def _env(path): return { - str('PATH'): salt.utils.stringutils.to_str( # future lint: disable=blacklisted-function + str( + "PATH" + ): salt.utils.stringutils.to_str( # future lint: disable=blacklisted-function self.pathsep.join(path) ) } @@ -84,23 +84,25 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): env = _env(path) mock_get = MagicMock(return_value=list(path)) mock_set = MagicMock(return_value=retval) - with patch.object(win_path, 'PATHSEP', self.pathsep), \ - patch.object(win_path, 'get_path', mock_get), \ - patch.object(os, 'environ', env), \ - patch.dict(win_path.__salt__, {'reg.set_value': mock_set}), \ - patch.object(win_path, 'rehash', MagicMock(return_value=True)): + with patch.object(win_path, "PATHSEP", self.pathsep), patch.object( + win_path, "get_path", mock_get + ), patch.object(os, "environ", env), patch.dict( + win_path.__salt__, {"reg.set_value": mock_set} + ), patch.object( + win_path, "rehash", MagicMock(return_value=True) + ): return win_path.add(name, index), env, mock_set # Test a successful reg update - ret, env, mock_set = _run('c:\\salt', retval=True) - new_path = ('C:\\Foo', 'C:\\Bar', 'c:\\salt') + ret, env, mock_set = _run("c:\\salt", retval=True) + new_path = ("C:\\Foo", "C:\\Bar", "c:\\salt") self.assertTrue(ret) self.assert_call_matches(mock_set, new_path) self.assert_path_matches(env, new_path) # Test an unsuccessful reg update - ret, env, mock_set = _run('c:\\salt', retval=False) - new_path = ('C:\\Foo', 'C:\\Bar', 'c:\\salt') + ret, env, mock_set = _run("c:\\salt", retval=False) + new_path = ("C:\\Foo", "C:\\Bar", "c:\\salt") self.assertFalse(ret) self.assert_call_matches(mock_set, new_path) # The local path should still have been modified even @@ -108,15 +110,15 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assert_path_matches(env, new_path) # Test adding with a custom index - ret, env, mock_set = _run('c:\\salt', index=1, retval=True) - new_path = ('C:\\Foo', 'c:\\salt', 'C:\\Bar') + ret, env, mock_set = _run("c:\\salt", index=1, retval=True) + new_path = ("C:\\Foo", "c:\\salt", "C:\\Bar") self.assertTrue(ret) self.assert_call_matches(mock_set, new_path) self.assert_path_matches(env, new_path) # Test adding with a custom index of 0 - ret, env, mock_set = _run('c:\\salt', index=0, retval=True) - new_path = ('c:\\salt', 'C:\\Foo', 'C:\\Bar') + ret, env, mock_set = _run("c:\\salt", index=0, retval=True) + new_path = ("c:\\salt", "C:\\Foo", "C:\\Bar") self.assertTrue(ret) self.assert_call_matches(mock_set, new_path) self.assert_path_matches(env, new_path) @@ -124,7 +126,7 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): # Test adding path with a case-insensitive match already present, and # no index provided. The path should remain unchanged and we should not # update the registry. - ret, env, mock_set = _run('c:\\foo', retval=True) + ret, env, mock_set = _run("c:\\foo", retval=True) self.assertTrue(ret) mock_set.assert_not_called() self.assert_path_matches(env, orig_path) @@ -133,8 +135,8 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): # negative index provided which does not match the current index. The # match should be removed, and the path should be added to the end of # the list. - ret, env, mock_set = _run('c:\\foo', index=-1, retval=True) - new_path = ('C:\\Bar', 'c:\\foo') + ret, env, mock_set = _run("c:\\foo", index=-1, retval=True) + new_path = ("C:\\Bar", "c:\\foo") self.assertTrue(ret) self.assert_call_matches(mock_set, new_path) self.assert_path_matches(env, new_path) @@ -142,7 +144,7 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): # Test adding path with a case-insensitive match already present, and a # negative index provided which matches the current index. No changes # should be made. - ret, env, mock_set = _run('c:\\foo', index=-2, retval=True) + ret, env, mock_set = _run("c:\\foo", index=-2, retval=True) self.assertTrue(ret) mock_set.assert_not_called() self.assert_path_matches(env, orig_path) @@ -151,7 +153,7 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): # negative index provided which is larger than the size of the list. No # changes should be made, since in these cases we assume an index of 0, # and the case-insensitive match is also at index 0. - ret, env, mock_set = _run('c:\\foo', index=-5, retval=True) + ret, env, mock_set = _run("c:\\foo", index=-5, retval=True) self.assertTrue(ret) mock_set.assert_not_called() self.assert_path_matches(env, orig_path) @@ -161,8 +163,8 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): # The match should be removed from its current location and inserted at # the beginning, since when a negative index is larger than the list, # we put it at the beginning of the list. - ret, env, mock_set = _run('c:\\bar', index=-5, retval=True) - new_path = ('c:\\bar', 'C:\\Foo') + ret, env, mock_set = _run("c:\\bar", index=-5, retval=True) + new_path = ("c:\\bar", "C:\\Foo") self.assertTrue(ret) self.assert_call_matches(mock_set, new_path) self.assert_path_matches(env, new_path) @@ -170,7 +172,7 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): # Test adding path with a case-insensitive match already present, and a # negative index provided which matches the current index. The path # should remain unchanged and we should not update the registry. - ret, env, mock_set = _run('c:\\bar', index=-1, retval=True) + ret, env, mock_set = _run("c:\\bar", index=-1, retval=True) self.assertTrue(ret) mock_set.assert_not_called() self.assert_path_matches(env, orig_path) @@ -179,64 +181,68 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): # an index provided which does not match the current index, and is also # larger than the size of the PATH list. The match should be removed, # and the path should be added to the end of the list. - ret, env, mock_set = _run('c:\\foo', index=5, retval=True) - new_path = ('C:\\Bar', 'c:\\foo') + ret, env, mock_set = _run("c:\\foo", index=5, retval=True) + new_path = ("C:\\Bar", "c:\\foo") self.assertTrue(ret) self.assert_call_matches(mock_set, new_path) self.assert_path_matches(env, new_path) def test_remove(self): - ''' + """ Test win_path.remove - ''' - orig_path = ('C:\\Foo', 'C:\\Bar', 'C:\\Baz') + """ + orig_path = ("C:\\Foo", "C:\\Bar", "C:\\Baz") def _env(path): return { - str('PATH'): salt.utils.stringutils.to_str( # future lint: disable=blacklisted-function + str( + "PATH" + ): salt.utils.stringutils.to_str( # future lint: disable=blacklisted-function self.pathsep.join(path) ) } - def _run(name='c:\\salt', index=None, retval=True, path=None): + def _run(name="c:\\salt", index=None, retval=True, path=None): if path is None: path = orig_path env = _env(path) mock_get = MagicMock(return_value=list(path)) mock_set = MagicMock(return_value=retval) - with patch.object(win_path, 'PATHSEP', self.pathsep), \ - patch.object(win_path, 'get_path', mock_get), \ - patch.object(os, 'environ', env), \ - patch.dict(win_path.__salt__, {'reg.set_value': mock_set}), \ - patch.object(win_path, 'rehash', MagicMock(return_value=True)): + with patch.object(win_path, "PATHSEP", self.pathsep), patch.object( + win_path, "get_path", mock_get + ), patch.object(os, "environ", env), patch.dict( + win_path.__salt__, {"reg.set_value": mock_set} + ), patch.object( + win_path, "rehash", MagicMock(return_value=True) + ): return win_path.remove(name), env, mock_set # Test a successful reg update - ret, env, mock_set = _run('C:\\Bar', retval=True) - new_path = ('C:\\Foo', 'C:\\Baz') + ret, env, mock_set = _run("C:\\Bar", retval=True) + new_path = ("C:\\Foo", "C:\\Baz") self.assertTrue(ret) self.assert_call_matches(mock_set, new_path) self.assert_path_matches(env, new_path) # Test a successful reg update with a case-insensitive match - ret, env, mock_set = _run('c:\\bar', retval=True) - new_path = ('C:\\Foo', 'C:\\Baz') + ret, env, mock_set = _run("c:\\bar", retval=True) + new_path = ("C:\\Foo", "C:\\Baz") self.assertTrue(ret) self.assert_call_matches(mock_set, new_path) self.assert_path_matches(env, new_path) # Test a successful reg update with multiple case-insensitive matches. # All matches should be removed. - old_path = orig_path + ('C:\\BAR',) - ret, env, mock_set = _run('c:\\bar', retval=True) - new_path = ('C:\\Foo', 'C:\\Baz') + old_path = orig_path + ("C:\\BAR",) + ret, env, mock_set = _run("c:\\bar", retval=True) + new_path = ("C:\\Foo", "C:\\Baz") self.assertTrue(ret) self.assert_call_matches(mock_set, new_path) self.assert_path_matches(env, new_path) # Test an unsuccessful reg update - ret, env, mock_set = _run('c:\\bar', retval=False) - new_path = ('C:\\Foo', 'C:\\Baz') + ret, env, mock_set = _run("c:\\bar", retval=False) + new_path = ("C:\\Foo", "C:\\Baz") self.assertFalse(ret) self.assert_call_matches(mock_set, new_path) # The local path should still have been modified even @@ -244,7 +250,7 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assert_path_matches(env, new_path) # Test when no match found - ret, env, mock_set = _run('C:\\NotThere', retval=True) + ret, env, mock_set = _run("C:\\NotThere", retval=True) self.assertTrue(ret) mock_set.assert_not_called() self.assert_path_matches(env, orig_path) diff --git a/tests/unit/modules/test_win_pkg.py b/tests/unit/modules/test_win_pkg.py index 18f4f6ca824..0b026b738cb 100644 --- a/tests/unit/modules/test_win_pkg.py +++ b/tests/unit/modules/test_win_pkg.py @@ -1,15 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the win_pkg module -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import MagicMock, patch -from tests.support.unit import TestCase, skipIf +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.config as config @@ -22,221 +17,269 @@ import salt.utils.win_reg as win_reg # Import 3rd Party Libs from salt.ext import six +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + @skipIf(not salt.utils.platform.is_windows(), "Must be on Windows!") class WinPkgInstallTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_pkg - ''' + """ + def setup_loader_modules(self): pkg_info = { - '3.03': { - 'full_name': 'Nullsoft Install System', - 'installer': 'http://download.sourceforge.net/project/nsis/NSIS%203/3.03/nsis-3.03-setup.exe', - 'install_flags': '/S', - 'uninstaller': '%PROGRAMFILES(x86)%\\NSIS\\uninst-nsis.exe', - 'uninstall_flags': '/S', - 'msiexec': False, - 'reboot': False + "3.03": { + "full_name": "Nullsoft Install System", + "installer": "http://download.sourceforge.net/project/nsis/NSIS%203/3.03/nsis-3.03-setup.exe", + "install_flags": "/S", + "uninstaller": "%PROGRAMFILES(x86)%\\NSIS\\uninst-nsis.exe", + "uninstall_flags": "/S", + "msiexec": False, + "reboot": False, + }, + "3.02": { + "full_name": "Nullsoft Install System", + "installer": "http://download.sourceforge.net/project/nsis/NSIS%203/3.02/nsis-3.02-setup.exe", + "install_flags": "/S", + "uninstaller": "%PROGRAMFILES(x86)%\\NSIS\\uninst-nsis.exe", + "uninstall_flags": "/S", + "msiexec": False, + "reboot": False, }, - '3.02': { - 'full_name': 'Nullsoft Install System', - 'installer': 'http://download.sourceforge.net/project/nsis/NSIS%203/3.02/nsis-3.02-setup.exe', - 'install_flags': '/S', - 'uninstaller': '%PROGRAMFILES(x86)%\\NSIS\\uninst-nsis.exe', - 'uninstall_flags': '/S', - 'msiexec': False, - 'reboot': False - } } - return{ + return { win_pkg: { - '_get_latest_package_version': MagicMock(return_value='3.03'), - '_get_package_info': MagicMock(return_value=pkg_info), - '__salt__': { - 'pkg_resource.add_pkg': pkg_resource.add_pkg, - 'pkg_resource.parse_targets': pkg_resource.parse_targets, - 'pkg_resource.sort_pkglist': pkg_resource.sort_pkglist, - 'pkg_resource.stringify': pkg_resource.stringify, - 'config.valid_fileproto': config.valid_fileproto, + "_get_latest_package_version": MagicMock(return_value="3.03"), + "_get_package_info": MagicMock(return_value=pkg_info), + "__salt__": { + "pkg_resource.add_pkg": pkg_resource.add_pkg, + "pkg_resource.parse_targets": pkg_resource.parse_targets, + "pkg_resource.sort_pkglist": pkg_resource.sort_pkglist, + "pkg_resource.stringify": pkg_resource.stringify, + "config.valid_fileproto": config.valid_fileproto, }, - '__utils__': { - 'reg.key_exists': win_reg.key_exists, - 'reg.list_keys': win_reg.list_keys, - 'reg.read_value': win_reg.read_value, - 'reg.value_exists': win_reg.value_exists, + "__utils__": { + "reg.key_exists": win_reg.key_exists, + "reg.list_keys": win_reg.list_keys, + "reg.read_value": win_reg.read_value, + "reg.value_exists": win_reg.value_exists, }, }, - pkg_resource: { - '__grains__': { - 'os': 'Windows' - } - }, + pkg_resource: {"__grains__": {"os": "Windows"}}, } def test_pkg__get_reg_software(self): result = win_pkg._get_reg_software() self.assertTrue(isinstance(result, dict)) found_python = False - search = 'Python 2' if six.PY2 else 'Python 3' + search = "Python 2" if six.PY2 else "Python 3" for key in result: if search in key: found_python = True self.assertTrue(found_python) def test_pkg_install_not_found(self): - ''' + """ Test pkg.install when the Version is NOT FOUND in the Software Definition - ''' - ret_reg = {'Nullsoft Install System': '3.03'} + """ + ret_reg = {"Nullsoft Install System": "3.03"} # The 2nd time it's run with stringify - se_list_pkgs = {'nsis': ['3.03']} - with patch.object(win_pkg, 'list_pkgs', return_value=se_list_pkgs), \ - patch.object(win_pkg, '_get_reg_software', return_value=ret_reg): - expected = {'nsis': {'not found': '3.01'}} - result = win_pkg.install(name='nsis', version='3.01') + se_list_pkgs = {"nsis": ["3.03"]} + with patch.object( + win_pkg, "list_pkgs", return_value=se_list_pkgs + ), patch.object(win_pkg, "_get_reg_software", return_value=ret_reg): + expected = {"nsis": {"not found": "3.01"}} + result = win_pkg.install(name="nsis", version="3.01") self.assertDictEqual(expected, result) def test_pkg_install_rollback(self): - ''' + """ test pkg.install rolling back to a previous version - ''' - ret_reg = {'Nullsoft Install System': '3.03'} + """ + ret_reg = {"Nullsoft Install System": "3.03"} # The 2nd time it's run, pkg.list_pkgs uses with stringify - se_list_pkgs = [{'nsis': ['3.03']}, - {'nsis': '3.02'}] - with patch.object(win_pkg, 'list_pkgs', side_effect=se_list_pkgs), \ - patch.object( - win_pkg, '_get_reg_software', return_value=ret_reg), \ - patch.dict( - win_pkg.__salt__, - {'cp.is_cached': MagicMock(return_value=False)}), \ - patch.dict( - win_pkg.__salt__, - {'cp.cache_file': - MagicMock(return_value='C:\\fake\\path.exe')}), \ - patch.dict( - win_pkg.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 0})}): - expected = {'nsis': {'new': '3.02', 'old': '3.03'}} - result = win_pkg.install(name='nsis', version='3.02') + se_list_pkgs = [{"nsis": ["3.03"]}, {"nsis": "3.02"}] + with patch.object(win_pkg, "list_pkgs", side_effect=se_list_pkgs), patch.object( + win_pkg, "_get_reg_software", return_value=ret_reg + ), patch.dict( + win_pkg.__salt__, {"cp.is_cached": MagicMock(return_value=False)} + ), patch.dict( + win_pkg.__salt__, + {"cp.cache_file": MagicMock(return_value="C:\\fake\\path.exe")}, + ), patch.dict( + win_pkg.__salt__, {"cmd.run_all": MagicMock(return_value={"retcode": 0})} + ): + expected = {"nsis": {"new": "3.02", "old": "3.03"}} + result = win_pkg.install(name="nsis", version="3.02") self.assertDictEqual(expected, result) def test_pkg_install_existing(self): - ''' + """ test pkg.install when the package is already installed no version passed - ''' - ret_reg = {'Nullsoft Install System': '3.03'} + """ + ret_reg = {"Nullsoft Install System": "3.03"} # The 2nd time it's run, pkg.list_pkgs uses with stringify - se_list_pkgs = {'nsis': ['3.03']} - with patch.object(win_pkg, 'list_pkgs', return_value=se_list_pkgs), \ - patch.object( - win_pkg, '_get_reg_software', return_value=ret_reg), \ - patch.dict( - win_pkg.__salt__, - {'cp.is_cached': MagicMock(return_value=False)}), \ - patch.dict( - win_pkg.__salt__, - {'cp.cache_file': - MagicMock(return_value='C:\\fake\\path.exe')}), \ - patch.dict( - win_pkg.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 0})}): + se_list_pkgs = {"nsis": ["3.03"]} + with patch.object( + win_pkg, "list_pkgs", return_value=se_list_pkgs + ), patch.object(win_pkg, "_get_reg_software", return_value=ret_reg), patch.dict( + win_pkg.__salt__, {"cp.is_cached": MagicMock(return_value=False)} + ), patch.dict( + win_pkg.__salt__, + {"cp.cache_file": MagicMock(return_value="C:\\fake\\path.exe")}, + ), patch.dict( + win_pkg.__salt__, {"cmd.run_all": MagicMock(return_value={"retcode": 0})} + ): expected = {} - result = win_pkg.install(name='nsis') + result = win_pkg.install(name="nsis") self.assertDictEqual(expected, result) def test_pkg_install_existing_with_version(self): - ''' + """ test pkg.install when the package is already installed A version is passed - ''' - ret_reg = {'Nullsoft Install System': '3.03'} + """ + ret_reg = {"Nullsoft Install System": "3.03"} # The 2nd time it's run, pkg.list_pkgs uses with stringify - se_list_pkgs = {'nsis': ['3.03']} - with patch.object(win_pkg, 'list_pkgs', return_value=se_list_pkgs), \ - patch.object( - win_pkg, '_get_reg_software', return_value=ret_reg), \ - patch.dict( - win_pkg.__salt__, - {'cp.is_cached': MagicMock(return_value=False)}), \ - patch.dict( - win_pkg.__salt__, - {'cp.cache_file': - MagicMock(return_value='C:\\fake\\path.exe')}), \ - patch.dict( - win_pkg.__salt__, - {'cmd.run_all': - MagicMock(return_value={'retcode': 0})}): + se_list_pkgs = {"nsis": ["3.03"]} + with patch.object( + win_pkg, "list_pkgs", return_value=se_list_pkgs + ), patch.object(win_pkg, "_get_reg_software", return_value=ret_reg), patch.dict( + win_pkg.__salt__, {"cp.is_cached": MagicMock(return_value=False)} + ), patch.dict( + win_pkg.__salt__, + {"cp.cache_file": MagicMock(return_value="C:\\fake\\path.exe")}, + ), patch.dict( + win_pkg.__salt__, {"cmd.run_all": MagicMock(return_value={"retcode": 0})} + ): expected = {} - result = win_pkg.install(name='nsis', version='3.03') + result = win_pkg.install(name="nsis", version="3.03") self.assertDictEqual(expected, result) def test_pkg_install_name(self): - ''' + """ test pkg.install name extra_install_flags - ''' + """ - ret__get_package_info = {'3.03': {'uninstaller': '%program.exe', 'reboot': False, - 'msiexec': False, - 'installer': 'runme.exe', - 'uninstall_flags': '/S', 'locale': 'en_US', 'install_flags': '/s', - 'full_name': 'Firebox 3.03 (x86 en-US)'}} + ret__get_package_info = { + "3.03": { + "uninstaller": "%program.exe", + "reboot": False, + "msiexec": False, + "installer": "runme.exe", + "uninstall_flags": "/S", + "locale": "en_US", + "install_flags": "/s", + "full_name": "Firebox 3.03 (x86 en-US)", + } + } - mock_cmd_run_all = MagicMock(return_value={'retcode': 0}) - with patch.object(salt.utils.data, 'is_true', MagicMock(return_value=True)),\ - patch.object(win_pkg, '_get_package_info', MagicMock(return_value=ret__get_package_info)),\ - patch.dict(win_pkg.__salt__, {'pkg_resource.parse_targets': - MagicMock(return_value=[{'firebox': '3.03'}, None]), - 'cp.is_cached': - MagicMock(return_value='C:\\fake\\path.exe'), - 'cmd.run_all': mock_cmd_run_all}): - ret = win_pkg.install(name='firebox', version='3.03', extra_install_flags='-e True -test_flag True') - self.assertTrue('-e True -test_flag True' in str(mock_cmd_run_all.call_args[0])) + mock_cmd_run_all = MagicMock(return_value={"retcode": 0}) + with patch.object( + salt.utils.data, "is_true", MagicMock(return_value=True) + ), patch.object( + win_pkg, "_get_package_info", MagicMock(return_value=ret__get_package_info) + ), patch.dict( + win_pkg.__salt__, + { + "pkg_resource.parse_targets": MagicMock( + return_value=[{"firebox": "3.03"}, None] + ), + "cp.is_cached": MagicMock(return_value="C:\\fake\\path.exe"), + "cmd.run_all": mock_cmd_run_all, + }, + ): + ret = win_pkg.install( + name="firebox", + version="3.03", + extra_install_flags="-e True -test_flag True", + ) + self.assertTrue( + "-e True -test_flag True" in str(mock_cmd_run_all.call_args[0]) + ) def test_pkg_install_single_pkg(self): - ''' + """ test pkg.install pkg with extra_install_flags - ''' - ret__get_package_info = {'3.03': {'uninstaller': '%program.exe', 'reboot': False, - 'msiexec': False, - 'installer': 'runme.exe', - 'uninstall_flags': '/S', 'locale': 'en_US', 'install_flags': '/s', - 'full_name': 'Firebox 3.03 (x86 en-US)'}} + """ + ret__get_package_info = { + "3.03": { + "uninstaller": "%program.exe", + "reboot": False, + "msiexec": False, + "installer": "runme.exe", + "uninstall_flags": "/S", + "locale": "en_US", + "install_flags": "/s", + "full_name": "Firebox 3.03 (x86 en-US)", + } + } - mock_cmd_run_all = MagicMock(return_value={'retcode': 0}) - with patch.object(salt.utils.data, 'is_true', MagicMock(return_value=True)), \ - patch.object(win_pkg, '_get_package_info', MagicMock(return_value=ret__get_package_info)), \ - patch.dict(win_pkg.__salt__, {'pkg_resource.parse_targets': - MagicMock(return_value=[{'firebox': '3.03'}, None]), - 'cp.is_cached': - MagicMock(return_value='C:\\fake\\path.exe'), - 'cmd.run_all': mock_cmd_run_all}): - ret = win_pkg.install(pkgs=['firebox'], version='3.03', extra_install_flags='-e True -test_flag True') - self.assertTrue('-e True -test_flag True' in str(mock_cmd_run_all.call_args[0])) + mock_cmd_run_all = MagicMock(return_value={"retcode": 0}) + with patch.object( + salt.utils.data, "is_true", MagicMock(return_value=True) + ), patch.object( + win_pkg, "_get_package_info", MagicMock(return_value=ret__get_package_info) + ), patch.dict( + win_pkg.__salt__, + { + "pkg_resource.parse_targets": MagicMock( + return_value=[{"firebox": "3.03"}, None] + ), + "cp.is_cached": MagicMock(return_value="C:\\fake\\path.exe"), + "cmd.run_all": mock_cmd_run_all, + }, + ): + ret = win_pkg.install( + pkgs=["firebox"], + version="3.03", + extra_install_flags="-e True -test_flag True", + ) + self.assertTrue( + "-e True -test_flag True" in str(mock_cmd_run_all.call_args[0]) + ) def test_pkg_install_multiple_pkgs(self): - ''' + """ test pkg.install pkg with extra_install_flags - ''' - ret__get_package_info = {'3.03': {'uninstaller': '%program.exe', 'reboot': False, - 'msiexec': False, - 'installer': 'runme.exe', - 'uninstall_flags': '/S', 'locale': 'en_US', 'install_flags': '/s', - 'full_name': 'Firebox 3.03 (x86 en-US)'}} + """ + ret__get_package_info = { + "3.03": { + "uninstaller": "%program.exe", + "reboot": False, + "msiexec": False, + "installer": "runme.exe", + "uninstall_flags": "/S", + "locale": "en_US", + "install_flags": "/s", + "full_name": "Firebox 3.03 (x86 en-US)", + } + } - mock_cmd_run_all = MagicMock(return_value={'retcode': 0}) - with patch.object(salt.utils.data, 'is_true', MagicMock(return_value=True)), \ - patch.object(win_pkg, '_get_package_info', MagicMock(return_value=ret__get_package_info)), \ - patch.dict(win_pkg.__salt__, {'pkg_resource.parse_targets': - MagicMock(return_value=[{'firebox': '3.03', 'got': '3.03'}, None]), - 'cp.is_cached': - MagicMock(return_value='C:\\fake\\path.exe'), - 'cmd.run_all': mock_cmd_run_all}): - ret = win_pkg.install(pkgs=['firebox', 'got'], extra_install_flags='-e True -test_flag True') - self.assertFalse('-e True -test_flag True' in str(mock_cmd_run_all.call_args[0])) + mock_cmd_run_all = MagicMock(return_value={"retcode": 0}) + with patch.object( + salt.utils.data, "is_true", MagicMock(return_value=True) + ), patch.object( + win_pkg, "_get_package_info", MagicMock(return_value=ret__get_package_info) + ), patch.dict( + win_pkg.__salt__, + { + "pkg_resource.parse_targets": MagicMock( + return_value=[{"firebox": "3.03", "got": "3.03"}, None] + ), + "cp.is_cached": MagicMock(return_value="C:\\fake\\path.exe"), + "cmd.run_all": mock_cmd_run_all, + }, + ): + ret = win_pkg.install( + pkgs=["firebox", "got"], extra_install_flags="-e True -test_flag True" + ) + self.assertFalse( + "-e True -test_flag True" in str(mock_cmd_run_all.call_args[0]) + ) diff --git a/tests/unit/modules/test_win_pki.py b/tests/unit/modules/test_win_pki.py index e42e06558a4..f2579020ea7 100644 --- a/tests/unit/modules/test_win_pki.py +++ b/tests/unit/modules/test_win_pki.py @@ -1,181 +1,175 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for Windows PKI Module 'module.win_pki' :platform: Windows :maturity: develop .. versionadded:: 2017.7.0 -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_pki as win_pki # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) -CERT_PATH = r'C:\certs\testdomain.local.cer' -THUMBPRINT = '9988776655443322111000AAABBBCCCDDDEEEFFF' +CERT_PATH = r"C:\certs\testdomain.local.cer" +THUMBPRINT = "9988776655443322111000AAABBBCCCDDDEEEFFF" CERTS = { THUMBPRINT: { - 'dnsnames': ['testdomain.local'], - 'serialnumber': '0123456789AABBCCDD', - 'subject': 'CN=testdomain.local, OU=testou, O=testorg, S=California, C=US', - 'thumbprint': THUMBPRINT, - 'version': 3 + "dnsnames": ["testdomain.local"], + "serialnumber": "0123456789AABBCCDD", + "subject": "CN=testdomain.local, OU=testou, O=testorg, S=California, C=US", + "thumbprint": THUMBPRINT, + "version": 3, } } STORES = { - 'CurrentUser': [ - 'AuthRoot', - 'CA', - 'ClientAuthIssuer', - 'Disallowed', - 'MSIEHistoryJournal', - 'My', - 'Root', - 'SmartCardRoot', - 'Trust', - 'TrustedPeople', - 'TrustedPublisher', - 'UserDS' + "CurrentUser": [ + "AuthRoot", + "CA", + "ClientAuthIssuer", + "Disallowed", + "MSIEHistoryJournal", + "My", + "Root", + "SmartCardRoot", + "Trust", + "TrustedPeople", + "TrustedPublisher", + "UserDS", + ], + "LocalMachine": [ + "AuthRoot", + "CA", + "ClientAuthIssuer", + "Disallowed", + "My", + "Remote Desktop", + "Root", + "SmartCardRoot", + "Trust", + "TrustedDevices", + "TrustedPeople", + "TrustedPublisher", + "WebHosting", ], - 'LocalMachine': [ - 'AuthRoot', - 'CA', - 'ClientAuthIssuer', - 'Disallowed', - 'My', - 'Remote Desktop', - 'Root', - 'SmartCardRoot', - 'Trust', - 'TrustedDevices', - 'TrustedPeople', - 'TrustedPublisher', - 'WebHosting' - ] } -JSON_CERTS = [{ - 'DnsNameList': [{ - 'Punycode': 'testdomain.local', - 'Unicode': 'testdomain.local' - }], - 'SerialNumber': '0123456789AABBCCDD', - 'Subject': 'CN=testdomain.local, OU=testou, O=testorg, S=California, C=US', - 'Thumbprint': '9988776655443322111000AAABBBCCCDDDEEEFFF', - 'Version': 3 -}] +JSON_CERTS = [ + { + "DnsNameList": [ + {"Punycode": "testdomain.local", "Unicode": "testdomain.local"} + ], + "SerialNumber": "0123456789AABBCCDD", + "Subject": "CN=testdomain.local, OU=testou, O=testorg, S=California, C=US", + "Thumbprint": "9988776655443322111000AAABBBCCCDDDEEEFFF", + "Version": 3, + } +] -JSON_STORES = [{ - 'LocationName': 'CurrentUser', - 'StoreNames': STORES['CurrentUser'] -}, { - 'LocationName': 'LocalMachine', - 'StoreNames': STORES['LocalMachine'] -}] +JSON_STORES = [ + {"LocationName": "CurrentUser", "StoreNames": STORES["CurrentUser"]}, + {"LocationName": "LocalMachine", "StoreNames": STORES["LocalMachine"]}, +] class WinPkiTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_pki - ''' + """ + def setup_loader_modules(self): return {win_pki: {}} def test_get_stores(self): - ''' + """ Test - Get the certificate location contexts and their corresponding stores. - ''' - with patch.dict(win_pki.__salt__), \ - patch('salt.modules.win_pki._cmd_run', - MagicMock(return_value=JSON_STORES)): + """ + with patch.dict(win_pki.__salt__), patch( + "salt.modules.win_pki._cmd_run", MagicMock(return_value=JSON_STORES) + ): self.assertEqual(win_pki.get_stores(), STORES) def test_get_certs(self): - ''' + """ Test - Get the available certificates in the given store. - ''' - with patch.dict(win_pki.__salt__), \ - patch('salt.modules.win_pki._cmd_run', - MagicMock(return_value=JSON_CERTS)), \ - patch('salt.modules.win_pki._validate_cert_path', - MagicMock(return_value=None)): + """ + with patch.dict(win_pki.__salt__), patch( + "salt.modules.win_pki._cmd_run", MagicMock(return_value=JSON_CERTS) + ), patch( + "salt.modules.win_pki._validate_cert_path", MagicMock(return_value=None) + ): self.assertEqual(win_pki.get_certs(), CERTS) def test_get_cert_file(self): - ''' + """ Test - Get the details of the certificate file. - ''' - kwargs = {'name': CERT_PATH} - with patch.dict(win_pki.__salt__), \ - patch('os.path.isfile', - MagicMock(return_value=True)), \ - patch('salt.modules.win_pki._cmd_run', - MagicMock(return_value=JSON_CERTS)): + """ + kwargs = {"name": CERT_PATH} + with patch.dict(win_pki.__salt__), patch( + "os.path.isfile", MagicMock(return_value=True) + ), patch("salt.modules.win_pki._cmd_run", MagicMock(return_value=JSON_CERTS)): self.assertEqual(win_pki.get_cert_file(**kwargs), CERTS[THUMBPRINT]) def test_import_cert(self): - ''' + """ Test - Import the certificate file into the given certificate store. - ''' - kwargs = {'name': CERT_PATH} + """ + kwargs = {"name": CERT_PATH} mock_value = MagicMock(return_value=CERT_PATH) - with patch.dict(win_pki.__salt__, {'cp.cache_file': mock_value}), \ - patch('salt.modules.win_pki._cmd_run', - MagicMock(return_value=JSON_CERTS)), \ - patch('salt.modules.win_pki._validate_cert_path', - MagicMock(return_value=None)), \ - patch('salt.modules.win_pki.get_cert_file', - MagicMock(return_value=CERTS[THUMBPRINT])), \ - patch('salt.modules.win_pki.get_certs', - MagicMock(return_value=CERTS)): + with patch.dict(win_pki.__salt__, {"cp.cache_file": mock_value}), patch( + "salt.modules.win_pki._cmd_run", MagicMock(return_value=JSON_CERTS) + ), patch( + "salt.modules.win_pki._validate_cert_path", MagicMock(return_value=None) + ), patch( + "salt.modules.win_pki.get_cert_file", + MagicMock(return_value=CERTS[THUMBPRINT]), + ), patch( + "salt.modules.win_pki.get_certs", MagicMock(return_value=CERTS) + ): self.assertTrue(win_pki.import_cert(**kwargs)) def test_export_cert(self): - ''' + """ Test - Export the certificate to a file from the given certificate store. - ''' - kwargs = {'name': CERT_PATH, - 'thumbprint': THUMBPRINT} - with patch.dict(win_pki.__salt__), \ - patch('salt.modules.win_pki._cmd_run', - MagicMock(return_value='True')), \ - patch('salt.modules.win_pki._validate_cert_format', - MagicMock(return_value=None)), \ - patch('salt.modules.win_pki._validate_cert_path', - MagicMock(return_value=None)): + """ + kwargs = {"name": CERT_PATH, "thumbprint": THUMBPRINT} + with patch.dict(win_pki.__salt__), patch( + "salt.modules.win_pki._cmd_run", MagicMock(return_value="True") + ), patch( + "salt.modules.win_pki._validate_cert_format", MagicMock(return_value=None) + ), patch( + "salt.modules.win_pki._validate_cert_path", MagicMock(return_value=None) + ): self.assertTrue(win_pki.export_cert(**kwargs)) def test_test_cert(self): - ''' + """ Test - Check the certificate for validity. - ''' - with patch.dict(win_pki.__salt__), \ - patch('salt.modules.win_pki._cmd_run', - MagicMock(return_value='True')), \ - patch('salt.modules.win_pki._validate_cert_path', - MagicMock(return_value=None)): + """ + with patch.dict(win_pki.__salt__), patch( + "salt.modules.win_pki._cmd_run", MagicMock(return_value="True") + ), patch( + "salt.modules.win_pki._validate_cert_path", MagicMock(return_value=None) + ): self.assertTrue(win_pki.test_cert(thumbprint=THUMBPRINT)) def test_remove_cert(self): - ''' + """ Test - Remove the certificate from the given certificate store. - ''' - with patch.dict(win_pki.__salt__), \ - patch('salt.modules.win_pki._cmd_run', - MagicMock(return_value=None)), \ - patch('salt.modules.win_pki._validate_cert_path', - MagicMock(return_value=None)), \ - patch('salt.modules.win_pki.get_certs', - MagicMock(return_value=CERTS)): + """ + with patch.dict(win_pki.__salt__), patch( + "salt.modules.win_pki._cmd_run", MagicMock(return_value=None) + ), patch( + "salt.modules.win_pki._validate_cert_path", MagicMock(return_value=None) + ), patch( + "salt.modules.win_pki.get_certs", MagicMock(return_value=CERTS) + ): self.assertTrue(win_pki.remove_cert(thumbprint=THUMBPRINT[::-1])) diff --git a/tests/unit/modules/test_win_powercfg.py b/tests/unit/modules/test_win_powercfg.py index 38434cb2486..27f28ae3bfa 100644 --- a/tests/unit/modules/test_win_powercfg.py +++ b/tests/unit/modules/test_win_powercfg.py @@ -1,30 +1,26 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_powercfg as powercfg # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - call -) class PowerCfgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the powercfg state - ''' + """ def setup_loader_modules(self): - return {powercfg: {'__grains__': {'osrelease': 8}}} + return {powercfg: {"__grains__": {"osrelease": 8}}} - query_output = '''Subgroup GUID: 238c9fa8-0aad-41ed-83f4-97be242c8f20 (Hibernate) + query_output = """Subgroup GUID: 238c9fa8-0aad-41ed-83f4-97be242c8f20 (Hibernate) GUID Alias: SUB_SLEEP Power Setting GUID: 29f6c1db-86da-48c5-9fdb-f2b67b1f44da (Hibernate after) GUID Alias: HIBERNATEIDLE @@ -33,218 +29,242 @@ class PowerCfgTestCase(TestCase, LoaderModuleMockMixin): Possible Settings increment: 0x00000001 Possible Settings units: Seconds Current AC Power Setting Index: 0x00000708 - Current DC Power Setting Index: 0x00000384''' + Current DC Power Setting Index: 0x00000384""" def test_set_monitor_timeout(self): - ''' + """ Test to make sure we can set the monitor timeout value - ''' + """ mock = MagicMock(return_value=0) mock.side_effect = [ - 'Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)', - self.query_output] + "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + self.query_output, + ] mock_retcode = MagicMock(return_value=0) - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): - with patch.dict(powercfg.__salt__, {'cmd.retcode': mock_retcode}): - powercfg.set_monitor_timeout(0, 'dc') + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): + with patch.dict(powercfg.__salt__, {"cmd.retcode": mock_retcode}): + powercfg.set_monitor_timeout(0, "dc") mock.assert_called_once_with( - 'powercfg /getactivescheme', - python_shell=False) + "powercfg /getactivescheme", python_shell=False + ) mock_retcode.assert_called_once_with( - 'powercfg /setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_VIDEO VIDEOIDLE 0', - python_shell=False) + "powercfg /setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_VIDEO VIDEOIDLE 0", + python_shell=False, + ) def test_set_disk_timeout(self): - ''' + """ Test to make sure we can set the disk timeout value - ''' + """ mock = MagicMock() mock.side_effect = [ - 'Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)', - self.query_output] + "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + self.query_output, + ] mock_retcode = MagicMock(return_value=0) - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): - with patch.dict(powercfg.__salt__, {'cmd.retcode': mock_retcode}): - powercfg.set_disk_timeout(0, 'dc') + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): + with patch.dict(powercfg.__salt__, {"cmd.retcode": mock_retcode}): + powercfg.set_disk_timeout(0, "dc") mock.assert_called_once_with( - 'powercfg /getactivescheme', - python_shell=False) + "powercfg /getactivescheme", python_shell=False + ) mock_retcode.assert_called_once_with( - 'powercfg /setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_DISK DISKIDLE 0', - python_shell=False) + "powercfg /setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_DISK DISKIDLE 0", + python_shell=False, + ) def test_set_standby_timeout(self): - ''' + """ Test to make sure we can set the standby timeout value - ''' + """ mock = MagicMock(return_value=0) mock.side_effect = [ - 'Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)', - self.query_output] + "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + self.query_output, + ] mock_retcode = MagicMock(return_value=0) - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): - with patch.dict(powercfg.__salt__, {'cmd.retcode': mock_retcode}): - powercfg.set_standby_timeout(0, 'dc') + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): + with patch.dict(powercfg.__salt__, {"cmd.retcode": mock_retcode}): + powercfg.set_standby_timeout(0, "dc") mock.assert_called_once_with( - 'powercfg /getactivescheme', - python_shell=False) + "powercfg /getactivescheme", python_shell=False + ) mock_retcode.assert_called_once_with( - 'powercfg /setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP STANDBYIDLE 0', - python_shell=False) + "powercfg /setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP STANDBYIDLE 0", + python_shell=False, + ) def test_set_hibernate_timeout(self): - ''' + """ Test to make sure we can set the hibernate timeout value - ''' + """ mock = MagicMock(return_value=0) mock.side_effect = [ - 'Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)', - self.query_output] + "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + self.query_output, + ] mock_retcode = MagicMock(return_value=0) - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): - with patch.dict(powercfg.__salt__, {'cmd.retcode': mock_retcode}): - powercfg.set_hibernate_timeout(0, 'dc') + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): + with patch.dict(powercfg.__salt__, {"cmd.retcode": mock_retcode}): + powercfg.set_hibernate_timeout(0, "dc") mock.assert_called_once_with( - 'powercfg /getactivescheme', - python_shell=False) + "powercfg /getactivescheme", python_shell=False + ) mock_retcode.assert_called_once_with( - 'powercfg /setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP HIBERNATEIDLE 0', - python_shell=False) + "powercfg /setdcvalueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP HIBERNATEIDLE 0", + python_shell=False, + ) def test_get_monitor_timeout(self): - ''' + """ Test to make sure we can get the monitor timeout value - ''' + """ mock = MagicMock() mock.side_effect = [ - 'Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)', - self.query_output] + "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + self.query_output, + ] - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): ret = powercfg.get_monitor_timeout() calls = [ - call('powercfg /getactivescheme', python_shell=False), - call('powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_VIDEO VIDEOIDLE', - python_shell=False) + call("powercfg /getactivescheme", python_shell=False), + call( + "powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_VIDEO VIDEOIDLE", + python_shell=False, + ), ] mock.assert_has_calls(calls) - self.assertEqual({'ac': 30, 'dc': 15}, ret) + self.assertEqual({"ac": 30, "dc": 15}, ret) def test_get_disk_timeout(self): - ''' + """ Test to make sure we can get the disk timeout value - ''' + """ mock = MagicMock() mock.side_effect = [ - 'Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)', - self.query_output] + "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + self.query_output, + ] - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): ret = powercfg.get_disk_timeout() calls = [ - call('powercfg /getactivescheme', python_shell=False), - call('powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_DISK DISKIDLE', - python_shell=False) + call("powercfg /getactivescheme", python_shell=False), + call( + "powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_DISK DISKIDLE", + python_shell=False, + ), ] mock.assert_has_calls(calls) - self.assertEqual({'ac': 30, 'dc': 15}, ret) + self.assertEqual({"ac": 30, "dc": 15}, ret) def test_get_standby_timeout(self): - ''' + """ Test to make sure we can get the standby timeout value - ''' + """ mock = MagicMock() mock.side_effect = [ - 'Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)', - self.query_output] + "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + self.query_output, + ] - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): ret = powercfg.get_standby_timeout() calls = [ - call('powercfg /getactivescheme', python_shell=False), - call('powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP STANDBYIDLE', - python_shell=False) + call("powercfg /getactivescheme", python_shell=False), + call( + "powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP STANDBYIDLE", + python_shell=False, + ), ] mock.assert_has_calls(calls) - self.assertEqual({'ac': 30, 'dc': 15}, ret) + self.assertEqual({"ac": 30, "dc": 15}, ret) def test_get_hibernate_timeout(self): - ''' + """ Test to make sure we can get the hibernate timeout value - ''' + """ mock = MagicMock() mock.side_effect = [ - 'Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)', - self.query_output] + "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + self.query_output, + ] - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): ret = powercfg.get_hibernate_timeout() calls = [ - call('powercfg /getactivescheme', python_shell=False), - call('powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP HIBERNATEIDLE', - python_shell=False) + call("powercfg /getactivescheme", python_shell=False), + call( + "powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP HIBERNATEIDLE", + python_shell=False, + ), ] mock.assert_has_calls(calls) - self.assertEqual({'ac': 30, 'dc': 15}, ret) + self.assertEqual({"ac": 30, "dc": 15}, ret) def test_windows_7(self): - ''' + """ Test to make sure we can get the hibernate timeout value on windows 7 - ''' + """ mock = MagicMock() mock.side_effect = [ - 'Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)', - self.query_output] + "Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)", + self.query_output, + ] - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): - with patch.dict(powercfg.__grains__, {'osrelease': '7'}): + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): + with patch.dict(powercfg.__grains__, {"osrelease": "7"}): ret = powercfg.get_hibernate_timeout() calls = [ - call('powercfg /getactivescheme', python_shell=False), - call('powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP', - python_shell=False) + call("powercfg /getactivescheme", python_shell=False), + call( + "powercfg /q 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP", + python_shell=False, + ), ] mock.assert_has_calls(calls) - self.assertEqual({'ac': 30, 'dc': 15}, ret) + self.assertEqual({"ac": 30, "dc": 15}, ret) def test_set_hibernate_timeout_scheme(self): - ''' + """ Test to make sure we can set the hibernate timeout value - ''' + """ mock = MagicMock(return_value=0) mock.side_effect = [self.query_output] - with patch.dict(powercfg.__salt__, {'cmd.retcode': mock}): - powercfg.set_hibernate_timeout(0, 'dc', scheme='SCHEME_MIN') + with patch.dict(powercfg.__salt__, {"cmd.retcode": mock}): + powercfg.set_hibernate_timeout(0, "dc", scheme="SCHEME_MIN") mock.assert_called_once_with( - 'powercfg /setdcvalueindex SCHEME_MIN SUB_SLEEP HIBERNATEIDLE 0', - python_shell=False) + "powercfg /setdcvalueindex SCHEME_MIN SUB_SLEEP HIBERNATEIDLE 0", + python_shell=False, + ) def test_get_hibernate_timeout_scheme(self): - ''' + """ Test to make sure we can get the hibernate timeout value with a specified scheme - ''' + """ mock = MagicMock() mock.side_effect = [self.query_output] - with patch.dict(powercfg.__salt__, {'cmd.run': mock}): - ret = powercfg.get_hibernate_timeout(scheme='SCHEME_MIN') + with patch.dict(powercfg.__salt__, {"cmd.run": mock}): + ret = powercfg.get_hibernate_timeout(scheme="SCHEME_MIN") mock.assert_called_once_with( - 'powercfg /q SCHEME_MIN SUB_SLEEP HIBERNATEIDLE', - python_shell=False) + "powercfg /q SCHEME_MIN SUB_SLEEP HIBERNATEIDLE", python_shell=False + ) - self.assertEqual({'ac': 30, 'dc': 15}, ret) + self.assertEqual({"ac": 30, "dc": 15}, ret) diff --git a/tests/unit/modules/test_win_psget.py b/tests/unit/modules/test_win_psget.py index 51c0c4d235f..7ea5ce58bc2 100644 --- a/tests/unit/modules/test_win_psget.py +++ b/tests/unit/modules/test_win_psget.py @@ -11,7 +11,7 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -BOOTSTRAP_PS_STR = '''<?xml version="1.0" encoding="utf-8"?> +BOOTSTRAP_PS_STR = """<?xml version="1.0" encoding="utf-8"?> <Objects> <Object Type="System.Management.Automation.PSCustomObject"> <Property Name="Name" Type="System.String">NuGet</Property> @@ -24,9 +24,9 @@ BOOTSTRAP_PS_STR = '''<?xml version="1.0" encoding="utf-8"?> <Property Name="ProviderPath" Type="System.String">C:\\Program Files\\PackageManagement\\ProviderAssemblies\\nuget\\2.8.5 .208\\Microsoft.PackageManagement.NuGetProvider.dll</Property> </Object> -</Objects>''' +</Objects>""" -AVAIL_MODULES_PS_STR = '''<?xml version="1.0" encoding="utf-8"?> +AVAIL_MODULES_PS_STR = """<?xml version="1.0" encoding="utf-8"?> <Objects> <Object Type="System.Management.Automation.PSCustomObject"> <Property Name="Name" Type="System.String">ActOnCmdlets</Property> @@ -44,31 +44,40 @@ AVAIL_MODULES_PS_STR = '''<?xml version="1.0" encoding="utf-8"?> <Property Name="Name" Type="System.String">DHCPMigration</Property> <Property Name="Description" Type="System.String">A module to copy various DHCP information from 1 server to another.</Property> </Object> -</Objects>''' +</Objects>""" class WinPsgetCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_psget - ''' + """ + def setup_loader_modules(self): return {win_psget: {}} def test_bootstrap(self): - mock_read_ok = MagicMock(return_value={'pid': 78, - 'retcode': 0, - 'stderr': '', - 'stdout': BOOTSTRAP_PS_STR}) + mock_read_ok = MagicMock( + return_value={ + "pid": 78, + "retcode": 0, + "stderr": "", + "stdout": BOOTSTRAP_PS_STR, + } + ) - with patch.dict(win_psget.__salt__, {'cmd.run_all': mock_read_ok}): - self.assertTrue('NuGet' in win_psget.bootstrap()) + with patch.dict(win_psget.__salt__, {"cmd.run_all": mock_read_ok}): + self.assertTrue("NuGet" in win_psget.bootstrap()) def test_avail_modules(self): - mock_read_ok = MagicMock(return_value={'pid': 78, - 'retcode': 0, - 'stderr': '', - 'stdout': AVAIL_MODULES_PS_STR}) + mock_read_ok = MagicMock( + return_value={ + "pid": 78, + "retcode": 0, + "stderr": "", + "stdout": AVAIL_MODULES_PS_STR, + } + ) - with patch.dict(win_psget.__salt__, {'cmd.run_all': mock_read_ok}): - self.assertTrue('DHCPMigration' in win_psget.avail_modules(False)) - self.assertTrue('DHCPMigration' in win_psget.avail_modules(True)) + with patch.dict(win_psget.__salt__, {"cmd.run_all": mock_read_ok}): + self.assertTrue("DHCPMigration" in win_psget.avail_modules(False)) + self.assertTrue("DHCPMigration" in win_psget.avail_modules(True)) diff --git a/tests/unit/modules/test_win_service.py b/tests/unit/modules/test_win_service.py index faf8ef88111..eceaef2dd1d 100644 --- a/tests/unit/modules/test_win_service.py +++ b/tests/unit/modules/test_win_service.py @@ -1,23 +1,20 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_service as win_service import salt.utils.path +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + # Import 3rd Party Libs try: WINAPI = True @@ -28,298 +25,337 @@ except ImportError: class WinServiceTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_service - ''' + """ + def setup_loader_modules(self): return {win_service: {}} def test_get_enabled(self): - ''' + """ Test to return the enabled services - ''' - mock = MagicMock(return_value=[{'ServiceName': 'spongebob'}, - {'ServiceName': 'squarepants'}, - {'ServiceName': 'patrick'}]) - with patch.object(win_service, '_get_services', mock): - mock_info = MagicMock(side_effect=[{'StartType': 'Auto'}, - {'StartType': 'Manual'}, - {'StartType': 'Disabled'}]) - with patch.object(win_service, 'info', mock_info): - self.assertListEqual(win_service.get_enabled(), - ['spongebob']) + """ + mock = MagicMock( + return_value=[ + {"ServiceName": "spongebob"}, + {"ServiceName": "squarepants"}, + {"ServiceName": "patrick"}, + ] + ) + with patch.object(win_service, "_get_services", mock): + mock_info = MagicMock( + side_effect=[ + {"StartType": "Auto"}, + {"StartType": "Manual"}, + {"StartType": "Disabled"}, + ] + ) + with patch.object(win_service, "info", mock_info): + self.assertListEqual(win_service.get_enabled(), ["spongebob"]) def test_get_disabled(self): - ''' + """ Test to return the disabled services - ''' - mock = MagicMock(return_value=[{'ServiceName': 'spongebob'}, - {'ServiceName': 'squarepants'}, - {'ServiceName': 'patrick'}]) - with patch.object(win_service, '_get_services', mock): - mock_info = MagicMock(side_effect=[{'StartType': 'Auto'}, - {'StartType': 'Manual'}, - {'StartType': 'Disabled'}]) - with patch.object(win_service, 'info', mock_info): - self.assertListEqual(win_service.get_disabled(), - ['patrick', 'squarepants']) + """ + mock = MagicMock( + return_value=[ + {"ServiceName": "spongebob"}, + {"ServiceName": "squarepants"}, + {"ServiceName": "patrick"}, + ] + ) + with patch.object(win_service, "_get_services", mock): + mock_info = MagicMock( + side_effect=[ + {"StartType": "Auto"}, + {"StartType": "Manual"}, + {"StartType": "Disabled"}, + ] + ) + with patch.object(win_service, "info", mock_info): + self.assertListEqual( + win_service.get_disabled(), ["patrick", "squarepants"] + ) def test_available(self): - ''' + """ Test to Returns ``True`` if the specified service is available, otherwise returns ``False`` - ''' - mock = MagicMock(return_value=['c', 'a', 'b']) - with patch.object(win_service, 'get_all', mock): + """ + mock = MagicMock(return_value=["c", "a", "b"]) + with patch.object(win_service, "get_all", mock): self.assertTrue(win_service.available("a")) def test_missing(self): - ''' + """ Test to the inverse of service.available - ''' - mock = MagicMock(return_value=['c', 'a', 'b']) - with patch.object(win_service, 'get_all', mock): + """ + mock = MagicMock(return_value=["c", "a", "b"]) + with patch.object(win_service, "get_all", mock): self.assertTrue(win_service.missing("d")) def test_get_all(self): - ''' + """ Test to return all installed services - ''' - mock = MagicMock(return_value=[{'ServiceName': 'spongebob'}, - {'ServiceName': 'squarepants'}, - {'ServiceName': 'patrick'}]) - with patch.object(win_service, '_get_services', mock): - self.assertListEqual(win_service.get_all(), - ['patrick', 'spongebob', 'squarepants']) + """ + mock = MagicMock( + return_value=[ + {"ServiceName": "spongebob"}, + {"ServiceName": "squarepants"}, + {"ServiceName": "patrick"}, + ] + ) + with patch.object(win_service, "_get_services", mock): + self.assertListEqual( + win_service.get_all(), ["patrick", "spongebob", "squarepants"] + ) def test_get_service_name(self): - ''' + """ Test to the Display Name is what is displayed in Windows when services.msc is executed. - ''' - mock = MagicMock(return_value=[{'ServiceName': 'spongebob', - 'DisplayName': 'Sponge Bob'}, - {'ServiceName': 'squarepants', - 'DisplayName': 'Square Pants'}, - {'ServiceName': 'patrick', - 'DisplayName': 'Patrick the Starfish'}]) - with patch.object(win_service, '_get_services', mock): - self.assertDictEqual(win_service.get_service_name(), - {'Patrick the Starfish': 'patrick', - 'Sponge Bob': 'spongebob', - 'Square Pants': 'squarepants'}) - self.assertDictEqual(win_service.get_service_name('patrick'), - {'Patrick the Starfish': 'patrick'}) + """ + mock = MagicMock( + return_value=[ + {"ServiceName": "spongebob", "DisplayName": "Sponge Bob"}, + {"ServiceName": "squarepants", "DisplayName": "Square Pants"}, + {"ServiceName": "patrick", "DisplayName": "Patrick the Starfish"}, + ] + ) + with patch.object(win_service, "_get_services", mock): + self.assertDictEqual( + win_service.get_service_name(), + { + "Patrick the Starfish": "patrick", + "Sponge Bob": "spongebob", + "Square Pants": "squarepants", + }, + ) + self.assertDictEqual( + win_service.get_service_name("patrick"), + {"Patrick the Starfish": "patrick"}, + ) - @skipIf(not WINAPI, 'win32serviceutil not available') + @skipIf(not WINAPI, "win32serviceutil not available") def test_start(self): - ''' + """ Test to start the specified service - ''' + """ mock_true = MagicMock(return_value=True) mock_false = MagicMock(return_value=False) - mock_info = MagicMock(side_effect=[{'Status': 'Running'}]) + mock_info = MagicMock(side_effect=[{"Status": "Running"}]) - with patch.object(win32serviceutil, 'StartService', mock_true), \ - patch.object(win_service, 'disabled', mock_false), \ - patch.object(win_service, 'info', mock_info): - self.assertTrue(win_service.start('spongebob')) + with patch.object(win32serviceutil, "StartService", mock_true), patch.object( + win_service, "disabled", mock_false + ), patch.object(win_service, "info", mock_info): + self.assertTrue(win_service.start("spongebob")) - mock_info = MagicMock(side_effect=[{'Status': 'Stopped', 'Status_WaitHint': 0}, - {'Status': 'Start Pending', 'Status_WaitHint': 0}, - {'Status': 'Running'}]) + mock_info = MagicMock( + side_effect=[ + {"Status": "Stopped", "Status_WaitHint": 0}, + {"Status": "Start Pending", "Status_WaitHint": 0}, + {"Status": "Running"}, + ] + ) - with patch.object(win32serviceutil, 'StartService', mock_true), \ - patch.object(win_service, 'disabled', mock_false), \ - patch.object(win_service, 'info', mock_info), \ - patch.object(win_service, 'status', mock_true): - self.assertTrue(win_service.start('spongebob')) + with patch.object(win32serviceutil, "StartService", mock_true), patch.object( + win_service, "disabled", mock_false + ), patch.object(win_service, "info", mock_info), patch.object( + win_service, "status", mock_true + ): + self.assertTrue(win_service.start("spongebob")) - @skipIf(not WINAPI, 'pywintypes not available') + @skipIf(not WINAPI, "pywintypes not available") def test_start_already_running(self): - ''' + """ Test starting a service that is already running - ''' + """ mock_false = MagicMock(return_value=False) mock_error = MagicMock( - side_effect=pywintypes.error(1056, - 'StartService', - 'Service is running')) - mock_info = MagicMock(side_effect=[{'Status': 'Running'}]) - with patch.object(win32serviceutil, 'StartService', mock_error), \ - patch.object(win_service, 'disabled', mock_false), \ - patch.object(win_service, '_status_wait', mock_info): - self.assertTrue(win_service.start('spongebob')) + side_effect=pywintypes.error(1056, "StartService", "Service is running") + ) + mock_info = MagicMock(side_effect=[{"Status": "Running"}]) + with patch.object(win32serviceutil, "StartService", mock_error), patch.object( + win_service, "disabled", mock_false + ), patch.object(win_service, "_status_wait", mock_info): + self.assertTrue(win_service.start("spongebob")) - @skipIf(not WINAPI, 'win32serviceutil not available') + @skipIf(not WINAPI, "win32serviceutil not available") def test_stop(self): - ''' + """ Test to stop the specified service - ''' + """ mock_true = MagicMock(return_value=True) mock_false = MagicMock(return_value=False) - mock_info = MagicMock(side_effect=[{'Status': 'Stopped'}]) + mock_info = MagicMock(side_effect=[{"Status": "Stopped"}]) - with patch.object(win32serviceutil, 'StopService', mock_true), \ - patch.object(win_service, '_status_wait', mock_info): - self.assertTrue(win_service.stop('spongebob')) + with patch.object(win32serviceutil, "StopService", mock_true), patch.object( + win_service, "_status_wait", mock_info + ): + self.assertTrue(win_service.stop("spongebob")) - mock_info = MagicMock(side_effect=[{'Status': 'Running', 'Status_WaitHint': 0}, - {'Status': 'Stop Pending', 'Status_WaitHint': 0}, - {'Status': 'Stopped'}]) + mock_info = MagicMock( + side_effect=[ + {"Status": "Running", "Status_WaitHint": 0}, + {"Status": "Stop Pending", "Status_WaitHint": 0}, + {"Status": "Stopped"}, + ] + ) - with patch.object(win32serviceutil, 'StopService', mock_true), \ - patch.object(win_service, 'info', mock_info), \ - patch.object(win_service, 'status', mock_false): - self.assertTrue(win_service.stop('spongebob')) + with patch.object(win32serviceutil, "StopService", mock_true), patch.object( + win_service, "info", mock_info + ), patch.object(win_service, "status", mock_false): + self.assertTrue(win_service.stop("spongebob")) - @skipIf(not WINAPI, 'pywintypes not available') + @skipIf(not WINAPI, "pywintypes not available") def test_stop_not_running(self): - ''' + """ Test stopping a service that is already stopped - ''' + """ mock_error = MagicMock( - side_effect=pywintypes.error(1062, - 'StopService', - 'Service is not running')) - mock_info = MagicMock(side_effect=[{'Status': 'Stopped'}]) - with patch.object(win32serviceutil, 'StopService', mock_error), \ - patch.object(win_service, '_status_wait', mock_info): - self.assertTrue(win_service.stop('spongebob')) + side_effect=pywintypes.error(1062, "StopService", "Service is not running") + ) + mock_info = MagicMock(side_effect=[{"Status": "Stopped"}]) + with patch.object(win32serviceutil, "StopService", mock_error), patch.object( + win_service, "_status_wait", mock_info + ): + self.assertTrue(win_service.stop("spongebob")) def test_restart(self): - ''' + """ Test to restart the named service - ''' + """ mock_true = MagicMock(return_value=True) - with patch.object(win_service, 'create_win_salt_restart_task', - mock_true): - with patch.object(win_service, 'execute_salt_restart_task', - mock_true): + with patch.object(win_service, "create_win_salt_restart_task", mock_true): + with patch.object(win_service, "execute_salt_restart_task", mock_true): self.assertTrue(win_service.restart("salt-minion")) - with patch.object(win_service, 'stop', mock_true): - with patch.object(win_service, 'start', mock_true): + with patch.object(win_service, "stop", mock_true): + with patch.object(win_service, "start", mock_true): self.assertTrue(win_service.restart("salt")) def test_createwin_saltrestart_task(self): - ''' + """ Test to create a task in Windows task scheduler to enable restarting the salt-minion - ''' - cmd = salt.utils.path.which('cmd') + """ + cmd = salt.utils.path.which("cmd") mock = MagicMock() - with patch.dict(win_service.__salt__, {'task.create_task': mock}): + with patch.dict(win_service.__salt__, {"task.create_task": mock}): win_service.create_win_salt_restart_task() mock.assert_called_once_with( - action_type='Execute', - arguments='/c ping -n 3 127.0.0.1 && net stop salt-minion && ' - 'net start salt-minion', + action_type="Execute", + arguments="/c ping -n 3 127.0.0.1 && net stop salt-minion && " + "net start salt-minion", cmd=cmd, force=True, - name='restart-salt-minion', - start_date='1975-01-01', - start_time='01:00', - trigger_type='Once', - user_name='System' + name="restart-salt-minion", + start_date="1975-01-01", + start_time="01:00", + trigger_type="Once", + user_name="System", ) def test_execute_salt_restart_task(self): - ''' + """ Test to run the Windows Salt restart task - ''' + """ mock_true = MagicMock(return_value=True) - with patch.dict(win_service.__salt__, {'task.run': mock_true}): + with patch.dict(win_service.__salt__, {"task.run": mock_true}): self.assertTrue(win_service.execute_salt_restart_task()) - @skipIf(not WINAPI, 'win32serviceutil not available') + @skipIf(not WINAPI, "win32serviceutil not available") def test_status(self): - ''' + """ Test to return the status for a service - ''' - mock_info = MagicMock(side_effect=[{'Status': 'Running'}, - {'Status': 'Stop Pending'}, - {'Status': 'Stopped'}]) + """ + mock_info = MagicMock( + side_effect=[ + {"Status": "Running"}, + {"Status": "Stop Pending"}, + {"Status": "Stopped"}, + ] + ) - with patch.object(win_service, 'info', mock_info): - self.assertTrue(win_service.status('spongebob')) - self.assertTrue(win_service.status('patrick')) - self.assertFalse(win_service.status('squidward')) + with patch.object(win_service, "info", mock_info): + self.assertTrue(win_service.status("spongebob")) + self.assertTrue(win_service.status("patrick")) + self.assertFalse(win_service.status("squidward")) def test_getsid(self): - ''' + """ Test to return the sid for this windows service - ''' - mock_info = MagicMock(side_effect=[{'sid': 'S-1-5-80-1956725871...'}, - {'sid': None}]) - with patch.object(win_service, 'info', mock_info): - self.assertEqual(win_service.getsid('spongebob'), - 'S-1-5-80-1956725871...') - self.assertEqual(win_service.getsid('plankton'), None) + """ + mock_info = MagicMock( + side_effect=[{"sid": "S-1-5-80-1956725871..."}, {"sid": None}] + ) + with patch.object(win_service, "info", mock_info): + self.assertEqual(win_service.getsid("spongebob"), "S-1-5-80-1956725871...") + self.assertEqual(win_service.getsid("plankton"), None) def test_enable(self): - ''' + """ Test to enable the named service to start at boot - ''' + """ mock_modify = MagicMock(return_value=True) - mock_info = MagicMock(return_value={'StartType': 'Auto', - 'StartTypeDelayed': False}) - with patch.object(win_service, 'modify', mock_modify): - with patch.object(win_service, 'info', mock_info): - self.assertTrue(win_service.enable('spongebob')) + mock_info = MagicMock( + return_value={"StartType": "Auto", "StartTypeDelayed": False} + ) + with patch.object(win_service, "modify", mock_modify): + with patch.object(win_service, "info", mock_info): + self.assertTrue(win_service.enable("spongebob")) def test_disable(self): - ''' + """ Test to disable the named service to start at boot - ''' + """ mock_modify = MagicMock(return_value=True) - mock_info = MagicMock(return_value={'StartType': 'Disabled'}) - with patch.object(win_service, 'modify', mock_modify): - with patch.object(win_service, 'info', mock_info): - self.assertTrue(win_service.disable('spongebob')) + mock_info = MagicMock(return_value={"StartType": "Disabled"}) + with patch.object(win_service, "modify", mock_modify): + with patch.object(win_service, "info", mock_info): + self.assertTrue(win_service.disable("spongebob")) def test_enabled(self): - ''' + """ Test to check to see if the named service is enabled to start on boot - ''' - mock = MagicMock(side_effect=[{'StartType': 'Auto'}, - {'StartType': 'Disabled'}]) - with patch.object(win_service, 'info', mock): - self.assertTrue(win_service.enabled('spongebob')) - self.assertFalse(win_service.enabled('squarepants')) + """ + mock = MagicMock(side_effect=[{"StartType": "Auto"}, {"StartType": "Disabled"}]) + with patch.object(win_service, "info", mock): + self.assertTrue(win_service.enabled("spongebob")) + self.assertFalse(win_service.enabled("squarepants")) def test_enabled_with_space_in_name(self): - ''' + """ Test to check to see if the named service is enabled to start on boot when have space in service name - ''' - mock = MagicMock(side_effect=[{'StartType': 'Auto'}, - {'StartType': 'Disabled'}]) - with patch.object(win_service, 'info', mock): - self.assertTrue(win_service.enabled('spongebob test')) - self.assertFalse(win_service.enabled('squarepants test')) + """ + mock = MagicMock(side_effect=[{"StartType": "Auto"}, {"StartType": "Disabled"}]) + with patch.object(win_service, "info", mock): + self.assertTrue(win_service.enabled("spongebob test")) + self.assertFalse(win_service.enabled("squarepants test")) def test_disabled(self): - ''' + """ Test to check to see if the named service is disabled to start on boot - ''' + """ mock = MagicMock(side_effect=[False, True]) - with patch.object(win_service, 'enabled', mock): - self.assertTrue(win_service.disabled('spongebob')) - self.assertFalse(win_service.disabled('squarepants')) + with patch.object(win_service, "enabled", mock): + self.assertTrue(win_service.disabled("spongebob")) + self.assertFalse(win_service.disabled("squarepants")) def test_cmd_quote(self): - ''' + """ Make sure the command gets quoted correctly - ''' + """ # Should always return command wrapped in double quotes expected = r'"C:\Program Files\salt\test.exe"' # test no quotes - bin_path = r'C:\Program Files\salt\test.exe' + bin_path = r"C:\Program Files\salt\test.exe" self.assertEqual(win_service._cmd_quote(bin_path), expected) # test single quotes @@ -327,9 +363,9 @@ class WinServiceTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(win_service._cmd_quote(bin_path), expected) # test double quoted single quotes - bin_path = '"\'C:\\Program Files\\salt\\test.exe\'"' + bin_path = "\"'C:\\Program Files\\salt\\test.exe'\"" self.assertEqual(win_service._cmd_quote(bin_path), expected) # test single quoted, double quoted, single quotes - bin_path = "\'\"\'C:\\Program Files\\salt\\test.exe\'\"\'" + bin_path = "'\"'C:\\Program Files\\salt\\test.exe'\"'" self.assertEqual(win_service._cmd_quote(bin_path), expected) diff --git a/tests/unit/modules/test_win_shadow.py b/tests/unit/modules/test_win_shadow.py index b0548bc4810..5f37c9eff1a 100644 --- a/tests/unit/modules/test_win_shadow.py +++ b/tests/unit/modules/test_win_shadow.py @@ -1,33 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_shadow as win_shadow +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class WinShadowTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_shadow - ''' + """ + def setup_loader_modules(self): return { win_shadow: { - '__salt__': { -# 'user.info': MagicMock(return_value=True), - 'user.update': MagicMock(return_value=True) + "__salt__": { + # 'user.info': MagicMock(return_value=True), + "user.update": MagicMock(return_value=True) } } } @@ -35,32 +33,38 @@ class WinShadowTestCase(TestCase, LoaderModuleMockMixin): # 'info' function tests: 1 def test_info(self): - ''' + """ Test if it return information for the specified user - ''' - mock_user_info = MagicMock(return_value={'name': 'SALT', - 'password_changed': '', - 'expiration_date': ''}) - with patch.dict(win_shadow.__salt__, {'user.info': mock_user_info}): - self.assertDictEqual(win_shadow.info('SALT'), {'name': 'SALT', - 'passwd': 'Unavailable', - 'lstchg': '', - 'min': '', - 'max': '', - 'warn': '', - 'inact': '', - 'expire': ''}) + """ + mock_user_info = MagicMock( + return_value={"name": "SALT", "password_changed": "", "expiration_date": ""} + ) + with patch.dict(win_shadow.__salt__, {"user.info": mock_user_info}): + self.assertDictEqual( + win_shadow.info("SALT"), + { + "name": "SALT", + "passwd": "Unavailable", + "lstchg": "", + "min": "", + "max": "", + "warn": "", + "inact": "", + "expire": "", + }, + ) # 'set_password' function tests: 1 def test_set_password(self): - ''' + """ Test if it set the password for a named user. - ''' - mock_cmd = MagicMock(return_value={'retcode': False}) - mock_user_info = MagicMock(return_value={'name': 'SALT', - 'password_changed': '', - 'expiration_date': ''}) - with patch.dict(win_shadow.__salt__, {'cmd.run_all': mock_cmd, - 'user.info': mock_user_info}): - self.assertTrue(win_shadow.set_password('root', 'mysecretpassword')) + """ + mock_cmd = MagicMock(return_value={"retcode": False}) + mock_user_info = MagicMock( + return_value={"name": "SALT", "password_changed": "", "expiration_date": ""} + ) + with patch.dict( + win_shadow.__salt__, {"cmd.run_all": mock_cmd, "user.info": mock_user_info} + ): + self.assertTrue(win_shadow.set_password("root", "mysecretpassword")) diff --git a/tests/unit/modules/test_win_snmp.py b/tests/unit/modules/test_win_snmp.py index dfdfe942ab8..c9dc5b9a870 100644 --- a/tests/unit/modules/test_win_snmp.py +++ b/tests/unit/modules/test_win_snmp.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for Windows SNMP Module 'module.win_snmp' :platform: Windows :maturity: develop .. versionadded:: 2017.7.0 -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_snmp as win_snmp @@ -15,100 +15,107 @@ from salt.exceptions import CommandExecutionError # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) -COMMUNITY_NAMES = {'TestCommunity': 'Read Create'} +COMMUNITY_NAMES = {"TestCommunity": "Read Create"} class WinSnmpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_snmp - ''' + """ + def setup_loader_modules(self): return {win_snmp: {}} def test_get_agent_service_types(self): - ''' + """ Test - Get the sysServices types that can be configured. - ''' + """ with patch.dict(win_snmp.__salt__): self.assertIsInstance(win_snmp.get_agent_service_types(), list) def test_get_permission_types(self): - ''' + """ Test - Get the permission types that can be configured for communities. - ''' + """ with patch.dict(win_snmp.__salt__): self.assertIsInstance(win_snmp.get_permission_types(), list) def test_get_auth_traps_enabled(self): - ''' + """ Test - Determine whether the host is configured to send authentication traps. - ''' - mock_value = MagicMock(return_value={'vdata': 1}) - with patch.dict(win_snmp.__salt__, {'reg.read_value': mock_value}): + """ + mock_value = MagicMock(return_value={"vdata": 1}) + with patch.dict(win_snmp.__salt__, {"reg.read_value": mock_value}): self.assertTrue(win_snmp.get_auth_traps_enabled()) def test_set_auth_traps_enabled(self): - ''' + """ Test - Manage the sending of authentication traps. - ''' + """ mock_value = MagicMock(return_value=True) - kwargs = {'status': True} - with patch.dict(win_snmp.__salt__, {'reg.set_value': mock_value}), \ - patch('salt.modules.win_snmp.get_auth_traps_enabled', - MagicMock(return_value=True)): + kwargs = {"status": True} + with patch.dict(win_snmp.__salt__, {"reg.set_value": mock_value}), patch( + "salt.modules.win_snmp.get_auth_traps_enabled", MagicMock(return_value=True) + ): self.assertTrue(win_snmp.set_auth_traps_enabled(**kwargs)) def test_get_community_names(self): - ''' + """ Test - Get the current accepted SNMP community names and their permissions. - ''' - mock_ret = MagicMock(return_value=[{'vdata': 16, - 'vname': 'TestCommunity'}]) + """ + mock_ret = MagicMock(return_value=[{"vdata": 16, "vname": "TestCommunity"}]) mock_false = MagicMock(return_value=False) - with patch.dict(win_snmp.__salt__, {'reg.list_values': mock_ret, - 'reg.key_exists': mock_false}): - self.assertEqual(win_snmp.get_community_names(), - COMMUNITY_NAMES) + with patch.dict( + win_snmp.__salt__, + {"reg.list_values": mock_ret, "reg.key_exists": mock_false}, + ): + self.assertEqual(win_snmp.get_community_names(), COMMUNITY_NAMES) def test_get_community_names_gpo(self): - ''' + """ Test - Get the current accepted SNMP community names and their permissions. - ''' - mock_ret = MagicMock(return_value=[{'vdata': 'TestCommunity', - 'vname': 1}]) + """ + mock_ret = MagicMock(return_value=[{"vdata": "TestCommunity", "vname": 1}]) mock_false = MagicMock(return_value=True) - with patch.dict(win_snmp.__salt__, {'reg.list_values': mock_ret, - 'reg.key_exists': mock_false}): - self.assertEqual(win_snmp.get_community_names(), - {'TestCommunity': 'Managed by GPO'}) + with patch.dict( + win_snmp.__salt__, + {"reg.list_values": mock_ret, "reg.key_exists": mock_false}, + ): + self.assertEqual( + win_snmp.get_community_names(), {"TestCommunity": "Managed by GPO"} + ) def test_set_community_names(self): - ''' + """ Test - Manage the SNMP accepted community names and their permissions. - ''' + """ mock_true = MagicMock(return_value=True) - kwargs = {'communities': COMMUNITY_NAMES} + kwargs = {"communities": COMMUNITY_NAMES} mock_false = MagicMock(return_value=False) - with patch.dict(win_snmp.__salt__, {'reg.set_value': mock_true, - 'reg.key_exists': mock_false}), \ - patch('salt.modules.win_snmp.get_community_names', - MagicMock(return_value=COMMUNITY_NAMES)): + with patch.dict( + win_snmp.__salt__, + {"reg.set_value": mock_true, "reg.key_exists": mock_false}, + ), patch( + "salt.modules.win_snmp.get_community_names", + MagicMock(return_value=COMMUNITY_NAMES), + ): self.assertTrue(win_snmp.set_community_names(**kwargs)) def test_set_community_names_gpo(self): - ''' + """ Test - Manage the SNMP accepted community names and their permissions. - ''' + """ mock_true = MagicMock(return_value=True) - kwargs = {'communities': COMMUNITY_NAMES} - with patch.dict(win_snmp.__salt__, {'reg.set_value': mock_true, - 'reg.key_exists': mock_true}), \ - patch('salt.modules.win_snmp.get_community_names', - MagicMock(return_value=COMMUNITY_NAMES)): - self.assertRaises(CommandExecutionError, win_snmp.set_community_names, **kwargs) + kwargs = {"communities": COMMUNITY_NAMES} + with patch.dict( + win_snmp.__salt__, {"reg.set_value": mock_true, "reg.key_exists": mock_true} + ), patch( + "salt.modules.win_snmp.get_community_names", + MagicMock(return_value=COMMUNITY_NAMES), + ): + self.assertRaises( + CommandExecutionError, win_snmp.set_community_names, **kwargs + ) diff --git a/tests/unit/modules/test_win_status.py b/tests/unit/modules/test_win_status.py index f52c3fd7558..79b411dda75 100644 --- a/tests/unit/modules/test_win_status.py +++ b/tests/unit/modules/test_win_status.py @@ -1,43 +1,43 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import sys +# This is imported late so mock can do its job +import salt.modules.win_status as status + # Import Salt libs from salt.ext import six +from tests.support.mock import ANY, Mock, patch # Import Salt Testing libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import Mock, patch, ANY +from tests.support.unit import TestCase, skipIf try: import wmi except ImportError: pass -# This is imported late so mock can do its job -import salt.modules.win_status as status - -@skipIf(status.HAS_WMI is False, 'This test requires Windows') +@skipIf(status.HAS_WMI is False, "This test requires Windows") class TestProcsBase(TestCase): def __init__(self, *args, **kwargs): TestCase.__init__(self, *args, **kwargs) self.__processes = [] def add_process( - self, - pid=100, - cmd='cmd', - name='name', - user='user', - user_domain='domain', - get_owner_result=0): + self, + pid=100, + cmd="cmd", + name="name", + user="user", + user_domain="domain", + get_owner_result=0, + ): process = Mock() - process.GetOwner = Mock( - return_value=(user_domain, get_owner_result, user) - ) + process.GetOwner = Mock(return_value=(user_domain, get_owner_result, user)) process.ProcessId = pid process.CommandLine = cmd process.Name = name @@ -46,7 +46,7 @@ class TestProcsBase(TestCase): def call_procs(self): WMI = Mock() WMI.win32_process = Mock(return_value=self.__processes) - with patch.object(wmi, 'WMI', Mock(return_value=WMI)): + with patch.object(wmi, "WMI", Mock(return_value=WMI)): self.result = status.procs() @@ -65,76 +65,81 @@ class TestProcsCount(TestProcsBase): class TestProcsAttributes(TestProcsBase): def setUp(self): - self._expected_name = 'name' - self._expected_cmd = 'cmd' - self._expected_user = 'user' - self._expected_domain = 'domain' + self._expected_name = "name" + self._expected_cmd = "cmd" + self._expected_user = "user" + self._expected_domain = "domain" pid = 100 self.add_process( pid=pid, cmd=self._expected_cmd, user=self._expected_user, user_domain=self._expected_domain, - get_owner_result=0) + get_owner_result=0, + ) self.call_procs() self.proc = self.result[pid] def test_process_cmd_is_set(self): - self.assertEqual(self.proc['cmd'], self._expected_cmd) + self.assertEqual(self.proc["cmd"], self._expected_cmd) def test_process_name_is_set(self): - self.assertEqual(self.proc['name'], self._expected_name) + self.assertEqual(self.proc["name"], self._expected_name) def test_process_user_is_set(self): - self.assertEqual(self.proc['user'], self._expected_user) + self.assertEqual(self.proc["user"], self._expected_user) def test_process_user_domain_is_set(self): - self.assertEqual(self.proc['user_domain'], self._expected_domain) + self.assertEqual(self.proc["user_domain"], self._expected_domain) -@skipIf(sys.stdin.encoding != 'UTF-8', 'UTF-8 encoding required for this test is not supported') +@skipIf( + sys.stdin.encoding != "UTF-8", + "UTF-8 encoding required for this test is not supported", +) class TestProcsUnicodeAttributes(TestProcsBase): def setUp(self): - unicode_str = u'\xc1' - self.ustr = unicode_str.encode('utf8') if six.PY2 else unicode_str + unicode_str = "\xc1" + self.ustr = unicode_str.encode("utf8") if six.PY2 else unicode_str pid = 100 self.add_process( pid=pid, user=unicode_str, user_domain=unicode_str, cmd=unicode_str, - name=unicode_str) + name=unicode_str, + ) self.call_procs() self.proc = self.result[pid] def test_process_cmd_is_utf8(self): - self.assertEqual(self.proc['cmd'], self.ustr) + self.assertEqual(self.proc["cmd"], self.ustr) def test_process_name_is_utf8(self): - self.assertEqual(self.proc['name'], self.ustr) + self.assertEqual(self.proc["name"], self.ustr) def test_process_user_is_utf8(self): - self.assertEqual(self.proc['user'], self.ustr) + self.assertEqual(self.proc["user"], self.ustr) def test_process_user_domain_is_utf8(self): - self.assertEqual(self.proc['user_domain'], self.ustr) + self.assertEqual(self.proc["user_domain"], self.ustr) class TestProcsWMIGetOwnerAccessDeniedWorkaround(TestProcsBase): def setUp(self): - self.expected_user = 'SYSTEM' - self.expected_domain = 'NT AUTHORITY' + self.expected_user = "SYSTEM" + self.expected_domain = "NT AUTHORITY" self.add_process(pid=0, get_owner_result=2) self.add_process(pid=4, get_owner_result=2) self.call_procs() def test_user_is_set(self): - self.assertEqual(self.result[0]['user'], self.expected_user) - self.assertEqual(self.result[4]['user'], self.expected_user) + self.assertEqual(self.result[0]["user"], self.expected_user) + self.assertEqual(self.result[4]["user"], self.expected_user) def test_process_user_domain_is_set(self): - self.assertEqual(self.result[0]['user_domain'], self.expected_domain) - self.assertEqual(self.result[4]['user_domain'], self.expected_domain) + self.assertEqual(self.result[0]["user_domain"], self.expected_domain) + self.assertEqual(self.result[4]["user_domain"], self.expected_domain) class TestProcsWMIGetOwnerErrorsAreLogged(TestProcsBase): @@ -143,7 +148,7 @@ class TestProcsWMIGetOwnerErrorsAreLogged(TestProcsBase): self.add_process(get_owner_result=self.expected_error_code) def test_error_logged_if_process_get_owner_fails(self): - with patch('salt.modules.win_status.log') as log: + with patch("salt.modules.win_status.log") as log: self.call_procs() log.warning.assert_called_once_with(ANY, ANY, self.expected_error_code) @@ -157,10 +162,10 @@ class TestEmptyCommandLine(TestProcsBase): self.proc = self.result[pid] def test_cmd_is_empty_string(self): - self.assertEqual(self.proc['cmd'], '') + self.assertEqual(self.proc["cmd"], "") -#class TestProcsComInitialization(TestProcsBase): +# class TestProcsComInitialization(TestProcsBase): # def setUp(self): # call_count = 5 # for _ in range(call_count): diff --git a/tests/unit/modules/test_win_system.py b/tests/unit/modules/test_win_system.py index 13c62919e35..d1f19350fb2 100644 --- a/tests/unit/modules/test_win_system.py +++ b/tests/unit/modules/test_win_system.py @@ -1,231 +1,269 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import types from datetime import datetime -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.win_system as win_system import salt.utils.platform +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinSystemTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_system - ''' + """ + def setup_loader_modules(self): modules_globals = {} if win_system.HAS_WIN32NET_MODS is False: win32api = types.ModuleType( - str('win32api') # future lint: disable=blacklisted-function + str("win32api") # future lint: disable=blacklisted-function ) now = datetime.now() - win32api.GetLocalTime = MagicMock(return_value=[now.year, now.month, now.weekday(), - now.day, now.hour, now.minute, - now.second, now.microsecond]) - modules_globals['win32api'] = win32api - win32net = types.ModuleType(str('win32net')) # future lint: disable=blacklisted-function + win32api.GetLocalTime = MagicMock( + return_value=[ + now.year, + now.month, + now.weekday(), + now.day, + now.hour, + now.minute, + now.second, + now.microsecond, + ] + ) + modules_globals["win32api"] = win32api + win32net = types.ModuleType( + str("win32net") + ) # future lint: disable=blacklisted-function win32net.NetServerGetInfo = MagicMock() win32net.NetServerSetInfo = MagicMock() - modules_globals['win32net'] = win32net + modules_globals["win32net"] = win32net return {win_system: modules_globals} def test_halt(self): - ''' + """ Test to halt a running system - ''' - mock = MagicMock(return_value='salt') - with patch.object(win_system, 'shutdown', mock): - self.assertEqual(win_system.halt(), 'salt') + """ + mock = MagicMock(return_value="salt") + with patch.object(win_system, "shutdown", mock): + self.assertEqual(win_system.halt(), "salt") def test_init(self): - ''' + """ Test to change the system runlevel on sysV compatible systems - ''' - self.assertEqual(win_system.init(3), - 'Not implemented on Windows at this time.') + """ + self.assertEqual(win_system.init(3), "Not implemented on Windows at this time.") - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_poweroff(self): - ''' + """ Test to poweroff a running system - ''' - mock = MagicMock(return_value='salt') - with patch.object(win_system, 'shutdown', mock): - self.assertEqual(win_system.poweroff(), 'salt') + """ + mock = MagicMock(return_value="salt") + with patch.object(win_system, "shutdown", mock): + self.assertEqual(win_system.poweroff(), "salt") - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_reboot(self): - ''' + """ Test to reboot the system - ''' - with patch('salt.modules.win_system.shutdown', - MagicMock(return_value=True)) as shutdown: + """ + with patch( + "salt.modules.win_system.shutdown", MagicMock(return_value=True) + ) as shutdown: self.assertEqual(win_system.reboot(), True) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_reboot_with_timeout_in_minutes(self): - ''' + """ Test to reboot the system with a timeout - ''' - with patch('salt.modules.win_system.shutdown', - MagicMock(return_value=True)) as shutdown: + """ + with patch( + "salt.modules.win_system.shutdown", MagicMock(return_value=True) + ) as shutdown: self.assertEqual(win_system.reboot(5, in_seconds=False), True) - shutdown.assert_called_with(timeout=5, in_seconds=False, reboot=True, - only_on_pending_reboot=False) + shutdown.assert_called_with( + timeout=5, in_seconds=False, reboot=True, only_on_pending_reboot=False + ) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_reboot_with_timeout_in_seconds(self): - ''' + """ Test to reboot the system with a timeout - ''' - with patch('salt.modules.win_system.shutdown', - MagicMock(return_value=True)) as shutdown: + """ + with patch( + "salt.modules.win_system.shutdown", MagicMock(return_value=True) + ) as shutdown: self.assertEqual(win_system.reboot(5, in_seconds=True), True) - shutdown.assert_called_with(timeout=5, in_seconds=True, reboot=True, - only_on_pending_reboot=False) + shutdown.assert_called_with( + timeout=5, in_seconds=True, reboot=True, only_on_pending_reboot=False + ) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_reboot_with_wait(self): - ''' + """ Test to reboot the system with a timeout and wait for it to finish - ''' - with patch('salt.modules.win_system.shutdown', - MagicMock(return_value=True)), \ - patch('salt.modules.win_system.time.sleep', - MagicMock()) as time: + """ + with patch( + "salt.modules.win_system.shutdown", MagicMock(return_value=True) + ), patch("salt.modules.win_system.time.sleep", MagicMock()) as time: self.assertEqual(win_system.reboot(wait_for_reboot=True), True) time.assert_called_with(330) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_shutdown(self): - ''' + """ Test to shutdown a running system - ''' - with patch('salt.modules.win_system.win32api.InitiateSystemShutdown', - MagicMock()): + """ + with patch( + "salt.modules.win_system.win32api.InitiateSystemShutdown", MagicMock() + ): self.assertEqual(win_system.shutdown(), True) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_shutdown_hard(self): - ''' + """ Test to shutdown a running system with no timeout or warning - ''' - with patch('salt.modules.win_system.shutdown', - MagicMock(return_value=True)) as shutdown: + """ + with patch( + "salt.modules.win_system.shutdown", MagicMock(return_value=True) + ) as shutdown: self.assertEqual(win_system.shutdown_hard(), True) shutdown.assert_called_with(timeout=0) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_set_computer_name(self): - ''' + """ Test to set the Windows computer name - ''' - with patch('salt.modules.win_system.windll.kernel32.SetComputerNameExW', - MagicMock(return_value=True)): - with patch.object(win_system, 'get_computer_name', - MagicMock(return_value='salt')): - with patch.object(win_system, 'get_pending_computer_name', - MagicMock(return_value='salt_new')): - self.assertDictEqual(win_system.set_computer_name("salt_new"), - {'Computer Name': {'Current': 'salt', - 'Pending': 'salt_new'}}) + """ + with patch( + "salt.modules.win_system.windll.kernel32.SetComputerNameExW", + MagicMock(return_value=True), + ): + with patch.object( + win_system, "get_computer_name", MagicMock(return_value="salt") + ): + with patch.object( + win_system, + "get_pending_computer_name", + MagicMock(return_value="salt_new"), + ): + self.assertDictEqual( + win_system.set_computer_name("salt_new"), + {"Computer Name": {"Current": "salt", "Pending": "salt_new"}}, + ) # Test set_computer_name failure - with patch('salt.modules.win_system.windll.kernel32.SetComputerNameExW', - MagicMock(return_value=False)): + with patch( + "salt.modules.win_system.windll.kernel32.SetComputerNameExW", + MagicMock(return_value=False), + ): self.assertFalse(win_system.set_computer_name("salt")) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_get_pending_computer_name(self): - ''' + """ Test to get a pending computer name. - ''' - with patch.object(win_system, 'get_computer_name', - MagicMock(return_value='salt')): - reg_mock = MagicMock(return_value={'vdata': 'salt'}) - with patch.dict(win_system.__salt__, {'reg.read_value': reg_mock}): + """ + with patch.object( + win_system, "get_computer_name", MagicMock(return_value="salt") + ): + reg_mock = MagicMock(return_value={"vdata": "salt"}) + with patch.dict(win_system.__salt__, {"reg.read_value": reg_mock}): self.assertFalse(win_system.get_pending_computer_name()) - reg_mock = MagicMock(return_value={'vdata': 'salt_pending'}) - with patch.dict(win_system.__salt__, {'reg.read_value': reg_mock}): - self.assertEqual(win_system.get_pending_computer_name(), - 'salt_pending') + reg_mock = MagicMock(return_value={"vdata": "salt_pending"}) + with patch.dict(win_system.__salt__, {"reg.read_value": reg_mock}): + self.assertEqual(win_system.get_pending_computer_name(), "salt_pending") - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_get_computer_name(self): - ''' + """ Test to get the Windows computer name - ''' - with patch('salt.modules.win_system.win32api.GetComputerNameEx', - MagicMock(side_effect=['computer name', ''])): - self.assertEqual(win_system.get_computer_name(), 'computer name') + """ + with patch( + "salt.modules.win_system.win32api.GetComputerNameEx", + MagicMock(side_effect=["computer name", ""]), + ): + self.assertEqual(win_system.get_computer_name(), "computer name") self.assertFalse(win_system.get_computer_name()) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_set_computer_desc(self): - ''' + """ Test to set the Windows computer description - ''' + """ mock = MagicMock() - mock_get_info = MagicMock(return_value={'comment': ''}) + mock_get_info = MagicMock(return_value={"comment": ""}) mock_get_desc = MagicMock(return_value="Salt's comp") - with patch('salt.modules.win_system.win32net.NetServerGetInfo', mock_get_info), \ - patch('salt.modules.win_system.win32net.NetServerSetInfo', mock), \ - patch.object(win_system, 'get_computer_desc', mock_get_desc): + with patch( + "salt.modules.win_system.win32net.NetServerGetInfo", mock_get_info + ), patch( + "salt.modules.win_system.win32net.NetServerSetInfo", mock + ), patch.object( + win_system, "get_computer_desc", mock_get_desc + ): self.assertDictEqual( win_system.set_computer_desc("Salt's comp"), - {'Computer Description': "Salt's comp"}) + {"Computer Description": "Salt's comp"}, + ) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_get_computer_desc(self): - ''' + """ Test to get the Windows computer description - ''' - with patch('salt.modules.win_system.get_system_info', - MagicMock(side_effect=[{'description': 'salt description'}, - {'description': None}])): - self.assertEqual(win_system.get_computer_desc(), 'salt description') + """ + with patch( + "salt.modules.win_system.get_system_info", + MagicMock( + side_effect=[{"description": "salt description"}, {"description": None}] + ), + ): + self.assertEqual(win_system.get_computer_desc(), "salt description") self.assertFalse(win_system.get_computer_desc()) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_join_domain(self): - ''' + """ Test to join a computer to an Active Directory domain - ''' - with patch('salt.modules.win_system._join_domain', - MagicMock(return_value=0)): - with patch('salt.modules.win_system.get_domain_workgroup', - MagicMock(return_value={'Workgroup': 'Workgroup'})): + """ + with patch("salt.modules.win_system._join_domain", MagicMock(return_value=0)): + with patch( + "salt.modules.win_system.get_domain_workgroup", + MagicMock(return_value={"Workgroup": "Workgroup"}), + ): self.assertDictEqual( - win_system.join_domain( - "saltstack", "salt", "salt@123"), - {'Domain': 'saltstack', 'Restart': False}) + win_system.join_domain("saltstack", "salt", "salt@123"), + {"Domain": "saltstack", "Restart": False}, + ) - with patch('salt.modules.win_system.get_domain_workgroup', - MagicMock(return_value={'Domain': 'saltstack'})): + with patch( + "salt.modules.win_system.get_domain_workgroup", + MagicMock(return_value={"Domain": "saltstack"}), + ): self.assertEqual( win_system.join_domain("saltstack", "salt", "salt@123"), - 'Already joined to saltstack') + "Already joined to saltstack", + ) def test_get_system_time(self): - ''' + """ Test to get system time - ''' + """ tm = datetime.strftime(datetime.now(), "%I:%M:%S %p") win_tm = win_system.get_system_time() try: @@ -233,108 +271,167 @@ class WinSystemTestCase(TestCase, LoaderModuleMockMixin): except AssertionError: # handle race condition import re - self.assertTrue(re.search(r'^\d{2}:\d{2} \w{2}$', win_tm)) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + self.assertTrue(re.search(r"^\d{2}:\d{2} \w{2}$", win_tm)) + + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_set_system_time(self): - ''' + """ Test to set system time - ''' - with patch('salt.modules.win_system.set_system_date_time', - MagicMock(side_effect=[False, True])): + """ + with patch( + "salt.modules.win_system.set_system_date_time", + MagicMock(side_effect=[False, True]), + ): self.assertFalse(win_system.set_system_time("11:31:15 AM")) self.assertTrue(win_system.set_system_time("11:31:15 AM")) def test_get_system_date(self): - ''' + """ Test to get system date - ''' + """ date = datetime.strftime(datetime.now(), "%m/%d/%Y") self.assertEqual(win_system.get_system_date(), date) - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_set_system_date(self): - ''' + """ Test to set system date - ''' - with patch('salt.modules.win_system.set_system_date_time', - MagicMock(side_effect=[False, True])): + """ + with patch( + "salt.modules.win_system.set_system_date_time", + MagicMock(side_effect=[False, True]), + ): self.assertFalse(win_system.set_system_date("03-28-13")) self.assertTrue(win_system.set_system_date("03-28-13")) def test_start_time_service(self): - ''' + """ Test to start the Windows time service - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(win_system.__salt__, {'service.start': mock}): + with patch.dict(win_system.__salt__, {"service.start": mock}): self.assertTrue(win_system.start_time_service()) def test_stop_time_service(self): - ''' + """ Test to stop the windows time service - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(win_system.__salt__, {'service.stop': mock}): + with patch.dict(win_system.__salt__, {"service.stop": mock}): self.assertTrue(win_system.stop_time_service()) def test_set_hostname(self): - ''' + """ Test setting a new hostname - ''' + """ cmd_run_mock = MagicMock(return_value="Method execution successful.") get_hostname = MagicMock(return_value="MINION") - with patch.dict(win_system.__salt__, {'cmd.run': cmd_run_mock}): - with patch.object(win_system, 'get_hostname', get_hostname): + with patch.dict(win_system.__salt__, {"cmd.run": cmd_run_mock}): + with patch.object(win_system, "get_hostname", get_hostname): win_system.set_hostname("NEW") - cmd_run_mock.assert_called_once_with(cmd="wmic computersystem where name='MINION' call rename name='NEW'") + cmd_run_mock.assert_called_once_with( + cmd="wmic computersystem where name='MINION' call rename name='NEW'" + ) def test_get_hostname(self): - ''' + """ Test setting a new hostname - ''' + """ cmd_run_mock = MagicMock(return_value="MINION") - with patch.dict(win_system.__salt__, {'cmd.run': cmd_run_mock}): + with patch.dict(win_system.__salt__, {"cmd.run": cmd_run_mock}): ret = win_system.get_hostname() self.assertEqual(ret, "MINION") cmd_run_mock.assert_called_once_with(cmd="hostname") - @skipIf(not win_system.HAS_WIN32NET_MODS, 'Missing win32 libraries') + @skipIf(not win_system.HAS_WIN32NET_MODS, "Missing win32 libraries") def test_get_system_info(self): - fields = ['bios_caption', 'bios_description', 'bios_details', - 'bios_manufacturer', 'bios_version', 'bootup_state', - 'caption', 'chassis_bootup_state', 'chassis_sku_number', - 'description', 'dns_hostname', 'domain', 'domain_role', - 'hardware_manufacturer', 'hardware_model', 'hardware_serial', - 'install_date', 'last_boot', 'name', - 'network_server_mode_enabled', 'organization', - 'os_architecture', 'os_manufacturer', 'os_name', 'os_type', - 'os_version', 'part_of_domain', 'pc_system_type', - 'power_state', 'primary', 'processor_cores', - 'processor_cores_enabled', 'processor_manufacturer', - 'processor_max_clock_speed', 'processors', - 'processors_logical', 'registered_user', 'status', - 'system_directory', 'system_drive', 'system_type', - 'thermal_state', 'total_physical_memory', - 'total_physical_memory_raw', 'users', 'windows_directory', - 'workgroup'] + fields = [ + "bios_caption", + "bios_description", + "bios_details", + "bios_manufacturer", + "bios_version", + "bootup_state", + "caption", + "chassis_bootup_state", + "chassis_sku_number", + "description", + "dns_hostname", + "domain", + "domain_role", + "hardware_manufacturer", + "hardware_model", + "hardware_serial", + "install_date", + "last_boot", + "name", + "network_server_mode_enabled", + "organization", + "os_architecture", + "os_manufacturer", + "os_name", + "os_type", + "os_version", + "part_of_domain", + "pc_system_type", + "power_state", + "primary", + "processor_cores", + "processor_cores_enabled", + "processor_manufacturer", + "processor_max_clock_speed", + "processors", + "processors_logical", + "registered_user", + "status", + "system_directory", + "system_drive", + "system_type", + "thermal_state", + "total_physical_memory", + "total_physical_memory_raw", + "users", + "windows_directory", + "workgroup", + ] ret = win_system.get_system_info() # Make sure all the fields are in the return for field in fields: self.assertIn(field, ret) # os_type - os_types = ['Work Station', 'Domain Controller', 'Server'] - self.assertIn(ret['os_type'], os_types) - domain_roles = ['Standalone Workstation', 'Member Workstation', - 'Standalone Server', 'Member Server', - 'Backup Domain Controller', 'Primary Domain Controller'] - self.assertIn(ret['domain_role'], domain_roles) - system_types = ['Unspecified', 'Desktop', 'Mobile', 'Workstation', - 'Enterprise Server', 'SOHO Server', 'Appliance PC', - 'Performance Server', 'Slate', 'Maximum'] - self.assertIn(ret['pc_system_type'], system_types) - warning_states = ['Other', 'Unknown', 'Safe', 'Warning', 'Critical', - 'Non-recoverable'] - self.assertIn(ret['chassis_bootup_state'], warning_states) - self.assertIn(ret['thermal_state'], warning_states) + os_types = ["Work Station", "Domain Controller", "Server"] + self.assertIn(ret["os_type"], os_types) + domain_roles = [ + "Standalone Workstation", + "Member Workstation", + "Standalone Server", + "Member Server", + "Backup Domain Controller", + "Primary Domain Controller", + ] + self.assertIn(ret["domain_role"], domain_roles) + system_types = [ + "Unspecified", + "Desktop", + "Mobile", + "Workstation", + "Enterprise Server", + "SOHO Server", + "Appliance PC", + "Performance Server", + "Slate", + "Maximum", + ] + self.assertIn(ret["pc_system_type"], system_types) + warning_states = [ + "Other", + "Unknown", + "Safe", + "Warning", + "Critical", + "Non-recoverable", + ] + self.assertIn(ret["chassis_bootup_state"], warning_states) + self.assertIn(ret["thermal_state"], warning_states) diff --git a/tests/unit/modules/test_win_task.py b/tests/unit/modules/test_win_task.py index f8d278fc447..1de3e7cc824 100644 --- a/tests/unit/modules/test_win_task.py +++ b/tests/unit/modules/test_win_task.py @@ -1,60 +1,64 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.helpers import destructiveTest - +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.modules.win_task as win_task import salt.utils.platform +from tests.support.helpers import destructiveTest + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf @destructiveTest -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinTaskTestCase(TestCase): - ''' + """ Test cases for salt.modules.win_task - ''' + """ + def test_repeat_interval(self): - task_name = 'SaltTest1' + task_name = "SaltTest1" try: - ret = win_task.create_task(task_name, - user_name='System', - force=True, - action_type='Execute', - cmd='c:\\salt\\salt-call.bat', - trigger_type='Daily', - trigger_enabled=True, - repeat_duration='30 minutes', - repeat_interval='30 minutes') + ret = win_task.create_task( + task_name, + user_name="System", + force=True, + action_type="Execute", + cmd="c:\\salt\\salt-call.bat", + trigger_type="Daily", + trigger_enabled=True, + repeat_duration="30 minutes", + repeat_interval="30 minutes", + ) self.assertTrue(ret) ret = win_task.info(task_name) - self.assertEqual(ret['triggers'][0]['trigger_type'], 'Daily') + self.assertEqual(ret["triggers"][0]["trigger_type"], "Daily") finally: ret = win_task.delete_task(task_name) self.assertTrue(ret) def test_repeat_interval_and_indefinitely(self): - task_name = 'SaltTest2' + task_name = "SaltTest2" try: - ret = win_task.create_task(task_name, - user_name='System', - force=True, - action_type='Execute', - cmd='c:\\salt\\salt-call.bat', - trigger_type='Daily', - trigger_enabled=True, - repeat_duration='Indefinitely', - repeat_interval='30 minutes') + ret = win_task.create_task( + task_name, + user_name="System", + force=True, + action_type="Execute", + cmd="c:\\salt\\salt-call.bat", + trigger_type="Daily", + trigger_enabled=True, + repeat_duration="Indefinitely", + repeat_interval="30 minutes", + ) self.assertTrue(ret) ret = win_task.info(task_name) - self.assertEqual(ret['triggers'][0]['trigger_type'], 'Daily') + self.assertEqual(ret["triggers"][0]["trigger_type"], "Daily") finally: ret = win_task.delete_task(task_name) self.assertTrue(ret) diff --git a/tests/unit/modules/test_win_timezone.py b/tests/unit/modules/test_win_timezone.py index dc589e7c00e..dfea7b5084f 100644 --- a/tests/unit/modules/test_win_timezone.py +++ b/tests/unit/modules/test_win_timezone.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -14,103 +14,111 @@ from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf -@skipIf(not win_timezone.HAS_PYTZ, 'This test requires pytz') +@skipIf(not win_timezone.HAS_PYTZ, "This test requires pytz") class WinTimezoneTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_timezone - ''' + """ + def setup_loader_modules(self): return {win_timezone: {}} # 'get_zone' function tests: 3 def test_get_zone(self): - ''' + """ Test if it gets current timezone (i.e. Asia/Calcutta) - ''' - mock_read = MagicMock(side_effect=[{'vdata': 'India Standard Time'}, - {'vdata': 'Indian Standard Time'}]) + """ + mock_read = MagicMock( + side_effect=[ + {"vdata": "India Standard Time"}, + {"vdata": "Indian Standard Time"}, + ] + ) - with patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}): - self.assertEqual(win_timezone.get_zone(), 'Asia/Calcutta') - self.assertEqual(win_timezone.get_zone(), 'Unknown') + with patch.dict(win_timezone.__utils__, {"reg.read_value": mock_read}): + self.assertEqual(win_timezone.get_zone(), "Asia/Calcutta") + self.assertEqual(win_timezone.get_zone(), "Unknown") def test_get_zone_null_terminated(self): - ''' + """ Test if it handles instances where the registry contains null values - ''' - mock_read = MagicMock(side_effect=[ - {'vdata': 'India Standard Time\0\0\0\0'}, - {'vdata': 'Indian Standard Time\0\0some more junk data\0\0'}]) + """ + mock_read = MagicMock( + side_effect=[ + {"vdata": "India Standard Time\0\0\0\0"}, + {"vdata": "Indian Standard Time\0\0some more junk data\0\0"}, + ] + ) - with patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}): - self.assertEqual(win_timezone.get_zone(), 'Asia/Calcutta') - self.assertEqual(win_timezone.get_zone(), 'Unknown') + with patch.dict(win_timezone.__utils__, {"reg.read_value": mock_read}): + self.assertEqual(win_timezone.get_zone(), "Asia/Calcutta") + self.assertEqual(win_timezone.get_zone(), "Unknown") # 'get_offset' function tests: 1 def test_get_offset(self): - ''' + """ Test if it get current numeric timezone offset from UCT (i.e. +0530) - ''' - mock_read = MagicMock(return_value={'vdata': 'India Standard Time'}) + """ + mock_read = MagicMock(return_value={"vdata": "India Standard Time"}) - with patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}): - self.assertEqual(win_timezone.get_offset(), '+0530') + with patch.dict(win_timezone.__utils__, {"reg.read_value": mock_read}): + self.assertEqual(win_timezone.get_offset(), "+0530") # 'get_zonecode' function tests: 1 def test_get_zonecode(self): - ''' + """ Test if it get current timezone (i.e. PST, MDT, etc) - ''' - mock_read = MagicMock(return_value={'vdata': 'India Standard Time'}) + """ + mock_read = MagicMock(return_value={"vdata": "India Standard Time"}) - with patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}): - self.assertEqual(win_timezone.get_zonecode(), 'IST') + with patch.dict(win_timezone.__utils__, {"reg.read_value": mock_read}): + self.assertEqual(win_timezone.get_zonecode(), "IST") # 'set_zone' function tests: 1 def test_set_zone(self): - ''' + """ Test if it unlinks, then symlinks /etc/localtime to the set timezone. - ''' - mock_cmd = MagicMock(return_value={'pid': 78, - 'retcode': 0, - 'stderr': '', - 'stdout': ''}) - mock_read = MagicMock(return_value={'vdata': 'India Standard Time'}) + """ + mock_cmd = MagicMock( + return_value={"pid": 78, "retcode": 0, "stderr": "", "stdout": ""} + ) + mock_read = MagicMock(return_value={"vdata": "India Standard Time"}) - with patch.dict(win_timezone.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}): + with patch.dict(win_timezone.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + win_timezone.__utils__, {"reg.read_value": mock_read} + ): - self.assertTrue(win_timezone.set_zone('Asia/Calcutta')) + self.assertTrue(win_timezone.set_zone("Asia/Calcutta")) # 'zone_compare' function tests: 1 def test_zone_compare(self): - ''' + """ Test if it checks the md5sum between the given timezone, and the one set in /etc/localtime. Returns True if they match, and False if not. Mostly useful for running state checks. - ''' - mock_read = MagicMock(return_value={'vdata': 'India Standard Time'}) + """ + mock_read = MagicMock(return_value={"vdata": "India Standard Time"}) - with patch.dict(win_timezone.__utils__, {'reg.read_value': mock_read}): - self.assertTrue(win_timezone.zone_compare('Asia/Calcutta')) + with patch.dict(win_timezone.__utils__, {"reg.read_value": mock_read}): + self.assertTrue(win_timezone.zone_compare("Asia/Calcutta")) # 'get_hwclock' function tests: 1 def test_get_hwclock(self): - ''' + """ Test if it get current hardware clock setting (UTC or localtime) - ''' - self.assertEqual(win_timezone.get_hwclock(), 'localtime') + """ + self.assertEqual(win_timezone.get_hwclock(), "localtime") # 'set_hwclock' function tests: 1 def test_set_hwclock(self): - ''' + """ Test if it sets the hardware clock to be either UTC or localtime - ''' - self.assertFalse(win_timezone.set_hwclock('UTC')) + """ + self.assertFalse(win_timezone.set_hwclock("UTC")) diff --git a/tests/unit/modules/test_win_wusa.py b/tests/unit/modules/test_win_wusa.py index 254e39b0a18..c55e1569c27 100644 --- a/tests/unit/modules/test_win_wusa.py +++ b/tests/unit/modules/test_win_wusa.py @@ -1,209 +1,250 @@ # -*- coding: utf-8 -*- -''' +""" Test the win_wusa execution module -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import patch, MagicMock -from tests.support.unit import TestCase, skipIf +import salt.modules.win_wusa as win_wusa # Import Salt Libs import salt.utils.platform -import salt.modules.win_wusa as win_wusa from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinWusaTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ test the functions in the win_wusa execution module - ''' + """ + def setup_loader_modules(self): return {win_wusa: {}} def test_is_installed_false(self): - ''' + """ test is_installed function when the KB is not installed - ''' + """ mock_retcode = MagicMock(return_value=1) - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}): - self.assertFalse(win_wusa.is_installed('KB123456')) + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + self.assertFalse(win_wusa.is_installed("KB123456")) def test_is_installed_true(self): - ''' + """ test is_installed function when the KB is installed - ''' + """ mock_retcode = MagicMock(return_value=0) - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}): - self.assertTrue(win_wusa.is_installed('KB123456')) + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): + self.assertTrue(win_wusa.is_installed("KB123456")) def test_list(self): - ''' + """ test list function - ''' - ret = {'pid': 1, - 'retcode': 0, - 'stderr': '', - 'stdout': '[{"HotFixID": "KB123456"}, ' - '{"HotFixID": "KB123457"}]'} + """ + ret = { + "pid": 1, + "retcode": 0, + "stderr": "", + "stdout": '[{"HotFixID": "KB123456"}, ' '{"HotFixID": "KB123457"}]', + } mock_all = MagicMock(return_value=ret) - with patch.dict(win_wusa.__salt__, {'cmd.run_all': mock_all}): - expected = ['KB123456', 'KB123457'] + with patch.dict(win_wusa.__salt__, {"cmd.run_all": mock_all}): + expected = ["KB123456", "KB123457"] returned = win_wusa.list() self.assertListEqual(expected, returned) def test_install(self): - ''' + """ test install function - ''' + """ mock_retcode = MagicMock(return_value=0) - path = 'C:\\KB123456.msu' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}): + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): self.assertTrue(win_wusa.install(path)) mock_retcode.assert_called_once_with( - ['wusa.exe', path, '/quiet', '/norestart'], ignore_retcode=True) + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) def test_install_restart(self): - ''' + """ test install function with restart=True - ''' + """ mock_retcode = MagicMock(return_value=0) - path = 'C:\\KB123456.msu' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}): + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): self.assertTrue(win_wusa.install(path, restart=True)) mock_retcode.assert_called_once_with( - ['wusa.exe', path, '/quiet', '/forcerestart'], ignore_retcode=True) + ["wusa.exe", path, "/quiet", "/forcerestart"], ignore_retcode=True + ) def test_install_already_installed(self): - ''' + """ test install function when KB already installed - ''' + """ retcode = 2359302 mock_retcode = MagicMock(return_value=retcode) - path = 'C:\\KB123456.msu' - name = 'KB123456.msu' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}): + path = "C:\\KB123456.msu" + name = "KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): with self.assertRaises(CommandExecutionError) as excinfo: win_wusa.install(path) mock_retcode.assert_called_once_with( - ['wusa.exe', path, '/quiet', '/norestart'], ignore_retcode=True) - self.assertEqual('{0} is already installed. Additional info follows:\n\n{1}'.format(name, retcode), - excinfo.exception.strerror) + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) + self.assertEqual( + "{0} is already installed. Additional info follows:\n\n{1}".format( + name, retcode + ), + excinfo.exception.strerror, + ) def test_install_reboot_needed(self): - ''' + """ test install function when KB need a reboot - ''' + """ retcode = 3010 mock_retcode = MagicMock(return_value=retcode) - path = 'C:\\KB123456.msu' - name = 'KB123456.msu' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}): + path = "C:\\KB123456.msu" + name = "KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): with self.assertRaises(CommandExecutionError) as excinfo: win_wusa.install(path) mock_retcode.assert_called_once_with( - ['wusa.exe', path, '/quiet', '/norestart'], ignore_retcode=True) - self.assertEqual('{0} correctly installed but server reboot is needed to complete installation. Additional info follows:\n\n{1}'.format(name, retcode), - excinfo.exception.strerror) + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) + self.assertEqual( + "{0} correctly installed but server reboot is needed to complete installation. Additional info follows:\n\n{1}".format( + name, retcode + ), + excinfo.exception.strerror, + ) def test_install_error_87(self): - ''' + """ test install function when error 87 returned - ''' + """ retcode = 87 mock_retcode = MagicMock(return_value=retcode) - path = 'C:\\KB123456.msu' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}): + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): with self.assertRaises(CommandExecutionError) as excinfo: win_wusa.install(path) mock_retcode.assert_called_once_with( - ['wusa.exe', path, '/quiet', '/norestart'], ignore_retcode=True) - self.assertEqual('Unknown error. Additional info follows:\n\n{0}'.format(retcode), - excinfo.exception.strerror) + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) + self.assertEqual( + "Unknown error. Additional info follows:\n\n{0}".format(retcode), + excinfo.exception.strerror, + ) def test_install_error_other(self): - ''' + """ test install function on other unknown error - ''' + """ mock_retcode = MagicMock(return_value=1234) - path = 'C:\\KB123456.msu' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}): + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): with self.assertRaises(CommandExecutionError) as excinfo: win_wusa.install(path) mock_retcode.assert_called_once_with( - ['wusa.exe', path, '/quiet', '/norestart'], ignore_retcode=True) - self.assertEqual('Unknown error: 1234', excinfo.exception.strerror) + ["wusa.exe", path, "/quiet", "/norestart"], ignore_retcode=True + ) + self.assertEqual("Unknown error: 1234", excinfo.exception.strerror) def test_uninstall_kb(self): - ''' + """ test uninstall function passing kb name - ''' + """ mock_retcode = MagicMock(return_value=0) - kb = 'KB123456' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}), \ - patch("os.path.exists", MagicMock(return_value=False)): + kb = "KB123456" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( + "os.path.exists", MagicMock(return_value=False) + ): self.assertTrue(win_wusa.uninstall(kb)) mock_retcode.assert_called_once_with( - ['wusa.exe', '/uninstall', '/quiet', '/kb:{0}'.format(kb[2:]), '/norestart'], - ignore_retcode=True) + [ + "wusa.exe", + "/uninstall", + "/quiet", + "/kb:{0}".format(kb[2:]), + "/norestart", + ], + ignore_retcode=True, + ) def test_uninstall_path(self): - ''' + """ test uninstall function passing full path to .msu file - ''' + """ mock_retcode = MagicMock(return_value=0) - path = 'C:\\KB123456.msu' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}), \ - patch("os.path.exists", MagicMock(return_value=True)): + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( + "os.path.exists", MagicMock(return_value=True) + ): self.assertTrue(win_wusa.uninstall(path)) mock_retcode.assert_called_once_with( - ['wusa.exe', '/uninstall', '/quiet', path, '/norestart'], - ignore_retcode=True) + ["wusa.exe", "/uninstall", "/quiet", path, "/norestart"], + ignore_retcode=True, + ) def test_uninstall_path_restart(self): - ''' + """ test uninstall function with full path and restart=True - ''' + """ mock_retcode = MagicMock(return_value=0) - path = 'C:\\KB123456.msu' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}), \ - patch("os.path.exists", MagicMock(return_value=True)): + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( + "os.path.exists", MagicMock(return_value=True) + ): self.assertTrue(win_wusa.uninstall(path, restart=True)) mock_retcode.assert_called_once_with( - ['wusa.exe', '/uninstall', '/quiet', path, '/forcerestart'], - ignore_retcode=True) + ["wusa.exe", "/uninstall", "/quiet", path, "/forcerestart"], + ignore_retcode=True, + ) def test_uninstall_already_uninstalled(self): - ''' + """ test uninstall function when KB already uninstalled - ''' + """ retcode = 2359303 mock_retcode = MagicMock(return_value=retcode) - kb = 'KB123456' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}): + kb = "KB123456" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}): with self.assertRaises(CommandExecutionError) as excinfo: win_wusa.uninstall(kb) mock_retcode.assert_called_once_with( - ['wusa.exe', '/uninstall', '/quiet', '/kb:{0}'.format(kb[2:]), '/norestart'], - ignore_retcode=True) - self.assertEqual('{0} not installed. Additional info follows:\n\n{1}'.format(kb, retcode), - excinfo.exception.strerror) + [ + "wusa.exe", + "/uninstall", + "/quiet", + "/kb:{0}".format(kb[2:]), + "/norestart", + ], + ignore_retcode=True, + ) + self.assertEqual( + "{0} not installed. Additional info follows:\n\n{1}".format(kb, retcode), + excinfo.exception.strerror, + ) def test_uninstall_path_error_other(self): - ''' + """ test uninstall function with unknown error - ''' + """ mock_retcode = MagicMock(return_value=1234) - path = 'C:\\KB123456.msu' - with patch.dict(win_wusa.__salt__, {'cmd.retcode': mock_retcode}), \ - patch("os.path.exists", MagicMock(return_value=True)), \ - self.assertRaises(CommandExecutionError) as excinfo: + path = "C:\\KB123456.msu" + with patch.dict(win_wusa.__salt__, {"cmd.retcode": mock_retcode}), patch( + "os.path.exists", MagicMock(return_value=True) + ), self.assertRaises(CommandExecutionError) as excinfo: win_wusa.uninstall(path) mock_retcode.assert_called_once_with( - ['wusa.exe', '/uninstall', '/quiet', path, '/norestart'], - ignore_retcode=True) - self.assertEqual('Unknown error: 1234', excinfo.exception.strerror) + ["wusa.exe", "/uninstall", "/quiet", path, "/norestart"], + ignore_retcode=True, + ) + self.assertEqual("Unknown error: 1234", excinfo.exception.strerror) diff --git a/tests/unit/modules/test_x509.py b/tests/unit/modules/test_x509.py index 624a927becd..abf920f01b0 100644 --- a/tests/unit/modules/test_x509.py +++ b/tests/unit/modules/test_x509.py @@ -17,27 +17,26 @@ # Import Salt Testing Libs from __future__ import absolute_import, print_function, unicode_literals + import os import tempfile +import salt.utils.files +import salt.utils.stringutils +from salt.modules import x509 +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + try: import pytest except ImportError as import_error: pytest = None -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - MagicMock, -) - -from salt.modules import x509 -import salt.utils.stringutils -import salt.utils.files try: import M2Crypto # pylint: disable=unused-import + HAS_M2CRYPTO = True except ImportError: HAS_M2CRYPTO = False @@ -45,26 +44,29 @@ except ImportError: @skipIf(not bool(pytest), False) class X509TestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {x509: {}} - @patch('salt.modules.x509.log', MagicMock()) + @patch("salt.modules.x509.log", MagicMock()) def test_private_func__parse_subject(self): - ''' + """ Test private function _parse_subject(subject) it handles a missing fields :return: - ''' + """ + class FakeSubject(object): - ''' + """ Class for faking x509'th subject. - ''' + """ + def __init__(self): - self.nid = {'Darth Vader': 1} + self.nid = {"Darth Vader": 1} def __getattr__(self, item): - if item != 'nid': - raise TypeError('A star wars satellite accidentally blew up the WAN.') + if item != "nid": + raise TypeError( + "A star wars satellite accidentally blew up the WAN." + ) subj = FakeSubject() x509._parse_subject(subj) @@ -72,13 +74,13 @@ class X509TestCase(TestCase, LoaderModuleMockMixin): assert x509.log.trace.call_args[0][1] == list(subj.nid.keys())[0] assert isinstance(x509.log.trace.call_args[0][2], TypeError) - @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypto is unavailble') + @skipIf(not HAS_M2CRYPTO, "Skipping, M2Crypto is unavailble") def test_get_pem_entry(self): - ''' + """ Test private function _parse_subject(subject) it handles a missing fields :return: - ''' - ca_key = b'''-----BEGIN RSA PRIVATE KEY----- + """ + ca_key = b"""-----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1 2rTPH81JyjbQDR5PyfCyzYOQtpwpB4zIUUK/Go7tTm409xGKbbUFugJNgQIDAQAB @@ -93,18 +95,18 @@ TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ== -----END RSA PRIVATE KEY----- -''' +""" ret = x509.get_pem_entry(ca_key) self.assertEqual(ret, ca_key) - @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypto is unavailble') + @skipIf(not HAS_M2CRYPTO, "Skipping, M2Crypto is unavailble") def test_get_private_key_size(self): - ''' + """ Test private function _parse_subject(subject) it handles a missing fields :return: - ''' - ca_key = ''' + """ + ca_key = """ -----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1 @@ -120,28 +122,27 @@ TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ== -----END RSA PRIVATE KEY----- -''' +""" ret = x509.get_private_key_size(ca_key) self.assertEqual(ret, 1024) - @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypto is unavailble') + @skipIf(not HAS_M2CRYPTO, "Skipping, M2Crypto is unavailble") def test_create_key(self): - ''' + """ Test that x509.create_key returns a private key :return: - ''' - ret = x509.create_private_key(text=True, - passphrase='super_secret_passphrase') - self.assertIn('BEGIN RSA PRIVATE KEY', ret) + """ + ret = x509.create_private_key(text=True, passphrase="super_secret_passphrase") + self.assertIn("BEGIN RSA PRIVATE KEY", ret) - @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypto is unavailble') + @skipIf(not HAS_M2CRYPTO, "Skipping, M2Crypto is unavailble") def test_create_certificate(self): - ''' + """ Test private function _parse_subject(subject) it handles a missing fields :return: - ''' - ca_key = ''' + """ + ca_key = """ -----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1 @@ -157,27 +158,29 @@ TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ== -----END RSA PRIVATE KEY----- -''' +""" - ret = x509.create_certificate(text=True, - signing_private_key=ca_key, - CN='Redacted Root CA', - O='Redacted', - C='BE', - ST='Antwerp', - L='Local Town', - Email='certadm@example.org', - basicConstraints="critical CA:true", - keyUsage="critical cRLSign, keyCertSign", - subjectKeyIdentifier='hash', - authorityKeyIdentifier='keyid,issuer:always', - days_valid=3650, - days_remaining=0) - self.assertIn('BEGIN CERTIFICATE', ret) + ret = x509.create_certificate( + text=True, + signing_private_key=ca_key, + CN="Redacted Root CA", + O="Redacted", + C="BE", + ST="Antwerp", + L="Local Town", + Email="certadm@example.org", + basicConstraints="critical CA:true", + keyUsage="critical cRLSign, keyCertSign", + subjectKeyIdentifier="hash", + authorityKeyIdentifier="keyid,issuer:always", + days_valid=3650, + days_remaining=0, + ) + self.assertIn("BEGIN CERTIFICATE", ret) - @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypto is unavailble') + @skipIf(not HAS_M2CRYPTO, "Skipping, M2Crypto is unavailble") def test_create_crl(self): - ca_key = ''' + ca_key = """ -----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1 @@ -193,43 +196,47 @@ TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ== -----END RSA PRIVATE KEY----- -''' +""" - ca_cert = x509.create_certificate(text=True, - signing_private_key=ca_key, - CN='Redacted Root CA', - O='Redacted', - C='BE', - ST='Antwerp', - L='Local Town', - Email='certadm@example.org', - basicConstraints="critical CA:true", - keyUsage="critical cRLSign, keyCertSign", - subjectKeyIdentifier='hash', - authorityKeyIdentifier='keyid,issuer:always', - days_valid=3650, - days_remaining=0) + ca_cert = x509.create_certificate( + text=True, + signing_private_key=ca_key, + CN="Redacted Root CA", + O="Redacted", + C="BE", + ST="Antwerp", + L="Local Town", + Email="certadm@example.org", + basicConstraints="critical CA:true", + keyUsage="critical cRLSign, keyCertSign", + subjectKeyIdentifier="hash", + authorityKeyIdentifier="keyid,issuer:always", + days_valid=3650, + days_remaining=0, + ) - with tempfile.NamedTemporaryFile('w+', delete=False) as ca_key_file: + with tempfile.NamedTemporaryFile("w+", delete=False) as ca_key_file: ca_key_file.write(ca_key) ca_key_file.flush() - with tempfile.NamedTemporaryFile('w+', delete=False) as ca_cert_file: + with tempfile.NamedTemporaryFile("w+", delete=False) as ca_cert_file: ca_cert_file.write(salt.utils.stringutils.to_str(ca_cert)) ca_cert_file.flush() - with tempfile.NamedTemporaryFile('w+', delete=False) as ca_crl_file: - x509.create_crl(path=ca_crl_file.name, - text=False, - signing_private_key=ca_key_file.name, - signing_private_key_passphrase=None, - signing_cert=ca_cert_file.name, - revoked=None, - include_expired=False, - days_valid=100, - digest='sha512') + with tempfile.NamedTemporaryFile("w+", delete=False) as ca_crl_file: + x509.create_crl( + path=ca_crl_file.name, + text=False, + signing_private_key=ca_key_file.name, + signing_private_key_passphrase=None, + signing_cert=ca_cert_file.name, + revoked=None, + include_expired=False, + days_valid=100, + digest="sha512", + ) - with salt.utils.files.fopen(ca_crl_file.name, 'r') as crl_file: + with salt.utils.files.fopen(ca_crl_file.name, "r") as crl_file: crl = crl_file.read() os.remove(ca_key_file.name) @@ -237,11 +244,11 @@ c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ== os.remove(ca_crl_file.name) # Ensure that a CRL was actually created - self.assertIn('BEGIN X509 CRL', crl) + self.assertIn("BEGIN X509 CRL", crl) - @skipIf(not HAS_M2CRYPTO, 'Skipping, M2Crypto is unavailble') + @skipIf(not HAS_M2CRYPTO, "Skipping, M2Crypto is unavailble") def test_revoke_certificate_with_crl(self): - ca_key = ''' + ca_key = """ -----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQCjdjbgL4kQ8Lu73xeRRM1q3C3K3ptfCLpyfw38LRnymxaoJ6ls pNSx2dU1uJ89YKFlYLo1QcEk4rJ2fdIjarV0kuNCY3rC8jYUp9BpAU5Z6p9HKeT1 @@ -257,74 +264,80 @@ TcKK0A8kOy0kMp3yvDHmJZ1L7wr7bBGIZPBlQ0Ddh8i1sJExm1gJ+uN2QKyg/XrK tDFf52zWnCdVGgDwcQJALW/WcbSEK+JVV6KDJYpwCzWpKIKpBI0F6fdCr1G7Xcwj c9bcgp7D7xD+TxWWNj4CSXEccJgGr91StV+gFg4ARQ== -----END RSA PRIVATE KEY----- -''' +""" # Issue the CA certificate (self-signed) - ca_cert = x509.create_certificate(text=True, - signing_private_key=ca_key, - CN='Redacted Root CA', - O='Redacted', - C='BE', - ST='Antwerp', - L='Local Town', - Email='certadm@example.org', - basicConstraints="critical CA:true", - keyUsage="critical cRLSign, keyCertSign", - subjectKeyIdentifier='hash', - authorityKeyIdentifier='keyid,issuer:always', - days_valid=3650, - days_remaining=0) + ca_cert = x509.create_certificate( + text=True, + signing_private_key=ca_key, + CN="Redacted Root CA", + O="Redacted", + C="BE", + ST="Antwerp", + L="Local Town", + Email="certadm@example.org", + basicConstraints="critical CA:true", + keyUsage="critical cRLSign, keyCertSign", + subjectKeyIdentifier="hash", + authorityKeyIdentifier="keyid,issuer:always", + days_valid=3650, + days_remaining=0, + ) # Sign a client certificate with the CA - server_cert = x509.create_certificate(text=True, - signing_private_key=ca_key, - signing_cert=ca_cert, - CN='Redacted Normal Certificate', - O='Redacted', - C='BE', - ST='Antwerp', - L='Local Town', - Email='certadm@example.org', - basicConstraints="critical CA:false", - keyUsage="critical keyEncipherment", - subjectKeyIdentifier='hash', - authorityKeyIdentifier='keyid,issuer:always', - days_valid=365, - days_remaining=0) + server_cert = x509.create_certificate( + text=True, + signing_private_key=ca_key, + signing_cert=ca_cert, + CN="Redacted Normal Certificate", + O="Redacted", + C="BE", + ST="Antwerp", + L="Local Town", + Email="certadm@example.org", + basicConstraints="critical CA:false", + keyUsage="critical keyEncipherment", + subjectKeyIdentifier="hash", + authorityKeyIdentifier="keyid,issuer:always", + days_valid=365, + days_remaining=0, + ) # Save CA cert + key and server cert to disk as PEM files - with tempfile.NamedTemporaryFile('w+', delete=False) as ca_key_file: + with tempfile.NamedTemporaryFile("w+", delete=False) as ca_key_file: ca_key_file.write(ca_key) ca_key_file.flush() - with tempfile.NamedTemporaryFile('w+', delete=False) as ca_cert_file: + with tempfile.NamedTemporaryFile("w+", delete=False) as ca_cert_file: ca_cert_file.write(salt.utils.stringutils.to_str(ca_cert)) ca_cert_file.flush() - with tempfile.NamedTemporaryFile('w+', delete=False) as server_cert_file: + with tempfile.NamedTemporaryFile("w+", delete=False) as server_cert_file: server_cert_file.write(salt.utils.stringutils.to_str(server_cert)) server_cert_file.flush() # Revoke server CRL revoked = [ { - 'certificate': server_cert_file.name, - 'revocation_date': '2015-03-01 00:00:00' + "certificate": server_cert_file.name, + "revocation_date": "2015-03-01 00:00:00", } ] - with tempfile.NamedTemporaryFile('w+', delete=False) as ca_crl_file: - x509.create_crl(path=ca_crl_file.name, - text=False, - signing_private_key=ca_key_file.name, - signing_private_key_passphrase=None, - signing_cert=ca_cert_file.name, - revoked=revoked, - include_expired=False, - days_valid=100, - digest='sha512') + with tempfile.NamedTemporaryFile("w+", delete=False) as ca_crl_file: + x509.create_crl( + path=ca_crl_file.name, + text=False, + signing_private_key=ca_key_file.name, + signing_private_key_passphrase=None, + signing_cert=ca_cert_file.name, + revoked=revoked, + include_expired=False, + days_valid=100, + digest="sha512", + ) # Retrieve serial number from server certificate server_cert_details = x509.read_certificate(server_cert_file.name) - serial_number = server_cert_details['Serial Number'].replace(':', '') + serial_number = server_cert_details["Serial Number"].replace(":", "") serial_number = salt.utils.stringutils.to_str(serial_number) # Retrieve CRL as text diff --git a/tests/unit/modules/test_xapi_virt.py b/tests/unit/modules/test_xapi_virt.py index 279dd6bf203..dc8d4d094f7 100644 --- a/tests/unit/modules/test_xapi_virt.py +++ b/tests/unit/modules/test_xapi_virt.py @@ -1,130 +1,132 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - mock_open, - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.xapi_virt as xapi +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class Mockxapi(object): - ''' + """ Mock xapi class - ''' + """ + def __init__(self): pass class Session(object): - ''' + """ Mock Session class - ''' + """ + def __init__(self, xapi_uri): pass class xenapi(object): - ''' + """ Mock xenapi class - ''' + """ + def __init__(self): pass @staticmethod def login_with_password(xapi_login, xapi_password): - ''' + """ Mock login_with_password method - ''' + """ return xapi_login, xapi_password class session(object): - ''' + """ Mock session class - ''' + """ + def __init__(self): pass @staticmethod def logout(): - ''' + """ Mock logout method - ''' + """ return Mockxapi() class XapiTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.xapi - ''' + """ + def setup_loader_modules(self): return {xapi: {}} def test_list_domains(self): - ''' + """ Test to return a list of domain names on the minion - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): self.assertListEqual(xapi.list_domains(), []) def test_vm_info(self): - ''' + """ Test to return detailed information about the vms - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(return_value=False) with patch.object(xapi, "_get_record_by_label", mock): self.assertDictEqual(xapi.vm_info(True), {True: False}) def test_vm_state(self): - ''' + """ Test to return list of all the vms and their state. - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(return_value={"power_state": "1"}) with patch.object(xapi, "_get_record_by_label", mock): - self.assertDictEqual(xapi.vm_state("salt"), {'salt': '1'}) + self.assertDictEqual(xapi.vm_state("salt"), {"salt": "1"}) self.assertDictEqual(xapi.vm_state(), {}) def test_get_nics(self): - ''' + """ Test to return info about the network interfaces of a named vm - ''' - ret = {'Stack': {'device': 'ETH0', 'mac': 'Stack', 'mtu': 1}} + """ + ret = {"Stack": {"device": "ETH0", "mac": "Stack", "mtu": 1}} with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, {"VIFs": "salt"}]) with patch.object(xapi, "_get_record_by_label", mock): self.assertFalse(xapi.get_nics("salt")) - mock = MagicMock(return_value={"MAC": "Stack", - "device": "ETH0", "MTU": 1}) + mock = MagicMock( + return_value={"MAC": "Stack", "device": "ETH0", "MTU": 1} + ) with patch.object(xapi, "_get_record", mock): self.assertDictEqual(xapi.get_nics("salt"), ret) def test_get_macs(self): - ''' + """ Test to return a list off MAC addresses from the named vm - ''' + """ mock = MagicMock(side_effect=[None, ["a", "b", "c"]]) with patch.object(xapi, "get_nics", mock): self.assertIsNone(xapi.get_macs("salt")) - self.assertListEqual(xapi.get_macs("salt"), ['a', 'b', 'c']) + self.assertListEqual(xapi.get_macs("salt"), ["a", "b", "c"]) def test_get_disks(self): - ''' + """ Test to return the disks of a named vm - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -133,9 +135,9 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual(xapi.get_disks("salt"), {}) def test_setmem(self): - ''' + """ Test to changes the amount of memory allocated to VM. - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -143,17 +145,16 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.setmem("salt", "1")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): self.assertFalse(xapi.setmem("salt", "1")) def test_setvcpus(self): - ''' + """ Test to changes the amount of vcpus allocated to VM. - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -161,17 +162,16 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.setvcpus("salt", "1")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): self.assertFalse(xapi.setvcpus("salt", "1")) def test_vcpu_pin(self): - ''' + """ Test to Set which CPUs a VCPU can use. - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -179,47 +179,47 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.vcpu_pin("salt", "1", "2")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): - with patch.dict(xapi.__salt__, {'cmd.run': mock}): + with patch.dict(xapi.__salt__, {"cmd.run": mock}): self.assertTrue(xapi.vcpu_pin("salt", "1", "2")) def test_freemem(self): - ''' + """ Test to return an int representing the amount of memory that has not been given to virtual machines on this node - ''' - mock = MagicMock(return_value={'free_memory': 1024}) + """ + mock = MagicMock(return_value={"free_memory": 1024}) with patch.object(xapi, "node_info", mock): self.assertEqual(xapi.freemem(), 1024) def test_freecpu(self): - ''' + """ Test to return an int representing the number of unallocated cpus on this hypervisor - ''' - mock = MagicMock(return_value={'free_cpus': 1024}) + """ + mock = MagicMock(return_value={"free_cpus": 1024}) with patch.object(xapi, "node_info", mock): self.assertEqual(xapi.freecpu(), 1024) def test_full_info(self): - ''' + """ Test to return the node_info, vm_info and freemem - ''' + """ mock = MagicMock(return_value="salt") with patch.object(xapi, "node_info", mock): mock = MagicMock(return_value="stack") with patch.object(xapi, "vm_info", mock): - self.assertDictEqual(xapi.full_info(), {'node_info': 'salt', - 'vm_info': 'stack'}) + self.assertDictEqual( + xapi.full_info(), {"node_info": "salt", "vm_info": "stack"} + ) def test_shutdown(self): - ''' + """ Test to send a soft shutdown signal to the named vm - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -227,17 +227,16 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.shutdown("salt")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): self.assertFalse(xapi.shutdown("salt")) def test_pause(self): - ''' + """ Test to pause the named vm - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -245,17 +244,16 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.pause("salt")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): self.assertFalse(xapi.pause("salt")) def test_resume(self): - ''' + """ Test to resume the named vm - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -263,25 +261,24 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.resume("salt")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): self.assertFalse(xapi.resume("salt")) def test_start(self): - ''' + """ Test to reboot a domain via ACPI request - ''' + """ mock = MagicMock(return_value=True) with patch.object(xapi, "start", mock): self.assertTrue(xapi.start("salt")) def test_reboot(self): - ''' + """ Test to reboot a domain via ACPI request - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -289,18 +286,17 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.reboot("salt")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): self.assertFalse(xapi.reboot("salt")) def test_reset(self): - ''' + """ Test to reset a VM by emulating the reset button on a physical machine - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -308,17 +304,16 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.reset("salt")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): self.assertFalse(xapi.reset("salt")) def test_migrate(self): - ''' + """ Test to migrates the virtual machine to another hypervisor - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -326,18 +321,17 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.migrate("salt", "stack")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): self.assertFalse(xapi.migrate("salt", "stack")) def test_stop(self): - ''' + """ Test to Hard power down the virtual machine, this is equivalent to pulling the power - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): mock = MagicMock(side_effect=[False, ["a", "b", "c"]]) with patch.object(xapi, "_get_label_uuid", mock): @@ -345,49 +339,48 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertTrue(xapi.stop("salt")) - with patch.object(xapi, "_check_xenapi", - MagicMock(return_value=Mockxapi)): + with patch.object(xapi, "_check_xenapi", MagicMock(return_value=Mockxapi)): mock = MagicMock(return_value=True) - with patch.dict(xapi.__salt__, {'config.option': mock}): + with patch.dict(xapi.__salt__, {"config.option": mock}): with patch.object(xapi, "_get_label_uuid", mock): self.assertFalse(xapi.stop("salt")) def test_is_hyper(self): - ''' + """ Test to returns a bool whether or not this node is a hypervisor of any kind - ''' - with patch.dict(xapi.__grains__, {'virtual_subtype': 'Dom0'}): + """ + with patch.dict(xapi.__grains__, {"virtual_subtype": "Dom0"}): self.assertFalse(xapi.is_hyper()) - with patch.dict(xapi.__grains__, {'virtual': 'Xen Dom0'}): + with patch.dict(xapi.__grains__, {"virtual": "Xen Dom0"}): self.assertFalse(xapi.is_hyper()) - with patch.dict(xapi.__grains__, {'virtual_subtype': 'Xen Dom0'}): - with patch('salt.utils.files.fopen', mock_open(read_data="salt")): + with patch.dict(xapi.__grains__, {"virtual_subtype": "Xen Dom0"}): + with patch("salt.utils.files.fopen", mock_open(read_data="salt")): self.assertFalse(xapi.is_hyper()) - with patch('salt.utils.files.fopen', mock_open()) as mock_read: + with patch("salt.utils.files.fopen", mock_open()) as mock_read: mock_read.side_effect = IOError self.assertFalse(xapi.is_hyper()) - with patch('salt.utils.files.fopen', mock_open(read_data="xen_")): - with patch.dict(xapi.__grains__, {'ps': 'salt'}): - mock = MagicMock(return_value={'xenstore': 'salt'}) - with patch.dict(xapi.__salt__, {'cmd.run': mock}): + with patch("salt.utils.files.fopen", mock_open(read_data="xen_")): + with patch.dict(xapi.__grains__, {"ps": "salt"}): + mock = MagicMock(return_value={"xenstore": "salt"}) + with patch.dict(xapi.__salt__, {"cmd.run": mock}): self.assertTrue(xapi.is_hyper()) def test_vm_cputime(self): - ''' + """ Test to Return cputime used by the vms - ''' - ret = {'1': {'cputime_percent': 0, 'cputime': 1}} + """ + ret = {"1": {"cputime_percent": 0, "cputime": 1}} with patch.object(xapi, "_get_xapi_session", MagicMock()): - mock = MagicMock(return_value={'host_CPUs': "1"}) + mock = MagicMock(return_value={"host_CPUs": "1"}) with patch.object(xapi, "_get_record_by_label", mock): - mock = MagicMock(return_value={'VCPUs_number': "1", - 'VCPUs_utilisation': {'0': '1'}} - ) + mock = MagicMock( + return_value={"VCPUs_number": "1", "VCPUs_utilisation": {"0": "1"}} + ) with patch.object(xapi, "_get_metrics_record", mock): self.assertDictEqual(xapi.vm_cputime("1"), ret) @@ -396,15 +389,15 @@ class XapiTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual(xapi.vm_cputime(""), {}) def test_vm_netstats(self): - ''' + """ Test to return combined network counters used by the vms - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): self.assertDictEqual(xapi.vm_netstats(""), {}) def test_vm_diskstats(self): - ''' + """ Test to return disk usage counters used by the vms - ''' + """ with patch.object(xapi, "_get_xapi_session", MagicMock()): self.assertDictEqual(xapi.vm_diskstats(""), {}) diff --git a/tests/unit/modules/test_xml.py b/tests/unit/modules/test_xml.py index 07662added0..d98adb9b170 100644 --- a/tests/unit/modules/test_xml.py +++ b/tests/unit/modules/test_xml.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Tests for xml module -''' +""" from __future__ import absolute_import, print_function, unicode_literals @@ -9,11 +9,10 @@ import os import tempfile from salt.modules import xml - from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -XML_STRING = ''' +XML_STRING = """ <root xmlns:foo="http://www.foo.org/" xmlns:bar="http://www.bar.org"> <actors> <actor id="1">Christian Bale</actor> @@ -26,22 +25,22 @@ XML_STRING = ''' <foo:singer id="6">Ray Charles</foo:singer> </foo:singers> </root> - ''' + """ class XmlTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.xml - ''' + """ def setup_loader_modules(self): return {xml: {}} def test_get_value(self): - ''' + """ Verify xml.get_value - ''' - with tempfile.NamedTemporaryFile('w+', delete=False) as xml_file: + """ + with tempfile.NamedTemporaryFile("w+", delete=False) as xml_file: xml_file.write(XML_STRING) xml_file.flush() @@ -51,14 +50,16 @@ class XmlTestCase(TestCase, LoaderModuleMockMixin): os.remove(xml_file.name) def test_set_value(self): - ''' + """ Verify xml.set_value - ''' - with tempfile.NamedTemporaryFile('w+', delete=False) as xml_file: + """ + with tempfile.NamedTemporaryFile("w+", delete=False) as xml_file: xml_file.write(XML_STRING) xml_file.flush() - xml_result = xml.set_value(xml_file.name, ".//actor[@id='2']", "Patrick Stewart") + xml_result = xml.set_value( + xml_file.name, ".//actor[@id='2']", "Patrick Stewart" + ) assert xml_result is True xml_result = xml.get_value(xml_file.name, ".//actor[@id='2']") @@ -67,10 +68,10 @@ class XmlTestCase(TestCase, LoaderModuleMockMixin): os.remove(xml_file.name) def test_get_attribute(self): - ''' + """ Verify xml.get_attribute - ''' - with tempfile.NamedTemporaryFile('w+', delete=False) as xml_file: + """ + with tempfile.NamedTemporaryFile("w+", delete=False) as xml_file: xml_file.write(XML_STRING) xml_file.flush() @@ -80,17 +81,19 @@ class XmlTestCase(TestCase, LoaderModuleMockMixin): os.remove(xml_file.name) def test_set_attribute(self): - ''' + """ Verify xml.set_value - ''' - with tempfile.NamedTemporaryFile('w+', delete=False) as xml_file: + """ + with tempfile.NamedTemporaryFile("w+", delete=False) as xml_file: xml_file.write(XML_STRING) xml_file.flush() - xml_result = xml.set_attribute(xml_file.name, ".//actor[@id='3']", "edited", "uh-huh") + xml_result = xml.set_attribute( + xml_file.name, ".//actor[@id='3']", "edited", "uh-huh" + ) assert xml_result is True xml_result = xml.get_attribute(xml_file.name, ".//actor[@id='3']") - self.assertEqual(xml_result, {'edited': 'uh-huh', 'id': '3'}) + self.assertEqual(xml_result, {"edited": "uh-huh", "id": "3"}) os.remove(xml_file.name) diff --git a/tests/unit/modules/test_yumpkg.py b/tests/unit/modules/test_yumpkg.py index 5e652b7e535..19ba0754f09 100644 --- a/tests/unit/modules/test_yumpkg.py +++ b/tests/unit/modules/test_yumpkg.py @@ -1,24 +1,22 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - Mock, - MagicMock, - patch, -) +import salt.modules.pkg_resource as pkg_resource +import salt.modules.rpm_lowpkg as rpm +import salt.modules.yumpkg as yumpkg # Import Salt libs from salt.exceptions import CommandExecutionError -import salt.modules.rpm_lowpkg as rpm from salt.ext import six -import salt.modules.yumpkg as yumpkg -import salt.modules.pkg_resource as pkg_resource + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase, skipIf try: import pytest @@ -26,288 +24,312 @@ except ImportError: pytest = None LIST_REPOS = { - 'base': { - 'file': '/etc/yum.repos.d/CentOS-Base.repo', - 'gpgcheck': '1', - 'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7', - 'mirrorlist': 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra', - 'name': 'CentOS-$releasever - Base' + "base": { + "file": "/etc/yum.repos.d/CentOS-Base.repo", + "gpgcheck": "1", + "gpgkey": "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7", + "mirrorlist": "http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os&infra=$infra", + "name": "CentOS-$releasever - Base", }, - 'base-source': { - 'baseurl': 'http://vault.centos.org/centos/$releasever/os/Source/', - 'enabled': '0', - 'file': '/etc/yum.repos.d/CentOS-Sources.repo', - 'gpgcheck': '1', - 'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7', - 'name': 'CentOS-$releasever - Base Sources' + "base-source": { + "baseurl": "http://vault.centos.org/centos/$releasever/os/Source/", + "enabled": "0", + "file": "/etc/yum.repos.d/CentOS-Sources.repo", + "gpgcheck": "1", + "gpgkey": "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7", + "name": "CentOS-$releasever - Base Sources", }, - 'updates': { - 'file': '/etc/yum.repos.d/CentOS-Base.repo', - 'gpgcheck': '1', - 'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7', - 'mirrorlist': 'http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates&infra=$infra', - 'name': 'CentOS-$releasever - Updates' + "updates": { + "file": "/etc/yum.repos.d/CentOS-Base.repo", + "gpgcheck": "1", + "gpgkey": "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7", + "mirrorlist": "http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=updates&infra=$infra", + "name": "CentOS-$releasever - Updates", + }, + "updates-source": { + "baseurl": "http://vault.centos.org/centos/$releasever/updates/Source/", + "enabled": "0", + "file": "/etc/yum.repos.d/CentOS-Sources.repo", + "gpgcheck": "1", + "gpgkey": "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7", + "name": "CentOS-$releasever - Updates Sources", }, - 'updates-source': { - 'baseurl': 'http://vault.centos.org/centos/$releasever/updates/Source/', - 'enabled': '0', - 'file': '/etc/yum.repos.d/CentOS-Sources.repo', - 'gpgcheck': '1', - 'gpgkey': 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7', - 'name': 'CentOS-$releasever - Updates Sources' - } } class YumTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.yumpkg - ''' + """ + def setup_loader_modules(self): return { yumpkg: { - '__context__': { - 'yum_bin': 'yum', - }, - '__grains__': { - 'osarch': 'x86_64', - 'os_family': 'RedHat', - 'osmajorrelease': 7, + "__context__": {"yum_bin": "yum"}, + "__grains__": { + "osarch": "x86_64", + "os": "CentOS", + "os_family": "RedHat", + "osmajorrelease": 7, }, }, - pkg_resource: {} + pkg_resource: {}, } def test_list_pkgs(self): - ''' + """ Test packages listing. :return: - ''' + """ + def _add_data(data, key, value): data.setdefault(key, []).append(value) rpm_out = [ - 'python-urlgrabber_|-(none)_|-3.10_|-8.el7_|-noarch_|-(none)_|-1487838471', - 'alsa-lib_|-(none)_|-1.1.1_|-1.el7_|-x86_64_|-(none)_|-1487838475', - 'gnupg2_|-(none)_|-2.0.22_|-4.el7_|-x86_64_|-(none)_|-1487838477', - 'rpm-python_|-(none)_|-4.11.3_|-21.el7_|-x86_64_|-(none)_|-1487838477', - 'pygpgme_|-(none)_|-0.3_|-9.el7_|-x86_64_|-(none)_|-1487838478', - 'yum_|-(none)_|-3.4.3_|-150.el7.centos_|-noarch_|-(none)_|-1487838479', - 'lzo_|-(none)_|-2.06_|-8.el7_|-x86_64_|-(none)_|-1487838479', - 'qrencode-libs_|-(none)_|-3.4.1_|-3.el7_|-x86_64_|-(none)_|-1487838480', - 'ustr_|-(none)_|-1.0.4_|-16.el7_|-x86_64_|-(none)_|-1487838480', - 'shadow-utils_|-2_|-4.1.5.1_|-24.el7_|-x86_64_|-(none)_|-1487838481', - 'util-linux_|-(none)_|-2.23.2_|-33.el7_|-x86_64_|-(none)_|-1487838484', - 'openssh_|-(none)_|-6.6.1p1_|-33.el7_3_|-x86_64_|-(none)_|-1487838485', - 'virt-what_|-(none)_|-1.13_|-8.el7_|-x86_64_|-(none)_|-1487838486', + "python-urlgrabber_|-(none)_|-3.10_|-8.el7_|-noarch_|-(none)_|-1487838471", + "alsa-lib_|-(none)_|-1.1.1_|-1.el7_|-x86_64_|-(none)_|-1487838475", + "gnupg2_|-(none)_|-2.0.22_|-4.el7_|-x86_64_|-(none)_|-1487838477", + "rpm-python_|-(none)_|-4.11.3_|-21.el7_|-x86_64_|-(none)_|-1487838477", + "pygpgme_|-(none)_|-0.3_|-9.el7_|-x86_64_|-(none)_|-1487838478", + "yum_|-(none)_|-3.4.3_|-150.el7.centos_|-noarch_|-(none)_|-1487838479", + "lzo_|-(none)_|-2.06_|-8.el7_|-x86_64_|-(none)_|-1487838479", + "qrencode-libs_|-(none)_|-3.4.1_|-3.el7_|-x86_64_|-(none)_|-1487838480", + "ustr_|-(none)_|-1.0.4_|-16.el7_|-x86_64_|-(none)_|-1487838480", + "shadow-utils_|-2_|-4.1.5.1_|-24.el7_|-x86_64_|-(none)_|-1487838481", + "util-linux_|-(none)_|-2.23.2_|-33.el7_|-x86_64_|-(none)_|-1487838484", + "openssh_|-(none)_|-6.6.1p1_|-33.el7_3_|-x86_64_|-(none)_|-1487838485", + "virt-what_|-(none)_|-1.13_|-8.el7_|-x86_64_|-(none)_|-1487838486", ] - with patch.dict(yumpkg.__grains__, {'osarch': 'x86_64'}), \ - patch.dict(yumpkg.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(rpm_out))}), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.add_pkg': _add_data}), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.format_pkg_list': pkg_resource.format_pkg_list}), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.stringify': MagicMock()}), \ - patch.dict(pkg_resource.__salt__, {'pkg.parse_arch': yumpkg.parse_arch}): + with patch.dict(yumpkg.__grains__, {"osarch": "x86_64"}), patch.dict( + yumpkg.__salt__, + {"cmd.run": MagicMock(return_value=os.linesep.join(rpm_out))}, + ), patch.dict(yumpkg.__salt__, {"pkg_resource.add_pkg": _add_data}), patch.dict( + yumpkg.__salt__, + {"pkg_resource.format_pkg_list": pkg_resource.format_pkg_list}, + ), patch.dict( + yumpkg.__salt__, {"pkg_resource.stringify": MagicMock()} + ), patch.dict( + pkg_resource.__salt__, {"pkg.parse_arch": yumpkg.parse_arch} + ): pkgs = yumpkg.list_pkgs(versions_as_list=True) for pkg_name, pkg_version in { - 'python-urlgrabber': '3.10-8.el7', - 'alsa-lib': '1.1.1-1.el7', - 'gnupg2': '2.0.22-4.el7', - 'rpm-python': '4.11.3-21.el7', - 'pygpgme': '0.3-9.el7', - 'yum': '3.4.3-150.el7.centos', - 'lzo': '2.06-8.el7', - 'qrencode-libs': '3.4.1-3.el7', - 'ustr': '1.0.4-16.el7', - 'shadow-utils': '2:4.1.5.1-24.el7', - 'util-linux': '2.23.2-33.el7', - 'openssh': '6.6.1p1-33.el7_3', - 'virt-what': '1.13-8.el7'}.items(): + "python-urlgrabber": "3.10-8.el7", + "alsa-lib": "1.1.1-1.el7", + "gnupg2": "2.0.22-4.el7", + "rpm-python": "4.11.3-21.el7", + "pygpgme": "0.3-9.el7", + "yum": "3.4.3-150.el7.centos", + "lzo": "2.06-8.el7", + "qrencode-libs": "3.4.1-3.el7", + "ustr": "1.0.4-16.el7", + "shadow-utils": "2:4.1.5.1-24.el7", + "util-linux": "2.23.2-33.el7", + "openssh": "6.6.1p1-33.el7_3", + "virt-what": "1.13-8.el7", + }.items(): self.assertTrue(pkgs.get(pkg_name)) self.assertEqual(pkgs[pkg_name], [pkg_version]) def test_list_pkgs_with_attr(self): - ''' + """ Test packages listing with the attr parameter :return: - ''' + """ + def _add_data(data, key, value): data.setdefault(key, []).append(value) rpm_out = [ - 'python-urlgrabber_|-(none)_|-3.10_|-8.el7_|-noarch_|-(none)_|-1487838471', - 'alsa-lib_|-(none)_|-1.1.1_|-1.el7_|-x86_64_|-(none)_|-1487838475', - 'gnupg2_|-(none)_|-2.0.22_|-4.el7_|-x86_64_|-(none)_|-1487838477', - 'rpm-python_|-(none)_|-4.11.3_|-21.el7_|-x86_64_|-(none)_|-1487838477', - 'pygpgme_|-(none)_|-0.3_|-9.el7_|-x86_64_|-(none)_|-1487838478', - 'yum_|-(none)_|-3.4.3_|-150.el7.centos_|-noarch_|-(none)_|-1487838479', - 'lzo_|-(none)_|-2.06_|-8.el7_|-x86_64_|-(none)_|-1487838479', - 'qrencode-libs_|-(none)_|-3.4.1_|-3.el7_|-x86_64_|-(none)_|-1487838480', - 'ustr_|-(none)_|-1.0.4_|-16.el7_|-x86_64_|-(none)_|-1487838480', - 'shadow-utils_|-2_|-4.1.5.1_|-24.el7_|-x86_64_|-(none)_|-1487838481', - 'util-linux_|-(none)_|-2.23.2_|-33.el7_|-x86_64_|-(none)_|-1487838484', - 'openssh_|-(none)_|-6.6.1p1_|-33.el7_3_|-x86_64_|-(none)_|-1487838485', - 'virt-what_|-(none)_|-1.13_|-8.el7_|-x86_64_|-(none)_|-1487838486', + "python-urlgrabber_|-(none)_|-3.10_|-8.el7_|-noarch_|-(none)_|-1487838471", + "alsa-lib_|-(none)_|-1.1.1_|-1.el7_|-x86_64_|-(none)_|-1487838475", + "gnupg2_|-(none)_|-2.0.22_|-4.el7_|-x86_64_|-(none)_|-1487838477", + "rpm-python_|-(none)_|-4.11.3_|-21.el7_|-x86_64_|-(none)_|-1487838477", + "pygpgme_|-(none)_|-0.3_|-9.el7_|-x86_64_|-(none)_|-1487838478", + "yum_|-(none)_|-3.4.3_|-150.el7.centos_|-noarch_|-(none)_|-1487838479", + "lzo_|-(none)_|-2.06_|-8.el7_|-x86_64_|-(none)_|-1487838479", + "qrencode-libs_|-(none)_|-3.4.1_|-3.el7_|-x86_64_|-(none)_|-1487838480", + "ustr_|-(none)_|-1.0.4_|-16.el7_|-x86_64_|-(none)_|-1487838480", + "shadow-utils_|-2_|-4.1.5.1_|-24.el7_|-x86_64_|-(none)_|-1487838481", + "util-linux_|-(none)_|-2.23.2_|-33.el7_|-x86_64_|-(none)_|-1487838484", + "openssh_|-(none)_|-6.6.1p1_|-33.el7_3_|-x86_64_|-(none)_|-1487838485", + "virt-what_|-(none)_|-1.13_|-8.el7_|-x86_64_|-(none)_|-1487838486", ] - with patch.dict(yumpkg.__grains__, {'osarch': 'x86_64'}), \ - patch.dict(yumpkg.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(rpm_out))}), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.add_pkg': _add_data}), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.format_pkg_list': pkg_resource.format_pkg_list}), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.stringify': MagicMock()}), \ - patch.dict(pkg_resource.__salt__, {'pkg.parse_arch': yumpkg.parse_arch}): - pkgs = yumpkg.list_pkgs(attr=['epoch', 'release', 'arch', 'install_date_time_t']) + with patch.dict(yumpkg.__grains__, {"osarch": "x86_64"}), patch.dict( + yumpkg.__salt__, + {"cmd.run": MagicMock(return_value=os.linesep.join(rpm_out))}, + ), patch.dict(yumpkg.__salt__, {"pkg_resource.add_pkg": _add_data}), patch.dict( + yumpkg.__salt__, + {"pkg_resource.format_pkg_list": pkg_resource.format_pkg_list}, + ), patch.dict( + yumpkg.__salt__, {"pkg_resource.stringify": MagicMock()} + ), patch.dict( + pkg_resource.__salt__, {"pkg.parse_arch": yumpkg.parse_arch} + ): + pkgs = yumpkg.list_pkgs( + attr=["epoch", "release", "arch", "install_date_time_t"] + ) for pkg_name, pkg_attr in { - 'python-urlgrabber': { - 'version': '3.10', - 'release': '8.el7', - 'arch': 'noarch', - 'install_date_time_t': 1487838471, - 'epoch': None + "python-urlgrabber": { + "version": "3.10", + "release": "8.el7", + "arch": "noarch", + "install_date_time_t": 1487838471, + "epoch": None, }, - 'alsa-lib': { - 'version': '1.1.1', - 'release': '1.el7', - 'arch': 'x86_64', - 'install_date_time_t': 1487838475, - 'epoch': None + "alsa-lib": { + "version": "1.1.1", + "release": "1.el7", + "arch": "x86_64", + "install_date_time_t": 1487838475, + "epoch": None, }, - 'gnupg2': { - 'version': '2.0.22', - 'release': '4.el7', - 'arch': 'x86_64', - 'install_date_time_t': 1487838477, - 'epoch': None + "gnupg2": { + "version": "2.0.22", + "release": "4.el7", + "arch": "x86_64", + "install_date_time_t": 1487838477, + "epoch": None, }, - 'rpm-python': { - 'version': '4.11.3', - 'release': '21.el7', - 'arch': 'x86_64', - 'install_date_time_t': 1487838477, - 'epoch': None + "rpm-python": { + "version": "4.11.3", + "release": "21.el7", + "arch": "x86_64", + "install_date_time_t": 1487838477, + "epoch": None, }, - 'pygpgme': { - 'version': '0.3', - 'release': '9.el7', - 'arch': 'x86_64', - 'install_date_time_t': 1487838478, - 'epoch': None + "pygpgme": { + "version": "0.3", + "release": "9.el7", + "arch": "x86_64", + "install_date_time_t": 1487838478, + "epoch": None, }, - 'yum': { - 'version': '3.4.3', - 'release': '150.el7.centos', - 'arch': 'noarch', - 'install_date_time_t': 1487838479, - 'epoch': None + "yum": { + "version": "3.4.3", + "release": "150.el7.centos", + "arch": "noarch", + "install_date_time_t": 1487838479, + "epoch": None, }, - 'lzo': { - 'version': '2.06', - 'release': '8.el7', - 'arch': 'x86_64', - 'install_date_time_t': 1487838479, - 'epoch': None + "lzo": { + "version": "2.06", + "release": "8.el7", + "arch": "x86_64", + "install_date_time_t": 1487838479, + "epoch": None, }, - 'qrencode-libs': { - 'version': '3.4.1', - 'release': '3.el7', - 'arch': 'x86_64', - 'install_date_time_t': 1487838480, - 'epoch': None + "qrencode-libs": { + "version": "3.4.1", + "release": "3.el7", + "arch": "x86_64", + "install_date_time_t": 1487838480, + "epoch": None, }, - 'ustr': { - 'version': '1.0.4', - 'release': '16.el7', - 'arch': 'x86_64', - 'install_date_time_t': 1487838480, - 'epoch': None + "ustr": { + "version": "1.0.4", + "release": "16.el7", + "arch": "x86_64", + "install_date_time_t": 1487838480, + "epoch": None, }, - 'shadow-utils': { - 'epoch': '2', - 'version': '4.1.5.1', - 'release': '24.el7', - 'arch': 'x86_64', - 'install_date_time_t': 1487838481, + "shadow-utils": { + "epoch": "2", + "version": "4.1.5.1", + "release": "24.el7", + "arch": "x86_64", + "install_date_time_t": 1487838481, }, - 'util-linux': { - 'version': '2.23.2', - 'release': '33.el7', - 'arch': 'x86_64', - 'install_date_time_t': 1487838484, - 'epoch': None + "util-linux": { + "version": "2.23.2", + "release": "33.el7", + "arch": "x86_64", + "install_date_time_t": 1487838484, + "epoch": None, }, - 'openssh': { - 'version': '6.6.1p1', - 'release': '33.el7_3', - 'arch': 'x86_64', - 'install_date_time_t': 1487838485, - 'epoch': None + "openssh": { + "version": "6.6.1p1", + "release": "33.el7_3", + "arch": "x86_64", + "install_date_time_t": 1487838485, + "epoch": None, }, - 'virt-what': { - 'version': '1.13', - 'release': '8.el7', - 'install_date_time_t': 1487838486, - 'arch': 'x86_64', - 'epoch': None - }}.items(): + "virt-what": { + "version": "1.13", + "release": "8.el7", + "install_date_time_t": 1487838486, + "arch": "x86_64", + "epoch": None, + }, + }.items(): self.assertTrue(pkgs.get(pkg_name)) self.assertEqual(pkgs[pkg_name], [pkg_attr]) def test_list_pkgs_with_attr_multiple_versions(self): - ''' + """ Test packages listing with the attr parameter reporting multiple version installed :return: - ''' + """ + def _add_data(data, key, value): data.setdefault(key, []).append(value) rpm_out = [ - 'glibc_|-(none)_|-2.12_|-1.212.el6_|-i686_|-(none)_|-1542394210' - 'glibc_|-(none)_|-2.12_|-1.212.el6_|-x86_64_|-(none)_|-1542394204', - 'virt-what_|-(none)_|-1.13_|-8.el7_|-x86_64_|-(none)_|-1487838486', - 'virt-what_|-(none)_|-1.10_|-2.el7_|-x86_64_|-(none)_|-1387838486', + "glibc_|-(none)_|-2.12_|-1.212.el6_|-i686_|-(none)_|-1542394210" + "glibc_|-(none)_|-2.12_|-1.212.el6_|-x86_64_|-(none)_|-1542394204", + "virt-what_|-(none)_|-1.13_|-8.el7_|-x86_64_|-(none)_|-1487838486", + "virt-what_|-(none)_|-1.10_|-2.el7_|-x86_64_|-(none)_|-1387838486", ] - with patch.dict(yumpkg.__grains__, {'osarch': 'x86_64'}), \ - patch.dict(yumpkg.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(rpm_out))}), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.add_pkg': _add_data}), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.format_pkg_list': pkg_resource.format_pkg_list}), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.stringify': MagicMock()}), \ - patch.dict(pkg_resource.__salt__, {'pkg.parse_arch': yumpkg.parse_arch}): - pkgs = yumpkg.list_pkgs(attr=['epoch', 'release', 'arch', 'install_date_time_t']) + with patch.dict(yumpkg.__grains__, {"osarch": "x86_64"}), patch.dict( + yumpkg.__salt__, + {"cmd.run": MagicMock(return_value=os.linesep.join(rpm_out))}, + ), patch.dict(yumpkg.__salt__, {"pkg_resource.add_pkg": _add_data}), patch.dict( + yumpkg.__salt__, + {"pkg_resource.format_pkg_list": pkg_resource.format_pkg_list}, + ), patch.dict( + yumpkg.__salt__, {"pkg_resource.stringify": MagicMock()} + ), patch.dict( + pkg_resource.__salt__, {"pkg.parse_arch": yumpkg.parse_arch} + ): + pkgs = yumpkg.list_pkgs( + attr=["epoch", "release", "arch", "install_date_time_t"] + ) expected_pkg_list = { - 'glibc': [ + "glibc": [ { - 'version': '2.12', - 'release': '1.212.el6', - 'install_date_time_t': 1542394210, - 'arch': 'i686', - 'epoch': None + "version": "2.12", + "release": "1.212.el6", + "install_date_time_t": 1542394210, + "arch": "i686", + "epoch": None, }, { - 'version': '2.12', - 'release': '1.212.el6', - 'install_date_time_t': 1542394204, - 'arch': 'x86_64', - 'epoch': None - } + "version": "2.12", + "release": "1.212.el6", + "install_date_time_t": 1542394204, + "arch": "x86_64", + "epoch": None, + }, ], - 'virt-what': [ + "virt-what": [ { - 'version': '1.10', - 'release': '2.el7', - 'install_date_time_t': 1387838486, - 'arch': 'x86_64', - 'epoch': None + "version": "1.10", + "release": "2.el7", + "install_date_time_t": 1387838486, + "arch": "x86_64", + "epoch": None, }, { - 'version': '1.13', - 'release': '8.el7', - 'install_date_time_t': 1487838486, - 'arch': 'x86_64', - 'epoch': None - } - ] + "version": "1.13", + "release": "8.el7", + "install_date_time_t": 1487838486, + "arch": "x86_64", + "epoch": None, + }, + ], } for pkgname, pkginfo in pkgs.items(): if six.PY3: @@ -316,179 +338,259 @@ class YumTestCase(TestCase, LoaderModuleMockMixin): self.assertItemsEqual(pkginfo, expected_pkg_list[pkgname]) def test_list_patches(self): - ''' + """ Test patches listing. :return: - ''' + """ yum_out = [ - 'i my-fake-patch-not-installed-1234 recommended spacewalk-usix-2.7.5.2-2.2.noarch', - ' my-fake-patch-not-installed-1234 recommended spacewalksd-5.0.26.2-21.2.x86_64', - 'i my-fake-patch-not-installed-1234 recommended suseRegisterInfo-3.1.1-18.2.x86_64', - 'i my-fake-patch-installed-1234 recommended my-package-one-1.1-0.1.x86_64', - 'i my-fake-patch-installed-1234 recommended my-package-two-1.1-0.1.x86_64', + "i my-fake-patch-not-installed-1234 recommended spacewalk-usix-2.7.5.2-2.2.noarch", + " my-fake-patch-not-installed-1234 recommended spacewalksd-5.0.26.2-21.2.x86_64", + "i my-fake-patch-not-installed-1234 recommended suseRegisterInfo-3.1.1-18.2.x86_64", + "i my-fake-patch-installed-1234 recommended my-package-one-1.1-0.1.x86_64", + "i my-fake-patch-installed-1234 recommended my-package-two-1.1-0.1.x86_64", ] expected_patches = { - 'my-fake-patch-not-installed-1234': { - 'installed': False, - 'summary': [ - 'spacewalk-usix-2.7.5.2-2.2.noarch', - 'spacewalksd-5.0.26.2-21.2.x86_64', - 'suseRegisterInfo-3.1.1-18.2.x86_64', - ] + "my-fake-patch-not-installed-1234": { + "installed": False, + "summary": [ + "spacewalk-usix-2.7.5.2-2.2.noarch", + "spacewalksd-5.0.26.2-21.2.x86_64", + "suseRegisterInfo-3.1.1-18.2.x86_64", + ], + }, + "my-fake-patch-installed-1234": { + "installed": True, + "summary": [ + "my-package-one-1.1-0.1.x86_64", + "my-package-two-1.1-0.1.x86_64", + ], }, - 'my-fake-patch-installed-1234': { - 'installed': True, - 'summary': [ - 'my-package-one-1.1-0.1.x86_64', - 'my-package-two-1.1-0.1.x86_64', - ] - } } - with patch.dict(yumpkg.__grains__, {'osarch': 'x86_64'}), \ - patch.dict(yumpkg.__salt__, {'cmd.run_stdout': MagicMock(return_value=os.linesep.join(yum_out))}): + with patch.dict(yumpkg.__grains__, {"osarch": "x86_64"}), patch.dict( + yumpkg.__salt__, + {"cmd.run_stdout": MagicMock(return_value=os.linesep.join(yum_out))}, + ): patches = yumpkg.list_patches() - self.assertFalse(patches['my-fake-patch-not-installed-1234']['installed']) - self.assertTrue(len(patches['my-fake-patch-not-installed-1234']['summary']) == 3) - for _patch in expected_patches['my-fake-patch-not-installed-1234']['summary']: - self.assertTrue(_patch in patches['my-fake-patch-not-installed-1234']['summary']) + self.assertFalse(patches["my-fake-patch-not-installed-1234"]["installed"]) + self.assertTrue( + len(patches["my-fake-patch-not-installed-1234"]["summary"]) == 3 + ) + for _patch in expected_patches["my-fake-patch-not-installed-1234"][ + "summary" + ]: + self.assertTrue( + _patch in patches["my-fake-patch-not-installed-1234"]["summary"] + ) - self.assertTrue(patches['my-fake-patch-installed-1234']['installed']) - self.assertTrue(len(patches['my-fake-patch-installed-1234']['summary']) == 2) - for _patch in expected_patches['my-fake-patch-installed-1234']['summary']: - self.assertTrue(_patch in patches['my-fake-patch-installed-1234']['summary']) + self.assertTrue(patches["my-fake-patch-installed-1234"]["installed"]) + self.assertTrue( + len(patches["my-fake-patch-installed-1234"]["summary"]) == 2 + ) + for _patch in expected_patches["my-fake-patch-installed-1234"]["summary"]: + self.assertTrue( + _patch in patches["my-fake-patch-installed-1234"]["summary"] + ) def test_latest_version_with_options(self): - with patch.object(yumpkg, 'list_pkgs', MagicMock(return_value={})): + with patch.object(yumpkg, "list_pkgs", MagicMock(return_value={})): # with fromrepo - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): yumpkg.latest_version( - 'foo', - refresh=False, - fromrepo='good', - branch='foo') + "foo", refresh=False, fromrepo="good", branch="foo" + ) cmd.assert_called_once_with( - ['yum', '--quiet', '--disablerepo=*', '--enablerepo=good', - '--branch=foo', 'list', 'available', 'foo'], env={}, + [ + "yum", + "--quiet", + "--disablerepo=*", + "--enablerepo=good", + "--branch=foo", + "list", + "available", + "foo", + ], + env={}, ignore_retcode=True, - output_loglevel='trace', - python_shell=False) + output_loglevel="trace", + python_shell=False, + ) # without fromrepo - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): yumpkg.latest_version( - 'foo', + "foo", refresh=False, - enablerepo='good', - disablerepo='bad', - branch='foo') + enablerepo="good", + disablerepo="bad", + branch="foo", + ) cmd.assert_called_once_with( - ['yum', '--quiet', '--disablerepo=bad', '--enablerepo=good', - '--branch=foo', 'list', 'available', 'foo'], env={}, + [ + "yum", + "--quiet", + "--disablerepo=bad", + "--enablerepo=good", + "--branch=foo", + "list", + "available", + "foo", + ], + env={}, ignore_retcode=True, - output_loglevel='trace', - python_shell=False) + output_loglevel="trace", + python_shell=False, + ) # without fromrepo, but within the scope - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch('salt.utils.systemd.has_scope', MagicMock(return_value=True)): - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, - 'config.get': MagicMock(return_value=True)}): + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch("salt.utils.systemd.has_scope", MagicMock(return_value=True)): + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=True)}, + ): yumpkg.latest_version( - 'foo', + "foo", refresh=False, - enablerepo='good', - disablerepo='bad', - branch='foo') + enablerepo="good", + disablerepo="bad", + branch="foo", + ) cmd.assert_called_once_with( - ['systemd-run', '--scope', 'yum', '--quiet', '--disablerepo=bad', '--enablerepo=good', - '--branch=foo', 'list', 'available', 'foo'], env={}, + [ + "systemd-run", + "--scope", + "yum", + "--quiet", + "--disablerepo=bad", + "--enablerepo=good", + "--branch=foo", + "list", + "available", + "foo", + ], + env={}, ignore_retcode=True, - output_loglevel='trace', - python_shell=False) + output_loglevel="trace", + python_shell=False, + ) def test_list_repo_pkgs_with_options(self): - ''' + """ Test list_repo_pkgs with and without fromrepo NOTE: mock_calls is a stack. The most recent call is indexed with 0, while the first call would have the highest index. - ''' - really_old_yum = MagicMock(return_value='3.2.0') - older_yum = MagicMock(return_value='3.4.0') - newer_yum = MagicMock(return_value='3.4.5') + """ + really_old_yum = MagicMock(return_value="3.2.0") + older_yum = MagicMock(return_value="3.4.0") + newer_yum = MagicMock(return_value="3.4.5") list_repos_mock = MagicMock(return_value=LIST_REPOS) - kwargs = {'output_loglevel': 'trace', - 'ignore_retcode': True, - 'python_shell': False, - 'env': {}} + kwargs = { + "output_loglevel": "trace", + "ignore_retcode": True, + "python_shell": False, + "env": {}, + } - with patch.object(yumpkg, 'list_repos', list_repos_mock): + with patch.object(yumpkg, "list_repos", list_repos_mock): # Test with really old yum. The fromrepo argument has no effect on # the yum commands we'd run. - with patch.dict(yumpkg.__salt__, {'cmd.run': really_old_yum}): + with patch.dict(yumpkg.__salt__, {"cmd.run": really_old_yum}): - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): - yumpkg.list_repo_pkgs('foo') + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): + yumpkg.list_repo_pkgs("foo") # We should have called cmd.run_all twice assert len(cmd.mock_calls) == 2 # Check args from first call - assert cmd.mock_calls[1][1] == (['yum', '--quiet', 'list', 'available'],) + assert cmd.mock_calls[1][1] == ( + ["yum", "--quiet", "list", "available"], + ) # Check kwargs from first call assert cmd.mock_calls[1][2] == kwargs # Check args from second call - assert cmd.mock_calls[0][1] == (['yum', '--quiet', 'list', 'installed'],) + assert cmd.mock_calls[0][1] == ( + ["yum", "--quiet", "list", "installed"], + ) # Check kwargs from second call assert cmd.mock_calls[0][2] == kwargs # Test with really old yum. The fromrepo argument has no effect on # the yum commands we'd run. - with patch.dict(yumpkg.__salt__, {'cmd.run': older_yum}): + with patch.dict(yumpkg.__salt__, {"cmd.run": older_yum}): - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): - yumpkg.list_repo_pkgs('foo') + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): + yumpkg.list_repo_pkgs("foo") # We should have called cmd.run_all twice assert len(cmd.mock_calls) == 2 # Check args from first call - assert cmd.mock_calls[1][1] == (['yum', '--quiet', '--showduplicates', 'list', 'available'],) + assert cmd.mock_calls[1][1] == ( + ["yum", "--quiet", "--showduplicates", "list", "available"], + ) # Check kwargs from first call assert cmd.mock_calls[1][2] == kwargs # Check args from second call - assert cmd.mock_calls[0][1] == (['yum', '--quiet', '--showduplicates', 'list', 'installed'],) + assert cmd.mock_calls[0][1] == ( + ["yum", "--quiet", "--showduplicates", "list", "installed"], + ) # Check kwargs from second call assert cmd.mock_calls[0][2] == kwargs # Test with newer yum. We should run one yum command per repo, so # fromrepo would limit how many calls we make. - with patch.dict(yumpkg.__salt__, {'cmd.run': newer_yum}): + with patch.dict(yumpkg.__salt__, {"cmd.run": newer_yum}): # When fromrepo is used, we would only run one yum command, for # that specific repo. - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): - yumpkg.list_repo_pkgs('foo', fromrepo='base') + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): + yumpkg.list_repo_pkgs("foo", fromrepo="base") # We should have called cmd.run_all once assert len(cmd.mock_calls) == 1 # Check args - assert cmd.mock_calls[0][1] == (['yum', '--quiet', '--showduplicates', - 'repository-packages', 'base', 'list', 'foo'],) + assert cmd.mock_calls[0][1] == ( + [ + "yum", + "--quiet", + "--showduplicates", + "repository-packages", + "base", + "list", + "foo", + ], + ) # Check kwargs assert cmd.mock_calls[0][2] == kwargs @@ -498,21 +600,32 @@ class YumTestCase(TestCase, LoaderModuleMockMixin): # do them in different orders, which is OK, but it will just # mean that we will have to check both the first and second # mock call both times. - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): yumpkg.list_repo_pkgs( - 'foo', - enablerepo='base-source', - disablerepo='updates') + "foo", enablerepo="base-source", disablerepo="updates" + ) # We should have called cmd.run_all twice assert len(cmd.mock_calls) == 2 - for repo in ('base', 'base-source'): + for repo in ("base", "base-source"): for index in (0, 1): try: # Check args - assert cmd.mock_calls[index][1] == (['yum', '--quiet', '--showduplicates', - 'repository-packages', repo, 'list', 'foo'],) + assert cmd.mock_calls[index][1] == ( + [ + "yum", + "--quiet", + "--showduplicates", + "repository-packages", + repo, + "list", + "foo", + ], + ) # Check kwargs assert cmd.mock_calls[index][2] == kwargs break @@ -522,380 +635,806 @@ class YumTestCase(TestCase, LoaderModuleMockMixin): self.fail("repo '{0}' not checked".format(repo)) def test_list_upgrades_dnf(self): - ''' + """ The subcommand should be "upgrades" with dnf - ''' - with patch.dict(yumpkg.__context__, {'yum_bin': 'dnf'}): + """ + with patch.dict(yumpkg.__context__, {"yum_bin": "dnf"}): # with fromrepo - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): - yumpkg.list_upgrades( - refresh=False, - fromrepo='good', - branch='foo') + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): + yumpkg.list_upgrades(refresh=False, fromrepo="good", branch="foo") cmd.assert_called_once_with( - ['dnf', '--quiet', '--disablerepo=*', '--enablerepo=good', - '--branch=foo', 'list', 'upgrades'], env={}, - output_loglevel='trace', + [ + "dnf", + "--quiet", + "--disablerepo=*", + "--enablerepo=good", + "--branch=foo", + "list", + "upgrades", + ], + env={}, + output_loglevel="trace", ignore_retcode=True, - python_shell=False) + python_shell=False, + ) # without fromrepo - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): yumpkg.list_upgrades( - refresh=False, - enablerepo='good', - disablerepo='bad', - branch='foo') + refresh=False, enablerepo="good", disablerepo="bad", branch="foo" + ) cmd.assert_called_once_with( - ['dnf', '--quiet', '--disablerepo=bad', '--enablerepo=good', - '--branch=foo', 'list', 'upgrades'], env={}, - output_loglevel='trace', + [ + "dnf", + "--quiet", + "--disablerepo=bad", + "--enablerepo=good", + "--branch=foo", + "list", + "upgrades", + ], + env={}, + output_loglevel="trace", ignore_retcode=True, - python_shell=False) + python_shell=False, + ) def test_list_upgrades_yum(self): - ''' + """ The subcommand should be "updates" with yum - ''' + """ # with fromrepo - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): - yumpkg.list_upgrades( - refresh=False, - fromrepo='good', - branch='foo') + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): + yumpkg.list_upgrades(refresh=False, fromrepo="good", branch="foo") cmd.assert_called_once_with( - ['yum', '--quiet', '--disablerepo=*', '--enablerepo=good', - '--branch=foo', 'list', 'updates'], env={}, - output_loglevel='trace', + [ + "yum", + "--quiet", + "--disablerepo=*", + "--enablerepo=good", + "--branch=foo", + "list", + "updates", + ], + env={}, + output_loglevel="trace", ignore_retcode=True, - python_shell=False) + python_shell=False, + ) # without fromrepo - cmd = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd, 'config.get': MagicMock(return_value=False)}): + cmd = MagicMock(return_value={"retcode": 0, "stdout": ""}) + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": cmd, "config.get": MagicMock(return_value=False)}, + ): yumpkg.list_upgrades( - refresh=False, - enablerepo='good', - disablerepo='bad', - branch='foo') + refresh=False, enablerepo="good", disablerepo="bad", branch="foo" + ) cmd.assert_called_once_with( - ['yum', '--quiet', '--disablerepo=bad', '--enablerepo=good', - '--branch=foo', 'list', 'updates'], env={}, - output_loglevel='trace', + [ + "yum", + "--quiet", + "--disablerepo=bad", + "--enablerepo=good", + "--branch=foo", + "list", + "updates", + ], + env={}, + output_loglevel="trace", ignore_retcode=True, - python_shell=False) + python_shell=False, + ) def test_refresh_db_with_options(self): - with patch('salt.utils.pkg.clear_rtag', Mock()): + with patch("salt.utils.pkg.clear_rtag", Mock()): # With check_update=True we will do a cmd.run to run the clean_cmd, and # then a separate cmd.retcode to check for updates. # with fromrepo yum_call = MagicMock() - with patch.dict(yumpkg.__salt__, {'cmd.run_all': yum_call, 'config.get': MagicMock(return_value=False)}): - yumpkg.refresh_db( - check_update=True, - fromrepo='good', - branch='foo') + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": yum_call, "config.get": MagicMock(return_value=False)}, + ): + yumpkg.refresh_db(check_update=True, fromrepo="good", branch="foo") assert yum_call.call_count == 2 - yum_call.assert_any_call(['yum', '--quiet', '--assumeyes', 'clean', 'expire-cache', '--disablerepo=*', - '--enablerepo=good', '--branch=foo'], - env={}, ignore_retcode=True, output_loglevel='trace', python_shell=False) - yum_call.assert_any_call(['yum', '--quiet', '--assumeyes', 'check-update', - '--setopt=autocheck_running_kernel=false', '--disablerepo=*', - '--enablerepo=good', '--branch=foo'], - output_loglevel='trace', env={}, - ignore_retcode=True, - python_shell=False) + yum_call.assert_any_call( + [ + "yum", + "--quiet", + "--assumeyes", + "clean", + "expire-cache", + "--disablerepo=*", + "--enablerepo=good", + "--branch=foo", + ], + env={}, + ignore_retcode=True, + output_loglevel="trace", + python_shell=False, + ) + yum_call.assert_any_call( + [ + "yum", + "--quiet", + "--assumeyes", + "check-update", + "--setopt=autocheck_running_kernel=false", + "--disablerepo=*", + "--enablerepo=good", + "--branch=foo", + ], + output_loglevel="trace", + env={}, + ignore_retcode=True, + python_shell=False, + ) # without fromrepo yum_call = MagicMock() - with patch.dict(yumpkg.__salt__, {'cmd.run_all': yum_call, 'config.get': MagicMock(return_value=False)}): + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": yum_call, "config.get": MagicMock(return_value=False)}, + ): yumpkg.refresh_db( check_update=True, - enablerepo='good', - disablerepo='bad', - branch='foo') + enablerepo="good", + disablerepo="bad", + branch="foo", + ) assert yum_call.call_count == 2 yum_call.assert_any_call( - ['yum', '--quiet', '--assumeyes', 'clean', 'expire-cache', '--disablerepo=bad', '--enablerepo=good', - '--branch=foo'], env={}, ignore_retcode=True, output_loglevel='trace', python_shell=False) + [ + "yum", + "--quiet", + "--assumeyes", + "clean", + "expire-cache", + "--disablerepo=bad", + "--enablerepo=good", + "--branch=foo", + ], + env={}, + ignore_retcode=True, + output_loglevel="trace", + python_shell=False, + ) yum_call.assert_any_call( - ['yum', '--quiet', '--assumeyes', 'check-update', '--setopt=autocheck_running_kernel=false', - '--disablerepo=bad', '--enablerepo=good', '--branch=foo'], - output_loglevel='trace', env={}, ignore_retcode=True, python_shell=False) + [ + "yum", + "--quiet", + "--assumeyes", + "check-update", + "--setopt=autocheck_running_kernel=false", + "--disablerepo=bad", + "--enablerepo=good", + "--branch=foo", + ], + output_loglevel="trace", + env={}, + ignore_retcode=True, + python_shell=False, + ) # With check_update=False we will just do a cmd.run for the clean_cmd # with fromrepo yum_call = MagicMock() - with patch.dict(yumpkg.__salt__, {'cmd.run_all': yum_call, 'config.get': MagicMock(return_value=False)}): - yumpkg.refresh_db( - check_update=False, - fromrepo='good', - branch='foo') + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": yum_call, "config.get": MagicMock(return_value=False)}, + ): + yumpkg.refresh_db(check_update=False, fromrepo="good", branch="foo") assert yum_call.call_count == 1 yum_call.assert_called_once_with( - ['yum', '--quiet', '--assumeyes', 'clean', 'expire-cache', - '--disablerepo=*', '--enablerepo=good', '--branch=foo'], - env={}, output_loglevel='trace', ignore_retcode=True, python_shell=False) + [ + "yum", + "--quiet", + "--assumeyes", + "clean", + "expire-cache", + "--disablerepo=*", + "--enablerepo=good", + "--branch=foo", + ], + env={}, + output_loglevel="trace", + ignore_retcode=True, + python_shell=False, + ) # without fromrepo yum_call = MagicMock() - with patch.dict(yumpkg.__salt__, {'cmd.run_all': yum_call, 'config.get': MagicMock(return_value=False)}): + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": yum_call, "config.get": MagicMock(return_value=False)}, + ): yumpkg.refresh_db( check_update=False, - enablerepo='good', - disablerepo='bad', - branch='foo') + enablerepo="good", + disablerepo="bad", + branch="foo", + ) assert yum_call.call_count == 1 yum_call.assert_called_once_with( - ['yum', '--quiet', '--assumeyes', 'clean', 'expire-cache', - '--disablerepo=bad', '--enablerepo=good', '--branch=foo'], - env={}, output_loglevel='trace', ignore_retcode=True, python_shell=False) + [ + "yum", + "--quiet", + "--assumeyes", + "clean", + "expire-cache", + "--disablerepo=bad", + "--enablerepo=good", + "--branch=foo", + ], + env={}, + output_loglevel="trace", + ignore_retcode=True, + python_shell=False, + ) def test_install_with_options(self): - parse_targets = MagicMock(return_value=({'foo': None}, 'repository')) - with patch.object(yumpkg, 'list_pkgs', MagicMock(return_value={})), \ - patch.object(yumpkg, 'list_holds', MagicMock(return_value=[])), \ - patch.dict(yumpkg.__salt__, {'pkg_resource.parse_targets': parse_targets}), \ - patch('salt.utils.systemd.has_scope', MagicMock(return_value=False)): + parse_targets = MagicMock(return_value=({"foo": None}, "repository")) + with patch.object( + yumpkg, "list_pkgs", MagicMock(return_value={}) + ), patch.object(yumpkg, "list_holds", MagicMock(return_value=[])), patch.dict( + yumpkg.__salt__, {"pkg_resource.parse_targets": parse_targets} + ), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ): # with fromrepo - cmd = MagicMock(return_value={'retcode': 0}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}): + cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict(yumpkg.__salt__, {"cmd.run_all": cmd}): yumpkg.install( refresh=False, - fromrepo='good', - branch='foo', - setopt='obsoletes=0,plugins=0') + fromrepo="good", + branch="foo", + setopt="obsoletes=0,plugins=0", + ) cmd.assert_called_once_with( - ['yum', '-y', '--disablerepo=*', '--enablerepo=good', - '--branch=foo', '--setopt', 'obsoletes=0', - '--setopt', 'plugins=0', 'install', 'foo'], env={}, - output_loglevel='trace', + [ + "yum", + "-y", + "--disablerepo=*", + "--enablerepo=good", + "--branch=foo", + "--setopt", + "obsoletes=0", + "--setopt", + "plugins=0", + "install", + "foo", + ], + env={}, + output_loglevel="trace", python_shell=False, ignore_retcode=False, - redirect_stderr=True) + redirect_stderr=True, + ) # without fromrepo - cmd = MagicMock(return_value={'retcode': 0}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}): + cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict(yumpkg.__salt__, {"cmd.run_all": cmd}): yumpkg.install( refresh=False, - enablerepo='good', - disablerepo='bad', - branch='foo', - setopt='obsoletes=0,plugins=0') + enablerepo="good", + disablerepo="bad", + branch="foo", + setopt="obsoletes=0,plugins=0", + ) cmd.assert_called_once_with( - ['yum', '-y', '--disablerepo=bad', '--enablerepo=good', - '--branch=foo', '--setopt', 'obsoletes=0', - '--setopt', 'plugins=0', 'install', 'foo'], env={}, - output_loglevel='trace', + [ + "yum", + "-y", + "--disablerepo=bad", + "--enablerepo=good", + "--branch=foo", + "--setopt", + "obsoletes=0", + "--setopt", + "plugins=0", + "install", + "foo", + ], + env={}, + output_loglevel="trace", python_shell=False, ignore_retcode=False, redirect_stderr=True, ) def test_install_with_epoch(self): - ''' + """ Tests that we properly identify a version containing an epoch as an upgrade instead of a downgrade. - ''' - name = 'foo' - old = '8:3.8.12-6.n.el7' - new = '9:3.8.12-4.n.el7' - list_pkgs_mock = MagicMock(side_effect=lambda **kwargs: {name: [old] if kwargs.get('versions_as_list', False) else old}) - cmd_mock = MagicMock(return_value={ - 'pid': 12345, - 'retcode': 0, - 'stdout': '', - 'stderr': '', - }) + """ + name = "foo" + old = "8:3.8.12-6.n.el7" + new = "9:3.8.12-4.n.el7" + list_pkgs_mock = MagicMock( + side_effect=lambda **kwargs: { + name: [old] if kwargs.get("versions_as_list", False) else old + } + ) + cmd_mock = MagicMock( + return_value={"pid": 12345, "retcode": 0, "stdout": "", "stderr": ""} + ) salt_mock = { - 'cmd.run_all': cmd_mock, - 'lowpkg.version_cmp': rpm.version_cmp, - 'pkg_resource.parse_targets': MagicMock(return_value=( - {name: new}, 'repository' - )), + "cmd.run_all": cmd_mock, + "lowpkg.version_cmp": rpm.version_cmp, + "pkg_resource.parse_targets": MagicMock( + return_value=({name: new}, "repository") + ), } - full_pkg_string = '-'.join((name, new[2:])) - with patch.object(yumpkg, 'list_pkgs', list_pkgs_mock), \ - patch('salt.utils.systemd.has_scope', - MagicMock(return_value=False)), \ - patch.dict(yumpkg.__salt__, salt_mock): + full_pkg_string = "-".join((name, new[2:])) + with patch.object(yumpkg, "list_pkgs", list_pkgs_mock), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ), patch.dict(yumpkg.__salt__, salt_mock): # Test yum - expected = ['yum', '-y', 'install', full_pkg_string] - with patch.dict(yumpkg.__grains__, {'os': 'CentOS', 'osrelease': 7}): - yumpkg.install('foo', version=new) + expected = ["yum", "-y", "install", full_pkg_string] + with patch.dict(yumpkg.__grains__, {"os": "CentOS", "osrelease": 7}): + yumpkg.install("foo", version=new) call = cmd_mock.mock_calls[0][1][0] assert call == expected, call # Test dnf - expected = ['dnf', '-y', '--best', '--allowerasing', - 'install', full_pkg_string] - yumpkg.__context__.pop('yum_bin') + expected = [ + "dnf", + "-y", + "--best", + "--allowerasing", + "install", + full_pkg_string, + ] + yumpkg.__context__.pop("yum_bin") cmd_mock.reset_mock() - with patch.dict(yumpkg.__grains__, {'os': 'Fedora', 'osrelease': 27}): - yumpkg.install('foo', version=new) + with patch.dict(yumpkg.__grains__, {"os": "Fedora", "osrelease": 27}): + yumpkg.install("foo", version=new) call = cmd_mock.mock_calls[0][1][0] assert call == expected, call def test_upgrade_with_options(self): - with patch.object(yumpkg, 'list_pkgs', MagicMock(return_value={})), \ - patch('salt.utils.systemd.has_scope', MagicMock(return_value=False)): + with patch.object(yumpkg, "list_pkgs", MagicMock(return_value={})), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ): # with fromrepo - cmd = MagicMock(return_value={'retcode': 0}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}): + cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict(yumpkg.__salt__, {"cmd.run_all": cmd}): yumpkg.upgrade( refresh=False, - fromrepo='good', - exclude='kernel*', - branch='foo', - setopt='obsoletes=0,plugins=0') + fromrepo="good", + exclude="kernel*", + branch="foo", + setopt="obsoletes=0,plugins=0", + ) cmd.assert_called_once_with( - ['yum', '--quiet', '-y', '--disablerepo=*', - '--enablerepo=good', '--branch=foo', - '--setopt', 'obsoletes=0', '--setopt', 'plugins=0', - '--exclude=kernel*', 'upgrade'], env={}, - output_loglevel='trace', - python_shell=False) + [ + "yum", + "--quiet", + "-y", + "--disablerepo=*", + "--enablerepo=good", + "--branch=foo", + "--setopt", + "obsoletes=0", + "--setopt", + "plugins=0", + "--exclude=kernel*", + "upgrade", + ], + env={}, + output_loglevel="trace", + python_shell=False, + ) # without fromrepo - cmd = MagicMock(return_value={'retcode': 0}) - with patch.dict(yumpkg.__salt__, {'cmd.run_all': cmd}): + cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict(yumpkg.__salt__, {"cmd.run_all": cmd}): yumpkg.upgrade( refresh=False, - enablerepo='good', - disablerepo='bad', - exclude='kernel*', - branch='foo', - setopt='obsoletes=0,plugins=0') + enablerepo="good", + disablerepo="bad", + exclude="kernel*", + branch="foo", + setopt="obsoletes=0,plugins=0", + ) cmd.assert_called_once_with( - ['yum', '--quiet', '-y', '--disablerepo=bad', - '--enablerepo=good', '--branch=foo', - '--setopt', 'obsoletes=0', '--setopt', 'plugins=0', - '--exclude=kernel*', 'upgrade'], env={}, - output_loglevel='trace', - python_shell=False) + [ + "yum", + "--quiet", + "-y", + "--disablerepo=bad", + "--enablerepo=good", + "--branch=foo", + "--setopt", + "obsoletes=0", + "--setopt", + "plugins=0", + "--exclude=kernel*", + "upgrade", + ], + env={}, + output_loglevel="trace", + python_shell=False, + ) def test_info_installed_with_all_versions(self): - ''' + """ Test the return information of all versions for the named package(s), installed on the system. :return: - ''' + """ run_out = { - 'virgo-dummy': [ - {'build_date': '2015-07-09T10:55:19Z', - 'vendor': 'openSUSE Build Service', - 'description': 'This is the Virgo dummy package used for testing SUSE Manager', - 'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com', - 'build_date_time_t': 1436432119, 'relocations': '(not relocatable)', - 'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z', - 'install_date_time_t': 1456241517, 'summary': 'Virgo dummy package', 'version': '1.0', - 'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9', - 'release': '1.1', 'group': 'Applications/System', 'arch': 'i686', 'size': '17992'}, - {'build_date': '2015-07-09T10:15:19Z', - 'vendor': 'openSUSE Build Service', - 'description': 'This is the Virgo dummy package used for testing SUSE Manager', - 'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com', - 'build_date_time_t': 1436432119, 'relocations': '(not relocatable)', - 'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z', - 'install_date_time_t': 14562415127, 'summary': 'Virgo dummy package', 'version': '1.0', - 'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9', - 'release': '1.1', 'group': 'Applications/System', 'arch': 'x86_64', 'size': '13124'} + "virgo-dummy": [ + { + "build_date": "2015-07-09T10:55:19Z", + "vendor": "openSUSE Build Service", + "description": "This is the Virgo dummy package used for testing SUSE Manager", + "license": "GPL-2.0", + "build_host": "sheep05", + "url": "http://www.suse.com", + "build_date_time_t": 1436432119, + "relocations": "(not relocatable)", + "source_rpm": "virgo-dummy-1.0-1.1.src.rpm", + "install_date": "2016-02-23T16:31:57Z", + "install_date_time_t": 1456241517, + "summary": "Virgo dummy package", + "version": "1.0", + "signature": "DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9", + "release": "1.1", + "group": "Applications/System", + "arch": "i686", + "size": "17992", + }, + { + "build_date": "2015-07-09T10:15:19Z", + "vendor": "openSUSE Build Service", + "description": "This is the Virgo dummy package used for testing SUSE Manager", + "license": "GPL-2.0", + "build_host": "sheep05", + "url": "http://www.suse.com", + "build_date_time_t": 1436432119, + "relocations": "(not relocatable)", + "source_rpm": "virgo-dummy-1.0-1.1.src.rpm", + "install_date": "2016-02-23T16:31:57Z", + "install_date_time_t": 14562415127, + "summary": "Virgo dummy package", + "version": "1.0", + "signature": "DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9", + "release": "1.1", + "group": "Applications/System", + "arch": "x86_64", + "size": "13124", + }, + ], + "libopenssl1_0_0": [ + { + "build_date": "2015-11-04T23:20:34Z", + "vendor": "SUSE LLC <https://www.suse.com/>", + "description": "The OpenSSL Project is a collaborative effort.", + "license": "OpenSSL", + "build_host": "sheep11", + "url": "https://www.openssl.org/", + "build_date_time_t": 1446675634, + "relocations": "(not relocatable)", + "source_rpm": "openssl-1.0.1i-34.1.src.rpm", + "install_date": "2016-02-23T16:31:35Z", + "install_date_time_t": 1456241495, + "summary": "Secure Sockets and Transport Layer Security", + "version": "1.0.1i", + "signature": "RSA/SHA256, Wed Nov 4 22:21:34 2015, Key ID 70af9e8139db7c82", + "release": "34.1", + "group": "Productivity/Networking/Security", + "packager": "https://www.suse.com/", + "arch": "x86_64", + "size": "2576912", + } ], - 'libopenssl1_0_0': [ - {'build_date': '2015-11-04T23:20:34Z', 'vendor': 'SUSE LLC <https://www.suse.com/>', - 'description': 'The OpenSSL Project is a collaborative effort.', - 'license': 'OpenSSL', 'build_host': 'sheep11', 'url': 'https://www.openssl.org/', - 'build_date_time_t': 1446675634, 'relocations': '(not relocatable)', - 'source_rpm': 'openssl-1.0.1i-34.1.src.rpm', 'install_date': '2016-02-23T16:31:35Z', - 'install_date_time_t': 1456241495, 'summary': 'Secure Sockets and Transport Layer Security', - 'version': '1.0.1i', 'signature': 'RSA/SHA256, Wed Nov 4 22:21:34 2015, Key ID 70af9e8139db7c82', - 'release': '34.1', 'group': 'Productivity/Networking/Security', 'packager': 'https://www.suse.com/', - 'arch': 'x86_64', 'size': '2576912'} - ] } - with patch.dict(yumpkg.__salt__, {'lowpkg.info': MagicMock(return_value=run_out)}): + with patch.dict( + yumpkg.__salt__, {"lowpkg.info": MagicMock(return_value=run_out)} + ): installed = yumpkg.info_installed(all_versions=True) # Test overall products length self.assertEqual(len(installed), 2) # Test multiple versions for the same package for pkg_name, pkg_info_list in installed.items(): - self.assertEqual(len(pkg_info_list), 2 if pkg_name == "virgo-dummy" else 1) + self.assertEqual( + len(pkg_info_list), 2 if pkg_name == "virgo-dummy" else 1 + ) for info in pkg_info_list: - self.assertTrue(info['arch'] in ('x86_64', 'i686')) + self.assertTrue(info["arch"] in ("x86_64", "i686")) - @skipIf(not yumpkg.HAS_YUM, 'Could not import yum') + def test_pkg_hold_yum(self): + """ + Tests that we properly identify versionlock plugin when using yum + for RHEL/CentOS 7 and Fedora < 22 + """ + + # Test RHEL/CentOS 7 + list_pkgs_mock = { + "yum-plugin-versionlock": "0:1.0.0-0.n.el7", + "yum-versionlock": "0:1.0.0-0.n.el7", + } + + cmd = MagicMock(return_value={"retcode": 0}) + with patch.object( + yumpkg, "list_pkgs", MagicMock(return_value=list_pkgs_mock) + ), patch.object(yumpkg, "list_holds", MagicMock(return_value=[])), patch.dict( + yumpkg.__salt__, {"cmd.run_all": cmd} + ), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ): + yumpkg.hold("foo") + cmd.assert_called_once_with( + ["yum", "versionlock", "foo"], + env={}, + output_loglevel="trace", + python_shell=False, + ) + + # Test Fedora 20 + cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict( + yumpkg.__grains__, {"os": "Fedora", "osrelease": 20} + ), patch.object( + yumpkg, "list_pkgs", MagicMock(return_value=list_pkgs_mock) + ), patch.object( + yumpkg, "list_holds", MagicMock(return_value=[]) + ), patch.dict( + yumpkg.__salt__, {"cmd.run_all": cmd} + ), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ): + yumpkg.hold("foo") + cmd.assert_called_once_with( + ["yum", "versionlock", "foo"], + env={}, + output_loglevel="trace", + python_shell=False, + ) + + def test_pkg_hold_dnf(self): + """ + Tests that we properly identify versionlock plugin when using dnf + for RHEL/CentOS 8 and Fedora >= 22 + """ + + # Test RHEL/CentOS 8 + list_pkgs_mock = { + "python2-dnf-plugin-versionlock": "0:1.0.0-0.n.el8", + "python3-dnf-plugin-versionlock": "0:1.0.0-0.n.el8", + } + + yumpkg.__context__.pop("yum_bin") + cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict(yumpkg.__grains__, {"osmajorrelease": 8}), patch.object( + yumpkg, "list_pkgs", MagicMock(return_value=list_pkgs_mock) + ), patch.object(yumpkg, "list_holds", MagicMock(return_value=[])), patch.dict( + yumpkg.__salt__, {"cmd.run_all": cmd} + ), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ): + yumpkg.hold("foo") + cmd.assert_called_once_with( + ["dnf", "versionlock", "foo"], + env={}, + output_loglevel="trace", + python_shell=False, + ) + + # Test Fedora 26+ + yumpkg.__context__.pop("yum_bin") + cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict( + yumpkg.__grains__, {"os": "Fedora", "osrelease": 26} + ), patch.object( + yumpkg, "list_pkgs", MagicMock(return_value=list_pkgs_mock) + ), patch.object( + yumpkg, "list_holds", MagicMock(return_value=[]) + ), patch.dict( + yumpkg.__salt__, {"cmd.run_all": cmd} + ), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ): + yumpkg.hold("foo") + cmd.assert_called_once_with( + ["dnf", "versionlock", "foo"], + env={}, + output_loglevel="trace", + python_shell=False, + ) + + # Test Fedora 22-25 + list_pkgs_mock = { + "python-dnf-plugins-extras-versionlock": "0:1.0.0-0.n.el8", + "python3-dnf-plugins-extras-versionlock": "0:1.0.0-0.n.el8", + } + + yumpkg.__context__.pop("yum_bin") + cmd = MagicMock(return_value={"retcode": 0}) + with patch.dict( + yumpkg.__grains__, {"os": "Fedora", "osrelease": 25} + ), patch.object( + yumpkg, "list_pkgs", MagicMock(return_value=list_pkgs_mock) + ), patch.object( + yumpkg, "list_holds", MagicMock(return_value=[]) + ), patch.dict( + yumpkg.__salt__, {"cmd.run_all": cmd} + ), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ): + yumpkg.hold("foo") + cmd.assert_called_once_with( + ["dnf", "versionlock", "foo"], + env={}, + output_loglevel="trace", + python_shell=False, + ) + + @skipIf(not yumpkg.HAS_YUM, "Could not import yum") def test_yum_base_error(self): - with patch('yum.YumBase') as mock_yum_yumbase: + with patch("yum.YumBase") as mock_yum_yumbase: mock_yum_yumbase.side_effect = CommandExecutionError with pytest.raises(CommandExecutionError): yumpkg._get_yum_config() + def test_group_install(self): + """ + Test group_install uses the correct keys from group_info and installs + default and mandatory packages. + """ + groupinfo_output = """ + Group: Printing Client + Group-Id: print-client + Description: Tools for printing to a local printer or a remote print server. + Mandatory Packages: + +cups + +cups-pk-helper + +enscript + +ghostscript-cups + Default Packages: + +colord + +gutenprint + +gutenprint-cups + +hpijs + +paps + +pnm2ppa + +python-smbc + +system-config-printer + +system-config-printer-udev + Optional Packages: + hplip + hplip-gui + samba-krb5-printing + """ + install = MagicMock() + with patch.dict( + yumpkg.__salt__, + {"cmd.run_stdout": MagicMock(return_value=groupinfo_output)}, + ): + with patch.dict(yumpkg.__salt__, {"cmd.run": MagicMock(return_value="")}): + with patch.dict( + yumpkg.__salt__, + {"pkg_resource.format_pkg_list": MagicMock(return_value={})}, + ): + with patch.object(yumpkg, "install", install): + yumpkg.group_install("Printing Client") + install.assert_called_once_with( + pkgs=[ + "cups", + "cups-pk-helper", + "enscript", + "ghostscript-cups", + "colord", + "gutenprint", + "gutenprint-cups", + "hpijs", + "paps", + "pnm2ppa", + "python-smbc", + "system-config-printer", + "system-config-printer-udev", + ] + ) -@skipIf(pytest is None, 'PyTest is missing') + +@skipIf(pytest is None, "PyTest is missing") class YumUtilsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Yum/Dnf utils tests. - ''' + """ + def setup_loader_modules(self): return { yumpkg: { - '__context__': { - 'yum_bin': 'fake-yum', - }, - '__grains__': { - 'osarch': 'x86_64', - 'os_family': 'RedHat', - 'osmajorrelease': 7, + "__context__": {"yum_bin": "fake-yum"}, + "__grains__": { + "osarch": "x86_64", + "os_family": "RedHat", + "osmajorrelease": 7, }, } } def test_call_yum_default(self): - ''' + """ Call default Yum/Dnf. :return: - ''' - with patch.dict(yumpkg.__salt__, {'cmd.run_all': MagicMock(), 'config.get': MagicMock(return_value=False)}): - yumpkg._call_yum(['-y', '--do-something']) # pylint: disable=W0106 - yumpkg.__salt__['cmd.run_all'].assert_called_once_with( - ['fake-yum', '-y', '--do-something'], env={}, - output_loglevel='trace', python_shell=False) + """ + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=False)}, + ): + yumpkg._call_yum(["-y", "--do-something"]) # pylint: disable=W0106 + yumpkg.__salt__["cmd.run_all"].assert_called_once_with( + ["fake-yum", "-y", "--do-something"], + env={}, + output_loglevel="trace", + python_shell=False, + ) - @patch('salt.utils.systemd.has_scope', MagicMock(return_value=True)) + @patch("salt.utils.systemd.has_scope", MagicMock(return_value=True)) def test_call_yum_in_scope(self): - ''' + """ Call Yum/Dnf within the scope. :return: - ''' - with patch.dict(yumpkg.__salt__, {'cmd.run_all': MagicMock(), 'config.get': MagicMock(return_value=True)}): - yumpkg._call_yum(['-y', '--do-something']) # pylint: disable=W0106 - yumpkg.__salt__['cmd.run_all'].assert_called_once_with( - ['systemd-run', '--scope', 'fake-yum', '-y', '--do-something'], env={}, - output_loglevel='trace', python_shell=False) + """ + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=True)}, + ): + yumpkg._call_yum(["-y", "--do-something"]) # pylint: disable=W0106 + yumpkg.__salt__["cmd.run_all"].assert_called_once_with( + ["systemd-run", "--scope", "fake-yum", "-y", "--do-something"], + env={}, + output_loglevel="trace", + python_shell=False, + ) def test_call_yum_with_kwargs(self): - ''' + """ Call Yum/Dnf with the optinal keyword arguments. :return: - ''' - with patch.dict(yumpkg.__salt__, {'cmd.run_all': MagicMock(), 'config.get': MagicMock(return_value=False)}): - yumpkg._call_yum(['-y', '--do-something'], - python_shell=True, output_loglevel='quiet', ignore_retcode=False, - username='Darth Vader') # pylint: disable=W0106 - yumpkg.__salt__['cmd.run_all'].assert_called_once_with( - ['fake-yum', '-y', '--do-something'], env={}, ignore_retcode=False, - output_loglevel='quiet', python_shell=True, username='Darth Vader') + """ + with patch.dict( + yumpkg.__salt__, + {"cmd.run_all": MagicMock(), "config.get": MagicMock(return_value=False)}, + ): + yumpkg._call_yum( + ["-y", "--do-something"], + python_shell=True, + output_loglevel="quiet", + ignore_retcode=False, + username="Darth Vader", + ) # pylint: disable=W0106 + yumpkg.__salt__["cmd.run_all"].assert_called_once_with( + ["fake-yum", "-y", "--do-something"], + env={}, + ignore_retcode=False, + output_loglevel="quiet", + python_shell=True, + username="Darth Vader", + ) diff --git a/tests/unit/modules/test_zabbix.py b/tests/unit/modules/test_zabbix.py index d88732d8e74..2221188236f 100644 --- a/tests/unit/modules/test_zabbix.py +++ b/tests/unit/modules/test_zabbix.py @@ -1,323 +1,319 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Christian McHugh <christian.mchugh@gmail.com>` -''' +""" # Import Python Libs -from __future__ import absolute_import -from __future__ import unicode_literals +from __future__ import absolute_import, unicode_literals + import salt.modules.zabbix as zabbix +from salt.exceptions import SaltException # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - -from salt.exceptions import SaltException CONN_ARGS = {} -CONN_ARGS['url'] = 'http://test.url' -CONN_ARGS['auth'] = '1234' +CONN_ARGS["url"] = "http://test.url" +CONN_ARGS["auth"] = "1234" -GETID_QUERY_RESULT_OK = [{'internal': '0', 'flags': '0', 'groupid': '11', 'name': 'Databases'}] -GETID_QUERY_RESULT_BAD = [{'internal': '0', 'flags': '0', 'groupid': '11', 'name': 'Databases'}, {'another': 'object'}] +GETID_QUERY_RESULT_OK = [ + {"internal": "0", "flags": "0", "groupid": "11", "name": "Databases"} +] +GETID_QUERY_RESULT_BAD = [ + {"internal": "0", "flags": "0", "groupid": "11", "name": "Databases"}, + {"another": "object"}, +] -DEFINED_PARAMS = {'name': 'beta', - 'eventsource': 2, - 'status': 0, - 'filter': {'evaltype': 2, - 'conditions': [{'conditiontype': 24, - 'operator': 2, - 'value': 'db'}]}, - 'operations': [{'operationtype': 2}, - {'operationtype': 4, - 'opgroup': [{'groupid': {'query_object': 'hostgroup', - 'query_name': 'Databases'}}]}], - 'empty_list': []} +DEFINED_PARAMS = { + "name": "beta", + "eventsource": 2, + "status": 0, + "filter": { + "evaltype": 2, + "conditions": [{"conditiontype": 24, "operator": 2, "value": "db"}], + }, + "operations": [ + {"operationtype": 2}, + { + "operationtype": 4, + "opgroup": [ + {"groupid": {"query_object": "hostgroup", "query_name": "Databases"}} + ], + }, + ], + "empty_list": [], +} -SUBSTITUTED_DEFINED_PARAMS = {'status': '0', - 'filter': {'evaltype': '2', - 'conditions': [{'operator': '2', - 'conditiontype': '24', - 'value': 'db'}]}, - 'eventsource': '2', - 'name': 'beta', - 'operations': [{'operationtype': '2'}, - {'opgroup': [{'groupid': '11'}], - 'operationtype': '4'}], - 'empty_list': []} +SUBSTITUTED_DEFINED_PARAMS = { + "status": "0", + "filter": { + "evaltype": "2", + "conditions": [{"operator": "2", "conditiontype": "24", "value": "db"}], + }, + "eventsource": "2", + "name": "beta", + "operations": [ + {"operationtype": "2"}, + {"opgroup": [{"groupid": "11"}], "operationtype": "4"}, + ], + "empty_list": [], +} -EXISTING_OBJECT_PARAMS = {'status': '0', - 'operations': [{'operationtype': '2', 'esc_period': '0', 'evaltype': '0', 'opconditions': [], - 'esc_step_to': '1', 'actionid': '23', 'esc_step_from': '1', - 'operationid': '64'}, - {'operationtype': '4', 'esc_period': '0', 'evaltype': '0', 'opconditions': [], - 'esc_step_to': '1', 'actionid': '23', 'esc_step_from': '1', - 'opgroup': [{'groupid': '11', - 'operationid': '65'}], - 'operationid': '65'}], - 'def_shortdata': '', - 'name': 'beta', - 'esc_period': '0', - 'def_longdata': '', - 'filter': {'formula': '', - 'evaltype': '2', - 'conditions': [{'operator': '2', - 'conditiontype': '24', - 'formulaid': 'A', - 'value': 'DIFFERENT VALUE HERE'}], - 'eval_formula': 'A'}, - 'eventsource': '2', - 'actionid': '23', - 'r_shortdata': '', - 'r_longdata': '', - 'recovery_msg': '0', - 'empty_list': [{'dict_key': 'dic_val'}]} +EXISTING_OBJECT_PARAMS = { + "status": "0", + "operations": [ + { + "operationtype": "2", + "esc_period": "0", + "evaltype": "0", + "opconditions": [], + "esc_step_to": "1", + "actionid": "23", + "esc_step_from": "1", + "operationid": "64", + }, + { + "operationtype": "4", + "esc_period": "0", + "evaltype": "0", + "opconditions": [], + "esc_step_to": "1", + "actionid": "23", + "esc_step_from": "1", + "opgroup": [{"groupid": "11", "operationid": "65"}], + "operationid": "65", + }, + ], + "def_shortdata": "", + "name": "beta", + "esc_period": "0", + "def_longdata": "", + "filter": { + "formula": "", + "evaltype": "2", + "conditions": [ + { + "operator": "2", + "conditiontype": "24", + "formulaid": "A", + "value": "DIFFERENT VALUE HERE", + } + ], + "eval_formula": "A", + }, + "eventsource": "2", + "actionid": "23", + "r_shortdata": "", + "r_longdata": "", + "recovery_msg": "0", + "empty_list": [{"dict_key": "dic_val"}], +} -DIFF_PARAMS_RESULT = {'filter': {'evaltype': '2', - 'conditions': [{'operator': '2', - 'conditiontype': '24', - 'value': 'db'}]}, - 'empty_list': []} +DIFF_PARAMS_RESULT = { + "filter": { + "evaltype": "2", + "conditions": [{"operator": "2", "conditiontype": "24", "value": "db"}], + }, + "empty_list": [], +} -DIFF_PARAMS_RESULT_WITH_ROLLBACK = {'new': DIFF_PARAMS_RESULT, - 'old': {'filter': {'formula': '', - 'evaltype': '2', - 'conditions': [{'operator': '2', - 'conditiontype': '24', - 'formulaid': 'A', - 'value': 'DIFFERENT VALUE HERE'}], - 'eval_formula': 'A'}, - 'empty_list': [{'dict_key': 'dic_val'}]}} +DIFF_PARAMS_RESULT_WITH_ROLLBACK = { + "new": DIFF_PARAMS_RESULT, + "old": { + "filter": { + "formula": "", + "evaltype": "2", + "conditions": [ + { + "operator": "2", + "conditiontype": "24", + "formulaid": "A", + "value": "DIFFERENT VALUE HERE", + } + ], + "eval_formula": "A", + }, + "empty_list": [{"dict_key": "dic_val"}], + }, +} class ZabbixTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.zabbix - ''' + """ def setup_loader_modules(self): - return {zabbix: {'__salt__': {'cmd.which_bin': lambda _: 'zabbix_server'}}} + return {zabbix: {"__salt__": {"cmd.which_bin": lambda _: "zabbix_server"}}} def test_get_object_id_by_params(self): - ''' + """ Test get_object_id function with expected result from API call - ''' - with patch('salt.modules.zabbix.run_query', MagicMock(return_value=GETID_QUERY_RESULT_OK)): - self.assertEqual(zabbix.get_object_id_by_params('hostgroup', 'Databases'), '11') + """ + with patch( + "salt.modules.zabbix.run_query", + MagicMock(return_value=GETID_QUERY_RESULT_OK), + ): + self.assertEqual( + zabbix.get_object_id_by_params("hostgroup", "Databases"), "11" + ) def test_get_obj_id_by_params_fail(self): - ''' + """ Test get_object_id function with unexpected result from API call - ''' - with patch('salt.modules.zabbix.run_query', MagicMock(return_value=GETID_QUERY_RESULT_BAD)): - self.assertRaises(SaltException, zabbix.get_object_id_by_params, 'hostgroup', 'Databases') + """ + with patch( + "salt.modules.zabbix.run_query", + MagicMock(return_value=GETID_QUERY_RESULT_BAD), + ): + self.assertRaises( + SaltException, zabbix.get_object_id_by_params, "hostgroup", "Databases" + ) def test_substitute_params(self): - ''' + """ Test proper parameter substitution for defined input - ''' - with patch('salt.modules.zabbix.get_object_id_by_params', MagicMock(return_value='11')): - self.assertEqual(zabbix.substitute_params(DEFINED_PARAMS), SUBSTITUTED_DEFINED_PARAMS) + """ + with patch( + "salt.modules.zabbix.get_object_id_by_params", MagicMock(return_value="11") + ): + self.assertEqual( + zabbix.substitute_params(DEFINED_PARAMS), SUBSTITUTED_DEFINED_PARAMS + ) def test_substitute_params_fail(self): - ''' + """ Test proper parameter substitution if there is needed parameter missing - ''' - self.assertRaises(SaltException, zabbix.substitute_params, {'groupid': {'query_object': 'hostgroup'}}) + """ + self.assertRaises( + SaltException, + zabbix.substitute_params, + {"groupid": {"query_object": "hostgroup"}}, + ) def test_compare_params(self): - ''' + """ Test result comparison of two params structures - ''' - self.assertEqual(zabbix.compare_params(SUBSTITUTED_DEFINED_PARAMS, EXISTING_OBJECT_PARAMS), - DIFF_PARAMS_RESULT) + """ + self.assertEqual( + zabbix.compare_params(SUBSTITUTED_DEFINED_PARAMS, EXISTING_OBJECT_PARAMS), + DIFF_PARAMS_RESULT, + ) def test_compare_params_rollback(self): - ''' + """ Test result comparison of two params structures with rollback return value option - ''' - self.assertEqual(zabbix.compare_params(SUBSTITUTED_DEFINED_PARAMS, EXISTING_OBJECT_PARAMS, True), - DIFF_PARAMS_RESULT_WITH_ROLLBACK) + """ + self.assertEqual( + zabbix.compare_params( + SUBSTITUTED_DEFINED_PARAMS, EXISTING_OBJECT_PARAMS, True + ), + DIFF_PARAMS_RESULT_WITH_ROLLBACK, + ) def test_compare_params_fail(self): - ''' + """ Test result comparison of two params structures where some data type mismatch exists - ''' - self.assertRaises(SaltException, zabbix.compare_params, {'dict': 'val'}, {'dict': ['list']}) + """ + self.assertRaises( + SaltException, zabbix.compare_params, {"dict": "val"}, {"dict": ["list"]} + ) def test_apiiinfo_version(self): - ''' + """ Test apiinfo_version - ''' + """ module_return = "3.4.5" query_return = {"jsonrpc": "2.0", "result": "3.4.5", "id": 1} - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): self.assertEqual(zabbix.apiinfo_version(**CONN_ARGS), module_return) def test_user_create(self): - ''' + """ query_submitted = {"params": {"passwd": "password007", "alias": "james", "name": "James Bond", "usrgrps": [{"usrgrpid": 7}, {"usrgrpid": 12}]}, "jsonrpc": "2.0", "id": 0, "auth": "f016981c4f0d3f8b9682e34588fe8a33", "method": "user.create"} - ''' + """ - module_return = ['3'] + module_return = ["3"] query_return = {"jsonrpc": "2.0", "result": {"userids": ["3"]}, "id": 0} - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.user_create('james', 'password007', '[7, 12]', - firstname='James Bond', **CONN_ARGS), module_return) + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.user_create( + "james", + "password007", + "[7, 12]", + firstname="James Bond", + **CONN_ARGS + ), + module_return, + ) def test_user_delete(self): - ''' + """ query_submitted = {"params": [3], "jsonrpc": "2.0", "id": 0, "auth": "68d38eace8e42a35c8d0c6a2ab0245a6", "method": "user.delete"} - ''' + """ - module_return = ['3'] + module_return = ["3"] query_return = {"jsonrpc": "2.0", "result": {"userids": ["3"]}, "id": 0} - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): self.assertEqual(zabbix.user_delete(3, **CONN_ARGS), module_return) def test_user_exists(self): - ''' + """ query_submitted = {"params": {"filter": {"alias": "Admin"}, "output": "extend"}, "jsonrpc": "2.0", "id": 0, "auth": "72435c7f754cb2adb4ecddc98216057f", "method": "user.get"} - ''' + """ module_return = True # pylint: disable=E8128 - query_return = {"jsonrpc": "2.0", "result": [{"userid": "1", "alias": "Admin", - "name": "Zabbix", "surname": "Administrator", "url": "", "autologin": "1", - "autologout": "0", "lang": "en_GB", "refresh": "30s", "type": "3", "theme": "default", - "attempt_failed": "0", "attempt_ip": "10.0.2.2", "attempt_clock": "1515922072", - "rows_per_page": "50"}], "id": 0} + query_return = { + "jsonrpc": "2.0", + "result": [ + { + "userid": "1", + "alias": "Admin", + "name": "Zabbix", + "surname": "Administrator", + "url": "", + "autologin": "1", + "autologout": "0", + "lang": "en_GB", + "refresh": "30s", + "type": "3", + "theme": "default", + "attempt_failed": "0", + "attempt_ip": "10.0.2.2", + "attempt_clock": "1515922072", + "rows_per_page": "50", + } + ], + "id": 0, + } - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.user_exists('Admin', **CONN_ARGS), module_return) + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.user_exists("Admin", **CONN_ARGS), module_return + ) def test_user_get(self): - ''' + """ query_submitted = {"params": {"filter": {"alias": "Admin"}, "output": "extend"}, "jsonrpc": "2.0", "id": 0, "auth": "49ef327f205d9e9150d4651cb6adc2d5", "method": "user.get"} - ''' - - module_return = [{ - "lang": "en_GB", - "rows_per_page": "50", - "surname": "Administrator", - "name": "Zabbix", - "url": "", - "attempt_clock": "1515922072", - "userid": "1", - "autologin": "1", - "refresh": "30s", - "attempt_failed": "0", - "alias": "Admin", - "theme": "default", - "autologout": "0", - "attempt_ip": "10.0.2.2", - "type": "3" - }] - # pylint: disable=E8128 - query_return = {"jsonrpc": "2.0", "result": [{"userid": "1", "alias": "Admin", - "name": "Zabbix", "surname": "Administrator", "url": "", "autologin": "1", - "autologout": "0", "lang": "en_GB", "refresh": "30s", "type": "3", "theme": "default", - "attempt_failed": "0", "attempt_ip": "10.0.2.2", "attempt_clock": "1515922072", - "rows_per_page": "50"}], "id": 0} - - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.user_get('Admin', **CONN_ARGS), module_return) - self.assertEqual(zabbix.user_get(userids='1', **CONN_ARGS), module_return) - - def test_user_update(self): - ''' - query_submitted = {"params": {"userid": 3, "name": "James Brown"}, "jsonrpc": "2.0", - "id": 0, "auth": "cdf2ee35e3bc47560585e9c457cbc398", "method": "user.update"} - ''' - - module_return = ['3'] - query_return = {"jsonrpc": "2.0", "result": {"userids": ["3"]}, "id": 0} - - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.user_update('3', visible_name='James Brown', **CONN_ARGS), module_return) - - def test_user_getmedia(self): - ''' - query_submitted = {"params": {"userids": 3}, "jsonrpc": "2.0", "id": 0, - "auth": "d4de741ea7cdd434b3ba7b56efa4efaf", "method": "usermedia.get"} - ''' - - module_return = [ - { - "mediatypeid": "1", - "mediaid": "1", - "severity": "63", - "userid": "3", - "period": "1-7,00:00-24:00", - "sendto": "email@example.com", - "active": "0" - } - ] - # pylint: disable=E8128 - query_return = {"jsonrpc": "2.0", "result": [{"mediaid": "1", "userid": "3", - "mediatypeid": "1", "sendto": "email@example.com", "active": "0", "severity": "63", - "period": "1-7,00:00-24:00"}], "id": 0} - - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.user_getmedia('3', **CONN_ARGS), module_return) - - def test_user_addmedia(self): - ''' - query_submitted = {"params": {"medias": [{"active": 0, "mediatypeid": 1, - "period": "1-7,00:00-24:00", "severity": 63, "sendto": "support2@example.com"}], - "users": [{"userid": 1}]}, "jsonrpc": "2.0", "id": 0, "auth": "b347fc1bf1f5617b93755619a037c19e", - "method": "user.addmedia"} - ''' - - module_return = ['2'] - query_return = {"jsonrpc": "2.0", "result": {"mediaids": ["2"]}, "id": 0} - - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.user_addmedia('1', active='0', mediatypeid='1', - period='1-7,00:00-24:00', sendto='support2@example.com', - severity='63', **CONN_ARGS), module_return) - - def test_user_deletemedia(self): - ''' - query_submitted = {"params": [1], "jsonrpc": "2.0", "id": 0, "auth": "9fb226c759a320de0de3b7a141404506", - "method": "user.deletemedia"} - ''' - - module_return = [1] - query_return = {"jsonrpc": "2.0", "result": {"mediaids": [1]}, "id": 0} - - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.user_deletemedia('1', **CONN_ARGS), module_return) - - def test_user_list(self): - ''' - query_submitted = {"params": {"output": "extend"}, "jsonrpc": "2.0", "id": 0, - "auth": "54d67b63c37e690cf06972678f1e9720", "method": "user.get"} - ''' + """ module_return = [ { @@ -335,7 +331,159 @@ class ZabbixTestCase(TestCase, LoaderModuleMockMixin): "theme": "default", "autologout": "0", "attempt_ip": "10.0.2.2", - "type": "3" + "type": "3", + } + ] + # pylint: disable=E8128 + query_return = { + "jsonrpc": "2.0", + "result": [ + { + "userid": "1", + "alias": "Admin", + "name": "Zabbix", + "surname": "Administrator", + "url": "", + "autologin": "1", + "autologout": "0", + "lang": "en_GB", + "refresh": "30s", + "type": "3", + "theme": "default", + "attempt_failed": "0", + "attempt_ip": "10.0.2.2", + "attempt_clock": "1515922072", + "rows_per_page": "50", + } + ], + "id": 0, + } + + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual(zabbix.user_get("Admin", **CONN_ARGS), module_return) + self.assertEqual( + zabbix.user_get(userids="1", **CONN_ARGS), module_return + ) + + def test_user_update(self): + """ + query_submitted = {"params": {"userid": 3, "name": "James Brown"}, "jsonrpc": "2.0", + "id": 0, "auth": "cdf2ee35e3bc47560585e9c457cbc398", "method": "user.update"} + """ + + module_return = ["3"] + query_return = {"jsonrpc": "2.0", "result": {"userids": ["3"]}, "id": 0} + + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.user_update("3", visible_name="James Brown", **CONN_ARGS), + module_return, + ) + + def test_user_getmedia(self): + """ + query_submitted = {"params": {"userids": 3}, "jsonrpc": "2.0", "id": 0, + "auth": "d4de741ea7cdd434b3ba7b56efa4efaf", "method": "usermedia.get"} + """ + + module_return = [ + { + "mediatypeid": "1", + "mediaid": "1", + "severity": "63", + "userid": "3", + "period": "1-7,00:00-24:00", + "sendto": "email@example.com", + "active": "0", + } + ] + # pylint: disable=E8128 + query_return = { + "jsonrpc": "2.0", + "result": [ + { + "mediaid": "1", + "userid": "3", + "mediatypeid": "1", + "sendto": "email@example.com", + "active": "0", + "severity": "63", + "period": "1-7,00:00-24:00", + } + ], + "id": 0, + } + + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual(zabbix.user_getmedia("3", **CONN_ARGS), module_return) + + def test_user_addmedia(self): + """ + query_submitted = {"params": {"medias": [{"active": 0, "mediatypeid": 1, + "period": "1-7,00:00-24:00", "severity": 63, "sendto": "support2@example.com"}], + "users": [{"userid": 1}]}, "jsonrpc": "2.0", "id": 0, "auth": "b347fc1bf1f5617b93755619a037c19e", + "method": "user.addmedia"} + """ + + module_return = ["2"] + query_return = {"jsonrpc": "2.0", "result": {"mediaids": ["2"]}, "id": 0} + + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.user_addmedia( + "1", + active="0", + mediatypeid="1", + period="1-7,00:00-24:00", + sendto="support2@example.com", + severity="63", + **CONN_ARGS + ), + module_return, + ) + + def test_user_deletemedia(self): + """ + query_submitted = {"params": [1], "jsonrpc": "2.0", "id": 0, "auth": "9fb226c759a320de0de3b7a141404506", + "method": "user.deletemedia"} + """ + + module_return = [1] + query_return = {"jsonrpc": "2.0", "result": {"mediaids": [1]}, "id": 0} + + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.user_deletemedia("1", **CONN_ARGS), module_return + ) + + def test_user_list(self): + """ + query_submitted = {"params": {"output": "extend"}, "jsonrpc": "2.0", "id": 0, + "auth": "54d67b63c37e690cf06972678f1e9720", "method": "user.get"} + """ + + module_return = [ + { + "lang": "en_GB", + "rows_per_page": "50", + "surname": "Administrator", + "name": "Zabbix", + "url": "", + "attempt_clock": "1515922072", + "userid": "1", + "autologin": "1", + "refresh": "30s", + "attempt_failed": "0", + "alias": "Admin", + "theme": "default", + "autologout": "0", + "attempt_ip": "10.0.2.2", + "type": "3", }, { "lang": "en_GB", @@ -352,7 +500,7 @@ class ZabbixTestCase(TestCase, LoaderModuleMockMixin): "theme": "default", "autologout": "15m", "attempt_ip": "", - "type": "1" + "type": "1", }, { "lang": "en_GB", @@ -369,74 +517,138 @@ class ZabbixTestCase(TestCase, LoaderModuleMockMixin): "theme": "default", "autologout": "15m", "attempt_ip": "", - "type": "1" - } + "type": "1", + }, ] # pylint: disable=E8128 - query_return = {"jsonrpc": "2.0", "result": [{"userid": "1", "alias": "Admin", - "name": "Zabbix", "surname": "Administrator", "url": "", "autologin": "1", - "autologout": "0", "lang": "en_GB", "refresh": "30s", "type": "3", "theme": "default", - "attempt_failed": "0", "attempt_ip": "10.0.2.2", "attempt_clock": "1515922072", - "rows_per_page": "50"}, {"userid": "2", "alias": "guest", "name": "", "surname": "", - "url": "", "autologin": "0", "autologout": "15m", "lang": "en_GB", "refresh": "30s", - "type": "1", "theme": "default", "attempt_failed": "0", "attempt_ip": "", "attempt_clock": "0", - "rows_per_page": "50"}, {"userid": "5", "alias": "james", "name": "James Brown", "surname": "", - "url": "", "autologin": "0", "autologout": "15m", "lang": "en_GB", "refresh": "30s", - "type": "1", "theme": "default", "attempt_failed": "0", "attempt_ip": "", "attempt_clock": "0", - "rows_per_page": "50"}], "id": 0} + query_return = { + "jsonrpc": "2.0", + "result": [ + { + "userid": "1", + "alias": "Admin", + "name": "Zabbix", + "surname": "Administrator", + "url": "", + "autologin": "1", + "autologout": "0", + "lang": "en_GB", + "refresh": "30s", + "type": "3", + "theme": "default", + "attempt_failed": "0", + "attempt_ip": "10.0.2.2", + "attempt_clock": "1515922072", + "rows_per_page": "50", + }, + { + "userid": "2", + "alias": "guest", + "name": "", + "surname": "", + "url": "", + "autologin": "0", + "autologout": "15m", + "lang": "en_GB", + "refresh": "30s", + "type": "1", + "theme": "default", + "attempt_failed": "0", + "attempt_ip": "", + "attempt_clock": "0", + "rows_per_page": "50", + }, + { + "userid": "5", + "alias": "james", + "name": "James Brown", + "surname": "", + "url": "", + "autologin": "0", + "autologout": "15m", + "lang": "en_GB", + "refresh": "30s", + "type": "1", + "theme": "default", + "attempt_failed": "0", + "attempt_ip": "", + "attempt_clock": "0", + "rows_per_page": "50", + }, + ], + "id": 0, + } - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): self.assertEqual(zabbix.user_list(**CONN_ARGS), module_return) def test_usergroup_create(self): - ''' + """ query_submitted = {"params": {"name": "testgroup"}, "jsonrpc": "2.0", "id": 0, "auth": "7f3ac5e90201e5de4eb19e5322606575", "method": "usergroup.create"} - ''' + """ module_return = ["13"] query_return = {"jsonrpc": "2.0", "result": {"usrgrpids": ["13"]}, "id": 0} - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.usergroup_create('testgroup', **CONN_ARGS), module_return) + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.usergroup_create("testgroup", **CONN_ARGS), module_return + ) def test_usergroup_delete(self): - ''' + """ query_submitted = {"params": [13], "jsonrpc": "2.0", "id": 0, "auth": "9bad39de2a5a9211da588dd06dad8773", "method": "usergroup.delete"} - ''' + """ - module_return = ['13'] + module_return = ["13"] query_return = {"jsonrpc": "2.0", "result": {"usrgrpids": ["13"]}, "id": 0} - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.usergroup_delete('13', **CONN_ARGS), module_return) + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.usergroup_delete("13", **CONN_ARGS), module_return + ) def test_usergroup_exists(self): - ''' + """ query_submitted = {"params": {"filter": {"name": "testgroup"}, "output": "extend", "selectRights": "extend"}, "jsonrpc": "2.0", "id": 0, "auth": "e62424cd7aa71f6748e1d69c190ac852", "method": "usergroup.get"} - ''' + """ module_return = True - query_return = {"jsonrpc": "2.0", "result": [{"usrgrpid": "13", "name": "testgroup", - "gui_access": "0", "users_status": "0", "debug_mode": "0", "rights": []}], "id": 0} + query_return = { + "jsonrpc": "2.0", + "result": [ + { + "usrgrpid": "13", + "name": "testgroup", + "gui_access": "0", + "users_status": "0", + "debug_mode": "0", + "rights": [], + } + ], + "id": 0, + } - with patch.object(zabbix, 'apiinfo_version', return_value='3.2'): - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.usergroup_exists('testgroup', **CONN_ARGS), module_return) + with patch.object(zabbix, "apiinfo_version", return_value="3.2"): + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.usergroup_exists("testgroup", **CONN_ARGS), module_return + ) def test_usergroup_get(self): - ''' + """ query_submitted = {"params": {"filter": {"name": "testgroup"}, "output": "extend", "selectRights": "extend"}, "jsonrpc": "2.0", "id": 0, "auth": "739cf358050f2a2d33162fdcfa714a3c", "method": "usergroup.get"} - ''' + """ module_return = [ { @@ -445,35 +657,52 @@ class ZabbixTestCase(TestCase, LoaderModuleMockMixin): "users_status": "0", "gui_access": "0", "debug_mode": "0", - "usrgrpid": "13" + "usrgrpid": "13", } ] - query_return = {"jsonrpc": "2.0", "result": [{"usrgrpid": "13", "name": "testgroup", - "gui_access": "0", "users_status": "0", "debug_mode": "0", "rights": []}], "id": 0} + query_return = { + "jsonrpc": "2.0", + "result": [ + { + "usrgrpid": "13", + "name": "testgroup", + "gui_access": "0", + "users_status": "0", + "debug_mode": "0", + "rights": [], + } + ], + "id": 0, + } - with patch.object(zabbix, 'apiinfo_version', return_value='3.2'): - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.usergroup_get('testgroup', **CONN_ARGS), module_return) + with patch.object(zabbix, "apiinfo_version", return_value="3.2"): + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.usergroup_get("testgroup", **CONN_ARGS), module_return + ) def test_usergroup_update(self): - ''' + """ query_submitted = {"params": {"usrgrpid": 13, "users_status": 1}, "jsonrpc": "2.0", "id": 0, "auth": "ef772237245f59f655871bc8fbbcd67c", "method": "usergroup.update"} - ''' + """ module_return = ["13"] query_return = {"jsonrpc": "2.0", "result": {"usrgrpids": ["13"]}, "id": 0} - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.usergroup_update('13', users_status='1', **CONN_ARGS), module_return) + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.usergroup_update("13", users_status="1", **CONN_ARGS), + module_return, + ) def test_usergroup_list(self): - ''' + """ query_submitted = {"params": {"output": "extend"}, "jsonrpc": "2.0", "id": 0, "auth": "4bc366bc7803c07e80f15b1bc14dc61f", "method": "usergroup.get"} - ''' + """ module_return = [ { @@ -481,62 +710,102 @@ class ZabbixTestCase(TestCase, LoaderModuleMockMixin): "gui_access": "0", "debug_mode": "0", "name": "Zabbix administrators", - "users_status": "0" + "users_status": "0", }, { "usrgrpid": "8", "gui_access": "0", "debug_mode": "0", "name": "Guests", - "users_status": "0" + "users_status": "0", }, { "usrgrpid": "9", "gui_access": "0", "debug_mode": "0", "name": "Disabled", - "users_status": "1" + "users_status": "1", }, { "usrgrpid": "11", "gui_access": "0", "debug_mode": "1", "name": "Enabled debug mode", - "users_status": "0" + "users_status": "0", }, { "usrgrpid": "12", "gui_access": "2", "debug_mode": "0", "name": "No access to the frontend", - "users_status": "0" + "users_status": "0", }, { "usrgrpid": "13", "gui_access": "0", "debug_mode": "0", "name": "testgroup", - "users_status": "0" - } + "users_status": "0", + }, ] # pylint: disable=E8128 - query_return = {"jsonrpc": "2.0", "result": [{"usrgrpid": "7", "name": "Zabbix administrators", - "gui_access": "0", "users_status": "0", "debug_mode": "0"}, {"usrgrpid": "8", "name": "Guests", - "gui_access": "0", "users_status": "0", "debug_mode": "0"}, {"usrgrpid": "9", - "name": "Disabled", "gui_access": "0", "users_status": "1", "debug_mode": "0"}, - {"usrgrpid": "11", "name": "Enabled debug mode", "gui_access": "0", "users_status": "0", - "debug_mode": "1"}, {"usrgrpid": "12", "name": "No access to the frontend", "gui_access": "2", - "users_status": "0", "debug_mode": "0"}, {"usrgrpid": "13", "name": "testgroup", - "gui_access": "0", "users_status": "0", "debug_mode": "0"}], "id": 0} + query_return = { + "jsonrpc": "2.0", + "result": [ + { + "usrgrpid": "7", + "name": "Zabbix administrators", + "gui_access": "0", + "users_status": "0", + "debug_mode": "0", + }, + { + "usrgrpid": "8", + "name": "Guests", + "gui_access": "0", + "users_status": "0", + "debug_mode": "0", + }, + { + "usrgrpid": "9", + "name": "Disabled", + "gui_access": "0", + "users_status": "1", + "debug_mode": "0", + }, + { + "usrgrpid": "11", + "name": "Enabled debug mode", + "gui_access": "0", + "users_status": "0", + "debug_mode": "1", + }, + { + "usrgrpid": "12", + "name": "No access to the frontend", + "gui_access": "2", + "users_status": "0", + "debug_mode": "0", + }, + { + "usrgrpid": "13", + "name": "testgroup", + "gui_access": "0", + "users_status": "0", + "debug_mode": "0", + }, + ], + "id": 0, + } - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): self.assertEqual(zabbix.usergroup_list(**CONN_ARGS), module_return) def test_host_inventory_get(self): - ''' + """ test host_inventory_get - ''' + """ module_return = { "poc_2_email": "", "poc_2_phone_b": "", @@ -609,141 +878,151 @@ class ZabbixTestCase(TestCase, LoaderModuleMockMixin): "os": "some", "url_b": "", "url_a": "", - "software": "" + "software": "", } - query_return = {"jsonrpc": "2.0", "result": [{ - "hostid": "10258", - "proxy_hostid": "0", - "host": "master", - "status": "0", - "disable_until": "1517766661", - "error": "Get value from agent failed: cannot connect to [[10.0.2.15]:10050]: [111] Connection refused", - "available": "2", - "errors_from": "1516087871", - "lastaccess": "0", - "ipmi_authtype": "-1", - "ipmi_privilege": "2", - "ipmi_username": "", - "ipmi_password": "", - "ipmi_disable_until": "0", - "ipmi_available": "0", - "snmp_disable_until": "0", - "snmp_available": "0", - "maintenanceid": "0", - "maintenance_status": "0", - "maintenance_type": "0", - "maintenance_from": "0", - "ipmi_errors_from": "0", - "snmp_errors_from": "0", - "ipmi_error": "", - "snmp_error": "", - "jmx_disable_until": "0", - "jmx_available": "0", - "jmx_errors_from": "0", - "jmx_error": "", - "name": "master", - "flags": "0", - "templateid": "0", - "description": "", - "tls_connect": "1", - "tls_accept": "1", - "tls_issuer": "", - "tls_subject": "", - "tls_psk_identity": "", - "tls_psk": "", - "inventory": { + query_return = { + "jsonrpc": "2.0", + "result": [ + { "hostid": "10258", - "inventory_mode": "0", - "type": "", - "type_full": "", - "name": "", - "alias": "other thing", - "os": "some", - "os_full": "", - "os_short": "", - "serialno_a": "", - "serialno_b": "", - "tag": "", - "asset_tag": "", - "macaddress_a": "", - "macaddress_b": "", - "hardware": "", - "hardware_full": "", - "software": "", - "software_full": "", - "software_app_a": "", - "software_app_b": "", - "software_app_c": "", - "software_app_d": "", - "software_app_e": "", - "contact": "", - "location": "", - "location_lat": "", - "location_lon": "", - "notes": "", - "chassis": "", - "model": "", - "hw_arch": "", - "vendor": "", - "contract_number": "", - "installer_name": "", - "deployment_status": "", - "url_a": "", - "url_b": "", - "url_c": "", - "host_networks": "", - "host_netmask": "", - "host_router": "", - "oob_ip": "", - "oob_netmask": "", - "oob_router": "", - "date_hw_purchase": "", - "date_hw_install": "", - "date_hw_expiry": "", - "date_hw_decomm": "", - "site_address_a": "", - "site_address_b": "", - "site_address_c": "", - "site_city": "", - "site_state": "", - "site_country": "", - "site_zip": "", - "site_rack": "", - "site_notes": "", - "poc_1_name": "", - "poc_1_email": "", - "poc_1_phone_a": "", - "poc_1_phone_b": "", - "poc_1_cell": "", - "poc_1_screen": "", - "poc_1_notes": "", - "poc_2_name": "", - "poc_2_email": "", - "poc_2_phone_a": "", - "poc_2_phone_b": "", - "poc_2_cell": "", - "poc_2_screen": "", - "poc_2_notes": "" + "proxy_hostid": "0", + "host": "master", + "status": "0", + "disable_until": "1517766661", + "error": "Get value from agent failed: cannot connect to [[10.0.2.15]:10050]: [111] Connection refused", + "available": "2", + "errors_from": "1516087871", + "lastaccess": "0", + "ipmi_authtype": "-1", + "ipmi_privilege": "2", + "ipmi_username": "", + "ipmi_password": "", + "ipmi_disable_until": "0", + "ipmi_available": "0", + "snmp_disable_until": "0", + "snmp_available": "0", + "maintenanceid": "0", + "maintenance_status": "0", + "maintenance_type": "0", + "maintenance_from": "0", + "ipmi_errors_from": "0", + "snmp_errors_from": "0", + "ipmi_error": "", + "snmp_error": "", + "jmx_disable_until": "0", + "jmx_available": "0", + "jmx_errors_from": "0", + "jmx_error": "", + "name": "master", + "flags": "0", + "templateid": "0", + "description": "", + "tls_connect": "1", + "tls_accept": "1", + "tls_issuer": "", + "tls_subject": "", + "tls_psk_identity": "", + "tls_psk": "", + "inventory": { + "hostid": "10258", + "inventory_mode": "0", + "type": "", + "type_full": "", + "name": "", + "alias": "other thing", + "os": "some", + "os_full": "", + "os_short": "", + "serialno_a": "", + "serialno_b": "", + "tag": "", + "asset_tag": "", + "macaddress_a": "", + "macaddress_b": "", + "hardware": "", + "hardware_full": "", + "software": "", + "software_full": "", + "software_app_a": "", + "software_app_b": "", + "software_app_c": "", + "software_app_d": "", + "software_app_e": "", + "contact": "", + "location": "", + "location_lat": "", + "location_lon": "", + "notes": "", + "chassis": "", + "model": "", + "hw_arch": "", + "vendor": "", + "contract_number": "", + "installer_name": "", + "deployment_status": "", + "url_a": "", + "url_b": "", + "url_c": "", + "host_networks": "", + "host_netmask": "", + "host_router": "", + "oob_ip": "", + "oob_netmask": "", + "oob_router": "", + "date_hw_purchase": "", + "date_hw_install": "", + "date_hw_expiry": "", + "date_hw_decomm": "", + "site_address_a": "", + "site_address_b": "", + "site_address_c": "", + "site_city": "", + "site_state": "", + "site_country": "", + "site_zip": "", + "site_rack": "", + "site_notes": "", + "poc_1_name": "", + "poc_1_email": "", + "poc_1_phone_a": "", + "poc_1_phone_b": "", + "poc_1_cell": "", + "poc_1_screen": "", + "poc_1_notes": "", + "poc_2_name": "", + "poc_2_email": "", + "poc_2_phone_a": "", + "poc_2_phone_b": "", + "poc_2_cell": "", + "poc_2_screen": "", + "poc_2_notes": "", + }, } - }], - "id": 1 + ], + "id": 1, } - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.host_inventory_get('12345', **CONN_ARGS), module_return) + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.host_inventory_get("12345", **CONN_ARGS), module_return + ) def test_host_inventory_set(self): - ''' + """ query_submitted = {"params": {"hostid": 10258, "inventory_mode": "0", "inventory": {"asset_tag": "jml3322", "type": "Xen"}}, "jsonrpc": "2.0", "id": 0, "auth": "a50d2c3030b9b73d7c28b5ebd89c044c", "method": "host.update"} - ''' + """ module_return = {"hostids": [10258]} query_return = {"jsonrpc": "2.0", "result": {"hostids": [10258]}, "id": 0} - with patch.object(zabbix, '_query', return_value=query_return): - with patch.object(zabbix, '_login', return_value=CONN_ARGS): - self.assertEqual(zabbix.host_inventory_set(10258, asset_tag='jml3322', - type='Xen', **CONN_ARGS), module_return) + with patch.object(zabbix, "_query", return_value=query_return): + with patch.object(zabbix, "_login", return_value=CONN_ARGS): + self.assertEqual( + zabbix.host_inventory_set( + 10258, asset_tag="jml3322", type="Xen", **CONN_ARGS + ), + module_return, + ) diff --git a/tests/unit/modules/test_zcbuildout.py b/tests/unit/modules/test_zcbuildout.py index 788bcd8ab05..cdad9351482 100644 --- a/tests/unit/modules/test_zcbuildout.py +++ b/tests/unit/modules/test_zcbuildout.py @@ -2,75 +2,72 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import shutil import subprocess import tempfile -# Import 3rd-party libs - -# pylint: disable=import-error,no-name-in-module,redefined-builtin -from salt.ext import six -from salt.ext.six.moves.urllib.error import URLError -from salt.ext.six.moves.urllib.request import urlopen -# pylint: enable=import-error,no-name-in-module,redefined-builtin - -# Import Salt Testing libs -from tests.support.helpers import requires_network, patched_environ -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import TestCase, skipIf +import salt.modules.cmdmod as cmd +import salt.modules.virtualenv_mod +import salt.modules.zcbuildout as buildout # Import Salt libs import salt.utils.files import salt.utils.path import salt.utils.platform -import salt.modules.zcbuildout as buildout -import salt.modules.virtualenv_mod -import salt.modules.cmdmod as cmd + +# pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six +from salt.ext.six.moves.urllib.error import URLError +from salt.ext.six.moves.urllib.request import urlopen + +# Import Salt Testing libs +from tests.support.helpers import patched_environ, requires_network +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + +# Import 3rd-party libs + + +# pylint: enable=import-error,no-name-in-module,redefined-builtin + KNOWN_VIRTUALENV_BINARY_NAMES = ( - 'virtualenv', - 'virtualenv2', - 'virtualenv-2.6', - 'virtualenv-2.7' + "virtualenv", + "virtualenv2", + "virtualenv-2.6", + "virtualenv-2.7", ) # temp workaround since virtualenv pip wheel package does not include # backports.ssl_match_hostname on windows python2.7 if salt.utils.platform.is_windows() and six.PY2: - KNOWN_VIRTUALENV_BINARY_NAMES = ( - 'c:\\Python27\\Scripts\\virtualenv.EXE', - ) + KNOWN_VIRTUALENV_BINARY_NAMES = ("c:\\Python27\\Scripts\\virtualenv.EXE",) BOOT_INIT = { - 1: [ - 'var/ver/1/bootstrap/bootstrap.py', - ], - 2: [ - 'var/ver/2/bootstrap/bootstrap.py', - 'b/bootstrap.py', - ]} + 1: ["var/ver/1/bootstrap/bootstrap.py"], + 2: ["var/ver/2/bootstrap/bootstrap.py", "b/bootstrap.py"], +} log = logging.getLogger(__name__) def download_to(url, dest): - with salt.utils.files.fopen(dest, 'wb') as fic: + with salt.utils.files.fopen(dest, "wb") as fic: fic.write(urlopen(url, timeout=10).read()) class Base(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { buildout: { - '__salt__': { - 'cmd.run_all': cmd.run_all, - 'cmd.run': cmd.run, - 'cmd.retcode': cmd.retcode, + "__salt__": { + "cmd.run_all": cmd.run_all, + "cmd.run": cmd.run, + "cmd.retcode": cmd.retcode, } } } @@ -80,38 +77,32 @@ class Base(TestCase, LoaderModuleMockMixin): if not os.path.isdir(RUNTIME_VARS.TMP): os.makedirs(RUNTIME_VARS.TMP) - cls.root = os.path.join(RUNTIME_VARS.BASE_FILES, 'buildout') + cls.root = os.path.join(RUNTIME_VARS.BASE_FILES, "buildout") cls.rdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - cls.tdir = os.path.join(cls.rdir, 'test') + cls.tdir = os.path.join(cls.rdir, "test") for idx, url in six.iteritems(buildout._URL_VERSIONS): - log.debug('Downloading bootstrap from %s', url) - dest = os.path.join( - cls.rdir, '{0}_bootstrap.py'.format(idx) - ) + log.debug("Downloading bootstrap from %s", url) + dest = os.path.join(cls.rdir, "{0}_bootstrap.py".format(idx)) try: download_to(url, dest) except URLError: - log.debug('Failed to download %s', url) + log.debug("Failed to download %s", url) # creating a new setuptools install - cls.ppy_st = os.path.join(cls.rdir, 'psetuptools') + cls.ppy_st = os.path.join(cls.rdir, "psetuptools") if salt.utils.platform.is_windows(): - cls.bin_st = os.path.join(cls.ppy_st, 'Scripts') - cls.py_st = os.path.join(cls.bin_st, 'python') + cls.bin_st = os.path.join(cls.ppy_st, "Scripts") + cls.py_st = os.path.join(cls.bin_st, "python") else: - cls.bin_st = os.path.join(cls.ppy_st, 'bin') - cls.py_st = os.path.join(cls.bin_st, 'python') + cls.bin_st = os.path.join(cls.ppy_st, "bin") + cls.py_st = os.path.join(cls.bin_st, "python") # `--no-site-packages` has been deprecated # https://virtualenv.pypa.io/en/stable/reference/#cmdoption-no-site-packages - subprocess.check_call([ - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - cls.ppy_st - ]) - subprocess.check_call([ - os.path.join(cls.bin_st, 'pip'), - 'install', - '-U', - 'setuptools', - ]) + subprocess.check_call( + [salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), cls.ppy_st] + ) + subprocess.check_call( + [os.path.join(cls.bin_st, "pip"), "install", "-U", "setuptools"] + ) # distribute has been merged back in to setuptools as of v0.7. So, no # need to upgrade distribute, but this seems to be the only way to get # the binary in the right place @@ -122,15 +113,13 @@ class Base(TestCase, LoaderModuleMockMixin): # images. (https://github.com/saltstack/salt-jenkins/pull/1479) # certutil -generateSSTFromWU roots.sst # powershell "(Get-ChildItem -Path .\roots.sst) | Import-Certificate -CertStoreLocation Cert:\LocalMachine\Root" - subprocess.check_call([ - os.path.join(cls.bin_st, 'easy_install'), - '-U', - 'distribute', - ]) + subprocess.check_call( + [os.path.join(cls.bin_st, "easy_install"), "-U", "distribute"] + ) def setUp(self): if salt.utils.platform.is_darwin and six.PY3: - self.patched_environ = patched_environ(__cleanup__=['__PYVENV_LAUNCHER__']) + self.patched_environ = patched_environ(__cleanup__=["__PYVENV_LAUNCHER__"]) self.patched_environ.__enter__() self.addCleanup(self.patched_environ.__exit__) @@ -139,9 +128,7 @@ class Base(TestCase, LoaderModuleMockMixin): shutil.copytree(self.root, self.tdir) for idx in BOOT_INIT: - path = os.path.join( - self.rdir, '{0}_bootstrap.py'.format(idx) - ) + path = os.path.join(self.rdir, "{0}_bootstrap.py".format(idx)) for fname in BOOT_INIT[idx]: shutil.copy2(path, os.path.join(self.tdir, fname)) @@ -154,51 +141,52 @@ class Base(TestCase, LoaderModuleMockMixin): shutil.rmtree(self.tdir) -@skipIf(salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES) is None, - "The 'virtualenv' packaged needs to be installed") +@skipIf( + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES) is None, + "The 'virtualenv' packaged needs to be installed", +) class BuildoutTestCase(Base): - @requires_network() def test_onlyif_unless(self): - b_dir = os.path.join(self.tdir, 'b') + b_dir = os.path.join(self.tdir, "b") ret = buildout.buildout(b_dir, onlyif=RUNTIME_VARS.SHELL_FALSE_PATH) - self.assertTrue(ret['comment'] == 'onlyif condition is false') - self.assertTrue(ret['status'] is True) + self.assertTrue(ret["comment"] == "onlyif condition is false") + self.assertTrue(ret["status"] is True) ret = buildout.buildout(b_dir, unless=RUNTIME_VARS.SHELL_TRUE_PATH) - self.assertTrue(ret['comment'] == 'unless condition is true') - self.assertTrue(ret['status'] is True) + self.assertTrue(ret["comment"] == "unless condition is true") + self.assertTrue(ret["status"] is True) @requires_network() def test_salt_callback(self): @buildout._salt_callback def callback1(a, b=1): for i in buildout.LOG.levels: - getattr(buildout.LOG, i)('{0}bar'.format(i[0])) - return 'foo' + getattr(buildout.LOG, i)("{0}bar".format(i[0])) + return "foo" def callback2(a, b=1): - raise Exception('foo') + raise Exception("foo") # pylint: disable=invalid-sequence-index ret1 = callback1(1, b=3) # These lines are throwing pylint errors - disabling for now since we are skipping # these tests - #self.assertEqual(ret1['status'], True) - #self.assertEqual(ret1['logs_by_level']['warn'], ['wbar']) - #self.assertEqual(ret1['comment'], '') + # self.assertEqual(ret1['status'], True) + # self.assertEqual(ret1['logs_by_level']['warn'], ['wbar']) + # self.assertEqual(ret1['comment'], '') # These lines are throwing pylint errors - disabling for now since we are skipping # these tests - #self.assertTrue( + # self.assertTrue( # u'' # u'OUTPUT:\n' # u'foo\n' # u'' # in ret1['outlog'] - #) + # ) # These lines are throwing pylint errors - disabling for now since we are skipping # these tests - #self.assertTrue(u'Log summary:\n' in ret1['outlog']) + # self.assertTrue(u'Log summary:\n' in ret1['outlog']) # These lines are throwing pylint errors - disabling for now since we are skipping # these tests # self.assertTrue( @@ -207,137 +195,149 @@ class BuildoutTestCase(Base): # u'DEBUG: dbar\n' # u'ERROR: ebar\n' # in ret1['outlog'] - #) + # ) # These lines are throwing pylint errors - disabling for now since we are skipping # these tests - #self.assertTrue('by level' in ret1['outlog_by_level']) - #self.assertEqual(ret1['out'], 'foo') + # self.assertTrue('by level' in ret1['outlog_by_level']) + # self.assertEqual(ret1['out'], 'foo') ret2 = buildout._salt_callback(callback2)(2, b=6) - self.assertEqual(ret2['status'], False) + self.assertEqual(ret2["status"], False) + self.assertTrue(ret2["logs_by_level"]["error"][0].startswith("Traceback")) self.assertTrue( - ret2['logs_by_level']['error'][0].startswith('Traceback')) - self.assertTrue( - 'We did not get any ' - 'expectable answer ' - 'from buildout' in ret2['comment']) - self.assertEqual(ret2['out'], None) + "We did not get any " + "expectable answer " + "from buildout" in ret2["comment"] + ) + self.assertEqual(ret2["out"], None) for l in buildout.LOG.levels: self.assertTrue(0 == len(buildout.LOG.by_level[l])) # pylint: enable=invalid-sequence-index @requires_network() def test_get_bootstrap_url(self): - for path in [os.path.join(self.tdir, 'var/ver/1/dumppicked'), - os.path.join(self.tdir, 'var/ver/1/bootstrap'), - os.path.join(self.tdir, 'var/ver/1/versions')]: - self.assertEqual(buildout._URL_VERSIONS[1], - buildout._get_bootstrap_url(path), - "b1 url for {0}".format(path)) for path in [ - os.path.join(self.tdir, '/non/existing'), - os.path.join(self.tdir, 'var/ver/2/versions'), - os.path.join(self.tdir, 'var/ver/2/bootstrap'), - os.path.join(self.tdir, 'var/ver/2/default'), + os.path.join(self.tdir, "var/ver/1/dumppicked"), + os.path.join(self.tdir, "var/ver/1/bootstrap"), + os.path.join(self.tdir, "var/ver/1/versions"), ]: - self.assertEqual(buildout._URL_VERSIONS[2], - buildout._get_bootstrap_url(path), - "b2 url for {0}".format(path)) + self.assertEqual( + buildout._URL_VERSIONS[1], + buildout._get_bootstrap_url(path), + "b1 url for {0}".format(path), + ) + for path in [ + os.path.join(self.tdir, "/non/existing"), + os.path.join(self.tdir, "var/ver/2/versions"), + os.path.join(self.tdir, "var/ver/2/bootstrap"), + os.path.join(self.tdir, "var/ver/2/default"), + ]: + self.assertEqual( + buildout._URL_VERSIONS[2], + buildout._get_bootstrap_url(path), + "b2 url for {0}".format(path), + ) @requires_network() def test_get_buildout_ver(self): - for path in [os.path.join(self.tdir, 'var/ver/1/dumppicked'), - os.path.join(self.tdir, 'var/ver/1/bootstrap'), - os.path.join(self.tdir, 'var/ver/1/versions')]: - self.assertEqual(1, - buildout._get_buildout_ver(path), - "1 for {0}".format(path)) - for path in [os.path.join(self.tdir, '/non/existing'), - os.path.join(self.tdir, 'var/ver/2/versions'), - os.path.join(self.tdir, 'var/ver/2/bootstrap'), - os.path.join(self.tdir, 'var/ver/2/default')]: - self.assertEqual(2, - buildout._get_buildout_ver(path), - "2 for {0}".format(path)) + for path in [ + os.path.join(self.tdir, "var/ver/1/dumppicked"), + os.path.join(self.tdir, "var/ver/1/bootstrap"), + os.path.join(self.tdir, "var/ver/1/versions"), + ]: + self.assertEqual( + 1, buildout._get_buildout_ver(path), "1 for {0}".format(path) + ) + for path in [ + os.path.join(self.tdir, "/non/existing"), + os.path.join(self.tdir, "var/ver/2/versions"), + os.path.join(self.tdir, "var/ver/2/bootstrap"), + os.path.join(self.tdir, "var/ver/2/default"), + ]: + self.assertEqual( + 2, buildout._get_buildout_ver(path), "2 for {0}".format(path) + ) @requires_network() def test_get_bootstrap_content(self): self.assertEqual( - '', - buildout._get_bootstrap_content( - os.path.join(self.tdir, 'non', 'existing')) + "", + buildout._get_bootstrap_content(os.path.join(self.tdir, "non", "existing")), ) self.assertEqual( - '', - buildout._get_bootstrap_content( - os.path.join(self.tdir, 'var', 'tb', '1'))) + "", + buildout._get_bootstrap_content(os.path.join(self.tdir, "var", "tb", "1")), + ) self.assertEqual( - 'foo{0}'.format(os.linesep), - buildout._get_bootstrap_content( - os.path.join(self.tdir, 'var', 'tb', '2'))) + "foo{0}".format(os.linesep), + buildout._get_bootstrap_content(os.path.join(self.tdir, "var", "tb", "2")), + ) @requires_network() def test_logger_clean(self): buildout.LOG.clear() # nothing in there self.assertTrue( - True not in - [len(buildout.LOG.by_level[a]) > 0 - for a in buildout.LOG.by_level]) - buildout.LOG.info('foo') + True + not in [len(buildout.LOG.by_level[a]) > 0 for a in buildout.LOG.by_level] + ) + buildout.LOG.info("foo") self.assertTrue( - True in - [len(buildout.LOG.by_level[a]) > 0 - for a in buildout.LOG.by_level]) + True in [len(buildout.LOG.by_level[a]) > 0 for a in buildout.LOG.by_level] + ) buildout.LOG.clear() self.assertTrue( - True not in - [len(buildout.LOG.by_level[a]) > 0 - for a in buildout.LOG.by_level]) + True + not in [len(buildout.LOG.by_level[a]) > 0 for a in buildout.LOG.by_level] + ) @requires_network() def test_logger_loggers(self): buildout.LOG.clear() # nothing in there for i in buildout.LOG.levels: - getattr(buildout.LOG, i)('foo') - getattr(buildout.LOG, i)('bar') - getattr(buildout.LOG, i)('moo') + getattr(buildout.LOG, i)("foo") + getattr(buildout.LOG, i)("bar") + getattr(buildout.LOG, i)("moo") self.assertTrue(len(buildout.LOG.by_level[i]) == 3) - self.assertEqual(buildout.LOG.by_level[i][0], 'foo') - self.assertEqual(buildout.LOG.by_level[i][-1], 'moo') + self.assertEqual(buildout.LOG.by_level[i][0], "foo") + self.assertEqual(buildout.LOG.by_level[i][-1], "moo") @requires_network() def test__find_cfgs(self): result = sorted( - [a.replace(self.root, '') for a in buildout._find_cfgs(self.root)]) + [a.replace(self.root, "") for a in buildout._find_cfgs(self.root)] + ) assertlist = sorted( - [os.path.join(os.sep, 'buildout.cfg'), - os.path.join(os.sep, 'c', 'buildout.cfg'), - os.path.join(os.sep, 'etc', 'buildout.cfg'), - os.path.join(os.sep, 'e', 'buildout.cfg'), - os.path.join(os.sep, 'b', 'buildout.cfg'), - os.path.join(os.sep, 'b', 'bdistribute', 'buildout.cfg'), - os.path.join(os.sep, 'b', 'b2', 'buildout.cfg'), - os.path.join(os.sep, 'foo', 'buildout.cfg')]) + [ + os.path.join(os.sep, "buildout.cfg"), + os.path.join(os.sep, "c", "buildout.cfg"), + os.path.join(os.sep, "etc", "buildout.cfg"), + os.path.join(os.sep, "e", "buildout.cfg"), + os.path.join(os.sep, "b", "buildout.cfg"), + os.path.join(os.sep, "b", "bdistribute", "buildout.cfg"), + os.path.join(os.sep, "b", "b2", "buildout.cfg"), + os.path.join(os.sep, "foo", "buildout.cfg"), + ] + ) self.assertEqual(result, assertlist) @requires_network() def skip_test_upgrade_bootstrap(self): - b_dir = os.path.join(self.tdir, 'b') - bpy = os.path.join(b_dir, 'bootstrap.py') + b_dir = os.path.join(self.tdir, "b") + bpy = os.path.join(b_dir, "bootstrap.py") buildout.upgrade_bootstrap(b_dir) time1 = os.stat(bpy).st_mtime with salt.utils.files.fopen(bpy) as fic: data = fic.read() - self.assertTrue('setdefaulttimeout(2)' in data) - flag = os.path.join(b_dir, '.buildout', '2.updated_bootstrap') + self.assertTrue("setdefaulttimeout(2)" in data) + flag = os.path.join(b_dir, ".buildout", "2.updated_bootstrap") self.assertTrue(os.path.exists(flag)) buildout.upgrade_bootstrap(b_dir, buildout_ver=1) time2 = os.stat(bpy).st_mtime with salt.utils.files.fopen(bpy) as fic: data = fic.read() - self.assertTrue('setdefaulttimeout(2)' in data) - flag = os.path.join(b_dir, '.buildout', '1.updated_bootstrap') + self.assertTrue("setdefaulttimeout(2)" in data) + flag = os.path.join(b_dir, ".buildout", "1.updated_bootstrap") self.assertTrue(os.path.exists(flag)) buildout.upgrade_bootstrap(b_dir, buildout_ver=1) time3 = os.stat(bpy).st_mtime @@ -345,76 +345,85 @@ class BuildoutTestCase(Base): self.assertEqual(time2, time3) -@skipIf(salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES) is None, - 'The \'virtualenv\' packaged needs to be installed') +@skipIf( + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES) is None, + "The 'virtualenv' packaged needs to be installed", +) class BuildoutOnlineTestCase(Base): - @classmethod def setUpClass(cls): super(BuildoutOnlineTestCase, cls).setUpClass() - cls.ppy_dis = os.path.join(cls.rdir, 'pdistribute') - cls.ppy_blank = os.path.join(cls.rdir, 'pblank') - cls.py_dis = os.path.join(cls.ppy_dis, 'bin', 'python') - cls.py_blank = os.path.join(cls.ppy_blank, 'bin', 'python') + cls.ppy_dis = os.path.join(cls.rdir, "pdistribute") + cls.ppy_blank = os.path.join(cls.rdir, "pblank") + cls.py_dis = os.path.join(cls.ppy_dis, "bin", "python") + cls.py_blank = os.path.join(cls.ppy_blank, "bin", "python") # creating a distribute based install try: # `--no-site-packages` has been deprecated # https://virtualenv.pypa.io/en/stable/reference/#cmdoption-no-site-packages - subprocess.check_call([ - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - '--no-setuptools', - '--no-pip', - cls.ppy_dis, - ]) + subprocess.check_call( + [ + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), + "--no-setuptools", + "--no-pip", + cls.ppy_dis, + ] + ) except subprocess.CalledProcessError: - subprocess.check_call([ - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - cls.ppy.dis, - ]) + subprocess.check_call( + [salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), cls.ppy.dis] + ) url = ( - 'https://pypi.python.org/packages/source' - '/d/distribute/distribute-0.6.43.tar.gz' + "https://pypi.python.org/packages/source" + "/d/distribute/distribute-0.6.43.tar.gz" ) download_to( - url, - os.path.join(cls.ppy_dis, 'distribute-0.6.43.tar.gz'), + url, os.path.join(cls.ppy_dis, "distribute-0.6.43.tar.gz"), ) - subprocess.check_call([ - 'tar', - '-C', - cls.ppy_dis, - '-xzvf', - '{0}/distribute-0.6.43.tar.gz'.format(cls.ppy_dis), - ]) + subprocess.check_call( + [ + "tar", + "-C", + cls.ppy_dis, + "-xzvf", + "{0}/distribute-0.6.43.tar.gz".format(cls.ppy_dis), + ] + ) - subprocess.check_call([ - '{0}/bin/python'.format(cls.ppy_dis), - '{0}/distribute-0.6.43/setup.py'.format(cls.ppy_dis), - 'install', - ]) + subprocess.check_call( + [ + "{0}/bin/python".format(cls.ppy_dis), + "{0}/distribute-0.6.43/setup.py".format(cls.ppy_dis), + "install", + ] + ) # creating a blank based install try: - subprocess.check_call([ - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - '--no-setuptools', - '--no-pip', - cls.ppy_blank, - ]) + subprocess.check_call( + [ + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), + "--no-setuptools", + "--no-pip", + cls.ppy_blank, + ] + ) except subprocess.CalledProcessError: - subprocess.check_call([ - salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), - cls.ppy_blank, - ]) + subprocess.check_call( + [ + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES), + cls.ppy_blank, + ] + ) @requires_network() - @skipIf(True, 'TODO this test should probably be fixed') + @skipIf(True, "TODO this test should probably be fixed") def test_buildout_bootstrap(self): - b_dir = os.path.join(self.tdir, 'b') - bd_dir = os.path.join(self.tdir, 'b', 'bdistribute') - b2_dir = os.path.join(self.tdir, 'b', 'b2') + b_dir = os.path.join(self.tdir, "b") + bd_dir = os.path.join(self.tdir, "b", "bdistribute") + b2_dir = os.path.join(self.tdir, "b", "b2") self.assertTrue(buildout._has_old_distribute(self.py_dis)) # this is too hard to check as on debian & other where old # packages are present (virtualenv), we can't have @@ -425,132 +434,126 @@ class BuildoutOnlineTestCase(Base): self.assertTrue(buildout._has_setuptools7(self.py_st)) self.assertFalse(buildout._has_setuptools7(self.py_blank)) - ret = buildout.bootstrap( - bd_dir, buildout_ver=1, python=self.py_dis) - comment = ret['outlog'] - self.assertTrue('--distribute' in comment) - self.assertTrue('Generated script' in comment) + ret = buildout.bootstrap(bd_dir, buildout_ver=1, python=self.py_dis) + comment = ret["outlog"] + self.assertTrue("--distribute" in comment) + self.assertTrue("Generated script" in comment) ret = buildout.bootstrap(b_dir, buildout_ver=1, python=self.py_blank) - comment = ret['outlog'] + comment = ret["outlog"] # as we may have old packages, this test the two # behaviors (failure with old setuptools/distribute) self.assertTrue( - ('Got ' in comment - and 'Generated script' in comment) - or ('setuptools>=0.7' in comment) + ("Got " in comment and "Generated script" in comment) + or ("setuptools>=0.7" in comment) ) ret = buildout.bootstrap(b_dir, buildout_ver=2, python=self.py_blank) - comment = ret['outlog'] + comment = ret["outlog"] self.assertTrue( - ('setuptools' in comment - and 'Generated script' in comment) - or ('setuptools>=0.7' in comment) + ("setuptools" in comment and "Generated script" in comment) + or ("setuptools>=0.7" in comment) ) ret = buildout.bootstrap(b_dir, buildout_ver=2, python=self.py_st) - comment = ret['outlog'] + comment = ret["outlog"] self.assertTrue( - ('setuptools' in comment - and 'Generated script' in comment) - or ('setuptools>=0.7' in comment) + ("setuptools" in comment and "Generated script" in comment) + or ("setuptools>=0.7" in comment) ) ret = buildout.bootstrap(b2_dir, buildout_ver=2, python=self.py_st) - comment = ret['outlog'] + comment = ret["outlog"] self.assertTrue( - ('setuptools' in comment - and 'Creating directory' in comment) - or ('setuptools>=0.7' in comment) + ("setuptools" in comment and "Creating directory" in comment) + or ("setuptools>=0.7" in comment) ) @requires_network() def test_run_buildout(self): if salt.modules.virtualenv_mod.virtualenv_ver(self.ppy_st) >= (20, 0, 0): - self.skipTest("Skiping until upstream resolved https://github.com/pypa/virtualenv/issues/1715") + self.skipTest( + "Skiping until upstream resolved https://github.com/pypa/virtualenv/issues/1715" + ) - b_dir = os.path.join(self.tdir, 'b') + b_dir = os.path.join(self.tdir, "b") ret = buildout.bootstrap(b_dir, buildout_ver=2, python=self.py_st) - self.assertTrue(ret['status']) - ret = buildout.run_buildout(b_dir, - parts=['a', 'b']) - out = ret['out'] - self.assertTrue('Installing a' in out) - self.assertTrue('Installing b' in out) + self.assertTrue(ret["status"]) + ret = buildout.run_buildout(b_dir, parts=["a", "b"]) + out = ret["out"] + self.assertTrue("Installing a" in out) + self.assertTrue("Installing b" in out) @requires_network() def test_buildout(self): if salt.modules.virtualenv_mod.virtualenv_ver(self.ppy_st) >= (20, 0, 0): - self.skipTest("Skiping until upstream resolved https://github.com/pypa/virtualenv/issues/1715") + self.skipTest( + "Skiping until upstream resolved https://github.com/pypa/virtualenv/issues/1715" + ) - b_dir = os.path.join(self.tdir, 'b') + b_dir = os.path.join(self.tdir, "b") ret = buildout.buildout(b_dir, buildout_ver=2, python=self.py_st) - self.assertTrue(ret['status']) - out = ret['out'] - comment = ret['comment'] - self.assertTrue(ret['status']) - self.assertTrue('Creating directory' in out) - self.assertTrue('Installing a.' in out) - self.assertTrue('{0} bootstrap.py'.format(self.py_st) in comment) - self.assertTrue('buildout -c buildout.cfg' in comment) - ret = buildout.buildout(b_dir, - parts=['a', 'b', 'c'], - buildout_ver=2, - python=self.py_st) - outlog = ret['outlog'] - out = ret['out'] - comment = ret['comment'] - self.assertTrue('Installing single part: a' in outlog) - self.assertTrue('buildout -c buildout.cfg -N install a' in comment) - self.assertTrue('Installing b.' in out) - self.assertTrue('Installing c.' in out) - ret = buildout.buildout(b_dir, - parts=['a', 'b', 'c'], - buildout_ver=2, - newest=True, - python=self.py_st) - outlog = ret['outlog'] - out = ret['out'] - comment = ret['comment'] - self.assertTrue('buildout -c buildout.cfg -n install a' in comment) + self.assertTrue(ret["status"]) + out = ret["out"] + comment = ret["comment"] + self.assertTrue(ret["status"]) + self.assertTrue("Creating directory" in out) + self.assertTrue("Installing a." in out) + self.assertTrue("{0} bootstrap.py".format(self.py_st) in comment) + self.assertTrue("buildout -c buildout.cfg" in comment) + ret = buildout.buildout( + b_dir, parts=["a", "b", "c"], buildout_ver=2, python=self.py_st + ) + outlog = ret["outlog"] + out = ret["out"] + comment = ret["comment"] + self.assertTrue("Installing single part: a" in outlog) + self.assertTrue("buildout -c buildout.cfg -N install a" in comment) + self.assertTrue("Installing b." in out) + self.assertTrue("Installing c." in out) + ret = buildout.buildout( + b_dir, parts=["a", "b", "c"], buildout_ver=2, newest=True, python=self.py_st + ) + outlog = ret["outlog"] + out = ret["out"] + comment = ret["comment"] + self.assertTrue("buildout -c buildout.cfg -n install a" in comment) # TODO: Is this test even still needed? class BuildoutAPITestCase(TestCase): - def test_merge(self): buildout.LOG.clear() - buildout.LOG.info('àé') - buildout.LOG.info(u'àé') - buildout.LOG.error('àé') - buildout.LOG.error(u'àé') - ret1 = buildout._set_status({}, out='éà') - uret1 = buildout._set_status({}, out=u'éà') + buildout.LOG.info("àé") + buildout.LOG.info("àé") + buildout.LOG.error("àé") + buildout.LOG.error("àé") + ret1 = buildout._set_status({}, out="éà") + uret1 = buildout._set_status({}, out="éà") buildout.LOG.clear() - buildout.LOG.info('ççàé') - buildout.LOG.info(u'ççàé') - buildout.LOG.error('ççàé') - buildout.LOG.error(u'ççàé') - ret2 = buildout._set_status({}, out='çéà') - uret2 = buildout._set_status({}, out=u'çéà') + buildout.LOG.info("ççàé") + buildout.LOG.info("ççàé") + buildout.LOG.error("ççàé") + buildout.LOG.error("ççàé") + ret2 = buildout._set_status({}, out="çéà") + uret2 = buildout._set_status({}, out="çéà") uretm = buildout._merge_statuses([ret1, uret1, ret2, uret2]) for ret in ret1, uret1, ret2, uret2: - out = ret['out'] - if not isinstance(ret['out'], six.text_type): - out = ret['out'].decode('utf-8') + out = ret["out"] + if not isinstance(ret["out"], six.text_type): + out = ret["out"].decode("utf-8") - for out in ['àé', 'ççàé']: - self.assertTrue(out in uretm['logs_by_level']['info']) - self.assertTrue(out in uretm['outlog_by_level']) + for out in ["àé", "ççàé"]: + self.assertTrue(out in uretm["logs_by_level"]["info"]) + self.assertTrue(out in uretm["outlog_by_level"]) def test_setup(self): buildout.LOG.clear() - buildout.LOG.info('àé') - buildout.LOG.info(u'àé') - buildout.LOG.error('àé') - buildout.LOG.error(u'àé') - ret = buildout._set_status({}, out='éà') - uret = buildout._set_status({}, out=u'éà') - self.assertTrue(ret['outlog'] == uret['outlog']) - self.assertTrue('àé' in uret['outlog_by_level']) + buildout.LOG.info("àé") + buildout.LOG.info("àé") + buildout.LOG.error("àé") + buildout.LOG.error("àé") + ret = buildout._set_status({}, out="éà") + uret = buildout._set_status({}, out="éà") + self.assertTrue(ret["outlog"] == uret["outlog"]) + self.assertTrue("àé" in uret["outlog_by_level"]) diff --git a/tests/unit/modules/test_zfs.py b/tests/unit/modules/test_zfs.py index 94573499bf0..1dfde2a745d 100644 --- a/tests/unit/modules/test_zfs.py +++ b/tests/unit/modules/test_zfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.modules.zfs :codeauthor: Nitin Madhok <nmadhok@clemson.edu>, Jorge Schrauwen <sjorge@blackdot.be> @@ -7,816 +7,1082 @@ Tests for salt.modules.zfs :maturity: new :depends: salt.utils.zfs :platform: illumos,freebsd,linux -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.zfs import ZFSMockData -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - -# Import Salt Execution module to test -import salt.utils.zfs -import salt.modules.zfs as zfs +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Utils import salt.loader -from salt.utils.odict import OrderedDict +import salt.modules.zfs as zfs + +# Import Salt Execution module to test +import salt.utils.zfs from salt.utils.dateutils import strftime +from salt.utils.odict import OrderedDict +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +# Import Salt Testing libs +from tests.support.zfs import ZFSMockData # Skip this test case if we don't have access to mock! class ZfsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ This class contains a set of functions that test salt.modules.zfs module - ''' + """ + def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() self.utils_patch = ZFSMockData().get_patched_utils() - for key in ('opts', 'utils_patch'): + for key in ("opts", "utils_patch"): self.addCleanup(delattr, self, key) utils = salt.loader.utils( - opts, - whitelist=['zfs', 'args', 'systemd', 'path', 'platform']) - zfs_obj = { - zfs: { - '__opts__': opts, - '__utils__': utils, - } - } + opts, whitelist=["zfs", "args", "systemd", "path", "platform"] + ) + zfs_obj = {zfs: {"__opts__": opts, "__utils__": utils}} return zfs_obj def test_exists_success(self): - ''' + """ Tests successful return of exists function - ''' + """ ret = {} - ret['stdout'] = "NAME USED AVAIL REFER MOUNTPOINT\nmyzpool/mydataset 30K 157G 30K /myzpool/mydataset" - ret['stderr'] = '' - ret['retcode'] = 0 + ret[ + "stdout" + ] = "NAME USED AVAIL REFER MOUNTPOINT\nmyzpool/mydataset 30K 157G 30K /myzpool/mydataset" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertTrue(zfs.exists('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertTrue(zfs.exists("myzpool/mydataset")) def test_exists_failure_not_exists(self): - ''' + """ Tests unsuccessful return of exists function if dataset does not exist - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'myzpool/mydataset': dataset does not exist" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'myzpool/mydataset': dataset does not exist" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertFalse(zfs.exists('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertFalse(zfs.exists("myzpool/mydataset")) def test_exists_failure_invalid_name(self): - ''' + """ Tests unsuccessful return of exists function if dataset name is invalid - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'myzpool/': invalid dataset name" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'myzpool/': invalid dataset name" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertFalse(zfs.exists('myzpool/')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertFalse(zfs.exists("myzpool/")) def test_create_success(self): - ''' + """ Tests successful return of create function on ZFS file system creation - ''' - res = OrderedDict([('created', True)]) + """ + res = OrderedDict([("created", True)]) ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.create('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.create("myzpool/mydataset")) def test_create_success_with_create_parent(self): - ''' + """ Tests successful return of create function when ``create_parent=True`` - ''' - res = OrderedDict([('created', True)]) + """ + res = OrderedDict([("created", True)]) ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.create('myzpool/mydataset/mysubdataset', create_parent=True)) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, zfs.create("myzpool/mydataset/mysubdataset", create_parent=True) + ) def test_create_success_with_properties(self): - ''' + """ Tests successful return of create function on ZFS file system creation (with properties) - ''' - res = OrderedDict([('created', True)]) + """ + res = OrderedDict([("created", True)]) ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): self.assertEqual( res, zfs.create( - 'myzpool/mydataset', - properties={ - 'mountpoint': '/export/zfs', - 'sharenfs': 'on' - } + "myzpool/mydataset", + properties={"mountpoint": "/export/zfs", "sharenfs": "on"}, ), ) def test_create_error_missing_dataset(self): - ''' + """ Tests unsuccessful return of create function if dataset name is missing - ''' - res = OrderedDict([ - ('created', False), - ('error', "cannot create 'myzpool': missing dataset name"), - ]) + """ + res = OrderedDict( + [ + ("created", False), + ("error", "cannot create 'myzpool': missing dataset name"), + ] + ) ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot create 'myzpool': missing dataset name" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot create 'myzpool': missing dataset name" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.create('myzpool')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.create("myzpool")) def test_create_error_trailing_slash(self): - ''' + """ Tests unsuccessful return of create function if trailing slash in name is present - ''' - res = OrderedDict([ - ('created', False), - ('error', "cannot create 'myzpool/': trailing slash in name"), - ]) + """ + res = OrderedDict( + [ + ("created", False), + ("error", "cannot create 'myzpool/': trailing slash in name"), + ] + ) ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot create 'myzpool/': trailing slash in name" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot create 'myzpool/': trailing slash in name" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.create('myzpool/')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.create("myzpool/")) def test_create_error_no_such_pool(self): - ''' + """ Tests unsuccessful return of create function if the pool is not present - ''' - res = OrderedDict([ - ('created', False), - ('error', "cannot create 'myzpool/mydataset': no such pool 'myzpool'"), - ]) + """ + res = OrderedDict( + [ + ("created", False), + ("error", "cannot create 'myzpool/mydataset': no such pool 'myzpool'"), + ] + ) ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot create 'myzpool/mydataset': no such pool 'myzpool'" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot create 'myzpool/mydataset': no such pool 'myzpool'" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.create('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.create("myzpool/mydataset")) def test_create_error_missing_parent(self): - ''' + """ Tests unsuccessful return of create function if the parent datasets do not exist - ''' - res = OrderedDict([ - ('created', False), - ('error', "cannot create 'myzpool/mydataset/mysubdataset': parent does not exist"), - ]) + """ + res = OrderedDict( + [ + ("created", False), + ( + "error", + "cannot create 'myzpool/mydataset/mysubdataset': parent does not exist", + ), + ] + ) ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot create 'myzpool/mydataset/mysubdataset': parent does not exist" - ret['retcode'] = 1 + ret["stdout"] = "" + ret[ + "stderr" + ] = "cannot create 'myzpool/mydataset/mysubdataset': parent does not exist" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.create('myzpool/mydataset/mysubdataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.create("myzpool/mydataset/mysubdataset")) def test_destroy_success(self): - ''' + """ Tests successful return of destroy function on ZFS file system destruction - ''' - res = OrderedDict([('destroyed', True)]) + """ + res = OrderedDict([("destroyed", True)]) ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.destroy('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.destroy("myzpool/mydataset")) def test_destroy_error_not_exists(self): - ''' + """ Tests failure return of destroy function on ZFS file system destruction - ''' - res = OrderedDict([ - ('destroyed', False), - ('error', "cannot open 'myzpool/mydataset': dataset does not exist"), - ]) + """ + res = OrderedDict( + [ + ("destroyed", False), + ("error", "cannot open 'myzpool/mydataset': dataset does not exist"), + ] + ) ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'myzpool/mydataset': dataset does not exist" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'myzpool/mydataset': dataset does not exist" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.destroy('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.destroy("myzpool/mydataset")) def test_destroy_error_has_children(self): - ''' + """ Tests failure return of destroy function on ZFS file system destruction - ''' - res = OrderedDict([ - ('destroyed', False), - ('error', "\n".join([ - "cannot destroy 'myzpool/mydataset': filesystem has children", - "use 'recursive=True' to destroy the following datasets:", - "myzpool/mydataset@snapshot", - ])), - ]) + """ + res = OrderedDict( + [ + ("destroyed", False), + ( + "error", + "\n".join( + [ + "cannot destroy 'myzpool/mydataset': filesystem has children", + "use 'recursive=True' to destroy the following datasets:", + "myzpool/mydataset@snapshot", + ] + ), + ), + ] + ) ret = {} - ret['stdout'] = "" - ret['stderr'] = "\n".join([ - "cannot destroy 'myzpool/mydataset': filesystem has children", - "use '-r' to destroy the following datasets:", - "myzpool/mydataset@snapshot", - ]) - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "\n".join( + [ + "cannot destroy 'myzpool/mydataset': filesystem has children", + "use '-r' to destroy the following datasets:", + "myzpool/mydataset@snapshot", + ] + ) + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.destroy('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.destroy("myzpool/mydataset")) def test_rename_success(self): - ''' + """ Tests successful return of rename function - ''' - res = OrderedDict([('renamed', True)]) + """ + res = OrderedDict([("renamed", True)]) ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.rename('myzpool/mydataset', 'myzpool/newdataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.rename("myzpool/mydataset", "myzpool/newdataset")) def test_rename_error_not_exists(self): - ''' + """ Tests failure return of rename function - ''' - res = OrderedDict([ - ('renamed', False), - ('error', "cannot open 'myzpool/mydataset': dataset does not exist"), - ]) + """ + res = OrderedDict( + [ + ("renamed", False), + ("error", "cannot open 'myzpool/mydataset': dataset does not exist"), + ] + ) ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'myzpool/mydataset': dataset does not exist" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'myzpool/mydataset': dataset does not exist" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.rename('myzpool/mydataset', 'myzpool/newdataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.rename("myzpool/mydataset", "myzpool/newdataset")) def test_list_success(self): - ''' + """ Tests zfs list - ''' - res = OrderedDict([ - ('myzpool', OrderedDict([ - ('used', 849329782784), - ('avail', 1081258016768), - ('refer', 98304), - ('mountpoint', '/myzpool'), - ])), - ]) + """ + res = OrderedDict( + [ + ( + "myzpool", + OrderedDict( + [ + ("used", 849329782784), + ("avail", 1081258016768), + ("refer", 98304), + ("mountpoint", "/myzpool"), + ] + ), + ), + ] + ) ret = {} - ret['retcode'] = 0 - ret['stdout'] = 'myzpool\t791G\t1007G\t96K\t/myzpool' - ret['stderr'] = '' + ret["retcode"] = 0 + ret["stdout"] = "myzpool\t791G\t1007G\t96K\t/myzpool" + ret["stderr"] = "" mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.list_('myzpool')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.list_("myzpool")) def test_list_parsable_success(self): - ''' + """ Tests zfs list with parsable set to False - ''' - res = OrderedDict([ - ('myzpool', OrderedDict([ - ('used', '791G'), - ('avail', '1007G'), - ('refer', '96K'), - ('mountpoint', '/myzpool'), - ])), - ]) + """ + res = OrderedDict( + [ + ( + "myzpool", + OrderedDict( + [ + ("used", "791G"), + ("avail", "1007G"), + ("refer", "96K"), + ("mountpoint", "/myzpool"), + ] + ), + ), + ] + ) ret = {} - ret['retcode'] = 0 - ret['stdout'] = 'myzpool\t791G\t1007G\t96K\t/myzpool' - ret['stderr'] = '' + ret["retcode"] = 0 + ret["stdout"] = "myzpool\t791G\t1007G\t96K\t/myzpool" + ret["stderr"] = "" mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.list_('myzpool', parsable=False)) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.list_("myzpool", parsable=False)) def test_list_custom_success(self): - ''' + """ Tests zfs list - ''' - res = OrderedDict([ - ('myzpool', OrderedDict([ - ('canmount', True), - ('used', 849329782784), - ('avail', 1081258016768), - ('compression', False), - ])), - ]) + """ + res = OrderedDict( + [ + ( + "myzpool", + OrderedDict( + [ + ("canmount", True), + ("used", 849329782784), + ("avail", 1081258016768), + ("compression", False), + ] + ), + ), + ] + ) ret = {} - ret['retcode'] = 0 - ret['stdout'] = 'myzpool\ton\t791G\t1007G\toff' - ret['stderr'] = '' + ret["retcode"] = 0 + ret["stdout"] = "myzpool\ton\t791G\t1007G\toff" + ret["stderr"] = "" mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.list_('myzpool', properties='canmount,used,avail,compression')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, zfs.list_("myzpool", properties="canmount,used,avail,compression") + ) def test_list_custom_parsable_success(self): - ''' + """ Tests zfs list - ''' - res = OrderedDict([ - ('myzpool', OrderedDict([ - ('canmount', 'on'), - ('used', '791G'), - ('avail', '1007G'), - ('compression', 'off'), - ])), - ]) + """ + res = OrderedDict( + [ + ( + "myzpool", + OrderedDict( + [ + ("canmount", "on"), + ("used", "791G"), + ("avail", "1007G"), + ("compression", "off"), + ] + ), + ), + ] + ) ret = {} - ret['retcode'] = 0 - ret['stdout'] = 'myzpool\ton\t791G\t1007G\toff' - ret['stderr'] = '' + ret["retcode"] = 0 + ret["stdout"] = "myzpool\ton\t791G\t1007G\toff" + ret["stderr"] = "" mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.list_('myzpool', properties='canmount,used,avail,compression', parsable=False)) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, + zfs.list_( + "myzpool", + properties="canmount,used,avail,compression", + parsable=False, + ), + ) def test_list_error_no_dataset(self): - ''' + """ Tests zfs list - ''' + """ res = OrderedDict() ret = {} - ret['retcode'] = 1 - ret['stdout'] = "cannot open 'myzpool': dataset does not exist" - ret['stderr'] = '' + ret["retcode"] = 1 + ret["stdout"] = "cannot open 'myzpool': dataset does not exist" + ret["stderr"] = "" mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.list_('myzpool')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.list_("myzpool")) def test_list_mount_success(self): - ''' + """ Tests zfs list_mount - ''' - res = OrderedDict([ - ('myzpool/data', '/data'), - ('myzpool/data/ares', '/data/ares'), - ]) + """ + res = OrderedDict( + [("myzpool/data", "/data"), ("myzpool/data/ares", "/data/ares")] + ) ret = {} - ret['retcode'] = 0 - ret['stdout'] = "\n".join([ - "myzpool/data\t\t\t\t/data", - "myzpool/data/ares\t\t\t/data/ares", - ]) - ret['stderr'] = '' + ret["retcode"] = 0 + ret["stdout"] = "\n".join( + ["myzpool/data\t\t\t\t/data", "myzpool/data/ares\t\t\t/data/ares"] + ) + ret["stderr"] = "" mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): self.assertEqual(res, zfs.list_mount()) def test_mount_success(self): - ''' + """ Tests zfs mount of filesystem - ''' - res = OrderedDict([('mounted', True)]) + """ + res = OrderedDict([("mounted", True)]) ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.mount('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.mount("myzpool/mydataset")) def test_mount_failure(self): - ''' + """ Tests zfs mount of already mounted filesystem - ''' - res = OrderedDict([('mounted', False), ('error', "cannot mount 'myzpool/mydataset': filesystem already mounted")]) + """ + res = OrderedDict( + [ + ("mounted", False), + ( + "error", + "cannot mount 'myzpool/mydataset': filesystem already mounted", + ), + ] + ) ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot mount 'myzpool/mydataset': filesystem already mounted" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot mount 'myzpool/mydataset': filesystem already mounted" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.mount('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.mount("myzpool/mydataset")) def test_unmount_success(self): - ''' + """ Tests zfs unmount of filesystem - ''' - res = OrderedDict([('unmounted', True)]) + """ + res = OrderedDict([("unmounted", True)]) ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.unmount('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.unmount("myzpool/mydataset")) def test_unmount_failure(self): - ''' + """ Tests zfs unmount of already mounted filesystem - ''' - res = OrderedDict([ - ('unmounted', False), - ('error', "cannot mount 'myzpool/mydataset': not currently mounted"), - ]) + """ + res = OrderedDict( + [ + ("unmounted", False), + ("error", "cannot mount 'myzpool/mydataset': not currently mounted"), + ] + ) ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot mount 'myzpool/mydataset': not currently mounted" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot mount 'myzpool/mydataset': not currently mounted" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.unmount('myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.unmount("myzpool/mydataset")) def test_inherit_success(self): - ''' + """ Tests zfs inherit of compression property - ''' - res = OrderedDict([('inherited', True)]) - ret = {'pid': 45193, 'retcode': 0, 'stderr': '', 'stdout': ''} + """ + res = OrderedDict([("inherited", True)]) + ret = {"pid": 45193, "retcode": 0, "stderr": "", "stdout": ""} mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.inherit('compression', 'myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.inherit("compression", "myzpool/mydataset")) def test_inherit_failure(self): - ''' + """ Tests zfs inherit of canmount - ''' - res = OrderedDict([ - ('inherited', False), - ('error', "'canmount' property cannot be inherited"), - ]) - ret = {'pid': 43898, 'retcode': 1, 'stderr': "'canmount' property cannot be inherited", 'stdout': ''} + """ + res = OrderedDict( + [ + ("inherited", False), + ("error", "'canmount' property cannot be inherited"), + ] + ) + ret = { + "pid": 43898, + "retcode": 1, + "stderr": "'canmount' property cannot be inherited", + "stdout": "", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.inherit('canmount', 'myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.inherit("canmount", "myzpool/mydataset")) def test_diff(self): - ''' + """ Tests zfs diff - ''' + """ res = [ "1517063879.144517494\tM\t\t/data/test/", "1517063875.296592355\t+\t\t/data/test/world", "1517063879.274438467\t+\t\t/data/test/hello", ] ret = {} - ret['retcode'] = 0 - ret['stdout'] = "\n".join([ - "1517063879.144517494\tM\t\t/data/test/", - "1517063875.296592355\t+\t\t/data/test/world", - "1517063879.274438467\t+\t\t/data/test/hello", - ]) - ret['stderr'] = '' + ret["retcode"] = 0 + ret["stdout"] = "\n".join( + [ + "1517063879.144517494\tM\t\t/data/test/", + "1517063875.296592355\t+\t\t/data/test/world", + "1517063879.274438467\t+\t\t/data/test/hello", + ] + ) + ret["stderr"] = "" mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.diff('myzpool/mydataset@yesterday', 'myzpool/mydataset')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, zfs.diff("myzpool/mydataset@yesterday", "myzpool/mydataset") + ) def test_diff_parsed_time(self): - ''' + """ Tests zfs diff - ''' + """ ## NOTE: do not hardcode parsed timestamps, timezone play a role here. ## zfs diff output seems to be timezone aware - res = OrderedDict([ - (strftime(1517063879.144517494, '%Y-%m-%d.%H:%M:%S.%f'), 'M\t\t/data/test/'), - (strftime(1517063875.296592355, '%Y-%m-%d.%H:%M:%S.%f'), '+\t\t/data/test/world'), - (strftime(1517063879.274438467, '%Y-%m-%d.%H:%M:%S.%f'), '+\t\t/data/test/hello'), - ]) + res = OrderedDict( + [ + ( + strftime(1517063879.144517494, "%Y-%m-%d.%H:%M:%S.%f"), + "M\t\t/data/test/", + ), + ( + strftime(1517063875.296592355, "%Y-%m-%d.%H:%M:%S.%f"), + "+\t\t/data/test/world", + ), + ( + strftime(1517063879.274438467, "%Y-%m-%d.%H:%M:%S.%f"), + "+\t\t/data/test/hello", + ), + ] + ) ret = {} - ret['retcode'] = 0 - ret['stdout'] = "\n".join([ - "1517063879.144517494\tM\t\t/data/test/", - "1517063875.296592355\t+\t\t/data/test/world", - "1517063879.274438467\t+\t\t/data/test/hello", - ]) - ret['stderr'] = '' + ret["retcode"] = 0 + ret["stdout"] = "\n".join( + [ + "1517063879.144517494\tM\t\t/data/test/", + "1517063875.296592355\t+\t\t/data/test/world", + "1517063879.274438467\t+\t\t/data/test/hello", + ] + ) + ret["stderr"] = "" mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.diff('myzpool/data@yesterday', 'myzpool/data', parsable=False)) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, zfs.diff("myzpool/data@yesterday", "myzpool/data", parsable=False) + ) def test_rollback_success(self): - ''' + """ Tests zfs rollback success - ''' - res = OrderedDict([('rolledback', True)]) - ret = {'pid': 56502, 'retcode': 0, 'stderr': '', 'stdout': ''} + """ + res = OrderedDict([("rolledback", True)]) + ret = {"pid": 56502, "retcode": 0, "stderr": "", "stdout": ""} mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.rollback('myzpool/mydataset@yesterday')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.rollback("myzpool/mydataset@yesterday")) def test_rollback_failure(self): - ''' + """ Tests zfs rollback failure - ''' - res = OrderedDict([ - ('rolledback', False), - ('error', "\n".join([ - "cannot rollback to 'myzpool/mydataset@yesterday': more recent snapshots or bookmarks exist", - "use 'recursive=True' to force deletion of the following snapshots and bookmarks:", - "myzpool/mydataset@today" - ]), - ), - ]) + """ + res = OrderedDict( + [ + ("rolledback", False), + ( + "error", + "\n".join( + [ + "cannot rollback to 'myzpool/mydataset@yesterday': more recent snapshots or bookmarks exist", + "use 'recursive=True' to force deletion of the following snapshots and bookmarks:", + "myzpool/mydataset@today", + ] + ), + ), + ] + ) ret = { - 'pid': 57471, - 'retcode': 1, - 'stderr': "cannot rollback to 'myzpool/mydataset@yesterday': more recent snapshots or bookmarks " - "exist\nuse '-r' to force deletion of the following snapshots and " - "bookmarks:\nmyzpool/mydataset@today", - 'stdout': '' + "pid": 57471, + "retcode": 1, + "stderr": "cannot rollback to 'myzpool/mydataset@yesterday': more recent snapshots or bookmarks " + "exist\nuse '-r' to force deletion of the following snapshots and " + "bookmarks:\nmyzpool/mydataset@today", + "stdout": "", } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.rollback('myzpool/mydataset@yesterday')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.rollback("myzpool/mydataset@yesterday")) def test_clone_success(self): - ''' + """ Tests zfs clone success - ''' - res = OrderedDict([('cloned', True)]) - ret = {'pid': 64532, 'retcode': 0, 'stderr': '', 'stdout': ''} + """ + res = OrderedDict([("cloned", True)]) + ret = {"pid": 64532, "retcode": 0, "stderr": "", "stdout": ""} mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.clone('myzpool/mydataset@yesterday', 'myzpool/yesterday')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, zfs.clone("myzpool/mydataset@yesterday", "myzpool/yesterday") + ) def test_clone_failure(self): - ''' + """ Tests zfs clone failure - ''' - res = OrderedDict([ - ('cloned', False), - ('error', "cannot create 'myzpool/archive/yesterday': parent does not exist"), - ]) - ret = {'pid': 64864, 'retcode': 1, 'stderr': "cannot create 'myzpool/archive/yesterday': parent does not exist", 'stdout': ''} + """ + res = OrderedDict( + [ + ("cloned", False), + ( + "error", + "cannot create 'myzpool/archive/yesterday': parent does not exist", + ), + ] + ) + ret = { + "pid": 64864, + "retcode": 1, + "stderr": "cannot create 'myzpool/archive/yesterday': parent does not exist", + "stdout": "", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.clone('myzpool/mydataset@yesterday', 'myzpool/archive/yesterday')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, + zfs.clone("myzpool/mydataset@yesterday", "myzpool/archive/yesterday"), + ) def test_promote_success(self): - ''' + """ Tests zfs promote success - ''' - res = OrderedDict([('promoted', True)]) - ret = {'pid': 69075, 'retcode': 0, 'stderr': '', 'stdout': ''} + """ + res = OrderedDict([("promoted", True)]) + ret = {"pid": 69075, "retcode": 0, "stderr": "", "stdout": ""} mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.promote('myzpool/yesterday')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.promote("myzpool/yesterday")) def test_promote_failure(self): - ''' + """ Tests zfs promote failure - ''' - res = OrderedDict([ - ('promoted', False), - ('error', "cannot promote 'myzpool/yesterday': not a cloned filesystem"), - ]) - ret = {'pid': 69209, 'retcode': 1, 'stderr': "cannot promote 'myzpool/yesterday': not a cloned filesystem", 'stdout': ''} + """ + res = OrderedDict( + [ + ("promoted", False), + ( + "error", + "cannot promote 'myzpool/yesterday': not a cloned filesystem", + ), + ] + ) + ret = { + "pid": 69209, + "retcode": 1, + "stderr": "cannot promote 'myzpool/yesterday': not a cloned filesystem", + "stdout": "", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.promote('myzpool/yesterday')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.promote("myzpool/yesterday")) def test_bookmark_success(self): - ''' + """ Tests zfs bookmark success - ''' - with patch('salt.utils.path.which', MagicMock(return_value='/usr/bin/man')): - res = OrderedDict([('bookmarked', True)]) - ret = {'pid': 20990, 'retcode': 0, 'stderr': '', 'stdout': ''} + """ + with patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/man")): + res = OrderedDict([("bookmarked", True)]) + ret = {"pid": 20990, "retcode": 0, "stderr": "", "stdout": ""} mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.bookmark('myzpool/mydataset@yesterday', 'myzpool/mydataset#important')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, + zfs.bookmark( + "myzpool/mydataset@yesterday", "myzpool/mydataset#important" + ), + ) def test_holds_success(self): - ''' + """ Tests zfs holds success - ''' - res = OrderedDict([ - ('important', 'Wed Dec 23 21:06 2015'), - ('release-1.0', 'Wed Dec 23 21:08 2015'), - ]) - ret = {'pid': 40216, 'retcode': 0, 'stderr': '', 'stdout': 'myzpool/mydataset@baseline\timportant \tWed Dec 23 21:06 2015\nmyzpool/mydataset@baseline\trelease-1.0\tWed Dec 23 21:08 2015'} + """ + res = OrderedDict( + [ + ("important", "Wed Dec 23 21:06 2015"), + ("release-1.0", "Wed Dec 23 21:08 2015"), + ] + ) + ret = { + "pid": 40216, + "retcode": 0, + "stderr": "", + "stdout": "myzpool/mydataset@baseline\timportant \tWed Dec 23 21:06 2015\nmyzpool/mydataset@baseline\trelease-1.0\tWed Dec 23 21:08 2015", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.holds('myzpool/mydataset@baseline')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.holds("myzpool/mydataset@baseline")) def test_holds_failure(self): - ''' + """ Tests zfs holds failure - ''' - res = OrderedDict([ - ('error', "cannot open 'myzpool/mydataset@baseline': dataset does not exist"), - ]) - ret = {'pid': 40993, 'retcode': 1, 'stderr': "cannot open 'myzpool/mydataset@baseline': dataset does not exist", 'stdout': 'no datasets available'} + """ + res = OrderedDict( + [ + ( + "error", + "cannot open 'myzpool/mydataset@baseline': dataset does not exist", + ), + ] + ) + ret = { + "pid": 40993, + "retcode": 1, + "stderr": "cannot open 'myzpool/mydataset@baseline': dataset does not exist", + "stdout": "no datasets available", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.holds('myzpool/mydataset@baseline')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.holds("myzpool/mydataset@baseline")) def test_hold_success(self): - ''' + """ Tests zfs hold success - ''' - res = OrderedDict([('held', True)]) - ret = {'pid': 50876, 'retcode': 0, 'stderr': '', 'stdout': ''} + """ + res = OrderedDict([("held", True)]) + ret = {"pid": 50876, "retcode": 0, "stderr": "", "stdout": ""} mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.hold('important', 'myzpool/mydataset@baseline', 'myzpool/mydataset@release-1.0')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, + zfs.hold( + "important", + "myzpool/mydataset@baseline", + "myzpool/mydataset@release-1.0", + ), + ) def test_hold_failure(self): - ''' + """ Tests zfs hold failure - ''' - res = OrderedDict([ - ('held', False), - ('error', "cannot hold snapshot 'myzpool/mydataset@baseline': tag already exists on this dataset"), - ]) - ret = {'pid': 51006, 'retcode': 1, 'stderr': "cannot hold snapshot 'myzpool/mydataset@baseline': tag already exists on this dataset", 'stdout': ''} + """ + res = OrderedDict( + [ + ("held", False), + ( + "error", + "cannot hold snapshot 'myzpool/mydataset@baseline': tag already exists on this dataset", + ), + ] + ) + ret = { + "pid": 51006, + "retcode": 1, + "stderr": "cannot hold snapshot 'myzpool/mydataset@baseline': tag already exists on this dataset", + "stdout": "", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.hold('important', 'myzpool/mydataset@baseline')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.hold("important", "myzpool/mydataset@baseline")) def test_release_success(self): - ''' + """ Tests zfs release success - ''' - res = OrderedDict([('released', True)]) - ret = {'pid': 50876, 'retcode': 0, 'stderr': '', 'stdout': ''} + """ + res = OrderedDict([("released", True)]) + ret = {"pid": 50876, "retcode": 0, "stderr": "", "stdout": ""} mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.release('important', 'myzpool/mydataset@baseline', 'myzpool/mydataset@release-1.0')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, + zfs.release( + "important", + "myzpool/mydataset@baseline", + "myzpool/mydataset@release-1.0", + ), + ) def test_release_failure(self): - ''' + """ Tests zfs release failure - ''' - res = OrderedDict([ - ('released', False), - ('error', "cannot release hold from snapshot 'myzpool/mydataset@baseline': no such tag on this dataset"), - ]) - ret = {'pid': 51006, 'retcode': 1, 'stderr': "cannot release hold from snapshot 'myzpool/mydataset@baseline': no such tag on this dataset", 'stdout': ''} + """ + res = OrderedDict( + [ + ("released", False), + ( + "error", + "cannot release hold from snapshot 'myzpool/mydataset@baseline': no such tag on this dataset", + ), + ] + ) + ret = { + "pid": 51006, + "retcode": 1, + "stderr": "cannot release hold from snapshot 'myzpool/mydataset@baseline': no such tag on this dataset", + "stdout": "", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.release('important', 'myzpool/mydataset@baseline')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, zfs.release("important", "myzpool/mydataset@baseline") + ) def test_snapshot_success(self): - ''' + """ Tests zfs snapshot success - ''' - res = OrderedDict([('snapshotted', True)]) - ret = {'pid': 69125, 'retcode': 0, 'stderr': '', 'stdout': ''} + """ + res = OrderedDict([("snapshotted", True)]) + ret = {"pid": 69125, "retcode": 0, "stderr": "", "stdout": ""} mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.snapshot('myzpool/mydataset@baseline')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.snapshot("myzpool/mydataset@baseline")) def test_snapshot_failure(self): - ''' + """ Tests zfs snapshot failure - ''' - res = OrderedDict([ - ('snapshotted', False), - ('error', "cannot create snapshot 'myzpool/mydataset@baseline': dataset already exists"), - ]) - ret = {'pid': 68526, 'retcode': 1, 'stderr': "cannot create snapshot 'myzpool/mydataset@baseline': dataset already exists", 'stdout': ''} + """ + res = OrderedDict( + [ + ("snapshotted", False), + ( + "error", + "cannot create snapshot 'myzpool/mydataset@baseline': dataset already exists", + ), + ] + ) + ret = { + "pid": 68526, + "retcode": 1, + "stderr": "cannot create snapshot 'myzpool/mydataset@baseline': dataset already exists", + "stdout": "", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.snapshot('myzpool/mydataset@baseline')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.snapshot("myzpool/mydataset@baseline")) def test_snapshot_failure2(self): - ''' + """ Tests zfs snapshot failure - ''' - res = OrderedDict([ - ('snapshotted', False), - ('error', "cannot open 'myzpool/mydataset': dataset does not exist"), - ]) - ret = {'pid': 69256, 'retcode': 2, 'stderr': "cannot open 'myzpool/mydataset': dataset does not exist\nusage:\n\tsnapshot [-r] [-o property=value] ... <filesystem|volume>@<snap> ...\n\nFor the property list, run: zfs set|get\n\nFor the delegated permission list, run: zfs allow|unallow", 'stdout': ''} + """ + res = OrderedDict( + [ + ("snapshotted", False), + ("error", "cannot open 'myzpool/mydataset': dataset does not exist"), + ] + ) + ret = { + "pid": 69256, + "retcode": 2, + "stderr": "cannot open 'myzpool/mydataset': dataset does not exist\nusage:\n\tsnapshot [-r] [-o property=value] ... <filesystem|volume>@<snap> ...\n\nFor the property list, run: zfs set|get\n\nFor the delegated permission list, run: zfs allow|unallow", + "stdout": "", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.snapshot('myzpool/mydataset@baseline')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.snapshot("myzpool/mydataset@baseline")) def test_set_success(self): - ''' + """ Tests zfs set success - ''' - res = OrderedDict([('set', True)]) - ret = {'pid': 79736, 'retcode': 0, 'stderr': '', 'stdout': ''} + """ + res = OrderedDict([("set", True)]) + ret = {"pid": 79736, "retcode": 0, "stderr": "", "stdout": ""} mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.set('myzpool/mydataset', compression='lz4')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.set("myzpool/mydataset", compression="lz4")) def test_set_failure(self): - ''' + """ Tests zfs set failure - ''' - res = OrderedDict([ - ('set', False), - ('error', "cannot set property for 'myzpool/mydataset': 'canmount' must be one of 'on | off | noauto'"), - ]) - ret = {'pid': 79887, 'retcode': 1, 'stderr': "cannot set property for 'myzpool/mydataset': 'canmount' must be one of 'on | off | noauto'", 'stdout': ''} + """ + res = OrderedDict( + [ + ("set", False), + ( + "error", + "cannot set property for 'myzpool/mydataset': 'canmount' must be one of 'on | off | noauto'", + ), + ] + ) + ret = { + "pid": 79887, + "retcode": 1, + "stderr": "cannot set property for 'myzpool/mydataset': 'canmount' must be one of 'on | off | noauto'", + "stdout": "", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.set('myzpool/mydataset', canmount='lz4')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.set("myzpool/mydataset", canmount="lz4")) def test_get_success(self): - ''' + """ Tests zfs get success - ''' - res = OrderedDict([ - ('myzpool', OrderedDict([ - ('used', OrderedDict([ - ('value', 906238099456), - ])), - ])), - ]) - ret = {'pid': 562, 'retcode': 0, 'stderr': '', 'stdout': 'myzpool\tused\t906238099456'} + """ + res = OrderedDict( + [ + ( + "myzpool", + OrderedDict([("used", OrderedDict([("value", 906238099456)]))]), + ), + ] + ) + ret = { + "pid": 562, + "retcode": 0, + "stderr": "", + "stdout": "myzpool\tused\t906238099456", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.get('myzpool', properties='used', fields='value')) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(res, zfs.get("myzpool", properties="used", fields="value")) def test_get_parsable_success(self): - ''' + """ Tests zfs get with parsable output - ''' - res = OrderedDict([ - ('myzpool', OrderedDict([ - ('used', OrderedDict([ - ('value', '844G'), - ])), - ])), - ]) - ret = {'pid': 562, 'retcode': 0, 'stderr': '', 'stdout': 'myzpool\tused\t906238099456'} + """ + res = OrderedDict( + [("myzpool", OrderedDict([("used", OrderedDict([("value", "844G")]))]))] + ) + ret = { + "pid": 562, + "retcode": 0, + "stderr": "", + "stdout": "myzpool\tused\t906238099456", + } mock_cmd = MagicMock(return_value=ret) - with patch.dict(zfs.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(res, zfs.get('myzpool', properties='used', fields='value', parsable=False)) + with patch.dict(zfs.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + res, + zfs.get("myzpool", properties="used", fields="value", parsable=False), + ) diff --git a/tests/unit/modules/test_znc.py b/tests/unit/modules/test_znc.py index 5cfe82d909b..a95996466d6 100644 --- a/tests/unit/modules/test_znc.py +++ b/tests/unit/modules/test_znc.py @@ -1,77 +1,80 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.modules.znc as znc +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ZncTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.modules.znc - ''' + """ + def setup_loader_modules(self): return {znc: {}} # 'buildmod' function tests: 1 def test_buildmod(self): - ''' + """ Tests build module using znc-buildmod - ''' - with patch('os.path.exists', MagicMock(return_value=False)): - self.assertEqual(znc.buildmod('modules.cpp'), - 'Error: The file (modules.cpp) does not exist.') + """ + with patch("os.path.exists", MagicMock(return_value=False)): + self.assertEqual( + znc.buildmod("modules.cpp"), + "Error: The file (modules.cpp) does not exist.", + ) def test_buildmod_module(self): - ''' + """ Tests build module using znc-buildmod - ''' - mock = MagicMock(return_value='SALT') - with patch.dict(znc.__salt__, {'cmd.run': mock}), \ - patch('os.path.exists', MagicMock(return_value=True)): - self.assertEqual(znc.buildmod('modules.cpp'), 'SALT') + """ + mock = MagicMock(return_value="SALT") + with patch.dict(znc.__salt__, {"cmd.run": mock}), patch( + "os.path.exists", MagicMock(return_value=True) + ): + self.assertEqual(znc.buildmod("modules.cpp"), "SALT") # 'dumpconf' function tests: 1 def test_dumpconf(self): - ''' + """ Tests write the active configuration state to config file - ''' - mock = MagicMock(return_value='SALT') - with patch.dict(znc.__salt__, {'ps.pkill': mock}), \ - patch.object(znc, 'signal', MagicMock()): - self.assertEqual(znc.dumpconf(), 'SALT') + """ + mock = MagicMock(return_value="SALT") + with patch.dict(znc.__salt__, {"ps.pkill": mock}), patch.object( + znc, "signal", MagicMock() + ): + self.assertEqual(znc.dumpconf(), "SALT") # 'rehashconf' function tests: 1 def test_rehashconf(self): - ''' + """ Tests rehash the active configuration state from config file - ''' - mock = MagicMock(return_value='SALT') - with patch.dict(znc.__salt__, {'ps.pkill': mock}), \ - patch.object(znc, 'signal', MagicMock()): - self.assertEqual(znc.rehashconf(), 'SALT') + """ + mock = MagicMock(return_value="SALT") + with patch.dict(znc.__salt__, {"ps.pkill": mock}), patch.object( + znc, "signal", MagicMock() + ): + self.assertEqual(znc.rehashconf(), "SALT") # 'version' function tests: 1 def test_version(self): - ''' + """ Tests return server version from znc --version - ''' - mock = MagicMock(return_value='ZNC 1.2 - http://znc.in') - with patch.dict(znc.__salt__, {'cmd.run': mock}): - self.assertEqual(znc.version(), 'ZNC 1.2') + """ + mock = MagicMock(return_value="ZNC 1.2 - http://znc.in") + with patch.dict(znc.__salt__, {"cmd.run": mock}): + self.assertEqual(znc.version(), "ZNC 1.2") diff --git a/tests/unit/modules/test_zpool.py b/tests/unit/modules/test_zpool.py index 8ebb96008c4..64316981c96 100644 --- a/tests/unit/modules/test_zpool.py +++ b/tests/unit/modules/test_zpool.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.modules.zpool :codeauthor: Nitin Madhok <nmadhok@clemson.edu>, Jorge Schrauwen <sjorge@blackdot.be> @@ -7,872 +7,1009 @@ Tests for salt.modules.zpool :maturity: new :depends: salt.utils.zfs :platform: illumos,freebsd,linux -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.zfs import ZFSMockData -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - +# Import Salt Utils +import salt.loader +import salt.modules.zpool as zpool +import salt.utils.decorators +import salt.utils.decorators.path # Import Salt Execution module to test import salt.utils.zfs -import salt.modules.zpool as zpool - -# Import Salt Utils -import salt.loader from salt.utils.odict import OrderedDict -import salt.utils.decorators -import salt.utils.decorators.path +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + +# Import Salt Testing libs +from tests.support.zfs import ZFSMockData # Skip this test case if we don't have access to mock! class ZpoolTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ This class contains a set of functions that test salt.modules.zpool module - ''' + """ + def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() self.utils_patch = ZFSMockData().get_patched_utils() - for key in ('opts', 'utils_patch'): + for key in ("opts", "utils_patch"): self.addCleanup(delattr, self, key) utils = salt.loader.utils( - opts, - whitelist=['zfs', 'args', 'systemd', 'path', 'platform']) - zpool_obj = { - zpool: { - '__opts__': opts, - '__utils__': utils, - } - } + opts, whitelist=["zfs", "args", "systemd", "path", "platform"] + ) + zpool_obj = {zpool: {"__opts__": opts, "__utils__": utils}} return zpool_obj def test_exists_success(self): - ''' + """ Tests successful return of exists function - ''' + """ ret = {} - ret['stdout'] = "NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT\n" \ - "myzpool 149G 128K 149G 0% 1.00x ONLINE -" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = ( + "NAME SIZE ALLOC FREE CAP DEDUP HEALTH ALTROOT\n" + "myzpool 149G 128K 149G 0% 1.00x ONLINE -" + ) + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - self.assertTrue(zpool.exists('myzpool')) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + self.assertTrue(zpool.exists("myzpool")) def test_exists_failure(self): - ''' + """ Tests failure return of exists function - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'myzpool': no such pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'myzpool': no such pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - self.assertFalse(zpool.exists('myzpool')) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + self.assertFalse(zpool.exists("myzpool")) def test_healthy(self): - ''' + """ Tests successful return of healthy function - ''' + """ ret = {} - ret['stdout'] = "all pools are healthy" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "all pools are healthy" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): self.assertTrue(zpool.healthy()) def test_status(self): - ''' + """ Tests successful return of status function - ''' + """ ret = {} - ret['stdout'] = "\n".join([ - " pool: mypool", - " state: ONLINE", - " scan: scrub repaired 0 in 0h6m with 0 errors on Mon Dec 21 02:06:17 2015", - "config:", - "", - "\tNAME STATE READ WRITE CKSUM", - "\tmypool ONLINE 0 0 0", - "\t mirror-0 ONLINE 0 0 0", - "\t c2t0d0 ONLINE 0 0 0", - "\t c2t1d0 ONLINE 0 0 0", - "", - "errors: No known data errors", - ]) - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "\n".join( + [ + " pool: mypool", + " state: ONLINE", + " scan: scrub repaired 0 in 0h6m with 0 errors on Mon Dec 21 02:06:17 2015", + "config:", + "", + "\tNAME STATE READ WRITE CKSUM", + "\tmypool ONLINE 0 0 0", + "\t mirror-0 ONLINE 0 0 0", + "\t c2t0d0 ONLINE 0 0 0", + "\t c2t1d0 ONLINE 0 0 0", + "", + "errors: No known data errors", + ] + ) + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): ret = zpool.status() - self.assertEqual('ONLINE', ret['mypool']['state']) + self.assertEqual("ONLINE", ret["mypool"]["state"]) def test_iostat(self): - ''' + """ Tests successful return of iostat function - ''' + """ ret = {} - ret['stdout'] = "\n".join([ - " capacity operations bandwidth", - "pool alloc free read write read write", - "---------- ----- ----- ----- ----- ----- -----", - "mypool 46.7G 64.3G 4 19 113K 331K", - " mirror 46.7G 64.3G 4 19 113K 331K", - " c2t0d0 - - 1 10 114K 334K", - " c2t1d0 - - 1 10 114K 334K", - "---------- ----- ----- ----- ----- ----- -----", - ]) - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "\n".join( + [ + " capacity operations bandwidth", + "pool alloc free read write read write", + "---------- ----- ----- ----- ----- ----- -----", + "mypool 46.7G 64.3G 4 19 113K 331K", + " mirror 46.7G 64.3G 4 19 113K 331K", + " c2t0d0 - - 1 10 114K 334K", + " c2t1d0 - - 1 10 114K 334K", + "---------- ----- ----- ----- ----- ----- -----", + ] + ) + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.iostat('mypool', parsable=False) - self.assertEqual('46.7G', ret['mypool']['capacity-alloc']) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.iostat("mypool", parsable=False) + self.assertEqual("46.7G", ret["mypool"]["capacity-alloc"]) def test_iostat_parsable(self): - ''' + """ Tests successful return of iostat function .. note: The command output is the same as the non parsable! There is no -p flag for zpool iostat, but our type conversions can handle this! - ''' + """ ret = {} - ret['stdout'] = "\n".join([ - " capacity operations bandwidth", - "pool alloc free read write read write", - "---------- ----- ----- ----- ----- ----- -----", - "mypool 46.7G 64.3G 4 19 113K 331K", - " mirror 46.7G 64.3G 4 19 113K 331K", - " c2t0d0 - - 1 10 114K 334K", - " c2t1d0 - - 1 10 114K 334K", - "---------- ----- ----- ----- ----- ----- -----", - ]) - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "\n".join( + [ + " capacity operations bandwidth", + "pool alloc free read write read write", + "---------- ----- ----- ----- ----- ----- -----", + "mypool 46.7G 64.3G 4 19 113K 331K", + " mirror 46.7G 64.3G 4 19 113K 331K", + " c2t0d0 - - 1 10 114K 334K", + " c2t1d0 - - 1 10 114K 334K", + "---------- ----- ----- ----- ----- ----- -----", + ] + ) + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.iostat('mypool', parsable=True) - self.assertEqual(50143743180, ret['mypool']['capacity-alloc']) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.iostat("mypool", parsable=True) + self.assertEqual(50143743180, ret["mypool"]["capacity-alloc"]) def test_list(self): - ''' + """ Tests successful return of list function - ''' + """ ret = {} - ret['stdout'] = "mypool\t1.81T\t661G\t1.17T\t35%\t11%\tONLINE" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "mypool\t1.81T\t661G\t1.17T\t35%\t11%\tONLINE" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): ret = zpool.list_(parsable=False) - res = OrderedDict([('mypool', OrderedDict([ - ('size', '1.81T'), - ('alloc', '661G'), - ('free', '1.17T'), - ('cap', '35%'), - ('frag', '11%'), - ('health', 'ONLINE'), - ]))]) + res = OrderedDict( + [ + ( + "mypool", + OrderedDict( + [ + ("size", "1.81T"), + ("alloc", "661G"), + ("free", "1.17T"), + ("cap", "35%"), + ("frag", "11%"), + ("health", "ONLINE"), + ] + ), + ) + ] + ) self.assertEqual(ret, res) def test_list_parsable(self): - ''' + """ Tests successful return of list function with parsable output - ''' + """ ret = {} - ret['stdout'] = "mypool\t1.81T\t661G\t1.17T\t35%\t11%\tONLINE" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "mypool\t1.81T\t661G\t1.17T\t35%\t11%\tONLINE" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): ret = zpool.list_(parsable=True) - res = OrderedDict([('mypool', OrderedDict([ - ('size', 1990116046274), - ('alloc', 709743345664), - ('free', 1286428604497), - ('cap', '35%'), - ('frag', '11%'), - ('health', 'ONLINE'), - ]))]) + res = OrderedDict( + [ + ( + "mypool", + OrderedDict( + [ + ("size", 1990116046274), + ("alloc", 709743345664), + ("free", 1286428604497), + ("cap", "35%"), + ("frag", "11%"), + ("health", "ONLINE"), + ] + ), + ) + ] + ) self.assertEqual(ret, res) def test_get(self): - ''' + """ Tests successful return of get function - ''' + """ ret = {} - ret['stdout'] = "mypool\tsize\t1.81T\t-\n" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "mypool\tsize\t1.81T\t-\n" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.get('mypool', 'size', parsable=False) - res = OrderedDict(OrderedDict([('size', '1.81T')])) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.get("mypool", "size", parsable=False) + res = OrderedDict(OrderedDict([("size", "1.81T")])) self.assertEqual(ret, res) def test_get_parsable(self): - ''' + """ Tests successful return of get function with parsable output - ''' + """ ret = {} - ret['stdout'] = "mypool\tsize\t1.81T\t-\n" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "mypool\tsize\t1.81T\t-\n" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.get('mypool', 'size', parsable=True) - res = OrderedDict(OrderedDict([('size', 1990116046274)])) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.get("mypool", "size", parsable=True) + res = OrderedDict(OrderedDict([("size", 1990116046274)])) self.assertEqual(ret, res) def test_get_whitespace(self): - ''' + """ Tests successful return of get function with a string with whitespaces - ''' + """ ret = {} - ret['stdout'] = "mypool\tcomment\tmy testing pool\t-\n" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "mypool\tcomment\tmy testing pool\t-\n" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.get('mypool', 'comment') - res = OrderedDict(OrderedDict([('comment', "my testing pool")])) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.get("mypool", "comment") + res = OrderedDict(OrderedDict([("comment", "my testing pool")])) self.assertEqual(ret, res) def test_scrub_start(self): - ''' + """ Tests start of scrub - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) mock_exists = MagicMock(return_value=True) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.scrub('mypool') - res = OrderedDict(OrderedDict([('scrubbing', True)])) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"cmd.run_all": mock_cmd} + ), patch.dict(zpool.__utils__, self.utils_patch): + ret = zpool.scrub("mypool") + res = OrderedDict(OrderedDict([("scrubbing", True)])) self.assertEqual(ret, res) def test_scrub_pause(self): - ''' + """ Tests pause of scrub - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) mock_exists = MagicMock(return_value=True) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.scrub('mypool', pause=True) - res = OrderedDict(OrderedDict([('scrubbing', False)])) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"cmd.run_all": mock_cmd} + ), patch.dict(zpool.__utils__, self.utils_patch): + ret = zpool.scrub("mypool", pause=True) + res = OrderedDict(OrderedDict([("scrubbing", False)])) self.assertEqual(ret, res) def test_scrub_stop(self): - ''' + """ Tests pauze of scrub - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) mock_exists = MagicMock(return_value=True) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.scrub('mypool', stop=True) - res = OrderedDict(OrderedDict([('scrubbing', False)])) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"cmd.run_all": mock_cmd} + ), patch.dict(zpool.__utils__, self.utils_patch): + ret = zpool.scrub("mypool", stop=True) + res = OrderedDict(OrderedDict([("scrubbing", False)])) self.assertEqual(ret, res) def test_split_success(self): - ''' + """ Tests split on success - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.split('datapool', 'backuppool') - res = OrderedDict([('split', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.split("datapool", "backuppool") + res = OrderedDict([("split", True)]) self.assertEqual(ret, res) def test_split_exist_new(self): - ''' + """ Tests split on exising new pool - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "Unable to split datapool: pool already exists" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "Unable to split datapool: pool already exists" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.split('datapool', 'backuppool') - res = OrderedDict([('split', False), ('error', 'Unable to split datapool: pool already exists')]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.split("datapool", "backuppool") + res = OrderedDict( + [ + ("split", False), + ("error", "Unable to split datapool: pool already exists"), + ] + ) self.assertEqual(ret, res) def test_split_missing_pool(self): - ''' + """ Tests split on missing source pool - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'datapool': no such pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'datapool': no such pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.split('datapool', 'backuppool') - res = OrderedDict([('split', False), ('error', "cannot open 'datapool': no such pool")]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.split("datapool", "backuppool") + res = OrderedDict( + [("split", False), ("error", "cannot open 'datapool': no such pool")] + ) self.assertEqual(ret, res) def test_split_not_mirror(self): - ''' + """ Tests split on source pool is not a mirror - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "Unable to split datapool: Source pool must be composed only of mirrors" - ret['retcode'] = 1 + ret["stdout"] = "" + ret[ + "stderr" + ] = "Unable to split datapool: Source pool must be composed only of mirrors" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.split('datapool', 'backuppool') - res = OrderedDict([('split', False), ('error', 'Unable to split datapool: Source pool must be composed only of mirrors')]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.split("datapool", "backuppool") + res = OrderedDict( + [ + ("split", False), + ( + "error", + "Unable to split datapool: Source pool must be composed only of mirrors", + ), + ] + ) self.assertEqual(ret, res) def test_labelclear_success(self): - ''' + """ Tests labelclear on succesful label removal - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.labelclear('/dev/rdsk/c0t0d0', force=False) - res = OrderedDict([('labelcleared', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.labelclear("/dev/rdsk/c0t0d0", force=False) + res = OrderedDict([("labelcleared", True)]) self.assertEqual(ret, res) def test_labelclear_nodevice(self): - ''' + """ Tests labelclear on non existing device - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "failed to open /dev/rdsk/c0t0d0: No such file or directory" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "failed to open /dev/rdsk/c0t0d0: No such file or directory" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.labelclear('/dev/rdsk/c0t0d0', force=False) - res = OrderedDict([ - ('labelcleared', False), - ('error', 'failed to open /dev/rdsk/c0t0d0: No such file or directory'), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.labelclear("/dev/rdsk/c0t0d0", force=False) + res = OrderedDict( + [ + ("labelcleared", False), + ( + "error", + "failed to open /dev/rdsk/c0t0d0: No such file or directory", + ), + ] + ) self.assertEqual(ret, res) def test_labelclear_cleared(self): - ''' + """ Tests labelclear on device with no label - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "failed to read label from /dev/rdsk/c0t0d0" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "failed to read label from /dev/rdsk/c0t0d0" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.labelclear('/dev/rdsk/c0t0d0', force=False) - res = OrderedDict([ - ('labelcleared', False), - ('error', 'failed to read label from /dev/rdsk/c0t0d0'), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.labelclear("/dev/rdsk/c0t0d0", force=False) + res = OrderedDict( + [ + ("labelcleared", False), + ("error", "failed to read label from /dev/rdsk/c0t0d0"), + ] + ) self.assertEqual(ret, res) def test_labelclear_exported(self): - ''' + """ Tests labelclear on device with from exported pool - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "\n".join([ - "use '-f' to override the following error:", - '/dev/rdsk/c0t0d0 is a member of exported pool "mypool"', - ]) - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "\n".join( + [ + "use '-f' to override the following error:", + '/dev/rdsk/c0t0d0 is a member of exported pool "mypool"', + ] + ) + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.labelclear('/dev/rdsk/c0t0d0', force=False) - res = OrderedDict([ - ('labelcleared', False), - ('error', 'use \'force=True\' to override the following error:\n/dev/rdsk/c0t0d0 is a member of exported pool "mypool"'), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.labelclear("/dev/rdsk/c0t0d0", force=False) + res = OrderedDict( + [ + ("labelcleared", False), + ( + "error", + "use 'force=True' to override the following error:\n/dev/rdsk/c0t0d0 is a member of exported pool \"mypool\"", + ), + ] + ) self.assertEqual(ret, res) - @skipIf(not salt.utils.path.which('mkfile'), 'Cannot find mkfile executable') + @skipIf(not salt.utils.path.which("mkfile"), "Cannot find mkfile executable") def test_create_file_vdev_success(self): - ''' + """ Tests create_file_vdev when out of space - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.create_file_vdev('64M', '/vdisks/disk0') - res = OrderedDict([ - ('/vdisks/disk0', 'created'), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.create_file_vdev("64M", "/vdisks/disk0") + res = OrderedDict([("/vdisks/disk0", "created")]) self.assertEqual(ret, res) - @skipIf(not salt.utils.path.which('mkfile'), 'Cannot find mkfile executable') + @skipIf(not salt.utils.path.which("mkfile"), "Cannot find mkfile executable") def test_create_file_vdev_nospace(self): - ''' + """ Tests create_file_vdev when out of space - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "/vdisks/disk0: initialized 10424320 of 67108864 bytes: No space left on device" - ret['retcode'] = 1 + ret["stdout"] = "" + ret[ + "stderr" + ] = "/vdisks/disk0: initialized 10424320 of 67108864 bytes: No space left on device" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.create_file_vdev('64M', '/vdisks/disk0') - res = OrderedDict([ - ('/vdisks/disk0', 'failed'), - ('error', OrderedDict([ - ('/vdisks/disk0', ' initialized 10424320 of 67108864 bytes: No space left on device'), - ])), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.create_file_vdev("64M", "/vdisks/disk0") + res = OrderedDict( + [ + ("/vdisks/disk0", "failed"), + ( + "error", + OrderedDict( + [ + ( + "/vdisks/disk0", + " initialized 10424320 of 67108864 bytes: No space left on device", + ), + ] + ), + ), + ] + ) self.assertEqual(ret, res) def test_export_success(self): - ''' + """ Tests export - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.export('mypool') - res = OrderedDict([('exported', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.export("mypool") + res = OrderedDict([("exported", True)]) self.assertEqual(ret, res) def test_export_nopool(self): - ''' + """ Tests export when the pool does not exists - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'mypool': no such pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'mypool': no such pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.export('mypool') - res = OrderedDict([('exported', False), ('error', "cannot open 'mypool': no such pool")]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.export("mypool") + res = OrderedDict( + [("exported", False), ("error", "cannot open 'mypool': no such pool")] + ) self.assertEqual(ret, res) def test_import_success(self): - ''' + """ Tests import - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.import_('mypool') - res = OrderedDict([('imported', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.import_("mypool") + res = OrderedDict([("imported", True)]) self.assertEqual(ret, res) def test_import_duplicate(self): - ''' + """ Tests import with already imported pool - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "\n".join([ - "cannot import 'mypool': a pool with that name already exists", - "use the form 'zpool import <pool | id> <newpool>' to give it a new name", - ]) - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "\n".join( + [ + "cannot import 'mypool': a pool with that name already exists", + "use the form 'zpool import <pool | id> <newpool>' to give it a new name", + ] + ) + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.import_('mypool') - res = OrderedDict([ - ('imported', False), - ('error', "cannot import 'mypool': a pool with that name already exists\nuse the form 'zpool import <pool | id> <newpool>' to give it a new name"), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.import_("mypool") + res = OrderedDict( + [ + ("imported", False), + ( + "error", + "cannot import 'mypool': a pool with that name already exists\nuse the form 'zpool import <pool | id> <newpool>' to give it a new name", + ), + ] + ) self.assertEqual(ret, res) def test_import_nopool(self): - ''' + """ Tests import - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot import 'mypool': no such pool available" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot import 'mypool': no such pool available" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.import_('mypool') - res = OrderedDict([ - ('imported', False), - ('error', "cannot import 'mypool': no such pool available"), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.import_("mypool") + res = OrderedDict( + [ + ("imported", False), + ("error", "cannot import 'mypool': no such pool available"), + ] + ) self.assertEqual(ret, res) def test_online_success(self): - ''' + """ Tests online - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.online('mypool', '/dev/rdsk/c0t0d0') - res = OrderedDict([('onlined', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.online("mypool", "/dev/rdsk/c0t0d0") + res = OrderedDict([("onlined", True)]) self.assertEqual(ret, res) def test_online_nodevice(self): - ''' + """ Tests online - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot online /dev/rdsk/c0t0d1: no such device in pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot online /dev/rdsk/c0t0d1: no such device in pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.online('mypool', '/dev/rdsk/c0t0d1') - res = OrderedDict([ - ('onlined', False), - ('error', 'cannot online /dev/rdsk/c0t0d1: no such device in pool'), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.online("mypool", "/dev/rdsk/c0t0d1") + res = OrderedDict( + [ + ("onlined", False), + ("error", "cannot online /dev/rdsk/c0t0d1: no such device in pool"), + ] + ) self.assertEqual(ret, res) def test_offline_success(self): - ''' + """ Tests offline - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.offline('mypool', '/dev/rdsk/c0t0d0') - res = OrderedDict([('offlined', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.offline("mypool", "/dev/rdsk/c0t0d0") + res = OrderedDict([("offlined", True)]) self.assertEqual(ret, res) def test_offline_nodevice(self): - ''' + """ Tests offline - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot offline /dev/rdsk/c0t0d1: no such device in pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot offline /dev/rdsk/c0t0d1: no such device in pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.offline('mypool', '/dev/rdsk/c0t0d1') - res = OrderedDict([ - ('offlined', False), - ('error', 'cannot offline /dev/rdsk/c0t0d1: no such device in pool'), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.offline("mypool", "/dev/rdsk/c0t0d1") + res = OrderedDict( + [ + ("offlined", False), + ( + "error", + "cannot offline /dev/rdsk/c0t0d1: no such device in pool", + ), + ] + ) self.assertEqual(ret, res) def test_offline_noreplica(self): - ''' + """ Tests offline - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot offline /dev/rdsk/c0t0d1: no valid replicas" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot offline /dev/rdsk/c0t0d1: no valid replicas" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.offline('mypool', '/dev/rdsk/c0t0d1') - res = OrderedDict([ - ('offlined', False), - ('error', 'cannot offline /dev/rdsk/c0t0d1: no valid replicas'), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.offline("mypool", "/dev/rdsk/c0t0d1") + res = OrderedDict( + [ + ("offlined", False), + ("error", "cannot offline /dev/rdsk/c0t0d1: no valid replicas"), + ] + ) self.assertEqual(ret, res) def test_reguid_success(self): - ''' + """ Tests reguid - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.reguid('mypool') - res = OrderedDict([('reguided', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.reguid("mypool") + res = OrderedDict([("reguided", True)]) self.assertEqual(ret, res) def test_reguid_nopool(self): - ''' + """ Tests reguid with missing pool - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'mypool': no such pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'mypool': no such pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.reguid('mypool') - res = OrderedDict([ - ('reguided', False), - ('error', "cannot open 'mypool': no such pool"), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.reguid("mypool") + res = OrderedDict( + [("reguided", False), ("error", "cannot open 'mypool': no such pool")] + ) self.assertEqual(ret, res) def test_reopen_success(self): - ''' + """ Tests reopen - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.reopen('mypool') - res = OrderedDict([('reopened', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.reopen("mypool") + res = OrderedDict([("reopened", True)]) self.assertEqual(ret, res) def test_reopen_nopool(self): - ''' + """ Tests reopen with missing pool - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'mypool': no such pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'mypool': no such pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.reopen('mypool') - res = OrderedDict([ - ('reopened', False), - ('error', "cannot open 'mypool': no such pool"), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.reopen("mypool") + res = OrderedDict( + [("reopened", False), ("error", "cannot open 'mypool': no such pool")] + ) self.assertEqual(ret, res) def test_upgrade_success(self): - ''' + """ Tests upgrade - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.upgrade('mypool') - res = OrderedDict([('upgraded', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.upgrade("mypool") + res = OrderedDict([("upgraded", True)]) self.assertEqual(ret, res) def test_upgrade_nopool(self): - ''' + """ Tests upgrade with missing pool - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'mypool': no such pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'mypool': no such pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.upgrade('mypool') - res = OrderedDict([ - ('upgraded', False), - ('error', "cannot open 'mypool': no such pool"), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.upgrade("mypool") + res = OrderedDict( + [("upgraded", False), ("error", "cannot open 'mypool': no such pool")] + ) self.assertEqual(ret, res) def test_history_success(self): - ''' + """ Tests history - ''' + """ ret = {} - ret['stdout'] = "\n".join([ - "History for 'mypool':", - "2018-01-18.16:56:12 zpool create -f mypool /dev/rdsk/c0t0d0", - "2018-01-19.16:01:55 zpool attach -f mypool /dev/rdsk/c0t0d0 /dev/rdsk/c0t0d1", - ]) - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "\n".join( + [ + "History for 'mypool':", + "2018-01-18.16:56:12 zpool create -f mypool /dev/rdsk/c0t0d0", + "2018-01-19.16:01:55 zpool attach -f mypool /dev/rdsk/c0t0d0 /dev/rdsk/c0t0d1", + ] + ) + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.history('mypool') - res = OrderedDict([ - ('mypool', OrderedDict([ - ('2018-01-18.16:56:12', 'zpool create -f mypool /dev/rdsk/c0t0d0'), - ('2018-01-19.16:01:55', 'zpool attach -f mypool /dev/rdsk/c0t0d0 /dev/rdsk/c0t0d1'), - ])), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.history("mypool") + res = OrderedDict( + [ + ( + "mypool", + OrderedDict( + [ + ( + "2018-01-18.16:56:12", + "zpool create -f mypool /dev/rdsk/c0t0d0", + ), + ( + "2018-01-19.16:01:55", + "zpool attach -f mypool /dev/rdsk/c0t0d0 /dev/rdsk/c0t0d1", + ), + ] + ), + ), + ] + ) self.assertEqual(ret, res) def test_history_nopool(self): - ''' + """ Tests history with missing pool - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'mypool': no such pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'mypool': no such pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.history('mypool') - res = OrderedDict([ - ('error', "cannot open 'mypool': no such pool"), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.history("mypool") + res = OrderedDict([("error", "cannot open 'mypool': no such pool")]) self.assertEqual(ret, res) def test_clear_success(self): - ''' + """ Tests clear - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "" - ret['retcode'] = 0 + ret["stdout"] = "" + ret["stderr"] = "" + ret["retcode"] = 0 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.clear('mypool') - res = OrderedDict([('cleared', True)]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.clear("mypool") + res = OrderedDict([("cleared", True)]) self.assertEqual(ret, res) def test_clear_nopool(self): - ''' + """ Tests clear with missing pool - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot open 'mypool': no such pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret["stderr"] = "cannot open 'mypool': no such pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.clear('mypool') - res = OrderedDict([ - ('cleared', False), - ('error', "cannot open 'mypool': no such pool"), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.clear("mypool") + res = OrderedDict( + [("cleared", False), ("error", "cannot open 'mypool': no such pool")] + ) def test_clear_nodevice(self): - ''' + """ Tests clear with non existign device - ''' + """ ret = {} - ret['stdout'] = "" - ret['stderr'] = "cannot clear errors for /dev/rdsk/c0t0d0: no such device in pool" - ret['retcode'] = 1 + ret["stdout"] = "" + ret[ + "stderr" + ] = "cannot clear errors for /dev/rdsk/c0t0d0: no such device in pool" + ret["retcode"] = 1 mock_cmd = MagicMock(return_value=ret) - with patch.dict(zpool.__salt__, {'cmd.run_all': mock_cmd}), \ - patch.dict(zpool.__utils__, self.utils_patch): - ret = zpool.clear('mypool', '/dev/rdsk/c0t0d0') - res = OrderedDict([ - ('cleared', False), - ('error', "cannot clear errors for /dev/rdsk/c0t0d0: no such device in pool"), - ]) + with patch.dict(zpool.__salt__, {"cmd.run_all": mock_cmd}), patch.dict( + zpool.__utils__, self.utils_patch + ): + ret = zpool.clear("mypool", "/dev/rdsk/c0t0d0") + res = OrderedDict( + [ + ("cleared", False), + ( + "error", + "cannot clear errors for /dev/rdsk/c0t0d0: no such device in pool", + ), + ] + ) self.assertEqual(ret, res) diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py index 78414ca4ac9..532bce9057c 100644 --- a/tests/unit/modules/test_zypperpkg.py +++ b/tests/unit/modules/test_zypperpkg.py @@ -1,33 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Bo Maryniuk <bo@suse.de> -''' +""" # Import Python Libs from __future__ import absolute_import + import os from xml.dom import minidom -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - call, - patch, -) +import salt.modules.pkg_resource as pkg_resource +import salt.modules.zypperpkg as zypper # Import Salt libs import salt.utils.files -import salt.modules.zypperpkg as zypper -import salt.modules.pkg_resource as pkg_resource +import salt.utils.pkg from salt.exceptions import CommandExecutionError +from salt.ext import six # Import 3rd-party libs from salt.ext.six.moves import configparser -from salt.ext import six -import salt.utils.pkg + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, call, patch +from tests.support.unit import TestCase class ZyppCallMock(object): @@ -42,49 +39,49 @@ class ZyppCallMock(object): def get_test_data(filename): - ''' + """ Return static test data - ''' + """ with salt.utils.files.fopen( - os.path.join( - os.path.join( - os.path.dirname(os.path.abspath(__file__)), 'zypp'), filename)) as rfh: + os.path.join( + os.path.join(os.path.dirname(os.path.abspath(__file__)), "zypp"), filename + ) + ) as rfh: return rfh.read() class ZypperTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.zypper - ''' + """ def setup_loader_modules(self): - return {zypper: {'rpm': None}, pkg_resource: {}} + return {zypper: {"rpm": None}, pkg_resource: {}} def setUp(self): self.new_repo_config = dict( - name='mock-repo-name', - url='http://repo.url/some/path' + name="mock-repo-name", url="http://repo.url/some/path" ) side_effect = [ - Mock(**{'sections.return_value': []}), - Mock(**{'sections.return_value': [self.new_repo_config['name']]}) + Mock(**{"sections.return_value": []}), + Mock(**{"sections.return_value": [self.new_repo_config["name"]]}), ] self.zypper_patcher_config = { - '_get_configured_repos': Mock(side_effect=side_effect), - '__zypper__': Mock(), - '_get_repo_info': Mock( + "_get_configured_repos": Mock(side_effect=side_effect), + "__zypper__": Mock(), + "_get_repo_info": Mock( return_value={ - 'keeppackages': False, - 'autorefresh': True, - 'enabled': False, - 'baseurl': self.new_repo_config['url'], - 'alias': self.new_repo_config['name'], - 'priority': 1, - 'type': 'rpm-md' + "keeppackages": False, + "autorefresh": True, + "enabled": False, + "baseurl": self.new_repo_config["url"], + "alias": self.new_repo_config["name"], + "priority": 1, + "type": "rpm-md", } ), - 'del_repo': Mock(), - 'mod_repo': Mock(wraps=zypper.mod_repo) + "del_repo": Mock(), + "mod_repo": Mock(wraps=zypper.mod_repo), } def tearDown(self): @@ -92,163 +89,251 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): del self.zypper_patcher_config def test_list_upgrades(self): - ''' + """ List package upgrades :return: - ''' + """ ref_out = { - 'stdout': get_test_data('zypper-updates.xml'), - 'stderr': None, - 'retcode': 0 + "stdout": get_test_data("zypper-updates.xml"), + "stderr": None, + "retcode": 0, } - with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}): + with patch.dict( + zypper.__salt__, {"cmd.run_all": MagicMock(return_value=ref_out)} + ): upgrades = zypper.list_upgrades(refresh=False) self.assertEqual(len(upgrades), 3) - for pkg, version in {'SUSEConnect': '0.2.33-7.1', - 'bind-utils': '9.9.6P1-35.1', - 'bind-libs': '9.9.6P1-35.1'}.items(): + for pkg, version in { + "SUSEConnect": "0.2.33-7.1", + "bind-utils": "9.9.6P1-35.1", + "bind-libs": "9.9.6P1-35.1", + }.items(): self.assertIn(pkg, upgrades) self.assertEqual(upgrades[pkg], version) - @patch('salt.utils.environment.get_module_environment', MagicMock(return_value={'SALT_RUNNING': "1"})) + @patch( + "salt.utils.environment.get_module_environment", + MagicMock(return_value={"SALT_RUNNING": "1"}), + ) def test_zypper_caller(self): - ''' + """ Test Zypper caller. :return: - ''' + """ + class RunSniffer(object): def __init__(self, stdout=None, stderr=None, retcode=None): self.calls = list() - self._stdout = stdout or '' - self._stderr = stderr or '' + self._stdout = stdout or "" + self._stderr = stderr or "" self._retcode = retcode or 0 def __call__(self, *args, **kwargs): - self.calls.append({'args': args, 'kwargs': kwargs}) - return {'stdout': self._stdout, - 'stderr': self._stderr, - 'retcode': self._retcode} + self.calls.append({"args": args, "kwargs": kwargs}) + return { + "stdout": self._stdout, + "stderr": self._stderr, + "retcode": self._retcode, + } stdout_xml_snippet = '<?xml version="1.0"?><test foo="bar"/>' sniffer = RunSniffer(stdout=stdout_xml_snippet) - with patch.dict('salt.modules.zypperpkg.__salt__', {'cmd.run_all': sniffer}): - self.assertEqual(zypper.__zypper__.call('foo'), stdout_xml_snippet) + with patch.dict("salt.modules.zypperpkg.__salt__", {"cmd.run_all": sniffer}): + self.assertEqual(zypper.__zypper__.call("foo"), stdout_xml_snippet) self.assertEqual(len(sniffer.calls), 1) - zypper.__zypper__.call('bar') + zypper.__zypper__.call("bar") self.assertEqual(len(sniffer.calls), 2) - self.assertEqual(sniffer.calls[0]['args'][0], ['zypper', '--non-interactive', '--no-refresh', 'foo']) - self.assertEqual(sniffer.calls[1]['args'][0], ['zypper', '--non-interactive', '--no-refresh', 'bar']) + self.assertEqual( + sniffer.calls[0]["args"][0], + ["zypper", "--non-interactive", "--no-refresh", "foo"], + ) + self.assertEqual( + sniffer.calls[1]["args"][0], + ["zypper", "--non-interactive", "--no-refresh", "bar"], + ) - dom = zypper.__zypper__.xml.call('xml-test') - self.assertEqual(sniffer.calls[2]['args'][0], ['zypper', '--non-interactive', '--xmlout', - '--no-refresh', 'xml-test']) - self.assertEqual(dom.getElementsByTagName('test')[0].getAttribute('foo'), 'bar') + dom = zypper.__zypper__.xml.call("xml-test") + self.assertEqual( + sniffer.calls[2]["args"][0], + ["zypper", "--non-interactive", "--xmlout", "--no-refresh", "xml-test"], + ) + self.assertEqual( + dom.getElementsByTagName("test")[0].getAttribute("foo"), "bar" + ) - zypper.__zypper__.refreshable.call('refresh-test') - self.assertEqual(sniffer.calls[3]['args'][0], ['zypper', '--non-interactive', 'refresh-test']) + zypper.__zypper__.refreshable.call("refresh-test") + self.assertEqual( + sniffer.calls[3]["args"][0], + ["zypper", "--non-interactive", "refresh-test"], + ) - zypper.__zypper__.nolock.call('no-locking-test') - self.assertEqual(sniffer.calls[4].get('kwargs', {}).get('env', {}).get('ZYPP_READONLY_HACK'), "1") - self.assertEqual(sniffer.calls[4].get('kwargs', {}).get('env', {}).get('SALT_RUNNING'), "1") + zypper.__zypper__.nolock.call("no-locking-test") + self.assertEqual( + sniffer.calls[4] + .get("kwargs", {}) + .get("env", {}) + .get("ZYPP_READONLY_HACK"), + "1", + ) + self.assertEqual( + sniffer.calls[4].get("kwargs", {}).get("env", {}).get("SALT_RUNNING"), + "1", + ) - zypper.__zypper__.call('locking-test') - self.assertEqual(sniffer.calls[5].get('kwargs', {}).get('env', {}).get('ZYPP_READONLY_HACK'), None) - self.assertEqual(sniffer.calls[5].get('kwargs', {}).get('env', {}).get('SALT_RUNNING'), "1") + zypper.__zypper__.call("locking-test") + self.assertEqual( + sniffer.calls[5] + .get("kwargs", {}) + .get("env", {}) + .get("ZYPP_READONLY_HACK"), + None, + ) + self.assertEqual( + sniffer.calls[5].get("kwargs", {}).get("env", {}).get("SALT_RUNNING"), + "1", + ) # Test exceptions stdout_xml_snippet = '<?xml version="1.0"?><stream><message type="error">Booya!</message></stream>' sniffer = RunSniffer(stdout=stdout_xml_snippet, retcode=1) - with patch.dict('salt.modules.zypperpkg.__salt__', {'cmd.run_all': sniffer}): - with self.assertRaisesRegex(CommandExecutionError, '^Zypper command failure: Booya!$'): - zypper.__zypper__.xml.call('crashme') + with patch.dict("salt.modules.zypperpkg.__salt__", {"cmd.run_all": sniffer}): + with self.assertRaisesRegex( + CommandExecutionError, "^Zypper command failure: Booya!$" + ): + zypper.__zypper__.xml.call("crashme") - with self.assertRaisesRegex(CommandExecutionError, "^Zypper command failure: Check Zypper's logs.$"): - zypper.__zypper__.call('crashme again') + with self.assertRaisesRegex( + CommandExecutionError, "^Zypper command failure: Check Zypper's logs.$" + ): + zypper.__zypper__.call("crashme again") - zypper.__zypper__.noraise.call('stay quiet') + zypper.__zypper__.noraise.call("stay quiet") self.assertEqual(zypper.__zypper__.error_msg, "Check Zypper's logs.") def test_list_upgrades_error_handling(self): - ''' + """ Test error handling in the list package upgrades. :return: - ''' + """ # Test handled errors ref_out = { - 'stdout': '''<?xml version='1.0'?> + "stdout": """<?xml version='1.0'?> <stream> <message type="info">Refreshing service &apos;container-suseconnect&apos;.</message> <message type="error">Some handled zypper internal error</message> <message type="error">Another zypper internal error</message> </stream> - ''', - 'stderr': '', - 'retcode': 1, + """, + "stderr": "", + "retcode": 1, } - with patch.dict('salt.modules.zypperpkg.__salt__', {'cmd.run_all': MagicMock(return_value=ref_out)}): - with self.assertRaisesRegex(CommandExecutionError, - "^Zypper command failure: Some handled zypper internal error{0}Another zypper internal error$".format(os.linesep)): + with patch.dict( + "salt.modules.zypperpkg.__salt__", + {"cmd.run_all": MagicMock(return_value=ref_out)}, + ): + with self.assertRaisesRegex( + CommandExecutionError, + "^Zypper command failure: Some handled zypper internal error{0}Another zypper internal error$".format( + os.linesep + ), + ): zypper.list_upgrades(refresh=False) # Test unhandled error - ref_out = { - 'retcode': 1, - 'stdout': '', - 'stderr': '' - } - with patch.dict('salt.modules.zypperpkg.__salt__', {'cmd.run_all': MagicMock(return_value=ref_out)}): - with self.assertRaisesRegex(CommandExecutionError, "^Zypper command failure: Check Zypper's logs.$"): + ref_out = {"retcode": 1, "stdout": "", "stderr": ""} + with patch.dict( + "salt.modules.zypperpkg.__salt__", + {"cmd.run_all": MagicMock(return_value=ref_out)}, + ): + with self.assertRaisesRegex( + CommandExecutionError, "^Zypper command failure: Check Zypper's logs.$" + ): zypper.list_upgrades(refresh=False) def test_list_products(self): - ''' + """ List products test. - ''' + """ for filename, test_data in { - 'zypper-products-sle12sp1.xml': { - 'name': ['SLES', 'SLES', 'SUSE-Manager-Proxy', - 'SUSE-Manager-Server', 'sle-manager-tools-beta', - 'sle-manager-tools-beta-broken-eol', 'sle-manager-tools-beta-no-eol'], - 'vendor': 'SUSE LLC <https://www.suse.com/>', - 'release': ['0', '0', '0', '0', '0', '0', '0'], - 'productline': [None, None, None, None, None, None, 'sles'], - 'eol_t': [None, 0, 1509408000, 1522454400, 1522454400, 1730332800, 1730332800], - 'isbase': [False, False, False, False, False, False, True], - 'installed': [False, False, False, False, False, False, True], - 'registerrelease': [None, None, None, None, None, None, '123'], + "zypper-products-sle12sp1.xml": { + "name": [ + "SLES", + "SLES", + "SUSE-Manager-Proxy", + "SUSE-Manager-Server", + "sle-manager-tools-beta", + "sle-manager-tools-beta-broken-eol", + "sle-manager-tools-beta-no-eol", + ], + "vendor": "SUSE LLC <https://www.suse.com/>", + "release": ["0", "0", "0", "0", "0", "0", "0"], + "productline": [None, None, None, None, None, None, "sles"], + "eol_t": [ + None, + 0, + 1509408000, + 1522454400, + 1522454400, + 1730332800, + 1730332800, + ], + "isbase": [False, False, False, False, False, False, True], + "installed": [False, False, False, False, False, False, True], + "registerrelease": [None, None, None, None, None, None, "123"], }, - 'zypper-products-sle11sp3.xml': { - 'name': ['SUSE-Manager-Server', 'SUSE-Manager-Server', 'SUSE-Manager-Server-Broken-EOL', - 'SUSE_SLES', 'SUSE_SLES', 'SUSE_SLES', 'SUSE_SLES-SP4-migration'], - 'vendor': 'SUSE LINUX Products GmbH, Nuernberg, Germany', - 'release': ['1.138', '1.2', '1.2', '1.2', '1.201', '1.201', '1.4'], - 'productline': [None, None, None, None, None, 'manager', 'manager'], - 'eol_t': [None, 0, 0, 0, 0, 0, 0], - 'isbase': [False, False, False, False, False, True, True], - 'installed': [False, False, False, False, False, True, True], - 'registerrelease': [None, None, None, None, None, None, "42"], - }}.items(): + "zypper-products-sle11sp3.xml": { + "name": [ + "SUSE-Manager-Server", + "SUSE-Manager-Server", + "SUSE-Manager-Server-Broken-EOL", + "SUSE_SLES", + "SUSE_SLES", + "SUSE_SLES", + "SUSE_SLES-SP4-migration", + ], + "vendor": "SUSE LINUX Products GmbH, Nuernberg, Germany", + "release": ["1.138", "1.2", "1.2", "1.2", "1.201", "1.201", "1.4"], + "productline": [None, None, None, None, None, "manager", "manager"], + "eol_t": [None, 0, 0, 0, 0, 0, 0], + "isbase": [False, False, False, False, False, True, True], + "installed": [False, False, False, False, False, True, True], + "registerrelease": [None, None, None, None, None, None, "42"], + }, + }.items(): - ref_out = { - 'retcode': 0, - 'stdout': get_test_data(filename) - } + ref_out = {"retcode": 0, "stdout": get_test_data(filename)} - with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}): + with patch.dict( + zypper.__salt__, {"cmd.run_all": MagicMock(return_value=ref_out)} + ): products = zypper.list_products() self.assertEqual(len(products), 7) - self.assertIn(test_data['vendor'], [product['vendor'] for product in products]) - for kwd in ['name', 'isbase', 'installed', 'release', 'productline', 'eol_t', 'registerrelease']: + self.assertIn( + test_data["vendor"], [product["vendor"] for product in products] + ) + for kwd in [ + "name", + "isbase", + "installed", + "release", + "productline", + "eol_t", + "registerrelease", + ]: if six.PY3: - self.assertCountEqual(test_data[kwd], [prod.get(kwd) for prod in products]) + self.assertCountEqual( + test_data[kwd], [prod.get(kwd) for prod in products] + ) else: - self.assertEqual(test_data[kwd], sorted([prod.get(kwd) for prod in products])) + self.assertEqual( + test_data[kwd], sorted([prod.get(kwd) for prod in products]) + ) def test_refresh_db(self): - ''' + """ Test if refresh DB handled correctly - ''' + """ ref_out = [ "Repository 'openSUSE-Leap-42.1-LATEST' is up to date.", "Repository 'openSUSE-Leap-42.1-Update' is up to date.", @@ -256,249 +341,432 @@ class ZypperTestCase(TestCase, LoaderModuleMockMixin): "Forcing building of repository cache", "Building repository 'openSUSE-Leap-42.1-Update-Non-Oss' cache ..........[done]", "Building repository 'salt-dev' cache", - "All repositories have been refreshed." + "All repositories have been refreshed.", ] - run_out = { - 'stderr': '', 'stdout': '\n'.join(ref_out), 'retcode': 0 - } + run_out = {"stderr": "", "stdout": "\n".join(ref_out), "retcode": 0} - with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=run_out)}): - with patch.object(salt.utils.pkg, 'clear_rtag', Mock()): + with patch.dict( + zypper.__salt__, {"cmd.run_all": MagicMock(return_value=run_out)} + ): + with patch.object(salt.utils.pkg, "clear_rtag", Mock()): result = zypper.refresh_db() self.assertEqual(result.get("openSUSE-Leap-42.1-LATEST"), False) self.assertEqual(result.get("openSUSE-Leap-42.1-Update"), False) self.assertEqual(result.get("openSUSE-Leap-42.1-Update-Non-Oss"), True) def test_info_installed(self): - ''' + """ Test the return information of the named package(s), installed on the system. :return: - ''' + """ run_out = { - 'virgo-dummy': - {'build_date': '2015-07-09T10:55:19Z', - 'vendor': 'openSUSE Build Service', - 'description': 'This is the Virgo dummy package used for testing SUSE Manager', - 'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com', - 'build_date_time_t': 1436432119, 'relocations': '(not relocatable)', - 'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z', - 'install_date_time_t': 1456241517, 'summary': 'Virgo dummy package', 'version': '1.0', - 'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9', - 'release': '1.1', 'group': 'Applications/System', 'arch': 'noarch', 'size': '17992'}, - - 'libopenssl1_0_0': - {'build_date': '2015-11-04T23:20:34Z', 'vendor': 'SUSE LLC <https://www.suse.com/>', - 'description': 'The OpenSSL Project is a collaborative effort.', - 'license': 'OpenSSL', 'build_host': 'sheep11', 'url': 'https://www.openssl.org/', - 'build_date_time_t': 1446675634, 'relocations': '(not relocatable)', - 'source_rpm': 'openssl-1.0.1i-34.1.src.rpm', 'install_date': '2016-02-23T16:31:35Z', - 'install_date_time_t': 1456241495, 'summary': 'Secure Sockets and Transport Layer Security', - 'version': '1.0.1i', 'signature': 'RSA/SHA256, Wed Nov 4 22:21:34 2015, Key ID 70af9e8139db7c82', - 'release': '34.1', 'group': 'Productivity/Networking/Security', 'packager': 'https://www.suse.com/', - 'arch': 'x86_64', 'size': '2576912'}, + "virgo-dummy": { + "build_date": "2015-07-09T10:55:19Z", + "vendor": "openSUSE Build Service", + "description": "This is the Virgo dummy package used for testing SUSE Manager", + "license": "GPL-2.0", + "build_host": "sheep05", + "url": "http://www.suse.com", + "build_date_time_t": 1436432119, + "relocations": "(not relocatable)", + "source_rpm": "virgo-dummy-1.0-1.1.src.rpm", + "install_date": "2016-02-23T16:31:57Z", + "install_date_time_t": 1456241517, + "summary": "Virgo dummy package", + "version": "1.0", + "signature": "DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9", + "release": "1.1", + "group": "Applications/System", + "arch": "noarch", + "size": "17992", + }, + "libopenssl1_0_0": { + "build_date": "2015-11-04T23:20:34Z", + "vendor": "SUSE LLC <https://www.suse.com/>", + "description": "The OpenSSL Project is a collaborative effort.", + "license": "OpenSSL", + "build_host": "sheep11", + "url": "https://www.openssl.org/", + "build_date_time_t": 1446675634, + "relocations": "(not relocatable)", + "source_rpm": "openssl-1.0.1i-34.1.src.rpm", + "install_date": "2016-02-23T16:31:35Z", + "install_date_time_t": 1456241495, + "summary": "Secure Sockets and Transport Layer Security", + "version": "1.0.1i", + "signature": "RSA/SHA256, Wed Nov 4 22:21:34 2015, Key ID 70af9e8139db7c82", + "release": "34.1", + "group": "Productivity/Networking/Security", + "packager": "https://www.suse.com/", + "arch": "x86_64", + "size": "2576912", + }, } - with patch.dict(zypper.__salt__, {'lowpkg.info': MagicMock(return_value=run_out)}): + with patch.dict( + zypper.__salt__, {"lowpkg.info": MagicMock(return_value=run_out)} + ): installed = zypper.info_installed() # Test overall products length self.assertEqual(len(installed), 2) # Test translated fields for pkg_name, pkg_info in installed.items(): - self.assertEqual(installed[pkg_name].get('source'), run_out[pkg_name]['source_rpm']) + self.assertEqual( + installed[pkg_name].get("source"), run_out[pkg_name]["source_rpm"] + ) # Test keys transition from the lowpkg.info - for pn_key, pn_val in run_out['virgo-dummy'].items(): - if pn_key == 'source_rpm': + for pn_key, pn_val in run_out["virgo-dummy"].items(): + if pn_key == "source_rpm": continue - self.assertEqual(installed['virgo-dummy'][pn_key], pn_val) + self.assertEqual(installed["virgo-dummy"][pn_key], pn_val) def test_info_installed_with_non_ascii_char(self): - ''' + """ Test the return information of the named package(s), installed on the system whith non-ascii chars :return: - ''' - run_out = {'vīrgô': {'description': 'vīrgô d€šçripţiǫñ'}} - with patch.dict(zypper.__salt__, {'lowpkg.info': MagicMock(return_value=run_out)}): + """ + run_out = {"vīrgô": {"description": "vīrgô d€šçripţiǫñ"}} + with patch.dict( + zypper.__salt__, {"lowpkg.info": MagicMock(return_value=run_out)} + ): installed = zypper.info_installed() - self.assertEqual(installed['vīrgô']['description'], 'vīrgô d€šçripţiǫñ') + self.assertEqual(installed["vīrgô"]["description"], "vīrgô d€šçripţiǫñ") def test_info_installed_with_all_versions(self): - ''' + """ Test the return information of all versions for the named package(s), installed on the system. :return: - ''' + """ run_out = { - 'virgo-dummy': [ - {'build_date': '2015-07-09T10:55:19Z', - 'vendor': 'openSUSE Build Service', - 'description': 'This is the Virgo dummy package used for testing SUSE Manager', - 'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com', - 'build_date_time_t': 1436432119, 'relocations': '(not relocatable)', - 'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z', - 'install_date_time_t': 1456241517, 'summary': 'Virgo dummy package', 'version': '1.0', - 'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9', - 'release': '1.1', 'group': 'Applications/System', 'arch': 'i686', 'size': '17992'}, - {'build_date': '2015-07-09T10:15:19Z', - 'vendor': 'openSUSE Build Service', - 'description': 'This is the Virgo dummy package used for testing SUSE Manager', - 'license': 'GPL-2.0', 'build_host': 'sheep05', 'url': 'http://www.suse.com', - 'build_date_time_t': 1436432119, 'relocations': '(not relocatable)', - 'source_rpm': 'virgo-dummy-1.0-1.1.src.rpm', 'install_date': '2016-02-23T16:31:57Z', - 'install_date_time_t': 14562415127, 'summary': 'Virgo dummy package', 'version': '1.0', - 'signature': 'DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9', - 'release': '1.1', 'group': 'Applications/System', 'arch': 'x86_64', 'size': '13124'} + "virgo-dummy": [ + { + "build_date": "2015-07-09T10:55:19Z", + "vendor": "openSUSE Build Service", + "description": "This is the Virgo dummy package used for testing SUSE Manager", + "license": "GPL-2.0", + "build_host": "sheep05", + "url": "http://www.suse.com", + "build_date_time_t": 1436432119, + "relocations": "(not relocatable)", + "source_rpm": "virgo-dummy-1.0-1.1.src.rpm", + "install_date": "2016-02-23T16:31:57Z", + "install_date_time_t": 1456241517, + "summary": "Virgo dummy package", + "version": "1.0", + "signature": "DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9", + "release": "1.1", + "group": "Applications/System", + "arch": "i686", + "size": "17992", + }, + { + "build_date": "2015-07-09T10:15:19Z", + "vendor": "openSUSE Build Service", + "description": "This is the Virgo dummy package used for testing SUSE Manager", + "license": "GPL-2.0", + "build_host": "sheep05", + "url": "http://www.suse.com", + "build_date_time_t": 1436432119, + "relocations": "(not relocatable)", + "source_rpm": "virgo-dummy-1.0-1.1.src.rpm", + "install_date": "2016-02-23T16:31:57Z", + "install_date_time_t": 14562415127, + "summary": "Virgo dummy package", + "version": "1.0", + "signature": "DSA/SHA1, Thu Jul 9 08:55:33 2015, Key ID 27fa41bd8a7c64f9", + "release": "1.1", + "group": "Applications/System", + "arch": "x86_64", + "size": "13124", + }, + ], + "libopenssl1_0_0": [ + { + "build_date": "2015-11-04T23:20:34Z", + "vendor": "SUSE LLC <https://www.suse.com/>", + "description": "The OpenSSL Project is a collaborative effort.", + "license": "OpenSSL", + "build_host": "sheep11", + "url": "https://www.openssl.org/", + "build_date_time_t": 1446675634, + "relocations": "(not relocatable)", + "source_rpm": "openssl-1.0.1i-34.1.src.rpm", + "install_date": "2016-02-23T16:31:35Z", + "install_date_time_t": 1456241495, + "summary": "Secure Sockets and Transport Layer Security", + "version": "1.0.1i", + "signature": "RSA/SHA256, Wed Nov 4 22:21:34 2015, Key ID 70af9e8139db7c82", + "release": "34.1", + "group": "Productivity/Networking/Security", + "packager": "https://www.suse.com/", + "arch": "x86_64", + "size": "2576912", + } ], - 'libopenssl1_0_0': [ - {'build_date': '2015-11-04T23:20:34Z', 'vendor': 'SUSE LLC <https://www.suse.com/>', - 'description': 'The OpenSSL Project is a collaborative effort.', - 'license': 'OpenSSL', 'build_host': 'sheep11', 'url': 'https://www.openssl.org/', - 'build_date_time_t': 1446675634, 'relocations': '(not relocatable)', - 'source_rpm': 'openssl-1.0.1i-34.1.src.rpm', 'install_date': '2016-02-23T16:31:35Z', - 'install_date_time_t': 1456241495, 'summary': 'Secure Sockets and Transport Layer Security', - 'version': '1.0.1i', 'signature': 'RSA/SHA256, Wed Nov 4 22:21:34 2015, Key ID 70af9e8139db7c82', - 'release': '34.1', 'group': 'Productivity/Networking/Security', 'packager': 'https://www.suse.com/', - 'arch': 'x86_64', 'size': '2576912'} - ] } - with patch.dict(zypper.__salt__, {'lowpkg.info': MagicMock(return_value=run_out)}): + with patch.dict( + zypper.__salt__, {"lowpkg.info": MagicMock(return_value=run_out)} + ): installed = zypper.info_installed(all_versions=True) # Test overall products length self.assertEqual(len(installed), 2) # Test multiple versions for the same package for pkg_name, pkg_info_list in installed.items(): - self.assertEqual(len(pkg_info_list), 2 if pkg_name == "virgo-dummy" else 1) + self.assertEqual( + len(pkg_info_list), 2 if pkg_name == "virgo-dummy" else 1 + ) for info in pkg_info_list: - self.assertTrue(info['arch'] in ('x86_64', 'i686')) + self.assertTrue(info["arch"] in ("x86_64", "i686")) def test_info_available(self): - ''' + """ Test return the information of the named package available for the system. :return: - ''' - test_pkgs = ['vim', 'emacs', 'python'] - with patch('salt.modules.zypperpkg.__zypper__', ZyppCallMock(return_value=get_test_data('zypper-available.txt'))): + """ + test_pkgs = ["vim", "emacs", "python"] + with patch( + "salt.modules.zypperpkg.__zypper__", + ZyppCallMock(return_value=get_test_data("zypper-available.txt")), + ): available = zypper.info_available(*test_pkgs, refresh=False) self.assertEqual(len(available), 3) for pkg_name, pkg_info in available.items(): self.assertIn(pkg_name, test_pkgs) - self.assertEqual(available['emacs']['status'], 'up-to-date') - self.assertTrue(available['emacs']['installed']) - self.assertEqual(available['emacs']['support level'], 'Level 3') - self.assertEqual(available['emacs']['vendor'], 'SUSE LLC <https://www.suse.com/>') - self.assertEqual(available['emacs']['summary'], 'GNU Emacs Base Package') + self.assertEqual(available["emacs"]["status"], "up-to-date") + self.assertTrue(available["emacs"]["installed"]) + self.assertEqual(available["emacs"]["support level"], "Level 3") + self.assertEqual( + available["emacs"]["vendor"], "SUSE LLC <https://www.suse.com/>" + ) + self.assertEqual(available["emacs"]["summary"], "GNU Emacs Base Package") - self.assertEqual(available['vim']['status'], 'not installed') - self.assertFalse(available['vim']['installed']) - self.assertEqual(available['vim']['support level'], 'Level 3') - self.assertEqual(available['vim']['vendor'], 'SUSE LLC <https://www.suse.com/>') - self.assertEqual(available['vim']['summary'], 'Vi IMproved') + self.assertEqual(available["vim"]["status"], "not installed") + self.assertFalse(available["vim"]["installed"]) + self.assertEqual(available["vim"]["support level"], "Level 3") + self.assertEqual( + available["vim"]["vendor"], "SUSE LLC <https://www.suse.com/>" + ) + self.assertEqual(available["vim"]["summary"], "Vi IMproved") def test_latest_version(self): - ''' + """ Test the latest version of the named package available for upgrade or installation. :return: - ''' - with patch('salt.modules.zypperpkg.__zypper__', - ZyppCallMock(return_value=get_test_data('zypper-available.txt'))), \ - patch('salt.modules.zypperpkg.refresh_db', MagicMock(return_value=True)): - self.assertEqual(zypper.latest_version('vim'), '7.4.326-2.62') - self.assertDictEqual(zypper.latest_version('vim', 'fakepkg'), {'vim': '7.4.326-2.62', 'fakepkg': ''}) + """ + with patch( + "salt.modules.zypperpkg.__zypper__", + ZyppCallMock(return_value=get_test_data("zypper-available.txt")), + ), patch("salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True)): + self.assertEqual(zypper.latest_version("vim"), "7.4.326-2.62") + self.assertDictEqual( + zypper.latest_version("vim", "fakepkg"), + {"vim": "7.4.326-2.62", "fakepkg": ""}, + ) def test_upgrade_success(self): - ''' + """ Test system upgrade and dist-upgrade success. :return: - ''' - with patch.dict(zypper.__grains__, {'osrelease_info': [12, 1]}), \ - patch('salt.modules.zypperpkg.refresh_db', MagicMock(return_value=True)), \ - patch('salt.modules.zypperpkg._systemd_scope', MagicMock(return_value=False)): - with patch('salt.modules.zypperpkg.__zypper__.noraise.call', MagicMock()) as zypper_mock: - with patch('salt.modules.zypperpkg.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}])): + """ + with patch.dict(zypper.__grains__, {"osrelease_info": [12, 1]}), patch( + "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) + ), patch( + "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) + ): + with patch( + "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() + ) as zypper_mock: + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}]), + ): ret = zypper.upgrade() self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": "1.2"}}) - zypper_mock.assert_any_call('update', '--auto-agree-with-licenses') + zypper_mock.assert_any_call("update", "--auto-agree-with-licenses") - with patch('salt.modules.zypperpkg.list_pkgs', - MagicMock(side_effect=[{"kernel-default": "1.1"}, {"kernel-default": "1.1,1.2"}])): + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock( + side_effect=[ + {"kernel-default": "1.1"}, + {"kernel-default": "1.1,1.2"}, + ] + ), + ): ret = zypper.upgrade() - self.assertDictEqual(ret, {"kernel-default": {"old": "1.1", "new": "1.1,1.2"}}) - zypper_mock.assert_any_call('update', '--auto-agree-with-licenses') + self.assertDictEqual( + ret, {"kernel-default": {"old": "1.1", "new": "1.1,1.2"}} + ) + zypper_mock.assert_any_call("update", "--auto-agree-with-licenses") - with patch('salt.modules.zypperpkg.list_pkgs', - MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1,1.2"}])): + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1,1.2"}]), + ): ret = zypper.upgrade() self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": "1.1,1.2"}}) - zypper_mock.assert_any_call('update', '--auto-agree-with-licenses') + zypper_mock.assert_any_call("update", "--auto-agree-with-licenses") - with patch('salt.modules.zypperpkg.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}])): + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}]), + ): ret = zypper.upgrade(dist_upgrade=True) self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": "1.2"}}) - zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses') + zypper_mock.assert_any_call( + "dist-upgrade", "--auto-agree-with-licenses" + ) - with patch('salt.modules.zypperpkg.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])): + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), + ): ret = zypper.upgrade(dist_upgrade=True, dryrun=True) - zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run') - zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', - '--dry-run', '--debug-solver') + zypper_mock.assert_any_call( + "dist-upgrade", "--auto-agree-with-licenses", "--dry-run" + ) + zypper_mock.assert_any_call( + "dist-upgrade", + "--auto-agree-with-licenses", + "--dry-run", + "--debug-solver", + ) - with patch('salt.modules.zypperpkg.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])): - ret = zypper.upgrade(dist_upgrade=True, dryrun=True, - fromrepo=["Dummy", "Dummy2"], novendorchange=True) - zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run', - '--from', "Dummy", '--from', 'Dummy2', '--no-allow-vendor-change') - zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run', - '--from', "Dummy", '--from', 'Dummy2', '--no-allow-vendor-change', - '--debug-solver') + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), + ): + ret = zypper.upgrade( + dist_upgrade=True, + dryrun=True, + fromrepo=["Dummy", "Dummy2"], + novendorchange=True, + ) + zypper_mock.assert_any_call( + "dist-upgrade", + "--auto-agree-with-licenses", + "--dry-run", + "--from", + "Dummy", + "--from", + "Dummy2", + "--no-allow-vendor-change", + ) + zypper_mock.assert_any_call( + "dist-upgrade", + "--auto-agree-with-licenses", + "--dry-run", + "--from", + "Dummy", + "--from", + "Dummy2", + "--no-allow-vendor-change", + "--debug-solver", + ) - with patch('salt.modules.zypperpkg.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])): - ret = zypper.upgrade(dist_upgrade=False, fromrepo=["Dummy", "Dummy2"], dryrun=False) - zypper_mock.assert_any_call('update', '--auto-agree-with-licenses', '--repo', "Dummy", '--repo', 'Dummy2') + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), + ): + ret = zypper.upgrade( + dist_upgrade=False, fromrepo=["Dummy", "Dummy2"], dryrun=False + ) + zypper_mock.assert_any_call( + "update", + "--auto-agree-with-licenses", + "--repo", + "Dummy", + "--repo", + "Dummy2", + ) - with patch('salt.modules.zypperpkg.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}])): - ret = zypper.upgrade(dist_upgrade=True, fromrepo=["Dummy", "Dummy2"], novendorchange=True) + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}]), + ): + ret = zypper.upgrade( + dist_upgrade=True, + fromrepo=["Dummy", "Dummy2"], + novendorchange=True, + ) self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": "1.2"}}) - zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--from', "Dummy", - '--from', 'Dummy2', '--no-allow-vendor-change') + zypper_mock.assert_any_call( + "dist-upgrade", + "--auto-agree-with-licenses", + "--from", + "Dummy", + "--from", + "Dummy2", + "--no-allow-vendor-change", + ) def test_upgrade_kernel(self): - ''' + """ Test kernel package upgrade success. :return: - ''' - with patch.dict(zypper.__grains__, {'osrelease_info': [12, 1]}), \ - patch('salt.modules.zypperpkg.refresh_db', MagicMock(return_value=True)), \ - patch('salt.modules.zypperpkg._systemd_scope', MagicMock(return_value=False)): - with patch.dict(zypper.__salt__, {'pkg_resource.parse_targets': MagicMock(return_value=(['kernel-default'], - None))}): - with patch('salt.modules.zypperpkg.__zypper__.noraise.call', MagicMock()): - with patch('salt.modules.zypperpkg.list_pkgs', MagicMock(side_effect=[ - {"kernel-default": "3.12.49-11.1"}, {"kernel-default": "3.12.49-11.1,3.12.51-60.20.2"}])): - ret = zypper.install('kernel-default', '--auto-agree-with-licenses') - self.assertDictEqual(ret, {"kernel-default": {"old": "3.12.49-11.1", "new": "3.12.49-11.1,3.12.51-60.20.2"}}) + """ + with patch.dict(zypper.__grains__, {"osrelease_info": [12, 1]}), patch( + "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) + ), patch( + "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) + ): + with patch.dict( + zypper.__salt__, + { + "pkg_resource.parse_targets": MagicMock( + return_value=(["kernel-default"], None) + ) + }, + ): + with patch( + "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() + ): + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock( + side_effect=[ + {"kernel-default": "3.12.49-11.1"}, + {"kernel-default": "3.12.49-11.1,3.12.51-60.20.2"}, + ] + ), + ): + ret = zypper.install( + "kernel-default", "--auto-agree-with-licenses" + ) + self.assertDictEqual( + ret, + { + "kernel-default": { + "old": "3.12.49-11.1", + "new": "3.12.49-11.1,3.12.51-60.20.2", + } + }, + ) def test_upgrade_failure(self): - ''' + """ Test system upgrade failure. :return: - ''' - zypper_out = ''' + """ + zypper_out = """ Loading repository data... Reading installed packages... Computing distribution upgrade... Use 'zypper repos' to get the list of defined repositories. Repository 'DUMMY' not found by its alias, number, or URI. -''' +""" class FailingZypperDummy(object): def __init__(self): @@ -512,227 +780,276 @@ Repository 'DUMMY' not found by its alias, number, or URI. def __call__(self, *args, **kwargs): return self - with patch.dict(zypper.__grains__, {'osrelease_info': [12, 1]}), \ - patch('salt.modules.zypperpkg.__zypper__', FailingZypperDummy()) as zypper_mock, \ - patch('salt.modules.zypperpkg.refresh_db', MagicMock(return_value=True)), \ - patch('salt.modules.zypperpkg._systemd_scope', MagicMock(return_value=False)): + with patch.dict(zypper.__grains__, {"osrelease_info": [12, 1]}), patch( + "salt.modules.zypperpkg.__zypper__", FailingZypperDummy() + ) as zypper_mock, patch( + "salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True) + ), patch( + "salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False) + ): zypper_mock.noraise.call = MagicMock() - with patch('salt.modules.zypperpkg.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])): + with patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}]), + ): with self.assertRaises(CommandExecutionError) as cmd_exc: ret = zypper.upgrade(dist_upgrade=True, fromrepo=["DUMMY"]) - self.assertEqual(cmd_exc.exception.info['changes'], {}) - self.assertEqual(cmd_exc.exception.info['result']['stdout'], zypper_out) - zypper_mock.noraise.call.assert_called_with('dist-upgrade', '--auto-agree-with-licenses', - '--from', 'DUMMY') + self.assertEqual(cmd_exc.exception.info["changes"], {}) + self.assertEqual(cmd_exc.exception.info["result"]["stdout"], zypper_out) + zypper_mock.noraise.call.assert_called_with( + "dist-upgrade", "--auto-agree-with-licenses", "--from", "DUMMY" + ) def test_upgrade_available(self): - ''' + """ Test whether or not an upgrade is available for a given package. :return: - ''' - ref_out = get_test_data('zypper-available.txt') - with patch('salt.modules.zypperpkg.__zypper__', - ZyppCallMock(return_value=get_test_data('zypper-available.txt'))), \ - patch('salt.modules.zypperpkg.refresh_db', MagicMock(return_value=True)): - for pkg_name in ['emacs', 'python']: + """ + ref_out = get_test_data("zypper-available.txt") + with patch( + "salt.modules.zypperpkg.__zypper__", + ZyppCallMock(return_value=get_test_data("zypper-available.txt")), + ), patch("salt.modules.zypperpkg.refresh_db", MagicMock(return_value=True)): + for pkg_name in ["emacs", "python"]: self.assertFalse(zypper.upgrade_available(pkg_name)) - self.assertTrue(zypper.upgrade_available('vim')) + self.assertTrue(zypper.upgrade_available("vim")) def test_list_pkgs(self): - ''' + """ Test packages listing. :return: - ''' + """ + def _add_data(data, key, value): data.setdefault(key, []).append(value) rpm_out = [ - 'protobuf-java_|-(none)_|-2.6.1_|-3.1.develHead_|-noarch_|-(none)_|-1499257756', - 'yast2-ftp-server_|-(none)_|-3.1.8_|-8.1_|-x86_64_|-(none)_|-1499257798', - 'jose4j_|-(none)_|-0.4.4_|-2.1.develHead_|-noarch_|-(none)_|-1499257756', - 'apache-commons-cli_|-(none)_|-1.2_|-1.233_|-noarch_|-(none)_|-1498636510', - 'jakarta-commons-discovery_|-(none)_|-0.4_|-129.686_|-noarch_|-(none)_|-1498636511', - 'susemanager-build-keys-web_|-(none)_|-12.0_|-5.1.develHead_|-noarch_|-(none)_|-1498636510', - 'gpg-pubkey_|-(none)_|-39db7c82_|-5847eb1f_|-(none)_|-(none)_|-1519203802', - 'gpg-pubkey_|-(none)_|-8a7c64f9_|-5aaa93ca_|-(none)_|-(none)_|-1529925595', - 'kernel-default_|-(none)_|-4.4.138_|-94.39.1_|-x86_64_|-(none)_|-1529936067', - 'kernel-default_|-(none)_|-4.4.73_|-5.1_|-x86_64_|-(none)_|-1503572639', - 'perseus-dummy_|-(none)_|-1.1_|-1.1_|-i586_|-(none)_|-1529936062', + "protobuf-java_|-(none)_|-2.6.1_|-3.1.develHead_|-noarch_|-(none)_|-1499257756", + "yast2-ftp-server_|-(none)_|-3.1.8_|-8.1_|-x86_64_|-(none)_|-1499257798", + "jose4j_|-(none)_|-0.4.4_|-2.1.develHead_|-noarch_|-(none)_|-1499257756", + "apache-commons-cli_|-(none)_|-1.2_|-1.233_|-noarch_|-(none)_|-1498636510", + "jakarta-commons-discovery_|-(none)_|-0.4_|-129.686_|-noarch_|-(none)_|-1498636511", + "susemanager-build-keys-web_|-(none)_|-12.0_|-5.1.develHead_|-noarch_|-(none)_|-1498636510", + "gpg-pubkey_|-(none)_|-39db7c82_|-5847eb1f_|-(none)_|-(none)_|-1519203802", + "gpg-pubkey_|-(none)_|-8a7c64f9_|-5aaa93ca_|-(none)_|-(none)_|-1529925595", + "kernel-default_|-(none)_|-4.4.138_|-94.39.1_|-x86_64_|-(none)_|-1529936067", + "kernel-default_|-(none)_|-4.4.73_|-5.1_|-x86_64_|-(none)_|-1503572639", + "perseus-dummy_|-(none)_|-1.1_|-1.1_|-i586_|-(none)_|-1529936062", ] - with patch.dict(zypper.__grains__, {'osarch': 'x86_64'}), \ - patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(rpm_out))}), \ - patch.dict(zypper.__salt__, {'pkg_resource.add_pkg': _add_data}), \ - patch.dict(zypper.__salt__, {'pkg_resource.format_pkg_list': pkg_resource.format_pkg_list}), \ - patch.dict(zypper.__salt__, {'pkg_resource.stringify': MagicMock()}): + with patch.dict(zypper.__grains__, {"osarch": "x86_64"}), patch.dict( + zypper.__salt__, + {"cmd.run": MagicMock(return_value=os.linesep.join(rpm_out))}, + ), patch.dict(zypper.__salt__, {"pkg_resource.add_pkg": _add_data}), patch.dict( + zypper.__salt__, + {"pkg_resource.format_pkg_list": pkg_resource.format_pkg_list}, + ), patch.dict( + zypper.__salt__, {"pkg_resource.stringify": MagicMock()} + ): pkgs = zypper.list_pkgs(versions_as_list=True) - self.assertFalse(pkgs.get('gpg-pubkey', False)) + self.assertFalse(pkgs.get("gpg-pubkey", False)) for pkg_name, pkg_version in { - 'jakarta-commons-discovery': ['0.4-129.686'], - 'yast2-ftp-server': ['3.1.8-8.1'], - 'protobuf-java': ['2.6.1-3.1.develHead'], - 'susemanager-build-keys-web': ['12.0-5.1.develHead'], - 'apache-commons-cli': ['1.2-1.233'], - 'kernel-default': ['4.4.138-94.39.1', '4.4.73-5.1'], - 'perseus-dummy.i586': ['1.1-1.1'], - 'jose4j': ['0.4.4-2.1.develHead']}.items(): + "jakarta-commons-discovery": ["0.4-129.686"], + "yast2-ftp-server": ["3.1.8-8.1"], + "protobuf-java": ["2.6.1-3.1.develHead"], + "susemanager-build-keys-web": ["12.0-5.1.develHead"], + "apache-commons-cli": ["1.2-1.233"], + "kernel-default": ["4.4.138-94.39.1", "4.4.73-5.1"], + "perseus-dummy.i586": ["1.1-1.1"], + "jose4j": ["0.4.4-2.1.develHead"], + }.items(): self.assertTrue(pkgs.get(pkg_name)) self.assertEqual(pkgs[pkg_name], pkg_version) def test_list_pkgs_with_attr(self): - ''' + """ Test packages listing with the attr parameter :return: - ''' + """ + def _add_data(data, key, value): data.setdefault(key, []).append(value) rpm_out = [ - 'protobuf-java_|-(none)_|-2.6.1_|-3.1.develHead_|-noarch_|-(none)_|-1499257756', - 'yast2-ftp-server_|-(none)_|-3.1.8_|-8.1_|-x86_64_|-(none)_|-1499257798', - 'jose4j_|-(none)_|-0.4.4_|-2.1.develHead_|-noarch_|-(none)_|-1499257756', - 'apache-commons-cli_|-(none)_|-1.2_|-1.233_|-noarch_|-(none)_|-1498636510', - 'jakarta-commons-discovery_|-(none)_|-0.4_|-129.686_|-noarch_|-(none)_|-1498636511', - 'susemanager-build-keys-web_|-(none)_|-12.0_|-5.1.develHead_|-noarch_|-(none)_|-1498636510', - 'gpg-pubkey_|-(none)_|-39db7c82_|-5847eb1f_|-(none)_|-(none)_|-1519203802', - 'gpg-pubkey_|-(none)_|-8a7c64f9_|-5aaa93ca_|-(none)_|-(none)_|-1529925595', - 'kernel-default_|-(none)_|-4.4.138_|-94.39.1_|-x86_64_|-(none)_|-1529936067', - 'kernel-default_|-(none)_|-4.4.73_|-5.1_|-x86_64_|-(none)_|-1503572639', - 'perseus-dummy_|-(none)_|-1.1_|-1.1_|-i586_|-(none)_|-1529936062', + "protobuf-java_|-(none)_|-2.6.1_|-3.1.develHead_|-noarch_|-(none)_|-1499257756", + "yast2-ftp-server_|-(none)_|-3.1.8_|-8.1_|-x86_64_|-(none)_|-1499257798", + "jose4j_|-(none)_|-0.4.4_|-2.1.develHead_|-noarch_|-(none)_|-1499257756", + "apache-commons-cli_|-(none)_|-1.2_|-1.233_|-noarch_|-(none)_|-1498636510", + "jakarta-commons-discovery_|-(none)_|-0.4_|-129.686_|-noarch_|-(none)_|-1498636511", + "susemanager-build-keys-web_|-(none)_|-12.0_|-5.1.develHead_|-noarch_|-(none)_|-1498636510", + "gpg-pubkey_|-(none)_|-39db7c82_|-5847eb1f_|-(none)_|-(none)_|-1519203802", + "gpg-pubkey_|-(none)_|-8a7c64f9_|-5aaa93ca_|-(none)_|-(none)_|-1529925595", + "kernel-default_|-(none)_|-4.4.138_|-94.39.1_|-x86_64_|-(none)_|-1529936067", + "kernel-default_|-(none)_|-4.4.73_|-5.1_|-x86_64_|-(none)_|-1503572639", + "perseus-dummy_|-(none)_|-1.1_|-1.1_|-i586_|-(none)_|-1529936062", ] - with patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(rpm_out))}), \ - patch.dict(zypper.__grains__, {'osarch': 'x86_64'}), \ - patch.dict(zypper.__salt__, {'pkg_resource.add_pkg': _add_data}), \ - patch.dict(zypper.__salt__, {'pkg_resource.format_pkg_list': pkg_resource.format_pkg_list}), \ - patch.dict(zypper.__salt__, {'pkg_resource.stringify': MagicMock()}), \ - patch.dict(pkg_resource.__salt__, {'pkg.parse_arch': zypper.parse_arch}): - pkgs = zypper.list_pkgs(attr=['epoch', 'release', 'arch', 'install_date_time_t']) - self.assertFalse(pkgs.get('gpg-pubkey', False)) + with patch.dict( + zypper.__salt__, + {"cmd.run": MagicMock(return_value=os.linesep.join(rpm_out))}, + ), patch.dict(zypper.__grains__, {"osarch": "x86_64"}), patch.dict( + zypper.__salt__, {"pkg_resource.add_pkg": _add_data} + ), patch.dict( + zypper.__salt__, + {"pkg_resource.format_pkg_list": pkg_resource.format_pkg_list}, + ), patch.dict( + zypper.__salt__, {"pkg_resource.stringify": MagicMock()} + ), patch.dict( + pkg_resource.__salt__, {"pkg.parse_arch": zypper.parse_arch} + ): + pkgs = zypper.list_pkgs( + attr=["epoch", "release", "arch", "install_date_time_t"] + ) + self.assertFalse(pkgs.get("gpg-pubkey", False)) for pkg_name, pkg_attr in { - 'jakarta-commons-discovery': [{ - 'version': '0.4', - 'release': '129.686', - 'arch': 'noarch', - 'install_date_time_t': 1498636511, - 'epoch': None, - }], - 'yast2-ftp-server': [{ - 'version': '3.1.8', - 'release': '8.1', - 'arch': 'x86_64', - 'install_date_time_t': 1499257798, - 'epoch': None, - }], - 'protobuf-java': [{ - 'version': '2.6.1', - 'release': '3.1.develHead', - 'install_date_time_t': 1499257756, - 'arch': 'noarch', - 'epoch': None, - }], - 'susemanager-build-keys-web': [{ - 'version': '12.0', - 'release': '5.1.develHead', - 'arch': 'noarch', - 'install_date_time_t': 1498636510, - 'epoch': None, - }], - 'apache-commons-cli': [{ - 'version': '1.2', - 'release': '1.233', - 'arch': 'noarch', - 'install_date_time_t': 1498636510, - 'epoch': None, - }], - 'kernel-default': [{ - 'version': '4.4.138', - 'release': '94.39.1', - 'arch': 'x86_64', - 'install_date_time_t': 1529936067, - 'epoch': None, - }, - { - 'version': '4.4.73', - 'release': '5.1', - 'arch': 'x86_64', - 'install_date_time_t': 1503572639, - 'epoch': None, - }], - 'perseus-dummy': [{ - 'version': '1.1', - 'release': '1.1', - 'arch': 'i586', - 'install_date_time_t': 1529936062, - 'epoch': None, - }], - 'jose4j': [{ - 'arch': 'noarch', - 'version': '0.4.4', - 'release': '2.1.develHead', - 'install_date_time_t': 1499257756, - 'epoch': None, - }]}.items(): + "jakarta-commons-discovery": [ + { + "version": "0.4", + "release": "129.686", + "arch": "noarch", + "install_date_time_t": 1498636511, + "epoch": None, + } + ], + "yast2-ftp-server": [ + { + "version": "3.1.8", + "release": "8.1", + "arch": "x86_64", + "install_date_time_t": 1499257798, + "epoch": None, + } + ], + "protobuf-java": [ + { + "version": "2.6.1", + "release": "3.1.develHead", + "install_date_time_t": 1499257756, + "arch": "noarch", + "epoch": None, + } + ], + "susemanager-build-keys-web": [ + { + "version": "12.0", + "release": "5.1.develHead", + "arch": "noarch", + "install_date_time_t": 1498636510, + "epoch": None, + } + ], + "apache-commons-cli": [ + { + "version": "1.2", + "release": "1.233", + "arch": "noarch", + "install_date_time_t": 1498636510, + "epoch": None, + } + ], + "kernel-default": [ + { + "version": "4.4.138", + "release": "94.39.1", + "arch": "x86_64", + "install_date_time_t": 1529936067, + "epoch": None, + }, + { + "version": "4.4.73", + "release": "5.1", + "arch": "x86_64", + "install_date_time_t": 1503572639, + "epoch": None, + }, + ], + "perseus-dummy": [ + { + "version": "1.1", + "release": "1.1", + "arch": "i586", + "install_date_time_t": 1529936062, + "epoch": None, + } + ], + "jose4j": [ + { + "arch": "noarch", + "version": "0.4.4", + "release": "2.1.develHead", + "install_date_time_t": 1499257756, + "epoch": None, + } + ], + }.items(): self.assertTrue(pkgs.get(pkg_name)) self.assertEqual(pkgs[pkg_name], pkg_attr) def test_list_pkgs_with_attr_multiple_versions(self): - ''' + """ Test packages listing with the attr parameter reporting multiple version installed :return: - ''' + """ + def _add_data(data, key, value): data.setdefault(key, []).append(value) rpm_out = [ - 'glibc_|-2.12_|-1.212.el6_|-i686_|-_|-1542394210', - 'glibc_|-2.12_|-1.212.el6_|-x86_64_|-_|-1542394204', - 'virt-what_|-1.13_|-8.el7_|-x86_64_|-_|-1487838486', - 'virt-what_|-1.10_|-2.el7_|-x86_64_|-_|-1387838486', + "glibc_|-2.12_|-1.212.el6_|-i686_|-_|-1542394210", + "glibc_|-2.12_|-1.212.el6_|-x86_64_|-_|-1542394204", + "virt-what_|-1.13_|-8.el7_|-x86_64_|-_|-1487838486", + "virt-what_|-1.10_|-2.el7_|-x86_64_|-_|-1387838486", ] - with patch.dict(zypper.__grains__, {'osarch': 'x86_64'}), \ - patch.dict(zypper.__salt__, {'cmd.run': MagicMock(return_value=os.linesep.join(rpm_out))}), \ - patch.dict(zypper.__salt__, {'pkg_resource.add_pkg': _add_data}), \ - patch.dict(zypper.__salt__, {'pkg_resource.format_pkg_list': pkg_resource.format_pkg_list}), \ - patch.dict(zypper.__salt__, {'pkg_resource.stringify': MagicMock()}), \ - patch.dict(pkg_resource.__salt__, {'pkg.parse_arch': zypper.parse_arch}): - pkgs = zypper.list_pkgs(attr=['epoch', 'release', 'arch', 'install_date_time_t']) + with patch.dict(zypper.__grains__, {"osarch": "x86_64"}), patch.dict( + zypper.__salt__, + {"cmd.run": MagicMock(return_value=os.linesep.join(rpm_out))}, + ), patch.dict(zypper.__salt__, {"pkg_resource.add_pkg": _add_data}), patch.dict( + zypper.__salt__, + {"pkg_resource.format_pkg_list": pkg_resource.format_pkg_list}, + ), patch.dict( + zypper.__salt__, {"pkg_resource.stringify": MagicMock()} + ), patch.dict( + pkg_resource.__salt__, {"pkg.parse_arch": zypper.parse_arch} + ): + pkgs = zypper.list_pkgs( + attr=["epoch", "release", "arch", "install_date_time_t"] + ) expected_pkg_list = { - 'glibc': [ + "glibc": [ { - 'version': '2.12', - 'release': '1.212.el6', - 'install_date_time_t': 1542394210, - 'arch': 'i686', - 'epoch': None + "version": "2.12", + "release": "1.212.el6", + "install_date_time_t": 1542394210, + "arch": "i686", + "epoch": None, }, { - 'version': '2.12', - 'release': '1.212.el6', - 'install_date_time_t': 1542394204, - 'arch': 'x86_64', - 'epoch': None - } + "version": "2.12", + "release": "1.212.el6", + "install_date_time_t": 1542394204, + "arch": "x86_64", + "epoch": None, + }, ], - 'virt-what': [ + "virt-what": [ { - 'version': '1.10', - 'release': '2.el7', - 'install_date_time_t': 1387838486, - 'arch': 'x86_64', - 'epoch': None + "version": "1.10", + "release": "2.el7", + "install_date_time_t": 1387838486, + "arch": "x86_64", + "epoch": None, }, { - 'version': '1.13', - 'release': '8.el7', - 'install_date_time_t': 1487838486, - 'arch': 'x86_64', - 'epoch': None - } - ] + "version": "1.13", + "release": "8.el7", + "install_date_time_t": 1487838486, + "arch": "x86_64", + "epoch": None, + }, + ], } for pkgname, pkginfo in pkgs.items(): if six.PY3: @@ -741,189 +1058,294 @@ Repository 'DUMMY' not found by its alias, number, or URI. self.assertItemsEqual(pkginfo, expected_pkg_list[pkgname]) def test_list_patches(self): - ''' + """ Test advisory patches listing. :return: - ''' + """ ref_out = { - 'stdout': get_test_data('zypper-patches.xml'), - 'stderr': None, - 'retcode': 0 + "stdout": get_test_data("zypper-patches.xml"), + "stderr": None, + "retcode": 0, } PATCHES_RET = { - 'SUSE-SLE-SERVER-12-SP2-2017-97': {'installed': False, 'summary': 'Recommended update for ovmf'}, - 'SUSE-SLE-SERVER-12-SP2-2017-98': {'installed': True, 'summary': 'Recommended update for kmod'}, - 'SUSE-SLE-SERVER-12-SP2-2017-99': {'installed': False, 'summary': 'Security update for apache2'} + "SUSE-SLE-SERVER-12-SP2-2017-97": { + "installed": False, + "summary": "Recommended update for ovmf", + }, + "SUSE-SLE-SERVER-12-SP2-2017-98": { + "installed": True, + "summary": "Recommended update for kmod", + }, + "SUSE-SLE-SERVER-12-SP2-2017-99": { + "installed": False, + "summary": "Security update for apache2", + }, } - with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=ref_out)}): + with patch.dict( + zypper.__salt__, {"cmd.run_all": MagicMock(return_value=ref_out)} + ): list_patches = zypper.list_patches(refresh=False) self.assertEqual(len(list_patches), 3) self.assertDictEqual(list_patches, PATCHES_RET) - @patch('salt.utils.path.os_walk', MagicMock(return_value=[('test', 'test', 'test')])) - @patch('os.path.getsize', MagicMock(return_value=123456)) - @patch('os.path.getctime', MagicMock(return_value=1234567890.123456)) - @patch('fnmatch.filter', MagicMock(return_value=['/var/cache/zypper/packages/foo/bar/test_package.rpm'])) + @patch( + "salt.utils.path.os_walk", MagicMock(return_value=[("test", "test", "test")]) + ) + @patch("os.path.getsize", MagicMock(return_value=123456)) + @patch("os.path.getctime", MagicMock(return_value=1234567890.123456)) + @patch( + "fnmatch.filter", + MagicMock(return_value=["/var/cache/zypper/packages/foo/bar/test_package.rpm"]), + ) def test_list_downloaded(self): - ''' + """ Test downloaded packages listing. :return: - ''' + """ DOWNLOADED_RET = { - 'test-package': { - '1.0': { - 'path': '/var/cache/zypper/packages/foo/bar/test_package.rpm', - 'size': 123456, - 'creation_date_time_t': 1234567890, - 'creation_date_time': '2009-02-13T23:31:30', + "test-package": { + "1.0": { + "path": "/var/cache/zypper/packages/foo/bar/test_package.rpm", + "size": 123456, + "creation_date_time_t": 1234567890, + "creation_date_time": "2009-02-13T23:31:30", } } } - with patch.dict(zypper.__salt__, {'lowpkg.bin_pkg_info': MagicMock(return_value={'name': 'test-package', - 'version': '1.0'})}): + with patch.dict( + zypper.__salt__, + { + "lowpkg.bin_pkg_info": MagicMock( + return_value={"name": "test-package", "version": "1.0"} + ) + }, + ): list_downloaded = zypper.list_downloaded() self.assertEqual(len(list_downloaded), 1) self.assertDictEqual(list_downloaded, DOWNLOADED_RET) def test_download(self): - ''' + """ Test package download :return: - ''' + """ download_out = { - 'stdout': get_test_data('zypper-download.xml'), - 'stderr': None, - 'retcode': 0 + "stdout": get_test_data("zypper-download.xml"), + "stderr": None, + "retcode": 0, } test_out = { - 'nmap': { - 'path': '/var/cache/zypp/packages/SLE-12-x86_64-Pool/x86_64/nmap-6.46-1.72.x86_64.rpm', - 'repository-alias': 'SLE-12-x86_64-Pool', - 'repository-name': 'SLE-12-x86_64-Pool' + "nmap": { + "path": "/var/cache/zypp/packages/SLE-12-x86_64-Pool/x86_64/nmap-6.46-1.72.x86_64.rpm", + "repository-alias": "SLE-12-x86_64-Pool", + "repository-name": "SLE-12-x86_64-Pool", } } - with patch.dict(zypper.__salt__, {'cmd.run_all': MagicMock(return_value=download_out)}): - with patch.dict(zypper.__salt__, {'lowpkg.checksum': MagicMock(return_value=True)}): + with patch.dict( + zypper.__salt__, {"cmd.run_all": MagicMock(return_value=download_out)} + ): + with patch.dict( + zypper.__salt__, {"lowpkg.checksum": MagicMock(return_value=True)} + ): self.assertEqual(zypper.download("nmap"), test_out) - test_out['_error'] = "The following package(s) failed to download: foo" + test_out["_error"] = "The following package(s) failed to download: foo" self.assertEqual(zypper.download("nmap", "foo"), test_out) - @patch('salt.modules.zypperpkg._systemd_scope', MagicMock(return_value=False)) - @patch('salt.modules.zypperpkg.list_downloaded', - MagicMock(side_effect=[{}, {'vim': {'1.1': {'path': '/foo/bar/test.rpm', - 'size': 1234, - 'creation_date_time_t': 1234567890, - 'creation_date_time': '2009-02-13T23:31:30'}}}])) + @patch("salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False)) + @patch( + "salt.modules.zypperpkg.list_downloaded", + MagicMock( + side_effect=[ + {}, + { + "vim": { + "1.1": { + "path": "/foo/bar/test.rpm", + "size": 1234, + "creation_date_time_t": 1234567890, + "creation_date_time": "2009-02-13T23:31:30", + } + } + }, + ] + ), + ) def test_install_with_downloadonly(self): - ''' + """ Test a package installation with downloadonly=True. :return: - ''' - with patch.dict(zypper.__salt__, - {'pkg_resource.parse_targets': MagicMock(return_value=({'vim': None}, 'repository'))}): - with patch('salt.modules.zypperpkg.__zypper__.noraise.call', MagicMock()) as zypper_mock: - ret = zypper.install(pkgs=['vim'], downloadonly=True) - zypper_mock.assert_called_once_with( - '--no-refresh', - 'install', - '--auto-agree-with-licenses', - '--name', - '--download-only', - 'vim' + """ + with patch.dict( + zypper.__salt__, + { + "pkg_resource.parse_targets": MagicMock( + return_value=({"vim": None}, "repository") + ) + }, + ): + with patch( + "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() + ) as zypper_mock: + ret = zypper.install(pkgs=["vim"], downloadonly=True) + zypper_mock.assert_called_once_with( + "--no-refresh", + "install", + "--auto-agree-with-licenses", + "--name", + "--download-only", + "vim", + ) + self.assertDictEqual( + ret, + { + "vim": { + "new": { + "1.1": { + "path": "/foo/bar/test.rpm", + "size": 1234, + "creation_date_time_t": 1234567890, + "creation_date_time": "2009-02-13T23:31:30", + } + }, + "old": "", + } + }, ) - self.assertDictEqual(ret, {'vim': {'new': {'1.1': {'path': '/foo/bar/test.rpm', - 'size': 1234, - 'creation_date_time_t': 1234567890, - 'creation_date_time': '2009-02-13T23:31:30'}}, - 'old': ''}}) - @patch('salt.modules.zypperpkg._systemd_scope', MagicMock(return_value=False)) - @patch('salt.modules.zypperpkg.list_downloaded', - MagicMock(return_value={'vim': {'1.1': {'path': '/foo/bar/test.rpm', - 'size': 1234, - 'creation_date_time_t': 1234567890, - 'creation_date_time': '2017-01-01T11:00:00'}}})) + @patch("salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False)) + @patch( + "salt.modules.zypperpkg.list_downloaded", + MagicMock( + return_value={ + "vim": { + "1.1": { + "path": "/foo/bar/test.rpm", + "size": 1234, + "creation_date_time_t": 1234567890, + "creation_date_time": "2017-01-01T11:00:00", + } + } + } + ), + ) def test_install_with_downloadonly_already_downloaded(self): - ''' + """ Test a package installation with downloadonly=True when package is already downloaded. :return: - ''' - with patch.dict(zypper.__salt__, {'pkg_resource.parse_targets': MagicMock(return_value=({'vim': None}, - 'repository'))}): - with patch('salt.modules.zypperpkg.__zypper__.noraise.call', MagicMock()) as zypper_mock: - ret = zypper.install(pkgs=['vim'], downloadonly=True) + """ + with patch.dict( + zypper.__salt__, + { + "pkg_resource.parse_targets": MagicMock( + return_value=({"vim": None}, "repository") + ) + }, + ): + with patch( + "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() + ) as zypper_mock: + ret = zypper.install(pkgs=["vim"], downloadonly=True) zypper_mock.assert_called_once_with( - '--no-refresh', - 'install', - '--auto-agree-with-licenses', - '--name', - '--download-only', - 'vim' + "--no-refresh", + "install", + "--auto-agree-with-licenses", + "--name", + "--download-only", + "vim", ) self.assertDictEqual(ret, {}) - @patch('salt.modules.zypperpkg._systemd_scope', MagicMock(return_value=False)) - @patch('salt.modules.zypperpkg._get_patches', - MagicMock(return_value={'SUSE-PATCH-1234': {'installed': False, 'summary': 'test'}})) - @patch('salt.modules.zypperpkg.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}])) + @patch("salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False)) + @patch( + "salt.modules.zypperpkg._get_patches", + MagicMock( + return_value={"SUSE-PATCH-1234": {"installed": False, "summary": "test"}} + ), + ) + @patch( + "salt.modules.zypperpkg.list_pkgs", + MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}]), + ) def test_install_advisory_patch_ok(self): - ''' + """ Test successfully advisory patch installation. :return: - ''' - with patch.dict(zypper.__salt__, - {'pkg_resource.parse_targets': MagicMock(return_value=({'SUSE-PATCH-1234': None}, - 'advisory'))}): - with patch('salt.modules.zypperpkg.__zypper__.noraise.call', MagicMock()) as zypper_mock: - ret = zypper.install(advisory_ids=['SUSE-PATCH-1234']) + """ + with patch.dict( + zypper.__salt__, + { + "pkg_resource.parse_targets": MagicMock( + return_value=({"SUSE-PATCH-1234": None}, "advisory") + ) + }, + ): + with patch( + "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() + ) as zypper_mock: + ret = zypper.install(advisory_ids=["SUSE-PATCH-1234"]) zypper_mock.assert_called_once_with( - '--no-refresh', - 'install', - '--auto-agree-with-licenses', - '--name', - 'patch:SUSE-PATCH-1234' + "--no-refresh", + "install", + "--auto-agree-with-licenses", + "--name", + "patch:SUSE-PATCH-1234", ) self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": "1.2"}}) - @patch('salt.modules.zypperpkg._systemd_scope', MagicMock(return_value=False)) - @patch('salt.modules.zypperpkg._get_patches', - MagicMock(return_value={'SUSE-PATCH-1234': {'installed': False, 'summary': 'test'}})) - @patch('salt.modules.zypperpkg.list_pkgs', MagicMock(return_value={"vim": "1.1"})) + @patch("salt.modules.zypperpkg._systemd_scope", MagicMock(return_value=False)) + @patch( + "salt.modules.zypperpkg._get_patches", + MagicMock( + return_value={"SUSE-PATCH-1234": {"installed": False, "summary": "test"}} + ), + ) + @patch("salt.modules.zypperpkg.list_pkgs", MagicMock(return_value={"vim": "1.1"})) def test_install_advisory_patch_failure(self): - ''' + """ Test failing advisory patch installation because patch does not exist. :return: - ''' - with patch.dict(zypper.__salt__, - {'pkg_resource.parse_targets': MagicMock(return_value=({'SUSE-PATCH-XXX': None}, 'advisory'))}): - with patch('salt.modules.zypperpkg.__zypper__.noraise.call', MagicMock()) as zypper_mock: - with self.assertRaisesRegex(CommandExecutionError, '^Advisory id "SUSE-PATCH-XXX" not found$'): - zypper.install(advisory_ids=['SUSE-PATCH-XXX']) + """ + with patch.dict( + zypper.__salt__, + { + "pkg_resource.parse_targets": MagicMock( + return_value=({"SUSE-PATCH-XXX": None}, "advisory") + ) + }, + ): + with patch( + "salt.modules.zypperpkg.__zypper__.noraise.call", MagicMock() + ) as zypper_mock: + with self.assertRaisesRegex( + CommandExecutionError, '^Advisory id "SUSE-PATCH-XXX" not found$' + ): + zypper.install(advisory_ids=["SUSE-PATCH-XXX"]) def test_remove_purge(self): - ''' + """ Test package removal :return: - ''' + """ + class ListPackages(object): def __init__(self): - self._packages = ['vim', 'pico'] + self._packages = ["vim", "pico"] self._pkgs = { - 'vim': '0.18.0', - 'emacs': '24.0.1', - 'pico': '0.1.1', + "vim": "0.18.0", + "emacs": "24.0.1", + "pico": "0.1.1", } def __call__(self): @@ -934,273 +1356,279 @@ Repository 'DUMMY' not found by its alias, number, or URI. return pkgs - parsed_targets = [{'vim': None, 'pico': None}, None] - cmd_out = { - 'retcode': 0, - 'stdout': '', - 'stderr': '' - } + parsed_targets = [{"vim": None, "pico": None}, None] + cmd_out = {"retcode": 0, "stdout": "", "stderr": ""} # If config.get starts being used elsewhere, we'll need to write a # side_effect function. patches = { - 'cmd.run_all': MagicMock(return_value=cmd_out), - 'pkg_resource.parse_targets': MagicMock(return_value=parsed_targets), - 'pkg_resource.stringify': MagicMock(), - 'config.get': MagicMock(return_value=True) + "cmd.run_all": MagicMock(return_value=cmd_out), + "pkg_resource.parse_targets": MagicMock(return_value=parsed_targets), + "pkg_resource.stringify": MagicMock(), + "config.get": MagicMock(return_value=True), } with patch.dict(zypper.__salt__, patches): - with patch('salt.modules.zypperpkg.list_pkgs', ListPackages()): - diff = zypper.remove(name='vim,pico') - for pkg_name in ['vim', 'pico']: + with patch("salt.modules.zypperpkg.list_pkgs", ListPackages()): + diff = zypper.remove(name="vim,pico") + for pkg_name in ["vim", "pico"]: self.assertTrue(diff.get(pkg_name)) - self.assertTrue(diff[pkg_name]['old']) - self.assertFalse(diff[pkg_name]['new']) + self.assertTrue(diff[pkg_name]["old"]) + self.assertFalse(diff[pkg_name]["new"]) def test_repo_value_info(self): - ''' + """ Tests if repo info is properly parsed. :return: - ''' + """ repos_cfg = configparser.ConfigParser() - for cfg in ['zypper-repo-1.cfg', 'zypper-repo-2.cfg']: + for cfg in ["zypper-repo-1.cfg", "zypper-repo-2.cfg"]: repos_cfg.readfp(six.moves.StringIO(get_test_data(cfg))) for alias in repos_cfg.sections(): r_info = zypper._get_repo_info(alias, repos_cfg=repos_cfg) - self.assertEqual(type(r_info['type']), type(None)) - self.assertEqual(type(r_info['enabled']), bool) - self.assertEqual(type(r_info['autorefresh']), bool) - self.assertEqual(type(r_info['baseurl']), str) - self.assertEqual(r_info['type'], None) - self.assertEqual(r_info['enabled'], alias == 'SLE12-SP1-x86_64-Update') - self.assertEqual(r_info['autorefresh'], alias == 'SLE12-SP1-x86_64-Update') + self.assertEqual(type(r_info["type"]), type(None)) + self.assertEqual(type(r_info["enabled"]), bool) + self.assertEqual(type(r_info["autorefresh"]), bool) + self.assertEqual(type(r_info["baseurl"]), str) + self.assertEqual(r_info["type"], None) + self.assertEqual(r_info["enabled"], alias == "SLE12-SP1-x86_64-Update") + self.assertEqual(r_info["autorefresh"], alias == "SLE12-SP1-x86_64-Update") def test_repo_add_nomod_noref(self): - ''' + """ Test mod_repo adds the new repo and nothing else :return: - ''' + """ zypper_patcher = patch.multiple( - 'salt.modules.zypperpkg', **self.zypper_patcher_config) + "salt.modules.zypperpkg", **self.zypper_patcher_config + ) - url = self.new_repo_config['url'] - name = self.new_repo_config['name'] + url = self.new_repo_config["url"] + name = self.new_repo_config["name"] with zypper_patcher: - zypper.mod_repo(name, **{'url': url}) + zypper.mod_repo(name, **{"url": url}) self.assertEqual( - zypper.__zypper__.xml.call.call_args_list, - [call('ar', url, name)] + zypper.__zypper__.xml.call.call_args_list, [call("ar", url, name)] ) self.assertTrue(zypper.__zypper__.refreshable.xml.call.call_count == 0) def test_repo_noadd_nomod_noref(self): - ''' + """ Test mod_repo detects the repo already exists, no modification was requested and no refresh requested either :return: - ''' - url = self.new_repo_config['url'] - name = self.new_repo_config['name'] - self.zypper_patcher_config['_get_configured_repos'] = Mock( - **{'return_value.sections.return_value': [name]} + """ + url = self.new_repo_config["url"] + name = self.new_repo_config["name"] + self.zypper_patcher_config["_get_configured_repos"] = Mock( + **{"return_value.sections.return_value": [name]} ) zypper_patcher = patch.multiple( - 'salt.modules.zypperpkg', **self.zypper_patcher_config) + "salt.modules.zypperpkg", **self.zypper_patcher_config + ) with zypper_patcher: - out = zypper.mod_repo(name, alias='new-alias') + out = zypper.mod_repo(name, alias="new-alias") self.assertEqual( - out['comment'], - 'Specified arguments did not result in modification of repo') + out["comment"], + "Specified arguments did not result in modification of repo", + ) self.assertTrue(zypper.__zypper__.xml.call.call_count == 0) self.assertTrue(zypper.__zypper__.refreshable.xml.call.call_count == 0) def test_repo_noadd_modbaseurl_ref(self): - ''' + """ Test mod_repo detects the repo already exists, no modification was requested and no refresh requested either :return: - ''' - url = self.new_repo_config['url'] - name = self.new_repo_config['name'] - self.zypper_patcher_config['_get_configured_repos'] = Mock( - **{'return_value.sections.side_effect': [[name], [], [], [name]]} + """ + url = self.new_repo_config["url"] + name = self.new_repo_config["name"] + self.zypper_patcher_config["_get_configured_repos"] = Mock( + **{"return_value.sections.side_effect": [[name], [], [], [name]]} ) zypper_patcher = patch.multiple( - 'salt.modules.zypperpkg', **self.zypper_patcher_config) + "salt.modules.zypperpkg", **self.zypper_patcher_config + ) with zypper_patcher: - params = {'baseurl': url + "-changed", 'enabled': False} + params = {"baseurl": url + "-changed", "enabled": False} zypper.mod_repo(name, **params) expected_params = { - 'alias': 'mock-repo-name', - 'autorefresh': True, - 'baseurl': 'http://repo.url/some/path-changed', - 'enabled': False, - 'priority': 1, - 'cache': False, - 'keeppackages': False, - 'type': 'rpm-md'} + "alias": "mock-repo-name", + "autorefresh": True, + "baseurl": "http://repo.url/some/path-changed", + "enabled": False, + "priority": 1, + "cache": False, + "keeppackages": False, + "type": "rpm-md", + } self.assertTrue(zypper.mod_repo.call_count == 2) - self.assertTrue(zypper.mod_repo.mock_calls[1] == call(name, **expected_params)) + self.assertTrue( + zypper.mod_repo.mock_calls[1] == call(name, **expected_params) + ) def test_repo_add_mod_noref(self): - ''' + """ Test mod_repo adds the new repo and call modify to update autorefresh :return: - ''' + """ zypper_patcher = patch.multiple( - 'salt.modules.zypperpkg', **self.zypper_patcher_config) + "salt.modules.zypperpkg", **self.zypper_patcher_config + ) - url = self.new_repo_config['url'] - name = self.new_repo_config['name'] + url = self.new_repo_config["url"] + name = self.new_repo_config["name"] with zypper_patcher: - zypper.mod_repo(name, **{'url': url, 'refresh': True}) + zypper.mod_repo(name, **{"url": url, "refresh": True}) self.assertEqual( - zypper.__zypper__.xml.call.call_args_list, - [call('ar', url, name)] + zypper.__zypper__.xml.call.call_args_list, [call("ar", url, name)] ) zypper.__zypper__.refreshable.xml.call.assert_called_once_with( - 'mr', '--refresh', name + "mr", "--refresh", name ) def test_repo_noadd_mod_noref(self): - ''' + """ Test mod_repo detects the repository exists, calls modify to update 'autorefresh' but does not call refresh :return: - ''' - url = self.new_repo_config['url'] - name = self.new_repo_config['name'] - self.zypper_patcher_config['_get_configured_repos'] = Mock( - **{'return_value.sections.return_value': [name]}) + """ + url = self.new_repo_config["url"] + name = self.new_repo_config["name"] + self.zypper_patcher_config["_get_configured_repos"] = Mock( + **{"return_value.sections.return_value": [name]} + ) zypper_patcher = patch.multiple( - 'salt.modules.zypperpkg', **self.zypper_patcher_config) + "salt.modules.zypperpkg", **self.zypper_patcher_config + ) with zypper_patcher: - zypper.mod_repo(name, **{'url': url, 'refresh': True}) + zypper.mod_repo(name, **{"url": url, "refresh": True}) self.assertTrue(zypper.__zypper__.xml.call.call_count == 0) zypper.__zypper__.refreshable.xml.call.assert_called_once_with( - 'mr', '--refresh', name + "mr", "--refresh", name ) def test_repo_add_nomod_ref(self): - ''' + """ Test mod_repo adds the new repo and refreshes the repo with `zypper --gpg-auto-import-keys refresh <repo-name>` :return: - ''' + """ zypper_patcher = patch.multiple( - 'salt.modules.zypperpkg', **self.zypper_patcher_config) + "salt.modules.zypperpkg", **self.zypper_patcher_config + ) - url = self.new_repo_config['url'] - name = self.new_repo_config['name'] + url = self.new_repo_config["url"] + name = self.new_repo_config["name"] with zypper_patcher: - zypper.mod_repo(name, **{'url': url, 'gpgautoimport': True}) + zypper.mod_repo(name, **{"url": url, "gpgautoimport": True}) self.assertEqual( zypper.__zypper__.xml.call.call_args_list, [ - call('ar', url, name), - call('--gpg-auto-import-keys', 'refresh', name) - ] + call("ar", url, name), + call("--gpg-auto-import-keys", "refresh", name), + ], ) self.assertTrue(zypper.__zypper__.refreshable.xml.call.call_count == 0) def test_repo_noadd_nomod_ref(self): - ''' + """ Test mod_repo detects the repo already exists, has nothing to modify and refreshes the repo with `zypper --gpg-auto-import-keys refresh <repo-name>` :return: - ''' - url = self.new_repo_config['url'] - name = self.new_repo_config['name'] - self.zypper_patcher_config['_get_configured_repos'] = Mock( - **{'return_value.sections.return_value': [name]} + """ + url = self.new_repo_config["url"] + name = self.new_repo_config["name"] + self.zypper_patcher_config["_get_configured_repos"] = Mock( + **{"return_value.sections.return_value": [name]} ) zypper_patcher = patch.multiple( - 'salt.modules.zypperpkg', **self.zypper_patcher_config) + "salt.modules.zypperpkg", **self.zypper_patcher_config + ) with zypper_patcher: - zypper.mod_repo(name, **{'url': url, 'gpgautoimport': True}) + zypper.mod_repo(name, **{"url": url, "gpgautoimport": True}) self.assertEqual( zypper.__zypper__.xml.call.call_args_list, - [call('--gpg-auto-import-keys', 'refresh', name)] + [call("--gpg-auto-import-keys", "refresh", name)], ) self.assertTrue(zypper.__zypper__.refreshable.xml.call.call_count == 0) def test_repo_add_mod_ref(self): - ''' + """ Test mod_repo adds the new repo, calls modify to update 'autorefresh' and refreshes the repo with `zypper --gpg-auto-import-keys refresh <repo-name>` :return: - ''' + """ zypper_patcher = patch.multiple( - 'salt.modules.zypperpkg', **self.zypper_patcher_config) + "salt.modules.zypperpkg", **self.zypper_patcher_config + ) - url = self.new_repo_config['url'] - name = self.new_repo_config['name'] + url = self.new_repo_config["url"] + name = self.new_repo_config["name"] with zypper_patcher: zypper.mod_repo( - name, - **{'url': url, 'refresh': True, 'gpgautoimport': True} + name, **{"url": url, "refresh": True, "gpgautoimport": True} ) self.assertEqual( zypper.__zypper__.xml.call.call_args_list, [ - call('ar', url, name), - call('--gpg-auto-import-keys', 'refresh', name) - ] + call("ar", url, name), + call("--gpg-auto-import-keys", "refresh", name), + ], ) zypper.__zypper__.refreshable.xml.call.assert_called_once_with( - '--gpg-auto-import-keys', 'mr', '--refresh', name + "--gpg-auto-import-keys", "mr", "--refresh", name ) def test_repo_noadd_mod_ref(self): - ''' + """ Test mod_repo detects the repo already exists, calls modify to update 'autorefresh' and refreshes the repo with `zypper --gpg-auto-import-keys refresh <repo-name>` :return: - ''' - url = self.new_repo_config['url'] - name = self.new_repo_config['name'] - self.zypper_patcher_config['_get_configured_repos'] = Mock( - **{'return_value.sections.return_value': [name]} + """ + url = self.new_repo_config["url"] + name = self.new_repo_config["name"] + self.zypper_patcher_config["_get_configured_repos"] = Mock( + **{"return_value.sections.return_value": [name]} ) zypper_patcher = patch.multiple( - 'salt.modules.zypperpkg', **self.zypper_patcher_config) + "salt.modules.zypperpkg", **self.zypper_patcher_config + ) with zypper_patcher: zypper.mod_repo( - name, - **{'url': url, 'refresh': True, 'gpgautoimport': True} + name, **{"url": url, "refresh": True, "gpgautoimport": True} ) self.assertEqual( zypper.__zypper__.xml.call.call_args_list, - [call('--gpg-auto-import-keys', 'refresh', name)] + [call("--gpg-auto-import-keys", "refresh", name)], ) zypper.__zypper__.refreshable.xml.call.assert_called_once_with( - '--gpg-auto-import-keys', 'mr', '--refresh', name + "--gpg-auto-import-keys", "mr", "--refresh", name ) def test_wildcard_to_query_match_all(self): - ''' + """ Test wildcard to query match all pattern :return: - ''' + """ xmldoc = """<?xml version='1.0'?><stream> <search-result version="0.0"><solvable-list> <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="SLE-12-SP2-x86_64-Pool"/> @@ -1214,14 +1642,18 @@ Repository 'DUMMY' not found by its alias, number, or URI. _zpr = MagicMock() _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) wcard = zypper.Wildcard(_zpr) - wcard.name, wcard.version = 'libzypp', '*' - assert wcard._get_scope_versions(wcard._get_available_versions()) == ['16.2.4-19.5', '16.3.2-25.1', '16.5.2-27.9.1'] + wcard.name, wcard.version = "libzypp", "*" + assert wcard._get_scope_versions(wcard._get_available_versions()) == [ + "16.2.4-19.5", + "16.3.2-25.1", + "16.5.2-27.9.1", + ] def test_wildcard_to_query_multiple_asterisk(self): - ''' + """ Test wildcard to query match multiple asterisk :return: - ''' + """ xmldoc = """<?xml version='1.0'?><stream> <search-result version="0.0"><solvable-list> <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="foo"/> @@ -1233,14 +1665,17 @@ Repository 'DUMMY' not found by its alias, number, or URI. _zpr = MagicMock() _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) wcard = zypper.Wildcard(_zpr) - wcard.name, wcard.version = 'libzypp', '16.2.*-2*' - assert wcard._get_scope_versions(wcard._get_available_versions()) == ['16.2.5-25.1', '16.2.6-27.9.1'] + wcard.name, wcard.version = "libzypp", "16.2.*-2*" + assert wcard._get_scope_versions(wcard._get_available_versions()) == [ + "16.2.5-25.1", + "16.2.6-27.9.1", + ] def test_wildcard_to_query_exact_match_at_end(self): - ''' + """ Test wildcard to query match exact pattern at the end :return: - ''' + """ xmldoc = """<?xml version='1.0'?><stream> <search-result version="0.0"><solvable-list> <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="foo"/> @@ -1252,14 +1687,16 @@ Repository 'DUMMY' not found by its alias, number, or URI. _zpr = MagicMock() _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) wcard = zypper.Wildcard(_zpr) - wcard.name, wcard.version = 'libzypp', '16.2.5*' - assert wcard._get_scope_versions(wcard._get_available_versions()) == ['16.2.5-25.1'] + wcard.name, wcard.version = "libzypp", "16.2.5*" + assert wcard._get_scope_versions(wcard._get_available_versions()) == [ + "16.2.5-25.1" + ] def test_wildcard_to_query_exact_match_at_beginning(self): - ''' + """ Test wildcard to query match exact pattern at the beginning :return: - ''' + """ xmldoc = """<?xml version='1.0'?><stream> <search-result version="0.0"><solvable-list> <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="foo"/> @@ -1271,15 +1708,18 @@ Repository 'DUMMY' not found by its alias, number, or URI. _zpr = MagicMock() _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) wcard = zypper.Wildcard(_zpr) - wcard.name, wcard.version = 'libzypp', '*.1' - assert wcard._get_scope_versions(wcard._get_available_versions()) == ['16.2.5-25.1', '17.2.6-27.9.1'] + wcard.name, wcard.version = "libzypp", "*.1" + assert wcard._get_scope_versions(wcard._get_available_versions()) == [ + "16.2.5-25.1", + "17.2.6-27.9.1", + ] def test_wildcard_to_query_usage(self): - ''' + """ Test wildcard to query usage. :return: - ''' + """ xmldoc = """<?xml version='1.0'?><stream> <search-result version="0.0"><solvable-list> <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="foo"/> @@ -1289,17 +1729,17 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ _zpr = MagicMock() _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) - assert zypper.Wildcard(_zpr)('libzypp', '16.2.4*') == '16.2.4-19.5' - assert zypper.Wildcard(_zpr)('libzypp', '16.2*') == '16.2.5-25.1' - assert zypper.Wildcard(_zpr)('libzypp', '*6-*') == '17.2.6-27.9.1' - assert zypper.Wildcard(_zpr)('libzypp', '*.1') == '17.2.6-27.9.1' + assert zypper.Wildcard(_zpr)("libzypp", "16.2.4*") == "16.2.4-19.5" + assert zypper.Wildcard(_zpr)("libzypp", "16.2*") == "16.2.5-25.1" + assert zypper.Wildcard(_zpr)("libzypp", "*6-*") == "17.2.6-27.9.1" + assert zypper.Wildcard(_zpr)("libzypp", "*.1") == "17.2.6-27.9.1" def test_wildcard_to_query_noversion(self): - ''' + """ Test wildcard to query when no version has been passed on. :return: - ''' + """ xmldoc = """<?xml version='1.0'?><stream> <search-result version="0.0"><solvable-list> <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="foo"/> @@ -1309,14 +1749,14 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ _zpr = MagicMock() _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) - assert zypper.Wildcard(_zpr)('libzypp', None) is None + assert zypper.Wildcard(_zpr)("libzypp", None) is None def test_wildcard_to_query_typecheck(self): - ''' + """ Test wildcard to query typecheck. :return: - ''' + """ xmldoc = """<?xml version='1.0'?><stream> <search-result version="0.0"><solvable-list> <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="foo"/> @@ -1326,14 +1766,14 @@ Repository 'DUMMY' not found by its alias, number, or URI. """ _zpr = MagicMock() _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) - assert isinstance(zypper.Wildcard(_zpr)('libzypp', '*.1'), six.string_types) + assert isinstance(zypper.Wildcard(_zpr)("libzypp", "*.1"), six.string_types) def test_wildcard_to_query_condition_preservation(self): - ''' + """ Test wildcard to query Zypper condition preservation. :return: - ''' + """ xmldoc = """<?xml version='1.0'?><stream> <search-result version="0.0"><solvable-list> <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="foo"/> @@ -1345,18 +1785,22 @@ Repository 'DUMMY' not found by its alias, number, or URI. _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) for op in zypper.Wildcard.Z_OP: - assert zypper.Wildcard(_zpr)('libzypp', '{0}*.1'.format(op)) == '{0}17.2.6-27.9.1'.format(op) + assert zypper.Wildcard(_zpr)( + "libzypp", "{0}*.1".format(op) + ) == "{0}17.2.6-27.9.1".format(op) # Auto-fix feature: moves operator from end to front for op in zypper.Wildcard.Z_OP: - assert zypper.Wildcard(_zpr)('libzypp', '16*{0}'.format(op)) == '{0}16.2.5-25.1'.format(op) + assert zypper.Wildcard(_zpr)( + "libzypp", "16*{0}".format(op) + ) == "{0}16.2.5-25.1".format(op) def test_wildcard_to_query_unsupported_operators(self): - ''' + """ Test wildcard to query unsupported operators. :return: - ''' + """ xmldoc = """<?xml version='1.0'?><stream> <search-result version="0.0"><solvable-list> <solvable status="installed" name="libzypp" kind="package" edition="16.2.4-19.5" arch="x86_64" repository="foo"/> @@ -1367,5 +1811,5 @@ Repository 'DUMMY' not found by its alias, number, or URI. _zpr = MagicMock() _zpr.nolock.xml.call = MagicMock(return_value=minidom.parseString(xmldoc)) with self.assertRaises(CommandExecutionError): - for op in ['>>', '==', '<<', '+']: - zypper.Wildcard(_zpr)('libzypp', '{0}*.1'.format(op)) + for op in [">>", "==", "<<", "+"]: + zypper.Wildcard(_zpr)("libzypp", "{0}*.1".format(op)) diff --git a/tests/unit/netapi/test_rest_cherrypy.py b/tests/unit/netapi/test_rest_cherrypy.py index 227699d3549..b6480f2875c 100644 --- a/tests/unit/netapi/test_rest_cherrypy.py +++ b/tests/unit/netapi/test_rest_cherrypy.py @@ -8,7 +8,9 @@ import salt.utils.json import salt.utils.yaml # Import 3rd-party libs -from salt.ext.six.moves.urllib.parse import urlencode # pylint: disable=no-name-in-module,import-error +from salt.ext.six.moves.urllib.parse import ( # pylint: disable=no-name-in-module,import-error + urlencode, +) # Import Salt libs from tests.support.cherrypy_testclasses import BaseToolsTest @@ -17,89 +19,95 @@ from tests.support.cherrypy_testclasses import BaseToolsTest class TestOutFormats(BaseToolsTest): def __get_cp_config__(self): return { - 'tools.hypermedia_out.on': True, + "tools.hypermedia_out.on": True, } def test_default_accept(self): - request, response = self.request('/') - self.assertEqual(response.headers['Content-type'], 'application/json') + request, response = self.request("/") + self.assertEqual(response.headers["Content-type"], "application/json") def test_unsupported_accept(self): - request, response = self.request('/', headers=( - ('Accept', 'application/ms-word'), - )) - self.assertEqual(response.status, '406 Not Acceptable') + request, response = self.request( + "/", headers=(("Accept", "application/ms-word"),) + ) + self.assertEqual(response.status, "406 Not Acceptable") def test_json_out(self): - request, response = self.request('/', headers=( - ('Accept', 'application/json'), - )) - self.assertEqual(response.headers['Content-type'], 'application/json') + request, response = self.request("/", headers=(("Accept", "application/json"),)) + self.assertEqual(response.headers["Content-type"], "application/json") def test_yaml_out(self): - request, response = self.request('/', headers=( - ('Accept', 'application/x-yaml'), - )) - self.assertEqual(response.headers['Content-type'], 'application/x-yaml') + request, response = self.request( + "/", headers=(("Accept", "application/x-yaml"),) + ) + self.assertEqual(response.headers["Content-type"], "application/x-yaml") class TestInFormats(BaseToolsTest): def __get_cp_config__(self): return { - 'tools.hypermedia_in.on': True, + "tools.hypermedia_in.on": True, } def test_urlencoded_ctype(self): - data = {'valid': 'stuff'} - request, response = self.request('/', method='POST', - body=urlencode(data), headers=( - ('Content-type', 'application/x-www-form-urlencoded'), - )) - self.assertEqual(response.status, '200 OK') + data = {"valid": "stuff"} + request, response = self.request( + "/", + method="POST", + body=urlencode(data), + headers=(("Content-type", "application/x-www-form-urlencoded"),), + ) + self.assertEqual(response.status, "200 OK") self.assertDictEqual(request.unserialized_data, data) def test_json_ctype(self): - data = {'valid': 'stuff'} - request, response = self.request('/', method='POST', - body=salt.utils.json.dumps(data), headers=( - ('Content-type', 'application/json'), - )) - self.assertEqual(response.status, '200 OK') + data = {"valid": "stuff"} + request, response = self.request( + "/", + method="POST", + body=salt.utils.json.dumps(data), + headers=(("Content-type", "application/json"),), + ) + self.assertEqual(response.status, "200 OK") self.assertDictEqual(request.unserialized_data, data) def test_json_as_text_out(self): - ''' + """ Some service send JSON as text/plain for compatibility purposes - ''' - data = {'valid': 'stuff'} - request, response = self.request('/', method='POST', - body=salt.utils.json.dumps(data), headers=( - ('Content-type', 'text/plain'), - )) - self.assertEqual(response.status, '200 OK') + """ + data = {"valid": "stuff"} + request, response = self.request( + "/", + method="POST", + body=salt.utils.json.dumps(data), + headers=(("Content-type", "text/plain"),), + ) + self.assertEqual(response.status, "200 OK") self.assertDictEqual(request.unserialized_data, data) def test_yaml_ctype(self): - data = {'valid': 'stuff'} - request, response = self.request('/', method='POST', - body=salt.utils.yaml.safe_dump(data), headers=( - ('Content-type', 'application/x-yaml'), - )) - self.assertEqual(response.status, '200 OK') + data = {"valid": "stuff"} + request, response = self.request( + "/", + method="POST", + body=salt.utils.yaml.safe_dump(data), + headers=(("Content-type", "application/x-yaml"),), + ) + self.assertEqual(response.status, "200 OK") self.assertDictEqual(request.unserialized_data, data) class TestCors(BaseToolsTest): def __get_cp_config__(self): return { - 'tools.cors_tool.on': True, + "tools.cors_tool.on": True, } def test_option_request(self): request, response = self.request( - '/', method='OPTIONS', headers=( - ('Origin', 'https://domain.com'), - )) - self.assertEqual(response.status, '200 OK') - self.assertEqual(response.headers.get( - 'Access-Control-Allow-Origin'), 'https://domain.com') + "/", method="OPTIONS", headers=(("Origin", "https://domain.com"),) + ) + self.assertEqual(response.status, "200 OK") + self.assertEqual( + response.headers.get("Access-Control-Allow-Origin"), "https://domain.com" + ) diff --git a/tests/unit/netapi/test_rest_tornado.py b/tests/unit/netapi/test_rest_tornado.py index e7c3097d2a3..4d6a6039141 100644 --- a/tests/unit/netapi/test_rest_tornado.py +++ b/tests/unit/netapi/test_rest_tornado.py @@ -2,24 +2,32 @@ # Import Python libs from __future__ import absolute_import -import os -import copy -import shutil -import hashlib -# Import Salt Testing Libs -from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.unit import TestCase, skipIf -from tests.support.helpers import patched_environ -from tests.support.runtests import RUNTIME_VARS -from tests.support.events import eventpublisher_process +import copy +import hashlib +import os +import shutil # Import Salt libs import salt.auth import salt.utils.event import salt.utils.json import salt.utils.yaml +from salt.ext import six from salt.ext.six.moves import map, range # pylint: disable=import-error +from salt.ext.six.moves.urllib.parse import ( # pylint: disable=no-name-in-module + urlencode, + urlparse, +) +from tests.support.events import eventpublisher_process +from tests.support.helpers import patched_environ + +# Import Salt Testing Libs +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + try: HAS_TORNADO = True except ImportError: @@ -36,6 +44,7 @@ try: from salt.ext.tornado.websocket import websocket_connect import salt.netapi.rest_tornado as rest_tornado from salt.netapi.rest_tornado import saltnado + HAS_TORNADO = True except ImportError: HAS_TORNADO = False @@ -47,30 +56,33 @@ except ImportError: class AsyncHTTPTestCase(object): pass -from salt.ext import six -from salt.ext.six.moves.urllib.parse import urlencode, urlparse # pylint: disable=no-name-in-module + # pylint: enable=import-error -from tests.support.mock import MagicMock, patch - -@skipIf(not HAS_TORNADO, 'The tornado package needs to be installed') # pylint: disable=W0223 +@skipIf( + not HAS_TORNADO, "The tornado package needs to be installed" +) # pylint: disable=W0223 class SaltnadoTestCase(TestCase, AdaptedConfigurationTestCaseMixin, AsyncHTTPTestCase): - ''' + """ Mixin to hold some shared things - ''' - content_type_map = {'json': 'application/json', - 'json-utf8': 'application/json; charset=utf-8', - 'yaml': 'application/x-yaml', - 'text': 'text/plain', - 'form': 'application/x-www-form-urlencoded', - 'xml': 'application/xml', - 'real-accept-header-json': 'application/json, text/javascript, */*; q=0.01', - 'real-accept-header-yaml': 'application/x-yaml, text/yaml, */*; q=0.01'} + """ + + content_type_map = { + "json": "application/json", + "json-utf8": "application/json; charset=utf-8", + "yaml": "application/x-yaml", + "text": "text/plain", + "form": "application/x-www-form-urlencoded", + "xml": "application/xml", + "real-accept-header-json": "application/json, text/javascript, */*; q=0.01", + "real-accept-header-yaml": "application/x-yaml, text/yaml, */*; q=0.01", + } auth_creds = ( - ('username', 'saltdev_api'), - ('password', 'saltdev'), - ('eauth', 'auto')) + ("username", "saltdev_api"), + ("password", "saltdev"), + ("eauth", "auto"), + ) @property def auth_creds_dict(self): @@ -78,50 +90,50 @@ class SaltnadoTestCase(TestCase, AdaptedConfigurationTestCaseMixin, AsyncHTTPTes @property def opts(self): - return self.get_temp_config('client_config') + return self.get_temp_config("client_config") @property def mod_opts(self): - return self.get_temp_config('minion') + return self.get_temp_config("minion") @property def auth(self): - if not hasattr(self, '__auth'): + if not hasattr(self, "__auth"): self.__auth = salt.auth.LoadAuth(self.opts) return self.__auth @property def token(self): - ''' Mint and return a valid token for auth_creds ''' + """ Mint and return a valid token for auth_creds """ return self.auth.mk_token(self.auth_creds_dict) def setUp(self): super(SaltnadoTestCase, self).setUp() - self.patched_environ = patched_environ(ASYNC_TEST_TIMEOUT='30') + self.patched_environ = patched_environ(ASYNC_TEST_TIMEOUT="30") self.patched_environ.__enter__() self.addCleanup(self.patched_environ.__exit__) def tearDown(self): super(SaltnadoTestCase, self).tearDown() - if hasattr(self, 'http_server'): + if hasattr(self, "http_server"): del self.http_server - if hasattr(self, 'io_loop'): + if hasattr(self, "io_loop"): del self.io_loop - if hasattr(self, '_app'): + if hasattr(self, "_app"): del self._app - if hasattr(self, 'http_client'): + if hasattr(self, "http_client"): del self.http_client - if hasattr(self, '__port'): + if hasattr(self, "__port"): del self.__port - if hasattr(self, '_AsyncHTTPTestCase__port'): + if hasattr(self, "_AsyncHTTPTestCase__port"): del self._AsyncHTTPTestCase__port - if hasattr(self, '__auth'): + if hasattr(self, "__auth"): del self.__auth - if hasattr(self, '_SaltnadoTestCase__auth'): + if hasattr(self, "_SaltnadoTestCase__auth"): del self._SaltnadoTestCase__auth - if hasattr(self, '_test_generator'): + if hasattr(self, "_test_generator"): del self._test_generator - if hasattr(self, 'application'): + if hasattr(self, "application"): del self.application def build_tornado_app(self, urls): @@ -140,8 +152,8 @@ class SaltnadoTestCase(TestCase, AdaptedConfigurationTestCaseMixin, AsyncHTTPTes return response if response.body: # Decode it - if response.headers.get('Content-Type') == 'application/json': - response._body = response.body.decode('utf-8') + if response.headers.get("Content-Type") == "application/json": + response._body = response.body.decode("utf-8") else: response._body = salt.ext.tornado.escape.native_str(response.body) return response @@ -160,316 +172,381 @@ class TestBaseSaltAPIHandler(SaltnadoTestCase): return self.echo_stuff() def echo_stuff(self): - ret_dict = {'foo': 'bar'} - attrs = ('token', - 'start', - 'connected', - 'lowstate', - ) + ret_dict = {"foo": "bar"} + attrs = ( + "token", + "start", + "connected", + "lowstate", + ) for attr in attrs: ret_dict[attr] = getattr(self, attr) self.write(self.serialize(ret_dict)) - urls = [('/', StubHandler), - ('/(.*)', StubHandler)] + + urls = [("/", StubHandler), ("/(.*)", StubHandler)] return self.build_tornado_app(urls) def test_accept_content_type(self): - ''' + """ Test the base handler's accept picking - ''' + """ # send NO accept header, should come back with json - response = self.fetch('/') - self.assertEqual(response.headers['Content-Type'], self.content_type_map['json']) + response = self.fetch("/") + self.assertEqual( + response.headers["Content-Type"], self.content_type_map["json"] + ) self.assertEqual(type(salt.utils.json.loads(response.body)), dict) # Request application/json - response = self.fetch('/', headers={'Accept': self.content_type_map['json']}) - self.assertEqual(response.headers['Content-Type'], self.content_type_map['json']) + response = self.fetch("/", headers={"Accept": self.content_type_map["json"]}) + self.assertEqual( + response.headers["Content-Type"], self.content_type_map["json"] + ) self.assertEqual(type(salt.utils.json.loads(response.body)), dict) # Request application/x-yaml - response = self.fetch('/', headers={'Accept': self.content_type_map['yaml']}) - self.assertEqual(response.headers['Content-Type'], self.content_type_map['yaml']) + response = self.fetch("/", headers={"Accept": self.content_type_map["yaml"]}) + self.assertEqual( + response.headers["Content-Type"], self.content_type_map["yaml"] + ) self.assertEqual(type(salt.utils.yaml.safe_load(response.body)), dict) # Request not supported content-type - response = self.fetch('/', headers={'Accept': self.content_type_map['xml']}) + response = self.fetch("/", headers={"Accept": self.content_type_map["xml"]}) self.assertEqual(response.code, 406) # Request some JSON with a browser like Accept - accept_header = self.content_type_map['real-accept-header-json'] - response = self.fetch('/', headers={'Accept': accept_header}) - self.assertEqual(response.headers['Content-Type'], self.content_type_map['json']) + accept_header = self.content_type_map["real-accept-header-json"] + response = self.fetch("/", headers={"Accept": accept_header}) + self.assertEqual( + response.headers["Content-Type"], self.content_type_map["json"] + ) self.assertEqual(type(salt.utils.json.loads(response.body)), dict) # Request some YAML with a browser like Accept - accept_header = self.content_type_map['real-accept-header-yaml'] - response = self.fetch('/', headers={'Accept': accept_header}) - self.assertEqual(response.headers['Content-Type'], self.content_type_map['yaml']) + accept_header = self.content_type_map["real-accept-header-yaml"] + response = self.fetch("/", headers={"Accept": accept_header}) + self.assertEqual( + response.headers["Content-Type"], self.content_type_map["yaml"] + ) self.assertEqual(type(salt.utils.yaml.safe_load(response.body)), dict) def test_token(self): - ''' + """ Test that the token is returned correctly - ''' - token = salt.utils.json.loads(self.fetch('/').body)['token'] + """ + token = salt.utils.json.loads(self.fetch("/").body)["token"] self.assertIs(token, None) # send a token as a header - response = self.fetch('/', headers={saltnado.AUTH_TOKEN_HEADER: 'foo'}) - token = salt.utils.json.loads(response.body)['token'] - self.assertEqual(token, 'foo') + response = self.fetch("/", headers={saltnado.AUTH_TOKEN_HEADER: "foo"}) + token = salt.utils.json.loads(response.body)["token"] + self.assertEqual(token, "foo") # send a token as a cookie - response = self.fetch('/', headers={'Cookie': '{0}=foo'.format(saltnado.AUTH_COOKIE_NAME)}) - token = salt.utils.json.loads(response.body)['token'] - self.assertEqual(token, 'foo') + response = self.fetch( + "/", headers={"Cookie": "{0}=foo".format(saltnado.AUTH_COOKIE_NAME)} + ) + token = salt.utils.json.loads(response.body)["token"] + self.assertEqual(token, "foo") # send both, make sure its the header - response = self.fetch('/', headers={saltnado.AUTH_TOKEN_HEADER: 'foo', - 'Cookie': '{0}=bar'.format(saltnado.AUTH_COOKIE_NAME)}) - token = salt.utils.json.loads(response.body)['token'] - self.assertEqual(token, 'foo') + response = self.fetch( + "/", + headers={ + saltnado.AUTH_TOKEN_HEADER: "foo", + "Cookie": "{0}=bar".format(saltnado.AUTH_COOKIE_NAME), + }, + ) + token = salt.utils.json.loads(response.body)["token"] + self.assertEqual(token, "foo") def test_deserialize(self): - ''' + """ Send various encoded forms of lowstates (and bad ones) to make sure we handle deserialization correctly - ''' - valid_lowstate = [{ - "client": "local", - "tgt": "*", - "fun": "test.fib", - "arg": ["10"] - }, + """ + valid_lowstate = [ + {"client": "local", "tgt": "*", "fun": "test.fib", "arg": ["10"]}, { "client": "runner", "fun": "jobs.lookup_jid", - "jid": "20130603122505459265" - }] + "jid": "20130603122505459265", + }, + ] # send as JSON - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(valid_lowstate), - headers={'Content-Type': self.content_type_map['json']}) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(valid_lowstate), + headers={"Content-Type": self.content_type_map["json"]}, + ) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) # send yaml as json (should break) - response = self.fetch('/', - method='POST', - body=salt.utils.yaml.safe_dump(valid_lowstate), - headers={'Content-Type': self.content_type_map['json']}) + response = self.fetch( + "/", + method="POST", + body=salt.utils.yaml.safe_dump(valid_lowstate), + headers={"Content-Type": self.content_type_map["json"]}, + ) self.assertEqual(response.code, 400) # send as yaml - response = self.fetch('/', - method='POST', - body=salt.utils.yaml.safe_dump(valid_lowstate), - headers={'Content-Type': self.content_type_map['yaml']}) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + response = self.fetch( + "/", + method="POST", + body=salt.utils.yaml.safe_dump(valid_lowstate), + headers={"Content-Type": self.content_type_map["yaml"]}, + ) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) # send json as yaml (works since yaml is a superset of json) - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(valid_lowstate), - headers={'Content-Type': self.content_type_map['yaml']}) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(valid_lowstate), + headers={"Content-Type": self.content_type_map["yaml"]}, + ) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) # send json as text/plain - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(valid_lowstate), - headers={'Content-Type': self.content_type_map['text']}) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(valid_lowstate), + headers={"Content-Type": self.content_type_map["text"]}, + ) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) # send form-urlencoded form_lowstate = ( - ('client', 'local'), - ('tgt', '*'), - ('fun', 'test.fib'), - ('arg', '10'), - ('arg', 'foo'), + ("client", "local"), + ("tgt", "*"), + ("fun", "test.fib"), + ("arg", "10"), + ("arg", "foo"), ) - response = self.fetch('/', - method='POST', - body=urlencode(form_lowstate), - headers={'Content-Type': self.content_type_map['form']}) - returned_lowstate = salt.utils.json.loads(response.body)['lowstate'] + response = self.fetch( + "/", + method="POST", + body=urlencode(form_lowstate), + headers={"Content-Type": self.content_type_map["form"]}, + ) + returned_lowstate = salt.utils.json.loads(response.body)["lowstate"] self.assertEqual(len(returned_lowstate), 1) returned_lowstate = returned_lowstate[0] - self.assertEqual(returned_lowstate['client'], 'local') - self.assertEqual(returned_lowstate['tgt'], '*') - self.assertEqual(returned_lowstate['fun'], 'test.fib') - self.assertEqual(returned_lowstate['arg'], ['10', 'foo']) + self.assertEqual(returned_lowstate["client"], "local") + self.assertEqual(returned_lowstate["tgt"], "*") + self.assertEqual(returned_lowstate["fun"], "test.fib") + self.assertEqual(returned_lowstate["arg"], ["10", "foo"]) # Send json with utf8 charset - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(valid_lowstate), - headers={'Content-Type': self.content_type_map['json-utf8']}) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(valid_lowstate), + headers={"Content-Type": self.content_type_map["json-utf8"]}, + ) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) def test_get_lowstate(self): - ''' + """ Test transformations low data of the function _get_lowstate - ''' - valid_lowstate = [{ - u"client": u"local", - u"tgt": u"*", - u"fun": u"test.fib", - u"arg": [u"10"] - }] + """ + valid_lowstate = [ + {u"client": u"local", u"tgt": u"*", u"fun": u"test.fib", u"arg": [u"10"]} + ] # Case 1. dictionary type of lowstate request_lowstate = { - "client": "local", - "tgt": "*", - "fun": "test.fib", - "arg": ["10"] - } + "client": "local", + "tgt": "*", + "fun": "test.fib", + "arg": ["10"], + } - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(request_lowstate), - headers={'Content-Type': self.content_type_map['json']}) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(request_lowstate), + headers={"Content-Type": self.content_type_map["json"]}, + ) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) # Case 2. string type of arg request_lowstate = { - "client": "local", - "tgt": "*", - "fun": "test.fib", - "arg": "10" - } + "client": "local", + "tgt": "*", + "fun": "test.fib", + "arg": "10", + } - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(request_lowstate), - headers={'Content-Type': self.content_type_map['json']}) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(request_lowstate), + headers={"Content-Type": self.content_type_map["json"]}, + ) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) # Case 3. Combine Case 1 and Case 2. request_lowstate = { - "client": "local", - "tgt": "*", - "fun": "test.fib", - "arg": "10" - } + "client": "local", + "tgt": "*", + "fun": "test.fib", + "arg": "10", + } # send as json - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(request_lowstate), - headers={'Content-Type': self.content_type_map['json']}) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(request_lowstate), + headers={"Content-Type": self.content_type_map["json"]}, + ) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) # send as yaml - response = self.fetch('/', - method='POST', - body=salt.utils.yaml.safe_dump(request_lowstate), - headers={'Content-Type': self.content_type_map['yaml']}) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + response = self.fetch( + "/", + method="POST", + body=salt.utils.yaml.safe_dump(request_lowstate), + headers={"Content-Type": self.content_type_map["yaml"]}, + ) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) # send as plain text - response = self.fetch('/', - method='POST', - body=salt.utils.json.dumps(request_lowstate), - headers={'Content-Type': self.content_type_map['text']}) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + response = self.fetch( + "/", + method="POST", + body=salt.utils.json.dumps(request_lowstate), + headers={"Content-Type": self.content_type_map["text"]}, + ) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) # send as form-urlencoded request_form_lowstate = ( - ('client', 'local'), - ('tgt', '*'), - ('fun', 'test.fib'), - ('arg', '10'), + ("client", "local"), + ("tgt", "*"), + ("fun", "test.fib"), + ("arg", "10"), ) - response = self.fetch('/', - method='POST', - body=urlencode(request_form_lowstate), - headers={'Content-Type': self.content_type_map['form']}) - self.assertEqual(valid_lowstate, salt.utils.json.loads(response.body)['lowstate']) + response = self.fetch( + "/", + method="POST", + body=urlencode(request_form_lowstate), + headers={"Content-Type": self.content_type_map["form"]}, + ) + self.assertEqual( + valid_lowstate, salt.utils.json.loads(response.body)["lowstate"] + ) def test_cors_origin_wildcard(self): - ''' + """ Check that endpoints returns Access-Control-Allow-Origin - ''' - self._app.mod_opts['cors_origin'] = '*' + """ + self._app.mod_opts["cors_origin"] = "*" - headers = self.fetch('/').headers + headers = self.fetch("/").headers self.assertEqual(headers["Access-Control-Allow-Origin"], "*") def test_cors_origin_single(self): - ''' + """ Check that endpoints returns the Access-Control-Allow-Origin when only one origins is set - ''' - self._app.mod_opts['cors_origin'] = 'http://example.foo' + """ + self._app.mod_opts["cors_origin"] = "http://example.foo" # Example.foo is an authorized origin - headers = self.fetch('/', headers={'Origin': 'http://example.foo'}).headers + headers = self.fetch("/", headers={"Origin": "http://example.foo"}).headers self.assertEqual(headers["Access-Control-Allow-Origin"], "http://example.foo") # Example2.foo is not an authorized origin - headers = self.fetch('/', headers={'Origin': 'http://example2.foo'}).headers + headers = self.fetch("/", headers={"Origin": "http://example2.foo"}).headers self.assertEqual(headers.get("Access-Control-Allow-Origin"), None) def test_cors_origin_multiple(self): - ''' + """ Check that endpoints returns the Access-Control-Allow-Origin when multiple origins are set - ''' - self._app.mod_opts['cors_origin'] = ['http://example.foo', 'http://foo.example'] + """ + self._app.mod_opts["cors_origin"] = ["http://example.foo", "http://foo.example"] # Example.foo is an authorized origin - headers = self.fetch('/', headers={'Origin': 'http://example.foo'}).headers + headers = self.fetch("/", headers={"Origin": "http://example.foo"}).headers self.assertEqual(headers["Access-Control-Allow-Origin"], "http://example.foo") # Example2.foo is not an authorized origin - headers = self.fetch('/', headers={'Origin': 'http://example2.foo'}).headers + headers = self.fetch("/", headers={"Origin": "http://example2.foo"}).headers self.assertEqual(headers.get("Access-Control-Allow-Origin"), None) def test_cors_preflight_request(self): - ''' + """ Check that preflight request contains right headers - ''' - self._app.mod_opts['cors_origin'] = '*' + """ + self._app.mod_opts["cors_origin"] = "*" - request_headers = 'X-Auth-Token, accept, content-type' - preflight_headers = {'Access-Control-Request-Headers': request_headers, - 'Access-Control-Request-Method': 'GET'} + request_headers = "X-Auth-Token, accept, content-type" + preflight_headers = { + "Access-Control-Request-Headers": request_headers, + "Access-Control-Request-Method": "GET", + } - response = self.fetch('/', method='OPTIONS', headers=preflight_headers) + response = self.fetch("/", method="OPTIONS", headers=preflight_headers) headers = response.headers self.assertEqual(response.code, 204) - self.assertEqual(headers['Access-Control-Allow-Headers'], request_headers) - self.assertEqual(headers['Access-Control-Expose-Headers'], 'X-Auth-Token') - self.assertEqual(headers['Access-Control-Allow-Methods'], 'OPTIONS, GET, POST') + self.assertEqual(headers["Access-Control-Allow-Headers"], request_headers) + self.assertEqual(headers["Access-Control-Expose-Headers"], "X-Auth-Token") + self.assertEqual(headers["Access-Control-Allow-Methods"], "OPTIONS, GET, POST") self.assertEqual(response.code, 204) def test_cors_origin_url_with_arguments(self): - ''' + """ Check that preflight requests works with url with components like jobs or minions endpoints. - ''' - self._app.mod_opts['cors_origin'] = '*' + """ + self._app.mod_opts["cors_origin"] = "*" - request_headers = 'X-Auth-Token, accept, content-type' - preflight_headers = {'Access-Control-Request-Headers': request_headers, - 'Access-Control-Request-Method': 'GET'} - response = self.fetch('/1234567890', method='OPTIONS', - headers=preflight_headers) + request_headers = "X-Auth-Token, accept, content-type" + preflight_headers = { + "Access-Control-Request-Headers": request_headers, + "Access-Control-Request-Method": "GET", + } + response = self.fetch( + "/1234567890", method="OPTIONS", headers=preflight_headers + ) headers = response.headers self.assertEqual(response.code, 204) @@ -477,213 +554,258 @@ class TestBaseSaltAPIHandler(SaltnadoTestCase): class TestWebhookSaltHandler(SaltnadoTestCase): - def get_app(self): urls = [ - (r'/hook(/.*)?', saltnado.WebhookSaltAPIHandler), + (r"/hook(/.*)?", saltnado.WebhookSaltAPIHandler), ] return self.build_tornado_app(urls) def test_hook_can_handle_get_parameters(self): - with patch('salt.utils.event.get_event') as get_event: - with patch.dict(self._app.mod_opts, {'webhook_disable_auth': True}): + with patch("salt.utils.event.get_event") as get_event: + with patch.dict(self._app.mod_opts, {"webhook_disable_auth": True}): event = MagicMock() event.fire_event.return_value = True get_event.return_value = event - response = self.fetch('/hook/my_service/?param=1&param=2', - body=salt.utils.json.dumps({}), - method='POST', - headers={'Content-Type': self.content_type_map['json']}) + response = self.fetch( + "/hook/my_service/?param=1&param=2", + body=salt.utils.json.dumps({}), + method="POST", + headers={"Content-Type": self.content_type_map["json"]}, + ) self.assertEqual(response.code, 200, response.body) host = urlparse(response.effective_url).netloc event.fire_event.assert_called_once_with( - {'headers': {'Content-Length': '2', - 'Connection': 'close', - 'Content-Type': 'application/json', - 'Host': host, - 'Accept-Encoding': 'gzip'}, - 'post': {}, - 'get': {'param': ['1', '2']} - }, - 'salt/netapi/hook/my_service/', + { + "headers": { + "Content-Length": "2", + "Connection": "close", + "Content-Type": "application/json", + "Host": host, + "Accept-Encoding": "gzip", + }, + "post": {}, + "get": {"param": ["1", "2"]}, + }, + "salt/netapi/hook/my_service/", ) class TestSaltAuthHandler(SaltnadoTestCase): - def get_app(self): - urls = [('/login', saltnado.SaltAuthHandler)] + urls = [("/login", saltnado.SaltAuthHandler)] return self.build_tornado_app(urls) def test_get(self): - ''' + """ We don't allow gets, so assert we get 401s - ''' - response = self.fetch('/login') + """ + response = self.fetch("/login") self.assertEqual(response.code, 401) def test_login(self): - ''' + """ Test valid logins - ''' + """ # Test in form encoded - response = self.fetch('/login', - method='POST', - body=urlencode(self.auth_creds), - headers={'Content-Type': self.content_type_map['form']}) + response = self.fetch( + "/login", + method="POST", + body=urlencode(self.auth_creds), + headers={"Content-Type": self.content_type_map["form"]}, + ) - cookies = response.headers['Set-Cookie'] + cookies = response.headers["Set-Cookie"] self.assertEqual(response.code, 200) - response_obj = salt.utils.json.loads(response.body)['return'][0] - token = response_obj['token'] - self.assertIn('session_id={0}'.format(token), cookies) - self.assertEqual(sorted(response_obj['perms']), sorted(self.opts['external_auth']['auto'][self.auth_creds_dict['username']])) - self.assertIn('token', response_obj) # TODO: verify that its valid? - self.assertEqual(response_obj['user'], self.auth_creds_dict['username']) - self.assertEqual(response_obj['eauth'], self.auth_creds_dict['eauth']) + response_obj = salt.utils.json.loads(response.body)["return"][0] + token = response_obj["token"] + self.assertIn("session_id={0}".format(token), cookies) + self.assertEqual( + sorted(response_obj["perms"]), + sorted( + self.opts["external_auth"]["auto"][self.auth_creds_dict["username"]] + ), + ) + self.assertIn("token", response_obj) # TODO: verify that its valid? + self.assertEqual(response_obj["user"], self.auth_creds_dict["username"]) + self.assertEqual(response_obj["eauth"], self.auth_creds_dict["eauth"]) # Test in JSON - response = self.fetch('/login', - method='POST', - body=salt.utils.json.dumps(self.auth_creds_dict), - headers={'Content-Type': self.content_type_map['json']}) + response = self.fetch( + "/login", + method="POST", + body=salt.utils.json.dumps(self.auth_creds_dict), + headers={"Content-Type": self.content_type_map["json"]}, + ) - cookies = response.headers['Set-Cookie'] + cookies = response.headers["Set-Cookie"] self.assertEqual(response.code, 200) - response_obj = salt.utils.json.loads(response.body)['return'][0] - token = response_obj['token'] - self.assertIn('session_id={0}'.format(token), cookies) - self.assertEqual(sorted(response_obj['perms']), sorted(self.opts['external_auth']['auto'][self.auth_creds_dict['username']])) - self.assertIn('token', response_obj) # TODO: verify that its valid? - self.assertEqual(response_obj['user'], self.auth_creds_dict['username']) - self.assertEqual(response_obj['eauth'], self.auth_creds_dict['eauth']) + response_obj = salt.utils.json.loads(response.body)["return"][0] + token = response_obj["token"] + self.assertIn("session_id={0}".format(token), cookies) + self.assertEqual( + sorted(response_obj["perms"]), + sorted( + self.opts["external_auth"]["auto"][self.auth_creds_dict["username"]] + ), + ) + self.assertIn("token", response_obj) # TODO: verify that its valid? + self.assertEqual(response_obj["user"], self.auth_creds_dict["username"]) + self.assertEqual(response_obj["eauth"], self.auth_creds_dict["eauth"]) # Test in YAML - response = self.fetch('/login', - method='POST', - body=salt.utils.yaml.safe_dump(self.auth_creds_dict), - headers={'Content-Type': self.content_type_map['yaml']}) + response = self.fetch( + "/login", + method="POST", + body=salt.utils.yaml.safe_dump(self.auth_creds_dict), + headers={"Content-Type": self.content_type_map["yaml"]}, + ) - cookies = response.headers['Set-Cookie'] + cookies = response.headers["Set-Cookie"] self.assertEqual(response.code, 200) - response_obj = salt.utils.json.loads(response.body)['return'][0] - token = response_obj['token'] - self.assertIn('session_id={0}'.format(token), cookies) - self.assertEqual(sorted(response_obj['perms']), sorted(self.opts['external_auth']['auto'][self.auth_creds_dict['username']])) - self.assertIn('token', response_obj) # TODO: verify that its valid? - self.assertEqual(response_obj['user'], self.auth_creds_dict['username']) - self.assertEqual(response_obj['eauth'], self.auth_creds_dict['eauth']) + response_obj = salt.utils.json.loads(response.body)["return"][0] + token = response_obj["token"] + self.assertIn("session_id={0}".format(token), cookies) + self.assertEqual( + sorted(response_obj["perms"]), + sorted( + self.opts["external_auth"]["auto"][self.auth_creds_dict["username"]] + ), + ) + self.assertIn("token", response_obj) # TODO: verify that its valid? + self.assertEqual(response_obj["user"], self.auth_creds_dict["username"]) + self.assertEqual(response_obj["eauth"], self.auth_creds_dict["eauth"]) def test_login_missing_password(self): - ''' + """ Test logins with bad/missing passwords - ''' + """ bad_creds = [] for key, val in six.iteritems(self.auth_creds_dict): - if key == 'password': + if key == "password": continue bad_creds.append((key, val)) - response = self.fetch('/login', - method='POST', - body=urlencode(bad_creds), - headers={'Content-Type': self.content_type_map['form']}) + response = self.fetch( + "/login", + method="POST", + body=urlencode(bad_creds), + headers={"Content-Type": self.content_type_map["form"]}, + ) self.assertEqual(response.code, 400) def test_login_bad_creds(self): - ''' + """ Test logins with bad/missing passwords - ''' + """ bad_creds = [] for key, val in six.iteritems(self.auth_creds_dict): - if key == 'username': - val = val + 'foo' - if key == 'eauth': - val = 'sharedsecret' + if key == "username": + val = val + "foo" + if key == "eauth": + val = "sharedsecret" bad_creds.append((key, val)) - response = self.fetch('/login', - method='POST', - body=urlencode(bad_creds), - headers={'Content-Type': self.content_type_map['form']}) + response = self.fetch( + "/login", + method="POST", + body=urlencode(bad_creds), + headers={"Content-Type": self.content_type_map["form"]}, + ) self.assertEqual(response.code, 401) def test_login_invalid_data_structure(self): - ''' + """ Test logins with either list or string JSON payload - ''' - response = self.fetch('/login', - method='POST', - body=salt.utils.json.dumps(self.auth_creds), - headers={'Content-Type': self.content_type_map['form']}) + """ + response = self.fetch( + "/login", + method="POST", + body=salt.utils.json.dumps(self.auth_creds), + headers={"Content-Type": self.content_type_map["form"]}, + ) self.assertEqual(response.code, 400) - response = self.fetch('/login', - method='POST', - body=salt.utils.json.dumps(42), - headers={'Content-Type': self.content_type_map['form']}) + response = self.fetch( + "/login", + method="POST", + body=salt.utils.json.dumps(42), + headers={"Content-Type": self.content_type_map["form"]}, + ) self.assertEqual(response.code, 400) - response = self.fetch('/login', - method='POST', - body=salt.utils.json.dumps('mystring42'), - headers={'Content-Type': self.content_type_map['form']}) + response = self.fetch( + "/login", + method="POST", + body=salt.utils.json.dumps("mystring42"), + headers={"Content-Type": self.content_type_map["form"]}, + ) self.assertEqual(response.code, 400) class TestSaltRunHandler(SaltnadoTestCase): - def get_app(self): - urls = [('/run', saltnado.RunSaltAPIHandler)] + urls = [("/run", saltnado.RunSaltAPIHandler)] return self.build_tornado_app(urls) def test_authentication_exception_consistency(self): - ''' + """ Test consistency of authentication exception of each clients. - ''' - valid_response = {'return': ['Failed to authenticate']} + """ + valid_response = {"return": ["Failed to authenticate"]} - clients = ['local', 'local_async', 'runner', 'runner_async'] - request_lowstates = map(lambda client: {"client": client, - "tgt": "*", - "fun": "test.fib", - "arg": ["10"]}, - clients) + clients = ["local", "local_async", "runner", "runner_async"] + request_lowstates = map( + lambda client: { + "client": client, + "tgt": "*", + "fun": "test.fib", + "arg": ["10"], + }, + clients, + ) for request_lowstate in request_lowstates: - response = self.fetch('/run', - method='POST', - body=salt.utils.json.dumps(request_lowstate), - headers={'Content-Type': self.content_type_map['json']}) + response = self.fetch( + "/run", + method="POST", + body=salt.utils.json.dumps(request_lowstate), + headers={"Content-Type": self.content_type_map["json"]}, + ) self.assertEqual(valid_response, salt.utils.json.loads(response.body)) -@skipIf(not HAS_TORNADO, 'The tornado package needs to be installed') # pylint: disable=W0223 +@skipIf( + not HAS_TORNADO, "The tornado package needs to be installed" +) # pylint: disable=W0223 class TestWebsocketSaltAPIHandler(SaltnadoTestCase): - def get_app(self): opts = copy.deepcopy(self.opts) - opts.setdefault('rest_tornado', {})['websockets'] = True + opts.setdefault("rest_tornado", {})["websockets"] = True return rest_tornado.get_application(opts) @gen_test def test_websocket_handler_upgrade_to_websocket(self): - response = yield self.http_client.fetch(self.get_url('/login'), - method='POST', - body=urlencode(self.auth_creds), - headers={'Content-Type': self.content_type_map['form']}) - token = salt.utils.json.loads(self.decode_body(response).body)['return'][0]['token'] + response = yield self.http_client.fetch( + self.get_url("/login"), + method="POST", + body=urlencode(self.auth_creds), + headers={"Content-Type": self.content_type_map["form"]}, + ) + token = salt.utils.json.loads(self.decode_body(response).body)["return"][0][ + "token" + ] - url = 'ws://127.0.0.1:{0}/all_events/{1}'.format(self.get_http_port(), token) - request = HTTPRequest(url, headers={'Origin': 'http://example.com', - 'Host': 'example.com'}) + url = "ws://127.0.0.1:{0}/all_events/{1}".format(self.get_http_port(), token) + request = HTTPRequest( + url, headers={"Origin": "http://example.com", "Host": "example.com"} + ) ws = yield websocket_connect(request) - ws.write_message('websocket client ready') + ws.write_message("websocket client ready") ws.close() @gen_test @@ -691,11 +813,14 @@ class TestWebsocketSaltAPIHandler(SaltnadoTestCase): """ A bad token should returns a 401 during a websocket connect """ - token = 'A'*len(getattr(hashlib, self.opts.get('hash_type', 'md5'))().hexdigest()) + token = "A" * len( + getattr(hashlib, self.opts.get("hash_type", "md5"))().hexdigest() + ) - url = 'ws://127.0.0.1:{0}/all_events/{1}'.format(self.get_http_port(), token) - request = HTTPRequest(url, headers={'Origin': 'http://example.com', - 'Host': 'example.com'}) + url = "ws://127.0.0.1:{0}/all_events/{1}".format(self.get_http_port(), token) + request = HTTPRequest( + url, headers={"Origin": "http://example.com", "Host": "example.com"} + ) try: ws = yield websocket_connect(request) except HTTPError as error: @@ -703,42 +828,53 @@ class TestWebsocketSaltAPIHandler(SaltnadoTestCase): @gen_test def test_websocket_handler_cors_origin_wildcard(self): - self._app.mod_opts['cors_origin'] = '*' + self._app.mod_opts["cors_origin"] = "*" - response = yield self.http_client.fetch(self.get_url('/login'), - method='POST', - body=urlencode(self.auth_creds), - headers={'Content-Type': self.content_type_map['form']}) - token = salt.utils.json.loads(self.decode_body(response).body)['return'][0]['token'] + response = yield self.http_client.fetch( + self.get_url("/login"), + method="POST", + body=urlencode(self.auth_creds), + headers={"Content-Type": self.content_type_map["form"]}, + ) + token = salt.utils.json.loads(self.decode_body(response).body)["return"][0][ + "token" + ] - url = 'ws://127.0.0.1:{0}/all_events/{1}'.format(self.get_http_port(), token) - request = HTTPRequest(url, headers={'Origin': 'http://foo.bar', - 'Host': 'example.com'}) + url = "ws://127.0.0.1:{0}/all_events/{1}".format(self.get_http_port(), token) + request = HTTPRequest( + url, headers={"Origin": "http://foo.bar", "Host": "example.com"} + ) ws = yield websocket_connect(request) - ws.write_message('websocket client ready') + ws.write_message("websocket client ready") ws.close() @gen_test def test_cors_origin_single(self): - self._app.mod_opts['cors_origin'] = 'http://example.com' + self._app.mod_opts["cors_origin"] = "http://example.com" - response = yield self.http_client.fetch(self.get_url('/login'), - method='POST', - body=urlencode(self.auth_creds), - headers={'Content-Type': self.content_type_map['form']}) - token = salt.utils.json.loads(self.decode_body(response).body)['return'][0]['token'] - url = 'ws://127.0.0.1:{0}/all_events/{1}'.format(self.get_http_port(), token) + response = yield self.http_client.fetch( + self.get_url("/login"), + method="POST", + body=urlencode(self.auth_creds), + headers={"Content-Type": self.content_type_map["form"]}, + ) + token = salt.utils.json.loads(self.decode_body(response).body)["return"][0][ + "token" + ] + url = "ws://127.0.0.1:{0}/all_events/{1}".format(self.get_http_port(), token) # Example.com should works - request = HTTPRequest(url, headers={'Origin': 'http://example.com', - 'Host': 'example.com'}) + request = HTTPRequest( + url, headers={"Origin": "http://example.com", "Host": "example.com"} + ) ws = yield websocket_connect(request) - ws.write_message('websocket client ready') + ws.write_message("websocket client ready") ws.close() # But foo.bar not - request = HTTPRequest(url, headers={'Origin': 'http://foo.bar', - 'Host': 'example.com'}) + request = HTTPRequest( + url, headers={"Origin": "http://foo.bar", "Host": "example.com"} + ) try: ws = yield websocket_connect(request) except HTTPError as error: @@ -746,36 +882,42 @@ class TestWebsocketSaltAPIHandler(SaltnadoTestCase): @gen_test def test_cors_origin_multiple(self): - self._app.mod_opts['cors_origin'] = ['http://example.com', 'http://foo.bar'] + self._app.mod_opts["cors_origin"] = ["http://example.com", "http://foo.bar"] - response = yield self.http_client.fetch(self.get_url('/login'), - method='POST', - body=urlencode(self.auth_creds), - headers={'Content-Type': self.content_type_map['form']}) - token = salt.utils.json.loads(self.decode_body(response).body)['return'][0]['token'] - url = 'ws://127.0.0.1:{0}/all_events/{1}'.format(self.get_http_port(), token) + response = yield self.http_client.fetch( + self.get_url("/login"), + method="POST", + body=urlencode(self.auth_creds), + headers={"Content-Type": self.content_type_map["form"]}, + ) + token = salt.utils.json.loads(self.decode_body(response).body)["return"][0][ + "token" + ] + url = "ws://127.0.0.1:{0}/all_events/{1}".format(self.get_http_port(), token) # Example.com should works - request = HTTPRequest(url, headers={'Origin': 'http://example.com', - 'Host': 'example.com'}) + request = HTTPRequest( + url, headers={"Origin": "http://example.com", "Host": "example.com"} + ) ws = yield websocket_connect(request) - ws.write_message('websocket client ready') + ws.write_message("websocket client ready") ws.close() # Foo.bar too - request = HTTPRequest(url, headers={'Origin': 'http://foo.bar', - 'Host': 'example.com'}) + request = HTTPRequest( + url, headers={"Origin": "http://foo.bar", "Host": "example.com"} + ) ws = yield websocket_connect(request) - ws.write_message('websocket client ready') + ws.write_message("websocket client ready") ws.close() -@skipIf(not HAS_TORNADO, 'The tornado package needs to be installed') +@skipIf(not HAS_TORNADO, "The tornado package needs to be installed") class TestSaltnadoUtils(AsyncTestCase): def test_any_future(self): - ''' + """ Test that the Any Future does what we think it does - ''' + """ # create a few futures futures = [] for x in range(0, 3): @@ -788,7 +930,7 @@ class TestSaltnadoUtils(AsyncTestCase): self.assertIs(any_.done(), False) # finish one, lets see who finishes - futures[0].set_result('foo') + futures[0].set_result("foo") self.wait() self.assertIs(any_.done(), True) @@ -802,149 +944,159 @@ class TestSaltnadoUtils(AsyncTestCase): futures = futures[1:] # re-wait on some other futures any_ = saltnado.Any(futures) - futures[0].set_result('foo') + futures[0].set_result("foo") self.wait() self.assertIs(any_.done(), True) self.assertIs(futures[0].done(), True) self.assertIs(futures[1].done(), False) -@skipIf(not HAS_TORNADO, 'The tornado package needs to be installed') +@skipIf(not HAS_TORNADO, "The tornado package needs to be installed") class TestEventListener(AsyncTestCase): def setUp(self): - self.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + self.sock_dir = os.path.join(RUNTIME_VARS.TMP, "test-socks") if not os.path.exists(self.sock_dir): os.makedirs(self.sock_dir) self.addCleanup(shutil.rmtree, self.sock_dir, ignore_errors=True) super(TestEventListener, self).setUp() def test_simple(self): - ''' + """ Test getting a few events - ''' + """ with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir) - event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? - {'sock_dir': self.sock_dir, - 'transport': 'zeromq'}) + event_listener = saltnado.EventListener( + {}, # we don't use mod_opts, don't save? + {"sock_dir": self.sock_dir, "transport": "zeromq"}, + ) self._finished = False # fit to event_listener's behavior - event_future = event_listener.get_event(self, 'evt1', callback=self.stop) # get an event future - me.fire_event({'data': 'foo2'}, 'evt2') # fire an event we don't want - me.fire_event({'data': 'foo1'}, 'evt1') # fire an event we do want + event_future = event_listener.get_event( + self, "evt1", callback=self.stop + ) # get an event future + me.fire_event({"data": "foo2"}, "evt2") # fire an event we don't want + me.fire_event({"data": "foo1"}, "evt1") # fire an event we do want self.wait() # wait for the future # check that we got the event we wanted self.assertTrue(event_future.done()) - self.assertEqual(event_future.result()['tag'], 'evt1') - self.assertEqual(event_future.result()['data']['data'], 'foo1') + self.assertEqual(event_future.result()["tag"], "evt1") + self.assertEqual(event_future.result()["data"]["data"], "foo1") def test_set_event_handler(self): - ''' + """ Test subscribing events using set_event_handler - ''' + """ with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir) - event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? - {'sock_dir': self.sock_dir, - 'transport': 'zeromq'}) + event_listener = saltnado.EventListener( + {}, # we don't use mod_opts, don't save? + {"sock_dir": self.sock_dir, "transport": "zeromq"}, + ) self._finished = False # fit to event_listener's behavior - event_future = event_listener.get_event(self, - tag='evt', - callback=self.stop, - timeout=1, - ) # get an event future - me.fire_event({'data': 'foo'}, 'evt') # fire an event we do want + event_future = event_listener.get_event( + self, tag="evt", callback=self.stop, timeout=1, + ) # get an event future + me.fire_event({"data": "foo"}, "evt") # fire an event we do want self.wait() # check that we subscribed the event we wanted self.assertEqual(len(event_listener.timeout_map), 0) def test_timeout(self): - ''' + """ Make sure timeouts work correctly - ''' + """ with eventpublisher_process(self.sock_dir): - event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? - {'sock_dir': self.sock_dir, - 'transport': 'zeromq'}) + event_listener = saltnado.EventListener( + {}, # we don't use mod_opts, don't save? + {"sock_dir": self.sock_dir, "transport": "zeromq"}, + ) self._finished = False # fit to event_listener's behavior - event_future = event_listener.get_event(self, - tag='evt1', - callback=self.stop, - timeout=1, - ) # get an event future + event_future = event_listener.get_event( + self, tag="evt1", callback=self.stop, timeout=1, + ) # get an event future self.wait() self.assertTrue(event_future.done()) with self.assertRaises(saltnado.TimeoutException): event_future.result() def test_clean_by_request(self): - ''' + """ Make sure the method clean_by_request clean up every related data in EventListener request_future_1 : will be timeout-ed by clean_by_request(self) request_future_2 : will be finished by me.fire_event ... dummy_request_future_1 : will be finished by me.fire_event ... dummy_request_future_2 : will be timeout-ed by clean-by_request(dummy_request) - ''' + """ + class DummyRequest(object): - ''' + """ Dummy request object to simulate the request object - ''' + """ + @property def _finished(self): - ''' + """ Simulate _finished of the request object - ''' + """ return False # Inner functions never permit modifying primitive values directly cnt = [0] def stop(): - ''' + """ To realize the scenario of this test, define a custom stop method to call self.stop after finished two events. - ''' + """ cnt[0] += 1 if cnt[0] == 2: self.stop() with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir) - event_listener = saltnado.EventListener({}, # we don't use mod_opts, don't save? - {'sock_dir': self.sock_dir, - 'transport': 'zeromq'}) + event_listener = saltnado.EventListener( + {}, # we don't use mod_opts, don't save? + {"sock_dir": self.sock_dir, "transport": "zeromq"}, + ) self.assertEqual(0, len(event_listener.tag_map)) self.assertEqual(0, len(event_listener.request_map)) self._finished = False # fit to event_listener's behavior dummy_request = DummyRequest() - request_future_1 = event_listener.get_event(self, tag='evt1') - request_future_2 = event_listener.get_event(self, tag='evt2', callback=lambda f: stop()) - dummy_request_future_1 = event_listener.get_event(dummy_request, tag='evt3', callback=lambda f: stop()) - dummy_request_future_2 = event_listener.get_event(dummy_request, timeout=10, tag='evt4') + request_future_1 = event_listener.get_event(self, tag="evt1") + request_future_2 = event_listener.get_event( + self, tag="evt2", callback=lambda f: stop() + ) + dummy_request_future_1 = event_listener.get_event( + dummy_request, tag="evt3", callback=lambda f: stop() + ) + dummy_request_future_2 = event_listener.get_event( + dummy_request, timeout=10, tag="evt4" + ) self.assertEqual(4, len(event_listener.tag_map)) self.assertEqual(2, len(event_listener.request_map)) - me.fire_event({'data': 'foo2'}, 'evt2') - me.fire_event({'data': 'foo3'}, 'evt3') + me.fire_event({"data": "foo2"}, "evt2") + me.fire_event({"data": "foo3"}, "evt3") self.wait() event_listener.clean_by_request(self) - me.fire_event({'data': 'foo1'}, 'evt1') + me.fire_event({"data": "foo1"}, "evt1") self.assertTrue(request_future_1.done()) with self.assertRaises(saltnado.TimeoutException): request_future_1.result() self.assertTrue(request_future_2.done()) - self.assertEqual(request_future_2.result()['tag'], 'evt2') - self.assertEqual(request_future_2.result()['data']['data'], 'foo2') + self.assertEqual(request_future_2.result()["tag"], "evt2") + self.assertEqual(request_future_2.result()["data"]["data"], "foo2") self.assertTrue(dummy_request_future_1.done()) - self.assertEqual(dummy_request_future_1.result()['tag'], 'evt3') - self.assertEqual(dummy_request_future_1.result()['data']['data'], 'foo3') + self.assertEqual(dummy_request_future_1.result()["tag"], "evt3") + self.assertEqual(dummy_request_future_1.result()["data"]["data"], "foo3") self.assertFalse(dummy_request_future_2.done()) diff --git a/tests/unit/output/test_highstate.py b/tests/unit/output/test_highstate.py index 15303c07d59..b34aa6a322c 100644 --- a/tests/unit/output/test_highstate.py +++ b/tests/unit/output/test_highstate.py @@ -1,201 +1,228 @@ # -*- coding: utf-8 -*- -''' +""" unittests for highstate outputter -''' +""" # Import Python Libs from __future__ import absolute_import +import salt.output.highstate as highstate + +# Import Salt Libs +import salt.utils.stringutils + +# Import 3rd-party libs +from salt.ext import six + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -# Import Salt Libs -import salt.utils.stringutils -import salt.output.highstate as highstate - -# Import 3rd-party libs -from salt.ext import six - class JsonTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.output.highstate - ''' + """ + def setup_loader_modules(self): return { highstate: { - '__opts__': { - 'extension_modules': '', - 'optimization_order': [0, 1, 2], - 'color': False, + "__opts__": { + "extension_modules": "", + "optimization_order": [0, 1, 2], + "color": False, } } } def setUp(self): self.data = { - 'data': { - 'master': { - 'salt_|-call_sleep_state_|-call_sleep_state_|-state': { - '__id__': 'call_sleep_state', - '__jid__': '20170418153529810135', - '__run_num__': 0, - '__sls__': 'orch.simple', - 'changes': { - 'out': 'highstate', - 'ret': { - 'minion': { - 'module_|-simple-ping_|-test.ping_|-run': { - '__id__': 'simple-ping', - '__run_num__': 0, - '__sls__': 'simple-ping', - 'changes': {'ret': True}, - 'comment': 'Module function test.ping executed', - 'duration': 56.179, - 'name': 'test.ping', - 'result': True, - 'start_time': '15:35:31.282099' + "data": { + "master": { + "salt_|-call_sleep_state_|-call_sleep_state_|-state": { + "__id__": "call_sleep_state", + "__jid__": "20170418153529810135", + "__run_num__": 0, + "__sls__": "orch.simple", + "changes": { + "out": "highstate", + "ret": { + "minion": { + "module_|-simple-ping_|-test.ping_|-run": { + "__id__": "simple-ping", + "__run_num__": 0, + "__sls__": "simple-ping", + "changes": {"ret": True}, + "comment": "Module function test.ping executed", + "duration": 56.179, + "name": "test.ping", + "result": True, + "start_time": "15:35:31.282099", } }, - 'sub_minion': { - 'module_|-simple-ping_|-test.ping_|-run': { - '__id__': 'simple-ping', - '__run_num__': 0, - '__sls__': 'simple-ping', - 'changes': {'ret': True}, - 'comment': 'Module function test.ping executed', - 'duration': 54.103, - 'name': 'test.ping', - 'result': True, - 'start_time': '15:35:31.005606' + "sub_minion": { + "module_|-simple-ping_|-test.ping_|-run": { + "__id__": "simple-ping", + "__run_num__": 0, + "__sls__": "simple-ping", + "changes": {"ret": True}, + "comment": "Module function test.ping executed", + "duration": 54.103, + "name": "test.ping", + "result": True, + "start_time": "15:35:31.005606", } - } - } + }, + }, }, - 'comment': 'States ran successfully. Updating sub_minion, minion.', - 'duration': 1638.047, - 'name': 'call_sleep_state', - 'result': True, - 'start_time': '15:35:29.762657' - } + "comment": "States ran successfully. Updating sub_minion, minion.", + "duration": 1638.047, + "name": "call_sleep_state", + "result": True, + "start_time": "15:35:29.762657", + }, + "salt_|-cmd_run_example_|-cmd.run_|-function": { + "__id__": "cmd_run_example", + "__jid__": "20200411195112288850", + "__run_num__": 1, + "__sls__": "orch.simple", + "changes": { + "out": "highstate", + "ret": {"minion": "file1\nfile2\nfile3"}, + }, + "comment": "Function ran successfully. Function cmd.run ran on minion.", + "duration": 412.397, + "name": "cmd.run", + "result": True, + "start_time": "21:51:12.185868", + }, } }, - 'outputter': 'highstate', - 'retcode': 0 + "outputter": "highstate", + "retcode": 0, } - self.addCleanup(delattr, self, 'data') + self.addCleanup(delattr, self, "data") def test_default_output(self): ret = highstate.output(self.data) - self.assertIn('Succeeded: 1 (changed=1)', ret) - self.assertIn('Failed: 0', ret) - self.assertIn('Total states run: 1', ret) + self.assertIn("Succeeded: 1 (changed=1)", ret) + self.assertIn("Failed: 0", ret) + self.assertIn("Total states run: 1", ret) + self.assertIn(" file2", ret) def test_output_comment_is_not_unicode(self): entry = None - for key in ('data', 'master', 'salt_|-call_sleep_state_|-call_sleep_state_|-state', - 'changes', 'ret', 'minion', 'module_|-simple-ping_|-test.ping_|-run'): + for key in ( + "data", + "master", + "salt_|-call_sleep_state_|-call_sleep_state_|-state", + "changes", + "ret", + "minion", + "module_|-simple-ping_|-test.ping_|-run", + ): if entry is None: entry = self.data[key] continue entry = entry[key] if six.PY2: - entry['comment'] = salt.utils.stringutils.to_unicode(entry['comment']) + entry["comment"] = salt.utils.stringutils.to_unicode(entry["comment"]) else: - entry['comment'] = salt.utils.stringutils.to_bytes(entry['comment']) + entry["comment"] = salt.utils.stringutils.to_bytes(entry["comment"]) ret = highstate.output(self.data) - self.assertIn('Succeeded: 1 (changed=1)', ret) - self.assertIn('Failed: 0', ret) - self.assertIn('Total states run: 1', ret) + self.assertIn("Succeeded: 1 (changed=1)", ret) + self.assertIn("Failed: 0", ret) + self.assertIn("Total states run: 1", ret) + self.assertIn(" file2", ret) # this should all pass the above tests class JsonNestedTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for nested salt.output.highstate (ie orchestrations calling other orchs) - ''' + """ + def setup_loader_modules(self): return { highstate: { - '__opts__': { - 'extension_modules': '', - 'color': False, - 'optimization_order': [0, 1, 2], + "__opts__": { + "extension_modules": "", + "color": False, + "optimization_order": [0, 1, 2], } } } def setUp(self): self.data = { - 'outputter': 'highstate', - 'data': { - 'local_master': { - 'salt_|-nested_|-state.orchestrate_|-runner': { - 'comment': 'Runner function \'state.orchestrate\' executed.', - 'name': 'state.orchestrate', - '__orchestration__': True, - 'start_time': '09:22:53.158742', - 'result': True, - 'duration': 980.694, - '__run_num__': 0, - '__jid__': '20180326092253538853', - '__sls__': 'orch.test.nested', - 'changes': { - 'return': { - 'outputter': 'highstate', - 'data': { - 'local_master': { - 'test_|-always-passes-with-changes_|-oinaosf_|-succeed_with_changes': { - 'comment': 'Success!', - 'name': 'oinaosf', - 'start_time': '09:22:54.128415', - 'result': True, - 'duration': 0.437, - '__run_num__': 0, - '__sls__': 'orch.test.changes', - 'changes': { - 'testing': { - 'new': 'Something pretended to change', - 'old': 'Unchanged' + "outputter": "highstate", + "data": { + "local_master": { + "salt_|-nested_|-state.orchestrate_|-runner": { + "comment": "Runner function 'state.orchestrate' executed.", + "name": "state.orchestrate", + "__orchestration__": True, + "start_time": "09:22:53.158742", + "result": True, + "duration": 980.694, + "__run_num__": 0, + "__jid__": "20180326092253538853", + "__sls__": "orch.test.nested", + "changes": { + "return": { + "outputter": "highstate", + "data": { + "local_master": { + "test_|-always-passes-with-changes_|-oinaosf_|-succeed_with_changes": { + "comment": "Success!", + "name": "oinaosf", + "start_time": "09:22:54.128415", + "result": True, + "duration": 0.437, + "__run_num__": 0, + "__sls__": "orch.test.changes", + "changes": { + "testing": { + "new": "Something pretended to change", + "old": "Unchanged", } }, - '__id__': 'always-passes-with-changes' + "__id__": "always-passes-with-changes", + }, + "test_|-always-passes_|-fasdfasddfasdfoo_|-succeed_without_changes": { + "comment": "Success!", + "name": "fasdfasddfasdfoo", + "start_time": "09:22:54.128986", + "result": True, + "duration": 0.25, + "__run_num__": 1, + "__sls__": "orch.test.changes", + "changes": {}, + "__id__": "always-passes", }, - 'test_|-always-passes_|-fasdfasddfasdfoo_|-succeed_without_changes': { - 'comment': 'Success!', - 'name': 'fasdfasddfasdfoo', - 'start_time': '09:22:54.128986', - 'result': True, - 'duration': 0.25, - '__run_num__': 1, - '__sls__': 'orch.test.changes', - 'changes': {}, - '__id__': 'always-passes' - } } }, - 'retcode': 0 + "retcode": 0, } }, - '__id__': 'nested' + "__id__": "nested", } } }, - 'retcode': 0 + "retcode": 0, } - self.addCleanup(delattr, self, 'data') + self.addCleanup(delattr, self, "data") def test_nested_output(self): ret = highstate.output(self.data) - self.assertIn('Succeeded: 1 (changed=1)', ret) - self.assertIn('Failed: 0', ret) - self.assertIn('Total states run: 1', ret) + self.assertIn("Succeeded: 1 (changed=1)", ret) + self.assertIn("Failed: 0", ret) + self.assertIn("Total states run: 1", ret) # the whitespace is relevant in this case, it is testing that it is nested - self.assertIn(' ID: always-passes-with-changes', ret) - self.assertIn(' Started: 09:22:54.128415', ret) - self.assertIn(' Succeeded: 2 (changed=1)', ret) - self.assertIn(' Failed: 0', ret) - self.assertIn(' Total states run: 2', ret) + self.assertIn(" ID: always-passes-with-changes", ret) + self.assertIn(" Started: 09:22:54.128415", ret) + self.assertIn(" Succeeded: 2 (changed=1)", ret) + self.assertIn(" Failed: 0", ret) + self.assertIn(" Total states run: 2", ret) diff --git a/tests/unit/output/test_json_out.py b/tests/unit/output/test_json_out.py index 9b218c9240c..fc40edcc130 100644 --- a/tests/unit/output/test_json_out.py +++ b/tests/unit/output/test_json_out.py @@ -1,32 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" unittests for json outputter -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch - # Import Salt Libs import salt.output.json_out as json_out import salt.utils.stringutils from salt.ext import six +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class JsonTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.output.json_out - ''' + """ + def setup_loader_modules(self): return {json_out: {}} def setUp(self): - self.data = {'test': 'two', 'example': 'one'} - self.addCleanup(delattr, self, 'data') + self.data = {"test": "two", "example": "one"} + self.addCleanup(delattr, self, "data") def test_default_output(self): ret = json_out.output(self.data) @@ -34,37 +35,39 @@ class JsonTestCase(TestCase, LoaderModuleMockMixin): self.assertIn('"example": "one"', ret) def test_pretty_output(self): - with patch.dict(json_out.__opts__, {'output_indent': 'pretty'}): + with patch.dict(json_out.__opts__, {"output_indent": "pretty"}): ret = json_out.output(self.data) self.assertIn('"test": "two"', ret) self.assertIn('"example": "one"', ret) def test_indent_output(self): - with patch.dict(json_out.__opts__, {'output_indent': 2}): + with patch.dict(json_out.__opts__, {"output_indent": 2}): ret = json_out.output(self.data) self.assertIn('"test": "two"', ret) self.assertIn('"example": "one"', ret) def test_negative_zero_output(self): - with patch.dict(json_out.__opts__, {'output_indent': 0}): + with patch.dict(json_out.__opts__, {"output_indent": 0}): ret = json_out.output(self.data) self.assertIn('"test": "two"', ret) self.assertIn('"example": "one"', ret) def test_negative_int_output(self): - with patch.dict(json_out.__opts__, {'output_indent': -1}): + with patch.dict(json_out.__opts__, {"output_indent": -1}): ret = json_out.output(self.data) self.assertIn('"test": "two"', ret) self.assertIn('"example": "one"', ret) def test_unicode_output(self): - with patch.dict(json_out.__opts__, {'output_indent': 'pretty'}): - decoded = {'test': 'Д', 'example': 'one'} - encoded = {'test': salt.utils.stringutils.to_str('Д'), 'example': 'one'} + with patch.dict(json_out.__opts__, {"output_indent": "pretty"}): + decoded = {"test": "Д", "example": "one"} + encoded = {"test": salt.utils.stringutils.to_str("Д"), "example": "one"} # json.dumps on Python 2 adds a space before a newline while in the # process of dumping a dictionary. if six.PY2: - expected = salt.utils.stringutils.to_str('{\n "example": "one", \n "test": "Д"\n}') + expected = salt.utils.stringutils.to_str( + '{\n "example": "one", \n "test": "Д"\n}' + ) else: expected = '{\n "example": "one",\n "test": "Д"\n}' self.assertEqual(json_out.output(decoded), expected) diff --git a/tests/unit/output/test_nested.py b/tests/unit/output/test_nested.py index 6a7a43a77f3..ac24f72ea00 100644 --- a/tests/unit/output/test_nested.py +++ b/tests/unit/output/test_nested.py @@ -1,53 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for the Nested outputter -''' +""" # Import Python Libs from __future__ import absolute_import +# Import Salt Libs +import salt.output.nested as nested + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -# Import Salt Libs -import salt.output.nested as nested - class NestedOutputterTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.output.nested - ''' + """ + def setup_loader_modules(self): - return { - nested: { - '__opts__': { - 'extension_modules': '', - 'color': True - } - } - } + return {nested: {"__opts__": {"extension_modules": "", "color": True}}} def setUp(self): # The example from the documentation for the test.arg execution function # Same function from the highstate outputter self.data = { - 'local': { - 'args': (1, 'two', 3.1), - 'kwargs': { - u'__pub_pid': 25938, - 'wow': { - 'a': 1, - 'b': 'hello' - }, - u'__pub_fun': 'test.arg', - u'__pub_jid': '20171207105927331329', - u'__pub_tgt': 'salt-call', - 'txt': 'hello' - } + "local": { + "args": (1, "two", 3.1), + "kwargs": { + u"__pub_pid": 25938, + "wow": {"a": 1, "b": "hello"}, + u"__pub_fun": "test.arg", + u"__pub_jid": "20171207105927331329", + u"__pub_tgt": "salt-call", + "txt": "hello", + }, } } - self.addCleanup(delattr, self, 'data') + self.addCleanup(delattr, self, "data") def test_output_with_colors(self): # Should look exacly like that, with the default color scheme: @@ -77,17 +68,17 @@ class NestedOutputterTestCase(TestCase, LoaderModuleMockMixin): # b: # hello expected_output_str = ( - '\x1b[0;36mlocal\x1b[0;0m:\n \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36margs\x1b[0;0m:\n' - ' \x1b[0;1;33m- 1\x1b[0;0m\n \x1b[0;32m- two\x1b[0;0m\n \x1b[0;1;33m- 3.1\x1b[0;0m\n' - ' \x1b[0;36mkwargs\x1b[0;0m:\n \x1b[0;36m----------\x1b[0;0m\n' - ' \x1b[0;36m__pub_fun\x1b[0;0m:\n \x1b[0;32mtest.arg\x1b[0;0m\n' - ' \x1b[0;36m__pub_jid\x1b[0;0m:\n \x1b[0;32m20171207105927331329\x1b[0;0m\n' - ' \x1b[0;36m__pub_pid\x1b[0;0m:\n \x1b[0;1;33m25938\x1b[0;0m\n' - ' \x1b[0;36m__pub_tgt\x1b[0;0m:\n \x1b[0;32msalt-call\x1b[0;0m\n' - ' \x1b[0;36mtxt\x1b[0;0m:\n \x1b[0;32mhello\x1b[0;0m\n \x1b[0;36mwow\x1b[0;0m:\n' - ' \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36ma\x1b[0;0m:\n' - ' \x1b[0;1;33m1\x1b[0;0m\n \x1b[0;36mb\x1b[0;0m:\n' - ' \x1b[0;32mhello\x1b[0;0m' + "\x1b[0;36mlocal\x1b[0;0m:\n \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36margs\x1b[0;0m:\n" + " \x1b[0;1;33m- 1\x1b[0;0m\n \x1b[0;32m- two\x1b[0;0m\n \x1b[0;1;33m- 3.1\x1b[0;0m\n" + " \x1b[0;36mkwargs\x1b[0;0m:\n \x1b[0;36m----------\x1b[0;0m\n" + " \x1b[0;36m__pub_fun\x1b[0;0m:\n \x1b[0;32mtest.arg\x1b[0;0m\n" + " \x1b[0;36m__pub_jid\x1b[0;0m:\n \x1b[0;32m20171207105927331329\x1b[0;0m\n" + " \x1b[0;36m__pub_pid\x1b[0;0m:\n \x1b[0;1;33m25938\x1b[0;0m\n" + " \x1b[0;36m__pub_tgt\x1b[0;0m:\n \x1b[0;32msalt-call\x1b[0;0m\n" + " \x1b[0;36mtxt\x1b[0;0m:\n \x1b[0;32mhello\x1b[0;0m\n \x1b[0;36mwow\x1b[0;0m:\n" + " \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36ma\x1b[0;0m:\n" + " \x1b[0;1;33m1\x1b[0;0m\n \x1b[0;36mb\x1b[0;0m:\n" + " \x1b[0;32mhello\x1b[0;0m" ) ret = nested.output(self.data) self.assertEqual(ret, expected_output_str) @@ -96,17 +87,17 @@ class NestedOutputterTestCase(TestCase, LoaderModuleMockMixin): # Non-zero retcode should change the colors # Same output format as above, just different colors expected_output_str = ( - '\x1b[0;31mlocal\x1b[0;0m:\n \x1b[0;31m----------\x1b[0;0m\n \x1b[0;31margs\x1b[0;0m:\n' - ' \x1b[0;1;33m- 1\x1b[0;0m\n \x1b[0;32m- two\x1b[0;0m\n \x1b[0;1;33m- 3.1\x1b[0;0m\n' - ' \x1b[0;31mkwargs\x1b[0;0m:\n \x1b[0;31m----------\x1b[0;0m\n' - ' \x1b[0;31m__pub_fun\x1b[0;0m:\n \x1b[0;32mtest.arg\x1b[0;0m\n' - ' \x1b[0;31m__pub_jid\x1b[0;0m:\n \x1b[0;32m20171207105927331329\x1b[0;0m\n' - ' \x1b[0;31m__pub_pid\x1b[0;0m:\n \x1b[0;1;33m25938\x1b[0;0m\n' - ' \x1b[0;31m__pub_tgt\x1b[0;0m:\n \x1b[0;32msalt-call\x1b[0;0m\n' - ' \x1b[0;31mtxt\x1b[0;0m:\n \x1b[0;32mhello\x1b[0;0m\n \x1b[0;31mwow\x1b[0;0m:\n' - ' \x1b[0;31m----------\x1b[0;0m\n \x1b[0;31ma\x1b[0;0m:\n' - ' \x1b[0;1;33m1\x1b[0;0m\n \x1b[0;31mb\x1b[0;0m:\n' - ' \x1b[0;32mhello\x1b[0;0m' + "\x1b[0;31mlocal\x1b[0;0m:\n \x1b[0;31m----------\x1b[0;0m\n \x1b[0;31margs\x1b[0;0m:\n" + " \x1b[0;1;33m- 1\x1b[0;0m\n \x1b[0;32m- two\x1b[0;0m\n \x1b[0;1;33m- 3.1\x1b[0;0m\n" + " \x1b[0;31mkwargs\x1b[0;0m:\n \x1b[0;31m----------\x1b[0;0m\n" + " \x1b[0;31m__pub_fun\x1b[0;0m:\n \x1b[0;32mtest.arg\x1b[0;0m\n" + " \x1b[0;31m__pub_jid\x1b[0;0m:\n \x1b[0;32m20171207105927331329\x1b[0;0m\n" + " \x1b[0;31m__pub_pid\x1b[0;0m:\n \x1b[0;1;33m25938\x1b[0;0m\n" + " \x1b[0;31m__pub_tgt\x1b[0;0m:\n \x1b[0;32msalt-call\x1b[0;0m\n" + " \x1b[0;31mtxt\x1b[0;0m:\n \x1b[0;32mhello\x1b[0;0m\n \x1b[0;31mwow\x1b[0;0m:\n" + " \x1b[0;31m----------\x1b[0;0m\n \x1b[0;31ma\x1b[0;0m:\n" + " \x1b[0;1;33m1\x1b[0;0m\n \x1b[0;31mb\x1b[0;0m:\n" + " \x1b[0;32mhello\x1b[0;0m" ) # You can notice that in test_output_with_colors the color code is \x1b[0;36m, i.e., GREEN, # while here the color code is \x1b[0;31m, i.e., RED (failure) @@ -117,18 +108,18 @@ class NestedOutputterTestCase(TestCase, LoaderModuleMockMixin): # Everything must be indented by exactly two spaces # (using nested_indent=2 sent to nested.output as kwarg) expected_output_str = ( - ' \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36mlocal\x1b[0;0m:\n \x1b[0;36m----------\x1b[0;0m\n' - ' \x1b[0;36margs\x1b[0;0m:\n \x1b[0;1;33m- 1\x1b[0;0m\n \x1b[0;32m- two\x1b[0;0m\n' - ' \x1b[0;1;33m- 3.1\x1b[0;0m\n \x1b[0;36mkwargs\x1b[0;0m:\n' - ' \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36m__pub_fun\x1b[0;0m:\n' - ' \x1b[0;32mtest.arg\x1b[0;0m\n \x1b[0;36m__pub_jid\x1b[0;0m:\n' - ' \x1b[0;32m20171207105927331329\x1b[0;0m\n \x1b[0;36m__pub_pid\x1b[0;0m:\n' - ' \x1b[0;1;33m25938\x1b[0;0m\n \x1b[0;36m__pub_tgt\x1b[0;0m:\n' - ' \x1b[0;32msalt-call\x1b[0;0m\n \x1b[0;36mtxt\x1b[0;0m:\n' - ' \x1b[0;32mhello\x1b[0;0m\n \x1b[0;36mwow\x1b[0;0m:\n' - ' \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36ma\x1b[0;0m:\n' - ' \x1b[0;1;33m1\x1b[0;0m\n \x1b[0;36mb\x1b[0;0m:\n' - ' \x1b[0;32mhello\x1b[0;0m' + " \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36mlocal\x1b[0;0m:\n \x1b[0;36m----------\x1b[0;0m\n" + " \x1b[0;36margs\x1b[0;0m:\n \x1b[0;1;33m- 1\x1b[0;0m\n \x1b[0;32m- two\x1b[0;0m\n" + " \x1b[0;1;33m- 3.1\x1b[0;0m\n \x1b[0;36mkwargs\x1b[0;0m:\n" + " \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36m__pub_fun\x1b[0;0m:\n" + " \x1b[0;32mtest.arg\x1b[0;0m\n \x1b[0;36m__pub_jid\x1b[0;0m:\n" + " \x1b[0;32m20171207105927331329\x1b[0;0m\n \x1b[0;36m__pub_pid\x1b[0;0m:\n" + " \x1b[0;1;33m25938\x1b[0;0m\n \x1b[0;36m__pub_tgt\x1b[0;0m:\n" + " \x1b[0;32msalt-call\x1b[0;0m\n \x1b[0;36mtxt\x1b[0;0m:\n" + " \x1b[0;32mhello\x1b[0;0m\n \x1b[0;36mwow\x1b[0;0m:\n" + " \x1b[0;36m----------\x1b[0;0m\n \x1b[0;36ma\x1b[0;0m:\n" + " \x1b[0;1;33m1\x1b[0;0m\n \x1b[0;36mb\x1b[0;0m:\n" + " \x1b[0;32mhello\x1b[0;0m" ) ret = nested.output(self.data, nested_indent=2) self.assertEqual(ret, expected_output_str) diff --git a/tests/unit/output/test_table_out.py b/tests/unit/output/test_table_out.py index d1875291705..a31d8f51834 100644 --- a/tests/unit/output/test_table_out.py +++ b/tests/unit/output/test_table_out.py @@ -1,24 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" unittests for table outputter -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase - # Import Salt Libs import salt.output.table_out as table_out import salt.utils.stringutils +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase + class TableTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.output.table_out - ''' + """ + def setup_loader_modules(self): return {table_out: {}} @@ -28,21 +29,24 @@ class TableTestCase(TestCase, LoaderModuleMockMixin): # salt.utils.stringutils.to_str and the latter by simply using a string # literal. data = [ - {'Food': salt.utils.stringutils.to_str('яйца, бекон, колбаса и спам'), - 'Price': 5.99}, - {'Food': 'спам, спам, спам, яйца и спам', - 'Price': 3.99}, + { + "Food": salt.utils.stringutils.to_str("яйца, бекон, колбаса и спам"), + "Price": 5.99, + }, + {"Food": "спам, спам, спам, яйца и спам", "Price": 3.99}, ] def test_output(self): ret = table_out.output(self.data) self.assertEqual( ret, - (' -----------------------------------------\n' - ' | Food | Price |\n' - ' -----------------------------------------\n' - ' | яйца, бекон, колбаса и спам | 5.99 |\n' - ' -----------------------------------------\n' - ' | спам, спам, спам, яйца и спам | 3.99 |\n' - ' -----------------------------------------') + ( + " -----------------------------------------\n" + " | Food | Price |\n" + " -----------------------------------------\n" + " | яйца, бекон, колбаса и спам | 5.99 |\n" + " -----------------------------------------\n" + " | спам, спам, спам, яйца и спам | 3.99 |\n" + " -----------------------------------------" + ), ) diff --git a/tests/unit/output/test_yaml_out.py b/tests/unit/output/test_yaml_out.py index d075444f1da..4aa818a99c4 100644 --- a/tests/unit/output/test_yaml_out.py +++ b/tests/unit/output/test_yaml_out.py @@ -1,38 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" unittests for yaml outputter -''' +""" # Import Python Libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch - # Import Salt Libs import salt.output.yaml_out as yaml +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class YamlTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.output.json_out - ''' + """ + def setup_loader_modules(self): return {yaml: {}} def setUp(self): - self.data = {'test': 'two', 'example': 'one'} - self.addCleanup(delattr, self, 'data') + self.data = {"test": "two", "example": "one"} + self.addCleanup(delattr, self, "data") def test_default_output(self): ret = yaml.output(self.data) - expect = 'example: one\ntest: two\n' + expect = "example: one\ntest: two\n" self.assertEqual(expect, ret) def test_negative_int_output(self): - with patch.dict(yaml.__opts__, {'output_indent': -1}): + with patch.dict(yaml.__opts__, {"output_indent": -1}): ret = yaml.output(self.data) - expect = '{example: one, test: two}\n' + expect = "{example: one, test: two}\n" self.assertEqual(expect, ret) diff --git a/tests/unit/pillar/test_consul_pillar.py b/tests/unit/pillar/test_consul_pillar.py index c4e73c1814c..ecb8c1385ac 100644 --- a/tests/unit/pillar/test_consul_pillar.py +++ b/tests/unit/pillar/test_consul_pillar.py @@ -3,102 +3,168 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.pillar.consul_pillar as consul_pillar # Import 3rd-party libs from salt.ext import six -OPTS = {'consul_config': {'consul.port': 8500, 'consul.host': '172.17.0.15'}} +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + +OPTS = {"consul_config": {"consul.port": 8500, "consul.host": "172.17.0.15"}} PILLAR_DATA = [ - {'Value': '/path/to/certs/testsite1.crt', 'Key': 'test-shared/sites/testsite1/ssl/certs/SSLCertificateFile'}, - {'Value': '/path/to/certs/testsite1.key', 'Key': 'test-shared/sites/testsite1/ssl/certs/SSLCertificateKeyFile'}, - {'Value': None, 'Key': 'test-shared/sites/testsite1/ssl/certs/'}, - {'Value': 'True', 'Key': 'test-shared/sites/testsite1/ssl/force'}, - {'Value': None, 'Key': 'test-shared/sites/testsite1/ssl/'}, - {'Value': 'salt://sites/testsite1.tmpl', 'Key': 'test-shared/sites/testsite1/template'}, - {'Value': 'test.example.com', 'Key': 'test-shared/sites/testsite1/uri'}, - {'Value': None, 'Key': 'test-shared/sites/testsite1/'}, - {'Value': None, 'Key': 'test-shared/sites/'}, - {'Value': 'Test User', 'Key': 'test-shared/user/full_name'}, - {'Value': 'adm\nwww-data\nmlocate', 'Key': 'test-shared/user/groups'}, - {'Value': '"adm\nwww-data\nmlocate"', 'Key': 'test-shared/user/dontsplit'}, - {'Value': 'yaml:\n key: value\n', 'Key': 'test-shared/user/dontexpand'}, - {'Value': None, 'Key': 'test-shared/user/blankvalue'}, - {'Value': 'test', 'Key': 'test-shared/user/login'}, - {'Value': None, 'Key': 'test-shared/user/'} + { + "Value": "/path/to/certs/testsite1.crt", + "Key": "test-shared/sites/testsite1/ssl/certs/SSLCertificateFile", + }, + { + "Value": "/path/to/certs/testsite1.key", + "Key": "test-shared/sites/testsite1/ssl/certs/SSLCertificateKeyFile", + }, + {"Value": None, "Key": "test-shared/sites/testsite1/ssl/certs/"}, + {"Value": "True", "Key": "test-shared/sites/testsite1/ssl/force"}, + {"Value": None, "Key": "test-shared/sites/testsite1/ssl/"}, + { + "Value": "salt://sites/testsite1.tmpl", + "Key": "test-shared/sites/testsite1/template", + }, + {"Value": "test.example.com", "Key": "test-shared/sites/testsite1/uri"}, + {"Value": None, "Key": "test-shared/sites/testsite1/"}, + {"Value": None, "Key": "test-shared/sites/"}, + {"Value": "Test User", "Key": "test-shared/user/full_name"}, + {"Value": "adm\nwww-data\nmlocate", "Key": "test-shared/user/groups"}, + {"Value": '"adm\nwww-data\nmlocate"', "Key": "test-shared/user/dontsplit"}, + {"Value": "yaml:\n key: value\n", "Key": "test-shared/user/dontexpand"}, + {"Value": None, "Key": "test-shared/user/blankvalue"}, + {"Value": "test", "Key": "test-shared/user/login"}, + {"Value": None, "Key": "test-shared/user/"}, ] -SIMPLE_DICT = {'key1': {'key2': 'val1'}} +SIMPLE_DICT = {"key1": {"key2": "val1"}} -@skipIf(not consul_pillar.consul, 'python-consul module not installed') +@skipIf(not consul_pillar.consul, "python-consul module not installed") class ConsulPillarTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.pillar.consul_pillar - ''' + """ + def setup_loader_modules(self): return { consul_pillar: { - '__opts__': OPTS, - 'get_conn': MagicMock(return_value='consul_connection') + "__opts__": OPTS, + "get_conn": MagicMock(return_value="consul_connection"), } } def test_connection(self): - with patch.dict(consul_pillar.__salt__, {'grains.get': MagicMock(return_value=({}))}): - with patch.object(consul_pillar, 'consul_fetch', MagicMock(return_value=('2232', PILLAR_DATA))): - consul_pillar.ext_pillar('testminion', {}, 'consul_config root=test-shared/') - consul_pillar.get_conn.assert_called_once_with(OPTS, 'consul_config') + with patch.dict( + consul_pillar.__salt__, {"grains.get": MagicMock(return_value=({}))} + ): + with patch.object( + consul_pillar, + "consul_fetch", + MagicMock(return_value=("2232", PILLAR_DATA)), + ): + consul_pillar.ext_pillar( + "testminion", {}, "consul_config root=test-shared/" + ) + consul_pillar.get_conn.assert_called_once_with(OPTS, "consul_config") def test_pillar_data(self): - with patch.dict(consul_pillar.__salt__, {'grains.get': MagicMock(return_value=({}))}): - with patch.object(consul_pillar, 'consul_fetch', MagicMock(return_value=('2232', PILLAR_DATA))): - pillar_data = consul_pillar.ext_pillar('testminion', {}, 'consul_config root=test-shared/') - consul_pillar.consul_fetch.assert_called_once_with('consul_connection', 'test-shared') - assert sorted(pillar_data) == ['sites', 'user'] - self.assertNotIn('blankvalue', pillar_data['user']) + with patch.dict( + consul_pillar.__salt__, {"grains.get": MagicMock(return_value=({}))} + ): + with patch.object( + consul_pillar, + "consul_fetch", + MagicMock(return_value=("2232", PILLAR_DATA)), + ): + pillar_data = consul_pillar.ext_pillar( + "testminion", {}, "consul_config root=test-shared/" + ) + consul_pillar.consul_fetch.assert_called_once_with( + "consul_connection", "test-shared" + ) + assert sorted(pillar_data) == ["sites", "user"] + self.assertNotIn("blankvalue", pillar_data["user"]) def test_blank_root(self): - with patch.dict(consul_pillar.__salt__, {'grains.get': MagicMock(return_value=({}))}): - with patch.object(consul_pillar, 'consul_fetch', MagicMock(return_value=('2232', PILLAR_DATA))): - pillar_data = consul_pillar.ext_pillar('testminion', {}, 'consul_config') - consul_pillar.consul_fetch.assert_called_once_with('consul_connection', '') - assert sorted(pillar_data) == ['test-shared'] + with patch.dict( + consul_pillar.__salt__, {"grains.get": MagicMock(return_value=({}))} + ): + with patch.object( + consul_pillar, + "consul_fetch", + MagicMock(return_value=("2232", PILLAR_DATA)), + ): + pillar_data = consul_pillar.ext_pillar( + "testminion", {}, "consul_config" + ) + consul_pillar.consul_fetch.assert_called_once_with( + "consul_connection", "" + ) + assert sorted(pillar_data) == ["test-shared"] def test_pillar_nest(self): - with patch.dict(consul_pillar.__salt__, {'grains.get': MagicMock(return_value=({}))}): - with patch.object(consul_pillar, 'consul_fetch', MagicMock(return_value=('2232', PILLAR_DATA))): + with patch.dict( + consul_pillar.__salt__, {"grains.get": MagicMock(return_value=({}))} + ): + with patch.object( + consul_pillar, + "consul_fetch", + MagicMock(return_value=("2232", PILLAR_DATA)), + ): pillar_data = consul_pillar.ext_pillar( - 'testminion', {}, 'consul_config pillar_root=nested-key/ root=test-shared/ ' + "testminion", + {}, + "consul_config pillar_root=nested-key/ root=test-shared/ ", ) - assert sorted(pillar_data['nested-key']) == ['sites', 'user'] - self.assertNotIn('blankvalue', pillar_data['nested-key']['user']) + assert sorted(pillar_data["nested-key"]) == ["sites", "user"] + self.assertNotIn("blankvalue", pillar_data["nested-key"]["user"]) def test_value_parsing(self): - with patch.dict(consul_pillar.__salt__, {'grains.get': MagicMock(return_value=({}))}): - with patch.object(consul_pillar, 'consul_fetch', MagicMock(return_value=('2232', PILLAR_DATA))): - pillar_data = consul_pillar.ext_pillar('testminion', {}, 'consul_config root=test-shared/') - assert isinstance(pillar_data['user']['dontsplit'], six.string_types) + with patch.dict( + consul_pillar.__salt__, {"grains.get": MagicMock(return_value=({}))} + ): + with patch.object( + consul_pillar, + "consul_fetch", + MagicMock(return_value=("2232", PILLAR_DATA)), + ): + pillar_data = consul_pillar.ext_pillar( + "testminion", {}, "consul_config root=test-shared/" + ) + assert isinstance(pillar_data["user"]["dontsplit"], six.string_types) def test_non_expansion(self): - with patch.dict(consul_pillar.__salt__, {'grains.get': MagicMock(return_value=({}))}): - with patch.object(consul_pillar, 'consul_fetch', MagicMock(return_value=('2232', PILLAR_DATA))): + with patch.dict( + consul_pillar.__salt__, {"grains.get": MagicMock(return_value=({}))} + ): + with patch.object( + consul_pillar, + "consul_fetch", + MagicMock(return_value=("2232", PILLAR_DATA)), + ): pillar_data = consul_pillar.ext_pillar( - 'testminion', {}, 'consul_config root=test-shared/ expand_keys=false' + "testminion", + {}, + "consul_config root=test-shared/ expand_keys=false", ) - assert isinstance(pillar_data['user']['dontexpand'], six.string_types) + assert isinstance(pillar_data["user"]["dontexpand"], six.string_types) def test_dict_merge(self): test_dict = {} with patch.dict(test_dict, SIMPLE_DICT): - self.assertDictEqual(consul_pillar.dict_merge(test_dict, SIMPLE_DICT), SIMPLE_DICT) - with patch.dict(test_dict, {'key1': {'key3': {'key4': 'value'}}}): - self.assertDictEqual(consul_pillar.dict_merge(test_dict, SIMPLE_DICT), - {'key1': {'key2': 'val1', 'key3': {'key4': 'value'}}}) + self.assertDictEqual( + consul_pillar.dict_merge(test_dict, SIMPLE_DICT), SIMPLE_DICT + ) + with patch.dict(test_dict, {"key1": {"key3": {"key4": "value"}}}): + self.assertDictEqual( + consul_pillar.dict_merge(test_dict, SIMPLE_DICT), + {"key1": {"key2": "val1", "key3": {"key4": "value"}}}, + ) diff --git a/tests/unit/pillar/test_csvpillar.py b/tests/unit/pillar/test_csvpillar.py index c166fc6d07e..254565510d5 100644 --- a/tests/unit/pillar/test_csvpillar.py +++ b/tests/unit/pillar/test_csvpillar.py @@ -1,33 +1,42 @@ # -*- coding: utf-8 -*- -'''test for pillar csvpillar.py''' +"""test for pillar csvpillar.py""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, mock_open - - # Import Salt Libs import salt.pillar.csvpillar as csvpillar +from tests.support.mock import mock_open, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase class CSVPillarTestCase(TestCase): def test_001_load_utf8_csv(self): fake_csv = "id,foo,bar\r\nminion1,foo1,bar1" - fake_dict = {'id': 'minion1', 'foo': 'foo1', 'bar': 'bar1'} + fake_dict = {"id": "minion1", "foo": "foo1", "bar": "bar1"} fopen_mock = mock_open(fake_csv) - with patch('salt.utils.files.fopen', fopen_mock): - result = csvpillar.ext_pillar(mid='minion1', pillar=None, - path="/fake/path/file.csv", idkey="id", namespace=None) + with patch("salt.utils.files.fopen", fopen_mock): + result = csvpillar.ext_pillar( + mid="minion1", + pillar=None, + path="/fake/path/file.csv", + idkey="id", + namespace=None, + ) self.assertDictEqual(fake_dict, result) def test_002_load_utf8_csv_namespc(self): fake_csv = "id,foo,bar\r\nminion1,foo1,bar1" - fake_dict = {'baz': {'id': 'minion1', 'foo': 'foo1', 'bar': 'bar1'}} + fake_dict = {"baz": {"id": "minion1", "foo": "foo1", "bar": "bar1"}} fopen_mock = mock_open(fake_csv) - with patch('salt.utils.files.fopen', fopen_mock): - result = csvpillar.ext_pillar(mid='minion1', pillar=None, - path="/fake/path/file.csv", idkey="id", namespace='baz') + with patch("salt.utils.files.fopen", fopen_mock): + result = csvpillar.ext_pillar( + mid="minion1", + pillar=None, + path="/fake/path/file.csv", + idkey="id", + namespace="baz", + ) self.assertDictEqual(fake_dict, result) diff --git a/tests/unit/pillar/test_extra_minion_data_in_pillar.py b/tests/unit/pillar/test_extra_minion_data_in_pillar.py index 7488ed9ed74..3ad467d3fe7 100644 --- a/tests/unit/pillar/test_extra_minion_data_in_pillar.py +++ b/tests/unit/pillar/test_extra_minion_data_in_pillar.py @@ -3,53 +3,62 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock - # Import Salt Libs from salt.pillar import extra_minion_data_in_pillar +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock +from tests.support.unit import TestCase + class ExtraMinionDataInPillarTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.pillar.extra_minion_data_in_pillar - ''' + """ + def setup_loader_modules(self): - return { - extra_minion_data_in_pillar: { - '__virtual__': True, - } - } + return {extra_minion_data_in_pillar: {"__virtual__": True}} def setUp(self): self.pillar = MagicMock() - self.extra_minion_data = {'key1': {'subkey1': 'value1'}, - 'key2': {'subkey2': {'subsubkey2': 'value2'}}, - 'key3': 'value3', - 'key4': {'subkey4': 'value4'}} + self.extra_minion_data = { + "key1": {"subkey1": "value1"}, + "key2": {"subkey2": {"subsubkey2": "value2"}}, + "key3": "value3", + "key4": {"subkey4": "value4"}, + } def test_extra_values_none_or_empty(self): - ret = extra_minion_data_in_pillar.ext_pillar('fake_id', self.pillar, - 'fake_include', None) + ret = extra_minion_data_in_pillar.ext_pillar( + "fake_id", self.pillar, "fake_include", None + ) self.assertEqual(ret, {}) - ret = extra_minion_data_in_pillar.ext_pillar('fake_id', self.pillar, - 'fake_include', {}) + ret = extra_minion_data_in_pillar.ext_pillar( + "fake_id", self.pillar, "fake_include", {} + ) self.assertEqual(ret, {}) def test_include_all(self): - for include_all in ['*', '<all>']: + for include_all in ["*", "<all>"]: ret = extra_minion_data_in_pillar.ext_pillar( - 'fake_id', self.pillar, include_all, self.extra_minion_data) + "fake_id", self.pillar, include_all, self.extra_minion_data + ) self.assertEqual(ret, self.extra_minion_data) def test_include_specific_keys(self): # Tests partially existing key, key with and without subkey, ret = extra_minion_data_in_pillar.ext_pillar( - 'fake_id', self.pillar, - include=['key1:subkey1', 'key2:subkey3', 'key3', 'key4'], - extra_minion_data=self.extra_minion_data) - self.assertEqual(ret, {'key1': {'subkey1': 'value1'}, - 'key3': 'value3', - 'key4': {'subkey4': 'value4'}}) + "fake_id", + self.pillar, + include=["key1:subkey1", "key2:subkey3", "key3", "key4"], + extra_minion_data=self.extra_minion_data, + ) + self.assertEqual( + ret, + { + "key1": {"subkey1": "value1"}, + "key3": "value3", + "key4": {"subkey4": "value4"}, + }, + ) diff --git a/tests/unit/pillar/test_file_tree.py b/tests/unit/pillar/test_file_tree.py index 0521bff4139..bd2a8995934 100644 --- a/tests/unit/pillar/test_file_tree.py +++ b/tests/unit/pillar/test_file_tree.py @@ -1,137 +1,161 @@ # -*- coding: utf-8 -*- -''' +""" test for pillar file_tree.py -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals import os -import tempfile import shutil +import tempfile -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.helpers import TstSuiteLoggingHandler +import salt.pillar.file_tree as file_tree # Import Salt Libs import salt.utils.files import salt.utils.stringutils -import salt.pillar.file_tree as file_tree +from tests.support.helpers import TstSuiteLoggingHandler +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase -MINION_ID = 'test-host' -NODEGROUP_PATH = os.path.join('nodegroups', 'test-group', 'files') -HOST_PATH = os.path.join('hosts', MINION_ID, 'files') +MINION_ID = "test-host" +NODEGROUP_PATH = os.path.join("nodegroups", "test-group", "files") +HOST_PATH = os.path.join("hosts", MINION_ID, "files") -BASE_PILLAR_CONTENT = {'files': {'hostfile': b'base', 'groupfile': b'base'}} -DEV_PILLAR_CONTENT = {'files': {'hostfile': b'base', 'groupfile': b'dev2', - 'hostfile1': b'dev1', 'groupfile1': b'dev1', - 'hostfile2': b'dev2'}} -PARENT_PILLAR_CONTENT = {'files': {'hostfile': b'base', 'groupfile': b'base', - 'hostfile2': b'dev2'}} - -FILE_DATA = { - os.path.join('base', HOST_PATH, 'hostfile'): 'base', - os.path.join('dev1', HOST_PATH, 'hostfile1'): 'dev1', - os.path.join('dev2', HOST_PATH, 'hostfile2'): 'dev2', - os.path.join('base', NODEGROUP_PATH, 'groupfile'): 'base', - os.path.join('dev1', NODEGROUP_PATH, 'groupfile1'): 'dev1', - os.path.join('dev2', NODEGROUP_PATH, 'groupfile'): 'dev2' # test merging +BASE_PILLAR_CONTENT = {"files": {"hostfile": b"base", "groupfile": b"base"}} +DEV_PILLAR_CONTENT = { + "files": { + "hostfile": b"base", + "groupfile": b"dev2", + "hostfile1": b"dev1", + "groupfile1": b"dev1", + "hostfile2": b"dev2", + } +} +PARENT_PILLAR_CONTENT = { + "files": {"hostfile": b"base", "groupfile": b"base", "hostfile2": b"dev2"} } -_CHECK_MINIONS_RETURN = {'minions': [MINION_ID], 'missing': []} +FILE_DATA = { + os.path.join("base", HOST_PATH, "hostfile"): "base", + os.path.join("dev1", HOST_PATH, "hostfile1"): "dev1", + os.path.join("dev2", HOST_PATH, "hostfile2"): "dev2", + os.path.join("base", NODEGROUP_PATH, "groupfile"): "base", + os.path.join("dev1", NODEGROUP_PATH, "groupfile1"): "dev1", + os.path.join("dev2", NODEGROUP_PATH, "groupfile"): "dev2", # test merging +} + +_CHECK_MINIONS_RETURN = {"minions": [MINION_ID], "missing": []} class FileTreePillarTestCase(TestCase, LoaderModuleMockMixin): - 'test file_tree pillar' + "test file_tree pillar" maxDiff = None def setup_loader_modules(self): self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.tmpdir) - cachedir = os.path.join(self.tmpdir, 'cachedir') - os.makedirs(os.path.join(cachedir, 'file_tree')) + cachedir = os.path.join(self.tmpdir, "cachedir") + os.makedirs(os.path.join(cachedir, "file_tree")) self.pillar_path = self._create_pillar_files() return { file_tree: { - '__opts__': { - 'cachedir': cachedir, - 'pillar_roots': { - 'base': [os.path.join(self.pillar_path, 'base')], - 'dev': [os.path.join(self.pillar_path, 'base'), - os.path.join(self.pillar_path, 'dev1'), - os.path.join(self.pillar_path, 'dev2') - ], - 'parent': [os.path.join(self.pillar_path, 'base', 'sub1'), - os.path.join(self.pillar_path, 'dev2', 'sub'), - os.path.join(self.pillar_path, 'base', 'sub2'), - ], + "__opts__": { + "cachedir": cachedir, + "pillar_roots": { + "base": [os.path.join(self.pillar_path, "base")], + "dev": [ + os.path.join(self.pillar_path, "base"), + os.path.join(self.pillar_path, "dev1"), + os.path.join(self.pillar_path, "dev2"), + ], + "parent": [ + os.path.join(self.pillar_path, "base", "sub1"), + os.path.join(self.pillar_path, "dev2", "sub"), + os.path.join(self.pillar_path, "base", "sub2"), + ], }, - 'pillarenv': 'base', - 'nodegroups': {'test-group': [MINION_ID]}, - 'optimization_order': [0, 1, 2], - 'file_buffer_size': 262144, - 'file_roots': {'base': '', 'dev': '', 'parent': ''}, - 'extension_modules': '', - 'renderer': 'yaml_jinja', - 'renderer_blacklist': [], - 'renderer_whitelist': [] + "pillarenv": "base", + "nodegroups": {"test-group": [MINION_ID]}, + "optimization_order": [0, 1, 2], + "file_buffer_size": 262144, + "file_roots": {"base": "", "dev": "", "parent": ""}, + "extension_modules": "", + "renderer": "yaml_jinja", + "renderer_blacklist": [], + "renderer_whitelist": [], } } } def _create_pillar_files(self): - 'create files in tempdir' - pillar_path = os.path.join(self.tmpdir, 'file_tree') + "create files in tempdir" + pillar_path = os.path.join(self.tmpdir, "file_tree") for filename in FILE_DATA: filepath = os.path.join(pillar_path, filename) os.makedirs(os.path.dirname(filepath)) - with salt.utils.files.fopen(filepath, 'w') as data_file: + with salt.utils.files.fopen(filepath, "w") as data_file: data_file.write(salt.utils.stringutils.to_str(FILE_DATA[filename])) return pillar_path def test_absolute_path(self): - 'check file tree is imported correctly with an absolute path' - absolute_path = os.path.join(self.pillar_path, 'base') - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_CHECK_MINIONS_RETURN)): + "check file tree is imported correctly with an absolute path" + absolute_path = os.path.join(self.pillar_path, "base") + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_CHECK_MINIONS_RETURN), + ): mypillar = file_tree.ext_pillar(MINION_ID, None, absolute_path) self.assertEqual(BASE_PILLAR_CONTENT, mypillar) - with patch.dict(file_tree.__opts__, {'pillarenv': 'dev'}): + with patch.dict(file_tree.__opts__, {"pillarenv": "dev"}): mypillar = file_tree.ext_pillar(MINION_ID, None, absolute_path) self.assertEqual(BASE_PILLAR_CONTENT, mypillar) def test_relative_path(self): - 'check file tree is imported correctly with a relative path' - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_CHECK_MINIONS_RETURN)): - mypillar = file_tree.ext_pillar(MINION_ID, None, '.') + "check file tree is imported correctly with a relative path" + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_CHECK_MINIONS_RETURN), + ): + mypillar = file_tree.ext_pillar(MINION_ID, None, ".") self.assertEqual(BASE_PILLAR_CONTENT, mypillar) - with patch.dict(file_tree.__opts__, {'pillarenv': 'dev'}): - mypillar = file_tree.ext_pillar(MINION_ID, None, '.') + with patch.dict(file_tree.__opts__, {"pillarenv": "dev"}): + mypillar = file_tree.ext_pillar(MINION_ID, None, ".") self.assertEqual(DEV_PILLAR_CONTENT, mypillar) def test_parent_path(self): - 'check if file tree is merged correctly with a .. path' - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_CHECK_MINIONS_RETURN)): - with patch.dict(file_tree.__opts__, {'pillarenv': 'parent'}): - mypillar = file_tree.ext_pillar(MINION_ID, None, '..') + "check if file tree is merged correctly with a .. path" + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_CHECK_MINIONS_RETURN), + ): + with patch.dict(file_tree.__opts__, {"pillarenv": "parent"}): + mypillar = file_tree.ext_pillar(MINION_ID, None, "..") self.assertEqual(PARENT_PILLAR_CONTENT, mypillar) def test_no_pillarenv(self): - 'confirm that file_tree yells when pillarenv is missing for a relative path' - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_CHECK_MINIONS_RETURN)): - with patch.dict(file_tree.__opts__, {'pillarenv': None}): + "confirm that file_tree yells when pillarenv is missing for a relative path" + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_CHECK_MINIONS_RETURN), + ): + with patch.dict(file_tree.__opts__, {"pillarenv": None}): with TstSuiteLoggingHandler() as handler: - mypillar = file_tree.ext_pillar(MINION_ID, None, '.') + mypillar = file_tree.ext_pillar(MINION_ID, None, ".") self.assertEqual({}, mypillar) for message in handler.messages: - if message.startswith('ERROR:') and 'pillarenv is not set' in message: + if ( + message.startswith("ERROR:") + and "pillarenv is not set" in message + ): break else: - raise AssertionError('Did not find error message') + raise AssertionError("Did not find error message") diff --git a/tests/unit/pillar/test_hg_pillar.py b/tests/unit/pillar/test_hg_pillar.py index 452d15cbcbe..e1bdbe41085 100644 --- a/tests/unit/pillar/test_hg_pillar.py +++ b/tests/unit/pillar/test_hg_pillar.py @@ -1,76 +1,96 @@ # -*- coding: utf-8 -*- -'''test for pillar hg_pillar.py''' +"""test for pillar hg_pillar.py""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals import os -import tempfile import shutil import subprocess +import tempfile -# Import Salt Testing libs -from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.runtests import RUNTIME_VARS - - -COMMIT_USER_NAME = 'test_user' -# file contents -PILLAR_CONTENT = {'gna': 'hello'} -FILE_DATA = { - 'top.sls': {'base': {'*': ['user']}}, - 'user.sls': PILLAR_CONTENT -} +import salt.pillar.hg_pillar as hg_pillar # Import Salt Libs import salt.utils.files import salt.utils.yaml -import salt.pillar.hg_pillar as hg_pillar + +# Import Salt Testing libs +from tests.support.mixins import ( + AdaptedConfigurationTestCaseMixin, + LoaderModuleMockMixin, +) +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + +COMMIT_USER_NAME = "test_user" +# file contents +PILLAR_CONTENT = {"gna": "hello"} +FILE_DATA = {"top.sls": {"base": {"*": ["user"]}}, "user.sls": PILLAR_CONTENT} + + HGLIB = hg_pillar.hglib -@skipIf(HGLIB is None, 'python-hglib library not installed') -class HgPillarTestCase(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): - 'test hg_pillar pillar' +@skipIf(HGLIB is None, "python-hglib library not installed") +class HgPillarTestCase( + TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin +): + "test hg_pillar pillar" maxDiff = None def setup_loader_modules(self): self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.tmpdir) - cachedir = os.path.join(self.tmpdir, 'cachedir') - os.makedirs(os.path.join(cachedir, 'hg_pillar')) + cachedir = os.path.join(self.tmpdir, "cachedir") + os.makedirs(os.path.join(cachedir, "hg_pillar")) self.hg_repo_path = self._create_hg_repo() return { hg_pillar: { - '__opts__': { - 'cachedir': cachedir, - 'pillar_roots': {}, - 'file_roots': {}, - 'state_top': 'top.sls', - 'extension_modules': '', - 'renderer': 'yaml_jinja', - 'pillar_opts': False, - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'file_ignore_glob': [], - 'file_ignore_regex': [], + "__opts__": { + "cachedir": cachedir, + "pillar_roots": {}, + "file_roots": {}, + "state_top": "top.sls", + "extension_modules": "", + "renderer": "yaml_jinja", + "pillar_opts": False, + "renderer_blacklist": [], + "renderer_whitelist": [], + "file_ignore_glob": [], + "file_ignore_regex": [], } } } def _create_hg_repo(self): - 'create repo in tempdir' - hg_repo = os.path.join(self.tmpdir, 'repo_pillar') + "create repo in tempdir" + hg_repo = os.path.join(self.tmpdir, "repo_pillar") os.makedirs(hg_repo) - subprocess.check_call(['hg', 'init', hg_repo]) + subprocess.check_call(["hg", "init", hg_repo]) for filename in FILE_DATA: - with salt.utils.files.fopen(os.path.join(hg_repo, filename), 'w') as data_file: + with salt.utils.files.fopen( + os.path.join(hg_repo, filename), "w" + ) as data_file: salt.utils.yaml.safe_dump(FILE_DATA[filename], data_file) - subprocess.check_call(['hg', 'ci', '-A', '-R', hg_repo, '-m', 'first commit', '-u', COMMIT_USER_NAME]) + subprocess.check_call( + [ + "hg", + "ci", + "-A", + "-R", + hg_repo, + "-m", + "first commit", + "-u", + COMMIT_USER_NAME, + ] + ) return hg_repo def test_base(self): - 'check hg repo is imported correctly' - mypillar = hg_pillar.ext_pillar('*', None, 'file://{0}'.format(self.hg_repo_path)) + "check hg repo is imported correctly" + mypillar = hg_pillar.ext_pillar( + "*", None, "file://{0}".format(self.hg_repo_path) + ) self.assertEqual(PILLAR_CONTENT, mypillar) diff --git a/tests/unit/pillar/test_mysql.py b/tests/unit/pillar/test_mysql.py index 94ecde8c20d..bc81eb41746 100644 --- a/tests/unit/pillar/test_mysql.py +++ b/tests/unit/pillar/test_mysql.py @@ -3,170 +3,460 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf - # Import Salt Libs import salt.pillar.mysql as mysql +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf -@skipIf(mysql.MySQLdb is None, 'MySQL-python module not installed') + +@skipIf(mysql.MySQLdb is None, "MySQL-python module not installed") class MysqlPillarTestCase(TestCase): maxDiff = None def test_001_extract_queries_legacy(self): return_data = mysql.MySQLExtPillar() - args, kwargs = ['SELECT blah'], {} + args, kwargs = ["SELECT blah"], {} qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ] + ], + qbuffer, + ) def test_002_extract_queries_list(self): return_data = mysql.MySQLExtPillar() - args, kwargs = [ - 'SELECT blah', - 'SELECT blah2', - ('SELECT blah3',), - ('SELECT blah4', 2), - {'query': 'SELECT blah5'}, - {'query': 'SELECT blah6', 'depth': 2}, - {'query': 'SELECT blah7', 'as_list': True}, - {'query': 'SELECT blah8', 'with_lists': '1'}, - {'query': 'SELECT blah9', 'with_lists': '1,2'} - ], {} + args, kwargs = ( + [ + "SELECT blah", + "SELECT blah2", + ("SELECT blah3",), + ("SELECT blah4", 2), + {"query": "SELECT blah5"}, + {"query": "SELECT blah6", "depth": 2}, + {"query": "SELECT blah7", "as_list": True}, + {"query": "SELECT blah8", "with_lists": "1"}, + {"query": "SELECT blah9", "with_lists": "1,2"}, + ], + {}, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah3', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah4', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah5', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah6', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah7', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah8', 'depth': 0, 'as_list': False, - 'with_lists': [1], 'ignore_null': False}], - [None, {'query': 'SELECT blah9', 'depth': 0, 'as_list': False, - 'with_lists': [1, 2], 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah3", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah4", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah5", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah6", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah7", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah8", + "depth": 0, + "as_list": False, + "with_lists": [1], + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah9", + "depth": 0, + "as_list": False, + "with_lists": [1, 2], + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_003_extract_queries_kwarg(self): return_data = mysql.MySQLExtPillar() - args, kwargs = [], { - '1': 'SELECT blah', - '2': 'SELECT blah2', - '3': ('SELECT blah3',), - '4': ('SELECT blah4', 2), - '5': {'query': 'SELECT blah5'}, - '6': {'query': 'SELECT blah6', 'depth': 2}, - '7': {'query': 'SELECT blah7', 'as_list': True}, - } + args, kwargs = ( + [], + { + "1": "SELECT blah", + "2": "SELECT blah2", + "3": ("SELECT blah3",), + "4": ("SELECT blah4", 2), + "5": {"query": "SELECT blah5"}, + "6": {"query": "SELECT blah6", "depth": 2}, + "7": {"query": "SELECT blah7", "as_list": True}, + }, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - ['1', {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['2', {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['3', {'query': 'SELECT blah3', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['4', {'query': 'SELECT blah4', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['5', {'query': 'SELECT blah5', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['6', {'query': 'SELECT blah6', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['7', {'query': 'SELECT blah7', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + "1", + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "2", + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "3", + { + "query": "SELECT blah3", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "4", + { + "query": "SELECT blah4", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "5", + { + "query": "SELECT blah5", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "6", + { + "query": "SELECT blah6", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "7", + { + "query": "SELECT blah7", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_004_extract_queries_mixed(self): return_data = mysql.MySQLExtPillar() - args, kwargs = [ - 'SELECT blah1', - ('SELECT blah2', 2), - {'query': 'SELECT blah3', 'as_list': True}, - ], { - '1': 'SELECT blah1', - '2': ('SELECT blah2', 2), - '3': {'query': 'SELECT blah3', 'as_list': True}, - } + args, kwargs = ( + [ + "SELECT blah1", + ("SELECT blah2", 2), + {"query": "SELECT blah3", "as_list": True}, + ], + { + "1": "SELECT blah1", + "2": ("SELECT blah2", 2), + "3": {"query": "SELECT blah3", "as_list": True}, + }, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah1', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah2', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah3', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}], - ['1', {'query': 'SELECT blah1', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['2', {'query': 'SELECT blah2', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['3', {'query': 'SELECT blah3', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah1", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah2", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah3", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "1", + { + "query": "SELECT blah1", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "2", + { + "query": "SELECT blah2", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "3", + { + "query": "SELECT blah3", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_005_extract_queries_bogus_list(self): # This test is specifically checking that empty queries are dropped return_data = mysql.MySQLExtPillar() - args, kwargs = [ - 'SELECT blah', - '', - 'SELECT blah2', - ('SELECT blah3',), - ('',), - ('SELECT blah4', 2), - tuple(), - ('SELECT blah5',), - {'query': 'SELECT blah6'}, - {'query': ''}, - {'query': 'SELECT blah7', 'depth': 2}, - {'not_a_query': 'in sight!'}, - {'query': 'SELECT blah8', 'as_list': True}, - ], {} + args, kwargs = ( + [ + "SELECT blah", + "", + "SELECT blah2", + ("SELECT blah3",), + ("",), + ("SELECT blah4", 2), + tuple(), + ("SELECT blah5",), + {"query": "SELECT blah6"}, + {"query": ""}, + {"query": "SELECT blah7", "depth": 2}, + {"not_a_query": "in sight!"}, + {"query": "SELECT blah8", "as_list": True}, + ], + {}, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah3', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah4', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah5', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah6', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah7', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah8', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah3", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah4", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah5", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah6", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah7", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah8", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_006_extract_queries_bogus_kwargs(self): # this test is cut down as most of the path matches test_*_bogus_list return_data = mysql.MySQLExtPillar() - args, kwargs = [], { - '1': 'SELECT blah', - '2': '', - '3': 'SELECT blah2' - } + args, kwargs = [], {"1": "SELECT blah", "2": "", "3": "SELECT blah2"} qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - ['1', {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['3', {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + "1", + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "3", + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_011_enter_root(self): return_data = mysql.MySQLExtPillar() @@ -177,348 +467,280 @@ class MysqlPillarTestCase(TestCase): def test_021_process_fields(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) self.assertEqual(return_data.num_fields, 2) self.assertEqual(return_data.depth, 1) - return_data.process_fields(['a', 'b'], 2) + return_data.process_fields(["a", "b"], 2) self.assertEqual(return_data.num_fields, 2) self.assertEqual(return_data.depth, 1) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 3) - return_data.process_fields(['a', 'b', 'c', 'd'], 1) + return_data.process_fields(["a", "b", "c", "d"], 1) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 1) - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 2) - return_data.process_fields(['a', 'b', 'c', 'd'], 3) + return_data.process_fields(["a", "b", "c", "d"], 3) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 3) - return_data.process_fields(['a', 'b', 'c', 'd'], 4) + return_data.process_fields(["a", "b", "c", "d"], 4) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 3) def test_111_process_results_legacy(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) return_data.with_lists = [] return_data.process_results([[1, 2]]) - self.assertEqual( - {1: 2}, - return_data.result - ) + self.assertEqual({1: 2}, return_data.result) def test_112_process_results_legacy_multiple(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) return_data.with_lists = [] return_data.process_results([[1, 2], [3, 4], [5, 6]]) - self.assertEqual( - {1: 2, 3: 4, 5: 6}, - return_data.result - ) + self.assertEqual({1: 2, 3: 4, 5: 6}, return_data.result) def test_121_process_results_depth_0(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}}, 5: {6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}}, 5: {6: {7: 8}}}, return_data.result) def test_122_process_results_depth_1(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 1) + return_data.process_fields(["a", "b", "c", "d"], 1) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) self.assertEqual( - {1: {'b': 2, 'c': 3, 'd': 4}, 5: {'b': 6, 'c': 7, 'd': 8}}, - return_data.result + {1: {"b": 2, "c": 3, "d": 4}, 5: {"b": 6, "c": 7, "d": 8}}, + return_data.result, ) def test_123_process_results_depth_2(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) self.assertEqual( - {1: {2: {'c': 3, 'd': 4}}, 5: {6: {'c': 7, 'd': 8}}}, - return_data.result + {1: {2: {"c": 3, "d": 4}}, 5: {6: {"c": 7, "d": 8}}}, return_data.result ) def test_124_process_results_depth_3(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 3) + return_data.process_fields(["a", "b", "c", "d"], 3) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}}, 5: {6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}}, 5: {6: {7: 8}}}, return_data.result) def test_125_process_results_depth_4(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 4) + return_data.process_fields(["a", "b", "c", "d"], 4) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}}, 5: {6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}}, 5: {6: {7: 8}}}, return_data.result) def test_131_process_results_overwrite_legacy_multiple(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) return_data.with_lists = [] return_data.process_results([[1, 2], [3, 4], [1, 6]]) - self.assertEqual( - {1: 6, 3: 4}, - return_data.result - ) + self.assertEqual({1: 6, 3: 4}, return_data.result) def test_132_process_results_merge_depth_0(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}, 6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}, 6: {7: 8}}}, return_data.result) def test_133_process_results_overwrite_depth_0(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 2, 3, 8]]) - self.assertEqual( - {1: {2: {3: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 8}}}, return_data.result) def test_134_process_results_deepmerge_depth_0(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4, 7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4, 7: 8}}}, return_data.result) def test_135_process_results_overwrite_depth_1(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 1) + return_data.process_fields(["a", "b", "c", "d"], 1) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 6, 7, 8]]) - self.assertEqual( - {1: {'b': 6, 'c': 7, 'd': 8}}, - return_data.result - ) + self.assertEqual({1: {"b": 6, "c": 7, "d": 8}}, return_data.result) def test_136_process_results_merge_depth_2(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 6, 7, 8]]) self.assertEqual( - {1: {2: {'c': 3, 'd': 4}, 6: {'c': 7, 'd': 8}}}, - return_data.result + {1: {2: {"c": 3, "d": 4}, 6: {"c": 7, "d": 8}}}, return_data.result ) def test_137_process_results_overwrite_depth_2(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {'c': 7, 'd': 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {"c": 7, "d": 8}}}, return_data.result) def test_201_process_results_complexity_multiresults(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {'c': 7, 'd': 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {"c": 7, "d": 8}}}, return_data.result) def test_202_process_results_complexity_as_list(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.as_list = True return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {'c': [3, 7], 'd': [4, 8]}}}, - return_data.result - ) + self.assertEqual({1: {2: {"c": [3, 7], "d": [4, 8]}}}, return_data.result) def test_203_process_results_complexity_as_list_deeper(self): return_data = mysql.MySQLExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.as_list = True return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 8]]) - self.assertEqual( - {1: {2: {3: [4, 8]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [4, 8]}}}, return_data.result) def test_204_process_results_complexity_as_list_mismatch_depth(self): return_data = mysql.MySQLExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - self.assertEqual( - {1: {2: {3: [4, 5, {6: 7}]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [4, 5, {6: 7}]}}}, return_data.result) def test_205_process_results_complexity_as_list_mismatch_depth_reversed(self): return_data = mysql.MySQLExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: [{6: 7, 8: 9}, 4, 5]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [{6: 7, 8: 9}, 4, 5]}}}, return_data.result) def test_206_process_results_complexity_as_list_mismatch_depth_weird_order(self): return_data = mysql.MySQLExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: [{6: 7, }, 4, {8: 9}, 5]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [{6: 7}, 4, {8: 9}, 5]}}}, return_data.result) def test_207_process_results_complexity_collision_mismatch_depth(self): return_data = mysql.MySQLExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - self.assertEqual( - {1: {2: {3: {6: 7}}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: {6: 7}}}}, return_data.result) def test_208_process_results_complexity_collision_mismatch_depth_reversed(self): return_data = mysql.MySQLExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: 5}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 5}}}, return_data.result) def test_209_process_results_complexity_collision_mismatch_depth_weird_order(self): return_data = mysql.MySQLExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: 5}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 5}}}, return_data.result) def test_20A_process_results_complexity_as_list_vary(self): return_data = mysql.MySQLExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.as_list = False return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: 5}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 5}}}, return_data.result) def test_207_process_results_complexity_roots_collision(self): return_data = mysql.MySQLExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.enter_root(1) return_data.process_results([[5, 6, 7, 8]]) - self.assertEqual( - {1: {5: {6: {7: 8}}}}, - return_data.result - ) + self.assertEqual({1: {5: {6: {7: 8}}}}, return_data.result) def test_301_process_results_with_lists(self): - ''' + """ Validates the following results: {'a': [ @@ -532,45 +754,49 @@ class MysqlPillarTestCase(TestCase): ] } ]} - ''' + """ return_data = mysql.MySQLExtPillar() return_data.as_list = False return_data.with_lists = [1, 3] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e', 'v'], 0) - return_data.process_results([['a', 'b', 'c', 'd', 'e', 1], - ['a', 'b', 'c', 'f', 'g', 2], - ['a', 'z', 'h', 'y', 'j', 3], - ['a', 'z', 'h', 'y', 'k', 4]]) - assert 'a' in return_data.result - for x in return_data.result['a']: - if 'c' in x: - assert list(x.keys()) == ['c'], x.keys() - for y in x['c']: - if 'e' in y: - assert list(y.keys()) == ['e'] - assert y['e'] == 1 - elif 'g' in y: - assert list(y.keys()) == ['g'] - assert y['g'] == 2 + return_data.process_fields(["a", "b", "c", "d", "e", "v"], 0) + return_data.process_results( + [ + ["a", "b", "c", "d", "e", 1], + ["a", "b", "c", "f", "g", 2], + ["a", "z", "h", "y", "j", 3], + ["a", "z", "h", "y", "k", 4], + ] + ) + assert "a" in return_data.result + for x in return_data.result["a"]: + if "c" in x: + assert list(x.keys()) == ["c"], x.keys() + for y in x["c"]: + if "e" in y: + assert list(y.keys()) == ["e"] + assert y["e"] == 1 + elif "g" in y: + assert list(y.keys()) == ["g"] + assert y["g"] == 2 else: raise ValueError("Unexpected value {0}".format(y)) - elif 'h' in x: - assert len(x['h']) == 1 - for y in x['h']: - if 'j' in y: + elif "h" in x: + assert len(x["h"]) == 1 + for y in x["h"]: + if "j" in y: assert len(y.keys()) == 2 - assert y['j'] == 3 - elif 'h' in y: + assert y["j"] == 3 + elif "h" in y: assert len(y.keys()) == 2 - assert y['k'] == 4 + assert y["k"] == 4 else: raise ValueError("Unexpected value {0}".format(y)) else: raise ValueError("Unexpected value {0}".format(x)) def test_302_process_results_with_lists_consecutive(self): - ''' + """ Validates the following results: {'a': [ @@ -584,41 +810,43 @@ class MysqlPillarTestCase(TestCase): ] ] ]} - ''' + """ return_data = mysql.MySQLExtPillar() return_data.as_list = False return_data.with_lists = [1, 2, 3] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e', 'v'], 0) - return_data.process_results([['a', 'b', 'c', 'd', 'e', 1], - ['a', 'b', 'c', 'f', 'g', 2], - ['a', 'z', 'h', 'y', 'j', 3], - ['a', 'z', 'h', 'y', 'k', 4]]) + return_data.process_fields(["a", "b", "c", "d", "e", "v"], 0) + return_data.process_results( + [ + ["a", "b", "c", "d", "e", 1], + ["a", "b", "c", "f", "g", 2], + ["a", "z", "h", "y", "j", 3], + ["a", "z", "h", "y", "k", 4], + ] + ) - assert 'a' in return_data.result - for x in return_data.result['a']: + assert "a" in return_data.result + for x in return_data.result["a"]: assert len(x) == 1 if len(x[0][0]) == 1: for y in x[0]: - if 'e' in y: - assert list(y.keys()) == ['e'] - assert y['e'] == 1 - elif 'g' in y: - assert list(y.keys()) == ['g'] - assert y['g'] == 2 + if "e" in y: + assert list(y.keys()) == ["e"] + assert y["e"] == 1 + elif "g" in y: + assert list(y.keys()) == ["g"] + assert y["g"] == 2 else: raise ValueError("Unexpected value {0}".format(y)) elif len(x[0][0]) == 2: for y in x[0]: - if 'j' in y: + if "j" in y: assert len(y.keys()) == 2 - assert y['j'] == 3 - elif 'k' in y: + assert y["j"] == 3 + elif "k" in y: assert len(y.keys()) == 2 - assert y['k'] == 4 + assert y["k"] == 4 else: - raise ValueError( - "Unexpected value {0}".format(len(x[0][0])) - ) + raise ValueError("Unexpected value {0}".format(len(x[0][0]))) else: raise ValueError("Unexpected value {0}".format(x)) diff --git a/tests/unit/pillar/test_nodegroups.py b/tests/unit/pillar/test_nodegroups.py index bb0315981c6..aa6d96ae577 100644 --- a/tests/unit/pillar/test_nodegroups.py +++ b/tests/unit/pillar/test_nodegroups.py @@ -3,50 +3,49 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock - # Import Salt Libs import salt.pillar.nodegroups as nodegroups -fake_minion_id = 'fake_id' +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +fake_minion_id = "fake_id" fake_pillar = {} fake_nodegroups = { - 'groupA': fake_minion_id, - 'groupB': 'another_minion_id', + "groupA": fake_minion_id, + "groupB": "another_minion_id", } -fake_opts = { - 'cache': 'localfs', - 'nodegroups': fake_nodegroups, - 'id': fake_minion_id -} -fake_pillar_name = 'fake_pillar_name' +fake_opts = {"cache": "localfs", "nodegroups": fake_nodegroups, "id": fake_minion_id} +fake_pillar_name = "fake_pillar_name" def side_effect(group_sel, t): if group_sel.find(fake_minion_id) != -1: - return {'minions': [fake_minion_id, ], - 'missing': []} - return {'minions': ['another_minion_id', ], - 'missing': []} + return {"minions": [fake_minion_id], "missing": []} + return {"minions": ["another_minion_id"], "missing": []} class NodegroupsPillarTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.pillar.nodegroups - ''' + """ + def setup_loader_modules(self): - return {nodegroups: {'__opts__': fake_opts}} + return {nodegroups: {"__opts__": fake_opts}} def _runner(self, expected_ret, pillar_name=None): - with patch('salt.utils.minions.CkMinions.check_minions', - MagicMock(side_effect=side_effect)): + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(side_effect=side_effect), + ): pillar_name = pillar_name or fake_pillar_name - actual_ret = nodegroups.ext_pillar(fake_minion_id, fake_pillar, pillar_name=pillar_name) + actual_ret = nodegroups.ext_pillar( + fake_minion_id, fake_pillar, pillar_name=pillar_name + ) self.assertDictEqual(actual_ret, expected_ret) def test_succeeds(self): - ret = {fake_pillar_name: ['groupA', ]} + ret = {fake_pillar_name: ["groupA"]} self._runner(ret) diff --git a/tests/unit/pillar/test_pepa.py b/tests/unit/pillar/test_pepa.py index 79dad1af8bb..81d07f9dc7c 100644 --- a/tests/unit/pillar/test_pepa.py +++ b/tests/unit/pillar/test_pepa.py @@ -2,17 +2,19 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from collections import OrderedDict -# Import Salt Testing libs -from tests.support.unit import TestCase +from collections import OrderedDict # Import Salt Libs import salt.pillar.pepa as pepa +# Import Salt Testing libs +from tests.support.unit import TestCase + class PepaPillarTestCase(TestCase): def test_repeated_keys(self): + # fmt: off expected_result = { "foo": { "bar": { @@ -25,5 +27,6 @@ class PepaPillarTestCase(TestCase): ('foo..bar..foo', True), ('foo..bar..baz', True), ]) + # fmt: on result = pepa.key_value_to_tree(data) self.assertDictEqual(result, expected_result) diff --git a/tests/unit/pillar/test_pillar_ldap.py b/tests/unit/pillar/test_pillar_ldap.py index 53ae293dcdf..3cfc8785c14 100644 --- a/tests/unit/pillar/test_pillar_ldap.py +++ b/tests/unit/pillar/test_pillar_ldap.py @@ -3,24 +3,21 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from tests.support.unit import TestCase import salt.utils.stringutils - - from salt.pillar.pillar_ldap import _config +from tests.support.unit import TestCase class LdapPillarTestCase(TestCase): - def test__config_returns_str(self): - conf = {'foo': 'bar'} - assert _config('foo', conf) == salt.utils.stringutils.to_str('bar') + conf = {"foo": "bar"} + assert _config("foo", conf) == salt.utils.stringutils.to_str("bar") def test__conf_defaults_to_none(self): - conf = {'foo': 'bar'} - assert _config('bang', conf) is None + conf = {"foo": "bar"} + assert _config("bang", conf) is None def test__conf_returns_str_from_unicode_default(self): - conf = {'foo': 'bar'} - default = salt.utils.stringutils.to_unicode('bam') - assert _config('bang', conf, default) == salt.utils.stringutils.to_str('bam') + conf = {"foo": "bar"} + default = salt.utils.stringutils.to_unicode("bam") + assert _config("bang", conf, default) == salt.utils.stringutils.to_str("bam") diff --git a/tests/unit/pillar/test_s3.py b/tests/unit/pillar/test_s3.py index 4f6184eee8e..f1944709674 100644 --- a/tests/unit/pillar/test_s3.py +++ b/tests/unit/pillar/test_s3.py @@ -5,89 +5,96 @@ from __future__ import absolute_import, print_function, unicode_literals import logging -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch, mock_open, MagicMock - # Import Salt Libs import salt.pillar.s3 as s3_pillar from salt.ext.six.moves import range +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + log = logging.getLogger(__name__) class S3PillarTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.pillar.s3 - ''' + """ + def setup_loader_modules(self): - s3_pillar_globals = { - '__utils__': {} - } + s3_pillar_globals = {"__utils__": {}} return {s3_pillar: s3_pillar_globals} def test_refresh_buckets_cache_file(self): - ''' + """ Test pagination with refresh_buckets_cache_file - ''' - key = 'XXXXXXXXXXXXXXXXXXXXX' - keyid = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' - bucket = 'dummy_bucket' - service_url = 's3.amazonaws.com' - cache_file = 'dummy_file' + """ + key = "XXXXXXXXXXXXXXXXXXXXX" + keyid = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + bucket = "dummy_bucket" + service_url = "s3.amazonaws.com" + cache_file = "dummy_file" s3_creds = s3_pillar.S3Credentials(key, keyid, bucket, service_url) - mock_return_first = [{'Name': 'pillar-bucket'}, - {'Prefix': 'test'}, - {'KeyCount': '10'}, - {'MaxKeys': '10'}, - {'NextContinuationToken': 'XXXXX'}, - {'IsTruncated': 'true'}] + mock_return_first = [ + {"Name": "pillar-bucket"}, + {"Prefix": "test"}, + {"KeyCount": "10"}, + {"MaxKeys": "10"}, + {"NextContinuationToken": "XXXXX"}, + {"IsTruncated": "true"}, + ] - mock_return_second = [{'Name': 'pillar-bucket'}, - {'Prefix': 'test'}, - {'KeyCount': '10'}, - {'MaxKeys': '10'}, - {'IsTruncated': 'true'}] + mock_return_second = [ + {"Name": "pillar-bucket"}, + {"Prefix": "test"}, + {"KeyCount": "10"}, + {"MaxKeys": "10"}, + {"IsTruncated": "true"}, + ] first_range_end = 999 second_range_end = 1200 for i in range(0, first_range_end): - key_name = '{0}/init.sls'.format(i) - tmp = {'Key': key_name, - 'LastModified': '2019-12-18T15:54:39.000Z', - 'ETag': '"fba0a053704e8b357c94be90b44bb640"', - 'Size': '5 ', - 'StorageClass': 'STANDARD'} + key_name = "{0}/init.sls".format(i) + tmp = { + "Key": key_name, + "LastModified": "2019-12-18T15:54:39.000Z", + "ETag": '"fba0a053704e8b357c94be90b44bb640"', + "Size": "5 ", + "StorageClass": "STANDARD", + } mock_return_first.append(tmp) for i in range(first_range_end, second_range_end): - key_name = '{0}/init.sls'.format(i) - tmp = {'Key': key_name, - 'LastModified': '2019-12-18T15:54:39.000Z', - 'ETag': '"fba0a053704e8b357c94be90b44bb640"', - 'Size': '5 ', - 'StorageClass': 'STANDARD'} + key_name = "{0}/init.sls".format(i) + tmp = { + "Key": key_name, + "LastModified": "2019-12-18T15:54:39.000Z", + "ETag": '"fba0a053704e8b357c94be90b44bb640"', + "Size": "5 ", + "StorageClass": "STANDARD", + } mock_return_second.append(tmp) - _expected = {'base': {'dummy_bucket': []}} + _expected = {"base": {"dummy_bucket": []}} for i in range(0, second_range_end): - key_name = '{0}/init.sls'.format(i) - tmp = {'Key': key_name, - 'LastModified': '2019-12-18T15:54:39.000Z', - 'ETag': '"fba0a053704e8b357c94be90b44bb640"', - 'Size': '5 ', - 'StorageClass': 'STANDARD'} - _expected['base']['dummy_bucket'].append(tmp) + key_name = "{0}/init.sls".format(i) + tmp = { + "Key": key_name, + "LastModified": "2019-12-18T15:54:39.000Z", + "ETag": '"fba0a053704e8b357c94be90b44bb640"', + "Size": "5 ", + "StorageClass": "STANDARD", + } + _expected["base"]["dummy_bucket"].append(tmp) mock_s3_query = MagicMock(side_effect=[mock_return_first, mock_return_second]) - with patch.dict(s3_pillar.__utils__, {'s3.query': mock_s3_query}): - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): - ret = s3_pillar._refresh_buckets_cache_file(s3_creds, - cache_file, - False, - 'base', - '') + with patch.dict(s3_pillar.__utils__, {"s3.query": mock_s3_query}): + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): + ret = s3_pillar._refresh_buckets_cache_file( + s3_creds, cache_file, False, "base", "" + ) self.assertEqual(ret, _expected) diff --git a/tests/unit/pillar/test_saltclass.py b/tests/unit/pillar/test_saltclass.py index f8a4e1f7e78..ecef8981a2a 100644 --- a/tests/unit/pillar/test_saltclass.py +++ b/tests/unit/pillar/test_saltclass.py @@ -2,40 +2,49 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os +# Import Salt Libs +import salt.pillar.saltclass as saltclass + # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -# Import Salt Libs -import salt.pillar.saltclass as saltclass - - base_path = os.path.dirname(os.path.realpath(__file__)) -fake_minion_id = 'fake_id' +fake_minion_id = "fake_id" fake_pillar = {} -fake_args = ({'path': os.path.abspath( - os.path.join(base_path, '..', '..', 'integration', - 'files', 'saltclass', 'examples'))}) +fake_args = { + "path": os.path.abspath( + os.path.join( + base_path, "..", "..", "integration", "files", "saltclass", "examples" + ) + ) +} fake_opts = {} fake_salt = {} fake_grains = {} class SaltclassPillarTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.pillar.saltclass - ''' + """ + def setup_loader_modules(self): - return {saltclass: {'__opts__': fake_opts, - '__salt__': fake_salt, - '__grains__': fake_grains}} + return { + saltclass: { + "__opts__": fake_opts, + "__salt__": fake_salt, + "__grains__": fake_grains, + } + } def _runner(self, expected_ret): try: full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) - parsed_ret = full_ret['__saltclass__']['classes'] + parsed_ret = full_ret["__saltclass__"]["classes"] # Fail the test if we hit our NoneType error except TypeError as err: self.fail(err) @@ -43,26 +52,30 @@ class SaltclassPillarTestCase(TestCase, LoaderModuleMockMixin): self.assertListEqual(parsed_ret, expected_ret) def test_succeeds(self): - ret = ['default.users', 'default.motd', 'default.empty', 'default', 'roles.app'] + ret = ["default.users", "default.motd", "default.empty", "default", "roles.app"] self._runner(ret) class SaltclassPillarTestCaseListExpansion(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.pillar.saltclass variable expansion in list - ''' + """ + def setup_loader_modules(self): - return {saltclass: {'__opts__': fake_opts, - '__salt__': fake_salt, - '__grains__': fake_grains - }} + return { + saltclass: { + "__opts__": fake_opts, + "__salt__": fake_salt, + "__grains__": fake_grains, + } + } def _runner(self, expected_ret): full_ret = {} parsed_ret = [] try: full_ret = saltclass.ext_pillar(fake_minion_id, fake_pillar, fake_args) - parsed_ret = full_ret['test_list'] + parsed_ret = full_ret["test_list"] # Fail the test if we hit our NoneType error except TypeError as err: self.fail(err) @@ -70,5 +83,5 @@ class SaltclassPillarTestCaseListExpansion(TestCase, LoaderModuleMockMixin): self.assertListEqual(parsed_ret, expected_ret) def test_succeeds(self): - ret = [{'a': '192.168.10.10'}, '192.168.10.20'] + ret = [{"a": "192.168.10.10"}, "192.168.10.20"] self._runner(ret) diff --git a/tests/unit/pillar/test_sqlcipher.py b/tests/unit/pillar/test_sqlcipher.py index 84382a90aac..d7e9eed6f6b 100644 --- a/tests/unit/pillar/test_sqlcipher.py +++ b/tests/unit/pillar/test_sqlcipher.py @@ -3,160 +3,439 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase - # Import Salt Libs import salt.pillar.sqlcipher as sqlcipher +# Import Salt Testing libs +from tests.support.unit import TestCase + class SQLCipherPillarTestCase(TestCase): maxDiff = None def test_001_extract_queries_list(self): return_data = sqlcipher.SQLCipherExtPillar() - args, kwargs = [ - 'SELECT blah', - 'SELECT blah2', - ('SELECT blah3',), - ('SELECT blah4', 2), - {'query': 'SELECT blah5'}, - {'query': 'SELECT blah6', 'depth': 2}, - {'query': 'SELECT blah7', 'as_list': True}, - {'query': 'SELECT blah8', 'with_lists': '1'}, - {'query': 'SELECT blah9', 'with_lists': '1,2'} - ], {} + args, kwargs = ( + [ + "SELECT blah", + "SELECT blah2", + ("SELECT blah3",), + ("SELECT blah4", 2), + {"query": "SELECT blah5"}, + {"query": "SELECT blah6", "depth": 2}, + {"query": "SELECT blah7", "as_list": True}, + {"query": "SELECT blah8", "with_lists": "1"}, + {"query": "SELECT blah9", "with_lists": "1,2"}, + ], + {}, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah3', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah4', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah5', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah6', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah7', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah8', 'depth': 0, 'as_list': False, - 'with_lists': [1], 'ignore_null': False}], - [None, {'query': 'SELECT blah9', 'depth': 0, 'as_list': False, - 'with_lists': [1, 2], 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah3", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah4", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah5", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah6", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah7", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah8", + "depth": 0, + "as_list": False, + "with_lists": [1], + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah9", + "depth": 0, + "as_list": False, + "with_lists": [1, 2], + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_002_extract_queries_kwarg(self): return_data = sqlcipher.SQLCipherExtPillar() - args, kwargs = [], { - '1': 'SELECT blah', - '2': 'SELECT blah2', - '3': ('SELECT blah3',), - '4': ('SELECT blah4', 2), - '5': {'query': 'SELECT blah5'}, - '6': {'query': 'SELECT blah6', 'depth': 2}, - '7': {'query': 'SELECT blah7', 'as_list': True}, - } + args, kwargs = ( + [], + { + "1": "SELECT blah", + "2": "SELECT blah2", + "3": ("SELECT blah3",), + "4": ("SELECT blah4", 2), + "5": {"query": "SELECT blah5"}, + "6": {"query": "SELECT blah6", "depth": 2}, + "7": {"query": "SELECT blah7", "as_list": True}, + }, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - ['1', {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['2', {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['3', {'query': 'SELECT blah3', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['4', {'query': 'SELECT blah4', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['5', {'query': 'SELECT blah5', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['6', {'query': 'SELECT blah6', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['7', {'query': 'SELECT blah7', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + "1", + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "2", + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "3", + { + "query": "SELECT blah3", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "4", + { + "query": "SELECT blah4", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "5", + { + "query": "SELECT blah5", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "6", + { + "query": "SELECT blah6", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "7", + { + "query": "SELECT blah7", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_003_extract_queries_mixed(self): return_data = sqlcipher.SQLCipherExtPillar() - args, kwargs = [ - 'SELECT blah1', - ('SELECT blah2', 2), - {'query': 'SELECT blah3', 'as_list': True}, - ], { - '1': 'SELECT blah1', - '2': ('SELECT blah2', 2), - '3': {'query': 'SELECT blah3', 'as_list': True}, - } + args, kwargs = ( + [ + "SELECT blah1", + ("SELECT blah2", 2), + {"query": "SELECT blah3", "as_list": True}, + ], + { + "1": "SELECT blah1", + "2": ("SELECT blah2", 2), + "3": {"query": "SELECT blah3", "as_list": True}, + }, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah1', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah2', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah3', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}], - ['1', {'query': 'SELECT blah1', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['2', {'query': 'SELECT blah2', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['3', {'query': 'SELECT blah3', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah1", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah2", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah3", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "1", + { + "query": "SELECT blah1", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "2", + { + "query": "SELECT blah2", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "3", + { + "query": "SELECT blah3", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_004_extract_queries_bogus_list(self): # This test is specifically checking that empty queries are dropped return_data = sqlcipher.SQLCipherExtPillar() - args, kwargs = [ - 'SELECT blah', - '', - 'SELECT blah2', - ('SELECT blah3',), - ('',), - ('SELECT blah4', 2), - tuple(), - ('SELECT blah5',), - {'query': 'SELECT blah6'}, - {'query': ''}, - {'query': 'SELECT blah7', 'depth': 2}, - {'not_a_query': 'in sight!'}, - {'query': 'SELECT blah8', 'as_list': True}, - ], {} + args, kwargs = ( + [ + "SELECT blah", + "", + "SELECT blah2", + ("SELECT blah3",), + ("",), + ("SELECT blah4", 2), + tuple(), + ("SELECT blah5",), + {"query": "SELECT blah6"}, + {"query": ""}, + {"query": "SELECT blah7", "depth": 2}, + {"not_a_query": "in sight!"}, + {"query": "SELECT blah8", "as_list": True}, + ], + {}, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah3', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah4', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah5', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah6', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah7', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah8', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah3", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah4", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah5", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah6", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah7", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah8", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_005_extract_queries_bogus_kwargs(self): # this test is cut down as most of the path matches test_*_bogus_list return_data = sqlcipher.SQLCipherExtPillar() - args, kwargs = [], { - '1': 'SELECT blah', - '2': '', - '3': 'SELECT blah2' - } + args, kwargs = [], {"1": "SELECT blah", "2": "", "3": "SELECT blah2"} qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - ['1', {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['3', {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + "1", + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "3", + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_011_enter_root(self): return_data = sqlcipher.SQLCipherExtPillar() @@ -167,369 +446,295 @@ class SQLCipherPillarTestCase(TestCase): def test_021_process_fields(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) self.assertEqual(return_data.num_fields, 2) self.assertEqual(return_data.depth, 1) - return_data.process_fields(['a', 'b'], 2) + return_data.process_fields(["a", "b"], 2) self.assertEqual(return_data.num_fields, 2) self.assertEqual(return_data.depth, 1) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 3) - return_data.process_fields(['a', 'b', 'c', 'd'], 1) + return_data.process_fields(["a", "b", "c", "d"], 1) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 1) - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 2) - return_data.process_fields(['a', 'b', 'c', 'd'], 3) + return_data.process_fields(["a", "b", "c", "d"], 3) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 3) - return_data.process_fields(['a', 'b', 'c', 'd'], 4) + return_data.process_fields(["a", "b", "c", "d"], 4) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 3) def test_111_process_results_legacy(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) return_data.with_lists = [] return_data.process_results([[1, 2]]) - self.assertEqual( - {1: 2}, - return_data.result - ) + self.assertEqual({1: 2}, return_data.result) def test_112_process_results_legacy_multiple(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) return_data.with_lists = [] return_data.process_results([[1, 2], [3, 4], [5, 6]]) - self.assertEqual( - {1: 2, 3: 4, 5: 6}, - return_data.result - ) + self.assertEqual({1: 2, 3: 4, 5: 6}, return_data.result) def test_121_process_results_depth_0(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}}, 5: {6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}}, 5: {6: {7: 8}}}, return_data.result) def test_122_process_results_depth_1(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 1) + return_data.process_fields(["a", "b", "c", "d"], 1) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) self.assertEqual( - {1: {'b': 2, 'c': 3, 'd': 4}, 5: {'b': 6, 'c': 7, 'd': 8}}, - return_data.result + {1: {"b": 2, "c": 3, "d": 4}, 5: {"b": 6, "c": 7, "d": 8}}, + return_data.result, ) def test_123_process_results_depth_2(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) self.assertEqual( - {1: {2: {'c': 3, 'd': 4}}, 5: {6: {'c': 7, 'd': 8}}}, - return_data.result + {1: {2: {"c": 3, "d": 4}}, 5: {6: {"c": 7, "d": 8}}}, return_data.result ) def test_124_process_results_depth_3(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 3) + return_data.process_fields(["a", "b", "c", "d"], 3) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}}, 5: {6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}}, 5: {6: {7: 8}}}, return_data.result) def test_125_process_results_depth_4(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 4) + return_data.process_fields(["a", "b", "c", "d"], 4) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}}, 5: {6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}}, 5: {6: {7: 8}}}, return_data.result) def test_131_process_results_overwrite_legacy_multiple(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) return_data.with_lists = [] return_data.process_results([[1, 2], [3, 4], [1, 6]]) - self.assertEqual( - {1: 6, 3: 4}, - return_data.result - ) + self.assertEqual({1: 6, 3: 4}, return_data.result) def test_132_process_results_merge_depth_0(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}, 6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}, 6: {7: 8}}}, return_data.result) def test_133_process_results_overwrite_depth_0(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 2, 3, 8]]) - self.assertEqual( - {1: {2: {3: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 8}}}, return_data.result) def test_134_process_results_deepmerge_depth_0(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4, 7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4, 7: 8}}}, return_data.result) def test_135_process_results_overwrite_depth_1(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 1) + return_data.process_fields(["a", "b", "c", "d"], 1) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 6, 7, 8]]) - self.assertEqual( - {1: {'b': 6, 'c': 7, 'd': 8}}, - return_data.result - ) + self.assertEqual({1: {"b": 6, "c": 7, "d": 8}}, return_data.result) def test_136_process_results_merge_depth_2(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 6, 7, 8]]) self.assertEqual( - {1: {2: {'c': 3, 'd': 4}, 6: {'c': 7, 'd': 8}}}, - return_data.result + {1: {2: {"c": 3, "d": 4}, 6: {"c": 7, "d": 8}}}, return_data.result ) def test_137_process_results_overwrite_depth_2(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {'c': 7, 'd': 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {"c": 7, "d": 8}}}, return_data.result) def test_201_process_results_complexity_multiresults(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {'c': 7, 'd': 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {"c": 7, "d": 8}}}, return_data.result) def test_202_process_results_complexity_as_list(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.as_list = True return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {'c': [3, 7], 'd': [4, 8]}}}, - return_data.result - ) + self.assertEqual({1: {2: {"c": [3, 7], "d": [4, 8]}}}, return_data.result) def test_203_process_results_complexity_as_list_deeper(self): return_data = sqlcipher.SQLCipherExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.as_list = True return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 8]]) - self.assertEqual( - {1: {2: {3: [4, 8]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [4, 8]}}}, return_data.result) def test_204_process_results_complexity_as_list_mismatch_depth(self): return_data = sqlcipher.SQLCipherExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - self.assertEqual( - {1: {2: {3: [4, 5, {6: 7}]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [4, 5, {6: 7}]}}}, return_data.result) def test_205_process_results_complexity_as_list_mismatch_depth_reversed(self): return_data = sqlcipher.SQLCipherExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: [{6: 7, 8: 9}, 4, 5]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [{6: 7, 8: 9}, 4, 5]}}}, return_data.result) def test_206_process_results_complexity_as_list_mismatch_depth_weird_order(self): return_data = sqlcipher.SQLCipherExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: [{6: 7, }, 4, {8: 9}, 5]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [{6: 7}, 4, {8: 9}, 5]}}}, return_data.result) def test_207_process_results_complexity_collision_mismatch_depth(self): return_data = sqlcipher.SQLCipherExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - self.assertEqual( - {1: {2: {3: {6: 7}}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: {6: 7}}}}, return_data.result) def test_208_process_results_complexity_collision_mismatch_depth_reversed(self): return_data = sqlcipher.SQLCipherExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: 5}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 5}}}, return_data.result) def test_209_process_results_complexity_collision_mismatch_depth_weird_order(self): return_data = sqlcipher.SQLCipherExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: 5}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 5}}}, return_data.result) def test_20A_process_results_complexity_as_list_vary(self): return_data = sqlcipher.SQLCipherExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.as_list = False return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: 5}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 5}}}, return_data.result) def test_207_process_results_complexity_roots_collision(self): return_data = sqlcipher.SQLCipherExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.enter_root(1) return_data.process_results([[5, 6, 7, 8]]) - self.assertEqual( - {1: {5: {6: {7: 8}}}}, - return_data.result - ) + self.assertEqual({1: {5: {6: {7: 8}}}}, return_data.result) def test_301_process_results_with_lists(self): return_data = sqlcipher.SQLCipherExtPillar() return_data.as_list = False return_data.with_lists = [1, 3] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e', 'v'], 0) - return_data.process_results([['a', 'b', 'c', 'd', 'e', 1], - ['a', 'b', 'c', 'f', 'g', 2], - ['a', 'z', 'h', 'y', 'j', 3], - ['a', 'z', 'h', 'y', 'k', 4]]) - self.assertEqual(sorted( - {'a': [ - {'c': [ - {'e': 1}, - {'g': 2} - ] - }, - {'h': [ - {'j': 3, 'k': 4} - ] - } - ]}), - sorted(return_data.result) + return_data.process_fields(["a", "b", "c", "d", "e", "v"], 0) + return_data.process_results( + [ + ["a", "b", "c", "d", "e", 1], + ["a", "b", "c", "f", "g", 2], + ["a", "z", "h", "y", "j", 3], + ["a", "z", "h", "y", "k", 4], + ] + ) + self.assertEqual( + sorted({"a": [{"c": [{"e": 1}, {"g": 2}]}, {"h": [{"j": 3, "k": 4}]}]}), + sorted(return_data.result), ) def test_302_process_results_with_lists_consecutive(self): @@ -537,22 +742,16 @@ class SQLCipherPillarTestCase(TestCase): return_data.as_list = False return_data.with_lists = [1, 2, 3] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e', 'v'], 0) - return_data.process_results([['a', 'b', 'c', 'd', 'e', 1], - ['a', 'b', 'c', 'f', 'g', 2], - ['a', 'z', 'h', 'y', 'j', 3], - ['a', 'z', 'h', 'y', 'k', 4]]) - self.assertEqual(sorted( - {'a': [ - [[ - {'e': 1}, - {'g': 2} - ] - ], - [[ - {'j': 3, 'k': 4} - ] - ] - ]}), - sorted(return_data.result) + return_data.process_fields(["a", "b", "c", "d", "e", "v"], 0) + return_data.process_results( + [ + ["a", "b", "c", "d", "e", 1], + ["a", "b", "c", "f", "g", 2], + ["a", "z", "h", "y", "j", 3], + ["a", "z", "h", "y", "k", 4], + ] + ) + self.assertEqual( + sorted({"a": [[[{"e": 1}, {"g": 2}]], [[{"j": 3, "k": 4}]]]}), + sorted(return_data.result), ) diff --git a/tests/unit/pillar/test_sqlite3.py b/tests/unit/pillar/test_sqlite3.py index f22423b6e5f..da780682e75 100644 --- a/tests/unit/pillar/test_sqlite3.py +++ b/tests/unit/pillar/test_sqlite3.py @@ -3,160 +3,439 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase - # Import Salt Libs import salt.pillar.sqlite3 as sqlite3 +# Import Salt Testing libs +from tests.support.unit import TestCase + class SQLite3PillarTestCase(TestCase): maxDiff = None def test_001_extract_queries_list(self): return_data = sqlite3.SQLite3ExtPillar() - args, kwargs = [ - 'SELECT blah', - 'SELECT blah2', - ('SELECT blah3',), - ('SELECT blah4', 2), - {'query': 'SELECT blah5'}, - {'query': 'SELECT blah6', 'depth': 2}, - {'query': 'SELECT blah7', 'as_list': True}, - {'query': 'SELECT blah8', 'with_lists': '1'}, - {'query': 'SELECT blah9', 'with_lists': '1,2'} - ], {} + args, kwargs = ( + [ + "SELECT blah", + "SELECT blah2", + ("SELECT blah3",), + ("SELECT blah4", 2), + {"query": "SELECT blah5"}, + {"query": "SELECT blah6", "depth": 2}, + {"query": "SELECT blah7", "as_list": True}, + {"query": "SELECT blah8", "with_lists": "1"}, + {"query": "SELECT blah9", "with_lists": "1,2"}, + ], + {}, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah3', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah4', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah5', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah6', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah7', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah8', 'depth': 0, 'as_list': False, - 'with_lists': [1], 'ignore_null': False}], - [None, {'query': 'SELECT blah9', 'depth': 0, 'as_list': False, - 'with_lists': [1, 2], 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah3", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah4", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah5", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah6", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah7", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah8", + "depth": 0, + "as_list": False, + "with_lists": [1], + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah9", + "depth": 0, + "as_list": False, + "with_lists": [1, 2], + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_002_extract_queries_kwarg(self): return_data = sqlite3.SQLite3ExtPillar() - args, kwargs = [], { - '1': 'SELECT blah', - '2': 'SELECT blah2', - '3': ('SELECT blah3',), - '4': ('SELECT blah4', 2), - '5': {'query': 'SELECT blah5'}, - '6': {'query': 'SELECT blah6', 'depth': 2}, - '7': {'query': 'SELECT blah7', 'as_list': True}, - } + args, kwargs = ( + [], + { + "1": "SELECT blah", + "2": "SELECT blah2", + "3": ("SELECT blah3",), + "4": ("SELECT blah4", 2), + "5": {"query": "SELECT blah5"}, + "6": {"query": "SELECT blah6", "depth": 2}, + "7": {"query": "SELECT blah7", "as_list": True}, + }, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - ['1', {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['2', {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['3', {'query': 'SELECT blah3', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['4', {'query': 'SELECT blah4', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['5', {'query': 'SELECT blah5', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['6', {'query': 'SELECT blah6', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['7', {'query': 'SELECT blah7', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + "1", + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "2", + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "3", + { + "query": "SELECT blah3", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "4", + { + "query": "SELECT blah4", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "5", + { + "query": "SELECT blah5", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "6", + { + "query": "SELECT blah6", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "7", + { + "query": "SELECT blah7", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_003_extract_queries_mixed(self): return_data = sqlite3.SQLite3ExtPillar() - args, kwargs = [ - 'SELECT blah1', - ('SELECT blah2', 2), - {'query': 'SELECT blah3', 'as_list': True}, - ], { - '1': 'SELECT blah1', - '2': ('SELECT blah2', 2), - '3': {'query': 'SELECT blah3', 'as_list': True}, - } + args, kwargs = ( + [ + "SELECT blah1", + ("SELECT blah2", 2), + {"query": "SELECT blah3", "as_list": True}, + ], + { + "1": "SELECT blah1", + "2": ("SELECT blah2", 2), + "3": {"query": "SELECT blah3", "as_list": True}, + }, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah1', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah2', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah3', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}], - ['1', {'query': 'SELECT blah1', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['2', {'query': 'SELECT blah2', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['3', {'query': 'SELECT blah3', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah1", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah2", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah3", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "1", + { + "query": "SELECT blah1", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "2", + { + "query": "SELECT blah2", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "3", + { + "query": "SELECT blah3", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_004_extract_queries_bogus_list(self): # This test is specifically checking that empty queries are dropped return_data = sqlite3.SQLite3ExtPillar() - args, kwargs = [ - 'SELECT blah', - '', - 'SELECT blah2', - ('SELECT blah3',), - ('',), - ('SELECT blah4', 2), - tuple(), - ('SELECT blah5',), - {'query': 'SELECT blah6'}, - {'query': ''}, - {'query': 'SELECT blah7', 'depth': 2}, - {'not_a_query': 'in sight!'}, - {'query': 'SELECT blah8', 'as_list': True}, - ], {} + args, kwargs = ( + [ + "SELECT blah", + "", + "SELECT blah2", + ("SELECT blah3",), + ("",), + ("SELECT blah4", 2), + tuple(), + ("SELECT blah5",), + {"query": "SELECT blah6"}, + {"query": ""}, + {"query": "SELECT blah7", "depth": 2}, + {"not_a_query": "in sight!"}, + {"query": "SELECT blah8", "as_list": True}, + ], + {}, + ) qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - [None, {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah3', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah4', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah5', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah6', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah7', 'depth': 2, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - [None, {'query': 'SELECT blah8', 'depth': 0, 'as_list': True, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + None, + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah3", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah4", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah5", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah6", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah7", + "depth": 2, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + None, + { + "query": "SELECT blah8", + "depth": 0, + "as_list": True, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_005_extract_queries_bogus_kwargs(self): # this test is cut down as most of the path matches test_*_bogus_list return_data = sqlite3.SQLite3ExtPillar() - args, kwargs = [], { - '1': 'SELECT blah', - '2': '', - '3': 'SELECT blah2' - } + args, kwargs = [], {"1": "SELECT blah", "2": "", "3": "SELECT blah2"} qbuffer = return_data.extract_queries(args, kwargs) - self.assertEqual([ - ['1', {'query': 'SELECT blah', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}], - ['3', {'query': 'SELECT blah2', 'depth': 0, 'as_list': False, - 'with_lists': None, 'ignore_null': False}] - ], qbuffer) + self.assertEqual( + [ + [ + "1", + { + "query": "SELECT blah", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + [ + "3", + { + "query": "SELECT blah2", + "depth": 0, + "as_list": False, + "with_lists": None, + "ignore_null": False, + }, + ], + ], + qbuffer, + ) def test_011_enter_root(self): return_data = sqlite3.SQLite3ExtPillar() @@ -167,369 +446,295 @@ class SQLite3PillarTestCase(TestCase): def test_021_process_fields(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) self.assertEqual(return_data.num_fields, 2) self.assertEqual(return_data.depth, 1) - return_data.process_fields(['a', 'b'], 2) + return_data.process_fields(["a", "b"], 2) self.assertEqual(return_data.num_fields, 2) self.assertEqual(return_data.depth, 1) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 3) - return_data.process_fields(['a', 'b', 'c', 'd'], 1) + return_data.process_fields(["a", "b", "c", "d"], 1) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 1) - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 2) - return_data.process_fields(['a', 'b', 'c', 'd'], 3) + return_data.process_fields(["a", "b", "c", "d"], 3) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 3) - return_data.process_fields(['a', 'b', 'c', 'd'], 4) + return_data.process_fields(["a", "b", "c", "d"], 4) self.assertEqual(return_data.num_fields, 4) self.assertEqual(return_data.depth, 3) def test_111_process_results_legacy(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) return_data.with_lists = [] return_data.process_results([[1, 2]]) - self.assertEqual( - {1: 2}, - return_data.result - ) + self.assertEqual({1: 2}, return_data.result) def test_112_process_results_legacy_multiple(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) return_data.with_lists = [] return_data.process_results([[1, 2], [3, 4], [5, 6]]) - self.assertEqual( - {1: 2, 3: 4, 5: 6}, - return_data.result - ) + self.assertEqual({1: 2, 3: 4, 5: 6}, return_data.result) def test_121_process_results_depth_0(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}}, 5: {6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}}, 5: {6: {7: 8}}}, return_data.result) def test_122_process_results_depth_1(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 1) + return_data.process_fields(["a", "b", "c", "d"], 1) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) self.assertEqual( - {1: {'b': 2, 'c': 3, 'd': 4}, 5: {'b': 6, 'c': 7, 'd': 8}}, - return_data.result + {1: {"b": 2, "c": 3, "d": 4}, 5: {"b": 6, "c": 7, "d": 8}}, + return_data.result, ) def test_123_process_results_depth_2(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) self.assertEqual( - {1: {2: {'c': 3, 'd': 4}}, 5: {6: {'c': 7, 'd': 8}}}, - return_data.result + {1: {2: {"c": 3, "d": 4}}, 5: {6: {"c": 7, "d": 8}}}, return_data.result ) def test_124_process_results_depth_3(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 3) + return_data.process_fields(["a", "b", "c", "d"], 3) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}}, 5: {6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}}, 5: {6: {7: 8}}}, return_data.result) def test_125_process_results_depth_4(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 4) + return_data.process_fields(["a", "b", "c", "d"], 4) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [5, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}}, 5: {6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}}, 5: {6: {7: 8}}}, return_data.result) def test_131_process_results_overwrite_legacy_multiple(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b'], 0) + return_data.process_fields(["a", "b"], 0) return_data.with_lists = [] return_data.process_results([[1, 2], [3, 4], [1, 6]]) - self.assertEqual( - {1: 6, 3: 4}, - return_data.result - ) + self.assertEqual({1: 6, 3: 4}, return_data.result) def test_132_process_results_merge_depth_0(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 6, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4}, 6: {7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4}, 6: {7: 8}}}, return_data.result) def test_133_process_results_overwrite_depth_0(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 2, 3, 8]]) - self.assertEqual( - {1: {2: {3: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 8}}}, return_data.result) def test_134_process_results_deepmerge_depth_0(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {3: 4, 7: 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 4, 7: 8}}}, return_data.result) def test_135_process_results_overwrite_depth_1(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 1) + return_data.process_fields(["a", "b", "c", "d"], 1) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 6, 7, 8]]) - self.assertEqual( - {1: {'b': 6, 'c': 7, 'd': 8}}, - return_data.result - ) + self.assertEqual({1: {"b": 6, "c": 7, "d": 8}}, return_data.result) def test_136_process_results_merge_depth_2(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 6, 7, 8]]) self.assertEqual( - {1: {2: {'c': 3, 'd': 4}, 6: {'c': 7, 'd': 8}}}, - return_data.result + {1: {2: {"c": 3, "d": 4}, 6: {"c": 7, "d": 8}}}, return_data.result ) def test_137_process_results_overwrite_depth_2(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4], [1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {'c': 7, 'd': 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {"c": 7, "d": 8}}}, return_data.result) def test_201_process_results_complexity_multiresults(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {'c': 7, 'd': 8}}}, - return_data.result - ) + self.assertEqual({1: {2: {"c": 7, "d": 8}}}, return_data.result) def test_202_process_results_complexity_as_list(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 2) + return_data.process_fields(["a", "b", "c", "d"], 2) return_data.with_lists = [] return_data.enter_root(None) return_data.as_list = True return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 7, 8]]) - self.assertEqual( - {1: {2: {'c': [3, 7], 'd': [4, 8]}}}, - return_data.result - ) + self.assertEqual({1: {2: {"c": [3, 7], "d": [4, 8]}}}, return_data.result) def test_203_process_results_complexity_as_list_deeper(self): return_data = sqlite3.SQLite3ExtPillar() - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.with_lists = [] return_data.enter_root(None) return_data.as_list = True return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 8]]) - self.assertEqual( - {1: {2: {3: [4, 8]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [4, 8]}}}, return_data.result) def test_204_process_results_complexity_as_list_mismatch_depth(self): return_data = sqlite3.SQLite3ExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - self.assertEqual( - {1: {2: {3: [4, 5, {6: 7}]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [4, 5, {6: 7}]}}}, return_data.result) def test_205_process_results_complexity_as_list_mismatch_depth_reversed(self): return_data = sqlite3.SQLite3ExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: [{6: 7, 8: 9}, 4, 5]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [{6: 7, 8: 9}, 4, 5]}}}, return_data.result) def test_206_process_results_complexity_as_list_mismatch_depth_weird_order(self): return_data = sqlite3.SQLite3ExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: [{6: 7, }, 4, {8: 9}, 5]}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: [{6: 7}, 4, {8: 9}, 5]}}}, return_data.result) def test_207_process_results_complexity_collision_mismatch_depth(self): return_data = sqlite3.SQLite3ExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - self.assertEqual( - {1: {2: {3: {6: 7}}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: {6: 7}}}}, return_data.result) def test_208_process_results_complexity_collision_mismatch_depth_reversed(self): return_data = sqlite3.SQLite3ExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: 5}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 5}}}, return_data.result) def test_209_process_results_complexity_collision_mismatch_depth_weird_order(self): return_data = sqlite3.SQLite3ExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: 5}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 5}}}, return_data.result) def test_20A_process_results_complexity_as_list_vary(self): return_data = sqlite3.SQLite3ExtPillar() return_data.as_list = True return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e'], 0) + return_data.process_fields(["a", "b", "c", "d", "e"], 0) return_data.process_results([[1, 2, 3, 6, 7]]) return_data.process_results([[1, 2, 3, 8, 9]]) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.as_list = False return_data.process_results([[1, 2, 3, 5]]) - self.assertEqual( - {1: {2: {3: 5}}}, - return_data.result - ) + self.assertEqual({1: {2: {3: 5}}}, return_data.result) def test_207_process_results_complexity_roots_collision(self): return_data = sqlite3.SQLite3ExtPillar() return_data.as_list = False return_data.with_lists = [] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd'], 0) + return_data.process_fields(["a", "b", "c", "d"], 0) return_data.process_results([[1, 2, 3, 4]]) return_data.enter_root(1) return_data.process_results([[5, 6, 7, 8]]) - self.assertEqual( - {1: {5: {6: {7: 8}}}}, - return_data.result - ) + self.assertEqual({1: {5: {6: {7: 8}}}}, return_data.result) def test_301_process_results_with_lists(self): return_data = sqlite3.SQLite3ExtPillar() return_data.as_list = False return_data.with_lists = [1, 3] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e', 'v'], 0) - return_data.process_results([['a', 'b', 'c', 'd', 'e', 1], - ['a', 'b', 'c', 'f', 'g', 2], - ['a', 'z', 'h', 'y', 'j', 3], - ['a', 'z', 'h', 'y', 'k', 4]]) - self.assertEqual(sorted( - {'a': [ - {'c': [ - {'e': 1}, - {'g': 2} - ] - }, - {'h': [ - {'j': 3, 'k': 4} - ] - } - ]}), - sorted(return_data.result) + return_data.process_fields(["a", "b", "c", "d", "e", "v"], 0) + return_data.process_results( + [ + ["a", "b", "c", "d", "e", 1], + ["a", "b", "c", "f", "g", 2], + ["a", "z", "h", "y", "j", 3], + ["a", "z", "h", "y", "k", 4], + ] + ) + self.assertEqual( + sorted({"a": [{"c": [{"e": 1}, {"g": 2}]}, {"h": [{"j": 3, "k": 4}]}]}), + sorted(return_data.result), ) def test_302_process_results_with_lists_consecutive(self): @@ -537,22 +742,16 @@ class SQLite3PillarTestCase(TestCase): return_data.as_list = False return_data.with_lists = [1, 2, 3] return_data.enter_root(None) - return_data.process_fields(['a', 'b', 'c', 'd', 'e', 'v'], 0) - return_data.process_results([['a', 'b', 'c', 'd', 'e', 1], - ['a', 'b', 'c', 'f', 'g', 2], - ['a', 'z', 'h', 'y', 'j', 3], - ['a', 'z', 'h', 'y', 'k', 4]]) - self.assertEqual(sorted( - {'a': [ - [[ - {'e': 1}, - {'g': 2} - ] - ], - [[ - {'j': 3, 'k': 4} - ] - ] - ]}), - sorted(return_data.result) + return_data.process_fields(["a", "b", "c", "d", "e", "v"], 0) + return_data.process_results( + [ + ["a", "b", "c", "d", "e", 1], + ["a", "b", "c", "f", "g", 2], + ["a", "z", "h", "y", "j", 3], + ["a", "z", "h", "y", "k", 4], + ] + ) + self.assertEqual( + sorted({"a": [[[{"e": 1}, {"g": 2}]], [[{"j": 3, "k": 4}]]]}), + sorted(return_data.result), ) diff --git a/tests/unit/proxy/test_esxcluster.py b/tests/unit/proxy/test_esxcluster.py index a7e79394c1b..cb5fcb51f4e 100644 --- a/tests/unit/proxy/test_esxcluster.py +++ b/tests/unit/proxy/test_esxcluster.py @@ -1,63 +1,79 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstanley.com>` Tests for esxcluster proxy -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import external libs -try: - import jsonschema - HAS_JSONSCHEMA = True -except ImportError: - HAS_JSONSCHEMA = False +import salt.exceptions # Import Salt Libs import salt.proxy.esxcluster as esxcluster -import salt.exceptions from salt.config.schemas.esxcluster import EsxclusterProxySchema # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) + +# Import external libs +try: + import jsonschema + + HAS_JSONSCHEMA = True +except ImportError: + HAS_JSONSCHEMA = False -@skipIf(not HAS_JSONSCHEMA, 'jsonschema is required') +@skipIf(not HAS_JSONSCHEMA, "jsonschema is required") class InitTestCase(TestCase, LoaderModuleMockMixin): - '''Tests for salt.proxy.esxcluster.init''' + """Tests for salt.proxy.esxcluster.init""" + def setup_loader_modules(self): - return {esxcluster: {'__virtual__': - MagicMock(return_value='esxcluster'), - 'DETAILS': {}, '__pillar__': {}}} + return { + esxcluster: { + "__virtual__": MagicMock(return_value="esxcluster"), + "DETAILS": {}, + "__pillar__": {}, + } + } def setUp(self): - self.opts_userpass = {'proxy': {'proxytype': 'esxcluster', - 'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'cluster': 'fake_cluster', - 'mechanism': 'userpass', - 'username': 'fake_username', - 'passwords': ['fake_password'], - 'protocol': 'fake_protocol', - 'port': 100}} - self.opts_sspi = {'proxy': {'proxytype': 'esxcluster', - 'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'cluster': 'fake_cluster', - 'mechanism': 'sspi', - 'domain': 'fake_domain', - 'principal': 'fake_principal', - 'protocol': 'fake_protocol', - 'port': 100}} - patches = (('salt.proxy.esxcluster.merge', - MagicMock(return_value=self.opts_sspi['proxy'])),) + self.opts_userpass = { + "proxy": { + "proxytype": "esxcluster", + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "cluster": "fake_cluster", + "mechanism": "userpass", + "username": "fake_username", + "passwords": ["fake_password"], + "protocol": "fake_protocol", + "port": 100, + } + } + self.opts_sspi = { + "proxy": { + "proxytype": "esxcluster", + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "cluster": "fake_cluster", + "mechanism": "sspi", + "domain": "fake_domain", + "principal": "fake_principal", + "protocol": "fake_protocol", + "port": 100, + } + } + patches = ( + ( + "salt.proxy.esxcluster.merge", + MagicMock(return_value=self.opts_sspi["proxy"]), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() @@ -66,117 +82,136 @@ class InitTestCase(TestCase, LoaderModuleMockMixin): def test_merge(self): mock_pillar_proxy = MagicMock() mock_opts_proxy = MagicMock() - mock_merge = MagicMock(return_value=self.opts_sspi['proxy']) - with patch.dict(esxcluster.__pillar__, - {'proxy': mock_pillar_proxy}): - with patch('salt.proxy.esxcluster.merge', mock_merge): - esxcluster.init(opts={'proxy': mock_opts_proxy}) + mock_merge = MagicMock(return_value=self.opts_sspi["proxy"]) + with patch.dict(esxcluster.__pillar__, {"proxy": mock_pillar_proxy}): + with patch("salt.proxy.esxcluster.merge", mock_merge): + esxcluster.init(opts={"proxy": mock_opts_proxy}) mock_merge.assert_called_once_with(mock_opts_proxy, mock_pillar_proxy) def test_esxcluster_schema(self): mock_json_validate = MagicMock() serialized_schema = EsxclusterProxySchema().serialize() - with patch('salt.proxy.esxcluster.jsonschema.validate', - mock_json_validate): + with patch("salt.proxy.esxcluster.jsonschema.validate", mock_json_validate): esxcluster.init(self.opts_sspi) mock_json_validate.assert_called_once_with( - self.opts_sspi['proxy'], serialized_schema) + self.opts_sspi["proxy"], serialized_schema + ) def test_invalid_proxy_input_error(self): - with patch('salt.proxy.esxcluster.jsonschema.validate', - MagicMock(side_effect=jsonschema.exceptions.ValidationError( - 'Validation Error'))): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + with patch( + "salt.proxy.esxcluster.jsonschema.validate", + MagicMock( + side_effect=jsonschema.exceptions.ValidationError("Validation Error") + ), + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxcluster.init(self.opts_userpass) - self.assertEqual(excinfo.exception.strerror, - 'Validation Error') + self.assertEqual(excinfo.exception.strerror, "Validation Error") def test_no_username(self): opts = self.opts_userpass.copy() - del opts['proxy']['username'] - with patch('salt.proxy.esxcluster.merge', - MagicMock(return_value=opts['proxy'])): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + del opts["proxy"]["username"] + with patch( + "salt.proxy.esxcluster.merge", MagicMock(return_value=opts["proxy"]) + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxcluster.init(opts) - self.assertEqual(excinfo.exception.strerror, - 'Mechanism is set to \'userpass\', but no ' - '\'username\' key found in proxy config.') + self.assertEqual( + excinfo.exception.strerror, + "Mechanism is set to 'userpass', but no " + "'username' key found in proxy config.", + ) def test_no_passwords(self): opts = self.opts_userpass.copy() - del opts['proxy']['passwords'] - with patch('salt.proxy.esxcluster.merge', - MagicMock(return_value=opts['proxy'])): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + del opts["proxy"]["passwords"] + with patch( + "salt.proxy.esxcluster.merge", MagicMock(return_value=opts["proxy"]) + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxcluster.init(opts) - self.assertEqual(excinfo.exception.strerror, - 'Mechanism is set to \'userpass\', but no ' - '\'passwords\' key found in proxy config.') + self.assertEqual( + excinfo.exception.strerror, + "Mechanism is set to 'userpass', but no " + "'passwords' key found in proxy config.", + ) def test_no_domain(self): opts = self.opts_sspi.copy() - del opts['proxy']['domain'] - with patch('salt.proxy.esxcluster.merge', - MagicMock(return_value=opts['proxy'])): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + del opts["proxy"]["domain"] + with patch( + "salt.proxy.esxcluster.merge", MagicMock(return_value=opts["proxy"]) + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxcluster.init(opts) - self.assertEqual(excinfo.exception.strerror, - 'Mechanism is set to \'sspi\', but no ' - '\'domain\' key found in proxy config.') + self.assertEqual( + excinfo.exception.strerror, + "Mechanism is set to 'sspi', but no " "'domain' key found in proxy config.", + ) def test_no_principal(self): opts = self.opts_sspi.copy() - del opts['proxy']['principal'] - with patch('salt.proxy.esxcluster.merge', - MagicMock(return_value=opts['proxy'])): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + del opts["proxy"]["principal"] + with patch( + "salt.proxy.esxcluster.merge", MagicMock(return_value=opts["proxy"]) + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxcluster.init(opts) - self.assertEqual(excinfo.exception.strerror, - 'Mechanism is set to \'sspi\', but no ' - '\'principal\' key found in proxy config.') + self.assertEqual( + excinfo.exception.strerror, + "Mechanism is set to 'sspi', but no " + "'principal' key found in proxy config.", + ) def test_find_credentials(self): - mock_find_credentials = MagicMock(return_value=('fake_username', - 'fake_password')) - with patch('salt.proxy.esxcluster.merge', - MagicMock(return_value=self.opts_userpass['proxy'])): - with patch('salt.proxy.esxcluster.find_credentials', - mock_find_credentials): + mock_find_credentials = MagicMock( + return_value=("fake_username", "fake_password") + ) + with patch( + "salt.proxy.esxcluster.merge", + MagicMock(return_value=self.opts_userpass["proxy"]), + ): + with patch("salt.proxy.esxcluster.find_credentials", mock_find_credentials): esxcluster.init(self.opts_userpass) mock_find_credentials.assert_called_once_with() def test_details_userpass(self): - mock_find_credentials = MagicMock(return_value=('fake_username', - 'fake_password')) - with patch('salt.proxy.esxcluster.merge', - MagicMock(return_value=self.opts_userpass['proxy'])): - with patch('salt.proxy.esxcluster.find_credentials', - mock_find_credentials): + mock_find_credentials = MagicMock( + return_value=("fake_username", "fake_password") + ) + with patch( + "salt.proxy.esxcluster.merge", + MagicMock(return_value=self.opts_userpass["proxy"]), + ): + with patch("salt.proxy.esxcluster.find_credentials", mock_find_credentials): esxcluster.init(self.opts_userpass) - self.assertDictEqual(esxcluster.DETAILS, - {'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'cluster': 'fake_cluster', - 'mechanism': 'userpass', - 'username': 'fake_username', - 'password': 'fake_password', - 'passwords': ['fake_password'], - 'protocol': 'fake_protocol', - 'port': 100}) + self.assertDictEqual( + esxcluster.DETAILS, + { + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "cluster": "fake_cluster", + "mechanism": "userpass", + "username": "fake_username", + "password": "fake_password", + "passwords": ["fake_password"], + "protocol": "fake_protocol", + "port": 100, + }, + ) def test_details_sspi(self): esxcluster.init(self.opts_sspi) - self.assertDictEqual(esxcluster.DETAILS, - {'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'cluster': 'fake_cluster', - 'mechanism': 'sspi', - 'domain': 'fake_domain', - 'principal': 'fake_principal', - 'protocol': 'fake_protocol', - 'port': 100}) + self.assertDictEqual( + esxcluster.DETAILS, + { + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "cluster": "fake_cluster", + "mechanism": "sspi", + "domain": "fake_domain", + "principal": "fake_principal", + "protocol": "fake_protocol", + "port": 100, + }, + ) diff --git a/tests/unit/proxy/test_esxdatacenter.py b/tests/unit/proxy/test_esxdatacenter.py index 89f2e55881a..5a51b76cb7f 100644 --- a/tests/unit/proxy/test_esxdatacenter.py +++ b/tests/unit/proxy/test_esxdatacenter.py @@ -1,61 +1,77 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstanley.com>` Tests for esxdatacenter proxy -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import external libs -try: - import jsonschema - HAS_JSONSCHEMA = True -except ImportError: - HAS_JSONSCHEMA = False +import salt.exceptions # Import Salt Libs import salt.proxy.esxdatacenter as esxdatacenter -import salt.exceptions from salt.config.schemas.esxdatacenter import EsxdatacenterProxySchema # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) + +# Import external libs +try: + import jsonschema + + HAS_JSONSCHEMA = True +except ImportError: + HAS_JSONSCHEMA = False -@skipIf(not HAS_JSONSCHEMA, 'jsonschema is required') +@skipIf(not HAS_JSONSCHEMA, "jsonschema is required") class InitTestCase(TestCase, LoaderModuleMockMixin): - '''Tests for salt.proxy.esxdatacenter.init''' + """Tests for salt.proxy.esxdatacenter.init""" + def setup_loader_modules(self): - return {esxdatacenter: {'__virtual__': - MagicMock(return_value='esxdatacenter'), - 'DETAILS': {}, '__pillar__': {}}} + return { + esxdatacenter: { + "__virtual__": MagicMock(return_value="esxdatacenter"), + "DETAILS": {}, + "__pillar__": {}, + } + } def setUp(self): - self.opts_userpass = {'proxy': {'proxytype': 'esxdatacenter', - 'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'mechanism': 'userpass', - 'username': 'fake_username', - 'passwords': ['fake_password'], - 'protocol': 'fake_protocol', - 'port': 100}} - self.opts_sspi = {'proxy': {'proxytype': 'esxdatacenter', - 'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'mechanism': 'sspi', - 'domain': 'fake_domain', - 'principal': 'fake_principal', - 'protocol': 'fake_protocol', - 'port': 100}} - patches = (('salt.proxy.esxdatacenter.merge', - MagicMock(return_value=self.opts_sspi['proxy'])),) + self.opts_userpass = { + "proxy": { + "proxytype": "esxdatacenter", + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "mechanism": "userpass", + "username": "fake_username", + "passwords": ["fake_password"], + "protocol": "fake_protocol", + "port": 100, + } + } + self.opts_sspi = { + "proxy": { + "proxytype": "esxdatacenter", + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "mechanism": "sspi", + "domain": "fake_domain", + "principal": "fake_principal", + "protocol": "fake_protocol", + "port": 100, + } + } + patches = ( + ( + "salt.proxy.esxdatacenter.merge", + MagicMock(return_value=self.opts_sspi["proxy"]), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() @@ -64,115 +80,138 @@ class InitTestCase(TestCase, LoaderModuleMockMixin): def test_merge(self): mock_pillar_proxy = MagicMock() mock_opts_proxy = MagicMock() - mock_merge = MagicMock(return_value=self.opts_sspi['proxy']) - with patch.dict(esxdatacenter.__pillar__, - {'proxy': mock_pillar_proxy}): - with patch('salt.proxy.esxdatacenter.merge', mock_merge): - esxdatacenter.init(opts={'proxy': mock_opts_proxy}) + mock_merge = MagicMock(return_value=self.opts_sspi["proxy"]) + with patch.dict(esxdatacenter.__pillar__, {"proxy": mock_pillar_proxy}): + with patch("salt.proxy.esxdatacenter.merge", mock_merge): + esxdatacenter.init(opts={"proxy": mock_opts_proxy}) mock_merge.assert_called_once_with(mock_opts_proxy, mock_pillar_proxy) def test_esxdatacenter_schema(self): mock_json_validate = MagicMock() serialized_schema = EsxdatacenterProxySchema().serialize() - with patch('salt.proxy.esxdatacenter.jsonschema.validate', - mock_json_validate): + with patch("salt.proxy.esxdatacenter.jsonschema.validate", mock_json_validate): esxdatacenter.init(self.opts_sspi) mock_json_validate.assert_called_once_with( - self.opts_sspi['proxy'], serialized_schema) + self.opts_sspi["proxy"], serialized_schema + ) def test_invalid_proxy_input_error(self): - with patch('salt.proxy.esxdatacenter.jsonschema.validate', - MagicMock(side_effect=jsonschema.exceptions.ValidationError( - 'Validation Error'))): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + with patch( + "salt.proxy.esxdatacenter.jsonschema.validate", + MagicMock( + side_effect=jsonschema.exceptions.ValidationError("Validation Error") + ), + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxdatacenter.init(self.opts_userpass) - self.assertEqual(excinfo.exception.strerror, - 'Validation Error') + self.assertEqual(excinfo.exception.strerror, "Validation Error") def test_no_username(self): opts = self.opts_userpass.copy() - del opts['proxy']['username'] - with patch('salt.proxy.esxdatacenter.merge', - MagicMock(return_value=opts['proxy'])): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + del opts["proxy"]["username"] + with patch( + "salt.proxy.esxdatacenter.merge", MagicMock(return_value=opts["proxy"]) + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxdatacenter.init(opts) - self.assertEqual(excinfo.exception.strerror, - 'Mechanism is set to \'userpass\', but no ' - '\'username\' key found in proxy config.') + self.assertEqual( + excinfo.exception.strerror, + "Mechanism is set to 'userpass', but no " + "'username' key found in proxy config.", + ) def test_no_passwords(self): opts = self.opts_userpass.copy() - del opts['proxy']['passwords'] - with patch('salt.proxy.esxdatacenter.merge', - MagicMock(return_value=opts['proxy'])): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + del opts["proxy"]["passwords"] + with patch( + "salt.proxy.esxdatacenter.merge", MagicMock(return_value=opts["proxy"]) + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxdatacenter.init(opts) - self.assertEqual(excinfo.exception.strerror, - 'Mechanism is set to \'userpass\', but no ' - '\'passwords\' key found in proxy config.') + self.assertEqual( + excinfo.exception.strerror, + "Mechanism is set to 'userpass', but no " + "'passwords' key found in proxy config.", + ) def test_no_domain(self): opts = self.opts_sspi.copy() - del opts['proxy']['domain'] - with patch('salt.proxy.esxdatacenter.merge', - MagicMock(return_value=opts['proxy'])): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + del opts["proxy"]["domain"] + with patch( + "salt.proxy.esxdatacenter.merge", MagicMock(return_value=opts["proxy"]) + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxdatacenter.init(opts) - self.assertEqual(excinfo.exception.strerror, - 'Mechanism is set to \'sspi\', but no ' - '\'domain\' key found in proxy config.') + self.assertEqual( + excinfo.exception.strerror, + "Mechanism is set to 'sspi', but no " "'domain' key found in proxy config.", + ) def test_no_principal(self): opts = self.opts_sspi.copy() - del opts['proxy']['principal'] - with patch('salt.proxy.esxdatacenter.merge', - MagicMock(return_value=opts['proxy'])): - with self.assertRaises(salt.exceptions.InvalidConfigError) as \ - excinfo: + del opts["proxy"]["principal"] + with patch( + "salt.proxy.esxdatacenter.merge", MagicMock(return_value=opts["proxy"]) + ): + with self.assertRaises(salt.exceptions.InvalidConfigError) as excinfo: esxdatacenter.init(opts) - self.assertEqual(excinfo.exception.strerror, - 'Mechanism is set to \'sspi\', but no ' - '\'principal\' key found in proxy config.') + self.assertEqual( + excinfo.exception.strerror, + "Mechanism is set to 'sspi', but no " + "'principal' key found in proxy config.", + ) def test_find_credentials(self): - mock_find_credentials = MagicMock(return_value=('fake_username', - 'fake_password')) - with patch('salt.proxy.esxdatacenter.merge', - MagicMock(return_value=self.opts_userpass['proxy'])): - with patch('salt.proxy.esxdatacenter.find_credentials', - mock_find_credentials): + mock_find_credentials = MagicMock( + return_value=("fake_username", "fake_password") + ) + with patch( + "salt.proxy.esxdatacenter.merge", + MagicMock(return_value=self.opts_userpass["proxy"]), + ): + with patch( + "salt.proxy.esxdatacenter.find_credentials", mock_find_credentials + ): esxdatacenter.init(self.opts_userpass) mock_find_credentials.assert_called_once_with() def test_details_userpass(self): - mock_find_credentials = MagicMock(return_value=('fake_username', - 'fake_password')) - with patch('salt.proxy.esxdatacenter.merge', - MagicMock(return_value=self.opts_userpass['proxy'])): - with patch('salt.proxy.esxdatacenter.find_credentials', - mock_find_credentials): + mock_find_credentials = MagicMock( + return_value=("fake_username", "fake_password") + ) + with patch( + "salt.proxy.esxdatacenter.merge", + MagicMock(return_value=self.opts_userpass["proxy"]), + ): + with patch( + "salt.proxy.esxdatacenter.find_credentials", mock_find_credentials + ): esxdatacenter.init(self.opts_userpass) - self.assertDictEqual(esxdatacenter.DETAILS, - {'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'mechanism': 'userpass', - 'username': 'fake_username', - 'password': 'fake_password', - 'passwords': ['fake_password'], - 'protocol': 'fake_protocol', - 'port': 100}) + self.assertDictEqual( + esxdatacenter.DETAILS, + { + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "mechanism": "userpass", + "username": "fake_username", + "password": "fake_password", + "passwords": ["fake_password"], + "protocol": "fake_protocol", + "port": 100, + }, + ) def test_details_sspi(self): esxdatacenter.init(self.opts_sspi) - self.assertDictEqual(esxdatacenter.DETAILS, - {'vcenter': 'fake_vcenter', - 'datacenter': 'fake_dc', - 'mechanism': 'sspi', - 'domain': 'fake_domain', - 'principal': 'fake_principal', - 'protocol': 'fake_protocol', - 'port': 100}) + self.assertDictEqual( + esxdatacenter.DETAILS, + { + "vcenter": "fake_vcenter", + "datacenter": "fake_dc", + "mechanism": "sspi", + "domain": "fake_domain", + "principal": "fake_principal", + "protocol": "fake_protocol", + "port": 100, + }, + ) diff --git a/tests/unit/proxy/test_napalm.py b/tests/unit/proxy/test_napalm.py index a450fa3cd38..6d631ac640d 100644 --- a/tests/unit/proxy/test_napalm.py +++ b/tests/unit/proxy/test_napalm.py @@ -1,53 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Anthony Shaw <anthonyshaw@apache.org>` -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import tests.support.napalm as napalm_test_support + # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) -import tests.support.napalm as napalm_test_support import salt.proxy.napalm as napalm_proxy # NOQA -TEST_OPTS = { - 'proxytype': 'napalm', - 'driver': 'junos', - 'host': 'core05.nrt02' -} +TEST_OPTS = {"proxytype": "napalm", "driver": "junos", "host": "core05.nrt02"} def mock_get_device(opts, *args, **kwargs): assert opts == TEST_OPTS - return { - 'DRIVER': napalm_test_support.MockNapalmDevice(), - 'UP': True - } + return {"DRIVER": napalm_test_support.MockNapalmDevice(), "UP": True} -@patch('salt.utils.napalm.get_device', mock_get_device) +@patch("salt.utils.napalm.get_device", mock_get_device) class NapalmProxyTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': { - 'config.option': MagicMock(return_value={ - 'test': { - 'driver': 'test', - 'key': '2orgk34kgk34g' - } - }) + "__salt__": { + "config.option": MagicMock( + return_value={"test": {"driver": "test", "key": "2orgk34kgk34g"}} + ) } } - module_globals['napalm_base'] = MagicMock() + module_globals["napalm_base"] = MagicMock() return {napalm_proxy: module_globals} def test_init(self): @@ -71,21 +58,21 @@ class NapalmProxyTestCase(TestCase, LoaderModuleMockMixin): def test_get_device(self): napalm_proxy.init(TEST_OPTS) ret = napalm_proxy.get_device() - assert ret['UP'] is True + assert ret["UP"] is True def test_get_grains(self): napalm_proxy.init(TEST_OPTS) ret = napalm_proxy.get_grains() - assert ret['out'] == napalm_test_support.TEST_FACTS + assert ret["out"] == napalm_test_support.TEST_FACTS def test_grains_refresh(self): napalm_proxy.init(TEST_OPTS) ret = napalm_proxy.grains_refresh() - assert ret['out'] == napalm_test_support.TEST_FACTS + assert ret["out"] == napalm_test_support.TEST_FACTS def test_fns(self): ret = napalm_proxy.fns() - assert 'details' in ret.keys() + assert "details" in ret.keys() def test_shutdown(self): ret = napalm_proxy.shutdown(TEST_OPTS) @@ -93,5 +80,5 @@ class NapalmProxyTestCase(TestCase, LoaderModuleMockMixin): def test_call(self): napalm_proxy.init(TEST_OPTS) - ret = napalm_proxy.call('get_arp_table') - assert ret['out'] == napalm_test_support.TEST_ARP_TABLE + ret = napalm_proxy.call("get_arp_table") + assert ret["out"] == napalm_test_support.TEST_ARP_TABLE diff --git a/tests/unit/renderers/test_aws_kms.py b/tests/unit/renderers/test_aws_kms.py index 50305b2959d..75986424fa9 100644 --- a/tests/unit/renderers/test_aws_kms.py +++ b/tests/unit/renderers/test_aws_kms.py @@ -1,202 +1,223 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for AWS KMS Decryption Renderer. -''' +""" # pylint: disable=protected-access # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt libs import salt.exceptions import salt.renderers.aws_kms as aws_kms +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + try: import botocore.exceptions import botocore.session import botocore.stub + NO_BOTOCORE = False except ImportError: NO_BOTOCORE = True try: import cryptography.fernet as fernet + NO_FERNET = False except ImportError: NO_FERNET = True -PLAINTEXT_SECRET = 'Use more salt.' -ENCRYPTED_DATA_KEY = 'encrypted-data-key' -PLAINTEXT_DATA_KEY = b'plaintext-data-key' -BASE64_DATA_KEY = b'cGxhaW50ZXh0LWRhdGEta2V5' -AWS_PROFILE = 'test-profile' -REGION_NAME = 'us-test-1' +PLAINTEXT_SECRET = "Use more salt." +ENCRYPTED_DATA_KEY = "encrypted-data-key" +PLAINTEXT_DATA_KEY = b"plaintext-data-key" +BASE64_DATA_KEY = b"cGxhaW50ZXh0LWRhdGEta2V5" +AWS_PROFILE = "test-profile" +REGION_NAME = "us-test-1" -@skipIf(NO_BOTOCORE, 'Unable to import botocore libraries') +@skipIf(NO_BOTOCORE, "Unable to import botocore libraries") class AWSKMSTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ unit test AWS KMS renderer - ''' + """ def setup_loader_modules(self): return {aws_kms: {}} def test__cfg_data_key(self): - ''' + """ _cfg_data_key returns the aws_kms:data_key from configuration. - ''' - config = {'aws_kms': {'data_key': ENCRYPTED_DATA_KEY}} - with patch.dict(aws_kms.__salt__, {'config.get': config.get}): # pylint: disable=no-member - self.assertEqual(aws_kms._cfg_data_key(), ENCRYPTED_DATA_KEY, - '_cfg_data_key did not return the data key configured in __salt__.') + """ + config = {"aws_kms": {"data_key": ENCRYPTED_DATA_KEY}} + with patch.dict( + aws_kms.__salt__, {"config.get": config.get} + ): # pylint: disable=no-member + self.assertEqual( + aws_kms._cfg_data_key(), + ENCRYPTED_DATA_KEY, + "_cfg_data_key did not return the data key configured in __salt__.", + ) with patch.dict(aws_kms.__opts__, config): # pylint: disable=no-member - self.assertEqual(aws_kms._cfg_data_key(), ENCRYPTED_DATA_KEY, - '_cfg_data_key did not return the data key configured in __opts__.') + self.assertEqual( + aws_kms._cfg_data_key(), + ENCRYPTED_DATA_KEY, + "_cfg_data_key did not return the data key configured in __opts__.", + ) def test__cfg_data_key_no_key(self): - ''' + """ When no aws_kms:data_key is configured, calling _cfg_data_key should raise a SaltConfigurationError - ''' + """ self.assertRaises(salt.exceptions.SaltConfigurationError, aws_kms._cfg_data_key) def test__session_profile(self): # pylint: disable=no-self-use - ''' + """ _session instantiates boto3.Session with the configured profile_name - ''' - with patch.object(aws_kms, '_cfg', lambda k: AWS_PROFILE): - with patch('boto3.Session') as session: + """ + with patch.object(aws_kms, "_cfg", lambda k: AWS_PROFILE): + with patch("boto3.Session") as session: aws_kms._session() session.assert_called_with(profile_name=AWS_PROFILE) def test__session_noprofile(self): - ''' + """ _session raises a SaltConfigurationError when boto3 raises botocore.exceptions.ProfileNotFound. - ''' - with patch('boto3.Session') as session: - session.side_effect = botocore.exceptions.ProfileNotFound(profile=AWS_PROFILE) + """ + with patch("boto3.Session") as session: + session.side_effect = botocore.exceptions.ProfileNotFound( + profile=AWS_PROFILE + ) self.assertRaises(salt.exceptions.SaltConfigurationError, aws_kms._session) def test__session_noregion(self): - ''' + """ _session raises a SaltConfigurationError when boto3 raises botocore.exceptions.NoRegionError - ''' - with patch('boto3.Session') as session: + """ + with patch("boto3.Session") as session: session.side_effect = botocore.exceptions.NoRegionError self.assertRaises(salt.exceptions.SaltConfigurationError, aws_kms._session) def test__kms(self): # pylint: disable=no-self-use - ''' + """ _kms calls boto3.Session.client with 'kms' as its only argument. - ''' - with patch('boto3.Session.client') as client: + """ + with patch("boto3.Session.client") as client: aws_kms._kms() - client.assert_called_with('kms') + client.assert_called_with("kms") def test__kms_noregion(self): - ''' + """ _kms raises a SaltConfigurationError when boto3 raises a NoRegionError. - ''' - with patch('boto3.Session') as session: + """ + with patch("boto3.Session") as session: session.side_effect = botocore.exceptions.NoRegionError self.assertRaises(salt.exceptions.SaltConfigurationError, aws_kms._kms) def test__api_decrypt(self): # pylint: disable=no-self-use - ''' + """ _api_decrypt_response calls kms.decrypt with the configured data key as the CiphertextBlob kwarg. - ''' + """ kms_client = MagicMock() - with patch.object(aws_kms, '_kms') as kms_getter: + with patch.object(aws_kms, "_kms") as kms_getter: kms_getter.return_value = kms_client - with patch.object(aws_kms, '_cfg_data_key', lambda: ENCRYPTED_DATA_KEY): + with patch.object(aws_kms, "_cfg_data_key", lambda: ENCRYPTED_DATA_KEY): aws_kms._api_decrypt() - kms_client.decrypt.assert_called_with(CiphertextBlob=ENCRYPTED_DATA_KEY) # pylint: disable=no-member + kms_client.decrypt.assert_called_with( + CiphertextBlob=ENCRYPTED_DATA_KEY + ) # pylint: disable=no-member def test__api_decrypt_badkey(self): - ''' + """ _api_decrypt_response raises SaltConfigurationError when kms.decrypt raises a botocore.exceptions.ClientError with an error_code of 'InvalidCiphertextException'. - ''' + """ kms_client = MagicMock() kms_client.decrypt.side_effect = botocore.exceptions.ClientError( # pylint: disable=no-member - error_response={'Error': {'Code': 'InvalidCiphertextException'}}, - operation_name='Decrypt', + error_response={"Error": {"Code": "InvalidCiphertextException"}}, + operation_name="Decrypt", ) - with patch.object(aws_kms, '_kms') as kms_getter: + with patch.object(aws_kms, "_kms") as kms_getter: kms_getter.return_value = kms_client - with patch.object(aws_kms, '_cfg_data_key', lambda: ENCRYPTED_DATA_KEY): - self.assertRaises(salt.exceptions.SaltConfigurationError, aws_kms._api_decrypt) + with patch.object(aws_kms, "_cfg_data_key", lambda: ENCRYPTED_DATA_KEY): + self.assertRaises( + salt.exceptions.SaltConfigurationError, aws_kms._api_decrypt + ) def test__plaintext_data_key(self): - ''' + """ _plaintext_data_key returns the 'Plaintext' value from the response. It caches the response and only calls _api_decrypt exactly once. - ''' - with patch.object(aws_kms, '_api_decrypt', return_value={'KeyId': 'key-id', 'Plaintext': PLAINTEXT_DATA_KEY}) as api_decrypt: + """ + with patch.object( + aws_kms, + "_api_decrypt", + return_value={"KeyId": "key-id", "Plaintext": PLAINTEXT_DATA_KEY}, + ) as api_decrypt: self.assertEqual(aws_kms._plaintext_data_key(), PLAINTEXT_DATA_KEY) aws_kms._plaintext_data_key() api_decrypt.assert_called_once() def test__base64_plaintext_data_key(self): - ''' + """ _base64_plaintext_data_key returns the urlsafe base64 encoded plain text data key. - ''' - with patch.object(aws_kms, '_plaintext_data_key', return_value=PLAINTEXT_DATA_KEY): + """ + with patch.object( + aws_kms, "_plaintext_data_key", return_value=PLAINTEXT_DATA_KEY + ): self.assertEqual(aws_kms._base64_plaintext_data_key(), BASE64_DATA_KEY) - @skipIf(NO_FERNET, 'Failed to import cryptography.fernet') + @skipIf(NO_FERNET, "Failed to import cryptography.fernet") def test__decrypt_ciphertext(self): - ''' + """ test _decrypt_ciphertext - ''' + """ test_key = fernet.Fernet.generate_key() crypted = fernet.Fernet(test_key).encrypt(PLAINTEXT_SECRET.encode()) - with patch.object(aws_kms, '_base64_plaintext_data_key', return_value=test_key): + with patch.object(aws_kms, "_base64_plaintext_data_key", return_value=test_key): self.assertEqual(aws_kms._decrypt_ciphertext(crypted), PLAINTEXT_SECRET) - @skipIf(NO_FERNET, 'Failed to import cryptography.fernet') + @skipIf(NO_FERNET, "Failed to import cryptography.fernet") def test__decrypt_object(self): - ''' + """ Test _decrypt_object - ''' + """ test_key = fernet.Fernet.generate_key() crypted = fernet.Fernet(test_key).encrypt(PLAINTEXT_SECRET.encode()) - secret_map = {'secret': PLAINTEXT_SECRET} - crypted_map = {'secret': crypted} + secret_map = {"secret": PLAINTEXT_SECRET} + crypted_map = {"secret": crypted} secret_list = [PLAINTEXT_SECRET] crypted_list = [crypted] - with patch.object(aws_kms, '_base64_plaintext_data_key', return_value=test_key): - self.assertEqual(aws_kms._decrypt_object(PLAINTEXT_SECRET), PLAINTEXT_SECRET) + with patch.object(aws_kms, "_base64_plaintext_data_key", return_value=test_key): + self.assertEqual( + aws_kms._decrypt_object(PLAINTEXT_SECRET), PLAINTEXT_SECRET + ) self.assertEqual(aws_kms._decrypt_object(crypted), PLAINTEXT_SECRET) self.assertEqual(aws_kms._decrypt_object(crypted_map), secret_map) self.assertEqual(aws_kms._decrypt_object(crypted_list), secret_list) self.assertEqual(aws_kms._decrypt_object(None), None) - @skipIf(NO_FERNET, 'Failed to import cryptography.fernet') + @skipIf(NO_FERNET, "Failed to import cryptography.fernet") def test_render(self): - ''' + """ Test that we can decrypt some data. - ''' + """ test_key = fernet.Fernet.generate_key() crypted = fernet.Fernet(test_key).encrypt(PLAINTEXT_SECRET.encode()) - with patch.object(aws_kms, '_base64_plaintext_data_key', return_value=test_key): + with patch.object(aws_kms, "_base64_plaintext_data_key", return_value=test_key): self.assertEqual(aws_kms.render(crypted), PLAINTEXT_SECRET) diff --git a/tests/unit/renderers/test_gpg.py b/tests/unit/renderers/test_gpg.py index 20af25b678d..8d5431057bf 100644 --- a/tests/unit/renderers/test_gpg.py +++ b/tests/unit/renderers/test_gpg.py @@ -5,48 +5,46 @@ from __future__ import absolute_import, print_function, unicode_literals from textwrap import dedent -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt libs import salt.renderers.gpg as gpg from salt.exceptions import SaltRenderError +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class GPGTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ unit test GPG renderer - ''' + """ + def setup_loader_modules(self): return {gpg: {}} def test__get_gpg_exec(self): - ''' + """ test _get_gpg_exec - ''' - gpg_exec = '/bin/gpg' + """ + gpg_exec = "/bin/gpg" - with patch('salt.utils.path.which', MagicMock(return_value=gpg_exec)): + with patch("salt.utils.path.which", MagicMock(return_value=gpg_exec)): self.assertEqual(gpg._get_gpg_exec(), gpg_exec) - with patch('salt.utils.path.which', MagicMock(return_value=False)): + with patch("salt.utils.path.which", MagicMock(return_value=False)): self.assertRaises(SaltRenderError, gpg._get_gpg_exec) def test__decrypt_ciphertext(self): - ''' + """ test _decrypt_ciphertext - ''' - key_dir = '/etc/salt/gpgkeys' - secret = 'Use more salt.' - crypted = '-----BEGIN PGP MESSAGE-----!@#$%^&*()_+-----END PGP MESSAGE-----' + """ + key_dir = "/etc/salt/gpgkeys" + secret = "Use more salt." + crypted = "-----BEGIN PGP MESSAGE-----!@#$%^&*()_+-----END PGP MESSAGE-----" - multisecret = 'password is {0} and salt is {0}'.format(secret) - multicrypted = 'password is {0} and salt is {0}'.format(crypted) + multisecret = "password is {0} and salt is {0}".format(secret) + multicrypted = "password is {0} and salt is {0}".format(crypted) class GPGDecrypt(object): def communicate(self, *args, **kwargs): @@ -54,33 +52,38 @@ class GPGTestCase(TestCase, LoaderModuleMockMixin): class GPGNotDecrypt(object): def communicate(self, *args, **kwargs): - return [None, 'decrypt error'] + return [None, "decrypt error"] - with patch('salt.renderers.gpg._get_key_dir', MagicMock(return_value=key_dir)), \ - patch('salt.utils.path.which', MagicMock()): - with patch('salt.renderers.gpg.Popen', MagicMock(return_value=GPGDecrypt())): + with patch( + "salt.renderers.gpg._get_key_dir", MagicMock(return_value=key_dir) + ), patch("salt.utils.path.which", MagicMock()): + with patch( + "salt.renderers.gpg.Popen", MagicMock(return_value=GPGDecrypt()) + ): self.assertEqual(gpg._decrypt_ciphertexts(crypted), secret) - self.assertEqual( - gpg._decrypt_ciphertexts(multicrypted), multisecret) - with patch('salt.renderers.gpg.Popen', MagicMock(return_value=GPGNotDecrypt())): + self.assertEqual(gpg._decrypt_ciphertexts(multicrypted), multisecret) + with patch( + "salt.renderers.gpg.Popen", MagicMock(return_value=GPGNotDecrypt()) + ): self.assertEqual(gpg._decrypt_ciphertexts(crypted), crypted) - self.assertEqual( - gpg._decrypt_ciphertexts(multicrypted), multicrypted) + self.assertEqual(gpg._decrypt_ciphertexts(multicrypted), multicrypted) def test__decrypt_object(self): - ''' + """ test _decrypt_object - ''' - secret = 'Use more salt.' - crypted = '-----BEGIN PGP MESSAGE-----!@#$%^&*()_+-----END PGP MESSAGE-----' + """ + secret = "Use more salt." + crypted = "-----BEGIN PGP MESSAGE-----!@#$%^&*()_+-----END PGP MESSAGE-----" - secret_map = {'secret': secret} - crypted_map = {'secret': crypted} + secret_map = {"secret": secret} + crypted_map = {"secret": crypted} secret_list = [secret] crypted_list = [crypted] - with patch('salt.renderers.gpg._decrypt_ciphertext', MagicMock(return_value=secret)): + with patch( + "salt.renderers.gpg._decrypt_ciphertext", MagicMock(return_value=secret) + ): self.assertEqual(gpg._decrypt_object(secret), secret) self.assertEqual(gpg._decrypt_object(crypted), secret) self.assertEqual(gpg._decrypt_object(crypted_map), secret_map) @@ -88,23 +91,28 @@ class GPGTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(gpg._decrypt_object(None), None) def test_render(self): - ''' + """ test render - ''' - key_dir = '/etc/salt/gpgkeys' - secret = 'Use more salt.' - crypted = '-----BEGIN PGP MESSAGE-----!@#$%^&*()_+' + """ + key_dir = "/etc/salt/gpgkeys" + secret = "Use more salt." + crypted = "-----BEGIN PGP MESSAGE-----!@#$%^&*()_+" - with patch('salt.renderers.gpg._get_gpg_exec', MagicMock(return_value=True)): - with patch('salt.renderers.gpg._get_key_dir', MagicMock(return_value=key_dir)): - with patch('salt.renderers.gpg._decrypt_object', MagicMock(return_value=secret)): + with patch("salt.renderers.gpg._get_gpg_exec", MagicMock(return_value=True)): + with patch( + "salt.renderers.gpg._get_key_dir", MagicMock(return_value=key_dir) + ): + with patch( + "salt.renderers.gpg._decrypt_object", MagicMock(return_value=secret) + ): self.assertEqual(gpg.render(crypted), secret) def test_multi_render(self): - key_dir = '/etc/salt/gpgkeys' - secret = 'Use more salt.' - expected = '\n'.join([secret]*3) - crypted = dedent('''\ + key_dir = "/etc/salt/gpgkeys" + secret = "Use more salt." + expected = "\n".join([secret] * 3) + crypted = dedent( + """\ -----BEGIN PGP MESSAGE----- !@#$%^&*()_+ -----END PGP MESSAGE----- @@ -114,18 +122,25 @@ class GPGTestCase(TestCase, LoaderModuleMockMixin): -----BEGIN PGP MESSAGE----- !@#$%^&*()_+ -----END PGP MESSAGE----- - ''') + """ + ) - with patch('salt.renderers.gpg._get_gpg_exec', MagicMock(return_value=True)): - with patch('salt.renderers.gpg._get_key_dir', MagicMock(return_value=key_dir)): - with patch('salt.renderers.gpg._decrypt_ciphertext', MagicMock(return_value=secret)): + with patch("salt.renderers.gpg._get_gpg_exec", MagicMock(return_value=True)): + with patch( + "salt.renderers.gpg._get_key_dir", MagicMock(return_value=key_dir) + ): + with patch( + "salt.renderers.gpg._decrypt_ciphertext", + MagicMock(return_value=secret), + ): self.assertEqual(gpg.render(crypted), expected) def test_render_with_binary_data_should_return_binary_data(self): - key_dir = '/etc/salt/gpgkeys' - secret = b'Use\x8b more\x8b salt.' - expected = b'\n'.join([secret]*3) - crypted = dedent('''\ + key_dir = "/etc/salt/gpgkeys" + secret = b"Use\x8b more\x8b salt." + expected = b"\n".join([secret] * 3) + crypted = dedent( + """\ -----BEGIN PGP MESSAGE----- !@#$%^&*()_+ -----END PGP MESSAGE----- @@ -135,18 +150,25 @@ class GPGTestCase(TestCase, LoaderModuleMockMixin): -----BEGIN PGP MESSAGE----- !@#$%^&*()_+ -----END PGP MESSAGE----- - ''') + """ + ) - with patch('salt.renderers.gpg._get_gpg_exec', MagicMock(return_value=True)): - with patch('salt.renderers.gpg._get_key_dir', MagicMock(return_value=key_dir)): - with patch('salt.renderers.gpg._decrypt_ciphertext', MagicMock(return_value=secret)): - self.assertEqual(gpg.render(crypted, encoding='utf-8'), expected) + with patch("salt.renderers.gpg._get_gpg_exec", MagicMock(return_value=True)): + with patch( + "salt.renderers.gpg._get_key_dir", MagicMock(return_value=key_dir) + ): + with patch( + "salt.renderers.gpg._decrypt_ciphertext", + MagicMock(return_value=secret), + ): + self.assertEqual(gpg.render(crypted, encoding="utf-8"), expected) def test_render_with_translate_newlines_should_translate_newlines(self): - key_dir = '/etc/salt/gpgkeys' - secret = b'Use\x8b more\x8b salt.' - expected = b'\n\n'.join([secret]*3) - crypted = dedent('''\ + key_dir = "/etc/salt/gpgkeys" + secret = b"Use\x8b more\x8b salt." + expected = b"\n\n".join([secret] * 3) + crypted = dedent( + """\ -----BEGIN PGP MESSAGE----- !@#$%^&*()_+ -----END PGP MESSAGE-----\\n @@ -156,12 +178,18 @@ class GPGTestCase(TestCase, LoaderModuleMockMixin): -----BEGIN PGP MESSAGE----- !@#$%^&*()_+ -----END PGP MESSAGE----- - ''') + """ + ) - with patch('salt.renderers.gpg._get_gpg_exec', MagicMock(return_value=True)): - with patch('salt.renderers.gpg._get_key_dir', MagicMock(return_value=key_dir)): - with patch('salt.renderers.gpg._decrypt_ciphertext', MagicMock(return_value=secret)): + with patch("salt.renderers.gpg._get_gpg_exec", MagicMock(return_value=True)): + with patch( + "salt.renderers.gpg._get_key_dir", MagicMock(return_value=key_dir) + ): + with patch( + "salt.renderers.gpg._decrypt_ciphertext", + MagicMock(return_value=secret), + ): self.assertEqual( - gpg.render(crypted, translate_newlines=True, encoding='utf-8'), + gpg.render(crypted, translate_newlines=True, encoding="utf-8"), expected, ) diff --git a/tests/unit/renderers/test_nacl.py b/tests/unit/renderers/test_nacl.py index 34e6227d03c..dc1c27773d4 100644 --- a/tests/unit/renderers/test_nacl.py +++ b/tests/unit/renderers/test_nacl.py @@ -3,39 +3,37 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt libs import salt.renderers.nacl as nacl +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class NaclTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ unit test NaCl renderer - ''' + """ + def setup_loader_modules(self): return {nacl: {}} def test__decrypt_object(self): - ''' + """ test _decrypt_object - ''' - secret = 'Use more salt.' - crypted = 'NACL[MRN3cc+fmdxyQbz6WMF+jq1hKdU5X5BBI7OjK+atvHo1ll+w1gZ7XyWtZVfq9gK9rQaMfkDxmidJKwE0Mw==]' + """ + secret = "Use more salt." + crypted = "NACL[MRN3cc+fmdxyQbz6WMF+jq1hKdU5X5BBI7OjK+atvHo1ll+w1gZ7XyWtZVfq9gK9rQaMfkDxmidJKwE0Mw==]" - secret_map = {'secret': secret} - crypted_map = {'secret': crypted} + secret_map = {"secret": secret} + crypted_map = {"secret": crypted} secret_list = [secret] crypted_list = [crypted] - with patch.dict(nacl.__salt__, {'nacl.dec': MagicMock(return_value=secret)}): + with patch.dict(nacl.__salt__, {"nacl.dec": MagicMock(return_value=secret)}): self.assertEqual(nacl._decrypt_object(secret), secret) self.assertEqual(nacl._decrypt_object(crypted), secret) self.assertEqual(nacl._decrypt_object(crypted_map), secret_map) @@ -43,10 +41,10 @@ class NaclTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(nacl._decrypt_object(None), None) def test_render(self): - ''' + """ test render - ''' - secret = 'Use more salt.' - crypted = 'NACL[MRN3cc+fmdxyQbz6WMF+jq1hKdU5X5BBI7OjK+atvHo1ll+w1gZ7XyWtZVfq9gK9rQaMfkDxmidJKwE0Mw==]' - with patch.dict(nacl.__salt__, {'nacl.dec': MagicMock(return_value=secret)}): + """ + secret = "Use more salt." + crypted = "NACL[MRN3cc+fmdxyQbz6WMF+jq1hKdU5X5BBI7OjK+atvHo1ll+w1gZ7XyWtZVfq9gK9rQaMfkDxmidJKwE0Mw==]" + with patch.dict(nacl.__salt__, {"nacl.dec": MagicMock(return_value=secret)}): self.assertEqual(nacl.render(crypted), secret) diff --git a/tests/unit/renderers/test_stateconf.py b/tests/unit/renderers/test_stateconf.py index 3acf11e1733..c31c563df57 100644 --- a/tests/unit/renderers/test_stateconf.py +++ b/tests/unit/renderers/test_stateconf.py @@ -2,33 +2,33 @@ # Import Python libs from __future__ import absolute_import + import os import os.path import tempfile -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.runtests import RUNTIME_VARS +import salt.config # Import Salt libs import salt.loader -import salt.config from salt.exceptions import SaltRenderError -from salt.ext.six.moves import StringIO # Import 3rd-party libs from salt.ext import six +from salt.ext.six.moves import StringIO +from tests.support.runtests import RUNTIME_VARS +# Import Salt Testing libs +from tests.support.unit import TestCase -REQUISITES = ['require', 'require_in', 'use', 'use_in', 'watch', 'watch_in'] +REQUISITES = ["require", "require_in", "use", "use_in", "watch", "watch_in"] class StateConfigRendererTestCase(TestCase): - def setUp(self): self.root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - self.state_tree_dir = os.path.join(self.root_dir, 'state_tree') - self.cache_dir = os.path.join(self.root_dir, 'cachedir') + self.state_tree_dir = os.path.join(self.root_dir, "state_tree") + self.cache_dir = os.path.join(self.root_dir, "cachedir") if not os.path.isdir(self.root_dir): os.makedirs(self.root_dir) @@ -38,40 +38,39 @@ class StateConfigRendererTestCase(TestCase): if not os.path.isdir(self.cache_dir): os.makedirs(self.cache_dir) self.config = salt.config.minion_config(None) - self.config['root_dir'] = self.root_dir - self.config['state_events'] = False - self.config['id'] = 'match' - self.config['file_client'] = 'local' - self.config['file_roots'] = dict(base=[self.state_tree_dir]) - self.config['cachedir'] = self.cache_dir - self.config['test'] = False + self.config["root_dir"] = self.root_dir + self.config["state_events"] = False + self.config["id"] = "match" + self.config["file_client"] = "local" + self.config["file_roots"] = dict(base=[self.state_tree_dir]) + self.config["cachedir"] = self.cache_dir + self.config["test"] = False self._renderers = salt.loader.render( - self.config, - {'config.get': lambda a, b: False} + self.config, {"config.get": lambda a, b: False} ) def tearDown(self): - for attrname in ('config', '_renderers'): + for attrname in ("config", "_renderers"): try: delattr(self, attrname) except AttributeError: continue - def _render_sls(self, - content, - sls='', - saltenv='base', - argline='-G yaml . jinja', - **kws): - return self._renderers['stateconf']( - StringIO(content), saltenv=saltenv, sls=sls, + def _render_sls( + self, content, sls="", saltenv="base", argline="-G yaml . jinja", **kws + ): + return self._renderers["stateconf"]( + StringIO(content), + saltenv=saltenv, + sls=sls, argline=argline, renderers=salt.loader.render(self.config, {}), **kws ) def test_state_config(self): - result = self._render_sls(''' + result = self._render_sls( + """ .sls_params: stateconf.set: - name1: value1 @@ -88,26 +87,34 @@ test: cmd.run: - name: echo name1={{sls_params.name1}} name2={{sls_params.name2}} {{extra.name}} - cwd: / -''', sls='test') +""", + sls="test", + ) self.assertEqual(len(result), 3) - self.assertTrue('test::sls_params' in result and 'test' in result) - self.assertTrue('test::extra' in result) - self.assertEqual(result['test']['cmd.run'][0]['name'], - 'echo name1=value1 name2=value2 value') + self.assertTrue("test::sls_params" in result and "test" in result) + self.assertTrue("test::extra" in result) + self.assertEqual( + result["test"]["cmd.run"][0]["name"], "echo name1=value1 name2=value2 value" + ) def test_sls_dir(self): - result = self._render_sls(''' + result = self._render_sls( + """ test: cmd.run: - name: echo sls_dir={{sls_dir}} - cwd: / -''', sls='path.to.sls') +""", + sls="path.to.sls", + ) self.assertEqual( - result['test']['cmd.run'][0]['name'], - 'echo sls_dir=path{0}to'.format(os.sep)) + result["test"]["cmd.run"][0]["name"], + "echo sls_dir=path{0}to".format(os.sep), + ) def test_states_declared_with_shorthand_no_args(self): - result = self._render_sls(''' + result = self._render_sls( + """ test: cmd.run: - name: echo testing @@ -116,32 +123,32 @@ test1: pkg.installed test2: user.present -''') +""" + ) self.assertEqual(len(result), 3) - for args in (result['test1']['pkg.installed'], - result['test2']['user.present']): + for args in (result["test1"]["pkg.installed"], result["test2"]["user.present"]): self.assertTrue(isinstance(args, list)) self.assertEqual(len(args), 0) - self.assertEqual(result['test']['cmd.run'][0]['name'], 'echo testing') + self.assertEqual(result["test"]["cmd.run"][0]["name"], "echo testing") def test_adding_state_name_arg_for_dot_state_id(self): - result = self._render_sls(''' + result = self._render_sls( + """ .test: pkg.installed: - cwd: / .test2: pkg.installed: - name: vim -''', sls='test') - self.assertEqual( - result['test::test']['pkg.installed'][0]['name'], 'test' - ) - self.assertEqual( - result['test::test2']['pkg.installed'][0]['name'], 'vim' +""", + sls="test", ) + self.assertEqual(result["test::test"]["pkg.installed"][0]["name"], "test") + self.assertEqual(result["test::test2"]["pkg.installed"][0]["name"], "vim") def test_state_prefix(self): - result = self._render_sls(''' + result = self._render_sls( + """ .test: cmd.run: - name: echo renamed @@ -152,14 +159,17 @@ state_id: - run - name: echo not renamed - cwd: / -''', sls='test') +""", + sls="test", + ) self.assertEqual(len(result), 2) - self.assertTrue('test::test' in result) - self.assertTrue('state_id' in result) + self.assertTrue("test::test" in result) + self.assertTrue("state_id" in result) def test_dot_state_id_in_requisites(self): for req in REQUISITES: - result = self._render_sls(''' + result = self._render_sls( + """ .test: cmd.run: - name: echo renamed @@ -172,17 +182,22 @@ state_id: - {0}: - cmd: .test - '''.format(req), sls='test') + """.format( + req + ), + sls="test", + ) self.assertEqual(len(result), 2) - self.assertTrue('test::test' in result) - self.assertTrue('state_id' in result) + self.assertTrue("test::test" in result) + self.assertTrue("state_id" in result) self.assertEqual( - result['state_id']['cmd.run'][2][req][0]['cmd'], 'test::test' + result["state_id"]["cmd.run"][2][req][0]["cmd"], "test::test" ) def test_relative_include_with_requisites(self): for req in REQUISITES: - result = self._render_sls(''' + result = self._render_sls( + """ include: - some.helper - .utils @@ -193,15 +208,20 @@ state_id: - cwd: / - {0}: - cmd: .utils::some_state -'''.format(req), sls='test.work') - self.assertEqual(result['include'][1], {'base': 'test.utils'}) +""".format( + req + ), + sls="test.work", + ) + self.assertEqual(result["include"][1], {"base": "test.utils"}) self.assertEqual( - result['state_id']['cmd.run'][2][req][0]['cmd'], - 'test.utils::some_state' + result["state_id"]["cmd.run"][2][req][0]["cmd"], + "test.utils::some_state", ) def test_relative_include_and_extend(self): - result = self._render_sls(''' + result = self._render_sls( + """ include: - some.helper - .utils @@ -210,12 +230,15 @@ extend: .utils::some_state: cmd.run: - name: echo overridden - ''', sls='test.work') - self.assertTrue('test.utils::some_state' in result['extend']) + """, + sls="test.work", + ) + self.assertTrue("test.utils::some_state" in result["extend"]) def test_multilevel_relative_include_with_requisites(self): for req in REQUISITES: - result = self._render_sls(''' + result = self._render_sls( + """ include: - .shared - ..utils @@ -227,24 +250,33 @@ state_id: - cwd: / - {0}: - cmd: ..utils::some_state -'''.format(req), sls='test.nested.work') - self.assertEqual(result['include'][0], - {'base': 'test.nested.shared'}) - self.assertEqual(result['include'][1], {'base': 'test.utils'}) - self.assertEqual(result['include'][2], {'base': 'helper'}) +""".format( + req + ), + sls="test.nested.work", + ) + self.assertEqual(result["include"][0], {"base": "test.nested.shared"}) + self.assertEqual(result["include"][1], {"base": "test.utils"}) + self.assertEqual(result["include"][2], {"base": "helper"}) self.assertEqual( - result['state_id']['cmd.run'][2][req][0]['cmd'], - 'test.utils::some_state' + result["state_id"]["cmd.run"][2][req][0]["cmd"], + "test.utils::some_state", ) def test_multilevel_relative_include_beyond_top_level(self): - self.assertRaises(SaltRenderError, self._render_sls, ''' + self.assertRaises( + SaltRenderError, + self._render_sls, + """ include: - ...shared -''', sls='test.work') +""", + sls="test.work", + ) def test_start_state_generation(self): - result = self._render_sls(''' + result = self._render_sls( + """ A: cmd.run: - name: echo hello @@ -253,15 +285,18 @@ B: cmd.run: - name: echo world - cwd: / -''', sls='test', argline='-so yaml . jinja') +""", + sls="test", + argline="-so yaml . jinja", + ) self.assertEqual(len(result), 4) self.assertEqual( - result['test::start']['stateconf.set'][0]['require_in'][0]['cmd'], - 'A' + result["test::start"]["stateconf.set"][0]["require_in"][0]["cmd"], "A" ) def test_goal_state_generation(self): - result = self._render_sls(''' + result = self._render_sls( + """ {% for sid in "ABCDE": %} {{sid}}: cmd.run: @@ -269,16 +304,18 @@ B: - cwd: / {% endfor %} -''', sls='test.goalstate', argline='yaml . jinja') - self.assertEqual(len(result), len('ABCDE') + 1) - - reqs = result['test.goalstate::goal']['stateconf.set'][0]['require'] - self.assertEqual( - set([next(six.itervalues(i)) for i in reqs]), set('ABCDE') +""", + sls="test.goalstate", + argline="yaml . jinja", ) + self.assertEqual(len(result), len("ABCDE") + 1) + + reqs = result["test.goalstate::goal"]["stateconf.set"][0]["require"] + self.assertEqual(set([next(six.itervalues(i)) for i in reqs]), set("ABCDE")) def test_implicit_require_with_goal_state(self): - result = self._render_sls(''' + result = self._render_sls( + """ {% for sid in "ABCDE": %} {{sid}}: cmd.run: @@ -301,44 +338,49 @@ G: - require: - cmd: D - cmd: F -''', sls='test', argline='-o yaml . jinja') +""", + sls="test", + argline="-o yaml . jinja", + ) - sids = 'ABCDEFG'[::-1] + sids = "ABCDEFG"[::-1] for i, sid in enumerate(sids): if i < len(sids) - 1: self.assertEqual( - result[sid]['cmd.run'][2]['require'][0]['cmd'], - sids[i + 1] + result[sid]["cmd.run"][2]["require"][0]["cmd"], sids[i + 1] ) - F_args = result['F']['cmd.run'] + F_args = result["F"]["cmd.run"] self.assertEqual(len(F_args), 3) - F_req = F_args[2]['require'] + F_req = F_args[2]["require"] self.assertEqual(len(F_req), 3) - self.assertEqual(F_req[1]['cmd'], 'A') - self.assertEqual(F_req[2]['cmd'], 'B') + self.assertEqual(F_req[1]["cmd"], "A") + self.assertEqual(F_req[2]["cmd"], "B") - G_args = result['G']['cmd.run'] + G_args = result["G"]["cmd.run"] self.assertEqual(len(G_args), 3) - G_req = G_args[2]['require'] + G_req = G_args[2]["require"] self.assertEqual(len(G_req), 3) - self.assertEqual(G_req[1]['cmd'], 'D') - self.assertEqual(G_req[2]['cmd'], 'F') + self.assertEqual(G_req[1]["cmd"], "D") + self.assertEqual(G_req[2]["cmd"], "F") - goal_args = result['test::goal']['stateconf.set'] + goal_args = result["test::goal"]["stateconf.set"] self.assertEqual(len(goal_args), 1) self.assertEqual( - [next(six.itervalues(i)) for i in goal_args[0]['require']], - list('ABCDEFG') + [next(six.itervalues(i)) for i in goal_args[0]["require"]], list("ABCDEFG") ) def test_slsdir(self): - result = self._render_sls(''' + result = self._render_sls( + """ formula/woot.sls: cmd.run: - name: echo {{ slspath }} - cwd: / -''', sls='formula.woot', argline='yaml . jinja') +""", + sls="formula.woot", + argline="yaml . jinja", + ) - r = result['formula/woot.sls']['cmd.run'][0]['name'] - self.assertEqual(r, 'echo formula/woot') + r = result["formula/woot.sls"]["cmd.run"][0]["name"] + self.assertEqual(r, "echo formula/woot") diff --git a/tests/unit/renderers/test_yaml.py b/tests/unit/renderers/test_yaml.py index b7b093c1c46..a745e83191a 100644 --- a/tests/unit/renderers/test_yaml.py +++ b/tests/unit/renderers/test_yaml.py @@ -6,27 +6,24 @@ from __future__ import absolute_import, print_function, unicode_literals import collections import textwrap -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch -) - # Import Salt libs import salt.renderers.yaml as yaml from salt.ext import six +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class YAMLRendererTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {yaml: {}} def assert_unicode(self, value): - ''' + """ Make sure the entire data structure is unicode - ''' + """ if six.PY3: return if isinstance(value, six.string_types): @@ -45,24 +42,28 @@ class YAMLRendererTestCase(TestCase, LoaderModuleMockMixin): self.assert_unicode(ret) def test_yaml_render_string(self): - data = 'string' + data = "string" result = yaml.render(data) self.assertEqual(result, data) def test_yaml_render_unicode(self): - data = '!!python/unicode python unicode string' + data = "!!python/unicode python unicode string" result = yaml.render(data) - self.assertEqual(result, u'python unicode string') + self.assertEqual(result, "python unicode string") def test_yaml_render_old_unicode(self): - config = {'use_yamlloader_old': True} + config = {"use_yamlloader_old": True} with patch.dict(yaml.__opts__, config): # pylint: disable=no-member self.assert_matches( - yaml.render(textwrap.dedent('''\ + yaml.render( + textwrap.dedent( + """\ foo: a: Д - b: {'a': u'\\u0414'}''')), - {'foo': {'a': u'\u0414', 'b': {'a': u'\u0414'}}} + b: {'a': u'\\u0414'}""" + ) + ), + {"foo": {"a": "\u0414", "b": {"a": "\u0414"}}}, ) diff --git a/tests/unit/renderers/test_yamlex.py b/tests/unit/renderers/test_yamlex.py index e3a8af41ffc..ccef27bf8ef 100644 --- a/tests/unit/renderers/test_yamlex.py +++ b/tests/unit/renderers/test_yamlex.py @@ -3,63 +3,59 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase +import salt.serializers.yamlex as yamlex # Import Salt libs import salt.state from salt.config import minion_config from salt.template import compile_template_str -import salt.serializers.yamlex as yamlex -basic_template = '''#!yamlex +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase, skipIf + +basic_template = """#!yamlex foo: bar -''' +""" -complex_template = '''#!yamlex +complex_template = """#!yamlex placeholder: {foo: !aggregate {foo: 42}} placeholder: {foo: !aggregate {bar: null}} placeholder: {foo: !aggregate {baz: inga}} -''' +""" -SKIP_MESSAGE = '%s is unavailable, do prerequisites have been met?' +SKIP_MESSAGE = "%s is unavailable, do prerequisites have been met?" class RendererMixin(object): def render(self, template, opts=None): _config = minion_config(None) - _config['file_client'] = 'local' + _config["file_client"] = "local" if opts: _config.update(opts) _state = salt.state.State(_config) - return compile_template_str(template, - _state.rend, - _state.opts['renderer'], - _state.opts['renderer_blacklist'], - _state.opts['renderer_whitelist']) + return compile_template_str( + template, + _state.rend, + _state.opts["renderer"], + _state.opts["renderer_blacklist"], + _state.opts["renderer_whitelist"], + ) class RendererTests(TestCase, RendererMixin, LoaderModuleMockMixin): - def setup_loader_modules(self): return {yamlex: {}} - @skipIf(not yamlex.available, SKIP_MESSAGE % 'yamlex') + @skipIf(not yamlex.available, SKIP_MESSAGE % "yamlex") def test_basic(self): sls_obj = self.render(basic_template) - assert sls_obj == {'foo': 'bar'}, sls_obj + assert sls_obj == {"foo": "bar"}, sls_obj - @skipIf(not yamlex.available, SKIP_MESSAGE % 'yamlex') + @skipIf(not yamlex.available, SKIP_MESSAGE % "yamlex") def test_complex(self): sls_obj = self.render(complex_template) assert sls_obj == { - 'placeholder': { - 'foo': { - 'foo': 42, - 'bar': None, - 'baz': 'inga' - } - } + "placeholder": {"foo": {"foo": 42, "bar": None, "baz": "inga"}} }, sls_obj diff --git a/tests/unit/returners/test_highstate_return.py b/tests/unit/returners/test_highstate_return.py index a00cf70f503..f3ad9633a55 100644 --- a/tests/unit/returners/test_highstate_return.py +++ b/tests/unit/returners/test_highstate_return.py @@ -1,73 +1,80 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.returners.test_highstate_return ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unit tests for the Highstate Returner Cache. -''' +""" # Import Python libs from __future__ import absolute_import + import json import logging import os +import salt.returners.highstate_return as highstate + +# Import Salt libs +import salt.utils.files + # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase -# Import Salt libs -import salt.utils.files -import salt.returners.highstate_return as highstate - log = logging.getLogger(__name__) class HighstateReturnerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for the highstate_return returner - ''' - output_file = os.path.join(RUNTIME_VARS.TMP, 'highstate_return') + """ + + output_file = os.path.join(RUNTIME_VARS.TMP, "highstate_return") def tearDown(self): os.unlink(self.output_file) def setup_loader_modules(self): - return {highstate: {'__opts__': { - 'highstate.report_everything': True, - 'highstate.report_format': 'json', - 'highstate.report_delivery': 'file', - 'highstate.file_output': self.output_file, - }}} + return { + highstate: { + "__opts__": { + "highstate.report_everything": True, + "highstate.report_format": "json", + "highstate.report_delivery": "file", + "highstate.file_output": self.output_file, + } + } + } def test_pipe_in_name(self): ret = { - 'fun_args': ['test'], - 'jid': '20180308201402941603', - 'return': { - 'cmd_|-test_|-echo hi | grep h\n_|-run': { - 'comment': 'Command "echo hi | grep h\n" run', - 'name': 'echo hi | grep h\n', - 'start_time': '20:14:03.053612', - 'result': True, - 'duration': 75.198, - '__run_num__': 0, - '__sls__': u'test', - 'changes': { - 'pid': 1429, - 'retcode': 0, - 'stderr': '', - 'stdout': 'hi', + "fun_args": ["test"], + "jid": "20180308201402941603", + "return": { + "cmd_|-test_|-echo hi | grep h\n_|-run": { + "comment": 'Command "echo hi | grep h\n" run', + "name": "echo hi | grep h\n", + "start_time": "20:14:03.053612", + "result": True, + "duration": 75.198, + "__run_num__": 0, + "__sls__": u"test", + "changes": { + "pid": 1429, + "retcode": 0, + "stderr": "", + "stdout": "hi", }, - '__id__': 'test', + "__id__": "test", } }, - 'retcode': 0, - 'success': True, - 'fun': 'state.apply', - 'id': 'salt', - 'out': 'highstate', + "retcode": 0, + "success": True, + "fun": "state.apply", + "id": "salt", + "out": "highstate", } expected = [ { @@ -75,7 +82,8 @@ class HighstateReturnerTestCase(TestCase, LoaderModuleMockMixin): {"total": 1}, {"failed": 0, "__style__": "failed"}, {"unchanged": 0, "__style__": "unchanged"}, - {"changed": 1, "__style__": "changed"}, {"duration": 75.198}, + {"changed": 1, "__style__": "changed"}, + {"duration": 75.198}, ], }, { @@ -84,7 +92,7 @@ class HighstateReturnerTestCase(TestCase, LoaderModuleMockMixin): {"arguments": ["test"]}, {"jid": "20180308201402941603"}, {"success": True}, - {"retcode": 0} + {"retcode": 0}, ], }, { @@ -95,19 +103,21 @@ class HighstateReturnerTestCase(TestCase, LoaderModuleMockMixin): {"name": "echo hi | grep h\n"}, {"result": True}, {"duration": 75.198}, - {"comment": "Command \"echo hi | grep h\n\" run"}, - {"changes": [ - {"pid": 1429}, - {"retcode": 0}, - {"stderr": ""}, - {"stdout": "hi"} - ]}, - {"started": "20:14:03.053612"} + {"comment": 'Command "echo hi | grep h\n" run'}, + { + "changes": [ + {"pid": 1429}, + {"retcode": 0}, + {"stderr": ""}, + {"stdout": "hi"}, + ] + }, + {"started": "20:14:03.053612"}, ], - "__style__": "changed" + "__style__": "changed", } ] - } + }, ] highstate.returner(ret) with salt.utils.files.fopen(self.output_file) as fh_: diff --git a/tests/unit/returners/test_local_cache.py b/tests/unit/returners/test_local_cache.py index 267ad317395..4aafcda8005 100644 --- a/tests/unit/returners/test_local_cache.py +++ b/tests/unit/returners/test_local_cache.py @@ -1,86 +1,90 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.returners.local_cache_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unit tests for the Default Job Cache (local_cache). -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import shutil -import time -import logging import tempfile import time -# Import Salt Testing libs -from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import salt.returners.local_cache as local_cache # Import Salt libs import salt.utils.files import salt.utils.jid import salt.utils.job import salt.utils.platform -import salt.returners.local_cache as local_cache from salt.ext import six +# Import Salt Testing libs +from tests.support.mixins import ( + AdaptedConfigurationTestCaseMixin, + LoaderModuleMockMixin, +) +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase + log = logging.getLogger(__name__) class LocalCacheCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for the local_cache.clean_old_jobs function. - ''' + """ + @classmethod def setUpClass(cls): - cls.TMP_CACHE_DIR = os.path.join(RUNTIME_VARS.TMP, 'salt_test_job_cache') - cls.TMP_JID_DIR = os.path.join(cls.TMP_CACHE_DIR, 'jobs') + cls.TMP_CACHE_DIR = os.path.join(RUNTIME_VARS.TMP, "salt_test_job_cache") + cls.TMP_JID_DIR = os.path.join(cls.TMP_CACHE_DIR, "jobs") def setup_loader_modules(self): - return {local_cache: {'__opts__': {'cachedir': self.TMP_CACHE_DIR, 'keep_jobs': 1}}} + return { + local_cache: {"__opts__": {"cachedir": self.TMP_CACHE_DIR, "keep_jobs": 1}} + } def tearDown(self): - ''' + """ Clean up after tests. Note that a setUp function is not used in this TestCase because the _make_tmp_jid_dirs replaces it. - ''' + """ if os.path.exists(self.TMP_CACHE_DIR): shutil.rmtree(self.TMP_CACHE_DIR) def test_clean_old_jobs_no_jid_root(self): - ''' + """ Tests that the function returns None when no jid_root is found. - ''' - with patch('os.path.exists', MagicMock(return_value=False)): + """ + with patch("os.path.exists", MagicMock(return_value=False)): self.assertEqual(local_cache.clean_old_jobs(), None) def test_clean_old_jobs_empty_jid_dir_removed(self): - ''' + """ Tests that an empty JID dir is removed when it is old enough to be deleted. - ''' + """ # Create temp job cache dir without files in it. jid_dir, jid_file = self._make_tmp_jid_dirs(create_files=False) # File timestamps on Windows aren't as precise. Let a little time pass if salt.utils.platform.is_windows(): - time.sleep(.01) + time.sleep(0.01) # Make sure there are no files in the directory before continuing self.assertEqual(jid_file, None) # Call clean_old_jobs function, patching the keep_jobs value with a # very small value to force the call to clean the job. - with patch.dict(local_cache.__opts__, {'keep_jobs': 0.00000001}): + with patch.dict(local_cache.__opts__, {"keep_jobs": 0.00000001}): # Sleep on Windows because time.time is only precise to 3 decimal # points, and therefore subtracting the jid_ctime from time.time # will result in a negative number @@ -92,10 +96,10 @@ class LocalCacheCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual([], os.listdir(self.TMP_JID_DIR)) def test_clean_old_jobs_empty_jid_dir_remains(self): - ''' + """ Tests that an empty JID dir is NOT removed because it was created within the keep_jobs time frame. - ''' + """ # Create temp job cache dir without files in it. jid_dir, jid_file = self._make_tmp_jid_dirs(create_files=False) @@ -107,29 +111,29 @@ class LocalCacheCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): # Get the name of the JID directory that was created to test against if salt.utils.platform.is_windows(): - jid_dir_name = jid_dir.rpartition('\\')[2] + jid_dir_name = jid_dir.rpartition("\\")[2] else: - jid_dir_name = jid_dir.rpartition('/')[2] + jid_dir_name = jid_dir.rpartition("/")[2] # Assert the JID directory is still present to be cleaned after keep_jobs interval self.assertEqual([jid_dir_name], os.listdir(self.TMP_JID_DIR)) def test_clean_old_jobs_jid_file_corrupted(self): - ''' + """ Tests that the entire JID dir is removed when the jid_file is not a file. This scenario indicates a corrupted cache entry, so the entire dir is scrubbed. - ''' + """ # Create temp job cache dir and jid file jid_dir, jid_file = self._make_tmp_jid_dirs() # Make sure there is a jid file in a new job cache director - jid_dir_name = jid_file.rpartition('/')[2] - self.assertEqual(jid_dir_name, 'jid') + jid_dir_name = jid_file.rpartition("/")[2] + self.assertEqual(jid_dir_name, "jid") # Even though we created a valid jid file in the _make_tmp_jid_dirs call to get # into the correct loop, we need to mock the 'os.path.isfile' check to force the # "corrupted file" check in the clean_old_jobs call. - with patch('os.path.isfile', MagicMock(return_value=False)) as mock: + with patch("os.path.isfile", MagicMock(return_value=False)) as mock: local_cache.clean_old_jobs() # there should be only 1 dir in TMP_JID_DIR @@ -141,23 +145,23 @@ class LocalCacheCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(False, os.path.exists(jid_dir_name)) def test_clean_old_jobs_jid_file_is_cleaned(self): - ''' + """ Test that the entire JID dir is removed when a job is old enough to be removed. - ''' + """ # Create temp job cache dir and jid file jid_dir, jid_file = self._make_tmp_jid_dirs() # File timestamps on Windows aren't as precise. Let a little time pass if salt.utils.platform.is_windows(): - time.sleep(.01) + time.sleep(0.01) # Make sure there is a jid directory - jid_dir_name = jid_file.rpartition('/')[2] - self.assertEqual(jid_dir_name, 'jid') + jid_dir_name = jid_file.rpartition("/")[2] + self.assertEqual(jid_dir_name, "jid") # Call clean_old_jobs function, patching the keep_jobs value with a # very small value to force the call to clean the job. - with patch.dict(local_cache.__opts__, {'keep_jobs': 0.00000001}): + with patch.dict(local_cache.__opts__, {"keep_jobs": 0.00000001}): # Sleep on Windows because time.time is only precise to 3 decimal # points, and therefore subtracting the jid_ctime from time.time # will result in a negative number @@ -174,7 +178,7 @@ class LocalCacheCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): self.assertEqual(False, os.path.exists(jid_dir_name)) def _make_tmp_jid_dirs(self, create_files=True): - ''' + """ Helper function to set up temporary directories and files used for testing the clean_old_jobs function. @@ -187,7 +191,7 @@ class LocalCacheCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): Returns a temp_dir name and a jid_file_path. If create_files is False, the jid_file_path will be None. - ''' + """ # First, create the /tmp/salt_test_job_cache/jobs/ directory to hold jid dirs if not os.path.exists(self.TMP_JID_DIR): os.makedirs(self.TMP_JID_DIR) @@ -197,44 +201,59 @@ class LocalCacheCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): jid_file_path = None if create_files: - dir_name = '/'.join([temp_dir, 'jid']) + dir_name = "/".join([temp_dir, "jid"]) os.mkdir(dir_name) - jid_file_path = '/'.join([dir_name, 'jid']) - with salt.utils.files.fopen(jid_file_path, 'w') as jid_file: - jid_file.write('this is a jid file') + jid_file_path = "/".join([dir_name, "jid"]) + with salt.utils.files.fopen(jid_file_path, "w") as jid_file: + jid_file.write("this is a jid file") return temp_dir, jid_file_path -class Local_CacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): - ''' +class Local_CacheTest( + TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin +): + """ Test the local cache returner - ''' + """ + def setup_loader_modules(self): return { local_cache: { - '__opts__': { - 'cachedir': self.TMP_CACHE_DIR, - 'keep_jobs': self.KEEP_JOBS + "__opts__": { + "cachedir": self.TMP_CACHE_DIR, + "keep_jobs": self.KEEP_JOBS, } } } @classmethod def setUpClass(cls): - cls.TMP_CACHE_DIR = os.path.join(RUNTIME_VARS.TMP, 'rootdir', 'cache') - cls.JOBS_DIR = os.path.join(cls.TMP_CACHE_DIR, 'jobs') - cls.JID_DIR = os.path.join(cls.JOBS_DIR, '31', 'c56eed380a4e899ae12bc42563cfdfc53066fb4a6b53e2378a08ac49064539') - cls.JID_FILE = os.path.join(cls.JID_DIR, 'jid') - cls.JID_MINION_DIR = os.path.join(cls.JID_DIR, 'minion', 'return.p') + cls.TMP_CACHE_DIR = os.path.join(RUNTIME_VARS.TMP, "rootdir", "cache") + cls.JOBS_DIR = os.path.join(cls.TMP_CACHE_DIR, "jobs") + cls.JID_DIR = os.path.join( + cls.JOBS_DIR, + "31", + "c56eed380a4e899ae12bc42563cfdfc53066fb4a6b53e2378a08ac49064539", + ) + cls.JID_FILE = os.path.join(cls.JID_DIR, "jid") + cls.JID_MINION_DIR = os.path.join(cls.JID_DIR, "minion", "return.p") cls.JOB_CACHE_DIR_FILES = [cls.JID_FILE, cls.JID_MINION_DIR] cls.KEEP_JOBS = 0.0000000010 cls.EMPTY_JID_DIR = [] @classmethod def tearDownClass(cls): - for attrname in ('TMP_CACHE_DIR', 'JOBS_DIR', 'JID_DIR', 'JID_FILE', 'JID_MINION_DIR', - 'JOB_CACHE_DIR_FILES', 'KEEP_JOBS', 'EMPTY_JID_DIR'): + for attrname in ( + "TMP_CACHE_DIR", + "JOBS_DIR", + "JID_DIR", + "JID_FILE", + "JID_MINION_DIR", + "JOB_CACHE_DIR_FILES", + "KEEP_JOBS", + "EMPTY_JID_DIR", + ): try: attr_instance = getattr(cls, attrname) if isinstance(attr_instance, six.string_types): @@ -246,16 +265,16 @@ class Local_CacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleM except AttributeError: continue - def _check_dir_files(self, msg, contents, status='None'): - ''' + def _check_dir_files(self, msg, contents, status="None"): + """ helper method to ensure files or dirs are either present or removed - ''' + """ for content in contents: - log.debug('CONTENT %s', content) - if status == 'present': + log.debug("CONTENT %s", content) + if status == "present": check_job_dir = os.path.exists(content) - elif status == 'removed': + elif status == "removed": if os.path.exists(content): check_job_dir = False else: @@ -263,55 +282,62 @@ class Local_CacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleM self.assertTrue(check_job_dir, msg=msg + content) def _add_job(self): - ''' + """ helper method to add job. - ''' + """ # add the job. - opts = self.get_temp_config('master') - opts['cachedir'] = self.TMP_CACHE_DIR - load = {'fun_args': [], 'jid': '20160603132323715452', - 'return': True, 'retcode': 0, 'success': True, - 'cmd': '_return', 'fun': 'test.ping', 'id': 'minion'} + opts = self.get_temp_config("master") + opts["cachedir"] = self.TMP_CACHE_DIR + load = { + "fun_args": [], + "jid": "20160603132323715452", + "return": True, + "retcode": 0, + "success": True, + "cmd": "_return", + "fun": "test.ping", + "id": "minion", + } add_job = salt.utils.job.store_job(opts, load) self.assertEqual(add_job, None) - self._check_dir_files('Dir/file does not exist: ', - self.JOB_CACHE_DIR_FILES, - status='present') + self._check_dir_files( + "Dir/file does not exist: ", self.JOB_CACHE_DIR_FILES, status="present" + ) def test_clean_old_jobs(self): - ''' + """ test to ensure jobs are removed from job cache - ''' + """ self._add_job() # remove job self.assertEqual(local_cache.clean_old_jobs(), None) - self._check_dir_files('job cache was not removed: ', - self.JOB_CACHE_DIR_FILES, - status='removed') + self._check_dir_files( + "job cache was not removed: ", self.JOB_CACHE_DIR_FILES, status="removed" + ) def test_not_clean_new_jobs(self): - ''' + """ test to ensure jobs are not removed when jobs dir is new - ''' + """ self._add_job() - with patch.dict(local_cache.__opts__, {'keep_jobs': 24}): + with patch.dict(local_cache.__opts__, {"keep_jobs": 24}): self.assertEqual(local_cache.clean_old_jobs(), None) - self._check_dir_files('job cache was removed: ', - self.JOB_CACHE_DIR_FILES, - status='present') + self._check_dir_files( + "job cache was removed: ", self.JOB_CACHE_DIR_FILES, status="present" + ) def test_empty_jid_dir(self): - ''' + """ test to ensure removal of empty jid dir - ''' + """ # add empty jid dir - new_jid_dir = os.path.join(self.JOBS_DIR, 'z0') + new_jid_dir = os.path.join(self.JOBS_DIR, "z0") self.EMPTY_JID_DIR.append(new_jid_dir) os.makedirs(new_jid_dir) @@ -320,7 +346,8 @@ class Local_CacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleM # `local_cache.clean_old_jobs` tries to delete the new_jid_dir if salt.utils.platform.is_windows(): import time - lock_dir = new_jid_dir + '.lckchk' + + lock_dir = new_jid_dir + ".lckchk" tries = 0 while True: tries += 1 @@ -338,14 +365,14 @@ class Local_CacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleM continue # check dir exists - self._check_dir_files('new_jid_dir was not created', - self.EMPTY_JID_DIR, - status='present') + self._check_dir_files( + "new_jid_dir was not created", self.EMPTY_JID_DIR, status="present" + ) # remove job self.assertEqual(local_cache.clean_old_jobs(), None) # check jid dir is removed - self._check_dir_files('new_jid_dir was not removed', - self.EMPTY_JID_DIR, - status='removed') + self._check_dir_files( + "new_jid_dir was not removed", self.EMPTY_JID_DIR, status="removed" + ) diff --git a/tests/unit/returners/test_pgjsonb.py b/tests/unit/returners/test_pgjsonb.py index 831464a15c9..b560afdf673 100644 --- a/tests/unit/returners/test_pgjsonb.py +++ b/tests/unit/returners/test_pgjsonb.py @@ -1,51 +1,50 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.returners.pgjsonb_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unit tests for the PGJsonb returner (pgjsonb). -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import logging # Import Salt libs import salt.returners.pgjsonb as pgjsonb +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + log = logging.getLogger(__name__) class PGJsonbCleanOldJobsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for the local_cache.clean_old_jobs function. - ''' + """ + def setup_loader_modules(self): - return {pgjsonb: {'__opts__': {'keep_jobs': 1, 'archive_jobs': 0}}} + return {pgjsonb: {"__opts__": {"keep_jobs": 1, "archive_jobs": 0}}} def test_clean_old_jobs_purge(self): - ''' + """ Tests that the function returns None when no jid_root is found. - ''' + """ connect_mock = MagicMock() - with patch.object(pgjsonb, '_get_serv', connect_mock): - with patch.dict(pgjsonb.__salt__, {'config.option': MagicMock()}): + with patch.object(pgjsonb, "_get_serv", connect_mock): + with patch.dict(pgjsonb.__salt__, {"config.option": MagicMock()}): self.assertEqual(pgjsonb.clean_old_jobs(), None) def test_clean_old_jobs_archive(self): - ''' + """ Tests that the function returns None when no jid_root is found. - ''' + """ connect_mock = MagicMock() - with patch.object(pgjsonb, '_get_serv', connect_mock): - with patch.dict(pgjsonb.__salt__, {'config.option': MagicMock()}): - with patch.dict(pgjsonb.__opts__, {'archive_jobs': 1}): + with patch.object(pgjsonb, "_get_serv", connect_mock): + with patch.dict(pgjsonb.__salt__, {"config.option": MagicMock()}): + with patch.dict(pgjsonb.__opts__, {"archive_jobs": 1}): self.assertEqual(pgjsonb.clean_old_jobs(), None) diff --git a/tests/unit/returners/test_sentry_return.py b/tests/unit/returners/test_sentry_return.py index ccf235919aa..4001e0010a6 100644 --- a/tests/unit/returners/test_sentry_return.py +++ b/tests/unit/returners/test_sentry_return.py @@ -3,24 +3,34 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase - # Import salt libs import salt.returners.sentry_return as sentry +# Import Salt Testing libs +from tests.support.unit import TestCase + class SentryReturnerTestCase(TestCase): - ''' + """ Test Sentry Returner - ''' - ret = {'id': '12345', - 'fun': 'mytest.func', - 'fun_args': ['arg1', 'arg2', {'foo': 'bar'}], - 'jid': '54321', - 'return': 'Long Return containing a Traceback'} + """ + + ret = { + "id": "12345", + "fun": "mytest.func", + "fun_args": ["arg1", "arg2", {"foo": "bar"}], + "jid": "54321", + "return": "Long Return containing a Traceback", + } def test_get_message(self): - self.assertEqual(sentry._get_message(self.ret), 'salt func: mytest.func arg1 arg2 foo=bar') - self.assertEqual(sentry._get_message({'fun': 'test.func', 'fun_args': []}), 'salt func: test.func') - self.assertEqual(sentry._get_message({'fun': 'test.func'}), 'salt func: test.func') + self.assertEqual( + sentry._get_message(self.ret), "salt func: mytest.func arg1 arg2 foo=bar" + ) + self.assertEqual( + sentry._get_message({"fun": "test.func", "fun_args": []}), + "salt func: test.func", + ) + self.assertEqual( + sentry._get_message({"fun": "test.func"}), "salt func: test.func" + ) diff --git a/tests/unit/returners/test_slack_webhook_return.py b/tests/unit/returners/test_slack_webhook_return.py index cdea753011b..78d83fd208f 100644 --- a/tests/unit/returners/test_slack_webhook_return.py +++ b/tests/unit/returners/test_slack_webhook_return.py @@ -1,152 +1,172 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Carlos D. Álvaro <github@cdalvaro.io>` tests.unit.returners.test_slack_webhook_return ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Unit tests for the Slack Webhook Returner. -''' +""" # Import Python libs from __future__ import absolute_import -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch - # Import Salt libs import salt.returners.slack_webhook_return as slack_webhook +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + class SlackWebhookReturnerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test slack_webhook returner - ''' - _WEBHOOK = 'T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX' - _AUTHOR_ICON = 'https://platform.slack-edge.com/img/default_application_icon.png' + """ + + _WEBHOOK = "T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX" + _AUTHOR_ICON = "https://platform.slack-edge.com/img/default_application_icon.png" _SHOW_TASKS = True - _MINION_NAME = 'MacPro' + _MINION_NAME = "MacPro" _RET = { - 'fun_args': ['config.vim'], - 'jid': '20181227105933129338', - 'return': - {'file_|-vim files present_|-/Users/cdalvaro/_|-recurse': - {'comment': 'The directory /Users/cdalvaro/ is in the correct state', - 'pchanges': {}, - 'name': '/Users/cdalvaro/', - 'start_time': '10:59:52.252830', - 'result': True, - 'duration': 373.25, - '__run_num__': 3, - '__sls__': 'config.vim', - 'changes': {}, - '__id__': 'vim files present'}, - 'pkg_|-vim present_|-vim_|-installed': - {'comment': 'All specified packages are already installed', - 'name': 'vim', - 'start_time': '10:59:36.830591', - 'result': True, - 'duration': 1280.127, - '__run_num__': 0, - '__sls__': 'config.vim', - 'changes': {}, - '__id__': 'vim present'}, - 'git_|-salt vim plugin updated_|-https://github.com/saltstack/salt-vim.git_|-latest': - {'comment': 'https://github.com/saltstack/salt-vim.git cloned to /Users/cdalvaro/.vim/pack/git-plugins/start/salt', - 'name': 'https://github.com/saltstack/salt-vim.git', - 'start_time': '11:00:01.892757', - 'result': True, - 'duration': 11243.445, - '__run_num__': 6, - '__sls__': 'config.vim', - 'changes': - {'new': 'https://github.com/saltstack/salt-vim.git => /Users/cdalvaro/.vim/pack/git-plugins/start/salt', - 'revision': {'new': '6ca9e3500cc39dd417b411435d58a1b720b331cc', 'old': None}}, - '__id__': 'salt vim plugin updated'}, - 'pkg_|-macvim present_|-caskroom/cask/macvim_|-installed': - {'comment': 'The following packages failed to install/update: caskroom/cask/macvim', - 'name': 'caskroom/cask/macvim', - 'start_time': '10:59:38.111119', - 'result': False, - 'duration': 14135.45, - '__run_num__': 1, - '__sls__': 'config.vim', - 'changes': {}, - '__id__': 'macvim present'}}, - 'retcode': 2, - 'success': True, - 'fun': 'state.apply', - 'id': _MINION_NAME, - 'out': 'highstate' + "fun_args": ["config.vim"], + "jid": "20181227105933129338", + "return": { + "file_|-vim files present_|-/Users/cdalvaro/_|-recurse": { + "comment": "The directory /Users/cdalvaro/ is in the correct state", + "pchanges": {}, + "name": "/Users/cdalvaro/", + "start_time": "10:59:52.252830", + "result": True, + "duration": 373.25, + "__run_num__": 3, + "__sls__": "config.vim", + "changes": {}, + "__id__": "vim files present", + }, + "pkg_|-vim present_|-vim_|-installed": { + "comment": "All specified packages are already installed", + "name": "vim", + "start_time": "10:59:36.830591", + "result": True, + "duration": 1280.127, + "__run_num__": 0, + "__sls__": "config.vim", + "changes": {}, + "__id__": "vim present", + }, + "git_|-salt vim plugin updated_|-https://github.com/saltstack/salt-vim.git_|-latest": { + "comment": "https://github.com/saltstack/salt-vim.git cloned to /Users/cdalvaro/.vim/pack/git-plugins/start/salt", + "name": "https://github.com/saltstack/salt-vim.git", + "start_time": "11:00:01.892757", + "result": True, + "duration": 11243.445, + "__run_num__": 6, + "__sls__": "config.vim", + "changes": { + "new": "https://github.com/saltstack/salt-vim.git => /Users/cdalvaro/.vim/pack/git-plugins/start/salt", + "revision": { + "new": "6ca9e3500cc39dd417b411435d58a1b720b331cc", + "old": None, + }, + }, + "__id__": "salt vim plugin updated", + }, + "pkg_|-macvim present_|-caskroom/cask/macvim_|-installed": { + "comment": "The following packages failed to install/update: caskroom/cask/macvim", + "name": "caskroom/cask/macvim", + "start_time": "10:59:38.111119", + "result": False, + "duration": 14135.45, + "__run_num__": 1, + "__sls__": "config.vim", + "changes": {}, + "__id__": "macvim present", + }, + }, + "retcode": 2, + "success": True, + "fun": "state.apply", + "id": _MINION_NAME, + "out": "highstate", } _EXPECTED_PAYLOAD = { - u'attachments': [ - {u'title': u'Success: False', - u'color': u'#272727', - u'text': u"Function: state.apply\nFunction Args: ['config.vim']\nJID: 20181227105933129338\nTotal: 4\nDuration: 27.03 secs", - u'author_link': u'{}'.format(_MINION_NAME), - u'author_name': u'{}'.format(_MINION_NAME), - u'fallback': u'{} | Failed'.format(_MINION_NAME), - u'author_icon': _AUTHOR_ICON}, - {u'color': u'good', - u'title': u'Unchanged: 2'}, - {u'color': u'warning', - u'fields': [ - {u'short': False, - u'value': u'config.vim.sls | salt vim plugin updated'} - ], - u'title': u'Changed: 1'}, - {u'color': u'danger', - u'fields': [ - {u'short': False, - u'value': u'config.vim.sls | macvim present'} - ], - u'title': u'Failed: 1'} + u"attachments": [ + { + u"title": u"Success: False", + u"color": u"#272727", + u"text": u"Function: state.apply\nFunction Args: ['config.vim']\nJID: 20181227105933129338\nTotal: 4\nDuration: 27.03 secs", + u"author_link": u"{}".format(_MINION_NAME), + u"author_name": u"{}".format(_MINION_NAME), + u"fallback": u"{} | Failed".format(_MINION_NAME), + u"author_icon": _AUTHOR_ICON, + }, + {u"color": u"good", u"title": u"Unchanged: 2"}, + { + u"color": u"warning", + u"fields": [ + { + u"short": False, + u"value": u"config.vim.sls | salt vim plugin updated", + } + ], + u"title": u"Changed: 1", + }, + { + u"color": u"danger", + u"fields": [ + {u"short": False, u"value": u"config.vim.sls | macvim present"} + ], + u"title": u"Failed: 1", + }, ] } def setup_loader_modules(self): - return {slack_webhook: {'__opts__': { - 'slack_webhook.webhook': self._WEBHOOK, - 'slack_webhook.author_icon': self._AUTHOR_ICON, - 'slack_webhook.success_title': '{id} | Succeeded', - 'slack_webhook.failure_title': '{id} | Failed', - 'slack_webhook.show_tasks': self._SHOW_TASKS - }}} + return { + slack_webhook: { + "__opts__": { + "slack_webhook.webhook": self._WEBHOOK, + "slack_webhook.author_icon": self._AUTHOR_ICON, + "slack_webhook.success_title": "{id} | Succeeded", + "slack_webhook.failure_title": "{id} | Failed", + "slack_webhook.show_tasks": self._SHOW_TASKS, + } + } + } def test_no_webhook(self): - ''' + """ Test returner stops if no webhook is defined - ''' - with patch.dict(slack_webhook.__opts__, {'slack_webhook.webhook': ''}): + """ + with patch.dict(slack_webhook.__opts__, {"slack_webhook.webhook": ""}): self.assertEqual(slack_webhook.returner(self._RET), None) def test_returner(self): - ''' + """ Test to see if the Slack Webhook returner sends a message - ''' - query_ret = {'body': 'ok', 'status': 200} - with patch('salt.utils.http.query', return_value=query_ret): + """ + query_ret = {"body": "ok", "status": 200} + with patch("salt.utils.http.query", return_value=query_ret): self.assertTrue(slack_webhook.returner(self._RET)) def test_generate_payload(self): - ''' + """ Test _generate_payload private method - ''' - test_title = '{} | Failed'.format(self._MINION_NAME) - test_report = slack_webhook._generate_report( - self._RET, self._SHOW_TASKS) + """ + test_title = "{} | Failed".format(self._MINION_NAME) + test_report = slack_webhook._generate_report(self._RET, self._SHOW_TASKS) custom_grains = slack_webhook.__grains__ - custom_grains['id'] = self._MINION_NAME - custom_grains['localhost'] = self._MINION_NAME + custom_grains["id"] = self._MINION_NAME + custom_grains["localhost"] = self._MINION_NAME with patch.dict(slack_webhook.__grains__, custom_grains): test_payload = slack_webhook._generate_payload( - self._AUTHOR_ICON, test_title, test_report) + self._AUTHOR_ICON, test_title, test_report + ) self.assertDictEqual(test_payload, self._EXPECTED_PAYLOAD) diff --git a/tests/unit/returners/test_smtp_return.py b/tests/unit/returners/test_smtp_return.py index c000a29e6fe..64b36492639 100644 --- a/tests/unit/returners/test_smtp_return.py +++ b/tests/unit/returners/test_smtp_return.py @@ -1,89 +1,99 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place (mp@saltstack.com) tests.unit.returners.smtp_return_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import salt libs import salt.returners.smtp_return as smtp from salt.utils.jinja import SaltCacheLoader +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + try: import gnupg # pylint: disable=unused-import + HAS_GNUPG = True except ImportError: HAS_GNUPG = False class SMTPReturnerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test SMTP returner - ''' + """ + def setup_loader_modules(self): return { smtp: { - '__opts__': { - 'extension_modules': '', - 'optimization_order': [0, 1, 2], - 'renderer': 'jinja|yaml', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'file_roots': {}, - 'pillar_roots': {}, - 'cachedir': '/', - 'master_uri': 'tcp://127.0.0.1:4505', - 'pki_dir': '/', - 'keysize': 2048, - 'id': 'test', - '__role': 'minion', + "__opts__": { + "extension_modules": "", + "optimization_order": [0, 1, 2], + "renderer": "jinja|yaml", + "renderer_blacklist": [], + "renderer_whitelist": [], + "file_roots": {}, + "pillar_roots": {}, + "cachedir": "/", + "master_uri": "tcp://127.0.0.1:4505", + "pki_dir": "/", + "keysize": 2048, + "id": "test", + "__role": "minion", } } } def _test_returner(self, mocked_smtplib): # pylint: disable=unused-argument - ''' + """ Test to see if the SMTP returner sends a message - ''' - ret = {'id': '12345', - 'fun': 'mytest.func', - 'fun_args': 'myfunc args', - 'jid': '54321', - 'return': 'The room is on fire as shes fixing her hair'} - options = {'username': '', - 'tls': '', - 'from': '', - 'fields': 'id,fun,fun_args,jid,return', - 'to': '', - 'host': '', - 'renderer': 'jinja|yaml', - 'template': '', - 'password': '', - 'gpgowner': '', - 'subject': ''} + """ + ret = { + "id": "12345", + "fun": "mytest.func", + "fun_args": "myfunc args", + "jid": "54321", + "return": "The room is on fire as shes fixing her hair", + } + options = { + "username": "", + "tls": "", + "from": "", + "fields": "id,fun,fun_args,jid,return", + "to": "", + "host": "", + "renderer": "jinja|yaml", + "template": "", + "password": "", + "gpgowner": "", + "subject": "", + } - with patch('salt.returners.smtp_return._get_options', MagicMock(return_value=options)), \ - patch.object(SaltCacheLoader, 'file_client', MagicMock()): + with patch( + "salt.returners.smtp_return._get_options", MagicMock(return_value=options) + ), patch.object(SaltCacheLoader, "file_client", MagicMock()): smtp.returner(ret) self.assertTrue(mocked_smtplib.return_value.sendmail.called) if HAS_GNUPG: + def test_returner(self): - with patch('salt.returners.smtp_return.gnupg'), \ - patch('salt.returners.smtp_return.smtplib.SMTP') as mocked_smtplib: + with patch("salt.returners.smtp_return.gnupg"), patch( + "salt.returners.smtp_return.smtplib.SMTP" + ) as mocked_smtplib: self._test_returner(mocked_smtplib) else: + def test_returner(self): - with patch('salt.returners.smtp_return.smtplib.SMTP') as mocked_smtplib: + with patch("salt.returners.smtp_return.smtplib.SMTP") as mocked_smtplib: self._test_returner(mocked_smtplib) diff --git a/tests/unit/returners/test_syslog_return.py b/tests/unit/returners/test_syslog_return.py index 5f107e5c94a..0b9db73e699 100644 --- a/tests/unit/returners/test_syslog_return.py +++ b/tests/unit/returners/test_syslog_return.py @@ -1,44 +1,57 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Megan Wilhite (mwilhite@saltstack.com)` tests.unit.returners.test_syslog_return ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch - # Import salt libs import salt.returners.syslog_return as syslog +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + class SyslogReturnerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test Syslog returner - ''' + """ + def setup_loader_modules(self): return {syslog: {}} - @skipIf(not syslog.HAS_SYSLOG, 'Skip when syslog not installed') + @skipIf(not syslog.HAS_SYSLOG, "Skip when syslog not installed") def test_syslog_returner_unicode(self): - ''' + """ test syslog returner with unicode - ''' - ret = {'fun_args': [], 'jid': '20180713160901624786', 'return': True, - 'retcode': 0, 'success': True, 'fun': 'test.ping', 'id': '02e10e971a30'} - opts = {u'level': u'LOG_INFO', u'options': [], - u'facility': u'LOG_USER', u'tag': u'salt-minion'} + """ + ret = { + "fun_args": [], + "jid": "20180713160901624786", + "return": True, + "retcode": 0, + "success": True, + "fun": "test.ping", + "id": "02e10e971a30", + } + opts = { + "level": "LOG_INFO", + "options": [], + "facility": "LOG_USER", + "tag": "salt-minion", + } - with patch('salt.returners.syslog_return._get_options', - MagicMock(return_value=opts)): + with patch( + "salt.returners.syslog_return._get_options", MagicMock(return_value=opts) + ): try: syslog.returner(ret) except Exception as e: # pylint: disable=broad-except - self.fail('syslog.returner() failed with exception: {0}'.format(e)) + self.fail("syslog.returner() failed with exception: {0}".format(e)) diff --git a/tests/unit/returners/test_telegram_return.py b/tests/unit/returners/test_telegram_return.py index 2a6ec6a66af..6540569d901 100644 --- a/tests/unit/returners/test_telegram_return.py +++ b/tests/unit/returners/test_telegram_return.py @@ -1,45 +1,49 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Roald Nefs (info@roaldnefs.com)` tests.unit.returners.telegram_return_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import salt libs import salt.returners.telegram_return as telegram +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class TelegramReturnerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test Telegram Returner - ''' + """ + def setup_loader_modules(self): return {telegram: {}} def test_returner(self): - ''' + """ Test to see if the Telegram returner sends a message - ''' - ret = {'id': '12345', - 'fun': 'mytest.func', - 'fun_args': 'myfunc args', - 'jid': '54321', - 'return': 'The room is on fire as shes fixing her hair'} - options = {'chat_id': '', - 'token': ''} + """ + ret = { + "id": "12345", + "fun": "mytest.func", + "fun_args": "myfunc args", + "jid": "54321", + "return": "The room is on fire as shes fixing her hair", + } + options = {"chat_id": "", "token": ""} - with patch('salt.returners.telegram_return._get_options', - MagicMock(return_value=options)), \ - patch.dict('salt.returners.telegram_return.__salt__', - {'telegram.post_message': MagicMock(return_value=True)} - ): + with patch( + "salt.returners.telegram_return._get_options", + MagicMock(return_value=options), + ), patch.dict( + "salt.returners.telegram_return.__salt__", + {"telegram.post_message": MagicMock(return_value=True)}, + ): self.assertTrue(telegram.returner(ret)) diff --git a/tests/unit/roster/test_ansible.py b/tests/unit/roster/test_ansible.py index a2ea996324e..a5cdcbbdbc4 100644 --- a/tests/unit/roster/test_ansible.py +++ b/tests/unit/roster/test_ansible.py @@ -2,95 +2,93 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mock import ( - patch -) -from tests.support import mixins -from tests.support.unit import skipIf, TestCase -from tests.support.runtests import RUNTIME_VARS +import os # Import Salt Libs import salt.config import salt.loader import salt.roster.ansible as ansible +from tests.support import mixins +# Import Salt Testing Libs +from tests.support.mock import patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf EXPECTED = { - 'host1': { - 'host': 'host1', - 'passwd': 'test123', - 'minion_opts': { - 'escape_pods': 2, - 'halon_system_timeout': 30, - 'self_destruct_countdown': 60, - 'some_server': 'foo.southeast.example.com' - } + "host1": { + "host": "host1", + "passwd": "test123", + "minion_opts": { + "escape_pods": 2, + "halon_system_timeout": 30, + "self_destruct_countdown": 60, + "some_server": "foo.southeast.example.com", + }, }, - 'host2': { - 'host': 'host2', - 'passwd': 'test123', - 'minion_opts': { - 'escape_pods': 2, - 'halon_system_timeout': 30, - 'self_destruct_countdown': 60, - 'some_server': 'foo.southeast.example.com' - } + "host2": { + "host": "host2", + "passwd": "test123", + "minion_opts": { + "escape_pods": 2, + "halon_system_timeout": 30, + "self_destruct_countdown": 60, + "some_server": "foo.southeast.example.com", + }, + }, + "host3": { + "host": "host3", + "passwd": "test123", + "minion_opts": { + "escape_pods": 2, + "halon_system_timeout": 30, + "self_destruct_countdown": 60, + "some_server": "foo.southeast.example.com", + }, }, - 'host3': { - 'host': 'host3', - 'passwd': 'test123', - 'minion_opts': { - 'escape_pods': 2, - 'halon_system_timeout': 30, - 'self_destruct_countdown': 60, - 'some_server': 'foo.southeast.example.com' - } - } } -@skipIf(not salt.utils.path.which('ansible-inventory'), 'Skipping because ansible-inventory is not available') +@skipIf( + not salt.utils.path.which("ansible-inventory"), + "Skipping because ansible-inventory is not available", +) class AnsibleRosterTestCase(TestCase, mixins.LoaderModuleMockMixin): - @classmethod def setUpClass(cls): - cls.roster_dir = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/rosters/ansible/') - cls.opts = {'roster_defaults': {'passwd': 'test123'}} + cls.roster_dir = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/rosters/ansible/" + ) + cls.opts = {"roster_defaults": {"passwd": "test123"}} @classmethod def tearDownClass(cls): - delattr(cls, 'roster_dir') - delattr(cls, 'opts') + delattr(cls, "roster_dir") + delattr(cls, "opts") def setup_loader_modules(self): - opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master')) - utils = salt.loader.utils(opts, whitelist=['json', 'stringutils']) - runner = salt.loader.runner(opts, utils=utils, whitelist=['salt']) - return { - ansible: { - '__utils__': utils, - '__opts__': {}, - '__runner__': runner - } - } + opts = salt.config.master_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master") + ) + utils = salt.loader.utils(opts, whitelist=["json", "stringutils"]) + runner = salt.loader.runner(opts, utils=utils, whitelist=["salt"]) + return {ansible: {"__utils__": utils, "__opts__": {}, "__runner__": runner}} def test_ini(self): - self.opts['roster_file'] = os.path.join(self.roster_dir, 'roster.ini') + self.opts["roster_file"] = os.path.join(self.roster_dir, "roster.ini") with patch.dict(ansible.__opts__, self.opts): - ret = ansible.targets('*') + ret = ansible.targets("*") assert ret == EXPECTED def test_yml(self): - self.opts['roster_file'] = os.path.join(self.roster_dir, 'roster.yml') + self.opts["roster_file"] = os.path.join(self.roster_dir, "roster.yml") with patch.dict(ansible.__opts__, self.opts): - ret = ansible.targets('*') + ret = ansible.targets("*") assert ret == EXPECTED def test_script(self): - self.opts['roster_file'] = os.path.join(self.roster_dir, 'roster.py') + self.opts["roster_file"] = os.path.join(self.roster_dir, "roster.py") with patch.dict(ansible.__opts__, self.opts): - ret = ansible.targets('*') + ret = ansible.targets("*") assert ret == EXPECTED diff --git a/tests/unit/roster/test_clustershell.py b/tests/unit/roster/test_clustershell.py index d02d3e7f86c..2870f84ac17 100644 --- a/tests/unit/roster/test_clustershell.py +++ b/tests/unit/roster/test_clustershell.py @@ -1,44 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for clustershell roster -''' +""" # Import Python libs from __future__ import absolute_import +from tests.support.mock import MagicMock, patch + # Import Salt Testing libraries from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch) # Import third-party libs try: from ClusterShell.NodeSet import NodeSet # pylint: disable=unused-import + HAS_CLUSTERSHELL = True except (ImportError, OSError) as e: HAS_CLUSTERSHELL = False -@skipIf(HAS_CLUSTERSHELL is False, 'Install Python Clustershell bindings before running these tests.') +@skipIf( + HAS_CLUSTERSHELL is False, + "Install Python Clustershell bindings before running these tests.", +) class ClusterShellTestCase(TestCase): - ''' + """ Test cases for clustershell roster - ''' + """ + def test_targets(self): mock_socket = MagicMock() mock_nodeset = MagicMock() - mock_nodeset.NodeSet.return_value = ['foo'] - with patch.dict('sys.modules', **{'socket': mock_socket, 'ClusterShell.NodeSet': mock_nodeset}): + mock_nodeset.NodeSet.return_value = ["foo"] + with patch.dict( + "sys.modules", + **{"socket": mock_socket, "ClusterShell.NodeSet": mock_nodeset} + ): import salt.roster.clustershell + salt.roster.clustershell.__opts__ = {} - with patch.dict(salt.roster.clustershell.__opts__, {'ssh_scan_ports': [1, 2, 3], - 'ssh_scan_timeout': 30}): + with patch.dict( + salt.roster.clustershell.__opts__, + {"ssh_scan_ports": [1, 2, 3], "ssh_scan_timeout": 30}, + ): # Reimports are necessary to re-init the namespace. # pylint: disable=unused-import import socket from ClusterShell.NodeSet import NodeSet + # pylint: enable=unused-import - ret = salt.roster.clustershell.targets('foo') - mock_socket.gethostbyname.assert_any_call('foo') - self.assertTrue('foo' in ret) - self.assertTrue(ret['foo']['port'] == 3) + ret = salt.roster.clustershell.targets("foo") + mock_socket.gethostbyname.assert_any_call("foo") + self.assertTrue("foo" in ret) + self.assertTrue(ret["foo"]["port"] == 3) diff --git a/tests/unit/roster/test_sshconfig.py b/tests/unit/roster/test_sshconfig.py index a06fa39f792..04a81000a16 100644 --- a/tests/unit/roster/test_sshconfig.py +++ b/tests/unit/roster/test_sshconfig.py @@ -2,18 +2,16 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import collections -# Import Salt Testing Libs -from tests.support.mock import ( - mock_open, - patch -) -from tests.support import mixins -from tests.support.unit import TestCase +import collections # Import Salt Libs import salt.roster.sshconfig as sshconfig +from tests.support import mixins + +# Import Salt Testing Libs +from tests.support.mock import mock_open, patch +from tests.support.unit import TestCase _SAMPLE_SSH_CONFIG = """ Host * @@ -35,38 +33,43 @@ Host def.asdfgfdhgjkl.com HostName 234.234.234.234 """ -_TARGET_ABC = collections.OrderedDict([ - ('user', 'user.mcuserface'), - ('priv', '~/.ssh/id_rsa_abc'), - ('host', 'abc.asdfgfdhgjkl.com') -]) +_TARGET_ABC = collections.OrderedDict( + [ + ("user", "user.mcuserface"), + ("priv", "~/.ssh/id_rsa_abc"), + ("host", "abc.asdfgfdhgjkl.com"), + ] +) -_TARGET_ABC123 = collections.OrderedDict([ - ('user', 'user.mcuserface'), - ('priv', '~/.ssh/id_rsa_abc'), - ('host', 'abc123.asdfgfdhgjkl.com') -]) +_TARGET_ABC123 = collections.OrderedDict( + [ + ("user", "user.mcuserface"), + ("priv", "~/.ssh/id_rsa_abc"), + ("host", "abc123.asdfgfdhgjkl.com"), + ] +) -_TARGET_DEF = collections.OrderedDict([ - ('user', 'user.mcuserface'), - ('priv', '~/.ssh/id_rsa_def'), - ('host', 'def.asdfgfdhgjkl.com') -]) +_TARGET_DEF = collections.OrderedDict( + [ + ("user", "user.mcuserface"), + ("priv", "~/.ssh/id_rsa_def"), + ("host", "def.asdfgfdhgjkl.com"), + ] +) _ALL = { - 'abc.asdfgfdhgjkl.com': _TARGET_ABC, - 'abc123.asdfgfdhgjkl.com': _TARGET_ABC123, - 'def.asdfgfdhgjkl.com': _TARGET_DEF + "abc.asdfgfdhgjkl.com": _TARGET_ABC, + "abc123.asdfgfdhgjkl.com": _TARGET_ABC123, + "def.asdfgfdhgjkl.com": _TARGET_DEF, } _ABC_GLOB = { - 'abc.asdfgfdhgjkl.com': _TARGET_ABC, - 'abc123.asdfgfdhgjkl.com': _TARGET_ABC123 + "abc.asdfgfdhgjkl.com": _TARGET_ABC, + "abc123.asdfgfdhgjkl.com": _TARGET_ABC123, } class SSHConfigRosterTestCase(TestCase, mixins.LoaderModuleMockMixin): - def setUp(self): self.mock_fp = mock_open(read_data=_SAMPLE_SSH_CONFIG) @@ -74,13 +77,13 @@ class SSHConfigRosterTestCase(TestCase, mixins.LoaderModuleMockMixin): return {sshconfig: {}} def test_all(self): - with patch('salt.utils.files.fopen', self.mock_fp): - with patch('salt.roster.sshconfig._get_ssh_config_file'): - targets = sshconfig.targets('*') + with patch("salt.utils.files.fopen", self.mock_fp): + with patch("salt.roster.sshconfig._get_ssh_config_file"): + targets = sshconfig.targets("*") self.assertEqual(targets, _ALL) def test_abc_glob(self): - with patch('salt.utils.files.fopen', self.mock_fp): - with patch('salt.roster.sshconfig._get_ssh_config_file'): - targets = sshconfig.targets('abc*') + with patch("salt.utils.files.fopen", self.mock_fp): + with patch("salt.roster.sshconfig._get_ssh_config_file"): + targets = sshconfig.targets("abc*") self.assertEqual(targets, _ABC_GLOB) diff --git a/tests/unit/roster/test_terraform.py b/tests/unit/roster/test_terraform.py index bc9fefd6fa6..579fc6261c2 100644 --- a/tests/unit/roster/test_terraform.py +++ b/tests/unit/roster/test_terraform.py @@ -1,101 +1,116 @@ # -*- coding: utf-8 -*- -''' +""" unittests for terraform roster -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals -import os.path -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.runtests import RUNTIME_VARS -from tests.support.mock import ( - patch, -) +import os.path # Import Salt Libs import salt.config import salt.loader from salt.roster import terraform +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase + class TerraformTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.roster.terraform - ''' + """ + def setup_loader_modules(self): - opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master')) - utils = salt.loader.utils(opts, whitelist=['roster_matcher']) - return { - terraform: { - '__utils__': utils, - '__opts__': {}, - } - } + opts = salt.config.master_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master") + ) + utils = salt.loader.utils(opts, whitelist=["roster_matcher"]) + return {terraform: {"__utils__": utils, "__opts__": {}}} def test_default_output(self): - ''' + """ Test the output of a fixture tfstate file wich contains libvirt resources. - ''' - tfstate = os.path.join(os.path.dirname(__file__), 'terraform.data', 'terraform.tfstate') - pki_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'terraform.data')) + """ + tfstate = os.path.join( + os.path.dirname(__file__), "terraform.data", "terraform.tfstate" + ) + pki_dir = os.path.abspath( + os.path.join(os.path.dirname(__file__), "terraform.data") + ) - with patch.dict(terraform.__opts__, {'roster_file': tfstate, 'pki_dir': pki_dir}): + with patch.dict( + terraform.__opts__, {"roster_file": tfstate, "pki_dir": pki_dir} + ): expected_result = { - 'db0': { - 'host': '192.168.122.174', - 'user': 'root', - 'passwd': 'dbpw', - 'tty': True, - 'priv': os.path.join(pki_dir, 'ssh', 'salt-ssh.rsa')}, - 'db1': { - 'host': '192.168.122.190', - 'user': 'root', - 'passwd': 'dbpw', - 'tty': True, - 'priv': os.path.join(pki_dir, 'ssh', 'salt-ssh.rsa')}, - 'web0': { - 'host': '192.168.122.106', - 'user': 'root', - 'passwd': 'linux', - 'timeout': 22, - 'priv': os.path.join(pki_dir, 'ssh', 'salt-ssh.rsa')}, - 'web1': { - 'host': '192.168.122.235', - 'user': 'root', - 'passwd': 'linux', - 'timeout': 22, - 'priv': os.path.join(pki_dir, 'ssh', 'salt-ssh.rsa')} + "db0": { + "host": "192.168.122.174", + "user": "root", + "passwd": "dbpw", + "tty": True, + "priv": os.path.join(pki_dir, "ssh", "salt-ssh.rsa"), + }, + "db1": { + "host": "192.168.122.190", + "user": "root", + "passwd": "dbpw", + "tty": True, + "priv": os.path.join(pki_dir, "ssh", "salt-ssh.rsa"), + }, + "web0": { + "host": "192.168.122.106", + "user": "root", + "passwd": "linux", + "timeout": 22, + "priv": os.path.join(pki_dir, "ssh", "salt-ssh.rsa"), + }, + "web1": { + "host": "192.168.122.235", + "user": "root", + "passwd": "linux", + "timeout": 22, + "priv": os.path.join(pki_dir, "ssh", "salt-ssh.rsa"), + }, } - ret = terraform.targets('*') + ret = terraform.targets("*") self.assertDictEqual(expected_result, ret) def test_default_matching(self): - ''' + """ Test the output of a fixture tfstate file wich contains libvirt resources using matching - ''' - tfstate = os.path.join(os.path.dirname(__file__), 'terraform.data', 'terraform.tfstate') - pki_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'terraform.data')) + """ + tfstate = os.path.join( + os.path.dirname(__file__), "terraform.data", "terraform.tfstate" + ) + pki_dir = os.path.abspath( + os.path.join(os.path.dirname(__file__), "terraform.data") + ) - with patch.dict(terraform.__opts__, {'roster_file': tfstate, 'pki_dir': pki_dir}): + with patch.dict( + terraform.__opts__, {"roster_file": tfstate, "pki_dir": pki_dir} + ): expected_result = { - 'web0': { - 'host': '192.168.122.106', - 'user': 'root', - 'passwd': 'linux', - 'timeout': 22, - 'priv': os.path.join(pki_dir, 'ssh', 'salt-ssh.rsa')}, - 'web1': { - 'host': '192.168.122.235', - 'user': 'root', - 'passwd': 'linux', - 'timeout': 22, - 'priv': os.path.join(pki_dir, 'ssh', 'salt-ssh.rsa')} + "web0": { + "host": "192.168.122.106", + "user": "root", + "passwd": "linux", + "timeout": 22, + "priv": os.path.join(pki_dir, "ssh", "salt-ssh.rsa"), + }, + "web1": { + "host": "192.168.122.235", + "user": "root", + "passwd": "linux", + "timeout": 22, + "priv": os.path.join(pki_dir, "ssh", "salt-ssh.rsa"), + }, } - ret = terraform.targets('*web*') + ret = terraform.targets("*web*") self.assertDictEqual(expected_result, ret) diff --git a/tests/unit/runners/test_cache.py b/tests/unit/runners/test_cache.py index 686c3c78e5b..d290329d32a 100644 --- a/tests/unit/runners/test_cache.py +++ b/tests/unit/runners/test_cache.py @@ -1,40 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the cache runner -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch -) - # Import Salt Libs import salt.runners.cache as cache import salt.utils.master +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class CacheTest(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the cache runner - ''' + """ + def setup_loader_modules(self): - return {cache: {'__opts__': {'cache': 'localfs', 'pki_dir': RUNTIME_VARS.TMP, 'key_cache': True}}} + return { + cache: { + "__opts__": { + "cache": "localfs", + "pki_dir": RUNTIME_VARS.TMP, + "key_cache": True, + } + } + } def test_grains(self): - ''' + """ test cache.grains runner - ''' - mock_minion = ['Larry'] + """ + mock_minion = ["Larry"] mock_ret = {} self.assertEqual(cache.grains(minion=mock_minion), mock_ret) - mock_data = 'grain stuff' + mock_data = "grain stuff" class MockMaster(object): def __init__(self, *args, **kwargs): @@ -43,5 +50,5 @@ class CacheTest(TestCase, LoaderModuleMockMixin): def get_minion_grains(self): return mock_data - with patch.object(salt.utils.master, 'MasterPillarUtil', MockMaster): + with patch.object(salt.utils.master, "MasterPillarUtil", MockMaster): self.assertEqual(cache.grains(), mock_data) diff --git a/tests/unit/runners/test_jobs.py b/tests/unit/runners/test_jobs.py index f0a18fc5a73..8487013029a 100644 --- a/tests/unit/runners/test_jobs.py +++ b/tests/unit/runners/test_jobs.py @@ -1,47 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the jobs runner -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch -) +import salt.minion # Import Salt Libs import salt.runners.jobs as jobs -import salt.minion + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase class JobsTest(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the jobs runner - ''' + """ + def setup_loader_modules(self): - return {jobs: {'__opts__': {'ext_job_cache': None, 'master_job_cache': 'local_cache'}}} + return { + jobs: { + "__opts__": {"ext_job_cache": None, "master_job_cache": "local_cache"} + } + } def test_list_jobs_with_search_target(self): - ''' + """ test jobs.list_jobs runner with search_target args - ''' + """ mock_jobs_cache = { - '20160524035503086853': {'Arguments': [], - 'Function': 'test.ping', - 'StartTime': '2016, May 24 03:55:03.086853', - 'Target': 'node-1-1.com', - 'Target-type': 'glob', - 'User': 'root'}, - '20160524035524895387': {'Arguments': [], - 'Function': 'test.ping', - 'StartTime': '2016, May 24 03:55:24.895387', - 'Target': ['node-1-2.com', 'node-1-1.com'], - 'Target-type': 'list', - 'User': 'sudo_ubuntu'} + "20160524035503086853": { + "Arguments": [], + "Function": "test.ping", + "StartTime": "2016, May 24 03:55:03.086853", + "Target": "node-1-1.com", + "Target-type": "glob", + "User": "root", + }, + "20160524035524895387": { + "Arguments": [], + "Function": "test.ping", + "StartTime": "2016, May 24 03:55:24.895387", + "Target": ["node-1-2.com", "node-1-1.com"], + "Target-type": "list", + "User": "sudo_ubuntu", + }, } def return_mock_jobs(): @@ -49,29 +57,35 @@ class JobsTest(TestCase, LoaderModuleMockMixin): class MockMasterMinion(object): - returners = {'local_cache.get_jids': return_mock_jobs} + returners = {"local_cache.get_jids": return_mock_jobs} def __init__(self, *args, **kwargs): pass - returns = {'all': mock_jobs_cache, - 'node-1-1.com': mock_jobs_cache, - 'node-1-2.com': {'20160524035524895387': - mock_jobs_cache['20160524035524895387']}, - 'non-existant': {}} + returns = { + "all": mock_jobs_cache, + "node-1-1.com": mock_jobs_cache, + "node-1-2.com": { + "20160524035524895387": mock_jobs_cache["20160524035524895387"] + }, + "non-existant": {}, + } - with patch.object(salt.minion, 'MasterMinion', MockMasterMinion): - self.assertEqual(jobs.list_jobs(), returns['all']) + with patch.object(salt.minion, "MasterMinion", MockMasterMinion): + self.assertEqual(jobs.list_jobs(), returns["all"]) - self.assertEqual(jobs.list_jobs(search_target=['node-1-1*', - 'node-1-2*']), - returns['all']) + self.assertEqual( + jobs.list_jobs(search_target=["node-1-1*", "node-1-2*"]), returns["all"] + ) - self.assertEqual(jobs.list_jobs(search_target='node-1-1.com'), - returns['node-1-1.com']) + self.assertEqual( + jobs.list_jobs(search_target="node-1-1.com"), returns["node-1-1.com"] + ) - self.assertEqual(jobs.list_jobs(search_target='node-1-2.com'), - returns['node-1-2.com']) + self.assertEqual( + jobs.list_jobs(search_target="node-1-2.com"), returns["node-1-2.com"] + ) - self.assertEqual(jobs.list_jobs(search_target='non-existant'), - returns['non-existant']) + self.assertEqual( + jobs.list_jobs(search_target="non-existant"), returns["non-existant"] + ) diff --git a/tests/unit/runners/test_queue.py b/tests/unit/runners/test_queue.py index 3fd41dd3541..6e14dcc71ba 100644 --- a/tests/unit/runners/test_queue.py +++ b/tests/unit/runners/test_queue.py @@ -1,66 +1,61 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the cache runner -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import os # Import Salt Libs import salt.runners.queue as queue_mod +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class QueueTest(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the queue runner - ''' + """ + def setup_loader_modules(self): return { queue_mod: { - '__opts__': { - 'sock_dir': os.path.join(RUNTIME_VARS.TMP, 'queue-runner-sock-dir'), - 'transport': 'zeromq' + "__opts__": { + "sock_dir": os.path.join(RUNTIME_VARS.TMP, "queue-runner-sock-dir"), + "transport": "zeromq", } } } def test_insert_runner(self): queue_insert = MagicMock(return_value=True) - with patch.object(queue_mod, 'insert', queue_insert): - queue_mod.insert_runner('test.stdout_print', queue='salt') + with patch.object(queue_mod, "insert", queue_insert): + queue_mod.insert_runner("test.stdout_print", queue="salt") expected_call = { - 'queue': 'salt', - 'items': { - 'fun': 'test.stdout_print', - 'args': [], - 'kwargs': {}, - }, - 'backend': 'pgjsonb', + "queue": "salt", + "items": {"fun": "test.stdout_print", "args": [], "kwargs": {}}, + "backend": "pgjsonb", } queue_insert.assert_called_once_with(**expected_call) def test_process_runner(self): - ret = [{ - 'fun': 'test.stdout_print', - 'args': [], - 'kwargs': {}, - }] + ret = [{"fun": "test.stdout_print", "args": [], "kwargs": {}}] queue_pop = MagicMock(return_value=ret) test_stdout_print = MagicMock(return_value=True) - with patch.dict(queue_mod.__salt__, {'test.stdout_print': test_stdout_print}): - with patch.object(queue_mod, 'pop', queue_pop): - queue_mod.process_runner(queue='salt') - queue_pop.assert_called_once_with(is_runner=True, queue='salt', quantity=1, backend='pgjsonb') + with patch.dict(queue_mod.__salt__, {"test.stdout_print": test_stdout_print}): + with patch.object(queue_mod, "pop", queue_pop): + queue_mod.process_runner(queue="salt") + queue_pop.assert_called_once_with( + is_runner=True, queue="salt", quantity=1, backend="pgjsonb" + ) test_stdout_print.assert_called_once_with() - queue_pop.assert_called_once_with(is_runner=True, queue='salt', quantity=1, backend='pgjsonb') + queue_pop.assert_called_once_with( + is_runner=True, queue="salt", quantity=1, backend="pgjsonb" + ) diff --git a/tests/unit/runners/test_vault.py b/tests/unit/runners/test_vault.py index c9ae7aa10a2..59e7ef38715 100644 --- a/tests/unit/runners/test_vault.py +++ b/tests/unit/runners/test_vault.py @@ -1,178 +1,187 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for the Vault runner -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import logging -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - Mock, - patch, - ANY, - call -) +import salt.runners.vault as vault # Import salt libs from salt.ext import six -import salt.runners.vault as vault + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import ANY, MagicMock, Mock, call, patch +from tests.support.unit import TestCase log = logging.getLogger(__name__) class VaultTest(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for the runner module of the Vault integration - ''' + """ def setup_loader_modules(self): return {vault: {}} def setUp(self): self.grains = { - 'id': 'test-minion', - 'roles': ['web', 'database'], - 'aux': ['foo', 'bar'], - 'deep': { - 'foo': { - 'bar': { - 'baz': [ - 'hello', - 'world' - ] - } - } - }, - 'mixedcase': 'UP-low-UP' - } + "id": "test-minion", + "roles": ["web", "database"], + "aux": ["foo", "bar"], + "deep": {"foo": {"bar": {"baz": ["hello", "world"]}}}, + "mixedcase": "UP-low-UP", + } def tearDown(self): del self.grains def test_pattern_list_expander(self): - ''' + """ Ensure _expand_pattern_lists works as intended: - Expand list-valued patterns - Do not change non-list-valued tokens - ''' + """ cases = { - 'no-tokens-to-replace': ['no-tokens-to-replace'], - 'single-dict:{minion}': ['single-dict:{minion}'], - 'single-list:{grains[roles]}': ['single-list:web', 'single-list:database'], - 'multiple-lists:{grains[roles]}+{grains[aux]}': [ - 'multiple-lists:web+foo', - 'multiple-lists:web+bar', - 'multiple-lists:database+foo', - 'multiple-lists:database+bar', - ], - 'single-list-with-dicts:{grains[id]}+{grains[roles]}+{grains[id]}': [ - 'single-list-with-dicts:{grains[id]}+web+{grains[id]}', - 'single-list-with-dicts:{grains[id]}+database+{grains[id]}' - ], - 'deeply-nested-list:{grains[deep][foo][bar][baz]}': [ - 'deeply-nested-list:hello', - 'deeply-nested-list:world' - ] - } + "no-tokens-to-replace": ["no-tokens-to-replace"], + "single-dict:{minion}": ["single-dict:{minion}"], + "single-list:{grains[roles]}": ["single-list:web", "single-list:database"], + "multiple-lists:{grains[roles]}+{grains[aux]}": [ + "multiple-lists:web+foo", + "multiple-lists:web+bar", + "multiple-lists:database+foo", + "multiple-lists:database+bar", + ], + "single-list-with-dicts:{grains[id]}+{grains[roles]}+{grains[id]}": [ + "single-list-with-dicts:{grains[id]}+web+{grains[id]}", + "single-list-with-dicts:{grains[id]}+database+{grains[id]}", + ], + "deeply-nested-list:{grains[deep][foo][bar][baz]}": [ + "deeply-nested-list:hello", + "deeply-nested-list:world", + ], + } # The mappings dict is assembled in _get_policies, so emulate here - mappings = {'minion': self.grains['id'], 'grains': self.grains} + mappings = {"minion": self.grains["id"], "grains": self.grains} for case, correct_output in six.iteritems(cases): - output = vault._expand_pattern_lists(case, **mappings) # pylint: disable=protected-access + output = vault._expand_pattern_lists( + case, **mappings + ) # pylint: disable=protected-access diff = set(output).symmetric_difference(set(correct_output)) if diff: - log.debug('Test %s failed', case) - log.debug('Expected:\n\t%s\nGot\n\t%s', output, correct_output) - log.debug('Difference:\n\t%s', diff) + log.debug("Test %s failed", case) + log.debug("Expected:\n\t%s\nGot\n\t%s", output, correct_output) + log.debug("Difference:\n\t%s", diff) self.assertEqual(output, correct_output) def test_get_policies_for_nonexisting_minions(self): - minion_id = 'salt_master' + minion_id = "salt_master" # For non-existing minions, or the master-minion, grains will be None cases = { - 'no-tokens-to-replace': ['no-tokens-to-replace'], - 'single-dict:{minion}': ['single-dict:{0}'.format(minion_id)], - 'single-list:{grains[roles]}': [] + "no-tokens-to-replace": ["no-tokens-to-replace"], + "single-dict:{minion}": ["single-dict:{0}".format(minion_id)], + "single-list:{grains[roles]}": [], } - with patch('salt.utils.minions.get_minion_data', - MagicMock(return_value=(None, None, None))): + with patch( + "salt.utils.minions.get_minion_data", + MagicMock(return_value=(None, None, None)), + ): for case, correct_output in six.iteritems(cases): - test_config = {'policies': [case]} - output = vault._get_policies(minion_id, test_config) # pylint: disable=protected-access + test_config = {"policies": [case]} + output = vault._get_policies( + minion_id, test_config + ) # pylint: disable=protected-access diff = set(output).symmetric_difference(set(correct_output)) if diff: - log.debug('Test %s failed', case) - log.debug('Expected:\n\t%s\nGot\n\t%s', output, correct_output) - log.debug('Difference:\n\t%s', diff) + log.debug("Test %s failed", case) + log.debug("Expected:\n\t%s\nGot\n\t%s", output, correct_output) + log.debug("Difference:\n\t%s", diff) self.assertEqual(output, correct_output) def test_get_policies(self): - ''' + """ Ensure _get_policies works as intended, including expansion of lists - ''' + """ cases = { - 'no-tokens-to-replace': ['no-tokens-to-replace'], - 'single-dict:{minion}': ['single-dict:test-minion'], - 'single-list:{grains[roles]}': ['single-list:web', 'single-list:database'], - 'multiple-lists:{grains[roles]}+{grains[aux]}': [ - 'multiple-lists:web+foo', - 'multiple-lists:web+bar', - 'multiple-lists:database+foo', - 'multiple-lists:database+bar', - ], - 'single-list-with-dicts:{grains[id]}+{grains[roles]}+{grains[id]}': [ - 'single-list-with-dicts:test-minion+web+test-minion', - 'single-list-with-dicts:test-minion+database+test-minion' - ], - 'deeply-nested-list:{grains[deep][foo][bar][baz]}': [ - 'deeply-nested-list:hello', - 'deeply-nested-list:world' - ], - 'should-not-cause-an-exception,but-result-empty:{foo}': [], - 'Case-Should-Be-Lowered:{grains[mixedcase]}': [ - 'case-should-be-lowered:up-low-up' - ] - } + "no-tokens-to-replace": ["no-tokens-to-replace"], + "single-dict:{minion}": ["single-dict:test-minion"], + "single-list:{grains[roles]}": ["single-list:web", "single-list:database"], + "multiple-lists:{grains[roles]}+{grains[aux]}": [ + "multiple-lists:web+foo", + "multiple-lists:web+bar", + "multiple-lists:database+foo", + "multiple-lists:database+bar", + ], + "single-list-with-dicts:{grains[id]}+{grains[roles]}+{grains[id]}": [ + "single-list-with-dicts:test-minion+web+test-minion", + "single-list-with-dicts:test-minion+database+test-minion", + ], + "deeply-nested-list:{grains[deep][foo][bar][baz]}": [ + "deeply-nested-list:hello", + "deeply-nested-list:world", + ], + "should-not-cause-an-exception,but-result-empty:{foo}": [], + "Case-Should-Be-Lowered:{grains[mixedcase]}": [ + "case-should-be-lowered:up-low-up" + ], + } - with patch('salt.utils.minions.get_minion_data', - MagicMock(return_value=(None, self.grains, None))): + with patch( + "salt.utils.minions.get_minion_data", + MagicMock(return_value=(None, self.grains, None)), + ): for case, correct_output in six.iteritems(cases): - test_config = {'policies': [case]} - output = vault._get_policies('test-minion', test_config) # pylint: disable=protected-access + test_config = {"policies": [case]} + output = vault._get_policies( + "test-minion", test_config + ) # pylint: disable=protected-access diff = set(output).symmetric_difference(set(correct_output)) if diff: - log.debug('Test %s failed', case) - log.debug('Expected:\n\t%s\nGot\n\t%s', output, correct_output) - log.debug('Difference:\n\t%s', diff) + log.debug("Test %s failed", case) + log.debug("Expected:\n\t%s\nGot\n\t%s", output, correct_output) + log.debug("Difference:\n\t%s", diff) self.assertEqual(output, correct_output) def test_get_token_create_url(self): - ''' + """ Ensure _get_token_create_url parses config correctly - ''' - self.assertEqual(vault._get_token_create_url( # pylint: disable=protected-access - {"url": "http://127.0.0.1"}), - "http://127.0.0.1/v1/auth/token/create") - self.assertEqual(vault._get_token_create_url( # pylint: disable=protected-access - {"url": "https://127.0.0.1/"}), - "https://127.0.0.1/v1/auth/token/create") - self.assertEqual(vault._get_token_create_url( # pylint: disable=protected-access - {"url": "http://127.0.0.1:8200", "role_name": "therole"}), - "http://127.0.0.1:8200/v1/auth/token/create/therole") - self.assertEqual(vault._get_token_create_url( # pylint: disable=protected-access - {"url": "https://127.0.0.1/test", "role_name": "therole"}), - "https://127.0.0.1/test/v1/auth/token/create/therole") + """ + self.assertEqual( + vault._get_token_create_url( # pylint: disable=protected-access + {"url": "http://127.0.0.1"} + ), + "http://127.0.0.1/v1/auth/token/create", + ) + self.assertEqual( + vault._get_token_create_url( # pylint: disable=protected-access + {"url": "https://127.0.0.1/"} + ), + "https://127.0.0.1/v1/auth/token/create", + ) + self.assertEqual( + vault._get_token_create_url( # pylint: disable=protected-access + {"url": "http://127.0.0.1:8200", "role_name": "therole"} + ), + "http://127.0.0.1:8200/v1/auth/token/create/therole", + ) + self.assertEqual( + vault._get_token_create_url( # pylint: disable=protected-access + {"url": "https://127.0.0.1/test", "role_name": "therole"} + ), + "https://127.0.0.1/test/v1/auth/token/create/therole", + ) def _mock_json_response(data, status_code=200, reason=""): - ''' + """ Mock helper for http response - ''' + """ response = MagicMock() response.json = MagicMock(return_value=data) response.status_code = status_code @@ -181,99 +190,105 @@ def _mock_json_response(data, status_code=200, reason=""): class VaultTokenAuthTest(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for the runner module of the Vault with token setup - ''' + """ def setup_loader_modules(self): return { vault: { - '__opts__': { - 'vault': { - 'url': "http://127.0.0.1", - "auth": { - 'token': 'test', - 'method': 'token' - } + "__opts__": { + "vault": { + "url": "http://127.0.0.1", + "auth": {"token": "test", "method": "token"}, } } } } - @patch('salt.runners.vault._validate_signature', MagicMock(return_value=None)) - @patch('salt.runners.vault._get_token_create_url', MagicMock(return_value="http://fake_url")) + @patch("salt.runners.vault._validate_signature", MagicMock(return_value=None)) + @patch( + "salt.runners.vault._get_token_create_url", + MagicMock(return_value="http://fake_url"), + ) def test_generate_token(self): - ''' + """ Basic tests for test_generate_token: all exits - ''' - mock = _mock_json_response({'auth': {'client_token': 'test'}}) - with patch('requests.post', mock): - result = vault.generate_token('test-minion', 'signature') - log.debug('generate_token result: %s', result) + """ + mock = _mock_json_response({"auth": {"client_token": "test"}}) + with patch("requests.post", mock): + result = vault.generate_token("test-minion", "signature") + log.debug("generate_token result: %s", result) self.assertTrue(isinstance(result, dict)) - self.assertFalse('error' in result) - self.assertTrue('token' in result) - self.assertEqual(result['token'], 'test') - mock.assert_called_with("http://fake_url", headers=ANY, json=ANY, verify=ANY) + self.assertFalse("error" in result) + self.assertTrue("token" in result) + self.assertEqual(result["token"], "test") + mock.assert_called_with( + "http://fake_url", headers=ANY, json=ANY, verify=ANY + ) mock = _mock_json_response({}, status_code=403, reason="no reason") - with patch('requests.post', mock): - result = vault.generate_token('test-minion', 'signature') + with patch("requests.post", mock): + result = vault.generate_token("test-minion", "signature") self.assertTrue(isinstance(result, dict)) - self.assertTrue('error' in result) - self.assertEqual(result['error'], "no reason") + self.assertTrue("error" in result) + self.assertEqual(result["error"], "no reason") - with patch('salt.runners.vault._get_policies', MagicMock(return_value=[])): - result = vault.generate_token('test-minion', 'signature') + with patch("salt.runners.vault._get_policies", MagicMock(return_value=[])): + result = vault.generate_token("test-minion", "signature") self.assertTrue(isinstance(result, dict)) - self.assertTrue('error' in result) - self.assertEqual(result['error'], 'No policies matched minion') + self.assertTrue("error" in result) + self.assertEqual(result["error"], "No policies matched minion") - with patch('requests.post', - MagicMock(side_effect=Exception('Test Exception Reason'))): - result = vault.generate_token('test-minion', 'signature') + with patch( + "requests.post", MagicMock(side_effect=Exception("Test Exception Reason")) + ): + result = vault.generate_token("test-minion", "signature") self.assertTrue(isinstance(result, dict)) - self.assertTrue('error' in result) - self.assertEqual(result['error'], 'Test Exception Reason') + self.assertTrue("error" in result) + self.assertEqual(result["error"], "Test Exception Reason") class VaultAppRoleAuthTest(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for the runner module of the Vault with approle setup - ''' + """ def setup_loader_modules(self): return { vault: { - '__opts__': { - 'vault': { - 'url': "http://127.0.0.1", + "__opts__": { + "vault": { + "url": "http://127.0.0.1", "auth": { - 'method': 'approle', - 'role_id': 'role', - 'secret_id': 'secret' - } + "method": "approle", + "role_id": "role", + "secret_id": "secret", + }, } } } } - @patch('salt.runners.vault._validate_signature', MagicMock(return_value=None)) - @patch('salt.runners.vault._get_token_create_url', MagicMock(return_value="http://fake_url")) + @patch("salt.runners.vault._validate_signature", MagicMock(return_value=None)) + @patch( + "salt.runners.vault._get_token_create_url", + MagicMock(return_value="http://fake_url"), + ) def test_generate_token(self): - ''' + """ Basic test for test_generate_token with approle (two vault calls) - ''' - mock = _mock_json_response({'auth': {'client_token': 'test'}}) - with patch('requests.post', mock): - result = vault.generate_token('test-minion', 'signature') - log.debug('generate_token result: %s', result) + """ + mock = _mock_json_response({"auth": {"client_token": "test"}}) + with patch("requests.post", mock): + result = vault.generate_token("test-minion", "signature") + log.debug("generate_token result: %s", result) self.assertTrue(isinstance(result, dict)) - self.assertFalse('error' in result) - self.assertTrue('token' in result) - self.assertEqual(result['token'], 'test') + self.assertFalse("error" in result) + self.assertTrue("token" in result) + self.assertEqual(result["token"], "test") calls = [ call("http://127.0.0.1/v1/auth/approle/login", json=ANY, verify=ANY), - call("http://fake_url", headers=ANY, json=ANY, verify=ANY) - ] + call("http://fake_url", headers=ANY, json=ANY, verify=ANY), + ] mock.assert_has_calls(calls) diff --git a/tests/unit/runners/test_winrepo.py b/tests/unit/runners/test_winrepo.py index f97b9df10a9..a112e22554a 100644 --- a/tests/unit/runners/test_winrepo.py +++ b/tests/unit/runners/test_winrepo.py @@ -2,24 +2,26 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import tempfile -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase +import salt.runners.winrepo as winrepo # Import salt libs import salt.utils.files import salt.utils.stringutils -import salt.runners.winrepo as winrepo +from tests.support.mixins import LoaderModuleMockMixin + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase # Can't use raw string with unicode_literals, since the \u in the uninstaller # will be interpreted as a unicode code point and the interpreter will raise a # SyntaxError. -_WINREPO_SLS = ''' +_WINREPO_SLS = """ winscp_x86: 5.7.5: full_name: 'WinSCP 5.7.5' @@ -39,72 +41,70 @@ winscp_x86: msiexec: False locale: en_US reboot: False -''' +""" _WINREPO_GENREPO_DATA = { - 'name_map': { - 'WinSCP 5.7.4': 'winscp_x86', - 'WinSCP 5.7.5': 'winscp_x86' - }, - 'repo': { - 'winscp_x86': { - '5.7.5': { - 'full_name': 'WinSCP 5.7.5', - 'installer': 'http://heanet.dl.sourceforge.net/project/winscp/WinSCP/5.7.5/winscp575setup.exe', - 'install_flags': '/SP- /verysilent /norestart', - 'uninstaller': '%PROGRAMFILES%\\WinSCP\\unins000.exe', - 'uninstall_flags': '/verysilent', - 'msiexec': False, - 'locale': 'en_US', - 'reboot': False + "name_map": {"WinSCP 5.7.4": "winscp_x86", "WinSCP 5.7.5": "winscp_x86"}, + "repo": { + "winscp_x86": { + "5.7.5": { + "full_name": "WinSCP 5.7.5", + "installer": "http://heanet.dl.sourceforge.net/project/winscp/WinSCP/5.7.5/winscp575setup.exe", + "install_flags": "/SP- /verysilent /norestart", + "uninstaller": "%PROGRAMFILES%\\WinSCP\\unins000.exe", + "uninstall_flags": "/verysilent", + "msiexec": False, + "locale": "en_US", + "reboot": False, + }, + "5.7.4": { + "full_name": "WinSCP 5.7.4", + "installer": "http://cznic.dl.sourceforge.net/project/winscp/WinSCP/5.7.4/winscp574setup.exe", + "install_flags": "/SP- /verysilent /norestart", + "uninstaller": "%PROGRAMFILES%\\WinSCP\\unins000.exe", + "uninstall_flags": "/verysilent", + "msiexec": False, + "locale": "en_US", + "reboot": False, }, - '5.7.4': { - 'full_name': 'WinSCP 5.7.4', - 'installer': 'http://cznic.dl.sourceforge.net/project/winscp/WinSCP/5.7.4/winscp574setup.exe', - 'install_flags': '/SP- /verysilent /norestart', - 'uninstaller': '%PROGRAMFILES%\\WinSCP\\unins000.exe', - 'uninstall_flags': '/verysilent', - 'msiexec': False, - 'locale': 'en_US', - 'reboot': False - } } - } + }, } class WinrepoTest(TestCase, LoaderModuleMockMixin): - ''' + """ Test the winrepo runner - ''' + """ + def setup_loader_modules(self): self.winrepo_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.winrepo_dir, ignore_errors=True) self.extmods_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.extmods_dir, ignore_errors=True) - self.winrepo_sls_dir = os.path.join(self.winrepo_dir, 'repo_sls') + self.winrepo_sls_dir = os.path.join(self.winrepo_dir, "repo_sls") os.mkdir(self.winrepo_sls_dir) return { winrepo: { - '__opts__': { - 'winrepo_cachefile': 'winrepo.p', - 'optimization_order': [0, 1, 2], - 'renderer': 'yaml', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'file_roots': {'base': [self.winrepo_dir]}, - 'winrepo_dir': self.winrepo_dir, - 'extension_modules': self.extmods_dir + "__opts__": { + "winrepo_cachefile": "winrepo.p", + "optimization_order": [0, 1, 2], + "renderer": "yaml", + "renderer_blacklist": [], + "renderer_whitelist": [], + "file_roots": {"base": [self.winrepo_dir]}, + "winrepo_dir": self.winrepo_dir, + "extension_modules": self.extmods_dir, } } } def test_genrepo(self): - ''' + """ Test winrepo.genrepo runner - ''' - sls_file = os.path.join(self.winrepo_sls_dir, 'wireshark.sls') + """ + sls_file = os.path.join(self.winrepo_sls_dir, "wireshark.sls") # Add a winrepo SLS file - with salt.utils.files.fopen(sls_file, 'w') as fp_: + with salt.utils.files.fopen(sls_file, "w") as fp_: fp_.write(salt.utils.stringutils.to_str(_WINREPO_SLS)) self.assertEqual(winrepo.genrepo(), _WINREPO_GENREPO_DATA) diff --git a/tests/unit/sdb/test_vault.py b/tests/unit/sdb/test_vault.py index 5f9efcf702b..ae6dd01f23e 100644 --- a/tests/unit/sdb/test_vault.py +++ b/tests/unit/sdb/test_vault.py @@ -1,93 +1,209 @@ # -*- coding: utf-8 -*- -''' +""" Test case for the vault SDB module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import ( - MagicMock, - call, - patch) - # Import Salt libs import salt.sdb.vault as vault +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase class TestVaultSDB(LoaderModuleMockMixin, TestCase): - ''' + """ Test case for the vault SDB module - ''' + """ + def setup_loader_modules(self): return { vault: { - '__opts__': { - 'vault': { - 'url': "http://127.0.0.1", - "auth": { - 'token': 'test', - 'method': 'token' - } + "__opts__": { + "vault": { + "url": "http://127.0.0.1", + "auth": {"token": "test", "method": "token"}, } } } } def test_set(self): - ''' + """ Test salt.sdb.vault.set function - ''' + """ + version = {"v2": False, "data": None, "metadata": None, "type": None} + mock_version = MagicMock(return_value=version) mock_vault = MagicMock() mock_vault.return_value.status_code = 200 - with patch.dict(vault.__utils__, {'vault.make_request': mock_vault}): - vault.set_('sdb://myvault/path/to/foo/bar', 'super awesome') + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + vault.set_("sdb://myvault/path/to/foo/bar", "super awesome") - assert mock_vault.call_args_list == [call('POST', - 'v1/sdb://myvault/path/to/foo', - None, json={'bar': 'super awesome'})] + self.assertEqual( + mock_vault.call_args_list, + [ + call( + "POST", + "v1/sdb://myvault/path/to/foo", + json={"bar": "super awesome"}, + ) + ], + ) + + def test_set_v2(self): + """ + Test salt.sdb.vault.set function with kv v2 backend + """ + version = { + "v2": True, + "data": "path/data/to/foo", + "metadata": "path/metadata/to/foo", + "type": "kv", + } + mock_version = MagicMock(return_value=version) + mock_vault = MagicMock() + mock_vault.return_value.status_code = 200 + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + vault.set_("sdb://myvault/path/to/foo/bar", "super awesome") + + self.assertEqual( + mock_vault.call_args_list, + [ + call( + "POST", + "v1/path/data/to/foo", + json={"data": {"bar": "super awesome"}}, + ) + ], + ) def test_set_question_mark(self): - ''' + """ Test salt.sdb.vault.set_ while using the old deprecated solution with a question mark. - ''' + """ + version = {"v2": False, "data": None, "metadata": None, "type": None} + mock_version = MagicMock(return_value=version) mock_vault = MagicMock() mock_vault.return_value.status_code = 200 - with patch.dict(vault.__utils__, {'vault.make_request': mock_vault}): - vault.set_('sdb://myvault/path/to/foo?bar', 'super awesome') + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + vault.set_("sdb://myvault/path/to/foo?bar", "super awesome") - assert mock_vault.call_args_list == [call('POST', - 'v1/sdb://myvault/path/to/foo', - None, json={'bar': 'super awesome'})] + self.assertEqual( + mock_vault.call_args_list, + [ + call( + "POST", + "v1/sdb://myvault/path/to/foo", + json={"bar": "super awesome"}, + ) + ], + ) def test_get(self): - ''' + """ Test salt.sdb.vault.get function - ''' + """ + version = {"v2": False, "data": None, "metadata": None, "type": None} + mock_version = MagicMock(return_value=version) mock_vault = MagicMock() mock_vault.return_value.status_code = 200 - mock_vault.content.return_value = [{'data': {'bar', 'test'}}] - with patch.dict(vault.__utils__, {'vault.make_request': mock_vault}): - vault.get('sdb://myvault/path/to/foo/bar') + mock_vault.return_value.json.return_value = {"data": {"bar": "test"}} + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + self.assertEqual(vault.get("sdb://myvault/path/to/foo/bar"), "test") - assert mock_vault.call_args_list == [call('GET', - 'v1/sdb://myvault/path/to/foo', - None)] + self.assertEqual( + mock_vault.call_args_list, + [call("GET", "v1/sdb://myvault/path/to/foo", None)], + ) + + def test_get_v2(self): + """ + Test salt.sdb.vault.get function with kv v2 backend + """ + version = { + "v2": True, + "data": "path/data/to/foo", + "metadata": "path/metadata/to/foo", + "type": "kv", + } + mock_version = MagicMock(return_value=version) + mock_vault = MagicMock() + mock_vault.return_value.status_code = 200 + mock_vault.return_value.json.return_value = {"data": {"data": {"bar": "test"}}} + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + self.assertEqual(vault.get("sdb://myvault/path/to/foo/bar"), "test") + + self.assertEqual( + mock_vault.call_args_list, [call("GET", "v1/path/data/to/foo", None)] + ) def test_get_question_mark(self): - ''' + """ Test salt.sdb.vault.get while using the old deprecated solution with a question mark. - ''' + """ + version = {"v2": False, "data": None, "metadata": None, "type": None} + mock_version = MagicMock(return_value=version) mock_vault = MagicMock() mock_vault.return_value.status_code = 200 - mock_vault.content.return_value = [{'data': {'bar', 'test'}}] - with patch.dict(vault.__utils__, {'vault.make_request': mock_vault}): - vault.get('sdb://myvault/path/to/foo?bar') - assert mock_vault.call_args_list == [call('GET', - 'v1/sdb://myvault/path/to/foo', - None)] + mock_vault.return_value.json.return_value = {"data": {"bar": "test"}} + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + self.assertEqual(vault.get("sdb://myvault/path/to/foo?bar"), "test") + self.assertEqual( + mock_vault.call_args_list, + [call("GET", "v1/sdb://myvault/path/to/foo", None)], + ) + + def test_get_missing(self): + """ + Test salt.sdb.vault.get function returns None + if vault does not have an entry + """ + version = {"v2": False, "data": None, "metadata": None, "type": None} + mock_version = MagicMock(return_value=version) + mock_vault = MagicMock() + mock_vault.return_value.status_code = 404 + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + self.assertIsNone(vault.get("sdb://myvault/path/to/foo/bar")) + + assert mock_vault.call_args_list == [ + call("GET", "v1/sdb://myvault/path/to/foo", None) + ] + + def test_get_missing_key(self): + """ + Test salt.sdb.vault.get function returns None + if vault does not have the key but does have the entry + """ + version = {"v2": False, "data": None, "metadata": None, "type": None} + mock_version = MagicMock(return_value=version) + mock_vault = MagicMock() + mock_vault.return_value.status_code = 200 + mock_vault.return_value.json.return_value = {"data": {"bar": "test"}} + with patch.dict( + vault.__utils__, {"vault.make_request": mock_vault} + ), patch.dict(vault.__utils__, {"vault.is_v2": mock_version}): + self.assertIsNone(vault.get("sdb://myvault/path/to/foo/foo")) + + assert mock_vault.call_args_list == [ + call("GET", "v1/sdb://myvault/path/to/foo", None) + ] diff --git a/tests/unit/sdb/test_yaml.py b/tests/unit/sdb/test_yaml.py index d2144641555..85eb1ed103a 100644 --- a/tests/unit/sdb/test_yaml.py +++ b/tests/unit/sdb/test_yaml.py @@ -1,47 +1,45 @@ # -*- coding: utf-8 -*- -''' +""" Test case for the YAML SDB module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt libs import salt.sdb.yaml as sdb +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase class TestYamlRenderer(TestCase): - ''' + """ Test case for the YAML SDB module - ''' + """ def test_plaintext(self): - ''' + """ Retrieve a value from the top level of the dictionary - ''' - plain = {'foo': 'bar'} - with patch('salt.sdb.yaml._get_values', MagicMock(return_value=plain)): - self.assertEqual(sdb.get('foo'), 'bar') + """ + plain = {"foo": "bar"} + with patch("salt.sdb.yaml._get_values", MagicMock(return_value=plain)): + self.assertEqual(sdb.get("foo"), "bar") def test_nested(self): - ''' + """ Retrieve a value from a nested level of the dictionary - ''' - plain = {'foo': {'bar': 'baz'}} - with patch('salt.sdb.yaml._get_values', MagicMock(return_value=plain)): - self.assertEqual(sdb.get('foo:bar'), 'baz') + """ + plain = {"foo": {"bar": "baz"}} + with patch("salt.sdb.yaml._get_values", MagicMock(return_value=plain)): + self.assertEqual(sdb.get("foo:bar"), "baz") def test_encrypted(self): - ''' + """ Assume the content is plaintext if GPG is not configured - ''' - plain = {'foo': 'bar'} - with patch('salt.sdb.yaml._decrypt', MagicMock(return_value=plain)): - with patch('salt.sdb.yaml._get_values', MagicMock(return_value=None)): - self.assertEqual(sdb.get('foo', profile={'gpg': True}), 'bar') + """ + plain = {"foo": "bar"} + with patch("salt.sdb.yaml._decrypt", MagicMock(return_value=plain)): + with patch("salt.sdb.yaml._get_values", MagicMock(return_value=None)): + self.assertEqual(sdb.get("foo", profile={"gpg": True}), "bar") diff --git a/tests/unit/serializers/test_serializers.py b/tests/unit/serializers/test_serializers.py index 84bb4724bbe..41544c6ae0c 100644 --- a/tests/unit/serializers/test_serializers.py +++ b/tests/unit/serializers/test_serializers.py @@ -2,113 +2,105 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from textwrap import dedent -# Import Salt Testing libs -from tests.support.unit import skipIf, TestCase +from textwrap import dedent # Import 3rd party libs import jinja2 -import yaml as _yaml # future lint: disable=blacklisted-import -from salt.ext import six # Import salt libs import salt.serializers.configparser as configparser import salt.serializers.json as json -import salt.serializers.yaml as yaml -import salt.serializers.yamlex as yamlex import salt.serializers.msgpack as msgpack import salt.serializers.python as python import salt.serializers.toml as toml -from salt.serializers.yaml import EncryptedString +import salt.serializers.yaml as yaml +import salt.serializers.yamlex as yamlex +import yaml as _yaml # future lint: disable=blacklisted-import +from salt.ext import six from salt.serializers import SerializationError +from salt.serializers.yaml import EncryptedString from salt.utils.odict import OrderedDict # Import test support libs from tests.support.helpers import flaky -SKIP_MESSAGE = '%s is unavailable, have prerequisites been met?' +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf + +SKIP_MESSAGE = "%s is unavailable, have prerequisites been met?" @flaky(condition=six.PY3) class TestSerializers(TestCase): - @skipIf(not json.available, SKIP_MESSAGE % 'json') + @skipIf(not json.available, SKIP_MESSAGE % "json") def test_serialize_json(self): - data = { - "foo": "bar" - } + data = {"foo": "bar"} serialized = json.serialize(data) assert serialized == '{"foo": "bar"}', serialized deserialized = json.deserialize(serialized) assert deserialized == data, deserialized - @skipIf(not yaml.available, SKIP_MESSAGE % 'yaml') + @skipIf(not yaml.available, SKIP_MESSAGE % "yaml") def test_serialize_yaml(self): - data = { - "foo": "bar", - "encrypted_data": EncryptedString("foo") - } + data = {"foo": "bar", "encrypted_data": EncryptedString("foo")} # The C dumper produces unquoted strings when serializing an # EncryptedString, while the non-C dumper produces quoted strings. - expected = '{encrypted_data: !encrypted foo, foo: bar}' \ - if hasattr(_yaml, 'CSafeDumper') \ + expected = ( + "{encrypted_data: !encrypted foo, foo: bar}" + if hasattr(_yaml, "CSafeDumper") else "{encrypted_data: !encrypted 'foo', foo: bar}" + ) serialized = yaml.serialize(data) assert serialized == expected, serialized deserialized = yaml.deserialize(serialized) assert deserialized == data, deserialized - @skipIf(not yaml.available, SKIP_MESSAGE % 'sls') + @skipIf(not yaml.available, SKIP_MESSAGE % "sls") def test_serialize_sls(self): - data = { - "foo": "bar" - } + data = {"foo": "bar"} serialized = yamlex.serialize(data) - assert serialized == '{foo: bar}', serialized + assert serialized == "{foo: bar}", serialized serialized = yamlex.serialize(data, default_flow_style=False) - assert serialized == 'foo: bar', serialized + assert serialized == "foo: bar", serialized deserialized = yamlex.deserialize(serialized) assert deserialized == data, deserialized serialized = yaml.serialize(data) - assert serialized == '{foo: bar}', serialized + assert serialized == "{foo: bar}", serialized deserialized = yaml.deserialize(serialized) assert deserialized == data, deserialized serialized = yaml.serialize(data, default_flow_style=False) - assert serialized == 'foo: bar', serialized + assert serialized == "foo: bar", serialized deserialized = yaml.deserialize(serialized) assert deserialized == data, deserialized - @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls') + @skipIf(not yamlex.available, SKIP_MESSAGE % "sls") def test_serialize_complex_sls(self): - data = OrderedDict([ - ("foo", 1), - ("bar", 2), - ("baz", True), - ]) + data = OrderedDict([("foo", 1), ("bar", 2), ("baz", True)]) serialized = yamlex.serialize(data) - assert serialized == '{foo: 1, bar: 2, baz: true}', serialized + assert serialized == "{foo: 1, bar: 2, baz: true}", serialized deserialized = yamlex.deserialize(serialized) assert deserialized == data, deserialized serialized = yaml.serialize(data) - assert serialized == '{bar: 2, baz: true, foo: 1}', serialized + assert serialized == "{bar: 2, baz: true, foo: 1}", serialized deserialized = yaml.deserialize(serialized) assert deserialized == data, deserialized - @skipIf(not yaml.available, SKIP_MESSAGE % 'yaml') - @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls') + @skipIf(not yaml.available, SKIP_MESSAGE % "yaml") + @skipIf(not yamlex.available, SKIP_MESSAGE % "sls") def test_compare_sls_vs_yaml(self): - src = '{foo: 1, bar: 2, baz: {qux: true}}' + src = "{foo: 1, bar: 2, baz: {qux: true}}" sls_data = yamlex.deserialize(src) yml_data = yaml.deserialize(src) @@ -121,13 +113,13 @@ class TestSerializers(TestCase): assert isinstance(sls_data, OrderedDict) assert not isinstance(yml_data, OrderedDict) - @skipIf(not yaml.available, SKIP_MESSAGE % 'yaml') - @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls') - @skipIf(six.PY3, 'Flaky on Python 3.') + @skipIf(not yaml.available, SKIP_MESSAGE % "yaml") + @skipIf(not yamlex.available, SKIP_MESSAGE % "sls") + @skipIf(six.PY3, "Flaky on Python 3.") def test_compare_sls_vs_yaml_with_jinja(self): - tpl = '{{ data }}' + tpl = "{{ data }}" env = jinja2.Environment() - src = '{foo: 1, bar: 2, baz: {qux: true}}' + src = "{foo: 1, bar: 2, baz: {qux: true}}" sls_src = env.from_string(tpl).render(data=yamlex.deserialize(src)) yml_src = env.from_string(tpl).render(data=yaml.deserialize(src)) @@ -143,7 +135,7 @@ class TestSerializers(TestCase): # running the loader test, the below passes. Even reloading the module # from disk does not reset its internal state (per the Python docs). ## - #assert sls_data == yml_data + # assert sls_data == yml_data # ensure that sls is ordered, while yaml not assert isinstance(sls_data, OrderedDict) @@ -151,11 +143,7 @@ class TestSerializers(TestCase): # prove that yaml does not handle well with OrderedDict # while sls is jinja friendly. - obj = OrderedDict([ - ('foo', 1), - ('bar', 2), - ('baz', {'qux': True}) - ]) + obj = OrderedDict([("foo", 1), ("bar", 2), ("baz", {"qux": True})]) sls_obj = yamlex.deserialize(yamlex.serialize(obj)) try: @@ -173,11 +161,12 @@ class TestSerializers(TestCase): # BLAAM! yml_src is not valid ! final_obj = OrderedDict(yaml.deserialize(yml_src)) - assert obj != final_obj, 'Objects matched! {} == {}'.format(obj, final_obj) + assert obj != final_obj, "Objects matched! {} == {}".format(obj, final_obj) - @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls') + @skipIf(not yamlex.available, SKIP_MESSAGE % "sls") def test_sls_aggregate(self): - src = dedent(""" + src = dedent( + """ a: lol foo: !aggregate hello bar: !aggregate [1, 2, 3] @@ -185,193 +174,184 @@ class TestSerializers(TestCase): a: 42 b: 666 c: the beast - """).strip() + """ + ).strip() # test that !aggregate is correctly parsed sls_obj = yamlex.deserialize(src) assert sls_obj == { - 'a': 'lol', - 'foo': ['hello'], - 'bar': [1, 2, 3], - 'baz': { - 'a': 42, - 'b': 666, - 'c': 'the beast' - } + "a": "lol", + "foo": ["hello"], + "bar": [1, 2, 3], + "baz": {"a": 42, "b": 666, "c": "the beast"}, }, sls_obj - assert dedent(""" + assert ( + dedent( + """ a: lol foo: [hello] bar: [1, 2, 3] baz: {a: 42, b: 666, c: the beast} - """).strip() == yamlex.serialize(sls_obj), sls_obj + """ + ).strip() + == yamlex.serialize(sls_obj) + ), sls_obj # test that !aggregate aggregates scalars - src = dedent(""" + src = dedent( + """ placeholder: !aggregate foo placeholder: !aggregate bar placeholder: !aggregate baz - """).strip() + """ + ).strip() sls_obj = yamlex.deserialize(src) - assert sls_obj == {'placeholder': ['foo', 'bar', 'baz']}, sls_obj + assert sls_obj == {"placeholder": ["foo", "bar", "baz"]}, sls_obj # test that !aggregate aggregates lists - src = dedent(""" + src = dedent( + """ placeholder: !aggregate foo placeholder: !aggregate [bar, baz] placeholder: !aggregate [] placeholder: !aggregate ~ - """).strip() + """ + ).strip() sls_obj = yamlex.deserialize(src) - assert sls_obj == {'placeholder': ['foo', 'bar', 'baz']}, sls_obj + assert sls_obj == {"placeholder": ["foo", "bar", "baz"]}, sls_obj # test that !aggregate aggregates dicts - src = dedent(""" + src = dedent( + """ placeholder: !aggregate {foo: 42} placeholder: !aggregate {bar: null} placeholder: !aggregate {baz: inga} - """).strip() + """ + ).strip() sls_obj = yamlex.deserialize(src) assert sls_obj == { - 'placeholder': { - 'foo': 42, - 'bar': None, - 'baz': 'inga' - } + "placeholder": {"foo": 42, "bar": None, "baz": "inga"} }, sls_obj # test that !aggregate aggregates deep dicts - src = dedent(""" + src = dedent( + """ placeholder: {foo: !aggregate {foo: 42}} placeholder: {foo: !aggregate {bar: null}} placeholder: {foo: !aggregate {baz: inga}} - """).strip() + """ + ).strip() sls_obj = yamlex.deserialize(src) assert sls_obj == { - 'placeholder': { - 'foo': { - 'foo': 42, - 'bar': None, - 'baz': 'inga' - } - } + "placeholder": {"foo": {"foo": 42, "bar": None, "baz": "inga"}} }, sls_obj # test that {foo: !aggregate bar} and {!aggregate foo: bar} # are roughly equivalent. - src = dedent(""" + src = dedent( + """ placeholder: {!aggregate foo: {foo: 42}} placeholder: {!aggregate foo: {bar: null}} placeholder: {!aggregate foo: {baz: inga}} - """).strip() + """ + ).strip() sls_obj = yamlex.deserialize(src) assert sls_obj == { - 'placeholder': { - 'foo': { - 'foo': 42, - 'bar': None, - 'baz': 'inga' - } - } + "placeholder": {"foo": {"foo": 42, "bar": None, "baz": "inga"}} }, sls_obj - @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls') + @skipIf(not yamlex.available, SKIP_MESSAGE % "sls") def test_sls_reset(self): - src = dedent(""" + src = dedent( + """ placeholder: {!aggregate foo: {foo: 42}} placeholder: {!aggregate foo: {bar: null}} !reset placeholder: {!aggregate foo: {baz: inga}} - """).strip() + """ + ).strip() sls_obj = yamlex.deserialize(src) - assert sls_obj == { - 'placeholder': { - 'foo': { - 'baz': 'inga' - } - } - }, sls_obj + assert sls_obj == {"placeholder": {"foo": {"baz": "inga"}}}, sls_obj - @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls') + @skipIf(not yamlex.available, SKIP_MESSAGE % "sls") def test_sls_repr(self): """ Ensure that obj __repr__ and __str__ methods are yaml friendly. """ + def convert(obj): return yamlex.deserialize(yamlex.serialize(obj)) - sls_obj = convert(OrderedDict([('foo', 'bar'), ('baz', 'qux')])) + + sls_obj = convert(OrderedDict([("foo", "bar"), ("baz", "qux")])) # ensure that repr and str are yaml friendly - assert sls_obj.__str__() == '{foo: bar, baz: qux}' - assert sls_obj.__repr__() == '{foo: bar, baz: qux}' + assert sls_obj.__str__() == "{foo: bar, baz: qux}" + assert sls_obj.__repr__() == "{foo: bar, baz: qux}" # ensure that repr and str are already quoted - assert sls_obj['foo'].__str__() == '"bar"' - assert sls_obj['foo'].__repr__() == '"bar"' + assert sls_obj["foo"].__str__() == '"bar"' + assert sls_obj["foo"].__repr__() == '"bar"' - @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls') + @skipIf(not yamlex.available, SKIP_MESSAGE % "sls") def test_sls_micking_file_merging(self): def convert(obj): return yamlex.deserialize(yamlex.serialize(obj)) # let say that we have 2 pillar files - src1 = dedent(""" + src1 = dedent( + """ a: first b: !aggregate first c: subkey1: first subkey2: !aggregate first - """).strip() + """ + ).strip() - src2 = dedent(""" + src2 = dedent( + """ a: second b: !aggregate second c: subkey2: !aggregate second subkey3: second - """).strip() + """ + ).strip() sls_obj1 = yamlex.deserialize(src1) sls_obj2 = yamlex.deserialize(src2) sls_obj3 = yamlex.merge_recursive(sls_obj1, sls_obj2) assert sls_obj3 == { - 'a': 'second', - 'b': ['first', 'second'], - 'c': { - 'subkey2': ['first', 'second'], - 'subkey3': 'second' - } + "a": "second", + "b": ["first", "second"], + "c": {"subkey2": ["first", "second"], "subkey3": "second"}, }, sls_obj3 - @skipIf(not msgpack.available, SKIP_MESSAGE % 'msgpack') + @skipIf(not msgpack.available, SKIP_MESSAGE % "msgpack") def test_msgpack(self): - data = OrderedDict([ - ("foo", 1), - ("bar", 2), - ("baz", True), - ]) + data = OrderedDict([("foo", 1), ("bar", 2), ("baz", True)]) serialized = msgpack.serialize(data) deserialized = msgpack.deserialize(serialized) assert deserialized == data, deserialized - @skipIf(not python.available, SKIP_MESSAGE % 'python') + @skipIf(not python.available, SKIP_MESSAGE % "python") def test_serialize_python(self): - data = {'foo': 'bar'} + data = {"foo": "bar"} serialized = python.serialize(data) - expected = repr({'foo': 'bar'}) + expected = repr({"foo": "bar"}) assert serialized == expected, serialized - @skipIf(not configparser.available, SKIP_MESSAGE % 'configparser') + @skipIf(not configparser.available, SKIP_MESSAGE % "configparser") def test_configparser(self): - data = {'foo': {'bar': 'baz'}} + data = {"foo": {"bar": "baz"}} # configparser appends empty lines serialized = configparser.serialize(data).strip() assert serialized == "[foo]\nbar = baz", serialized @@ -379,11 +359,9 @@ class TestSerializers(TestCase): deserialized = configparser.deserialize(serialized) assert deserialized == data, deserialized - @skipIf(not toml.available, SKIP_MESSAGE % 'toml') + @skipIf(not toml.available, SKIP_MESSAGE % "toml") def test_serialize_toml(self): - data = { - "foo": "bar" - } + data = {"foo": "bar"} serialized = toml.serialize(data) assert serialized == 'foo = "bar"\n', serialized diff --git a/tests/unit/states/test_acme.py b/tests/unit/states/test_acme.py index 86644de7323..f57f288cd8e 100644 --- a/tests/unit/states/test_acme.py +++ b/tests/unit/states/test_acme.py @@ -1,127 +1,179 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Herbert Buurman <herbert.buurman@ogd.nl> -''' +""" # Import future libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Module import salt.states.acme as acme +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class AcmeTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.acme - ''' + """ def setup_loader_modules(self): - return {acme: {'__opts__': {'test': False}}} + return {acme: {"__opts__": {"test": False}}} def test_cert_no_changes_t(self): - ''' + """ Test cert state with no needed changes. (test=True) - ''' + """ # With test=True - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'acme.has': MagicMock(return_value=True), - 'acme.needs_renewal': MagicMock(return_value=False), - }), \ - patch.dict(acme.__opts__, {'test': True}): # pylint: disable=no-member - self.assertEqual(acme.cert('test'), { - 'name': 'test', - 'result': True, - 'comment': ['Certificate test exists and does not need renewal.'], - 'changes': {}, - }) + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "acme.has": MagicMock(return_value=True), + "acme.needs_renewal": MagicMock(return_value=False), + }, + ), patch.dict( + acme.__opts__, {"test": True} + ): # pylint: disable=no-member + self.assertEqual( + acme.cert("test"), + { + "name": "test", + "result": True, + "comment": ["Certificate test exists and does not need renewal."], + "changes": {}, + }, + ) def test_cert_no_changes(self): - ''' + """ Test cert state with no needed changes. (test=False) - ''' + """ # With test=False - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'acme.has': MagicMock(return_value=True), - 'acme.needs_renewal': MagicMock(return_value=False), - }): - self.assertEqual(acme.cert('test'), { - 'name': 'test', - 'result': True, - 'comment': ['Certificate test exists and does not need renewal.'], - 'changes': {}, - }) + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "acme.has": MagicMock(return_value=True), + "acme.needs_renewal": MagicMock(return_value=False), + }, + ): + self.assertEqual( + acme.cert("test"), + { + "name": "test", + "result": True, + "comment": ["Certificate test exists and does not need renewal."], + "changes": {}, + }, + ) def test_cert_fresh_certificate_t(self): - ''' + """ Test cert state fetching a new certificate. (test=True) - ''' + """ # With test=True - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'acme.has': MagicMock(return_value=False), - # 'acme.cert': MagicMock(return_value={'result': True, 'comment': 'Mockery'}), - 'acme.info': MagicMock(return_value={'foo': 'bar'}), - }), \ - patch.dict(acme.__opts__, {'test': True}): # pylint: disable=no-member - self.assertEqual(acme.cert('test'), { - 'name': 'test', - 'result': None, - 'comment': ['Certificate test would have been obtained.'], - 'changes': {'old': 'current certificate', 'new': 'new certificate'}, - }) + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "acme.has": MagicMock(return_value=False), + # 'acme.cert': MagicMock(return_value={'result': True, 'comment': 'Mockery'}), + "acme.info": MagicMock(return_value={"foo": "bar"}), + }, + ), patch.dict( + acme.__opts__, {"test": True} + ): # pylint: disable=no-member + self.assertEqual( + acme.cert("test"), + { + "name": "test", + "result": None, + "comment": ["Certificate test would have been obtained."], + "changes": {"old": "current certificate", "new": "new certificate"}, + }, + ) def test_cert_fresh_certificate(self): - ''' + """ Test cert state fetching a new certificate. (test=False) - ''' + """ # With test=False - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'acme.has': MagicMock(return_value=False), - 'acme.cert': MagicMock(return_value={'result': True, 'comment': 'Mockery'}), - 'acme.info': MagicMock(return_value={'foo': 'bar'}), - }): - self.assertEqual(acme.cert('test'), { - 'name': 'test', - 'result': True, - 'comment': ['Mockery'], - 'changes': {'new': {'foo': 'bar'}}, - }) + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "acme.has": MagicMock(return_value=False), + "acme.cert": MagicMock( + return_value={"result": True, "comment": "Mockery"} + ), + "acme.info": MagicMock(return_value={"foo": "bar"}), + }, + ): + self.assertEqual( + acme.cert("test"), + { + "name": "test", + "result": True, + "comment": ["Mockery"], + "changes": {"new": {"foo": "bar"}}, + }, + ) def test_cert_renew_certificate_t(self): - ''' + """ Test cert state renewing a certificate. (test=True) - ''' - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'acme.has': MagicMock(return_value=True), - 'acme.needs_renewal': MagicMock(return_value=True), - 'acme.info': MagicMock(side_effect=[{'name': 'old cert'}, {'name': 'new cert'}]), - 'acme.cert': MagicMock(return_value={'result': True, 'comment': 'Mockery'}), - }), \ - patch.dict(acme.__opts__, {'test': True}): # pylint: disable=no-member - self.assertEqual(acme.cert('test'), { - 'name': 'test', - 'result': None, - 'comment': ['Certificate test would have been renewed.'], - 'changes': {'old': 'current certificate', 'new': 'new certificate'}, - }) + """ + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "acme.has": MagicMock(return_value=True), + "acme.needs_renewal": MagicMock(return_value=True), + "acme.info": MagicMock( + side_effect=[{"name": "old cert"}, {"name": "new cert"}] + ), + "acme.cert": MagicMock( + return_value={"result": True, "comment": "Mockery"} + ), + }, + ), patch.dict( + acme.__opts__, {"test": True} + ): # pylint: disable=no-member + self.assertEqual( + acme.cert("test"), + { + "name": "test", + "result": None, + "comment": ["Certificate test would have been renewed."], + "changes": {"old": "current certificate", "new": "new certificate"}, + }, + ) def test_cert_renew_certificate(self): - ''' + """ Test cert state renewing a certificate. (test=False) - ''' - with patch.dict(acme.__salt__, { # pylint: disable=no-member - 'acme.has': MagicMock(return_value=True), - 'acme.needs_renewal': MagicMock(return_value=True), - 'acme.info': MagicMock(side_effect=[{'name': 'old cert'}, {'name': 'new cert'}]), - 'acme.cert': MagicMock(return_value={'result': True, 'comment': 'Mockery'}), - }): - self.assertEqual(acme.cert('test'), { - 'name': 'test', - 'result': True, - 'comment': ['Mockery'], - 'changes': {'old': {'name': 'old cert'}, 'new': {'name': 'new cert'}}, - }) + """ + with patch.dict( + acme.__salt__, + { # pylint: disable=no-member + "acme.has": MagicMock(return_value=True), + "acme.needs_renewal": MagicMock(return_value=True), + "acme.info": MagicMock( + side_effect=[{"name": "old cert"}, {"name": "new cert"}] + ), + "acme.cert": MagicMock( + return_value={"result": True, "comment": "Mockery"} + ), + }, + ): + self.assertEqual( + acme.cert("test"), + { + "name": "test", + "result": True, + "comment": ["Mockery"], + "changes": { + "old": {"name": "old cert"}, + "new": {"name": "new cert"}, + }, + }, + ) diff --git a/tests/unit/states/test_alias.py b/tests/unit/states/test_alias.py index 059ef3bc57c..cec51d0e013 100644 --- a/tests/unit/states/test_alias.py +++ b/tests/unit/states/test_alias.py @@ -1,160 +1,174 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the alias state -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.states.alias as alias +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class AliasTest(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the alias state - ''' + """ + def setup_loader_modules(self): return {alias: {}} def test_present_has_target(self): - ''' + """ test alias.present has target already - ''' - name = 'saltdude' - target = 'dude@saltstack.com' - ret = {'comment': 'Alias {0} already present'.format(name), - 'changes': {}, - 'name': name, - 'result': True} + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": "Alias {0} already present".format(name), + "changes": {}, + "name": name, + "result": True, + } has_target = MagicMock(return_value=True) - with patch.dict(alias.__salt__, {'aliases.has_target': has_target}): + with patch.dict(alias.__salt__, {"aliases.has_target": has_target}): self.assertEqual(alias.present(name, target), ret) def test_present_has_not_target_test(self): - ''' + """ test alias.present has't got target yet test mode - ''' - name = 'saltdude' - target = 'dude@saltstack.com' - ret = {'comment': 'Alias {0} -> {1} is set to be added'.format(name, target), - 'changes': {}, - 'name': name, - 'result': None} + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": "Alias {0} -> {1} is set to be added".format(name, target), + "changes": {}, + "name": name, + "result": None, + } has_target = MagicMock(return_value=False) - with patch.dict(alias.__salt__, {'aliases.has_target': has_target}): - with patch.dict(alias.__opts__, {'test': True}): + with patch.dict(alias.__salt__, {"aliases.has_target": has_target}): + with patch.dict(alias.__opts__, {"test": True}): self.assertEqual(alias.present(name, target), ret) def test_present_set_target(self): - ''' + """ test alias.present set target - ''' - name = 'saltdude' - target = 'dude@saltstack.com' - ret = {'comment': 'Set email alias {0} -> {1}'.format(name, target), - 'changes': {'alias': name}, - 'name': name, - 'result': True} + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": "Set email alias {0} -> {1}".format(name, target), + "changes": {"alias": name}, + "name": name, + "result": True, + } has_target = MagicMock(return_value=False) set_target = MagicMock(return_value=True) - with patch.dict(alias.__salt__, {'aliases.has_target': has_target}): - with patch.dict(alias.__opts__, {'test': False}): - with patch.dict(alias.__salt__, {'aliases.set_target': set_target}): + with patch.dict(alias.__salt__, {"aliases.has_target": has_target}): + with patch.dict(alias.__opts__, {"test": False}): + with patch.dict(alias.__salt__, {"aliases.set_target": set_target}): self.assertEqual(alias.present(name, target), ret) def test_present_set_target_failed(self): - ''' + """ test alias.present set target failure - ''' - name = 'saltdude' - target = 'dude@saltstack.com' - ret = {'comment': 'Failed to set alias {0} -> {1}'.format(name, target), - 'changes': {}, - 'name': name, - 'result': False} + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": "Failed to set alias {0} -> {1}".format(name, target), + "changes": {}, + "name": name, + "result": False, + } has_target = MagicMock(return_value=False) set_target = MagicMock(return_value=False) - with patch.dict(alias.__salt__, {'aliases.has_target': has_target}): - with patch.dict(alias.__opts__, {'test': False}): - with patch.dict(alias.__salt__, {'aliases.set_target': set_target}): + with patch.dict(alias.__salt__, {"aliases.has_target": has_target}): + with patch.dict(alias.__opts__, {"test": False}): + with patch.dict(alias.__salt__, {"aliases.set_target": set_target}): self.assertEqual(alias.present(name, target), ret) def test_absent_already_gone(self): - ''' + """ test alias.absent already gone - ''' - name = 'saltdude' - target = 'dude@saltstack.com' - ret = {'comment': 'Alias {0} already absent'.format(name), - 'changes': {}, - 'name': name, - 'result': True} + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": "Alias {0} already absent".format(name), + "changes": {}, + "name": name, + "result": True, + } get_target = MagicMock(return_value=False) - with patch.dict(alias.__salt__, {'aliases.get_target': get_target}): + with patch.dict(alias.__salt__, {"aliases.get_target": get_target}): self.assertEqual(alias.absent(name), ret) def test_absent_not_gone_test(self): - ''' + """ test alias.absent already gone test mode - ''' - name = 'saltdude' - target = 'dude@saltstack.com' - ret = {'comment': 'Alias {0} is set to be removed'.format(name), - 'changes': {}, - 'name': name, - 'result': None} + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": "Alias {0} is set to be removed".format(name), + "changes": {}, + "name": name, + "result": None, + } get_target = MagicMock(return_value=True) - with patch.dict(alias.__salt__, {'aliases.get_target': get_target}): - with patch.dict(alias.__opts__, {'test': True}): + with patch.dict(alias.__salt__, {"aliases.get_target": get_target}): + with patch.dict(alias.__opts__, {"test": True}): self.assertEqual(alias.absent(name), ret) def test_absent_rm_alias(self): - ''' + """ test alias.absent remove alias - ''' - name = 'saltdude' - target = 'dude@saltstack.com' - ret = {'comment': 'Removed alias {0}'.format(name), - 'changes': {'alias': name}, - 'name': name, - 'result': True} + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": "Removed alias {0}".format(name), + "changes": {"alias": name}, + "name": name, + "result": True, + } get_target = MagicMock(return_value=True) rm_alias = MagicMock(return_value=True) - with patch.dict(alias.__salt__, {'aliases.get_target': get_target}): - with patch.dict(alias.__opts__, {'test': False}): - with patch.dict(alias.__salt__, {'aliases.rm_alias': rm_alias}): + with patch.dict(alias.__salt__, {"aliases.get_target": get_target}): + with patch.dict(alias.__opts__, {"test": False}): + with patch.dict(alias.__salt__, {"aliases.rm_alias": rm_alias}): self.assertEqual(alias.absent(name), ret) def test_absent_rm_alias_failed(self): - ''' + """ test alias.absent remove alias failure - ''' - name = 'saltdude' - target = 'dude@saltstack.com' - ret = {'comment': 'Failed to remove alias {0}'.format(name), - 'changes': {}, - 'name': name, - 'result': False} + """ + name = "saltdude" + target = "dude@saltstack.com" + ret = { + "comment": "Failed to remove alias {0}".format(name), + "changes": {}, + "name": name, + "result": False, + } get_target = MagicMock(return_value=True) rm_alias = MagicMock(return_value=False) - with patch.dict(alias.__salt__, {'aliases.get_target': get_target}): - with patch.dict(alias.__opts__, {'test': False}): - with patch.dict(alias.__salt__, {'aliases.rm_alias': rm_alias}): + with patch.dict(alias.__salt__, {"aliases.get_target": get_target}): + with patch.dict(alias.__opts__, {"test": False}): + with patch.dict(alias.__salt__, {"aliases.rm_alias": rm_alias}): self.assertEqual(alias.absent(name), ret) diff --git a/tests/unit/states/test_alternatives.py b/tests/unit/states/test_alternatives.py index af2c0d8fe0c..d506c148b2b 100644 --- a/tests/unit/states/test_alternatives.py +++ b/tests/unit/states/test_alternatives.py @@ -1,213 +1,244 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.states.alternatives as alternatives +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class AlternativesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.alternatives - ''' + """ + def setup_loader_modules(self): return {alternatives: {}} # 'install' function tests: 1 def test_install(self): - ''' + """ Test to install new alternative for defined <name> - ''' - name = 'pager' - link = '/usr/bin/pager' - path = '/usr/bin/less' + """ + name = "pager" + link = "/usr/bin/pager" + path = "/usr/bin/less" priority = 5 - ret = {'name': name, - 'link': link, - 'path': path, - 'priority': priority, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = { + "name": name, + "link": link, + "path": path, + "priority": priority, + "result": None, + "changes": {}, + "comment": "", + } - bad_link = '/bin/pager' - err = 'the primary link for {0} must be {1}'.format(name, link) + bad_link = "/bin/pager" + err = "the primary link for {0} must be {1}".format(name, link) mock_cinst = MagicMock(side_effect=[True, False]) - mock_cexist = MagicMock(side_effect=[True, False, False, True, False, False, False, True]) - mock_out = MagicMock(side_effect=['', err, '']) + mock_cexist = MagicMock( + side_effect=[True, False, False, True, False, False, False, True] + ) + mock_out = MagicMock(side_effect=["", err, ""]) mock_path = MagicMock(return_value=path) mock_link = MagicMock(return_value=link) - with patch.dict(alternatives.__salt__, - {'alternatives.check_installed': mock_cinst, - 'alternatives.check_exists': mock_cexist, - 'alternatives.install': mock_out, - 'alternatives.show_current': mock_path, - 'alternatives.show_link': mock_link}): - comt = 'Alternative {0} for {1} is already registered'.format(path, name) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(alternatives.install(name, link, path, - priority), ret) + with patch.dict( + alternatives.__salt__, + { + "alternatives.check_installed": mock_cinst, + "alternatives.check_exists": mock_cexist, + "alternatives.install": mock_out, + "alternatives.show_current": mock_path, + "alternatives.show_link": mock_link, + }, + ): + comt = "Alternative {0} for {1} is already registered".format(path, name) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(alternatives.install(name, link, path, priority), ret) - comt = 'Alternative will be set for {0} to {1} with priority {2}'.format(name, path, priority) - ret.update({'comment': comt, 'result': None}) - with patch.dict(alternatives.__opts__, {'test': True}): - self.assertDictEqual(alternatives.install(name, link, path, - priority), ret) + comt = "Alternative will be set for {0} to {1} with priority {2}".format( + name, path, priority + ) + ret.update({"comment": comt, "result": None}) + with patch.dict(alternatives.__opts__, {"test": True}): + self.assertDictEqual( + alternatives.install(name, link, path, priority), ret + ) - comt = 'Alternative for {0} set to path {1} with priority {2}'.format(name, path, priority) - ret.update({'comment': comt, 'result': True, - 'changes': {'name': name, 'link': link, 'path': path, - 'priority': priority}}) - with patch.dict(alternatives.__opts__, {'test': False}): - self.assertDictEqual(alternatives.install(name, link, path, - priority), ret) + comt = "Alternative for {0} set to path {1} with priority {2}".format( + name, path, priority + ) + ret.update( + { + "comment": comt, + "result": True, + "changes": { + "name": name, + "link": link, + "path": path, + "priority": priority, + }, + } + ) + with patch.dict(alternatives.__opts__, {"test": False}): + self.assertDictEqual( + alternatives.install(name, link, path, priority), ret + ) - comt = 'Alternative for {0} not installed: {1}'.format(name, err) - ret.update({'comment': comt, 'result': False, - 'changes': {}, 'link': bad_link}) - with patch.dict(alternatives.__opts__, {'test': False}): - self.assertDictEqual(alternatives.install(name, bad_link, path, - priority), ret) + comt = "Alternative for {0} not installed: {1}".format(name, err) + ret.update( + {"comment": comt, "result": False, "changes": {}, "link": bad_link} + ) + with patch.dict(alternatives.__opts__, {"test": False}): + self.assertDictEqual( + alternatives.install(name, bad_link, path, priority), ret + ) - comt = 'Alternative {0} for {1} registered with priority {2} and not set to default'.format(path, name, priority) - ret.update({'comment': comt, 'result': True, - 'changes': {'name': name, 'link': link, 'path': path, - 'priority': priority}, 'link': link}) - with patch.dict(alternatives.__opts__, {'test': False}): - self.assertDictEqual(alternatives.install(name, link, path, - priority), ret) + comt = "Alternative {0} for {1} registered with priority {2} and not set to default".format( + path, name, priority + ) + ret.update( + { + "comment": comt, + "result": True, + "changes": { + "name": name, + "link": link, + "path": path, + "priority": priority, + }, + "link": link, + } + ) + with patch.dict(alternatives.__opts__, {"test": False}): + self.assertDictEqual( + alternatives.install(name, link, path, priority), ret + ) # 'remove' function tests: 1 def test_remove(self): - ''' + """ Test to removes installed alternative for defined <name> and <path> or fallback to default alternative, if some defined before. - ''' - name = 'pager' - path = '/usr/bin/less' + """ + name = "pager" + path = "/usr/bin/less" - ret = {'name': name, - 'path': path, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "path": path, "result": None, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, True, True, False, False]) mock_bool = MagicMock(return_value=True) mock_show = MagicMock(side_effect=[False, True, True, False]) - with patch.dict(alternatives.__salt__, - {'alternatives.check_exists': mock, - 'alternatives.show_current': mock_show, - 'alternatives.remove': mock_bool}): - comt = 'Alternative for {0} will be removed'.format(name) - ret.update({'comment': comt}) - with patch.dict(alternatives.__opts__, {'test': True}): + with patch.dict( + alternatives.__salt__, + { + "alternatives.check_exists": mock, + "alternatives.show_current": mock_show, + "alternatives.remove": mock_bool, + }, + ): + comt = "Alternative for {0} will be removed".format(name) + ret.update({"comment": comt}) + with patch.dict(alternatives.__opts__, {"test": True}): self.assertDictEqual(alternatives.remove(name, path), ret) - comt = 'Alternative for {0} removed'.format(name) - ret.update({'comment': comt, 'result': True}) - with patch.dict(alternatives.__opts__, {'test': False}): + comt = "Alternative for {0} removed".format(name) + ret.update({"comment": comt, "result": True}) + with patch.dict(alternatives.__opts__, {"test": False}): self.assertDictEqual(alternatives.remove(name, path), ret) - comt = ('Alternative for pager removed. Falling back to path True') - ret.update({'comment': comt, 'result': True, - 'changes': {'path': True}}) - with patch.dict(alternatives.__opts__, {'test': False}): + comt = "Alternative for pager removed. Falling back to path True" + ret.update({"comment": comt, "result": True, "changes": {"path": True}}) + with patch.dict(alternatives.__opts__, {"test": False}): self.assertDictEqual(alternatives.remove(name, path), ret) - comt = 'Alternative for {0} is set to it\'s default path True'.format(name) - ret.update({'comment': comt, 'result': True, 'changes': {}}) + comt = "Alternative for {0} is set to it's default path True".format(name) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(alternatives.remove(name, path), ret) - comt = 'Alternative for {0} doesn\'t exist'.format(name) - ret.update({'comment': comt, 'result': False}) + comt = "Alternative for {0} doesn't exist".format(name) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(alternatives.remove(name, path), ret) # 'auto' function tests: 1 def test_auto(self): - ''' + """ Test to instruct alternatives to use the highest priority path for <name> - ''' - name = 'pager' + """ + name = "pager" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - mock = MagicMock(side_effect=[' auto mode', ' ', ' ']) + mock = MagicMock(side_effect=[" auto mode", " ", " "]) mock_auto = MagicMock(return_value=True) - with patch.dict(alternatives.__salt__, - {'alternatives.display': mock, - 'alternatives.auto': mock_auto}): - comt = '{0} already in auto mode'.format(name) - ret.update({'comment': comt}) + with patch.dict( + alternatives.__salt__, + {"alternatives.display": mock, "alternatives.auto": mock_auto}, + ): + comt = "{0} already in auto mode".format(name) + ret.update({"comment": comt}) self.assertDictEqual(alternatives.auto(name), ret) - comt = '{0} will be put in auto mode'.format(name) - ret.update({'comment': comt, 'result': None}) - with patch.dict(alternatives.__opts__, {'test': True}): + comt = "{0} will be put in auto mode".format(name) + ret.update({"comment": comt, "result": None}) + with patch.dict(alternatives.__opts__, {"test": True}): self.assertDictEqual(alternatives.auto(name), ret) - ret.update({'comment': '', 'result': True, - 'changes': {'result': True}}) - with patch.dict(alternatives.__opts__, {'test': False}): + ret.update({"comment": "", "result": True, "changes": {"result": True}}) + with patch.dict(alternatives.__opts__, {"test": False}): self.assertDictEqual(alternatives.auto(name), ret) # 'set_' function tests: 1 def test_set(self): - ''' + """ Test to sets alternative for <name> to <path>, if <path> is defined as an alternative for <name>. - ''' - name = 'pager' - path = '/usr/bin/less' + """ + name = "pager" + path = "/usr/bin/less" - ret = {'name': name, - 'path': path, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "path": path, "result": True, "changes": {}, "comment": ""} - mock = MagicMock(side_effect=[path, path, '']) + mock = MagicMock(side_effect=[path, path, ""]) mock_bool = MagicMock(return_value=True) mock_show = MagicMock(side_effect=[path, False, False, False, False]) - with patch.dict(alternatives.__salt__, - {'alternatives.display': mock, - 'alternatives.show_current': mock_show, - 'alternatives.set': mock_bool}): - comt = 'Alternative for {0} already set to {1}'.format(name, path) - ret.update({'comment': comt}) + with patch.dict( + alternatives.__salt__, + { + "alternatives.display": mock, + "alternatives.show_current": mock_show, + "alternatives.set": mock_bool, + }, + ): + comt = "Alternative for {0} already set to {1}".format(name, path) + ret.update({"comment": comt}) self.assertDictEqual(alternatives.set_(name, path), ret) - comt = 'Alternative for {0} will be set to path /usr/bin/less'.format(name) - ret.update({'comment': comt, 'result': None}) - with patch.dict(alternatives.__opts__, {'test': True}): + comt = "Alternative for {0} will be set to path /usr/bin/less".format(name) + ret.update({"comment": comt, "result": None}) + with patch.dict(alternatives.__opts__, {"test": True}): self.assertDictEqual(alternatives.set_(name, path), ret) - comt = 'Alternative for {0} not updated'.format(name) - ret.update({'comment': comt, 'result': True}) - with patch.dict(alternatives.__opts__, {'test': False}): + comt = "Alternative for {0} not updated".format(name) + ret.update({"comment": comt, "result": True}) + with patch.dict(alternatives.__opts__, {"test": False}): self.assertDictEqual(alternatives.set_(name, path), ret) - comt = 'Alternative {0} for {1} doesn\'t exist'.format(path, name) - ret.update({'comment': comt, 'result': False}) + comt = "Alternative {0} for {1} doesn't exist".format(path, name) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(alternatives.set_(name, path), ret) diff --git a/tests/unit/states/test_apache.py b/tests/unit/states/test_apache.py index 412a366edb0..4e6b46584f8 100644 --- a/tests/unit/states/test_apache.py +++ b/tests/unit/states/test_apache.py @@ -1,67 +1,69 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - mock_open) - # Import Salt Libs import salt.states.apache as apache import salt.utils.files +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class ApacheTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.apache - ''' + """ + def setup_loader_modules(self): return {apache: {}} # 'configfile' function tests: 1 def test_configfile(self): - ''' + """ Test to allows for inputting a yaml dictionary into a file for apache configuration files. - ''' - with patch('os.path.exists', MagicMock(return_value=True)): - name = '/etc/distro/specific/apache.conf' + """ + with patch("os.path.exists", MagicMock(return_value=True)): + name = "/etc/distro/specific/apache.conf" config = 'VirtualHost: this: "*:80"' new_config = 'LiteralHost: that: "*:79"' - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - with patch.object(salt.utils.files, 'fopen', mock_open(read_data=config)): + with patch.object(salt.utils.files, "fopen", mock_open(read_data=config)): mock_config = MagicMock(return_value=config) - with patch.dict(apache.__salt__, {'apache.config': mock_config}): - ret.update({'comment': 'Configuration is up to date.'}) + with patch.dict(apache.__salt__, {"apache.config": mock_config}): + ret.update({"comment": "Configuration is up to date."}) self.assertDictEqual(apache.configfile(name, config), ret) - with patch.object(salt.utils.files, 'fopen', mock_open(read_data=config)): + with patch.object(salt.utils.files, "fopen", mock_open(read_data=config)): mock_config = MagicMock(return_value=new_config) - with patch.dict(apache.__salt__, {'apache.config': mock_config}): - ret.update({'comment': 'Configuration will update.', - 'changes': {'new': new_config, - 'old': config}, - 'result': None}) - with patch.dict(apache.__opts__, {'test': True}): + with patch.dict(apache.__salt__, {"apache.config": mock_config}): + ret.update( + { + "comment": "Configuration will update.", + "changes": {"new": new_config, "old": config}, + "result": None, + } + ) + with patch.dict(apache.__opts__, {"test": True}): self.assertDictEqual(apache.configfile(name, new_config), ret) - with patch.object(salt.utils.files, 'fopen', mock_open(read_data=config)): + with patch.object(salt.utils.files, "fopen", mock_open(read_data=config)): mock_config = MagicMock(return_value=new_config) - with patch.dict(apache.__salt__, {'apache.config': mock_config}): - ret.update({'comment': 'Successfully created configuration.', - 'result': True}) - with patch.dict(apache.__opts__, {'test': False}): + with patch.dict(apache.__salt__, {"apache.config": mock_config}): + ret.update( + { + "comment": "Successfully created configuration.", + "result": True, + } + ) + with patch.dict(apache.__opts__, {"test": False}): self.assertDictEqual(apache.configfile(name, config), ret) diff --git a/tests/unit/states/test_apache_conf.py b/tests/unit/states/test_apache_conf.py index 084c76f9d75..ade2211e7e5 100644 --- a/tests/unit/states/test_apache_conf.py +++ b/tests/unit/states/test_apache_conf.py @@ -2,87 +2,81 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.states.apache_conf as apache_conf +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ApacheConfTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.apache_conf - ''' + """ + def setup_loader_modules(self): return {apache_conf: {}} # 'enabled' function tests: 1 def test_enabled(self): - ''' + """ Test to ensure an Apache conf is enabled. - ''' - name = 'saltstack.com' + """ + name = "saltstack.com" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False, False]) - mock_str = MagicMock(return_value={'Status': ['enabled']}) - with patch.dict(apache_conf.__salt__, - {'apache.check_conf_enabled': mock, - 'apache.a2enconf': mock_str}): - comt = '{0} already enabled.'.format(name) - ret.update({'comment': comt}) + mock_str = MagicMock(return_value={"Status": ["enabled"]}) + with patch.dict( + apache_conf.__salt__, + {"apache.check_conf_enabled": mock, "apache.a2enconf": mock_str}, + ): + comt = "{0} already enabled.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(apache_conf.enabled(name), ret) - comt = 'Apache conf {0} is set to be enabled.'.format(name) - ret.update({'comment': comt, 'result': None, - 'changes': {'new': name, 'old': None}}) - with patch.dict(apache_conf.__opts__, {'test': True}): + comt = "Apache conf {0} is set to be enabled.".format(name) + ret.update( + {"comment": comt, "result": None, "changes": {"new": name, "old": None}} + ) + with patch.dict(apache_conf.__opts__, {"test": True}): self.assertDictEqual(apache_conf.enabled(name), ret) - comt = 'Failed to enable {0} Apache conf'.format(name) - ret.update({'comment': comt, 'result': False, 'changes': {}}) - with patch.dict(apache_conf.__opts__, {'test': False}): + comt = "Failed to enable {0} Apache conf".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) + with patch.dict(apache_conf.__opts__, {"test": False}): self.assertDictEqual(apache_conf.enabled(name), ret) # 'disabled' function tests: 1 def test_disabled(self): - ''' + """ Test to ensure an Apache conf is disabled. - ''' - name = 'saltstack.com' + """ + name = "saltstack.com" - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, True, False]) - mock_str = MagicMock(return_value={'Status': ['disabled']}) - with patch.dict(apache_conf.__salt__, - {'apache.check_conf_enabled': mock, - 'apache.a2disconf': mock_str}): - comt = 'Apache conf {0} is set to be disabled.'.format(name) - ret.update({'comment': comt, 'changes': {'new': None, 'old': name}}) - with patch.dict(apache_conf.__opts__, {'test': True}): + mock_str = MagicMock(return_value={"Status": ["disabled"]}) + with patch.dict( + apache_conf.__salt__, + {"apache.check_conf_enabled": mock, "apache.a2disconf": mock_str}, + ): + comt = "Apache conf {0} is set to be disabled.".format(name) + ret.update({"comment": comt, "changes": {"new": None, "old": name}}) + with patch.dict(apache_conf.__opts__, {"test": True}): self.assertDictEqual(apache_conf.disabled(name), ret) - comt = 'Failed to disable {0} Apache conf'.format(name) - ret.update({'comment': comt, 'result': False, - 'changes': {}}) - with patch.dict(apache_conf.__opts__, {'test': False}): + comt = "Failed to disable {0} Apache conf".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) + with patch.dict(apache_conf.__opts__, {"test": False}): self.assertDictEqual(apache_conf.disabled(name), ret) - comt = '{0} already disabled.'.format(name) - ret.update({'comment': comt, 'result': True}) + comt = "{0} already disabled.".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(apache_conf.disabled(name), ret) diff --git a/tests/unit/states/test_apache_module.py b/tests/unit/states/test_apache_module.py index 3d43deb0e43..62f9d9ddfb4 100644 --- a/tests/unit/states/test_apache_module.py +++ b/tests/unit/states/test_apache_module.py @@ -1,91 +1,89 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.states.apache_module as apache_module +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ApacheModuleTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.apache_module - ''' + """ + def setup_loader_modules(self): return {apache_module: {}} # 'enabled' function tests: 1 def test_enabled(self): - ''' + """ Test to ensure an Apache module is enabled. - ''' - name = 'cgi' + """ + name = "cgi" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False, False]) - mock_str = MagicMock(return_value={'Status': ['enabled']}) - with patch.dict(apache_module.__salt__, - {'apache.check_mod_enabled': mock, - 'apache.a2enmod': mock_str}): - comt = '{0} already enabled.'.format(name) - ret.update({'comment': comt}) + mock_str = MagicMock(return_value={"Status": ["enabled"]}) + with patch.dict( + apache_module.__salt__, + {"apache.check_mod_enabled": mock, "apache.a2enmod": mock_str}, + ): + comt = "{0} already enabled.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(apache_module.enabled(name), ret) - comt = 'Apache module {0} is set to be enabled.'.format(name) - ret.update({'comment': comt, 'result': None, - 'changes': {'new': 'cgi', 'old': None}}) - with patch.dict(apache_module.__opts__, {'test': True}): + comt = "Apache module {0} is set to be enabled.".format(name) + ret.update( + { + "comment": comt, + "result": None, + "changes": {"new": "cgi", "old": None}, + } + ) + with patch.dict(apache_module.__opts__, {"test": True}): self.assertDictEqual(apache_module.enabled(name), ret) - comt = 'Failed to enable {0} Apache module'.format(name) - ret.update({'comment': comt, 'result': False, 'changes': {}}) - with patch.dict(apache_module.__opts__, {'test': False}): + comt = "Failed to enable {0} Apache module".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) + with patch.dict(apache_module.__opts__, {"test": False}): self.assertDictEqual(apache_module.enabled(name), ret) # 'disabled' function tests: 1 def test_disabled(self): - ''' + """ Test to ensure an Apache module is disabled. - ''' - name = 'cgi' + """ + name = "cgi" - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, True, False]) - mock_str = MagicMock(return_value={'Status': ['disabled']}) - with patch.dict(apache_module.__salt__, - {'apache.check_mod_enabled': mock, - 'apache.a2dismod': mock_str}): - comt = 'Apache module {0} is set to be disabled.'.format(name) - ret.update({'comment': comt, 'changes': {'new': None, 'old': 'cgi'}}) - with patch.dict(apache_module.__opts__, {'test': True}): + mock_str = MagicMock(return_value={"Status": ["disabled"]}) + with patch.dict( + apache_module.__salt__, + {"apache.check_mod_enabled": mock, "apache.a2dismod": mock_str}, + ): + comt = "Apache module {0} is set to be disabled.".format(name) + ret.update({"comment": comt, "changes": {"new": None, "old": "cgi"}}) + with patch.dict(apache_module.__opts__, {"test": True}): self.assertDictEqual(apache_module.disabled(name), ret) - comt = 'Failed to disable {0} Apache module'.format(name) - ret.update({'comment': comt, 'result': False, - 'changes': {}}) - with patch.dict(apache_module.__opts__, {'test': False}): + comt = "Failed to disable {0} Apache module".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) + with patch.dict(apache_module.__opts__, {"test": False}): self.assertDictEqual(apache_module.disabled(name), ret) - comt = '{0} already disabled.'.format(name) - ret.update({'comment': comt, 'result': True}) + comt = "{0} already disabled.".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(apache_module.disabled(name), ret) diff --git a/tests/unit/states/test_apache_site.py b/tests/unit/states/test_apache_site.py index f199c63260f..2924da49adb 100644 --- a/tests/unit/states/test_apache_site.py +++ b/tests/unit/states/test_apache_site.py @@ -2,87 +2,81 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.states.apache_site as apache_site +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ApacheSiteTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.apache_site - ''' + """ + def setup_loader_modules(self): return {apache_site: {}} # 'enabled' function tests: 1 def test_enabled(self): - ''' + """ Test to ensure an Apache site is enabled. - ''' - name = 'saltstack.com' + """ + name = "saltstack.com" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False, False]) - mock_str = MagicMock(return_value={'Status': ['enabled']}) - with patch.dict(apache_site.__salt__, - {'apache.check_site_enabled': mock, - 'apache.a2ensite': mock_str}): - comt = '{0} already enabled.'.format(name) - ret.update({'comment': comt}) + mock_str = MagicMock(return_value={"Status": ["enabled"]}) + with patch.dict( + apache_site.__salt__, + {"apache.check_site_enabled": mock, "apache.a2ensite": mock_str}, + ): + comt = "{0} already enabled.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(apache_site.enabled(name), ret) - comt = 'Apache site {0} is set to be enabled.'.format(name) - ret.update({'comment': comt, 'result': None, - 'changes': {'new': name, 'old': None}}) - with patch.dict(apache_site.__opts__, {'test': True}): + comt = "Apache site {0} is set to be enabled.".format(name) + ret.update( + {"comment": comt, "result": None, "changes": {"new": name, "old": None}} + ) + with patch.dict(apache_site.__opts__, {"test": True}): self.assertDictEqual(apache_site.enabled(name), ret) - comt = 'Failed to enable {0} Apache site'.format(name) - ret.update({'comment': comt, 'result': False, 'changes': {}}) - with patch.dict(apache_site.__opts__, {'test': False}): + comt = "Failed to enable {0} Apache site".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) + with patch.dict(apache_site.__opts__, {"test": False}): self.assertDictEqual(apache_site.enabled(name), ret) # 'disabled' function tests: 1 def test_disabled(self): - ''' + """ Test to ensure an Apache site is disabled. - ''' - name = 'saltstack.com' + """ + name = "saltstack.com" - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, True, False]) - mock_str = MagicMock(return_value={'Status': ['disabled']}) - with patch.dict(apache_site.__salt__, - {'apache.check_site_enabled': mock, - 'apache.a2dissite': mock_str}): - comt = 'Apache site {0} is set to be disabled.'.format(name) - ret.update({'comment': comt, 'changes': {'new': None, 'old': name}}) - with patch.dict(apache_site.__opts__, {'test': True}): + mock_str = MagicMock(return_value={"Status": ["disabled"]}) + with patch.dict( + apache_site.__salt__, + {"apache.check_site_enabled": mock, "apache.a2dissite": mock_str}, + ): + comt = "Apache site {0} is set to be disabled.".format(name) + ret.update({"comment": comt, "changes": {"new": None, "old": name}}) + with patch.dict(apache_site.__opts__, {"test": True}): self.assertDictEqual(apache_site.disabled(name), ret) - comt = 'Failed to disable {0} Apache site'.format(name) - ret.update({'comment': comt, 'result': False, - 'changes': {}}) - with patch.dict(apache_site.__opts__, {'test': False}): + comt = "Failed to disable {0} Apache site".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) + with patch.dict(apache_site.__opts__, {"test": False}): self.assertDictEqual(apache_site.disabled(name), ret) - comt = '{0} already disabled.'.format(name) - ret.update({'comment': comt, 'result': True}) + comt = "{0} already disabled.".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(apache_site.disabled(name), ret) diff --git a/tests/unit/states/test_aptpkg.py b/tests/unit/states/test_aptpkg.py index 30bd6986b46..9dc03de1102 100644 --- a/tests/unit/states/test_aptpkg.py +++ b/tests/unit/states/test_aptpkg.py @@ -1,41 +1,42 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.aptpkg as aptpkg +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class AptTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.aptpkg - ''' + """ + def setup_loader_modules(self): return {aptpkg: {}} # 'held' function tests: 1 def test_held(self): - ''' + """ Test to set package in 'hold' state, meaning it will not be upgraded. - ''' - name = 'tmux' + """ + name = "tmux" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': 'Package {0} does not have a state'.format(name)} + ret = { + "name": name, + "result": False, + "changes": {}, + "comment": "Package {0} does not have a state".format(name), + } mock = MagicMock(return_value=False) - with patch.dict(aptpkg.__salt__, {'pkg.get_selections': mock}): + with patch.dict(aptpkg.__salt__, {"pkg.get_selections": mock}): self.assertDictEqual(aptpkg.held(name), ret) diff --git a/tests/unit/states/test_archive.py b/tests/unit/states/test_archive.py index d8a375efe9c..5ecd0e46249 100644 --- a/tests/unit/states/test_archive.py +++ b/tests/unit/states/test_archive.py @@ -1,362 +1,429 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Alexander Schwartz <alexander.schwartz@gmx.net> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import os # Import Salt Libs import salt.states.archive as archive -from salt.ext.six.moves import zip # pylint: disable=import-error,redefined-builtin import salt.utils.platform +from salt.ext.six.moves import zip # pylint: disable=import-error,redefined-builtin + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase def _isfile_side_effect(path): - ''' + """ MagicMock side_effect for os.path.isfile(). We don't want to use dict.get here because we want the test to fail if there's a path we haven't accounted for, so that we can add it. NOTE: This may fall over on some platforms if /usr/bin/tar does not exist. If so, just add an entry in the dictionary for the path being used for tar. - ''' + """ return { - '/tmp/foo.tar.gz': True, - 'c:\\tmp\\foo.tar.gz': True, - '/private/tmp/foo.tar.gz': True, - '/tmp/out': False, - '\\tmp\\out': False, - '/usr/bin/tar': True, - '/bin/tar': True, - '/tmp/test_extracted_tar': False, - 'c:\\tmp\\test_extracted_tar': False, - '/private/tmp/test_extracted_tar': False, + "/tmp/foo.tar.gz": True, + "c:\\tmp\\foo.tar.gz": True, + "/private/tmp/foo.tar.gz": True, + "/tmp/out": False, + "\\tmp\\out": False, + "/usr/bin/tar": True, + "/bin/tar": True, + "/tmp/test_extracted_tar": False, + "c:\\tmp\\test_extracted_tar": False, + "/private/tmp/test_extracted_tar": False, }[path.lower() if salt.utils.platform.is_windows() else path] class ArchiveTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { archive: { - '__grains__': {'os': 'FooOS!'}, - '__opts__': {'cachedir': '/tmp', - 'test': False, - 'hash_type': 'sha256'}, - '__env__': 'test' + "__grains__": {"os": "FooOS!"}, + "__opts__": {"cachedir": "/tmp", "test": False, "hash_type": "sha256"}, + "__env__": "test", } } def test_extracted_tar(self): - ''' + """ archive.extracted tar options - ''' + """ if salt.utils.platform.is_windows(): - source = 'c:\\tmp\\foo.tar.gz' - tmp_dir = 'c:\\tmp\\test_extracted_tar' + source = "c:\\tmp\\foo.tar.gz" + tmp_dir = "c:\\tmp\\test_extracted_tar" elif salt.utils.platform.is_darwin(): - source = '/private/tmp/foo.tar.gz' - tmp_dir = '/private/tmp/test_extracted_tar' + source = "/private/tmp/foo.tar.gz" + tmp_dir = "/private/tmp/test_extracted_tar" else: - source = '/tmp/foo.tar.gz' - tmp_dir = '/tmp/test_extracted_tar' + source = "/tmp/foo.tar.gz" + tmp_dir = "/tmp/test_extracted_tar" test_tar_opts = [ - '--no-anchored foo', - 'v -p --opt', - '-v -p', - '--long-opt -z', - 'z -v -weird-long-opt arg', + "--no-anchored foo", + "v -p --opt", + "-v -p", + "--long-opt -z", + "z -v -weird-long-opt arg", ] ret_tar_opts = [ - ['tar', 'xv', '--no-anchored', 'foo', '-f'], - ['tar', 'xv', '-p', '--opt', '-f'], - ['tar', 'xv', '-p', '-f'], - ['tar', 'xv', '--long-opt', '-z', '-f'], - ['tar', 'xvz', '-weird-long-opt', 'arg', '-f'], + ["tar", "xv", "--no-anchored", "foo", "-f"], + ["tar", "xv", "-p", "--opt", "-f"], + ["tar", "xv", "-p", "-f"], + ["tar", "xv", "--long-opt", "-z", "-f"], + ["tar", "xvz", "-weird-long-opt", "arg", "-f"], ] mock_true = MagicMock(return_value=True) mock_false = MagicMock(return_value=False) - ret = {'stdout': ['cheese', 'ham', 'saltines'], 'stderr': 'biscuits', 'retcode': '31337', 'pid': '1337'} + ret = { + "stdout": ["cheese", "ham", "saltines"], + "stderr": "biscuits", + "retcode": "31337", + "pid": "1337", + } mock_run = MagicMock(return_value=ret) mock_source_list = MagicMock(return_value=(source, None)) - state_single_mock = MagicMock(return_value={'local': {'result': True}}) - list_mock = MagicMock(return_value={ - 'dirs': [], - 'files': ['cheese', 'saltines'], - 'links': ['ham'], - 'top_level_dirs': [], - 'top_level_files': ['cheese', 'saltines'], - 'top_level_links': ['ham'], - }) + state_single_mock = MagicMock(return_value={"local": {"result": True}}) + list_mock = MagicMock( + return_value={ + "dirs": [], + "files": ["cheese", "saltines"], + "links": ["ham"], + "top_level_dirs": [], + "top_level_files": ["cheese", "saltines"], + "top_level_links": ["ham"], + } + ) isfile_mock = MagicMock(side_effect=_isfile_side_effect) - with patch.dict(archive.__opts__, {'test': False, - 'cachedir': tmp_dir, - 'hash_type': 'sha256'}),\ - patch.dict(archive.__salt__, {'file.directory_exists': mock_false, - 'file.file_exists': mock_false, - 'state.single': state_single_mock, - 'file.makedirs': mock_true, - 'cmd.run_all': mock_run, - 'archive.list': list_mock, - 'file.source_list': mock_source_list}),\ - patch.dict(archive.__states__, {'file.directory': mock_true}),\ - patch.object(os.path, 'isfile', isfile_mock),\ - patch('salt.utils.path.which', MagicMock(return_value=True)): + with patch.dict( + archive.__opts__, + {"test": False, "cachedir": tmp_dir, "hash_type": "sha256"}, + ), patch.dict( + archive.__salt__, + { + "file.directory_exists": mock_false, + "file.file_exists": mock_false, + "state.single": state_single_mock, + "file.makedirs": mock_true, + "cmd.run_all": mock_run, + "archive.list": list_mock, + "file.source_list": mock_source_list, + }, + ), patch.dict( + archive.__states__, {"file.directory": mock_true} + ), patch.object( + os.path, "isfile", isfile_mock + ), patch( + "salt.utils.path.which", MagicMock(return_value=True) + ): for test_opts, ret_opts in zip(test_tar_opts, ret_tar_opts): - archive.extracted(tmp_dir, source, options=test_opts, - enforce_toplevel=False) + archive.extracted( + tmp_dir, source, options=test_opts, enforce_toplevel=False + ) ret_opts.append(source) - mock_run.assert_called_with(ret_opts, cwd=tmp_dir + os.sep, - python_shell=False) + mock_run.assert_called_with( + ret_opts, cwd=tmp_dir + os.sep, python_shell=False + ) def test_tar_gnutar(self): - ''' + """ Tests the call of extraction with gnutar - ''' - gnutar = MagicMock(return_value='tar (GNU tar)') - source = '/tmp/foo.tar.gz' + """ + gnutar = MagicMock(return_value="tar (GNU tar)") + source = "/tmp/foo.tar.gz" mock_false = MagicMock(return_value=False) mock_true = MagicMock(return_value=True) - state_single_mock = MagicMock(return_value={'local': {'result': True}}) - run_all = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout', 'stderr': 'stderr'}) + state_single_mock = MagicMock(return_value={"local": {"result": True}}) + run_all = MagicMock( + return_value={"retcode": 0, "stdout": "stdout", "stderr": "stderr"} + ) mock_source_list = MagicMock(return_value=(source, None)) - list_mock = MagicMock(return_value={ - 'dirs': [], - 'files': ['stdout'], - 'links': [], - 'top_level_dirs': [], - 'top_level_files': ['stdout'], - 'top_level_links': [], - }) + list_mock = MagicMock( + return_value={ + "dirs": [], + "files": ["stdout"], + "links": [], + "top_level_dirs": [], + "top_level_files": ["stdout"], + "top_level_links": [], + } + ) isfile_mock = MagicMock(side_effect=_isfile_side_effect) - with patch.dict(archive.__salt__, {'cmd.run': gnutar, - 'file.directory_exists': mock_false, - 'file.file_exists': mock_false, - 'state.single': state_single_mock, - 'file.makedirs': mock_true, - 'cmd.run_all': run_all, - 'archive.list': list_mock, - 'file.source_list': mock_source_list}),\ - patch.dict(archive.__states__, {'file.directory': mock_true}),\ - patch.object(os.path, 'isfile', isfile_mock),\ - patch('salt.utils.path.which', MagicMock(return_value=True)): - ret = archive.extracted(os.path.join(os.sep + 'tmp', 'out'), - source, - options='xvzf', - enforce_toplevel=False, - keep=True) - self.assertEqual(ret['changes']['extracted_files'], ['stdout']) + with patch.dict( + archive.__salt__, + { + "cmd.run": gnutar, + "file.directory_exists": mock_false, + "file.file_exists": mock_false, + "state.single": state_single_mock, + "file.makedirs": mock_true, + "cmd.run_all": run_all, + "archive.list": list_mock, + "file.source_list": mock_source_list, + }, + ), patch.dict(archive.__states__, {"file.directory": mock_true}), patch.object( + os.path, "isfile", isfile_mock + ), patch( + "salt.utils.path.which", MagicMock(return_value=True) + ): + ret = archive.extracted( + os.path.join(os.sep + "tmp", "out"), + source, + options="xvzf", + enforce_toplevel=False, + keep=True, + ) + self.assertEqual(ret["changes"]["extracted_files"], ["stdout"]) def test_tar_bsdtar(self): - ''' + """ Tests the call of extraction with bsdtar - ''' - bsdtar = MagicMock(return_value='tar (bsdtar)') - source = '/tmp/foo.tar.gz' + """ + bsdtar = MagicMock(return_value="tar (bsdtar)") + source = "/tmp/foo.tar.gz" mock_false = MagicMock(return_value=False) mock_true = MagicMock(return_value=True) - state_single_mock = MagicMock(return_value={'local': {'result': True}}) - run_all = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout', 'stderr': 'stderr'}) + state_single_mock = MagicMock(return_value={"local": {"result": True}}) + run_all = MagicMock( + return_value={"retcode": 0, "stdout": "stdout", "stderr": "stderr"} + ) mock_source_list = MagicMock(return_value=(source, None)) - list_mock = MagicMock(return_value={ - 'dirs': [], - 'files': ['stderr'], - 'links': [], - 'top_level_dirs': [], - 'top_level_files': ['stderr'], - 'top_level_links': [], - }) + list_mock = MagicMock( + return_value={ + "dirs": [], + "files": ["stderr"], + "links": [], + "top_level_dirs": [], + "top_level_files": ["stderr"], + "top_level_links": [], + } + ) isfile_mock = MagicMock(side_effect=_isfile_side_effect) - with patch.dict(archive.__salt__, {'cmd.run': bsdtar, - 'file.directory_exists': mock_false, - 'file.file_exists': mock_false, - 'state.single': state_single_mock, - 'file.makedirs': mock_true, - 'cmd.run_all': run_all, - 'archive.list': list_mock, - 'file.source_list': mock_source_list}),\ - patch.dict(archive.__states__, {'file.directory': mock_true}),\ - patch.object(os.path, 'isfile', isfile_mock),\ - patch('salt.utils.path.which', MagicMock(return_value=True)): - ret = archive.extracted(os.path.join(os.sep + 'tmp', 'out'), - source, - options='xvzf', - enforce_toplevel=False, - keep=True) - self.assertEqual(ret['changes']['extracted_files'], ['stderr']) + with patch.dict( + archive.__salt__, + { + "cmd.run": bsdtar, + "file.directory_exists": mock_false, + "file.file_exists": mock_false, + "state.single": state_single_mock, + "file.makedirs": mock_true, + "cmd.run_all": run_all, + "archive.list": list_mock, + "file.source_list": mock_source_list, + }, + ), patch.dict(archive.__states__, {"file.directory": mock_true}), patch.object( + os.path, "isfile", isfile_mock + ), patch( + "salt.utils.path.which", MagicMock(return_value=True) + ): + ret = archive.extracted( + os.path.join(os.sep + "tmp", "out"), + source, + options="xvzf", + enforce_toplevel=False, + keep=True, + ) + self.assertEqual(ret["changes"]["extracted_files"], ["stderr"]) def test_extracted_when_if_missing_path_exists(self): - ''' + """ When if_missing exists, we should exit without making any changes. NOTE: We're not mocking the __salt__ dunder because if we actually run any functions from that dunder, we're doing something wrong. So, in those cases we'll just let it raise a KeyError and cause the test to fail. - ''' - name = if_missing = '/tmp/foo' - source = 'salt://foo.bar.tar' - with patch.object(os.path, 'exists', MagicMock(return_value=True)): - ret = archive.extracted( - name, - source=source, - if_missing=if_missing) - self.assertTrue(ret['result'], ret) - self.assertEqual( - ret['comment'], - 'Path {0} exists'.format(if_missing) - ) + """ + name = if_missing = "/tmp/foo" + source = "salt://foo.bar.tar" + with patch.object(os.path, "exists", MagicMock(return_value=True)): + ret = archive.extracted(name, source=source, if_missing=if_missing) + self.assertTrue(ret["result"], ret) + self.assertEqual(ret["comment"], "Path {0} exists".format(if_missing)) def test_clean_parent_conflict(self): - ''' + """ Tests the call of extraction with gnutar with both clean_parent plus clean set to True - ''' - gnutar = MagicMock(return_value='tar (GNU tar)') - source = '/tmp/foo.tar.gz' + """ + gnutar = MagicMock(return_value="tar (GNU tar)") + source = "/tmp/foo.tar.gz" ret_comment = "Only one of 'clean' and 'clean_parent' can be set to True" mock_false = MagicMock(return_value=False) mock_true = MagicMock(return_value=True) - state_single_mock = MagicMock(return_value={'local': {'result': True}}) - run_all = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout', 'stderr': 'stderr'}) + state_single_mock = MagicMock(return_value={"local": {"result": True}}) + run_all = MagicMock( + return_value={"retcode": 0, "stdout": "stdout", "stderr": "stderr"} + ) mock_source_list = MagicMock(return_value=(source, None)) - list_mock = MagicMock(return_value={ - 'dirs': [], - 'files': ['stdout'], - 'links': [], - 'top_level_dirs': [], - 'top_level_files': ['stdout'], - 'top_level_links': [], - }) + list_mock = MagicMock( + return_value={ + "dirs": [], + "files": ["stdout"], + "links": [], + "top_level_dirs": [], + "top_level_files": ["stdout"], + "top_level_links": [], + } + ) isfile_mock = MagicMock(side_effect=_isfile_side_effect) - with patch.dict(archive.__salt__, {'cmd.run': gnutar, - 'file.directory_exists': mock_false, - 'file.file_exists': mock_false, - 'state.single': state_single_mock, - 'file.makedirs': mock_true, - 'cmd.run_all': run_all, - 'archive.list': list_mock, - 'file.source_list': mock_source_list}),\ - patch.dict(archive.__states__, {'file.directory': mock_true}),\ - patch.object(os.path, 'isfile', isfile_mock),\ - patch('salt.utils.path.which', MagicMock(return_value=True)): - ret = archive.extracted(os.path.join(os.sep + 'tmp', 'out'), - source, - options='xvzf', - enforce_toplevel=False, - clean=True, - clean_parent=True, - keep=True) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['changes'], {}) - self.assertEqual(ret['comment'], ret_comment) + with patch.dict( + archive.__salt__, + { + "cmd.run": gnutar, + "file.directory_exists": mock_false, + "file.file_exists": mock_false, + "state.single": state_single_mock, + "file.makedirs": mock_true, + "cmd.run_all": run_all, + "archive.list": list_mock, + "file.source_list": mock_source_list, + }, + ), patch.dict(archive.__states__, {"file.directory": mock_true}), patch.object( + os.path, "isfile", isfile_mock + ), patch( + "salt.utils.path.which", MagicMock(return_value=True) + ): + ret = archive.extracted( + os.path.join(os.sep + "tmp", "out"), + source, + options="xvzf", + enforce_toplevel=False, + clean=True, + clean_parent=True, + keep=True, + ) + self.assertEqual(ret["result"], False) + self.assertEqual(ret["changes"], {}) + self.assertEqual(ret["comment"], ret_comment) def test_skip_files_list_verify_conflict(self): - ''' + """ Tests the call of extraction with both skip_files_list_verify and skip_verify set to True - ''' - gnutar = MagicMock(return_value='tar (GNU tar)') - source = '/tmp/foo.tar.gz' - ret_comment = 'Only one of "skip_files_list_verify" and "skip_verify" can be set to True' + """ + gnutar = MagicMock(return_value="tar (GNU tar)") + source = "/tmp/foo.tar.gz" + ret_comment = ( + 'Only one of "skip_files_list_verify" and "skip_verify" can be set to True' + ) mock_false = MagicMock(return_value=False) mock_true = MagicMock(return_value=True) - state_single_mock = MagicMock(return_value={'local': {'result': True}}) - run_all = MagicMock(return_value={'retcode': 0, 'stdout': 'stdout', 'stderr': 'stderr'}) + state_single_mock = MagicMock(return_value={"local": {"result": True}}) + run_all = MagicMock( + return_value={"retcode": 0, "stdout": "stdout", "stderr": "stderr"} + ) mock_source_list = MagicMock(return_value=(source, None)) - list_mock = MagicMock(return_value={ - 'dirs': [], - 'files': ['stdout'], - 'links': [], - 'top_level_dirs': [], - 'top_level_files': ['stdout'], - 'top_level_links': [], - }) + list_mock = MagicMock( + return_value={ + "dirs": [], + "files": ["stdout"], + "links": [], + "top_level_dirs": [], + "top_level_files": ["stdout"], + "top_level_links": [], + } + ) isfile_mock = MagicMock(side_effect=_isfile_side_effect) - with patch.dict(archive.__salt__, {'cmd.run': gnutar, - 'file.directory_exists': mock_false, - 'file.file_exists': mock_false, - 'state.single': state_single_mock, - 'file.makedirs': mock_true, - 'cmd.run_all': run_all, - 'archive.list': list_mock, - 'file.source_list': mock_source_list}),\ - patch.dict(archive.__states__, {'file.directory': mock_true}),\ - patch.object(os.path, 'isfile', isfile_mock),\ - patch('salt.utils.path.which', MagicMock(return_value=True)): - ret = archive.extracted(os.path.join(os.sep + 'tmp', 'out'), - source, - options='xvzf', - enforce_toplevel=False, - clean=True, - skip_files_list_verify=True, - skip_verify=True, - keep=True) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['changes'], {}) - self.assertEqual(ret['comment'], ret_comment) + with patch.dict( + archive.__salt__, + { + "cmd.run": gnutar, + "file.directory_exists": mock_false, + "file.file_exists": mock_false, + "state.single": state_single_mock, + "file.makedirs": mock_true, + "cmd.run_all": run_all, + "archive.list": list_mock, + "file.source_list": mock_source_list, + }, + ), patch.dict(archive.__states__, {"file.directory": mock_true}), patch.object( + os.path, "isfile", isfile_mock + ), patch( + "salt.utils.path.which", MagicMock(return_value=True) + ): + ret = archive.extracted( + os.path.join(os.sep + "tmp", "out"), + source, + options="xvzf", + enforce_toplevel=False, + clean=True, + skip_files_list_verify=True, + skip_verify=True, + keep=True, + ) + self.assertEqual(ret["result"], False) + self.assertEqual(ret["changes"], {}) + self.assertEqual(ret["comment"], ret_comment) def test_skip_files_list_verify_success(self): - ''' + """ Test that if the local and expected source hash are the same we won't do anything. - ''' + """ if salt.utils.platform.is_windows(): - source = 'c:\\tmp\\foo.tar.gz' - tmp_dir = 'c:\\tmp\\test_extracted_tar' + source = "c:\\tmp\\foo.tar.gz" + tmp_dir = "c:\\tmp\\test_extracted_tar" elif salt.utils.platform.is_darwin(): - source = '/private/tmp/foo.tar.gz' - tmp_dir = '/private/tmp/test_extracted_tar' + source = "/private/tmp/foo.tar.gz" + tmp_dir = "/private/tmp/test_extracted_tar" else: - source = '/tmp/foo.tar.gz' - tmp_dir = '/tmp/test_extracted_tar' + source = "/tmp/foo.tar.gz" + tmp_dir = "/tmp/test_extracted_tar" - expected_comment = ('Archive {} existing source sum is the same as the ' - 'expected one and skip_files_list_verify argument ' - 'was set to True. Extraction is not needed'.format(source)) - expected_ret = {'name': tmp_dir, - 'result': True, - 'changes': {}, - 'comment': expected_comment - } + expected_comment = ( + "Archive {} existing source sum is the same as the " + "expected one and skip_files_list_verify argument " + "was set to True. Extraction is not needed".format(source) + ) + expected_ret = { + "name": tmp_dir, + "result": True, + "changes": {}, + "comment": expected_comment, + } mock_true = MagicMock(return_value=True) mock_false = MagicMock(return_value=False) - mock_cached = MagicMock(return_value='{}/{}'.format(tmp_dir, source)) - source_sum = {'hsum': 'testhash', 'hash_type': 'sha256'} + mock_cached = MagicMock(return_value="{}/{}".format(tmp_dir, source)) + source_sum = {"hsum": "testhash", "hash_type": "sha256"} mock_hash = MagicMock(return_value=source_sum) mock_source_list = MagicMock(return_value=(source, None)) isfile_mock = MagicMock(side_effect=_isfile_side_effect) - with patch('salt.states.archive._read_cached_checksum', mock_hash): - with patch.dict(archive.__opts__, {'test': False, - 'cachedir': tmp_dir, - 'hash_type': 'sha256'}),\ - patch.dict(archive.__salt__, {'file.directory_exists': mock_false, - 'file.get_source_sum': mock_hash, - 'file.check_hash': mock_true, - 'cp.is_cached': mock_cached, - 'file.source_list': mock_source_list}),\ - patch.object(os.path, 'isfile', isfile_mock): + with patch("salt.states.archive._read_cached_checksum", mock_hash): + with patch.dict( + archive.__opts__, + {"test": False, "cachedir": tmp_dir, "hash_type": "sha256"}, + ), patch.dict( + archive.__salt__, + { + "file.directory_exists": mock_false, + "file.get_source_sum": mock_hash, + "file.check_hash": mock_true, + "cp.is_cached": mock_cached, + "file.source_list": mock_source_list, + }, + ), patch.object( + os.path, "isfile", isfile_mock + ): - ret = archive.extracted(tmp_dir, - source, - source_hash='testhash', - skip_files_list_verify=True, - enforce_toplevel=False) + ret = archive.extracted( + tmp_dir, + source, + source_hash="testhash", + skip_files_list_verify=True, + enforce_toplevel=False, + ) self.assertDictEqual(ret, expected_ret) diff --git a/tests/unit/states/test_artifactory.py b/tests/unit/states/test_artifactory.py index adf3ded728e..9a223548a5c 100644 --- a/tests/unit/states/test_artifactory.py +++ b/tests/unit/states/test_artifactory.py @@ -1,85 +1,97 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.artifactory as artifactory +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ArtifactoryTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.artifactory - ''' + """ + def setup_loader_modules(self): return {artifactory: {}} # 'downloaded' function tests: 1 def test_downloaded(self): - ''' + """ Test to ensures that the artifact from artifactory exists at given location. - ''' - name = 'jboss' - arti_url = 'http://artifactory.intranet.example.com/artifactory' - artifact = {'artifactory_url': arti_url, 'artifact_id': 'module', - 'repository': 'libs-release-local', 'packaging': 'jar', - 'group_id': 'com.company.module', 'classifier': 'sources', - 'version': '1.0'} + """ + name = "jboss" + arti_url = "http://artifactory.intranet.example.com/artifactory" + artifact = { + "artifactory_url": arti_url, + "artifact_id": "module", + "repository": "libs-release-local", + "packaging": "jar", + "group_id": "com.company.module", + "classifier": "sources", + "version": "1.0", + } - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - mck = MagicMock(return_value={'status': False, 'changes': {}, - 'comment': ''}) - with patch.dict(artifactory.__salt__, {'artifactory.get_release': mck}): + mck = MagicMock(return_value={"status": False, "changes": {}, "comment": ""}) + with patch.dict(artifactory.__salt__, {"artifactory.get_release": mck}): self.assertDictEqual(artifactory.downloaded(name, artifact), ret) - with patch.object(artifactory, '__fetch_from_artifactory', - MagicMock(side_effect=Exception('error'))): + with patch.object( + artifactory, + "__fetch_from_artifactory", + MagicMock(side_effect=Exception("error")), + ): ret = artifactory.downloaded(name, artifact) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'error') + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "error") # 'downloaded test=True' function tests: 1 def test_downloaded_test_true(self): - ''' + """ Test to ensures that the artifact from artifactory exists at given location. - ''' - name = 'jboss' - arti_url = 'http://artifactory.intranet.example.com/artifactory' - artifact = {'artifactory_url': arti_url, 'artifact_id': 'module', - 'repository': 'libs-release-local', 'packaging': 'jar', - 'group_id': 'com.company.module', 'classifier': 'sources', - 'version': '1.0'} + """ + name = "jboss" + arti_url = "http://artifactory.intranet.example.com/artifactory" + artifact = { + "artifactory_url": arti_url, + "artifact_id": "module", + "repository": "libs-release-local", + "packaging": "jar", + "group_id": "com.company.module", + "classifier": "sources", + "version": "1.0", + } - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': 'Artifact would be downloaded from URL: http://artifactory.intranet.example.com/artifactory'} + ret = { + "name": name, + "result": True, + "changes": {}, + "comment": "Artifact would be downloaded from URL: http://artifactory.intranet.example.com/artifactory", + } - mck = MagicMock(return_value={'status': False, 'changes': {}, - 'comment': ''}) - with patch.dict(artifactory.__salt__, {'artifactory.get_release': mck}): - with patch.dict(artifactory.__opts__, {'test': True}): + mck = MagicMock(return_value={"status": False, "changes": {}, "comment": ""}) + with patch.dict(artifactory.__salt__, {"artifactory.get_release": mck}): + with patch.dict(artifactory.__opts__, {"test": True}): self.assertDictEqual(artifactory.downloaded(name, artifact), ret) - with patch.object(artifactory, '__fetch_from_artifactory', - MagicMock(side_effect=Exception('error'))): + with patch.object( + artifactory, + "__fetch_from_artifactory", + MagicMock(side_effect=Exception("error")), + ): ret = artifactory.downloaded(name, artifact) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'error') + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "error") diff --git a/tests/unit/states/test_at.py b/tests/unit/states/test_at.py index 4ceb29a78ee..65dd5f1d14a 100644 --- a/tests/unit/states/test_at.py +++ b/tests/unit/states/test_at.py @@ -1,211 +1,196 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.at as at +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class AtTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.at - ''' + """ + def setup_loader_modules(self): return {at: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to add a job to queue. - ''' + """ # input variables - name = 'jboss' - timespec = '9:09 11/04/15' - tag = 'love' - user = 'jam' + name = "jboss" + timespec = "9:09 11/04/15" + tag = "love" + user = "jam" # mock for at.at module call mock_atat = { - 'jobs': [ + "jobs": [ { - 'date': '2015-11-04', - 'job': '1476031633.a', - 'queue': 'a', - 'tag': tag, - 'time': '09:09:00', - 'user': user, + "date": "2015-11-04", + "job": "1476031633.a", + "queue": "a", + "tag": tag, + "time": "09:09:00", + "user": user, }, ], } # normale return ret = { - 'name': name, - 'result': True, - 'changes': { - 'date': '2015-11-04', - 'job': '1476031633.a', - 'queue': 'a', - 'tag': 'love', - 'time': '09:09:00', - 'user': 'jam', + "name": name, + "result": True, + "changes": { + "date": "2015-11-04", + "job": "1476031633.a", + "queue": "a", + "tag": "love", + "time": "09:09:00", + "user": "jam", }, - 'comment': 'job {name} added and will run on {timespec}'.format( - name=name, - timespec=timespec + "comment": "job {name} added and will run on {timespec}".format( + name=name, timespec=timespec ), } # unknown user return ret_user = {} ret_user.update(ret) - ret_user.update({ - 'result': False, - 'changes': {}, - 'comment': 'user {0} does not exists'.format(user), - }) + ret_user.update( + { + "result": False, + "changes": {}, + "comment": "user {0} does not exists".format(user), + } + ) # add a job mock = MagicMock(return_value=mock_atat) - with patch.dict(at.__opts__, {'test': False}): - with patch.dict(at.__salt__, {'at.at': mock}): + with patch.dict(at.__opts__, {"test": False}): + with patch.dict(at.__salt__, {"at.at": mock}): self.assertDictEqual(at.present(name, timespec, tag), ret) # add a job with a non-existing user mock = MagicMock(return_value=False) - with patch.dict(at.__opts__, {'test': False}): - with patch.dict(at.__salt__, {'user.info': mock}): + with patch.dict(at.__opts__, {"test": False}): + with patch.dict(at.__salt__, {"user.info": mock}): self.assertDictEqual(at.present(name, timespec, tag, user), ret_user) # add a job with test=True - with patch.dict(at.__opts__, {'test': True}): + with patch.dict(at.__opts__, {"test": True}): ret_test = {} ret_test.update(ret) - ret_test.update({'result': None, 'changes': {}}) + ret_test.update({"result": None, "changes": {}}) self.assertDictEqual(at.present(name, timespec, tag, user), ret_test) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to remove a job from queue - ''' + """ # input variables - name = 'jboss' - tag = 'rose' - user = 'jam' + name = "jboss" + tag = "rose" + user = "jam" # mock for at.atrm module call mock_atatrm = { - 'jobs': { - 'removed': ['1476033859.a', '1476033855.a'], - 'tag': None, - }, + "jobs": {"removed": ["1476033859.a", "1476033855.a"], "tag": None}, } # mock for at.jobcheck module call mock_atjobcheck = { - 'jobs': [ + "jobs": [ { - 'date': '2015-11-04', - 'job': '1476031633.a', - 'queue': 'a', - 'tag': tag, - 'time': '09:09:00', - 'user': user, + "date": "2015-11-04", + "job": "1476031633.a", + "queue": "a", + "tag": tag, + "time": "09:09:00", + "user": user, }, ], } # normal return ret = { - 'name': name, - 'result': True, - 'changes': { - 'removed': ['1476033859.a', '1476033855.a'], - }, - 'comment': 'removed 2 job(s)', + "name": name, + "result": True, + "changes": {"removed": ["1476033859.a", "1476033855.a"]}, + "comment": "removed 2 job(s)", } # remove a job with test=True - with patch.dict(at.__opts__, {'test': True}): + with patch.dict(at.__opts__, {"test": True}): ret_test = {} ret_test.update(ret) - ret_test.update({ - 'result': None, - 'changes': {}, - 'comment': 'removed ? job(s)' - }) + ret_test.update( + {"result": None, "changes": {}, "comment": "removed ? job(s)"} + ) self.assertDictEqual(at.absent(name), ret_test) # remove a job and pass limit parameter - with patch.dict(at.__opts__, {'test': False}): + with patch.dict(at.__opts__, {"test": False}): ret_limit = {} ret_limit.update(ret) - ret_limit.update({ - 'result': False, - 'changes': {}, - 'comment': 'limit parameter not supported {0}'.format(name), - }) - self.assertDictEqual(at.absent(name, limit='all'), ret_limit) + ret_limit.update( + { + "result": False, + "changes": {}, + "comment": "limit parameter not supported {0}".format(name), + } + ) + self.assertDictEqual(at.absent(name, limit="all"), ret_limit) # remove all jobs (2 jobs found) mock = MagicMock(return_value=mock_atatrm) - with patch.dict(at.__salt__, {'at.atrm': mock}): - with patch.dict(at.__opts__, {'test': False}): + with patch.dict(at.__salt__, {"at.atrm": mock}): + with patch.dict(at.__opts__, {"test": False}): self.assertDictEqual(at.absent(name), ret) # remove all jobs (0 jobs found) mock_atatrm_nojobs = {} mock_atatrm_nojobs.update(mock_atatrm) - mock_atatrm_nojobs.update({ - 'jobs': { - 'removed': [], - }, - }) + mock_atatrm_nojobs.update({"jobs": {"removed": []}}) mock = MagicMock(return_value=mock_atatrm_nojobs) - with patch.dict(at.__salt__, {'at.atrm': mock}): - with patch.dict(at.__opts__, {'test': False}): + with patch.dict(at.__salt__, {"at.atrm": mock}): + with patch.dict(at.__opts__, {"test": False}): ret_nojobs = {} ret_nojobs.update(ret) - ret_nojobs.update({ - 'changes': {}, - 'comment': ret['comment'].replace('2', '0'), - }) + ret_nojobs.update( + {"changes": {}, "comment": ret["comment"].replace("2", "0")} + ) self.assertDictEqual(at.absent(name), ret_nojobs) # remove all tagged jobs (1 jobs found) mock_atatrm_tag = {} mock_atatrm_tag.update(mock_atatrm) - mock_atatrm_tag.update({ - 'jobs': { - 'removed': ['1476031633.a'], - 'tag': 'rose', - }, - }) + mock_atatrm_tag.update({"jobs": {"removed": ["1476031633.a"], "tag": "rose"}}) mock = MagicMock(return_value=mock_atatrm_tag) - with patch.dict(at.__salt__, {'at.atrm': mock}): + with patch.dict(at.__salt__, {"at.atrm": mock}): mock = MagicMock(return_value=mock_atjobcheck) - with patch.dict(at.__salt__, {'at.jobcheck': mock}): - with patch.dict(at.__opts__, {'test': False}): + with patch.dict(at.__salt__, {"at.jobcheck": mock}): + with patch.dict(at.__opts__, {"test": False}): ret_tag = {} ret_tag.update(ret) - ret_tag.update({ - 'changes': { - 'removed': ['1476031633.a'], - }, - 'comment': ret['comment'].replace('2', '1'), - }) + ret_tag.update( + { + "changes": {"removed": ["1476031633.a"]}, + "comment": ret["comment"].replace("2", "1"), + } + ) self.assertDictEqual(at.absent(name, tag=tag), ret_tag) diff --git a/tests/unit/states/test_augeas.py b/tests/unit/states/test_augeas.py index 4e74299bdc2..d20dcc1fa9b 100644 --- a/tests/unit/states/test_augeas.py +++ b/tests/unit/states/test_augeas.py @@ -1,54 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> :codeauthor: Andrew Colin Kissa <andrew@topdog.za.net> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - mock_open, - MagicMock, - patch -) +import os # Import Salt Libs import salt.states.augeas as augeas +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch +from tests.support.unit import TestCase + class AugeasTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.augeas - ''' + """ + def setup_loader_modules(self): return {augeas: {}} # 'change' function tests: 1 def setUp(self): - self.name = 'zabbix' - self.context = '/files/etc/services' - self.changes = ['ins service-name after service-name[last()]', - 'set service-name[last()] zabbix-agent'] + self.name = "zabbix" + self.context = "/files/etc/services" + self.changes = [ + "ins service-name after service-name[last()]", + "set service-name[last()] zabbix-agent", + ] self.fp_changes = [ - 'ins service-name after /files/etc/services/service-name[last()]', - 'set /files/etc/services/service-name[last()] zabbix-agent'] - self.ret = {'name': self.name, - 'result': False, - 'changes': {}, - 'comment': ''} + "ins service-name after /files/etc/services/service-name[last()]", + "set /files/etc/services/service-name[last()] zabbix-agent", + ] + self.ret = {"name": self.name, "result": False, "changes": {}, "comment": ""} method_map = { - 'set': 'set', - 'setm': 'setm', - 'mv': 'move', - 'move': 'move', - 'ins': 'insert', - 'insert': 'insert', - 'rm': 'remove', - 'remove': 'remove', + "set": "set", + "setm": "setm", + "mv": "move", + "move": "move", + "ins": "insert", + "insert": "insert", + "rm": "remove", + "remove": "remove", } self.mock_method_map = MagicMock(return_value=method_map) @@ -59,188 +57,218 @@ class AugeasTestCase(TestCase, LoaderModuleMockMixin): del self.mock_method_map def test_change_non_list_changes(self): - ''' + """ Test if none list changes handled correctly - ''' - comt = ('\'changes\' must be specified as a list') - self.ret.update({'comment': comt}) + """ + comt = "'changes' must be specified as a list" + self.ret.update({"comment": comt}) self.assertDictEqual(augeas.change(self.name), self.ret) def test_change_non_list_load_path(self): - ''' + """ Test if none list load_path is handled correctly - ''' - comt = ('\'load_path\' must be specified as a list') - self.ret.update({'comment': comt}) + """ + comt = "'load_path' must be specified as a list" + self.ret.update({"comment": comt}) - self.assertDictEqual(augeas.change( - self.name, self.context, self.changes, load_path='x'), self.ret) + self.assertDictEqual( + augeas.change(self.name, self.context, self.changes, load_path="x"), + self.ret, + ) def test_change_in_test_mode(self): - ''' + """ Test test mode handling - ''' - comt = ('Executing commands in file "/files/etc/services":\n' - 'ins service-name after service-name[last()]' - '\nset service-name[last()] zabbix-agent') - self.ret.update({'comment': comt, 'result': True}) + """ + comt = ( + 'Executing commands in file "/files/etc/services":\n' + "ins service-name after service-name[last()]" + "\nset service-name[last()] zabbix-agent" + ) + self.ret.update({"comment": comt, "result": True}) - with patch.dict(augeas.__opts__, {'test': True}): + with patch.dict(augeas.__opts__, {"test": True}): self.assertDictEqual( - augeas.change(self.name, self.context, self.changes), - self.ret) + augeas.change(self.name, self.context, self.changes), self.ret + ) def test_change_no_context_without_full_path(self): - ''' + """ Test handling of no context without full path - ''' + """ comt = ( - 'Error: Changes should be prefixed with /files if no ' - 'context is provided, change: {0}'.format(self.changes[0]) + "Error: Changes should be prefixed with /files if no " + "context is provided, change: {0}".format(self.changes[0]) ) - self.ret.update({'comment': comt, 'result': False}) + self.ret.update({"comment": comt, "result": False}) - with patch.dict(augeas.__opts__, {'test': False}): - mock_dict_ = {'augeas.method_map': self.mock_method_map} + with patch.dict(augeas.__opts__, {"test": False}): + mock_dict_ = {"augeas.method_map": self.mock_method_map} with patch.dict(augeas.__salt__, mock_dict_): self.assertDictEqual( - augeas.change(self.name, changes=self.changes), - self.ret) + augeas.change(self.name, changes=self.changes), self.ret + ) def test_change_no_context_with_full_path_fail(self): - ''' + """ Test handling of no context with full path with execute fail - ''' - self.ret.update({'comment': 'Error: error', 'result': False}) + """ + self.ret.update({"comment": "Error: error", "result": False}) - with patch.dict(augeas.__opts__, {'test': False}): - mock_execute = MagicMock( - return_value=dict(retval=False, error='error')) - mock_dict_ = {'augeas.execute': mock_execute, - 'augeas.method_map': self.mock_method_map} + with patch.dict(augeas.__opts__, {"test": False}): + mock_execute = MagicMock(return_value=dict(retval=False, error="error")) + mock_dict_ = { + "augeas.execute": mock_execute, + "augeas.method_map": self.mock_method_map, + } with patch.dict(augeas.__salt__, mock_dict_): self.assertDictEqual( - augeas.change(self.name, changes=self.fp_changes), - self.ret) + augeas.change(self.name, changes=self.fp_changes), self.ret + ) def test_change_no_context_with_full_path_pass(self): - ''' + """ Test handling of no context with full path with execute pass - ''' - self.ret.update(dict(comment='Changes have been saved', - result=True, - changes={'diff': '+ zabbix-agent'})) + """ + self.ret.update( + dict( + comment="Changes have been saved", + result=True, + changes={"diff": "+ zabbix-agent"}, + ) + ) - with patch.dict(augeas.__opts__, {'test': False}): + with patch.dict(augeas.__opts__, {"test": False}): mock_execute = MagicMock(return_value=dict(retval=True)) - mock_dict_ = {'augeas.execute': mock_execute, - 'augeas.method_map': self.mock_method_map} + mock_dict_ = { + "augeas.execute": mock_execute, + "augeas.method_map": self.mock_method_map, + } with patch.dict(augeas.__salt__, mock_dict_): - mock_filename = MagicMock(return_value='/etc/services') - with patch.object(augeas, '_workout_filename', mock_filename), \ - patch('os.path.isfile', MagicMock(return_value=True)): - with patch('salt.utils.files.fopen', MagicMock(mock_open)): - mock_diff = MagicMock(return_value=['+ zabbix-agent']) - with patch('difflib.unified_diff', mock_diff): - self.assertDictEqual(augeas.change(self.name, - changes=self.fp_changes), - self.ret) + mock_filename = MagicMock(return_value="/etc/services") + with patch.object(augeas, "_workout_filename", mock_filename), patch( + "os.path.isfile", MagicMock(return_value=True) + ): + with patch("salt.utils.files.fopen", MagicMock(mock_open)): + mock_diff = MagicMock(return_value=["+ zabbix-agent"]) + with patch("difflib.unified_diff", mock_diff): + self.assertDictEqual( + augeas.change(self.name, changes=self.fp_changes), + self.ret, + ) def test_change_no_context_without_full_path_invalid_cmd(self): - ''' + """ Test handling of invalid commands when no context supplied - ''' - self.ret.update(dict(comment='Error: Command det is not supported (yet)', - result=False)) + """ + self.ret.update( + dict(comment="Error: Command det is not supported (yet)", result=False) + ) - with patch.dict(augeas.__opts__, {'test': False}): + with patch.dict(augeas.__opts__, {"test": False}): mock_execute = MagicMock(return_value=dict(retval=True)) - mock_dict_ = {'augeas.execute': mock_execute, - 'augeas.method_map': self.mock_method_map} + mock_dict_ = { + "augeas.execute": mock_execute, + "augeas.method_map": self.mock_method_map, + } with patch.dict(augeas.__salt__, mock_dict_): - changes = ['det service-name[last()] zabbix-agent'] - self.assertDictEqual(augeas.change(self.name, - changes=changes), - self.ret) + changes = ["det service-name[last()] zabbix-agent"] + self.assertDictEqual( + augeas.change(self.name, changes=changes), self.ret + ) def test_change_no_context_without_full_path_invalid_change(self): - ''' + """ Test handling of invalid change when no context supplied - ''' - comt = ('Error: Invalid formatted command, see ' - 'debug log for details: require') - self.ret.update(dict(comment=comt, - result=False)) - changes = ['require'] + """ + comt = "Error: Invalid formatted command, see " "debug log for details: require" + self.ret.update(dict(comment=comt, result=False)) + changes = ["require"] - with patch.dict(augeas.__opts__, {'test': False}): + with patch.dict(augeas.__opts__, {"test": False}): mock_execute = MagicMock(return_value=dict(retval=True)) - mock_dict_ = {'augeas.execute': mock_execute, - 'augeas.method_map': self.mock_method_map} + mock_dict_ = { + "augeas.execute": mock_execute, + "augeas.method_map": self.mock_method_map, + } with patch.dict(augeas.__salt__, mock_dict_): - self.assertDictEqual(augeas.change(self.name, - changes=changes), - self.ret) + self.assertDictEqual( + augeas.change(self.name, changes=changes), self.ret + ) def test_change_no_context_with_full_path_multiple_files(self): - ''' + """ Test handling of different paths with no context supplied - ''' - changes = ['set /files/etc/hosts/service-name test', - 'set /files/etc/services/service-name test'] - filename = '/etc/hosts/service-name' - filename_ = '/etc/services/service-name' + """ + changes = [ + "set /files/etc/hosts/service-name test", + "set /files/etc/services/service-name test", + ] + filename = "/etc/hosts/service-name" + filename_ = "/etc/services/service-name" comt = ( - 'Error: Changes should be made to one file at a time, ' - 'detected changes to {0} and {1}'.format(filename, filename_) + "Error: Changes should be made to one file at a time, " + "detected changes to {0} and {1}".format(filename, filename_) ) - self.ret.update(dict(comment=comt, - result=False)) + self.ret.update(dict(comment=comt, result=False)) - with patch.dict(augeas.__opts__, {'test': False}): + with patch.dict(augeas.__opts__, {"test": False}): mock_execute = MagicMock(return_value=dict(retval=True)) - mock_dict_ = {'augeas.execute': mock_execute, - 'augeas.method_map': self.mock_method_map} + mock_dict_ = { + "augeas.execute": mock_execute, + "augeas.method_map": self.mock_method_map, + } with patch.dict(augeas.__salt__, mock_dict_): - self.assertDictEqual(augeas.change(self.name, - changes=changes), - self.ret) + self.assertDictEqual( + augeas.change(self.name, changes=changes), self.ret + ) def test_change_with_context_without_full_path_fail(self): - ''' + """ Test handling of context without full path fails - ''' - self.ret.update(dict(comment='Error: error', result=False)) + """ + self.ret.update(dict(comment="Error: error", result=False)) - with patch.dict(augeas.__opts__, {'test': False}): - mock_execute = MagicMock( - return_value=dict(retval=False, error='error')) - mock_dict_ = {'augeas.execute': mock_execute, - 'augeas.method_map': self.mock_method_map} + with patch.dict(augeas.__opts__, {"test": False}): + mock_execute = MagicMock(return_value=dict(retval=False, error="error")) + mock_dict_ = { + "augeas.execute": mock_execute, + "augeas.method_map": self.mock_method_map, + } with patch.dict(augeas.__salt__, mock_dict_): - with patch('salt.utils.files.fopen', MagicMock(mock_open)): - self.assertDictEqual(augeas.change(self.name, - context=self.context, - changes=self.changes), - self.ret) + with patch("salt.utils.files.fopen", MagicMock(mock_open)): + self.assertDictEqual( + augeas.change( + self.name, context=self.context, changes=self.changes + ), + self.ret, + ) def test_change_with_context_without_old_file(self): - ''' + """ Test handling of context without oldfile pass - ''' - self.ret.update(dict(comment='Changes have been saved', - result=True, - changes={'updates': self.changes})) + """ + self.ret.update( + dict( + comment="Changes have been saved", + result=True, + changes={"updates": self.changes}, + ) + ) - with patch.dict(augeas.__opts__, {'test': False}): + with patch.dict(augeas.__opts__, {"test": False}): mock_execute = MagicMock(return_value=dict(retval=True)) - mock_dict_ = {'augeas.execute': mock_execute, - 'augeas.method_map': self.mock_method_map} + mock_dict_ = { + "augeas.execute": mock_execute, + "augeas.method_map": self.mock_method_map, + } with patch.dict(augeas.__salt__, mock_dict_): mock_isfile = MagicMock(return_value=False) - with patch.object(os.path, 'isfile', mock_isfile): - self.assertDictEqual(augeas.change(self.name, - context=self.context, - changes=self.changes), - self.ret) + with patch.object(os.path, "isfile", mock_isfile): + self.assertDictEqual( + augeas.change( + self.name, context=self.context, changes=self.changes + ), + self.ret, + ) diff --git a/tests/unit/states/test_aws_sqs.py b/tests/unit/states/test_aws_sqs.py index 063e61bd799..f41103c6beb 100644 --- a/tests/unit/states/test_aws_sqs.py +++ b/tests/unit/states/test_aws_sqs.py @@ -1,74 +1,67 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.aws_sqs as aws_sqs +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class AwsSqsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.aws_sqs - ''' + """ + def setup_loader_modules(self): return {aws_sqs: {}} # 'exists' function tests: 1 def test_exists(self): - ''' + """ Test to ensure the SQS queue exists. - ''' - name = 'myqueue' - region = 'eu-west-1' + """ + name = "myqueue" + region = "eu-west-1" - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[False, True]) - with patch.dict(aws_sqs.__salt__, {'aws_sqs.queue_exists': mock}): - comt = 'AWS SQS queue {0} is set to be created'.format(name) - ret.update({'comment': comt}) - with patch.dict(aws_sqs.__opts__, {'test': True}): + with patch.dict(aws_sqs.__salt__, {"aws_sqs.queue_exists": mock}): + comt = "AWS SQS queue {0} is set to be created".format(name) + ret.update({"comment": comt}) + with patch.dict(aws_sqs.__opts__, {"test": True}): self.assertDictEqual(aws_sqs.exists(name, region), ret) - comt = '{0} exists in {1}'.format(name, region) - ret.update({'comment': comt, 'result': True}) + comt = "{0} exists in {1}".format(name, region) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(aws_sqs.exists(name, region), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to remove the named SQS queue if it exists. - ''' - name = 'myqueue' - region = 'eu-west-1' + """ + name = "myqueue" + region = "eu-west-1" - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False]) - with patch.dict(aws_sqs.__salt__, {'aws_sqs.queue_exists': mock}): - comt = 'AWS SQS queue {0} is set to be removed'.format(name) - ret.update({'comment': comt}) - with patch.dict(aws_sqs.__opts__, {'test': True}): + with patch.dict(aws_sqs.__salt__, {"aws_sqs.queue_exists": mock}): + comt = "AWS SQS queue {0} is set to be removed".format(name) + ret.update({"comment": comt}) + with patch.dict(aws_sqs.__opts__, {"test": True}): self.assertDictEqual(aws_sqs.absent(name, region), ret) - comt = '{0} does not exist in {1}'.format(name, region) - ret.update({'comment': comt, 'result': True}) + comt = "{0} does not exist in {1}".format(name, region) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(aws_sqs.absent(name, region), ret) diff --git a/tests/unit/states/test_beacon.py b/tests/unit/states/test_beacon.py index 93b5e93cb80..6eba62e7fbe 100644 --- a/tests/unit/states/test_beacon.py +++ b/tests/unit/states/test_beacon.py @@ -1,84 +1,77 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.states.beacon as beacon +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BeaconTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.beacon - ''' + """ + def setup_loader_modules(self): return {beacon: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure a job is present in the beacon. - ''' - beacon_name = 'ps' + """ + beacon_name = "ps" - ret = {'name': beacon_name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": beacon_name, "changes": {}, "result": False, "comment": ""} mock_mod = MagicMock(return_value=ret) - mock_lst = MagicMock(side_effect=[{beacon_name: {}}, - {beacon_name: {}}, - {}, - {}]) - with patch.dict(beacon.__salt__, - {'beacons.list': mock_lst, - 'beacons.modify': mock_mod, - 'beacons.add': mock_mod}): + mock_lst = MagicMock(side_effect=[{beacon_name: {}}, {beacon_name: {}}, {}, {}]) + with patch.dict( + beacon.__salt__, + { + "beacons.list": mock_lst, + "beacons.modify": mock_mod, + "beacons.add": mock_mod, + }, + ): self.assertDictEqual(beacon.present(beacon_name), ret) - with patch.dict(beacon.__opts__, {'test': False}): + with patch.dict(beacon.__opts__, {"test": False}): self.assertDictEqual(beacon.present(beacon_name), ret) self.assertDictEqual(beacon.present(beacon_name), ret) - with patch.dict(beacon.__opts__, {'test': True}): - ret.update({'result': True}) + with patch.dict(beacon.__opts__, {"test": True}): + ret.update({"result": True}) self.assertDictEqual(beacon.present(beacon_name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure a job is absent from the schedule. - ''' - beacon_name = 'ps' + """ + beacon_name = "ps" - ret = {'name': beacon_name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": beacon_name, "changes": {}, "result": False, "comment": ""} mock_mod = MagicMock(return_value=ret) mock_lst = MagicMock(side_effect=[{beacon_name: {}}, {}]) - with patch.dict(beacon.__salt__, - {'beacons.list': mock_lst, - 'beacons.delete': mock_mod}): - with patch.dict(beacon.__opts__, {'test': False}): + with patch.dict( + beacon.__salt__, {"beacons.list": mock_lst, "beacons.delete": mock_mod} + ): + with patch.dict(beacon.__opts__, {"test": False}): self.assertDictEqual(beacon.absent(beacon_name), ret) - with patch.dict(beacon.__opts__, {'test': True}): - comt = ('ps not configured in beacons') - ret.update({'comment': comt, 'result': True}) + with patch.dict(beacon.__opts__, {"test": True}): + comt = "ps not configured in beacons" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(beacon.absent(beacon_name), ret) diff --git a/tests/unit/states/test_blockdev.py b/tests/unit/states/test_blockdev.py index 0f37afb0205..e0996ea0006 100644 --- a/tests/unit/states/test_blockdev.py +++ b/tests/unit/states/test_blockdev.py @@ -1,119 +1,124 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch) +import os # Import Salt Libs import salt.states.blockdev as blockdev import salt.utils.path +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase + class BlockdevTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.blockdev - ''' + """ + def setup_loader_modules(self): return {blockdev: {}} # 'tuned' function tests: 1 def test_tuned(self): - ''' + """ Test to manage options of block device - ''' - name = '/dev/vg/master-data' + """ + name = "/dev/vg/master-data" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - comt = ('Changes to {0} cannot be applied. ' - 'Not a block device. ').format(name) - with patch.dict(blockdev.__salt__, {'file.is_blkdev': False}): - ret.update({'comment': comt}) + comt = ("Changes to {0} cannot be applied. " "Not a block device. ").format( + name + ) + with patch.dict(blockdev.__salt__, {"file.is_blkdev": False}): + ret.update({"comment": comt}) self.assertDictEqual(blockdev.tuned(name), ret) - comt = ('Changes to {0} will be applied '.format(name)) - with patch.dict(blockdev.__salt__, {'file.is_blkdev': True}): - ret.update({'comment': comt, 'result': None}) - with patch.dict(blockdev.__opts__, {'test': True}): + comt = "Changes to {0} will be applied ".format(name) + with patch.dict(blockdev.__salt__, {"file.is_blkdev": True}): + ret.update({"comment": comt, "result": None}) + with patch.dict(blockdev.__opts__, {"test": True}): self.assertDictEqual(blockdev.tuned(name), ret) # 'formatted' function tests: 1 def test_formatted(self): - ''' + """ Test to manage filesystems of partitions. - ''' - name = '/dev/vg/master-data' + """ + name = "/dev/vg/master-data" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - with patch.object(os.path, 'exists', MagicMock(side_effect=[False, True, - True, True, - True])): - comt = ('{0} does not exist'.format(name)) - ret.update({'comment': comt}) + with patch.object( + os.path, "exists", MagicMock(side_effect=[False, True, True, True, True]) + ): + comt = "{0} does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(blockdev.formatted(name), ret) - mock_ext4 = MagicMock(return_value='ext4') + mock_ext4 = MagicMock(return_value="ext4") # Test state return when block device is already in the correct state - with patch.dict(blockdev.__salt__, {'cmd.run': mock_ext4}): - comt = '{0} already formatted with ext4'.format(name) - ret.update({'comment': comt, 'result': True}) + with patch.dict(blockdev.__salt__, {"cmd.run": mock_ext4}): + comt = "{0} already formatted with ext4".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(blockdev.formatted(name), ret) # Test state return when provided block device is an invalid fs_type - with patch.dict(blockdev.__salt__, {'cmd.run': MagicMock(return_value='')}): - ret.update({'comment': 'Invalid fs_type: foo-bar', - 'result': False}) - with patch.object(salt.utils.path, 'which', - MagicMock(return_value=False)): - self.assertDictEqual(blockdev.formatted(name, fs_type='foo-bar'), ret) + with patch.dict(blockdev.__salt__, {"cmd.run": MagicMock(return_value="")}): + ret.update({"comment": "Invalid fs_type: foo-bar", "result": False}) + with patch.object( + salt.utils.path, "which", MagicMock(return_value=False) + ): + self.assertDictEqual( + blockdev.formatted(name, fs_type="foo-bar"), ret + ) # Test state return when provided block device state will change and test=True - with patch.dict(blockdev.__salt__, {'cmd.run': MagicMock(return_value='new-thing')}): - comt = ('Changes to {0} will be applied '.format(name)) - ret.update({'comment': comt, 'result': None}) - with patch.object(salt.utils.path, 'which', - MagicMock(return_value=True)): - with patch.dict(blockdev.__opts__, {'test': True}): + with patch.dict( + blockdev.__salt__, {"cmd.run": MagicMock(return_value="new-thing")} + ): + comt = "Changes to {0} will be applied ".format(name) + ret.update({"comment": comt, "result": None}) + with patch.object( + salt.utils.path, "which", MagicMock(return_value=True) + ): + with patch.dict(blockdev.__opts__, {"test": True}): self.assertDictEqual(blockdev.formatted(name), ret) # Test state return when block device format fails - with patch.dict(blockdev.__salt__, {'cmd.run': MagicMock(return_value=mock_ext4), - 'disk.format': MagicMock(return_value=True)}): - comt = ('Failed to format {0}'.format(name)) - ret.update({'comment': comt, 'result': False}) - with patch.object(salt.utils.path, 'which', - MagicMock(return_value=True)): - with patch.dict(blockdev.__opts__, {'test': False}): + with patch.dict( + blockdev.__salt__, + { + "cmd.run": MagicMock(return_value=mock_ext4), + "disk.format": MagicMock(return_value=True), + }, + ): + comt = "Failed to format {0}".format(name) + ret.update({"comment": comt, "result": False}) + with patch.object( + salt.utils.path, "which", MagicMock(return_value=True) + ): + with patch.dict(blockdev.__opts__, {"test": False}): self.assertDictEqual(blockdev.formatted(name), ret) def test__checkblk(self): - ''' + """ Confirm that we call cmd.run with ignore_retcode=True - ''' + """ cmd_mock = Mock() - with patch.dict(blockdev.__salt__, {'cmd.run': cmd_mock}): - blockdev._checkblk('/dev/foo') + with patch.dict(blockdev.__salt__, {"cmd.run": cmd_mock}): + blockdev._checkblk("/dev/foo") cmd_mock.assert_called_once_with( - ['blkid', '-o', 'value', '-s', 'TYPE', '/dev/foo'], - ignore_retcode=True) + ["blkid", "-o", "value", "-s", "TYPE", "/dev/foo"], ignore_retcode=True + ) diff --git a/tests/unit/states/test_boto_apigateway.py b/tests/unit/states/test_boto_apigateway.py index 8d5a034f0d3..80d2e031094 100644 --- a/tests/unit/states/test_boto_apigateway.py +++ b/tests/unit/states/test_boto_apigateway.py @@ -2,316 +2,383 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime import logging import os -import datetime import random import string -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch - # Import Salt libs import salt.config import salt.loader + +# Import Salt Libs +import salt.states.boto_apigateway as boto_apigateway import salt.utils.files import salt.utils.yaml + +# Import 3rd-party libs +from salt.ext.six.moves import range from salt.utils.versions import LooseVersion +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + # pylint: disable=import-error,no-name-in-module from tests.unit.modules.test_boto_apigateway import BotoApiGatewayTestCaseMixin -# Import 3rd-party libs -from salt.ext.six.moves import range try: import boto3 import botocore from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False -# Import Salt Libs -import salt.states.boto_apigateway as boto_apigateway - # pylint: enable=import-error,no-name-in-module # the boto_apigateway module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' -required_botocore_version = '1.4.49' +required_boto3_version = "1.2.1" +required_botocore_version = "1.4.49" -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} -error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' -error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, +} +error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" +) +error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} + +api_ret = dict( + description='{\n "context": "See deployment or stage description",\n "provisioned_by": "Salt boto_apigateway.present State"\n}', + createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + id="vni0vq8wzi", + name="unit test api", +) + +no_apis_ret = {"items": []} + +apis_ret = {"items": [api_ret]} + +mock_model_ret = dict( + contentType="application/json", + description="mock model", + id="123abc", + name="mock model", + schema=( + "{\n" + ' "$schema": "http://json-schema.org/draft-04/schema#",\n' + ' "properties": {\n' + ' "field": {\n' + ' "type": "string"\n' + " }\n" + " }\n" + "}" + ), +) + +models_ret = { + "items": [ + dict( + contentType="application/json", + description="Error", + id="50nw8r", + name="Error", + schema=( + "{\n" + ' "$schema": "http://json-schema.org/draft-04/schema#",\n' + ' "properties": {\n' + ' "code": {\n' + ' "format": "int32",\n' + ' "type": "integer"\n' + " },\n" + ' "fields": {\n' + ' "type": "string"\n' + " },\n" + ' "message": {\n' + ' "type": "string"\n' + " }\n" + " },\n" + ' "title": "Error Schema",\n' + ' "type": "object"\n' + "}" + ), + ), + dict( + contentType="application/json", + description="User", + id="terlnw", + name="User", + schema=( + "{\n" + ' "$schema": "http://json-schema.org/draft-04/schema#",\n' + ' "properties": {\n' + ' "password": {\n' + ' "description": "A password for the new user",\n' + ' "type": "string"\n' + " },\n" + ' "username": {\n' + ' "description": "A unique username for the user",\n' + ' "type": "string"\n' + " }\n" + " },\n" + ' "title": "User Schema",\n' + ' "type": "object"\n' + "}" + ), + ), + ] } -api_ret = dict(description='{\n "context": "See deployment or stage description",\n "provisioned_by": "Salt boto_apigateway.present State"\n}', - createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - id='vni0vq8wzi', - name='unit test api') +root_resources_ret = {"items": [dict(id="bgk0rk8rqb", path="/")]} -no_apis_ret = {'items': []} +resources_ret = { + "items": [ + dict(id="bgk0rk8rqb", path="/"), + dict( + id="9waiaz", + parentId="bgk0rk8rqb", + path="/users", + pathPart="users", + resourceMethods={"POST": {}}, + ), + ] +} -apis_ret = {'items': [api_ret]} +no_resources_ret = {"items": []} -mock_model_ret = dict(contentType='application/json', - description='mock model', - id='123abc', - name='mock model', - schema=('{\n' - ' "$schema": "http://json-schema.org/draft-04/schema#",\n' - ' "properties": {\n' - ' "field": {\n' - ' "type": "string"\n' - ' }\n' - ' }\n' - '}')) +stage1_deployment1_ret = dict( + cacheClusterEnabled=False, + cacheClusterSize=0.5, + cacheClusterStatus="NOT_AVAILABLE", + createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + deploymentId="kobnrb", + description=( + "{\n" + ' "current_deployment_label": {\n' + ' "api_name": "unit test api",\n' + ' "swagger_file": "temp-swagger-sample.yaml",\n' + ' "swagger_file_md5sum": "4fb17e43bab3a96e7f2410a1597cd0a5",\n' + ' "swagger_info_object": {\n' + ' "description": "salt boto apigateway unit test service",\n' + ' "title": "salt boto apigateway unit test service",\n' + ' "version": "0.0.0"\n' + " }\n" + " }\n" + "}" + ), + lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + methodSettings=dict(), + stageName="test", + variables=dict(), +) -models_ret = {'items': [dict(contentType='application/json', - description='Error', - id='50nw8r', - name='Error', - schema=('{\n' - ' "$schema": "http://json-schema.org/draft-04/schema#",\n' - ' "properties": {\n' - ' "code": {\n' - ' "format": "int32",\n' - ' "type": "integer"\n' - ' },\n' - ' "fields": {\n' - ' "type": "string"\n' - ' },\n' - ' "message": {\n' - ' "type": "string"\n' - ' }\n' - ' },\n' - ' "title": "Error Schema",\n' - ' "type": "object"\n' - '}')), - dict(contentType='application/json', - description='User', - id='terlnw', - name='User', - schema=('{\n' - ' "$schema": "http://json-schema.org/draft-04/schema#",\n' - ' "properties": {\n' - ' "password": {\n' - ' "description": "A password for the new user",\n' - ' "type": "string"\n' - ' },\n' - ' "username": {\n' - ' "description": "A unique username for the user",\n' - ' "type": "string"\n' - ' }\n' - ' },\n' - ' "title": "User Schema",\n' - ' "type": "object"\n' - '}'))]} +stage1_deployment1_vars_ret = dict( + cacheClusterEnabled=False, + cacheClusterSize=0.5, + cacheClusterStatus="NOT_AVAILABLE", + createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + deploymentId="kobnrb", + description=( + "{\n" + ' "current_deployment_label": {\n' + ' "api_name": "unit test api",\n' + ' "swagger_file": "temp-swagger-sample.yaml",\n' + ' "swagger_file_md5sum": "4fb17e43bab3a96e7f2410a1597cd0a5",\n' + ' "swagger_info_object": {\n' + ' "description": "salt boto apigateway unit test service",\n' + ' "title": "salt boto apigateway unit test service",\n' + ' "version": "0.0.0"\n' + " }\n" + " }\n" + "}" + ), + lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + methodSettings=dict(), + stageName="test", + variables={"var1": "val1"}, +) -root_resources_ret = {'items': [dict(id='bgk0rk8rqb', - path='/')]} +stage1_deployment2_ret = dict( + cacheClusterEnabled=False, + cacheClusterSize=0.5, + cacheClusterStatus="NOT_AVAILABLE", + createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + deploymentId="kobnrc", + description=( + "{\n" + ' "current_deployment_label": {\n' + ' "api_name": "unit test api",\n' + ' "swagger_file": "temp-swagger-sample.yaml",\n' + ' "swagger_file_md5sum": "5fd538c4336ed5c54b4bf39ddf97c661",\n' + ' "swagger_info_object": {\n' + ' "description": "salt boto apigateway unit test service",\n' + ' "title": "salt boto apigateway unit test service",\n' + ' "version": "0.0.2"\n' + " }\n" + " }\n" + "}" + ), + lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + methodSettings=dict(), + stageName="test", + variables=dict(), +) -resources_ret = {'items': [dict(id='bgk0rk8rqb', - path='/'), - dict(id='9waiaz', - parentId='bgk0rk8rqb', - path='/users', - pathPart='users', - resourceMethods={'POST': {}})]} +stage2_ret = dict( + cacheClusterEnabled=False, + cacheClusterSize=0.5, + cacheClusterStatus="NOT_AVAILABLE", + createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + deploymentId="kobnrb", + description=( + "{\n" + ' "current_deployment_label": {\n' + ' "api_name": "unit test api",\n' + ' "swagger_file": "temp-swagger-sample.yaml",\n' + ' "swagger_file_md5sum": "4fb17e43bab3a96e7f2410a1597cd0a5",\n' + ' "swagger_info_object": {\n' + ' "description": "salt boto apigateway unit test service",\n' + ' "title": "salt boto apigateway unit test service",\n' + ' "version": "0.0.0"\n' + " }\n" + " }\n" + "}" + ), + lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + methodSettings=dict(), + stageName="dev", + variables=dict(), +) -no_resources_ret = {'items': []} +stages_stage2_ret = {"item": [stage2_ret]} -stage1_deployment1_ret = dict(cacheClusterEnabled=False, - cacheClusterSize=0.5, - cacheClusterStatus='NOT_AVAILABLE', - createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - deploymentId='kobnrb', - description=('{\n' - ' "current_deployment_label": {\n' - ' "api_name": "unit test api",\n' - ' "swagger_file": "temp-swagger-sample.yaml",\n' - ' "swagger_file_md5sum": "4fb17e43bab3a96e7f2410a1597cd0a5",\n' - ' "swagger_info_object": {\n' - ' "description": "salt boto apigateway unit test service",\n' - ' "title": "salt boto apigateway unit test service",\n' - ' "version": "0.0.0"\n' - ' }\n' - ' }\n' - '}'), - lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - methodSettings=dict(), - stageName='test', - variables=dict()) +no_stages_ret = {"item": []} -stage1_deployment1_vars_ret = dict(cacheClusterEnabled=False, - cacheClusterSize=0.5, - cacheClusterStatus='NOT_AVAILABLE', - createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - deploymentId='kobnrb', - description=('{\n' - ' "current_deployment_label": {\n' - ' "api_name": "unit test api",\n' - ' "swagger_file": "temp-swagger-sample.yaml",\n' - ' "swagger_file_md5sum": "4fb17e43bab3a96e7f2410a1597cd0a5",\n' - ' "swagger_info_object": {\n' - ' "description": "salt boto apigateway unit test service",\n' - ' "title": "salt boto apigateway unit test service",\n' - ' "version": "0.0.0"\n' - ' }\n' - ' }\n' - '}'), - lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - methodSettings=dict(), - stageName='test', - variables={'var1': 'val1'}) +deployment1_ret = dict( + createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + description=( + "{\n" + ' "api_name": "unit test api",\n' + ' "swagger_file": "temp-swagger-sample.yaml",\n' + ' "swagger_file_md5sum": "55a948ff90ad80ff747ec91657c7a299",\n' + ' "swagger_info_object": {\n' + ' "description": "salt boto apigateway unit test service",\n' + ' "title": "salt boto apigateway unit test service",\n' + ' "version": "0.0.0"\n' + " }\n" + "}" + ), + id="kobnrb", +) -stage1_deployment2_ret = dict(cacheClusterEnabled=False, - cacheClusterSize=0.5, - cacheClusterStatus='NOT_AVAILABLE', - createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - deploymentId='kobnrc', - description=('{\n' - ' "current_deployment_label": {\n' - ' "api_name": "unit test api",\n' - ' "swagger_file": "temp-swagger-sample.yaml",\n' - ' "swagger_file_md5sum": "5fd538c4336ed5c54b4bf39ddf97c661",\n' - ' "swagger_info_object": {\n' - ' "description": "salt boto apigateway unit test service",\n' - ' "title": "salt boto apigateway unit test service",\n' - ' "version": "0.0.2"\n' - ' }\n' - ' }\n' - '}'), - lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - methodSettings=dict(), - stageName='test', - variables=dict()) +deployment2_ret = dict( + createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), + description=( + "{\n" + ' "api_name": "unit test api",\n' + ' "swagger_file": "temp-swagger-sample.yaml",\n' + ' "swagger_file_md5sum": "5fd538c4336ed5c54b4bf39ddf97c661",\n' + ' "swagger_info_object": {\n' + ' "description": "salt boto apigateway unit test service",\n' + ' "title": "salt boto apigateway unit test service",\n' + ' "version": "0.0.2"\n' + " }\n" + "}" + ), + id="kobnrc", +) -stage2_ret = dict(cacheClusterEnabled=False, - cacheClusterSize=0.5, - cacheClusterStatus='NOT_AVAILABLE', - createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - deploymentId='kobnrb', - description=('{\n' - ' "current_deployment_label": {\n' - ' "api_name": "unit test api",\n' - ' "swagger_file": "temp-swagger-sample.yaml",\n' - ' "swagger_file_md5sum": "4fb17e43bab3a96e7f2410a1597cd0a5",\n' - ' "swagger_info_object": {\n' - ' "description": "salt boto apigateway unit test service",\n' - ' "title": "salt boto apigateway unit test service",\n' - ' "version": "0.0.0"\n' - ' }\n' - ' }\n' - '}'), - lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - methodSettings=dict(), - stageName='dev', - variables=dict()) +deployments_ret = {"items": [deployment1_ret, deployment2_ret]} -stages_stage2_ret = {'item': [stage2_ret]} +function_ret = dict( + FunctionName="unit_test_api_users_post", + Runtime="python2.7", + Role=None, + Handler="handler", + Description="abcdefg", + Timeout=5, + MemorySize=128, + CodeSha256="abcdef", + CodeSize=199, + FunctionArn="arn:lambda:us-east-1:1234:Something", + LastModified="yes", +) -no_stages_ret = {'item': []} +method_integration_response_200_ret = dict( + responseParameters={"method.response.header.Access-Control-Allow-Origin": "*"}, + responseTemplates={}, + selectionPattern=".*", + statusCode="200", +) -deployment1_ret = dict(createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - description=('{\n' - ' "api_name": "unit test api",\n' - ' "swagger_file": "temp-swagger-sample.yaml",\n' - ' "swagger_file_md5sum": "55a948ff90ad80ff747ec91657c7a299",\n' - ' "swagger_info_object": {\n' - ' "description": "salt boto apigateway unit test service",\n' - ' "title": "salt boto apigateway unit test service",\n' - ' "version": "0.0.0"\n' - ' }\n' - '}'), - id='kobnrb') +method_integration_ret = dict( + cacheKeyParameters={}, + cacheNamespace="9waiaz", + credentials="arn:aws:iam::1234:role/apigatewayrole", + httpMethod="POST", + integrationResponses={"200": method_integration_response_200_ret}, + requestParameters={}, + requestTemplates={ + "application/json": ( + "#set($inputRoot = $input.path('$'))" + "{" + '"header-params" : {#set ($map = $input.params().header)#foreach( $param in $map.entrySet() )"$param.key" : "$param.value" #if( $foreach.hasNext ), #end#end},' + '"query-params" : {#set ($map = $input.params().querystring)#foreach( $param in $map.entrySet() )"$param.key" : "$param.value" #if( $foreach.hasNext ), #end#end},' + '"path-params" : {#set ($map = $input.params().path)#foreach( $param in $map.entrySet() )"$param.key" : "$param.value" #if( $foreach.hasNext ), #end#end},' + "\"body-params\" : $input.json('$')" + "}" + ) + }, + type="AWS", + uri=( + "arn:aws:apigateway:us-west-2:" + "lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:1234567:" + "function:unit_test_api_api_users_post/invocations" + ), +) -deployment2_ret = dict(createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50), - description=('{\n' - ' "api_name": "unit test api",\n' - ' "swagger_file": "temp-swagger-sample.yaml",\n' - ' "swagger_file_md5sum": "5fd538c4336ed5c54b4bf39ddf97c661",\n' - ' "swagger_info_object": {\n' - ' "description": "salt boto apigateway unit test service",\n' - ' "title": "salt boto apigateway unit test service",\n' - ' "version": "0.0.2"\n' - ' }\n' - '}'), - id='kobnrc') +method_response_200_ret = dict( + responseModels={"application/json": "User"}, + responseParameters={"method.response.header.Access-Control-Allow-Origin": False}, + statusCode="200", +) -deployments_ret = {'items': [deployment1_ret, deployment2_ret]} - -function_ret = dict(FunctionName='unit_test_api_users_post', - Runtime='python2.7', - Role=None, - Handler='handler', - Description='abcdefg', - Timeout=5, - MemorySize=128, - CodeSha256='abcdef', - CodeSize=199, - FunctionArn='arn:lambda:us-east-1:1234:Something', - LastModified='yes') - -method_integration_response_200_ret = dict(responseParameters={'method.response.header.Access-Control-Allow-Origin': '*'}, - responseTemplates={}, - selectionPattern='.*', - statusCode='200') - -method_integration_ret = dict(cacheKeyParameters={}, - cacheNamespace='9waiaz', - credentials='arn:aws:iam::1234:role/apigatewayrole', - httpMethod='POST', - integrationResponses={'200': method_integration_response_200_ret}, - requestParameters={}, - requestTemplates={'application/json': ('#set($inputRoot = $input.path(\'$\'))' - '{' - '"header-params" : {#set ($map = $input.params().header)#foreach( $param in $map.entrySet() )"$param.key" : "$param.value" #if( $foreach.hasNext ), #end#end},' - '"query-params" : {#set ($map = $input.params().querystring)#foreach( $param in $map.entrySet() )"$param.key" : "$param.value" #if( $foreach.hasNext ), #end#end},' - '"path-params" : {#set ($map = $input.params().path)#foreach( $param in $map.entrySet() )"$param.key" : "$param.value" #if( $foreach.hasNext ), #end#end},' - '"body-params" : $input.json(\'$\')' - '}')}, - type='AWS', - uri=('arn:aws:apigateway:us-west-2:' - 'lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:1234567:' - 'function:unit_test_api_api_users_post/invocations')) - -method_response_200_ret = dict(responseModels={'application/json': 'User'}, - responseParameters={'method.response.header.Access-Control-Allow-Origin': False}, - statusCode='200') - -method_ret = dict(apiKeyRequired=False, - authorizationType='None', - httpMethod='POST', - methodIntegration=method_integration_ret, - methodResponses={'200': method_response_200_ret}, - requestModels={'application/json': 'User'}, - requestParameters={}) +method_ret = dict( + apiKeyRequired=False, + authorizationType="None", + httpMethod="POST", + methodIntegration=method_integration_ret, + methodResponses={"200": method_response_200_ret}, + requestModels={"application/json": "User"}, + requestParameters={}, +) throttle_rateLimit = 10.0 -association_stage_1 = {'apiId': 'apiId1', 'stage': 'stage1'} -association_stage_2 = {'apiId': 'apiId1', 'stage': 'stage2'} +association_stage_1 = {"apiId": "apiId1", "stage": "stage1"} +association_stage_2 = {"apiId": "apiId1", "stage": "stage2"} log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -321,9 +388,9 @@ def _has_required_boto(): def _has_required_botocore(): - ''' + """ Returns True/False boolean depending on if botocore supports usage plan - ''' + """ if not HAS_BOTO: return False elif LooseVersion(botocore.__version__) < LooseVersion(required_botocore_version): @@ -333,44 +400,71 @@ def _has_required_botocore(): class TempSwaggerFile(object): - _tmp_swagger_dict = {'info': {'version': '0.0.0', - 'description': 'salt boto apigateway unit test service', - 'title': 'salt boto apigateway unit test service'}, - 'paths': {'/users': {'post': {'responses': { - '200': {'headers': {'Access-Control-Allow-Origin': {'type': 'string'}}, - 'description': 'The username of the new user', - 'schema': {'$ref': '#/definitions/User'}} - }, - 'parameters': [{'in': 'body', - 'description': 'New user details.', - 'name': 'NewUser', - 'schema': {'$ref': '#/definitions/User'}}], - 'produces': ['application/json'], - 'description': 'Creates a new user.', - 'tags': ['Auth'], - 'consumes': ['application/json'], - 'summary': 'Registers a new user'}}}, - 'schemes': ['https'], - 'produces': ['application/json'], - 'basePath': '/api', - 'host': 'rm06h9oac4.execute-api.us-west-2.amazonaws.com', - 'definitions': {'User': {'properties': { - 'username': {'type': 'string', - 'description': 'A unique username for the user'}, - 'password': {'type': 'string', - 'description': 'A password for the new user'} - }}, - 'Error': {'properties': { - 'fields': {'type': 'string'}, - 'message': {'type': 'string'}, - 'code': {'type': 'integer', - 'format': 'int32'} - }}}, - 'swagger': '2.0'} + _tmp_swagger_dict = { + "info": { + "version": "0.0.0", + "description": "salt boto apigateway unit test service", + "title": "salt boto apigateway unit test service", + }, + "paths": { + "/users": { + "post": { + "responses": { + "200": { + "headers": { + "Access-Control-Allow-Origin": {"type": "string"} + }, + "description": "The username of the new user", + "schema": {"$ref": "#/definitions/User"}, + } + }, + "parameters": [ + { + "in": "body", + "description": "New user details.", + "name": "NewUser", + "schema": {"$ref": "#/definitions/User"}, + } + ], + "produces": ["application/json"], + "description": "Creates a new user.", + "tags": ["Auth"], + "consumes": ["application/json"], + "summary": "Registers a new user", + } + } + }, + "schemes": ["https"], + "produces": ["application/json"], + "basePath": "/api", + "host": "rm06h9oac4.execute-api.us-west-2.amazonaws.com", + "definitions": { + "User": { + "properties": { + "username": { + "type": "string", + "description": "A unique username for the user", + }, + "password": { + "type": "string", + "description": "A password for the new user", + }, + } + }, + "Error": { + "properties": { + "fields": {"type": "string"}, + "message": {"type": "string"}, + "code": {"type": "integer", "format": "int32"}, + } + }, + }, + "swagger": "2.0", + } def __enter__(self): - self.swaggerfile = 'temp-swagger-sample.yaml' - with salt.utils.files.fopen(self.swaggerfile, 'w') as fp_: + self.swaggerfile = "temp-swagger-sample.yaml" + with salt.utils.files.fopen(self.swaggerfile, "w") as fp_: salt.utils.yaml.safe_dump(self.swaggerdict, fp_, default_flow_style=False) return self.swaggerfile @@ -381,13 +475,13 @@ class TempSwaggerFile(object): if create_invalid_file: self.swaggerdict = TempSwaggerFile._tmp_swagger_dict.copy() # add an invalid top level key - self.swaggerdict['invalid_key'] = 'invalid' + self.swaggerdict["invalid_key"] = "invalid" # remove one of the required keys 'schemes' - self.swaggerdict.pop('schemes', None) + self.swaggerdict.pop("schemes", None) # set swagger version to an unsupported version 3.0 - self.swaggerdict['swagger'] = '3.0' + self.swaggerdict["swagger"] = "3.0" # missing info object - self.swaggerdict.pop('info', None) + self.swaggerdict.pop("info", None) else: self.swaggerdict = TempSwaggerFile._tmp_swagger_dict @@ -398,7 +492,7 @@ class BotoApiGatewayStateTestCaseBase(TestCase, LoaderModuleMockMixin): @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) @classmethod def tearDownClass(cls): @@ -408,120 +502,142 @@ class BotoApiGatewayStateTestCaseBase(TestCase, LoaderModuleMockMixin): context = {} utils = salt.loader.utils( self.opts, - whitelist=['boto', 'boto3', 'args', 'systemd', 'path', 'platform', 'reg'], - context=context) + whitelist=["boto", "boto3", "args", "systemd", "path", "platform", "reg"], + context=context, + ) serializers = salt.loader.serializers(self.opts) - self.funcs = salt.loader.minion_mods(self.opts, context=context, utils=utils, whitelist=['boto_apigateway']) - self.salt_states = salt.loader.states(opts=self.opts, functions=self.funcs, utils=utils, whitelist=['boto_apigateway'], serializers=serializers) + self.funcs = salt.loader.minion_mods( + self.opts, context=context, utils=utils, whitelist=["boto_apigateway"] + ) + self.salt_states = salt.loader.states( + opts=self.opts, + functions=self.funcs, + utils=utils, + whitelist=["boto_apigateway"], + serializers=serializers, + ) return { boto_apigateway: { - '__opts__': self.opts, - '__utils__': utils, - '__salt__': self.funcs, - '__states__': self.salt_states, - '__serializers__': serializers, + "__opts__": self.opts, + "__utils__": utils, + "__salt__": self.funcs, + "__states__": self.salt_states, + "__serializers__": serializers, } } # Set up MagicMock to replace the boto3 session def setUp(self): - self.addCleanup(delattr, self, 'funcs') - self.addCleanup(delattr, self, 'salt_states') + self.addCleanup(delattr, self, "funcs") + self.addCleanup(delattr, self, "salt_states") # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - patcher = patch('boto3.session.Session') + patcher = patch("boto3.session.Session") self.addCleanup(patcher.stop) mock_session = patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -class BotoApiGatewayTestCase(BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin): - ''' +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) +class BotoApiGatewayTestCase( + BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin +): + """ TestCase for salt.modules.boto_apigateway state.module - ''' + """ def test_present_when_swagger_file_is_invalid(self): - ''' + """ Tests present when the swagger file is invalid. - ''' + """ result = {} with TempSwaggerFile(create_invalid_file=True) as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertFalse(result.get('result', True)) + self.assertFalse(result.get("result", True)) def test_present_when_stage_is_already_at_desired_deployment(self): - ''' + """ Tests scenario where no action will be taken since we're already at desired state - ''' + """ self.conn.get_rest_apis.return_value = apis_ret self.conn.get_deployment.return_value = deployment1_ret self.conn.get_stage.return_value = stage1_deployment1_ret - self.conn.update_stage.side_effect = ClientError(error_content, 'update_stage should not be called') + self.conn.update_stage.side_effect = ClientError( + error_content, "update_stage should not be called" + ) result = {} with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) - self.assertFalse(result.get('abort')) - self.assertTrue(result.get('current')) - self.assertIs(result.get('result'), True) - self.assertNotIn('update_stage should not be called', result.get('comment', '')) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) + self.assertFalse(result.get("abort")) + self.assertTrue(result.get("current")) + self.assertIs(result.get("result"), True) + self.assertNotIn("update_stage should not be called", result.get("comment", "")) - def test_present_when_stage_is_already_at_desired_deployment_and_needs_stage_variables_update(self): - ''' + def test_present_when_stage_is_already_at_desired_deployment_and_needs_stage_variables_update( + self, + ): + """ Tests scenario where the deployment is current except for the need to update stage variables from {} to {'var1':'val1'} - ''' + """ self.conn.get_rest_apis.return_value = apis_ret self.conn.get_deployment.return_value = deployment1_ret self.conn.get_stage.return_value = stage1_deployment1_ret self.conn.update_stage.return_value = stage1_deployment1_vars_ret result = {} with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - stage_variables={'var1': 'val1'}, - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + stage_variables={"var1": "val1"}, + **conn_parameters + ) - self.assertFalse(result.get('abort')) - self.assertTrue(result.get('current')) - self.assertIs(result.get('result'), True) + self.assertFalse(result.get("abort")) + self.assertTrue(result.get("current")) + self.assertIs(result.get("result"), True) def test_present_when_stage_exists_and_is_to_associate_to_existing_deployment(self): - ''' + """ Tests scenario where we merely reassociate a stage to a pre-existing deployments - ''' + """ self.conn.get_rest_apis.return_value = apis_ret self.conn.get_deployment.return_value = deployment2_ret self.conn.get_deployments.return_value = deployments_ret @@ -529,41 +645,46 @@ class BotoApiGatewayTestCase(BotoApiGatewayStateTestCaseBase, BotoApiGatewayTest self.conn.update_stage.return_value = stage1_deployment1_ret # should never get to the following calls - self.conn.create_stage.side_effect = ClientError(error_content, 'create_stage') - self.conn.create_deployment.side_effect = ClientError(error_content, 'create_deployment') + self.conn.create_stage.side_effect = ClientError(error_content, "create_stage") + self.conn.create_deployment.side_effect = ClientError( + error_content, "create_deployment" + ) result = {} with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertTrue(result.get('publish')) - self.assertIs(result.get('result'), True) - self.assertFalse(result.get('abort')) - self.assertTrue(result.get('changes', {}).get('new', [{}])[0]) + self.assertTrue(result.get("publish")) + self.assertIs(result.get("result"), True) + self.assertFalse(result.get("abort")) + self.assertTrue(result.get("changes", {}).get("new", [{}])[0]) def test_present_when_stage_is_to_associate_to_new_deployment(self): - ''' + """ Tests creation of a new api/model/resource given nothing has been created previously - ''' + """ # no api existed self.conn.get_rest_apis.return_value = no_apis_ret # create top level api self.conn.create_rest_api.return_value = api_ret # no models existed in the newly created api - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') + self.conn.get_model.side_effect = ClientError(error_content, "get_model") # create model return values self.conn.create_model.return_value = mock_model_ret # various paths/resources already created self.conn.get_resources.return_value = resources_ret # the following should never be called - self.conn.create_resource.side_effect = ClientError(error_content, 'create_resource') + self.conn.create_resource.side_effect = ClientError( + error_content, "create_resource" + ) # create api method for POST self.conn.put_method.return_value = method_ret @@ -572,286 +693,369 @@ class BotoApiGatewayTestCase(BotoApiGatewayStateTestCaseBase, BotoApiGatewayTest # create api method response for POST/200 self.conn.put_method_response.return_value = method_response_200_ret # create api method integration response for POST - self.conn.put_intgration_response.return_value = method_integration_response_200_ret + self.conn.put_intgration_response.return_value = ( + method_integration_response_200_ret + ) result = {} - with patch.dict(self.funcs, {'boto_lambda.describe_function': MagicMock(return_value={'function': function_ret})}): + with patch.dict( + self.funcs, + { + "boto_lambda.describe_function": MagicMock( + return_value={"function": function_ret} + ) + }, + ): with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertIs(result.get('result'), True) - self.assertIs(result.get('abort'), None) + self.assertIs(result.get("result"), True) + self.assertIs(result.get("abort"), None) - def test_present_when_stage_associating_to_new_deployment_errored_on_api_creation(self): - ''' + def test_present_when_stage_associating_to_new_deployment_errored_on_api_creation( + self, + ): + """ Tests creation of a new api/model/resource given nothing has been created previously, and we failed on creating the top level api object. - ''' + """ # no api existed self.conn.get_rest_apis.return_value = no_apis_ret # create top level api - self.conn.create_rest_api.side_effect = ClientError(error_content, 'create_rest_api') + self.conn.create_rest_api.side_effect = ClientError( + error_content, "create_rest_api" + ) result = {} with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertIs(result.get('abort'), True) - self.assertIs(result.get('result'), False) - self.assertIn('create_rest_api', result.get('comment', '')) + self.assertIs(result.get("abort"), True) + self.assertIs(result.get("result"), False) + self.assertIn("create_rest_api", result.get("comment", "")) - def test_present_when_stage_associating_to_new_deployment_errored_on_model_creation(self): - ''' + def test_present_when_stage_associating_to_new_deployment_errored_on_model_creation( + self, + ): + """ Tests creation of a new api/model/resource given nothing has been created previously, and we failed on creating the models after successful creation of top level api object. - ''' + """ # no api existed self.conn.get_rest_apis.return_value = no_apis_ret # create top level api self.conn.create_rest_api.return_value = api_ret # no models existed in the newly created api - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') + self.conn.get_model.side_effect = ClientError(error_content, "get_model") # create model return error - self.conn.create_model.side_effect = ClientError(error_content, 'create_model') + self.conn.create_model.side_effect = ClientError(error_content, "create_model") result = {} with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertIs(result.get('abort'), True) - self.assertIs(result.get('result'), False) - self.assertIn('create_model', result.get('comment', '')) + self.assertIs(result.get("abort"), True) + self.assertIs(result.get("result"), False) + self.assertIn("create_model", result.get("comment", "")) - def test_present_when_stage_associating_to_new_deployment_errored_on_resource_creation(self): - ''' + def test_present_when_stage_associating_to_new_deployment_errored_on_resource_creation( + self, + ): + """ Tests creation of a new api/model/resource given nothing has been created previously, and we failed on creating the resource (paths) after successful creation of top level api/model objects. - ''' + """ # no api existed self.conn.get_rest_apis.return_value = no_apis_ret # create top level api self.conn.create_rest_api.return_value = api_ret # no models existed in the newly created api - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') + self.conn.get_model.side_effect = ClientError(error_content, "get_model") # create model return values self.conn.create_model.return_value = mock_model_ret # get resources has nothing intiially except to the root path '/' self.conn.get_resources.return_value = root_resources_ret # create resources return error - self.conn.create_resource.side_effect = ClientError(error_content, 'create_resource') + self.conn.create_resource.side_effect = ClientError( + error_content, "create_resource" + ) result = {} with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) - self.assertIs(result.get('abort'), True) - self.assertIs(result.get('result'), False) - self.assertIn('create_resource', result.get('comment', '')) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) + self.assertIs(result.get("abort"), True) + self.assertIs(result.get("result"), False) + self.assertIn("create_resource", result.get("comment", "")) - def test_present_when_stage_associating_to_new_deployment_errored_on_put_method(self): - ''' + def test_present_when_stage_associating_to_new_deployment_errored_on_put_method( + self, + ): + """ Tests creation of a new api/model/resource given nothing has been created previously, and we failed on adding a post method to the resource after successful creation of top level api, model, resource objects. - ''' + """ # no api existed self.conn.get_rest_apis.return_value = no_apis_ret # create top level api self.conn.create_rest_api.return_value = api_ret # no models existed in the newly created api - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') + self.conn.get_model.side_effect = ClientError(error_content, "get_model") # create model return values self.conn.create_model.return_value = mock_model_ret # various paths/resources already created self.conn.get_resources.return_value = resources_ret # the following should never be called - self.conn.create_resource.side_effect = ClientError(error_content, 'create_resource') + self.conn.create_resource.side_effect = ClientError( + error_content, "create_resource" + ) # create api method for POST - self.conn.put_method.side_effect = ClientError(error_content, 'put_method') + self.conn.put_method.side_effect = ClientError(error_content, "put_method") result = {} - with patch.dict(self.funcs, {'boto_lambda.describe_function': MagicMock(return_value={'function': function_ret})}): + with patch.dict( + self.funcs, + { + "boto_lambda.describe_function": MagicMock( + return_value={"function": function_ret} + ) + }, + ): with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertIs(result.get('abort'), True) - self.assertIs(result.get('result'), False) - self.assertIn('put_method', result.get('comment', '')) + self.assertIs(result.get("abort"), True) + self.assertIs(result.get("result"), False) + self.assertIn("put_method", result.get("comment", "")) - def test_present_when_stage_associating_to_new_deployment_errored_on_lambda_function_lookup(self): - ''' + def test_present_when_stage_associating_to_new_deployment_errored_on_lambda_function_lookup( + self, + ): + """ Tests creation of a new api/model/resource given nothing has been created previously, and we failed on adding a post method due to a lamda look up failure after successful creation of top level api, model, resource objects. - ''' + """ # no api existed self.conn.get_rest_apis.return_value = no_apis_ret # create top level api self.conn.create_rest_api.return_value = api_ret # no models existed in the newly created api - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') + self.conn.get_model.side_effect = ClientError(error_content, "get_model") # create model return values self.conn.create_model.return_value = mock_model_ret # various paths/resources already created self.conn.get_resources.return_value = resources_ret # the following should never be called - self.conn.create_resource.side_effect = ClientError(error_content, 'create_resource') + self.conn.create_resource.side_effect = ClientError( + error_content, "create_resource" + ) # create api method for POST self.conn.put_method.return_value = method_ret # create api method integration for POST - self.conn.put_integration.side_effect = ClientError(error_content, 'put_integration should not be invoked') + self.conn.put_integration.side_effect = ClientError( + error_content, "put_integration should not be invoked" + ) result = {} - with patch.dict(self.funcs, {'boto_lambda.describe_function': MagicMock(return_value={'error': 'no such lambda'})}): + with patch.dict( + self.funcs, + { + "boto_lambda.describe_function": MagicMock( + return_value={"error": "no such lambda"} + ) + }, + ): with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertIs(result.get('result'), False) - self.assertNotIn('put_integration should not be invoked', result.get('comment', '')) - self.assertIn('not find lambda function', result.get('comment', '')) + self.assertIs(result.get("result"), False) + self.assertNotIn( + "put_integration should not be invoked", result.get("comment", "") + ) + self.assertIn("not find lambda function", result.get("comment", "")) - def test_present_when_stage_associating_to_new_deployment_errored_on_put_integration(self): - ''' + def test_present_when_stage_associating_to_new_deployment_errored_on_put_integration( + self, + ): + """ Tests creation of a new api/model/resource given nothing has been created previously, and we failed on adding an integration for the post method to the resource after successful creation of top level api, model, resource objects. - ''' + """ # no api existed self.conn.get_rest_apis.return_value = no_apis_ret # create top level api self.conn.create_rest_api.return_value = api_ret # no models existed in the newly created api - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') + self.conn.get_model.side_effect = ClientError(error_content, "get_model") # create model return values self.conn.create_model.return_value = mock_model_ret # various paths/resources already created self.conn.get_resources.return_value = resources_ret # the following should never be called - self.conn.create_resource.side_effect = ClientError(error_content, 'create_resource') + self.conn.create_resource.side_effect = ClientError( + error_content, "create_resource" + ) # create api method for POST self.conn.put_method.return_value = method_ret # create api method integration for POST - self.conn.put_integration.side_effect = ClientError(error_content, 'put_integration') + self.conn.put_integration.side_effect = ClientError( + error_content, "put_integration" + ) result = {} - with patch.dict(self.funcs, {'boto_lambda.describe_function': MagicMock(return_value={'function': function_ret})}): + with patch.dict( + self.funcs, + { + "boto_lambda.describe_function": MagicMock( + return_value={"function": function_ret} + ) + }, + ): with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertIs(result.get('abort'), True) - self.assertIs(result.get('result'), False) - self.assertIn('put_integration', result.get('comment', '')) + self.assertIs(result.get("abort"), True) + self.assertIs(result.get("result"), False) + self.assertIn("put_integration", result.get("comment", "")) - def test_present_when_stage_associating_to_new_deployment_errored_on_put_method_response(self): - ''' + def test_present_when_stage_associating_to_new_deployment_errored_on_put_method_response( + self, + ): + """ Tests creation of a new api/model/resource given nothing has been created previously, and we failed on adding a method response for the post method to the resource after successful creation of top level api, model, resource objects. - ''' + """ # no api existed self.conn.get_rest_apis.return_value = no_apis_ret # create top level api self.conn.create_rest_api.return_value = api_ret # no models existed in the newly created api - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') + self.conn.get_model.side_effect = ClientError(error_content, "get_model") # create model return values self.conn.create_model.return_value = mock_model_ret # various paths/resources already created self.conn.get_resources.return_value = resources_ret # the following should never be called - self.conn.create_resource.side_effect = ClientError(error_content, 'create_resource') + self.conn.create_resource.side_effect = ClientError( + error_content, "create_resource" + ) # create api method for POST self.conn.put_method.return_value = method_ret # create api method integration for POST self.conn.put_integration.return_value = method_integration_ret # create api method response for POST/200 - self.conn.put_method_response.side_effect = ClientError(error_content, 'put_method_response') + self.conn.put_method_response.side_effect = ClientError( + error_content, "put_method_response" + ) result = {} - with patch.dict(self.funcs, {'boto_lambda.describe_function': MagicMock(return_value={'function': function_ret})}): + with patch.dict( + self.funcs, + { + "boto_lambda.describe_function": MagicMock( + return_value={"function": function_ret} + ) + }, + ): with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertIs(result.get('abort'), True) - self.assertIs(result.get('result'), False) - self.assertIn('put_method_response', result.get('comment', '')) + self.assertIs(result.get("abort"), True) + self.assertIs(result.get("result"), False) + self.assertIn("put_method_response", result.get("comment", "")) - def test_present_when_stage_associating_to_new_deployment_errored_on_put_integration_response(self): - ''' + def test_present_when_stage_associating_to_new_deployment_errored_on_put_integration_response( + self, + ): + """ Tests creation of a new api/model/resource given nothing has been created previously, and we failed on adding an integration response for the post method to the resource after successful creation of top level api, model, resource objects. - ''' + """ # no api existed self.conn.get_rest_apis.return_value = no_apis_ret # create top level api self.conn.create_rest_api.return_value = api_ret # no models existed in the newly created api - self.conn.get_model.side_effect = ClientError(error_content, 'get_model') + self.conn.get_model.side_effect = ClientError(error_content, "get_model") # create model return values self.conn.create_model.return_value = mock_model_ret # various paths/resources already created self.conn.get_resources.return_value = resources_ret # the following should never be called - self.conn.create_resource.side_effect = ClientError(error_content, 'create_resource') + self.conn.create_resource.side_effect = ClientError( + error_content, "create_resource" + ) # create api method for POST self.conn.put_method.return_value = method_ret @@ -860,700 +1064,1228 @@ class BotoApiGatewayTestCase(BotoApiGatewayStateTestCaseBase, BotoApiGatewayTest # create api method response for POST/200 self.conn.put_method_response.return_value = method_response_200_ret # create api method integration response for POST - self.conn.put_integration_response.side_effect = ClientError(error_content, 'put_integration_response') + self.conn.put_integration_response.side_effect = ClientError( + error_content, "put_integration_response" + ) result = {} - with patch.dict(self.funcs, {'boto_lambda.describe_function': MagicMock(return_value={'function': function_ret})}): + with patch.dict( + self.funcs, + { + "boto_lambda.describe_function": MagicMock( + return_value={"function": function_ret} + ) + }, + ): with TempSwaggerFile() as swagger_file: - result = self.salt_states['boto_apigateway.present']( - 'api present', - 'unit test api', - swagger_file, - 'test', - False, - 'arn:aws:iam::1234:role/apigatewayrole', - **conn_parameters) + result = self.salt_states["boto_apigateway.present"]( + "api present", + "unit test api", + swagger_file, + "test", + False, + "arn:aws:iam::1234:role/apigatewayrole", + **conn_parameters + ) - self.assertIs(result.get('abort'), True) - self.assertIs(result.get('result'), False) - self.assertIn('put_integration_response', result.get('comment', '')) + self.assertIs(result.get("abort"), True) + self.assertIs(result.get("result"), False) + self.assertIn("put_integration_response", result.get("comment", "")) def test_absent_when_rest_api_does_not_exist(self): - ''' + """ Tests scenario where the given api_name does not exist, absent state should return True with no changes. - ''' + """ self.conn.get_rest_apis.return_value = apis_ret - self.conn.get_stage.side_effect = ClientError(error_content, 'get_stage should not be called') + self.conn.get_stage.side_effect = ClientError( + error_content, "get_stage should not be called" + ) - result = self.salt_states['boto_apigateway.absent']( - 'api present', - 'no_such_rest_api', - 'no_such_stage', - nuke_api=False, - **conn_parameters) + result = self.salt_states["boto_apigateway.absent"]( + "api present", + "no_such_rest_api", + "no_such_stage", + nuke_api=False, + **conn_parameters + ) - self.assertIs(result.get('result'), True) - self.assertNotIn('get_stage should not be called', result.get('comment', '')) - self.assertEqual(result.get('changes'), {}) + self.assertIs(result.get("result"), True) + self.assertNotIn("get_stage should not be called", result.get("comment", "")) + self.assertEqual(result.get("changes"), {}) def test_absent_when_stage_is_invalid(self): - ''' + """ Tests scenario where the stagename doesn't exist - ''' + """ self.conn.get_rest_apis.return_value = apis_ret self.conn.get_stage.return_value = stage1_deployment1_ret - self.conn.delete_stage.side_effect = ClientError(error_content, 'delete_stage') + self.conn.delete_stage.side_effect = ClientError(error_content, "delete_stage") - result = self.salt_states['boto_apigateway.absent']( - 'api present', - 'unit test api', - 'no_such_stage', - nuke_api=False, - **conn_parameters) + result = self.salt_states["boto_apigateway.absent"]( + "api present", + "unit test api", + "no_such_stage", + nuke_api=False, + **conn_parameters + ) - self.assertTrue(result.get('abort', False)) + self.assertTrue(result.get("abort", False)) - def test_absent_when_stage_is_valid_and_only_one_stage_is_associated_to_deployment(self): - ''' + def test_absent_when_stage_is_valid_and_only_one_stage_is_associated_to_deployment( + self, + ): + """ Tests scenario where the stagename exists - ''' + """ self.conn.get_rest_apis.return_value = apis_ret self.conn.get_stage.return_value = stage1_deployment1_ret - self.conn.delete_stage.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + self.conn.delete_stage.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } self.conn.get_stages.return_value = no_stages_ret - self.conn.delete_deployment.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + self.conn.delete_deployment.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } - result = self.salt_states['boto_apigateway.absent']( - 'api present', - 'unit test api', - 'test', - nuke_api=False, - **conn_parameters) + result = self.salt_states["boto_apigateway.absent"]( + "api present", "unit test api", "test", nuke_api=False, **conn_parameters + ) - self.assertTrue(result.get('result', False)) + self.assertTrue(result.get("result", False)) - def test_absent_when_stage_is_valid_and_two_stages_are_associated_to_deployment(self): - ''' + def test_absent_when_stage_is_valid_and_two_stages_are_associated_to_deployment( + self, + ): + """ Tests scenario where the stagename exists and there are two stages associated with same deployment - ''' + """ self.conn.get_rest_apis.return_value = apis_ret self.conn.get_stage.return_value = stage1_deployment1_ret - self.conn.delete_stage.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + self.conn.delete_stage.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } self.conn.get_stages.return_value = stages_stage2_ret - result = self.salt_states['boto_apigateway.absent']( - 'api present', - 'unit test api', - 'test', - nuke_api=False, - **conn_parameters) + result = self.salt_states["boto_apigateway.absent"]( + "api present", "unit test api", "test", nuke_api=False, **conn_parameters + ) - self.assertTrue(result.get('result', False)) + self.assertTrue(result.get("result", False)) - def test_absent_when_failing_to_delete_a_deployment_no_longer_associated_with_any_stages(self): - ''' + def test_absent_when_failing_to_delete_a_deployment_no_longer_associated_with_any_stages( + self, + ): + """ Tests scenario where stagename exists and is deleted, but a failure occurs when trying to delete the deployment which is no longer associated to any other stages - ''' + """ self.conn.get_rest_apis.return_value = apis_ret self.conn.get_stage.return_value = stage1_deployment1_ret - self.conn.delete_stage.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + self.conn.delete_stage.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } self.conn.get_stages.return_value = no_stages_ret - self.conn.delete_deployment.side_effect = ClientError(error_content, 'delete_deployment') + self.conn.delete_deployment.side_effect = ClientError( + error_content, "delete_deployment" + ) - result = self.salt_states['boto_apigateway.absent']( - 'api present', - 'unit test api', - 'test', - nuke_api=False, - **conn_parameters) + result = self.salt_states["boto_apigateway.absent"]( + "api present", "unit test api", "test", nuke_api=False, **conn_parameters + ) - self.assertTrue(result.get('abort', False)) + self.assertTrue(result.get("abort", False)) def test_absent_when_nuke_api_and_no_more_stages_deployments_remain(self): - ''' + """ Tests scenario where the stagename exists and there are no stages associated with same deployment, the api would be deleted. - ''' + """ self.conn.get_rest_apis.return_value = apis_ret self.conn.get_stage.return_value = stage1_deployment1_ret - self.conn.delete_stage.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + self.conn.delete_stage.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } self.conn.get_stages.return_value = no_stages_ret self.conn.get_deployments.return_value = deployments_ret - self.conn.delete_rest_api.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + self.conn.delete_rest_api.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } - result = self.salt_states['boto_apigateway.absent']( - 'api present', - 'unit test api', - 'test', - nuke_api=True, - **conn_parameters) + result = self.salt_states["boto_apigateway.absent"]( + "api present", "unit test api", "test", nuke_api=True, **conn_parameters + ) - self.assertIs(result.get('result'), True) - self.assertIsNot(result.get('abort'), True) - self.assertIs(result.get('changes', {}).get('new', [{}])[0].get('delete_api', {}).get('deleted'), True) + self.assertIs(result.get("result"), True) + self.assertIsNot(result.get("abort"), True) + self.assertIs( + result.get("changes", {}) + .get("new", [{}])[0] + .get("delete_api", {}) + .get("deleted"), + True, + ) def test_absent_when_nuke_api_and_other_stages_deployments_exist(self): - ''' + """ Tests scenario where the stagename exists and there are two stages associated with same deployment, though nuke_api is requested, due to remaining deployments, we will not call the delete_rest_api call. - ''' + """ self.conn.get_rest_apis.return_value = apis_ret self.conn.get_stage.return_value = stage1_deployment1_ret - self.conn.delete_stage.return_value = {'ResponseMetadata': {'HTTPStatusCode': 200, 'RequestId': '2d31072c-9d15-11e5-9977-6d9fcfda9c0a'}} + self.conn.delete_stage.return_value = { + "ResponseMetadata": { + "HTTPStatusCode": 200, + "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a", + } + } self.conn.get_stages.return_value = stages_stage2_ret self.conn.get_deployments.return_value = deployments_ret - self.conn.delete_rest_api.side_effect = ClientError(error_content, 'unexpected_api_delete') + self.conn.delete_rest_api.side_effect = ClientError( + error_content, "unexpected_api_delete" + ) - result = self.salt_states['boto_apigateway.absent']( - 'api present', - 'unit test api', - 'test', - nuke_api=True, - **conn_parameters) + result = self.salt_states["boto_apigateway.absent"]( + "api present", "unit test api", "test", nuke_api=True, **conn_parameters + ) - self.assertIs(result.get('result'), True) - self.assertIsNot(result.get('abort'), True) + self.assertIs(result.get("result"), True) + self.assertIsNot(result.get("abort"), True) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(_has_required_botocore() is False, - 'The botocore module must be greater than' - ' or equal to version {0}'.format(required_botocore_version)) -class BotoApiGatewayUsagePlanTestCase(BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin): - ''' +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) +@skipIf( + _has_required_botocore() is False, + "The botocore module must be greater than" + " or equal to version {0}".format(required_botocore_version), +) +class BotoApiGatewayUsagePlanTestCase( + BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin +): + """ TestCase for salt.modules.boto_apigateway state.module, usage_plans portion - ''' + """ def test_usage_plan_present_if_describe_fails(self, *args): - ''' + """ Tests correct error processing for describe_usage_plan failure - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'error': 'error'})}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"error": "error"} + ) + }, + ): + result = boto_apigateway.usage_plan_present( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Failed to describe existing usage plans') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "Failed to describe existing usage plans" + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) - def test_usage_plan_present_if_there_is_no_such_plan_and_test_option_is_set(self, *args): - ''' + def test_usage_plan_present_if_there_is_no_such_plan_and_test_option_is_set( + self, *args + ): + """ TestCse for salt.modules.boto_apigateway state.module, checking that if __opts__['test'] is set and usage plan does not exist, correct diagnostic will be returned - ''' - with patch.dict(boto_apigateway.__opts__, {'test': True}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': []})}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', **conn_parameters) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'a new usage plan plan_name would be created') - self.assertIn('result', result) - self.assertEqual(result['result'], None) + """ + with patch.dict(boto_apigateway.__opts__, {"test": True}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": []} + ) + }, + ): + result = boto_apigateway.usage_plan_present( + "name", "plan_name", **conn_parameters + ) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "a new usage plan plan_name would be created" + ) + self.assertIn("result", result) + self.assertEqual(result["result"], None) def test_usage_plan_present_if_create_usage_plan_fails(self, *args): - ''' + """ Tests behavior for the case when creating a new usage plan fails - ''' - with patch.dict(boto_apigateway.__opts__, {'test': False}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': []}), - 'boto_apigateway.create_usage_plan': MagicMock(return_value={'error': 'error'})}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', **conn_parameters) + """ + with patch.dict(boto_apigateway.__opts__, {"test": False}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": []} + ), + "boto_apigateway.create_usage_plan": MagicMock( + return_value={"error": "error"} + ), + }, + ): + result = boto_apigateway.usage_plan_present( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Failed to create a usage plan plan_name, error') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "Failed to create a usage plan plan_name, error" + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_present_if_plan_is_there_and_needs_no_updates(self, *args): - ''' + """ Tests behavior for the case when plan is present and needs no updates - ''' - with patch.dict(boto_apigateway.__opts__, {'test': False}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{ - 'id': 'planid', - 'name': 'planname' - }]}), - 'boto_apigateway.update_usage_plan': MagicMock()}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', **conn_parameters) + """ + with patch.dict(boto_apigateway.__opts__, {"test": False}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": [{"id": "planid", "name": "planname"}]} + ), + "boto_apigateway.update_usage_plan": MagicMock(), + }, + ): + result = boto_apigateway.usage_plan_present( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'usage plan plan_name is already in a correct state') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], + "usage plan plan_name is already in a correct state", + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) - self.assertTrue(boto_apigateway.__salt__['boto_apigateway.update_usage_plan'].call_count == 0) + self.assertTrue( + boto_apigateway.__salt__[ + "boto_apigateway.update_usage_plan" + ].call_count + == 0 + ) - def test_usage_plan_present_if_plan_is_there_and_needs_updates_but_test_is_set(self, *args): - ''' + def test_usage_plan_present_if_plan_is_there_and_needs_updates_but_test_is_set( + self, *args + ): + """ Tests behavior when usage plan needs to be updated by tests option is set - ''' - with patch.dict(boto_apigateway.__opts__, {'test': True}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{ - 'id': 'planid', - 'name': 'planname', - 'throttle': {'rateLimit': 10.0} - }]}), - 'boto_apigateway.update_usage_plan': MagicMock()}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', **conn_parameters) + """ + with patch.dict(boto_apigateway.__opts__, {"test": True}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={ + "plans": [ + { + "id": "planid", + "name": "planname", + "throttle": {"rateLimit": 10.0}, + } + ] + } + ), + "boto_apigateway.update_usage_plan": MagicMock(), + }, + ): + result = boto_apigateway.usage_plan_present( + "name", "plan_name", **conn_parameters + ) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'a new usage plan plan_name would be updated') - self.assertIn('result', result) - self.assertEqual(result['result'], None) - self.assertTrue(boto_apigateway.__salt__['boto_apigateway.update_usage_plan'].call_count == 0) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "a new usage plan plan_name would be updated" + ) + self.assertIn("result", result) + self.assertEqual(result["result"], None) + self.assertTrue( + boto_apigateway.__salt__[ + "boto_apigateway.update_usage_plan" + ].call_count + == 0 + ) - def test_usage_plan_present_if_plan_is_there_and_needs_updates_but_update_fails(self, *args): - ''' + def test_usage_plan_present_if_plan_is_there_and_needs_updates_but_update_fails( + self, *args + ): + """ Tests error processing for the case when updating an existing usage plan fails - ''' - with patch.dict(boto_apigateway.__opts__, {'test': False}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{ - 'id': 'planid', - 'name': 'planname', - 'throttle': {'rateLimit': 10.0} - }]}), - 'boto_apigateway.update_usage_plan': MagicMock(return_value={'error': 'error'})}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', **conn_parameters) + """ + with patch.dict(boto_apigateway.__opts__, {"test": False}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={ + "plans": [ + { + "id": "planid", + "name": "planname", + "throttle": {"rateLimit": 10.0}, + } + ] + } + ), + "boto_apigateway.update_usage_plan": MagicMock( + return_value={"error": "error"} + ), + }, + ): + result = boto_apigateway.usage_plan_present( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Failed to update a usage plan plan_name, error') + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "Failed to update a usage plan plan_name, error" + ) def test_usage_plan_present_if_plan_has_been_created(self, *args): - ''' + """ Tests successful case for creating a new usage plan - ''' - with patch.dict(boto_apigateway.__opts__, {'test': False}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=[{'plans': []}, {'plans': [{'id': 'id'}]}]), - 'boto_apigateway.create_usage_plan': MagicMock(return_value={'created': True})}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', **conn_parameters) + """ + with patch.dict(boto_apigateway.__opts__, {"test": False}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=[{"plans": []}, {"plans": [{"id": "id"}]}] + ), + "boto_apigateway.create_usage_plan": MagicMock( + return_value={"created": True} + ), + }, + ): + result = boto_apigateway.usage_plan_present( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'A new usage plan plan_name has been created') - self.assertEqual(result['changes']['old'], {'plan': None}) - self.assertEqual(result['changes']['new'], {'plan': {'id': 'id'}}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "A new usage plan plan_name has been created" + ) + self.assertEqual(result["changes"]["old"], {"plan": None}) + self.assertEqual(result["changes"]["new"], {"plan": {"id": "id"}}) def test_usage_plan_present_if_plan_has_been_updated(self, *args): - ''' + """ Tests successful case for updating a usage plan - ''' - with patch.dict(boto_apigateway.__opts__, {'test': False}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=[{'plans': [{'id': 'id'}]}, - {'plans': [{'id': 'id', - 'throttle': {'rateLimit': throttle_rateLimit}}]}]), - 'boto_apigateway.update_usage_plan': MagicMock(return_value={'updated': True})}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', throttle={'rateLimit': throttle_rateLimit}, **conn_parameters) + """ + with patch.dict(boto_apigateway.__opts__, {"test": False}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=[ + {"plans": [{"id": "id"}]}, + { + "plans": [ + { + "id": "id", + "throttle": {"rateLimit": throttle_rateLimit}, + } + ] + }, + ] + ), + "boto_apigateway.update_usage_plan": MagicMock( + return_value={"updated": True} + ), + }, + ): + result = boto_apigateway.usage_plan_present( + "name", + "plan_name", + throttle={"rateLimit": throttle_rateLimit}, + **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'usage plan plan_name has been updated') - self.assertEqual(result['changes']['old'], {'plan': {'id': 'id'}}) - self.assertEqual(result['changes']['new'], {'plan': {'id': 'id', 'throttle': {'rateLimit': throttle_rateLimit}}}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "usage plan plan_name has been updated" + ) + self.assertEqual(result["changes"]["old"], {"plan": {"id": "id"}}) + self.assertEqual( + result["changes"]["new"], + { + "plan": { + "id": "id", + "throttle": {"rateLimit": throttle_rateLimit}, + } + }, + ) def test_usage_plan_present_if_ValueError_is_raised(self, *args): - ''' + """ Tests error processing for the case when ValueError is raised when creating a usage plan - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=ValueError('error'))}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', throttle={'rateLimit': throttle_rateLimit}, **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=ValueError("error") + ) + }, + ): + result = boto_apigateway.usage_plan_present( + "name", + "plan_name", + throttle={"rateLimit": throttle_rateLimit}, + **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], repr(('error',))) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], repr(("error",))) def test_usage_plan_present_if_IOError_is_raised(self, *args): - ''' + """ Tests error processing for the case when IOError is raised when creating a usage plan - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=IOError('error'))}): - result = boto_apigateway.usage_plan_present('name', 'plan_name', throttle={'rateLimit': throttle_rateLimit}, **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=IOError("error") + ) + }, + ): + result = boto_apigateway.usage_plan_present( + "name", + "plan_name", + throttle={"rateLimit": throttle_rateLimit}, + **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], repr(('error',))) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], repr(("error",))) def test_usage_plan_absent_if_describe_fails(self, *args): - ''' + """ Tests correct error processing for describe_usage_plan failure - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'error': 'error'})}): + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"error": "error"} + ) + }, + ): result = {} - result = boto_apigateway.usage_plan_absent('name', 'plan_name', **conn_parameters) + result = boto_apigateway.usage_plan_absent( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Failed to describe existing usage plans') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "Failed to describe existing usage plans" + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_absent_if_plan_is_not_present(self, *args): - ''' + """ Tests behavior for the case when the plan that needs to be absent does not exist - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': []})}): + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": []} + ) + }, + ): result = {} - result = boto_apigateway.usage_plan_absent('name', 'plan_name', **conn_parameters) + result = boto_apigateway.usage_plan_absent( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Usage plan plan_name does not exist already') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "Usage plan plan_name does not exist already" + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_absent_if_plan_is_present_but_test_option_is_set(self, *args): - ''' + """ Tests behavior for the case when usage plan needs to be deleted by tests option is set - ''' - with patch.dict(boto_apigateway.__opts__, {'test': True}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id'}]})}): + """ + with patch.dict(boto_apigateway.__opts__, {"test": True}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": [{"id": "id"}]} + ) + }, + ): result = {} - result = boto_apigateway.usage_plan_absent('name', 'plan_name', **conn_parameters) + result = boto_apigateway.usage_plan_absent( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], None) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Usage plan plan_name exists and would be deleted') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], None) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], + "Usage plan plan_name exists and would be deleted", + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_absent_if_plan_is_present_but_delete_fails(self, *args): - ''' + """ Tests correct error processing when deleting a usage plan fails - ''' - with patch.dict(boto_apigateway.__opts__, {'test': False}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id'}]}), - 'boto_apigateway.delete_usage_plan': MagicMock(return_value={'error': 'error'})}): - result = boto_apigateway.usage_plan_absent('name', 'plan_name', **conn_parameters) + """ + with patch.dict(boto_apigateway.__opts__, {"test": False}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": [{"id": "id"}]} + ), + "boto_apigateway.delete_usage_plan": MagicMock( + return_value={"error": "error"} + ), + }, + ): + result = boto_apigateway.usage_plan_absent( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Failed to delete usage plan plan_name, ' + repr({'error': 'error'})) - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], + "Failed to delete usage plan plan_name, " + + repr({"error": "error"}), + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_absent_if_plan_has_been_deleted(self, *args): - ''' + """ Tests successful case for deleting a usage plan - ''' - with patch.dict(boto_apigateway.__opts__, {'test': False}): - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id'}]}), - 'boto_apigateway.delete_usage_plan': MagicMock(return_value={'deleted': True})}): - result = boto_apigateway.usage_plan_absent('name', 'plan_name', **conn_parameters) + """ + with patch.dict(boto_apigateway.__opts__, {"test": False}): + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": [{"id": "id"}]} + ), + "boto_apigateway.delete_usage_plan": MagicMock( + return_value={"deleted": True} + ), + }, + ): + result = boto_apigateway.usage_plan_absent( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Usage plan plan_name has been deleted') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {'new': {'plan': None}, 'old': {'plan': {'id': 'id'}}}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "Usage plan plan_name has been deleted" + ) + self.assertIn("changes", result) + self.assertEqual( + result["changes"], + {"new": {"plan": None}, "old": {"plan": {"id": "id"}}}, + ) def test_usage_plan_absent_if_ValueError_is_raised(self, *args): - ''' + """ Tests correct error processing for the case when ValueError is raised when deleting a usage plan - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=ValueError('error'))}): - result = boto_apigateway.usage_plan_absent('name', 'plan_name', **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=ValueError("error") + ) + }, + ): + result = boto_apigateway.usage_plan_absent( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], repr(('error',))) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], repr(("error",))) def test_usage_plan_absent_if_IOError_is_raised(self, *args): - ''' + """ Tests correct error processing for the case when IOError is raised when deleting a usage plan - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=IOError('error'))}): - result = boto_apigateway.usage_plan_absent('name', 'plan_name', **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=IOError("error") + ) + }, + ): + result = boto_apigateway.usage_plan_absent( + "name", "plan_name", **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], repr(('error',))) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], repr(("error",))) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -@skipIf(_has_required_botocore() is False, - 'The botocore module must be greater than' - ' or equal to version {0}'.format(required_botocore_version)) -class BotoApiGatewayUsagePlanAssociationTestCase(BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin): - ''' +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) +@skipIf( + _has_required_botocore() is False, + "The botocore module must be greater than" + " or equal to version {0}".format(required_botocore_version), +) +class BotoApiGatewayUsagePlanAssociationTestCase( + BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin +): + """ TestCase for salt.modules.boto_apigateway state.module, usage_plans_association portion - ''' + """ def test_usage_plan_association_present_if_describe_fails(self, *args): - ''' + """ Tests correct error processing for describe_usage_plan failure - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'error': 'error'})}): - result = boto_apigateway.usage_plan_association_present('name', 'plan_name', [association_stage_1], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"error": "error"} + ) + }, + ): + result = boto_apigateway.usage_plan_association_present( + "name", "plan_name", [association_stage_1], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Failed to describe existing usage plans') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "Failed to describe existing usage plans" + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_present_if_plan_is_not_present(self, *args): - ''' + """ Tests correct error processing if a plan for which association has been requested is not present - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': []})}): - result = boto_apigateway.usage_plan_association_present('name', 'plan_name', [association_stage_1], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": []} + ) + }, + ): + result = boto_apigateway.usage_plan_association_present( + "name", "plan_name", [association_stage_1], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Usage plan plan_name does not exist') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], "Usage plan plan_name does not exist") + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) - def test_usage_plan_association_present_if_multiple_plans_with_the_same_name_exist(self, *args): - ''' + def test_usage_plan_association_present_if_multiple_plans_with_the_same_name_exist( + self, *args + ): + """ Tests correct error processing for the case when multiple plans with the same name exist - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id1'}, - {'id': 'id2'}]})}): - result = boto_apigateway.usage_plan_association_present('name', 'plan_name', [association_stage_1], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": [{"id": "id1"}, {"id": "id2"}]} + ) + }, + ): + result = boto_apigateway.usage_plan_association_present( + "name", "plan_name", [association_stage_1], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'There are multiple usage plans with the same name - it is not supported') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], + "There are multiple usage plans with the same name - it is not supported", + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_present_if_association_already_exists(self, *args): - ''' + """ Tests the behavior for the case when requested association is already present - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id1', - 'apiStages': - [association_stage_1]}]})}): - result = boto_apigateway.usage_plan_association_present('name', 'plan_name', [association_stage_1], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={ + "plans": [{"id": "id1", "apiStages": [association_stage_1]}] + } + ) + }, + ): + result = boto_apigateway.usage_plan_association_present( + "name", "plan_name", [association_stage_1], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Usage plan is already asssociated to all api stages') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "Usage plan is already asssociated to all api stages" + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_present_if_update_fails(self, *args): - ''' + """ Tests correct error processing for the case when adding associations fails - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id1', - 'apiStages': - [association_stage_1]}]}), - 'boto_apigateway.attach_usage_plan_to_apis': MagicMock(return_value={'error': 'error'})}): - result = boto_apigateway.usage_plan_association_present('name', 'plan_name', [association_stage_2], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={ + "plans": [{"id": "id1", "apiStages": [association_stage_1]}] + } + ), + "boto_apigateway.attach_usage_plan_to_apis": MagicMock( + return_value={"error": "error"} + ), + }, + ): + result = boto_apigateway.usage_plan_association_present( + "name", "plan_name", [association_stage_2], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertTrue(result['comment'].startswith('Failed to associate a usage plan')) - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertTrue( + result["comment"].startswith("Failed to associate a usage plan") + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_present_success(self, *args): - ''' + """ Tests successful case for adding usage plan associations to a given api stage - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id1', - 'apiStages': - [association_stage_1]}]}), - 'boto_apigateway.attach_usage_plan_to_apis': MagicMock(return_value={'result': {'apiStages': [association_stage_1, - association_stage_2]}})}): - result = boto_apigateway.usage_plan_association_present('name', 'plan_name', [association_stage_2], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={ + "plans": [{"id": "id1", "apiStages": [association_stage_1]}] + } + ), + "boto_apigateway.attach_usage_plan_to_apis": MagicMock( + return_value={ + "result": { + "apiStages": [association_stage_1, association_stage_2] + } + } + ), + }, + ): + result = boto_apigateway.usage_plan_association_present( + "name", "plan_name", [association_stage_2], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'successfully associated usage plan to apis') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {'new': [association_stage_1, association_stage_2], 'old': [association_stage_1]}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "successfully associated usage plan to apis" + ) + self.assertIn("changes", result) + self.assertEqual( + result["changes"], + { + "new": [association_stage_1, association_stage_2], + "old": [association_stage_1], + }, + ) def test_usage_plan_association_present_if_value_error_is_thrown(self, *args): - ''' + """ Tests correct error processing for the case when IOError is raised while trying to set usage plan associations - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=ValueError('error'))}): - result = boto_apigateway.usage_plan_association_present('name', 'plan_name', [], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=ValueError("error") + ) + }, + ): + result = boto_apigateway.usage_plan_association_present( + "name", "plan_name", [], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], repr(('error',))) - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], repr(("error",))) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_present_if_io_error_is_thrown(self, *args): - ''' + """ Tests correct error processing for the case when IOError is raised while trying to set usage plan associations - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=IOError('error'))}): - result = boto_apigateway.usage_plan_association_present('name', 'plan_name', [], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=IOError("error") + ) + }, + ): + result = boto_apigateway.usage_plan_association_present( + "name", "plan_name", [], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], repr(('error',))) - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], repr(("error",))) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_absent_if_describe_fails(self, *args): - ''' + """ Tests correct error processing for describe_usage_plan failure - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'error': 'error'})}): - result = boto_apigateway.usage_plan_association_absent('name', 'plan_name', [association_stage_1], **conn_parameters) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Failed to describe existing usage plans') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"error": "error"} + ) + }, + ): + result = boto_apigateway.usage_plan_association_absent( + "name", "plan_name", [association_stage_1], **conn_parameters + ) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "Failed to describe existing usage plans" + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_absent_if_plan_is_not_present(self, *args): - ''' + """ Tests error processing for the case when plan for which associations need to be modified is not present - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': []})}): - result = boto_apigateway.usage_plan_association_absent('name', 'plan_name', [association_stage_1], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": []} + ) + }, + ): + result = boto_apigateway.usage_plan_association_absent( + "name", "plan_name", [association_stage_1], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Usage plan plan_name does not exist') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], "Usage plan plan_name does not exist") + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) - def test_usage_plan_association_absent_if_multiple_plans_with_the_same_name_exist(self, *args): - ''' + def test_usage_plan_association_absent_if_multiple_plans_with_the_same_name_exist( + self, *args + ): + """ Tests the case when there are multiple plans with the same name but different Ids - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id1'}, - {'id': 'id2'}]})}): - result = boto_apigateway.usage_plan_association_absent('name', 'plan_name', [association_stage_1], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": [{"id": "id1"}, {"id": "id2"}]} + ) + }, + ): + result = boto_apigateway.usage_plan_association_absent( + "name", "plan_name", [association_stage_1], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'There are multiple usage plans with the same name - it is not supported') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], + "There are multiple usage plans with the same name - it is not supported", + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_absent_if_plan_has_no_associations(self, *args): - ''' + """ Tests the case when the plan has no associations at all - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id1', 'apiStages': []}]})}): - result = boto_apigateway.usage_plan_association_absent('name', 'plan_name', [association_stage_1], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={"plans": [{"id": "id1", "apiStages": []}]} + ) + }, + ): + result = boto_apigateway.usage_plan_association_absent( + "name", "plan_name", [association_stage_1], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Usage plan plan_name has no associated stages already') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], + "Usage plan plan_name has no associated stages already", + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) - def test_usage_plan_association_absent_if_plan_has_no_specific_association(self, *args): - ''' + def test_usage_plan_association_absent_if_plan_has_no_specific_association( + self, *args + ): + """ Tests the case when requested association is not present already - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id1', 'apiStages': [association_stage_1]}]})}): - result = boto_apigateway.usage_plan_association_absent('name', 'plan_name', [association_stage_2], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={ + "plans": [{"id": "id1", "apiStages": [association_stage_1]}] + } + ) + }, + ): + result = boto_apigateway.usage_plan_association_absent( + "name", "plan_name", [association_stage_2], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'Usage plan is already not asssociated to any api stages') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], + "Usage plan is already not asssociated to any api stages", + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_absent_if_detaching_association_fails(self, *args): - ''' + """ Tests correct error processing when detaching the usage plan from the api function is called - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id1', 'apiStages': [association_stage_1, - association_stage_2]}]}), - 'boto_apigateway.detach_usage_plan_from_apis': MagicMock(return_value={'error': 'error'})}): - result = boto_apigateway.usage_plan_association_absent('name', 'plan_name', [association_stage_2], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={ + "plans": [ + { + "id": "id1", + "apiStages": [association_stage_1, association_stage_2], + } + ] + } + ), + "boto_apigateway.detach_usage_plan_from_apis": MagicMock( + return_value={"error": "error"} + ), + }, + ): + result = boto_apigateway.usage_plan_association_absent( + "name", "plan_name", [association_stage_2], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertTrue(result['comment'].startswith('Failed to disassociate a usage plan plan_name from the apis')) - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertTrue( + result["comment"].startswith( + "Failed to disassociate a usage plan plan_name from the apis" + ) + ) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_absent_success(self, *args): - ''' + """ Tests successful case of disaccosiation the usage plan from api stages - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(return_value={'plans': [{'id': 'id1', 'apiStages': [association_stage_1, - association_stage_2]}]}), - 'boto_apigateway.detach_usage_plan_from_apis': MagicMock(return_value={'result': {'apiStages': [association_stage_1]}})}): - result = boto_apigateway.usage_plan_association_absent('name', 'plan_name', [association_stage_2], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + return_value={ + "plans": [ + { + "id": "id1", + "apiStages": [association_stage_1, association_stage_2], + } + ] + } + ), + "boto_apigateway.detach_usage_plan_from_apis": MagicMock( + return_value={"result": {"apiStages": [association_stage_1]}} + ), + }, + ): + result = boto_apigateway.usage_plan_association_absent( + "name", "plan_name", [association_stage_2], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], True) - self.assertIn('comment', result) - self.assertEqual(result['comment'], 'successfully disassociated usage plan from apis') - self.assertIn('changes', result) - self.assertEqual(result['changes'], {'new': [association_stage_1], 'old': [association_stage_1, association_stage_2]}) + self.assertIn("result", result) + self.assertEqual(result["result"], True) + self.assertIn("comment", result) + self.assertEqual( + result["comment"], "successfully disassociated usage plan from apis" + ) + self.assertIn("changes", result) + self.assertEqual( + result["changes"], + { + "new": [association_stage_1], + "old": [association_stage_1, association_stage_2], + }, + ) def test_usage_plan_association_absent_if_ValueError_is_raised(self, *args): - ''' + """ Tests correct error processing for the case where ValueError is raised while trying to remove plan associations - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=ValueError('error'))}): - result = boto_apigateway.usage_plan_association_absent('name', 'plan_name', [association_stage_1], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=ValueError("error") + ) + }, + ): + result = boto_apigateway.usage_plan_association_absent( + "name", "plan_name", [association_stage_1], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], repr(('error',))) - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], repr(("error",))) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) def test_usage_plan_association_absent_if_IOError_is_raised(self, *args): - ''' + """ Tests correct error processing for the case where IOError exception is raised while trying to remove plan associations - ''' - with patch.dict(boto_apigateway.__salt__, {'boto_apigateway.describe_usage_plans': MagicMock(side_effect=IOError('error'))}): - result = boto_apigateway.usage_plan_association_absent('name', 'plan_name', [association_stage_1], **conn_parameters) + """ + with patch.dict( + boto_apigateway.__salt__, + { + "boto_apigateway.describe_usage_plans": MagicMock( + side_effect=IOError("error") + ) + }, + ): + result = boto_apigateway.usage_plan_association_absent( + "name", "plan_name", [association_stage_1], **conn_parameters + ) - self.assertIn('result', result) - self.assertEqual(result['result'], False) - self.assertIn('comment', result) - self.assertEqual(result['comment'], repr(('error',))) - self.assertIn('changes', result) - self.assertEqual(result['changes'], {}) + self.assertIn("result", result) + self.assertEqual(result["result"], False) + self.assertIn("comment", result) + self.assertEqual(result["comment"], repr(("error",))) + self.assertIn("changes", result) + self.assertEqual(result["changes"], {}) diff --git a/tests/unit/states/test_boto_asg.py b/tests/unit/states/test_boto_asg.py index 275ec234400..31fced2a777 100644 --- a/tests/unit/states/test_boto_asg.py +++ b/tests/unit/states/test_boto_asg.py @@ -1,23 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.states.boto_asg as boto_asg +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoAsgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_asg - ''' + """ + # 'present' function tests: 1 def setup_loader_modules(self): @@ -26,36 +27,34 @@ class BotoAsgTestCase(TestCase, LoaderModuleMockMixin): maxSize = None def test_present(self): - ''' + """ Test to ensure the autoscale group exists. - ''' - name = 'myasg' - launch_config_name = 'mylc' - availability_zones = ['us-east-1a', 'us-east-1b'] + """ + name = "myasg" + launch_config_name = "mylc" + availability_zones = ["us-east-1a", "us-east-1b"] min_size = 1 max_size = 1 - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} - mock = MagicMock(side_effect=[False, {'min_size': 2}, ['']]) - with patch.dict(boto_asg.__salt__, {'boto_asg.get_config': mock}): - with patch.dict(boto_asg.__opts__, {'test': True}): - comt = 'Autoscale group set to be created.' - ret.update({'comment': comt}) - with patch.dict(boto_asg.__salt__, - {'config.option': MagicMock(return_value={})}): + mock = MagicMock(side_effect=[False, {"min_size": 2}, [""]]) + with patch.dict(boto_asg.__salt__, {"boto_asg.get_config": mock}): + with patch.dict(boto_asg.__opts__, {"test": True}): + comt = "Autoscale group set to be created." + ret.update({"comment": comt}) + with patch.dict( + boto_asg.__salt__, {"config.option": MagicMock(return_value={})} + ): self.assertDictEqual( boto_asg.present( name, launch_config_name, availability_zones, min_size, - max_size + max_size, ), - ret + ret, ) def magic_side_effect(value): @@ -63,66 +62,67 @@ class BotoAsgTestCase(TestCase, LoaderModuleMockMixin): if value == 1: return 4 return value - return '' + return "" - comt = 'Autoscale group set to be updated.' - ret.update({'comment': comt, 'result': None}) - ret.update({'changes': {'new': {'min_size': 4}, - 'old': {'min_size': 2}}}) - utils_ordered_mock = MagicMock( - side_effect=magic_side_effect + comt = "Autoscale group set to be updated." + ret.update({"comment": comt, "result": None}) + ret.update( + {"changes": {"new": {"min_size": 4}, "old": {"min_size": 2}}} ) - with patch.dict(boto_asg.__salt__, - {'config.option': MagicMock(return_value={})}): - with patch.dict(boto_asg.__utils__, - {'boto3.ordered': utils_ordered_mock}): + utils_ordered_mock = MagicMock(side_effect=magic_side_effect) + with patch.dict( + boto_asg.__salt__, {"config.option": MagicMock(return_value={})} + ): + with patch.dict( + boto_asg.__utils__, {"boto3.ordered": utils_ordered_mock} + ): call_ret = boto_asg.present( name, launch_config_name, availability_zones, min_size, - max_size + max_size, ) self.assertDictEqual(call_ret, ret) - with patch.dict(boto_asg.__salt__, - {'config.option': MagicMock(return_value={})}): - with patch.dict(boto_asg.__utils__, - {'boto3.ordered': MagicMock(return_value='')}): - comt = 'Autoscale group present. ' - ret.update({'comment': comt, 'result': True}) - ret.update({'changes': {}}) + with patch.dict( + boto_asg.__salt__, {"config.option": MagicMock(return_value={})} + ): + with patch.dict( + boto_asg.__utils__, + {"boto3.ordered": MagicMock(return_value="")}, + ): + comt = "Autoscale group present. " + ret.update({"comment": comt, "result": True}) + ret.update({"changes": {}}) self.assertDictEqual( boto_asg.present( name, launch_config_name, availability_zones, min_size, - max_size + max_size, ), - ret + ret, ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the named autoscale group is deleted. - ''' - name = 'myasg' + """ + name = "myasg" - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False]) - with patch.dict(boto_asg.__salt__, {'boto_asg.get_config': mock}): - with patch.dict(boto_asg.__opts__, {'test': True}): - comt = ('Autoscale group set to be deleted.') - ret.update({'comment': comt}) + with patch.dict(boto_asg.__salt__, {"boto_asg.get_config": mock}): + with patch.dict(boto_asg.__opts__, {"test": True}): + comt = "Autoscale group set to be deleted." + ret.update({"comment": comt}) self.assertDictEqual(boto_asg.absent(name), ret) - comt = ('Autoscale group does not exist.') - ret.update({'comment': comt, 'result': True}) + comt = "Autoscale group does not exist." + ret.update({"comment": comt, "result": True}) self.assertDictEqual(boto_asg.absent(name), ret) diff --git a/tests/unit/states/test_boto_cloudfront.py b/tests/unit/states/test_boto_cloudfront.py index 5db1b2ad847..12d024184ea 100644 --- a/tests/unit/states/test_boto_cloudfront.py +++ b/tests/unit/states/test_boto_cloudfront.py @@ -1,48 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for the boto_cloudfront state module. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import textwrap -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.config import salt.loader import salt.states.boto_cloudfront as boto_cloudfront +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoCloudfrontTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_cloudfront - ''' + """ + def setup_loader_modules(self): utils = salt.loader.utils( self.opts, - whitelist=['boto3', 'dictdiffer', 'yaml', 'args', 'systemd', 'path', 'platform'], - context={}) - return { - boto_cloudfront: { - '__utils__': utils, - } - } + whitelist=[ + "boto3", + "dictdiffer", + "yaml", + "args", + "systemd", + "path", + "platform", + ], + context={}, + ) + return {boto_cloudfront: {"__utils__": utils}} @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.name = 'my_distribution' - cls.base_ret = {'name': cls.name, 'changes': {}} + cls.name = "my_distribution" + cls.base_ret = {"name": cls.name, "changes": {}} # Most attributes elided since there are so many required ones - cls.config = {'Enabled': True, 'HttpVersion': 'http2'} - cls.tags = {'test_tag1': 'value1'} + cls.config = {"Enabled": True, "HttpVersion": "http2"} + cls.tags = {"test_tag1": "value1"} @classmethod def tearDownClass(cls): @@ -60,105 +67,122 @@ class BotoCloudfrontTestCase(TestCase, LoaderModuleMockMixin): return new_ret def test_present_distribution_retrieval_error(self): - ''' + """ Test for boto_cloudfront.present when we cannot get the distribution. - ''' - mock_get = MagicMock(return_value={'error': 'get_distribution error'}) - with patch.multiple(boto_cloudfront, - __salt__={'boto_cloudfront.get_distribution': mock_get}, - __opts__={'test': False}, + """ + mock_get = MagicMock(return_value={"error": "get_distribution error"}) + with patch.multiple( + boto_cloudfront, + __salt__={"boto_cloudfront.get_distribution": mock_get}, + __opts__={"test": False}, ): - comment = 'Error checking distribution {0}: get_distribution error' + comment = "Error checking distribution {0}: get_distribution error" self.assertDictEqual( boto_cloudfront.present(self.name, self.config, self.tags), - self.base_ret_with({ - 'result': False, - 'comment': comment.format(self.name), - }), + self.base_ret_with( + {"result": False, "comment": comment.format(self.name)} + ), ) def test_present_from_scratch(self): - mock_get = MagicMock(return_value={'result': None}) + mock_get = MagicMock(return_value={"result": None}) - with patch.multiple(boto_cloudfront, - __salt__={'boto_cloudfront.get_distribution': mock_get}, - __opts__={'test': True}, + with patch.multiple( + boto_cloudfront, + __salt__={"boto_cloudfront.get_distribution": mock_get}, + __opts__={"test": True}, ): - comment = 'Distribution {0} set for creation.'.format(self.name) + comment = "Distribution {0} set for creation.".format(self.name) self.assertDictEqual( boto_cloudfront.present(self.name, self.config, self.tags), - self.base_ret_with({ - 'result': None, - 'comment': comment, - 'changes': {'old': None, 'new': self.name}, - }), + self.base_ret_with( + { + "result": None, + "comment": comment, + "changes": {"old": None, "new": self.name}, + } + ), ) - mock_create_failure = MagicMock(return_value={'error': 'create error'}) - with patch.multiple(boto_cloudfront, + mock_create_failure = MagicMock(return_value={"error": "create error"}) + with patch.multiple( + boto_cloudfront, __salt__={ - 'boto_cloudfront.get_distribution': mock_get, - 'boto_cloudfront.create_distribution': mock_create_failure, + "boto_cloudfront.get_distribution": mock_get, + "boto_cloudfront.create_distribution": mock_create_failure, }, - __opts__={'test': False}, + __opts__={"test": False}, ): - comment = 'Error creating distribution {0}: create error' + comment = "Error creating distribution {0}: create error" self.assertDictEqual( boto_cloudfront.present(self.name, self.config, self.tags), - self.base_ret_with({ - 'result': False, - 'comment': comment.format(self.name), - }), + self.base_ret_with( + {"result": False, "comment": comment.format(self.name)} + ), ) - mock_create_success = MagicMock(return_value={'result': True}) - with patch.multiple(boto_cloudfront, + mock_create_success = MagicMock(return_value={"result": True}) + with patch.multiple( + boto_cloudfront, __salt__={ - 'boto_cloudfront.get_distribution': mock_get, - 'boto_cloudfront.create_distribution': mock_create_success, + "boto_cloudfront.get_distribution": mock_get, + "boto_cloudfront.create_distribution": mock_create_success, }, - __opts__={'test': False}, + __opts__={"test": False}, ): - comment = 'Created distribution {0}.' + comment = "Created distribution {0}." self.assertDictEqual( boto_cloudfront.present(self.name, self.config, self.tags), - self.base_ret_with({ - 'result': True, - 'comment': comment.format(self.name), - 'changes': {'old': None, 'new': self.name}, - }), + self.base_ret_with( + { + "result": True, + "comment": comment.format(self.name), + "changes": {"old": None, "new": self.name}, + } + ), ) def test_present_correct_state(self): - mock_get = MagicMock(return_value={'result': { - 'distribution': {'DistributionConfig': self.config}, - 'tags': self.tags, - 'etag': 'test etag', - }}) - with patch.multiple(boto_cloudfront, - __salt__={'boto_cloudfront.get_distribution': mock_get}, - __opts__={'test': False}, + mock_get = MagicMock( + return_value={ + "result": { + "distribution": {"DistributionConfig": self.config}, + "tags": self.tags, + "etag": "test etag", + } + } + ) + with patch.multiple( + boto_cloudfront, + __salt__={"boto_cloudfront.get_distribution": mock_get}, + __opts__={"test": False}, ): - comment = 'Distribution {0} has correct config.' + comment = "Distribution {0} has correct config." self.assertDictEqual( boto_cloudfront.present(self.name, self.config, self.tags), - self.base_ret_with({ - 'result': True, - 'comment': comment.format(self.name), - }), + self.base_ret_with( + {"result": True, "comment": comment.format(self.name)} + ), ) def test_present_update_config_and_tags(self): - mock_get = MagicMock(return_value={'result': { - 'distribution': {'DistributionConfig': { - 'Enabled': False, - 'Comment': 'to be removed', - }}, - 'tags': {'bad existing tag': 'also to be removed'}, - 'etag': 'test etag', - }}) + mock_get = MagicMock( + return_value={ + "result": { + "distribution": { + "DistributionConfig": { + "Enabled": False, + "Comment": "to be removed", + } + }, + "tags": {"bad existing tag": "also to be removed"}, + "etag": "test etag", + } + } + ) - diff = textwrap.dedent('''\ + diff = textwrap.dedent( + """\ --- +++ @@ -1,5 +1,5 @@ @@ -171,59 +195,66 @@ class BotoCloudfrontTestCase(TestCase, LoaderModuleMockMixin): - bad existing tag: also to be removed + test_tag1: value1 - ''').splitlines() + """ + ).splitlines() # Difflib adds a trailing space after the +++/--- lines, # programatically add them back here. Having them in the test file # itself is not feasible since a few popular plugins for vim will # remove trailing whitespace. for idx in (0, 1): - diff[idx] += ' ' - diff = '\n'.join(diff) + diff[idx] += " " + diff = "\n".join(diff) - with patch.multiple(boto_cloudfront, - __salt__={'boto_cloudfront.get_distribution': mock_get}, - __opts__={'test': True}, + with patch.multiple( + boto_cloudfront, + __salt__={"boto_cloudfront.get_distribution": mock_get}, + __opts__={"test": True}, ): - header = 'Distribution {0} set for new config:'.format(self.name) + header = "Distribution {0} set for new config:".format(self.name) self.assertDictEqual( boto_cloudfront.present(self.name, self.config, self.tags), - self.base_ret_with({ - 'result': None, - 'comment': '\n'.join([header, diff]), - 'changes': {'diff': diff}, - }), + self.base_ret_with( + { + "result": None, + "comment": "\n".join([header, diff]), + "changes": {"diff": diff}, + } + ), ) - mock_update_failure = MagicMock(return_value={'error': 'update error'}) - with patch.multiple(boto_cloudfront, + mock_update_failure = MagicMock(return_value={"error": "update error"}) + with patch.multiple( + boto_cloudfront, __salt__={ - 'boto_cloudfront.get_distribution': mock_get, - 'boto_cloudfront.update_distribution': mock_update_failure, + "boto_cloudfront.get_distribution": mock_get, + "boto_cloudfront.update_distribution": mock_update_failure, }, - __opts__={'test': False}, + __opts__={"test": False}, ): - comment = 'Error updating distribution {0}: update error' + comment = "Error updating distribution {0}: update error" self.assertDictEqual( boto_cloudfront.present(self.name, self.config, self.tags), - self.base_ret_with({ - 'result': False, - 'comment': comment.format(self.name), - }), + self.base_ret_with( + {"result": False, "comment": comment.format(self.name)} + ), ) - mock_update_success = MagicMock(return_value={'result': True}) - with patch.multiple(boto_cloudfront, + mock_update_success = MagicMock(return_value={"result": True}) + with patch.multiple( + boto_cloudfront, __salt__={ - 'boto_cloudfront.get_distribution': mock_get, - 'boto_cloudfront.update_distribution': mock_update_success, + "boto_cloudfront.get_distribution": mock_get, + "boto_cloudfront.update_distribution": mock_update_success, }, - __opts__={'test': False}, + __opts__={"test": False}, ): self.assertDictEqual( boto_cloudfront.present(self.name, self.config, self.tags), - self.base_ret_with({ - 'result': True, - 'comment': 'Updated distribution {0}.'.format(self.name), - 'changes': {'diff': diff}, - }), + self.base_ret_with( + { + "result": True, + "comment": "Updated distribution {0}.".format(self.name), + "changes": {"diff": diff}, + } + ), ) diff --git a/tests/unit/states/test_boto_cloudtrail.py b/tests/unit/states/test_boto_cloudtrail.py index 93c3367a547..5ec1db2fb15 100644 --- a/tests/unit/states/test_boto_cloudtrail.py +++ b/tests/unit/states/test_boto_cloudtrail.py @@ -2,55 +2,55 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import random import string -import logging - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch -) # Import Salt libs import salt.config import salt.loader + +# Import Salt Libs +import salt.states.boto_cloudtrail as boto_cloudtrail + +# Import 3rd-party libs +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin from salt.utils.versions import LooseVersion +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + # pylint: disable=import-error,no-name-in-module,unused-import from tests.unit.modules.test_boto_cloudtrail import BotoCloudTrailTestCaseMixin -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin try: import boto import boto3 from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False # pylint: enable=import-error,no-name-in-module,unused-import -# Import Salt Libs -import salt.states.boto_cloudtrail as boto_cloudtrail # the boto_cloudtrail module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' +required_boto3_version = "1.2.1" log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -60,46 +60,52 @@ def _has_required_boto(): if _has_required_boto(): - region = 'us-east-1' - access_key = 'GKTADJGHEIQSXMKKRBJ08H' - secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' - conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} - error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' - not_found_error = ClientError({ - 'Error': { - 'Code': 'TrailNotFoundException', - 'Message': "Test-defined error" - } - }, 'msg') - error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } + region = "us-east-1" + access_key = "GKTADJGHEIQSXMKKRBJ08H" + secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" + conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } - trail_ret = dict(Name='testtrail', - IncludeGlobalServiceEvents=True, - KmsKeyId=None, - LogFileValidationEnabled=False, - S3BucketName='auditinfo', - TrailARN='arn:aws:cloudtrail:us-east-1:214351231622:trail/testtrail') - status_ret = dict(IsLogging=False, - LatestCloudWatchLogsDeliveryError=None, - LatestCloudWatchLogsDeliveryTime=None, - LatestDeliveryError=None, - LatestDeliveryTime=None, - LatestDigestDeliveryError=None, - LatestDigestDeliveryTime=None, - LatestNotificationError=None, - LatestNotificationTime=None, - StartLoggingTime=None, - StopLoggingTime=None) + error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" + ) + not_found_error = ClientError( + {"Error": {"Code": "TrailNotFoundException", "Message": "Test-defined error"}}, + "msg", + ) + error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} + trail_ret = dict( + Name="testtrail", + IncludeGlobalServiceEvents=True, + KmsKeyId=None, + LogFileValidationEnabled=False, + S3BucketName="auditinfo", + TrailARN="arn:aws:cloudtrail:us-east-1:214351231622:trail/testtrail", + ) + status_ret = dict( + IsLogging=False, + LatestCloudWatchLogsDeliveryError=None, + LatestCloudWatchLogsDeliveryTime=None, + LatestDeliveryError=None, + LatestDeliveryTime=None, + LatestDigestDeliveryError=None, + LatestDigestDeliveryTime=None, + LatestNotificationError=None, + LatestNotificationTime=None, + StartLoggingTime=None, + StopLoggingTime=None, + ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) class BotoCloudTrailStateTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None @@ -107,26 +113,34 @@ class BotoCloudTrailStateTestCaseBase(TestCase, LoaderModuleMockMixin): ctx = {} utils = salt.loader.utils( self.opts, - whitelist=['boto', 'boto3', 'args', 'systemd', 'path', 'platform', 'reg'], - context=ctx) + whitelist=["boto", "boto3", "args", "systemd", "path", "platform", "reg"], + context=ctx, + ) serializers = salt.loader.serializers(self.opts) - self.funcs = funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_cloudtrail']) - self.salt_states = salt.loader.states(opts=self.opts, functions=funcs, utils=utils, whitelist=['boto_cloudtrail'], - serializers=serializers) + self.funcs = funcs = salt.loader.minion_mods( + self.opts, context=ctx, utils=utils, whitelist=["boto_cloudtrail"] + ) + self.salt_states = salt.loader.states( + opts=self.opts, + functions=funcs, + utils=utils, + whitelist=["boto_cloudtrail"], + serializers=serializers, + ) return { boto_cloudtrail: { - '__opts__': self.opts, - '__salt__': funcs, - '__utils__': utils, - '__states__': self.salt_states, - '__serializers__': serializers, + "__opts__": self.opts, + "__salt__": funcs, + "__utils__": utils, + "__states__": self.salt_states, + "__serializers__": serializers, } } @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) @classmethod def tearDownClass(cls): @@ -134,90 +148,102 @@ class BotoCloudTrailStateTestCaseBase(TestCase, LoaderModuleMockMixin): # Set up MagicMock to replace the boto3 session def setUp(self): - self.addCleanup(delattr, self, 'funcs') - self.addCleanup(delattr, self, 'salt_states') + self.addCleanup(delattr, self, "funcs") + self.addCleanup(delattr, self, "salt_states") # Set up MagicMock to replace the boto3 session # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn -class BotoCloudTrailTestCase(BotoCloudTrailStateTestCaseBase, BotoCloudTrailTestCaseMixin): - ''' +class BotoCloudTrailTestCase( + BotoCloudTrailStateTestCaseBase, BotoCloudTrailTestCaseMixin +): + """ TestCase for salt.modules.boto_cloudtrail state.module - ''' + """ def test_present_when_trail_does_not_exist(self): - ''' + """ Tests present on a trail that does not exist. - ''' + """ self.conn.get_trail_status.side_effect = [not_found_error, status_ret] self.conn.create_trail.return_value = trail_ret - self.conn.describe_trails.return_value = {'trailList': [trail_ret]} - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = self.salt_states['boto_cloudtrail.present']( - 'trail present', - Name=trail_ret['Name'], - S3BucketName=trail_ret['S3BucketName']) + self.conn.describe_trails.return_value = {"trailList": [trail_ret]} + with patch.dict( + self.funcs, {"boto_iam.get_account_id": MagicMock(return_value="1234")} + ): + result = self.salt_states["boto_cloudtrail.present"]( + "trail present", + Name=trail_ret["Name"], + S3BucketName=trail_ret["S3BucketName"], + ) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['trail']['Name'], - trail_ret['Name']) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["trail"]["Name"], trail_ret["Name"]) def test_present_when_trail_exists(self): self.conn.get_trail_status.return_value = status_ret self.conn.create_trail.return_value = trail_ret - self.conn.describe_trails.return_value = {'trailList': [trail_ret]} - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = self.salt_states['boto_cloudtrail.present']( - 'trail present', - Name=trail_ret['Name'], - S3BucketName=trail_ret['S3BucketName'], - LoggingEnabled=False) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + self.conn.describe_trails.return_value = {"trailList": [trail_ret]} + with patch.dict( + self.funcs, {"boto_iam.get_account_id": MagicMock(return_value="1234")} + ): + result = self.salt_states["boto_cloudtrail.present"]( + "trail present", + Name=trail_ret["Name"], + S3BucketName=trail_ret["S3BucketName"], + LoggingEnabled=False, + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_present_with_failure(self): self.conn.get_trail_status.side_effect = [not_found_error, status_ret] - self.conn.create_trail.side_effect = ClientError(error_content, 'create_trail') - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): - result = self.salt_states['boto_cloudtrail.present']( - 'trail present', - Name=trail_ret['Name'], - S3BucketName=trail_ret['S3BucketName'], - LoggingEnabled=False) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.create_trail.side_effect = ClientError(error_content, "create_trail") + with patch.dict( + self.funcs, {"boto_iam.get_account_id": MagicMock(return_value="1234")} + ): + result = self.salt_states["boto_cloudtrail.present"]( + "trail present", + Name=trail_ret["Name"], + S3BucketName=trail_ret["S3BucketName"], + LoggingEnabled=False, + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_absent_when_trail_does_not_exist(self): - ''' + """ Tests absent on a trail that does not exist. - ''' + """ self.conn.get_trail_status.side_effect = not_found_error - result = self.salt_states['boto_cloudtrail.absent']('test', 'mytrail') - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_cloudtrail.absent"]("test", "mytrail") + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_absent_when_trail_exists(self): self.conn.get_trail_status.return_value = status_ret - result = self.salt_states['boto_cloudtrail.absent']('test', trail_ret['Name']) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['trail'], None) + result = self.salt_states["boto_cloudtrail.absent"]("test", trail_ret["Name"]) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["trail"], None) def test_absent_with_failure(self): self.conn.get_trail_status.return_value = status_ret - self.conn.delete_trail.side_effect = ClientError(error_content, 'delete_trail') - result = self.salt_states['boto_cloudtrail.absent']('test', trail_ret['Name']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.delete_trail.side_effect = ClientError(error_content, "delete_trail") + result = self.salt_states["boto_cloudtrail.absent"]("test", trail_ret["Name"]) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) diff --git a/tests/unit/states/test_boto_cloudwatch_alarm.py b/tests/unit/states/test_boto_cloudwatch_alarm.py index 758166d347f..c1892a98f42 100644 --- a/tests/unit/states/test_boto_cloudwatch_alarm.py +++ b/tests/unit/states/test_boto_cloudwatch_alarm.py @@ -1,92 +1,95 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.boto_cloudwatch_alarm as boto_cloudwatch_alarm +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoCloudwatchAlarmTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_cloudwatch_alarm - ''' + """ + def setup_loader_modules(self): return {boto_cloudwatch_alarm: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the cloudwatch alarm exists. - ''' - name = 'my test alarm' - attributes = {'metric': 'ApproximateNumberOfMessagesVisible', - 'namespace': 'AWS/SQS'} + """ + name = "my test alarm" + attributes = { + "metric": "ApproximateNumberOfMessagesVisible", + "namespace": "AWS/SQS", + } - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} - mock = MagicMock(side_effect=[['ok_actions'], [], []]) + mock = MagicMock(side_effect=[["ok_actions"], [], []]) mock_bool = MagicMock(return_value=True) - with patch.dict(boto_cloudwatch_alarm.__salt__, - {'boto_cloudwatch.get_alarm': mock, - 'boto_cloudwatch.create_or_update_alarm': mock_bool}): - with patch.dict(boto_cloudwatch_alarm.__opts__, {'test': True}): - comt = ('alarm my test alarm is to be created/updated.') - ret.update({'comment': comt}) - self.assertDictEqual(boto_cloudwatch_alarm.present(name, - attributes), - ret) + with patch.dict( + boto_cloudwatch_alarm.__salt__, + { + "boto_cloudwatch.get_alarm": mock, + "boto_cloudwatch.create_or_update_alarm": mock_bool, + }, + ): + with patch.dict(boto_cloudwatch_alarm.__opts__, {"test": True}): + comt = "alarm my test alarm is to be created/updated." + ret.update({"comment": comt}) + self.assertDictEqual( + boto_cloudwatch_alarm.present(name, attributes), ret + ) - comt = ('alarm my test alarm is to be created/updated.') - ret.update({'comment': comt}) - self.assertDictEqual(boto_cloudwatch_alarm.present(name, - attributes), - ret) + comt = "alarm my test alarm is to be created/updated." + ret.update({"comment": comt}) + self.assertDictEqual( + boto_cloudwatch_alarm.present(name, attributes), ret + ) - with patch.dict(boto_cloudwatch_alarm.__opts__, {'test': False}): - changes = {'new': - {'metric': 'ApproximateNumberOfMessagesVisible', - 'namespace': 'AWS/SQS'}} - comt = ('alarm my test alarm is to be created/updated.') - ret.update({'changes': changes, 'comment': '', 'result': True}) - self.assertDictEqual(boto_cloudwatch_alarm.present(name, - attributes), - ret) + with patch.dict(boto_cloudwatch_alarm.__opts__, {"test": False}): + changes = { + "new": { + "metric": "ApproximateNumberOfMessagesVisible", + "namespace": "AWS/SQS", + } + } + comt = "alarm my test alarm is to be created/updated." + ret.update({"changes": changes, "comment": "", "result": True}) + self.assertDictEqual( + boto_cloudwatch_alarm.present(name, attributes), ret + ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the named cloudwatch alarm is deleted. - ''' - name = 'my test alarm' + """ + name = "my test alarm" - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False]) - with patch.dict(boto_cloudwatch_alarm.__salt__, - {'boto_cloudwatch.get_alarm': mock}): - with patch.dict(boto_cloudwatch_alarm.__opts__, {'test': True}): - comt = ('alarm {0} is set to be removed.'.format(name)) - ret.update({'comment': comt}) + with patch.dict( + boto_cloudwatch_alarm.__salt__, {"boto_cloudwatch.get_alarm": mock} + ): + with patch.dict(boto_cloudwatch_alarm.__opts__, {"test": True}): + comt = "alarm {0} is set to be removed.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_cloudwatch_alarm.absent(name), ret) - comt = ('my test alarm does not exist in None.') - ret.update({'comment': comt, 'result': True}) + comt = "my test alarm does not exist in None." + ret.update({"comment": comt, "result": True}) self.assertDictEqual(boto_cloudwatch_alarm.absent(name), ret) diff --git a/tests/unit/states/test_boto_cloudwatch_event.py b/tests/unit/states/test_boto_cloudwatch_event.py index 9d0008fb5a3..c05f559dd10 100644 --- a/tests/unit/states/test_boto_cloudwatch_event.py +++ b/tests/unit/states/test_boto_cloudwatch_event.py @@ -2,59 +2,68 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import random import string -import logging - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import patch, MagicMock # Import Salt libs import salt.config import salt.loader import salt.states.boto_cloudwatch_event as boto_cloudwatch_event +from salt.ext.six.moves import range + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error,no-name-in-module -from tests.unit.modules.test_boto_cloudwatch_event import BotoCloudWatchEventTestCaseMixin +from tests.unit.modules.test_boto_cloudwatch_event import ( + BotoCloudWatchEventTestCaseMixin, +) # pylint: disable=unused-import # Import 3rd-party libs try: import boto3 from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False # pylint: enable=unused-import -from salt.ext.six.moves import range # pylint: enable=import-error,no-name-in-module -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} -error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' -error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } +error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" +) +error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} if HAS_BOTO: - not_found_error = ClientError({ - 'Error': { - 'Code': 'ResourceNotFoundException', - 'Message': "Test-defined error" - } - }, 'msg') -rule_name = 'test_thing_type' -rule_desc = 'test_thing_type_desc' -rule_sched = 'rate(20 min)' -rule_arn = 'arn:::::rule/arn' + not_found_error = ClientError( + { + "Error": { + "Code": "ResourceNotFoundException", + "Message": "Test-defined error", + } + }, + "msg", + ) +rule_name = "test_thing_type" +rule_desc = "test_thing_type_desc" +rule_sched = "rate(20 min)" +rule_arn = "arn:::::rule/arn" rule_ret = dict( Arn=rule_arn, Description=rule_desc, @@ -62,7 +71,7 @@ rule_ret = dict( Name=rule_name, RoleArn=None, ScheduleExpression=rule_sched, - State='ENABLED' + State="ENABLED", ) @@ -70,10 +79,10 @@ log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False else: @@ -87,351 +96,352 @@ class BotoCloudWatchEventStateTestCaseBase(TestCase, LoaderModuleMockMixin): ctx = {} utils = salt.loader.utils( self.opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context=ctx) + whitelist=["boto3", "args", "systemd", "path", "platform"], + context=ctx, + ) serializers = salt.loader.serializers(self.opts) - self.funcs = funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_cloudwatch_event']) - self.salt_states = salt.loader.states(opts=self.opts, functions=funcs, utils=utils, whitelist=['boto_cloudwatch_event'], - serializers=serializers) + self.funcs = funcs = salt.loader.minion_mods( + self.opts, context=ctx, utils=utils, whitelist=["boto_cloudwatch_event"] + ) + self.salt_states = salt.loader.states( + opts=self.opts, + functions=funcs, + utils=utils, + whitelist=["boto_cloudwatch_event"], + serializers=serializers, + ) return { boto_cloudwatch_event: { - '__opts__': self.opts, - '__salt__': funcs, - '__utils__': utils, - '__states__': self.salt_states, - '__serializers__': serializers, + "__opts__": self.opts, + "__salt__": funcs, + "__utils__": utils, + "__states__": self.salt_states, + "__serializers__": serializers, } } @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) @classmethod def tearDownClass(cls): del cls.opts def setUp(self): - self.addCleanup(delattr, self, 'funcs') - self.addCleanup(delattr, self, 'salt_states') + self.addCleanup(delattr, self, "funcs") + self.addCleanup(delattr, self, "salt_states") # Set up MagicMock to replace the boto3 session # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -class BotoCloudWatchEventTestCase(BotoCloudWatchEventStateTestCaseBase, BotoCloudWatchEventTestCaseMixin): +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +class BotoCloudWatchEventTestCase( + BotoCloudWatchEventStateTestCaseBase, BotoCloudWatchEventTestCaseMixin +): def test_present_when_failing_to_describe_rule(self): - ''' + """ Tests exceptions when checking rule existence - ''' - self.conn.list_rules.side_effect = ClientError(error_content, 'error on list rules') - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('error on list rules' in result.get('comment', {})) + """ + self.conn.list_rules.side_effect = ClientError( + error_content, "error on list rules" + ) + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("error on list rules" in result.get("comment", {})) def test_present_when_failing_to_create_a_new_rule(self): - ''' + """ Tests present on a rule name that doesn't exist and an error is thrown on creation. - ''' - self.conn.list_rules.return_value = {'Rules': []} - self.conn.put_rule.side_effect = ClientError(error_content, 'put_rule') - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('put_rule' in result.get('comment', '')) + """ + self.conn.list_rules.return_value = {"Rules": []} + self.conn.put_rule.side_effect = ClientError(error_content, "put_rule") + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("put_rule" in result.get("comment", "")) def test_present_when_failing_to_describe_the_new_rule(self): - ''' + """ Tests present on a rule name that doesn't exist and an error is thrown when adding targets. - ''' - self.conn.list_rules.return_value = {'Rules': []} + """ + self.conn.list_rules.return_value = {"Rules": []} self.conn.put_rule.return_value = rule_ret - self.conn.describe_rule.side_effect = ClientError(error_content, 'describe_rule') - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('describe_rule' in result.get('comment', '')) + self.conn.describe_rule.side_effect = ClientError( + error_content, "describe_rule" + ) + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("describe_rule" in result.get("comment", "")) def test_present_when_failing_to_create_a_new_rules_targets(self): - ''' + """ Tests present on a rule name that doesn't exist and an error is thrown when adding targets. - ''' - self.conn.list_rules.return_value = {'Rules': []} + """ + self.conn.list_rules.return_value = {"Rules": []} self.conn.put_rule.return_value = rule_ret self.conn.describe_rule.return_value = rule_ret - self.conn.put_targets.side_effect = ClientError(error_content, 'put_targets') - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('put_targets' in result.get('comment', '')) + self.conn.put_targets.side_effect = ClientError(error_content, "put_targets") + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("put_targets" in result.get("comment", "")) def test_present_when_rule_does_not_exist(self): - ''' + """ Tests the successful case of creating a new rule, and updating its targets - ''' - self.conn.list_rules.return_value = {'Rules': []} + """ + self.conn.list_rules.return_value = {"Rules": []} self.conn.put_rule.return_value = rule_ret self.conn.describe_rule.return_value = rule_ret - self.conn.put_targets.return_value = {'FailedEntryCount': 0} - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), True) + self.conn.put_targets.return_value = {"FailedEntryCount": 0} + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), True) def test_present_when_failing_to_update_an_existing_rule(self): - ''' + """ Tests present on an existing rule where an error is thrown on updating the pool properties. - ''' - self.conn.list_rules.return_value = {'Rules': [rule_ret]} - self.conn.describe_rule.side_effect = ClientError(error_content, 'describe_rule') - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('describe_rule' in result.get('comment', '')) + """ + self.conn.list_rules.return_value = {"Rules": [rule_ret]} + self.conn.describe_rule.side_effect = ClientError( + error_content, "describe_rule" + ) + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("describe_rule" in result.get("comment", "")) def test_present_when_failing_to_get_targets(self): - ''' + """ Tests present on an existing rule where put_rule succeeded, but an error is thrown on getting targets - ''' - self.conn.list_rules.return_value = {'Rules': [rule_ret]} + """ + self.conn.list_rules.return_value = {"Rules": [rule_ret]} self.conn.put_rule.return_value = rule_ret self.conn.describe_rule.return_value = rule_ret - self.conn.list_targets_by_rule.side_effect = ClientError(error_content, 'list_targets') - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('list_targets' in result.get('comment', '')) + self.conn.list_targets_by_rule.side_effect = ClientError( + error_content, "list_targets" + ) + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("list_targets" in result.get("comment", "")) def test_present_when_failing_to_put_targets(self): - ''' + """ Tests present on an existing rule where put_rule succeeded, but an error is thrown on putting targets - ''' - self.conn.list_rules.return_value = {'Rules': []} + """ + self.conn.list_rules.return_value = {"Rules": []} self.conn.put_rule.return_value = rule_ret self.conn.describe_rule.return_value = rule_ret - self.conn.list_targets.return_value = {'Targets': []} - self.conn.put_targets.side_effect = ClientError(error_content, 'put_targets') - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('put_targets' in result.get('comment', '')) + self.conn.list_targets.return_value = {"Targets": []} + self.conn.put_targets.side_effect = ClientError(error_content, "put_targets") + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("put_targets" in result.get("comment", "")) def test_present_when_putting_targets(self): - ''' + """ Tests present on an existing rule where put_rule succeeded, and targets must be added - ''' - self.conn.list_rules.return_value = {'Rules': []} + """ + self.conn.list_rules.return_value = {"Rules": []} self.conn.put_rule.return_value = rule_ret self.conn.describe_rule.return_value = rule_ret - self.conn.list_targets.return_value = {'Targets': []} - self.conn.put_targets.return_value = {'FailedEntryCount': 0} - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), True) + self.conn.list_targets.return_value = {"Targets": []} + self.conn.put_targets.return_value = {"FailedEntryCount": 0} + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), True) def test_present_when_removing_targets(self): - ''' + """ Tests present on an existing rule where put_rule succeeded, and targets must be removed - ''' - self.conn.list_rules.return_value = {'Rules': []} + """ + self.conn.list_rules.return_value = {"Rules": []} self.conn.put_rule.return_value = rule_ret self.conn.describe_rule.return_value = rule_ret - self.conn.list_targets.return_value = {'Targets': [{'Id': 'target1'}, {'Id': 'target2'}]} - self.conn.put_targets.return_value = {'FailedEntryCount': 0} - result = self.salt_states['boto_cloudwatch_event.present']( - name='test present', - Name=rule_name, - Description=rule_desc, - ScheduleExpression=rule_sched, - Targets=[{ - 'Id': 'target1', - 'Arn': 'arn::::::*', - }], - **conn_parameters) - self.assertEqual(result.get('result'), True) + self.conn.list_targets.return_value = { + "Targets": [{"Id": "target1"}, {"Id": "target2"}] + } + self.conn.put_targets.return_value = {"FailedEntryCount": 0} + result = self.salt_states["boto_cloudwatch_event.present"]( + name="test present", + Name=rule_name, + Description=rule_desc, + ScheduleExpression=rule_sched, + Targets=[{"Id": "target1", "Arn": "arn::::::*"}], + **conn_parameters + ) + self.assertEqual(result.get("result"), True) def test_absent_when_failing_to_describe_rule(self): - ''' + """ Tests exceptions when checking rule existence - ''' - self.conn.list_rules.side_effect = ClientError(error_content, 'error on list rules') - result = self.salt_states['boto_cloudwatch_event.absent']( - name='test present', - Name=rule_name, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('error on list rules' in result.get('comment', {})) + """ + self.conn.list_rules.side_effect = ClientError( + error_content, "error on list rules" + ) + result = self.salt_states["boto_cloudwatch_event.absent"]( + name="test present", Name=rule_name, **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("error on list rules" in result.get("comment", {})) def test_absent_when_rule_does_not_exist(self): - ''' + """ Tests absent on an non-existing rule - ''' - self.conn.list_rules.return_value = {'Rules': []} - result = self.salt_states['boto_cloudwatch_event.absent']( - name='test absent', - Name=rule_name, - **conn_parameters) - self.assertEqual(result.get('result'), True) - self.assertEqual(result['changes'], {}) + """ + self.conn.list_rules.return_value = {"Rules": []} + result = self.salt_states["boto_cloudwatch_event.absent"]( + name="test absent", Name=rule_name, **conn_parameters + ) + self.assertEqual(result.get("result"), True) + self.assertEqual(result["changes"], {}) def test_absent_when_failing_to_list_targets(self): - ''' + """ Tests absent on an rule when the list_targets call fails - ''' - self.conn.list_rules.return_value = {'Rules': [rule_ret]} - self.conn.list_targets_by_rule.side_effect = ClientError(error_content, 'list_targets') - result = self.salt_states['boto_cloudwatch_event.absent']( - name='test absent', - Name=rule_name, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('list_targets' in result.get('comment', '')) + """ + self.conn.list_rules.return_value = {"Rules": [rule_ret]} + self.conn.list_targets_by_rule.side_effect = ClientError( + error_content, "list_targets" + ) + result = self.salt_states["boto_cloudwatch_event.absent"]( + name="test absent", Name=rule_name, **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("list_targets" in result.get("comment", "")) def test_absent_when_failing_to_remove_targets_exception(self): - ''' + """ Tests absent on an rule when the remove_targets call fails - ''' - self.conn.list_rules.return_value = {'Rules': [rule_ret]} - self.conn.list_targets_by_rule.return_value = {'Targets': [{'Id': 'target1'}]} - self.conn.remove_targets.side_effect = ClientError(error_content, 'remove_targets') - result = self.salt_states['boto_cloudwatch_event.absent']( - name='test absent', - Name=rule_name, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('remove_targets' in result.get('comment', '')) + """ + self.conn.list_rules.return_value = {"Rules": [rule_ret]} + self.conn.list_targets_by_rule.return_value = {"Targets": [{"Id": "target1"}]} + self.conn.remove_targets.side_effect = ClientError( + error_content, "remove_targets" + ) + result = self.salt_states["boto_cloudwatch_event.absent"]( + name="test absent", Name=rule_name, **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("remove_targets" in result.get("comment", "")) def test_absent_when_failing_to_remove_targets_nonexception(self): - ''' + """ Tests absent on an rule when the remove_targets call fails - ''' - self.conn.list_rules.return_value = {'Rules': [rule_ret]} - self.conn.list_targets_by_rule.return_value = {'Targets': [{'Id': 'target1'}]} - self.conn.remove_targets.return_value = {'FailedEntryCount': 1} - result = self.salt_states['boto_cloudwatch_event.absent']( - name='test absent', - Name=rule_name, - **conn_parameters) - self.assertEqual(result.get('result'), False) + """ + self.conn.list_rules.return_value = {"Rules": [rule_ret]} + self.conn.list_targets_by_rule.return_value = {"Targets": [{"Id": "target1"}]} + self.conn.remove_targets.return_value = {"FailedEntryCount": 1} + result = self.salt_states["boto_cloudwatch_event.absent"]( + name="test absent", Name=rule_name, **conn_parameters + ) + self.assertEqual(result.get("result"), False) def test_absent_when_failing_to_delete_rule(self): - ''' + """ Tests absent on an rule when the delete_rule call fails - ''' - self.conn.list_rules.return_value = {'Rules': [rule_ret]} - self.conn.list_targets_by_rule.return_value = {'Targets': [{'Id': 'target1'}]} - self.conn.remove_targets.return_value = {'FailedEntryCount': 0} - self.conn.delete_rule.side_effect = ClientError(error_content, 'delete_rule') - result = self.salt_states['boto_cloudwatch_event.absent']( - name='test absent', - Name=rule_name, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('delete_rule' in result.get('comment', '')) + """ + self.conn.list_rules.return_value = {"Rules": [rule_ret]} + self.conn.list_targets_by_rule.return_value = {"Targets": [{"Id": "target1"}]} + self.conn.remove_targets.return_value = {"FailedEntryCount": 0} + self.conn.delete_rule.side_effect = ClientError(error_content, "delete_rule") + result = self.salt_states["boto_cloudwatch_event.absent"]( + name="test absent", Name=rule_name, **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("delete_rule" in result.get("comment", "")) def test_absent(self): - ''' + """ Tests absent on an rule - ''' - self.conn.list_rules.return_value = {'Rules': [rule_ret]} - self.conn.list_targets_by_rule.return_value = {'Targets': [{'Id': 'target1'}]} - self.conn.remove_targets.return_value = {'FailedEntryCount': 0} - result = self.salt_states['boto_cloudwatch_event.absent']( - name='test absent', - Name=rule_name, - **conn_parameters) - self.assertEqual(result.get('result'), True) + """ + self.conn.list_rules.return_value = {"Rules": [rule_ret]} + self.conn.list_targets_by_rule.return_value = {"Targets": [{"Id": "target1"}]} + self.conn.remove_targets.return_value = {"FailedEntryCount": 0} + result = self.salt_states["boto_cloudwatch_event.absent"]( + name="test absent", Name=rule_name, **conn_parameters + ) + self.assertEqual(result.get("result"), True) diff --git a/tests/unit/states/test_boto_cognitoidentity.py b/tests/unit/states/test_boto_cognitoidentity.py index 71439fc2de8..06efa9f0813 100644 --- a/tests/unit/states/test_boto_cognitoidentity.py +++ b/tests/unit/states/test_boto_cognitoidentity.py @@ -2,109 +2,130 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import random import string -import logging - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import patch, MagicMock # Import Salt libs import salt.config import salt.loader -from salt.utils.versions import LooseVersion import salt.states.boto_cognitoidentity as boto_cognitoidentity +from salt.ext.six.moves import range +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error,no-name-in-module -from tests.unit.modules.test_boto_cognitoidentity import BotoCognitoIdentityTestCaseMixin +from tests.unit.modules.test_boto_cognitoidentity import ( + BotoCognitoIdentityTestCaseMixin, +) # Import 3rd-party libs try: import boto3 from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False -from salt.ext.six.moves import range # pylint: enable=import-error,no-name-in-module # the boto_cognitoidentity module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' +required_boto3_version = "1.2.1" -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} -error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' -error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } +error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" +) +error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} -first_pool_id = 'first_pool_id' -first_pool_name = 'first_pool' -second_pool_id = 'second_pool_id' -second_pool_name = 'second_pool' -second_pool_name_updated = 'second_pool_updated' -third_pool_id = 'third_pool_id' +first_pool_id = "first_pool_id" +first_pool_name = "first_pool" +second_pool_id = "second_pool_id" +second_pool_name = "second_pool" +second_pool_name_updated = "second_pool_updated" +third_pool_id = "third_pool_id" third_pool_name = first_pool_name -default_pool_name = 'default_pool_name' -default_pool_id = 'default_pool_id' -default_dev_provider = 'test_provider_default' +default_pool_name = "default_pool_name" +default_pool_id = "default_pool_id" +default_dev_provider = "test_provider_default" -identity_pools_ret = dict(IdentityPools=[dict(IdentityPoolId=first_pool_id, - IdentityPoolName=first_pool_name), - dict(IdentityPoolId=second_pool_id, - IdentityPoolName=second_pool_name), - dict(IdentityPoolId=third_pool_id, - IdentityPoolName=third_pool_name)]) +identity_pools_ret = dict( + IdentityPools=[ + dict(IdentityPoolId=first_pool_id, IdentityPoolName=first_pool_name), + dict(IdentityPoolId=second_pool_id, IdentityPoolName=second_pool_name), + dict(IdentityPoolId=third_pool_id, IdentityPoolName=third_pool_name), + ] +) -first_pool_ret = dict(IdentityPoolId=first_pool_id, - IdentityPoolName=first_pool_name, - AllowUnauthenticatedIdentities=False, - SupportedLoginProviders={'accounts.google.com': 'testing123', - 'api.twitter.com': 'testing123', - 'graph.facebook.com': 'testing123', - 'www.amazon.com': 'testing123'}, - DeveloperProviderName='test_provider', - OpenIdConnectProviderARNs=['some_provider_arn', - 'another_provider_arn']) +first_pool_ret = dict( + IdentityPoolId=first_pool_id, + IdentityPoolName=first_pool_name, + AllowUnauthenticatedIdentities=False, + SupportedLoginProviders={ + "accounts.google.com": "testing123", + "api.twitter.com": "testing123", + "graph.facebook.com": "testing123", + "www.amazon.com": "testing123", + }, + DeveloperProviderName="test_provider", + OpenIdConnectProviderARNs=["some_provider_arn", "another_provider_arn"], +) -first_pool_role_ret = dict(IdentityPoolId=first_pool_id, - Roles=dict(authenticated='first_pool_auth_role', - unauthenticated='first_pool_unauth_role')) +first_pool_role_ret = dict( + IdentityPoolId=first_pool_id, + Roles=dict( + authenticated="first_pool_auth_role", unauthenticated="first_pool_unauth_role" + ), +) -second_pool_ret = dict(IdentityPoolId=second_pool_id, - IdentityPoolName=second_pool_name, - AllowUnauthenticatedIdentities=False) +second_pool_ret = dict( + IdentityPoolId=second_pool_id, + IdentityPoolName=second_pool_name, + AllowUnauthenticatedIdentities=False, +) -second_pool_role_ret = dict(IdentityPoolId=second_pool_id, - Roles=dict(authenticated='second_pool_auth_role')) +second_pool_role_ret = dict( + IdentityPoolId=second_pool_id, Roles=dict(authenticated="second_pool_auth_role") +) -second_pool_update_ret = dict(IdentityPoolId=second_pool_id, - IdentityPoolName=second_pool_name, - AllowUnauthenticatedIdentities=True) +second_pool_update_ret = dict( + IdentityPoolId=second_pool_id, + IdentityPoolName=second_pool_name, + AllowUnauthenticatedIdentities=True, +) -third_pool_ret = dict(IdentityPoolId=third_pool_id, - IdentityPoolName=third_pool_name, - AllowUnauthenticatedIdentities=False, - DeveloperProviderName='test_provider2') +third_pool_ret = dict( + IdentityPoolId=third_pool_id, + IdentityPoolName=third_pool_name, + AllowUnauthenticatedIdentities=False, + DeveloperProviderName="test_provider2", +) third_pool_role_ret = dict(IdentityPoolId=third_pool_id) -default_pool_ret = dict(IdentityPoolId=default_pool_id, - IdentityPoolName=default_pool_name, - AllowUnauthenticatedIdentities=False, - DeveloperProviderName=default_dev_provider) +default_pool_ret = dict( + IdentityPoolId=default_pool_id, + IdentityPoolName=default_pool_name, + AllowUnauthenticatedIdentities=False, + DeveloperProviderName=default_dev_provider, +) default_pool_role_ret = dict(IdentityPoolId=default_pool_id) @@ -113,10 +134,10 @@ log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -132,340 +153,468 @@ class BotoCognitoIdentityStateTestCaseBase(TestCase, LoaderModuleMockMixin): ctx = {} utils = salt.loader.utils( self.opts, - whitelist=['boto', 'boto3', 'args', 'systemd', 'path', 'platform', 'reg'], - context=ctx) + whitelist=["boto", "boto3", "args", "systemd", "path", "platform", "reg"], + context=ctx, + ) serializers = salt.loader.serializers(self.opts) - self.funcs = funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_cognitoidentity']) - self.salt_states = salt.loader.states(opts=self.opts, functions=funcs, utils=utils, whitelist=['boto_cognitoidentity'], - serializers=serializers) + self.funcs = funcs = salt.loader.minion_mods( + self.opts, context=ctx, utils=utils, whitelist=["boto_cognitoidentity"] + ) + self.salt_states = salt.loader.states( + opts=self.opts, + functions=funcs, + utils=utils, + whitelist=["boto_cognitoidentity"], + serializers=serializers, + ) return { boto_cognitoidentity: { - '__opts__': self.opts, - '__salt__': funcs, - '__utils__': utils, - '__states__': self.salt_states, - '__serializers__': serializers, + "__opts__": self.opts, + "__salt__": funcs, + "__utils__": utils, + "__states__": self.salt_states, + "__serializers__": serializers, } } @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) @classmethod def tearDownClass(cls): del cls.opts def setUp(self): - self.addCleanup(delattr, self, 'funcs') - self.addCleanup(delattr, self, 'salt_states') + self.addCleanup(delattr, self, "funcs") + self.addCleanup(delattr, self, "salt_states") # Set up MagicMock to replace the boto3 session # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -class BotoCognitoIdentityTestCase(BotoCognitoIdentityStateTestCaseBase, BotoCognitoIdentityTestCaseMixin): - ''' +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) +class BotoCognitoIdentityTestCase( + BotoCognitoIdentityStateTestCaseBase, BotoCognitoIdentityTestCaseMixin +): + """ TestCase for salt.states.boto_cognitoidentity state.module - ''' + """ + def _describe_identity_pool_side_effect(self, *args, **kwargs): - if kwargs.get('IdentityPoolId') == first_pool_id: + if kwargs.get("IdentityPoolId") == first_pool_id: return first_pool_ret - elif kwargs.get('IdentityPoolId') == second_pool_id: + elif kwargs.get("IdentityPoolId") == second_pool_id: return second_pool_ret - elif kwargs.get('IdentityPoolId') == third_pool_id: + elif kwargs.get("IdentityPoolId") == third_pool_id: return third_pool_ret else: return default_pool_ret def test_present_when_failing_to_describe_identity_pools(self): - ''' + """ Tests exceptions when describing identity pools - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = ClientError(error_content, 'error on describe identity pool') - result = self.salt_states['boto_cognitoidentity.pool_present']( - name='test pool present', - IdentityPoolName=first_pool_name, - AuthenticatedRole='my_auth_role', - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('error on describe identity pool' in result.get('comment', {})) + self.conn.describe_identity_pool.side_effect = ClientError( + error_content, "error on describe identity pool" + ) + result = self.salt_states["boto_cognitoidentity.pool_present"]( + name="test pool present", + IdentityPoolName=first_pool_name, + AuthenticatedRole="my_auth_role", + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("error on describe identity pool" in result.get("comment", {})) def test_present_when_multiple_pools_with_same_name_exist(self): - ''' + """ Tests present on an identity pool name where it matched multiple pools. The result should fail. - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect - result = self.salt_states['boto_cognitoidentity.pool_present']( - name='test pool present', - IdentityPoolName=first_pool_name, - AuthenticatedRole='my_auth_role', - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertIn('{0}'.format([first_pool_ret, third_pool_ret]), result.get('comment', '')) + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) + result = self.salt_states["boto_cognitoidentity.pool_present"]( + name="test pool present", + IdentityPoolName=first_pool_name, + AuthenticatedRole="my_auth_role", + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertIn( + "{0}".format([first_pool_ret, third_pool_ret]), result.get("comment", "") + ) def test_present_when_failing_to_create_a_new_identity_pool(self): - ''' + """ Tests present on an identity pool name that doesn't exist and an error is thrown on creation. - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect - self.conn.create_identity_pool.side_effect = ClientError(error_content, 'error on create_identity_pool') - result = self.salt_states['boto_cognitoidentity.pool_present']( - name='test pool present', - IdentityPoolName=default_pool_name, - AuthenticatedRole='my_auth_role', - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('error on create_identity_pool' in result.get('comment', '')) + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) + self.conn.create_identity_pool.side_effect = ClientError( + error_content, "error on create_identity_pool" + ) + result = self.salt_states["boto_cognitoidentity.pool_present"]( + name="test pool present", + IdentityPoolName=default_pool_name, + AuthenticatedRole="my_auth_role", + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("error on create_identity_pool" in result.get("comment", "")) self.assertTrue(self.conn.update_identity_pool.call_count == 0) def test_present_when_failing_to_update_an_existing_identity_pool(self): - ''' + """ Tests present on a unique instance of identity pool having the matching IdentityPoolName, and an error is thrown on updating the pool properties. - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect - self.conn.update_identity_pool.side_effect = ClientError(error_content, 'error on update_identity_pool') - result = self.salt_states['boto_cognitoidentity.pool_present']( - name='test pool present', - IdentityPoolName=second_pool_name, - AuthenticatedRole='my_auth_role', - AllowUnauthenticatedIdentities=True, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('error on update_identity_pool' in result.get('comment', '')) + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) + self.conn.update_identity_pool.side_effect = ClientError( + error_content, "error on update_identity_pool" + ) + result = self.salt_states["boto_cognitoidentity.pool_present"]( + name="test pool present", + IdentityPoolName=second_pool_name, + AuthenticatedRole="my_auth_role", + AllowUnauthenticatedIdentities=True, + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("error on update_identity_pool" in result.get("comment", "")) self.assertTrue(self.conn.create_identity_pool.call_count == 0) def _get_identity_pool_roles_side_effect(self, *args, **kwargs): - if kwargs.get('IdentityPoolId') == first_pool_id: + if kwargs.get("IdentityPoolId") == first_pool_id: return first_pool_role_ret - elif kwargs.get('IdentityPoolId') == second_pool_id: + elif kwargs.get("IdentityPoolId") == second_pool_id: return second_pool_role_ret - elif kwargs.get('IdentityPoolId') == third_pool_id: + elif kwargs.get("IdentityPoolId") == third_pool_id: return third_pool_role_ret else: return default_pool_role_ret def test_present_when_failing_to_get_identity_pool_roles(self): - ''' + """ Tests present on a unique instance of identity pool having the matching IdentityPoolName, where update_identity_pool succeeded, but an error is thrown on getting the identity pool role prior to setting the roles. - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) self.conn.update_identity_pool.return_value = second_pool_update_ret - self.conn.get_identity_pool_roles.side_effect = ClientError(error_content, 'error on get_identity_pool_roles') - result = self.salt_states['boto_cognitoidentity.pool_present']( - name='test pool present', - IdentityPoolName=second_pool_name, - AuthenticatedRole='my_auth_role', - AllowUnauthenticatedIdentities=True, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('error on get_identity_pool_roles' in result.get('comment', '')) + self.conn.get_identity_pool_roles.side_effect = ClientError( + error_content, "error on get_identity_pool_roles" + ) + result = self.salt_states["boto_cognitoidentity.pool_present"]( + name="test pool present", + IdentityPoolName=second_pool_name, + AuthenticatedRole="my_auth_role", + AllowUnauthenticatedIdentities=True, + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("error on get_identity_pool_roles" in result.get("comment", "")) self.assertTrue(self.conn.create_identity_pool.call_count == 0) self.assertTrue(self.conn.set_identity_pool_roles.call_count == 0) def test_present_when_failing_to_set_identity_pool_roles(self): - ''' + """ Tests present on a unique instance of identity pool having the matching IdentityPoolName, where update_identity_pool succeeded, but an error is thrown on setting the identity pool role. - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) self.conn.update_identity_pool.return_value = second_pool_update_ret self.conn.get_identity_pool_roles.return_value = second_pool_role_ret - self.conn.set_identity_pool_roles.side_effect = ClientError(error_content, 'error on set_identity_pool_roles') - with patch.dict(self.funcs, {'boto_iam.describe_role': MagicMock(return_value={'arn': 'my_auth_role_arn'})}): - result = self.salt_states['boto_cognitoidentity.pool_present']( - name='test pool present', - IdentityPoolName=second_pool_name, - AuthenticatedRole='my_auth_role', - AllowUnauthenticatedIdentities=True, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('error on set_identity_pool_roles' in result.get('comment', '')) - expected_call_args = (dict(IdentityPoolId=second_pool_id, - Roles={'authenticated': 'my_auth_role_arn'}),) - self.assertTrue(self.conn.set_identity_pool_roles.call_args == expected_call_args) + self.conn.set_identity_pool_roles.side_effect = ClientError( + error_content, "error on set_identity_pool_roles" + ) + with patch.dict( + self.funcs, + { + "boto_iam.describe_role": MagicMock( + return_value={"arn": "my_auth_role_arn"} + ) + }, + ): + result = self.salt_states["boto_cognitoidentity.pool_present"]( + name="test pool present", + IdentityPoolName=second_pool_name, + AuthenticatedRole="my_auth_role", + AllowUnauthenticatedIdentities=True, + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue( + "error on set_identity_pool_roles" in result.get("comment", "") + ) + expected_call_args = ( + dict( + IdentityPoolId=second_pool_id, + Roles={"authenticated": "my_auth_role_arn"}, + ), + ) + self.assertTrue( + self.conn.set_identity_pool_roles.call_args == expected_call_args + ) def test_present_when_pool_name_does_not_exist(self): - ''' + """ Tests the successful case of creating a new instance, and updating its roles - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.create_identity_pool.side_effect = self._describe_identity_pool_side_effect + self.conn.create_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) self.conn.get_identity_pool_roles.return_value = default_pool_role_ret self.conn.set_identity_pool_roles.return_value = None - with patch.dict(self.funcs, {'boto_iam.describe_role': MagicMock(return_value={'arn': 'my_auth_role_arn'})}): - result = self.salt_states['boto_cognitoidentity.pool_present']( - name='test pool present', - IdentityPoolName=default_pool_name, - AuthenticatedRole='my_auth_role', - AllowUnauthenticatedIdentities=True, - DeveloperProviderName=default_dev_provider, - **conn_parameters) - self.assertEqual(result.get('result'), True) - expected_call_args = (dict(AllowUnauthenticatedIdentities=True, - IdentityPoolName=default_pool_name, - DeveloperProviderName=default_dev_provider, - SupportedLoginProviders={}, - OpenIdConnectProviderARNs=[]),) - self.assertTrue(self.conn.create_identity_pool.call_args == expected_call_args) - expected_call_args = (dict(IdentityPoolId=default_pool_id, - Roles={'authenticated': 'my_auth_role_arn'}),) - self.assertTrue(self.conn.set_identity_pool_roles.call_args == expected_call_args) + with patch.dict( + self.funcs, + { + "boto_iam.describe_role": MagicMock( + return_value={"arn": "my_auth_role_arn"} + ) + }, + ): + result = self.salt_states["boto_cognitoidentity.pool_present"]( + name="test pool present", + IdentityPoolName=default_pool_name, + AuthenticatedRole="my_auth_role", + AllowUnauthenticatedIdentities=True, + DeveloperProviderName=default_dev_provider, + **conn_parameters + ) + self.assertEqual(result.get("result"), True) + expected_call_args = ( + dict( + AllowUnauthenticatedIdentities=True, + IdentityPoolName=default_pool_name, + DeveloperProviderName=default_dev_provider, + SupportedLoginProviders={}, + OpenIdConnectProviderARNs=[], + ), + ) + self.assertTrue( + self.conn.create_identity_pool.call_args == expected_call_args + ) + expected_call_args = ( + dict( + IdentityPoolId=default_pool_id, + Roles={"authenticated": "my_auth_role_arn"}, + ), + ) + self.assertTrue( + self.conn.set_identity_pool_roles.call_args == expected_call_args + ) self.assertTrue(self.conn.update_identity_pool.call_count == 0) def test_present_when_pool_name_exists(self): - ''' + """ Tests the successful case of updating a single instance with matching IdentityPoolName and its roles. - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) self.conn.update_identity_pool.return_value = second_pool_update_ret self.conn.get_identity_pool_roles.return_value = second_pool_role_ret self.conn.set_identity_pool_roles.return_value = None - with patch.dict(self.funcs, {'boto_iam.describe_role': MagicMock(return_value={'arn': 'my_auth_role_arn'})}): - result = self.salt_states['boto_cognitoidentity.pool_present']( - name='test pool present', - IdentityPoolName=second_pool_name, - AuthenticatedRole='my_auth_role', - AllowUnauthenticatedIdentities=True, - **conn_parameters) - self.assertEqual(result.get('result'), True) - expected_call_args = (dict(AllowUnauthenticatedIdentities=True, - IdentityPoolId=second_pool_id, - IdentityPoolName=second_pool_name),) - self.assertTrue(self.conn.update_identity_pool.call_args == expected_call_args) - expected_call_args = (dict(IdentityPoolId=second_pool_id, - Roles={'authenticated': 'my_auth_role_arn'}),) - self.assertTrue(self.conn.set_identity_pool_roles.call_args == expected_call_args) + with patch.dict( + self.funcs, + { + "boto_iam.describe_role": MagicMock( + return_value={"arn": "my_auth_role_arn"} + ) + }, + ): + result = self.salt_states["boto_cognitoidentity.pool_present"]( + name="test pool present", + IdentityPoolName=second_pool_name, + AuthenticatedRole="my_auth_role", + AllowUnauthenticatedIdentities=True, + **conn_parameters + ) + self.assertEqual(result.get("result"), True) + expected_call_args = ( + dict( + AllowUnauthenticatedIdentities=True, + IdentityPoolId=second_pool_id, + IdentityPoolName=second_pool_name, + ), + ) + self.assertTrue( + self.conn.update_identity_pool.call_args == expected_call_args + ) + expected_call_args = ( + dict( + IdentityPoolId=second_pool_id, + Roles={"authenticated": "my_auth_role_arn"}, + ), + ) + self.assertTrue( + self.conn.set_identity_pool_roles.call_args == expected_call_args + ) self.assertTrue(self.conn.create_identity_pool.call_count == 0) def test_absent_when_pool_does_not_exist(self): - ''' + """ Tests absent on an identity pool that does not exist. - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - result = self.salt_states['boto_cognitoidentity.pool_absent']( - name='test pool absent', - IdentityPoolName='no_such_pool_name', - RemoveAllMatched=False, - **conn_parameters) - self.assertEqual(result.get('result'), True) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_cognitoidentity.pool_absent"]( + name="test pool absent", + IdentityPoolName="no_such_pool_name", + RemoveAllMatched=False, + **conn_parameters + ) + self.assertEqual(result.get("result"), True) + self.assertEqual(result["changes"], {}) def test_absent_when_removeallmatched_is_false_and_multiple_pools_matched(self): - ''' + """ Tests absent on when RemoveAllMatched flag is false and there are multiple matches for the given pool name first_pool_name is matched to first and third pool with different id's - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect - result = self.salt_states['boto_cognitoidentity.pool_absent']( - name='test pool absent', - IdentityPoolName=first_pool_name, - RemoveAllMatched=False, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertEqual(result['changes'], {}) - self.assertTrue('{0}'.format([first_pool_ret, third_pool_ret]) in result.get('comment', '')) + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) + result = self.salt_states["boto_cognitoidentity.pool_absent"]( + name="test pool absent", + IdentityPoolName=first_pool_name, + RemoveAllMatched=False, + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertEqual(result["changes"], {}) + self.assertTrue( + "{0}".format([first_pool_ret, third_pool_ret]) in result.get("comment", "") + ) def test_absent_when_failing_to_describe_identity_pools(self): - ''' + """ Tests exceptions when describing identity pools - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = ClientError(error_content, 'error on describe identity pool') - result = self.salt_states['boto_cognitoidentity.pool_absent']( - name='test pool absent', - IdentityPoolName=first_pool_name, - RemoveAllMatched=False, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertTrue('error on describe identity pool' in result.get('comment', {})) + self.conn.describe_identity_pool.side_effect = ClientError( + error_content, "error on describe identity pool" + ) + result = self.salt_states["boto_cognitoidentity.pool_absent"]( + name="test pool absent", + IdentityPoolName=first_pool_name, + RemoveAllMatched=False, + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertTrue("error on describe identity pool" in result.get("comment", {})) def test_absent_when_erroring_on_delete_identity_pool(self): - ''' + """ Tests error due to delete_identity_pools - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect - self.conn.delete_identity_pool.side_effect = ClientError(error_content, 'error on delete identity pool') - result = self.salt_states['boto_cognitoidentity.pool_absent']( - name='test pool absent', - IdentityPoolName=first_pool_name, - RemoveAllMatched=True, - **conn_parameters) - self.assertEqual(result.get('result'), False) - self.assertEqual(result['changes'], {}) - self.assertTrue('error on delete identity pool' in result.get('comment', '')) + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) + self.conn.delete_identity_pool.side_effect = ClientError( + error_content, "error on delete identity pool" + ) + result = self.salt_states["boto_cognitoidentity.pool_absent"]( + name="test pool absent", + IdentityPoolName=first_pool_name, + RemoveAllMatched=True, + **conn_parameters + ) + self.assertEqual(result.get("result"), False) + self.assertEqual(result["changes"], {}) + self.assertTrue("error on delete identity pool" in result.get("comment", "")) def test_absent_when_a_single_pool_exists(self): - ''' + """ Tests absent succeeds on delete when a single pool matched and RemoveAllMatched is False - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret self.conn.describe_identity_pool.return_value = second_pool_ret self.conn.delete_identity_pool.return_value = None - result = self.salt_states['boto_cognitoidentity.pool_absent']( - name='test pool absent', - IdentityPoolName=second_pool_name, - RemoveAllMatched=False, - **conn_parameters) - self.assertEqual(result.get('result'), True) - expected_changes = {'new': {'Identity Pool Id {0}'.format(second_pool_id): None}, - 'old': {'Identity Pool Id {0}'.format(second_pool_id): second_pool_name}} - self.assertEqual(result['changes'], expected_changes) + result = self.salt_states["boto_cognitoidentity.pool_absent"]( + name="test pool absent", + IdentityPoolName=second_pool_name, + RemoveAllMatched=False, + **conn_parameters + ) + self.assertEqual(result.get("result"), True) + expected_changes = { + "new": {"Identity Pool Id {0}".format(second_pool_id): None}, + "old": {"Identity Pool Id {0}".format(second_pool_id): second_pool_name}, + } + self.assertEqual(result["changes"], expected_changes) def test_absent_when_multiple_pool_exists_and_removeallmatched_flag_is_true(self): - ''' + """ Tests absent succeeds on delete when a multiple pools matched and RemoveAllMatched is True first_pool_name should match to first_pool_id and third_pool_id - ''' + """ self.conn.list_identity_pools.return_value = identity_pools_ret - self.conn.describe_identity_pool.side_effect = self._describe_identity_pool_side_effect + self.conn.describe_identity_pool.side_effect = ( + self._describe_identity_pool_side_effect + ) self.conn.delete_identity_pool.return_value = None - result = self.salt_states['boto_cognitoidentity.pool_absent']( - name='test pool absent', - IdentityPoolName=first_pool_name, - RemoveAllMatched=True, - **conn_parameters) - self.assertEqual(result.get('result'), True) - expected_changes = {'new': {'Identity Pool Id {0}'.format(first_pool_id): None, - 'Identity Pool Id {0}'.format(third_pool_id): None}, - 'old': {'Identity Pool Id {0}'.format(first_pool_id): first_pool_name, - 'Identity Pool Id {0}'.format(third_pool_id): third_pool_name}} - self.assertEqual(result['changes'], expected_changes) + result = self.salt_states["boto_cognitoidentity.pool_absent"]( + name="test pool absent", + IdentityPoolName=first_pool_name, + RemoveAllMatched=True, + **conn_parameters + ) + self.assertEqual(result.get("result"), True) + expected_changes = { + "new": { + "Identity Pool Id {0}".format(first_pool_id): None, + "Identity Pool Id {0}".format(third_pool_id): None, + }, + "old": { + "Identity Pool Id {0}".format(first_pool_id): first_pool_name, + "Identity Pool Id {0}".format(third_pool_id): third_pool_name, + }, + } + self.assertEqual(result["changes"], expected_changes) diff --git a/tests/unit/states/test_boto_dynamodb.py b/tests/unit/states/test_boto_dynamodb.py index e94f0786bc5..23b87faf97a 100644 --- a/tests/unit/states/test_boto_dynamodb.py +++ b/tests/unit/states/test_boto_dynamodb.py @@ -1,112 +1,117 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.boto_dynamodb as boto_dynamodb +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoDynamodbTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_dynamodb - ''' + """ + def setup_loader_modules(self): return {boto_dynamodb: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the DynamoDB table exists. - ''' - name = 'new_table' + """ + name = "new_table" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} exists_mock = MagicMock(side_effect=[True, False, False]) dict_mock = MagicMock(return_value={}) mock_bool = MagicMock(return_value=True) pillar_mock = MagicMock(return_value=[]) - with patch.dict(boto_dynamodb.__salt__, - {'boto_dynamodb.exists': exists_mock, - 'boto_dynamodb.describe': dict_mock, - 'config.option': dict_mock, - 'pillar.get': pillar_mock, - 'boto_dynamodb.create_table': mock_bool}): - comt = ('DynamoDB table {0} exists,\n' - 'DynamoDB table {0} throughput matches,\n' - 'All global secondary indexes match,\n'.format(name)) - ret.update({'comment': comt}) + with patch.dict( + boto_dynamodb.__salt__, + { + "boto_dynamodb.exists": exists_mock, + "boto_dynamodb.describe": dict_mock, + "config.option": dict_mock, + "pillar.get": pillar_mock, + "boto_dynamodb.create_table": mock_bool, + }, + ): + comt = ( + "DynamoDB table {0} exists,\n" + "DynamoDB table {0} throughput matches,\n" + "All global secondary indexes match,\n".format(name) + ) + ret.update({"comment": comt}) self.assertDictEqual(boto_dynamodb.present(name), ret) - with patch.dict(boto_dynamodb.__opts__, {'test': True}): - comt = ('DynamoDB table {0} would be created.'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(boto_dynamodb.__opts__, {"test": True}): + comt = "DynamoDB table {0} would be created.".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_dynamodb.present(name), ret) - changes = {'new': {'global_indexes': None, - 'hash_key': None, - 'hash_key_data_type': None, - 'local_indexes': None, - 'range_key': None, - 'range_key_data_type': None, - 'read_capacity_units': None, - 'table': 'new_table', - 'write_capacity_units': None}} + changes = { + "new": { + "global_indexes": None, + "hash_key": None, + "hash_key_data_type": None, + "local_indexes": None, + "range_key": None, + "range_key_data_type": None, + "read_capacity_units": None, + "table": "new_table", + "write_capacity_units": None, + } + } - with patch.dict(boto_dynamodb.__opts__, {'test': False}): - comt = ('DynamoDB table {0} was successfully created,\n' - 'DynamoDB table new_table throughput matches,\n' - .format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': changes}) + with patch.dict(boto_dynamodb.__opts__, {"test": False}): + comt = ( + "DynamoDB table {0} was successfully created,\n" + "DynamoDB table new_table throughput matches,\n".format(name) + ) + ret.update({"comment": comt, "result": True, "changes": changes}) self.assertDictEqual(ret, boto_dynamodb.present(name)) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the DynamoDB table does not exist. - ''' - name = 'new_table' + """ + name = "new_table" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[False, True, True]) mock_bool = MagicMock(return_value=True) - with patch.dict(boto_dynamodb.__salt__, - {'boto_dynamodb.exists': mock, - 'boto_dynamodb.delete': mock_bool}): - comt = ('DynamoDB table {0} does not exist'.format(name)) - ret.update({'comment': comt}) + with patch.dict( + boto_dynamodb.__salt__, + {"boto_dynamodb.exists": mock, "boto_dynamodb.delete": mock_bool}, + ): + comt = "DynamoDB table {0} does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_dynamodb.absent(name), ret) - with patch.dict(boto_dynamodb.__opts__, {'test': True}): - comt = 'DynamoDB table {0} is set to be deleted'.format(name) - ret.update({'comment': comt, 'result': None}) + with patch.dict(boto_dynamodb.__opts__, {"test": True}): + comt = "DynamoDB table {0} is set to be deleted".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_dynamodb.absent(name), ret) - changes = {'new': 'Table new_table deleted', - 'old': 'Table new_table exists'} + changes = { + "new": "Table new_table deleted", + "old": "Table new_table exists", + } - with patch.dict(boto_dynamodb.__opts__, {'test': False}): - comt = ('Deleted DynamoDB table {0}'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': changes}) + with patch.dict(boto_dynamodb.__opts__, {"test": False}): + comt = "Deleted DynamoDB table {0}".format(name) + ret.update({"comment": comt, "result": True, "changes": changes}) self.assertDictEqual(boto_dynamodb.absent(name), ret) diff --git a/tests/unit/states/test_boto_ec2.py b/tests/unit/states/test_boto_ec2.py index 20bc53d2eae..42ef9e8b95e 100644 --- a/tests/unit/states/test_boto_ec2.py +++ b/tests/unit/states/test_boto_ec2.py @@ -1,80 +1,75 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.states.boto_ec2 as boto_ec2 +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoEc2TestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_ec2 - ''' + """ + def setup_loader_modules(self): return {boto_ec2: {}} # 'key_present' function tests: 1 def test_key_present(self): - ''' + """ Test to ensure key pair is present. - ''' - name = 'mykeypair' - upublic = 'salt://mybase/public_key.pub' + """ + name = "mykeypair" + upublic = "salt://mybase/public_key.pub" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False, False]) mock_bool = MagicMock(side_effect=[IOError, True]) - with patch.dict(boto_ec2.__salt__, {'boto_ec2.get_key': mock, - 'cp.get_file_str': mock_bool}): - comt = ('The key name {0} already exists'.format(name)) - ret.update({'comment': comt}) + with patch.dict( + boto_ec2.__salt__, {"boto_ec2.get_key": mock, "cp.get_file_str": mock_bool} + ): + comt = "The key name {0} already exists".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_ec2.key_present(name), ret) - comt = ('File {0} not found.'.format(upublic)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(boto_ec2.key_present(name, - upload_public=upublic), - ret) + comt = "File {0} not found.".format(upublic) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual(boto_ec2.key_present(name, upload_public=upublic), ret) - with patch.dict(boto_ec2.__opts__, {'test': True}): - comt = ('The key {0} is set to be created.'.format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(boto_ec2.key_present - (name, upload_public=upublic), ret) + with patch.dict(boto_ec2.__opts__, {"test": True}): + comt = "The key {0} is set to be created.".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + boto_ec2.key_present(name, upload_public=upublic), ret + ) # 'key_absent' function tests: 1 def test_key_absent(self): - ''' + """ Test to deletes a key pair - ''' - name = 'new_table' + """ + name = "new_table" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[False, True]) - with patch.dict(boto_ec2.__salt__, {'boto_ec2.get_key': mock}): - comt = ('The key name {0} does not exist'.format(name)) - ret.update({'comment': comt}) + with patch.dict(boto_ec2.__salt__, {"boto_ec2.get_key": mock}): + comt = "The key name {0} does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_ec2.key_absent(name), ret) - with patch.dict(boto_ec2.__opts__, {'test': True}): - comt = ('The key {0} is set to be deleted.'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(boto_ec2.__opts__, {"test": True}): + comt = "The key {0} is set to be deleted.".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_ec2.key_absent(name), ret) diff --git a/tests/unit/states/test_boto_elasticache.py b/tests/unit/states/test_boto_elasticache.py index 56c4c3c36f9..abea2e4f69e 100644 --- a/tests/unit/states/test_boto_elasticache.py +++ b/tests/unit/states/test_boto_elasticache.py @@ -1,116 +1,113 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.states.boto_elasticache as boto_elasticache +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoElasticacheTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_elasticache - ''' + """ + def setup_loader_modules(self): return {boto_elasticache: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the cache cluster exists. - ''' - name = 'myelasticache' - engine = 'redis' - cache_node_type = 'cache.t1.micro' + """ + name = "myelasticache" + engine = "redis" + cache_node_type = "cache.t1.micro" - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[None, False, False, True]) mock_bool = MagicMock(return_value=False) - with patch.dict(boto_elasticache.__salt__, - {'boto_elasticache.get_config': mock, - 'boto_elasticache.create': mock_bool}): - comt = ('Failed to retrieve cache cluster info from AWS.') - ret.update({'comment': comt}) - self.assertDictEqual(boto_elasticache.present(name, engine, - cache_node_type), ret) + with patch.dict( + boto_elasticache.__salt__, + {"boto_elasticache.get_config": mock, "boto_elasticache.create": mock_bool}, + ): + comt = "Failed to retrieve cache cluster info from AWS." + ret.update({"comment": comt}) + self.assertDictEqual( + boto_elasticache.present(name, engine, cache_node_type), ret + ) - with patch.dict(boto_elasticache.__opts__, {'test': True}): - comt = ('Cache cluster {0} is set to be created.'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(boto_elasticache.present(name, engine, - cache_node_type), - ret) + with patch.dict(boto_elasticache.__opts__, {"test": True}): + comt = "Cache cluster {0} is set to be created.".format(name) + ret.update({"comment": comt}) + self.assertDictEqual( + boto_elasticache.present(name, engine, cache_node_type), ret + ) - with patch.dict(boto_elasticache.__opts__, {'test': False}): - comt = ('Failed to create {0} cache cluster.'.format(name)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(boto_elasticache.present(name, engine, - cache_node_type), - ret) + with patch.dict(boto_elasticache.__opts__, {"test": False}): + comt = "Failed to create {0} cache cluster.".format(name) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + boto_elasticache.present(name, engine, cache_node_type), ret + ) - comt = ('Cache cluster {0} is present.'.format(name)) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(boto_elasticache.present(name, engine, - cache_node_type), - ret) + comt = "Cache cluster {0} is present.".format(name) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + boto_elasticache.present(name, engine, cache_node_type), ret + ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the named elasticache cluster is deleted. - ''' - name = 'new_table' + """ + name = "new_table" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[False, True]) - with patch.dict(boto_elasticache.__salt__, - {'boto_elasticache.exists': mock}): - comt = ('{0} does not exist in None.'.format(name)) - ret.update({'comment': comt}) + with patch.dict(boto_elasticache.__salt__, {"boto_elasticache.exists": mock}): + comt = "{0} does not exist in None.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_elasticache.absent(name), ret) - with patch.dict(boto_elasticache.__opts__, {'test': True}): - comt = ('Cache cluster {0} is set to be removed.'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(boto_elasticache.__opts__, {"test": True}): + comt = "Cache cluster {0} is set to be removed.".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_elasticache.absent(name), ret) # 'creategroup' function tests: 1 def test_creategroup(self): - ''' + """ Test to ensure the a replication group is create. - ''' - name = 'new_table' - primary_cluster_id = 'A' - replication_group_description = 'my description' + """ + name = "new_table" + primary_cluster_id = "A" + replication_group_description = "my description" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(return_value=True) - with patch.dict(boto_elasticache.__salt__, - {'boto_elasticache.group_exists': mock}): - comt = ('{0} replication group exists .'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(boto_elasticache.creategroup - (name, primary_cluster_id, - replication_group_description), ret) + with patch.dict( + boto_elasticache.__salt__, {"boto_elasticache.group_exists": mock} + ): + comt = "{0} replication group exists .".format(name) + ret.update({"comment": comt}) + self.assertDictEqual( + boto_elasticache.creategroup( + name, primary_cluster_id, replication_group_description + ), + ret, + ) diff --git a/tests/unit/states/test_boto_elasticsearch_domain.py b/tests/unit/states/test_boto_elasticsearch_domain.py index 2484a2cccde..e279c6a7ae1 100644 --- a/tests/unit/states/test_boto_elasticsearch_domain.py +++ b/tests/unit/states/test_boto_elasticsearch_domain.py @@ -2,32 +2,39 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import random import string -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch +import salt.loader +import salt.states.boto_elasticsearch_domain as boto_elasticsearch_domain # Import Salt libs from salt.ext import six -import salt.loader -from salt.utils.versions import LooseVersion -import salt.states.boto_elasticsearch_domain as boto_elasticsearch_domain - -# Import test suite libs - -# pylint: disable=import-error,no-name-in-module,unused-import -from tests.unit.modules.test_boto_elasticsearch_domain import BotoElasticsearchDomainTestCaseMixin # Import 3rd-party libs from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + +# pylint: disable=import-error,no-name-in-module,unused-import +from tests.unit.modules.test_boto_elasticsearch_domain import ( + BotoElasticsearchDomainTestCaseMixin, +) + +# Import test suite libs + + try: import boto import boto3 from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -37,16 +44,16 @@ except ImportError: # the boto_elasticsearch_domain module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' +required_boto3_version = "1.2.1" log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -56,31 +63,37 @@ def _has_required_boto(): if _has_required_boto(): - region = 'us-east-1' - access_key = 'GKTADJGHEIQSXMKKRBJ08H' - secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' - conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} - error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' - not_found_error = ClientError({ - 'Error': { - 'Code': 'ResourceNotFoundException', - 'Message': "Test-defined error" - } - }, 'msg') - error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } + region = "us-east-1" + access_key = "GKTADJGHEIQSXMKKRBJ08H" + secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" + conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } - domain_ret = dict(DomainName='testdomain', - ElasticsearchClusterConfig={}, - EBSOptions={}, - AccessPolicies={}, - SnapshotOptions={}, - AdvancedOptions={}, - ElasticsearchVersion='1.5', - ) + error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" + ) + not_found_error = ClientError( + { + "Error": { + "Code": "ResourceNotFoundException", + "Message": "Test-defined error", + } + }, + "msg", + ) + error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} + domain_ret = dict( + DomainName="testdomain", + ElasticsearchClusterConfig={}, + EBSOptions={}, + AccessPolicies={}, + SnapshotOptions={}, + AdvancedOptions={}, + ElasticsearchVersion="1.5", + ) class BotoElasticsearchDomainStateTestCaseBase(TestCase, LoaderModuleMockMixin): @@ -90,26 +103,34 @@ class BotoElasticsearchDomainStateTestCaseBase(TestCase, LoaderModuleMockMixin): ctx = {} utils = salt.loader.utils( self.opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context=ctx) + whitelist=["boto3", "args", "systemd", "path", "platform"], + context=ctx, + ) serializers = salt.loader.serializers(self.opts) - self.funcs = funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_elasticsearch_domain']) - self.salt_states = salt.loader.states(opts=self.opts, functions=funcs, utils=utils, whitelist=['boto_elasticsearch_domain'], - serializers=serializers) + self.funcs = funcs = salt.loader.minion_mods( + self.opts, context=ctx, utils=utils, whitelist=["boto_elasticsearch_domain"] + ) + self.salt_states = salt.loader.states( + opts=self.opts, + functions=funcs, + utils=utils, + whitelist=["boto_elasticsearch_domain"], + serializers=serializers, + ) return { boto_elasticsearch_domain: { - '__opts__': self.opts, - '__salt__': funcs, - '__utils__': utils, - '__states__': self.salt_states, - '__serializers__': serializers, + "__opts__": self.opts, + "__salt__": funcs, + "__utils__": utils, + "__states__": self.salt_states, + "__serializers__": serializers, } } @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) @classmethod def tearDownClass(cls): @@ -117,92 +138,133 @@ class BotoElasticsearchDomainStateTestCaseBase(TestCase, LoaderModuleMockMixin): # Set up MagicMock to replace the boto3 session def setUp(self): - self.addCleanup(delattr, self, 'funcs') - self.addCleanup(delattr, self, 'salt_states') + self.addCleanup(delattr, self, "funcs") + self.addCleanup(delattr, self, "salt_states") # Set up MagicMock to replace the boto3 session # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) -class BotoElasticsearchDomainTestCase(BotoElasticsearchDomainStateTestCaseBase, BotoElasticsearchDomainTestCaseMixin): - ''' +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) +class BotoElasticsearchDomainTestCase( + BotoElasticsearchDomainStateTestCaseBase, BotoElasticsearchDomainTestCaseMixin +): + """ TestCase for salt.modules.boto_elasticsearch_domain state.module - ''' + """ def test_present_when_domain_does_not_exist(self): - ''' + """ Tests present on a domain that does not exist. - ''' + """ self.conn.describe_elasticsearch_domain.side_effect = not_found_error - self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': domain_ret} - self.conn.create_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - result = self.salt_states['boto_elasticsearch_domain.present']( - 'domain present', - **domain_ret) + self.conn.describe_elasticsearch_domain_config.return_value = { + "DomainConfig": domain_ret + } + self.conn.create_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + result = self.salt_states["boto_elasticsearch_domain.present"]( + "domain present", **domain_ret + ) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['domain']['ElasticsearchClusterConfig'], None) + self.assertTrue(result["result"]) + self.assertEqual( + result["changes"]["new"]["domain"]["ElasticsearchClusterConfig"], None + ) def test_present_when_domain_exists(self): - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } cfg = {} for k, v in six.iteritems(domain_ret): - cfg[k] = {'Options': v} - cfg['AccessPolicies'] = {'Options': '{"a": "b"}'} - self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': cfg} - self.conn.update_elasticsearch_domain_config.return_value = {'DomainConfig': cfg} - result = self.salt_states['boto_elasticsearch_domain.present']( - 'domain present', - **domain_ret) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {'new': {'AccessPolicies': {}}, 'old': {'AccessPolicies': {'a': 'b'}}}) + cfg[k] = {"Options": v} + cfg["AccessPolicies"] = {"Options": '{"a": "b"}'} + self.conn.describe_elasticsearch_domain_config.return_value = { + "DomainConfig": cfg + } + self.conn.update_elasticsearch_domain_config.return_value = { + "DomainConfig": cfg + } + result = self.salt_states["boto_elasticsearch_domain.present"]( + "domain present", **domain_ret + ) + self.assertTrue(result["result"]) + self.assertEqual( + result["changes"], + {"new": {"AccessPolicies": {}}, "old": {"AccessPolicies": {"a": "b"}}}, + ) def test_present_with_failure(self): self.conn.describe_elasticsearch_domain.side_effect = not_found_error - self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': domain_ret} - self.conn.create_elasticsearch_domain.side_effect = ClientError(error_content, 'create_domain') - result = self.salt_states['boto_elasticsearch_domain.present']( - 'domain present', - **domain_ret) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.describe_elasticsearch_domain_config.return_value = { + "DomainConfig": domain_ret + } + self.conn.create_elasticsearch_domain.side_effect = ClientError( + error_content, "create_domain" + ) + result = self.salt_states["boto_elasticsearch_domain.present"]( + "domain present", **domain_ret + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_absent_when_domain_does_not_exist(self): - ''' + """ Tests absent on a domain that does not exist. - ''' + """ self.conn.describe_elasticsearch_domain.side_effect = not_found_error - result = self.salt_states['boto_elasticsearch_domain.absent']('test', 'mydomain') - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_elasticsearch_domain.absent"]( + "test", "mydomain" + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_absent_when_domain_exists(self): - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': domain_ret} - result = self.salt_states['boto_elasticsearch_domain.absent']('test', domain_ret['DomainName']) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['domain'], None) + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + self.conn.describe_elasticsearch_domain_config.return_value = { + "DomainConfig": domain_ret + } + result = self.salt_states["boto_elasticsearch_domain.absent"]( + "test", domain_ret["DomainName"] + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["domain"], None) def test_absent_with_failure(self): - self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret} - self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': domain_ret} - self.conn.delete_elasticsearch_domain.side_effect = ClientError(error_content, 'delete_domain') - result = self.salt_states['boto_elasticsearch_domain.absent']('test', domain_ret['DomainName']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.describe_elasticsearch_domain.return_value = { + "DomainStatus": domain_ret + } + self.conn.describe_elasticsearch_domain_config.return_value = { + "DomainConfig": domain_ret + } + self.conn.delete_elasticsearch_domain.side_effect = ClientError( + error_content, "delete_domain" + ) + result = self.salt_states["boto_elasticsearch_domain.absent"]( + "test", domain_ret["DomainName"] + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) diff --git a/tests/unit/states/test_boto_elb.py b/tests/unit/states/test_boto_elb.py index 69c49664ddd..d677515b64a 100644 --- a/tests/unit/states/test_boto_elb.py +++ b/tests/unit/states/test_boto_elb.py @@ -1,51 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import copy -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +import copy # Import Salt Libs import salt.states.boto_elb as boto_elb +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoElbTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_elb - ''' + """ + def setup_loader_modules(self): return {boto_elb: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the IAM role exists. - ''' - name = 'myelb' - listeners = [{'elb_port': 'ELBPORT', 'instance_port': 'PORT', - 'elb_protocol': 'HTTPS', 'certificate': 'A'}] - alarms = {'MyAlarm': {'name': name, - 'attributes': {'description': 'A'}}} - attrs = {'alarm_actions': ['arn:aws:sns:us-east-1:12345:myalarm'], - 'insufficient_data_actions': [], - 'ok_actions': ['arn:aws:sns:us-east-1:12345:myalarm']} - health_check = {'target:': 'HTTP:80/'} - avail_zones = ['us-east-1a', 'us-east-1c', 'us-east-1d'] - cnames = [{'name': 'www.test.com', 'zone': 'test.com', 'ttl': 60}] + """ + name = "myelb" + listeners = [ + { + "elb_port": "ELBPORT", + "instance_port": "PORT", + "elb_protocol": "HTTPS", + "certificate": "A", + } + ] + alarms = {"MyAlarm": {"name": name, "attributes": {"description": "A"}}} + attrs = { + "alarm_actions": ["arn:aws:sns:us-east-1:12345:myalarm"], + "insufficient_data_actions": [], + "ok_actions": ["arn:aws:sns:us-east-1:12345:myalarm"], + } + health_check = {"target:": "HTTP:80/"} + avail_zones = ["us-east-1a", "us-east-1c", "us-east-1d"] + cnames = [{"name": "www.test.com", "zone": "test.com", "ttl": 60}] - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} ret1 = copy.deepcopy(ret) mock = MagicMock(return_value={}) @@ -54,122 +58,138 @@ class BotoElbTestCase(TestCase, LoaderModuleMockMixin): mock_attributes = MagicMock(return_value=attrs) mock_health_check = MagicMock(return_value=health_check) - with patch.dict(boto_elb.__salt__, - {'config.option': mock, - 'boto_elb.exists': mock_false_bool, - 'boto_elb.create': mock_false_bool, - 'pillar.get': MagicMock(return_value={})}): - with patch.dict(boto_elb.__opts__, {'test': False}): - ret = boto_elb.present( - name, - listeners, - availability_zones=avail_zones - ) - self.assertTrue(boto_elb.__salt__['boto_elb.exists'].called) - self.assertTrue(boto_elb.__salt__['boto_elb.create'].called) - self.assertIn('Failed to create myelb ELB.', ret['comment']) - self.assertFalse(ret['result']) + with patch.dict( + boto_elb.__salt__, + { + "config.option": mock, + "boto_elb.exists": mock_false_bool, + "boto_elb.create": mock_false_bool, + "pillar.get": MagicMock(return_value={}), + }, + ): + with patch.dict(boto_elb.__opts__, {"test": False}): + ret = boto_elb.present(name, listeners, availability_zones=avail_zones) + self.assertTrue(boto_elb.__salt__["boto_elb.exists"].called) + self.assertTrue(boto_elb.__salt__["boto_elb.create"].called) + self.assertIn("Failed to create myelb ELB.", ret["comment"]) + self.assertFalse(ret["result"]) def mock_config_option(*args, **kwargs): - if args[0] == 'boto_elb_policies': + if args[0] == "boto_elb_policies": return [] return {} mock = MagicMock(return_value={}) - with patch.dict(boto_elb.__salt__, - {'config.option': MagicMock(side_effect=mock_config_option), - 'boto_elb.exists': mock_false_bool, - 'boto_elb.create': mock_true_bool, - 'boto_elb.get_attributes': mock_attributes, - 'boto_elb.get_health_check': mock_health_check, - 'boto_elb.get_elb_config': MagicMock(side_effect=[mock, MagicMock()]), - 'pillar.get': MagicMock(return_value={})}): - with patch.dict(boto_elb.__opts__, {'test': False}): - with patch.dict(boto_elb.__states__, {'boto_cloudwatch_alarm.present': MagicMock(return_value=ret1)}): + with patch.dict( + boto_elb.__salt__, + { + "config.option": MagicMock(side_effect=mock_config_option), + "boto_elb.exists": mock_false_bool, + "boto_elb.create": mock_true_bool, + "boto_elb.get_attributes": mock_attributes, + "boto_elb.get_health_check": mock_health_check, + "boto_elb.get_elb_config": MagicMock(side_effect=[mock, MagicMock()]), + "pillar.get": MagicMock(return_value={}), + }, + ): + with patch.dict(boto_elb.__opts__, {"test": False}): + with patch.dict( + boto_elb.__states__, + {"boto_cloudwatch_alarm.present": MagicMock(return_value=ret1)}, + ): ret = boto_elb.present( name, listeners, availability_zones=avail_zones, health_check=health_check, - alarms=alarms + alarms=alarms, + ) + self.assertTrue(boto_elb.__salt__["boto_elb.exists"].called) + self.assertTrue(boto_elb.__salt__["boto_elb.create"].called) + self.assertTrue( + boto_elb.__states__["boto_cloudwatch_alarm.present"].called ) - self.assertTrue(boto_elb.__salt__['boto_elb.exists'].called) - self.assertTrue(boto_elb.__salt__['boto_elb.create'].called) - self.assertTrue(boto_elb.__states__['boto_cloudwatch_alarm.present'].called) self.assertFalse( - boto_elb.__salt__['boto_elb.get_attributes'].called + boto_elb.__salt__["boto_elb.get_attributes"].called ) self.assertTrue( - boto_elb.__salt__['boto_elb.get_health_check'].called + boto_elb.__salt__["boto_elb.get_health_check"].called ) - self.assertIn('ELB myelb created.', ret['comment']) - self.assertTrue(ret['result']) + self.assertIn("ELB myelb created.", ret["comment"]) + self.assertTrue(ret["result"]) mock = MagicMock(return_value={}) - mock_elb = MagicMock(return_value={'dns_name': 'myelb.amazon.com', 'policies': [], 'listeners': [], 'backends': []}) - with patch.dict(boto_elb.__salt__, - {'config.option': MagicMock(side_effect=mock_config_option), - 'boto_elb.exists': mock_false_bool, - 'boto_elb.create': mock_true_bool, - 'boto_elb.get_attributes': mock_attributes, - 'boto_elb.get_health_check': mock_health_check, - 'boto_elb.get_elb_config': mock_elb, - 'pillar.get': MagicMock(return_value={})}): - with patch.dict(boto_elb.__opts__, {'test': False}): - with patch.dict(boto_elb.__states__, {'boto_route53.present': MagicMock(return_value=ret1)}): + mock_elb = MagicMock( + return_value={ + "dns_name": "myelb.amazon.com", + "policies": [], + "listeners": [], + "backends": [], + } + ) + with patch.dict( + boto_elb.__salt__, + { + "config.option": MagicMock(side_effect=mock_config_option), + "boto_elb.exists": mock_false_bool, + "boto_elb.create": mock_true_bool, + "boto_elb.get_attributes": mock_attributes, + "boto_elb.get_health_check": mock_health_check, + "boto_elb.get_elb_config": mock_elb, + "pillar.get": MagicMock(return_value={}), + }, + ): + with patch.dict(boto_elb.__opts__, {"test": False}): + with patch.dict( + boto_elb.__states__, + {"boto_route53.present": MagicMock(return_value=ret1)}, + ): ret = boto_elb.present( name, listeners, availability_zones=avail_zones, health_check=health_check, - cnames=cnames + cnames=cnames, ) - mock_changes = {'new': {'elb': 'myelb'}, 'old': {'elb': None}} - self.assertTrue(boto_elb.__states__['boto_route53.present'].called) - self.assertEqual(mock_changes, ret['changes']) - self.assertTrue(ret['result']) + mock_changes = {"new": {"elb": "myelb"}, "old": {"elb": None}} + self.assertTrue(boto_elb.__states__["boto_route53.present"].called) + self.assertEqual(mock_changes, ret["changes"]) + self.assertTrue(ret["result"]) # 'register_instances' function tests: 1 def test_register_instances(self): - ''' + """ Test to add instance/s to load balancer - ''' - name = 'myelb' - instances = ['instance-id1', 'instance-id2'] + """ + name = "myelb" + instances = ["instance-id1", "instance-id2"] - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} mock_bool = MagicMock(return_value=False) - with patch.dict(boto_elb.__salt__, {'boto_elb.exists': mock_bool}): - comt = ('Could not find lb {0}'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(boto_elb.register_instances(name, - instances), ret) + with patch.dict(boto_elb.__salt__, {"boto_elb.exists": mock_bool}): + comt = "Could not find lb {0}".format(name) + ret.update({"comment": comt}) + self.assertDictEqual(boto_elb.register_instances(name, instances), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the IAM role is deleted. - ''' - name = 'new_table' + """ + name = "new_table" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[False, True]) - with patch.dict(boto_elb.__salt__, {'boto_elb.exists': mock}): - comt = ('{0} ELB does not exist.'.format(name)) - ret.update({'comment': comt}) + with patch.dict(boto_elb.__salt__, {"boto_elb.exists": mock}): + comt = "{0} ELB does not exist.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_elb.absent(name), ret) - with patch.dict(boto_elb.__opts__, {'test': True}): - comt = ('ELB {0} is set to be removed.'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(boto_elb.__opts__, {"test": True}): + comt = "ELB {0} is set to be removed.".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_elb.absent(name), ret) diff --git a/tests/unit/states/test_boto_iam_role.py b/tests/unit/states/test_boto_iam_role.py index 4792035d1ed..8ca5350ad20 100644 --- a/tests/unit/states/test_boto_iam_role.py +++ b/tests/unit/states/test_boto_iam_role.py @@ -1,174 +1,209 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.boto_iam_role as boto_iam_role +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoIAMRoleTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_iam_role - ''' + """ + def setup_loader_modules(self): return {boto_iam_role: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the IAM role exists. - ''' - name = 'myrole' + """ + name = "myrole" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} _desc_role = { - 'create_date': '2015-02-11T19:47:14Z', - 'role_id': 'HIUHBIUBIBNKJNBKJ', - 'assume_role_policy_document': { - 'Version': '2008-10-17', - 'Statement': [{ - 'Action': 'sts:AssumeRole', - 'Principal': {'Service': 'ec2.amazonaws.com'}, - 'Effect': 'Allow' - }]}, - 'role_name': 'myfakerole', - 'path': '/', - 'arn': 'arn:aws:iam::12345:role/myfakerole' + "create_date": "2015-02-11T19:47:14Z", + "role_id": "HIUHBIUBIBNKJNBKJ", + "assume_role_policy_document": { + "Version": "2008-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": {"Service": "ec2.amazonaws.com"}, + "Effect": "Allow", + } + ], + }, + "role_name": "myfakerole", + "path": "/", + "arn": "arn:aws:iam::12345:role/myfakerole", } _desc_role2 = { - 'create_date': '2015-02-11T19:47:14Z', - 'role_id': 'HIUHBIUBIBNKJNBKJ', - 'assume_role_policy_document': { - 'Version': '2008-10-17', - 'Statement': [{ - 'Action': 'sts:AssumeRole', - 'Principal': { - 'Service': [ - 'ec2.amazonaws.com', - 'datapipeline.amazonaws.com' - ] - }, - 'Effect': 'Allow' - }]}, - 'role_name': 'myfakerole', - 'path': '/', - 'arn': 'arn:aws:iam::12345:role/myfakerole' + "create_date": "2015-02-11T19:47:14Z", + "role_id": "HIUHBIUBIBNKJNBKJ", + "assume_role_policy_document": { + "Version": "2008-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Principal": { + "Service": [ + "ec2.amazonaws.com", + "datapipeline.amazonaws.com", + ] + }, + "Effect": "Allow", + } + ], + }, + "role_name": "myfakerole", + "path": "/", + "arn": "arn:aws:iam::12345:role/myfakerole", } - mock_desc = MagicMock(side_effect=[ - False, _desc_role, _desc_role, _desc_role2, _desc_role - ]) + mock_desc = MagicMock( + side_effect=[False, _desc_role, _desc_role, _desc_role2, _desc_role] + ) _build_policy = { - 'Version': '2008-10-17', - 'Statement': [{ - 'Action': 'sts:AssumeRole', - 'Effect': 'Allow', - 'Principal': {'Service': 'ec2.amazonaws.com'} - }] + "Version": "2008-10-17", + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": {"Service": "ec2.amazonaws.com"}, + } + ], } mock_policy = MagicMock(return_value=_build_policy) mock_ipe = MagicMock(side_effect=[False, True, True, True]) mock_pa = MagicMock(side_effect=[False, True, True, True]) mock_bool = MagicMock(return_value=False) mock_lst = MagicMock(return_value=[]) - with patch.dict(boto_iam_role.__salt__, - {'boto_iam.describe_role': mock_desc, - 'boto_iam.create_role': mock_bool, - 'boto_iam.build_policy': mock_policy, - 'boto_iam.update_assume_role_policy': mock_bool, - 'boto_iam.instance_profile_exists': mock_ipe, - 'boto_iam.list_attached_role_policies': mock_lst, - 'boto_iam.create_instance_profile': mock_bool, - 'boto_iam.profile_associated': mock_pa, - 'boto_iam.associate_profile_to_role': mock_bool, - 'boto_iam.list_role_policies': mock_lst}): - with patch.dict(boto_iam_role.__opts__, {'test': False}): - comt = (' Failed to create {0} IAM role.'.format(name)) - ret.update({'comment': comt}) + with patch.dict( + boto_iam_role.__salt__, + { + "boto_iam.describe_role": mock_desc, + "boto_iam.create_role": mock_bool, + "boto_iam.build_policy": mock_policy, + "boto_iam.update_assume_role_policy": mock_bool, + "boto_iam.instance_profile_exists": mock_ipe, + "boto_iam.list_attached_role_policies": mock_lst, + "boto_iam.create_instance_profile": mock_bool, + "boto_iam.profile_associated": mock_pa, + "boto_iam.associate_profile_to_role": mock_bool, + "boto_iam.list_role_policies": mock_lst, + }, + ): + with patch.dict(boto_iam_role.__opts__, {"test": False}): + comt = " Failed to create {0} IAM role.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_iam_role.present(name), ret) - comt = (' myrole role present. ' - 'Failed to create myrole instance profile.') - ret.update({'comment': comt}) + comt = ( + " myrole role present. " "Failed to create myrole instance profile." + ) + ret.update({"comment": comt}) self.assertDictEqual(boto_iam_role.present(name), ret) - comt = (' myrole role present. Failed to associate myrole' - ' instance profile with myrole role.') - ret.update({'comment': comt}) + comt = ( + " myrole role present. Failed to associate myrole" + " instance profile with myrole role." + ) + ret.update({"comment": comt}) self.assertDictEqual(boto_iam_role.present(name), ret) - comt = (' myrole role present. Failed to update assume role' - ' policy.') - ret.update({'comment': comt}) + comt = " myrole role present. Failed to update assume role" " policy." + ret.update({"comment": comt}) self.assertDictEqual(boto_iam_role.present(name), ret) - comt = (' myrole role present. ') - ret.update({'comment': comt, 'result': True}) + comt = " myrole role present. " + ret.update({"comment": comt, "result": True}) self.assertDictEqual(boto_iam_role.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the IAM role is deleted. - ''' - name = 'myrole' + """ + name = "myrole" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - mock = MagicMock(side_effect=[['mypolicy'], ['mypolicy'], False, True, - False, False, True, False, False, False, - True]) + mock = MagicMock( + side_effect=[ + ["mypolicy"], + ["mypolicy"], + False, + True, + False, + False, + True, + False, + False, + False, + True, + ] + ) mock_bool = MagicMock(return_value=False) mock_lst = MagicMock(return_value=[]) - with patch.dict(boto_iam_role.__salt__, - {'boto_iam.list_role_policies': mock, - 'boto_iam.delete_role_policy': mock_bool, - 'boto_iam.profile_associated': mock, - 'boto_iam.disassociate_profile_from_role': mock_bool, - 'boto_iam.instance_profile_exists': mock, - 'boto_iam.list_attached_role_policies': mock_lst, - 'boto_iam.delete_instance_profile': mock_bool, - 'boto_iam.role_exists': mock, - 'boto_iam.delete_role': mock_bool}): - with patch.dict(boto_iam_role.__opts__, {'test': False}): - comt = (' Failed to add policy mypolicy to role myrole') - ret.update({'comment': comt, - 'changes': {'new': {'policies': ['mypolicy']}, - 'old': {'policies': ['mypolicy']}}}) + with patch.dict( + boto_iam_role.__salt__, + { + "boto_iam.list_role_policies": mock, + "boto_iam.delete_role_policy": mock_bool, + "boto_iam.profile_associated": mock, + "boto_iam.disassociate_profile_from_role": mock_bool, + "boto_iam.instance_profile_exists": mock, + "boto_iam.list_attached_role_policies": mock_lst, + "boto_iam.delete_instance_profile": mock_bool, + "boto_iam.role_exists": mock, + "boto_iam.delete_role": mock_bool, + }, + ): + with patch.dict(boto_iam_role.__opts__, {"test": False}): + comt = " Failed to add policy mypolicy to role myrole" + ret.update( + { + "comment": comt, + "changes": { + "new": {"policies": ["mypolicy"]}, + "old": {"policies": ["mypolicy"]}, + }, + } + ) self.assertDictEqual(boto_iam_role.absent(name), ret) - comt = (' No policies in role myrole.' - ' No attached policies in role myrole. Failed to disassociate ' - 'myrole instance profile from myrole role.') - ret.update({'comment': comt, 'changes': {}}) + comt = ( + " No policies in role myrole." + " No attached policies in role myrole. Failed to disassociate " + "myrole instance profile from myrole role." + ) + ret.update({"comment": comt, "changes": {}}) self.assertDictEqual(boto_iam_role.absent(name), ret) - comt = (' No policies in role myrole.' - ' No attached policies in role myrole. ' - ' Failed to delete myrole instance profile.') - ret.update({'comment': comt, 'changes': {}}) + comt = ( + " No policies in role myrole." + " No attached policies in role myrole. " + " Failed to delete myrole instance profile." + ) + ret.update({"comment": comt, "changes": {}}) self.assertDictEqual(boto_iam_role.absent(name), ret) - comt = (' No policies in role myrole.' - ' No attached policies in role myrole. myrole instance profile ' - 'does not exist. Failed to delete myrole iam role.') - ret.update({'comment': comt, 'changes': {}}) + comt = ( + " No policies in role myrole." + " No attached policies in role myrole. myrole instance profile " + "does not exist. Failed to delete myrole iam role." + ) + ret.update({"comment": comt, "changes": {}}) self.assertDictEqual(boto_iam_role.absent(name), ret) diff --git a/tests/unit/states/test_boto_iot.py b/tests/unit/states/test_boto_iot.py index e03aa33a817..62beb894d72 100644 --- a/tests/unit/states/test_boto_iot.py +++ b/tests/unit/states/test_boto_iot.py @@ -2,33 +2,37 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import random import string -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch - # Import Salt libs import salt.config import salt.loader -from salt.utils.versions import LooseVersion import salt.states.boto_iot as boto_iot -# Import test suite libs +# Import 3rd-party libs +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error,no-name-in-module,unused-import from tests.unit.modules.test_boto_iot import BotoIoTTestCaseMixin -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +# Import test suite libs + + try: import boto import boto3 from botocore.exceptions import ClientError from botocore import __version__ as found_botocore_version + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -38,17 +42,17 @@ except ImportError: # the boto_iot module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' -required_botocore_version = '1.4.41' +required_boto3_version = "1.2.1" +required_botocore_version = "1.4.41" log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -60,45 +64,52 @@ def _has_required_boto(): if _has_required_boto(): - region = 'us-east-1' - access_key = 'GKTADJGHEIQSXMKKRBJ08H' - secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' - conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} - error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' - not_found_error = ClientError({ - 'Error': { - 'Code': 'ResourceNotFoundException', - 'Message': "Test-defined error" - } - }, 'msg') - topic_rule_not_found_error = ClientError({ - 'Error': { - 'Code': 'UnauthorizedException', - 'Message': "Test-defined error" - } - }, 'msg') - error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } + region = "us-east-1" + access_key = "GKTADJGHEIQSXMKKRBJ08H" + secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" + conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } - policy_ret = dict(policyName='testpolicy', - policyDocument='{"Version": "2012-10-17", "Statement": [{"Action": ["iot:Publish"], "Resource": ["*"], "Effect": "Allow"}]}', - policyArn='arn:aws:iot:us-east-1:123456:policy/my_policy', - policyVersionId=1, - defaultVersionId=1) - topic_rule_ret = dict(ruleName='testrule', - sql="SELECT * FROM 'iot/test'", - description='topic rule description', - createdAt='1970-01-01', - actions=[{'iot': {'functionArn': 'arn:aws:::function'}}], - ruleDisabled=True) - principal = 'arn:aws:iot:us-east-1:1234:cert/21fc104aaaf6043f5756c1b57bda84ea8395904c43f28517799b19e4c42514' + error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" + ) + not_found_error = ClientError( + { + "Error": { + "Code": "ResourceNotFoundException", + "Message": "Test-defined error", + } + }, + "msg", + ) + topic_rule_not_found_error = ClientError( + {"Error": {"Code": "UnauthorizedException", "Message": "Test-defined error"}}, + "msg", + ) + error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} + policy_ret = dict( + policyName="testpolicy", + policyDocument='{"Version": "2012-10-17", "Statement": [{"Action": ["iot:Publish"], "Resource": ["*"], "Effect": "Allow"}]}', + policyArn="arn:aws:iot:us-east-1:123456:policy/my_policy", + policyVersionId=1, + defaultVersionId=1, + ) + topic_rule_ret = dict( + ruleName="testrule", + sql="SELECT * FROM 'iot/test'", + description="topic rule description", + createdAt="1970-01-01", + actions=[{"iot": {"functionArn": "arn:aws:::function"}}], + ruleDisabled=True, + ) + principal = "arn:aws:iot:us-east-1:1234:cert/21fc104aaaf6043f5756c1b57bda84ea8395904c43f28517799b19e4c42514" - thing_type_name = 'test_thing_type' - thing_type_desc = 'test_thing_type_desc' - thing_type_attr_1 = 'test_thing_type_search_attr_1' + thing_type_name = "test_thing_type" + thing_type_desc = "test_thing_type_desc" + thing_type_attr_1 = "test_thing_type_search_attr_1" thing_type_ret = dict( thingTypeName=thing_type_name, thingTypeProperties=dict( @@ -106,9 +117,8 @@ if _has_required_boto(): searchableAttributes=[thing_type_attr_1], ), thingTypeMetadata=dict( - deprecated=False, - creationDate='2010-08-01 15:54:49.699000+00:00' - ) + deprecated=False, creationDate="2010-08-01 15:54:49.699000+00:00" + ), ) deprecated_thing_type_ret = dict( thingTypeName=thing_type_name, @@ -118,21 +128,22 @@ if _has_required_boto(): ), thingTypeMetadata=dict( deprecated=True, - creationDate='2010-08-01 15:54:49.699000+00:00', - deprecationDate='2010-08-02 15:54:49.699000+00:00' - ) + creationDate="2010-08-01 15:54:49.699000+00:00", + deprecationDate="2010-08-02 15:54:49.699000+00:00", + ), ) - thing_type_arn = 'test_thing_type_arn' + thing_type_arn = "test_thing_type_arn" create_thing_type_ret = dict( - thingTypeName=thing_type_name, - thingTypeArn=thing_type_arn + thingTypeName=thing_type_name, thingTypeArn=thing_type_arn ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) class BotoIoTStateTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None @@ -140,332 +151,405 @@ class BotoIoTStateTestCaseBase(TestCase, LoaderModuleMockMixin): ctx = {} utils = salt.loader.utils( self.opts, - whitelist=['boto3', 'args', 'systemd', 'path', 'platform'], - context=ctx) + whitelist=["boto3", "args", "systemd", "path", "platform"], + context=ctx, + ) serializers = salt.loader.serializers(self.opts) - self.funcs = funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_iot']) - self.salt_states = salt.loader.states(opts=self.opts, functions=funcs, utils=utils, whitelist=['boto_iot'], - serializers=serializers) + self.funcs = funcs = salt.loader.minion_mods( + self.opts, context=ctx, utils=utils, whitelist=["boto_iot"] + ) + self.salt_states = salt.loader.states( + opts=self.opts, + functions=funcs, + utils=utils, + whitelist=["boto_iot"], + serializers=serializers, + ) return { boto_iot: { - '__opts__': self.opts, - '__salt__': funcs, - '__utils__': utils, - '__states__': self.salt_states, - '__serializers__': serializers, + "__opts__": self.opts, + "__salt__": funcs, + "__utils__": utils, + "__states__": self.salt_states, + "__serializers__": serializers, } } @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) @classmethod def tearDownClass(cls): del cls.opts def setUp(self): - self.addCleanup(delattr, self, 'funcs') - self.addCleanup(delattr, self, 'salt_states') + self.addCleanup(delattr, self, "funcs") + self.addCleanup(delattr, self, "salt_states") # Set up MagicMock to replace the boto3 session # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn class BotoIoTThingTypeTestCase(BotoIoTStateTestCaseBase, BotoIoTTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_iot state.module - ''' + """ def test_present_when_thing_type_does_not_exist(self): - ''' + """ tests present on a thing type that does not exist. - ''' + """ self.conn.describe_thing_type.side_effect = [not_found_error, thing_type_ret] self.conn.create_thing_type.return_value = create_thing_type_ret - result = self.salt_states['boto_iot.thing_type_present']( - 'thing type present', + result = self.salt_states["boto_iot.thing_type_present"]( + "thing type present", thingTypeName=thing_type_name, thingTypeDescription=thing_type_desc, searchableAttributesList=[thing_type_attr_1], **conn_parameters ) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['thing_type']['thingTypeName'], - thing_type_name) + self.assertTrue(result["result"]) + self.assertEqual( + result["changes"]["new"]["thing_type"]["thingTypeName"], thing_type_name + ) def test_present_when_thing_type_exists(self): self.conn.describe_thing_type.return_value = thing_type_ret - result = self.salt_states['boto_iot.thing_type_present']( - 'thing type present', + result = self.salt_states["boto_iot.thing_type_present"]( + "thing type present", thingTypeName=thing_type_name, thingTypeDescription=thing_type_desc, searchableAttributesList=[thing_type_attr_1], **conn_parameters ) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) self.assertTrue(self.conn.create_thing_type.call_count == 0) def test_present_with_failure(self): self.conn.describe_thing_type.side_effect = [not_found_error, thing_type_ret] - self.conn.create_thing_type.side_effect = ClientError(error_content, 'create_thing_type') - result = self.salt_states['boto_iot.thing_type_present']( - 'thing type present', + self.conn.create_thing_type.side_effect = ClientError( + error_content, "create_thing_type" + ) + result = self.salt_states["boto_iot.thing_type_present"]( + "thing type present", thingTypeName=thing_type_name, thingTypeDescription=thing_type_desc, searchableAttributesList=[thing_type_attr_1], **conn_parameters ) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_absent_when_thing_type_does_not_exist(self): - ''' + """ Tests absent on a thing type does not exist - ''' + """ self.conn.describe_thing_type.side_effect = not_found_error - result = self.salt_states['boto_iot.thing_type_absent']('test', 'mythingtype', **conn_parameters) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_iot.thing_type_absent"]( + "test", "mythingtype", **conn_parameters + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_absent_when_thing_type_exists(self): - ''' + """ Tests absent on a thing type - ''' + """ self.conn.describe_thing_type.return_value = deprecated_thing_type_ret - result = self.salt_states['boto_iot.thing_type_absent']('test', thing_type_name, **conn_parameters) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['thing_type'], None) + result = self.salt_states["boto_iot.thing_type_absent"]( + "test", thing_type_name, **conn_parameters + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["thing_type"], None) self.assertTrue(self.conn.deprecate_thing_type.call_count == 0) def test_absent_with_deprecate_failure(self): self.conn.describe_thing_type.return_value = thing_type_ret - self.conn.deprecate_thing_type.side_effect = ClientError(error_content, 'deprecate_thing_type') - result = self.salt_states['boto_iot.thing_type_absent']('test', thing_type_name, **conn_parameters) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) - self.assertTrue('deprecate_thing_type' in result['comment']) + self.conn.deprecate_thing_type.side_effect = ClientError( + error_content, "deprecate_thing_type" + ) + result = self.salt_states["boto_iot.thing_type_absent"]( + "test", thing_type_name, **conn_parameters + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) + self.assertTrue("deprecate_thing_type" in result["comment"]) self.assertTrue(self.conn.delete_thing_type.call_count == 0) def test_absent_with_delete_failure(self): self.conn.describe_thing_type.return_value = deprecated_thing_type_ret - self.conn.delete_thing_type.side_effect = ClientError(error_content, 'delete_thing_type') - result = self.salt_states['boto_iot.thing_type_absent']('test', thing_type_name, **conn_parameters) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) - self.assertTrue('delete_thing_type' in result['comment']) + self.conn.delete_thing_type.side_effect = ClientError( + error_content, "delete_thing_type" + ) + result = self.salt_states["boto_iot.thing_type_absent"]( + "test", thing_type_name, **conn_parameters + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) + self.assertTrue("delete_thing_type" in result["comment"]) self.assertTrue(self.conn.deprecate_thing_type.call_count == 0) class BotoIoTPolicyTestCase(BotoIoTStateTestCaseBase, BotoIoTTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_iot state.module - ''' + """ def test_present_when_policy_does_not_exist(self): - ''' + """ Tests present on a policy that does not exist. - ''' + """ self.conn.get_policy.side_effect = [not_found_error, policy_ret] self.conn.create_policy.return_value = policy_ret - result = self.salt_states['boto_iot.policy_present']( - 'policy present', - policyName=policy_ret['policyName'], - policyDocument=policy_ret['policyDocument']) + result = self.salt_states["boto_iot.policy_present"]( + "policy present", + policyName=policy_ret["policyName"], + policyDocument=policy_ret["policyDocument"], + ) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['policy']['policyName'], - policy_ret['policyName']) + self.assertTrue(result["result"]) + self.assertEqual( + result["changes"]["new"]["policy"]["policyName"], policy_ret["policyName"] + ) def test_present_when_policy_exists(self): self.conn.get_policy.return_value = policy_ret self.conn.create_policy_version.return_value = policy_ret - result = self.salt_states['boto_iot.policy_present']( - 'policy present', - policyName=policy_ret['policyName'], - policyDocument=policy_ret['policyDocument']) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_iot.policy_present"]( + "policy present", + policyName=policy_ret["policyName"], + policyDocument=policy_ret["policyDocument"], + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_present_with_failure(self): self.conn.get_policy.side_effect = [not_found_error, policy_ret] - self.conn.create_policy.side_effect = ClientError(error_content, 'create_policy') - result = self.salt_states['boto_iot.policy_present']( - 'policy present', - policyName=policy_ret['policyName'], - policyDocument=policy_ret['policyDocument']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.create_policy.side_effect = ClientError( + error_content, "create_policy" + ) + result = self.salt_states["boto_iot.policy_present"]( + "policy present", + policyName=policy_ret["policyName"], + policyDocument=policy_ret["policyDocument"], + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_absent_when_policy_does_not_exist(self): - ''' + """ Tests absent on a policy that does not exist. - ''' + """ self.conn.get_policy.side_effect = not_found_error - result = self.salt_states['boto_iot.policy_absent']('test', 'mypolicy') - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_iot.policy_absent"]("test", "mypolicy") + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_absent_when_policy_exists(self): self.conn.get_policy.return_value = policy_ret - self.conn.list_policy_versions.return_value = {'policyVersions': []} - result = self.salt_states['boto_iot.policy_absent']('test', policy_ret['policyName']) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['policy'], None) + self.conn.list_policy_versions.return_value = {"policyVersions": []} + result = self.salt_states["boto_iot.policy_absent"]( + "test", policy_ret["policyName"] + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["policy"], None) def test_absent_with_failure(self): self.conn.get_policy.return_value = policy_ret - self.conn.list_policy_versions.return_value = {'policyVersions': []} - self.conn.delete_policy.side_effect = ClientError(error_content, 'delete_policy') - result = self.salt_states['boto_iot.policy_absent']('test', policy_ret['policyName']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.list_policy_versions.return_value = {"policyVersions": []} + self.conn.delete_policy.side_effect = ClientError( + error_content, "delete_policy" + ) + result = self.salt_states["boto_iot.policy_absent"]( + "test", policy_ret["policyName"] + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_attached_when_policy_not_attached(self): - ''' + """ Tests attached on a policy that is not attached. - ''' - self.conn.list_principal_policies.return_value = {'policies': []} - result = self.salt_states['boto_iot.policy_attached']('test', 'myfunc', principal) - self.assertTrue(result['result']) - self.assertTrue(result['changes']['new']['attached']) + """ + self.conn.list_principal_policies.return_value = {"policies": []} + result = self.salt_states["boto_iot.policy_attached"]( + "test", "myfunc", principal + ) + self.assertTrue(result["result"]) + self.assertTrue(result["changes"]["new"]["attached"]) def test_attached_when_policy_attached(self): - ''' + """ Tests attached on a policy that is attached. - ''' - self.conn.list_principal_policies.return_value = {'policies': [policy_ret]} - result = self.salt_states['boto_iot.policy_attached']('test', policy_ret['policyName'], principal) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + """ + self.conn.list_principal_policies.return_value = {"policies": [policy_ret]} + result = self.salt_states["boto_iot.policy_attached"]( + "test", policy_ret["policyName"], principal + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_attached_with_failure(self): - ''' + """ Tests attached on a policy that is attached. - ''' - self.conn.list_principal_policies.return_value = {'policies': []} - self.conn.attach_principal_policy.side_effect = ClientError(error_content, 'attach_principal_policy') - result = self.salt_states['boto_iot.policy_attached']('test', policy_ret['policyName'], principal) - self.assertFalse(result['result']) - self.assertEqual(result['changes'], {}) + """ + self.conn.list_principal_policies.return_value = {"policies": []} + self.conn.attach_principal_policy.side_effect = ClientError( + error_content, "attach_principal_policy" + ) + result = self.salt_states["boto_iot.policy_attached"]( + "test", policy_ret["policyName"], principal + ) + self.assertFalse(result["result"]) + self.assertEqual(result["changes"], {}) def test_detached_when_policy_not_detached(self): - ''' + """ Tests detached on a policy that is not detached. - ''' - self.conn.list_principal_policies.return_value = {'policies': [policy_ret]} - result = self.salt_states['boto_iot.policy_detached']('test', policy_ret['policyName'], principal) - self.assertTrue(result['result']) + """ + self.conn.list_principal_policies.return_value = {"policies": [policy_ret]} + result = self.salt_states["boto_iot.policy_detached"]( + "test", policy_ret["policyName"], principal + ) + self.assertTrue(result["result"]) log.warning(result) - self.assertFalse(result['changes']['new']['attached']) + self.assertFalse(result["changes"]["new"]["attached"]) def test_detached_when_policy_detached(self): - ''' + """ Tests detached on a policy that is detached. - ''' - self.conn.list_principal_policies.return_value = {'policies': []} - result = self.salt_states['boto_iot.policy_detached']('test', policy_ret['policyName'], principal) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + """ + self.conn.list_principal_policies.return_value = {"policies": []} + result = self.salt_states["boto_iot.policy_detached"]( + "test", policy_ret["policyName"], principal + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_detached_with_failure(self): - ''' + """ Tests detached on a policy that is detached. - ''' - self.conn.list_principal_policies.return_value = {'policies': [policy_ret]} - self.conn.detach_principal_policy.side_effect = ClientError(error_content, 'detach_principal_policy') - result = self.salt_states['boto_iot.policy_detached']('test', policy_ret['policyName'], principal) - self.assertFalse(result['result']) - self.assertEqual(result['changes'], {}) + """ + self.conn.list_principal_policies.return_value = {"policies": [policy_ret]} + self.conn.detach_principal_policy.side_effect = ClientError( + error_content, "detach_principal_policy" + ) + result = self.salt_states["boto_iot.policy_detached"]( + "test", policy_ret["policyName"], principal + ) + self.assertFalse(result["result"]) + self.assertEqual(result["changes"], {}) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}. The botocore' - ' module must be greater than or equal to' - ' version {1}.' - .format(required_boto3_version, required_botocore_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}. The botocore" + " module must be greater than or equal to" + " version {1}.".format(required_boto3_version, required_botocore_version), +) class BotoIoTTopicRuleTestCase(BotoIoTStateTestCaseBase, BotoIoTTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_iot state.module rules - ''' + """ def test_present_when_topic_rule_does_not_exist(self): - ''' + """ Tests present on a topic_rule that does not exist. - ''' - self.conn.get_topic_rule.side_effect = [topic_rule_not_found_error, {'rule': topic_rule_ret}] - self.conn.create_topic_rule.return_value = {'created': True} - result = self.salt_states['boto_iot.topic_rule_present']( - 'topic rule present', - ruleName=topic_rule_ret['ruleName'], - sql=topic_rule_ret['sql'], - description=topic_rule_ret['description'], - actions=topic_rule_ret['actions'], - ruleDisabled=topic_rule_ret['ruleDisabled']) + """ + self.conn.get_topic_rule.side_effect = [ + topic_rule_not_found_error, + {"rule": topic_rule_ret}, + ] + self.conn.create_topic_rule.return_value = {"created": True} + result = self.salt_states["boto_iot.topic_rule_present"]( + "topic rule present", + ruleName=topic_rule_ret["ruleName"], + sql=topic_rule_ret["sql"], + description=topic_rule_ret["description"], + actions=topic_rule_ret["actions"], + ruleDisabled=topic_rule_ret["ruleDisabled"], + ) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['rule']['ruleName'], - topic_rule_ret['ruleName']) + self.assertTrue(result["result"]) + self.assertEqual( + result["changes"]["new"]["rule"]["ruleName"], topic_rule_ret["ruleName"] + ) def test_present_when_policy_exists(self): - self.conn.get_topic_rule.return_value = {'rule': topic_rule_ret} - self.conn.create_topic_rule.return_value = {'created': True} - result = self.salt_states['boto_iot.topic_rule_present']( - 'topic rule present', - ruleName=topic_rule_ret['ruleName'], - sql=topic_rule_ret['sql'], - description=topic_rule_ret['description'], - actions=topic_rule_ret['actions'], - ruleDisabled=topic_rule_ret['ruleDisabled']) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + self.conn.get_topic_rule.return_value = {"rule": topic_rule_ret} + self.conn.create_topic_rule.return_value = {"created": True} + result = self.salt_states["boto_iot.topic_rule_present"]( + "topic rule present", + ruleName=topic_rule_ret["ruleName"], + sql=topic_rule_ret["sql"], + description=topic_rule_ret["description"], + actions=topic_rule_ret["actions"], + ruleDisabled=topic_rule_ret["ruleDisabled"], + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_present_with_failure(self): - self.conn.get_topic_rule.side_effect = [topic_rule_not_found_error, {'rule': topic_rule_ret}] - self.conn.create_topic_rule.side_effect = ClientError(error_content, 'create_topic_rule') - result = self.salt_states['boto_iot.topic_rule_present']( - 'topic rule present', - ruleName=topic_rule_ret['ruleName'], - sql=topic_rule_ret['sql'], - description=topic_rule_ret['description'], - actions=topic_rule_ret['actions'], - ruleDisabled=topic_rule_ret['ruleDisabled']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.get_topic_rule.side_effect = [ + topic_rule_not_found_error, + {"rule": topic_rule_ret}, + ] + self.conn.create_topic_rule.side_effect = ClientError( + error_content, "create_topic_rule" + ) + result = self.salt_states["boto_iot.topic_rule_present"]( + "topic rule present", + ruleName=topic_rule_ret["ruleName"], + sql=topic_rule_ret["sql"], + description=topic_rule_ret["description"], + actions=topic_rule_ret["actions"], + ruleDisabled=topic_rule_ret["ruleDisabled"], + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_absent_when_topic_rule_does_not_exist(self): - ''' + """ Tests absent on a topic rule that does not exist. - ''' + """ self.conn.get_topic_rule.side_effect = topic_rule_not_found_error - result = self.salt_states['boto_iot.topic_rule_absent']('test', 'myrule') - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_iot.topic_rule_absent"]("test", "myrule") + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_absent_when_topic_rule_exists(self): self.conn.get_topic_rule.return_value = topic_rule_ret - result = self.salt_states['boto_iot.topic_rule_absent']('test', topic_rule_ret['ruleName']) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['rule'], None) + result = self.salt_states["boto_iot.topic_rule_absent"]( + "test", topic_rule_ret["ruleName"] + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["rule"], None) def test_absent_with_failure(self): self.conn.get_topic_rule.return_value = topic_rule_ret - self.conn.delete_topic_rule.side_effect = ClientError(error_content, 'delete_topic_rule') - result = self.salt_states['boto_iot.topic_rule_absent']('test', topic_rule_ret['ruleName']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.delete_topic_rule.side_effect = ClientError( + error_content, "delete_topic_rule" + ) + result = self.salt_states["boto_iot.topic_rule_absent"]( + "test", topic_rule_ret["ruleName"] + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) diff --git a/tests/unit/states/test_boto_kinesis.py b/tests/unit/states/test_boto_kinesis.py index 4ef3d1ba518..1bb35d9aa3d 100644 --- a/tests/unit/states/test_boto_kinesis.py +++ b/tests/unit/states/test_boto_kinesis.py @@ -2,21 +2,20 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.boto_kinesis as boto_kinesis +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoKinesisTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_kinesis - ''' + """ + # 'present' function tests: 1 maxDiff = None @@ -25,116 +24,165 @@ class BotoKinesisTestCase(TestCase, LoaderModuleMockMixin): return {boto_kinesis: {}} def test_stream_present(self): - ''' + """ Test to ensure the kinesis stream exists. - ''' - name = 'new_stream' + """ + name = "new_stream" retention_hours = 24 - enhanced_monitoring = ['IteratorAgeMilliseconds'] - different_enhanced_monitoring = ['IncomingBytes'] + enhanced_monitoring = ["IteratorAgeMilliseconds"] + different_enhanced_monitoring = ["IncomingBytes"] num_shards = 1 - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - shards = [{'ShardId': 'shardId-000000000000', - 'HashKeyRange': {'EndingHashKey': 'big number', 'StartingHashKey': '0'}, - 'SequenceNumberRange': {'StartingSequenceNumber': 'bigger number'}}] - stream_description = {'HasMoreShards': False, - 'RetentionPeriodHours': retention_hours, - 'StreamName': name, - 'Shards': shards, - 'StreamARN': "", - 'EnhancedMonitoring': [{'ShardLevelMetrics': enhanced_monitoring}], - 'StreamStatus': 'ACTIVE'} + shards = [ + { + "ShardId": "shardId-000000000000", + "HashKeyRange": {"EndingHashKey": "big number", "StartingHashKey": "0"}, + "SequenceNumberRange": {"StartingSequenceNumber": "bigger number"}, + } + ] + stream_description = { + "HasMoreShards": False, + "RetentionPeriodHours": retention_hours, + "StreamName": name, + "Shards": shards, + "StreamARN": "", + "EnhancedMonitoring": [{"ShardLevelMetrics": enhanced_monitoring}], + "StreamStatus": "ACTIVE", + } - exists_mock = MagicMock(side_effect=[{'result': True}, {'result': False}, {'result': True}, {'result': False}]) - get_stream_mock = MagicMock(return_value={'result': {'StreamDescription': stream_description}}) - shard_mock = MagicMock(return_value=[0, 0, {'OpenShards': shards}]) - dict_mock = MagicMock(return_value={'result': True}) + exists_mock = MagicMock( + side_effect=[ + {"result": True}, + {"result": False}, + {"result": True}, + {"result": False}, + ] + ) + get_stream_mock = MagicMock( + return_value={"result": {"StreamDescription": stream_description}} + ) + shard_mock = MagicMock(return_value=[0, 0, {"OpenShards": shards}]) + dict_mock = MagicMock(return_value={"result": True}) mock_bool = MagicMock(return_value=True) - with patch.dict(boto_kinesis.__salt__, - {'boto_kinesis.exists': exists_mock, - 'boto_kinesis.create_stream': dict_mock, - 'boto_kinesis.get_stream_when_active': get_stream_mock, - 'boto_kinesis.get_info_for_reshard': shard_mock, - 'boto_kinesis.num_shards_matches': mock_bool}): + with patch.dict( + boto_kinesis.__salt__, + { + "boto_kinesis.exists": exists_mock, + "boto_kinesis.create_stream": dict_mock, + "boto_kinesis.get_stream_when_active": get_stream_mock, + "boto_kinesis.get_info_for_reshard": shard_mock, + "boto_kinesis.num_shards_matches": mock_bool, + }, + ): # already present, no change required - comt = ('Kinesis stream {0} already exists,\n' - 'Kinesis stream {0}: retention hours did not require change, already set at {1},\n' - 'Kinesis stream {0}: enhanced monitoring did not require change, already set at {2},\n' - 'Kinesis stream {0}: did not require resharding, remains at {3} shards' - .format(name, retention_hours, enhanced_monitoring, num_shards)) - ret.update({'comment': comt}) - self.assertDictEqual(boto_kinesis.present(name, retention_hours, enhanced_monitoring, num_shards), ret) + comt = ( + "Kinesis stream {0} already exists,\n" + "Kinesis stream {0}: retention hours did not require change, already set at {1},\n" + "Kinesis stream {0}: enhanced monitoring did not require change, already set at {2},\n" + "Kinesis stream {0}: did not require resharding, remains at {3} shards".format( + name, retention_hours, enhanced_monitoring, num_shards + ) + ) + ret.update({"comment": comt}) + self.assertDictEqual( + boto_kinesis.present( + name, retention_hours, enhanced_monitoring, num_shards + ), + ret, + ) - with patch.dict(boto_kinesis.__opts__, {'test': True}): + with patch.dict(boto_kinesis.__opts__, {"test": True}): # not present, test environment (dry run) - comt = ('Kinesis stream {0} would be created' - .format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(boto_kinesis.present(name, retention_hours, enhanced_monitoring, num_shards), ret) + comt = "Kinesis stream {0} would be created".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + boto_kinesis.present( + name, retention_hours, enhanced_monitoring, num_shards + ), + ret, + ) # already present, changes required, test environment (dry run) - comt = ('Kinesis stream {0} already exists,\n' - 'Kinesis stream {0}: retention hours would be updated to {1},\n' - 'Kinesis stream {0}: would enable enhanced monitoring for {2},\n' - 'Kinesis stream {0}: would disable enhanced monitoring for {3},\n' - 'Kinesis stream {0}: would be resharded from {4} to {5} shards' - .format(name, retention_hours+1, different_enhanced_monitoring, - enhanced_monitoring, num_shards, num_shards+1)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(boto_kinesis.present(name, retention_hours+1, different_enhanced_monitoring, - num_shards+1), ret) + comt = ( + "Kinesis stream {0} already exists,\n" + "Kinesis stream {0}: retention hours would be updated to {1},\n" + "Kinesis stream {0}: would enable enhanced monitoring for {2},\n" + "Kinesis stream {0}: would disable enhanced monitoring for {3},\n" + "Kinesis stream {0}: would be resharded from {4} to {5} shards".format( + name, + retention_hours + 1, + different_enhanced_monitoring, + enhanced_monitoring, + num_shards, + num_shards + 1, + ) + ) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + boto_kinesis.present( + name, + retention_hours + 1, + different_enhanced_monitoring, + num_shards + 1, + ), + ret, + ) # not present, create and configure - changes = {'new': {'name': name, - 'num_shards': num_shards}} + changes = {"new": {"name": name, "num_shards": num_shards}} - with patch.dict(boto_kinesis.__opts__, {'test': False}): - comt = ('Kinesis stream {0} successfully created,\n' - 'Kinesis stream {0}: retention hours did not require change, already set at {1},\n' - 'Kinesis stream {0}: enhanced monitoring did not require change, already set at {2},\n' - 'Kinesis stream {0}: did not require resharding, remains at {3} shards' - .format(name, retention_hours, enhanced_monitoring, num_shards)) - ret.update({'comment': comt, 'result': True, - 'changes': changes}) - self.assertDictEqual(ret, boto_kinesis.present(name, retention_hours, enhanced_monitoring, num_shards)) + with patch.dict(boto_kinesis.__opts__, {"test": False}): + comt = ( + "Kinesis stream {0} successfully created,\n" + "Kinesis stream {0}: retention hours did not require change, already set at {1},\n" + "Kinesis stream {0}: enhanced monitoring did not require change, already set at {2},\n" + "Kinesis stream {0}: did not require resharding, remains at {3} shards".format( + name, retention_hours, enhanced_monitoring, num_shards + ) + ) + ret.update({"comment": comt, "result": True, "changes": changes}) + self.assertDictEqual( + ret, + boto_kinesis.present( + name, retention_hours, enhanced_monitoring, num_shards + ), + ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the Kinesis stream does not exist. - ''' - name = 'new_stream' + """ + name = "new_stream" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - mock = MagicMock(side_effect=[{'result': False}, {'result': True}, {'result': True}]) - mock_bool = MagicMock(return_value={'result': True}) - with patch.dict(boto_kinesis.__salt__, - {'boto_kinesis.exists': mock, - 'boto_kinesis.delete_stream': mock_bool}): - comt = ('Kinesis stream {0} does not exist'.format(name)) - ret.update({'comment': comt}) + mock = MagicMock( + side_effect=[{"result": False}, {"result": True}, {"result": True}] + ) + mock_bool = MagicMock(return_value={"result": True}) + with patch.dict( + boto_kinesis.__salt__, + {"boto_kinesis.exists": mock, "boto_kinesis.delete_stream": mock_bool}, + ): + comt = "Kinesis stream {0} does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_kinesis.absent(name), ret) - with patch.dict(boto_kinesis.__opts__, {'test': True}): - comt = ('Kinesis stream {0} would be deleted'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(boto_kinesis.__opts__, {"test": True}): + comt = "Kinesis stream {0} would be deleted".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_kinesis.absent(name), ret) - changes = {'new': 'Stream {0} deleted'.format(name), - 'old': 'Stream {0} exists'.format(name)} + changes = { + "new": "Stream {0} deleted".format(name), + "old": "Stream {0} exists".format(name), + } - with patch.dict(boto_kinesis.__opts__, {'test': False}): - comt = ('Deleted stream {0}'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': changes}) + with patch.dict(boto_kinesis.__opts__, {"test": False}): + comt = "Deleted stream {0}".format(name) + ret.update({"comment": comt, "result": True, "changes": changes}) self.assertDictEqual(boto_kinesis.absent(name), ret) diff --git a/tests/unit/states/test_boto_lambda.py b/tests/unit/states/test_boto_lambda.py index 99e9d64768c..bc3bba540cd 100644 --- a/tests/unit/states/test_boto_lambda.py +++ b/tests/unit/states/test_boto_lambda.py @@ -2,33 +2,37 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import random import string -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch - # Import Salt libs import salt.config import salt.loader -import salt.utils.json -from salt.utils.versions import LooseVersion import salt.states.boto_lambda as boto_lambda +import salt.utils.json -# Import test suite libs +# Import 3rd-party libs +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.utils.versions import LooseVersion + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error,no-name-in-module from tests.unit.modules.test_boto_lambda import BotoLambdaTestCaseMixin, TempZipFile -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +# Import test suite libs + + try: import boto3 from botocore.exceptions import ClientError from botocore import __version__ as found_botocore_version + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -38,54 +42,61 @@ except ImportError: # the boto_lambda module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' -required_botocore_version = '1.5.2' +required_boto3_version = "1.2.1" +required_botocore_version = "1.5.2" -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, - 'keyid': secret_key, 'profile': {}} -error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' -error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } -function_ret = dict(FunctionName='testfunction', - Runtime='python2.7', - Role='arn:aws:iam::1234:role/functionrole', - Handler='handler', - Description='abcdefg', - Timeout=5, - MemorySize=128, - CodeSha256='abcdef', - CodeSize=199, - FunctionArn='arn:lambda:us-east-1:1234:Something', - LastModified='yes', - VpcConfig={'SubnetIds': [], 'SecurityGroupIds': []}) -alias_ret = dict(AliasArn='arn:lambda:us-east-1:1234:Something', - Name='testalias', - FunctionVersion='3', - Description='Alias description') -event_source_mapping_ret = dict(UUID='1234-1-123', - BatchSize=123, - EventSourceArn='arn:aws:dynamodb:us-east-1:1234::Something', - FunctionArn='arn:aws:lambda:us-east-1:1234:function:myfunc', - LastModified='yes', - LastProcessingResult='SUCCESS', - State='Enabled', - StateTransitionReason='Random') +error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" +) +error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} +function_ret = dict( + FunctionName="testfunction", + Runtime="python2.7", + Role="arn:aws:iam::1234:role/functionrole", + Handler="handler", + Description="abcdefg", + Timeout=5, + MemorySize=128, + CodeSha256="abcdef", + CodeSize=199, + FunctionArn="arn:lambda:us-east-1:1234:Something", + LastModified="yes", + VpcConfig={"SubnetIds": [], "SecurityGroupIds": []}, +) +alias_ret = dict( + AliasArn="arn:lambda:us-east-1:1234:Something", + Name="testalias", + FunctionVersion="3", + Description="Alias description", +) +event_source_mapping_ret = dict( + UUID="1234-1-123", + BatchSize=123, + EventSourceArn="arn:aws:dynamodb:us-east-1:1234::Something", + FunctionArn="arn:aws:lambda:us-east-1:1234:function:myfunc", + LastModified="yes", + LastProcessingResult="SUCCESS", + State="Enabled", + StateTransitionReason="Random", +) log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -96,11 +107,16 @@ def _has_required_boto(): return True -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, - ('The boto3 module must be greater than or equal to version {0}, ' - 'and botocore must be greater than or equal to {1}'.format( - required_boto3_version, required_botocore_version))) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + ( + "The boto3 module must be greater than or equal to version {0}, " + "and botocore must be greater than or equal to {1}".format( + required_boto3_version, required_botocore_version + ) + ), +) class BotoLambdaStateTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None @@ -108,381 +124,441 @@ class BotoLambdaStateTestCaseBase(TestCase, LoaderModuleMockMixin): ctx = {} utils = salt.loader.utils( self.opts, - whitelist=['boto', 'boto3', 'args', 'systemd', 'path', 'platform', 'reg'], - context=ctx) + whitelist=["boto", "boto3", "args", "systemd", "path", "platform", "reg"], + context=ctx, + ) serializers = salt.loader.serializers(self.opts) - self.funcs = funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_lambda']) - self.salt_states = salt.loader.states(opts=self.opts, functions=funcs, utils=utils, whitelist=['boto_lambda'], - serializers=serializers) + self.funcs = funcs = salt.loader.minion_mods( + self.opts, context=ctx, utils=utils, whitelist=["boto_lambda"] + ) + self.salt_states = salt.loader.states( + opts=self.opts, + functions=funcs, + utils=utils, + whitelist=["boto_lambda"], + serializers=serializers, + ) return { boto_lambda: { - '__opts__': self.opts, - '__salt__': funcs, - '__utils__': utils, - '__states__': self.salt_states, - '__serializers__': serializers, + "__opts__": self.opts, + "__salt__": funcs, + "__utils__": utils, + "__states__": self.salt_states, + "__serializers__": serializers, } } @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) @classmethod def tearDownClass(cls): del cls.opts def setUp(self): - self.addCleanup(delattr, self, 'funcs') - self.addCleanup(delattr, self, 'salt_states') + self.addCleanup(delattr, self, "funcs") + self.addCleanup(delattr, self, "salt_states") # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice( - string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn class BotoLambdaFunctionTestCase(BotoLambdaStateTestCaseBase, BotoLambdaTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_lambda state.module - ''' + """ def test_present_when_function_does_not_exist(self): - ''' + """ Tests present on a function that does not exist. - ''' + """ self.conn.list_functions.side_effect = [ - {'Functions': []}, {'Functions': [function_ret]}] + {"Functions": []}, + {"Functions": [function_ret]}, + ] self.conn.create_function.return_value = function_ret - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + with patch.dict( + self.funcs, {"boto_iam.get_account_id": MagicMock(return_value="1234")} + ): with TempZipFile() as zipfile: - result = self.salt_states['boto_lambda.function_present']( - 'function present', - FunctionName=function_ret['FunctionName'], - Runtime=function_ret['Runtime'], - Role=function_ret['Role'], - Handler=function_ret['Handler'], - ZipFile=zipfile) + result = self.salt_states["boto_lambda.function_present"]( + "function present", + FunctionName=function_ret["FunctionName"], + Runtime=function_ret["Runtime"], + Role=function_ret["Role"], + Handler=function_ret["Handler"], + ZipFile=zipfile, + ) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['function']['FunctionName'], - function_ret['FunctionName']) + self.assertTrue(result["result"]) + self.assertEqual( + result["changes"]["new"]["function"]["FunctionName"], + function_ret["FunctionName"], + ) def test_present_when_function_exists(self): - self.conn.list_functions.return_value = {'Functions': [function_ret]} + self.conn.list_functions.return_value = {"Functions": [function_ret]} self.conn.update_function_code.return_value = function_ret - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + with patch.dict( + self.funcs, {"boto_iam.get_account_id": MagicMock(return_value="1234")} + ): with TempZipFile() as zipfile: - with patch('hashlib.sha256') as sha256: - with patch('os.path.getsize', return_value=199): + with patch("hashlib.sha256") as sha256: + with patch("os.path.getsize", return_value=199): sha = sha256() digest = sha.digest() encoded = sha.encode() - encoded.strip.return_value = function_ret['CodeSha256'] - result = self.salt_states['boto_lambda.function_present']( - 'function present', - FunctionName=function_ret['FunctionName'], - Runtime=function_ret['Runtime'], - Role=function_ret['Role'], - Handler=function_ret['Handler'], + encoded.strip.return_value = function_ret["CodeSha256"] + result = self.salt_states["boto_lambda.function_present"]( + "function present", + FunctionName=function_ret["FunctionName"], + Runtime=function_ret["Runtime"], + Role=function_ret["Role"], + Handler=function_ret["Handler"], ZipFile=zipfile, - Description=function_ret['Description'], - Timeout=function_ret['Timeout']) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + Description=function_ret["Description"], + Timeout=function_ret["Timeout"], + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_present_with_failure(self): self.conn.list_functions.side_effect = [ - {'Functions': []}, {'Functions': [function_ret]}] + {"Functions": []}, + {"Functions": [function_ret]}, + ] self.conn.create_function.side_effect = ClientError( - error_content, 'create_function') - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + error_content, "create_function" + ) + with patch.dict( + self.funcs, {"boto_iam.get_account_id": MagicMock(return_value="1234")} + ): with TempZipFile() as zipfile: - with patch('hashlib.sha256') as sha256: - with patch('os.path.getsize', return_value=199): + with patch("hashlib.sha256") as sha256: + with patch("os.path.getsize", return_value=199): sha = sha256() digest = sha.digest() encoded = sha.encode() - encoded.strip.return_value = function_ret['CodeSha256'] - result = self.salt_states['boto_lambda.function_present']( - 'function present', - FunctionName=function_ret['FunctionName'], - Runtime=function_ret['Runtime'], - Role=function_ret['Role'], - Handler=function_ret['Handler'], + encoded.strip.return_value = function_ret["CodeSha256"] + result = self.salt_states["boto_lambda.function_present"]( + "function present", + FunctionName=function_ret["FunctionName"], + Runtime=function_ret["Runtime"], + Role=function_ret["Role"], + Handler=function_ret["Handler"], ZipFile=zipfile, - Description=function_ret['Description'], - Timeout=function_ret['Timeout']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + Description=function_ret["Description"], + Timeout=function_ret["Timeout"], + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_absent_when_function_does_not_exist(self): - ''' + """ Tests absent on a function that does not exist. - ''' - self.conn.list_functions.return_value = {'Functions': [function_ret]} - result = self.salt_states['boto_lambda.function_absent']('test', 'myfunc') - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + """ + self.conn.list_functions.return_value = {"Functions": [function_ret]} + result = self.salt_states["boto_lambda.function_absent"]("test", "myfunc") + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_absent_when_function_exists(self): - self.conn.list_functions.return_value = {'Functions': [function_ret]} - result = self.salt_states['boto_lambda.function_absent']( - 'test', function_ret['FunctionName']) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['function'], None) + self.conn.list_functions.return_value = {"Functions": [function_ret]} + result = self.salt_states["boto_lambda.function_absent"]( + "test", function_ret["FunctionName"] + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["function"], None) def test_absent_with_failure(self): - self.conn.list_functions.return_value = {'Functions': [function_ret]} + self.conn.list_functions.return_value = {"Functions": [function_ret]} self.conn.delete_function.side_effect = ClientError( - error_content, 'delete_function') - result = self.salt_states['boto_lambda.function_absent']( - 'test', function_ret['FunctionName']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + error_content, "delete_function" + ) + result = self.salt_states["boto_lambda.function_absent"]( + "test", function_ret["FunctionName"] + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_present_when_function_exists_and_permissions(self): - self.conn.list_functions.return_value = {'Functions': [function_ret]} + self.conn.list_functions.return_value = {"Functions": [function_ret]} self.conn.update_function_code.return_value = function_ret self.conn.get_policy.return_value = { "Policy": salt.utils.json.dumps( - {"Version": "2012-10-17", - "Statement": [ - {"Condition": - {"ArnLike": { - "AWS:SourceArn": "arn:aws:events:us-east-1:9999999999:rule/fooo"}}, - "Action": "lambda:InvokeFunction", - "Resource": "arn:aws:lambda:us-east-1:999999999999:function:testfunction", - "Effect": "Allow", - "Principal": {"Service": "events.amazonaws.com"}, - "Sid": "AWSEvents_foo-bar999999999999"}], - "Id": "default"}) + { + "Version": "2012-10-17", + "Statement": [ + { + "Condition": { + "ArnLike": { + "AWS:SourceArn": "arn:aws:events:us-east-1:9999999999:rule/fooo" + } + }, + "Action": "lambda:InvokeFunction", + "Resource": "arn:aws:lambda:us-east-1:999999999999:function:testfunction", + "Effect": "Allow", + "Principal": {"Service": "events.amazonaws.com"}, + "Sid": "AWSEvents_foo-bar999999999999", + } + ], + "Id": "default", + } + ) } - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='1234')}): + with patch.dict( + self.funcs, {"boto_iam.get_account_id": MagicMock(return_value="1234")} + ): with TempZipFile() as zipfile: - with patch('hashlib.sha256') as sha256: - with patch('os.path.getsize', return_value=199): + with patch("hashlib.sha256") as sha256: + with patch("os.path.getsize", return_value=199): sha = sha256() digest = sha.digest() encoded = sha.encode() - encoded.strip.return_value = function_ret['CodeSha256'] - result = self.salt_states['boto_lambda.function_present']( - 'function present', - FunctionName=function_ret['FunctionName'], - Runtime=function_ret['Runtime'], - Role=function_ret['Role'], - Handler=function_ret['Handler'], + encoded.strip.return_value = function_ret["CodeSha256"] + result = self.salt_states["boto_lambda.function_present"]( + "function present", + FunctionName=function_ret["FunctionName"], + Runtime=function_ret["Runtime"], + Role=function_ret["Role"], + Handler=function_ret["Handler"], ZipFile=zipfile, - Description=function_ret['Description'], - Timeout=function_ret['Timeout']) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], { - 'old': { - 'Permissions': { - 'AWSEvents_foo-bar999999999999': - {'Action': 'lambda:InvokeFunction', - 'Principal': 'events.amazonaws.com', - 'SourceArn': 'arn:aws:events:us-east-1:9999999999:rule/fooo'}}}, - 'new': { - 'Permissions': { - 'AWSEvents_foo-bar999999999999': {}}}}) + Description=function_ret["Description"], + Timeout=function_ret["Timeout"], + ) + self.assertTrue(result["result"]) + self.assertEqual( + result["changes"], + { + "old": { + "Permissions": { + "AWSEvents_foo-bar999999999999": { + "Action": "lambda:InvokeFunction", + "Principal": "events.amazonaws.com", + "SourceArn": "arn:aws:events:us-east-1:9999999999:rule/fooo", + } + } + }, + "new": {"Permissions": {"AWSEvents_foo-bar999999999999": {}}}, + }, + ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, - ('The boto3 module must be greater than or equal to version {0}, ' - 'and botocore must be greater than or equal to {1}'.format( - required_boto3_version, required_botocore_version))) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + ( + "The boto3 module must be greater than or equal to version {0}, " + "and botocore must be greater than or equal to {1}".format( + required_boto3_version, required_botocore_version + ) + ), +) class BotoLambdaAliasTestCase(BotoLambdaStateTestCaseBase, BotoLambdaTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_lambda state.module aliases - ''' + """ def test_present_when_alias_does_not_exist(self): - ''' + """ Tests present on a alias that does not exist. - ''' - self.conn.list_aliases.side_effect = [ - {'Aliases': []}, {'Aliases': [alias_ret]}] + """ + self.conn.list_aliases.side_effect = [{"Aliases": []}, {"Aliases": [alias_ret]}] self.conn.create_alias.return_value = alias_ret - result = self.salt_states['boto_lambda.alias_present']( - 'alias present', - FunctionName='testfunc', - Name=alias_ret['Name'], - FunctionVersion=alias_ret['FunctionVersion']) + result = self.salt_states["boto_lambda.alias_present"]( + "alias present", + FunctionName="testfunc", + Name=alias_ret["Name"], + FunctionVersion=alias_ret["FunctionVersion"], + ) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['alias']['Name'], - alias_ret['Name']) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["alias"]["Name"], alias_ret["Name"]) def test_present_when_alias_exists(self): - self.conn.list_aliases.return_value = {'Aliases': [alias_ret]} + self.conn.list_aliases.return_value = {"Aliases": [alias_ret]} self.conn.create_alias.return_value = alias_ret - result = self.salt_states['boto_lambda.alias_present']( - 'alias present', - FunctionName='testfunc', - Name=alias_ret['Name'], - FunctionVersion=alias_ret['FunctionVersion'], - Description=alias_ret['Description']) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_lambda.alias_present"]( + "alias present", + FunctionName="testfunc", + Name=alias_ret["Name"], + FunctionVersion=alias_ret["FunctionVersion"], + Description=alias_ret["Description"], + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_present_with_failure(self): - self.conn.list_aliases.side_effect = [ - {'Aliases': []}, {'Aliases': [alias_ret]}] - self.conn.create_alias.side_effect = ClientError( - error_content, 'create_alias') - result = self.salt_states['boto_lambda.alias_present']( - 'alias present', - FunctionName='testfunc', - Name=alias_ret['Name'], - FunctionVersion=alias_ret['FunctionVersion']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.list_aliases.side_effect = [{"Aliases": []}, {"Aliases": [alias_ret]}] + self.conn.create_alias.side_effect = ClientError(error_content, "create_alias") + result = self.salt_states["boto_lambda.alias_present"]( + "alias present", + FunctionName="testfunc", + Name=alias_ret["Name"], + FunctionVersion=alias_ret["FunctionVersion"], + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_absent_when_alias_does_not_exist(self): - ''' + """ Tests absent on a alias that does not exist. - ''' - self.conn.list_aliases.return_value = {'Aliases': [alias_ret]} - result = self.salt_states['boto_lambda.alias_absent']( - 'alias absent', - FunctionName='testfunc', - Name='myalias') - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + """ + self.conn.list_aliases.return_value = {"Aliases": [alias_ret]} + result = self.salt_states["boto_lambda.alias_absent"]( + "alias absent", FunctionName="testfunc", Name="myalias" + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_absent_when_alias_exists(self): - self.conn.list_aliases.return_value = {'Aliases': [alias_ret]} - result = self.salt_states['boto_lambda.alias_absent']( - 'alias absent', - FunctionName='testfunc', - Name=alias_ret['Name']) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['alias'], None) + self.conn.list_aliases.return_value = {"Aliases": [alias_ret]} + result = self.salt_states["boto_lambda.alias_absent"]( + "alias absent", FunctionName="testfunc", Name=alias_ret["Name"] + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["alias"], None) def test_absent_with_failure(self): - self.conn.list_aliases.return_value = {'Aliases': [alias_ret]} - self.conn.delete_alias.side_effect = ClientError( - error_content, 'delete_alias') - result = self.salt_states['boto_lambda.alias_absent']( - 'alias absent', - FunctionName='testfunc', - Name=alias_ret['Name']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + self.conn.list_aliases.return_value = {"Aliases": [alias_ret]} + self.conn.delete_alias.side_effect = ClientError(error_content, "delete_alias") + result = self.salt_states["boto_lambda.alias_absent"]( + "alias absent", FunctionName="testfunc", Name=alias_ret["Name"] + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, - ('The boto3 module must be greater than or equal to version {0}, ' - 'and botocore must be greater than or equal to {1}'.format( - required_boto3_version, required_botocore_version))) -class BotoLambdaEventSourceMappingTestCase(BotoLambdaStateTestCaseBase, BotoLambdaTestCaseMixin): - ''' +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + ( + "The boto3 module must be greater than or equal to version {0}, " + "and botocore must be greater than or equal to {1}".format( + required_boto3_version, required_botocore_version + ) + ), +) +class BotoLambdaEventSourceMappingTestCase( + BotoLambdaStateTestCaseBase, BotoLambdaTestCaseMixin +): + """ TestCase for salt.modules.boto_lambda state.module event_source_mappings - ''' + """ def test_present_when_event_source_mapping_does_not_exist(self): - ''' + """ Tests present on a event_source_mapping that does not exist. - ''' + """ self.conn.list_event_source_mappings.side_effect = [ - {'EventSourceMappings': []}, {'EventSourceMappings': [event_source_mapping_ret]}] + {"EventSourceMappings": []}, + {"EventSourceMappings": [event_source_mapping_ret]}, + ] self.conn.get_event_source_mapping.return_value = event_source_mapping_ret self.conn.create_event_source_mapping.return_value = event_source_mapping_ret - result = self.salt_states['boto_lambda.event_source_mapping_present']( - 'event source mapping present', - EventSourceArn=event_source_mapping_ret[ - 'EventSourceArn'], - FunctionName='myfunc', - StartingPosition='LATEST') + result = self.salt_states["boto_lambda.event_source_mapping_present"]( + "event source mapping present", + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName="myfunc", + StartingPosition="LATEST", + ) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['event_source_mapping']['UUID'], - event_source_mapping_ret['UUID']) + self.assertTrue(result["result"]) + self.assertEqual( + result["changes"]["new"]["event_source_mapping"]["UUID"], + event_source_mapping_ret["UUID"], + ) def test_present_when_event_source_mapping_exists(self): self.conn.list_event_source_mappings.return_value = { - 'EventSourceMappings': [event_source_mapping_ret]} + "EventSourceMappings": [event_source_mapping_ret] + } self.conn.get_event_source_mapping.return_value = event_source_mapping_ret self.conn.create_event_source_mapping.return_value = event_source_mapping_ret - result = self.salt_states['boto_lambda.event_source_mapping_present']( - 'event source mapping present', - EventSourceArn=event_source_mapping_ret[ - 'EventSourceArn'], - FunctionName=event_source_mapping_ret['FunctionArn'], - StartingPosition='LATEST', - BatchSize=event_source_mapping_ret['BatchSize']) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_lambda.event_source_mapping_present"]( + "event source mapping present", + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName=event_source_mapping_ret["FunctionArn"], + StartingPosition="LATEST", + BatchSize=event_source_mapping_ret["BatchSize"], + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_present_with_failure(self): self.conn.list_event_source_mappings.side_effect = [ - {'EventSourceMappings': []}, {'EventSourceMappings': [event_source_mapping_ret]}] + {"EventSourceMappings": []}, + {"EventSourceMappings": [event_source_mapping_ret]}, + ] self.conn.get_event_source_mapping.return_value = event_source_mapping_ret self.conn.create_event_source_mapping.side_effect = ClientError( - error_content, 'create_event_source_mapping') - result = self.salt_states['boto_lambda.event_source_mapping_present']( - 'event source mapping present', - EventSourceArn=event_source_mapping_ret[ - 'EventSourceArn'], - FunctionName=event_source_mapping_ret['FunctionArn'], - StartingPosition='LATEST', - BatchSize=event_source_mapping_ret['BatchSize']) - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + error_content, "create_event_source_mapping" + ) + result = self.salt_states["boto_lambda.event_source_mapping_present"]( + "event source mapping present", + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName=event_source_mapping_ret["FunctionArn"], + StartingPosition="LATEST", + BatchSize=event_source_mapping_ret["BatchSize"], + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) def test_absent_when_event_source_mapping_does_not_exist(self): - ''' + """ Tests absent on a event_source_mapping that does not exist. - ''' - self.conn.list_event_source_mappings.return_value = { - 'EventSourceMappings': []} - result = self.salt_states['boto_lambda.event_source_mapping_absent']( - 'event source mapping absent', - EventSourceArn=event_source_mapping_ret[ - 'EventSourceArn'], - FunctionName='myfunc') - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + """ + self.conn.list_event_source_mappings.return_value = {"EventSourceMappings": []} + result = self.salt_states["boto_lambda.event_source_mapping_absent"]( + "event source mapping absent", + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName="myfunc", + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_absent_when_event_source_mapping_exists(self): self.conn.list_event_source_mappings.return_value = { - 'EventSourceMappings': [event_source_mapping_ret]} + "EventSourceMappings": [event_source_mapping_ret] + } self.conn.get_event_source_mapping.return_value = event_source_mapping_ret - result = self.salt_states['boto_lambda.event_source_mapping_absent']( - 'event source mapping absent', - EventSourceArn=event_source_mapping_ret[ - 'EventSourceArn'], - FunctionName='myfunc') - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new'][ - 'event_source_mapping'], None) + result = self.salt_states["boto_lambda.event_source_mapping_absent"]( + "event source mapping absent", + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName="myfunc", + ) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["event_source_mapping"], None) def test_absent_with_failure(self): self.conn.list_event_source_mappings.return_value = { - 'EventSourceMappings': [event_source_mapping_ret]} + "EventSourceMappings": [event_source_mapping_ret] + } self.conn.get_event_source_mapping.return_value = event_source_mapping_ret self.conn.delete_event_source_mapping.side_effect = ClientError( - error_content, 'delete_event_source_mapping') - result = self.salt_states['boto_lambda.event_source_mapping_absent']( - 'event source mapping absent', - EventSourceArn=event_source_mapping_ret[ - 'EventSourceArn'], - FunctionName='myfunc') - self.assertFalse(result['result']) - self.assertTrue('An error occurred' in result['comment']) + error_content, "delete_event_source_mapping" + ) + result = self.salt_states["boto_lambda.event_source_mapping_absent"]( + "event source mapping absent", + EventSourceArn=event_source_mapping_ret["EventSourceArn"], + FunctionName="myfunc", + ) + self.assertFalse(result["result"]) + self.assertTrue("An error occurred" in result["comment"]) diff --git a/tests/unit/states/test_boto_lc.py b/tests/unit/states/test_boto_lc.py index 62cc3f196ae..aa3c0c40c43 100644 --- a/tests/unit/states/test_boto_lc.py +++ b/tests/unit/states/test_boto_lc.py @@ -1,77 +1,80 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.states.boto_lc as boto_lc from salt.exceptions import SaltInvocationError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoLcTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_lc - ''' + """ + def setup_loader_modules(self): return {boto_lc: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the launch configuration exists. - ''' - name = 'mylc' - image_id = 'ami-0b9c9f62' + """ + name = "mylc" + image_id = "ami-0b9c9f62" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} - self.assertRaises(SaltInvocationError, boto_lc.present, name, - image_id, user_data=True, cloud_init=True) + self.assertRaises( + SaltInvocationError, + boto_lc.present, + name, + image_id, + user_data=True, + cloud_init=True, + ) mock = MagicMock(side_effect=[True, False]) - with patch.dict(boto_lc.__salt__, - {'boto_asg.launch_configuration_exists': mock}): - comt = ('Launch configuration present.') - ret.update({'comment': comt}) + with patch.dict( + boto_lc.__salt__, {"boto_asg.launch_configuration_exists": mock} + ): + comt = "Launch configuration present." + ret.update({"comment": comt}) self.assertDictEqual(boto_lc.present(name, image_id), ret) - with patch.dict(boto_lc.__opts__, {'test': True}): - comt = ('Launch configuration set to be created.') - ret.update({'comment': comt, 'result': None}) + with patch.dict(boto_lc.__opts__, {"test": True}): + comt = "Launch configuration set to be created." + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_lc.present(name, image_id), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the named launch configuration is deleted. - ''' - name = 'mylc' + """ + name = "mylc" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[False, True]) - with patch.dict(boto_lc.__salt__, - {'boto_asg.launch_configuration_exists': mock}): - comt = ('Launch configuration does not exist.') - ret.update({'comment': comt}) + with patch.dict( + boto_lc.__salt__, {"boto_asg.launch_configuration_exists": mock} + ): + comt = "Launch configuration does not exist." + ret.update({"comment": comt}) self.assertDictEqual(boto_lc.absent(name), ret) - with patch.dict(boto_lc.__opts__, {'test': True}): - comt = ('Launch configuration set to be deleted.') - ret.update({'comment': comt, 'result': None}) + with patch.dict(boto_lc.__opts__, {"test": True}): + comt = "Launch configuration set to be deleted." + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_lc.absent(name), ret) diff --git a/tests/unit/states/test_boto_route53.py b/tests/unit/states/test_boto_route53.py index d0b66b2f351..3718a9e2606 100644 --- a/tests/unit/states/test_boto_route53.py +++ b/tests/unit/states/test_boto_route53.py @@ -1,95 +1,90 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.boto_route53 as boto_route53 +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoRoute53TestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_route53 - ''' + """ + def setup_loader_modules(self): return {boto_route53: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the Route53 record is present. - ''' - name = 'test.example.com.' - value = '1.1.1.1' - zone = 'example.com.' - record_type = 'A' + """ + name = "test.example.com." + value = "1.1.1.1" + zone = "example.com." + record_type = "A" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - mock = MagicMock(side_effect=[{}, {}, {'value': ''}, False]) + mock = MagicMock(side_effect=[{}, {}, {"value": ""}, False]) mock_bool = MagicMock(return_value=False) - with patch.dict(boto_route53.__salt__, - {'boto_route53.get_record': mock, - 'boto_route53.add_record': mock_bool}): - with patch.dict(boto_route53.__opts__, {'test': False}): - comt = ('Failed to add {0} Route53 record.'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(boto_route53.present(name, value, zone, - record_type), ret) + with patch.dict( + boto_route53.__salt__, + {"boto_route53.get_record": mock, "boto_route53.add_record": mock_bool}, + ): + with patch.dict(boto_route53.__opts__, {"test": False}): + comt = "Failed to add {0} Route53 record.".format(name) + ret.update({"comment": comt}) + self.assertDictEqual( + boto_route53.present(name, value, zone, record_type), ret + ) - with patch.dict(boto_route53.__opts__, {'test': True}): - comt = ('Route53 record {0} set to be added.'.format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(boto_route53.present(name, value, zone, - record_type), ret) + with patch.dict(boto_route53.__opts__, {"test": True}): + comt = "Route53 record {0} set to be added.".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + boto_route53.present(name, value, zone, record_type), ret + ) - comt = ('Route53 record {0} set to be updated.'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(boto_route53.present(name, value, zone, - record_type), ret) + comt = "Route53 record {0} set to be updated.".format(name) + ret.update({"comment": comt}) + self.assertDictEqual( + boto_route53.present(name, value, zone, record_type), ret + ) - ret.update({'comment': '', 'result': True}) - self.assertDictEqual(boto_route53.present(name, value, zone, - record_type), ret) + ret.update({"comment": "", "result": True}) + self.assertDictEqual( + boto_route53.present(name, value, zone, record_type), ret + ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the Route53 record is deleted. - ''' - name = 'test.example.com.' - zone = 'example.com.' - record_type = 'A' + """ + name = "test.example.com." + zone = "example.com." + record_type = "A" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[False, True]) - with patch.dict(boto_route53.__salt__, - {'boto_route53.get_record': mock}): - comt = ('{0} does not exist.'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(boto_route53.absent(name, zone, record_type), - ret) + with patch.dict(boto_route53.__salt__, {"boto_route53.get_record": mock}): + comt = "{0} does not exist.".format(name) + ret.update({"comment": comt}) + self.assertDictEqual(boto_route53.absent(name, zone, record_type), ret) - with patch.dict(boto_route53.__opts__, {'test': True}): - comt = ('Route53 record {0} set to be deleted.'.format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(boto_route53.absent(name, zone, - record_type), ret) + with patch.dict(boto_route53.__opts__, {"test": True}): + comt = "Route53 record {0} set to be deleted.".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual(boto_route53.absent(name, zone, record_type), ret) diff --git a/tests/unit/states/test_boto_s3_bucket.py b/tests/unit/states/test_boto_s3_bucket.py index 8c6c96b083c..09fe97a2c6b 100644 --- a/tests/unit/states/test_boto_s3_bucket.py +++ b/tests/unit/states/test_boto_s3_bucket.py @@ -2,31 +2,35 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -from copy import deepcopy + import logging import random import string +from copy import deepcopy -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch +import salt.loader +import salt.states.boto_s3_bucket as boto_s3_bucket # Import Salt libs from salt.ext import six -import salt.loader + +# Import 3rd-party libs +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin from salt.utils.versions import LooseVersion -import salt.states.boto_s3_bucket as boto_s3_bucket + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error,no-name-in-module,unused-import from tests.unit.modules.test_boto_s3_bucket import BotoS3BucketTestCaseMixin -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin try: import boto import boto3 from botocore.exceptions import ClientError + HAS_BOTO = True except ImportError: HAS_BOTO = False @@ -36,16 +40,16 @@ except ImportError: # the boto_s3_bucket module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto3_version = '1.2.1' +required_boto3_version = "1.2.1" log = logging.getLogger(__name__) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version): @@ -55,215 +59,164 @@ def _has_required_boto(): if _has_required_boto(): - region = 'us-east-1' - access_key = 'GKTADJGHEIQSXMKKRBJ08H' - secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' - conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} - error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error' - not_found_error = ClientError({ - 'Error': { - 'Code': '404', - 'Message': "Test-defined error" - } - }, 'msg') - error_content = { - 'Error': { - 'Code': 101, - 'Message': "Test-defined error" - } + region = "us-east-1" + access_key = "GKTADJGHEIQSXMKKRBJ08H" + secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" + conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, } + error_message = ( + "An error occurred (101) when calling the {0} operation: Test-defined error" + ) + not_found_error = ClientError( + {"Error": {"Code": "404", "Message": "Test-defined error"}}, "msg" + ) + error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}} list_ret = { - 'Buckets': [{ - 'Name': 'mybucket', - 'CreationDate': None - }], - 'Owner': { - 'Type': 'CanonicalUser', - 'DisplayName': 'testuser', - 'ID': '111111222222' + "Buckets": [{"Name": "mybucket", "CreationDate": None}], + "Owner": { + "Type": "CanonicalUser", + "DisplayName": "testuser", + "ID": "111111222222", }, - 'ResponseMetadata': {'Key': 'Value'} + "ResponseMetadata": {"Key": "Value"}, } config_in = { - 'LocationConstraint': 'EU', - 'ACL': { - 'ACL': 'public-read' - }, - 'CORSRules': [{ - 'AllowedMethods': ["GET"], - 'AllowedOrigins': ["*"], - }], - 'LifecycleConfiguration': [{ - 'Expiration': { - 'Days': 1 - }, - 'Prefix': 'prefix', - 'Status': 'Enabled', - 'ID': 'asdfghjklpoiuytrewq' - }], - 'Logging': { - 'TargetBucket': 'my-bucket', - 'TargetPrefix': 'prefix' - }, - 'NotificationConfiguration': { - 'LambdaFunctionConfigurations': [{ - 'LambdaFunctionArn': 'arn:aws:lambda:us-east-1:111111222222:function:my-function', - 'Id': 'zxcvbnmlkjhgfdsa', - 'Events': ["s3:ObjectCreated:*"], - 'Filter': { - 'Key': { - 'FilterRules': [{ - 'Name': 'prefix', - 'Value': 'string' - }] - } - } - }] - }, - 'Policy': { - 'Version': "2012-10-17", - 'Statement': [{ - 'Sid': "", - 'Effect': "Allow", - 'Principal': { - 'AWS': "arn:aws:iam::111111222222:root" - }, - 'Action': "s3:PutObject", - 'Resource': "arn:aws:s3:::my-bucket/*" - }] - }, - 'Replication': { - 'Role': 'arn:aws:iam::11111222222:my-role', - 'Rules': [{ - 'ID': "r1", - 'Prefix': "prefix", - 'Status': "Enabled", - 'Destination': { - 'Bucket': "arn:aws:s3:::my-bucket" - } - }] - }, - 'RequestPayment': { - 'Payer': 'Requester' - }, - 'Tagging': { - 'a': 'b', - 'c': 'd' - }, - 'Versioning': { - 'Status': 'Enabled' - }, - 'Website': { - 'ErrorDocument': { - 'Key': 'error.html' - }, - 'IndexDocument': { - 'Suffix': 'index.html' + "LocationConstraint": "EU", + "ACL": {"ACL": "public-read"}, + "CORSRules": [{"AllowedMethods": ["GET"], "AllowedOrigins": ["*"]}], + "LifecycleConfiguration": [ + { + "Expiration": {"Days": 1}, + "Prefix": "prefix", + "Status": "Enabled", + "ID": "asdfghjklpoiuytrewq", } - } + ], + "Logging": {"TargetBucket": "my-bucket", "TargetPrefix": "prefix"}, + "NotificationConfiguration": { + "LambdaFunctionConfigurations": [ + { + "LambdaFunctionArn": "arn:aws:lambda:us-east-1:111111222222:function:my-function", + "Id": "zxcvbnmlkjhgfdsa", + "Events": ["s3:ObjectCreated:*"], + "Filter": { + "Key": {"FilterRules": [{"Name": "prefix", "Value": "string"}]} + }, + } + ] + }, + "Policy": { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "", + "Effect": "Allow", + "Principal": {"AWS": "arn:aws:iam::111111222222:root"}, + "Action": "s3:PutObject", + "Resource": "arn:aws:s3:::my-bucket/*", + } + ], + }, + "Replication": { + "Role": "arn:aws:iam::11111222222:my-role", + "Rules": [ + { + "ID": "r1", + "Prefix": "prefix", + "Status": "Enabled", + "Destination": {"Bucket": "arn:aws:s3:::my-bucket"}, + } + ], + }, + "RequestPayment": {"Payer": "Requester"}, + "Tagging": {"a": "b", "c": "d"}, + "Versioning": {"Status": "Enabled"}, + "Website": { + "ErrorDocument": {"Key": "error.html"}, + "IndexDocument": {"Suffix": "index.html"}, + }, } config_ret = { - 'get_bucket_acl': { - 'Grants': [{ - 'Grantee': { - 'Type': 'Group', - 'URI': 'http://acs.amazonaws.com/groups/global/AllUsers' - }, - 'Permission': 'READ' - }], - 'Owner': { - 'DisplayName': 'testuser', - 'ID': '111111222222' - } - }, - 'get_bucket_cors': { - 'CORSRules': [{ - 'AllowedMethods': ["GET"], - 'AllowedOrigins': ["*"], - }] - }, - 'get_bucket_lifecycle_configuration': { - 'Rules': [{ - 'Expiration': { - 'Days': 1 - }, - 'Prefix': 'prefix', - 'Status': 'Enabled', - 'ID': 'asdfghjklpoiuytrewq' - }] - }, - 'get_bucket_location': { - 'LocationConstraint': 'EU' - }, - 'get_bucket_logging': { - 'LoggingEnabled': { - 'TargetBucket': 'my-bucket', - 'TargetPrefix': 'prefix' - } - }, - 'get_bucket_notification_configuration': { - 'LambdaFunctionConfigurations': [{ - 'LambdaFunctionArn': 'arn:aws:lambda:us-east-1:111111222222:function:my-function', - 'Id': 'zxcvbnmlkjhgfdsa', - 'Events': ["s3:ObjectCreated:*"], - 'Filter': { - 'Key': { - 'FilterRules': [{ - 'Name': 'prefix', - 'Value': 'string' - }] - } + "get_bucket_acl": { + "Grants": [ + { + "Grantee": { + "Type": "Group", + "URI": "http://acs.amazonaws.com/groups/global/AllUsers", + }, + "Permission": "READ", } - }] + ], + "Owner": {"DisplayName": "testuser", "ID": "111111222222"}, }, - 'get_bucket_policy': { - 'Policy': - '{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111222222:root"},"Action":"s3:PutObject","Resource":"arn:aws:s3:::my-bucket/*"}]}' + "get_bucket_cors": { + "CORSRules": [{"AllowedMethods": ["GET"], "AllowedOrigins": ["*"]}] }, - 'get_bucket_replication': { - 'ReplicationConfiguration': { - 'Role': 'arn:aws:iam::11111222222:my-role', - 'Rules': [{ - 'ID': "r1", - 'Prefix': "prefix", - 'Status': "Enabled", - 'Destination': { - 'Bucket': "arn:aws:s3:::my-bucket" + "get_bucket_lifecycle_configuration": { + "Rules": [ + { + "Expiration": {"Days": 1}, + "Prefix": "prefix", + "Status": "Enabled", + "ID": "asdfghjklpoiuytrewq", + } + ] + }, + "get_bucket_location": {"LocationConstraint": "EU"}, + "get_bucket_logging": { + "LoggingEnabled": {"TargetBucket": "my-bucket", "TargetPrefix": "prefix"} + }, + "get_bucket_notification_configuration": { + "LambdaFunctionConfigurations": [ + { + "LambdaFunctionArn": "arn:aws:lambda:us-east-1:111111222222:function:my-function", + "Id": "zxcvbnmlkjhgfdsa", + "Events": ["s3:ObjectCreated:*"], + "Filter": { + "Key": {"FilterRules": [{"Name": "prefix", "Value": "string"}]} + }, + } + ] + }, + "get_bucket_policy": { + "Policy": '{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::111111222222:root"},"Action":"s3:PutObject","Resource":"arn:aws:s3:::my-bucket/*"}]}' + }, + "get_bucket_replication": { + "ReplicationConfiguration": { + "Role": "arn:aws:iam::11111222222:my-role", + "Rules": [ + { + "ID": "r1", + "Prefix": "prefix", + "Status": "Enabled", + "Destination": {"Bucket": "arn:aws:s3:::my-bucket"}, } - }] + ], } }, - 'get_bucket_request_payment': {'Payer': 'Requester'}, - 'get_bucket_tagging': { - 'TagSet': [{ - 'Key': 'c', - 'Value': 'd' - }, { - 'Key': 'a', - 'Value': 'b', - }] + "get_bucket_request_payment": {"Payer": "Requester"}, + "get_bucket_tagging": { + "TagSet": [{"Key": "c", "Value": "d"}, {"Key": "a", "Value": "b"}] }, - 'get_bucket_versioning': { - 'Status': 'Enabled' + "get_bucket_versioning": {"Status": "Enabled"}, + "get_bucket_website": { + "ErrorDocument": {"Key": "error.html"}, + "IndexDocument": {"Suffix": "index.html"}, }, - 'get_bucket_website': { - 'ErrorDocument': { - 'Key': 'error.html' - }, - 'IndexDocument': { - 'Suffix': 'index.html' - } - } - } - bucket_ret = { - 'Location': 'EU' } + bucket_ret = {"Location": "EU"} -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) class BotoS3BucketStateTestCaseBase(TestCase, LoaderModuleMockMixin): conn = None @@ -271,131 +224,154 @@ class BotoS3BucketStateTestCaseBase(TestCase, LoaderModuleMockMixin): ctx = {} utils = salt.loader.utils( self.opts, - whitelist=['boto', 'boto3', 'args', 'systemd', 'path', 'platform', 'reg'], - context=ctx) + whitelist=["boto", "boto3", "args", "systemd", "path", "platform", "reg"], + context=ctx, + ) serializers = salt.loader.serializers(self.opts) - self.funcs = funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_s3_bucket']) - self.salt_states = salt.loader.states(opts=self.opts, functions=funcs, utils=utils, whitelist=['boto_s3_bucket'], - serializers=serializers) + self.funcs = funcs = salt.loader.minion_mods( + self.opts, context=ctx, utils=utils, whitelist=["boto_s3_bucket"] + ) + self.salt_states = salt.loader.states( + opts=self.opts, + functions=funcs, + utils=utils, + whitelist=["boto_s3_bucket"], + serializers=serializers, + ) return { boto_s3_bucket: { - '__opts__': self.opts, - '__salt__': funcs, - '__utils__': utils, - '__states__': self.salt_states, - '__serializers__': serializers, + "__opts__": self.opts, + "__salt__": funcs, + "__utils__": utils, + "__states__": self.salt_states, + "__serializers__": serializers, } } @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) @classmethod def tearDownClass(cls): del cls.opts def setUp(self): - self.addCleanup(delattr, self, 'funcs') - self.addCleanup(delattr, self, 'salt_states') + self.addCleanup(delattr, self, "funcs") + self.addCleanup(delattr, self, "salt_states") # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) - self.patcher = patch('boto3.session.Session') + self.patcher = patch("boto3.session.Session") self.addCleanup(self.patcher.stop) - self.addCleanup(delattr, self, 'patcher') + self.addCleanup(delattr, self, "patcher") mock_session = self.patcher.start() session_instance = mock_session.return_value self.conn = MagicMock() - self.addCleanup(delattr, self, 'conn') + self.addCleanup(delattr, self, "conn") session_instance.client.return_value = self.conn class BotoS3BucketTestCase(BotoS3BucketStateTestCaseBase, BotoS3BucketTestCaseMixin): - ''' + """ TestCase for salt.modules.boto_s3_bucket state.module - ''' + """ def test_present_when_bucket_does_not_exist(self): - ''' + """ Tests present on a bucket that does not exist. - ''' + """ self.conn.head_bucket.side_effect = [not_found_error, None] self.conn.list_buckets.return_value = deepcopy(list_ret) self.conn.create_bucket.return_value = bucket_ret for key, value in six.iteritems(config_ret): getattr(self.conn, key).return_value = deepcopy(value) - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='111111222222')}): - result = self.salt_states['boto_s3_bucket.present']( - 'bucket present', - Bucket='testbucket', - **config_in - ) + with patch.dict( + self.funcs, + {"boto_iam.get_account_id": MagicMock(return_value="111111222222")}, + ): + result = self.salt_states["boto_s3_bucket.present"]( + "bucket present", Bucket="testbucket", **config_in + ) - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['bucket']['Location'], config_ret['get_bucket_location']) + self.assertTrue(result["result"]) + self.assertEqual( + result["changes"]["new"]["bucket"]["Location"], + config_ret["get_bucket_location"], + ) def test_present_when_bucket_exists_no_mods(self): self.conn.list_buckets.return_value = deepcopy(list_ret) for key, value in six.iteritems(config_ret): getattr(self.conn, key).return_value = deepcopy(value) - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='111111222222')}): - result = self.salt_states['boto_s3_bucket.present']( - 'bucket present', - Bucket='testbucket', - **config_in - ) + with patch.dict( + self.funcs, + {"boto_iam.get_account_id": MagicMock(return_value="111111222222")}, + ): + result = self.salt_states["boto_s3_bucket.present"]( + "bucket present", Bucket="testbucket", **config_in + ) - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_present_when_bucket_exists_all_mods(self): self.conn.list_buckets.return_value = deepcopy(list_ret) for key, value in six.iteritems(config_ret): getattr(self.conn, key).return_value = deepcopy(value) - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='111111222222')}): - result = self.salt_states['boto_s3_bucket.present']( - 'bucket present', - Bucket='testbucket', - LocationConstraint=config_in['LocationConstraint'] - ) + with patch.dict( + self.funcs, + {"boto_iam.get_account_id": MagicMock(return_value="111111222222")}, + ): + result = self.salt_states["boto_s3_bucket.present"]( + "bucket present", + Bucket="testbucket", + LocationConstraint=config_in["LocationConstraint"], + ) - self.assertTrue(result['result']) - self.assertNotEqual(result['changes'], {}) + self.assertTrue(result["result"]) + self.assertNotEqual(result["changes"], {}) def test_present_with_failure(self): self.conn.head_bucket.side_effect = [not_found_error, None] self.conn.list_buckets.return_value = deepcopy(list_ret) - self.conn.create_bucket.side_effect = ClientError(error_content, 'create_bucket') - with patch.dict(self.funcs, {'boto_iam.get_account_id': MagicMock(return_value='111111222222')}): - result = self.salt_states['boto_s3_bucket.present']( - 'bucket present', - Bucket='testbucket', - **config_in - ) - self.assertFalse(result['result']) - self.assertTrue('Failed to create bucket' in result['comment']) + self.conn.create_bucket.side_effect = ClientError( + error_content, "create_bucket" + ) + with patch.dict( + self.funcs, + {"boto_iam.get_account_id": MagicMock(return_value="111111222222")}, + ): + result = self.salt_states["boto_s3_bucket.present"]( + "bucket present", Bucket="testbucket", **config_in + ) + self.assertFalse(result["result"]) + self.assertTrue("Failed to create bucket" in result["comment"]) def test_absent_when_bucket_does_not_exist(self): - ''' + """ Tests absent on a bucket that does not exist. - ''' + """ self.conn.head_bucket.side_effect = [not_found_error, None] - result = self.salt_states['boto_s3_bucket.absent']('test', 'mybucket') - self.assertTrue(result['result']) - self.assertEqual(result['changes'], {}) + result = self.salt_states["boto_s3_bucket.absent"]("test", "mybucket") + self.assertTrue(result["result"]) + self.assertEqual(result["changes"], {}) def test_absent_when_bucket_exists(self): - result = self.salt_states['boto_s3_bucket.absent']('test', 'testbucket') - self.assertTrue(result['result']) - self.assertEqual(result['changes']['new']['bucket'], None) + result = self.salt_states["boto_s3_bucket.absent"]("test", "testbucket") + self.assertTrue(result["result"]) + self.assertEqual(result["changes"]["new"]["bucket"], None) def test_absent_with_failure(self): - self.conn.delete_bucket.side_effect = ClientError(error_content, 'delete_bucket') - result = self.salt_states['boto_s3_bucket.absent']('test', 'testbucket') - self.assertFalse(result['result']) - self.assertTrue('Failed to delete bucket' in result['comment']) + self.conn.delete_bucket.side_effect = ClientError( + error_content, "delete_bucket" + ) + result = self.salt_states["boto_s3_bucket.absent"]("test", "testbucket") + self.assertFalse(result["result"]) + self.assertTrue("Failed to delete bucket" in result["comment"]) diff --git a/tests/unit/states/test_boto_secgroup.py b/tests/unit/states/test_boto_secgroup.py index 3ee10fcf25c..181f4642360 100644 --- a/tests/unit/states/test_boto_secgroup.py +++ b/tests/unit/states/test_boto_secgroup.py @@ -3,48 +3,125 @@ # import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.case import TestCase - # Import Salt Libs import salt.states.boto_secgroup as boto_secgroup from salt.utils.odict import OrderedDict +from tests.support.case import TestCase + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin class Boto_SecgroupTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.states.boto_secgroup module - ''' + """ + def setup_loader_modules(self): return {boto_secgroup: {}} def test__get_rule_changes_no_rules_no_change(self): - ''' + """ tests a condition with no rules in present or desired group - ''' + """ present_rules = [] desired_rules = [] - self.assertEqual(boto_secgroup._get_rule_changes(desired_rules, present_rules), ([], [])) + self.assertEqual( + boto_secgroup._get_rule_changes(desired_rules, present_rules), ([], []) + ) def test__get_rule_changes_create_rules(self): - ''' + """ tests a condition where a rule must be created - ''' - present_rules = [OrderedDict([('ip_protocol', 'tcp'), ('from_port', 22), ('to_port', 22), ('cidr_ip', '0.0.0.0/0')])] - desired_rules = [OrderedDict([('ip_protocol', 'tcp'), ('from_port', 22), ('to_port', 22), ('cidr_ip', '0.0.0.0/0')]), - OrderedDict([('ip_protocol', 'tcp'), ('from_port', 80), ('to_port', 80), ('cidr_ip', '0.0.0.0/0')])] + """ + present_rules = [ + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 22), + ("to_port", 22), + ("cidr_ip", "0.0.0.0/0"), + ] + ) + ] + desired_rules = [ + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 22), + ("to_port", 22), + ("cidr_ip", "0.0.0.0/0"), + ] + ), + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 80), + ("to_port", 80), + ("cidr_ip", "0.0.0.0/0"), + ] + ), + ] # can also use: rules_to_create = [rule for rule in desired_rules if rule not in present_rules] - rules_to_create = [OrderedDict([('ip_protocol', 'tcp'), ('from_port', 80), ('to_port', 80), ('cidr_ip', '0.0.0.0/0')])] - self.assertEqual(boto_secgroup._get_rule_changes(desired_rules, present_rules), ([], rules_to_create)) + rules_to_create = [ + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 80), + ("to_port", 80), + ("cidr_ip", "0.0.0.0/0"), + ] + ) + ] + self.assertEqual( + boto_secgroup._get_rule_changes(desired_rules, present_rules), + ([], rules_to_create), + ) def test__get_rule_changes_delete_rules(self): - ''' + """ tests a condition where a rule must be deleted - ''' - present_rules = [OrderedDict([('ip_protocol', 'tcp'), ('from_port', 22), ('to_port', 22), ('cidr_ip', '0.0.0.0/0')]), - OrderedDict([('ip_protocol', 'tcp'), ('from_port', 80), ('to_port', 80), ('cidr_ip', '0.0.0.0/0')])] - desired_rules = [OrderedDict([('ip_protocol', 'tcp'), ('from_port', 22), ('to_port', 22), ('cidr_ip', '0.0.0.0/0')])] + """ + present_rules = [ + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 22), + ("to_port", 22), + ("cidr_ip", "0.0.0.0/0"), + ] + ), + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 80), + ("to_port", 80), + ("cidr_ip", "0.0.0.0/0"), + ] + ), + ] + desired_rules = [ + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 22), + ("to_port", 22), + ("cidr_ip", "0.0.0.0/0"), + ] + ) + ] # can also use: rules_to_delete = [rule for rule in present_rules if rule not in desired_rules] - rules_to_delete = [OrderedDict([('ip_protocol', 'tcp'), ('from_port', 80), ('to_port', 80), ('cidr_ip', '0.0.0.0/0')])] - self.assertEqual(boto_secgroup._get_rule_changes(desired_rules, present_rules), (rules_to_delete, [])) + rules_to_delete = [ + OrderedDict( + [ + ("ip_protocol", "tcp"), + ("from_port", 80), + ("to_port", 80), + ("cidr_ip", "0.0.0.0/0"), + ] + ) + ] + self.assertEqual( + boto_secgroup._get_rule_changes(desired_rules, present_rules), + (rules_to_delete, []), + ) diff --git a/tests/unit/states/test_boto_sns.py b/tests/unit/states/test_boto_sns.py index 70da6084729..23279104a95 100644 --- a/tests/unit/states/test_boto_sns.py +++ b/tests/unit/states/test_boto_sns.py @@ -1,143 +1,170 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.states.boto_sns as boto_sns +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoSnsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_sns - ''' + """ + def setup_loader_modules(self): return {boto_sns: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the SNS topic exists. - ''' - name = 'test.example.com.' + """ + name = "test.example.com." - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False, False]) mock_bool = MagicMock(return_value=False) - with patch.dict(boto_sns.__salt__, - {'boto_sns.exists': mock, - 'boto_sns.create': mock_bool}): - comt = ('AWS SNS topic {0} present.'.format(name)) - ret.update({'comment': comt}) + with patch.dict( + boto_sns.__salt__, {"boto_sns.exists": mock, "boto_sns.create": mock_bool} + ): + comt = "AWS SNS topic {0} present.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_sns.present(name), ret) - with patch.dict(boto_sns.__opts__, {'test': True}): - comt = ('AWS SNS topic {0} is set to be created.'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(boto_sns.__opts__, {"test": True}): + comt = "AWS SNS topic {0} is set to be created.".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_sns.present(name), ret) - with patch.dict(boto_sns.__opts__, {'test': False}): - comt = ('Failed to create {0} AWS SNS topic'.format(name)) - ret.update({'comment': comt, 'result': False}) + with patch.dict(boto_sns.__opts__, {"test": False}): + comt = "Failed to create {0} AWS SNS topic".format(name) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(boto_sns.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the named sns topic is deleted. - ''' - name = 'test.example.com.' + """ + name = "test.example.com." - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} self.maxDiff = None exists_mock = MagicMock(side_effect=[False, True, True, True, True, True, True]) - with patch.dict(boto_sns.__salt__, - {'boto_sns.exists': exists_mock}): + with patch.dict(boto_sns.__salt__, {"boto_sns.exists": exists_mock}): # tests topic already absent - comt = ('AWS SNS topic {0} does not exist.'.format(name)) - ret.update({'comment': comt}) + comt = "AWS SNS topic {0} does not exist.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(boto_sns.absent(name), ret) - with patch.dict(boto_sns.__opts__, {'test': True}): + with patch.dict(boto_sns.__opts__, {"test": True}): # tests topic present, test option, unsubscribe is False - comt = ('AWS SNS topic {0} is set to be removed. ' - '0 subscription(s) will be removed.'.format(name)) - ret.update({'comment': comt, 'result': None}) + comt = ( + "AWS SNS topic {0} is set to be removed. " + "0 subscription(s) will be removed.".format(name) + ) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_sns.absent(name), ret) - subscriptions = [dict( - Endpoint='arn:aws:lambda:us-west-2:123456789:function:test', - Owner=123456789, - Protocol='Lambda', - TopicArn='arn:aws:sns:us-west-2:123456789:test', - SubscriptionArn='arn:aws:sns:us-west-2:123456789:test:some_uuid' - )] - with patch.dict(boto_sns.__opts__, {'test': True}): + subscriptions = [ + dict( + Endpoint="arn:aws:lambda:us-west-2:123456789:function:test", + Owner=123456789, + Protocol="Lambda", + TopicArn="arn:aws:sns:us-west-2:123456789:test", + SubscriptionArn="arn:aws:sns:us-west-2:123456789:test:some_uuid", + ) + ] + with patch.dict(boto_sns.__opts__, {"test": True}): subs_mock = MagicMock(return_value=subscriptions) - with patch.dict(boto_sns.__salt__, - {'boto_sns.get_all_subscriptions_by_topic': subs_mock}): + with patch.dict( + boto_sns.__salt__, + {"boto_sns.get_all_subscriptions_by_topic": subs_mock}, + ): # tests topic present, 1 subscription, test option, unsubscribe is True - comt = ('AWS SNS topic {0} is set to be removed. ' - '1 subscription(s) will be removed.'.format(name)) - ret.update({'comment': comt, 'result': None}) + comt = ( + "AWS SNS topic {0} is set to be removed. " + "1 subscription(s) will be removed.".format(name) + ) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(boto_sns.absent(name, unsubscribe=True), ret) subs_mock = MagicMock(return_value=subscriptions) unsubscribe_mock = MagicMock(side_effect=[True, False]) - with patch.dict(boto_sns.__salt__, - {'boto_sns.unsubscribe': unsubscribe_mock}): - with patch.dict(boto_sns.__salt__, - {'boto_sns.get_all_subscriptions_by_topic': subs_mock}): + with patch.dict( + boto_sns.__salt__, {"boto_sns.unsubscribe": unsubscribe_mock} + ): + with patch.dict( + boto_sns.__salt__, + {"boto_sns.get_all_subscriptions_by_topic": subs_mock}, + ): delete_mock = MagicMock(side_effect=[True, True, True, False]) - with patch.dict(boto_sns.__salt__, - {'boto_sns.delete': delete_mock}): + with patch.dict( + boto_sns.__salt__, {"boto_sns.delete": delete_mock} + ): # tests topic present, unsubscribe flag True, unsubscribe succeeded, # delete succeeded - comt = ('AWS SNS topic {0} deleted.'.format(name)) - ret.update({'changes': {'new': None, - 'old': {'topic': name, - 'subscriptions': subscriptions}}, - 'result': True, - 'comment': comt}) - self.assertDictEqual(boto_sns.absent(name, unsubscribe=True), ret) + comt = "AWS SNS topic {0} deleted.".format(name) + ret.update( + { + "changes": { + "new": None, + "old": { + "topic": name, + "subscriptions": subscriptions, + }, + }, + "result": True, + "comment": comt, + } + ) + self.assertDictEqual( + boto_sns.absent(name, unsubscribe=True), ret + ) # tests topic present, unsubscribe flag True, unsubscribe fails, # delete succeeded - ret.update({'changes': {'new': {'subscriptions': subscriptions}, - 'old': {'topic': name, - 'subscriptions': subscriptions}}, - 'result': True, - 'comment': comt}) - self.assertDictEqual(boto_sns.absent(name, unsubscribe=True), ret) + ret.update( + { + "changes": { + "new": {"subscriptions": subscriptions}, + "old": { + "topic": name, + "subscriptions": subscriptions, + }, + }, + "result": True, + "comment": comt, + } + ) + self.assertDictEqual( + boto_sns.absent(name, unsubscribe=True), ret + ) # tests topic present, unsubscribe flag False, delete succeeded - ret.update({'changes': {'new': None, - 'old': {'topic': name}}, - 'result': True, - 'comment': comt}) + ret.update( + { + "changes": {"new": None, "old": {"topic": name}}, + "result": True, + "comment": comt, + } + ) self.assertDictEqual(boto_sns.absent(name), ret) # tests topic present, unsubscribe flag False, delete failed - comt = 'Failed to delete {0} AWS SNS topic.'.format(name) - ret.update({'changes': {}, - 'result': False, - 'comment': comt}) + comt = "Failed to delete {0} AWS SNS topic.".format(name) + ret.update({"changes": {}, "result": False, "comment": comt}) self.assertDictEqual(boto_sns.absent(name), ret) diff --git a/tests/unit/states/test_boto_sqs.py b/tests/unit/states/test_boto_sqs.py index edecaf558e3..668f065346d 100644 --- a/tests/unit/states/test_boto_sqs.py +++ b/tests/unit/states/test_boto_sqs.py @@ -1,36 +1,35 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import textwrap -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +import textwrap # Import Salt Libs import salt.config import salt.loader import salt.states.boto_sqs as boto_sqs +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BotoSqsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.boto_sqs - ''' + """ + def setup_loader_modules(self): utils = salt.loader.utils( self.opts, - whitelist=['boto3', 'yaml', 'args', 'systemd', 'path', 'platform'], - context={}) - return { - boto_sqs: { - '__utils__': utils, - } - } + whitelist=["boto3", "yaml", "args", "systemd", "path", "platform"], + context={}, + ) + return {boto_sqs: {"__utils__": utils}} @classmethod def setUpClass(cls): @@ -43,95 +42,97 @@ class BotoSqsTestCase(TestCase, LoaderModuleMockMixin): # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the SQS queue exists. - ''' - name = 'mysqs' - attributes = {'DelaySeconds': 20} - base_ret = {'name': name, 'changes': {}} + """ + name = "mysqs" + attributes = {"DelaySeconds": 20} + base_ret = {"name": name, "changes": {}} mock = MagicMock( - side_effect=[{'result': b} for b in [False, False, True, True]], + side_effect=[{"result": b} for b in [False, False, True, True]], ) - mock_bool = MagicMock(return_value={'error': 'create error'}) - mock_attr = MagicMock(return_value={'result': {}}) - with patch.dict(boto_sqs.__salt__, - {'boto_sqs.exists': mock, - 'boto_sqs.create': mock_bool, - 'boto_sqs.get_attributes': mock_attr}): - with patch.dict(boto_sqs.__opts__, {'test': False}): - comt = ['Failed to create SQS queue {0}: create error'.format( - name, - )] + mock_bool = MagicMock(return_value={"error": "create error"}) + mock_attr = MagicMock(return_value={"result": {}}) + with patch.dict( + boto_sqs.__salt__, + { + "boto_sqs.exists": mock, + "boto_sqs.create": mock_bool, + "boto_sqs.get_attributes": mock_attr, + }, + ): + with patch.dict(boto_sqs.__opts__, {"test": False}): + comt = ["Failed to create SQS queue {0}: create error".format(name,)] ret = base_ret.copy() - ret.update({'result': False, 'comment': comt}) + ret.update({"result": False, "comment": comt}) self.assertDictEqual(boto_sqs.present(name), ret) - with patch.dict(boto_sqs.__opts__, {'test': True}): - comt = ['SQS queue {0} is set to be created.'.format(name)] + with patch.dict(boto_sqs.__opts__, {"test": True}): + comt = ["SQS queue {0} is set to be created.".format(name)] ret = base_ret.copy() - ret.update({ - 'result': None, - 'comment': comt, - 'changes': {'old': None, 'new': 'mysqs'}, - }) + ret.update( + { + "result": None, + "comment": comt, + "changes": {"old": None, "new": "mysqs"}, + } + ) self.assertDictEqual(boto_sqs.present(name), ret) - diff = textwrap.dedent('''\ + diff = textwrap.dedent( + """\ --- +++ @@ -1 +1 @@ -{} +DelaySeconds: 20 - ''').splitlines() + """ + ).splitlines() # Difflib adds a trailing space after the +++/--- lines, # programatically add them back here. Having them in the test # file itself is not feasible since a few popular plugins for # vim will remove trailing whitespace. for idx in (0, 1): - diff[idx] += ' ' - diff = '\n'.join(diff) + diff[idx] += " " + diff = "\n".join(diff) comt = [ - 'SQS queue mysqs present.', - 'Attribute(s) DelaySeconds set to be updated:\n{0}'.format( - diff, - ), + "SQS queue mysqs present.", + "Attribute(s) DelaySeconds set to be updated:\n{0}".format(diff,), ] - ret.update({ - 'comment': comt, - 'changes': {'attributes': {'diff': diff}}, - }) + ret.update({"comment": comt, "changes": {"attributes": {"diff": diff}}}) self.assertDictEqual(boto_sqs.present(name, attributes), ret) - comt = ['SQS queue mysqs present.'] + comt = ["SQS queue mysqs present."] ret = base_ret.copy() - ret.update({'result': True, 'comment': comt}) + ret.update({"result": True, "comment": comt}) self.assertDictEqual(boto_sqs.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the named sqs queue is deleted. - ''' - name = 'test.example.com.' - base_ret = {'name': name, 'changes': {}} + """ + name = "test.example.com." + base_ret = {"name": name, "changes": {}} - mock = MagicMock(side_effect=[{'result': False}, {'result': True}]) - with patch.dict(boto_sqs.__salt__, - {'boto_sqs.exists': mock}): - comt = ('SQS queue {0} does not exist in None.'.format(name)) + mock = MagicMock(side_effect=[{"result": False}, {"result": True}]) + with patch.dict(boto_sqs.__salt__, {"boto_sqs.exists": mock}): + comt = "SQS queue {0} does not exist in None.".format(name) ret = base_ret.copy() - ret.update({'result': True, 'comment': comt}) + ret.update({"result": True, "comment": comt}) self.assertDictEqual(boto_sqs.absent(name), ret) - with patch.dict(boto_sqs.__opts__, {'test': True}): - comt = ('SQS queue {0} is set to be removed.'.format(name)) + with patch.dict(boto_sqs.__opts__, {"test": True}): + comt = "SQS queue {0} is set to be removed.".format(name) ret = base_ret.copy() - ret.update({ - 'result': None, - 'comment': comt, - 'changes': {'old': name, 'new': None}, - }) + ret.update( + { + "result": None, + "comment": comt, + "changes": {"old": name, "new": None}, + } + ) self.assertDictEqual(boto_sqs.absent(name), ret) diff --git a/tests/unit/states/test_boto_vpc.py b/tests/unit/states/test_boto_vpc.py index 972e0738c97..c5b7d132b39 100644 --- a/tests/unit/states/test_boto_vpc.py +++ b/tests/unit/states/test_boto_vpc.py @@ -2,33 +2,37 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + +import os.path import random import string -import os.path import sys -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import patch -from tests.support.runtests import RUNTIME_VARS - - # Import Salt libs import salt.config +import salt.states.boto_vpc as boto_vpc import salt.utils.botomod as botomod from salt.ext import six + +# Import 3rd-party libs +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin from salt.utils.versions import LooseVersion -import salt.states.boto_vpc as boto_vpc + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error,unused-import from tests.unit.modules.test_boto_vpc import BotoVpcTestCaseMixin -# Import 3rd-party libs -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin try: import boto - boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') + + boto.ENDPOINTS_PATH = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/endpoints.json" + ) import boto3 from boto.exception import BotoServerError @@ -44,40 +48,52 @@ except ImportError: HAS_MOTO = False def mock_ec2_deprecated(self): - ''' + """ if the mock_ec2_deprecated function is not available due to import failure this replaces the decorated function with stub_function. Allows boto_vpc unit tests to use the @mock_ec2_deprecated decorator without a "NameError: name 'mock_ec2_deprecated' is not defined" error. - ''' + """ def stub_function(self): pass return stub_function + + # pylint: enable=import-error,unused-import # the boto_vpc module relies on the connect_to_region() method # which was added in boto 2.8.0 # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 -required_boto_version = '2.8.0' -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} -cidr_block = '10.0.0.0/24' -subnet_id = 'subnet-123456' -dhcp_options_parameters = {'domain_name': 'example.com', 'domain_name_servers': ['1.2.3.4'], 'ntp_servers': ['5.6.7.8'], - 'netbios_name_servers': ['10.0.0.1'], 'netbios_node_type': 2} -network_acl_entry_parameters = ('fake', 100, -1, 'allow', cidr_block) +required_boto_version = "2.8.0" +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, +} +cidr_block = "10.0.0.0/24" +subnet_id = "subnet-123456" +dhcp_options_parameters = { + "domain_name": "example.com", + "domain_name_servers": ["1.2.3.4"], + "ntp_servers": ["5.6.7.8"], + "netbios_name_servers": ["10.0.0.1"], + "netbios_node_type": 2, +} +network_acl_entry_parameters = ("fake", 100, -1, "allow", cidr_block) dhcp_options_parameters.update(conn_parameters) def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto.__version__) < LooseVersion(required_boto_version): @@ -91,107 +107,143 @@ class BotoVpcStateTestCaseBase(TestCase, LoaderModuleMockMixin): ctx = {} utils = salt.loader.utils( self.opts, - whitelist=['boto', 'boto3', 'args', 'systemd', 'path', 'platform', 'reg'], - context=ctx) + whitelist=["boto", "boto3", "args", "systemd", "path", "platform", "reg"], + context=ctx, + ) serializers = salt.loader.serializers(self.opts) - self.funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_vpc', 'config']) - self.salt_states = salt.loader.states(opts=self.opts, functions=self.funcs, utils=utils, whitelist=['boto_vpc'], - serializers=serializers) + self.funcs = salt.loader.minion_mods( + self.opts, context=ctx, utils=utils, whitelist=["boto_vpc", "config"] + ) + self.salt_states = salt.loader.states( + opts=self.opts, + functions=self.funcs, + utils=utils, + whitelist=["boto_vpc"], + serializers=serializers, + ) return { boto_vpc: { - '__opts__': self.opts, - '__salt__': self.funcs, - '__utils__': utils, - '__states__': self.salt_states, - '__serializers__': serializers, + "__opts__": self.opts, + "__salt__": self.funcs, + "__utils__": utils, + "__states__": self.salt_states, + "__serializers__": serializers, }, - botomod: {} + botomod: {}, } @classmethod def setUpClass(cls): cls.opts = salt.config.DEFAULT_MINION_OPTS.copy() - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) @classmethod def tearDownClass(cls): del cls.opts def setUp(self): - self.addCleanup(delattr, self, 'funcs') - self.addCleanup(delattr, self, 'salt_states') + self.addCleanup(delattr, self, "funcs") + self.addCleanup(delattr, self, "salt_states") # connections keep getting cached from prior tests, can't find the # correct context object to clear it. So randomize the cache key, to prevent any # cache hits - conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50)) + conn_parameters["key"] = "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(50) + ) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {0}".format(required_boto_version), +) class BotoVpcTestCase(BotoVpcStateTestCaseBase, BotoVpcTestCaseMixin): - ''' + """ TestCase for salt.states.boto_vpc state.module - ''' + """ - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_present_when_vpc_does_not_exist(self): - ''' + """ Tests present on a VPC that does not exist. - ''' + """ with patch.dict(botomod.__salt__, self.funcs): - vpc_present_result = self.salt_states['boto_vpc.present']('test', cidr_block) + vpc_present_result = self.salt_states["boto_vpc.present"]( + "test", cidr_block + ) - self.assertTrue(vpc_present_result['result']) - self.assertEqual(vpc_present_result['changes']['new']['vpc']['state'], 'available') + self.assertTrue(vpc_present_result["result"]) + self.assertEqual( + vpc_present_result["changes"]["new"]["vpc"]["state"], "available" + ) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_present_when_vpc_exists(self): - vpc = self._create_vpc(name='test') - vpc_present_result = self.salt_states['boto_vpc.present']('test', cidr_block) - self.assertTrue(vpc_present_result['result']) - self.assertEqual(vpc_present_result['changes'], {}) + vpc = self._create_vpc(name="test") + vpc_present_result = self.salt_states["boto_vpc.present"]("test", cidr_block) + self.assertTrue(vpc_present_result["result"]) + self.assertEqual(vpc_present_result["changes"], {}) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") def test_present_with_failure(self): - with patch('moto.ec2.models.VPCBackend.create_vpc', side_effect=BotoServerError(400, 'Mocked error')): - vpc_present_result = self.salt_states['boto_vpc.present']('test', cidr_block) - self.assertFalse(vpc_present_result['result']) - self.assertTrue('Mocked error' in vpc_present_result['comment']) + with patch( + "moto.ec2.models.VPCBackend.create_vpc", + side_effect=BotoServerError(400, "Mocked error"), + ): + vpc_present_result = self.salt_states["boto_vpc.present"]( + "test", cidr_block + ) + self.assertFalse(vpc_present_result["result"]) + self.assertTrue("Mocked error" in vpc_present_result["comment"]) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_absent_when_vpc_does_not_exist(self): - ''' + """ Tests absent on a VPC that does not exist. - ''' + """ with patch.dict(botomod.__salt__, self.funcs): - vpc_absent_result = self.salt_states['boto_vpc.absent']('test') - self.assertTrue(vpc_absent_result['result']) - self.assertEqual(vpc_absent_result['changes'], {}) + vpc_absent_result = self.salt_states["boto_vpc.absent"]("test") + self.assertTrue(vpc_absent_result["result"]) + self.assertEqual(vpc_absent_result["changes"], {}) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_absent_when_vpc_exists(self): - vpc = self._create_vpc(name='test') + vpc = self._create_vpc(name="test") with patch.dict(botomod.__salt__, self.funcs): - vpc_absent_result = self.salt_states['boto_vpc.absent']('test') - self.assertTrue(vpc_absent_result['result']) - self.assertEqual(vpc_absent_result['changes']['new']['vpc'], None) + vpc_absent_result = self.salt_states["boto_vpc.absent"]("test") + self.assertTrue(vpc_absent_result["result"]) + self.assertEqual(vpc_absent_result["changes"]["new"]["vpc"], None) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") def test_absent_with_failure(self): - vpc = self._create_vpc(name='test') - with patch('moto.ec2.models.VPCBackend.delete_vpc', side_effect=BotoServerError(400, 'Mocked error')): - vpc_absent_result = self.salt_states['boto_vpc.absent']('test') - self.assertFalse(vpc_absent_result['result']) - self.assertTrue('Mocked error' in vpc_absent_result['comment']) + vpc = self._create_vpc(name="test") + with patch( + "moto.ec2.models.VPCBackend.delete_vpc", + side_effect=BotoServerError(400, "Mocked error"), + ): + vpc_absent_result = self.salt_states["boto_vpc.absent"]("test") + self.assertFalse(vpc_absent_result["result"]) + self.assertTrue("Mocked error" in vpc_absent_result["comment"]) class BotoVpcResourceTestCaseMixin(BotoVpcTestCaseMixin): @@ -201,166 +253,231 @@ class BotoVpcResourceTestCaseMixin(BotoVpcTestCaseMixin): extra_kwargs = {} def _create_resource(self, vpc_id=None, name=None): - _create = getattr(self, '_create_' + self.resource_type) + _create = getattr(self, "_create_" + self.resource_type) _create(vpc_id=vpc_id, name=name, **self.extra_kwargs) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_present_when_resource_does_not_exist(self): - ''' + """ Tests present on a resource that does not exist. - ''' - vpc = self._create_vpc(name='test') + """ + vpc = self._create_vpc(name="test") with patch.dict(botomod.__salt__, self.funcs): - resource_present_result = self.salt_states['boto_vpc.{0}_present'.format(self.resource_type)]( - name='test', vpc_name='test', **self.extra_kwargs) + resource_present_result = self.salt_states[ + "boto_vpc.{0}_present".format(self.resource_type) + ](name="test", vpc_name="test", **self.extra_kwargs) - self.assertTrue(resource_present_result['result']) + self.assertTrue(resource_present_result["result"]) - exists = self.funcs['boto_vpc.resource_exists'](self.resource_type, 'test').get('exists') + exists = self.funcs["boto_vpc.resource_exists"](self.resource_type, "test").get( + "exists" + ) self.assertTrue(exists) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_present_when_resource_exists(self): - vpc = self._create_vpc(name='test') - self._create_resource(vpc_id=vpc.id, name='test') + vpc = self._create_vpc(name="test") + self._create_resource(vpc_id=vpc.id, name="test") with patch.dict(botomod.__salt__, self.funcs): - resource_present_result = self.salt_states['boto_vpc.{0}_present'.format(self.resource_type)]( - name='test', vpc_name='test', **self.extra_kwargs) - self.assertTrue(resource_present_result['result']) - self.assertEqual(resource_present_result['changes'], {}) + resource_present_result = self.salt_states[ + "boto_vpc.{0}_present".format(self.resource_type) + ](name="test", vpc_name="test", **self.extra_kwargs) + self.assertTrue(resource_present_result["result"]) + self.assertEqual(resource_present_result["changes"], {}) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") def test_present_with_failure(self): - vpc = self._create_vpc(name='test') - with patch('moto.ec2.models.{0}'.format(self.backend_create), side_effect=BotoServerError(400, 'Mocked error')): - resource_present_result = self.salt_states['boto_vpc.{0}_present'.format(self.resource_type)]( - name='test', vpc_name='test', **self.extra_kwargs) + vpc = self._create_vpc(name="test") + with patch( + "moto.ec2.models.{0}".format(self.backend_create), + side_effect=BotoServerError(400, "Mocked error"), + ): + resource_present_result = self.salt_states[ + "boto_vpc.{0}_present".format(self.resource_type) + ](name="test", vpc_name="test", **self.extra_kwargs) - self.assertFalse(resource_present_result['result']) - self.assertTrue('Mocked error' in resource_present_result['comment']) + self.assertFalse(resource_present_result["result"]) + self.assertTrue("Mocked error" in resource_present_result["comment"]) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_absent_when_resource_does_not_exist(self): - ''' + """ Tests absent on a resource that does not exist. - ''' + """ with patch.dict(botomod.__salt__, self.funcs): - resource_absent_result = self.salt_states['boto_vpc.{0}_absent'.format(self.resource_type)]('test') - self.assertTrue(resource_absent_result['result']) - self.assertEqual(resource_absent_result['changes'], {}) + resource_absent_result = self.salt_states[ + "boto_vpc.{0}_absent".format(self.resource_type) + ]("test") + self.assertTrue(resource_absent_result["result"]) + self.assertEqual(resource_absent_result["changes"], {}) - @skipIf(sys.version_info > (3, 6), 'Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.') + @skipIf( + sys.version_info > (3, 6), + "Disabled for 3.7+ pending https://github.com/spulec/moto/issues/1706.", + ) @mock_ec2_deprecated def test_absent_when_resource_exists(self): - vpc = self._create_vpc(name='test') - self._create_resource(vpc_id=vpc.id, name='test') + vpc = self._create_vpc(name="test") + self._create_resource(vpc_id=vpc.id, name="test") with patch.dict(botomod.__salt__, self.funcs): - resource_absent_result = self.salt_states['boto_vpc.{0}_absent'.format(self.resource_type)]('test') - self.assertTrue(resource_absent_result['result']) - self.assertEqual(resource_absent_result['changes']['new'][self.resource_type], None) - exists = self.funcs['boto_vpc.resource_exists'](self.resource_type, 'test').get('exists') + resource_absent_result = self.salt_states[ + "boto_vpc.{0}_absent".format(self.resource_type) + ]("test") + self.assertTrue(resource_absent_result["result"]) + self.assertEqual( + resource_absent_result["changes"]["new"][self.resource_type], None + ) + exists = self.funcs["boto_vpc.resource_exists"](self.resource_type, "test").get( + "exists" + ) self.assertFalse(exists) @mock_ec2_deprecated - @skipIf(True, 'Disabled pending https://github.com/spulec/moto/issues/493') + @skipIf(True, "Disabled pending https://github.com/spulec/moto/issues/493") def test_absent_with_failure(self): - vpc = self._create_vpc(name='test') - self._create_resource(vpc_id=vpc.id, name='test') + vpc = self._create_vpc(name="test") + self._create_resource(vpc_id=vpc.id, name="test") - with patch('moto.ec2.models.{0}'.format(self.backend_delete), side_effect=BotoServerError(400, 'Mocked error')): - resource_absent_result = self.salt_states['boto_vpc.{0}_absent'.format(self.resource_type)]('test') - self.assertFalse(resource_absent_result['result']) - self.assertTrue('Mocked error' in resource_absent_result['comment']) + with patch( + "moto.ec2.models.{0}".format(self.backend_delete), + side_effect=BotoServerError(400, "Mocked error"), + ): + resource_absent_result = self.salt_states[ + "boto_vpc.{0}_absent".format(self.resource_type) + ]("test") + self.assertFalse(resource_absent_result["result"]) + self.assertTrue("Mocked error" in resource_absent_result["comment"]) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {0}".format(required_boto_version), +) class BotoVpcSubnetsTestCase(BotoVpcStateTestCaseBase, BotoVpcResourceTestCaseMixin): - resource_type = 'subnet' - backend_create = 'SubnetBackend.create_subnet' - backend_delete = 'SubnetBackend.delete_subnet' - extra_kwargs = {'cidr_block': cidr_block} + resource_type = "subnet" + backend_create = "SubnetBackend.create_subnet" + backend_delete = "SubnetBackend.delete_subnet" + extra_kwargs = {"cidr_block": cidr_block} -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) -class BotoVpcInternetGatewayTestCase(BotoVpcStateTestCaseBase, BotoVpcResourceTestCaseMixin): - resource_type = 'internet_gateway' - backend_create = 'InternetGatewayBackend.create_internet_gateway' - backend_delete = 'InternetGatewayBackend.delete_internet_gateway' +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {0}".format(required_boto_version), +) +class BotoVpcInternetGatewayTestCase( + BotoVpcStateTestCaseBase, BotoVpcResourceTestCaseMixin +): + resource_type = "internet_gateway" + backend_create = "InternetGatewayBackend.create_internet_gateway" + backend_delete = "InternetGatewayBackend.delete_internet_gateway" -@skipIf(six.PY3, 'Disabled for Python 3 due to upstream bugs: ' - 'https://github.com/spulec/moto/issues/548 and ' - 'https://github.com/gabrielfalcao/HTTPretty/issues/325') -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) +@skipIf( + six.PY3, + "Disabled for Python 3 due to upstream bugs: " + "https://github.com/spulec/moto/issues/548 and " + "https://github.com/gabrielfalcao/HTTPretty/issues/325", +) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {0}".format(required_boto_version), +) class BotoVpcRouteTableTestCase(BotoVpcStateTestCaseBase, BotoVpcResourceTestCaseMixin): - resource_type = 'route_table' - backend_create = 'RouteTableBackend.create_route_table' - backend_delete = 'RouteTableBackend.delete_route_table' + resource_type = "route_table" + backend_create = "RouteTableBackend.create_route_table" + backend_delete = "RouteTableBackend.delete_route_table" @mock_ec2_deprecated def test_present_with_subnets(self): - vpc = self._create_vpc(name='test') - subnet1 = self._create_subnet(vpc_id=vpc.id, cidr_block='10.0.0.0/25', name='test1') - subnet2 = self._create_subnet(vpc_id=vpc.id, cidr_block='10.0.0.128/25', name='test2') + vpc = self._create_vpc(name="test") + subnet1 = self._create_subnet( + vpc_id=vpc.id, cidr_block="10.0.0.0/25", name="test1" + ) + subnet2 = self._create_subnet( + vpc_id=vpc.id, cidr_block="10.0.0.128/25", name="test2" + ) - route_table_present_result = self.salt_states['boto_vpc.route_table_present']( - name='test', vpc_name='test', subnet_names=['test1'], subnet_ids=[subnet2.id]) + route_table_present_result = self.salt_states["boto_vpc.route_table_present"]( + name="test", + vpc_name="test", + subnet_names=["test1"], + subnet_ids=[subnet2.id], + ) - associations = route_table_present_result['changes']['new']['subnets_associations'] + associations = route_table_present_result["changes"]["new"][ + "subnets_associations" + ] - assoc_subnets = [x['subnet_id'] for x in associations] + assoc_subnets = [x["subnet_id"] for x in associations] self.assertEqual(set(assoc_subnets), set([subnet1.id, subnet2.id])) - route_table_present_result = self.salt_states['boto_vpc.route_table_present']( - name='test', vpc_name='test', subnet_ids=[subnet2.id]) + route_table_present_result = self.salt_states["boto_vpc.route_table_present"]( + name="test", vpc_name="test", subnet_ids=[subnet2.id] + ) - changes = route_table_present_result['changes'] + changes = route_table_present_result["changes"] - old_subnets = [x['subnet_id'] for x in changes['old']['subnets_associations']] + old_subnets = [x["subnet_id"] for x in changes["old"]["subnets_associations"]] self.assertEqual(set(assoc_subnets), set(old_subnets)) - new_subnets = changes['new']['subnets_associations'] - self.assertEqual(new_subnets[0]['subnet_id'], subnet2.id) + new_subnets = changes["new"]["subnets_associations"] + self.assertEqual(new_subnets[0]["subnet_id"], subnet2.id) @mock_ec2_deprecated def test_present_with_routes(self): - vpc = self._create_vpc(name='test') - igw = self._create_internet_gateway(name='test', vpc_id=vpc.id) + vpc = self._create_vpc(name="test") + igw = self._create_internet_gateway(name="test", vpc_id=vpc.id) with patch.dict(botomod.__salt__, self.funcs): - route_table_present_result = self.salt_states['boto_vpc.route_table_present']( - name='test', vpc_name='test', routes=[{'destination_cidr_block': '0.0.0.0/0', - 'gateway_id': igw.id}, - {'destination_cidr_block': '10.0.0.0/24', - 'gateway_id': 'local'}]) - routes = [x['gateway_id'] for x in route_table_present_result['changes']['new']['routes']] + route_table_present_result = self.salt_states[ + "boto_vpc.route_table_present" + ]( + name="test", + vpc_name="test", + routes=[ + {"destination_cidr_block": "0.0.0.0/0", "gateway_id": igw.id}, + {"destination_cidr_block": "10.0.0.0/24", "gateway_id": "local"}, + ], + ) + routes = [ + x["gateway_id"] + for x in route_table_present_result["changes"]["new"]["routes"] + ] - self.assertEqual(set(routes), set(['local', igw.id])) + self.assertEqual(set(routes), set(["local", igw.id])) - route_table_present_result = self.salt_states['boto_vpc.route_table_present']( - name='test', vpc_name='test', routes=[{'destination_cidr_block': '10.0.0.0/24', - 'gateway_id': 'local'}]) + route_table_present_result = self.salt_states["boto_vpc.route_table_present"]( + name="test", + vpc_name="test", + routes=[{"destination_cidr_block": "10.0.0.0/24", "gateway_id": "local"}], + ) - changes = route_table_present_result['changes'] + changes = route_table_present_result["changes"] - old_routes = [x['gateway_id'] for x in changes['old']['routes']] + old_routes = [x["gateway_id"] for x in changes["old"]["routes"]] self.assertEqual(set(routes), set(old_routes)) - self.assertEqual(changes['new']['routes'][0]['gateway_id'], 'local') + self.assertEqual(changes["new"]["routes"][0]["gateway_id"], "local") diff --git a/tests/unit/states/test_bower.py b/tests/unit/states/test_bower.py index c0ee24a8b10..53214a98171 100644 --- a/tests/unit/states/test_bower.py +++ b/tests/unit/states/test_bower.py @@ -1,233 +1,256 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Alexander Pyatkin <asp@thexyz.net> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.states.bower as bower from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class BowerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.bower - ''' + """ + def setup_loader_modules(self): - return {bower: {'__opts__': {'test': False}}} + return {bower: {"__opts__": {"test": False}}} def test_removed_not_installed(self): - ''' + """ Test if it returns True when specified package is not installed - ''' + """ - mock = MagicMock(return_value={'underscore': {}}) + mock = MagicMock(return_value={"underscore": {}}) - with patch.dict(bower.__salt__, {'bower.list': mock}): - ret = bower.removed('jquery', '/path/to/project') - expected = {'name': 'jquery', - 'result': True, - 'comment': 'Package \'jquery\' is not installed', - 'changes': {}} + with patch.dict(bower.__salt__, {"bower.list": mock}): + ret = bower.removed("jquery", "/path/to/project") + expected = { + "name": "jquery", + "result": True, + "comment": "Package 'jquery' is not installed", + "changes": {}, + } self.assertEqual(ret, expected) def test_removed_with_error(self): - ''' + """ Test if returns False when list packages fails - ''' + """ mock = MagicMock(side_effect=CommandExecutionError) - with patch.dict(bower.__salt__, {'bower.list': mock}): - ret = bower.removed('underscore', '/path/to/project') - expected = {'name': 'underscore', - 'result': False, - 'comment': 'Error removing \'underscore\': ', - 'changes': {}} + with patch.dict(bower.__salt__, {"bower.list": mock}): + ret = bower.removed("underscore", "/path/to/project") + expected = { + "name": "underscore", + "result": False, + "comment": "Error removing 'underscore': ", + "changes": {}, + } self.assertEqual(ret, expected) def test_removed_existing(self): - ''' + """ Test if it returns True when specified package is installed and uninstall succeeds - ''' + """ - mock_list = MagicMock(return_value={'underscore': {}}) + mock_list = MagicMock(return_value={"underscore": {}}) mock_uninstall = MagicMock(return_value=True) - with patch.dict(bower.__salt__, {'bower.list': mock_list, - 'bower.uninstall': mock_uninstall}): - ret = bower.removed('underscore', '/path/to/project') - expected = {'name': 'underscore', - 'result': True, - 'comment': - 'Package \'underscore\' was successfully removed', - 'changes': {'underscore': 'Removed'}} + with patch.dict( + bower.__salt__, {"bower.list": mock_list, "bower.uninstall": mock_uninstall} + ): + ret = bower.removed("underscore", "/path/to/project") + expected = { + "name": "underscore", + "result": True, + "comment": "Package 'underscore' was successfully removed", + "changes": {"underscore": "Removed"}, + } self.assertEqual(ret, expected) def test_removed_existing_with_error(self): - ''' + """ Test if it returns False when specified package is installed and uninstall fails - ''' + """ - mock_list = MagicMock(return_value={'underscore': {}}) + mock_list = MagicMock(return_value={"underscore": {}}) mock_uninstall = MagicMock(side_effect=CommandExecutionError) - with patch.dict(bower.__salt__, {'bower.list': mock_list, - 'bower.uninstall': mock_uninstall}): - ret = bower.removed('underscore', '/path/to/project') - expected = {'name': 'underscore', - 'result': False, - 'comment': - 'Error removing \'underscore\': ', - 'changes': {}} + with patch.dict( + bower.__salt__, {"bower.list": mock_list, "bower.uninstall": mock_uninstall} + ): + ret = bower.removed("underscore", "/path/to/project") + expected = { + "name": "underscore", + "result": False, + "comment": "Error removing 'underscore': ", + "changes": {}, + } self.assertEqual(ret, expected) def test_bootstrap_with_error(self): - ''' + """ Test if it return False when install packages fails - ''' + """ mock = MagicMock(side_effect=CommandExecutionError) - with patch.dict(bower.__salt__, {'bower.install': mock}): - ret = bower.bootstrap('/path/to/project') - expected = {'name': '/path/to/project', - 'result': False, - 'comment': - 'Error bootstrapping \'/path/to/project\': ', - 'changes': {}} + with patch.dict(bower.__salt__, {"bower.install": mock}): + ret = bower.bootstrap("/path/to/project") + expected = { + "name": "/path/to/project", + "result": False, + "comment": "Error bootstrapping '/path/to/project': ", + "changes": {}, + } self.assertEqual(ret, expected) def test_bootstrap_not_needed(self): - ''' + """ Test if it returns True when there is nothing to install - ''' + """ mock = MagicMock(return_value=False) - with patch.dict(bower.__salt__, {'bower.install': mock}): - ret = bower.bootstrap('/path/to/project') - expected = {'name': '/path/to/project', - 'result': True, - 'comment': - 'Directory is already bootstrapped', - 'changes': {}} + with patch.dict(bower.__salt__, {"bower.install": mock}): + ret = bower.bootstrap("/path/to/project") + expected = { + "name": "/path/to/project", + "result": True, + "comment": "Directory is already bootstrapped", + "changes": {}, + } self.assertEqual(ret, expected) def test_bootstrap_success(self): - ''' + """ Test if it returns True when install packages succeeds - ''' + """ mock = MagicMock(return_value=True) - with patch.dict(bower.__salt__, {'bower.install': mock}): - ret = bower.bootstrap('/path/to/project') - expected = {'name': '/path/to/project', - 'result': True, - 'comment': - 'Directory was successfully bootstrapped', - 'changes': {'/path/to/project': 'Bootstrapped'}} + with patch.dict(bower.__salt__, {"bower.install": mock}): + ret = bower.bootstrap("/path/to/project") + expected = { + "name": "/path/to/project", + "result": True, + "comment": "Directory was successfully bootstrapped", + "changes": {"/path/to/project": "Bootstrapped"}, + } self.assertEqual(ret, expected) def test_installed_with_error(self): - ''' + """ Test if it returns False when list packages fails - ''' + """ mock = MagicMock(side_effect=CommandExecutionError) - with patch.dict(bower.__salt__, {'bower.list': mock}): - ret = bower.installed('underscore', '/path/to/project') - expected = {'name': 'underscore', - 'result': False, - 'comment': 'Error looking up \'underscore\': ', - 'changes': {}} + with patch.dict(bower.__salt__, {"bower.list": mock}): + ret = bower.installed("underscore", "/path/to/project") + expected = { + "name": "underscore", + "result": False, + "comment": "Error looking up 'underscore': ", + "changes": {}, + } self.assertEqual(ret, expected) def test_installed_not_needed(self): - ''' + """ Test if it returns True when there is nothing to install - ''' + """ - mock = MagicMock(return_value={ - 'underscore': { - 'pkgMeta': {'version': '1.7.0'}}, - 'jquery': { - 'pkgMeta': {'version': '2.0.0'}}}) + mock = MagicMock( + return_value={ + "underscore": {"pkgMeta": {"version": "1.7.0"}}, + "jquery": {"pkgMeta": {"version": "2.0.0"}}, + } + ) - with patch.dict(bower.__salt__, {'bower.list': mock}): - ret = bower.installed('test', '/path/to/project', - ['underscore', 'jquery#2.0.0']) - expected = {'name': 'test', - 'result': True, - 'comment': - ('Package(s) \'underscore, jquery#2.0.0\'' - ' satisfied by underscore#1.7.0, jquery#2.0.0'), - 'changes': {}} + with patch.dict(bower.__salt__, {"bower.list": mock}): + ret = bower.installed( + "test", "/path/to/project", ["underscore", "jquery#2.0.0"] + ) + expected = { + "name": "test", + "result": True, + "comment": ( + "Package(s) 'underscore, jquery#2.0.0'" + " satisfied by underscore#1.7.0, jquery#2.0.0" + ), + "changes": {}, + } self.assertEqual(ret, expected) def test_installed_new_with_exc(self): - ''' + """ Test if it returns False when install packages fails (exception) - ''' + """ mock_list = MagicMock(return_value={}) mock_install = MagicMock(side_effect=CommandExecutionError) - with patch.dict(bower.__salt__, {'bower.list': mock_list, - 'bower.install': mock_install}): - ret = bower.installed('underscore', '/path/to/project') - expected = {'name': 'underscore', - 'result': False, - 'comment': 'Error installing \'underscore\': ', - 'changes': {}} + with patch.dict( + bower.__salt__, {"bower.list": mock_list, "bower.install": mock_install} + ): + ret = bower.installed("underscore", "/path/to/project") + expected = { + "name": "underscore", + "result": False, + "comment": "Error installing 'underscore': ", + "changes": {}, + } self.assertEqual(ret, expected) def test_installed_new_with_error(self): - ''' + """ Test if returns False when install packages fails (bower error) - ''' + """ mock_list = MagicMock(return_value={}) mock_install = MagicMock(return_value=False) - with patch.dict(bower.__salt__, {'bower.list': mock_list, - 'bower.install': mock_install}): - ret = bower.installed('underscore', '/path/to/project') - expected = {'name': 'underscore', - 'result': False, - 'comment': - 'Could not install package(s) \'underscore\'', - 'changes': {}} + with patch.dict( + bower.__salt__, {"bower.list": mock_list, "bower.install": mock_install} + ): + ret = bower.installed("underscore", "/path/to/project") + expected = { + "name": "underscore", + "result": False, + "comment": "Could not install package(s) 'underscore'", + "changes": {}, + } self.assertEqual(ret, expected) def test_installed_success(self): - ''' + """ Test if it returns True when install succeeds - ''' + """ mock_list = MagicMock(return_value={}) mock_install = MagicMock(return_value=True) - with patch.dict(bower.__salt__, {'bower.list': mock_list, - 'bower.install': mock_install}): - ret = bower.installed('underscore', '/path/to/project') - expected = {'name': 'underscore', - 'result': True, - 'comment': - 'Package(s) \'underscore\' successfully installed', - 'changes': {'new': ['underscore'], 'old': []}} + with patch.dict( + bower.__salt__, {"bower.list": mock_list, "bower.install": mock_install} + ): + ret = bower.installed("underscore", "/path/to/project") + expected = { + "name": "underscore", + "result": True, + "comment": "Package(s) 'underscore' successfully installed", + "changes": {"new": ["underscore"], "old": []}, + } self.assertEqual(ret, expected) diff --git a/tests/unit/states/test_chef.py b/tests/unit/states/test_chef.py index 53bdf8db84d..55be384506d 100644 --- a/tests/unit/states/test_chef.py +++ b/tests/unit/states/test_chef.py @@ -1,66 +1,57 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.chef as chef +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ChefTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.chef - ''' + """ + def setup_loader_modules(self): return {chef: {}} # 'client' function tests: 1 def test_client(self): - ''' + """ Test to run chef-client - ''' - name = 'my-chef-run' + """ + name = "my-chef-run" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - mock = MagicMock(return_value={'retcode': 1, 'stdout': '', - 'stderr': 'error'}) - with patch.dict(chef.__salt__, {'chef.client': mock}): - with patch.dict(chef.__opts__, {'test': True}): - comt = ('\nerror') - ret.update({'comment': comt}) + mock = MagicMock(return_value={"retcode": 1, "stdout": "", "stderr": "error"}) + with patch.dict(chef.__salt__, {"chef.client": mock}): + with patch.dict(chef.__opts__, {"test": True}): + comt = "\nerror" + ret.update({"comment": comt}) self.assertDictEqual(chef.client(name), ret) # 'solo' function tests: 1 def test_solo(self): - ''' + """ Test to run chef-solo - ''' - name = 'my-chef-run' + """ + name = "my-chef-run" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - mock = MagicMock(return_value={'retcode': 1, 'stdout': '', - 'stderr': 'error'}) - with patch.dict(chef.__salt__, {'chef.solo': mock}): - with patch.dict(chef.__opts__, {'test': True}): - comt = ('\nerror') - ret.update({'comment': comt}) + mock = MagicMock(return_value={"retcode": 1, "stdout": "", "stderr": "error"}) + with patch.dict(chef.__salt__, {"chef.solo": mock}): + with patch.dict(chef.__opts__, {"test": True}): + comt = "\nerror" + ret.update({"comment": comt}) self.assertDictEqual(chef.solo(name), ret) diff --git a/tests/unit/states/test_cloud.py b/tests/unit/states/test_cloud.py index 1f40d7bcba1..be2ed8081ae 100644 --- a/tests/unit/states/test_cloud.py +++ b/tests/unit/states/test_cloud.py @@ -1,389 +1,409 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.cloud as cloud import salt.utils.cloud +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class CloudTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.cloud - ''' + """ + def setup_loader_modules(self): return {cloud: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to spin up a single instance on a cloud provider, using salt-cloud. - ''' - name = 'mycloud' - cloud_provider = 'my_cloud_provider' + """ + name = "mycloud" + cloud_provider = "my_cloud_provider" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False]) mock_bool = MagicMock(side_effect=[True, False, False]) - mock_dict = MagicMock(return_value={'cloud': 'saltcloud'}) - with patch.dict(cloud.__salt__, {'cmd.retcode': mock, - 'cloud.has_instance': mock_bool, - 'cloud.create': mock_dict}): - comt = ('onlyif condition is false') - ret.update({'comment': comt}) - self.assertDictEqual(cloud.present(name, cloud_provider, - onlyif=False), ret) + mock_dict = MagicMock(return_value={"cloud": "saltcloud"}) + with patch.dict( + cloud.__salt__, + { + "cmd.retcode": mock, + "cloud.has_instance": mock_bool, + "cloud.create": mock_dict, + }, + ): + comt = "onlyif condition is false" + ret.update({"comment": comt}) + self.assertDictEqual(cloud.present(name, cloud_provider, onlyif=False), ret) - self.assertDictEqual(cloud.present(name, cloud_provider, onlyif=''), - ret) + self.assertDictEqual(cloud.present(name, cloud_provider, onlyif=""), ret) - comt = ('unless condition is true') - ret.update({'comment': comt}) - self.assertDictEqual(cloud.present(name, cloud_provider, - unless=True), ret) + comt = "unless condition is true" + ret.update({"comment": comt}) + self.assertDictEqual(cloud.present(name, cloud_provider, unless=True), ret) - self.assertDictEqual(cloud.present(name, cloud_provider, unless=''), - ret) + self.assertDictEqual(cloud.present(name, cloud_provider, unless=""), ret) - comt = ('Already present instance {0}'.format(name)) - ret.update({'comment': comt}) + comt = "Already present instance {0}".format(name) + ret.update({"comment": comt}) self.assertDictEqual(cloud.present(name, cloud_provider), ret) - with patch.dict(cloud.__opts__, {'test': True}): - comt = ('Instance {0} needs to be created'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(cloud.__opts__, {"test": True}): + comt = "Instance {0} needs to be created".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(cloud.present(name, cloud_provider), ret) - with patch.dict(cloud.__opts__, {'test': False}): - comt = ('Created instance mycloud using provider ' - 'my_cloud_provider and the following options: {}') - ret.update({'comment': comt, 'result': True, - 'changes': {'cloud': 'saltcloud'}}) + with patch.dict(cloud.__opts__, {"test": False}): + comt = ( + "Created instance mycloud using provider " + "my_cloud_provider and the following options: {}" + ) + ret.update( + {"comment": comt, "result": True, "changes": {"cloud": "saltcloud"}} + ) self.assertDictEqual(cloud.present(name, cloud_provider), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that no instances with the specified names exist. - ''' - name = 'mycloud' + """ + name = "mycloud" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False]) mock_bool = MagicMock(side_effect=[False, True, True]) - mock_dict = MagicMock(return_value={'cloud': 'saltcloud'}) - with patch.dict(cloud.__salt__, {'cmd.retcode': mock, - 'cloud.has_instance': mock_bool, - 'cloud.destroy': mock_dict}): - comt = ('onlyif condition is false') - ret.update({'comment': comt}) + mock_dict = MagicMock(return_value={"cloud": "saltcloud"}) + with patch.dict( + cloud.__salt__, + { + "cmd.retcode": mock, + "cloud.has_instance": mock_bool, + "cloud.destroy": mock_dict, + }, + ): + comt = "onlyif condition is false" + ret.update({"comment": comt}) self.assertDictEqual(cloud.absent(name, onlyif=False), ret) - self.assertDictEqual(cloud.absent(name, onlyif=''), ret) + self.assertDictEqual(cloud.absent(name, onlyif=""), ret) - comt = ('unless condition is true') - ret.update({'comment': comt}) + comt = "unless condition is true" + ret.update({"comment": comt}) self.assertDictEqual(cloud.absent(name, unless=True), ret) - self.assertDictEqual(cloud.absent(name, unless=''), ret) + self.assertDictEqual(cloud.absent(name, unless=""), ret) - comt = ('Already absent instance {0}'.format(name)) - ret.update({'comment': comt}) + comt = "Already absent instance {0}".format(name) + ret.update({"comment": comt}) self.assertDictEqual(cloud.absent(name), ret) - with patch.dict(cloud.__opts__, {'test': True}): - comt = ('Instance {0} needs to be destroyed'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(cloud.__opts__, {"test": True}): + comt = "Instance {0} needs to be destroyed".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(cloud.absent(name), ret) - with patch.dict(cloud.__opts__, {'test': False}): - comt = (('Destroyed instance {0}').format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'cloud': 'saltcloud'}}) + with patch.dict(cloud.__opts__, {"test": False}): + comt = ("Destroyed instance {0}").format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"cloud": "saltcloud"}} + ) self.assertDictEqual(cloud.absent(name), ret) # 'profile' function tests: 1 def test_profile(self): - ''' + """ Test to create a single instance on a cloud provider, using a salt-cloud profile. - ''' - name = 'mycloud' - profile = 'myprofile' + """ + name = "mycloud" + profile = "myprofile" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} mock = MagicMock(side_effect=[True, False]) - mock_dict = MagicMock(side_effect=[{'cloud': 'saltcloud'}, - {'Not Actioned': True}, - {'Not Actioned': True}, - { - 'Not Found': True, - 'Not Actioned/Not Running': True - }]) + mock_dict = MagicMock( + side_effect=[ + {"cloud": "saltcloud"}, + {"Not Actioned": True}, + {"Not Actioned": True}, + {"Not Found": True, "Not Actioned/Not Running": True}, + ] + ) mock_d = MagicMock(return_value={}) - with patch.dict(cloud.__salt__, {'cmd.retcode': mock, - 'cloud.profile': mock_d, - 'cloud.action': mock_dict}): - comt = ('onlyif condition is false') - ret.update({'comment': comt}) - self.assertDictEqual(cloud.profile(name, profile, onlyif=False), - ret) + with patch.dict( + cloud.__salt__, + {"cmd.retcode": mock, "cloud.profile": mock_d, "cloud.action": mock_dict}, + ): + comt = "onlyif condition is false" + ret.update({"comment": comt}) + self.assertDictEqual(cloud.profile(name, profile, onlyif=False), ret) - self.assertDictEqual(cloud.profile(name, profile, onlyif=''), ret) + self.assertDictEqual(cloud.profile(name, profile, onlyif=""), ret) - comt = ('unless condition is true') - ret.update({'comment': comt}) + comt = "unless condition is true" + ret.update({"comment": comt}) self.assertDictEqual(cloud.profile(name, profile, unless=True), ret) - self.assertDictEqual(cloud.profile(name, profile, unless=''), ret) + self.assertDictEqual(cloud.profile(name, profile, unless=""), ret) - comt = ('Already present instance {0}'.format(name)) - ret.update({'comment': comt}) + comt = "Already present instance {0}".format(name) + ret.update({"comment": comt}) self.assertDictEqual(cloud.profile(name, profile), ret) - with patch.dict(cloud.__opts__, {'test': True}): - comt = ('Instance {0} needs to be created'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(cloud.__opts__, {"test": True}): + comt = "Instance {0} needs to be created".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(cloud.profile(name, profile), ret) - with patch.dict(cloud.__opts__, {'test': False}): - comt = (('Failed to create instance {0}' - 'using profile {1}').format(name, profile)) - ret.update({'comment': comt, 'result': False}) + with patch.dict(cloud.__opts__, {"test": False}): + comt = ("Failed to create instance {0}" "using profile {1}").format( + name, profile + ) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(cloud.profile(name, profile), ret) - with patch.dict(cloud.__opts__, {'test': False}): - comt = (('Failed to create instance {0}' - 'using profile {1}').format(name, profile)) - ret.update({'comment': comt, 'result': False}) + with patch.dict(cloud.__opts__, {"test": False}): + comt = ("Failed to create instance {0}" "using profile {1}").format( + name, profile + ) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(cloud.profile(name, profile), ret) # 'volume_present' function tests: 1 def test_volume_present(self): - ''' + """ Test to check that a block volume exists. - ''' - name = 'mycloud' + """ + name = "mycloud" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} mock = MagicMock(return_value=name) mock_lst = MagicMock(side_effect=[[name], [], []]) - with patch.dict(cloud.__salt__, {'cloud.volume_list': mock_lst, - 'cloud.volume_create': mock}): - with patch.object(salt.utils.cloud, 'check_name', - MagicMock(return_value=True)): - comt = ('Invalid characters in name.') - ret.update({'comment': comt}) + with patch.dict( + cloud.__salt__, {"cloud.volume_list": mock_lst, "cloud.volume_create": mock} + ): + with patch.object( + salt.utils.cloud, "check_name", MagicMock(return_value=True) + ): + comt = "Invalid characters in name." + ret.update({"comment": comt}) self.assertDictEqual(cloud.volume_present(name), ret) - comt = ('Volume exists: {0}'.format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "Volume exists: {0}".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(cloud.volume_present(name), ret) - with patch.dict(cloud.__opts__, {'test': True}): - comt = ('Volume {0} will be created.'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(cloud.__opts__, {"test": True}): + comt = "Volume {0} will be created.".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(cloud.volume_present(name), ret) - with patch.dict(cloud.__opts__, {'test': False}): - comt = ('Volume {0} was created'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'old': None, 'new': name}}) + with patch.dict(cloud.__opts__, {"test": False}): + comt = "Volume {0} was created".format(name) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"old": None, "new": name}, + } + ) self.assertDictEqual(cloud.volume_present(name), ret) # 'volume_absent' function tests: 1 def test_volume_absent(self): - ''' + """ Test to check that a block volume exists. - ''' - name = 'mycloud' + """ + name = "mycloud" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} mock = MagicMock(return_value=False) mock_lst = MagicMock(side_effect=[[], [name], [name]]) - with patch.dict(cloud.__salt__, {'cloud.volume_list': mock_lst, - 'cloud.volume_delete': mock}): - with patch.object(salt.utils.cloud, 'check_name', - MagicMock(return_value=True)): - comt = ('Invalid characters in name.') - ret.update({'comment': comt}) + with patch.dict( + cloud.__salt__, {"cloud.volume_list": mock_lst, "cloud.volume_delete": mock} + ): + with patch.object( + salt.utils.cloud, "check_name", MagicMock(return_value=True) + ): + comt = "Invalid characters in name." + ret.update({"comment": comt}) self.assertDictEqual(cloud.volume_absent(name), ret) - comt = ('Volume is absent.') - ret.update({'comment': comt, 'result': True}) + comt = "Volume is absent." + ret.update({"comment": comt, "result": True}) self.assertDictEqual(cloud.volume_absent(name), ret) - with patch.dict(cloud.__opts__, {'test': True}): - comt = ('Volume {0} will be deleted.'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(cloud.__opts__, {"test": True}): + comt = "Volume {0} will be deleted.".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(cloud.volume_absent(name), ret) - with patch.dict(cloud.__opts__, {'test': False}): - comt = ('Volume {0} failed to delete.'.format(name)) - ret.update({'comment': comt, 'result': False}) + with patch.dict(cloud.__opts__, {"test": False}): + comt = "Volume {0} failed to delete.".format(name) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(cloud.volume_absent(name), ret) # 'volume_attached' function tests: 1 def test_volume_attached(self): - ''' + """ Test to check if a block volume is attached. - ''' - name = 'mycloud' - server_name = 'mycloud_server' - disk_name = 'trogdor' + """ + name = "mycloud" + server_name = "mycloud_server" + disk_name = "trogdor" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} mock = MagicMock(return_value=False) - mock_dict = MagicMock(side_effect=[{name: {'name': disk_name, 'attachments': True}}, {}, - {name: {'name': disk_name, 'attachments': False}}, - {name: {'name': disk_name, 'attachments': False}}, - {name: {'name': disk_name, 'attachments': False}}]) - with patch.dict(cloud.__salt__, {'cloud.volume_list': mock_dict, - 'cloud.action': mock}): - with patch.object(salt.utils.cloud, 'check_name', - MagicMock(side_effect=[True, False, True])): - comt = ('Invalid characters in name.') - ret.update({'comment': comt}) - self.assertDictEqual(cloud.volume_attached(name, server_name), - ret) + mock_dict = MagicMock( + side_effect=[ + {name: {"name": disk_name, "attachments": True}}, + {}, + {name: {"name": disk_name, "attachments": False}}, + {name: {"name": disk_name, "attachments": False}}, + {name: {"name": disk_name, "attachments": False}}, + ] + ) + with patch.dict( + cloud.__salt__, {"cloud.volume_list": mock_dict, "cloud.action": mock} + ): + with patch.object( + salt.utils.cloud, + "check_name", + MagicMock(side_effect=[True, False, True]), + ): + comt = "Invalid characters in name." + ret.update({"comment": comt}) + self.assertDictEqual(cloud.volume_attached(name, server_name), ret) - ret.update({'name': server_name}) - self.assertDictEqual(cloud.volume_attached(name, server_name), - ret) + ret.update({"name": server_name}) + self.assertDictEqual(cloud.volume_attached(name, server_name), ret) - comt = ('Volume {0} is already attached: True'.format(disk_name)) - ret.update({'comment': comt, 'result': True}) + comt = "Volume {0} is already attached: True".format(disk_name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(cloud.volume_attached(name, server_name), ret) - comt = ('Volume {0} does not exist'.format(name)) - ret.update({'comment': comt, 'result': False}) + comt = "Volume {0} does not exist".format(name) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(cloud.volume_attached(name, server_name), ret) - comt = ('Server {0} does not exist'.format(server_name)) - ret.update({'comment': comt, 'result': False}) + comt = "Server {0} does not exist".format(server_name) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(cloud.volume_attached(name, server_name), ret) mock = MagicMock(return_value=True) - with patch.dict(cloud.__salt__, {'cloud.action': mock, - 'cloud.volume_attach': mock}): - with patch.dict(cloud.__opts__, {'test': True}): - comt = ('Volume {0} will be will be attached.'.format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(cloud.volume_attached(name, - server_name), - ret) + with patch.dict( + cloud.__salt__, {"cloud.action": mock, "cloud.volume_attach": mock} + ): + with patch.dict(cloud.__opts__, {"test": True}): + comt = "Volume {0} will be will be attached.".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual(cloud.volume_attached(name, server_name), ret) - with patch.dict(cloud.__opts__, {'test': False}): - comt = ('Volume {0} was created'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'new': True, - 'old': {'name': disk_name, - 'attachments': False}}}) - self.assertDictEqual(cloud.volume_attached(name, - server_name), - ret) + with patch.dict(cloud.__opts__, {"test": False}): + comt = "Volume {0} was created".format(name) + ret.update( + { + "comment": comt, + "result": True, + "changes": { + "new": True, + "old": {"name": disk_name, "attachments": False}, + }, + } + ) + self.assertDictEqual(cloud.volume_attached(name, server_name), ret) # 'volume_detached' function tests: 1 def test_volume_detached(self): - ''' + """ Test to check if a block volume is detached. - ''' - name = 'mycloud' - server_name = 'mycloud_server' - disk_name = 'trogdor' + """ + name = "mycloud" + server_name = "mycloud_server" + disk_name = "trogdor" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} mock = MagicMock(return_value=False) - mock_dict = MagicMock(side_effect=[{name: {'name': disk_name, 'attachments': False}}, {}, - {name: {'name': disk_name, 'attachments': True}}, - {name: {'name': disk_name, 'attachments': True}}, - {name: {'name': disk_name, 'attachments': True}}]) - with patch.dict(cloud.__salt__, {'cloud.volume_list': mock_dict, - 'cloud.action': mock}): - with patch.object(salt.utils.cloud, 'check_name', - MagicMock(side_effect=[True, False, True])): - comt = ('Invalid characters in name.') - ret.update({'comment': comt}) - self.assertDictEqual(cloud.volume_detached(name, server_name), - ret) + mock_dict = MagicMock( + side_effect=[ + {name: {"name": disk_name, "attachments": False}}, + {}, + {name: {"name": disk_name, "attachments": True}}, + {name: {"name": disk_name, "attachments": True}}, + {name: {"name": disk_name, "attachments": True}}, + ] + ) + with patch.dict( + cloud.__salt__, {"cloud.volume_list": mock_dict, "cloud.action": mock} + ): + with patch.object( + salt.utils.cloud, + "check_name", + MagicMock(side_effect=[True, False, True]), + ): + comt = "Invalid characters in name." + ret.update({"comment": comt}) + self.assertDictEqual(cloud.volume_detached(name, server_name), ret) - ret.update({'name': server_name}) - self.assertDictEqual(cloud.volume_detached(name, server_name), - ret) + ret.update({"name": server_name}) + self.assertDictEqual(cloud.volume_detached(name, server_name), ret) - comt = ('Volume {0} is not currently attached to anything.'.format(disk_name)) - ret.update({'comment': comt, 'result': True}) + comt = "Volume {0} is not currently attached to anything.".format(disk_name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(cloud.volume_detached(name, server_name), ret) - comt = ('Volume {0} does not exist'.format(name)) - ret.update({'comment': comt}) + comt = "Volume {0} does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(cloud.volume_detached(name, server_name), ret) - comt = ('Server {0} does not exist'.format(server_name)) - ret.update({'comment': comt}) + comt = "Server {0} does not exist".format(server_name) + ret.update({"comment": comt}) self.assertDictEqual(cloud.volume_detached(name, server_name), ret) mock = MagicMock(return_value=True) - with patch.dict(cloud.__salt__, {'cloud.action': mock, - 'cloud.volume_detach': mock}): - with patch.dict(cloud.__opts__, {'test': True}): - comt = ('Volume {0} will be will be detached.'.format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(cloud.volume_detached(name, - server_name), - ret) + with patch.dict( + cloud.__salt__, {"cloud.action": mock, "cloud.volume_detach": mock} + ): + with patch.dict(cloud.__opts__, {"test": True}): + comt = "Volume {0} will be will be detached.".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual(cloud.volume_detached(name, server_name), ret) - with patch.dict(cloud.__opts__, {'test': False}): - comt = ('Volume {0} was created'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'new': True, - 'old': {'name': disk_name, - 'attachments': True}}}) - self.assertDictEqual(cloud.volume_detached(name, - server_name), - ret) + with patch.dict(cloud.__opts__, {"test": False}): + comt = "Volume {0} was created".format(name) + ret.update( + { + "comment": comt, + "result": True, + "changes": { + "new": True, + "old": {"name": disk_name, "attachments": True}, + }, + } + ) + self.assertDictEqual(cloud.volume_detached(name, server_name), ret) diff --git a/tests/unit/states/test_cmd.py b/tests/unit/states/test_cmd.py index 738fcdad6b4..e4d7118524e 100644 --- a/tests/unit/states/test_cmd.py +++ b/tests/unit/states/test_cmd.py @@ -1,327 +1,353 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os.path -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -from salt.exceptions import CommandExecutionError - # Import Salt Libs import salt.states.cmd as cmd +from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class CmdTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.cmd - ''' + """ + def setup_loader_modules(self): - return {cmd: {'__env__': 'base'}} + return {cmd: {"__env__": "base"}} # 'mod_run_check' function tests: 1 def test_mod_run_check(self): - ''' + """ Test to execute the onlyif and unless logic. - ''' + """ cmd_kwargs = {} - creates = '/tmp' + creates = "/tmp" mock = MagicMock(return_value=1) - with patch.dict(cmd.__salt__, {'cmd.retcode': mock}): - with patch.dict(cmd.__opts__, {'test': True}): - ret = {'comment': 'onlyif condition is false', 'result': True, - 'skip_watch': True} - self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, '', '', creates), ret) + with patch.dict(cmd.__salt__, {"cmd.retcode": mock}): + with patch.dict(cmd.__opts__, {"test": True}): + ret = { + "comment": "onlyif condition is false", + "result": True, + "skip_watch": True, + } + self.assertDictEqual( + cmd.mod_run_check(cmd_kwargs, "", "", creates), ret + ) - self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, {}, '', creates), ret) + self.assertDictEqual( + cmd.mod_run_check(cmd_kwargs, {}, "", creates), ret + ) mock = MagicMock(return_value=1) - with patch.dict(cmd.__salt__, {'cmd.retcode': mock}): - with patch.dict(cmd.__opts__, {'test': True}): - ret = {'comment': 'onlyif condition is false: ', 'result': True, - 'skip_watch': True} - self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, [''], '', creates), ret) + with patch.dict(cmd.__salt__, {"cmd.retcode": mock}): + with patch.dict(cmd.__opts__, {"test": True}): + ret = { + "comment": "onlyif condition is false: ", + "result": True, + "skip_watch": True, + } + self.assertDictEqual( + cmd.mod_run_check(cmd_kwargs, [""], "", creates), ret + ) mock = MagicMock(return_value=0) - with patch.dict(cmd.__salt__, {'cmd.retcode': mock}): - ret = {'comment': 'unless condition is true', 'result': True, - 'skip_watch': True} - self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, None, '', creates), ret) + with patch.dict(cmd.__salt__, {"cmd.retcode": mock}): + ret = { + "comment": "unless condition is true", + "result": True, + "skip_watch": True, + } + self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, None, "", creates), ret) - self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, None, [''], creates), ret) + self.assertDictEqual( + cmd.mod_run_check(cmd_kwargs, None, [""], creates), ret + ) - self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, None, True, creates), ret) + self.assertDictEqual( + cmd.mod_run_check(cmd_kwargs, None, True, creates), ret + ) - with patch.object(os.path, 'exists', - MagicMock(sid_effect=[True, True, False])): - ret = {'comment': '/tmp exists', 'result': True} - self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, None, None, creates), ret) + with patch.object(os.path, "exists", MagicMock(sid_effect=[True, True, False])): + ret = {"comment": "/tmp exists", "result": True} + self.assertDictEqual( + cmd.mod_run_check(cmd_kwargs, None, None, creates), ret + ) - ret = {'comment': 'All files in creates exist', 'result': True} - self.assertDictEqual(cmd.mod_run_check(cmd_kwargs, None, None, [creates]), ret) + ret = {"comment": "All files in creates exist", "result": True} + self.assertDictEqual( + cmd.mod_run_check(cmd_kwargs, None, None, [creates]), ret + ) self.assertTrue(cmd.mod_run_check(cmd_kwargs, None, None, {})) # 'wait' function tests: 1 def test_wait(self): - ''' + """ Test to run the given command only if the watch statement calls it. - ''' - name = 'cmd.script' + """ + name = "cmd.script" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} self.assertDictEqual(cmd.wait(name), ret) # 'wait_script' function tests: 1 def test_wait_script(self): - ''' + """ Test to download a script from a remote source and execute it only if a watch statement calls it. - ''' - name = 'cmd.script' + """ + name = "cmd.script" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} self.assertDictEqual(cmd.wait_script(name), ret) # 'run' function tests: 1 def test_run(self): - ''' + """ Test to run a command if certain circumstances are met. - ''' - name = 'cmd.script' + """ + name = "cmd.script" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - with patch.dict(cmd.__opts__, {'test': False}): - comt = ("Invalidly-formatted 'env' parameter. See documentation.") - ret.update({'comment': comt}) - self.assertDictEqual(cmd.run(name, env='salt'), ret) + with patch.dict(cmd.__opts__, {"test": False}): + comt = "Invalidly-formatted 'env' parameter. See documentation." + ret.update({"comment": comt}) + self.assertDictEqual(cmd.run(name, env="salt"), ret) - with patch.dict(cmd.__grains__, {'shell': 'shell'}): - with patch.dict(cmd.__opts__, {'test': False}): - mock = MagicMock(side_effect=[CommandExecutionError, - {'retcode': 1}]) - with patch.dict(cmd.__salt__, {'cmd.run_all': mock}): - ret.update({'comment': '', 'result': False}) + with patch.dict(cmd.__grains__, {"shell": "shell"}): + with patch.dict(cmd.__opts__, {"test": False}): + mock = MagicMock(side_effect=[CommandExecutionError, {"retcode": 1}]) + with patch.dict(cmd.__salt__, {"cmd.run_all": mock}): + ret.update({"comment": "", "result": False}) self.assertDictEqual(cmd.run(name), ret) - ret.update({'comment': 'Command "cmd.script" run', - 'result': False, 'changes': {'retcode': 1}}) + ret.update( + { + "comment": 'Command "cmd.script" run', + "result": False, + "changes": {"retcode": 1}, + } + ) self.assertDictEqual(cmd.run(name), ret) - with patch.dict(cmd.__opts__, {'test': True}): - comt = ('Command "cmd.script" would have been executed') - ret.update({'comment': comt, 'result': None, 'changes': {}}) + with patch.dict(cmd.__opts__, {"test": True}): + comt = 'Command "cmd.script" would have been executed' + ret.update({"comment": comt, "result": None, "changes": {}}) self.assertDictEqual(cmd.run(name), ret) mock = MagicMock(return_value=1) - with patch.dict(cmd.__salt__, {'cmd.retcode': mock}): - with patch.dict(cmd.__opts__, {'test': False}): - comt = ('onlyif condition is false') - ret.update({'comment': comt, 'result': True, - 'skip_watch': True}) - self.assertDictEqual(cmd.run(name, onlyif=''), ret) + with patch.dict(cmd.__salt__, {"cmd.retcode": mock}): + with patch.dict(cmd.__opts__, {"test": False}): + comt = "onlyif condition is false" + ret.update({"comment": comt, "result": True, "skip_watch": True}) + self.assertDictEqual(cmd.run(name, onlyif=""), ret) def test_run_root(self): - ''' + """ Test to run a command with a different root - ''' - name = 'cmd.script' + """ + name = "cmd.script" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - with patch.dict(cmd.__grains__, {'shell': 'shell'}): - with patch.dict(cmd.__opts__, {'test': False}): - mock = MagicMock(side_effect=[CommandExecutionError, - {'retcode': 1}]) - with patch.dict(cmd.__salt__, {'cmd.run_chroot': mock}): - ret.update({'comment': '', 'result': False}) - self.assertDictEqual(cmd.run(name, root='/mnt'), ret) + with patch.dict(cmd.__grains__, {"shell": "shell"}): + with patch.dict(cmd.__opts__, {"test": False}): + mock = MagicMock(side_effect=[CommandExecutionError, {"retcode": 1}]) + with patch.dict(cmd.__salt__, {"cmd.run_chroot": mock}): + ret.update({"comment": "", "result": False}) + self.assertDictEqual(cmd.run(name, root="/mnt"), ret) - ret.update({'comment': 'Command "cmd.script" run', - 'result': False, 'changes': {'retcode': 1}}) - self.assertDictEqual(cmd.run(name, root='/mnt'), ret) + ret.update( + { + "comment": 'Command "cmd.script" run', + "result": False, + "changes": {"retcode": 1}, + } + ) + self.assertDictEqual(cmd.run(name, root="/mnt"), ret) # 'script' function tests: 1 def test_script(self): - ''' + """ Test to download a script and execute it with specified arguments. - ''' - name = 'cmd.script' + """ + name = "cmd.script" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - with patch.dict(cmd.__opts__, {'test': False}): - comt = ("Invalidly-formatted 'env' parameter. See documentation.") - ret.update({'comment': comt}) - self.assertDictEqual(cmd.script(name, env='salt'), ret) + with patch.dict(cmd.__opts__, {"test": False}): + comt = "Invalidly-formatted 'env' parameter. See documentation." + ret.update({"comment": comt}) + self.assertDictEqual(cmd.script(name, env="salt"), ret) - with patch.dict(cmd.__grains__, {'shell': 'shell'}): - with patch.dict(cmd.__opts__, {'test': True}): - comt = ("Command 'cmd.script' would have been executed") - ret.update({'comment': comt, 'result': None, 'changes': {}}) + with patch.dict(cmd.__grains__, {"shell": "shell"}): + with patch.dict(cmd.__opts__, {"test": True}): + comt = "Command 'cmd.script' would have been executed" + ret.update({"comment": comt, "result": None, "changes": {}}) self.assertDictEqual(cmd.script(name), ret) - with patch.dict(cmd.__opts__, {'test': False}): - mock = MagicMock(side_effect=[CommandExecutionError, - {'retcode': 1}]) - with patch.dict(cmd.__salt__, {'cmd.script': mock}): - ret.update({'comment': '', 'result': False}) + with patch.dict(cmd.__opts__, {"test": False}): + mock = MagicMock(side_effect=[CommandExecutionError, {"retcode": 1}]) + with patch.dict(cmd.__salt__, {"cmd.script": mock}): + ret.update({"comment": "", "result": False}) self.assertDictEqual(cmd.script(name), ret) - ret.update({'comment': "Command 'cmd.script' run", - 'result': False, 'changes': {'retcode': 1}}) + ret.update( + { + "comment": "Command 'cmd.script' run", + "result": False, + "changes": {"retcode": 1}, + } + ) self.assertDictEqual(cmd.script(name), ret) mock = MagicMock(return_value=1) - with patch.dict(cmd.__salt__, {'cmd.retcode': mock}): - with patch.dict(cmd.__opts__, {'test': False}): - comt = ('onlyif condition is false') - ret.update({'comment': comt, 'result': True, - 'skip_watch': True, 'changes': {}}) - self.assertDictEqual(cmd.script(name, onlyif=''), ret) + with patch.dict(cmd.__salt__, {"cmd.retcode": mock}): + with patch.dict(cmd.__opts__, {"test": False}): + comt = "onlyif condition is false" + ret.update( + { + "comment": comt, + "result": True, + "skip_watch": True, + "changes": {}, + } + ) + self.assertDictEqual(cmd.script(name, onlyif=""), ret) # 'call' function tests: 1 def test_call(self): - ''' + """ Test to invoke a pre-defined Python function with arguments specified in the state declaration. - ''' - name = 'cmd.script' -# func = 'myfunc' + """ + name = "cmd.script" + # func = 'myfunc' - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} flag = None def func(): - ''' + """ Mock func method - ''' + """ if flag: return {} else: return [] - with patch.dict(cmd.__grains__, {'shell': 'shell'}): + with patch.dict(cmd.__grains__, {"shell": "shell"}): flag = True self.assertDictEqual(cmd.call(name, func), ret) flag = False - comt = ('onlyif condition is false') - ret.update({'comment': '', 'result': False, - 'changes': {'retval': []}}) + comt = "onlyif condition is false" + ret.update({"comment": "", "result": False, "changes": {"retval": []}}) self.assertDictEqual(cmd.call(name, func), ret) mock = MagicMock(return_value=1) - with patch.dict(cmd.__salt__, {'cmd.retcode': mock}): - with patch.dict(cmd.__opts__, {'test': True}): - comt = ('onlyif condition is false') - ret.update({'comment': comt, 'skip_watch': True, - 'result': True, 'changes': {}}) - self.assertDictEqual(cmd.call(name, func, onlyif=''), ret) + with patch.dict(cmd.__salt__, {"cmd.retcode": mock}): + with patch.dict(cmd.__opts__, {"test": True}): + comt = "onlyif condition is false" + ret.update( + { + "comment": comt, + "skip_watch": True, + "result": True, + "changes": {}, + } + ) + self.assertDictEqual(cmd.call(name, func, onlyif=""), ret) # 'wait_call' function tests: 1 def test_wait_call(self): - ''' + """ Test to run wait_call. - ''' - name = 'cmd.script' - func = 'myfunc' + """ + name = "cmd.script" + func = "myfunc" - ret = {'name': name, - 'result': True, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": True, "changes": {}, "comment": ""} self.assertDictEqual(cmd.wait_call(name, func), ret) # 'mod_watch' function tests: 1 def test_mod_watch(self): - ''' + """ Test to execute a cmd function based on a watch call - ''' - name = 'cmd.script' + """ + name = "cmd.script" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} def func(): - ''' + """ Mock func method - ''' + """ return {} - with patch.dict(cmd.__grains__, {'shell': 'shell'}): - with patch.dict(cmd.__opts__, {'test': False}): - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(cmd.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(cmd.mod_watch(name, sfun='wait', - stateful=True), ret) + with patch.dict(cmd.__grains__, {"shell": "shell"}): + with patch.dict(cmd.__opts__, {"test": False}): + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(cmd.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual( + cmd.mod_watch(name, sfun="wait", stateful=True), ret + ) - comt = ('Command "cmd.script" run') - ret.update({'comment': comt, 'changes': {'retcode': 1}}) - self.assertDictEqual(cmd.mod_watch(name, sfun='wait', - stateful=False), ret) + comt = 'Command "cmd.script" run' + ret.update({"comment": comt, "changes": {"retcode": 1}}) + self.assertDictEqual( + cmd.mod_watch(name, sfun="wait", stateful=False), ret + ) - with patch.dict(cmd.__salt__, {'cmd.script': mock}): - ret.update({'comment': '', 'changes': {}}) - self.assertDictEqual(cmd.mod_watch(name, sfun='script', - stateful=True), ret) + with patch.dict(cmd.__salt__, {"cmd.script": mock}): + ret.update({"comment": "", "changes": {}}) + self.assertDictEqual( + cmd.mod_watch(name, sfun="script", stateful=True), ret + ) - comt = ("Command 'cmd.script' run") - ret.update({'comment': comt, 'changes': {'retcode': 1}}) - self.assertDictEqual(cmd.mod_watch(name, sfun='script', - stateful=False), ret) + comt = "Command 'cmd.script' run" + ret.update({"comment": comt, "changes": {"retcode": 1}}) + self.assertDictEqual( + cmd.mod_watch(name, sfun="script", stateful=False), ret + ) - with patch.dict(cmd.__salt__, {'cmd.script': mock}): - ret.update({'comment': '', 'changes': {}}) - self.assertDictEqual(cmd.mod_watch(name, sfun='call', - func=func), ret) + with patch.dict(cmd.__salt__, {"cmd.script": mock}): + ret.update({"comment": "", "changes": {}}) + self.assertDictEqual( + cmd.mod_watch(name, sfun="call", func=func), ret + ) - comt = ('cmd.call needs a named parameter func') - ret.update({'comment': comt}) - self.assertDictEqual(cmd.mod_watch(name, sfun='call'), ret) + comt = "cmd.call needs a named parameter func" + ret.update({"comment": comt}) + self.assertDictEqual(cmd.mod_watch(name, sfun="call"), ret) - comt = ('cmd.salt does not work with the watch requisite,' - ' please use cmd.wait or cmd.wait_script') - ret.update({'comment': comt}) - self.assertDictEqual(cmd.mod_watch(name, sfun='salt'), ret) + comt = ( + "cmd.salt does not work with the watch requisite," + " please use cmd.wait or cmd.wait_script" + ) + ret.update({"comment": comt}) + self.assertDictEqual(cmd.mod_watch(name, sfun="salt"), ret) diff --git a/tests/unit/states/test_composer.py b/tests/unit/states/test_composer.py index 7a2984e8ad7..a013e963b6c 100644 --- a/tests/unit/states/test_composer.py +++ b/tests/unit/states/test_composer.py @@ -1,106 +1,101 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -from salt.exceptions import SaltException - # Import Salt Libs import salt.states.composer as composer +from salt.exceptions import SaltException + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class ComposerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.composer - ''' + """ + def setup_loader_modules(self): return {composer: {}} # 'installed' function tests: 1 def test_installed(self): - ''' + """ Test to verify that the correct versions of composer dependencies are present. - ''' - name = 'CURL' + """ + name = "CURL" - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(return_value=True) - with patch.dict(composer.__salt__, - {'composer.did_composer_install': mock}): - comt = ('Composer already installed this directory') - ret.update({'comment': comt}) - self.assertDictEqual(composer.installed(name, always_check=False), - ret) + with patch.dict(composer.__salt__, {"composer.did_composer_install": mock}): + comt = "Composer already installed this directory" + ret.update({"comment": comt}) + self.assertDictEqual(composer.installed(name, always_check=False), ret) - with patch.dict(composer.__opts__, {'test': True}): - comt = ('The state of "CURL" will be changed.') - changes = {'new': 'composer install will be run in CURL', - 'old': 'composer install has been run in CURL'} - ret.update({'comment': comt, 'result': None, - 'changes': changes}) + with patch.dict(composer.__opts__, {"test": True}): + comt = 'The state of "CURL" will be changed.' + changes = { + "new": "composer install will be run in CURL", + "old": "composer install has been run in CURL", + } + ret.update({"comment": comt, "result": None, "changes": changes}) self.assertDictEqual(composer.installed(name), ret) - with patch.dict(composer.__opts__, {'test': False}): + with patch.dict(composer.__opts__, {"test": False}): mock = MagicMock(side_effect=[SaltException, {}]) - with patch.dict(composer.__salt__, {'composer.install': mock}): - comt = ("Error executing composer in " - "'CURL': ") - ret.update({'comment': comt, 'result': False, - 'changes': {}}) + with patch.dict(composer.__salt__, {"composer.install": mock}): + comt = "Error executing composer in " "'CURL': " + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(composer.installed(name), ret) - comt = ('Composer install completed successfully,' - ' output silenced by quiet flag') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(composer.installed(name, quiet=True), - ret) + comt = ( + "Composer install completed successfully," + " output silenced by quiet flag" + ) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(composer.installed(name, quiet=True), ret) # 'update' function tests: 1 def test_update(self): - ''' + """ Test to composer update the directory to ensure we have the latest versions of all project dependencies. - ''' - name = 'CURL' + """ + name = "CURL" - ret = {'name': name, 'result': True, 'comment': '', 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - changes = {'new': 'composer install/update will be run in CURL', - 'old': 'composer install has not yet been run in CURL'} + changes = { + "new": "composer install/update will be run in CURL", + "old": "composer install has not yet been run in CURL", + } mock = MagicMock(return_value=True) - with patch.dict(composer.__salt__, - {'composer.did_composer_install': mock}): - with patch.dict(composer.__opts__, {'test': True}): - comt = ('The state of "CURL" will be changed.') - ret.update({'comment': comt, 'result': None, - 'changes': changes}) + with patch.dict(composer.__salt__, {"composer.did_composer_install": mock}): + with patch.dict(composer.__opts__, {"test": True}): + comt = 'The state of "CURL" will be changed.' + ret.update({"comment": comt, "result": None, "changes": changes}) self.assertDictEqual(composer.update(name), ret) - with patch.dict(composer.__opts__, {'test': False}): + with patch.dict(composer.__opts__, {"test": False}): mock = MagicMock(side_effect=[SaltException, {}]) - with patch.dict(composer.__salt__, {'composer.update': mock}): - comt = ("Error executing composer in " - "'CURL': ") - ret.update({'comment': comt, 'result': False, - 'changes': {}}) + with patch.dict(composer.__salt__, {"composer.update": mock}): + comt = "Error executing composer in " "'CURL': " + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(composer.update(name), ret) - comt = ('Composer update completed successfully,' - ' output silenced by quiet flag') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(composer.update(name, quiet=True), - ret) + comt = ( + "Composer update completed successfully," + " output silenced by quiet flag" + ) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(composer.update(name, quiet=True), ret) diff --git a/tests/unit/states/test_cron.py b/tests/unit/states/test_cron.py index 1d42e3a94b8..8b98534ddff 100644 --- a/tests/unit/states/test_cron.py +++ b/tests/unit/states/test_cron.py @@ -1,36 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place <mp@saltstack.com> -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import salt.modules.cron as cronmod +import salt.states.cron as cron +from salt.ext.six.moves import StringIO # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase -from salt.ext.six.moves import StringIO -import salt.modules.cron as cronmod -import salt.states.cron as cron - -STUB_USER = 'root' -STUB_PATH = '/tmp' +STUB_USER = "root" +STUB_PATH = "/tmp" STUB_CRON_TIMESTAMP = { - 'minute': '1', - 'hour': '2', - 'daymonth': '3', - 'month': '4', - 'dayweek': '5'} + "minute": "1", + "hour": "2", + "daymonth": "3", + "month": "4", + "dayweek": "5", +} -STUB_SIMPLE_RAW_CRON = '5 0 * * * /tmp/no_script.sh' +STUB_SIMPLE_RAW_CRON = "5 0 * * * /tmp/no_script.sh" STUB_SIMPLE_CRON_DICT = { - 'pre': ['5 0 * * * /tmp/no_script.sh'], - 'crons': [], - 'env': [], - 'special': []} + "pre": ["5 0 * * * /tmp/no_script.sh"], + "crons": [], + "env": [], + "special": [], +} CRONTAB = StringIO() @@ -46,279 +48,273 @@ def set_crontab(val): def write_crontab(*args, **kw): - set_crontab('\n'.join( - [a.strip() for a in args[1]])) + set_crontab("\n".join([a.strip() for a in args[1]])) return { - 'retcode': False, + "retcode": False, } class CronTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): loader_gloabals = { - '__low__': {'__id__': 'noo'}, - '__grains__': { - 'os': 'Debian', - 'os_family': 'Debian', + "__low__": {"__id__": "noo"}, + "__grains__": {"os": "Debian", "os_family": "Debian"}, + "__opts__": {"test": False}, + "__salt__": { + "cmd.run_all": MagicMock( + return_value={"pid": 5, "retcode": 0, "stderr": "", "stdout": ""} + ), + "cron.list_tab": cronmod.list_tab, + "cron.rm_job": cronmod.rm_job, + "cron.set_job": cronmod.set_job, }, - '__opts__': {'test': False}, - '__salt__': { - 'cmd.run_all': MagicMock(return_value={ - 'pid': 5, - 'retcode': 0, - 'stderr': '', - 'stdout': ''}), - 'cron.list_tab': cronmod.list_tab, - 'cron.rm_job': cronmod.rm_job, - 'cron.set_job': cronmod.set_job, - } } return {cron: loader_gloabals, cronmod: loader_gloabals} def setUp(self): super(CronTestCase, self).setUp() - set_crontab('') - for mod, mock in (('salt.modules.cron.raw_cron', MagicMock(side_effect=get_crontab)), - ('salt.modules.cron._write_cron_lines', MagicMock(side_effect=write_crontab))): + set_crontab("") + for mod, mock in ( + ("salt.modules.cron.raw_cron", MagicMock(side_effect=get_crontab)), + ( + "salt.modules.cron._write_cron_lines", + MagicMock(side_effect=write_crontab), + ), + ): patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) - self.addCleanup(set_crontab, '') + self.addCleanup(set_crontab, "") def test_present(self): - cron.present( - name='foo', - hour='1', - identifier='1', - user='root') + cron.present(name="foo", hour="1", identifier="1", user="root") self.assertMultiLineEqual( get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 1 * * * foo') - cron.present( - name='foo', - hour='2', - identifier='1', - user='root') + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 1 * * * foo", + ) + cron.present(name="foo", hour="2", identifier="1", user="root") self.assertMultiLineEqual( get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 2 * * * foo') - cron.present( - name='cmd1', - minute='0', - comment='Commented cron job', - commented=True, - identifier='commented_1', - user='root') - self.assertMultiLineEqual( - get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 2 * * * foo\n' - '# Commented cron job SALT_CRON_IDENTIFIER:commented_1\n' - '#DISABLED#0 * * * * cmd1') - cron.present( - name='foo', - hour='2', - identifier='2', - user='root') - self.assertMultiLineEqual( - get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 2 * * * foo\n' - '# Commented cron job SALT_CRON_IDENTIFIER:commented_1\n' - '#DISABLED#0 * * * * cmd1\n' - '# SALT_CRON_IDENTIFIER:2\n' - '* 2 * * * foo') - cron.present( - name='cmd2', - commented=True, - identifier='commented_2', - user='root') - self.assertMultiLineEqual( - get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 2 * * * foo\n' - '# Commented cron job SALT_CRON_IDENTIFIER:commented_1\n' - '#DISABLED#0 * * * * cmd1\n' - '# SALT_CRON_IDENTIFIER:2\n' - '* 2 * * * foo\n' - '# SALT_CRON_IDENTIFIER:commented_2\n' - '#DISABLED#* * * * * cmd2') - set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 2 * * * foo\n' - '# SALT_CRON_IDENTIFIER:2\n' - '* 2 * * * foo\n' - '* 2 * * * foo\n' + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 2 * * * foo", ) cron.present( - name='foo', - hour='2', - user='root', - identifier=None) + name="cmd1", + minute="0", + comment="Commented cron job", + commented=True, + identifier="commented_1", + user="root", + ) + self.assertMultiLineEqual( + get_crontab(), + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 2 * * * foo\n" + "# Commented cron job SALT_CRON_IDENTIFIER:commented_1\n" + "#DISABLED#0 * * * * cmd1", + ) + cron.present(name="foo", hour="2", identifier="2", user="root") + self.assertMultiLineEqual( + get_crontab(), + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 2 * * * foo\n" + "# Commented cron job SALT_CRON_IDENTIFIER:commented_1\n" + "#DISABLED#0 * * * * cmd1\n" + "# SALT_CRON_IDENTIFIER:2\n" + "* 2 * * * foo", + ) + cron.present(name="cmd2", commented=True, identifier="commented_2", user="root") + self.assertMultiLineEqual( + get_crontab(), + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 2 * * * foo\n" + "# Commented cron job SALT_CRON_IDENTIFIER:commented_1\n" + "#DISABLED#0 * * * * cmd1\n" + "# SALT_CRON_IDENTIFIER:2\n" + "* 2 * * * foo\n" + "# SALT_CRON_IDENTIFIER:commented_2\n" + "#DISABLED#* * * * * cmd2", + ) + set_crontab( + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 2 * * * foo\n" + "# SALT_CRON_IDENTIFIER:2\n" + "* 2 * * * foo\n" + "* 2 * * * foo\n" + ) + cron.present(name="foo", hour="2", user="root", identifier=None) self.assertEqual( get_crontab(), - ('# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 2 * * * foo\n' - '# SALT_CRON_IDENTIFIER:2\n' - '* 2 * * * foo\n' - '* 2 * * * foo')) + ( + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 2 * * * foo\n" + "# SALT_CRON_IDENTIFIER:2\n" + "* 2 * * * foo\n" + "* 2 * * * foo" + ), + ) def test_remove(self): - with patch.dict(cron.__opts__, {'test': True}): + with patch.dict(cron.__opts__, {"test": True}): set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 1 * * * foo') - result = cron.absent(name='bar', identifier='1') + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 1 * * * foo" + ) + result = cron.absent(name="bar", identifier="1") self.assertEqual( result, - {'changes': {}, - 'comment': 'Cron bar is set to be removed', - 'name': 'bar', - 'result': None} + { + "changes": {}, + "comment": "Cron bar is set to be removed", + "name": "bar", + "result": None, + }, ) self.assertEqual( get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 1 * * * foo') - with patch.dict(cron.__opts__, {'test': False}): + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 1 * * * foo", + ) + with patch.dict(cron.__opts__, {"test": False}): set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 1 * * * foo') - cron.absent(name='bar', identifier='1') + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 1 * * * foo" + ) + cron.absent(name="bar", identifier="1") self.assertEqual( - get_crontab(), - '# Lines below here are managed by Salt, do not edit' + get_crontab(), "# Lines below here are managed by Salt, do not edit" ) set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '* * * * * foo') - cron.absent(name='bar', identifier='1') + "# Lines below here are managed by Salt, do not edit\n" "* * * * * foo" + ) + cron.absent(name="bar", identifier="1") self.assertEqual( get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '* * * * * foo' + "# Lines below here are managed by Salt, do not edit\n" "* * * * * foo", ) # old behavior, do not remove with identifier set and # even if command match ! set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '* * * * * foo') - cron.absent(name='foo', identifier='1') + "# Lines below here are managed by Salt, do not edit\n" "* * * * * foo" + ) + cron.absent(name="foo", identifier="1") self.assertEqual( - get_crontab(), - '# Lines below here are managed by Salt, do not edit' + get_crontab(), "# Lines below here are managed by Salt, do not edit" ) # old behavior, remove if no identifier and command match set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '* * * * * foo') - cron.absent(name='foo') + "# Lines below here are managed by Salt, do not edit\n" "* * * * * foo" + ) + cron.absent(name="foo") self.assertEqual( - get_crontab(), - '# Lines below here are managed by Salt, do not edit' + get_crontab(), "# Lines below here are managed by Salt, do not edit" ) def test_multiline_comments_are_updated(self): set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '# First crontab - single line comment SALT_CRON_IDENTIFIER:1\n' - '* 1 * * * foo' + "# Lines below here are managed by Salt, do not edit\n" + "# First crontab - single line comment SALT_CRON_IDENTIFIER:1\n" + "* 1 * * * foo" ) cron.present( - name='foo', - hour='1', - comment='First crontab\nfirst multi-line comment\n', - identifier='1', - user='root') + name="foo", + hour="1", + comment="First crontab\nfirst multi-line comment\n", + identifier="1", + user="root", + ) cron.present( - name='foo', - hour='1', - comment='First crontab\nsecond multi-line comment\n', - identifier='1', - user='root') + name="foo", + hour="1", + comment="First crontab\nsecond multi-line comment\n", + identifier="1", + user="root", + ) cron.present( - name='foo', - hour='1', - comment='Second crontab\nmulti-line comment\n', - identifier='2', - user='root') + name="foo", + hour="1", + comment="Second crontab\nmulti-line comment\n", + identifier="2", + user="root", + ) self.assertEqual( get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# First crontab\n' - '# second multi-line comment\n' - '# SALT_CRON_IDENTIFIER:1\n' - '* 1 * * * foo\n' - '# Second crontab\n' - '# multi-line comment\n' - '# SALT_CRON_IDENTIFIER:2\n' - '* 1 * * * foo') + "# Lines below here are managed by Salt, do not edit\n" + "# First crontab\n" + "# second multi-line comment\n" + "# SALT_CRON_IDENTIFIER:1\n" + "* 1 * * * foo\n" + "# Second crontab\n" + "# multi-line comment\n" + "# SALT_CRON_IDENTIFIER:2\n" + "* 1 * * * foo", + ) def test_existing_unmanaged_jobs_are_made_managed(self): set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '0 2 * * * foo' + "# Lines below here are managed by Salt, do not edit\n" "0 2 * * * foo" ) - ret = cron._check_cron('root', 'foo', hour='2', minute='0') - self.assertEqual(ret, 'present') - ret = cron.present('foo', 'root', minute='0', hour='2') - self.assertEqual(ret['changes'], {'root': 'foo'}) - self.assertEqual(ret['comment'], 'Cron foo updated') + ret = cron._check_cron("root", "foo", hour="2", minute="0") + self.assertEqual(ret, "present") + ret = cron.present("foo", "root", minute="0", hour="2") + self.assertEqual(ret["changes"], {"root": "foo"}) + self.assertEqual(ret["comment"], "Cron foo updated") self.assertEqual( get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:foo\n' - '0 2 * * * foo') - ret = cron.present('foo', 'root', minute='0', hour='2') - self.assertEqual(ret['changes'], {}) - self.assertEqual(ret['comment'], 'Cron foo already present') + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:foo\n" + "0 2 * * * foo", + ) + ret = cron.present("foo", "root", minute="0", hour="2") + self.assertEqual(ret["changes"], {}) + self.assertEqual(ret["comment"], "Cron foo already present") def test_existing_noid_jobs_are_updated_with_identifier(self): set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:NO ID SET\n' - '1 * * * * foo' + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:NO ID SET\n" + "1 * * * * foo" ) - ret = cron._check_cron('root', 'foo', minute=1) - self.assertEqual(ret, 'present') - ret = cron.present('foo', 'root', minute=1) - self.assertEqual(ret['changes'], {'root': 'foo'}) - self.assertEqual(ret['comment'], 'Cron foo updated') + ret = cron._check_cron("root", "foo", minute=1) + self.assertEqual(ret, "present") + ret = cron.present("foo", "root", minute=1) + self.assertEqual(ret["changes"], {"root": "foo"}) + self.assertEqual(ret["comment"], "Cron foo updated") self.assertEqual( get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:foo\n' - '1 * * * * foo') + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:foo\n" + "1 * * * * foo", + ) def test_existing_duplicate_unmanaged_jobs_are_merged_and_given_id(self): set_crontab( - '# Lines below here are managed by Salt, do not edit\n' - '0 2 * * * foo\n' - '0 2 * * * foo' + "# Lines below here are managed by Salt, do not edit\n" + "0 2 * * * foo\n" + "0 2 * * * foo" ) - ret = cron._check_cron('root', 'foo', hour='2', minute='0') - self.assertEqual(ret, 'present') - ret = cron.present('foo', 'root', minute='0', hour='2') - self.assertEqual(ret['changes'], {'root': 'foo'}) - self.assertEqual(ret['comment'], 'Cron foo updated') + ret = cron._check_cron("root", "foo", hour="2", minute="0") + self.assertEqual(ret, "present") + ret = cron.present("foo", "root", minute="0", hour="2") + self.assertEqual(ret["changes"], {"root": "foo"}) + self.assertEqual(ret["comment"], "Cron foo updated") self.assertEqual( get_crontab(), - '# Lines below here are managed by Salt, do not edit\n' - '# SALT_CRON_IDENTIFIER:foo\n' - '0 2 * * * foo') - ret = cron.present('foo', 'root', minute='0', hour='2') - self.assertEqual(ret['changes'], {}) - self.assertEqual(ret['comment'], 'Cron foo already present') + "# Lines below here are managed by Salt, do not edit\n" + "# SALT_CRON_IDENTIFIER:foo\n" + "0 2 * * * foo", + ) + ret = cron.present("foo", "root", minute="0", hour="2") + self.assertEqual(ret["changes"], {}) + self.assertEqual(ret["comment"], "Cron foo already present") diff --git a/tests/unit/states/test_ddns.py b/tests/unit/states/test_ddns.py index adb79a95cd0..d965744f9c8 100644 --- a/tests/unit/states/test_ddns.py +++ b/tests/unit/states/test_ddns.py @@ -1,75 +1,74 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.ddns as ddns +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DdnsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ddns - ''' + """ + def setup_loader_modules(self): return {ddns: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensures that the named DNS record is present with the given ttl. - ''' - name = 'webserver' - zone = 'example.com' - ttl = '60' - data = '111.222.333.444' + """ + name = "webserver" + zone = "example.com" + ttl = "60" + data = "111.222.333.444" - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - with patch.dict(ddns.__opts__, {'test': True}): - comt = ('A record "{0}" will be updated'.format(name)) - ret.update({'comment': comt}) + with patch.dict(ddns.__opts__, {"test": True}): + comt = 'A record "{0}" will be updated'.format(name) + ret.update({"comment": comt}) self.assertDictEqual(ddns.present(name, zone, ttl, data), ret) - with patch.dict(ddns.__opts__, {'test': False}): + with patch.dict(ddns.__opts__, {"test": False}): mock = MagicMock(return_value=None) - with patch.dict(ddns.__salt__, {'ddns.update': mock}): - comt = ('A record "{0}" already present with ttl of {1}' - .format(name, ttl)) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(ddns.present(name, zone, ttl, data), - ret) + with patch.dict(ddns.__salt__, {"ddns.update": mock}): + comt = 'A record "{0}" already present with ttl of {1}'.format( + name, ttl + ) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(ddns.present(name, zone, ttl, data), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensures that the named DNS record is absent. - ''' - name = 'webserver' - zone = 'example.com' - data = '111.222.333.444' + """ + name = "webserver" + zone = "example.com" + data = "111.222.333.444" - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - with patch.dict(ddns.__opts__, {'test': True}): - comt = ('None record "{0}" will be deleted'.format(name)) - ret.update({'comment': comt}) + with patch.dict(ddns.__opts__, {"test": True}): + comt = 'None record "{0}" will be deleted'.format(name) + ret.update({"comment": comt}) self.assertDictEqual(ddns.absent(name, zone, data), ret) - with patch.dict(ddns.__opts__, {'test': False}): + with patch.dict(ddns.__opts__, {"test": False}): mock = MagicMock(return_value=None) - with patch.dict(ddns.__salt__, {'ddns.delete': mock}): - comt = ('No matching DNS record(s) present') - ret.update({'comment': comt, 'result': True}) + with patch.dict(ddns.__salt__, {"ddns.delete": mock}): + comt = "No matching DNS record(s) present" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ddns.absent(name, zone, data), ret) diff --git a/tests/unit/states/test_debconfmod.py b/tests/unit/states/test_debconfmod.py index b05afac255d..2f5cbd8d653 100644 --- a/tests/unit/states/test_debconfmod.py +++ b/tests/unit/states/test_debconfmod.py @@ -1,82 +1,79 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.debconfmod as debconfmod +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DebconfmodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.debconfmod - ''' + """ + def setup_loader_modules(self): return {debconfmod: {}} # 'set_file' function tests: 1 def test_set_file(self): - ''' + """ Test to set debconf selections from a file or a template - ''' - name = 'nullmailer' - source = 'salt://pathto/pkg.selections' + """ + name = "nullmailer" + source = "salt://pathto/pkg.selections" - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Context must be formed as a dict') - ret.update({'comment': comt}) - self.assertDictEqual(debconfmod.set_file(name, source, context='salt'), - ret) + comt = "Context must be formed as a dict" + ret.update({"comment": comt}) + self.assertDictEqual(debconfmod.set_file(name, source, context="salt"), ret) - comt = ('Defaults must be formed as a dict') - ret.update({'comment': comt}) - self.assertDictEqual(debconfmod.set_file(name, source, defaults='salt'), - ret) + comt = "Defaults must be formed as a dict" + ret.update({"comment": comt}) + self.assertDictEqual(debconfmod.set_file(name, source, defaults="salt"), ret) - with patch.dict(debconfmod.__opts__, {'test': True}): - comt = ('Debconf selections would have been set.') - ret.update({'comment': comt, 'result': None}) + with patch.dict(debconfmod.__opts__, {"test": True}): + comt = "Debconf selections would have been set." + ret.update({"comment": comt, "result": None}) self.assertDictEqual(debconfmod.set_file(name, source), ret) - with patch.dict(debconfmod.__opts__, {'test': False}): + with patch.dict(debconfmod.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.dict(debconfmod.__salt__, - {'debconf.set_file': mock}): - comt = ('Debconf selections were set.') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(debconfmod.set_file(name, source), - ret) + with patch.dict(debconfmod.__salt__, {"debconf.set_file": mock}): + comt = "Debconf selections were set." + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(debconfmod.set_file(name, source), ret) # 'set' function tests: 1 def test_set(self): - ''' + """ Test to set debconf selections - ''' - name = 'nullmailer' - data = {'shared/mailname': {'type': 'string', - 'value': 'server.domain.tld'}, - 'nullmailer/relayhost': {'type': 'string', - 'value': 'mail.domain.tld'}} + """ + name = "nullmailer" + data = { + "shared/mailname": {"type": "string", "value": "server.domain.tld"}, + "nullmailer/relayhost": {"type": "string", "value": "mail.domain.tld"}, + } - ret = {'name': name, 'result': None, 'comment': '', 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - changes = {'nullmailer/relayhost': 'New value: mail.domain.tld', - 'shared/mailname': 'New value: server.domain.tld'} + changes = { + "nullmailer/relayhost": "New value: mail.domain.tld", + "shared/mailname": "New value: server.domain.tld", + } mock = MagicMock(return_value=None) - with patch.dict(debconfmod.__salt__, {'debconf.show': mock}): - with patch.dict(debconfmod.__opts__, {'test': True}): - ret.update({'changes': changes}) + with patch.dict(debconfmod.__salt__, {"debconf.show": mock}): + with patch.dict(debconfmod.__opts__, {"test": True}): + ret.update({"changes": changes}) self.assertDictEqual(debconfmod.set(name, data), ret) diff --git a/tests/unit/states/test_disk.py b/tests/unit/states/test_disk.py index 64ac2a917f8..975f248a601 100644 --- a/tests/unit/states/test_disk.py +++ b/tests/unit/states/test_disk.py @@ -1,195 +1,222 @@ # -*- coding: utf-8 -*- -''' +""" Tests for disk state -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) from os import path # Import Salt Libs import salt.states.disk as disk +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DiskTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test disk state - ''' + """ + def setup_loader_modules(self): self.mock_data = { - '/': { - '1K-blocks': '41147472', - 'available': '37087976', - 'capacity': '6%', - 'filesystem': '/dev/xvda1', - 'used': '2172880'}, - '/dev': { - '1K-blocks': '10240', - 'available': '10240', - 'capacity': '0%', - 'filesystem': 'udev', - 'used': '0'}, - '/run': { - '1K-blocks': '410624', - 'available': '379460', - 'capacity': '8%', - 'filesystem': 'tmpfs', - 'used': '31164'}, - '/sys/fs/cgroup': { - '1K-blocks': '1026556', - 'available': '1026556', - 'capacity': '0%', - 'filesystem': 'tmpfs', - 'used': '0'} + "/": { + "1K-blocks": "41147472", + "available": "37087976", + "capacity": "6%", + "filesystem": "/dev/xvda1", + "used": "2172880", + }, + "/dev": { + "1K-blocks": "10240", + "available": "10240", + "capacity": "0%", + "filesystem": "udev", + "used": "0", + }, + "/run": { + "1K-blocks": "410624", + "available": "379460", + "capacity": "8%", + "filesystem": "tmpfs", + "used": "31164", + }, + "/sys/fs/cgroup": { + "1K-blocks": "1026556", + "available": "1026556", + "capacity": "0%", + "filesystem": "tmpfs", + "used": "0", + }, } - self.mock_data_path = {'/foo': {'available': '42', 'total': '100'}} + self.mock_data_path = {"/foo": {"available": "42", "total": "100"}} - self.addCleanup(delattr, self, 'mock_data') - self.addCleanup(delattr, self, 'mock_data_path') - return {disk: {'__salt__': {'disk.usage': MagicMock(return_value=self.mock_data), 'status.diskusage': MagicMock(return_value=self.mock_data_path)}}} + self.addCleanup(delattr, self, "mock_data") + self.addCleanup(delattr, self, "mock_data_path") + return { + disk: { + "__salt__": { + "disk.usage": MagicMock(return_value=self.mock_data), + "status.diskusage": MagicMock(return_value=self.mock_data_path), + } + } + } def test_status_missing(self): - ''' + """ Test disk.status when name not found - ''' - mock_fs = '/mnt/cheese' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk mount /mnt/cheese not present. Directory /mnt/cheese does not exist or is not a directory', - 'changes': {}, - 'data': {}} + """ + mock_fs = "/mnt/cheese" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk mount /mnt/cheese not present. Directory /mnt/cheese does not exist or is not a directory", + "changes": {}, + "data": {}, + } ret = disk.status(mock_fs) self.assertEqual(ret, mock_ret) def test_status_type_error(self): - ''' + """ Test disk.status with incorrectly formatted arguments - ''' - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': '', - 'changes': {}, - 'data': {}} + """ + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "", + "changes": {}, + "data": {}, + } - mock_ret['comment'] = 'maximum must be an integer ' - ret = disk.status(mock_fs, maximum=r'e^{i\pi}') + mock_ret["comment"] = "maximum must be an integer " + ret = disk.status(mock_fs, maximum=r"e^{i\pi}") self.assertEqual(ret, mock_ret) - mock_ret['comment'] = 'minimum must be an integer ' - ret = disk.status(mock_fs, minimum=r'\cos\pi + i\sin\pi') + mock_ret["comment"] = "minimum must be an integer " + ret = disk.status(mock_fs, minimum=r"\cos\pi + i\sin\pi") self.assertEqual(ret, mock_ret) def test_status_range_error(self): - ''' + """ Test disk.status with excessive extrema - ''' - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': '', - 'changes': {}, - 'data': {}} + """ + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "", + "changes": {}, + "data": {}, + } - mock_ret['comment'] = 'maximum must be in the range [0, 100] ' - ret = disk.status(mock_fs, maximum='-1') + mock_ret["comment"] = "maximum must be in the range [0, 100] " + ret = disk.status(mock_fs, maximum="-1") self.assertEqual(ret, mock_ret) - mock_ret['comment'] = 'minimum must be in the range [0, 100] ' - ret = disk.status(mock_fs, minimum='101') + mock_ret["comment"] = "minimum must be in the range [0, 100] " + ret = disk.status(mock_fs, minimum="101") self.assertEqual(ret, mock_ret) def test_status_inverted_range(self): - ''' + """ Test disk.status when minimum > maximum - ''' - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'minimum must be less than maximum ', - 'changes': {}, - 'data': {}} + """ + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "minimum must be less than maximum ", + "changes": {}, + "data": {}, + } - ret = disk.status(mock_fs, maximum='0', minimum='1') + ret = disk.status(mock_fs, maximum="0", minimum="1") self.assertEqual(ret, mock_ret) def test_status_threshold(self): - ''' + """ Test disk.status when filesystem triggers thresholds - ''' + """ mock_min = 100 mock_max = 0 - mock_fs = '/' - mock_used = int(self.mock_data[mock_fs]['capacity'].strip('%')) - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': '', - 'changes': {}, - 'data': self.mock_data[mock_fs]} + mock_fs = "/" + mock_used = int(self.mock_data[mock_fs]["capacity"].strip("%")) + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "", + "changes": {}, + "data": self.mock_data[mock_fs], + } - mock_ret['comment'] = 'Disk used space is below minimum of {0} % at {1} %'.format( - mock_min, - mock_used - ) + mock_ret[ + "comment" + ] = "Disk used space is below minimum of {0} % at {1} %".format( + mock_min, mock_used + ) ret = disk.status(mock_fs, minimum=mock_min) self.assertEqual(ret, mock_ret) - mock_ret['comment'] = 'Disk used space is above maximum of {0} % at {1} %'.format( - mock_max, - mock_used - ) + mock_ret[ + "comment" + ] = "Disk used space is above maximum of {0} % at {1} %".format( + mock_max, mock_used + ) ret = disk.status(mock_fs, maximum=mock_max) self.assertEqual(ret, mock_ret) def test_status_strip(self): - ''' + """ Test disk.status appropriately strips unit info from numbers - ''' - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data[mock_fs]} + """ + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data[mock_fs], + } - ret = disk.status(mock_fs, minimum='0%') + ret = disk.status(mock_fs, minimum="0%") self.assertEqual(ret, mock_ret) - ret = disk.status(mock_fs, minimum='0 %') + ret = disk.status(mock_fs, minimum="0 %") self.assertEqual(ret, mock_ret) - ret = disk.status(mock_fs, maximum='100%') + ret = disk.status(mock_fs, maximum="100%") self.assertEqual(ret, mock_ret) - ret = disk.status(mock_fs, minimum='1024K', absolute=True) + ret = disk.status(mock_fs, minimum="1024K", absolute=True) self.assertEqual(ret, mock_ret) - ret = disk.status(mock_fs, minimum='1024KB', absolute=True) + ret = disk.status(mock_fs, minimum="1024KB", absolute=True) self.assertEqual(ret, mock_ret) - ret = disk.status(mock_fs, maximum='4194304 KB', absolute=True) + ret = disk.status(mock_fs, maximum="4194304 KB", absolute=True) self.assertEqual(ret, mock_ret) def test_status(self): - ''' + """ Test disk.status when filesystem meets thresholds - ''' + """ mock_min = 0 mock_max = 100 - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data[mock_fs]} + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data[mock_fs], + } ret = disk.status(mock_fs, minimum=mock_min) self.assertEqual(ret, mock_ret) @@ -199,296 +226,416 @@ class DiskTestCase(TestCase, LoaderModuleMockMixin): # Reset mock because it's an iterator to run the tests with the # absolute flag - ret = {'name': mock_fs, - 'result': False, - 'comment': '', - 'changes': {}, - 'data': {}} + ret = { + "name": mock_fs, + "result": False, + "comment": "", + "changes": {}, + "data": {}, + } - data_1 = {'capacity': '8 %', 'used': '8', 'available': '92'} - data_2 = {'capacity': '22 %', 'used': '22', 'available': '78'} - data_3 = {'capacity': '15 %', 'used': '15', 'available': '85'} - mock = MagicMock(side_effect=[[], {mock_fs: data_1}, {mock_fs: data_2}, {mock_fs: data_3}]) - with patch.dict(disk.__salt__, {'disk.usage': mock}): + data_1 = {"capacity": "8 %", "used": "8", "available": "92"} + data_2 = {"capacity": "22 %", "used": "22", "available": "78"} + data_3 = {"capacity": "15 %", "used": "15", "available": "85"} + mock = MagicMock( + side_effect=[[], {mock_fs: data_1}, {mock_fs: data_2}, {mock_fs: data_3}] + ) + with patch.dict(disk.__salt__, {"disk.usage": mock}): mock = MagicMock(return_value=False) - with patch.object(path, 'isdir', mock): - comt = 'Disk mount / not present. Directory / does not exist or is not a directory' - ret.update({'comment': comt}) + with patch.object(path, "isdir", mock): + comt = "Disk mount / not present. Directory / does not exist or is not a directory" + ret.update({"comment": comt}) self.assertDictEqual(disk.status(mock_fs), ret) - comt = 'minimum must be less than maximum ' - ret.update({'comment': comt}) - self.assertDictEqual(disk.status(mock_fs, '10', '20', absolute=True), ret) + comt = "minimum must be less than maximum " + ret.update({"comment": comt}) + self.assertDictEqual( + disk.status(mock_fs, "10", "20", absolute=True), ret + ) - comt = 'Disk used space is below minimum of 10 KB at 8 KB' - ret.update({'comment': comt, 'data': data_1}) - self.assertDictEqual(disk.status(mock_fs, '20', '10', absolute=True), ret) + comt = "Disk used space is below minimum of 10 KB at 8 KB" + ret.update({"comment": comt, "data": data_1}) + self.assertDictEqual( + disk.status(mock_fs, "20", "10", absolute=True), ret + ) - comt = 'Disk used space is above maximum of 20 KB at 22 KB' - ret.update({'comment': comt, 'data': data_2}) - self.assertDictEqual(disk.status(mock_fs, '20', '10', absolute=True), ret) + comt = "Disk used space is above maximum of 20 KB at 22 KB" + ret.update({"comment": comt, "data": data_2}) + self.assertDictEqual( + disk.status(mock_fs, "20", "10", absolute=True), ret + ) - comt = 'Disk used space in acceptable range' - ret.update({'comment': comt, 'result': True, 'data': data_3}) - self.assertDictEqual(disk.status(mock_fs, '20', '10', absolute=True), ret) + comt = "Disk used space in acceptable range" + ret.update({"comment": comt, "result": True, "data": data_3}) + self.assertDictEqual( + disk.status(mock_fs, "20", "10", absolute=True), ret + ) def test_path_missing(self): - mock_fs = '/bar' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk mount {0} not present. Directory {0} does not exist or is not a directory'.format( - mock_fs), - 'changes': {}, - 'data': {}} + mock_fs = "/bar" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk mount {0} not present. Directory {0} does not exist or is not a directory".format( + mock_fs + ), + "changes": {}, + "data": {}, + } mock = MagicMock(return_value=False) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '58', '55', absolute=True, free=False), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "58", "55", absolute=True, free=False), mock_ret + ) # acceptable range def test_path_used_absolute_acceptable(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '58', '55', absolute=True, free=False), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "58", "55", absolute=True, free=False), mock_ret + ) def test_path_used_relative_acceptable(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '100%', '57%', absolute=False, free=False), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "100%", "57%", absolute=False, free=False), + mock_ret, + ) def test_path_free_absolute_acceptable(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '100', '42', absolute=True, free=True), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "100", "42", absolute=True, free=True), mock_ret + ) def test_path_free_relative_acceptable(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '42%', '41%', absolute=False, free=True), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "42%", "41%", absolute=False, free=True), mock_ret + ) def test_mount_used_absolute_acceptable(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data[mock_fs]} - self.assertDictEqual(disk.status(mock_fs, '2172881', '2172880', absolute=True, free=False), mock_ret) + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data[mock_fs], + } + self.assertDictEqual( + disk.status(mock_fs, "2172881", "2172880", absolute=True, free=False), + mock_ret, + ) def test_mount_used_relative_acceptable(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data[mock_fs]} + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data[mock_fs], + } - self.assertDictEqual(disk.status(mock_fs, '7%', '6%', absolute=False, free=False), mock_ret) + self.assertDictEqual( + disk.status(mock_fs, "7%", "6%", absolute=False, free=False), mock_ret + ) def test_mount_free_absolute_acceptable(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data[mock_fs]} - self.assertDictEqual(disk.status(mock_fs, '37087976', '37087975', absolute=True, free=True), mock_ret) + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data[mock_fs], + } + self.assertDictEqual( + disk.status(mock_fs, "37087976", "37087975", absolute=True, free=True), + mock_ret, + ) def test_mount_free_relative_acceptable(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': True, - 'comment': 'Disk used space in acceptable range', - 'changes': {}, - 'data': self.mock_data[mock_fs]} + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": True, + "comment": "Disk used space in acceptable range", + "changes": {}, + "data": self.mock_data[mock_fs], + } - self.assertDictEqual(disk.status(mock_fs, '100%', '94%', absolute=False, free=True), mock_ret) + self.assertDictEqual( + disk.status(mock_fs, "100%", "94%", absolute=False, free=True), mock_ret + ) # below minimum def test_path_used_absolute_below(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk used space is below minimum of 59 KB at 58 KB', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk used space is below minimum of 59 KB at 58 KB", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '60', '59', absolute=True, free=False), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "60", "59", absolute=True, free=False), mock_ret + ) def test_path_used_relative_below(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk used space is below minimum of 59 % at 58.0 %', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk used space is below minimum of 59 % at 58.0 %", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '60%', '59%', absolute=False, free=False), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "60%", "59%", absolute=False, free=False), mock_ret + ) def test_path_free_absolute_below(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk available space is below minimum of 43 KB at 42 KB', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk available space is below minimum of 43 KB at 42 KB", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '100', '43', absolute=True, free=True), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "100", "43", absolute=True, free=True), mock_ret + ) def test_path_free_relative_below(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk available space is below minimum of 43 % at 42.0 %', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk available space is below minimum of 43 % at 42.0 %", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '100%', '43%', absolute=False, free=True), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "100%", "43%", absolute=False, free=True), mock_ret + ) def test_mount_used_absolute_below(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk used space is below minimum of 2172881 KB at 2172880 KB', - 'changes': {}, - 'data': self.mock_data[mock_fs]} - self.assertDictEqual(disk.status(mock_fs, '2172882', '2172881', absolute=True, free=False), mock_ret) + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk used space is below minimum of 2172881 KB at 2172880 KB", + "changes": {}, + "data": self.mock_data[mock_fs], + } + self.assertDictEqual( + disk.status(mock_fs, "2172882", "2172881", absolute=True, free=False), + mock_ret, + ) def test_mount_used_relative_below(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk used space is below minimum of 7 % at 6 %', - 'changes': {}, - 'data': self.mock_data[mock_fs]} + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk used space is below minimum of 7 % at 6 %", + "changes": {}, + "data": self.mock_data[mock_fs], + } - self.assertDictEqual(disk.status(mock_fs, '8%', '7%', absolute=False, free=False), mock_ret) + self.assertDictEqual( + disk.status(mock_fs, "8%", "7%", absolute=False, free=False), mock_ret + ) def test_mount_free_absolute_below(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk available space is below minimum of 37087977 KB at 37087976 KB', - 'changes': {}, - 'data': self.mock_data[mock_fs]} - self.assertDictEqual(disk.status(mock_fs, '37087978', '37087977', absolute=True, free=True), mock_ret) + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk available space is below minimum of 37087977 KB at 37087976 KB", + "changes": {}, + "data": self.mock_data[mock_fs], + } + self.assertDictEqual( + disk.status(mock_fs, "37087978", "37087977", absolute=True, free=True), + mock_ret, + ) def test_mount_free_relative_below(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk available space is below minimum of 95 % at 94 %', - 'changes': {}, - 'data': self.mock_data[mock_fs]} + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk available space is below minimum of 95 % at 94 %", + "changes": {}, + "data": self.mock_data[mock_fs], + } - self.assertDictEqual(disk.status(mock_fs, '100%', '95%', absolute=False, free=True), mock_ret) + self.assertDictEqual( + disk.status(mock_fs, "100%", "95%", absolute=False, free=True), mock_ret + ) # above maximum def test_path_used_absolute_above(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk used space is above maximum of 57 KB at 58 KB', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk used space is above maximum of 57 KB at 58 KB", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '57', '56', absolute=True, free=False), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "57", "56", absolute=True, free=False), mock_ret + ) def test_path_used_relative_above(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk used space is above maximum of 57 % at 58.0 %', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk used space is above maximum of 57 % at 58.0 %", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '57%', '56%', absolute=False, free=False), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "57%", "56%", absolute=False, free=False), mock_ret + ) def test_path_free_absolute_above(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk available space is above maximum of 41 KB at 42 KB', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk available space is above maximum of 41 KB at 42 KB", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '41', '40', absolute=True, free=True), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "41", "40", absolute=True, free=True), mock_ret + ) def test_path_free_relative_above(self): - mock_fs = '/foo' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk available space is above maximum of 41 % at 42.0 %', - 'changes': {}, - 'data': self.mock_data_path} + mock_fs = "/foo" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk available space is above maximum of 41 % at 42.0 %", + "changes": {}, + "data": self.mock_data_path, + } mock = MagicMock(return_value=True) - with patch.object(path, 'isdir', mock): - self.assertDictEqual(disk.status(mock_fs, '41%', '40%', absolute=False, free=True), mock_ret) + with patch.object(path, "isdir", mock): + self.assertDictEqual( + disk.status(mock_fs, "41%", "40%", absolute=False, free=True), mock_ret + ) def test_mount_used_absolute_above(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk used space is above maximum of 2172879 KB at 2172880 KB', - 'changes': {}, - 'data': self.mock_data[mock_fs]} - self.assertDictEqual(disk.status(mock_fs, '2172879', '2172878', absolute=True, free=False), mock_ret) + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk used space is above maximum of 2172879 KB at 2172880 KB", + "changes": {}, + "data": self.mock_data[mock_fs], + } + self.assertDictEqual( + disk.status(mock_fs, "2172879", "2172878", absolute=True, free=False), + mock_ret, + ) def test_mount_used_relative_above(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk used space is above maximum of 5 % at 6 %', - 'changes': {}, - 'data': self.mock_data[mock_fs]} + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk used space is above maximum of 5 % at 6 %", + "changes": {}, + "data": self.mock_data[mock_fs], + } - self.assertDictEqual(disk.status(mock_fs, '5%', '4%', absolute=False, free=False), mock_ret) + self.assertDictEqual( + disk.status(mock_fs, "5%", "4%", absolute=False, free=False), mock_ret + ) def test_mount_free_absolute_above(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk available space is above maximum of 37087975 KB at 37087976 KB', - 'changes': {}, - 'data': self.mock_data[mock_fs]} - self.assertDictEqual(disk.status(mock_fs, '37087975', '37087974', absolute=True, free=True), mock_ret) + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk available space is above maximum of 37087975 KB at 37087976 KB", + "changes": {}, + "data": self.mock_data[mock_fs], + } + self.assertDictEqual( + disk.status(mock_fs, "37087975", "37087974", absolute=True, free=True), + mock_ret, + ) def test_mount_free_relative_above(self): - mock_fs = '/' - mock_ret = {'name': mock_fs, - 'result': False, - 'comment': 'Disk available space is above maximum of 93 % at 94 %', - 'changes': {}, - 'data': self.mock_data[mock_fs]} + mock_fs = "/" + mock_ret = { + "name": mock_fs, + "result": False, + "comment": "Disk available space is above maximum of 93 % at 94 %", + "changes": {}, + "data": self.mock_data[mock_fs], + } - self.assertDictEqual(disk.status(mock_fs, '93%', '92%', absolute=False, free=True), mock_ret) + self.assertDictEqual( + disk.status(mock_fs, "93%", "92%", absolute=False, free=True), mock_ret + ) diff --git a/tests/unit/states/test_docker_image.py b/tests/unit/states/test_docker_image.py index 416267bf86b..74382823b6e 100644 --- a/tests/unit/states/test_docker_image.py +++ b/tests/unit/states/test_docker_image.py @@ -1,40 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for the docker state -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.modules.dockermod as docker_mod import salt.states.docker_image as docker_state +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DockerImageTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test docker_image states - ''' + """ + def setup_loader_modules(self): return { - docker_mod: { - '__context__': {'docker.docker_version': ''} - }, - docker_state: { - '__opts__': {'test': False} - } + docker_mod: {"__context__": {"docker.docker_version": ""}}, + docker_state: {"__opts__": {"test": False}}, } def test_present_already_local(self): - ''' + """ According following sls, .. code-block:: yaml @@ -45,33 +39,38 @@ class DockerImageTestCase(TestCase, LoaderModuleMockMixin): if ``image:latest`` is already downloaded locally the state should not report changes. - ''' - docker_inspect_image = MagicMock(return_value={'Id': 'abcdefghijkl'}) + """ + docker_inspect_image = MagicMock(return_value={"Id": "abcdefghijkl"}) docker_pull = MagicMock( - return_value={'Layers': - {'Already_Pulled': ['abcdefghijkl'], - 'Pulled': []}, - 'Status': 'Image is up to date for image:latest', - 'Time_Elapsed': 1.1}) - docker_list_tags = MagicMock(return_value=['image:latest']) - docker_resolve_tag = MagicMock(return_value='image:latest') - __salt__ = {'docker.list_tags': docker_list_tags, - 'docker.pull': docker_pull, - 'docker.inspect_image': docker_inspect_image, - 'docker.resolve_tag': docker_resolve_tag} - with patch.dict(docker_state.__dict__, - {'__salt__': __salt__}): - ret = docker_state.present('image:latest', force=True) - self.assertEqual(ret, - {'changes': {}, - 'result': True, - 'comment': "Image 'image:latest' was pulled, " - "but there were no changes", - 'name': 'image:latest', - }) + return_value={ + "Layers": {"Already_Pulled": ["abcdefghijkl"], "Pulled": []}, + "Status": "Image is up to date for image:latest", + "Time_Elapsed": 1.1, + } + ) + docker_list_tags = MagicMock(return_value=["image:latest"]) + docker_resolve_tag = MagicMock(return_value="image:latest") + __salt__ = { + "docker.list_tags": docker_list_tags, + "docker.pull": docker_pull, + "docker.inspect_image": docker_inspect_image, + "docker.resolve_tag": docker_resolve_tag, + } + with patch.dict(docker_state.__dict__, {"__salt__": __salt__}): + ret = docker_state.present("image:latest", force=True) + self.assertEqual( + ret, + { + "changes": {}, + "result": True, + "comment": "Image 'image:latest' was pulled, " + "but there were no changes", + "name": "image:latest", + }, + ) def test_present_and_force(self): - ''' + """ According following sls, .. code-block:: yaml @@ -82,28 +81,35 @@ class DockerImageTestCase(TestCase, LoaderModuleMockMixin): if ``image:latest`` is not downloaded and force is true should pull a new image successfully. - ''' - docker_inspect_image = MagicMock(return_value={'Id': '1234567890ab'}) + """ + docker_inspect_image = MagicMock(return_value={"Id": "1234567890ab"}) docker_pull = MagicMock( - return_value={'Layers': - {'Pulled': ['abcdefghijkl']}, - 'Status': "Image 'image:latest' was pulled", - 'Time_Elapsed': 1.1}) - docker_list_tags = MagicMock(side_effect=[[], ['image:latest']]) - docker_resolve_tag = MagicMock(return_value='image:latest') - __salt__ = {'docker.list_tags': docker_list_tags, - 'docker.pull': docker_pull, - 'docker.inspect_image': docker_inspect_image, - 'docker.resolve_tag': docker_resolve_tag} - with patch.dict(docker_state.__dict__, - {'__salt__': __salt__}): - ret = docker_state.present('image:latest', force=True) - self.assertEqual(ret, - {'changes': { - 'Layers': {'Pulled': ['abcdefghijkl']}, - 'Status': "Image 'image:latest' was pulled", - 'Time_Elapsed': 1.1}, - 'result': True, - 'comment': "Image 'image:latest' was pulled", - 'name': 'image:latest', - }) + return_value={ + "Layers": {"Pulled": ["abcdefghijkl"]}, + "Status": "Image 'image:latest' was pulled", + "Time_Elapsed": 1.1, + } + ) + docker_list_tags = MagicMock(side_effect=[[], ["image:latest"]]) + docker_resolve_tag = MagicMock(return_value="image:latest") + __salt__ = { + "docker.list_tags": docker_list_tags, + "docker.pull": docker_pull, + "docker.inspect_image": docker_inspect_image, + "docker.resolve_tag": docker_resolve_tag, + } + with patch.dict(docker_state.__dict__, {"__salt__": __salt__}): + ret = docker_state.present("image:latest", force=True) + self.assertEqual( + ret, + { + "changes": { + "Layers": {"Pulled": ["abcdefghijkl"]}, + "Status": "Image 'image:latest' was pulled", + "Time_Elapsed": 1.1, + }, + "result": True, + "comment": "Image 'image:latest' was pulled", + "name": "image:latest", + }, + ) diff --git a/tests/unit/states/test_docker_volume.py b/tests/unit/states/test_docker_volume.py index f62ea0a655d..21f9bfc07e4 100644 --- a/tests/unit/states/test_docker_volume.py +++ b/tests/unit/states/test_docker_volume.py @@ -1,59 +1,53 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for the docker state -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - patch -) - # Import Salt Libs import salt.modules.dockermod as docker_mod import salt.states.docker_volume as docker_state +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock, patch +from tests.support.unit import TestCase + class DockerVolumeTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test docker_volume states - ''' + """ + def setup_loader_modules(self): return { - docker_mod: { - '__context__': {'docker.docker_version': ''} - }, - docker_state: { - '__opts__': {'test': False} - } + docker_mod: {"__context__": {"docker.docker_version": ""}}, + docker_state: {"__opts__": {"test": False}}, } def test_present(self): - ''' + """ Test docker_volume.present - ''' + """ volumes = [] - default_driver = 'dummy_default' + default_driver = "dummy_default" def create_volume(name, driver=None, driver_opts=None): for v in volumes: # present should never try to add a conflicting # volume - self.assertNotEqual(v['Name'], name) + self.assertNotEqual(v["Name"], name) if driver is None: driver = default_driver - new = {'Name': name, 'Driver': driver} + new = {"Name": name, "Driver": driver} volumes.append(new) return new def remove_volume(name): old_len = len(volumes) - removed = [v for v in volumes if v['Name'] == name] + removed = [v for v in volumes if v["Name"] == name] # present should not have tried to remove a volume # that didn't exist self.assertEqual(1, len(removed)) @@ -62,153 +56,152 @@ class DockerVolumeTestCase(TestCase, LoaderModuleMockMixin): docker_create_volume = Mock(side_effect=create_volume) __salt__ = { - 'docker.create_volume': docker_create_volume, - 'docker.volumes': Mock(return_value={'Volumes': volumes}), - 'docker.remove_volume': Mock(side_effect=remove_volume) + "docker.create_volume": docker_create_volume, + "docker.volumes": Mock(return_value={"Volumes": volumes}), + "docker.remove_volume": Mock(side_effect=remove_volume), } - with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = docker_state.present('volume_foo') - docker_create_volume.assert_called_with('volume_foo', - driver=None, - driver_opts=None) + with patch.dict(docker_state.__dict__, {"__salt__": __salt__}): + ret = docker_state.present("volume_foo") + docker_create_volume.assert_called_with( + "volume_foo", driver=None, driver_opts=None + ) self.assertEqual( { - 'name': 'volume_foo', - 'comment': '', - 'changes': { - 'created': { - 'Driver': default_driver, - 'Name': 'volume_foo', - }, + "name": "volume_foo", + "comment": "", + "changes": { + "created": {"Driver": default_driver, "Name": "volume_foo"}, }, - 'result': True, + "result": True, }, - ret) + ret, + ) self.assertEqual(len(volumes), 1) - self.assertEqual(volumes[0]['Name'], 'volume_foo') - self.assertIs(volumes[0]['Driver'], default_driver) + self.assertEqual(volumes[0]["Name"], "volume_foo") + self.assertIs(volumes[0]["Driver"], default_driver) # run it again with the same arguments orig_volumes = [volumes[0].copy()] - ret = docker_state.present('volume_foo') + ret = docker_state.present("volume_foo") self.assertEqual( { - 'name': 'volume_foo', - 'comment': "Volume 'volume_foo' already exists.", - 'changes': {}, - 'result': True, + "name": "volume_foo", + "comment": "Volume 'volume_foo' already exists.", + "changes": {}, + "result": True, }, - ret) + ret, + ) self.assertEqual(orig_volumes, volumes) # run it again with a different driver but don't force - ret = docker_state.present('volume_foo', driver='local') + ret = docker_state.present("volume_foo", driver="local") self.assertEqual( { - 'name': 'volume_foo', - 'comment': ("Driver for existing volume 'volume_foo'" - " ('dummy_default') does not match specified" - " driver ('local') and force is False"), - 'changes': {}, - 'result': False, + "name": "volume_foo", + "comment": ( + "Driver for existing volume 'volume_foo'" + " ('dummy_default') does not match specified" + " driver ('local') and force is False" + ), + "changes": {}, + "result": False, }, - ret) + ret, + ) self.assertEqual(orig_volumes, volumes) # run it again with a different driver and force - ret = docker_state.present( - 'volume_foo', driver='local', force=True) + ret = docker_state.present("volume_foo", driver="local", force=True) self.assertEqual( { - 'name': 'volume_foo', - 'comment': "", - 'changes': { - 'removed': { - 'Driver': default_driver, - 'Name': 'volume_foo', - }, - 'created': { - 'Driver': 'local', - 'Name': 'volume_foo', - }, + "name": "volume_foo", + "comment": "", + "changes": { + "removed": {"Driver": default_driver, "Name": "volume_foo"}, + "created": {"Driver": "local", "Name": "volume_foo"}, }, - 'result': True, + "result": True, }, - ret) + ret, + ) mod_orig_volumes = [orig_volumes[0].copy()] - mod_orig_volumes[0]['Driver'] = 'local' + mod_orig_volumes[0]["Driver"] = "local" self.assertEqual(mod_orig_volumes, volumes) def test_present_with_another_driver(self): - ''' + """ Test docker_volume.present - ''' - docker_create_volume = Mock(return_value='created') - docker_remove_volume = Mock(return_value='removed') - __salt__ = {'docker.create_volume': docker_create_volume, - 'docker.remove_volume': docker_remove_volume, - 'docker.volumes': Mock(return_value={ - 'Volumes': [{'Name': 'volume_foo', - 'Driver': 'foo'}]}), - } - with patch.dict(docker_state.__dict__, - {'__salt__': __salt__}): - ret = docker_state.present( - 'volume_foo', - driver='bar', - force=True, - ) - docker_remove_volume.assert_called_with('volume_foo') - docker_create_volume.assert_called_with('volume_foo', - driver='bar', - driver_opts=None) - self.assertEqual(ret, {'name': 'volume_foo', - 'comment': '', - 'changes': {'created': 'created', - 'removed': 'removed'}, - 'result': True}) + """ + docker_create_volume = Mock(return_value="created") + docker_remove_volume = Mock(return_value="removed") + __salt__ = { + "docker.create_volume": docker_create_volume, + "docker.remove_volume": docker_remove_volume, + "docker.volumes": Mock( + return_value={"Volumes": [{"Name": "volume_foo", "Driver": "foo"}]} + ), + } + with patch.dict(docker_state.__dict__, {"__salt__": __salt__}): + ret = docker_state.present("volume_foo", driver="bar", force=True,) + docker_remove_volume.assert_called_with("volume_foo") + docker_create_volume.assert_called_with( + "volume_foo", driver="bar", driver_opts=None + ) + self.assertEqual( + ret, + { + "name": "volume_foo", + "comment": "", + "changes": {"created": "created", "removed": "removed"}, + "result": True, + }, + ) def test_present_wo_existing_volumes(self): - ''' + """ Test docker_volume.present without existing volumes. - ''' - docker_create_volume = Mock(return_value='created') - docker_remove_volume = Mock(return_value='removed') - __salt__ = {'docker.create_volume': docker_create_volume, - 'docker.remove_volume': docker_remove_volume, - 'docker.volumes': Mock(return_value={'Volumes': None}), - } - with patch.dict(docker_state.__dict__, - {'__salt__': __salt__}): - ret = docker_state.present( - 'volume_foo', - driver='bar', - force=True, - ) - docker_create_volume.assert_called_with('volume_foo', - driver='bar', - driver_opts=None) - self.assertEqual(ret, {'name': 'volume_foo', - 'comment': '', - 'changes': {'created': 'created'}, - 'result': True}) + """ + docker_create_volume = Mock(return_value="created") + docker_remove_volume = Mock(return_value="removed") + __salt__ = { + "docker.create_volume": docker_create_volume, + "docker.remove_volume": docker_remove_volume, + "docker.volumes": Mock(return_value={"Volumes": None}), + } + with patch.dict(docker_state.__dict__, {"__salt__": __salt__}): + ret = docker_state.present("volume_foo", driver="bar", force=True,) + docker_create_volume.assert_called_with( + "volume_foo", driver="bar", driver_opts=None + ) + self.assertEqual( + ret, + { + "name": "volume_foo", + "comment": "", + "changes": {"created": "created"}, + "result": True, + }, + ) def test_absent(self): - ''' + """ Test docker_volume.absent - ''' - docker_remove_volume = Mock(return_value='removed') - __salt__ = {'docker.remove_volume': docker_remove_volume, - 'docker.volumes': Mock(return_value={ - 'Volumes': [{'Name': 'volume_foo'}]}), - } - with patch.dict(docker_state.__dict__, - {'__salt__': __salt__}): - ret = docker_state.absent( - 'volume_foo', - ) - docker_remove_volume.assert_called_with('volume_foo') - self.assertEqual(ret, {'name': 'volume_foo', - 'comment': '', - 'changes': {'removed': 'removed'}, - 'result': True}) + """ + docker_remove_volume = Mock(return_value="removed") + __salt__ = { + "docker.remove_volume": docker_remove_volume, + "docker.volumes": Mock(return_value={"Volumes": [{"Name": "volume_foo"}]}), + } + with patch.dict(docker_state.__dict__, {"__salt__": __salt__}): + ret = docker_state.absent("volume_foo",) + docker_remove_volume.assert_called_with("volume_foo") + self.assertEqual( + ret, + { + "name": "volume_foo", + "comment": "", + "changes": {"removed": "removed"}, + "result": True, + }, + ) diff --git a/tests/unit/states/test_drac.py b/tests/unit/states/test_drac.py index ef6560be145..e7c3653a998 100644 --- a/tests/unit/states/test_drac.py +++ b/tests/unit/states/test_drac.py @@ -1,112 +1,107 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.drac as drac +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class DracTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.drac - ''' + """ + def setup_loader_modules(self): return {drac: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the user exists on the Dell DRAC - ''' - name = 'damian' - password = 'secret' - permission = 'login,test_alerts,clear_logs' + """ + name = "damian" + password = "secret" + permission = "login,test_alerts,clear_logs" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(return_value=[name]) - with patch.dict(drac.__salt__, {'drac.list_users': mock}): - with patch.dict(drac.__opts__, {'test': True}): - comt = ('`{0}` already exists'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(drac.present(name, password, permission), - ret) + with patch.dict(drac.__salt__, {"drac.list_users": mock}): + with patch.dict(drac.__opts__, {"test": True}): + comt = "`{0}` already exists".format(name) + ret.update({"comment": comt}) + self.assertDictEqual(drac.present(name, password, permission), ret) - with patch.dict(drac.__opts__, {'test': False}): - comt = ('`{0}` already exists'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(drac.present(name, password, permission), - ret) + with patch.dict(drac.__opts__, {"test": False}): + comt = "`{0}` already exists".format(name) + ret.update({"comment": comt}) + self.assertDictEqual(drac.present(name, password, permission), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure a user does not exist on the Dell DRAC - ''' - name = 'damian' + """ + name = "damian" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(return_value=[]) - with patch.dict(drac.__salt__, {'drac.list_users': mock}): - with patch.dict(drac.__opts__, {'test': True}): - comt = ('`{0}` does not exist'.format(name)) - ret.update({'comment': comt}) + with patch.dict(drac.__salt__, {"drac.list_users": mock}): + with patch.dict(drac.__opts__, {"test": True}): + comt = "`{0}` does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(drac.absent(name), ret) - with patch.dict(drac.__opts__, {'test': False}): - comt = ('`{0}` does not exist'.format(name)) - ret.update({'comment': comt}) + with patch.dict(drac.__opts__, {"test": False}): + comt = "`{0}` does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(drac.absent(name), ret) # 'network' function tests: 1 def test_network(self): - ''' + """ Test to ensure the DRAC network settings are consistent - ''' - ip_ = '10.225.108.29' - netmask = '255.255.255.224' - gateway = '10.225.108.1' + """ + ip_ = "10.225.108.29" + netmask = "255.255.255.224" + gateway = "10.225.108.1" - ret = {'name': ip_, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": ip_, "result": None, "comment": "", "changes": {}} - net_info = {'IPv4 settings': {'IP Address': ip_, 'Subnet Mask': netmask, - 'Gateway': gateway}} + net_info = { + "IPv4 settings": { + "IP Address": ip_, + "Subnet Mask": netmask, + "Gateway": gateway, + } + } mock_info = MagicMock(return_value=net_info) mock_bool = MagicMock(side_effect=[True, False]) - with patch.dict(drac.__salt__, {'drac.network_info': mock_info, - 'drac.set_network': mock_bool}): - with patch.dict(drac.__opts__, {'test': True}): + with patch.dict( + drac.__salt__, + {"drac.network_info": mock_info, "drac.set_network": mock_bool}, + ): + with patch.dict(drac.__opts__, {"test": True}): self.assertDictEqual(drac.network(ip_, netmask, gateway), ret) - with patch.dict(drac.__opts__, {'test': False}): - comt = ('Network is in the desired state') - ret.update({'comment': comt, 'result': True}) + with patch.dict(drac.__opts__, {"test": False}): + comt = "Network is in the desired state" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(drac.network(ip_, netmask, gateway), ret) - comt = ('unable to configure network') - ret.update({'comment': comt, 'result': False}) + comt = "unable to configure network" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(drac.network(ip_, netmask, gateway), ret) diff --git a/tests/unit/states/test_elasticsearch.py b/tests/unit/states/test_elasticsearch.py index c8bf3debaa5..8446110f0c9 100644 --- a/tests/unit/states/test_elasticsearch.py +++ b/tests/unit/states/test_elasticsearch.py @@ -1,503 +1,908 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Lukas Raska <lukas@raska.me> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -from salt.exceptions import CommandExecutionError - # Import Salt Libs import salt.utils.dictdiffer as dictdiffer +from salt.exceptions import CommandExecutionError from salt.states import elasticsearch +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ElasticsearchTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.elasticsearch - ''' + """ + def setup_loader_modules(self): return { elasticsearch: { - '__opts__': {'test': False}, - '__utils__': {'dictdiffer.deep_diff': dictdiffer.deep_diff} + "__opts__": {"test": False}, + "__utils__": {"dictdiffer.deep_diff": dictdiffer.deep_diff}, } } # 'index_absent' function tests: 1 def test_index_absent(self): - ''' + """ Test to manage a elasticsearch index. - ''' - name = 'foo' + """ + name = "foo" - ret = {'name': name, - 'result': True, - 'comment': 'Index foo is already absent', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Index foo is already absent", + "changes": {}, + } - mock_get = MagicMock(side_effect=[None, {name: {"test": "key"}}, {name: {}}, {name: {"test": "key"}}, CommandExecutionError, {name: {"test": "key"}}]) + mock_get = MagicMock( + side_effect=[ + None, + {name: {"test": "key"}}, + {name: {}}, + {name: {"test": "key"}}, + CommandExecutionError, + {name: {"test": "key"}}, + ] + ) mock_delete = MagicMock(side_effect=[True, False, CommandExecutionError]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.index_get': mock_get, - 'elasticsearch.index_delete': mock_delete}): + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.index_get": mock_get, + "elasticsearch.index_delete": mock_delete, + }, + ): self.assertDictEqual(elasticsearch.index_absent(name), ret) - ret.update({'comment': 'Successfully removed index foo', 'changes': {"old": {"test": "key"}}}) + ret.update( + { + "comment": "Successfully removed index foo", + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.index_absent(name), ret) - ret.update({'comment': 'Failed to remove index foo for unknown reasons', 'result': False, 'changes': {}}) + ret.update( + { + "comment": "Failed to remove index foo for unknown reasons", + "result": False, + "changes": {}, + } + ) self.assertDictEqual(elasticsearch.index_absent(name), ret) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Index foo will be removed", 'result': None, 'changes': {"old": {"test": "key"}}}) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Index foo will be removed", + "result": None, + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.index_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_absent(name), ret) # 'index_present' function tests: 1 def test_index_present(self): - ''' + """ Test to manage a elasticsearch index. - ''' - name = 'foo' + """ + name = "foo" - ret = {'name': name, - 'result': True, - 'comment': 'Index foo is already present', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Index foo is already present", + "changes": {}, + } - mock_exists = MagicMock(side_effect=[True, False, False, False, CommandExecutionError, False, False]) - mock_get = MagicMock(side_effect=[{name: {"test": "key"}}, CommandExecutionError]) + mock_exists = MagicMock( + side_effect=[True, False, False, False, CommandExecutionError, False, False] + ) + mock_get = MagicMock( + side_effect=[{name: {"test": "key"}}, CommandExecutionError] + ) mock_create = MagicMock(side_effect=[True, False, CommandExecutionError, True]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.index_get': mock_get, - 'elasticsearch.index_exists': mock_exists, - 'elasticsearch.index_create': mock_create}): + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.index_get": mock_get, + "elasticsearch.index_exists": mock_exists, + "elasticsearch.index_create": mock_create, + }, + ): self.assertDictEqual(elasticsearch.index_present(name), ret) - ret.update({'comment': 'Successfully created index foo', 'changes': {"new": {"test": "key"}}}) + ret.update( + { + "comment": "Successfully created index foo", + "changes": {"new": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.index_present(name), ret) - ret.update({'comment': 'Cannot create index foo, False', 'result': False, 'changes': {}}) + ret.update( + { + "comment": "Cannot create index foo, False", + "result": False, + "changes": {}, + } + ) self.assertDictEqual(elasticsearch.index_present(name), ret) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Index foo does not exist and will be created", 'result': None, 'changes': {"new": {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.index_present(name, {"test2": "key"}), ret) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Index foo does not exist and will be created", + "result": None, + "changes": {"new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.index_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_absent(name), ret) # 'alias_absent' function tests: 1 def test_alias_absent(self): - ''' + """ Test to manage a elasticsearch alias. - ''' - name = 'foo' - index = 'bar' + """ + name = "foo" + index = "bar" alias = {index: {"aliases": {name: {"test": "key"}}}} - ret = {'name': name, - 'result': True, - 'comment': 'Alias foo for index bar is already absent', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Alias foo for index bar is already absent", + "changes": {}, + } - mock_get = MagicMock(side_effect=[None, {"foo2": {}}, alias, alias, alias, CommandExecutionError, alias]) + mock_get = MagicMock( + side_effect=[ + None, + {"foo2": {}}, + alias, + alias, + alias, + CommandExecutionError, + alias, + ] + ) mock_delete = MagicMock(side_effect=[True, False, CommandExecutionError]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.alias_get': mock_get, - 'elasticsearch.alias_delete': mock_delete}): + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.alias_get": mock_get, + "elasticsearch.alias_delete": mock_delete, + }, + ): self.assertDictEqual(elasticsearch.alias_absent(name, index), ret) self.assertDictEqual(elasticsearch.alias_absent(name, index), ret) - ret.update({'comment': 'Successfully removed alias foo for index bar', 'changes': {"old": {"test": "key"}}}) + ret.update( + { + "comment": "Successfully removed alias foo for index bar", + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.alias_absent(name, index), ret) - ret.update({'comment': 'Failed to remove alias foo for index bar for unknown reasons', 'result': False, 'changes': {}}) + ret.update( + { + "comment": "Failed to remove alias foo for index bar for unknown reasons", + "result": False, + "changes": {}, + } + ) self.assertDictEqual(elasticsearch.alias_absent(name, index), ret) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Alias foo for index bar will be removed", 'result': None, 'changes': {"old": {"test": "key"}}}) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Alias foo for index bar will be removed", + "result": None, + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.alias_absent(name, index), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.alias_absent(name, index), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.alias_absent(name, index), ret) # 'alias_present' function tests: 1 def test_alias_present(self): - ''' + """ Test to manage a elasticsearch alias. - ''' - name = 'foo' - index = 'bar' + """ + name = "foo" + index = "bar" alias = {index: {"aliases": {name: {"test": "key"}}}} - ret = {'name': name, - 'result': True, - 'comment': 'Alias foo for index bar is already present', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Alias foo for index bar is already present", + "changes": {}, + } - mock_get = MagicMock(side_effect=[alias, alias, None, None, None, alias, CommandExecutionError, None]) + mock_get = MagicMock( + side_effect=[ + alias, + alias, + None, + None, + None, + alias, + CommandExecutionError, + None, + ] + ) mock_create = MagicMock(side_effect=[True, True, False, CommandExecutionError]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.alias_get': mock_get, - 'elasticsearch.alias_create': mock_create}): - self.assertDictEqual(elasticsearch.alias_present(name, index, {"test": "key"}), ret) + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.alias_get": mock_get, + "elasticsearch.alias_create": mock_create, + }, + ): + self.assertDictEqual( + elasticsearch.alias_present(name, index, {"test": "key"}), ret + ) - ret.update({'comment': "Successfully replaced alias foo for index bar", 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.alias_present(name, index, {"test2": "key"}), ret) + ret.update( + { + "comment": "Successfully replaced alias foo for index bar", + "changes": {"old": {"test": "key"}, "new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.alias_present(name, index, {"test2": "key"}), ret + ) - ret.update({'comment': "Successfully created alias foo for index bar", 'changes': {'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.alias_present(name, index, {"test2": "key"}), ret) + ret.update( + { + "comment": "Successfully created alias foo for index bar", + "changes": {"new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.alias_present(name, index, {"test2": "key"}), ret + ) - ret.update({'comment': 'Cannot create alias foo for index bar, False', 'result': False}) - self.assertDictEqual(elasticsearch.alias_present(name, index, {"test2": "key"}), ret) + ret.update( + { + "comment": "Cannot create alias foo for index bar, False", + "result": False, + } + ) + self.assertDictEqual( + elasticsearch.alias_present(name, index, {"test2": "key"}), ret + ) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Alias foo for index bar does not exist and will be created", 'result': None, 'changes': {'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.alias_present(name, index, {"test2": "key"}), ret) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Alias foo for index bar does not exist and will be created", + "result": None, + "changes": {"new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.alias_present(name, index, {"test2": "key"}), ret + ) - ret.update({'comment': "Alias foo for index bar exists with wrong configuration and will be overridden", 'result': None, 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.alias_present(name, index, {"test2": "key"}), ret) + ret.update( + { + "comment": "Alias foo for index bar exists with wrong configuration and will be overridden", + "result": None, + "changes": {"old": {"test": "key"}, "new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.alias_present(name, index, {"test2": "key"}), ret + ) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.alias_present(name, index), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.alias_present(name, index), ret) # 'index_template_absent' function tests: 1 def test_index_template_absent(self): - ''' + """ Test to manage a elasticsearch index template. - ''' - name = 'foo' + """ + name = "foo" index_template = {name: {"test": "key"}} - ret = {'name': name, - 'result': True, - 'comment': 'Index template foo is already absent', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Index template foo is already absent", + "changes": {}, + } - mock_get = MagicMock(side_effect=[None, {"bar": {}}, index_template, index_template, index_template, CommandExecutionError, index_template]) + mock_get = MagicMock( + side_effect=[ + None, + {"bar": {}}, + index_template, + index_template, + index_template, + CommandExecutionError, + index_template, + ] + ) mock_delete = MagicMock(side_effect=[True, False, CommandExecutionError]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.index_template_get': mock_get, - 'elasticsearch.index_template_delete': mock_delete}): + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.index_template_get": mock_get, + "elasticsearch.index_template_delete": mock_delete, + }, + ): self.assertDictEqual(elasticsearch.index_template_absent(name), ret) self.assertDictEqual(elasticsearch.index_template_absent(name), ret) - ret.update({'comment': 'Successfully removed index template foo', 'changes': {"old": {"test": "key"}}}) + ret.update( + { + "comment": "Successfully removed index template foo", + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.index_template_absent(name), ret) - ret.update({'comment': 'Failed to remove index template foo for unknown reasons', 'result': False, 'changes': {}}) + ret.update( + { + "comment": "Failed to remove index template foo for unknown reasons", + "result": False, + "changes": {}, + } + ) self.assertDictEqual(elasticsearch.index_template_absent(name), ret) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Index template foo will be removed", 'result': None, 'changes': {"old": {"test": "key"}}}) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Index template foo will be removed", + "result": None, + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.index_template_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_template_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_template_absent(name), ret) # 'index_template_present' function tests: 1 def test_index_template_present(self): - ''' + """ Test to manage a elasticsearch index template. - ''' - name = 'foo' + """ + name = "foo" index_template = {name: {"test": "key"}} - ret = {'name': name, - 'result': True, - 'comment': 'Index template foo is already present', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Index template foo is already present", + "changes": {}, + } - mock_exists = MagicMock(side_effect=[True, False, False, False, CommandExecutionError, False, False]) + mock_exists = MagicMock( + side_effect=[True, False, False, False, CommandExecutionError, False, False] + ) mock_create = MagicMock(side_effect=[True, False, CommandExecutionError, True]) mock_get = MagicMock(side_effect=[index_template, CommandExecutionError]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.index_template_get': mock_get, - 'elasticsearch.index_template_create': mock_create, - 'elasticsearch.index_template_exists': mock_exists}): - self.assertDictEqual(elasticsearch.index_template_present(name, {"test2": "key"}), ret) + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.index_template_get": mock_get, + "elasticsearch.index_template_create": mock_create, + "elasticsearch.index_template_exists": mock_exists, + }, + ): + self.assertDictEqual( + elasticsearch.index_template_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': "Successfully created index template foo", 'changes': {'new': {"test": "key"}}}) - self.assertDictEqual(elasticsearch.index_template_present(name, {"test2": "key"}), ret) + ret.update( + { + "comment": "Successfully created index template foo", + "changes": {"new": {"test": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.index_template_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': 'Cannot create index template foo, False', 'result': False, 'changes': {}}) - self.assertDictEqual(elasticsearch.index_template_present(name, {"test2": "key"}), ret) + ret.update( + { + "comment": "Cannot create index template foo, False", + "result": False, + "changes": {}, + } + ) + self.assertDictEqual( + elasticsearch.index_template_present(name, {"test2": "key"}), ret + ) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Index template foo does not exist and will be created", 'result': None, 'changes': {'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.index_template_present(name, {"test2": "key"}), ret) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Index template foo does not exist and will be created", + "result": None, + "changes": {"new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.index_template_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_template_present(name, {}), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_template_present(name, {}), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.index_template_present(name, {}), ret) def test_index_template_present_check_definition(self): - ''' + """ Test to manage a elasticsearch index template. with check_definition set - ''' - name = 'foo' + """ + name = "foo" - index_template = {name: {"test2": "key", - "aliases": {}, - "mappings": {}, - "settings": {}}} + index_template = { + name: {"test2": "key", "aliases": {}, "mappings": {}, "settings": {}} + } - expected = {'name': name, - 'result': True, - 'comment': 'Index template foo is already present and up to date', - 'changes': {}} + expected = { + "name": name, + "result": True, + "comment": "Index template foo is already present and up to date", + "changes": {}, + } mock_exists = MagicMock(side_effect=[True]) mock_create = MagicMock(side_effect=[True]) mock_get = MagicMock(side_effect=[index_template]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.index_template_get': mock_get, - 'elasticsearch.index_template_create': mock_create, - 'elasticsearch.index_template_exists': mock_exists}): + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.index_template_get": mock_get, + "elasticsearch.index_template_create": mock_create, + "elasticsearch.index_template_exists": mock_exists, + }, + ): - ret = elasticsearch.index_template_present(name, - {"test2": "key", - "aliases": {}}, - check_definition=True) + ret = elasticsearch.index_template_present( + name, {"test2": "key", "aliases": {}}, check_definition=True + ) self.assertDictEqual(expected, ret) def test_index_template_present_check_definition_alias_not_empty(self): - ''' + """ Test to manage a elasticsearch index template. with check_definition set and alias is not empty - ''' - name = 'foo' + """ + name = "foo" - index_template = {name: {"test2": "key", - "aliases": {}, - "mappings": {}, - "settings": {}}} + index_template = { + name: {"test2": "key", "aliases": {}, "mappings": {}, "settings": {}} + } - expected = {'name': name, - 'result': True, - 'comment': 'Successfully updated index template foo', - 'changes': {'new': {'aliases': {'alias1': {}}}, 'old': {'aliases': {}}}} + expected = { + "name": name, + "result": True, + "comment": "Successfully updated index template foo", + "changes": {"new": {"aliases": {"alias1": {}}}, "old": {"aliases": {}}}, + } mock_exists = MagicMock(side_effect=[True]) mock_create = MagicMock(side_effect=[True]) mock_get = MagicMock(side_effect=[index_template]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.index_template_get': mock_get, - 'elasticsearch.index_template_create': mock_create, - 'elasticsearch.index_template_exists': mock_exists}): + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.index_template_get": mock_get, + "elasticsearch.index_template_create": mock_create, + "elasticsearch.index_template_exists": mock_exists, + }, + ): - ret = elasticsearch.index_template_present(name, - {"test2": "key", - "aliases": {'alias1': {}}}, - check_definition=True) + ret = elasticsearch.index_template_present( + name, {"test2": "key", "aliases": {"alias1": {}}}, check_definition=True + ) self.assertDictEqual(expected, ret) # 'pipeline_absent' function tests: 1 def test_pipeline_absent(self): - ''' + """ Test to manage a elasticsearch pipeline. - ''' - name = 'foo' + """ + name = "foo" pipeline = {name: {"test": "key"}} - ret = {'name': name, - 'result': True, - 'comment': 'Pipeline foo is already absent', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Pipeline foo is already absent", + "changes": {}, + } - mock_get = MagicMock(side_effect=[None, {"foo2": {}}, pipeline, pipeline, pipeline, CommandExecutionError, pipeline]) + mock_get = MagicMock( + side_effect=[ + None, + {"foo2": {}}, + pipeline, + pipeline, + pipeline, + CommandExecutionError, + pipeline, + ] + ) mock_delete = MagicMock(side_effect=[True, False, CommandExecutionError]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.pipeline_get': mock_get, - 'elasticsearch.pipeline_delete': mock_delete}): + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.pipeline_get": mock_get, + "elasticsearch.pipeline_delete": mock_delete, + }, + ): self.assertDictEqual(elasticsearch.pipeline_absent(name), ret) self.assertDictEqual(elasticsearch.pipeline_absent(name), ret) - ret.update({'comment': 'Successfully removed pipeline foo', 'changes': {"old": {"test": "key"}}}) + ret.update( + { + "comment": "Successfully removed pipeline foo", + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.pipeline_absent(name), ret) - ret.update({'comment': 'Failed to remove pipeline foo for unknown reasons', 'result': False, 'changes': {}}) + ret.update( + { + "comment": "Failed to remove pipeline foo for unknown reasons", + "result": False, + "changes": {}, + } + ) self.assertDictEqual(elasticsearch.pipeline_absent(name), ret) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Pipeline foo will be removed", 'result': None, 'changes': {"old": {"test": "key"}}}) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Pipeline foo will be removed", + "result": None, + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.pipeline_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.pipeline_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.pipeline_absent(name), ret) # 'pipeline_present' function tests: 1 def test_pipeline_present(self): - ''' + """ Test to manage a elasticsearch pipeline. - ''' - name = 'foo' + """ + name = "foo" pipeline = {name: {"test": "key"}} - ret = {'name': name, - 'result': True, - 'comment': 'Pipeline foo is already present', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Pipeline foo is already present", + "changes": {}, + } - mock_get = MagicMock(side_effect=[pipeline, pipeline, None, None, None, pipeline, CommandExecutionError, None]) + mock_get = MagicMock( + side_effect=[ + pipeline, + pipeline, + None, + None, + None, + pipeline, + CommandExecutionError, + None, + ] + ) mock_create = MagicMock(side_effect=[True, True, False, CommandExecutionError]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.pipeline_get': mock_get, - 'elasticsearch.pipeline_create': mock_create}): - self.assertDictEqual(elasticsearch.pipeline_present(name, {"test": "key"}), ret) + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.pipeline_get": mock_get, + "elasticsearch.pipeline_create": mock_create, + }, + ): + self.assertDictEqual( + elasticsearch.pipeline_present(name, {"test": "key"}), ret + ) - ret.update({'comment': "Successfully replaced pipeline foo", 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.pipeline_present(name, {"test2": "key"}), ret) + ret.update( + { + "comment": "Successfully replaced pipeline foo", + "changes": {"old": {"test": "key"}, "new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.pipeline_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': "Successfully created pipeline foo", 'changes': {'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.pipeline_present(name, {"test2": "key"}), ret) + ret.update( + { + "comment": "Successfully created pipeline foo", + "changes": {"new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.pipeline_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': 'Cannot create pipeline foo, False', 'result': False}) - self.assertDictEqual(elasticsearch.pipeline_present(name, {"test2": "key"}), ret) + ret.update( + {"comment": "Cannot create pipeline foo, False", "result": False} + ) + self.assertDictEqual( + elasticsearch.pipeline_present(name, {"test2": "key"}), ret + ) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Pipeline foo does not exist and will be created", 'result': None, 'changes': {'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.pipeline_present(name, {"test2": "key"}), ret) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Pipeline foo does not exist and will be created", + "result": None, + "changes": {"new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.pipeline_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': "Pipeline foo exists with wrong configuration and will be overridden", 'result': None, 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.pipeline_present(name, {"test2": "key"}), ret) + ret.update( + { + "comment": "Pipeline foo exists with wrong configuration and will be overridden", + "result": None, + "changes": {"old": {"test": "key"}, "new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.pipeline_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.pipeline_present(name, {}), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.pipeline_present(name, {}), ret) # 'search_template_absent' function tests: 1 def test_search_template_absent(self): - ''' + """ Test to manage a elasticsearch search template. - ''' - name = 'foo' + """ + name = "foo" template = {"template": '{"test": "key"}'} - ret = {'name': name, - 'result': True, - 'comment': 'Search template foo is already absent', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Search template foo is already absent", + "changes": {}, + } - mock_get = MagicMock(side_effect=[None, template, template, template, CommandExecutionError, template]) + mock_get = MagicMock( + side_effect=[ + None, + template, + template, + template, + CommandExecutionError, + template, + ] + ) mock_delete = MagicMock(side_effect=[True, False, CommandExecutionError]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.search_template_get': mock_get, - 'elasticsearch.search_template_delete': mock_delete}): + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.search_template_get": mock_get, + "elasticsearch.search_template_delete": mock_delete, + }, + ): self.assertDictEqual(elasticsearch.search_template_absent(name), ret) - ret.update({'comment': 'Successfully removed search template foo', 'changes': {"old": {"test": "key"}}}) + ret.update( + { + "comment": "Successfully removed search template foo", + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.search_template_absent(name), ret) - ret.update({'comment': 'Failed to remove search template foo for unknown reasons', 'result': False, 'changes': {}}) + ret.update( + { + "comment": "Failed to remove search template foo for unknown reasons", + "result": False, + "changes": {}, + } + ) self.assertDictEqual(elasticsearch.search_template_absent(name), ret) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Search template foo will be removed", 'result': None, 'changes': {"old": {"test": "key"}}}) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Search template foo will be removed", + "result": None, + "changes": {"old": {"test": "key"}}, + } + ) self.assertDictEqual(elasticsearch.search_template_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.search_template_absent(name), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.search_template_absent(name), ret) # 'pipeline_present' function tests: 1 def test_search_template_present(self): - ''' + """ Test to manage a elasticsearch search template. - ''' - name = 'foo' + """ + name = "foo" template = {"template": '{"test": "key"}'} - ret = {'name': name, - 'result': True, - 'comment': 'Search template foo is already present', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Search template foo is already present", + "changes": {}, + } - mock_get = MagicMock(side_effect=[template, template, None, None, None, template, CommandExecutionError, None]) + mock_get = MagicMock( + side_effect=[ + template, + template, + None, + None, + None, + template, + CommandExecutionError, + None, + ] + ) mock_create = MagicMock(side_effect=[True, True, False, CommandExecutionError]) - with patch.dict(elasticsearch.__salt__, {'elasticsearch.search_template_get': mock_get, - 'elasticsearch.search_template_create': mock_create}): - self.assertDictEqual(elasticsearch.search_template_present(name, {"test": "key"}), ret) + with patch.dict( + elasticsearch.__salt__, + { + "elasticsearch.search_template_get": mock_get, + "elasticsearch.search_template_create": mock_create, + }, + ): + self.assertDictEqual( + elasticsearch.search_template_present(name, {"test": "key"}), ret + ) - ret.update({'comment': "Successfully replaced search template foo", 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.search_template_present(name, {"test2": "key"}), ret) + ret.update( + { + "comment": "Successfully replaced search template foo", + "changes": {"old": {"test": "key"}, "new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.search_template_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': "Successfully created search template foo", 'changes': {'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.search_template_present(name, {"test2": "key"}), ret) + ret.update( + { + "comment": "Successfully created search template foo", + "changes": {"new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.search_template_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': 'Cannot create search template foo, False', 'result': False}) - self.assertDictEqual(elasticsearch.search_template_present(name, {"test2": "key"}), ret) + ret.update( + {"comment": "Cannot create search template foo, False", "result": False} + ) + self.assertDictEqual( + elasticsearch.search_template_present(name, {"test2": "key"}), ret + ) - with patch.dict(elasticsearch.__opts__, {'test': True}): - ret.update({'comment': "Search template foo does not exist and will be created", 'result': None, 'changes': {'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.search_template_present(name, {"test2": "key"}), ret) + with patch.dict(elasticsearch.__opts__, {"test": True}): + ret.update( + { + "comment": "Search template foo does not exist and will be created", + "result": None, + "changes": {"new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.search_template_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': "Search template foo exists with wrong configuration and will be overridden", 'result': None, 'changes': {'old': {"test": "key"}, 'new': {"test2": "key"}}}) - self.assertDictEqual(elasticsearch.search_template_present(name, {"test2": "key"}), ret) + ret.update( + { + "comment": "Search template foo exists with wrong configuration and will be overridden", + "result": None, + "changes": {"old": {"test": "key"}, "new": {"test2": "key"}}, + } + ) + self.assertDictEqual( + elasticsearch.search_template_present(name, {"test2": "key"}), ret + ) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.search_template_present(name, {}), ret) - ret.update({'comment': '', 'result': False, 'changes': {}}) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(elasticsearch.search_template_present(name, {}), ret) diff --git a/tests/unit/states/test_environ.py b/tests/unit/states/test_environ.py index 994bb7eac53..f76a4077f73 100644 --- a/tests/unit/states/test_environ.py +++ b/tests/unit/states/test_environ.py @@ -2,37 +2,36 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os +import salt.modules.environ as envmodule +import salt.modules.reg + +# Import salt libs +import salt.states.environ as envstate +import salt.utils.platform + # Import Salt Testing libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch -) -# Import salt libs -import salt.states.environ as envstate -import salt.modules.environ as envmodule -import salt.modules.reg -import salt.utils.platform class TestEnvironState(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): loader_globals = { - '__env__': 'base', - '__opts__': {'test': False}, - '__salt__': { - 'environ.setenv': envmodule.setenv, - 'reg.read_value': salt.modules.reg.read_value, - } + "__env__": "base", + "__opts__": {"test": False}, + "__salt__": { + "environ.setenv": envmodule.setenv, + "reg.read_value": salt.modules.reg.read_value, + }, } return {envstate: loader_globals, envmodule: loader_globals} def setUp(self): - patcher = patch.dict(os.environ, {'INITIAL': 'initial'}, clear=True) + patcher = patch.dict(os.environ, {"INITIAL": "initial"}, clear=True) patcher.start() def reset_environ(patcher): @@ -41,112 +40,124 @@ class TestEnvironState(TestCase, LoaderModuleMockMixin): self.addCleanup(reset_environ, patcher) def test_setenv(self): - ''' + """ test that a subsequent calls of setenv changes nothing - ''' - ret = envstate.setenv('test', 'value') - self.assertEqual(ret['changes'], {'test': 'value'}) + """ + ret = envstate.setenv("test", "value") + self.assertEqual(ret["changes"], {"test": "value"}) - ret = envstate.setenv('test', 'other') - self.assertEqual(ret['changes'], {'test': 'other'}) + ret = envstate.setenv("test", "other") + self.assertEqual(ret["changes"], {"test": "other"}) # once again with the same value - ret = envstate.setenv('test', 'other') - self.assertEqual(ret['changes'], {}) + ret = envstate.setenv("test", "other") + self.assertEqual(ret["changes"], {}) - @skipIf(not salt.utils.platform.is_windows(), 'Windows only') + @skipIf(not salt.utils.platform.is_windows(), "Windows only") def test_setenv_permanent(self): - ''' + """ test that we can set perminent environment variables (requires pywin32) - ''' - with patch.dict(envmodule.__salt__, {'reg.set_value': MagicMock(), 'reg.delete_value': MagicMock()}): - ret = envstate.setenv('test', 'value', permanent=True) - self.assertEqual(ret['changes'], {'test': 'value'}) - envmodule.__salt__['reg.set_value'].assert_called_with("HKCU", "Environment", 'test', 'value') + """ + with patch.dict( + envmodule.__salt__, + {"reg.set_value": MagicMock(), "reg.delete_value": MagicMock()}, + ): + ret = envstate.setenv("test", "value", permanent=True) + self.assertEqual(ret["changes"], {"test": "value"}) + envmodule.__salt__["reg.set_value"].assert_called_with( + "HKCU", "Environment", "test", "value" + ) - ret = envstate.setenv('test', False, false_unsets=True, permanent=True) - self.assertEqual(ret['changes'], {'test': None}) - envmodule.__salt__['reg.delete_value'].assert_called_with("HKCU", "Environment", 'test') + ret = envstate.setenv("test", False, false_unsets=True, permanent=True) + self.assertEqual(ret["changes"], {"test": None}) + envmodule.__salt__["reg.delete_value"].assert_called_with( + "HKCU", "Environment", "test" + ) def test_setenv_dict(self): - ''' + """ test that setenv can be invoked with dict - ''' - ret = envstate.setenv('notimportant', {'test': 'value'}) - self.assertEqual(ret['changes'], {'test': 'value'}) + """ + ret = envstate.setenv("notimportant", {"test": "value"}) + self.assertEqual(ret["changes"], {"test": "value"}) def test_setenv_int(self): - ''' + """ test that setenv can not be invoked with int (actually it's anything other than strings and dict) - ''' - ret = envstate.setenv('test', 1) - self.assertEqual(ret['result'], False) + """ + ret = envstate.setenv("test", 1) + self.assertEqual(ret["result"], False) def test_setenv_unset(self): - ''' + """ test that ``false_unsets`` option removes variable from environment - ''' - ret = envstate.setenv('test', 'value') - self.assertEqual(ret['changes'], {'test': 'value'}) + """ + ret = envstate.setenv("test", "value") + self.assertEqual(ret["changes"], {"test": "value"}) - ret = envstate.setenv('notimportant', {'test': False}, false_unsets=True) - self.assertEqual(ret['changes'], {'test': None}) - self.assertEqual(envstate.os.environ, {'INITIAL': 'initial'}) + ret = envstate.setenv("notimportant", {"test": False}, false_unsets=True) + self.assertEqual(ret["changes"], {"test": None}) + self.assertEqual(envstate.os.environ, {"INITIAL": "initial"}) def test_setenv_clearall(self): - ''' + """ test that ``clear_all`` option sets other values to '' - ''' - ret = envstate.setenv('test', 'value', clear_all=True) - self.assertEqual(ret['changes'], {'test': 'value', 'INITIAL': ''}) + """ + ret = envstate.setenv("test", "value", clear_all=True) + self.assertEqual(ret["changes"], {"test": "value", "INITIAL": ""}) if salt.utils.platform.is_windows(): - self.assertEqual(envstate.os.environ, {'TEST': 'value', 'INITIAL': ''}) + self.assertEqual(envstate.os.environ, {"TEST": "value", "INITIAL": ""}) else: - self.assertEqual(envstate.os.environ, {'test': 'value', 'INITIAL': ''}) + self.assertEqual(envstate.os.environ, {"test": "value", "INITIAL": ""}) def test_setenv_clearall_with_unset(self): - ''' + """ test that ``clear_all`` option combined with ``false_unsets`` unsets other values from environment - ''' - ret = envstate.setenv('test', 'value', false_unsets=True, clear_all=True) - self.assertEqual(ret['changes'], {'test': 'value', 'INITIAL': None}) + """ + ret = envstate.setenv("test", "value", false_unsets=True, clear_all=True) + self.assertEqual(ret["changes"], {"test": "value", "INITIAL": None}) if salt.utils.platform.is_windows(): - self.assertEqual(envstate.os.environ, {'TEST': 'value'}) + self.assertEqual(envstate.os.environ, {"TEST": "value"}) else: - self.assertEqual(envstate.os.environ, {'test': 'value'}) + self.assertEqual(envstate.os.environ, {"test": "value"}) def test_setenv_unset_multi(self): - ''' + """ test basically same things that above tests but with multiple values passed - ''' - ret = envstate.setenv('notimportant', {'foo': 'bar'}) - self.assertEqual(ret['changes'], {'foo': 'bar'}) + """ + ret = envstate.setenv("notimportant", {"foo": "bar"}) + self.assertEqual(ret["changes"], {"foo": "bar"}) - with patch.dict(envstate.__salt__, {'reg.read_value': MagicMock()}): + with patch.dict(envstate.__salt__, {"reg.read_value": MagicMock()}): ret = envstate.setenv( - 'notimportant', {'test': False, 'foo': 'baz'}, false_unsets=True) - self.assertEqual(ret['changes'], {'test': None, 'foo': 'baz'}) + "notimportant", {"test": False, "foo": "baz"}, false_unsets=True + ) + self.assertEqual(ret["changes"], {"test": None, "foo": "baz"}) if salt.utils.platform.is_windows(): - self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'FOO': 'baz'}) + self.assertEqual(envstate.os.environ, {"INITIAL": "initial", "FOO": "baz"}) else: - self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'foo': 'baz'}) + self.assertEqual(envstate.os.environ, {"INITIAL": "initial", "foo": "baz"}) - with patch.dict(envstate.__salt__, {'reg.read_value': MagicMock()}): - ret = envstate.setenv('notimportant', {'test': False, 'foo': 'bax'}) - self.assertEqual(ret['changes'], {'test': '', 'foo': 'bax'}) + with patch.dict(envstate.__salt__, {"reg.read_value": MagicMock()}): + ret = envstate.setenv("notimportant", {"test": False, "foo": "bax"}) + self.assertEqual(ret["changes"], {"test": "", "foo": "bax"}) if salt.utils.platform.is_windows(): - self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'FOO': 'bax', 'TEST': ''}) + self.assertEqual( + envstate.os.environ, {"INITIAL": "initial", "FOO": "bax", "TEST": ""} + ) else: - self.assertEqual(envstate.os.environ, {'INITIAL': 'initial', 'foo': 'bax', 'test': ''}) + self.assertEqual( + envstate.os.environ, {"INITIAL": "initial", "foo": "bax", "test": ""} + ) def test_setenv_test_mode(self): - ''' + """ test that imitating action returns good values - ''' - with patch.dict(envstate.__opts__, {'test': True}): - ret = envstate.setenv('test', 'value') - self.assertEqual(ret['changes'], {'test': 'value'}) - ret = envstate.setenv('INITIAL', 'initial') - self.assertEqual(ret['changes'], {}) + """ + with patch.dict(envstate.__opts__, {"test": True}): + ret = envstate.setenv("test", "value") + self.assertEqual(ret["changes"], {"test": "value"}) + ret = envstate.setenv("INITIAL", "initial") + self.assertEqual(ret["changes"], {}) diff --git a/tests/unit/states/test_eselect.py b/tests/unit/states/test_eselect.py index 9030727ab32..2791220a1f9 100644 --- a/tests/unit/states/test_eselect.py +++ b/tests/unit/states/test_eselect.py @@ -1,45 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.eselect as eselect +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class EselectTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.eselect - ''' + """ + def setup_loader_modules(self): return {eselect: {}} # 'set_' function tests: 1 def test_set_(self): - ''' + """ Test to verify that the given module is set to the given target - ''' - name = 'myeselect' - target = 'hardened/linux/amd64' + """ + name = "myeselect" + target = "hardened/linux/amd64" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(return_value=target) - with patch.dict(eselect.__salt__, {'eselect.get_current_target': mock}): - comt = ('Target \'{0}\' is already set on \'{1}\' module.' - .format(target, name)) - ret.update({'comment': comt}) + with patch.dict(eselect.__salt__, {"eselect.get_current_target": mock}): + comt = "Target '{0}' is already set on '{1}' module.".format(target, name) + ret.update({"comment": comt}) self.assertDictEqual(eselect.set_(name, target), ret) diff --git a/tests/unit/states/test_esxdatacenter.py b/tests/unit/states/test_esxdatacenter.py index 53348c92e00..02fd3be6ded 100644 --- a/tests/unit/states/test_esxdatacenter.py +++ b/tests/unit/states/test_esxdatacenter.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstanley.com>` Tests for functions in salt.states.esxdatacenter -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals @@ -14,20 +14,15 @@ from salt.exceptions import CommandExecutionError # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class DatacenterConfiguredTestCase(TestCase, LoaderModuleMockMixin): - '''Tests for salt.modules.esxdatacenter.datacenter_configured''' + """Tests for salt.modules.esxdatacenter.datacenter_configured""" def setup_loader_modules(self): - return { - esxdatacenter: { - '__virtual__': MagicMock(return_value='esxdatacenter')}} + return {esxdatacenter: {"__virtual__": MagicMock(return_value="esxdatacenter")}} def setUp(self): self.mock_si = MagicMock() @@ -35,142 +30,190 @@ class DatacenterConfiguredTestCase(TestCase, LoaderModuleMockMixin): patcher = patch.dict( esxdatacenter.__salt__, - {'vsphere.get_proxy_type': MagicMock(), - 'vsphere.get_service_instance_via_proxy': - MagicMock(return_value=self.mock_si), - 'vsphere.list_datacenters_via_proxy': - MagicMock(return_value=[self.mock_dc]), - 'vsphere.disconnect': MagicMock()}) + { + "vsphere.get_proxy_type": MagicMock(), + "vsphere.get_service_instance_via_proxy": MagicMock( + return_value=self.mock_si + ), + "vsphere.list_datacenters_via_proxy": MagicMock( + return_value=[self.mock_dc] + ), + "vsphere.disconnect": MagicMock(), + }, + ) patcher.start() self.addCleanup(patcher.stop) - patcher = patch.dict(esxdatacenter.__opts__, {'test': False}) + patcher = patch.dict(esxdatacenter.__opts__, {"test": False}) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attrname in ('mock_si',): + for attrname in ("mock_si",): try: delattr(self, attrname) except AttributeError: continue def test_dc_name_different_proxy(self): - with patch.dict(esxdatacenter.__salt__, - {'vsphere.get_proxy_type': - MagicMock(return_value='different_proxy')}): - res = esxdatacenter.datacenter_configured('fake_dc') - self.assertDictEqual(res, {'name': 'fake_dc', - 'changes': {}, - 'result': True, - 'comment': 'Datacenter \'fake_dc\' already ' - 'exists. Nothing to be done.'}) + with patch.dict( + esxdatacenter.__salt__, + {"vsphere.get_proxy_type": MagicMock(return_value="different_proxy")}, + ): + res = esxdatacenter.datacenter_configured("fake_dc") + self.assertDictEqual( + res, + { + "name": "fake_dc", + "changes": {}, + "result": True, + "comment": "Datacenter 'fake_dc' already " + "exists. Nothing to be done.", + }, + ) def test_dc_name_esxdatacenter_proxy(self): - with patch.dict(esxdatacenter.__salt__, - {'vsphere.get_proxy_type': - MagicMock(return_value='esxdatacenter'), - 'esxdatacenter.get_details': - MagicMock(return_value={'datacenter': 'proxy_dc'})}): - res = esxdatacenter.datacenter_configured('fake_dc') - self.assertDictEqual(res, {'name': 'fake_dc', - 'changes': {}, - 'result': True, - 'comment': 'Datacenter \'proxy_dc\' ' - 'already exists. Nothing to be done.'}) + with patch.dict( + esxdatacenter.__salt__, + { + "vsphere.get_proxy_type": MagicMock(return_value="esxdatacenter"), + "esxdatacenter.get_details": MagicMock( + return_value={"datacenter": "proxy_dc"} + ), + }, + ): + res = esxdatacenter.datacenter_configured("fake_dc") + self.assertDictEqual( + res, + { + "name": "fake_dc", + "changes": {}, + "result": True, + "comment": "Datacenter 'proxy_dc' " + "already exists. Nothing to be done.", + }, + ) def test_get_service_instance(self): mock_get_service_instance = MagicMock() - with patch.dict(esxdatacenter.__salt__, - {'vsphere.get_service_instance_via_proxy': - mock_get_service_instance}): - esxdatacenter.datacenter_configured('fake_dc') + with patch.dict( + esxdatacenter.__salt__, + {"vsphere.get_service_instance_via_proxy": mock_get_service_instance}, + ): + esxdatacenter.datacenter_configured("fake_dc") mock_get_service_instance.assert_called_once_with() def test_list_datacenters(self): mock_list_datacenters = MagicMock() - with patch.dict(esxdatacenter.__salt__, - {'vsphere.list_datacenters_via_proxy': - mock_list_datacenters}): - esxdatacenter.datacenter_configured('fake_dc') + with patch.dict( + esxdatacenter.__salt__, + {"vsphere.list_datacenters_via_proxy": mock_list_datacenters}, + ): + esxdatacenter.datacenter_configured("fake_dc") mock_list_datacenters.assert_called_once_with( - datacenter_names=['fake_dc'], service_instance=self.mock_si) + datacenter_names=["fake_dc"], service_instance=self.mock_si + ) def test_create_datacenter(self): mock_create_datacenter = MagicMock() - with patch.dict(esxdatacenter.__salt__, - {'vsphere.list_datacenters_via_proxy': - MagicMock(return_value=[]), - 'vsphere.create_datacenter': - mock_create_datacenter}): - res = esxdatacenter.datacenter_configured('fake_dc') - mock_create_datacenter.assert_called_once_with('fake_dc', self.mock_si) - self.assertDictEqual(res, - {'name': 'fake_dc', - 'changes': {'new': {'name': 'fake_dc'}}, - 'result': True, - 'comment': 'Created datacenter \'fake_dc\'.'}) + with patch.dict( + esxdatacenter.__salt__, + { + "vsphere.list_datacenters_via_proxy": MagicMock(return_value=[]), + "vsphere.create_datacenter": mock_create_datacenter, + }, + ): + res = esxdatacenter.datacenter_configured("fake_dc") + mock_create_datacenter.assert_called_once_with("fake_dc", self.mock_si) + self.assertDictEqual( + res, + { + "name": "fake_dc", + "changes": {"new": {"name": "fake_dc"}}, + "result": True, + "comment": "Created datacenter 'fake_dc'.", + }, + ) def test_create_datacenter_test_mode(self): - with patch.dict(esxdatacenter.__opts__, {'test': True}): - with patch.dict(esxdatacenter.__salt__, - {'vsphere.list_datacenters_via_proxy': - MagicMock(return_value=[])}): - res = esxdatacenter.datacenter_configured('fake_dc') - self.assertDictEqual(res, - {'name': 'fake_dc', - 'changes': {'new': {'name': 'fake_dc'}}, - 'result': None, - 'comment': 'State will create ' - 'datacenter \'fake_dc\'.'}) + with patch.dict(esxdatacenter.__opts__, {"test": True}): + with patch.dict( + esxdatacenter.__salt__, + {"vsphere.list_datacenters_via_proxy": MagicMock(return_value=[])}, + ): + res = esxdatacenter.datacenter_configured("fake_dc") + self.assertDictEqual( + res, + { + "name": "fake_dc", + "changes": {"new": {"name": "fake_dc"}}, + "result": None, + "comment": "State will create " "datacenter 'fake_dc'.", + }, + ) def test_nothing_to_be_done_test_mode(self): - with patch.dict(esxdatacenter.__opts__, {'test': True}): - with patch.dict(esxdatacenter.__salt__, - {'vsphere.get_proxy_type': - MagicMock(return_value='different_proxy')}): - res = esxdatacenter.datacenter_configured('fake_dc') - self.assertDictEqual(res, {'name': 'fake_dc', - 'changes': {}, - 'result': True, - 'comment': 'Datacenter \'fake_dc\' already ' - 'exists. Nothing to be done.'}) + with patch.dict(esxdatacenter.__opts__, {"test": True}): + with patch.dict( + esxdatacenter.__salt__, + {"vsphere.get_proxy_type": MagicMock(return_value="different_proxy")}, + ): + res = esxdatacenter.datacenter_configured("fake_dc") + self.assertDictEqual( + res, + { + "name": "fake_dc", + "changes": {}, + "result": True, + "comment": "Datacenter 'fake_dc' already " + "exists. Nothing to be done.", + }, + ) def test_state_get_service_instance_raise_command_execution_error(self): mock_disconnect = MagicMock() - with patch.dict(esxdatacenter.__salt__, - {'vsphere.disconnect': mock_disconnect, - 'vsphere.get_service_instance_via_proxy': - MagicMock( - side_effect=CommandExecutionError('Error'))}): - res = esxdatacenter.datacenter_configured('fake_dc') + with patch.dict( + esxdatacenter.__salt__, + { + "vsphere.disconnect": mock_disconnect, + "vsphere.get_service_instance_via_proxy": MagicMock( + side_effect=CommandExecutionError("Error") + ), + }, + ): + res = esxdatacenter.datacenter_configured("fake_dc") self.assertEqual(mock_disconnect.call_count, 0) - self.assertDictEqual(res, {'name': 'fake_dc', - 'changes': {}, - 'result': False, - 'comment': 'Error'}) + self.assertDictEqual( + res, {"name": "fake_dc", "changes": {}, "result": False, "comment": "Error"} + ) def test_state_raise_command_execution_error_after_si(self): mock_disconnect = MagicMock() - with patch.dict(esxdatacenter.__salt__, - {'vsphere.disconnect': mock_disconnect, - 'vsphere.list_datacenters_via_proxy': - MagicMock( - side_effect=CommandExecutionError('Error'))}): - res = esxdatacenter.datacenter_configured('fake_dc') + with patch.dict( + esxdatacenter.__salt__, + { + "vsphere.disconnect": mock_disconnect, + "vsphere.list_datacenters_via_proxy": MagicMock( + side_effect=CommandExecutionError("Error") + ), + }, + ): + res = esxdatacenter.datacenter_configured("fake_dc") mock_disconnect.assert_called_once_with(self.mock_si) - self.assertDictEqual(res, {'name': 'fake_dc', - 'changes': {}, - 'result': False, - 'comment': 'Error'}) + self.assertDictEqual( + res, {"name": "fake_dc", "changes": {}, "result": False, "comment": "Error"} + ) def test_state_raise_command_execution_error_test_mode(self): - with patch.dict(esxdatacenter.__opts__, {'test': True}): - with patch.dict(esxdatacenter.__salt__, - {'vsphere.list_datacenters_via_proxy': - MagicMock( - side_effect=CommandExecutionError('Error'))}): - res = esxdatacenter.datacenter_configured('fake_dc') - self.assertDictEqual(res, {'name': 'fake_dc', - 'changes': {}, - 'result': None, - 'comment': 'Error'}) + with patch.dict(esxdatacenter.__opts__, {"test": True}): + with patch.dict( + esxdatacenter.__salt__, + { + "vsphere.list_datacenters_via_proxy": MagicMock( + side_effect=CommandExecutionError("Error") + ) + }, + ): + res = esxdatacenter.datacenter_configured("fake_dc") + self.assertDictEqual( + res, {"name": "fake_dc", "changes": {}, "result": None, "comment": "Error"} + ) diff --git a/tests/unit/states/test_event.py b/tests/unit/states/test_event.py index cb0878df258..5256c616230 100644 --- a/tests/unit/states/test_event.py +++ b/tests/unit/states/test_event.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,50 +10,51 @@ import salt.states.event as event # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class EventTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the event state - ''' + """ + def setup_loader_modules(self): return {event: {}} def test_send(self): - ''' + """ Test to send an event to the Salt Master - ''' - with patch.dict(event.__opts__, {'test': True}): - self.assertDictEqual(event.send("salt"), - {'changes': {'data': None, 'tag': 'salt'}, - 'comment': 'Event would have been fired', - 'name': 'salt', - 'result': None - } - ) + """ + with patch.dict(event.__opts__, {"test": True}): + self.assertDictEqual( + event.send("salt"), + { + "changes": {"data": None, "tag": "salt"}, + "comment": "Event would have been fired", + "name": "salt", + "result": None, + }, + ) - with patch.dict(event.__opts__, {'test': False}): + with patch.dict(event.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.dict(event.__salt__, {'event.send': mock}): - self.assertDictEqual(event.send("salt"), - {'changes': {'data': None, 'tag': 'salt'}, - 'comment': 'Event fired', - 'name': 'salt', - 'result': True - }) + with patch.dict(event.__salt__, {"event.send": mock}): + self.assertDictEqual( + event.send("salt"), + { + "changes": {"data": None, "tag": "salt"}, + "comment": "Event fired", + "name": "salt", + "result": True, + }, + ) def test_wait(self): - ''' + """ Test to fire an event on the Salt master - ''' - self.assertDictEqual(event.wait("salt"), - {'changes': {}, - 'comment': '', - 'name': 'salt', - 'result': True} - ) + """ + self.assertDictEqual( + event.wait("salt"), + {"changes": {}, "comment": "", "name": "salt", "result": True}, + ) diff --git a/tests/unit/states/test_file.py b/tests/unit/states/test_file.py index 2ea92a5c276..a6ee01221e6 100644 --- a/tests/unit/states/test_file.py +++ b/tests/unit/states/test_file.py @@ -2,69 +2,66 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from datetime import datetime + import os import pprint import shutil +from datetime import datetime -try: - from dateutil.relativedelta import relativedelta - HAS_DATEUTIL = True -except ImportError: - HAS_DATEUTIL = False - -NO_DATEUTIL_REASON = 'python-dateutil is not installed' - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.helpers import destructiveTest -from tests.support.mock import ( - Mock, - MagicMock, - call, - mock_open, - patch) -from tests.support.runtests import RUNTIME_VARS +import salt.modules.file as filemod +import salt.serializers.json as jsonserializer +import salt.serializers.python as pythonserializer +import salt.serializers.yaml as yamlserializer +import salt.states.file as filestate # Import salt libs import salt.utils.files import salt.utils.json import salt.utils.platform +import salt.utils.win_functions import salt.utils.yaml -import salt.modules.file as filemod -import salt.states.file as filestate -import salt.serializers.yaml as yamlserializer -import salt.serializers.json as jsonserializer -import salt.serializers.python as pythonserializer from salt.exceptions import CommandExecutionError from salt.ext.six.moves import range -import salt.utils.win_functions +from tests.support.helpers import destructiveTest + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, call, mock_open, patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + +try: + from dateutil.relativedelta import relativedelta + + HAS_DATEUTIL = True +except ImportError: + HAS_DATEUTIL = False + +NO_DATEUTIL_REASON = "python-dateutil is not installed" class TestFileState(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { filestate: { - '__env__': 'base', - '__salt__': {'file.manage_file': False}, - '__serializers__': { - 'yaml.serialize': yamlserializer.serialize, - 'python.serialize': pythonserializer.serialize, - 'json.serialize': jsonserializer.serialize + "__env__": "base", + "__salt__": {"file.manage_file": False}, + "__serializers__": { + "yaml.serialize": yamlserializer.serialize, + "python.serialize": pythonserializer.serialize, + "json.serialize": jsonserializer.serialize, }, - '__opts__': {'test': False, 'cachedir': ''}, - '__instance_id__': '', - '__low__': {}, - '__utils__': {}, + "__opts__": {"test": False, "cachedir": ""}, + "__instance_id__": "", + "__low__": {}, + "__utils__": {}, } } def tearDown(self): - remove_dir = '/tmp/etc' + remove_dir = "/tmp/etc" if salt.utils.platform.is_windows(): - remove_dir = 'c:\\tmp\\etc' + remove_dir = "c:\\tmp\\etc" try: salt.utils.files.rm_rf(remove_dir) except OSError: @@ -73,73 +70,76 @@ class TestFileState(TestCase, LoaderModuleMockMixin): def test_serialize(self): def returner(contents, *args, **kwargs): returner.returned = contents + returner.returned = None - with patch.dict(filestate.__salt__, {'file.manage_file': returner}): + with patch.dict(filestate.__salt__, {"file.manage_file": returner}): - dataset = { - "foo": True, - "bar": 42, - "baz": [1, 2, 3], - "qux": 2.0 - } + dataset = {"foo": True, "bar": 42, "baz": [1, 2, 3], "qux": 2.0} - filestate.serialize('/tmp', dataset) + filestate.serialize("/tmp", dataset) self.assertEqual(salt.utils.yaml.safe_load(returner.returned), dataset) - filestate.serialize('/tmp', dataset, formatter="yaml") + filestate.serialize("/tmp", dataset, formatter="yaml") self.assertEqual(salt.utils.yaml.safe_load(returner.returned), dataset) - filestate.serialize('/tmp', dataset, formatter="json") + filestate.serialize("/tmp", dataset, formatter="json") self.assertEqual(salt.utils.json.loads(returner.returned), dataset) - filestate.serialize('/tmp', dataset, formatter="python") - self.assertEqual(returner.returned, pprint.pformat(dataset) + '\n') + filestate.serialize("/tmp", dataset, formatter="python") + self.assertEqual(returner.returned, pprint.pformat(dataset) + "\n") - mock_serializer = Mock(return_value='') - with patch.dict(filestate.__serializers__, - {'json.serialize': mock_serializer}): + mock_serializer = Mock(return_value="") + with patch.dict( + filestate.__serializers__, {"json.serialize": mock_serializer} + ): filestate.serialize( - '/tmp', - dataset, - formatter='json', - serializer_opts=[{'indent': 8}]) + "/tmp", dataset, formatter="json", serializer_opts=[{"indent": 8}] + ) mock_serializer.assert_called_with( - dataset, - indent=8, - separators=(',', ': '), - sort_keys=True) + dataset, indent=8, separators=(",", ": "), sort_keys=True + ) def test_contents_and_contents_pillar(self): def returner(contents, *args, **kwargs): returner.returned = contents + returner.returned = None manage_mode_mock = MagicMock() - with patch.dict(filestate.__salt__, {'file.manage_file': returner, - 'config.manage_mode': manage_mode_mock}): + with patch.dict( + filestate.__salt__, + {"file.manage_file": returner, "config.manage_mode": manage_mode_mock}, + ): - ret = filestate.managed('/tmp/foo', contents='hi', contents_pillar='foo:bar') - self.assertEqual(False, ret['result']) + ret = filestate.managed( + "/tmp/foo", contents="hi", contents_pillar="foo:bar" + ) + self.assertEqual(False, ret["result"]) def test_contents_pillar_doesnt_add_more_newlines(self): # make sure the newline - pillar_value = 'i am the pillar value{0}'.format(os.linesep) + pillar_value = "i am the pillar value{0}".format(os.linesep) self.run_contents_pillar(pillar_value, expected=pillar_value) def run_contents_pillar(self, pillar_value, expected): returner = MagicMock(return_value=None) - path = '/tmp/foo' - pillar_path = 'foo:bar' + path = "/tmp/foo" + pillar_path = "foo:bar" # the values don't matter here pillar_mock = MagicMock(return_value=pillar_value) - with patch.dict(filestate.__salt__, {'file.manage_file': returner, - 'config.manage_mode': MagicMock(), - 'file.source_list': MagicMock(return_value=[None, None]), - 'file.get_managed': MagicMock(return_value=[None, None, None]), - 'pillar.get': pillar_mock}): + with patch.dict( + filestate.__salt__, + { + "file.manage_file": returner, + "config.manage_mode": MagicMock(), + "file.source_list": MagicMock(return_value=[None, None]), + "file.get_managed": MagicMock(return_value=[None, None, None]), + "pillar.get": pillar_mock, + }, + ): ret = filestate.managed(path, contents_pillar=pillar_path) @@ -156,1938 +156,2302 @@ class TestFileState(TestCase, LoaderModuleMockMixin): self.assertEqual(expected, returner.call_args[0][-5]) def test_symlink(self): - ''' + """ Test to create a symlink. - ''' - name = os.sep + os.path.join('tmp', 'testfile.txt') + """ + name = os.sep + os.path.join("tmp", "testfile.txt") target = salt.utils.files.mkstemp() - test_dir = os.sep + 'tmp' - user = 'salt' + test_dir = os.sep + "tmp" + user = "salt" if salt.utils.platform.is_windows(): - group = 'salt' + group = "salt" else: - group = 'saltstack' + group = "saltstack" def return_val(kwargs): val = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {}, + "name": name, + "result": False, + "comment": "", + "changes": {}, } val.update(kwargs) return val mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock_empty = MagicMock(return_value='') - mock_uid = MagicMock(return_value='U1001') - mock_gid = MagicMock(return_value='g1001') + mock_empty = MagicMock(return_value="") + mock_uid = MagicMock(return_value="U1001") + mock_gid = MagicMock(return_value="g1001") mock_target = MagicMock(return_value=target) mock_user = MagicMock(return_value=user) mock_grp = MagicMock(return_value=group) mock_os_error = MagicMock(side_effect=OSError) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t}): - comt = ('Must provide name to file.symlink') - ret = return_val({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.symlink('', target), ret) + with patch.dict(filestate.__salt__, {"config.manage_mode": mock_t}): + comt = "Must provide name to file.symlink" + ret = return_val({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.symlink("", target), ret) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_empty, - 'file.group_to_gid': mock_empty, - 'user.info': mock_empty, - 'user.current': mock_user}): + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_empty, + "file.group_to_gid": mock_empty, + "user.info": mock_empty, + "user.current": mock_user, + }, + ): if salt.utils.platform.is_windows(): - comt = ('User {0} does not exist'.format(user)) - ret = return_val({'comment': comt, 'name': name}) + comt = "User {0} does not exist".format(user) + ret = return_val({"comment": comt, "name": name}) else: - comt = ('User {0} does not exist. Group {1} does not exist.'.format(user, group)) - ret = return_val({'comment': comt, 'name': name}) + comt = "User {0} does not exist. Group {1} does not exist.".format( + user, group + ) + ret = return_val({"comment": comt, "name": name}) self.assertDictEqual( - filestate.symlink(name, target, user=user, group=group), - ret) + filestate.symlink(name, target, user=user, group=group), ret + ) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_f, - 'user.info': mock_empty, - 'user.current': mock_user}),\ - patch.dict(filestate.__opts__, {'test': True}),\ - patch.object(os.path, 'exists', mock_f): + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_f, + "user.info": mock_empty, + "user.current": mock_user, + }, + ), patch.dict(filestate.__opts__, {"test": True}), patch.object( + os.path, "exists", mock_f + ): if salt.utils.platform.is_windows(): - comt = ('User {0} does not exist'.format(user)) - ret = return_val({'comment': comt, - 'result': False, - 'name': name, - 'changes': {}}) + comt = "User {0} does not exist".format(user) + ret = return_val( + {"comment": comt, "result": False, "name": name, "changes": {}} + ) else: - comt = 'Symlink {0} to {1} is set for creation'.format(name, target) - ret = return_val({'comment': comt, - 'result': None, - 'changes': {'new': name}}) - self.assertDictEqual(filestate.symlink(name, target, user=user, - group=group), ret) + comt = "Symlink {0} to {1} is set for creation".format(name, target) + ret = return_val( + {"comment": comt, "result": None, "changes": {"new": name}} + ) + self.assertDictEqual( + filestate.symlink(name, target, user=user, group=group), ret + ) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_f, - 'user.info': mock_empty, - 'user.current': mock_user}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'isdir', mock_f),\ - patch.object(os.path, 'exists', mock_f): + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_f, + "user.info": mock_empty, + "user.current": mock_user, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "isdir", mock_f + ), patch.object( + os.path, "exists", mock_f + ): if salt.utils.platform.is_windows(): - comt = 'User {0} does not exist'.format(user) - ret = return_val({'comment': comt, - 'result': False, - 'name': name, - 'changes': {}}) + comt = "User {0} does not exist".format(user) + ret = return_val( + {"comment": comt, "result": False, "name": name, "changes": {}} + ) else: - comt = 'Directory {0} for symlink is not present'.format(test_dir) - ret = return_val({'comment': comt, - 'result': False, - 'changes': {}}) - self.assertDictEqual(filestate.symlink(name, target, - user=user, - group=group), ret) + comt = "Directory {0} for symlink is not present".format(test_dir) + ret = return_val({"comment": comt, "result": False, "changes": {}}) + self.assertDictEqual( + filestate.symlink(name, target, user=user, group=group), ret + ) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_t, - 'file.readlink': mock_target, - 'user.info': mock_empty, - 'user.current': mock_user}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'isdir', mock_t),\ - patch.object(salt.states.file, '_check_symlink_ownership', mock_t),\ - patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'): + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_t, + "file.readlink": mock_target, + "user.info": mock_empty, + "user.current": mock_user, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "isdir", mock_t + ), patch.object( + salt.states.file, "_check_symlink_ownership", mock_t + ), patch( + "salt.utils.win_functions.get_sid_from_name", return_value="test-sid" + ): if salt.utils.platform.is_windows(): - comt = 'Symlink {0} is present and owned by {1}'.format(name, user) + comt = "Symlink {0} is present and owned by {1}".format(name, user) else: - comt = 'Symlink {0} is present and owned by {1}:{2}'.format(name, user, group) - ret = return_val({'comment': comt, - 'result': True, - 'changes': {}}) - self.assertDictEqual(filestate.symlink(name, target, - user=user, - group=group), ret) - - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_f, - 'file.readlink': mock_target, - 'user.info': mock_empty, - 'user.current': mock_user}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'isdir', mock_t),\ - patch.object(os.path, 'exists', mock_t),\ - patch.object(os.path, 'lexists', mock_t),\ - patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'): - comt = 'Symlink & backup dest exists and Force not set. {0} -> ' \ - '{1} - backup: {2}'.format(name, target, os.path.join(test_dir, 'SALT')) - ret.update({'comment': comt, - 'result': False, - 'changes': {}}) + comt = "Symlink {0} is present and owned by {1}:{2}".format( + name, user, group + ) + ret = return_val({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual( - filestate.symlink(name, target, user=user, group=group, - backupname='SALT'), - ret) + filestate.symlink(name, target, user=user, group=group), ret + ) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_f, - 'file.readlink': mock_target, - 'user.info': mock_empty, - 'user.current': mock_user}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'exists', mock_t),\ - patch.object(os.path, 'isfile', mock_t), \ - patch.object(os.path, 'isdir', mock_t),\ - patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'): - comt = 'Backupname must be an absolute path or a file name: {0}'.format('tmp/SALT') - ret.update({'comment': comt, - 'result': False, - 'changes': {}}) + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_f, + "file.readlink": mock_target, + "user.info": mock_empty, + "user.current": mock_user, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "isdir", mock_t + ), patch.object( + os.path, "exists", mock_t + ), patch.object( + os.path, "lexists", mock_t + ), patch( + "salt.utils.win_functions.get_sid_from_name", return_value="test-sid" + ): + comt = ( + "Symlink & backup dest exists and Force not set. {0} -> " + "{1} - backup: {2}".format(name, target, os.path.join(test_dir, "SALT")) + ) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual( - filestate.symlink(name, target, user=user, group=group, backupname='tmp/SALT'), - ret) + filestate.symlink( + name, target, user=user, group=group, backupname="SALT" + ), + ret, + ) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_f, - 'file.readlink': mock_target, - 'user.info': mock_empty, - 'user.current': mock_user}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'isdir', mock_t),\ - patch.object(os.path, 'exists', mock_t),\ - patch.object(os.path, 'isfile', mock_t),\ - patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'): - comt = 'File exists where the symlink {0} should be'.format(name) - ret = return_val({'comment': comt, - 'changes': {}, - 'result': False}) + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_f, + "file.readlink": mock_target, + "user.info": mock_empty, + "user.current": mock_user, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "exists", mock_t + ), patch.object( + os.path, "isfile", mock_t + ), patch.object( + os.path, "isdir", mock_t + ), patch( + "salt.utils.win_functions.get_sid_from_name", return_value="test-sid" + ): + comt = "Backupname must be an absolute path or a file name: {0}".format( + "tmp/SALT" + ) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual( - filestate.symlink(name, target, user=user, group=group), - ret) + filestate.symlink( + name, target, user=user, group=group, backupname="tmp/SALT" + ), + ret, + ) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_f, - 'file.readlink': mock_target, - 'file.symlink': mock_t, - 'user.info': mock_t, - 'file.lchown': mock_f}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])),\ - patch.object(os.path, 'isdir', mock_t),\ - patch.object(os.path, 'exists', mock_t),\ - patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'): - comt = 'Directory exists where the symlink {0} should be'.format(name) - ret = return_val({'comment': comt, - 'result': False, - 'changes': {}}) + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_f, + "file.readlink": mock_target, + "user.info": mock_empty, + "user.current": mock_user, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "isdir", mock_t + ), patch.object( + os.path, "exists", mock_t + ), patch.object( + os.path, "isfile", mock_t + ), patch( + "salt.utils.win_functions.get_sid_from_name", return_value="test-sid" + ): + comt = "File exists where the symlink {0} should be".format(name) + ret = return_val({"comment": comt, "changes": {}, "result": False}) self.assertDictEqual( - filestate.symlink(name, target, user=user, group=group), - ret) + filestate.symlink(name, target, user=user, group=group), ret + ) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_f, - 'file.readlink': mock_target, - 'file.symlink': mock_os_error, - 'user.info': mock_t, - 'file.lchown': mock_f}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])),\ - patch.object(os.path, 'isfile', mock_f),\ - patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'): - comt = 'Unable to create new symlink {0} -> {1}: '.format(name, target) - ret = return_val({'comment': comt, - 'result': False, - 'changes': {}}) + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_f, + "file.readlink": mock_target, + "file.symlink": mock_t, + "user.info": mock_t, + "file.lchown": mock_f, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "isdir", MagicMock(side_effect=[True, False]) + ), patch.object( + os.path, "isdir", mock_t + ), patch.object( + os.path, "exists", mock_t + ), patch( + "salt.utils.win_functions.get_sid_from_name", return_value="test-sid" + ): + comt = "Directory exists where the symlink {0} should be".format(name) + ret = return_val({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual( - filestate.symlink(name, target, user=user, group=group), - ret) + filestate.symlink(name, target, user=user, group=group), ret + ) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_f, - 'file.readlink': mock_target, - 'file.symlink': mock_t, - 'user.info': mock_t, - 'file.lchown': mock_f, - 'file.get_user': mock_user, - 'file.get_group': mock_grp}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])),\ - patch.object(os.path, 'isfile', mock_f),\ - patch('salt.states.file._check_symlink_ownership', return_value=True),\ - patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'): - comt = 'Created new symlink {0} -> {1}'.format(name, target) - ret = return_val({'comment': comt, - 'result': True, - 'changes': {'new': name}}) + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_f, + "file.readlink": mock_target, + "file.symlink": mock_os_error, + "user.info": mock_t, + "file.lchown": mock_f, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "isdir", MagicMock(side_effect=[True, False]) + ), patch.object( + os.path, "isfile", mock_f + ), patch( + "salt.utils.win_functions.get_sid_from_name", return_value="test-sid" + ): + comt = "Unable to create new symlink {0} -> {1}: ".format(name, target) + ret = return_val({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual( - filestate.symlink(name, target, user=user, group=group), - ret) + filestate.symlink(name, target, user=user, group=group), ret + ) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.is_link': mock_f, - 'file.readlink': mock_target, - 'file.symlink': mock_t, - 'user.info': mock_t, - 'file.lchown': mock_f, - 'file.get_user': mock_empty, - 'file.get_group': mock_empty}),\ - patch.dict(filestate.__opts__, {'test': False}),\ - patch.object(os.path, 'isdir', MagicMock(side_effect=[True, False])),\ - patch.object(os.path, 'isfile', mock_f),\ - patch('salt.utils.win_functions.get_sid_from_name', return_value='test-sid'),\ - patch('salt.states.file._set_symlink_ownership', return_value=False),\ - patch('salt.states.file._check_symlink_ownership', return_value=False): - comt = 'Created new symlink {0} -> {1}, but was unable to set ' \ - 'ownership to {2}:{3}'.format(name, target, user, group) - ret = return_val({'comment': comt, - 'result': False, - 'changes': {'new': name}}) + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_f, + "file.readlink": mock_target, + "file.symlink": mock_t, + "user.info": mock_t, + "file.lchown": mock_f, + "file.get_user": mock_user, + "file.get_group": mock_grp, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "isdir", MagicMock(side_effect=[True, False]) + ), patch.object( + os.path, "isfile", mock_f + ), patch( + "salt.states.file._check_symlink_ownership", return_value=True + ), patch( + "salt.utils.win_functions.get_sid_from_name", return_value="test-sid" + ): + comt = "Created new symlink {0} -> {1}".format(name, target) + ret = return_val( + {"comment": comt, "result": True, "changes": {"new": name}} + ) self.assertDictEqual( - filestate.symlink(name, target, user=user, group=group), - ret) + filestate.symlink(name, target, user=user, group=group), ret + ) - @skipIf(salt.utils.platform.is_windows(), 'Do not run on Windows') + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.is_link": mock_f, + "file.readlink": mock_target, + "file.symlink": mock_t, + "user.info": mock_t, + "file.lchown": mock_f, + "file.get_user": mock_empty, + "file.get_group": mock_empty, + }, + ), patch.dict(filestate.__opts__, {"test": False}), patch.object( + os.path, "isdir", MagicMock(side_effect=[True, False]) + ), patch.object( + os.path, "isfile", mock_f + ), patch( + "salt.utils.win_functions.get_sid_from_name", return_value="test-sid" + ), patch( + "salt.states.file._set_symlink_ownership", return_value=False + ), patch( + "salt.states.file._check_symlink_ownership", return_value=False + ): + comt = ( + "Created new symlink {0} -> {1}, but was unable to set " + "ownership to {2}:{3}".format(name, target, user, group) + ) + ret = return_val( + {"comment": comt, "result": False, "changes": {"new": name}} + ) + self.assertDictEqual( + filestate.symlink(name, target, user=user, group=group), ret + ) + + @skipIf(salt.utils.platform.is_windows(), "Do not run on Windows") def test_hardlink(self): - ''' + """ Test to create a hardlink. - ''' + """ - name = os.path.join(os.sep, 'tmp', 'testfile.txt') + name = os.path.join(os.sep, "tmp", "testfile.txt") target = salt.utils.files.mkstemp() - test_dir = os.path.join(os.sep, 'tmp') - user, group = 'salt', 'saltstack' + test_dir = os.path.join(os.sep, "tmp") + user, group = "salt", "saltstack" def return_val(**kwargs): res = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {}, + "name": name, + "result": False, + "comment": "", + "changes": {}, } res.update(kwargs) return res mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock_empty = MagicMock(return_value='') - mock_uid = MagicMock(return_value='U1001') - mock_gid = MagicMock(return_value='g1001') + mock_empty = MagicMock(return_value="") + mock_uid = MagicMock(return_value="U1001") + mock_gid = MagicMock(return_value="g1001") mock_nothing = MagicMock(return_value={}) - mock_stats = MagicMock(return_value={'inode': 1}) + mock_stats = MagicMock(return_value={"inode": 1}) mock_execerror = MagicMock(side_effect=CommandExecutionError) patches = {} - patches['file.user_to_uid'] = mock_empty - patches['file.group_to_gid'] = mock_empty - patches['user.info'] = mock_empty - patches['file.is_hardlink'] = mock_t - patches['file.stats'] = mock_empty + patches["file.user_to_uid"] = mock_empty + patches["file.group_to_gid"] = mock_empty + patches["user.info"] = mock_empty + patches["file.is_hardlink"] = mock_t + patches["file.stats"] = mock_empty # Argument validation with patch.dict(filestate.__salt__, patches): - expected = ('Must provide name to file.hardlink') - ret = return_val(comment=expected, name='') - self.assertDictEqual(filestate.hardlink('', target), ret) + expected = "Must provide name to file.hardlink" + ret = return_val(comment=expected, name="") + self.assertDictEqual(filestate.hardlink("", target), ret) # User validation for dir_mode - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_empty}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.object(os.path, 'isabs', mock_t): - expected = 'User {0} does not exist'.format(user) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_empty} + ), patch.dict( + filestate.__salt__, {"file.group_to_gid": mock_gid} + ), patch.object( + os.path, "isabs", mock_t + ): + expected = "User {0} does not exist".format(user) ret = return_val(comment=expected, name=name) self.assertDictEqual( - filestate.hardlink(name, target, user=user, group=group), - ret) + filestate.hardlink(name, target, user=user, group=group), ret + ) # Group validation for dir_mode - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_empty}),\ - patch.object(os.path, 'isabs', mock_t): - expected = 'Group {0} does not exist'.format(group) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict( + filestate.__salt__, {"file.group_to_gid": mock_empty} + ), patch.object( + os.path, "isabs", mock_t + ): + expected = "Group {0} does not exist".format(group) ret = return_val(comment=expected, name=name) self.assertDictEqual( - filestate.hardlink(name, target, user=user, group=group), - ret) + filestate.hardlink(name, target, user=user, group=group), ret + ) # Absolute path for name - nonabs = './non-existent-path/to/non-existent-file' - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}): - expected = 'Specified file {0} is not an absolute path'.format(nonabs) + nonabs = "./non-existent-path/to/non-existent-file" + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}): + expected = "Specified file {0} is not an absolute path".format(nonabs) ret = return_val(comment=expected, name=nonabs) - self.assertDictEqual(filestate.hardlink(nonabs, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(nonabs, target, user=user, group=group), ret + ) # Absolute path for target - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}): - expected = 'Specified target {0} is not an absolute path'.format(nonabs) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}): + expected = "Specified target {0} is not an absolute path".format(nonabs) ret = return_val(comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, nonabs, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, nonabs, user=user, group=group), ret + ) # Test option -- nonexistent target - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.object(os.path, 'exists', mock_f),\ - patch.dict(filestate.__opts__, {'test': True}): - expected = 'Target {0} for hard link does not exist'.format(target) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict( + filestate.__salt__, {"file.group_to_gid": mock_gid} + ), patch.object( + os.path, "exists", mock_f + ), patch.dict( + filestate.__opts__, {"test": True} + ): + expected = "Target {0} for hard link does not exist".format(target) ret = return_val(comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Test option -- target is a directory - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.object(os.path, 'exists', mock_t),\ - patch.dict(filestate.__opts__, {'test': True}): - expected = 'Unable to hard link from directory {0}'.format(test_dir) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict( + filestate.__salt__, {"file.group_to_gid": mock_gid} + ), patch.object( + os.path, "exists", mock_t + ), patch.dict( + filestate.__opts__, {"test": True} + ): + expected = "Unable to hard link from directory {0}".format(test_dir) ret = return_val(comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, test_dir, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, test_dir, user=user, group=group), ret + ) # Test option -- name is a directory - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__opts__, {'test': True}): - expected = 'Unable to hard link to directory {0}'.format(test_dir) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__opts__, {"test": True} + ): + expected = "Unable to hard link to directory {0}".format(test_dir) ret = return_val(comment=expected, name=test_dir) - self.assertDictEqual(filestate.hardlink(test_dir, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(test_dir, target, user=user, group=group), ret + ) # Test option -- name does not exist - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__opts__, {'test': True}): - expected = 'Hard link {0} to {1} is set for creation'.format(name, target) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__opts__, {"test": True} + ): + expected = "Hard link {0} to {1} is set for creation".format(name, target) changes = dict(new=name) ret = return_val(result=None, comment=expected, name=name, changes=changes) - self.assertDictEqual(filestate.hardlink(name, target, - user=user, group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Test option -- hardlink matches - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ - patch.dict(filestate.__salt__, {'file.stats': mock_stats}),\ - patch.object(os.path, 'exists', mock_t),\ - patch.dict(filestate.__opts__, {'test': True}): - expected = 'The hard link {0} is presently targetting {1}'.format(name, target) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_t} + ), patch.dict( + filestate.__salt__, {"file.stats": mock_stats} + ), patch.object( + os.path, "exists", mock_t + ), patch.dict( + filestate.__opts__, {"test": True} + ): + expected = "The hard link {0} is presently targetting {1}".format( + name, target + ) ret = return_val(result=True, comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, target, - user=user, group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Test option -- hardlink does not match - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ - patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ - patch.object(os.path, 'exists', mock_t),\ - patch.dict(filestate.__opts__, {'test': True}): - expected = 'Link {0} target is set to be changed to {1}'.format(name, target) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_t} + ), patch.dict( + filestate.__salt__, {"file.stats": mock_nothing} + ), patch.object( + os.path, "exists", mock_t + ), patch.dict( + filestate.__opts__, {"test": True} + ): + expected = "Link {0} target is set to be changed to {1}".format( + name, target + ) changes = dict(change=name) ret = return_val(result=None, comment=expected, name=name, changes=changes) - self.assertDictEqual(filestate.hardlink(name, target, - user=user, group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Test option -- force removal - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ - patch.object(os.path, 'exists', mock_t),\ - patch.dict(filestate.__opts__, {'test': True}): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_f} + ), patch.object( + os.path, "exists", mock_t + ), patch.dict( + filestate.__opts__, {"test": True} + ): expected = ( - 'The file or directory {0} is set for removal to ' - 'make way for a new hard link targeting {1}'.format(name, target) + "The file or directory {0} is set for removal to " + "make way for a new hard link targeting {1}".format(name, target) ) ret = return_val(result=None, comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, target, force=True, - user=user, group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, force=True, user=user, group=group), + ret, + ) # Test option -- without force removal - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ - patch.object(os.path, 'exists', mock_t),\ - patch.dict(filestate.__opts__, {'test': True}): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_f} + ), patch.object( + os.path, "exists", mock_t + ), patch.dict( + filestate.__opts__, {"test": True} + ): expected = ( - 'File or directory exists where the hard link {0} ' - 'should be. Did you mean to use force?'.format(name) + "File or directory exists where the hard link {0} " + "should be. Did you mean to use force?".format(name) ) ret = return_val(result=False, comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, target, force=False, - user=user, group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, force=False, user=user, group=group), + ret, + ) # Target is a directory - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}): - expected = 'Unable to hard link from directory {0}'.format(test_dir) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}): + expected = "Unable to hard link from directory {0}".format(test_dir) ret = return_val(comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, test_dir, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, test_dir, user=user, group=group), ret + ) # Name is a directory - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}): - expected = 'Unable to hard link to directory {0}'.format(test_dir) + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}): + expected = "Unable to hard link to directory {0}".format(test_dir) ret = return_val(comment=expected, name=test_dir) - self.assertDictEqual(filestate.hardlink(test_dir, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(test_dir, target, user=user, group=group), ret + ) # Try overwrite file with link - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ - patch.object(os.path, 'isfile', mock_t): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_f} + ), patch.object( + os.path, "isfile", mock_t + ): - expected = 'File exists where the hard link {0} should be'.format(name) + expected = "File exists where the hard link {0} should be".format(name) ret = return_val(comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Try overwrite link with same - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ - patch.dict(filestate.__salt__, {'file.stats': mock_stats}),\ - patch.object(os.path, 'isfile', mock_f): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_t} + ), patch.dict( + filestate.__salt__, {"file.stats": mock_stats} + ), patch.object( + os.path, "isfile", mock_f + ): - expected = ('Target of hard link {0} is already pointing ' - 'to {1}'.format(name, target)) + expected = "Target of hard link {0} is already pointing " "to {1}".format( + name, target + ) ret = return_val(result=True, comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Really overwrite link with same - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ - patch.dict(filestate.__salt__, {'file.link': mock_t}),\ - patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ - patch.object(os, 'remove', mock_t),\ - patch.object(os.path, 'isfile', mock_f): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_t} + ), patch.dict( + filestate.__salt__, {"file.link": mock_t} + ), patch.dict( + filestate.__salt__, {"file.stats": mock_nothing} + ), patch.object( + os, "remove", mock_t + ), patch.object( + os.path, "isfile", mock_f + ): - expected = 'Set target of hard link {0} -> {1}'.format(name, target) + expected = "Set target of hard link {0} -> {1}".format(name, target) changes = dict(new=name) ret = return_val(result=True, comment=expected, name=name, changes=changes) - self.assertDictEqual(filestate.hardlink(name, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Fail at overwriting link with same - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_t}),\ - patch.dict(filestate.__salt__, {'file.link': mock_execerror}),\ - patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ - patch.object(os, 'remove', mock_t),\ - patch.object(os.path, 'isfile', mock_f): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_t} + ), patch.dict( + filestate.__salt__, {"file.link": mock_execerror} + ), patch.dict( + filestate.__salt__, {"file.stats": mock_nothing} + ), patch.object( + os, "remove", mock_t + ), patch.object( + os.path, "isfile", mock_f + ): - expected = ('Unable to set target of hard link {0} -> ' - '{1}: {2}'.format(name, target, '')) + expected = "Unable to set target of hard link {0} -> " "{1}: {2}".format( + name, target, "" + ) ret = return_val(result=False, comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Make new link - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ - patch.dict(filestate.__salt__, {'file.link': mock_f}),\ - patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ - patch.object(os, 'remove', mock_t),\ - patch.object(os.path, 'isfile', mock_f): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_f} + ), patch.dict( + filestate.__salt__, {"file.link": mock_f} + ), patch.dict( + filestate.__salt__, {"file.stats": mock_nothing} + ), patch.object( + os, "remove", mock_t + ), patch.object( + os.path, "isfile", mock_f + ): - expected = 'Created new hard link {0} -> {1}'.format(name, target) + expected = "Created new hard link {0} -> {1}".format(name, target) changes = dict(new=name) ret = return_val(result=True, comment=expected, name=name, changes=changes) - self.assertDictEqual(filestate.hardlink(name, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Fail while making new link - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ - patch.dict(filestate.__salt__, {'file.link': mock_execerror}),\ - patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ - patch.object(os, 'remove', mock_t),\ - patch.object(os.path, 'isfile', mock_f): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_f} + ), patch.dict( + filestate.__salt__, {"file.link": mock_execerror} + ), patch.dict( + filestate.__salt__, {"file.stats": mock_nothing} + ), patch.object( + os, "remove", mock_t + ), patch.object( + os.path, "isfile", mock_f + ): - expected = ('Unable to create new hard link {0} -> ' - '{1}: {2}'.format(name, target, '')) + expected = "Unable to create new hard link {0} -> " "{1}: {2}".format( + name, target, "" + ) ret = return_val(result=False, comment=expected, name=name) - self.assertDictEqual(filestate.hardlink(name, target, user=user, - group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, group=group), ret + ) # Force making new link over file - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ - patch.dict(filestate.__salt__, {'file.link': mock_t}),\ - patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ - patch.object(os, 'remove', mock_t),\ - patch.object(os.path, 'isfile', mock_t): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_f} + ), patch.dict( + filestate.__salt__, {"file.link": mock_t} + ), patch.dict( + filestate.__salt__, {"file.stats": mock_nothing} + ), patch.object( + os, "remove", mock_t + ), patch.object( + os.path, "isfile", mock_t + ): - expected = 'Created new hard link {0} -> {1}'.format(name, target) + expected = "Created new hard link {0} -> {1}".format(name, target) changes = dict(new=name) - changes['forced'] = 'File for hard link was forcibly replaced' + changes["forced"] = "File for hard link was forcibly replaced" ret = return_val(result=True, comment=expected, name=name, changes=changes) - self.assertDictEqual(filestate.hardlink(name, target, user=user, - force=True, group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, force=True, group=group), + ret, + ) # Force making new link over file but error out - with patch.dict(filestate.__salt__, patches),\ - patch.dict(filestate.__salt__, {'file.user_to_uid': mock_uid}),\ - patch.dict(filestate.__salt__, {'file.group_to_gid': mock_gid}),\ - patch.dict(filestate.__salt__, {'file.is_hardlink': mock_f}),\ - patch.dict(filestate.__salt__, {'file.link': mock_execerror}),\ - patch.dict(filestate.__salt__, {'file.stats': mock_nothing}),\ - patch.object(os, 'remove', mock_t),\ - patch.object(os.path, 'isfile', mock_t): + with patch.dict(filestate.__salt__, patches), patch.dict( + filestate.__salt__, {"file.user_to_uid": mock_uid} + ), patch.dict(filestate.__salt__, {"file.group_to_gid": mock_gid}), patch.dict( + filestate.__salt__, {"file.is_hardlink": mock_f} + ), patch.dict( + filestate.__salt__, {"file.link": mock_execerror} + ), patch.dict( + filestate.__salt__, {"file.stats": mock_nothing} + ), patch.object( + os, "remove", mock_t + ), patch.object( + os.path, "isfile", mock_t + ): - expected = ('Unable to create new hard link {0} -> ' - '{1}: {2}'.format(name, target, '')) - changes = dict(forced='File for hard link was forcibly replaced') + expected = "Unable to create new hard link {0} -> " "{1}: {2}".format( + name, target, "" + ) + changes = dict(forced="File for hard link was forcibly replaced") ret = return_val(result=False, comment=expected, name=name, changes=changes) - self.assertDictEqual(filestate.hardlink(name, target, user=user, - force=True, group=group), ret) + self.assertDictEqual( + filestate.hardlink(name, target, user=user, force=True, group=group), + ret, + ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to make sure that the named file or directory is absent. - ''' - name = '/fake/file.conf' + """ + name = "/fake/file.conf" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) mock_file = MagicMock(side_effect=[True, CommandExecutionError]) mock_tree = MagicMock(side_effect=[True, OSError]) - comt = ('Must provide name to file.absent') - ret.update({'comment': comt, 'name': ''}) + comt = "Must provide name to file.absent" + ret.update({"comment": comt, "name": ""}) - with patch.object(os.path, 'islink', MagicMock(return_value=False)): - self.assertDictEqual(filestate.absent(''), ret) + with patch.object(os.path, "islink", MagicMock(return_value=False)): + self.assertDictEqual(filestate.absent(""), ret) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path' - .format(name)) - ret.update({'comment': comt, 'name': name}) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.absent(name), ret) - with patch.object(os.path, 'isabs', mock_t): - comt = ('Refusing to make "/" absent') - ret.update({'comment': comt, 'name': '/'}) - self.assertDictEqual(filestate.absent('/'), ret) + with patch.object(os.path, "isabs", mock_t): + comt = 'Refusing to make "/" absent' + ret.update({"comment": comt, "name": "/"}) + self.assertDictEqual(filestate.absent("/"), ret) - with patch.object(os.path, 'isfile', mock_t): - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('File {0} is set for removal'.format(name)) - ret.update({'comment': comt, - 'name': name, - 'result': None, - 'changes': {'removed': '/fake/file.conf'}}) + with patch.object(os.path, "isfile", mock_t): + with patch.dict(filestate.__opts__, {"test": True}): + comt = "File {0} is set for removal".format(name) + ret.update( + { + "comment": comt, + "name": name, + "result": None, + "changes": {"removed": "/fake/file.conf"}, + } + ) self.assertDictEqual(filestate.absent(name), ret) - with patch.dict(filestate.__opts__, {'test': False}): - with patch.dict(filestate.__salt__, - {'file.remove': mock_file}): - comt = ('Removed file {0}'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'removed': name}}) + with patch.dict(filestate.__opts__, {"test": False}): + with patch.dict(filestate.__salt__, {"file.remove": mock_file}): + comt = "Removed file {0}".format(name) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"removed": name}, + } + ) self.assertDictEqual(filestate.absent(name), ret) - comt = ('Removed file {0}'.format(name)) - ret.update({'comment': '', - 'result': False, - 'changes': {}}) + comt = "Removed file {0}".format(name) + ret.update({"comment": "", "result": False, "changes": {}}) self.assertDictEqual(filestate.absent(name), ret) - with patch.object(os.path, 'isfile', mock_f): - with patch.object(os.path, 'isdir', mock_t): - with patch.dict(filestate.__opts__, {'test': True}): - comt = \ - 'Directory {0} is set for removal'.format(name) - ret.update({'comment': comt, - 'changes': {'removed': name}, - 'result': None}) + with patch.object(os.path, "isfile", mock_f): + with patch.object(os.path, "isdir", mock_t): + with patch.dict(filestate.__opts__, {"test": True}): + comt = "Directory {0} is set for removal".format(name) + ret.update( + { + "comment": comt, + "changes": {"removed": name}, + "result": None, + } + ) self.assertDictEqual(filestate.absent(name), ret) - with patch.dict(filestate.__opts__, {'test': False}): - with patch.dict(filestate.__salt__, - {'file.remove': mock_tree}): - comt = ('Removed directory {0}'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'removed': name}}) + with patch.dict(filestate.__opts__, {"test": False}): + with patch.dict(filestate.__salt__, {"file.remove": mock_tree}): + comt = "Removed directory {0}".format(name) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"removed": name}, + } + ) self.assertDictEqual(filestate.absent(name), ret) - comt = \ - 'Failed to remove directory {0}'.format(name) - ret.update({'comment': comt, 'result': False, - 'changes': {}}) + comt = "Failed to remove directory {0}".format(name) + ret.update( + {"comment": comt, "result": False, "changes": {}} + ) self.assertDictEqual(filestate.absent(name), ret) - with patch.object(os.path, 'isdir', mock_f): - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('File {0} is not present'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.object(os.path, "isdir", mock_f): + with patch.dict(filestate.__opts__, {"test": True}): + comt = "File {0} is not present".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(filestate.absent(name), ret) # 'exists' function tests: 1 def test_exists(self): - ''' + """ Test to verify that the named file or directory is present or exists. - ''' - name = '/etc/grub.conf' + """ + name = "/etc/grub.conf" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - comt = ('Must provide name to file.exists') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.exists(''), ret) + comt = "Must provide name to file.exists" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.exists(""), ret) - with patch.object(os.path, 'exists', mock_f): - comt = ('Specified path {0} does not exist'.format(name)) - ret.update({'comment': comt, 'name': name}) + with patch.object(os.path, "exists", mock_f): + comt = "Specified path {0} does not exist".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.exists(name), ret) - with patch.object(os.path, 'exists', mock_t): - comt = ('Path {0} exists'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.object(os.path, "exists", mock_t): + comt = "Path {0} exists".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(filestate.exists(name), ret) # 'missing' function tests: 1 def test_missing(self): - ''' + """ Test to verify that the named file or directory is missing. - ''' - name = '/etc/grub.conf' + """ + name = "/etc/grub.conf" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - comt = ('Must provide name to file.missing') - ret.update({'comment': comt, 'name': '', 'changes': {}}) - self.assertDictEqual(filestate.missing(''), ret) + comt = "Must provide name to file.missing" + ret.update({"comment": comt, "name": "", "changes": {}}) + self.assertDictEqual(filestate.missing(""), ret) - with patch.object(os.path, 'exists', mock_t): - comt = ('Specified path {0} exists'.format(name)) - ret.update({'comment': comt, 'name': name}) + with patch.object(os.path, "exists", mock_t): + comt = "Specified path {0} exists".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.missing(name), ret) - with patch.object(os.path, 'exists', mock_f): - comt = ('Path {0} is missing'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.object(os.path, "exists", mock_f): + comt = "Path {0} is missing".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(filestate.missing(name), ret) # 'managed' function tests: 1 def test_file_managed_should_fall_back_to_binary(self): - expected_contents = b'\x8b' - filename = '/tmp/blarg' - mock_manage = MagicMock(return_value={'fnord': 'fnords'}) - with patch('salt.states.file._load_accumulators', - MagicMock(return_value=([], []))): - with patch.dict(filestate.__salt__, - { - 'file.get_managed': MagicMock(return_value=['', '', '']), - 'file.source_list': MagicMock(return_value=['', '']), - 'file.manage_file': mock_manage, - 'pillar.get': MagicMock(return_value=expected_contents), - }): + expected_contents = b"\x8b" + filename = "/tmp/blarg" + mock_manage = MagicMock(return_value={"fnord": "fnords"}) + with patch( + "salt.states.file._load_accumulators", MagicMock(return_value=([], [])) + ): + with patch.dict( + filestate.__salt__, + { + "file.get_managed": MagicMock(return_value=["", "", ""]), + "file.source_list": MagicMock(return_value=["", ""]), + "file.manage_file": mock_manage, + "pillar.get": MagicMock(return_value=expected_contents), + }, + ): ret = filestate.managed( - filename, - contents_pillar='fnord', - encoding='utf-8' + filename, contents_pillar="fnord", encoding="utf-8" ) actual_contents = mock_manage.call_args[0][14] self.assertEqual(actual_contents, expected_contents) def test_managed(self): - ''' + """ Test to manage a given file, this function allows for a file to be downloaded from the salt master and potentially run through a templating system. - ''' - with patch('salt.states.file._load_accumulators', - MagicMock(return_value=([], []))): - name = '/etc/grub.conf' - user = 'salt' - group = 'saltstack' + """ + with patch( + "salt.states.file._load_accumulators", MagicMock(return_value=([], [])) + ): + name = "/etc/grub.conf" + user = "salt" + group = "saltstack" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock_cmd_fail = MagicMock(return_value={'retcode': 1}) - mock_uid = MagicMock(side_effect=['', 'U12', 'U12', 'U12', 'U12', 'U12', - 'U12', 'U12', 'U12', 'U12', 'U12', - 'U12', 'U12', 'U12', 'U12', 'U12']) - mock_gid = MagicMock(side_effect=['', 'G12', 'G12', 'G12', 'G12', 'G12', - 'G12', 'G12', 'G12', 'G12', 'G12', - 'G12', 'G12', 'G12', 'G12', 'G12']) - mock_if = MagicMock(side_effect=[True, False, False, False, False, - False, False, False]) + mock_cmd_fail = MagicMock(return_value={"retcode": 1}) + mock_uid = MagicMock( + side_effect=[ + "", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + ] + ) + mock_gid = MagicMock( + side_effect=[ + "", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + ] + ) + mock_if = MagicMock( + side_effect=[True, False, False, False, False, False, False, False] + ) if salt.utils.platform.is_windows(): mock_ret = MagicMock(return_value=ret) else: mock_ret = MagicMock(return_value=(ret, None)) mock_dict = MagicMock(return_value={}) mock_cp = MagicMock(side_effect=[Exception, True]) - mock_ex = MagicMock(side_effect=[Exception, {'changes': {name: name}}, - True, Exception]) - mock_mng = MagicMock(side_effect=[Exception, ('', '', ''), ('', '', ''), - ('', '', True), ('', '', True), - ('', '', ''), ('', '', '')]) - mock_file = MagicMock(side_effect=[CommandExecutionError, ('', ''), - ('', ''), ('', ''), ('', ''), - ('', ''), ('', ''), ('', ''), - ('', '')]) - with patch.dict(filestate.__salt__, - {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.file_exists': mock_if, - 'file.check_perms': mock_ret, - 'file.check_managed_changes': mock_dict, - 'file.get_managed': mock_mng, - 'file.source_list': mock_file, - 'file.copy': mock_cp, - 'file.manage_file': mock_ex, - 'cmd.run_all': mock_cmd_fail}): - comt = ('Destination file name is required') - ret.update({'comment': comt, 'name': '', 'changes': {}}) - self.assertDictEqual(filestate.managed(''), ret) + mock_ex = MagicMock( + side_effect=[Exception, {"changes": {name: name}}, True, Exception] + ) + mock_mng = MagicMock( + side_effect=[ + Exception, + ("", "", ""), + ("", "", ""), + ("", "", True), + ("", "", True), + ("", "", ""), + ("", "", ""), + ] + ) + mock_file = MagicMock( + side_effect=[ + CommandExecutionError, + ("", ""), + ("", ""), + ("", ""), + ("", ""), + ("", ""), + ("", ""), + ("", ""), + ("", ""), + ] + ) + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.file_exists": mock_if, + "file.check_perms": mock_ret, + "file.check_managed_changes": mock_dict, + "file.get_managed": mock_mng, + "file.source_list": mock_file, + "file.copy": mock_cp, + "file.manage_file": mock_ex, + "cmd.run_all": mock_cmd_fail, + }, + ): + comt = "Destination file name is required" + ret.update({"comment": comt, "name": "", "changes": {}}) + self.assertDictEqual(filestate.managed(""), ret) - with patch.object(os.path, 'isfile', mock_f): - comt = ('File {0} is not present and is not set for ' - 'creation'.format(name)) - ret.update({'comment': comt, 'name': name, 'result': True}) - self.assertDictEqual(filestate.managed(name, create=False), - ret) + with patch.object(os.path, "isfile", mock_f): + comt = ( + "File {0} is not present and is not set for " + "creation".format(name) + ) + ret.update({"comment": comt, "name": name, "result": True}) + self.assertDictEqual(filestate.managed(name, create=False), ret) # Group argument is ignored on Windows systems. Group is set to # user if salt.utils.platform.is_windows(): - comt = ('User salt is not available Group salt' - ' is not available') + comt = "User salt is not available Group salt" " is not available" else: - comt = ('User salt is not available Group saltstack' - ' is not available') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.managed(name, user=user, - group=group), ret) + comt = ( + "User salt is not available Group saltstack" " is not available" + ) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.managed(name, user=user, group=group), ret + ) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path' - .format(name)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.managed(name, user=user, - group=group), ret) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.managed(name, user=user, group=group), ret + ) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'isdir', mock_t): - comt = ('Specified target {0} is a directory'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(filestate.managed(name, user=user, - group=group), ret) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "isdir", mock_t): + comt = "Specified target {0} is a directory".format(name) + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.managed(name, user=user, group=group), ret + ) - with patch.object(os.path, 'isdir', mock_f): - comt = ('Context must be formed as a dict') - ret.update({'comment': comt}) - self.assertDictEqual(filestate.managed(name, user=user, - group=group, - context=True), ret) + with patch.object(os.path, "isdir", mock_f): + comt = "Context must be formed as a dict" + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group, context=True + ), + ret, + ) - comt = ('Defaults must be formed as a dict') - ret.update({'comment': comt}) - self.assertDictEqual(filestate.managed(name, user=user, - group=group, - defaults=True), ret) + comt = "Defaults must be formed as a dict" + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group, defaults=True + ), + ret, + ) - comt = ('Only one of \'contents\', \'contents_pillar\', ' - 'and \'contents_grains\' is permitted') - ret.update({'comment': comt}) - self.assertDictEqual(filestate.managed - (name, user=user, group=group, - contents='A', contents_grains='B', - contents_pillar='C'), ret) + comt = ( + "Only one of 'contents', 'contents_pillar', " + "and 'contents_grains' is permitted" + ) + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.managed( + name, + user=user, + group=group, + contents="A", + contents_grains="B", + contents_pillar="C", + ), + ret, + ) - with patch.object(os.path, 'exists', mock_t): - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('File {0} not updated'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(filestate.managed - (name, user=user, group=group, - replace=False), ret) + with patch.object(os.path, "exists", mock_t): + with patch.dict(filestate.__opts__, {"test": True}): + comt = "File {0} not updated".format(name) + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group, replace=False + ), + ret, + ) - comt = ('The file {0} is in the correct state' - .format(name)) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(filestate.managed - (name, user=user, contents='A', - group=group), ret) + comt = "The file {0} is in the correct state".format( + name + ) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + filestate.managed( + name, user=user, contents="A", group=group + ), + ret, + ) - with patch.object(os.path, 'exists', mock_f): - with patch.dict(filestate.__opts__, - {'test': False}): - comt = ('Unable to manage file: ') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.managed - (name, user=user, group=group, - contents='A'), ret) + with patch.object(os.path, "exists", mock_f): + with patch.dict(filestate.__opts__, {"test": False}): + comt = "Unable to manage file: " + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group, contents="A" + ), + ret, + ) - comt = ('Unable to manage file: ') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.managed - (name, user=user, group=group, - contents='A'), ret) + comt = "Unable to manage file: " + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group, contents="A" + ), + ret, + ) - with patch.object(salt.utils.files, 'mkstemp', - return_value=name): - comt = 'Unable to copy file {0} to {0}: '.format(name) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.managed - (name, user=user, - group=group, - check_cmd='A'), ret) + with patch.object( + salt.utils.files, "mkstemp", return_value=name + ): + comt = "Unable to copy file {0} to {0}: ".format( + name + ) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group, check_cmd="A" + ), + ret, + ) - comt = ('Unable to check_cmd file: ') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.managed - (name, user=user, group=group, - check_cmd='A'), ret) + comt = "Unable to check_cmd file: " + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group, check_cmd="A" + ), + ret, + ) - comt = ('check_cmd execution failed') - ret.update({'comment': comt, 'result': False, 'skip_watch': True}) - self.assertDictEqual(filestate.managed - (name, user=user, group=group, - check_cmd='A'), ret) + comt = "check_cmd execution failed" + ret.update( + { + "comment": comt, + "result": False, + "skip_watch": True, + } + ) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group, check_cmd="A" + ), + ret, + ) - comt = ('check_cmd execution failed') - ret.update({'comment': True, 'changes': {}}) - ret.pop('skip_watch', None) - self.assertDictEqual(filestate.managed - (name, user=user, group=group), - ret) + comt = "check_cmd execution failed" + ret.update({"comment": True, "changes": {}}) + ret.pop("skip_watch", None) + self.assertDictEqual( + filestate.managed(name, user=user, group=group), ret + ) - self.assertTrue(filestate.managed - (name, user=user, group=group)) + self.assertTrue( + filestate.managed(name, user=user, group=group) + ) - comt = ('Unable to manage file: ') - ret.update({'comment': comt}) - self.assertDictEqual(filestate.managed - (name, user=user, group=group), - ret) + comt = "Unable to manage file: " + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.managed(name, user=user, group=group), ret + ) if salt.utils.platform.is_windows(): mock_ret = MagicMock(return_value=ret) - comt = ('File {0} not updated'.format(name)) + comt = "File {0} not updated".format(name) else: - perms = {'luser': user, - 'lmode': '0644', - 'lgroup': group} + perms = {"luser": user, "lmode": "0644", "lgroup": group} mock_ret = MagicMock(return_value=(ret, perms)) - comt = ('File {0} will be updated with ' - 'permissions 0400 from its current ' - 'state of 0644'.format(name)) + comt = ( + "File {0} will be updated with " + "permissions 0400 from its current " + "state of 0644".format(name) + ) - with patch.dict(filestate.__salt__, - {'file.check_perms': mock_ret}): - with patch.object(os.path, 'exists', mock_t): - with patch.dict(filestate.__opts__, {'test': True}): - ret.update({'comment': comt}) + with patch.dict( + filestate.__salt__, {"file.check_perms": mock_ret} + ): + with patch.object(os.path, "exists", mock_t): + with patch.dict(filestate.__opts__, {"test": True}): + ret.update({"comment": comt}) if salt.utils.platform.is_windows(): - self.assertDictEqual(filestate.managed - (name, user=user, - group=group), ret) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group + ), + ret, + ) else: - self.assertDictEqual(filestate.managed - (name, user=user, - group=group, - mode=400), ret) + self.assertDictEqual( + filestate.managed( + name, user=user, group=group, mode=400 + ), + ret, + ) # 'directory' function tests: 1 def test_directory(self): - ''' + """ Test to ensure that a named directory is present and has the right perms - ''' - name = '/etc/testdir' - user = 'salt' - group = 'saltstack' + """ + name = "/etc/testdir" + user = "salt" + group = "saltstack" if salt.utils.platform.is_windows(): - name = name.replace('/', '\\') + name = name.replace("/", "\\") - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.directory') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.directory(''), ret) + comt = "Must provide name to file.directory" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.directory(""), ret) - comt = ('Cannot specify both max_depth and clean') - ret.update({'comment': comt, 'name': name}) - self.assertDictEqual( - filestate.directory(name, clean=True, max_depth=2), - ret) + comt = "Cannot specify both max_depth and clean" + ret.update({"comment": comt, "name": name}) + self.assertDictEqual(filestate.directory(name, clean=True, max_depth=2), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) if salt.utils.platform.is_windows(): mock_perms = MagicMock(return_value=ret) else: - mock_perms = MagicMock(return_value=(ret, '')) - mock_uid = MagicMock(side_effect=['', 'U12', 'U12', 'U12', 'U12', 'U12', - 'U12', 'U12', 'U12', 'U12', 'U12']) - mock_gid = MagicMock(side_effect=['', 'G12', 'G12', 'G12', 'G12', 'G12', - 'G12', 'G12', 'G12', 'G12', 'G12']) - mock_check = MagicMock(return_value=( - None, - 'The directory "{0}" will be changed'.format(name), - {name: {'directory': 'new'}})) + mock_perms = MagicMock(return_value=(ret, "")) + mock_uid = MagicMock( + side_effect=[ + "", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + "U12", + ] + ) + mock_gid = MagicMock( + side_effect=[ + "", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + "G12", + ] + ) + mock_check = MagicMock( + return_value=( + None, + 'The directory "{0}" will be changed'.format(name), + {name: {"directory": "new"}}, + ) + ) mock_error = CommandExecutionError - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.stats': mock_f, - 'file.check_perms': mock_perms, - 'file.mkdir': mock_t}), \ - patch('salt.utils.win_dacl.get_sid', mock_error), \ - patch('os.path.isdir', mock_t), \ - patch('salt.states.file._check_directory_win', mock_check): + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.stats": mock_f, + "file.check_perms": mock_perms, + "file.mkdir": mock_t, + }, + ), patch("salt.utils.win_dacl.get_sid", mock_error), patch( + "os.path.isdir", mock_t + ), patch( + "salt.states.file._check_directory_win", mock_check + ): if salt.utils.platform.is_windows(): - comt = ('User salt is not available Group salt' - ' is not available') + comt = "User salt is not available Group salt" " is not available" else: - comt = ('User salt is not available Group saltstack' - ' is not available') - ret.update({'comment': comt, 'name': name}) - self.assertDictEqual( - filestate.directory(name, user=user, group=group), ret) + comt = "User salt is not available Group saltstack" " is not available" + ret.update({"comment": comt, "name": name}) + self.assertDictEqual(filestate.directory(name, user=user, group=group), ret) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path' - .format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(filestate.directory(name, user=user, - group=group), ret) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.directory(name, user=user, group=group), ret + ) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'isfile', - MagicMock(side_effect=[True, True, False, - True, True, True, - False])): - with patch.object(os.path, 'lexists', mock_t): - comt = ('File exists where the backup target' - ' A should go') - ret.update({'comment': comt}) - self.assertDictEqual(filestate.directory - (name, user=user, - group=group, - backupname='A'), ret) + with patch.object(os.path, "isabs", mock_t): + with patch.object( + os.path, + "isfile", + MagicMock(side_effect=[True, True, False, True, True, True, False]), + ): + with patch.object(os.path, "lexists", mock_t): + comt = "File exists where the backup target" " A should go" + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.directory( + name, user=user, group=group, backupname="A" + ), + ret, + ) - with patch.object(os.path, 'isfile', mock_t): - comt = ('Specified location {0} exists and is a file' - .format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(filestate.directory(name, user=user, - group=group), ret) + with patch.object(os.path, "isfile", mock_t): + comt = "Specified location {0} exists and is a file".format( + name + ) + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.directory(name, user=user, group=group), ret + ) - with patch.object(os.path, 'islink', mock_t): - comt = ('Specified location {0} exists and is a symlink' - .format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(filestate.directory(name, - user=user, - group=group), - ret) + with patch.object(os.path, "islink", mock_t): + comt = "Specified location {0} exists and is a symlink".format( + name + ) + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.directory(name, user=user, group=group), ret + ) - with patch.object(os.path, 'isdir', mock_f): - with patch.dict(filestate.__opts__, {'test': True}): + with patch.object(os.path, "isdir", mock_f): + with patch.dict(filestate.__opts__, {"test": True}): if salt.utils.platform.is_windows(): - comt = 'The directory "{0}" will be changed' \ - ''.format(name) + comt = 'The directory "{0}" will be changed' "".format(name) else: - comt = ('The following files will be changed:\n{0}:' - ' directory - new\n'.format(name)) - ret.update({ - 'comment': comt, - 'result': None, - 'changes': {name: {'directory': 'new'}} - }) - self.assertDictEqual(filestate.directory(name, - user=user, - group=group), - ret) + comt = ( + "The following files will be changed:\n{0}:" + " directory - new\n".format(name) + ) + ret.update( + { + "comment": comt, + "result": None, + "changes": {name: {"directory": "new"}}, + } + ) + self.assertDictEqual( + filestate.directory(name, user=user, group=group), ret + ) - with patch.dict(filestate.__opts__, {'test': False}): - with patch.object(os.path, 'isdir', mock_f): - comt = ('No directory to create {0} in' - .format(name)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.directory - (name, user=user, group=group), - ret) + with patch.dict(filestate.__opts__, {"test": False}): + with patch.object(os.path, "isdir", mock_f): + comt = "No directory to create {0} in".format(name) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.directory(name, user=user, group=group), ret + ) - with patch.object(os.path, 'isdir', - MagicMock(side_effect=[True, False, True, True])): - comt = ('Failed to create directory {0}' - .format(name)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.directory - (name, user=user, group=group), - ret) + with patch.object( + os.path, + "isdir", + MagicMock(side_effect=[True, False, True, True]), + ): + comt = "Failed to create directory {0}".format(name) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.directory(name, user=user, group=group), ret + ) - recurse = ['ignore_files', 'ignore_dirs'] - ret.update({'comment': 'Must not specify "recurse" ' - 'options "ignore_files" and ' - '"ignore_dirs" at the same ' - 'time.', - 'changes': {}}) - with patch.object(os.path, 'isdir', mock_t): - self.assertDictEqual(filestate.directory - (name, user=user, - recurse=recurse, group=group), - ret) + recurse = ["ignore_files", "ignore_dirs"] + ret.update( + { + "comment": 'Must not specify "recurse" ' + 'options "ignore_files" and ' + '"ignore_dirs" at the same ' + "time.", + "changes": {}, + } + ) + with patch.object(os.path, "isdir", mock_t): + self.assertDictEqual( + filestate.directory( + name, user=user, recurse=recurse, group=group + ), + ret, + ) - self.assertDictEqual(filestate.directory - (name, user=user, group=group), - ret) + self.assertDictEqual( + filestate.directory(name, user=user, group=group), ret + ) # 'recurse' function tests: 1 def test_recurse(self): - ''' + """ Test to recurse through a subdirectory on the master and copy said subdirectory over to the specified path. - ''' - name = '/opt/code/flask' - source = 'salt://code/flask' - user = 'salt' - group = 'saltstack' + """ + name = "/opt/code/flask" + source = "salt://code/flask" + user = "salt" + group = "saltstack" if salt.utils.platform.is_windows(): - name = name.replace('/', '\\') + name = name.replace("/", "\\") - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ("'mode' is not allowed in 'file.recurse'." - " Please use 'file_mode' and 'dir_mode'.") - ret.update({'comment': comt}) - self.assertDictEqual(filestate.recurse(name, source, mode='W'), ret) + comt = ( + "'mode' is not allowed in 'file.recurse'." + " Please use 'file_mode' and 'dir_mode'." + ) + ret.update({"comment": comt}) + self.assertDictEqual(filestate.recurse(name, source, mode="W"), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock_uid = MagicMock(return_value='') - mock_gid = MagicMock(return_value='') + mock_uid = MagicMock(return_value="") + mock_gid = MagicMock(return_value="") mock_l = MagicMock(return_value=[]) - mock_emt = MagicMock(side_effect=[[], ['code/flask'], ['code/flask']]) - mock_lst = MagicMock(side_effect=[CommandExecutionError, (source, ''), - (source, ''), (source, '')]) - with patch.dict(filestate.__salt__, {'config.manage_mode': mock_t, - 'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.source_list': mock_lst, - 'cp.list_master_dirs': mock_emt, - 'cp.list_master': mock_l}): + mock_emt = MagicMock(side_effect=[[], ["code/flask"], ["code/flask"]]) + mock_lst = MagicMock( + side_effect=[ + CommandExecutionError, + (source, ""), + (source, ""), + (source, ""), + ] + ) + with patch.dict( + filestate.__salt__, + { + "config.manage_mode": mock_t, + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.source_list": mock_lst, + "cp.list_master_dirs": mock_emt, + "cp.list_master": mock_l, + }, + ): # Group argument is ignored on Windows systems. Group is set to user if salt.utils.platform.is_windows(): - comt = ('User salt is not available Group salt' - ' is not available') + comt = "User salt is not available Group salt" " is not available" else: - comt = ('User salt is not available Group saltstack' - ' is not available') - ret.update({'comment': comt}) - self.assertDictEqual(filestate.recurse(name, source, user=user, - group=group), ret) + comt = "User salt is not available Group saltstack" " is not available" + ret.update({"comment": comt}) + self.assertDictEqual( + filestate.recurse(name, source, user=user, group=group), ret + ) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path' - .format(name)) - ret.update({'comment': comt}) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt}) self.assertDictEqual(filestate.recurse(name, source), ret) - with patch.object(os.path, 'isabs', mock_t): - comt = ("Invalid source '1' (must be a salt:// URI)") - ret.update({'comment': comt}) + with patch.object(os.path, "isabs", mock_t): + comt = "Invalid source '1' (must be a salt:// URI)" + ret.update({"comment": comt}) self.assertDictEqual(filestate.recurse(name, 1), ret) - comt = ("Invalid source '//code/flask' (must be a salt:// URI)") - ret.update({'comment': comt}) - self.assertDictEqual(filestate.recurse(name, '//code/flask'), - ret) + comt = "Invalid source '//code/flask' (must be a salt:// URI)" + ret.update({"comment": comt}) + self.assertDictEqual(filestate.recurse(name, "//code/flask"), ret) - comt = ('Recurse failed: ') - ret.update({'comment': comt}) + comt = "Recurse failed: " + ret.update({"comment": comt}) self.assertDictEqual(filestate.recurse(name, source), ret) - comt = ("The directory 'code/flask' does not exist" - " on the salt fileserver in saltenv 'base'") - ret.update({'comment': comt}) + comt = ( + "The directory 'code/flask' does not exist" + " on the salt fileserver in saltenv 'base'" + ) + ret.update({"comment": comt}) self.assertDictEqual(filestate.recurse(name, source), ret) - with patch.object(os.path, 'isdir', mock_f): - with patch.object(os.path, 'exists', mock_t): - comt = ('The path {0} exists and is not a directory' - .format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(filestate.recurse(name, source), - ret) + with patch.object(os.path, "isdir", mock_f): + with patch.object(os.path, "exists", mock_t): + comt = "The path {0} exists and is not a directory".format(name) + ret.update({"comment": comt}) + self.assertDictEqual(filestate.recurse(name, source), ret) - with patch.object(os.path, 'isdir', mock_t): - comt = ('The directory {0} is in the correct state' - .format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.object(os.path, "isdir", mock_t): + comt = "The directory {0} is in the correct state".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(filestate.recurse(name, source), ret) # 'replace' function tests: 1 def test_replace(self): - ''' + """ Test to maintain an edit in a file. - ''' - name = '/etc/grub.conf' - pattern = ('CentOS +') - repl = 'salt' + """ + name = "/etc/grub.conf" + pattern = "CentOS +" + repl = "salt" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.replace') - ret.update({'comment': comt, 'name': '', 'changes': {}}) - self.assertDictEqual(filestate.replace('', pattern, repl), ret) + comt = "Must provide name to file.replace" + ret.update({"comment": comt, "name": "", "changes": {}}) + self.assertDictEqual(filestate.replace("", pattern, repl), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path'.format(name)) - ret.update({'comment': comt, 'name': name}) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.replace(name, pattern, repl), ret) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'exists', mock_t): - with patch.dict(filestate.__salt__, {'file.replace': mock_f}): - with patch.dict(filestate.__opts__, {'test': False}): - comt = ('No changes needed to be made') - ret.update({'comment': comt, 'name': name, - 'result': True}) - self.assertDictEqual(filestate.replace(name, pattern, - repl), ret) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "exists", mock_t): + with patch.dict(filestate.__salt__, {"file.replace": mock_f}): + with patch.dict(filestate.__opts__, {"test": False}): + comt = "No changes needed to be made" + ret.update({"comment": comt, "name": name, "result": True}) + self.assertDictEqual( + filestate.replace(name, pattern, repl), ret + ) # 'blockreplace' function tests: 1 def test_blockreplace(self): - ''' + """ Test to maintain an edit in a file in a zone delimited by two line markers. - ''' - with patch('salt.states.file._load_accumulators', - MagicMock(return_value=([], []))): - name = '/etc/hosts' + """ + with patch( + "salt.states.file._load_accumulators", MagicMock(return_value=([], [])) + ): + name = "/etc/hosts" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.blockreplace') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.blockreplace(''), ret) + comt = "Must provide name to file.blockreplace" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.blockreplace(""), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path'.format(name)) - ret.update({'comment': comt, 'name': name}) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.blockreplace(name), ret) - with patch.object(os.path, 'isabs', mock_t), \ - patch.object(os.path, 'exists', mock_t): - with patch.dict(filestate.__salt__, {'file.blockreplace': mock_t}): - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('Changes would be made') - ret.update({'comment': comt, 'result': None, - 'changes': {'diff': True}}) + with patch.object(os.path, "isabs", mock_t), patch.object( + os.path, "exists", mock_t + ): + with patch.dict(filestate.__salt__, {"file.blockreplace": mock_t}): + with patch.dict(filestate.__opts__, {"test": True}): + comt = "Changes would be made" + ret.update( + {"comment": comt, "result": None, "changes": {"diff": True}} + ) self.assertDictEqual(filestate.blockreplace(name), ret) # 'comment' function tests: 1 def test_comment(self): - ''' + """ Test to comment out specified lines in a file. - ''' - with patch.object(os.path, 'exists', MagicMock(return_value=True)): - name = '/etc/aliases' if salt.utils.platform.is_darwin() else '/etc/fstab' - regex = 'bind 127.0.0.1' + """ + with patch.object(os.path, "exists", MagicMock(return_value=True)): + name = "/etc/aliases" if salt.utils.platform.is_darwin() else "/etc/fstab" + regex = "bind 127.0.0.1" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.comment') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.comment('', regex), ret) + comt = "Must provide name to file.comment" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.comment("", regex), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path'.format(name)) - ret.update({'comment': comt, 'name': name}) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.comment(name, regex), ret) - with patch.object(os.path, 'isabs', mock_t): - with patch.dict(filestate.__salt__, - {'file.search': MagicMock(side_effect=[False, True, False, False])}): - comt = ('Pattern already commented') - ret.update({'comment': comt, 'result': True}) + with patch.object(os.path, "isabs", mock_t): + with patch.dict( + filestate.__salt__, + {"file.search": MagicMock(side_effect=[False, True, False, False])}, + ): + comt = "Pattern already commented" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(filestate.comment(name, regex), ret) - comt = ('{0}: Pattern not found'.format(regex)) - ret.update({'comment': comt, 'result': False}) + comt = "{0}: Pattern not found".format(regex) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(filestate.comment(name, regex), ret) - with patch.dict(filestate.__salt__, - {'file.search': MagicMock(side_effect=[True, True, True]), - 'file.comment': mock_t, - 'file.comment_line': mock_t}): - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('File {0} is set to be updated'.format(name)) - ret.update({'comment': comt, 'result': None, 'changes': {name: 'updated'}}) + with patch.dict( + filestate.__salt__, + { + "file.search": MagicMock(side_effect=[True, True, True]), + "file.comment": mock_t, + "file.comment_line": mock_t, + }, + ): + with patch.dict(filestate.__opts__, {"test": True}): + comt = "File {0} is set to be updated".format(name) + ret.update( + { + "comment": comt, + "result": None, + "changes": {name: "updated"}, + } + ) self.assertDictEqual(filestate.comment(name, regex), ret) - with patch.dict(filestate.__opts__, {'test': False}): - with patch.object(salt.utils.files, 'fopen', - MagicMock(mock_open())): - comt = ('Commented lines successfully') - ret.update({'comment': comt, 'result': True, - 'changes': {}}) - self.assertDictEqual(filestate.comment(name, regex), - ret) + with patch.dict(filestate.__opts__, {"test": False}): + with patch.object( + salt.utils.files, "fopen", MagicMock(mock_open()) + ): + comt = "Commented lines successfully" + ret.update({"comment": comt, "result": True, "changes": {}}) + self.assertDictEqual(filestate.comment(name, regex), ret) # 'uncomment' function tests: 1 def test_uncomment(self): - ''' + """ Test to uncomment specified commented lines in a file - ''' - with patch.object(os.path, 'exists', MagicMock(return_value=True)): - name = '/etc/aliases' if salt.utils.platform.is_darwin() else '/etc/fstab' - regex = 'bind 127.0.0.1' + """ + with patch.object(os.path, "exists", MagicMock(return_value=True)): + name = "/etc/aliases" if salt.utils.platform.is_darwin() else "/etc/fstab" + regex = "bind 127.0.0.1" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.uncomment') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.uncomment('', regex), ret) + comt = "Must provide name to file.uncomment" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.uncomment("", regex), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock = MagicMock(side_effect=[False, True, - False, False, - True, - True, True]) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path'.format(name)) - ret.update({'comment': comt, 'name': name}) + mock = MagicMock(side_effect=[False, True, False, False, True, True, True]) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.uncomment(name, regex), ret) - with patch.object(os.path, 'isabs', mock_t): - with patch.dict(filestate.__salt__, - {'file.search': mock, - 'file.uncomment': mock_t, - 'file.comment_line': mock_t}): - comt = ('Pattern already uncommented') - ret.update({'comment': comt, 'result': True}) + with patch.object(os.path, "isabs", mock_t): + with patch.dict( + filestate.__salt__, + { + "file.search": mock, + "file.uncomment": mock_t, + "file.comment_line": mock_t, + }, + ): + comt = "Pattern already uncommented" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(filestate.uncomment(name, regex), ret) - comt = ('{0}: Pattern not found'.format(regex)) - ret.update({'comment': comt, 'result': False}) + comt = "{0}: Pattern not found".format(regex) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(filestate.uncomment(name, regex), ret) - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('File {0} is set to be updated'.format(name)) - ret.update({'comment': comt, 'result': None, - 'changes': {name: 'updated'}}) + with patch.dict(filestate.__opts__, {"test": True}): + comt = "File {0} is set to be updated".format(name) + ret.update( + { + "comment": comt, + "result": None, + "changes": {name: "updated"}, + } + ) self.assertDictEqual(filestate.uncomment(name, regex), ret) - with patch.dict(filestate.__opts__, {'test': False}): - with patch.object(salt.utils.files, 'fopen', - MagicMock(mock_open())): - comt = ('Uncommented lines successfully') - ret.update({'comment': comt, 'result': True, - 'changes': {}}) + with patch.dict(filestate.__opts__, {"test": False}): + with patch.object( + salt.utils.files, "fopen", MagicMock(mock_open()) + ): + comt = "Uncommented lines successfully" + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(filestate.uncomment(name, regex), ret) # 'prepend' function tests: 1 def test_prepend(self): - ''' + """ Test to ensure that some text appears at the beginning of a file. - ''' - name = '/tmp/etc/motd' + """ + name = "/tmp/etc/motd" if salt.utils.platform.is_windows(): - name = 'c:\\tmp\\etc\\motd' + name = "c:\\tmp\\etc\\motd" assert not os.path.exists(os.path.split(name)[0]) - source = ['salt://motd/hr-messages.tmpl'] - sources = ['salt://motd/devops-messages.tmpl'] - text = ['Trust no one unless you have eaten much salt with him.'] + source = ["salt://motd/hr-messages.tmpl"] + sources = ["salt://motd/devops-messages.tmpl"] + text = ["Trust no one unless you have eaten much salt with him."] - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.prepend') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.prepend(''), ret) + comt = "Must provide name to file.prepend" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.prepend(""), ret) - comt = ('source and sources are mutually exclusive') - ret.update({'comment': comt, 'name': name}) - self.assertDictEqual(filestate.prepend(name, source=source, - sources=sources), ret) + comt = "source and sources are mutually exclusive" + ret.update({"comment": comt, "name": name}) + self.assertDictEqual( + filestate.prepend(name, source=source, sources=sources), ret + ) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - with patch.dict(filestate.__salt__, - {'file.directory_exists': mock_f, - 'file.makedirs': mock_t, - 'file.stats': mock_f, - 'cp.get_template': mock_f, - 'file.search': mock_f, - 'file.prepend': mock_t}): - comt = ('The following files will be changed:\n/tmp/etc:' - ' directory - new\n') - changes = {'/tmp/etc': {'directory': 'new'}} + with patch.dict( + filestate.__salt__, + { + "file.directory_exists": mock_f, + "file.makedirs": mock_t, + "file.stats": mock_f, + "cp.get_template": mock_f, + "file.search": mock_f, + "file.prepend": mock_t, + }, + ): + comt = ( + "The following files will be changed:\n/tmp/etc:" " directory - new\n" + ) + changes = {"/tmp/etc": {"directory": "new"}} if salt.utils.platform.is_windows(): comt = 'The directory "c:\\tmp\\etc" will be changed' - changes = {'c:\\tmp\\etc': {'directory': 'new'}} - ret.update({'comment': comt, 'name': name, 'changes': changes}) - self.assertDictEqual(filestate.prepend(name, makedirs=True), - ret) + changes = {"c:\\tmp\\etc": {"directory": "new"}} + ret.update({"comment": comt, "name": name, "changes": changes}) + self.assertDictEqual(filestate.prepend(name, makedirs=True), ret) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path' - .format(name)) - ret.update({'comment': comt, 'changes': {}}) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "changes": {}}) self.assertDictEqual(filestate.prepend(name), ret) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'exists', mock_t): - comt = ("Failed to load template file {0}".format(source)) - ret.update({'comment': comt, 'name': source, 'data': []}) - self.assertDictEqual(filestate.prepend(name, source=source), - ret) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "exists", mock_t): + comt = "Failed to load template file {0}".format(source) + ret.update({"comment": comt, "name": source, "data": []}) + self.assertDictEqual(filestate.prepend(name, source=source), ret) - ret.pop('data', None) - ret.update({'name': name}) - with patch.object(salt.utils.files, 'fopen', - MagicMock(mock_open(read_data=''))): - with patch.dict(filestate.__utils__, {'files.is_text': mock_f}): - with patch.dict(filestate.__opts__, {'test': True}): - change = {'diff': 'Replace binary file'} - comt = ('File {0} is set to be updated' - .format(name)) - ret.update({'comment': comt, - 'result': None, - 'changes': change}) - self.assertDictEqual(filestate.prepend - (name, text=text), ret) + ret.pop("data", None) + ret.update({"name": name}) + with patch.object( + salt.utils.files, "fopen", MagicMock(mock_open(read_data="")) + ): + with patch.dict(filestate.__utils__, {"files.is_text": mock_f}): + with patch.dict(filestate.__opts__, {"test": True}): + change = {"diff": "Replace binary file"} + comt = "File {0} is set to be updated".format(name) + ret.update( + {"comment": comt, "result": None, "changes": change} + ) + self.assertDictEqual( + filestate.prepend(name, text=text), ret + ) - with patch.dict(filestate.__opts__, - {'test': False}): - comt = ('Prepended 1 lines') - ret.update({'comment': comt, 'result': True, - 'changes': {}}) - self.assertDictEqual(filestate.prepend - (name, text=text), ret) + with patch.dict(filestate.__opts__, {"test": False}): + comt = "Prepended 1 lines" + ret.update( + {"comment": comt, "result": True, "changes": {}} + ) + self.assertDictEqual( + filestate.prepend(name, text=text), ret + ) # 'touch' function tests: 1 def test_touch(self): - ''' + """ Test to replicate the 'nix "touch" command to create a new empty file or update the atime and mtime of an existing file. - ''' - name = '/var/log/httpd/logrotate.empty' + """ + name = "/var/log/httpd/logrotate.empty" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.touch') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.touch(''), ret) + comt = "Must provide name to file.touch" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.touch(""), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path'.format(name)) - ret.update({'comment': comt, 'name': name}) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.touch(name), ret) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'exists', mock_f): - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('File {0} is set to be created'.format(name)) - ret.update({'comment': comt, - 'result': None, - 'changes': {'new': name}}) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "exists", mock_f): + with patch.dict(filestate.__opts__, {"test": True}): + comt = "File {0} is set to be created".format(name) + ret.update( + {"comment": comt, "result": None, "changes": {"new": name}} + ) self.assertDictEqual(filestate.touch(name), ret) - with patch.dict(filestate.__opts__, {'test': False}): - with patch.object(os.path, 'isdir', mock_f): - comt = ('Directory not present to touch file {0}' - .format(name)) - ret.update({'comment': comt, - 'result': False, - 'changes': {}}) + with patch.dict(filestate.__opts__, {"test": False}): + with patch.object(os.path, "isdir", mock_f): + comt = "Directory not present to touch file {0}".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(filestate.touch(name), ret) - with patch.object(os.path, 'isdir', mock_t): - with patch.dict(filestate.__salt__, {'file.touch': mock_t}): - comt = ('Created empty file {0}'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'new': name}}) + with patch.object(os.path, "isdir", mock_t): + with patch.dict(filestate.__salt__, {"file.touch": mock_t}): + comt = "Created empty file {0}".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"new": name}} + ) self.assertDictEqual(filestate.touch(name), ret) # 'copy' function tests: 1 def test_copy(self): - ''' + """ Test if the source file exists on the system, copy it to the named file. - ''' - name = '/tmp/salt' - source = '/tmp/salt/salt' - user = 'salt' - group = 'saltstack' + """ + name = "/tmp/salt" + source = "/tmp/salt/salt" + user = "salt" + group = "saltstack" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.copy') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.copy_('', source), ret) + comt = "Must provide name to file.copy" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.copy_("", source), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock_uid = MagicMock(side_effect=['']) - mock_gid = MagicMock(side_effect=['']) + mock_uid = MagicMock(side_effect=[""]) + mock_gid = MagicMock(side_effect=[""]) mock_user = MagicMock(return_value=user) mock_grp = MagicMock(return_value=group) mock_io = MagicMock(side_effect=IOError) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path'.format(name)) - ret.update({'comment': comt, 'name': name}) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.copy_(name, source), ret) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'exists', mock_f): - comt = ('Source file "{0}" is not present'.format(source)) - ret.update({'comment': comt, 'result': False}) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "exists", mock_f): + comt = 'Source file "{0}" is not present'.format(source) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(filestate.copy_(name, source), ret) - with patch.object(os.path, 'exists', mock_t): - with patch.dict(filestate.__salt__, - {'file.user_to_uid': mock_uid, - 'file.group_to_gid': mock_gid, - 'file.get_user': mock_user, - 'file.get_group': mock_grp, - 'file.get_mode': mock_grp, - 'file.check_perms': mock_t}): + with patch.object(os.path, "exists", mock_t): + with patch.dict( + filestate.__salt__, + { + "file.user_to_uid": mock_uid, + "file.group_to_gid": mock_gid, + "file.get_user": mock_user, + "file.get_group": mock_grp, + "file.get_mode": mock_grp, + "file.check_perms": mock_t, + }, + ): # Group argument is ignored on Windows systems. Group is set # to user if salt.utils.platform.is_windows(): - comt = ('User salt is not available Group salt' - ' is not available') + comt = ( + "User salt is not available Group salt" " is not available" + ) else: - comt = ('User salt is not available Group saltstack' - ' is not available') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.copy_(name, source, user=user, - group=group), ret) + comt = ( + "User salt is not available Group saltstack" + " is not available" + ) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.copy_(name, source, user=user, group=group), ret + ) - comt1 = ('Failed to delete "{0}" in preparation for' - ' forced move'.format(name)) - comt2 = ('The target file "{0}" exists and will not be ' - 'overwritten'.format(name)) - comt3 = ('File "{0}" is set to be copied to "{1}"' - .format(source, name)) - with patch.object(os.path, 'isdir', mock_f): - with patch.object(os.path, 'lexists', mock_t): - with patch.dict(filestate.__opts__, - {'test': False}): - with patch.dict(filestate.__salt__, - {'file.remove': mock_io}): - ret.update({'comment': comt1, - 'result': False}) - self.assertDictEqual(filestate.copy_ - (name, source, - preserve=True, - force=True), ret) + comt1 = ( + 'Failed to delete "{0}" in preparation for' + " forced move".format(name) + ) + comt2 = ( + 'The target file "{0}" exists and will not be ' + "overwritten".format(name) + ) + comt3 = 'File "{0}" is set to be copied to "{1}"'.format( + source, name + ) + with patch.object(os.path, "isdir", mock_f): + with patch.object(os.path, "lexists", mock_t): + with patch.dict(filestate.__opts__, {"test": False}): + with patch.dict( + filestate.__salt__, {"file.remove": mock_io} + ): + ret.update({"comment": comt1, "result": False}) + self.assertDictEqual( + filestate.copy_( + name, source, preserve=True, force=True + ), + ret, + ) - with patch.object(os.path, 'isfile', mock_t): - ret.update({'comment': comt2, - 'result': True}) - self.assertDictEqual(filestate.copy_ - (name, source, - preserve=True), ret) + with patch.object(os.path, "isfile", mock_t): + ret.update({"comment": comt2, "result": True}) + self.assertDictEqual( + filestate.copy_(name, source, preserve=True), + ret, + ) - with patch.object(os.path, 'lexists', mock_f): - with patch.dict(filestate.__opts__, {'test': True}): - ret.update({'comment': comt3, 'result': None}) - self.assertDictEqual(filestate.copy_ - (name, source, - preserve=True), ret) + with patch.object(os.path, "lexists", mock_f): + with patch.dict(filestate.__opts__, {"test": True}): + ret.update({"comment": comt3, "result": None}) + self.assertDictEqual( + filestate.copy_(name, source, preserve=True), ret + ) - with patch.dict(filestate.__opts__, {'test': False}): - comt = ('The target directory /tmp is' - ' not present') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.copy_ - (name, source, - preserve=True), ret) + with patch.dict(filestate.__opts__, {"test": False}): + comt = "The target directory /tmp is" " not present" + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.copy_(name, source, preserve=True), ret + ) # 'rename' function tests: 1 def test_rename(self): - ''' + """ Test if the source file exists on the system, rename it to the named file. - ''' - name = '/tmp/salt' - source = '/tmp/salt/salt' + """ + name = "/tmp/salt" + source = "/tmp/salt/salt" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.rename') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.rename('', source), ret) + comt = "Must provide name to file.rename" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.rename("", source), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) mock_lex = MagicMock(side_effect=[False, True, True]) - with patch.object(os.path, 'isabs', mock_f): - comt = ('Specified file {0} is not an absolute path'.format(name)) - ret.update({'comment': comt, 'name': name}) + with patch.object(os.path, "isabs", mock_f): + comt = "Specified file {0} is not an absolute path".format(name) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.rename(name, source), ret) mock_lex = MagicMock(return_value=False) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'lexists', mock_lex): - comt = ('Source file "{0}" has already been moved out of ' - 'place'.format(source)) - ret.update({'comment': comt, 'result': True}) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "lexists", mock_lex): + comt = ( + 'Source file "{0}" has already been moved out of ' + "place".format(source) + ) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(filestate.rename(name, source), ret) mock_lex = MagicMock(side_effect=[True, True, True]) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'lexists', mock_lex): - comt = ('The target file "{0}" exists and will not be ' - 'overwritten'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "lexists", mock_lex): + comt = ( + 'The target file "{0}" exists and will not be ' + "overwritten".format(name) + ) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(filestate.rename(name, source), ret) mock_lex = MagicMock(side_effect=[True, True, True]) mock_rem = MagicMock(side_effect=IOError) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'lexists', mock_lex): - with patch.dict(filestate.__opts__, {'test': False}): - comt = ('Failed to delete "{0}" in preparation for ' - 'forced move'.format(name)) - with patch.dict(filestate.__salt__, - {'file.remove': mock_rem}): - ret.update({'name': name, - 'comment': comt, - 'result': False}) - self.assertDictEqual(filestate.rename(name, source, - force=True), ret) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "lexists", mock_lex): + with patch.dict(filestate.__opts__, {"test": False}): + comt = ( + 'Failed to delete "{0}" in preparation for ' + "forced move".format(name) + ) + with patch.dict(filestate.__salt__, {"file.remove": mock_rem}): + ret.update({"name": name, "comment": comt, "result": False}) + self.assertDictEqual( + filestate.rename(name, source, force=True), ret + ) mock_lex = MagicMock(side_effect=[True, False, False]) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'lexists', mock_lex): - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('File "{0}" is set to be moved to "{1}"' - .format(source, name)) - ret.update({'name': name, - 'comment': comt, - 'result': None}) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "lexists", mock_lex): + with patch.dict(filestate.__opts__, {"test": True}): + comt = 'File "{0}" is set to be moved to "{1}"'.format(source, name) + ret.update({"name": name, "comment": comt, "result": None}) self.assertDictEqual(filestate.rename(name, source), ret) mock_lex = MagicMock(side_effect=[True, False, False]) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'lexists', mock_lex): - with patch.object(os.path, 'isdir', mock_f): - with patch.dict(filestate.__opts__, {'test': False}): - comt = ('The target directory /tmp is not present') - ret.update({'name': name, - 'comment': comt, - 'result': False}) - self.assertDictEqual(filestate.rename(name, source), - ret) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "lexists", mock_lex): + with patch.object(os.path, "isdir", mock_f): + with patch.dict(filestate.__opts__, {"test": False}): + comt = "The target directory /tmp is not present" + ret.update({"name": name, "comment": comt, "result": False}) + self.assertDictEqual(filestate.rename(name, source), ret) mock_lex = MagicMock(side_effect=[True, False, False]) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'lexists', mock_lex): - with patch.object(os.path, 'isdir', mock_t): - with patch.object(os.path, 'islink', mock_f): - with patch.dict(filestate.__opts__, {'test': False}): - with patch.object(shutil, 'move', - MagicMock(side_effect=IOError)): - comt = ('Failed to move "{0}" to "{1}"' - .format(source, name)) - ret.update({'name': name, - 'comment': comt, - 'result': False}) - self.assertDictEqual(filestate.rename(name, - source), - ret) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "lexists", mock_lex): + with patch.object(os.path, "isdir", mock_t): + with patch.object(os.path, "islink", mock_f): + with patch.dict(filestate.__opts__, {"test": False}): + with patch.object( + shutil, "move", MagicMock(side_effect=IOError) + ): + comt = 'Failed to move "{0}" to "{1}"'.format( + source, name + ) + ret.update( + {"name": name, "comment": comt, "result": False} + ) + self.assertDictEqual( + filestate.rename(name, source), ret + ) mock_lex = MagicMock(side_effect=[True, False, False]) - with patch.object(os.path, 'isabs', mock_t): - with patch.object(os.path, 'lexists', mock_lex): - with patch.object(os.path, 'isdir', mock_t): - with patch.object(os.path, 'islink', mock_f): - with patch.dict(filestate.__opts__, {'test': False}): - with patch.object(shutil, 'move', MagicMock()): - comt = ('Moved "{0}" to "{1}"'.format(source, - name)) - ret.update({'name': name, - 'comment': comt, - 'result': True, - 'changes': {name: source}}) - self.assertDictEqual(filestate.rename(name, - source), - ret) + with patch.object(os.path, "isabs", mock_t): + with patch.object(os.path, "lexists", mock_lex): + with patch.object(os.path, "isdir", mock_t): + with patch.object(os.path, "islink", mock_f): + with patch.dict(filestate.__opts__, {"test": False}): + with patch.object(shutil, "move", MagicMock()): + comt = 'Moved "{0}" to "{1}"'.format(source, name) + ret.update( + { + "name": name, + "comment": comt, + "result": True, + "changes": {name: source}, + } + ) + self.assertDictEqual( + filestate.rename(name, source), ret + ) # 'accumulated' function tests: 1 def test_accumulated(self): - ''' + """ Test to prepare accumulator which can be used in template in file. - ''' - with patch('salt.states.file._load_accumulators', - MagicMock(return_value=({}, {}))), \ - patch('salt.states.file._persist_accummulators', - MagicMock(return_value=True)): - name = 'animals_doing_things' - filename = '/tmp/animal_file.txt' - text = ' jumps over the lazy dog.' + """ + with patch( + "salt.states.file._load_accumulators", MagicMock(return_value=({}, {})) + ), patch( + "salt.states.file._persist_accummulators", MagicMock(return_value=True) + ): + name = "animals_doing_things" + filename = "/tmp/animal_file.txt" + text = " jumps over the lazy dog." - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.accumulated') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.accumulated('', filename, text), ret) + comt = "Must provide name to file.accumulated" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.accumulated("", filename, text), ret) - comt = ('No text supplied for accumulator') - ret.update({'comment': comt, 'name': name}) + comt = "No text supplied for accumulator" + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.accumulated(name, filename, None), ret) - with patch.dict(filestate.__low__, {'require_in': 'file', - 'watch_in': 'salt', - '__sls__': 'SLS', '__id__': 'ID'}): - comt = ('Orphaned accumulator animals_doing_things in SLS:ID') - ret.update({'comment': comt, 'name': name}) - self.assertDictEqual(filestate.accumulated(name, filename, text), - ret) + with patch.dict( + filestate.__low__, + { + "require_in": "file", + "watch_in": "salt", + "__sls__": "SLS", + "__id__": "ID", + }, + ): + comt = "Orphaned accumulator animals_doing_things in SLS:ID" + ret.update({"comment": comt, "name": name}) + self.assertDictEqual(filestate.accumulated(name, filename, text), ret) - with patch.dict(filestate.__low__, {'require_in': [{'file': 'A'}], - 'watch_in': [{'B': 'C'}], - '__sls__': 'SLS', '__id__': 'ID'}): - comt = ('Accumulator {0} for file {1} ' - 'was charged by text'.format(name, filename)) - ret.update({'comment': comt, 'name': name, 'result': True}) - self.assertDictEqual(filestate.accumulated(name, filename, text), - ret) + with patch.dict( + filestate.__low__, + { + "require_in": [{"file": "A"}], + "watch_in": [{"B": "C"}], + "__sls__": "SLS", + "__id__": "ID", + }, + ): + comt = "Accumulator {0} for file {1} " "was charged by text".format( + name, filename + ) + ret.update({"comment": comt, "name": name, "result": True}) + self.assertDictEqual(filestate.accumulated(name, filename, text), ret) # 'serialize' function tests: 1 def test_serialize_into_managed_file(self): - ''' + """ Test to serializes dataset and store it into managed file. - ''' - name = '/etc/dummy/package.json' + """ + name = "/etc/dummy/package.json" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.serialize') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.serialize(''), ret) + comt = "Must provide name to file.serialize" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.serialize(""), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - with patch.object(os.path, 'isfile', mock_f): - comt = ('File {0} is not present and is not set for ' - 'creation'.format(name)) - ret.update({'comment': comt, 'name': name, 'result': True}) + with patch.object(os.path, "isfile", mock_f): + comt = "File {0} is not present and is not set for " "creation".format( + name + ) + ret.update({"comment": comt, "name": name, "result": True}) self.assertDictEqual(filestate.serialize(name, create=False), ret) - comt = ("Only one of 'dataset' and 'dataset_pillar' is permitted") - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.serialize(name, dataset=True, - dataset_pillar=True), ret) + comt = "Only one of 'dataset' and 'dataset_pillar' is permitted" + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.serialize(name, dataset=True, dataset_pillar=True), ret + ) - comt = ("Neither 'dataset' nor 'dataset_pillar' was defined") - ret.update({'comment': comt, 'result': False}) + comt = "Neither 'dataset' nor 'dataset_pillar' was defined" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(filestate.serialize(name), ret) - with patch.object(os.path, 'isfile', mock_t): - comt = ('Python format is not supported for merging') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.serialize(name, dataset=True, - merge_if_exists=True, - formatter='python'), ret) + with patch.object(os.path, "isfile", mock_t): + comt = "Python format is not supported for merging" + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.serialize( + name, dataset=True, merge_if_exists=True, formatter="python" + ), + ret, + ) - comt = ('A format is not supported') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(filestate.serialize(name, dataset=True, - formatter='A'), ret) + comt = "A format is not supported" + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + filestate.serialize(name, dataset=True, formatter="A"), ret + ) mock_changes = MagicMock(return_value=True) mock_no_changes = MagicMock(return_value=False) # __opts__['test']=True with changes - with patch.dict(filestate.__salt__, - {'file.check_managed_changes': mock_changes}): - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('Dataset will be serialized and stored into {0}' - .format(name)) - ret.update({'comment': comt, 'result': None, 'changes': True}) + with patch.dict( + filestate.__salt__, {"file.check_managed_changes": mock_changes} + ): + with patch.dict(filestate.__opts__, {"test": True}): + comt = "Dataset will be serialized and stored into {0}".format(name) + ret.update({"comment": comt, "result": None, "changes": True}) self.assertDictEqual( - filestate.serialize(name, dataset=True, - formatter='python'), ret) + filestate.serialize(name, dataset=True, formatter="python"), ret + ) # __opts__['test']=True without changes - with patch.dict(filestate.__salt__, - {'file.check_managed_changes': mock_no_changes}): - with patch.dict(filestate.__opts__, {'test': True}): - comt = ('The file {0} is in the correct state' - .format(name)) - ret.update({'comment': comt, 'result': True, 'changes': False}) + with patch.dict( + filestate.__salt__, {"file.check_managed_changes": mock_no_changes} + ): + with patch.dict(filestate.__opts__, {"test": True}): + comt = "The file {0} is in the correct state".format(name) + ret.update({"comment": comt, "result": True, "changes": False}) self.assertDictEqual( - filestate.serialize(name, - dataset=True, formatter='python'), ret) + filestate.serialize(name, dataset=True, formatter="python"), ret + ) mock = MagicMock(return_value=ret) - with patch.dict(filestate.__opts__, {'test': False}): - with patch.dict(filestate.__salt__, {'file.manage_file': mock}): - comt = ('Dataset will be serialized and stored into {0}' - .format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(filestate.serialize(name, dataset=True, - formatter='python'), - ret) + with patch.dict(filestate.__opts__, {"test": False}): + with patch.dict(filestate.__salt__, {"file.manage_file": mock}): + comt = "Dataset will be serialized and stored into {0}".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + filestate.serialize(name, dataset=True, formatter="python"), ret + ) # 'mknod' function tests: 1 def test_mknod(self): - ''' + """ Test to create a special file similar to the 'nix mknod command. - ''' - name = '/dev/AA' - ntype = 'a' + """ + name = "/dev/AA" + ntype = "a" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Must provide name to file.mknod') - ret.update({'comment': comt, 'name': ''}) - self.assertDictEqual(filestate.mknod('', ntype), ret) + comt = "Must provide name to file.mknod" + ret.update({"comment": comt, "name": ""}) + self.assertDictEqual(filestate.mknod("", ntype), ret) - comt = ("Node type unavailable: 'a'. Available node types are " - "character ('c'), block ('b'), and pipe ('p')") - ret.update({'comment': comt, 'name': name}) + comt = ( + "Node type unavailable: 'a'. Available node types are " + "character ('c'), block ('b'), and pipe ('p')" + ) + ret.update({"comment": comt, "name": name}) self.assertDictEqual(filestate.mknod(name, ntype), ret) # 'mod_run_check_cmd' function tests: 1 def test_mod_run_check_cmd(self): - ''' + """ Test to execute the check_cmd logic. - ''' - cmd = 'A' - filename = 'B' + """ + cmd = "A" + filename = "B" - ret = {'comment': 'check_cmd execution failed', - 'result': False, 'skip_watch': True} + ret = { + "comment": "check_cmd execution failed", + "result": False, + "skip_watch": True, + } - mock = MagicMock(side_effect=[{'retcode': 1}, {'retcode': 0}]) - with patch.dict(filestate.__salt__, {'cmd.run_all': mock}): - self.assertDictEqual(filestate.mod_run_check_cmd(cmd, filename), - ret) + mock = MagicMock(side_effect=[{"retcode": 1}, {"retcode": 0}]) + with patch.dict(filestate.__salt__, {"cmd.run_all": mock}): + self.assertDictEqual(filestate.mod_run_check_cmd(cmd, filename), ret) self.assertTrue(filestate.mod_run_check_cmd(cmd, filename)) @skipIf(not HAS_DATEUTIL, NO_DATEUTIL_REASON) def test_retention_schedule(self): - ''' + """ Test to execute the retention_schedule logic. This test takes advantage of knowing which files it is generating, which means it can easily generate list of which files it should keep. - ''' + """ - def generate_fake_files(format='example_name_%Y%m%dT%H%M%S.tar.bz2', - starting=datetime(2016, 2, 8, 9), - every=relativedelta(minutes=30), - ending=datetime(2015, 12, 25), - maxfiles=None): - ''' + def generate_fake_files( + format="example_name_%Y%m%dT%H%M%S.tar.bz2", + starting=datetime(2016, 2, 8, 9), + every=relativedelta(minutes=30), + ending=datetime(2015, 12, 25), + maxfiles=None, + ): + """ For starting, make sure that it's over a week from the beginning of the month For every, pick only one of minutes, hours, days, weeks, months or years For ending, the further away it is from starting, the slower the tests run Full coverage requires over a year of separation, but that's painfully slow. - ''' + """ if every.years: ts = datetime(starting.year, 1, 1) @@ -2096,9 +2460,13 @@ class TestFileState(TestCase, LoaderModuleMockMixin): elif every.days: ts = datetime(starting.year, starting.month, starting.day) elif every.hours: - ts = datetime(starting.year, starting.month, starting.day, starting.hour) + ts = datetime( + starting.year, starting.month, starting.day, starting.hour + ) elif every.minutes: - ts = datetime(starting.year, starting.month, starting.day, starting.hour, 0) + ts = datetime( + starting.year, starting.month, starting.day, starting.hour, 0 + ) else: raise NotImplementedError("not sure what you're trying to do here") @@ -2107,35 +2475,43 @@ class TestFileState(TestCase, LoaderModuleMockMixin): while ending < ts: fake_files.append(ts.strftime(format=format)) count += 1 - if maxfiles and maxfiles == 'all' or maxfiles and count >= maxfiles: + if maxfiles and maxfiles == "all" or maxfiles and count >= maxfiles: break ts -= every return fake_files - fake_name = '/some/dir/name' + fake_name = "/some/dir/name" fake_retain = { - 'most_recent': 2, - 'first_of_hour': 4, - 'first_of_day': 7, - 'first_of_week': 6, - 'first_of_month': 6, - 'first_of_year': 'all', + "most_recent": 2, + "first_of_hour": 4, + "first_of_day": 7, + "first_of_week": 6, + "first_of_month": 6, + "first_of_year": "all", } - fake_strptime_format = 'example_name_%Y%m%dT%H%M%S.tar.bz2' + fake_strptime_format = "example_name_%Y%m%dT%H%M%S.tar.bz2" fake_matching_file_list = generate_fake_files() # Add some files which do not match fake_strptime_format - fake_no_match_file_list = generate_fake_files(format='no_match_%Y%m%dT%H%M%S.tar.bz2', - every=relativedelta(days=1)) + fake_no_match_file_list = generate_fake_files( + format="no_match_%Y%m%dT%H%M%S.tar.bz2", every=relativedelta(days=1) + ) def lstat_side_effect(path): import re from time import mktime - x = re.match(r'^[^\d]*(\d{8}T\d{6})\.tar\.bz2$', path).group(1) - ts = mktime(datetime.strptime(x, '%Y%m%dT%H%M%S').timetuple()) - return {'st_atime': 0.0, 'st_ctime': 0.0, 'st_gid': 0, - 'st_mode': 33188, 'st_mtime': ts, - 'st_nlink': 1, 'st_size': 0, 'st_uid': 0, - } + + x = re.match(r"^[^\d]*(\d{8}T\d{6})\.tar\.bz2$", path).group(1) + ts = mktime(datetime.strptime(x, "%Y%m%dT%H%M%S").timetuple()) + return { + "st_atime": 0.0, + "st_ctime": 0.0, + "st_gid": 0, + "st_mode": 33188, + "st_mtime": ts, + "st_nlink": 1, + "st_size": 0, + "st_uid": 0, + } mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) @@ -2144,65 +2520,96 @@ class TestFileState(TestCase, LoaderModuleMockMixin): def run_checks(isdir=mock_t, strptime_format=None, test=False): expected_ret = { - 'name': fake_name, - 'changes': {'retained': [], 'deleted': [], 'ignored': []}, - 'result': True, - 'comment': 'Name provided to file.retention must be a directory', - } + "name": fake_name, + "changes": {"retained": [], "deleted": [], "ignored": []}, + "result": True, + "comment": "Name provided to file.retention must be a directory", + } if strptime_format: - fake_file_list = sorted(fake_matching_file_list + fake_no_match_file_list) + fake_file_list = sorted( + fake_matching_file_list + fake_no_match_file_list + ) else: fake_file_list = sorted(fake_matching_file_list) mock_readdir = MagicMock(return_value=fake_file_list) - with patch.dict(filestate.__opts__, {'test': test}): - with patch.object(os.path, 'isdir', isdir): + with patch.dict(filestate.__opts__, {"test": test}): + with patch.object(os.path, "isdir", isdir): mock_readdir.reset_mock() - with patch.dict(filestate.__salt__, {'file.readdir': mock_readdir}): - with patch.dict(filestate.__salt__, {'file.lstat': mock_lstat}): + with patch.dict(filestate.__salt__, {"file.readdir": mock_readdir}): + with patch.dict(filestate.__salt__, {"file.lstat": mock_lstat}): mock_remove.reset_mock() - with patch.dict(filestate.__salt__, {'file.remove': mock_remove}): + with patch.dict( + filestate.__salt__, {"file.remove": mock_remove} + ): if strptime_format: - actual_ret = filestate.retention_schedule(fake_name, fake_retain, - strptime_format=fake_strptime_format) + actual_ret = filestate.retention_schedule( + fake_name, + fake_retain, + strptime_format=fake_strptime_format, + ) else: - actual_ret = filestate.retention_schedule(fake_name, fake_retain) + actual_ret = filestate.retention_schedule( + fake_name, fake_retain + ) if not isdir(): mock_readdir.assert_has_calls([]) - expected_ret['result'] = False + expected_ret["result"] = False else: mock_readdir.assert_called_once_with(fake_name) ignored_files = fake_no_match_file_list if strptime_format else [] - retained_files = set(generate_fake_files(maxfiles=fake_retain['most_recent'])) - junk_list = [('first_of_hour', relativedelta(hours=1)), - ('first_of_day', relativedelta(days=1)), - ('first_of_week', relativedelta(weeks=1)), - ('first_of_month', relativedelta(months=1)), - ('first_of_year', relativedelta(years=1))] + retained_files = set( + generate_fake_files(maxfiles=fake_retain["most_recent"]) + ) + junk_list = [ + ("first_of_hour", relativedelta(hours=1)), + ("first_of_day", relativedelta(days=1)), + ("first_of_week", relativedelta(weeks=1)), + ("first_of_month", relativedelta(months=1)), + ("first_of_year", relativedelta(years=1)), + ] for retainable, retain_interval in junk_list: - new_retains = set(generate_fake_files(maxfiles=fake_retain[retainable], every=retain_interval)) + new_retains = set( + generate_fake_files( + maxfiles=fake_retain[retainable], every=retain_interval + ) + ) # if we generate less than the number of files expected, # then the oldest file will also be retained - # (correctly, since it's the first in its category) - if fake_retain[retainable] == 'all' or len(new_retains) < fake_retain[retainable]: + # (correctly, since its the first in it's category) + if ( + fake_retain[retainable] == "all" + or len(new_retains) < fake_retain[retainable] + ): new_retains.add(fake_file_list[0]) retained_files |= new_retains - deleted_files = sorted(list(set(fake_file_list) - retained_files - set(ignored_files)), reverse=True) + deleted_files = sorted( + list(set(fake_file_list) - retained_files - set(ignored_files)), + reverse=True, + ) retained_files = sorted(list(retained_files), reverse=True) - expected_ret['changes'] = {'retained': retained_files, 'deleted': deleted_files, 'ignored': ignored_files} + expected_ret["changes"] = { + "retained": retained_files, + "deleted": deleted_files, + "ignored": ignored_files, + } if test: - expected_ret['result'] = None - expected_ret['comment'] = ('{0} backups would have been removed from {1}.\n' - ''.format(len(deleted_files), fake_name)) + expected_ret["result"] = None + expected_ret["comment"] = ( + "{0} backups would have been removed from {1}.\n" + "".format(len(deleted_files), fake_name) + ) else: - expected_ret['comment'] = ('{0} backups were removed from {1}.\n' - ''.format(len(deleted_files), fake_name)) + expected_ret["comment"] = ( + "{0} backups were removed from {1}.\n" + "".format(len(deleted_files), fake_name) + ) mock_remove.assert_has_calls( - [call(os.path.join(fake_name, x)) for x in deleted_files], - any_order=True - ) + [call(os.path.join(fake_name, x)) for x in deleted_files], + any_order=True, + ) self.assertDictEqual(actual_ret, expected_ret) @@ -2214,157 +2621,168 @@ class TestFileState(TestCase, LoaderModuleMockMixin): class TestFindKeepFiles(TestCase): - - @skipIf(salt.utils.platform.is_windows(), 'Do not run on Windows') + @skipIf(salt.utils.platform.is_windows(), "Do not run on Windows") def test__find_keep_files_unix(self): keep = filestate._find_keep_files( - '/test/parent_folder', - ['/test/parent_folder/meh.txt'] + "/test/parent_folder", ["/test/parent_folder/meh.txt"] ) expected = [ - '/', - '/test', - '/test/parent_folder', - '/test/parent_folder/meh.txt', + "/", + "/test", + "/test/parent_folder", + "/test/parent_folder/meh.txt", ] actual = sorted(list(keep)) assert actual == expected, actual - @skipIf(not salt.utils.platform.is_windows(), 'Only run on Windows') + @skipIf(not salt.utils.platform.is_windows(), "Only run on Windows") def test__find_keep_files_win32(self): - ''' + """ Test _find_keep_files. The `_find_keep_files` function is only called by _clean_dir, so case doesn't matter. Should return all lower case. - ''' + """ keep = filestate._find_keep_files( - 'c:\\test\\parent_folder', - ['C:\\test\\parent_folder\\meh-1.txt', - 'C:\\Test\\Parent_folder\\Meh-2.txt'] + "c:\\test\\parent_folder", + [ + "C:\\test\\parent_folder\\meh-1.txt", + "C:\\Test\\Parent_folder\\Meh-2.txt", + ], ) expected = [ - 'c:\\', - 'c:\\test', - 'c:\\test\\parent_folder', - 'c:\\test\\parent_folder\\meh-1.txt', - 'c:\\test\\parent_folder\\meh-2.txt'] + "c:\\", + "c:\\test", + "c:\\test\\parent_folder", + "c:\\test\\parent_folder\\meh-1.txt", + "c:\\test\\parent_folder\\meh-2.txt", + ] actual = sorted(list(keep)) self.assertListEqual(actual, expected) class TestFileTidied(TestCase): def setUp(self): - setattr(filestate, '__opts__', {}) - setattr(filestate, '__salt__', {}) + setattr(filestate, "__opts__", {}) + setattr(filestate, "__salt__", {}) def tearDown(self): - delattr(filestate, '__opts__') - delattr(filestate, '__salt__') + delattr(filestate, "__opts__") + delattr(filestate, "__salt__") def test__tidied(self): - name = os.sep + 'test' + name = os.sep + "test" if salt.utils.platform.is_windows(): - name = 'c:' + name + name = "c:" + name walker = [ - (os.path.join('test', 'test1'), [], ['file1']), - (os.path.join('test', 'test2', 'test3'), [], []), - (os.path.join('test', 'test2'), ['test3'], ['file2']), - ('test', ['test1', 'test2'], ['file3'])] + (os.path.join("test", "test1"), [], ["file1"]), + (os.path.join("test", "test2", "test3"), [], []), + (os.path.join("test", "test2"), ["test3"], ["file2"]), + ("test", ["test1", "test2"], ["file3"]), + ] today_delta = datetime.today() - datetime.utcfromtimestamp(0) - remove = MagicMock(name='file.remove') - with patch('os.walk', return_value=walker), \ - patch('os.path.islink', return_value=False), \ - patch('os.path.getatime', return_value=today_delta.total_seconds()), \ - patch('os.path.getsize', return_value=10), \ - patch.dict(filestate.__opts__, {'test': False}), \ - patch.dict(filestate.__salt__, {'file.remove': remove}), \ - patch('os.path.isdir', return_value=True): + remove = MagicMock(name="file.remove") + with patch("os.walk", return_value=walker), patch( + "os.path.islink", return_value=False + ), patch("os.path.getatime", return_value=today_delta.total_seconds()), patch( + "os.path.getsize", return_value=10 + ), patch.dict( + filestate.__opts__, {"test": False} + ), patch.dict( + filestate.__salt__, {"file.remove": remove} + ), patch( + "os.path.isdir", return_value=True + ): ret = filestate.tidied(name=name) exp = { - 'name': name, - 'changes': { - 'removed': [ - os.path.join('test', 'test1', 'file1'), - os.path.join('test', 'test2', 'file2'), - os.path.join('test', 'file3')]}, - 'pchanges': {}, - 'result': True, - 'comment': 'Removed 3 files or directories from directory {0}'.format(name), + "name": name, + "changes": { + "removed": [ + os.path.join("test", "test1", "file1"), + os.path.join("test", "test2", "file2"), + os.path.join("test", "file3"), + ] + }, + "pchanges": {}, + "result": True, + "comment": "Removed 3 files or directories from directory {0}".format(name), } self.assertDictEqual(exp, ret) assert remove.call_count == 3 remove.reset_mock() - with patch('os.walk', return_value=walker), \ - patch('os.path.islink', return_value=False), \ - patch('os.path.getatime', return_value=today_delta.total_seconds()), \ - patch('os.path.getsize', return_value=10), \ - patch.dict(filestate.__opts__, {'test': False}), \ - patch.dict(filestate.__salt__, {'file.remove': remove}), \ - patch('os.path.isdir', return_value=True): + with patch("os.walk", return_value=walker), patch( + "os.path.islink", return_value=False + ), patch("os.path.getatime", return_value=today_delta.total_seconds()), patch( + "os.path.getsize", return_value=10 + ), patch.dict( + filestate.__opts__, {"test": False} + ), patch.dict( + filestate.__salt__, {"file.remove": remove} + ), patch( + "os.path.isdir", return_value=True + ): ret = filestate.tidied(name=name, rmdirs=True) exp = { - 'name': name, - 'changes': { - 'removed': [ - os.path.join('test', 'test1', 'file1'), - os.path.join('test', 'test2', 'file2'), - os.path.join('test', 'test2', 'test3'), - os.path.join('test', 'file3'), - os.path.join('test', 'test1'), - os.path.join('test', 'test2')]}, - 'pchanges': {}, - 'result': True, - 'comment': 'Removed 6 files or directories from directory {0}'.format(name), + "name": name, + "changes": { + "removed": [ + os.path.join("test", "test1", "file1"), + os.path.join("test", "test2", "file2"), + os.path.join("test", "test2", "test3"), + os.path.join("test", "file3"), + os.path.join("test", "test1"), + os.path.join("test", "test2"), + ] + }, + "pchanges": {}, + "result": True, + "comment": "Removed 6 files or directories from directory {0}".format(name), } self.assertDictEqual(exp, ret) assert remove.call_count == 6 def test__bad_input(self): exp = { - 'name': 'test/', - 'changes': {}, - 'pchanges': {}, - 'result': False, - 'comment': 'Specified file test/ is not an absolute path', + "name": "test/", + "changes": {}, + "pchanges": {}, + "result": False, + "comment": "Specified file test/ is not an absolute path", } - assert filestate.tidied(name='test/') == exp + assert filestate.tidied(name="test/") == exp exp = { - 'name': '/bad-directory-name/', - 'changes': {}, - 'pchanges': {}, - 'result': False, - 'comment': '/bad-directory-name/ does not exist or is not a directory.', + "name": "/bad-directory-name/", + "changes": {}, + "pchanges": {}, + "result": False, + "comment": "/bad-directory-name/ does not exist or is not a directory.", } - assert filestate.tidied(name='/bad-directory-name/') == exp + assert filestate.tidied(name="/bad-directory-name/") == exp class TestFilePrivateFunctions(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - return { - filestate: { - '__salt__': {'file.stats': filemod.stats}, - } - } + return {filestate: {"__salt__": {"file.stats": filemod.stats}}} @destructiveTest - @skipIf(salt.utils.platform.is_windows(), 'File modes do not exist on windows') + @skipIf(salt.utils.platform.is_windows(), "File modes do not exist on windows") def test__check_directory(self): - ''' + """ Test the _check_directory function Make sure that recursive file permission checks return correctly - ''' + """ # set file permissions # Run _check_directory function # Verify that it returns correctly # Delete tmp directory structure - root_tmp_dir = os.path.join(RUNTIME_VARS.TMP, 'test__check_dir') + root_tmp_dir = os.path.join(RUNTIME_VARS.TMP, "test__check_dir") expected_dir_mode = 0o777 depth = 3 try: + def create_files(tmp_dir): for f in range(depth): - path = os.path.join(tmp_dir, 'file_{:03}.txt'.format(f)) - with salt.utils.files.fopen(path, 'w+'): + path = os.path.join(tmp_dir, "file_{:03}.txt".format(f)) + with salt.utils.files.fopen(path, "w+"): os.chmod(path, expected_dir_mode) # Create tmp directory structure @@ -2373,30 +2791,34 @@ class TestFilePrivateFunctions(TestCase, LoaderModuleMockMixin): create_files(root_tmp_dir) for d in range(depth): - dir_name = os.path.join(root_tmp_dir, 'dir{:03}'.format(d)) + dir_name = os.path.join(root_tmp_dir, "dir{:03}".format(d)) os.mkdir(dir_name) os.chmod(dir_name, expected_dir_mode) create_files(dir_name) for s in range(depth): - sub_dir_name = os.path.join(dir_name, 'dir{:03}'.format(s)) + sub_dir_name = os.path.join(dir_name, "dir{:03}".format(s)) os.mkdir(sub_dir_name) os.chmod(sub_dir_name, expected_dir_mode) create_files(sub_dir_name) # Set some bad permissions changed_files = { - os.path.join(root_tmp_dir, 'file_000.txt'), - os.path.join(root_tmp_dir, 'dir002', 'file_000.txt'), - os.path.join(root_tmp_dir, 'dir000', 'dir001', 'file_002.txt'), - os.path.join(root_tmp_dir, 'dir001', 'dir002'), - os.path.join(root_tmp_dir, 'dir002', 'dir000'), - os.path.join(root_tmp_dir, 'dir001'), + os.path.join(root_tmp_dir, "file_000.txt"), + os.path.join(root_tmp_dir, "dir002", "file_000.txt"), + os.path.join(root_tmp_dir, "dir000", "dir001", "file_002.txt"), + os.path.join(root_tmp_dir, "dir001", "dir002"), + os.path.join(root_tmp_dir, "dir002", "dir000"), + os.path.join(root_tmp_dir, "dir001"), } for c in changed_files: os.chmod(c, 0o770) - ret = filestate._check_directory(root_tmp_dir, dir_mode=oct(expected_dir_mode), - file_mode=oct(expected_dir_mode), recurse=['mode']) + ret = filestate._check_directory( + root_tmp_dir, + dir_mode=oct(expected_dir_mode), + file_mode=oct(expected_dir_mode), + recurse=["mode"], + ) self.assertSetEqual(changed_files, set(ret[-1].keys())) finally: diff --git a/tests/unit/states/test_gem.py b/tests/unit/states/test_gem.py index 0f63a06f95f..170c24f77ce 100644 --- a/tests/unit/states/test_gem.py +++ b/tests/unit/states/test_gem.py @@ -1,120 +1,141 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +from __future__ import absolute_import, print_function, unicode_literals # Late import so mock can do its job import salt.states.gem as gem +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class TestGemState(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - return {gem: {'__opts__': {'test': False}}} + return {gem: {"__opts__": {"test": False}}} def test_installed(self): - gems = {'foo': ['1.0'], 'bar': ['2.0']} + gems = {"foo": ["1.0"], "bar": ["2.0"]} gem_list = MagicMock(return_value=gems) gem_install_succeeds = MagicMock(return_value=True) gem_install_fails = MagicMock(return_value=False) - with patch.dict(gem.__salt__, {'gem.list': gem_list}): - with patch.dict(gem.__salt__, - {'gem.install': gem_install_succeeds}): - ret = gem.installed('foo') - self.assertEqual(True, ret['result']) - ret = gem.installed('quux') - self.assertEqual(True, ret['result']) + with patch.dict(gem.__salt__, {"gem.list": gem_list}): + with patch.dict(gem.__salt__, {"gem.install": gem_install_succeeds}): + ret = gem.installed("foo") + self.assertEqual(True, ret["result"]) + ret = gem.installed("quux") + self.assertEqual(True, ret["result"]) gem_install_succeeds.assert_called_once_with( - 'quux', pre_releases=False, ruby=None, runas=None, - version=None, proxy=None, rdoc=False, source=None, - ri=False, gem_bin=None + "quux", + pre_releases=False, + ruby=None, + runas=None, + version=None, + proxy=None, + rdoc=False, + source=None, + ri=False, + gem_bin=None, ) - with patch.dict(gem.__salt__, - {'gem.install': gem_install_fails}): - ret = gem.installed('quux') - self.assertEqual(False, ret['result']) + with patch.dict(gem.__salt__, {"gem.install": gem_install_fails}): + ret = gem.installed("quux") + self.assertEqual(False, ret["result"]) gem_install_fails.assert_called_once_with( - 'quux', pre_releases=False, ruby=None, runas=None, - version=None, proxy=None, rdoc=False, source=None, - ri=False, gem_bin=None + "quux", + pre_releases=False, + ruby=None, + runas=None, + version=None, + proxy=None, + rdoc=False, + source=None, + ri=False, + gem_bin=None, ) def test_installed_version(self): - gems = {'foo': ['1.0'], 'bar': ['2.0']} + gems = {"foo": ["1.0"], "bar": ["2.0"]} gem_list = MagicMock(return_value=gems) gem_install_succeeds = MagicMock(return_value=True) - with patch.dict(gem.__salt__, {'gem.list': gem_list}): - with patch.dict(gem.__salt__, - {'gem.install': gem_install_succeeds}): - ret = gem.installed('foo', version='>= 1.0') - self.assertEqual(True, ret['result']) - self.assertEqual('Installed Gem meets version requirements.', - ret['comment']) + with patch.dict(gem.__salt__, {"gem.list": gem_list}): + with patch.dict(gem.__salt__, {"gem.install": gem_install_succeeds}): + ret = gem.installed("foo", version=">= 1.0") + self.assertEqual(True, ret["result"]) + self.assertEqual( + "Installed Gem meets version requirements.", ret["comment"] + ) def test_removed(self): - gems = ['foo', 'bar'] + gems = ["foo", "bar"] gem_list = MagicMock(return_value=gems) gem_uninstall_succeeds = MagicMock(return_value=True) gem_uninstall_fails = MagicMock(return_value=False) - with patch.dict(gem.__salt__, {'gem.list': gem_list}): - with patch.dict(gem.__salt__, - {'gem.uninstall': gem_uninstall_succeeds}): - ret = gem.removed('quux') - self.assertEqual(True, ret['result']) - ret = gem.removed('foo') - self.assertEqual(True, ret['result']) + with patch.dict(gem.__salt__, {"gem.list": gem_list}): + with patch.dict(gem.__salt__, {"gem.uninstall": gem_uninstall_succeeds}): + ret = gem.removed("quux") + self.assertEqual(True, ret["result"]) + ret = gem.removed("foo") + self.assertEqual(True, ret["result"]) gem_uninstall_succeeds.assert_called_once_with( - 'foo', None, runas=None, gem_bin=None) + "foo", None, runas=None, gem_bin=None + ) - with patch.dict(gem.__salt__, - {'gem.uninstall': gem_uninstall_fails}): - ret = gem.removed('bar') - self.assertEqual(False, ret['result']) + with patch.dict(gem.__salt__, {"gem.uninstall": gem_uninstall_fails}): + ret = gem.removed("bar") + self.assertEqual(False, ret["result"]) gem_uninstall_fails.assert_called_once_with( - 'bar', None, runas=None, gem_bin=None) + "bar", None, runas=None, gem_bin=None + ) def test_sources_add(self): - gem_sources = ['http://foo', 'http://bar'] + gem_sources = ["http://foo", "http://bar"] gem_sources_list = MagicMock(return_value=gem_sources) gem_sources_add_succeeds = MagicMock(return_value=True) gem_sources_add_fails = MagicMock(return_value=False) - with patch.dict(gem.__salt__, {'gem.sources_list': gem_sources_list}): - with patch.dict(gem.__salt__, {'gem.sources_add': gem_sources_add_succeeds}): - ret = gem.sources_add('http://foo') - self.assertEqual(True, ret['result']) - ret = gem.sources_add('http://fui') - self.assertEqual(True, ret['result']) + with patch.dict(gem.__salt__, {"gem.sources_list": gem_sources_list}): + with patch.dict( + gem.__salt__, {"gem.sources_add": gem_sources_add_succeeds} + ): + ret = gem.sources_add("http://foo") + self.assertEqual(True, ret["result"]) + ret = gem.sources_add("http://fui") + self.assertEqual(True, ret["result"]) gem_sources_add_succeeds.assert_called_once_with( - source_uri='http://fui', ruby=None, runas=None) - with patch.dict(gem.__salt__, {'gem.sources_add': gem_sources_add_fails}): - ret = gem.sources_add('http://fui') - self.assertEqual(False, ret['result']) + source_uri="http://fui", ruby=None, runas=None + ) + with patch.dict(gem.__salt__, {"gem.sources_add": gem_sources_add_fails}): + ret = gem.sources_add("http://fui") + self.assertEqual(False, ret["result"]) gem_sources_add_fails.assert_called_once_with( - source_uri='http://fui', ruby=None, runas=None) + source_uri="http://fui", ruby=None, runas=None + ) def test_sources_remove(self): - gem_sources = ['http://foo', 'http://bar'] + gem_sources = ["http://foo", "http://bar"] gem_sources_list = MagicMock(return_value=gem_sources) gem_sources_remove_succeeds = MagicMock(return_value=True) gem_sources_remove_fails = MagicMock(return_value=False) - with patch.dict(gem.__salt__, {'gem.sources_list': gem_sources_list}): - with patch.dict(gem.__salt__, {'gem.sources_remove': gem_sources_remove_succeeds}): - ret = gem.sources_remove('http://fui') - self.assertEqual(True, ret['result']) - ret = gem.sources_remove('http://foo') - self.assertEqual(True, ret['result']) + with patch.dict(gem.__salt__, {"gem.sources_list": gem_sources_list}): + with patch.dict( + gem.__salt__, {"gem.sources_remove": gem_sources_remove_succeeds} + ): + ret = gem.sources_remove("http://fui") + self.assertEqual(True, ret["result"]) + ret = gem.sources_remove("http://foo") + self.assertEqual(True, ret["result"]) gem_sources_remove_succeeds.assert_called_once_with( - source_uri='http://foo', ruby=None, runas=None) - with patch.dict(gem.__salt__, {'gem.sources_remove': gem_sources_remove_fails}): - ret = gem.sources_remove('http://bar') - self.assertEqual(False, ret['result']) + source_uri="http://foo", ruby=None, runas=None + ) + with patch.dict( + gem.__salt__, {"gem.sources_remove": gem_sources_remove_fails} + ): + ret = gem.sources_remove("http://bar") + self.assertEqual(False, ret["result"]) gem_sources_remove_fails.assert_called_once_with( - source_uri='http://bar', ruby=None, runas=None) + source_uri="http://bar", ruby=None, runas=None + ) diff --git a/tests/unit/states/test_git.py b/tests/unit/states/test_git.py index 3248f4d58fc..2135af1a1a3 100644 --- a/tests/unit/states/test_git.py +++ b/tests/unit/states/test_git.py @@ -1,82 +1,75 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Erik Johnson <erik@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import + import logging import os +# Import Salt Libs +import salt.states.git as git_state # Don't potentially shadow GitPython + # Import Salt Testing Libs from tests.support.helpers import with_tempdir from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import DEFAULT, MagicMock, Mock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch, - DEFAULT, -) - -# Import Salt Libs -import salt.states.git as git_state # Don't potentially shadow GitPython log = logging.getLogger(__name__) class GitTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.git - ''' + """ + def setup_loader_modules(self): return { - git_state: { - '__env__': 'base', - '__opts__': {'test': False}, - '__salt__': {}, - } + git_state: {"__env__": "base", "__opts__": {"test": False}, "__salt__": {}} } @with_tempdir() def test_latest_no_diff_for_bare_repo(self, target): - ''' + """ This test ensures that we don't attempt to diff when cloning a repo using either bare=True or mirror=True. - ''' - name = 'https://foo.com/bar/baz.git' - gitdir = os.path.join(target, 'refs') + """ + name = "https://foo.com/bar/baz.git" + gitdir = os.path.join(target, "refs") isdir_mock = MagicMock( - side_effect=lambda path: DEFAULT if path != gitdir else True) + side_effect=lambda path: DEFAULT if path != gitdir else True + ) - branches = ['foo', 'bar', 'baz'] - tags = ['v1.1.0', 'v.1.1.1', 'v1.2.0'] - local_head = 'b9ef06ab6b7524eb7c27d740dbbd5109c6d75ee4' - remote_head = 'eef672c1ec9b8e613905dbcd22a4612e31162807' + branches = ["foo", "bar", "baz"] + tags = ["v1.1.0", "v.1.1.1", "v1.2.0"] + local_head = "b9ef06ab6b7524eb7c27d740dbbd5109c6d75ee4" + remote_head = "eef672c1ec9b8e613905dbcd22a4612e31162807" git_diff = Mock() dunder_salt = { - 'git.current_branch': MagicMock(return_value=branches[0]), - 'git.config_get_regexp': MagicMock(return_value={}), - 'git.diff': git_diff, - 'git.fetch': MagicMock(return_value={}), - 'git.is_worktree': MagicMock(return_value=False), - 'git.list_branches': MagicMock(return_value=branches), - 'git.list_tags': MagicMock(return_value=tags), - 'git.remote_refs': MagicMock(return_value={'HEAD': remote_head}), - 'git.remotes': MagicMock(return_value={ - 'origin': {'fetch': name, 'push': name}, - }), - 'git.rev_parse': MagicMock(side_effect=git_state.CommandExecutionError()), - 'git.revision': MagicMock(return_value=local_head), - 'git.version': MagicMock(return_value='1.8.3.1'), + "git.current_branch": MagicMock(return_value=branches[0]), + "git.config_get_regexp": MagicMock(return_value={}), + "git.diff": git_diff, + "git.fetch": MagicMock(return_value={}), + "git.is_worktree": MagicMock(return_value=False), + "git.list_branches": MagicMock(return_value=branches), + "git.list_tags": MagicMock(return_value=tags), + "git.remote_refs": MagicMock(return_value={"HEAD": remote_head}), + "git.remotes": MagicMock( + return_value={"origin": {"fetch": name, "push": name}} + ), + "git.rev_parse": MagicMock(side_effect=git_state.CommandExecutionError()), + "git.revision": MagicMock(return_value=local_head), + "git.version": MagicMock(return_value="1.8.3.1"), } - with patch('os.path.isdir', isdir_mock), \ - patch.dict(git_state.__salt__, dunder_salt): + with patch("os.path.isdir", isdir_mock), patch.dict( + git_state.__salt__, dunder_salt + ): result = git_state.latest( - name=name, - target=target, - mirror=True, # mirror=True implies bare=True + name=name, target=target, mirror=True, # mirror=True implies bare=True ) - assert result['result'] is True, result + assert result["result"] is True, result git_diff.assert_not_called() diff --git a/tests/unit/states/test_glusterfs.py b/tests/unit/states/test_glusterfs.py index 795847e9f66..31d797b5413 100644 --- a/tests/unit/states/test_glusterfs.py +++ b/tests/unit/states/test_glusterfs.py @@ -1,417 +1,448 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +import salt.modules.glusterfs as mod_glusterfs # Import Salt Libs import salt.states.glusterfs as glusterfs import salt.utils.cloud import salt.utils.network -import salt.modules.glusterfs as mod_glusterfs + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class GlusterfsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.glusterfs - ''' + """ def setup_loader_modules(self): - return { - glusterfs: { - '__salt__': {'glusterfs.peer': mod_glusterfs.peer} - } - } + return {glusterfs: {"__salt__": {"glusterfs.peer": mod_glusterfs.peer}}} # 'peered' function tests: 1 def test_peered(self): - ''' + """ Test to verify if node is peered. - ''' - name = 'server1' + """ + name = "server1" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock_ip = MagicMock(return_value=['1.2.3.4', '1.2.3.5']) - mock_ip6 = MagicMock(return_value=['2001:db8::1']) - mock_host_ips = MagicMock(return_value=['1.2.3.5']) + mock_ip = MagicMock(return_value=["1.2.3.4", "1.2.3.5"]) + mock_ip6 = MagicMock(return_value=["2001:db8::1"]) + mock_host_ips = MagicMock(return_value=["1.2.3.5"]) mock_peer = MagicMock(return_value=True) - mock_status = MagicMock(return_value={'uuid1': {'hostnames': [name]}}) + mock_status = MagicMock(return_value={"uuid1": {"hostnames": [name]}}) - with patch.dict(glusterfs.__salt__, {'glusterfs.peer_status': mock_status, - 'glusterfs.peer': mock_peer}): - with patch.object(salt.utils.network, 'ip_addrs', mock_ip), \ - patch.object(salt.utils.network, 'ip_addrs6', mock_ip6), \ - patch.object(salt.utils.network, 'host_to_ips', mock_host_ips): - comt = 'Peering with localhost is not needed' - ret.update({'comment': comt}) + with patch.dict( + glusterfs.__salt__, + {"glusterfs.peer_status": mock_status, "glusterfs.peer": mock_peer}, + ): + with patch.object(salt.utils.network, "ip_addrs", mock_ip), patch.object( + salt.utils.network, "ip_addrs6", mock_ip6 + ), patch.object(salt.utils.network, "host_to_ips", mock_host_ips): + comt = "Peering with localhost is not needed" + ret.update({"comment": comt}) self.assertDictEqual(glusterfs.peered(name), ret) - mock_host_ips.return_value = ['2001:db8::1'] + mock_host_ips.return_value = ["2001:db8::1"] self.assertDictEqual(glusterfs.peered(name), ret) - mock_host_ips.return_value = ['1.2.3.42'] - comt = ('Host {0} already peered'.format(name)) - ret.update({'comment': comt}) + mock_host_ips.return_value = ["1.2.3.42"] + comt = "Host {0} already peered".format(name) + ret.update({"comment": comt}) self.assertDictEqual(glusterfs.peered(name), ret) - with patch.dict(glusterfs.__opts__, {'test': False}): - old = {'uuid1': {'hostnames': ['other1']}} - new = {'uuid1': {'hostnames': ['other1']}, - 'uuid2': {'hostnames': ['someAlias', name]}} + with patch.dict(glusterfs.__opts__, {"test": False}): + old = {"uuid1": {"hostnames": ["other1"]}} + new = { + "uuid1": {"hostnames": ["other1"]}, + "uuid2": {"hostnames": ["someAlias", name]}, + } mock_status.side_effect = [old, new] - comt = 'Host {0} successfully peered'.format(name) - ret.update({'comment': comt, - 'changes': {'old': old, 'new': new}}) + comt = "Host {0} successfully peered".format(name) + ret.update({"comment": comt, "changes": {"old": old, "new": new}}) self.assertDictEqual(glusterfs.peered(name), ret) mock_status.side_effect = None - mock_status.return_value = { - 'uuid1': {'hostnames': ['other']} - } + mock_status.return_value = {"uuid1": {"hostnames": ["other"]}} mock_peer.return_value = False - ret.update({'result': False}) + ret.update({"result": False}) - comt = ('Failed to peer with {0},' - + ' please check logs for errors').format(name) - ret.update({'comment': comt, 'changes': {}}) + comt = ( + "Failed to peer with {0}," + " please check logs for errors" + ).format(name) + ret.update({"comment": comt, "changes": {}}) self.assertDictEqual(glusterfs.peered(name), ret) - comt = ('Invalid characters in peer name.') - ret.update({'comment': comt, 'name': ':/'}) - self.assertDictEqual(glusterfs.peered(':/'), ret) - ret.update({'name': name}) + comt = "Invalid characters in peer name." + ret.update({"comment": comt, "name": ":/"}) + self.assertDictEqual(glusterfs.peered(":/"), ret) + ret.update({"name": name}) - with patch.dict(glusterfs.__opts__, {'test': True}): - comt = ('Peer {0} will be added.'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(glusterfs.__opts__, {"test": True}): + comt = "Peer {0} will be added.".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(glusterfs.peered(name), ret) # 'volume_present' function tests: 1 def test_volume_present(self): - ''' + """ Test to ensure that a volume exists - ''' - name = 'salt' - bricks = ['host1:/brick1'] - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + name = "salt" + bricks = ["host1:/brick1"] + ret = {"name": name, "result": True, "comment": "", "changes": {}} - started_info = {name: {'status': '1'}} - stopped_info = {name: {'status': '0'}} + started_info = {name: {"status": "1"}} + stopped_info = {name: {"status": "0"}} mock_info = MagicMock() mock_list = MagicMock() mock_create = MagicMock() mock_start = MagicMock(return_value=True) - with patch.dict(glusterfs.__salt__, { - 'glusterfs.info': mock_info, - 'glusterfs.list_volumes': mock_list, - 'glusterfs.create_volume': mock_create, - 'glusterfs.start_volume': mock_start}): - with patch.dict(glusterfs.__opts__, {'test': False}): + with patch.dict( + glusterfs.__salt__, + { + "glusterfs.info": mock_info, + "glusterfs.list_volumes": mock_list, + "glusterfs.create_volume": mock_create, + "glusterfs.start_volume": mock_start, + }, + ): + with patch.dict(glusterfs.__opts__, {"test": False}): mock_list.return_value = [name] mock_info.return_value = started_info - comt = ('Volume {0} already exists and is started'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(glusterfs.volume_present(name, bricks, - start=True), ret) + comt = "Volume {0} already exists and is started".format(name) + ret.update({"comment": comt}) + self.assertDictEqual( + glusterfs.volume_present(name, bricks, start=True), ret + ) mock_info.return_value = stopped_info - comt = ('Volume {0} already exists and is now started'.format(name)) - ret.update({'comment': comt, - 'changes': {'old': 'stopped', 'new': 'started'}}) - self.assertDictEqual(glusterfs.volume_present(name, bricks, - start=True), ret) + comt = "Volume {0} already exists and is now started".format(name) + ret.update( + {"comment": comt, "changes": {"old": "stopped", "new": "started"}} + ) + self.assertDictEqual( + glusterfs.volume_present(name, bricks, start=True), ret + ) - comt = ('Volume {0} already exists'.format(name)) - ret.update({'comment': comt, 'changes': {}}) - self.assertDictEqual(glusterfs.volume_present(name, bricks, - start=False), ret) - with patch.dict(glusterfs.__opts__, {'test': True}): - comt = ('Volume {0} already exists'.format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(glusterfs.volume_present(name, bricks, - start=False), ret) + comt = "Volume {0} already exists".format(name) + ret.update({"comment": comt, "changes": {}}) + self.assertDictEqual( + glusterfs.volume_present(name, bricks, start=False), ret + ) + with patch.dict(glusterfs.__opts__, {"test": True}): + comt = "Volume {0} already exists".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + glusterfs.volume_present(name, bricks, start=False), ret + ) - comt = ('Volume {0} already exists' - + ' and will be started').format(name) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(glusterfs.volume_present(name, bricks, - start=True), ret) + comt = ("Volume {0} already exists" + " and will be started").format( + name + ) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + glusterfs.volume_present(name, bricks, start=True), ret + ) mock_list.return_value = [] - comt = ('Volume {0} will be created'.format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(glusterfs.volume_present(name, bricks, - start=False), ret) + comt = "Volume {0} will be created".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + glusterfs.volume_present(name, bricks, start=False), ret + ) - comt = ('Volume {0} will be created' - + ' and started').format(name) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(glusterfs.volume_present(name, bricks, - start=True), ret) + comt = ("Volume {0} will be created" + " and started").format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + glusterfs.volume_present(name, bricks, start=True), ret + ) - with patch.dict(glusterfs.__opts__, {'test': False}): + with patch.dict(glusterfs.__opts__, {"test": False}): mock_list.side_effect = [[], [name]] - comt = ('Volume {0} is created'.format(name)) - ret.update({'comment': comt, - 'result': True, - 'changes': {'old': [], 'new': [name]}}) - self.assertDictEqual(glusterfs.volume_present(name, bricks, - start=False), ret) + comt = "Volume {0} is created".format(name) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"old": [], "new": [name]}, + } + ) + self.assertDictEqual( + glusterfs.volume_present(name, bricks, start=False), ret + ) mock_list.side_effect = [[], [name]] - comt = ('Volume {0} is created and is now started'.format(name)) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(glusterfs.volume_present(name, bricks, - start=True), ret) + comt = "Volume {0} is created and is now started".format(name) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + glusterfs.volume_present(name, bricks, start=True), ret + ) mock_list.side_effect = None mock_list.return_value = [] mock_create.return_value = False - comt = 'Creation of volume {0} failed'.format(name) - ret.update({'comment': comt, 'result': False, 'changes': {}}) - self.assertDictEqual(glusterfs.volume_present(name, bricks), - ret) + comt = "Creation of volume {0} failed".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) + self.assertDictEqual(glusterfs.volume_present(name, bricks), ret) - with patch.object(salt.utils.cloud, 'check_name', - MagicMock(return_value=True)): - comt = ('Invalid characters in volume name.') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(glusterfs.volume_present(name, bricks), - ret) + with patch.object( + salt.utils.cloud, "check_name", MagicMock(return_value=True) + ): + comt = "Invalid characters in volume name." + ret.update({"comment": comt, "result": False}) + self.assertDictEqual(glusterfs.volume_present(name, bricks), ret) # 'started' function tests: 1 def test_started(self): - ''' + """ Test to check if volume has been started - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - started_info = {name: {'status': '1'}} - stopped_info = {name: {'status': '0'}} + started_info = {name: {"status": "1"}} + stopped_info = {name: {"status": "0"}} mock_info = MagicMock(return_value={}) mock_start = MagicMock(return_value=True) - with patch.dict(glusterfs.__salt__, - {'glusterfs.info': mock_info, - 'glusterfs.start_volume': mock_start}): - comt = ('Volume {0} does not exist'.format(name)) - ret.update({'comment': comt}) + with patch.dict( + glusterfs.__salt__, + {"glusterfs.info": mock_info, "glusterfs.start_volume": mock_start}, + ): + comt = "Volume {0} does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(glusterfs.started(name), ret) mock_info.return_value = started_info - comt = ('Volume {0} is already started'.format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "Volume {0} is already started".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(glusterfs.started(name), ret) - with patch.dict(glusterfs.__opts__, {'test': True}): + with patch.dict(glusterfs.__opts__, {"test": True}): mock_info.return_value = stopped_info - comt = ('Volume {0} will be started'.format(name)) - ret.update({'comment': comt, 'result': None}) + comt = "Volume {0} will be started".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(glusterfs.started(name), ret) - with patch.dict(glusterfs.__opts__, {'test': False}): - comt = 'Volume {0} is started'.format(name) - ret.update({'comment': comt, 'result': True, - 'change': {'new': 'started', 'old': 'stopped'}}) + with patch.dict(glusterfs.__opts__, {"test": False}): + comt = "Volume {0} is started".format(name) + ret.update( + { + "comment": comt, + "result": True, + "change": {"new": "started", "old": "stopped"}, + } + ) self.assertDictEqual(glusterfs.started(name), ret) # 'add_volume_bricks' function tests: 1 def test_add_volume_bricks(self): - ''' + """ Test to add brick(s) to an existing volume - ''' - name = 'salt' - bricks = ['host1:/drive1'] - old_bricks = ['host1:/drive2'] + """ + name = "salt" + bricks = ["host1:/drive1"] + old_bricks = ["host1:/drive2"] - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - stopped_volinfo = {'salt': {'status': '0'}} + stopped_volinfo = {"salt": {"status": "0"}} volinfo = { - 'salt': { - 'status': '1', - 'bricks': {'brick1': {'path': old_bricks[0]}} - } + "salt": {"status": "1", "bricks": {"brick1": {"path": old_bricks[0]}}} } new_volinfo = { - 'salt': { - 'status': '1', - 'bricks': { - 'brick1': {'path': old_bricks[0]}, - 'brick2': {'path': bricks[0]} - } + "salt": { + "status": "1", + "bricks": { + "brick1": {"path": old_bricks[0]}, + "brick2": {"path": bricks[0]}, + }, } } mock_info = MagicMock(return_value={}) mock_add = MagicMock(side_effect=[False, True]) - with patch.dict(glusterfs.__salt__, - {'glusterfs.info': mock_info, - 'glusterfs.add_volume_bricks': mock_add}): - ret.update({'comment': 'Volume salt does not exist'}) + with patch.dict( + glusterfs.__salt__, + {"glusterfs.info": mock_info, "glusterfs.add_volume_bricks": mock_add}, + ): + ret.update({"comment": "Volume salt does not exist"}) self.assertDictEqual(glusterfs.add_volume_bricks(name, bricks), ret) mock_info.return_value = stopped_volinfo - ret.update({'comment': 'Volume salt is not started'}) + ret.update({"comment": "Volume salt is not started"}) self.assertDictEqual(glusterfs.add_volume_bricks(name, bricks), ret) mock_info.return_value = volinfo - ret.update({'comment': 'Adding bricks to volume salt failed'}) + ret.update({"comment": "Adding bricks to volume salt failed"}) self.assertDictEqual(glusterfs.add_volume_bricks(name, bricks), ret) - ret.update({'result': True}) - ret.update({'comment': 'Bricks already added in volume salt'}) - self.assertDictEqual(glusterfs.add_volume_bricks(name, old_bricks), - ret) + ret.update({"result": True}) + ret.update({"comment": "Bricks already added in volume salt"}) + self.assertDictEqual(glusterfs.add_volume_bricks(name, old_bricks), ret) mock_info.side_effect = [volinfo, new_volinfo] - ret.update({'comment': 'Bricks successfully added to volume salt', - 'changes': {'new': bricks + old_bricks, - 'old': old_bricks}}) + ret.update( + { + "comment": "Bricks successfully added to volume salt", + "changes": {"new": bricks + old_bricks, "old": old_bricks}, + } + ) # Let's sort ourselves because the test under python 3 sometimes fails # just because of the new changes list order result = glusterfs.add_volume_bricks(name, bricks) - ret['changes']['new'] = sorted(ret['changes']['new']) - result['changes']['new'] = sorted(result['changes']['new']) + ret["changes"]["new"] = sorted(ret["changes"]["new"]) + result["changes"]["new"] = sorted(result["changes"]["new"]) self.assertDictEqual(result, ret) # 'op_version' function tests: 1 def test_op_version(self): - ''' + """ Test setting the Glusterfs op-version - ''' - name = 'salt' + """ + name = "salt" current = 30707 new = 31200 - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock_get_version = MagicMock(return_value={}) mock_set_version = MagicMock(return_value={}) - with patch.dict(glusterfs.__salt__, - {'glusterfs.get_op_version': mock_get_version, - 'glusterfs.set_op_version': mock_set_version}): - mock_get_version.return_value = [False, 'some error message'] - ret.update({'result': False}) - ret.update({'comment': 'some error message'}) + with patch.dict( + glusterfs.__salt__, + { + "glusterfs.get_op_version": mock_get_version, + "glusterfs.set_op_version": mock_set_version, + }, + ): + mock_get_version.return_value = [False, "some error message"] + ret.update({"result": False}) + ret.update({"comment": "some error message"}) self.assertDictEqual(glusterfs.op_version(name, current), ret) mock_get_version.return_value = current - ret.update({'result': True}) - ret.update({'comment': 'Glusterfs cluster.op-version for {0} already set to {1}'.format(name, current)}) + ret.update({"result": True}) + ret.update( + { + "comment": "Glusterfs cluster.op-version for {0} already set to {1}".format( + name, current + ) + } + ) self.assertDictEqual(glusterfs.op_version(name, current), ret) - with patch.dict(glusterfs.__opts__, {'test': True}): - mock_set_version.return_value = [False, 'Failed to set version'] - ret.update({'result': None}) - ret.update({'comment': 'An attempt would be made to set the cluster.op-version for {0} to {1}.'. - format(name, new)}) + with patch.dict(glusterfs.__opts__, {"test": True}): + mock_set_version.return_value = [False, "Failed to set version"] + ret.update({"result": None}) + ret.update( + { + "comment": "An attempt would be made to set the cluster.op-version for {0} to {1}.".format( + name, new + ) + } + ) self.assertDictEqual(glusterfs.op_version(name, new), ret) - with patch.dict(glusterfs.__opts__, {'test': False}): - mock_set_version.return_value = [False, 'Failed to set version'] - ret.update({'result': False}) - ret.update({'comment': 'Failed to set version'}) + with patch.dict(glusterfs.__opts__, {"test": False}): + mock_set_version.return_value = [False, "Failed to set version"] + ret.update({"result": False}) + ret.update({"comment": "Failed to set version"}) self.assertDictEqual(glusterfs.op_version(name, new), ret) - mock_set_version.return_value = 'some success message' - ret.update({'comment': 'some success message'}) - ret.update({'changes': {'old': current, 'new': new}}) - ret.update({'result': True}) + mock_set_version.return_value = "some success message" + ret.update({"comment": "some success message"}) + ret.update({"changes": {"old": current, "new": new}}) + ret.update({"result": True}) self.assertDictEqual(glusterfs.op_version(name, new), ret) # 'max_op_version' function tests: 1 def test_max_op_version(self): - ''' + """ Test setting the Glusterfs to its self reported max-op-version - ''' - name = 'salt' + """ + name = "salt" current = 30707 new = 31200 - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock_get_version = MagicMock(return_value={}) mock_get_max_op_version = MagicMock(return_value={}) mock_set_version = MagicMock(return_value={}) - with patch.dict(glusterfs.__salt__, - {'glusterfs.get_op_version': mock_get_version, - 'glusterfs.set_op_version': mock_set_version, - 'glusterfs.get_max_op_version': mock_get_max_op_version}): - mock_get_version.return_value = [False, 'some error message'] - ret.update({'result': False}) - ret.update({'comment': 'some error message'}) + with patch.dict( + glusterfs.__salt__, + { + "glusterfs.get_op_version": mock_get_version, + "glusterfs.set_op_version": mock_set_version, + "glusterfs.get_max_op_version": mock_get_max_op_version, + }, + ): + mock_get_version.return_value = [False, "some error message"] + ret.update({"result": False}) + ret.update({"comment": "some error message"}) self.assertDictEqual(glusterfs.max_op_version(name), ret) mock_get_version.return_value = current - mock_get_max_op_version.return_value = [False, 'some error message'] - ret.update({'result': False}) - ret.update({'comment': 'some error message'}) + mock_get_max_op_version.return_value = [False, "some error message"] + ret.update({"result": False}) + ret.update({"comment": "some error message"}) self.assertDictEqual(glusterfs.max_op_version(name), ret) mock_get_version.return_value = current mock_get_max_op_version.return_value = current - ret.update({'result': True}) - ret.update({'comment': 'The cluster.op-version is already set to the cluster.max-op-version of {0}'. - format(current)}) + ret.update({"result": True}) + ret.update( + { + "comment": "The cluster.op-version is already set to the cluster.max-op-version of {0}".format( + current + ) + } + ) self.assertDictEqual(glusterfs.max_op_version(name), ret) - with patch.dict(glusterfs.__opts__, {'test': True}): + with patch.dict(glusterfs.__opts__, {"test": True}): mock_get_max_op_version.return_value = new - ret.update({'result': None}) - ret.update({'comment': 'An attempt would be made to set the cluster.op-version to {0}.'. - format(new)}) + ret.update({"result": None}) + ret.update( + { + "comment": "An attempt would be made to set the cluster.op-version to {0}.".format( + new + ) + } + ) self.assertDictEqual(glusterfs.max_op_version(name), ret) - with patch.dict(glusterfs.__opts__, {'test': False}): - mock_set_version.return_value = [False, 'Failed to set version'] - ret.update({'result': False}) - ret.update({'comment': 'Failed to set version'}) + with patch.dict(glusterfs.__opts__, {"test": False}): + mock_set_version.return_value = [False, "Failed to set version"] + ret.update({"result": False}) + ret.update({"comment": "Failed to set version"}) self.assertDictEqual(glusterfs.max_op_version(name), ret) - mock_set_version.return_value = 'some success message' - ret.update({'comment': 'some success message'}) - ret.update({'changes': {'old': current, 'new': new}}) - ret.update({'result': True}) + mock_set_version.return_value = "some success message" + ret.update({"comment": "some success message"}) + ret.update({"changes": {"old": current, "new": new}}) + ret.update({"result": True}) self.assertDictEqual(glusterfs.max_op_version(name), ret) diff --git a/tests/unit/states/test_gnomedesktop.py b/tests/unit/states/test_gnomedesktop.py index 912ed99e4db..7ffe78db73b 100644 --- a/tests/unit/states/test_gnomedesktop.py +++ b/tests/unit/states/test_gnomedesktop.py @@ -1,62 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase - # Import Salt Libs import salt.states.gnomedesktop as gnomedesktop +# Import Salt Testing Libs +from tests.support.unit import TestCase + class GnomedesktopTestCase(TestCase): - ''' + """ Test cases for salt.states.gnomedesktop - ''' + """ + # 'wm_preferences' function tests: 1 def test_wm_preferences(self): - ''' + """ Test to sets values in the org.gnome.desktop.wm.preferences schema - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} self.assertDictEqual(gnomedesktop.wm_preferences(name), ret) # 'desktop_lockdown' function tests: 1 def test_desktop_lockdown(self): - ''' + """ Test to sets values in the org.gnome.desktop.lockdown schema - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} self.assertDictEqual(gnomedesktop.desktop_lockdown(name), ret) # 'desktop_interface' function tests: 1 def test_desktop_interface(self): - ''' + """ Test to sets values in the org.gnome.desktop.interface schema - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} self.assertDictEqual(gnomedesktop.desktop_interface(name), ret) diff --git a/tests/unit/states/test_grafana.py b/tests/unit/states/test_grafana.py index 83b9a7791d9..4e871a0f761 100644 --- a/tests/unit/states/test_grafana.py +++ b/tests/unit/states/test_grafana.py @@ -1,138 +1,151 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import salt.states.grafana as grafana # Import Salt Libs import salt.utils.json -import salt.states.grafana as grafana from salt.exceptions import SaltInvocationError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class GrafanaTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.grafana - ''' + """ + def setup_loader_modules(self): return {grafana: {}} # 'dashboard_present' function tests: 1 def test_dashboard_present(self): - ''' + """ Test to ensure the grafana dashboard exists and is managed. - ''' - name = 'myservice' - rows = ['systemhealth', 'requests', 'title'] - row = [{'panels': [{'id': 'a'}], 'title': 'systemhealth'}] + """ + name = "myservice" + rows = ["systemhealth", "requests", "title"] + row = [{"panels": [{"id": "a"}], "title": "systemhealth"}] - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} - comt1 = ('Dashboard myservice is set to be updated. The following rows ' - 'set to be updated: {0}'.format(['systemhealth'])) - self.assertRaises(SaltInvocationError, grafana.dashboard_present, name, - profile=False) + comt1 = ( + "Dashboard myservice is set to be updated. The following rows " + "set to be updated: {0}".format(["systemhealth"]) + ) + self.assertRaises( + SaltInvocationError, grafana.dashboard_present, name, profile=False + ) - self.assertRaises(SaltInvocationError, grafana.dashboard_present, name, - True, True) + self.assertRaises( + SaltInvocationError, grafana.dashboard_present, name, True, True + ) - mock = MagicMock(side_effect=[{'hosts': True, 'index': False}, - {'hosts': True, 'index': True}, - {'hosts': True, 'index': True}, - {'hosts': True, 'index': True}, - {'hosts': True, 'index': True}, - {'hosts': True, 'index': True}, - {'hosts': True, 'index': True}]) + mock = MagicMock( + side_effect=[ + {"hosts": True, "index": False}, + {"hosts": True, "index": True}, + {"hosts": True, "index": True}, + {"hosts": True, "index": True}, + {"hosts": True, "index": True}, + {"hosts": True, "index": True}, + {"hosts": True, "index": True}, + ] + ) mock_f = MagicMock(side_effect=[False, False, True, True, True, True]) - mock_t = MagicMock(return_value='') + mock_t = MagicMock(return_value="") mock_i = MagicMock(return_value=False) - source = {'dashboard': '["rows", {"rows":["baz", null, 1.0, 2]}]'} - mock_dict = MagicMock(return_value={'_source': source}) - with patch.dict(grafana.__salt__, {'config.option': mock, - 'elasticsearch.exists': mock_f, - 'pillar.get': mock_t, - 'elasticsearch.get': mock_dict, - 'elasticsearch.index': mock_i}): - self.assertRaises(SaltInvocationError, grafana.dashboard_present, - name) + source = {"dashboard": '["rows", {"rows":["baz", null, 1.0, 2]}]'} + mock_dict = MagicMock(return_value={"_source": source}) + with patch.dict( + grafana.__salt__, + { + "config.option": mock, + "elasticsearch.exists": mock_f, + "pillar.get": mock_t, + "elasticsearch.get": mock_dict, + "elasticsearch.index": mock_i, + }, + ): + self.assertRaises(SaltInvocationError, grafana.dashboard_present, name) - with patch.dict(grafana.__opts__, {'test': True}): - self.assertRaises(SaltInvocationError, grafana.dashboard_present, - name) + with patch.dict(grafana.__opts__, {"test": True}): + self.assertRaises(SaltInvocationError, grafana.dashboard_present, name) - comt = ('Dashboard {0} is set to be created.'.format(name)) - ret.update({'comment': comt}) + comt = "Dashboard {0} is set to be created.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(grafana.dashboard_present(name, True), ret) - mock = MagicMock(return_value={'rows': - [{'panels': 'b', - 'title': 'systemhealth'}]}) - with patch.object(salt.utils.json, 'loads', mock): - ret.update({'comment': comt1, 'result': None}) - self.assertDictEqual(grafana.dashboard_present(name, True, - rows=row), - ret) + mock = MagicMock( + return_value={"rows": [{"panels": "b", "title": "systemhealth"}]} + ) + with patch.object(salt.utils.json, "loads", mock): + ret.update({"comment": comt1, "result": None}) + self.assertDictEqual( + grafana.dashboard_present(name, True, rows=row), ret + ) - with patch.object(salt.utils.json, 'loads', - MagicMock(return_value={'rows': {}})): - self.assertRaises(SaltInvocationError, - grafana.dashboard_present, name, - rows_from_pillar=rows) + with patch.object( + salt.utils.json, "loads", MagicMock(return_value={"rows": {}}) + ): + self.assertRaises( + SaltInvocationError, + grafana.dashboard_present, + name, + rows_from_pillar=rows, + ) - comt = ('Dashboard myservice is up to date') - ret.update({'comment': comt, 'result': True}) + comt = "Dashboard myservice is up to date" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(grafana.dashboard_present(name, True), ret) - mock = MagicMock(return_value={'rows': [{'panels': 'b', - 'title': 'systemhealth'}]}) - with patch.dict(grafana.__opts__, {'test': False}): - with patch.object(salt.utils.json, 'loads', mock): - comt = ('Failed to update dashboard myservice.') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(grafana.dashboard_present(name, True, - rows=row), - ret) + mock = MagicMock( + return_value={"rows": [{"panels": "b", "title": "systemhealth"}]} + ) + with patch.dict(grafana.__opts__, {"test": False}): + with patch.object(salt.utils.json, "loads", mock): + comt = "Failed to update dashboard myservice." + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + grafana.dashboard_present(name, True, rows=row), ret + ) # 'dashboard_absent' function tests: 1 def test_dashboard_absent(self): - ''' + """ Test to ensure the named grafana dashboard is deleted. - ''' - name = 'myservice' + """ + name = "myservice" - ret = {'name': name, - 'result': None, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": None, "changes": {}, "comment": ""} - mock = MagicMock(side_effect=[{'hosts': True, 'index': False}, - {'hosts': True, 'index': True}, - {'hosts': True, 'index': True}]) + mock = MagicMock( + side_effect=[ + {"hosts": True, "index": False}, + {"hosts": True, "index": True}, + {"hosts": True, "index": True}, + ] + ) mock_f = MagicMock(side_effect=[True, False]) - with patch.dict(grafana.__salt__, {'config.option': mock, - 'elasticsearch.exists': mock_f}): - self.assertRaises(SaltInvocationError, grafana.dashboard_absent, - name) + with patch.dict( + grafana.__salt__, {"config.option": mock, "elasticsearch.exists": mock_f} + ): + self.assertRaises(SaltInvocationError, grafana.dashboard_absent, name) - with patch.dict(grafana.__opts__, {'test': True}): - comt = ('Dashboard myservice is set to be removed.') - ret.update({'comment': comt, 'result': None}) + with patch.dict(grafana.__opts__, {"test": True}): + comt = "Dashboard myservice is set to be removed." + ret.update({"comment": comt, "result": None}) self.assertDictEqual(grafana.dashboard_absent(name), ret) - comt = ('Dashboard myservice does not exist.') - ret.update({'comment': comt, 'result': True}) + comt = "Dashboard myservice does not exist." + ret.update({"comment": comt, "result": True}) self.assertDictEqual(grafana.dashboard_absent(name), ret) diff --git a/tests/unit/states/test_grafana_datasource.py b/tests/unit/states/test_grafana_datasource.py index aef1f8a4a33..a789374c964 100644 --- a/tests/unit/states/test_grafana_datasource.py +++ b/tests/unit/states/test_grafana_datasource.py @@ -2,21 +2,17 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch -) - # Import Salt Libs import salt.states.grafana_datasource as grafana_datasource +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase + profile = { - 'grafana_url': 'http://grafana', - 'grafana_token': 'token', + "grafana_url": "http://grafana", + "grafana_token": "token", } @@ -31,60 +27,76 @@ class GrafanaDatasourceTestCase(TestCase, LoaderModuleMockMixin): return {grafana_datasource: {}} def test_present(self): - with patch('requests.get', mock_json_response([])): - with patch('requests.post') as rpost: - ret = grafana_datasource.present('test', 'type', 'url', profile=profile) + with patch("requests.get", mock_json_response([])): + with patch("requests.post") as rpost: + ret = grafana_datasource.present("test", "type", "url", profile=profile) rpost.assert_called_once_with( - 'http://grafana/api/datasources', - grafana_datasource._get_json_data('test', 'type', 'url'), - headers={'Authorization': 'Bearer token', 'Accept': 'application/json'}, - timeout=3 + "http://grafana/api/datasources", + grafana_datasource._get_json_data("test", "type", "url"), + headers={ + "Authorization": "Bearer token", + "Accept": "application/json", + }, + timeout=3, ) - self.assertTrue(ret['result']) - self.assertEqual(ret['comment'], 'New data source test added') + self.assertTrue(ret["result"]) + self.assertEqual(ret["comment"], "New data source test added") - data = grafana_datasource._get_json_data('test', 'type', 'url') - data.update({'id': 1, 'orgId': 1}) - with patch('requests.get', mock_json_response([data])): - with patch('requests.put') as rput: - ret = grafana_datasource.present('test', 'type', 'url', profile=profile) + data = grafana_datasource._get_json_data("test", "type", "url") + data.update({"id": 1, "orgId": 1}) + with patch("requests.get", mock_json_response([data])): + with patch("requests.put") as rput: + ret = grafana_datasource.present("test", "type", "url", profile=profile) rput.assert_called_once_with( - 'http://grafana/api/datasources/1', - grafana_datasource._get_json_data('test', 'type', 'url'), - headers={'Authorization': 'Bearer token', 'Accept': 'application/json'}, - timeout=3 + "http://grafana/api/datasources/1", + grafana_datasource._get_json_data("test", "type", "url"), + headers={ + "Authorization": "Bearer token", + "Accept": "application/json", + }, + timeout=3, ) - self.assertTrue(ret['result']) - self.assertEqual(ret['comment'], 'Data source test already up-to-date') - self.assertEqual(ret['changes'], {}) + self.assertTrue(ret["result"]) + self.assertEqual(ret["comment"], "Data source test already up-to-date") + self.assertEqual(ret["changes"], {}) - with patch('requests.put') as rput: - ret = grafana_datasource.present('test', 'type', 'newurl', profile=profile) - rput.assert_called_once_with( - 'http://grafana/api/datasources/1', - grafana_datasource._get_json_data('test', 'type', 'newurl'), - headers={'Authorization': 'Bearer token', 'Accept': 'application/json'}, - timeout=3 + with patch("requests.put") as rput: + ret = grafana_datasource.present( + "test", "type", "newurl", profile=profile + ) + rput.assert_called_once_with( + "http://grafana/api/datasources/1", + grafana_datasource._get_json_data("test", "type", "newurl"), + headers={ + "Authorization": "Bearer token", + "Accept": "application/json", + }, + timeout=3, + ) + self.assertTrue(ret["result"]) + self.assertEqual(ret["comment"], "Data source test updated") + self.assertEqual( + ret["changes"], {"old": {"url": "url"}, "new": {"url": "newurl"}} ) - self.assertTrue(ret['result']) - self.assertEqual(ret['comment'], 'Data source test updated') - self.assertEqual(ret['changes'], {'old': {'url': 'url'}, 'new': {'url': 'newurl'}}) def test_absent(self): - with patch('requests.get', mock_json_response([])): - with patch('requests.delete') as rdelete: - ret = grafana_datasource.absent('test', profile=profile) + with patch("requests.get", mock_json_response([])): + with patch("requests.delete") as rdelete: + ret = grafana_datasource.absent("test", profile=profile) self.assertTrue(rdelete.call_count == 0) - self.assertTrue(ret['result']) - self.assertEqual(ret['comment'], 'Data source test already absent') + self.assertTrue(ret["result"]) + self.assertEqual(ret["comment"], "Data source test already absent") - with patch('requests.get', mock_json_response([{'name': 'test', 'id': 1}])): - with patch('requests.delete') as rdelete: - ret = grafana_datasource.absent('test', profile=profile) + with patch("requests.get", mock_json_response([{"name": "test", "id": 1}])): + with patch("requests.delete") as rdelete: + ret = grafana_datasource.absent("test", profile=profile) rdelete.assert_called_once_with( - 'http://grafana/api/datasources/1', - headers={'Authorization': 'Bearer token', 'Accept': 'application/json'}, - timeout=3 + "http://grafana/api/datasources/1", + headers={ + "Authorization": "Bearer token", + "Accept": "application/json", + }, + timeout=3, ) - self.assertTrue(ret['result']) - self.assertEqual(ret['comment'], 'Data source test was deleted') + self.assertTrue(ret["result"]) + self.assertEqual(ret["comment"], "Data source test was deleted") diff --git a/tests/unit/states/test_grains.py b/tests/unit/states/test_grains.py index 65f6cc6df68..936dcbcf681 100644 --- a/tests/unit/states/test_grains.py +++ b/tests/unit/states/test_grains.py @@ -1,68 +1,65 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the grains state -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import Python libs -import os import contextlib -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +# Import Python libs +import os + +import salt.modules.grains as grainsmod +import salt.states.grains as grains # Import salt libs import salt.utils.files import salt.utils.stringutils import salt.utils.yaml -import salt.modules.grains as grainsmod -import salt.states.grains as grains from salt.ext import six +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class GrainsTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - grains_test_dir = '__salt_test_state_grains' + grains_test_dir = "__salt_test_state_grains" if not os.path.exists(os.path.join(RUNTIME_VARS.TMP, grains_test_dir)): os.makedirs(os.path.join(RUNTIME_VARS.TMP, grains_test_dir)) loader_globals = { - '__opts__': { - 'test': False, - 'conf_file': os.path.join(RUNTIME_VARS.TMP, grains_test_dir, 'minion'), - 'cachedir': os.path.join(RUNTIME_VARS.TMP, grains_test_dir), - 'local': True, + "__opts__": { + "test": False, + "conf_file": os.path.join(RUNTIME_VARS.TMP, grains_test_dir, "minion"), + "cachedir": os.path.join(RUNTIME_VARS.TMP, grains_test_dir), + "local": True, + }, + "__salt__": { + "cmd.run_all": MagicMock( + return_value={"pid": 5, "retcode": 0, "stderr": "", "stdout": ""} + ), + "grains.get": grainsmod.get, + "grains.set": grainsmod.set, + "grains.setval": grainsmod.setval, + "grains.delval": grainsmod.delval, + "grains.append": grainsmod.append, + "grains.remove": grainsmod.remove, + "saltutil.sync_grains": MagicMock(), }, - '__salt__': { - 'cmd.run_all': MagicMock(return_value={ - 'pid': 5, - 'retcode': 0, - 'stderr': '', - 'stdout': ''}), - 'grains.get': grainsmod.get, - 'grains.set': grainsmod.set, - 'grains.setval': grainsmod.setval, - 'grains.delval': grainsmod.delval, - 'grains.append': grainsmod.append, - 'grains.remove': grainsmod.remove, - 'saltutil.sync_grains': MagicMock() - } } return {grains: loader_globals, grainsmod: loader_globals} def assertGrainFileContent(self, grains_string): - if os.path.isdir(grains.__opts__['conf_file']): - grains_file = os.path.join( - grains.__opts__['conf_file'], - 'grains') + if os.path.isdir(grains.__opts__["conf_file"]): + grains_file = os.path.join(grains.__opts__["conf_file"], "grains") else: grains_file = os.path.join( - os.path.dirname(grains.__opts__['conf_file']), - 'grains') + os.path.dirname(grains.__opts__["conf_file"]), "grains" + ) with salt.utils.files.fopen(grains_file, "r") as grf: grains_data = salt.utils.stringutils.to_unicode(grf.read()) self.assertMultiLineEqual(grains_string, grains_data) @@ -71,1046 +68,868 @@ class GrainsTestCase(TestCase, LoaderModuleMockMixin): def setGrains(self, grains_data): with patch.dict(grains.__grains__, grains_data): with patch.dict(grainsmod.__grains__, grains_data): - if os.path.isdir(grains.__opts__['conf_file']): - grains_file = os.path.join( - grains.__opts__['conf_file'], - 'grains') + if os.path.isdir(grains.__opts__["conf_file"]): + grains_file = os.path.join(grains.__opts__["conf_file"], "grains") else: grains_file = os.path.join( - os.path.dirname(grains.__opts__['conf_file']), 'grains') + os.path.dirname(grains.__opts__["conf_file"]), "grains" + ) with salt.utils.files.fopen(grains_file, "w+") as grf: - salt.utils.yaml.safe_dump(grains_data, grf, default_flow_style=False) + salt.utils.yaml.safe_dump( + grains_data, grf, default_flow_style=False + ) yield # 'exists' function tests: 2 def test_exists_missing(self): - with self.setGrains({'a': 'aval'}): - ret = grains.exists(name='foo') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'Grain does not exist') - self.assertEqual(ret['changes'], {}) + with self.setGrains({"a": "aval"}): + ret = grains.exists(name="foo") + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "Grain does not exist") + self.assertEqual(ret["changes"], {}) def test_exists_found(self): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Grain already set - ret = grains.exists(name='foo') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain exists') - self.assertEqual(ret['changes'], {}) + ret = grains.exists(name="foo") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain exists") + self.assertEqual(ret["changes"], {}) # 'make_hashable' function tests: 1 def test_make_hashable(self): - with self.setGrains({'cmplx_lst_grain': [{'a': 'aval'}, {'foo': 'bar'}]}): - hashable_list = {'cmplx_lst_grain': [{'a': 'aval'}, {'foo': 'bar'}]} - self.assertEqual(grains.make_hashable(grains.__grains__).issubset(grains.make_hashable(hashable_list)), True) + with self.setGrains({"cmplx_lst_grain": [{"a": "aval"}, {"foo": "bar"}]}): + hashable_list = {"cmplx_lst_grain": [{"a": "aval"}, {"foo": "bar"}]} + self.assertEqual( + grains.make_hashable(grains.__grains__).issubset( + grains.make_hashable(hashable_list) + ), + True, + ) # 'present' function tests: 12 def test_present_add(self): # Set a non existing grain - with self.setGrains({'a': 'aval'}): - ret = grains.present(name='foo', value='bar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['changes'], {'foo': 'bar'}) - self.assertEqual(grains.__grains__, {'a': 'aval', 'foo': 'bar'}) + with self.setGrains({"a": "aval"}): + ret = grains.present(name="foo", value="bar") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["changes"], {"foo": "bar"}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "bar"}) self.assertGrainFileContent("a: aval\nfoo: bar\n") # Set a non existing nested grain - with self.setGrains({'a': 'aval'}): - ret = grains.present(name='foo:is:nested', value='bar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['changes'], {'foo': {'is': {'nested': 'bar'}}}) - self.assertEqual(grains.__grains__, {'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}) - self.assertGrainFileContent("a: aval\n" - "foo:\n" - " is:\n" - " nested: bar\n" + with self.setGrains({"a": "aval"}): + ret = grains.present(name="foo:is:nested", value="bar") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["changes"], {"foo": {"is": {"nested": "bar"}}}) + self.assertEqual( + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": "bar"}}} + ) + self.assertGrainFileContent( + "a: aval\n" "foo:\n" " is:\n" " nested: bar\n" ) # Set a non existing nested dict grain - with self.setGrains({'a': 'aval'}): - ret = grains.present( - name='foo:is:nested', - value={'bar': 'is a dict'}) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['changes'], {'foo': {'is': {'nested': {'bar': 'is a dict'}}}}) + with self.setGrains({"a": "aval"}): + ret = grains.present(name="foo:is:nested", value={"bar": "is a dict"}) + self.assertEqual(ret["result"], True) + self.assertEqual( + ret["changes"], {"foo": {"is": {"nested": {"bar": "is a dict"}}}} + ) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': {'bar': 'is a dict'}}}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " is:\n" - + " nested:\n" - + " bar: is a dict\n" + {"a": "aval", "foo": {"is": {"nested": {"bar": "is a dict"}}}}, + ) + self.assertGrainFileContent( + "a: aval\n" + + "foo:\n" + + " is:\n" + + " nested:\n" + + " bar: is a dict\n" ) def test_present_add_key_to_existing(self): - with self.setGrains({'a': 'aval', 'foo': {'k1': 'v1'}}): + with self.setGrains({"a": "aval", "foo": {"k1": "v1"}}): # Fails setting a grain to a dict - ret = grains.present( - name='foo:k2', - value='v2') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Set grain foo:k2 to v2') - self.assertEqual(ret['changes'], {'foo': {'k2': 'v2', 'k1': 'v1'}}) + ret = grains.present(name="foo:k2", value="v2") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Set grain foo:k2 to v2") + self.assertEqual(ret["changes"], {"foo": {"k2": "v2", "k1": "v1"}}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'k1': 'v1', 'k2': 'v2'}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " k1: v1\n" - + " k2: v2\n" + grains.__grains__, {"a": "aval", "foo": {"k1": "v1", "k2": "v2"}} + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + " k1: v1\n" + " k2: v2\n" ) def test_present_already_set(self): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Grain already set - ret = grains.present( - name='foo', - value='bar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain is already set') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': 'bar'}) + ret = grains.present(name="foo", value="bar") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain is already set") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "bar"}) - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}): + with self.setGrains({"a": "aval", "foo": {"is": {"nested": "bar"}}}): # Nested grain already set - ret = grains.present( - name='foo:is:nested', - value='bar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain is already set') - self.assertEqual(ret['changes'], {}) + ret = grains.present(name="foo:is:nested", value="bar") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain is already set") + self.assertEqual(ret["changes"], {}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}) + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": "bar"}}} + ) - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}): + with self.setGrains({"a": "aval", "foo": {"is": {"nested": "bar"}}}): # Nested dict grain already set - ret = grains.present( - name='foo:is', - value={'nested': 'bar'}) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain is already set') - self.assertEqual(ret['changes'], {}) + ret = grains.present(name="foo:is", value={"nested": "bar"}) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain is already set") + self.assertEqual(ret["changes"], {}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}) + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": "bar"}}} + ) def test_present_overwrite(self): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Overwrite an existing grain - ret = grains.present( - name='foo', - value='newbar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['changes'], {'foo': 'newbar'}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': 'newbar'}) - self.assertGrainFileContent("a: aval\n" - + "foo: newbar\n" - ) + ret = grains.present(name="foo", value="newbar") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["changes"], {"foo": "newbar"}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "newbar"}) + self.assertGrainFileContent("a: aval\n" + "foo: newbar\n") - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Clear a grain (set to None) - ret = grains.present( - name='foo', - value=None) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['changes'], {'foo': None}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': None}) - self.assertGrainFileContent("a: aval\n" - + "foo: null\n" - ) + ret = grains.present(name="foo", value=None) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["changes"], {"foo": None}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": None}) + self.assertGrainFileContent("a: aval\n" + "foo: null\n") - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}): + with self.setGrains({"a": "aval", "foo": {"is": {"nested": "bar"}}}): # Overwrite an existing nested grain - ret = grains.present( - name='foo:is:nested', - value='newbar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['changes'], {'foo': {'is': {'nested': 'newbar'}}}) + ret = grains.present(name="foo:is:nested", value="newbar") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["changes"], {"foo": {"is": {"nested": "newbar"}}}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': 'newbar'}}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " is:\n" - + " nested: newbar\n" + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": "newbar"}}} + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + " is:\n" + " nested: newbar\n" ) - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}): + with self.setGrains({"a": "aval", "foo": {"is": {"nested": "bar"}}}): # Clear a nested grain (set to None) - ret = grains.present( - name='foo:is:nested', - value=None) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['changes'], {'foo': {'is': {'nested': None}}}) + ret = grains.present(name="foo:is:nested", value=None) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["changes"], {"foo": {"is": {"nested": None}}}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': None}}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " is:\n" - + " nested: null\n" + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": None}}} + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + " is:\n" + " nested: null\n" ) def test_present_fail_overwrite(self): - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'val'}}}): + with self.setGrains({"a": "aval", "foo": {"is": {"nested": "val"}}}): # Overwrite an existing grain - ret = grains.present( - name='foo:is', - value='newbar') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['changes'], {}) - self.assertEqual(ret['comment'], 'The key \'foo:is\' exists but is a dict or a list. Use \'force=True\' to overwrite.') + ret = grains.present(name="foo:is", value="newbar") + self.assertEqual(ret["result"], False) + self.assertEqual(ret["changes"], {}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': 'val'}}}) + ret["comment"], + "The key 'foo:is' exists but is a dict or a list. Use 'force=True' to overwrite.", + ) + self.assertEqual( + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": "val"}}} + ) - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'val'}}}): + with self.setGrains({"a": "aval", "foo": {"is": {"nested": "val"}}}): # Clear a grain (set to None) - ret = grains.present( - name='foo:is', - value=None) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['changes'], {}) - self.assertEqual(ret['comment'], 'The key \'foo:is\' exists but is a dict or a list. Use \'force=True\' to overwrite.') + ret = grains.present(name="foo:is", value=None) + self.assertEqual(ret["result"], False) + self.assertEqual(ret["changes"], {}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': 'val'}}}) + ret["comment"], + "The key 'foo:is' exists but is a dict or a list. Use 'force=True' to overwrite.", + ) + self.assertEqual( + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": "val"}}} + ) def test_present_fails_to_set_dict_or_list(self): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Fails to overwrite a grain to a list - ret = grains.present( - name='foo', - value=['l1', 'l2']) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'The key \'foo\' exists and the ' - + 'given value is a dict or a list. ' - + 'Use \'force=True\' to overwrite.') - self.assertEqual(ret['changes'], {}) + ret = grains.present(name="foo", value=["l1", "l2"]) + self.assertEqual(ret["result"], False) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': 'bar'}) + ret["comment"], + "The key 'foo' exists and the " + + "given value is a dict or a list. " + + "Use 'force=True' to overwrite.", + ) + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "bar"}) - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Fails setting a grain to a dict - ret = grains.present( - name='foo', - value={'k1': 'v1'}) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'The key \'foo\' exists and the given ' - + 'value is a dict or a list. Use ' - + '\'force=True\' to overwrite.') - self.assertEqual(ret['changes'], {}) + ret = grains.present(name="foo", value={"k1": "v1"}) + self.assertEqual(ret["result"], False) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': 'bar'}) + ret["comment"], + "The key 'foo' exists and the given " + + "value is a dict or a list. Use " + + "'force=True' to overwrite.", + ) + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "bar"}) - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}): + with self.setGrains({"a": "aval", "foo": {"is": {"nested": "bar"}}}): # Fails to overwrite a nested grain to a list ret = grains.present( - name='foo,is,nested', - value=['l1', 'l2'], - delimiter=',') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['changes'], {}) - self.assertEqual(ret['comment'], 'The key \'foo:is:nested\' exists and the ' - + 'given value is a dict or a list. ' - + 'Use \'force=True\' to overwrite.') + name="foo,is,nested", value=["l1", "l2"], delimiter="," + ) + self.assertEqual(ret["result"], False) + self.assertEqual(ret["changes"], {}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}) + ret["comment"], + "The key 'foo:is:nested' exists and the " + + "given value is a dict or a list. " + + "Use 'force=True' to overwrite.", + ) + self.assertEqual( + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": "bar"}}} + ) - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}): + with self.setGrains({"a": "aval", "foo": {"is": {"nested": "bar"}}}): # Fails setting a nested grain to a dict - ret = grains.present( - name='foo:is:nested', - value={'k1': 'v1'}) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'The key \'foo:is:nested\' exists and the ' - + 'given value is a dict or a list. ' - + 'Use \'force=True\' to overwrite.') - self.assertEqual(ret['changes'], {}) + ret = grains.present(name="foo:is:nested", value={"k1": "v1"}) + self.assertEqual(ret["result"], False) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}) + ret["comment"], + "The key 'foo:is:nested' exists and the " + + "given value is a dict or a list. " + + "Use 'force=True' to overwrite.", + ) + self.assertEqual(ret["changes"], {}) + self.assertEqual( + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": "bar"}}} + ) def test_present_fail_merge_dict(self): - with self.setGrains({'a': 'aval', 'foo': {'k1': 'v1'}}): + with self.setGrains({"a": "aval", "foo": {"k1": "v1"}}): # Fails setting a grain to a dict - ret = grains.present( - name='foo', - value={'k2': 'v2'}) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'The key \'foo\' exists but ' - + 'is a dict or a list. ' - + 'Use \'force=True\' to overwrite.') + ret = grains.present(name="foo", value={"k2": "v2"}) + self.assertEqual(ret["result"], False) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'k1': 'v1'}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " k1: v1\n" + ret["comment"], + "The key 'foo' exists but " + + "is a dict or a list. " + + "Use 'force=True' to overwrite.", ) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": {"k1": "v1"}}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + " k1: v1\n") def test_present_force_to_set_dict_or_list(self): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Force to overwrite a grain to a list - ret = grains.present( - name='foo', - value=['l1', 'l2'], - force=True) - self.assertEqual(ret['result'], True) + ret = grains.present(name="foo", value=["l1", "l2"], force=True) + self.assertEqual(ret["result"], True) self.assertEqual( - ret['comment'], - "Set grain foo to ['l1', 'l2']" if six.PY3 - else "Set grain foo to [u'l1', u'l2']" - ) - self.assertEqual(ret['changes'], {'foo': ['l1', 'l2']}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': ['l1', 'l2']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- l1\n" - + "- l2\n" + ret["comment"], + "Set grain foo to ['l1', 'l2']" + if six.PY3 + else "Set grain foo to [u'l1', u'l2']", ) + self.assertEqual(ret["changes"], {"foo": ["l1", "l2"]}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": ["l1", "l2"]}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + "- l1\n" + "- l2\n") - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Force setting a grain to a dict - ret = grains.present( - name='foo', - value={'k1': 'v1'}, - force=True) - self.assertEqual(ret['result'], True) + ret = grains.present(name="foo", value={"k1": "v1"}, force=True) + self.assertEqual(ret["result"], True) self.assertEqual( - ret['comment'], - "Set grain foo to {'k1': 'v1'}" if six.PY3 - else "Set grain foo to {u'k1': u'v1'}" - ) - self.assertEqual(ret['changes'], {'foo': {'k1': 'v1'}}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'k1': 'v1'}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " k1: v1\n" + ret["comment"], + "Set grain foo to {'k1': 'v1'}" + if six.PY3 + else "Set grain foo to {u'k1': u'v1'}", ) + self.assertEqual(ret["changes"], {"foo": {"k1": "v1"}}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": {"k1": "v1"}}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + " k1: v1\n") - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'bar'}}}): + with self.setGrains({"a": "aval", "foo": {"is": {"nested": "bar"}}}): # Force to overwrite a nested grain to a list ret = grains.present( - name='foo,is,nested', - value=['l1', 'l2'], - delimiter=',', - force=True) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['changes'], {'foo': {'is': {'nested': ['l1', 'l2']}}}) + name="foo,is,nested", value=["l1", "l2"], delimiter=",", force=True + ) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["changes"], {"foo": {"is": {"nested": ["l1", "l2"]}}}) self.assertEqual( - ret['comment'], - "Set grain foo:is:nested to ['l1', 'l2']" if six.PY3 - else "Set grain foo:is:nested to [u'l1', u'l2']" + ret["comment"], + "Set grain foo:is:nested to ['l1', 'l2']" + if six.PY3 + else "Set grain foo:is:nested to [u'l1', u'l2']", ) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': ['l1', 'l2']}}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " is:\n" - + " nested:\n" - + " - l1\n" - + " - l2\n" + {"a": "aval", "foo": {"is": {"nested": ["l1", "l2"]}}}, + ) + self.assertGrainFileContent( + "a: aval\n" + + "foo:\n" + + " is:\n" + + " nested:\n" + + " - l1\n" + + " - l2\n" ) - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': 'bar'}, 'and': 'other'}}): + with self.setGrains( + {"a": "aval", "foo": {"is": {"nested": "bar"}, "and": "other"}} + ): # Force setting a nested grain to a dict - ret = grains.present( - name='foo:is:nested', - value={'k1': 'v1'}, - force=True) - self.assertEqual(ret['result'], True) + ret = grains.present(name="foo:is:nested", value={"k1": "v1"}, force=True) + self.assertEqual(ret["result"], True) self.assertEqual( - ret['comment'], - "Set grain foo:is:nested to {'k1': 'v1'}" if six.PY3 - else "Set grain foo:is:nested to {u'k1': u'v1'}" + ret["comment"], + "Set grain foo:is:nested to {'k1': 'v1'}" + if six.PY3 + else "Set grain foo:is:nested to {u'k1': u'v1'}", + ) + self.assertEqual( + ret["changes"], + {"foo": {"is": {"nested": {"k1": "v1"}}, "and": "other"}}, ) - self.assertEqual(ret['changes'], {'foo': {'is': {'nested': {'k1': 'v1'}}, 'and': 'other'}}) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': {'k1': 'v1'}}, 'and': 'other'}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " and: other\n" - + " is:\n" - + " nested:\n" - + " k1: v1\n" + {"a": "aval", "foo": {"is": {"nested": {"k1": "v1"}}, "and": "other"}}, + ) + self.assertGrainFileContent( + "a: aval\n" + + "foo:\n" + + " and: other\n" + + " is:\n" + + " nested:\n" + + " k1: v1\n" ) def test_present_fails_to_convert_value_to_key(self): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Fails converting a value to a nested grain key - ret = grains.present( - name='foo:is:nested', - value={'k1': 'v1'}) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'The key \'foo\' value is \'bar\', ' - + 'which is different from the provided ' - + 'key \'is\'. Use \'force=True\' to overwrite.') - self.assertEqual(ret['changes'], {}) + ret = grains.present(name="foo:is:nested", value={"k1": "v1"}) + self.assertEqual(ret["result"], False) + self.assertEqual( + ret["comment"], + "The key 'foo' value is 'bar', " + + "which is different from the provided " + + "key 'is'. Use 'force=True' to overwrite.", + ) + self.assertEqual(ret["changes"], {}) def test_present_overwrite_test(self): - with patch.dict(grains.__opts__, {'test': True}): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with patch.dict(grains.__opts__, {"test": True}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Overwrite an existing grain - ret = grains.present( - name='foo', - value='newbar') - self.assertEqual(ret['result'], None) - self.assertEqual(ret['changes'], {'changed': {'foo': 'newbar'}}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': 'bar'}) - self.assertGrainFileContent("a: aval\n" - + "foo: bar\n" - ) + ret = grains.present(name="foo", value="newbar") + self.assertEqual(ret["result"], None) + self.assertEqual(ret["changes"], {"changed": {"foo": "newbar"}}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "bar"}) + self.assertGrainFileContent("a: aval\n" + "foo: bar\n") def test_present_convert_value_to_key(self): - with self.setGrains({'a': 'aval', 'foo': 'is'}): + with self.setGrains({"a": "aval", "foo": "is"}): # Converts a value to a nested grain key - ret = grains.present( - name='foo:is:nested', - value={'k1': 'v1'}) - self.assertEqual(ret['result'], True) + ret = grains.present(name="foo:is:nested", value={"k1": "v1"}) + self.assertEqual(ret["result"], True) self.assertEqual( - ret['comment'], - "Set grain foo:is:nested to {'k1': 'v1'}" if six.PY3 - else "Set grain foo:is:nested to {u'k1': u'v1'}" + ret["comment"], + "Set grain foo:is:nested to {'k1': 'v1'}" + if six.PY3 + else "Set grain foo:is:nested to {u'k1': u'v1'}", ) - self.assertEqual(ret['changes'], {'foo': {'is': {'nested': {'k1': 'v1'}}}}) + self.assertEqual(ret["changes"], {"foo": {"is": {"nested": {"k1": "v1"}}}}) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': {'k1': 'v1'}}}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " is:\n" - + " nested:\n" - + " k1: v1\n" + {"a": "aval", "foo": {"is": {"nested": {"k1": "v1"}}}}, + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + " is:\n" + " nested:\n" + " k1: v1\n" ) - with self.setGrains({'a': 'aval', 'foo': ['one', 'is', 'correct']}): + with self.setGrains({"a": "aval", "foo": ["one", "is", "correct"]}): # Converts a list element to a nested grain key - ret = grains.present( - name='foo:is:nested', - value={'k1': 'v1'}) - self.assertEqual(ret['result'], True) + ret = grains.present(name="foo:is:nested", value={"k1": "v1"}) + self.assertEqual(ret["result"], True) self.assertEqual( - ret['comment'], - "Set grain foo:is:nested to {'k1': 'v1'}" if six.PY3 - else "Set grain foo:is:nested to {u'k1': u'v1'}" + ret["comment"], + "Set grain foo:is:nested to {'k1': 'v1'}" + if six.PY3 + else "Set grain foo:is:nested to {u'k1': u'v1'}", + ) + self.assertEqual( + ret["changes"], + {"foo": ["one", {"is": {"nested": {"k1": "v1"}}}, "correct"]}, ) - self.assertEqual(ret['changes'], {'foo': ['one', {'is': {'nested': {'k1': 'v1'}}}, 'correct']}) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': ['one', {'is': {'nested': {'k1': 'v1'}}}, 'correct']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- one\n" - + "- is:\n" - + " nested:\n" - + " k1: v1\n" - + "- correct\n" + { + "a": "aval", + "foo": ["one", {"is": {"nested": {"k1": "v1"}}}, "correct"], + }, + ) + self.assertGrainFileContent( + "a: aval\n" + + "foo:\n" + + "- one\n" + + "- is:\n" + + " nested:\n" + + " k1: v1\n" + + "- correct\n" ) def test_present_unknown_failure(self): - with patch('salt.modules.grains.setval') as mocked_setval: - mocked_setval.return_value = 'Failed to set grain foo' - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with patch("salt.modules.grains.setval") as mocked_setval: + mocked_setval.return_value = "Failed to set grain foo" + with self.setGrains({"a": "aval", "foo": "bar"}): # Unknown reason failure - ret = grains.present( - name='foo', - value='baz') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'Failed to set grain foo') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': 'bar'}) - self.assertGrainFileContent("a: aval\n" - + "foo: bar\n" - ) + ret = grains.present(name="foo", value="baz") + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "Failed to set grain foo") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "bar"}) + self.assertGrainFileContent("a: aval\n" + "foo: bar\n") # 'absent' function tests: 6 def test_absent_already(self): # Unset a non existent grain - with self.setGrains({'a': 'aval'}): - ret = grains.absent( - name='foo') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain foo does not exist') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval'}) + with self.setGrains({"a": "aval"}): + ret = grains.absent(name="foo") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain foo does not exist") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval"}) self.assertGrainFileContent("a: aval\n") # Unset a non existent nested grain - with self.setGrains({'a': 'aval'}): - ret = grains.absent( - name='foo:is:nested') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain foo:is:nested does not exist') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval'}) + with self.setGrains({"a": "aval"}): + ret = grains.absent(name="foo:is:nested") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain foo:is:nested does not exist") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval"}) self.assertGrainFileContent("a: aval\n") def test_absent_unset(self): # Unset a grain - with self.setGrains({'a': 'aval', 'foo': 'bar'}): - ret = grains.absent( - name='foo') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value for grain foo was set to None') - self.assertEqual(ret['changes'], {'grain': 'foo', 'value': None}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': None}) - self.assertGrainFileContent("a: aval\n" - + "foo: null\n" - ) + with self.setGrains({"a": "aval", "foo": "bar"}): + ret = grains.absent(name="foo") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value for grain foo was set to None") + self.assertEqual(ret["changes"], {"grain": "foo", "value": None}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": None}) + self.assertGrainFileContent("a: aval\n" + "foo: null\n") # Unset grain when its value is False - with self.setGrains({'a': 'aval', 'foo': False}): - ret = grains.absent( - name='foo') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value for grain foo was set to None') - self.assertEqual(ret['changes'], {'grain': 'foo', 'value': None}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': None}) - self.assertGrainFileContent("a: aval\n" - + "foo: null\n" - ) + with self.setGrains({"a": "aval", "foo": False}): + ret = grains.absent(name="foo") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value for grain foo was set to None") + self.assertEqual(ret["changes"], {"grain": "foo", "value": None}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": None}) + self.assertGrainFileContent("a: aval\n" + "foo: null\n") # Unset a nested grain - with self.setGrains({'a': 'aval', 'foo': ['order', {'is': {'nested': 'bar'}}, 'correct']}): - ret = grains.absent( - name='foo,is,nested', - delimiter=',') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value for grain foo:is:nested was set to None') - self.assertEqual(ret['changes'], {'grain': 'foo:is:nested', 'value': None}) + with self.setGrains( + {"a": "aval", "foo": ["order", {"is": {"nested": "bar"}}, "correct"]} + ): + ret = grains.absent(name="foo,is,nested", delimiter=",") + self.assertEqual(ret["result"], True) + self.assertEqual( + ret["comment"], "Value for grain foo:is:nested was set to None" + ) + self.assertEqual(ret["changes"], {"grain": "foo:is:nested", "value": None}) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': ['order', {'is': {'nested': None}}, 'correct']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- order\n" - + "- is:\n" - + " nested: null\n" - + "- correct\n" + {"a": "aval", "foo": ["order", {"is": {"nested": None}}, "correct"]}, + ) + self.assertGrainFileContent( + "a: aval\n" + + "foo:\n" + + "- order\n" + + "- is:\n" + + " nested: null\n" + + "- correct\n" ) # Unset a nested value don't change anything - with self.setGrains({'a': 'aval', 'foo': ['order', {'is': 'nested'}, 'correct']}): - ret = grains.absent( - name='foo:is:nested') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain foo:is:nested does not exist') - self.assertEqual(ret['changes'], {}) + with self.setGrains( + {"a": "aval", "foo": ["order", {"is": "nested"}, "correct"]} + ): + ret = grains.absent(name="foo:is:nested") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain foo:is:nested does not exist") + self.assertEqual(ret["changes"], {}) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': ['order', {'is': 'nested'}, 'correct']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- order\n" - + "- is: nested\n" - + "- correct\n" + {"a": "aval", "foo": ["order", {"is": "nested"}, "correct"]}, + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + "- order\n" + "- is: nested\n" + "- correct\n" ) def test_absent_unset_test(self): - with patch.dict(grains.__opts__, {'test': True}): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): + with patch.dict(grains.__opts__, {"test": True}): + with self.setGrains({"a": "aval", "foo": "bar"}): # Overwrite an existing grain - ret = grains.absent(name='foo') - self.assertEqual(ret['result'], None) - self.assertEqual(ret['changes'], {'grain': 'foo', 'value': None}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': 'bar'}) - self.assertGrainFileContent("a: aval\n" - + "foo: bar\n" - ) + ret = grains.absent(name="foo") + self.assertEqual(ret["result"], None) + self.assertEqual(ret["changes"], {"grain": "foo", "value": None}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "bar"}) + self.assertGrainFileContent("a: aval\n" + "foo: bar\n") def test_absent_fails_nested_complex_grain(self): # Unset a nested complex grain - with self.setGrains({'a': 'aval', 'foo': ['order', {'is': {'nested': 'bar'}}, 'correct']}): - ret = grains.absent( - name='foo:is') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'The key \'foo:is\' exists but is a dict or a list. Use \'force=True\' to overwrite.') - self.assertEqual(ret['changes'], {}) + with self.setGrains( + {"a": "aval", "foo": ["order", {"is": {"nested": "bar"}}, "correct"]} + ): + ret = grains.absent(name="foo:is") + self.assertEqual(ret["result"], False) + self.assertEqual( + ret["comment"], + "The key 'foo:is' exists but is a dict or a list. Use 'force=True' to overwrite.", + ) + self.assertEqual(ret["changes"], {}) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': ['order', {'is': {'nested': 'bar'}}, 'correct']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- order\n" - + "- is:\n" - + " nested: bar\n" - + "- correct\n" + {"a": "aval", "foo": ["order", {"is": {"nested": "bar"}}, "correct"]}, + ) + self.assertGrainFileContent( + "a: aval\n" + + "foo:\n" + + "- order\n" + + "- is:\n" + + " nested: bar\n" + + "- correct\n" ) def test_absent_force_nested_complex_grain(self): # Unset a nested complex grain - with self.setGrains({'a': 'aval', 'foo': ['order', {'is': {'nested': 'bar'}}, 'correct']}): - ret = grains.absent( - name='foo:is', - force=True) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value for grain foo:is was set to None') - self.assertEqual(ret['changes'], {'grain': 'foo:is', 'value': None}) + with self.setGrains( + {"a": "aval", "foo": ["order", {"is": {"nested": "bar"}}, "correct"]} + ): + ret = grains.absent(name="foo:is", force=True) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value for grain foo:is was set to None") + self.assertEqual(ret["changes"], {"grain": "foo:is", "value": None}) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': ['order', {'is': None}, 'correct']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- order\n" - + "- is: null\n" - + "- correct\n" + {"a": "aval", "foo": ["order", {"is": None}, "correct"]}, + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + "- order\n" + "- is: null\n" + "- correct\n" ) def test_absent_delete(self): # Delete a grain - with self.setGrains({'a': 'aval', 'foo': 'bar'}): - ret = grains.absent( - name='foo', - destructive=True) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain foo was deleted') - self.assertEqual(ret['changes'], {'deleted': 'foo'}) - self.assertEqual( - grains.__grains__, - {'a': 'aval'}) + with self.setGrains({"a": "aval", "foo": "bar"}): + ret = grains.absent(name="foo", destructive=True) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain foo was deleted") + self.assertEqual(ret["changes"], {"deleted": "foo"}) + self.assertEqual(grains.__grains__, {"a": "aval"}) self.assertGrainFileContent("a: aval\n") # Delete a previously unset grain - with self.setGrains({'a': 'aval', 'foo': None}): - ret = grains.absent( - name='foo', - destructive=True) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain foo was deleted') - self.assertEqual(ret['changes'], {'deleted': 'foo'}) - self.assertEqual( - grains.__grains__, - {'a': 'aval'}) + with self.setGrains({"a": "aval", "foo": None}): + ret = grains.absent(name="foo", destructive=True) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain foo was deleted") + self.assertEqual(ret["changes"], {"deleted": "foo"}) + self.assertEqual(grains.__grains__, {"a": "aval"}) self.assertGrainFileContent("a: aval\n") # Delete a nested grain - with self.setGrains({'a': 'aval', 'foo': ['order', {'is': {'nested': 'bar', 'other': 'value'}}, 'correct']}): - ret = grains.absent( - name='foo:is:nested', - destructive=True) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain foo:is:nested was deleted') - self.assertEqual(ret['changes'], {'deleted': 'foo:is:nested'}) + with self.setGrains( + { + "a": "aval", + "foo": [ + "order", + {"is": {"nested": "bar", "other": "value"}}, + "correct", + ], + } + ): + ret = grains.absent(name="foo:is:nested", destructive=True) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain foo:is:nested was deleted") + self.assertEqual(ret["changes"], {"deleted": "foo:is:nested"}) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': ['order', {'is': {'other': 'value'}}, 'correct']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- order\n" - + "- is:\n" - + " other: value\n" - + "- correct\n" + {"a": "aval", "foo": ["order", {"is": {"other": "value"}}, "correct"]}, + ) + self.assertGrainFileContent( + "a: aval\n" + + "foo:\n" + + "- order\n" + + "- is:\n" + + " other: value\n" + + "- correct\n" ) # 'append' function tests: 6 def test_append(self): # Append to an existing list - with self.setGrains({'a': 'aval', 'foo': ['bar']}): - ret = grains.append( - name='foo', - value='baz') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value baz was added to grain foo') - self.assertEqual(ret['changes'], {'added': 'baz'}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': ['bar', 'baz']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- bar\n" - + "- baz\n" - ) + with self.setGrains({"a": "aval", "foo": ["bar"]}): + ret = grains.append(name="foo", value="baz") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value baz was added to grain foo") + self.assertEqual(ret["changes"], {"added": "baz"}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": ["bar", "baz"]}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + "- bar\n" + "- baz\n") def test_append_nested(self): # Append to an existing nested list - with self.setGrains({'a': 'aval', 'foo': {'list': ['bar']}}): - ret = grains.append( - name='foo,list', - value='baz', - delimiter=',') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value baz was added to grain foo:list') - self.assertEqual(ret['changes'], {'added': 'baz'}) + with self.setGrains({"a": "aval", "foo": {"list": ["bar"]}}): + ret = grains.append(name="foo,list", value="baz", delimiter=",") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value baz was added to grain foo:list") + self.assertEqual(ret["changes"], {"added": "baz"}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'list': ['bar', 'baz']}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " list:\n" - + " - bar\n" - + " - baz\n" + grains.__grains__, {"a": "aval", "foo": {"list": ["bar", "baz"]}} + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + " list:\n" + " - bar\n" + " - baz\n" ) def test_append_already(self): # Append to an existing list - with self.setGrains({'a': 'aval', 'foo': ['bar']}): - ret = grains.append( - name='foo', - value='bar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value bar is already in the list ' - + 'for grain foo') - self.assertEqual(ret['changes'], {}) + with self.setGrains({"a": "aval", "foo": ["bar"]}): + ret = grains.append(name="foo", value="bar") + self.assertEqual(ret["result"], True) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': ['bar']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- bar\n" + ret["comment"], "Value bar is already in the list " + "for grain foo" ) + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": ["bar"]}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + "- bar\n") def test_append_fails_not_a_list(self): # Fail to append to an existing grain, not a list - with self.setGrains({'a': 'aval', 'foo': {'bar': 'val'}}): - ret = grains.append( - name='foo', - value='baz') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'Grain foo is not a valid list') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'bar': 'val'}}) + with self.setGrains({"a": "aval", "foo": {"bar": "val"}}): + ret = grains.append(name="foo", value="baz") + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "Grain foo is not a valid list") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": {"bar": "val"}}) def test_append_convert_to_list(self): # Append to an existing grain, converting to a list - with self.setGrains({'a': 'aval', 'foo': {'bar': 'val'}}): - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " bar: val\n" - ) - ret = grains.append( - name='foo', - value='baz', - convert=True) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value baz was added to grain foo') - self.assertEqual(ret['changes'], {'added': 'baz'}) + with self.setGrains({"a": "aval", "foo": {"bar": "val"}}): + self.assertGrainFileContent("a: aval\n" + "foo:\n" + " bar: val\n") + ret = grains.append(name="foo", value="baz", convert=True) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value baz was added to grain foo") + self.assertEqual(ret["changes"], {"added": "baz"}) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': [{'bar': 'val'}, 'baz']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- bar: val\n" - + "- baz\n" + grains.__grains__, {"a": "aval", "foo": [{"bar": "val"}, "baz"]} + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + "- bar: val\n" + "- baz\n" ) # Append to an existing grain, converting to a list a multi-value dict - with self.setGrains({'a': 'aval', 'foo': {'bar': 'val', 'other': 'value'}}): - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " bar: val\n" - + " other: value\n" + with self.setGrains({"a": "aval", "foo": {"bar": "val", "other": "value"}}): + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + " bar: val\n" + " other: value\n" ) - ret = grains.append( - name='foo', - value='baz', - convert=True) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value baz was added to grain foo') - self.assertEqual(ret['changes'], {'added': 'baz'}) + ret = grains.append(name="foo", value="baz", convert=True) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value baz was added to grain foo") + self.assertEqual(ret["changes"], {"added": "baz"}) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': [{'bar': 'val', 'other': 'value'}, 'baz']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- bar: val\n" - + " other: value\n" - + "- baz\n" + {"a": "aval", "foo": [{"bar": "val", "other": "value"}, "baz"]}, + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + "- bar: val\n" + " other: value\n" + "- baz\n" ) def test_append_fails_inexistent(self): # Append to a non existing grain - with self.setGrains({'a': 'aval'}): - ret = grains.append( - name='foo', - value='bar') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'Grain foo does not exist') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval'}) + with self.setGrains({"a": "aval"}): + ret = grains.append(name="foo", value="bar") + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "Grain foo does not exist") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval"}) def test_append_convert_to_list_empty(self): # Append to an existing list - with self.setGrains({'foo': None}): - ret = grains.append(name='foo', - value='baz', - convert=True) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value baz was added to grain foo') - self.assertEqual(ret['changes'], {'added': 'baz'}) - self.assertEqual( - grains.__grains__, - {'foo': ['baz']}) - self.assertGrainFileContent("foo:\n" - + "- baz\n") + with self.setGrains({"foo": None}): + ret = grains.append(name="foo", value="baz", convert=True) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value baz was added to grain foo") + self.assertEqual(ret["changes"], {"added": "baz"}) + self.assertEqual(grains.__grains__, {"foo": ["baz"]}) + self.assertGrainFileContent("foo:\n" + "- baz\n") # 'list_present' function tests: 7 def test_list_present(self): - with self.setGrains({'a': 'aval', 'foo': ['bar']}): - ret = grains.list_present( - name='foo', - value='baz') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Append value baz to grain foo') - self.assertEqual(ret['changes'], {'new': {'foo': ['bar', 'baz']}}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': ['bar', 'baz']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- bar\n" - + "- baz\n" - ) + with self.setGrains({"a": "aval", "foo": ["bar"]}): + ret = grains.list_present(name="foo", value="baz") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Append value baz to grain foo") + self.assertEqual(ret["changes"], {"new": {"foo": ["bar", "baz"]}}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": ["bar", "baz"]}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + "- bar\n" + "- baz\n") def test_list_present_nested(self): - with self.setGrains({'a': 'aval', 'foo': {'is': {'nested': ['bar']}}}): - ret = grains.list_present( - name='foo,is,nested', - value='baz', - delimiter=',') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Append value baz to grain foo:is:nested') - self.assertEqual(ret['changes'], {'new': {'foo': {'is': {'nested': ['bar', 'baz']}}}}) + with self.setGrains({"a": "aval", "foo": {"is": {"nested": ["bar"]}}}): + ret = grains.list_present(name="foo,is,nested", value="baz", delimiter=",") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Append value baz to grain foo:is:nested") + self.assertEqual( + ret["changes"], {"new": {"foo": {"is": {"nested": ["bar", "baz"]}}}} + ) self.assertEqual( grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': ['bar', 'baz']}}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " is:\n" - + " nested:\n" - + " - bar\n" - + " - baz\n" + {"a": "aval", "foo": {"is": {"nested": ["bar", "baz"]}}}, + ) + self.assertGrainFileContent( + "a: aval\n" + + "foo:\n" + + " is:\n" + + " nested:\n" + + " - bar\n" + + " - baz\n" ) def test_list_present_inexistent(self): - with self.setGrains({'a': 'aval'}): - ret = grains.list_present( - name='foo', - value='baz') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Append value baz to grain foo') - self.assertEqual(ret['changes'], {'new': {'foo': ['baz']}}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': ['baz']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- baz\n" - ) + with self.setGrains({"a": "aval"}): + ret = grains.list_present(name="foo", value="baz") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Append value baz to grain foo") + self.assertEqual(ret["changes"], {"new": {"foo": ["baz"]}}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": ["baz"]}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + "- baz\n") def test_list_present_inexistent_nested(self): - with self.setGrains({'a': 'aval'}): - ret = grains.list_present( - name='foo:is:nested', - value='baz') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Append value baz to grain foo:is:nested') - self.assertEqual(ret['changes'], {'new': {'foo': {'is': {'nested': ['baz']}}}}) + with self.setGrains({"a": "aval"}): + ret = grains.list_present(name="foo:is:nested", value="baz") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Append value baz to grain foo:is:nested") self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'is': {'nested': ['baz']}}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " is:\n" - + " nested:\n" - + " - baz\n" + ret["changes"], {"new": {"foo": {"is": {"nested": ["baz"]}}}} + ) + self.assertEqual( + grains.__grains__, {"a": "aval", "foo": {"is": {"nested": ["baz"]}}} + ) + self.assertGrainFileContent( + "a: aval\n" + "foo:\n" + " is:\n" + " nested:\n" + " - baz\n" ) def test_list_present_not_a_list(self): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): - ret = grains.list_present( - name='foo', - value='baz') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'Grain foo is not a valid list') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': 'bar'}) - self.assertGrainFileContent("a: aval\n" - + "foo: bar\n" - ) + with self.setGrains({"a": "aval", "foo": "bar"}): + ret = grains.list_present(name="foo", value="baz") + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "Grain foo is not a valid list") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "bar"}) + self.assertGrainFileContent("a: aval\n" + "foo: bar\n") def test_list_present_nested_already(self): - with self.setGrains({'a': 'aval', 'b': {'foo': ['bar']}}): - ret = grains.list_present( - name='b:foo', - value='bar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value bar is already in grain b:foo') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'b': {'foo': ['bar']}}) - self.assertGrainFileContent("a: aval\n" - + "b:\n" - + " foo:\n" - + " - bar\n" - ) + with self.setGrains({"a": "aval", "b": {"foo": ["bar"]}}): + ret = grains.list_present(name="b:foo", value="bar") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value bar is already in grain b:foo") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "b": {"foo": ["bar"]}}) + self.assertGrainFileContent("a: aval\n" + "b:\n" + " foo:\n" + " - bar\n") def test_list_present_already(self): - with self.setGrains({'a': 'aval', 'foo': ['bar']}): - ret = grains.list_present( - name='foo', - value='bar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value bar is already in grain foo') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': ['bar']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- bar\n" - ) + with self.setGrains({"a": "aval", "foo": ["bar"]}): + ret = grains.list_present(name="foo", value="bar") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value bar is already in grain foo") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": ["bar"]}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + "- bar\n") def test_list_present_unknown_failure(self): - with self.setGrains({'a': 'aval', 'foo': ['bar']}): + with self.setGrains({"a": "aval", "foo": ["bar"]}): # Unknown reason failure - with patch.dict(grainsmod.__salt__, {'grains.append': MagicMock()}): - ret = grains.list_present( - name='foo', - value='baz') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'Failed append value baz to grain foo') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': ['bar']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- bar\n" - ) + with patch.dict(grainsmod.__salt__, {"grains.append": MagicMock()}): + ret = grains.list_present(name="foo", value="baz") + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "Failed append value baz to grain foo") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": ["bar"]}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + "- bar\n") # 'list_absent' function tests: 6 def test_list_absent(self): - with self.setGrains({'a': 'aval', 'foo': ['bar']}): - ret = grains.list_absent( - name='foo', - value='bar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value bar was deleted from grain foo') - self.assertEqual(ret['changes'], {'deleted': ['bar']}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': []}) - self.assertGrainFileContent("a: aval\n" - + "foo: []\n" - ) + with self.setGrains({"a": "aval", "foo": ["bar"]}): + ret = grains.list_absent(name="foo", value="bar") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value bar was deleted from grain foo") + self.assertEqual(ret["changes"], {"deleted": ["bar"]}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": []}) + self.assertGrainFileContent("a: aval\n" + "foo: []\n") def test_list_absent_nested(self): - with self.setGrains({'a': 'aval', 'foo': {'list': ['bar']}}): - ret = grains.list_absent( - name='foo:list', - value='bar') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value bar was deleted from grain foo:list') - self.assertEqual(ret['changes'], {'deleted': ['bar']}) + with self.setGrains({"a": "aval", "foo": {"list": ["bar"]}}): + ret = grains.list_absent(name="foo:list", value="bar") + self.assertEqual(ret["result"], True) self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': {'list': []}}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + " list: []\n" + ret["comment"], "Value bar was deleted from grain foo:list" ) + self.assertEqual(ret["changes"], {"deleted": ["bar"]}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": {"list": []}}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + " list: []\n") def test_list_absent_inexistent(self): - with self.setGrains({'a': 'aval'}): - ret = grains.list_absent( - name='foo', - value='baz') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain foo does not exist') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval'}) + with self.setGrains({"a": "aval"}): + ret = grains.list_absent(name="foo", value="baz") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain foo does not exist") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval"}) self.assertGrainFileContent("a: aval\n") def test_list_absent_inexistent_nested(self): - with self.setGrains({'a': 'aval'}): - ret = grains.list_absent( - name='foo:list', - value='baz') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Grain foo:list does not exist') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval'}) + with self.setGrains({"a": "aval"}): + ret = grains.list_absent(name="foo:list", value="baz") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Grain foo:list does not exist") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval"}) self.assertGrainFileContent("a: aval\n") def test_list_absent_not_a_list(self): - with self.setGrains({'a': 'aval', 'foo': 'bar'}): - ret = grains.list_absent( - name='foo', - value='bar') - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'Grain foo is not a valid list') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': 'bar'}) - self.assertGrainFileContent("a: aval\n" - + "foo: bar\n" - ) + with self.setGrains({"a": "aval", "foo": "bar"}): + ret = grains.list_absent(name="foo", value="bar") + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "Grain foo is not a valid list") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": "bar"}) + self.assertGrainFileContent("a: aval\n" + "foo: bar\n") def test_list_absent_already(self): - with self.setGrains({'a': 'aval', 'foo': ['bar']}): - ret = grains.list_absent( - name='foo', - value='baz') - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], 'Value baz is absent from grain foo') - self.assertEqual(ret['changes'], {}) - self.assertEqual( - grains.__grains__, - {'a': 'aval', 'foo': ['bar']}) - self.assertGrainFileContent("a: aval\n" - + "foo:\n" - + "- bar\n" - ) + with self.setGrains({"a": "aval", "foo": ["bar"]}): + ret = grains.list_absent(name="foo", value="baz") + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], "Value baz is absent from grain foo") + self.assertEqual(ret["changes"], {}) + self.assertEqual(grains.__grains__, {"a": "aval", "foo": ["bar"]}) + self.assertGrainFileContent("a: aval\n" + "foo:\n" + "- bar\n") diff --git a/tests/unit/states/test_group.py b/tests/unit/states/test_group.py index 5dba7c51a1a..065eae4214c 100644 --- a/tests/unit/states/test_group.py +++ b/tests/unit/states/test_group.py @@ -1,107 +1,122 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.states.group as group from salt.utils.odict import OrderedDict +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class GroupTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the group state - ''' + """ + def setup_loader_modules(self): return {group: {}} def test_present(self): - ''' + """ Test to ensure that a group is present - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': {} - } + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": {}} - ret.update({'comment': 'Error: Conflicting options "members" with' - ' "addusers" and/or "delusers" can not be used together. ', - 'result': None}) - self.assertDictEqual(group.present("salt", delusers=True, - members=True), ret) + ret.update( + { + "comment": 'Error: Conflicting options "members" with' + ' "addusers" and/or "delusers" can not be used together. ', + "result": None, + } + ) + self.assertDictEqual(group.present("salt", delusers=True, members=True), ret) - ret.update({'comment': 'Error. Same user(s) can not be' - ' added and deleted simultaneously'}) - self.assertDictEqual(group.present("salt", addusers=['a'], - delusers=['a']), ret) + ret.update( + { + "comment": "Error. Same user(s) can not be" + " added and deleted simultaneously" + } + ) + self.assertDictEqual(group.present("salt", addusers=["a"], delusers=["a"]), ret) - ret.update({'comment': 'The following group attributes are set' - ' to be changed:\nkey0: value0\nkey1: value1\n'}) + ret.update( + { + "comment": "The following group attributes are set" + " to be changed:\nkey0: value0\nkey1: value1\n" + } + ) - mock = MagicMock(side_effect=[OrderedDict((('key0', 'value0'), ('key1', 'value1'))), - False, - False, - False]) - with patch.object(group, '_changes', mock): + mock = MagicMock( + side_effect=[ + OrderedDict((("key0", "value0"), ("key1", "value1"))), + False, + False, + False, + ] + ) + with patch.object(group, "_changes", mock): with patch.dict(group.__opts__, {"test": True}): self.assertDictEqual(group.present("salt"), ret) - ret.update({'comment': 'Group salt set to be added'}) + ret.update({"comment": "Group salt set to be added"}) self.assertDictEqual(group.present("salt"), ret) with patch.dict(group.__opts__, {"test": False}): - mock = MagicMock(return_value=[{'gid': 1, 'name': 'stack'}]) - with patch.dict(group.__salt__, {'group.getent': mock}): - ret.update({'result': False, - 'comment': 'Group salt is not present but' - ' gid 1 is already taken by group stack'}) + mock = MagicMock(return_value=[{"gid": 1, "name": "stack"}]) + with patch.dict(group.__salt__, {"group.getent": mock}): + ret.update( + { + "result": False, + "comment": "Group salt is not present but" + " gid 1 is already taken by group stack", + } + ) self.assertDictEqual(group.present("salt", 1), ret) mock = MagicMock(return_value=False) - with patch.dict(group.__salt__, {'group.add': mock}): - ret.update({'comment': - 'Failed to create new group salt'}) + with patch.dict(group.__salt__, {"group.add": mock}): + ret.update({"comment": "Failed to create new group salt"}) self.assertDictEqual(group.present("salt"), ret) def test_absent(self): - ''' + """ Test to ensure that the named group is absent - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': {} - } + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": {}} mock = MagicMock(side_effect=[True, True, True, False]) - with patch.dict(group.__salt__, {'group.info': mock}): + with patch.dict(group.__salt__, {"group.info": mock}): with patch.dict(group.__opts__, {"test": True}): - ret.update({'result': None, - 'comment': 'Group salt is set for removal'}) + ret.update({"result": None, "comment": "Group salt is set for removal"}) self.assertDictEqual(group.absent("salt"), ret) with patch.dict(group.__opts__, {"test": False}): mock = MagicMock(side_effect=[True, False]) - with patch.dict(group.__salt__, {'group.delete': mock}): - ret.update({'result': True, 'changes': {'salt': ''}, - 'comment': 'Removed group salt'}) - self.assertDictEqual(group.absent('salt'), ret) + with patch.dict(group.__salt__, {"group.delete": mock}): + ret.update( + { + "result": True, + "changes": {"salt": ""}, + "comment": "Removed group salt", + } + ) + self.assertDictEqual(group.absent("salt"), ret) - ret.update({'changes': {}, 'result': False, - 'comment': 'Failed to remove group salt'}) - self.assertDictEqual(group.absent('salt'), ret) + ret.update( + { + "changes": {}, + "result": False, + "comment": "Failed to remove group salt", + } + ) + self.assertDictEqual(group.absent("salt"), ret) - ret.update({'result': True, - 'comment': 'Group not present'}) - self.assertDictEqual(group.absent('salt'), ret) + ret.update({"result": True, "comment": "Group not present"}) + self.assertDictEqual(group.absent("salt"), ret) diff --git a/tests/unit/states/test_heat.py b/tests/unit/states/test_heat.py index 8ce7d1db230..74b3cd0f052 100644 --- a/tests/unit/states/test_heat.py +++ b/tests/unit/states/test_heat.py @@ -1,156 +1,207 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.runtests import RUNTIME_VARS -from tests.support.mock import ( - MagicMock, - patch, -) - -# Import Salt Libs -import tests.unit.modules.test_heat -import salt.utils.platform -import salt.states.heat as heat import salt.modules.file as file_ import salt.modules.heat import salt.modules.win_file as win_file +import salt.states.heat as heat +import salt.utils.platform import salt.utils.win_dacl as dacl +# Import Salt Libs +import tests.unit.modules.test_heat + +# Import Salt Testing Libs +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 + class HeatTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.heat - ''' + """ + def setup_loader_modules(self): return { heat: { - '_auth': tests.unit.modules.test_heat.MockClient, - '__opts__': {'test': False}, + "_auth": tests.unit.modules.test_heat.MockClient, + "__opts__": {"test": False}, }, - salt.modules.heat: {'_auth': - tests.unit.modules.test_heat.MockClient, }, + salt.modules.heat: {"_auth": tests.unit.modules.test_heat.MockClient}, file_: { - '__opts__': {'hash_type': 'sha256', - 'cachedir': os.path.join(RUNTIME_VARS.TMP, - 'rootdir', 'cache'), - 'test': False}, - '__salt__': {'config.option': - MagicMock(return_value={'obfuscate_templates': - False}), - 'config.backup_mode': - MagicMock(return_value=False)} + "__opts__": { + "hash_type": "sha256", + "cachedir": os.path.join(RUNTIME_VARS.TMP, "rootdir", "cache"), + "test": False, + }, + "__salt__": { + "config.option": MagicMock( + return_value={"obfuscate_templates": False} + ), + "config.backup_mode": MagicMock(return_value=False), + }, }, win_file: { - '__utils__': {'dacl.check_perms': salt.utils.win_dacl.check_perms} - }, - dacl: {'__opts__': {'test': False}}, + "__utils__": {"dacl.check_perms": salt.utils.win_dacl.check_perms} + }, + dacl: {"__opts__": {"test": False}}, } def setUp(self): - self.patch_check = patch('salt.modules.file.check_perms', file_.check_perms) + self.patch_check = patch("salt.modules.file.check_perms", file_.check_perms) if salt.utils.platform.is_windows(): - self.patch_check = patch('salt.modules.file.check_perms', win_file.check_perms) + self.patch_check = patch( + "salt.modules.file.check_perms", win_file.check_perms + ) def test_heat_deployed(self): - ''' + """ Test salt.states.heat.deployed method - ''' - exp_ret = {'name': ('mystack',), - 'comment': "Created stack 'mystack'.", - 'changes': {'stack_name': 'mystack', - 'comment': 'Create stack'}, - 'result': True} + """ + exp_ret = { + "name": ("mystack",), + "comment": "Created stack 'mystack'.", + "changes": {"stack_name": "mystack", "comment": "Create stack"}, + "result": True, + } - patch_heat = patch.dict(heat.__salt__, - {'heat.show_stack': MagicMock(return_value={'result': False}), - 'heat.create_stack': salt.modules.heat.create_stack}) + patch_heat = patch.dict( + heat.__salt__, + { + "heat.show_stack": MagicMock(return_value={"result": False}), + "heat.create_stack": salt.modules.heat.create_stack, + }, + ) - patch_file = patch.dict('salt.modules.heat.__salt__', - {'file.get_managed': file_.get_managed, - 'file.manage_file': file_.manage_file, },) + patch_file = patch.dict( + "salt.modules.heat.__salt__", + { + "file.get_managed": file_.get_managed, + "file.manage_file": file_.manage_file, + }, + ) - patch_create = patch('salt.modules.heat.create_stack', - MagicMock(return_value={'result': True, 'comment': - "Created stack 'mystack'."})) + patch_create = patch( + "salt.modules.heat.create_stack", + MagicMock( + return_value={"result": True, "comment": "Created stack 'mystack'."} + ), + ) with patch_heat, patch_file, patch_create, self.patch_check: - ret = heat.deployed(name='mystack', - profile='openstack1', - template=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-template.yml'), - poll=0) + ret = heat.deployed( + name="mystack", + profile="openstack1", + template=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-template.yml" + ), + poll=0, + ) assert ret == exp_ret def test_heat_deployed_environment(self): - ''' + """ Test salt.states.heat.deployed method with environment set - ''' - exp_ret = {'name': ('mystack',), - 'comment': "Created stack 'mystack'.", - 'changes': {'stack_name': 'mystack', - 'comment': 'Create stack'}, - 'result': True} + """ + exp_ret = { + "name": ("mystack",), + "comment": "Created stack 'mystack'.", + "changes": {"stack_name": "mystack", "comment": "Create stack"}, + "result": True, + } - patch_heat = patch.dict(heat.__salt__, - {'heat.show_stack': MagicMock(return_value={'result': False}), - 'heat.create_stack': salt.modules.heat.create_stack}) + patch_heat = patch.dict( + heat.__salt__, + { + "heat.show_stack": MagicMock(return_value={"result": False}), + "heat.create_stack": salt.modules.heat.create_stack, + }, + ) - patch_file = patch.dict('salt.modules.heat.__salt__', - {'file.get_managed': file_.get_managed, - 'file.manage_file': file_.manage_file, },) + patch_file = patch.dict( + "salt.modules.heat.__salt__", + { + "file.get_managed": file_.get_managed, + "file.manage_file": file_.manage_file, + }, + ) - patch_create = patch('salt.modules.heat.create_stack', - MagicMock(return_value={'result': True, 'comment': - "Created stack 'mystack'."})) + patch_create = patch( + "salt.modules.heat.create_stack", + MagicMock( + return_value={"result": True, "comment": "Created stack 'mystack'."} + ), + ) with patch_heat, patch_file, patch_create, self.patch_check: - ret = heat.deployed(name='mystack', - profile='openstack1', - template=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-template.yml'), - poll=0, - environment=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-env.yml')) + ret = heat.deployed( + name="mystack", + profile="openstack1", + template=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-template.yml" + ), + poll=0, + environment=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-env.yml" + ), + ) assert ret == exp_ret def test_heat_deployed_environment_error(self): - ''' + """ Test salt.states.heat.deployed method with environment set and there is an error reading the environment file. - ''' - exp_ret = {'name': ('mystack',), - 'comment': 'Error parsing template Template format version not found.', - 'changes': {'stack_name': 'mystack', - 'comment': 'Create stack'}, - 'result': False} + """ + exp_ret = { + "name": ("mystack",), + "comment": "Error parsing template Template format version not found.", + "changes": {"stack_name": "mystack", "comment": "Create stack"}, + "result": False, + } - patch_heat = patch.dict(heat.__salt__, - {'heat.show_stack': MagicMock(return_value={'result': False}), - 'heat.create_stack': salt.modules.heat.create_stack}) + patch_heat = patch.dict( + heat.__salt__, + { + "heat.show_stack": MagicMock(return_value={"result": False}), + "heat.create_stack": salt.modules.heat.create_stack, + }, + ) - patch_file = patch.dict('salt.modules.heat.__salt__', - {'file.get_managed': file_.get_managed, - 'file.manage_file': MagicMock(side_effect=[{'result': True}, - {'result': False}])}) + patch_file = patch.dict( + "salt.modules.heat.__salt__", + { + "file.get_managed": file_.get_managed, + "file.manage_file": MagicMock( + side_effect=[{"result": True}, {"result": False}] + ), + }, + ) - patch_create = patch('salt.modules.heat.create_stack', - MagicMock(return_value={'result': True, 'comment': - "Created stack 'mystack'."})) + patch_create = patch( + "salt.modules.heat.create_stack", + MagicMock( + return_value={"result": True, "comment": "Created stack 'mystack'."} + ), + ) with patch_heat, patch_file, patch_create, self.patch_check: - ret = heat.deployed(name='mystack', - profile='openstack1', - template=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-template.yml'), - poll=0, - environment=os.path.join(RUNTIME_VARS.BASE_FILES, - 'templates', 'heat-env.yml')) + ret = heat.deployed( + name="mystack", + profile="openstack1", + template=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-template.yml" + ), + poll=0, + environment=os.path.join( + RUNTIME_VARS.BASE_FILES, "templates", "heat-env.yml" + ), + ) assert ret == exp_ret diff --git a/tests/unit/states/test_helm.py b/tests/unit/states/test_helm.py new file mode 100644 index 00000000000..43c50df8971 --- /dev/null +++ b/tests/unit/states/test_helm.py @@ -0,0 +1,263 @@ +# -*- coding: utf-8 -*- + +# Import Python libs +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.states.helm as helm + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + + +class HelmTestCase(TestCase, LoaderModuleMockMixin): + """ + Test cases for salt.modules.helm + """ + + def setup_loader_modules(self): + return {helm: {}} + + def test_repo_managed_import_failed_repo_manage(self): + ret = { + "name": "state_id", + "changes": {}, + "result": False, + "comment": "'helm.repo_manage' modules not available on this minion.", + } + self.assertEqual(helm.repo_managed("state_id"), ret) + + def test_repo_managed_import_failed_repo_update(self): + mock_helm_modules = {"helm.repo_manage": MagicMock(return_value=True)} + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "changes": {}, + "result": False, + "comment": "'helm.repo_update' modules not available on this minion.", + } + self.assertEqual(helm.repo_managed("state_id"), ret) + + def test_repo_managed_is_testing(self): + mock_helm_modules = { + "helm.repo_manage": MagicMock(return_value=True), + "helm.repo_update": MagicMock(return_value=True), + } + with patch.dict(helm.__salt__, mock_helm_modules): + mock__opts__ = {"test": MagicMock(return_value=True)} + with patch.dict(helm.__opts__, mock__opts__): + ret = { + "name": "state_id", + "result": None, + "comment": "Helm repo would have been managed.", + "changes": {}, + } + self.assertEqual(helm.repo_managed("state_id"), ret) + + def test_repo_managed_success(self): + result_changes = {"added": True, "removed": True, "failed": False} + mock_helm_modules = { + "helm.repo_manage": MagicMock(return_value=result_changes), + "helm.repo_update": MagicMock(return_value=True), + } + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "result": True, + "comment": "Repositories were added or removed.", + "changes": result_changes, + } + self.assertEqual(helm.repo_managed("state_id"), ret) + + def test_repo_managed_success_with_update(self): + result_changes = {"added": True, "removed": True, "failed": False} + mock_helm_modules = { + "helm.repo_manage": MagicMock(return_value=result_changes), + "helm.repo_update": MagicMock(return_value=True), + } + result_wanted = result_changes + result_wanted.update({"repo_update": True}) + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "result": True, + "comment": "Repositories were added or removed.", + "changes": result_wanted, + } + self.assertEqual(helm.repo_managed("state_id"), ret) + + def test_repo_managed_failed(self): + result_changes = {"added": True, "removed": True, "failed": True} + mock_helm_modules = { + "helm.repo_manage": MagicMock(return_value=result_changes), + "helm.repo_update": MagicMock(return_value=True), + } + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "result": False, + "comment": "Failed to add or remove some repositories.", + "changes": result_changes, + } + self.assertEqual(helm.repo_managed("state_id"), ret) + + def test_repo_updated_import_failed(self): + ret = { + "name": "state_id", + "changes": {}, + "result": False, + "comment": "'helm.repo_update' modules not available on this minion.", + } + self.assertEqual(helm.repo_updated("state_id"), ret) + + def test_repo_updated_is_testing(self): + mock_helm_modules = {"helm.repo_update": MagicMock(return_value=True)} + with patch.dict(helm.__salt__, mock_helm_modules): + mock__opts__ = {"test": MagicMock(return_value=True)} + with patch.dict(helm.__opts__, mock__opts__): + ret = { + "name": "state_id", + "result": None, + "comment": "Helm repo would have been updated.", + "changes": {}, + } + self.assertEqual(helm.repo_updated("state_id"), ret) + + def test_repo_updated_success(self): + mock_helm_modules = {"helm.repo_update": MagicMock(return_value=True)} + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "result": True, + "comment": "Helm repo is updated.", + "changes": {}, + } + self.assertEqual(helm.repo_updated("state_id"), ret) + + def test_repo_updated_failed(self): + mock_helm_modules = {"helm.repo_update": MagicMock(return_value=False)} + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "result": False, + "comment": "Failed to sync some repositories.", + "changes": False, + } + self.assertEqual(helm.repo_updated("state_id"), ret) + + def test_release_present_import_failed_helm_status(self): + ret = { + "name": "state_id", + "changes": {}, + "result": False, + "comment": "'helm.status' modules not available on this minion.", + } + self.assertEqual(helm.release_present("state_id", "mychart"), ret) + + def test_release_present_import_failed_helm_install(self): + mock_helm_modules = {"helm.status": MagicMock(return_value=True)} + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "changes": {}, + "result": False, + "comment": "'helm.install' modules not available on this minion.", + } + self.assertEqual(helm.release_present("state_id", "mychart"), ret) + + def test_release_present_import_failed_helm_upgrade(self): + mock_helm_modules = { + "helm.status": MagicMock(return_value=True), + "helm.install": MagicMock(return_value=True), + } + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "changes": {}, + "result": False, + "comment": "'helm.upgrade' modules not available on this minion.", + } + self.assertEqual(helm.release_present("state_id", "mychart"), ret) + + def test_release_present_is_testing(self): + mock_helm_modules = { + "helm.status": MagicMock(return_value=True), + "helm.install": MagicMock(return_value=True), + "helm.upgrade": MagicMock(return_value=True), + } + with patch.dict(helm.__salt__, mock_helm_modules): + mock__opts__ = {"test": MagicMock(return_value=True)} + with patch.dict(helm.__opts__, mock__opts__): + ret = { + "name": "state_id", + "result": None, + "comment": "Helm release would have been installed or updated.", + "changes": {}, + } + self.assertEqual(helm.release_present("state_id", "mychart"), ret) + + def test_release_absent_import_failed_helm_uninstall(self): + ret = { + "name": "state_id", + "changes": {}, + "result": False, + "comment": "'helm.uninstall' modules not available on this minion.", + } + self.assertEqual(helm.release_absent("state_id"), ret) + + def test_release_absent_import_failed_helm_status(self): + mock_helm_modules = {"helm.uninstall": MagicMock(return_value=True)} + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "changes": {}, + "result": False, + "comment": "'helm.status' modules not available on this minion.", + } + self.assertEqual(helm.release_absent("state_id"), ret) + + def test_release_absent_is_testing(self): + mock_helm_modules = { + "helm.status": MagicMock(return_value=True), + "helm.uninstall": MagicMock(return_value=True), + } + with patch.dict(helm.__salt__, mock_helm_modules): + mock__opts__ = {"test": MagicMock(return_value=True)} + with patch.dict(helm.__opts__, mock__opts__): + ret = { + "name": "state_id", + "result": None, + "comment": "Helm release would have been uninstalled.", + "changes": {}, + } + self.assertEqual(helm.release_absent("state_id"), ret) + + def test_release_absent_success(self): + mock_helm_modules = { + "helm.status": MagicMock(return_value={}), + "helm.uninstall": MagicMock(return_value=True), + } + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "result": True, + "comment": "Helm release state_id is absent.", + "changes": {"absent": "state_id"}, + } + self.assertEqual(helm.release_absent("state_id"), ret) + + def test_release_absent_error(self): + mock_helm_modules = { + "helm.status": MagicMock(return_value={}), + "helm.uninstall": MagicMock(return_value="error"), + } + with patch.dict(helm.__salt__, mock_helm_modules): + ret = { + "name": "state_id", + "result": False, + "comment": "error", + "changes": {}, + } + self.assertEqual(helm.release_absent("state_id"), ret) diff --git a/tests/unit/states/test_hg.py b/tests/unit/states/test_hg.py index d1b5efe302a..903ba60002c 100644 --- a/tests/unit/states/test_hg.py +++ b/tests/unit/states/test_hg.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt Libs @@ -11,107 +12,132 @@ import salt.states.hg as hg # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class HgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the svn state - ''' + """ + def setup_loader_modules(self): return {hg: {}} def test_latest(self): - ''' + """ Test to Make sure the repository is cloned to the given directory and is up to date - ''' - ret = {'changes': {}, 'comment': '', 'name': 'salt', 'result': True} + """ + ret = {"changes": {}, "comment": "", "name": "salt", "result": True} mock = MagicMock(return_value=True) - with patch.object(hg, '_fail', mock): + with patch.object(hg, "_fail", mock): self.assertTrue(hg.latest("salt")) mock = MagicMock(side_effect=[False, True, False, False, False, False]) - with patch.object(os.path, 'isdir', mock): + with patch.object(os.path, "isdir", mock): mock = MagicMock(return_value=True) - with patch.object(hg, '_handle_existing', mock): + with patch.object(hg, "_handle_existing", mock): self.assertTrue(hg.latest("salt", target="c:\\salt")) - with patch.dict(hg.__opts__, {'test': True}): + with patch.dict(hg.__opts__, {"test": True}): mock = MagicMock(return_value=True) - with patch.object(hg, '_neutral_test', mock): + with patch.object(hg, "_neutral_test", mock): self.assertTrue(hg.latest("salt", target="c:\\salt")) - with patch.dict(hg.__opts__, {'test': False}): + with patch.dict(hg.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.object(hg, '_clone_repo', mock): - self.assertDictEqual(hg.latest("salt", target="c:\\salt"), - ret) + with patch.object(hg, "_clone_repo", mock): + self.assertDictEqual(hg.latest("salt", target="c:\\salt"), ret) def test_latest_update_changes(self): - ''' + """ Test to make sure we don't update even if we have changes - ''' - ret = {'changes': {}, 'comment': '', - 'name': 'salt', 'result': True} - revision_mock = MagicMock(return_value='abcdef') - pull_mock = MagicMock(return_value='Blah.') + """ + ret = {"changes": {}, "comment": "", "name": "salt", "result": True} + revision_mock = MagicMock(return_value="abcdef") + pull_mock = MagicMock(return_value="Blah.") update_mock = MagicMock() - with patch.dict(hg.__salt__, {'hg.revision': revision_mock, - 'hg.pull': pull_mock, - 'hg.update': update_mock}): + with patch.dict( + hg.__salt__, + { + "hg.revision": revision_mock, + "hg.pull": pull_mock, + "hg.update": update_mock, + }, + ): mock = MagicMock(side_effect=[True, True]) - with patch.object(os.path, 'isdir', mock): + with patch.object(os.path, "isdir", mock): mock = MagicMock(return_value=True) - with patch.dict(hg.__opts__, {'test': False}): - with patch.object(hg, '_clone_repo', mock): - self.assertDictEqual(hg.latest("salt", target="c:\\salt", update_head=True), ret) + with patch.dict(hg.__opts__, {"test": False}): + with patch.object(hg, "_clone_repo", mock): + self.assertDictEqual( + hg.latest("salt", target="c:\\salt", update_head=True), ret + ) assert update_mock.called def test_latest_no_update_changes(self): - ''' + """ Test to make sure we don't update even if we have changes - ''' - ret = {'changes': {}, 'comment': 'Update is probably required but update_head=False so we will skip updating.', - 'name': 'salt', 'result': True} - revision_mock = MagicMock(return_value='abcdef') - pull_mock = MagicMock(return_value='Blah.') + """ + ret = { + "changes": {}, + "comment": "Update is probably required but update_head=False so we will skip updating.", + "name": "salt", + "result": True, + } + revision_mock = MagicMock(return_value="abcdef") + pull_mock = MagicMock(return_value="Blah.") update_mock = MagicMock() - with patch.dict(hg.__salt__, {'hg.revision': revision_mock, - 'hg.pull': pull_mock, - 'hg.update': update_mock}): + with patch.dict( + hg.__salt__, + { + "hg.revision": revision_mock, + "hg.pull": pull_mock, + "hg.update": update_mock, + }, + ): mock = MagicMock(side_effect=[True, True]) - with patch.object(os.path, 'isdir', mock): + with patch.object(os.path, "isdir", mock): mock = MagicMock(return_value=True) - with patch.dict(hg.__opts__, {'test': False}): - with patch.object(hg, '_clone_repo', mock): - self.assertDictEqual(hg.latest("salt", target="c:\\salt", update_head=False), ret) + with patch.dict(hg.__opts__, {"test": False}): + with patch.object(hg, "_clone_repo", mock): + self.assertDictEqual( + hg.latest("salt", target="c:\\salt", update_head=False), ret + ) assert not update_mock.called def test_latest_no_update_no_changes(self): - ''' + """ Test to Make sure the repository is cloned to the given directory and is up to date - ''' - ret = {'changes': {}, 'comment': 'No changes found and update_head=False so will skip updating.', - 'name': 'salt', 'result': True} - revision_mock = MagicMock(return_value='abcdef') - pull_mock = MagicMock(return_value='Blah no changes found.') + """ + ret = { + "changes": {}, + "comment": "No changes found and update_head=False so will skip updating.", + "name": "salt", + "result": True, + } + revision_mock = MagicMock(return_value="abcdef") + pull_mock = MagicMock(return_value="Blah no changes found.") update_mock = MagicMock() - with patch.dict(hg.__salt__, {'hg.revision': revision_mock, - 'hg.pull': pull_mock, - 'hg.update': update_mock}): + with patch.dict( + hg.__salt__, + { + "hg.revision": revision_mock, + "hg.pull": pull_mock, + "hg.update": update_mock, + }, + ): mock = MagicMock(side_effect=[True, True]) - with patch.object(os.path, 'isdir', mock): + with patch.object(os.path, "isdir", mock): mock = MagicMock(return_value=True) - with patch.dict(hg.__opts__, {'test': False}): - with patch.object(hg, '_clone_repo', mock): - self.assertDictEqual(hg.latest("salt", target="c:\\salt", update_head=False), ret) + with patch.dict(hg.__opts__, {"test": False}): + with patch.object(hg, "_clone_repo", mock): + self.assertDictEqual( + hg.latest("salt", target="c:\\salt", update_head=False), ret + ) assert not update_mock.called diff --git a/tests/unit/states/test_host.py b/tests/unit/states/test_host.py index dc114d2a9de..35578da7285 100644 --- a/tests/unit/states/test_host.py +++ b/tests/unit/states/test_host.py @@ -1,125 +1,128 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import logging + # Import Salt Libs import salt.states.host as host # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - call, - patch, -) + +log = logging.getLogger(__name__) class HostTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the host state - ''' - hostname = 'salt' - localhost_ip = '127.0.0.1' - ip_list = ['203.0.113.113', '203.0.113.14'] + """ + + hostname = "salt" + localhost_ip = "127.0.0.1" + ip_list = ["203.0.113.113", "203.0.113.14"] default_hosts = { - ip_list[0]: [hostname], - ip_list[1]: [hostname], + ip_list[0]: {"aliases": [hostname]}, + ip_list[1]: {"aliases": [hostname]}, } def setUp(self): self.add_host_mock = MagicMock(return_value=True) self.rm_host_mock = MagicMock(return_value=True) + self.set_comment_mock = MagicMock(return_value=True) self.list_hosts_mock = MagicMock(return_value=self.default_hosts) def setup_loader_modules(self): return { - host: { - '__opts__': { - 'test': False, - }, - }, + host: {"__opts__": {"test": False}}, } def test_present(self): - ''' + """ Test to ensures that the named host is present with the given ip - ''' + """ add_host = MagicMock(return_value=True) rm_host = MagicMock(return_value=True) - hostname = 'salt' - ip_str = '127.0.0.1' - ip_list = ['10.1.2.3', '10.4.5.6'] + set_comment = MagicMock(return_value=True) + hostname = "salt" + ip_str = "127.0.0.1" + ip_list = ["10.1.2.3", "10.4.5.6"] # Case 1: No match for hostname. Single IP address passed to the state. - list_hosts = MagicMock(return_value={ - '127.0.0.1': ['localhost'], - }) - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + list_hosts = MagicMock(return_value={"127.0.0.1": {"aliases": ["localhost"]}}) + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_str) - assert ret['result'] is True - assert ret['comment'] == 'Added host {0} ({1})'.format(hostname, ip_str), ret['comment'] - assert ret['changes'] == { - 'added': { - ip_str: [hostname], - } - }, ret['changes'] + assert ret["result"] is True + assert ret["comment"] == "Added host {0} ({1})".format( + hostname, ip_str + ), ret["comment"] + assert ret["changes"] == {"added": {ip_str: [hostname]}}, ret["changes"] expected = [call(ip_str, hostname)] assert add_host.mock_calls == expected, add_host.mock_calls assert rm_host.mock_calls == [], rm_host.mock_calls # Case 2: No match for hostname. Multiple IP addresses passed to the # state. - list_hosts = MagicMock(return_value={ - '127.0.0.1': ['localhost'], - }) + list_hosts = MagicMock(return_value={"127.0.0.1": {"aliases": ["localhost"]}}) add_host.reset_mock() rm_host.reset_mock() - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_list) - assert ret['result'] is True - assert 'Added host {0} ({1})'.format(hostname, ip_list[0]) in ret['comment'] - assert 'Added host {0} ({1})'.format(hostname, ip_list[1]) in ret['comment'] - assert ret['changes'] == { - 'added': { - ip_list[0]: [hostname], - ip_list[1]: [hostname], - } - }, ret['changes'] + assert ret["result"] is True + assert "Added host {0} ({1})".format(hostname, ip_list[0]) in ret["comment"] + assert "Added host {0} ({1})".format(hostname, ip_list[1]) in ret["comment"] + assert ret["changes"] == { + "added": {ip_list[0]: [hostname], ip_list[1]: [hostname]} + }, ret["changes"] expected = sorted([call(x, hostname) for x in ip_list]) assert sorted(add_host.mock_calls) == expected, add_host.mock_calls assert rm_host.mock_calls == [], rm_host.mock_calls # Case 3: Match for hostname, but no matching IP. Single IP address # passed to the state. - list_hosts = MagicMock(return_value={ - '127.0.0.1': ['localhost'], - ip_list[0]: [hostname], - }) + list_hosts = MagicMock( + return_value={ + "127.0.0.1": {"aliases": ["localhost"]}, + ip_list[0]: {"aliases": [hostname]}, + } + ) add_host.reset_mock() rm_host.reset_mock() - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_str) - assert ret['result'] is True - assert 'Added host {0} ({1})'.format(hostname, ip_str) in ret['comment'] - assert 'Host {0} present for IP address {1}'.format(hostname, ip_list[0]) in ret['warnings'][0] - assert ret['changes'] == { - 'added': { - ip_str: [hostname], - }, - }, ret['changes'] + assert ret["result"] is True + assert "Added host {0} ({1})".format(hostname, ip_str) in ret["comment"] + assert ( + "Host {0} present for IP address {1}".format(hostname, ip_list[0]) + in ret["warnings"][0] + ) + assert ret["changes"] == {"added": {ip_str: [hostname]}}, ret["changes"] expected = [call(ip_str, hostname)] assert add_host.mock_calls == expected, add_host.mock_calls assert rm_host.mock_calls == [], rm_host.mock_calls @@ -127,22 +130,24 @@ class HostTestCase(TestCase, LoaderModuleMockMixin): # Case 3a: Repeat the above with clean=True add_host.reset_mock() rm_host.reset_mock() - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_str, clean=True) - assert ret['result'] is True - assert 'Added host {0} ({1})'.format(hostname, ip_str) in ret['comment'] - assert 'Removed host {0} ({1})'.format(hostname, ip_list[0]) in ret['comment'] - assert ret['changes'] == { - 'added': { - ip_str: [hostname], - }, - 'removed': { - ip_list[0]: [hostname], - } - }, ret['changes'] + assert ret["result"] is True + assert "Added host {0} ({1})".format(hostname, ip_str) in ret["comment"] + assert ( + "Removed host {0} ({1})".format(hostname, ip_list[0]) in ret["comment"] + ) + assert ret["changes"] == { + "added": {ip_str: [hostname]}, + "removed": {ip_list[0]: [hostname]}, + }, ret["changes"] expected = [call(ip_str, hostname)] assert add_host.mock_calls == expected, add_host.mock_calls expected = [call(ip_list[0], hostname)] @@ -150,27 +155,30 @@ class HostTestCase(TestCase, LoaderModuleMockMixin): # Case 4: Match for hostname, but no matching IP. Multiple IP addresses # passed to the state. - cur_ip = '1.2.3.4' - list_hosts = MagicMock(return_value={ - '127.0.0.1': ['localhost'], - cur_ip: [hostname], - }) + cur_ip = "1.2.3.4" + list_hosts = MagicMock( + return_value={ + "127.0.0.1": {"aliases": ["localhost"]}, + cur_ip: {"aliases": [hostname]}, + } + ) add_host.reset_mock() rm_host.reset_mock() - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_list) - assert ret['result'] is True - assert 'Added host {0} ({1})'.format(hostname, ip_list[0]) in ret['comment'] - assert 'Added host {0} ({1})'.format(hostname, ip_list[1]) in ret['comment'] - assert ret['changes'] == { - 'added': { - ip_list[0]: [hostname], - ip_list[1]: [hostname], - }, - }, ret['changes'] + assert ret["result"] is True + assert "Added host {0} ({1})".format(hostname, ip_list[0]) in ret["comment"] + assert "Added host {0} ({1})".format(hostname, ip_list[1]) in ret["comment"] + assert ret["changes"] == { + "added": {ip_list[0]: [hostname], ip_list[1]: [hostname]}, + }, ret["changes"] expected = sorted([call(x, hostname) for x in ip_list]) assert sorted(add_host.mock_calls) == expected, add_host.mock_calls assert rm_host.mock_calls == [], rm_host.mock_calls @@ -178,24 +186,23 @@ class HostTestCase(TestCase, LoaderModuleMockMixin): # Case 4a: Repeat the above with clean=True add_host.reset_mock() rm_host.reset_mock() - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_list, clean=True) - assert ret['result'] is True - assert 'Added host {0} ({1})'.format(hostname, ip_list[0]) in ret['comment'] - assert 'Added host {0} ({1})'.format(hostname, ip_list[1]) in ret['comment'] - assert 'Removed host {0} ({1})'.format(hostname, cur_ip) in ret['comment'] - assert ret['changes'] == { - 'added': { - ip_list[0]: [hostname], - ip_list[1]: [hostname], - }, - 'removed': { - cur_ip: [hostname], - } - }, ret['changes'] + assert ret["result"] is True + assert "Added host {0} ({1})".format(hostname, ip_list[0]) in ret["comment"] + assert "Added host {0} ({1})".format(hostname, ip_list[1]) in ret["comment"] + assert "Removed host {0} ({1})".format(hostname, cur_ip) in ret["comment"] + assert ret["changes"] == { + "added": {ip_list[0]: [hostname], ip_list[1]: [hostname]}, + "removed": {cur_ip: [hostname]}, + }, ret["changes"] expected = sorted([call(x, hostname) for x in ip_list]) assert sorted(add_host.mock_calls) == expected, add_host.mock_calls expected = [call(cur_ip, hostname)] @@ -204,26 +211,28 @@ class HostTestCase(TestCase, LoaderModuleMockMixin): # Case 5: Multiple IP addresses passed to the state. One of them # matches, the other does not. There is also a non-matching IP that # must be removed. - cur_ip = '1.2.3.4' - list_hosts = MagicMock(return_value={ - '127.0.0.1': ['localhost'], - cur_ip: [hostname], - ip_list[0]: [hostname], - }) + cur_ip = "1.2.3.4" + list_hosts = MagicMock( + return_value={ + "127.0.0.1": {"aliases": ["localhost"]}, + cur_ip: {"aliases": [hostname]}, + ip_list[0]: {"aliases": [hostname]}, + } + ) add_host.reset_mock() rm_host.reset_mock() - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_list) - assert ret['result'] is True - assert 'Added host {0} ({1})'.format(hostname, ip_list[1]) in ret['comment'] - assert ret['changes'] == { - 'added': { - ip_list[1]: [hostname], - }, - }, ret['changes'] + assert ret["result"] is True + assert "Added host {0} ({1})".format(hostname, ip_list[1]) in ret["comment"] + assert ret["changes"] == {"added": {ip_list[1]: [hostname]}}, ret["changes"] expected = [call(ip_list[1], hostname)] assert add_host.mock_calls == expected, add_host.mock_calls assert rm_host.mock_calls == [], rm_host.mock_calls @@ -231,22 +240,22 @@ class HostTestCase(TestCase, LoaderModuleMockMixin): # Case 5a: Repeat the above with clean=True add_host.reset_mock() rm_host.reset_mock() - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_list, clean=True) - assert ret['result'] is True - assert 'Added host {0} ({1})'.format(hostname, ip_list[1]) in ret['comment'] - assert 'Removed host {0} ({1})'.format(hostname, cur_ip) in ret['comment'] - assert ret['changes'] == { - 'added': { - ip_list[1]: [hostname], - }, - 'removed': { - cur_ip: [hostname], - } - }, ret['changes'] + assert ret["result"] is True + assert "Added host {0} ({1})".format(hostname, ip_list[1]) in ret["comment"] + assert "Removed host {0} ({1})".format(hostname, cur_ip) in ret["comment"] + assert ret["changes"] == { + "added": {ip_list[1]: [hostname]}, + "removed": {cur_ip: [hostname]}, + }, ret["changes"] expected = [call(ip_list[1], hostname)] assert add_host.mock_calls == expected, add_host.mock_calls expected = [call(cur_ip, hostname)] @@ -254,196 +263,272 @@ class HostTestCase(TestCase, LoaderModuleMockMixin): # Case 6: Single IP address passed to the state, which matches the # current configuration for that hostname. No changes should be made. - list_hosts = MagicMock(return_value={ - ip_str: [hostname], - }) + list_hosts = MagicMock(return_value={ip_str: {"aliases": [hostname]}}) add_host.reset_mock() rm_host.reset_mock() - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_str) - assert ret['result'] is True - assert ret['comment'] == 'Host {0} ({1}) already present'.format(hostname, ip_str) in ret['comment'] - assert ret['changes'] == {}, ret['changes'] + assert ret["result"] is True + assert ( + ret["comment"] + == "Host {0} ({1}) already present".format(hostname, ip_str) + in ret["comment"] + ) + assert ret["changes"] == {}, ret["changes"] assert add_host.mock_calls == [], add_host.mock_calls assert rm_host.mock_calls == [], rm_host.mock_calls # Case 7: Multiple IP addresses passed to the state, which both match # the current configuration for that hostname. No changes should be # made. - list_hosts = MagicMock(return_value={ - ip_list[0]: [hostname], - ip_list[1]: [hostname], - }) + list_hosts = MagicMock( + return_value={ + ip_list[0]: {"aliases": [hostname]}, + ip_list[1]: {"aliases": [hostname]}, + } + ) add_host.reset_mock() rm_host.reset_mock() - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': add_host, - 'hosts.rm_host': rm_host}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": add_host, + "hosts.rm_host": rm_host, + }, + ): ret = host.present(hostname, ip_list) - assert ret['result'] is True - assert 'Host {0} ({1}) already present'.format(hostname, ip_list[0]) in ret['comment'] - assert 'Host {0} ({1}) already present'.format(hostname, ip_list[1]) in ret['comment'] - assert ret['changes'] == {}, ret['changes'] + assert ret["result"] is True + assert ( + "Host {0} ({1}) already present".format(hostname, ip_list[0]) + in ret["comment"] + ) + assert ( + "Host {0} ({1}) already present".format(hostname, ip_list[1]) + in ret["comment"] + ) + assert ret["changes"] == {}, ret["changes"] assert add_host.mock_calls == [], add_host.mock_calls assert rm_host.mock_calls == [], rm_host.mock_calls + # Case 8: Passing a comment to host.present with multiple IPs + list_hosts = MagicMock(return_value={"127.0.0.1": {"aliases": ["localhost"]}}) + self.add_host_mock.reset_mock() + self.rm_host_mock.reset_mock() + self.set_comment_mock.reset_mock() + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": self.add_host_mock, + "hosts.rm_host": self.rm_host_mock, + "hosts.set_comment": self.set_comment_mock, + }, + ): + ret = host.present(self.hostname, self.ip_list, comment="A comment") + assert ret["result"] is True + assert ( + "Added host {0} ({1})".format(self.hostname, self.ip_list[0]) + in ret["comment"] + ) + assert ( + "Added host {0} ({1})".format(self.hostname, self.ip_list[1]) + in ret["comment"] + ) + assert ( + "Set comment for host {0} (A comment)".format(self.ip_list[0]) + in ret["comment"] + ) + assert ( + "Set comment for host {0} (A comment)".format(self.ip_list[1]) + in ret["comment"] + ) + assert ret["changes"] == { + "added": { + self.ip_list[0]: [self.hostname], + self.ip_list[1]: [self.hostname], + }, + "comment_added": { + self.ip_list[0]: ["A comment"], + self.ip_list[1]: ["A comment"], + }, + }, ret["changes"] + expected = sorted([call(x, self.hostname) for x in self.ip_list]) + assert ( + sorted(self.add_host_mock.mock_calls) == expected + ), self.add_host_mock.mock_calls + + expected = sorted([call(x, "A comment") for x in self.ip_list]) + assert ( + sorted(self.set_comment_mock.mock_calls) == expected + ), self.set_comment_mock.mock_calls + + assert self.rm_host_mock.mock_calls == [], self.rm_host_mock.mock_calls + def test_host_present_should_return_True_if_test_and_no_changes(self): expected = { - 'comment': 'Host {} ({}) already present'.format( - self.hostname, - self.ip_list[0], + "comment": "Host {} ({}) already present".format( + self.hostname, self.ip_list[0], ), - 'changes': {}, - 'name': self.hostname, - 'result': True, + "changes": {}, + "name": self.hostname, + "result": True, } list_hosts = MagicMock( - return_value={self.ip_list[0]: [self.hostname]}, + return_value={self.ip_list[0]: {"aliases": [self.hostname]}}, ) - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': self.add_host_mock, - 'hosts.rm_host': self.rm_host_mock}): - with patch.dict(host.__opts__, {'test': True}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": self.add_host_mock, + "hosts.rm_host": self.rm_host_mock, + }, + ): + with patch.dict(host.__opts__, {"test": True}): ret = host.present(self.hostname, self.ip_list[:1]) self.assertDictEqual(ret, expected) def test_host_present_should_return_None_if_test_and_adding(self): expected = { - 'comment': '\n'.join([ - 'Host {} ({}) already present', - 'Host {} ({}) would be added', - ]).format( - self.hostname, - self.ip_list[0], - self.hostname, - self.ip_list[1], - ), - 'changes': {'added': {self.ip_list[1]: [self.hostname]}}, - 'name': self.hostname, - 'result': None, + "comment": "\n".join( + ["Host {} ({}) already present", "Host {} ({}) would be added"] + ).format(self.hostname, self.ip_list[0], self.hostname, self.ip_list[1],), + "changes": {"added": {self.ip_list[1]: [self.hostname]}}, + "name": self.hostname, + "result": None, } list_hosts = MagicMock( - return_value={self.ip_list[0]: [self.hostname]}, + return_value={self.ip_list[0]: {"aliases": [self.hostname]}}, ) - with patch.dict(host.__salt__, - {'hosts.list_hosts': list_hosts, - 'hosts.add_host': self.add_host_mock, - 'hosts.rm_host': self.rm_host_mock}): - with patch.dict(host.__opts__, {'test': True}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": list_hosts, + "hosts.add_host": self.add_host_mock, + "hosts.rm_host": self.rm_host_mock, + }, + ): + with patch.dict(host.__opts__, {"test": True}): ret = host.present(self.hostname, self.ip_list) self.assertDictEqual(ret, expected) def test_host_present_should_return_None_if_test_and_removing(self): expected = { - 'comment': '\n'.join([ - 'Host {} ({}) already present', - 'Host {} ({}) would be removed', - ]).format( - self.hostname, - self.ip_list[0], - self.hostname, - self.ip_list[1], - ), - 'changes': {'removed': {self.ip_list[1]: [self.hostname]}}, - 'name': self.hostname, - 'result': None, + "comment": "\n".join( + ["Host {} ({}) already present", "Host {} ({}) would be removed"] + ).format(self.hostname, self.ip_list[0], self.hostname, self.ip_list[1],), + "changes": {"removed": {self.ip_list[1]: [self.hostname]}}, + "name": self.hostname, + "result": None, } - with patch.dict(host.__salt__, - {'hosts.list_hosts': self.list_hosts_mock, - 'hosts.add_host': self.add_host_mock, - 'hosts.rm_host': self.rm_host_mock}): - with patch.dict(host.__opts__, {'test': True}): + with patch.dict( + host.__salt__, + { + "hosts.list_hosts": self.list_hosts_mock, + "hosts.add_host": self.add_host_mock, + "hosts.rm_host": self.rm_host_mock, + }, + ): + with patch.dict(host.__opts__, {"test": True}): ret = host.present(self.hostname, self.ip_list[:1], clean=True) self.assertDictEqual(ret, expected) def test_absent(self): - ''' + """ Test to ensure that the named host is absent - ''' - ret = {'changes': {}, - 'comment': 'Host salt (127.0.0.1) already absent', - 'name': 'salt', 'result': True} + """ + ret = { + "changes": {}, + "comment": "Host salt (127.0.0.1) already absent", + "name": "salt", + "result": True, + } mock = MagicMock(return_value=False) - with patch.dict(host.__salt__, {'hosts.has_pair': mock}): + with patch.dict(host.__salt__, {"hosts.has_pair": mock}): self.assertDictEqual(host.absent("salt", "127.0.0.1"), ret) def test_only_already(self): - ''' + """ Test only() when the state hasn't changed - ''' + """ expected = { - 'name': '127.0.1.1', - 'changes': {}, - 'result': True, - 'comment': 'IP address 127.0.1.1 already set to "foo.bar"'} - mock1 = MagicMock(return_value=['foo.bar']) - with patch.dict(host.__salt__, {'hosts.get_alias': mock1}): + "name": "127.0.1.1", + "changes": {}, + "result": True, + "comment": 'IP address 127.0.1.1 already set to "foo.bar"', + } + mock1 = MagicMock(return_value=["foo.bar"]) + with patch.dict(host.__salt__, {"hosts.get_alias": mock1}): mock2 = MagicMock(return_value=True) - with patch.dict(host.__salt__, {'hosts.set_host': mock2}): - with patch.dict(host.__opts__, {'test': False}): - self.assertDictEqual( - expected, - host.only("127.0.1.1", 'foo.bar')) + with patch.dict(host.__salt__, {"hosts.set_host": mock2}): + with patch.dict(host.__opts__, {"test": False}): + self.assertDictEqual(expected, host.only("127.0.1.1", "foo.bar")) def test_only_dryrun(self): - ''' + """ Test only() when state would change, but it's a dry run - ''' + """ expected = { - 'name': '127.0.1.1', - 'changes': {}, - 'result': None, - 'comment': 'Would change 127.0.1.1 from "foo.bar" to "foo.bar foo"'} - mock1 = MagicMock(return_value=['foo.bar']) - with patch.dict(host.__salt__, {'hosts.get_alias': mock1}): + "name": "127.0.1.1", + "changes": {}, + "result": None, + "comment": 'Would change 127.0.1.1 from "foo.bar" to "foo.bar foo"', + } + mock1 = MagicMock(return_value=["foo.bar"]) + with patch.dict(host.__salt__, {"hosts.get_alias": mock1}): mock2 = MagicMock(return_value=True) - with patch.dict(host.__salt__, {'hosts.set_host': mock2}): - with patch.dict(host.__opts__, {'test': True}): + with patch.dict(host.__salt__, {"hosts.set_host": mock2}): + with patch.dict(host.__opts__, {"test": True}): self.assertDictEqual( - expected, - host.only("127.0.1.1", ['foo.bar', 'foo'])) + expected, host.only("127.0.1.1", ["foo.bar", "foo"]) + ) def test_only_fail(self): - ''' + """ Test only() when state change fails - ''' + """ expected = { - 'name': '127.0.1.1', - 'changes': {}, - 'result': False, - 'comment': 'hosts.set_host failed to change 127.0.1.1' - + ' from "foo.bar" to "foo.bar foo"'} - mock = MagicMock(return_value=['foo.bar']) - with patch.dict(host.__salt__, {'hosts.get_alias': mock}): + "name": "127.0.1.1", + "changes": {}, + "result": False, + "comment": "hosts.set_host failed to change 127.0.1.1" + + ' from "foo.bar" to "foo.bar foo"', + } + mock = MagicMock(return_value=["foo.bar"]) + with patch.dict(host.__salt__, {"hosts.get_alias": mock}): mock = MagicMock(return_value=False) - with patch.dict(host.__salt__, {'hosts.set_host': mock}): - with patch.dict(host.__opts__, {'test': False}): + with patch.dict(host.__salt__, {"hosts.set_host": mock}): + with patch.dict(host.__opts__, {"test": False}): self.assertDictEqual( - expected, - host.only("127.0.1.1", ['foo.bar', 'foo'])) + expected, host.only("127.0.1.1", ["foo.bar", "foo"]) + ) def test_only_success(self): - ''' + """ Test only() when state successfully changes - ''' + """ expected = { - 'name': '127.0.1.1', - 'changes': {'127.0.1.1': {'old': 'foo.bar', 'new': 'foo.bar foo'}}, - 'result': True, - 'comment': 'successfully changed 127.0.1.1' - + ' from "foo.bar" to "foo.bar foo"'} - mock = MagicMock(return_value=['foo.bar']) - with patch.dict(host.__salt__, {'hosts.get_alias': mock}): + "name": "127.0.1.1", + "changes": {"127.0.1.1": {"old": "foo.bar", "new": "foo.bar foo"}}, + "result": True, + "comment": "successfully changed 127.0.1.1" + + ' from "foo.bar" to "foo.bar foo"', + } + mock = MagicMock(return_value=["foo.bar"]) + with patch.dict(host.__salt__, {"hosts.get_alias": mock}): mock = MagicMock(return_value=True) - with patch.dict(host.__salt__, {'hosts.set_host': mock}): - with patch.dict(host.__opts__, {'test': False}): + with patch.dict(host.__salt__, {"hosts.set_host": mock}): + with patch.dict(host.__opts__, {"test": False}): self.assertDictEqual( - expected, - host.only("127.0.1.1", ['foo.bar', 'foo'])) + expected, host.only("127.0.1.1", ["foo.bar", "foo"]) + ) diff --git a/tests/unit/states/test_http.py b/tests/unit/states/test_http.py index 6ee22300886..8bf5d1291f5 100644 --- a/tests/unit/states/test_http.py +++ b/tests/unit/states/test_http.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,131 +10,154 @@ import salt.states.http as http # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class HttpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the HTTP state - ''' + """ + def setup_loader_modules(self): return {http: {}} def test_query(self): - ''' + """ Test to perform an HTTP query and statefully return the result - ''' - ret = [{'changes': {}, - 'comment': ' Either match text (match) or a ' - 'status code (status) is required.', 'data': {}, - 'name': 'salt', 'result': False}, - {'changes': {}, 'comment': ' (TEST MODE)', 'data': True, 'name': 'salt', - 'result': None}] + """ + ret = [ + { + "changes": {}, + "comment": " Either match text (match) or a " + "status code (status) is required.", + "data": {}, + "name": "salt", + "result": False, + }, + { + "changes": {}, + "comment": " (TEST MODE)", + "data": True, + "name": "salt", + "result": None, + }, + ] self.assertDictEqual(http.query("salt"), ret[0]) - with patch.dict(http.__opts__, {'test': True}): + with patch.dict(http.__opts__, {"test": True}): mock = MagicMock(return_value=True) - with patch.dict(http.__salt__, {'http.query': mock}): - self.assertDictEqual(http.query("salt", "Dude", "stack"), - ret[1]) + with patch.dict(http.__salt__, {"http.query": mock}): + self.assertDictEqual(http.query("salt", "Dude", "stack"), ret[1]) def test_query_pcre_statustype(self): - ''' + """ Test to perform an HTTP query with a regex used to match the status code and statefully return the result - ''' + """ testurl = "salturl" - http_result = { - "text": "This page returned a 201 status code", - "status": "201" - } - state_return = {'changes': {}, - 'comment': 'Match text "This page returned" was found. Status pattern "200|201" was found.', - 'data': {'status': '201', 'text': 'This page returned a 201 status code'}, - 'name': testurl, - 'result': True} + http_result = {"text": "This page returned a 201 status code", "status": "201"} + state_return = { + "changes": {}, + "comment": 'Match text "This page returned" was found. Status pattern "200|201" was found.', + "data": {"status": "201", "text": "This page returned a 201 status code"}, + "name": testurl, + "result": True, + } - with patch.dict(http.__opts__, {'test': False}): + with patch.dict(http.__opts__, {"test": False}): mock = MagicMock(return_value=http_result) - with patch.dict(http.__salt__, {'http.query': mock}): - self.assertDictEqual(http.query(testurl, - match="This page returned", - status="200|201", - status_type='pcre' - ), state_return) + with patch.dict(http.__salt__, {"http.query": mock}): + self.assertDictEqual( + http.query( + testurl, + match="This page returned", + status="200|201", + status_type="pcre", + ), + state_return, + ) def test_query_stringstatustype(self): - ''' + """ Test to perform an HTTP query with a string status code and statefully return the result - ''' + """ testurl = "salturl" - http_result = { - "text": "This page returned a 201 status code", - "status": "201" - } - state_return = {'changes': {}, - 'comment': 'Match text "This page returned" was found. Status 201 was found.', - 'data': {'status': '201', 'text': 'This page returned a 201 status code'}, - 'name': testurl, - 'result': True} + http_result = {"text": "This page returned a 201 status code", "status": "201"} + state_return = { + "changes": {}, + "comment": 'Match text "This page returned" was found. Status 201 was found.', + "data": {"status": "201", "text": "This page returned a 201 status code"}, + "name": testurl, + "result": True, + } - with patch.dict(http.__opts__, {'test': False}): + with patch.dict(http.__opts__, {"test": False}): mock = MagicMock(return_value=http_result) - with patch.dict(http.__salt__, {'http.query': mock}): - self.assertDictEqual(http.query(testurl, - match="This page returned", - status="201", - status_type='string' - ), state_return) + with patch.dict(http.__salt__, {"http.query": mock}): + self.assertDictEqual( + http.query( + testurl, + match="This page returned", + status="201", + status_type="string", + ), + state_return, + ) def test_query_liststatustype(self): - ''' + """ Test to perform an HTTP query with a list of status codes and statefully return the result - ''' + """ testurl = "salturl" - http_result = { - "text": "This page returned a 201 status code", - "status": "201" - } - state_return = {'changes': {}, - 'comment': 'Match text "This page returned" was found. Status 201 was found.', - 'data': {'status': '201', 'text': 'This page returned a 201 status code'}, - 'name': testurl, - 'result': True} + http_result = {"text": "This page returned a 201 status code", "status": "201"} + state_return = { + "changes": {}, + "comment": 'Match text "This page returned" was found. Status 201 was found.', + "data": {"status": "201", "text": "This page returned a 201 status code"}, + "name": testurl, + "result": True, + } - with patch.dict(http.__opts__, {'test': False}): + with patch.dict(http.__opts__, {"test": False}): mock = MagicMock(return_value=http_result) - with patch.dict(http.__salt__, {'http.query': mock}): - self.assertDictEqual(http.query(testurl, - match="This page returned", - status=["200", "201"], - status_type='list' - ), state_return) + with patch.dict(http.__salt__, {"http.query": mock}): + self.assertDictEqual( + http.query( + testurl, + match="This page returned", + status=["200", "201"], + status_type="list", + ), + state_return, + ) def test_wait_for_with_interval(self): - ''' + """ Test for wait_for_successful_query waits for request_interval - ''' + """ - query_mock = MagicMock(side_effect=[{'error': 'error'}, {'result': True}]) + query_mock = MagicMock(side_effect=[{"error": "error"}, {"result": True}]) - with patch.object(http, 'query', query_mock): - with patch('time.sleep', MagicMock()) as sleep_mock: - self.assertEqual(http.wait_for_successful_query('url', request_interval=1, status=200), - {'result': True}) + with patch.object(http, "query", query_mock): + with patch("time.sleep", MagicMock()) as sleep_mock: + self.assertEqual( + http.wait_for_successful_query( + "url", request_interval=1, status=200 + ), + {"result": True}, + ) sleep_mock.assert_called_once_with(1) def test_wait_for_without_interval(self): - ''' + """ Test for wait_for_successful_query waits for request_interval - ''' + """ - query_mock = MagicMock(side_effect=[{'error': 'error'}, {'result': True}]) + query_mock = MagicMock(side_effect=[{"error": "error"}, {"result": True}]) - with patch.object(http, 'query', query_mock): - with patch('time.sleep', MagicMock()) as sleep_mock: - self.assertEqual(http.wait_for_successful_query('url', status=200), {'result': True}) + with patch.object(http, "query", query_mock): + with patch("time.sleep", MagicMock()) as sleep_mock: + self.assertEqual( + http.wait_for_successful_query("url", status=200), {"result": True} + ) sleep_mock.assert_not_called() diff --git a/tests/unit/states/test_incron.py b/tests/unit/states/test_incron.py index d6dcba00c80..44af1077c9e 100644 --- a/tests/unit/states/test_incron.py +++ b/tests/unit/states/test_incron.py @@ -1,110 +1,107 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.incron as incron +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class IncronTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.incron - ''' + """ + def setup_loader_modules(self): return {incron: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to verifies that the specified incron job is present for the specified user. - ''' - name = 'salt' - path = '/home/user' - mask = 'IN_MODIFY' + """ + name = "salt" + path = "/home/user" + mask = "IN_MODIFY" cmd = 'echo "$$ $@"' - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - comt4 = ('Incron {0} for user root failed to commit with error \nabsent' - .format(name)) - mock_dict = MagicMock(return_value={'crons': [{'path': path, 'cmd': cmd, - 'mask': mask}]}) - mock = MagicMock(side_effect=['present', 'new', 'updated', 'absent']) - with patch.dict(incron.__salt__, {'incron.list_tab': mock_dict, - 'incron.set_job': mock}): - with patch.dict(incron.__opts__, {'test': True}): - comt = ('Incron {0} is set to be added'.format(name)) - ret.update({'comment': comt}) + comt4 = "Incron {0} for user root failed to commit with error \nabsent".format( + name + ) + mock_dict = MagicMock( + return_value={"crons": [{"path": path, "cmd": cmd, "mask": mask}]} + ) + mock = MagicMock(side_effect=["present", "new", "updated", "absent"]) + with patch.dict( + incron.__salt__, {"incron.list_tab": mock_dict, "incron.set_job": mock} + ): + with patch.dict(incron.__opts__, {"test": True}): + comt = "Incron {0} is set to be added".format(name) + ret.update({"comment": comt}) self.assertDictEqual(incron.present(name, path, mask, cmd), ret) - with patch.dict(incron.__opts__, {'test': False}): - comt = ('Incron {0} already present'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict(incron.__opts__, {"test": False}): + comt = "Incron {0} already present".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(incron.present(name, path, mask, cmd), ret) - comt = ('Incron {0} added to root\'s incrontab'.format(name)) - ret.update({'comment': comt, 'changes': {'root': 'salt'}}) + comt = "Incron {0} added to root's incrontab".format(name) + ret.update({"comment": comt, "changes": {"root": "salt"}}) self.assertDictEqual(incron.present(name, path, mask, cmd), ret) - comt = ('Incron {0} updated'.format(name)) - ret.update({'comment': comt}) + comt = "Incron {0} updated".format(name) + ret.update({"comment": comt}) self.assertDictEqual(incron.present(name, path, mask, cmd), ret) - ret.update({'comment': comt4, 'result': False, 'changes': {}}) + ret.update({"comment": comt4, "result": False, "changes": {}}) self.assertDictEqual(incron.present(name, path, mask, cmd), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to verifies that the specified incron job is absent for the specified user. - ''' - name = 'salt' - path = '/home/user' - mask = 'IN_MODIFY' + """ + name = "salt" + path = "/home/user" + mask = "IN_MODIFY" cmd = 'echo "$$ $@"' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - comt4 = (("Incron {0} for user root failed to commit with error new" - .format(name))) - mock_dict = MagicMock(return_value={'crons': [{'path': path, 'cmd': cmd, - 'mask': mask}]}) - mock = MagicMock(side_effect=['absent', 'removed', 'new']) - with patch.dict(incron.__salt__, {'incron.list_tab': mock_dict, - 'incron.rm_job': mock}): - with patch.dict(incron.__opts__, {'test': True}): - comt = ('Incron {0} is absent'.format(name)) - ret.update({'comment': comt}) + comt4 = "Incron {0} for user root failed to commit with error new".format(name) + mock_dict = MagicMock( + return_value={"crons": [{"path": path, "cmd": cmd, "mask": mask}]} + ) + mock = MagicMock(side_effect=["absent", "removed", "new"]) + with patch.dict( + incron.__salt__, {"incron.list_tab": mock_dict, "incron.rm_job": mock} + ): + with patch.dict(incron.__opts__, {"test": True}): + comt = "Incron {0} is absent".format(name) + ret.update({"comment": comt}) self.assertDictEqual(incron.absent(name, path, mask, cmd), ret) - with patch.dict(incron.__opts__, {'test': False}): - comt = ("Incron {0} already absent".format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict(incron.__opts__, {"test": False}): + comt = "Incron {0} already absent".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(incron.absent(name, path, mask, cmd), ret) - comt = (("Incron {0} removed from root's crontab".format(name))) - ret.update({'comment': comt, 'changes': {'root': 'salt'}}) + comt = "Incron {0} removed from root's crontab".format(name) + ret.update({"comment": comt, "changes": {"root": "salt"}}) self.assertDictEqual(incron.absent(name, path, mask, cmd), ret) - ret.update({'comment': comt4, 'result': False, 'changes': {}}) + ret.update({"comment": comt4, "result": False, "changes": {}}) self.assertDictEqual(incron.absent(name, path, mask, cmd), ret) diff --git a/tests/unit/states/test_influxdb08_database.py b/tests/unit/states/test_influxdb08_database.py index e2f8fa168ba..4338769042f 100644 --- a/tests/unit/states/test_influxdb08_database.py +++ b/tests/unit/states/test_influxdb08_database.py @@ -1,102 +1,95 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.influxdb08_database as influxdb08_database +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class InfluxdbDatabaseTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.influxdb08_database - ''' + """ + def setup_loader_modules(self): return {influxdb08_database: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named database is present. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} mock = MagicMock(side_effect=[False, False, False, True]) mock_t = MagicMock(side_effect=[True, False]) - with patch.dict(influxdb08_database.__salt__, - {'influxdb08.db_exists': mock, - 'influxdb08.db_create': mock_t}): - with patch.dict(influxdb08_database.__opts__, {'test': True}): - comt = ('Database {0} is absent and needs to be created' - .format(name)) - ret.update({'comment': comt}) + with patch.dict( + influxdb08_database.__salt__, + {"influxdb08.db_exists": mock, "influxdb08.db_create": mock_t}, + ): + with patch.dict(influxdb08_database.__opts__, {"test": True}): + comt = "Database {0} is absent and needs to be created".format(name) + ret.update({"comment": comt}) self.assertDictEqual(influxdb08_database.present(name), ret) - with patch.dict(influxdb08_database.__opts__, {'test': False}): - comt = ('Database {0} has been created'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'salt': 'Present'}}) + with patch.dict(influxdb08_database.__opts__, {"test": False}): + comt = "Database {0} has been created".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"salt": "Present"}} + ) self.assertDictEqual(influxdb08_database.present(name), ret) - comt = ('Failed to create database {0}'.format(name)) - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "Failed to create database {0}".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(influxdb08_database.present(name), ret) - comt = ('Database {0} is already present, so cannot be created' - .format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "Database {0} is already present, so cannot be created".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(influxdb08_database.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named database is absent. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} mock = MagicMock(side_effect=[True, True, True, False]) mock_t = MagicMock(side_effect=[True, False]) - with patch.dict(influxdb08_database.__salt__, - {'influxdb08.db_exists': mock, - 'influxdb08.db_remove': mock_t}): - with patch.dict(influxdb08_database.__opts__, {'test': True}): - comt = ('Database {0} is present and needs to be removed' - .format(name)) - ret.update({'comment': comt}) + with patch.dict( + influxdb08_database.__salt__, + {"influxdb08.db_exists": mock, "influxdb08.db_remove": mock_t}, + ): + with patch.dict(influxdb08_database.__opts__, {"test": True}): + comt = "Database {0} is present and needs to be removed".format(name) + ret.update({"comment": comt}) self.assertDictEqual(influxdb08_database.absent(name), ret) - with patch.dict(influxdb08_database.__opts__, {'test': False}): - comt = ('Database {0} has been removed'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'salt': 'Absent'}}) + with patch.dict(influxdb08_database.__opts__, {"test": False}): + comt = "Database {0} has been removed".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"salt": "Absent"}} + ) self.assertDictEqual(influxdb08_database.absent(name), ret) - comt = ('Failed to remove database {0}'.format(name)) - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "Failed to remove database {0}".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(influxdb08_database.absent(name), ret) - comt = ('Database {0} is not present, so it cannot be removed' - .format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "Database {0} is not present, so it cannot be removed".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(influxdb08_database.absent(name), ret) diff --git a/tests/unit/states/test_influxdb08_user.py b/tests/unit/states/test_influxdb08_user.py index 930f037ea18..d707d5b1eaf 100644 --- a/tests/unit/states/test_influxdb08_user.py +++ b/tests/unit/states/test_influxdb08_user.py @@ -1,109 +1,107 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.influxdb08_user as influxdb08_user +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class InfluxdbUserTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.influxdb08_user - ''' + """ + def setup_loader_modules(self): return {influxdb08_user: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the cluster admin or database user is present. - ''' - name = 'salt' - passwd = 'salt' + """ + name = "salt" + passwd = "salt" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock = MagicMock(side_effect=[False, False, False, True]) mock_t = MagicMock(side_effect=[True, False]) mock_f = MagicMock(return_value=False) - with patch.dict(influxdb08_user.__salt__, - {'influxdb08.db_exists': mock_f, - 'influxdb08.user_exists': mock, - 'influxdb08.user_create': mock_t}): - comt = ('Database mydb does not exist') - ret.update({'comment': comt}) - self.assertDictEqual(influxdb08_user.present(name, passwd, - database='mydb'), ret) + with patch.dict( + influxdb08_user.__salt__, + { + "influxdb08.db_exists": mock_f, + "influxdb08.user_exists": mock, + "influxdb08.user_create": mock_t, + }, + ): + comt = "Database mydb does not exist" + ret.update({"comment": comt}) + self.assertDictEqual( + influxdb08_user.present(name, passwd, database="mydb"), ret + ) - with patch.dict(influxdb08_user.__opts__, {'test': True}): - comt = ('User {0} is not present and needs to be created' - .format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(influxdb08_user.__opts__, {"test": True}): + comt = "User {0} is not present and needs to be created".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(influxdb08_user.present(name, passwd), ret) - with patch.dict(influxdb08_user.__opts__, {'test': False}): - comt = ('User {0} has been created'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'salt': 'Present'}}) + with patch.dict(influxdb08_user.__opts__, {"test": False}): + comt = "User {0} has been created".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"salt": "Present"}} + ) self.assertDictEqual(influxdb08_user.present(name, passwd), ret) - comt = ('Failed to create user {0}'.format(name)) - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "Failed to create user {0}".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(influxdb08_user.present(name, passwd), ret) - comt = ('User {0} is already present'.format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "User {0} is already present".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(influxdb08_user.present(name, passwd), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named cluster admin or database user is absent. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} mock = MagicMock(side_effect=[True, True, True, False]) mock_t = MagicMock(side_effect=[True, False]) - with patch.dict(influxdb08_user.__salt__, - {'influxdb08.user_exists': mock, - 'influxdb08.user_remove': mock_t}): - with patch.dict(influxdb08_user.__opts__, {'test': True}): - comt = ('User {0} is present and needs to be removed' - .format(name)) - ret.update({'comment': comt}) + with patch.dict( + influxdb08_user.__salt__, + {"influxdb08.user_exists": mock, "influxdb08.user_remove": mock_t}, + ): + with patch.dict(influxdb08_user.__opts__, {"test": True}): + comt = "User {0} is present and needs to be removed".format(name) + ret.update({"comment": comt}) self.assertDictEqual(influxdb08_user.absent(name), ret) - with patch.dict(influxdb08_user.__opts__, {'test': False}): - comt = ('User {0} has been removed'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'salt': 'Absent'}}) + with patch.dict(influxdb08_user.__opts__, {"test": False}): + comt = "User {0} has been removed".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"salt": "Absent"}} + ) self.assertDictEqual(influxdb08_user.absent(name), ret) - comt = ('Failed to remove user {0}'.format(name)) - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "Failed to remove user {0}".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(influxdb08_user.absent(name), ret) - comt = ('User {0} is not present, so it cannot be removed' - .format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "User {0} is not present, so it cannot be removed".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(influxdb08_user.absent(name), ret) diff --git a/tests/unit/states/test_ini_manage.py b/tests/unit/states/test_ini_manage.py index e0707f871b4..ed335087c7a 100644 --- a/tests/unit/states/test_ini_manage.py +++ b/tests/unit/states/test_ini_manage.py @@ -1,145 +1,167 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.ini_manage as ini_manage +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + # pylint: disable=no-member class IniManageTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ini_manage - ''' + """ + def setup_loader_modules(self): return {ini_manage: {}} # 'options_present' function tests: 1 def test_options_present(self): - ''' + """ Test to verify options present in file. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - with patch.dict(ini_manage.__opts__, {'test': True}): - comt = '' - ret.update({'comment': comt, 'result': True}) + with patch.dict(ini_manage.__opts__, {"test": True}): + comt = "" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ini_manage.options_present(name), ret) - changes = {'first': 'who is on', - 'second': 'what is on', - 'third': "I don't know"} - with patch.dict(ini_manage.__salt__, {'ini.set_option': MagicMock(return_value=changes)}): - with patch.dict(ini_manage.__opts__, {'test': False}): - comt = ('Changes take effect') - ret.update({'comment': comt, 'result': True, 'changes': changes}) + changes = { + "first": "who is on", + "second": "what is on", + "third": "I don't know", + } + with patch.dict( + ini_manage.__salt__, {"ini.set_option": MagicMock(return_value=changes)} + ): + with patch.dict(ini_manage.__opts__, {"test": False}): + comt = "Changes take effect" + ret.update({"comment": comt, "result": True, "changes": changes}) self.assertDictEqual(ini_manage.options_present(name), ret) - original = {'mysection': {'first': 'who is on', - 'second': 'what is on', - 'third': "I don't know"}} - desired = {'mysection': {'first': 'who is on', - 'second': 'what is on'}} - changes = {'mysection': {'first': 'who is on', - 'second': 'what is on', - 'third': {'after': None, 'before': "I don't know"}}} - with patch.dict(ini_manage.__salt__, {'ini.get_ini': MagicMock(return_value=original)}): - with patch.dict(ini_manage.__salt__, {'ini.remove_option': MagicMock(return_value='third')}): - with patch.dict(ini_manage.__salt__, {'ini.get_option': MagicMock(return_value="I don't know")}): - with patch.dict(ini_manage.__salt__, {'ini.set_option': MagicMock(return_value=desired)}): - with patch.dict(ini_manage.__opts__, {'test': False}): - comt = ('Changes take effect') - ret.update({'comment': comt, 'result': True, 'changes': changes}) - self.assertDictEqual(ini_manage.options_present(name, desired, strict=True), ret) + original = { + "mysection": { + "first": "who is on", + "second": "what is on", + "third": "I don't know", + } + } + desired = {"mysection": {"first": "who is on", "second": "what is on"}} + changes = { + "mysection": { + "first": "who is on", + "second": "what is on", + "third": {"after": None, "before": "I don't know"}, + } + } + with patch.dict( + ini_manage.__salt__, {"ini.get_ini": MagicMock(return_value=original)} + ): + with patch.dict( + ini_manage.__salt__, + {"ini.remove_option": MagicMock(return_value="third")}, + ): + with patch.dict( + ini_manage.__salt__, + {"ini.get_option": MagicMock(return_value="I don't know")}, + ): + with patch.dict( + ini_manage.__salt__, + {"ini.set_option": MagicMock(return_value=desired)}, + ): + with patch.dict(ini_manage.__opts__, {"test": False}): + comt = "Changes take effect" + ret.update( + {"comment": comt, "result": True, "changes": changes} + ) + self.assertDictEqual( + ini_manage.options_present(name, desired, strict=True), + ret, + ) # 'options_absent' function tests: 1 def test_options_absent(self): - ''' + """ Test to verify options absent in file. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - with patch.dict(ini_manage.__opts__, {'test': True}): - comt = 'No changes detected.' - ret.update({'comment': comt, 'result': True}) + with patch.dict(ini_manage.__opts__, {"test": True}): + comt = "No changes detected." + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ini_manage.options_absent(name), ret) - with patch.dict(ini_manage.__opts__, {'test': False}): - comt = ('No anomaly detected') - ret.update({'comment': comt, 'result': True}) + with patch.dict(ini_manage.__opts__, {"test": False}): + comt = "No anomaly detected" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ini_manage.options_absent(name), ret) # 'sections_present' function tests: 1 def test_sections_present(self): - ''' + """ Test to verify sections present in file. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - with patch.dict(ini_manage.__opts__, {'test': True}): - with patch.dict(ini_manage.__salt__, {'ini.get_ini': MagicMock(return_value=None)}): - comt = 'No changes detected.' - ret.update({'comment': comt, 'result': True}) + with patch.dict(ini_manage.__opts__, {"test": True}): + with patch.dict( + ini_manage.__salt__, {"ini.get_ini": MagicMock(return_value=None)} + ): + comt = "No changes detected." + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ini_manage.sections_present(name), ret) - changes = {'first': 'who is on', - 'second': 'what is on', - 'third': "I don't know"} - with patch.dict(ini_manage.__salt__, {'ini.set_option': MagicMock(return_value=changes)}): - with patch.dict(ini_manage.__opts__, {'test': False}): - comt = ('Changes take effect') - ret.update({'comment': comt, 'result': True, 'changes': changes}) + changes = { + "first": "who is on", + "second": "what is on", + "third": "I don't know", + } + with patch.dict( + ini_manage.__salt__, {"ini.set_option": MagicMock(return_value=changes)} + ): + with patch.dict(ini_manage.__opts__, {"test": False}): + comt = "Changes take effect" + ret.update({"comment": comt, "result": True, "changes": changes}) self.assertDictEqual(ini_manage.sections_present(name), ret) # 'sections_absent' function tests: 1 def test_sections_absent(self): - ''' + """ Test to verify sections absent in file. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - with patch.dict(ini_manage.__opts__, {'test': True}): - with patch.dict(ini_manage.__salt__, {'ini.get_ini': MagicMock(return_value=None)}): - comt = 'No changes detected.' - ret.update({'comment': comt, 'result': True}) + with patch.dict(ini_manage.__opts__, {"test": True}): + with patch.dict( + ini_manage.__salt__, {"ini.get_ini": MagicMock(return_value=None)} + ): + comt = "No changes detected." + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ini_manage.sections_absent(name), ret) - with patch.dict(ini_manage.__opts__, {'test': False}): - comt = ('No anomaly detected') - ret.update({'comment': comt, 'result': True}) + with patch.dict(ini_manage.__opts__, {"test": False}): + comt = "No anomaly detected" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ini_manage.sections_absent(name), ret) diff --git a/tests/unit/states/test_ipmi.py b/tests/unit/states/test_ipmi.py index f7a929b32e7..0f65aaf9542 100644 --- a/tests/unit/states/test_ipmi.py +++ b/tests/unit/states/test_ipmi.py @@ -1,154 +1,182 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.ipmi as ipmi +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class IpmiTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ipmi - ''' + """ + def setup_loader_modules(self): return {ipmi: {}} # 'boot_device' function tests: 1 def test_boot_device(self): - ''' + """ Test to request power state change. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(return_value=name) - with patch.dict(ipmi.__salt__, {'ipmi.get_bootdev': mock, - 'ipmi.set_bootdev': mock}): - comt = ('system already in this state') - ret.update({'comment': comt}) + with patch.dict( + ipmi.__salt__, {"ipmi.get_bootdev": mock, "ipmi.set_bootdev": mock} + ): + comt = "system already in this state" + ret.update({"comment": comt}) self.assertDictEqual(ipmi.boot_device(name), ret) - with patch.dict(ipmi.__opts__, {'test': False}): - comt = ('changed boot device') - ret.update({'name': 'default', 'comment': comt, 'result': True, - 'changes': {'new': 'default', 'old': 'salt'}}) + with patch.dict(ipmi.__opts__, {"test": False}): + comt = "changed boot device" + ret.update( + { + "name": "default", + "comment": comt, + "result": True, + "changes": {"new": "default", "old": "salt"}, + } + ) self.assertDictEqual(ipmi.boot_device(), ret) - with patch.dict(ipmi.__opts__, {'test': True}): - comt = ('would change boot device') - ret.update({'comment': comt, 'result': None}) + with patch.dict(ipmi.__opts__, {"test": True}): + comt = "would change boot device" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(ipmi.boot_device(), ret) # 'power' function tests: 1 def test_power(self): - ''' + """ Test to request power state change - ''' - ret = {'name': 'power_on', - 'result': True, - 'comment': '', - 'changes': {}} + """ + ret = {"name": "power_on", "result": True, "comment": "", "changes": {}} - mock = MagicMock(return_value='on') - with patch.dict(ipmi.__salt__, {'ipmi.get_power': mock, - 'ipmi.set_power': mock}): - comt = ('system already in this state') - ret.update({'comment': comt}) + mock = MagicMock(return_value="on") + with patch.dict( + ipmi.__salt__, {"ipmi.get_power": mock, "ipmi.set_power": mock} + ): + comt = "system already in this state" + ret.update({"comment": comt}) self.assertDictEqual(ipmi.power(), ret) - with patch.dict(ipmi.__opts__, {'test': False}): - comt = ('changed system power') - ret.update({'name': 'off', 'comment': comt, 'result': True, - 'changes': {'new': 'off', 'old': 'on'}}) - self.assertDictEqual(ipmi.power('off'), ret) + with patch.dict(ipmi.__opts__, {"test": False}): + comt = "changed system power" + ret.update( + { + "name": "off", + "comment": comt, + "result": True, + "changes": {"new": "off", "old": "on"}, + } + ) + self.assertDictEqual(ipmi.power("off"), ret) - with patch.dict(ipmi.__opts__, {'test': True}): - comt = ('would power: off system') - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(ipmi.power('off'), ret) + with patch.dict(ipmi.__opts__, {"test": True}): + comt = "would power: off system" + ret.update({"comment": comt, "result": None}) + self.assertDictEqual(ipmi.power("off"), ret) # 'user_present' function tests: 1 def test_user_present(self): - ''' + """ Test to ensure IPMI user and user privileges. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock_ret = {'access': {'callback': False, 'link_auth': True, - 'ipmi_msg': True, - 'privilege_level': 'administrator'}} + mock_ret = { + "access": { + "callback": False, + "link_auth": True, + "ipmi_msg": True, + "privilege_level": "administrator", + } + } mock = MagicMock(return_value=mock_ret) mock_bool = MagicMock(side_effect=[True, False, False, False]) - with patch.dict(ipmi.__salt__, {'ipmi.get_user': mock, - 'ipmi.set_user_password': mock_bool, - 'ipmi.ensure_user': mock_bool}): - comt = ('user already present') - ret.update({'comment': comt}) - self.assertDictEqual(ipmi.user_present(name, 5, 'salt@123'), ret) + with patch.dict( + ipmi.__salt__, + { + "ipmi.get_user": mock, + "ipmi.set_user_password": mock_bool, + "ipmi.ensure_user": mock_bool, + }, + ): + comt = "user already present" + ret.update({"comment": comt}) + self.assertDictEqual(ipmi.user_present(name, 5, "salt@123"), ret) - with patch.dict(ipmi.__opts__, {'test': True}): - comt = ('would (re)create user') - ret.update({'comment': comt, 'result': None, - 'changes': {'new': 'salt', 'old': mock_ret}}) - self.assertDictEqual(ipmi.user_present(name, 5, 'pw@123'), ret) + with patch.dict(ipmi.__opts__, {"test": True}): + comt = "would (re)create user" + ret.update( + { + "comment": comt, + "result": None, + "changes": {"new": "salt", "old": mock_ret}, + } + ) + self.assertDictEqual(ipmi.user_present(name, 5, "pw@123"), ret) - with patch.dict(ipmi.__opts__, {'test': False}): - comt = ('(re)created user') - ret.update({'comment': comt, 'result': True, - 'changes': {'new': mock_ret, 'old': mock_ret}}) - self.assertDictEqual(ipmi.user_present(name, 5, 'pw@123'), ret) + with patch.dict(ipmi.__opts__, {"test": False}): + comt = "(re)created user" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"new": mock_ret, "old": mock_ret}, + } + ) + self.assertDictEqual(ipmi.user_present(name, 5, "pw@123"), ret) # 'user_absent' function tests: 1 def test_user_absent(self): - ''' + """ Test to delete all user (uid) records having the matching name. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(side_effect=[[], [5], [5]]) mock_bool = MagicMock(return_value=True) - with patch.dict(ipmi.__salt__, {'ipmi.get_name_uids': mock, - 'ipmi.delete_user': mock_bool}): - comt = ('user already absent') - ret.update({'comment': comt}) + with patch.dict( + ipmi.__salt__, {"ipmi.get_name_uids": mock, "ipmi.delete_user": mock_bool} + ): + comt = "user already absent" + ret.update({"comment": comt}) self.assertDictEqual(ipmi.user_absent(name), ret) - with patch.dict(ipmi.__opts__, {'test': True}): - comt = ('would delete user(s)') - ret.update({'comment': comt, 'result': None, - 'changes': {'delete': [5]}}) + with patch.dict(ipmi.__opts__, {"test": True}): + comt = "would delete user(s)" + ret.update( + {"comment": comt, "result": None, "changes": {"delete": [5]}} + ) self.assertDictEqual(ipmi.user_absent(name), ret) - with patch.dict(ipmi.__opts__, {'test': False}): - comt = ('user(s) removed') - ret.update({'comment': comt, 'result': False, - 'changes': {'new': 'None', 'old': [5]}}) + with patch.dict(ipmi.__opts__, {"test": False}): + comt = "user(s) removed" + ret.update( + { + "comment": comt, + "result": False, + "changes": {"new": "None", "old": [5]}, + } + ) self.assertDictEqual(ipmi.user_absent(name), ret) diff --git a/tests/unit/states/test_ipset.py b/tests/unit/states/test_ipset.py index 5c33bc8f417..75154f6eeca 100644 --- a/tests/unit/states/test_ipset.py +++ b/tests/unit/states/test_ipset.py @@ -1,168 +1,224 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - call, - patch) - # Import Salt Libs import salt.states.ipset as ipset +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase + class IpsetSetPresentTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ipset.present - ''' + """ - fake_name = 'fake_ipset' - fake_set_type = {'bitmap': '192.168.0.3'} + fake_name = "fake_ipset" + fake_set_type = {"bitmap": "192.168.0.3"} def setup_loader_modules(self): return {ipset: {}} - def _runner(self, expected_ret, test=False, check_set=False, new_set=None, - new_set_assertion=True): + def _runner( + self, + expected_ret, + test=False, + check_set=False, + new_set=None, + new_set_assertion=True, + ): mock_check_set = MagicMock(return_value=check_set) - mock_new_set = MagicMock() if new_set is None else MagicMock(return_value=new_set) - with patch.dict(ipset.__salt__, {'ipset.check_set': mock_check_set, - 'ipset.new_set': mock_new_set}): - with patch.dict(ipset.__opts__, {'test': test}): + mock_new_set = ( + MagicMock() if new_set is None else MagicMock(return_value=new_set) + ) + with patch.dict( + ipset.__salt__, + {"ipset.check_set": mock_check_set, "ipset.new_set": mock_new_set}, + ): + with patch.dict(ipset.__opts__, {"test": test}): actual_ret = ipset.set_present(self.fake_name, self.fake_set_type) mock_check_set.assert_called_once_with(self.fake_name) if new_set_assertion: - mock_new_set.assert_called_once_with(self.fake_name, self.fake_set_type, 'ipv4') + mock_new_set.assert_called_once_with( + self.fake_name, self.fake_set_type, "ipv4" + ) else: self.assertTrue(mock_new_set.call_count == 0) self.assertDictEqual(actual_ret, expected_ret) def test_already_exists(self): - ''' + """ Test to verify the chain exists when it already exists. - ''' - ret = {'name': self.fake_name, - 'result': True, - 'comment': 'ipset set {0} already exists for ipv4'.format(self.fake_name), - 'changes': {}} + """ + ret = { + "name": self.fake_name, + "result": True, + "comment": "ipset set {0} already exists for ipv4".format(self.fake_name), + "changes": {}, + } self._runner(ret, check_set=True, new_set_assertion=False) def test_needs_update_test_mode(self): - ''' + """ Test to verify that detects need for update but doesn't apply when in test mode. - ''' + """ - ret = {'name': self.fake_name, - 'result': None, - 'comment': 'ipset set {0} would be added for ipv4'.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": None, + "comment": "ipset set {0} would be added for ipv4".format(self.fake_name), + "changes": {}, + } self._runner(ret, test=True, new_set_assertion=False) def test_creates_set(self): - ret = {'name': self.fake_name, - 'result': True, - 'comment': 'ipset set {0} created successfully for ipv4'.format(self.fake_name), - 'changes': {'locale': self.fake_name}} + ret = { + "name": self.fake_name, + "result": True, + "comment": "ipset set {0} created successfully for ipv4".format( + self.fake_name + ), + "changes": {"locale": self.fake_name}, + } self._runner(ret, new_set=True) def test_create_fails(self): - ret = {'name': self.fake_name, - 'result': False, - 'comment': 'Failed to create set {0} for ipv4: '.format(self.fake_name), - 'changes': {}} - self._runner(ret, new_set='') + ret = { + "name": self.fake_name, + "result": False, + "comment": "Failed to create set {0} for ipv4: ".format(self.fake_name), + "changes": {}, + } + self._runner(ret, new_set="") class IpsetSetAbsentTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ipset.present - ''' + """ - fake_name = 'fake_ipset' - fake_set_type = {'bitmap': '192.168.0.3'} + fake_name = "fake_ipset" + fake_set_type = {"bitmap": "192.168.0.3"} def setup_loader_modules(self): return {ipset: {}} - def _runner(self, expected_ret, test=False, check_set=True, delete_set='', - flush_assertion=False, delete_set_assertion=False): + def _runner( + self, + expected_ret, + test=False, + check_set=True, + delete_set="", + flush_assertion=False, + delete_set_assertion=False, + ): mock_check_set = MagicMock(return_value=check_set) mock_flush = MagicMock() - mock_delete_set = MagicMock() if delete_set is None else MagicMock(return_value=delete_set) - with patch.dict(ipset.__opts__, {'test': test}): - with patch.dict(ipset.__salt__, {'ipset.check_set': mock_check_set, - 'ipset.flush': mock_flush, - 'ipset.delete_set': mock_delete_set}): + mock_delete_set = ( + MagicMock() if delete_set is None else MagicMock(return_value=delete_set) + ) + with patch.dict(ipset.__opts__, {"test": test}): + with patch.dict( + ipset.__salt__, + { + "ipset.check_set": mock_check_set, + "ipset.flush": mock_flush, + "ipset.delete_set": mock_delete_set, + }, + ): actual_ret = ipset.set_absent(self.fake_name) - mock_check_set.assert_called_once_with(self.fake_name, 'ipv4') + mock_check_set.assert_called_once_with(self.fake_name, "ipv4") if flush_assertion: - mock_flush.assert_called_once_with(self.fake_name, 'ipv4') + mock_flush.assert_called_once_with(self.fake_name, "ipv4") else: self.assertTrue(mock_flush.call_count == 0) if delete_set_assertion: - mock_delete_set.assert_called_once_with(self.fake_name, 'ipv4') + mock_delete_set.assert_called_once_with(self.fake_name, "ipv4") else: self.assertTrue(mock_delete_set.call_count == 0) self.assertDictEqual(actual_ret, expected_ret) def test_already_absent(self): - ret = {'name': self.fake_name, - 'result': True, - 'comment': 'ipset set {0} for ipv4 is already absent'.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": True, + "comment": "ipset set {0} for ipv4 is already absent".format( + self.fake_name + ), + "changes": {}, + } self._runner(ret, check_set=False, delete_set=None) def test_remove_test_mode(self): - ret = {'name': self.fake_name, - 'result': None, - 'comment': 'ipset set {0} for ipv4 would be removed'.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": None, + "comment": "ipset set {0} for ipv4 would be removed".format(self.fake_name), + "changes": {}, + } self._runner(ret, test=True, delete_set=None) def test_remove_fails(self): - ret = {'name': self.fake_name, - 'result': False, - 'comment': 'Failed to delete set {0} for ipv4: '.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": False, + "comment": "Failed to delete set {0} for ipv4: ".format(self.fake_name), + "changes": {}, + } self._runner(ret, flush_assertion=True, delete_set_assertion=True) def test_remove_success(self): - ret = {'name': self.fake_name, - 'result': True, - 'comment': 'ipset set {0} deleted successfully for family ipv4'.format(self.fake_name), - 'changes': {'locale': 'fake_ipset'}} - self._runner(ret, delete_set=True, flush_assertion=True, delete_set_assertion=True) + ret = { + "name": self.fake_name, + "result": True, + "comment": "ipset set {0} deleted successfully for family ipv4".format( + self.fake_name + ), + "changes": {"locale": "fake_ipset"}, + } + self._runner( + ret, delete_set=True, flush_assertion=True, delete_set_assertion=True + ) class IpsetPresentTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ipset.present - ''' + """ - fake_name = 'fake_ipset' - fake_entries = ['192.168.0.3', '192.168.1.3'] + fake_name = "fake_ipset" + fake_entries = ["192.168.0.3", "192.168.1.3"] def setup_loader_modules(self): return {ipset: {}} - def _runner(self, expected_ret, test=False, check=False, add=False, - add_assertion=False): + def _runner( + self, expected_ret, test=False, check=False, add=False, add_assertion=False + ): mock_check = MagicMock(return_value=check) mock_add = MagicMock(return_value=add) - with patch.dict(ipset.__opts__, {'test': test}): - with patch.dict(ipset.__salt__, {'ipset.check': mock_check, - 'ipset.add': mock_add}): - actual_ret = ipset.present(self.fake_name, self.fake_entries, set_name=self.fake_name) + with patch.dict(ipset.__opts__, {"test": test}): + with patch.dict( + ipset.__salt__, {"ipset.check": mock_check, "ipset.add": mock_add} + ): + actual_ret = ipset.present( + self.fake_name, self.fake_entries, set_name=self.fake_name + ) - mock_check.assert_has_calls([call(self.fake_name, e, 'ipv4') for e in self.fake_entries], any_order=True) + mock_check.assert_has_calls( + [call(self.fake_name, e, "ipv4") for e in self.fake_entries], any_order=True + ) if add_assertion: - expected_calls = [call(self.fake_name, e, 'ipv4', set_name=self.fake_name) for e in self.fake_entries] + expected_calls = [ + call(self.fake_name, e, "ipv4", set_name=self.fake_name) + for e in self.fake_entries + ] if add is not True: # if the add fails, then it will only get called once. expected_calls = expected_calls[:1] @@ -172,72 +228,96 @@ class IpsetPresentTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual(actual_ret, expected_ret) def test_entries_already_present(self): - ret = {'name': self.fake_name, - 'result': True, - 'comment': 'entry for 192.168.0.3 already in set {0} for ipv4\n' - 'entry for 192.168.1.3 already in set {0} for ipv4\n' - ''.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": True, + "comment": "entry for 192.168.0.3 already in set {0} for ipv4\n" + "entry for 192.168.1.3 already in set {0} for ipv4\n" + "".format(self.fake_name), + "changes": {}, + } self._runner(ret, check=True) def test_in_test_mode(self): - ret = {'name': self.fake_name, - 'result': None, - 'comment': 'entry 192.168.0.3 would be added to set {0} for family ipv4\n' - 'entry 192.168.1.3 would be added to set {0} for family ipv4\n' - ''.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": None, + "comment": "entry 192.168.0.3 would be added to set {0} for family ipv4\n" + "entry 192.168.1.3 would be added to set {0} for family ipv4\n" + "".format(self.fake_name), + "changes": {}, + } self._runner(ret, test=True) def test_add_fails(self): - ret = {'name': self.fake_name, - 'result': False, - 'comment': 'Failed to add to entry 192.168.1.3 to set {0} for family ipv4.\n' - 'Error'.format(self.fake_name), - 'changes': {}} - self._runner(ret, add='Error', add_assertion=True) + ret = { + "name": self.fake_name, + "result": False, + "comment": "Failed to add to entry 192.168.1.3 to set {0} for family ipv4.\n" + "Error".format(self.fake_name), + "changes": {}, + } + self._runner(ret, add="Error", add_assertion=True) def test_success(self): - ret = {'name': self.fake_name, - 'result': True, - 'comment': 'entry 192.168.0.3 added to set {0} for family ipv4\n' - 'entry 192.168.1.3 added to set {0} for family ipv4\n' - ''.format(self.fake_name), - 'changes': {'locale': 'fake_ipset'}} - self._runner(ret, add='worked', add_assertion=True) + ret = { + "name": self.fake_name, + "result": True, + "comment": "entry 192.168.0.3 added to set {0} for family ipv4\n" + "entry 192.168.1.3 added to set {0} for family ipv4\n" + "".format(self.fake_name), + "changes": {"locale": "fake_ipset"}, + } + self._runner(ret, add="worked", add_assertion=True) def test_missing_entry(self): - ret = {'name': self.fake_name, - 'result': False, - 'comment': 'ipset entry must be specified', - 'changes': {}} + ret = { + "name": self.fake_name, + "result": False, + "comment": "ipset entry must be specified", + "changes": {}, + } mock = MagicMock(return_value=True) - with patch.dict(ipset.__salt__, {'ipset.check': mock}): + with patch.dict(ipset.__salt__, {"ipset.check": mock}): self.assertDictEqual(ipset.present(self.fake_name), ret) class IpsetAbsentTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ipset.present - ''' + """ - fake_name = 'fake_ipset' - fake_entries = ['192.168.0.3', '192.168.1.3'] + fake_name = "fake_ipset" + fake_entries = ["192.168.0.3", "192.168.1.3"] def setup_loader_modules(self): return {ipset: {}} - def _runner(self, expected_ret, test=False, check=False, delete=False, - delete_assertion=False): + def _runner( + self, + expected_ret, + test=False, + check=False, + delete=False, + delete_assertion=False, + ): mock_check = MagicMock(return_value=check) mock_delete = MagicMock(return_value=delete) - with patch.dict(ipset.__opts__, {'test': test}): - with patch.dict(ipset.__salt__, {'ipset.check': mock_check, - 'ipset.delete': mock_delete}): - actual_ret = ipset.absent(self.fake_name, self.fake_entries, set_name=self.fake_name) - mock_check.assert_has_calls([call(self.fake_name, e, 'ipv4') for e in self.fake_entries], any_order=True) + with patch.dict(ipset.__opts__, {"test": test}): + with patch.dict( + ipset.__salt__, {"ipset.check": mock_check, "ipset.delete": mock_delete} + ): + actual_ret = ipset.absent( + self.fake_name, self.fake_entries, set_name=self.fake_name + ) + mock_check.assert_has_calls( + [call(self.fake_name, e, "ipv4") for e in self.fake_entries], any_order=True + ) if delete_assertion: - expected_calls = [call(self.fake_name, e, 'ipv4', set_name=self.fake_name) for e in self.fake_entries] + expected_calls = [ + call(self.fake_name, e, "ipv4", set_name=self.fake_name) + for e in self.fake_entries + ] if delete is not True: expected_calls = expected_calls[:1] mock_delete.assert_has_calls(expected_calls, any_order=True) @@ -246,99 +326,126 @@ class IpsetAbsentTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual(actual_ret, expected_ret) def test_already_absent(self): - ret = {'name': self.fake_name, - 'result': True, - 'comment': 'ipset entry for 192.168.0.3 not present in set {0} for ipv4\n' - 'ipset entry for 192.168.1.3 not present in set {0} for ipv4\n' - ''.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": True, + "comment": "ipset entry for 192.168.0.3 not present in set {0} for ipv4\n" + "ipset entry for 192.168.1.3 not present in set {0} for ipv4\n" + "".format(self.fake_name), + "changes": {}, + } self._runner(ret) def test_in_test_mode(self): - ret = {'name': self.fake_name, - 'result': None, - 'comment': 'ipset entry 192.168.0.3 would be removed from set {0} for ipv4\n' - 'ipset entry 192.168.1.3 would be removed from set {0} for ipv4\n' - ''.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": None, + "comment": "ipset entry 192.168.0.3 would be removed from set {0} for ipv4\n" + "ipset entry 192.168.1.3 would be removed from set {0} for ipv4\n" + "".format(self.fake_name), + "changes": {}, + } self._runner(ret, test=True, check=True) def test_del_fails(self): - ret = {'name': self.fake_name, - 'result': False, - 'comment': 'Failed to delete ipset entry from set {0} for ipv4. Attempted entry was 192.168.1.3.\n' - 'Error\n'.format(self.fake_name), - 'changes': {}} - self._runner(ret, check=True, delete='Error', delete_assertion=True) + ret = { + "name": self.fake_name, + "result": False, + "comment": "Failed to delete ipset entry from set {0} for ipv4. Attempted entry was 192.168.1.3.\n" + "Error\n".format(self.fake_name), + "changes": {}, + } + self._runner(ret, check=True, delete="Error", delete_assertion=True) def test_success(self): - ret = {'name': self.fake_name, - 'result': True, - 'comment': 'ipset entry 192.168.0.3 removed from set {0} for ipv4\n' - 'ipset entry 192.168.1.3 removed from set {0} for ipv4\n' - ''.format(self.fake_name), - 'changes': {'locale': 'fake_ipset'}} - self._runner(ret, check=True, delete='worked', delete_assertion=True) + ret = { + "name": self.fake_name, + "result": True, + "comment": "ipset entry 192.168.0.3 removed from set {0} for ipv4\n" + "ipset entry 192.168.1.3 removed from set {0} for ipv4\n" + "".format(self.fake_name), + "changes": {"locale": "fake_ipset"}, + } + self._runner(ret, check=True, delete="worked", delete_assertion=True) def test_absent(self): - ret = {'name': self.fake_name, - 'result': False, - 'comment': 'ipset entry must be specified', - 'changes': {}} + ret = { + "name": self.fake_name, + "result": False, + "comment": "ipset entry must be specified", + "changes": {}, + } mock = MagicMock(return_value=True) - with patch.dict(ipset.__salt__, {'ipset.check': mock}): + with patch.dict(ipset.__salt__, {"ipset.check": mock}): self.assertDictEqual(ipset.absent(self.fake_name), ret) class IpsetFlushTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ipset.present - ''' + """ - fake_name = 'fake_ipset' + fake_name = "fake_ipset" def setup_loader_modules(self): return {ipset: {}} - def _runner(self, expected_ret, test=False, check_set=True, flush=True, - flush_assertion=True): + def _runner( + self, expected_ret, test=False, check_set=True, flush=True, flush_assertion=True + ): mock_check_set = MagicMock(return_value=check_set) mock_flush = MagicMock(return_value=flush) - with patch.dict(ipset.__opts__, {'test': test}): - with patch.dict(ipset.__salt__, {'ipset.check_set': mock_check_set, - 'ipset.flush': mock_flush}): + with patch.dict(ipset.__opts__, {"test": test}): + with patch.dict( + ipset.__salt__, + {"ipset.check_set": mock_check_set, "ipset.flush": mock_flush}, + ): actual_ret = ipset.flush(self.fake_name) mock_check_set.assert_called_once_with(self.fake_name) if flush_assertion: - mock_flush.assert_called_once_with(self.fake_name, 'ipv4') + mock_flush.assert_called_once_with(self.fake_name, "ipv4") else: self.assertTrue(mock_flush.call_count == 0) self.assertDictEqual(actual_ret, expected_ret) def test_no_set(self): - ret = {'name': self.fake_name, - 'result': False, - 'comment': 'ipset set {0} does not exist for ipv4'.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": False, + "comment": "ipset set {0} does not exist for ipv4".format(self.fake_name), + "changes": {}, + } self._runner(ret, check_set=False, flush_assertion=False) def test_in_test_mode(self): - ret = {'name': self.fake_name, - 'result': None, - 'comment': 'ipset entries in set {0} for ipv4 would be flushed'.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": None, + "comment": "ipset entries in set {0} for ipv4 would be flushed".format( + self.fake_name + ), + "changes": {}, + } self._runner(ret, test=True, flush_assertion=False) def test_flush_fails(self): - ret = {'name': self.fake_name, - 'result': False, - 'comment': 'Failed to flush ipset entries from set {0} for ipv4'.format(self.fake_name), - 'changes': {}} + ret = { + "name": self.fake_name, + "result": False, + "comment": "Failed to flush ipset entries from set {0} for ipv4".format( + self.fake_name + ), + "changes": {}, + } self._runner(ret, flush=False) def test_success(self): - ret = {'name': self.fake_name, - 'result': True, - 'comment': 'Flushed ipset entries from set {0} for ipv4'.format(self.fake_name), - 'changes': {'locale': 'fake_ipset'}} + ret = { + "name": self.fake_name, + "result": True, + "comment": "Flushed ipset entries from set {0} for ipv4".format( + self.fake_name + ), + "changes": {"locale": "fake_ipset"}, + } self._runner(ret) diff --git a/tests/unit/states/test_iptables.py b/tests/unit/states/test_iptables.py index 90afa1ea3ea..c49022c9622 100644 --- a/tests/unit/states/test_iptables.py +++ b/tests/unit/states/test_iptables.py @@ -1,372 +1,449 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.iptables as iptables +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class IptablesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the iptables state - ''' + """ + def setup_loader_modules(self): return {iptables: {}} def test_chain_present(self): - ''' + """ Test to verify the chain is exist. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': '' - } + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[True, False, False, False]) - with patch.dict(iptables.__salt__, {'iptables.check_chain': mock}): - ret.update({'comment': 'iptables salt chain is already' - ' exist in filter table for ipv4'}) + with patch.dict(iptables.__salt__, {"iptables.check_chain": mock}): + ret.update( + { + "comment": "iptables salt chain is already" + " exist in filter table for ipv4" + } + ) self.assertDictEqual(iptables.chain_present("salt"), ret) - with patch.dict(iptables.__opts__, {'test': True}): - ret.update({'comment': 'iptables salt chain in filter' - ' table needs to be set for ipv4', - 'result': None}) + with patch.dict(iptables.__opts__, {"test": True}): + ret.update( + { + "comment": "iptables salt chain in filter" + " table needs to be set for ipv4", + "result": None, + } + ) self.assertDictEqual(iptables.chain_present("salt"), ret) - with patch.dict(iptables.__opts__, {'test': False}): - mock = MagicMock(side_effect=[True, '']) - with patch.dict(iptables.__salt__, - {'iptables.new_chain': mock}): - ret.update({'result': True, - 'comment': 'iptables salt chain in filter' - ' table create success for ipv4', - 'changes': {'locale': 'salt'}}) - self.assertDictEqual(iptables.chain_present('salt'), ret) + with patch.dict(iptables.__opts__, {"test": False}): + mock = MagicMock(side_effect=[True, ""]) + with patch.dict(iptables.__salt__, {"iptables.new_chain": mock}): + ret.update( + { + "result": True, + "comment": "iptables salt chain in filter" + " table create success for ipv4", + "changes": {"locale": "salt"}, + } + ) + self.assertDictEqual(iptables.chain_present("salt"), ret) - ret.update({'changes': {}, 'result': False, - 'comment': 'Failed to create salt chain' - ' in filter table: for ipv4'}) - self.assertDictEqual(iptables.chain_present('salt'), ret) + ret.update( + { + "changes": {}, + "result": False, + "comment": "Failed to create salt chain" + " in filter table: for ipv4", + } + ) + self.assertDictEqual(iptables.chain_present("salt"), ret) def test_chain_absent(self): - ''' + """ Test to verify the chain is absent. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': '' - } + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[False, True, True, True]) - with patch.dict(iptables.__salt__, {'iptables.check_chain': mock}): - ret.update({'comment': 'iptables salt chain is already' - ' absent in filter table for ipv4'}) + with patch.dict(iptables.__salt__, {"iptables.check_chain": mock}): + ret.update( + { + "comment": "iptables salt chain is already" + " absent in filter table for ipv4" + } + ) self.assertDictEqual(iptables.chain_absent("salt"), ret) - with patch.dict(iptables.__opts__, {'test': True}): - ret.update({'comment': 'iptables salt chain in filter' - ' table needs to be removed ipv4', - 'result': None}) + with patch.dict(iptables.__opts__, {"test": True}): + ret.update( + { + "comment": "iptables salt chain in filter" + " table needs to be removed ipv4", + "result": None, + } + ) self.assertDictEqual(iptables.chain_absent("salt"), ret) - with patch.dict(iptables.__opts__, {'test': False}): - mock = MagicMock(side_effect=[False, 'a']) - with patch.dict(iptables.__salt__, {'iptables.flush': mock}): + with patch.dict(iptables.__opts__, {"test": False}): + mock = MagicMock(side_effect=[False, "a"]) + with patch.dict(iptables.__salt__, {"iptables.flush": mock}): mock = MagicMock(return_value=True) - with patch.dict(iptables.__salt__, - {'iptables.delete_chain': mock}): - ret.update({'changes': {'locale': 'salt'}, - 'comment': 'iptables salt chain in filter' - ' table delete success for ipv4', - 'result': True}) - self.assertDictEqual(iptables.chain_absent("salt"), - ret) + with patch.dict(iptables.__salt__, {"iptables.delete_chain": mock}): + ret.update( + { + "changes": {"locale": "salt"}, + "comment": "iptables salt chain in filter" + " table delete success for ipv4", + "result": True, + } + ) + self.assertDictEqual(iptables.chain_absent("salt"), ret) - ret.update({'changes': {}, 'result': False, - 'comment': 'Failed to flush salt chain' - ' in filter table: a for ipv4'}) + ret.update( + { + "changes": {}, + "result": False, + "comment": "Failed to flush salt chain" + " in filter table: a for ipv4", + } + ) self.assertDictEqual(iptables.chain_absent("salt"), ret) def test_append(self): - ''' + """ Test to append a rule to a chain - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': '' - } + """ + ret = {"name": "salt", "changes": {}, "result": None, "comment": ""} - self.assertDictEqual(iptables.append('salt', rules=[]), ret) + self.assertDictEqual(iptables.append("salt", rules=[]), ret) mock = MagicMock(return_value=[]) - with patch.object(iptables, '_STATE_INTERNAL_KEYWORDS', mock): - mock = MagicMock(return_value='a') - with patch.dict(iptables.__salt__, {'iptables.build_rule': mock}): + with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock): + mock = MagicMock(return_value="a") + with patch.dict(iptables.__salt__, {"iptables.build_rule": mock}): mock = MagicMock(side_effect=[True, False, False, False]) - with patch.dict(iptables.__salt__, {'iptables.check': mock}): - ret.update({'comment': 'iptables rule for salt' - ' already set (a) for ipv4', - 'result': True}) - self.assertDictEqual(iptables.append('salt', - table='', chain=''), - ret) + with patch.dict(iptables.__salt__, {"iptables.check": mock}): + ret.update( + { + "comment": "iptables rule for salt" + " already set (a) for ipv4", + "result": True, + } + ) + self.assertDictEqual( + iptables.append("salt", table="", chain=""), ret + ) - with patch.dict(iptables.__opts__, {'test': True}): - ret.update({'result': None, - 'comment': 'iptables rule for salt' - ' needs to be set (a) for ipv4'}) - self.assertDictEqual(iptables.append('salt', - table='', - chain=''), ret) + with patch.dict(iptables.__opts__, {"test": True}): + ret.update( + { + "result": None, + "comment": "iptables rule for salt" + " needs to be set (a) for ipv4", + } + ) + self.assertDictEqual( + iptables.append("salt", table="", chain=""), ret + ) - with patch.dict(iptables.__opts__, {'test': False}): + with patch.dict(iptables.__opts__, {"test": False}): mock = MagicMock(side_effect=[True, False]) - with patch.dict(iptables.__salt__, - {'iptables.append': mock}): - ret.update({'changes': {'locale': 'salt'}, - 'result': True, - 'comment': 'Set iptables rule' - ' for salt to: a for ipv4'}) - self.assertDictEqual(iptables.append('salt', - table='', - chain=''), - ret) + with patch.dict(iptables.__salt__, {"iptables.append": mock}): + ret.update( + { + "changes": {"locale": "salt"}, + "result": True, + "comment": "Set iptables rule" + " for salt to: a for ipv4", + } + ) + self.assertDictEqual( + iptables.append("salt", table="", chain=""), ret + ) - ret.update({'changes': {}, - 'result': False, - 'comment': 'Failed to set iptables' - ' rule for salt.\nAttempted rule was' - ' a for ipv4'}) - self.assertDictEqual(iptables.append('salt', - table='', - chain=''), - ret) + ret.update( + { + "changes": {}, + "result": False, + "comment": "Failed to set iptables" + " rule for salt.\nAttempted rule was" + " a for ipv4", + } + ) + self.assertDictEqual( + iptables.append("salt", table="", chain=""), ret + ) def test_insert(self): - ''' + """ Test to insert a rule into a chain - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": None, "comment": ""} - self.assertDictEqual(iptables.insert('salt', rules=[]), ret) + self.assertDictEqual(iptables.insert("salt", rules=[]), ret) mock = MagicMock(return_value=[]) - with patch.object(iptables, '_STATE_INTERNAL_KEYWORDS', mock): - mock = MagicMock(return_value='a') - with patch.dict(iptables.__salt__, {'iptables.build_rule': mock}): + with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock): + mock = MagicMock(return_value="a") + with patch.dict(iptables.__salt__, {"iptables.build_rule": mock}): mock = MagicMock(side_effect=[True, False, False, False]) - with patch.dict(iptables.__salt__, {'iptables.check': mock}): - ret.update({'comment': 'iptables rule for salt' - ' already set for ipv4 (a)', - 'result': True}) - self.assertDictEqual(iptables.insert('salt', - table='', chain=''), - ret) + with patch.dict(iptables.__salt__, {"iptables.check": mock}): + ret.update( + { + "comment": "iptables rule for salt" + " already set for ipv4 (a)", + "result": True, + } + ) + self.assertDictEqual( + iptables.insert("salt", table="", chain=""), ret + ) - with patch.dict(iptables.__opts__, {'test': True}): - ret.update({'result': None, - 'comment': 'iptables rule for salt' - ' needs to be set for ipv4 (a)'}) - self.assertDictEqual(iptables.insert('salt', - table='', - chain=''), ret) + with patch.dict(iptables.__opts__, {"test": True}): + ret.update( + { + "result": None, + "comment": "iptables rule for salt" + " needs to be set for ipv4 (a)", + } + ) + self.assertDictEqual( + iptables.insert("salt", table="", chain=""), ret + ) - with patch.dict(iptables.__opts__, {'test': False}): + with patch.dict(iptables.__opts__, {"test": False}): mock = MagicMock(side_effect=[False, True]) - with patch.dict(iptables.__salt__, - {'iptables.insert': mock}): - ret.update({'changes': {'locale': 'salt'}, - 'result': True, - 'comment': 'Set iptables rule' - ' for salt to: a for ipv4'}) - self.assertDictEqual(iptables.insert('salt', - table='', - chain='', - position=''), - ret) + with patch.dict(iptables.__salt__, {"iptables.insert": mock}): + ret.update( + { + "changes": {"locale": "salt"}, + "result": True, + "comment": "Set iptables rule" + " for salt to: a for ipv4", + } + ) + self.assertDictEqual( + iptables.insert( + "salt", table="", chain="", position="" + ), + ret, + ) - ret.update({'changes': {}, - 'result': False, - 'comment': 'Failed to set iptables' - ' rule for salt.\nAttempted rule was a' - }) - self.assertDictEqual(iptables.insert('salt', - table='', - chain='', - position=''), - ret) + ret.update( + { + "changes": {}, + "result": False, + "comment": "Failed to set iptables" + " rule for salt.\nAttempted rule was a", + } + ) + self.assertDictEqual( + iptables.insert( + "salt", table="", chain="", position="" + ), + ret, + ) def test_delete(self): - ''' + """ Test to delete a rule to a chain - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": None, "comment": ""} - self.assertDictEqual(iptables.delete('salt', rules=[]), ret) + self.assertDictEqual(iptables.delete("salt", rules=[]), ret) mock = MagicMock(return_value=[]) - with patch.object(iptables, '_STATE_INTERNAL_KEYWORDS', mock): - mock = MagicMock(return_value='a') - with patch.dict(iptables.__salt__, {'iptables.build_rule': mock}): + with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock): + mock = MagicMock(return_value="a") + with patch.dict(iptables.__salt__, {"iptables.build_rule": mock}): mock = MagicMock(side_effect=[False, True, True, True]) - with patch.dict(iptables.__salt__, {'iptables.check': mock}): - ret.update({'comment': 'iptables rule for salt' - ' already absent for ipv4 (a)', - 'result': True}) - self.assertDictEqual(iptables.delete('salt', - table='', chain=''), - ret) + with patch.dict(iptables.__salt__, {"iptables.check": mock}): + ret.update( + { + "comment": "iptables rule for salt" + " already absent for ipv4 (a)", + "result": True, + } + ) + self.assertDictEqual( + iptables.delete("salt", table="", chain=""), ret + ) - with patch.dict(iptables.__opts__, {'test': True}): - ret.update({'result': None, - 'comment': 'iptables rule for salt needs' - ' to be deleted for ipv4 (a)'}) - self.assertDictEqual(iptables.delete('salt', - table='', - chain=''), ret) + with patch.dict(iptables.__opts__, {"test": True}): + ret.update( + { + "result": None, + "comment": "iptables rule for salt needs" + " to be deleted for ipv4 (a)", + } + ) + self.assertDictEqual( + iptables.delete("salt", table="", chain=""), ret + ) - with patch.dict(iptables.__opts__, {'test': False}): + with patch.dict(iptables.__opts__, {"test": False}): mock = MagicMock(side_effect=[False, True]) - with patch.dict(iptables.__salt__, - {'iptables.delete': mock}): - ret.update({'result': True, - 'changes': {'locale': 'salt'}, - 'comment': 'Delete iptables rule' - ' for salt a'}) - self.assertDictEqual(iptables.delete('salt', - table='', - chain='', - position=''), - ret) + with patch.dict(iptables.__salt__, {"iptables.delete": mock}): + ret.update( + { + "result": True, + "changes": {"locale": "salt"}, + "comment": "Delete iptables rule" " for salt a", + } + ) + self.assertDictEqual( + iptables.delete( + "salt", table="", chain="", position="" + ), + ret, + ) - ret.update({'result': False, - 'changes': {}, - 'comment': 'Failed to delete iptables' - ' rule for salt.\nAttempted rule was a' - }) - self.assertDictEqual(iptables.delete('salt', - table='', - chain='', - position=''), - ret) + ret.update( + { + "result": False, + "changes": {}, + "comment": "Failed to delete iptables" + " rule for salt.\nAttempted rule was a", + } + ) + self.assertDictEqual( + iptables.delete( + "salt", table="", chain="", position="" + ), + ret, + ) def test_set_policy(self): - ''' + """ Test to sets the default policy for iptables firewall tables - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(return_value=[]) - with patch.object(iptables, '_STATE_INTERNAL_KEYWORDS', mock): - mock = MagicMock(return_value='stack') - with patch.dict(iptables.__salt__, {'iptables.get_policy': mock}): - ret.update({'comment': 'iptables default policy for chain' - ' on table for ipv4 already set to stack'}) - self.assertDictEqual(iptables.set_policy('salt', - table='', - chain='', - policy='stack'), ret) + with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock): + mock = MagicMock(return_value="stack") + with patch.dict(iptables.__salt__, {"iptables.get_policy": mock}): + ret.update( + { + "comment": "iptables default policy for chain" + " on table for ipv4 already set to stack" + } + ) + self.assertDictEqual( + iptables.set_policy("salt", table="", chain="", policy="stack"), ret + ) - with patch.dict(iptables.__opts__, {'test': True}): - ret.update({'comment': 'iptables default policy for chain' - ' on table for ipv4 needs to be set' - ' to sal', 'result': None}) - self.assertDictEqual(iptables.set_policy('salt', - table='', - chain='', - policy='sal'), - ret) + with patch.dict(iptables.__opts__, {"test": True}): + ret.update( + { + "comment": "iptables default policy for chain" + " on table for ipv4 needs to be set" + " to sal", + "result": None, + } + ) + self.assertDictEqual( + iptables.set_policy("salt", table="", chain="", policy="sal"), + ret, + ) - with patch.dict(iptables.__opts__, {'test': False}): + with patch.dict(iptables.__opts__, {"test": False}): mock = MagicMock(side_effect=[False, True]) - with patch.dict(iptables.__salt__, - {'iptables.set_policy': mock}): - ret.update({'changes': {'locale': 'salt'}, - 'comment': 'Set default policy for' - ' to sal family ipv4', - 'result': True}) - self.assertDictEqual(iptables.set_policy('salt', - table='', - chain='', - policy='sal'), - ret) + with patch.dict(iptables.__salt__, {"iptables.set_policy": mock}): + ret.update( + { + "changes": {"locale": "salt"}, + "comment": "Set default policy for" + " to sal family ipv4", + "result": True, + } + ) + self.assertDictEqual( + iptables.set_policy( + "salt", table="", chain="", policy="sal" + ), + ret, + ) - ret.update({'comment': 'Failed to set iptables' - ' default policy', - 'result': False, - 'changes': {}}) - self.assertDictEqual(iptables.set_policy('salt', - table='', - chain='', - policy='sal'), - ret) + ret.update( + { + "comment": "Failed to set iptables" " default policy", + "result": False, + "changes": {}, + } + ) + self.assertDictEqual( + iptables.set_policy( + "salt", table="", chain="", policy="sal" + ), + ret, + ) def test_flush(self): - ''' + """ Test to flush current iptables state - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": None, "comment": ""} mock = MagicMock(return_value=[]) - with patch.object(iptables, '_STATE_INTERNAL_KEYWORDS', mock): - with patch.dict(iptables.__opts__, {'test': True}): - ret.update({'comment': 'iptables rules in salt table filter' - ' chain ipv4 family needs to be flushed'}) - self.assertDictEqual(iptables.flush('salt'), ret) + with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock): + with patch.dict(iptables.__opts__, {"test": True}): + ret.update( + { + "comment": "iptables rules in salt table filter" + " chain ipv4 family needs to be flushed" + } + ) + self.assertDictEqual(iptables.flush("salt"), ret) - with patch.dict(iptables.__opts__, {'test': False}): + with patch.dict(iptables.__opts__, {"test": False}): mock = MagicMock(side_effect=[False, True]) - with patch.dict(iptables.__salt__, - {'iptables.flush': mock}): - ret.update({'changes': {'locale': 'salt'}, - 'comment': 'Flush iptables rules in ' - 'table chain ipv4 family', - 'result': True}) - self.assertDictEqual(iptables.flush('salt', - table='', chain=''), - ret) + with patch.dict(iptables.__salt__, {"iptables.flush": mock}): + ret.update( + { + "changes": {"locale": "salt"}, + "comment": "Flush iptables rules in " + "table chain ipv4 family", + "result": True, + } + ) + self.assertDictEqual( + iptables.flush("salt", table="", chain=""), ret + ) - ret.update({'changes': {}, - 'comment': 'Failed to flush iptables rules', - 'result': False}) - self.assertDictEqual(iptables.flush('salt', - table='', chain=''), - ret) + ret.update( + { + "changes": {}, + "comment": "Failed to flush iptables rules", + "result": False, + } + ) + self.assertDictEqual( + iptables.flush("salt", table="", chain=""), ret + ) def test_mod_aggregate(self): - ''' + """ Test to mod_aggregate function - ''' - self.assertDictEqual(iptables.mod_aggregate({'fun': 'salt'}, [], []), - {'fun': 'salt'}) + """ + self.assertDictEqual( + iptables.mod_aggregate({"fun": "salt"}, [], []), {"fun": "salt"} + ) - self.assertDictEqual(iptables.mod_aggregate({'fun': 'append'}, [], []), - {'fun': 'append'}) + self.assertDictEqual( + iptables.mod_aggregate({"fun": "append"}, [], []), {"fun": "append"} + ) diff --git a/tests/unit/states/test_jboss7.py b/tests/unit/states/test_jboss7.py index 31b57637db3..eee16f13a86 100644 --- a/tests/unit/states/test_jboss7.py +++ b/tests/unit/states/test_jboss7.py @@ -4,69 +4,109 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt libs import salt.states.jboss7 as jboss7 from salt.exceptions import CommandExecutionError from salt.ext import six +# Import Salt testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class JBoss7StateTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { jboss7: { - '__salt__': { - 'jboss7.read_datasource': MagicMock(), - 'jboss7.create_datasource': MagicMock(), - 'jboss7.update_datasource': MagicMock(), - 'jboss7.remove_datasource': MagicMock(), - 'jboss7.read_simple_binding': MagicMock(), - 'jboss7.create_simple_binding': MagicMock(), - 'jboss7.update_simple_binding': MagicMock(), - 'jboss7.undeploy': MagicMock(), - 'jboss7.deploy': MagicMock, - 'file.get_managed': MagicMock, - 'file.manage_file': MagicMock, - 'jboss7.list_deployments': MagicMock, + "__salt__": { + "jboss7.read_datasource": MagicMock(), + "jboss7.create_datasource": MagicMock(), + "jboss7.update_datasource": MagicMock(), + "jboss7.remove_datasource": MagicMock(), + "jboss7.read_simple_binding": MagicMock(), + "jboss7.create_simple_binding": MagicMock(), + "jboss7.update_simple_binding": MagicMock(), + "jboss7.undeploy": MagicMock(), + "jboss7.deploy": MagicMock, + "file.get_managed": MagicMock, + "file.manage_file": MagicMock, + "jboss7.list_deployments": MagicMock, }, - '__env__': 'base' + "__env__": "base", } } def test_should_not_redeploy_unchanged(self): # given - parameters = {'target_file': 'some_artifact', 'undeploy_force': False, 'undeploy': 'some_artifact', - 'source': 'some_artifact_on_master'} - jboss_conf = {'cli_path': 'somewhere', 'controller': 'some_controller'} + parameters = { + "target_file": "some_artifact", + "undeploy_force": False, + "undeploy": "some_artifact", + "source": "some_artifact_on_master", + } + jboss_conf = {"cli_path": "somewhere", "controller": "some_controller"} def list_deployments(jboss_config): - return ['some_artifact'] + return ["some_artifact"] - def file_get_managed(name, template, source, source_hash, source_hash_name, user, group, mode, attrs, saltenv, - context, defaults, skip_verify, kwargs): - return 'sfn', 'hash', '' + def file_get_managed( + name, + template, + source, + source_hash, + source_hash_name, + user, + group, + mode, + attrs, + saltenv, + context, + defaults, + skip_verify, + kwargs, + ): + return "sfn", "hash", "" - def file_manage_file(name, sfn, ret, source, source_sum, user, group, mode, attrs, saltenv, backup, makedirs, - template, show_diff, contents, dir_mode): - return {'result': True, 'changes': False} + def file_manage_file( + name, + sfn, + ret, + source, + source_sum, + user, + group, + mode, + attrs, + saltenv, + backup, + makedirs, + template, + show_diff, + contents, + dir_mode, + ): + return {"result": True, "changes": False} jboss7_undeploy_mock = MagicMock() jboss7_deploy_mock = MagicMock() file_get_managed = MagicMock(side_effect=file_get_managed) file_manage_file = MagicMock(side_effect=file_manage_file) list_deployments_mock = MagicMock(side_effect=list_deployments) - with patch.dict(jboss7.__salt__, {'jboss7.undeploy': jboss7_undeploy_mock, - 'jboss7.deploy': jboss7_deploy_mock, - 'file.get_managed': file_get_managed, - 'file.manage_file': file_manage_file, - 'jboss7.list_deployments': list_deployments_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.undeploy": jboss7_undeploy_mock, + "jboss7.deploy": jboss7_deploy_mock, + "file.get_managed": file_get_managed, + "file.manage_file": file_manage_file, + "jboss7.list_deployments": list_deployments_mock, + }, + ): # when - result = jboss7.deployed(name="unchanged", jboss_config=jboss_conf, salt_source=parameters) + result = jboss7.deployed( + name="unchanged", jboss_config=jboss_conf, salt_source=parameters + ) # then self.assertFalse(jboss7_undeploy_mock.called) @@ -74,33 +114,74 @@ class JBoss7StateTestCase(TestCase, LoaderModuleMockMixin): def test_should_redeploy_changed(self): # given - parameters = {'target_file': 'some_artifact', 'undeploy_force': False, 'undeploy': 'some_artifact', - 'source': 'some_artifact_on_master'} - jboss_conf = {'cli_path': 'somewhere', 'controller': 'some_controller'} + parameters = { + "target_file": "some_artifact", + "undeploy_force": False, + "undeploy": "some_artifact", + "source": "some_artifact_on_master", + } + jboss_conf = {"cli_path": "somewhere", "controller": "some_controller"} def list_deployments(jboss_config): - return ['some_artifact'] + return ["some_artifact"] - def file_get_managed(name, template, source, source_hash, source_hash_name, user, group, mode, attrs, saltenv, - context, defaults, skip_verify, kwargs): - return 'sfn', 'hash', '' + def file_get_managed( + name, + template, + source, + source_hash, + source_hash_name, + user, + group, + mode, + attrs, + saltenv, + context, + defaults, + skip_verify, + kwargs, + ): + return "sfn", "hash", "" - def file_manage_file(name, sfn, ret, source, source_sum, user, group, mode, attrs, saltenv, backup, makedirs, - template, show_diff, contents, dir_mode): - return {'result': True, 'changes': True} + def file_manage_file( + name, + sfn, + ret, + source, + source_sum, + user, + group, + mode, + attrs, + saltenv, + backup, + makedirs, + template, + show_diff, + contents, + dir_mode, + ): + return {"result": True, "changes": True} jboss7_undeploy_mock = MagicMock() jboss7_deploy_mock = MagicMock() file_get_managed = MagicMock(side_effect=file_get_managed) file_manage_file = MagicMock(side_effect=file_manage_file) list_deployments_mock = MagicMock(side_effect=list_deployments) - with patch.dict(jboss7.__salt__, {'jboss7.undeploy': jboss7_undeploy_mock, - 'jboss7.deploy': jboss7_deploy_mock, - 'file.get_managed': file_get_managed, - 'file.manage_file': file_manage_file, - 'jboss7.list_deployments': list_deployments_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.undeploy": jboss7_undeploy_mock, + "jboss7.deploy": jboss7_deploy_mock, + "file.get_managed": file_get_managed, + "file.manage_file": file_manage_file, + "jboss7.list_deployments": list_deployments_mock, + }, + ): # when - result = jboss7.deployed(name="unchanged", jboss_config=jboss_conf, salt_source=parameters) + result = jboss7.deployed( + name="unchanged", jboss_config=jboss_conf, salt_source=parameters + ) # then self.assertTrue(jboss7_undeploy_mock.called) @@ -108,33 +189,74 @@ class JBoss7StateTestCase(TestCase, LoaderModuleMockMixin): def test_should_deploy_different_artifact(self): # given - parameters = {'target_file': 'some_artifact', 'undeploy_force': False, 'undeploy': 'some_artifact', - 'source': 'some_artifact_on_master'} - jboss_conf = {'cli_path': 'somewhere', 'controller': 'some_controller'} + parameters = { + "target_file": "some_artifact", + "undeploy_force": False, + "undeploy": "some_artifact", + "source": "some_artifact_on_master", + } + jboss_conf = {"cli_path": "somewhere", "controller": "some_controller"} def list_deployments(jboss_config): - return ['some_other_artifact'] + return ["some_other_artifact"] - def file_get_managed(name, template, source, source_hash, source_hash_name, user, group, mode, attrs, saltenv, - context, defaults, skip_verify, kwargs): - return 'sfn', 'hash', '' + def file_get_managed( + name, + template, + source, + source_hash, + source_hash_name, + user, + group, + mode, + attrs, + saltenv, + context, + defaults, + skip_verify, + kwargs, + ): + return "sfn", "hash", "" - def file_manage_file(name, sfn, ret, source, source_sum, user, group, mode, attrs, saltenv, backup, makedirs, - template, show_diff, contents, dir_mode): - return {'result': True, 'changes': False} + def file_manage_file( + name, + sfn, + ret, + source, + source_sum, + user, + group, + mode, + attrs, + saltenv, + backup, + makedirs, + template, + show_diff, + contents, + dir_mode, + ): + return {"result": True, "changes": False} jboss7_undeploy_mock = MagicMock() jboss7_deploy_mock = MagicMock() file_get_managed = MagicMock(side_effect=file_get_managed) file_manage_file = MagicMock(side_effect=file_manage_file) list_deployments_mock = MagicMock(side_effect=list_deployments) - with patch.dict(jboss7.__salt__, {'jboss7.undeploy': jboss7_undeploy_mock, - 'jboss7.deploy': jboss7_deploy_mock, - 'file.get_managed': file_get_managed, - 'file.manage_file': file_manage_file, - 'jboss7.list_deployments': list_deployments_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.undeploy": jboss7_undeploy_mock, + "jboss7.deploy": jboss7_deploy_mock, + "file.get_managed": file_get_managed, + "file.manage_file": file_manage_file, + "jboss7.list_deployments": list_deployments_mock, + }, + ): # when - result = jboss7.deployed(name="unchanged", jboss_config=jboss_conf, salt_source=parameters) + result = jboss7.deployed( + name="unchanged", jboss_config=jboss_conf, salt_source=parameters + ) # then self.assertFalse(jboss7_undeploy_mock.called) @@ -142,33 +264,74 @@ class JBoss7StateTestCase(TestCase, LoaderModuleMockMixin): def test_should_redploy_undeploy_force(self): # given - parameters = {'target_file': 'some_artifact', 'undeploy_force': True, 'undeploy': 'some_artifact', - 'source': 'some_artifact_on_master'} - jboss_conf = {'cli_path': 'somewhere', 'controller': 'some_controller'} + parameters = { + "target_file": "some_artifact", + "undeploy_force": True, + "undeploy": "some_artifact", + "source": "some_artifact_on_master", + } + jboss_conf = {"cli_path": "somewhere", "controller": "some_controller"} def list_deployments(jboss_config): - return ['some_artifact'] + return ["some_artifact"] - def file_get_managed(name, template, source, source_hash, source_hash_name, user, group, mode, attrs, saltenv, - context, defaults, skip_verify, kwargs): - return 'sfn', 'hash', '' + def file_get_managed( + name, + template, + source, + source_hash, + source_hash_name, + user, + group, + mode, + attrs, + saltenv, + context, + defaults, + skip_verify, + kwargs, + ): + return "sfn", "hash", "" - def file_manage_file(name, sfn, ret, source, source_sum, user, group, mode, attrs, saltenv, backup, makedirs, - template, show_diff, contents, dir_mode): - return {'result': True, 'changes': False} + def file_manage_file( + name, + sfn, + ret, + source, + source_sum, + user, + group, + mode, + attrs, + saltenv, + backup, + makedirs, + template, + show_diff, + contents, + dir_mode, + ): + return {"result": True, "changes": False} jboss7_undeploy_mock = MagicMock() jboss7_deploy_mock = MagicMock() file_get_managed = MagicMock(side_effect=file_get_managed) file_manage_file = MagicMock(side_effect=file_manage_file) list_deployments_mock = MagicMock(side_effect=list_deployments) - with patch.dict(jboss7.__salt__, {'jboss7.undeploy': jboss7_undeploy_mock, - 'jboss7.deploy': jboss7_deploy_mock, - 'file.get_managed': file_get_managed, - 'file.manage_file': file_manage_file, - 'jboss7.list_deployments': list_deployments_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.undeploy": jboss7_undeploy_mock, + "jboss7.deploy": jboss7_deploy_mock, + "file.get_managed": file_get_managed, + "file.manage_file": file_manage_file, + "jboss7.list_deployments": list_deployments_mock, + }, + ): # when - result = jboss7.deployed(name="unchanged", jboss_config=jboss_conf, salt_source=parameters) + result = jboss7.deployed( + name="unchanged", jboss_config=jboss_conf, salt_source=parameters + ) # then self.assertTrue(jboss7_undeploy_mock.called) @@ -176,283 +339,410 @@ class JBoss7StateTestCase(TestCase, LoaderModuleMockMixin): def test_should_create_new_datasource_if_not_exists(self): # given - datasource_properties = {'connection-url': 'jdbc:/old-connection-url'} - ds_status = {'created': False} + datasource_properties = {"connection-url": "jdbc:/old-connection-url"} + ds_status = {"created": False} def read_func(jboss_config, name, profile): - if ds_status['created']: - return {'success': True, 'result': datasource_properties} + if ds_status["created"]: + return {"success": True, "result": datasource_properties} else: - return {'success': False, 'err_code': 'JBAS014807'} + return {"success": False, "err_code": "JBAS014807"} def create_func(jboss_config, name, datasource_properties, profile): - ds_status['created'] = True - return {'success': True} + ds_status["created"] = True + return {"success": True} read_mock = MagicMock(side_effect=read_func) create_mock = MagicMock(side_effect=create_func) update_mock = MagicMock() - with patch.dict(jboss7.__salt__, {'jboss7.read_datasource': read_mock, - 'jboss7.create_datasource': create_mock, - 'jboss7.update_datasource': update_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_datasource": read_mock, + "jboss7.create_datasource": create_mock, + "jboss7.update_datasource": update_mock, + }, + ): # when - result = jboss7.datasource_exists(name='appDS', jboss_config={}, - datasource_properties=datasource_properties, profile=None) + result = jboss7.datasource_exists( + name="appDS", + jboss_config={}, + datasource_properties=datasource_properties, + profile=None, + ) # then - create_mock.assert_called_with(name='appDS', jboss_config={}, - datasource_properties=datasource_properties, profile=None) + create_mock.assert_called_with( + name="appDS", + jboss_config={}, + datasource_properties=datasource_properties, + profile=None, + ) self.assertFalse(update_mock.called) - self.assertEqual(result['comment'], 'Datasource created.') + self.assertEqual(result["comment"], "Datasource created.") def test_should_update_the_datasource_if_exists(self): - ds_status = {'updated': False} + ds_status = {"updated": False} def read_func(jboss_config, name, profile): - if ds_status['updated']: - return {'success': True, 'result': {'connection-url': 'jdbc:/new-connection-url'}} + if ds_status["updated"]: + return { + "success": True, + "result": {"connection-url": "jdbc:/new-connection-url"}, + } else: - return {'success': True, 'result': {'connection-url': 'jdbc:/old-connection-url'}} + return { + "success": True, + "result": {"connection-url": "jdbc:/old-connection-url"}, + } def update_func(jboss_config, name, new_properties, profile): - ds_status['updated'] = True - return {'success': True} + ds_status["updated"] = True + return {"success": True} read_mock = MagicMock(side_effect=read_func) create_mock = MagicMock() update_mock = MagicMock(side_effect=update_func) - with patch.dict(jboss7.__salt__, {'jboss7.read_datasource': read_mock, - 'jboss7.create_datasource': create_mock, - 'jboss7.update_datasource': update_mock}): - result = jboss7.datasource_exists(name='appDS', jboss_config={}, - datasource_properties={'connection-url': 'jdbc:/new-connection-url'}, - profile=None) + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_datasource": read_mock, + "jboss7.create_datasource": create_mock, + "jboss7.update_datasource": update_mock, + }, + ): + result = jboss7.datasource_exists( + name="appDS", + jboss_config={}, + datasource_properties={"connection-url": "jdbc:/new-connection-url"}, + profile=None, + ) - update_mock.assert_called_with(name='appDS', jboss_config={}, - new_properties={'connection-url': 'jdbc:/new-connection-url'}, - profile=None) + update_mock.assert_called_with( + name="appDS", + jboss_config={}, + new_properties={"connection-url": "jdbc:/new-connection-url"}, + profile=None, + ) self.assertTrue(read_mock.called) - self.assertEqual(result['comment'], 'Datasource updated.') + self.assertEqual(result["comment"], "Datasource updated.") def test_should_recreate_the_datasource_if_specified(self): - read_mock = MagicMock(return_value={'success': True, - 'result': {'connection-url': 'jdbc:/same-connection-url'}}) - create_mock = MagicMock(return_value={'success': True}) - remove_mock = MagicMock(return_value={'success': True}) + read_mock = MagicMock( + return_value={ + "success": True, + "result": {"connection-url": "jdbc:/same-connection-url"}, + } + ) + create_mock = MagicMock(return_value={"success": True}) + remove_mock = MagicMock(return_value={"success": True}) update_mock = MagicMock() - with patch.dict(jboss7.__salt__, {'jboss7.read_datasource': read_mock, - 'jboss7.create_datasource': create_mock, - 'jboss7.remove_datasource': remove_mock, - 'jboss7.update_datasource': update_mock}): - result = jboss7.datasource_exists(name='appDS', jboss_config={}, - datasource_properties={'connection-url': 'jdbc:/same-connection-url'}, - recreate=True) + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_datasource": read_mock, + "jboss7.create_datasource": create_mock, + "jboss7.remove_datasource": remove_mock, + "jboss7.update_datasource": update_mock, + }, + ): + result = jboss7.datasource_exists( + name="appDS", + jboss_config={}, + datasource_properties={"connection-url": "jdbc:/same-connection-url"}, + recreate=True, + ) - remove_mock.assert_called_with(name='appDS', jboss_config={}, profile=None) - create_mock.assert_called_with(name='appDS', jboss_config={}, - datasource_properties={'connection-url': 'jdbc:/same-connection-url'}, - profile=None) - self.assertEqual(result['changes']['removed'], 'appDS') - self.assertEqual(result['changes']['created'], 'appDS') + remove_mock.assert_called_with(name="appDS", jboss_config={}, profile=None) + create_mock.assert_called_with( + name="appDS", + jboss_config={}, + datasource_properties={"connection-url": "jdbc:/same-connection-url"}, + profile=None, + ) + self.assertEqual(result["changes"]["removed"], "appDS") + self.assertEqual(result["changes"]["created"], "appDS") def test_should_inform_if_the_datasource_has_not_changed(self): - read_mock = MagicMock(return_value={'success': True, - 'result': {'connection-url': 'jdbc:/same-connection-url'}}) + read_mock = MagicMock( + return_value={ + "success": True, + "result": {"connection-url": "jdbc:/same-connection-url"}, + } + ) create_mock = MagicMock() remove_mock = MagicMock() - update_mock = MagicMock(return_value={'success': True}) + update_mock = MagicMock(return_value={"success": True}) - with patch.dict(jboss7.__salt__, {'jboss7.read_datasource': read_mock, - 'jboss7.create_datasource': create_mock, - 'jboss7.remove_datasource': remove_mock, - 'jboss7.update_datasource': update_mock}): - result = jboss7.datasource_exists(name='appDS', jboss_config={}, - datasource_properties={'connection-url': 'jdbc:/old-connection-url'}) + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_datasource": read_mock, + "jboss7.create_datasource": create_mock, + "jboss7.remove_datasource": remove_mock, + "jboss7.update_datasource": update_mock, + }, + ): + result = jboss7.datasource_exists( + name="appDS", + jboss_config={}, + datasource_properties={"connection-url": "jdbc:/old-connection-url"}, + ) - update_mock.assert_called_with(name='appDS', jboss_config={}, - new_properties={'connection-url': 'jdbc:/old-connection-url'}, profile=None) + update_mock.assert_called_with( + name="appDS", + jboss_config={}, + new_properties={"connection-url": "jdbc:/old-connection-url"}, + profile=None, + ) self.assertFalse(create_mock.called) - self.assertEqual(result['comment'], 'Datasource not changed.') + self.assertEqual(result["comment"], "Datasource not changed.") def test_should_create_binding_if_not_exists(self): # given - binding_status = {'created': False} + binding_status = {"created": False} def read_func(jboss_config, binding_name, profile): - if binding_status['created']: - return {'success': True, 'result': {'value': 'DEV'}} + if binding_status["created"]: + return {"success": True, "result": {"value": "DEV"}} else: - return {'success': False, 'err_code': 'JBAS014807'} + return {"success": False, "err_code": "JBAS014807"} def create_func(jboss_config, binding_name, value, profile): - binding_status['created'] = True - return {'success': True} + binding_status["created"] = True + return {"success": True} read_mock = MagicMock(side_effect=read_func) create_mock = MagicMock(side_effect=create_func) update_mock = MagicMock() - with patch.dict(jboss7.__salt__, {'jboss7.read_simple_binding': read_mock, - 'jboss7.create_simple_binding': create_mock, - 'jboss7.update_simple_binding': update_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_simple_binding": read_mock, + "jboss7.create_simple_binding": create_mock, + "jboss7.update_simple_binding": update_mock, + }, + ): # when - result = jboss7.bindings_exist(name='bindings', jboss_config={}, bindings={'env': 'DEV'}, profile=None) + result = jboss7.bindings_exist( + name="bindings", jboss_config={}, bindings={"env": "DEV"}, profile=None + ) # then - create_mock.assert_called_with(jboss_config={}, binding_name='env', value='DEV', profile=None) + create_mock.assert_called_with( + jboss_config={}, binding_name="env", value="DEV", profile=None + ) self.assertEqual(update_mock.call_count, 0) - self.assertEqual(result['changes'], {'added': 'env:DEV\n'}) - self.assertEqual(result['comment'], 'Bindings changed.') + self.assertEqual(result["changes"], {"added": "env:DEV\n"}) + self.assertEqual(result["comment"], "Bindings changed.") def test_should_update_bindings_if_exists_and_different(self): # given - binding_status = {'updated': False} + binding_status = {"updated": False} def read_func(jboss_config, binding_name, profile): - if binding_status['updated']: - return {'success': True, 'result': {'value': 'DEV2'}} + if binding_status["updated"]: + return {"success": True, "result": {"value": "DEV2"}} else: - return {'success': True, 'result': {'value': 'DEV'}} + return {"success": True, "result": {"value": "DEV"}} def update_func(jboss_config, binding_name, value, profile): - binding_status['updated'] = True - return {'success': True} + binding_status["updated"] = True + return {"success": True} read_mock = MagicMock(side_effect=read_func) create_mock = MagicMock() update_mock = MagicMock(side_effect=update_func) - with patch.dict(jboss7.__salt__, {'jboss7.read_simple_binding': read_mock, - 'jboss7.create_simple_binding': create_mock, - 'jboss7.update_simple_binding': update_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_simple_binding": read_mock, + "jboss7.create_simple_binding": create_mock, + "jboss7.update_simple_binding": update_mock, + }, + ): # when - result = jboss7.bindings_exist(name='bindings', jboss_config={}, bindings={'env': 'DEV2'}, profile=None) + result = jboss7.bindings_exist( + name="bindings", jboss_config={}, bindings={"env": "DEV2"}, profile=None + ) # then - update_mock.assert_called_with(jboss_config={}, binding_name='env', value='DEV2', profile=None) + update_mock.assert_called_with( + jboss_config={}, binding_name="env", value="DEV2", profile=None + ) self.assertEqual(create_mock.call_count, 0) - self.assertEqual(result['changes'], {'changed': 'env:DEV->DEV2\n'}) - self.assertEqual(result['comment'], 'Bindings changed.') + self.assertEqual(result["changes"], {"changed": "env:DEV->DEV2\n"}) + self.assertEqual(result["comment"], "Bindings changed.") def test_should_not_update_bindings_if_same(self): # given - read_mock = MagicMock(return_value={'success': True, 'result': {'value': 'DEV2'}}) + read_mock = MagicMock( + return_value={"success": True, "result": {"value": "DEV2"}} + ) create_mock = MagicMock() update_mock = MagicMock() - with patch.dict(jboss7.__salt__, {'jboss7.read_simple_binding': read_mock, - 'jboss7.create_simple_binding': create_mock, - 'jboss7.update_simple_binding': update_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_simple_binding": read_mock, + "jboss7.create_simple_binding": create_mock, + "jboss7.update_simple_binding": update_mock, + }, + ): # when - result = jboss7.bindings_exist(name='bindings', jboss_config={}, bindings={'env': 'DEV2'}) + result = jboss7.bindings_exist( + name="bindings", jboss_config={}, bindings={"env": "DEV2"} + ) # then self.assertEqual(create_mock.call_count, 0) self.assertEqual(update_mock.call_count, 0) - self.assertEqual(result['changes'], {}) - self.assertEqual(result['comment'], 'Bindings not changed.') + self.assertEqual(result["changes"], {}) + self.assertEqual(result["comment"], "Bindings not changed.") def test_should_raise_exception_if_cannot_create_binding(self): def read_func(jboss_config, binding_name, profile): - return {'success': False, 'err_code': 'JBAS014807'} + return {"success": False, "err_code": "JBAS014807"} def create_func(jboss_config, binding_name, value, profile): - return {'success': False, 'failure-description': 'Incorrect binding name.'} + return {"success": False, "failure-description": "Incorrect binding name."} read_mock = MagicMock(side_effect=read_func) create_mock = MagicMock(side_effect=create_func) update_mock = MagicMock() - with patch.dict(jboss7.__salt__, {'jboss7.read_simple_binding': read_mock, - 'jboss7.create_simple_binding': create_mock, - 'jboss7.update_simple_binding': update_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_simple_binding": read_mock, + "jboss7.create_simple_binding": create_mock, + "jboss7.update_simple_binding": update_mock, + }, + ): # when try: - jboss7.bindings_exist(name='bindings', jboss_config={}, bindings={'env': 'DEV2'}, profile=None) - self.fail('An exception should be thrown') + jboss7.bindings_exist( + name="bindings", + jboss_config={}, + bindings={"env": "DEV2"}, + profile=None, + ) + self.fail("An exception should be thrown") except CommandExecutionError as e: - self.assertEqual(six.text_type(e), 'Incorrect binding name.') + self.assertEqual(six.text_type(e), "Incorrect binding name.") def test_should_raise_exception_if_cannot_update_binding(self): def read_func(jboss_config, binding_name, profile): - return {'success': True, 'result': {'value': 'DEV'}} + return {"success": True, "result": {"value": "DEV"}} def update_func(jboss_config, binding_name, value, profile): - return {'success': False, 'failure-description': 'Incorrect binding name.'} + return {"success": False, "failure-description": "Incorrect binding name."} read_mock = MagicMock(side_effect=read_func) create_mock = MagicMock() update_mock = MagicMock(side_effect=update_func) - with patch.dict(jboss7.__salt__, {'jboss7.read_simple_binding': read_mock, - 'jboss7.create_simple_binding': create_mock, - 'jboss7.update_simple_binding': update_mock}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_simple_binding": read_mock, + "jboss7.create_simple_binding": create_mock, + "jboss7.update_simple_binding": update_mock, + }, + ): # when try: - jboss7.bindings_exist(name='bindings', jboss_config={}, - bindings={'env': '!@#!///some weird value'}, - profile=None) - self.fail('An exception should be thrown') + jboss7.bindings_exist( + name="bindings", + jboss_config={}, + bindings={"env": "!@#!///some weird value"}, + profile=None, + ) + self.fail("An exception should be thrown") except CommandExecutionError as e: - self.assertEqual(six.text_type(e), 'Incorrect binding name.') + self.assertEqual(six.text_type(e), "Incorrect binding name.") def test_datasource_exist_create_datasource_good_code(self): - jboss_config = {'cli_path': '/home/ch44d/Desktop/wildfly-18.0.0.Final/bin/jboss-cli.sh', - 'controller': '127.0.0.1: 9990', - 'cli_user': 'user', - 'cli_password': 'user'} + jboss_config = { + "cli_path": "/home/ch44d/Desktop/wildfly-18.0.0.Final/bin/jboss-cli.sh", + "controller": "127.0.0.1: 9990", + "cli_user": "user", + "cli_password": "user", + } - datasource_properties = {'driver - name': 'h2', - 'connection - url': 'jdbc:sqlserver://127.0.0.1:1433;DatabaseName=test_s2', - 'jndi - name': 'java:/home/ch44d/Desktop/sqljdbc_7.4/enu/mssql-jdbc-7.4.1.jre8.jar', - 'user - name': 'user', - 'password': 'user', - 'use - java - context': True} + datasource_properties = { + "driver - name": "h2", + "connection - url": "jdbc:sqlserver://127.0.0.1:1433;DatabaseName=test_s2", + "jndi - name": "java:/home/ch44d/Desktop/sqljdbc_7.4/enu/mssql-jdbc-7.4.1.jre8.jar", + "user - name": "user", + "password": "user", + "use - java - context": True, + } - read_datasource = MagicMock(return_value={'success': False, - 'err_code': 'WFLYCTL0216'}) + read_datasource = MagicMock( + return_value={"success": False, "err_code": "WFLYCTL0216"} + ) - error_msg = 'Error: -1' - create_datasource = MagicMock(return_value={'success': False, - 'stdout': error_msg}) + error_msg = "Error: -1" + create_datasource = MagicMock( + return_value={"success": False, "stdout": error_msg} + ) - with patch.dict(jboss7.__salt__, {'jboss7.read_datasource': read_datasource, - 'jboss7.create_datasource': create_datasource}): + with patch.dict( + jboss7.__salt__, + { + "jboss7.read_datasource": read_datasource, + "jboss7.create_datasource": create_datasource, + }, + ): ret = jboss7.datasource_exists("SQL", jboss_config, datasource_properties) - self.assertTrue('result' in ret) - self.assertFalse(ret['result']) - self.assertTrue('comment' in ret) - self.assertTrue(error_msg in ret['comment']) + self.assertTrue("result" in ret) + self.assertFalse(ret["result"]) + self.assertTrue("comment" in ret) + self.assertTrue(error_msg in ret["comment"]) read_datasource.assert_called_once() create_datasource.assert_called_once() def test_datasource_exist_create_datasource_bad_code(self): - jboss_config = {'cli_path': '/home/ch44d/Desktop/wildfly-18.0.0.Final/bin/jboss-cli.sh', - 'controller': '127.0.0.1: 9990', - 'cli_user': 'user', - 'cli_password': 'user'} + jboss_config = { + "cli_path": "/home/ch44d/Desktop/wildfly-18.0.0.Final/bin/jboss-cli.sh", + "controller": "127.0.0.1: 9990", + "cli_user": "user", + "cli_password": "user", + } - datasource_properties = {'driver - name': 'h2', - 'connection - url': 'jdbc:sqlserver://127.0.0.1:1433;DatabaseName=test_s2', - 'jndi - name': 'java:/home/ch44d/Desktop/sqljdbc_7.4/enu/mssql-jdbc-7.4.1.jre8.jar', - 'user - name': 'user', - 'password': 'user', - 'use - java - context': True} + datasource_properties = { + "driver - name": "h2", + "connection - url": "jdbc:sqlserver://127.0.0.1:1433;DatabaseName=test_s2", + "jndi - name": "java:/home/ch44d/Desktop/sqljdbc_7.4/enu/mssql-jdbc-7.4.1.jre8.jar", + "user - name": "user", + "password": "user", + "use - java - context": True, + } - read_datasource = MagicMock(return_value={'success': False, - 'err_code': 'WFLYCTL0217', - 'failure-description': 'Something happened'}) + read_datasource = MagicMock( + return_value={ + "success": False, + "err_code": "WFLYCTL0217", + "failure-description": "Something happened", + } + ) - with patch.dict(jboss7.__salt__, {'jboss7.read_datasource': read_datasource}): - self.assertRaises(CommandExecutionError, - jboss7.datasource_exists, - 'SQL', - jboss_config, - datasource_properties) + with patch.dict(jboss7.__salt__, {"jboss7.read_datasource": read_datasource}): + self.assertRaises( + CommandExecutionError, + jboss7.datasource_exists, + "SQL", + jboss_config, + datasource_properties, + ) read_datasource.assert_called_once() diff --git a/tests/unit/states/test_kapacitor.py b/tests/unit/states/test_kapacitor.py index f4f0001aad1..30b7435f00b 100644 --- a/tests/unit/states/test_kapacitor.py +++ b/tests/unit/states/test_kapacitor.py @@ -3,112 +3,136 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import Mock, patch, mock_open - # Import Salt libs import salt.states.kapacitor as kapacitor +# Import Salt testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import Mock, mock_open, patch +from tests.support.unit import TestCase -def _present(name='testname', - tick_script='/tmp/script.tick', - task_type='stream', - database='testdb', - retention_policy='default', - dbrps=None, - enable=True, - task=None, - define_result=True, - enable_result=True, - disable_result=True, - script='testscript'): - ''' + +def _present( + name="testname", + tick_script="/tmp/script.tick", + task_type="stream", + database="testdb", + retention_policy="default", + dbrps=None, + enable=True, + task=None, + define_result=True, + enable_result=True, + disable_result=True, + script="testscript", +): + """ Run a "kapacitor.present" state after setting up mocks, and return the state return value as well as the mocks to make assertions. - ''' + """ get_mock = Mock(return_value=task) if isinstance(define_result, bool): - define_result = {'success': define_result} + define_result = {"success": define_result} define_mock = Mock(return_value=define_result) if isinstance(enable_result, bool): - enable_result = {'success': enable_result} + enable_result = {"success": enable_result} enable_mock = Mock(return_value=enable_result) if isinstance(disable_result, bool): - disable_result = {'success': disable_result} + disable_result = {"success": disable_result} disable_mock = Mock(return_value=disable_result) - with patch.dict(kapacitor.__salt__, { - 'kapacitor.get_task': get_mock, - 'kapacitor.define_task': define_mock, - 'kapacitor.enable_task': enable_mock, - 'kapacitor.disable_task': disable_mock, - }): - with patch('salt.utils.files.fopen', mock_open(read_data=script)) as open_mock: - retval = kapacitor.task_present(name, tick_script, task_type=task_type, - database=database, retention_policy=retention_policy, enable=enable, dbrps=dbrps) + with patch.dict( + kapacitor.__salt__, + { + "kapacitor.get_task": get_mock, + "kapacitor.define_task": define_mock, + "kapacitor.enable_task": enable_mock, + "kapacitor.disable_task": disable_mock, + }, + ): + with patch("salt.utils.files.fopen", mock_open(read_data=script)) as open_mock: + retval = kapacitor.task_present( + name, + tick_script, + task_type=task_type, + database=database, + retention_policy=retention_policy, + enable=enable, + dbrps=dbrps, + ) return retval, get_mock, define_mock, enable_mock, disable_mock -def _task(script='testscript', enabled=True, task_type='stream', - db='testdb', rp='default'): +def _task( + script="testscript", enabled=True, task_type="stream", db="testdb", rp="default" +): return { - 'script': script, - 'enabled': enabled, - 'type': task_type, - 'dbrps': [{'db': db, 'rp': rp}], + "script": script, + "enabled": enabled, + "type": task_type, + "dbrps": [{"db": db, "rp": rp}], } class KapacitorTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - return { - kapacitor: { - '__opts__': {'test': False}, - '__env__': 'test' - } - } + return {kapacitor: {"__opts__": {"test": False}, "__env__": "test"}} def test_task_present_new_task(self): - ret, get_mock, define_mock, enable_mock, _ = _present(dbrps=['testdb2.default_rp']) - get_mock.assert_called_once_with('testname') - define_mock.assert_called_once_with('testname', '/tmp/script.tick', - database='testdb', retention_policy='default', - task_type='stream', dbrps=['testdb2.default_rp', 'testdb.default']) - enable_mock.assert_called_once_with('testname') - self.assertIn('TICKscript diff', ret['changes']) - self.assertIn('enabled', ret['changes']) - self.assertEqual(True, ret['changes']['enabled']['new']) + ret, get_mock, define_mock, enable_mock, _ = _present( + dbrps=["testdb2.default_rp"] + ) + get_mock.assert_called_once_with("testname") + define_mock.assert_called_once_with( + "testname", + "/tmp/script.tick", + database="testdb", + retention_policy="default", + task_type="stream", + dbrps=["testdb2.default_rp", "testdb.default"], + ) + enable_mock.assert_called_once_with("testname") + self.assertIn("TICKscript diff", ret["changes"]) + self.assertIn("enabled", ret["changes"]) + self.assertEqual(True, ret["changes"]["enabled"]["new"]) def test_task_present_existing_task_updated_script(self): - ret, get_mock, define_mock, enable_mock, _ = _present(task=_task(script='oldscript')) - get_mock.assert_called_once_with('testname') - define_mock.assert_called_once_with('testname', '/tmp/script.tick', - database='testdb', retention_policy='default', - task_type='stream', dbrps=['testdb.default']) + ret, get_mock, define_mock, enable_mock, _ = _present( + task=_task(script="oldscript") + ) + get_mock.assert_called_once_with("testname") + define_mock.assert_called_once_with( + "testname", + "/tmp/script.tick", + database="testdb", + retention_policy="default", + task_type="stream", + dbrps=["testdb.default"], + ) self.assertEqual(False, enable_mock.called) - self.assertIn('TICKscript diff', ret['changes']) - self.assertNotIn('enabled', ret['changes']) + self.assertIn("TICKscript diff", ret["changes"]) + self.assertNotIn("enabled", ret["changes"]) def test_task_present_existing_task_not_enabled(self): ret, get_mock, define_mock, enable_mock, _ = _present(task=_task(enabled=False)) - get_mock.assert_called_once_with('testname') + get_mock.assert_called_once_with("testname") self.assertEqual(False, define_mock.called) - enable_mock.assert_called_once_with('testname') - self.assertNotIn('diff', ret['changes']) - self.assertIn('enabled', ret['changes']) - self.assertEqual(True, ret['changes']['enabled']['new']) + enable_mock.assert_called_once_with("testname") + self.assertNotIn("diff", ret["changes"]) + self.assertIn("enabled", ret["changes"]) + self.assertEqual(True, ret["changes"]["enabled"]["new"]) def test_task_present_disable_existing_task(self): - ret, get_mock, define_mock, _, disable_mock = _present(task=_task(), enable=False) - get_mock.assert_called_once_with('testname') + ret, get_mock, define_mock, _, disable_mock = _present( + task=_task(), enable=False + ) + get_mock.assert_called_once_with("testname") self.assertEqual(False, define_mock.called) - disable_mock.assert_called_once_with('testname') - self.assertNotIn('diff', ret['changes']) - self.assertIn('enabled', ret['changes']) - self.assertEqual(False, ret['changes']['enabled']['new']) + disable_mock.assert_called_once_with("testname") + self.assertNotIn("diff", ret["changes"]) + self.assertIn("enabled", ret["changes"]) + self.assertEqual(False, ret["changes"]["enabled"]["new"]) diff --git a/tests/unit/states/test_kernelpkg.py b/tests/unit/states/test_kernelpkg.py index 12cdf32ff91..09765f66ddd 100644 --- a/tests/unit/states/test_kernelpkg.py +++ b/tests/unit/states/test_kernelpkg.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for 'module.aptkernelpkg' :platform: Linux :maturity: develop versionadded:: 2018.3.0 -''' +""" # pylint: disable=invalid-name,no-member # Import Python libs @@ -17,155 +17,152 @@ try: # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import skipIf, TestCase - from tests.support.mock import ( - MagicMock, - patch) + from tests.support.mock import MagicMock, patch # Import Salt Libs import salt.states.kernelpkg as kernelpkg + HAS_MODULES = True except ImportError: HAS_MODULES = False -KERNEL_LIST = ['4.4.0-70-generic', '4.4.0-71-generic', '4.5.1-14-generic'] -STATE_NAME = 'kernelpkg-test' +KERNEL_LIST = ["4.4.0-70-generic", "4.4.0-71-generic", "4.5.1-14-generic"] +STATE_NAME = "kernelpkg-test" -@skipIf(not HAS_MODULES, 'Salt modules could not be loaded') +@skipIf(not HAS_MODULES, "Salt modules could not be loaded") class KernelPkgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.aptpkg - ''' + """ def setup_loader_modules(self): return { kernelpkg: { - '__salt__': { - 'system.reboot': MagicMock(return_value=None), - 'kernelpkg.upgrade': MagicMock(return_value={ - 'upgrades': { - 'kernel': { - 'old': '1.0.0', - 'new': '2.0.0', - } + "__salt__": { + "system.reboot": MagicMock(return_value=None), + "kernelpkg.upgrade": MagicMock( + return_value={ + "upgrades": {"kernel": {"old": "1.0.0", "new": "2.0.0"}} } - }), - 'kernelpkg.active': MagicMock(return_value=0), - 'kernelpkg.latest_installed': MagicMock(return_value=0) + ), + "kernelpkg.active": MagicMock(return_value=0), + "kernelpkg.latest_installed": MagicMock(return_value=0), } } } def test_latest_installed_with_changes(self): - ''' + """ Test - latest_installed when an upgrade is available - ''' + """ installed = MagicMock(return_value=KERNEL_LIST[:-1]) upgrade = MagicMock(return_value=KERNEL_LIST[-1]) - with patch.dict(kernelpkg.__salt__, {'kernelpkg.list_installed': installed}): - with patch.dict(kernelpkg.__salt__, {'kernelpkg.latest_available': upgrade}): - with patch.dict(kernelpkg.__opts__, {'test': False}): - kernelpkg.__salt__['kernelpkg.upgrade'].reset_mock() + with patch.dict(kernelpkg.__salt__, {"kernelpkg.list_installed": installed}): + with patch.dict( + kernelpkg.__salt__, {"kernelpkg.latest_available": upgrade} + ): + with patch.dict(kernelpkg.__opts__, {"test": False}): + kernelpkg.__salt__["kernelpkg.upgrade"].reset_mock() ret = kernelpkg.latest_installed(name=STATE_NAME) - self.assertEqual(ret['name'], STATE_NAME) - self.assertTrue(ret['result']) - self.assertIsInstance(ret['changes'], dict) - self.assertIsInstance(ret['comment'], six.text_type) - self.assert_called_once(kernelpkg.__salt__['kernelpkg.upgrade']) + self.assertEqual(ret["name"], STATE_NAME) + self.assertTrue(ret["result"]) + self.assertIsInstance(ret["changes"], dict) + self.assertIsInstance(ret["comment"], six.text_type) + self.assert_called_once(kernelpkg.__salt__["kernelpkg.upgrade"]) - with patch.dict(kernelpkg.__opts__, {'test': True}): - kernelpkg.__salt__['kernelpkg.upgrade'].reset_mock() + with patch.dict(kernelpkg.__opts__, {"test": True}): + kernelpkg.__salt__["kernelpkg.upgrade"].reset_mock() ret = kernelpkg.latest_installed(name=STATE_NAME) - self.assertEqual(ret['name'], STATE_NAME) - self.assertIsNone(ret['result']) - self.assertDictEqual(ret['changes'], {}) - self.assertIsInstance(ret['comment'], six.text_type) - kernelpkg.__salt__['kernelpkg.upgrade'].assert_not_called() + self.assertEqual(ret["name"], STATE_NAME) + self.assertIsNone(ret["result"]) + self.assertDictEqual(ret["changes"], {}) + self.assertIsInstance(ret["comment"], six.text_type) + kernelpkg.__salt__["kernelpkg.upgrade"].assert_not_called() def test_latest_installed_at_latest(self): - ''' + """ Test - latest_installed when no upgrade is available - ''' + """ installed = MagicMock(return_value=KERNEL_LIST) upgrade = MagicMock(return_value=KERNEL_LIST[-1]) - with patch.dict(kernelpkg.__salt__, {'kernelpkg.list_installed': installed}): - with patch.dict(kernelpkg.__salt__, {'kernelpkg.latest_available': upgrade}): - with patch.dict(kernelpkg.__opts__, {'test': False}): + with patch.dict(kernelpkg.__salt__, {"kernelpkg.list_installed": installed}): + with patch.dict( + kernelpkg.__salt__, {"kernelpkg.latest_available": upgrade} + ): + with patch.dict(kernelpkg.__opts__, {"test": False}): ret = kernelpkg.latest_installed(name=STATE_NAME) - self.assertEqual(ret['name'], STATE_NAME) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], {}) - self.assertIsInstance(ret['comment'], six.text_type) - kernelpkg.__salt__['kernelpkg.upgrade'].assert_not_called() + self.assertEqual(ret["name"], STATE_NAME) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], {}) + self.assertIsInstance(ret["comment"], six.text_type) + kernelpkg.__salt__["kernelpkg.upgrade"].assert_not_called() - with patch.dict(kernelpkg.__opts__, {'test': True}): + with patch.dict(kernelpkg.__opts__, {"test": True}): ret = kernelpkg.latest_installed(name=STATE_NAME) - self.assertEqual(ret['name'], STATE_NAME) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], {}) - self.assertIsInstance(ret['comment'], six.text_type) - kernelpkg.__salt__['kernelpkg.upgrade'].assert_not_called() + self.assertEqual(ret["name"], STATE_NAME) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], {}) + self.assertIsInstance(ret["comment"], six.text_type) + kernelpkg.__salt__["kernelpkg.upgrade"].assert_not_called() def test_latest_active_with_changes(self): - ''' + """ Test - latest_active when a new kernel is available - ''' + """ reboot = MagicMock(return_value=True) latest = MagicMock(return_value=1) with patch.dict( - kernelpkg.__salt__, {'kernelpkg.needs_reboot': reboot, - 'kernelpkg.latest_installed': latest}), \ - patch.dict(kernelpkg.__opts__, {'test': False}): - kernelpkg.__salt__['system.reboot'].reset_mock() + kernelpkg.__salt__, + {"kernelpkg.needs_reboot": reboot, "kernelpkg.latest_installed": latest}, + ), patch.dict(kernelpkg.__opts__, {"test": False}): + kernelpkg.__salt__["system.reboot"].reset_mock() ret = kernelpkg.latest_active(name=STATE_NAME) - self.assertEqual(ret['name'], STATE_NAME) - self.assertTrue(ret['result']) - self.assertIsInstance(ret['changes'], dict) - self.assertIsInstance(ret['comment'], six.text_type) - self.assert_called_once(kernelpkg.__salt__['system.reboot']) + self.assertEqual(ret["name"], STATE_NAME) + self.assertTrue(ret["result"]) + self.assertIsInstance(ret["changes"], dict) + self.assertIsInstance(ret["comment"], six.text_type) + self.assert_called_once(kernelpkg.__salt__["system.reboot"]) - with patch.dict(kernelpkg.__opts__, {'test': True}): - kernelpkg.__salt__['system.reboot'].reset_mock() + with patch.dict(kernelpkg.__opts__, {"test": True}): + kernelpkg.__salt__["system.reboot"].reset_mock() ret = kernelpkg.latest_active(name=STATE_NAME) - self.assertEqual(ret['name'], STATE_NAME) - self.assertIsNone(ret['result']) - self.assertDictEqual( - ret['changes'], - {'kernel': {'new': 1, 'old': 0}} - ) - self.assertIsInstance(ret['comment'], six.text_type) - kernelpkg.__salt__['system.reboot'].assert_not_called() + self.assertEqual(ret["name"], STATE_NAME) + self.assertIsNone(ret["result"]) + self.assertDictEqual(ret["changes"], {"kernel": {"new": 1, "old": 0}}) + self.assertIsInstance(ret["comment"], six.text_type) + kernelpkg.__salt__["system.reboot"].assert_not_called() def test_latest_active_at_latest(self): - ''' + """ Test - latest_active when the newest kernel is already active - ''' + """ reboot = MagicMock(return_value=False) - with patch.dict(kernelpkg.__salt__, {'kernelpkg.needs_reboot': reboot}): - with patch.dict(kernelpkg.__opts__, {'test': False}): - kernelpkg.__salt__['system.reboot'].reset_mock() + with patch.dict(kernelpkg.__salt__, {"kernelpkg.needs_reboot": reboot}): + with patch.dict(kernelpkg.__opts__, {"test": False}): + kernelpkg.__salt__["system.reboot"].reset_mock() ret = kernelpkg.latest_active(name=STATE_NAME) - self.assertEqual(ret['name'], STATE_NAME) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], {}) - self.assertIsInstance(ret['comment'], six.text_type) - kernelpkg.__salt__['system.reboot'].assert_not_called() + self.assertEqual(ret["name"], STATE_NAME) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], {}) + self.assertIsInstance(ret["comment"], six.text_type) + kernelpkg.__salt__["system.reboot"].assert_not_called() - with patch.dict(kernelpkg.__opts__, {'test': True}): - kernelpkg.__salt__['system.reboot'].reset_mock() + with patch.dict(kernelpkg.__opts__, {"test": True}): + kernelpkg.__salt__["system.reboot"].reset_mock() ret = kernelpkg.latest_active(name=STATE_NAME) - self.assertEqual(ret['name'], STATE_NAME) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], {}) - self.assertIsInstance(ret['comment'], six.text_type) - kernelpkg.__salt__['system.reboot'].assert_not_called() + self.assertEqual(ret["name"], STATE_NAME) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], {}) + self.assertIsInstance(ret["comment"], six.text_type) + kernelpkg.__salt__["system.reboot"].assert_not_called() def test_latest_wait(self): - ''' + """ Test - latest_wait static results - ''' + """ ret = kernelpkg.latest_wait(name=STATE_NAME) - self.assertEqual(ret['name'], STATE_NAME) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], {}) - self.assertIsInstance(ret['comment'], six.text_type) + self.assertEqual(ret["name"], STATE_NAME) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], {}) + self.assertIsInstance(ret["comment"], six.text_type) diff --git a/tests/unit/states/test_keyboard.py b/tests/unit/states/test_keyboard.py index c015ec00d86..bc142de3dfd 100644 --- a/tests/unit/states/test_keyboard.py +++ b/tests/unit/states/test_keyboard.py @@ -1,96 +1,93 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.keyboard as keyboard +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class KeyboardTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.keyboard - ''' + """ + def setup_loader_modules(self): return {keyboard: {}} # 'system' function tests: 1 def test_system(self): - ''' + """ Test to set the keyboard layout for the system. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[name, '', '', '']) + mock = MagicMock(side_effect=[name, "", "", ""]) mock_t = MagicMock(side_effect=[True, False]) - with patch.dict(keyboard.__salt__, {'keyboard.get_sys': mock, - 'keyboard.set_sys': mock_t}): - comt = ('System layout {0} already set'.format(name)) - ret.update({'comment': comt}) + with patch.dict( + keyboard.__salt__, {"keyboard.get_sys": mock, "keyboard.set_sys": mock_t} + ): + comt = "System layout {0} already set".format(name) + ret.update({"comment": comt}) self.assertDictEqual(keyboard.system(name), ret) - with patch.dict(keyboard.__opts__, {'test': True}): - comt = ('System layout {0} needs to be set'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(keyboard.__opts__, {"test": True}): + comt = "System layout {0} needs to be set".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(keyboard.system(name), ret) - with patch.dict(keyboard.__opts__, {'test': False}): - comt = ('Set system keyboard layout {0}'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'layout': name}}) + with patch.dict(keyboard.__opts__, {"test": False}): + comt = "Set system keyboard layout {0}".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"layout": name}} + ) self.assertDictEqual(keyboard.system(name), ret) - comt = ('Failed to set system keyboard layout') - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "Failed to set system keyboard layout" + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(keyboard.system(name), ret) # 'xorg' function tests: 1 def test_xorg(self): - ''' + """ Test to set the keyboard layout for XOrg. - ''' - name = 'salt' + """ + name = "salt" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[name, '', '', '']) + mock = MagicMock(side_effect=[name, "", "", ""]) mock_t = MagicMock(side_effect=[True, False]) - with patch.dict(keyboard.__salt__, {'keyboard.get_x': mock, - 'keyboard.set_x': mock_t}): - comt = ('XOrg layout {0} already set'.format(name)) - ret.update({'comment': comt}) + with patch.dict( + keyboard.__salt__, {"keyboard.get_x": mock, "keyboard.set_x": mock_t} + ): + comt = "XOrg layout {0} already set".format(name) + ret.update({"comment": comt}) self.assertDictEqual(keyboard.xorg(name), ret) - with patch.dict(keyboard.__opts__, {'test': True}): - comt = ('XOrg layout {0} needs to be set'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(keyboard.__opts__, {"test": True}): + comt = "XOrg layout {0} needs to be set".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(keyboard.xorg(name), ret) - with patch.dict(keyboard.__opts__, {'test': False}): - comt = ('Set XOrg keyboard layout {0}'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'layout': name}}) + with patch.dict(keyboard.__opts__, {"test": False}): + comt = "Set XOrg keyboard layout {0}".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"layout": name}} + ) self.assertDictEqual(keyboard.xorg(name), ret) - comt = ('Failed to set XOrg keyboard layout') - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "Failed to set XOrg keyboard layout" + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(keyboard.xorg(name), ret) diff --git a/tests/unit/states/test_keystone.py b/tests/unit/states/test_keystone.py index 0161fa7838d..8ba54489cd5 100644 --- a/tests/unit/states/test_keystone.py +++ b/tests/unit/states/test_keystone.py @@ -1,354 +1,419 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.keystone as keystone +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class KeystoneTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.keystone - ''' + """ + def setup_loader_modules(self): return {keystone: {}} # 'user_present' function tests: 1 def test_user_present(self): - ''' + """ Test to ensure that the keystone user is present with the specified properties. - ''' - name = 'nova' - password = '$up3rn0v4' - email = 'nova@domain.com' - tenant = 'demo' + """ + name = "nova" + password = "$up3rn0v4" + email = "nova@domain.com" + tenant = "demo" - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} mock_f = MagicMock(return_value=False) - mock_lst = MagicMock(return_value=['Error']) - with patch.dict(keystone.__salt__, {'keystone.tenant_get': mock_lst}): - comt = ('Tenant / project "{0}" does not exist'.format(tenant)) - ret.update({'comment': comt}) - self.assertDictEqual(keystone.user_present(name, password, email, - tenant), ret) + mock_lst = MagicMock(return_value=["Error"]) + with patch.dict(keystone.__salt__, {"keystone.tenant_get": mock_lst}): + comt = 'Tenant / project "{0}" does not exist'.format(tenant) + ret.update({"comment": comt}) + self.assertDictEqual( + keystone.user_present(name, password, email, tenant), ret + ) - mock_dict = MagicMock(side_effect=[{name: {'email': 'a@a.com'}}, - {name: {'email': email, - 'enabled': False}}, - {name: {'email': email, - 'enabled': True}}, - {name: {'email': email, - 'enabled': True}}, - {'Error': 'error'}, - {'Error': 'error'}]) - mock_l = MagicMock(return_value={tenant: {'id': 'abc'}}) - with patch.dict(keystone.__salt__, - {'keystone.user_get': mock_dict, - 'keystone.tenant_get': mock_l, - 'keystone.user_verify_password': mock_f, - 'keystone.user_create': mock_f}): - with patch.dict(keystone.__opts__, {'test': True}): - comt = ('User "{0}" will be updated'.format(name)) - ret.update({'comment': comt, 'result': None, - 'changes': {'Email': 'Will be updated', - 'Enabled': 'Will be True', - 'Password': 'Will be updated'}}) - self.assertDictEqual(keystone.user_present(name, password, - email), ret) + mock_dict = MagicMock( + side_effect=[ + {name: {"email": "a@a.com"}}, + {name: {"email": email, "enabled": False}}, + {name: {"email": email, "enabled": True}}, + {name: {"email": email, "enabled": True}}, + {"Error": "error"}, + {"Error": "error"}, + ] + ) + mock_l = MagicMock(return_value={tenant: {"id": "abc"}}) + with patch.dict( + keystone.__salt__, + { + "keystone.user_get": mock_dict, + "keystone.tenant_get": mock_l, + "keystone.user_verify_password": mock_f, + "keystone.user_create": mock_f, + }, + ): + with patch.dict(keystone.__opts__, {"test": True}): + comt = 'User "{0}" will be updated'.format(name) + ret.update( + { + "comment": comt, + "result": None, + "changes": { + "Email": "Will be updated", + "Enabled": "Will be True", + "Password": "Will be updated", + }, + } + ) + self.assertDictEqual(keystone.user_present(name, password, email), ret) - ret.update({'comment': comt, 'result': None, - 'changes': {'Enabled': 'Will be True', - 'Password': 'Will be updated'}}) - self.assertDictEqual(keystone.user_present(name, password, - email), ret) + ret.update( + { + "comment": comt, + "result": None, + "changes": { + "Enabled": "Will be True", + "Password": "Will be updated", + }, + } + ) + self.assertDictEqual(keystone.user_present(name, password, email), ret) - ret.update({'comment': comt, 'result': None, - 'changes': {'Tenant': 'Will be added to "demo" tenant', - 'Password': 'Will be updated'}}) - self.assertDictEqual(keystone.user_present(name, password, - email, tenant), ret) + ret.update( + { + "comment": comt, + "result": None, + "changes": { + "Tenant": 'Will be added to "demo" tenant', + "Password": "Will be updated", + }, + } + ) + self.assertDictEqual( + keystone.user_present(name, password, email, tenant), ret + ) - ret.update({'comment': comt, 'result': None, - 'changes': {'Password': 'Will be updated'}}) - self.assertDictEqual(keystone.user_present(name, password, - email), ret) + ret.update( + { + "comment": comt, + "result": None, + "changes": {"Password": "Will be updated"}, + } + ) + self.assertDictEqual(keystone.user_present(name, password, email), ret) - comt = ('Keystone user "nova" will be added') - ret.update({'comment': comt, 'result': None, - 'changes': {'User': 'Will be created'}}) - self.assertDictEqual(keystone.user_present(name, password, - email), ret) + comt = 'Keystone user "nova" will be added' + ret.update( + { + "comment": comt, + "result": None, + "changes": {"User": "Will be created"}, + } + ) + self.assertDictEqual(keystone.user_present(name, password, email), ret) - with patch.dict(keystone.__opts__, {'test': False}): - comt = ('Keystone user {0} has been added'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'User': 'Created'}}) - self.assertDictEqual(keystone.user_present(name, password, - email), ret) + with patch.dict(keystone.__opts__, {"test": False}): + comt = "Keystone user {0} has been added".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"User": "Created"}} + ) + self.assertDictEqual(keystone.user_present(name, password, email), ret) # 'user_absent' function tests: 1 def test_user_absent(self): - ''' + """ Test to ensure that the keystone user is absent. - ''' - name = 'nova' + """ + name = "nova" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'User "{0}" is already absent'.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'User "{0}" is already absent'.format(name), + } - mock_lst = MagicMock(side_effect=[['Error'], []]) - with patch.dict(keystone.__salt__, {'keystone.user_get': mock_lst}): + mock_lst = MagicMock(side_effect=[["Error"], []]) + with patch.dict(keystone.__salt__, {"keystone.user_get": mock_lst}): self.assertDictEqual(keystone.user_absent(name), ret) - with patch.dict(keystone.__opts__, {'test': True}): + with patch.dict(keystone.__opts__, {"test": True}): comt = 'User "{0}" will be deleted'.format(name) - ret.update({'comment': comt, 'result': None}) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(keystone.user_absent(name), ret) # 'tenant_present' function tests: 1 def test_tenant_present(self): - ''' + """ Test to ensures that the keystone tenant exists - ''' - name = 'nova' - description = 'OpenStack Compute Service' + """ + name = "nova" + description = "OpenStack Compute Service" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Tenant / project "{0}" already exists'.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Tenant / project "{0}" already exists'.format(name), + } - mock_dict = MagicMock(side_effect=[{name: {'description': 'desc'}}, - {name: {'description': description, - 'enabled': False}}, - {'Error': 'error'}, - {'Error': 'error'}]) + mock_dict = MagicMock( + side_effect=[ + {name: {"description": "desc"}}, + {name: {"description": description, "enabled": False}}, + {"Error": "error"}, + {"Error": "error"}, + ] + ) mock_t = MagicMock(return_value=True) - with patch.dict(keystone.__salt__, {'keystone.tenant_get': mock_dict, - 'keystone.tenant_create': mock_t}): - with patch.dict(keystone.__opts__, {'test': True}): - comt = ('Tenant / project "{0}" will be updated'.format(name)) - ret.update({'comment': comt, 'result': None, - 'changes': {'Description': 'Will be updated'}}) + with patch.dict( + keystone.__salt__, + {"keystone.tenant_get": mock_dict, "keystone.tenant_create": mock_t}, + ): + with patch.dict(keystone.__opts__, {"test": True}): + comt = 'Tenant / project "{0}" will be updated'.format(name) + ret.update( + { + "comment": comt, + "result": None, + "changes": {"Description": "Will be updated"}, + } + ) self.assertDictEqual(keystone.tenant_present(name), ret) - comt = ('Tenant / project "{0}" will be updated'.format(name)) - ret.update({'comment': comt, 'result': None, - 'changes': {'Enabled': 'Will be True'}}) - self.assertDictEqual(keystone.tenant_present(name, - description), ret) + comt = 'Tenant / project "{0}" will be updated'.format(name) + ret.update( + { + "comment": comt, + "result": None, + "changes": {"Enabled": "Will be True"}, + } + ) + self.assertDictEqual(keystone.tenant_present(name, description), ret) - comt = ('Tenant / project "{0}" will be added'.format(name)) - ret.update({'comment': comt, 'result': None, - 'changes': {'Tenant': 'Will be created'}}) + comt = 'Tenant / project "{0}" will be added'.format(name) + ret.update( + { + "comment": comt, + "result": None, + "changes": {"Tenant": "Will be created"}, + } + ) self.assertDictEqual(keystone.tenant_present(name), ret) - with patch.dict(keystone.__opts__, {'test': False}): - comt = ('Tenant / project "{0}" has been added'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'Tenant': 'Created'}}) + with patch.dict(keystone.__opts__, {"test": False}): + comt = 'Tenant / project "{0}" has been added'.format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"Tenant": "Created"}} + ) self.assertDictEqual(keystone.tenant_present(name), ret) # 'tenant_absent' function tests: 1 def test_tenant_absent(self): - ''' + """ Test to ensure that the keystone tenant is absent. - ''' - name = 'nova' + """ + name = "nova" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Tenant / project "{0}" is already absent'.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Tenant / project "{0}" is already absent'.format(name), + } - mock_lst = MagicMock(side_effect=[['Error'], []]) - with patch.dict(keystone.__salt__, {'keystone.tenant_get': mock_lst}): + mock_lst = MagicMock(side_effect=[["Error"], []]) + with patch.dict(keystone.__salt__, {"keystone.tenant_get": mock_lst}): self.assertDictEqual(keystone.tenant_absent(name), ret) - with patch.dict(keystone.__opts__, {'test': True}): + with patch.dict(keystone.__opts__, {"test": True}): comt = 'Tenant / project "{0}" will be deleted'.format(name) - ret.update({'comment': comt, 'result': None}) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(keystone.tenant_absent(name), ret) # 'role_present' function tests: 1 def test_role_present(self): - ''' + """ Test to ensures that the keystone role exists - ''' - name = 'nova' + """ + name = "nova" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Role "{0}" already exists'.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Role "{0}" already exists'.format(name), + } - mock_lst = MagicMock(side_effect=[[], ['Error']]) - with patch.dict(keystone.__salt__, {'keystone.role_get': mock_lst}): + mock_lst = MagicMock(side_effect=[[], ["Error"]]) + with patch.dict(keystone.__salt__, {"keystone.role_get": mock_lst}): self.assertDictEqual(keystone.role_present(name), ret) - with patch.dict(keystone.__opts__, {'test': True}): - comt = ('Role "{0}" will be added'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(keystone.__opts__, {"test": True}): + comt = 'Role "{0}" will be added'.format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(keystone.role_present(name), ret) # 'role_absent' function tests: 1 def test_role_absent(self): - ''' + """ Test to ensure that the keystone role is absent. - ''' - name = 'nova' + """ + name = "nova" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Role "{0}" is already absent'.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Role "{0}" is already absent'.format(name), + } - mock_lst = MagicMock(side_effect=[['Error'], []]) - with patch.dict(keystone.__salt__, {'keystone.role_get': mock_lst}): + mock_lst = MagicMock(side_effect=[["Error"], []]) + with patch.dict(keystone.__salt__, {"keystone.role_get": mock_lst}): self.assertDictEqual(keystone.role_absent(name), ret) - with patch.dict(keystone.__opts__, {'test': True}): + with patch.dict(keystone.__opts__, {"test": True}): comt = 'Role "{0}" will be deleted'.format(name) - ret.update({'comment': comt, 'result': None}) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(keystone.role_absent(name), ret) # 'service_present' function tests: 1 def test_service_present(self): - ''' + """ Test to ensure service present in Keystone catalog - ''' - name = 'nova' - service_type = 'compute' + """ + name = "nova" + service_type = "compute" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Service "{0}" already exists'.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Service "{0}" already exists'.format(name), + } - mock_lst = MagicMock(side_effect=[[], ['Error']]) - with patch.dict(keystone.__salt__, {'keystone.service_get': mock_lst}): - self.assertDictEqual(keystone.service_present(name, service_type), - ret) + mock_lst = MagicMock(side_effect=[[], ["Error"]]) + with patch.dict(keystone.__salt__, {"keystone.service_get": mock_lst}): + self.assertDictEqual(keystone.service_present(name, service_type), ret) - with patch.dict(keystone.__opts__, {'test': True}): - comt = ('Service "{0}" will be added'.format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(keystone.service_present(name, - service_type), - ret) + with patch.dict(keystone.__opts__, {"test": True}): + comt = 'Service "{0}" will be added'.format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual(keystone.service_present(name, service_type), ret) # 'service_absent' function tests: 1 def test_service_absent(self): - ''' + """ Test to ensure that the service doesn't exist in Keystone catalog - ''' - name = 'nova' + """ + name = "nova" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Service "{0}" is already absent'.format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": 'Service "{0}" is already absent'.format(name), + } - mock_lst = MagicMock(side_effect=[['Error'], []]) - with patch.dict(keystone.__salt__, {'keystone.service_get': mock_lst}): + mock_lst = MagicMock(side_effect=[["Error"], []]) + with patch.dict(keystone.__salt__, {"keystone.service_get": mock_lst}): self.assertDictEqual(keystone.service_absent(name), ret) - with patch.dict(keystone.__opts__, {'test': True}): + with patch.dict(keystone.__opts__, {"test": True}): comt = 'Service "{0}" will be deleted'.format(name) - ret.update({'comment': comt, 'result': None}) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(keystone.service_absent(name), ret) # 'endpoint_present' function tests: 1 def test_endpoint_present(self): - ''' + """ Test to ensure the specified endpoints exists for service - ''' - name = 'nova' - region = 'RegionOne' + """ + name = "nova" + region = "RegionOne" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - endpoint = {'adminurl': None, - 'region': None, - 'internalurl': None, - 'publicurl': None, - 'id': 1, 'service_id': None} + endpoint = { + "adminurl": None, + "region": None, + "internalurl": None, + "publicurl": None, + "id": 1, + "service_id": None, + } - mock_lst = MagicMock(side_effect=[endpoint, ['Error'], - {'id': 1, 'service_id': None}, []]) + mock_lst = MagicMock( + side_effect=[endpoint, ["Error"], {"id": 1, "service_id": None}, []] + ) mock = MagicMock(return_value=True) - with patch.dict(keystone.__salt__, {'keystone.endpoint_get': mock_lst, - 'keystone.endpoint_create': mock}): + with patch.dict( + keystone.__salt__, + {"keystone.endpoint_get": mock_lst, "keystone.endpoint_create": mock}, + ): - comt = ('Endpoint for service "{0}" already exists'.format(name)) - ret.update({'comment': comt, 'result': True, 'changes': {}}) + comt = 'Endpoint for service "{0}" already exists'.format(name) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(keystone.endpoint_present(name), ret) - with patch.dict(keystone.__opts__, {'test': True}): - comt = ('Endpoint for service "{0}" will be added'.format(name)) - ret.update({'comment': comt, 'result': None, 'changes': {'Endpoint': 'Will be created'}}) + with patch.dict(keystone.__opts__, {"test": True}): + comt = 'Endpoint for service "{0}" will be added'.format(name) + ret.update( + { + "comment": comt, + "result": None, + "changes": {"Endpoint": "Will be created"}, + } + ) self.assertDictEqual(keystone.endpoint_present(name), ret) - comt = ('Endpoint for service "{0}" already exists'.format(name)) - ret.update({'comment': comt, 'result': True, 'changes': {}}) + comt = 'Endpoint for service "{0}" already exists'.format(name) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(keystone.endpoint_present(name), ret) - with patch.dict(keystone.__opts__, {'test': False}): - comt = ('Endpoint for service "{0}" has been added'.format(name)) - ret.update({'comment': comt, 'result': True, 'changes': True}) + with patch.dict(keystone.__opts__, {"test": False}): + comt = 'Endpoint for service "{0}" has been added'.format(name) + ret.update({"comment": comt, "result": True, "changes": True}) self.assertDictEqual(keystone.endpoint_present(name), ret) # 'endpoint_absent' function tests: 1 def test_endpoint_absent(self): - ''' + """ Test to ensure that the endpoint for a service doesn't exist in Keystone catalog - ''' - name = 'nova' - region = 'RegionOne' - comment = ('Endpoint for service "{0}" is already absent'.format(name)) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': comment} + """ + name = "nova" + region = "RegionOne" + comment = 'Endpoint for service "{0}" is already absent'.format(name) + ret = {"name": name, "changes": {}, "result": True, "comment": comment} - mock_lst = MagicMock(side_effect=[[], ['Error']]) - with patch.dict(keystone.__salt__, {'keystone.endpoint_get': mock_lst}): + mock_lst = MagicMock(side_effect=[[], ["Error"]]) + with patch.dict(keystone.__salt__, {"keystone.endpoint_get": mock_lst}): self.assertDictEqual(keystone.endpoint_absent(name, region), ret) - with patch.dict(keystone.__opts__, {'test': True}): - comt = ('Endpoint for service "{0}" will be deleted' - .format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(keystone.__opts__, {"test": True}): + comt = 'Endpoint for service "{0}" will be deleted'.format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(keystone.endpoint_absent(name, region), ret) diff --git a/tests/unit/states/test_keystore.py b/tests/unit/states/test_keystore.py index 34d35f08bb2..2fbfc73bcdf 100644 --- a/tests/unit/states/test_keystore.py +++ b/tests/unit/states/test_keystore.py @@ -1,40 +1,43 @@ # -*- coding: utf-8 -*- -''' +""" Test cases for keystore state -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - # Import Salt Libs import salt.states.keystore as keystore +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class KeystoreTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.keystore - ''' + """ + def setup_loader_modules(self): - return {keystore: {'__opts__': {'test': False}}} + return {keystore: {"__opts__": {"test": False}}} - @patch('os.path.exists', MagicMock(return_value=True)) + @patch("os.path.exists", MagicMock(return_value=True)) def test_cert_already_present(self): - ''' + """ Test for existing value_present - ''' + """ - cert_return = [{ - "valid_until": "August 21 2017", - "sha1": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", - "valid_start": "August 22 2012", - "type": "TrustedCertEntry", - "alias": "stringhost", - "expired": True - }] + cert_return = [ + { + "valid_until": "August 21 2017", + "sha1": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", + "valid_start": "August 22 2012", + "type": "TrustedCertEntry", + "alias": "stringhost", + "expired": True, + } + ] x509_return = { "Not After": "2017-08-21 05:26:54", "Subject Hash": "97:95:14:4F", @@ -52,7 +55,7 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): "SP": "Tokyo", "L": "Chuo-ku", "emailAddress": "support@frank4dd.com", - "OU": "WebCert Support" + "OU": "WebCert Support", }, "Issuer Hash": "92:DA:45:6B", "Not Before": "2012-08-22 05:26:54", @@ -60,14 +63,16 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): "C": "JP", "SP": "Tokyo", "organizationName": "Frank4DD", - "CN": "www.example.com" - } + "CN": "www.example.com", + }, } - name = 'keystore.jks' + name = "keystore.jks" passphrase = "changeit" - entries = [{'alias': 'stringhost', - 'certificate': '''-----BEGIN CERTIFICATE----- + entries = [ + { + "alias": "stringhost", + "certificate": """-----BEGIN CERTIFICATE----- MIICEjCCAXsCAg36MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl @@ -79,40 +84,58 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): AQABMA0GCSqGSIb3DQEBBQUAA4GBABS2TLuBeTPmcaTaUW/LCB2NYOy8GMdzR1mx 8iBIu2H6/E2tiY3RIevV2OW61qY2/XRQg7YPxx3ffeUugX9F4J/iPnnu1zAxxyBy 2VguKv4SWjRFoRkIfIlHX0qVviMhSlNy2ioFLy7JcPZb+v3ftDGywUqcBiVDoea0 - Hn+GmxZA\n-----END CERTIFICATE-----'''} - ] + Hn+GmxZA\n-----END CERTIFICATE-----""", + } + ] state_return = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'No changes made.\n' - } + "name": name, + "changes": {}, + "result": True, + "comment": "No changes made.\n", + } - #with patch.dict(keystore.__opts__, {'test': False}): - with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): - with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): - self.assertDictEqual(keystore.managed(name, passphrase, entries), state_return) + # with patch.dict(keystore.__opts__, {'test': False}): + with patch.dict( + keystore.__salt__, {"keystore.list": MagicMock(return_value=cert_return)} + ): + with patch.dict( + keystore.__salt__, + {"x509.read_certificate": MagicMock(return_value=x509_return)}, + ): + self.assertDictEqual( + keystore.managed(name, passphrase, entries), state_return + ) - with patch.dict(keystore.__opts__, {'test': True}): - with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): - with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): - self.assertDictEqual(keystore.managed(name, passphrase, entries), state_return) + with patch.dict(keystore.__opts__, {"test": True}): + with patch.dict( + keystore.__salt__, + {"keystore.list": MagicMock(return_value=cert_return)}, + ): + with patch.dict( + keystore.__salt__, + {"x509.read_certificate": MagicMock(return_value=x509_return)}, + ): + self.assertDictEqual( + keystore.managed(name, passphrase, entries), state_return + ) - @patch('os.path.exists', MagicMock(return_value=True)) + @patch("os.path.exists", MagicMock(return_value=True)) def test_cert_update(self): - ''' + """ Test for existing value_present - ''' + """ - cert_return = [{ - "valid_until": "August 21 2017", - "sha1": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", - "valid_start": "August 22 2012", - "type": "TrustedCertEntry", - "alias": "stringhost", - "expired": True - }] + cert_return = [ + { + "valid_until": "August 21 2017", + "sha1": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", + "valid_start": "August 22 2012", + "type": "TrustedCertEntry", + "alias": "stringhost", + "expired": True, + } + ] x509_return = { "Not After": "2017-08-21 05:26:54", "Subject Hash": "97:95:14:4F", @@ -130,7 +153,7 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): "SP": "Tokyo", "L": "Chuo-ku", "emailAddress": "support@frank4dd.com", - "OU": "WebCert Support" + "OU": "WebCert Support", }, "Issuer Hash": "92:DA:45:6B", "Not Before": "2012-08-22 05:26:54", @@ -138,14 +161,16 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): "C": "JP", "SP": "Tokyo", "organizationName": "Frank4DD", - "CN": "www.example.com" - } + "CN": "www.example.com", + }, } - name = 'keystore.jks' + name = "keystore.jks" passphrase = "changeit" - entries = [{'alias': 'stringhost', - 'certificate': '''-----BEGIN CERTIFICATE----- + entries = [ + { + "alias": "stringhost", + "certificate": """-----BEGIN CERTIFICATE----- MIICEjCCAXsCAg36MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl @@ -157,42 +182,65 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): AQABMA0GCSqGSIb3DQEBBQUAA4GBABS2TLuBeTPmcaTaUW/LCB2NYOy8GMdzR1mx 8iBIu2H6/E2tiY3RIevV2OW61qY2/XRQg7YPxx3ffeUugX9F4J/iPnnu1zAxxyBy 2VguKv4SWjRFoRkIfIlHX0qVviMhSlNy2ioFLy7JcPZb+v3ftDGywUqcBiVDoea0 - Hn+GmxZA\n-----END CERTIFICATE-----'''} - ] + Hn+GmxZA\n-----END CERTIFICATE-----""", + } + ] test_return = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': 'Alias stringhost would have been updated\n' - } + "name": name, + "changes": {}, + "result": None, + "comment": "Alias stringhost would have been updated\n", + } state_return = { - 'name': name, - 'changes': {'stringhost': 'Updated'}, - 'result': True, - 'comment': 'Alias stringhost updated.\n' - } + "name": name, + "changes": {"stringhost": "Updated"}, + "result": True, + "comment": "Alias stringhost updated.\n", + } - with patch.dict(keystore.__opts__, {'test': True}): - with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): - with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): - self.assertDictEqual(keystore.managed(name, passphrase, entries), test_return) + with patch.dict(keystore.__opts__, {"test": True}): + with patch.dict( + keystore.__salt__, + {"keystore.list": MagicMock(return_value=cert_return)}, + ): + with patch.dict( + keystore.__salt__, + {"x509.read_certificate": MagicMock(return_value=x509_return)}, + ): + self.assertDictEqual( + keystore.managed(name, passphrase, entries), test_return + ) - with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): - with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): - with patch.dict(keystore.__salt__, {'keystore.remove': MagicMock(return_value=True)}): - with patch.dict(keystore.__salt__, {'keystore.add': MagicMock(return_value=True)}): - self.assertDictEqual(keystore.managed(name, passphrase, entries), state_return) + with patch.dict( + keystore.__salt__, {"keystore.list": MagicMock(return_value=cert_return)} + ): + with patch.dict( + keystore.__salt__, + {"x509.read_certificate": MagicMock(return_value=x509_return)}, + ): + with patch.dict( + keystore.__salt__, {"keystore.remove": MagicMock(return_value=True)} + ): + with patch.dict( + keystore.__salt__, + {"keystore.add": MagicMock(return_value=True)}, + ): + self.assertDictEqual( + keystore.managed(name, passphrase, entries), state_return + ) - @patch('os.path.exists', MagicMock(return_value=False)) + @patch("os.path.exists", MagicMock(return_value=False)) def test_new_file(self): - ''' + """ Test for existing value_present - ''' - name = 'keystore.jks' + """ + name = "keystore.jks" passphrase = "changeit" - entries = [{'alias': 'stringhost', - 'certificate': '''-----BEGIN CERTIFICATE----- + entries = [ + { + "alias": "stringhost", + "certificate": """-----BEGIN CERTIFICATE----- MIICEjCCAXsCAg36MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl @@ -204,43 +252,54 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): AQABMA0GCSqGSIb3DQEBBQUAA4GBABS2TLuBeTPmcaTaUW/LCB2NYOy8GMdzR1mx 8iBIu2H6/E2tiY3RIevV2OW61qY2/XRQg7YPxx3ffeUugX9F4J/iPnnu1zAxxyBy 2VguKv4SWjRFoRkIfIlHX0qVviMhSlNy2ioFLy7JcPZb+v3ftDGywUqcBiVDoea0 - Hn+GmxZA\n-----END CERTIFICATE-----'''} - ] + Hn+GmxZA\n-----END CERTIFICATE-----""", + } + ] test_return = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': 'Alias stringhost would have been added\n' - } + "name": name, + "changes": {}, + "result": None, + "comment": "Alias stringhost would have been added\n", + } state_return = { - 'name': name, - 'changes': {'stringhost': 'Added'}, - 'result': True, - 'comment': 'Alias stringhost added.\n' - } + "name": name, + "changes": {"stringhost": "Added"}, + "result": True, + "comment": "Alias stringhost added.\n", + } - with patch.dict(keystore.__opts__, {'test': True}): - self.assertDictEqual(keystore.managed(name, passphrase, entries), test_return) + with patch.dict(keystore.__opts__, {"test": True}): + self.assertDictEqual( + keystore.managed(name, passphrase, entries), test_return + ) - with patch.dict(keystore.__salt__, {'keystore.remove': MagicMock(return_value=True)}): - with patch.dict(keystore.__salt__, {'keystore.add': MagicMock(return_value=True)}): - self.assertDictEqual(keystore.managed(name, passphrase, entries), state_return) + with patch.dict( + keystore.__salt__, {"keystore.remove": MagicMock(return_value=True)} + ): + with patch.dict( + keystore.__salt__, {"keystore.add": MagicMock(return_value=True)} + ): + self.assertDictEqual( + keystore.managed(name, passphrase, entries), state_return + ) - @patch('os.path.exists', MagicMock(return_value=True)) + @patch("os.path.exists", MagicMock(return_value=True)) def test_force_remove(self): - ''' + """ Test for existing value_present - ''' + """ - cert_return = [{ - "valid_until": "August 21 2017", - "sha1": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", - "valid_start": "August 22 2012", - "type": "TrustedCertEntry", - "alias": "oldhost", - "expired": True - }] + cert_return = [ + { + "valid_until": "August 21 2017", + "sha1": "07:1C:B9:4F:0C:C8:51:4D:02:41:24:70:8E:E8:B2:68:7B:D7:D9:D5", + "valid_start": "August 22 2012", + "type": "TrustedCertEntry", + "alias": "oldhost", + "expired": True, + } + ] x509_return = { "Not After": "2017-08-21 05:26:54", "Subject Hash": "97:95:14:4F", @@ -258,7 +317,7 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): "SP": "Tokyo", "L": "Chuo-ku", "emailAddress": "support@frank4dd.com", - "OU": "WebCert Support" + "OU": "WebCert Support", }, "Issuer Hash": "92:DA:45:6B", "Not Before": "2012-08-22 05:26:54", @@ -266,14 +325,16 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): "C": "JP", "SP": "Tokyo", "organizationName": "Frank4DD", - "CN": "www.example.com" - } + "CN": "www.example.com", + }, } - name = 'keystore.jks' + name = "keystore.jks" passphrase = "changeit" - entries = [{'alias': 'stringhost', - 'certificate': '''-----BEGIN CERTIFICATE----- + entries = [ + { + "alias": "stringhost", + "certificate": """-----BEGIN CERTIFICATE----- MIICEjCCAXsCAg36MA0GCSqGSIb3DQEBBQUAMIGbMQswCQYDVQQGEwJKUDEOMAwG A1UECBMFVG9reW8xEDAOBgNVBAcTB0NodW8ta3UxETAPBgNVBAoTCEZyYW5rNERE MRgwFgYDVQQLEw9XZWJDZXJ0IFN1cHBvcnQxGDAWBgNVBAMTD0ZyYW5rNEREIFdl @@ -285,29 +346,54 @@ class KeystoreTestCase(TestCase, LoaderModuleMockMixin): AQABMA0GCSqGSIb3DQEBBQUAA4GBABS2TLuBeTPmcaTaUW/LCB2NYOy8GMdzR1mx 8iBIu2H6/E2tiY3RIevV2OW61qY2/XRQg7YPxx3ffeUugX9F4J/iPnnu1zAxxyBy 2VguKv4SWjRFoRkIfIlHX0qVviMhSlNy2ioFLy7JcPZb+v3ftDGywUqcBiVDoea0 - Hn+GmxZA\n-----END CERTIFICATE-----'''} - ] + Hn+GmxZA\n-----END CERTIFICATE-----""", + } + ] test_return = { - 'name': name, - 'changes': {}, - 'result': None, - 'comment': 'Alias stringhost would have been updated\nAlias oldhost would have been removed' - } + "name": name, + "changes": {}, + "result": None, + "comment": "Alias stringhost would have been updated\nAlias oldhost would have been removed", + } state_return = { - 'name': name, - 'changes': {'oldhost': 'Removed', 'stringhost': 'Updated'}, - 'result': True, - 'comment': 'Alias stringhost updated.\nAlias oldhost removed.\n' - } + "name": name, + "changes": {"oldhost": "Removed", "stringhost": "Updated"}, + "result": True, + "comment": "Alias stringhost updated.\nAlias oldhost removed.\n", + } - with patch.dict(keystore.__opts__, {'test': True}): - with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): - with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): - self.assertDictEqual(keystore.managed(name, passphrase, entries, force_remove=True), test_return) + with patch.dict(keystore.__opts__, {"test": True}): + with patch.dict( + keystore.__salt__, + {"keystore.list": MagicMock(return_value=cert_return)}, + ): + with patch.dict( + keystore.__salt__, + {"x509.read_certificate": MagicMock(return_value=x509_return)}, + ): + self.assertDictEqual( + keystore.managed(name, passphrase, entries, force_remove=True), + test_return, + ) - with patch.dict(keystore.__salt__, {'keystore.list': MagicMock(return_value=cert_return)}): - with patch.dict(keystore.__salt__, {'x509.read_certificate': MagicMock(return_value=x509_return)}): - with patch.dict(keystore.__salt__, {'keystore.remove': MagicMock(return_value=True)}): - with patch.dict(keystore.__salt__, {'keystore.add': MagicMock(return_value=True)}): - self.assertDictEqual(keystore.managed(name, passphrase, entries, force_remove=True), state_return) + with patch.dict( + keystore.__salt__, {"keystore.list": MagicMock(return_value=cert_return)} + ): + with patch.dict( + keystore.__salt__, + {"x509.read_certificate": MagicMock(return_value=x509_return)}, + ): + with patch.dict( + keystore.__salt__, {"keystore.remove": MagicMock(return_value=True)} + ): + with patch.dict( + keystore.__salt__, + {"keystore.add": MagicMock(return_value=True)}, + ): + self.assertDictEqual( + keystore.managed( + name, passphrase, entries, force_remove=True + ), + state_return, + ) diff --git a/tests/unit/states/test_kmod.py b/tests/unit/states/test_kmod.py index a820d0cdfa3..1417abdf036 100644 --- a/tests/unit/states/test_kmod.py +++ b/tests/unit/states/test_kmod.py @@ -1,100 +1,99 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.kmod as kmod +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class KmodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.kmod - ''' + """ + def setup_loader_modules(self): return {kmod: {}} # 'present' function tests: 2 def test_present(self): - ''' + """ Test to ensure that the specified kernel module is loaded. - ''' - name = 'cheese' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + name = "cheese" + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock_mod_list = MagicMock(return_value=[name]) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list}): - comment = 'Kernel module {0} is already present'.format(name) - ret.update({'comment': comment}) + with patch.dict(kmod.__salt__, {"kmod.mod_list": mock_mod_list}): + comment = "Kernel module {0} is already present".format(name) + ret.update({"comment": comment}) self.assertDictEqual(kmod.present(name), ret) mock_mod_list = MagicMock(return_value=[]) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list}): - with patch.dict(kmod.__opts__, {'test': True}): - comment = 'Kernel module {0} is set to be loaded'.format(name) - ret.update({'comment': comment, 'result': None}) + with patch.dict(kmod.__salt__, {"kmod.mod_list": mock_mod_list}): + with patch.dict(kmod.__opts__, {"test": True}): + comment = "Kernel module {0} is set to be loaded".format(name) + ret.update({"comment": comment, "result": None}) self.assertDictEqual(kmod.present(name), ret) mock_mod_list = MagicMock(return_value=[]) mock_available = MagicMock(return_value=[name]) mock_load = MagicMock(return_value=[name]) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list, - 'kmod.available': mock_available, - 'kmod.load': mock_load}): - with patch.dict(kmod.__opts__, {'test': False}): - comment = 'Loaded kernel module {0}'.format(name) - ret.update({'comment': comment, - 'result': True, - 'changes': {name: 'loaded'}}) + with patch.dict( + kmod.__salt__, + { + "kmod.mod_list": mock_mod_list, + "kmod.available": mock_available, + "kmod.load": mock_load, + }, + ): + with patch.dict(kmod.__opts__, {"test": False}): + comment = "Loaded kernel module {0}".format(name) + ret.update( + {"comment": comment, "result": True, "changes": {name: "loaded"}} + ) self.assertDictEqual(kmod.present(name), ret) def test_present_multi(self): - ''' + """ Test to ensure that multiple kernel modules are loaded. - ''' - name = 'salted kernel' - mods = ['cheese', 'crackers'] - ret = {'name': name, - 'result': True, - 'changes': {}} + """ + name = "salted kernel" + mods = ["cheese", "crackers"] + ret = {"name": name, "result": True, "changes": {}} mock_mod_list = MagicMock(return_value=mods) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list}): + with patch.dict(kmod.__salt__, {"kmod.mod_list": mock_mod_list}): call_ret = kmod.present(name, mods=mods) # Check comment independently: makes test more stable on PY3 - comment = call_ret.pop('comment') - self.assertIn('cheese', comment) - self.assertIn('crackers', comment) - self.assertIn('are already present', comment) + comment = call_ret.pop("comment") + self.assertIn("cheese", comment) + self.assertIn("crackers", comment) + self.assertIn("are already present", comment) # Assert against all other dictionary key/values self.assertDictEqual(ret, call_ret) mock_mod_list = MagicMock(return_value=[]) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list}): - with patch.dict(kmod.__opts__, {'test': True}): + with patch.dict(kmod.__salt__, {"kmod.mod_list": mock_mod_list}): + with patch.dict(kmod.__opts__, {"test": True}): call_ret = kmod.present(name, mods=mods) - ret.update({'result': None}) + ret.update({"result": None}) # Check comment independently: makes test more stable on PY3 - comment = call_ret.pop('comment') - self.assertIn('cheese', comment) - self.assertIn('crackers', comment) - self.assertIn('are set to be loaded', comment) + comment = call_ret.pop("comment") + self.assertIn("cheese", comment) + self.assertIn("crackers", comment) + self.assertIn("are set to be loaded", comment) # Assert against all other dictionary key/values self.assertDictEqual(ret, call_ret) @@ -102,20 +101,25 @@ class KmodTestCase(TestCase, LoaderModuleMockMixin): mock_mod_list = MagicMock(return_value=[]) mock_available = MagicMock(return_value=mods) mock_load = MagicMock(return_value=mods) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list, - 'kmod.available': mock_available, - 'kmod.load': mock_load}): - with patch.dict(kmod.__opts__, {'test': False}): + with patch.dict( + kmod.__salt__, + { + "kmod.mod_list": mock_mod_list, + "kmod.available": mock_available, + "kmod.load": mock_load, + }, + ): + with patch.dict(kmod.__opts__, {"test": False}): call_ret = kmod.present(name, mods=mods) - ret.update({'result': True, - 'changes': {mods[0]: 'loaded', - mods[1]: 'loaded'}}) + ret.update( + {"result": True, "changes": {mods[0]: "loaded", mods[1]: "loaded"}} + ) # Check comment independently: makes test more stable on PY3 - comment = call_ret.pop('comment') - self.assertIn('cheese', comment) - self.assertIn('crackers', comment) - self.assertIn('Loaded kernel modules', comment) + comment = call_ret.pop("comment") + self.assertIn("cheese", comment) + self.assertIn("crackers", comment) + self.assertIn("Loaded kernel modules", comment) # Assert against all other dictionary key/values self.assertDictEqual(ret, call_ret) @@ -123,91 +127,89 @@ class KmodTestCase(TestCase, LoaderModuleMockMixin): # 'absent' function tests: 2 def test_absent(self): - ''' + """ Test to verify that the named kernel module is not loaded. - ''' - name = 'cheese' - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + """ + name = "cheese" + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock_mod_list = MagicMock(return_value=[name]) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list}): - with patch.dict(kmod.__opts__, {'test': True}): - comment = 'Kernel module {0} is set to be removed'.format(name) - ret.update({'comment': comment, 'result': None}) + with patch.dict(kmod.__salt__, {"kmod.mod_list": mock_mod_list}): + with patch.dict(kmod.__opts__, {"test": True}): + comment = "Kernel module {0} is set to be removed".format(name) + ret.update({"comment": comment, "result": None}) self.assertDictEqual(kmod.absent(name), ret) mock_mod_list = MagicMock(return_value=[name]) mock_remove = MagicMock(return_value=[name]) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list, - 'kmod.remove': mock_remove}): - with patch.dict(kmod.__opts__, {'test': False}): - comment = 'Removed kernel module {0}'.format(name) - ret.update({'comment': comment, - 'result': True, - 'changes': {name: 'removed'}}) + with patch.dict( + kmod.__salt__, {"kmod.mod_list": mock_mod_list, "kmod.remove": mock_remove} + ): + with patch.dict(kmod.__opts__, {"test": False}): + comment = "Removed kernel module {0}".format(name) + ret.update( + {"comment": comment, "result": True, "changes": {name: "removed"}} + ) self.assertDictEqual(kmod.absent(name), ret) mock_mod_list = MagicMock(return_value=[]) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list}): - with patch.dict(kmod.__opts__, {'test': True}): - comment = 'Kernel module {0} is already removed'.format(name) - ret.update({'comment': comment, - 'result': True, - 'changes': {}}) + with patch.dict(kmod.__salt__, {"kmod.mod_list": mock_mod_list}): + with patch.dict(kmod.__opts__, {"test": True}): + comment = "Kernel module {0} is already removed".format(name) + ret.update({"comment": comment, "result": True, "changes": {}}) self.assertDictEqual(kmod.absent(name), ret) def test_absent_multi(self): - ''' + """ Test to verify that multiple kernel modules are not loaded. - ''' - name = 'salted kernel' - mods = ['cheese', 'crackers'] - ret = {'name': name, - 'result': True, - 'changes': {}} + """ + name = "salted kernel" + mods = ["cheese", "crackers"] + ret = {"name": name, "result": True, "changes": {}} mock_mod_list = MagicMock(return_value=mods) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list}): - with patch.dict(kmod.__opts__, {'test': True}): - ret.update({'result': None}) + with patch.dict(kmod.__salt__, {"kmod.mod_list": mock_mod_list}): + with patch.dict(kmod.__opts__, {"test": True}): + ret.update({"result": None}) call_ret = kmod.absent(name, mods=mods) # Check comment independently: makes test more stable on PY3 - comment = call_ret.pop('comment') - self.assertIn('cheese', comment) - self.assertIn('crackers', comment) - self.assertIn('are set to be removed', comment) + comment = call_ret.pop("comment") + self.assertIn("cheese", comment) + self.assertIn("crackers", comment) + self.assertIn("are set to be removed", comment) # Assert against all other dictionary key/values self.assertDictEqual(ret, call_ret) mock_mod_list = MagicMock(return_value=mods) mock_remove = MagicMock(return_value=mods) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list, - 'kmod.remove': mock_remove}): - with patch.dict(kmod.__opts__, {'test': False}): + with patch.dict( + kmod.__salt__, {"kmod.mod_list": mock_mod_list, "kmod.remove": mock_remove} + ): + with patch.dict(kmod.__opts__, {"test": False}): call_ret = kmod.absent(name, mods=mods) - ret.update({'result': True, - 'changes': {mods[0]: 'removed', - mods[1]: 'removed'}}) + ret.update( + { + "result": True, + "changes": {mods[0]: "removed", mods[1]: "removed"}, + } + ) # Check comment independently: makes test more stable on PY3 - comment = call_ret.pop('comment') - self.assertIn('cheese', comment) - self.assertIn('crackers', comment) - self.assertIn('Removed kernel modules', comment) + comment = call_ret.pop("comment") + self.assertIn("cheese", comment) + self.assertIn("crackers", comment) + self.assertIn("Removed kernel modules", comment) # Assert against all other dictionary key/values self.assertDictEqual(ret, call_ret) mock_mod_list = MagicMock(return_value=[]) - with patch.dict(kmod.__salt__, {'kmod.mod_list': mock_mod_list}): - with patch.dict(kmod.__opts__, {'test': True}): - comment = 'Kernel modules {0} are already removed'.format(', '.join(mods)) - ret.update({'comment': comment, - 'result': True, - 'changes': {}}) + with patch.dict(kmod.__salt__, {"kmod.mod_list": mock_mod_list}): + with patch.dict(kmod.__opts__, {"test": True}): + comment = "Kernel modules {0} are already removed".format( + ", ".join(mods) + ) + ret.update({"comment": comment, "result": True, "changes": {}}) self.assertDictEqual(kmod.absent(name, mods=mods), ret) diff --git a/tests/unit/states/test_kubernetes.py b/tests/unit/states/test_kubernetes.py index bcb40f0a7d9..fec2782d94b 100644 --- a/tests/unit/states/test_kubernetes.py +++ b/tests/unit/states/test_kubernetes.py @@ -1,542 +1,499 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Jeff Schroeder <jeffschroeder@computer.org>` -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import base64 - from contextlib import contextmanager - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.utils.stringutils -from salt.states import kubernetes from salt.ext import six +from salt.states import kubernetes + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -@skipIf(kubernetes is False, "Probably Kubernetes client lib is not installed. \ - Skipping test_kubernetes.py") +@skipIf( + kubernetes is False, + "Probably Kubernetes client lib is not installed. \ + Skipping test_kubernetes.py", +) class KubernetesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.kubernetes - ''' + """ + def setup_loader_modules(self): - return {kubernetes: {'__env__': 'base'}} + return {kubernetes: {"__env__": "base"}} @contextmanager def mock_func(self, func_name, return_value, test=False): - ''' + """ Mock any of the kubernetes state function return values and set the test options. - ''' - name = 'kubernetes.{0}'.format(func_name) + """ + name = "kubernetes.{0}".format(func_name) mocked = {name: MagicMock(return_value=return_value)} with patch.dict(kubernetes.__salt__, mocked) as patched: - with patch.dict(kubernetes.__opts__, {'test': test}): + with patch.dict(kubernetes.__opts__, {"test": test}): yield patched - def make_configmap(self, name, namespace='default', data=None): + def make_configmap(self, name, namespace="default", data=None): return self.make_ret_dict( - kind='ConfigMap', - name=name, - namespace=namespace, - data=data, + kind="ConfigMap", name=name, namespace=namespace, data=data, ) - def make_secret(self, name, namespace='default', data=None): + def make_secret(self, name, namespace="default", data=None): secret_data = self.make_ret_dict( - kind='Secret', - name=name, - namespace=namespace, - data=data, + kind="Secret", name=name, namespace=namespace, data=data, ) # Base64 all of the values just like kubectl does - for key, value in six.iteritems(secret_data['data']): - secret_data['data'][key] = base64.b64encode( + for key, value in six.iteritems(secret_data["data"]): + secret_data["data"][key] = base64.b64encode( salt.utils.stringutils.to_bytes(value) ) return secret_data - def make_node_labels(self, name='minikube'): + def make_node_labels(self, name="minikube"): return { - 'kubernetes.io/hostname': name, - 'beta.kubernetes.io/os': 'linux', - 'beta.kubernetes.io/arch': 'amd64', - 'failure-domain.beta.kubernetes.io/region': 'us-west-1', + "kubernetes.io/hostname": name, + "beta.kubernetes.io/os": "linux", + "beta.kubernetes.io/arch": "amd64", + "failure-domain.beta.kubernetes.io/region": "us-west-1", } - def make_node(self, name='minikube'): - node_data = self.make_ret_dict(kind='Node', name='minikube') - node_data.update({ - 'api_version': 'v1', - 'kind': 'Node', - 'metadata': { - 'annotations': { - 'node.alpha.kubernetes.io/ttl': '0', + def make_node(self, name="minikube"): + node_data = self.make_ret_dict(kind="Node", name="minikube") + node_data.update( + { + "api_version": "v1", + "kind": "Node", + "metadata": { + "annotations": {"node.alpha.kubernetes.io/ttl": "0"}, + "labels": self.make_node_labels(name=name), + "name": name, + "namespace": None, + "self_link": "/api/v1/nodes/{name}".format(name=name), + "uid": "7811b8ae-c1a1-11e7-a55a-0800279fb61e", }, - 'labels': self.make_node_labels(name=name), - 'name': name, - 'namespace': None, - 'self_link': '/api/v1/nodes/{name}'.format(name=name), - 'uid': '7811b8ae-c1a1-11e7-a55a-0800279fb61e', - }, - 'spec': { - 'external_id': name, - }, - 'status': {}, - }) + "spec": {"external_id": name}, + "status": {}, + } + ) return node_data - def make_namespace(self, name='default'): - namespace_data = self.make_ret_dict(kind='Namespace', name=name) - del namespace_data['data'] - namespace_data.update({ - 'status': { - 'phase': 'Active', - }, - 'spec': { - 'finalizers': ['kubernetes'], - }, - 'metadata': { - 'name': name, - 'namespace': None, - 'labels': None, - 'self_link': '/api/v1/namespaces/{namespace}'.format( - namespace=name, - ), - 'annotations': None, - 'uid': '752fceeb-c1a1-11e7-a55a-0800279fb61e', - }, - }) + def make_namespace(self, name="default"): + namespace_data = self.make_ret_dict(kind="Namespace", name=name) + del namespace_data["data"] + namespace_data.update( + { + "status": {"phase": "Active"}, + "spec": {"finalizers": ["kubernetes"]}, + "metadata": { + "name": name, + "namespace": None, + "labels": None, + "self_link": "/api/v1/namespaces/{namespace}".format( + namespace=name, + ), + "annotations": None, + "uid": "752fceeb-c1a1-11e7-a55a-0800279fb61e", + }, + } + ) return namespace_data def make_ret_dict(self, kind, name, namespace=None, data=None): - ''' + """ Make a minimal example configmap or secret for using in mocks - ''' + """ - assert kind in ('Secret', 'ConfigMap', 'Namespace', 'Node') + assert kind in ("Secret", "ConfigMap", "Namespace", "Node") if data is None: data = {} - self_link = '/api/v1/namespaces/{namespace}/{kind}s/{name}'.format( - namespace=namespace, - kind=kind.lower(), - name=name, + self_link = "/api/v1/namespaces/{namespace}/{kind}s/{name}".format( + namespace=namespace, kind=kind.lower(), name=name, ) return_data = { - 'kind': kind, - 'data': data, - 'api_version': 'v1', - 'metadata': { - 'name': name, - 'labels': None, - 'namespace': namespace, - 'self_link': self_link, - 'annotations': { - 'kubernetes.io/change-cause': 'salt-call state.apply', - }, + "kind": kind, + "data": data, + "api_version": "v1", + "metadata": { + "name": name, + "labels": None, + "namespace": namespace, + "self_link": self_link, + "annotations": {"kubernetes.io/change-cause": "salt-call state.apply"}, }, } return return_data def test_configmap_present__fail(self): error = kubernetes.configmap_present( - name='testme', - data={1: 1}, - source='salt://beyond/oblivion.jinja', + name="testme", data={1: 1}, source="salt://beyond/oblivion.jinja", ) self.assertDictEqual( { - 'changes': {}, - 'result': False, - 'name': 'testme', - 'comment': "'source' cannot be used in combination with 'data'", + "changes": {}, + "result": False, + "name": "testme", + "comment": "'source' cannot be used in combination with 'data'", }, error, ) def test_configmap_present__create_test_true(self): # Create a new configmap with test=True - with self.mock_func('show_configmap', return_value=None, test=True): + with self.mock_func("show_configmap", return_value=None, test=True): ret = kubernetes.configmap_present( - name='example', - data={'example.conf': '# empty config file'}, + name="example", data={"example.conf": "# empty config file"}, ) self.assertDictEqual( { - 'comment': 'The configmap is going to be created', - 'changes': {}, - 'name': 'example', - 'result': None, + "comment": "The configmap is going to be created", + "changes": {}, + "name": "example", + "result": None, }, ret, ) def test_configmap_present__create(self): # Create a new configmap - with self.mock_func('show_configmap', return_value=None): + with self.mock_func("show_configmap", return_value=None): cm = self.make_configmap( - name='test', - namespace='default', - data={'foo': 'bar'}, + name="test", namespace="default", data={"foo": "bar"}, ) - with self.mock_func('create_configmap', return_value=cm): - actual = kubernetes.configmap_present( - name='test', - data={'foo': 'bar'}, - ) + with self.mock_func("create_configmap", return_value=cm): + actual = kubernetes.configmap_present(name="test", data={"foo": "bar"},) self.assertDictEqual( { - 'comment': '', - 'changes': {'data': {'foo': 'bar'}}, - 'name': 'test', - 'result': True, + "comment": "", + "changes": {"data": {"foo": "bar"}}, + "name": "test", + "result": True, }, actual, ) def test_configmap_present__create_no_data(self): # Create a new configmap with no 'data' attribute - with self.mock_func('show_configmap', return_value=None): - cm = self.make_configmap( - name='test', - namespace='default', - ) - with self.mock_func('create_configmap', return_value=cm): - actual = kubernetes.configmap_present(name='test') + with self.mock_func("show_configmap", return_value=None): + cm = self.make_configmap(name="test", namespace="default",) + with self.mock_func("create_configmap", return_value=cm): + actual = kubernetes.configmap_present(name="test") self.assertDictEqual( { - 'comment': '', - 'changes': {'data': {}}, - 'name': 'test', - 'result': True, + "comment": "", + "changes": {"data": {}}, + "name": "test", + "result": True, }, actual, ) def test_configmap_present__replace_test_true(self): cm = self.make_configmap( - name='settings', - namespace='saltstack', - data={'foobar.conf': '# Example configuration'}, + name="settings", + namespace="saltstack", + data={"foobar.conf": "# Example configuration"}, ) - with self.mock_func('show_configmap', return_value=cm, test=True): + with self.mock_func("show_configmap", return_value=cm, test=True): ret = kubernetes.configmap_present( - name='settings', - namespace='saltstack', - data={'foobar.conf': '# Example configuration'}, + name="settings", + namespace="saltstack", + data={"foobar.conf": "# Example configuration"}, ) self.assertDictEqual( { - 'comment': 'The configmap is going to be replaced', - 'changes': {}, - 'name': 'settings', - 'result': None, + "comment": "The configmap is going to be replaced", + "changes": {}, + "name": "settings", + "result": None, }, ret, ) def test_configmap_present__replace(self): - cm = self.make_configmap(name='settings', data={'action': 'make=war'}) + cm = self.make_configmap(name="settings", data={"action": "make=war"}) # Replace an existing configmap - with self.mock_func('show_configmap', return_value=cm): + with self.mock_func("show_configmap", return_value=cm): new_cm = cm.copy() - new_cm.update({ - 'data': {'action': 'make=peace'}, - }) - with self.mock_func('replace_configmap', return_value=new_cm): + new_cm.update({"data": {"action": "make=peace"}}) + with self.mock_func("replace_configmap", return_value=new_cm): actual = kubernetes.configmap_present( - name='settings', - data={'action': 'make=peace'}, + name="settings", data={"action": "make=peace"}, ) self.assertDictEqual( { - 'comment': 'The configmap is already present. Forcing recreation', - 'changes': { - 'data': { - 'action': 'make=peace', - }, - }, - 'name': 'settings', - 'result': True, + "comment": "The configmap is already present. Forcing recreation", + "changes": {"data": {"action": "make=peace"}}, + "name": "settings", + "result": True, }, actual, ) def test_configmap_absent__noop_test_true(self): # Nothing to delete with test=True - with self.mock_func('show_configmap', return_value=None, test=True): - actual = kubernetes.configmap_absent(name='NOT_FOUND') + with self.mock_func("show_configmap", return_value=None, test=True): + actual = kubernetes.configmap_absent(name="NOT_FOUND") self.assertDictEqual( { - 'comment': 'The configmap does not exist', - 'changes': {}, - 'name': 'NOT_FOUND', - 'result': None, + "comment": "The configmap does not exist", + "changes": {}, + "name": "NOT_FOUND", + "result": None, }, actual, ) def test_configmap_absent__test_true(self): # Configmap exists with test=True - cm = self.make_configmap(name='deleteme', namespace='default') - with self.mock_func('show_configmap', return_value=cm, test=True): - actual = kubernetes.configmap_absent(name='deleteme') + cm = self.make_configmap(name="deleteme", namespace="default") + with self.mock_func("show_configmap", return_value=cm, test=True): + actual = kubernetes.configmap_absent(name="deleteme") self.assertDictEqual( { - 'comment': 'The configmap is going to be deleted', - 'changes': {}, - 'name': 'deleteme', - 'result': None, + "comment": "The configmap is going to be deleted", + "changes": {}, + "name": "deleteme", + "result": None, }, actual, ) def test_configmap_absent__noop(self): # Nothing to delete - with self.mock_func('show_configmap', return_value=None): - actual = kubernetes.configmap_absent(name='NOT_FOUND') + with self.mock_func("show_configmap", return_value=None): + actual = kubernetes.configmap_absent(name="NOT_FOUND") self.assertDictEqual( { - 'comment': 'The configmap does not exist', - 'changes': {}, - 'name': 'NOT_FOUND', - 'result': True, + "comment": "The configmap does not exist", + "changes": {}, + "name": "NOT_FOUND", + "result": True, }, actual, ) def test_configmap_absent(self): # Configmap exists, delete it! - cm = self.make_configmap(name='deleteme', namespace='default') - with self.mock_func('show_configmap', return_value=cm): + cm = self.make_configmap(name="deleteme", namespace="default") + with self.mock_func("show_configmap", return_value=cm): # The return from this module isn't used in the state - with self.mock_func('delete_configmap', return_value={}): - actual = kubernetes.configmap_absent(name='deleteme') + with self.mock_func("delete_configmap", return_value={}): + actual = kubernetes.configmap_absent(name="deleteme") self.assertDictEqual( { - 'comment': 'ConfigMap deleted', - 'changes': { - 'kubernetes.configmap': { - 'new': 'absent', - 'old': 'present', + "comment": "ConfigMap deleted", + "changes": { + "kubernetes.configmap": { + "new": "absent", + "old": "present", }, }, - 'name': 'deleteme', - 'result': True, + "name": "deleteme", + "result": True, }, actual, ) def test_secret_present__fail(self): actual = kubernetes.secret_present( - name='sekret', - data={'password': 'monk3y'}, - source='salt://nope.jinja', + name="sekret", data={"password": "monk3y"}, source="salt://nope.jinja", ) self.assertDictEqual( { - 'changes': {}, - 'result': False, - 'name': 'sekret', - 'comment': "'source' cannot be used in combination with 'data'", + "changes": {}, + "result": False, + "name": "sekret", + "comment": "'source' cannot be used in combination with 'data'", }, actual, ) def test_secret_present__exists_test_true(self): - secret = self.make_secret(name='sekret') + secret = self.make_secret(name="sekret") new_secret = secret.copy() - new_secret.update({ - 'data': {'password': 'uncle'}, - }) + new_secret.update({"data": {"password": "uncle"}}) # Secret exists already and needs replacing with test=True - with self.mock_func('show_secret', return_value=secret): - with self.mock_func('replace_secret', return_value=new_secret, test=True): + with self.mock_func("show_secret", return_value=secret): + with self.mock_func("replace_secret", return_value=new_secret, test=True): actual = kubernetes.secret_present( - name='sekret', - data={'password': 'uncle'}, + name="sekret", data={"password": "uncle"}, ) self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'sekret', - 'comment': 'The secret is going to be replaced', + "changes": {}, + "result": None, + "name": "sekret", + "comment": "The secret is going to be replaced", }, actual, ) def test_secret_present__exists(self): # Secret exists and gets replaced - secret = self.make_secret(name='sekret', data={'password': 'booyah'}) - with self.mock_func('show_secret', return_value=secret): - with self.mock_func('replace_secret', return_value=secret): + secret = self.make_secret(name="sekret", data={"password": "booyah"}) + with self.mock_func("show_secret", return_value=secret): + with self.mock_func("replace_secret", return_value=secret): actual = kubernetes.secret_present( - name='sekret', - data={'password': 'booyah'}, + name="sekret", data={"password": "booyah"}, ) self.assertDictEqual( { - 'changes': {'data': ['password']}, - 'result': True, - 'name': 'sekret', - 'comment': "The secret is already present. Forcing recreation", + "changes": {"data": ["password"]}, + "result": True, + "name": "sekret", + "comment": "The secret is already present. Forcing recreation", }, actual, ) def test_secret_present__create(self): # Secret exists and gets replaced - secret = self.make_secret(name='sekret', data={'password': 'booyah'}) - with self.mock_func('show_secret', return_value=None): - with self.mock_func('create_secret', return_value=secret): + secret = self.make_secret(name="sekret", data={"password": "booyah"}) + with self.mock_func("show_secret", return_value=None): + with self.mock_func("create_secret", return_value=secret): actual = kubernetes.secret_present( - name='sekret', - data={'password': 'booyah'}, + name="sekret", data={"password": "booyah"}, ) self.assertDictEqual( { - 'changes': {'data': ['password']}, - 'result': True, - 'name': 'sekret', - 'comment': '', + "changes": {"data": ["password"]}, + "result": True, + "name": "sekret", + "comment": "", }, actual, ) def test_secret_present__create_no_data(self): # Secret exists and gets replaced - secret = self.make_secret(name='sekret') - with self.mock_func('show_secret', return_value=None): - with self.mock_func('create_secret', return_value=secret): - actual = kubernetes.secret_present(name='sekret') + secret = self.make_secret(name="sekret") + with self.mock_func("show_secret", return_value=None): + with self.mock_func("create_secret", return_value=secret): + actual = kubernetes.secret_present(name="sekret") self.assertDictEqual( { - 'changes': {'data': []}, - 'result': True, - 'name': 'sekret', - 'comment': '', + "changes": {"data": []}, + "result": True, + "name": "sekret", + "comment": "", }, actual, ) def test_secret_present__create_test_true(self): # Secret exists and gets replaced with test=True - secret = self.make_secret(name='sekret') - with self.mock_func('show_secret', return_value=None): - with self.mock_func('create_secret', return_value=secret, test=True): - actual = kubernetes.secret_present(name='sekret') + secret = self.make_secret(name="sekret") + with self.mock_func("show_secret", return_value=None): + with self.mock_func("create_secret", return_value=secret, test=True): + actual = kubernetes.secret_present(name="sekret") self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'sekret', - 'comment': 'The secret is going to be created', + "changes": {}, + "result": None, + "name": "sekret", + "comment": "The secret is going to be created", }, actual, ) def test_secret_absent__noop_test_true(self): - with self.mock_func('show_secret', return_value=None, test=True): - actual = kubernetes.secret_absent(name='sekret') + with self.mock_func("show_secret", return_value=None, test=True): + actual = kubernetes.secret_absent(name="sekret") self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'sekret', - 'comment': 'The secret does not exist', + "changes": {}, + "result": None, + "name": "sekret", + "comment": "The secret does not exist", }, actual, ) def test_secret_absent__noop(self): - with self.mock_func('show_secret', return_value=None): - actual = kubernetes.secret_absent(name='passwords') + with self.mock_func("show_secret", return_value=None): + actual = kubernetes.secret_absent(name="passwords") self.assertDictEqual( { - 'changes': {}, - 'result': True, - 'name': 'passwords', - 'comment': 'The secret does not exist', + "changes": {}, + "result": True, + "name": "passwords", + "comment": "The secret does not exist", }, actual, ) def test_secret_absent__delete_test_true(self): - secret = self.make_secret(name='credentials', data={'redis': 'letmein'}) - with self.mock_func('show_secret', return_value=secret): - with self.mock_func('delete_secret', return_value=secret, test=True): - actual = kubernetes.secret_absent(name='credentials') + secret = self.make_secret(name="credentials", data={"redis": "letmein"}) + with self.mock_func("show_secret", return_value=secret): + with self.mock_func("delete_secret", return_value=secret, test=True): + actual = kubernetes.secret_absent(name="credentials") self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'credentials', - 'comment': 'The secret is going to be deleted', + "changes": {}, + "result": None, + "name": "credentials", + "comment": "The secret is going to be deleted", }, actual, ) def test_secret_absent__delete(self): - secret = self.make_secret(name='foobar', data={'redis': 'letmein'}) + secret = self.make_secret(name="foobar", data={"redis": "letmein"}) deleted = { - 'status': None, - 'kind': 'Secret', - 'code': None, - 'reason': None, - 'details': None, - 'message': None, - 'api_version': 'v1', - 'metadata': { - 'self_link': '/api/v1/namespaces/default/secrets/foobar', - 'resource_version': '30292', + "status": None, + "kind": "Secret", + "code": None, + "reason": None, + "details": None, + "message": None, + "api_version": "v1", + "metadata": { + "self_link": "/api/v1/namespaces/default/secrets/foobar", + "resource_version": "30292", }, } - with self.mock_func('show_secret', return_value=secret): - with self.mock_func('delete_secret', return_value=deleted): - actual = kubernetes.secret_absent(name='foobar') + with self.mock_func("show_secret", return_value=secret): + with self.mock_func("delete_secret", return_value=deleted): + actual = kubernetes.secret_absent(name="foobar") self.assertDictEqual( { - 'changes': { - 'kubernetes.secret': { - 'new': 'absent', - 'old': 'present', - }, + "changes": { + "kubernetes.secret": {"new": "absent", "old": "present"}, }, - 'result': True, - 'name': 'foobar', - 'comment': 'Secret deleted', + "result": True, + "name": "foobar", + "comment": "Secret deleted", }, actual, ) def test_node_label_present__add_test_true(self): labels = self.make_node_labels() - with self.mock_func('node_labels', return_value=labels, test=True): + with self.mock_func("node_labels", return_value=labels, test=True): actual = kubernetes.node_label_present( - name='com.zoo-animal', - node='minikube', - value='monkey', + name="com.zoo-animal", node="minikube", value="monkey", ) self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'com.zoo-animal', - 'comment': 'The label is going to be set', + "changes": {}, + "result": None, + "name": "com.zoo-animal", + "comment": "The label is going to be set", }, actual, ) @@ -544,74 +501,72 @@ class KubernetesTestCase(TestCase, LoaderModuleMockMixin): def test_node_label_present__add(self): node_data = self.make_node() # Remove some of the defaults to make it simpler - node_data['metadata']['labels'] = { - 'beta.kubernetes.io/os': 'linux', + node_data["metadata"]["labels"] = { + "beta.kubernetes.io/os": "linux", } - labels = node_data['metadata']['labels'] + labels = node_data["metadata"]["labels"] - with self.mock_func('node_labels', return_value=labels): - with self.mock_func('node_add_label', return_value=node_data): + with self.mock_func("node_labels", return_value=labels): + with self.mock_func("node_add_label", return_value=node_data): actual = kubernetes.node_label_present( - name='failure-domain.beta.kubernetes.io/zone', - node='minikube', - value='us-central1-a', + name="failure-domain.beta.kubernetes.io/zone", + node="minikube", + value="us-central1-a", ) self.assertDictEqual( { - 'comment': '', - 'changes': { - 'minikube.failure-domain.beta.kubernetes.io/zone': { - 'new': { - 'failure-domain.beta.kubernetes.io/zone': 'us-central1-a', - 'beta.kubernetes.io/os': 'linux' - }, - 'old': { - 'beta.kubernetes.io/os': 'linux', + "comment": "", + "changes": { + "minikube.failure-domain.beta.kubernetes.io/zone": { + "new": { + "failure-domain.beta.kubernetes.io/zone": "us-central1-a", + "beta.kubernetes.io/os": "linux", }, + "old": {"beta.kubernetes.io/os": "linux"}, }, }, - 'name': 'failure-domain.beta.kubernetes.io/zone', - 'result': True, + "name": "failure-domain.beta.kubernetes.io/zone", + "result": True, }, actual, ) def test_node_label_present__already_set(self): node_data = self.make_node() - labels = node_data['metadata']['labels'] - with self.mock_func('node_labels', return_value=labels): - with self.mock_func('node_add_label', return_value=node_data): + labels = node_data["metadata"]["labels"] + with self.mock_func("node_labels", return_value=labels): + with self.mock_func("node_add_label", return_value=node_data): actual = kubernetes.node_label_present( - name='failure-domain.beta.kubernetes.io/region', - node='minikube', - value='us-west-1', + name="failure-domain.beta.kubernetes.io/region", + node="minikube", + value="us-west-1", ) self.assertDictEqual( { - 'changes': {}, - 'result': True, - 'name': 'failure-domain.beta.kubernetes.io/region', - 'comment': 'The label is already set and has the specified value', + "changes": {}, + "result": True, + "name": "failure-domain.beta.kubernetes.io/region", + "comment": "The label is already set and has the specified value", }, actual, ) def test_node_label_present__update_test_true(self): node_data = self.make_node() - labels = node_data['metadata']['labels'] - with self.mock_func('node_labels', return_value=labels): - with self.mock_func('node_add_label', return_value=node_data, test=True): + labels = node_data["metadata"]["labels"] + with self.mock_func("node_labels", return_value=labels): + with self.mock_func("node_add_label", return_value=node_data, test=True): actual = kubernetes.node_label_present( - name='failure-domain.beta.kubernetes.io/region', - node='minikube', - value='us-east-1', + name="failure-domain.beta.kubernetes.io/region", + node="minikube", + value="us-east-1", ) self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'failure-domain.beta.kubernetes.io/region', - 'comment': 'The label is going to be updated', + "changes": {}, + "result": None, + "name": "failure-domain.beta.kubernetes.io/region", + "comment": "The label is going to be updated", }, actual, ) @@ -619,309 +574,288 @@ class KubernetesTestCase(TestCase, LoaderModuleMockMixin): def test_node_label_present__update(self): node_data = self.make_node() # Remove some of the defaults to make it simpler - node_data['metadata']['labels'] = { - 'failure-domain.beta.kubernetes.io/region': 'us-west-1', + node_data["metadata"]["labels"] = { + "failure-domain.beta.kubernetes.io/region": "us-west-1", } - labels = node_data['metadata']['labels'] - with self.mock_func('node_labels', return_value=labels): - with self.mock_func('node_add_label', return_value=node_data): + labels = node_data["metadata"]["labels"] + with self.mock_func("node_labels", return_value=labels): + with self.mock_func("node_add_label", return_value=node_data): actual = kubernetes.node_label_present( - name='failure-domain.beta.kubernetes.io/region', - node='minikube', - value='us-east-1', + name="failure-domain.beta.kubernetes.io/region", + node="minikube", + value="us-east-1", ) self.assertDictEqual( { - 'changes': { - 'minikube.failure-domain.beta.kubernetes.io/region': { - 'new': {'failure-domain.beta.kubernetes.io/region': 'us-east-1'}, - 'old': {'failure-domain.beta.kubernetes.io/region': 'us-west-1'}, + "changes": { + "minikube.failure-domain.beta.kubernetes.io/region": { + "new": { + "failure-domain.beta.kubernetes.io/region": "us-east-1" + }, + "old": { + "failure-domain.beta.kubernetes.io/region": "us-west-1" + }, } }, - 'result': True, - 'name': 'failure-domain.beta.kubernetes.io/region', - 'comment': 'The label is already set, changing the value', + "result": True, + "name": "failure-domain.beta.kubernetes.io/region", + "comment": "The label is already set, changing the value", }, actual, ) def test_node_label_absent__noop_test_true(self): labels = self.make_node_labels() - with self.mock_func('node_labels', return_value=labels, test=True): + with self.mock_func("node_labels", return_value=labels, test=True): actual = kubernetes.node_label_absent( - name='non-existent-label', - node='minikube', + name="non-existent-label", node="minikube", ) self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'non-existent-label', - 'comment': 'The label does not exist', + "changes": {}, + "result": None, + "name": "non-existent-label", + "comment": "The label does not exist", }, - actual + actual, ) def test_node_label_absent__noop(self): labels = self.make_node_labels() - with self.mock_func('node_labels', return_value=labels): + with self.mock_func("node_labels", return_value=labels): actual = kubernetes.node_label_absent( - name='non-existent-label', - node='minikube', + name="non-existent-label", node="minikube", ) self.assertDictEqual( { - 'changes': {}, - 'result': True, - 'name': 'non-existent-label', - 'comment': 'The label does not exist', + "changes": {}, + "result": True, + "name": "non-existent-label", + "comment": "The label does not exist", }, - actual + actual, ) def test_node_label_absent__delete_test_true(self): labels = self.make_node_labels() - with self.mock_func('node_labels', return_value=labels, test=True): + with self.mock_func("node_labels", return_value=labels, test=True): actual = kubernetes.node_label_absent( - name='failure-domain.beta.kubernetes.io/region', - node='minikube', + name="failure-domain.beta.kubernetes.io/region", node="minikube", ) self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'failure-domain.beta.kubernetes.io/region', - 'comment': 'The label is going to be deleted', + "changes": {}, + "result": None, + "name": "failure-domain.beta.kubernetes.io/region", + "comment": "The label is going to be deleted", }, - actual + actual, ) def test_node_label_absent__delete(self): node_data = self.make_node() - labels = node_data['metadata']['labels'].copy() + labels = node_data["metadata"]["labels"].copy() - node_data['metadata']['labels'].pop('failure-domain.beta.kubernetes.io/region') + node_data["metadata"]["labels"].pop("failure-domain.beta.kubernetes.io/region") - with self.mock_func('node_labels', return_value=labels): - with self.mock_func('node_remove_label', return_value=node_data): + with self.mock_func("node_labels", return_value=labels): + with self.mock_func("node_remove_label", return_value=node_data): actual = kubernetes.node_label_absent( - name='failure-domain.beta.kubernetes.io/region', - node='minikube', + name="failure-domain.beta.kubernetes.io/region", node="minikube", ) self.assertDictEqual( { - 'result': True, - 'changes': { - 'kubernetes.node_label': { - 'new': 'absent', - 'old': 'present', + "result": True, + "changes": { + "kubernetes.node_label": { + "new": "absent", + "old": "present", } }, - 'comment': 'Label removed from node', - 'name': 'failure-domain.beta.kubernetes.io/region', + "comment": "Label removed from node", + "name": "failure-domain.beta.kubernetes.io/region", }, - actual + actual, ) def test_namespace_present__create_test_true(self): - with self.mock_func('show_namespace', return_value=None, test=True): - actual = kubernetes.namespace_present(name='saltstack') + with self.mock_func("show_namespace", return_value=None, test=True): + actual = kubernetes.namespace_present(name="saltstack") self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'saltstack', - 'comment': 'The namespace is going to be created', + "changes": {}, + "result": None, + "name": "saltstack", + "comment": "The namespace is going to be created", }, - actual + actual, ) def test_namespace_present__create(self): - namespace_data = self.make_namespace(name='saltstack') - with self.mock_func('show_namespace', return_value=None): - with self.mock_func('create_namespace', return_value=namespace_data): - actual = kubernetes.namespace_present(name='saltstack') + namespace_data = self.make_namespace(name="saltstack") + with self.mock_func("show_namespace", return_value=None): + with self.mock_func("create_namespace", return_value=namespace_data): + actual = kubernetes.namespace_present(name="saltstack") self.assertDictEqual( { - 'changes': { - 'namespace': { - 'new': namespace_data, - 'old': {}, - }, - }, - 'result': True, - 'name': 'saltstack', - 'comment': '', + "changes": {"namespace": {"new": namespace_data, "old": {}}}, + "result": True, + "name": "saltstack", + "comment": "", }, - actual + actual, ) def test_namespace_present__noop_test_true(self): - namespace_data = self.make_namespace(name='saltstack') - with self.mock_func('show_namespace', return_value=namespace_data, test=True): - actual = kubernetes.namespace_present(name='saltstack') + namespace_data = self.make_namespace(name="saltstack") + with self.mock_func("show_namespace", return_value=namespace_data, test=True): + actual = kubernetes.namespace_present(name="saltstack") self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'saltstack', - 'comment': 'The namespace already exists', + "changes": {}, + "result": None, + "name": "saltstack", + "comment": "The namespace already exists", }, - actual + actual, ) def test_namespace_present__noop(self): - namespace_data = self.make_namespace(name='saltstack') - with self.mock_func('show_namespace', return_value=namespace_data): - actual = kubernetes.namespace_present(name='saltstack') + namespace_data = self.make_namespace(name="saltstack") + with self.mock_func("show_namespace", return_value=namespace_data): + actual = kubernetes.namespace_present(name="saltstack") self.assertDictEqual( { - 'changes': {}, - 'result': True, - 'name': 'saltstack', - 'comment': 'The namespace already exists', + "changes": {}, + "result": True, + "name": "saltstack", + "comment": "The namespace already exists", }, - actual + actual, ) def test_namespace_absent__noop_test_true(self): - with self.mock_func('show_namespace', return_value=None, test=True): - actual = kubernetes.namespace_absent(name='salt') + with self.mock_func("show_namespace", return_value=None, test=True): + actual = kubernetes.namespace_absent(name="salt") self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'salt', - 'comment': 'The namespace does not exist', + "changes": {}, + "result": None, + "name": "salt", + "comment": "The namespace does not exist", }, - actual + actual, ) def test_namespace_absent__noop(self): - with self.mock_func('show_namespace', return_value=None): - actual = kubernetes.namespace_absent(name='salt') + with self.mock_func("show_namespace", return_value=None): + actual = kubernetes.namespace_absent(name="salt") self.assertDictEqual( { - 'changes': {}, - 'result': True, - 'name': 'salt', - 'comment': 'The namespace does not exist', + "changes": {}, + "result": True, + "name": "salt", + "comment": "The namespace does not exist", }, - actual + actual, ) def test_namespace_absent__delete_test_true(self): - namespace_data = self.make_namespace(name='salt') - with self.mock_func('show_namespace', return_value=namespace_data, test=True): - actual = kubernetes.namespace_absent(name='salt') + namespace_data = self.make_namespace(name="salt") + with self.mock_func("show_namespace", return_value=namespace_data, test=True): + actual = kubernetes.namespace_absent(name="salt") self.assertDictEqual( { - 'changes': {}, - 'result': None, - 'name': 'salt', - 'comment': 'The namespace is going to be deleted', + "changes": {}, + "result": None, + "name": "salt", + "comment": "The namespace is going to be deleted", }, - actual + actual, ) def test_namespace_absent__delete_code_200(self): - namespace_data = self.make_namespace(name='salt') + namespace_data = self.make_namespace(name="salt") deleted = namespace_data.copy() - deleted['code'] = 200 - deleted.update({ - 'code': 200, - 'message': None, - }) - with self.mock_func('show_namespace', return_value=namespace_data): - with self.mock_func('delete_namespace', return_value=deleted): - actual = kubernetes.namespace_absent(name='salt') + deleted["code"] = 200 + deleted.update({"code": 200, "message": None}) + with self.mock_func("show_namespace", return_value=namespace_data): + with self.mock_func("delete_namespace", return_value=deleted): + actual = kubernetes.namespace_absent(name="salt") self.assertDictEqual( { - 'changes': { - 'kubernetes.namespace': { - 'new': 'absent', - 'old': 'present', - } + "changes": { + "kubernetes.namespace": {"new": "absent", "old": "present"} }, - 'result': True, - 'name': 'salt', - 'comment': 'Terminating', + "result": True, + "name": "salt", + "comment": "Terminating", }, - actual + actual, ) def test_namespace_absent__delete_status_terminating(self): - namespace_data = self.make_namespace(name='salt') + namespace_data = self.make_namespace(name="salt") deleted = namespace_data.copy() - deleted.update({ - 'code': None, - 'status': 'Terminating namespace', - 'message': 'Terminating this shizzzle yo', - }) - with self.mock_func('show_namespace', return_value=namespace_data): - with self.mock_func('delete_namespace', return_value=deleted): - actual = kubernetes.namespace_absent(name='salt') + deleted.update( + { + "code": None, + "status": "Terminating namespace", + "message": "Terminating this shizzzle yo", + } + ) + with self.mock_func("show_namespace", return_value=namespace_data): + with self.mock_func("delete_namespace", return_value=deleted): + actual = kubernetes.namespace_absent(name="salt") self.assertDictEqual( { - 'changes': { - 'kubernetes.namespace': { - 'new': 'absent', - 'old': 'present', - } + "changes": { + "kubernetes.namespace": {"new": "absent", "old": "present"} }, - 'result': True, - 'name': 'salt', - 'comment': 'Terminating this shizzzle yo', + "result": True, + "name": "salt", + "comment": "Terminating this shizzzle yo", }, - actual + actual, ) def test_namespace_absent__delete_status_phase_terminating(self): # This is what kubernetes 1.8.0 looks like when deleting namespaces - namespace_data = self.make_namespace(name='salt') + namespace_data = self.make_namespace(name="salt") deleted = namespace_data.copy() - deleted.update({ - 'code': None, - 'message': None, - 'status': {'phase': 'Terminating'}, - }) - with self.mock_func('show_namespace', return_value=namespace_data): - with self.mock_func('delete_namespace', return_value=deleted): - actual = kubernetes.namespace_absent(name='salt') + deleted.update( + {"code": None, "message": None, "status": {"phase": "Terminating"}} + ) + with self.mock_func("show_namespace", return_value=namespace_data): + with self.mock_func("delete_namespace", return_value=deleted): + actual = kubernetes.namespace_absent(name="salt") self.assertDictEqual( { - 'changes': { - 'kubernetes.namespace': { - 'new': 'absent', - 'old': 'present', - } + "changes": { + "kubernetes.namespace": {"new": "absent", "old": "present"} }, - 'result': True, - 'name': 'salt', - 'comment': 'Terminating', + "result": True, + "name": "salt", + "comment": "Terminating", }, - actual + actual, ) def test_namespace_absent__delete_error(self): - namespace_data = self.make_namespace(name='salt') + namespace_data = self.make_namespace(name="salt") deleted = namespace_data.copy() - deleted.update({ - 'code': 418, - 'message': 'I\' a teapot!', - 'status': None, - }) - with self.mock_func('show_namespace', return_value=namespace_data): - with self.mock_func('delete_namespace', return_value=deleted): - actual = kubernetes.namespace_absent(name='salt') + deleted.update({"code": 418, "message": "I' a teapot!", "status": None}) + with self.mock_func("show_namespace", return_value=namespace_data): + with self.mock_func("delete_namespace", return_value=deleted): + actual = kubernetes.namespace_absent(name="salt") self.assertDictEqual( { - 'changes': {}, - 'result': False, - 'name': 'salt', - 'comment': 'Something went wrong, response: {0}'.format( + "changes": {}, + "result": False, + "name": "salt", + "comment": "Something went wrong, response: {0}".format( deleted, ), }, - actual + actual, ) diff --git a/tests/unit/states/test_layman.py b/tests/unit/states/test_layman.py index b738e02cf08..245c0a6ae27 100644 --- a/tests/unit/states/test_layman.py +++ b/tests/unit/states/test_layman.py @@ -1,72 +1,65 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.layman as layman +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LaymanTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.layman - ''' + """ + def setup_loader_modules(self): return {layman: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to verify that the overlay is present. - ''' - name = 'sunrise' + """ + name = "sunrise" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(side_effect=[[name], []]) - with patch.dict(layman.__salt__, {'layman.list_local': mock}): - comt = ('Overlay {0} already present'.format(name)) - ret.update({'comment': comt}) + with patch.dict(layman.__salt__, {"layman.list_local": mock}): + comt = "Overlay {0} already present".format(name) + ret.update({"comment": comt}) self.assertDictEqual(layman.present(name), ret) - with patch.dict(layman.__opts__, {'test': True}): - comt = ('Overlay {0} is set to be added'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(layman.__opts__, {"test": True}): + comt = "Overlay {0} is set to be added".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(layman.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to verify that the overlay is absent. - ''' - name = 'sunrise' + """ + name = "sunrise" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(side_effect=[[], [name]]) - with patch.dict(layman.__salt__, {'layman.list_local': mock}): - comt = ('Overlay {0} already absent'.format(name)) - ret.update({'comment': comt}) + with patch.dict(layman.__salt__, {"layman.list_local": mock}): + comt = "Overlay {0} already absent".format(name) + ret.update({"comment": comt}) self.assertDictEqual(layman.absent(name), ret) - with patch.dict(layman.__opts__, {'test': True}): - comt = ('Overlay {0} is set to be deleted'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(layman.__opts__, {"test": True}): + comt = "Overlay {0} is set to be deleted".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(layman.absent(name), ret) diff --git a/tests/unit/states/test_ldap.py b/tests/unit/states/test_ldap.py index 01af141d73e..8d65ac7b77f 100644 --- a/tests/unit/states/test_ldap.py +++ b/tests/unit/states/test_ldap.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -'''Test cases for the ``ldap`` state module +"""Test cases for the ``ldap`` state module This code is gross. I started out trying to remove some of the duplicate code in the test cases, and before I knew it the test code @@ -7,15 +7,15 @@ was an ugly second implementation. I'm leaving it for now, but this should really be gutted and replaced with something sensible. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import copy -from salt.ext import six -import salt.states.ldap -from salt.utils.oset import OrderedSet +import salt.states.ldap +from salt.ext import six +from salt.utils.oset import OrderedSet from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase @@ -33,23 +33,13 @@ def _init_db(newdb=None): def _complex_db(): return { - 'dnfoo': { - 'attrfoo1': OrderedSet(( - 'valfoo1.1', - 'valfoo1.2', - )), - 'attrfoo2': OrderedSet(( - 'valfoo2.1', - )), + "dnfoo": { + "attrfoo1": OrderedSet(("valfoo1.1", "valfoo1.2",)), + "attrfoo2": OrderedSet(("valfoo2.1",)), }, - 'dnbar': { - 'attrbar1': OrderedSet(( - 'valbar1.1', - 'valbar1.2', - )), - 'attrbar2': OrderedSet(( - 'valbar2.1', - )), + "dnbar": { + "attrbar1": OrderedSet(("valbar1.1", "valbar1.2",)), + "attrbar2": OrderedSet(("valbar2.1",)), }, } @@ -72,9 +62,11 @@ def _dummy_connect(connect_spec): def _dummy_search(connect_spec, base, scope): if base not in db: return {} - return {base: dict(((attr, list(db[base][attr])) - for attr in db[base] - if len(db[base][attr])))} + return { + base: dict( + ((attr, list(db[base][attr])) for attr in db[base] if len(db[base][attr])) + ) + } def _dummy_add(connect_spec, dn, attributes): @@ -108,20 +100,20 @@ def _dummy_change(connect_spec, dn, before, after): if attr not in before: assert attr in after assert after[attr] - directives.append(('add', attr, after[attr])) + directives.append(("add", attr, after[attr])) elif attr not in after: assert attr in before assert before[attr] - directives.append(('delete', attr, ())) + directives.append(("delete", attr, ())) else: assert before[attr] assert after[attr] to_del = before[attr] - after[attr] if to_del: - directives.append(('delete', attr, to_del)) + directives.append(("delete", attr, to_del)) to_add = after[attr] - before[attr] if to_add: - directives.append(('add', attr, to_add)) + directives.append(("add", attr, to_add)) return _dummy_modify(connect_spec, dn, directives) @@ -129,13 +121,13 @@ def _dummy_modify(connect_spec, dn, directives): assert dn in db e = db[dn] for op, attr, vals in directives: - if op == 'add': + if op == "add": assert vals existing_vals = e.setdefault(attr, OrderedSet()) for val in vals: assert val not in existing_vals existing_vals.add(val) - elif op == 'delete': + elif op == "delete": assert attr in e existing_vals = e[attr] assert existing_vals @@ -147,7 +139,7 @@ def _dummy_modify(connect_spec, dn, directives): existing_vals.remove(val) if not existing_vals: del e[attr] - elif op == 'replace': + elif op == "replace": e.pop(attr, None) e[attr] = OrderedSet(vals) else: @@ -158,26 +150,19 @@ def _dummy_modify(connect_spec, dn, directives): def _dump_db(d=None): if d is None: d = db - return dict(((dn, dict(((attr, list(d[dn][attr])) - for attr in d[dn]))) - for dn in d)) + return dict(((dn, dict(((attr, list(d[dn][attr])) for attr in d[dn]))) for dn in d)) class LDAPTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): salt_dunder = {} - for fname in ('connect', 'search', 'add', 'delete', 'change', 'modify'): - salt_dunder['ldap3.{}'.format(fname)] = globals()['_dummy_' + fname] + for fname in ("connect", "search", "add", "delete", "change", "modify"): + salt_dunder["ldap3.{}".format(fname)] = globals()["_dummy_" + fname] return { - salt.states.ldap: { - '__opts__': {'test': False}, - '__salt__': salt_dunder - } + salt.states.ldap: {"__opts__": {"test": False}, "__salt__": salt_dunder} } - def _test_helper(self, init_db, expected_ret, replace, - delete_others=False): + def _test_helper(self, init_db, expected_ret, replace, delete_others=False): _init_db(copy.deepcopy(init_db)) old = _dump_db() new = _dump_db() @@ -209,24 +194,42 @@ class LDAPTestCase(TestCase, LoaderModuleMockMixin): for dn in dn_to_delete: del new[dn] del expected_db[dn] - name = 'ldapi:///' - expected_ret['name'] = name - expected_ret.setdefault('result', True) - expected_ret.setdefault('comment', 'Successfully updated LDAP entries') - expected_ret.setdefault('changes', dict( - ((dn, {'old': dict((attr, vals) - for attr, vals in six.iteritems(old[dn]) - if vals != new.get(dn, {}).get(attr, ())) - if dn in old else None, - 'new': dict((attr, vals) - for attr, vals in six.iteritems(new[dn]) - if vals != old.get(dn, {}).get(attr, ())) - if dn in new else None}) - for dn in replace - if old.get(dn, {}) != new.get(dn, {})))) - entries = [{dn: [{'replace': attrs}, - {'delete_others': delete_others}]} - for dn, attrs in six.iteritems(replace)] + name = "ldapi:///" + expected_ret["name"] = name + expected_ret.setdefault("result", True) + expected_ret.setdefault("comment", "Successfully updated LDAP entries") + expected_ret.setdefault( + "changes", + dict( + ( + ( + dn, + { + "old": dict( + (attr, vals) + for attr, vals in six.iteritems(old[dn]) + if vals != new.get(dn, {}).get(attr, ()) + ) + if dn in old + else None, + "new": dict( + (attr, vals) + for attr, vals in six.iteritems(new[dn]) + if vals != old.get(dn, {}).get(attr, ()) + ) + if dn in new + else None, + }, + ) + for dn in replace + if old.get(dn, {}) != new.get(dn, {}) + ) + ), + ) + entries = [ + {dn: [{"replace": attrs}, {"delete_others": delete_others}]} + for dn, attrs in six.iteritems(replace) + ] actual = salt.states.ldap.managed(name, entries) self.assertDictEqual(expected_ret, actual) self.assertDictEqual(expected_db, db) @@ -236,77 +239,58 @@ class LDAPTestCase(TestCase, LoaderModuleMockMixin): def _test_helper_nochange(self, init_db, replace, delete_others=False): expected = { - 'changes': {}, - 'comment': 'LDAP entries already set', + "changes": {}, + "comment": "LDAP entries already set", } self._test_helper(init_db, expected, replace, delete_others) def test_managed_empty(self): _init_db() - name = 'ldapi:///' + name = "ldapi:///" expected = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': 'LDAP entries already set', + "name": name, + "changes": {}, + "result": True, + "comment": "LDAP entries already set", } actual = salt.states.ldap.managed(name, {}) self.assertDictEqual(expected, actual) def test_managed_add_entry(self): - self._test_helper_success( - {}, - {'dummydn': {'foo': ['bar', 'baz']}}) + self._test_helper_success({}, {"dummydn": {"foo": ["bar", "baz"]}}) def test_managed_add_attr(self): - self._test_helper_success( - _complex_db(), - {'dnfoo': {'attrfoo3': ['valfoo3.1']}}) + self._test_helper_success(_complex_db(), {"dnfoo": {"attrfoo3": ["valfoo3.1"]}}) def test_managed_simplereplace(self): - self._test_helper_success( - _complex_db(), - {'dnfoo': {'attrfoo1': ['valfoo1.3']}}) + self._test_helper_success(_complex_db(), {"dnfoo": {"attrfoo1": ["valfoo1.3"]}}) def test_managed_deleteattr(self): - self._test_helper_success( - _complex_db(), - {'dnfoo': {'attrfoo1': []}}) + self._test_helper_success(_complex_db(), {"dnfoo": {"attrfoo1": []}}) def test_managed_deletenonexistattr(self): - self._test_helper_nochange( - _complex_db(), - {'dnfoo': {'dummyattr': []}}) + self._test_helper_nochange(_complex_db(), {"dnfoo": {"dummyattr": []}}) def test_managed_deleteentry(self): - self._test_helper_success( - _complex_db(), - {'dnfoo': {}}, - True) + self._test_helper_success(_complex_db(), {"dnfoo": {}}, True) def test_managed_deletenonexistentry(self): - self._test_helper_nochange( - _complex_db(), - {'dummydn': {}}, - True) + self._test_helper_nochange(_complex_db(), {"dummydn": {}}, True) def test_managed_deletenonexistattrinnonexistentry(self): - self._test_helper_nochange( - _complex_db(), - {'dummydn': {'dummyattr': []}}) + self._test_helper_nochange(_complex_db(), {"dummydn": {"dummyattr": []}}) def test_managed_add_attr_delete_others(self): self._test_helper_success( - _complex_db(), - {'dnfoo': {'dummyattr': ['dummyval']}}, - True) + _complex_db(), {"dnfoo": {"dummyattr": ["dummyval"]}}, True + ) def test_managed_no_net_change(self): self._test_helper_nochange( - _complex_db(), - {'dnfoo': {'attrfoo1': ['valfoo1.1', 'valfoo1.2']}}) + _complex_db(), {"dnfoo": {"attrfoo1": ["valfoo1.1", "valfoo1.2"]}} + ) def test_managed_repeated_values(self): self._test_helper_success( - {}, - {'dummydn': {'dummyattr': ['dummyval', 'dummyval']}}) + {}, {"dummydn": {"dummyattr": ["dummyval", "dummyval"]}} + ) diff --git a/tests/unit/states/test_libcloud_dns.py b/tests/unit/states/test_libcloud_dns.py index 108d1d274fb..0626e4b97b3 100644 --- a/tests/unit/states/test_libcloud_dns.py +++ b/tests/unit/states/test_libcloud_dns.py @@ -1,22 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Anthony Shaw <anthonyshaw@apache.org> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +import salt.states.libcloud_dns as libcloud_dns +from salt.modules.libcloud_dns import _simple_record, _simple_zone # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin from tests.support.unit import TestCase -import salt.states.libcloud_dns as libcloud_dns -from salt.modules.libcloud_dns import _simple_record, _simple_zone class DNSTestZone(object): def __init__(self, id, domain): self.id = id - self.type = 'master' + self.type = "master" self.ttl = 4400 self.domain = domain self.extra = {} @@ -29,19 +30,18 @@ class DNSTestRecord(object): self.type = record_type self.ttl = 4400 self.data = data - self.zone = DNSTestZone('test', 'domain') + self.zone = DNSTestZone("test", "domain") self.extra = {} class LibcloudDnsModuleTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): test_records = { - 'zone1': [_simple_record(DNSTestRecord(0, 'www', 'A', '127.0.0.1'))] + "zone1": [_simple_record(DNSTestRecord(0, "www", "A", "127.0.0.1"))] } def list_zones(profile): - return [_simple_zone(DNSTestZone('zone1', 'test.com'))] + return [_simple_zone(DNSTestZone("zone1", "test.com"))] def list_records(zone_id, profile): return test_records[zone_id] @@ -60,85 +60,95 @@ class LibcloudDnsModuleTestCase(TestCase, LoaderModuleMockMixin): return { libcloud_dns: { - '__salt__': { - 'libcloud_dns.list_zones': list_zones, - 'libcloud_dns.list_records': list_records, - 'libcloud_dns.create_record': create_record, - 'libcloud_dns.delete_record': delete_record, - 'libcloud_dns.create_zone': create_zone, - 'libcloud_dns.delete_zone': delete_zone + "__salt__": { + "libcloud_dns.list_zones": list_zones, + "libcloud_dns.list_records": list_records, + "libcloud_dns.create_record": create_record, + "libcloud_dns.delete_record": delete_record, + "libcloud_dns.create_zone": create_zone, + "libcloud_dns.delete_zone": delete_zone, } } } def test_present_record_exists(self): - ''' + """ Try and create a record that already exists - ''' - result = libcloud_dns.record_present('www', 'test.com', 'A', '127.0.0.1', 'test') + """ + result = libcloud_dns.record_present( + "www", "test.com", "A", "127.0.0.1", "test" + ) self.assertTrue(result) def test_present_record_does_not_exist(self): - ''' + """ Try and create a record that already exists - ''' - result = libcloud_dns.record_present('mail', 'test.com', 'A', '127.0.0.1', 'test') + """ + result = libcloud_dns.record_present( + "mail", "test.com", "A", "127.0.0.1", "test" + ) self.assertTrue(result) def test_absent_record_exists(self): - ''' + """ Try and deny a record that already exists - ''' - result = libcloud_dns.record_absent('www', 'test.com', 'A', '127.0.0.1', 'test') + """ + result = libcloud_dns.record_absent("www", "test.com", "A", "127.0.0.1", "test") self.assertTrue(result) def test_absent_record_does_not_exist(self): - ''' + """ Try and deny a record that already exists - ''' - result = libcloud_dns.record_absent('mail', 'test.com', 'A', '127.0.0.1', 'test') + """ + result = libcloud_dns.record_absent( + "mail", "test.com", "A", "127.0.0.1", "test" + ) self.assertTrue(result) def test_present_zone_not_found(self): - ''' + """ Assert that when you try and ensure present state for a record to a zone that doesn't exist it fails gracefully - ''' - result = libcloud_dns.record_present('mail', 'notatest.com', 'A', '127.0.0.1', 'test') - self.assertFalse(result['result']) + """ + result = libcloud_dns.record_present( + "mail", "notatest.com", "A", "127.0.0.1", "test" + ) + self.assertFalse(result["result"]) def test_absent_zone_not_found(self): - ''' + """ Assert that when you try and ensure absent state for a record to a zone that doesn't exist it fails gracefully - ''' - result = libcloud_dns.record_absent('mail', 'notatest.com', 'A', '127.0.0.1', 'test') - self.assertFalse(result['result']) + """ + result = libcloud_dns.record_absent( + "mail", "notatest.com", "A", "127.0.0.1", "test" + ) + self.assertFalse(result["result"]) def test_zone_present(self): - ''' + """ Assert that a zone is present (that did not exist) - ''' - result = libcloud_dns.zone_present('testing.com', 'master', 'test1') + """ + result = libcloud_dns.zone_present("testing.com", "master", "test1") self.assertTrue(result) def test_zone_already_present(self): - ''' + """ Assert that a zone is present (that did exist) - ''' - result = libcloud_dns.zone_present('test.com', 'master', 'test1') + """ + result = libcloud_dns.zone_present("test.com", "master", "test1") self.assertTrue(result) def test_zone_absent(self): - ''' + """ Assert that a zone that did exist is absent - ''' - result = libcloud_dns.zone_absent('test.com', 'test1') + """ + result = libcloud_dns.zone_absent("test.com", "test1") self.assertTrue(result) def test_zone_already_absent(self): - ''' + """ Assert that a zone that did not exist is absent - ''' - result = libcloud_dns.zone_absent('testing.com', 'test1') + """ + result = libcloud_dns.zone_absent("testing.com", "test1") self.assertTrue(result) diff --git a/tests/unit/states/test_linux_acl.py b/tests/unit/states/test_linux_acl.py index a86706b0fa4..8e0ed5cca5c 100644 --- a/tests/unit/states/test_linux_acl.py +++ b/tests/unit/states/test_linux_acl.py @@ -1,403 +1,553 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import sys -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import ( - MagicMock, - patch) - - # Import Salt Libs import salt.states.linux_acl as linux_acl from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -@skipIf(not sys.platform.startswith('linux'), 'Test for Linux only') + +@skipIf(not sys.platform.startswith("linux"), "Test for Linux only") class LinuxAclTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.linux_acl - ''' + """ + def setup_loader_modules(self): return {linux_acl: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure a Linux ACL is present - ''' + """ self.maxDiff = None - name = '/root' - acl_type = 'users' - acl_name = 'damian' - perms = 'rwx' + name = "/root" + acl_type = "users" + acl_name = "damian" + perms = "rwx" - mock = MagicMock(side_effect=[{name: {acl_type: [{acl_name: - {'octal': 5}}]}}, - {name: {acl_type: [{acl_name: - {'octal': 5}}]}}, - {name: {acl_type: [{acl_name: - {'octal': 5}}]}}, - {name: {acl_type: [{}]}}, - {name: {acl_type: [{}]}}, - {name: {acl_type: [{}]}}, - { - name: {acl_type: [{acl_name: {'octal': 7}}]}, - name+"/foo": {acl_type: [{acl_name: {'octal': 5}}]} - }, - { - name: {acl_type: [{acl_name: {'octal': 7}}]}, - name+"/foo": {acl_type: [{acl_name: {'octal': 7}}]} - }, - {name: {acl_type: ''}}]) + mock = MagicMock( + side_effect=[ + {name: {acl_type: [{acl_name: {"octal": 5}}]}}, + {name: {acl_type: [{acl_name: {"octal": 5}}]}}, + {name: {acl_type: [{acl_name: {"octal": 5}}]}}, + {name: {acl_type: [{}]}}, + {name: {acl_type: [{}]}}, + {name: {acl_type: [{}]}}, + { + name: {acl_type: [{acl_name: {"octal": 7}}]}, + name + "/foo": {acl_type: [{acl_name: {"octal": 5}}]}, + }, + { + name: {acl_type: [{acl_name: {"octal": 7}}]}, + name + "/foo": {acl_type: [{acl_name: {"octal": 7}}]}, + }, + {name: {acl_type: ""}}, + ] + ) mock_modfacl = MagicMock(return_value=True) - with patch.dict(linux_acl.__salt__, {'acl.getfacl': mock}): + with patch.dict(linux_acl.__salt__, {"acl.getfacl": mock}): # Update - test=True - with patch.dict(linux_acl.__opts__, {'test': True}): - comt = ('Updated permissions will be applied for {0}: r-x -> {1}' - ''.format(acl_name, perms)) - ret = {'name': name, - 'comment': comt, - 'changes': {'new': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': perms}, - 'old': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': 'r-x'}}, - 'result': None} + with patch.dict(linux_acl.__opts__, {"test": True}): + comt = ( + "Updated permissions will be applied for {0}: r-x -> {1}" + "".format(acl_name, perms) + ) + ret = { + "name": name, + "comment": comt, + "changes": { + "new": { + "acl_name": acl_name, + "acl_type": acl_type, + "perms": perms, + }, + "old": { + "acl_name": acl_name, + "acl_type": acl_type, + "perms": "r-x", + }, + }, + "result": None, + } - self.assertDictEqual(linux_acl.present(name, acl_type, acl_name, - perms), ret) + self.assertDictEqual( + linux_acl.present(name, acl_type, acl_name, perms), ret + ) # Update - test=False - with patch.dict(linux_acl.__salt__, {'acl.modfacl': mock_modfacl}): - with patch.dict(linux_acl.__opts__, {'test': False}): - comt = ('Updated permissions for {0}'.format(acl_name)) - ret = {'name': name, - 'comment': comt, - 'changes': {'new': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': perms}, - 'old': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': 'r-x'}}, - 'result': True} - self.assertDictEqual(linux_acl.present(name, acl_type, - acl_name, perms), - ret) + with patch.dict(linux_acl.__salt__, {"acl.modfacl": mock_modfacl}): + with patch.dict(linux_acl.__opts__, {"test": False}): + comt = "Updated permissions for {0}".format(acl_name) + ret = { + "name": name, + "comment": comt, + "changes": { + "new": { + "acl_name": acl_name, + "acl_type": acl_type, + "perms": perms, + }, + "old": { + "acl_name": acl_name, + "acl_type": acl_type, + "perms": "r-x", + }, + }, + "result": True, + } + self.assertDictEqual( + linux_acl.present(name, acl_type, acl_name, perms), ret + ) # Update - modfacl error - with patch.dict(linux_acl.__salt__, {'acl.modfacl': MagicMock( - side_effect=CommandExecutionError('Custom err'))}): - with patch.dict(linux_acl.__opts__, {'test': False}): - comt = ('Error updating permissions for {0}: Custom err' - ''.format(acl_name)) - ret = {'name': name, - 'comment': comt, - 'changes': {}, - 'result': False} - self.assertDictEqual(linux_acl.present(name, acl_type, - acl_name, perms), - ret) + with patch.dict( + linux_acl.__salt__, + { + "acl.modfacl": MagicMock( + side_effect=CommandExecutionError("Custom err") + ) + }, + ): + with patch.dict(linux_acl.__opts__, {"test": False}): + comt = "Error updating permissions for {0}: Custom err" "".format( + acl_name + ) + ret = { + "name": name, + "comment": comt, + "changes": {}, + "result": False, + } + self.assertDictEqual( + linux_acl.present(name, acl_type, acl_name, perms), ret + ) # New - test=True - with patch.dict(linux_acl.__salt__, {'acl.modfacl': mock_modfacl}): - with patch.dict(linux_acl.__opts__, {'test': True}): - comt = ('New permissions will be applied ' - 'for {0}: {1}'.format(acl_name, perms)) - ret = {'name': name, - 'comment': comt, - 'changes': {'new': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': perms}}, - 'result': None} - self.assertDictEqual(linux_acl.present(name, acl_type, - acl_name, perms), - ret) + with patch.dict(linux_acl.__salt__, {"acl.modfacl": mock_modfacl}): + with patch.dict(linux_acl.__opts__, {"test": True}): + comt = "New permissions will be applied " "for {0}: {1}".format( + acl_name, perms + ) + ret = { + "name": name, + "comment": comt, + "changes": { + "new": { + "acl_name": acl_name, + "acl_type": acl_type, + "perms": perms, + } + }, + "result": None, + } + self.assertDictEqual( + linux_acl.present(name, acl_type, acl_name, perms), ret + ) # New - test=False - with patch.dict(linux_acl.__salt__, {'acl.modfacl': mock_modfacl}): - with patch.dict(linux_acl.__opts__, {'test': False}): - comt = ('Applied new permissions for {0}'.format(acl_name)) - ret = {'name': name, - 'comment': comt, - 'changes': {'new': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': perms}}, - 'result': True} - self.assertDictEqual(linux_acl.present(name, acl_type, - acl_name, perms), - ret) + with patch.dict(linux_acl.__salt__, {"acl.modfacl": mock_modfacl}): + with patch.dict(linux_acl.__opts__, {"test": False}): + comt = "Applied new permissions for {0}".format(acl_name) + ret = { + "name": name, + "comment": comt, + "changes": { + "new": { + "acl_name": acl_name, + "acl_type": acl_type, + "perms": perms, + } + }, + "result": True, + } + self.assertDictEqual( + linux_acl.present(name, acl_type, acl_name, perms), ret + ) # New - modfacl error - with patch.dict(linux_acl.__salt__, {'acl.modfacl': MagicMock( - side_effect=CommandExecutionError('Custom err'))}): - with patch.dict(linux_acl.__opts__, {'test': False}): - comt = ('Error updating permissions for {0}: Custom err' - ''.format(acl_name)) - ret = {'name': name, - 'comment': comt, - 'changes': {}, - 'result': False} - self.assertDictEqual(linux_acl.present(name, acl_type, - acl_name, perms), - ret) + with patch.dict( + linux_acl.__salt__, + { + "acl.modfacl": MagicMock( + side_effect=CommandExecutionError("Custom err") + ) + }, + ): + with patch.dict(linux_acl.__opts__, {"test": False}): + comt = "Error updating permissions for {0}: Custom err" "".format( + acl_name + ) + ret = { + "name": name, + "comment": comt, + "changes": {}, + "result": False, + } + self.assertDictEqual( + linux_acl.present(name, acl_type, acl_name, perms), ret + ) # New - recurse true - with patch.dict(linux_acl.__salt__, {'acl.getfacl': mock}): + with patch.dict(linux_acl.__salt__, {"acl.getfacl": mock}): # Update - test=True - with patch.dict(linux_acl.__opts__, {'test': True}): - comt = ('Updated permissions will be applied for {0}: rwx -> {1}' - ''.format(acl_name, perms)) - ret = {'name': name, - 'comment': comt, - 'changes': {'new': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': perms}, - 'old': {'acl_name': acl_name, - 'acl_type': acl_type, - 'perms': 'rwx'}}, - 'result': None} + with patch.dict(linux_acl.__opts__, {"test": True}): + comt = ( + "Updated permissions will be applied for {0}: rwx -> {1}" + "".format(acl_name, perms) + ) + ret = { + "name": name, + "comment": comt, + "changes": { + "new": { + "acl_name": acl_name, + "acl_type": acl_type, + "perms": perms, + }, + "old": { + "acl_name": acl_name, + "acl_type": acl_type, + "perms": "rwx", + }, + }, + "result": None, + } - self.assertDictEqual(linux_acl.present(name, acl_type, acl_name, - perms, recurse=False), ret) + self.assertDictEqual( + linux_acl.present( + name, acl_type, acl_name, perms, recurse=False + ), + ret, + ) # New - recurse true - nothing to do - with patch.dict(linux_acl.__salt__, {'acl.getfacl': mock}): + with patch.dict(linux_acl.__salt__, {"acl.getfacl": mock}): # Update - test=True - with patch.dict(linux_acl.__opts__, {'test': True}): - comt = ('Permissions are in the desired state') - ret = {'name': name, - 'comment': comt, - 'changes': {}, - 'result': True} + with patch.dict(linux_acl.__opts__, {"test": True}): + comt = "Permissions are in the desired state" + ret = {"name": name, "comment": comt, "changes": {}, "result": True} - self.assertDictEqual(linux_acl.present(name, acl_type, acl_name, - perms, recurse=True), ret) + self.assertDictEqual( + linux_acl.present( + name, acl_type, acl_name, perms, recurse=True + ), + ret, + ) # No acl type - comt = ('ACL Type does not exist') - ret = {'name': name, 'comment': comt, 'result': False, 'changes': {}} - self.assertDictEqual(linux_acl.present(name, acl_type, acl_name, - perms), ret) + comt = "ACL Type does not exist" + ret = {"name": name, "comment": comt, "result": False, "changes": {}} + self.assertDictEqual( + linux_acl.present(name, acl_type, acl_name, perms), ret + ) # 'absent' function tests: 2 def test_absent(self): - ''' + """ Test to ensure a Linux ACL does not exist - ''' - name = '/root' - acl_type = 'users' - acl_name = 'damian' - perms = 'rwx' + """ + name = "/root" + acl_type = "users" + acl_name = "damian" + perms = "rwx" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[{name: {acl_type: [{acl_name: {'octal': 'A'}}]}}, - {name: {acl_type: ''}}]) - with patch.dict(linux_acl.__salt__, {'acl.getfacl': mock}): - with patch.dict(linux_acl.__opts__, {'test': True}): - comt = ('Removing permissions') - ret.update({'comment': comt}) - self.assertDictEqual(linux_acl.absent(name, acl_type, acl_name, perms), ret) + mock = MagicMock( + side_effect=[ + {name: {acl_type: [{acl_name: {"octal": "A"}}]}}, + {name: {acl_type: ""}}, + ] + ) + with patch.dict(linux_acl.__salt__, {"acl.getfacl": mock}): + with patch.dict(linux_acl.__opts__, {"test": True}): + comt = "Removing permissions" + ret.update({"comment": comt}) + self.assertDictEqual( + linux_acl.absent(name, acl_type, acl_name, perms), ret + ) - comt = ('ACL Type does not exist') - ret.update({'comment': comt, 'result': False}) + comt = "ACL Type does not exist" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(linux_acl.absent(name, acl_type, acl_name, perms), ret) # 'list_present' function tests: 1 def test_list_present(self): - ''' + """ Test to ensure a Linux ACL is present - ''' + """ self.maxDiff = None - name = '/root' - acl_type = 'user' - acl_names = ['root', 'damian', 'homer'] - acl_comment = {u'owner': u'root', - u'group': u'root', - u'file': u'/root'} - perms = 'rwx' + name = "/root" + acl_type = "user" + acl_names = ["root", "damian", "homer"] + acl_comment = {"owner": "root", "group": "root", "file": "/root"} + perms = "rwx" - mock = MagicMock(side_effect=[{name: {acl_type: [{acl_names[0]: - {'octal': 'A'}}, - {acl_names[1]: - {'octal': 'A'}}, - {acl_names[2]: - {'octal': 'A'}}], - 'comment': acl_comment}}, - {name: {acl_type: [{acl_names[0]: - {'octal': 'A'}}, - {acl_names[1]: - {'octal': 'A'}}], - 'comment': acl_comment}}, - {name: {acl_type: [{acl_names[0]: - {'octal': 'A'}}, - {acl_names[1]: - {'octal': 'A'}}]}}, - {name: {acl_type: [{}]}}, - {name: {acl_type: [{}]}}, - {name: {acl_type: [{}]}}, - {name: {acl_type: ''}}]) + mock = MagicMock( + side_effect=[ + { + name: { + acl_type: [ + {acl_names[0]: {"octal": "A"}}, + {acl_names[1]: {"octal": "A"}}, + {acl_names[2]: {"octal": "A"}}, + ], + "comment": acl_comment, + } + }, + { + name: { + acl_type: [ + {acl_names[0]: {"octal": "A"}}, + {acl_names[1]: {"octal": "A"}}, + ], + "comment": acl_comment, + } + }, + { + name: { + acl_type: [ + {acl_names[0]: {"octal": "A"}}, + {acl_names[1]: {"octal": "A"}}, + ] + } + }, + {name: {acl_type: [{}]}}, + {name: {acl_type: [{}]}}, + {name: {acl_type: [{}]}}, + {name: {acl_type: ""}}, + ] + ) mock_modfacl = MagicMock(return_value=True) - with patch.dict(linux_acl.__salt__, {'acl.getfacl': mock}): + with patch.dict(linux_acl.__salt__, {"acl.getfacl": mock}): # Update - test=True - with patch.dict(linux_acl.__opts__, {'test': True}): - comt = ('Updated permissions will be applied for {0}: A -> {1}' - ''.format(acl_names, perms)) - expected = {'name': name, - 'comment': comt, - 'changes': {}, - 'pchanges': {'new': {'acl_name': ', '.join(acl_names), - 'acl_type': acl_type, - 'perms': 7}, - 'old': {'acl_name': ', '.join(acl_names), - 'acl_type': acl_type, - 'perms': 'A'}}, - 'result': None} + with patch.dict(linux_acl.__opts__, {"test": True}): + comt = ( + "Updated permissions will be applied for {0}: A -> {1}" + "".format(acl_names, perms) + ) + expected = { + "name": name, + "comment": comt, + "changes": {}, + "pchanges": { + "new": { + "acl_name": ", ".join(acl_names), + "acl_type": acl_type, + "perms": 7, + }, + "old": { + "acl_name": ", ".join(acl_names), + "acl_type": acl_type, + "perms": "A", + }, + }, + "result": None, + } ret = linux_acl.list_present(name, acl_type, acl_names, perms) self.assertDictEqual(ret, expected) # Update - test=False - with patch.dict(linux_acl.__salt__, {'acl.modfacl': mock_modfacl}): - with patch.dict(linux_acl.__opts__, {'test': False}): - comt = ('Applied new permissions for {0}'.format(', '.join(acl_names))) - expected = {'name': name, - 'comment': comt, - 'changes': {'new': {'acl_name': ', '.join(acl_names), - 'acl_type': acl_type, - 'perms': 'rwx'}}, - 'pchanges': {}, - 'result': True} + with patch.dict(linux_acl.__salt__, {"acl.modfacl": mock_modfacl}): + with patch.dict(linux_acl.__opts__, {"test": False}): + comt = "Applied new permissions for {0}".format( + ", ".join(acl_names) + ) + expected = { + "name": name, + "comment": comt, + "changes": { + "new": { + "acl_name": ", ".join(acl_names), + "acl_type": acl_type, + "perms": "rwx", + } + }, + "pchanges": {}, + "result": True, + } ret = linux_acl.list_present(name, acl_type, acl_names, perms) self.assertDictEqual(expected, ret) # Update - modfacl error - with patch.dict(linux_acl.__salt__, {'acl.modfacl': MagicMock( - side_effect=CommandExecutionError('Custom err'))}): - with patch.dict(linux_acl.__opts__, {'test': False}): - comt = ('Error updating permissions for {0}: Custom err' - ''.format(acl_names)) - expected = {'name': name, - 'comment': comt, - 'changes': {}, - 'pchanges': {}, - 'result': False} + with patch.dict( + linux_acl.__salt__, + { + "acl.modfacl": MagicMock( + side_effect=CommandExecutionError("Custom err") + ) + }, + ): + with patch.dict(linux_acl.__opts__, {"test": False}): + comt = "Error updating permissions for {0}: Custom err" "".format( + acl_names + ) + expected = { + "name": name, + "comment": comt, + "changes": {}, + "pchanges": {}, + "result": False, + } ret = linux_acl.list_present(name, acl_type, acl_names, perms) self.assertDictEqual(expected, ret) # New - test=True - with patch.dict(linux_acl.__salt__, {'acl.modfacl': mock_modfacl}): - with patch.dict(linux_acl.__opts__, {'test': True}): - comt = ('New permissions will be applied ' - 'for {0}: {1}'.format(acl_names, perms)) - expected = {'name': name, - 'comment': comt, - 'changes': {}, - 'pchanges': {'new': {'acl_name': ', '.join(acl_names), - 'acl_type': acl_type, - 'perms': perms}}, - 'result': None} + with patch.dict(linux_acl.__salt__, {"acl.modfacl": mock_modfacl}): + with patch.dict(linux_acl.__opts__, {"test": True}): + comt = "New permissions will be applied " "for {0}: {1}".format( + acl_names, perms + ) + expected = { + "name": name, + "comment": comt, + "changes": {}, + "pchanges": { + "new": { + "acl_name": ", ".join(acl_names), + "acl_type": acl_type, + "perms": perms, + } + }, + "result": None, + } ret = linux_acl.list_present(name, acl_type, acl_names, perms) self.assertDictEqual(expected, ret) # New - test=False - with patch.dict(linux_acl.__salt__, {'acl.modfacl': mock_modfacl}): - with patch.dict(linux_acl.__opts__, {'test': False}): - comt = ('Applied new permissions for {0}'.format(', '.join(acl_names))) - expected = {'name': name, - 'comment': comt, - 'changes': {'new': {'acl_name': ', '.join(acl_names), - 'acl_type': acl_type, - 'perms': perms}}, - 'pchanges': {}, - 'result': True} + with patch.dict(linux_acl.__salt__, {"acl.modfacl": mock_modfacl}): + with patch.dict(linux_acl.__opts__, {"test": False}): + comt = "Applied new permissions for {0}".format( + ", ".join(acl_names) + ) + expected = { + "name": name, + "comment": comt, + "changes": { + "new": { + "acl_name": ", ".join(acl_names), + "acl_type": acl_type, + "perms": perms, + } + }, + "pchanges": {}, + "result": True, + } ret = linux_acl.list_present(name, acl_type, acl_names, perms) self.assertDictEqual(expected, ret) # New - modfacl error - with patch.dict(linux_acl.__salt__, {'acl.modfacl': MagicMock( - side_effect=CommandExecutionError('Custom err'))}): - with patch.dict(linux_acl.__opts__, {'test': False}): - comt = ('Error updating permissions for {0}: Custom err' - ''.format(acl_names)) - expected = {'name': name, - 'comment': comt, - 'changes': {}, - 'pchanges': {}, - 'result': False} + with patch.dict( + linux_acl.__salt__, + { + "acl.modfacl": MagicMock( + side_effect=CommandExecutionError("Custom err") + ) + }, + ): + with patch.dict(linux_acl.__opts__, {"test": False}): + comt = "Error updating permissions for {0}: Custom err" "".format( + acl_names + ) + expected = { + "name": name, + "comment": comt, + "changes": {}, + "pchanges": {}, + "result": False, + } ret = linux_acl.list_present(name, acl_type, acl_names, perms) self.assertDictEqual(expected, ret) # No acl type - comt = ('ACL Type does not exist') - expected = {'name': name, 'comment': comt, 'result': False, - 'changes': {}, 'pchanges': {}} + comt = "ACL Type does not exist" + expected = { + "name": name, + "comment": comt, + "result": False, + "changes": {}, + "pchanges": {}, + } ret = linux_acl.list_present(name, acl_type, acl_names, perms) self.assertDictEqual(expected, ret) # 'list_absent' function tests: 2 def test_list_absent(self): - ''' + """ Test to ensure a Linux ACL does not exist - ''' - name = '/root' - acl_type = 'users' - acl_names = ['damian', 'homer'] - perms = 'rwx' + """ + name = "/root" + acl_type = "users" + acl_names = ["damian", "homer"] + perms = "rwx" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[{name: {acl_type: [{acl_names[0]: {'octal': 'A'}, - acl_names[1]: {'octal': 'A'}}]}}, - {name: {acl_type: ''}}]) - with patch.dict(linux_acl.__salt__, {'acl.getfacl': mock}): - with patch.dict(linux_acl.__opts__, {'test': True}): - comt = ('Removing permissions') - ret.update({'comment': comt}) - self.assertDictEqual(linux_acl.list_absent(name, acl_type, acl_names, perms), ret) + mock = MagicMock( + side_effect=[ + { + name: { + acl_type: [ + {acl_names[0]: {"octal": "A"}, acl_names[1]: {"octal": "A"}} + ] + } + }, + {name: {acl_type: ""}}, + ] + ) + with patch.dict(linux_acl.__salt__, {"acl.getfacl": mock}): + with patch.dict(linux_acl.__opts__, {"test": True}): + comt = "Removing permissions" + ret.update({"comment": comt}) + self.assertDictEqual( + linux_acl.list_absent(name, acl_type, acl_names, perms), ret + ) - comt = ('ACL Type does not exist') - ret.update({'comment': comt, 'result': False}) + comt = "ACL Type does not exist" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(linux_acl.list_absent(name, acl_type, acl_names), ret) def test_absent_recursive(self): - ''' + """ Test to ensure a Linux ACL does not exist - ''' - name = '/root' - acl_type = 'users' - acl_name = 'damian' - perms = 'rwx' + """ + name = "/root" + acl_type = "users" + acl_name = "damian" + perms = "rwx" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[ - {name: {acl_type: [{acl_name: {'octal': 7}}]}, name+"/foo": {acl_type: [{acl_name: {'octal': 'A'}}]}} - ]) - with patch.dict(linux_acl.__salt__, {'acl.getfacl': mock}): - with patch.dict(linux_acl.__opts__, {'test': True}): - comt = ('Removing permissions') - ret.update({'comment': comt}) - self.assertDictEqual(linux_acl.absent(name, acl_type, acl_name, perms, recurse=True), ret) + mock = MagicMock( + side_effect=[ + { + name: {acl_type: [{acl_name: {"octal": 7}}]}, + name + "/foo": {acl_type: [{acl_name: {"octal": "A"}}]}, + } + ] + ) + with patch.dict(linux_acl.__salt__, {"acl.getfacl": mock}): + with patch.dict(linux_acl.__opts__, {"test": True}): + comt = "Removing permissions" + ret.update({"comment": comt}) + self.assertDictEqual( + linux_acl.absent(name, acl_type, acl_name, perms, recurse=True), ret + ) diff --git a/tests/unit/states/test_locale.py b/tests/unit/states/test_locale.py index ec1d3332b82..06b6422467b 100644 --- a/tests/unit/states/test_locale.py +++ b/tests/unit/states/test_locale.py @@ -1,45 +1,58 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.locale as locale +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LocaleTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the locale state - ''' + """ + def setup_loader_modules(self): return {locale: {}} def test_system(self): - ''' + """ Test to set the locale for the system - ''' - ret = [{'changes': {}, 'comment': 'System locale salt already set', - 'name': 'salt', 'result': True}, - {'changes': {}, - 'comment': 'System locale saltstack needs to be set', - 'name': 'saltstack', 'result': None}, - {'changes': {'locale': 'saltstack'}, - 'comment': 'Set system locale saltstack', 'name': 'saltstack', - 'result': True}, - {'changes': {}, - 'comment': 'Failed to set system locale to saltstack', - 'name': 'saltstack', 'result': False}] + """ + ret = [ + { + "changes": {}, + "comment": "System locale salt already set", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "System locale saltstack needs to be set", + "name": "saltstack", + "result": None, + }, + { + "changes": {"locale": "saltstack"}, + "comment": "Set system locale saltstack", + "name": "saltstack", + "result": True, + }, + { + "changes": {}, + "comment": "Failed to set system locale to saltstack", + "name": "saltstack", + "result": False, + }, + ] mock = MagicMock(return_value="salt") with patch.dict(locale.__salt__, {"locale.get_locale": mock}): @@ -56,20 +69,35 @@ class LocaleTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual(locale.system("saltstack"), ret[3]) def test_present(self): - ''' + """ Test to generate a locale if it is not present - ''' - ret = [{'changes': {}, - 'comment': 'Locale salt is already present', 'name': 'salt', - 'result': True}, - {'changes': {}, - 'comment': 'Locale salt needs to be generated', 'name': 'salt', - 'result': None}, - {'changes': {'locale': 'salt'}, - 'comment': 'Generated locale salt', 'name': 'salt', - 'result': True}, - {'changes': {}, 'comment': 'Failed to generate locale salt', - 'name': 'salt', 'result': False}] + """ + ret = [ + { + "changes": {}, + "comment": "Locale salt is already present", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "Locale salt needs to be generated", + "name": "salt", + "result": None, + }, + { + "changes": {"locale": "salt"}, + "comment": "Generated locale salt", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "Failed to generate locale salt", + "name": "salt", + "result": False, + }, + ] mock = MagicMock(side_effect=[True, False, False, False]) with patch.dict(locale.__salt__, {"locale.avail": mock}): diff --git a/tests/unit/states/test_loop.py b/tests/unit/states/test_loop.py index 4d42b5a84e2..e5f9a188dbd 100644 --- a/tests/unit/states/test_loop.py +++ b/tests/unit/states/test_loop.py @@ -1,39 +1,35 @@ # -*- coding: utf-8 -*- -''' +""" Tests for loop state(s) -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Disable pylint complaining about incompatible python3 code and suggesting -# from salt.ext.six.moves import range -# which is not needed (nor used) as the range is used as iterable for both py2 and py3. -# pylint: disable=incompatible-py3-code +# Import Salt Libs +import salt.states.loop +from salt.ext.six.moves import range # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import MagicMock, patch - -# Import Salt Libs -import salt.states.loop +from tests.support.unit import TestCase class LoopTestCase(TestCase, LoaderModuleMockMixin): mock = MagicMock(return_value=True) - func = 'foo.bar' - m_args = ['foo', 'bar', 'baz'] - m_kwargs = {'hello': 'world'} - condition = 'm_ret is True' + func = "foo.bar" + m_args = ["foo", "bar", "baz"] + m_kwargs = {"hello": "world"} + condition = "m_ret is True" period = 1 timeout = 3 def setup_loader_modules(self): return { salt.states.loop: { - '__opts__': {'test': False}, - '__salt__': {self.func: self.mock}, + "__opts__": {"test": False}, + "__salt__": {self.func: self.mock}, } } @@ -47,8 +43,9 @@ class LoopTestCase(TestCase, LoaderModuleMockMixin): m_kwargs=self.m_kwargs, condition=self.condition, period=self.period, - timeout=self.timeout) - assert ret['result'] is True + timeout=self.timeout, + ) + assert ret["result"] is True self.mock.assert_called_once_with(*self.m_args, **self.m_kwargs) def test_until_without_args(self): @@ -57,8 +54,9 @@ class LoopTestCase(TestCase, LoaderModuleMockMixin): m_kwargs=self.m_kwargs, condition=self.condition, period=self.period, - timeout=self.timeout) - assert ret['result'] is True + timeout=self.timeout, + ) + assert ret["result"] is True self.mock.assert_called_once_with(**self.m_kwargs) def test_until_without_kwargs(self): @@ -67,8 +65,9 @@ class LoopTestCase(TestCase, LoaderModuleMockMixin): m_args=self.m_args, condition=self.condition, period=self.period, - timeout=self.timeout) - assert ret['result'] is True + timeout=self.timeout, + ) + assert ret["result"] is True self.mock.assert_called_once_with(*self.m_args) def test_until_without_args_or_kwargs(self): @@ -76,243 +75,286 @@ class LoopTestCase(TestCase, LoaderModuleMockMixin): name=self.func, condition=self.condition, period=self.period, - timeout=self.timeout) - assert ret['result'] is True + timeout=self.timeout, + ) + assert ret["result"] is True self.mock.assert_called_once_with() class LoopTestCaseNoEval(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.loop - ''' + """ + def setup_loader_modules(self): opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils(opts) - return {salt.states.loop: { - '__opts__': opts, - '__utils__': utils, - }} + return {salt.states.loop: {"__opts__": opts, "__utils__": utils}} def test_test_mode(self): - ''' + """ Test response when test_mode is enabled. - ''' - with \ - patch.dict(salt.states.loop.__salt__, { # pylint: disable=no-member - 'foo.foo': True, - }), \ - patch.dict(salt.states.loop.__opts__, { # pylint: disable=no-member - 'test': True, - }): + """ + with patch.dict( + salt.states.loop.__salt__, {"foo.foo": True} # pylint: disable=no-member + ), patch.dict( + salt.states.loop.__opts__, {"test": True} # pylint: disable=no-member + ): self.assertDictEqual( - salt.states.loop.until( - name='foo.foo', - condition='m_ret'), - {'name': 'foo.foo', 'result': None, 'changes': {}, - 'comment': 'The execution module foo.foo will be run'} + salt.states.loop.until(name="foo.foo", condition="m_ret"), + { + "name": "foo.foo", + "result": None, + "changes": {}, + "comment": "The execution module foo.foo will be run", + }, ) self.assertDictEqual( - salt.states.loop.until_no_eval( - name='foo.foo', - expected=2), - {'name': 'foo.foo', 'result': None, 'changes': {}, - 'comment': 'Would have waited for "foo.foo" to produce "2".'} + salt.states.loop.until_no_eval(name="foo.foo", expected=2), + { + "name": "foo.foo", + "result": None, + "changes": {}, + "comment": 'Would have waited for "foo.foo" to produce "2".', + }, ) def test_immediate_success(self): - ''' + """ Test for an immediate success. - ''' - with \ - patch.dict(salt.states.loop.__salt__, { # pylint: disable=no-member - 'foo.foo': lambda: 2, - 'foo.baz': lambda x, y: True, - }), \ - patch.dict(salt.states.loop.__utils__, { # pylint: disable=no-member - 'foo.baz': lambda x, y: True, - }): + """ + with patch.dict( + salt.states.loop.__salt__, + { # pylint: disable=no-member + "foo.foo": lambda: 2, + "foo.baz": lambda x, y: True, + }, + ), patch.dict( + salt.states.loop.__utils__, + {"foo.baz": lambda x, y: True}, # pylint: disable=no-member + ): self.assertDictEqual( - salt.states.loop.until( - name='foo.foo', - condition='m_ret'), - {'name': 'foo.foo', 'result': True, 'changes': {}, - 'comment': 'Condition m_ret was met'} + salt.states.loop.until(name="foo.foo", condition="m_ret"), + { + "name": "foo.foo", + "result": True, + "changes": {}, + "comment": "Condition m_ret was met", + }, ) # Using default compare_operator 'eq' self.assertDictEqual( - salt.states.loop.until_no_eval( - name='foo.foo', - expected=2), - {'name': 'foo.foo', 'result': True, 'changes': {}, - 'comment': 'Call provided the expected results in 1 attempts'} + salt.states.loop.until_no_eval(name="foo.foo", expected=2), + { + "name": "foo.foo", + "result": True, + "changes": {}, + "comment": "Call provided the expected results in 1 attempts", + }, ) # Using compare_operator 'gt' self.assertDictEqual( salt.states.loop.until_no_eval( - name='foo.foo', # Returns 2 - expected=1, - compare_operator='gt'), - {'name': 'foo.foo', 'result': True, 'changes': {}, - 'comment': 'Call provided the expected results in 1 attempts'} + name="foo.foo", expected=1, compare_operator="gt" # Returns 2 + ), + { + "name": "foo.foo", + "result": True, + "changes": {}, + "comment": "Call provided the expected results in 1 attempts", + }, ) # Using compare_operator 'ne' self.assertDictEqual( salt.states.loop.until_no_eval( - name='foo.foo', # Returns 2 - expected=3, - compare_operator='ne'), - {'name': 'foo.foo', 'result': True, 'changes': {}, - 'comment': 'Call provided the expected results in 1 attempts'} + name="foo.foo", expected=3, compare_operator="ne" # Returns 2 + ), + { + "name": "foo.foo", + "result": True, + "changes": {}, + "comment": "Call provided the expected results in 1 attempts", + }, ) # Using __utils__['foo.baz'] as compare_operator self.assertDictEqual( salt.states.loop.until_no_eval( - name='foo.foo', - expected='anything, mocked compare_operator returns True anyway', - compare_operator='foo.baz'), - {'name': 'foo.foo', 'result': True, 'changes': {}, - 'comment': 'Call provided the expected results in 1 attempts'} + name="foo.foo", + expected="anything, mocked compare_operator returns True anyway", + compare_operator="foo.baz", + ), + { + "name": "foo.foo", + "result": True, + "changes": {}, + "comment": "Call provided the expected results in 1 attempts", + }, ) # Using __salt__['foo.baz]' as compare_operator self.assertDictEqual( salt.states.loop.until_no_eval( - name='foo.foo', - expected='anything, mocked compare_operator returns True anyway', - compare_operator='foo.baz'), - {'name': 'foo.foo', 'result': True, 'changes': {}, - 'comment': 'Call provided the expected results in 1 attempts'} + name="foo.foo", + expected="anything, mocked compare_operator returns True anyway", + compare_operator="foo.baz", + ), + { + "name": "foo.foo", + "result": True, + "changes": {}, + "comment": "Call provided the expected results in 1 attempts", + }, ) def test_immediate_failure(self): - ''' + """ Test for an immediate failure. Period and timeout will be set to 0.01 to assume one attempt. - ''' - with patch.dict(salt.states.loop.__salt__, {'foo.bar': lambda: False}): # pylint: disable=no-member + """ + with patch.dict( + salt.states.loop.__salt__, {"foo.bar": lambda: False} + ): # pylint: disable=no-member self.assertDictEqual( salt.states.loop.until( - name='foo.bar', - condition='m_ret', - period=0.01, - timeout=0.01), - {'name': 'foo.bar', 'result': False, 'changes': {}, - 'comment': 'Timed out while waiting for condition m_ret'} + name="foo.bar", condition="m_ret", period=0.01, timeout=0.01 + ), + { + "name": "foo.bar", + "result": False, + "changes": {}, + "comment": "Timed out while waiting for condition m_ret", + }, ) self.assertDictEqual( salt.states.loop.until_no_eval( - name='foo.bar', - expected=True, - period=0.01, - timeout=0.01), - {'name': 'foo.bar', 'result': False, 'changes': {}, - 'comment': 'Call did not produce the expected result after 1 attempts'} + name="foo.bar", expected=True, period=0.01, timeout=0.01 + ), + { + "name": "foo.bar", + "result": False, + "changes": {}, + "comment": "Call did not produce the expected result after 1 attempts", + }, ) def test_eval_exceptions(self): - ''' + """ Test a couple of eval exceptions. - ''' - with patch.dict(salt.states.loop.__salt__, {'foo.bar': lambda: None}): # pylint: disable=no-member + """ + with patch.dict( + salt.states.loop.__salt__, {"foo.bar": lambda: None} + ): # pylint: disable=no-member self.assertRaises( SyntaxError, salt.states.loop.until, - name='foo.bar', - condition='raise NameError("FOO")' + name="foo.bar", + condition='raise NameError("FOO")', ) self.assertRaises( - NameError, - salt.states.loop.until, - name='foo.bar', - condition='foo' + NameError, salt.states.loop.until, name="foo.bar", condition="foo" ) def test_no_eval_exceptions(self): - ''' + """ Test exception handling in until_no_eval. - ''' - with \ - patch.dict( - salt.states.loop.__salt__, # pylint: disable=no-member - {'foo.bar': MagicMock(side_effect=KeyError(str('FOO')))} - ): + """ + with patch.dict( + salt.states.loop.__salt__, # pylint: disable=no-member + {"foo.bar": MagicMock(side_effect=KeyError(str("FOO")))}, + ): self.assertDictEqual( - salt.states.loop.until_no_eval( - name='foo.bar', - expected=True), - {'name': 'foo.bar', 'result': False, 'changes': {}, - 'comment': 'Exception occurred while executing foo.bar: {}:{}'.format(type(KeyError()), "'FOO'")} + salt.states.loop.until_no_eval(name="foo.bar", expected=True), + { + "name": "foo.bar", + "result": False, + "changes": {}, + "comment": "Exception occurred while executing foo.bar: {}:{}".format( + type(KeyError()), "'FOO'" + ), + }, ) def test_retried_success(self): - ''' + """ Test if the function does indeed return after a fixed amount of retries. Note: Do not merge these two tests in one with-block, as the side_effect iterator is shared. - ''' - with \ - patch.dict( - salt.states.loop.__salt__, # pylint: disable=no-member - {'foo.bar': MagicMock(side_effect=range(1, 7))} # pylint: disable=incompatible-py3-code - ): + """ + with patch.dict( + salt.states.loop.__salt__, # pylint: disable=no-member + { + "foo.bar": MagicMock(side_effect=range(1, 7)) + }, # pylint: disable=incompatible-py3-code + ): self.assertDictEqual( salt.states.loop.until( - name='foo.bar', - condition='m_ret == 5', - period=0, - timeout=1), - {'name': 'foo.bar', 'result': True, 'changes': {}, - 'comment': 'Condition m_ret == 5 was met'} + name="foo.bar", condition="m_ret == 5", period=0, timeout=1 + ), + { + "name": "foo.bar", + "result": True, + "changes": {}, + "comment": "Condition m_ret == 5 was met", + }, ) - with \ - patch.dict( - salt.states.loop.__salt__, # pylint: disable=no-member - {'foo.bar': MagicMock(side_effect=range(1, 7))} # pylint: disable=incompatible-py3-code - ): + with patch.dict( + salt.states.loop.__salt__, # pylint: disable=no-member + { + "foo.bar": MagicMock(side_effect=range(1, 7)) + }, # pylint: disable=incompatible-py3-code + ): self.assertDictEqual( salt.states.loop.until_no_eval( - name='foo.bar', - expected=5, - period=0, - timeout=1), - {'name': 'foo.bar', 'result': True, 'changes': {}, - 'comment': 'Call provided the expected results in 5 attempts'} + name="foo.bar", expected=5, period=0, timeout=1 + ), + { + "name": "foo.bar", + "result": True, + "changes": {}, + "comment": "Call provided the expected results in 5 attempts", + }, ) def test_retried_failure(self): - ''' + """ Test if the function fails after the designated timeout. - ''' - with \ - patch.dict( - salt.states.loop.__salt__, # pylint: disable=no-member - {'foo.bar': MagicMock(side_effect=range(1, 7))} # pylint: disable=incompatible-py3-code - ): + """ + with patch.dict( + salt.states.loop.__salt__, # pylint: disable=no-member + { + "foo.bar": MagicMock(side_effect=range(1, 7)) + }, # pylint: disable=incompatible-py3-code + ): self.assertDictEqual( salt.states.loop.until( - name='foo.bar', - condition='m_ret == 5', - period=0.01, - timeout=0.03), - {'name': 'foo.bar', 'result': False, 'changes': {}, - 'comment': 'Timed out while waiting for condition m_ret == 5'} + name="foo.bar", condition="m_ret == 5", period=0.01, timeout=0.03 + ), + { + "name": "foo.bar", + "result": False, + "changes": {}, + "comment": "Timed out while waiting for condition m_ret == 5", + }, ) # period and timeout below has been increased to keep windows machines from # returning a lower number of attempts (because it's slower). - with \ - patch.dict( - salt.states.loop.__salt__, # pylint: disable=no-member - {'foo.bar': MagicMock(side_effect=range(1, 7))} # pylint: disable=incompatible-py3-code - ): + with patch.dict( + salt.states.loop.__salt__, # pylint: disable=no-member + { + "foo.bar": MagicMock(side_effect=range(1, 7)) + }, # pylint: disable=incompatible-py3-code + ): self.assertDictEqual( salt.states.loop.until_no_eval( - name='foo.bar', - expected=5, - period=0.2, - timeout=0.5), - {'name': 'foo.bar', 'result': False, 'changes': {}, - 'comment': 'Call did not produce the expected result after 3 attempts'} + name="foo.bar", expected=5, period=0.2, timeout=0.5 + ), + { + "name": "foo.bar", + "result": False, + "changes": {}, + "comment": "Call did not produce the expected result after 3 attempts", + }, ) diff --git a/tests/unit/states/test_lvm.py b/tests/unit/states/test_lvm.py index 2b99717925d..dfbb32c828b 100644 --- a/tests/unit/states/test_lvm.py +++ b/tests/unit/states/test_lvm.py @@ -1,194 +1,170 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.lvm as lvm +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LvmTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.lvm - ''' + """ + def setup_loader_modules(self): return {lvm: {}} # 'pv_present' function tests: 1 def test_pv_present(self): - ''' + """ Test to set a physical device to be used as an LVM physical volume - ''' - name = '/dev/sda5' + """ + name = "/dev/sda5" - comt = ('Physical Volume {0} already present'.format(name)) + comt = "Physical Volume {0} already present".format(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": True, "comment": comt} mock = MagicMock(side_effect=[True, False]) - with patch.dict(lvm.__salt__, {'lvm.pvdisplay': mock}): + with patch.dict(lvm.__salt__, {"lvm.pvdisplay": mock}): self.assertDictEqual(lvm.pv_present(name), ret) - comt = ('Physical Volume {0} is set to be created'.format(name)) - ret.update({'comment': comt, 'result': None}) - with patch.dict(lvm.__opts__, {'test': True}): + comt = "Physical Volume {0} is set to be created".format(name) + ret.update({"comment": comt, "result": None}) + with patch.dict(lvm.__opts__, {"test": True}): self.assertDictEqual(lvm.pv_present(name), ret) # 'pv_absent' function tests: 1 def test_pv_absent(self): - ''' + """ Test to ensure that a Physical Device is not being used by lvm - ''' - name = '/dev/sda5' + """ + name = "/dev/sda5" - comt = ('Physical Volume {0} does not exist'.format(name)) + comt = "Physical Volume {0} does not exist".format(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": True, "comment": comt} mock = MagicMock(side_effect=[False, True]) - with patch.dict(lvm.__salt__, {'lvm.pvdisplay': mock}): + with patch.dict(lvm.__salt__, {"lvm.pvdisplay": mock}): self.assertDictEqual(lvm.pv_absent(name), ret) - comt = ('Physical Volume {0} is set to be removed'.format(name)) - ret.update({'comment': comt, 'result': None}) - with patch.dict(lvm.__opts__, {'test': True}): + comt = "Physical Volume {0} is set to be removed".format(name) + ret.update({"comment": comt, "result": None}) + with patch.dict(lvm.__opts__, {"test": True}): self.assertDictEqual(lvm.pv_absent(name), ret) # 'vg_present' function tests: 1 def test_vg_present(self): - ''' + """ Test to create an LVM volume group - ''' - name = '/dev/sda5' + """ + name = "/dev/sda5" - comt = ('Failed to create Volume Group {0}'.format(name)) + comt = "Failed to create Volume Group {0}".format(name) - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": False, "comment": comt} mock = MagicMock(return_value=False) - with patch.dict(lvm.__salt__, {'lvm.vgdisplay': mock, - 'lvm.vgcreate': mock}): - with patch.dict(lvm.__opts__, {'test': False}): + with patch.dict(lvm.__salt__, {"lvm.vgdisplay": mock, "lvm.vgcreate": mock}): + with patch.dict(lvm.__opts__, {"test": False}): self.assertDictEqual(lvm.vg_present(name), ret) - comt = ('Volume Group {0} is set to be created'.format(name)) - ret.update({'comment': comt, 'result': None}) - with patch.dict(lvm.__opts__, {'test': True}): + comt = "Volume Group {0} is set to be created".format(name) + ret.update({"comment": comt, "result": None}) + with patch.dict(lvm.__opts__, {"test": True}): self.assertDictEqual(lvm.vg_present(name), ret) # 'vg_absent' function tests: 1 def test_vg_absent(self): - ''' + """ Test to remove an LVM volume group - ''' - name = '/dev/sda5' + """ + name = "/dev/sda5" - comt = ('Volume Group {0} already absent'.format(name)) + comt = "Volume Group {0} already absent".format(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": True, "comment": comt} mock = MagicMock(side_effect=[False, True]) - with patch.dict(lvm.__salt__, {'lvm.vgdisplay': mock}): + with patch.dict(lvm.__salt__, {"lvm.vgdisplay": mock}): self.assertDictEqual(lvm.vg_absent(name), ret) - comt = ('Volume Group {0} is set to be removed'.format(name)) - ret.update({'comment': comt, 'result': None}) - with patch.dict(lvm.__opts__, {'test': True}): + comt = "Volume Group {0} is set to be removed".format(name) + ret.update({"comment": comt, "result": None}) + with patch.dict(lvm.__opts__, {"test": True}): self.assertDictEqual(lvm.vg_absent(name), ret) # 'lv_present' function tests: 1 def test_lv_present(self): - ''' + """ Test to create a new logical volume - ''' - name = '/dev/sda5' + """ + name = "/dev/sda5" - comt = ('Logical Volume {0} already present'.format(name)) + comt = "Logical Volume {0} already present".format(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": True, "comment": comt} mock = MagicMock(side_effect=[True, False]) - with patch.dict(lvm.__salt__, {'lvm.lvdisplay': mock}): + with patch.dict(lvm.__salt__, {"lvm.lvdisplay": mock}): self.assertDictEqual(lvm.lv_present(name), ret) - comt = ('Logical Volume {0} is set to be created'.format(name)) - ret.update({'comment': comt, 'result': None}) - with patch.dict(lvm.__opts__, {'test': True}): + comt = "Logical Volume {0} is set to be created".format(name) + ret.update({"comment": comt, "result": None}) + with patch.dict(lvm.__opts__, {"test": True}): self.assertDictEqual(lvm.lv_present(name), ret) def test_lv_present_with_force(self): - ''' + """ Test to create a new logical volume with force=True - ''' - name = '/dev/sda5' + """ + name = "/dev/sda5" - comt = ('Logical Volume {0} already present'.format(name)) + comt = "Logical Volume {0} already present".format(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": True, "comment": comt} mock = MagicMock(side_effect=[True, False]) - with patch.dict(lvm.__salt__, {'lvm.lvdisplay': mock}): + with patch.dict(lvm.__salt__, {"lvm.lvdisplay": mock}): self.assertDictEqual(lvm.lv_present(name, force=True), ret) - comt = ('Logical Volume {0} is set to be created'.format(name)) - ret.update({'comment': comt, 'result': None}) - with patch.dict(lvm.__opts__, {'test': True}): + comt = "Logical Volume {0} is set to be created".format(name) + ret.update({"comment": comt, "result": None}) + with patch.dict(lvm.__opts__, {"test": True}): self.assertDictEqual(lvm.lv_present(name, force=True), ret) # 'lv_absent' function tests: 1 def test_lv_absent(self): - ''' + """ Test to remove a given existing logical volume from a named existing volume group - ''' - name = '/dev/sda5' + """ + name = "/dev/sda5" - comt = ('Logical Volume {0} already absent'.format(name)) + comt = "Logical Volume {0} already absent".format(name) - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": True, "comment": comt} mock = MagicMock(side_effect=[False, True]) - with patch.dict(lvm.__salt__, {'lvm.lvdisplay': mock}): + with patch.dict(lvm.__salt__, {"lvm.lvdisplay": mock}): self.assertDictEqual(lvm.lv_absent(name), ret) - comt = ('Logical Volume {0} is set to be removed'.format(name)) - ret.update({'comment': comt, 'result': None}) - with patch.dict(lvm.__opts__, {'test': True}): + comt = "Logical Volume {0} is set to be removed".format(name) + ret.update({"comment": comt, "result": None}) + with patch.dict(lvm.__opts__, {"test": True}): self.assertDictEqual(lvm.lv_absent(name), ret) diff --git a/tests/unit/states/test_lvs_server.py b/tests/unit/states/test_lvs_server.py index 402ec90dd75..eb5e7f8ebb0 100644 --- a/tests/unit/states/test_lvs_server.py +++ b/tests/unit/states/test_lvs_server.py @@ -1,125 +1,142 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.lvs_server as lvs_server +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LvsServerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.lvs_server - ''' + """ + def setup_loader_modules(self): return {lvs_server: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named service is present. - ''' - name = 'lvsrs' + """ + name = "lvsrs" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock_check = MagicMock(side_effect=[True, True, True, False, True, - False, True, False, False, False, - False]) + mock_check = MagicMock( + side_effect=[ + True, + True, + True, + False, + True, + False, + True, + False, + False, + False, + False, + ] + ) mock_edit = MagicMock(side_effect=[True, False]) mock_add = MagicMock(side_effect=[True, False]) - with patch.dict(lvs_server.__salt__, {'lvs.check_server': mock_check, - 'lvs.edit_server': mock_edit, - 'lvs.add_server': mock_add}): - with patch.dict(lvs_server.__opts__, {'test': True}): - comt = ('LVS Server lvsrs in service None(None) is present') - ret.update({'comment': comt}) + with patch.dict( + lvs_server.__salt__, + { + "lvs.check_server": mock_check, + "lvs.edit_server": mock_edit, + "lvs.add_server": mock_add, + }, + ): + with patch.dict(lvs_server.__opts__, {"test": True}): + comt = "LVS Server lvsrs in service None(None) is present" + ret.update({"comment": comt}) self.assertDictEqual(lvs_server.present(name), ret) - comt = ('LVS Server lvsrs in service None(None) is present ' - 'but some options should update') - ret.update({'comment': comt, 'result': None}) + comt = ( + "LVS Server lvsrs in service None(None) is present " + "but some options should update" + ) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(lvs_server.present(name), ret) - with patch.dict(lvs_server.__opts__, {'test': False}): - comt = ('LVS Server lvsrs in service None(None) ' - 'has been updated') - ret.update({'comment': comt, 'result': True, - 'changes': {'lvsrs': 'Update'}}) + with patch.dict(lvs_server.__opts__, {"test": False}): + comt = "LVS Server lvsrs in service None(None) " "has been updated" + ret.update( + {"comment": comt, "result": True, "changes": {"lvsrs": "Update"}} + ) self.assertDictEqual(lvs_server.present(name), ret) - comt = ('LVS Server lvsrs in service None(None) ' - 'update failed(False)') - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "LVS Server lvsrs in service None(None) " "update failed(False)" + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(lvs_server.present(name), ret) - with patch.dict(lvs_server.__opts__, {'test': True}): - comt = ('LVS Server lvsrs in service None(None) is not present ' - 'and needs to be created') - ret.update({'comment': comt, 'result': None}) + with patch.dict(lvs_server.__opts__, {"test": True}): + comt = ( + "LVS Server lvsrs in service None(None) is not present " + "and needs to be created" + ) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(lvs_server.present(name), ret) - with patch.dict(lvs_server.__opts__, {'test': False}): - comt = ('LVS Server lvsrs in service None(None) ' - 'has been created') - ret.update({'comment': comt, 'result': True, - 'changes': {'lvsrs': 'Present'}}) + with patch.dict(lvs_server.__opts__, {"test": False}): + comt = "LVS Server lvsrs in service None(None) " "has been created" + ret.update( + {"comment": comt, "result": True, "changes": {"lvsrs": "Present"}} + ) self.assertDictEqual(lvs_server.present(name), ret) - comt = ('LVS Service lvsrs in service None(None) ' - 'create failed(False)') - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "LVS Service lvsrs in service None(None) " "create failed(False)" + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(lvs_server.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the LVS Real Server in specified service is absent. - ''' - name = 'lvsrs' + """ + name = "lvsrs" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} mock_check = MagicMock(side_effect=[True, True, True, False]) mock_delete = MagicMock(side_effect=[True, False]) - with patch.dict(lvs_server.__salt__, {'lvs.check_server': mock_check, - 'lvs.delete_server': mock_delete}): - with patch.dict(lvs_server.__opts__, {'test': True}): - comt = ('LVS Server lvsrs in service None(None) is present' - ' and needs to be removed') - ret.update({'comment': comt}) + with patch.dict( + lvs_server.__salt__, + {"lvs.check_server": mock_check, "lvs.delete_server": mock_delete}, + ): + with patch.dict(lvs_server.__opts__, {"test": True}): + comt = ( + "LVS Server lvsrs in service None(None) is present" + " and needs to be removed" + ) + ret.update({"comment": comt}) self.assertDictEqual(lvs_server.absent(name), ret) - with patch.dict(lvs_server.__opts__, {'test': False}): - comt = ('LVS Server lvsrs in service None(None) ' - 'has been removed') - ret.update({'comment': comt, 'result': True, - 'changes': {'lvsrs': 'Absent'}}) + with patch.dict(lvs_server.__opts__, {"test": False}): + comt = "LVS Server lvsrs in service None(None) " "has been removed" + ret.update( + {"comment": comt, "result": True, "changes": {"lvsrs": "Absent"}} + ) self.assertDictEqual(lvs_server.absent(name), ret) - comt = ('LVS Server lvsrs in service None(None) removed ' - 'failed(False)') - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "LVS Server lvsrs in service None(None) removed " "failed(False)" + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(lvs_server.absent(name), ret) - comt = ('LVS Server lvsrs in service None(None) is not present,' - ' so it cannot be removed') - ret.update({'comment': comt, 'result': True}) + comt = ( + "LVS Server lvsrs in service None(None) is not present," + " so it cannot be removed" + ) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(lvs_server.absent(name), ret) diff --git a/tests/unit/states/test_lvs_service.py b/tests/unit/states/test_lvs_service.py index 9efb46ef535..2d1e0cd8b1b 100644 --- a/tests/unit/states/test_lvs_service.py +++ b/tests/unit/states/test_lvs_service.py @@ -1,118 +1,130 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.lvs_service as lvs_service +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LvsServiceTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.lvs_service - ''' + """ + def setup_loader_modules(self): return {lvs_service: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named service is present. - ''' - name = 'lvsrs' + """ + name = "lvsrs" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock_check = MagicMock(side_effect=[True, True, True, False, True, - False, True, False, False, False, - False]) + mock_check = MagicMock( + side_effect=[ + True, + True, + True, + False, + True, + False, + True, + False, + False, + False, + False, + ] + ) mock_edit = MagicMock(side_effect=[True, False]) mock_add = MagicMock(side_effect=[True, False]) - with patch.dict(lvs_service.__salt__, {'lvs.check_service': mock_check, - 'lvs.edit_service': mock_edit, - 'lvs.add_service': mock_add}): - with patch.dict(lvs_service.__opts__, {'test': True}): - comt = ('LVS Service lvsrs is present') - ret.update({'comment': comt}) + with patch.dict( + lvs_service.__salt__, + { + "lvs.check_service": mock_check, + "lvs.edit_service": mock_edit, + "lvs.add_service": mock_add, + }, + ): + with patch.dict(lvs_service.__opts__, {"test": True}): + comt = "LVS Service lvsrs is present" + ret.update({"comment": comt}) self.assertDictEqual(lvs_service.present(name), ret) - comt = ('LVS Service lvsrs is present but some ' - 'options should update') - ret.update({'comment': comt, 'result': None}) + comt = "LVS Service lvsrs is present but some " "options should update" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(lvs_service.present(name), ret) - with patch.dict(lvs_service.__opts__, {'test': False}): - comt = ('LVS Service lvsrs has been updated') - ret.update({'comment': comt, 'result': True, - 'changes': {'lvsrs': 'Update'}}) + with patch.dict(lvs_service.__opts__, {"test": False}): + comt = "LVS Service lvsrs has been updated" + ret.update( + {"comment": comt, "result": True, "changes": {"lvsrs": "Update"}} + ) self.assertDictEqual(lvs_service.present(name), ret) - comt = ('LVS Service lvsrs update failed') - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "LVS Service lvsrs update failed" + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(lvs_service.present(name), ret) - with patch.dict(lvs_service.__opts__, {'test': True}): - comt = ('LVS Service lvsrs is not present and needs' - ' to be created') - ret.update({'comment': comt, 'result': None}) + with patch.dict(lvs_service.__opts__, {"test": True}): + comt = "LVS Service lvsrs is not present and needs" " to be created" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(lvs_service.present(name), ret) - with patch.dict(lvs_service.__opts__, {'test': False}): - comt = ('LVS Service lvsrs has been created') - ret.update({'comment': comt, 'result': True, - 'changes': {'lvsrs': 'Present'}}) + with patch.dict(lvs_service.__opts__, {"test": False}): + comt = "LVS Service lvsrs has been created" + ret.update( + {"comment": comt, "result": True, "changes": {"lvsrs": "Present"}} + ) self.assertDictEqual(lvs_service.present(name), ret) - comt = ('LVS Service lvsrs create failed(False)') - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "LVS Service lvsrs create failed(False)" + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(lvs_service.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the LVS Real Server in specified service is absent. - ''' - name = 'lvsrs' + """ + name = "lvsrs" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} mock_check = MagicMock(side_effect=[True, True, True, False]) mock_delete = MagicMock(side_effect=[True, False]) - with patch.dict(lvs_service.__salt__, - {'lvs.check_service': mock_check, - 'lvs.delete_service': mock_delete}): - with patch.dict(lvs_service.__opts__, {'test': True}): - comt = ('LVS Service lvsrs is present and needs to be removed') - ret.update({'comment': comt}) + with patch.dict( + lvs_service.__salt__, + {"lvs.check_service": mock_check, "lvs.delete_service": mock_delete}, + ): + with patch.dict(lvs_service.__opts__, {"test": True}): + comt = "LVS Service lvsrs is present and needs to be removed" + ret.update({"comment": comt}) self.assertDictEqual(lvs_service.absent(name), ret) - with patch.dict(lvs_service.__opts__, {'test': False}): - comt = ('LVS Service lvsrs has been removed') - ret.update({'comment': comt, 'result': True, - 'changes': {'lvsrs': 'Absent'}}) + with patch.dict(lvs_service.__opts__, {"test": False}): + comt = "LVS Service lvsrs has been removed" + ret.update( + {"comment": comt, "result": True, "changes": {"lvsrs": "Absent"}} + ) self.assertDictEqual(lvs_service.absent(name), ret) - comt = ('LVS Service lvsrs removed failed(False)') - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "LVS Service lvsrs removed failed(False)" + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(lvs_service.absent(name), ret) - comt = ('LVS Service lvsrs is not present, so it cannot be removed') - ret.update({'comment': comt, 'result': True}) + comt = "LVS Service lvsrs is not present, so it cannot be removed" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(lvs_service.absent(name), ret) diff --git a/tests/unit/states/test_lxc.py b/tests/unit/states/test_lxc.py index 372ded7bce3..f91647e18f2 100644 --- a/tests/unit/states/test_lxc.py +++ b/tests/unit/states/test_lxc.py @@ -1,254 +1,256 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.lxc as lxc import salt.utils.versions +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class LxcTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.lxc - ''' + """ + def setup_loader_modules(self): return {lxc: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to verify the named container if it exist. - ''' - name = 'web01' + """ + name = "web01" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[False, True, True, True, True, True, - True]) - mock_t = MagicMock(side_effect=[None, True, 'frozen', 'frozen', - 'stopped', 'running', 'running']) - with patch.dict(lxc.__salt__, {'lxc.exists': mock, - 'lxc.state': mock_t}): - comt = ("Clone source 'True' does not exist") - ret.update({'comment': comt}) + mock = MagicMock(side_effect=[False, True, True, True, True, True, True]) + mock_t = MagicMock( + side_effect=[ + None, + True, + "frozen", + "frozen", + "stopped", + "running", + "running", + ] + ) + with patch.dict(lxc.__salt__, {"lxc.exists": mock, "lxc.state": mock_t}): + comt = "Clone source 'True' does not exist" + ret.update({"comment": comt}) self.assertDictEqual(lxc.present(name, clone_from=True), ret) - with patch.dict(lxc.__opts__, {'test': True}): - comt = ("Container 'web01' will be cloned from True") - ret.update({'comment': comt, 'result': None}) + with patch.dict(lxc.__opts__, {"test": True}): + comt = "Container 'web01' will be cloned from True" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(lxc.present(name, clone_from=True), ret) - comt = ("Container 'web01' already exists") - ret.update({'comment': comt, 'result': True}) + comt = "Container 'web01' already exists" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(lxc.present(name, clone_from=True), ret) - comt = ("Container 'web01' would be unfrozen") - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(lxc.present(name, running=True, - clone_from=True), ret) + comt = "Container 'web01' would be unfrozen" + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + lxc.present(name, running=True, clone_from=True), ret + ) - comt = ("Container '{0}' would be stopped".format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(lxc.present(name, running=False, - clone_from=True), ret) + comt = "Container '{0}' would be stopped".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + lxc.present(name, running=False, clone_from=True), ret + ) - comt = ("Container 'web01' already exists and is stopped") - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(lxc.present(name, running=False, - clone_from=True), ret) + comt = "Container 'web01' already exists and is stopped" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + lxc.present(name, running=False, clone_from=True), ret + ) - with patch.dict(lxc.__opts__, {'test': False}): - comt = ("Container 'web01' already exists") - ret.update({'comment': comt, 'result': True}) + with patch.dict(lxc.__opts__, {"test": False}): + comt = "Container 'web01' already exists" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(lxc.present(name, clone_from=True), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure a container is not present, destroying it if present. - ''' - name = 'web01' + """ + name = "web01" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(side_effect=[False, True, True]) - mock_des = MagicMock(return_value={'state': True}) - with patch.dict(lxc.__salt__, {'lxc.exists': mock, - 'lxc.destroy': mock_des}): - comt = ("Container '{0}' does not exist".format(name)) - ret.update({'comment': comt}) + mock_des = MagicMock(return_value={"state": True}) + with patch.dict(lxc.__salt__, {"lxc.exists": mock, "lxc.destroy": mock_des}): + comt = "Container '{0}' does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(lxc.absent(name), ret) - with patch.dict(lxc.__opts__, {'test': True}): - comt = ("Container '{0}' would be destroyed".format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(lxc.__opts__, {"test": True}): + comt = "Container '{0}' would be destroyed".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(lxc.absent(name), ret) - with patch.dict(lxc.__opts__, {'test': False}): - comt = ("Container '{0}' was destroyed".format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'state': True}}) + with patch.dict(lxc.__opts__, {"test": False}): + comt = "Container '{0}' was destroyed".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"state": True}} + ) self.assertDictEqual(lxc.absent(name), ret) # 'running' function tests: 1 def test_running(self): - ''' + """ Test to ensure that a container is running. - ''' - name = 'web01' + """ + name = "web01" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock = MagicMock(return_value={'state': {'new': 'stop'}}) - mock_t = MagicMock(side_effect=[None, 'running', 'stopped', 'start']) - with patch.dict(lxc.__salt__, {'lxc.exists': mock, - 'lxc.state': mock_t, - 'lxc.start': mock}): - comt = ("Container '{0}' does not exist".format(name)) - ret.update({'comment': comt}) + mock = MagicMock(return_value={"state": {"new": "stop"}}) + mock_t = MagicMock(side_effect=[None, "running", "stopped", "start"]) + with patch.dict( + lxc.__salt__, {"lxc.exists": mock, "lxc.state": mock_t, "lxc.start": mock} + ): + comt = "Container '{0}' does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(lxc.running(name), ret) - comt = ("Container 'web01' is already running") - ret.update({'comment': comt, 'result': True}) + comt = "Container 'web01' is already running" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(lxc.running(name), ret) - with patch.dict(lxc.__opts__, {'test': True}): - comt = ("Container 'web01' would be started") - ret.update({'comment': comt, 'result': None}) + with patch.dict(lxc.__opts__, {"test": True}): + comt = "Container 'web01' would be started" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(lxc.running(name), ret) - with patch.dict(lxc.__opts__, {'test': False}): - comt = ("Unable to start container 'web01'") - ret.update({'comment': comt, 'result': False, 'changes': - {'state': {'new': 'stop', 'old': 'start'}}}) + with patch.dict(lxc.__opts__, {"test": False}): + comt = "Unable to start container 'web01'" + ret.update( + { + "comment": comt, + "result": False, + "changes": {"state": {"new": "stop", "old": "start"}}, + } + ) self.assertDictEqual(lxc.running(name), ret) # 'frozen' function tests: 1 def test_frozen(self): - ''' + """ Test to ensure that a container is frozen. - ''' - name = 'web01' + """ + name = "web01" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock = MagicMock(return_value={'state': {'new': 'stop'}}) - mock_t = MagicMock(side_effect=['frozen', 'stopped', 'stopped']) - with patch.dict(lxc.__salt__, {'lxc.freeze': mock, - 'lxc.state': mock_t}): - comt = ("Container '{0}' is already frozen".format(name)) - ret.update({'comment': comt}) + mock = MagicMock(return_value={"state": {"new": "stop"}}) + mock_t = MagicMock(side_effect=["frozen", "stopped", "stopped"]) + with patch.dict(lxc.__salt__, {"lxc.freeze": mock, "lxc.state": mock_t}): + comt = "Container '{0}' is already frozen".format(name) + ret.update({"comment": comt}) self.assertDictEqual(lxc.frozen(name), ret) - with patch.dict(lxc.__opts__, {'test': True}): - comt = ("Container 'web01' would be started and frozen") - ret.update({'comment': comt, 'result': None}) + with patch.dict(lxc.__opts__, {"test": True}): + comt = "Container 'web01' would be started and frozen" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(lxc.frozen(name), ret) - with patch.dict(lxc.__opts__, {'test': False}): - comt = ("Unable to start and freeze container 'web01'") - ret.update({'comment': comt, 'result': False, 'changes': - {'state': {'new': 'stop', 'old': 'stopped'}}}) + with patch.dict(lxc.__opts__, {"test": False}): + comt = "Unable to start and freeze container 'web01'" + ret.update( + { + "comment": comt, + "result": False, + "changes": {"state": {"new": "stop", "old": "stopped"}}, + } + ) self.assertDictEqual(lxc.frozen(name), ret) # 'stopped' function tests: 1 def test_stopped(self): - ''' + """ Test to ensure that a container is stopped. - ''' - name = 'web01' + """ + name = "web01" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock = MagicMock(return_value={'state': {'new': 'stop'}}) - mock_t = MagicMock(side_effect=[None, 'stopped', 'frozen', 'frozen']) - with patch.dict(lxc.__salt__, {'lxc.stop': mock, - 'lxc.state': mock_t}): - comt = ("Container '{0}' does not exist".format(name)) - ret.update({'comment': comt}) + mock = MagicMock(return_value={"state": {"new": "stop"}}) + mock_t = MagicMock(side_effect=[None, "stopped", "frozen", "frozen"]) + with patch.dict(lxc.__salt__, {"lxc.stop": mock, "lxc.state": mock_t}): + comt = "Container '{0}' does not exist".format(name) + ret.update({"comment": comt}) self.assertDictEqual(lxc.stopped(name), ret) - comt = ("Container '{0}' is already stopped".format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "Container '{0}' is already stopped".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(lxc.stopped(name), ret) - with patch.dict(lxc.__opts__, {'test': True}): - comt = ("Container 'web01' would be stopped") - ret.update({'comment': comt, 'result': None}) + with patch.dict(lxc.__opts__, {"test": True}): + comt = "Container 'web01' would be stopped" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(lxc.stopped(name), ret) - with patch.dict(lxc.__opts__, {'test': False}): - comt = ("Unable to stop container 'web01'") - ret.update({'comment': comt, 'result': False, 'changes': - {'state': {'new': 'stop', 'old': 'frozen'}}}) + with patch.dict(lxc.__opts__, {"test": False}): + comt = "Unable to stop container 'web01'" + ret.update( + { + "comment": comt, + "result": False, + "changes": {"state": {"new": "stop", "old": "frozen"}}, + } + ) self.assertDictEqual(lxc.stopped(name), ret) # 'set_pass' function tests: 1 def test_set_pass(self): - ''' + """ Test to execute set_pass func. - ''' - comment = ('The lxc.set_pass state is no longer supported. Please see ' - 'the LXC states documentation for further information.') - ret = {'name': 'web01', - 'comment': comment, - 'result': False, - 'changes': {}} + """ + comment = ( + "The lxc.set_pass state is no longer supported. Please see " + "the LXC states documentation for further information." + ) + ret = {"name": "web01", "comment": comment, "result": False, "changes": {}} - self.assertDictEqual(lxc.set_pass('web01'), ret) + self.assertDictEqual(lxc.set_pass("web01"), ret) # 'edited_conf' function tests: 1 def test_edited_conf(self): - ''' + """ Test to edit LXC configuration options - ''' - name = 'web01' + """ + name = "web01" - comment = ('{0} lxc.conf will be edited'.format(name)) + comment = "{0} lxc.conf will be edited".format(name) - ret = {'name': name, - 'result': True, - 'comment': comment, - 'changes': {}} + ret = {"name": name, "result": True, "comment": comment, "changes": {}} - with patch.object(salt.utils.versions, 'warn_until', MagicMock()): - with patch.dict(lxc.__opts__, {'test': True}): + with patch.object(salt.utils.versions, "warn_until", MagicMock()): + with patch.dict(lxc.__opts__, {"test": True}): self.assertDictEqual(lxc.edited_conf(name), ret) - with patch.dict(lxc.__opts__, {'test': False}): + with patch.dict(lxc.__opts__, {"test": False}): mock = MagicMock(return_value={}) - with patch.dict(lxc.__salt__, {'lxc.update_lxc_conf': mock}): - self.assertDictEqual(lxc.edited_conf(name), - {'name': 'web01'}) + with patch.dict(lxc.__salt__, {"lxc.update_lxc_conf": mock}): + self.assertDictEqual(lxc.edited_conf(name), {"name": "web01"}) diff --git a/tests/unit/states/test_mac_assistive.py b/tests/unit/states/test_mac_assistive.py index 04ac8739fba..56c3e0d5a1a 100644 --- a/tests/unit/states/test_mac_assistive.py +++ b/tests/unit/states/test_mac_assistive.py @@ -1,18 +1,15 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.mac_assistive as assistive # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class AssistiveTestCase(TestCase, LoaderModuleMockMixin): @@ -20,35 +17,37 @@ class AssistiveTestCase(TestCase, LoaderModuleMockMixin): return {assistive: {}} def test_installed(self): - ''' + """ Test installing a bundle ID as being allowed to run with assistive access - ''' + """ expected = { - 'changes': {}, - 'comment': 'Installed com.apple.Chess into the assistive access panel', - 'name': 'com.apple.Chess', - 'result': True + "changes": {}, + "comment": "Installed com.apple.Chess into the assistive access panel", + "name": "com.apple.Chess", + "result": True, } installed_mock = MagicMock(return_value=False) install_mock = MagicMock() - with patch.dict(assistive.__salt__, {'assistive.installed': installed_mock, - 'assistive.install': install_mock}): - out = assistive.installed('com.apple.Chess') - installed_mock.assert_called_once_with('com.apple.Chess') - install_mock.assert_called_once_with('com.apple.Chess', True) + with patch.dict( + assistive.__salt__, + {"assistive.installed": installed_mock, "assistive.install": install_mock}, + ): + out = assistive.installed("com.apple.Chess") + installed_mock.assert_called_once_with("com.apple.Chess") + install_mock.assert_called_once_with("com.apple.Chess", True) self.assertEqual(out, expected) def test_installed_not_enabled(self): - ''' + """ Test installing a bundle ID as being allowed to run with assistive access - ''' + """ expected = { - 'changes': {}, - 'comment': 'Updated enable to True', - 'name': 'com.apple.Chess', - 'result': True + "changes": {}, + "comment": "Updated enable to True", + "name": "com.apple.Chess", + "result": True, } installed_mock = MagicMock(return_value=True) @@ -56,25 +55,30 @@ class AssistiveTestCase(TestCase, LoaderModuleMockMixin): enabled_mock = MagicMock(return_value=False) enable_mock = MagicMock() - with patch.dict(assistive.__salt__, {'assistive.installed': installed_mock, - 'assistive.install': install_mock, - 'assistive.enabled': enabled_mock, - 'assistive.enable': enable_mock}): - out = assistive.installed('com.apple.Chess') - enabled_mock.assert_called_once_with('com.apple.Chess') - enable_mock.assert_called_once_with('com.apple.Chess', True) + with patch.dict( + assistive.__salt__, + { + "assistive.installed": installed_mock, + "assistive.install": install_mock, + "assistive.enabled": enabled_mock, + "assistive.enable": enable_mock, + }, + ): + out = assistive.installed("com.apple.Chess") + enabled_mock.assert_called_once_with("com.apple.Chess") + enable_mock.assert_called_once_with("com.apple.Chess", True) assert not install_mock.called self.assertEqual(out, expected) def test_installed_enabled(self): - ''' + """ Test enabling an already enabled bundle ID - ''' + """ expected = { - 'changes': {}, - 'comment': 'Already in the correct state', - 'name': 'com.apple.Chess', - 'result': True + "changes": {}, + "comment": "Already in the correct state", + "name": "com.apple.Chess", + "result": True, } installed_mock = MagicMock(return_value=True) @@ -82,25 +86,30 @@ class AssistiveTestCase(TestCase, LoaderModuleMockMixin): enabled_mock = MagicMock(return_value=True) enable_mock = MagicMock() - with patch.dict(assistive.__salt__, {'assistive.installed': installed_mock, - 'assistive.install': install_mock, - 'assistive.enabled': enabled_mock, - 'assistive.enable': enable_mock}): - out = assistive.installed('com.apple.Chess') - enabled_mock.assert_called_once_with('com.apple.Chess') + with patch.dict( + assistive.__salt__, + { + "assistive.installed": installed_mock, + "assistive.install": install_mock, + "assistive.enabled": enabled_mock, + "assistive.enable": enable_mock, + }, + ): + out = assistive.installed("com.apple.Chess") + enabled_mock.assert_called_once_with("com.apple.Chess") assert not enable_mock.called assert not install_mock.called self.assertEqual(out, expected) def test_installed_not_disabled(self): - ''' + """ Test disabling an enabled and installed bundle ID - ''' + """ expected = { - 'changes': {}, - 'comment': 'Updated enable to False', - 'name': 'com.apple.Chess', - 'result': True + "changes": {}, + "comment": "Updated enable to False", + "name": "com.apple.Chess", + "result": True, } installed_mock = MagicMock(return_value=True) @@ -108,12 +117,17 @@ class AssistiveTestCase(TestCase, LoaderModuleMockMixin): enabled_mock = MagicMock(return_value=True) enable_mock = MagicMock() - with patch.dict(assistive.__salt__, {'assistive.installed': installed_mock, - 'assistive.install': install_mock, - 'assistive.enabled': enabled_mock, - 'assistive.enable': enable_mock}): - out = assistive.installed('com.apple.Chess', False) - enabled_mock.assert_called_once_with('com.apple.Chess') - enable_mock.assert_called_once_with('com.apple.Chess', False) + with patch.dict( + assistive.__salt__, + { + "assistive.installed": installed_mock, + "assistive.install": install_mock, + "assistive.enabled": enabled_mock, + "assistive.enable": enable_mock, + }, + ): + out = assistive.installed("com.apple.Chess", False) + enabled_mock.assert_called_once_with("com.apple.Chess") + enable_mock.assert_called_once_with("com.apple.Chess", False) assert not install_mock.called self.assertEqual(out, expected) diff --git a/tests/unit/states/test_mac_keychain.py b/tests/unit/states/test_mac_keychain.py index eded231b845..391f0641100 100644 --- a/tests/unit/states/test_mac_keychain.py +++ b/tests/unit/states/test_mac_keychain.py @@ -1,19 +1,15 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.mac_keychain as keychain # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - call -) class KeychainTestCase(TestCase, LoaderModuleMockMixin): @@ -21,215 +17,278 @@ class KeychainTestCase(TestCase, LoaderModuleMockMixin): return {keychain: {}} def test_install_cert(self): - ''' + """ Test installing a certificate into the macOS keychain - ''' + """ expected = { - 'changes': {'installed': 'Friendly Name'}, - 'comment': '', - 'name': '/path/to/cert.p12', - 'result': True + "changes": {"installed": "Friendly Name"}, + "comment": "", + "name": "/path/to/cert.p12", + "result": True, } - list_mock = MagicMock(return_value=['Cert1']) - friendly_mock = MagicMock(return_value='Friendly Name') - install_mock = MagicMock(return_value='1 identity imported.') - with patch.dict(keychain.__salt__, {'keychain.list_certs': list_mock, - 'keychain.get_friendly_name': friendly_mock, - 'keychain.install': install_mock}): - out = keychain.installed('/path/to/cert.p12', 'passw0rd') - list_mock.assert_called_once_with('/Library/Keychains/System.keychain') - friendly_mock.assert_called_once_with('/path/to/cert.p12', 'passw0rd') - install_mock.assert_called_once_with('/path/to/cert.p12', 'passw0rd', '/Library/Keychains/System.keychain') + list_mock = MagicMock(return_value=["Cert1"]) + friendly_mock = MagicMock(return_value="Friendly Name") + install_mock = MagicMock(return_value="1 identity imported.") + with patch.dict( + keychain.__salt__, + { + "keychain.list_certs": list_mock, + "keychain.get_friendly_name": friendly_mock, + "keychain.install": install_mock, + }, + ): + out = keychain.installed("/path/to/cert.p12", "passw0rd") + list_mock.assert_called_once_with("/Library/Keychains/System.keychain") + friendly_mock.assert_called_once_with("/path/to/cert.p12", "passw0rd") + install_mock.assert_called_once_with( + "/path/to/cert.p12", "passw0rd", "/Library/Keychains/System.keychain" + ) self.assertEqual(out, expected) def test_installed_cert(self): - ''' + """ Test installing a certificate into the macOS keychain when it's already installed - ''' + """ expected = { - 'changes': {}, - 'comment': 'Friendly Name already installed.', - 'name': '/path/to/cert.p12', - 'result': True + "changes": {}, + "comment": "Friendly Name already installed.", + "name": "/path/to/cert.p12", + "result": True, } - list_mock = MagicMock(return_value=['Friendly Name']) - friendly_mock = MagicMock(return_value='Friendly Name') - install_mock = MagicMock(return_value='1 identity imported.') - hash_mock = MagicMock(return_value='ABCD') - with patch.dict(keychain.__salt__, {'keychain.list_certs': list_mock, - 'keychain.get_friendly_name': friendly_mock, - 'keychain.install': install_mock, - 'keychain.get_hash': hash_mock}): - out = keychain.installed('/path/to/cert.p12', 'passw0rd') - list_mock.assert_called_once_with('/Library/Keychains/System.keychain') - friendly_mock.assert_called_once_with('/path/to/cert.p12', 'passw0rd') + list_mock = MagicMock(return_value=["Friendly Name"]) + friendly_mock = MagicMock(return_value="Friendly Name") + install_mock = MagicMock(return_value="1 identity imported.") + hash_mock = MagicMock(return_value="ABCD") + with patch.dict( + keychain.__salt__, + { + "keychain.list_certs": list_mock, + "keychain.get_friendly_name": friendly_mock, + "keychain.install": install_mock, + "keychain.get_hash": hash_mock, + }, + ): + out = keychain.installed("/path/to/cert.p12", "passw0rd") + list_mock.assert_called_once_with("/Library/Keychains/System.keychain") + friendly_mock.assert_called_once_with("/path/to/cert.p12", "passw0rd") assert not install_mock.called self.assertEqual(out, expected) def test_uninstall_cert(self): - ''' + """ Test uninstalling a certificate into the macOS keychain when it's already installed - ''' + """ expected = { - 'changes': {'uninstalled': 'Friendly Name'}, - 'comment': '', - 'name': '/path/to/cert.p12', - 'result': True + "changes": {"uninstalled": "Friendly Name"}, + "comment": "", + "name": "/path/to/cert.p12", + "result": True, } - list_mock = MagicMock(return_value=['Friendly Name']) - friendly_mock = MagicMock(return_value='Friendly Name') - uninstall_mock = MagicMock(return_value='1 identity imported.') - with patch.dict(keychain.__salt__, {'keychain.list_certs': list_mock, - 'keychain.get_friendly_name': friendly_mock, - 'keychain.uninstall': uninstall_mock}): - out = keychain.uninstalled('/path/to/cert.p12', 'passw0rd') - list_mock.assert_called_once_with('/Library/Keychains/System.keychain') - friendly_mock.assert_called_once_with('/path/to/cert.p12', 'passw0rd') - uninstall_mock.assert_called_once_with('Friendly Name', '/Library/Keychains/System.keychain', None) + list_mock = MagicMock(return_value=["Friendly Name"]) + friendly_mock = MagicMock(return_value="Friendly Name") + uninstall_mock = MagicMock(return_value="1 identity imported.") + with patch.dict( + keychain.__salt__, + { + "keychain.list_certs": list_mock, + "keychain.get_friendly_name": friendly_mock, + "keychain.uninstall": uninstall_mock, + }, + ): + out = keychain.uninstalled("/path/to/cert.p12", "passw0rd") + list_mock.assert_called_once_with("/Library/Keychains/System.keychain") + friendly_mock.assert_called_once_with("/path/to/cert.p12", "passw0rd") + uninstall_mock.assert_called_once_with( + "Friendly Name", "/Library/Keychains/System.keychain", None + ) self.assertEqual(out, expected) def test_uninstalled_cert(self): - ''' + """ Test uninstalling a certificate into the macOS keychain when it's not installed - ''' + """ expected = { - 'changes': {}, - 'comment': 'Friendly Name already uninstalled.', - 'name': '/path/to/cert.p12', - 'result': True + "changes": {}, + "comment": "Friendly Name already uninstalled.", + "name": "/path/to/cert.p12", + "result": True, } - list_mock = MagicMock(return_value=['Cert2']) - friendly_mock = MagicMock(return_value='Friendly Name') - uninstall_mock = MagicMock(return_value='1 identity imported.') - with patch.dict(keychain.__salt__, {'keychain.list_certs': list_mock, - 'keychain.get_friendly_name': friendly_mock, - 'keychain.uninstall': uninstall_mock}): - out = keychain.uninstalled('/path/to/cert.p12', 'passw0rd') - list_mock.assert_called_once_with('/Library/Keychains/System.keychain') - friendly_mock.assert_called_once_with('/path/to/cert.p12', 'passw0rd') + list_mock = MagicMock(return_value=["Cert2"]) + friendly_mock = MagicMock(return_value="Friendly Name") + uninstall_mock = MagicMock(return_value="1 identity imported.") + with patch.dict( + keychain.__salt__, + { + "keychain.list_certs": list_mock, + "keychain.get_friendly_name": friendly_mock, + "keychain.uninstall": uninstall_mock, + }, + ): + out = keychain.uninstalled("/path/to/cert.p12", "passw0rd") + list_mock.assert_called_once_with("/Library/Keychains/System.keychain") + friendly_mock.assert_called_once_with("/path/to/cert.p12", "passw0rd") assert not uninstall_mock.called self.assertEqual(out, expected) def test_default_keychain(self): - ''' + """ Test setting the default keychain - ''' - with patch('os.path.exists') as exists_mock: + """ + with patch("os.path.exists") as exists_mock: expected = { - 'changes': {'default': '/path/to/chain.keychain'}, - 'comment': '', - 'name': '/path/to/chain.keychain', - 'result': True + "changes": {"default": "/path/to/chain.keychain"}, + "comment": "", + "name": "/path/to/chain.keychain", + "result": True, } exists_mock.return_value = True - get_default_mock = MagicMock(return_value='/path/to/other.keychain') - set_mock = MagicMock(return_value='') - with patch.dict(keychain.__salt__, {'keychain.get_default_keychain': get_default_mock, - 'keychain.set_default_keychain': set_mock}): - out = keychain.default_keychain('/path/to/chain.keychain', 'system', 'frank') - get_default_mock.assert_called_once_with('frank', 'system') - set_mock.assert_called_once_with('/path/to/chain.keychain', 'system', 'frank') + get_default_mock = MagicMock(return_value="/path/to/other.keychain") + set_mock = MagicMock(return_value="") + with patch.dict( + keychain.__salt__, + { + "keychain.get_default_keychain": get_default_mock, + "keychain.set_default_keychain": set_mock, + }, + ): + out = keychain.default_keychain( + "/path/to/chain.keychain", "system", "frank" + ) + get_default_mock.assert_called_once_with("frank", "system") + set_mock.assert_called_once_with( + "/path/to/chain.keychain", "system", "frank" + ) self.assertEqual(out, expected) def test_default_keychain_set_already(self): - ''' + """ Test setting the default keychain when it's already set - ''' - with patch('os.path.exists') as exists_mock: + """ + with patch("os.path.exists") as exists_mock: expected = { - 'changes': {}, - 'comment': '/path/to/chain.keychain was already the default keychain.', - 'name': '/path/to/chain.keychain', - 'result': True + "changes": {}, + "comment": "/path/to/chain.keychain was already the default keychain.", + "name": "/path/to/chain.keychain", + "result": True, } exists_mock.return_value = True - get_default_mock = MagicMock(return_value='/path/to/chain.keychain') - set_mock = MagicMock(return_value='') - with patch.dict(keychain.__salt__, {'keychain.get_default_keychain': get_default_mock, - 'keychain.set_default_keychain': set_mock}): - out = keychain.default_keychain('/path/to/chain.keychain', 'system', 'frank') - get_default_mock.assert_called_once_with('frank', 'system') + get_default_mock = MagicMock(return_value="/path/to/chain.keychain") + set_mock = MagicMock(return_value="") + with patch.dict( + keychain.__salt__, + { + "keychain.get_default_keychain": get_default_mock, + "keychain.set_default_keychain": set_mock, + }, + ): + out = keychain.default_keychain( + "/path/to/chain.keychain", "system", "frank" + ) + get_default_mock.assert_called_once_with("frank", "system") assert not set_mock.called self.assertEqual(out, expected) def test_default_keychain_missing(self): - ''' + """ Test setting the default keychain when the keychain is missing - ''' - with patch('os.path.exists') as exists_mock: + """ + with patch("os.path.exists") as exists_mock: expected = { - 'changes': {}, - 'comment': 'Keychain not found at /path/to/cert.p12', - 'name': '/path/to/cert.p12', - 'result': False + "changes": {}, + "comment": "Keychain not found at /path/to/cert.p12", + "name": "/path/to/cert.p12", + "result": False, } exists_mock.return_value = False - out = keychain.default_keychain('/path/to/cert.p12', 'system', 'frank') + out = keychain.default_keychain("/path/to/cert.p12", "system", "frank") self.assertEqual(out, expected) def test_install_cert_salt_fileserver(self): - ''' + """ Test installing a certificate into the macOS keychain from the salt fileserver - ''' + """ expected = { - 'changes': {'installed': 'Friendly Name'}, - 'comment': '', - 'name': 'salt://path/to/cert.p12', - 'result': True + "changes": {"installed": "Friendly Name"}, + "comment": "", + "name": "salt://path/to/cert.p12", + "result": True, } - list_mock = MagicMock(return_value=['Cert1']) - friendly_mock = MagicMock(return_value='Friendly Name') - install_mock = MagicMock(return_value='1 identity imported.') - cp_cache_mock = MagicMock(return_value='/tmp/path/to/cert.p12') - with patch.dict(keychain.__salt__, {'keychain.list_certs': list_mock, - 'keychain.get_friendly_name': friendly_mock, - 'keychain.install': install_mock, - 'cp.cache_file': cp_cache_mock}): - out = keychain.installed('salt://path/to/cert.p12', 'passw0rd') - list_mock.assert_called_once_with('/Library/Keychains/System.keychain') - friendly_mock.assert_called_once_with('/tmp/path/to/cert.p12', 'passw0rd') - install_mock.assert_called_once_with('/tmp/path/to/cert.p12', 'passw0rd', '/Library/Keychains/System.keychain') + list_mock = MagicMock(return_value=["Cert1"]) + friendly_mock = MagicMock(return_value="Friendly Name") + install_mock = MagicMock(return_value="1 identity imported.") + cp_cache_mock = MagicMock(return_value="/tmp/path/to/cert.p12") + with patch.dict( + keychain.__salt__, + { + "keychain.list_certs": list_mock, + "keychain.get_friendly_name": friendly_mock, + "keychain.install": install_mock, + "cp.cache_file": cp_cache_mock, + }, + ): + out = keychain.installed("salt://path/to/cert.p12", "passw0rd") + list_mock.assert_called_once_with("/Library/Keychains/System.keychain") + friendly_mock.assert_called_once_with("/tmp/path/to/cert.p12", "passw0rd") + install_mock.assert_called_once_with( + "/tmp/path/to/cert.p12", + "passw0rd", + "/Library/Keychains/System.keychain", + ) self.assertEqual(out, expected) def test_installed_cert_hash_different(self): - ''' + """ Test installing a certificate into the macOS keychain when it's already installed but the certificate has changed - ''' + """ expected = { - 'changes': {'installed': 'Friendly Name', 'uninstalled': 'Friendly Name'}, - 'comment': 'Found a certificate with the same name but different hash, removing it.\n', - 'name': '/path/to/cert.p12', - 'result': True + "changes": {"installed": "Friendly Name", "uninstalled": "Friendly Name"}, + "comment": "Found a certificate with the same name but different hash, removing it.\n", + "name": "/path/to/cert.p12", + "result": True, } - list_mock = MagicMock(side_effect=[['Friendly Name'], []]) - friendly_mock = MagicMock(return_value='Friendly Name') - install_mock = MagicMock(return_value='1 identity imported.') - uninstall_mock = MagicMock(return_value='removed.') - hash_mock = MagicMock(side_effect=['ABCD', 'XYZ']) - with patch.dict(keychain.__salt__, {'keychain.list_certs': list_mock, - 'keychain.get_friendly_name': friendly_mock, - 'keychain.install': install_mock, - 'keychain.uninstall': uninstall_mock, - 'keychain.get_hash': hash_mock}): - out = keychain.installed('/path/to/cert.p12', 'passw0rd') - list_mock.assert_has_calls(calls=[call('/Library/Keychains/System.keychain'), - call('/Library/Keychains/System.keychain')]) - friendly_mock.assert_called_once_with('/path/to/cert.p12', 'passw0rd') - install_mock.assert_called_once_with('/path/to/cert.p12', 'passw0rd', '/Library/Keychains/System.keychain') - uninstall_mock.assert_called_once_with('Friendly Name', '/Library/Keychains/System.keychain', - keychain_password=None) + list_mock = MagicMock(side_effect=[["Friendly Name"], []]) + friendly_mock = MagicMock(return_value="Friendly Name") + install_mock = MagicMock(return_value="1 identity imported.") + uninstall_mock = MagicMock(return_value="removed.") + hash_mock = MagicMock(side_effect=["ABCD", "XYZ"]) + with patch.dict( + keychain.__salt__, + { + "keychain.list_certs": list_mock, + "keychain.get_friendly_name": friendly_mock, + "keychain.install": install_mock, + "keychain.uninstall": uninstall_mock, + "keychain.get_hash": hash_mock, + }, + ): + out = keychain.installed("/path/to/cert.p12", "passw0rd") + list_mock.assert_has_calls( + calls=[ + call("/Library/Keychains/System.keychain"), + call("/Library/Keychains/System.keychain"), + ] + ) + friendly_mock.assert_called_once_with("/path/to/cert.p12", "passw0rd") + install_mock.assert_called_once_with( + "/path/to/cert.p12", "passw0rd", "/Library/Keychains/System.keychain" + ) + uninstall_mock.assert_called_once_with( + "Friendly Name", + "/Library/Keychains/System.keychain", + keychain_password=None, + ) self.assertEqual(out, expected) diff --git a/tests/unit/states/test_mac_xattr.py b/tests/unit/states/test_mac_xattr.py index 194a623b393..1b13618bc92 100644 --- a/tests/unit/states/test_mac_xattr.py +++ b/tests/unit/states/test_mac_xattr.py @@ -1,141 +1,146 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.mac_xattr as xattr # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class XAttrTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {xattr: {}} def test_exists_not(self): - ''' + """ Test adding an attribute when it doesn't exist - ''' - with patch('os.path.exists') as exists_mock: + """ + with patch("os.path.exists") as exists_mock: expected = { - 'changes': {'key': 'value'}, - 'comment': '', - 'name': '/path/to/file', - 'result': True + "changes": {"key": "value"}, + "comment": "", + "name": "/path/to/file", + "result": True, } exists_mock.return_value = True - list_mock = MagicMock(return_value={'other.id': 'value2'}) + list_mock = MagicMock(return_value={"other.id": "value2"}) write_mock = MagicMock() - with patch.dict(xattr.__salt__, {'xattr.list': list_mock, - 'xattr.write': write_mock}): - out = xattr.exists('/path/to/file', ['key=value']) + with patch.dict( + xattr.__salt__, {"xattr.list": list_mock, "xattr.write": write_mock} + ): + out = xattr.exists("/path/to/file", ["key=value"]) - list_mock.assert_called_once_with('/path/to/file') - write_mock.assert_called_once_with('/path/to/file', 'key', 'value', False) + list_mock.assert_called_once_with("/path/to/file") + write_mock.assert_called_once_with( + "/path/to/file", "key", "value", False + ) self.assertEqual(out, expected) def test_exists_change(self): - ''' + """ Test changing and attribute value - ''' - with patch('os.path.exists') as exists_mock: + """ + with patch("os.path.exists") as exists_mock: expected = { - 'changes': {'key': 'other_value'}, - 'comment': '', - 'name': '/path/to/file', - 'result': True + "changes": {"key": "other_value"}, + "comment": "", + "name": "/path/to/file", + "result": True, } exists_mock.return_value = True - list_mock = MagicMock(return_value={'key': 'value'}) + list_mock = MagicMock(return_value={"key": "value"}) write_mock = MagicMock() - with patch.dict(xattr.__salt__, {'xattr.list': list_mock, - 'xattr.write': write_mock}): - out = xattr.exists('/path/to/file', ['key=other_value']) + with patch.dict( + xattr.__salt__, {"xattr.list": list_mock, "xattr.write": write_mock} + ): + out = xattr.exists("/path/to/file", ["key=other_value"]) - list_mock.assert_called_once_with('/path/to/file') - write_mock.assert_called_once_with('/path/to/file', 'key', 'other_value', False) + list_mock.assert_called_once_with("/path/to/file") + write_mock.assert_called_once_with( + "/path/to/file", "key", "other_value", False + ) self.assertEqual(out, expected) def test_exists_already(self): - ''' + """ Test that having the same value does nothing - ''' - with patch('os.path.exists') as exists_mock: + """ + with patch("os.path.exists") as exists_mock: expected = { - 'changes': {}, - 'comment': 'All values existed correctly.', - 'name': '/path/to/file', - 'result': True + "changes": {}, + "comment": "All values existed correctly.", + "name": "/path/to/file", + "result": True, } exists_mock.return_value = True - list_mock = MagicMock(return_value={'key': 'value'}) + list_mock = MagicMock(return_value={"key": "value"}) write_mock = MagicMock() - with patch.dict(xattr.__salt__, {'xattr.list': list_mock, - 'xattr.write': write_mock}): - out = xattr.exists('/path/to/file', ['key=value']) + with patch.dict( + xattr.__salt__, {"xattr.list": list_mock, "xattr.write": write_mock} + ): + out = xattr.exists("/path/to/file", ["key=value"]) - list_mock.assert_called_once_with('/path/to/file') + list_mock.assert_called_once_with("/path/to/file") assert not write_mock.called self.assertEqual(out, expected) def test_delete(self): - ''' + """ Test deleting an attribute from a file - ''' - with patch('os.path.exists') as exists_mock: + """ + with patch("os.path.exists") as exists_mock: expected = { - 'changes': {'key': 'delete'}, - 'comment': '', - 'name': '/path/to/file', - 'result': True + "changes": {"key": "delete"}, + "comment": "", + "name": "/path/to/file", + "result": True, } exists_mock.return_value = True - list_mock = MagicMock(return_value={'key': 'value2'}) + list_mock = MagicMock(return_value={"key": "value2"}) delete_mock = MagicMock() - with patch.dict(xattr.__salt__, {'xattr.list': list_mock, - 'xattr.delete': delete_mock}): - out = xattr.delete('/path/to/file', ['key']) + with patch.dict( + xattr.__salt__, {"xattr.list": list_mock, "xattr.delete": delete_mock} + ): + out = xattr.delete("/path/to/file", ["key"]) - list_mock.assert_called_once_with('/path/to/file') - delete_mock.assert_called_once_with('/path/to/file', 'key') + list_mock.assert_called_once_with("/path/to/file") + delete_mock.assert_called_once_with("/path/to/file", "key") self.assertEqual(out, expected) def test_delete_not(self): - ''' + """ Test deleting an attribute that doesn't exist from a file - ''' - with patch('os.path.exists') as exists_mock: + """ + with patch("os.path.exists") as exists_mock: expected = { - 'changes': {}, - 'comment': 'All attributes were already deleted.', - 'name': '/path/to/file', - 'result': True + "changes": {}, + "comment": "All attributes were already deleted.", + "name": "/path/to/file", + "result": True, } exists_mock.return_value = True - list_mock = MagicMock(return_value={'other.key': 'value2'}) + list_mock = MagicMock(return_value={"other.key": "value2"}) delete_mock = MagicMock() - with patch.dict(xattr.__salt__, {'xattr.list': list_mock, - 'xattr.delete': delete_mock}): - out = xattr.delete('/path/to/file', ['key']) + with patch.dict( + xattr.__salt__, {"xattr.list": list_mock, "xattr.delete": delete_mock} + ): + out = xattr.delete("/path/to/file", ["key"]) - list_mock.assert_called_once_with('/path/to/file') + list_mock.assert_called_once_with("/path/to/file") assert not delete_mock.called self.assertEqual(out, expected) diff --git a/tests/unit/states/test_macdefaults.py b/tests/unit/states/test_macdefaults.py index 7e43cb4c75b..4e1cfaf9630 100644 --- a/tests/unit/states/test_macdefaults.py +++ b/tests/unit/states/test_macdefaults.py @@ -1,18 +1,15 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.macdefaults as macdefaults # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class MacDefaultsTestCase(TestCase, LoaderModuleMockMixin): @@ -20,155 +17,181 @@ class MacDefaultsTestCase(TestCase, LoaderModuleMockMixin): return {macdefaults: {}} def test_write(self): - ''' + """ Test writing a default setting - ''' + """ expected = { - 'changes': {'written': 'com.apple.CrashReporter DialogType is set to Server'}, - 'comment': '', - 'name': 'DialogType', - 'result': True + "changes": { + "written": "com.apple.CrashReporter DialogType is set to Server" + }, + "comment": "", + "name": "DialogType", + "result": True, } - read_mock = MagicMock(return_value='Local') - write_mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock, - 'macdefaults.write': write_mock}): - out = macdefaults.write('DialogType', 'com.apple.CrashReporter', 'Server') - read_mock.assert_called_once_with('com.apple.CrashReporter', 'DialogType', None) - write_mock.assert_called_once_with('com.apple.CrashReporter', 'DialogType', 'Server', 'string', None) + read_mock = MagicMock(return_value="Local") + write_mock = MagicMock(return_value={"retcode": 0}) + with patch.dict( + macdefaults.__salt__, + {"macdefaults.read": read_mock, "macdefaults.write": write_mock}, + ): + out = macdefaults.write("DialogType", "com.apple.CrashReporter", "Server") + read_mock.assert_called_once_with( + "com.apple.CrashReporter", "DialogType", None + ) + write_mock.assert_called_once_with( + "com.apple.CrashReporter", "DialogType", "Server", "string", None + ) self.assertEqual(out, expected) def test_write_set(self): - ''' + """ Test writing a default setting that is already set - ''' + """ expected = { - 'changes': {}, - 'comment': 'com.apple.CrashReporter DialogType is already set to Server', - 'name': 'DialogType', - 'result': True + "changes": {}, + "comment": "com.apple.CrashReporter DialogType is already set to Server", + "name": "DialogType", + "result": True, } - read_mock = MagicMock(return_value='Server') - write_mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock, - 'macdefaults.write': write_mock}): - out = macdefaults.write('DialogType', 'com.apple.CrashReporter', 'Server') - read_mock.assert_called_once_with('com.apple.CrashReporter', 'DialogType', None) + read_mock = MagicMock(return_value="Server") + write_mock = MagicMock(return_value={"retcode": 0}) + with patch.dict( + macdefaults.__salt__, + {"macdefaults.read": read_mock, "macdefaults.write": write_mock}, + ): + out = macdefaults.write("DialogType", "com.apple.CrashReporter", "Server") + read_mock.assert_called_once_with( + "com.apple.CrashReporter", "DialogType", None + ) assert not write_mock.called self.assertEqual(out, expected) def test_write_boolean(self): - ''' + """ Test writing a default setting with a boolean - ''' + """ expected = { - 'changes': {'written': 'com.apple.something Key is set to True'}, - 'comment': '', - 'name': 'Key', - 'result': True + "changes": {"written": "com.apple.something Key is set to True"}, + "comment": "", + "name": "Key", + "result": True, } - read_mock = MagicMock(return_value='0') - write_mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock, - 'macdefaults.write': write_mock}): - out = macdefaults.write('Key', 'com.apple.something', True, vtype='boolean') - read_mock.assert_called_once_with('com.apple.something', 'Key', None) - write_mock.assert_called_once_with('com.apple.something', 'Key', True, 'boolean', None) + read_mock = MagicMock(return_value="0") + write_mock = MagicMock(return_value={"retcode": 0}) + with patch.dict( + macdefaults.__salt__, + {"macdefaults.read": read_mock, "macdefaults.write": write_mock}, + ): + out = macdefaults.write("Key", "com.apple.something", True, vtype="boolean") + read_mock.assert_called_once_with("com.apple.something", "Key", None) + write_mock.assert_called_once_with( + "com.apple.something", "Key", True, "boolean", None + ) self.assertEqual(out, expected) def test_write_boolean_match(self): - ''' + """ Test writing a default setting with a boolean that is already set to the same value - ''' + """ expected = { - 'changes': {}, - 'comment': 'com.apple.something Key is already set to YES', - 'name': 'Key', - 'result': True + "changes": {}, + "comment": "com.apple.something Key is already set to YES", + "name": "Key", + "result": True, } - read_mock = MagicMock(return_value='1') - write_mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock, - 'macdefaults.write': write_mock}): - out = macdefaults.write('Key', 'com.apple.something', 'YES', vtype='boolean') - read_mock.assert_called_once_with('com.apple.something', 'Key', None) + read_mock = MagicMock(return_value="1") + write_mock = MagicMock(return_value={"retcode": 0}) + with patch.dict( + macdefaults.__salt__, + {"macdefaults.read": read_mock, "macdefaults.write": write_mock}, + ): + out = macdefaults.write( + "Key", "com.apple.something", "YES", vtype="boolean" + ) + read_mock.assert_called_once_with("com.apple.something", "Key", None) assert not write_mock.called self.assertEqual(out, expected) def test_write_integer(self): - ''' + """ Test writing a default setting with a integer - ''' + """ expected = { - 'changes': {'written': 'com.apple.something Key is set to 1337'}, - 'comment': '', - 'name': 'Key', - 'result': True + "changes": {"written": "com.apple.something Key is set to 1337"}, + "comment": "", + "name": "Key", + "result": True, } - read_mock = MagicMock(return_value='99') - write_mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock, - 'macdefaults.write': write_mock}): - out = macdefaults.write('Key', 'com.apple.something', 1337, vtype='integer') - read_mock.assert_called_once_with('com.apple.something', 'Key', None) - write_mock.assert_called_once_with('com.apple.something', 'Key', 1337, 'integer', None) + read_mock = MagicMock(return_value="99") + write_mock = MagicMock(return_value={"retcode": 0}) + with patch.dict( + macdefaults.__salt__, + {"macdefaults.read": read_mock, "macdefaults.write": write_mock}, + ): + out = macdefaults.write("Key", "com.apple.something", 1337, vtype="integer") + read_mock.assert_called_once_with("com.apple.something", "Key", None) + write_mock.assert_called_once_with( + "com.apple.something", "Key", 1337, "integer", None + ) self.assertEqual(out, expected) def test_write_integer_match(self): - ''' + """ Test writing a default setting with a integer that is already set to the same value - ''' + """ expected = { - 'changes': {}, - 'comment': 'com.apple.something Key is already set to 1337', - 'name': 'Key', - 'result': True + "changes": {}, + "comment": "com.apple.something Key is already set to 1337", + "name": "Key", + "result": True, } - read_mock = MagicMock(return_value='1337') - write_mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(macdefaults.__salt__, {'macdefaults.read': read_mock, - 'macdefaults.write': write_mock}): - out = macdefaults.write('Key', 'com.apple.something', 1337, vtype='integer') - read_mock.assert_called_once_with('com.apple.something', 'Key', None) + read_mock = MagicMock(return_value="1337") + write_mock = MagicMock(return_value={"retcode": 0}) + with patch.dict( + macdefaults.__salt__, + {"macdefaults.read": read_mock, "macdefaults.write": write_mock}, + ): + out = macdefaults.write("Key", "com.apple.something", 1337, vtype="integer") + read_mock.assert_called_once_with("com.apple.something", "Key", None) assert not write_mock.called self.assertEqual(out, expected) def test_absent_already(self): - ''' + """ Test ensuring non-existent defaults value is absent - ''' + """ expected = { - 'changes': {}, - 'comment': 'com.apple.something Key is already absent', - 'name': 'Key', - 'result': True + "changes": {}, + "comment": "com.apple.something Key is already absent", + "name": "Key", + "result": True, } - mock = MagicMock(return_value={'retcode': 1}) - with patch.dict(macdefaults.__salt__, {'macdefaults.delete': mock}): - out = macdefaults.absent('Key', 'com.apple.something') - mock.assert_called_once_with('com.apple.something', 'Key', None) + mock = MagicMock(return_value={"retcode": 1}) + with patch.dict(macdefaults.__salt__, {"macdefaults.delete": mock}): + out = macdefaults.absent("Key", "com.apple.something") + mock.assert_called_once_with("com.apple.something", "Key", None) self.assertEqual(out, expected) def test_absent_deleting_existing(self): - ''' + """ Test removing an existing value - ''' + """ expected = { - 'changes': {'absent': 'com.apple.something Key is now absent'}, - 'comment': '', - 'name': 'Key', - 'result': True + "changes": {"absent": "com.apple.something Key is now absent"}, + "comment": "", + "name": "Key", + "result": True, } - mock = MagicMock(return_value={'retcode': 0}) - with patch.dict(macdefaults.__salt__, {'macdefaults.delete': mock}): - out = macdefaults.absent('Key', 'com.apple.something') - mock.assert_called_once_with('com.apple.something', 'Key', None) + mock = MagicMock(return_value={"retcode": 0}) + with patch.dict(macdefaults.__salt__, {"macdefaults.delete": mock}): + out = macdefaults.absent("Key", "com.apple.something") + mock.assert_called_once_with("com.apple.something", "Key", None) self.assertEqual(out, expected) diff --git a/tests/unit/states/test_macpackage.py b/tests/unit/states/test_macpackage.py index 2d8e167bf77..0ca1842e96b 100644 --- a/tests/unit/states/test_macpackage.py +++ b/tests/unit/states/test_macpackage.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import sys # Import Salt Libs @@ -9,360 +10,440 @@ import salt.states.macpackage as macpackage # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch -) -@skipIf(sys.platform.startswith('win'), "Not a Windows test") +@skipIf(sys.platform.startswith("win"), "Not a Windows test") class MacPackageTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return {macpackage: {}} def test_installed_pkg(self): - ''' + """ Test installing a PKG file - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock: + """ + with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock: expected = { - 'changes': {'installed': ['some.other.id']}, - 'comment': '/path/to/file.pkg installed', - 'name': '/path/to/file.pkg', - 'result': True + "changes": {"installed": ["some.other.id"]}, + "comment": "/path/to/file.pkg installed", + "name": "/path/to/file.pkg", + "result": True, } - installed_mock = MagicMock(return_value=['com.apple.id']) - get_pkg_id_mock = MagicMock(return_value=['some.other.id']) - install_mock = MagicMock(return_value={'retcode': 0}) + installed_mock = MagicMock(return_value=["com.apple.id"]) + get_pkg_id_mock = MagicMock(return_value=["some.other.id"]) + install_mock = MagicMock(return_value={"retcode": 0}) _mod_run_check_mock.return_value = True - with patch.dict(macpackage.__salt__, {'macpackage.installed_pkgs': installed_mock, - 'macpackage.get_pkg_id': get_pkg_id_mock, - 'macpackage.install': install_mock}): - out = macpackage.installed('/path/to/file.pkg') + with patch.dict( + macpackage.__salt__, + { + "macpackage.installed_pkgs": installed_mock, + "macpackage.get_pkg_id": get_pkg_id_mock, + "macpackage.install": install_mock, + }, + ): + out = macpackage.installed("/path/to/file.pkg") installed_mock.assert_called_once_with() - get_pkg_id_mock.assert_called_once_with('/path/to/file.pkg') - install_mock.assert_called_once_with('/path/to/file.pkg', 'LocalSystem', False, False) + get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg") + install_mock.assert_called_once_with( + "/path/to/file.pkg", "LocalSystem", False, False + ) self.assertEqual(out, expected) def test_installed_pkg_exists(self): - ''' + """ Test installing a PKG file where it's already installed - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock: + """ + with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock: expected = { - 'changes': {}, - 'comment': '', - 'name': '/path/to/file.pkg', - 'result': True + "changes": {}, + "comment": "", + "name": "/path/to/file.pkg", + "result": True, } - installed_mock = MagicMock(return_value=['com.apple.id', 'some.other.id']) - get_pkg_id_mock = MagicMock(return_value=['some.other.id']) - install_mock = MagicMock(return_value={'retcode': 0}) + installed_mock = MagicMock(return_value=["com.apple.id", "some.other.id"]) + get_pkg_id_mock = MagicMock(return_value=["some.other.id"]) + install_mock = MagicMock(return_value={"retcode": 0}) _mod_run_check_mock.return_value = True - with patch.dict(macpackage.__salt__, {'macpackage.installed_pkgs': installed_mock, - 'macpackage.get_pkg_id': get_pkg_id_mock, - 'macpackage.install': install_mock}): - out = macpackage.installed('/path/to/file.pkg') + with patch.dict( + macpackage.__salt__, + { + "macpackage.installed_pkgs": installed_mock, + "macpackage.get_pkg_id": get_pkg_id_mock, + "macpackage.install": install_mock, + }, + ): + out = macpackage.installed("/path/to/file.pkg") installed_mock.assert_called_once_with() - get_pkg_id_mock.assert_called_once_with('/path/to/file.pkg') + get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg") assert not install_mock.called self.assertEqual(out, expected) def test_installed_pkg_version_succeeds(self): - ''' + """ Test installing a PKG file where the version number matches the current installed version - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock: + """ + with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock: expected = { - 'changes': {}, - 'comment': 'Version already matches .*5\\.1\\.[0-9]', - 'name': '/path/to/file.pkg', - 'result': True + "changes": {}, + "comment": "Version already matches .*5\\.1\\.[0-9]", + "name": "/path/to/file.pkg", + "result": True, } - installed_mock = MagicMock(return_value=['com.apple.id', 'some.other.id']) - get_pkg_id_mock = MagicMock(return_value=['some.other.id']) - install_mock = MagicMock(return_value={'retcode': 0}) - cmd_mock = MagicMock(return_value='Version of this: 5.1.9') + installed_mock = MagicMock(return_value=["com.apple.id", "some.other.id"]) + get_pkg_id_mock = MagicMock(return_value=["some.other.id"]) + install_mock = MagicMock(return_value={"retcode": 0}) + cmd_mock = MagicMock(return_value="Version of this: 5.1.9") _mod_run_check_mock.return_value = True - with patch.dict(macpackage.__salt__, {'macpackage.installed_pkgs': installed_mock, - 'macpackage.get_pkg_id': get_pkg_id_mock, - 'macpackage.install': install_mock, - 'cmd.run': cmd_mock}): - out = macpackage.installed('/path/to/file.pkg', version_check=r'/usr/bin/runme --version=.*5\.1\.[0-9]') - cmd_mock.assert_called_once_with('/usr/bin/runme --version', output_loglevel="quiet", ignore_retcode=True) + with patch.dict( + macpackage.__salt__, + { + "macpackage.installed_pkgs": installed_mock, + "macpackage.get_pkg_id": get_pkg_id_mock, + "macpackage.install": install_mock, + "cmd.run": cmd_mock, + }, + ): + out = macpackage.installed( + "/path/to/file.pkg", + version_check=r"/usr/bin/runme --version=.*5\.1\.[0-9]", + ) + cmd_mock.assert_called_once_with( + "/usr/bin/runme --version", + output_loglevel="quiet", + ignore_retcode=True, + ) assert not installed_mock.called assert not get_pkg_id_mock.called assert not install_mock.called self.assertEqual(out, expected) def test_installed_pkg_version_fails(self): - ''' + """ Test installing a PKG file where the version number if different from the expected one - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock: + """ + with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock: expected = { - 'changes': {'installed': ['some.other.id']}, - 'comment': 'Version Version of this: 1.8.9 doesn\'t match .*5\\.1\\.[0-9]. /path/to/file.pkg installed', - 'name': '/path/to/file.pkg', - 'result': True + "changes": {"installed": ["some.other.id"]}, + "comment": "Version Version of this: 1.8.9 doesn't match .*5\\.1\\.[0-9]. /path/to/file.pkg installed", + "name": "/path/to/file.pkg", + "result": True, } - installed_mock = MagicMock(return_value=['com.apple.id']) - get_pkg_id_mock = MagicMock(return_value=['some.other.id']) - install_mock = MagicMock(return_value={'retcode': 0}) - cmd_mock = MagicMock(return_value='Version of this: 1.8.9') + installed_mock = MagicMock(return_value=["com.apple.id"]) + get_pkg_id_mock = MagicMock(return_value=["some.other.id"]) + install_mock = MagicMock(return_value={"retcode": 0}) + cmd_mock = MagicMock(return_value="Version of this: 1.8.9") _mod_run_check_mock.return_value = True - with patch.dict(macpackage.__salt__, {'macpackage.installed_pkgs': installed_mock, - 'macpackage.get_pkg_id': get_pkg_id_mock, - 'macpackage.install': install_mock, - 'cmd.run': cmd_mock}): - out = macpackage.installed('/path/to/file.pkg', version_check=r'/usr/bin/runme --version=.*5\.1\.[0-9]') - cmd_mock.assert_called_once_with('/usr/bin/runme --version', output_loglevel="quiet", ignore_retcode=True) + with patch.dict( + macpackage.__salt__, + { + "macpackage.installed_pkgs": installed_mock, + "macpackage.get_pkg_id": get_pkg_id_mock, + "macpackage.install": install_mock, + "cmd.run": cmd_mock, + }, + ): + out = macpackage.installed( + "/path/to/file.pkg", + version_check=r"/usr/bin/runme --version=.*5\.1\.[0-9]", + ) + cmd_mock.assert_called_once_with( + "/usr/bin/runme --version", + output_loglevel="quiet", + ignore_retcode=True, + ) installed_mock.assert_called_once_with() - get_pkg_id_mock.assert_called_once_with('/path/to/file.pkg') - install_mock.assert_called_once_with('/path/to/file.pkg', 'LocalSystem', False, False) + get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg") + install_mock.assert_called_once_with( + "/path/to/file.pkg", "LocalSystem", False, False + ) self.assertEqual(out, expected) def test_installed_dmg(self): - ''' + """ Test installing a DMG file - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock: + """ + with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock: expected = { - 'changes': {'installed': ['some.other.id']}, - 'comment': '/path/to/file.dmg installed', - 'name': '/path/to/file.dmg', - 'result': True + "changes": {"installed": ["some.other.id"]}, + "comment": "/path/to/file.dmg installed", + "name": "/path/to/file.dmg", + "result": True, } - mount_mock = MagicMock(return_value=['success', '/tmp/dmg-X']) + mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"]) unmount_mock = MagicMock() - installed_mock = MagicMock(return_value=['com.apple.id']) - get_pkg_id_mock = MagicMock(return_value=['some.other.id']) - install_mock = MagicMock(return_value={'retcode': 0}) + installed_mock = MagicMock(return_value=["com.apple.id"]) + get_pkg_id_mock = MagicMock(return_value=["some.other.id"]) + install_mock = MagicMock(return_value={"retcode": 0}) _mod_run_check_mock.return_value = True - with patch.dict(macpackage.__salt__, {'macpackage.mount': mount_mock, - 'macpackage.unmount': unmount_mock, - 'macpackage.installed_pkgs': installed_mock, - 'macpackage.get_pkg_id': get_pkg_id_mock, - 'macpackage.install': install_mock}): - out = macpackage.installed('/path/to/file.dmg', dmg=True) - mount_mock.assert_called_once_with('/path/to/file.dmg') - unmount_mock.assert_called_once_with('/tmp/dmg-X') + with patch.dict( + macpackage.__salt__, + { + "macpackage.mount": mount_mock, + "macpackage.unmount": unmount_mock, + "macpackage.installed_pkgs": installed_mock, + "macpackage.get_pkg_id": get_pkg_id_mock, + "macpackage.install": install_mock, + }, + ): + out = macpackage.installed("/path/to/file.dmg", dmg=True) + mount_mock.assert_called_once_with("/path/to/file.dmg") + unmount_mock.assert_called_once_with("/tmp/dmg-X") installed_mock.assert_called_once_with() - get_pkg_id_mock.assert_called_once_with('/tmp/dmg-X/*.pkg') - install_mock.assert_called_once_with('/tmp/dmg-X/*.pkg', 'LocalSystem', False, False) + get_pkg_id_mock.assert_called_once_with("/tmp/dmg-X/*.pkg") + install_mock.assert_called_once_with( + "/tmp/dmg-X/*.pkg", "LocalSystem", False, False + ) self.assertEqual(out, expected) def test_installed_dmg_exists(self): - ''' + """ Test installing a DMG file when the package already exists - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock: + """ + with patch("salt.states.macpackage._mod_run_check") as _mod_run_check_mock: expected = { - 'changes': {}, - 'comment': '', - 'name': '/path/to/file.dmg', - 'result': True + "changes": {}, + "comment": "", + "name": "/path/to/file.dmg", + "result": True, } - mount_mock = MagicMock(return_value=['success', '/tmp/dmg-X']) + mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"]) unmount_mock = MagicMock() - installed_mock = MagicMock(return_value=['com.apple.id', 'some.other.id']) - get_pkg_id_mock = MagicMock(return_value=['some.other.id']) - install_mock = MagicMock(return_value={'retcode': 0}) + installed_mock = MagicMock(return_value=["com.apple.id", "some.other.id"]) + get_pkg_id_mock = MagicMock(return_value=["some.other.id"]) + install_mock = MagicMock(return_value={"retcode": 0}) _mod_run_check_mock.return_value = True - with patch.dict(macpackage.__salt__, {'macpackage.mount': mount_mock, - 'macpackage.unmount': unmount_mock, - 'macpackage.installed_pkgs': installed_mock, - 'macpackage.get_pkg_id': get_pkg_id_mock, - 'macpackage.install': install_mock}): - out = macpackage.installed('/path/to/file.dmg', dmg=True) - mount_mock.assert_called_once_with('/path/to/file.dmg') - unmount_mock.assert_called_once_with('/tmp/dmg-X') + with patch.dict( + macpackage.__salt__, + { + "macpackage.mount": mount_mock, + "macpackage.unmount": unmount_mock, + "macpackage.installed_pkgs": installed_mock, + "macpackage.get_pkg_id": get_pkg_id_mock, + "macpackage.install": install_mock, + }, + ): + out = macpackage.installed("/path/to/file.dmg", dmg=True) + mount_mock.assert_called_once_with("/path/to/file.dmg") + unmount_mock.assert_called_once_with("/tmp/dmg-X") installed_mock.assert_called_once_with() - get_pkg_id_mock.assert_called_once_with('/tmp/dmg-X/*.pkg') + get_pkg_id_mock.assert_called_once_with("/tmp/dmg-X/*.pkg") assert not install_mock.called self.assertEqual(out, expected) def test_installed_app(self): - ''' + """ Test installing an APP file - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock, \ - patch('os.path.exists') as exists_mock: + """ + with patch( + "salt.states.macpackage._mod_run_check" + ) as _mod_run_check_mock, patch("os.path.exists") as exists_mock: expected = { - 'changes': {'installed': ['file.app']}, - 'comment': 'file.app installed', - 'name': '/path/to/file.app', - 'result': True + "changes": {"installed": ["file.app"]}, + "comment": "file.app installed", + "name": "/path/to/file.app", + "result": True, } install_mock = MagicMock() _mod_run_check_mock.return_value = True exists_mock.return_value = False - with patch.dict(macpackage.__salt__, {'macpackage.install_app': install_mock}): - out = macpackage.installed('/path/to/file.app', app=True) + with patch.dict( + macpackage.__salt__, {"macpackage.install_app": install_mock} + ): + out = macpackage.installed("/path/to/file.app", app=True) - install_mock.assert_called_once_with('/path/to/file.app', '/Applications/') + install_mock.assert_called_once_with( + "/path/to/file.app", "/Applications/" + ) self.assertEqual(out, expected) def test_installed_app_exists(self): - ''' + """ Test installing an APP file that already exists - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock, \ - patch('os.path.exists') as exists_mock: + """ + with patch( + "salt.states.macpackage._mod_run_check" + ) as _mod_run_check_mock, patch("os.path.exists") as exists_mock: expected = { - 'changes': {}, - 'comment': '', - 'name': '/path/to/file.app', - 'result': True + "changes": {}, + "comment": "", + "name": "/path/to/file.app", + "result": True, } install_mock = MagicMock() _mod_run_check_mock.return_value = True exists_mock.return_value = True - with patch.dict(macpackage.__salt__, {'macpackage.install_app': install_mock}): - out = macpackage.installed('/path/to/file.app', app=True) + with patch.dict( + macpackage.__salt__, {"macpackage.install_app": install_mock} + ): + out = macpackage.installed("/path/to/file.app", app=True) assert not install_mock.called self.assertEqual(out, expected) def test_installed_app_dmg(self): - ''' + """ Test installing an APP file contained in a DMG file - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock, \ - patch('os.path.exists') as exists_mock: + """ + with patch( + "salt.states.macpackage._mod_run_check" + ) as _mod_run_check_mock, patch("os.path.exists") as exists_mock: expected = { - 'changes': {'installed': ['file.app']}, - 'comment': 'file.app installed', - 'name': '/path/to/file.dmg', - 'result': True + "changes": {"installed": ["file.app"]}, + "comment": "file.app installed", + "name": "/path/to/file.dmg", + "result": True, } install_mock = MagicMock() - mount_mock = MagicMock(return_value=['success', '/tmp/dmg-X']) + mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"]) unmount_mock = MagicMock() - cmd_mock = MagicMock(return_value='file.app') + cmd_mock = MagicMock(return_value="file.app") _mod_run_check_mock.return_value = True exists_mock.return_value = False - with patch.dict(macpackage.__salt__, {'macpackage.install_app': install_mock, - 'macpackage.mount': mount_mock, - 'macpackage.unmount': unmount_mock, - 'cmd.run': cmd_mock}): - out = macpackage.installed('/path/to/file.dmg', app=True, dmg=True) + with patch.dict( + macpackage.__salt__, + { + "macpackage.install_app": install_mock, + "macpackage.mount": mount_mock, + "macpackage.unmount": unmount_mock, + "cmd.run": cmd_mock, + }, + ): + out = macpackage.installed("/path/to/file.dmg", app=True, dmg=True) - mount_mock.assert_called_once_with('/path/to/file.dmg') - unmount_mock.assert_called_once_with('/tmp/dmg-X') - cmd_mock.assert_called_once_with('ls -d *.app', python_shell=True, cwd='/tmp/dmg-X') - install_mock.assert_called_once_with('/tmp/dmg-X/file.app', '/Applications/') + mount_mock.assert_called_once_with("/path/to/file.dmg") + unmount_mock.assert_called_once_with("/tmp/dmg-X") + cmd_mock.assert_called_once_with( + "ls -d *.app", python_shell=True, cwd="/tmp/dmg-X" + ) + install_mock.assert_called_once_with( + "/tmp/dmg-X/file.app", "/Applications/" + ) self.assertEqual(out, expected) def test_installed_app_dmg_exists(self): - ''' + """ Test installing an APP file contained in a DMG file where the file exists - ''' - with patch('salt.states.macpackage._mod_run_check') as _mod_run_check_mock, \ - patch('os.path.exists') as exists_mock: + """ + with patch( + "salt.states.macpackage._mod_run_check" + ) as _mod_run_check_mock, patch("os.path.exists") as exists_mock: expected = { - 'changes': {}, - 'comment': '', - 'name': '/path/to/file.dmg', - 'result': True + "changes": {}, + "comment": "", + "name": "/path/to/file.dmg", + "result": True, } install_mock = MagicMock() - mount_mock = MagicMock(return_value=['success', '/tmp/dmg-X']) + mount_mock = MagicMock(return_value=["success", "/tmp/dmg-X"]) unmount_mock = MagicMock() - cmd_mock = MagicMock(return_value='file.app') + cmd_mock = MagicMock(return_value="file.app") _mod_run_check_mock.return_value = True exists_mock.return_value = True - with patch.dict(macpackage.__salt__, {'macpackage.install_app': install_mock, - 'macpackage.mount': mount_mock, - 'macpackage.unmount': unmount_mock, - 'cmd.run': cmd_mock}): - out = macpackage.installed('/path/to/file.dmg', app=True, dmg=True) + with patch.dict( + macpackage.__salt__, + { + "macpackage.install_app": install_mock, + "macpackage.mount": mount_mock, + "macpackage.unmount": unmount_mock, + "cmd.run": cmd_mock, + }, + ): + out = macpackage.installed("/path/to/file.dmg", app=True, dmg=True) - mount_mock.assert_called_once_with('/path/to/file.dmg') - unmount_mock.assert_called_once_with('/tmp/dmg-X') - cmd_mock.assert_called_once_with('ls -d *.app', python_shell=True, cwd='/tmp/dmg-X') + mount_mock.assert_called_once_with("/path/to/file.dmg") + unmount_mock.assert_called_once_with("/tmp/dmg-X") + cmd_mock.assert_called_once_with( + "ls -d *.app", python_shell=True, cwd="/tmp/dmg-X" + ) assert not install_mock.called self.assertEqual(out, expected) def test_installed_pkg_only_if_pass(self): - ''' + """ Test installing a PKG file where the only if call passes - ''' + """ expected = { - 'changes': {'installed': ['some.other.id']}, - 'comment': '/path/to/file.pkg installed', - 'name': '/path/to/file.pkg', - 'result': True + "changes": {"installed": ["some.other.id"]}, + "comment": "/path/to/file.pkg installed", + "name": "/path/to/file.pkg", + "result": True, } - installed_mock = MagicMock(return_value=['com.apple.id']) - get_pkg_id_mock = MagicMock(return_value=['some.other.id']) - install_mock = MagicMock(return_value={'retcode': 0}) + installed_mock = MagicMock(return_value=["com.apple.id"]) + get_pkg_id_mock = MagicMock(return_value=["some.other.id"]) + install_mock = MagicMock(return_value={"retcode": 0}) cmd_mock = MagicMock(return_value=0) - with patch.dict(macpackage.__salt__, {'macpackage.installed_pkgs': installed_mock, - 'macpackage.get_pkg_id': get_pkg_id_mock, - 'macpackage.install': install_mock, - 'cmd.retcode': cmd_mock}): - out = macpackage.installed('/path/to/file.pkg') + with patch.dict( + macpackage.__salt__, + { + "macpackage.installed_pkgs": installed_mock, + "macpackage.get_pkg_id": get_pkg_id_mock, + "macpackage.install": install_mock, + "cmd.retcode": cmd_mock, + }, + ): + out = macpackage.installed("/path/to/file.pkg") installed_mock.assert_called_once_with() - get_pkg_id_mock.assert_called_once_with('/path/to/file.pkg') - install_mock.assert_called_once_with('/path/to/file.pkg', 'LocalSystem', False, False) + get_pkg_id_mock.assert_called_once_with("/path/to/file.pkg") + install_mock.assert_called_once_with( + "/path/to/file.pkg", "LocalSystem", False, False + ) self.assertEqual(out, expected) def test_installed_pkg_onlyif_fail(self,): - ''' + """ Test installing a PKG file where the onlyif call fails - ''' + """ expected = { - 'changes': {}, - 'comment': 'onlyif condition is false', - 'skip_watch': True, - 'result': True, - 'name': '/path/to/file.pkg', + "changes": {}, + "comment": "onlyif condition is false", + "skip_watch": True, + "result": True, + "name": "/path/to/file.pkg", } mock = MagicMock(return_value=1) - with patch.dict(macpackage.__salt__, {'cmd.retcode': mock}): - out = macpackage.installed('/path/to/file.pkg', onlyif='some command') + with patch.dict(macpackage.__salt__, {"cmd.retcode": mock}): + out = macpackage.installed("/path/to/file.pkg", onlyif="some command") self.assertEqual(out, expected) def test_installed_pkg_unless_fail(self,): - ''' + """ Test installing a PKG file where the unless run fails - ''' + """ expected = { - 'changes': {}, - 'comment': 'unless condition is true', - 'skip_watch': True, - 'result': True, - 'name': '/path/to/file.pkg', + "changes": {}, + "comment": "unless condition is true", + "skip_watch": True, + "result": True, + "name": "/path/to/file.pkg", } mock = MagicMock(return_value=0) - with patch.dict(macpackage.__salt__, {'cmd.retcode': mock}): - out = macpackage.installed('/path/to/file.pkg', unless='some command') + with patch.dict(macpackage.__salt__, {"cmd.retcode": mock}): + out = macpackage.installed("/path/to/file.pkg", unless="some command") self.assertEqual(out, expected) diff --git a/tests/unit/states/test_makeconf.py b/tests/unit/states/test_makeconf.py index 530207eb3f0..c054beb8f8c 100644 --- a/tests/unit/states/test_makeconf.py +++ b/tests/unit/states/test_makeconf.py @@ -1,64 +1,56 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.makeconf as makeconf +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MakeconfTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.makeconf - ''' + """ + def setup_loader_modules(self): return {makeconf: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to verify that the variable is in the ``make.conf`` and has the provided settings. - ''' - name = 'makeopts' + """ + name = "makeopts" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock_t = MagicMock(return_value=True) - with patch.dict(makeconf.__salt__, {'makeconf.get_var': mock_t}): - comt = ('Variable {0} is already present in make.conf'.format(name)) - ret.update({'comment': comt}) + with patch.dict(makeconf.__salt__, {"makeconf.get_var": mock_t}): + comt = "Variable {0} is already present in make.conf".format(name) + ret.update({"comment": comt}) self.assertDictEqual(makeconf.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to verify that the variable is not in the ``make.conf``. - ''' - name = 'makeopts' + """ + name = "makeopts" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(return_value=None) - with patch.dict(makeconf.__salt__, {'makeconf.get_var': mock}): - comt = ('Variable {0} is already absent from make.conf' - .format(name)) - ret.update({'comment': comt}) + with patch.dict(makeconf.__salt__, {"makeconf.get_var": mock}): + comt = "Variable {0} is already absent from make.conf".format(name) + ret.update({"comment": comt}) self.assertDictEqual(makeconf.absent(name), ret) diff --git a/tests/unit/states/test_mdadm_raid.py b/tests/unit/states/test_mdadm_raid.py index f742c932f74..82c3e48fb39 100644 --- a/tests/unit/states/test_mdadm_raid.py +++ b/tests/unit/states/test_mdadm_raid.py @@ -1,62 +1,97 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.states.mdadm_raid as mdadm +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MdadmTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the mdadm state - ''' + """ + def setup_loader_modules(self): return {mdadm: {}} def test_present(self): - ''' + """ Test to verify that the raid is present - ''' - ret = [{'changes': {}, 'comment': 'Raid salt already present.', - 'name': 'salt', 'result': True}, - {'changes': {}, - 'comment': "Devices are a mix of RAID constituents with multiple MD_UUIDs:" + """ + ret = [ + { + "changes": {}, + "comment": "Raid salt already present.", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "Devices are a mix of RAID constituents with multiple MD_UUIDs:" " ['6be5fc45:05802bba:1c2d6722:666f0e03', 'ffffffff:ffffffff:ffffffff:ffffffff'].", - 'name': 'salt', 'result': False}, - {'changes': {}, - 'comment': 'Raid will be created with: True', 'name': 'salt', - 'result': None}, - {'changes': {}, 'comment': 'Raid salt failed to be created.', - 'name': 'salt', 'result': False}, - {'changes': {'uuid': '6be5fc45:05802bba:1c2d6722:666f0e03'}, 'comment': 'Raid salt created.', - 'name': 'salt', 'result': True}, - {'changes': {'added': ['dev1'], 'uuid': '6be5fc45:05802bba:1c2d6722:666f0e03'}, - 'comment': 'Raid salt assembled. Added new device dev1 to salt.\n', - 'name': 'salt', 'result': True}, - {'changes': {'added': ['dev1']}, - 'comment': 'Raid salt already present. Added new device dev1 to salt.\n', - 'name': 'salt', 'result': True}, - {'changes': {}, 'comment': 'Raid salt failed to be assembled.', - 'name': 'salt', 'result': False}] + "name": "salt", + "result": False, + }, + { + "changes": {}, + "comment": "Raid will be created with: True", + "name": "salt", + "result": None, + }, + { + "changes": {}, + "comment": "Raid salt failed to be created.", + "name": "salt", + "result": False, + }, + { + "changes": {"uuid": "6be5fc45:05802bba:1c2d6722:666f0e03"}, + "comment": "Raid salt created.", + "name": "salt", + "result": True, + }, + { + "changes": { + "added": ["dev1"], + "uuid": "6be5fc45:05802bba:1c2d6722:666f0e03", + }, + "comment": "Raid salt assembled. Added new device dev1 to salt.\n", + "name": "salt", + "result": True, + }, + { + "changes": {"added": ["dev1"]}, + "comment": "Raid salt already present. Added new device dev1 to salt.\n", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "Raid salt failed to be assembled.", + "name": "salt", + "result": False, + }, + ] - mock_raid_list_exists = MagicMock(return_value={'salt': {'uuid': '6be5fc45:05802bba:1c2d6722:666f0e03'}}) + mock_raid_list_exists = MagicMock( + return_value={"salt": {"uuid": "6be5fc45:05802bba:1c2d6722:666f0e03"}} + ) mock_raid_list_missing = MagicMock(return_value={}) mock_file_access_ok = MagicMock(return_value=True) - mock_raid_examine_ok = MagicMock(return_value={'MD_UUID': '6be5fc45:05802bba:1c2d6722:666f0e03'}) + mock_raid_examine_ok = MagicMock( + return_value={"MD_UUID": "6be5fc45:05802bba:1c2d6722:666f0e03"} + ) mock_raid_examine_missing = MagicMock(return_value={}) mock_raid_create_success = MagicMock(return_value=True) @@ -69,114 +104,158 @@ class MdadmTestCase(TestCase, LoaderModuleMockMixin): mock_raid_save_config = MagicMock(return_value=True) - with patch.dict(mdadm.__salt__, { - 'raid.list': mock_raid_list_exists, - 'file.access': mock_file_access_ok, - 'raid.examine': mock_raid_examine_ok - }): - with patch.dict(mdadm.__opts__, {'test': False}): + with patch.dict( + mdadm.__salt__, + { + "raid.list": mock_raid_list_exists, + "file.access": mock_file_access_ok, + "raid.examine": mock_raid_examine_ok, + }, + ): + with patch.dict(mdadm.__opts__, {"test": False}): self.assertEqual(mdadm.present("salt", 5, "dev0"), ret[0]) - mock_raid_examine_mixed = MagicMock(side_effect=[ - {'MD_UUID': '6be5fc45:05802bba:1c2d6722:666f0e03'}, {'MD_UUID': 'ffffffff:ffffffff:ffffffff:ffffffff'}, - ]) - with patch.dict(mdadm.__salt__, { - 'raid.list': mock_raid_list_missing, - 'file.access': mock_file_access_ok, - 'raid.examine': mock_raid_examine_mixed - }): - with patch.dict(mdadm.__opts__, {'test': False}): + mock_raid_examine_mixed = MagicMock( + side_effect=[ + {"MD_UUID": "6be5fc45:05802bba:1c2d6722:666f0e03"}, + {"MD_UUID": "ffffffff:ffffffff:ffffffff:ffffffff"}, + ] + ) + with patch.dict( + mdadm.__salt__, + { + "raid.list": mock_raid_list_missing, + "file.access": mock_file_access_ok, + "raid.examine": mock_raid_examine_mixed, + }, + ): + with patch.dict(mdadm.__opts__, {"test": False}): self.assertEqual(mdadm.present("salt", 5, ["dev0", "dev1"]), ret[1]) - with patch.dict(mdadm.__salt__, { - 'raid.list': mock_raid_list_missing, - 'file.access': mock_file_access_ok, - 'raid.examine': mock_raid_examine_missing, - 'raid.create': mock_raid_create_success - }): - with patch.dict(mdadm.__opts__, {'test': True}): + with patch.dict( + mdadm.__salt__, + { + "raid.list": mock_raid_list_missing, + "file.access": mock_file_access_ok, + "raid.examine": mock_raid_examine_missing, + "raid.create": mock_raid_create_success, + }, + ): + with patch.dict(mdadm.__opts__, {"test": True}): self.assertDictEqual(mdadm.present("salt", 5, "dev0"), ret[2]) - with patch.dict(mdadm.__salt__, { - 'raid.list': mock_raid_list_missing, - 'file.access': mock_file_access_ok, - 'raid.examine': mock_raid_examine_missing, - 'raid.create': mock_raid_create_fail - }): - with patch.dict(mdadm.__opts__, {'test': False}): + with patch.dict( + mdadm.__salt__, + { + "raid.list": mock_raid_list_missing, + "file.access": mock_file_access_ok, + "raid.examine": mock_raid_examine_missing, + "raid.create": mock_raid_create_fail, + }, + ): + with patch.dict(mdadm.__opts__, {"test": False}): self.assertDictEqual(mdadm.present("salt", 5, "dev0"), ret[3]) - mock_raid_list_create = MagicMock(side_effect=[{}, {'salt': {'uuid': '6be5fc45:05802bba:1c2d6722:666f0e03'}}]) - with patch.dict(mdadm.__salt__, { - 'raid.list': mock_raid_list_create, - 'file.access': mock_file_access_ok, - 'raid.examine': mock_raid_examine_missing, - 'raid.create': mock_raid_create_success, - 'raid.save_config': mock_raid_save_config - }): - with patch.dict(mdadm.__opts__, {'test': False}): + mock_raid_list_create = MagicMock( + side_effect=[{}, {"salt": {"uuid": "6be5fc45:05802bba:1c2d6722:666f0e03"}}] + ) + with patch.dict( + mdadm.__salt__, + { + "raid.list": mock_raid_list_create, + "file.access": mock_file_access_ok, + "raid.examine": mock_raid_examine_missing, + "raid.create": mock_raid_create_success, + "raid.save_config": mock_raid_save_config, + }, + ): + with patch.dict(mdadm.__opts__, {"test": False}): self.assertDictEqual(mdadm.present("salt", 5, "dev0"), ret[4]) - mock_raid_examine_replaced = MagicMock(side_effect=[ - {'MD_UUID': '6be5fc45:05802bba:1c2d6722:666f0e03'}, {}, - ]) - mock_raid_list_create = MagicMock(side_effect=[{}, {'salt': {'uuid': '6be5fc45:05802bba:1c2d6722:666f0e03'}}]) - with patch.dict(mdadm.__salt__, { - 'raid.list': mock_raid_list_create, - 'file.access': mock_file_access_ok, - 'raid.examine': mock_raid_examine_replaced, - 'raid.assemble': mock_raid_assemble_success, - 'raid.add': mock_raid_add_success, - 'raid.save_config': mock_raid_save_config - }): - with patch.dict(mdadm.__opts__, {'test': False}): + mock_raid_examine_replaced = MagicMock( + side_effect=[{"MD_UUID": "6be5fc45:05802bba:1c2d6722:666f0e03"}, {}] + ) + mock_raid_list_create = MagicMock( + side_effect=[{}, {"salt": {"uuid": "6be5fc45:05802bba:1c2d6722:666f0e03"}}] + ) + with patch.dict( + mdadm.__salt__, + { + "raid.list": mock_raid_list_create, + "file.access": mock_file_access_ok, + "raid.examine": mock_raid_examine_replaced, + "raid.assemble": mock_raid_assemble_success, + "raid.add": mock_raid_add_success, + "raid.save_config": mock_raid_save_config, + }, + ): + with patch.dict(mdadm.__opts__, {"test": False}): self.assertDictEqual(mdadm.present("salt", 5, ["dev0", "dev1"]), ret[5]) - mock_raid_examine_replaced = MagicMock(side_effect=[ - {'MD_UUID': '6be5fc45:05802bba:1c2d6722:666f0e03'}, {}, - ]) - with patch.dict(mdadm.__salt__, { - 'raid.list': mock_raid_list_exists, - 'file.access': mock_file_access_ok, - 'raid.examine': mock_raid_examine_replaced, - 'raid.add': mock_raid_add_success, - 'raid.save_config': mock_raid_save_config - }): - with patch.dict(mdadm.__opts__, {'test': False}): + mock_raid_examine_replaced = MagicMock( + side_effect=[{"MD_UUID": "6be5fc45:05802bba:1c2d6722:666f0e03"}, {}] + ) + with patch.dict( + mdadm.__salt__, + { + "raid.list": mock_raid_list_exists, + "file.access": mock_file_access_ok, + "raid.examine": mock_raid_examine_replaced, + "raid.add": mock_raid_add_success, + "raid.save_config": mock_raid_save_config, + }, + ): + with patch.dict(mdadm.__opts__, {"test": False}): self.assertDictEqual(mdadm.present("salt", 5, ["dev0", "dev1"]), ret[6]) - mock_raid_examine_replaced = MagicMock(side_effect=[ - {'MD_UUID': '6be5fc45:05802bba:1c2d6722:666f0e03'}, {}, - ]) - with patch.dict(mdadm.__salt__, { - 'raid.list': mock_raid_list_missing, - 'file.access': mock_file_access_ok, - 'raid.examine': mock_raid_examine_replaced, - 'raid.assemble': mock_raid_assemble_fail, - }): - with patch.dict(mdadm.__opts__, {'test': False}): + mock_raid_examine_replaced = MagicMock( + side_effect=[{"MD_UUID": "6be5fc45:05802bba:1c2d6722:666f0e03"}, {}] + ) + with patch.dict( + mdadm.__salt__, + { + "raid.list": mock_raid_list_missing, + "file.access": mock_file_access_ok, + "raid.examine": mock_raid_examine_replaced, + "raid.assemble": mock_raid_assemble_fail, + }, + ): + with patch.dict(mdadm.__opts__, {"test": False}): self.assertDictEqual(mdadm.present("salt", 5, ["dev0", "dev1"]), ret[7]) def test_absent(self): - ''' + """ Test to verify that the raid is absent - ''' - ret = [{'changes': {}, 'comment': 'Raid salt already absent', - 'name': 'salt', 'result': True}, - {'changes': {}, - 'comment': 'Raid saltstack is set to be destroyed', - 'name': 'saltstack', 'result': None}, - {'changes': {}, 'comment': 'Raid saltstack has been destroyed', - 'name': 'saltstack', 'result': True}] + """ + ret = [ + { + "changes": {}, + "comment": "Raid salt already absent", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "Raid saltstack is set to be destroyed", + "name": "saltstack", + "result": None, + }, + { + "changes": {}, + "comment": "Raid saltstack has been destroyed", + "name": "saltstack", + "result": True, + }, + ] mock = MagicMock(return_value=["saltstack"]) - with patch.dict(mdadm.__salt__, {'raid.list': mock}): + with patch.dict(mdadm.__salt__, {"raid.list": mock}): self.assertDictEqual(mdadm.absent("salt"), ret[0]) - with patch.dict(mdadm.__opts__, {'test': True}): + with patch.dict(mdadm.__opts__, {"test": True}): self.assertDictEqual(mdadm.absent("saltstack"), ret[1]) - with patch.dict(mdadm.__opts__, {'test': False}): + with patch.dict(mdadm.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.dict(mdadm.__salt__, {'raid.destroy': mock}): + with patch.dict(mdadm.__salt__, {"raid.destroy": mock}): self.assertDictEqual(mdadm.absent("saltstack"), ret[2]) diff --git a/tests/unit/states/test_memcached.py b/tests/unit/states/test_memcached.py index d67402915ab..086b597f245 100644 --- a/tests/unit/states/test_memcached.py +++ b/tests/unit/states/test_memcached.py @@ -1,98 +1,104 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -from salt.exceptions import CommandExecutionError - # Import Salt Libs import salt.states.memcached as memcached +from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class MemcachedTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.memcached - ''' + """ + def setup_loader_modules(self): return {memcached: {}} # 'managed' function tests: 1 def test_managed(self): - ''' + """ Test to manage a memcached key. - ''' - name = 'foo' + """ + name = "foo" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock_t = MagicMock(side_effect=[CommandExecutionError, 'salt', True, - True, True]) - with patch.dict(memcached.__salt__, {'memcached.get': mock_t, - 'memcached.set': mock_t}): + mock_t = MagicMock( + side_effect=[CommandExecutionError, "salt", True, True, True] + ) + with patch.dict( + memcached.__salt__, {"memcached.get": mock_t, "memcached.set": mock_t} + ): self.assertDictEqual(memcached.managed(name), ret) - comt = ("Key 'foo' does not need to be updated") - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(memcached.managed(name, 'salt'), ret) + comt = "Key 'foo' does not need to be updated" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(memcached.managed(name, "salt"), ret) - with patch.dict(memcached.__opts__, {'test': True}): - comt = ("Value of key 'foo' would be changed") - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(memcached.managed(name, 'salt'), ret) + with patch.dict(memcached.__opts__, {"test": True}): + comt = "Value of key 'foo' would be changed" + ret.update({"comment": comt, "result": None}) + self.assertDictEqual(memcached.managed(name, "salt"), ret) - with patch.dict(memcached.__opts__, {'test': False}): - comt = ("Successfully set key 'foo'") - ret.update({'comment': comt, 'result': True, - 'changes': {'new': 'salt', 'old': True}}) - self.assertDictEqual(memcached.managed(name, 'salt'), ret) + with patch.dict(memcached.__opts__, {"test": False}): + comt = "Successfully set key 'foo'" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"new": "salt", "old": True}, + } + ) + self.assertDictEqual(memcached.managed(name, "salt"), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that a memcached key is not present. - ''' - name = 'foo' + """ + name = "foo" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock_t = MagicMock(side_effect=[CommandExecutionError, 'salt', None, - True, True, True]) - with patch.dict(memcached.__salt__, {'memcached.get': mock_t, - 'memcached.delete': mock_t}): + mock_t = MagicMock( + side_effect=[CommandExecutionError, "salt", None, True, True, True] + ) + with patch.dict( + memcached.__salt__, {"memcached.get": mock_t, "memcached.delete": mock_t} + ): self.assertDictEqual(memcached.absent(name), ret) - comt = ("Value of key 'foo' ('salt') is not 'bar'") - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(memcached.absent(name, 'bar'), ret) + comt = "Value of key 'foo' ('salt') is not 'bar'" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(memcached.absent(name, "bar"), ret) - comt = ("Key 'foo' does not exist") - ret.update({'comment': comt}) + comt = "Key 'foo' does not exist" + ret.update({"comment": comt}) self.assertDictEqual(memcached.absent(name), ret) - with patch.dict(memcached.__opts__, {'test': True}): - comt = ("Key 'foo' would be deleted") - ret.update({'comment': comt, 'result': None}) + with patch.dict(memcached.__opts__, {"test": True}): + comt = "Key 'foo' would be deleted" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(memcached.absent(name), ret) - with patch.dict(memcached.__opts__, {'test': False}): - comt = ("Successfully deleted key 'foo'") - ret.update({'comment': comt, 'result': True, - 'changes': {'key deleted': 'foo', 'value': True}}) + with patch.dict(memcached.__opts__, {"test": False}): + comt = "Successfully deleted key 'foo'" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"key deleted": "foo", "value": True}, + } + ) self.assertDictEqual(memcached.absent(name), ret) diff --git a/tests/unit/states/test_modjk.py b/tests/unit/states/test_modjk.py index 5d063c99c6b..128de537ac6 100644 --- a/tests/unit/states/test_modjk.py +++ b/tests/unit/states/test_modjk.py @@ -1,17 +1,16 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.unit import TestCase - # Import Salt Libs import salt.states.modjk as modjk from salt.ext import six +# Import Salt Testing Libs +from tests.support.unit import TestCase if six.PY2: LIST_NOT_STR = "workers should be a list not a <type 'unicode'>" @@ -20,69 +19,58 @@ else: class ModjkTestCase(TestCase): - ''' + """ Test cases for salt.states.modjk - ''' + """ + # 'worker_stopped' function tests: 1 def test_worker_stopped(self): - ''' + """ Test to stop all the workers in the modjk load balancer - ''' - name = 'loadbalancer' + """ + name = "loadbalancer" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - ret.update({'comment': LIST_NOT_STR}) - self.assertDictEqual(modjk.worker_stopped(name, 'app1'), ret) + ret.update({"comment": LIST_NOT_STR}) + self.assertDictEqual(modjk.worker_stopped(name, "app1"), ret) # 'worker_activated' function tests: 1 def test_worker_activated(self): - ''' + """ Test to activate all the workers in the modjk load balancer - ''' - name = 'loadbalancer' + """ + name = "loadbalancer" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - ret.update({'comment': LIST_NOT_STR}) - self.assertDictEqual(modjk.worker_activated(name, 'app1'), ret) + ret.update({"comment": LIST_NOT_STR}) + self.assertDictEqual(modjk.worker_activated(name, "app1"), ret) # 'worker_disabled' function tests: 1 def test_worker_disabled(self): - ''' + """ Test to disable all the workers in the modjk load balancer - ''' - name = 'loadbalancer' + """ + name = "loadbalancer" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - ret.update({'comment': LIST_NOT_STR}) - self.assertDictEqual(modjk.worker_disabled(name, 'app1'), ret) + ret.update({"comment": LIST_NOT_STR}) + self.assertDictEqual(modjk.worker_disabled(name, "app1"), ret) # 'worker_recover' function tests: 1 def test_worker_recover(self): - ''' + """ Test to recover all the workers in the modjk load balancer - ''' - name = 'loadbalancer' + """ + name = "loadbalancer" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - ret.update({'comment': LIST_NOT_STR}) - self.assertDictEqual(modjk.worker_recover(name, 'app1'), ret) + ret.update({"comment": LIST_NOT_STR}) + self.assertDictEqual(modjk.worker_recover(name, "app1"), ret) diff --git a/tests/unit/states/test_modjk_worker.py b/tests/unit/states/test_modjk_worker.py index aacc806cfd1..d90820b035b 100644 --- a/tests/unit/states/test_modjk_worker.py +++ b/tests/unit/states/test_modjk_worker.py @@ -1,89 +1,80 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.modjk_worker as modjk_worker +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ModjkWorkerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.modjk_worker - ''' + """ + def setup_loader_modules(self): return {modjk_worker: {}} + # 'stop' function tests: 1 def test_stop(self): - ''' + """ Test to stop the named worker from the lbn load balancers at the targeted minions. - ''' + """ name = "{{ grains['id'] }}" - lbn = 'application' - target = 'roles:balancer' + lbn = "application" + target = "roles:balancer" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = 'no servers answered the published command modjk.worker_status' + comt = "no servers answered the published command modjk.worker_status" mock = MagicMock(return_value=False) - with patch.dict(modjk_worker.__salt__, {'publish.publish': mock}): - ret.update({'comment': comt}) + with patch.dict(modjk_worker.__salt__, {"publish.publish": mock}): + ret.update({"comment": comt}) self.assertDictEqual(modjk_worker.stop(name, lbn, target), ret) # 'activate' function tests: 1 def test_activate(self): - ''' + """ Test to activate the named worker from the lbn load balancers at the targeted minions. - ''' + """ name = "{{ grains['id'] }}" - lbn = 'application' - target = 'roles:balancer' + lbn = "application" + target = "roles:balancer" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = 'no servers answered the published command modjk.worker_status' + comt = "no servers answered the published command modjk.worker_status" mock = MagicMock(return_value=False) - with patch.dict(modjk_worker.__salt__, {'publish.publish': mock}): - ret.update({'comment': comt}) + with patch.dict(modjk_worker.__salt__, {"publish.publish": mock}): + ret.update({"comment": comt}) self.assertDictEqual(modjk_worker.activate(name, lbn, target), ret) # 'disable' function tests: 1 def test_disable(self): - ''' + """ Test to disable the named worker from the lbn load balancers at the targeted minions. - ''' + """ name = "{{ grains['id'] }}" - lbn = 'application' - target = 'roles:balancer' + lbn = "application" + target = "roles:balancer" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = 'no servers answered the published command modjk.worker_status' + comt = "no servers answered the published command modjk.worker_status" mock = MagicMock(return_value=False) - with patch.dict(modjk_worker.__salt__, {'publish.publish': mock}): - ret.update({'comment': comt}) + with patch.dict(modjk_worker.__salt__, {"publish.publish": mock}): + ret.update({"comment": comt}) self.assertDictEqual(modjk_worker.disable(name, lbn, target), ret) diff --git a/tests/unit/states/test_module.py b/tests/unit/states/test_module.py index 917c7b9430e..b5d38de499f 100644 --- a/tests/unit/states/test_module.py +++ b/tests/unit/states/test_module.py @@ -1,133 +1,121 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas (nicole@saltstack.com) -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -from inspect import ArgSpec + import logging +from inspect import ArgSpec # Import Salt Libs import salt.states.module as module # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) log = logging.getLogger(__name__) -CMD = 'foo.bar' +CMD = "foo.bar" STATE_APPLY_RET = { - 'module_|-test2_|-state.apply_|-run': { - 'comment': 'Module function state.apply executed', - 'name': 'state.apply', - 'start_time': '16:11:48.818932', - 'result': False, - 'duration': 179.439, - '__run_num__': 0, - 'changes': { - 'ret': { - 'module_|-test3_|-state.apply_|-run': { - 'comment': 'Module function state.apply executed', - 'name': 'state.apply', - 'start_time': '16:11:48.904796', - 'result': True, - 'duration': 89.522, - '__run_num__': 0, - 'changes': { - 'ret': { - 'module_|-test4_|-cmd.run_|-run': { - 'comment': 'Module function cmd.run executed', - 'name': 'cmd.run', - 'start_time': '16:11:48.988574', - 'result': True, - 'duration': 4.543, - '__run_num__': 0, - 'changes': { - 'ret': 'Wed Mar 7 16:11:48 CET 2018' - }, - '__id__': 'test4' + "module_|-test2_|-state.apply_|-run": { + "comment": "Module function state.apply executed", + "name": "state.apply", + "start_time": "16:11:48.818932", + "result": False, + "duration": 179.439, + "__run_num__": 0, + "changes": { + "ret": { + "module_|-test3_|-state.apply_|-run": { + "comment": "Module function state.apply executed", + "name": "state.apply", + "start_time": "16:11:48.904796", + "result": True, + "duration": 89.522, + "__run_num__": 0, + "changes": { + "ret": { + "module_|-test4_|-cmd.run_|-run": { + "comment": "Module function cmd.run executed", + "name": "cmd.run", + "start_time": "16:11:48.988574", + "result": True, + "duration": 4.543, + "__run_num__": 0, + "changes": {"ret": "Wed Mar 7 16:11:48 CET 2018"}, + "__id__": "test4", } } }, - '__id__': 'test3' + "__id__": "test3", + }, + "module_|-test3_fail_|-test3_fail_|-run": { + "comment": "Module function test3_fail is not available", + "name": "test3_fail", + "start_time": "16:11:48.994607", + "result": False, + "duration": 0.466, + "__run_num__": 1, + "changes": {}, + "__id__": "test3_fail", }, - 'module_|-test3_fail_|-test3_fail_|-run': { - 'comment': 'Module function test3_fail is not available', - 'name': 'test3_fail', - 'start_time': '16:11:48.994607', - 'result': False, - 'duration': 0.466, - '__run_num__': 1, - 'changes': {}, - '__id__': 'test3_fail' - } } }, - '__id__': 'test2' + "__id__": "test2", } } -def _mocked_func_named(name, names=('Fred', 'Swen',)): - ''' +def _mocked_func_named(name, names=("Fred", "Swen",)): + """ Mocked function with named defaults. :param name: :param names: :return: - ''' - return {'name': name, 'names': names} + """ + return {"name": name, "names": names} def _mocked_func_args(*args): - ''' + """ Mocked function with args. :param args: :return: - ''' - assert args == ('foo', 'bar') - return {'args': args} + """ + assert args == ("foo", "bar") + return {"args": args} def _mocked_none_return(ret=None): - ''' + """ Mocked function returns None :return: - ''' + """ return ret class ModuleStateTest(TestCase, LoaderModuleMockMixin): - ''' + """ Tests module state (salt/states/module.py) - ''' + """ + def setup_loader_modules(self): - return { - module: { - '__opts__': {'test': False}, - '__salt__': {CMD: MagicMock()} - } - } + return {module: {"__opts__": {"test": False}, "__salt__": {CMD: MagicMock()}}} @classmethod def setUpClass(cls): - cls.aspec = ArgSpec(args=['hello', 'world'], - varargs=None, - keywords=None, - defaults=False) + cls.aspec = ArgSpec( + args=["hello", "world"], varargs=None, keywords=None, defaults=False + ) - cls.bspec = ArgSpec(args=[], - varargs='names', - keywords='kwargs', - defaults=None) + cls.bspec = ArgSpec(args=[], varargs="names", keywords="kwargs", defaults=None) @classmethod def tearDownClass(cls): @@ -135,260 +123,357 @@ class ModuleStateTest(TestCase, LoaderModuleMockMixin): del cls.bspec def test_run_module_not_available(self): - ''' + """ Tests the return of module.run state when the module function is not available. :return: - ''' - with \ - patch.dict(module.__salt__, {}, clear=True), \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}): + """ + with patch.dict(module.__salt__, {}, clear=True), patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ): ret = module.run(**{CMD: None}) - if ret['comment'] != "Unavailable function: {0}.".format(CMD) or ret['result']: - self.fail('module.run did not fail as expected: {0}'.format(ret)) + if ret["comment"] != "Unavailable function: {0}.".format(CMD) or ret["result"]: + self.fail("module.run did not fail as expected: {0}".format(ret)) def test_module_run_hidden_varargs(self): - ''' + """ Tests the return of module.run state when hidden varargs are used with wrong type. - ''' - with patch('salt.utils.args.get_function_argspec', MagicMock(return_value=self.bspec)): - ret = module._run(CMD, m_names='anyname') - self.assertEqual( - ret['comment'], - "'names' must be a list." - ) + """ + with patch( + "salt.utils.args.get_function_argspec", MagicMock(return_value=self.bspec) + ): + ret = module._run(CMD, m_names="anyname") + self.assertEqual(ret["comment"], "'names' must be a list.") def test_run_testmode(self): - ''' + """ Tests the return of the module.run state when test=True is passed. :return: - ''' - with patch.dict(module.__opts__, {'test': True, 'use_superseded': ['module.run']}): + """ + with patch.dict( + module.__opts__, {"test": True, "use_superseded": ["module.run"]} + ): ret = module.run(**{CMD: None}) - if ret['comment'] != "Function {0} to be executed.".format(CMD) or not ret['result']: - self.fail('module.run failed: {0}'.format(ret)) + if ( + ret["comment"] != "Function {0} to be executed.".format(CMD) + or not ret["result"] + ): + self.fail("module.run failed: {0}".format(ret)) def test_run_missing_arg(self): - ''' + """ Tests the return of module.run state when arguments are missing :return: - ''' - with \ - patch.dict(module.__salt__, {CMD: _mocked_func_named}), \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}): + """ + with patch.dict(module.__salt__, {CMD: _mocked_func_named}), patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ): ret = module.run(**{CMD: None}) self.assertEqual( - ret['comment'], - "'{}' failed: Missing arguments: name".format(CMD) + ret["comment"], "'{}' failed: Missing arguments: name".format(CMD) ) def test_run_correct_arg(self): - ''' + """ Tests the return of module.run state when arguments are correct :return: - ''' - with \ - patch.dict(module.__salt__, {CMD: _mocked_func_named}), \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}): - ret = module.run(**{CMD: ['Fred']}) - if ret['comment'] != '{0}: Success'.format(CMD) or not ret['result']: - self.fail('module.run failed: {0}'.format(ret)) + """ + with patch.dict(module.__salt__, {CMD: _mocked_func_named}), patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ): + ret = module.run(**{CMD: ["Fred"]}) + if ret["comment"] != "{0}: Success".format(CMD) or not ret["result"]: + self.fail("module.run failed: {0}".format(ret)) def test_run_state_apply_result_false(self): - ''' + """ Tests the 'result' of module.run that calls state.apply execution module :return: - ''' - with \ - patch.dict(module.__salt__, {"state.apply": MagicMock(return_value=STATE_APPLY_RET)}), \ - patch.dict(module.__opts__, {'use_deprecated': ['module.run']}): - ret = module.run(**{"name": "state.apply", 'mods': 'test2'}) - self.assertFalse(ret['result']) + """ + with patch.dict( + module.__salt__, {"state.apply": MagicMock(return_value=STATE_APPLY_RET)} + ), patch.dict(module.__opts__, {"use_deprecated": ["module.run"]}): + ret = module.run(**{"name": "state.apply", "mods": "test2"}) + self.assertFalse(ret["result"]) def test_run_unexpected_keywords(self): - with \ - patch.dict(module.__salt__, {CMD: _mocked_func_args}), \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}): - ret = module.run(**{CMD: [{'foo': 'bar'}]}) + with patch.dict(module.__salt__, {CMD: _mocked_func_args}), patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ): + ret = module.run(**{CMD: [{"foo": "bar"}]}) module_function = module.__salt__[CMD].__name__ self.assertEqual( - ret['comment'], - ("'{0}' failed: {1}() got an unexpected keyword argument " - "'foo'".format(CMD, module_function)) + ret["comment"], + ( + "'{0}' failed: {1}() got an unexpected keyword argument " + "'foo'".format(CMD, module_function) + ), ) - self.assertFalse(ret['result']) + self.assertFalse(ret["result"]) def test_run_args(self): - ''' + """ Test unnamed args. :return: - ''' - with \ - patch.dict(module.__salt__, {CMD: _mocked_func_args}), \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}): - ret = module.run(**{CMD: ['foo', 'bar']}) - self.assertTrue(ret['result']) - self.assertEqual( - ret['changes'], - {CMD: {'args': ('foo', 'bar')}} - ) + """ + with patch.dict(module.__salt__, {CMD: _mocked_func_args}), patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ): + ret = module.run(**{CMD: ["foo", "bar"]}) + self.assertTrue(ret["result"]) + self.assertEqual(ret["changes"], {CMD: {"args": ("foo", "bar")}}) def test_run_42270(self): - ''' + """ Test example provided in issue 42270 - ''' + """ + def test_func(arg1, arg2, **kwargs): - return {'args': [arg1, arg2], 'kwargs': kwargs or {}} - with \ - patch.dict(module.__salt__, {CMD: test_func}), \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}): - ret = module.run(**{CMD: ['bla', {'example': 'bla'}]}) - self.assertFalse(ret['result']) + return {"args": [arg1, arg2], "kwargs": kwargs or {}} + + with patch.dict(module.__salt__, {CMD: test_func}), patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ): + ret = module.run(**{CMD: ["bla", {"example": "bla"}]}) + self.assertFalse(ret["result"]) self.assertEqual( - ret['comment'], - "'{}' failed: Missing arguments: arg2".format(CMD) + ret["comment"], "'{}' failed: Missing arguments: arg2".format(CMD) ) def test_run_42270_kwargs_to_args(self): - ''' + """ Test module.run filling in args with kwargs with the same name. - ''' + """ + def test_func(arg1, arg2, arg3, *args, **kwargs): - return {'args': [arg1, arg2, arg3] + list(args), - 'kwargs': kwargs} - with \ - patch.dict(module.__salt__, {CMD: test_func}), \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}): - ret = module.run(**{CMD: ['foo', 'bar', {'arg3': 'baz'}, {'foo': 'bar'}]}) - self.assertTrue(ret['result']) + return {"args": [arg1, arg2, arg3] + list(args), "kwargs": kwargs} + + with patch.dict(module.__salt__, {CMD: test_func}), patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ): + ret = module.run(**{CMD: ["foo", "bar", {"arg3": "baz"}, {"foo": "bar"}]}) + self.assertTrue(ret["result"]) self.assertEqual( - ret['changes'], - {CMD: {'args': ['foo', 'bar', 'baz'], 'kwargs': {'foo': 'bar'}}} + ret["changes"], + {CMD: {"args": ["foo", "bar", "baz"], "kwargs": {"foo": "bar"}}}, ) def test_run_none_return(self): - ''' + """ Test handling of a broken function that returns None. :return: - ''' - with \ - patch.dict(module.__salt__, {CMD: _mocked_none_return}), \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}): + """ + with patch.dict(module.__salt__, {CMD: _mocked_none_return}), patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ): ret = module.run(**{CMD: None}) - self.assertTrue(ret['result']) - self.assertEqual( - ret['changes'], - {CMD: None} - ) + self.assertTrue(ret["result"]) + self.assertEqual(ret["changes"], {CMD: None}) def test_run_typed_return(self): - ''' + """ Test handling of a broken function that returns any type. :return: - ''' - for val in [1, 0, 'a', '', (1, 2,), (), [1, 2], [], {'a': 'b'}, {}, True, False]: - with \ - patch.dict(module.__salt__, {CMD: _mocked_none_return}), \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}): - log.debug('test_run_typed_return: trying %s', val) - ret = module.run(**{CMD: [{'ret': val}]}) - self.assertTrue(ret['result']) + """ + for val in [ + 1, + 0, + "a", + "", + (1, 2,), + (), + [1, 2], + [], + {"a": "b"}, + {}, + True, + False, + ]: + with patch.dict(module.__salt__, {CMD: _mocked_none_return}), patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ): + log.debug("test_run_typed_return: trying %s", val) + ret = module.run(**{CMD: [{"ret": val}]}) + self.assertTrue(ret["result"]) def test_run_batch_call(self): - ''' + """ Test batch call :return: - ''' - with \ - patch.dict(module.__opts__, {'use_superseded': ['module.run']}), \ - patch.dict(module.__salt__, { - 'first': _mocked_none_return, - 'second': _mocked_none_return, - 'third': _mocked_none_return - }, clear=True): + """ + with patch.dict( + module.__opts__, {"use_superseded": ["module.run"]} + ), patch.dict( + module.__salt__, + { + "first": _mocked_none_return, + "second": _mocked_none_return, + "third": _mocked_none_return, + }, + clear=True, + ): for f_name in module.__salt__: - log.debug('test_run_batch_call: trying %s', f_name) + log.debug("test_run_batch_call: trying %s", f_name) ret = module.run(**{f_name: None}) - self.assertTrue(ret['result']) + self.assertTrue(ret["result"]) def test_module_run_module_not_available(self): - ''' + """ Tests the return of module.run state when the module function name isn't available - ''' + """ with patch.dict(module.__salt__, {}, clear=True): ret = module._run(CMD) - self.assertFalse(ret['result']) + self.assertFalse(ret["result"]) self.assertEqual( - ret['comment'], - 'Module function {0} is not available'.format(CMD) + ret["comment"], "Module function {0} is not available".format(CMD) ) def test_module_run_test_true(self): - ''' + """ Tests the return of module.run state when test=True is passed in - ''' - with patch.dict(module.__opts__, {'test': True}): + """ + with patch.dict(module.__opts__, {"test": True}): ret = module._run(CMD) self.assertEqual( - ret['comment'], - 'Module function {0} is set to execute'.format(CMD) + ret["comment"], "Module function {0} is set to execute".format(CMD) ) def test_module_run_missing_arg(self): - ''' + """ Tests the return of module.run state when arguments are missing - ''' - with patch('salt.utils.args.get_function_argspec', MagicMock(return_value=self.aspec)): + """ + with patch( + "salt.utils.args.get_function_argspec", MagicMock(return_value=self.aspec) + ): ret = module._run(CMD) - self.assertIn( - 'The following arguments are missing:', - ret['comment'] - ) - self.assertIn('world', ret['comment']) - self.assertIn('hello', ret['comment']) + self.assertIn("The following arguments are missing:", ret["comment"]) + self.assertIn("world", ret["comment"]) + self.assertIn("hello", ret["comment"]) def test_call_function_named_args(self): - ''' + """ Test _call_function routine when params are named. Their position ordering should not matter. :return: - ''' - with patch.dict(module.__salt__, - {'testfunc': lambda a, b, c, *args, **kwargs: (a, b, c, args, kwargs)}, clear=True): + """ + with patch.dict( + module.__salt__, + {"testfunc": lambda a, b, c, *args, **kwargs: (a, b, c, args, kwargs)}, + clear=True, + ): self.assertEqual( - module._call_function('testfunc', func_args=[{'a': 1}, {'b': 2}, {'c': 3}]), - (1, 2, 3, (), {}) + module._call_function( + "testfunc", func_args=[{"a": 1}, {"b": 2}, {"c": 3}] + ), + (1, 2, 3, (), {}), ) self.assertEqual( - module._call_function('testfunc', func_args=[{'c': 3}, {'a': 1}, {'b': 2}]), - (1, 2, 3, (), {}) + module._call_function( + "testfunc", func_args=[{"c": 3}, {"a": 1}, {"b": 2}] + ), + (1, 2, 3, (), {}), ) - with patch.dict(module.__salt__, - {'testfunc': lambda c, a, b, *args, **kwargs: (a, b, c, args, kwargs)}, clear=True): self.assertEqual( - module._call_function('testfunc', func_args=[{'a': 1}, {'b': 2}, {'c': 3}]), - (1, 2, 3, (), {}) + module._call_function("testfunc", func_kwargs={"a": 1, "b": 2, "c": 3}), + (1, 2, 3, (), {}), + ) + + self.assertEqual( + module._call_function("testfunc", func_kwargs={"c": 3, "a": 1, "b": 2}), + (1, 2, 3, (), {}), + ) + + self.assertEqual( + module._call_function("testfunc", func_kwargs={"b": 2, "a": 1, "c": 3}), + (1, 2, 3, (), {}), + ) + + self.assertEqual( + module._call_function("testfunc", func_kwargs={"a": 1, "c": 3, "b": 2}), + (1, 2, 3, (), {}), + ) + + with patch.dict( + module.__salt__, + {"testfunc": lambda c, a, b, *args, **kwargs: (a, b, c, args, kwargs)}, + clear=True, + ): + self.assertEqual( + module._call_function( + "testfunc", func_args=[{"a": 1}, {"b": 2}, {"c": 3}] + ), + (1, 2, 3, (), {}), ) self.assertEqual( - module._call_function('testfunc', func_args=[{'c': 3}, {'a': 1}, {'b': 2}]), - (1, 2, 3, (), {}) + module._call_function( + "testfunc", func_args=[{"c": 3}, {"a": 1}, {"b": 2}] + ), + (1, 2, 3, (), {}), + ) + + self.assertEqual( + module._call_function("testfunc", func_kwargs={"a": 1, "b": 2, "c": 3}), + (1, 2, 3, (), {}), + ) + + self.assertEqual( + module._call_function("testfunc", func_kwargs={"c": 3, "a": 1, "b": 2}), + (1, 2, 3, (), {}), + ) + + self.assertEqual( + module._call_function("testfunc", func_kwargs={"b": 2, "a": 1, "c": 3}), + (1, 2, 3, (), {}), + ) + + self.assertEqual( + module._call_function("testfunc", func_kwargs={"a": 1, "c": 3, "b": 2}), + (1, 2, 3, (), {}), ) def test_call_function_ordered_args(self): - ''' + """ Test _call_function routine when params are not named. Their position should matter. :return: - ''' - with patch.dict(module.__salt__, - {'testfunc': lambda a, b, c, *args, **kwargs: (a, b, c, args, kwargs)}, clear=True): + """ + with patch.dict( + module.__salt__, + {"testfunc": lambda a, b, c, *args, **kwargs: (a, b, c, args, kwargs)}, + clear=True, + ): self.assertEqual( - module._call_function('testfunc', func_args=[1, 2, 3]), - (1, 2, 3, (), {}) + module._call_function("testfunc", func_args=[1, 2, 3]), + (1, 2, 3, (), {}), ) self.assertEqual( - module._call_function('testfunc', func_args=[3, 1, 2]), - (3, 1, 2, (), {}) + module._call_function("testfunc", func_args=[3, 1, 2]), + (3, 1, 2, (), {}), + ) + + def test_call_function_ordered_and_named_args(self): + """ + Test _call_function routine when params are not named. Their position should matter. + + :return: + """ + with patch.dict( + module.__salt__, + {"testfunc": lambda a, b, c, *args, **kwargs: (a, b, c, args, kwargs)}, + clear=True, + ): + self.assertEqual( + module._call_function( + "testfunc", func_args=[1], func_kwargs={"b": 2, "c": 3} + ), + (1, 2, 3, (), {}), + ) + + self.assertEqual( + module._call_function( + "testfunc", func_args=[1, 2], func_kwargs={"c": 3} + ), + (1, 2, 3, (), {}), ) diff --git a/tests/unit/states/test_mongodb_database.py b/tests/unit/states/test_mongodb_database.py index cf313bc4d43..f32372ee73a 100644 --- a/tests/unit/states/test_mongodb_database.py +++ b/tests/unit/states/test_mongodb_database.py @@ -1,58 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.mongodb_database as mongodb_database +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MongodbDatabaseTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.mongodb_database - ''' + """ + def setup_loader_modules(self): return {mongodb_database: {}} # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named database is absent. - ''' + """ name = "mydb" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} mock = MagicMock(side_effect=[True, True, False]) mock_t = MagicMock(return_value=True) - with patch.dict(mongodb_database.__salt__, - {'mongodb.db_exists': mock, - 'mongodb.db_remove': mock_t}): - with patch.dict(mongodb_database.__opts__, {'test': True}): - comt = ('Database {0} is present and needs to be removed' - .format(name)) - ret.update({'comment': comt}) + with patch.dict( + mongodb_database.__salt__, + {"mongodb.db_exists": mock, "mongodb.db_remove": mock_t}, + ): + with patch.dict(mongodb_database.__opts__, {"test": True}): + comt = "Database {0} is present and needs to be removed".format(name) + ret.update({"comment": comt}) self.assertDictEqual(mongodb_database.absent(name), ret) - with patch.dict(mongodb_database.__opts__, {'test': False}): - comt = ('Database {0} has been removed'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'mydb': 'Absent'}}) + with patch.dict(mongodb_database.__opts__, {"test": False}): + comt = "Database {0} has been removed".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {"mydb": "Absent"}} + ) self.assertDictEqual(mongodb_database.absent(name), ret) - comt = 'Database {0} is not present'.format(name) - ret.update({'comment': comt, 'changes': {}}) + comt = "Database {0} is not present".format(name) + ret.update({"comment": comt, "changes": {}}) self.assertDictEqual(mongodb_database.absent(name), ret) diff --git a/tests/unit/states/test_mongodb_user.py b/tests/unit/states/test_mongodb_user.py index a0e76cdbcc4..fe247e5c9d2 100644 --- a/tests/unit/states/test_mongodb_user.py +++ b/tests/unit/states/test_mongodb_user.py @@ -1,100 +1,92 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.mongodb_user as mongodb_user +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MongodbUserTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.mongodb_user - ''' + """ + def setup_loader_modules(self): - return {mongodb_user: {'__opts__': {'test': True}}} + return {mongodb_user: {"__opts__": {"test": True}}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the user is present with the specified properties. - ''' - name = 'myapp' - passwd = 'password-of-myapp' + """ + name = "myapp" + passwd = "password-of-myapp" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - comt = ('Port ({}) is not an integer.') - ret.update({'comment': comt}) + comt = "Port ({}) is not an integer." + ret.update({"comment": comt}) self.assertDictEqual(mongodb_user.present(name, passwd, port={}), ret) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=[]) - with patch.dict(mongodb_user.__salt__, - { - 'mongodb.user_create': mock_t, - 'mongodb.user_find': mock_f - }): - comt = ('User {0} is not present and needs to be created' - ).format(name) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + mongodb_user.__salt__, + {"mongodb.user_create": mock_t, "mongodb.user_find": mock_f}, + ): + comt = ("User {0} is not present and needs to be created").format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(mongodb_user.present(name, passwd), ret) - with patch.dict(mongodb_user.__opts__, {'test': True}): - comt = ('User {0} is not present and needs to be created' - .format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict(mongodb_user.__opts__, {"test": True}): + comt = "User {0} is not present and needs to be created".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(mongodb_user.present(name, passwd), ret) - with patch.dict(mongodb_user.__opts__, {'test': False}): - comt = ('User {0} has been created'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Present'}}) + with patch.dict(mongodb_user.__opts__, {"test": False}): + comt = "User {0} has been created".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {name: "Present"}} + ) self.assertDictEqual(mongodb_user.present(name, passwd), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named user is absent. - ''' - name = 'myapp' + """ + name = "myapp" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock = MagicMock(side_effect=[True, True, False]) mock_t = MagicMock(return_value=True) - with patch.dict(mongodb_user.__salt__, - {'mongodb.user_exists': mock, - 'mongodb.user_remove': mock_t}): - with patch.dict(mongodb_user.__opts__, {'test': True}): - comt = ('User {0} is present and needs to be removed' - .format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + mongodb_user.__salt__, + {"mongodb.user_exists": mock, "mongodb.user_remove": mock_t}, + ): + with patch.dict(mongodb_user.__opts__, {"test": True}): + comt = "User {0} is present and needs to be removed".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(mongodb_user.absent(name), ret) - with patch.dict(mongodb_user.__opts__, {'test': False}): - comt = ('User {0} has been removed'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Absent'}}) + with patch.dict(mongodb_user.__opts__, {"test": False}): + comt = "User {0} has been removed".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {name: "Absent"}} + ) self.assertDictEqual(mongodb_user.absent(name), ret) - comt = 'User {0} is not present'.format(name) - ret.update({'comment': comt, 'result': True, 'changes': {}}) + comt = "User {0} is not present".format(name) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(mongodb_user.absent(name), ret) diff --git a/tests/unit/states/test_mount.py b/tests/unit/states/test_mount.py index 651b78ec6f1..08091f705be 100644 --- a/tests/unit/states/test_mount.py +++ b/tests/unit/states/test_mount.py @@ -1,1103 +1,1260 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +import os # Import Salt Libs import salt.states.mount as mount +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MountTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.mount - ''' + """ + def setup_loader_modules(self): return {mount: {}} # 'mounted' function tests: 1 def test_mounted(self): - ''' + """ Test to verify that a device is mounted. - ''' - name = os.path.realpath('/mnt/sdb') - device = os.path.realpath('/dev/sdb5') - fstype = 'xfs' + """ + name = os.path.realpath("/mnt/sdb") + device = os.path.realpath("/dev/sdb5") + fstype = "xfs" - name2 = os.path.realpath('/mnt/cifs') - device2 = '//SERVER/SHARE/' - fstype2 = 'cifs' - opts2 = ['noowners'] - superopts2 = ['uid=510', 'gid=100', 'username=cifsuser', - 'domain=cifsdomain'] + name2 = os.path.realpath("/mnt/cifs") + device2 = "//SERVER/SHARE/" + fstype2 = "cifs" + opts2 = ["noowners"] + superopts2 = ["uid=510", "gid=100", "username=cifsuser", "domain=cifsdomain"] - name3 = os.path.realpath('/mnt/jfs2') - device3 = '/dev/hd1' - fstype3 = 'jfs2' - opts3 = [''] - superopts3 = ['uid=510', 'gid=100', 'username=jfs2user', - 'domain=jfs2sdomain'] + name3 = os.path.realpath("/mnt/jfs2") + device3 = "/dev/hd1" + fstype3 = "jfs2" + opts3 = [""] + superopts3 = ["uid=510", "gid=100", "username=jfs2user", "domain=jfs2sdomain"] - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock = MagicMock(side_effect=['new', 'present', 'new', 'change', - 'bad config', 'salt', 'present']) + mock = MagicMock( + side_effect=[ + "new", + "present", + "new", + "change", + "bad config", + "salt", + "present", + ] + ) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock_ret = MagicMock(return_value={'retcode': 1}) - mock_mnt = MagicMock(return_value={name: {'device': device, 'opts': [], - 'superopts': []}, - name2: {'device': device2, 'opts': opts2, - 'superopts': superopts2}, - name3: {'device': device3, 'opts': opts3, - 'superopts': superopts3}}) - mock_aixfs_retn = MagicMock(return_value='present') + mock_ret = MagicMock(return_value={"retcode": 1}) + mock_mnt = MagicMock( + return_value={ + name: {"device": device, "opts": [], "superopts": []}, + name2: {"device": device2, "opts": opts2, "superopts": superopts2}, + name3: {"device": device3, "opts": opts3, "superopts": superopts3}, + } + ) + mock_aixfs_retn = MagicMock(return_value="present") mock_emt = MagicMock(return_value={}) - mock_str = MagicMock(return_value='salt') - mock_user = MagicMock(return_value={'uid': 510}) - mock_group = MagicMock(return_value={'gid': 100}) + mock_str = MagicMock(return_value="salt") + mock_user = MagicMock(return_value={"uid": 510}) + mock_group = MagicMock(return_value={"gid": 100}) mock_read_cache = MagicMock(return_value={}) mock_write_cache = MagicMock(return_value=True) - with patch.dict(mount.__grains__, {'os': 'Darwin'}): - with patch.dict(mount.__salt__, {'mount.active': mock_mnt, - 'cmd.run_all': mock_ret, - 'mount.umount': mock_f}), \ - patch('os.path.exists', MagicMock(return_value=True)): - comt = ('Unable to find device with label /dev/sdb5.') - ret.update({'comment': comt}) - self.assertDictEqual(mount.mounted(name, 'LABEL=/dev/sdb5', - fstype), ret) + with patch.dict(mount.__grains__, {"os": "Darwin"}): + with patch.dict( + mount.__salt__, + { + "mount.active": mock_mnt, + "cmd.run_all": mock_ret, + "mount.umount": mock_f, + }, + ), patch("os.path.exists", MagicMock(return_value=True)): + comt = "Unable to find device with label /dev/sdb5." + ret.update({"comment": comt}) + self.assertDictEqual( + mount.mounted(name, "LABEL=/dev/sdb5", fstype), ret + ) - with patch.dict(mount.__opts__, {'test': True}): - comt = ('Remount would be forced because' - ' options (noowners) changed') - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(mount.mounted(name, device, fstype), - ret) + with patch.dict(mount.__opts__, {"test": True}): + comt = ( + "Remount would be forced because" " options (noowners) changed" + ) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual(mount.mounted(name, device, fstype), ret) - with patch.dict(mount.__opts__, {'test': False}): - comt = ('Unable to unmount {0}: False.'.format(name)) - umount = ('Forced unmount and mount because' - ' options (noowners) changed') - ret.update({'comment': comt, 'result': False, - 'changes': {'umount': umount}}) - self.assertDictEqual(mount.mounted(name, device, 'nfs'), - ret) + with patch.dict(mount.__opts__, {"test": False}): + comt = "Unable to unmount {0}: False.".format(name) + umount = ( + "Forced unmount and mount because" " options (noowners) changed" + ) + ret.update( + { + "comment": comt, + "result": False, + "changes": {"umount": umount}, + } + ) + self.assertDictEqual(mount.mounted(name, device, "nfs"), ret) - umount1 = ("Forced unmount because devices don't match. " - "Wanted: {0}, current: {1}, {1}" - .format(os.path.realpath('/dev/sdb6'), device)) - comt = ('Unable to unmount') - ret.update({'comment': comt, 'result': None, - 'changes': {'umount': umount1}}) - self.assertDictEqual(mount.mounted(name, os.path.realpath('/dev/sdb6'), - fstype, opts=[]), ret) + umount1 = ( + "Forced unmount because devices don't match. " + "Wanted: {0}, current: {1}, {1}".format( + os.path.realpath("/dev/sdb6"), device + ) + ) + comt = "Unable to unmount" + ret.update( + { + "comment": comt, + "result": None, + "changes": {"umount": umount1}, + } + ) + self.assertDictEqual( + mount.mounted( + name, os.path.realpath("/dev/sdb6"), fstype, opts=[] + ), + ret, + ) - with patch.dict(mount.__salt__, {'mount.active': mock_emt, - 'mount.mount': mock_str, - 'mount.set_automaster': mock}): - with patch.dict(mount.__opts__, {'test': True}), \ - patch('os.path.exists', MagicMock(return_value=False)): - comt = ('{0} does not exist and would not be created'.format(name)) - ret.update({'comment': comt, 'changes': {}}) - self.assertDictEqual(mount.mounted(name, device, - fstype), ret) + with patch.dict( + mount.__salt__, + { + "mount.active": mock_emt, + "mount.mount": mock_str, + "mount.set_automaster": mock, + }, + ): + with patch.dict(mount.__opts__, {"test": True}), patch( + "os.path.exists", MagicMock(return_value=False) + ): + comt = "{0} does not exist and would not be created".format( + name + ) + ret.update({"comment": comt, "changes": {}}) + self.assertDictEqual(mount.mounted(name, device, fstype), ret) - with patch.dict(mount.__opts__, {'test': False}): - with patch.object(os.path, 'exists', mock_f): - comt = ('Mount directory is not present') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(mount.mounted(name, device, - fstype), ret) + with patch.dict(mount.__opts__, {"test": False}): + with patch.object(os.path, "exists", mock_f): + comt = "Mount directory is not present" + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + mount.mounted(name, device, fstype), ret + ) - with patch.object(os.path, 'exists', mock_t): - comt = ('Mount directory is not present') - ret.update({'comment': 'salt', 'result': False}) - self.assertDictEqual(mount.mounted(name, device, - fstype), ret) + with patch.object(os.path, "exists", mock_t): + comt = "Mount directory is not present" + ret.update({"comment": "salt", "result": False}) + self.assertDictEqual( + mount.mounted(name, device, fstype), ret + ) - with patch.dict(mount.__opts__, {'test': True}), \ - patch('os.path.exists', MagicMock(return_value=False)): - comt = ('{0} does not exist and would neither be created nor mounted. ' - '{0} needs to be written to the fstab in order to be made persistent.' - .format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(mount.mounted(name, device, fstype, - mount=False), ret) + with patch.dict(mount.__opts__, {"test": True}), patch( + "os.path.exists", MagicMock(return_value=False) + ): + comt = ( + "{0} does not exist and would neither be created nor mounted. " + "{0} needs to be written to the fstab in order to be made persistent.".format( + name + ) + ) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + mount.mounted(name, device, fstype, mount=False), ret + ) - with patch.dict(mount.__opts__, {'test': False}), \ - patch('os.path.exists', MagicMock(return_value=False)): - comt = ('{0} not present and not mounted. ' - 'Entry already exists in the fstab.'.format(name)) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(mount.mounted(name, device, fstype, - mount=False), ret) + with patch.dict(mount.__opts__, {"test": False}), patch( + "os.path.exists", MagicMock(return_value=False) + ): + comt = ( + "{0} not present and not mounted. " + "Entry already exists in the fstab.".format(name) + ) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + mount.mounted(name, device, fstype, mount=False), ret + ) - comt = ('{0} not present and not mounted. ' - 'Added new entry to the fstab.'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'persist': 'new'}}) - self.assertDictEqual(mount.mounted(name, device, fstype, - mount=False), ret) + comt = ( + "{0} not present and not mounted. " + "Added new entry to the fstab.".format(name) + ) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"persist": "new"}, + } + ) + self.assertDictEqual( + mount.mounted(name, device, fstype, mount=False), ret + ) - comt = ('{0} not present and not mounted. ' - 'Updated the entry in the fstab.'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {'persist': 'update'}}) - self.assertDictEqual(mount.mounted(name, device, fstype, - mount=False), ret) + comt = ( + "{0} not present and not mounted. " + "Updated the entry in the fstab.".format(name) + ) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"persist": "update"}, + } + ) + self.assertDictEqual( + mount.mounted(name, device, fstype, mount=False), ret + ) - comt = ('{0} not present and not mounted. ' - 'However, the fstab was not found.'.format(name)) - ret.update({'comment': comt, 'result': False, - 'changes': {}}) - self.assertDictEqual(mount.mounted(name, device, fstype, - mount=False), ret) + comt = ( + "{0} not present and not mounted. " + "However, the fstab was not found.".format(name) + ) + ret.update({"comment": comt, "result": False, "changes": {}}) + self.assertDictEqual( + mount.mounted(name, device, fstype, mount=False), ret + ) - comt = ('{0} not present and not mounted'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {}}) - self.assertDictEqual(mount.mounted(name, device, fstype, - mount=False), ret) + comt = "{0} not present and not mounted".format(name) + ret.update({"comment": comt, "result": True, "changes": {}}) + self.assertDictEqual( + mount.mounted(name, device, fstype, mount=False), ret + ) # Test no change for uid provided as a name #25293 - with patch.dict(mount.__grains__, {'os': 'CentOS'}): - with patch.dict(mount.__salt__, {'mount.active': mock_mnt, - 'mount.mount': mock_str, - 'mount.umount': mock_f, - 'mount.read_mount_cache': mock_read_cache, - 'mount.write_mount_cache': mock_write_cache, - 'mount.set_fstab': mock, - 'user.info': mock_user, - 'group.info': mock_group}): - with patch.dict(mount.__opts__, {'test': True}): - with patch.object(os.path, 'exists', mock_t): - comt = 'Target was already mounted. Entry already exists in the fstab.' - ret.update({'name': name2, 'result': True}) - ret.update({'comment': comt, 'changes': {}}) - self.assertDictEqual(mount.mounted(name2, device2, - fstype2, - opts=['uid=user1', - 'gid=group1']), - ret) + with patch.dict(mount.__grains__, {"os": "CentOS"}): + with patch.dict( + mount.__salt__, + { + "mount.active": mock_mnt, + "mount.mount": mock_str, + "mount.umount": mock_f, + "mount.read_mount_cache": mock_read_cache, + "mount.write_mount_cache": mock_write_cache, + "mount.set_fstab": mock, + "user.info": mock_user, + "group.info": mock_group, + }, + ): + with patch.dict(mount.__opts__, {"test": True}): + with patch.object(os.path, "exists", mock_t): + comt = "Target was already mounted. Entry already exists in the fstab." + ret.update({"name": name2, "result": True}) + ret.update({"comment": comt, "changes": {}}) + self.assertDictEqual( + mount.mounted( + name2, + device2, + fstype2, + opts=["uid=user1", "gid=group1"], + ), + ret, + ) - with patch.dict(mount.__grains__, {'os': 'AIX'}): - with patch.dict(mount.__salt__, {'mount.active': mock_mnt, - 'mount.mount': mock_str, - 'mount.umount': mock_f, - 'mount.read_mount_cache': mock_read_cache, - 'mount.write_mount_cache': mock_write_cache, - 'mount.set_filesystems': mock_aixfs_retn, - 'user.info': mock_user, - 'group.info': mock_group}): - with patch.dict(mount.__opts__, {'test': True}): - with patch.object(os.path, 'exists', mock_t): - comt = 'Target was already mounted. Entry already exists in the fstab.' - ret.update({'name': name3, 'result': True}) - ret.update({'comment': comt, 'changes': {}}) - self.assertDictEqual(mount.mounted(name3, device3, - fstype3, - opts=['uid=user1', - 'gid=group1']), - ret) + with patch.dict(mount.__grains__, {"os": "AIX"}): + with patch.dict( + mount.__salt__, + { + "mount.active": mock_mnt, + "mount.mount": mock_str, + "mount.umount": mock_f, + "mount.read_mount_cache": mock_read_cache, + "mount.write_mount_cache": mock_write_cache, + "mount.set_filesystems": mock_aixfs_retn, + "user.info": mock_user, + "group.info": mock_group, + }, + ): + with patch.dict(mount.__opts__, {"test": True}): + with patch.object(os.path, "exists", mock_t): + comt = "Target was already mounted. Entry already exists in the fstab." + ret.update({"name": name3, "result": True}) + ret.update({"comment": comt, "changes": {}}) + self.assertDictEqual( + mount.mounted( + name3, + device3, + fstype3, + opts=["uid=user1", "gid=group1"], + ), + ret, + ) # 'swap' function tests: 1 def test_swap(self): - ''' + """ Test to activates a swap device. - ''' - name = '/mnt/sdb' + """ + name = "/mnt/sdb" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - mock = MagicMock(side_effect=['present', 'new', 'change', 'bad config']) + mock = MagicMock(side_effect=["present", "new", "change", "bad config"]) mock_f = MagicMock(return_value=False) mock_swp = MagicMock(return_value=[name]) - mock_fs = MagicMock(return_value={'none': {'device': name, - 'fstype': 'xfs'}}) - mock_fs_diff = MagicMock(return_value={'none': {'device': 'something_else', - 'fstype': 'xfs'}}) - mock_aixfs = MagicMock(return_value={name: {'dev': name, - 'fstype': 'jfs2'}}) + mock_fs = MagicMock(return_value={"none": {"device": name, "fstype": "xfs"}}) + mock_fs_diff = MagicMock( + return_value={"none": {"device": "something_else", "fstype": "xfs"}} + ) + mock_aixfs = MagicMock(return_value={name: {"dev": name, "fstype": "jfs2"}}) mock_emt = MagicMock(return_value={}) - with patch.dict(mount.__grains__, {'os': 'test'}): - with patch.dict(mount.__salt__, {'mount.swaps': mock_swp, - 'mount.fstab': mock_fs_diff, - 'file.is_link': mock_f}): - with patch.dict(mount.__opts__, {'test': True}): - comt = ('Swap {0} is set to be added to the ' - 'fstab and to be activated'.format(name)) - ret.update({'comment': comt}) + with patch.dict(mount.__grains__, {"os": "test"}): + with patch.dict( + mount.__salt__, + { + "mount.swaps": mock_swp, + "mount.fstab": mock_fs_diff, + "file.is_link": mock_f, + }, + ): + with patch.dict(mount.__opts__, {"test": True}): + comt = ( + "Swap {0} is set to be added to the " + "fstab and to be activated".format(name) + ) + ret.update({"comment": comt}) self.assertDictEqual(mount.swap(name), ret) - with patch.dict(mount.__opts__, {'test': False}): - comt = ('Swap {0} already active'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict(mount.__opts__, {"test": False}): + comt = "Swap {0} already active".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mount.swap(name, persist=False), ret) - with patch.dict(mount.__salt__, {'mount.fstab': mock_emt, - 'mount.set_fstab': mock}): - comt = ('Swap {0} already active'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict( + mount.__salt__, + {"mount.fstab": mock_emt, "mount.set_fstab": mock}, + ): + comt = "Swap {0} already active".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mount.swap(name), ret) - comt = ('Swap /mnt/sdb already active. ' - 'Added new entry to the fstab.') - ret.update({'comment': comt, 'result': True, - 'changes': {'persist': 'new'}}) + comt = ( + "Swap /mnt/sdb already active. " + "Added new entry to the fstab." + ) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"persist": "new"}, + } + ) self.assertDictEqual(mount.swap(name), ret) - comt = ('Swap /mnt/sdb already active. ' - 'Updated the entry in the fstab.') - ret.update({'comment': comt, 'result': True, - 'changes': {'persist': 'update'}}) + comt = ( + "Swap /mnt/sdb already active. " + "Updated the entry in the fstab." + ) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"persist": "update"}, + } + ) self.assertDictEqual(mount.swap(name), ret) - comt = ('Swap /mnt/sdb already active. ' - 'However, the fstab was not found.') - ret.update({'comment': comt, 'result': False, - 'changes': {}}) + comt = ( + "Swap /mnt/sdb already active. " + "However, the fstab was not found." + ) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(mount.swap(name), ret) - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - mock = MagicMock(side_effect=['present', 'new', 'change', 'bad config']) + mock = MagicMock(side_effect=["present", "new", "change", "bad config"]) mock_emt = MagicMock(return_value={}) - with patch.dict(mount.__grains__, {'os': 'test'}): - with patch.dict(mount.__salt__, {'mount.swaps': mock_swp, - 'mount.fstab': mock_fs, - 'file.is_link': mock_f}): - with patch.dict(mount.__opts__, {'test': True}): - comt = ('Swap {0} already active'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict(mount.__grains__, {"os": "test"}): + with patch.dict( + mount.__salt__, + { + "mount.swaps": mock_swp, + "mount.fstab": mock_fs, + "file.is_link": mock_f, + }, + ): + with patch.dict(mount.__opts__, {"test": True}): + comt = "Swap {0} already active".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mount.swap(name), ret) - with patch.dict(mount.__opts__, {'test': False}): - comt = ('Swap {0} already active'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict(mount.__opts__, {"test": False}): + comt = "Swap {0} already active".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mount.swap(name), ret) - with patch.dict(mount.__salt__, {'mount.fstab': mock_emt, - 'mount.set_fstab': mock}): - comt = ('Swap {0} already active'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict( + mount.__salt__, + {"mount.fstab": mock_emt, "mount.set_fstab": mock}, + ): + comt = "Swap {0} already active".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mount.swap(name), ret) - comt = ('Swap /mnt/sdb already active. ' - 'Added new entry to the fstab.') - ret.update({'comment': comt, 'result': True, - 'changes': {'persist': 'new'}}) + comt = ( + "Swap /mnt/sdb already active. " + "Added new entry to the fstab." + ) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"persist": "new"}, + } + ) self.assertDictEqual(mount.swap(name), ret) - comt = ('Swap /mnt/sdb already active. ' - 'Updated the entry in the fstab.') - ret.update({'comment': comt, 'result': True, - 'changes': {'persist': 'update'}}) + comt = ( + "Swap /mnt/sdb already active. " + "Updated the entry in the fstab." + ) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"persist": "update"}, + } + ) self.assertDictEqual(mount.swap(name), ret) - comt = ('Swap /mnt/sdb already active. ' - 'However, the fstab was not found.') - ret.update({'comment': comt, 'result': False, - 'changes': {}}) + comt = ( + "Swap /mnt/sdb already active. " + "However, the fstab was not found." + ) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(mount.swap(name), ret) - with patch.dict(mount.__grains__, {'os': 'AIX'}): - with patch.dict(mount.__salt__, {'mount.swaps': mock_swp, - 'mount.filesystems': mock_aixfs, - 'file.is_link': mock_f}): - with patch.dict(mount.__opts__, {'test': True}): - comt = ('Swap {0} already active'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict(mount.__grains__, {"os": "AIX"}): + with patch.dict( + mount.__salt__, + { + "mount.swaps": mock_swp, + "mount.filesystems": mock_aixfs, + "file.is_link": mock_f, + }, + ): + with patch.dict(mount.__opts__, {"test": True}): + comt = "Swap {0} already active".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mount.swap(name), ret) - with patch.dict(mount.__opts__, {'test': False}): - comt = ('Swap {0} already active. swap not present' - ' in /etc/filesystems on AIX.'.format(name)) - ret.update({'comment': comt, 'result': False}) + with patch.dict(mount.__opts__, {"test": False}): + comt = ( + "Swap {0} already active. swap not present" + " in /etc/filesystems on AIX.".format(name) + ) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(mount.swap(name), ret) - with patch.dict(mount.__salt__, {'mount.filesystems': mock_emt, - 'mount.set_filesystems': mock}): - comt = ('Swap {0} already active. swap not present' - ' in /etc/filesystems on AIX.'.format(name)) - ret.update({'comment': comt, 'result': False}) + with patch.dict( + mount.__salt__, + {"mount.filesystems": mock_emt, "mount.set_filesystems": mock}, + ): + comt = ( + "Swap {0} already active. swap not present" + " in /etc/filesystems on AIX.".format(name) + ) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(mount.swap(name), ret) # 'unmounted' function tests: 1 def test_unmounted(self): - ''' + """ Test to verify that a device is not mounted - ''' - name = '/mnt/sdb' - device = '/dev/sdb5' + """ + name = "/mnt/sdb" + device = "/dev/sdb5" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} mock_f = MagicMock(return_value=False) mock_t = MagicMock(return_value=True) - mock_dev = MagicMock(return_value={name: {'device': device}}) - mock_fs = MagicMock(return_value={name: {'device': name}}) + mock_dev = MagicMock(return_value={name: {"device": device}}) + mock_fs = MagicMock(return_value={name: {"device": name}}) mock_mnt = MagicMock(side_effect=[{name: {}}, {}, {}, {}]) - name3 = os.path.realpath('/mnt/jfs2') - device3 = '/dev/hd1' - fstype3 = 'jfs2' - opts3 = [''] - mock_mnta = MagicMock(return_value={name3: {'device': device3, 'opts': opts3}}) - mock_aixfs = MagicMock(return_value={name: {'dev': name3, 'fstype': fstype3}}) + name3 = os.path.realpath("/mnt/jfs2") + device3 = "/dev/hd1" + fstype3 = "jfs2" + opts3 = [""] + mock_mnta = MagicMock(return_value={name3: {"device": device3, "opts": opts3}}) + mock_aixfs = MagicMock(return_value={name: {"dev": name3, "fstype": fstype3}}) mock_delete_cache = MagicMock(return_value=True) - comt3 = ('Mount point /mnt/sdb is unmounted but needs to be purged ' - 'from /etc/auto_salt to be made persistent') + comt3 = ( + "Mount point /mnt/sdb is unmounted but needs to be purged " + "from /etc/auto_salt to be made persistent" + ) - with patch.dict(mount.__grains__, {'os': 'Darwin'}): - with patch.dict(mount.__salt__, {'mount.active': mock_mnt, - 'mount.automaster': mock_fs, - 'file.is_link': mock_f}): - with patch.dict(mount.__opts__, {'test': True}): - comt = ('Mount point {0} is mounted but should not ' - 'be'.format(name)) - ret.update({'comment': comt}) + with patch.dict(mount.__grains__, {"os": "Darwin"}): + with patch.dict( + mount.__salt__, + { + "mount.active": mock_mnt, + "mount.automaster": mock_fs, + "file.is_link": mock_f, + }, + ): + with patch.dict(mount.__opts__, {"test": True}): + comt = "Mount point {0} is mounted but should not " "be".format( + name + ) + ret.update({"comment": comt}) self.assertDictEqual(mount.unmounted(name, device), ret) - comt = ('Target was already unmounted. ' - 'fstab entry for device {0} not found'.format(device)) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(mount.unmounted(name, device, - persist=True), ret) + comt = ( + "Target was already unmounted. " + "fstab entry for device {0} not found".format(device) + ) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + mount.unmounted(name, device, persist=True), ret + ) - with patch.dict(mount.__salt__, - {'mount.automaster': mock_dev}): - ret.update({'comment': comt3, 'result': None}) - self.assertDictEqual(mount.unmounted(name, device, - persist=True), ret) + with patch.dict(mount.__salt__, {"mount.automaster": mock_dev}): + ret.update({"comment": comt3, "result": None}) + self.assertDictEqual( + mount.unmounted(name, device, persist=True), ret + ) - comt = ('Target was already unmounted') - ret.update({'comment': comt, 'result': True}) + comt = "Target was already unmounted" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mount.unmounted(name, device), ret) - with patch.dict(mount.__grains__, {'os': 'AIX'}): - with patch.dict(mount.__salt__, {'mount.active': mock_mnta, - 'mount.filesystems': mock_aixfs, - 'file.is_link': mock_f}): - with patch.dict(mount.__opts__, {'test': True}): - comt = ('Target was already unmounted') - ret.update({'comment': comt, 'result': True}) + with patch.dict(mount.__grains__, {"os": "AIX"}): + with patch.dict( + mount.__salt__, + { + "mount.active": mock_mnta, + "mount.filesystems": mock_aixfs, + "file.is_link": mock_f, + }, + ): + with patch.dict(mount.__opts__, {"test": True}): + comt = "Target was already unmounted" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mount.unmounted(name, device), ret) - comt = ('Target was already unmounted. ' - 'fstab entry for device /dev/sdb5 not found') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(mount.unmounted(name, device, - persist=True), ret) + comt = ( + "Target was already unmounted. " + "fstab entry for device /dev/sdb5 not found" + ) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + mount.unmounted(name, device, persist=True), ret + ) - with patch.dict(mount.__salt__, - {'mount.filesystems': mock_dev}): - comt = ('Mount point {0} is mounted but should not ' - 'be'.format(name3)) - ret.update({'comment': comt, 'result': None, 'name': name3}) - self.assertDictEqual(mount.unmounted(name3, device3, - persist=True), ret) + with patch.dict(mount.__salt__, {"mount.filesystems": mock_dev}): + comt = "Mount point {0} is mounted but should not " "be".format( + name3 + ) + ret.update({"comment": comt, "result": None, "name": name3}) + self.assertDictEqual( + mount.unmounted(name3, device3, persist=True), ret + ) - with patch.dict(mount.__opts__, {'test': False}), \ - patch.dict(mount.__salt__, {'mount.umount': mock_t, - 'mount.delete_mount_cache': mock_delete_cache}): - comt = ('Target was successfully unmounted') - ret.update({'comment': comt, 'result': True, - 'name': name3, 'changes': {'umount': True}}) + with patch.dict(mount.__opts__, {"test": False}), patch.dict( + mount.__salt__, + { + "mount.umount": mock_t, + "mount.delete_mount_cache": mock_delete_cache, + }, + ): + comt = "Target was successfully unmounted" + ret.update( + { + "comment": comt, + "result": True, + "name": name3, + "changes": {"umount": True}, + } + ) self.assertDictEqual(mount.unmounted(name3, device3), ret) # 'mod_watch' function tests: 1 def test_mod_watch(self): - ''' + """ Test the mounted watcher, called to invoke the watch command. - ''' - name = '/mnt/sdb' + """ + name = "/mnt/sdb" - ret = {'name': name, - 'result': True, - 'comment': 'Watch not supported in unmount at this time', - 'changes': {}} + ret = { + "name": name, + "result": True, + "comment": "Watch not supported in unmount at this time", + "changes": {}, + } - self.assertDictEqual(mount.mod_watch(name, sfun='unmount'), ret) + self.assertDictEqual(mount.mod_watch(name, sfun="unmount"), ret) def test_mounted_multiple_mounts(self): - ''' + """ Test to verify that a device is mounted. - ''' - name = '/mnt/nfs1' - device = 'localhost:/mnt/nfsshare' - fstype = 'nfs4' + """ + name = "/mnt/nfs1" + device = "localhost:/mnt/nfsshare" + fstype = "nfs4" - name2 = '/mnt/nfs2' - device2 = 'localhost:/mnt/nfsshare' - fstype2 = 'nfs4' + name2 = "/mnt/nfs2" + device2 = "localhost:/mnt/nfsshare" + fstype2 = "nfs4" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock = MagicMock(side_effect=['new', 'present', 'new', 'change', - 'bad config', 'salt', 'present']) + mock = MagicMock( + side_effect=[ + "new", + "present", + "new", + "change", + "bad config", + "salt", + "present", + ] + ) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock_ret = MagicMock(return_value={'retcode': 1}) - mock_mnt = MagicMock(return_value={name: {'device': device, 'opts': [], - 'superopts': []}}) + mock_ret = MagicMock(return_value={"retcode": 1}) + mock_mnt = MagicMock( + return_value={name: {"device": device, "opts": [], "superopts": []}} + ) mock_read_cache = MagicMock(return_value={}) mock_write_cache = MagicMock(return_value=True) - mock_user = MagicMock(return_value={'uid': 510}) - mock_group = MagicMock(return_value={'gid': 100}) - mock_str = MagicMock(return_value='salt') - mock_fstab_config = ['localhost:/mnt/nfsshare /mnt/nfs1 nfs defaults 0 0'] + mock_user = MagicMock(return_value={"uid": 510}) + mock_group = MagicMock(return_value={"gid": 100}) + mock_str = MagicMock(return_value="salt") + mock_fstab_config = ["localhost:/mnt/nfsshare /mnt/nfs1 nfs defaults 0 0"] # Test no change for uid provided as a name #25293 - with patch.dict(mount.__grains__, {'os': 'CentOS'}): - with patch.dict(mount.__opts__, {'test': True}): - with patch.dict(mount.__salt__, {'mount.active': mock_mnt, - 'mount.mount': mock_str, - 'mount.umount': mock_f, - 'mount.read_mount_cache': mock_read_cache, - 'mount.write_mount_cache': mock_write_cache, - 'user.info': mock_user, - 'group.info': mock_group}): - with patch.object(os.path, 'exists', mock_t): - comt = '/mnt/nfs2 would be mounted' - ret.update({'name': name2, 'result': None}) - ret.update({'comment': comt, 'changes': {}}) - self.assertDictEqual(mount.mounted(name2, device2, - fstype2, - opts=[]), - ret) + with patch.dict(mount.__grains__, {"os": "CentOS"}): + with patch.dict(mount.__opts__, {"test": True}): + with patch.dict( + mount.__salt__, + { + "mount.active": mock_mnt, + "mount.mount": mock_str, + "mount.umount": mock_f, + "mount.read_mount_cache": mock_read_cache, + "mount.write_mount_cache": mock_write_cache, + "user.info": mock_user, + "group.info": mock_group, + }, + ): + with patch.object(os.path, "exists", mock_t): + comt = "/mnt/nfs2 would be mounted" + ret.update({"name": name2, "result": None}) + ret.update({"comment": comt, "changes": {}}) + self.assertDictEqual( + mount.mounted(name2, device2, fstype2, opts=[]), ret + ) def test__convert_to_fast_none(self): - ''' + """ Test the device name conversor - ''' - assert mount._convert_to('/dev/sda1', None) == '/dev/sda1' + """ + assert mount._convert_to("/dev/sda1", None) == "/dev/sda1" def test__convert_to_fast_device(self): - ''' + """ Test the device name conversor - ''' - assert mount._convert_to('/dev/sda1', 'device') == '/dev/sda1' + """ + assert mount._convert_to("/dev/sda1", "device") == "/dev/sda1" def test__convert_to_fast_token(self): - ''' + """ Test the device name conversor - ''' - assert mount._convert_to('LABEL=home', 'label') == 'LABEL=home' + """ + assert mount._convert_to("LABEL=home", "label") == "LABEL=home" def test__convert_to_device_none(self): - ''' + """ Test the device name conversor - ''' + """ salt_mock = { - 'disk.blkid': MagicMock(return_value={}), + "disk.blkid": MagicMock(return_value={}), } with patch.dict(mount.__salt__, salt_mock): - assert mount._convert_to('/dev/sda1', 'uuid') is None - salt_mock['disk.blkid'].assert_called_with('/dev/sda1') + assert mount._convert_to("/dev/sda1", "uuid") is None + salt_mock["disk.blkid"].assert_called_with("/dev/sda1") def test__convert_to_device_token(self): - ''' + """ Test the device name conversor - ''' - uuid = '988c663d-74a2-432b-ba52-3eea34015f22' + """ + uuid = "988c663d-74a2-432b-ba52-3eea34015f22" salt_mock = { - 'disk.blkid': MagicMock(return_value={ - '/dev/sda1': {'UUID': uuid} - }), + "disk.blkid": MagicMock(return_value={"/dev/sda1": {"UUID": uuid}}), } with patch.dict(mount.__salt__, salt_mock): - uuid = 'UUID={}'.format(uuid) - assert mount._convert_to('/dev/sda1', 'uuid') == uuid - salt_mock['disk.blkid'].assert_called_with('/dev/sda1') + uuid = "UUID={}".format(uuid) + assert mount._convert_to("/dev/sda1", "uuid") == uuid + salt_mock["disk.blkid"].assert_called_with("/dev/sda1") def test__convert_to_token_device(self): - ''' + """ Test the device name conversor - ''' - uuid = '988c663d-74a2-432b-ba52-3eea34015f22' + """ + uuid = "988c663d-74a2-432b-ba52-3eea34015f22" salt_mock = { - 'disk.blkid': MagicMock(return_value={ - '/dev/sda1': {'UUID': uuid} - }), + "disk.blkid": MagicMock(return_value={"/dev/sda1": {"UUID": uuid}}), } with patch.dict(mount.__salt__, salt_mock): - uuid = 'UUID={}'.format(uuid) - assert mount._convert_to(uuid, 'device') == '/dev/sda1' - salt_mock['disk.blkid'].assert_called_with(token=uuid) + uuid = "UUID={}".format(uuid) + assert mount._convert_to(uuid, "device") == "/dev/sda1" + salt_mock["disk.blkid"].assert_called_with(token=uuid) def test_fstab_present_macos_test_present(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': None, - 'changes': {}, - 'comment': ['/home entry is already in /etc/auto_salt.'], + "name": "/dev/sda1", + "result": None, + "changes": {}, + "comment": ["/home entry is already in /etc/auto_salt."], } - grains_mock = {'os': 'MacOS'} - opts_mock = {'test': True} - salt_mock = { - 'mount.set_automaster': MagicMock(return_value='present') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_automaster'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='noowners', - config='/etc/auto_salt', - test=True, - not_change=False) + grains_mock = {"os": "MacOS"} + opts_mock = {"test": True} + salt_mock = {"mount.set_automaster": MagicMock(return_value="present")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_automaster"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="noowners", + config="/etc/auto_salt", + test=True, + not_change=False, + ) def test_fstab_present_aix_test_present(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': None, - 'changes': {}, - 'comment': ['/home entry is already in /etc/filesystems.'], + "name": "/dev/sda1", + "result": None, + "changes": {}, + "comment": ["/home entry is already in /etc/filesystems."], } - grains_mock = {'os': 'AIX'} - opts_mock = {'test': True} - salt_mock = { - 'mount.set_filesystems': MagicMock(return_value='present') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_filesystems'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - mount=True, - opts='', - config='/etc/filesystems', - test=True, - match_on='auto', - not_change=False) + grains_mock = {"os": "AIX"} + opts_mock = {"test": True} + salt_mock = {"mount.set_filesystems": MagicMock(return_value="present")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_filesystems"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + mount=True, + opts="", + config="/etc/filesystems", + test=True, + match_on="auto", + not_change=False, + ) def test_fstab_present_test_present(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': None, - 'changes': {}, - 'comment': ['/home entry is already in /etc/fstab.'], + "name": "/dev/sda1", + "result": None, + "changes": {}, + "comment": ["/home entry is already in /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': True} - salt_mock = { - 'mount.set_fstab': MagicMock(return_value='present') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_fstab'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - test=True, - match_on='auto', - not_change=False) + grains_mock = {"os": "Linux"} + opts_mock = {"test": True} + salt_mock = {"mount.set_fstab": MagicMock(return_value="present")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_fstab"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + test=True, + match_on="auto", + not_change=False, + ) def test_fstab_present_test_new(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': None, - 'changes': {}, - 'comment': ['/home entry will be written in /etc/fstab.'], + "name": "/dev/sda1", + "result": None, + "changes": {}, + "comment": ["/home entry will be written in /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': True} - salt_mock = { - 'mount.set_fstab': MagicMock(return_value='new') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_fstab'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - test=True, - match_on='auto', - not_change=False) + grains_mock = {"os": "Linux"} + opts_mock = {"test": True} + salt_mock = {"mount.set_fstab": MagicMock(return_value="new")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_fstab"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + test=True, + match_on="auto", + not_change=False, + ) def test_fstab_present_test_change(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': None, - 'changes': {}, - 'comment': ['/home entry will be updated in /etc/fstab.'], + "name": "/dev/sda1", + "result": None, + "changes": {}, + "comment": ["/home entry will be updated in /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': True} - salt_mock = { - 'mount.set_fstab': MagicMock(return_value='change') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_fstab'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - test=True, - match_on='auto', - not_change=False) + grains_mock = {"os": "Linux"} + opts_mock = {"test": True} + salt_mock = {"mount.set_fstab": MagicMock(return_value="change")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_fstab"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + test=True, + match_on="auto", + not_change=False, + ) def test_fstab_present_test_error(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': False, - 'changes': {}, - 'comment': ['/home entry cannot be created in /etc/fstab: error.'], + "name": "/dev/sda1", + "result": False, + "changes": {}, + "comment": ["/home entry cannot be created in /etc/fstab: error."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': True} - salt_mock = { - 'mount.set_fstab': MagicMock(return_value='error') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_fstab'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - test=True, - match_on='auto', - not_change=False) + grains_mock = {"os": "Linux"} + opts_mock = {"test": True} + salt_mock = {"mount.set_fstab": MagicMock(return_value="error")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_fstab"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + test=True, + match_on="auto", + not_change=False, + ) def test_fstab_present_macos_present(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': True, - 'changes': {}, - 'comment': ['/home entry was already in /etc/auto_salt.'], + "name": "/dev/sda1", + "result": True, + "changes": {}, + "comment": ["/home entry was already in /etc/auto_salt."], } - grains_mock = {'os': 'MacOS'} - opts_mock = {'test': False} - salt_mock = { - 'mount.set_automaster': MagicMock(return_value='present') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_automaster'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='noowners', - config='/etc/auto_salt', - not_change=False) + grains_mock = {"os": "MacOS"} + opts_mock = {"test": False} + salt_mock = {"mount.set_automaster": MagicMock(return_value="present")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_automaster"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="noowners", + config="/etc/auto_salt", + not_change=False, + ) def test_fstab_present_aix_present(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': True, - 'changes': {}, - 'comment': ['/home entry was already in /etc/filesystems.'], + "name": "/dev/sda1", + "result": True, + "changes": {}, + "comment": ["/home entry was already in /etc/filesystems."], } - grains_mock = {'os': 'AIX'} - opts_mock = {'test': False} - salt_mock = { - 'mount.set_filesystems': MagicMock(return_value='present') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_filesystems'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - mount=True, - opts='', - config='/etc/filesystems', - match_on='auto', - not_change=False) + grains_mock = {"os": "AIX"} + opts_mock = {"test": False} + salt_mock = {"mount.set_filesystems": MagicMock(return_value="present")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_filesystems"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + mount=True, + opts="", + config="/etc/filesystems", + match_on="auto", + not_change=False, + ) def test_fstab_present_present(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': True, - 'changes': {}, - 'comment': ['/home entry was already in /etc/fstab.'], + "name": "/dev/sda1", + "result": True, + "changes": {}, + "comment": ["/home entry was already in /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': False} - salt_mock = { - 'mount.set_fstab': MagicMock(return_value='present') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_fstab'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - match_on='auto', - not_change=False) + grains_mock = {"os": "Linux"} + opts_mock = {"test": False} + salt_mock = {"mount.set_fstab": MagicMock(return_value="present")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_fstab"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + match_on="auto", + not_change=False, + ) def test_fstab_present_new(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': True, - 'changes': {'persist': 'new'}, - 'comment': ['/home entry added in /etc/fstab.'], + "name": "/dev/sda1", + "result": True, + "changes": {"persist": "new"}, + "comment": ["/home entry added in /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': False} - salt_mock = { - 'mount.set_fstab': MagicMock(return_value='new') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_fstab'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - match_on='auto', - not_change=False) + grains_mock = {"os": "Linux"} + opts_mock = {"test": False} + salt_mock = {"mount.set_fstab": MagicMock(return_value="new")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_fstab"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + match_on="auto", + not_change=False, + ) def test_fstab_present_change(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': True, - 'changes': {'persist': 'change'}, - 'comment': ['/home entry updated in /etc/fstab.'], + "name": "/dev/sda1", + "result": True, + "changes": {"persist": "change"}, + "comment": ["/home entry updated in /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': False} - salt_mock = { - 'mount.set_fstab': MagicMock(return_value='change') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_fstab'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - match_on='auto', - not_change=False) + grains_mock = {"os": "Linux"} + opts_mock = {"test": False} + salt_mock = {"mount.set_fstab": MagicMock(return_value="change")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_fstab"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + match_on="auto", + not_change=False, + ) def test_fstab_present_fail(self): - ''' + """ Test fstab_present - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': False, - 'changes': {}, - 'comment': ['/home entry cannot be changed in /etc/fstab: error.'], + "name": "/dev/sda1", + "result": False, + "changes": {}, + "comment": ["/home entry cannot be changed in /etc/fstab: error."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': False} - salt_mock = { - 'mount.set_fstab': MagicMock(return_value='error') - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_present('/dev/sda1', '/home', 'ext2') == ret - salt_mock['mount.set_fstab'].assert_called_with(name='/home', - device='/dev/sda1', - fstype='ext2', - opts='defaults', - dump=0, - pass_num=0, - config='/etc/fstab', - match_on='auto', - not_change=False) + grains_mock = {"os": "Linux"} + opts_mock = {"test": False} + salt_mock = {"mount.set_fstab": MagicMock(return_value="error")} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_present("/dev/sda1", "/home", "ext2") == ret + salt_mock["mount.set_fstab"].assert_called_with( + name="/home", + device="/dev/sda1", + fstype="ext2", + opts="defaults", + dump=0, + pass_num=0, + config="/etc/fstab", + match_on="auto", + not_change=False, + ) def test_fstab_absent_macos_test_absent(self): - ''' + """ Test fstab_absent - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': None, - 'changes': {}, - 'comment': ['/home entry is already missing in /etc/auto_salt.'], + "name": "/dev/sda1", + "result": None, + "changes": {}, + "comment": ["/home entry is already missing in /etc/auto_salt."], } - grains_mock = {'os': 'MacOS'} - opts_mock = {'test': True} - salt_mock = { - 'mount.automaster': MagicMock(return_value={}) - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_absent('/dev/sda1', '/home') == ret - salt_mock['mount.automaster'].assert_called_with('/etc/auto_salt') + grains_mock = {"os": "MacOS"} + opts_mock = {"test": True} + salt_mock = {"mount.automaster": MagicMock(return_value={})} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent("/dev/sda1", "/home") == ret + salt_mock["mount.automaster"].assert_called_with("/etc/auto_salt") def test_fstab_absent_aix_test_absent(self): - ''' + """ Test fstab_absent - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': None, - 'changes': {}, - 'comment': ['/home entry is already missing in /etc/filesystems.'], + "name": "/dev/sda1", + "result": None, + "changes": {}, + "comment": ["/home entry is already missing in /etc/filesystems."], } - grains_mock = {'os': 'AIX'} - opts_mock = {'test': True} - salt_mock = { - 'mount.filesystems': MagicMock(return_value={}) - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_absent('/dev/sda1', '/home') == ret - salt_mock['mount.filesystems'].assert_called_with('/etc/filesystems') + grains_mock = {"os": "AIX"} + opts_mock = {"test": True} + salt_mock = {"mount.filesystems": MagicMock(return_value={})} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent("/dev/sda1", "/home") == ret + salt_mock["mount.filesystems"].assert_called_with("/etc/filesystems") def test_fstab_absent_test_absent(self): - ''' + """ Test fstab_absent - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': None, - 'changes': {}, - 'comment': ['/home entry is already missing in /etc/fstab.'], + "name": "/dev/sda1", + "result": None, + "changes": {}, + "comment": ["/home entry is already missing in /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': True} - salt_mock = { - 'mount.fstab': MagicMock(return_value={}) - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_absent('/dev/sda1', '/home') == ret - salt_mock['mount.fstab'].assert_called_with('/etc/fstab') + grains_mock = {"os": "Linux"} + opts_mock = {"test": True} + salt_mock = {"mount.fstab": MagicMock(return_value={})} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent("/dev/sda1", "/home") == ret + salt_mock["mount.fstab"].assert_called_with("/etc/fstab") def test_fstab_absent_test_present(self): - ''' + """ Test fstab_absent - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': None, - 'changes': {}, - 'comment': ['/home entry will be removed from /etc/fstab.'], + "name": "/dev/sda1", + "result": None, + "changes": {}, + "comment": ["/home entry will be removed from /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': True} - salt_mock = { - 'mount.fstab': MagicMock(return_value={'/home': {}}) - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_absent('/dev/sda1', '/home') == ret - salt_mock['mount.fstab'].assert_called_with('/etc/fstab') + grains_mock = {"os": "Linux"} + opts_mock = {"test": True} + salt_mock = {"mount.fstab": MagicMock(return_value={"/home": {}})} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent("/dev/sda1", "/home") == ret + salt_mock["mount.fstab"].assert_called_with("/etc/fstab") def test_fstab_absent_macos_present(self): - ''' + """ Test fstab_absent - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': True, - 'changes': {'persist': 'removed'}, - 'comment': ['/home entry removed from /etc/auto_salt.'], + "name": "/dev/sda1", + "result": True, + "changes": {"persist": "removed"}, + "comment": ["/home entry removed from /etc/auto_salt."], } - grains_mock = {'os': 'MacOS'} - opts_mock = {'test': False} + grains_mock = {"os": "MacOS"} + opts_mock = {"test": False} salt_mock = { - 'mount.automaster': MagicMock(return_value={'/home': {}}), - 'mount.rm_automaster': MagicMock(return_value=True) + "mount.automaster": MagicMock(return_value={"/home": {}}), + "mount.rm_automaster": MagicMock(return_value=True), } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_absent('/dev/sda1', '/home') == ret - salt_mock['mount.automaster'].assert_called_with('/etc/auto_salt') - salt_mock['mount.rm_automaster'].assert_called_with(name='/home', - device='/dev/sda1', - config='/etc/auto_salt') + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent("/dev/sda1", "/home") == ret + salt_mock["mount.automaster"].assert_called_with("/etc/auto_salt") + salt_mock["mount.rm_automaster"].assert_called_with( + name="/home", device="/dev/sda1", config="/etc/auto_salt" + ) def test_fstab_absent_aix_present(self): - ''' + """ Test fstab_absent - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': True, - 'changes': {'persist': 'removed'}, - 'comment': ['/home entry removed from /etc/filesystems.'], + "name": "/dev/sda1", + "result": True, + "changes": {"persist": "removed"}, + "comment": ["/home entry removed from /etc/filesystems."], } - grains_mock = {'os': 'AIX'} - opts_mock = {'test': False} + grains_mock = {"os": "AIX"} + opts_mock = {"test": False} salt_mock = { - 'mount.filesystems': MagicMock(return_value={'/home': {}}), - 'mount.rm_filesystems': MagicMock(return_value=True) + "mount.filesystems": MagicMock(return_value={"/home": {}}), + "mount.rm_filesystems": MagicMock(return_value=True), } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_absent('/dev/sda1', '/home') == ret - salt_mock['mount.filesystems'].assert_called_with('/etc/filesystems') - salt_mock['mount.rm_filesystems'].assert_called_with(name='/home', - device='/dev/sda1', - config='/etc/filesystems') + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent("/dev/sda1", "/home") == ret + salt_mock["mount.filesystems"].assert_called_with("/etc/filesystems") + salt_mock["mount.rm_filesystems"].assert_called_with( + name="/home", device="/dev/sda1", config="/etc/filesystems" + ) def test_fstab_absent_present(self): - ''' + """ Test fstab_absent - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': True, - 'changes': {'persist': 'removed'}, - 'comment': ['/home entry removed from /etc/fstab.'], + "name": "/dev/sda1", + "result": True, + "changes": {"persist": "removed"}, + "comment": ["/home entry removed from /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': False} + grains_mock = {"os": "Linux"} + opts_mock = {"test": False} salt_mock = { - 'mount.fstab': MagicMock(return_value={'/home': {}}), - 'mount.rm_fstab': MagicMock(return_value=True) + "mount.fstab": MagicMock(return_value={"/home": {}}), + "mount.rm_fstab": MagicMock(return_value=True), } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_absent('/dev/sda1', '/home') == ret - salt_mock['mount.fstab'].assert_called_with('/etc/fstab') - salt_mock['mount.rm_fstab'].assert_called_with(name='/home', - device='/dev/sda1', - config='/etc/fstab') + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent("/dev/sda1", "/home") == ret + salt_mock["mount.fstab"].assert_called_with("/etc/fstab") + salt_mock["mount.rm_fstab"].assert_called_with( + name="/home", device="/dev/sda1", config="/etc/fstab" + ) def test_fstab_absent_absent(self): - ''' + """ Test fstab_absent - ''' + """ ret = { - 'name': '/dev/sda1', - 'result': True, - 'changes': {}, - 'comment': ['/home entry is already missing in /etc/fstab.'], + "name": "/dev/sda1", + "result": True, + "changes": {}, + "comment": ["/home entry is already missing in /etc/fstab."], } - grains_mock = {'os': 'Linux'} - opts_mock = {'test': False} - salt_mock = { - 'mount.fstab': MagicMock(return_value={}) - } - with patch.dict(mount.__grains__, grains_mock), \ - patch.dict(mount.__opts__, opts_mock), \ - patch.dict(mount.__salt__, salt_mock): - assert mount.fstab_absent('/dev/sda1', '/home') == ret - salt_mock['mount.fstab'].assert_called_with('/etc/fstab') + grains_mock = {"os": "Linux"} + opts_mock = {"test": False} + salt_mock = {"mount.fstab": MagicMock(return_value={})} + with patch.dict(mount.__grains__, grains_mock), patch.dict( + mount.__opts__, opts_mock + ), patch.dict(mount.__salt__, salt_mock): + assert mount.fstab_absent("/dev/sda1", "/home") == ret + salt_mock["mount.fstab"].assert_called_with("/etc/fstab") diff --git a/tests/unit/states/test_mysql_grants.py b/tests/unit/states/test_mysql_grants.py index 3b30b8f42fe..2547bbbac55 100644 --- a/tests/unit/states/test_mysql_grants.py +++ b/tests/unit/states/test_mysql_grants.py @@ -1,113 +1,115 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.mysql_grants as mysql_grants +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MysqlGrantsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.mysql_grants - ''' + """ + def setup_loader_modules(self): return {mysql_grants: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the grant is present with the specified properties. - ''' - name = 'frank_exampledb' + """ + name = "frank_exampledb" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(side_effect=[True, False, False, False]) mock_t = MagicMock(return_value=True) - mock_str = MagicMock(return_value='salt') + mock_str = MagicMock(return_value="salt") mock_none = MagicMock(return_value=None) - with patch.dict(mysql_grants.__salt__, {'mysql.grant_exists': mock, - 'mysql.grant_add': mock_t}): - comt = ('Grant None on None to None@localhost is already present') - ret.update({'comment': comt}) + with patch.dict( + mysql_grants.__salt__, + {"mysql.grant_exists": mock, "mysql.grant_add": mock_t}, + ): + comt = "Grant None on None to None@localhost is already present" + ret.update({"comment": comt}) self.assertDictEqual(mysql_grants.present(name), ret) - with patch.object(mysql_grants, '_get_mysql_error', mock_str): - ret.update({'comment': 'salt', 'result': False}) + with patch.object(mysql_grants, "_get_mysql_error", mock_str): + ret.update({"comment": "salt", "result": False}) self.assertDictEqual(mysql_grants.present(name), ret) - with patch.object(mysql_grants, '_get_mysql_error', mock_none): - with patch.dict(mysql_grants.__opts__, {'test': True}): - comt = ('MySQL grant frank_exampledb is set to be created') - ret.update({'comment': comt, 'result': None}) + with patch.object(mysql_grants, "_get_mysql_error", mock_none): + with patch.dict(mysql_grants.__opts__, {"test": True}): + comt = "MySQL grant frank_exampledb is set to be created" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(mysql_grants.present(name), ret) - with patch.dict(mysql_grants.__opts__, {'test': False}): - comt = ('Grant None on None to None@localhost' - ' has been added') - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Present'}}) + with patch.dict(mysql_grants.__opts__, {"test": False}): + comt = "Grant None on None to None@localhost" " has been added" + ret.update( + {"comment": comt, "result": True, "changes": {name: "Present"}} + ) self.assertDictEqual(mysql_grants.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the grant is absent. - ''' - name = 'frank_exampledb' + """ + name = "frank_exampledb" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(side_effect=[True, False]) mock_t = MagicMock(side_effect=[True, True, True, False, False]) - mock_str = MagicMock(return_value='salt') + mock_str = MagicMock(return_value="salt") mock_none = MagicMock(return_value=None) - with patch.dict(mysql_grants.__salt__, {'mysql.grant_exists': mock_t, - 'mysql.grant_revoke': mock}): - with patch.dict(mysql_grants.__opts__, {'test': True}): - comt = ('MySQL grant frank_exampledb is set to be revoked') - ret.update({'comment': comt, 'result': None}) + with patch.dict( + mysql_grants.__salt__, + {"mysql.grant_exists": mock_t, "mysql.grant_revoke": mock}, + ): + with patch.dict(mysql_grants.__opts__, {"test": True}): + comt = "MySQL grant frank_exampledb is set to be revoked" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(mysql_grants.absent(name), ret) - with patch.dict(mysql_grants.__opts__, {'test': False}): - comt = ('Grant None on None for None@localhost' - ' has been revoked') - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Absent'}}) + with patch.dict(mysql_grants.__opts__, {"test": False}): + comt = "Grant None on None for None@localhost" " has been revoked" + ret.update( + {"comment": comt, "result": True, "changes": {name: "Absent"}} + ) self.assertDictEqual(mysql_grants.absent(name), ret) - with patch.object(mysql_grants, '_get_mysql_error', mock_str): - comt = ('Unable to revoke grant None on None' - ' for None@localhost (salt)') - ret.update({'comment': comt, 'result': False, - 'changes': {}}) + with patch.object(mysql_grants, "_get_mysql_error", mock_str): + comt = ( + "Unable to revoke grant None on None" + " for None@localhost (salt)" + ) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(mysql_grants.absent(name), ret) - comt = ('Unable to determine if grant None on ' - 'None for None@localhost exists (salt)') - ret.update({'comment': comt}) + comt = ( + "Unable to determine if grant None on " + "None for None@localhost exists (salt)" + ) + ret.update({"comment": comt}) self.assertDictEqual(mysql_grants.absent(name), ret) - with patch.object(mysql_grants, '_get_mysql_error', mock_none): - comt = ('Grant None on None to None@localhost is not present,' - ' so it cannot be revoked') - ret.update({'comment': comt, 'result': True}) + with patch.object(mysql_grants, "_get_mysql_error", mock_none): + comt = ( + "Grant None on None to None@localhost is not present," + " so it cannot be revoked" + ) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mysql_grants.absent(name), ret) diff --git a/tests/unit/states/test_mysql_query.py b/tests/unit/states/test_mysql_query.py index 95a7e00774a..8457f3e465d 100644 --- a/tests/unit/states/test_mysql_query.py +++ b/tests/unit/states/test_mysql_query.py @@ -1,120 +1,148 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +import os # Import Salt Libs import salt.states.mysql_query as mysql_query +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MysqlQueryTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.mysql_query - ''' + """ + def setup_loader_modules(self): return {mysql_query: {}} # 'run' function tests: 1 def test_run(self): - ''' + """ Test to execute an arbitrary query on the specified database. - ''' - name = 'query_id' - database = 'my_database' + """ + name = "query_id" + database = "my_database" query = "SELECT * FROM table;" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock_str = MagicMock(return_value='salt') + mock_str = MagicMock(return_value="salt") mock_none = MagicMock(return_value=None) - mock_dict = MagicMock(return_value={'salt': 'SALT'}) - mock_lst = MagicMock(return_value=['grain']) - with patch.dict(mysql_query.__salt__, {'mysql.db_exists': mock_f}): - with patch.object(mysql_query, '_get_mysql_error', mock_str): - ret.update({'comment': 'salt', 'result': False}) - self.assertDictEqual(mysql_query.run(name, database, query), - ret) + mock_dict = MagicMock(return_value={"salt": "SALT"}) + mock_lst = MagicMock(return_value=["grain"]) + with patch.dict(mysql_query.__salt__, {"mysql.db_exists": mock_f}): + with patch.object(mysql_query, "_get_mysql_error", mock_str): + ret.update({"comment": "salt", "result": False}) + self.assertDictEqual(mysql_query.run(name, database, query), ret) - with patch.object(mysql_query, '_get_mysql_error', mock_none): - comt = ('Database {0} is not present'.format(name)) - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(mysql_query.run(name, database, query), - ret) + with patch.object(mysql_query, "_get_mysql_error", mock_none): + comt = "Database {0} is not present".format(name) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual(mysql_query.run(name, database, query), ret) - with patch.dict(mysql_query.__salt__, {'mysql.db_exists': mock_t, - 'grains.ls': mock_lst, - 'grains.get': mock_dict, - 'mysql.query': mock_str}): - comt = ('No execution needed. Grain grain already set') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(mysql_query.run(name, database, query, - output='grain', grain='grain', - overwrite=False), ret) + with patch.dict( + mysql_query.__salt__, + { + "mysql.db_exists": mock_t, + "grains.ls": mock_lst, + "grains.get": mock_dict, + "mysql.query": mock_str, + }, + ): + comt = "No execution needed. Grain grain already set" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + mysql_query.run( + name, + database, + query, + output="grain", + grain="grain", + overwrite=False, + ), + ret, + ) - with patch.dict(mysql_query.__opts__, {'test': True}): - comt = ('Query would execute, storing result in grain: grain') - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(mysql_query.run(name, database, query, - output='grain', - grain='grain'), ret) + with patch.dict(mysql_query.__opts__, {"test": True}): + comt = "Query would execute, storing result in grain: grain" + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + mysql_query.run( + name, database, query, output="grain", grain="grain" + ), + ret, + ) - comt = ('Query would execute, storing result' - ' in grain: grain:salt') - ret.update({'comment': comt}) - self.assertDictEqual(mysql_query.run(name, database, query, - output='grain', - grain='grain', - key='salt'), ret) + comt = "Query would execute, storing result" " in grain: grain:salt" + ret.update({"comment": comt}) + self.assertDictEqual( + mysql_query.run( + name, database, query, output="grain", grain="grain", key="salt" + ), + ret, + ) - comt = ('Query would execute, storing result in file: salt') - ret.update({'comment': comt}) - self.assertDictEqual(mysql_query.run(name, database, query, - output='salt', - grain='grain'), ret) + comt = "Query would execute, storing result in file: salt" + ret.update({"comment": comt}) + self.assertDictEqual( + mysql_query.run( + name, database, query, output="salt", grain="grain" + ), + ret, + ) - comt = ('Query would execute, not storing result') - ret.update({'comment': comt}) - self.assertDictEqual(mysql_query.run(name, database, query), - ret) + comt = "Query would execute, not storing result" + ret.update({"comment": comt}) + self.assertDictEqual(mysql_query.run(name, database, query), ret) - comt = ('No execution needed. Grain grain:salt already set') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(mysql_query.run(name, database, query, - output='grain', grain='grain', - key='salt', overwrite=False), - ret) + comt = "No execution needed. Grain grain:salt already set" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + mysql_query.run( + name, + database, + query, + output="grain", + grain="grain", + key="salt", + overwrite=False, + ), + ret, + ) - comt = ("Error: output type 'grain' needs the grain parameter\n") - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(mysql_query.run(name, database, query, - output='grain'), ret) + comt = "Error: output type 'grain' needs the grain parameter\n" + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + mysql_query.run(name, database, query, output="grain"), ret + ) - with patch.object(os.path, 'isfile', mock_t): - comt = ('No execution needed. File salt already set') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(mysql_query.run(name, database, query, - output='salt', - grain='grain', - overwrite=False), ret) + with patch.object(os.path, "isfile", mock_t): + comt = "No execution needed. File salt already set" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + mysql_query.run( + name, + database, + query, + output="salt", + grain="grain", + overwrite=False, + ), + ret, + ) - with patch.dict(mysql_query.__opts__, {'test': False}): - ret.update({'comment': 'salt', - 'changes': {'query': 'Executed'}}) - self.assertDictEqual(mysql_query.run(name, database, query), - ret) + with patch.dict(mysql_query.__opts__, {"test": False}): + ret.update({"comment": "salt", "changes": {"query": "Executed"}}) + self.assertDictEqual(mysql_query.run(name, database, query), ret) diff --git a/tests/unit/states/test_mysql_user.py b/tests/unit/states/test_mysql_user.py index c1797a343a4..b066152390d 100644 --- a/tests/unit/states/test_mysql_user.py +++ b/tests/unit/states/test_mysql_user.py @@ -1,153 +1,182 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.mysql_user as mysql_user import salt.utils.data +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class MysqlUserTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.mysql_user - ''' + """ + def setup_loader_modules(self): return {mysql_user: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named user is present with the specified properties. - ''' - name = 'frank' + """ + name = "frank" password = "bob@cat" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[True, False, True, False, False, True, - False, False, False, False, False, True]) + mock = MagicMock( + side_effect=[ + True, + False, + True, + False, + False, + True, + False, + False, + False, + False, + False, + True, + ] + ) mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - mock_str = MagicMock(return_value='salt') + mock_str = MagicMock(return_value="salt") mock_none = MagicMock(return_value=None) - mock_sn = MagicMock(side_effect=[None, 'salt', None, None, None]) - with patch.object(salt.utils.data, 'is_true', mock_f): - comt = ('Either password or password_hash must be specified,' - ' unless allow_passwordless is True') - ret.update({'comment': comt}) + mock_sn = MagicMock(side_effect=[None, "salt", None, None, None]) + with patch.object(salt.utils.data, "is_true", mock_f): + comt = ( + "Either password or password_hash must be specified," + " unless allow_passwordless is True" + ) + ret.update({"comment": comt}) self.assertDictEqual(mysql_user.present(name), ret) - with patch.dict(mysql_user.__salt__, {'mysql.user_exists': mock, - 'mysql.user_chpass': mock_t}): - with patch.object(salt.utils.data, 'is_true', mock_t): - comt = ('User frank@localhost is already present' - ' with passwordless login') - ret.update({'comment': comt, 'result': True}) + with patch.dict( + mysql_user.__salt__, + {"mysql.user_exists": mock, "mysql.user_chpass": mock_t}, + ): + with patch.object(salt.utils.data, "is_true", mock_t): + comt = ( + "User frank@localhost is already present" " with passwordless login" + ) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(mysql_user.present(name), ret) - with patch.object(mysql_user, '_get_mysql_error', mock_str): - ret.update({'comment': 'salt', 'result': False}) + with patch.object(mysql_user, "_get_mysql_error", mock_str): + ret.update({"comment": "salt", "result": False}) self.assertDictEqual(mysql_user.present(name), ret) - with patch.object(mysql_user, '_get_mysql_error', mock_str): - comt = ('User frank@localhost is already present' - ' with the desired password') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(mysql_user.present(name, - password=password), ret) + with patch.object(mysql_user, "_get_mysql_error", mock_str): + comt = ( + "User frank@localhost is already present" + " with the desired password" + ) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(mysql_user.present(name, password=password), ret) - with patch.object(mysql_user, '_get_mysql_error', mock_str): - ret.update({'comment': 'salt', 'result': False}) - self.assertDictEqual(mysql_user.present(name, - password=password), - ret) + with patch.object(mysql_user, "_get_mysql_error", mock_str): + ret.update({"comment": "salt", "result": False}) + self.assertDictEqual( + mysql_user.present(name, password=password), ret + ) - with patch.object(mysql_user, '_get_mysql_error', mock_none): - with patch.dict(mysql_user.__opts__, {'test': True}): - comt = ('Password for user frank@localhost' - ' is set to be changed') - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(mysql_user.present - (name, password=password), ret) + with patch.object(mysql_user, "_get_mysql_error", mock_none): + with patch.dict(mysql_user.__opts__, {"test": True}): + comt = ( + "Password for user frank@localhost" " is set to be changed" + ) + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + mysql_user.present(name, password=password), ret + ) - with patch.object(mysql_user, '_get_mysql_error', mock_sn): - with patch.dict(mysql_user.__opts__, {'test': False}): - ret.update({'comment': 'salt', 'result': False}) - self.assertDictEqual(mysql_user.present - (name, password=password), ret) + with patch.object(mysql_user, "_get_mysql_error", mock_sn): + with patch.dict(mysql_user.__opts__, {"test": False}): + ret.update({"comment": "salt", "result": False}) + self.assertDictEqual( + mysql_user.present(name, password=password), ret + ) - with patch.dict(mysql_user.__opts__, {'test': True}): - comt = ('User frank@localhost is set to be added') - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(mysql_user.present - (name, password=password), ret) + with patch.dict(mysql_user.__opts__, {"test": True}): + comt = "User frank@localhost is set to be added" + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + mysql_user.present(name, password=password), ret + ) - with patch.dict(mysql_user.__opts__, {'test': False}): - comt = ('Password for user frank@localhost' - ' has been changed') - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Updated'}}) - self.assertDictEqual(mysql_user.present - (name, password=password), ret) + with patch.dict(mysql_user.__opts__, {"test": False}): + comt = "Password for user frank@localhost" " has been changed" + ret.update( + { + "comment": comt, + "result": True, + "changes": {name: "Updated"}, + } + ) + self.assertDictEqual( + mysql_user.present(name, password=password), ret + ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named user is absent. - ''' - name = 'frank_exampledb' + """ + name = "frank_exampledb" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock = MagicMock(side_effect=[True, True, True, False, False, False]) mock_t = MagicMock(side_effect=[True, False]) - mock_str = MagicMock(return_value='salt') + mock_str = MagicMock(return_value="salt") mock_none = MagicMock(return_value=None) - with patch.dict(mysql_user.__salt__, {'mysql.user_exists': mock, - 'mysql.user_remove': mock_t}): - with patch.dict(mysql_user.__opts__, {'test': True}): - comt = ('User frank_exampledb@localhost is set to be removed') - ret.update({'comment': comt, 'result': None}) + with patch.dict( + mysql_user.__salt__, + {"mysql.user_exists": mock, "mysql.user_remove": mock_t}, + ): + with patch.dict(mysql_user.__opts__, {"test": True}): + comt = "User frank_exampledb@localhost is set to be removed" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(mysql_user.absent(name), ret) - with patch.dict(mysql_user.__opts__, {'test': False}): - comt = ('User frank_exampledb@localhost has been removed') - ret.update({'comment': comt, 'result': True, - 'changes': {'frank_exampledb': 'Absent'}}) + with patch.dict(mysql_user.__opts__, {"test": False}): + comt = "User frank_exampledb@localhost has been removed" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"frank_exampledb": "Absent"}, + } + ) self.assertDictEqual(mysql_user.absent(name), ret) - with patch.object(mysql_user, '_get_mysql_error', mock_str): - comt = ('User frank_exampledb@localhost has been removed') - ret.update({'comment': 'salt', 'result': False, - 'changes': {}}) + with patch.object(mysql_user, "_get_mysql_error", mock_str): + comt = "User frank_exampledb@localhost has been removed" + ret.update({"comment": "salt", "result": False, "changes": {}}) self.assertDictEqual(mysql_user.absent(name), ret) - comt = ('User frank_exampledb@localhost has been removed') - ret.update({'comment': 'salt'}) + comt = "User frank_exampledb@localhost has been removed" + ret.update({"comment": "salt"}) self.assertDictEqual(mysql_user.absent(name), ret) - with patch.object(mysql_user, '_get_mysql_error', mock_none): - comt = ('User frank_exampledb@localhost is not present,' - ' so it cannot be removed') - ret.update({'comment': comt, 'result': True, - 'changes': {}}) + with patch.object(mysql_user, "_get_mysql_error", mock_none): + comt = ( + "User frank_exampledb@localhost is not present," + " so it cannot be removed" + ) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(mysql_user.absent(name), ret) diff --git a/tests/unit/states/test_net_napalm_yang.py b/tests/unit/states/test_net_napalm_yang.py index ea66ac9b97f..177bf7160c3 100644 --- a/tests/unit/states/test_net_napalm_yang.py +++ b/tests/unit/states/test_net_napalm_yang.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Anthony Shaw <anthonyshaw@apache.org> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,15 +10,10 @@ import salt.states.net_napalm_yang as netyang # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) -TEST_DATA = { - 'foo': 'bar' -} +TEST_DATA = {"foo": "bar"} class NetyangTestCase(TestCase, LoaderModuleMockMixin): @@ -26,23 +21,28 @@ class NetyangTestCase(TestCase, LoaderModuleMockMixin): return {netyang: {}} def test_managed(self): - ret = {'changes': {}, 'comment': 'Loaded.', - 'name': 'test', 'result': False} - parse = MagicMock(return_value='abcdef') - temp_file = MagicMock(return_value='') - compliance_report = MagicMock(return_value={'complies': False}) - load_config = MagicMock(return_value={'comment': 'Loaded.'}) + ret = {"changes": {}, "comment": "Loaded.", "name": "test", "result": False} + parse = MagicMock(return_value="abcdef") + temp_file = MagicMock(return_value="") + compliance_report = MagicMock(return_value={"complies": False}) + load_config = MagicMock(return_value={"comment": "Loaded."}) file_remove = MagicMock() - with patch('salt.utils.files.fopen'): - with patch.dict(netyang.__salt__, - {'temp.file': temp_file, - 'napalm_yang.parse': parse, - 'napalm_yang.load_config': load_config, - 'napalm_yang.compliance_report': compliance_report, - 'file.remove': file_remove}): - with patch.dict(netyang.__opts__, {'test': False}): - self.assertDictEqual(netyang.managed('test', 'test', models=('model1',)), ret) + with patch("salt.utils.files.fopen"): + with patch.dict( + netyang.__salt__, + { + "temp.file": temp_file, + "napalm_yang.parse": parse, + "napalm_yang.load_config": load_config, + "napalm_yang.compliance_report": compliance_report, + "file.remove": file_remove, + }, + ): + with patch.dict(netyang.__opts__, {"test": False}): + self.assertDictEqual( + netyang.managed("test", "test", models=("model1",)), ret + ) assert parse.called assert temp_file.called assert compliance_report.called @@ -50,14 +50,14 @@ class NetyangTestCase(TestCase, LoaderModuleMockMixin): assert file_remove.called def test_configured(self): - ret = {'changes': {}, 'comment': 'Loaded.', - 'name': 'test', 'result': False} - load_config = MagicMock(return_value={'comment': 'Loaded.'}) + ret = {"changes": {}, "comment": "Loaded.", "name": "test", "result": False} + load_config = MagicMock(return_value={"comment": "Loaded."}) - with patch('salt.utils.files.fopen'): - with patch.dict(netyang.__salt__, - {'napalm_yang.load_config': load_config}): - with patch.dict(netyang.__opts__, {'test': False}): - self.assertDictEqual(netyang.configured('test', 'test', models=('model1',)), ret) + with patch("salt.utils.files.fopen"): + with patch.dict(netyang.__salt__, {"napalm_yang.load_config": load_config}): + with patch.dict(netyang.__opts__, {"test": False}): + self.assertDictEqual( + netyang.configured("test", "test", models=("model1",)), ret + ) assert load_config.called diff --git a/tests/unit/states/test_network.py b/tests/unit/states/test_network.py index 17da548b3b3..e9d0f695368 100644 --- a/tests/unit/states/test_network.py +++ b/tests/unit/states/test_network.py @@ -1,221 +1,261 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import logging # Import Salt Libs import salt.states.network as network -import logging +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + log = logging.getLogger(__name__) class MockNetwork(object): - ''' + """ Mock network class - ''' + """ + def __init__(self): pass @staticmethod def interfaces(): - ''' + """ Mock interface method - ''' - return {'salt': {'up': 1}} + """ + return {"salt": {"up": 1}} class MockGrains(object): - ''' + """ Mock Grains class - ''' + """ + def __init__(self): pass @staticmethod def grains(lis, bol): - ''' + """ Mock grains method - ''' - return {'A': 'B'} + """ + return {"A": "B"} class NetworkTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the network state - ''' + """ + def setup_loader_modules(self): return {network: {}} def test_managed(self): - ''' + """ Test to ensure that the named interface is configured properly - ''' - with patch('salt.states.network.salt.utils.network', MockNetwork()), \ - patch('salt.states.network.salt.loader', MockGrains()): - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} + """ + with patch("salt.states.network.salt.utils.network", MockNetwork()), patch( + "salt.states.network.salt.loader", MockGrains() + ): + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} - change = {'interface': '--- \n+++ \n@@ -1 +1 @@\n-A\n+B', - 'status': 'Interface salt restart to validate'} + change = { + "interface": "--- \n+++ \n@@ -1 +1 @@\n-A\n+B", + "status": "Interface salt restart to validate", + } - mock = MagicMock(side_effect=[AttributeError, 'A', 'A', 'A', 'A', 'A']) + mock = MagicMock(side_effect=[AttributeError, "A", "A", "A", "A", "A"]) with patch.dict(network.__salt__, {"ip.get_interface": mock}): - self.assertDictEqual(network.managed('salt', - 'stack', test='a'), ret) + self.assertDictEqual(network.managed("salt", "stack", test="a"), ret) - mock = MagicMock(return_value='B') + mock = MagicMock(return_value="B") with patch.dict(network.__salt__, {"ip.build_interface": mock}): mock = MagicMock(side_effect=AttributeError) with patch.dict(network.__salt__, {"ip.get_bond": mock}): - self.assertDictEqual(network.managed('salt', - 'bond', - test='a'), ret) + self.assertDictEqual( + network.managed("salt", "bond", test="a"), ret + ) - ret.update({'comment': 'Interface salt is set to be' - ' updated:\n--- \n+++ \n@@ -1 +1 @@\n-A\n+B', - 'result': None}) - self.assertDictEqual(network.managed('salt', 'stack', - test='a'), ret) + ret.update( + { + "comment": "Interface salt is set to be" + " updated:\n--- \n+++ \n@@ -1 +1 @@\n-A\n+B", + "result": None, + } + ) + self.assertDictEqual( + network.managed("salt", "stack", test="a"), ret + ) mock = MagicMock(return_value=True) with patch.dict(network.__salt__, {"ip.down": mock}): with patch.dict(network.__salt__, {"ip.up": mock}): - ret.update({'comment': 'Interface salt updated.', - 'result': True, - 'changes': change}) - self.assertDictEqual(network.managed('salt', 'stack'), - ret) + ret.update( + { + "comment": "Interface salt updated.", + "result": True, + "changes": change, + } + ) + self.assertDictEqual(network.managed("salt", "stack"), ret) with patch.dict(network.__grains__, {"A": True}): - with patch.dict(network.__salt__, - {"saltutil.refresh_modules": mock} - ): - ret.update({'result': True, - 'changes': {'interface': '--- \n+' - '++ \n@@ -1 +1 @@\n-A' - '\n+B', - 'status': 'Interface' - ' salt down'}}) - self.assertDictEqual(network.managed('salt', - 'stack', - False), - ret) + with patch.dict( + network.__salt__, {"saltutil.refresh_modules": mock} + ): + ret.update( + { + "result": True, + "changes": { + "interface": "--- \n+" + "++ \n@@ -1 +1 @@\n-A" + "\n+B", + "status": "Interface" " salt down", + }, + } + ) + self.assertDictEqual( + network.managed("salt", "stack", False), ret + ) def test_routes(self): - ''' + """ Test to manage network interface static routes. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} - mock = MagicMock(side_effect=[AttributeError, False, False, "True", - False, False]) + mock = MagicMock( + side_effect=[AttributeError, False, False, "True", False, False] + ) with patch.dict(network.__salt__, {"ip.get_routes": mock}): - self.assertDictEqual(network.routes('salt'), ret) + self.assertDictEqual(network.routes("salt"), ret) - mock = MagicMock(side_effect=[False, True, '', True, True]) + mock = MagicMock(side_effect=[False, True, "", True, True]) with patch.dict(network.__salt__, {"ip.build_routes": mock}): - ret.update({'result': True, - 'comment': 'Interface salt routes are up to date.' - }) - self.assertDictEqual(network.routes('salt', test='a'), ret) + ret.update( + {"result": True, "comment": "Interface salt routes are up to date."} + ) + self.assertDictEqual(network.routes("salt", test="a"), ret) - ret.update({'comment': 'Interface salt routes are' - ' set to be added.', - 'result': None}) - self.assertDictEqual(network.routes('salt', test='a'), ret) + ret.update( + { + "comment": "Interface salt routes are" " set to be added.", + "result": None, + } + ) + self.assertDictEqual(network.routes("salt", test="a"), ret) - ret.update({'comment': 'Interface salt routes are set to be' - ' updated:\n--- \n+++ \n@@ -1,4 +0,0 @@\n-T\n-r' - '\n-u\n-e'}) - self.assertDictEqual(network.routes('salt', test='a'), ret) + ret.update( + { + "comment": "Interface salt routes are set to be" + " updated:\n--- \n+++ \n@@ -1,4 +0,0 @@\n-T\n-r" + "\n-u\n-e" + } + ) + self.assertDictEqual(network.routes("salt", test="a"), ret) mock = MagicMock(side_effect=[AttributeError, True]) - with patch.dict(network.__salt__, - {"ip.apply_network_settings": mock}): - ret.update({'changes': {'network_routes': - 'Added interface salt routes.'}, - 'comment': '', - 'result': False}) - self.assertDictEqual(network.routes('salt'), ret) + with patch.dict(network.__salt__, {"ip.apply_network_settings": mock}): + ret.update( + { + "changes": { + "network_routes": "Added interface salt routes." + }, + "comment": "", + "result": False, + } + ) + self.assertDictEqual(network.routes("salt"), ret) - ret.update({'changes': {'network_routes': - 'Added interface salt routes.'}, - 'comment': 'Interface salt routes added.', - 'result': True}) - self.assertDictEqual(network.routes('salt'), ret) + ret.update( + { + "changes": { + "network_routes": "Added interface salt routes." + }, + "comment": "Interface salt routes added.", + "result": True, + } + ) + self.assertDictEqual(network.routes("salt"), ret) def test_system(self): - ''' + """ Test to ensure that global network settings are configured properly - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} with patch.dict(network.__opts__, {"test": True}): - mock = MagicMock(side_effect=[AttributeError, False, False, 'As']) - with patch.dict(network.__salt__, - {"ip.get_network_settings": mock}): - self.assertDictEqual(network.system('salt'), ret) + mock = MagicMock(side_effect=[AttributeError, False, False, "As"]) + with patch.dict(network.__salt__, {"ip.get_network_settings": mock}): + self.assertDictEqual(network.system("salt"), ret) - mock = MagicMock(side_effect=[False, True, '']) - with patch.dict(network.__salt__, - {"ip.build_network_settings": mock}): - ret.update({'comment': 'Global network settings' - ' are up to date.', - 'result': True}) - self.assertDictEqual(network.system('salt'), ret) + mock = MagicMock(side_effect=[False, True, ""]) + with patch.dict(network.__salt__, {"ip.build_network_settings": mock}): + ret.update( + { + "comment": "Global network settings" " are up to date.", + "result": True, + } + ) + self.assertDictEqual(network.system("salt"), ret) - ret.update({'comment': 'Global network settings are set to' - ' be added.', - 'result': None}) - self.assertDictEqual(network.system('salt'), ret) + ret.update( + { + "comment": "Global network settings are set to" + " be added.", + "result": None, + } + ) + self.assertDictEqual(network.system("salt"), ret) - ret.update({'comment': 'Global network settings are set to' - ' be updated:\n--- \n+++ \n@@ -1,2 +0,0' - ' @@\n-A\n-s'}) - self.assertDictEqual(network.system('salt'), ret) + ret.update( + { + "comment": "Global network settings are set to" + " be updated:\n--- \n+++ \n@@ -1,2 +0,0" + " @@\n-A\n-s" + } + ) + self.assertDictEqual(network.system("salt"), ret) with patch.dict(network.__opts__, {"test": False}): mock = MagicMock(side_effect=[False, False]) - with patch.dict(network.__salt__, - {"ip.get_network_settings": mock}): + with patch.dict(network.__salt__, {"ip.get_network_settings": mock}): mock = MagicMock(side_effect=[True, True]) - with patch.dict(network.__salt__, - {"ip.build_network_settings": mock}): + with patch.dict(network.__salt__, {"ip.build_network_settings": mock}): mock = MagicMock(side_effect=[AttributeError, True]) - with patch.dict(network.__salt__, - {"ip.apply_network_settings": mock}): - ret.update({'changes': {'network_settings': - 'Added global network' - ' settings.'}, - 'comment': '', - 'result': False}) - self.assertDictEqual(network.system('salt'), ret) + with patch.dict( + network.__salt__, {"ip.apply_network_settings": mock} + ): + ret.update( + { + "changes": { + "network_settings": "Added global network" + " settings." + }, + "comment": "", + "result": False, + } + ) + self.assertDictEqual(network.system("salt"), ret) - ret.update({'changes': {'network_settings': - 'Added global network' - ' settings.'}, - 'comment': 'Global network settings' - ' are up to date.', - 'result': True}) - self.assertDictEqual(network.system('salt'), ret) + ret.update( + { + "changes": { + "network_settings": "Added global network" + " settings." + }, + "comment": "Global network settings" " are up to date.", + "result": True, + } + ) + self.assertDictEqual(network.system("salt"), ret) diff --git a/tests/unit/states/test_nexus.py b/tests/unit/states/test_nexus.py index 17299ecbc18..1716d9b75f6 100644 --- a/tests/unit/states/test_nexus.py +++ b/tests/unit/states/test_nexus.py @@ -1,54 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Jayesh Kariya <jayeshk@saltstack.com>` -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.nexus as nexus +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class nexusTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.nexus - ''' + """ + def setup_loader_modules(self): return {nexus: {}} # 'downloaded' function tests: 1 def test_downloaded(self): - ''' + """ Test to ensures that the artifact from nexus exists at given location. - ''' - name = 'jboss' - arti_url = 'http://nexus.example.com/repository' - artifact = {'nexus_url': arti_url, 'artifact_id': 'module', - 'repository': 'libs-releases', 'packaging': 'jar', - 'group_id': 'com.company.module', 'classifier': 'sources', - 'version': '1.0'} + """ + name = "jboss" + arti_url = "http://nexus.example.com/repository" + artifact = { + "nexus_url": arti_url, + "artifact_id": "module", + "repository": "libs-releases", + "packaging": "jar", + "group_id": "com.company.module", + "classifier": "sources", + "version": "1.0", + } - ret = {'name': name, - 'result': False, - 'changes': {}, - 'comment': ''} + ret = {"name": name, "result": False, "changes": {}, "comment": ""} - mck = MagicMock(return_value={'status': False, 'changes': {}, - 'comment': ''}) - with patch.dict(nexus.__salt__, {'nexus.get_release': mck}): + mck = MagicMock(return_value={"status": False, "changes": {}, "comment": ""}) + with patch.dict(nexus.__salt__, {"nexus.get_release": mck}): self.assertDictEqual(nexus.downloaded(name, artifact), ret) - with patch.object(nexus, '__fetch_from_nexus', - MagicMock(side_effect=Exception('error'))): + with patch.object( + nexus, "__fetch_from_nexus", MagicMock(side_effect=Exception("error")) + ): ret = nexus.downloaded(name, artifact) - self.assertEqual(ret['result'], False) - self.assertEqual(ret['comment'], 'error') + self.assertEqual(ret["result"], False) + self.assertEqual(ret["comment"], "error") diff --git a/tests/unit/states/test_nftables.py b/tests/unit/states/test_nftables.py index d9767f2a923..e1d0d838eda 100644 --- a/tests/unit/states/test_nftables.py +++ b/tests/unit/states/test_nftables.py @@ -1,306 +1,401 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.nftables as nftables +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class NftablesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the nftables state - ''' + """ + def setup_loader_modules(self): return {nftables: {}} def test_chain_present(self): - ''' + """ Test to verify the chain is exist. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} - mock = MagicMock(side_effect=[{'result': True, 'comment': ''}, - {'result': False, 'comment': ''}, - {'result': False, 'comment': ''}]) - with patch.dict(nftables.__salt__, {'nftables.check_chain': mock}): - ret.update({'comment': 'nftables salt chain is already' - ' exist in filter table for ipv4'}) - self.assertDictEqual(nftables.chain_present('salt'), ret) + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + mock = MagicMock( + side_effect=[ + {"result": True, "comment": ""}, + {"result": False, "comment": ""}, + {"result": False, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.check_chain": mock}): + ret.update( + { + "comment": "nftables salt chain is already" + " exist in filter table for ipv4" + } + ) + self.assertDictEqual(nftables.chain_present("salt"), ret) - mock = MagicMock(side_effect=[{'result': True, 'comment': ''}, - {'result': False, 'comment': ''}]) - with patch.dict(nftables.__salt__, {'nftables.new_chain': mock}): - ret.update({'changes': {'locale': 'salt'}, - 'comment': 'nftables salt chain in filter' - ' table create success for ipv4'}) - self.assertDictEqual(nftables.chain_present('salt'), ret) + mock = MagicMock( + side_effect=[ + {"result": True, "comment": ""}, + {"result": False, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.new_chain": mock}): + ret.update( + { + "changes": {"locale": "salt"}, + "comment": "nftables salt chain in filter" + " table create success for ipv4", + } + ) + self.assertDictEqual(nftables.chain_present("salt"), ret) - ret.update({'changes': {}, - 'comment': 'Failed to create salt chain' - ' in filter table: for ipv4', - 'result': False}) - self.assertDictEqual(nftables.chain_present('salt'), ret) + ret.update( + { + "changes": {}, + "comment": "Failed to create salt chain" + " in filter table: for ipv4", + "result": False, + } + ) + self.assertDictEqual(nftables.chain_present("salt"), ret) def test_chain_absent(self): - ''' + """ Test to verify the chain is absent. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[False, True]) - with patch.dict(nftables.__salt__, {'nftables.check_chain': mock}): - ret.update({'comment': 'nftables salt chain is already absent' - ' in filter table for ipv4'}) - self.assertDictEqual(nftables.chain_absent('salt'), ret) + with patch.dict(nftables.__salt__, {"nftables.check_chain": mock}): + ret.update( + { + "comment": "nftables salt chain is already absent" + " in filter table for ipv4" + } + ) + self.assertDictEqual(nftables.chain_absent("salt"), ret) - mock = MagicMock(return_value='') - with patch.dict(nftables.__salt__, {'nftables.flush': mock}): - ret.update({'result': False, - 'comment': 'Failed to flush salt chain' - ' in filter table: for ipv4'}) - self.assertDictEqual(nftables.chain_absent('salt'), ret) + mock = MagicMock(return_value="") + with patch.dict(nftables.__salt__, {"nftables.flush": mock}): + ret.update( + { + "result": False, + "comment": "Failed to flush salt chain" + " in filter table: for ipv4", + } + ) + self.assertDictEqual(nftables.chain_absent("salt"), ret) def test_append(self): - ''' + """ Test to append a rule to a chain - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(return_value=[]) - with patch.object(nftables, '_STATE_INTERNAL_KEYWORDS', mock): - mock = MagicMock(return_value={'result': True, - 'comment': '', - 'rule': 'a'}) - with patch.dict(nftables.__salt__, {'nftables.build_rule': mock}): - mock = MagicMock(side_effect=[{'result': True, 'comment': ''}, - {'result': False, 'comment': ''}, - {'result': False, 'comment': ''}, - {'result': False, 'comment': ''}]) - with patch.dict(nftables.__salt__, {'nftables.check': mock}): - ret.update({'comment': 'nftables rule for salt' - ' already set (a) for ipv4'}) - self.assertDictEqual(nftables.append('salt', table='', - chain=''), ret) + with patch.object(nftables, "_STATE_INTERNAL_KEYWORDS", mock): + mock = MagicMock(return_value={"result": True, "comment": "", "rule": "a"}) + with patch.dict(nftables.__salt__, {"nftables.build_rule": mock}): + mock = MagicMock( + side_effect=[ + {"result": True, "comment": ""}, + {"result": False, "comment": ""}, + {"result": False, "comment": ""}, + {"result": False, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.check": mock}): + ret.update( + { + "comment": "nftables rule for salt" + " already set (a) for ipv4" + } + ) + self.assertDictEqual( + nftables.append("salt", table="", chain=""), ret + ) - with patch.dict(nftables.__opts__, {'test': True}): - ret.update({'result': None, - 'comment': 'nftables rule for salt needs' - ' to be set (a) for ipv4'}) - self.assertDictEqual(nftables.append('salt', table='', - chain=''), ret) + with patch.dict(nftables.__opts__, {"test": True}): + ret.update( + { + "result": None, + "comment": "nftables rule for salt needs" + " to be set (a) for ipv4", + } + ) + self.assertDictEqual( + nftables.append("salt", table="", chain=""), ret + ) - with patch.dict(nftables.__opts__, {'test': False}): - mock = MagicMock(side_effect=[{'result': True, - 'comment': ''}, - {'result': False, - 'comment': ''}]) - with patch.dict(nftables.__salt__, - {'nftables.append': mock}): - ret.update({'changes': {'locale': 'salt'}, - 'comment': 'Set nftables rule for salt' - ' to: a for ipv4', - 'result': True}) - self.assertDictEqual(nftables.append('salt', - table='', - chain=''), - ret) + with patch.dict(nftables.__opts__, {"test": False}): + mock = MagicMock( + side_effect=[ + {"result": True, "comment": ""}, + {"result": False, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.append": mock}): + ret.update( + { + "changes": {"locale": "salt"}, + "comment": "Set nftables rule for salt" + " to: a for ipv4", + "result": True, + } + ) + self.assertDictEqual( + nftables.append("salt", table="", chain=""), ret + ) - ret.update({'changes': {}, - 'comment': 'Failed to set nftables' - ' rule for salt.\nAttempted rule was' - ' a for ipv4.\n', 'result': False}) - self.assertDictEqual(nftables.append('salt', - table='', - chain=''), - ret) + ret.update( + { + "changes": {}, + "comment": "Failed to set nftables" + " rule for salt.\nAttempted rule was" + " a for ipv4.\n", + "result": False, + } + ) + self.assertDictEqual( + nftables.append("salt", table="", chain=""), ret + ) def test_insert(self): - ''' + """ Test to insert a rule into a chain - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(return_value=[]) - with patch.object(nftables, '_STATE_INTERNAL_KEYWORDS', mock): - mock = MagicMock(return_value={'result': True, - 'comment': '', - 'rule': 'a'}) - with patch.dict(nftables.__salt__, {'nftables.build_rule': mock}): - mock = MagicMock(side_effect=[{'result': True, 'comment': ''}, - {'result': False, 'comment': ''}, - {'result': False, 'comment': ''}, - {'result': False, 'comment': ''}]) - with patch.dict(nftables.__salt__, {'nftables.check': mock}): - ret.update({'comment': 'nftables rule for salt already' - ' set for ipv4 (a)'}) - self.assertDictEqual(nftables.insert('salt', table='', - chain=''), ret) + with patch.object(nftables, "_STATE_INTERNAL_KEYWORDS", mock): + mock = MagicMock(return_value={"result": True, "comment": "", "rule": "a"}) + with patch.dict(nftables.__salt__, {"nftables.build_rule": mock}): + mock = MagicMock( + side_effect=[ + {"result": True, "comment": ""}, + {"result": False, "comment": ""}, + {"result": False, "comment": ""}, + {"result": False, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.check": mock}): + ret.update( + { + "comment": "nftables rule for salt already" + " set for ipv4 (a)" + } + ) + self.assertDictEqual( + nftables.insert("salt", table="", chain=""), ret + ) - with patch.dict(nftables.__opts__, {'test': True}): - ret.update({'result': None, - 'comment': 'nftables rule for salt' - ' needs to be set for ipv4 (a)'}) - self.assertDictEqual(nftables.insert('salt', table='', - chain=''), ret) + with patch.dict(nftables.__opts__, {"test": True}): + ret.update( + { + "result": None, + "comment": "nftables rule for salt" + " needs to be set for ipv4 (a)", + } + ) + self.assertDictEqual( + nftables.insert("salt", table="", chain=""), ret + ) - with patch.dict(nftables.__opts__, {'test': False}): - mock = MagicMock(side_effect=[{'result': True, - 'comment': ''}, - {'result': False, - 'comment': ''}]) - with patch.dict(nftables.__salt__, - {'nftables.insert': mock}): - ret.update({'changes': {'locale': 'salt'}, - 'comment': 'Set nftables rule for' - ' salt to: a for ipv4', - 'result': True}) - self.assertDictEqual(nftables.insert('salt', - table='', - chain='', - position=''), - ret) + with patch.dict(nftables.__opts__, {"test": False}): + mock = MagicMock( + side_effect=[ + {"result": True, "comment": ""}, + {"result": False, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.insert": mock}): + ret.update( + { + "changes": {"locale": "salt"}, + "comment": "Set nftables rule for" + " salt to: a for ipv4", + "result": True, + } + ) + self.assertDictEqual( + nftables.insert( + "salt", table="", chain="", position="" + ), + ret, + ) - ret.update({'changes': {}, - 'comment': 'Failed to set nftables' - ' rule for salt.\nAttempted rule was' - ' a', 'result': False}) - self.assertDictEqual(nftables.insert('salt', - table='', - chain='', - position=''), - ret) + ret.update( + { + "changes": {}, + "comment": "Failed to set nftables" + " rule for salt.\nAttempted rule was" + " a", + "result": False, + } + ) + self.assertDictEqual( + nftables.insert( + "salt", table="", chain="", position="" + ), + ret, + ) def test_delete(self): - ''' + """ Test to delete a rule to a chain - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": None, "comment": ""} mock = MagicMock(return_value=[]) - with patch.object(nftables, '_STATE_INTERNAL_KEYWORDS', mock): - mock = MagicMock(return_value={'result': True, - 'comment': '', - 'rule': 'a'}) - with patch.dict(nftables.__salt__, {'nftables.build_rule': mock}): - mock = MagicMock(side_effect=[{'result': False, 'comment': ''}, - {'result': True, 'comment': ''}, - {'result': True, 'comment': ''}, - {'result': True, 'comment': ''}]) - with patch.dict(nftables.__salt__, {'nftables.check': mock}): - ret.update({'comment': 'nftables rule for salt' - ' already absent for ipv4 (a)', - 'result': True}) - self.assertDictEqual(nftables.delete('salt', - table='', chain=''), - ret) + with patch.object(nftables, "_STATE_INTERNAL_KEYWORDS", mock): + mock = MagicMock(return_value={"result": True, "comment": "", "rule": "a"}) + with patch.dict(nftables.__salt__, {"nftables.build_rule": mock}): + mock = MagicMock( + side_effect=[ + {"result": False, "comment": ""}, + {"result": True, "comment": ""}, + {"result": True, "comment": ""}, + {"result": True, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.check": mock}): + ret.update( + { + "comment": "nftables rule for salt" + " already absent for ipv4 (a)", + "result": True, + } + ) + self.assertDictEqual( + nftables.delete("salt", table="", chain=""), ret + ) - with patch.dict(nftables.__opts__, {'test': True}): - ret.update({'result': None, - 'comment': 'nftables rule for salt needs' - ' to be deleted for ipv4 (a)'}) - self.assertDictEqual(nftables.delete('salt', - table='', - chain=''), ret) + with patch.dict(nftables.__opts__, {"test": True}): + ret.update( + { + "result": None, + "comment": "nftables rule for salt needs" + " to be deleted for ipv4 (a)", + } + ) + self.assertDictEqual( + nftables.delete("salt", table="", chain=""), ret + ) - with patch.dict(nftables.__opts__, {'test': False}): - mock = MagicMock(side_effect=[{'result': True, - 'comment': ''}, - {'result': False, - 'comment': ''}]) - with patch.dict(nftables.__salt__, - {'nftables.delete': mock}): - ret.update({'result': True, - 'changes': {'locale': 'salt'}, - 'comment': 'Delete nftables rule' - ' for salt a'}) - self.assertDictEqual(nftables.delete('salt', - table='', - chain='', - position=''), - ret) + with patch.dict(nftables.__opts__, {"test": False}): + mock = MagicMock( + side_effect=[ + {"result": True, "comment": ""}, + {"result": False, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.delete": mock}): + ret.update( + { + "result": True, + "changes": {"locale": "salt"}, + "comment": "Delete nftables rule" " for salt a", + } + ) + self.assertDictEqual( + nftables.delete( + "salt", table="", chain="", position="" + ), + ret, + ) - ret.update({'result': False, - 'changes': {}, - 'comment': 'Failed to delete nftables' - ' rule for salt.\nAttempted rule was a' - }) - self.assertDictEqual(nftables.delete('salt', - table='', - chain='', - position=''), - ret) + ret.update( + { + "result": False, + "changes": {}, + "comment": "Failed to delete nftables" + " rule for salt.\nAttempted rule was a", + } + ) + self.assertDictEqual( + nftables.delete( + "salt", table="", chain="", position="" + ), + ret, + ) def test_flush(self): - ''' + """ Test to flush current nftables state - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": None, "comment": ""} mock = MagicMock(return_value=[]) - with patch.object(nftables, '_STATE_INTERNAL_KEYWORDS', mock): - mock = MagicMock(side_effect=[{'result': False, 'comment': ''}, - {'result': True, 'comment': ''}, - {'result': True, 'comment': ''}, - {'result': True, 'comment': ''}]) - with patch.dict(nftables.__salt__, {'nftables.check_table': mock}): - ret.update({'comment': 'Failed to flush table in family' - ' ipv4, table does not exist.', - 'result': False}) - self.assertDictEqual(nftables.flush('salt', - table='', chain=''), - ret) + with patch.object(nftables, "_STATE_INTERNAL_KEYWORDS", mock): + mock = MagicMock( + side_effect=[ + {"result": False, "comment": ""}, + {"result": True, "comment": ""}, + {"result": True, "comment": ""}, + {"result": True, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.check_table": mock}): + ret.update( + { + "comment": "Failed to flush table in family" + " ipv4, table does not exist.", + "result": False, + } + ) + self.assertDictEqual(nftables.flush("salt", table="", chain=""), ret) - mock = MagicMock(side_effect=[{'result': False, 'comment': ''}, - {'result': True, 'comment': ''}, - {'result': True, 'comment': ''}]) - with patch.dict(nftables.__salt__, - {'nftables.check_chain': mock}): - ret.update({'comment': 'Failed to flush chain in table' - ' in family ipv4, chain does not exist.'}) - self.assertDictEqual(nftables.flush('salt', table='', - chain=''), ret) + mock = MagicMock( + side_effect=[ + {"result": False, "comment": ""}, + {"result": True, "comment": ""}, + {"result": True, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.check_chain": mock}): + ret.update( + { + "comment": "Failed to flush chain in table" + " in family ipv4, chain does not exist." + } + ) + self.assertDictEqual( + nftables.flush("salt", table="", chain=""), ret + ) - mock = MagicMock(side_effect=[{'result': True, - 'comment': ''}, - {'result': False, - 'comment': ''}]) - with patch.dict(nftables.__salt__, - {'nftables.flush': mock}): - ret.update({'changes': {'locale': 'salt'}, - 'comment': 'Flush nftables rules in table' - ' chain ipv4 family', - 'result': True}) - self.assertDictEqual(nftables.flush('salt', table='', - chain=''), ret) + mock = MagicMock( + side_effect=[ + {"result": True, "comment": ""}, + {"result": False, "comment": ""}, + ] + ) + with patch.dict(nftables.__salt__, {"nftables.flush": mock}): + ret.update( + { + "changes": {"locale": "salt"}, + "comment": "Flush nftables rules in table" + " chain ipv4 family", + "result": True, + } + ) + self.assertDictEqual( + nftables.flush("salt", table="", chain=""), ret + ) - ret.update({'changes': {}, - 'comment': 'Failed to flush' - ' nftables rules', - 'result': False}) - self.assertDictEqual(nftables.flush('salt', table='', - chain=''), ret) + ret.update( + { + "changes": {}, + "comment": "Failed to flush" " nftables rules", + "result": False, + } + ) + self.assertDictEqual( + nftables.flush("salt", table="", chain=""), ret + ) diff --git a/tests/unit/states/test_npm.py b/tests/unit/states/test_npm.py index b51f8e070f5..b039b4b2609 100644 --- a/tests/unit/states/test_npm.py +++ b/tests/unit/states/test_npm.py @@ -1,217 +1,205 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -from salt.exceptions import CommandExecutionError +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.npm as npm +from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class NpmTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.npm - ''' + """ + def setup_loader_modules(self): - return {npm: {'__opts__': {'test': False}}} + return {npm: {"__opts__": {"test": False}}} # 'installed' function tests: 1 def test_installed(self): - ''' + """ Test to verify that the given package is installed and is at the correct version. - ''' - name = 'coffee-script' + """ + name = "coffee-script" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock_err = MagicMock(side_effect=CommandExecutionError) - mock_dict = MagicMock(return_value={name: {'version': '1.2'}}) - with patch.dict(npm.__salt__, {'npm.list': mock_err}): - comt = ("Error looking up 'coffee-script': ") - ret.update({'comment': comt}) + mock_dict = MagicMock(return_value={name: {"version": "1.2"}}) + with patch.dict(npm.__salt__, {"npm.list": mock_err}): + comt = "Error looking up 'coffee-script': " + ret.update({"comment": comt}) self.assertDictEqual(npm.installed(name), ret) - with patch.dict(npm.__salt__, {'npm.list': mock_dict, - 'npm.install': mock_err}): - with patch.dict(npm.__opts__, {'test': True}): - comt = ("Package(s) 'coffee-script' " - "satisfied by coffee-script@1.2") - ret.update({'comment': comt, 'result': True}) + with patch.dict(npm.__salt__, {"npm.list": mock_dict, "npm.install": mock_err}): + with patch.dict(npm.__opts__, {"test": True}): + comt = "Package(s) 'coffee-script' " "satisfied by coffee-script@1.2" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(npm.installed(name), ret) - with patch.dict(npm.__opts__, {'test': False}): - comt = ("Package(s) 'coffee-script' " - "satisfied by coffee-script@1.2") - ret.update({'comment': comt, 'result': True}) + with patch.dict(npm.__opts__, {"test": False}): + comt = "Package(s) 'coffee-script' " "satisfied by coffee-script@1.2" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(npm.installed(name), ret) - comt = ("Error installing 'n, p, m': ") - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(npm.installed(name, 'npm'), ret) + comt = "Error installing 'n, p, m': " + ret.update({"comment": comt, "result": False}) + self.assertDictEqual(npm.installed(name, "npm"), ret) - with patch.dict(npm.__salt__, {'npm.install': mock_dict}): - comt = ("Package(s) 'n, p, m' successfully installed") - ret.update({'comment': comt, 'result': True, - 'changes': {'new': ['n', 'p', 'm'], 'old': []}}) - self.assertDictEqual(npm.installed(name, 'npm'), ret) + with patch.dict(npm.__salt__, {"npm.install": mock_dict}): + comt = "Package(s) 'n, p, m' successfully installed" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"new": ["n", "p", "m"], "old": []}, + } + ) + self.assertDictEqual(npm.installed(name, "npm"), ret) # 'removed' function tests: 1 def test_removed(self): - ''' + """ Test to verify that the given package is not installed. - ''' - name = 'coffee-script' + """ + name = "coffee-script" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock_err = MagicMock(side_effect=[CommandExecutionError, {}, - {name: ''}, {name: ''}]) + mock_err = MagicMock( + side_effect=[CommandExecutionError, {}, {name: ""}, {name: ""}] + ) mock_t = MagicMock(return_value=True) - with patch.dict(npm.__salt__, {'npm.list': mock_err, - 'npm.uninstall': mock_t}): - comt = ("Error uninstalling 'coffee-script': ") - ret.update({'comment': comt}) + with patch.dict(npm.__salt__, {"npm.list": mock_err, "npm.uninstall": mock_t}): + comt = "Error uninstalling 'coffee-script': " + ret.update({"comment": comt}) self.assertDictEqual(npm.removed(name), ret) - comt = ("Package 'coffee-script' is not installed") - ret.update({'comment': comt, 'result': True}) + comt = "Package 'coffee-script' is not installed" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(npm.removed(name), ret) - with patch.dict(npm.__opts__, {'test': True}): - comt = ("Package 'coffee-script' is set to be removed") - ret.update({'comment': comt, 'result': None}) + with patch.dict(npm.__opts__, {"test": True}): + comt = "Package 'coffee-script' is set to be removed" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(npm.removed(name), ret) - with patch.dict(npm.__opts__, {'test': False}): - comt = ("Package 'coffee-script' was successfully removed") - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Removed'}}) + with patch.dict(npm.__opts__, {"test": False}): + comt = "Package 'coffee-script' was successfully removed" + ret.update( + {"comment": comt, "result": True, "changes": {name: "Removed"}} + ) self.assertDictEqual(npm.removed(name), ret) # 'bootstrap' function tests: 1 def test_bootstrap(self): - ''' + """ Test to bootstraps a node.js application. - ''' - name = 'coffee-script' + """ + name = "coffee-script" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock_err = MagicMock(side_effect=[CommandExecutionError, False, True]) - with patch.dict(npm.__salt__, {'npm.install': mock_err}): - comt = ("Error Bootstrapping 'coffee-script': ") - ret.update({'comment': comt}) + with patch.dict(npm.__salt__, {"npm.install": mock_err}): + comt = "Error Bootstrapping 'coffee-script': " + ret.update({"comment": comt}) self.assertDictEqual(npm.bootstrap(name), ret) - comt = ('Directory is already bootstrapped') - ret.update({'comment': comt, 'result': True}) + comt = "Directory is already bootstrapped" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(npm.bootstrap(name), ret) - comt = ('Directory was successfully bootstrapped') - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Bootstrapped'}}) + comt = "Directory was successfully bootstrapped" + ret.update( + {"comment": comt, "result": True, "changes": {name: "Bootstrapped"}} + ) self.assertDictEqual(npm.bootstrap(name), ret) # 'bootstrap' function tests: 1 def test_cache_cleaned(self): - ''' + """ Test to verify that the npm cache is cleaned. - ''' - name = 'coffee-script' + """ + name = "coffee-script" - pkg_ret = { - 'name': name, - 'result': False, - 'comment': '', - 'changes': {} - } - ret = { - 'name': None, - 'result': False, - 'comment': '', - 'changes': {} - } + pkg_ret = {"name": name, "result": False, "comment": "", "changes": {}} + ret = {"name": None, "result": False, "comment": "", "changes": {}} - mock_list = MagicMock(return_value=['~/.npm', '~/.npm/{0}/'.format(name)]) + mock_list = MagicMock(return_value=["~/.npm", "~/.npm/{0}/".format(name)]) mock_cache_clean_success = MagicMock(return_value=True) mock_cache_clean_failure = MagicMock(return_value=False) mock_err = MagicMock(side_effect=CommandExecutionError) - with patch.dict(npm.__salt__, {'npm.cache_list': mock_err}): - comt = ('Error looking up cached packages: ') - ret.update({'comment': comt}) + with patch.dict(npm.__salt__, {"npm.cache_list": mock_err}): + comt = "Error looking up cached packages: " + ret.update({"comment": comt}) self.assertDictEqual(npm.cache_cleaned(), ret) - with patch.dict(npm.__salt__, {'npm.cache_list': mock_err}): - comt = ("Error looking up cached {0}: ".format(name)) - pkg_ret.update({'comment': comt}) + with patch.dict(npm.__salt__, {"npm.cache_list": mock_err}): + comt = "Error looking up cached {0}: ".format(name) + pkg_ret.update({"comment": comt}) self.assertDictEqual(npm.cache_cleaned(name), pkg_ret) - mock_data = {'npm.cache_list': mock_list, 'npm.cache_clean': MagicMock()} + mock_data = {"npm.cache_list": mock_list, "npm.cache_clean": MagicMock()} with patch.dict(npm.__salt__, mock_data): - non_cached_pkg = 'salt' - comt = ('Package {0} is not in the cache'.format(non_cached_pkg)) - pkg_ret.update({'name': non_cached_pkg, 'result': True, 'comment': comt}) + non_cached_pkg = "salt" + comt = "Package {0} is not in the cache".format(non_cached_pkg) + pkg_ret.update({"name": non_cached_pkg, "result": True, "comment": comt}) self.assertDictEqual(npm.cache_cleaned(non_cached_pkg), pkg_ret) - pkg_ret.update({'name': name}) + pkg_ret.update({"name": name}) - with patch.dict(npm.__opts__, {'test': True}): - comt = ('Cached packages set to be removed') - ret.update({'result': None, 'comment': comt}) + with patch.dict(npm.__opts__, {"test": True}): + comt = "Cached packages set to be removed" + ret.update({"result": None, "comment": comt}) self.assertDictEqual(npm.cache_cleaned(), ret) - with patch.dict(npm.__opts__, {'test': True}): - comt = ('Cached {0} set to be removed'.format(name)) - pkg_ret.update({'result': None, 'comment': comt}) + with patch.dict(npm.__opts__, {"test": True}): + comt = "Cached {0} set to be removed".format(name) + pkg_ret.update({"result": None, "comment": comt}) self.assertDictEqual(npm.cache_cleaned(name), pkg_ret) - with patch.dict(npm.__opts__, {'test': False}): - comt = ('Cached packages successfully removed') - ret.update({'result': True, 'comment': comt, - 'changes': {'cache': 'Removed'}}) + with patch.dict(npm.__opts__, {"test": False}): + comt = "Cached packages successfully removed" + ret.update( + {"result": True, "comment": comt, "changes": {"cache": "Removed"}} + ) self.assertDictEqual(npm.cache_cleaned(), ret) - with patch.dict(npm.__opts__, {'test': False}): - comt = ('Cached {0} successfully removed'.format(name)) - pkg_ret.update({'result': True, 'comment': comt, - 'changes': {name: 'Removed'}}) + with patch.dict(npm.__opts__, {"test": False}): + comt = "Cached {0} successfully removed".format(name) + pkg_ret.update( + {"result": True, "comment": comt, "changes": {name: "Removed"}} + ) self.assertDictEqual(npm.cache_cleaned(name), pkg_ret) - mock_data = {'npm.cache_list': mock_list, 'npm.cache_clean': MagicMock(return_value=False)} + mock_data = { + "npm.cache_list": mock_list, + "npm.cache_clean": MagicMock(return_value=False), + } with patch.dict(npm.__salt__, mock_data): - with patch.dict(npm.__opts__, {'test': False}): - comt = ('Error cleaning cached packages') - ret.update({'result': False, 'comment': comt}) - ret['changes'] = {} + with patch.dict(npm.__opts__, {"test": False}): + comt = "Error cleaning cached packages" + ret.update({"result": False, "comment": comt}) + ret["changes"] = {} self.assertDictEqual(npm.cache_cleaned(), ret) - with patch.dict(npm.__opts__, {'test': False}): - comt = ('Error cleaning cached {0}'.format(name)) - pkg_ret.update({'result': False, 'comment': comt}) - pkg_ret['changes'] = {} + with patch.dict(npm.__opts__, {"test": False}): + comt = "Error cleaning cached {0}".format(name) + pkg_ret.update({"result": False, "comment": comt}) + pkg_ret["changes"] = {} self.assertDictEqual(npm.cache_cleaned(name), pkg_ret) diff --git a/tests/unit/states/test_ntp.py b/tests/unit/states/test_ntp.py index 01133d04184..54e4355212b 100644 --- a/tests/unit/states/test_ntp.py +++ b/tests/unit/states/test_ntp.py @@ -1,54 +1,51 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.ntp as ntp +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class NtpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ntp - ''' + """ + def setup_loader_modules(self): return {ntp: {}} # 'managed' function tests: 1 def test_managed(self): - ''' + """ Test to manage NTP servers. - ''' - name = 'coffee-script' + """ + name = "coffee-script" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} mock_lst = MagicMock(return_value=[]) - with patch.dict(ntp.__salt__, {'ntp.get_servers': mock_lst, - 'ntp.set_servers': mock_lst}): - comt = ('NTP servers already configured as specified') - ret.update({'comment': comt, 'result': True}) + with patch.dict( + ntp.__salt__, {"ntp.get_servers": mock_lst, "ntp.set_servers": mock_lst} + ): + comt = "NTP servers already configured as specified" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ntp.managed(name, []), ret) - with patch.dict(ntp.__opts__, {'test': True}): - comt = ('NTP servers will be updated to: coffee-script') - ret.update({'comment': comt, 'result': None}) + with patch.dict(ntp.__opts__, {"test": True}): + comt = "NTP servers will be updated to: coffee-script" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(ntp.managed(name, [name]), ret) - with patch.dict(ntp.__opts__, {'test': False}): - comt = ('Failed to update NTP servers') - ret.update({'comment': comt, 'result': False}) + with patch.dict(ntp.__opts__, {"test": False}): + comt = "Failed to update NTP servers" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(ntp.managed(name, [name]), ret) diff --git a/tests/unit/states/test_openstack_config.py b/tests/unit/states/test_openstack_config.py index 7bd203be17d..484c02828ce 100644 --- a/tests/unit/states/test_openstack_config.py +++ b/tests/unit/states/test_openstack_config.py @@ -1,95 +1,100 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -from salt.exceptions import CommandExecutionError +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.openstack_config as openstack_config +from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class OpenstackConfigTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.openstack_config - ''' + """ + def setup_loader_modules(self): - return {openstack_config: {'__opts__': {'test': False}}} + return {openstack_config: {"__opts__": {"test": False}}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure a value is set in an OpenStack configuration file. - ''' - name = 'salt' - filename = '/tmp/salt' - section = 'A' - value = 'SALT' + """ + name = "salt" + filename = "/tmp/salt" + section = "A" + value = "SALT" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock_lst = MagicMock(side_effect=[value, CommandExecutionError, 'A']) + mock_lst = MagicMock(side_effect=[value, CommandExecutionError, "A"]) mock_t = MagicMock(return_value=True) - with patch.dict(openstack_config.__salt__, - {'openstack_config.get': mock_lst, - 'openstack_config.set': mock_t}): - comt = ('The value is already set to the correct value') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(openstack_config.present(name, filename, - section, value), ret) + with patch.dict( + openstack_config.__salt__, + {"openstack_config.get": mock_lst, "openstack_config.set": mock_t}, + ): + comt = "The value is already set to the correct value" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + openstack_config.present(name, filename, section, value), ret + ) - self.assertRaises(CommandExecutionError, openstack_config.present, - name, filename, section, value) + self.assertRaises( + CommandExecutionError, + openstack_config.present, + name, + filename, + section, + value, + ) - comt = ('The value has been updated') - ret.update({'comment': comt, 'changes': {'Value': 'Updated'}}) - self.assertDictEqual(openstack_config.present(name, filename, - section, value), ret) + comt = "The value has been updated" + ret.update({"comment": comt, "changes": {"Value": "Updated"}}) + self.assertDictEqual( + openstack_config.present(name, filename, section, value), ret + ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure a value is not set in an OpenStack configuration file. - ''' - name = 'salt' - filename = '/tmp/salt' - section = 'A' + """ + name = "salt" + filename = "/tmp/salt" + section = "A" - ret = {'name': name, - 'result': False, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "", "changes": {}} - mock_lst = MagicMock(side_effect=[CommandExecutionError - ('parameter not found:'), - CommandExecutionError, 'A']) + mock_lst = MagicMock( + side_effect=[ + CommandExecutionError("parameter not found:"), + CommandExecutionError, + "A", + ] + ) mock_t = MagicMock(return_value=True) - with patch.dict(openstack_config.__salt__, - {'openstack_config.get': mock_lst, - 'openstack_config.delete': mock_t}): - comt = ('The value is already absent') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(openstack_config.absent(name, filename, - section), ret) + with patch.dict( + openstack_config.__salt__, + {"openstack_config.get": mock_lst, "openstack_config.delete": mock_t}, + ): + comt = "The value is already absent" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(openstack_config.absent(name, filename, section), ret) - self.assertRaises(CommandExecutionError, openstack_config.absent, - name, filename, section) + self.assertRaises( + CommandExecutionError, openstack_config.absent, name, filename, section + ) - comt = ('The value has been deleted') - ret.update({'comment': comt, 'changes': {'Value': 'Deleted'}}) - self.assertDictEqual(openstack_config.absent(name, filename, - section), ret) + comt = "The value has been deleted" + ret.update({"comment": comt, "changes": {"Value": "Deleted"}}) + self.assertDictEqual(openstack_config.absent(name, filename, section), ret) diff --git a/tests/unit/states/test_openvswitch_port.py b/tests/unit/states/test_openvswitch_port.py index f481dbf1035..81155291a78 100644 --- a/tests/unit/states/test_openvswitch_port.py +++ b/tests/unit/states/test_openvswitch_port.py @@ -1,83 +1,103 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.openvswitch_port as openvswitch_port +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class OpenvswitchPortTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.openvswitch_port - ''' + """ + def setup_loader_modules(self): - return {openvswitch_port: {'__opts__': {'test': False}}} + return {openvswitch_port: {"__opts__": {"test": False}}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to verify that the named port exists on bridge, eventually creates it. - ''' - name = 'salt' - bridge = 'br-salt' + """ + name = "salt" + bridge = "br-salt" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} mock = MagicMock(return_value=True) - mock_l = MagicMock(return_value=['salt']) + mock_l = MagicMock(return_value=["salt"]) mock_n = MagicMock(return_value=[]) - with patch.dict(openvswitch_port.__salt__, {'openvswitch.bridge_exists': mock, - 'openvswitch.interface_get_type': MagicMock(return_value='""'), - 'openvswitch.port_list': mock_l - }): - comt = 'Port salt already exists.' - ret.update({'comment': comt, 'result': True}) + with patch.dict( + openvswitch_port.__salt__, + { + "openvswitch.bridge_exists": mock, + "openvswitch.interface_get_type": MagicMock(return_value='""'), + "openvswitch.port_list": mock_l, + }, + ): + comt = "Port salt already exists." + ret.update({"comment": comt, "result": True}) self.assertDictEqual(openvswitch_port.present(name, bridge), ret) - with patch.dict(openvswitch_port.__salt__, {'openvswitch.bridge_exists': mock, - 'openvswitch.interface_get_type': MagicMock(return_value='""'), - 'openvswitch.port_list': mock_n, - 'openvswitch.port_add': mock - }): - comt = 'Port salt created on bridge br-salt.' - ret.update({'comment': comt, 'result': True, 'changes': - {'salt': - {'new': 'Created port salt on bridge br-salt.', - 'old': 'No port named salt present.', - }, - } - }) + with patch.dict( + openvswitch_port.__salt__, + { + "openvswitch.bridge_exists": mock, + "openvswitch.interface_get_type": MagicMock(return_value='""'), + "openvswitch.port_list": mock_n, + "openvswitch.port_add": mock, + }, + ): + comt = "Port salt created on bridge br-salt." + ret.update( + { + "comment": comt, + "result": True, + "changes": { + "salt": { + "new": "Created port salt on bridge br-salt.", + "old": "No port named salt present.", + }, + }, + } + ) self.assertDictEqual(openvswitch_port.present(name, bridge), ret) - with patch.dict(openvswitch_port.__salt__, {'openvswitch.bridge_exists': mock, - 'openvswitch.port_list': mock_n, - 'openvswitch.port_add': mock, - 'openvswitch.interface_get_options': mock_n, - 'openvswitch.interface_get_type': MagicMock(return_value=''), - 'openvswitch.port_create_gre': mock, - 'dig.check_ip': mock, - }): - comt = 'Port salt created on bridge br-salt.' + with patch.dict( + openvswitch_port.__salt__, + { + "openvswitch.bridge_exists": mock, + "openvswitch.port_list": mock_n, + "openvswitch.port_add": mock, + "openvswitch.interface_get_options": mock_n, + "openvswitch.interface_get_type": MagicMock(return_value=""), + "openvswitch.port_create_gre": mock, + "dig.check_ip": mock, + }, + ): + comt = "Port salt created on bridge br-salt." self.maxDiff = None - ret.update({'result': True, - 'comment': 'Created GRE tunnel interface salt with remote ip 10.0.0.1 and key 1 on bridge br-salt.', - 'changes': - {'salt': - { - 'new': 'Created GRE tunnel interface salt with remote ip 10.0.0.1 and key 1 on bridge br-salt.', - 'old': 'No GRE tunnel interface salt with remote ip 10.0.0.1 and key 1 on bridge br-salt present.', - }, - } - }) - self.assertDictEqual(openvswitch_port.present(name, bridge, tunnel_type="gre", id=1, remote="10.0.0.1"), ret) + ret.update( + { + "result": True, + "comment": "Created GRE tunnel interface salt with remote ip 10.0.0.1 and key 1 on bridge br-salt.", + "changes": { + "salt": { + "new": "Created GRE tunnel interface salt with remote ip 10.0.0.1 and key 1 on bridge br-salt.", + "old": "No GRE tunnel interface salt with remote ip 10.0.0.1 and key 1 on bridge br-salt present.", + }, + }, + } + ) + self.assertDictEqual( + openvswitch_port.present( + name, bridge, tunnel_type="gre", id=1, remote="10.0.0.1" + ), + ret, + ) diff --git a/tests/unit/states/test_pagerduty.py b/tests/unit/states/test_pagerduty.py index 54e98ca7633..53fa5a2a443 100644 --- a/tests/unit/states/test_pagerduty.py +++ b/tests/unit/states/test_pagerduty.py @@ -1,57 +1,52 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.pagerduty as pagerduty +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PagerdutyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.pagerduty - ''' + """ + def setup_loader_modules(self): return {pagerduty: {}} # 'create_event' function tests: 1 def test_create_event(self): - ''' + """ Test to create an event on the PagerDuty service. - ''' - name = 'This is a server warning message' - details = 'This is a much more detailed message' - service_key = '9abcd123456789efabcde362783cdbaf' - profile = 'my-pagerduty-account' + """ + name = "This is a server warning message" + details = "This is a much more detailed message" + service_key = "9abcd123456789efabcde362783cdbaf" + profile = "my-pagerduty-account" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - with patch.dict(pagerduty.__opts__, {'test': True}): - comt = ('Need to create event: {0}'.format(name)) - ret.update({'comment': comt}) - self.assertDictEqual(pagerduty.create_event(name, details, - service_key, profile), - ret) + with patch.dict(pagerduty.__opts__, {"test": True}): + comt = "Need to create event: {0}".format(name) + ret.update({"comment": comt}) + self.assertDictEqual( + pagerduty.create_event(name, details, service_key, profile), ret + ) - with patch.dict(pagerduty.__opts__, {'test': False}): + with patch.dict(pagerduty.__opts__, {"test": False}): mock_t = MagicMock(return_value=True) - with patch.dict(pagerduty.__salt__, - {'pagerduty.create_event': mock_t}): - comt = ('Created event: {0}'.format(name)) - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(pagerduty.create_event(name, details, - service_key, - profile), ret) + with patch.dict(pagerduty.__salt__, {"pagerduty.create_event": mock_t}): + comt = "Created event: {0}".format(name) + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + pagerduty.create_event(name, details, service_key, profile), ret + ) diff --git a/tests/unit/states/test_pdbedit.py b/tests/unit/states/test_pdbedit.py index 664aa6cec61..1f32d8d02be 100644 --- a/tests/unit/states/test_pdbedit.py +++ b/tests/unit/states/test_pdbedit.py @@ -3,35 +3,35 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.modules.pdbedit as pdbedit_mod + # Import Salt Libs import salt.states.pdbedit as pdbedit -import salt.modules.pdbedit as pdbedit_mod # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock class PdbeditTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.states.pdbedit module - ''' + """ def setup_loader_modules(self): - return {pdbedit: {}, - pdbedit_mod: {}} + return {pdbedit: {}, pdbedit_mod: {}} def test_generate_absent(self): - ''' + """ Test salt.states.pdbedit.absent when user is already absent - ''' - name = 'testname' - cmd_ret = {'pid': 13172, 'retcode': 0, 'stdout': '', 'stderr': ''} - with patch.dict(pdbedit.__salt__, {'pdbedit.list': - pdbedit_mod.list_users}): - with patch.dict(pdbedit_mod.__salt__, {'cmd.run_all': - MagicMock(return_value=cmd_ret)}): + """ + name = "testname" + cmd_ret = {"pid": 13172, "retcode": 0, "stdout": "", "stderr": ""} + with patch.dict(pdbedit.__salt__, {"pdbedit.list": pdbedit_mod.list_users}): + with patch.dict( + pdbedit_mod.__salt__, {"cmd.run_all": MagicMock(return_value=cmd_ret)} + ): ret = pdbedit.absent(name) - assert ret['comment'] == 'account {0} is absent'.format(name) + assert ret["comment"] == "account {0} is absent".format(name) diff --git a/tests/unit/states/test_pecl.py b/tests/unit/states/test_pecl.py index e6d37b0c2f1..3a92d1e830d 100644 --- a/tests/unit/states/test_pecl.py +++ b/tests/unit/states/test_pecl.py @@ -1,90 +1,88 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.pecl as pecl +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PeclTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.pecl - ''' + """ + def setup_loader_modules(self): return {pecl: {}} # 'installed' function tests: 1 def test_installed(self): - ''' + """ Test to make sure that a pecl extension is installed. - ''' - name = 'mongo' - ver = '1.0.1' + """ + name = "mongo" + ver = "1.0.1" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - mock_lst = MagicMock(return_value={name: 'stable'}) + mock_lst = MagicMock(return_value={name: "stable"}) mock_t = MagicMock(return_value=True) - with patch.dict(pecl.__salt__, {'pecl.list': mock_lst, - 'pecl.install': mock_t}): - comt = ('Pecl extension {0} is already installed.'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict(pecl.__salt__, {"pecl.list": mock_lst, "pecl.install": mock_t}): + comt = "Pecl extension {0} is already installed.".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(pecl.installed(name), ret) - with patch.dict(pecl.__opts__, {'test': True}): - comt = ('Pecl extension mongo-1.0.1 would have been installed') - ret.update({'comment': comt, 'result': None}) + with patch.dict(pecl.__opts__, {"test": True}): + comt = "Pecl extension mongo-1.0.1 would have been installed" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(pecl.installed(name, version=ver), ret) - with patch.dict(pecl.__opts__, {'test': False}): - comt = ('Pecl extension mongo-1.0.1 was successfully installed') - ret.update({'comment': comt, 'result': True, - 'changes': {'mongo-1.0.1': 'Installed'}}) + with patch.dict(pecl.__opts__, {"test": False}): + comt = "Pecl extension mongo-1.0.1 was successfully installed" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"mongo-1.0.1": "Installed"}, + } + ) self.assertDictEqual(pecl.installed(name, version=ver), ret) # 'removed' function tests: 1 def test_removed(self): - ''' + """ Test to make sure that a pecl extension is not installed. - ''' - name = 'mongo' + """ + name = "mongo" - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} - mock_lst = MagicMock(side_effect=[{}, {name: 'stable'}, - {name: 'stable'}]) + mock_lst = MagicMock(side_effect=[{}, {name: "stable"}, {name: "stable"}]) mock_t = MagicMock(return_value=True) - with patch.dict(pecl.__salt__, {'pecl.list': mock_lst, - 'pecl.uninstall': mock_t}): - comt = ('Pecl extension {0} is not installed.'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict( + pecl.__salt__, {"pecl.list": mock_lst, "pecl.uninstall": mock_t} + ): + comt = "Pecl extension {0} is not installed.".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(pecl.removed(name), ret) - with patch.dict(pecl.__opts__, {'test': True}): - comt = ('Pecl extension mongo would have been removed') - ret.update({'comment': comt, 'result': None}) + with patch.dict(pecl.__opts__, {"test": True}): + comt = "Pecl extension mongo would have been removed" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(pecl.removed(name), ret) - with patch.dict(pecl.__opts__, {'test': False}): - comt = ('Pecl extension mongo was successfully removed.') - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Removed'}}) + with patch.dict(pecl.__opts__, {"test": False}): + comt = "Pecl extension mongo was successfully removed." + ret.update( + {"comment": comt, "result": True, "changes": {name: "Removed"}} + ) self.assertDictEqual(pecl.removed(name), ret) diff --git a/tests/unit/states/test_pip_state.py b/tests/unit/states/test_pip_state.py index f86e6da1685..b12eac51c32 100644 --- a/tests/unit/states/test_pip_state.py +++ b/tests/unit/states/test_pip_state.py @@ -1,33 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.unit.states.pip_test ~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import logging -import sys -import subprocess -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin, SaltReturnAssertsMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch -from tests.support.helpers import dedent, VirtualEnv +import logging +import os +import subprocess +import sys + +import salt.states.pip_state as pip_state +import salt.utils.path # Import salt libs import salt.version -import salt.utils.path -import salt.states.pip_state as pip_state from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.helpers import VirtualEnv, dedent + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin, SaltReturnAssertsMixin +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf # Import 3rd-party libs try: import pip + HAS_PIP = True except ImportError: HAS_PIP = False @@ -36,310 +41,407 @@ except ImportError: log = logging.getLogger(__name__) -@skipIf(not HAS_PIP, - 'The \'pip\' library is not importable(installed system-wide)') +@skipIf(not HAS_PIP, "The 'pip' library is not importable(installed system-wide)") class PipStateTest(TestCase, SaltReturnAssertsMixin, LoaderModuleMockMixin): - def setup_loader_modules(self): return { pip_state: { - '__env__': 'base', - '__opts__': {'test': False}, - '__salt__': {'cmd.which_bin': lambda _: 'pip'} + "__env__": "base", + "__opts__": {"test": False}, + "__salt__": {"cmd.which_bin": lambda _: "pip"}, } } def test_install_requirements_parsing(self): log.debug("Real pip version is %s", pip.__version__) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'pep8': '1.3.3'}) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"pep8": "1.3.3"}) pip_version = pip.__version__ mock_pip_version = MagicMock(return_value=pip_version) - with patch.dict(pip_state.__salt__, {'pip.version': mock_pip_version}): - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list}): - with patch.dict(pip_state.__opts__, {'test': True}): + with patch.dict(pip_state.__salt__, {"pip.version": mock_pip_version}): + with patch.dict( + pip_state.__salt__, {"cmd.run_all": mock, "pip.list": pip_list} + ): + with patch.dict(pip_state.__opts__, {"test": True}): log.debug( - 'pip_state._from_line globals: %s', - [name for name in pip_state._from_line.__globals__] + "pip_state._from_line globals: %s", + [name for name in pip_state._from_line.__globals__], ) - ret = pip_state.installed('pep8=1.3.2') - self.assertSaltFalseReturn({'test': ret}) + ret = pip_state.installed("pep8=1.3.2") + self.assertSaltFalseReturn({"test": ret}) self.assertInSaltComment( - 'Invalid version specification in package pep8=1.3.2. ' - '\'=\' is not supported, use \'==\' instead.', - {'test': ret}) - - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'pep8': '1.3.3'}) - pip_install = MagicMock(return_value={'retcode': 0}) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list, - 'pip.install': pip_install}): - with patch.dict(pip_state.__opts__, {'test': True}): - ret = pip_state.installed('pep8>=1.3.2') - self.assertSaltTrueReturn({'test': ret}) - self.assertInSaltComment( - 'Python package pep8>=1.3.2 was already installed', - {'test': ret} + "Invalid version specification in package pep8=1.3.2. " + "'=' is not supported, use '==' instead.", + {"test": ret}, ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'pep8': '1.3.3'}) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list}): - with patch.dict(pip_state.__opts__, {'test': True}): - ret = pip_state.installed('pep8<1.3.2') - self.assertSaltNoneReturn({'test': ret}) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"pep8": "1.3.3"}) + pip_install = MagicMock(return_value={"retcode": 0}) + with patch.dict( + pip_state.__salt__, + {"cmd.run_all": mock, "pip.list": pip_list, "pip.install": pip_install}, + ): + with patch.dict(pip_state.__opts__, {"test": True}): + ret = pip_state.installed("pep8>=1.3.2") + self.assertSaltTrueReturn({"test": ret}) self.assertInSaltComment( - 'Python package pep8<1.3.2 is set to be installed', - {'test': ret} + "Python package pep8>=1.3.2 was already installed", + {"test": ret}, ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'pep8': '1.3.2'}) - pip_install = MagicMock(return_value={'retcode': 0}) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list, - 'pip.install': pip_install}): - with patch.dict(pip_state.__opts__, {'test': True}): - ret = pip_state.installed('pep8>1.3.1,<1.3.3') - self.assertSaltTrueReturn({'test': ret}) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"pep8": "1.3.3"}) + with patch.dict( + pip_state.__salt__, {"cmd.run_all": mock, "pip.list": pip_list} + ): + with patch.dict(pip_state.__opts__, {"test": True}): + ret = pip_state.installed("pep8<1.3.2") + self.assertSaltNoneReturn({"test": ret}) self.assertInSaltComment( - 'Python package pep8>1.3.1,<1.3.3 was already installed', - {'test': ret} + "Python package pep8<1.3.2 is set to be installed", + {"test": ret}, ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'pep8': '1.3.1'}) - pip_install = MagicMock(return_value={'retcode': 0}) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list, - 'pip.install': pip_install}): - with patch.dict(pip_state.__opts__, {'test': True}): - ret = pip_state.installed('pep8>1.3.1,<1.3.3') - self.assertSaltNoneReturn({'test': ret}) + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"pep8": "1.3.2"}) + pip_install = MagicMock(return_value={"retcode": 0}) + with patch.dict( + pip_state.__salt__, + {"cmd.run_all": mock, "pip.list": pip_list, "pip.install": pip_install}, + ): + with patch.dict(pip_state.__opts__, {"test": True}): + ret = pip_state.installed("pep8>1.3.1,<1.3.3") + self.assertSaltTrueReturn({"test": ret}) self.assertInSaltComment( - 'Python package pep8>1.3.1,<1.3.3 is set to be installed', - {'test': ret} + "Python package pep8>1.3.1,<1.3.3 was already installed", + {"test": ret}, ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'pep8': '1.3.1'}) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list}): - with patch.dict(pip_state.__opts__, {'test': True}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"pep8": "1.3.1"}) + pip_install = MagicMock(return_value={"retcode": 0}) + with patch.dict( + pip_state.__salt__, + {"cmd.run_all": mock, "pip.list": pip_list, "pip.install": pip_install}, + ): + with patch.dict(pip_state.__opts__, {"test": True}): + ret = pip_state.installed("pep8>1.3.1,<1.3.3") + self.assertSaltNoneReturn({"test": ret}) + self.assertInSaltComment( + "Python package pep8>1.3.1,<1.3.3 is set to be installed", + {"test": ret}, + ) + + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"pep8": "1.3.1"}) + with patch.dict( + pip_state.__salt__, {"cmd.run_all": mock, "pip.list": pip_list} + ): + with patch.dict(pip_state.__opts__, {"test": True}): ret = pip_state.installed( - 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting>=0.5.1' + "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting>=0.5.1" ) - self.assertSaltNoneReturn({'test': ret}) + self.assertSaltNoneReturn({"test": ret}) self.assertInSaltComment( - 'Python package git+https://github.com/saltstack/' - 'salt-testing.git#egg=SaltTesting>=0.5.1 is set to be ' - 'installed', - {'test': ret} + "Python package git+https://github.com/saltstack/" + "salt-testing.git#egg=SaltTesting>=0.5.1 is set to be " + "installed", + {"test": ret}, ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'pep8': '1.3.1'}) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list}): - with patch.dict(pip_state.__opts__, {'test': True}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"pep8": "1.3.1"}) + with patch.dict( + pip_state.__salt__, {"cmd.run_all": mock, "pip.list": pip_list} + ): + with patch.dict(pip_state.__opts__, {"test": True}): ret = pip_state.installed( - 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting' + "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting" ) - self.assertSaltNoneReturn({'test': ret}) + self.assertSaltNoneReturn({"test": ret}) self.assertInSaltComment( - 'Python package git+https://github.com/saltstack/' - 'salt-testing.git#egg=SaltTesting is set to be ' - 'installed', - {'test': ret} + "Python package git+https://github.com/saltstack/" + "salt-testing.git#egg=SaltTesting is set to be " + "installed", + {"test": ret}, ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'pep8': '1.3.1'}) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list}): - with patch.dict(pip_state.__opts__, {'test': True}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"pep8": "1.3.1"}) + with patch.dict( + pip_state.__salt__, {"cmd.run_all": mock, "pip.list": pip_list} + ): + with patch.dict(pip_state.__opts__, {"test": True}): ret = pip_state.installed( - 'https://pypi.python.org/packages/source/S/SaltTesting/' - 'SaltTesting-0.5.0.tar.gz' - '#md5=e6760af92b7165f8be53b5763e40bc24' + "https://pypi.python.org/packages/source/S/SaltTesting/" + "SaltTesting-0.5.0.tar.gz" + "#md5=e6760af92b7165f8be53b5763e40bc24" ) - self.assertSaltNoneReturn({'test': ret}) + self.assertSaltNoneReturn({"test": ret}) self.assertInSaltComment( - 'Python package https://pypi.python.org/packages/source/' - 'S/SaltTesting/SaltTesting-0.5.0.tar.gz' - '#md5=e6760af92b7165f8be53b5763e40bc24 is set to be ' - 'installed', - {'test': ret} + "Python package https://pypi.python.org/packages/source/" + "S/SaltTesting/SaltTesting-0.5.0.tar.gz" + "#md5=e6760af92b7165f8be53b5763e40bc24 is set to be " + "installed", + {"test": ret}, ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'}) - pip_install = MagicMock(return_value={ - 'retcode': 0, - 'stderr': '', - 'stdout': 'Downloading/unpacking https://pypi.python.org/packages' - '/source/S/SaltTesting/SaltTesting-0.5.0.tar.gz\n ' - 'Downloading SaltTesting-0.5.0.tar.gz\n Running ' - 'setup.py egg_info for package from ' - 'https://pypi.python.org/packages/source/S/SaltTesting/' - 'SaltTesting-0.5.0.tar.gz\n \nCleaning up...' - }) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list, - 'pip.install': pip_install}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"SaltTesting": "0.5.0"}) + pip_install = MagicMock( + return_value={ + "retcode": 0, + "stderr": "", + "stdout": "Downloading/unpacking https://pypi.python.org/packages" + "/source/S/SaltTesting/SaltTesting-0.5.0.tar.gz\n " + "Downloading SaltTesting-0.5.0.tar.gz\n Running " + "setup.py egg_info for package from " + "https://pypi.python.org/packages/source/S/SaltTesting/" + "SaltTesting-0.5.0.tar.gz\n \nCleaning up...", + } + ) + with patch.dict( + pip_state.__salt__, + {"cmd.run_all": mock, "pip.list": pip_list, "pip.install": pip_install}, + ): ret = pip_state.installed( - 'https://pypi.python.org/packages/source/S/SaltTesting/' - 'SaltTesting-0.5.0.tar.gz' - '#md5=e6760af92b7165f8be53b5763e40bc24' + "https://pypi.python.org/packages/source/S/SaltTesting/" + "SaltTesting-0.5.0.tar.gz" + "#md5=e6760af92b7165f8be53b5763e40bc24" ) - self.assertSaltTrueReturn({'test': ret}) - self.assertInSaltComment('All packages were successfully installed', - {'test': ret} + self.assertSaltTrueReturn({"test": ret}) + self.assertInSaltComment( + "All packages were successfully installed", {"test": ret} ) self.assertInSaltReturn( - 'Installed', - {'test': ret}, - ('changes', 'https://pypi.python.org/packages/source/S/' - 'SaltTesting/SaltTesting-0.5.0.tar.gz' - '#md5=e6760af92b7165f8be53b5763e40bc24==???') + "Installed", + {"test": ret}, + ( + "changes", + "https://pypi.python.org/packages/source/S/" + "SaltTesting/SaltTesting-0.5.0.tar.gz" + "#md5=e6760af92b7165f8be53b5763e40bc24==???", + ), ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'}) - pip_install = MagicMock(return_value={ - 'retcode': 0, - 'stderr': '', - 'stdout': 'Cloned!' - }) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list, - 'pip.install': pip_install}): - with patch.dict(pip_state.__opts__, {'test': False}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"SaltTesting": "0.5.0"}) + pip_install = MagicMock( + return_value={"retcode": 0, "stderr": "", "stdout": "Cloned!"} + ) + with patch.dict( + pip_state.__salt__, + {"cmd.run_all": mock, "pip.list": pip_list, "pip.install": pip_install}, + ): + with patch.dict(pip_state.__opts__, {"test": False}): ret = pip_state.installed( - 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting' + "git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting" ) - self.assertSaltTrueReturn({'test': ret}) + self.assertSaltTrueReturn({"test": ret}) self.assertInSaltComment( - 'packages are already installed', - {'test': ret} + "packages are already installed", {"test": ret} ) - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'pep8': '1.3.1'}) - pip_install = MagicMock(return_value={'retcode': 0}) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list, - 'pip.install': pip_install}): - with patch.dict(pip_state.__opts__, {'test': False}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"pep8": "1.3.1"}) + pip_install = MagicMock(return_value={"retcode": 0}) + with patch.dict( + pip_state.__salt__, + {"cmd.run_all": mock, "pip.list": pip_list, "pip.install": pip_install}, + ): + with patch.dict(pip_state.__opts__, {"test": False}): ret = pip_state.installed( - 'arbitrary ID that should be ignored due to requirements specified', - requirements='/tmp/non-existing-requirements.txt' + "arbitrary ID that should be ignored due to requirements specified", + requirements="/tmp/non-existing-requirements.txt", ) - self.assertSaltTrueReturn({'test': ret}) + self.assertSaltTrueReturn({"test": ret}) # Test VCS installations using git+git:// - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) - pip_list = MagicMock(return_value={'SaltTesting': '0.5.0'}) - pip_install = MagicMock(return_value={ - 'retcode': 0, - 'stderr': '', - 'stdout': 'Cloned!' - }) - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list, - 'pip.install': pip_install}): - with patch.dict(pip_state.__opts__, {'test': False}): + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) + pip_list = MagicMock(return_value={"SaltTesting": "0.5.0"}) + pip_install = MagicMock( + return_value={"retcode": 0, "stderr": "", "stdout": "Cloned!"} + ) + with patch.dict( + pip_state.__salt__, + {"cmd.run_all": mock, "pip.list": pip_list, "pip.install": pip_install}, + ): + with patch.dict(pip_state.__opts__, {"test": False}): ret = pip_state.installed( - 'git+git://github.com/saltstack/salt-testing.git#egg=SaltTesting' + "git+git://github.com/saltstack/salt-testing.git#egg=SaltTesting" ) - self.assertSaltTrueReturn({'test': ret}) + self.assertSaltTrueReturn({"test": ret}) self.assertInSaltComment( - 'packages are already installed', - {'test': ret} + "packages are already installed", {"test": ret} + ) + + def test_install_requirements_custom_pypi(self): + """ + test requirement parsing for both when a custom + pypi index-url is set and when it is not and + the requirement is already installed. + """ + + # create requirements file + req_filename = os.path.join( + RUNTIME_VARS.TMP_STATE_TREE, "custom-pypi-requirements.txt" + ) + with salt.utils.files.fopen(req_filename, "wb") as reqf: + reqf.write(b"pep8\n") + + site_pkgs = "/tmp/pip-env/lib/python3.7/site-packages" + check_stdout = [ + ( + "Looking in indexes: https://custom-pypi-url.org," + "https://pypi.org/simple/\nRequirement already satisfied: pep8 in {1}" + "(from -r /tmp/files/prod/{0} (line 1)) (1.7.1)".format( + req_filename, site_pkgs + ) + ), + ( + "Requirement already satisfied: pep8 in {1}" + "(from -r /tmp/files/prod/{0} (line1)) (1.7.1)".format( + req_filename, site_pkgs + ) + ), + ] + pip_version = pip.__version__ + mock_pip_version = MagicMock(return_value=pip_version) + + for stdout in check_stdout: + pip_install = MagicMock(return_value={"retcode": 0, "stdout": stdout}) + with patch.dict(pip_state.__salt__, {"pip.version": mock_pip_version}): + with patch.dict(pip_state.__salt__, {"pip.install": pip_install}): + ret = pip_state.installed(name="", requirements=req_filename) + self.assertSaltTrueReturn({"test": ret}) + assert "Requirements were already installed." == ret["comment"] + + def test_install_requirements_custom_pypi_changes(self): + """ + test requirement parsing for both when a custom + pypi index-url is set and when it is not and + the requirement is not installed. + """ + + # create requirements file + req_filename = os.path.join( + RUNTIME_VARS.TMP_STATE_TREE, "custom-pypi-requirements.txt" + ) + with salt.utils.files.fopen(req_filename, "wb") as reqf: + reqf.write(b"pep8\n") + + site_pkgs = "/tmp/pip-env/lib/python3.7/site-packages" + check_stdout = [ + ( + "Looking in indexes: https://custom-pypi-url.org," + "https://pypi.org/simple/\nCollecting pep8\n Using cached" + "https://custom-pypi-url.org//packages/42/3f/669429cef5acb4/pep8-1.7.1-py2.py3-none-any.whl" + " (41 kB)\nInstalling collected packages: pep8\nSuccessfully installed pep8-1.7.1" + ), + ( + "Collecting pep8\n Using cached" + "https://custom-pypi-url.org//packages/42/3f/669429cef5acb4/pep8-1.7.1-py2.py3-none-any.whl" + " (41 kB)\nInstalling collected packages: pep8\nSuccessfully installed pep8-1.7.1" + ), + ] + + pip_version = pip.__version__ + mock_pip_version = MagicMock(return_value=pip_version) + + for stdout in check_stdout: + pip_install = MagicMock(return_value={"retcode": 0, "stdout": stdout}) + with patch.dict(pip_state.__salt__, {"pip.version": mock_pip_version}): + with patch.dict(pip_state.__salt__, {"pip.install": pip_install}): + ret = pip_state.installed(name="", requirements=req_filename) + self.assertSaltTrueReturn({"test": ret}) + assert ( + "Successfully processed requirements file {0}.".format( + req_filename + ) + == ret["comment"] ) def test_install_in_editable_mode(self): - ''' + """ Check that `name` parameter containing bad characters is not parsed by pip when package is being installed in editable mode. For more information, see issue #21890. - ''' - mock = MagicMock(return_value={'retcode': 0, 'stdout': ''}) + """ + mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) pip_list = MagicMock(return_value={}) - pip_install = MagicMock(return_value={ - 'retcode': 0, - 'stderr': '', - 'stdout': 'Cloned!' - }) - pip_version = MagicMock(return_value='10.0.1') - with patch.dict(pip_state.__salt__, {'cmd.run_all': mock, - 'pip.list': pip_list, - 'pip.install': pip_install, - 'pip.version': pip_version}): - ret = pip_state.installed('state@name', - cwd='/path/to/project', - editable=['.']) - self.assertSaltTrueReturn({'test': ret}) - self.assertInSaltComment( - 'successfully installed', - {'test': ret} + pip_install = MagicMock( + return_value={"retcode": 0, "stderr": "", "stdout": "Cloned!"} + ) + pip_version = MagicMock(return_value="10.0.1") + with patch.dict( + pip_state.__salt__, + { + "cmd.run_all": mock, + "pip.list": pip_list, + "pip.install": pip_install, + "pip.version": pip_version, + }, + ): + ret = pip_state.installed( + "state@name", cwd="/path/to/project", editable=["."] ) + self.assertSaltTrueReturn({"test": ret}) + self.assertInSaltComment("successfully installed", {"test": ret}) class PipStateUtilsTest(TestCase): - def test_has_internal_exceptions_mod_function(self): - assert pip_state.pip_has_internal_exceptions_mod('10.0') - assert pip_state.pip_has_internal_exceptions_mod('18.1') - assert not pip_state.pip_has_internal_exceptions_mod('9.99') + assert pip_state.pip_has_internal_exceptions_mod("10.0") + assert pip_state.pip_has_internal_exceptions_mod("18.1") + assert not pip_state.pip_has_internal_exceptions_mod("9.99") def test_has_exceptions_mod_function(self): - assert pip_state.pip_has_exceptions_mod('1.0') - assert not pip_state.pip_has_exceptions_mod('0.1') - assert not pip_state.pip_has_exceptions_mod('10.0') + assert pip_state.pip_has_exceptions_mod("1.0") + assert not pip_state.pip_has_exceptions_mod("0.1") + assert not pip_state.pip_has_exceptions_mod("10.0") def test_pip_purge_method_with_pip(self): mock_modules = sys.modules.copy() - mock_modules.pop('pip', None) - mock_modules['pip'] = object() - with patch('sys.modules', mock_modules): + mock_modules.pop("pip", None) + mock_modules["pip"] = object() + with patch("sys.modules", mock_modules): pip_state.purge_pip() - assert 'pip' not in mock_modules + assert "pip" not in mock_modules def test_pip_purge_method_without_pip(self): mock_modules = sys.modules.copy() - mock_modules.pop('pip', None) - with patch('sys.modules', mock_modules): + mock_modules.pop("pip", None) + with patch("sys.modules", mock_modules): pip_state.purge_pip() -@skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed') +@skipIf( + salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, "virtualenv not installed" +) class PipStateInstallationErrorTest(TestCase): - def test_importable_installation_error(self): extra_requirements = [] for name, version in salt.version.dependency_information(): - if name in ['PyYAML']: - extra_requirements.append('{}=={}'.format(name, version)) + if name in ["PyYAML"]: + extra_requirements.append("{}=={}".format(name, version)) failures = {} pip_version_requirements = [ # Latest pip 8 - '<9.0', + "<9.0", # Latest pip 9 - '<10.0', + "<10.0", # Latest pip 18 - '<19.0', + "<19.0", # Latest pip 19 - '<20.0', + "<20.0", # Latest pip 20 - '<21.0', + "<21.0", # Latest pip None, ] - code = dedent('''\ + code = dedent( + """\ import sys import traceback try: @@ -358,28 +460,37 @@ class PipStateInstallationErrorTest(TestCase): sys.stdout.flush() sys.exit(3) sys.exit(0) - ''') + """ + ) for requirement in list(pip_version_requirements): try: with VirtualEnv() as venv: venv.install(*extra_requirements) if requirement: - venv.install('pip{}'.format(requirement)) + venv.install("pip{}".format(requirement)) try: - subprocess.check_output([venv.venv_python, '-c', code]) + subprocess.check_output([venv.venv_python, "-c", code]) except subprocess.CalledProcessError as exc: if exc.returncode == 1: - failures[requirement] = 'Failed to import pip:\n{}'.format(exc.output) + failures[requirement] = "Failed to import pip:\n{}".format( + exc.output + ) elif exc.returncode == 2: - failures[requirement] = 'Failed to import InstallationError from pip:\n{}'.format(exc.output) + failures[ + requirement + ] = "Failed to import InstallationError from pip:\n{}".format( + exc.output + ) else: failures[requirement] = exc.output except Exception as exc: # pylint: disable=broad-except failures[requirement] = str(exc) if failures: - errors = '' + errors = "" for requirement, exception in failures.items(): - errors += 'pip{}: {}\n\n'.format(requirement or '', exception) + errors += "pip{}: {}\n\n".format(requirement or "", exception) self.fail( - 'Failed to get InstallationError exception under at least one pip version:\n{}'.format(errors) + "Failed to get InstallationError exception under at least one pip version:\n{}".format( + errors + ) ) diff --git a/tests/unit/states/test_pkg.py b/tests/unit/states/test_pkg.py index 174ab65ab8e..b0ced9fd290 100644 --- a/tests/unit/states/test_pkg.py +++ b/tests/unit/states/test_pkg.py @@ -3,198 +3,239 @@ # Import Python libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +import salt.states.pkg as pkg # Import Salt Libs from salt.ext import six -import salt.states.pkg as pkg from salt.ext.six.moves import zip +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PkgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.pkg - ''' + """ + pkgs = { - 'pkga': {'old': '1.0.1', 'new': '2.0.1'}, - 'pkgb': {'old': '1.0.2', 'new': '2.0.2'}, - 'pkgc': {'old': '1.0.3', 'new': '2.0.3'} + "pkga": {"old": "1.0.1", "new": "2.0.1"}, + "pkgb": {"old": "1.0.2", "new": "2.0.2"}, + "pkgc": {"old": "1.0.3", "new": "2.0.3"}, } def setup_loader_modules(self): - return { - pkg: { - '__grains__': { - 'os': 'CentOS' - } - } - } + return {pkg: {"__grains__": {"os": "CentOS"}}} def test_uptodate_with_changes(self): - ''' + """ Test pkg.uptodate with simulated changes - ''' - list_upgrades = MagicMock(return_value={ - pkgname: pkgver['new'] for pkgname, pkgver in six.iteritems(self.pkgs) - }) + """ + list_upgrades = MagicMock( + return_value={ + pkgname: pkgver["new"] for pkgname, pkgver in six.iteritems(self.pkgs) + } + ) upgrade = MagicMock(return_value=self.pkgs) - version = MagicMock(side_effect=lambda pkgname: self.pkgs[pkgname]['old']) + version = MagicMock(side_effect=lambda pkgname: self.pkgs[pkgname]["old"]) - with patch.dict(pkg.__salt__, - {'pkg.list_upgrades': list_upgrades, - 'pkg.upgrade': upgrade, - 'pkg.version': version}): + with patch.dict( + pkg.__salt__, + { + "pkg.list_upgrades": list_upgrades, + "pkg.upgrade": upgrade, + "pkg.version": version, + }, + ): # Run state with test=false - with patch.dict(pkg.__opts__, {'test': False}): + with patch.dict(pkg.__opts__, {"test": False}): - ret = pkg.uptodate('dummy', test=True) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], self.pkgs) + ret = pkg.uptodate("dummy", test=True) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], self.pkgs) # Run state with test=true - with patch.dict(pkg.__opts__, {'test': True}): - ret = pkg.uptodate('dummy', test=True) - self.assertIsNone(ret['result']) - self.assertDictEqual(ret['changes'], self.pkgs) + with patch.dict(pkg.__opts__, {"test": True}): + ret = pkg.uptodate("dummy", test=True) + self.assertIsNone(ret["result"]) + self.assertDictEqual(ret["changes"], self.pkgs) def test_uptodate_with_pkgs_with_changes(self): - ''' + """ Test pkg.uptodate with simulated changes - ''' + """ pkgs = { - 'pkga': {'old': '1.0.1', 'new': '2.0.1'}, - 'pkgb': {'old': '1.0.2', 'new': '2.0.2'}, - 'pkgc': {'old': '1.0.3', 'new': '2.0.3'} + "pkga": {"old": "1.0.1", "new": "2.0.1"}, + "pkgb": {"old": "1.0.2", "new": "2.0.2"}, + "pkgc": {"old": "1.0.3", "new": "2.0.3"}, } - list_upgrades = MagicMock(return_value={ - pkgname: pkgver['new'] for pkgname, pkgver in six.iteritems(self.pkgs) - }) + list_upgrades = MagicMock( + return_value={ + pkgname: pkgver["new"] for pkgname, pkgver in six.iteritems(self.pkgs) + } + ) upgrade = MagicMock(return_value=self.pkgs) - version = MagicMock(side_effect=lambda pkgname: pkgs[pkgname]['old']) + version = MagicMock(side_effect=lambda pkgname: pkgs[pkgname]["old"]) - with patch.dict(pkg.__salt__, - {'pkg.list_upgrades': list_upgrades, - 'pkg.upgrade': upgrade, - 'pkg.version': version}): + with patch.dict( + pkg.__salt__, + { + "pkg.list_upgrades": list_upgrades, + "pkg.upgrade": upgrade, + "pkg.version": version, + }, + ): # Run state with test=false - with patch.dict(pkg.__opts__, {'test': False}): - ret = pkg.uptodate('dummy', test=True, pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)]) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], pkgs) + with patch.dict(pkg.__opts__, {"test": False}): + ret = pkg.uptodate( + "dummy", + test=True, + pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)], + ) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], pkgs) # Run state with test=true - with patch.dict(pkg.__opts__, {'test': True}): - ret = pkg.uptodate('dummy', test=True, pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)]) - self.assertIsNone(ret['result']) - self.assertDictEqual(ret['changes'], pkgs) + with patch.dict(pkg.__opts__, {"test": True}): + ret = pkg.uptodate( + "dummy", + test=True, + pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)], + ) + self.assertIsNone(ret["result"]) + self.assertDictEqual(ret["changes"], pkgs) def test_uptodate_no_changes(self): - ''' + """ Test pkg.uptodate with no changes - ''' + """ list_upgrades = MagicMock(return_value={}) upgrade = MagicMock(return_value={}) - with patch.dict(pkg.__salt__, - {'pkg.list_upgrades': list_upgrades, - 'pkg.upgrade': upgrade}): + with patch.dict( + pkg.__salt__, {"pkg.list_upgrades": list_upgrades, "pkg.upgrade": upgrade} + ): # Run state with test=false - with patch.dict(pkg.__opts__, {'test': False}): + with patch.dict(pkg.__opts__, {"test": False}): - ret = pkg.uptodate('dummy', test=True) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], {}) + ret = pkg.uptodate("dummy", test=True) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], {}) # Run state with test=true - with patch.dict(pkg.__opts__, {'test': True}): - ret = pkg.uptodate('dummy', test=True) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], {}) + with patch.dict(pkg.__opts__, {"test": True}): + ret = pkg.uptodate("dummy", test=True) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], {}) def test_uptodate_with_pkgs_no_changes(self): - ''' + """ Test pkg.uptodate with no changes - ''' + """ list_upgrades = MagicMock(return_value={}) upgrade = MagicMock(return_value={}) - with patch.dict(pkg.__salt__, - {'pkg.list_upgrades': list_upgrades, - 'pkg.upgrade': upgrade}): + with patch.dict( + pkg.__salt__, {"pkg.list_upgrades": list_upgrades, "pkg.upgrade": upgrade} + ): # Run state with test=false - with patch.dict(pkg.__opts__, {'test': False}): - ret = pkg.uptodate('dummy', test=True, pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)]) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], {}) + with patch.dict(pkg.__opts__, {"test": False}): + ret = pkg.uptodate( + "dummy", + test=True, + pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)], + ) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], {}) # Run state with test=true - with patch.dict(pkg.__opts__, {'test': True}): - ret = pkg.uptodate('dummy', test=True, pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)]) - self.assertTrue(ret['result']) - self.assertDictEqual(ret['changes'], {}) + with patch.dict(pkg.__opts__, {"test": True}): + ret = pkg.uptodate( + "dummy", + test=True, + pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)], + ) + self.assertTrue(ret["result"]) + self.assertDictEqual(ret["changes"], {}) def test_uptodate_with_failed_changes(self): - ''' + """ Test pkg.uptodate with simulated failed changes - ''' + """ pkgs = { - 'pkga': {'old': '1.0.1', 'new': '2.0.1'}, - 'pkgb': {'old': '1.0.2', 'new': '2.0.2'}, - 'pkgc': {'old': '1.0.3', 'new': '2.0.3'} + "pkga": {"old": "1.0.1", "new": "2.0.1"}, + "pkgb": {"old": "1.0.2", "new": "2.0.2"}, + "pkgc": {"old": "1.0.3", "new": "2.0.3"}, } - list_upgrades = MagicMock(return_value={ - pkgname: pkgver['new'] for pkgname, pkgver in six.iteritems(self.pkgs) - }) + list_upgrades = MagicMock( + return_value={ + pkgname: pkgver["new"] for pkgname, pkgver in six.iteritems(self.pkgs) + } + ) upgrade = MagicMock(return_value={}) - version = MagicMock(side_effect=lambda pkgname: pkgs[pkgname]['old']) + version = MagicMock(side_effect=lambda pkgname: pkgs[pkgname]["old"]) - with patch.dict(pkg.__salt__, - {'pkg.list_upgrades': list_upgrades, - 'pkg.upgrade': upgrade, - 'pkg.version': version}): + with patch.dict( + pkg.__salt__, + { + "pkg.list_upgrades": list_upgrades, + "pkg.upgrade": upgrade, + "pkg.version": version, + }, + ): # Run state with test=false - with patch.dict(pkg.__opts__, {'test': False}): - ret = pkg.uptodate('dummy', test=True, pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)]) - self.assertFalse(ret['result']) - self.assertDictEqual(ret['changes'], {}) + with patch.dict(pkg.__opts__, {"test": False}): + ret = pkg.uptodate( + "dummy", + test=True, + pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)], + ) + self.assertFalse(ret["result"]) + self.assertDictEqual(ret["changes"], {}) # Run state with test=true - with patch.dict(pkg.__opts__, {'test': True}): - ret = pkg.uptodate('dummy', test=True, pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)]) - self.assertIsNone(ret['result']) - self.assertDictEqual(ret['changes'], pkgs) + with patch.dict(pkg.__opts__, {"test": True}): + ret = pkg.uptodate( + "dummy", + test=True, + pkgs=[pkgname for pkgname in six.iterkeys(self.pkgs)], + ) + self.assertIsNone(ret["result"]) + self.assertDictEqual(ret["changes"], pkgs) def test_parse_version_string(self): test_parameters = [ - ("> 1.0.0, < 15.0.0, != 14.0.1", [(">", "1.0.0"), ("<", "15.0.0"), ("!=", "14.0.1")]), - ("> 1.0.0,< 15.0.0,!= 14.0.1", [(">", "1.0.0"), ("<", "15.0.0"), ("!=", "14.0.1")]), + ( + "> 1.0.0, < 15.0.0, != 14.0.1", + [(">", "1.0.0"), ("<", "15.0.0"), ("!=", "14.0.1")], + ), + ( + "> 1.0.0,< 15.0.0,!= 14.0.1", + [(">", "1.0.0"), ("<", "15.0.0"), ("!=", "14.0.1")], + ), (">= 1.0.0, < 15.0.0", [(">=", "1.0.0"), ("<", "15.0.0")]), (">=1.0.0,< 15.0.0", [(">=", "1.0.0"), ("<", "15.0.0")]), ("< 15.0.0", [("<", "15.0.0")]), ("<15.0.0", [("<", "15.0.0")]), ("15.0.0", [("==", "15.0.0")]), - ("", []) + ("", []), ] for version_string, expected_version_conditions in test_parameters: version_conditions = pkg._parse_version_string(version_string) - self.assertEqual(len(expected_version_conditions), - len(version_conditions)) - for expected_version_condition, version_condition in zip(expected_version_conditions, version_conditions): - self.assertEqual( - expected_version_condition[0], version_condition[0]) - self.assertEqual( - expected_version_condition[1], version_condition[1]) + self.assertEqual(len(expected_version_conditions), len(version_conditions)) + for expected_version_condition, version_condition in zip( + expected_version_conditions, version_conditions + ): + self.assertEqual(expected_version_condition[0], version_condition[0]) + self.assertEqual(expected_version_condition[1], version_condition[1]) def test_fulfills_version_string(self): test_parameters = [ @@ -203,7 +244,11 @@ class PkgTestCase(TestCase, LoaderModuleMockMixin): ("> 1.0.0, < 15.0.0, != 14.0.1", ["14.0.1"], False), ("> 1.0.0, < 15.0.0, != 14.0.1", ["16.0.0"], False), ("> 1.0.0, < 15.0.0, != 14.0.1", ["2.0.0"], True), - ("> 1.0.0, < 15.0.0, != 14.0.1", ["1.0.0", "14.0.1", "16.0.0", "2.0.0"], True), + ( + "> 1.0.0, < 15.0.0, != 14.0.1", + ["1.0.0", "14.0.1", "16.0.0", "2.0.0"], + True, + ), ("> 15.0.0", [], False), ("> 15.0.0", ["1.0.0"], False), ("> 15.0.0", ["16.0.0"], True), @@ -212,11 +257,17 @@ class PkgTestCase(TestCase, LoaderModuleMockMixin): # No version specified, whatever version installed. This is threated like ANY version installed fulfills. ("", ["15.0.0"], True), # No version specified, no version installed. - ("", [], False) + ("", [], False), ] for version_string, installed_versions, expected_result in test_parameters: - msg = "version_string: {}, installed_versions: {}, expected_result: {}".format(version_string, installed_versions, expected_result) - self.assertEqual(expected_result, pkg._fulfills_version_string(installed_versions, version_string), msg) + msg = "version_string: {}, installed_versions: {}, expected_result: {}".format( + version_string, installed_versions, expected_result + ) + self.assertEqual( + expected_result, + pkg._fulfills_version_string(installed_versions, version_string), + msg, + ) def test_fulfills_version_spec(self): test_parameters = [ @@ -231,5 +282,11 @@ class PkgTestCase(TestCase, LoaderModuleMockMixin): ([], "==", "17.0.0", False), ] for installed_versions, operator, version, expected_result in test_parameters: - msg = "installed_versions: {}, operator: {}, version: {}, expected_result: {}".format(installed_versions, operator, version, expected_result) - self.assertEqual(expected_result, pkg._fulfills_version_spec(installed_versions, operator, version), msg) + msg = "installed_versions: {}, operator: {}, version: {}, expected_result: {}".format( + installed_versions, operator, version, expected_result + ) + self.assertEqual( + expected_result, + pkg._fulfills_version_spec(installed_versions, operator, version), + msg, + ) diff --git a/tests/unit/states/test_pkgng.py b/tests/unit/states/test_pkgng.py index 42d9101fcc0..6108ec01c0e 100644 --- a/tests/unit/states/test_pkgng.py +++ b/tests/unit/states/test_pkgng.py @@ -1,41 +1,37 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.pkgng as pkgng +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PkgngTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.pkgng - ''' + """ + def setup_loader_modules(self): return {pkgng: {}} # 'update_packaging_site' function tests: 1 def test_update_packaging_site(self): - ''' + """ Test to execute update_packaging_site. - ''' + """ name = "http://192.168.0.2" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} mock_t = MagicMock(return_value=True) - with patch.dict(pkgng.__salt__, {'pkgng.update_package_site': mock_t}): + with patch.dict(pkgng.__salt__, {"pkgng.update_package_site": mock_t}): self.assertDictEqual(pkgng.update_packaging_site(name), ret) diff --git a/tests/unit/states/test_portage_config.py b/tests/unit/states/test_portage_config.py index 2feb5cb5dfb..25c0438677f 100644 --- a/tests/unit/states/test_portage_config.py +++ b/tests/unit/states/test_portage_config.py @@ -1,39 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.portage_config as portage_config +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PortageConfigTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.portage_config - ''' + """ + def setup_loader_modules(self): - return {portage_config: {'__opts__': {'test': True}}} + return {portage_config: {"__opts__": {"test": True}}} # 'mod_init' function tests: 1 def test_mod_init(self): - ''' + """ Test to enforce a nice structure on the configuration files. - ''' - name = 'salt' + """ + name = "salt" mock = MagicMock(side_effect=[True, Exception]) - with patch.dict(portage_config.__salt__, - {'portage_config.enforce_nice_config': mock}): + with patch.dict( + portage_config.__salt__, {"portage_config.enforce_nice_config": mock} + ): self.assertTrue(portage_config.mod_init(name)) self.assertFalse(portage_config.mod_init(name)) @@ -41,36 +41,33 @@ class PortageConfigTestCase(TestCase, LoaderModuleMockMixin): # 'flags' function tests: 1 def test_flags(self): - ''' + """ Test to enforce the given flags on the given package or ``DEPEND`` atom. - ''' - with patch('traceback.format_exc', MagicMock(return_value='SALTSTACK')): - name = 'salt' + """ + with patch("traceback.format_exc", MagicMock(return_value="SALTSTACK")): + name = "salt" - ret = {'name': name, - 'result': False, - 'comment': 'SALTSTACK', - 'changes': {}} + ret = {"name": name, "result": False, "comment": "SALTSTACK", "changes": {}} - mock = MagicMock(side_effect=Exception('error')) - with patch.dict(portage_config.__salt__, - {'portage_config.get_missing_flags': mock}): - self.assertDictEqual(portage_config.flags(name, use='openssl'), ret) + mock = MagicMock(side_effect=Exception("error")) + with patch.dict( + portage_config.__salt__, {"portage_config.get_missing_flags": mock} + ): + self.assertDictEqual(portage_config.flags(name, use="openssl"), ret) - self.assertDictEqual(portage_config.flags(name, - accept_keywords=True), - ret) + self.assertDictEqual( + portage_config.flags(name, accept_keywords=True), ret + ) self.assertDictEqual(portage_config.flags(name, env=True), ret) self.assertDictEqual(portage_config.flags(name, license=True), ret) - self.assertDictEqual(portage_config.flags(name, properties=True), - ret) + self.assertDictEqual(portage_config.flags(name, properties=True), ret) self.assertDictEqual(portage_config.flags(name, mask=True), ret) self.assertDictEqual(portage_config.flags(name, unmask=True), ret) - ret.update({'comment': '', 'result': True}) + ret.update({"comment": "", "result": True}) self.assertDictEqual(portage_config.flags(name), ret) diff --git a/tests/unit/states/test_ports.py b/tests/unit/states/test_ports.py index 27130ceca82..f896ec30692 100644 --- a/tests/unit/states/test_ports.py +++ b/tests/unit/states/test_ports.py @@ -1,134 +1,145 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -from salt.exceptions import SaltInvocationError - # Import Salt Libs import salt.states.ports as ports +from salt.exceptions import SaltInvocationError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class MockModule(object): """ Mock of __module__ """ - __module__ = 'A' + + __module__ = "A" class MockContext(object): """ Mock of __context__ """ - __context__ = {'ports.install_error': 'salt'} + + __context__ = {"ports.install_error": "salt"} class MockSys(object): """ Mock of sys """ + def __init__(self): - self.modules = {'A': MockContext()} + self.modules = {"A": MockContext()} class PortsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ports - ''' + """ + def setup_loader_modules(self): - return {ports: {'sys': MockSys()}} + return {ports: {"sys": MockSys()}} # 'installed' function tests: 1 def test_installed(self): - ''' + """ Test to verify that the desired port is installed, and that it was compiled with the desired options. - ''' - name = 'security/nmap' - options = [{'IPV6': 'on'}] + """ + name = "security/nmap" + options = [{"IPV6": "on"}] - ret = {'name': name, - 'result': None, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": None, "comment": "", "changes": {}} mock = MagicMock(side_effect=SaltInvocationError) - with patch.dict(ports.__salt__, {'ports.showconfig': mock}): - comt = ('Unable to get configuration for {0}. Port name may ' - 'be invalid, or ports tree may need to be updated. ' - 'Error message: '.format(name)) - ret.update({'comment': comt, 'result': False}) + with patch.dict(ports.__salt__, {"ports.showconfig": mock}): + comt = ( + "Unable to get configuration for {0}. Port name may " + "be invalid, or ports tree may need to be updated. " + "Error message: ".format(name) + ) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(ports.installed(name), ret) mock = MagicMock(return_value={}) - mock_lst = MagicMock(return_value={'origin': {'origin': name}}) - with patch.dict(ports.__salt__, {'ports.showconfig': mock, - 'pkg.list_pkgs': mock_lst}): - comt = ('security/nmap is already installed') - ret.update({'comment': comt, 'result': True}) + mock_lst = MagicMock(return_value={"origin": {"origin": name}}) + with patch.dict( + ports.__salt__, {"ports.showconfig": mock, "pkg.list_pkgs": mock_lst} + ): + comt = "security/nmap is already installed" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ports.installed(name), ret) - comt = ('security/nmap does not have any build options,' - ' yet options were specified') - ret.update({'comment': comt, 'result': False}) + comt = ( + "security/nmap does not have any build options," + " yet options were specified" + ) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(ports.installed(name, options), ret) - mock_dict = MagicMock(return_value={'origin': {'origin': 'salt'}}) - with patch.dict(ports.__salt__, {'pkg.list_pkgs': mock_dict}): - with patch.dict(ports.__opts__, {'test': True}): - comt = ('{0} will be installed'.format(name)) - ret.update({'comment': comt, 'result': None}) + mock_dict = MagicMock(return_value={"origin": {"origin": "salt"}}) + with patch.dict(ports.__salt__, {"pkg.list_pkgs": mock_dict}): + with patch.dict(ports.__opts__, {"test": True}): + comt = "{0} will be installed".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(ports.installed(name), ret) - mock = MagicMock(return_value={'salt': {'salt': 'salt'}}) - mock_dict = MagicMock(return_value={'origin': {'origin': 'salt'}}) + mock = MagicMock(return_value={"salt": {"salt": "salt"}}) + mock_dict = MagicMock(return_value={"origin": {"origin": "salt"}}) mock_f = MagicMock(return_value=False) mock_t = MagicMock(return_value=True) - with patch.dict(ports.__salt__, {'ports.showconfig': mock, - 'pkg.list_pkgs': mock_dict, - 'ports.config': mock_f, - 'ports.rmconfig': mock_t}): - with patch.dict(ports.__opts__, {'test': True}): - comt = ('The following options are not available' - ' for security/nmap: IPV6') - ret.update({'comment': comt, 'result': False}) + with patch.dict( + ports.__salt__, + { + "ports.showconfig": mock, + "pkg.list_pkgs": mock_dict, + "ports.config": mock_f, + "ports.rmconfig": mock_t, + }, + ): + with patch.dict(ports.__opts__, {"test": True}): + comt = ( + "The following options are not available" " for security/nmap: IPV6" + ) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(ports.installed(name, options), ret) - comt = ('security/nmap will be installed with the ' - 'default build options') - ret.update({'comment': comt, 'result': None}) + comt = ( + "security/nmap will be installed with the " "default build options" + ) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(ports.installed(name), ret) - with patch.dict(ports.__opts__, {'test': False}): - comt = ('Unable to set options for security/nmap') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(ports.installed(name, [{'salt': 'salt'}]), - ret) + with patch.dict(ports.__opts__, {"test": False}): + comt = "Unable to set options for security/nmap" + ret.update({"comment": comt, "result": False}) + self.assertDictEqual(ports.installed(name, [{"salt": "salt"}]), ret) - with patch.object(os.path, 'isfile', mock_t): - with patch.object(os.path, 'isdir', mock_t): - comt = ('Unable to clear options for security/nmap') - ret.update({'comment': comt, 'result': False}) + with patch.object(os.path, "isfile", mock_t): + with patch.object(os.path, "isdir", mock_t): + comt = "Unable to clear options for security/nmap" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(ports.installed(name), ret) - with patch.dict(ports.__salt__, {'ports.config': mock_t, - 'ports.install': mock_t, - 'test.ping': MockModule()}): - comt = ('Failed to install security/nmap.' - ' Error message:\nsalt') - ret.update({'comment': comt, 'result': False, - 'changes': True}) - self.assertDictEqual(ports.installed(name, - [{'salt': 'salt'}]), - ret) + with patch.dict( + ports.__salt__, + { + "ports.config": mock_t, + "ports.install": mock_t, + "test.ping": MockModule(), + }, + ): + comt = "Failed to install security/nmap." " Error message:\nsalt" + ret.update({"comment": comt, "result": False, "changes": True}) + self.assertDictEqual(ports.installed(name, [{"salt": "salt"}]), ret) diff --git a/tests/unit/states/test_postgres.py b/tests/unit/states/test_postgres.py index e8b9793112c..79b5c04552e 100644 --- a/tests/unit/states/test_postgres.py +++ b/tests/unit/states/test_postgres.py @@ -1,582 +1,761 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import Mock, MagicMock, patch +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.modules.postgres as postgresmod import salt.states.postgres_database as postgres_database -import salt.states.postgres_user as postgres_user -import salt.states.postgres_group as postgres_group import salt.states.postgres_extension as postgres_extension +import salt.states.postgres_group as postgres_group import salt.states.postgres_schema as postgres_schema +import salt.states.postgres_user as postgres_user + +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase class PostgresUserTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/pgsql')) + patcher = patch("salt.utils.path.which", Mock(return_value="/usr/bin/pgsql")) patcher.start() self.addCleanup(patcher.stop) self.salt_stub = { - 'config.option': Mock(), - 'cmd.run_all': Mock(), - 'file.chown': Mock(), - 'file.remove': Mock(), + "config.option": Mock(), + "cmd.run_all": Mock(), + "file.chown": Mock(), + "file.remove": Mock(), } - self.addCleanup(delattr, self, 'salt_stub') + self.addCleanup(delattr, self, "salt_stub") return { postgres_database: {}, postgres_group: {}, postgres_extension: {}, postgres_schema: {}, postgres_user: { - '__grains__': {'os_family': 'Linux'}, - '__salt__': self.salt_stub, - '__opts__': {'test': False}, - } + "__grains__": {"os_family": "Linux"}, + "__salt__": self.salt_stub, + "__opts__": {"test": False}, + }, } def test_present__creation(self): # test=True - with patch.dict(postgres_user.__salt__, {'postgres.role_get': Mock(return_value=None), - 'postgres.user_create': MagicMock()}): - with patch.dict(postgres_user.__opts__, {'test': True}): - ret = postgres_user.present('foo') + with patch.dict( + postgres_user.__salt__, + { + "postgres.role_get": Mock(return_value=None), + "postgres.user_create": MagicMock(), + }, + ): + with patch.dict(postgres_user.__opts__, {"test": True}): + ret = postgres_user.present("foo") self.assertEqual( ret, - {'comment': 'User foo is set to be created', - 'changes': {}, 'name': 'foo', 'result': None} + { + "comment": "User foo is set to be created", + "changes": {}, + "name": "foo", + "result": None, + }, ) - self.assertEqual(self.salt_stub['postgres.user_create'].call_count, 0) + self.assertEqual(self.salt_stub["postgres.user_create"].call_count, 0) # test=False - ret = postgres_user.present('foo') + ret = postgres_user.present("foo") self.assertEqual( ret, - {'comment': 'The user foo has been created', - 'changes': {'foo': 'Present'}, 'name': 'foo', 'result': True} + { + "comment": "The user foo has been created", + "changes": {"foo": "Present"}, + "name": "foo", + "result": True, + }, + ) + self.salt_stub["postgres.user_create"].assert_called_once_with( + username="foo", + superuser=None, + encrypted=True, + runas=None, + inherit=None, + rolepassword=None, + port=None, + replication=None, + host=None, + createroles=None, + user=None, + groups=None, + maintenance_db=None, + login=None, + password=None, + valid_until=None, + createdb=None, ) - self.salt_stub['postgres.user_create'].assert_called_once_with(username='foo', - superuser=None, - encrypted=True, - runas=None, - inherit=None, - rolepassword=None, - port=None, - replication=None, - host=None, - createroles=None, - user=None, - groups=None, - maintenance_db=None, - login=None, - password=None, - valid_until=None, - createdb=None) def test_present__update(self): # test=True - with patch.dict(postgres_user.__salt__, {'postgres.role_get': Mock(return_value={ - 'can create databases': False, - 'can create roles': False, - 'can login': False, - 'can update system catalogs': False, - 'connections': None, - 'defaults variables': {}, - 'expiry time': None, - 'inherits privileges': True, - 'replication': False, - 'superuser': False, - }), - 'postgres.user_update': MagicMock()}): - with patch.dict(postgres_user.__opts__, {'test': True}): - ret = postgres_user.present('foo', login=True, replication=False) + with patch.dict( + postgres_user.__salt__, + { + "postgres.role_get": Mock( + return_value={ + "can create databases": False, + "can create roles": False, + "can login": False, + "can update system catalogs": False, + "connections": None, + "defaults variables": {}, + "expiry time": None, + "inherits privileges": True, + "replication": False, + "superuser": False, + } + ), + "postgres.user_update": MagicMock(), + }, + ): + with patch.dict(postgres_user.__opts__, {"test": True}): + ret = postgres_user.present("foo", login=True, replication=False) self.assertEqual( ret, - {'comment': 'User foo is set to be updated', - 'changes': {'foo': {'login': True}}, 'name': 'foo', 'result': None} + { + "comment": "User foo is set to be updated", + "changes": {"foo": {"login": True}}, + "name": "foo", + "result": None, + }, ) - self.assertEqual(self.salt_stub['postgres.user_update'].call_count, 0) + self.assertEqual(self.salt_stub["postgres.user_update"].call_count, 0) # test=False - ret = postgres_user.present('foo', login=True, replication=False) + ret = postgres_user.present("foo", login=True, replication=False) self.assertEqual( ret, - {'comment': 'The user foo has been updated', - 'changes': {'foo': {'login': True}}, 'name': 'foo', 'result': True} + { + "comment": "The user foo has been updated", + "changes": {"foo": {"login": True}}, + "name": "foo", + "result": True, + }, + ) + self.salt_stub["postgres.user_update"].assert_called_once_with( + username="foo", + superuser=None, + encrypted=True, + runas=None, + inherit=None, + rolepassword=None, + port=None, + replication=False, + host=None, + createroles=None, + user=None, + groups=None, + maintenance_db=None, + login=True, + password=None, + valid_until=None, + createdb=None, ) - self.salt_stub['postgres.user_update'].assert_called_once_with(username='foo', - superuser=None, - encrypted=True, - runas=None, - inherit=None, - rolepassword=None, - port=None, - replication=False, - host=None, - createroles=None, - user=None, - groups=None, - maintenance_db=None, - login=True, - password=None, - valid_until=None, - createdb=None) def test_present__no_update(self): # test=True - with patch.dict(postgres_user.__salt__, {'postgres.role_get': Mock(return_value={ - 'can create databases': False, - 'can create roles': False, - 'can login': False, - 'can update system catalogs': False, - 'connections': None, - 'defaults variables': {}, - 'expiry time': None, - 'inherits privileges': True, - 'replication': False, - 'superuser': False, - }), - 'postgres.user_update': MagicMock()}): - with patch.dict(postgres_user.__opts__, {'test': True}): - ret = postgres_user.present('foo', login=False, replication=False) + with patch.dict( + postgres_user.__salt__, + { + "postgres.role_get": Mock( + return_value={ + "can create databases": False, + "can create roles": False, + "can login": False, + "can update system catalogs": False, + "connections": None, + "defaults variables": {}, + "expiry time": None, + "inherits privileges": True, + "replication": False, + "superuser": False, + } + ), + "postgres.user_update": MagicMock(), + }, + ): + with patch.dict(postgres_user.__opts__, {"test": True}): + ret = postgres_user.present("foo", login=False, replication=False) self.assertEqual( ret, - {'comment': 'User foo is already present', - 'changes': {}, 'name': 'foo', 'result': True} + { + "comment": "User foo is already present", + "changes": {}, + "name": "foo", + "result": True, + }, ) - self.assertEqual(self.salt_stub['postgres.user_update'].call_count, 0) + self.assertEqual(self.salt_stub["postgres.user_update"].call_count, 0) # test=False - ret = postgres_user.present('foo', login=False, replication=False) + ret = postgres_user.present("foo", login=False, replication=False) self.assertEqual( ret, - {'comment': 'User foo is already present', - 'changes': {}, 'name': 'foo', 'result': True} + { + "comment": "User foo is already present", + "changes": {}, + "name": "foo", + "result": True, + }, ) - self.assertEqual(self.salt_stub['postgres.user_update'].call_count, 0) + self.assertEqual(self.salt_stub["postgres.user_update"].call_count, 0) class PostgresGroupTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/pgsql')) + patcher = patch("salt.utils.path.which", Mock(return_value="/usr/bin/pgsql")) patcher.start() self.addCleanup(patcher.stop) self.salt_stub = { - 'config.option': Mock(), - 'cmd.run_all': Mock(), - 'file.chown': Mock(), - 'file.remove': Mock(), + "config.option": Mock(), + "cmd.run_all": Mock(), + "file.chown": Mock(), + "file.remove": Mock(), } - self.addCleanup(delattr, self, 'salt_stub') + self.addCleanup(delattr, self, "salt_stub") return { postgres_database: {}, postgres_user: {}, postgres_extension: {}, postgres_schema: {}, postgres_group: { - '__grains__': {'os_family': 'Linux'}, - '__salt__': self.salt_stub, - '__opts__': {'test': False}, - } + "__grains__": {"os_family": "Linux"}, + "__salt__": self.salt_stub, + "__opts__": {"test": False}, + }, } def test_present__creation(self): # test=True - with patch.dict(postgres_group.__salt__, {'postgres.role_get': Mock(return_value=None), - 'postgres.group_create': MagicMock()}): - with patch.dict(postgres_group.__opts__, {'test': True}): - ret = postgres_group.present('foo') + with patch.dict( + postgres_group.__salt__, + { + "postgres.role_get": Mock(return_value=None), + "postgres.group_create": MagicMock(), + }, + ): + with patch.dict(postgres_group.__opts__, {"test": True}): + ret = postgres_group.present("foo") self.assertEqual( ret, - {'comment': 'Group foo is set to be created', - 'changes': {}, 'name': 'foo', 'result': None} + { + "comment": "Group foo is set to be created", + "changes": {}, + "name": "foo", + "result": None, + }, ) - self.assertEqual(self.salt_stub['postgres.group_create'].call_count, 0) + self.assertEqual(self.salt_stub["postgres.group_create"].call_count, 0) # test=False - ret = postgres_group.present('foo') + ret = postgres_group.present("foo") self.assertEqual( ret, - {'comment': 'The group foo has been created', - 'changes': {}, 'name': 'foo', 'result': True} + { + "comment": "The group foo has been created", + "changes": {}, + "name": "foo", + "result": True, + }, + ) + self.salt_stub["postgres.group_create"].assert_called_once_with( + superuser=None, + replication=None, + encrypted=True, + runas=None, + inherit=None, + rolepassword=None, + port=None, + groupname="foo", + host=None, + createroles=None, + user=None, + groups=None, + maintenance_db=None, + login=None, + password=None, + createdb=None, ) - self.salt_stub['postgres.group_create'].assert_called_once_with(superuser=None, - replication=None, - encrypted=True, - runas=None, - inherit=None, - rolepassword=None, - port=None, - groupname='foo', - host=None, - createroles=None, - user=None, - groups=None, - maintenance_db=None, - login=None, - password=None, - createdb=None) def test_present__update(self): # test=True - with patch.dict(postgres_group.__salt__, {'postgres.role_get': Mock(return_value={ - 'can create databases': False, - 'can create roles': False, - 'can login': False, - 'can update system catalogs': False, - 'connections': None, - 'defaults variables': {}, - 'expiry time': None, - 'inherits privileges': True, - 'replication': False, - 'superuser': False, - }), - 'postgres.group_update': MagicMock()}): - with patch.dict(postgres_group.__opts__, {'test': True}): - ret = postgres_group.present('foo', login=True, replication=False) + with patch.dict( + postgres_group.__salt__, + { + "postgres.role_get": Mock( + return_value={ + "can create databases": False, + "can create roles": False, + "can login": False, + "can update system catalogs": False, + "connections": None, + "defaults variables": {}, + "expiry time": None, + "inherits privileges": True, + "replication": False, + "superuser": False, + } + ), + "postgres.group_update": MagicMock(), + }, + ): + with patch.dict(postgres_group.__opts__, {"test": True}): + ret = postgres_group.present("foo", login=True, replication=False) self.assertEqual( ret, - {'comment': 'Group foo is set to be updated', - 'changes': {'foo': {'login': True}}, 'name': 'foo', 'result': None} + { + "comment": "Group foo is set to be updated", + "changes": {"foo": {"login": True}}, + "name": "foo", + "result": None, + }, ) - self.assertEqual(self.salt_stub['postgres.group_update'].call_count, 0) + self.assertEqual(self.salt_stub["postgres.group_update"].call_count, 0) # test=False - ret = postgres_group.present('foo', login=True, replication=False) + ret = postgres_group.present("foo", login=True, replication=False) self.assertEqual( ret, - {'comment': 'The group foo has been updated', - 'changes': {'foo': {'login': True}}, 'name': 'foo', 'result': True} + { + "comment": "The group foo has been updated", + "changes": {"foo": {"login": True}}, + "name": "foo", + "result": True, + }, + ) + self.salt_stub["postgres.group_update"].assert_called_once_with( + superuser=None, + replication=False, + encrypted=True, + runas=None, + inherit=None, + rolepassword=None, + port=None, + groupname="foo", + host=None, + createroles=None, + user=None, + groups=None, + maintenance_db=None, + login=True, + password=None, + createdb=None, ) - self.salt_stub['postgres.group_update'].assert_called_once_with(superuser=None, - replication=False, - encrypted=True, - runas=None, - inherit=None, - rolepassword=None, - port=None, - groupname='foo', - host=None, - createroles=None, - user=None, - groups=None, - maintenance_db=None, - login=True, - password=None, - createdb=None) def test_present__no_update(self): # test=True - with patch.dict(postgres_group.__salt__, {'postgres.role_get': Mock(return_value={ - 'can create databases': False, - 'can create roles': False, - 'can login': False, - 'can update system catalogs': False, - 'connections': None, - 'defaults variables': {}, - 'expiry time': None, - 'inherits privileges': True, - 'replication': False, - 'superuser': False, - }), - 'postgres.group_update': MagicMock()}): - with patch.dict(postgres_group.__opts__, {'test': True}): - ret = postgres_group.present('foo', login=False, replication=False) + with patch.dict( + postgres_group.__salt__, + { + "postgres.role_get": Mock( + return_value={ + "can create databases": False, + "can create roles": False, + "can login": False, + "can update system catalogs": False, + "connections": None, + "defaults variables": {}, + "expiry time": None, + "inherits privileges": True, + "replication": False, + "superuser": False, + } + ), + "postgres.group_update": MagicMock(), + }, + ): + with patch.dict(postgres_group.__opts__, {"test": True}): + ret = postgres_group.present("foo", login=False, replication=False) self.assertEqual( ret, - {'comment': 'Group foo is already present', - 'changes': {}, 'name': 'foo', 'result': True} + { + "comment": "Group foo is already present", + "changes": {}, + "name": "foo", + "result": True, + }, ) - self.assertEqual(self.salt_stub['postgres.group_update'].call_count, 0) + self.assertEqual(self.salt_stub["postgres.group_update"].call_count, 0) # test=False - ret = postgres_group.present('foo', login=False, replication=False) + ret = postgres_group.present("foo", login=False, replication=False) self.assertEqual( ret, - {'comment': 'Group foo is already present', - 'changes': {}, 'name': 'foo', 'result': True} + { + "comment": "Group foo is already present", + "changes": {}, + "name": "foo", + "result": True, + }, ) - self.assertEqual(self.salt_stub['postgres.group_update'].call_count, 0) + self.assertEqual(self.salt_stub["postgres.group_update"].call_count, 0) class PostgresExtensionTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/pgsql')) + patcher = patch("salt.utils.path.which", Mock(return_value="/usr/bin/pgsql")) patcher.start() self.addCleanup(patcher.stop) self.salt_stub = { - 'config.option': Mock(), - 'cmd.run_all': Mock(), - 'file.chown': Mock(), - 'file.remove': Mock(), + "config.option": Mock(), + "cmd.run_all": Mock(), + "file.chown": Mock(), + "file.remove": Mock(), } - self.addCleanup(delattr, self, 'salt_stub') + self.addCleanup(delattr, self, "salt_stub") return { postgres_database: {}, postgres_user: {}, postgres_group: {}, postgres_schema: {}, postgres_extension: { - '__grains__': {'os_family': 'Linux'}, - '__salt__': self.salt_stub, - '__opts__': {'test': False}, - } + "__grains__": {"os_family": "Linux"}, + "__salt__": self.salt_stub, + "__opts__": {"test": False}, + }, } def test_present_failed(self): - ''' + """ scenario of creating upgrading extensions with possible schema and version specifications - ''' - with patch.dict(postgres_extension.__salt__, { - 'postgres.create_metadata': Mock(side_effect=[ + """ + with patch.dict( + postgres_extension.__salt__, + { + "postgres.create_metadata": Mock( + side_effect=[ [postgresmod._EXTENSION_NOT_INSTALLED], - [postgresmod._EXTENSION_TO_MOVE, postgresmod._EXTENSION_INSTALLED], - - ]), - 'postgres.create_extension': Mock(side_effect=[ - False, False, - ])}): - ret = postgres_extension.present('foo') + [ + postgresmod._EXTENSION_TO_MOVE, + postgresmod._EXTENSION_INSTALLED, + ], + ] + ), + "postgres.create_extension": Mock(side_effect=[False, False]), + }, + ): + ret = postgres_extension.present("foo") self.assertEqual( ret, - {'comment': 'Failed to install extension foo', - 'changes': {}, 'name': 'foo', 'result': False}, + { + "comment": "Failed to install extension foo", + "changes": {}, + "name": "foo", + "result": False, + }, ) - ret = postgres_extension.present('foo') + ret = postgres_extension.present("foo") self.assertEqual( ret, - {'comment': 'Failed to upgrade extension foo', - 'changes': {}, 'name': 'foo', 'result': False} + { + "comment": "Failed to upgrade extension foo", + "changes": {}, + "name": "foo", + "result": False, + }, ) def test_present(self): - ''' + """ scenario of creating upgrading extensions with possible schema and version specifications - ''' - with patch.dict(postgres_extension.__salt__, { - 'postgres.create_metadata': Mock(side_effect=[ + """ + with patch.dict( + postgres_extension.__salt__, + { + "postgres.create_metadata": Mock( + side_effect=[ [postgresmod._EXTENSION_NOT_INSTALLED], [postgresmod._EXTENSION_INSTALLED], - [postgresmod._EXTENSION_TO_MOVE, postgresmod._EXTENSION_INSTALLED], - - ]), - 'postgres.create_extension': Mock(side_effect=[ - True, True, True, - ])}): - ret = postgres_extension.present('foo') + [ + postgresmod._EXTENSION_TO_MOVE, + postgresmod._EXTENSION_INSTALLED, + ], + ] + ), + "postgres.create_extension": Mock(side_effect=[True, True, True]), + }, + ): + ret = postgres_extension.present("foo") self.assertEqual( ret, - {'comment': 'The extension foo has been installed', - 'changes': {'foo': 'Installed'}, 'name': 'foo', 'result': True} + { + "comment": "The extension foo has been installed", + "changes": {"foo": "Installed"}, + "name": "foo", + "result": True, + }, ) - ret = postgres_extension.present('foo') + ret = postgres_extension.present("foo") self.assertEqual( ret, - {'comment': 'Extension foo is already present', - 'changes': {}, 'name': 'foo', 'result': True} + { + "comment": "Extension foo is already present", + "changes": {}, + "name": "foo", + "result": True, + }, ) - ret = postgres_extension.present('foo') + ret = postgres_extension.present("foo") self.assertEqual( ret, - {'comment': 'The extension foo has been upgraded', - 'changes': {'foo': 'Upgraded'}, 'name': 'foo', 'result': True} + { + "comment": "The extension foo has been upgraded", + "changes": {"foo": "Upgraded"}, + "name": "foo", + "result": True, + }, ) def test_presenttest(self): - ''' + """ scenario of creating upgrading extensions with possible schema and version specifications - ''' - with patch.dict(postgres_extension.__salt__, { - 'postgres.create_metadata': Mock(side_effect=[ + """ + with patch.dict( + postgres_extension.__salt__, + { + "postgres.create_metadata": Mock( + side_effect=[ [postgresmod._EXTENSION_NOT_INSTALLED], [postgresmod._EXTENSION_INSTALLED], - [postgresmod._EXTENSION_TO_MOVE, postgresmod._EXTENSION_INSTALLED], - - ]), - 'postgres.create_extension': Mock(side_effect=[ - True, True, True, - ])}): - with patch.dict(postgres_extension.__opts__, {'test': True}): - ret = postgres_extension.present('foo') + [ + postgresmod._EXTENSION_TO_MOVE, + postgresmod._EXTENSION_INSTALLED, + ], + ] + ), + "postgres.create_extension": Mock(side_effect=[True, True, True]), + }, + ): + with patch.dict(postgres_extension.__opts__, {"test": True}): + ret = postgres_extension.present("foo") self.assertEqual( ret, - {'comment': 'Extension foo is set to be installed', - 'changes': {}, 'name': 'foo', 'result': None} - + { + "comment": "Extension foo is set to be installed", + "changes": {}, + "name": "foo", + "result": None, + }, ) - ret = postgres_extension.present('foo') + ret = postgres_extension.present("foo") self.assertEqual( ret, - {'comment': "Extension foo is already present", - 'changes': {}, 'name': 'foo', 'result': True} - + { + "comment": "Extension foo is already present", + "changes": {}, + "name": "foo", + "result": True, + }, ) - ret = postgres_extension.present('foo') + ret = postgres_extension.present("foo") self.assertEqual( ret, - {'comment': "Extension foo is set to be upgraded", - 'changes': {}, 'name': 'foo', 'result': None} + { + "comment": "Extension foo is set to be upgraded", + "changes": {}, + "name": "foo", + "result": None, + }, ) def test_absent(self): - ''' + """ scenario of creating upgrading extensions with possible schema and version specifications - ''' - with patch.dict(postgres_extension.__salt__, { - 'postgres.is_installed_extension': Mock(side_effect=[ - True, False, - ]), - 'postgres.drop_extension': Mock(side_effect=[ - True, True, - ])}): - ret = postgres_extension.absent('foo') + """ + with patch.dict( + postgres_extension.__salt__, + { + "postgres.is_installed_extension": Mock(side_effect=[True, False]), + "postgres.drop_extension": Mock(side_effect=[True, True]), + }, + ): + ret = postgres_extension.absent("foo") self.assertEqual( ret, - {'comment': 'Extension foo has been removed', - 'changes': {'foo': 'Absent'}, 'name': 'foo', 'result': True} + { + "comment": "Extension foo has been removed", + "changes": {"foo": "Absent"}, + "name": "foo", + "result": True, + }, ) - ret = postgres_extension.absent('foo') + ret = postgres_extension.absent("foo") self.assertEqual( ret, - {'comment': ( - 'Extension foo is not present, ' - 'so it cannot be removed'), - 'changes': {}, 'name': 'foo', 'result': True} - + { + "comment": ( + "Extension foo is not present, " "so it cannot be removed" + ), + "changes": {}, + "name": "foo", + "result": True, + }, ) def test_absent_failed(self): - ''' + """ scenario of creating upgrading extensions with possible schema and version specifications - ''' - with patch.dict(postgres_extension.__opts__, {'test': False}): - with patch.dict(postgres_extension.__salt__, { - 'postgres.is_installed_extension': Mock(side_effect=[ - True, True, - ]), - 'postgres.drop_extension': Mock(side_effect=[ - False, False, - ])}): - ret = postgres_extension.absent('foo') + """ + with patch.dict(postgres_extension.__opts__, {"test": False}): + with patch.dict( + postgres_extension.__salt__, + { + "postgres.is_installed_extension": Mock(side_effect=[True, True]), + "postgres.drop_extension": Mock(side_effect=[False, False]), + }, + ): + ret = postgres_extension.absent("foo") self.assertEqual( ret, - {'comment': 'Extension foo failed to be removed', - 'changes': {}, 'name': 'foo', 'result': False} + { + "comment": "Extension foo failed to be removed", + "changes": {}, + "name": "foo", + "result": False, + }, ) def test_absent_failedtest(self): - with patch.dict(postgres_extension.__salt__, { - 'postgres.is_installed_extension': Mock(side_effect=[ - True, True, - ]), - 'postgres.drop_extension': Mock(side_effect=[ - False, False, - ])}): - with patch.dict(postgres_extension.__opts__, {'test': True}): - ret = postgres_extension.absent('foo') + with patch.dict( + postgres_extension.__salt__, + { + "postgres.is_installed_extension": Mock(side_effect=[True, True]), + "postgres.drop_extension": Mock(side_effect=[False, False]), + }, + ): + with patch.dict(postgres_extension.__opts__, {"test": True}): + ret = postgres_extension.absent("foo") self.assertEqual( ret, - {'comment': 'Extension foo is set to be removed', - 'changes': {}, 'name': 'foo', 'result': None} + { + "comment": "Extension foo is set to be removed", + "changes": {}, + "name": "foo", + "result": None, + }, ) class PostgresSchemaTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - patcher = patch('salt.utils.path.which', Mock(return_value='/usr/bin/pgsql')) + patcher = patch("salt.utils.path.which", Mock(return_value="/usr/bin/pgsql")) patcher.start() self.addCleanup(patcher.stop) self.salt_stub = { - 'config.option': Mock(), - 'cmd.run_all': Mock(), - 'file.chown': Mock(), - 'file.remove': Mock(), + "config.option": Mock(), + "cmd.run_all": Mock(), + "file.chown": Mock(), + "file.remove": Mock(), } - self.addCleanup(delattr, self, 'salt_stub') + self.addCleanup(delattr, self, "salt_stub") return { postgres_database: {}, postgres_user: {}, postgres_extension: {}, postgres_group: {}, postgres_schema: { - '__grains__': {'os_family': 'Linux'}, - '__salt__': self.salt_stub, - '__opts__': {'test': False}, - } + "__grains__": {"os_family": "Linux"}, + "__salt__": self.salt_stub, + "__opts__": {"test": False}, + }, } def test_present_creation(self): - with patch.dict(postgres_schema.__salt__, {'postgres.schema_get': Mock(return_value=None), - 'postgres.schema_create': MagicMock()}): - ret = postgres_schema.present('dbname', 'foo') + with patch.dict( + postgres_schema.__salt__, + { + "postgres.schema_get": Mock(return_value=None), + "postgres.schema_create": MagicMock(), + }, + ): + ret = postgres_schema.present("dbname", "foo") self.assertEqual( ret, - {'comment': 'Schema foo has been created in database dbname', - 'changes': {'foo': 'Present'}, - 'dbname': 'dbname', - 'name': 'foo', - 'result': True} - ) - self.assertEqual(self.salt_stub['postgres.schema_create'].call_count, 1) + { + "comment": "Schema foo has been created in database dbname", + "changes": {"foo": "Present"}, + "dbname": "dbname", + "name": "foo", + "result": True, + }, + ) + self.assertEqual(self.salt_stub["postgres.schema_create"].call_count, 1) def test_present_nocreation(self): - with patch.dict(postgres_schema.__salt__, { - 'postgres.schema_get': Mock(return_value={'foo': - {'acl': '', - 'owner': 'postgres'} - }), - 'postgres.schema_create': MagicMock()}): - ret = postgres_schema.present('dbname', 'foo') + with patch.dict( + postgres_schema.__salt__, + { + "postgres.schema_get": Mock( + return_value={"foo": {"acl": "", "owner": "postgres"}} + ), + "postgres.schema_create": MagicMock(), + }, + ): + ret = postgres_schema.present("dbname", "foo") self.assertEqual( ret, - {'comment': 'Schema foo already exists in database dbname', - 'changes': {}, - 'dbname': 'dbname', - 'name': 'foo', - 'result': True} - ) - self.assertEqual(self.salt_stub['postgres.schema_create'].call_count, 0) + { + "comment": "Schema foo already exists in database dbname", + "changes": {}, + "dbname": "dbname", + "name": "foo", + "result": True, + }, + ) + self.assertEqual(self.salt_stub["postgres.schema_create"].call_count, 0) def test_absent_remove(self): - with patch.dict(postgres_schema.__salt__, {'postgres.schema_exists': Mock(return_value=True), - 'postgres.schema_remove': MagicMock()}): - ret = postgres_schema.absent('dbname', 'foo') + with patch.dict( + postgres_schema.__salt__, + { + "postgres.schema_exists": Mock(return_value=True), + "postgres.schema_remove": MagicMock(), + }, + ): + ret = postgres_schema.absent("dbname", "foo") self.assertEqual( ret, - {'comment': 'Schema foo has been removed from database dbname', - 'changes': {'foo': 'Absent'}, - 'dbname': 'dbname', - 'name': 'foo', - 'result': True} - ) - self.assertEqual(self.salt_stub['postgres.schema_remove'].call_count, 1) + { + "comment": "Schema foo has been removed from database dbname", + "changes": {"foo": "Absent"}, + "dbname": "dbname", + "name": "foo", + "result": True, + }, + ) + self.assertEqual(self.salt_stub["postgres.schema_remove"].call_count, 1) def test_absent_noremove(self): - with patch.dict(postgres_schema.__salt__, {'postgres.schema_exists': Mock(return_value=False), - 'postgres.schema_remove': MagicMock()}): - ret = postgres_schema.absent('dbname', 'foo') + with patch.dict( + postgres_schema.__salt__, + { + "postgres.schema_exists": Mock(return_value=False), + "postgres.schema_remove": MagicMock(), + }, + ): + ret = postgres_schema.absent("dbname", "foo") self.assertEqual( ret, - {'comment': 'Schema foo is not present in database dbname,' - ' so it cannot be removed', - 'changes': {}, - 'dbname': 'dbname', - 'name': 'foo', - 'result': True} - ) - self.assertEqual(self.salt_stub['postgres.schema_remove'].call_count, 0) + { + "comment": "Schema foo is not present in database dbname," + " so it cannot be removed", + "changes": {}, + "dbname": "dbname", + "name": "foo", + "result": True, + }, + ) + self.assertEqual(self.salt_stub["postgres.schema_remove"].call_count, 0) diff --git a/tests/unit/states/test_postgres_cluster.py b/tests/unit/states/test_postgres_cluster.py index 632c00db0af..cb3ac9b6b4b 100644 --- a/tests/unit/states/test_postgres_cluster.py +++ b/tests/unit/states/test_postgres_cluster.py @@ -1,125 +1,138 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Logilab <contact@logilab.fr> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.postgres_cluster as postgres_cluster +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PostgresClusterTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.postgres_cluster - ''' + """ + def setup_loader_modules(self): return {postgres_cluster: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named database is present with the specified properties. - ''' - name = 'main' - version = '9.4' + """ + name = "main" + version = "9.4" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_t = MagicMock(return_value=True) mock_f = MagicMock(return_value=False) - infos = {'{0}/{1}'.format(version, name): {}} + infos = {"{0}/{1}".format(version, name): {}} mock = MagicMock(return_value=infos) - with patch.dict(postgres_cluster.__salt__, - {'postgres.cluster_list': mock, - 'postgres.cluster_exists': mock_t, - 'postgres.cluster_create': mock_t, - }): - comt = ('Cluster {0}/{1} is already present'.format(version, name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict( + postgres_cluster.__salt__, + { + "postgres.cluster_list": mock, + "postgres.cluster_exists": mock_t, + "postgres.cluster_create": mock_t, + }, + ): + comt = "Cluster {0}/{1} is already present".format(version, name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(postgres_cluster.present(version, name), ret) - infos['{0}/{1}'.format(version, name)]['port'] = 5433 - comt = ('Cluster {0}/{1} has wrong parameters ' - 'which couldn\'t be changed on fly.'.format(version, name)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(postgres_cluster.present(version, name, port=5434), ret) - infos['{0}/{1}'.format(version, name)]['datadir'] = '/tmp/' - comt = ('Cluster {0}/{1} has wrong parameters ' - 'which couldn\'t be changed on fly.'.format(version, name)) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(postgres_cluster.present(version, name, port=5434), ret) + infos["{0}/{1}".format(version, name)]["port"] = 5433 + comt = ( + "Cluster {0}/{1} has wrong parameters " + "which couldn't be changed on fly.".format(version, name) + ) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + postgres_cluster.present(version, name, port=5434), ret + ) + infos["{0}/{1}".format(version, name)]["datadir"] = "/tmp/" + comt = ( + "Cluster {0}/{1} has wrong parameters " + "which couldn't be changed on fly.".format(version, name) + ) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + postgres_cluster.present(version, name, port=5434), ret + ) - with patch.dict(postgres_cluster.__salt__, - {'postgres.cluster_list': mock, - 'postgres.cluster_exists': mock_f, - 'postgres.cluster_create': mock_t, - }): - comt = 'The cluster {0}/{1} has been created'.format(version, name) - ret.update({'comment': comt, 'result': True, - 'changes': {'{0}/{1}'.format(version, name): 'Present'} - }) - self.assertDictEqual(postgres_cluster.present(version, name), - ret) - with patch.dict(postgres_cluster.__opts__, {'test': True}): - comt = 'Cluster {0}/{1} is set to be created'.format(version, name) - ret.update({'comment': comt, 'result': None, 'changes': {}}) - self.assertDictEqual(postgres_cluster.present(version, name), - ret) + with patch.dict( + postgres_cluster.__salt__, + { + "postgres.cluster_list": mock, + "postgres.cluster_exists": mock_f, + "postgres.cluster_create": mock_t, + }, + ): + comt = "The cluster {0}/{1} has been created".format(version, name) + ret.update( + { + "comment": comt, + "result": True, + "changes": {"{0}/{1}".format(version, name): "Present"}, + } + ) + self.assertDictEqual(postgres_cluster.present(version, name), ret) + with patch.dict(postgres_cluster.__opts__, {"test": True}): + comt = "Cluster {0}/{1} is set to be created".format(version, name) + ret.update({"comment": comt, "result": None, "changes": {}}) + self.assertDictEqual(postgres_cluster.present(version, name), ret) - with patch.dict(postgres_cluster.__salt__, - {'postgres.cluster_list': mock, - 'postgres.cluster_exists': mock_f, - 'postgres.cluster_create': mock_f, - }): - comt = 'Failed to create cluster {0}/{1}'.format(version, name) - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(postgres_cluster.present(version, name), - ret) + with patch.dict( + postgres_cluster.__salt__, + { + "postgres.cluster_list": mock, + "postgres.cluster_exists": mock_f, + "postgres.cluster_create": mock_f, + }, + ): + comt = "Failed to create cluster {0}/{1}".format(version, name) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual(postgres_cluster.present(version, name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named database is absent. - ''' - name = 'main' - version = '9.4' + """ + name = "main" + version = "9.4" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_t = MagicMock(return_value=True) mock = MagicMock(side_effect=[True, True, False]) - with patch.dict(postgres_cluster.__salt__, - {'postgres.cluster_exists': mock, - 'postgres.cluster_remove': mock_t}): - with patch.dict(postgres_cluster.__opts__, {'test': True}): - comt = ('Cluster {0}/{1} is set to be removed'.format(version, name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + postgres_cluster.__salt__, + {"postgres.cluster_exists": mock, "postgres.cluster_remove": mock_t}, + ): + with patch.dict(postgres_cluster.__opts__, {"test": True}): + comt = "Cluster {0}/{1} is set to be removed".format(version, name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(postgres_cluster.absent(version, name), ret) - with patch.dict(postgres_cluster.__opts__, {'test': False}): - comt = ('Cluster {0}/{1} has been removed'.format(version, name)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Absent'}}) + with patch.dict(postgres_cluster.__opts__, {"test": False}): + comt = "Cluster {0}/{1} has been removed".format(version, name) + ret.update( + {"comment": comt, "result": True, "changes": {name: "Absent"}} + ) self.assertDictEqual(postgres_cluster.absent(version, name), ret) - comt = ('Cluster {0}/{1} is not present, so it cannot be removed' - .format(version, name)) - ret.update({'comment': comt, 'result': True, 'changes': {}}) + comt = "Cluster {0}/{1} is not present, so it cannot be removed".format( + version, name + ) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(postgres_cluster.absent(version, name), ret) diff --git a/tests/unit/states/test_postgres_database.py b/tests/unit/states/test_postgres_database.py index b24fd73b5e0..61b45178570 100644 --- a/tests/unit/states/test_postgres_database.py +++ b/tests/unit/states/test_postgres_database.py @@ -1,105 +1,107 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.postgres_database as postgres_database +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PostgresDatabaseTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.postgres_database - ''' + """ + def setup_loader_modules(self): return {postgres_database: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named database is present with the specified properties. - ''' - name = 'frank' + """ + name = "frank" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_t = MagicMock(return_value=True) mock = MagicMock(return_value={name: {}}) - with patch.dict(postgres_database.__salt__, - {'postgres.db_list': mock, - 'postgres.db_alter': mock_t}): - comt = ('Database {0} is already present'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict( + postgres_database.__salt__, + {"postgres.db_list": mock, "postgres.db_alter": mock_t}, + ): + comt = "Database {0} is already present".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(postgres_database.present(name), ret) - comt = ("Database frank has wrong parameters " - "which couldn't be changed on fly.") - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(postgres_database.present(name, tablespace='A', - lc_collate=True), - ret) + comt = ( + "Database frank has wrong parameters " + "which couldn't be changed on fly." + ) + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + postgres_database.present(name, tablespace="A", lc_collate=True), ret + ) - with patch.dict(postgres_database.__opts__, {'test': True}): - comt = ('Database frank exists, ' - 'but parameters need to be changed') - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(postgres_database.present(name, - tablespace='A'), - ret) + with patch.dict(postgres_database.__opts__, {"test": True}): + comt = "Database frank exists, " "but parameters need to be changed" + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + postgres_database.present(name, tablespace="A"), ret + ) - with patch.dict(postgres_database.__opts__, {'test': False}): - comt = ('Parameters for database frank have been changed') - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Parameters changed'}}) - self.assertDictEqual(postgres_database.present(name, - tablespace='A'), - ret) + with patch.dict(postgres_database.__opts__, {"test": False}): + comt = "Parameters for database frank have been changed" + ret.update( + { + "comment": comt, + "result": True, + "changes": {name: "Parameters changed"}, + } + ) + self.assertDictEqual( + postgres_database.present(name, tablespace="A"), ret + ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named database is absent. - ''' - name = 'frank' + """ + name = "frank" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_t = MagicMock(return_value=True) mock = MagicMock(side_effect=[True, True, False]) - with patch.dict(postgres_database.__salt__, - {'postgres.db_exists': mock, - 'postgres.db_remove': mock_t}): - with patch.dict(postgres_database.__opts__, {'test': True}): - comt = ('Database {0} is set to be removed'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + postgres_database.__salt__, + {"postgres.db_exists": mock, "postgres.db_remove": mock_t}, + ): + with patch.dict(postgres_database.__opts__, {"test": True}): + comt = "Database {0} is set to be removed".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(postgres_database.absent(name), ret) - with patch.dict(postgres_database.__opts__, {'test': False}): - comt = ('Database {0} has been removed'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Absent'}}) + with patch.dict(postgres_database.__opts__, {"test": False}): + comt = "Database {0} has been removed".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {name: "Absent"}} + ) self.assertDictEqual(postgres_database.absent(name), ret) - comt = ('Database {0} is not present, so it cannot be removed' - .format(name)) - ret.update({'comment': comt, 'result': True, 'changes': {}}) + comt = "Database {0} is not present, so it cannot be removed".format( + name + ) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(postgres_database.absent(name), ret) diff --git a/tests/unit/states/test_postgres_extension.py b/tests/unit/states/test_postgres_extension.py index 5f454f76624..f0fc850889a 100644 --- a/tests/unit/states/test_postgres_extension.py +++ b/tests/unit/states/test_postgres_extension.py @@ -1,90 +1,87 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.postgres_extension as postgres_extension +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PostgresExtensionTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.postgres_extension - ''' + """ + def setup_loader_modules(self): return {postgres_extension: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named extension is present with the specified privileges. - ''' - name = 'frank' + """ + name = "frank" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock = MagicMock(return_value={}) - with patch.dict(postgres_extension.__salt__, - {'postgres.create_metadata': mock}): - with patch.dict(postgres_extension.__opts__, {'test': True}): - comt = ('Extension {0} is already present'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict( + postgres_extension.__salt__, {"postgres.create_metadata": mock} + ): + with patch.dict(postgres_extension.__opts__, {"test": True}): + comt = "Extension {0} is already present".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(postgres_extension.present(name), ret) - with patch.dict(postgres_extension.__opts__, {'test': False}): - comt = ('Extension {0} is already present'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict(postgres_extension.__opts__, {"test": False}): + comt = "Extension {0} is already present".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(postgres_extension.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named extension is absent. - ''' - name = 'frank' + """ + name = "frank" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_t = MagicMock(side_effect=[True, False]) mock = MagicMock(side_effect=[True, True, True, False]) - with patch.dict(postgres_extension.__salt__, - {'postgres.is_installed_extension': mock, - 'postgres.drop_extension': mock_t}): - with patch.dict(postgres_extension.__opts__, {'test': True}): - comt = ('Extension {0} is set to be removed'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + postgres_extension.__salt__, + { + "postgres.is_installed_extension": mock, + "postgres.drop_extension": mock_t, + }, + ): + with patch.dict(postgres_extension.__opts__, {"test": True}): + comt = "Extension {0} is set to be removed".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(postgres_extension.absent(name), ret) - with patch.dict(postgres_extension.__opts__, {'test': False}): - comt = ('Extension {0} has been removed'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Absent'}}) + with patch.dict(postgres_extension.__opts__, {"test": False}): + comt = "Extension {0} has been removed".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {name: "Absent"}} + ) self.assertDictEqual(postgres_extension.absent(name), ret) - comt = ('Extension {0} failed to be removed'.format(name)) - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "Extension {0} failed to be removed".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(postgres_extension.absent(name), ret) - comt = ('Extension {0} is not present, so it cannot be removed' - .format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "Extension {0} is not present, so it cannot be removed".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(postgres_extension.absent(name), ret) diff --git a/tests/unit/states/test_postgres_group.py b/tests/unit/states/test_postgres_group.py index 95a66239b27..f859625a76c 100644 --- a/tests/unit/states/test_postgres_group.py +++ b/tests/unit/states/test_postgres_group.py @@ -1,88 +1,82 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.postgres_group as postgres_group +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PostgresGroupTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.postgres_group - ''' + """ + def setup_loader_modules(self): return {postgres_group: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named group is present with the specified privileges. - ''' - name = 'frank' + """ + name = "frank" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_t = MagicMock(return_value=True) mock = MagicMock(return_value=None) - with patch.dict(postgres_group.__salt__, - {'postgres.role_get': mock, - 'postgres.group_create': mock_t}): - with patch.dict(postgres_group.__opts__, {'test': True}): - comt = ('Group {0} is set to be created'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + postgres_group.__salt__, + {"postgres.role_get": mock, "postgres.group_create": mock_t}, + ): + with patch.dict(postgres_group.__opts__, {"test": True}): + comt = "Group {0} is set to be created".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(postgres_group.present(name), ret) - with patch.dict(postgres_group.__opts__, {'test': False}): - comt = ('The group {0} has been created'.format(name)) - ret.update({'comment': comt, 'result': True}) + with patch.dict(postgres_group.__opts__, {"test": False}): + comt = "The group {0} has been created".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(postgres_group.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named group is absent. - ''' - name = 'frank' + """ + name = "frank" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_t = MagicMock(return_value=True) mock = MagicMock(side_effect=[True, True, False]) - with patch.dict(postgres_group.__salt__, - {'postgres.user_exists': mock, - 'postgres.group_remove': mock_t}): - with patch.dict(postgres_group.__opts__, {'test': True}): - comt = ('Group {0} is set to be removed'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + postgres_group.__salt__, + {"postgres.user_exists": mock, "postgres.group_remove": mock_t}, + ): + with patch.dict(postgres_group.__opts__, {"test": True}): + comt = "Group {0} is set to be removed".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(postgres_group.absent(name), ret) - with patch.dict(postgres_group.__opts__, {'test': False}): - comt = ('Group {0} has been removed'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Absent'}}) + with patch.dict(postgres_group.__opts__, {"test": False}): + comt = "Group {0} has been removed".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {name: "Absent"}} + ) self.assertDictEqual(postgres_group.absent(name), ret) - comt = ('Group {0} is not present, so it cannot be removed' - .format(name)) - ret.update({'comment': comt, 'result': True, 'changes': {}}) + comt = "Group {0} is not present, so it cannot be removed".format(name) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(postgres_group.absent(name), ret) diff --git a/tests/unit/states/test_postgres_initdb.py b/tests/unit/states/test_postgres_initdb.py index 42b825682e5..f939bc587f7 100644 --- a/tests/unit/states/test_postgres_initdb.py +++ b/tests/unit/states/test_postgres_initdb.py @@ -1,37 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Andrew Colin Kissa <andrew@topdog.za.net> -''' -from __future__ import absolute_import, unicode_literals, print_function - -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +""" +from __future__ import absolute_import, print_function, unicode_literals import salt.states.postgres_initdb as postgres_initdb +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class PostgresInitdbTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.postgres_initdb - ''' + """ def setup_loader_modules(self): return {postgres_initdb: {}} def setUp(self): - ''' + """ Setup data for the tests - ''' - self.name = '/var/lib/psql/data' - self.ret = { - 'name': self.name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + self.name = "/var/lib/psql/data" + self.ret = {"name": self.name, "changes": {}, "result": False, "comment": ""} self.mock_true = MagicMock(return_value=True) self.mock_false = MagicMock(return_value=False) @@ -41,54 +33,56 @@ class PostgresInitdbTestCase(TestCase, LoaderModuleMockMixin): del self.mock_false def test_present_existing(self): - ''' + """ Test existing data directory handled correctly - ''' - with patch.dict(postgres_initdb.__salt__, - {'postgres.datadir_exists': self.mock_true}): - _comt = 'Postgres data directory {0} is already present'\ - .format(self.name) - self.ret.update({'comment': _comt, 'result': True}) + """ + with patch.dict( + postgres_initdb.__salt__, {"postgres.datadir_exists": self.mock_true} + ): + _comt = "Postgres data directory {0} is already present".format(self.name) + self.ret.update({"comment": _comt, "result": True}) self.assertDictEqual(postgres_initdb.present(self.name), self.ret) def test_present_non_existing_pass(self): - ''' + """ Test non existing data directory ok - ''' - with patch.dict(postgres_initdb.__salt__, - {'postgres.datadir_exists': self.mock_false, - 'postgres.datadir_init': self.mock_true}): - with patch.dict(postgres_initdb.__opts__, {'test': True}): - _comt = 'Postgres data directory {0} is set to be initialized'\ - .format(self.name) - self.ret.update({'comment': _comt, 'result': None}) - self.assertDictEqual( - postgres_initdb.present(self.name), self.ret) + """ + with patch.dict( + postgres_initdb.__salt__, + { + "postgres.datadir_exists": self.mock_false, + "postgres.datadir_init": self.mock_true, + }, + ): + with patch.dict(postgres_initdb.__opts__, {"test": True}): + _comt = "Postgres data directory {0} is set to be initialized".format( + self.name + ) + self.ret.update({"comment": _comt, "result": None}) + self.assertDictEqual(postgres_initdb.present(self.name), self.ret) - with patch.dict(postgres_initdb.__opts__, {'test': False}): - _comt = 'Postgres data directory {0} has been initialized'\ - .format(self.name) - _changes = {self.name: 'Present'} - self.ret.update({ - 'comment': _comt, - 'result': True, - 'changes': _changes}) - self.assertDictEqual( - postgres_initdb.present(self.name), self.ret) + with patch.dict(postgres_initdb.__opts__, {"test": False}): + _comt = "Postgres data directory {0} has been initialized".format( + self.name + ) + _changes = {self.name: "Present"} + self.ret.update({"comment": _comt, "result": True, "changes": _changes}) + self.assertDictEqual(postgres_initdb.present(self.name), self.ret) def test_present_non_existing_fail(self): - ''' + """ Test non existing data directory fail - ''' - with patch.dict(postgres_initdb.__salt__, - {'postgres.datadir_exists': self.mock_false, - 'postgres.datadir_init': self.mock_false}): - with patch.dict(postgres_initdb.__opts__, {'test': False}): - _comt = 'Postgres data directory {0} initialization failed'\ - .format(self.name) - self.ret.update({ - 'comment': _comt, - 'result': False - }) - self.assertDictEqual( - postgres_initdb.present(self.name), self.ret) + """ + with patch.dict( + postgres_initdb.__salt__, + { + "postgres.datadir_exists": self.mock_false, + "postgres.datadir_init": self.mock_false, + }, + ): + with patch.dict(postgres_initdb.__opts__, {"test": False}): + _comt = "Postgres data directory {0} initialization failed".format( + self.name + ) + self.ret.update({"comment": _comt, "result": False}) + self.assertDictEqual(postgres_initdb.present(self.name), self.ret) diff --git a/tests/unit/states/test_postgres_language.py b/tests/unit/states/test_postgres_language.py index 843ad921e10..923dd2124e5 100644 --- a/tests/unit/states/test_postgres_language.py +++ b/tests/unit/states/test_postgres_language.py @@ -1,41 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Andrew Colin Kissa <andrew@topdog.za.net> -''' -from __future__ import absolute_import, unicode_literals, print_function - -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +""" +from __future__ import absolute_import, print_function, unicode_literals import salt.states.postgres_language as postgres_language +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class PostgresLanguageTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.postgres_language - ''' + """ def setup_loader_modules(self): return {postgres_language: {}} def setUp(self): - ''' + """ Setup data for the tests - ''' - self.name = 'plpgsql' - self.ret = {'name': self.name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + self.name = "plpgsql" + self.ret = {"name": self.name, "changes": {}, "result": False, "comment": ""} self.mock_true = MagicMock(return_value=True) self.mock_false = MagicMock(return_value=False) self.mock_empty_language_list = MagicMock(return_value={}) - self.mock_language_list = MagicMock( - return_value={'plpgsql': self.name}) + self.mock_language_list = MagicMock(return_value={"plpgsql": self.name}) def tearDown(self): del self.ret @@ -45,86 +37,110 @@ class PostgresLanguageTestCase(TestCase, LoaderModuleMockMixin): del self.mock_empty_language_list def test_present_existing(self): - ''' + """ Test present, language is already present in database - ''' - with patch.dict(postgres_language.__salt__, - {'postgres.language_list': self.mock_language_list}): - comt = 'Language {0} is already installed'.format(self.name) - self.ret.update({'comment': comt, 'result': True}) + """ + with patch.dict( + postgres_language.__salt__, + {"postgres.language_list": self.mock_language_list}, + ): + comt = "Language {0} is already installed".format(self.name) + self.ret.update({"comment": comt, "result": True}) self.assertDictEqual( - postgres_language.present(self.name, 'testdb'), self.ret) + postgres_language.present(self.name, "testdb"), self.ret + ) def test_present_non_existing_pass(self): - ''' + """ Test present, language not present in database - pass - ''' - with patch.dict(postgres_language.__salt__, - {'postgres.language_list': self.mock_empty_language_list, - 'postgres.language_create': self.mock_true}): - with patch.dict(postgres_language.__opts__, {'test': True}): - comt = 'Language {0} is set to be installed'.format(self.name) - self.ret.update({'comment': comt, 'result': None}) + """ + with patch.dict( + postgres_language.__salt__, + { + "postgres.language_list": self.mock_empty_language_list, + "postgres.language_create": self.mock_true, + }, + ): + with patch.dict(postgres_language.__opts__, {"test": True}): + comt = "Language {0} is set to be installed".format(self.name) + self.ret.update({"comment": comt, "result": None}) self.assertDictEqual( - postgres_language.present(self.name, 'testdb'), self.ret) + postgres_language.present(self.name, "testdb"), self.ret + ) - with patch.dict(postgres_language.__opts__, {'test': False}): - comt = 'Language {0} has been installed'.format(self.name) - self.ret.update({'comment': comt, - 'result': True, - 'changes': {'plpgsql': 'Present'}}) + with patch.dict(postgres_language.__opts__, {"test": False}): + comt = "Language {0} has been installed".format(self.name) + self.ret.update( + {"comment": comt, "result": True, "changes": {"plpgsql": "Present"}} + ) self.assertDictEqual( - postgres_language.present(self.name, 'testdb'), self.ret) + postgres_language.present(self.name, "testdb"), self.ret + ) def test_present_non_existing_fail(self): - ''' + """ Test present, language not present in database - fail - ''' - with patch.dict(postgres_language.__salt__, - {'postgres.language_list': self.mock_empty_language_list, - 'postgres.language_create': self.mock_false}): - with patch.dict(postgres_language.__opts__, {'test': True}): - comt = 'Language {0} is set to be installed'.format(self.name) - self.ret.update({'comment': comt, 'result': None}) + """ + with patch.dict( + postgres_language.__salt__, + { + "postgres.language_list": self.mock_empty_language_list, + "postgres.language_create": self.mock_false, + }, + ): + with patch.dict(postgres_language.__opts__, {"test": True}): + comt = "Language {0} is set to be installed".format(self.name) + self.ret.update({"comment": comt, "result": None}) self.assertDictEqual( - postgres_language.present(self.name, 'testdb'), self.ret) + postgres_language.present(self.name, "testdb"), self.ret + ) - with patch.dict(postgres_language.__opts__, {'test': False}): - comt = 'Failed to install language {0}'.format(self.name) - self.ret.update({'comment': comt, 'result': False}) + with patch.dict(postgres_language.__opts__, {"test": False}): + comt = "Failed to install language {0}".format(self.name) + self.ret.update({"comment": comt, "result": False}) self.assertDictEqual( - postgres_language.present(self.name, 'testdb'), self.ret) + postgres_language.present(self.name, "testdb"), self.ret + ) def test_absent_existing(self): - ''' + """ Test absent, language present in database - ''' - with patch.dict(postgres_language.__salt__, - {'postgres.language_exists': self.mock_true, - 'postgres.language_remove': self.mock_true}): - with patch.dict(postgres_language.__opts__, {'test': True}): - comt = 'Language {0} is set to be removed'.format(self.name) - self.ret.update({'comment': comt, 'result': None}) + """ + with patch.dict( + postgres_language.__salt__, + { + "postgres.language_exists": self.mock_true, + "postgres.language_remove": self.mock_true, + }, + ): + with patch.dict(postgres_language.__opts__, {"test": True}): + comt = "Language {0} is set to be removed".format(self.name) + self.ret.update({"comment": comt, "result": None}) self.assertDictEqual( - postgres_language.absent(self.name, 'testdb'), self.ret) + postgres_language.absent(self.name, "testdb"), self.ret + ) - with patch.dict(postgres_language.__opts__, {'test': False}): - comt = 'Language {0} has been removed'.format(self.name) - self.ret.update({'comment': comt, - 'result': True, - 'changes': {'plpgsql': 'Absent'}}) + with patch.dict(postgres_language.__opts__, {"test": False}): + comt = "Language {0} has been removed".format(self.name) + self.ret.update( + {"comment": comt, "result": True, "changes": {"plpgsql": "Absent"}} + ) self.assertDictEqual( - postgres_language.absent(self.name, 'testdb'), self.ret) + postgres_language.absent(self.name, "testdb"), self.ret + ) def test_absent_non_existing(self): - ''' + """ Test absent, language not present in database - ''' - with patch.dict(postgres_language.__salt__, - {'postgres.language_exists': self.mock_false}): - with patch.dict(postgres_language.__opts__, {'test': True}): - comt = 'Language {0} is not present so ' \ - 'it cannot be removed'.format(self.name) - self.ret.update({'comment': comt, 'result': True}) + """ + with patch.dict( + postgres_language.__salt__, {"postgres.language_exists": self.mock_false} + ): + with patch.dict(postgres_language.__opts__, {"test": True}): + comt = "Language {0} is not present so " "it cannot be removed".format( + self.name + ) + self.ret.update({"comment": comt, "result": True}) self.assertDictEqual( - postgres_language.absent(self.name, 'testdb'), self.ret) + postgres_language.absent(self.name, "testdb"), self.ret + ) diff --git a/tests/unit/states/test_postgres_privileges.py b/tests/unit/states/test_postgres_privileges.py index 372ffed20db..eafb08b1ac0 100644 --- a/tests/unit/states/test_postgres_privileges.py +++ b/tests/unit/states/test_postgres_privileges.py @@ -1,38 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Andrew Colin Kissa <andrew@topdog.za.net> -''' -from __future__ import absolute_import, unicode_literals, print_function - -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +""" +from __future__ import absolute_import, print_function, unicode_literals import salt.states.postgres_privileges as postgres_privileges +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class PostgresPrivilegesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.postgres_privileges - ''' + """ def setup_loader_modules(self): return {postgres_privileges: {}} def setUp(self): - ''' + """ Setup data for the tests - ''' - self.table_name = 'awl' - self.group_name = 'admins' - self.name = 'baruwa' - self.ret = {'name': self.name, - 'changes': {}, - 'result': False, - 'comment': ''} + """ + self.table_name = "awl" + self.group_name = "admins" + self.name = "baruwa" + self.ret = {"name": self.name, "changes": {}, "result": False, "comment": ""} self.mock_true = MagicMock(return_value=True) self.mock_false = MagicMock(return_value=False) @@ -42,129 +35,162 @@ class PostgresPrivilegesTestCase(TestCase, LoaderModuleMockMixin): del self.mock_false def test_present_table(self): - ''' + """ Test present - ''' - with patch.dict(postgres_privileges.__salt__, - {'postgres.has_privileges': self.mock_true}): - comt = 'The requested privilege(s) are already set' - self.ret.update({'comment': comt, 'result': True}) + """ + with patch.dict( + postgres_privileges.__salt__, {"postgres.has_privileges": self.mock_true} + ): + comt = "The requested privilege(s) are already set" + self.ret.update({"comment": comt, "result": True}) self.assertDictEqual( - postgres_privileges.present( - self.name, - self.table_name, - 'table'), - self.ret) + postgres_privileges.present(self.name, self.table_name, "table"), + self.ret, + ) - with patch.dict(postgres_privileges.__salt__, - {'postgres.has_privileges': self.mock_false, - 'postgres.privileges_grant': self.mock_true}): - with patch.dict(postgres_privileges.__opts__, {'test': True}): - comt = ('The privilege(s): {0} are' - ' set to be granted to {1}').format('ALL', self.name) - self.ret.update({'comment': comt, 'result': None}) + with patch.dict( + postgres_privileges.__salt__, + { + "postgres.has_privileges": self.mock_false, + "postgres.privileges_grant": self.mock_true, + }, + ): + with patch.dict(postgres_privileges.__opts__, {"test": True}): + comt = ("The privilege(s): {0} are" " set to be granted to {1}").format( + "ALL", self.name + ) + self.ret.update({"comment": comt, "result": None}) self.assertDictEqual( - postgres_privileges.present(self.name, - self.table_name, 'table', privileges=['ALL']), self.ret) + postgres_privileges.present( + self.name, self.table_name, "table", privileges=["ALL"] + ), + self.ret, + ) - with patch.dict(postgres_privileges.__opts__, {'test': False}): - comt = ('The privilege(s): {0} have ' - 'been granted to {1}').format('ALL', self.name) - self.ret.update({'comment': comt, - 'result': True, - 'changes': {'baruwa': 'Present'}}) + with patch.dict(postgres_privileges.__opts__, {"test": False}): + comt = ("The privilege(s): {0} have " "been granted to {1}").format( + "ALL", self.name + ) + self.ret.update( + {"comment": comt, "result": True, "changes": {"baruwa": "Present"}} + ) self.assertDictEqual( - postgres_privileges.present(self.name, - self.table_name, 'table', privileges=['ALL']), self.ret) + postgres_privileges.present( + self.name, self.table_name, "table", privileges=["ALL"] + ), + self.ret, + ) def test_present_group(self): - ''' + """ Test present group - ''' - with patch.dict(postgres_privileges.__salt__, - {'postgres.has_privileges': self.mock_false, - 'postgres.privileges_grant': self.mock_true}): - with patch.dict(postgres_privileges.__opts__, {'test': True}): - comt = ('The privilege(s): {0} are' - ' set to be granted to {1}').format(self.group_name, - self.name) - self.ret.update({'comment': comt, 'result': None}) + """ + with patch.dict( + postgres_privileges.__salt__, + { + "postgres.has_privileges": self.mock_false, + "postgres.privileges_grant": self.mock_true, + }, + ): + with patch.dict(postgres_privileges.__opts__, {"test": True}): + comt = ("The privilege(s): {0} are" " set to be granted to {1}").format( + self.group_name, self.name + ) + self.ret.update({"comment": comt, "result": None}) self.assertDictEqual( - postgres_privileges.present(self.name, - self.group_name, 'group'), self.ret) + postgres_privileges.present(self.name, self.group_name, "group"), + self.ret, + ) - with patch.dict(postgres_privileges.__opts__, {'test': False}): - comt = ('The privilege(s): {0} have ' - 'been granted to {1}').format(self.group_name, - self.name) - self.ret.update({'comment': comt, - 'result': True, - 'changes': {'baruwa': 'Present'}}) + with patch.dict(postgres_privileges.__opts__, {"test": False}): + comt = ("The privilege(s): {0} have " "been granted to {1}").format( + self.group_name, self.name + ) + self.ret.update( + {"comment": comt, "result": True, "changes": {"baruwa": "Present"}} + ) self.assertDictEqual( - postgres_privileges.present(self.name, - self.group_name, 'group'), self.ret) + postgres_privileges.present(self.name, self.group_name, "group"), + self.ret, + ) def test_absent_table(self): - ''' + """ Test absent - ''' - with patch.dict(postgres_privileges.__salt__, - {'postgres.has_privileges': self.mock_false}): - with patch.dict(postgres_privileges.__opts__, {'test': True}): - comt = ('The requested privilege(s)' - ' are not set so cannot be revoked') - self.ret.update({'comment': comt, 'result': True}) + """ + with patch.dict( + postgres_privileges.__salt__, {"postgres.has_privileges": self.mock_false} + ): + with patch.dict(postgres_privileges.__opts__, {"test": True}): + comt = "The requested privilege(s)" " are not set so cannot be revoked" + self.ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + postgres_privileges.absent(self.name, self.table_name, "table"), + self.ret, + ) + + with patch.dict( + postgres_privileges.__salt__, + { + "postgres.has_privileges": self.mock_true, + "postgres.privileges_revoke": self.mock_true, + }, + ): + with patch.dict(postgres_privileges.__opts__, {"test": True}): + comt = ( + "The privilege(s): {0} are" " set to be revoked from {1}" + ).format("ALL", self.name) + self.ret.update({"comment": comt, "result": None}) self.assertDictEqual( postgres_privileges.absent( - self.name, - self.table_name, - 'table'), - self.ret) + self.name, self.table_name, "table", privileges=["ALL"] + ), + self.ret, + ) - with patch.dict(postgres_privileges.__salt__, - {'postgres.has_privileges': self.mock_true, - 'postgres.privileges_revoke': self.mock_true}): - with patch.dict(postgres_privileges.__opts__, {'test': True}): - comt = ('The privilege(s): {0} are' - ' set to be revoked from {1}').format('ALL', self.name) - self.ret.update({'comment': comt, 'result': None}) + with patch.dict(postgres_privileges.__opts__, {"test": False}): + comt = ("The privilege(s): {0} have " "been revoked from {1}").format( + "ALL", self.name + ) + self.ret.update( + {"comment": comt, "result": True, "changes": {"baruwa": "Absent"}} + ) self.assertDictEqual( - postgres_privileges.absent(self.name, - self.table_name, 'table', privileges=['ALL']), self.ret) - - with patch.dict(postgres_privileges.__opts__, {'test': False}): - comt = ('The privilege(s): {0} have ' - 'been revoked from {1}').format('ALL', self.name) - self.ret.update({'comment': comt, - 'result': True, - 'changes': {'baruwa': 'Absent'}}) - self.assertDictEqual( - postgres_privileges.absent(self.name, - self.table_name, 'table', privileges=['ALL']), self.ret) + postgres_privileges.absent( + self.name, self.table_name, "table", privileges=["ALL"] + ), + self.ret, + ) def test_absent_group(self): - ''' + """ Test absent group - ''' - with patch.dict(postgres_privileges.__salt__, - {'postgres.has_privileges': self.mock_true, - 'postgres.privileges_revoke': self.mock_true}): - with patch.dict(postgres_privileges.__opts__, {'test': True}): - comt = ('The privilege(s): {0} are' - ' set to be revoked from {1}').format(self.group_name, - self.name) - self.ret.update({'comment': comt, 'result': None}) + """ + with patch.dict( + postgres_privileges.__salt__, + { + "postgres.has_privileges": self.mock_true, + "postgres.privileges_revoke": self.mock_true, + }, + ): + with patch.dict(postgres_privileges.__opts__, {"test": True}): + comt = ( + "The privilege(s): {0} are" " set to be revoked from {1}" + ).format(self.group_name, self.name) + self.ret.update({"comment": comt, "result": None}) self.assertDictEqual( - postgres_privileges.absent(self.name, - self.group_name, 'group'), self.ret) + postgres_privileges.absent(self.name, self.group_name, "group"), + self.ret, + ) - with patch.dict(postgres_privileges.__opts__, {'test': False}): - comt = ('The privilege(s): {0} have ' - 'been revoked from {1}').format(self.group_name, - self.name) - self.ret.update({'comment': comt, - 'result': True, - 'changes': {'baruwa': 'Absent'}}) + with patch.dict(postgres_privileges.__opts__, {"test": False}): + comt = ("The privilege(s): {0} have " "been revoked from {1}").format( + self.group_name, self.name + ) + self.ret.update( + {"comment": comt, "result": True, "changes": {"baruwa": "Absent"}} + ) self.assertDictEqual( - postgres_privileges.absent(self.name, - self.group_name, 'group'), self.ret) + postgres_privileges.absent(self.name, self.group_name, "group"), + self.ret, + ) diff --git a/tests/unit/states/test_postgres_schema.py b/tests/unit/states/test_postgres_schema.py index 8e6b1a38903..708760d58f3 100644 --- a/tests/unit/states/test_postgres_schema.py +++ b/tests/unit/states/test_postgres_schema.py @@ -1,91 +1,97 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.postgres_schema as postgres_schema +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PostgresSchemaTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.postgres_schema - ''' + """ + def setup_loader_modules(self): return {postgres_schema: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named schema is present in the database. - ''' - name = 'myname' - dbname = 'mydb' + """ + name = "myname" + dbname = "mydb" - ret = {'name': name, - 'dbname': dbname, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = { + "name": name, + "dbname": dbname, + "changes": {}, + "result": True, + "comment": "", + } mock = MagicMock(return_value=name) - with patch.dict(postgres_schema.__salt__, - {'postgres.schema_get': mock}): - with patch.dict(postgres_schema.__opts__, {'test': False}): - comt = ('Schema {0} already exists in database {1}'.format(name, - dbname)) - ret.update({'comment': comt}) + with patch.dict(postgres_schema.__salt__, {"postgres.schema_get": mock}): + with patch.dict(postgres_schema.__opts__, {"test": False}): + comt = "Schema {0} already exists in database {1}".format(name, dbname) + ret.update({"comment": comt}) self.assertDictEqual(postgres_schema.present(dbname, name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named schema is absent. - ''' - name = 'myname' - dbname = 'mydb' + """ + name = "myname" + dbname = "mydb" - ret = {'name': name, - 'dbname': dbname, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = { + "name": name, + "dbname": dbname, + "changes": {}, + "result": True, + "comment": "", + } mock_t = MagicMock(side_effect=[True, False]) mock = MagicMock(side_effect=[True, True, True, False]) - with patch.dict(postgres_schema.__salt__, - {'postgres.schema_exists': mock, - 'postgres.schema_remove': mock_t}): - with patch.dict(postgres_schema.__opts__, {'test': True}): - comt = ('Schema {0} is set to be removed from database {1}'. - format(name, dbname)) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + postgres_schema.__salt__, + {"postgres.schema_exists": mock, "postgres.schema_remove": mock_t}, + ): + with patch.dict(postgres_schema.__opts__, {"test": True}): + comt = "Schema {0} is set to be removed from database {1}".format( + name, dbname + ) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(postgres_schema.absent(dbname, name), ret) - with patch.dict(postgres_schema.__opts__, {'test': False}): - comt = ('Schema {0} has been removed from database {1}'. - format(name, dbname)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Absent'}}) + with patch.dict(postgres_schema.__opts__, {"test": False}): + comt = "Schema {0} has been removed from database {1}".format( + name, dbname + ) + ret.update( + {"comment": comt, "result": True, "changes": {name: "Absent"}} + ) self.assertDictEqual(postgres_schema.absent(dbname, name), ret) - comt = ('Schema {0} failed to be removed'.format(name)) - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "Schema {0} failed to be removed".format(name) + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(postgres_schema.absent(dbname, name), ret) - comt = ('Schema {0} is not present in database {1},' - ' so it cannot be removed'.format(name, dbname)) - ret.update({'comment': comt, 'result': True}) + comt = ( + "Schema {0} is not present in database {1}," + " so it cannot be removed".format(name, dbname) + ) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(postgres_schema.absent(dbname, name), ret) diff --git a/tests/unit/states/test_postgres_user.py b/tests/unit/states/test_postgres_user.py index 3c636ac4c55..471bdbd4686 100644 --- a/tests/unit/states/test_postgres_user.py +++ b/tests/unit/states/test_postgres_user.py @@ -1,89 +1,84 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.postgres_user as postgres_user +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PostgresUserTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.postgres_user - ''' + """ + def setup_loader_modules(self): return {postgres_user: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named user is present with the specified privileges. - ''' - name = 'frank' + """ + name = "frank" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_t = MagicMock(return_value=True) mock = MagicMock(return_value=None) - with patch.dict(postgres_user.__salt__, - {'postgres.role_get': mock, - 'postgres.user_create': mock_t}): - with patch.dict(postgres_user.__opts__, {'test': True}): - comt = ('User {0} is set to be created'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + postgres_user.__salt__, + {"postgres.role_get": mock, "postgres.user_create": mock_t}, + ): + with patch.dict(postgres_user.__opts__, {"test": True}): + comt = "User {0} is set to be created".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(postgres_user.present(name), ret) - with patch.dict(postgres_user.__opts__, {'test': False}): - comt = ('The user {0} has been created'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Present'}}) + with patch.dict(postgres_user.__opts__, {"test": False}): + comt = "The user {0} has been created".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {name: "Present"}} + ) self.assertDictEqual(postgres_user.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure that the named user is absent. - ''' - name = 'frank' + """ + name = "frank" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_t = MagicMock(return_value=True) mock = MagicMock(side_effect=[True, True, False]) - with patch.dict(postgres_user.__salt__, - {'postgres.user_exists': mock, - 'postgres.user_remove': mock_t}): - with patch.dict(postgres_user.__opts__, {'test': True}): - comt = ('User {0} is set to be removed'.format(name)) - ret.update({'comment': comt, 'result': None}) + with patch.dict( + postgres_user.__salt__, + {"postgres.user_exists": mock, "postgres.user_remove": mock_t}, + ): + with patch.dict(postgres_user.__opts__, {"test": True}): + comt = "User {0} is set to be removed".format(name) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(postgres_user.absent(name), ret) - with patch.dict(postgres_user.__opts__, {'test': False}): - comt = ('User {0} has been removed'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Absent'}}) + with patch.dict(postgres_user.__opts__, {"test": False}): + comt = "User {0} has been removed".format(name) + ret.update( + {"comment": comt, "result": True, "changes": {name: "Absent"}} + ) self.assertDictEqual(postgres_user.absent(name), ret) - comt = ('User {0} is not present, so it cannot be removed' - .format(name)) - ret.update({'comment': comt, 'result': True, 'changes': {}}) + comt = "User {0} is not present, so it cannot be removed".format(name) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(postgres_user.absent(name), ret) diff --git a/tests/unit/states/test_powerpath.py b/tests/unit/states/test_powerpath.py index 584041a866e..373c5106d05 100644 --- a/tests/unit/states/test_powerpath.py +++ b/tests/unit/states/test_powerpath.py @@ -1,111 +1,121 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.powerpath as powerpath +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PowerpathTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.powerpath - ''' + """ + def setup_loader_modules(self): return {powerpath: {}} # 'license_present' function tests: 1 def test_license_present(self): - ''' + """ Test to ensures that the specified PowerPath license key is present on the host. - ''' - name = 'mylic' + """ + name = "mylic" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - mock_t = MagicMock(side_effect=[{'result': True, 'output': name}, - {'result': False, 'output': name}]) + mock_t = MagicMock( + side_effect=[ + {"result": True, "output": name}, + {"result": False, "output": name}, + ] + ) mock = MagicMock(side_effect=[False, True, True, True, True]) - mock_l = MagicMock(return_value=[{'key': name}]) - with patch.dict(powerpath.__salt__, - {'powerpath.has_powerpath': mock, - 'powerpath.list_licenses': mock_l, - 'powerpath.add_license': mock_t}): - comt = ('PowerPath is not installed.') - ret.update({'comment': comt, 'result': False}) + mock_l = MagicMock(return_value=[{"key": name}]) + with patch.dict( + powerpath.__salt__, + { + "powerpath.has_powerpath": mock, + "powerpath.list_licenses": mock_l, + "powerpath.add_license": mock_t, + }, + ): + comt = "PowerPath is not installed." + ret.update({"comment": comt, "result": False}) self.assertDictEqual(powerpath.license_present(name), ret) - comt = ('License key {0} already present'.format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "License key {0} already present".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(powerpath.license_present(name), ret) - with patch.dict(powerpath.__opts__, {'test': True}): - comt = ('License key Mylic is set to be added') - ret.update({'comment': comt, 'result': None, 'name': 'Mylic'}) - self.assertDictEqual(powerpath.license_present('Mylic'), ret) + with patch.dict(powerpath.__opts__, {"test": True}): + comt = "License key Mylic is set to be added" + ret.update({"comment": comt, "result": None, "name": "Mylic"}) + self.assertDictEqual(powerpath.license_present("Mylic"), ret) - with patch.dict(powerpath.__opts__, {'test': False}): - ret.update({'comment': name, 'result': True, - 'changes': {'Mylic': 'added'}}) - self.assertDictEqual(powerpath.license_present('Mylic'), ret) + with patch.dict(powerpath.__opts__, {"test": False}): + ret.update( + {"comment": name, "result": True, "changes": {"Mylic": "added"}} + ) + self.assertDictEqual(powerpath.license_present("Mylic"), ret) - ret.update({'result': False, 'changes': {}}) - self.assertDictEqual(powerpath.license_present('Mylic'), ret) + ret.update({"result": False, "changes": {}}) + self.assertDictEqual(powerpath.license_present("Mylic"), ret) # 'license_absent' function tests: 1 def test_license_absent(self): - ''' + """ Test to ensures that the specified PowerPath license key is absent on the host. - ''' - name = 'mylic' + """ + name = "mylic" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - mock_t = MagicMock(side_effect=[{'result': True, 'output': name}, - {'result': False, 'output': name}]) + mock_t = MagicMock( + side_effect=[ + {"result": True, "output": name}, + {"result": False, "output": name}, + ] + ) mock = MagicMock(side_effect=[False, True, True, True, True]) - mock_l = MagicMock(return_value=[{'key': 'salt'}]) - with patch.dict(powerpath.__salt__, - {'powerpath.has_powerpath': mock, - 'powerpath.list_licenses': mock_l, - 'powerpath.remove_license': mock_t}): - comt = ('PowerPath is not installed.') - ret.update({'comment': comt, 'result': False}) + mock_l = MagicMock(return_value=[{"key": "salt"}]) + with patch.dict( + powerpath.__salt__, + { + "powerpath.has_powerpath": mock, + "powerpath.list_licenses": mock_l, + "powerpath.remove_license": mock_t, + }, + ): + comt = "PowerPath is not installed." + ret.update({"comment": comt, "result": False}) self.assertDictEqual(powerpath.license_absent(name), ret) - comt = ('License key {0} not present'.format(name)) - ret.update({'comment': comt, 'result': True}) + comt = "License key {0} not present".format(name) + ret.update({"comment": comt, "result": True}) self.assertDictEqual(powerpath.license_absent(name), ret) - with patch.dict(powerpath.__opts__, {'test': True}): - comt = ('License key salt is set to be removed') - ret.update({'comment': comt, 'result': None, 'name': 'salt'}) - self.assertDictEqual(powerpath.license_absent('salt'), ret) + with patch.dict(powerpath.__opts__, {"test": True}): + comt = "License key salt is set to be removed" + ret.update({"comment": comt, "result": None, "name": "salt"}) + self.assertDictEqual(powerpath.license_absent("salt"), ret) - with patch.dict(powerpath.__opts__, {'test': False}): - ret.update({'comment': name, 'result': True, - 'changes': {'salt': 'removed'}}) - self.assertDictEqual(powerpath.license_absent('salt'), ret) + with patch.dict(powerpath.__opts__, {"test": False}): + ret.update( + {"comment": name, "result": True, "changes": {"salt": "removed"}} + ) + self.assertDictEqual(powerpath.license_absent("salt"), ret) - ret.update({'result': False, 'changes': {}}) - self.assertDictEqual(powerpath.license_absent('salt'), ret) + ret.update({"result": False, "changes": {}}) + self.assertDictEqual(powerpath.license_absent("salt"), ret) diff --git a/tests/unit/states/test_process.py b/tests/unit/states/test_process.py index 326dfd627ee..fc7fadecb17 100644 --- a/tests/unit/states/test_process.py +++ b/tests/unit/states/test_process.py @@ -1,50 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.process as process +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ProcessTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.process - ''' + """ + def setup_loader_modules(self): return {process: {}} # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensures that the named command is not running. - ''' - name = 'apache2' + """ + name = "apache2" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - mock = MagicMock(return_value='') - with patch.dict(process.__salt__, {'ps.pgrep': mock, - 'ps.pkill': mock}): - with patch.dict(process.__opts__, {'test': True}): - comt = ('No matching processes running') - ret.update({'comment': comt}) + mock = MagicMock(return_value="") + with patch.dict(process.__salt__, {"ps.pgrep": mock, "ps.pkill": mock}): + with patch.dict(process.__opts__, {"test": True}): + comt = "No matching processes running" + ret.update({"comment": comt}) self.assertDictEqual(process.absent(name), ret) - with patch.dict(process.__opts__, {'test': False}): - ret.update({'result': True}) + with patch.dict(process.__opts__, {"test": False}): + ret.update({"result": True}) self.assertDictEqual(process.absent(name), ret) diff --git a/tests/unit/states/test_proxy.py b/tests/unit/states/test_proxy.py index 3ef1484889d..057de781a4a 100644 --- a/tests/unit/states/test_proxy.py +++ b/tests/unit/states/test_proxy.py @@ -1,186 +1,207 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, - call -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.proxy as proxy +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, patch +from tests.support.unit import TestCase + class ProxyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the proxy state - ''' + """ + def setup_loader_modules(self): return {proxy: {}} def test_set_proxy_macos(self): - ''' + """ Test to make sure we can set the proxy settings on macOS - ''' - with patch.dict(proxy.__grains__, {'os': 'Darwin'}): - expected = {'changes': { - 'new': [ - {'port': '3128', - 'server': '192.168.0.1', - 'service': 'http', - 'user': 'frank'}, - {'port': '3128', - 'server': '192.168.0.1', - 'service': 'https', - 'user': 'frank'}, - {'port': '3128', - 'server': '192.168.0.1', - 'service': 'ftp', - 'user': 'frank'}, - {'bypass_domains': ['salt.com', 'test.com']}] + """ + with patch.dict(proxy.__grains__, {"os": "Darwin"}): + expected = { + "changes": { + "new": [ + { + "port": "3128", + "server": "192.168.0.1", + "service": "http", + "user": "frank", + }, + { + "port": "3128", + "server": "192.168.0.1", + "service": "https", + "user": "frank", + }, + { + "port": "3128", + "server": "192.168.0.1", + "service": "ftp", + "user": "frank", + }, + {"bypass_domains": ["salt.com", "test.com"]}, + ] }, - 'comment': 'http proxy settings updated correctly\nhttps proxy settings updated correctly\nftp proxy ' - 'settings updated correctly\nProxy bypass domains updated correctly\n', - 'name': '192.168.0.1', - 'result': True} + "comment": "http proxy settings updated correctly\nhttps proxy settings updated correctly\nftp proxy " + "settings updated correctly\nProxy bypass domains updated correctly\n", + "name": "192.168.0.1", + "result": True, + } set_proxy_mock = MagicMock(return_value=True) patches = { - 'proxy.get_http_proxy': MagicMock(return_value={}), - 'proxy.get_https_proxy': MagicMock(return_value={}), - 'proxy.get_ftp_proxy': MagicMock(return_value={}), - 'proxy.get_proxy_bypass': MagicMock(return_value=[]), - 'proxy.set_http_proxy': set_proxy_mock, - 'proxy.set_https_proxy': set_proxy_mock, - 'proxy.set_ftp_proxy': set_proxy_mock, - 'proxy.set_proxy_bypass': set_proxy_mock, + "proxy.get_http_proxy": MagicMock(return_value={}), + "proxy.get_https_proxy": MagicMock(return_value={}), + "proxy.get_ftp_proxy": MagicMock(return_value={}), + "proxy.get_proxy_bypass": MagicMock(return_value=[]), + "proxy.set_http_proxy": set_proxy_mock, + "proxy.set_https_proxy": set_proxy_mock, + "proxy.set_ftp_proxy": set_proxy_mock, + "proxy.set_proxy_bypass": set_proxy_mock, } with patch.dict(proxy.__salt__, patches): - out = proxy.managed('192.168.0.1', '3128', user='frank', password='passw0rd', - bypass_domains=['salt.com', 'test.com']) - out['changes']['new'][-1]['bypass_domains'] = sorted(out['changes']['new'][-1]['bypass_domains']) + out = proxy.managed( + "192.168.0.1", + "3128", + user="frank", + password="passw0rd", + bypass_domains=["salt.com", "test.com"], + ) + out["changes"]["new"][-1]["bypass_domains"] = sorted( + out["changes"]["new"][-1]["bypass_domains"] + ) calls = [ - call('192.168.0.1', '3128', 'frank', 'passw0rd', 'Ethernet'), - call('192.168.0.1', '3128', 'frank', 'passw0rd', 'Ethernet'), - call('192.168.0.1', '3128', 'frank', 'passw0rd', 'Ethernet'), - call(['salt.com', 'test.com'], 'Ethernet') + call("192.168.0.1", "3128", "frank", "passw0rd", "Ethernet"), + call("192.168.0.1", "3128", "frank", "passw0rd", "Ethernet"), + call("192.168.0.1", "3128", "frank", "passw0rd", "Ethernet"), + call(["salt.com", "test.com"], "Ethernet"), ] set_proxy_mock.assert_has_calls(calls) self.assertEqual(out, expected) def test_set_proxy_macos_same(self): - ''' + """ Test to make sure we can set the proxy settings on macOS - ''' - with patch.dict(proxy.__grains__, {'os': 'Darwin'}): - expected = {'changes': { - }, - 'comment': 'http proxy settings already set.\nhttps proxy settings already set.\nftp proxy settings' - ' already set.\nProxy bypass domains are already set correctly.\n', - 'name': '192.168.0.1', - 'result': True} - - proxy_val = { - 'enabled': True, - 'server': '192.168.0.1', - 'port': '3128' + """ + with patch.dict(proxy.__grains__, {"os": "Darwin"}): + expected = { + "changes": {}, + "comment": "http proxy settings already set.\nhttps proxy settings already set.\nftp proxy settings" + " already set.\nProxy bypass domains are already set correctly.\n", + "name": "192.168.0.1", + "result": True, } + proxy_val = {"enabled": True, "server": "192.168.0.1", "port": "3128"} + set_proxy_mock = MagicMock() patches = { - 'proxy.get_http_proxy': MagicMock(return_value=proxy_val), - 'proxy.get_https_proxy': MagicMock(return_value=proxy_val), - 'proxy.get_ftp_proxy': MagicMock(return_value=proxy_val), - 'proxy.get_proxy_bypass': MagicMock(return_value=['test.com', 'salt.com']), - 'proxy.set_http_proxy': set_proxy_mock, - 'proxy.set_https_proxy': set_proxy_mock, - 'proxy.set_ftp_proxy': set_proxy_mock, - 'proxy.set_proxy_bypass': set_proxy_mock, + "proxy.get_http_proxy": MagicMock(return_value=proxy_val), + "proxy.get_https_proxy": MagicMock(return_value=proxy_val), + "proxy.get_ftp_proxy": MagicMock(return_value=proxy_val), + "proxy.get_proxy_bypass": MagicMock( + return_value=["test.com", "salt.com"] + ), + "proxy.set_http_proxy": set_proxy_mock, + "proxy.set_https_proxy": set_proxy_mock, + "proxy.set_ftp_proxy": set_proxy_mock, + "proxy.set_proxy_bypass": set_proxy_mock, } with patch.dict(proxy.__salt__, patches): - out = proxy.managed('192.168.0.1', '3128', user='frank', password='passw0rd', - bypass_domains=['salt.com', 'test.com']) + out = proxy.managed( + "192.168.0.1", + "3128", + user="frank", + password="passw0rd", + bypass_domains=["salt.com", "test.com"], + ) assert not set_proxy_mock.called self.assertEqual(out, expected) def test_set_proxy_windows(self): - ''' + """ Test to make sure we can set the proxy settings on Windows - ''' - with patch.dict(proxy.__grains__, {'os': 'Windows'}): + """ + with patch.dict(proxy.__grains__, {"os": "Windows"}): expected = { - 'changes': {}, - 'comment': 'Proxy settings updated correctly', - 'name': '192.168.0.1', - 'result': True + "changes": {}, + "comment": "Proxy settings updated correctly", + "name": "192.168.0.1", + "result": True, } set_proxy_mock = MagicMock(return_value=True) patches = { - 'proxy.get_proxy_win': MagicMock(return_value={}), - 'proxy.get_proxy_bypass': MagicMock(return_value=[]), - 'proxy.set_proxy_win': set_proxy_mock, + "proxy.get_proxy_win": MagicMock(return_value={}), + "proxy.get_proxy_bypass": MagicMock(return_value=[]), + "proxy.set_proxy_win": set_proxy_mock, } with patch.dict(proxy.__salt__, patches): - out = proxy.managed('192.168.0.1', '3128', user='frank', password='passw0rd', - bypass_domains=['salt.com', 'test.com']) + out = proxy.managed( + "192.168.0.1", + "3128", + user="frank", + password="passw0rd", + bypass_domains=["salt.com", "test.com"], + ) - set_proxy_mock.assert_called_once_with('192.168.0.1', '3128', ['http', 'https', 'ftp'], - ['salt.com', 'test.com']) + set_proxy_mock.assert_called_once_with( + "192.168.0.1", + "3128", + ["http", "https", "ftp"], + ["salt.com", "test.com"], + ) self.assertEqual(out, expected) def test_set_proxy_windows_same(self): - ''' + """ Test to make sure we can set the proxy settings on Windows - ''' - with patch.dict(proxy.__grains__, {'os': 'Windows'}): + """ + with patch.dict(proxy.__grains__, {"os": "Windows"}): expected = { - 'changes': {}, - 'comment': 'Proxy settings already correct.', - 'name': '192.168.0.1', - 'result': True + "changes": {}, + "comment": "Proxy settings already correct.", + "name": "192.168.0.1", + "result": True, } proxy_val = { - 'enabled': True, - 'http': { - 'enabled': True, - 'server': '192.168.0.1', - 'port': '3128' - }, - 'https': { - 'enabled': True, - 'server': '192.168.0.1', - 'port': '3128' - }, - 'ftp': { - 'enabled': True, - 'server': '192.168.0.1', - 'port': '3128' - } + "enabled": True, + "http": {"enabled": True, "server": "192.168.0.1", "port": "3128"}, + "https": {"enabled": True, "server": "192.168.0.1", "port": "3128"}, + "ftp": {"enabled": True, "server": "192.168.0.1", "port": "3128"}, } set_proxy_mock = MagicMock(return_value=True) patches = { - 'proxy.get_proxy_win': MagicMock(return_value=proxy_val), - 'proxy.get_proxy_bypass': MagicMock(return_value=['salt.com', 'test.com']), - 'proxy.set_proxy_win': set_proxy_mock, + "proxy.get_proxy_win": MagicMock(return_value=proxy_val), + "proxy.get_proxy_bypass": MagicMock( + return_value=["salt.com", "test.com"] + ), + "proxy.set_proxy_win": set_proxy_mock, } with patch.dict(proxy.__salt__, patches): - out = proxy.managed('192.168.0.1', '3128', user='frank', password='passw0rd', - bypass_domains=['salt.com', 'test.com']) + out = proxy.managed( + "192.168.0.1", + "3128", + user="frank", + password="passw0rd", + bypass_domains=["salt.com", "test.com"], + ) assert not set_proxy_mock.called self.assertEqual(out, expected) diff --git a/tests/unit/states/test_pyenv.py b/tests/unit/states/test_pyenv.py index e6392fe4aaf..08926deb806 100644 --- a/tests/unit/states/test_pyenv.py +++ b/tests/unit/states/test_pyenv.py @@ -1,62 +1,62 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.pyenv as pyenv +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PyenvTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.pyenv - ''' + """ + def setup_loader_modules(self): return {pyenv: {}} # 'installed' function tests: 1 def test_installed(self): - ''' + """ Test to verify that the specified python is installed with pyenv. - ''' - name = 'python-2.7.6' + """ + name = "python-2.7.6" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - with patch.dict(pyenv.__opts__, {'test': True}): - comt = ('python 2.7.6 is set to be installed') - ret.update({'comment': comt}) + with patch.dict(pyenv.__opts__, {"test": True}): + comt = "python 2.7.6 is set to be installed" + ret.update({"comment": comt}) self.assertDictEqual(pyenv.installed(name), ret) - with patch.dict(pyenv.__opts__, {'test': False}): + with patch.dict(pyenv.__opts__, {"test": False}): mock_f = MagicMock(side_effect=[False, False, True]) mock_fa = MagicMock(side_effect=[False, True]) - mock_str = MagicMock(return_value='2.7.6') - mock_lst = MagicMock(return_value=['2.7.6']) - with patch.dict(pyenv.__salt__, {'pyenv.is_installed': mock_f, - 'pyenv.install': mock_fa, - 'pyenv.default': mock_str, - 'pyenv.versions': mock_lst}): - comt = ('pyenv failed to install') - ret.update({'comment': comt, 'result': False}) + mock_str = MagicMock(return_value="2.7.6") + mock_lst = MagicMock(return_value=["2.7.6"]) + with patch.dict( + pyenv.__salt__, + { + "pyenv.is_installed": mock_f, + "pyenv.install": mock_fa, + "pyenv.default": mock_str, + "pyenv.versions": mock_lst, + }, + ): + comt = "pyenv failed to install" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(pyenv.installed(name), ret) - comt = ('Requested python exists.') - ret.update({'comment': comt, 'result': True, 'default': True}) + comt = "Requested python exists." + ret.update({"comment": comt, "result": True, "default": True}) self.assertDictEqual(pyenv.installed(name), ret) self.assertDictEqual(pyenv.installed(name), ret) @@ -64,65 +64,81 @@ class PyenvTestCase(TestCase, LoaderModuleMockMixin): # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to verify that the specified python is not installed with pyenv. - ''' - name = 'python-2.7.6' + """ + name = "python-2.7.6" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - with patch.dict(pyenv.__opts__, {'test': True}): - comt = ('python 2.7.6 is set to be uninstalled') - ret.update({'comment': comt}) + with patch.dict(pyenv.__opts__, {"test": True}): + comt = "python 2.7.6 is set to be uninstalled" + ret.update({"comment": comt}) self.assertDictEqual(pyenv.absent(name), ret) - with patch.dict(pyenv.__opts__, {'test': False}): + with patch.dict(pyenv.__opts__, {"test": False}): mock_f = MagicMock(side_effect=[False, True]) mock_t = MagicMock(return_value=True) - mock_str = MagicMock(return_value='2.7.6') - mock_lst = MagicMock(return_value=['2.7.6']) - with patch.dict(pyenv.__salt__, {'pyenv.is_installed': mock_f, - 'pyenv.uninstall_python': mock_t, - 'pyenv.default': mock_str, - 'pyenv.versions': mock_lst}): - comt = ('pyenv not installed, 2.7.6 not either') - ret.update({'comment': comt, 'result': True}) + mock_str = MagicMock(return_value="2.7.6") + mock_lst = MagicMock(return_value=["2.7.6"]) + with patch.dict( + pyenv.__salt__, + { + "pyenv.is_installed": mock_f, + "pyenv.uninstall_python": mock_t, + "pyenv.default": mock_str, + "pyenv.versions": mock_lst, + }, + ): + comt = "pyenv not installed, 2.7.6 not either" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(pyenv.absent(name), ret) - comt = ('Successfully removed python') - ret.update({'comment': comt, 'result': True, 'default': True, - 'changes': {'2.7.6': 'Uninstalled'}}) + comt = "Successfully removed python" + ret.update( + { + "comment": comt, + "result": True, + "default": True, + "changes": {"2.7.6": "Uninstalled"}, + } + ) self.assertDictEqual(pyenv.absent(name), ret) # 'install_pyenv' function tests: 1 def test_install_pyenv(self): - ''' + """ Test to install pyenv if not installed. - ''' - name = 'python-2.7.6' + """ + name = "python-2.7.6" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - with patch.dict(pyenv.__opts__, {'test': True}): - comt = ('pyenv is set to be installed') - ret.update({'comment': comt}) + with patch.dict(pyenv.__opts__, {"test": True}): + comt = "pyenv is set to be installed" + ret.update({"comment": comt}) self.assertDictEqual(pyenv.install_pyenv(name), ret) - with patch.dict(pyenv.__opts__, {'test': False}): + with patch.dict(pyenv.__opts__, {"test": False}): mock_t = MagicMock(return_value=True) - mock_str = MagicMock(return_value='2.7.6') - mock_lst = MagicMock(return_value=['2.7.6']) - with patch.dict(pyenv.__salt__, {'pyenv.install_python': mock_t, - 'pyenv.default': mock_str, - 'pyenv.versions': mock_lst}): - comt = ('Successfully installed python') - ret.update({'comment': comt, 'result': True, 'default': False, - 'changes': {None: 'Installed'}}) + mock_str = MagicMock(return_value="2.7.6") + mock_lst = MagicMock(return_value=["2.7.6"]) + with patch.dict( + pyenv.__salt__, + { + "pyenv.install_python": mock_t, + "pyenv.default": mock_str, + "pyenv.versions": mock_lst, + }, + ): + comt = "Successfully installed python" + ret.update( + { + "comment": comt, + "result": True, + "default": False, + "changes": {None: "Installed"}, + } + ) self.assertDictEqual(pyenv.install_pyenv(name), ret) diff --git a/tests/unit/states/test_pyrax_queues.py b/tests/unit/states/test_pyrax_queues.py index c39664686fe..de9e80ea7cc 100644 --- a/tests/unit/states/test_pyrax_queues.py +++ b/tests/unit/states/test_pyrax_queues.py @@ -1,83 +1,81 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.pyrax_queues as pyrax_queues +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class PyraxQueuesTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.pyrax_queues - ''' + """ + def setup_loader_modules(self): return {pyrax_queues: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the RackSpace queue exists. - ''' - name = 'myqueue' - provider = 'my-pyrax' + """ + name = "myqueue" + provider = "my-pyrax" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - mock_dct = MagicMock(side_effect=[{provider: {'salt': True}}, - {provider: {'salt': False}}, - {provider: {'salt': False}}, False]) - with patch.dict(pyrax_queues.__salt__, {'cloud.action': mock_dct}): - comt = ('{0} present.'.format(name)) - ret.update({'comment': comt}) + mock_dct = MagicMock( + side_effect=[ + {provider: {"salt": True}}, + {provider: {"salt": False}}, + {provider: {"salt": False}}, + False, + ] + ) + with patch.dict(pyrax_queues.__salt__, {"cloud.action": mock_dct}): + comt = "{0} present.".format(name) + ret.update({"comment": comt}) self.assertDictEqual(pyrax_queues.present(name, provider), ret) - with patch.dict(pyrax_queues.__opts__, {'test': True}): - comt = ('Rackspace queue myqueue is set to be created.') - ret.update({'comment': comt, 'result': None}) + with patch.dict(pyrax_queues.__opts__, {"test": True}): + comt = "Rackspace queue myqueue is set to be created." + ret.update({"comment": comt, "result": None}) self.assertDictEqual(pyrax_queues.present(name, provider), ret) - with patch.dict(pyrax_queues.__opts__, {'test': False}): - comt = ('Failed to create myqueue Rackspace queue.') - ret.update({'comment': comt, 'result': False}) + with patch.dict(pyrax_queues.__opts__, {"test": False}): + comt = "Failed to create myqueue Rackspace queue." + ret.update({"comment": comt, "result": False}) self.assertDictEqual(pyrax_queues.present(name, provider), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the named Rackspace queue is deleted. - ''' - name = 'myqueue' - provider = 'my-pyrax' + """ + name = "myqueue" + provider = "my-pyrax" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - mock_dct = MagicMock(side_effect=[{provider: {'salt': False}}, - {provider: {'salt': True}}]) - with patch.dict(pyrax_queues.__salt__, {'cloud.action': mock_dct}): - comt = ('myqueue does not exist.') - ret.update({'comment': comt}) + mock_dct = MagicMock( + side_effect=[{provider: {"salt": False}}, {provider: {"salt": True}}] + ) + with patch.dict(pyrax_queues.__salt__, {"cloud.action": mock_dct}): + comt = "myqueue does not exist." + ret.update({"comment": comt}) self.assertDictEqual(pyrax_queues.absent(name, provider), ret) - with patch.dict(pyrax_queues.__opts__, {'test': True}): - comt = ('Rackspace queue myqueue is set to be removed.') - ret.update({'comment': comt, 'result': None}) + with patch.dict(pyrax_queues.__opts__, {"test": True}): + comt = "Rackspace queue myqueue is set to be removed." + ret.update({"comment": comt, "result": None}) self.assertDictEqual(pyrax_queues.absent(name, provider), ret) diff --git a/tests/unit/states/test_quota.py b/tests/unit/states/test_quota.py index 4362adcd6fd..f772b4e1fba 100644 --- a/tests/unit/states/test_quota.py +++ b/tests/unit/states/test_quota.py @@ -1,65 +1,62 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.quota as quota +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class QuotaTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.quota - ''' + """ + def setup_loader_modules(self): return {quota: {}} # 'mode' function tests: 1 def test_mode(self): - ''' + """ Test to set the quota for the system. - ''' - name = '/' + """ + name = "/" mode = True - quotatype = 'user' + quotatype = "user" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_bool = MagicMock(side_effect=[True, False]) - mock = MagicMock(return_value={name: {quotatype: 'on'}}) - with patch.dict(quota.__salt__, {'quota.get_mode': mock}): - comt = ('Quota for / already set to on') - ret.update({'comment': comt, 'result': True}) + mock = MagicMock(return_value={name: {quotatype: "on"}}) + with patch.dict(quota.__salt__, {"quota.get_mode": mock}): + comt = "Quota for / already set to on" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(quota.mode(name, mode, quotatype), ret) - mock = MagicMock(return_value={name: {quotatype: 'off'}}) - with patch.dict(quota.__salt__, {'quota.get_mode': mock, - 'quota.on': mock_bool}): - with patch.dict(quota.__opts__, {'test': True}): - comt = ('Quota for / needs to be set to on') - ret.update({'comment': comt, 'result': None}) + mock = MagicMock(return_value={name: {quotatype: "off"}}) + with patch.dict( + quota.__salt__, {"quota.get_mode": mock, "quota.on": mock_bool} + ): + with patch.dict(quota.__opts__, {"test": True}): + comt = "Quota for / needs to be set to on" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(quota.mode(name, mode, quotatype), ret) - with patch.dict(quota.__opts__, {'test': False}): - comt = ('Set quota for / to on') - ret.update({'comment': comt, 'result': True, - 'changes': {'quota': name}}) + with patch.dict(quota.__opts__, {"test": False}): + comt = "Set quota for / to on" + ret.update( + {"comment": comt, "result": True, "changes": {"quota": name}} + ) self.assertDictEqual(quota.mode(name, mode, quotatype), ret) - comt = ('Failed to set quota for / to on') - ret.update({'comment': comt, 'result': False, 'changes': {}}) + comt = "Failed to set quota for / to on" + ret.update({"comment": comt, "result": False, "changes": {}}) self.assertDictEqual(quota.mode(name, mode, quotatype), ret) diff --git a/tests/unit/states/test_rabbitmq_cluster.py b/tests/unit/states/test_rabbitmq_cluster.py index 51a5bb1bf56..0bd13b16bee 100644 --- a/tests/unit/states/test_rabbitmq_cluster.py +++ b/tests/unit/states/test_rabbitmq_cluster.py @@ -1,63 +1,60 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.rabbitmq_cluster as rabbitmq_cluster +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RabbitmqClusterTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the rabbitmq_cluster state - ''' + """ + def setup_loader_modules(self): return {rabbitmq_cluster: {}} def test_joined(self): - ''' + """ Test to ensure the current node joined to a cluster with node user@host - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} - mock = MagicMock(side_effect=[['rahulha@salt'], [''], ['']]) - with patch.dict(rabbitmq_cluster.__salt__, - {"rabbitmq.cluster_status": mock}): - ret.update({'comment': 'Already in cluster'}) - self.assertDictEqual(rabbitmq_cluster.joined('salt', 'salt', - 'rahulha'), ret) + mock = MagicMock(side_effect=[["rahulha@salt"], [""], [""]]) + with patch.dict(rabbitmq_cluster.__salt__, {"rabbitmq.cluster_status": mock}): + ret.update({"comment": "Already in cluster"}) + self.assertDictEqual( + rabbitmq_cluster.joined("salt", "salt", "rahulha"), ret + ) with patch.dict(rabbitmq_cluster.__opts__, {"test": True}): - ret.update({'result': None, - 'comment': 'Node is set to join ' - 'cluster rahulha@salt', - 'changes': {'new': 'rahulha@salt', 'old': ''}}) - self.assertDictEqual(rabbitmq_cluster.joined('salt', 'salt', - 'rahulha'), ret) + ret.update( + { + "result": None, + "comment": "Node is set to join " "cluster rahulha@salt", + "changes": {"new": "rahulha@salt", "old": ""}, + } + ) + self.assertDictEqual( + rabbitmq_cluster.joined("salt", "salt", "rahulha"), ret + ) with patch.dict(rabbitmq_cluster.__opts__, {"test": False}): - mock = MagicMock(return_value={'Error': 'ERR'}) - with patch.dict(rabbitmq_cluster.__salt__, - {"rabbitmq.join_cluster": mock}): - ret.update({'result': False, - 'comment': 'ERR', - 'changes': {}}) - self.assertDictEqual(rabbitmq_cluster.joined('salt', - 'salt', - 'rahulha'), - ret) + mock = MagicMock(return_value={"Error": "ERR"}) + with patch.dict( + rabbitmq_cluster.__salt__, {"rabbitmq.join_cluster": mock} + ): + ret.update({"result": False, "comment": "ERR", "changes": {}}) + self.assertDictEqual( + rabbitmq_cluster.joined("salt", "salt", "rahulha"), ret + ) diff --git a/tests/unit/states/test_rabbitmq_plugin.py b/tests/unit/states/test_rabbitmq_plugin.py index 6590788b715..e0454d13fbc 100644 --- a/tests/unit/states/test_rabbitmq_plugin.py +++ b/tests/unit/states/test_rabbitmq_plugin.py @@ -1,77 +1,67 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.rabbitmq_plugin as rabbitmq_plugin +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RabbitmqPluginTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.rabbitmq_plugin - ''' + """ + def setup_loader_modules(self): return {rabbitmq_plugin: {}} # 'enabled' function tests: 1 def test_enabled(self): - ''' + """ Test to ensure the RabbitMQ plugin is enabled. - ''' - name = 'some_plugin' + """ + name = "some_plugin" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[True, False]) - with patch.dict(rabbitmq_plugin.__salt__, - {'rabbitmq.plugin_is_enabled': mock}): + with patch.dict(rabbitmq_plugin.__salt__, {"rabbitmq.plugin_is_enabled": mock}): comment = "Plugin 'some_plugin' is already enabled." - ret.update({'comment': comment}) + ret.update({"comment": comment}) self.assertDictEqual(rabbitmq_plugin.enabled(name), ret) - with patch.dict(rabbitmq_plugin.__opts__, {'test': True}): + with patch.dict(rabbitmq_plugin.__opts__, {"test": True}): comment = "Plugin 'some_plugin' is set to be enabled." - changes = {'new': 'some_plugin', 'old': ''} - ret.update({'comment': comment, 'result': None, 'changes': changes}) + changes = {"new": "some_plugin", "old": ""} + ret.update({"comment": comment, "result": None, "changes": changes}) self.assertDictEqual(rabbitmq_plugin.enabled(name), ret) # 'disabled' function tests: 1 def test_disabled(self): - ''' + """ Test to ensure the RabbitMQ plugin is disabled. - ''' - name = 'some_plugin' + """ + name = "some_plugin" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[False, True]) - with patch.dict(rabbitmq_plugin.__salt__, - {'rabbitmq.plugin_is_enabled': mock}): + with patch.dict(rabbitmq_plugin.__salt__, {"rabbitmq.plugin_is_enabled": mock}): comment = "Plugin 'some_plugin' is already disabled." - ret.update({'comment': comment}) + ret.update({"comment": comment}) self.assertDictEqual(rabbitmq_plugin.disabled(name), ret) - with patch.dict(rabbitmq_plugin.__opts__, {'test': True}): + with patch.dict(rabbitmq_plugin.__opts__, {"test": True}): comment = "Plugin 'some_plugin' is set to be disabled." - changes = {'new': '', 'old': 'some_plugin'} - ret.update({'comment': comment, 'result': None, 'changes': changes}) + changes = {"new": "", "old": "some_plugin"} + ret.update({"comment": comment, "result": None, "changes": changes}) self.assertDictEqual(rabbitmq_plugin.disabled(name), ret) diff --git a/tests/unit/states/test_rabbitmq_policy.py b/tests/unit/states/test_rabbitmq_policy.py index 9209e77a4a3..c9eb446da7a 100644 --- a/tests/unit/states/test_rabbitmq_policy.py +++ b/tests/unit/states/test_rabbitmq_policy.py @@ -1,83 +1,86 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.rabbitmq_policy as rabbitmq_policy +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RabbitmqPolicyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.rabbitmq_policy - ''' + """ + def setup_loader_modules(self): return {rabbitmq_policy: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the RabbitMQ policy exists. - ''' - name = 'HA' - pattern = '.*' + """ + name = "HA" + pattern = ".*" definition = '{"ha-mode":"all"}' - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - mock = MagicMock(side_effect=[{'/': {name: {'pattern': pattern, - 'definition': definition, - 'priority': 0}}}, {}]) - with patch.dict(rabbitmq_policy.__salt__, - {'rabbitmq.list_policies': mock}): - comt = ('Policy / HA is already present') - ret.update({'comment': comt}) - self.assertDictEqual(rabbitmq_policy.present(name, pattern, - definition), ret) + mock = MagicMock( + side_effect=[ + { + "/": { + name: { + "pattern": pattern, + "definition": definition, + "priority": 0, + } + } + }, + {}, + ] + ) + with patch.dict(rabbitmq_policy.__salt__, {"rabbitmq.list_policies": mock}): + comt = "Policy / HA is already present" + ret.update({"comment": comt}) + self.assertDictEqual( + rabbitmq_policy.present(name, pattern, definition), ret + ) - with patch.dict(rabbitmq_policy.__opts__, {'test': True}): - comment = 'Policy / HA is set to be created' - changes = {'new': 'HA', 'old': {}} - ret.update({'comment': comment, 'result': None, 'changes': changes}) - self.assertDictEqual(rabbitmq_policy.present(name, pattern, - definition), ret) + with patch.dict(rabbitmq_policy.__opts__, {"test": True}): + comment = "Policy / HA is set to be created" + changes = {"new": "HA", "old": {}} + ret.update({"comment": comment, "result": None, "changes": changes}) + self.assertDictEqual( + rabbitmq_policy.present(name, pattern, definition), ret + ) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the named policy is absent. - ''' - name = 'HA' + """ + name = "HA" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[False, True]) - with patch.dict(rabbitmq_policy.__salt__, - {'rabbitmq.policy_exists': mock}): + with patch.dict(rabbitmq_policy.__salt__, {"rabbitmq.policy_exists": mock}): comment = "Policy '/ HA' is not present." - ret.update({'comment': comment}) + ret.update({"comment": comment}) self.assertDictEqual(rabbitmq_policy.absent(name), ret) - with patch.dict(rabbitmq_policy.__opts__, {'test': True}): + with patch.dict(rabbitmq_policy.__opts__, {"test": True}): comment = "Policy '/ HA' will be removed." - changes = {'new': '', 'old': 'HA'} - ret.update({'comment': comment, 'result': None, 'changes': changes}) + changes = {"new": "", "old": "HA"} + ret.update({"comment": comment, "result": None, "changes": changes}) self.assertDictEqual(rabbitmq_policy.absent(name), ret) diff --git a/tests/unit/states/test_rabbitmq_vhost.py b/tests/unit/states/test_rabbitmq_vhost.py index 70854cce7ef..67d395559f4 100644 --- a/tests/unit/states/test_rabbitmq_vhost.py +++ b/tests/unit/states/test_rabbitmq_vhost.py @@ -1,62 +1,62 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.rabbitmq_vhost as rabbitmq_vhost +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RabbitmqVhostTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.rabbitmq_vhost - ''' + """ + def setup_loader_modules(self): return {rabbitmq_vhost: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure the RabbitMQ VHost exists. - ''' - name = 'virtual_host' + """ + name = "virtual_host" - ret = {'name': name, - 'changes': {'new': 'virtual_host', 'old': ''}, - 'result': None, - 'comment': "Virtual Host 'virtual_host' will be created."} + ret = { + "name": name, + "changes": {"new": "virtual_host", "old": ""}, + "result": None, + "comment": "Virtual Host 'virtual_host' will be created.", + } mock = MagicMock(return_value=False) - with patch.dict(rabbitmq_vhost.__salt__, - {'rabbitmq.vhost_exists': mock}): - with patch.dict(rabbitmq_vhost.__opts__, {'test': True}): + with patch.dict(rabbitmq_vhost.__salt__, {"rabbitmq.vhost_exists": mock}): + with patch.dict(rabbitmq_vhost.__opts__, {"test": True}): self.assertDictEqual(rabbitmq_vhost.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure the named user is absent. - ''' - name = 'myqueue' + """ + name = "myqueue" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': "Virtual Host '{0}' is not present.".format(name)} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Virtual Host '{0}' is not present.".format(name), + } mock = MagicMock(return_value=False) - with patch.dict(rabbitmq_vhost.__salt__, - {'rabbitmq.vhost_exists': mock}): + with patch.dict(rabbitmq_vhost.__salt__, {"rabbitmq.vhost_exists": mock}): self.assertDictEqual(rabbitmq_vhost.absent(name), ret) diff --git a/tests/unit/states/test_rbenv.py b/tests/unit/states/test_rbenv.py index a8b1833a350..105be286d32 100644 --- a/tests/unit/states/test_rbenv.py +++ b/tests/unit/states/test_rbenv.py @@ -1,35 +1,33 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.rbenv as rbenv +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RbenvTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.rbenv - ''' + """ + def setup_loader_modules(self): return {rbenv: {}} # 'installed' function tests: 1 def test_installed(self): - ''' + """ Test to verify that the specified ruby is installed with rbenv. - ''' + """ # rbenv.is_installed is used wherever test is False. mock_is = MagicMock(side_effect=[False, True, True, True, True]) @@ -41,177 +39,231 @@ class RbenvTestCase(TestCase, LoaderModuleMockMixin): # installed and an attempt to install a version of Ruby is # made. mock_ir = MagicMock(side_effect=[True, False]) - mock_def = MagicMock(return_value='2.3.4') - mock_ver = MagicMock(return_value=['2.3.4', '2.4.1']) - with patch.dict(rbenv.__salt__, - {'rbenv.is_installed': mock_is, - 'rbenv.install': mock_i, - 'rbenv.default': mock_def, - 'rbenv.versions': mock_ver, - 'rbenv.install_ruby': mock_ir}): - with patch.dict(rbenv.__opts__, {'test': True}): - name = '1.9.3-p551' - comt = 'Ruby {0} is set to be installed'.format(name) - ret = {'name': name, 'changes': {}, 'comment': comt, - 'result': None} + mock_def = MagicMock(return_value="2.3.4") + mock_ver = MagicMock(return_value=["2.3.4", "2.4.1"]) + with patch.dict( + rbenv.__salt__, + { + "rbenv.is_installed": mock_is, + "rbenv.install": mock_i, + "rbenv.default": mock_def, + "rbenv.versions": mock_ver, + "rbenv.install_ruby": mock_ir, + }, + ): + with patch.dict(rbenv.__opts__, {"test": True}): + name = "1.9.3-p551" + comt = "Ruby {0} is set to be installed".format(name) + ret = {"name": name, "changes": {}, "comment": comt, "result": None} self.assertDictEqual(rbenv.installed(name), ret) - name = '2.4.1' - comt = 'Ruby {0} is already installed'.format(name) - ret = {'name': name, 'changes': {}, 'comment': comt, - 'default': False, 'result': True} + name = "2.4.1" + comt = "Ruby {0} is already installed".format(name) + ret = { + "name": name, + "changes": {}, + "comment": comt, + "default": False, + "result": True, + } self.assertDictEqual(rbenv.installed(name), ret) - name = '2.3.4' - comt = 'Ruby {0} is already installed'.format(name) - ret = {'name': name, 'changes': {}, 'comment': comt, - 'default': True, 'result': True} + name = "2.3.4" + comt = "Ruby {0} is already installed".format(name) + ret = { + "name": name, + "changes": {}, + "comment": comt, + "default": True, + "result": True, + } self.assertDictEqual(rbenv.installed(name), ret) - with patch.dict(rbenv.__opts__, {'test': False}): - name = '2.4.1' - comt = 'Rbenv failed to install' - ret = {'name': name, 'changes': {}, 'comment': comt, - 'result': False} + with patch.dict(rbenv.__opts__, {"test": False}): + name = "2.4.1" + comt = "Rbenv failed to install" + ret = {"name": name, "changes": {}, "comment": comt, "result": False} self.assertDictEqual(rbenv.installed(name), ret) - comt = 'Requested ruby exists' - ret = {'name': name, 'comment': comt, 'default': False, - 'changes': {}, 'result': True} + comt = "Requested ruby exists" + ret = { + "name": name, + "comment": comt, + "default": False, + "changes": {}, + "result": True, + } self.assertDictEqual(rbenv.installed(name), ret) - name = '2.3.4' - comt = 'Requested ruby exists' - ret = {'name': name, 'comment': comt, 'default': True, - 'changes': {}, 'result': True} + name = "2.3.4" + comt = "Requested ruby exists" + ret = { + "name": name, + "comment": comt, + "default": True, + "changes": {}, + "result": True, + } self.assertDictEqual(rbenv.installed(name), ret) - name = '1.9.3-p551' - comt = 'Successfully installed ruby' - ret = {'name': name, 'comment': comt, 'default': False, - 'changes': {name: 'Installed'}, 'result': True} + name = "1.9.3-p551" + comt = "Successfully installed ruby" + ret = { + "name": name, + "comment": comt, + "default": False, + "changes": {name: "Installed"}, + "result": True, + } self.assertDictEqual(rbenv.installed(name), ret) - comt = 'Failed to install ruby' - ret = {'name': name, 'comment': comt, - 'changes': {}, 'result': False} + comt = "Failed to install ruby" + ret = {"name": name, "comment": comt, "changes": {}, "result": False} self.assertDictEqual(rbenv.installed(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to verify that the specified ruby is not installed with rbenv. - ''' + """ # rbenv.is_installed is used for all tests here. - mock_is = MagicMock(side_effect=[False, True, True, True, False, - True, True, True, True, True]) + mock_is = MagicMock( + side_effect=[False, True, True, True, False, True, True, True, True, True] + ) # rbenv.uninstall_ruby is only called when an action is # attempted (ie. Successfully... or Failed...) mock_uninstalled = MagicMock(side_effect=[True, False, False, True]) - mock_def = MagicMock(return_value='2.3.4') - mock_ver = MagicMock(return_value=['2.3.4', '2.4.1']) - with patch.dict(rbenv.__salt__, - {'rbenv.is_installed': mock_is, - 'rbenv.default': mock_def, - 'rbenv.versions': mock_ver, - 'rbenv.uninstall_ruby': mock_uninstalled}): + mock_def = MagicMock(return_value="2.3.4") + mock_ver = MagicMock(return_value=["2.3.4", "2.4.1"]) + with patch.dict( + rbenv.__salt__, + { + "rbenv.is_installed": mock_is, + "rbenv.default": mock_def, + "rbenv.versions": mock_ver, + "rbenv.uninstall_ruby": mock_uninstalled, + }, + ): - with patch.dict(rbenv.__opts__, {'test': True}): - name = '1.9.3-p551' - comt = 'Rbenv not installed, {0} not either'.format(name) - ret = {'name': name, 'changes': {}, 'comment': comt, - 'result': True} + with patch.dict(rbenv.__opts__, {"test": True}): + name = "1.9.3-p551" + comt = "Rbenv not installed, {0} not either".format(name) + ret = {"name": name, "changes": {}, "comment": comt, "result": True} self.assertDictEqual(rbenv.absent(name), ret) - comt = 'Ruby {0} is already uninstalled'.format(name) - ret = {'name': name, 'changes': {}, 'comment': comt, - 'result': True} + comt = "Ruby {0} is already uninstalled".format(name) + ret = {"name": name, "changes": {}, "comment": comt, "result": True} self.assertDictEqual(rbenv.absent(name), ret) - name = '2.3.4' - comt = 'Ruby {0} is set to be uninstalled'.format(name) - ret = {'name': name, 'changes': {}, 'comment': comt, - 'default': True, 'result': None} - self.assertDictEqual(rbenv.absent('2.3.4'), ret) + name = "2.3.4" + comt = "Ruby {0} is set to be uninstalled".format(name) + ret = { + "name": name, + "changes": {}, + "comment": comt, + "default": True, + "result": None, + } + self.assertDictEqual(rbenv.absent("2.3.4"), ret) - name = '2.4.1' - comt = 'Ruby {0} is set to be uninstalled'.format(name) - ret = {'name': name, 'changes': {}, 'comment': comt, - 'default': False, 'result': None} - self.assertDictEqual(rbenv.absent('2.4.1'), ret) + name = "2.4.1" + comt = "Ruby {0} is set to be uninstalled".format(name) + ret = { + "name": name, + "changes": {}, + "comment": comt, + "default": False, + "result": None, + } + self.assertDictEqual(rbenv.absent("2.4.1"), ret) - with patch.dict(rbenv.__opts__, {'test': False}): - name = '1.9.3-p551' - comt = 'Rbenv not installed, {0} not either'.format(name) - ret = {'name': name, 'changes': {}, 'comment': comt, - 'result': True} + with patch.dict(rbenv.__opts__, {"test": False}): + name = "1.9.3-p551" + comt = "Rbenv not installed, {0} not either".format(name) + ret = {"name": name, "changes": {}, "comment": comt, "result": True} self.assertDictEqual(rbenv.absent(name), ret) - comt = 'Ruby {0} is already absent'.format(name) - ret = {'name': name, 'changes': {}, 'comment': comt, - 'result': True} + comt = "Ruby {0} is already absent".format(name) + ret = {"name": name, "changes": {}, "comment": comt, "result": True} self.assertDictEqual(rbenv.absent(name), ret) - name = '2.3.4' - comt = 'Successfully removed ruby' - ret = {'name': name, 'changes': {name: 'Uninstalled'}, - 'comment': comt, 'default': True, 'result': True} + name = "2.3.4" + comt = "Successfully removed ruby" + ret = { + "name": name, + "changes": {name: "Uninstalled"}, + "comment": comt, + "default": True, + "result": True, + } self.assertDictEqual(rbenv.absent(name), ret) - comt = 'Failed to uninstall ruby' - ret = {'name': name, 'changes': {}, 'comment': comt, - 'default': True, 'result': False} + comt = "Failed to uninstall ruby" + ret = { + "name": name, + "changes": {}, + "comment": comt, + "default": True, + "result": False, + } self.assertDictEqual(rbenv.absent(name), ret) - name = '2.4.1' - comt = 'Failed to uninstall ruby' - ret = {'name': name, 'changes': {}, 'comment': comt, - 'default': False, 'result': False} + name = "2.4.1" + comt = "Failed to uninstall ruby" + ret = { + "name": name, + "changes": {}, + "comment": comt, + "default": False, + "result": False, + } self.assertDictEqual(rbenv.absent(name), ret) - comt = 'Successfully removed ruby' - ret = {'name': name, 'changes': {name: 'Uninstalled'}, - 'comment': comt, 'default': False, 'result': True} + comt = "Successfully removed ruby" + ret = { + "name": name, + "changes": {name: "Uninstalled"}, + "comment": comt, + "default": False, + "result": True, + } self.assertDictEqual(rbenv.absent(name), ret) # 'install_rbenv' function tests: 1 def test_install_rbenv(self): - ''' + """ Test to install rbenv if not installed. - ''' - name = 'myqueue' + """ + name = "myqueue" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} mock_is = MagicMock(side_effect=[False, True, True, False, False]) mock_i = MagicMock(side_effect=[False, True]) - with patch.dict(rbenv.__salt__, - {'rbenv.is_installed': mock_is, - 'rbenv.install': mock_i}): + with patch.dict( + rbenv.__salt__, {"rbenv.is_installed": mock_is, "rbenv.install": mock_i} + ): - with patch.dict(rbenv.__opts__, {'test': True}): - comt = 'Rbenv is set to be installed' - ret.update({'comment': comt, 'result': None}) + with patch.dict(rbenv.__opts__, {"test": True}): + comt = "Rbenv is set to be installed" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(rbenv.install_rbenv(name), ret) - comt = 'Rbenv is already installed' - ret.update({'comment': comt, 'result': True}) + comt = "Rbenv is already installed" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(rbenv.install_rbenv(name), ret) - with patch.dict(rbenv.__opts__, {'test': False}): - comt = 'Rbenv is already installed' - ret.update({'comment': comt, 'result': True}) + with patch.dict(rbenv.__opts__, {"test": False}): + comt = "Rbenv is already installed" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(rbenv.install_rbenv(name), ret) - comt = 'Rbenv failed to install' - ret.update({'comment': comt, 'result': False}) + comt = "Rbenv failed to install" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(rbenv.install_rbenv(name), ret) - comt = 'Rbenv installed' - ret.update({'comment': comt, 'result': True}) + comt = "Rbenv installed" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(rbenv.install_rbenv(name), ret) diff --git a/tests/unit/states/test_rdp.py b/tests/unit/states/test_rdp.py index 248cfb67093..f7b14ffbbb2 100644 --- a/tests/unit/states/test_rdp.py +++ b/tests/unit/states/test_rdp.py @@ -1,91 +1,88 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.rdp as rdp +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RdpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.rdp - ''' + """ + def setup_loader_modules(self): return {rdp: {}} # 'enabled' function tests: 1 def test_enabled(self): - ''' + """ Test to enable the RDP service and make sure access to the RDP port is allowed in the firewall configuration. - ''' - name = 'my_service' + """ + name = "my_service" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} mock_t = MagicMock(side_effect=[False, False, True]) mock_f = MagicMock(return_value=False) - with patch.dict(rdp.__salt__, - {'rdp.status': mock_t, - 'rdp.enable': mock_f}): - with patch.dict(rdp.__opts__, {'test': True}): - comt = ('RDP will be enabled') - ret.update({'comment': comt, 'result': None}) + with patch.dict(rdp.__salt__, {"rdp.status": mock_t, "rdp.enable": mock_f}): + with patch.dict(rdp.__opts__, {"test": True}): + comt = "RDP will be enabled" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(rdp.enabled(name), ret) - with patch.dict(rdp.__opts__, {'test': False}): - ret.update({'comment': '', 'result': False, - 'changes': {'RDP was enabled': True}}) + with patch.dict(rdp.__opts__, {"test": False}): + ret.update( + { + "comment": "", + "result": False, + "changes": {"RDP was enabled": True}, + } + ) self.assertDictEqual(rdp.enabled(name), ret) - comt = ('RDP is enabled') - ret.update({'comment': comt, 'result': True, - 'changes': {}}) + comt = "RDP is enabled" + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(rdp.enabled(name), ret) # 'disabled' function tests: 1 def test_disabled(self): - ''' + """ Test to disable the RDP service. - ''' - name = 'my_service' + """ + name = "my_service" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[True, True, False]) mock_t = MagicMock(return_value=True) - with patch.dict(rdp.__salt__, - {'rdp.status': mock, - 'rdp.disable': mock_t}): - with patch.dict(rdp.__opts__, {'test': True}): - comt = ('RDP will be disabled') - ret.update({'comment': comt, 'result': None}) + with patch.dict(rdp.__salt__, {"rdp.status": mock, "rdp.disable": mock_t}): + with patch.dict(rdp.__opts__, {"test": True}): + comt = "RDP will be disabled" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(rdp.disabled(name), ret) - with patch.dict(rdp.__opts__, {'test': False}): - ret.update({'comment': '', 'result': True, - 'changes': {'RDP was disabled': True}}) + with patch.dict(rdp.__opts__, {"test": False}): + ret.update( + { + "comment": "", + "result": True, + "changes": {"RDP was disabled": True}, + } + ) self.assertDictEqual(rdp.disabled(name), ret) - comt = ('RDP is disabled') - ret.update({'comment': comt, 'result': True, 'changes': {}}) + comt = "RDP is disabled" + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(rdp.disabled(name), ret) diff --git a/tests/unit/states/test_redismod.py b/tests/unit/states/test_redismod.py index e417e2de26b..adbc780229c 100644 --- a/tests/unit/states/test_redismod.py +++ b/tests/unit/states/test_redismod.py @@ -1,78 +1,74 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.redismod as redismod +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class RedismodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.redismod - ''' + """ + def setup_loader_modules(self): return {redismod: {}} # 'string' function tests: 1 def test_string(self): - ''' + """ Test to ensure that the key exists in redis with the value specified. - ''' - name = 'key_in_redis' - value = 'string data' + """ + name = "key_in_redis" + value = "string data" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'Key already set to defined value'} + ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "Key already set to defined value", + } mock = MagicMock(return_value=value) - with patch.dict(redismod.__salt__, {'redis.get_key': mock}): + with patch.dict(redismod.__salt__, {"redis.get_key": mock}): self.assertDictEqual(redismod.string(name, value), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure key absent from redis. - ''' - name = 'key_in_redis' + """ + name = "key_in_redis" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[False, True, True]) mock_t = MagicMock(return_value=False) - with patch.dict(redismod.__salt__, - {'redis.exists': mock, - 'redis.delete': mock_t}): - comt = ('`keys` not formed as a list type') - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(redismod.absent(name, 'key'), ret) + with patch.dict( + redismod.__salt__, {"redis.exists": mock, "redis.delete": mock_t} + ): + comt = "`keys` not formed as a list type" + ret.update({"comment": comt, "result": False}) + self.assertDictEqual(redismod.absent(name, "key"), ret) - comt = ('Key(s) specified already absent') - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(redismod.absent(name, ['key']), ret) + comt = "Key(s) specified already absent" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual(redismod.absent(name, ["key"]), ret) - comt = ('Keys deleted') - ret.update({'comment': comt, 'changes': {'deleted': ['key']}}) - self.assertDictEqual(redismod.absent(name, ['key']), ret) + comt = "Keys deleted" + ret.update({"comment": comt, "changes": {"deleted": ["key"]}}) + self.assertDictEqual(redismod.absent(name, ["key"]), ret) - comt = ('Key deleted') - ret.update({'comment': comt, - 'changes': {'deleted': ['key_in_redis']}}) + comt = "Key deleted" + ret.update({"comment": comt, "changes": {"deleted": ["key_in_redis"]}}) self.assertDictEqual(redismod.absent(name), ret) diff --git a/tests/unit/states/test_reg.py b/tests/unit/states/test_reg.py index e20df558485..b1dc95aed0b 100644 --- a/tests/unit/states/test_reg.py +++ b/tests/unit/states/test_reg.py @@ -1,240 +1,241 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.helpers import destructiveTest -from tests.support.runtests import RUNTIME_VARS -from tests.support.mock import patch +import salt.config +import salt.loader # Import Salt Libs import salt.states.reg as reg import salt.utils.platform import salt.utils.win_reg -import salt.config -import salt.loader +from tests.support.helpers import destructiveTest + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class RegTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.reg - ''' - hive = 'HKEY_CURRENT_USER' - key = 'SOFTWARE\\Salt-Testing' - name = hive + '\\' + key - vname = 'version' - vdata = '0.15.3' + """ + + hive = "HKEY_CURRENT_USER" + key = "SOFTWARE\\Salt-Testing" + name = hive + "\\" + key + vname = "version" + vdata = "0.15.3" def setup_loader_modules(self): - opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) - utils = salt.loader.utils(opts, whitelist=['dacl', 'reg']) - return { - reg: { - '__opts__': {'test': False}, - '__utils__': utils}} + opts = salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion") + ) + utils = salt.loader.utils(opts, whitelist=["dacl", "reg"]) + return {reg: {"__opts__": {"test": False}, "__utils__": utils}} def tearDown(self): salt.utils.win_reg.delete_key_recursive(hive=self.hive, key=self.key) @destructiveTest def test_present(self): - ''' + """ Test to set a registry entry. - ''' + """ expected = { - 'comment': 'Added {0} to {1}'.format(self.vname, self.name), - 'pchanges': {}, - 'changes': { - 'reg': { - 'Added': { - 'Inheritance': True, - 'Perms': { - 'Deny': None, - 'Grant': None}, - 'Value': self.vdata, - 'Key': self.name, - 'Owner': None, - 'Entry': self.vname}}}, - 'name': self.name, - 'result': True} + "comment": "Added {0} to {1}".format(self.vname, self.name), + "pchanges": {}, + "changes": { + "reg": { + "Added": { + "Inheritance": True, + "Perms": {"Deny": None, "Grant": None}, + "Value": self.vdata, + "Key": self.name, + "Owner": None, + "Entry": self.vname, + } + } + }, + "name": self.name, + "result": True, + } ret = reg.present(self.name, vname=self.vname, vdata=self.vdata) self.assertDictEqual(ret, expected) @destructiveTest def test_present_string_dword(self): - ''' + """ Test to set a registry entry. - ''' - vname = 'dword_data' - vdata = '00000001' - vtype = 'REG_DWORD' + """ + vname = "dword_data" + vdata = "00000001" + vtype = "REG_DWORD" expected_vdata = 1 expected = { - 'comment': 'Added {0} to {1}'.format(vname, self.name), - 'pchanges': {}, - 'changes': { - 'reg': { - 'Added': { - 'Inheritance': True, - 'Perms': { - 'Deny': None, - 'Grant': None}, - 'Value': expected_vdata, - 'Key': self.name, - 'Owner': None, - 'Entry': vname}}}, - 'name': self.name, - 'result': True} - ret = reg.present( - self.name, vname=vname, vdata=vdata, vtype=vtype) + "comment": "Added {0} to {1}".format(vname, self.name), + "pchanges": {}, + "changes": { + "reg": { + "Added": { + "Inheritance": True, + "Perms": {"Deny": None, "Grant": None}, + "Value": expected_vdata, + "Key": self.name, + "Owner": None, + "Entry": vname, + } + } + }, + "name": self.name, + "result": True, + } + ret = reg.present(self.name, vname=vname, vdata=vdata, vtype=vtype) self.assertDictEqual(ret, expected) @destructiveTest def test_present_string_dword_existing(self): - ''' + """ Test to set a registry entry. - ''' - vname = 'dword_data' - vdata = '0000001' - vtype = 'REG_DWORD' + """ + vname = "dword_data" + vdata = "0000001" + vtype = "REG_DWORD" # Set it first - reg.present( - self.name, vname=vname, vdata=vdata, vtype=vtype) + reg.present(self.name, vname=vname, vdata=vdata, vtype=vtype) expected = { - 'comment': '{0} in {1} is already present'.format(vname, self.name), - 'pchanges': {}, - 'changes': {}, - 'name': self.name, - 'result': True} - ret = reg.present( - self.name, vname=vname, vdata=vdata, vtype=vtype) + "comment": "{0} in {1} is already present".format(vname, self.name), + "pchanges": {}, + "changes": {}, + "name": self.name, + "result": True, + } + ret = reg.present(self.name, vname=vname, vdata=vdata, vtype=vtype) self.assertDictEqual(ret, expected) def test_present_test_true(self): expected = { - 'comment': '', - 'pchanges': {}, - 'changes': { - 'reg': { - 'Will add': { - 'Inheritance': True, - 'Perms': { - 'Deny': None, - 'Grant': None}, - 'Value': self.vdata, - 'Key': self.name, - 'Owner': None, - 'Entry': 'version'}}}, - 'name': self.name, - 'result': None} - with patch.dict(reg.__opts__, {'test': True}): + "comment": "", + "pchanges": {}, + "changes": { + "reg": { + "Will add": { + "Inheritance": True, + "Perms": {"Deny": None, "Grant": None}, + "Value": self.vdata, + "Key": self.name, + "Owner": None, + "Entry": "version", + } + } + }, + "name": self.name, + "result": None, + } + with patch.dict(reg.__opts__, {"test": True}): ret = reg.present(self.name, vname=self.vname, vdata=self.vdata) self.assertDictEqual(ret, expected) def test_present_existing(self): # Create the reg key for testing - salt.utils.win_reg.set_value(hive=self.hive, - key=self.key, - vname=self.vname, - vdata=self.vdata) + salt.utils.win_reg.set_value( + hive=self.hive, key=self.key, vname=self.vname, vdata=self.vdata + ) expected = { - 'comment': '{0} in {1} is already present'.format(self.vname, self.name), - 'pchanges': {}, - 'changes': {}, - 'name': self.name, - 'result': True} + "comment": "{0} in {1} is already present".format(self.vname, self.name), + "pchanges": {}, + "changes": {}, + "name": self.name, + "result": True, + } ret = reg.present(self.name, vname=self.vname, vdata=self.vdata) self.assertDictEqual(ret, expected) def test_present_existing_test_true(self): # Create the reg key for testing - salt.utils.win_reg.set_value(hive=self.hive, - key=self.key, - vname=self.vname, - vdata=self.vdata) + salt.utils.win_reg.set_value( + hive=self.hive, key=self.key, vname=self.vname, vdata=self.vdata + ) expected = { - 'comment': '{0} in {1} is already present'.format(self.vname, self.name), - 'pchanges': {}, - 'changes': {}, - 'name': self.name, - 'result': True} - with patch.dict(reg.__opts__, {'test': True}): + "comment": "{0} in {1} is already present".format(self.vname, self.name), + "pchanges": {}, + "changes": {}, + "name": self.name, + "result": True, + } + with patch.dict(reg.__opts__, {"test": True}): ret = reg.present(self.name, vname=self.vname, vdata=self.vdata) self.assertDictEqual(ret, expected) @destructiveTest def test_absent(self): - ''' + """ Test to remove a registry entry. - ''' + """ # Create the reg key for testing - salt.utils.win_reg.set_value(hive=self.hive, - key=self.key, - vname=self.vname, - vdata=self.vdata) + salt.utils.win_reg.set_value( + hive=self.hive, key=self.key, vname=self.vname, vdata=self.vdata + ) expected = { - 'comment': 'Removed {0} from {1}'.format(self.key, self.hive), - 'changes': { - 'reg': { - 'Removed': { - 'Entry': - self.vname, - 'Key': self.name}}}, - 'name': self.name, - 'result': True} + "comment": "Removed {0} from {1}".format(self.key, self.hive), + "changes": {"reg": {"Removed": {"Entry": self.vname, "Key": self.name}}}, + "name": self.name, + "result": True, + } ret = reg.absent(self.name, self.vname) self.assertDictEqual(ret, expected) @destructiveTest def test_absent_test_true(self): # Create the reg key for testing - salt.utils.win_reg.set_value(hive=self.hive, - key=self.key, - vname=self.vname, - vdata=self.vdata) + salt.utils.win_reg.set_value( + hive=self.hive, key=self.key, vname=self.vname, vdata=self.vdata + ) expected = { - 'comment': '', - 'changes': { - 'reg': { - 'Will remove': { - 'Entry': self.vname, - 'Key': self.name}}}, - 'name': self.name, - 'result': None} - with patch.dict(reg.__opts__, {'test': True}): + "comment": "", + "changes": { + "reg": {"Will remove": {"Entry": self.vname, "Key": self.name}} + }, + "name": self.name, + "result": None, + } + with patch.dict(reg.__opts__, {"test": True}): ret = reg.absent(self.name, self.vname) self.assertDictEqual(ret, expected) def test_absent_already_absent(self): - ''' + """ Test to remove a registry entry. - ''' + """ expected = { - 'comment': '{0} is already absent'.format(self.name), - 'changes': {}, - 'name': self.name, - 'result': True} + "comment": "{0} is already absent".format(self.name), + "changes": {}, + "name": self.name, + "result": True, + } ret = reg.absent(self.name, self.vname) self.assertDictEqual(ret, expected) def test_absent_already_absent_test_true(self): - ''' + """ Test to remove a registry entry. - ''' + """ expected = { - 'comment': '{0} is already absent'.format(self.name), - 'changes': {}, - 'name': self.name, - 'result': True} - with patch.dict(reg.__opts__, {'test': True}): + "comment": "{0} is already absent".format(self.name), + "changes": {}, + "name": self.name, + "result": True, + } + with patch.dict(reg.__opts__, {"test": True}): ret = reg.absent(self.name, self.vname) self.assertDictEqual(ret, expected) diff --git a/tests/unit/states/test_rsync.py b/tests/unit/states/test_rsync.py index cecebfc37c6..9005a9cff23 100644 --- a/tests/unit/states/test_rsync.py +++ b/tests/unit/states/test_rsync.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Gareth J. Greenaway <gareth@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -10,114 +10,136 @@ import salt.states.rsync as rsync # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class RsyncTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the Rsync state - ''' + """ + def setup_loader_modules(self): return {rsync: {}} def test_syncronized_failed(self): - ''' + """ Test to perform an rsync.synchorized call that fails - ''' - ret = {'pid': 100, 'retcode': 23, 'stderr': '', 'stdout': ''} - _expected = {'changes': {}, - 'comment': 'Destination directory name was not found.', - 'name': u'name', - 'result': False} + """ + ret = {"pid": 100, "retcode": 23, "stderr": "", "stdout": ""} + _expected = { + "changes": {}, + "comment": "Destination directory name was not found.", + "name": "name", + "result": False, + } - with patch.dict(rsync.__opts__, {'test': True}): + with patch.dict(rsync.__opts__, {"test": True}): mock = MagicMock(return_value=ret) - with patch.dict(rsync.__salt__, {'rsync.rsync': mock}): - self.assertDictEqual(rsync.synchronized("name", "source"), - _expected) + with patch.dict(rsync.__salt__, {"rsync.rsync": mock}): + self.assertDictEqual(rsync.synchronized("name", "source"), _expected) # Run again mocking os.path.exists as True - ret = {'pid': 100, - 'retcode': 23, - 'stderr': 'Something went wrong', - 'stdout': ''} - _expected = {'changes': {}, - 'comment': 'Something went wrong', - 'name': u'name', - 'result': False} + ret = { + "pid": 100, + "retcode": 23, + "stderr": "Something went wrong", + "stdout": "", + } + _expected = { + "changes": {}, + "comment": "Something went wrong", + "name": "name", + "result": False, + } - with patch('os.path.exists', MagicMock(return_value=True)): - with patch.dict(rsync.__opts__, {'test': False}): + with patch("os.path.exists", MagicMock(return_value=True)): + with patch.dict(rsync.__opts__, {"test": False}): mock = MagicMock(return_value=ret) - with patch.dict(rsync.__salt__, {'rsync.rsync': mock}): - self.assertDictEqual(rsync.synchronized("name", "source"), - _expected) + with patch.dict(rsync.__salt__, {"rsync.rsync": mock}): + self.assertDictEqual( + rsync.synchronized("name", "source"), _expected + ) def test_syncronized(self): - ''' + """ Test to perform an rsync.synchorized call that succeeds - ''' - ret = {'pid': 100, - 'retcode': 0, - 'stderr': '', - 'stdout': ('sending incremental file list\nsnapshot.jar\n\n' - 'sent 106 bytes received 35 bytes 282.00 bytes/sec\n' - 'total size is 0 speedup is 0.00')} - _expected = {'changes': {'copied': 'snapshot.jar', 'deleted': u'N/A'}, - 'comment': ('- sent 106 bytes\n- received 35 bytes\n- ' - '282.00 bytes/sec\n- total size is 0\n- speedup is 0.00'), - 'name': 'name', - 'result': True} + """ + ret = { + "pid": 100, + "retcode": 0, + "stderr": "", + "stdout": ( + "sending incremental file list\nsnapshot.jar\n\n" + "sent 106 bytes received 35 bytes 282.00 bytes/sec\n" + "total size is 0 speedup is 0.00" + ), + } + _expected = { + "changes": {"copied": "snapshot.jar", "deleted": "N/A"}, + "comment": ( + "- sent 106 bytes\n- received 35 bytes\n- " + "282.00 bytes/sec\n- total size is 0\n- speedup is 0.00" + ), + "name": "name", + "result": True, + } - with patch('os.path.exists', MagicMock(return_value=True)): - with patch.dict(rsync.__opts__, {'test': False}): + with patch("os.path.exists", MagicMock(return_value=True)): + with patch.dict(rsync.__opts__, {"test": False}): mock = MagicMock(return_value=ret) - with patch.dict(rsync.__salt__, {'rsync.rsync': mock}): - self.assertDictEqual(rsync.synchronized("name", "source"), - _expected) + with patch.dict(rsync.__salt__, {"rsync.rsync": mock}): + self.assertDictEqual( + rsync.synchronized("name", "source"), _expected + ) # Second pass simulating the file being in place - ret = {'pid': 100, - 'retcode': 0, - 'stderr': '', - 'stdout': ('sending incremental file list\n\n' - 'sent 106 bytes received 35 bytes ' - '282.00 bytes/sec\ntotal size is 0 ' - 'speedup is 0.00')} - _expected = {'changes': {}, - 'comment': ('- sent 106 bytes\n- received ' - '35 bytes\n- 282.00 bytes/sec\n- total ' - 'size is 0\n- speedup is 0.00'), - 'name': 'name', - 'result': True} + ret = { + "pid": 100, + "retcode": 0, + "stderr": "", + "stdout": ( + "sending incremental file list\n\n" + "sent 106 bytes received 35 bytes " + "282.00 bytes/sec\ntotal size is 0 " + "speedup is 0.00" + ), + } + _expected = { + "changes": {}, + "comment": ( + "- sent 106 bytes\n- received " + "35 bytes\n- 282.00 bytes/sec\n- total " + "size is 0\n- speedup is 0.00" + ), + "name": "name", + "result": True, + } - with patch('os.path.exists', MagicMock(return_value=True)): - with patch.dict(rsync.__opts__, {'test': False}): + with patch("os.path.exists", MagicMock(return_value=True)): + with patch.dict(rsync.__opts__, {"test": False}): mock = MagicMock(return_value=ret) - with patch.dict(rsync.__salt__, {'rsync.rsync': mock}): - self.assertDictEqual(rsync.synchronized("name", "source"), - _expected) + with patch.dict(rsync.__salt__, {"rsync.rsync": mock}): + self.assertDictEqual( + rsync.synchronized("name", "source"), _expected + ) def test_syncronized_test_true(self): - ''' + """ Test to perform an rsync.synchorized call that fails - ''' - ret = {'pid': 100, - 'retcode': 23, - 'stderr': 'Something went wrong', - 'stdout': ''} - _expected = {'changes': {}, - 'comment': '- ', - 'name': u'name', - 'result': None} + """ + ret = { + "pid": 100, + "retcode": 23, + "stderr": "Something went wrong", + "stdout": "", + } + _expected = {"changes": {}, "comment": "- ", "name": "name", "result": None} - with patch('os.path.exists', MagicMock(return_value=True)): - with patch.dict(rsync.__opts__, {'test': True}): + with patch("os.path.exists", MagicMock(return_value=True)): + with patch.dict(rsync.__opts__, {"test": True}): mock = MagicMock(return_value=ret) - with patch.dict(rsync.__salt__, {'rsync.rsync': mock}): - self.assertDictEqual(rsync.synchronized("name", "source"), - _expected) + with patch.dict(rsync.__salt__, {"rsync.rsync": mock}): + self.assertDictEqual( + rsync.synchronized("name", "source"), _expected + ) diff --git a/tests/unit/states/test_rvm.py b/tests/unit/states/test_rvm.py index 1acad27db14..da82c6b183f 100644 --- a/tests/unit/states/test_rvm.py +++ b/tests/unit/states/test_rvm.py @@ -1,12 +1,7 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +from __future__ import absolute_import, print_function, unicode_literals # Import salt libs import salt.states.rvm as rvm @@ -14,17 +9,21 @@ import salt.states.rvm as rvm # Import 3rd-party libs from salt.ext import six +# Import Salt Testing libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class TestRvmState(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { rvm: { - '__opts__': {'test': False}, - '__salt__': { - 'cmd.has_exec': MagicMock(return_value=True), - 'config.option': MagicMock(return_value=None) - } + "__opts__": {"test": False}, + "__salt__": { + "cmd.has_exec": MagicMock(return_value=True), + "config.option": MagicMock(return_value=None), + }, } } @@ -32,86 +31,111 @@ class TestRvmState(TestCase, LoaderModuleMockMixin): mock = MagicMock(return_value=True) with patch.dict( rvm.__salt__, - {'rvm.is_installed': MagicMock(return_value=False), - 'rvm.install': mock}): - rvm._check_rvm({'changes': {}}) + {"rvm.is_installed": MagicMock(return_value=False), "rvm.install": mock}, + ): + rvm._check_rvm({"changes": {}}) # rvm.install is not run anymore while checking rvm.is_installed self.assertEqual(mock.call_count, 0) def test__check_and_install_ruby(self): - mock_check_rvm = MagicMock( - return_value={'changes': {}, 'result': True}) - mock_check_ruby = MagicMock( - return_value={'changes': {}, 'result': False}) - mock_install_ruby = MagicMock(return_value='') - with patch.object(rvm, '_check_rvm', new=mock_check_rvm): - with patch.object(rvm, '_check_ruby', new=mock_check_ruby): - with patch.dict(rvm.__salt__, - {'rvm.install_ruby': mock_install_ruby}): - rvm._check_and_install_ruby({'changes': {}}, '1.9.3') - mock_install_ruby.assert_called_once_with('1.9.3', runas=None, opts=None, env=None) + mock_check_rvm = MagicMock(return_value={"changes": {}, "result": True}) + mock_check_ruby = MagicMock(return_value={"changes": {}, "result": False}) + mock_install_ruby = MagicMock(return_value="") + with patch.object(rvm, "_check_rvm", new=mock_check_rvm): + with patch.object(rvm, "_check_ruby", new=mock_check_ruby): + with patch.dict(rvm.__salt__, {"rvm.install_ruby": mock_install_ruby}): + rvm._check_and_install_ruby({"changes": {}}, "1.9.3") + mock_install_ruby.assert_called_once_with( + "1.9.3", runas=None, opts=None, env=None + ) def test__check_ruby(self): - mock = MagicMock(return_value=[['ruby', '1.9.3-p125', False], - ['jruby', '1.6.5.1', True]]) - with patch.dict(rvm.__salt__, {'rvm.list': mock}): - for ruby, result in six.iteritems({'1.9.3': True, - 'ruby-1.9.3': True, - 'ruby-1.9.3-p125': True, - '1.9.3-p125': True, - '1.9.3-p126': False, - 'rbx': False, - 'jruby': True, - 'jruby-1.6.5.1': True, - 'jruby-1.6': False, - 'jruby-1.9.3': False, - 'jruby-1.9.3-p125': False}): - ret = rvm._check_ruby({'changes': {}, 'result': False}, ruby) - self.assertEqual(result, ret['result']) + mock = MagicMock( + return_value=[["ruby", "1.9.3-p125", False], ["jruby", "1.6.5.1", True]] + ) + with patch.dict(rvm.__salt__, {"rvm.list": mock}): + for ruby, result in six.iteritems( + { + "1.9.3": True, + "ruby-1.9.3": True, + "ruby-1.9.3-p125": True, + "1.9.3-p125": True, + "1.9.3-p126": False, + "rbx": False, + "jruby": True, + "jruby-1.6.5.1": True, + "jruby-1.6": False, + "jruby-1.9.3": False, + "jruby-1.9.3-p125": False, + } + ): + ret = rvm._check_ruby({"changes": {}, "result": False}, ruby) + self.assertEqual(result, ret["result"]) def test_gemset_present(self): - with patch.object(rvm, '_check_rvm') as mock_method: - mock_method.return_value = {'result': True, 'changes': {}} - gems = ['global', 'foo', 'bar'] + with patch.object(rvm, "_check_rvm") as mock_method: + mock_method.return_value = {"result": True, "changes": {}} + gems = ["global", "foo", "bar"] gemset_list = MagicMock(return_value=gems) gemset_create = MagicMock(return_value=True) - check_ruby = MagicMock( - return_value={'result': False, 'changes': {}}) - with patch.object(rvm, '_check_ruby', new=check_ruby): - with patch.dict(rvm.__salt__, - {'rvm.gemset_list': gemset_list, - 'rvm.gemset_create': gemset_create}): - ret = rvm.gemset_present('foo') - self.assertEqual(True, ret['result']) + check_ruby = MagicMock(return_value={"result": False, "changes": {}}) + with patch.object(rvm, "_check_ruby", new=check_ruby): + with patch.dict( + rvm.__salt__, + { + "rvm.gemset_list": gemset_list, + "rvm.gemset_create": gemset_create, + }, + ): + ret = rvm.gemset_present("foo") + self.assertEqual(True, ret["result"]) - ret = rvm.gemset_present('quux') - self.assertEqual(True, ret['result']) - gemset_create.assert_called_once_with( - 'default', 'quux', runas=None) + ret = rvm.gemset_present("quux") + self.assertEqual(True, ret["result"]) + gemset_create.assert_called_once_with("default", "quux", runas=None) def test_installed(self): mock = MagicMock() - with patch.object(rvm, '_check_rvm') as mock_method: - mock_method.return_value = {'result': True} - with patch.object(rvm, '_check_and_install_ruby', new=mock): - rvm.installed('1.9.3', default=True) + with patch.object(rvm, "_check_rvm") as mock_method: + mock_method.return_value = {"result": True} + with patch.object(rvm, "_check_and_install_ruby", new=mock): + rvm.installed("1.9.3", default=True) mock.assert_called_once_with( - {'result': True}, '1.9.3', True, user=None, opts=None, env=None) + {"result": True}, "1.9.3", True, user=None, opts=None, env=None + ) def test_installed_with_env(self): mock = MagicMock() - with patch.object(rvm, '_check_rvm') as mock_method: - mock_method.return_value = {'result': True} - with patch.object(rvm, '_check_and_install_ruby', new=mock): - rvm.installed('1.9.3', default=True, env=[{'RUBY_CONFIGURE_OPTS': '--foobar'}]) + with patch.object(rvm, "_check_rvm") as mock_method: + mock_method.return_value = {"result": True} + with patch.object(rvm, "_check_and_install_ruby", new=mock): + rvm.installed( + "1.9.3", default=True, env=[{"RUBY_CONFIGURE_OPTS": "--foobar"}] + ) mock.assert_called_once_with( - {'result': True}, '1.9.3', True, user=None, opts=None, env=[{'RUBY_CONFIGURE_OPTS': '--foobar'}]) + {"result": True}, + "1.9.3", + True, + user=None, + opts=None, + env=[{"RUBY_CONFIGURE_OPTS": "--foobar"}], + ) def test_installed_with_opts(self): mock = MagicMock() - with patch.object(rvm, '_check_rvm') as mock_method: - mock_method.return_value = {'result': True} - with patch.object(rvm, '_check_and_install_ruby', new=mock): - rvm.installed('1.9.3', default=True, opts=[{'-C': '--enable-shared,--with-readline-dir=$HOME/.rvm/usr'}]) + with patch.object(rvm, "_check_rvm") as mock_method: + mock_method.return_value = {"result": True} + with patch.object(rvm, "_check_and_install_ruby", new=mock): + rvm.installed( + "1.9.3", + default=True, + opts=[{"-C": "--enable-shared,--with-readline-dir=$HOME/.rvm/usr"}], + ) mock.assert_called_once_with( - {'result': True}, '1.9.3', True, user=None, opts=[{'-C': '--enable-shared,--with-readline-dir=$HOME/.rvm/usr'}], env=None) + {"result": True}, + "1.9.3", + True, + user=None, + opts=[{"-C": "--enable-shared,--with-readline-dir=$HOME/.rvm/usr"}], + env=None, + ) diff --git a/tests/unit/states/test_saltmod.py b/tests/unit/states/test_saltmod.py index 1519aefa225..4ba443b1e1e 100644 --- a/tests/unit/states/test_saltmod.py +++ b/tests/unit/states/test_saltmod.py @@ -1,287 +1,329 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import os -import time -import tempfile +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import os +import tempfile +import time # Import Salt Libs import salt.config import salt.loader -import salt.utils.jid -import salt.utils.event import salt.states.saltmod as saltmod +import salt.utils.event +import salt.utils.jid +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class SaltmodTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.saltmod - ''' + """ + def setup_loader_modules(self): utils = salt.loader.utils( - salt.config.DEFAULT_MINION_OPTS.copy(), - whitelist=['state'] + salt.config.DEFAULT_MINION_OPTS.copy(), whitelist=["state"] ) return { saltmod: { - '__env__': 'base', - '__opts__': { - '__role': 'master', - 'file_client': 'remote', - 'sock_dir': tempfile.mkdtemp(dir=RUNTIME_VARS.TMP), - 'transport': 'tcp' + "__env__": "base", + "__opts__": { + "__role": "master", + "file_client": "remote", + "sock_dir": tempfile.mkdtemp(dir=RUNTIME_VARS.TMP), + "transport": "tcp", }, - '__salt__': {'saltutil.cmd': MagicMock()}, - '__orchestration_jid__': salt.utils.jid.gen_jid({}), - '__utils__': utils, + "__salt__": {"saltutil.cmd": MagicMock()}, + "__orchestration_jid__": salt.utils.jid.gen_jid({}), + "__utils__": utils, } } # 'state' function tests: 1 def test_state(self): - ''' + """ Test to invoke a state run on a given target - ''' - name = 'state' - tgt = 'minion1' + """ + name = "state" + tgt = "minion1" - comt = ('Passed invalid value for \'allow_fail\', must be an int') + comt = "Passed invalid value for 'allow_fail', must be an int" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": False, "comment": comt} - test_ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': 'States ran successfully.' - } - - test_batch_return = { - 'minion1': { - 'ret': { - 'test_|-notify_me_|-this is a name_|-show_notification': { - 'comment': 'Notify me', - 'name': 'this is a name', - 'start_time': '10:43:41.487565', - 'result': True, - 'duration': 0.35, - '__run_num__': 0, - '__sls__': 'demo', - 'changes': {}, - '__id__': 'notify_me' - }, - 'retcode': 0 - }, - 'out': 'highstate' - }, - 'minion2': { - 'ret': { - 'test_|-notify_me_|-this is a name_|-show_notification': { - 'comment': 'Notify me', - 'name': 'this is a name', - 'start_time': '10:43:41.487565', - 'result': True, - 'duration': 0.35, - '__run_num__': 0, - '__sls__': 'demo', - 'changes': {}, - '__id__': 'notify_me' - }, - 'retcode': 0 - }, - 'out': 'highstate' - }, - 'minion3': { - 'ret': { - 'test_|-notify_me_|-this is a name_|-show_notification': { - 'comment': 'Notify me', - 'name': 'this is a name', - 'start_time': '10:43:41.487565', - 'result': True, - 'duration': 0.35, - '__run_num__': 0, - '__sls__': 'demo', - 'changes': {}, - '__id__': 'notify_me' - }, - 'retcode': 0 - }, - 'out': 'highstate' - } + test_ret = { + "name": name, + "changes": {}, + "result": True, + "comment": "States ran successfully.", } - self.assertDictEqual(saltmod.state(name, tgt, allow_fail='a'), ret) + test_batch_return = { + "minion1": { + "ret": { + "test_|-notify_me_|-this is a name_|-show_notification": { + "comment": "Notify me", + "name": "this is a name", + "start_time": "10:43:41.487565", + "result": True, + "duration": 0.35, + "__run_num__": 0, + "__sls__": "demo", + "changes": {}, + "__id__": "notify_me", + }, + "retcode": 0, + }, + "out": "highstate", + }, + "minion2": { + "ret": { + "test_|-notify_me_|-this is a name_|-show_notification": { + "comment": "Notify me", + "name": "this is a name", + "start_time": "10:43:41.487565", + "result": True, + "duration": 0.35, + "__run_num__": 0, + "__sls__": "demo", + "changes": {}, + "__id__": "notify_me", + }, + "retcode": 0, + }, + "out": "highstate", + }, + "minion3": { + "ret": { + "test_|-notify_me_|-this is a name_|-show_notification": { + "comment": "Notify me", + "name": "this is a name", + "start_time": "10:43:41.487565", + "result": True, + "duration": 0.35, + "__run_num__": 0, + "__sls__": "demo", + "changes": {}, + "__id__": "notify_me", + }, + "retcode": 0, + }, + "out": "highstate", + }, + } - comt = ('No highstate or sls specified, no execution made') - ret.update({'comment': comt}) + self.assertDictEqual(saltmod.state(name, tgt, allow_fail="a"), ret) + + comt = "No highstate or sls specified, no execution made" + ret.update({"comment": comt}) self.assertDictEqual(saltmod.state(name, tgt), ret) - comt = ("Must pass in boolean for value of 'concurrent'") - ret.update({'comment': comt}) - self.assertDictEqual(saltmod.state(name, tgt, highstate=True, - concurrent='a'), ret) + comt = "Must pass in boolean for value of 'concurrent'" + ret.update({"comment": comt}) + self.assertDictEqual( + saltmod.state(name, tgt, highstate=True, concurrent="a"), ret + ) - ret.update({'comment': comt, 'result': None}) - with patch.dict(saltmod.__opts__, {'test': True}): + ret.update({"comment": comt, "result": None}) + with patch.dict(saltmod.__opts__, {"test": True}): self.assertDictEqual(saltmod.state(name, tgt, highstate=True), test_ret) - ret.update({'comment': 'States ran successfully. No changes made to silver.', 'result': True, '__jid__': '20170406104341210934'}) - with patch.dict(saltmod.__opts__, {'test': False}): - mock = MagicMock(return_value={'silver': {'jid': '20170406104341210934', 'retcode': 0, 'ret': {'test_|-notify_me_|-this is a name_|-show_notification': {'comment': 'Notify me', 'name': 'this is a name', 'start_time': '10:43:41.487565', 'result': True, 'duration': 0.35, '__run_num__': 0, '__sls__': 'demo', 'changes': {}, '__id__': 'notify_me'}}, 'out': 'highstate'}}) - with patch.dict(saltmod.__salt__, {'saltutil.cmd': mock}): + ret.update( + { + "comment": "States ran successfully. No changes made to silver.", + "result": True, + "__jid__": "20170406104341210934", + } + ) + with patch.dict(saltmod.__opts__, {"test": False}): + mock = MagicMock( + return_value={ + "silver": { + "jid": "20170406104341210934", + "retcode": 0, + "ret": { + "test_|-notify_me_|-this is a name_|-show_notification": { + "comment": "Notify me", + "name": "this is a name", + "start_time": "10:43:41.487565", + "result": True, + "duration": 0.35, + "__run_num__": 0, + "__sls__": "demo", + "changes": {}, + "__id__": "notify_me", + } + }, + "out": "highstate", + } + } + ) + with patch.dict(saltmod.__salt__, {"saltutil.cmd": mock}): self.assertDictEqual(saltmod.state(name, tgt, highstate=True), ret) - ret.update({'comment': 'States ran successfully. No changes made to minion1, minion3, minion2.'}) - del ret['__jid__'] - with patch.dict(saltmod.__opts__, {'test': False}): - with patch.dict(saltmod.__salt__, {'saltutil.cmd': MagicMock(return_value=test_batch_return)}): + ret.update( + { + "comment": "States ran successfully. No changes made to minion1, minion3, minion2." + } + ) + del ret["__jid__"] + with patch.dict(saltmod.__opts__, {"test": False}): + with patch.dict( + saltmod.__salt__, + {"saltutil.cmd": MagicMock(return_value=test_batch_return)}, + ): state_run = saltmod.state(name, tgt, highstate=True) # Test return without checking the comment contents. Comments are tested later. - comment = state_run.pop('comment') - ret.pop('comment') + comment = state_run.pop("comment") + ret.pop("comment") self.assertDictEqual(state_run, ret) # Check the comment contents in a non-order specific way (ordering fails sometimes on PY3) - self.assertIn('States ran successfully. No changes made to', comment) - for minion in ['minion1', 'minion2', 'minion3']: + self.assertIn("States ran successfully. No changes made to", comment) + for minion in ["minion1", "minion2", "minion3"]: self.assertIn(minion, comment) # 'function' function tests: 1 def test_function(self): - ''' + """ Test to execute a single module function on a remote minion via salt or salt-ssh - ''' - name = 'state' - tgt = 'larry' + """ + name = "state" + tgt = "larry" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': 'Function state would be executed ' - 'on target {0}'.format(tgt)} + ret = { + "name": name, + "changes": {}, + "result": None, + "comment": "Function state would be executed " "on target {0}".format(tgt), + } - with patch.dict(saltmod.__opts__, {'test': True}): + with patch.dict(saltmod.__opts__, {"test": True}): self.assertDictEqual(saltmod.function(name, tgt), ret) - ret.update({'result': True, - 'changes': {'out': 'highstate', 'ret': {tgt: ''}}, - 'comment': 'Function ran successfully.' - ' Function state ran on {0}.'.format(tgt)}) - with patch.dict(saltmod.__opts__, {'test': False}): - mock_ret = {'larry': {'ret': '', 'retcode': 0, 'failed': False}} + ret.update( + { + "result": True, + "changes": {"out": "highstate", "ret": {tgt: ""}}, + "comment": "Function ran successfully." + " Function state ran on {0}.".format(tgt), + } + ) + with patch.dict(saltmod.__opts__, {"test": False}): + mock_ret = {"larry": {"ret": "", "retcode": 0, "failed": False}} mock_cmd = MagicMock(return_value=mock_ret) - with patch.dict(saltmod.__salt__, {'saltutil.cmd': mock_cmd}): + with patch.dict(saltmod.__salt__, {"saltutil.cmd": mock_cmd}): self.assertDictEqual(saltmod.function(name, tgt), ret) # 'wait_for_event' function tests: 1 def test_wait_for_event(self): - ''' + """ Test to watch Salt's event bus and block until a condition is met - ''' - name = 'state' - tgt = 'minion1' + """ + name = "state" + tgt = "minion1" - comt = ('Timeout value reached.') + comt = "Timeout value reached." - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": False, "comment": comt} class Mockevent(object): - ''' + """ Mock event class - ''' + """ + flag = None def __init__(self): self.full = None def get_event(self, full): - ''' + """ Mock get_event method - ''' + """ self.full = full if self.flag: - return {'tag': name, 'data': {}} + return {"tag": name, "data": {}} return None - with patch.object(salt.utils.event, 'get_event', - MagicMock(return_value=Mockevent())): - with patch.dict(saltmod.__opts__, {'sock_dir': True, - 'transport': True}): - with patch.object(time, 'time', MagicMock(return_value=1.0)): - self.assertDictEqual(saltmod.wait_for_event(name, 'salt', - timeout=-1.0), - ret) + with patch.object( + salt.utils.event, "get_event", MagicMock(return_value=Mockevent()) + ): + with patch.dict(saltmod.__opts__, {"sock_dir": True, "transport": True}): + with patch.object(time, "time", MagicMock(return_value=1.0)): + self.assertDictEqual( + saltmod.wait_for_event(name, "salt", timeout=-1.0), ret + ) Mockevent.flag = True - ret.update({'comment': 'All events seen in 0.0 seconds.', - 'result': True}) - self.assertDictEqual(saltmod.wait_for_event(name, ''), ret) + ret.update( + {"comment": "All events seen in 0.0 seconds.", "result": True} + ) + self.assertDictEqual(saltmod.wait_for_event(name, ""), ret) - ret.update({'comment': 'Timeout value reached.', - 'result': False}) - self.assertDictEqual(saltmod.wait_for_event(name, tgt, - timeout=-1.0), - ret) + ret.update({"comment": "Timeout value reached.", "result": False}) + self.assertDictEqual( + saltmod.wait_for_event(name, tgt, timeout=-1.0), ret + ) # 'runner' function tests: 1 def test_runner(self): - ''' + """ Test to execute a runner module on the master - ''' - name = 'state' + """ + name = "state" - ret = {'changes': {'return': True}, 'name': 'state', 'result': True, - 'comment': 'Runner function \'state\' executed.', - '__orchestration__': True} - runner_mock = MagicMock(return_value={'return': True}) + ret = { + "changes": {"return": True}, + "name": "state", + "result": True, + "comment": "Runner function 'state' executed.", + "__orchestration__": True, + } + runner_mock = MagicMock(return_value={"return": True}) - with patch.dict(saltmod.__salt__, {'saltutil.runner': runner_mock}): + with patch.dict(saltmod.__salt__, {"saltutil.runner": runner_mock}): self.assertDictEqual(saltmod.runner(name), ret) # 'wheel' function tests: 1 def test_wheel(self): - ''' + """ Test to execute a wheel module on the master - ''' - name = 'state' + """ + name = "state" - ret = {'changes': {'return': True}, 'name': 'state', 'result': True, - 'comment': 'Wheel function \'state\' executed.', - '__orchestration__': True} - wheel_mock = MagicMock(return_value={'return': True}) + ret = { + "changes": {"return": True}, + "name": "state", + "result": True, + "comment": "Wheel function 'state' executed.", + "__orchestration__": True, + } + wheel_mock = MagicMock(return_value={"return": True}) - with patch.dict(saltmod.__salt__, {'saltutil.wheel': wheel_mock}): + with patch.dict(saltmod.__salt__, {"saltutil.wheel": wheel_mock}): self.assertDictEqual(saltmod.wheel(name), ret) def test_state_ssh(self): - ''' + """ Test saltmod passes roster to saltutil.cmd - ''' - origcmd = saltmod.__salt__['saltutil.cmd'] + """ + origcmd = saltmod.__salt__["saltutil.cmd"] cmd_kwargs = {} cmd_args = [] @@ -290,10 +332,12 @@ class SaltmodTestCase(TestCase, LoaderModuleMockMixin): cmd_kwargs.update(kwargs) return origcmd(*args, **kwargs) - with patch.dict(saltmod.__salt__, {'saltutil.cmd': cmd_mock}): - ret = saltmod.state('state.sls', tgt='*', ssh=True, highstate=True, roster='my_roster') - assert 'roster' in cmd_kwargs - assert cmd_kwargs['roster'] == 'my_roster' + with patch.dict(saltmod.__salt__, {"saltutil.cmd": cmd_mock}): + ret = saltmod.state( + "state.sls", tgt="*", ssh=True, highstate=True, roster="my_roster" + ) + assert "roster" in cmd_kwargs + assert cmd_kwargs["roster"] == "my_roster" class StatemodTests(TestCase, LoaderModuleMockMixin): @@ -301,49 +345,50 @@ class StatemodTests(TestCase, LoaderModuleMockMixin): self.tmp_cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) return { saltmod: { - '__env__': 'base', - '__opts__': { - 'id': 'webserver2', - 'argv': [], - '__role': 'master', - 'cachedir': self.tmp_cachedir, - 'extension_modules': os.path.join(self.tmp_cachedir, 'extmods'), + "__env__": "base", + "__opts__": { + "id": "webserver2", + "argv": [], + "__role": "master", + "cachedir": self.tmp_cachedir, + "extension_modules": os.path.join(self.tmp_cachedir, "extmods"), }, - '__salt__': {'saltutil.cmd': MagicMock()}, - '__orchestration_jid__': salt.utils.jid.gen_jid({}) + "__salt__": {"saltutil.cmd": MagicMock()}, + "__orchestration_jid__": salt.utils.jid.gen_jid({}), } } def test_statemod_state(self): - ''' Smoke test for for salt.states.statemod.state(). Ensures that we + """ Smoke test for for salt.states.statemod.state(). Ensures that we don't take an exception if optional parameters are not specified in __opts__ or __env__. - ''' - args = ('webserver_setup', 'webserver2') + """ + args = ("webserver_setup", "webserver2") kwargs = { - 'tgt_type': 'glob', - 'fail_minions': None, - 'pillar': None, - 'top': None, - 'batch': None, - 'orchestration_jid': None, - 'sls': 'vroom', - 'queue': False, - 'concurrent': False, - 'highstate': None, - 'expr_form': None, - 'ret': '', - 'ssh': False, - 'timeout': None, 'test': False, - 'allow_fail': 0, - 'saltenv': None, - 'expect_minions': False + "tgt_type": "glob", + "fail_minions": None, + "pillar": None, + "top": None, + "batch": None, + "orchestration_jid": None, + "sls": "vroom", + "queue": False, + "concurrent": False, + "highstate": None, + "expr_form": None, + "ret": "", + "ssh": False, + "timeout": None, + "test": False, + "allow_fail": 0, + "saltenv": None, + "expect_minions": False, } ret = saltmod.state(*args, **kwargs) expected = { - 'comment': 'States ran successfully.', - 'changes': {}, - 'name': 'webserver_setup', - 'result': True + "comment": "States ran successfully.", + "changes": {}, + "name": "webserver_setup", + "result": True, } self.assertEqual(ret, expected) diff --git a/tests/unit/states/test_saltutil.py b/tests/unit/states/test_saltutil.py index 1f7eb062cf8..72217b415d6 100644 --- a/tests/unit/states/test_saltutil.py +++ b/tests/unit/states/test_saltutil.py @@ -1,145 +1,153 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the saltutil state -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals import inspect -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import salt.modules.saltutil as saltutil_module # Import Salt Libs import salt.states.saltutil as saltutil_state -import salt.modules.saltutil as saltutil_module + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class Saltutil(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.saltutil - ''' + """ + def setup_loader_modules(self): - return {saltutil_state: {'__opts__': {'test': False}}} + return {saltutil_state: {"__opts__": {"test": False}}} def test_saltutil_sync_all_nochange(self): sync_output = { - 'clouds': [], - 'engines': [], - 'executors': [], - 'grains': [], - 'beacons': [], - 'utils': [], - 'returners': [], - 'modules': [], - 'renderers': [], - 'log_handlers': [], - 'thorium': [], - 'states': [], - 'sdb': [], - 'proxymodules': [], - 'output': [], - 'pillar': [], - 'matchers': [], - 'serializers': [], + "clouds": [], + "engines": [], + "executors": [], + "grains": [], + "beacons": [], + "utils": [], + "returners": [], + "modules": [], + "renderers": [], + "log_handlers": [], + "thorium": [], + "states": [], + "sdb": [], + "proxymodules": [], + "output": [], + "pillar": [], + "matchers": [], + "serializers": [], + } + state_id = "somename" + state_result = { + "changes": {}, + "comment": "No updates to sync", + "name": "somename", + "result": True, } - state_id = 'somename' - state_result = {'changes': {}, - 'comment': 'No updates to sync', - 'name': 'somename', - 'result': True - } mock_moduleout = MagicMock(return_value=sync_output) - with patch.dict(saltutil_state.__salt__, {'saltutil.sync_all': mock_moduleout}): + with patch.dict(saltutil_state.__salt__, {"saltutil.sync_all": mock_moduleout}): result = saltutil_state.sync_all(state_id, refresh=True) self.assertEqual(result, state_result) def test_saltutil_sync_all_test(self): sync_output = { - 'clouds': [], - 'engines': [], - 'executors': [], - 'grains': [], - 'beacons': [], - 'utils': [], - 'returners': [], - 'modules': [], - 'renderers': [], - 'log_handlers': [], - 'thorium': [], - 'states': [], - 'sdb': [], - 'proxymodules': [], - 'output': [], - 'pillar': [], - 'matchers': [], - 'serializers': [], + "clouds": [], + "engines": [], + "executors": [], + "grains": [], + "beacons": [], + "utils": [], + "returners": [], + "modules": [], + "renderers": [], + "log_handlers": [], + "thorium": [], + "states": [], + "sdb": [], + "proxymodules": [], + "output": [], + "pillar": [], + "matchers": [], + "serializers": [], + } + state_id = "somename" + state_result = { + "changes": {}, + "comment": "saltutil.sync_all would have been run", + "name": "somename", + "result": None, } - state_id = 'somename' - state_result = {'changes': {}, - 'comment': 'saltutil.sync_all would have been run', - 'name': 'somename', - 'result': None - } mock_moduleout = MagicMock(return_value=sync_output) - with patch.dict(saltutil_state.__salt__, {'saltutil.sync_all': mock_moduleout}): - with patch.dict(saltutil_state.__opts__, {'test': True}): + with patch.dict(saltutil_state.__salt__, {"saltutil.sync_all": mock_moduleout}): + with patch.dict(saltutil_state.__opts__, {"test": True}): result = saltutil_state.sync_all(state_id, refresh=True) self.assertEqual(result, state_result) def test_saltutil_sync_all_change(self): sync_output = { - 'clouds': [], - 'engines': [], - 'executors': [], - 'grains': [], - 'beacons': [], - 'utils': [], - 'returners': [], - 'modules': ['modules.file'], - 'renderers': [], - 'log_handlers': [], - 'thorium': [], - 'states': ['states.saltutil', 'states.ssh_auth'], - 'sdb': [], - 'proxymodules': [], - 'output': [], - 'pillar': [], - 'matchers': [], - 'serializers': [], + "clouds": [], + "engines": [], + "executors": [], + "grains": [], + "beacons": [], + "utils": [], + "returners": [], + "modules": ["modules.file"], + "renderers": [], + "log_handlers": [], + "thorium": [], + "states": ["states.saltutil", "states.ssh_auth"], + "sdb": [], + "proxymodules": [], + "output": [], + "pillar": [], + "matchers": [], + "serializers": [], + } + state_id = "somename" + state_result = { + "changes": { + "modules": ["modules.file"], + "states": ["states.saltutil", "states.ssh_auth"], + }, + "comment": "Sync performed", + "name": "somename", + "result": True, } - state_id = 'somename' - state_result = {'changes': {'modules': ['modules.file'], - 'states': ['states.saltutil', 'states.ssh_auth']}, - 'comment': 'Sync performed', - 'name': 'somename', - 'result': True - } mock_moduleout = MagicMock(return_value=sync_output) - with patch.dict(saltutil_state.__salt__, {'saltutil.sync_all': mock_moduleout}): + with patch.dict(saltutil_state.__salt__, {"saltutil.sync_all": mock_moduleout}): result = saltutil_state.sync_all(state_id, refresh=True) self.assertEqual(result, state_result) def test_saltutil_sync_states_should_match_saltutil_module(self): module_functions = [ - f[0] for f in inspect.getmembers(saltutil_module, inspect.isfunction) - if f[0].startswith('sync_') + f[0] + for f in inspect.getmembers(saltutil_module, inspect.isfunction) + if f[0].startswith("sync_") ] state_functions = [ - f[0] for f in inspect.getmembers(saltutil_state, inspect.isfunction) - if f[0].startswith('sync_') + f[0] + for f in inspect.getmembers(saltutil_state, inspect.isfunction) + if f[0].startswith("sync_") ] for fn in module_functions: self.assertIn( fn, state_functions, - msg='modules.saltutil.{} has no matching state in states.saltutil'.format(fn) + msg="modules.saltutil.{} has no matching state in states.saltutil".format( + fn + ), ) diff --git a/tests/unit/states/test_schedule.py b/tests/unit/states/test_schedule.py index 79e6ba3c72d..1b9c4599170 100644 --- a/tests/unit/states/test_schedule.py +++ b/tests/unit/states/test_schedule.py @@ -1,83 +1,79 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.states.schedule as schedule +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ScheduleTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.schedule - ''' + """ + def setup_loader_modules(self): return {schedule: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure a job is present in the schedule. - ''' - name = 'job1' + """ + name = "job1" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_dict = MagicMock(side_effect=[ret, []]) mock_mod = MagicMock(return_value=ret) mock_lst = MagicMock(side_effect=[{name: {}}, {name: {}}, {}, {}]) - with patch.dict(schedule.__salt__, - {'schedule.list': mock_lst, - 'schedule.build_schedule_item': mock_dict, - 'schedule.modify': mock_mod, - 'schedule.add': mock_mod}): + with patch.dict( + schedule.__salt__, + { + "schedule.list": mock_lst, + "schedule.build_schedule_item": mock_dict, + "schedule.modify": mock_mod, + "schedule.add": mock_mod, + }, + ): self.assertDictEqual(schedule.present(name), ret) - with patch.dict(schedule.__opts__, {'test': False}): + with patch.dict(schedule.__opts__, {"test": False}): self.assertDictEqual(schedule.present(name), ret) self.assertDictEqual(schedule.present(name), ret) - with patch.dict(schedule.__opts__, {'test': True}): - ret.update({'result': True}) + with patch.dict(schedule.__opts__, {"test": True}): + ret.update({"result": True}) self.assertDictEqual(schedule.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure a job is absent from the schedule. - ''' - name = 'job1' + """ + name = "job1" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_mod = MagicMock(return_value=ret) mock_lst = MagicMock(side_effect=[{name: {}}, {}]) - with patch.dict(schedule.__salt__, - {'schedule.list': mock_lst, - 'schedule.delete': mock_mod}): - with patch.dict(schedule.__opts__, {'test': False}): + with patch.dict( + schedule.__salt__, {"schedule.list": mock_lst, "schedule.delete": mock_mod} + ): + with patch.dict(schedule.__opts__, {"test": False}): self.assertDictEqual(schedule.absent(name), ret) - with patch.dict(schedule.__opts__, {'test': True}): - comt = ('Job job1 not present in schedule') - ret.update({'comment': comt, 'result': True}) + with patch.dict(schedule.__opts__, {"test": True}): + comt = "Job job1 not present in schedule" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(schedule.absent(name), ret) diff --git a/tests/unit/states/test_selinux.py b/tests/unit/states/test_selinux.py index 4040815ad90..019e961933a 100644 --- a/tests/unit/states/test_selinux.py +++ b/tests/unit/states/test_selinux.py @@ -1,125 +1,135 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.selinux as selinux +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SelinuxTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.selinux - ''' + """ + def setup_loader_modules(self): return {selinux: {}} # 'mode' function tests: 1 def test_mode(self): - ''' + """ Test to verifies the mode SELinux is running in, can be set to enforcing or permissive. - ''' - ret = {'name': 'unknown', - 'changes': {}, - 'result': False, - 'comment': 'unknown is not an accepted mode'} - self.assertDictEqual(selinux.mode('unknown'), ret) + """ + ret = { + "name": "unknown", + "changes": {}, + "result": False, + "comment": "unknown is not an accepted mode", + } + self.assertDictEqual(selinux.mode("unknown"), ret) - mock_en = MagicMock(return_value='Enforcing') - mock_pr = MagicMock(side_effect=['Permissive', 'Enforcing']) - with patch.dict(selinux.__salt__, - {'selinux.getenforce': mock_en, - 'selinux.getconfig': mock_en, - 'selinux.setenforce': mock_pr}): - comt = ('SELinux is already in Enforcing mode') - ret = {'name': 'Enforcing', 'comment': comt, 'result': True, 'changes': {}} - self.assertDictEqual(selinux.mode('Enforcing'), ret) + mock_en = MagicMock(return_value="Enforcing") + mock_pr = MagicMock(side_effect=["Permissive", "Enforcing"]) + with patch.dict( + selinux.__salt__, + { + "selinux.getenforce": mock_en, + "selinux.getconfig": mock_en, + "selinux.setenforce": mock_pr, + }, + ): + comt = "SELinux is already in Enforcing mode" + ret = {"name": "Enforcing", "comment": comt, "result": True, "changes": {}} + self.assertDictEqual(selinux.mode("Enforcing"), ret) - with patch.dict(selinux.__opts__, {'test': True}): - comt = ('SELinux mode is set to be changed to Permissive') - ret = {'name': 'Permissive', 'comment': comt, - 'result': None, 'changes': {'new': 'Permissive', 'old': 'Enforcing'}} - self.assertDictEqual(selinux.mode('Permissive'), ret) + with patch.dict(selinux.__opts__, {"test": True}): + comt = "SELinux mode is set to be changed to Permissive" + ret = { + "name": "Permissive", + "comment": comt, + "result": None, + "changes": {"new": "Permissive", "old": "Enforcing"}, + } + self.assertDictEqual(selinux.mode("Permissive"), ret) - with patch.dict(selinux.__opts__, {'test': False}): - comt = ('SELinux has been set to Permissive mode') - ret = {'name': 'Permissive', 'comment': comt, - 'result': True, 'changes': {'new': 'Permissive', 'old': 'Enforcing'}} - self.assertDictEqual(selinux.mode('Permissive'), ret) + with patch.dict(selinux.__opts__, {"test": False}): + comt = "SELinux has been set to Permissive mode" + ret = { + "name": "Permissive", + "comment": comt, + "result": True, + "changes": {"new": "Permissive", "old": "Enforcing"}, + } + self.assertDictEqual(selinux.mode("Permissive"), ret) - comt = ('Failed to set SELinux to Permissive mode') - ret.update({'name': 'Permissive', 'comment': comt, - 'result': False, 'changes': {}}) - self.assertDictEqual(selinux.mode('Permissive'), ret) + comt = "Failed to set SELinux to Permissive mode" + ret.update( + { + "name": "Permissive", + "comment": comt, + "result": False, + "changes": {}, + } + ) + self.assertDictEqual(selinux.mode("Permissive"), ret) # 'boolean' function tests: 1 def test_boolean(self): - ''' + """ Test to set up an SELinux boolean. - ''' - name = 'samba_create_home_dirs' + """ + name = "samba_create_home_dirs" value = True - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} mock_en = MagicMock(return_value=[]) - with patch.dict(selinux.__salt__, - {'selinux.list_sebool': mock_en}): - comt = ('Boolean {0} is not available'.format(name)) - ret.update({'comment': comt}) + with patch.dict(selinux.__salt__, {"selinux.list_sebool": mock_en}): + comt = "Boolean {0} is not available".format(name) + ret.update({"comment": comt}) self.assertDictEqual(selinux.boolean(name, value), ret) - mock_bools = MagicMock(return_value={name: {'State': 'on', - 'Default': 'on'}}) - with patch.dict(selinux.__salt__, - {'selinux.list_sebool': mock_bools}): - comt = ('None is not a valid value for the boolean') - ret.update({'comment': comt}) + mock_bools = MagicMock(return_value={name: {"State": "on", "Default": "on"}}) + with patch.dict(selinux.__salt__, {"selinux.list_sebool": mock_bools}): + comt = "None is not a valid value for the boolean" + ret.update({"comment": comt}) self.assertDictEqual(selinux.boolean(name, None), ret) - comt = ('Boolean is in the correct state') - ret.update({'comment': comt, 'result': True}) + comt = "Boolean is in the correct state" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(selinux.boolean(name, value, True), ret) - comt = ('Boolean is in the correct state') - ret.update({'comment': comt, 'result': True}) + comt = "Boolean is in the correct state" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(selinux.boolean(name, value), ret) - mock_bools = MagicMock(return_value={name: {'State': 'off', - 'Default': 'on'}}) + mock_bools = MagicMock(return_value={name: {"State": "off", "Default": "on"}}) mock = MagicMock(side_effect=[True, False]) - with patch.dict(selinux.__salt__, - {'selinux.list_sebool': mock_bools, - 'selinux.setsebool': mock}): - with patch.dict(selinux.__opts__, {'test': True}): - comt = ('Boolean samba_create_home_dirs' - ' is set to be changed to on') - ret.update({'comment': comt, 'result': None}) + with patch.dict( + selinux.__salt__, + {"selinux.list_sebool": mock_bools, "selinux.setsebool": mock}, + ): + with patch.dict(selinux.__opts__, {"test": True}): + comt = "Boolean samba_create_home_dirs" " is set to be changed to on" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(selinux.boolean(name, value), ret) - with patch.dict(selinux.__opts__, {'test': False}): - comt = ('Boolean samba_create_home_dirs has been set to on') - ret.update({'comment': comt, 'result': True}) - ret.update({'changes': {'State': {'old': 'off', 'new': 'on'}}}) + with patch.dict(selinux.__opts__, {"test": False}): + comt = "Boolean samba_create_home_dirs has been set to on" + ret.update({"comment": comt, "result": True}) + ret.update({"changes": {"State": {"old": "off", "new": "on"}}}) self.assertDictEqual(selinux.boolean(name, value), ret) - comt = ('Failed to set the boolean ' - 'samba_create_home_dirs to on') - ret.update({'comment': comt, 'result': False}) - ret.update({'changes': {}}) + comt = "Failed to set the boolean " "samba_create_home_dirs to on" + ret.update({"comment": comt, "result": False}) + ret.update({"changes": {}}) self.assertDictEqual(selinux.boolean(name, value), ret) diff --git a/tests/unit/states/test_serverdensity_device.py b/tests/unit/states/test_serverdensity_device.py index 6e54931548b..80fe01b3bc0 100644 --- a/tests/unit/states/test_serverdensity_device.py +++ b/tests/unit/states/test_serverdensity_device.py @@ -1,62 +1,69 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.serverdensity_device as serverdensity_device +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ServerdensityDeviceTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.serverdensity_device - ''' + """ + def setup_loader_modules(self): return {serverdensity_device: {}} # 'monitored' function tests: 1 def test_monitored(self): - ''' + """ Test to device is monitored with Server Density. - ''' - name = 'my_special_server' + """ + name = "my_special_server" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - mock_dict = MagicMock(return_value={'id': name}) - mock_t = MagicMock(side_effect=[True, {'agentKey': True}, - [{'agentKey': True}]]) - mock_sd = MagicMock(side_effect=[['sd-agent'], [], True]) - with patch.multiple(serverdensity_device, - __salt__={'status.all_status': mock_dict, - 'grains.items': mock_dict, - 'serverdensity_device.ls': mock_t, - 'pkg.list_pkgs': mock_sd, - 'serverdensity_device.install_agent': mock_sd}, - __opts__={'test': False}, + mock_dict = MagicMock(return_value={"id": name}) + mock_t = MagicMock(side_effect=[True, {"agentKey": True}, [{"agentKey": True}]]) + mock_sd = MagicMock(side_effect=[["sd-agent"], [], True]) + with patch.multiple( + serverdensity_device, + __salt__={ + "status.all_status": mock_dict, + "grains.items": mock_dict, + "serverdensity_device.ls": mock_t, + "pkg.list_pkgs": mock_sd, + "serverdensity_device.install_agent": mock_sd, + }, + __opts__={"test": False}, ): - comt = ('Such server name already exists in this' - ' Server Density account. And sd-agent is installed') - ret.update({'comment': comt}) + comt = ( + "Such server name already exists in this" + " Server Density account. And sd-agent is installed" + ) + ret.update({"comment": comt}) self.assertDictEqual(serverdensity_device.monitored(name), ret) - comt = ('Successfully installed agent and created' - ' device in Server Density db.') - ret.update({'comment': comt, 'changes': {'created_device': - {'agentKey': True}, - 'installed_agent': True}}) + comt = ( + "Successfully installed agent and created" + " device in Server Density db." + ) + ret.update( + { + "comment": comt, + "changes": { + "created_device": {"agentKey": True}, + "installed_agent": True, + }, + } + ) self.assertDictEqual(serverdensity_device.monitored(name), ret) diff --git a/tests/unit/states/test_service.py b/tests/unit/states/test_service.py index 3eead4c3576..fe1dbfbe880 100644 --- a/tests/unit/states/test_service.py +++ b/tests/unit/states/test_service.py @@ -1,255 +1,383 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.helpers import destructiveTest -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch - -# Import Salt Libs -import salt.utils.platform import salt.config import salt.loader import salt.states.service as service +# Import Salt Libs +import salt.utils.platform + +# Import Salt Testing Libs +from tests.support.helpers import destructiveTest +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + def func(name): - ''' + """ Mock func method - ''' + """ return name class ServiceTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the service state - ''' + """ + def setup_loader_modules(self): return {service: {}} def test_running(self): - ''' + """ Test to verify that the service is running - ''' - ret = [{'comment': '', 'changes': {}, 'name': 'salt', 'result': True}, - {'changes': {}, - 'comment': 'The service salt is already running', - 'name': 'salt', 'result': True}, - {'changes': 'saltstack', - 'comment': 'The service salt is already running', - 'name': 'salt', 'result': True}, - {'changes': {}, - 'comment': 'Service salt is set to start', 'name': 'salt', - 'result': None}, - {'changes': 'saltstack', - 'comment': 'Started Service salt', 'name': 'salt', - 'result': True}, - {'changes': {}, - 'comment': 'The service salt is already running', - 'name': 'salt', 'result': True}, - {'changes': 'saltstack', - 'comment': 'Service salt failed to start', 'name': 'salt', - 'result': False}, - {'changes': 'saltstack', - 'comment': 'Started Service salt\nService masking not available on this minion', - 'name': 'salt', 'result': True, 'warnings': ["The 'unmask' argument is not supported by this platform/action"]}] + """ + ret = [ + {"comment": "", "changes": {}, "name": "salt", "result": True}, + { + "changes": {}, + "comment": "The service salt is already running", + "name": "salt", + "result": True, + }, + { + "changes": "saltstack", + "comment": "The service salt is already running", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "Service salt is set to start", + "name": "salt", + "result": None, + }, + { + "changes": "saltstack", + "comment": "Started Service salt", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "The service salt is already running", + "name": "salt", + "result": True, + }, + { + "changes": "saltstack", + "comment": "Service salt failed to start", + "name": "salt", + "result": False, + }, + { + "changes": "saltstack", + "comment": "Started Service salt\nService masking not available on this minion", + "name": "salt", + "result": True, + "warnings": [ + "The 'unmask' argument is not supported by this platform/action" + ], + }, + ] tmock = MagicMock(return_value=True) fmock = MagicMock(return_value=False) vmock = MagicMock(return_value="salt") - with patch.object(service, '_enabled_used_error', vmock): - self.assertEqual(service.running("salt", enabled=1), 'salt') + with patch.object(service, "_enabled_used_error", vmock): + self.assertEqual(service.running("salt", enabled=1), "salt") - with patch.object(service, '_available', fmock): + with patch.object(service, "_available", fmock): self.assertDictEqual(service.running("salt"), ret[0]) - with patch.object(service, '_available', tmock): - with patch.dict(service.__opts__, {'test': False}): - with patch.dict(service.__salt__, {'service.enabled': tmock, - 'service.status': tmock}): + with patch.object(service, "_available", tmock): + with patch.dict(service.__opts__, {"test": False}): + with patch.dict( + service.__salt__, + {"service.enabled": tmock, "service.status": tmock}, + ): self.assertDictEqual(service.running("salt"), ret[1]) - mock = MagicMock(return_value={'changes': 'saltstack'}) - with patch.dict(service.__salt__, {'service.enabled': MagicMock(side_effect=[False, True]), - 'service.status': tmock}): - with patch.object(service, '_enable', mock): + mock = MagicMock(return_value={"changes": "saltstack"}) + with patch.dict( + service.__salt__, + { + "service.enabled": MagicMock(side_effect=[False, True]), + "service.status": tmock, + }, + ): + with patch.object(service, "_enable", mock): self.assertDictEqual(service.running("salt", True), ret[2]) - with patch.dict(service.__salt__, {'service.enabled': MagicMock(side_effect=[True, False]), - 'service.status': tmock}): - with patch.object(service, '_disable', mock): + with patch.dict( + service.__salt__, + { + "service.enabled": MagicMock(side_effect=[True, False]), + "service.status": tmock, + }, + ): + with patch.object(service, "_disable", mock): self.assertDictEqual(service.running("salt", False), ret[2]) - with patch.dict(service.__salt__, {'service.status': MagicMock(side_effect=[False, True]), - 'service.enabled': MagicMock(side_effect=[False, True]), - 'service.start': MagicMock(return_value="stack")}): - with patch.object(service, '_enable', MagicMock(return_value={'changes': 'saltstack'})): + with patch.dict( + service.__salt__, + { + "service.status": MagicMock(side_effect=[False, True]), + "service.enabled": MagicMock(side_effect=[False, True]), + "service.start": MagicMock(return_value="stack"), + }, + ): + with patch.object( + service, + "_enable", + MagicMock(return_value={"changes": "saltstack"}), + ): self.assertDictEqual(service.running("salt", True), ret[4]) - with patch.dict(service.__salt__, {'service.status': MagicMock(side_effect=[False, True]), - 'service.enabled': MagicMock(side_effect=[False, True]), - 'service.unmask': MagicMock(side_effect=[False, True]), - 'service.start': MagicMock(return_value="stack")}): - with patch.object(service, '_enable', MagicMock(return_value={'changes': 'saltstack'})): - self.assertDictEqual(service.running("salt", True, unmask=True), ret[7]) + with patch.dict( + service.__salt__, + { + "service.status": MagicMock(side_effect=[False, True]), + "service.enabled": MagicMock(side_effect=[False, True]), + "service.unmask": MagicMock(side_effect=[False, True]), + "service.start": MagicMock(return_value="stack"), + }, + ): + with patch.object( + service, + "_enable", + MagicMock(return_value={"changes": "saltstack"}), + ): + self.assertDictEqual( + service.running("salt", True, unmask=True), ret[7] + ) - with patch.dict(service.__opts__, {'test': True}): - with patch.dict(service.__salt__, {'service.status': tmock}): + with patch.dict(service.__opts__, {"test": True}): + with patch.dict(service.__salt__, {"service.status": tmock}): self.assertDictEqual(service.running("salt"), ret[5]) - with patch.dict(service.__salt__, {'service.status': fmock}): + with patch.dict(service.__salt__, {"service.status": fmock}): self.assertDictEqual(service.running("salt"), ret[3]) - with patch.dict(service.__opts__, {'test': False}): - with patch.dict(service.__salt__, {'service.status': MagicMock(side_effect=[False, False]), - 'service.enabled': MagicMock(side_effecct=[True, True]), - 'service.start': MagicMock(return_value='stack')}): - with patch.object(service, '_enable', MagicMock(return_value={'changes': 'saltstack'})): - self.assertDictEqual(service.running('salt', True), ret[6]) + with patch.dict(service.__opts__, {"test": False}): + with patch.dict( + service.__salt__, + { + "service.status": MagicMock(side_effect=[False, False]), + "service.enabled": MagicMock(side_effecct=[True, True]), + "service.start": MagicMock(return_value="stack"), + }, + ): + with patch.object( + service, + "_enable", + MagicMock(return_value={"changes": "saltstack"}), + ): + self.assertDictEqual(service.running("salt", True), ret[6]) def test_dead(self): - ''' + """ Test to ensure that the named service is dead - ''' - ret = [{'changes': {}, 'comment': '', 'name': 'salt', 'result': True}, - {'changes': 'saltstack', - 'comment': 'The service salt is already dead', 'name': 'salt', - 'result': True}, - {'changes': {}, - 'comment': 'Service salt is set to be killed', 'name': 'salt', - 'result': None}, - {'changes': 'saltstack', - 'comment': 'Service salt was killed', 'name': 'salt', - 'result': True}, - {'changes': {}, - 'comment': 'Service salt failed to die', 'name': 'salt', - 'result': False}, - {'changes': 'saltstack', - 'comment': 'The service salt is already dead', 'name': 'salt', - 'result': True}] - info_mock = MagicMock(return_value={'StartType': ''}) + """ + ret = [ + {"changes": {}, "comment": "", "name": "salt", "result": True}, + { + "changes": "saltstack", + "comment": "The service salt is already dead", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "Service salt is set to be killed", + "name": "salt", + "result": None, + }, + { + "changes": "saltstack", + "comment": "Service salt was killed", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "Service salt failed to die", + "name": "salt", + "result": False, + }, + { + "changes": "saltstack", + "comment": "The service salt is already dead", + "name": "salt", + "result": True, + }, + ] + info_mock = MagicMock(return_value={"StartType": ""}) mock = MagicMock(return_value="salt") - with patch.object(service, '_enabled_used_error', mock): - self.assertEqual(service.dead("salt", enabled=1), 'salt') + with patch.object(service, "_enabled_used_error", mock): + self.assertEqual(service.dead("salt", enabled=1), "salt") tmock = MagicMock(return_value=True) fmock = MagicMock(return_value=False) - with patch.object(service, '_available', fmock): + with patch.object(service, "_available", fmock): self.assertDictEqual(service.dead("salt"), ret[0]) - with patch.object(service, '_available', tmock): - mock = MagicMock(return_value={'changes': 'saltstack'}) - with patch.dict(service.__opts__, {'test': True}): - with patch.dict(service.__salt__, {'service.enabled': fmock, - 'service.stop': tmock, - 'service.status': fmock, - 'service.info': info_mock}): - with patch.object(service, '_enable', mock): + with patch.object(service, "_available", tmock): + mock = MagicMock(return_value={"changes": "saltstack"}) + with patch.dict(service.__opts__, {"test": True}): + with patch.dict( + service.__salt__, + { + "service.enabled": fmock, + "service.stop": tmock, + "service.status": fmock, + "service.info": info_mock, + }, + ): + with patch.object(service, "_enable", mock): self.assertDictEqual(service.dead("salt", True), ret[5]) - with patch.dict(service.__salt__, {'service.enabled': tmock, - 'service.status': tmock, - 'service.info': info_mock}): + with patch.dict( + service.__salt__, + { + "service.enabled": tmock, + "service.status": tmock, + "service.info": info_mock, + }, + ): self.assertDictEqual(service.dead("salt"), ret[2]) - with patch.dict(service.__opts__, {'test': False}): - with patch.dict(service.__salt__, {'service.enabled': fmock, - 'service.stop': tmock, - 'service.status': fmock, - 'service.info': info_mock}): - with patch.object(service, '_enable', mock): + with patch.dict(service.__opts__, {"test": False}): + with patch.dict( + service.__salt__, + { + "service.enabled": fmock, + "service.stop": tmock, + "service.status": fmock, + "service.info": info_mock, + }, + ): + with patch.object(service, "_enable", mock): self.assertDictEqual(service.dead("salt", True), ret[1]) - with patch.dict(service.__salt__, {'service.enabled': MagicMock(side_effect=[True, True, False]), - 'service.status': MagicMock(side_effect=[True, False, False]), - 'service.stop': MagicMock(return_value="stack"), - 'service.info': info_mock}): - with patch.object(service, '_enable', MagicMock(return_value={'changes': 'saltstack'})): + with patch.dict( + service.__salt__, + { + "service.enabled": MagicMock(side_effect=[True, True, False]), + "service.status": MagicMock(side_effect=[True, False, False]), + "service.stop": MagicMock(return_value="stack"), + "service.info": info_mock, + }, + ): + with patch.object( + service, + "_enable", + MagicMock(return_value={"changes": "saltstack"}), + ): self.assertDictEqual(service.dead("salt", True), ret[3]) # test an initd which a wrong status (True even if dead) - with patch.dict(service.__salt__, {'service.enabled': MagicMock(side_effect=[False, False, False]), - 'service.status': MagicMock(side_effect=[True, True, True]), - 'service.stop': MagicMock(return_value="stack"), - 'service.info': info_mock}): - with patch.object(service, '_disable', MagicMock(return_value={})): + with patch.dict( + service.__salt__, + { + "service.enabled": MagicMock(side_effect=[False, False, False]), + "service.status": MagicMock(side_effect=[True, True, True]), + "service.stop": MagicMock(return_value="stack"), + "service.info": info_mock, + }, + ): + with patch.object(service, "_disable", MagicMock(return_value={})): self.assertDictEqual(service.dead("salt", False), ret[4]) def test_dead_with_missing_service(self): - ''' + """ Tests the case in which a service.dead state is executed on a state which does not exist. See https://github.com/saltstack/salt/issues/37511 - ''' - name = 'thisisnotarealservice' - with patch.dict(service.__salt__, - {'service.available': MagicMock(return_value=False)}): + """ + name = "thisisnotarealservice" + with patch.dict( + service.__salt__, {"service.available": MagicMock(return_value=False)} + ): ret = service.dead(name=name) self.assertDictEqual( ret, - {'changes': {}, - 'comment': 'The named service {0} is not available'.format(name), - 'result': True, - 'name': name} + { + "changes": {}, + "comment": "The named service {0} is not available".format(name), + "result": True, + "name": name, + }, ) def test_enabled(self): - ''' + """ Test to verify that the service is enabled - ''' - ret = {'changes': 'saltstack', 'comment': '', 'name': 'salt', - 'result': True} - mock = MagicMock(return_value={'changes': 'saltstack'}) - with patch.object(service, '_enable', mock): + """ + ret = {"changes": "saltstack", "comment": "", "name": "salt", "result": True} + mock = MagicMock(return_value={"changes": "saltstack"}) + with patch.object(service, "_enable", mock): self.assertDictEqual(service.enabled("salt"), ret) def test_disabled(self): - ''' + """ Test to verify that the service is disabled - ''' - ret = {'changes': 'saltstack', 'comment': '', 'name': 'salt', - 'result': True} - mock = MagicMock(return_value={'changes': 'saltstack'}) - with patch.object(service, '_disable', mock): + """ + ret = {"changes": "saltstack", "comment": "", "name": "salt", "result": True} + mock = MagicMock(return_value={"changes": "saltstack"}) + with patch.object(service, "_disable", mock): self.assertDictEqual(service.disabled("salt"), ret) def test_mod_watch(self): - ''' + """ Test to the service watcher, called to invoke the watch command. - ''' - ret = [{'changes': {}, - 'comment': 'Service is already stopped', 'name': 'salt', - 'result': True}, - {'changes': {}, - 'comment': 'Unable to trigger watch for service.stack', - 'name': 'salt', 'result': False}, - {'changes': {}, - 'comment': 'Service is set to be started', 'name': 'salt', - 'result': None}, - {'changes': {'salt': 'salt'}, - 'comment': 'Service started', 'name': 'salt', - 'result': 'salt'}] + """ + ret = [ + { + "changes": {}, + "comment": "Service is already stopped", + "name": "salt", + "result": True, + }, + { + "changes": {}, + "comment": "Unable to trigger watch for service.stack", + "name": "salt", + "result": False, + }, + { + "changes": {}, + "comment": "Service is set to be started", + "name": "salt", + "result": None, + }, + { + "changes": {"salt": "salt"}, + "comment": "Service started", + "name": "salt", + "result": "salt", + }, + ] mock = MagicMock(return_value=False) - with patch.dict(service.__salt__, {'service.status': mock}): + with patch.dict(service.__salt__, {"service.status": mock}): self.assertDictEqual(service.mod_watch("salt", "dead"), ret[0]) - with patch.dict(service.__salt__, {'service.start': func}): - with patch.dict(service.__opts__, {'test': True}): - self.assertDictEqual(service.mod_watch("salt", "running"), - ret[2]) + with patch.dict(service.__salt__, {"service.start": func}): + with patch.dict(service.__opts__, {"test": True}): + self.assertDictEqual(service.mod_watch("salt", "running"), ret[2]) - with patch.dict(service.__opts__, {'test': False}): - self.assertDictEqual(service.mod_watch("salt", "running"), - ret[3]) + with patch.dict(service.__opts__, {"test": False}): + self.assertDictEqual(service.mod_watch("salt", "running"), ret[3]) self.assertDictEqual(service.mod_watch("salt", "stack"), ret[1]) @@ -257,66 +385,69 @@ class ServiceTestCase(TestCase, LoaderModuleMockMixin): @destructiveTest @skipIf(salt.utils.platform.is_darwin(), "service.running is currently failing on OSX") class ServiceTestCaseFunctional(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the service state - ''' + """ + def setup_loader_modules(self): self.opts = salt.config.DEFAULT_MINION_OPTS.copy() - self.opts['grains'] = salt.loader.grains(self.opts) + self.opts["grains"] = salt.loader.grains(self.opts) self.utils = salt.loader.utils(self.opts) self.modules = salt.loader.minion_mods(self.opts, utils=self.utils) - self.service_name = 'cron' - cmd_name = 'crontab' - os_family = self.opts['grains']['os_family'] - os_release = self.opts['grains']['osrelease'] - if os_family == 'RedHat': - self.service_name = 'crond' - elif os_family == 'Arch': - self.service_name = 'sshd' - cmd_name = 'systemctl' - elif os_family == 'MacOS': - self.service_name = 'org.ntp.ntpd' - if int(os_release.split('.')[1]) >= 13: - self.service_name = 'com.openssh.sshd' - elif os_family == 'Windows': - self.service_name = 'Spooler' + self.service_name = "cron" + cmd_name = "crontab" + os_family = self.opts["grains"]["os_family"] + os_release = self.opts["grains"]["osrelease"] + if os_family == "RedHat": + self.service_name = "crond" + elif os_family == "Arch": + self.service_name = "sshd" + cmd_name = "systemctl" + elif os_family == "MacOS": + self.service_name = "org.ntp.ntpd" + if int(os_release.split(".")[1]) >= 13: + self.service_name = "com.openssh.sshd" + elif os_family == "Windows": + self.service_name = "Spooler" - if os_family != 'Windows' and salt.utils.path.which(cmd_name) is None: - self.skipTest('{0} is not installed'.format(cmd_name)) + if os_family != "Windows" and salt.utils.path.which(cmd_name) is None: + self.skipTest("{0} is not installed".format(cmd_name)) return { service: { - '__grains__': self.opts['grains'], - '__opts__': self.opts, - '__salt__': self.modules, - '__utils__': self.utils, + "__grains__": self.opts["grains"], + "__opts__": self.opts, + "__salt__": self.modules, + "__utils__": self.utils, }, } def setUp(self): - self.pre_srv_enabled = True if self.service_name in self.modules['service.get_enabled']() else False + self.pre_srv_enabled = ( + True + if self.service_name in self.modules["service.get_enabled"]() + else False + ) self.post_srv_disable = False if not self.pre_srv_enabled: - self.modules['service.enable'](self.service_name) + self.modules["service.enable"](self.service_name) self.post_srv_disable = True def tearDown(self): if self.post_srv_disable: - self.modules['service.disable'](self.service_name) + self.modules["service.disable"](self.service_name) def test_running_with_reload(self): - with patch.dict(service.__opts__, {'test': False}): + with patch.dict(service.__opts__, {"test": False}): service.dead(self.service_name, enable=False) result = service.running(name=self.service_name, enable=True, reload=False) expected = { - 'changes': { - self.service_name: True - }, - 'comment': 'Service {0} has been enabled, and is ' - 'running'.format(self.service_name), - 'name': self.service_name, - 'result': True + "changes": {self.service_name: True}, + "comment": "Service {0} has been enabled, and is " + "running".format(self.service_name), + "name": self.service_name, + "result": True, } self.assertDictEqual(result, expected) diff --git a/tests/unit/states/test_slack.py b/tests/unit/states/test_slack.py index 69a28756683..0166aabc8a7 100644 --- a/tests/unit/states/test_slack.py +++ b/tests/unit/states/test_slack.py @@ -1,158 +1,199 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> :codeauthor: Gareth J. Greenaway <gareth@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.slack as slack +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SlackTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.slack - ''' + """ + def setup_loader_modules(self): return {slack: {}} # 'post_message' function tests: 1 def test_post_message_apikey(self): - ''' + """ Test to send a message to a Slack channel using an API Key. - ''' - name = 'slack-message' - channel = '#general' - from_name = 'SuperAdmin' - message = 'This state was executed successfully.' - api_key = 'xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXXXX' + """ + name = "slack-message" + channel = "#general" + from_name = "SuperAdmin" + message = "This state was executed successfully." + api_key = "xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXXXX" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - with patch.dict(slack.__opts__, {'test': True}): - comt = 'The following message is to be sent to Slack: {0}'.format(message) - ret.update({'comment': comt}) - self.assertDictEqual(slack.post_message(name, - channel=channel, - from_name=from_name, - message=message, - api_key=api_key), ret) + with patch.dict(slack.__opts__, {"test": True}): + comt = "The following message is to be sent to Slack: {0}".format(message) + ret.update({"comment": comt}) + self.assertDictEqual( + slack.post_message( + name, + channel=channel, + from_name=from_name, + message=message, + api_key=api_key, + ), + ret, + ) - with patch.dict(slack.__opts__, {'test': False}): - comt = 'Please specify api_key or webhook.' - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, - channel=None, - from_name=from_name, - message=message, - api_key=None), ret) + with patch.dict(slack.__opts__, {"test": False}): + comt = "Please specify api_key or webhook." + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + slack.post_message( + name, + channel=None, + from_name=from_name, + message=message, + api_key=None, + ), + ret, + ) - comt = 'Slack channel is missing.' - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, - channel=None, - from_name=from_name, - message=message, - api_key=api_key), ret) + comt = "Slack channel is missing." + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + slack.post_message( + name, + channel=None, + from_name=from_name, + message=message, + api_key=api_key, + ), + ret, + ) - comt = 'Slack from name is missing.' - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, - channel=channel, - from_name=None, - message=message, - api_key=api_key), ret) + comt = "Slack from name is missing." + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + slack.post_message( + name, + channel=channel, + from_name=None, + message=message, + api_key=api_key, + ), + ret, + ) - comt = 'Slack message is missing.' - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, - channel=channel, - from_name=from_name, - message=None, - api_key=api_key), ret) + comt = "Slack message is missing." + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + slack.post_message( + name, + channel=channel, + from_name=from_name, + message=None, + api_key=api_key, + ), + ret, + ) mock = MagicMock(return_value=True) - with patch.dict(slack.__salt__, {'slack.post_message': mock}): - comt = 'Sent message: slack-message' - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(slack.post_message(name, - channel=channel, - from_name=from_name, - message=message, - api_key=api_key), - ret) + with patch.dict(slack.__salt__, {"slack.post_message": mock}): + comt = "Sent message: slack-message" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + slack.post_message( + name, + channel=channel, + from_name=from_name, + message=message, + api_key=api_key, + ), + ret, + ) def test_post_message_webhook(self): - ''' + """ Test to send a message to a Slack channel using an webhook. - ''' - name = 'slack-message' - channel = '#general' - username = 'SuperAdmin' - message = 'This state was executed successfully.' - webhook = 'XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX' - api_key = 'xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXXXX' + """ + name = "slack-message" + channel = "#general" + username = "SuperAdmin" + message = "This state was executed successfully." + webhook = "XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX" + api_key = "xoxp-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXXXX" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - with patch.dict(slack.__opts__, {'test': True}): - comt = 'The following message is to be sent to Slack: {0}'.format(message) - ret.update({'comment': comt}) - self.assertDictEqual(slack.post_message(name, - channel=channel, - username=username, - message=message, - webhook=webhook), ret) + with patch.dict(slack.__opts__, {"test": True}): + comt = "The following message is to be sent to Slack: {0}".format(message) + ret.update({"comment": comt}) + self.assertDictEqual( + slack.post_message( + name, + channel=channel, + username=username, + message=message, + webhook=webhook, + ), + ret, + ) - with patch.dict(slack.__opts__, {'test': False}): - comt = 'Please specify api_key or webhook.' - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, - channel=channel, - username=username, - message=None, - webhook=None), ret) + with patch.dict(slack.__opts__, {"test": False}): + comt = "Please specify api_key or webhook." + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + slack.post_message( + name, channel=channel, username=username, message=None, webhook=None + ), + ret, + ) - comt = 'Please specify only either api_key or webhook.' - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, - channel=channel, - username=username, - message=message, - api_key=api_key, - webhook=webhook), ret) + comt = "Please specify only either api_key or webhook." + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + slack.post_message( + name, + channel=channel, + username=username, + message=message, + api_key=api_key, + webhook=webhook, + ), + ret, + ) - comt = 'Slack message is missing.' - ret.update({'comment': comt, 'result': False}) - self.assertDictEqual(slack.post_message(name, - channel=channel, - username=username, - message=None, - webhook=webhook), ret) + comt = "Slack message is missing." + ret.update({"comment": comt, "result": False}) + self.assertDictEqual( + slack.post_message( + name, + channel=channel, + username=username, + message=None, + webhook=webhook, + ), + ret, + ) mock = MagicMock(return_value=True) - with patch.dict(slack.__salt__, {'slack.call_hook': mock}): - comt = 'Sent message: slack-message' - ret.update({'comment': comt, 'result': True}) - self.assertDictEqual(slack.post_message(name, - channel=channel, - username=username, - message=message, - webhook=webhook), - ret) + with patch.dict(slack.__salt__, {"slack.call_hook": mock}): + comt = "Sent message: slack-message" + ret.update({"comment": comt, "result": True}) + self.assertDictEqual( + slack.post_message( + name, + channel=channel, + username=username, + message=message, + webhook=webhook, + ), + ret, + ) diff --git a/tests/unit/states/test_smartos.py b/tests/unit/states/test_smartos.py index 65e6d419081..fa204150510 100644 --- a/tests/unit/states/test_smartos.py +++ b/tests/unit/states/test_smartos.py @@ -9,35 +9,35 @@ from salt.utils.odict import OrderedDict # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import patch +from tests.support.unit import TestCase class SmartOsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ TestCase for salt.states.smartos - ''' + """ def setup_loader_modules(self): - return {smartos: { - '__opts__': {'test': False}} - } + return {smartos: {"__opts__": {"test": False}}} def test_config_present_does_not_exist(self): - ''' + """ Test salt.states.smartos.config_present when the config files does not exist - ''' - name = 'test' - value = 'test_value' - with patch('os.path.isfile', return_value=False): - with patch('salt.utils.atomicfile.atomic_open', side_effect=IOError): + """ + name = "test" + value = "test_value" + with patch("os.path.isfile", return_value=False): + with patch("salt.utils.atomicfile.atomic_open", side_effect=IOError): ret = smartos.config_present(name=name, value=value) - assert not ret['result'] - assert ret['comment'] == 'Could not add property {0} with value "{1}" to config'.format(name, value) + assert not ret["result"] + assert ret[ + "comment" + ] == 'Could not add property {0} with value "{1}" to config'.format(name, value) def test_parse_vmconfig_vrrp(self): - ''' + """ Test _parse_vmconfig's vrid -> mac convertor SmartOS will always use a mac based on the vrrp_vrid, @@ -47,34 +47,52 @@ class SmartOsTestCase(TestCase, LoaderModuleMockMixin): Doing so ensures that 'old' nics are removed and 'new' nics get added as these actions are keyed on the mac property. - ''' + """ # NOTE: vmconfig is not a full vmadm payload, # this is not an issue given we are only testing # the vrrp_vrid to mac conversions ret = smartos._parse_vmconfig( - OrderedDict([ - ('nics', OrderedDict([ - ('00:00:5e:00:01:01', OrderedDict([ - ('vrrp_vrid', 1), - ('vrrp_primary_ip', '12.34.5.6'), - ])), - ('00:00:5e:00:01:24', OrderedDict([ - ('vrrp_vrid', 240), - ('vrrp_primary_ip', '12.34.5.6'), - ])), - ('00:22:06:00:00:01', OrderedDict([ - ('ips', ['12.34.5.6/24']), - ])) - ])) - ]), - {'nics': 'mac'}, + OrderedDict( + [ + ( + "nics", + OrderedDict( + [ + ( + "00:00:5e:00:01:01", + OrderedDict( + [ + ("vrrp_vrid", 1), + ("vrrp_primary_ip", "12.34.5.6"), + ] + ), + ), + ( + "00:00:5e:00:01:24", + OrderedDict( + [ + ("vrrp_vrid", 240), + ("vrrp_primary_ip", "12.34.5.6"), + ] + ), + ), + ( + "00:22:06:00:00:01", + OrderedDict([("ips", ["12.34.5.6/24"])]), + ), + ] + ), + ) + ] + ), + {"nics": "mac"}, ) # NOTE: nics.0 is a vrrp nic with correct mac (check mac == vrid based -> unchanged) - assert ret['nics'][0]['mac'] == '00:00:5e:00:01:01' + assert ret["nics"][0]["mac"] == "00:00:5e:00:01:01" # NOTE: nics.1 is a vrrp nic with incorrect mac (check mac == vrid based -> changed) - assert ret['nics'][1]['mac'] == '00:00:5e:00:01:f0' + assert ret["nics"][1]["mac"] == "00:00:5e:00:01:f0" # NOTE: nics.2 was not a vrrp nic (check mac was not changed) - assert ret['nics'][2]['mac'] == '00:22:06:00:00:01' + assert ret["nics"][2]["mac"] == "00:22:06:00:00:01" diff --git a/tests/unit/states/test_smtp.py b/tests/unit/states/test_smtp.py index 9dd8693ed8a..ab1ed97d69f 100644 --- a/tests/unit/states/test_smtp.py +++ b/tests/unit/states/test_smtp.py @@ -1,62 +1,67 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.smtp as smtp +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SmtpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.smtp - ''' + """ + def setup_loader_modules(self): return {smtp: {}} # 'send_msg' function tests: 1 def test_send_msg(self): - ''' + """ Test to send a message via SMTP - ''' - name = 'This is a salt states module' + """ + name = "This is a salt states module" - comt = ('Need to send message to admin@example.com:' - ' This is a salt states module') + comt = ( + "Need to send message to admin@example.com:" " This is a salt states module" + ) - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': comt} + ret = {"name": name, "changes": {}, "result": None, "comment": comt} - with patch.dict(smtp.__opts__, {'test': True}): - self.assertDictEqual(smtp.send_msg(name, - 'admin@example.com', - 'Message from Salt', - 'admin@example.com', - 'my-smtp-account'), ret) + with patch.dict(smtp.__opts__, {"test": True}): + self.assertDictEqual( + smtp.send_msg( + name, + "admin@example.com", + "Message from Salt", + "admin@example.com", + "my-smtp-account", + ), + ret, + ) - comt = ('Sent message to admin@example.com: ' - 'This is a salt states module') + comt = "Sent message to admin@example.com: " "This is a salt states module" - with patch.dict(smtp.__opts__, {'test': False}): + with patch.dict(smtp.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.dict(smtp.__salt__, {'smtp.send_msg': mock}): - ret['comment'] = comt - ret['result'] = True - self.assertDictEqual(smtp.send_msg(name, - 'admin@example.com', - 'Message from Salt', - 'admin@example.com', - 'my-smtp-account'), ret) + with patch.dict(smtp.__salt__, {"smtp.send_msg": mock}): + ret["comment"] = comt + ret["result"] = True + self.assertDictEqual( + smtp.send_msg( + name, + "admin@example.com", + "Message from Salt", + "admin@example.com", + "my-smtp-account", + ), + ret, + ) diff --git a/tests/unit/states/test_splunk_search.py b/tests/unit/states/test_splunk_search.py index 7e4824f3dc8..9584e30c9a2 100644 --- a/tests/unit/states/test_splunk_search.py +++ b/tests/unit/states/test_splunk_search.py @@ -1,79 +1,78 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.splunk_search as splunk_search +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SplunkSearchTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.splunk_search - ''' + """ + def setup_loader_modules(self): return {splunk_search: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure a search is present. - ''' - name = 'API Error Search' + """ + name = "API Error Search" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} mock = MagicMock(side_effect=[True, False, False, True]) - with patch.dict(splunk_search.__salt__, {'splunk_search.get': mock, - 'splunk_search.create': mock}): - with patch.dict(splunk_search.__opts__, {'test': True}): - comt = ("Would update {0}".format(name)) - ret.update({'comment': comt}) + with patch.dict( + splunk_search.__salt__, + {"splunk_search.get": mock, "splunk_search.create": mock}, + ): + with patch.dict(splunk_search.__opts__, {"test": True}): + comt = "Would update {0}".format(name) + ret.update({"comment": comt}) self.assertDictEqual(splunk_search.present(name), ret) - comt = ("Would create {0}".format(name)) - ret.update({'comment': comt}) + comt = "Would create {0}".format(name) + ret.update({"comment": comt}) self.assertDictEqual(splunk_search.present(name), ret) - with patch.dict(splunk_search.__opts__, {'test': False}): - ret.update({'comment': '', 'result': True, - 'changes': {'new': {}, 'old': False}}) + with patch.dict(splunk_search.__opts__, {"test": False}): + ret.update( + { + "comment": "", + "result": True, + "changes": {"new": {}, "old": False}, + } + ) self.assertDictEqual(splunk_search.present(name), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to ensure a search is absent. - ''' - name = 'API Error Search' + """ + name = "API Error Search" - ret = {'name': name, - 'result': None, - 'comment': ''} + ret = {"name": name, "result": None, "comment": ""} mock = MagicMock(side_effect=[True, False]) - with patch.dict(splunk_search.__salt__, {'splunk_search.get': mock}): - with patch.dict(splunk_search.__opts__, {'test': True}): - comt = ("Would delete {0}".format(name)) - ret.update({'comment': comt}) + with patch.dict(splunk_search.__salt__, {"splunk_search.get": mock}): + with patch.dict(splunk_search.__opts__, {"test": True}): + comt = "Would delete {0}".format(name) + ret.update({"comment": comt}) self.assertDictEqual(splunk_search.absent(name), ret) - comt = ('{0} is absent.'.format(name)) - ret.update({'comment': comt, 'result': True, - 'changes': {}}) + comt = "{0} is absent.".format(name) + ret.update({"comment": comt, "result": True, "changes": {}}) self.assertDictEqual(splunk_search.absent(name), ret) diff --git a/tests/unit/states/test_ssh_auth.py b/tests/unit/states/test_ssh_auth.py index 3039daaa3df..78c630c2626 100644 --- a/tests/unit/states/test_ssh_auth.py +++ b/tests/unit/states/test_ssh_auth.py @@ -1,176 +1,189 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.ssh_auth as ssh_auth +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SshAuthTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ssh_auth - ''' + """ + def setup_loader_modules(self): return {ssh_auth: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to verifies that the specified SSH key is present for the specified user. - ''' - name = 'sshkeys' - user = 'root' - source = 'salt://ssh_keys/id_rsa.pub' + """ + name = "sshkeys" + user = "root" + source = "salt://ssh_keys/id_rsa.pub" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - mock = MagicMock(return_value='exists') - mock_data = MagicMock(side_effect=['replace', 'new']) - with patch.dict(ssh_auth.__salt__, {'ssh.check_key': mock, - 'ssh.set_auth_key': mock_data}): - with patch.dict(ssh_auth.__opts__, {'test': True}): - comt = ('The authorized host key sshkeys is already ' - 'present for user root') - ret.update({'comment': comt}) + mock = MagicMock(return_value="exists") + mock_data = MagicMock(side_effect=["replace", "new"]) + with patch.dict( + ssh_auth.__salt__, {"ssh.check_key": mock, "ssh.set_auth_key": mock_data} + ): + with patch.dict(ssh_auth.__opts__, {"test": True}): + comt = ( + "The authorized host key sshkeys is already " + "present for user root" + ) + ret.update({"comment": comt}) self.assertDictEqual(ssh_auth.present(name, user, source), ret) - with patch.dict(ssh_auth.__opts__, {'test': False}): - comt = ('The authorized host key sshkeys ' - 'for user root was updated') - ret.update({'comment': comt, 'changes': {name: 'Updated'}}) + with patch.dict(ssh_auth.__opts__, {"test": False}): + comt = "The authorized host key sshkeys " "for user root was updated" + ret.update({"comment": comt, "changes": {name: "Updated"}}) self.assertDictEqual(ssh_auth.present(name, user, source), ret) - comt = ('The authorized host key sshkeys ' - 'for user root was added') - ret.update({'comment': comt, 'changes': {name: 'New'}}) + comt = "The authorized host key sshkeys " "for user root was added" + ret.update({"comment": comt, "changes": {name: "New"}}) self.assertDictEqual(ssh_auth.present(name, user, source), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to verifies that the specified SSH key is absent. - ''' - name = 'sshkeys' - user = 'root' - source = 'salt://ssh_keys/id_rsa.pub' + """ + name = "sshkeys" + user = "root" + source = "salt://ssh_keys/id_rsa.pub" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - mock = MagicMock(side_effect=['User authorized keys file not present', - 'Key removed']) - mock_up = MagicMock(side_effect=['update', 'updated']) - with patch.dict(ssh_auth.__salt__, {'ssh.rm_auth_key': mock, - 'ssh.check_key': mock_up}): - with patch.dict(ssh_auth.__opts__, {'test': True}): - comt = ('Key sshkeys for user root is set for removal') - ret.update({'comment': comt}) + mock = MagicMock( + side_effect=["User authorized keys file not present", "Key removed"] + ) + mock_up = MagicMock(side_effect=["update", "updated"]) + with patch.dict( + ssh_auth.__salt__, {"ssh.rm_auth_key": mock, "ssh.check_key": mock_up} + ): + with patch.dict(ssh_auth.__opts__, {"test": True}): + comt = "Key sshkeys for user root is set for removal" + ret.update({"comment": comt}) self.assertDictEqual(ssh_auth.absent(name, user, source), ret) - comt = ('Key is already absent') - ret.update({'comment': comt, 'result': True}) + comt = "Key is already absent" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ssh_auth.absent(name, user, source), ret) - with patch.dict(ssh_auth.__opts__, {'test': False}): - comt = ('User authorized keys file not present') - ret.update({'comment': comt, 'result': False}) + with patch.dict(ssh_auth.__opts__, {"test": False}): + comt = "User authorized keys file not present" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(ssh_auth.absent(name, user, source), ret) - comt = ('Key removed') - ret.update({'comment': comt, 'result': True, - 'changes': {name: 'Removed'}}) + comt = "Key removed" + ret.update( + {"comment": comt, "result": True, "changes": {name: "Removed"}} + ) self.assertDictEqual(ssh_auth.absent(name, user, source), ret) def test_manage(self): - ''' + """ Test to verifies that the specified SSH key is absent. - ''' - user = 'root' - ret = {'name': '', - 'changes': {}, - 'result': None, - 'comment': ''} + """ + user = "root" + ret = {"name": "", "changes": {}, "result": None, "comment": ""} - mock_rm = MagicMock(side_effect=['User authorized keys file not present', - 'Key removed']) - mock_up = MagicMock(side_effect=['update', 'updated']) - mock_set = MagicMock(side_effect=['replace', 'new']) - mock_keys = MagicMock(return_value={'somekey': { - "enc": "ssh-rsa", - "comment": "user@host", - "options": [], - "fingerprint": "b7"}}) - with patch.dict(ssh_auth.__salt__, {'ssh.rm_auth_key': mock_rm, - 'ssh.set_auth_key': mock_set, - 'ssh.check_key': mock_up, - 'ssh.auth_keys': mock_keys}): - with patch('salt.states.ssh_auth.present') as call_mocked_present: - mock_present = {'comment': '', - 'changes': {}, - 'result': None - } + mock_rm = MagicMock( + side_effect=["User authorized keys file not present", "Key removed"] + ) + mock_up = MagicMock(side_effect=["update", "updated"]) + mock_set = MagicMock(side_effect=["replace", "new"]) + mock_keys = MagicMock( + return_value={ + "somekey": { + "enc": "ssh-rsa", + "comment": "user@host", + "options": [], + "fingerprint": "b7", + } + } + ) + with patch.dict( + ssh_auth.__salt__, + { + "ssh.rm_auth_key": mock_rm, + "ssh.set_auth_key": mock_set, + "ssh.check_key": mock_up, + "ssh.auth_keys": mock_keys, + }, + ): + with patch("salt.states.ssh_auth.present") as call_mocked_present: + mock_present = {"comment": "", "changes": {}, "result": None} call_mocked_present.return_value = mock_present - with patch.dict(ssh_auth.__opts__, {'test': True}): + with patch.dict(ssh_auth.__opts__, {"test": True}): # test: expected keys found. No chanages - self.assertDictEqual(ssh_auth.manage('sshid', ['somekey'], user), ret) + self.assertDictEqual( + ssh_auth.manage("sshid", ["somekey"], user), ret + ) - comt = ('somekey Key set for removal') - ret.update({'comment': comt}) + comt = "somekey Key set for removal" + ret.update({"comment": comt}) # test: unexpected sshkey found. Should be removed. - self.assertDictEqual(ssh_auth.manage('sshid', [], user), ret) + self.assertDictEqual(ssh_auth.manage("sshid", [], user), ret) - with patch('salt.states.ssh_auth.present') as call_mocked_present: - mock_present = {'comment': '', - 'changes': {}, - 'result': True - } + with patch("salt.states.ssh_auth.present") as call_mocked_present: + mock_present = {"comment": "", "changes": {}, "result": True} call_mocked_present.return_value = mock_present - with patch.dict(ssh_auth.__opts__, {'test': False}): + with patch.dict(ssh_auth.__opts__, {"test": False}): # expected keys found. No changes - ret = {'name': '', - 'changes': {}, - 'result': True, - 'comment': ''} - self.assertDictEqual(ssh_auth.manage('sshid', ['somekey'], user), ret) + ret = {"name": "", "changes": {}, "result": True, "comment": ""} + self.assertDictEqual( + ssh_auth.manage("sshid", ["somekey"], user), ret + ) - with patch('salt.states.ssh_auth.absent') as call_mocked_absent: - mock_absent = {'comment': 'Key removed'} + with patch("salt.states.ssh_auth.absent") as call_mocked_absent: + mock_absent = {"comment": "Key removed"} call_mocked_absent.return_value = mock_absent - ret.update({'comment': '', 'result': True, - 'changes': {'somekey': 'Key removed'}}) + ret.update( + { + "comment": "", + "result": True, + "changes": {"somekey": "Key removed"}, + } + ) # unexpected sshkey found. Was removed. - self.assertDictEqual(ssh_auth.manage('sshid', ['addkey'], user), ret) + self.assertDictEqual( + ssh_auth.manage("sshid", ["addkey"], user), ret + ) # add a key - with patch('salt.states.ssh_auth.present') as call_mocked_present: - mock_present = {'comment': 'The authorized host key newkey for user {} was added'.format(user), - 'changes': {'newkey': 'New'}, - 'result': True - } + with patch("salt.states.ssh_auth.present") as call_mocked_present: + mock_present = { + "comment": "The authorized host key newkey for user {} was added".format( + user + ), + "changes": {"newkey": "New"}, + "result": True, + } call_mocked_present.return_value = mock_present - with patch.dict(ssh_auth.__opts__, {'test': False}): + with patch.dict(ssh_auth.__opts__, {"test": False}): # added key newkey - ret = {'name': '', - 'changes': {'newkey': 'New'}, - 'result': True, - 'comment': ''} - self.assertDictEqual(ssh_auth.manage('sshid', ['newkey', 'somekey'], user), ret) + ret = { + "name": "", + "changes": {"newkey": "New"}, + "result": True, + "comment": "", + } + self.assertDictEqual( + ssh_auth.manage("sshid", ["newkey", "somekey"], user), ret + ) diff --git a/tests/unit/states/test_ssh_known_hosts.py b/tests/unit/states/test_ssh_known_hosts.py index a8a9a709915..e210abfadeb 100644 --- a/tests/unit/states/test_ssh_known_hosts.py +++ b/tests/unit/states/test_ssh_known_hosts.py @@ -1,167 +1,161 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +import os # Import Salt Libs import salt.states.ssh_known_hosts as ssh_known_hosts +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SshKnownHostsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.ssh_known_hosts - ''' + """ + def setup_loader_modules(self): return {ssh_known_hosts: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to verifies that the specified host is known by the specified user. - ''' - name = 'github.com' - user = 'root' - key = '16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48' + """ + name = "github.com" + user = "root" + key = "16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48" fingerprint = [key] - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - with patch.dict(ssh_known_hosts.__opts__, {'test': True}): - with patch.object(os.path, 'isabs', MagicMock(return_value=False)): - comt = ('If not specifying a "user", ' - 'specify an absolute "config".') - ret.update({'comment': comt}) + with patch.dict(ssh_known_hosts.__opts__, {"test": True}): + with patch.object(os.path, "isabs", MagicMock(return_value=False)): + comt = 'If not specifying a "user", ' 'specify an absolute "config".' + ret.update({"comment": comt}) self.assertDictEqual(ssh_known_hosts.present(name), ret) - comt = ('Specify either "key" or "fingerprint", not both.') - ret.update({'comment': comt}) - self.assertDictEqual(ssh_known_hosts.present(name, user, key=key, - fingerprint=[key]), - ret) + comt = 'Specify either "key" or "fingerprint", not both.' + ret.update({"comment": comt}) + self.assertDictEqual( + ssh_known_hosts.present(name, user, key=key, fingerprint=[key]), ret + ) - comt = ('Required argument "enc" if using "key" argument.') - ret.update({'comment': comt}) - self.assertDictEqual(ssh_known_hosts.present(name, user, key=key), - ret) + comt = 'Required argument "enc" if using "key" argument.' + ret.update({"comment": comt}) + self.assertDictEqual(ssh_known_hosts.present(name, user, key=key), ret) - mock = MagicMock(side_effect=['exists', 'add', 'update']) - with patch.dict(ssh_known_hosts.__salt__, - {'ssh.check_known_host': mock}): - comt = ('Host github.com is already in .ssh/known_hosts') - ret.update({'comment': comt, 'result': True}) + mock = MagicMock(side_effect=["exists", "add", "update"]) + with patch.dict(ssh_known_hosts.__salt__, {"ssh.check_known_host": mock}): + comt = "Host github.com is already in .ssh/known_hosts" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ssh_known_hosts.present(name, user), ret) - comt = ('Key for github.com is set to be' - ' added to .ssh/known_hosts') - ret.update({'comment': comt, 'result': None}) + comt = "Key for github.com is set to be" " added to .ssh/known_hosts" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(ssh_known_hosts.present(name, user), ret) - comt = ('Key for github.com is set to be ' - 'updated in .ssh/known_hosts') - ret.update({'comment': comt}) + comt = "Key for github.com is set to be " "updated in .ssh/known_hosts" + ret.update({"comment": comt}) self.assertDictEqual(ssh_known_hosts.present(name, user), ret) - with patch.dict(ssh_known_hosts.__opts__, {'test': False}): - result = {'status': 'exists', 'error': ''} + with patch.dict(ssh_known_hosts.__opts__, {"test": False}): + result = {"status": "exists", "error": ""} mock = MagicMock(return_value=result) - with patch.dict(ssh_known_hosts.__salt__, - {'ssh.set_known_host': mock}): - comt = ('github.com already exists in .ssh/known_hosts') - ret.update({'comment': comt, 'result': True}) + with patch.dict(ssh_known_hosts.__salt__, {"ssh.set_known_host": mock}): + comt = "github.com already exists in .ssh/known_hosts" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ssh_known_hosts.present(name, user), ret) - result = {'status': 'error', 'error': ''} + result = {"status": "error", "error": ""} mock = MagicMock(return_value=result) - with patch.dict(ssh_known_hosts.__salt__, - {'ssh.set_known_host': mock}): - ret.update({'comment': '', 'result': False}) + with patch.dict(ssh_known_hosts.__salt__, {"ssh.set_known_host": mock}): + ret.update({"comment": "", "result": False}) self.assertDictEqual(ssh_known_hosts.present(name, user), ret) - result = {'status': 'updated', 'error': '', - 'new': [{'fingerprint': fingerprint, 'key': key}], - 'old': ''} + result = { + "status": "updated", + "error": "", + "new": [{"fingerprint": fingerprint, "key": key}], + "old": "", + } mock = MagicMock(return_value=result) - with patch.dict(ssh_known_hosts.__salt__, - {'ssh.set_known_host': mock}): - comt = ("{0}'s key saved to .ssh/known_hosts (key: {1})" - .format(name, key)) - ret.update({'comment': comt, 'result': True, - 'changes': {'new': [{'fingerprint': fingerprint, - 'key': key}], 'old': ''}}) - self.assertDictEqual(ssh_known_hosts.present(name, user, - key=key), ret) + with patch.dict(ssh_known_hosts.__salt__, {"ssh.set_known_host": mock}): + comt = "{0}'s key saved to .ssh/known_hosts (key: {1})".format( + name, key + ) + ret.update( + { + "comment": comt, + "result": True, + "changes": { + "new": [{"fingerprint": fingerprint, "key": key}], + "old": "", + }, + } + ) + self.assertDictEqual(ssh_known_hosts.present(name, user, key=key), ret) - comt = ("{0}'s key saved to .ssh/known_hosts (fingerprint: {1})" - .format(name, fingerprint)) - ret.update({'comment': comt}) + comt = "{0}'s key saved to .ssh/known_hosts (fingerprint: {1})".format( + name, fingerprint + ) + ret.update({"comment": comt}) self.assertDictEqual(ssh_known_hosts.present(name, user), ret) # 'absent' function tests: 1 def test_absent(self): - ''' + """ Test to verifies that the specified host is not known by the given user. - ''' - name = 'github.com' - user = 'root' + """ + name = "github.com" + user = "root" - ret = {'name': name, - 'changes': {}, - 'result': False, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": False, "comment": ""} - with patch.object(os.path, 'isabs', MagicMock(return_value=False)): - comt = ('If not specifying a "user", ' - 'specify an absolute "config".') - ret.update({'comment': comt}) + with patch.object(os.path, "isabs", MagicMock(return_value=False)): + comt = 'If not specifying a "user", ' 'specify an absolute "config".' + ret.update({"comment": comt}) self.assertDictEqual(ssh_known_hosts.absent(name), ret) mock = MagicMock(return_value=False) - with patch.dict(ssh_known_hosts.__salt__, - {'ssh.get_known_host_entries': mock}): - comt = ('Host is already absent') - ret.update({'comment': comt, 'result': True}) + with patch.dict(ssh_known_hosts.__salt__, {"ssh.get_known_host_entries": mock}): + comt = "Host is already absent" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(ssh_known_hosts.absent(name, user), ret) mock = MagicMock(return_value=True) - with patch.dict(ssh_known_hosts.__salt__, - {'ssh.get_known_host_entries': mock}): - with patch.dict(ssh_known_hosts.__opts__, {'test': True}): - comt = ('Key for github.com is set to be' - ' removed from .ssh/known_hosts') - ret.update({'comment': comt, 'result': None}) + with patch.dict(ssh_known_hosts.__salt__, {"ssh.get_known_host_entries": mock}): + with patch.dict(ssh_known_hosts.__opts__, {"test": True}): + comt = ( + "Key for github.com is set to be" " removed from .ssh/known_hosts" + ) + ret.update({"comment": comt, "result": None}) self.assertDictEqual(ssh_known_hosts.absent(name, user), ret) - with patch.dict(ssh_known_hosts.__opts__, {'test': False}): - result = {'status': 'error', 'error': ''} + with patch.dict(ssh_known_hosts.__opts__, {"test": False}): + result = {"status": "error", "error": ""} mock = MagicMock(return_value=result) - with patch.dict(ssh_known_hosts.__salt__, - {'ssh.rm_known_host': mock}): - ret.update({'comment': '', 'result': False}) - self.assertDictEqual(ssh_known_hosts.absent(name, user), - ret) + with patch.dict(ssh_known_hosts.__salt__, {"ssh.rm_known_host": mock}): + ret.update({"comment": "", "result": False}) + self.assertDictEqual(ssh_known_hosts.absent(name, user), ret) - result = {'status': 'removed', 'error': '', - 'comment': 'removed'} + result = {"status": "removed", "error": "", "comment": "removed"} mock = MagicMock(return_value=result) - with patch.dict(ssh_known_hosts.__salt__, - {'ssh.rm_known_host': mock}): - ret.update({'comment': 'removed', 'result': True, - 'changes': {'new': None, 'old': True}}) - self.assertDictEqual(ssh_known_hosts.absent(name, user), - ret) + with patch.dict(ssh_known_hosts.__salt__, {"ssh.rm_known_host": mock}): + ret.update( + { + "comment": "removed", + "result": True, + "changes": {"new": None, "old": True}, + } + ) + self.assertDictEqual(ssh_known_hosts.absent(name, user), ret) diff --git a/tests/unit/states/test_status.py b/tests/unit/states/test_status.py index fa0c67bbc30..444fa74c61d 100644 --- a/tests/unit/states/test_status.py +++ b/tests/unit/states/test_status.py @@ -1,88 +1,77 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - # Import Salt Libs import salt.states.status as status +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class StatusTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.status - ''' + """ + def setup_loader_modules(self): return {status: {}} # 'loadavg' function tests: 1 def test_loadavg(self): - ''' + """ Test to return the current load average for the specified minion. - ''' - name = 'mymonitor' + """ + name = "mymonitor" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'data': {}, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "data": {}, "comment": ""} mock = MagicMock(return_value=[]) - with patch.dict(status.__salt__, {'status.loadavg': mock}): - comt = ('Requested load average mymonitor not available ') - ret.update({'comment': comt, 'result': False}) + with patch.dict(status.__salt__, {"status.loadavg": mock}): + comt = "Requested load average mymonitor not available " + ret.update({"comment": comt, "result": False}) self.assertDictEqual(status.loadavg(name), ret) mock = MagicMock(return_value={name: 3}) - with patch.dict(status.__salt__, {'status.loadavg': mock}): - comt = ('Min must be less than max') - ret.update({'comment': comt, 'result': False}) + with patch.dict(status.__salt__, {"status.loadavg": mock}): + comt = "Min must be less than max" + ret.update({"comment": comt, "result": False}) self.assertDictEqual(status.loadavg(name, 1, 5), ret) - comt = ('Load avg is below minimum of 4 at 3.0') - ret.update({'comment': comt, 'data': 3}) + comt = "Load avg is below minimum of 4 at 3.0" + ret.update({"comment": comt, "data": 3}) self.assertDictEqual(status.loadavg(name, 5, 4), ret) - comt = ('Load avg above maximum of 2 at 3.0') - ret.update({'comment': comt, 'data': 3}) + comt = "Load avg above maximum of 2 at 3.0" + ret.update({"comment": comt, "data": 3}) self.assertDictEqual(status.loadavg(name, 2, 1), ret) - comt = ('Load avg in acceptable range') - ret.update({'comment': comt, 'result': True}) + comt = "Load avg in acceptable range" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(status.loadavg(name, 3, 1), ret) # 'process' function tests: 1 def test_process(self): - ''' + """ Test to return whether the specified signature is found in the process tree. - ''' - name = 'mymonitor' + """ + name = "mymonitor" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'data': {}, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "data": {}, "comment": ""} mock = MagicMock(side_effect=[{}, {name: 1}]) - with patch.dict(status.__salt__, {'status.pid': mock}): - comt = ('Process signature "mymonitor" not found ') - ret.update({'comment': comt, 'result': False}) + with patch.dict(status.__salt__, {"status.pid": mock}): + comt = 'Process signature "mymonitor" not found ' + ret.update({"comment": comt, "result": False}) self.assertDictEqual(status.process(name), ret) - comt = ('Process signature "mymonitor" was found ') - ret.update({'comment': comt, 'result': True, - 'data': {name: 1}}) + comt = 'Process signature "mymonitor" was found ' + ret.update({"comment": comt, "result": True, "data": {name: 1}}) self.assertDictEqual(status.process(name), ret) diff --git a/tests/unit/states/test_supervisord.py b/tests/unit/states/test_supervisord.py index 5b363b3ead4..18ccfaff4e1 100644 --- a/tests/unit/states/test_supervisord.py +++ b/tests/unit/states/test_supervisord.py @@ -1,91 +1,82 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.supervisord as supervisord +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SupervisordTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.supervisord - ''' + """ + def setup_loader_modules(self): return {supervisord: {}} # 'running' function tests: 1 def test_running(self): - ''' + """ Test to ensure the named service is running. - ''' - name = 'wsgi_server' + """ + name = "wsgi_server" - ret = {'name': name, - 'changes': {}, - 'result': True, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": True, "comment": ""} - comt = ('Supervisord module not activated.' - ' Do you need to install supervisord?') - ret.update({'comment': comt, 'result': False}) + comt = ( + "Supervisord module not activated." " Do you need to install supervisord?" + ) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(supervisord.running(name), ret) - mock = MagicMock(return_value={name: {'state': 'running'}}) - with patch.dict(supervisord.__salt__, {'supervisord.status': mock}): - with patch.dict(supervisord.__opts__, {'test': True}): - comt = ('Service wsgi_server is already running') - ret.update({'comment': comt, 'result': True}) + mock = MagicMock(return_value={name: {"state": "running"}}) + with patch.dict(supervisord.__salt__, {"supervisord.status": mock}): + with patch.dict(supervisord.__opts__, {"test": True}): + comt = "Service wsgi_server is already running" + ret.update({"comment": comt, "result": True}) self.assertDictEqual(supervisord.running(name), ret) - with patch.dict(supervisord.__opts__, {'test': False}): - comt = ('Not starting already running service: wsgi_server') - ret.update({'comment': comt}) + with patch.dict(supervisord.__opts__, {"test": False}): + comt = "Not starting already running service: wsgi_server" + ret.update({"comment": comt}) self.assertDictEqual(supervisord.running(name), ret) # 'dead' function tests: 1 def test_dead(self): - ''' + """ Test to ensure the named service is dead (not running). - ''' - name = 'wsgi_server' + """ + name = "wsgi_server" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - with patch.dict(supervisord.__opts__, {'test': True}): - comt = ('Service {0} is set to be stopped'.format(name)) - ret.update({'comment': comt}) + with patch.dict(supervisord.__opts__, {"test": True}): + comt = "Service {0} is set to be stopped".format(name) + ret.update({"comment": comt}) self.assertDictEqual(supervisord.dead(name), ret) # 'mod_watch' function tests: 1 def test_mod_watch(self): - ''' + """ Test to always restart on watch. - ''' - name = 'wsgi_server' + """ + name = "wsgi_server" - ret = {'name': name, - 'changes': {}, - 'result': None, - 'comment': ''} + ret = {"name": name, "changes": {}, "result": None, "comment": ""} - comt = ('Supervisord module not activated.' - ' Do you need to install supervisord?') - ret.update({'comment': comt, 'result': False}) + comt = ( + "Supervisord module not activated." " Do you need to install supervisord?" + ) + ret.update({"comment": comt, "result": False}) self.assertDictEqual(supervisord.mod_watch(name), ret) diff --git a/tests/unit/states/test_svn.py b/tests/unit/states/test_svn.py index 9ebd22e2d74..f8ac99d87e1 100644 --- a/tests/unit/states/test_svn.py +++ b/tests/unit/states/test_svn.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os # Import Salt Libs @@ -11,113 +12,117 @@ import salt.states.svn as svn # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class SvnTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the svn state - ''' + """ + def setup_loader_modules(self): return {svn: {}} def test_latest(self): - ''' + """ Checkout or update the working directory to the latest revision from the remote repository. - ''' + """ mock = MagicMock(return_value=True) - with patch.object(svn, '_fail', mock): + with patch.object(svn, "_fail", mock): self.assertTrue(svn.latest("salt")) mock = MagicMock(side_effect=[True, False, False, False]) - with patch.object(os.path, 'exists', mock): + with patch.object(os.path, "exists", mock): mock = MagicMock(return_value=False) - with patch.object(os.path, 'isdir', mock): - with patch.object(svn, '_fail', mock): + with patch.object(os.path, "isdir", mock): + with patch.object(svn, "_fail", mock): self.assertFalse(svn.latest("salt", "c://salt")) - with patch.dict(svn.__opts__, {'test': True}): + with patch.dict(svn.__opts__, {"test": True}): mock = MagicMock(return_value=["salt"]) - with patch.object(svn, '_neutral_test', mock): - self.assertListEqual(svn.latest("salt", "c://salt"), - ['salt']) + with patch.object(svn, "_neutral_test", mock): + self.assertListEqual(svn.latest("salt", "c://salt"), ["salt"]) mock = MagicMock(side_effect=[False, True]) - with patch.object(os.path, 'exists', mock): + with patch.object(os.path, "exists", mock): mock = MagicMock(return_value=True) - info_mock = MagicMock(return_value=[{'Revision': 'mocked'}]) - with patch.dict(svn.__salt__, {'svn.diff': mock, 'svn.info': info_mock}): + info_mock = MagicMock(return_value=[{"Revision": "mocked"}]) + with patch.dict( + svn.__salt__, {"svn.diff": mock, "svn.info": info_mock} + ): mock = MagicMock(return_value=["Dude"]) - with patch.object(svn, '_neutral_test', mock): - self.assertListEqual(svn.latest("salt", - "c://salt"), - ['Dude']) + with patch.object(svn, "_neutral_test", mock): + self.assertListEqual( + svn.latest("salt", "c://salt"), ["Dude"] + ) - with patch.dict(svn.__opts__, {'test': False}): - mock = MagicMock(return_value=[{'Revision': 'a'}]) - with patch.dict(svn.__salt__, {'svn.info': mock}): + with patch.dict(svn.__opts__, {"test": False}): + mock = MagicMock(return_value=[{"Revision": "a"}]) + with patch.dict(svn.__salt__, {"svn.info": mock}): mock = MagicMock(return_value=True) - with patch.dict(svn.__salt__, {'svn.update': mock}): - self.assertDictEqual(svn.latest("salt", "c://salt"), - {'changes': {}, 'comment': True, - 'name': 'salt', 'result': True}) + with patch.dict(svn.__salt__, {"svn.update": mock}): + self.assertDictEqual( + svn.latest("salt", "c://salt"), + { + "changes": {}, + "comment": True, + "name": "salt", + "result": True, + }, + ) def test_export(self): - ''' + """ Test to export a file or directory from an SVN repository - ''' + """ mock = MagicMock(return_value=True) - with patch.object(svn, '_fail', mock): + with patch.object(svn, "_fail", mock): self.assertTrue(svn.export("salt")) mock = MagicMock(side_effect=[True, False, False, False]) - with patch.object(os.path, 'exists', mock): + with patch.object(os.path, "exists", mock): mock = MagicMock(return_value=False) - with patch.object(os.path, 'isdir', mock): - with patch.object(svn, '_fail', mock): + with patch.object(os.path, "isdir", mock): + with patch.object(svn, "_fail", mock): self.assertFalse(svn.export("salt", "c://salt")) - with patch.dict(svn.__opts__, {'test': True}): + with patch.dict(svn.__opts__, {"test": True}): mock = MagicMock(return_value=["salt"]) - with patch.object(svn, '_neutral_test', mock): - self.assertListEqual(svn.export("salt", "c://salt"), - ['salt']) + with patch.object(svn, "_neutral_test", mock): + self.assertListEqual(svn.export("salt", "c://salt"), ["salt"]) mock = MagicMock(side_effect=[False, True]) - with patch.object(os.path, 'exists', mock): + with patch.object(os.path, "exists", mock): mock = MagicMock(return_value=True) - with patch.dict(svn.__salt__, {'svn.list': mock}): + with patch.dict(svn.__salt__, {"svn.list": mock}): mock = MagicMock(return_value=["Dude"]) - with patch.object(svn, '_neutral_test', mock): - self.assertListEqual(svn.export("salt", - "c://salt"), - ['Dude']) + with patch.object(svn, "_neutral_test", mock): + self.assertListEqual( + svn.export("salt", "c://salt"), ["Dude"] + ) - with patch.dict(svn.__opts__, {'test': False}): + with patch.dict(svn.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.dict(svn.__salt__, {'svn.export': mock}): + with patch.dict(svn.__salt__, {"svn.export": mock}): self.assertDictEqual( svn.export("salt", "c://salt"), { - 'changes': { - 'new': 'salt', - 'comment': 'salt was Exported to c://salt', + "changes": { + "new": "salt", + "comment": "salt was Exported to c://salt", }, - 'comment': '', - 'name': 'salt', - 'result': True, - } + "comment": "", + "name": "salt", + "result": True, + }, ) def test_dirty(self): - ''' + """ Test to determine if the working directory has been changed. - ''' + """ mock = MagicMock(return_value=True) - with patch.object(svn, '_fail', mock): + with patch.object(svn, "_fail", mock): self.assertTrue(svn.dirty("salt", "c://salt")) diff --git a/tests/unit/states/test_sysctl.py b/tests/unit/states/test_sysctl.py index 1bf55191763..b17fd5332d8 100644 --- a/tests/unit/states/test_sysctl.py +++ b/tests/unit/states/test_sysctl.py @@ -1,134 +1,144 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) - -from salt.exceptions import CommandExecutionError +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.sysctl as sysctl +from salt.exceptions import CommandExecutionError + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class SysctlTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.sysctl - ''' + """ + def setup_loader_modules(self): return {sysctl: {}} # 'present' function tests: 1 def test_present(self): - ''' + """ Test to ensure that the named sysctl value is set in memory and persisted to the named configuration file. - ''' - name = 'net.ipv4.conf.all.rp_filter' - value = '1' - config = '/etc/sysctl.conf' + """ + name = "net.ipv4.conf.all.rp_filter" + value = "1" + config = "/etc/sysctl.conf" - comment = ('Sysctl option {0} might be changed, we failed to check ' - 'config file at {1}. The file is either unreadable, or ' - 'missing.'.format(name, config)) + comment = ( + "Sysctl option {0} might be changed, we failed to check " + "config file at {1}. The file is either unreadable, or " + "missing.".format(name, config) + ) - ret = {'name': name, 'result': None, 'changes': {}, 'comment': comment} + ret = {"name": name, "result": None, "changes": {}, "comment": comment} - comment_empty = ('Sysctl option {0} would be changed to {1}' - ''.format(name, value)) + comment_empty = "Sysctl option {0} would be changed to {1}" "".format( + name, value + ) - comment1 = ('Sysctl option {0} set to be changed to {1}' - .format(name, value)) + comment1 = "Sysctl option {0} set to be changed to {1}".format(name, value) - comment2 = ('Sysctl value is currently set on the running system but ' - 'not in a config file. Sysctl option {0} set to be ' - 'changed to 2 in config file.'.format(name)) + comment2 = ( + "Sysctl value is currently set on the running system but " + "not in a config file. Sysctl option {0} set to be " + "changed to 2 in config file.".format(name) + ) - comt3 = ('Sysctl value {0} is present in configuration file but is not ' - 'present in the running config. The value {0} is set to be ' - 'changed to {1}'.format(name, value)) + comt3 = ( + "Sysctl value {0} is present in configuration file but is not " + "present in the running config. The value {0} is set to be " + "changed to {1}".format(name, value) + ) - comt4 = ('Sysctl value {0} = {1} is already set'.format(name, value)) + comt4 = "Sysctl value {0} = {1} is already set".format(name, value) - comt5 = ('Sysctl option {0} would be changed to {1}' - .format(name, value)) + comt5 = "Sysctl option {0} would be changed to {1}".format(name, value) - comt6 = ('Failed to set {0} to {1}: '.format(name, value)) + comt6 = "Failed to set {0} to {1}: ".format(name, value) - comt7 = ('Sysctl value {0} = {1} is already set'.format(name, value)) + comt7 = "Sysctl value {0} = {1} is already set".format(name, value) + + comt8 = "Sysctl value {0} = {1} was ignored".format(name, value) def mock_current(config_file=None): - ''' + """ Mock return value for __salt__. - ''' + """ if config_file is None: - return {name: '2'} - return [''] + return {name: "2"} + return [""] def mock_config(config_file=None): - ''' + """ Mock return value for __salt__. - ''' + """ if config_file is None: - return {'salt': '2'} + return {"salt": "2"} return [name] def mock_both(config_file=None): - ''' + """ Mock return value for __salt__. - ''' + """ if config_file is None: return {name: value} return [name] - with patch.dict(sysctl.__opts__, {'test': True}): + with patch.dict(sysctl.__opts__, {"test": True}): mock = MagicMock(return_value=None) - with patch.dict(sysctl.__salt__, {'sysctl.show': mock}): + with patch.dict(sysctl.__salt__, {"sysctl.show": mock}): self.assertDictEqual(sysctl.present(name, value), ret) mock = MagicMock(return_value=[]) - with patch.dict(sysctl.__salt__, {'sysctl.show': mock}): - ret.update({'comment': comment_empty}) + with patch.dict(sysctl.__salt__, {"sysctl.show": mock}): + ret.update({"comment": comment_empty}) self.assertDictEqual(sysctl.present(name, value), ret) - with patch.dict(sysctl.__salt__, {'sysctl.show': mock_current}): - ret.update({'comment': comment1}) + with patch.dict(sysctl.__salt__, {"sysctl.show": mock_current}): + ret.update({"comment": comment1}) self.assertDictEqual(sysctl.present(name, value), ret) - ret.update({'comment': comment2}) - self.assertDictEqual(sysctl.present(name, '2'), ret) + ret.update({"comment": comment2}) + self.assertDictEqual(sysctl.present(name, "2"), ret) - with patch.dict(sysctl.__salt__, {'sysctl.show': mock_config}): - ret.update({'comment': comt3}) + with patch.dict(sysctl.__salt__, {"sysctl.show": mock_config}): + ret.update({"comment": comt3}) self.assertDictEqual(sysctl.present(name, value), ret) mock = MagicMock(return_value=value) - with patch.dict(sysctl.__salt__, {'sysctl.show': mock_both, - 'sysctl.get': mock}): - ret.update({'comment': comt4, 'result': True}) + with patch.dict( + sysctl.__salt__, {"sysctl.show": mock_both, "sysctl.get": mock} + ): + ret.update({"comment": comt4, "result": True}) self.assertDictEqual(sysctl.present(name, value), ret) mock = MagicMock(return_value=[True]) - with patch.dict(sysctl.__salt__, {'sysctl.show': mock}): - ret.update({'comment': comt5, 'result': None}) + with patch.dict(sysctl.__salt__, {"sysctl.show": mock}): + ret.update({"comment": comt5, "result": None}) self.assertDictEqual(sysctl.present(name, value), ret) - with patch.dict(sysctl.__opts__, {'test': False}): + with patch.dict(sysctl.__opts__, {"test": False}): mock = MagicMock(side_effect=CommandExecutionError) - with patch.dict(sysctl.__salt__, {'sysctl.persist': mock}): - ret.update({'comment': comt6, 'result': False}) + with patch.dict(sysctl.__salt__, {"sysctl.persist": mock}): + ret.update({"comment": comt6, "result": False}) self.assertDictEqual(sysctl.present(name, value), ret) - mock = MagicMock(return_value='Already set') - with patch.dict(sysctl.__salt__, {'sysctl.persist': mock}): - ret.update({'comment': comt7, 'result': True}) + mock = MagicMock(return_value="Already set") + with patch.dict(sysctl.__salt__, {"sysctl.persist": mock}): + ret.update({"comment": comt7, "result": True}) + self.assertDictEqual(sysctl.present(name, value), ret) + + mock = MagicMock(return_value="Ignored") + with patch.dict(sysctl.__salt__, {"sysctl.persist": mock}): + ret.update({"comment": comt8, "result": True}) self.assertDictEqual(sysctl.present(name, value), ret) diff --git a/tests/unit/states/test_syslog_ng.py b/tests/unit/states/test_syslog_ng.py index 929f4353032..ea329927fe6 100644 --- a/tests/unit/states/test_syslog_ng.py +++ b/tests/unit/states/test_syslog_ng.py @@ -1,22 +1,22 @@ # -*- coding: utf-8 -*- -''' +""" Test module for syslog_ng state -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os import re import tempfile -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch - +import salt.modules.syslog_ng as syslog_ng_module +import salt.states.syslog_ng as syslog_ng import salt.utils.files import salt.utils.yaml -import salt.states.syslog_ng as syslog_ng -import salt.modules.syslog_ng as syslog_ng_module +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase SOURCE_1_CONFIG = { "id": "s_tail", @@ -29,11 +29,11 @@ SOURCE_1_CONFIG = { - flags: - no-parse - validate-utf8 - """) + """ + ), } -SOURCE_1_EXPECTED = ( -""" +SOURCE_1_EXPECTED = """ source s_tail { file( "/var/log/apache/access.log", @@ -42,7 +42,6 @@ source s_tail { ); }; """ -) SOURCE_2_CONFIG = { "id": "s_gsoc2014", @@ -54,11 +53,10 @@ SOURCE_2_CONFIG = { - port: 1234 - flags: no-parse """ - ) + ), } -SOURCE_2_EXPECTED = ( -""" +SOURCE_2_EXPECTED = """ source s_gsoc2014 { tcp( ip("0.0.0.0"), @@ -66,7 +64,6 @@ source s_gsoc2014 { flags(no-parse) ); };""" -) FILTER_1_CONFIG = { "id": "f_json", @@ -76,18 +73,16 @@ FILTER_1_CONFIG = { - match: - '"@json:"' """ - ) + ), } -FILTER_1_EXPECTED = ( - """ +FILTER_1_EXPECTED = """ filter f_json { match( "@json:" ); }; """ -) TEMPLATE_1_CONFIG = { "id": "t_demo_filetemplate", @@ -99,11 +94,10 @@ TEMPLATE_1_CONFIG = { - template_escape: - "no" """ - ) + ), } -TEMPLATE_1_EXPECTED = ( - """ +TEMPLATE_1_EXPECTED = """ template t_demo_filetemplate { template( "$ISODATE $HOST $MSG " @@ -113,7 +107,6 @@ TEMPLATE_1_EXPECTED = ( ); }; """ -) REWRITE_1_CONFIG = { "id": "r_set_message_to_MESSAGE", @@ -124,11 +117,10 @@ REWRITE_1_CONFIG = { - '"${.json.message}"' - value : '"$MESSAGE"' """ - ) + ), } -REWRITE_1_EXPECTED = ( - """ +REWRITE_1_EXPECTED = """ rewrite r_set_message_to_MESSAGE { set( "${.json.message}", @@ -136,7 +128,6 @@ REWRITE_1_EXPECTED = ( ); }; """ -) LOG_1_CONFIG = { "id": "l_gsoc2014", @@ -166,11 +157,10 @@ LOG_1_CONFIG = { - '"/tmp/all.log"' - template: t_gsoc2014 """ - ) + ), } -LOG_1_EXPECTED = ( - """ +LOG_1_EXPECTED = """ log { source(s_gsoc2014); junction { @@ -206,7 +196,6 @@ LOG_1_EXPECTED = ( }; }; """ -) OPTIONS_1_CONFIG = { "id": "global_options", @@ -217,18 +206,16 @@ OPTIONS_1_CONFIG = { - mark_freq: 10 - keep_hostname: "yes" """ - ) + ), } -OPTIONS_1_EXPECTED = ( - """ +OPTIONS_1_EXPECTED = """ options { time_reap(30); mark_freq(10); keep_hostname(yes); }; """ -) SHORT_FORM_CONFIG = { "id": "source.s_gsoc", @@ -239,11 +226,10 @@ SHORT_FORM_CONFIG = { - port: 1234 - flags: no-parse """ - ) + ), } -SHORT_FORM_EXPECTED = ( - """ +SHORT_FORM_EXPECTED = """ source s_gsoc { tcp( ip( @@ -258,11 +244,10 @@ SHORT_FORM_EXPECTED = ( ); }; """ -) GIVEN_CONFIG = { - 'id': "config.some_name", - 'config': ( + "id": "config.some_name", + "config": ( """ source s_gsoc { tcp( @@ -278,16 +263,16 @@ GIVEN_CONFIG = { ); }; """ - ) + ), } _SALT_VAR_WITH_MODULE_METHODS = { - 'syslog_ng.config': syslog_ng_module.config, - 'syslog_ng.start': syslog_ng_module.start, - 'syslog_ng.reload': syslog_ng_module.reload_, - 'syslog_ng.stop': syslog_ng_module.stop, - 'syslog_ng.write_version': syslog_ng_module.write_version, - 'syslog_ng.write_config': syslog_ng_module.write_config + "syslog_ng.config": syslog_ng_module.config, + "syslog_ng.start": syslog_ng_module.start, + "syslog_ng.reload": syslog_ng_module.reload_, + "syslog_ng.stop": syslog_ng_module.stop, + "syslog_ng.write_version": syslog_ng_module.write_version, + "syslog_ng.write_config": syslog_ng_module.write_config, } @@ -297,10 +282,7 @@ def remove_whitespaces(source): class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): - return { - syslog_ng: {}, - syslog_ng_module: {'__opts__': {'test': False}} - } + return {syslog_ng: {}, syslog_ng_module: {"__opts__": {"test": False}}} def test_generate_source_config(self): self._config_generator_template(SOURCE_1_CONFIG, SOURCE_1_EXPECTED) @@ -330,7 +312,9 @@ class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): self._config_generator_template(GIVEN_CONFIG, SHORT_FORM_EXPECTED) def _config_generator_template(self, yaml_input, expected): - parsed_yaml_config = salt.utils.data.decode(salt.utils.yaml.safe_load(yaml_input["config"])) + parsed_yaml_config = salt.utils.data.decode( + salt.utils.yaml.safe_load(yaml_input["config"]) + ) id = yaml_input["id"] with patch.dict(syslog_ng.__salt__, _SALT_VAR_WITH_MODULE_METHODS): @@ -341,11 +325,20 @@ class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): def test_write_config(self): yaml_inputs = ( - SOURCE_2_CONFIG, SOURCE_1_CONFIG, FILTER_1_CONFIG, TEMPLATE_1_CONFIG, REWRITE_1_CONFIG, LOG_1_CONFIG + SOURCE_2_CONFIG, + SOURCE_1_CONFIG, + FILTER_1_CONFIG, + TEMPLATE_1_CONFIG, + REWRITE_1_CONFIG, + LOG_1_CONFIG, ) expected_outputs = ( - SOURCE_2_EXPECTED, SOURCE_1_EXPECTED, FILTER_1_EXPECTED, TEMPLATE_1_EXPECTED, REWRITE_1_EXPECTED, - LOG_1_EXPECTED + SOURCE_2_EXPECTED, + SOURCE_1_EXPECTED, + FILTER_1_EXPECTED, + TEMPLATE_1_EXPECTED, + REWRITE_1_EXPECTED, + LOG_1_EXPECTED, ) config_file_fd, config_file_name = tempfile.mkstemp() os.close(config_file_fd) @@ -356,7 +349,9 @@ class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): syslog_ng_module.write_config(config='@include "scl.conf"') for i in yaml_inputs: - parsed_yaml_config = salt.utils.data.decode(salt.utils.yaml.safe_load(i["config"])) + parsed_yaml_config = salt.utils.data.decode( + salt.utils.yaml.safe_load(i["config"]) + ) id = i["id"] got = syslog_ng.config(id, config=parsed_yaml_config, write=True) @@ -376,8 +371,11 @@ class SyslogNGTestCase(TestCase, LoaderModuleMockMixin): mock_func = MagicMock(return_value={"retcode": 0, "stdout": "", "pid": 1000}) with patch.dict(syslog_ng.__salt__, _SALT_VAR_WITH_MODULE_METHODS): - with patch.dict(syslog_ng_module.__salt__, {'cmd.run_all': mock_func}): + with patch.dict(syslog_ng_module.__salt__, {"cmd.run_all": mock_func}): got = syslog_ng.started(user="joe", group="users", enable_core=True) command = got["changes"]["new"] self.assertTrue( - command.endswith("syslog-ng --user=joe --group=users --enable-core --cfgfile=/etc/syslog-ng.conf")) + command.endswith( + "syslog-ng --user=joe --group=users --enable-core --cfgfile=/etc/syslog-ng.conf" + ) + ) diff --git a/tests/unit/states/test_sysrc.py b/tests/unit/states/test_sysrc.py index 957023d6e68..6d857060ab0 100644 --- a/tests/unit/states/test_sysrc.py +++ b/tests/unit/states/test_sysrc.py @@ -1,82 +1,86 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.sysrc as sysrc +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class SysrcTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the sysrc state - ''' + """ + def setup_loader_modules(self): return {sysrc: {}} def test_managed(self): - ''' + """ Test to ensure a sysrc variable is set to a specific value. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} - mock = MagicMock(side_effect=[{'key1': {'salt': 'stack'}}, None, None]) + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + mock = MagicMock(side_effect=[{"key1": {"salt": "stack"}}, None, None]) mock1 = MagicMock(return_value=True) - with patch.dict(sysrc.__salt__, {"sysrc.get": mock, - "sysrc.set": mock1}): - ret.update({'comment': 'salt is already set to the desired' - ' value.'}) - self.assertDictEqual(sysrc.managed('salt', 'stack'), ret) + with patch.dict(sysrc.__salt__, {"sysrc.get": mock, "sysrc.set": mock1}): + ret.update({"comment": "salt is already set to the desired" " value."}) + self.assertDictEqual(sysrc.managed("salt", "stack"), ret) with patch.dict(sysrc.__opts__, {"test": True}): - ret.update({'changes': {'new': 'salt = stack will be set.', - 'old': None}, 'comment': 'The value' - ' of "salt" will be changed!', 'result': None}) - self.assertDictEqual(sysrc.managed('salt', 'stack'), ret) + ret.update( + { + "changes": {"new": "salt = stack will be set.", "old": None}, + "comment": "The value" ' of "salt" will be changed!', + "result": None, + } + ) + self.assertDictEqual(sysrc.managed("salt", "stack"), ret) with patch.dict(sysrc.__opts__, {"test": False}): - ret.update({'changes': {'new': True, 'old': None}, - 'comment': 'The value of "salt" was changed!', - 'result': True}) - self.assertDictEqual(sysrc.managed('salt', 'stack'), ret) + ret.update( + { + "changes": {"new": True, "old": None}, + "comment": 'The value of "salt" was changed!', + "result": True, + } + ) + self.assertDictEqual(sysrc.managed("salt", "stack"), ret) def test_absent(self): - ''' + """ Test to ensure a sysrc variable is absent. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[None, True, True]) mock1 = MagicMock(return_value=True) - with patch.dict(sysrc.__salt__, {"sysrc.get": mock, - "sysrc.remove": mock1}): - ret.update({'comment': '"salt" is already absent.'}) - self.assertDictEqual(sysrc.absent('salt'), ret) + with patch.dict(sysrc.__salt__, {"sysrc.get": mock, "sysrc.remove": mock1}): + ret.update({"comment": '"salt" is already absent.'}) + self.assertDictEqual(sysrc.absent("salt"), ret) with patch.dict(sysrc.__opts__, {"test": True}): - ret.update({'changes': {'new': '"salt" will be removed.', - 'old': True}, - 'comment': '"salt" will be removed!', - 'result': None}) - self.assertDictEqual(sysrc.absent('salt'), ret) + ret.update( + { + "changes": {"new": '"salt" will be removed.', "old": True}, + "comment": '"salt" will be removed!', + "result": None, + } + ) + self.assertDictEqual(sysrc.absent("salt"), ret) with patch.dict(sysrc.__opts__, {"test": False}): - ret.update({'changes': {'new': True, 'old': True}, - 'comment': '"salt" was removed!', - 'result': True}) - self.assertDictEqual(sysrc.absent('salt'), ret) + ret.update( + { + "changes": {"new": True, "old": True}, + "comment": '"salt" was removed!', + "result": True, + } + ) + self.assertDictEqual(sysrc.absent("salt"), ret) diff --git a/tests/unit/states/test_test.py b/tests/unit/states/test_test.py index e655379fd6e..d00c1c8b564 100644 --- a/tests/unit/states/test_test.py +++ b/tests/unit/states/test_test.py @@ -1,445 +1,495 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch, - MagicMock, -) +import salt.states.test as test # Import Salt Libs from salt.exceptions import SaltInvocationError -import salt.states.test as test -from salt.utils.odict import OrderedDict from salt.ext import six +from salt.utils.odict import OrderedDict + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class TestTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the test state - ''' + """ + def setup_loader_modules(self): - return {test: {'__low__': {'__reqs__': {'watch': ''}}}} + return {test: {"__low__": {"__reqs__": {"watch": ""}}}} def test_succeed_without_changes(self): - ''' + """ Test to returns successful. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} with patch.dict(test.__opts__, {"test": False}): - ret.update({'comment': 'Success!'}) - self.assertDictEqual(test.succeed_without_changes('salt'), ret) + ret.update({"comment": "Success!"}) + self.assertDictEqual(test.succeed_without_changes("salt"), ret) + + with patch.dict(test.__opts__, {"test": False}): + ret.update({"comment": "A success comment!"}) + self.assertDictEqual( + test.succeed_without_changes("salt", comment="A success comment!"), ret + ) def test_fail_without_changes(self): - ''' + """ Test to returns failure. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} with patch.dict(test.__opts__, {"test": False}): - ret.update({'comment': 'Failure!'}) - self.assertDictEqual(test.fail_without_changes('salt'), ret) + ret.update({"comment": "Failure!"}) + self.assertDictEqual(test.fail_without_changes("salt"), ret) + + with patch.dict(test.__opts__, {"test": False}): + ret.update({"comment": "A failure comment!"}) + self.assertDictEqual( + test.fail_without_changes("salt", comment="A failure comment!"), ret + ) def test_succeed_with_changes(self): - ''' + """ Test to returns successful and changes is not empty - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} with patch.dict(test.__opts__, {"test": False}): - ret.update({'changes': {'testing': {'new': 'Something pretended' - ' to change', - 'old': 'Unchanged'}}, - 'comment': 'Success!', 'result': True}) - self.assertDictEqual(test.succeed_with_changes('salt'), ret) + ret.update( + { + "changes": { + "testing": { + "new": "Something pretended" " to change", + "old": "Unchanged", + } + }, + "comment": "Success!", + "result": True, + } + ) + self.assertDictEqual(test.succeed_with_changes("salt"), ret) + + with patch.dict(test.__opts__, {"test": False}): + ret.update( + { + "changes": { + "testing": { + "new": "Something pretended" " to change", + "old": "Unchanged", + } + }, + "comment": "A success comment!", + "result": True, + } + ) + self.assertDictEqual( + test.succeed_with_changes("salt", comment="A success comment!"), ret + ) def test_fail_with_changes(self): - ''' + """ Test to returns failure and changes is not empty. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} with patch.dict(test.__opts__, {"test": False}): - ret.update({'changes': {'testing': {'new': 'Something pretended' - ' to change', - 'old': 'Unchanged'}}, - 'comment': 'Success!', - 'result': True}) - self.assertDictEqual(test.succeed_with_changes('salt'), ret) + ret.update( + { + "changes": { + "testing": { + "new": "Something pretended" " to change", + "old": "Unchanged", + } + }, + "comment": "Success!", + "result": True, + } + ) + self.assertDictEqual(test.succeed_with_changes("salt"), ret) + + with patch.dict(test.__opts__, {"test": False}): + ret.update( + { + "changes": { + "testing": { + "new": "Something pretended" " to change", + "old": "Unchanged", + } + }, + "comment": "A failure comment!", + "result": True, + } + ) + self.assertDictEqual( + test.succeed_with_changes("salt", comment="A failure comment!"), ret + ) def test_configurable_test_state(self): - ''' + """ Test test.configurable_test_state with and without comment - ''' + """ # Configure mock parameters - mock_name = 'cheese_shop' + mock_name = "cheese_shop" mock_comment = "I'm afraid we're fresh out of Red Leicester sir." mock_changes = { - 'testing': { - 'old': 'Unchanged', - 'new': 'Something pretended to change' - } + "testing": {"old": "Unchanged", "new": "Something pretended to change"} } # Test default state with comment - with patch.dict(test.__opts__, {'test': False}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': True, - 'comment': ''} + with patch.dict(test.__opts__, {"test": False}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": True, + "comment": "", + } ret = test.configurable_test_state(mock_name) self.assertDictEqual(ret, mock_ret) # Test default state without comment - with patch.dict(test.__opts__, {'test': False}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': True, - 'comment': mock_comment} - ret = test.configurable_test_state(mock_name, - comment=mock_comment) + with patch.dict(test.__opts__, {"test": False}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": True, + "comment": mock_comment, + } + ret = test.configurable_test_state(mock_name, comment=mock_comment) self.assertDictEqual(ret, mock_ret) def test_configurable_test_state_changes(self): - ''' + """ Test test.configurable_test_state with permutations of changes and with comment - ''' + """ # Configure mock parameters - mock_name = 'cheese_shop' + mock_name = "cheese_shop" mock_comment = "I'm afraid we're fresh out of Red Leicester sir." mock_changes = { - 'testing': { - 'old': 'Unchanged', - 'new': 'Something pretended to change' - } + "testing": {"old": "Unchanged", "new": "Something pretended to change"} } # Test changes=Random and comment - with patch.dict(test.__opts__, {'test': False}): - ret = test.configurable_test_state(mock_name, - changes='Random', - comment=mock_comment) - self.assertEqual(ret['name'], mock_name) - self.assertIn(ret['changes'], [mock_changes, {}]) - self.assertEqual(ret['result'], True) - self.assertEqual(ret['comment'], mock_comment) + with patch.dict(test.__opts__, {"test": False}): + ret = test.configurable_test_state( + mock_name, changes="Random", comment=mock_comment + ) + self.assertEqual(ret["name"], mock_name) + self.assertIn(ret["changes"], [mock_changes, {}]) + self.assertEqual(ret["result"], True) + self.assertEqual(ret["comment"], mock_comment) # Test changes=True and comment - with patch.dict(test.__opts__, {'test': False}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': True, - 'comment': mock_comment} - ret = test.configurable_test_state(mock_name, - changes=True, - comment=mock_comment) + with patch.dict(test.__opts__, {"test": False}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": True, + "comment": mock_comment, + } + ret = test.configurable_test_state( + mock_name, changes=True, comment=mock_comment + ) self.assertDictEqual(ret, mock_ret) # Test changes=False and comment - with patch.dict(test.__opts__, {'test': False}): - mock_ret = {'name': mock_name, - 'changes': {}, - 'result': True, - 'comment': mock_comment} - ret = test.configurable_test_state(mock_name, - changes=False, - comment=mock_comment) + with patch.dict(test.__opts__, {"test": False}): + mock_ret = { + "name": mock_name, + "changes": {}, + "result": True, + "comment": mock_comment, + } + ret = test.configurable_test_state( + mock_name, changes=False, comment=mock_comment + ) self.assertDictEqual(ret, mock_ret) # Test changes=Cheese - with patch.dict(test.__opts__, {'test': False}): - self.assertRaises(SaltInvocationError, - test.configurable_test_state, - mock_name, - changes='Cheese') + with patch.dict(test.__opts__, {"test": False}): + self.assertRaises( + SaltInvocationError, + test.configurable_test_state, + mock_name, + changes="Cheese", + ) def test_configurable_test_state_result(self): - ''' + """ Test test.configurable_test_state with permutations of result and with comment - ''' + """ # Configure mock parameters - mock_name = 'cheese_shop' + mock_name = "cheese_shop" mock_comment = "I'm afraid we're fresh out of Red Leicester sir." mock_changes = { - 'testing': { - 'old': 'Unchanged', - 'new': 'Something pretended to change' - } + "testing": {"old": "Unchanged", "new": "Something pretended to change"} } # Test result=Random and comment - with patch.dict(test.__opts__, {'test': False}): - ret = test.configurable_test_state(mock_name, - result='Random', - comment=mock_comment) - self.assertEqual(ret['name'], mock_name) - self.assertEqual(ret['changes'], mock_changes) - self.assertIn(ret['result'], [True, False]) - self.assertEqual(ret['comment'], mock_comment) + with patch.dict(test.__opts__, {"test": False}): + ret = test.configurable_test_state( + mock_name, result="Random", comment=mock_comment + ) + self.assertEqual(ret["name"], mock_name) + self.assertEqual(ret["changes"], mock_changes) + self.assertIn(ret["result"], [True, False]) + self.assertEqual(ret["comment"], mock_comment) # Test result=True and comment - with patch.dict(test.__opts__, {'test': False}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': True, - 'comment': mock_comment} - ret = test.configurable_test_state(mock_name, - result=True, - comment=mock_comment) + with patch.dict(test.__opts__, {"test": False}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": True, + "comment": mock_comment, + } + ret = test.configurable_test_state( + mock_name, result=True, comment=mock_comment + ) self.assertDictEqual(ret, mock_ret) # Test result=False and comment - with patch.dict(test.__opts__, {'test': False}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': False, - 'comment': mock_comment} - ret = test.configurable_test_state(mock_name, - result=False, - comment=mock_comment) + with patch.dict(test.__opts__, {"test": False}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": False, + "comment": mock_comment, + } + ret = test.configurable_test_state( + mock_name, result=False, comment=mock_comment + ) self.assertDictEqual(ret, mock_ret) # Test result=Cheese - with patch.dict(test.__opts__, {'test': False}): - self.assertRaises(SaltInvocationError, - test.configurable_test_state, - mock_name, - result='Cheese') + with patch.dict(test.__opts__, {"test": False}): + self.assertRaises( + SaltInvocationError, + test.configurable_test_state, + mock_name, + result="Cheese", + ) def test_configurable_test_state_warnings(self): - ''' + """ Test test.configurable_test_state with and without warnings - ''' + """ # Configure mock parameters - mock_name = 'cheese_shop' + mock_name = "cheese_shop" mock_comment = "I'm afraid we're fresh out of Red Leicester sir." - mock_warning = 'Today the van broke down.' - mock_warning_list = [ - mock_warning, - "Oooooooooohhh........!" - ] + mock_warning = "Today the van broke down." + mock_warning_list = [mock_warning, "Oooooooooohhh........!"] mock_changes = { - 'testing': { - 'old': 'Unchanged', - 'new': 'Something pretended to change' - } + "testing": {"old": "Unchanged", "new": "Something pretended to change"} } # Test default state without warnings - with patch.dict(test.__opts__, {'test': False}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': True, - 'comment': ''} + with patch.dict(test.__opts__, {"test": False}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": True, + "comment": "", + } ret = test.configurable_test_state(mock_name) self.assertDictEqual(ret, mock_ret) # Test default state with warnings (string) - with patch.dict(test.__opts__, {'test': False}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': True, - 'comment': '', - 'warnings': mock_warning_list} - ret = test.configurable_test_state(mock_name, - warnings=mock_warning_list) + with patch.dict(test.__opts__, {"test": False}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": True, + "comment": "", + "warnings": mock_warning_list, + } + ret = test.configurable_test_state(mock_name, warnings=mock_warning_list) self.assertDictEqual(ret, mock_ret) # Test default state with warnings (list) - with patch.dict(test.__opts__, {'test': False}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': True, - 'comment': '', - 'warnings': ['Today the van broke down.']} - ret = test.configurable_test_state(mock_name, - warnings=mock_warning) + with patch.dict(test.__opts__, {"test": False}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": True, + "comment": "", + "warnings": ["Today the van broke down."], + } + ret = test.configurable_test_state(mock_name, warnings=mock_warning) self.assertDictEqual(ret, mock_ret) def test_configurable_test_state_test(self): - ''' + """ Test test.configurable_test_state with test=True with and without comment - ''' + """ # Configure mock parameters - mock_name = 'cheese_shop' + mock_name = "cheese_shop" mock_comment = "I'm afraid we're fresh out of Red Leicester sir." mock_changes = { - 'testing': { - 'old': 'Unchanged', - 'new': 'Something pretended to change' - } + "testing": {"old": "Unchanged", "new": "Something pretended to change"} } # Test test=True without comment - with patch.dict(test.__opts__, {'test': True}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': None, - 'comment': 'This is a test'} + with patch.dict(test.__opts__, {"test": True}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": None, + "comment": "This is a test", + } ret = test.configurable_test_state(mock_name) self.assertDictEqual(ret, mock_ret) # Test test=True with comment - with patch.dict(test.__opts__, {'test': True}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': None, - 'comment': mock_comment} - ret = test.configurable_test_state(mock_name, - comment=mock_comment) + with patch.dict(test.__opts__, {"test": True}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": None, + "comment": mock_comment, + } + ret = test.configurable_test_state(mock_name, comment=mock_comment) self.assertDictEqual(ret, mock_ret) # Test test=True and changes=True with comment - with patch.dict(test.__opts__, {'test': True}): - mock_ret = {'name': mock_name, - 'changes': mock_changes, - 'result': None, - 'comment': mock_comment} - ret = test.configurable_test_state(mock_name, - changes=True, - comment=mock_comment) + with patch.dict(test.__opts__, {"test": True}): + mock_ret = { + "name": mock_name, + "changes": mock_changes, + "result": None, + "comment": mock_comment, + } + ret = test.configurable_test_state( + mock_name, changes=True, comment=mock_comment + ) self.assertDictEqual(ret, mock_ret) # Test test=True and changes=False with comment - with patch.dict(test.__opts__, {'test': True}): - mock_ret = {'name': mock_name, - 'changes': {}, - 'result': True, - 'comment': mock_comment} - ret = test.configurable_test_state(mock_name, - changes=False, - comment=mock_comment) + with patch.dict(test.__opts__, {"test": True}): + mock_ret = { + "name": mock_name, + "changes": {}, + "result": True, + "comment": mock_comment, + } + ret = test.configurable_test_state( + mock_name, changes=False, comment=mock_comment + ) self.assertDictEqual(ret, mock_ret) def test_mod_watch(self): - ''' + """ Test to call this function via a watch statement - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} - ret.update({'changes': {'Requisites with changes': []}, - 'comment': 'Watch statement fired.'}) - self.assertDictEqual(test.mod_watch('salt'), ret) + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + ret.update( + { + "changes": {"Requisites with changes": []}, + "comment": "Watch statement fired.", + } + ) + self.assertDictEqual(test.mod_watch("salt"), ret) def test_check_pillar_present(self): - ''' + """ Test to ensure the check_pillar function works properly with the 'present' keyword in the absence of a 'type' keyword. - ''' - ret = { - 'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': '' - } - pillar_return = 'I am a pillar.' + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + pillar_return = "I am a pillar." pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertEqual(test.check_pillar('salt', present='my_pillar'), ret) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertEqual(test.check_pillar("salt", present="my_pillar"), ret) def test_check_pillar_string(self): - ''' + """ Test to ensure the check_pillar function works properly with the 'key_type' checks, using the string key_type. - ''' - ret = { - 'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': '' - } - pillar_return = 'I am a pillar.' + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + pillar_return = "I am a pillar." pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertEqual(test.check_pillar('salt', string='my_pillar'), ret) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertEqual(test.check_pillar("salt", string="my_pillar"), ret) # With unicode (py2) or str (py3) strings - pillar_return = six.text_type('I am a pillar.') + pillar_return = six.text_type("I am a pillar.") pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertEqual(test.check_pillar('salt', string='my_pillar'), ret) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertEqual(test.check_pillar("salt", string="my_pillar"), ret) # With a dict - pillar_return = {'this': 'dictionary'} + pillar_return = {"this": "dictionary"} pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertFalse(test.check_pillar('salt', string='my_pillar')['result']) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertFalse(test.check_pillar("salt", string="my_pillar")["result"]) # With a list - pillar_return = ['I am a pillar.'] + pillar_return = ["I am a pillar."] pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertFalse(test.check_pillar('salt', string='my_pillar')['result']) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertFalse(test.check_pillar("salt", string="my_pillar")["result"]) # With a boolean pillar_return = True pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertFalse(test.check_pillar('salt', string='my_pillar')['result']) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertFalse(test.check_pillar("salt", string="my_pillar")["result"]) # With an int pillar_return = 1 pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertFalse(test.check_pillar('salt', string='my_pillar')['result']) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertFalse(test.check_pillar("salt", string="my_pillar")["result"]) def test_check_pillar_dictionary(self): - ''' + """ Test to ensure the check_pillar function works properly with the 'key_type' checks, using the dictionary key_type. - ''' - ret = { - 'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': '' - } - pillar_return = {'this': 'dictionary'} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + pillar_return = {"this": "dictionary"} pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertEqual(test.check_pillar('salt', dictionary='my_pillar'), ret) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertEqual(test.check_pillar("salt", dictionary="my_pillar"), ret) # With an ordered dict - pillar_return = OrderedDict({'this': 'dictionary'}) + pillar_return = OrderedDict({"this": "dictionary"}) pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertEqual(test.check_pillar('salt', dictionary='my_pillar'), ret) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertEqual(test.check_pillar("salt", dictionary="my_pillar"), ret) # With a string - pillar_return = 'I am a pillar.' + pillar_return = "I am a pillar." pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertFalse(test.check_pillar('salt', dictionary='my_pillar')['result']) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertFalse( + test.check_pillar("salt", dictionary="my_pillar")["result"] + ) # With a list - pillar_return = ['I am a pillar.'] + pillar_return = ["I am a pillar."] pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertFalse(test.check_pillar('salt', dictionary='my_pillar')['result']) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertFalse( + test.check_pillar("salt", dictionary="my_pillar")["result"] + ) # With a boolean pillar_return = True pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertFalse(test.check_pillar('salt', dictionary='my_pillar')['result']) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertFalse( + test.check_pillar("salt", dictionary="my_pillar")["result"] + ) # With an int pillar_return = 1 pillar_mock = MagicMock(return_value=pillar_return) - with patch.dict(test.__salt__, {'pillar.get': pillar_mock}): - self.assertFalse(test.check_pillar('salt', dictionary='my_pillar')['result']) + with patch.dict(test.__salt__, {"pillar.get": pillar_mock}): + self.assertFalse( + test.check_pillar("salt", dictionary="my_pillar")["result"] + ) diff --git a/tests/unit/states/test_timezone.py b/tests/unit/states/test_timezone.py index 53d327a6147..ba61102e82a 100644 --- a/tests/unit/states/test_timezone.py +++ b/tests/unit/states/test_timezone.py @@ -1,59 +1,67 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import salt.states.timezone as timezone # Import Salt Libs from salt.exceptions import CommandExecutionError -import salt.states.timezone as timezone + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class TimezoneTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the timezone state - ''' + """ + def setup_loader_modules(self): return {timezone: {}} def test_system(self): - ''' + """ Test to set the timezone for the system. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[CommandExecutionError, True, True, True]) - mock1 = MagicMock(side_effect=['local', 'localtime', 'localtime']) + mock1 = MagicMock(side_effect=["local", "localtime", "localtime"]) mock2 = MagicMock(return_value=False) - with patch.dict(timezone.__salt__, {"timezone.zone_compare": mock, - "timezone.get_hwclock": mock1, - "timezone.set_hwclock": mock2}): - ret.update({'comment': "Unable to compare desired timezone" - " 'salt' to system timezone: ", 'result': False}) - self.assertDictEqual(timezone.system('salt'), ret) + with patch.dict( + timezone.__salt__, + { + "timezone.zone_compare": mock, + "timezone.get_hwclock": mock1, + "timezone.set_hwclock": mock2, + }, + ): + ret.update( + { + "comment": "Unable to compare desired timezone" + " 'salt' to system timezone: ", + "result": False, + } + ) + self.assertDictEqual(timezone.system("salt"), ret) - ret.update({'comment': 'Timezone salt already set,' - ' UTC already set to salt', 'result': True}) - self.assertDictEqual(timezone.system('salt'), ret) + ret.update( + { + "comment": "Timezone salt already set," " UTC already set to salt", + "result": True, + } + ) + self.assertDictEqual(timezone.system("salt"), ret) with patch.dict(timezone.__opts__, {"test": True}): - ret.update({'comment': 'UTC needs to be set to True', - 'result': None}) - self.assertDictEqual(timezone.system('salt'), ret) + ret.update({"comment": "UTC needs to be set to True", "result": None}) + self.assertDictEqual(timezone.system("salt"), ret) with patch.dict(timezone.__opts__, {"test": False}): - ret.update({'comment': 'Failed to set UTC to True', - 'result': False}) - self.assertDictEqual(timezone.system('salt'), ret) + ret.update({"comment": "Failed to set UTC to True", "result": False}) + self.assertDictEqual(timezone.system("salt"), ret) diff --git a/tests/unit/states/test_tomcat.py b/tests/unit/states/test_tomcat.py index 9c2e84566b1..2f340231151 100644 --- a/tests/unit/states/test_tomcat.py +++ b/tests/unit/states/test_tomcat.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import @@ -12,210 +12,263 @@ from salt.modules import tomcat as tomcatmod # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class TomcatTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the tomcat state - ''' + """ + def setup_loader_modules(self): - return {tomcat: {'__env__': 'base'}} + return {tomcat: {"__env__": "base"}} def test_war_deployed(self): - ''' + """ Test to enforce that the WAR will be deployed and started in the context path it will make use of WAR versions - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} - mock_start = MagicMock(return_value='saltstack') - mock_undeploy = MagicMock(side_effect=['FAIL', 'saltstack']) - mock_deploy = MagicMock(return_value='deploy') - mock_ls = MagicMock(side_effect=[{'salt': {'version': 'jenkins-1.20.4', - 'mode': 'running'}}, - {'salt': {'version': '1'}}, - {'salt': {'version': 'jenkins-1.2.4', - 'mode': 'run'}}, - {'salt': {'version': '1'}}, - {'salt': {'version': '1'}}, - {'salt': {'version': '1'}}]) - with patch.dict(tomcat.__salt__, {"tomcat.ls": mock_ls, - 'tomcat.extract_war_version': tomcatmod.extract_war_version, - 'tomcat.start': mock_start, - 'tomcat.undeploy': mock_undeploy, - 'tomcat.deploy_war': mock_deploy}): - ret.update({'comment': 'salt with version 1.20.4' - ' is already deployed'}) - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins' - '-1.20.4.war'), ret) + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + mock_start = MagicMock(return_value="saltstack") + mock_undeploy = MagicMock(side_effect=["FAIL", "saltstack"]) + mock_deploy = MagicMock(return_value="deploy") + mock_ls = MagicMock( + side_effect=[ + {"salt": {"version": "jenkins-1.20.4", "mode": "running"}}, + {"salt": {"version": "1"}}, + {"salt": {"version": "jenkins-1.2.4", "mode": "run"}}, + {"salt": {"version": "1"}}, + {"salt": {"version": "1"}}, + {"salt": {"version": "1"}}, + ] + ) + with patch.dict( + tomcat.__salt__, + { + "tomcat.ls": mock_ls, + "tomcat.extract_war_version": tomcatmod.extract_war_version, + "tomcat.start": mock_start, + "tomcat.undeploy": mock_undeploy, + "tomcat.deploy_war": mock_deploy, + }, + ): + ret.update({"comment": "salt with version 1.20.4" " is already deployed"}) + self.assertDictEqual( + tomcat.war_deployed("salt", "salt://jenkins" "-1.20.4.war"), ret + ) with patch.dict(tomcat.__opts__, {"test": True}): - ret.update({'changes': {'deploy': 'will deploy salt' - ' with version 1.2.4', - 'undeploy': 'undeployed salt' - ' with version 1'}, - 'result': None, 'comment': ''}) - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins' - '-1.2.4.war'), ret) + ret.update( + { + "changes": { + "deploy": "will deploy salt" " with version 1.2.4", + "undeploy": "undeployed salt" " with version 1", + }, + "result": None, + "comment": "", + } + ) + self.assertDictEqual( + tomcat.war_deployed("salt", "salt://jenkins" "-1.2.4.war"), ret + ) with patch.dict(tomcat.__opts__, {"test": False}): - ret.update({'changes': {'start': 'starting salt'}, - 'comment': 'saltstack', 'result': False}) - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins' - '-1.2.4.war'), ret) + ret.update( + { + "changes": {"start": "starting salt"}, + "comment": "saltstack", + "result": False, + } + ) + self.assertDictEqual( + tomcat.war_deployed("salt", "salt://jenkins" "-1.2.4.war"), ret + ) - ret.update({'changes': {'deploy': 'will deploy salt with' - ' version 1.2.4', - 'undeploy': 'undeployed salt with' - ' version 1'}, - 'comment': 'FAIL'}) - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins' - '-1.2.4.war'), ret) + ret.update( + { + "changes": { + "deploy": "will deploy salt with" " version 1.2.4", + "undeploy": "undeployed salt with" " version 1", + }, + "comment": "FAIL", + } + ) + self.assertDictEqual( + tomcat.war_deployed("salt", "salt://jenkins" "-1.2.4.war"), ret + ) - ret.update({'changes': {'undeploy': 'undeployed salt' - ' with version 1'}, - 'comment': 'deploy'}) - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins' - '-1.2.4.war'), ret) + ret.update( + { + "changes": {"undeploy": "undeployed salt" " with version 1"}, + "comment": "deploy", + } + ) + self.assertDictEqual( + tomcat.war_deployed("salt", "salt://jenkins" "-1.2.4.war"), ret + ) def test_war_deployed_no_version(self): - ''' + """ Tests that going from versions to no versions and back work, as well as not overwriting a WAR without version with another without version. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": None, "comment": ""} - mock_deploy = MagicMock(return_value='deploy') - mock_undeploy = MagicMock(return_value='SUCCESS') - mock_ls_version = MagicMock(return_value={'salt': {'version': '1.2.4', - 'mode': 'running'}}) - mock_ls_no_version = MagicMock(return_value={'salt': {'version': '', - 'mode': 'running'}}) + mock_deploy = MagicMock(return_value="deploy") + mock_undeploy = MagicMock(return_value="SUCCESS") + mock_ls_version = MagicMock( + return_value={"salt": {"version": "1.2.4", "mode": "running"}} + ) + mock_ls_no_version = MagicMock( + return_value={"salt": {"version": "", "mode": "running"}} + ) # We're just checking what it *would* do. with patch.dict(tomcat.__opts__, {"test": True}): - with patch.dict(tomcat.__salt__, - {"tomcat.ls": mock_ls_version, - "tomcat.extract_war_version": tomcatmod.extract_war_version, - "tomcat.deploy_war": mock_deploy, - "tomcat.undeploy": mock_undeploy}): + with patch.dict( + tomcat.__salt__, + { + "tomcat.ls": mock_ls_version, + "tomcat.extract_war_version": tomcatmod.extract_war_version, + "tomcat.deploy_war": mock_deploy, + "tomcat.undeploy": mock_undeploy, + }, + ): # We deploy from version to no version - ret.update({'changes': - {'deploy': 'will deploy salt with no version', - 'undeploy': 'undeployed salt with version 1.2.4'}, - }) - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins.war'), - ret) + ret.update( + { + "changes": { + "deploy": "will deploy salt with no version", + "undeploy": "undeployed salt with version 1.2.4", + }, + } + ) + self.assertDictEqual( + tomcat.war_deployed("salt", "salt://jenkins.war"), ret + ) - with patch.dict(tomcat.__salt__, - {"tomcat.ls": mock_ls_no_version, - "tomcat.extract_war_version": tomcatmod.extract_war_version, - "tomcat.deploy_war": mock_deploy, - "tomcat.undeploy": mock_undeploy}): + with patch.dict( + tomcat.__salt__, + { + "tomcat.ls": mock_ls_no_version, + "tomcat.extract_war_version": tomcatmod.extract_war_version, + "tomcat.deploy_war": mock_deploy, + "tomcat.undeploy": mock_undeploy, + }, + ): # Deploy from none to specified version - ret.update({'changes': - {'deploy': 'will deploy salt with version 1.2.4', - 'undeploy': 'undeployed salt with no version'}, - }) - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins.war', - version='1.2.4'), - ret) + ret.update( + { + "changes": { + "deploy": "will deploy salt with version 1.2.4", + "undeploy": "undeployed salt with no version", + }, + } + ) + self.assertDictEqual( + tomcat.war_deployed("salt", "salt://jenkins.war", version="1.2.4"), + ret, + ) # Deploy from none to extracted version - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins-1.2.4.war'), - ret) + self.assertDictEqual( + tomcat.war_deployed("salt", "salt://jenkins-1.2.4.war"), ret + ) # Don't deploy from no version to no version - ret.update({'changes': {}, - 'comment': 'salt with no version is already deployed', - 'result': True - }) + ret.update( + { + "changes": {}, + "comment": "salt with no version is already deployed", + "result": True, + } + ) # Don't deploy from blank to blank version - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins.war'), - ret) + self.assertDictEqual( + tomcat.war_deployed("salt", "salt://jenkins.war"), ret + ) # Don't deploy from blank to false version - self.assertDictEqual(tomcat.war_deployed('salt', - 'salt://jenkins-1.2.4.war', - version=False), - ret) + self.assertDictEqual( + tomcat.war_deployed( + "salt", "salt://jenkins-1.2.4.war", version=False + ), + ret, + ) def test_wait(self): - ''' + """ Test to wait for the tomcat manager to load - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': 'tomcat manager is ready'} + """ + ret = { + "name": "salt", + "changes": {}, + "result": True, + "comment": "tomcat manager is ready", + } mock = MagicMock(return_value=True) - with patch.dict(tomcat.__salt__, {"tomcat.status": mock, - "tomcat.extract_war_version": tomcatmod.extract_war_version}): - self.assertDictEqual(tomcat.wait('salt'), ret) + with patch.dict( + tomcat.__salt__, + { + "tomcat.status": mock, + "tomcat.extract_war_version": tomcatmod.extract_war_version, + }, + ): + self.assertDictEqual(tomcat.wait("salt"), ret) def test_mod_watch(self): - ''' + """ Test to the tomcat watcher function. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': 'True'} - mock = MagicMock(return_value='True') - with patch.dict(tomcat.__salt__, {"tomcat.reload": mock, - "tomcat.extract_war_version": tomcatmod.extract_war_version}): - ret.update({'changes': {'salt': False}}) - self.assertDictEqual(tomcat.mod_watch('salt'), ret) + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": "True"} + mock = MagicMock(return_value="True") + with patch.dict( + tomcat.__salt__, + { + "tomcat.reload": mock, + "tomcat.extract_war_version": tomcatmod.extract_war_version, + }, + ): + ret.update({"changes": {"salt": False}}) + self.assertDictEqual(tomcat.mod_watch("salt"), ret) def test_undeployed(self): - ''' + """ Test to enforce that the WAR will be un-deployed from the server - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': 'True'} + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": "True"} mock = MagicMock(side_effect=[False, True, True, True, True]) - mock1 = MagicMock(side_effect=[{'salt': {'a': 1}}, - {'salt': {'version': 1}}, - {'salt': {'version': 1}}, - {'salt': {'version': 1}}]) - mock2 = MagicMock(side_effect=['FAIL', 'saltstack']) - with patch.dict(tomcat.__salt__, {"tomcat.status": mock, - "tomcat.extract_war_version": tomcatmod.extract_war_version, - "tomcat.ls": mock1, - "tomcat.undeploy": mock2}): - ret.update({'comment': 'Tomcat Manager does not respond'}) - self.assertDictEqual(tomcat.undeployed('salt'), ret) + mock1 = MagicMock( + side_effect=[ + {"salt": {"a": 1}}, + {"salt": {"version": 1}}, + {"salt": {"version": 1}}, + {"salt": {"version": 1}}, + ] + ) + mock2 = MagicMock(side_effect=["FAIL", "saltstack"]) + with patch.dict( + tomcat.__salt__, + { + "tomcat.status": mock, + "tomcat.extract_war_version": tomcatmod.extract_war_version, + "tomcat.ls": mock1, + "tomcat.undeploy": mock2, + }, + ): + ret.update({"comment": "Tomcat Manager does not respond"}) + self.assertDictEqual(tomcat.undeployed("salt"), ret) - ret.update({'comment': '', 'result': True}) - self.assertDictEqual(tomcat.undeployed('salt'), ret) + ret.update({"comment": "", "result": True}) + self.assertDictEqual(tomcat.undeployed("salt"), ret) with patch.dict(tomcat.__opts__, {"test": True}): - ret.update({'changes': {'undeploy': 1}, 'result': None}) - self.assertDictEqual(tomcat.undeployed('salt'), ret) + ret.update({"changes": {"undeploy": 1}, "result": None}) + self.assertDictEqual(tomcat.undeployed("salt"), ret) with patch.dict(tomcat.__opts__, {"test": False}): - ret.update({'changes': {'undeploy': 1}, - 'comment': 'FAIL', 'result': False}) - self.assertDictEqual(tomcat.undeployed('salt'), ret) + ret.update( + {"changes": {"undeploy": 1}, "comment": "FAIL", "result": False} + ) + self.assertDictEqual(tomcat.undeployed("salt"), ret) - ret.update({'changes': {'undeploy': 1}, - 'comment': '', 'result': True}) - self.assertDictEqual(tomcat.undeployed('salt'), ret) + ret.update({"changes": {"undeploy": 1}, "comment": "", "result": True}) + self.assertDictEqual(tomcat.undeployed("salt"), ret) diff --git a/tests/unit/states/test_user.py b/tests/unit/states/test_user.py index 43d1454b9b0..5a3a9f0f9bc 100644 --- a/tests/unit/states/test_user.py +++ b/tests/unit/states/test_user.py @@ -1,256 +1,293 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import logging -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch, -) +import logging # Import Salt Libs import salt.states.user as user +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase + log = logging.getLogger(__name__) class UserTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the user state - ''' + """ + def setup_loader_modules(self): return {user: {}} def test_present(self): - ''' + """ Test to ensure that the named user is present with the specified properties - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} mock_false = MagicMock(return_value=False) mock_empty_list = MagicMock(return_value=[]) - with patch.dict(user.__grains__, {"kernel": 'Linux'}): - with patch.dict(user.__salt__, {'group.info': mock_false, - 'user.info': mock_empty_list, - "user.chkey": mock_empty_list, - 'user.add': mock_false}): - ret.update({'comment': 'The following group(s) are' - ' not present: salt'}) - self.assertDictEqual(user.present('salt', groups=['salt']), ret) + with patch.dict(user.__grains__, {"kernel": "Linux"}): + with patch.dict( + user.__salt__, + { + "group.info": mock_false, + "user.info": mock_empty_list, + "user.chkey": mock_empty_list, + "user.add": mock_false, + }, + ): + ret.update( + {"comment": "The following group(s) are" " not present: salt"} + ) + self.assertDictEqual(user.present("salt", groups=["salt"]), ret) - mock_false = MagicMock(side_effect=[{'key': 'value'}, {'key': 'value'}, - {'key': 'value'}, False, False]) - with patch.object(user, '_changes', mock_false): + mock_false = MagicMock( + side_effect=[ + {"key": "value"}, + {"key": "value"}, + {"key": "value"}, + False, + False, + ] + ) + with patch.object(user, "_changes", mock_false): with patch.dict(user.__opts__, {"test": True}): ret.update( - {'comment': 'The following user attributes are set ' - 'to be changed:\n' - 'key: value\n', - 'result': None}) - self.assertDictEqual(user.present('salt'), ret) + { + "comment": "The following user attributes are set " + "to be changed:\n" + "key: value\n", + "result": None, + } + ) + self.assertDictEqual(user.present("salt"), ret) with patch.dict(user.__opts__, {"test": False}): # pylint: disable=repr-flag-used-in-string - comment = ( - 'These values could not be changed: {0!r}' - .format({'key': 'value'}) + comment = "These values could not be changed: {0!r}".format( + {"key": "value"} ) # pylint: enable=repr-flag-used-in-string - ret.update({'comment': comment, 'result': False}) - self.assertDictEqual(user.present('salt'), ret) + ret.update({"comment": comment, "result": False}) + self.assertDictEqual(user.present("salt"), ret) with patch.dict(user.__opts__, {"test": True}): - ret.update({'comment': 'User salt set to' - ' be added', 'result': None}) - self.assertDictEqual(user.present('salt'), ret) + ret.update( + { + "comment": "User salt set to" " be added", + "result": None, + } + ) + self.assertDictEqual(user.present("salt"), ret) with patch.dict(user.__opts__, {"test": False}): - ret.update({'comment': 'Failed to create new' - ' user salt', 'result': False}) - self.assertDictEqual(user.present('salt'), ret) + ret.update( + { + "comment": "Failed to create new" " user salt", + "result": False, + } + ) + self.assertDictEqual(user.present("salt"), ret) def test_present_invalid_uid_change(self): - mock_info = MagicMock(side_effect=[ - {'uid': 5000, - 'gid': 5000, - 'groups': ['foo'], - 'home': '/home/foo', - 'fullname': 'Foo Bar'} - ]) - dunder_salt = {'user.info': mock_info, - 'file.group_to_gid': MagicMock(side_effect=['foo']), - 'file.gid_to_group': MagicMock(side_effect=[5000])} + mock_info = MagicMock( + side_effect=[ + { + "uid": 5000, + "gid": 5000, + "groups": ["foo"], + "home": "/home/foo", + "fullname": "Foo Bar", + } + ] + ) + dunder_salt = { + "user.info": mock_info, + "file.group_to_gid": MagicMock(side_effect=["foo"]), + "file.gid_to_group": MagicMock(side_effect=[5000]), + } # side_effect used because these mocks should only be called once - with patch.dict(user.__grains__, {'kernel': 'Linux'}), \ - patch.dict(user.__salt__, dunder_salt): - ret = user.present('foo', uid=5001) + with patch.dict(user.__grains__, {"kernel": "Linux"}), patch.dict( + user.__salt__, dunder_salt + ): + ret = user.present("foo", uid=5001) # State should have failed - self.assertFalse(ret['result']) + self.assertFalse(ret["result"]) # Only one of uid/gid should have been flagged in the comment - self.assertEqual(ret['comment'].count('not permitted'), 1) + self.assertEqual(ret["comment"].count("not permitted"), 1) def test_present_invalid_gid_change(self): - mock_info = MagicMock(side_effect=[ - {'uid': 5000, - 'gid': 5000, - 'groups': ['foo'], - 'home': '/home/foo', - 'fullname': 'Foo Bar'} - ]) - dunder_salt = {'user.info': mock_info, - 'file.group_to_gid': MagicMock(side_effect=['foo']), - 'file.gid_to_group': MagicMock(side_effect=[5000])} + mock_info = MagicMock( + side_effect=[ + { + "uid": 5000, + "gid": 5000, + "groups": ["foo"], + "home": "/home/foo", + "fullname": "Foo Bar", + } + ] + ) + dunder_salt = { + "user.info": mock_info, + "file.group_to_gid": MagicMock(side_effect=["foo"]), + "file.gid_to_group": MagicMock(side_effect=[5000]), + } # side_effect used because these mocks should only be called once - with patch.dict(user.__grains__, {'kernel': 'Linux'}), \ - patch.dict(user.__salt__, dunder_salt): - ret = user.present('foo', gid=5001) + with patch.dict(user.__grains__, {"kernel": "Linux"}), patch.dict( + user.__salt__, dunder_salt + ): + ret = user.present("foo", gid=5001) # State should have failed - self.assertFalse(ret['result']) + self.assertFalse(ret["result"]) # Only one of uid/gid should have been flagged in the comment - self.assertEqual(ret['comment'].count('not permitted'), 1) + self.assertEqual(ret["comment"].count("not permitted"), 1) def test_present_invalid_uid_gid_change(self): - mock_info = MagicMock(side_effect=[ - {'uid': 5000, - 'gid': 5000, - 'groups': ['foo'], - 'home': '/home/foo', - 'fullname': 'Foo Bar'} - ]) - dunder_salt = {'user.info': mock_info, - 'file.group_to_gid': MagicMock(side_effect=['foo']), - 'file.gid_to_group': MagicMock(side_effect=[5000])} + mock_info = MagicMock( + side_effect=[ + { + "uid": 5000, + "gid": 5000, + "groups": ["foo"], + "home": "/home/foo", + "fullname": "Foo Bar", + } + ] + ) + dunder_salt = { + "user.info": mock_info, + "file.group_to_gid": MagicMock(side_effect=["foo"]), + "file.gid_to_group": MagicMock(side_effect=[5000]), + } # side_effect used because these mocks should only be called once - with patch.dict(user.__grains__, {'kernel': 'Linux'}), \ - patch.dict(user.__salt__, dunder_salt): - ret = user.present('foo', uid=5001, gid=5001) + with patch.dict(user.__grains__, {"kernel": "Linux"}), patch.dict( + user.__salt__, dunder_salt + ): + ret = user.present("foo", uid=5001, gid=5001) # State should have failed - self.assertFalse(ret['result']) + self.assertFalse(ret["result"]) # Both the uid and gid should have been flagged in the comment - self.assertEqual(ret['comment'].count('not permitted'), 2) + self.assertEqual(ret["comment"].count("not permitted"), 2) def test_present_uid_gid_change(self): - before = {'uid': 5000, - 'gid': 5000, - 'groups': ['foo'], - 'home': '/home/foo', - 'fullname': 'Foo Bar'} - after = {'uid': 5001, - 'gid': 5001, - 'groups': ['othergroup'], - 'home': '/home/foo', - 'fullname': 'Foo Bar'} + before = { + "uid": 5000, + "gid": 5000, + "groups": ["foo"], + "home": "/home/foo", + "fullname": "Foo Bar", + } + after = { + "uid": 5001, + "gid": 5001, + "groups": ["othergroup"], + "home": "/home/foo", + "fullname": "Foo Bar", + } # user.info should be called 4 times. Once the first time that # _changes() is called, once before and after changes are applied (to # get the before/after for the changes dict, and one last time to # confirm that no changes still need to be made. mock_info = MagicMock(side_effect=[before, before, after, after]) - mock_group_to_gid = MagicMock(side_effect=['foo', 'othergroup']) + mock_group_to_gid = MagicMock(side_effect=["foo", "othergroup"]) mock_gid_to_group = MagicMock(side_effect=[5000, 5001]) - dunder_salt = {'user.info': mock_info, - 'user.chuid': Mock(), - 'user.chgid': Mock(), - 'file.group_to_gid': mock_group_to_gid, - 'file.gid_to_group': mock_gid_to_group} + dunder_salt = { + "user.info": mock_info, + "user.chuid": Mock(), + "user.chgid": Mock(), + "file.group_to_gid": mock_group_to_gid, + "file.gid_to_group": mock_gid_to_group, + } # side_effect used because these mocks should only be called once - with patch.dict(user.__grains__, {'kernel': 'Linux'}), \ - patch.dict(user.__salt__, dunder_salt), \ - patch.dict(user.__opts__, {'test': False}), \ - patch('os.path.isdir', MagicMock(return_value=True)): + with patch.dict(user.__grains__, {"kernel": "Linux"}), patch.dict( + user.__salt__, dunder_salt + ), patch.dict(user.__opts__, {"test": False}), patch( + "os.path.isdir", MagicMock(return_value=True) + ): ret = user.present( - 'foo', - uid=5001, - gid=5001, - allow_uid_change=True, - allow_gid_change=True) + "foo", uid=5001, gid=5001, allow_uid_change=True, allow_gid_change=True + ) self.assertEqual( ret, - {'comment': 'Updated user foo', - 'changes': {'gid': 5001, - 'uid': 5001, - 'groups': ['othergroup']}, - 'name': 'foo', - 'result': True} + { + "comment": "Updated user foo", + "changes": {"gid": 5001, "uid": 5001, "groups": ["othergroup"]}, + "name": "foo", + "result": True, + }, ) def test_absent(self): - ''' + """ Test to ensure that the named user is absent - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": None, "comment": ""} mock = MagicMock(side_effect=[True, True, False]) mock1 = MagicMock(return_value=False) - with patch.dict(user.__salt__, {'user.info': mock, - 'user.delete': mock1, - 'group.info': mock1}): + with patch.dict( + user.__salt__, + {"user.info": mock, "user.delete": mock1, "group.info": mock1}, + ): with patch.dict(user.__opts__, {"test": True}): - ret.update({'comment': 'User salt set for removal'}) - self.assertDictEqual(user.absent('salt'), ret) + ret.update({"comment": "User salt set for removal"}) + self.assertDictEqual(user.absent("salt"), ret) with patch.dict(user.__opts__, {"test": False}): - ret.update({'comment': 'Failed to remove user salt', - 'result': False}) - self.assertDictEqual(user.absent('salt'), ret) + ret.update({"comment": "Failed to remove user salt", "result": False}) + self.assertDictEqual(user.absent("salt"), ret) - ret.update({'comment': 'User salt is not present', - 'result': True}) - self.assertDictEqual(user.absent('salt'), ret) + ret.update({"comment": "User salt is not present", "result": True}) + self.assertDictEqual(user.absent("salt"), ret) def test_changes(self): - ''' + """ Test salt.states.user._changes - ''' + """ mock_info = MagicMock( return_value={ - 'uid': 5000, - 'gid': 5000, - 'groups': ['foo'], - 'home': '/home/foo', - 'fullname': 'Foo Bar', + "uid": 5000, + "gid": 5000, + "groups": ["foo"], + "home": "/home/foo", + "fullname": "Foo Bar", } ) shadow_info = MagicMock( - return_value={ - 'min': 2, - 'max': 88888, - 'inact': 77, - 'warn': 14, - } + return_value={"min": 2, "max": 88888, "inact": 77, "warn": 14} ) - shadow_hash = MagicMock(return_value='abcd') - dunder_salt = {'user.info': mock_info, - 'shadow.info': shadow_info, - 'shadow.default_hash': shadow_hash, - 'file.group_to_gid': MagicMock(side_effect=['foo']), - 'file.gid_to_group': MagicMock(side_effect=[5000])} + shadow_hash = MagicMock(return_value="abcd") + dunder_salt = { + "user.info": mock_info, + "shadow.info": shadow_info, + "shadow.default_hash": shadow_hash, + "file.group_to_gid": MagicMock(side_effect=["foo"]), + "file.gid_to_group": MagicMock(side_effect=[5000]), + } def mock_exists(*args): return True # side_effect used because these mocks should only be called once - with patch.dict(user.__grains__, {'kernel': 'Linux'}), \ - patch.dict(user.__salt__, dunder_salt), \ - patch.dict(user.__opts__, {"test": False}), \ - patch('os.path.isdir', mock_exists): - ret = user._changes('foo', maxdays=999999, inactdays=0, warndays=7) + with patch.dict(user.__grains__, {"kernel": "Linux"}), patch.dict( + user.__salt__, dunder_salt + ), patch.dict(user.__opts__, {"test": False}), patch( + "os.path.isdir", mock_exists + ): + ret = user._changes("foo", maxdays=999999, inactdays=0, warndays=7) assert ret == { - 'maxdays': 999999, - 'mindays': 0, - 'fullname': '', - 'warndays': 7, - 'inactdays': 0 + "maxdays": 999999, + "mindays": 0, + "fullname": "", + "warndays": 7, + "inactdays": 0, } diff --git a/tests/unit/states/test_vbox_guest.py b/tests/unit/states/test_vbox_guest.py index 594518a7de6..6f58600862b 100644 --- a/tests/unit/states/test_vbox_guest.py +++ b/tests/unit/states/test_vbox_guest.py @@ -1,120 +1,140 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.vbox_guest as vbox_guest +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class VboxGuestTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the vbox_guest state - ''' + """ + def setup_loader_modules(self): return {vbox_guest: {}} def test_additions_installed(self): - ''' + """ Test to ensure that the VirtualBox Guest Additions are installed - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[True, False, False, False]) - with patch.dict(vbox_guest.__salt__, - {"vbox_guest.additions_version": mock, - "vbox_guest.additions_install": mock}): - ret.update({'comment': 'System already in the correct state'}) - self.assertDictEqual(vbox_guest.additions_installed('salt'), ret) + with patch.dict( + vbox_guest.__salt__, + { + "vbox_guest.additions_version": mock, + "vbox_guest.additions_install": mock, + }, + ): + ret.update({"comment": "System already in the correct state"}) + self.assertDictEqual(vbox_guest.additions_installed("salt"), ret) with patch.dict(vbox_guest.__opts__, {"test": True}): - ret.update({'changes': {'new': True, 'old': False}, - 'comment': 'The state of VirtualBox Guest' - ' Additions will be changed.', 'result': None}) - self.assertDictEqual(vbox_guest.additions_installed('salt'), - ret) + ret.update( + { + "changes": {"new": True, "old": False}, + "comment": "The state of VirtualBox Guest" + " Additions will be changed.", + "result": None, + } + ) + self.assertDictEqual(vbox_guest.additions_installed("salt"), ret) with patch.dict(vbox_guest.__opts__, {"test": False}): - ret.update({'changes': {'new': False, 'old': False}, - 'comment': 'The state of VirtualBox Guest' - ' Additions was changed!', 'result': False}) - self.assertDictEqual(vbox_guest.additions_installed('salt'), - ret) + ret.update( + { + "changes": {"new": False, "old": False}, + "comment": "The state of VirtualBox Guest" + " Additions was changed!", + "result": False, + } + ) + self.assertDictEqual(vbox_guest.additions_installed("salt"), ret) def test_additions_removed(self): - ''' + """ Test to ensure that the VirtualBox Guest Additions are removed. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} mock = MagicMock(side_effect=[False, True, True, True]) - with patch.dict(vbox_guest.__salt__, - {"vbox_guest.additions_version": mock, - "vbox_guest.additions_remove": mock}): - ret.update({'comment': 'System already in the correct state'}) - self.assertDictEqual(vbox_guest.additions_removed('salt'), ret) + with patch.dict( + vbox_guest.__salt__, + {"vbox_guest.additions_version": mock, "vbox_guest.additions_remove": mock}, + ): + ret.update({"comment": "System already in the correct state"}) + self.assertDictEqual(vbox_guest.additions_removed("salt"), ret) with patch.dict(vbox_guest.__opts__, {"test": True}): - ret.update({'changes': {'new': True, 'old': True}, - 'comment': 'The state of VirtualBox Guest' - ' Additions will be changed.', 'result': None}) - self.assertDictEqual(vbox_guest.additions_removed('salt'), - ret) + ret.update( + { + "changes": {"new": True, "old": True}, + "comment": "The state of VirtualBox Guest" + " Additions will be changed.", + "result": None, + } + ) + self.assertDictEqual(vbox_guest.additions_removed("salt"), ret) with patch.dict(vbox_guest.__opts__, {"test": False}): - ret.update({'comment': 'The state of VirtualBox Guest' - ' Additions was changed!', 'result': True}) - self.assertDictEqual(vbox_guest.additions_removed('salt'), - ret) + ret.update( + { + "comment": "The state of VirtualBox Guest" + " Additions was changed!", + "result": True, + } + ) + self.assertDictEqual(vbox_guest.additions_removed("salt"), ret) def test_grantaccess_to_sharedfolders(self): - ''' + """ Test to grant access to auto-mounted shared folders to the users. - ''' - ret = {'name': 'AB', - 'changes': {}, - 'result': True, - 'comment': ''} - mock = MagicMock(side_effect=[['AB'], 'salt', 'salt', 'salt']) - with patch.dict(vbox_guest.__salt__, - {"vbox_guest.list_shared_folders_users": mock, - "vbox_guest.grant_access_to_shared_folders_to": mock} - ): - ret.update({'comment': 'System already in the correct state'}) + """ + ret = {"name": "AB", "changes": {}, "result": True, "comment": ""} + mock = MagicMock(side_effect=[["AB"], "salt", "salt", "salt"]) + with patch.dict( + vbox_guest.__salt__, + { + "vbox_guest.list_shared_folders_users": mock, + "vbox_guest.grant_access_to_shared_folders_to": mock, + }, + ): + ret.update({"comment": "System already in the correct state"}) self.assert_method(ret) with patch.dict(vbox_guest.__opts__, {"test": True}): - ret.update({'changes': {'new': ['AB'], 'old': 'salt'}, - 'comment': 'List of users who have access to' - ' auto-mounted shared folders will be changed', - 'result': None}) + ret.update( + { + "changes": {"new": ["AB"], "old": "salt"}, + "comment": "List of users who have access to" + " auto-mounted shared folders will be changed", + "result": None, + } + ) self.assert_method(ret) with patch.dict(vbox_guest.__opts__, {"test": False}): - ret.update({'changes': {'new': 'salt', 'old': 'salt'}, - 'comment': 'List of users who have access to' - ' auto-mounted shared folders was changed', - 'result': True}) + ret.update( + { + "changes": {"new": "salt", "old": "salt"}, + "comment": "List of users who have access to" + " auto-mounted shared folders was changed", + "result": True, + } + ) self.assert_method(ret) def assert_method(self, ret): - ''' + """ Method call for assert statements - ''' - self.assertDictEqual(vbox_guest.grant_access_to_shared_folders_to('AB'), - ret) + """ + self.assertDictEqual(vbox_guest.grant_access_to_shared_folders_to("AB"), ret) diff --git a/tests/unit/states/test_virt.py b/tests/unit/states/test_virt.py index c50c04b8abe..d5fd2d985aa 100644 --- a/tests/unit/states/test_virt.py +++ b/tests/unit/states/test_virt.py @@ -1,20 +1,12 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import tempfile -import shutil -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - mock_open, - patch) +import shutil +import tempfile # Import Salt Libs import salt.states.virt as virt @@ -23,33 +15,42 @@ from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, mock_open, patch + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class LibvirtMock(MagicMock): # pylint: disable=too-many-ancestors - ''' + """ libvirt library mockup - ''' + """ + class libvirtError(Exception): # pylint: disable=invalid-name - ''' + """ libvirt error mockup - ''' + """ + def get_error_message(self): - ''' + """ Fake function return error message - ''' + """ return six.text_type(self) class LibvirtTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.libvirt - ''' + """ + def setup_loader_modules(self): - self.mock_libvirt = LibvirtMock() # pylint: disable=attribute-defined-outside-init - self.addCleanup(delattr, self, 'mock_libvirt') - loader_globals = { - 'libvirt': self.mock_libvirt - } + self.mock_libvirt = ( + LibvirtMock() + ) # pylint: disable=attribute-defined-outside-init + self.addCleanup(delattr, self, "mock_libvirt") + loader_globals = {"libvirt": self.mock_libvirt} return {virt: loader_globals} @classmethod @@ -64,951 +65,1439 @@ class LibvirtTestCase(TestCase, LoaderModuleMockMixin): # 'keys' function tests: 1 def test_keys(self): - ''' + """ Test to manage libvirt keys. - ''' - with patch('os.path.isfile', MagicMock(return_value=False)): - name = 'sunrise' + """ + with patch("os.path.isfile", MagicMock(return_value=False)): + name = "sunrise" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'], - {'libvirt.servercert.pem': 'A'}]) - with patch.dict(virt.__salt__, {'pillar.ext': mock}): - comt = ('All keys are correct') - ret.update({'comment': comt}) + mock = MagicMock( + side_effect=[ + [], + ["libvirt.servercert.pem"], + {"libvirt.servercert.pem": "A"}, + ] + ) + with patch.dict(virt.__salt__, {"pillar.ext": mock}): + comt = "All keys are correct" + ret.update({"comment": comt}) self.assertDictEqual(virt.keys(name, basepath=self.pki_dir), ret) - with patch.dict(virt.__opts__, {'test': True}): - comt = ('Libvirt keys are set to be updated') - ret.update({'comment': comt, 'result': None}) + with patch.dict(virt.__opts__, {"test": True}): + comt = "Libvirt keys are set to be updated" + ret.update({"comment": comt, "result": None}) self.assertDictEqual(virt.keys(name, basepath=self.pki_dir), ret) - with patch.dict(virt.__opts__, {'test': False}): - with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())): - comt = ('Updated libvirt certs and keys') - ret.update({'comment': comt, 'result': True, - 'changes': {'servercert': 'new'}}) - self.assertDictEqual(virt.keys(name, basepath=self.pki_dir), ret) + with patch.dict(virt.__opts__, {"test": False}): + with patch.object( + salt.utils.files, "fopen", MagicMock(mock_open()) + ): + comt = "Updated libvirt certs and keys" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"servercert": "new"}, + } + ) + self.assertDictEqual( + virt.keys(name, basepath=self.pki_dir), ret + ) def test_keys_with_expiration_days(self): - ''' + """ Test to manage libvirt keys. - ''' - with patch('os.path.isfile', MagicMock(return_value=False)): - name = 'sunrise' + """ + with patch("os.path.isfile", MagicMock(return_value=False)): + name = "sunrise" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'], - {'libvirt.servercert.pem': 'A'}]) - with patch.dict(virt.__salt__, {'pillar.ext': mock}): - comt = ('All keys are correct') - ret.update({'comment': comt}) - self.assertDictEqual(virt.keys(name, - basepath=self.pki_dir, - expiration_days=700), ret) + mock = MagicMock( + side_effect=[ + [], + ["libvirt.servercert.pem"], + {"libvirt.servercert.pem": "A"}, + ] + ) + with patch.dict(virt.__salt__, {"pillar.ext": mock}): + comt = "All keys are correct" + ret.update({"comment": comt}) + self.assertDictEqual( + virt.keys(name, basepath=self.pki_dir, expiration_days=700), ret + ) - with patch.dict(virt.__opts__, {'test': True}): - comt = ('Libvirt keys are set to be updated') - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(virt.keys(name, - basepath=self.pki_dir, - expiration_days=700), ret) + with patch.dict(virt.__opts__, {"test": True}): + comt = "Libvirt keys are set to be updated" + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + virt.keys(name, basepath=self.pki_dir, expiration_days=700), ret + ) - with patch.dict(virt.__opts__, {'test': False}): - with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())): - comt = ('Updated libvirt certs and keys') - ret.update({'comment': comt, 'result': True, - 'changes': {'servercert': 'new'}}) - self.assertDictEqual(virt.keys(name, - basepath=self.pki_dir, - expiration_days=700), ret) + with patch.dict(virt.__opts__, {"test": False}): + with patch.object( + salt.utils.files, "fopen", MagicMock(mock_open()) + ): + comt = "Updated libvirt certs and keys" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"servercert": "new"}, + } + ) + self.assertDictEqual( + virt.keys(name, basepath=self.pki_dir, expiration_days=700), + ret, + ) def test_keys_with_state(self): - ''' + """ Test to manage libvirt keys. - ''' - with patch('os.path.isfile', MagicMock(return_value=False)): - name = 'sunrise' + """ + with patch("os.path.isfile", MagicMock(return_value=False)): + name = "sunrise" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'], - {'libvirt.servercert.pem': 'A'}]) - with patch.dict(virt.__salt__, {'pillar.ext': mock}): - comt = ('All keys are correct') - ret.update({'comment': comt}) - self.assertDictEqual(virt.keys(name, - basepath=self.pki_dir, - st='California'), ret) + mock = MagicMock( + side_effect=[ + [], + ["libvirt.servercert.pem"], + {"libvirt.servercert.pem": "A"}, + ] + ) + with patch.dict(virt.__salt__, {"pillar.ext": mock}): + comt = "All keys are correct" + ret.update({"comment": comt}) + self.assertDictEqual( + virt.keys(name, basepath=self.pki_dir, st="California"), ret + ) - with patch.dict(virt.__opts__, {'test': True}): - comt = ('Libvirt keys are set to be updated') - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(virt.keys(name, - basepath=self.pki_dir, - st='California'), ret) + with patch.dict(virt.__opts__, {"test": True}): + comt = "Libvirt keys are set to be updated" + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + virt.keys(name, basepath=self.pki_dir, st="California"), ret + ) - with patch.dict(virt.__opts__, {'test': False}): - with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())): - comt = ('Updated libvirt certs and keys') - ret.update({'comment': comt, 'result': True, - 'changes': {'servercert': 'new'}}) - self.assertDictEqual(virt.keys(name, - basepath=self.pki_dir, - st='California'), ret) + with patch.dict(virt.__opts__, {"test": False}): + with patch.object( + salt.utils.files, "fopen", MagicMock(mock_open()) + ): + comt = "Updated libvirt certs and keys" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"servercert": "new"}, + } + ) + self.assertDictEqual( + virt.keys(name, basepath=self.pki_dir, st="California"), ret + ) def test_keys_with_all_options(self): - ''' + """ Test to manage libvirt keys. - ''' - with patch('os.path.isfile', MagicMock(return_value=False)): - name = 'sunrise' + """ + with patch("os.path.isfile", MagicMock(return_value=False)): + name = "sunrise" - ret = {'name': name, - 'result': True, - 'comment': '', - 'changes': {}} + ret = {"name": name, "result": True, "comment": "", "changes": {}} - mock = MagicMock(side_effect=[[], ['libvirt.servercert.pem'], - {'libvirt.servercert.pem': 'A'}]) - with patch.dict(virt.__salt__, {'pillar.ext': mock}): - comt = ('All keys are correct') - ret.update({'comment': comt}) - self.assertDictEqual(virt.keys(name, - basepath=self.pki_dir, - country='USA', - st='California', - locality='Los_Angeles', - organization='SaltStack', - expiration_days=700), ret) + mock = MagicMock( + side_effect=[ + [], + ["libvirt.servercert.pem"], + {"libvirt.servercert.pem": "A"}, + ] + ) + with patch.dict(virt.__salt__, {"pillar.ext": mock}): + comt = "All keys are correct" + ret.update({"comment": comt}) + self.assertDictEqual( + virt.keys( + name, + basepath=self.pki_dir, + country="USA", + st="California", + locality="Los_Angeles", + organization="SaltStack", + expiration_days=700, + ), + ret, + ) - with patch.dict(virt.__opts__, {'test': True}): - comt = ('Libvirt keys are set to be updated') - ret.update({'comment': comt, 'result': None}) - self.assertDictEqual(virt.keys(name, - basepath=self.pki_dir, - country='USA', - st='California', - locality='Los_Angeles', - organization='SaltStack', - expiration_days=700), ret) + with patch.dict(virt.__opts__, {"test": True}): + comt = "Libvirt keys are set to be updated" + ret.update({"comment": comt, "result": None}) + self.assertDictEqual( + virt.keys( + name, + basepath=self.pki_dir, + country="USA", + st="California", + locality="Los_Angeles", + organization="SaltStack", + expiration_days=700, + ), + ret, + ) - with patch.dict(virt.__opts__, {'test': False}): - with patch.object(salt.utils.files, 'fopen', MagicMock(mock_open())): - comt = ('Updated libvirt certs and keys') - ret.update({'comment': comt, 'result': True, - 'changes': {'servercert': 'new'}}) - self.assertDictEqual(virt.keys(name, - basepath=self.pki_dir, - country='USA', - st='California', - locality='Los_Angeles', - organization='SaltStack', - expiration_days=700), ret) + with patch.dict(virt.__opts__, {"test": False}): + with patch.object( + salt.utils.files, "fopen", MagicMock(mock_open()) + ): + comt = "Updated libvirt certs and keys" + ret.update( + { + "comment": comt, + "result": True, + "changes": {"servercert": "new"}, + } + ) + self.assertDictEqual( + virt.keys( + name, + basepath=self.pki_dir, + country="USA", + st="California", + locality="Los_Angeles", + organization="SaltStack", + expiration_days=700, + ), + ret, + ) def test_running(self): - ''' + """ running state test cases. - ''' - ret = {'name': 'myvm', - 'changes': {}, - 'result': True, - 'comment': 'myvm is running'} - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}), - 'virt.start': MagicMock(return_value=0), - }): - ret.update({'changes': {'myvm': 'Domain started'}, - 'comment': 'Domain myvm started'}) - self.assertDictEqual(virt.running('myvm'), ret) + """ + ret = { + "name": "myvm", + "changes": {}, + "result": True, + "comment": "myvm is running", + } + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.vm_state": MagicMock(return_value={"myvm": "stopped"}), + "virt.start": MagicMock(return_value=0), + }, + ): + ret.update( + { + "changes": {"myvm": "Domain started"}, + "comment": "Domain myvm started", + } + ) + self.assertDictEqual(virt.running("myvm"), ret) init_mock = MagicMock(return_value=True) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')), - 'virt.init': init_mock, - 'virt.start': MagicMock(return_value=0) - }): - ret.update({'changes': {'myvm': 'Domain defined and started'}, - 'comment': 'Domain myvm defined and started'}) - self.assertDictEqual(virt.running('myvm', - cpu=2, - mem=2048, - image='/path/to/img.qcow2'), ret) - init_mock.assert_called_with('myvm', cpu=2, mem=2048, image='/path/to/img.qcow2', - os_type=None, arch=None, boot=None, - disk=None, disks=None, nic=None, interfaces=None, - graphics=None, hypervisor=None, - seed=True, install=True, pub_key=None, priv_key=None, - connection=None, username=None, password=None) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.vm_state": MagicMock( + side_effect=CommandExecutionError("not found") + ), + "virt.init": init_mock, + "virt.start": MagicMock(return_value=0), + }, + ): + ret.update( + { + "changes": {"myvm": "Domain defined and started"}, + "comment": "Domain myvm defined and started", + } + ) + self.assertDictEqual( + virt.running( + "myvm", + cpu=2, + mem=2048, + disks=[{"name": "system", "image": "/path/to/img.qcow2"}], + ), + ret, + ) + init_mock.assert_called_with( + "myvm", + cpu=2, + mem=2048, + os_type=None, + arch=None, + boot=None, + disk=None, + disks=[{"name": "system", "image": "/path/to/img.qcow2"}], + nic=None, + interfaces=None, + graphics=None, + hypervisor=None, + seed=True, + install=True, + pub_key=None, + priv_key=None, + connection=None, + username=None, + password=None, + ) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(side_effect=CommandExecutionError('not found')), - 'virt.init': init_mock, - 'virt.start': MagicMock(return_value=0) - }): - ret.update({'changes': {'myvm': 'Domain defined and started'}, - 'comment': 'Domain myvm defined and started'}) - disks = [{ - 'name': 'system', - 'size': 8192, - 'overlay_image': True, - 'pool': 'default', - 'image': '/path/to/image.qcow2' - }, - { - 'name': 'data', - 'size': 16834 - }] - ifaces = [{ - 'name': 'eth0', - 'mac': '01:23:45:67:89:AB' - }, - { - 'name': 'eth1', - 'type': 'network', - 'source': 'admin' - }] - graphics = {'type': 'spice', 'listen': {'type': 'address', 'address': '192.168.0.1'}} - self.assertDictEqual(virt.running('myvm', - cpu=2, - mem=2048, - os_type='linux', - arch='i686', - vm_type='qemu', - disk_profile='prod', - disks=disks, - nic_profile='prod', - interfaces=ifaces, - graphics=graphics, - seed=False, - install=False, - pub_key='/path/to/key.pub', - priv_key='/path/to/key', - connection='someconnection', - username='libvirtuser', - password='supersecret'), ret) - init_mock.assert_called_with('myvm', - cpu=2, - mem=2048, - os_type='linux', - arch='i686', - image=None, - disk='prod', - disks=disks, - nic='prod', - interfaces=ifaces, - graphics=graphics, - hypervisor='qemu', - seed=False, - boot=None, - install=False, - pub_key='/path/to/key.pub', - priv_key='/path/to/key', - connection='someconnection', - username='libvirtuser', - password='supersecret') + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.vm_state": MagicMock( + side_effect=CommandExecutionError("not found") + ), + "virt.init": init_mock, + "virt.start": MagicMock(return_value=0), + }, + ): + ret.update( + { + "changes": {"myvm": "Domain defined and started"}, + "comment": "Domain myvm defined and started", + } + ) + disks = [ + { + "name": "system", + "size": 8192, + "overlay_image": True, + "pool": "default", + "image": "/path/to/image.qcow2", + }, + {"name": "data", "size": 16834}, + ] + ifaces = [ + {"name": "eth0", "mac": "01:23:45:67:89:AB"}, + {"name": "eth1", "type": "network", "source": "admin"}, + ] + graphics = { + "type": "spice", + "listen": {"type": "address", "address": "192.168.0.1"}, + } + self.assertDictEqual( + virt.running( + "myvm", + cpu=2, + mem=2048, + os_type="linux", + arch="i686", + vm_type="qemu", + disk_profile="prod", + disks=disks, + nic_profile="prod", + interfaces=ifaces, + graphics=graphics, + seed=False, + install=False, + pub_key="/path/to/key.pub", + priv_key="/path/to/key", + connection="someconnection", + username="libvirtuser", + password="supersecret", + ), + ret, + ) + init_mock.assert_called_with( + "myvm", + cpu=2, + mem=2048, + os_type="linux", + arch="i686", + disk="prod", + disks=disks, + nic="prod", + interfaces=ifaces, + graphics=graphics, + hypervisor="qemu", + seed=False, + boot=None, + install=False, + pub_key="/path/to/key.pub", + priv_key="/path/to/key", + connection="someconnection", + username="libvirtuser", + password="supersecret", + ) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}), - 'virt.start': MagicMock(side_effect=[self.mock_libvirt.libvirtError('libvirt error msg')]) - }): - ret.update({'changes': {}, 'result': False, 'comment': 'libvirt error msg'}) - self.assertDictEqual(virt.running('myvm'), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.vm_state": MagicMock(return_value={"myvm": "stopped"}), + "virt.start": MagicMock( + side_effect=[self.mock_libvirt.libvirtError("libvirt error msg")] + ), + }, + ): + ret.update({"changes": {}, "result": False, "comment": "libvirt error msg"}) + self.assertDictEqual(virt.running("myvm"), ret) # Working update case when running - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.update': MagicMock(return_value={'definition': True, 'cpu': True}) - }): - ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}}, - 'result': True, - 'comment': 'Domain myvm updated, restart to fully apply the changes'}) - self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.update": MagicMock( + return_value={"definition": True, "cpu": True} + ), + }, + ): + ret.update( + { + "changes": {"myvm": {"definition": True, "cpu": True}}, + "result": True, + "comment": "Domain myvm updated, restart to fully apply the changes", + } + ) + self.assertDictEqual(virt.running("myvm", update=True, cpu=2), ret) # Working update case when running with boot params boot = { - 'kernel': '/root/f8-i386-vmlinuz', - 'initrd': '/root/f8-i386-initrd', - 'cmdline': 'console=ttyS0 ks=http://example.com/f8-i386/os/' + "kernel": "/root/f8-i386-vmlinuz", + "initrd": "/root/f8-i386-initrd", + "cmdline": "console=ttyS0 ks=http://example.com/f8-i386/os/", } - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.update': MagicMock(return_value={'definition': True, 'cpu': True}) - }): - ret.update({'changes': {'myvm': {'definition': True, 'cpu': True}}, - 'result': True, - 'comment': 'Domain myvm updated, restart to fully apply the changes'}) - self.assertDictEqual(virt.running('myvm', update=True, boot=boot), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.update": MagicMock( + return_value={"definition": True, "cpu": True} + ), + }, + ): + ret.update( + { + "changes": {"myvm": {"definition": True, "cpu": True}}, + "result": True, + "comment": "Domain myvm updated, restart to fully apply the changes", + } + ) + self.assertDictEqual(virt.running("myvm", update=True, boot=boot), ret) # Working update case when stopped - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value={'myvm': 'stopped'}), - 'virt.start': MagicMock(return_value=0), - 'virt.update': MagicMock(return_value={'definition': True}) - }): - ret.update({'changes': {'myvm': 'Domain updated and started'}, - 'result': True, - 'comment': 'Domain myvm updated and started'}) - self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.vm_state": MagicMock(return_value={"myvm": "stopped"}), + "virt.start": MagicMock(return_value=0), + "virt.update": MagicMock(return_value={"definition": True}), + }, + ): + ret.update( + { + "changes": {"myvm": "Domain updated and started"}, + "result": True, + "comment": "Domain myvm updated and started", + } + ) + self.assertDictEqual(virt.running("myvm", update=True, cpu=2), ret) # Failed live update case - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.update': MagicMock(return_value={'definition': True, 'cpu': False, 'errors': ['some error']}) - }): - ret.update({'changes': {'myvm': {'definition': True, 'cpu': False, 'errors': ['some error']}}, - 'result': True, - 'comment': 'Domain myvm updated, but some live update(s) failed'}) - self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.update": MagicMock( + return_value={ + "definition": True, + "cpu": False, + "errors": ["some error"], + } + ), + }, + ): + ret.update( + { + "changes": { + "myvm": { + "definition": True, + "cpu": False, + "errors": ["some error"], + } + }, + "result": True, + "comment": "Domain myvm updated, but some live update(s) failed", + } + ) + self.assertDictEqual(virt.running("myvm", update=True, cpu=2), ret) # Failed definition update case - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.update': MagicMock(side_effect=[self.mock_libvirt.libvirtError('error message')]) - }): - ret.update({'changes': {}, - 'result': False, - 'comment': 'error message'}) - self.assertDictEqual(virt.running('myvm', update=True, cpu=2), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.update": MagicMock( + side_effect=[self.mock_libvirt.libvirtError("error message")] + ), + }, + ): + ret.update({"changes": {}, "result": False, "comment": "error message"}) + self.assertDictEqual(virt.running("myvm", update=True, cpu=2), ret) def test_stopped(self): - ''' + """ stopped state test cases. - ''' - ret = {'name': 'myvm', - 'changes': {}, - 'result': True} + """ + ret = {"name": "myvm", "changes": {}, "result": True} shutdown_mock = MagicMock(return_value=True) # Normal case - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.shutdown': shutdown_mock - }): - ret.update({'changes': { - 'stopped': [{'domain': 'myvm', 'shutdown': True}] - }, - 'comment': 'Machine has been shut down'}) - self.assertDictEqual(virt.stopped('myvm'), ret) - shutdown_mock.assert_called_with('myvm', connection=None, username=None, password=None) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.shutdown": shutdown_mock, + }, + ): + ret.update( + { + "changes": {"stopped": [{"domain": "myvm", "shutdown": True}]}, + "comment": "Machine has been shut down", + } + ) + self.assertDictEqual(virt.stopped("myvm"), ret) + shutdown_mock.assert_called_with( + "myvm", connection=None, username=None, password=None + ) # Normal case with user-provided connection parameters - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.shutdown': shutdown_mock, - }): - self.assertDictEqual(virt.stopped('myvm', - connection='myconnection', - username='user', - password='secret'), ret) - shutdown_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret') + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.shutdown": shutdown_mock, + }, + ): + self.assertDictEqual( + virt.stopped( + "myvm", + connection="myconnection", + username="user", + password="secret", + ), + ret, + ) + shutdown_mock.assert_called_with( + "myvm", connection="myconnection", username="user", password="secret" + ) # Case where an error occurred during the shutdown - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.shutdown': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error')) - }): - ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]}, - 'result': False, - 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.stopped('myvm'), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.shutdown": MagicMock( + side_effect=self.mock_libvirt.libvirtError("Some error") + ), + }, + ): + ret.update( + { + "changes": {"ignored": [{"domain": "myvm", "issue": "Some error"}]}, + "result": False, + "comment": "No changes had happened", + } + ) + self.assertDictEqual(virt.stopped("myvm"), ret) # Case there the domain doesn't exist - with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member - ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.stopped('myvm'), ret) + with patch.dict( + virt.__salt__, {"virt.list_domains": MagicMock(return_value=[])} + ): # pylint: disable=no-member + ret.update( + {"changes": {}, "result": False, "comment": "No changes had happened"} + ) + self.assertDictEqual(virt.stopped("myvm"), ret) # Case where the domain is already stopped - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.vm_state': MagicMock(return_value={'myvm': 'shutdown'}) - }): - ret.update({'changes': {}, - 'result': True, - 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.stopped('myvm'), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.vm_state": MagicMock(return_value={"myvm": "shutdown"}), + }, + ): + ret.update( + {"changes": {}, "result": True, "comment": "No changes had happened"} + ) + self.assertDictEqual(virt.stopped("myvm"), ret) def test_powered_off(self): - ''' + """ powered_off state test cases. - ''' - ret = {'name': 'myvm', - 'changes': {}, - 'result': True} + """ + ret = {"name": "myvm", "changes": {}, "result": True} stop_mock = MagicMock(return_value=True) # Normal case - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.stop': stop_mock - }): - ret.update({'changes': { - 'unpowered': [{'domain': 'myvm', 'stop': True}] - }, - 'comment': 'Machine has been powered off'}) - self.assertDictEqual(virt.powered_off('myvm'), ret) - stop_mock.assert_called_with('myvm', connection=None, username=None, password=None) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.stop": stop_mock, + }, + ): + ret.update( + { + "changes": {"unpowered": [{"domain": "myvm", "stop": True}]}, + "comment": "Machine has been powered off", + } + ) + self.assertDictEqual(virt.powered_off("myvm"), ret) + stop_mock.assert_called_with( + "myvm", connection=None, username=None, password=None + ) # Normal case with user-provided connection parameters - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.stop': stop_mock, - }): - self.assertDictEqual(virt.powered_off('myvm', - connection='myconnection', - username='user', - password='secret'), ret) - stop_mock.assert_called_with('myvm', connection='myconnection', username='user', password='secret') + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.stop": stop_mock, + }, + ): + self.assertDictEqual( + virt.powered_off( + "myvm", + connection="myconnection", + username="user", + password="secret", + ), + ret, + ) + stop_mock.assert_called_with( + "myvm", connection="myconnection", username="user", password="secret" + ) # Case where an error occurred during the poweroff - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.vm_state': MagicMock(return_value={'myvm': 'running'}), - 'virt.stop': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error')) - }): - ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]}, - 'result': False, - 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.powered_off('myvm'), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.vm_state": MagicMock(return_value={"myvm": "running"}), + "virt.stop": MagicMock( + side_effect=self.mock_libvirt.libvirtError("Some error") + ), + }, + ): + ret.update( + { + "changes": {"ignored": [{"domain": "myvm", "issue": "Some error"}]}, + "result": False, + "comment": "No changes had happened", + } + ) + self.assertDictEqual(virt.powered_off("myvm"), ret) # Case there the domain doesn't exist - with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member - ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.powered_off('myvm'), ret) + with patch.dict( + virt.__salt__, {"virt.list_domains": MagicMock(return_value=[])} + ): # pylint: disable=no-member + ret.update( + {"changes": {}, "result": False, "comment": "No changes had happened"} + ) + self.assertDictEqual(virt.powered_off("myvm"), ret) # Case where the domain is already stopped - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.vm_state': MagicMock(return_value={'myvm': 'shutdown'}) - }): - ret.update({'changes': {}, - 'result': True, - 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.powered_off('myvm'), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.vm_state": MagicMock(return_value={"myvm": "shutdown"}), + }, + ): + ret.update( + {"changes": {}, "result": True, "comment": "No changes had happened"} + ) + self.assertDictEqual(virt.powered_off("myvm"), ret) def test_snapshot(self): - ''' + """ snapshot state test cases. - ''' - ret = {'name': 'myvm', - 'changes': {}, - 'result': True} + """ + ret = {"name": "myvm", "changes": {}, "result": True} snapshot_mock = MagicMock(return_value=True) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.snapshot': snapshot_mock - }): - ret.update({'changes': { - 'saved': [{'domain': 'myvm', 'snapshot': True}] - }, - 'comment': 'Snapshot has been taken'}) - self.assertDictEqual(virt.snapshot('myvm'), ret) - snapshot_mock.assert_called_with('myvm', suffix=None, connection=None, username=None, password=None) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.snapshot": snapshot_mock, + }, + ): + ret.update( + { + "changes": {"saved": [{"domain": "myvm", "snapshot": True}]}, + "comment": "Snapshot has been taken", + } + ) + self.assertDictEqual(virt.snapshot("myvm"), ret) + snapshot_mock.assert_called_with( + "myvm", suffix=None, connection=None, username=None, password=None + ) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.snapshot': snapshot_mock, - }): - self.assertDictEqual(virt.snapshot('myvm', - suffix='snap', - connection='myconnection', - username='user', - password='secret'), ret) - snapshot_mock.assert_called_with('myvm', - suffix='snap', - connection='myconnection', - username='user', - password='secret') + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.snapshot": snapshot_mock, + }, + ): + self.assertDictEqual( + virt.snapshot( + "myvm", + suffix="snap", + connection="myconnection", + username="user", + password="secret", + ), + ret, + ) + snapshot_mock.assert_called_with( + "myvm", + suffix="snap", + connection="myconnection", + username="user", + password="secret", + ) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.snapshot': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error')) - }): - ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]}, - 'result': False, - 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.snapshot('myvm'), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.snapshot": MagicMock( + side_effect=self.mock_libvirt.libvirtError("Some error") + ), + }, + ): + ret.update( + { + "changes": {"ignored": [{"domain": "myvm", "issue": "Some error"}]}, + "result": False, + "comment": "No changes had happened", + } + ) + self.assertDictEqual(virt.snapshot("myvm"), ret) - with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member - ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.snapshot('myvm'), ret) + with patch.dict( + virt.__salt__, {"virt.list_domains": MagicMock(return_value=[])} + ): # pylint: disable=no-member + ret.update( + {"changes": {}, "result": False, "comment": "No changes had happened"} + ) + self.assertDictEqual(virt.snapshot("myvm"), ret) def test_rebooted(self): - ''' + """ rebooted state test cases. - ''' - ret = {'name': 'myvm', - 'changes': {}, - 'result': True} + """ + ret = {"name": "myvm", "changes": {}, "result": True} reboot_mock = MagicMock(return_value=True) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.reboot': reboot_mock - }): - ret.update({'changes': { - 'rebooted': [{'domain': 'myvm', 'reboot': True}] - }, - 'comment': 'Machine has been rebooted'}) - self.assertDictEqual(virt.rebooted('myvm'), ret) - reboot_mock.assert_called_with('myvm', connection=None, username=None, password=None) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.reboot": reboot_mock, + }, + ): + ret.update( + { + "changes": {"rebooted": [{"domain": "myvm", "reboot": True}]}, + "comment": "Machine has been rebooted", + } + ) + self.assertDictEqual(virt.rebooted("myvm"), ret) + reboot_mock.assert_called_with( + "myvm", connection=None, username=None, password=None + ) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.reboot': reboot_mock, - }): - self.assertDictEqual(virt.rebooted('myvm', - connection='myconnection', - username='user', - password='secret'), ret) - reboot_mock.assert_called_with('myvm', - connection='myconnection', - username='user', - password='secret') + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.reboot": reboot_mock, + }, + ): + self.assertDictEqual( + virt.rebooted( + "myvm", + connection="myconnection", + username="user", + password="secret", + ), + ret, + ) + reboot_mock.assert_called_with( + "myvm", connection="myconnection", username="user", password="secret" + ) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.list_domains': MagicMock(return_value=['myvm', 'vm1']), - 'virt.reboot': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error')) - }): - ret.update({'changes': {'ignored': [{'domain': 'myvm', 'issue': 'Some error'}]}, - 'result': False, - 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.rebooted('myvm'), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.list_domains": MagicMock(return_value=["myvm", "vm1"]), + "virt.reboot": MagicMock( + side_effect=self.mock_libvirt.libvirtError("Some error") + ), + }, + ): + ret.update( + { + "changes": {"ignored": [{"domain": "myvm", "issue": "Some error"}]}, + "result": False, + "comment": "No changes had happened", + } + ) + self.assertDictEqual(virt.rebooted("myvm"), ret) - with patch.dict(virt.__salt__, {'virt.list_domains': MagicMock(return_value=[])}): # pylint: disable=no-member - ret.update({'changes': {}, 'result': False, 'comment': 'No changes had happened'}) - self.assertDictEqual(virt.rebooted('myvm'), ret) + with patch.dict( + virt.__salt__, {"virt.list_domains": MagicMock(return_value=[])} + ): # pylint: disable=no-member + ret.update( + {"changes": {}, "result": False, "comment": "No changes had happened"} + ) + self.assertDictEqual(virt.rebooted("myvm"), ret) def test_network_running(self): - ''' + """ network_running state test cases. - ''' - ret = {'name': 'mynet', 'changes': {}, 'result': True, 'comment': ''} + """ + ret = {"name": "mynet", "changes": {}, "result": True, "comment": ""} define_mock = MagicMock(return_value=True) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.network_info': MagicMock(return_value={}), - 'virt.network_define': define_mock - }): - ret.update({'changes': {'mynet': 'Network defined and started'}, - 'comment': 'Network mynet defined and started'}) - self.assertDictEqual(virt.network_running('mynet', - 'br2', - 'bridge', - vport='openvswitch', - tag=180, - ipv4_config={ - 'cidr': '192.168.2.0/24', - 'dhcp_ranges': [ - {'start': '192.168.2.10', 'end': '192.168.2.25'}, - {'start': '192.168.2.110', 'end': '192.168.2.125'}, - ] - }, - ipv6_config={ - 'cidr': '2001:db8:ca2:2::1/64', - 'dhcp_ranges': [ - {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'}, - ] - }, - autostart=False, - connection='myconnection', - username='user', - password='secret'), ret) - define_mock.assert_called_with('mynet', - 'br2', - 'bridge', - vport='openvswitch', - tag=180, - autostart=False, - start=True, - ipv4_config={ - 'cidr': '192.168.2.0/24', - 'dhcp_ranges': [ - {'start': '192.168.2.10', 'end': '192.168.2.25'}, - {'start': '192.168.2.110', 'end': '192.168.2.125'}, - ] - }, - ipv6_config={ - 'cidr': '2001:db8:ca2:2::1/64', - 'dhcp_ranges': [ - {'start': '2001:db8:ca2:1::10', 'end': '2001:db8:ca2::1f'}, - ] - }, - connection='myconnection', - username='user', - password='secret') + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.network_info": MagicMock(return_value={}), + "virt.network_define": define_mock, + }, + ): + ret.update( + { + "changes": {"mynet": "Network defined and started"}, + "comment": "Network mynet defined and started", + } + ) + self.assertDictEqual( + virt.network_running( + "mynet", + "br2", + "bridge", + vport="openvswitch", + tag=180, + ipv4_config={ + "cidr": "192.168.2.0/24", + "dhcp_ranges": [ + {"start": "192.168.2.10", "end": "192.168.2.25"}, + {"start": "192.168.2.110", "end": "192.168.2.125"}, + ], + }, + ipv6_config={ + "cidr": "2001:db8:ca2:2::1/64", + "dhcp_ranges": [ + {"start": "2001:db8:ca2:1::10", "end": "2001:db8:ca2::1f"}, + ], + }, + autostart=False, + connection="myconnection", + username="user", + password="secret", + ), + ret, + ) + define_mock.assert_called_with( + "mynet", + "br2", + "bridge", + vport="openvswitch", + tag=180, + autostart=False, + start=True, + ipv4_config={ + "cidr": "192.168.2.0/24", + "dhcp_ranges": [ + {"start": "192.168.2.10", "end": "192.168.2.25"}, + {"start": "192.168.2.110", "end": "192.168.2.125"}, + ], + }, + ipv6_config={ + "cidr": "2001:db8:ca2:2::1/64", + "dhcp_ranges": [ + {"start": "2001:db8:ca2:1::10", "end": "2001:db8:ca2::1f"}, + ], + }, + connection="myconnection", + username="user", + password="secret", + ) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.network_info': MagicMock(return_value={'mynet': {'active': True}}), - 'virt.network_define': define_mock, - }): - ret.update({'changes': {}, 'comment': 'Network mynet exists and is running'}) - self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.network_info": MagicMock( + return_value={"mynet": {"active": True}} + ), + "virt.network_define": define_mock, + }, + ): + ret.update( + {"changes": {}, "comment": "Network mynet exists and is running"} + ) + self.assertDictEqual(virt.network_running("mynet", "br2", "bridge"), ret) start_mock = MagicMock(return_value=True) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.network_info': MagicMock(return_value={'mynet': {'active': False}}), - 'virt.network_start': start_mock, - 'virt.network_define': define_mock, - }): - ret.update({'changes': {'mynet': 'Network started'}, 'comment': 'Network mynet started'}) - self.assertDictEqual(virt.network_running('mynet', - 'br2', - 'bridge', - connection='myconnection', - username='user', - password='secret'), ret) - start_mock.assert_called_with('mynet', connection='myconnection', username='user', password='secret') + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.network_info": MagicMock( + return_value={"mynet": {"active": False}} + ), + "virt.network_start": start_mock, + "virt.network_define": define_mock, + }, + ): + ret.update( + { + "changes": {"mynet": "Network started"}, + "comment": "Network mynet started", + } + ) + self.assertDictEqual( + virt.network_running( + "mynet", + "br2", + "bridge", + connection="myconnection", + username="user", + password="secret", + ), + ret, + ) + start_mock.assert_called_with( + "mynet", connection="myconnection", username="user", password="secret" + ) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.network_info': MagicMock(return_value={}), - 'virt.network_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error')) - }): - ret.update({'changes': {}, 'comment': 'Some error', 'result': False}) - self.assertDictEqual(virt.network_running('mynet', 'br2', 'bridge'), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.network_info": MagicMock(return_value={}), + "virt.network_define": MagicMock( + side_effect=self.mock_libvirt.libvirtError("Some error") + ), + }, + ): + ret.update({"changes": {}, "comment": "Some error", "result": False}) + self.assertDictEqual(virt.network_running("mynet", "br2", "bridge"), ret) def test_pool_running(self): - ''' + """ pool_running state test cases. - ''' - ret = {'name': 'mypool', 'changes': {}, 'result': True, 'comment': ''} - mocks = {mock: MagicMock(return_value=True) for mock in ['define', 'autostart', 'build', 'start', 'stop']} - with patch.dict(virt.__opts__, {'test': False}): - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={}), - 'virt.pool_define': mocks['define'], - 'virt.pool_build': mocks['build'], - 'virt.pool_start': mocks['start'], - 'virt.pool_set_autostart': mocks['autostart'] - }): - ret.update({'changes': {'mypool': 'Pool defined, started and marked for autostart'}, - 'comment': 'Pool mypool defined, started and marked for autostart'}) - self.assertDictEqual(virt.pool_running('mypool', - ptype='logical', - target='/dev/base', - permissions={'mode': '0770', - 'owner': 1000, - 'group': 100, - 'label': 'seclabel'}, - source={'devices': [{'path': '/dev/sda'}]}, - transient=True, - autostart=True, - connection='myconnection', - username='user', - password='secret'), ret) - mocks['define'].assert_called_with('mypool', - ptype='logical', - target='/dev/base', - permissions={'mode': '0770', - 'owner': 1000, - 'group': 100, - 'label': 'seclabel'}, - source_devices=[{'path': '/dev/sda'}], - source_dir=None, - source_adapter=None, - source_hosts=None, - source_auth=None, - source_name=None, - source_format=None, - source_initiator=None, - transient=True, - start=False, - connection='myconnection', - username='user', - password='secret') - mocks['autostart'].assert_called_with('mypool', - state='on', - connection='myconnection', - username='user', - password='secret') - mocks['build'].assert_called_with('mypool', - connection='myconnection', - username='user', - password='secret') - mocks['start'].assert_called_with('mypool', - connection='myconnection', - username='user', - password='secret') + """ + ret = {"name": "mypool", "changes": {}, "result": True, "comment": ""} + mocks = { + mock: MagicMock(return_value=True) + for mock in ["define", "autostart", "build", "start", "stop"] + } + with patch.dict(virt.__opts__, {"test": False}): + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.pool_info": MagicMock(return_value={}), + "virt.pool_define": mocks["define"], + "virt.pool_build": mocks["build"], + "virt.pool_start": mocks["start"], + "virt.pool_set_autostart": mocks["autostart"], + }, + ): + ret.update( + { + "changes": { + "mypool": "Pool defined, started and marked for autostart" + }, + "comment": "Pool mypool defined, started and marked for autostart", + } + ) + self.assertDictEqual( + virt.pool_running( + "mypool", + ptype="logical", + target="/dev/base", + permissions={ + "mode": "0770", + "owner": 1000, + "group": 100, + "label": "seclabel", + }, + source={"devices": [{"path": "/dev/sda"}]}, + transient=True, + autostart=True, + connection="myconnection", + username="user", + password="secret", + ), + ret, + ) + mocks["define"].assert_called_with( + "mypool", + ptype="logical", + target="/dev/base", + permissions={ + "mode": "0770", + "owner": 1000, + "group": 100, + "label": "seclabel", + }, + source_devices=[{"path": "/dev/sda"}], + source_dir=None, + source_adapter=None, + source_hosts=None, + source_auth=None, + source_name=None, + source_format=None, + source_initiator=None, + transient=True, + start=False, + connection="myconnection", + username="user", + password="secret", + ) + mocks["autostart"].assert_called_with( + "mypool", + state="on", + connection="myconnection", + username="user", + password="secret", + ) + mocks["build"].assert_called_with( + "mypool", + connection="myconnection", + username="user", + password="secret", + ) + mocks["start"].assert_called_with( + "mypool", + connection="myconnection", + username="user", + password="secret", + ) - mocks['update'] = MagicMock(return_value=False) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': True}}), - 'virt.pool_update': MagicMock(return_value=False), - }): - ret.update({'changes': {}, 'comment': 'Pool mypool unchanged and is running'}) - self.assertDictEqual(virt.pool_running('mypool', - ptype='logical', - target='/dev/base', - source={'devices': [{'path': '/dev/sda'}]}), ret) + mocks["update"] = MagicMock(return_value=False) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.pool_info": MagicMock( + return_value={"mypool": {"state": "running", "autostart": True}} + ), + "virt.pool_update": MagicMock(return_value=False), + }, + ): + ret.update( + {"changes": {}, "comment": "Pool mypool unchanged and is running"} + ) + self.assertDictEqual( + virt.pool_running( + "mypool", + ptype="logical", + target="/dev/base", + source={"devices": [{"path": "/dev/sda"}]}, + ), + ret, + ) for mock in mocks: mocks[mock].reset_mock() - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'stopped', 'autostart': True}}), - 'virt.pool_update': mocks['update'], - 'virt.pool_build': mocks['build'], - 'virt.pool_start': mocks['start'] - }): - ret.update({'changes': {'mypool': 'Pool started'}, 'comment': 'Pool mypool started'}) - self.assertDictEqual(virt.pool_running('mypool', - ptype='logical', - target='/dev/base', - source={'devices': [{'path': '/dev/sda'}]}), ret) - mocks['start'].assert_called_with('mypool', connection=None, username=None, password=None) - mocks['build'].assert_not_called() + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.pool_info": MagicMock( + return_value={"mypool": {"state": "stopped", "autostart": True}} + ), + "virt.pool_update": mocks["update"], + "virt.pool_build": mocks["build"], + "virt.pool_start": mocks["start"], + }, + ): + ret.update( + { + "changes": {"mypool": "Pool started"}, + "comment": "Pool mypool started", + } + ) + self.assertDictEqual( + virt.pool_running( + "mypool", + ptype="logical", + target="/dev/base", + source={"devices": [{"path": "/dev/sda"}]}, + ), + ret, + ) + mocks["start"].assert_called_with( + "mypool", connection=None, username=None, password=None + ) + mocks["build"].assert_not_called() - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={}), - 'virt.pool_define': MagicMock(side_effect=self.mock_libvirt.libvirtError('Some error')) - }): - ret.update({'changes': {}, 'comment': 'Some error', 'result': False}) - self.assertDictEqual(virt.pool_running('mypool', - ptype='logical', - target='/dev/base', - source={'devices': [{'path': '/dev/sda'}]}), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.pool_info": MagicMock(return_value={}), + "virt.pool_define": MagicMock( + side_effect=self.mock_libvirt.libvirtError("Some error") + ), + }, + ): + ret.update({"changes": {}, "comment": "Some error", "result": False}) + self.assertDictEqual( + virt.pool_running( + "mypool", + ptype="logical", + target="/dev/base", + source={"devices": [{"path": "/dev/sda"}]}, + ), + ret, + ) # Test case with update and autostart change on stopped pool for mock in mocks: mocks[mock].reset_mock() - mocks['update'] = MagicMock(return_value=True) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'stopped', 'autostart': True}}), - 'virt.pool_update': mocks['update'], - 'virt.pool_set_autostart': mocks['autostart'], - 'virt.pool_build': mocks['build'], - 'virt.pool_start': mocks['start'] - }): - ret.update({'changes': {'mypool': 'Pool updated, built, autostart flag changed and started'}, - 'comment': 'Pool mypool updated, built, autostart flag changed and started', - 'result': True}) - self.assertDictEqual(virt.pool_running('mypool', - ptype='logical', - target='/dev/base', - autostart=False, - permissions={'mode': '0770', - 'owner': 1000, - 'group': 100, - 'label': 'seclabel'}, - source={'devices': [{'path': '/dev/sda'}]}), ret) - mocks['start'].assert_called_with('mypool', connection=None, username=None, password=None) - mocks['build'].assert_called_with('mypool', connection=None, username=None, password=None) - mocks['autostart'].assert_called_with('mypool', state='off', - connection=None, username=None, password=None) - mocks['update'].assert_called_with('mypool', - ptype='logical', - target='/dev/base', - permissions={'mode': '0770', - 'owner': 1000, - 'group': 100, - 'label': 'seclabel'}, - source_devices=[{'path': '/dev/sda'}], - source_dir=None, - source_adapter=None, - source_hosts=None, - source_auth=None, - source_name=None, - source_format=None, - source_initiator=None, - connection=None, - username=None, - password=None) + mocks["update"] = MagicMock(return_value=True) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.pool_info": MagicMock( + return_value={"mypool": {"state": "stopped", "autostart": True}} + ), + "virt.pool_update": mocks["update"], + "virt.pool_set_autostart": mocks["autostart"], + "virt.pool_build": mocks["build"], + "virt.pool_start": mocks["start"], + }, + ): + ret.update( + { + "changes": { + "mypool": "Pool updated, built, autostart flag changed and started" + }, + "comment": "Pool mypool updated, built, autostart flag changed and started", + "result": True, + } + ) + self.assertDictEqual( + virt.pool_running( + "mypool", + ptype="logical", + target="/dev/base", + autostart=False, + permissions={ + "mode": "0770", + "owner": 1000, + "group": 100, + "label": "seclabel", + }, + source={"devices": [{"path": "/dev/sda"}]}, + ), + ret, + ) + mocks["start"].assert_called_with( + "mypool", connection=None, username=None, password=None + ) + mocks["build"].assert_called_with( + "mypool", connection=None, username=None, password=None + ) + mocks["autostart"].assert_called_with( + "mypool", state="off", connection=None, username=None, password=None + ) + mocks["update"].assert_called_with( + "mypool", + ptype="logical", + target="/dev/base", + permissions={ + "mode": "0770", + "owner": 1000, + "group": 100, + "label": "seclabel", + }, + source_devices=[{"path": "/dev/sda"}], + source_dir=None, + source_adapter=None, + source_hosts=None, + source_auth=None, + source_name=None, + source_format=None, + source_initiator=None, + connection=None, + username=None, + password=None, + ) # test case with update and no autostart change on running pool for mock in mocks: mocks[mock].reset_mock() - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': False}}), - 'virt.pool_update': mocks['update'], - 'virt.pool_build': mocks['build'], - 'virt.pool_start': mocks['start'], - 'virt.pool_stop': mocks['stop'] - }): - ret.update({'changes': {'mypool': 'Pool updated, built and restarted'}, - 'comment': 'Pool mypool updated, built and restarted', - 'result': True}) - self.assertDictEqual(virt.pool_running('mypool', - ptype='logical', - target='/dev/base', - autostart=False, - permissions={'mode': '0770', - 'owner': 1000, - 'group': 100, - 'label': 'seclabel'}, - source={'devices': [{'path': '/dev/sda'}]}), ret) - mocks['stop'].assert_called_with('mypool', connection=None, username=None, password=None) - mocks['start'].assert_called_with('mypool', connection=None, username=None, password=None) - mocks['build'].assert_called_with('mypool', connection=None, username=None, password=None) - mocks['update'].assert_called_with('mypool', - ptype='logical', - target='/dev/base', - permissions={'mode': '0770', - 'owner': 1000, - 'group': 100, - 'label': 'seclabel'}, - source_devices=[{'path': '/dev/sda'}], - source_dir=None, - source_adapter=None, - source_hosts=None, - source_auth=None, - source_name=None, - source_format=None, - source_initiator=None, - connection=None, - username=None, - password=None) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.pool_info": MagicMock( + return_value={ + "mypool": {"state": "running", "autostart": False} + } + ), + "virt.pool_update": mocks["update"], + "virt.pool_build": mocks["build"], + "virt.pool_start": mocks["start"], + "virt.pool_stop": mocks["stop"], + }, + ): + ret.update( + { + "changes": {"mypool": "Pool updated, built and restarted"}, + "comment": "Pool mypool updated, built and restarted", + "result": True, + } + ) + self.assertDictEqual( + virt.pool_running( + "mypool", + ptype="logical", + target="/dev/base", + autostart=False, + permissions={ + "mode": "0770", + "owner": 1000, + "group": 100, + "label": "seclabel", + }, + source={"devices": [{"path": "/dev/sda"}]}, + ), + ret, + ) + mocks["stop"].assert_called_with( + "mypool", connection=None, username=None, password=None + ) + mocks["start"].assert_called_with( + "mypool", connection=None, username=None, password=None + ) + mocks["build"].assert_called_with( + "mypool", connection=None, username=None, password=None + ) + mocks["update"].assert_called_with( + "mypool", + ptype="logical", + target="/dev/base", + permissions={ + "mode": "0770", + "owner": 1000, + "group": 100, + "label": "seclabel", + }, + source_devices=[{"path": "/dev/sda"}], + source_dir=None, + source_adapter=None, + source_hosts=None, + source_auth=None, + source_name=None, + source_format=None, + source_initiator=None, + connection=None, + username=None, + password=None, + ) - with patch.dict(virt.__opts__, {'test': True}): + with patch.dict(virt.__opts__, {"test": True}): # test case with test=True and no change - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'running', 'autostart': True}}), - 'virt.pool_update': MagicMock(return_value=False), - }): - ret.update({'changes': {}, 'comment': 'Pool mypool unchanged and is running', - 'result': True}) - self.assertDictEqual(virt.pool_running('mypool', - ptype='logical', - target='/dev/base', - source={'devices': [{'path': '/dev/sda'}]}), ret) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.pool_info": MagicMock( + return_value={"mypool": {"state": "running", "autostart": True}} + ), + "virt.pool_update": MagicMock(return_value=False), + }, + ): + ret.update( + { + "changes": {}, + "comment": "Pool mypool unchanged and is running", + "result": True, + } + ) + self.assertDictEqual( + virt.pool_running( + "mypool", + ptype="logical", + target="/dev/base", + source={"devices": [{"path": "/dev/sda"}]}, + ), + ret, + ) # test case with test=True and started for mock in mocks: mocks[mock].reset_mock() - mocks['update'] = MagicMock(return_value=False) - with patch.dict(virt.__salt__, { # pylint: disable=no-member - 'virt.pool_info': MagicMock(return_value={'mypool': {'state': 'stopped', 'autostart': True}}), - 'virt.pool_update': mocks['update'] - }): - ret.update({'changes': {'mypool': 'Pool started'}, - 'comment': 'Pool mypool started', - 'result': None}) - self.assertDictEqual(virt.pool_running('mypool', - ptype='logical', - target='/dev/base', - source={'devices': [{'path': '/dev/sda'}]}), ret) + mocks["update"] = MagicMock(return_value=False) + with patch.dict( + virt.__salt__, + { # pylint: disable=no-member + "virt.pool_info": MagicMock( + return_value={"mypool": {"state": "stopped", "autostart": True}} + ), + "virt.pool_update": mocks["update"], + }, + ): + ret.update( + { + "changes": {"mypool": "Pool started"}, + "comment": "Pool mypool started", + "result": None, + } + ) + self.assertDictEqual( + virt.pool_running( + "mypool", + ptype="logical", + target="/dev/base", + source={"devices": [{"path": "/dev/sda"}]}, + ), + ret, + ) def test_pool_deleted(self): - ''' + """ Test the pool_deleted state - ''' + """ # purge=False test case, stopped pool - with patch.dict(virt.__salt__, { - 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'stopped', 'type': 'dir'}}), - 'virt.pool_undefine': MagicMock(return_value=True) - }): + with patch.dict( + virt.__salt__, + { + "virt.pool_info": MagicMock( + return_value={"test01": {"state": "stopped", "type": "dir"}} + ), + "virt.pool_undefine": MagicMock(return_value=True), + }, + ): expected = { - 'name': 'test01', - 'changes': { - 'stopped': False, - 'deleted_volumes': [], - 'deleted': False, - 'undefined': True, + "name": "test01", + "changes": { + "stopped": False, + "deleted_volumes": [], + "deleted": False, + "undefined": True, }, - 'result': True, - 'comment': '', + "result": True, + "comment": "", } - with patch.dict(virt.__opts__, {'test': False}): - self.assertDictEqual(expected, virt.pool_deleted('test01')) + with patch.dict(virt.__opts__, {"test": False}): + self.assertDictEqual(expected, virt.pool_deleted("test01")) - with patch.dict(virt.__opts__, {'test': True}): - expected['result'] = None - self.assertDictEqual(expected, virt.pool_deleted('test01')) + with patch.dict(virt.__opts__, {"test": True}): + expected["result"] = None + self.assertDictEqual(expected, virt.pool_deleted("test01")) # purge=False test case - with patch.dict(virt.__salt__, { - 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'running', 'type': 'dir'}}), - 'virt.pool_undefine': MagicMock(return_value=True), - 'virt.pool_stop': MagicMock(return_value=True) - }): + with patch.dict( + virt.__salt__, + { + "virt.pool_info": MagicMock( + return_value={"test01": {"state": "running", "type": "dir"}} + ), + "virt.pool_undefine": MagicMock(return_value=True), + "virt.pool_stop": MagicMock(return_value=True), + }, + ): expected = { - 'name': 'test01', - 'changes': { - 'stopped': True, - 'deleted_volumes': [], - 'deleted': False, - 'undefined': True, + "name": "test01", + "changes": { + "stopped": True, + "deleted_volumes": [], + "deleted": False, + "undefined": True, }, - 'result': True, - 'comment': '', + "result": True, + "comment": "", } - with patch.dict(virt.__opts__, {'test': False}): - self.assertDictEqual(expected, virt.pool_deleted('test01')) + with patch.dict(virt.__opts__, {"test": False}): + self.assertDictEqual(expected, virt.pool_deleted("test01")) - with patch.dict(virt.__opts__, {'test': True}): - expected['result'] = None - self.assertDictEqual(expected, virt.pool_deleted('test01')) + with patch.dict(virt.__opts__, {"test": True}): + expected["result"] = None + self.assertDictEqual(expected, virt.pool_deleted("test01")) # purge=True test case - with patch.dict(virt.__salt__, { - 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'running', 'type': 'dir'}}), - 'virt.pool_list_volumes': MagicMock(return_value=['vm01.qcow2', 'vm02.qcow2']), - 'virt.pool_refresh': MagicMock(return_value=True), - 'virt.volume_delete': MagicMock(return_value=True), - 'virt.pool_stop': MagicMock(return_value=True), - 'virt.pool_delete': MagicMock(return_value=True), - 'virt.pool_undefine': MagicMock(return_value=True) - }): + with patch.dict( + virt.__salt__, + { + "virt.pool_info": MagicMock( + return_value={"test01": {"state": "running", "type": "dir"}} + ), + "virt.pool_list_volumes": MagicMock( + return_value=["vm01.qcow2", "vm02.qcow2"] + ), + "virt.pool_refresh": MagicMock(return_value=True), + "virt.volume_delete": MagicMock(return_value=True), + "virt.pool_stop": MagicMock(return_value=True), + "virt.pool_delete": MagicMock(return_value=True), + "virt.pool_undefine": MagicMock(return_value=True), + }, + ): expected = { - 'name': 'test01', - 'changes': { - 'stopped': True, - 'deleted_volumes': ['vm01.qcow2', 'vm02.qcow2'], - 'deleted': True, - 'undefined': True, + "name": "test01", + "changes": { + "stopped": True, + "deleted_volumes": ["vm01.qcow2", "vm02.qcow2"], + "deleted": True, + "undefined": True, }, - 'result': True, - 'comment': '', + "result": True, + "comment": "", } - with patch.dict(virt.__opts__, {'test': False}): - self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True)) + with patch.dict(virt.__opts__, {"test": False}): + self.assertDictEqual(expected, virt.pool_deleted("test01", purge=True)) - with patch.dict(virt.__opts__, {'test': True}): - expected['result'] = None - self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True)) + with patch.dict(virt.__opts__, {"test": True}): + expected["result"] = None + self.assertDictEqual(expected, virt.pool_deleted("test01", purge=True)) # Case of backend not unsupporting delete operations - with patch.dict(virt.__salt__, { - 'virt.pool_info': MagicMock(return_value={'test01': {'state': 'running', 'type': 'iscsi'}}), - 'virt.pool_stop': MagicMock(return_value=True), - 'virt.pool_undefine': MagicMock(return_value=True) - }): + with patch.dict( + virt.__salt__, + { + "virt.pool_info": MagicMock( + return_value={"test01": {"state": "running", "type": "iscsi"}} + ), + "virt.pool_stop": MagicMock(return_value=True), + "virt.pool_undefine": MagicMock(return_value=True), + }, + ): expected = { - 'name': 'test01', - 'changes': { - 'stopped': True, - 'deleted_volumes': [], - 'deleted': False, - 'undefined': True, + "name": "test01", + "changes": { + "stopped": True, + "deleted_volumes": [], + "deleted": False, + "undefined": True, }, - 'result': True, - 'comment': 'Unsupported actions for pool of type "iscsi": deleting volume, deleting pool', + "result": True, + "comment": 'Unsupported actions for pool of type "iscsi": deleting volume, deleting pool', } - with patch.dict(virt.__opts__, {'test': False}): - self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True)) + with patch.dict(virt.__opts__, {"test": False}): + self.assertDictEqual(expected, virt.pool_deleted("test01", purge=True)) - with patch.dict(virt.__opts__, {'test': True}): - expected['result'] = None - self.assertDictEqual(expected, virt.pool_deleted('test01', purge=True)) + with patch.dict(virt.__opts__, {"test": True}): + expected["result"] = None + self.assertDictEqual(expected, virt.pool_deleted("test01", purge=True)) diff --git a/tests/unit/states/test_virtualenv_mod.py b/tests/unit/states/test_virtualenv_mod.py index 20472714f3e..38a8cfd4930 100644 --- a/tests/unit/states/test_virtualenv_mod.py +++ b/tests/unit/states/test_virtualenv_mod.py @@ -1,84 +1,98 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.states.virtualenv_mod as virtualenv_mod +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class VirtualenvModTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the virtualenv_mod state - ''' + """ + def setup_loader_modules(self): - return {virtualenv_mod: {'__env__': 'base'}} + return {virtualenv_mod: {"__env__": "base"}} def test_managed(self): - ''' + """ Test to create a virtualenv and optionally manage it with pip - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} - ret.update({'comment': 'Virtualenv was not detected on this system'}) - self.assertDictEqual(virtualenv_mod.managed('salt'), ret) + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} + ret.update({"comment": "Virtualenv was not detected on this system"}) + self.assertDictEqual(virtualenv_mod.managed("salt"), ret) - mock1 = MagicMock(return_value='True') + mock1 = MagicMock(return_value="True") mock = MagicMock(return_value=False) - mock2 = MagicMock(return_value='1.1') - with patch.dict(virtualenv_mod.__salt__, {"virtualenv.create": True, - "cp.is_cached": mock, - "cp.cache_file": mock, - "cp.hash_file": mock, - "pip.freeze": mock1, - "cmd.run_stderr": mock1, - "pip.version": mock2}): + mock2 = MagicMock(return_value="1.1") + with patch.dict( + virtualenv_mod.__salt__, + { + "virtualenv.create": True, + "cp.is_cached": mock, + "cp.cache_file": mock, + "cp.hash_file": mock, + "pip.freeze": mock1, + "cmd.run_stderr": mock1, + "pip.version": mock2, + }, + ): mock = MagicMock(side_effect=[True, True, True, False, True, True]) - with patch.object(os.path, 'exists', mock): - ret.update({'comment': "pip requirements file" - " 'salt://a' not found"}) - self.assertDictEqual(virtualenv_mod.managed('salt', None, - 'salt://a'), ret) + with patch.object(os.path, "exists", mock): + ret.update({"comment": "pip requirements file" " 'salt://a' not found"}) + self.assertDictEqual( + virtualenv_mod.managed("salt", None, "salt://a"), ret + ) with patch.dict(virtualenv_mod.__opts__, {"test": True}): - ret.update({'changes': {'cleared_packages': 'True', - 'old': 'True'}, - 'comment': 'Virtualenv salt is set to' - ' be cleared', 'result': None}) - self.assertDictEqual(virtualenv_mod.managed('salt', - clear=1), ret) - ret.update({'comment': 'Virtualenv salt is already' - ' created', 'changes': {}, - 'result': True}) - self.assertDictEqual(virtualenv_mod.managed('salt'), ret) + ret.update( + { + "changes": {"cleared_packages": "True", "old": "True"}, + "comment": "Virtualenv salt is set to" " be cleared", + "result": None, + } + ) + self.assertDictEqual(virtualenv_mod.managed("salt", clear=1), ret) + ret.update( + { + "comment": "Virtualenv salt is already" " created", + "changes": {}, + "result": True, + } + ) + self.assertDictEqual(virtualenv_mod.managed("salt"), ret) - ret.update({'comment': 'Virtualenv salt is set to' - ' be created', 'result': None}) - self.assertDictEqual(virtualenv_mod.managed('salt'), ret) + ret.update( + { + "comment": "Virtualenv salt is set to" " be created", + "result": None, + } + ) + self.assertDictEqual(virtualenv_mod.managed("salt"), ret) with patch.dict(virtualenv_mod.__opts__, {"test": False}): - ret.update({'comment': "The 'use_wheel' option is" - " only supported in pip between 1.4 and 9.0.3." - " The version of pip detected was 1.1.", - 'result': False}) - self.assertDictEqual(virtualenv_mod.managed('salt', - use_wheel=1), - ret) + ret.update( + { + "comment": "The 'use_wheel' option is" + " only supported in pip between 1.4 and 9.0.3." + " The version of pip detected was 1.1.", + "result": False, + } + ) + self.assertDictEqual( + virtualenv_mod.managed("salt", use_wheel=1), ret + ) - ret.update({'comment': 'virtualenv exists', - 'result': True}) - self.assertDictEqual(virtualenv_mod.managed('salt'), ret) + ret.update({"comment": "virtualenv exists", "result": True}) + self.assertDictEqual(virtualenv_mod.managed("salt"), ret) diff --git a/tests/unit/states/test_webutil.py b/tests/unit/states/test_webutil.py index 1b0d1ccca80..228f3d7ef03 100644 --- a/tests/unit/states/test_webutil.py +++ b/tests/unit/states/test_webutil.py @@ -1,82 +1,81 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Alexander Pyatkin <asp@thexyz.net> -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - # Import Salt Libs import salt.states.webutil as htpasswd +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class HtpasswdTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.webutil - ''' + """ + def setup_loader_modules(self): - return {htpasswd: {'__opts__': {'test': False}}} + return {htpasswd: {"__opts__": {"test": False}}} def test_user_exists_already(self): - ''' + """ Test if it returns True when user already exists in htpasswd file - ''' + """ - mock = MagicMock(return_value={'retcode': 0}) + mock = MagicMock(return_value={"retcode": 0}) - with patch.dict(htpasswd.__salt__, {'file.grep': mock}): - ret = htpasswd.user_exists('larry', 'badpass', - '/etc/httpd/htpasswd') - expected = {'name': 'larry', - 'result': True, - 'comment': 'User already known', - 'changes': {}} + with patch.dict(htpasswd.__salt__, {"file.grep": mock}): + ret = htpasswd.user_exists("larry", "badpass", "/etc/httpd/htpasswd") + expected = { + "name": "larry", + "result": True, + "comment": "User already known", + "changes": {}, + } self.assertEqual(ret, expected) def test_new_user_success(self): - ''' + """ Test if it returns True when new user is added to htpasswd file - ''' + """ - mock_grep = MagicMock(return_value={'retcode': 1}) - mock_useradd = MagicMock(return_value={'retcode': 0, - 'stderr': 'Success'}) + mock_grep = MagicMock(return_value={"retcode": 1}) + mock_useradd = MagicMock(return_value={"retcode": 0, "stderr": "Success"}) - with patch.dict(htpasswd.__salt__, - {'file.grep': mock_grep, - 'webutil.useradd': mock_useradd}): - ret = htpasswd.user_exists('larry', 'badpass', - '/etc/httpd/htpasswd') - expected = {'name': 'larry', - 'result': True, - 'comment': 'Success', - 'changes': {'larry': True}} + with patch.dict( + htpasswd.__salt__, {"file.grep": mock_grep, "webutil.useradd": mock_useradd} + ): + ret = htpasswd.user_exists("larry", "badpass", "/etc/httpd/htpasswd") + expected = { + "name": "larry", + "result": True, + "comment": "Success", + "changes": {"larry": True}, + } self.assertEqual(ret, expected) def test_new_user_error(self): - ''' + """ Test if it returns False when adding user to htpasswd failed - ''' + """ - mock_grep = MagicMock(return_value={'retcode': 1}) - mock_useradd = MagicMock(return_value={'retcode': 1, - 'stderr': 'Error'}) + mock_grep = MagicMock(return_value={"retcode": 1}) + mock_useradd = MagicMock(return_value={"retcode": 1, "stderr": "Error"}) - with patch.dict(htpasswd.__salt__, - {'file.grep': mock_grep, - 'webutil.useradd': mock_useradd}): - ret = htpasswd.user_exists('larry', 'badpass', - '/etc/httpd/htpasswd') - expected = {'name': 'larry', - 'result': False, - 'comment': 'Error', - 'changes': {}} + with patch.dict( + htpasswd.__salt__, {"file.grep": mock_grep, "webutil.useradd": mock_useradd} + ): + ret = htpasswd.user_exists("larry", "badpass", "/etc/httpd/htpasswd") + expected = { + "name": "larry", + "result": False, + "comment": "Error", + "changes": {}, + } self.assertEqual(ret, expected) diff --git a/tests/unit/states/test_win_certutil.py b/tests/unit/states/test_win_certutil.py index 9fd04cdd6da..e10a354f59c 100644 --- a/tests/unit/states/test_win_certutil.py +++ b/tests/unit/states/test_win_certutil.py @@ -1,229 +1,265 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_certutil as certutil # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class CertUtilTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {certutil: {}} def test_add_serial(self): - ''' + """ Test adding a certificate to specified certificate store - ''' + """ expected = { - 'changes': {'added': '/path/to/cert.cer'}, - 'comment': '', - 'name': '/path/to/cert.cer', - 'result': True + "changes": {"added": "/path/to/cert.cer"}, + "comment": "", + "name": "/path/to/cert.cer", + "result": True, } - cache_mock = MagicMock(return_value='/tmp/cert.cer') - get_cert_serial_mock = MagicMock(return_value='ABCDEF') - get_store_serials_mock = MagicMock(return_value=['123456']) - add_mock = MagicMock(return_value='Added successfully') - with patch.dict(certutil.__salt__, {'cp.cache_file': cache_mock, - 'certutil.get_cert_serial': get_cert_serial_mock, - 'certutil.get_stored_cert_serials': get_store_serials_mock, - 'certutil.add_store': add_mock}): - out = certutil.add_store('/path/to/cert.cer', 'TrustedPublisher') - cache_mock.assert_called_once_with('/path/to/cert.cer', 'base') - get_cert_serial_mock.assert_called_once_with('/tmp/cert.cer') - get_store_serials_mock.assert_called_once_with('TrustedPublisher') - add_mock.assert_called_once_with('/path/to/cert.cer', 'TrustedPublisher') + cache_mock = MagicMock(return_value="/tmp/cert.cer") + get_cert_serial_mock = MagicMock(return_value="ABCDEF") + get_store_serials_mock = MagicMock(return_value=["123456"]) + add_mock = MagicMock(return_value="Added successfully") + with patch.dict( + certutil.__salt__, + { + "cp.cache_file": cache_mock, + "certutil.get_cert_serial": get_cert_serial_mock, + "certutil.get_stored_cert_serials": get_store_serials_mock, + "certutil.add_store": add_mock, + }, + ): + out = certutil.add_store("/path/to/cert.cer", "TrustedPublisher") + cache_mock.assert_called_once_with("/path/to/cert.cer", "base") + get_cert_serial_mock.assert_called_once_with("/tmp/cert.cer") + get_store_serials_mock.assert_called_once_with("TrustedPublisher") + add_mock.assert_called_once_with("/path/to/cert.cer", "TrustedPublisher") self.assertEqual(expected, out) def test_add_serial_missing(self): - ''' + """ Test adding a certificate to specified certificate store when the file doesn't exist - ''' + """ expected = { - 'changes': {}, - 'comment': 'Certificate file not found.', - 'name': '/path/to/cert.cer', - 'result': False + "changes": {}, + "comment": "Certificate file not found.", + "name": "/path/to/cert.cer", + "result": False, } cache_mock = MagicMock(return_value=False) - get_cert_serial_mock = MagicMock(return_value='ABCDEF') - get_store_serials_mock = MagicMock(return_value=['123456']) - add_mock = MagicMock(return_value='Added successfully') - with patch.dict(certutil.__salt__, {'cp.cache_file': cache_mock, - 'certutil.get_cert_serial': get_cert_serial_mock, - 'certutil.get_stored_cert_serials': get_store_serials_mock, - 'certutil.add_store': add_mock}): - out = certutil.add_store('/path/to/cert.cer', 'TrustedPublisher') - cache_mock.assert_called_once_with('/path/to/cert.cer', 'base') + get_cert_serial_mock = MagicMock(return_value="ABCDEF") + get_store_serials_mock = MagicMock(return_value=["123456"]) + add_mock = MagicMock(return_value="Added successfully") + with patch.dict( + certutil.__salt__, + { + "cp.cache_file": cache_mock, + "certutil.get_cert_serial": get_cert_serial_mock, + "certutil.get_stored_cert_serials": get_store_serials_mock, + "certutil.add_store": add_mock, + }, + ): + out = certutil.add_store("/path/to/cert.cer", "TrustedPublisher") + cache_mock.assert_called_once_with("/path/to/cert.cer", "base") assert not get_cert_serial_mock.called assert not get_store_serials_mock.called assert not add_mock.called self.assertEqual(expected, out) def test_add_serial_exists(self): - ''' + """ Test adding a certificate to specified certificate store when the cert already exists - ''' + """ expected = { - 'changes': {}, - 'comment': '/path/to/cert.cer already stored.', - 'name': '/path/to/cert.cer', - 'result': True + "changes": {}, + "comment": "/path/to/cert.cer already stored.", + "name": "/path/to/cert.cer", + "result": True, } - cache_mock = MagicMock(return_value='/tmp/cert.cer') - get_cert_serial_mock = MagicMock(return_value='ABCDEF') - get_store_serials_mock = MagicMock(return_value=['123456', 'ABCDEF']) - add_mock = MagicMock(return_value='Added successfully') - with patch.dict(certutil.__salt__, {'cp.cache_file': cache_mock, - 'certutil.get_cert_serial': get_cert_serial_mock, - 'certutil.get_stored_cert_serials': get_store_serials_mock, - 'certutil.add_store': add_mock}): - out = certutil.add_store('/path/to/cert.cer', 'TrustedPublisher') - cache_mock.assert_called_once_with('/path/to/cert.cer', 'base') - get_cert_serial_mock.assert_called_once_with('/tmp/cert.cer') - get_store_serials_mock.assert_called_once_with('TrustedPublisher') + cache_mock = MagicMock(return_value="/tmp/cert.cer") + get_cert_serial_mock = MagicMock(return_value="ABCDEF") + get_store_serials_mock = MagicMock(return_value=["123456", "ABCDEF"]) + add_mock = MagicMock(return_value="Added successfully") + with patch.dict( + certutil.__salt__, + { + "cp.cache_file": cache_mock, + "certutil.get_cert_serial": get_cert_serial_mock, + "certutil.get_stored_cert_serials": get_store_serials_mock, + "certutil.add_store": add_mock, + }, + ): + out = certutil.add_store("/path/to/cert.cer", "TrustedPublisher") + cache_mock.assert_called_once_with("/path/to/cert.cer", "base") + get_cert_serial_mock.assert_called_once_with("/tmp/cert.cer") + get_store_serials_mock.assert_called_once_with("TrustedPublisher") assert not add_mock.called self.assertEqual(expected, out) def test_add_serial_fail(self): - ''' + """ Test adding a certificate when the add fails - ''' + """ expected = { - 'changes': {}, - 'comment': 'Failed to store certificate /path/to/cert.cer', - 'name': '/path/to/cert.cer', - 'result': False + "changes": {}, + "comment": "Failed to store certificate /path/to/cert.cer", + "name": "/path/to/cert.cer", + "result": False, } - cache_mock = MagicMock(return_value='/tmp/cert.cer') - get_cert_serial_mock = MagicMock(return_value='ABCDEF') - get_store_serials_mock = MagicMock(return_value=['123456']) - add_mock = MagicMock(return_value='Failed') - with patch.dict(certutil.__salt__, {'cp.cache_file': cache_mock, - 'certutil.get_cert_serial': get_cert_serial_mock, - 'certutil.get_stored_cert_serials': get_store_serials_mock, - 'certutil.add_store': add_mock}): - out = certutil.add_store('/path/to/cert.cer', 'TrustedPublisher') - cache_mock.assert_called_once_with('/path/to/cert.cer', 'base') - get_cert_serial_mock.assert_called_once_with('/tmp/cert.cer') - get_store_serials_mock.assert_called_once_with('TrustedPublisher') - add_mock.assert_called_once_with('/path/to/cert.cer', 'TrustedPublisher') + cache_mock = MagicMock(return_value="/tmp/cert.cer") + get_cert_serial_mock = MagicMock(return_value="ABCDEF") + get_store_serials_mock = MagicMock(return_value=["123456"]) + add_mock = MagicMock(return_value="Failed") + with patch.dict( + certutil.__salt__, + { + "cp.cache_file": cache_mock, + "certutil.get_cert_serial": get_cert_serial_mock, + "certutil.get_stored_cert_serials": get_store_serials_mock, + "certutil.add_store": add_mock, + }, + ): + out = certutil.add_store("/path/to/cert.cer", "TrustedPublisher") + cache_mock.assert_called_once_with("/path/to/cert.cer", "base") + get_cert_serial_mock.assert_called_once_with("/tmp/cert.cer") + get_store_serials_mock.assert_called_once_with("TrustedPublisher") + add_mock.assert_called_once_with("/path/to/cert.cer", "TrustedPublisher") self.assertEqual(expected, out) def test_del_serial(self): - ''' + """ Test deleting a certificate from a specified certificate store - ''' + """ expected = { - 'changes': {'removed': '/path/to/cert.cer'}, - 'comment': '', - 'name': '/path/to/cert.cer', - 'result': True + "changes": {"removed": "/path/to/cert.cer"}, + "comment": "", + "name": "/path/to/cert.cer", + "result": True, } - cache_mock = MagicMock(return_value='/tmp/cert.cer') - get_cert_serial_mock = MagicMock(return_value='ABCDEF') - get_store_serials_mock = MagicMock(return_value=['123456', 'ABCDEF']) - del_mock = MagicMock(return_value='Removed successfully') - with patch.dict(certutil.__salt__, {'cp.cache_file': cache_mock, - 'certutil.get_cert_serial': get_cert_serial_mock, - 'certutil.get_stored_cert_serials': get_store_serials_mock, - 'certutil.del_store': del_mock}): - out = certutil.del_store('/path/to/cert.cer', 'TrustedPublisher') - cache_mock.assert_called_once_with('/path/to/cert.cer', 'base') - get_cert_serial_mock.assert_called_once_with('/tmp/cert.cer') - get_store_serials_mock.assert_called_once_with('TrustedPublisher') - del_mock.assert_called_once_with('/tmp/cert.cer', 'TrustedPublisher') + cache_mock = MagicMock(return_value="/tmp/cert.cer") + get_cert_serial_mock = MagicMock(return_value="ABCDEF") + get_store_serials_mock = MagicMock(return_value=["123456", "ABCDEF"]) + del_mock = MagicMock(return_value="Removed successfully") + with patch.dict( + certutil.__salt__, + { + "cp.cache_file": cache_mock, + "certutil.get_cert_serial": get_cert_serial_mock, + "certutil.get_stored_cert_serials": get_store_serials_mock, + "certutil.del_store": del_mock, + }, + ): + out = certutil.del_store("/path/to/cert.cer", "TrustedPublisher") + cache_mock.assert_called_once_with("/path/to/cert.cer", "base") + get_cert_serial_mock.assert_called_once_with("/tmp/cert.cer") + get_store_serials_mock.assert_called_once_with("TrustedPublisher") + del_mock.assert_called_once_with("/tmp/cert.cer", "TrustedPublisher") self.assertEqual(expected, out) def test_del_serial_missing(self): - ''' + """ Test deleting a certificate to specified certificate store when the file doesn't exist - ''' + """ expected = { - 'changes': {}, - 'comment': 'Certificate file not found.', - 'name': '/path/to/cert.cer', - 'result': False + "changes": {}, + "comment": "Certificate file not found.", + "name": "/path/to/cert.cer", + "result": False, } cache_mock = MagicMock(return_value=False) - get_cert_serial_mock = MagicMock(return_value='ABCDEF') - get_store_serials_mock = MagicMock(return_value=['123456']) - del_mock = MagicMock(return_value='Added successfully') - with patch.dict(certutil.__salt__, {'cp.cache_file': cache_mock, - 'certutil.get_cert_serial': get_cert_serial_mock, - 'certutil.get_stored_cert_serials': get_store_serials_mock, - 'certutil.del_store': del_mock}): - out = certutil.del_store('/path/to/cert.cer', 'TrustedPublisher') - cache_mock.assert_called_once_with('/path/to/cert.cer', 'base') + get_cert_serial_mock = MagicMock(return_value="ABCDEF") + get_store_serials_mock = MagicMock(return_value=["123456"]) + del_mock = MagicMock(return_value="Added successfully") + with patch.dict( + certutil.__salt__, + { + "cp.cache_file": cache_mock, + "certutil.get_cert_serial": get_cert_serial_mock, + "certutil.get_stored_cert_serials": get_store_serials_mock, + "certutil.del_store": del_mock, + }, + ): + out = certutil.del_store("/path/to/cert.cer", "TrustedPublisher") + cache_mock.assert_called_once_with("/path/to/cert.cer", "base") assert not get_cert_serial_mock.called assert not get_store_serials_mock.called assert not del_mock.called self.assertEqual(expected, out) def test_del_serial_doesnt_exists(self): - ''' + """ Test deleting a certificate to specified certificate store when the cert doesn't exists - ''' + """ expected = { - 'changes': {}, - 'comment': '/path/to/cert.cer already removed.', - 'name': '/path/to/cert.cer', - 'result': True + "changes": {}, + "comment": "/path/to/cert.cer already removed.", + "name": "/path/to/cert.cer", + "result": True, } - cache_mock = MagicMock(return_value='/tmp/cert.cer') - get_cert_serial_mock = MagicMock(return_value='ABCDEF') - get_store_serials_mock = MagicMock(return_value=['123456']) - del_mock = MagicMock(return_value='Added successfully') - with patch.dict(certutil.__salt__, {'cp.cache_file': cache_mock, - 'certutil.get_cert_serial': get_cert_serial_mock, - 'certutil.get_stored_cert_serials': get_store_serials_mock, - 'certutil.del_store': del_mock}): - out = certutil.del_store('/path/to/cert.cer', 'TrustedPublisher') - cache_mock.assert_called_once_with('/path/to/cert.cer', 'base') - get_cert_serial_mock.assert_called_once_with('/tmp/cert.cer') - get_store_serials_mock.assert_called_once_with('TrustedPublisher') + cache_mock = MagicMock(return_value="/tmp/cert.cer") + get_cert_serial_mock = MagicMock(return_value="ABCDEF") + get_store_serials_mock = MagicMock(return_value=["123456"]) + del_mock = MagicMock(return_value="Added successfully") + with patch.dict( + certutil.__salt__, + { + "cp.cache_file": cache_mock, + "certutil.get_cert_serial": get_cert_serial_mock, + "certutil.get_stored_cert_serials": get_store_serials_mock, + "certutil.del_store": del_mock, + }, + ): + out = certutil.del_store("/path/to/cert.cer", "TrustedPublisher") + cache_mock.assert_called_once_with("/path/to/cert.cer", "base") + get_cert_serial_mock.assert_called_once_with("/tmp/cert.cer") + get_store_serials_mock.assert_called_once_with("TrustedPublisher") assert not del_mock.called self.assertEqual(expected, out) def test_del_serial_fail(self): - ''' + """ Test deleting a certificate from the store when the delete fails - ''' + """ expected = { - 'changes': {}, - 'comment': 'Failed to remove the certificate /path/to/cert.cer', - 'name': '/path/to/cert.cer', - 'result': False + "changes": {}, + "comment": "Failed to remove the certificate /path/to/cert.cer", + "name": "/path/to/cert.cer", + "result": False, } - cache_mock = MagicMock(return_value='/tmp/cert.cer') - get_cert_serial_mock = MagicMock(return_value='ABCDEF') - get_store_serials_mock = MagicMock(return_value=['123456', 'ABCDEF']) - del_mock = MagicMock(return_value='Failed') - with patch.dict(certutil.__salt__, {'cp.cache_file': cache_mock, - 'certutil.get_cert_serial': get_cert_serial_mock, - 'certutil.get_stored_cert_serials': get_store_serials_mock, - 'certutil.del_store': del_mock}): - out = certutil.del_store('/path/to/cert.cer', 'TrustedPublisher') - cache_mock.assert_called_once_with('/path/to/cert.cer', 'base') - get_cert_serial_mock.assert_called_once_with('/tmp/cert.cer') - get_store_serials_mock.assert_called_once_with('TrustedPublisher') - del_mock.assert_called_once_with('/tmp/cert.cer', 'TrustedPublisher') + cache_mock = MagicMock(return_value="/tmp/cert.cer") + get_cert_serial_mock = MagicMock(return_value="ABCDEF") + get_store_serials_mock = MagicMock(return_value=["123456", "ABCDEF"]) + del_mock = MagicMock(return_value="Failed") + with patch.dict( + certutil.__salt__, + { + "cp.cache_file": cache_mock, + "certutil.get_cert_serial": get_cert_serial_mock, + "certutil.get_stored_cert_serials": get_store_serials_mock, + "certutil.del_store": del_mock, + }, + ): + out = certutil.del_store("/path/to/cert.cer", "TrustedPublisher") + cache_mock.assert_called_once_with("/path/to/cert.cer", "base") + get_cert_serial_mock.assert_called_once_with("/tmp/cert.cer") + get_store_serials_mock.assert_called_once_with("TrustedPublisher") + del_mock.assert_called_once_with("/tmp/cert.cer", "TrustedPublisher") self.assertEqual(expected, out) diff --git a/tests/unit/states/test_win_dism.py b/tests/unit/states/test_win_dism.py index 28fc3f0df62..492cbe3c886 100644 --- a/tests/unit/states/test_win_dism.py +++ b/tests/unit/states/test_win_dism.py @@ -1,520 +1,552 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_dism as dism # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class WinDismTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {dism: {}} def test_capability_installed(self): - ''' + """ Test capability installed state - ''' + """ expected = { - 'comment': "Installed Capa2", - 'changes': {'capability': {'new': ['Capa2']}, - 'retcode': 0}, - 'name': 'Capa2', - 'result': True} + "comment": "Installed Capa2", + "changes": {"capability": {"new": ["Capa2"]}, "retcode": 0}, + "name": "Capa2", + "result": True, + } - mock_installed = MagicMock( - side_effect=[['Capa1'], ['Capa1', 'Capa2']]) - mock_add = MagicMock( - return_value={'retcode': 0}) + mock_installed = MagicMock(side_effect=[["Capa1"], ["Capa1", "Capa2"]]) + mock_add = MagicMock(return_value={"retcode": 0}) with patch.dict( - dism.__salt__, {'dism.installed_capabilities': mock_installed, - 'dism.add_capability': mock_add}): - with patch.dict(dism.__opts__, {'test': False}): + dism.__salt__, + { + "dism.installed_capabilities": mock_installed, + "dism.add_capability": mock_add, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): - out = dism.capability_installed('Capa2', 'somewhere', True) + out = dism.capability_installed("Capa2", "somewhere", True) mock_installed.assert_called_with() mock_add.assert_called_once_with( - 'Capa2', 'somewhere', True, None, False) + "Capa2", "somewhere", True, None, False + ) self.assertEqual(out, expected) def test_capability_installed_failure(self): - ''' + """ Test installing a capability which fails with DISM - ''' + """ expected = { - 'comment': "Failed to install Capa2: Failed", - 'changes': {}, - 'name': 'Capa2', - 'result': False} + "comment": "Failed to install Capa2: Failed", + "changes": {}, + "name": "Capa2", + "result": False, + } - mock_installed = MagicMock( - side_effect=[['Capa1'], ['Capa1']]) - mock_add = MagicMock( - return_value={'retcode': 67, 'stdout': 'Failed'}) + mock_installed = MagicMock(side_effect=[["Capa1"], ["Capa1"]]) + mock_add = MagicMock(return_value={"retcode": 67, "stdout": "Failed"}) with patch.dict( - dism.__salt__, {'dism.installed_capabilities': mock_installed, - 'dism.add_capability': mock_add}): - with patch.dict(dism.__opts__, {'test': False}): + dism.__salt__, + { + "dism.installed_capabilities": mock_installed, + "dism.add_capability": mock_add, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): - out = dism.capability_installed('Capa2', 'somewhere', True) + out = dism.capability_installed("Capa2", "somewhere", True) mock_installed.assert_called_with() mock_add.assert_called_once_with( - 'Capa2', 'somewhere', True, None, False) + "Capa2", "somewhere", True, None, False + ) self.assertEqual(out, expected) def test_capability_installed_installed(self): - ''' + """ Test installing a capability already installed - ''' + """ expected = { - 'comment': "The capability Capa2 is already installed", - 'changes': {}, - 'name': 'Capa2', - 'result': True} + "comment": "The capability Capa2 is already installed", + "changes": {}, + "name": "Capa2", + "result": True, + } - mock_installed = MagicMock( - return_value=["Capa1", "Capa2"]) + mock_installed = MagicMock(return_value=["Capa1", "Capa2"]) mock_add = MagicMock() with patch.dict( - dism.__salt__, {'dism.installed_capabilities': mock_installed, - 'dism.add_capability': mock_add}): + dism.__salt__, + { + "dism.installed_capabilities": mock_installed, + "dism.add_capability": mock_add, + }, + ): - with patch.dict(dism.__opts__, {'test': False}): - out = dism.capability_installed('Capa2', 'somewhere', True) + with patch.dict(dism.__opts__, {"test": False}): + out = dism.capability_installed("Capa2", "somewhere", True) mock_installed.assert_called_once_with() assert not mock_add.called self.assertEqual(out, expected) def test_capability_removed(self): - ''' + """ Test capability removed state - ''' + """ expected = { - 'comment': "Removed Capa2", - 'changes': {'capability': {'old': ['Capa2']}, - 'retcode': 0}, - 'name': 'Capa2', - 'result': True} + "comment": "Removed Capa2", + "changes": {"capability": {"old": ["Capa2"]}, "retcode": 0}, + "name": "Capa2", + "result": True, + } - mock_removed = MagicMock( - side_effect=[['Capa1', 'Capa2'], ['Capa1']]) - mock_remove = MagicMock( - return_value={'retcode': 0}) + mock_removed = MagicMock(side_effect=[["Capa1", "Capa2"], ["Capa1"]]) + mock_remove = MagicMock(return_value={"retcode": 0}) with patch.dict( - dism.__salt__, {'dism.installed_capabilities': mock_removed, - 'dism.remove_capability': mock_remove}): - with patch.dict(dism.__opts__, {'test': False}): + dism.__salt__, + { + "dism.installed_capabilities": mock_removed, + "dism.remove_capability": mock_remove, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): - out = dism.capability_removed('Capa2') + out = dism.capability_removed("Capa2") mock_removed.assert_called_with() - mock_remove.assert_called_once_with('Capa2', None, False) + mock_remove.assert_called_once_with("Capa2", None, False) self.assertEqual(out, expected) def test_capability_removed_failure(self): - ''' + """ Test removing a capability which fails with DISM - ''' + """ expected = { - 'comment': "Failed to remove Capa2: Failed", - 'changes': {}, - 'name': 'Capa2', - 'result': False} + "comment": "Failed to remove Capa2: Failed", + "changes": {}, + "name": "Capa2", + "result": False, + } - mock_removed = MagicMock( - side_effect=[['Capa1', 'Capa2'], ['Capa1', 'Capa2']]) - mock_remove = MagicMock( - return_value={'retcode': 67, 'stdout': 'Failed'}) + mock_removed = MagicMock(side_effect=[["Capa1", "Capa2"], ["Capa1", "Capa2"]]) + mock_remove = MagicMock(return_value={"retcode": 67, "stdout": "Failed"}) with patch.dict( - dism.__salt__, {'dism.installed_capabilities': mock_removed, - 'dism.remove_capability': mock_remove}): - with patch.dict(dism.__opts__, {'test': False}): + dism.__salt__, + { + "dism.installed_capabilities": mock_removed, + "dism.remove_capability": mock_remove, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): - out = dism.capability_removed('Capa2') + out = dism.capability_removed("Capa2") mock_removed.assert_called_with() - mock_remove.assert_called_once_with( - 'Capa2', None, False) + mock_remove.assert_called_once_with("Capa2", None, False) self.assertEqual(out, expected) def test_capability_removed_removed(self): - ''' + """ Test removing a capability already removed - ''' + """ expected = { - 'comment': "The capability Capa2 is already removed", - 'changes': {}, - 'name': 'Capa2', - 'result': True} + "comment": "The capability Capa2 is already removed", + "changes": {}, + "name": "Capa2", + "result": True, + } - mock_removed = MagicMock( - return_value=["Capa1"]) + mock_removed = MagicMock(return_value=["Capa1"]) mock_remove = MagicMock() with patch.dict( - dism.__salt__, {'dism.installed_capabilities': mock_removed, - 'dism.add_capability': mock_remove}): + dism.__salt__, + { + "dism.installed_capabilities": mock_removed, + "dism.add_capability": mock_remove, + }, + ): - out = dism.capability_removed('Capa2', 'somewhere', True) + out = dism.capability_removed("Capa2", "somewhere", True) mock_removed.assert_called_once_with() assert not mock_remove.called self.assertEqual(out, expected) def test_feature_installed(self): - ''' + """ Test installing a feature with DISM - ''' + """ expected = { - 'comment': "Installed Feat2", - 'changes': {'feature': {'new': ['Feat2']}, - 'retcode': 0}, - 'name': 'Feat2', - 'result': True} + "comment": "Installed Feat2", + "changes": {"feature": {"new": ["Feat2"]}, "retcode": 0}, + "name": "Feat2", + "result": True, + } - mock_installed = MagicMock( - side_effect=[['Feat1'], ['Feat1', 'Feat2']]) - mock_add = MagicMock( - return_value={'retcode': 0}) + mock_installed = MagicMock(side_effect=[["Feat1"], ["Feat1", "Feat2"]]) + mock_add = MagicMock(return_value={"retcode": 0}) with patch.dict( - dism.__salt__, {'dism.installed_features': mock_installed, - 'dism.add_feature': mock_add}): - with patch.dict(dism.__opts__, {'test': False}): + dism.__salt__, + {"dism.installed_features": mock_installed, "dism.add_feature": mock_add}, + ): + with patch.dict(dism.__opts__, {"test": False}): - out = dism.feature_installed('Feat2') + out = dism.feature_installed("Feat2") mock_installed.assert_called_with() mock_add.assert_called_once_with( - 'Feat2', None, None, False, False, None, False) + "Feat2", None, None, False, False, None, False + ) self.assertEqual(out, expected) def test_feature_installed_failure(self): - ''' + """ Test installing a feature which fails with DISM - ''' + """ expected = { - 'comment': "Failed to install Feat2: Failed", - 'changes': {}, - 'name': 'Feat2', - 'result': False} + "comment": "Failed to install Feat2: Failed", + "changes": {}, + "name": "Feat2", + "result": False, + } - mock_installed = MagicMock( - side_effect=[['Feat1'], ['Feat1']]) - mock_add = MagicMock( - return_value={'retcode': 67, 'stdout': 'Failed'}) + mock_installed = MagicMock(side_effect=[["Feat1"], ["Feat1"]]) + mock_add = MagicMock(return_value={"retcode": 67, "stdout": "Failed"}) with patch.dict( - dism.__salt__, {'dism.installed_features': mock_installed, - 'dism.add_feature': mock_add}): - with patch.dict(dism.__opts__, {'test': False}): + dism.__salt__, + {"dism.installed_features": mock_installed, "dism.add_feature": mock_add}, + ): + with patch.dict(dism.__opts__, {"test": False}): - out = dism.feature_installed('Feat2') + out = dism.feature_installed("Feat2") mock_installed.assert_called_with() mock_add.assert_called_once_with( - 'Feat2', None, None, False, False, None, False) + "Feat2", None, None, False, False, None, False + ) self.assertEqual(out, expected) def test_feature_installed_installed(self): - ''' + """ Test installing a feature already installed - ''' + """ expected = { - 'comment': "The feature Feat1 is already installed", - 'changes': {}, - 'name': 'Feat1', - 'result': True} + "comment": "The feature Feat1 is already installed", + "changes": {}, + "name": "Feat1", + "result": True, + } - mock_installed = MagicMock( - side_effect=[['Feat1', 'Feat2'], ['Feat1', 'Feat2']]) + mock_installed = MagicMock(side_effect=[["Feat1", "Feat2"], ["Feat1", "Feat2"]]) mock_add = MagicMock() with patch.dict( - dism.__salt__, {'dism.installed_features': mock_installed, - 'dism.add_feature': mock_add}): + dism.__salt__, + {"dism.installed_features": mock_installed, "dism.add_feature": mock_add}, + ): - out = dism.feature_installed('Feat1') + out = dism.feature_installed("Feat1") mock_installed.assert_called_once_with() assert not mock_add.called self.assertEqual(out, expected) def test_feature_removed(self): - ''' + """ Test removing a feature with DISM - ''' + """ expected = { - 'comment': "Removed Feat2", - 'changes': {'feature': {'old': ['Feat2']}, - 'retcode': 0}, - 'name': 'Feat2', - 'result': True} + "comment": "Removed Feat2", + "changes": {"feature": {"old": ["Feat2"]}, "retcode": 0}, + "name": "Feat2", + "result": True, + } - mock_removed = MagicMock( - side_effect=[['Feat1', 'Feat2'], ['Feat1']]) - mock_remove = MagicMock( - return_value={'retcode': 0}) + mock_removed = MagicMock(side_effect=[["Feat1", "Feat2"], ["Feat1"]]) + mock_remove = MagicMock(return_value={"retcode": 0}) with patch.dict( - dism.__salt__, {'dism.installed_features': mock_removed, - 'dism.remove_feature': mock_remove}): - with patch.dict(dism.__opts__, {'test': False}): + dism.__salt__, + { + "dism.installed_features": mock_removed, + "dism.remove_feature": mock_remove, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): - out = dism.feature_removed('Feat2') + out = dism.feature_removed("Feat2") mock_removed.assert_called_with() - mock_remove.assert_called_once_with( - 'Feat2', False, None, False) + mock_remove.assert_called_once_with("Feat2", False, None, False) self.assertEqual(out, expected) def test_feature_removed_failure(self): - ''' + """ Test removing a feature which fails with DISM - ''' + """ expected = { - 'comment': "Failed to remove Feat2: Failed", - 'changes': {}, - 'name': 'Feat2', - 'result': False} + "comment": "Failed to remove Feat2: Failed", + "changes": {}, + "name": "Feat2", + "result": False, + } - mock_removed = MagicMock( - side_effect=[['Feat1', 'Feat2'], ['Feat1', 'Feat2']]) - mock_remove = MagicMock( - return_value={'retcode': 67, 'stdout': 'Failed'}) + mock_removed = MagicMock(side_effect=[["Feat1", "Feat2"], ["Feat1", "Feat2"]]) + mock_remove = MagicMock(return_value={"retcode": 67, "stdout": "Failed"}) with patch.dict( - dism.__salt__, {'dism.installed_features': mock_removed, - 'dism.remove_feature': mock_remove}): - with patch.dict(dism.__opts__, {'test': False}): + dism.__salt__, + { + "dism.installed_features": mock_removed, + "dism.remove_feature": mock_remove, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): - out = dism.feature_removed('Feat2') + out = dism.feature_removed("Feat2") mock_removed.assert_called_with() - mock_remove.assert_called_once_with( - 'Feat2', False, None, False) + mock_remove.assert_called_once_with("Feat2", False, None, False) self.assertEqual(out, expected) def test_feature_removed_removed(self): - ''' + """ Test removing a feature already removed - ''' + """ expected = { - 'comment': "The feature Feat2 is already removed", - 'changes': {}, - 'name': 'Feat2', - 'result': True} + "comment": "The feature Feat2 is already removed", + "changes": {}, + "name": "Feat2", + "result": True, + } - mock_removed = MagicMock( - side_effect=[['Feat1'], ['Feat1']]) + mock_removed = MagicMock(side_effect=[["Feat1"], ["Feat1"]]) mock_remove = MagicMock() with patch.dict( - dism.__salt__, {'dism.installed_features': mock_removed, - 'dism.remove_feature': mock_remove}): + dism.__salt__, + { + "dism.installed_features": mock_removed, + "dism.remove_feature": mock_remove, + }, + ): - out = dism.feature_removed('Feat2') + out = dism.feature_removed("Feat2") mock_removed.assert_called_once_with() assert not mock_remove.called self.assertEqual(out, expected) def test_package_installed(self): - ''' + """ Test installing a package with DISM - ''' + """ expected = { - 'comment': "Installed Pack2", - 'changes': {'package': {'new': ['Pack2']}, - 'retcode': 0}, - 'name': 'Pack2', - 'result': True} + "comment": "Installed Pack2", + "changes": {"package": {"new": ["Pack2"]}, "retcode": 0}, + "name": "Pack2", + "result": True, + } - mock_installed = MagicMock( - side_effect=[['Pack1'], ['Pack1', 'Pack2']]) - mock_add = MagicMock( - return_value={'retcode': 0}) - mock_info = MagicMock( - return_value={'Package Identity': 'Pack2'}) + mock_installed = MagicMock(side_effect=[["Pack1"], ["Pack1", "Pack2"]]) + mock_add = MagicMock(return_value={"retcode": 0}) + mock_info = MagicMock(return_value={"Package Identity": "Pack2"}) with patch.dict( - dism.__salt__, {'dism.installed_packages': mock_installed, - 'dism.add_package': mock_add, - 'dism.package_info': mock_info}): - with patch.dict(dism.__opts__, {'test': False}): - with patch('os.path.exists'): + dism.__salt__, + { + "dism.installed_packages": mock_installed, + "dism.add_package": mock_add, + "dism.package_info": mock_info, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): + with patch("os.path.exists"): - out = dism.package_installed('Pack2') + out = dism.package_installed("Pack2") mock_installed.assert_called_with() - mock_add.assert_called_once_with( - 'Pack2', False, False, None, False) + mock_add.assert_called_once_with("Pack2", False, False, None, False) self.assertEqual(out, expected) def test_package_installed_failure(self): - ''' + """ Test installing a package which fails with DISM - ''' + """ expected = { - 'comment': "Failed to install Pack2: Failed", - 'changes': {}, - 'name': 'Pack2', - 'result': False} + "comment": "Failed to install Pack2: Failed", + "changes": {}, + "name": "Pack2", + "result": False, + } - mock_installed = MagicMock( - side_effect=[['Pack1'], ['Pack1']]) - mock_add = MagicMock( - return_value={'retcode': 67, 'stdout': 'Failed'}) - mock_info = MagicMock( - return_value={'Package Identity': 'Pack2'}) + mock_installed = MagicMock(side_effect=[["Pack1"], ["Pack1"]]) + mock_add = MagicMock(return_value={"retcode": 67, "stdout": "Failed"}) + mock_info = MagicMock(return_value={"Package Identity": "Pack2"}) with patch.dict( - dism.__salt__, {'dism.installed_packages': mock_installed, - 'dism.add_package': mock_add, - 'dism.package_info': mock_info}): - with patch.dict(dism.__opts__, {'test': False}): - with patch('os.path.exists'): + dism.__salt__, + { + "dism.installed_packages": mock_installed, + "dism.add_package": mock_add, + "dism.package_info": mock_info, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): + with patch("os.path.exists"): - out = dism.package_installed('Pack2') + out = dism.package_installed("Pack2") mock_installed.assert_called_with() - mock_add.assert_called_once_with( - 'Pack2', False, False, None, False) + mock_add.assert_called_once_with("Pack2", False, False, None, False) self.assertEqual(out, expected) def test_package_installed_installed(self): - ''' + """ Test installing a package already installed - ''' + """ expected = { - 'comment': "The package Pack2 is already installed: Pack2", - 'changes': {}, - 'name': 'Pack2', - 'result': True} + "comment": "The package Pack2 is already installed: Pack2", + "changes": {}, + "name": "Pack2", + "result": True, + } - mock_installed = MagicMock( - side_effect=[['Pack1', 'Pack2'], ['Pack1', 'Pack2']]) + mock_installed = MagicMock(side_effect=[["Pack1", "Pack2"], ["Pack1", "Pack2"]]) mock_add = MagicMock() - mock_info = MagicMock( - return_value={'Package Identity': 'Pack2'}) + mock_info = MagicMock(return_value={"Package Identity": "Pack2"}) with patch.dict( - dism.__salt__, {'dism.installed_packages': mock_installed, - 'dism.add_package': mock_add, - 'dism.package_info': mock_info}): - with patch.dict(dism.__opts__, {'test': False}): - with patch('os.path.exists'): + dism.__salt__, + { + "dism.installed_packages": mock_installed, + "dism.add_package": mock_add, + "dism.package_info": mock_info, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): + with patch("os.path.exists"): - out = dism.package_installed('Pack2') + out = dism.package_installed("Pack2") mock_installed.assert_called_once_with() assert not mock_add.called self.assertEqual(out, expected) def test_package_removed(self): - ''' + """ Test removing a package with DISM - ''' + """ expected = { - 'comment': "Removed Pack2", - 'changes': {'package': {'old': ['Pack2']}, - 'retcode': 0}, - 'name': 'Pack2', - 'result': True} + "comment": "Removed Pack2", + "changes": {"package": {"old": ["Pack2"]}, "retcode": 0}, + "name": "Pack2", + "result": True, + } - mock_removed = MagicMock( - side_effect=[['Pack1', 'Pack2'], ['Pack1']]) - mock_remove = MagicMock( - return_value={'retcode': 0}) - mock_info = MagicMock( - return_value={'Package Identity': 'Pack2'}) + mock_removed = MagicMock(side_effect=[["Pack1", "Pack2"], ["Pack1"]]) + mock_remove = MagicMock(return_value={"retcode": 0}) + mock_info = MagicMock(return_value={"Package Identity": "Pack2"}) with patch.dict( - dism.__salt__, {'dism.installed_packages': mock_removed, - 'dism.remove_package': mock_remove, - 'dism.package_info': mock_info}): - with patch.dict(dism.__opts__, {'test': False}): - with patch('os.path.exists'): + dism.__salt__, + { + "dism.installed_packages": mock_removed, + "dism.remove_package": mock_remove, + "dism.package_info": mock_info, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): + with patch("os.path.exists"): - out = dism.package_removed('Pack2') + out = dism.package_removed("Pack2") mock_removed.assert_called_with() - mock_remove.assert_called_once_with( - 'Pack2', None, False) + mock_remove.assert_called_once_with("Pack2", None, False) self.assertEqual(out, expected) def test_package_removed_failure(self): - ''' + """ Test removing a package which fails with DISM - ''' + """ expected = { - 'comment': "Failed to remove Pack2: Failed", - 'changes': {}, - 'name': 'Pack2', - 'result': False} + "comment": "Failed to remove Pack2: Failed", + "changes": {}, + "name": "Pack2", + "result": False, + } - mock_removed = MagicMock( - side_effect=[['Pack1', 'Pack2'], ['Pack1', 'Pack2']]) - mock_remove = MagicMock( - return_value={'retcode': 67, 'stdout': 'Failed'}) - mock_info = MagicMock( - return_value={'Package Identity': 'Pack2'}) + mock_removed = MagicMock(side_effect=[["Pack1", "Pack2"], ["Pack1", "Pack2"]]) + mock_remove = MagicMock(return_value={"retcode": 67, "stdout": "Failed"}) + mock_info = MagicMock(return_value={"Package Identity": "Pack2"}) with patch.dict( - dism.__salt__, {'dism.installed_packages': mock_removed, - 'dism.remove_package': mock_remove, - 'dism.package_info': mock_info}): - with patch.dict(dism.__opts__, {'test': False}): - with patch('os.path.exists'): + dism.__salt__, + { + "dism.installed_packages": mock_removed, + "dism.remove_package": mock_remove, + "dism.package_info": mock_info, + }, + ): + with patch.dict(dism.__opts__, {"test": False}): + with patch("os.path.exists"): - out = dism.package_removed('Pack2') + out = dism.package_removed("Pack2") mock_removed.assert_called_with() - mock_remove.assert_called_once_with( - 'Pack2', None, False) + mock_remove.assert_called_once_with("Pack2", None, False) self.assertEqual(out, expected) def test_package_removed_removed(self): - ''' + """ Test removing a package already removed - ''' + """ expected = { - 'comment': "The package Pack2 is already removed", - 'changes': {}, - 'name': 'Pack2', - 'result': True} + "comment": "The package Pack2 is already removed", + "changes": {}, + "name": "Pack2", + "result": True, + } - mock_removed = MagicMock( - side_effect=[['Pack1'], ['Pack1']]) + mock_removed = MagicMock(side_effect=[["Pack1"], ["Pack1"]]) mock_remove = MagicMock() - mock_info = MagicMock( - return_value={'Package Identity': 'Pack2'}) + mock_info = MagicMock(return_value={"Package Identity": "Pack2"}) with patch.dict( - dism.__salt__, {'dism.installed_packages': mock_removed, - 'dism.remove_package': mock_remove, - 'dism.package_info': mock_info}): + dism.__salt__, + { + "dism.installed_packages": mock_removed, + "dism.remove_package": mock_remove, + "dism.package_info": mock_info, + }, + ): - with patch.dict(dism.__opts__, {'test': False}): - with patch('os.path.exists'): - out = dism.package_removed('Pack2') + with patch.dict(dism.__opts__, {"test": False}): + with patch("os.path.exists"): + out = dism.package_removed("Pack2") mock_removed.assert_called_once_with() assert not mock_remove.called diff --git a/tests/unit/states/test_win_dns_client.py b/tests/unit/states/test_win_dns_client.py index 20d22cd30f1..122f492b19a 100644 --- a/tests/unit/states/test_win_dns_client.py +++ b/tests/unit/states/test_win_dns_client.py @@ -1,131 +1,159 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_dns_client as win_dns_client +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class WinDnsClientTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the win_dns_client state - ''' + """ + def setup_loader_modules(self): return {win_dns_client: {}} def test_dns_exists(self): - ''' + """ Test to configure the DNS server list in the specified interface - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} with patch.dict(win_dns_client.__opts__, {"test": False}): - ret.update({'changes': {'Servers Added': [], - 'Servers Removed': [], - 'Servers Reordered': []}, - 'comment': 'servers entry is not a list !'}) - self.assertDictEqual(win_dns_client.dns_exists('salt'), ret) + ret.update( + { + "changes": { + "Servers Added": [], + "Servers Removed": [], + "Servers Reordered": [], + }, + "comment": "servers entry is not a list !", + } + ) + self.assertDictEqual(win_dns_client.dns_exists("salt"), ret) - mock = MagicMock(return_value=[2, 'salt']) - with patch.dict(win_dns_client.__salt__, - {'win_dns_client.get_dns_servers': mock}): - ret.update({'changes': {}, 'comment': repr([2, 'salt']) + " are already" - " configured", 'result': True}) - self.assertDictEqual(win_dns_client.dns_exists('salt', - [2, 'salt']), - ret) + mock = MagicMock(return_value=[2, "salt"]) + with patch.dict( + win_dns_client.__salt__, {"win_dns_client.get_dns_servers": mock} + ): + ret.update( + { + "changes": {}, + "comment": repr([2, "salt"]) + " are already" " configured", + "result": True, + } + ) + self.assertDictEqual( + win_dns_client.dns_exists("salt", [2, "salt"]), ret + ) mock = MagicMock(side_effect=[False, True, True]) - with patch.dict(win_dns_client.__salt__, - {'win_dns_client.add_dns': mock}): - ret.update({'comment': 'Failed to add 1 as DNS' - ' server number 1', 'result': False}) - self.assertDictEqual(win_dns_client.dns_exists('salt', - [1, 'salt'] - ), ret) + with patch.dict( + win_dns_client.__salt__, {"win_dns_client.add_dns": mock} + ): + ret.update( + { + "comment": "Failed to add 1 as DNS" " server number 1", + "result": False, + } + ) + self.assertDictEqual( + win_dns_client.dns_exists("salt", [1, "salt"]), ret + ) mock = MagicMock(return_value=False) - with patch.dict(win_dns_client.__salt__, - {'win_dns_client.rm_dns': mock}): - ret.update({'changes': {'Servers Added': ['a'], - 'Servers Removed': [], - 'Servers Reordered': []}, - 'comment': 'Failed to remove 2 from DNS' - ' server list'}) - self.assertDictEqual(win_dns_client.dns_exists('salt', - ['a'], - 'a', - 1), - ret) + with patch.dict( + win_dns_client.__salt__, {"win_dns_client.rm_dns": mock} + ): + ret.update( + { + "changes": { + "Servers Added": ["a"], + "Servers Removed": [], + "Servers Reordered": [], + }, + "comment": "Failed to remove 2 from DNS" " server list", + } + ) + self.assertDictEqual( + win_dns_client.dns_exists("salt", ["a"], "a", 1), ret + ) - ret.update({'comment': 'DNS Servers have been updated', - 'result': True}) - self.assertDictEqual(win_dns_client.dns_exists('salt', - ['a']), ret) + ret.update( + {"comment": "DNS Servers have been updated", "result": True} + ) + self.assertDictEqual(win_dns_client.dns_exists("salt", ["a"]), ret) def test_dns_dhcp(self): - ''' + """ Test to configure the DNS server list from DHCP Server - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} - mock = MagicMock(side_effect=['dhcp', 'salt', 'salt']) - with patch.dict(win_dns_client.__salt__, - {'win_dns_client.get_dns_config': mock}): - ret.update({'comment': 'Local Area Connection already configured' - ' with DNS from DHCP'}) - self.assertDictEqual(win_dns_client.dns_dhcp('salt'), ret) + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + mock = MagicMock(side_effect=["dhcp", "salt", "salt"]) + with patch.dict( + win_dns_client.__salt__, {"win_dns_client.get_dns_config": mock} + ): + ret.update( + { + "comment": "Local Area Connection already configured" + " with DNS from DHCP" + } + ) + self.assertDictEqual(win_dns_client.dns_dhcp("salt"), ret) with patch.dict(win_dns_client.__opts__, {"test": True}): - ret.update({'comment': '', 'result': None, - 'changes': {'dns': 'configured from DHCP'}}) - self.assertDictEqual(win_dns_client.dns_dhcp('salt'), ret) + ret.update( + { + "comment": "", + "result": None, + "changes": {"dns": "configured from DHCP"}, + } + ) + self.assertDictEqual(win_dns_client.dns_dhcp("salt"), ret) with patch.dict(win_dns_client.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.dict(win_dns_client.__salt__, - {'win_dns_client.dns_dhcp': mock}): - ret.update({'result': True}) - self.assertDictEqual(win_dns_client.dns_dhcp('salt'), ret) + with patch.dict( + win_dns_client.__salt__, {"win_dns_client.dns_dhcp": mock} + ): + ret.update({"result": True}) + self.assertDictEqual(win_dns_client.dns_dhcp("salt"), ret) def test_primary_suffix(self): - ''' + """ Test to configure the global primary DNS suffix of a DHCP client. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} - ret.update({'comment': "'updates' must be a boolean value"}) - self.assertDictEqual(win_dns_client.primary_suffix('salt', updates='a' - ), ret) + """ + ret = {"name": "salt", "changes": {}, "result": False, "comment": ""} + ret.update({"comment": "'updates' must be a boolean value"}) + self.assertDictEqual(win_dns_client.primary_suffix("salt", updates="a"), ret) - mock = MagicMock(side_effect=[{'vdata': 'a'}, {'vdata': False}, {'vdata': 'b'}, {'vdata': False}]) - with patch.dict(win_dns_client.__salt__, {'reg.read_value': mock}): - ret.update({'comment': 'No changes needed', 'result': True}) - self.assertDictEqual(win_dns_client.primary_suffix('salt', 'a'), - ret) + mock = MagicMock( + side_effect=[ + {"vdata": "a"}, + {"vdata": False}, + {"vdata": "b"}, + {"vdata": False}, + ] + ) + with patch.dict(win_dns_client.__salt__, {"reg.read_value": mock}): + ret.update({"comment": "No changes needed", "result": True}) + self.assertDictEqual(win_dns_client.primary_suffix("salt", "a"), ret) mock = MagicMock(return_value=True) - with patch.dict(win_dns_client.__salt__, {'reg.set_value': mock}): - ret.update({'changes': {'new': {'suffix': 'a'}, - 'old': {'suffix': 'b'}}, - 'comment': 'Updated primary DNS suffix (a)'}) - self.assertDictEqual(win_dns_client.primary_suffix('salt', - 'a'), ret) + with patch.dict(win_dns_client.__salt__, {"reg.set_value": mock}): + ret.update( + { + "changes": {"new": {"suffix": "a"}, "old": {"suffix": "b"}}, + "comment": "Updated primary DNS suffix (a)", + } + ) + self.assertDictEqual(win_dns_client.primary_suffix("salt", "a"), ret) diff --git a/tests/unit/states/test_win_iis.py b/tests/unit/states/test_win_iis.py index ab3c74f4a91..d5d8b0f7f14 100644 --- a/tests/unit/states/test_win_iis.py +++ b/tests/unit/states/test_win_iis.py @@ -1,111 +1,145 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for Windows iis Module 'state.win_iis' :platform: Windows .. versionadded:: 2019.2.2 -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_iis as win_iis # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class WinIisTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.win_pki - ''' + """ def setup_loader_modules(self): return {win_iis: {}} - def __base_webconfiguration_ret(self, comment='', changes=None, name='', result=None): + def __base_webconfiguration_ret( + self, comment="", changes=None, name="", result=None + ): return { - 'name': name, - 'changes': changes if changes else {}, - 'comment': comment, - 'result': result, + "name": name, + "changes": changes if changes else {}, + "comment": comment, + "result": result, } def test_webconfiguration_settings_no_settings(self): - name = 'IIS' + name = "IIS" settings = {} - expected_ret = self.__base_webconfiguration_ret(name=name, comment='No settings to change provided.', - result=True) + expected_ret = self.__base_webconfiguration_ret( + name=name, comment="No settings to change provided.", result=True + ) actual_ret = win_iis.webconfiguration_settings(name, settings) self.assertEqual(expected_ret, actual_ret) def test_webconfiguration_settings_collection_failure(self): - name = 'IIS:\\' + name = "IIS:\\" settings = { - 'system.applicationHost/sites': { - 'Collection[{name: site0}].logFile.directory': 'C:\\logs\\iis\\site0', + "system.applicationHost/sites": { + "Collection[{name: site0}].logFile.directory": "C:\\logs\\iis\\site0", }, } old_settings = [ - {'filter': 'system.applicationHost/sites', 'name': 'Collection[{name: site0}].logFile.directory', - 'value': 'C:\\logs\\iis\\old_site'}] + { + "filter": "system.applicationHost/sites", + "name": "Collection[{name: site0}].logFile.directory", + "value": "C:\\logs\\iis\\old_site", + } + ] current_settings = old_settings new_settings = old_settings expected_ret = self.__base_webconfiguration_ret( name=name, result=False, changes={ - 'changes': {old_settings[0]['filter'] + '.' + old_settings[0]['name']: { - 'old': old_settings[0]['value'], - 'new': settings[old_settings[0]['filter']][old_settings[0]['name']], - }}, - 'failures': {old_settings[0]['filter'] + '.' + old_settings[0]['name']: { - 'old': old_settings[0]['value'], - 'new': new_settings[0]['value'], - }}, + "changes": { + old_settings[0]["filter"] + + "." + + old_settings[0]["name"]: { + "old": old_settings[0]["value"], + "new": settings[old_settings[0]["filter"]][ + old_settings[0]["name"] + ], + } + }, + "failures": { + old_settings[0]["filter"] + + "." + + old_settings[0]["name"]: { + "old": old_settings[0]["value"], + "new": new_settings[0]["value"], + } + }, }, - comment='Some settings failed to change.' + comment="Some settings failed to change.", ) - with patch.dict(win_iis.__salt__, { - 'win_iis.get_webconfiguration_settings': MagicMock( - side_effect=[old_settings, current_settings, new_settings]), - 'win_iis.set_webconfiguration_settings': MagicMock(return_value=True), - }), patch.dict(win_iis.__opts__, {'test': False}): + with patch.dict( + win_iis.__salt__, + { + "win_iis.get_webconfiguration_settings": MagicMock( + side_effect=[old_settings, current_settings, new_settings] + ), + "win_iis.set_webconfiguration_settings": MagicMock(return_value=True), + }, + ), patch.dict(win_iis.__opts__, {"test": False}): actual_ret = win_iis.webconfiguration_settings(name, settings) self.assertEqual(expected_ret, actual_ret) def test_webconfiguration_settings_collection(self): - name = 'IIS:\\' + name = "IIS:\\" settings = { - 'system.applicationHost/sites': { - 'Collection[{name: site0}].logFile.directory': 'C:\\logs\\iis\\site0', + "system.applicationHost/sites": { + "Collection[{name: site0}].logFile.directory": "C:\\logs\\iis\\site0", }, } old_settings = [ - {'filter': 'system.applicationHost/sites', 'name': 'Collection[{name: site0}].logFile.directory', - 'value': 'C:\\logs\\iis\\old_site'}] + { + "filter": "system.applicationHost/sites", + "name": "Collection[{name: site0}].logFile.directory", + "value": "C:\\logs\\iis\\old_site", + } + ] current_settings = [ - {'filter': 'system.applicationHost/sites', 'name': 'Collection[{name: site0}].logFile.directory', - 'value': 'C:\\logs\\iis\\site0'}] + { + "filter": "system.applicationHost/sites", + "name": "Collection[{name: site0}].logFile.directory", + "value": "C:\\logs\\iis\\site0", + } + ] new_settings = current_settings expected_ret = self.__base_webconfiguration_ret( name=name, result=True, - changes={old_settings[0]['filter'] + '.' + old_settings[0]['name']: { - 'old': old_settings[0]['value'], - 'new': new_settings[0]['value'], - }}, - comment='Set settings to contain the provided values.' + changes={ + old_settings[0]["filter"] + + "." + + old_settings[0]["name"]: { + "old": old_settings[0]["value"], + "new": new_settings[0]["value"], + } + }, + comment="Set settings to contain the provided values.", ) - with patch.dict(win_iis.__salt__, { - 'win_iis.get_webconfiguration_settings': MagicMock( - side_effect=[old_settings, current_settings, new_settings]), - 'win_iis.set_webconfiguration_settings': MagicMock(return_value=True), - }), patch.dict(win_iis.__opts__, {'test': False}): + with patch.dict( + win_iis.__salt__, + { + "win_iis.get_webconfiguration_settings": MagicMock( + side_effect=[old_settings, current_settings, new_settings] + ), + "win_iis.set_webconfiguration_settings": MagicMock(return_value=True), + }, + ), patch.dict(win_iis.__opts__, {"test": False}): actual_ret = win_iis.webconfiguration_settings(name, settings) self.assertEqual(expected_ret, actual_ret) diff --git a/tests/unit/states/test_win_lgpo.py b/tests/unit/states/test_win_lgpo.py index c9e2bf01983..35d4417752a 100644 --- a/tests/unit/states/test_win_lgpo.py +++ b/tests/unit/states/test_win_lgpo.py @@ -1,9 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Shane Lee <slee@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + +# Import Salt Libs +import salt.config + +# Import 3rd Party Libs +import salt.ext.six as six +import salt.loader +import salt.states.win_lgpo as win_lgpo +import salt.utils.platform +import salt.utils.stringutils # Import Salt Testing Libs from tests.support.helpers import destructiveTest @@ -11,269 +21,206 @@ from tests.support.mixins import LoaderModuleMockMixin from tests.support.mock import patch from tests.support.unit import TestCase, skipIf -# Import Salt Libs -import salt.config -import salt.loader -import salt.states.win_lgpo as win_lgpo -import salt.utils.platform -import salt.utils.stringutils - -# Import 3rd Party Libs -import salt.ext.six as six - # We're going to actually use the loader, without grains (slow) opts = salt.config.DEFAULT_MINION_OPTS.copy() utils = salt.loader.utils(opts) modules = salt.loader.minion_mods(opts, utils=utils) -LOADER_DICTS = { - win_lgpo: { - '__opts__': opts, - '__salt__': modules, - '__utils__': utils, - } -} +LOADER_DICTS = {win_lgpo: {"__opts__": opts, "__salt__": modules, "__utils__": utils}} class WinLGPOComparePoliciesTestCase(TestCase): - ''' + """ Test cases for the win_lgpo state - ''' + """ + def test__compare_policies_string(self): - ''' + """ ``_compare_policies`` should only return ``True`` when the string values are the same. All other scenarios should return ``False`` - ''' - compare_string = 'Salty test' + """ + compare_string = "Salty test" # Same - self.assertTrue( - win_lgpo._compare_policies(compare_string, compare_string) - ) + self.assertTrue(win_lgpo._compare_policies(compare_string, compare_string)) # Different - self.assertFalse( - win_lgpo._compare_policies(compare_string, 'Not the same') - ) + self.assertFalse(win_lgpo._compare_policies(compare_string, "Not the same")) # List - self.assertFalse( - win_lgpo._compare_policies(compare_string, ['item1', 'item2']) - ) + self.assertFalse(win_lgpo._compare_policies(compare_string, ["item1", "item2"])) # Dict - self.assertFalse( - win_lgpo._compare_policies(compare_string, {'key': 'value'}) - ) + self.assertFalse(win_lgpo._compare_policies(compare_string, {"key": "value"})) # None - self.assertFalse( - win_lgpo._compare_policies(compare_string, None) - ) + self.assertFalse(win_lgpo._compare_policies(compare_string, None)) def test__compare_policies_list(self): - ''' + """ ``_compare_policies`` should only return ``True`` when the lists are the same. All other scenarios should return ``False`` - ''' - compare_list = ['Salty', 'test'] + """ + compare_list = ["Salty", "test"] # Same - self.assertTrue( - win_lgpo._compare_policies(compare_list, compare_list) - ) + self.assertTrue(win_lgpo._compare_policies(compare_list, compare_list)) # Different self.assertFalse( - win_lgpo._compare_policies(compare_list, ['Not', 'the', 'same']) + win_lgpo._compare_policies(compare_list, ["Not", "the", "same"]) ) # String - self.assertFalse( - win_lgpo._compare_policies(compare_list, 'Not a list') - ) + self.assertFalse(win_lgpo._compare_policies(compare_list, "Not a list")) # Dict - self.assertFalse( - win_lgpo._compare_policies(compare_list, {'key': 'value'}) - ) + self.assertFalse(win_lgpo._compare_policies(compare_list, {"key": "value"})) # None - self.assertFalse( - win_lgpo._compare_policies(compare_list, None) - ) + self.assertFalse(win_lgpo._compare_policies(compare_list, None)) def test__compare_policies_dict(self): - ''' + """ ``_compare_policies`` should only return ``True`` when the dicts are the same. All other scenarios should return ``False`` - ''' - compare_dict = {'Salty': 'test'} + """ + compare_dict = {"Salty": "test"} # Same - self.assertTrue( - win_lgpo._compare_policies(compare_dict, compare_dict) - ) + self.assertTrue(win_lgpo._compare_policies(compare_dict, compare_dict)) # Different - self.assertFalse( - win_lgpo._compare_policies(compare_dict, {'key': 'value'}) - ) + self.assertFalse(win_lgpo._compare_policies(compare_dict, {"key": "value"})) # String - self.assertFalse( - win_lgpo._compare_policies(compare_dict, 'Not a dict') - ) + self.assertFalse(win_lgpo._compare_policies(compare_dict, "Not a dict")) # List - self.assertFalse( - win_lgpo._compare_policies(compare_dict, ['Not', 'a', 'dict']) - ) + self.assertFalse(win_lgpo._compare_policies(compare_dict, ["Not", "a", "dict"])) # None - self.assertFalse( - win_lgpo._compare_policies(compare_dict, None) - ) + self.assertFalse(win_lgpo._compare_policies(compare_dict, None)) def test__compare_policies_integer(self): - ''' + """ ``_compare_policies`` should only return ``True`` when the integer values are the same. All other scenarios should return ``False`` - ''' + """ compare_integer = 1 # Same - self.assertTrue( - win_lgpo._compare_policies(compare_integer, compare_integer) - ) + self.assertTrue(win_lgpo._compare_policies(compare_integer, compare_integer)) # Different - self.assertFalse( - win_lgpo._compare_policies(compare_integer, 0) - ) + self.assertFalse(win_lgpo._compare_policies(compare_integer, 0)) # List self.assertFalse( - win_lgpo._compare_policies(compare_integer, ['item1', 'item2']) + win_lgpo._compare_policies(compare_integer, ["item1", "item2"]) ) # Dict - self.assertFalse( - win_lgpo._compare_policies(compare_integer, {'key': 'value'}) - ) + self.assertFalse(win_lgpo._compare_policies(compare_integer, {"key": "value"})) # None - self.assertFalse( - win_lgpo._compare_policies(compare_integer, None) - ) + self.assertFalse(win_lgpo._compare_policies(compare_integer, None)) @destructiveTest -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLGPOPolicyElementNames(TestCase, LoaderModuleMockMixin): - ''' + """ Test variations of the Point and Print Restrictions policy when Not Configured (NC) - ''' + """ + def setup_loader_modules(self): return LOADER_DICTS def setUp(self): - computer_policy = {'Point and Print Restrictions': 'Not Configured'} - with patch.dict(win_lgpo.__opts__, {'test': False}): - win_lgpo.set_(name='nc_state', computer_policy=computer_policy) + computer_policy = {"Point and Print Restrictions": "Not Configured"} + with patch.dict(win_lgpo.__opts__, {"test": False}): + win_lgpo.set_(name="nc_state", computer_policy=computer_policy) def test_current_element_naming_style(self): computer_policy = { - 'Point and Print Restrictions': { - 'Users can only point and print to these servers': - True, - 'Enter fully qualified server names separated by ' - 'semicolons': - 'fakeserver1;fakeserver2', - 'Users can only point and print to machines in their ' - 'forest': - True, - 'When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'When updating drivers for an existing connection': - 'Show warning only', - }} - with patch.dict(win_lgpo.__opts__, {'test': False}): - result = win_lgpo.set_(name='test_state', - computer_policy=computer_policy) + "Point and Print Restrictions": { + "Users can only point and print to these servers": True, + "Enter fully qualified server names separated by " + "semicolons": "fakeserver1;fakeserver2", + "Users can only point and print to machines in their " "forest": True, + "When installing drivers for a new connection": "Show warning and elevation prompt", + "When updating drivers for an existing connection": "Show warning only", + } + } + with patch.dict(win_lgpo.__opts__, {"test": False}): + result = win_lgpo.set_(name="test_state", computer_policy=computer_policy) result = win_lgpo._convert_to_unicode(result) expected = { - 'Point and Print Restrictions': { - 'Enter fully qualified server names separated by ' - 'semicolons': - 'fakeserver1;fakeserver2', - 'When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'Users can only point and print to machines in ' - 'their forest': - True, - 'Users can only point and print to these servers': - True, - 'When updating drivers for an existing connection': - 'Show warning only'}} + "Point and Print Restrictions": { + "Enter fully qualified server names separated by " + "semicolons": "fakeserver1;fakeserver2", + "When installing drivers for a new connection": "Show warning and elevation prompt", + "Users can only point and print to machines in " "their forest": True, + "Users can only point and print to these servers": True, + "When updating drivers for an existing connection": "Show warning only", + } + } self.assertDictEqual( - result['changes']['new']['Computer Configuration'], expected) + result["changes"]["new"]["Computer Configuration"], expected + ) def test_old_element_naming_style(self): computer_policy = { - 'Point and Print Restrictions': { - 'Users can only point and print to these servers': - True, - 'Enter fully qualified server names separated by ' - 'semicolons': - 'fakeserver1;fakeserver2', - 'Users can only point and print to machines in their ' - 'forest': - True, + "Point and Print Restrictions": { + "Users can only point and print to these servers": True, + "Enter fully qualified server names separated by " + "semicolons": "fakeserver1;fakeserver2", + "Users can only point and print to machines in their " "forest": True, # Here's the old one - 'Security Prompts: When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'When updating drivers for an existing connection': - 'Show warning only', - }} + "Security Prompts: When installing drivers for a new connection": "Show warning and elevation prompt", + "When updating drivers for an existing connection": "Show warning only", + } + } - with patch.dict(win_lgpo.__opts__, {'test': False}): - result = win_lgpo.set_(name='test_state', - computer_policy=computer_policy) + with patch.dict(win_lgpo.__opts__, {"test": False}): + result = win_lgpo.set_(name="test_state", computer_policy=computer_policy) if six.PY2: result = win_lgpo._convert_to_unicode(result) expected = { - 'Point and Print Restrictions': { - 'Enter fully qualified server names separated by ' - 'semicolons': - 'fakeserver1;fakeserver2', - 'When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'Users can only point and print to machines in ' - 'their forest': - True, - 'Users can only point and print to these servers': - True, - 'When updating drivers for an existing connection': - 'Show warning only'}} + "Point and Print Restrictions": { + "Enter fully qualified server names separated by " + "semicolons": "fakeserver1;fakeserver2", + "When installing drivers for a new connection": "Show warning and elevation prompt", + "Users can only point and print to machines in " "their forest": True, + "Users can only point and print to these servers": True, + "When updating drivers for an existing connection": "Show warning only", + } + } self.assertDictEqual( - result['changes']['new']['Computer Configuration'], expected) - expected = 'The LGPO module changed the way it gets policy element names.\n' \ - '"Security Prompts: When installing drivers for a new connection" is no longer valid.\n' \ - 'Please use "When installing drivers for a new connection" instead.\n' \ - 'The following policies changed:\n' \ - 'Point and Print Restrictions' - self.assertEqual(result['comment'], expected) + result["changes"]["new"]["Computer Configuration"], expected + ) + expected = ( + "The LGPO module changed the way it gets policy element names.\n" + '"Security Prompts: When installing drivers for a new connection" is no longer valid.\n' + 'Please use "When installing drivers for a new connection" instead.\n' + "The following policies changed:\n" + "Point and Print Restrictions" + ) + self.assertEqual(result["comment"], expected) def test_invalid_elements(self): computer_policy = { - 'Point and Print Restrictions': { - 'Invalid element spongebob': True, - 'Invalid element squidward': False}} + "Point and Print Restrictions": { + "Invalid element spongebob": True, + "Invalid element squidward": False, + } + } - with patch.dict(win_lgpo.__opts__, {'test': False}): - result = win_lgpo.set_(name='test_state', - computer_policy=computer_policy) + with patch.dict(win_lgpo.__opts__, {"test": False}): + result = win_lgpo.set_(name="test_state", computer_policy=computer_policy) expected = { - 'changes': {}, - 'comment': 'Invalid element name: Invalid element squidward\n' - 'Invalid element name: Invalid element spongebob', - 'name': 'test_state', - 'result': False} - self.assertDictEqual(result['changes'], expected['changes']) - self.assertIn('Invalid element squidward', result['comment']) - self.assertIn('Invalid element spongebob', result['comment']) - self.assertFalse(expected['result']) + "changes": {}, + "comment": "Invalid element name: Invalid element squidward\n" + "Invalid element name: Invalid element spongebob", + "name": "test_state", + "result": False, + } + self.assertDictEqual(result["changes"], expected["changes"]) + self.assertIn("Invalid element squidward", result["comment"]) + self.assertIn("Invalid element spongebob", result["comment"]) + self.assertFalse(expected["result"]) @destructiveTest -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLGPOPolicyElementNamesTestTrue(TestCase, LoaderModuleMockMixin): - ''' + """ Test variations of the Point and Print Restrictions policy when Not Configured (NC) - ''' + """ + configured = False def setup_loader_modules(self): @@ -282,96 +229,84 @@ class WinLGPOPolicyElementNamesTestTrue(TestCase, LoaderModuleMockMixin): def setUp(self): if not self.configured: computer_policy = { - 'Point and Print Restrictions': { - 'Users can only point and print to these servers': - True, - 'Enter fully qualified server names separated by ' - 'semicolons': - 'fakeserver1;fakeserver2', - 'Users can only point and print to machines in their ' - 'forest': - True, - 'When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'When updating drivers for an existing connection': - 'Show warning only', - }} - with patch.dict(win_lgpo.__opts__, {'test': False}): - win_lgpo.set_(name='nc_state', computer_policy=computer_policy) + "Point and Print Restrictions": { + "Users can only point and print to these servers": True, + "Enter fully qualified server names separated by " + "semicolons": "fakeserver1;fakeserver2", + "Users can only point and print to machines in their " + "forest": True, + "When installing drivers for a new connection": "Show warning and elevation prompt", + "When updating drivers for an existing connection": "Show warning only", + } + } + with patch.dict(win_lgpo.__opts__, {"test": False}): + win_lgpo.set_(name="nc_state", computer_policy=computer_policy) self.configured = True def test_current_element_naming_style(self): computer_policy = { - 'Point and Print Restrictions': { - 'Users can only point and print to these servers': - True, - 'Enter fully qualified server names separated by ' - 'semicolons': - 'fakeserver1;fakeserver2', - 'Users can only point and print to machines in their ' - 'forest': - True, - 'When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'When updating drivers for an existing connection': - 'Show warning only', - }} - with patch.dict(win_lgpo.__opts__, {'test': True}): - result = win_lgpo.set_(name='test_state', - computer_policy=computer_policy) + "Point and Print Restrictions": { + "Users can only point and print to these servers": True, + "Enter fully qualified server names separated by " + "semicolons": "fakeserver1;fakeserver2", + "Users can only point and print to machines in their " "forest": True, + "When installing drivers for a new connection": "Show warning and elevation prompt", + "When updating drivers for an existing connection": "Show warning only", + } + } + with patch.dict(win_lgpo.__opts__, {"test": True}): + result = win_lgpo.set_(name="test_state", computer_policy=computer_policy) expected = { - 'changes': {}, - 'comment': 'All specified policies are properly configured'} - self.assertDictEqual(result['changes'], expected['changes']) - self.assertTrue(result['result']) - self.assertEqual(result['comment'], expected['comment']) + "changes": {}, + "comment": "All specified policies are properly configured", + } + self.assertDictEqual(result["changes"], expected["changes"]) + self.assertTrue(result["result"]) + self.assertEqual(result["comment"], expected["comment"]) def test_old_element_naming_style(self): computer_policy = { - 'Point and Print Restrictions': { - 'Users can only point and print to these servers': - True, - 'Enter fully qualified server names separated by ' - 'semicolons': - 'fakeserver1;fakeserver2', - 'Users can only point and print to machines in their ' - 'forest': - True, + "Point and Print Restrictions": { + "Users can only point and print to these servers": True, + "Enter fully qualified server names separated by " + "semicolons": "fakeserver1;fakeserver2", + "Users can only point and print to machines in their " "forest": True, # Here's the old one - 'Security Prompts: When installing drivers for a new connection': - 'Show warning and elevation prompt', - 'When updating drivers for an existing connection': - 'Show warning only', - }} - with patch.dict(win_lgpo.__opts__, {'test': True}): - result = win_lgpo.set_(name='test_state', - computer_policy=computer_policy) + "Security Prompts: When installing drivers for a new connection": "Show warning and elevation prompt", + "When updating drivers for an existing connection": "Show warning only", + } + } + with patch.dict(win_lgpo.__opts__, {"test": True}): + result = win_lgpo.set_(name="test_state", computer_policy=computer_policy) expected = { - 'changes': {}, - 'comment': 'The LGPO module changed the way it gets policy element names.\n' - '"Security Prompts: When installing drivers for a new connection" is no longer valid.\n' - 'Please use "When installing drivers for a new connection" instead.\n' - 'All specified policies are properly configured'} - self.assertDictEqual(result['changes'], expected['changes']) - self.assertTrue(result['result']) - self.assertEqual(result['comment'], expected['comment']) + "changes": {}, + "comment": "The LGPO module changed the way it gets policy element names.\n" + '"Security Prompts: When installing drivers for a new connection" is no longer valid.\n' + 'Please use "When installing drivers for a new connection" instead.\n' + "All specified policies are properly configured", + } + self.assertDictEqual(result["changes"], expected["changes"]) + self.assertTrue(result["result"]) + self.assertEqual(result["comment"], expected["comment"]) def test_invalid_elements(self): computer_policy = { - 'Point and Print Restrictions': { - 'Invalid element spongebob': True, - 'Invalid element squidward': False}} + "Point and Print Restrictions": { + "Invalid element spongebob": True, + "Invalid element squidward": False, + } + } - with patch.dict(win_lgpo.__opts__, {'test': True}): - result = win_lgpo.set_(name='test_state', - computer_policy=computer_policy) + with patch.dict(win_lgpo.__opts__, {"test": True}): + result = win_lgpo.set_(name="test_state", computer_policy=computer_policy) expected = { - 'changes': {}, - 'comment': 'Invalid element name: Invalid element squidward\n' - 'Invalid element name: Invalid element spongebob', - 'name': 'test_state', - 'result': False} - self.assertDictEqual(result['changes'], expected['changes']) - self.assertIn('Invalid element squidward', result['comment']) - self.assertIn('Invalid element spongebob', result['comment']) - self.assertFalse(expected['result']) + "changes": {}, + "comment": "Invalid element name: Invalid element squidward\n" + "Invalid element name: Invalid element spongebob", + "name": "test_state", + "result": False, + } + self.assertDictEqual(result["changes"], expected["changes"]) + self.assertIn("Invalid element squidward", result["comment"]) + self.assertIn("Invalid element spongebob", result["comment"]) + self.assertFalse(expected["result"]) diff --git a/tests/unit/states/test_win_license.py b/tests/unit/states/test_win_license.py index 4441b257362..a022ad6cf48 100644 --- a/tests/unit/states/test_win_license.py +++ b/tests/unit/states/test_win_license.py @@ -1,170 +1,191 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_license as win_license # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class LicenseTestCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return {win_license: {}} def test_activate(self): - ''' + """ Test activating the given product key - ''' + """ expected = { - 'changes': {}, - 'comment': 'Windows is now activated.', - 'name': 'AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE', - 'result': True + "changes": {}, + "comment": "Windows is now activated.", + "name": "AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE", + "result": True, } info = { - 'description': 'Prof', - 'licensed': False, - 'name': 'Win7', - 'partial_key': 'XXXXX' + "description": "Prof", + "licensed": False, + "name": "Win7", + "partial_key": "XXXXX", } info_mock = MagicMock(return_value=info) - install_mock = MagicMock(return_value='Installed successfully') - activate_mock = MagicMock(return_value='Activated successfully') - with patch.dict(win_license.__salt__, {'license.info': info_mock, - 'license.install': install_mock, - 'license.activate': activate_mock}): - out = win_license.activate('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') + install_mock = MagicMock(return_value="Installed successfully") + activate_mock = MagicMock(return_value="Activated successfully") + with patch.dict( + win_license.__salt__, + { + "license.info": info_mock, + "license.install": install_mock, + "license.activate": activate_mock, + }, + ): + out = win_license.activate("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") info_mock.assert_called_once_with() - install_mock.assert_called_once_with('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') + install_mock.assert_called_once_with("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") activate_mock.assert_called_once_with() self.assertEqual(out, expected) def test_installed_not_activated(self): - ''' + """ Test activating the given product key when the key is installed but not activated - ''' + """ expected = { - 'changes': {}, - 'comment': 'Windows is now activated.', - 'name': 'AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE', - 'result': True + "changes": {}, + "comment": "Windows is now activated.", + "name": "AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE", + "result": True, } info = { - 'description': 'Prof', - 'licensed': False, - 'name': 'Win7', - 'partial_key': 'ABCDE' + "description": "Prof", + "licensed": False, + "name": "Win7", + "partial_key": "ABCDE", } info_mock = MagicMock(return_value=info) - install_mock = MagicMock(return_value='Installed successfully') - activate_mock = MagicMock(return_value='Activated successfully') - with patch.dict(win_license.__salt__, {'license.info': info_mock, - 'license.install': install_mock, - 'license.activate': activate_mock}): - out = win_license.activate('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') + install_mock = MagicMock(return_value="Installed successfully") + activate_mock = MagicMock(return_value="Activated successfully") + with patch.dict( + win_license.__salt__, + { + "license.info": info_mock, + "license.install": install_mock, + "license.activate": activate_mock, + }, + ): + out = win_license.activate("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") info_mock.assert_called_once_with() assert not install_mock.called activate_mock.assert_called_once_with() self.assertEqual(out, expected) def test_installed_activated(self): - ''' + """ Test activating the given product key when its already activated - ''' + """ expected = { - 'changes': {}, - 'comment': 'Windows is already activated.', - 'name': 'AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE', - 'result': True + "changes": {}, + "comment": "Windows is already activated.", + "name": "AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE", + "result": True, } info = { - 'description': 'Prof', - 'licensed': True, - 'name': 'Win7', - 'partial_key': 'ABCDE' + "description": "Prof", + "licensed": True, + "name": "Win7", + "partial_key": "ABCDE", } info_mock = MagicMock(return_value=info) - install_mock = MagicMock(return_value='Installed successfully') - activate_mock = MagicMock(return_value='Activated successfully') - with patch.dict(win_license.__salt__, {'license.info': info_mock, - 'license.install': install_mock, - 'license.activate': activate_mock}): - out = win_license.activate('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') + install_mock = MagicMock(return_value="Installed successfully") + activate_mock = MagicMock(return_value="Activated successfully") + with patch.dict( + win_license.__salt__, + { + "license.info": info_mock, + "license.install": install_mock, + "license.activate": activate_mock, + }, + ): + out = win_license.activate("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") info_mock.assert_called_once_with() assert not install_mock.called assert not activate_mock.called self.assertEqual(out, expected) def test_installed_install_fail(self): - ''' + """ Test activating the given product key when the install fails - ''' + """ expected = { - 'changes': {}, - 'comment': 'Unable to install the given product key is it valid?', - 'name': 'AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE', - 'result': False + "changes": {}, + "comment": "Unable to install the given product key is it valid?", + "name": "AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE", + "result": False, } info = { - 'description': 'Prof', - 'licensed': False, - 'name': 'Win7', - 'partial_key': '12345' + "description": "Prof", + "licensed": False, + "name": "Win7", + "partial_key": "12345", } info_mock = MagicMock(return_value=info) - install_mock = MagicMock(return_value='Failed') + install_mock = MagicMock(return_value="Failed") activate_mock = MagicMock() - with patch.dict(win_license.__salt__, {'license.info': info_mock, - 'license.install': install_mock, - 'license.activate': activate_mock}): - out = win_license.activate('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') + with patch.dict( + win_license.__salt__, + { + "license.info": info_mock, + "license.install": install_mock, + "license.activate": activate_mock, + }, + ): + out = win_license.activate("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") info_mock.assert_called_once_with() - install_mock.assert_called_once_with('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') + install_mock.assert_called_once_with("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") assert not activate_mock.called self.assertEqual(out, expected) def test_installed_activate_fail(self): - ''' + """ Test activating the given product key when the install fails - ''' + """ expected = { - 'changes': {}, - 'comment': 'Unable to activate the given product key.', - 'name': 'AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE', - 'result': False + "changes": {}, + "comment": "Unable to activate the given product key.", + "name": "AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE", + "result": False, } info = { - 'description': 'Prof', - 'licensed': False, - 'name': 'Win7', - 'partial_key': 'ABCDE' + "description": "Prof", + "licensed": False, + "name": "Win7", + "partial_key": "ABCDE", } info_mock = MagicMock(return_value=info) install_mock = MagicMock() - activate_mock = MagicMock(return_value='Failed to activate') - with patch.dict(win_license.__salt__, {'license.info': info_mock, - 'license.install': install_mock, - 'license.activate': activate_mock}): - out = win_license.activate('AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE') + activate_mock = MagicMock(return_value="Failed to activate") + with patch.dict( + win_license.__salt__, + { + "license.info": info_mock, + "license.install": install_mock, + "license.activate": activate_mock, + }, + ): + out = win_license.activate("AAAAA-AAAAA-AAAAA-AAAA-AAAAA-ABCDE") info_mock.assert_called_once_with() assert not install_mock.called activate_mock.assert_called_once_with() diff --git a/tests/unit/states/test_win_network.py b/tests/unit/states/test_win_network.py index 03a047ecb32..4f1c8022144 100644 --- a/tests/unit/states/test_win_network.py +++ b/tests/unit/states/test_win_network.py @@ -1,266 +1,346 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_network as win_network +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class WinNetworkTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the nftables state - ''' + """ + def setup_loader_modules(self): return {win_network: {}} def test_managed_missing_parameters(self): - ''' + """ Test to ensure that the named interface is configured properly. - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': 'dns_proto must be one of the following: static, dhcp\n' - 'ip_proto must be one of the following: static, dhcp'} - self.assertDictEqual(win_network.managed('salt'), ret) + """ + ret = { + "name": "salt", + "changes": {}, + "result": False, + "comment": "dns_proto must be one of the following: static, dhcp\n" + "ip_proto must be one of the following: static, dhcp", + } + self.assertDictEqual(win_network.managed("salt"), ret) def test_managed_static_enabled_false(self): - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': 'Interface \'salt\' is up to date (already disabled)'} + ret = { + "name": "salt", + "changes": {}, + "result": True, + "comment": "Interface 'salt' is up to date (already disabled)", + } mock_false = MagicMock(return_value=False) with patch.dict(win_network.__salt__, {"ip.is_enabled": mock_false}): self.assertDictEqual( win_network.managed( - 'salt', dns_proto='static', ip_proto='static', enabled=False), - ret) + "salt", dns_proto="static", ip_proto="static", enabled=False + ), + ret, + ) def test_managed_test_true(self): - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': 'Failed to enable interface \'salt\' to make changes'} + ret = { + "name": "salt", + "changes": {}, + "result": False, + "comment": "Failed to enable interface 'salt' to make changes", + } mock_false = MagicMock(return_value=False) - with patch.dict(win_network.__salt__, {"ip.is_enabled": mock_false, - "ip.enable": mock_false}), \ - patch.dict(win_network.__opts__, {"test": False}): + with patch.dict( + win_network.__salt__, {"ip.is_enabled": mock_false, "ip.enable": mock_false} + ), patch.dict(win_network.__opts__, {"test": False}): self.assertDictEqual( - win_network.managed( - 'salt', dns_proto='static', ip_proto='static'), - ret) + win_network.managed("salt", dns_proto="static", ip_proto="static"), ret + ) def test_managed_validate_errors(self): - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': 'The following SLS configuration errors were ' - 'detected:\n' - '- First Error\n' - '- Second Error'} + ret = { + "name": "salt", + "changes": {}, + "result": False, + "comment": "The following SLS configuration errors were " + "detected:\n" + "- First Error\n" + "- Second Error", + } mock_true = MagicMock(return_value=True) - mock_validate = MagicMock(return_value=['First Error', 'Second Error']) - with patch.dict(win_network.__salt__, {"ip.is_enabled": mock_true}),\ - patch.object(win_network, '_validate', mock_validate): + mock_validate = MagicMock(return_value=["First Error", "Second Error"]) + with patch.dict( + win_network.__salt__, {"ip.is_enabled": mock_true} + ), patch.object(win_network, "_validate", mock_validate): self.assertDictEqual( - win_network.managed( - 'salt', dns_proto='static', ip_proto='static'), - ret) + win_network.managed("salt", dns_proto="static", ip_proto="static"), ret + ) def test_managed_get_current_config_failed(self): - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': 'Unable to get current configuration for interface ' - '\'salt\''} + ret = { + "name": "salt", + "changes": {}, + "result": False, + "comment": "Unable to get current configuration for interface " "'salt'", + } mock_true = MagicMock(return_value=True) mock_false = MagicMock(return_value=False) mock_validate = MagicMock(return_value=[]) - with patch.dict(win_network.__salt__, {'ip.is_enabled': mock_true, - 'ip.get_interface': mock_false}), \ - patch.object(win_network, '_validate', mock_validate): + with patch.dict( + win_network.__salt__, + {"ip.is_enabled": mock_true, "ip.get_interface": mock_false}, + ), patch.object(win_network, "_validate", mock_validate): self.assertDictEqual( - win_network.managed('salt', dns_proto='dhcp', ip_proto='dhcp'), - ret) + win_network.managed("salt", dns_proto="dhcp", ip_proto="dhcp"), ret + ) def test_managed_test_true_no_changes(self): - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': 'Interface \'salt\' is up to date'} + ret = { + "name": "salt", + "changes": {}, + "result": True, + "comment": "Interface 'salt' is up to date", + } mock_true = MagicMock(return_value=True) mock_validate = MagicMock(return_value=[]) - mock_get_int = MagicMock(return_value={ - 'DHCP enabled': 'yes', - 'DNS servers configured through DHCP': '192.168.0.10'}) - with patch.dict(win_network.__salt__, {'ip.is_enabled': mock_true, - 'ip.get_interface': mock_get_int}), \ - patch.dict(win_network.__opts__, {"test": True}), \ - patch.object(win_network, '_validate', mock_validate): + mock_get_int = MagicMock( + return_value={ + "DHCP enabled": "yes", + "DNS servers configured through DHCP": "192.168.0.10", + } + ) + with patch.dict( + win_network.__salt__, + {"ip.is_enabled": mock_true, "ip.get_interface": mock_get_int}, + ), patch.dict(win_network.__opts__, {"test": True}), patch.object( + win_network, "_validate", mock_validate + ): self.assertDictEqual( - win_network.managed('salt', dns_proto='dhcp', ip_proto='dhcp'), - ret) + win_network.managed("salt", dns_proto="dhcp", ip_proto="dhcp"), ret + ) def test_managed_test_true_changes(self): - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': 'The following changes will be made to interface ' - '\'salt\':\n' - '- DNS protocol will be changed to: dhcp'} + ret = { + "name": "salt", + "changes": {}, + "result": None, + "comment": "The following changes will be made to interface " + "'salt':\n" + "- DNS protocol will be changed to: dhcp", + } mock_true = MagicMock(return_value=True) mock_validate = MagicMock(return_value=[]) - mock_get_int = MagicMock(return_value={ - 'DHCP enabled': 'no', - 'Statically Configured DNS Servers': '192.168.0.10'}) - with patch.dict(win_network.__salt__, {'ip.is_enabled': mock_true, - 'ip.get_interface': mock_get_int}), \ - patch.dict(win_network.__opts__, {"test": True}), \ - patch.object(win_network, '_validate', mock_validate): + mock_get_int = MagicMock( + return_value={ + "DHCP enabled": "no", + "Statically Configured DNS Servers": "192.168.0.10", + } + ) + with patch.dict( + win_network.__salt__, + {"ip.is_enabled": mock_true, "ip.get_interface": mock_get_int}, + ), patch.dict(win_network.__opts__, {"test": True}), patch.object( + win_network, "_validate", mock_validate + ): self.assertDictEqual( - win_network.managed('salt', dns_proto='dhcp', ip_proto='dhcp'), - ret) + win_network.managed("salt", dns_proto="dhcp", ip_proto="dhcp"), ret + ) def test_managed_failed(self): - ret = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': 'Failed to set desired configuration settings for ' - 'interface \'salt\''} + ret = { + "name": "salt", + "changes": {}, + "result": False, + "comment": "Failed to set desired configuration settings for " + "interface 'salt'", + } mock_true = MagicMock(return_value=True) mock_validate = MagicMock(return_value=[]) - mock_get_int = MagicMock(return_value={ - 'DHCP enabled': 'no', - 'Statically Configured DNS Servers': '192.168.0.10'}) - with patch.dict(win_network.__salt__, {'ip.is_enabled': mock_true, - 'ip.get_interface': mock_get_int, - 'ip.set_dhcp_dns': mock_true, - 'ip.set_dhcp_ip': mock_true}), \ - patch.dict(win_network.__opts__, {"test": False}), \ - patch.object(win_network, '_validate', mock_validate): + mock_get_int = MagicMock( + return_value={ + "DHCP enabled": "no", + "Statically Configured DNS Servers": "192.168.0.10", + } + ) + with patch.dict( + win_network.__salt__, + { + "ip.is_enabled": mock_true, + "ip.get_interface": mock_get_int, + "ip.set_dhcp_dns": mock_true, + "ip.set_dhcp_ip": mock_true, + }, + ), patch.dict(win_network.__opts__, {"test": False}), patch.object( + win_network, "_validate", mock_validate + ): self.assertDictEqual( - win_network.managed('salt', dns_proto='dhcp', ip_proto='dhcp'), - ret) + win_network.managed("salt", dns_proto="dhcp", ip_proto="dhcp"), ret + ) def test_managed(self): - ret = {'name': 'salt', - 'changes': { - 'DHCP enabled': { - 'new': 'yes', - 'old': 'no'}, - 'DNS servers configured through DHCP': { - 'new': '192.168.0.10', - 'old': ''}, - 'Statically Configured DNS Servers': { - 'new': '', - 'old': '192.168.0.10' - } - }, - 'result': True, - 'comment': 'Successfully updated configuration for interface ' - '\'salt\''} + ret = { + "name": "salt", + "changes": { + "DHCP enabled": {"new": "yes", "old": "no"}, + "DNS servers configured through DHCP": { + "new": "192.168.0.10", + "old": "", + }, + "Statically Configured DNS Servers": {"new": "", "old": "192.168.0.10"}, + }, + "result": True, + "comment": "Successfully updated configuration for interface " "'salt'", + } mock_true = MagicMock(return_value=True) mock_validate = MagicMock(return_value=[]) - mock_get_int = MagicMock(side_effect=[ - {'DHCP enabled': 'no', 'Statically Configured DNS Servers': '192.168.0.10'}, - {'DHCP enabled': 'yes', 'DNS servers configured through DHCP': '192.168.0.10'}, - ]) - with patch.dict(win_network.__salt__, {'ip.is_enabled': mock_true, - 'ip.get_interface': mock_get_int, - 'ip.set_dhcp_dns': mock_true, - 'ip.set_dhcp_ip': mock_true}), \ - patch.dict(win_network.__opts__, {"test": False}), \ - patch.object(win_network, '_validate', mock_validate): + mock_get_int = MagicMock( + side_effect=[ + { + "DHCP enabled": "no", + "Statically Configured DNS Servers": "192.168.0.10", + }, + { + "DHCP enabled": "yes", + "DNS servers configured through DHCP": "192.168.0.10", + }, + ] + ) + with patch.dict( + win_network.__salt__, + { + "ip.is_enabled": mock_true, + "ip.get_interface": mock_get_int, + "ip.set_dhcp_dns": mock_true, + "ip.set_dhcp_ip": mock_true, + }, + ), patch.dict(win_network.__opts__, {"test": False}), patch.object( + win_network, "_validate", mock_validate + ): self.assertDictEqual( - win_network.managed('salt', dns_proto='dhcp', ip_proto='dhcp'), - ret) + win_network.managed("salt", dns_proto="dhcp", ip_proto="dhcp"), ret + ) def test_managed_static_dns_clear(self): - expected = {'name': 'salt', - 'changes': { - 'Statically Configured DNS Servers': { - 'new': 'None', - 'old': '192.168.0.10' - } - }, - 'result': True, - 'comment': 'Successfully updated configuration for ' - 'interface \'salt\''} + expected = { + "name": "salt", + "changes": { + "Statically Configured DNS Servers": { + "new": "None", + "old": "192.168.0.10", + } + }, + "result": True, + "comment": "Successfully updated configuration for " "interface 'salt'", + } mock_true = MagicMock(return_value=True) mock_validate = MagicMock(return_value=[]) - mock_get_int = MagicMock(side_effect=[ - {'DHCP enabled': 'no', 'Statically Configured DNS Servers': '192.168.0.10'}, - {'DHCP enabled': 'no', 'Statically Configured DNS Servers': 'None'}, - ]) - with patch.dict(win_network.__salt__, {'ip.is_enabled': mock_true, - 'ip.get_interface': mock_get_int, - 'ip.set_static_dns': mock_true}), \ - patch.dict(win_network.__opts__, {"test": False}), \ - patch.object(win_network, '_validate', mock_validate): + mock_get_int = MagicMock( + side_effect=[ + { + "DHCP enabled": "no", + "Statically Configured DNS Servers": "192.168.0.10", + }, + {"DHCP enabled": "no", "Statically Configured DNS Servers": "None"}, + ] + ) + with patch.dict( + win_network.__salt__, + { + "ip.is_enabled": mock_true, + "ip.get_interface": mock_get_int, + "ip.set_static_dns": mock_true, + }, + ), patch.dict(win_network.__opts__, {"test": False}), patch.object( + win_network, "_validate", mock_validate + ): ret = win_network.managed( - 'salt', dns_proto='static', dns_servers=[], ip_proto='dhcp') + "salt", dns_proto="static", dns_servers=[], ip_proto="dhcp" + ) self.assertDictEqual(ret, expected) def test_managed_static_dns(self): - expected = {'name': 'salt', - 'changes': { - 'Statically Configured DNS Servers': { - 'new': '192.168.0.10', - 'old': 'None' - } - }, - 'result': True, - 'comment': 'Successfully updated configuration for ' - 'interface \'salt\''} + expected = { + "name": "salt", + "changes": { + "Statically Configured DNS Servers": { + "new": "192.168.0.10", + "old": "None", + } + }, + "result": True, + "comment": "Successfully updated configuration for " "interface 'salt'", + } mock_true = MagicMock(return_value=True) mock_validate = MagicMock(return_value=[]) - mock_get_int = MagicMock(side_effect=[ - {'DHCP enabled': 'no', 'Statically Configured DNS Servers': 'None'}, - {'DHCP enabled': 'no', 'Statically Configured DNS Servers': '192.168.0.10'}, - ]) - with patch.dict(win_network.__salt__, {'ip.is_enabled': mock_true, - 'ip.get_interface': mock_get_int, - 'ip.set_static_dns': mock_true}), \ - patch.dict(win_network.__opts__, {"test": False}), \ - patch.object(win_network, '_validate', mock_validate): + mock_get_int = MagicMock( + side_effect=[ + {"DHCP enabled": "no", "Statically Configured DNS Servers": "None"}, + { + "DHCP enabled": "no", + "Statically Configured DNS Servers": "192.168.0.10", + }, + ] + ) + with patch.dict( + win_network.__salt__, + { + "ip.is_enabled": mock_true, + "ip.get_interface": mock_get_int, + "ip.set_static_dns": mock_true, + }, + ), patch.dict(win_network.__opts__, {"test": False}), patch.object( + win_network, "_validate", mock_validate + ): ret = win_network.managed( - 'salt', dns_proto='static', dns_servers=['192.168.0.10'], ip_proto='dhcp') + "salt", + dns_proto="static", + dns_servers=["192.168.0.10"], + ip_proto="dhcp", + ) self.assertDictEqual(ret, expected) def test_managed_static_dns_no_action(self): - expected = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': 'Interface \'salt\' is up to date'} + expected = { + "name": "salt", + "changes": {}, + "result": True, + "comment": "Interface 'salt' is up to date", + } mock_true = MagicMock(return_value=True) mock_validate = MagicMock(return_value=[]) - mock_get_int = MagicMock(return_value={ - 'DHCP enabled': 'no', - 'Statically Configured DNS Servers': '192.168.0.10' - }) - with patch.dict(win_network.__salt__, {'ip.is_enabled': mock_true, - 'ip.get_interface': mock_get_int, - 'ip.set_static_dns': mock_true}), \ - patch.dict(win_network.__opts__, {"test": False}), \ - patch.object(win_network, '_validate', mock_validate): + mock_get_int = MagicMock( + return_value={ + "DHCP enabled": "no", + "Statically Configured DNS Servers": "192.168.0.10", + } + ) + with patch.dict( + win_network.__salt__, + { + "ip.is_enabled": mock_true, + "ip.get_interface": mock_get_int, + "ip.set_static_dns": mock_true, + }, + ), patch.dict(win_network.__opts__, {"test": False}), patch.object( + win_network, "_validate", mock_validate + ): # Don't pass dns_servers - ret = win_network.managed('salt', dns_proto='static', ip_proto='dhcp') + ret = win_network.managed("salt", dns_proto="static", ip_proto="dhcp") self.assertDictEqual(ret, expected) # Pass dns_servers=None ret = win_network.managed( - 'salt', dns_proto='static', dns_servers=None, ip_proto='dhcp') + "salt", dns_proto="static", dns_servers=None, ip_proto="dhcp" + ) self.assertDictEqual(ret, expected) diff --git a/tests/unit/states/test_win_path.py b/tests/unit/states/test_win_path.py index 027af1e7cdb..1bd7a98b638 100644 --- a/tests/unit/states/test_win_path.py +++ b/tests/unit/states/test_win_path.py @@ -1,123 +1,125 @@ # -*- coding: utf-8 -*- -''' +""" Tests for win_path states -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import copy -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - Mock, - MagicMock, - patch, -) +import copy # Import Salt Libs import salt.states.win_path as win_path -NAME = 'salt' +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.unit import TestCase + +NAME = "salt" class WinPathTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the win_path state - ''' + """ + def setup_loader_modules(self): return {win_path: {}} def test_absent(self): - ''' + """ Test various cases for win_path.absent - ''' - ret_base = {'name': NAME, 'result': True, 'changes': {}} + """ + ret_base = {"name": NAME, "result": True, "changes": {}} def _mock(retval): # Return a new MagicMock for each test case return MagicMock(side_effect=retval) # We don't really want to run the remove func - with patch.dict(win_path.__salt__, {'win_path.remove': Mock()}): + with patch.dict(win_path.__salt__, {"win_path.remove": Mock()}): # Test mode OFF - with patch.dict(win_path.__opts__, {'test': False}): + with patch.dict(win_path.__opts__, {"test": False}): # Test already absent - with patch.dict(win_path.__salt__, {'win_path.exists': _mock([False])}): + with patch.dict(win_path.__salt__, {"win_path.exists": _mock([False])}): ret = copy.deepcopy(ret_base) - ret['comment'] = '{0} is not in the PATH'.format(NAME) - ret['result'] = True + ret["comment"] = "{0} is not in the PATH".format(NAME) + ret["result"] = True self.assertDictEqual(win_path.absent(NAME), ret) # Test successful removal - with patch.dict(win_path.__salt__, {'win_path.exists': _mock([True, False])}): + with patch.dict( + win_path.__salt__, {"win_path.exists": _mock([True, False])} + ): ret = copy.deepcopy(ret_base) - ret['comment'] = 'Removed {0} from the PATH'.format(NAME) - ret['changes']['removed'] = NAME - ret['result'] = True + ret["comment"] = "Removed {0} from the PATH".format(NAME) + ret["changes"]["removed"] = NAME + ret["result"] = True self.assertDictEqual(win_path.absent(NAME), ret) # Test unsucessful removal - with patch.dict(win_path.__salt__, {'win_path.exists': _mock([True, True])}): + with patch.dict( + win_path.__salt__, {"win_path.exists": _mock([True, True])} + ): ret = copy.deepcopy(ret_base) - ret['comment'] = 'Failed to remove {0} from the PATH'.format(NAME) - ret['result'] = False + ret["comment"] = "Failed to remove {0} from the PATH".format(NAME) + ret["result"] = False self.assertDictEqual(win_path.absent(NAME), ret) # Test mode ON - with patch.dict(win_path.__opts__, {'test': True}): + with patch.dict(win_path.__opts__, {"test": True}): # Test already absent - with patch.dict(win_path.__salt__, {'win_path.exists': _mock([False])}): + with patch.dict(win_path.__salt__, {"win_path.exists": _mock([False])}): ret = copy.deepcopy(ret_base) - ret['comment'] = '{0} is not in the PATH'.format(NAME) - ret['result'] = True + ret["comment"] = "{0} is not in the PATH".format(NAME) + ret["result"] = True self.assertDictEqual(win_path.absent(NAME), ret) # Test the test-mode return - with patch.dict(win_path.__salt__, {'win_path.exists': _mock([True])}): + with patch.dict(win_path.__salt__, {"win_path.exists": _mock([True])}): ret = copy.deepcopy(ret_base) - ret['comment'] = '{0} would be removed from the PATH'.format(NAME) - ret['result'] = None + ret["comment"] = "{0} would be removed from the PATH".format(NAME) + ret["result"] = None self.assertDictEqual(win_path.absent(NAME), ret) def test_exists_invalid_index(self): - ''' + """ Tests win_path.exists when a non-integer index is specified. - ''' - ret = win_path.exists(NAME, index='foo') + """ + ret = win_path.exists(NAME, index="foo") self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {}, - 'result': False, - 'comment': 'Index must be an integer' - } + "name": NAME, + "changes": {}, + "result": False, + "comment": "Index must be an integer", + }, ) def test_exists_add_no_index_success(self): - ''' + """ Tests win_path.exists when the directory isn't already in the PATH and no index is specified (successful run). - ''' + """ add_mock = MagicMock(return_value=True) rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', 'baz'], - ['foo', 'bar', 'baz', NAME] - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock( + side_effect=[["foo", "bar", "baz"], ["foo", "bar", "baz", NAME]] + ), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': False} + dunder_opts = {"test": False} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME) add_mock.assert_called_once_with(NAME, index=None, rehash=False) @@ -125,32 +127,32 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {'index': {'old': None, 'new': 3}}, - 'result': True, - 'comment': 'Added {0} to the PATH.'.format(NAME) - } + "name": NAME, + "changes": {"index": {"old": None, "new": 3}}, + "result": True, + "comment": "Added {0} to the PATH.".format(NAME), + }, ) def test_exists_add_no_index_failure(self): - ''' + """ Tests win_path.exists when the directory isn't already in the PATH and no index is specified (failed run). - ''' + """ add_mock = MagicMock(return_value=False) rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', 'baz'], - ['foo', 'bar', 'baz'] - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock( + side_effect=[["foo", "bar", "baz"], ["foo", "bar", "baz"]] + ), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': False} + dunder_opts = {"test": False} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME) add_mock.assert_called_once_with(NAME, index=None, rehash=False) @@ -158,32 +160,32 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {}, - 'result': False, - 'comment': 'Failed to add {0} to the PATH.'.format(NAME) - } + "name": NAME, + "changes": {}, + "result": False, + "comment": "Failed to add {0} to the PATH.".format(NAME), + }, ) def test_exists_add_no_index_failure_exception(self): - ''' + """ Tests win_path.exists when the directory isn't already in the PATH and no index is specified (failed run due to exception). - ''' - add_mock = MagicMock(side_effect=Exception('Global Thermonuclear War')) + """ + add_mock = MagicMock(side_effect=Exception("Global Thermonuclear War")) rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', 'baz'], - ['foo', 'bar', 'baz'] - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock( + side_effect=[["foo", "bar", "baz"], ["foo", "bar", "baz"]] + ), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': False} + dunder_opts = {"test": False} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME) add_mock.assert_called_once_with(NAME, index=None, rehash=False) @@ -191,33 +193,33 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {}, - 'result': False, - 'comment': 'Encountered error: Global Thermonuclear War. ' - 'Failed to add {0} to the PATH.'.format(NAME) - } + "name": NAME, + "changes": {}, + "result": False, + "comment": "Encountered error: Global Thermonuclear War. " + "Failed to add {0} to the PATH.".format(NAME), + }, ) def test_exists_change_index_success(self): - ''' + """ Tests win_path.exists when the directory is already in the PATH and needs to be moved to a different position (successful run). - ''' + """ add_mock = MagicMock(return_value=True) rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', 'baz', NAME], - [NAME, 'foo', 'bar', 'baz'] - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock( + side_effect=[["foo", "bar", "baz", NAME], [NAME, "foo", "bar", "baz"]] + ), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': False} + dunder_opts = {"test": False} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME, index=0) add_mock.assert_called_once_with(NAME, index=0, rehash=False) @@ -225,34 +227,34 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {'index': {'old': 3, 'new': 0}}, - 'result': True, - 'comment': 'Moved {0} from index 3 to 0.'.format(NAME) - } + "name": NAME, + "changes": {"index": {"old": 3, "new": 0}}, + "result": True, + "comment": "Moved {0} from index 3 to 0.".format(NAME), + }, ) def test_exists_change_negative_index_success(self): - ''' + """ Tests win_path.exists when the directory is already in the PATH and needs to be moved to a different position (successful run). This tests a negative index. - ''' + """ add_mock = MagicMock(return_value=True) rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', NAME, 'baz'], - ['foo', 'bar', 'baz', NAME] - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock( + side_effect=[["foo", "bar", NAME, "baz"], ["foo", "bar", "baz", NAME]] + ), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': False} + dunder_opts = {"test": False} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME, index=-1) add_mock.assert_called_once_with(NAME, index=-1, rehash=False) @@ -260,32 +262,32 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {'index': {'old': -2, 'new': -1}}, - 'result': True, - 'comment': 'Moved {0} from index -2 to -1.'.format(NAME) - } + "name": NAME, + "changes": {"index": {"old": -2, "new": -1}}, + "result": True, + "comment": "Moved {0} from index -2 to -1.".format(NAME), + }, ) def test_exists_change_index_add_exception(self): - ''' + """ Tests win_path.exists when the directory is already in the PATH but an exception is raised when we attempt to add the key to its new location. - ''' - add_mock = MagicMock(side_effect=Exception('Global Thermonuclear War')) + """ + add_mock = MagicMock(side_effect=Exception("Global Thermonuclear War")) rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', 'baz', NAME], - ['foo', 'bar', 'baz', NAME], - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock( + side_effect=[["foo", "bar", "baz", NAME], ["foo", "bar", "baz", NAME]] + ), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': False} + dunder_opts = {"test": False} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME, index=0) add_mock.assert_called_once_with(NAME, index=0, rehash=False) @@ -293,35 +295,35 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {}, - 'result': False, - 'comment': 'Encountered error: Global Thermonuclear War. ' - 'Failed to move {0} from index 3 to 0.'.format(NAME) - } + "name": NAME, + "changes": {}, + "result": False, + "comment": "Encountered error: Global Thermonuclear War. " + "Failed to move {0} from index 3 to 0.".format(NAME), + }, ) def test_exists_change_negative_index_add_exception(self): - ''' + """ Tests win_path.exists when the directory is already in the PATH but an exception is raised when we attempt to add the key to its new location. This tests a negative index. - ''' - add_mock = MagicMock(side_effect=Exception('Global Thermonuclear War')) + """ + add_mock = MagicMock(side_effect=Exception("Global Thermonuclear War")) rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', NAME, 'baz'], - ['foo', 'bar', NAME, 'baz'], - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock( + side_effect=[["foo", "bar", NAME, "baz"], ["foo", "bar", NAME, "baz"]] + ), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': False} + dunder_opts = {"test": False} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME, index=-1) add_mock.assert_called_once_with(NAME, index=-1, rehash=False) @@ -329,33 +331,33 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {}, - 'result': False, - 'comment': 'Encountered error: Global Thermonuclear War. ' - 'Failed to move {0} from index -2 to -1.'.format(NAME) - } + "name": NAME, + "changes": {}, + "result": False, + "comment": "Encountered error: Global Thermonuclear War. " + "Failed to move {0} from index -2 to -1.".format(NAME), + }, ) def test_exists_change_index_failure(self): - ''' + """ Tests win_path.exists when the directory is already in the PATH and needs to be moved to a different position (failed run). - ''' + """ add_mock = MagicMock(return_value=False) rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', 'baz', NAME], - ['foo', 'bar', 'baz', NAME] - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock( + side_effect=[["foo", "bar", "baz", NAME], ["foo", "bar", "baz", NAME]] + ), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': False} + dunder_opts = {"test": False} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME, index=0) add_mock.assert_called_once_with(NAME, index=0, rehash=False) @@ -363,34 +365,34 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {}, - 'result': False, - 'comment': 'Failed to move {0} from index 3 to 0.'.format(NAME) - } + "name": NAME, + "changes": {}, + "result": False, + "comment": "Failed to move {0} from index 3 to 0.".format(NAME), + }, ) def test_exists_change_negative_index_failure(self): - ''' + """ Tests win_path.exists when the directory is already in the PATH and needs to be moved to a different position (failed run). This tests a negative index. - ''' + """ add_mock = MagicMock(return_value=False) rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', NAME, 'baz'], - ['foo', 'bar', NAME, 'baz'] - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock( + side_effect=[["foo", "bar", NAME, "baz"], ["foo", "bar", NAME, "baz"]] + ), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': False} + dunder_opts = {"test": False} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME, index=-1) add_mock.assert_called_once_with(NAME, index=-1, rehash=False) @@ -398,31 +400,30 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {}, - 'result': False, - 'comment': 'Failed to move {0} from index -2 to -1.'.format(NAME) - } + "name": NAME, + "changes": {}, + "result": False, + "comment": "Failed to move {0} from index -2 to -1.".format(NAME), + }, ) def test_exists_change_index_test_mode(self): - ''' + """ Tests win_path.exists when the directory is already in the PATH and needs to be moved to a different position (test mode enabled). - ''' + """ add_mock = Mock() rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', 'baz', NAME], - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock(side_effect=[["foo", "bar", "baz", NAME]]), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': True} + dunder_opts = {"test": True} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME, index=0) add_mock.assert_not_called() @@ -430,31 +431,30 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {'index': {'old': 3, 'new': 0}}, - 'result': None, - 'comment': '{0} would be moved from index 3 to 0.'.format(NAME) - } + "name": NAME, + "changes": {"index": {"old": 3, "new": 0}}, + "result": None, + "comment": "{0} would be moved from index 3 to 0.".format(NAME), + }, ) def test_exists_change_negative_index_test_mode(self): - ''' + """ Tests win_path.exists when the directory is already in the PATH and needs to be moved to a different position (test mode enabled). - ''' + """ add_mock = Mock() rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[ - ['foo', 'bar', NAME, 'baz'], - ]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock(side_effect=[["foo", "bar", NAME, "baz"]]), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': True} + dunder_opts = {"test": True} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME, index=-1) add_mock.assert_not_called() @@ -462,20 +462,20 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {'index': {'old': -2, 'new': -1}}, - 'result': None, - 'comment': '{0} would be moved from index -2 to -1.'.format(NAME) - } + "name": NAME, + "changes": {"index": {"old": -2, "new": -1}}, + "result": None, + "comment": "{0} would be moved from index -2 to -1.".format(NAME), + }, ) def _test_exists_add_already_present(self, index, test_mode): - ''' + """ Tests win_path.exists when the directory already exists in the PATH. Helper function to test both with and without and index, and with test mode both disabled and enabled. - ''' - current_path = ['foo', 'bar', 'baz'] + """ + current_path = ["foo", "bar", "baz"] if index is None: current_path.append(NAME) else: @@ -485,14 +485,15 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): add_mock = Mock() rehash_mock = MagicMock(return_value=True) dunder_salt = { - 'win_path.get_path': MagicMock(side_effect=[current_path]), - 'win_path.add': add_mock, - 'win_path.rehash': rehash_mock, + "win_path.get_path": MagicMock(side_effect=[current_path]), + "win_path.add": add_mock, + "win_path.rehash": rehash_mock, } - dunder_opts = {'test': test_mode} + dunder_opts = {"test": test_mode} - with patch.dict(win_path.__salt__, dunder_salt), \ - patch.dict(win_path.__opts__, dunder_opts): + with patch.dict(win_path.__salt__, dunder_salt), patch.dict( + win_path.__opts__, dunder_opts + ): ret = win_path.exists(NAME, index=index) add_mock.assert_not_called() @@ -500,14 +501,13 @@ class WinPathTestCase(TestCase, LoaderModuleMockMixin): self.assertDictEqual( ret, { - 'name': NAME, - 'changes': {}, - 'result': True, - 'comment': '{0} already exists in the PATH{1}.'.format( - NAME, - ' at index {0}'.format(index) if index is not None else '' - ) - } + "name": NAME, + "changes": {}, + "result": True, + "comment": "{0} already exists in the PATH{1}.".format( + NAME, " at index {0}".format(index) if index is not None else "" + ), + }, ) def test_exists_add_no_index_already_present(self): diff --git a/tests/unit/states/test_win_pki.py b/tests/unit/states/test_win_pki.py index de0b996eb39..803831dd1c8 100644 --- a/tests/unit/states/test_win_pki.py +++ b/tests/unit/states/test_win_pki.py @@ -1,99 +1,104 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for Windows PKI Module 'state.win_pki' :platform: Windows :maturity: develop .. versionadded:: 2017.7.0 -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_pki as win_pki # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) -CERT_PATH = r'C:\certs\testdomain.local.cer' -THUMBPRINT = '9988776655443322111000AAABBBCCCDDDEEEFFF' -STORE_PATH = r'Cert:\LocalMachine\My' +CERT_PATH = r"C:\certs\testdomain.local.cer" +THUMBPRINT = "9988776655443322111000AAABBBCCCDDDEEEFFF" +STORE_PATH = r"Cert:\LocalMachine\My" CERTS = { THUMBPRINT: { - 'dnsnames': ['testdomain.local'], - 'serialnumber': '0123456789AABBCCDD', - 'subject': 'CN=testdomain.local, OU=testou, O=testorg, S=California, C=US', - 'thumbprint': THUMBPRINT, - 'version': 3 + "dnsnames": ["testdomain.local"], + "serialnumber": "0123456789AABBCCDD", + "subject": "CN=testdomain.local, OU=testou, O=testorg, S=California, C=US", + "thumbprint": THUMBPRINT, + "version": 3, } } class WinPkiTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.win_pki - ''' + """ + def setup_loader_modules(self): return {win_pki: {}} def test_import_cert(self): - ''' + """ Test - Import the certificate file into the given certificate store. - ''' - kwargs = {'name': CERT_PATH} + """ + kwargs = {"name": CERT_PATH} ret = { - 'name': kwargs['name'], - 'changes': { - 'old': None, - 'new': THUMBPRINT - }, - 'comment': "Certificate '{0}' imported into store: {1}".format(THUMBPRINT, STORE_PATH), - 'result': True + "name": kwargs["name"], + "changes": {"old": None, "new": THUMBPRINT}, + "comment": "Certificate '{0}' imported into store: {1}".format( + THUMBPRINT, STORE_PATH + ), + "result": True, } mock_cache_file = MagicMock(return_value=CERT_PATH) mock_certs = MagicMock(return_value={}) mock_cert_file = MagicMock(return_value=CERTS[THUMBPRINT]) mock_import_cert = MagicMock(return_value=True) - with patch.dict(win_pki.__salt__, {'cp.cache_file': mock_cache_file, - 'win_pki.get_certs': mock_certs, - 'win_pki.get_cert_file': mock_cert_file, - 'win_pki.import_cert': mock_import_cert}): - with patch.dict(win_pki.__opts__, {'test': False}): + with patch.dict( + win_pki.__salt__, + { + "cp.cache_file": mock_cache_file, + "win_pki.get_certs": mock_certs, + "win_pki.get_cert_file": mock_cert_file, + "win_pki.import_cert": mock_import_cert, + }, + ): + with patch.dict(win_pki.__opts__, {"test": False}): self.assertEqual(win_pki.import_cert(**kwargs), ret) - with patch.dict(win_pki.__opts__, {'test': True}): - ret['comment'] = ("Certificate '{0}' will be imported into store:" - " {1}").format(THUMBPRINT, STORE_PATH) - ret['result'] = None + with patch.dict(win_pki.__opts__, {"test": True}): + ret["comment"] = ( + "Certificate '{0}' will be imported into store:" " {1}" + ).format(THUMBPRINT, STORE_PATH) + ret["result"] = None self.assertEqual(win_pki.import_cert(**kwargs), ret) def test_remove_cert(self): - ''' + """ Test - Remove the certificate from the given certificate store. - ''' - kwargs = {'name': 'remove-cert', 'thumbprint': THUMBPRINT} + """ + kwargs = {"name": "remove-cert", "thumbprint": THUMBPRINT} ret = { - 'name': kwargs['name'], - 'changes': { - 'old': kwargs['thumbprint'], - 'new': None - }, - 'comment': "Certificate '{0}' removed from store: {1}".format(kwargs['thumbprint'], STORE_PATH), - 'result': True + "name": kwargs["name"], + "changes": {"old": kwargs["thumbprint"], "new": None}, + "comment": "Certificate '{0}' removed from store: {1}".format( + kwargs["thumbprint"], STORE_PATH + ), + "result": True, } mock_certs = MagicMock(return_value=CERTS) mock_remove_cert = MagicMock(return_value=True) - with patch.dict(win_pki.__salt__, {'win_pki.get_certs': mock_certs, - 'win_pki.remove_cert': mock_remove_cert}): - with patch.dict(win_pki.__opts__, {'test': False}): + with patch.dict( + win_pki.__salt__, + {"win_pki.get_certs": mock_certs, "win_pki.remove_cert": mock_remove_cert}, + ): + with patch.dict(win_pki.__opts__, {"test": False}): self.assertEqual(win_pki.remove_cert(**kwargs), ret) - with patch.dict(win_pki.__opts__, {'test': True}): - ret['comment'] = ("Certificate '{0}' will be removed from store:" - " {1}").format(kwargs['thumbprint'], STORE_PATH) - ret['result'] = None + with patch.dict(win_pki.__opts__, {"test": True}): + ret["comment"] = ( + "Certificate '{0}' will be removed from store:" " {1}" + ).format(kwargs["thumbprint"], STORE_PATH) + ret["result"] = None self.assertEqual(win_pki.remove_cert(**kwargs), ret) diff --git a/tests/unit/states/test_win_powercfg.py b/tests/unit/states/test_win_powercfg.py index 083a2c61876..575a2ec84a8 100644 --- a/tests/unit/states/test_win_powercfg.py +++ b/tests/unit/states/test_win_powercfg.py @@ -1,94 +1,103 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_powercfg as powercfg # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch -) class PowerCfgTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the powercfg state - ''' + """ + def setup_loader_modules(self): return {powercfg: {}} def test_set_monitor(self): - ''' + """ Test to make sure we can set the monitor timeout value - ''' + """ ret = { - 'changes': { - 'monitor': { - 'ac': { - 'new': 0, - 'old': 45 - } - } + "changes": {"monitor": {"ac": {"new": 0, "old": 45}}}, + "comment": "Monitor timeout on AC power set to 0", + "name": "monitor", + "result": True, + } + get_monitor_side_effect = MagicMock( + side_effect=[{"ac": 45, "dc": 22}, {"ac": 0, "dc": 22}] + ) + with patch.dict( + powercfg.__salt__, + { + "powercfg.get_monitor_timeout": get_monitor_side_effect, + "powercfg.set_monitor_timeout": MagicMock(return_value=True), }, - 'comment': 'Monitor timeout on AC power set to 0', - 'name': 'monitor', - 'result': True} - get_monitor_side_effect = MagicMock(side_effect=[{"ac": 45, "dc": 22}, - {"ac": 0, "dc": 22}]) - with patch.dict(powercfg.__salt__, - {"powercfg.get_monitor_timeout": get_monitor_side_effect, - "powercfg.set_monitor_timeout": MagicMock(return_value=True)}): + ): with patch.dict(powercfg.__opts__, {"test": False}): self.assertEqual(powercfg.set_timeout("monitor", 0), ret) def test_set_monitor_already_set(self): - ''' + """ Test to make sure we can set the monitor timeout value - ''' - ret = {'changes': {}, - 'comment': 'Monitor timeout on AC power is already set to 0', - 'name': 'monitor', - 'result': True} + """ + ret = { + "changes": {}, + "comment": "Monitor timeout on AC power is already set to 0", + "name": "monitor", + "result": True, + } monitor_val = MagicMock(return_value={"ac": 0, "dc": 0}) - with patch.dict(powercfg.__salt__, {"powercfg.get_monitor_timeout": monitor_val}): + with patch.dict( + powercfg.__salt__, {"powercfg.get_monitor_timeout": monitor_val} + ): self.assertEqual(powercfg.set_timeout("monitor", 0), ret) def test_set_monitor_test_true_with_change(self): - ''' + """ Test to make sure set monitor works correctly with test=True with changes - ''' - ret = {'changes': {}, - 'comment': 'Monitor timeout on AC power will be set to 0', - 'name': 'monitor', - 'result': None} + """ + ret = { + "changes": {}, + "comment": "Monitor timeout on AC power will be set to 0", + "name": "monitor", + "result": None, + } get_monitor_return_value = MagicMock(return_value={"ac": 45, "dc": 22}) - with patch.dict(powercfg.__salt__, {"powercfg.get_monitor_timeout": get_monitor_return_value}): + with patch.dict( + powercfg.__salt__, + {"powercfg.get_monitor_timeout": get_monitor_return_value}, + ): with patch.dict(powercfg.__opts__, {"test": True}): self.assertEqual(powercfg.set_timeout("monitor", 0), ret) def test_fail_invalid_setting(self): - ''' + """ Test to make sure we can set the monitor timeout value - ''' - ret = {'changes': {}, - 'comment': '"fakesetting" is not a valid setting', - 'name': 'fakesetting', - 'result': False} + """ + ret = { + "changes": {}, + "comment": '"fakesetting" is not a valid setting', + "name": "fakesetting", + "result": False, + } self.assertEqual(powercfg.set_timeout("fakesetting", 0), ret) def test_fail_invalid_power(self): - ''' + """ Test to make sure we can set the monitor timeout value - ''' - ret = {'changes': {}, - 'comment': '"fakepower" is not a power type', - 'name': 'monitor', - 'result': False} - self.assertEqual(powercfg.set_timeout("monitor", 0, power="fakepower"), - ret) + """ + ret = { + "changes": {}, + "comment": '"fakepower" is not a power type', + "name": "monitor", + "result": False, + } + self.assertEqual(powercfg.set_timeout("monitor", 0, power="fakepower"), ret) diff --git a/tests/unit/states/test_win_servermanager.py b/tests/unit/states/test_win_servermanager.py index c14b03fc99a..6a36f2d6b71 100644 --- a/tests/unit/states/test_win_servermanager.py +++ b/tests/unit/states/test_win_servermanager.py @@ -1,130 +1,149 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_servermanager as win_servermanager +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class WinServermanagerTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the win_servermanager state - ''' + """ + def setup_loader_modules(self): return {win_servermanager: {}} def test_installed(self): - ''' + """ Test to install the windows feature - ''' + """ mock_list = MagicMock( - side_effect=[{'spongebob': 'squarepants'}, - {'squidward': 'patrick'}, - {'spongebob': 'squarepants'}, - {'spongebob': 'squarepants', - 'squidward': 'patrick'}]) + side_effect=[ + {"spongebob": "squarepants"}, + {"squidward": "patrick"}, + {"spongebob": "squarepants"}, + {"spongebob": "squarepants", "squidward": "patrick"}, + ] + ) mock_install = MagicMock( - return_value={'Success': True, - 'Restarted': False, - 'RestartNeeded': False, - 'ExitCode': 1234, - 'Features': { - 'squidward': { - 'DisplayName': 'Squidward', - 'Message': '', - 'RestartNeeded': True, - 'SkipReason': 0, - 'Success': True - } - }}) - with patch.dict(win_servermanager.__salt__, - {"win_servermanager.list_installed": mock_list, - "win_servermanager.install": mock_install}): - ret = {'name': 'spongebob', - 'changes': {}, - 'result': True, - 'comment': 'The following features are already installed:\n' - '- spongebob'} - self.assertDictEqual(win_servermanager.installed('spongebob'), ret) + return_value={ + "Success": True, + "Restarted": False, + "RestartNeeded": False, + "ExitCode": 1234, + "Features": { + "squidward": { + "DisplayName": "Squidward", + "Message": "", + "RestartNeeded": True, + "SkipReason": 0, + "Success": True, + } + }, + } + ) + with patch.dict( + win_servermanager.__salt__, + { + "win_servermanager.list_installed": mock_list, + "win_servermanager.install": mock_install, + }, + ): + ret = { + "name": "spongebob", + "changes": {}, + "result": True, + "comment": "The following features are already installed:\n" + "- spongebob", + } + self.assertDictEqual(win_servermanager.installed("spongebob"), ret) with patch.dict(win_servermanager.__opts__, {"test": True}): - ret = {'name': 'spongebob', - 'result': None, - 'comment': '', - 'changes': { - 'spongebob': 'Will be installed recurse=False'}} - self.assertDictEqual( - win_servermanager.installed('spongebob'), ret) + ret = { + "name": "spongebob", + "result": None, + "comment": "", + "changes": {"spongebob": "Will be installed recurse=False"}, + } + self.assertDictEqual(win_servermanager.installed("spongebob"), ret) with patch.dict(win_servermanager.__opts__, {"test": False}): - ret = {'name': 'squidward', - 'result': True, - 'comment': 'Installed the following:\n- squidward', - 'changes': { - 'squidward': {'new': 'patrick', 'old': ''}}} - self.assertDictEqual( - win_servermanager.installed('squidward'), ret) + ret = { + "name": "squidward", + "result": True, + "comment": "Installed the following:\n- squidward", + "changes": {"squidward": {"new": "patrick", "old": ""}}, + } + self.assertDictEqual(win_servermanager.installed("squidward"), ret) def test_removed(self): - ''' + """ Test to remove the windows feature - ''' + """ mock_list = MagicMock( - side_effect=[{'spongebob': 'squarepants'}, - {'squidward': 'patrick'}, - {'spongebob': 'squarepants', - 'squidward': 'patrick'}, - {'spongebob': 'squarepants'}]) + side_effect=[ + {"spongebob": "squarepants"}, + {"squidward": "patrick"}, + {"spongebob": "squarepants", "squidward": "patrick"}, + {"spongebob": "squarepants"}, + ] + ) mock_remove = MagicMock( - return_value={'Success': True, - 'RestartNeeded': False, - 'Restarted': False, - 'ExitCode': 1234, - 'Features': { - 'squidward': { - 'DisplayName': 'Squidward', - 'Message': '', - 'RestartNeeded': True, - 'SkipReason': 0, - 'Success': True - } - }}) - with patch.dict(win_servermanager.__salt__, - {"win_servermanager.list_installed": mock_list, - "win_servermanager.remove": mock_remove}): - ret = {'name': 'squidward', - 'changes': {}, - 'result': True, - 'comment': 'The following features are not installed:\n' - '- squidward'} - self.assertDictEqual( - win_servermanager.removed('squidward'), ret) + return_value={ + "Success": True, + "RestartNeeded": False, + "Restarted": False, + "ExitCode": 1234, + "Features": { + "squidward": { + "DisplayName": "Squidward", + "Message": "", + "RestartNeeded": True, + "SkipReason": 0, + "Success": True, + } + }, + } + ) + with patch.dict( + win_servermanager.__salt__, + { + "win_servermanager.list_installed": mock_list, + "win_servermanager.remove": mock_remove, + }, + ): + ret = { + "name": "squidward", + "changes": {}, + "result": True, + "comment": "The following features are not installed:\n" "- squidward", + } + self.assertDictEqual(win_servermanager.removed("squidward"), ret) with patch.dict(win_servermanager.__opts__, {"test": True}): - ret = {'name': 'squidward', - 'result': None, - 'comment': '', - 'changes': {'squidward': 'Will be removed'}} - self.assertDictEqual( - win_servermanager.removed('squidward'), ret) + ret = { + "name": "squidward", + "result": None, + "comment": "", + "changes": {"squidward": "Will be removed"}, + } + self.assertDictEqual(win_servermanager.removed("squidward"), ret) with patch.dict(win_servermanager.__opts__, {"test": False}): - ret = {'name': 'squidward', - 'result': True, - 'comment': 'Removed the following:\n- squidward', - 'changes': { - 'squidward': {'new': '', 'old': 'patrick'}}} - self.assertDictEqual( - win_servermanager.removed('squidward'), ret) + ret = { + "name": "squidward", + "result": True, + "comment": "Removed the following:\n- squidward", + "changes": {"squidward": {"new": "", "old": "patrick"}}, + } + self.assertDictEqual(win_servermanager.removed("squidward"), ret) diff --git a/tests/unit/states/test_win_snmp.py b/tests/unit/states/test_win_snmp.py index a62d12eeeed..ec843af6be1 100644 --- a/tests/unit/states/test_win_snmp.py +++ b/tests/unit/states/test_win_snmp.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- -''' +""" :synopsis: Unit Tests for Windows SNMP Module 'state.win_snmp' :platform: Windows :maturity: develop .. versionadded:: 2017.7.0 -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_snmp as win_snmp @@ -15,80 +15,99 @@ from salt.ext import six # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class WinSnmpTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.win_snmp - ''' + """ + def setup_loader_modules(self): return {win_snmp: {}} def test_agent_settings(self): - ''' + """ Test - Manage the SNMP sysContact, sysLocation, and sysServices settings. - ''' - kwargs = {'name': 'agent-settings', 'contact': 'TestContact', - 'location': 'TestLocation', 'services': ['Internet']} + """ + kwargs = { + "name": "agent-settings", + "contact": "TestContact", + "location": "TestLocation", + "services": ["Internet"], + } ret = { - 'name': kwargs['name'], - 'changes': {}, - 'comment': 'Agent settings already contain the provided values.', - 'result': True + "name": kwargs["name"], + "changes": {}, + "comment": "Agent settings already contain the provided values.", + "result": True, } # Using this instead of dictionary comprehension in order to make pylint happy. - get_ret = dict((key, value) for (key, value) in six.iteritems(kwargs) if key != 'name') + get_ret = dict( + (key, value) for (key, value) in six.iteritems(kwargs) if key != "name" + ) mock_value_get = MagicMock(return_value=get_ret) mock_value_set = MagicMock(return_value=True) - with patch.dict(win_snmp.__salt__, {'win_snmp.get_agent_settings': mock_value_get, - 'win_snmp.set_agent_settings': mock_value_set}): - with patch.dict(win_snmp.__opts__, {'test': False}): + with patch.dict( + win_snmp.__salt__, + { + "win_snmp.get_agent_settings": mock_value_get, + "win_snmp.set_agent_settings": mock_value_set, + }, + ): + with patch.dict(win_snmp.__opts__, {"test": False}): self.assertEqual(win_snmp.agent_settings(**kwargs), ret) def test_auth_traps_enabled(self): - ''' + """ Test - Manage the sending of authentication traps. - ''' - kwargs = {'name': 'auth-traps', 'status': True} + """ + kwargs = {"name": "auth-traps", "status": True} ret = { - 'name': kwargs['name'], - 'changes': { - 'old': False, - 'new': True - }, - 'comment': 'Set EnableAuthenticationTraps to contain the provided value.', - 'result': True + "name": kwargs["name"], + "changes": {"old": False, "new": True}, + "comment": "Set EnableAuthenticationTraps to contain the provided value.", + "result": True, } mock_value_get = MagicMock(return_value=False) mock_value_set = MagicMock(return_value=True) - with patch.dict(win_snmp.__salt__, {'win_snmp.get_auth_traps_enabled': mock_value_get, - 'win_snmp.set_auth_traps_enabled': mock_value_set}): - with patch.dict(win_snmp.__opts__, {'test': False}): + with patch.dict( + win_snmp.__salt__, + { + "win_snmp.get_auth_traps_enabled": mock_value_get, + "win_snmp.set_auth_traps_enabled": mock_value_set, + }, + ): + with patch.dict(win_snmp.__opts__, {"test": False}): self.assertEqual(win_snmp.auth_traps_enabled(**kwargs), ret) - with patch.dict(win_snmp.__opts__, {'test': True}): - ret['comment'] = 'EnableAuthenticationTraps will be changed.' - ret['result'] = None + with patch.dict(win_snmp.__opts__, {"test": True}): + ret["comment"] = "EnableAuthenticationTraps will be changed." + ret["result"] = None self.assertEqual(win_snmp.auth_traps_enabled(**kwargs), ret) def test_community_names(self): - ''' + """ Test - Manage the SNMP accepted community names and their permissions. - ''' - kwargs = {'name': 'community-names', 'communities': {'TestCommunity': 'Read Create'}} - ret = { - 'name': kwargs['name'], - 'changes': {}, - 'comment': 'Communities already contain the provided values.', - 'result': True + """ + kwargs = { + "name": "community-names", + "communities": {"TestCommunity": "Read Create"}, } - mock_value_get = MagicMock(return_value=kwargs['communities']) + ret = { + "name": kwargs["name"], + "changes": {}, + "comment": "Communities already contain the provided values.", + "result": True, + } + mock_value_get = MagicMock(return_value=kwargs["communities"]) mock_value_set = MagicMock(return_value=True) - with patch.dict(win_snmp.__salt__, {'win_snmp.get_community_names': mock_value_get, - 'win_snmp.set_community_names': mock_value_set}): - with patch.dict(win_snmp.__opts__, {'test': False}): + with patch.dict( + win_snmp.__salt__, + { + "win_snmp.get_community_names": mock_value_get, + "win_snmp.set_community_names": mock_value_set, + }, + ): + with patch.dict(win_snmp.__opts__, {"test": False}): self.assertEqual(win_snmp.community_names(**kwargs), ret) diff --git a/tests/unit/states/test_win_system.py b/tests/unit/states/test_win_system.py index 83561858131..2105eee56b7 100644 --- a/tests/unit/states/test_win_system.py +++ b/tests/unit/states/test_win_system.py @@ -1,131 +1,154 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_system as win_system +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class WinSystemTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the win_system state - ''' + """ + def setup_loader_modules(self): return {win_system: {}} def test_computer_desc(self): - ''' + """ Test to manage the computer's description field - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} - mock = MagicMock(side_effect=['salt', 'stack', 'stack']) - with patch.dict(win_system.__salt__, - {"system.get_computer_desc": mock}): - ret.update({'comment': "Computer description" - " already set to 'salt'"}) - self.assertDictEqual(win_system.computer_desc('salt'), ret) + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + mock = MagicMock(side_effect=["salt", "stack", "stack"]) + with patch.dict(win_system.__salt__, {"system.get_computer_desc": mock}): + ret.update({"comment": "Computer description" " already set to 'salt'"}) + self.assertDictEqual(win_system.computer_desc("salt"), ret) with patch.dict(win_system.__opts__, {"test": True}): - ret.update({'result': None, 'comment': "Computer description" - " will be changed to 'salt'"}) - self.assertDictEqual(win_system.computer_desc('salt'), ret) + ret.update( + { + "result": None, + "comment": "Computer description" " will be changed to 'salt'", + } + ) + self.assertDictEqual(win_system.computer_desc("salt"), ret) with patch.dict(win_system.__opts__, {"test": False}): - mock = MagicMock(return_value={'Computer Description': 'nfs'}) - with patch.dict(win_system.__salt__, - {"system.set_computer_desc": mock}): - ret.update({'result': False, 'comment': "Unable to set" - " computer description to 'salt'"}) - self.assertDictEqual(win_system.computer_desc('salt'), ret) + mock = MagicMock(return_value={"Computer Description": "nfs"}) + with patch.dict( + win_system.__salt__, {"system.set_computer_desc": mock} + ): + ret.update( + { + "result": False, + "comment": "Unable to set" + " computer description to 'salt'", + } + ) + self.assertDictEqual(win_system.computer_desc("salt"), ret) def test_computer_name(self): - ''' + """ Test to manage the computer's name - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} - mock = MagicMock(return_value='salt') - with patch.dict(win_system.__salt__, - {"system.get_computer_name": mock}): - mock = MagicMock(side_effect=[None, 'SALT', 'Stack', 'stack']) - with patch.dict(win_system.__salt__, - {"system.get_pending_computer_name": mock}): - ret.update({'comment': "Computer name already set to 'salt'"}) - self.assertDictEqual(win_system.computer_name('salt'), ret) + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} + mock = MagicMock(return_value="salt") + with patch.dict(win_system.__salt__, {"system.get_computer_name": mock}): + mock = MagicMock(side_effect=[None, "SALT", "Stack", "stack"]) + with patch.dict( + win_system.__salt__, {"system.get_pending_computer_name": mock} + ): + ret.update({"comment": "Computer name already set to 'salt'"}) + self.assertDictEqual(win_system.computer_name("salt"), ret) - ret.update({'comment': "The current computer name" - " is 'salt', but will be changed to 'salt' on" - " the next reboot"}) - self.assertDictEqual(win_system.computer_name('salt'), ret) + ret.update( + { + "comment": "The current computer name" + " is 'salt', but will be changed to 'salt' on" + " the next reboot" + } + ) + self.assertDictEqual(win_system.computer_name("salt"), ret) with patch.dict(win_system.__opts__, {"test": True}): - ret.update({'result': None, 'comment': "Computer name will" - " be changed to 'salt'"}) - self.assertDictEqual(win_system.computer_name('salt'), ret) + ret.update( + { + "result": None, + "comment": "Computer name will" " be changed to 'salt'", + } + ) + self.assertDictEqual(win_system.computer_name("salt"), ret) with patch.dict(win_system.__opts__, {"test": False}): mock = MagicMock(return_value=False) - with patch.dict(win_system.__salt__, - {"system.set_computer_name": mock}): - ret.update({'comment': "Unable to set computer name" - " to 'salt'", 'result': False}) - self.assertDictEqual(win_system.computer_name('salt'), - ret) + with patch.dict( + win_system.__salt__, {"system.set_computer_name": mock} + ): + ret.update( + { + "comment": "Unable to set computer name" " to 'salt'", + "result": False, + } + ) + self.assertDictEqual(win_system.computer_name("salt"), ret) def test_hostname(self): - ret = { - 'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': '' - } + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} - mock = MagicMock(return_value='minion') + mock = MagicMock(return_value="minion") with patch.dict(win_system.__salt__, {"system.get_hostname": mock}): mock = MagicMock(return_value=True) with patch.dict(win_system.__salt__, {"system.set_hostname": mock}): - ret.update({'comment': "The current hostname is 'minion', " - "but will be changed to 'salt' on the next reboot", - 'changes': {'hostname': 'salt'}}) - self.assertDictEqual(win_system.hostname('salt'), ret) + ret.update( + { + "comment": "The current hostname is 'minion', " + "but will be changed to 'salt' on the next reboot", + "changes": {"hostname": "salt"}, + } + ) + self.assertDictEqual(win_system.hostname("salt"), ret) mock = MagicMock(return_value=False) with patch.dict(win_system.__salt__, {"system.set_hostname": mock}): - ret.update({'comment': "Unable to set hostname", - 'changes': {}, - 'result': False}) - self.assertDictEqual(win_system.hostname('salt'), ret) + ret.update( + { + "comment": "Unable to set hostname", + "changes": {}, + "result": False, + } + ) + self.assertDictEqual(win_system.hostname("salt"), ret) - mock = MagicMock(return_value='salt') + mock = MagicMock(return_value="salt") with patch.dict(win_system.__salt__, {"system.get_hostname": mock}): - ret.update({'comment': "Hostname is already set to 'salt'", - 'changes': {}, - 'result': True}) + ret.update( + { + "comment": "Hostname is already set to 'salt'", + "changes": {}, + "result": True, + } + ) - self.assertDictEqual(win_system.hostname('salt'), ret) + self.assertDictEqual(win_system.hostname("salt"), ret) - mock = MagicMock(return_value='salt') + mock = MagicMock(return_value="salt") with patch.dict(win_system.__salt__, {"system.get_hostname": mock}): - ret.update({'name': 'SALT', - 'comment': "Hostname is already set to 'SALT'", - 'changes': {}, - 'result': True}) + ret.update( + { + "name": "SALT", + "comment": "Hostname is already set to 'SALT'", + "changes": {}, + "result": True, + } + ) - self.assertDictEqual(win_system.hostname('SALT'), ret) + self.assertDictEqual(win_system.hostname("SALT"), ret) diff --git a/tests/unit/states/test_win_wusa.py b/tests/unit/states/test_win_wusa.py index 934243c4abe..475f29c75ef 100644 --- a/tests/unit/states/test_win_wusa.py +++ b/tests/unit/states/test_win_wusa.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.win_wusa as wusa @@ -9,161 +9,203 @@ from salt.exceptions import SaltInvocationError # Import Salt Testing Libs from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class WinWusaTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ test the function in the win_wusa state module - ''' - kb = 'KB123456' + """ + + kb = "KB123456" def setup_loader_modules(self): - return {wusa: {'__opts__': {'test': False}, - '__env__': 'base'}} + return {wusa: {"__opts__": {"test": False}, "__env__": "base"}} def test_installed_no_source(self): - ''' + """ test wusa.installed without passing source - ''' + """ with self.assertRaises(SaltInvocationError) as excinfo: - wusa.installed(name='KB123456', source=None) + wusa.installed(name="KB123456", source=None) - self.assertEqual(excinfo.exception.strerror, - 'Must specify a "source" file to install') + self.assertEqual( + excinfo.exception.strerror, 'Must specify a "source" file to install' + ) def test_installed_existing(self): - ''' + """ test wusa.installed when the kb is already installed - ''' + """ mock_installed = MagicMock(return_value=True) - with patch.dict(wusa.__salt__, {'wusa.is_installed': mock_installed}): - returned = wusa.installed(name=self.kb, - source='salt://{0}.msu'.format(self.kb)) - expected = {'changes': {}, - 'comment': '{0} already installed'.format(self.kb), - 'name': self.kb, - 'result': True} + with patch.dict(wusa.__salt__, {"wusa.is_installed": mock_installed}): + returned = wusa.installed( + name=self.kb, source="salt://{0}.msu".format(self.kb) + ) + expected = { + "changes": {}, + "comment": "{0} already installed".format(self.kb), + "name": self.kb, + "result": True, + } self.assertDictEqual(expected, returned) def test_installed_test_true(self): - ''' + """ test wusa.installed with test=True - ''' + """ mock_installed = MagicMock(return_value=False) - with patch.dict(wusa.__salt__, {'wusa.is_installed': mock_installed}), \ - patch.dict(wusa.__opts__, {'test': True}): - returned = wusa.installed(name=self.kb, - source='salt://{0}.msu'.format(self.kb)) - expected = {'changes': {}, - 'comment': '{0} would be installed'.format(self.kb), - 'name': self.kb, - 'result': None} + with patch.dict( + wusa.__salt__, {"wusa.is_installed": mock_installed} + ), patch.dict(wusa.__opts__, {"test": True}): + returned = wusa.installed( + name=self.kb, source="salt://{0}.msu".format(self.kb) + ) + expected = { + "changes": {}, + "comment": "{0} would be installed".format(self.kb), + "name": self.kb, + "result": None, + } self.assertDictEqual(expected, returned) def test_installed_cache_fail(self): - ''' + """ test wusa.install when it fails to cache the file - ''' + """ mock_installed = MagicMock(return_value=False) - mock_cache = MagicMock(return_value='') - with patch.dict(wusa.__salt__, {'wusa.is_installed': mock_installed, - 'cp.cache_file': mock_cache}): - returned = wusa.installed(name=self.kb, - source='salt://{0}.msu'.format(self.kb)) - expected = {'changes': {}, - 'comment': 'Unable to cache salt://{0}.msu from ' - 'saltenv "base"'.format(self.kb), - 'name': self.kb, - 'result': False} + mock_cache = MagicMock(return_value="") + with patch.dict( + wusa.__salt__, + {"wusa.is_installed": mock_installed, "cp.cache_file": mock_cache}, + ): + returned = wusa.installed( + name=self.kb, source="salt://{0}.msu".format(self.kb) + ) + expected = { + "changes": {}, + "comment": "Unable to cache salt://{0}.msu from " + 'saltenv "base"'.format(self.kb), + "name": self.kb, + "result": False, + } self.assertDictEqual(expected, returned) def test_installed(self): - ''' + """ test wusa.installed assuming success - ''' + """ mock_installed = MagicMock(side_effect=[False, True]) - mock_cache = MagicMock(return_value='C:\\{0}.msu'.format(self.kb)) - with patch.dict(wusa.__salt__, {'wusa.is_installed': mock_installed, - 'cp.cache_file': mock_cache, - 'wusa.install': MagicMock()}): - returned = wusa.installed(name=self.kb, - source='salt://{0}.msu'.format(self.kb)) - expected = {'changes': {'new': True, 'old': False}, - 'comment': '{0} was installed. '.format(self.kb), - 'name': self.kb, - 'result': True} + mock_cache = MagicMock(return_value="C:\\{0}.msu".format(self.kb)) + with patch.dict( + wusa.__salt__, + { + "wusa.is_installed": mock_installed, + "cp.cache_file": mock_cache, + "wusa.install": MagicMock(), + }, + ): + returned = wusa.installed( + name=self.kb, source="salt://{0}.msu".format(self.kb) + ) + expected = { + "changes": {"new": True, "old": False}, + "comment": "{0} was installed. ".format(self.kb), + "name": self.kb, + "result": True, + } self.assertDictEqual(expected, returned) def test_installed_failed(self): - ''' + """ test wusa.installed with a failure - ''' + """ mock_installed = MagicMock(side_effect=[False, False]) - mock_cache = MagicMock(return_value='C:\\{0}.msu'.format(self.kb)) - with patch.dict(wusa.__salt__, {'wusa.is_installed': mock_installed, - 'cp.cache_file': mock_cache, - 'wusa.install': MagicMock()}): - returned = wusa.installed(name=self.kb, - source='salt://{0}.msu'.format(self.kb)) - expected = {'changes': {}, - 'comment': '{0} failed to install. '.format(self.kb), - 'name': self.kb, - 'result': False} + mock_cache = MagicMock(return_value="C:\\{0}.msu".format(self.kb)) + with patch.dict( + wusa.__salt__, + { + "wusa.is_installed": mock_installed, + "cp.cache_file": mock_cache, + "wusa.install": MagicMock(), + }, + ): + returned = wusa.installed( + name=self.kb, source="salt://{0}.msu".format(self.kb) + ) + expected = { + "changes": {}, + "comment": "{0} failed to install. ".format(self.kb), + "name": self.kb, + "result": False, + } self.assertDictEqual(expected, returned) def test_uninstalled_non_existing(self): - ''' + """ test wusa.uninstalled when the kb is not installed - ''' + """ mock_installed = MagicMock(return_value=False) - with patch.dict(wusa.__salt__, {'wusa.is_installed': mock_installed}): + with patch.dict(wusa.__salt__, {"wusa.is_installed": mock_installed}): returned = wusa.uninstalled(name=self.kb) - expected = {'changes': {}, - 'comment': '{0} already uninstalled'.format(self.kb), - 'name': self.kb, - 'result': True} + expected = { + "changes": {}, + "comment": "{0} already uninstalled".format(self.kb), + "name": self.kb, + "result": True, + } self.assertDictEqual(expected, returned) def test_uninstalled_test_true(self): - ''' + """ test wusa.uninstalled with test=True - ''' + """ mock_installed = MagicMock(return_value=True) - with patch.dict(wusa.__salt__, {'wusa.is_installed': mock_installed}), \ - patch.dict(wusa.__opts__, {'test': True}): + with patch.dict( + wusa.__salt__, {"wusa.is_installed": mock_installed} + ), patch.dict(wusa.__opts__, {"test": True}): returned = wusa.uninstalled(name=self.kb) - expected = {'changes': {}, - 'comment': '{0} would be uninstalled'.format(self.kb), - 'name': self.kb, - 'result': None} + expected = { + "changes": {}, + "comment": "{0} would be uninstalled".format(self.kb), + "name": self.kb, + "result": None, + } self.assertDictEqual(expected, returned) def test_uninstalled(self): - ''' + """ test wusa.uninstalled assuming success - ''' + """ mock_installed = MagicMock(side_effect=[True, False]) - with patch.dict(wusa.__salt__, {'wusa.is_installed': mock_installed, - 'wusa.uninstall': MagicMock()}): + with patch.dict( + wusa.__salt__, + {"wusa.is_installed": mock_installed, "wusa.uninstall": MagicMock()}, + ): returned = wusa.uninstalled(name=self.kb) - expected = {'changes': {'new': False, 'old': True}, - 'comment': '{0} was uninstalled'.format(self.kb), - 'name': self.kb, - 'result': True} + expected = { + "changes": {"new": False, "old": True}, + "comment": "{0} was uninstalled".format(self.kb), + "name": self.kb, + "result": True, + } self.assertDictEqual(expected, returned) def test_uninstalled_failed(self): - ''' + """ test wusa.uninstalled with a failure - ''' + """ mock_installed = MagicMock(side_effect=[True, True]) - with patch.dict(wusa.__salt__, {'wusa.is_installed': mock_installed, - 'wusa.uninstall': MagicMock()}): + with patch.dict( + wusa.__salt__, + {"wusa.is_installed": mock_installed, "wusa.uninstall": MagicMock()}, + ): returned = wusa.uninstalled(name=self.kb) - expected = {'changes': {}, - 'comment': '{0} failed to uninstall'.format(self.kb), - 'name': self.kb, - 'result': False} + expected = { + "changes": {}, + "comment": "{0} failed to uninstall".format(self.kb), + "name": self.kb, + "result": False, + } self.assertDictEqual(expected, returned) diff --git a/tests/unit/states/test_winrepo.py b/tests/unit/states/test_winrepo.py index 729b242d4c0..dad269946fe 100644 --- a/tests/unit/states/test_winrepo.py +++ b/tests/unit/states/test_winrepo.py @@ -1,115 +1,122 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +import os # Import Salt Libs import salt.config +import salt.states.winrepo as winrepo import salt.utils.path from salt.syspaths import BASE_FILE_ROOTS_DIR -import salt.states.winrepo as winrepo + +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class MockRunnerClient(object): - ''' + """ Mock RunnerClient class - ''' + """ + def __init__(self): pass class RunnerClient(object): - ''' + """ Mock RunnerClient class - ''' + """ + def __init__(self, master_config): - ''' + """ init method - ''' + """ @staticmethod def cmd(arg1, arg2): - ''' + """ Mock cmd method - ''' + """ # TODO: Figure out how to have this return an empty dict or a dict # TODO: with expected data return [] class WinrepoTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the winrepo state - ''' + """ + def setup_loader_modules(self): - patcher = patch('salt.states.winrepo.salt.runner', MockRunnerClient) + patcher = patch("salt.states.winrepo.salt.runner", MockRunnerClient) patcher.start() self.addCleanup(patcher.stop) return {winrepo: {}} def test_genrepo(self): - ''' + """ Test to refresh the winrepo.p file of the repository - ''' - expected = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': ''} - mock_config = MagicMock(return_value={'winrepo_dir': 'salt', - 'winrepo_cachefile': 'abc'}) + """ + expected = {"name": "salt", "changes": {}, "result": False, "comment": ""} + mock_config = MagicMock( + return_value={"winrepo_dir": "salt", "winrepo_cachefile": "abc"} + ) mock_stat = MagicMock(return_value=[0, 1, 2, 3, 4, 5, 6, 7, 8]) mock_empty_list = MagicMock(return_value=[]) - with patch.object(salt.config, 'master_config', mock_config), \ - patch.object(os, 'stat', mock_stat), \ - patch.object(salt.utils.path, 'os_walk', mock_empty_list), \ - patch.dict(winrepo.__opts__, {'test': True}): + with patch.object(salt.config, "master_config", mock_config), patch.object( + os, "stat", mock_stat + ), patch.object(salt.utils.path, "os_walk", mock_empty_list), patch.dict( + winrepo.__opts__, {"test": True} + ): # With test=True - expected.update({'comment': '', 'result': None}) - self.assertDictEqual(winrepo.genrepo('salt'), expected) + expected.update({"comment": "", "result": None}) + self.assertDictEqual(winrepo.genrepo("salt"), expected) - with patch.dict(winrepo.__opts__, {'test': False}): + with patch.dict(winrepo.__opts__, {"test": False}): # With test=False - expected.update({'result': True}) - self.assertDictEqual(winrepo.genrepo('salt'), expected) + expected.update({"result": True}) + self.assertDictEqual(winrepo.genrepo("salt"), expected) # Now with no changes, existing winrepo.p - expected.update({'changes': {'winrepo': []}}) - self.assertDictEqual(winrepo.genrepo('salt', True), expected) + expected.update({"changes": {"winrepo": []}}) + self.assertDictEqual(winrepo.genrepo("salt", True), expected) def test_genrepo_no_dir(self): - ''' + """ Test genrepo when the dir does not exist - ''' - expected = {'name': 'salt', - 'changes': {}, - 'result': False, - 'comment': '{0} is missing'.format( - os.sep.join([BASE_FILE_ROOTS_DIR, 'win', 'repo']))} - with patch.dict(winrepo.__opts__, {'test': False}), \ - patch('os.path.exists', MagicMock(return_value=False)): - ret = winrepo.genrepo('salt') + """ + expected = { + "name": "salt", + "changes": {}, + "result": False, + "comment": "{0} is missing".format( + os.sep.join([BASE_FILE_ROOTS_DIR, "win", "repo"]) + ), + } + with patch.dict(winrepo.__opts__, {"test": False}), patch( + "os.path.exists", MagicMock(return_value=False) + ): + ret = winrepo.genrepo("salt") self.assertDictEqual(ret, expected) def test_genrepo_no_dir_force(self): - ''' + """ Test genrepo when the dir does not exist and force=True - ''' - expected = {'name': 'salt', - 'changes': {'winrepo': []}, - 'result': True, - 'comment': ''} - with patch.dict(winrepo.__opts__, {'test': False}), \ - patch('os.path.exists', MagicMock(return_value=False)): - ret = winrepo.genrepo('salt', force=True) + """ + expected = { + "name": "salt", + "changes": {"winrepo": []}, + "result": True, + "comment": "", + } + with patch.dict(winrepo.__opts__, {"test": False}), patch( + "os.path.exists", MagicMock(return_value=False) + ): + ret = winrepo.genrepo("salt", force=True) self.assertDictEqual(ret, expected) diff --git a/tests/unit/states/test_xml.py b/tests/unit/states/test_xml.py index 29a6337eace..2491c128b05 100644 --- a/tests/unit/states/test_xml.py +++ b/tests/unit/states/test_xml.py @@ -1,51 +1,50 @@ # -*- coding: utf-8 -*- -''' +""" Test cases for xml state -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - # Import Salt Libs import salt.states.xml as xml +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class XMLTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.xml - ''' + """ + def setup_loader_modules(self): return {xml: {}} def test_value_already_present(self): - ''' + """ Test for existing value_present - ''' + """ name = "testfile.xml" xpath = ".//list[@id='1']" value = "test value" state_return = { - 'name': name, - 'changes': {}, - 'result': True, - 'comment': '{0} is already present'.format(value) - } + "name": name, + "changes": {}, + "result": True, + "comment": "{0} is already present".format(value), + } - with patch.dict(xml.__salt__, {'xml.get_value': MagicMock(return_value=value)}): + with patch.dict(xml.__salt__, {"xml.get_value": MagicMock(return_value=value)}): self.assertDictEqual(xml.value_present(name, xpath, value), state_return) def test_value_update(self): - ''' + """ Test for updating value_present - ''' + """ name = "testfile.xml" xpath = ".//list[@id='1']" @@ -54,20 +53,26 @@ class XMLTestCase(TestCase, LoaderModuleMockMixin): old_value = "not test value" state_return = { - 'name': name, - 'changes': {name: {'new': value, 'old': old_value}}, - 'result': True, - 'comment': '{0} updated'.format(name) - } + "name": name, + "changes": {name: {"new": value, "old": old_value}}, + "result": True, + "comment": "{0} updated".format(name), + } - with patch.dict(xml.__salt__, {'xml.get_value': MagicMock(return_value=old_value)}): - with patch.dict(xml.__salt__, {'xml.set_value': MagicMock(return_value=True)}): - self.assertDictEqual(xml.value_present(name, xpath, value), state_return) + with patch.dict( + xml.__salt__, {"xml.get_value": MagicMock(return_value=old_value)} + ): + with patch.dict( + xml.__salt__, {"xml.set_value": MagicMock(return_value=True)} + ): + self.assertDictEqual( + xml.value_present(name, xpath, value), state_return + ) def test_value_update_test(self): - ''' + """ Test for value_present test=True - ''' + """ name = "testfile.xml" xpath = ".//list[@id='1']" @@ -76,30 +81,36 @@ class XMLTestCase(TestCase, LoaderModuleMockMixin): old_value = "not test value" state_return = { - 'name': name, - 'changes': {name: {'old': old_value, 'new': value}}, - 'result': None, - 'comment': '{0} will be updated'.format(name) - } + "name": name, + "changes": {name: {"old": old_value, "new": value}}, + "result": None, + "comment": "{0} will be updated".format(name), + } - with patch.dict(xml.__salt__, {'xml.get_value': MagicMock(return_value=old_value)}): - self.assertDictEqual(xml.value_present(name, xpath, value, test=True), state_return) + with patch.dict( + xml.__salt__, {"xml.get_value": MagicMock(return_value=old_value)} + ): + self.assertDictEqual( + xml.value_present(name, xpath, value, test=True), state_return + ) def test_value_update_invalid_xpath(self): - ''' + """ Test for value_present invalid xpath - ''' + """ name = "testfile.xml" xpath = ".//list[@id='1']" value = "test value" state_return = { - 'name': name, - 'changes': {}, - 'result': False, - 'comment': 'xpath query {0} not found in {1}'.format(xpath, name) - } + "name": name, + "changes": {}, + "result": False, + "comment": "xpath query {0} not found in {1}".format(xpath, name), + } - with patch.dict(xml.__salt__, {'xml.get_value': MagicMock(return_value=False)}): - self.assertDictEqual(xml.value_present(name, xpath, value, test=True), state_return) + with patch.dict(xml.__salt__, {"xml.get_value": MagicMock(return_value=False)}): + self.assertDictEqual( + xml.value_present(name, xpath, value, test=True), state_return + ) diff --git a/tests/unit/states/test_xmpp.py b/tests/unit/states/test_xmpp.py index 1c7bab91ec3..2cfd5f7b94f 100644 --- a/tests/unit/states/test_xmpp.py +++ b/tests/unit/states/test_xmpp.py @@ -1,48 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Rahul Handay <rahulha@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.xmpp as xmpp +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class XmppTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the xmpp state - ''' + """ + def setup_loader_modules(self): return {xmpp: {}} def test_send_msg(self): - ''' + """ Test to send a message to an XMPP user - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': None, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": None, "comment": ""} with patch.dict(xmpp.__opts__, {"test": True}): - ret.update({'comment': 'Need to send message to myaccount: salt'}) - self.assertDictEqual(xmpp.send_msg('salt', 'myaccount', - 'salt@saltstack.com'), ret) + ret.update({"comment": "Need to send message to myaccount: salt"}) + self.assertDictEqual( + xmpp.send_msg("salt", "myaccount", "salt@saltstack.com"), ret + ) with patch.dict(xmpp.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.dict(xmpp.__salt__, {'xmpp.send_msg': mock, - 'xmpp.send_msg_multi': mock}): - ret.update({'result': True, - 'comment': 'Sent message to myaccount: salt'}) - self.assertDictEqual(xmpp.send_msg('salt', 'myaccount', - 'salt@saltstack.com'), ret) + with patch.dict( + xmpp.__salt__, {"xmpp.send_msg": mock, "xmpp.send_msg_multi": mock} + ): + ret.update( + {"result": True, "comment": "Sent message to myaccount: salt"} + ) + self.assertDictEqual( + xmpp.send_msg("salt", "myaccount", "salt@saltstack.com"), ret + ) diff --git a/tests/unit/states/test_zabbix_action.py b/tests/unit/states/test_zabbix_action.py index 31e0dc62097..235cb1b0d7a 100644 --- a/tests/unit/states/test_zabbix_action.py +++ b/tests/unit/states/test_zabbix_action.py @@ -1,184 +1,290 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Jakub Sliva <jakub.sliva@ultimum.io>` -''' +""" # Import Python Libs -from __future__ import absolute_import -from __future__ import unicode_literals - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, unicode_literals import salt.states.zabbix_action as zabbix_action +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase INPUT_PARAMS = { - 'status': '0', - 'filter': {'evaltype': '2', 'conditions': [{'operator': '2', 'conditiontype': '24', 'value': 'database'}]}, - 'eventsource': '2', - 'name': 'Auto registration Databases', - 'operations': [{'opgroup': [{'groupid': '6'}], 'operationtype': '4'}]} + "status": "0", + "filter": { + "evaltype": "2", + "conditions": [{"operator": "2", "conditiontype": "24", "value": "database"}], + }, + "eventsource": "2", + "name": "Auto registration Databases", + "operations": [{"opgroup": [{"groupid": "6"}], "operationtype": "4"}], +} -EXISTING_OBJ = [{ - 'status': '0', - 'operations': [{'operationtype': '4', 'esc_period': '0', 'evaltype': '0', 'opconditions': [], - 'esc_step_to': '1', 'actionid': '28', 'esc_step_from': '1', - 'opgroup': [{'groupid': '6', 'operationid': '92'}], - 'operationid': '92'}], - 'def_shortdata': '', - 'name': 'Auto registration Databases', - 'esc_period': '0', - 'def_longdata': '', - 'filter': {'formula': '', 'evaltype': '2', 'conditions': [{'operator': '2', 'conditiontype': '24', - 'formulaid': 'A', 'value': 'database'}], - 'eval_formula': 'A'}, - 'eventsource': '2', - 'actionid': '28', - 'r_shortdata': '', - 'r_longdata': '', - 'recovery_msg': '0'}] +EXISTING_OBJ = [ + { + "status": "0", + "operations": [ + { + "operationtype": "4", + "esc_period": "0", + "evaltype": "0", + "opconditions": [], + "esc_step_to": "1", + "actionid": "28", + "esc_step_from": "1", + "opgroup": [{"groupid": "6", "operationid": "92"}], + "operationid": "92", + } + ], + "def_shortdata": "", + "name": "Auto registration Databases", + "esc_period": "0", + "def_longdata": "", + "filter": { + "formula": "", + "evaltype": "2", + "conditions": [ + { + "operator": "2", + "conditiontype": "24", + "formulaid": "A", + "value": "database", + } + ], + "eval_formula": "A", + }, + "eventsource": "2", + "actionid": "28", + "r_shortdata": "", + "r_longdata": "", + "recovery_msg": "0", + } +] EXISTING_OBJ_DIFF = { - 'status': '0', - 'operations': [{'operationtype': '4', 'esc_period': '0', 'evaltype': '0', 'opconditions': [], - 'esc_step_to': '1', 'actionid': '28', 'esc_step_from': '1', - 'opgroup': [{'groupid': '6', 'operationid': '92'}], - 'operationid': '92'}], - 'def_shortdata': '', - 'name': 'Auto registration Databases', - 'esc_period': '0', - 'def_longdata': '', - 'filter': {'formula': '', 'evaltype': '2', 'conditions': [{'operator': '2', 'conditiontype': '24', - 'formulaid': 'A', 'value': 'SOME OTHER VALUE'}], - 'eval_formula': 'A'}, - 'eventsource': '2', - 'actionid': '28', - 'r_shortdata': '', - 'r_longdata': '', - 'recovery_msg': '0'} + "status": "0", + "operations": [ + { + "operationtype": "4", + "esc_period": "0", + "evaltype": "0", + "opconditions": [], + "esc_step_to": "1", + "actionid": "28", + "esc_step_from": "1", + "opgroup": [{"groupid": "6", "operationid": "92"}], + "operationid": "92", + } + ], + "def_shortdata": "", + "name": "Auto registration Databases", + "esc_period": "0", + "def_longdata": "", + "filter": { + "formula": "", + "evaltype": "2", + "conditions": [ + { + "operator": "2", + "conditiontype": "24", + "formulaid": "A", + "value": "SOME OTHER VALUE", + } + ], + "eval_formula": "A", + }, + "eventsource": "2", + "actionid": "28", + "r_shortdata": "", + "r_longdata": "", + "recovery_msg": "0", +} -DIFF_PARAMS = {'filter': {'evaltype': '2', - 'conditions': [{'operator': '2', 'conditiontype': '24', 'value': 'virtual'}]}, - 'actionid': '28'} +DIFF_PARAMS = { + "filter": { + "evaltype": "2", + "conditions": [{"operator": "2", "conditiontype": "24", "value": "virtual"}], + }, + "actionid": "28", +} class ZabbixActionTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.zabbix - ''' + """ + def setup_loader_modules(self): return {zabbix_action: {}} def test_present_create(self): - ''' + """ Test to ensure that named action is created - ''' - name = 'Auto registration Databases' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + name = "Auto registration Databases" + ret = {"name": name, "result": False, "comment": "", "changes": {}} def side_effect_run_query(*args): - ''' + """ Differentiate between __salt__ exec module function calls with different parameters. - ''' - if args[0] == 'action.get': + """ + if args[0] == "action.get": return False - elif args[0] == 'action.create': + elif args[0] == "action.create": return True - with patch.dict(zabbix_action.__opts__, {'test': False}): - with patch.dict(zabbix_action.__salt__, - {'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'action': 'actionid'}), - 'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, False]), - 'zabbix.run_query': MagicMock(side_effect=side_effect_run_query), - 'zabbix.compare_params': MagicMock(return_value={})}): - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" created.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" did not exist.'.format(name), - 'new': 'Zabbix Action "{0}" created according definition.'.format(name)}} + with patch.dict(zabbix_action.__opts__, {"test": False}): + with patch.dict( + zabbix_action.__salt__, + { + "zabbix.get_zabbix_id_mapper": MagicMock( + return_value={"action": "actionid"} + ), + "zabbix.substitute_params": MagicMock( + side_effect=[INPUT_PARAMS, False] + ), + "zabbix.run_query": MagicMock(side_effect=side_effect_run_query), + "zabbix.compare_params": MagicMock(return_value={}), + }, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" created.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" did not exist.'.format(name), + "new": 'Zabbix Action "{0}" created according definition.'.format( + name + ), + } + } self.assertDictEqual(zabbix_action.present(name, {}), ret) def test_present_exists(self): - ''' + """ Test to ensure that named action is present and not changed - ''' - name = 'Auto registration Databases' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + name = "Auto registration Databases" + ret = {"name": name, "result": False, "comment": "", "changes": {}} - with patch.dict(zabbix_action.__opts__, {'test': False}): - with patch.dict(zabbix_action.__salt__, - {'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'action': 'actionid'}), - 'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, EXISTING_OBJ]), - 'zabbix.run_query': MagicMock(return_value=['length of result is 1']), - 'zabbix.compare_params': MagicMock(return_value={})}): - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" already exists and corresponds to a definition.'.format(name) + with patch.dict(zabbix_action.__opts__, {"test": False}): + with patch.dict( + zabbix_action.__salt__, + { + "zabbix.get_zabbix_id_mapper": MagicMock( + return_value={"action": "actionid"} + ), + "zabbix.substitute_params": MagicMock( + side_effect=[INPUT_PARAMS, EXISTING_OBJ] + ), + "zabbix.run_query": MagicMock( + return_value=["length of result is 1"] + ), + "zabbix.compare_params": MagicMock(return_value={}), + }, + ): + ret["result"] = True + ret[ + "comment" + ] = 'Zabbix Action "{0}" already exists and corresponds to a definition.'.format( + name + ) self.assertDictEqual(zabbix_action.present(name, {}), ret) def test_present_update(self): - ''' + """ Test to ensure that named action is present but must be updated - ''' - name = 'Auto registration Databases' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + name = "Auto registration Databases" + ret = {"name": name, "result": False, "comment": "", "changes": {}} def side_effect_run_query(*args): - ''' + """ Differentiate between __salt__ exec module function calls with different parameters. - ''' - if args[0] == 'action.get': - return ['length of result is 1 = action exists'] - elif args[0] == 'action.update': + """ + if args[0] == "action.get": + return ["length of result is 1 = action exists"] + elif args[0] == "action.update": return DIFF_PARAMS - with patch.dict(zabbix_action.__opts__, {'test': False}): - with patch.dict(zabbix_action.__salt__, - {'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'action': 'actionid'}), - 'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, EXISTING_OBJ_DIFF]), - 'zabbix.run_query': MagicMock(side_effect=side_effect_run_query), - 'zabbix.compare_params': MagicMock(return_value=DIFF_PARAMS)}): - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" updated.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" differed ' - 'in following parameters: {1}'.format(name, DIFF_PARAMS), - 'new': 'Zabbix Action "{0}" fixed.'.format(name)}} + with patch.dict(zabbix_action.__opts__, {"test": False}): + with patch.dict( + zabbix_action.__salt__, + { + "zabbix.get_zabbix_id_mapper": MagicMock( + return_value={"action": "actionid"} + ), + "zabbix.substitute_params": MagicMock( + side_effect=[INPUT_PARAMS, EXISTING_OBJ_DIFF] + ), + "zabbix.run_query": MagicMock(side_effect=side_effect_run_query), + "zabbix.compare_params": MagicMock(return_value=DIFF_PARAMS), + }, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" updated.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" differed ' + "in following parameters: {1}".format(name, DIFF_PARAMS), + "new": 'Zabbix Action "{0}" fixed.'.format(name), + } + } self.assertDictEqual(zabbix_action.present(name, {}), ret) def test_absent_test_mode(self): - ''' + """ Test to ensure that named action is absent in test mode - ''' - name = 'Auto registration Databases' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - with patch.dict(zabbix_action.__opts__, {'test': True}): - with patch.dict(zabbix_action.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}): - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" would be deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" exists.'.format(name), - 'new': 'Zabbix Action "{0}" would be deleted.'.format(name)}} + """ + name = "Auto registration Databases" + ret = {"name": name, "result": False, "comment": "", "changes": {}} + with patch.dict(zabbix_action.__opts__, {"test": True}): + with patch.dict( + zabbix_action.__salt__, + {"zabbix.get_object_id_by_params": MagicMock(return_value=11)}, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" would be deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" exists.'.format(name), + "new": 'Zabbix Action "{0}" would be deleted.'.format(name), + } + } self.assertDictEqual(zabbix_action.absent(name), ret) def test_absent(self): - ''' + """ Test to ensure that named action is absent - ''' - name = 'Auto registration Databases' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - with patch.dict(zabbix_action.__opts__, {'test': False}): - with patch.dict(zabbix_action.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=False)}): - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" does not exist.'.format(name) + """ + name = "Auto registration Databases" + ret = {"name": name, "result": False, "comment": "", "changes": {}} + with patch.dict(zabbix_action.__opts__, {"test": False}): + with patch.dict( + zabbix_action.__salt__, + {"zabbix.get_object_id_by_params": MagicMock(return_value=False)}, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" does not exist.'.format(name) self.assertDictEqual(zabbix_action.absent(name), ret) - with patch.dict(zabbix_action.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}): - with patch.dict(zabbix_action.__salt__, {'zabbix.run_query': MagicMock(return_value=True)}): - ret['result'] = True - ret['comment'] = 'Zabbix Action "{0}" deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Action "{0}" existed.'.format(name), - 'new': 'Zabbix Action "{0}" deleted.'.format(name)}} + with patch.dict( + zabbix_action.__salt__, + {"zabbix.get_object_id_by_params": MagicMock(return_value=11)}, + ): + with patch.dict( + zabbix_action.__salt__, + {"zabbix.run_query": MagicMock(return_value=True)}, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Action "{0}" deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Action "{0}" existed.'.format(name), + "new": 'Zabbix Action "{0}" deleted.'.format(name), + } + } self.assertDictEqual(zabbix_action.absent(name), ret) diff --git a/tests/unit/states/test_zabbix_template.py b/tests/unit/states/test_zabbix_template.py index f95918f9fd2..84c9ac5ee8a 100644 --- a/tests/unit/states/test_zabbix_template.py +++ b/tests/unit/states/test_zabbix_template.py @@ -1,179 +1,313 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Jakub Sliva <jakub.sliva@ultimum.io>` -''' +""" # Import Python Libs -from __future__ import absolute_import -from __future__ import unicode_literals - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, unicode_literals import salt.states.zabbix_template as zabbix_template +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase INPUT_PARAMS = {"applications": [{"name": "Ceph OSD"}]} -DEFINED_OBJ = {"macros": [{"macro": "{$CEPH_CLUSTER_NAME}", "value": "ceph"}], "host": "A Testing Template", - "hosts": [{"hostid": "10112"}, {"hostid": "10113"}], "description": "Template for Ceph nodes", - "groups": [{"groupid": "1"}]} +DEFINED_OBJ = { + "macros": [{"macro": "{$CEPH_CLUSTER_NAME}", "value": "ceph"}], + "host": "A Testing Template", + "hosts": [{"hostid": "10112"}, {"hostid": "10113"}], + "description": "Template for Ceph nodes", + "groups": [{"groupid": "1"}], +} -DEFINED_C_LIST_SUBS = {"applications": [{"name": "Ceph OSD"}], 'graphs': [], 'triggers': [], 'items': [], - 'httpTests': [], 'screens': [], 'gitems': [], 'discoveries': []} +DEFINED_C_LIST_SUBS = { + "applications": [{"name": "Ceph OSD"}], + "graphs": [], + "triggers": [], + "items": [], + "httpTests": [], + "screens": [], + "gitems": [], + "discoveries": [], +} -SUBSTITUTE_PARAMS_CREATE = [DEFINED_OBJ, [], DEFINED_C_LIST_SUBS['applications'], [], [], [], [], [], [], [], []] +SUBSTITUTE_PARAMS_CREATE = [ + DEFINED_OBJ, + [], + DEFINED_C_LIST_SUBS["applications"], + [], + [], + [], + [], + [], + [], + [], + [], +] -EXISTING_OBJ = [{"available": "0", "tls_connect": "1", "maintenance_type": "0", "groups": [{"groupid": "1"}], - "macros": [{"macro": "{$CEPH_CLUSTER_NAME}", "hostmacroid": "60", "hostid": "10206", "value": "ceph"}], - "hosts": [{"hostid": "10112"}, {"hostid": "10113"}], "status": "3", - "description": "Template for Ceph nodes", "host": "A Testing Template", "disable_until": "0", - "templateid": "10206", "name": "A Testing Template"}] +EXISTING_OBJ = [ + { + "available": "0", + "tls_connect": "1", + "maintenance_type": "0", + "groups": [{"groupid": "1"}], + "macros": [ + { + "macro": "{$CEPH_CLUSTER_NAME}", + "hostmacroid": "60", + "hostid": "10206", + "value": "ceph", + } + ], + "hosts": [{"hostid": "10112"}, {"hostid": "10113"}], + "status": "3", + "description": "Template for Ceph nodes", + "host": "A Testing Template", + "disable_until": "0", + "templateid": "10206", + "name": "A Testing Template", + } +] -SUBSTITUTE_PARAMS_EXISTS = [DEFINED_OBJ, EXISTING_OBJ[0], [], [], [], [], [], [], [], []] +SUBSTITUTE_PARAMS_EXISTS = [ + DEFINED_OBJ, + EXISTING_OBJ[0], + [], + [], + [], + [], + [], + [], + [], + [], +] -EXISTING_OBJ_DIFF = [{"groups": [{"groupid": "1"}], "macros": [{"macro": "{$CEPH_CLUSTER_NAME}", "hostmacroid": "60", - "hostid": "10206", "value": "ceph"}], - "hosts": [{"hostid": "10112"}, {"hostid": "10113"}], "status": "3", "templateid": "10206", - "name": "A Testing Template"}] +EXISTING_OBJ_DIFF = [ + { + "groups": [{"groupid": "1"}], + "macros": [ + { + "macro": "{$CEPH_CLUSTER_NAME}", + "hostmacroid": "60", + "hostid": "10206", + "value": "ceph", + } + ], + "hosts": [{"hostid": "10112"}, {"hostid": "10113"}], + "status": "3", + "templateid": "10206", + "name": "A Testing Template", + } +] -SUBSTITUTE_PARAMS_UPDATE = [DEFINED_OBJ, EXISTING_OBJ_DIFF[0], [], [], [], [], [], [], [], []] +SUBSTITUTE_PARAMS_UPDATE = [ + DEFINED_OBJ, + EXISTING_OBJ_DIFF[0], + [], + [], + [], + [], + [], + [], + [], + [], +] -DIFF_PARAMS = {'old': {}, 'new': {'macros': [], 'templateid': '10206'}} +DIFF_PARAMS = {"old": {}, "new": {"macros": [], "templateid": "10206"}} class ZabbixTemplateTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.zabbix - ''' + """ + def setup_loader_modules(self): return {zabbix_template: {}} - @patch('salt.states.zabbix_template.CHANGE_STACK', []) + @patch("salt.states.zabbix_template.CHANGE_STACK", []) def test_present_create(self): - ''' + """ Test to ensure that named template is created - ''' - name = 'A Testing Template' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + name = "A Testing Template" + ret = {"name": name, "result": False, "comment": "", "changes": {}} def side_effect_run_query(*args): - ''' + """ Differentiate between __salt__ exec module function calls with different parameters. - ''' - if args[0] == 'template.get': + """ + if args[0] == "template.get": return [] - elif args[0] == 'template.create': - return {'templateids': ['10206']} - elif args[0] == 'application.get': + elif args[0] == "template.create": + return {"templateids": ["10206"]} + elif args[0] == "application.get": return [] - elif args[0] == 'application.create': + elif args[0] == "application.create": return {"applicationids": ["701"]} - with patch.dict(zabbix_template.__opts__, {'test': False}): - with patch.dict(zabbix_template.__salt__, - {'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'template': 'templateid'}), - 'zabbix.substitute_params': MagicMock(side_effect=SUBSTITUTE_PARAMS_CREATE), - 'zabbix.run_query': MagicMock(side_effect=side_effect_run_query), - 'zabbix.compare_params': MagicMock(return_value={})}): - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" created.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" did not exist.'.format(name), - 'new': 'Zabbix Template "{0}" created according definition.'.format(name)}} + with patch.dict(zabbix_template.__opts__, {"test": False}): + with patch.dict( + zabbix_template.__salt__, + { + "zabbix.get_zabbix_id_mapper": MagicMock( + return_value={"template": "templateid"} + ), + "zabbix.substitute_params": MagicMock( + side_effect=SUBSTITUTE_PARAMS_CREATE + ), + "zabbix.run_query": MagicMock(side_effect=side_effect_run_query), + "zabbix.compare_params": MagicMock(return_value={}), + }, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Template "{0}" created.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" did not exist.'.format(name), + "new": 'Zabbix Template "{0}" created according definition.'.format( + name + ), + } + } self.assertDictEqual(zabbix_template.present(name, {}), ret) - @patch('salt.states.zabbix_template.CHANGE_STACK', []) + @patch("salt.states.zabbix_template.CHANGE_STACK", []) def test_present_exists(self): - ''' + """ Test to ensure that named template is present and not changed - ''' - name = 'A Testing Template' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + name = "A Testing Template" + ret = {"name": name, "result": False, "comment": "", "changes": {}} def side_effect_run_query(*args): - ''' + """ Differentiate between __salt__ exec module function calls with different parameters. - ''' - if args[0] == 'template.get': + """ + if args[0] == "template.get": return EXISTING_OBJ - elif args[0] == 'application.get': - return ['non-empty'] + elif args[0] == "application.get": + return ["non-empty"] - with patch.dict(zabbix_template.__opts__, {'test': False}): - with patch.dict(zabbix_template.__salt__, - {'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'template': 'templateid'}), - 'zabbix.substitute_params': MagicMock(side_effect=SUBSTITUTE_PARAMS_EXISTS), - 'zabbix.run_query': MagicMock(side_effect=side_effect_run_query), - 'zabbix.compare_params': MagicMock(return_value={'new': {}, 'old': {}})}): - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" already exists and corresponds to a definition.'.format(name) + with patch.dict(zabbix_template.__opts__, {"test": False}): + with patch.dict( + zabbix_template.__salt__, + { + "zabbix.get_zabbix_id_mapper": MagicMock( + return_value={"template": "templateid"} + ), + "zabbix.substitute_params": MagicMock( + side_effect=SUBSTITUTE_PARAMS_EXISTS + ), + "zabbix.run_query": MagicMock(side_effect=side_effect_run_query), + "zabbix.compare_params": MagicMock( + return_value={"new": {}, "old": {}} + ), + }, + ): + ret["result"] = True + ret[ + "comment" + ] = 'Zabbix Template "{0}" already exists and corresponds to a definition.'.format( + name + ) self.assertDictEqual(zabbix_template.present(name, {}), ret) - @patch('salt.states.zabbix_template.CHANGE_STACK', []) + @patch("salt.states.zabbix_template.CHANGE_STACK", []) def test_present_update(self): - ''' + """ Test to ensure that named template is present but must be updated - ''' - name = 'A Testing Template' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + name = "A Testing Template" + ret = {"name": name, "result": False, "comment": "", "changes": {}} def side_effect_run_query(*args): - ''' + """ Differentiate between __salt__ exec module function calls with different parameters. - ''' - if args[0] == 'template.get': - return ['length of result is 1 = template exists'] - elif args[0] == 'template.update': + """ + if args[0] == "template.get": + return ["length of result is 1 = template exists"] + elif args[0] == "template.update": return DIFF_PARAMS - with patch.dict(zabbix_template.__opts__, {'test': False}): - with patch.dict(zabbix_template.__salt__, - {'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'template': 'templateid'}), - 'zabbix.substitute_params': MagicMock(side_effect=SUBSTITUTE_PARAMS_UPDATE), - 'zabbix.run_query': MagicMock(side_effect=side_effect_run_query), - 'zabbix.compare_params': MagicMock(return_value=DIFF_PARAMS)}): - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" updated.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" differed.'.format(name), - 'new': 'Zabbix Template "{0}" updated according definition.'.format(name)}} + with patch.dict(zabbix_template.__opts__, {"test": False}): + with patch.dict( + zabbix_template.__salt__, + { + "zabbix.get_zabbix_id_mapper": MagicMock( + return_value={"template": "templateid"} + ), + "zabbix.substitute_params": MagicMock( + side_effect=SUBSTITUTE_PARAMS_UPDATE + ), + "zabbix.run_query": MagicMock(side_effect=side_effect_run_query), + "zabbix.compare_params": MagicMock(return_value=DIFF_PARAMS), + }, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Template "{0}" updated.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" differed.'.format(name), + "new": 'Zabbix Template "{0}" updated according definition.'.format( + name + ), + } + } self.assertDictEqual(zabbix_template.present(name, {}), ret) def test_absent_test_mode(self): - ''' + """ Test to ensure that named template is absent in test mode - ''' - name = 'A Testing Template' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - with patch.dict(zabbix_template.__opts__, {'test': True}): - with patch.dict(zabbix_template.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}): - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" would be deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" exists.'.format(name), - 'new': 'Zabbix Template "{0}" would be deleted.'.format(name)}} + """ + name = "A Testing Template" + ret = {"name": name, "result": False, "comment": "", "changes": {}} + with patch.dict(zabbix_template.__opts__, {"test": True}): + with patch.dict( + zabbix_template.__salt__, + {"zabbix.get_object_id_by_params": MagicMock(return_value=11)}, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Template "{0}" would be deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" exists.'.format(name), + "new": 'Zabbix Template "{0}" would be deleted.'.format(name), + } + } self.assertDictEqual(zabbix_template.absent(name), ret) def test_absent(self): - ''' + """ Test to ensure that named template is absent - ''' - name = 'A Testing Template' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - with patch.dict(zabbix_template.__opts__, {'test': False}): - with patch.dict(zabbix_template.__salt__, - {'zabbix.get_object_id_by_params': MagicMock(return_value=False)}): - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" does not exist.'.format(name) + """ + name = "A Testing Template" + ret = {"name": name, "result": False, "comment": "", "changes": {}} + with patch.dict(zabbix_template.__opts__, {"test": False}): + with patch.dict( + zabbix_template.__salt__, + {"zabbix.get_object_id_by_params": MagicMock(return_value=False)}, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Template "{0}" does not exist.'.format(name) self.assertDictEqual(zabbix_template.absent(name), ret) - with patch.dict(zabbix_template.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}): - with patch.dict(zabbix_template.__salt__, {'zabbix.run_query': MagicMock(return_value=True)}): - ret['result'] = True - ret['comment'] = 'Zabbix Template "{0}" deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Template "{0}" existed.'.format(name), - 'new': 'Zabbix Template "{0}" deleted.'.format(name)}} + with patch.dict( + zabbix_template.__salt__, + {"zabbix.get_object_id_by_params": MagicMock(return_value=11)}, + ): + with patch.dict( + zabbix_template.__salt__, + {"zabbix.run_query": MagicMock(return_value=True)}, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Template "{0}" deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Template "{0}" existed.'.format(name), + "new": 'Zabbix Template "{0}" deleted.'.format(name), + } + } self.assertDictEqual(zabbix_template.absent(name), ret) diff --git a/tests/unit/states/test_zabbix_valuemap.py b/tests/unit/states/test_zabbix_valuemap.py index 58652ab1ec1..0c11de47feb 100644 --- a/tests/unit/states/test_zabbix_valuemap.py +++ b/tests/unit/states/test_zabbix_valuemap.py @@ -1,149 +1,228 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Jakub Sliva <jakub.sliva@ultimum.io>` -''' +""" # Import Python Libs -from __future__ import absolute_import -from __future__ import unicode_literals - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, unicode_literals import salt.states.zabbix_valuemap as zabbix_valuemap +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase -INPUT_PARAMS = {'mappings': [{'newvalue': 'OK', 'value': '0h'}, {'newvalue': 'Failure', 'value': '1'}], - 'name': 'Server HP Health'} +INPUT_PARAMS = { + "mappings": [ + {"newvalue": "OK", "value": "0h"}, + {"newvalue": "Failure", "value": "1"}, + ], + "name": "Server HP Health", +} -EXISTING_OBJ = [{'valuemapid': '21', 'name': 'Server HP Health', 'mappings': [{'newvalue': 'OK', 'value': '0h'}, - {'newvalue': 'Failure', 'value': '1'}]}] +EXISTING_OBJ = [ + { + "valuemapid": "21", + "name": "Server HP Health", + "mappings": [ + {"newvalue": "OK", "value": "0h"}, + {"newvalue": "Failure", "value": "1"}, + ], + } +] -EXISTING_OBJ_DIFF = {'valuemapid': '21', 'name': 'Server HP Health', 'mappings': [{'newvalue': 'OK', 'value': '0h'}, - {'newvalue': 'Failure', 'value': '1'}, - {'newvalue': 'some', 'value': '2'}]} +EXISTING_OBJ_DIFF = { + "valuemapid": "21", + "name": "Server HP Health", + "mappings": [ + {"newvalue": "OK", "value": "0h"}, + {"newvalue": "Failure", "value": "1"}, + {"newvalue": "some", "value": "2"}, + ], +} -DIFF_PARAMS = {'valuemapid': '21', 'mappings': [{'newvalue': 'OK', 'value': '0h'}, - {'newvalue': 'Failure', 'value': '1'}]} +DIFF_PARAMS = { + "valuemapid": "21", + "mappings": [ + {"newvalue": "OK", "value": "0h"}, + {"newvalue": "Failure", "value": "1"}, + ], +} class ZabbixActionTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.zabbix - ''' + """ + def setup_loader_modules(self): return {zabbix_valuemap: {}} def test_present_create(self): - ''' + """ Test to ensure that named value map is created - ''' - name = 'Server HP Health' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + name = "Server HP Health" + ret = {"name": name, "result": False, "comment": "", "changes": {}} def side_effect_run_query(*args): - ''' + """ Differentiate between __salt__ exec module function calls with different parameters. - ''' - if args[0] == 'valuemap.get': + """ + if args[0] == "valuemap.get": return False - elif args[0] == 'valuemap.create': + elif args[0] == "valuemap.create": return True - with patch.dict(zabbix_valuemap.__opts__, {'test': False}): - with patch.dict(zabbix_valuemap.__salt__, - {'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'valuemap': 'valuemapid'}), - 'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, False]), - 'zabbix.run_query': MagicMock(side_effect=side_effect_run_query), - 'zabbix.compare_params': MagicMock(return_value={})}): - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" created.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" did not exist.'.format(name), - 'new': 'Zabbix Value map "{0}" created according definition.'.format(name)}} + with patch.dict(zabbix_valuemap.__opts__, {"test": False}): + with patch.dict( + zabbix_valuemap.__salt__, + { + "zabbix.get_zabbix_id_mapper": MagicMock( + return_value={"valuemap": "valuemapid"} + ), + "zabbix.substitute_params": MagicMock( + side_effect=[INPUT_PARAMS, False] + ), + "zabbix.run_query": MagicMock(side_effect=side_effect_run_query), + "zabbix.compare_params": MagicMock(return_value={}), + }, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" created.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" did not exist.'.format(name), + "new": 'Zabbix Value map "{0}" created according definition.'.format( + name + ), + } + } self.assertDictEqual(zabbix_valuemap.present(name, {}), ret) def test_present_exists(self): - ''' + """ Test to ensure that named value map is present and not changed - ''' - name = 'Server HP Health' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + name = "Server HP Health" + ret = {"name": name, "result": False, "comment": "", "changes": {}} - with patch.dict(zabbix_valuemap.__opts__, {'test': False}): - with patch.dict(zabbix_valuemap.__salt__, - {'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'valuemap': 'valuemapid'}), - 'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, EXISTING_OBJ]), - 'zabbix.run_query': MagicMock(return_value=['length of result is 1']), - 'zabbix.compare_params': MagicMock(return_value={})}): - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" already exists and corresponds to a definition.'.format(name) + with patch.dict(zabbix_valuemap.__opts__, {"test": False}): + with patch.dict( + zabbix_valuemap.__salt__, + { + "zabbix.get_zabbix_id_mapper": MagicMock( + return_value={"valuemap": "valuemapid"} + ), + "zabbix.substitute_params": MagicMock( + side_effect=[INPUT_PARAMS, EXISTING_OBJ] + ), + "zabbix.run_query": MagicMock( + return_value=["length of result is 1"] + ), + "zabbix.compare_params": MagicMock(return_value={}), + }, + ): + ret["result"] = True + ret[ + "comment" + ] = 'Zabbix Value map "{0}" already exists and corresponds to a definition.'.format( + name + ) self.assertDictEqual(zabbix_valuemap.present(name, {}), ret) def test_present_update(self): - ''' + """ Test to ensure that named value map is present but must be updated - ''' - name = 'Server HP Health' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} + """ + name = "Server HP Health" + ret = {"name": name, "result": False, "comment": "", "changes": {}} def side_effect_run_query(*args): - ''' + """ Differentiate between __salt__ exec module function calls with different parameters. - ''' - if args[0] == 'valuemap.get': - return ['length of result is 1 = valuemap exists'] - elif args[0] == 'valuemap.update': + """ + if args[0] == "valuemap.get": + return ["length of result is 1 = valuemap exists"] + elif args[0] == "valuemap.update": return DIFF_PARAMS - with patch.dict(zabbix_valuemap.__opts__, {'test': False}): - with patch.dict(zabbix_valuemap.__salt__, - {'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'valuemap': 'valuemapid'}), - 'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, EXISTING_OBJ_DIFF]), - 'zabbix.run_query': MagicMock(side_effect=side_effect_run_query), - 'zabbix.compare_params': MagicMock(return_value=DIFF_PARAMS)}): - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" updated.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" differed ' - 'in following parameters: {1}'.format(name, DIFF_PARAMS), - 'new': 'Zabbix Value map "{0}" fixed.'.format(name)}} + with patch.dict(zabbix_valuemap.__opts__, {"test": False}): + with patch.dict( + zabbix_valuemap.__salt__, + { + "zabbix.get_zabbix_id_mapper": MagicMock( + return_value={"valuemap": "valuemapid"} + ), + "zabbix.substitute_params": MagicMock( + side_effect=[INPUT_PARAMS, EXISTING_OBJ_DIFF] + ), + "zabbix.run_query": MagicMock(side_effect=side_effect_run_query), + "zabbix.compare_params": MagicMock(return_value=DIFF_PARAMS), + }, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" updated.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" differed ' + "in following parameters: {1}".format(name, DIFF_PARAMS), + "new": 'Zabbix Value map "{0}" fixed.'.format(name), + } + } self.assertDictEqual(zabbix_valuemap.present(name, {}), ret) def test_absent_test_mode(self): - ''' + """ Test to ensure that named value map is absent in test mode - ''' - name = 'Server HP Health' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - with patch.dict(zabbix_valuemap.__opts__, {'test': True}): - with patch.dict(zabbix_valuemap.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}): - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" would be deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" exists.'.format(name), - 'new': 'Zabbix Value map "{0}" would be deleted.'.format(name)}} + """ + name = "Server HP Health" + ret = {"name": name, "result": False, "comment": "", "changes": {}} + with patch.dict(zabbix_valuemap.__opts__, {"test": True}): + with patch.dict( + zabbix_valuemap.__salt__, + {"zabbix.get_object_id_by_params": MagicMock(return_value=11)}, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" would be deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" exists.'.format(name), + "new": 'Zabbix Value map "{0}" would be deleted.'.format(name), + } + } self.assertDictEqual(zabbix_valuemap.absent(name), ret) def test_absent(self): - ''' + """ Test to ensure that named value map is absent - ''' - name = 'Server HP Health' - ret = {'name': name, 'result': False, 'comment': '', 'changes': {}} - with patch.dict(zabbix_valuemap.__opts__, {'test': False}): - with patch.dict(zabbix_valuemap.__salt__, - {'zabbix.get_object_id_by_params': MagicMock(return_value=False)}): - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" does not exist.'.format(name) + """ + name = "Server HP Health" + ret = {"name": name, "result": False, "comment": "", "changes": {}} + with patch.dict(zabbix_valuemap.__opts__, {"test": False}): + with patch.dict( + zabbix_valuemap.__salt__, + {"zabbix.get_object_id_by_params": MagicMock(return_value=False)}, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" does not exist.'.format(name) self.assertDictEqual(zabbix_valuemap.absent(name), ret) - with patch.dict(zabbix_valuemap.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}): - with patch.dict(zabbix_valuemap.__salt__, {'zabbix.run_query': MagicMock(return_value=True)}): - ret['result'] = True - ret['comment'] = 'Zabbix Value map "{0}" deleted.'.format(name) - ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" existed.'.format(name), - 'new': 'Zabbix Value map "{0}" deleted.'.format(name)}} + with patch.dict( + zabbix_valuemap.__salt__, + {"zabbix.get_object_id_by_params": MagicMock(return_value=11)}, + ): + with patch.dict( + zabbix_valuemap.__salt__, + {"zabbix.run_query": MagicMock(return_value=True)}, + ): + ret["result"] = True + ret["comment"] = 'Zabbix Value map "{0}" deleted.'.format(name) + ret["changes"] = { + name: { + "old": 'Zabbix Value map "{0}" existed.'.format(name), + "new": 'Zabbix Value map "{0}" deleted.'.format(name), + } + } self.assertDictEqual(zabbix_valuemap.absent(name), ret) diff --git a/tests/unit/states/test_zcbuildout.py b/tests/unit/states/test_zcbuildout.py index 6ef7017ac9f..2b1398a4b69 100644 --- a/tests/unit/states/test_zcbuildout.py +++ b/tests/unit/states/test_zcbuildout.py @@ -1,37 +1,40 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os +import salt.modules.cmdmod as cmd +import salt.modules.virtualenv_mod +import salt.modules.zcbuildout as modbuildout +import salt.states.zcbuildout as buildout + +# Import Salt libs +import salt.utils.path + # Import Salt Testing libs from tests.support.helpers import requires_network from tests.support.runtests import RUNTIME_VARS from tests.support.unit import skipIf -from tests.unit.modules.test_zcbuildout import Base, KNOWN_VIRTUALENV_BINARY_NAMES - -# Import Salt libs -import salt.utils.path -import salt.modules.zcbuildout as modbuildout -import salt.modules.virtualenv_mod -import salt.states.zcbuildout as buildout -import salt.modules.cmdmod as cmd +from tests.unit.modules.test_zcbuildout import KNOWN_VIRTUALENV_BINARY_NAMES, Base -@skipIf(salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES) is None, - "The 'virtualenv' packaged needs to be installed") +@skipIf( + salt.utils.path.which_bin(KNOWN_VIRTUALENV_BINARY_NAMES) is None, + "The 'virtualenv' packaged needs to be installed", +) class BuildoutTestCase(Base): - def setup_loader_modules(self): module_globals = { - '__env__': 'base', - '__opts__': {'test': False}, - '__salt__': { - 'cmd.run_all': cmd.run_all, - 'cmd.run': cmd.run, - 'cmd.retcode': cmd.retcode, - 'buildout.buildout': modbuildout.buildout, - } + "__env__": "base", + "__opts__": {"test": False}, + "__salt__": { + "cmd.run_all": cmd.run_all, + "cmd.run": cmd.run, + "cmd.retcode": cmd.retcode, + "buildout.buildout": modbuildout.buildout, + }, } return {buildout: module_globals, modbuildout: module_globals} @@ -41,43 +44,44 @@ class BuildoutTestCase(Base): @requires_network() @skipIf(True, "Buildout is still in beta. Test needs fixing.") def test_quiet(self): - c_dir = os.path.join(self.tdir, 'c') + c_dir = os.path.join(self.tdir, "c") assert False, os.listdir(self.rdir) modbuildout.upgrade_bootstrap(c_dir) cret = buildout.installed(c_dir, python=self.py_st) - self.assertFalse('OUTPUT:' in cret['comment'], cret['comment']) - self.assertFalse('Log summary:' in cret['comment'], cret['comment']) - self.assertTrue(cret['result'], cret['comment']) + self.assertFalse("OUTPUT:" in cret["comment"], cret["comment"]) + self.assertFalse("Log summary:" in cret["comment"], cret["comment"]) + self.assertTrue(cret["result"], cret["comment"]) @requires_network() def test_error(self): - b_dir = os.path.join(self.tdir, 'e') + b_dir = os.path.join(self.tdir, "e") ret = buildout.installed(b_dir, python=self.py_st) self.assertTrue( - 'We did not get any expectable answer from buildout' - in ret['comment'] + "We did not get any expectable answer from buildout" in ret["comment"] ) - self.assertFalse(ret['result']) + self.assertFalse(ret["result"]) @requires_network() def test_installed(self): if salt.modules.virtualenv_mod.virtualenv_ver(self.ppy_st) >= (20, 0, 0): - self.skipTest("Skiping until upstream resolved https://github.com/pypa/virtualenv/issues/1715") - b_dir = os.path.join(self.tdir, 'b') - ret = buildout.installed(b_dir, - python=self.py_st, - onlyif=RUNTIME_VARS.SHELL_FALSE_PATH) - self.assertEqual(ret['comment'], '\nonlyif condition is false') - self.assertEqual(ret['result'], True) - self.assertTrue(os.sep + 'b' in ret['name']) - b_dir = os.path.join(self.tdir, 'b') - ret = buildout.installed(b_dir, - python=self.py_st, - unless=RUNTIME_VARS.SHELL_TRUE_PATH) - self.assertEqual(ret['comment'], '\nunless condition is true') - self.assertEqual(ret['result'], True) - self.assertTrue(os.sep + 'b' in ret['name']) + self.skipTest( + "Skiping until upstream resolved https://github.com/pypa/virtualenv/issues/1715" + ) + b_dir = os.path.join(self.tdir, "b") + ret = buildout.installed( + b_dir, python=self.py_st, onlyif=RUNTIME_VARS.SHELL_FALSE_PATH + ) + self.assertEqual(ret["comment"], "\nonlyif condition is false") + self.assertEqual(ret["result"], True) + self.assertTrue(os.sep + "b" in ret["name"]) + b_dir = os.path.join(self.tdir, "b") + ret = buildout.installed( + b_dir, python=self.py_st, unless=RUNTIME_VARS.SHELL_TRUE_PATH + ) + self.assertEqual(ret["comment"], "\nunless condition is true") + self.assertEqual(ret["result"], True) + self.assertTrue(os.sep + "b" in ret["name"]) ret = buildout.installed(b_dir, python=self.py_st) - self.assertEqual(ret['result'], True) - self.assertTrue('OUTPUT:' in ret['comment']) - self.assertTrue('Log summary:' in ret['comment']) + self.assertEqual(ret["result"], True) + self.assertTrue("OUTPUT:" in ret["comment"]) + self.assertTrue("Log summary:" in ret["comment"]) diff --git a/tests/unit/states/test_zfs.py b/tests/unit/states/test_zfs.py index 6504bebb19a..f8883b82bd7 100644 --- a/tests/unit/states/test_zfs.py +++ b/tests/unit/states/test_zfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.states.zfs :codeauthor: Jorge Schrauwen <sjorge@blackdot.be> @@ -7,688 +7,898 @@ Tests for salt.states.zfs :maturity: new :depends: salt.utils.zfs, salt.modules.zfs :platform: illumos,freebsd,linux -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.zfs import ZFSMockData -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -# Import Salt Execution module to test -import salt.utils.zfs -import salt.states.zfs as zfs +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Utils import salt.loader +import salt.states.zfs as zfs + +# Import Salt Execution module to test +import salt.utils.zfs from salt.utils.odict import OrderedDict +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +# Import Salt Testing Libs +from tests.support.zfs import ZFSMockData class ZfsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.zfs - ''' + """ + def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() self.utils_patch = ZFSMockData().get_patched_utils() - for key in ('opts', 'utils_patch'): + for key in ("opts", "utils_patch"): self.addCleanup(delattr, self, key) - utils = salt.loader.utils(opts, whitelist=['zfs']) + utils = salt.loader.utils(opts, whitelist=["zfs"]) zfs_obj = { zfs: { - '__opts__': opts, - '__grains__': {'kernel': 'SunOS'}, - '__utils__': utils, + "__opts__": opts, + "__grains__": {"kernel": "SunOS"}, + "__utils__": utils, } } return zfs_obj def test_filesystem_absent_nofs(self): - ''' + """ Test if filesystem is absent (non existing filesystem) - ''' - ret = {'name': 'myzpool/filesystem', - 'result': True, - 'comment': 'filesystem myzpool/filesystem is absent', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem", + "result": True, + "comment": "filesystem myzpool/filesystem is absent", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.filesystem_absent('myzpool/filesystem')) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.filesystem_absent("myzpool/filesystem")) def test_filesystem_absent_removed(self): - ''' + """ Test if filesystem is absent - ''' - ret = {'name': 'myzpool/filesystem', - 'result': True, - 'comment': 'filesystem myzpool/filesystem was destroyed', - 'changes': {'myzpool/filesystem': 'destroyed'}} + """ + ret = { + "name": "myzpool/filesystem", + "result": True, + "comment": "filesystem myzpool/filesystem was destroyed", + "changes": {"myzpool/filesystem": "destroyed"}, + } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([('destroyed', True)])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.filesystem_absent('myzpool/filesystem')) + mock_destroy = MagicMock(return_value=OrderedDict([("destroyed", True)])) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.destroy": mock_destroy} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.filesystem_absent("myzpool/filesystem")) def test_filesystem_absent_fail(self): - ''' + """ Test if filesystem is absent (with snapshots) - ''' - ret = {'name': 'myzpool/filesystem', - 'result': False, - 'comment': "\n".join([ + """ + ret = { + "name": "myzpool/filesystem", + "result": False, + "comment": "\n".join( + [ "cannot destroy 'myzpool/filesystem': filesystem has children", "use 'recursive=True' to destroy the following datasets:", "myzpool/filesystem@snap", - ]), - 'changes': {}} + ] + ), + "changes": {}, + } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([ - ('destroyed', False), - ('error', "\n".join([ - "cannot destroy 'myzpool/filesystem': filesystem has children", - "use 'recursive=True' to destroy the following datasets:", - "myzpool/filesystem@snap", - ])), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.filesystem_absent('myzpool/filesystem')) + mock_destroy = MagicMock( + return_value=OrderedDict( + [ + ("destroyed", False), + ( + "error", + "\n".join( + [ + "cannot destroy 'myzpool/filesystem': filesystem has children", + "use 'recursive=True' to destroy the following datasets:", + "myzpool/filesystem@snap", + ] + ), + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.destroy": mock_destroy} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.filesystem_absent("myzpool/filesystem")) def test_volume_absent_novol(self): - ''' + """ Test if volume is absent (non existing volume) - ''' - ret = {'name': 'myzpool/volume', - 'result': True, - 'comment': 'volume myzpool/volume is absent', - 'changes': {}} + """ + ret = { + "name": "myzpool/volume", + "result": True, + "comment": "volume myzpool/volume is absent", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.volume_absent('myzpool/volume')) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.volume_absent("myzpool/volume")) def test_volume_absent_removed(self): - ''' + """ Test if volume is absent - ''' - ret = {'name': 'myzpool/volume', - 'result': True, - 'comment': 'volume myzpool/volume was destroyed', - 'changes': {'myzpool/volume': 'destroyed'}} + """ + ret = { + "name": "myzpool/volume", + "result": True, + "comment": "volume myzpool/volume was destroyed", + "changes": {"myzpool/volume": "destroyed"}, + } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([('destroyed', True)])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.volume_absent('myzpool/volume')) + mock_destroy = MagicMock(return_value=OrderedDict([("destroyed", True)])) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.destroy": mock_destroy} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.volume_absent("myzpool/volume")) def test_volume_absent_fail(self): - ''' + """ Test if volume is absent (with snapshots) - ''' - ret = {'name': 'myzpool/volume', - 'result': False, - 'comment': "\n".join([ + """ + ret = { + "name": "myzpool/volume", + "result": False, + "comment": "\n".join( + [ "cannot destroy 'myzpool/volume': volume has children", "use 'recursive=True' to destroy the following datasets:", "myzpool/volume@snap", - ]), - 'changes': {}} + ] + ), + "changes": {}, + } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([ - ('destroyed', False), - ('error', "\n".join([ - "cannot destroy 'myzpool/volume': volume has children", - "use 'recursive=True' to destroy the following datasets:", - "myzpool/volume@snap", - ])), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.volume_absent('myzpool/volume')) + mock_destroy = MagicMock( + return_value=OrderedDict( + [ + ("destroyed", False), + ( + "error", + "\n".join( + [ + "cannot destroy 'myzpool/volume': volume has children", + "use 'recursive=True' to destroy the following datasets:", + "myzpool/volume@snap", + ] + ), + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.destroy": mock_destroy} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.volume_absent("myzpool/volume")) def test_snapshot_absent_nosnap(self): - ''' + """ Test if snapshot is absent (non existing snapshot) - ''' - ret = {'name': 'myzpool/filesystem@snap', - 'result': True, - 'comment': 'snapshot myzpool/filesystem@snap is absent', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem@snap", + "result": True, + "comment": "snapshot myzpool/filesystem@snap is absent", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.snapshot_absent('myzpool/filesystem@snap')) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.snapshot_absent("myzpool/filesystem@snap")) def test_snapshot_absent_removed(self): - ''' + """ Test if snapshot is absent - ''' - ret = {'name': 'myzpool/filesystem@snap', - 'result': True, - 'comment': 'snapshot myzpool/filesystem@snap was destroyed', - 'changes': {'myzpool/filesystem@snap': 'destroyed'}} + """ + ret = { + "name": "myzpool/filesystem@snap", + "result": True, + "comment": "snapshot myzpool/filesystem@snap was destroyed", + "changes": {"myzpool/filesystem@snap": "destroyed"}, + } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([('destroyed', True)])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.snapshot_absent('myzpool/filesystem@snap')) + mock_destroy = MagicMock(return_value=OrderedDict([("destroyed", True)])) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.destroy": mock_destroy} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.snapshot_absent("myzpool/filesystem@snap")) def test_snapshot_absent_fail(self): - ''' + """ Test if snapshot is absent (with snapshots) - ''' - ret = {'name': 'myzpool/filesystem@snap', - 'result': False, - 'comment': 'cannot destroy snapshot myzpool/filesystem@snap: dataset is busy', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem@snap", + "result": False, + "comment": "cannot destroy snapshot myzpool/filesystem@snap: dataset is busy", + "changes": {}, + } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([ - ('destroyed', False), - ('error', 'cannot destroy snapshot myzpool/filesystem@snap: dataset is busy'), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.snapshot_absent('myzpool/filesystem@snap')) + mock_destroy = MagicMock( + return_value=OrderedDict( + [ + ("destroyed", False), + ( + "error", + "cannot destroy snapshot myzpool/filesystem@snap: dataset is busy", + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.destroy": mock_destroy} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.snapshot_absent("myzpool/filesystem@snap")) def test_bookmark_absent_nobook(self): - ''' + """ Test if bookmark is absent (non existing bookmark) - ''' - ret = {'name': 'myzpool/filesystem#book', - 'result': True, - 'comment': 'bookmark myzpool/filesystem#book is absent', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem#book", + "result": True, + "comment": "bookmark myzpool/filesystem#book is absent", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.bookmark_absent('myzpool/filesystem#book')) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.bookmark_absent("myzpool/filesystem#book")) def test_bookmark_absent_removed(self): - ''' + """ Test if bookmark is absent - ''' - ret = {'name': 'myzpool/filesystem#book', - 'result': True, - 'comment': 'bookmark myzpool/filesystem#book was destroyed', - 'changes': {'myzpool/filesystem#book': 'destroyed'}} + """ + ret = { + "name": "myzpool/filesystem#book", + "result": True, + "comment": "bookmark myzpool/filesystem#book was destroyed", + "changes": {"myzpool/filesystem#book": "destroyed"}, + } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([('destroyed', True)])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.destroy': mock_destroy}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.bookmark_absent('myzpool/filesystem#book')) + mock_destroy = MagicMock(return_value=OrderedDict([("destroyed", True)])) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.destroy": mock_destroy} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.bookmark_absent("myzpool/filesystem#book")) def test_hold_absent_nohold(self): - ''' + """ Test if hold is absent (non existing hold) - ''' - ret = {'name': 'myhold', - 'result': True, - 'comment': 'hold myhold is absent', - 'changes': {}} + """ + ret = { + "name": "myhold", + "result": True, + "comment": "hold myhold is absent", + "changes": {}, + } mock_holds = MagicMock(return_value=OrderedDict([])) - with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.hold_absent('myhold', 'myzpool/filesystem@snap')) + with patch.dict(zfs.__salt__, {"zfs.holds": mock_holds}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.hold_absent("myhold", "myzpool/filesystem@snap")) def test_hold_absent_removed(self): - ''' + """ Test if hold is absent - ''' - ret = {'name': 'myhold', - 'result': True, - 'comment': 'hold myhold released', - 'changes': OrderedDict([ - ('myzpool/filesystem@snap', OrderedDict([ - ('myhold', 'released'), - ])), - ])} + """ + ret = { + "name": "myhold", + "result": True, + "comment": "hold myhold released", + "changes": OrderedDict( + [("myzpool/filesystem@snap", OrderedDict([("myhold", "released")]))] + ), + } - mock_holds = MagicMock(return_value=OrderedDict([('myhold', 'Thu Feb 15 16:24 2018')])) - mock_release = MagicMock(return_value=OrderedDict([('released', True)])) - with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ - patch.dict(zfs.__salt__, {'zfs.release': mock_release}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.hold_absent('myhold', 'myzpool/filesystem@snap')) + mock_holds = MagicMock( + return_value=OrderedDict([("myhold", "Thu Feb 15 16:24 2018")]) + ) + mock_release = MagicMock(return_value=OrderedDict([("released", True)])) + with patch.dict(zfs.__salt__, {"zfs.holds": mock_holds}), patch.dict( + zfs.__salt__, {"zfs.release": mock_release} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.hold_absent("myhold", "myzpool/filesystem@snap")) def test_hold_absent_fail(self): - ''' + """ Test if hold is absent (non existing snapshot) - ''' - ret = {'name': 'myhold', - 'result': False, - 'comment': "cannot open 'myzpool/filesystem@snap': dataset does not exist", - 'changes': {}} + """ + ret = { + "name": "myhold", + "result": False, + "comment": "cannot open 'myzpool/filesystem@snap': dataset does not exist", + "changes": {}, + } - mock_holds = MagicMock(return_value=OrderedDict([ - ('error', "cannot open 'myzpool/filesystem@snap': dataset does not exist"), - ])) - with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.hold_absent('myhold', 'myzpool/filesystem@snap')) + mock_holds = MagicMock( + return_value=OrderedDict( + [ + ( + "error", + "cannot open 'myzpool/filesystem@snap': dataset does not exist", + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.holds": mock_holds}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.hold_absent("myhold", "myzpool/filesystem@snap")) def test_hold_present(self): - ''' + """ Test if hold is present (hold already present) - ''' - ret = {'name': 'myhold', - 'result': True, - 'comment': 'hold myhold is present for myzpool/filesystem@snap', - 'changes': {}} + """ + ret = { + "name": "myhold", + "result": True, + "comment": "hold myhold is present for myzpool/filesystem@snap", + "changes": {}, + } - mock_holds = MagicMock(return_value=OrderedDict([('myhold', 'Thu Feb 15 16:24 2018')])) - with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.hold_present('myhold', 'myzpool/filesystem@snap')) + mock_holds = MagicMock( + return_value=OrderedDict([("myhold", "Thu Feb 15 16:24 2018")]) + ) + with patch.dict(zfs.__salt__, {"zfs.holds": mock_holds}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.hold_present("myhold", "myzpool/filesystem@snap")) def test_hold_present_new(self): - ''' + """ Test if hold is present (new) - ''' - ret = {'name': 'myhold', - 'result': True, - 'comment': 'hold myhold added to myzpool/filesystem@snap', - 'changes': {'myzpool/filesystem@snap': {'myhold': 'held'}}} + """ + ret = { + "name": "myhold", + "result": True, + "comment": "hold myhold added to myzpool/filesystem@snap", + "changes": {"myzpool/filesystem@snap": {"myhold": "held"}}, + } mock_holds = MagicMock(return_value=OrderedDict([])) - mock_hold = MagicMock(return_value=OrderedDict([('held', True)])) - with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ - patch.dict(zfs.__salt__, {'zfs.hold': mock_hold}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.hold_present('myhold', 'myzpool/filesystem@snap')) + mock_hold = MagicMock(return_value=OrderedDict([("held", True)])) + with patch.dict(zfs.__salt__, {"zfs.holds": mock_holds}), patch.dict( + zfs.__salt__, {"zfs.hold": mock_hold} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.hold_present("myhold", "myzpool/filesystem@snap")) def test_hold_present_fail(self): - ''' + """ Test if hold is present (using non existing snapshot) - ''' - ret = {'name': 'myhold', - 'result': False, - 'comment': "cannot hold snapshot 'zsalt/filesystem@snap': dataset does not exist", - 'changes': {}} + """ + ret = { + "name": "myhold", + "result": False, + "comment": "cannot hold snapshot 'zsalt/filesystem@snap': dataset does not exist", + "changes": {}, + } mock_holds = MagicMock(return_value=OrderedDict([])) - mock_hold = MagicMock(return_value=OrderedDict([ - ('held', False), - ('error', "cannot hold snapshot 'zsalt/filesystem@snap': dataset does not exist"), - ])) - with patch.dict(zfs.__salt__, {'zfs.holds': mock_holds}), \ - patch.dict(zfs.__salt__, {'zfs.hold': mock_hold}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.hold_present('myhold', 'myzpool/filesystem@snap')) + mock_hold = MagicMock( + return_value=OrderedDict( + [ + ("held", False), + ( + "error", + "cannot hold snapshot 'zsalt/filesystem@snap': dataset does not exist", + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.holds": mock_holds}), patch.dict( + zfs.__salt__, {"zfs.hold": mock_hold} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.hold_present("myhold", "myzpool/filesystem@snap")) def test_filesystem_present(self): - ''' + """ Test if filesystem is present (existing filesystem) - ''' - ret = {'name': 'myzpool/filesystem', - 'result': True, - 'comment': 'filesystem myzpool/filesystem is uptodate', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem", + "result": True, + "comment": "filesystem myzpool/filesystem is uptodate", + "changes": {}, + } mock_exists = MagicMock(return_value=True) - mock_get = MagicMock(return_value=OrderedDict([ - ('myzpool/filesystem', OrderedDict([ - ('type', OrderedDict([ - ('value', 'filesystem'), - ])), - ('compression', OrderedDict([ - ('value', False), - ])), - ])), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.filesystem_present('myzpool/filesystem')) + mock_get = MagicMock( + return_value=OrderedDict( + [ + ( + "myzpool/filesystem", + OrderedDict( + [ + ("type", OrderedDict([("value", "filesystem")])), + ("compression", OrderedDict([("value", False)])), + ] + ), + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.get": mock_get} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.filesystem_present("myzpool/filesystem")) def test_filesystem_present_new(self): - ''' + """ Test if filesystem is present (non existing filesystem) - ''' - ret = {'name': 'myzpool/filesystem', - 'result': True, - 'comment': 'filesystem myzpool/filesystem was created', - 'changes': {'myzpool/filesystem': u'created'}} + """ + ret = { + "name": "myzpool/filesystem", + "result": True, + "comment": "filesystem myzpool/filesystem was created", + "changes": {"myzpool/filesystem": "created"}, + } mock_exists = MagicMock(return_value=False) - mock_create = MagicMock(return_value=OrderedDict([('created', True)])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.create': mock_create}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.filesystem_present('myzpool/filesystem')) + mock_create = MagicMock(return_value=OrderedDict([("created", True)])) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.create": mock_create} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.filesystem_present("myzpool/filesystem")) def test_filesystem_present_update(self): - ''' + """ Test if filesystem is present (non existing filesystem) - ''' - ret = {'name': 'myzpool/filesystem', - 'result': True, - 'comment': 'filesystem myzpool/filesystem was updated', - 'changes': {'myzpool/filesystem': {'compression': 'lz4'}}} + """ + ret = { + "name": "myzpool/filesystem", + "result": True, + "comment": "filesystem myzpool/filesystem was updated", + "changes": {"myzpool/filesystem": {"compression": "lz4"}}, + } mock_exists = MagicMock(return_value=True) - mock_set = MagicMock(return_value=OrderedDict([('set', True)])) - mock_get = MagicMock(return_value=OrderedDict([ - ('myzpool/filesystem', OrderedDict([ - ('type', OrderedDict([ - ('value', 'filesystem'), - ])), - ('compression', OrderedDict([ - ('value', False), - ])), - ])), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ - patch.dict(zfs.__salt__, {'zfs.set': mock_set}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.filesystem_present( - name='myzpool/filesystem', - properties={'compression': 'lz4'}, - )) + mock_set = MagicMock(return_value=OrderedDict([("set", True)])) + mock_get = MagicMock( + return_value=OrderedDict( + [ + ( + "myzpool/filesystem", + OrderedDict( + [ + ("type", OrderedDict([("value", "filesystem")])), + ("compression", OrderedDict([("value", False)])), + ] + ), + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.get": mock_get} + ), patch.dict(zfs.__salt__, {"zfs.set": mock_set}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + ret, + zfs.filesystem_present( + name="myzpool/filesystem", properties={"compression": "lz4"}, + ), + ) def test_filesystem_present_fail(self): - ''' + """ Test if filesystem is present (non existing pool) - ''' - ret = {'name': 'myzpool/filesystem', - 'result': False, - 'comment': "cannot create 'myzpool/filesystem': no such pool 'myzpool'", - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem", + "result": False, + "comment": "cannot create 'myzpool/filesystem': no such pool 'myzpool'", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - mock_create = MagicMock(return_value=OrderedDict([ - ('created', False), - ('error', "cannot create 'myzpool/filesystem': no such pool 'myzpool'"), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.create': mock_create}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.filesystem_present('myzpool/filesystem')) + mock_create = MagicMock( + return_value=OrderedDict( + [ + ("created", False), + ( + "error", + "cannot create 'myzpool/filesystem': no such pool 'myzpool'", + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.create": mock_create} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.filesystem_present("myzpool/filesystem")) def test_volume_present(self): - ''' + """ Test if volume is present (existing volume) - ''' - ret = {'name': 'myzpool/volume', - 'result': True, - 'comment': 'volume myzpool/volume is uptodate', - 'changes': {}} + """ + ret = { + "name": "myzpool/volume", + "result": True, + "comment": "volume myzpool/volume is uptodate", + "changes": {}, + } mock_exists = MagicMock(return_value=True) - mock_get = MagicMock(return_value=OrderedDict([ - ('myzpool/volume', OrderedDict([ - ('type', OrderedDict([ - ('value', 'volume'), - ])), - ('compression', OrderedDict([ - ('value', False), - ])), - ])), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.volume_present('myzpool/volume', volume_size='1G')) + mock_get = MagicMock( + return_value=OrderedDict( + [ + ( + "myzpool/volume", + OrderedDict( + [ + ("type", OrderedDict([("value", "volume")])), + ("compression", OrderedDict([("value", False)])), + ] + ), + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.get": mock_get} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual( + ret, zfs.volume_present("myzpool/volume", volume_size="1G") + ) def test_volume_present_new(self): - ''' + """ Test if volume is present (non existing volume) - ''' - ret = {'name': 'myzpool/volume', - 'result': True, - 'comment': 'volume myzpool/volume was created', - 'changes': {'myzpool/volume': u'created'}} + """ + ret = { + "name": "myzpool/volume", + "result": True, + "comment": "volume myzpool/volume was created", + "changes": {"myzpool/volume": "created"}, + } mock_exists = MagicMock(return_value=False) - mock_create = MagicMock(return_value=OrderedDict([('created', True)])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.create': mock_create}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.volume_present('myzpool/volume', volume_size='1G')) + mock_create = MagicMock(return_value=OrderedDict([("created", True)])) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.create": mock_create} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual( + ret, zfs.volume_present("myzpool/volume", volume_size="1G") + ) def test_volume_present_update(self): - ''' + """ Test if volume is present (non existing volume) - ''' - ret = {'name': 'myzpool/volume', - 'result': True, - 'comment': 'volume myzpool/volume was updated', - 'changes': {'myzpool/volume': {'compression': 'lz4'}}} + """ + ret = { + "name": "myzpool/volume", + "result": True, + "comment": "volume myzpool/volume was updated", + "changes": {"myzpool/volume": {"compression": "lz4"}}, + } mock_exists = MagicMock(return_value=True) - mock_set = MagicMock(return_value=OrderedDict([('set', True)])) - mock_get = MagicMock(return_value=OrderedDict([ - ('myzpool/volume', OrderedDict([ - ('type', OrderedDict([ - ('value', 'volume'), - ])), - ('compression', OrderedDict([ - ('value', False), - ])), - ])), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ - patch.dict(zfs.__salt__, {'zfs.set': mock_set}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.volume_present( - name='myzpool/volume', - volume_size='1G', - properties={'compression': 'lz4'}, - )) + mock_set = MagicMock(return_value=OrderedDict([("set", True)])) + mock_get = MagicMock( + return_value=OrderedDict( + [ + ( + "myzpool/volume", + OrderedDict( + [ + ("type", OrderedDict([("value", "volume")])), + ("compression", OrderedDict([("value", False)])), + ] + ), + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.get": mock_get} + ), patch.dict(zfs.__salt__, {"zfs.set": mock_set}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + ret, + zfs.volume_present( + name="myzpool/volume", + volume_size="1G", + properties={"compression": "lz4"}, + ), + ) def test_volume_present_fail(self): - ''' + """ Test if volume is present (non existing pool) - ''' - ret = {'name': 'myzpool/volume', - 'result': False, - 'comment': "cannot create 'myzpool/volume': no such pool 'myzpool'", - 'changes': {}} + """ + ret = { + "name": "myzpool/volume", + "result": False, + "comment": "cannot create 'myzpool/volume': no such pool 'myzpool'", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - mock_create = MagicMock(return_value=OrderedDict([ - ('created', False), - ('error', "cannot create 'myzpool/volume': no such pool 'myzpool'"), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.create': mock_create}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.volume_present('myzpool/volume', volume_size='1G')) + mock_create = MagicMock( + return_value=OrderedDict( + [ + ("created", False), + ("error", "cannot create 'myzpool/volume': no such pool 'myzpool'"), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.create": mock_create} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual( + ret, zfs.volume_present("myzpool/volume", volume_size="1G") + ) def test_bookmark_present(self): - ''' + """ Test if bookmark is present (bookmark already present) - ''' - ret = {'name': 'myzpool/filesystem#mybookmark', - 'result': True, - 'comment': 'bookmark is present', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem#mybookmark", + "result": True, + "comment": "bookmark is present", + "changes": {}, + } mock_exists = MagicMock(return_value=True) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.bookmark_present('mybookmark', 'myzpool/filesystem@snap')) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + ret, zfs.bookmark_present("mybookmark", "myzpool/filesystem@snap") + ) def test_bookmark_present_new(self): - ''' + """ Test if bookmark is present (new) - ''' - ret = {'name': 'myzpool/filesystem#mybookmark', - 'result': True, - 'comment': 'myzpool/filesystem@snap bookmarked as myzpool/filesystem#mybookmark', - 'changes': {'myzpool/filesystem#mybookmark': 'myzpool/filesystem@snap'}} + """ + ret = { + "name": "myzpool/filesystem#mybookmark", + "result": True, + "comment": "myzpool/filesystem@snap bookmarked as myzpool/filesystem#mybookmark", + "changes": {"myzpool/filesystem#mybookmark": "myzpool/filesystem@snap"}, + } mock_exists = MagicMock(return_value=False) - mock_bookmark = MagicMock(return_value=OrderedDict([('bookmarked', True)])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.bookmark': mock_bookmark}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.bookmark_present('mybookmark', 'myzpool/filesystem@snap')) + mock_bookmark = MagicMock(return_value=OrderedDict([("bookmarked", True)])) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.bookmark": mock_bookmark} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual( + ret, zfs.bookmark_present("mybookmark", "myzpool/filesystem@snap") + ) def test_bookmark_present_fail(self): - ''' + """ Test if bookmark is present (using non existing snapshot) - ''' - ret = {'name': 'myzpool/filesystem#mybookmark', - 'result': False, - 'comment': "cannot bookmark snapshot 'zsalt/filesystem@snap': dataset does not exist", - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem#mybookmark", + "result": False, + "comment": "cannot bookmark snapshot 'zsalt/filesystem@snap': dataset does not exist", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - mock_bookmark = MagicMock(return_value=OrderedDict([ - ('bookmarked', False), - ('error', "cannot bookmark snapshot 'zsalt/filesystem@snap': dataset does not exist"), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.bookmark': mock_bookmark}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.bookmark_present('mybookmark', 'myzpool/filesystem@snap')) + mock_bookmark = MagicMock( + return_value=OrderedDict( + [ + ("bookmarked", False), + ( + "error", + "cannot bookmark snapshot 'zsalt/filesystem@snap': dataset does not exist", + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.bookmark": mock_bookmark} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual( + ret, zfs.bookmark_present("mybookmark", "myzpool/filesystem@snap") + ) def test_snapshot_present(self): - ''' + """ Test if snapshot is present (snapshot already present) - ''' - ret = {'name': 'myzpool/filesystem@snap', - 'result': True, - 'comment': 'snapshot is present', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem@snap", + "result": True, + "comment": "snapshot is present", + "changes": {}, + } mock_exists = MagicMock(return_value=True) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.snapshot_present('myzpool/filesystem@snap')) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.snapshot_present("myzpool/filesystem@snap")) def test_snapshot_present_new(self): - ''' + """ Test if snapshot is present (new) - ''' - ret = {'name': 'myzpool/filesystem@snap', - 'result': True, - 'comment': 'snapshot myzpool/filesystem@snap was created', - 'changes': {u'myzpool/filesystem@snap': u'snapshotted'}} + """ + ret = { + "name": "myzpool/filesystem@snap", + "result": True, + "comment": "snapshot myzpool/filesystem@snap was created", + "changes": {"myzpool/filesystem@snap": "snapshotted"}, + } mock_exists = MagicMock(return_value=False) - mock_snapshot = MagicMock(return_value=OrderedDict([('snapshotted', True)])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.snapshot': mock_snapshot}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.snapshot_present('myzpool/filesystem@snap')) + mock_snapshot = MagicMock(return_value=OrderedDict([("snapshotted", True)])) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.snapshot": mock_snapshot} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.snapshot_present("myzpool/filesystem@snap")) def test_snapshot_present_fail(self): - ''' + """ Test if snapshot is present (using non existing snapshot) - ''' - ret = {'name': 'myzpool/filesystem@snap', - 'result': False, - 'comment': "cannot open 'myzpool/filesystem': dataset does not exist", - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem@snap", + "result": False, + "comment": "cannot open 'myzpool/filesystem': dataset does not exist", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - mock_snapshot = MagicMock(return_value=OrderedDict([ - ('snapshotted', False), - ('error', "cannot open 'myzpool/filesystem': dataset does not exist"), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.snapshot': mock_snapshot}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.snapshot_present('myzpool/filesystem@snap')) + mock_snapshot = MagicMock( + return_value=OrderedDict( + [ + ("snapshotted", False), + ( + "error", + "cannot open 'myzpool/filesystem': dataset does not exist", + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.snapshot": mock_snapshot} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.snapshot_present("myzpool/filesystem@snap")) def test_propmoted(self): - ''' + """ Test promotion of clone (already promoted) - ''' - ret = {'name': 'myzpool/filesystem', - 'result': True, - 'comment': 'myzpool/filesystem already promoted', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem", + "result": True, + "comment": "myzpool/filesystem already promoted", + "changes": {}, + } mock_exists = MagicMock(return_value=True) - mock_get = MagicMock(return_value=OrderedDict([ - ('myzpool/filesystem', OrderedDict([ - ('origin', OrderedDict([ - ('value', '-'), - ])), - ])), - ])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.promoted('myzpool/filesystem')) + mock_get = MagicMock( + return_value=OrderedDict( + [ + ( + "myzpool/filesystem", + OrderedDict([("origin", OrderedDict([("value", "-")]))]), + ), + ] + ) + ) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.get": mock_get} + ), patch.dict(zfs.__utils__, self.utils_patch): + self.assertEqual(ret, zfs.promoted("myzpool/filesystem")) def test_propmoted_clone(self): - ''' + """ Test promotion of clone - ''' - ret = {'name': 'myzpool/filesystem', - 'result': True, - 'comment': 'myzpool/filesystem promoted', - 'changes': {'myzpool/filesystem': 'promoted'}} + """ + ret = { + "name": "myzpool/filesystem", + "result": True, + "comment": "myzpool/filesystem promoted", + "changes": {"myzpool/filesystem": "promoted"}, + } mock_exists = MagicMock(return_value=True) - mock_get = MagicMock(return_value=OrderedDict([ - ('myzpool/filesystem', OrderedDict([ - ('origin', OrderedDict([ - ('value', 'myzool/filesystem_source@clean'), - ])), - ])), - ])) - mock_promote = MagicMock(return_value=OrderedDict([('promoted', True)])) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__salt__, {'zfs.get': mock_get}), \ - patch.dict(zfs.__salt__, {'zfs.promote': mock_promote}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.promoted('myzpool/filesystem')) + mock_get = MagicMock( + return_value=OrderedDict( + [ + ( + "myzpool/filesystem", + OrderedDict( + [ + ( + "origin", + OrderedDict( + [("value", "myzool/filesystem_source@clean")] + ), + ), + ] + ), + ), + ] + ) + ) + mock_promote = MagicMock(return_value=OrderedDict([("promoted", True)])) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__salt__, {"zfs.get": mock_get} + ), patch.dict(zfs.__salt__, {"zfs.promote": mock_promote}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.promoted("myzpool/filesystem")) def test_propmoted_fail(self): - ''' + """ Test promotion of clone (unknown dataset) - ''' - ret = {'name': 'myzpool/filesystem', - 'result': False, - 'comment': 'dataset myzpool/filesystem does not exist', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem", + "result": False, + "comment": "dataset myzpool/filesystem does not exist", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.promoted('myzpool/filesystem')) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual(ret, zfs.promoted("myzpool/filesystem")) def test_scheduled_snapshot_fail(self): - ''' + """ Test scheduled_snapshot of unknown dataset - ''' - ret = {'name': 'myzpool/filesystem', - 'result': False, - 'comment': 'dataset myzpool/filesystem does not exist', - 'changes': {}} + """ + ret = { + "name": "myzpool/filesystem", + "result": False, + "comment": "dataset myzpool/filesystem does not exist", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - with patch.dict(zfs.__salt__, {'zfs.exists': mock_exists}), \ - patch.dict(zfs.__utils__, self.utils_patch): - self.assertEqual(ret, zfs.scheduled_snapshot('myzpool/filesystem', 'shadow', schedule={'hour': 6})) + with patch.dict(zfs.__salt__, {"zfs.exists": mock_exists}), patch.dict( + zfs.__utils__, self.utils_patch + ): + self.assertEqual( + ret, + zfs.scheduled_snapshot( + "myzpool/filesystem", "shadow", schedule={"hour": 6} + ), + ) diff --git a/tests/unit/states/test_zk_concurrency.py b/tests/unit/states/test_zk_concurrency.py index 0572e7e1c90..a26141f9b66 100644 --- a/tests/unit/states/test_zk_concurrency.py +++ b/tests/unit/states/test_zk_concurrency.py @@ -1,93 +1,91 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.states.zk_concurrency as zk_concurrency +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + class ZkConcurrencyTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Validate the zk_concurrency state - ''' + """ + def setup_loader_modules(self): return {zk_concurrency: {}} def test_lock(self): - ''' + """ Test to block state execution until you are able to get the lock - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} with patch.dict(zk_concurrency.__opts__, {"test": True}): - ret.update({'comment': 'Attempt to acquire lock', 'result': None}) - self.assertDictEqual(zk_concurrency.lock('salt', 'dude'), ret) + ret.update({"comment": "Attempt to acquire lock", "result": None}) + self.assertDictEqual(zk_concurrency.lock("salt", "dude"), ret) with patch.dict(zk_concurrency.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.dict(zk_concurrency.__salt__, - {"zk_concurrency.lock": mock}): - ret.update({'comment': 'lock acquired', 'result': True}) - self.assertDictEqual(zk_concurrency.lock('salt', 'dude', - 'stack'), ret) + with patch.dict(zk_concurrency.__salt__, {"zk_concurrency.lock": mock}): + ret.update({"comment": "lock acquired", "result": True}) + self.assertDictEqual(zk_concurrency.lock("salt", "dude", "stack"), ret) def test_unlock(self): - ''' + """ Test to remove lease from semaphore - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} with patch.dict(zk_concurrency.__opts__, {"test": True}): - ret.update({'comment': 'Released lock if it is here', - 'result': None}) - self.assertDictEqual(zk_concurrency.unlock('salt'), ret) + ret.update({"comment": "Released lock if it is here", "result": None}) + self.assertDictEqual(zk_concurrency.unlock("salt"), ret) with patch.dict(zk_concurrency.__opts__, {"test": False}): mock = MagicMock(return_value=True) - with patch.dict(zk_concurrency.__salt__, - {"zk_concurrency.unlock": mock}): - ret.update({'comment': '', 'result': True}) - self.assertDictEqual(zk_concurrency.unlock('salt', - identifier='stack'), - ret) + with patch.dict(zk_concurrency.__salt__, {"zk_concurrency.unlock": mock}): + ret.update({"comment": "", "result": True}) + self.assertDictEqual( + zk_concurrency.unlock("salt", identifier="stack"), ret + ) def test_min_party(self): - ''' + """ Test to ensure min party of nodes and the blocking behavior - ''' - ret = {'name': 'salt', - 'changes': {}, - 'result': True, - 'comment': ''} + """ + ret = {"name": "salt", "changes": {}, "result": True, "comment": ""} with patch.dict(zk_concurrency.__opts__, {"test": True}): - ret.update({'comment': 'Attempt to ensure min_party', 'result': None}) - self.assertDictEqual(zk_concurrency.min_party('salt', 'dude', 1), ret) + ret.update({"comment": "Attempt to ensure min_party", "result": None}) + self.assertDictEqual(zk_concurrency.min_party("salt", "dude", 1), ret) with patch.dict(zk_concurrency.__opts__, {"test": False}): - mock = MagicMock(return_value=['1', '2', '3']) - with patch.dict(zk_concurrency.__salt__, - {"zk_concurrency.party_members": mock}): - ret.update({'comment': 'Currently 3 nodes, which is >= 2', 'result': True}) - self.assertDictEqual(zk_concurrency.min_party('salt', 'dude', 2), ret) - ret.update({'comment': 'Blocked until 2 nodes were available. ' + - 'Unblocked after 3 nodes became available', 'result': True}) - self.assertDictEqual(zk_concurrency.min_party('salt', 'dude', 2, True), ret) - ret.update({'comment': 'Currently 3 nodes, which is < 4', 'result': False}) - self.assertDictEqual(zk_concurrency.min_party('salt', 'dude', 4), ret) + mock = MagicMock(return_value=["1", "2", "3"]) + with patch.dict( + zk_concurrency.__salt__, {"zk_concurrency.party_members": mock} + ): + ret.update( + {"comment": "Currently 3 nodes, which is >= 2", "result": True} + ) + self.assertDictEqual(zk_concurrency.min_party("salt", "dude", 2), ret) + ret.update( + { + "comment": "Blocked until 2 nodes were available. " + + "Unblocked after 3 nodes became available", + "result": True, + } + ) + self.assertDictEqual( + zk_concurrency.min_party("salt", "dude", 2, True), ret + ) + ret.update( + {"comment": "Currently 3 nodes, which is < 4", "result": False} + ) + self.assertDictEqual(zk_concurrency.min_party("salt", "dude", 4), ret) diff --git a/tests/unit/states/test_zpool.py b/tests/unit/states/test_zpool.py index 52cc544d100..ea8f7813cb9 100644 --- a/tests/unit/states/test_zpool.py +++ b/tests/unit/states/test_zpool.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.states.zpool :codeauthor: Jorge Schrauwen <sjorge@blackdot.be> @@ -7,209 +7,229 @@ Tests for salt.states.zpool :maturity: new :depends: salt.utils.zfs, salt.modules.zpool :platform: illumos,freebsd,linux -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.zfs import ZFSMockData -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch) - -# Import Salt Execution module to test -import salt.utils.zfs -import salt.states.zpool as zpool +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Utils import salt.loader +import salt.states.zpool as zpool + +# Import Salt Execution module to test +import salt.utils.zfs from salt.utils.odict import OrderedDict +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +# Import Salt Testing Libs +from tests.support.zfs import ZFSMockData class ZpoolTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.states.zpool - ''' + """ + def setup_loader_modules(self): self.opts = opts = salt.config.DEFAULT_MINION_OPTS.copy() self.utils_patch = ZFSMockData().get_patched_utils() - for key in ('opts', 'utils_patch'): + for key in ("opts", "utils_patch"): self.addCleanup(delattr, self, key) - utils = salt.loader.utils(opts, whitelist=['zfs']) + utils = salt.loader.utils(opts, whitelist=["zfs"]) zpool_obj = { zpool: { - '__opts__': opts, - '__grains__': {'kernel': 'SunOS'}, - '__utils__': utils, + "__opts__": opts, + "__grains__": {"kernel": "SunOS"}, + "__utils__": utils, } } return zpool_obj def test_absent_without_pool(self): - ''' + """ Test zpool absent without a pool - ''' - ret = {'name': 'myzpool', - 'result': True, - 'comment': 'storage pool myzpool is absent', - 'changes': {}} + """ + ret = { + "name": "myzpool", + "result": True, + "comment": "storage pool myzpool is absent", + "changes": {}, + } mock_exists = MagicMock(return_value=False) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__utils__, self.utils_patch): - self.assertEqual(zpool.absent('myzpool'), ret) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__utils__, self.utils_patch + ): + self.assertEqual(zpool.absent("myzpool"), ret) def test_absent_destroy_pool(self): - ''' + """ Test zpool absent destroying pool - ''' + """ ret = { - 'name': 'myzpool', - 'result': True, - 'comment': 'storage pool myzpool was destroyed', - 'changes': {'myzpool': 'destroyed'}, + "name": "myzpool", + "result": True, + "comment": "storage pool myzpool was destroyed", + "changes": {"myzpool": "destroyed"}, } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([ - ('destroyed', True), - ])) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'zpool.destroy': mock_destroy}), \ - patch.dict(zpool.__utils__, self.utils_patch): - self.assertEqual(zpool.absent('myzpool'), ret) + mock_destroy = MagicMock(return_value=OrderedDict([("destroyed", True)])) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.destroy": mock_destroy} + ), patch.dict(zpool.__utils__, self.utils_patch): + self.assertEqual(zpool.absent("myzpool"), ret) def test_absent_exporty_pool(self): - ''' + """ Test zpool absent exporting pool - ''' + """ ret = { - 'name': 'myzpool', - 'result': True, - 'comment': 'storage pool myzpool was exported', - 'changes': {'myzpool': 'exported'}, + "name": "myzpool", + "result": True, + "comment": "storage pool myzpool was exported", + "changes": {"myzpool": "exported"}, } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([ - ('exported', True), - ])) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'zpool.export': mock_destroy}), \ - patch.dict(zpool.__utils__, self.utils_patch): - self.assertEqual(zpool.absent('myzpool', export=True), ret) + mock_destroy = MagicMock(return_value=OrderedDict([("exported", True)])) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.export": mock_destroy} + ), patch.dict(zpool.__utils__, self.utils_patch): + self.assertEqual(zpool.absent("myzpool", export=True), ret) def test_absent_busy(self): - ''' + """ Test zpool absent on a busy pool - ''' + """ ret = { - 'name': 'myzpool', - 'result': False, - 'comment': "\n".join([ - "cannot unmount '/myzpool': Device busy", - "cannot export 'myzpool': pool is busy", - ]), - 'changes': {}, + "name": "myzpool", + "result": False, + "comment": "\n".join( + [ + "cannot unmount '/myzpool': Device busy", + "cannot export 'myzpool': pool is busy", + ] + ), + "changes": {}, } mock_exists = MagicMock(return_value=True) - mock_destroy = MagicMock(return_value=OrderedDict([ - ('exported', False), - ('error', "\n".join([ - "cannot unmount '/myzpool': Device busy", - "cannot export 'myzpool': pool is busy", - ])), - ])) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'zpool.export': mock_destroy}), \ - patch.dict(zpool.__utils__, self.utils_patch): - self.assertEqual(zpool.absent('myzpool', export=True), ret) + mock_destroy = MagicMock( + return_value=OrderedDict( + [ + ("exported", False), + ( + "error", + "\n".join( + [ + "cannot unmount '/myzpool': Device busy", + "cannot export 'myzpool': pool is busy", + ] + ), + ), + ] + ) + ) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.export": mock_destroy} + ), patch.dict(zpool.__utils__, self.utils_patch): + self.assertEqual(zpool.absent("myzpool", export=True), ret) def test_present_import_success(self): - ''' + """ Test zpool present with import allowed and unimported pool - ''' - ret = {'name': 'myzpool', - 'result': True, - 'comment': 'storage pool myzpool was imported', - 'changes': {'myzpool': 'imported'}} + """ + ret = { + "name": "myzpool", + "result": True, + "comment": "storage pool myzpool was imported", + "changes": {"myzpool": "imported"}, + } config = { - 'import': True, + "import": True, } mock_exists = MagicMock(return_value=False) - mock_import = MagicMock(return_value=OrderedDict([ - ('imported', True), - ])) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'zpool.import': mock_import}), \ - patch.dict(zpool.__utils__, self.utils_patch): - self.assertEqual(zpool.present('myzpool', config=config), ret) + mock_import = MagicMock(return_value=OrderedDict([("imported", True)])) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.import": mock_import} + ), patch.dict(zpool.__utils__, self.utils_patch): + self.assertEqual(zpool.present("myzpool", config=config), ret) def test_present_import_fail(self): - ''' + """ Test zpool present with import allowed and no unimported pool or layout - ''' - ret = {'name': 'myzpool', - 'result': False, - 'comment': 'storage pool myzpool was not imported, no (valid) layout specified for creation', - 'changes': {}} + """ + ret = { + "name": "myzpool", + "result": False, + "comment": "storage pool myzpool was not imported, no (valid) layout specified for creation", + "changes": {}, + } config = { - 'import': True, + "import": True, } mock_exists = MagicMock(return_value=False) - mock_import = MagicMock(return_value=OrderedDict([ - ('imported', False), - ])) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'zpool.import': mock_import}), \ - patch.dict(zpool.__utils__, self.utils_patch): - self.assertEqual(zpool.present('myzpool', config=config), ret) + mock_import = MagicMock(return_value=OrderedDict([("imported", False)])) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.import": mock_import} + ), patch.dict(zpool.__utils__, self.utils_patch): + self.assertEqual(zpool.present("myzpool", config=config), ret) def test_present_create_success(self): - ''' + """ Test zpool present with non existing pool - ''' - ret = {'name': 'myzpool', - 'result': True, - 'comment': 'storage pool myzpool was created', - 'changes': {'myzpool': 'created'}} + """ + ret = { + "name": "myzpool", + "result": True, + "comment": "storage pool myzpool was created", + "changes": {"myzpool": "created"}, + } config = { - 'import': False, + "import": False, } layout = [ - OrderedDict([('mirror', ['disk0', 'disk1'])]), - OrderedDict([('mirror', ['disk2', 'disk3'])]), + OrderedDict([("mirror", ["disk0", "disk1"])]), + OrderedDict([("mirror", ["disk2", "disk3"])]), ] properties = { - 'autoexpand': True, + "autoexpand": True, } filesystem_properties = { - 'quota': '5G', + "quota": "5G", } mock_exists = MagicMock(return_value=False) - mock_create = MagicMock(return_value=OrderedDict([ - ('created', True), - ('vdevs', OrderedDict([ - ('mirror-0', ['/dev/dsk/disk0', '/dev/dsk/disk1']), - ('mirror-1', ['/dev/dsk/disk2', '/dev/dsk/disk3']), - ])), - ])) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'zpool.create': mock_create}), \ - patch.dict(zpool.__utils__, self.utils_patch): + mock_create = MagicMock( + return_value=OrderedDict( + [ + ("created", True), + ( + "vdevs", + OrderedDict( + [ + ("mirror-0", ["/dev/dsk/disk0", "/dev/dsk/disk1"]), + ("mirror-1", ["/dev/dsk/disk2", "/dev/dsk/disk3"]), + ] + ), + ), + ] + ) + ) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.create": mock_create} + ), patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual( zpool.present( - 'myzpool', + "myzpool", config=config, layout=layout, properties=properties, @@ -219,68 +239,84 @@ class ZpoolTestCase(TestCase, LoaderModuleMockMixin): ) def test_present_create_fail(self): - ''' + """ Test zpool present with non existing pool (without a layout) - ''' - ret = {'name': 'myzpool', - 'result': False, - 'comment': 'storage pool myzpool was not imported, no (valid) layout specified for creation', - 'changes': {}} + """ + ret = { + "name": "myzpool", + "result": False, + "comment": "storage pool myzpool was not imported, no (valid) layout specified for creation", + "changes": {}, + } config = { - 'import': False, + "import": False, } mock_exists = MagicMock(return_value=False) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__utils__, self.utils_patch): - self.assertEqual(zpool.present('myzpool', config=config), ret) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__utils__, self.utils_patch + ): + self.assertEqual(zpool.present("myzpool", config=config), ret) def test_present_create_passthrough_fail(self): - ''' + """ Test zpool present with non existing pool (without a layout) - ''' - ret = {'name': 'myzpool', - 'result': False, - 'comment': "\n".join([ + """ + ret = { + "name": "myzpool", + "result": False, + "comment": "\n".join( + [ "invalid vdev specification", "use 'force=True' to override the following errors:", "/data/salt/vdisk0 is part of exported pool 'zsalt'", "/data/salt/vdisk1 is part of exported pool 'zsalt'", - ]), - 'changes': {}} + ] + ), + "changes": {}, + } config = { - 'force': False, - 'import': False, + "force": False, + "import": False, } layout = [ - OrderedDict([('mirror', ['disk0', 'disk1'])]), - OrderedDict([('mirror', ['disk2', 'disk3'])]), + OrderedDict([("mirror", ["disk0", "disk1"])]), + OrderedDict([("mirror", ["disk2", "disk3"])]), ] properties = { - 'autoexpand': True, + "autoexpand": True, } filesystem_properties = { - 'quota': '5G', + "quota": "5G", } mock_exists = MagicMock(return_value=False) - mock_create = MagicMock(return_value=OrderedDict([ - ('created', False), - ('error', "\n".join([ - "invalid vdev specification", - "use 'force=True' to override the following errors:", - "/data/salt/vdisk0 is part of exported pool 'zsalt'", - "/data/salt/vdisk1 is part of exported pool 'zsalt'", - ])), - ])) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'zpool.create': mock_create}), \ - patch.dict(zpool.__utils__, self.utils_patch): + mock_create = MagicMock( + return_value=OrderedDict( + [ + ("created", False), + ( + "error", + "\n".join( + [ + "invalid vdev specification", + "use 'force=True' to override the following errors:", + "/data/salt/vdisk0 is part of exported pool 'zsalt'", + "/data/salt/vdisk1 is part of exported pool 'zsalt'", + ] + ), + ), + ] + ) + ) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.create": mock_create} + ), patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual( zpool.present( - 'myzpool', + "myzpool", config=config, layout=layout, properties=properties, @@ -290,159 +326,184 @@ class ZpoolTestCase(TestCase, LoaderModuleMockMixin): ) def test_present_update_success(self): - ''' + """ Test zpool present with an existing pool that needs an update - ''' - ret = {'name': 'myzpool', - 'result': True, - 'comment': 'properties updated', - 'changes': {'myzpool': {'autoexpand': False}}} + """ + ret = { + "name": "myzpool", + "result": True, + "comment": "properties updated", + "changes": {"myzpool": {"autoexpand": False}}, + } config = { - 'import': False, + "import": False, } layout = [ - OrderedDict([('mirror', ['disk0', 'disk1'])]), - OrderedDict([('mirror', ['disk2', 'disk3'])]), + OrderedDict([("mirror", ["disk0", "disk1"])]), + OrderedDict([("mirror", ["disk2", "disk3"])]), ] properties = { - 'autoexpand': False, + "autoexpand": False, } mock_exists = MagicMock(return_value=True) - mock_get = MagicMock(return_value=OrderedDict([ - ('comment', 'salt managed pool'), - ('freeing', 0), - ('listsnapshots', False), - ('leaked', 0), - ('feature@obsolete_counts', 'enabled'), - ('feature@sha512', 'enabled'), - ('delegation', True), - ('dedupditto', '0'), - ('dedupratio', '1.00x'), - ('autoexpand', True), - ('feature@bookmarks', 'enabled'), - ('allocated', 115712), - ('guid', 1591906802560842214), - ('feature@large_blocks', 'enabled'), - ('size', 2113929216), - ('feature@enabled_txg', 'active'), - ('feature@hole_birth', 'active'), - ('capacity', 0), - ('feature@multi_vdev_crash_dump', 'enabled'), - ('feature@extensible_dataset', 'enabled'), - ('cachefile', '-'), - ('bootfs', '-'), - ('autoreplace', True), - ('readonly', False), - ('version', '-'), - ('health', 'ONLINE'), - ('expandsize', '-'), - ('feature@embedded_data', 'active'), - ('feature@lz4_compress', 'active'), - ('feature@async_destroy', 'enabled'), - ('feature@skein', 'enabled'), - ('feature@empty_bpobj', 'enabled'), - ('feature@spacemap_histogram', 'active'), - ('bootsize', '-'), - ('free', 2113813504), - ('feature@device_removal', 'enabled'), - ('failmode', 'wait'), - ('feature@filesystem_limits', 'enabled'), - ('feature@edonr', 'enabled'), - ('altroot', '-'), - ('fragmentation', '0%'), - ])) - mock_set = MagicMock(return_value=OrderedDict([ - ('set', True), - ])) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'zpool.get': mock_get}), \ - patch.dict(zpool.__salt__, {'zpool.set': mock_set}), \ - patch.dict(zpool.__utils__, self.utils_patch): + mock_get = MagicMock( + return_value=OrderedDict( + [ + ("comment", "salt managed pool"), + ("freeing", 0), + ("listsnapshots", False), + ("leaked", 0), + ("feature@obsolete_counts", "enabled"), + ("feature@sha512", "enabled"), + ("delegation", True), + ("dedupditto", "0"), + ("dedupratio", "1.00x"), + ("autoexpand", True), + ("feature@bookmarks", "enabled"), + ("allocated", 115712), + ("guid", 1591906802560842214), + ("feature@large_blocks", "enabled"), + ("size", 2113929216), + ("feature@enabled_txg", "active"), + ("feature@hole_birth", "active"), + ("capacity", 0), + ("feature@multi_vdev_crash_dump", "enabled"), + ("feature@extensible_dataset", "enabled"), + ("cachefile", "-"), + ("bootfs", "-"), + ("autoreplace", True), + ("readonly", False), + ("version", "-"), + ("health", "ONLINE"), + ("expandsize", "-"), + ("feature@embedded_data", "active"), + ("feature@lz4_compress", "active"), + ("feature@async_destroy", "enabled"), + ("feature@skein", "enabled"), + ("feature@empty_bpobj", "enabled"), + ("feature@spacemap_histogram", "active"), + ("bootsize", "-"), + ("free", 2113813504), + ("feature@device_removal", "enabled"), + ("failmode", "wait"), + ("feature@filesystem_limits", "enabled"), + ("feature@edonr", "enabled"), + ("altroot", "-"), + ("fragmentation", "0%"), + ] + ) + ) + mock_set = MagicMock(return_value=OrderedDict([("set", True)])) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.get": mock_get} + ), patch.dict(zpool.__salt__, {"zpool.set": mock_set}), patch.dict( + zpool.__utils__, self.utils_patch + ): self.assertEqual( zpool.present( - 'myzpool', - config=config, - layout=layout, - properties=properties, + "myzpool", config=config, layout=layout, properties=properties, ), ret, ) def test_present_update_nochange_success(self): - ''' + """ Test zpool present with non existing pool - ''' - ret = {'name': 'myzpool', - 'result': True, - 'comment': 'no update needed', - 'changes': {}} + """ + ret = { + "name": "myzpool", + "result": True, + "comment": "no update needed", + "changes": {}, + } config = { - 'import': False, + "import": False, } layout = [ - OrderedDict([('mirror', ['disk0', 'disk1'])]), - OrderedDict([('mirror', ['disk2', 'disk3'])]), + OrderedDict([("mirror", ["disk0", "disk1"])]), + OrderedDict([("mirror", ["disk2", "disk3"])]), ] properties = { - 'autoexpand': True, + "autoexpand": True, } mock_exists = MagicMock(return_value=True) - mock_get = MagicMock(return_value=OrderedDict([ - ('comment', 'salt managed pool'), - ('freeing', 0), - ('listsnapshots', False), - ('leaked', 0), - ('feature@obsolete_counts', 'enabled'), - ('feature@sha512', 'enabled'), - ('delegation', True), - ('dedupditto', '0'), - ('dedupratio', '1.00x'), - ('autoexpand', True), - ('feature@bookmarks', 'enabled'), - ('allocated', 115712), - ('guid', 1591906802560842214), - ('feature@large_blocks', 'enabled'), - ('size', 2113929216), - ('feature@enabled_txg', 'active'), - ('feature@hole_birth', 'active'), - ('capacity', 0), - ('feature@multi_vdev_crash_dump', 'enabled'), - ('feature@extensible_dataset', 'enabled'), - ('cachefile', '-'), - ('bootfs', '-'), - ('autoreplace', True), - ('readonly', False), - ('version', '-'), - ('health', 'ONLINE'), - ('expandsize', '-'), - ('feature@embedded_data', 'active'), - ('feature@lz4_compress', 'active'), - ('feature@async_destroy', 'enabled'), - ('feature@skein', 'enabled'), - ('feature@empty_bpobj', 'enabled'), - ('feature@spacemap_histogram', 'active'), - ('bootsize', '-'), - ('free', 2113813504), - ('feature@device_removal', 'enabled'), - ('failmode', 'wait'), - ('feature@filesystem_limits', 'enabled'), - ('feature@edonr', 'enabled'), - ('altroot', '-'), - ('fragmentation', '0%'), - ])) - with patch.dict(zpool.__salt__, {'zpool.exists': mock_exists}), \ - patch.dict(zpool.__salt__, {'zpool.get': mock_get}), \ - patch.dict(zpool.__utils__, self.utils_patch): + mock_get = MagicMock( + return_value=OrderedDict( + [ + ("comment", "salt managed pool"), + ("freeing", 0), + ("listsnapshots", False), + ("leaked", 0), + ("feature@obsolete_counts", "enabled"), + ("feature@sha512", "enabled"), + ("delegation", True), + ("dedupditto", "0"), + ("dedupratio", "1.00x"), + ("autoexpand", True), + ("feature@bookmarks", "enabled"), + ("allocated", 115712), + ("guid", 1591906802560842214), + ("feature@large_blocks", "enabled"), + ("size", 2113929216), + ("feature@enabled_txg", "active"), + ("feature@hole_birth", "active"), + ("capacity", 0), + ("feature@multi_vdev_crash_dump", "enabled"), + ("feature@extensible_dataset", "enabled"), + ("cachefile", "-"), + ("bootfs", "-"), + ("autoreplace", True), + ("readonly", False), + ("version", "-"), + ("health", "ONLINE"), + ("expandsize", "-"), + ("feature@embedded_data", "active"), + ("feature@lz4_compress", "active"), + ("feature@async_destroy", "enabled"), + ("feature@skein", "enabled"), + ("feature@empty_bpobj", "enabled"), + ("feature@spacemap_histogram", "active"), + ("bootsize", "-"), + ("free", 2113813504), + ("feature@device_removal", "enabled"), + ("failmode", "wait"), + ("feature@filesystem_limits", "enabled"), + ("feature@edonr", "enabled"), + ("altroot", "-"), + ("fragmentation", "0%"), + ] + ) + ) + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.get": mock_get} + ), patch.dict(zpool.__utils__, self.utils_patch): self.assertEqual( zpool.present( - 'myzpool', - config=config, - layout=layout, - properties=properties, + "myzpool", config=config, layout=layout, properties=properties, + ), + ret, + ) + + # Run state with test=true + ret = { + "name": "myzpool", + "result": True, + "comment": "storage pool myzpool is uptodate", + "changes": {}, + } + + with patch.dict(zpool.__salt__, {"zpool.exists": mock_exists}), patch.dict( + zpool.__salt__, {"zpool.get": mock_get} + ), patch.dict(zpool.__utils__, self.utils_patch), patch.dict( + zpool.__opts__, {"test": True} + ): + self.assertEqual( + zpool.present( + "myzpool", config=config, layout=layout, properties=properties, ), ret, ) diff --git a/tests/unit/test__compat.py b/tests/unit/test__compat.py index d7deb12004c..85d74fb9d7c 100644 --- a/tests/unit/test__compat.py +++ b/tests/unit/test__compat.py @@ -1,83 +1,84 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for salt._compat -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import sys -# Import Salt Testing libs -from tests.support.unit import TestCase - # Import Salt libs import salt._compat as compat # Import 3rd Party libs from salt.ext.six import binary_type, text_type +# Import Salt Testing libs +from tests.support.unit import TestCase + log = logging.getLogger(__name__) PY3 = sys.version_info.major == 3 class CompatTestCase(TestCase): def test_text(self): - ret = compat.text_('test string') + ret = compat.text_("test string") self.assertTrue(isinstance(ret, text_type)) def test_text_binary(self): - ret = compat.text_(b'test string') + ret = compat.text_(b"test string") self.assertTrue(isinstance(ret, text_type)) def test_bytes(self): - ret = compat.bytes_('test string') + ret = compat.bytes_("test string") self.assertTrue(isinstance(ret, binary_type)) def test_bytes_binary(self): - ret = compat.bytes_(b'test string') + ret = compat.bytes_(b"test string") self.assertTrue(isinstance(ret, binary_type)) def test_ascii_native(self): - ret = compat.ascii_native_('test string') + ret = compat.ascii_native_("test string") self.assertTrue(isinstance(ret, str)) def test_ascii_native_binary(self): - ret = compat.ascii_native_(b'test string') + ret = compat.ascii_native_(b"test string") self.assertTrue(isinstance(ret, str)) def test_native(self): - ret = compat.native_('test string') + ret = compat.native_("test string") self.assertTrue(isinstance(ret, str)) def test_native_binary(self): - ret = compat.native_(b'test string') + ret = compat.native_(b"test string") self.assertTrue(isinstance(ret, str)) def test_string_io(self): - ret = compat.string_io('test string') + ret = compat.string_io("test string") if PY3: - expected = 'io.StringIO object' + expected = "io.StringIO object" else: - expected = 'cStringIO.StringI object' + expected = "cStringIO.StringI object" self.assertTrue(expected in repr(ret)) def test_string_io_unicode(self): - ret = compat.string_io(u'test string \xf8') + ret = compat.string_io("test string \xf8") if PY3: - expected = 'io.StringIO object' + expected = "io.StringIO object" else: - expected = 'StringIO.StringIO instance' + expected = "StringIO.StringIO instance" self.assertTrue(expected in repr(ret)) def test_ipv6_class__is_packed_binary(self): - ipv6 = compat.IPv6AddressScoped('2001:db8::') - self.assertEqual(str(ipv6), '2001:db8::') + ipv6 = compat.IPv6AddressScoped("2001:db8::") + self.assertEqual(str(ipv6), "2001:db8::") def test_ipv6_class__is_packed_binary_integer(self): ipv6 = compat.IPv6AddressScoped(42540766411282592856903984951653826560) - self.assertEqual(str(ipv6), '2001:db8::') + self.assertEqual(str(ipv6), "2001:db8::") def test_ipv6_class__is_packed_binary__issue_51831(self): - ipv6 = compat.IPv6AddressScoped(b'sixteen.digit.bn') - self.assertEqual(str(ipv6), '7369:7874:6565:6e2e:6469:6769:742e:626e') + ipv6 = compat.IPv6AddressScoped(b"sixteen.digit.bn") + self.assertEqual(str(ipv6), "7369:7874:6565:6e2e:6469:6769:742e:626e") diff --git a/tests/unit/test_acl.py b/tests/unit/test_acl.py index ac68688b404..e1f298246ad 100644 --- a/tests/unit/test_acl.py +++ b/tests/unit/test_acl.py @@ -11,17 +11,18 @@ from tests.support.unit import TestCase class ClientACLTestCase(TestCase): - ''' + """ Unit tests for salt.acl.ClientACL - ''' + """ + def setUp(self): self.blacklist = { - 'users': ['joker', 'penguin', '*bad_*', 'blocked_.*', '^Homer$'], - 'modules': ['cmd.run', 'test.fib', 'rm-rf.*'], + "users": ["joker", "penguin", "*bad_*", "blocked_.*", "^Homer$"], + "modules": ["cmd.run", "test.fib", "rm-rf.*"], } self.whitelist = { - 'users': ['testuser', 'saltuser'], - 'modules': ['test.ping', 'grains.items'], + "users": ["testuser", "saltuser"], + "modules": ["test.ping", "grains.items"], } def tearDown(self): @@ -29,64 +30,66 @@ class ClientACLTestCase(TestCase): del self.whitelist def test_user_is_blacklisted(self): - ''' + """ test user_is_blacklisted - ''' + """ client_acl = acl.PublisherACL(self.blacklist) - self.assertTrue(client_acl.user_is_blacklisted('joker')) - self.assertTrue(client_acl.user_is_blacklisted('penguin')) - self.assertTrue(client_acl.user_is_blacklisted('bad_')) - self.assertTrue(client_acl.user_is_blacklisted('bad_user')) - self.assertTrue(client_acl.user_is_blacklisted('bad_*')) - self.assertTrue(client_acl.user_is_blacklisted('user_bad_')) - self.assertTrue(client_acl.user_is_blacklisted('blocked_')) - self.assertTrue(client_acl.user_is_blacklisted('blocked_user')) - self.assertTrue(client_acl.user_is_blacklisted('blocked_.*')) - self.assertTrue(client_acl.user_is_blacklisted('Homer')) + self.assertTrue(client_acl.user_is_blacklisted("joker")) + self.assertTrue(client_acl.user_is_blacklisted("penguin")) + self.assertTrue(client_acl.user_is_blacklisted("bad_")) + self.assertTrue(client_acl.user_is_blacklisted("bad_user")) + self.assertTrue(client_acl.user_is_blacklisted("bad_*")) + self.assertTrue(client_acl.user_is_blacklisted("user_bad_")) + self.assertTrue(client_acl.user_is_blacklisted("blocked_")) + self.assertTrue(client_acl.user_is_blacklisted("blocked_user")) + self.assertTrue(client_acl.user_is_blacklisted("blocked_.*")) + self.assertTrue(client_acl.user_is_blacklisted("Homer")) - self.assertFalse(client_acl.user_is_blacklisted('batman')) - self.assertFalse(client_acl.user_is_blacklisted('robin')) - self.assertFalse(client_acl.user_is_blacklisted('bad')) - self.assertFalse(client_acl.user_is_blacklisted('blocked')) - self.assertFalse(client_acl.user_is_blacklisted('NotHomer')) - self.assertFalse(client_acl.user_is_blacklisted('HomerSimpson')) + self.assertFalse(client_acl.user_is_blacklisted("batman")) + self.assertFalse(client_acl.user_is_blacklisted("robin")) + self.assertFalse(client_acl.user_is_blacklisted("bad")) + self.assertFalse(client_acl.user_is_blacklisted("blocked")) + self.assertFalse(client_acl.user_is_blacklisted("NotHomer")) + self.assertFalse(client_acl.user_is_blacklisted("HomerSimpson")) def test_cmd_is_blacklisted(self): - ''' + """ test cmd_is_blacklisted - ''' + """ client_acl = acl.PublisherACL(self.blacklist) - self.assertTrue(client_acl.cmd_is_blacklisted('cmd.run')) - self.assertTrue(client_acl.cmd_is_blacklisted('test.fib')) - self.assertTrue(client_acl.cmd_is_blacklisted('rm-rf.root')) + self.assertTrue(client_acl.cmd_is_blacklisted("cmd.run")) + self.assertTrue(client_acl.cmd_is_blacklisted("test.fib")) + self.assertTrue(client_acl.cmd_is_blacklisted("rm-rf.root")) - self.assertFalse(client_acl.cmd_is_blacklisted('cmd.shell')) - self.assertFalse(client_acl.cmd_is_blacklisted('test.versions')) - self.assertFalse(client_acl.cmd_is_blacklisted('arm-rf.root')) + self.assertFalse(client_acl.cmd_is_blacklisted("cmd.shell")) + self.assertFalse(client_acl.cmd_is_blacklisted("test.versions")) + self.assertFalse(client_acl.cmd_is_blacklisted("arm-rf.root")) - self.assertTrue(client_acl.cmd_is_blacklisted(['cmd.run', 'state.sls'])) - self.assertFalse(client_acl.cmd_is_blacklisted(['state.highstate', 'state.sls'])) + self.assertTrue(client_acl.cmd_is_blacklisted(["cmd.run", "state.sls"])) + self.assertFalse( + client_acl.cmd_is_blacklisted(["state.highstate", "state.sls"]) + ) def test_user_is_whitelisted(self): - ''' + """ test user_is_whitelisted - ''' + """ client_acl = acl.PublisherACL(self.whitelist) - self.assertTrue(client_acl.user_is_whitelisted('testuser')) - self.assertTrue(client_acl.user_is_whitelisted('saltuser')) - self.assertFalse(client_acl.user_is_whitelisted('three')) - self.assertFalse(client_acl.user_is_whitelisted('hans')) + self.assertTrue(client_acl.user_is_whitelisted("testuser")) + self.assertTrue(client_acl.user_is_whitelisted("saltuser")) + self.assertFalse(client_acl.user_is_whitelisted("three")) + self.assertFalse(client_acl.user_is_whitelisted("hans")) def test_cmd_is_whitelisted(self): - ''' + """ test cmd_is_whitelisted - ''' + """ client_acl = acl.PublisherACL(self.whitelist) - self.assertTrue(client_acl.cmd_is_whitelisted('test.ping')) - self.assertTrue(client_acl.cmd_is_whitelisted('grains.items')) - self.assertFalse(client_acl.cmd_is_whitelisted('cmd.run')) - self.assertFalse(client_acl.cmd_is_whitelisted('test.version')) + self.assertTrue(client_acl.cmd_is_whitelisted("test.ping")) + self.assertTrue(client_acl.cmd_is_whitelisted("grains.items")) + self.assertFalse(client_acl.cmd_is_whitelisted("cmd.run")) + self.assertFalse(client_acl.cmd_is_whitelisted("test.version")) diff --git a/tests/unit/test_auth.py b/tests/unit/test_auth.py index 12d50a47384..6a564f223a9 100644 --- a/tests/unit/test_auth.py +++ b/tests/unit/test_auth.py @@ -1,34 +1,48 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place <mp@saltstack.com> -''' +""" # Import pytohn libs from __future__ import absolute_import, print_function, unicode_literals import time -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, call, MagicMock - # Import Salt libraries import salt.master -from tests.support.case import ModuleCase +import salt.utils.platform from salt import auth from salt.exceptions import SaltDeserializationError -import salt.utils.platform +from tests.support.case import ModuleCase +from tests.support.mock import MagicMock, call, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf class LoadAuthTestCase(TestCase): - def setUp(self): # pylint: disable=W0221 patches = ( - ('salt.payload.Serial', None), - ('salt.loader.auth', dict(return_value={'pam.auth': 'fake_func_str', 'pam.groups': 'fake_groups_function_str'})), - ('salt.loader.eauth_tokens', dict(return_value={'localfs.mk_token': 'fake_func_mktok', - 'localfs.get_token': 'fake_func_gettok', - 'localfs.rm_roken': 'fake_func_rmtok'})) + ("salt.payload.Serial", None), + ( + "salt.loader.auth", + dict( + return_value={ + "pam.auth": "fake_func_str", + "pam.groups": "fake_groups_function_str", + } + ), + ), + ( + "salt.loader.eauth_tokens", + dict( + return_value={ + "localfs.mk_token": "fake_func_mktok", + "localfs.get_token": "fake_func_gettok", + "localfs.rm_roken": "fake_func_rmtok", + } + ), + ), ) for mod, mock in patches: if mock: @@ -40,237 +54,268 @@ class LoadAuthTestCase(TestCase): self.lauth = auth.LoadAuth({}) # Load with empty opts def test_get_tok_with_broken_file_will_remove_bad_token(self): - fake_get_token = MagicMock(side_effect=SaltDeserializationError('hi')) - patch_opts = patch.dict(self.lauth.opts, {'eauth_tokens': 'testfs'}) + fake_get_token = MagicMock(side_effect=SaltDeserializationError("hi")) + patch_opts = patch.dict(self.lauth.opts, {"eauth_tokens": "testfs"}) patch_get_token = patch.dict( - self.lauth.tokens, - { - 'testfs.get_token': fake_get_token - }, + self.lauth.tokens, {"testfs.get_token": fake_get_token}, ) mock_rm_token = MagicMock() - patch_rm_token = patch.object(self.lauth, 'rm_token', mock_rm_token) + patch_rm_token = patch.object(self.lauth, "rm_token", mock_rm_token) with patch_opts, patch_get_token, patch_rm_token: - expected_token = 'fnord' + expected_token = "fnord" self.lauth.get_tok(expected_token) mock_rm_token.assert_called_with(expected_token) def test_get_tok_with_no_expiration_should_remove_bad_token(self): - fake_get_token = MagicMock(return_value={'no_expire_here': 'Nope'}) - patch_opts = patch.dict(self.lauth.opts, {'eauth_tokens': 'testfs'}) + fake_get_token = MagicMock(return_value={"no_expire_here": "Nope"}) + patch_opts = patch.dict(self.lauth.opts, {"eauth_tokens": "testfs"}) patch_get_token = patch.dict( - self.lauth.tokens, - { - 'testfs.get_token': fake_get_token - }, + self.lauth.tokens, {"testfs.get_token": fake_get_token}, ) mock_rm_token = MagicMock() - patch_rm_token = patch.object(self.lauth, 'rm_token', mock_rm_token) + patch_rm_token = patch.object(self.lauth, "rm_token", mock_rm_token) with patch_opts, patch_get_token, patch_rm_token: - expected_token = 'fnord' + expected_token = "fnord" self.lauth.get_tok(expected_token) mock_rm_token.assert_called_with(expected_token) def test_get_tok_with_expire_before_current_time_should_remove_token(self): - fake_get_token = MagicMock(return_value={'expire': time.time()-1}) - patch_opts = patch.dict(self.lauth.opts, {'eauth_tokens': 'testfs'}) + fake_get_token = MagicMock(return_value={"expire": time.time() - 1}) + patch_opts = patch.dict(self.lauth.opts, {"eauth_tokens": "testfs"}) patch_get_token = patch.dict( - self.lauth.tokens, - { - 'testfs.get_token': fake_get_token - }, + self.lauth.tokens, {"testfs.get_token": fake_get_token}, ) mock_rm_token = MagicMock() - patch_rm_token = patch.object(self.lauth, 'rm_token', mock_rm_token) + patch_rm_token = patch.object(self.lauth, "rm_token", mock_rm_token) with patch_opts, patch_get_token, patch_rm_token: - expected_token = 'fnord' + expected_token = "fnord" self.lauth.get_tok(expected_token) mock_rm_token.assert_called_with(expected_token) def test_get_tok_with_valid_expiration_should_return_token(self): - expected_token = {'expire': time.time()+1} + expected_token = {"expire": time.time() + 1} fake_get_token = MagicMock(return_value=expected_token) - patch_opts = patch.dict(self.lauth.opts, {'eauth_tokens': 'testfs'}) + patch_opts = patch.dict(self.lauth.opts, {"eauth_tokens": "testfs"}) patch_get_token = patch.dict( - self.lauth.tokens, - { - 'testfs.get_token': fake_get_token - }, + self.lauth.tokens, {"testfs.get_token": fake_get_token}, ) mock_rm_token = MagicMock() - patch_rm_token = patch.object(self.lauth, 'rm_token', mock_rm_token) + patch_rm_token = patch.object(self.lauth, "rm_token", mock_rm_token) with patch_opts, patch_get_token, patch_rm_token: - token_name = 'fnord' + token_name = "fnord" actual_token = self.lauth.get_tok(token_name) mock_rm_token.assert_not_called() - assert expected_token is actual_token, 'Token was not returned' + assert expected_token is actual_token, "Token was not returned" def test_load_name(self): - valid_eauth_load = {'username': 'test_user', - 'show_timeout': False, - 'test_password': '', - 'eauth': 'pam'} + valid_eauth_load = { + "username": "test_user", + "show_timeout": False, + "test_password": "", + "eauth": "pam", + } # Test a case where the loader auth doesn't have the auth type without_auth_type = dict(valid_eauth_load) - without_auth_type.pop('eauth') + without_auth_type.pop("eauth") ret = self.lauth.load_name(without_auth_type) - self.assertEqual(ret, '', "Did not bail when the auth loader didn't have the auth type.") + self.assertEqual( + ret, "", "Did not bail when the auth loader didn't have the auth type." + ) # Test a case with valid params - with patch('salt.utils.args.arg_lookup', - MagicMock(return_value={'args': ['username', 'password']})) as format_call_mock: - expected_ret = call('fake_func_str') + with patch( + "salt.utils.args.arg_lookup", + MagicMock(return_value={"args": ["username", "password"]}), + ) as format_call_mock: + expected_ret = call("fake_func_str") ret = self.lauth.load_name(valid_eauth_load) format_call_mock.assert_has_calls((expected_ret,), any_order=True) - self.assertEqual(ret, 'test_user') + self.assertEqual(ret, "test_user") def test_get_groups(self): - valid_eauth_load = {'username': 'test_user', - 'show_timeout': False, - 'test_password': '', - 'eauth': 'pam'} - with patch('salt.utils.args.format_call') as format_call_mock: - expected_ret = call('fake_groups_function_str', { - 'username': 'test_user', - 'test_password': '', - 'show_timeout': False, - 'eauth': 'pam' - }, expected_extra_kws=auth.AUTH_INTERNAL_KEYWORDS) + valid_eauth_load = { + "username": "test_user", + "show_timeout": False, + "test_password": "", + "eauth": "pam", + } + with patch("salt.utils.args.format_call") as format_call_mock: + expected_ret = call( + "fake_groups_function_str", + { + "username": "test_user", + "test_password": "", + "show_timeout": False, + "eauth": "pam", + }, + expected_extra_kws=auth.AUTH_INTERNAL_KEYWORDS, + ) self.lauth.get_groups(valid_eauth_load) format_call_mock.assert_has_calls((expected_ret,), any_order=True) class MasterACLTestCase(ModuleCase): - ''' + """ A class to check various aspects of the publisher ACL system - ''' + """ def setUp(self): - self.fire_event_mock = MagicMock(return_value='dummy_tag') - self.addCleanup(delattr, self, 'fire_event_mock') - opts = self.get_temp_config('master') + self.fire_event_mock = MagicMock(return_value="dummy_tag") + self.addCleanup(delattr, self, "fire_event_mock") + opts = self.get_temp_config("master") patches = ( - ('zmq.Context', MagicMock()), - ('salt.payload.Serial.dumps', MagicMock()), - ('salt.master.tagify', MagicMock()), - ('salt.utils.event.SaltEvent.fire_event', self.fire_event_mock), - ('salt.auth.LoadAuth.time_auth', MagicMock(return_value=True)), - ('salt.minion.MasterMinion', MagicMock()), - ('salt.utils.verify.check_path_traversal', MagicMock()), - ('salt.client.get_local_client', MagicMock(return_value=opts['conf_file'])), + ("zmq.Context", MagicMock()), + ("salt.payload.Serial.dumps", MagicMock()), + ("salt.master.tagify", MagicMock()), + ("salt.utils.event.SaltEvent.fire_event", self.fire_event_mock), + ("salt.auth.LoadAuth.time_auth", MagicMock(return_value=True)), + ("salt.minion.MasterMinion", MagicMock()), + ("salt.utils.verify.check_path_traversal", MagicMock()), + ("salt.client.get_local_client", MagicMock(return_value=opts["conf_file"])), ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) - opts['publisher_acl'] = {} - opts['publisher_acl_blacklist'] = {} - opts['master_job_cache'] = '' - opts['sign_pub_messages'] = False - opts['con_cache'] = '' - opts['external_auth'] = {} - opts['external_auth']['pam'] = \ - {'test_user': [{'*': ['test.ping']}, - {'minion_glob*': ['foo.bar']}, - {'minion_func_test': ['func_test.*']}], - 'test_group%': [{'*': ['test.echo']}], - 'test_user_mminion': [{'target_minion': ['test.ping']}], - '*': [{'my_minion': ['my_mod.my_func']}], - 'test_user_func': [{'*': [{'test.echo': {'args': ['MSG:.*']}}, - {'test.echo': {'kwargs': {'text': 'KWMSG:.*', - 'anything': '.*', - 'none': None}}}, - {'my_mod.*': {'args': ['a.*', 'b.*'], - 'kwargs': {'kwa': 'kwa.*', - 'kwb': 'kwb'}}}]}, - {'minion1': [{'test.echo': {'args': ['TEST', - None, - 'TEST.*']}}, - {'test.empty': {}}]} - ] - } + opts["publisher_acl"] = {} + opts["publisher_acl_blacklist"] = {} + opts["master_job_cache"] = "" + opts["sign_pub_messages"] = False + opts["con_cache"] = "" + opts["external_auth"] = {} + opts["external_auth"]["pam"] = { + "test_user": [ + {"*": ["test.ping"]}, + {"minion_glob*": ["foo.bar"]}, + {"minion_func_test": ["func_test.*"]}, + ], + "test_group%": [{"*": ["test.echo"]}], + "test_user_mminion": [{"target_minion": ["test.ping"]}], + "*": [{"my_minion": ["my_mod.my_func"]}], + "test_user_func": [ + { + "*": [ + {"test.echo": {"args": ["MSG:.*"]}}, + { + "test.echo": { + "kwargs": { + "text": "KWMSG:.*", + "anything": ".*", + "none": None, + } + } + }, + { + "my_mod.*": { + "args": ["a.*", "b.*"], + "kwargs": {"kwa": "kwa.*", "kwb": "kwb"}, + } + }, + ] + }, + { + "minion1": [ + {"test.echo": {"args": ["TEST", None, "TEST.*"]}}, + {"test.empty": {}}, + ] + }, + ], + } self.clear = salt.master.ClearFuncs(opts, MagicMock()) - self.addCleanup(delattr, self, 'clear') + self.addCleanup(delattr, self, "clear") # overwrite the _send_pub method so we don't have to serialize MagicMock self.clear._send_pub = lambda payload: True # make sure to return a JID, instead of a mock - self.clear.mminion.returners = {'.prep_jid': lambda x: 1} + self.clear.mminion.returners = {".prep_jid": lambda x: 1} - self.valid_clear_load = {'tgt_type': 'glob', - 'jid': '', - 'cmd': 'publish', - 'tgt': 'test_minion', - 'kwargs': - {'username': 'test_user', - 'password': 'test_password', - 'show_timeout': False, - 'eauth': 'pam', - 'show_jid': False}, - 'ret': '', - 'user': 'test_user', - 'key': '', - 'arg': '', - 'fun': 'test.ping', - } - self.addCleanup(delattr, self, 'valid_clear_load') + self.valid_clear_load = { + "tgt_type": "glob", + "jid": "", + "cmd": "publish", + "tgt": "test_minion", + "kwargs": { + "username": "test_user", + "password": "test_password", + "show_timeout": False, + "eauth": "pam", + "show_jid": False, + }, + "ret": "", + "user": "test_user", + "key": "", + "arg": "", + "fun": "test.ping", + } + self.addCleanup(delattr, self, "valid_clear_load") - @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "PAM eauth not available on Windows") def test_master_publish_name(self): - ''' + """ Test to ensure a simple name can auth against a given function. This tests to ensure test_user can access test.ping but *not* sys.doc - ''' - _check_minions_return = {'minions': ['some_minions'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): + """ + _check_minions_return = {"minions": ["some_minions"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): # Can we access test.ping? self.clear.publish(self.valid_clear_load) - self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.ping') + self.assertEqual(self.fire_event_mock.call_args[0][0]["fun"], "test.ping") # Are we denied access to sys.doc? sys_doc_load = self.valid_clear_load - sys_doc_load['fun'] = 'sys.doc' + sys_doc_load["fun"] = "sys.doc" self.clear.publish(sys_doc_load) - self.assertNotEqual(self.fire_event_mock.call_args[0][0]['fun'], 'sys.doc') # If sys.doc were to fire, this would match + self.assertNotEqual( + self.fire_event_mock.call_args[0][0]["fun"], "sys.doc" + ) # If sys.doc were to fire, this would match def test_master_publish_group(self): - ''' + """ Tests to ensure test_group can access test.echo but *not* sys.doc - ''' - _check_minions_return = {'minions': ['some_minions'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): - self.valid_clear_load['kwargs']['user'] = 'new_user' - self.valid_clear_load['fun'] = 'test.echo' - self.valid_clear_load['arg'] = 'hello' - with patch('salt.auth.LoadAuth.get_groups', return_value=['test_group', 'second_test_group']): + """ + _check_minions_return = {"minions": ["some_minions"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): + self.valid_clear_load["kwargs"]["user"] = "new_user" + self.valid_clear_load["fun"] = "test.echo" + self.valid_clear_load["arg"] = "hello" + with patch( + "salt.auth.LoadAuth.get_groups", + return_value=["test_group", "second_test_group"], + ): self.clear.publish(self.valid_clear_load) # Did we fire test.echo? - self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.echo') + self.assertEqual(self.fire_event_mock.call_args[0][0]["fun"], "test.echo") # Request sys.doc - self.valid_clear_load['fun'] = 'sys.doc' + self.valid_clear_load["fun"] = "sys.doc" # Did we fire it? - self.assertNotEqual(self.fire_event_mock.call_args[0][0]['fun'], 'sys.doc') + self.assertNotEqual(self.fire_event_mock.call_args[0][0]["fun"], "sys.doc") def test_master_publish_some_minions(self): - ''' + """ Tests to ensure we can only target minions for which we have permission with publisher acl. Note that in order for these sorts of tests to run correctly that you should NOT patch check_minions! - ''' - self.valid_clear_load['kwargs']['username'] = 'test_user_mminion' - self.valid_clear_load['user'] = 'test_user_mminion' + """ + self.valid_clear_load["kwargs"]["username"] = "test_user_mminion" + self.valid_clear_load["user"] = "test_user_mminion" self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) def test_master_not_user_glob_all(self): - ''' + """ Test to ensure that we DO NOT access to a given function to all users with publisher acl. ex: @@ -283,16 +328,16 @@ class MasterACLTestCase(ModuleCase): is NOT supported currently. WARNING: Do not patch this wit - ''' - self.valid_clear_load['kwargs']['username'] = 'NOT_A_VALID_USERNAME' - self.valid_clear_load['user'] = 'NOT_A_VALID_USERNAME' - self.valid_clear_load['fun'] = 'test.ping' + """ + self.valid_clear_load["kwargs"]["username"] = "NOT_A_VALID_USERNAME" + self.valid_clear_load["user"] = "NOT_A_VALID_USERNAME" + self.valid_clear_load["fun"] = "test.ping" self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) - @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "PAM eauth not available on Windows") def test_master_minion_glob(self): - ''' + """ Test to ensure we can allow access to a given function for a user to a subset of minions selected by a glob. ex: @@ -304,19 +349,31 @@ class MasterACLTestCase(ModuleCase): This test is a bit tricky, because ultimately the real functionality lies in what's returned from check_minions, but this checks a limited amount of logic on the way there as well. Note the inline patch. - ''' - requested_function = 'foo.bar' - requested_tgt = 'minion_glob1' - self.valid_clear_load['tgt'] = requested_tgt - self.valid_clear_load['fun'] = requested_function - _check_minions_return = {'minions': ['minion_glob1'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): # Assume that there is a listening minion match + """ + requested_function = "foo.bar" + requested_tgt = "minion_glob1" + self.valid_clear_load["tgt"] = requested_tgt + self.valid_clear_load["fun"] = requested_function + _check_minions_return = {"minions": ["minion_glob1"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): # Assume that there is a listening minion match self.clear.publish(self.valid_clear_load) - self.assertTrue(self.fire_event_mock.called, 'Did not fire {0} for minion tgt {1}'.format(requested_function, requested_tgt)) - self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], requested_function, 'Did not fire {0} for minion glob'.format(requested_function)) + self.assertTrue( + self.fire_event_mock.called, + "Did not fire {0} for minion tgt {1}".format( + requested_function, requested_tgt + ), + ) + self.assertEqual( + self.fire_event_mock.call_args[0][0]["fun"], + requested_function, + "Did not fire {0} for minion glob".format(requested_function), + ) def test_master_function_glob(self): - ''' + """ Test to ensure that we can allow access to a given set of functions in an execution module as selected by a glob. ex: @@ -324,31 +381,38 @@ class MasterACLTestCase(ModuleCase): my_user: my_minion: 'test.*' - ''' + """ # Unimplemented - @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "PAM eauth not available on Windows") def test_args_empty_spec(self): - ''' + """ Test simple arg restriction allowed. 'test_user_func': minion1: - test.empty: - ''' - _check_minions_return = {'minions': ['minion1'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): - self.valid_clear_load['kwargs'].update({'username': 'test_user_func'}) - self.valid_clear_load.update({'user': 'test_user_func', - 'tgt': 'minion1', - 'fun': 'test.empty', - 'arg': ['TEST']}) + """ + _check_minions_return = {"minions": ["minion1"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): + self.valid_clear_load["kwargs"].update({"username": "test_user_func"}) + self.valid_clear_load.update( + { + "user": "test_user_func", + "tgt": "minion1", + "fun": "test.empty", + "arg": ["TEST"], + } + ) self.clear.publish(self.valid_clear_load) - self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.empty') + self.assertEqual(self.fire_event_mock.call_args[0][0]["fun"], "test.empty") - @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "PAM eauth not available on Windows") def test_args_simple_match(self): - ''' + """ Test simple arg restriction allowed. 'test_user_func': @@ -357,20 +421,27 @@ class MasterACLTestCase(ModuleCase): args: - 'TEST' - 'TEST.*' - ''' - _check_minions_return = {'minions': ['minion1'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): - self.valid_clear_load['kwargs'].update({'username': 'test_user_func'}) - self.valid_clear_load.update({'user': 'test_user_func', - 'tgt': 'minion1', - 'fun': 'test.echo', - 'arg': ['TEST', 'any', 'TEST ABC']}) + """ + _check_minions_return = {"minions": ["minion1"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): + self.valid_clear_load["kwargs"].update({"username": "test_user_func"}) + self.valid_clear_load.update( + { + "user": "test_user_func", + "tgt": "minion1", + "fun": "test.echo", + "arg": ["TEST", "any", "TEST ABC"], + } + ) self.clear.publish(self.valid_clear_load) - self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.echo') + self.assertEqual(self.fire_event_mock.call_args[0][0]["fun"], "test.echo") - @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "PAM eauth not available on Windows") def test_args_more_args(self): - ''' + """ Test simple arg restriction allowed to pass unlisted args. 'test_user_func': @@ -379,24 +450,32 @@ class MasterACLTestCase(ModuleCase): args: - 'TEST' - 'TEST.*' - ''' - _check_minions_return = {'minions': ['minion1'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): - self.valid_clear_load['kwargs'].update({'username': 'test_user_func'}) - self.valid_clear_load.update({'user': 'test_user_func', - 'tgt': 'minion1', - 'fun': 'test.echo', - 'arg': ['TEST', - 'any', - 'TEST ABC', - 'arg 3', - {'kwarg1': 'val1', - '__kwarg__': True}]}) + """ + _check_minions_return = {"minions": ["minion1"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): + self.valid_clear_load["kwargs"].update({"username": "test_user_func"}) + self.valid_clear_load.update( + { + "user": "test_user_func", + "tgt": "minion1", + "fun": "test.echo", + "arg": [ + "TEST", + "any", + "TEST ABC", + "arg 3", + {"kwarg1": "val1", "__kwarg__": True}, + ], + } + ) self.clear.publish(self.valid_clear_load) - self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.echo') + self.assertEqual(self.fire_event_mock.call_args[0][0]["fun"], "test.echo") def test_args_simple_forbidden(self): - ''' + """ Test simple arg restriction forbidden. 'test_user_func': @@ -405,33 +484,40 @@ class MasterACLTestCase(ModuleCase): args: - 'TEST' - 'TEST.*' - ''' - _check_minions_return = {'minions': ['minion1'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): - self.valid_clear_load['kwargs'].update({'username': 'test_user_func'}) + """ + _check_minions_return = {"minions": ["minion1"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): + self.valid_clear_load["kwargs"].update({"username": "test_user_func"}) # Wrong last arg - self.valid_clear_load.update({'user': 'test_user_func', - 'tgt': 'minion1', - 'fun': 'test.echo', - 'arg': ['TEST', 'any', 'TESLA']}) + self.valid_clear_load.update( + { + "user": "test_user_func", + "tgt": "minion1", + "fun": "test.echo", + "arg": ["TEST", "any", "TESLA"], + } + ) self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) # Wrong first arg - self.valid_clear_load['arg'] = ['TES', 'any', 'TEST1234'] + self.valid_clear_load["arg"] = ["TES", "any", "TEST1234"] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) # Missing the last arg - self.valid_clear_load['arg'] = ['TEST', 'any'] + self.valid_clear_load["arg"] = ["TEST", "any"] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) # No args - self.valid_clear_load['arg'] = [] + self.valid_clear_load["arg"] = [] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) - @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "PAM eauth not available on Windows") def test_args_kwargs_match(self): - ''' + """ Test simple kwargs restriction allowed. 'test_user_func': @@ -439,22 +525,33 @@ class MasterACLTestCase(ModuleCase): - test.echo: kwargs: text: 'KWMSG:.*' - ''' - _check_minions_return = {'minions': ['some_minions'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): - self.valid_clear_load['kwargs'].update({'username': 'test_user_func'}) - self.valid_clear_load.update({'user': 'test_user_func', - 'tgt': '*', - 'fun': 'test.echo', - 'arg': [{'text': 'KWMSG: a message', - 'anything': 'hello all', - 'none': 'hello none', - '__kwarg__': True}]}) + """ + _check_minions_return = {"minions": ["some_minions"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): + self.valid_clear_load["kwargs"].update({"username": "test_user_func"}) + self.valid_clear_load.update( + { + "user": "test_user_func", + "tgt": "*", + "fun": "test.echo", + "arg": [ + { + "text": "KWMSG: a message", + "anything": "hello all", + "none": "hello none", + "__kwarg__": True, + } + ], + } + ) self.clear.publish(self.valid_clear_load) - self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], 'test.echo') + self.assertEqual(self.fire_event_mock.call_args[0][0]["fun"], "test.echo") def test_args_kwargs_mismatch(self): - ''' + """ Test simple kwargs restriction allowed. 'test_user_func': @@ -462,50 +559,57 @@ class MasterACLTestCase(ModuleCase): - test.echo: kwargs: text: 'KWMSG:.*' - ''' - _check_minions_return = {'minions': ['some_minions'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): - self.valid_clear_load['kwargs'].update({'username': 'test_user_func'}) - self.valid_clear_load.update({'user': 'test_user_func', - 'tgt': '*', - 'fun': 'test.echo'}) + """ + _check_minions_return = {"minions": ["some_minions"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): + self.valid_clear_load["kwargs"].update({"username": "test_user_func"}) + self.valid_clear_load.update( + {"user": "test_user_func", "tgt": "*", "fun": "test.echo"} + ) # Wrong kwarg value - self.valid_clear_load['arg'] = [{'text': 'KWMSG a message', - 'anything': 'hello all', - 'none': 'hello none', - '__kwarg__': True}] + self.valid_clear_load["arg"] = [ + { + "text": "KWMSG a message", + "anything": "hello all", + "none": "hello none", + "__kwarg__": True, + } + ] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) # Missing kwarg value - self.valid_clear_load['arg'] = [{'anything': 'hello all', - 'none': 'hello none', - '__kwarg__': True}] + self.valid_clear_load["arg"] = [ + {"anything": "hello all", "none": "hello none", "__kwarg__": True} + ] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) - self.valid_clear_load['arg'] = [{'__kwarg__': True}] + self.valid_clear_load["arg"] = [{"__kwarg__": True}] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) - self.valid_clear_load['arg'] = [{}] + self.valid_clear_load["arg"] = [{}] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) - self.valid_clear_load['arg'] = [] + self.valid_clear_load["arg"] = [] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) # Missing kwarg allowing any value - self.valid_clear_load['arg'] = [{'text': 'KWMSG: a message', - 'none': 'hello none', - '__kwarg__': True}] + self.valid_clear_load["arg"] = [ + {"text": "KWMSG: a message", "none": "hello none", "__kwarg__": True} + ] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) - self.valid_clear_load['arg'] = [{'text': 'KWMSG: a message', - 'anything': 'hello all', - '__kwarg__': True}] + self.valid_clear_load["arg"] = [ + {"text": "KWMSG: a message", "anything": "hello all", "__kwarg__": True} + ] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) - @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "PAM eauth not available on Windows") def test_args_mixed_match(self): - ''' + """ Test mixed args and kwargs restriction allowed. 'test_user_func': @@ -517,26 +621,38 @@ class MasterACLTestCase(ModuleCase): kwargs: 'kwa': 'kwa.*' 'kwb': 'kwb' - ''' - _check_minions_return = {'minions': ['some_minions'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): - self.valid_clear_load['kwargs'].update({'username': 'test_user_func'}) - self.valid_clear_load.update({'user': 'test_user_func', - 'tgt': '*', - 'fun': 'my_mod.some_func', - 'arg': ['alpha', - 'beta', - 'gamma', - {'kwa': 'kwarg #1', - 'kwb': 'kwb', - 'one_more': 'just one more', - '__kwarg__': True}]}) + """ + _check_minions_return = {"minions": ["some_minions"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): + self.valid_clear_load["kwargs"].update({"username": "test_user_func"}) + self.valid_clear_load.update( + { + "user": "test_user_func", + "tgt": "*", + "fun": "my_mod.some_func", + "arg": [ + "alpha", + "beta", + "gamma", + { + "kwa": "kwarg #1", + "kwb": "kwb", + "one_more": "just one more", + "__kwarg__": True, + }, + ], + } + ) self.clear.publish(self.valid_clear_load) - self.assertEqual(self.fire_event_mock.call_args[0][0]['fun'], - 'my_mod.some_func') + self.assertEqual( + self.fire_event_mock.call_args[0][0]["fun"], "my_mod.some_func" + ) def test_args_mixed_mismatch(self): - ''' + """ Test mixed args and kwargs restriction forbidden. 'test_user_func': @@ -548,115 +664,138 @@ class MasterACLTestCase(ModuleCase): kwargs: 'kwa': 'kwa.*' 'kwb': 'kwb' - ''' - _check_minions_return = {'minions': ['some_minions'], 'missing': []} - with patch('salt.utils.minions.CkMinions.check_minions', MagicMock(return_value=_check_minions_return)): - self.valid_clear_load['kwargs'].update({'username': 'test_user_func'}) - self.valid_clear_load.update({'user': 'test_user_func', - 'tgt': '*', - 'fun': 'my_mod.some_func'}) + """ + _check_minions_return = {"minions": ["some_minions"], "missing": []} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock(return_value=_check_minions_return), + ): + self.valid_clear_load["kwargs"].update({"username": "test_user_func"}) + self.valid_clear_load.update( + {"user": "test_user_func", "tgt": "*", "fun": "my_mod.some_func"} + ) # Wrong arg value - self.valid_clear_load['arg'] = ['alpha', - 'gamma', - {'kwa': 'kwarg #1', - 'kwb': 'kwb', - 'one_more': 'just one more', - '__kwarg__': True}] + self.valid_clear_load["arg"] = [ + "alpha", + "gamma", + { + "kwa": "kwarg #1", + "kwb": "kwb", + "one_more": "just one more", + "__kwarg__": True, + }, + ] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) # Wrong kwarg value - self.valid_clear_load['arg'] = ['alpha', - 'beta', - 'gamma', - {'kwa': 'kkk', - 'kwb': 'kwb', - 'one_more': 'just one more', - '__kwarg__': True}] + self.valid_clear_load["arg"] = [ + "alpha", + "beta", + "gamma", + { + "kwa": "kkk", + "kwb": "kwb", + "one_more": "just one more", + "__kwarg__": True, + }, + ] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) # Missing arg - self.valid_clear_load['arg'] = ['alpha', - {'kwa': 'kwarg #1', - 'kwb': 'kwb', - 'one_more': 'just one more', - '__kwarg__': True}] + self.valid_clear_load["arg"] = [ + "alpha", + { + "kwa": "kwarg #1", + "kwb": "kwb", + "one_more": "just one more", + "__kwarg__": True, + }, + ] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) # Missing kwarg - self.valid_clear_load['arg'] = ['alpha', - 'beta', - 'gamma', - {'kwa': 'kwarg #1', - 'one_more': 'just one more', - '__kwarg__': True}] + self.valid_clear_load["arg"] = [ + "alpha", + "beta", + "gamma", + {"kwa": "kwarg #1", "one_more": "just one more", "__kwarg__": True}, + ] self.clear.publish(self.valid_clear_load) self.assertEqual(self.fire_event_mock.mock_calls, []) class AuthACLTestCase(ModuleCase): - ''' + """ A class to check various aspects of the publisher ACL system - ''' + """ + def setUp(self): self.auth_check_mock = MagicMock(return_value=True) - opts = self.get_temp_config('master') + opts = self.get_temp_config("master") patches = ( - ('salt.minion.MasterMinion', MagicMock()), - ('salt.utils.verify.check_path_traversal', MagicMock()), - ('salt.utils.minions.CkMinions.auth_check', self.auth_check_mock), - ('salt.auth.LoadAuth.time_auth', MagicMock(return_value=True)), - ('salt.client.get_local_client', MagicMock(return_value=opts['conf_file'])), + ("salt.minion.MasterMinion", MagicMock()), + ("salt.utils.verify.check_path_traversal", MagicMock()), + ("salt.utils.minions.CkMinions.auth_check", self.auth_check_mock), + ("salt.auth.LoadAuth.time_auth", MagicMock(return_value=True)), + ("salt.client.get_local_client", MagicMock(return_value=opts["conf_file"])), ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) - self.addCleanup(delattr, self, 'auth_check_mock') + self.addCleanup(delattr, self, "auth_check_mock") - opts['publisher_acl'] = {} - opts['publisher_acl_blacklist'] = {} - opts['master_job_cache'] = '' - opts['sign_pub_messages'] = False - opts['con_cache'] = '' - opts['external_auth'] = {} - opts['external_auth']['pam'] = {'test_user': [{'alpha_minion': ['test.ping']}]} + opts["publisher_acl"] = {} + opts["publisher_acl_blacklist"] = {} + opts["master_job_cache"] = "" + opts["sign_pub_messages"] = False + opts["con_cache"] = "" + opts["external_auth"] = {} + opts["external_auth"]["pam"] = {"test_user": [{"alpha_minion": ["test.ping"]}]} self.clear = salt.master.ClearFuncs(opts, MagicMock()) - self.addCleanup(delattr, self, 'clear') + self.addCleanup(delattr, self, "clear") # overwrite the _send_pub method so we don't have to serialize MagicMock self.clear._send_pub = lambda payload: True # make sure to return a JID, instead of a mock - self.clear.mminion.returners = {'.prep_jid': lambda x: 1} + self.clear.mminion.returners = {".prep_jid": lambda x: 1} - self.valid_clear_load = {'tgt_type': 'glob', - 'jid': '', - 'cmd': 'publish', - 'tgt': 'test_minion', - 'kwargs': - {'username': 'test_user', - 'password': 'test_password', - 'show_timeout': False, - 'eauth': 'pam', - 'show_jid': False}, - 'ret': '', - 'user': 'test_user', - 'key': '', - 'arg': '', - 'fun': 'test.ping', - } - self.addCleanup(delattr, self, 'valid_clear_load') + self.valid_clear_load = { + "tgt_type": "glob", + "jid": "", + "cmd": "publish", + "tgt": "test_minion", + "kwargs": { + "username": "test_user", + "password": "test_password", + "show_timeout": False, + "eauth": "pam", + "show_jid": False, + }, + "ret": "", + "user": "test_user", + "key": "", + "arg": "", + "fun": "test.ping", + } + self.addCleanup(delattr, self, "valid_clear_load") - @skipIf(salt.utils.platform.is_windows(), 'PAM eauth not available on Windows') + @skipIf(salt.utils.platform.is_windows(), "PAM eauth not available on Windows") def test_acl_simple_allow(self): self.clear.publish(self.valid_clear_load) - self.assertEqual(self.auth_check_mock.call_args[0][0], - [{'alpha_minion': ['test.ping']}]) + self.assertEqual( + self.auth_check_mock.call_args[0][0], [{"alpha_minion": ["test.ping"]}] + ) def test_acl_simple_deny(self): - with patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[{'beta_minion': ['test.ping']}])): + with patch( + "salt.auth.LoadAuth.get_auth_list", + MagicMock(return_value=[{"beta_minion": ["test.ping"]}]), + ): self.clear.publish(self.valid_clear_load) - self.assertEqual(self.auth_check_mock.call_args[0][0], - [{'beta_minion': ['test.ping']}]) + self.assertEqual( + self.auth_check_mock.call_args[0][0], [{"beta_minion": ["test.ping"]}] + ) diff --git a/tests/unit/test_beacons.py b/tests/unit/test_beacons.py index 837cd114f41..d51ce9ecd98 100644 --- a/tests/unit/test_beacons.py +++ b/tests/unit/test_beacons.py @@ -1,43 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the beacon_module parameter -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import patch +import logging # Import Salt Libs import salt.beacons as beacons import salt.config -import logging +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + log = logging.getLogger(__name__) class BeaconsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt beacon_module parameter - ''' + """ def setup_loader_modules(self): return {beacons: {}} def test_beacon_module(self): - ''' + """ Test that beacon_module parameter for beacon configuration - ''' + """ mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() - mock_opts['id'] = 'minion' - mock_opts['__role'] = 'minion' - mock_opts['beacons'] = {'watch_apache': [{'processes': {'apache2': 'stopped'}}, - {'beacon_module': 'ps'}]} + mock_opts["id"] = "minion" + mock_opts["__role"] = "minion" + mock_opts["beacons"] = { + "watch_apache": [ + {"processes": {"apache2": "stopped"}}, + {"beacon_module": "ps"}, + ] + } with patch.dict(beacons.__opts__, mock_opts): - ret = salt.beacons.Beacon(mock_opts, []).process(mock_opts['beacons'], mock_opts['grains']) - _expected = [{'tag': 'salt/beacon/minion/watch_apache/', - 'data': {'id': u'minion', u'apache2': u'Stopped'}, - 'beacon_name': 'ps'}] + ret = salt.beacons.Beacon(mock_opts, []).process( + mock_opts["beacons"], mock_opts["grains"] + ) + _expected = [ + { + "tag": "salt/beacon/minion/watch_apache/", + "data": {"id": "minion", "apache2": "Stopped"}, + "beacon_name": "ps", + } + ] self.assertEqual(ret, _expected) diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index 94704be116f..0818c09c641 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -1,41 +1,38 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place <mp@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.mixins import SaltClientTestCaseMixin -from tests.support.mock import patch, MagicMock -from tests.support.unit import TestCase, skipIf +import salt.utils.platform # Import Salt libs from salt import client -import salt.utils.platform from salt.exceptions import ( - EauthAuthenticationError, SaltInvocationError, SaltClientError, SaltReqTimeoutError + EauthAuthenticationError, + SaltClientError, + SaltInvocationError, + SaltReqTimeoutError, ) +# Import Salt Testing libs +from tests.support.mixins import SaltClientTestCaseMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -class LocalClientTestCase(TestCase, - SaltClientTestCaseMixin): +class LocalClientTestCase(TestCase, SaltClientTestCaseMixin): def test_job_result_return_success(self): """ Should return the `expected_return`, since there is a job with the right jid. """ minions = () - jid = '0815' - raw_return = { - 'id': 'fake-id', - 'jid': jid, - 'data': '', - 'return': 'fake-return' - } - expected_return = {'fake-id': {'ret': 'fake-return'}} - local_client = client.LocalClient(mopts=self.get_temp_config('master')) + jid = "0815" + raw_return = {"id": "fake-id", "jid": jid, "data": "", "return": "fake-return"} + expected_return = {"fake-id": {"ret": "fake-return"}} + local_client = client.LocalClient(mopts=self.get_temp_config("master")) local_client.event.get_event = MagicMock(return_value=raw_return) local_client.returners = MagicMock() ret = local_client.get_event_iter_returns(jid, minions) @@ -48,14 +45,14 @@ class LocalClientTestCase(TestCase, get a StopIteration exception. """ minions = () - jid = '0815' + jid = "0815" raw_return = { - 'id': 'fake-id', - 'jid': '0816', - 'data': '', - 'return': 'fake-return' + "id": "fake-id", + "jid": "0816", + "data": "", + "return": "fake-return", } - local_client = client.LocalClient(mopts=self.get_temp_config('master')) + local_client = client.LocalClient(mopts=self.get_temp_config("master")) local_client.event.get_event = MagicMock() local_client.event.get_event.side_effect = [raw_return, None] local_client.returners = MagicMock() @@ -64,105 +61,185 @@ class LocalClientTestCase(TestCase, next(ret) def test_create_local_client(self): - local_client = client.LocalClient(mopts=self.get_temp_config('master')) - self.assertIsInstance(local_client, client.LocalClient, 'LocalClient did not create a LocalClient instance') + local_client = client.LocalClient(mopts=self.get_temp_config("master")) + self.assertIsInstance( + local_client, + client.LocalClient, + "LocalClient did not create a LocalClient instance", + ) def test_check_pub_data(self): - just_minions = {'minions': ['m1', 'm2']} - jid_no_minions = {'jid': '1234', 'minions': []} - valid_pub_data = {'minions': ['m1', 'm2'], 'jid': '1234'} + just_minions = {"minions": ["m1", "m2"]} + jid_no_minions = {"jid": "1234", "minions": []} + valid_pub_data = {"minions": ["m1", "m2"], "jid": "1234"} - self.assertRaises(EauthAuthenticationError, self.client._check_pub_data, '') - self.assertDictEqual({}, + self.assertRaises(EauthAuthenticationError, self.client._check_pub_data, "") + self.assertDictEqual( + {}, self.client._check_pub_data(just_minions), - 'Did not handle lack of jid correctly') + "Did not handle lack of jid correctly", + ) self.assertDictEqual( {}, - self.client._check_pub_data({'jid': '0'}), - 'Passing JID of zero is not handled gracefully') + self.client._check_pub_data({"jid": "0"}), + "Passing JID of zero is not handled gracefully", + ) with patch.dict(self.client.opts, {}): self.client._check_pub_data(jid_no_minions) - self.assertDictEqual(valid_pub_data, self.client._check_pub_data(valid_pub_data)) + self.assertDictEqual( + valid_pub_data, self.client._check_pub_data(valid_pub_data) + ) def test_cmd_subset(self): - with patch('salt.client.LocalClient.cmd', return_value={'minion1': ['first.func', 'second.func'], - 'minion2': ['first.func', 'second.func']}): - with patch('salt.client.LocalClient.cmd_cli') as cmd_cli_mock: - self.client.cmd_subset('*', 'first.func', sub=1, cli=True) + with patch( + "salt.client.LocalClient.cmd", + return_value={ + "minion1": ["first.func", "second.func"], + "minion2": ["first.func", "second.func"], + }, + ): + with patch("salt.client.LocalClient.cmd_cli") as cmd_cli_mock: + self.client.cmd_subset("*", "first.func", sub=1, cli=True) try: - cmd_cli_mock.assert_called_with(['minion2'], 'first.func', (), progress=False, - kwarg=None, tgt_type='list', full_return=False, - ret='') + cmd_cli_mock.assert_called_with( + ["minion2"], + "first.func", + (), + progress=False, + kwarg=None, + tgt_type="list", + full_return=False, + ret="", + ) except AssertionError: - cmd_cli_mock.assert_called_with(['minion1'], 'first.func', (), progress=False, - kwarg=None, tgt_type='list', full_return=False, - ret='') - self.client.cmd_subset('*', 'first.func', sub=10, cli=True) + cmd_cli_mock.assert_called_with( + ["minion1"], + "first.func", + (), + progress=False, + kwarg=None, + tgt_type="list", + full_return=False, + ret="", + ) + self.client.cmd_subset("*", "first.func", sub=10, cli=True) try: - cmd_cli_mock.assert_called_with(['minion2', 'minion1'], 'first.func', (), progress=False, - kwarg=None, tgt_type='list', full_return=False, - ret='') + cmd_cli_mock.assert_called_with( + ["minion2", "minion1"], + "first.func", + (), + progress=False, + kwarg=None, + tgt_type="list", + full_return=False, + ret="", + ) except AssertionError: - cmd_cli_mock.assert_called_with(['minion1', 'minion2'], 'first.func', (), progress=False, - kwarg=None, tgt_type='list', full_return=False, - ret='') + cmd_cli_mock.assert_called_with( + ["minion1", "minion2"], + "first.func", + (), + progress=False, + kwarg=None, + tgt_type="list", + full_return=False, + ret="", + ) - ret = self.client.cmd_subset('*', 'first.func', sub=1, cli=True, full_return=True) + ret = self.client.cmd_subset( + "*", "first.func", sub=1, cli=True, full_return=True + ) try: - cmd_cli_mock.assert_called_with(['minion2'], 'first.func', (), progress=False, - kwarg=None, tgt_type='list', full_return=True, - ret='') + cmd_cli_mock.assert_called_with( + ["minion2"], + "first.func", + (), + progress=False, + kwarg=None, + tgt_type="list", + full_return=True, + ret="", + ) except AssertionError: - cmd_cli_mock.assert_called_with(['minion1'], 'first.func', (), progress=False, - kwarg=None, tgt_type='list', full_return=True, - ret='') + cmd_cli_mock.assert_called_with( + ["minion1"], + "first.func", + (), + progress=False, + kwarg=None, + tgt_type="list", + full_return=True, + ret="", + ) - @skipIf(salt.utils.platform.is_windows(), 'Not supported on Windows') + @skipIf(salt.utils.platform.is_windows(), "Not supported on Windows") def test_pub(self): - ''' + """ Tests that the client cleanly returns when the publisher is not running Note: Requires ZeroMQ's IPC transport which is not supported on windows. - ''' - if self.get_config('minion')['transport'] != 'zeromq': - self.skipTest('This test only works with ZeroMQ') + """ + if self.get_config("minion")["transport"] != "zeromq": + self.skipTest("This test only works with ZeroMQ") # Make sure we cleanly return if the publisher isn't running - with patch('os.path.exists', return_value=False): - self.assertRaises(SaltClientError, lambda: self.client.pub('*', 'test.ping')) + with patch("os.path.exists", return_value=False): + self.assertRaises( + SaltClientError, lambda: self.client.pub("*", "test.ping") + ) # Check nodegroups behavior - with patch('os.path.exists', return_value=True): - with patch.dict(self.client.opts, - {'nodegroups': - {'group1': 'L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com'}}): + with patch("os.path.exists", return_value=True): + with patch.dict( + self.client.opts, + { + "nodegroups": { + "group1": "L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com" + } + }, + ): # Do we raise an exception if the nodegroup can't be matched? - self.assertRaises(SaltInvocationError, - self.client.pub, - 'non_existent_group', 'test.ping', tgt_type='nodegroup') + self.assertRaises( + SaltInvocationError, + self.client.pub, + "non_existent_group", + "test.ping", + tgt_type="nodegroup", + ) - @skipIf(not salt.utils.platform.is_windows(), 'Windows only test') + @skipIf(not salt.utils.platform.is_windows(), "Windows only test") def test_pub_win32(self): - ''' + """ Tests that the client raises a timeout error when using ZeroMQ's TCP transport and publisher is not running. Note: Requires ZeroMQ's TCP transport, this is only the default on Windows. - ''' - if self.get_config('minion')['transport'] != 'zeromq': - self.skipTest('This test only works with ZeroMQ') + """ + if self.get_config("minion")["transport"] != "zeromq": + self.skipTest("This test only works with ZeroMQ") # Make sure we cleanly return if the publisher isn't running - with patch('os.path.exists', return_value=False): - self.assertRaises(SaltReqTimeoutError, lambda: self.client.pub('*', 'test.ping')) + with patch("os.path.exists", return_value=False): + self.assertRaises( + SaltReqTimeoutError, lambda: self.client.pub("*", "test.ping") + ) # Check nodegroups behavior - with patch('os.path.exists', return_value=True): - with patch.dict(self.client.opts, - {'nodegroups': - {'group1': 'L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com'}}): + with patch("os.path.exists", return_value=True): + with patch.dict( + self.client.opts, + { + "nodegroups": { + "group1": "L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com" + } + }, + ): # Do we raise an exception if the nodegroup can't be matched? - self.assertRaises(SaltInvocationError, - self.client.pub, - 'non_existent_group', 'test.ping', tgt_type='nodegroup') + self.assertRaises( + SaltInvocationError, + self.client.pub, + "non_existent_group", + "test.ping", + tgt_type="nodegroup", + ) diff --git a/tests/unit/test_cloud.py b/tests/unit/test_cloud.py index cbf01181003..c5e94d29e3c 100644 --- a/tests/unit/test_cloud.py +++ b/tests/unit/test_cloud.py @@ -1,193 +1,232 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Eric Radman <ericshane@eradman.com> -''' +""" # Import Python libs from __future__ import absolute_import -import os -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch -from tests.support.runtests import RUNTIME_VARS +import os # Import Salt libs import salt.cloud import salt.config +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing libs +from tests.support.unit import TestCase EXAMPLE_PROVIDERS = { - 'nyc_vcenter': {'vmware': {'driver': 'vmware', - 'password': '123456', - 'url': 'vca1.saltstack.com', - 'minion': { - 'master': 'providermaster', - 'grains': { - 'providergrain': True - } - }, - 'profiles': {}, - 'user': 'root'}}, - 'nj_vcenter': {'vmware': {'driver': 'vmware', - 'password': '333', - 'profiles': {}, - 'minion': { - 'master': 'providermaster', - 'grains': { - 'providergrain': True - } - }, - 'image': 'rhel6_64prod', - 'url': 'vca2.saltstack.com', - 'user': 'root'}} + "nyc_vcenter": { + "vmware": { + "driver": "vmware", + "password": "123456", + "url": "vca1.saltstack.com", + "minion": {"master": "providermaster", "grains": {"providergrain": True}}, + "profiles": {}, + "user": "root", + } + }, + "nj_vcenter": { + "vmware": { + "driver": "vmware", + "password": "333", + "profiles": {}, + "minion": {"master": "providermaster", "grains": {"providergrain": True}}, + "image": "rhel6_64prod", + "url": "vca2.saltstack.com", + "user": "root", + } + }, } EXAMPLE_PROFILES = { - 'nyc-vm': {'cluster': 'nycvirt', - 'datastore': 'datastore1', - 'devices': {'disk': {'Hard disk 1': {'controller': 'SCSI controller 1', - 'size': 20}}, - 'network': {'Network Adapter 1': {'mac': '88:88:88:88:88:42', - 'name': 'vlan50', - 'switch_type': 'standard'}}, - 'scsi': {'SCSI controller 1': {'type': 'paravirtual'}}}, - 'extra_config': {'mem.hotadd': 'yes'}, - 'folder': 'coreinfra', - 'image': 'rhel6_64Guest', - 'minion': { - 'master': 'profilemaster', - 'grains': { - 'profilegrain': True + "nyc-vm": { + "cluster": "nycvirt", + "datastore": "datastore1", + "devices": { + "disk": {"Hard disk 1": {"controller": "SCSI controller 1", "size": 20}}, + "network": { + "Network Adapter 1": { + "mac": "88:88:88:88:88:42", + "name": "vlan50", + "switch_type": "standard", } }, - 'memory': '8GB', - 'num_cpus': 2, - 'power_on': True, - 'profile': 'nyc-vm', - 'provider': 'nyc_vcenter:vmware', - 'resourcepool': 'Resources'}, - 'nj-vm': {'cluster': 'njvirt', - 'folder': 'coreinfra', - 'image': 'rhel6_64Guest', - 'memory': '8GB', - 'num_cpus': 2, - 'power_on': True, - 'profile': 'nj-vm', - 'provider': 'nj_vcenter:vmware', - 'resourcepool': 'Resources'}, - + "scsi": {"SCSI controller 1": {"type": "paravirtual"}}, + }, + "extra_config": {"mem.hotadd": "yes"}, + "folder": "coreinfra", + "image": "rhel6_64Guest", + "minion": {"master": "profilemaster", "grains": {"profilegrain": True}}, + "memory": "8GB", + "num_cpus": 2, + "power_on": True, + "profile": "nyc-vm", + "provider": "nyc_vcenter:vmware", + "resourcepool": "Resources", + }, + "nj-vm": { + "cluster": "njvirt", + "folder": "coreinfra", + "image": "rhel6_64Guest", + "memory": "8GB", + "num_cpus": 2, + "power_on": True, + "profile": "nj-vm", + "provider": "nj_vcenter:vmware", + "resourcepool": "Resources", + }, } EXAMPLE_MAP = { - 'nyc-vm': {'db1': {'cpus': 4, - 'devices': {'disk': {'Hard disk 1': {'size': 40}}, - 'network': {'Network Adapter 1': {'mac': '22:4a:b2:92:b3:eb'}}}, - 'memory': '16GB', - 'minion': { - 'master': 'mapmaster', - 'grains': { - 'mapgrain': True - } - }, - 'name': 'db1'}, - 'db2': {'name': 'db2', - 'password': '456', - 'provider': 'nj_vcenter:vmware'}}, - 'nj-vm': {'db3': {'name': 'db3', - 'password': '789', - }} + "nyc-vm": { + "db1": { + "cpus": 4, + "devices": { + "disk": {"Hard disk 1": {"size": 40}}, + "network": {"Network Adapter 1": {"mac": "22:4a:b2:92:b3:eb"}}, + }, + "memory": "16GB", + "minion": {"master": "mapmaster", "grains": {"mapgrain": True}}, + "name": "db1", + }, + "db2": {"name": "db2", "password": "456", "provider": "nj_vcenter:vmware"}, + }, + "nj-vm": {"db3": {"name": "db3", "password": "789"}}, } class MapConfTest(TestCase): - ''' + """ Validate evaluation of salt-cloud map configuration - ''' + """ def test_cloud_map_merge_conf(self): - ''' + """ Ensure that nested values can be selectivly overridden in a map file - ''' - with patch('salt.config.check_driver_dependencies', MagicMock(return_value=True)), \ - patch('salt.cloud.Map.read', MagicMock(return_value=EXAMPLE_MAP)): + """ + with patch( + "salt.config.check_driver_dependencies", MagicMock(return_value=True) + ), patch("salt.cloud.Map.read", MagicMock(return_value=EXAMPLE_MAP)): self.maxDiff = None - opts = salt.config.cloud_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'cloud')) - opts.update({ - 'optimization_order': [0, 1, 2], - 'providers': EXAMPLE_PROVIDERS, - 'profiles': EXAMPLE_PROFILES - }) + opts = salt.config.cloud_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "cloud") + ) + opts.update( + { + "optimization_order": [0, 1, 2], + "providers": EXAMPLE_PROVIDERS, + "profiles": EXAMPLE_PROFILES, + } + ) cloud_map = salt.cloud.Map(opts) merged_profile = { - 'create': {'db1': {'cluster': 'nycvirt', - 'cpus': 4, - 'datastore': 'datastore1', - 'devices': {'disk': {'Hard disk 1': {'controller': 'SCSI controller 1', - 'size': 40}}, - 'network': {'Network Adapter 1': {'mac': '22:4a:b2:92:b3:eb', - 'name': 'vlan50', - 'switch_type': 'standard'}}, - 'scsi': {'SCSI controller 1': {'type': 'paravirtual'}}}, - 'driver': 'vmware', - 'extra_config': {'mem.hotadd': 'yes'}, - 'folder': 'coreinfra', - 'image': 'rhel6_64Guest', - 'memory': '16GB', - 'minion': {'grains': {'mapgrain': True, - 'profilegrain': True, - 'providergrain': True}, - 'master': 'mapmaster'}, - 'name': 'db1', - 'num_cpus': 2, - 'password': '123456', - 'power_on': True, - 'profile': 'nyc-vm', - 'provider': 'nyc_vcenter:vmware', - 'resourcepool': 'Resources', - 'url': 'vca1.saltstack.com', - 'user': 'root'}, - 'db2': {'cluster': 'nycvirt', - 'datastore': 'datastore1', - 'devices': {'disk': {'Hard disk 1': {'controller': 'SCSI controller 1', - 'size': 20}}, - 'network': {'Network Adapter 1': {'mac': '88:88:88:88:88:42', - 'name': 'vlan50', - 'switch_type': 'standard'}}, - 'scsi': {'SCSI controller 1': {'type': 'paravirtual'}}}, - 'driver': 'vmware', - 'extra_config': {'mem.hotadd': 'yes'}, - 'folder': 'coreinfra', - 'image': 'rhel6_64Guest', - 'memory': '8GB', - 'minion': {'grains': {'profilegrain': True, - 'providergrain': True}, - 'master': 'profilemaster'}, - 'name': 'db2', - 'num_cpus': 2, - 'password': '456', - 'power_on': True, - 'profile': 'nyc-vm', - 'provider': 'nj_vcenter:vmware', - 'resourcepool': 'Resources', - 'url': 'vca2.saltstack.com', - 'user': 'root'}, - 'db3': {'cluster': 'njvirt', - 'driver': 'vmware', - 'folder': 'coreinfra', - 'image': 'rhel6_64Guest', - 'memory': '8GB', - 'minion': {'grains': {'providergrain': True}, - 'master': 'providermaster'}, - 'name': 'db3', - 'num_cpus': 2, - 'password': '789', - 'power_on': True, - 'profile': 'nj-vm', - 'provider': 'nj_vcenter:vmware', - 'resourcepool': 'Resources', - 'url': 'vca2.saltstack.com', - 'user': 'root'}} + "create": { + "db1": { + "cluster": "nycvirt", + "cpus": 4, + "datastore": "datastore1", + "devices": { + "disk": { + "Hard disk 1": { + "controller": "SCSI controller 1", + "size": 40, + } + }, + "network": { + "Network Adapter 1": { + "mac": "22:4a:b2:92:b3:eb", + "name": "vlan50", + "switch_type": "standard", + } + }, + "scsi": {"SCSI controller 1": {"type": "paravirtual"}}, + }, + "driver": "vmware", + "extra_config": {"mem.hotadd": "yes"}, + "folder": "coreinfra", + "image": "rhel6_64Guest", + "memory": "16GB", + "minion": { + "grains": { + "mapgrain": True, + "profilegrain": True, + "providergrain": True, + }, + "master": "mapmaster", + }, + "name": "db1", + "num_cpus": 2, + "password": "123456", + "power_on": True, + "profile": "nyc-vm", + "provider": "nyc_vcenter:vmware", + "resourcepool": "Resources", + "url": "vca1.saltstack.com", + "user": "root", + }, + "db2": { + "cluster": "nycvirt", + "datastore": "datastore1", + "devices": { + "disk": { + "Hard disk 1": { + "controller": "SCSI controller 1", + "size": 20, + } + }, + "network": { + "Network Adapter 1": { + "mac": "88:88:88:88:88:42", + "name": "vlan50", + "switch_type": "standard", + } + }, + "scsi": {"SCSI controller 1": {"type": "paravirtual"}}, + }, + "driver": "vmware", + "extra_config": {"mem.hotadd": "yes"}, + "folder": "coreinfra", + "image": "rhel6_64Guest", + "memory": "8GB", + "minion": { + "grains": {"profilegrain": True, "providergrain": True}, + "master": "profilemaster", + }, + "name": "db2", + "num_cpus": 2, + "password": "456", + "power_on": True, + "profile": "nyc-vm", + "provider": "nj_vcenter:vmware", + "resourcepool": "Resources", + "url": "vca2.saltstack.com", + "user": "root", + }, + "db3": { + "cluster": "njvirt", + "driver": "vmware", + "folder": "coreinfra", + "image": "rhel6_64Guest", + "memory": "8GB", + "minion": { + "grains": {"providergrain": True}, + "master": "providermaster", + }, + "name": "db3", + "num_cpus": 2, + "password": "789", + "power_on": True, + "profile": "nj-vm", + "provider": "nj_vcenter:vmware", + "resourcepool": "Resources", + "url": "vca2.saltstack.com", + "user": "root", + }, + } } # what we assert above w.r.t db2 using nj_vcenter:vmware provider: diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index 4c811c43976..b36779309f2 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -1,25 +1,15 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for salt.config -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import os import textwrap -# Import Salt Testing libs -from tests.support.helpers import with_tempdir, with_tempfile, patched_environ -from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.unit import skipIf, TestCase -from tests.support.runtests import RUNTIME_VARS -from tests.support.mock import ( - Mock, - MagicMock, - patch -) - # Import Salt libs import salt.config import salt.minion @@ -28,22 +18,30 @@ import salt.utils.files import salt.utils.network import salt.utils.platform import salt.utils.yaml -from salt.ext import six -from salt.syspaths import CONFIG_DIR from salt.exceptions import ( CommandExecutionError, + SaltCloudConfigError, SaltConfigurationError, - SaltCloudConfigError ) +from salt.ext import six +from salt.syspaths import CONFIG_DIR + +# Import Salt Testing libs +from tests.support.helpers import patched_environ, with_tempdir, with_tempfile +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.mock import MagicMock, Mock, patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf log = logging.getLogger(__name__) -SAMPLE_CONF_DIR = os.path.join(RUNTIME_VARS.CODE_DIR, 'conf') + os.sep +SAMPLE_CONF_DIR = os.path.join(RUNTIME_VARS.CODE_DIR, "conf") + os.sep # mock hostname should be more complex than the systems FQDN -MOCK_HOSTNAME = 'very.long.complex.fqdn.that.is.crazy.extra.long.example.com' +MOCK_HOSTNAME = "very.long.complex.fqdn.that.is.crazy.extra.long.example.com" -MOCK_ETC_HOSTS = textwrap.dedent('''\ +MOCK_ETC_HOSTS = textwrap.dedent( + """\ ## # Host Database # @@ -57,142 +55,140 @@ MOCK_ETC_HOSTS = textwrap.dedent('''\ ::1 ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix - '''.format(hostname=MOCK_HOSTNAME)) -MOCK_ETC_HOSTNAME = '{}\n'.format(MOCK_HOSTNAME) -PATH = 'path/to/some/cloud/conf/file' -DEFAULT = {'default_include': PATH} + """.format( + hostname=MOCK_HOSTNAME + ) +) +MOCK_ETC_HOSTNAME = "{}\n".format(MOCK_HOSTNAME) +PATH = "path/to/some/cloud/conf/file" +DEFAULT = {"default_include": PATH} class DefaultConfigsBase(object): - @classmethod def setUpClass(cls): cls.mock_master_default_opts = dict( root_dir=RUNTIME_VARS.TMP_ROOT_DIR, - log_file=os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'var', 'log', 'salt', 'master'), - pid_file=os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'var', 'run', 'salt-master.pid') + log_file=os.path.join( + RUNTIME_VARS.TMP_ROOT_DIR, "var", "log", "salt", "master" + ), + pid_file=os.path.join( + RUNTIME_VARS.TMP_ROOT_DIR, "var", "run", "salt-master.pid" + ), ) class SampleConfTest(DefaultConfigsBase, TestCase): - ''' + """ Validate files in the salt/conf directory. - ''' + """ def test_conf_master_sample_is_commented(self): - ''' + """ The sample config file located in salt/conf/master must be completely commented out. This test checks for any lines that are not commented or blank. - ''' - master_config = SAMPLE_CONF_DIR + 'master' + """ + master_config = SAMPLE_CONF_DIR + "master" ret = salt.config._read_conf_file(master_config) self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( - master_config - ) + "Sample config file '{}' must be commented out.".format(master_config), ) def test_conf_minion_sample_is_commented(self): - ''' + """ The sample config file located in salt/conf/minion must be completely commented out. This test checks for any lines that are not commented or blank. - ''' - minion_config = SAMPLE_CONF_DIR + 'minion' + """ + minion_config = SAMPLE_CONF_DIR + "minion" ret = salt.config._read_conf_file(minion_config) self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( - minion_config - ) + "Sample config file '{}' must be commented out.".format(minion_config), ) def test_conf_cloud_sample_is_commented(self): - ''' + """ The sample config file located in salt/conf/cloud must be completely commented out. This test checks for any lines that are not commented or blank. - ''' - cloud_config = SAMPLE_CONF_DIR + 'cloud' + """ + cloud_config = SAMPLE_CONF_DIR + "cloud" ret = salt.config._read_conf_file(cloud_config) self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( - cloud_config - ) + "Sample config file '{}' must be commented out.".format(cloud_config), ) def test_conf_cloud_profiles_sample_is_commented(self): - ''' + """ The sample config file located in salt/conf/cloud.profiles must be completely commented out. This test checks for any lines that are not commented or blank. - ''' - cloud_profiles_config = SAMPLE_CONF_DIR + 'cloud.profiles' + """ + cloud_profiles_config = SAMPLE_CONF_DIR + "cloud.profiles" ret = salt.config._read_conf_file(cloud_profiles_config) self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( + "Sample config file '{}' must be commented out.".format( cloud_profiles_config - ) + ), ) def test_conf_cloud_providers_sample_is_commented(self): - ''' + """ The sample config file located in salt/conf/cloud.providers must be completely commented out. This test checks for any lines that are not commented or blank. - ''' - cloud_providers_config = SAMPLE_CONF_DIR + 'cloud.providers' + """ + cloud_providers_config = SAMPLE_CONF_DIR + "cloud.providers" ret = salt.config._read_conf_file(cloud_providers_config) self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( + "Sample config file '{}' must be commented out.".format( cloud_providers_config - ) + ), ) def test_conf_proxy_sample_is_commented(self): - ''' + """ The sample config file located in salt/conf/proxy must be completely commented out. This test checks for any lines that are not commented or blank. - ''' - proxy_config = SAMPLE_CONF_DIR + 'proxy' + """ + proxy_config = SAMPLE_CONF_DIR + "proxy" ret = salt.config._read_conf_file(proxy_config) self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( - proxy_config - ) + "Sample config file '{}' must be commented out.".format(proxy_config), ) def test_conf_roster_sample_is_commented(self): - ''' + """ The sample config file located in salt/conf/roster must be completely commented out. This test checks for any lines that are not commented or blank. - ''' - roster_config = SAMPLE_CONF_DIR + 'roster' + """ + roster_config = SAMPLE_CONF_DIR + "roster" ret = salt.config._read_conf_file(roster_config) self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( - roster_config - ) + "Sample config file '{}' must be commented out.".format(roster_config), ) def test_conf_cloud_profiles_d_files_are_commented(self): - ''' + """ All cloud profile sample configs in salt/conf/cloud.profiles.d/* must be completely commented out. This test loops through all of the files in that directory to check for any lines that are not commented or blank. - ''' - cloud_sample_dir = SAMPLE_CONF_DIR + 'cloud.profiles.d/' + """ + cloud_sample_dir = SAMPLE_CONF_DIR + "cloud.profiles.d/" if not os.path.exists(cloud_sample_dir): - self.skipTest("Sample config directory '{}' is missing.".format(cloud_sample_dir)) + self.skipTest( + "Sample config directory '{}' is missing.".format(cloud_sample_dir) + ) cloud_sample_files = os.listdir(cloud_sample_dir) for conf_file in cloud_sample_files: profile_conf = cloud_sample_dir + conf_file @@ -200,20 +196,20 @@ class SampleConfTest(DefaultConfigsBase, TestCase): self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( - conf_file - ) + "Sample config file '{}' must be commented out.".format(conf_file), ) def test_conf_cloud_providers_d_files_are_commented(self): - ''' + """ All cloud profile sample configs in salt/conf/cloud.providers.d/* must be completely commented out. This test loops through all of the files in that directory to check for any lines that are not commented or blank. - ''' - cloud_sample_dir = SAMPLE_CONF_DIR + 'cloud.providers.d/' + """ + cloud_sample_dir = SAMPLE_CONF_DIR + "cloud.providers.d/" if not os.path.exists(cloud_sample_dir): - self.skipTest("Sample config directory '{}' is missing.".format(cloud_sample_dir)) + self.skipTest( + "Sample config directory '{}' is missing.".format(cloud_sample_dir) + ) cloud_sample_files = os.listdir(cloud_sample_dir) for conf_file in cloud_sample_files: provider_conf = cloud_sample_dir + conf_file @@ -221,20 +217,20 @@ class SampleConfTest(DefaultConfigsBase, TestCase): self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( - conf_file - ) + "Sample config file '{}' must be commented out.".format(conf_file), ) def test_conf_cloud_maps_d_files_are_commented(self): - ''' + """ All cloud profile sample configs in salt/conf/cloud.maps.d/* must be completely commented out. This test loops through all of the files in that directory to check for any lines that are not commented or blank. - ''' - cloud_sample_dir = SAMPLE_CONF_DIR + 'cloud.maps.d/' + """ + cloud_sample_dir = SAMPLE_CONF_DIR + "cloud.maps.d/" if not os.path.exists(cloud_sample_dir): - self.skipTest("Sample config directory '{}' is missing.".format(cloud_sample_dir)) + self.skipTest( + "Sample config directory '{}' is missing.".format(cloud_sample_dir) + ) cloud_sample_files = os.listdir(cloud_sample_dir) for conf_file in cloud_sample_files: map_conf = cloud_sample_dir + conf_file @@ -242,229 +238,198 @@ class SampleConfTest(DefaultConfigsBase, TestCase): self.assertEqual( ret, {}, - 'Sample config file \'{}\' must be commented out.'.format( - conf_file - ) + "Sample config file '{}' must be commented out.".format(conf_file), ) def _unhandled_mock_read(filename): - ''' + """ Raise an error because we should not be calling salt.utils.files.fopen() - ''' - raise CommandExecutionError('Unhandled mock read for {}'.format(filename)) + """ + raise CommandExecutionError("Unhandled mock read for {}".format(filename)) def _salt_configuration_error(filename): - ''' + """ Raise an error to indicate error in the Salt configuration file - ''' - raise SaltConfigurationError('Configuration error in {}'.format(filename)) + """ + raise SaltConfigurationError("Configuration error in {}".format(filename)) class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin): - @with_tempfile() def test_sha256_is_default_for_master(self, fpath): - with salt.utils.files.fopen(fpath, 'w') as wfh: - wfh.write( - "root_dir: /\n" - "key_logfile: key\n" - ) + with salt.utils.files.fopen(fpath, "w") as wfh: + wfh.write("root_dir: /\n" "key_logfile: key\n") config = salt.config.master_config(fpath) - self.assertEqual(config['hash_type'], 'sha256') + self.assertEqual(config["hash_type"], "sha256") @with_tempfile() def test_sha256_is_default_for_minion(self, fpath): - with salt.utils.files.fopen(fpath, 'w') as wfh: - wfh.write( - "root_dir: /\n" - "key_logfile: key\n" - ) + with salt.utils.files.fopen(fpath, "w") as wfh: + wfh.write("root_dir: /\n" "key_logfile: key\n") config = salt.config.minion_config(fpath) - self.assertEqual(config['hash_type'], 'sha256') + self.assertEqual(config["hash_type"], "sha256") @with_tempfile() def test_proper_path_joining(self, fpath): - temp_config = 'root_dir: /\n' \ - 'key_logfile: key\n' + temp_config = "root_dir: /\n" "key_logfile: key\n" if salt.utils.platform.is_windows(): - temp_config = 'root_dir: c:\\\n' \ - 'key_logfile: key\n' - with salt.utils.files.fopen(fpath, 'w') as fp_: + temp_config = "root_dir: c:\\\n" "key_logfile: key\n" + with salt.utils.files.fopen(fpath, "w") as fp_: fp_.write(temp_config) config = salt.config.master_config(fpath) - expect_path_join = os.path.join('/', 'key') - expect_sep_join = '//key' + expect_path_join = os.path.join("/", "key") + expect_sep_join = "//key" if salt.utils.platform.is_windows(): - expect_path_join = os.path.join('c:\\', 'key') - expect_sep_join = 'c:\\\\key' + expect_path_join = os.path.join("c:\\", "key") + expect_sep_join = "c:\\\\key" # os.path.join behavior - self.assertEqual(config['key_logfile'], expect_path_join) + self.assertEqual(config["key_logfile"], expect_path_join) # os.sep.join behavior - self.assertNotEqual(config['key_logfile'], expect_sep_join) + self.assertNotEqual(config["key_logfile"], expect_sep_join) @with_tempdir() def test_common_prefix_stripping(self, tempdir): - root_dir = os.path.join(tempdir, 'foo', 'bar') + root_dir = os.path.join(tempdir, "foo", "bar") os.makedirs(root_dir) - fpath = os.path.join(root_dir, 'config') - with salt.utils.files.fopen(fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(root_dir, fpath) - ) + fpath = os.path.join(root_dir, "config") + with salt.utils.files.fopen(fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(root_dir, fpath)) config = salt.config.master_config(fpath) - self.assertEqual(config['log_file'], fpath) + self.assertEqual(config["log_file"], fpath) @with_tempdir() def test_default_root_dir_included_in_config_root_dir(self, tempdir): - root_dir = os.path.join(tempdir, 'foo', 'bar') + root_dir = os.path.join(tempdir, "foo", "bar") os.makedirs(root_dir) - fpath = os.path.join(root_dir, 'config') - with salt.utils.files.fopen(fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(root_dir, fpath) - ) + fpath = os.path.join(root_dir, "config") + with salt.utils.files.fopen(fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(root_dir, fpath)) config = salt.config.master_config(fpath) - self.assertEqual(config['log_file'], fpath) + self.assertEqual(config["log_file"], fpath) @skipIf( salt.utils.platform.is_windows(), - 'You can\'t set an environment dynamically in Windows') + "You can't set an environment dynamically in Windows", + ) @with_tempdir() def test_load_master_config_from_environ_var(self, tempdir): - env_root_dir = os.path.join(tempdir, 'foo', 'env') + env_root_dir = os.path.join(tempdir, "foo", "env") os.makedirs(env_root_dir) - env_fpath = os.path.join(env_root_dir, 'config-env') + env_fpath = os.path.join(env_root_dir, "config-env") - with salt.utils.files.fopen(env_fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(env_root_dir, env_fpath) - ) + with salt.utils.files.fopen(env_fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(env_root_dir, env_fpath)) with patched_environ(SALT_MASTER_CONFIG=env_fpath): # Should load from env variable, not the default configuration file. - config = salt.config.master_config('{}/master'.format(CONFIG_DIR)) - self.assertEqual(config['log_file'], env_fpath) + config = salt.config.master_config("{}/master".format(CONFIG_DIR)) + self.assertEqual(config["log_file"], env_fpath) - root_dir = os.path.join(tempdir, 'foo', 'bar') + root_dir = os.path.join(tempdir, "foo", "bar") os.makedirs(root_dir) - fpath = os.path.join(root_dir, 'config') - with salt.utils.files.fopen(fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(root_dir, fpath) - ) + fpath = os.path.join(root_dir, "config") + with salt.utils.files.fopen(fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(root_dir, fpath)) # Let's set the environment variable, yet, since the configuration # file path is not the default one, i.e., the user has passed an # alternative configuration file form the CLI parser, the # environment variable will be ignored. with patched_environ(SALT_MASTER_CONFIG=env_fpath): config = salt.config.master_config(fpath) - self.assertEqual(config['log_file'], fpath) + self.assertEqual(config["log_file"], fpath) @skipIf( salt.utils.platform.is_windows(), - 'You can\'t set an environment dynamically in Windows') + "You can't set an environment dynamically in Windows", + ) @with_tempdir() def test_load_minion_config_from_environ_var(self, tempdir): - env_root_dir = os.path.join(tempdir, 'foo', 'env') + env_root_dir = os.path.join(tempdir, "foo", "env") os.makedirs(env_root_dir) - env_fpath = os.path.join(env_root_dir, 'config-env') + env_fpath = os.path.join(env_root_dir, "config-env") - with salt.utils.files.fopen(env_fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(env_root_dir, env_fpath) - ) + with salt.utils.files.fopen(env_fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(env_root_dir, env_fpath)) with patched_environ(SALT_MINION_CONFIG=env_fpath): # Should load from env variable, not the default configuration file - config = salt.config.minion_config('{}/minion'.format(CONFIG_DIR)) - self.assertEqual(config['log_file'], env_fpath) + config = salt.config.minion_config("{}/minion".format(CONFIG_DIR)) + self.assertEqual(config["log_file"], env_fpath) - root_dir = os.path.join(tempdir, 'foo', 'bar') + root_dir = os.path.join(tempdir, "foo", "bar") os.makedirs(root_dir) - fpath = os.path.join(root_dir, 'config') - with salt.utils.files.fopen(fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(root_dir, fpath) - ) + fpath = os.path.join(root_dir, "config") + with salt.utils.files.fopen(fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(root_dir, fpath)) # Let's set the environment variable, yet, since the configuration # file path is not the default one, i.e., the user has passed an # alternative configuration file form the CLI parser, the # environment variable will be ignored. with patched_environ(SALT_MINION_CONFIG=env_fpath): config = salt.config.minion_config(fpath) - self.assertEqual(config['log_file'], fpath) + self.assertEqual(config["log_file"], fpath) @with_tempdir() def test_load_client_config_from_environ_var(self, tempdir): - env_root_dir = os.path.join(tempdir, 'foo', 'env') + env_root_dir = os.path.join(tempdir, "foo", "env") os.makedirs(env_root_dir) # Let's populate a master configuration file which should not get # picked up since the client configuration tries to load the master # configuration settings using the provided client configuration # file - master_config = os.path.join(env_root_dir, 'master') - with salt.utils.files.fopen(master_config, 'w') as fp_: + master_config = os.path.join(env_root_dir, "master") + with salt.utils.files.fopen(master_config, "w") as fp_: fp_.write( - 'blah: true\n' - 'root_dir: {}\n' - 'log_file: {}\n'.format(env_root_dir, master_config) + "blah: true\n" + "root_dir: {}\n" + "log_file: {}\n".format(env_root_dir, master_config) ) # Now the client configuration file - env_fpath = os.path.join(env_root_dir, 'config-env') - with salt.utils.files.fopen(env_fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(env_root_dir, env_fpath) - ) + env_fpath = os.path.join(env_root_dir, "config-env") + with salt.utils.files.fopen(env_fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(env_root_dir, env_fpath)) - with patched_environ(SALT_MASTER_CONFIG=master_config, - SALT_CLIENT_CONFIG=env_fpath): + with patched_environ( + SALT_MASTER_CONFIG=master_config, SALT_CLIENT_CONFIG=env_fpath + ): # Should load from env variable, not the default configuration file - config = salt.config.client_config(os.path.expanduser('~/.salt')) - self.assertEqual(config['log_file'], env_fpath) - self.assertTrue('blah' not in config) + config = salt.config.client_config(os.path.expanduser("~/.salt")) + self.assertEqual(config["log_file"], env_fpath) + self.assertTrue("blah" not in config) - root_dir = os.path.join(tempdir, 'foo', 'bar') + root_dir = os.path.join(tempdir, "foo", "bar") os.makedirs(root_dir) - fpath = os.path.join(root_dir, 'config') - with salt.utils.files.fopen(fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(root_dir, fpath) - ) + fpath = os.path.join(root_dir, "config") + with salt.utils.files.fopen(fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(root_dir, fpath)) # Let's set the environment variable, yet, since the configuration # file path is not the default one, i.e., the user has passed an # alternative configuration file form the CLI parser, the # environment variable will be ignored. - with patched_environ(SALT_MASTER_CONFIG=env_fpath, - SALT_CLIENT_CONFIG=env_fpath): + with patched_environ( + SALT_MASTER_CONFIG=env_fpath, SALT_CLIENT_CONFIG=env_fpath + ): config = salt.config.master_config(fpath) - self.assertEqual(config['log_file'], fpath) + self.assertEqual(config["log_file"], fpath) @with_tempdir() def test_issue_5970_minion_confd_inclusion(self, tempdir): - minion_config = os.path.join(tempdir, 'minion') - minion_confd = os.path.join(tempdir, 'minion.d') + minion_config = os.path.join(tempdir, "minion") + minion_confd = os.path.join(tempdir, "minion.d") os.makedirs(minion_confd) # Let's populate a minion configuration file with some basic # settings - with salt.utils.files.fopen(minion_config, 'w') as fp_: + with salt.utils.files.fopen(minion_config, "w") as fp_: fp_.write( - 'blah: false\n' - 'root_dir: {}\n' - 'log_file: {}\n'.format(tempdir, minion_config) + "blah: false\n" + "root_dir: {}\n" + "log_file: {}\n".format(tempdir, minion_config) ) # Now, let's populate an extra configuration file under minion.d @@ -472,30 +437,30 @@ class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin): # Since the minion.d files are loaded after the main configuration # file so overrides can happen, the final value of blah should be # True. - extra_config = os.path.join(minion_confd, 'extra.conf') - with salt.utils.files.fopen(extra_config, 'w') as fp_: - fp_.write('blah: true\n') + extra_config = os.path.join(minion_confd, "extra.conf") + with salt.utils.files.fopen(extra_config, "w") as fp_: + fp_.write("blah: true\n") # Let's load the configuration config = salt.config.minion_config(minion_config) - self.assertEqual(config['log_file'], minion_config) + self.assertEqual(config["log_file"], minion_config) # As proven by the assertion below, blah is True - self.assertTrue(config['blah']) + self.assertTrue(config["blah"]) @with_tempdir() def test_master_confd_inclusion(self, tempdir): - master_config = os.path.join(tempdir, 'master') - master_confd = os.path.join(tempdir, 'master.d') + master_config = os.path.join(tempdir, "master") + master_confd = os.path.join(tempdir, "master.d") os.makedirs(master_confd) # Let's populate a master configuration file with some basic # settings - with salt.utils.files.fopen(master_config, 'w') as fp_: + with salt.utils.files.fopen(master_config, "w") as fp_: fp_.write( - 'blah: false\n' - 'root_dir: {}\n' - 'log_file: {}\n'.format(tempdir, master_config) + "blah: false\n" + "root_dir: {}\n" + "log_file: {}\n".format(tempdir, master_config) ) # Now, let's populate an extra configuration file under master.d @@ -503,322 +468,372 @@ class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin): # Since the master.d files are loaded after the main configuration # file so overrides can happen, the final value of blah should be # True. - extra_config = os.path.join(master_confd, 'extra.conf') - with salt.utils.files.fopen(extra_config, 'w') as fp_: - fp_.write('blah: true\n') + extra_config = os.path.join(master_confd, "extra.conf") + with salt.utils.files.fopen(extra_config, "w") as fp_: + fp_.write("blah: true\n") # Let's load the configuration config = salt.config.master_config(master_config) - self.assertEqual(config['log_file'], master_config) + self.assertEqual(config["log_file"], master_config) # As proven by the assertion below, blah is True - self.assertTrue(config['blah']) + self.assertTrue(config["blah"]) @with_tempfile() @with_tempdir() def test_master_file_roots_glob(self, tempdir, fpath): # Create some files - for f in 'abc': + for f in "abc": fpath = os.path.join(tempdir, f) - with salt.utils.files.fopen(fpath, 'w') as wfh: + with salt.utils.files.fopen(fpath, "w") as wfh: wfh.write(f) - with salt.utils.files.fopen(fpath, 'w') as wfh: + with salt.utils.files.fopen(fpath, "w") as wfh: wfh.write( - 'file_roots:\n' - ' base:\n' - ' - {}'.format(os.path.join(tempdir, '*')) + "file_roots:\n" + " base:\n" + " - {}".format(os.path.join(tempdir, "*")) ) config = salt.config.master_config(fpath) - base = config['file_roots']['base'] - self.assertEqual(set(base), - {os.path.join(tempdir, 'a'), os.path.join(tempdir, 'b'), os.path.join(tempdir, 'c')}) + base = config["file_roots"]["base"] + self.assertEqual( + set(base), + { + os.path.join(tempdir, "a"), + os.path.join(tempdir, "b"), + os.path.join(tempdir, "c"), + }, + ) def test_validate_bad_file_roots(self): - expected = salt.config._expand_glob_path( - [salt.syspaths.BASE_FILE_ROOTS_DIR] - ) - with patch('salt.config._normalize_roots') as mk: + expected = salt.config._expand_glob_path([salt.syspaths.BASE_FILE_ROOTS_DIR]) + with patch("salt.config._normalize_roots") as mk: ret = salt.config._validate_file_roots(None) assert not mk.called - assert ret == {'base': expected} + assert ret == {"base": expected} @with_tempfile() @with_tempdir() def test_master_pillar_roots_glob(self, tempdir, fpath): # Create some files. - for f in 'abc': + for f in "abc": fpath = os.path.join(tempdir, f) - with salt.utils.files.fopen(fpath, 'w') as wfh: + with salt.utils.files.fopen(fpath, "w") as wfh: wfh.write(f) - with salt.utils.files.fopen(fpath, 'w') as wfh: + with salt.utils.files.fopen(fpath, "w") as wfh: wfh.write( - 'pillar_roots:\n' - ' base:\n' - ' - {}'.format(os.path.join(tempdir, '*')) + "pillar_roots:\n" + " base:\n" + " - {}".format(os.path.join(tempdir, "*")) ) config = salt.config.master_config(fpath) - base = config['pillar_roots']['base'] - self.assertEqual(set(base), - {os.path.join(tempdir, 'a'), os.path.join(tempdir, 'b'), os.path.join(tempdir, 'c')}) + base = config["pillar_roots"]["base"] + self.assertEqual( + set(base), + { + os.path.join(tempdir, "a"), + os.path.join(tempdir, "b"), + os.path.join(tempdir, "c"), + }, + ) def test_validate_bad_pillar_roots(self): - expected = salt.config._expand_glob_path( - [salt.syspaths.BASE_PILLAR_ROOTS_DIR] - ) - with patch('salt.config._normalize_roots') as mk: + expected = salt.config._expand_glob_path([salt.syspaths.BASE_PILLAR_ROOTS_DIR]) + with patch("salt.config._normalize_roots") as mk: ret = salt.config._validate_pillar_roots(None) assert not mk.called - assert ret == {'base': expected} + assert ret == {"base": expected} @with_tempdir() def test_master_id_function(self, tempdir): - master_config = os.path.join(tempdir, 'master') + master_config = os.path.join(tempdir, "master") - with salt.utils.files.fopen(master_config, 'w') as fp_: + with salt.utils.files.fopen(master_config, "w") as fp_: fp_.write( - 'id_function:\n' - ' test.echo:\n' - ' text: hello_world\n' - 'root_dir: {}\n' - 'log_file: {}\n'.format(tempdir, master_config) + "id_function:\n" + " test.echo:\n" + " text: hello_world\n" + "root_dir: {}\n" + "log_file: {}\n".format(tempdir, master_config) ) # Let's load the configuration config = salt.config.master_config(master_config) - self.assertEqual(config['log_file'], master_config) + self.assertEqual(config["log_file"], master_config) # 'master_config' appends '_master' to the ID - self.assertEqual(config['id'], 'hello_world_master') + self.assertEqual(config["id"], "hello_world_master") @with_tempfile() @with_tempdir() def test_minion_file_roots_glob(self, tempdir, fpath): # Create some files. - for f in 'abc': + for f in "abc": fpath = os.path.join(tempdir, f) - with salt.utils.files.fopen(fpath, 'w') as wfh: + with salt.utils.files.fopen(fpath, "w") as wfh: wfh.write(f) - with salt.utils.files.fopen(fpath, 'w') as wfh: + with salt.utils.files.fopen(fpath, "w") as wfh: wfh.write( - 'file_roots:\n' - ' base:\n' - ' - {}'.format(os.path.join(tempdir, '*')) + "file_roots:\n" + " base:\n" + " - {}".format(os.path.join(tempdir, "*")) ) config = salt.config.minion_config(fpath) - base = config['file_roots']['base'] - self.assertEqual(set(base), - {os.path.join(tempdir, 'a'), os.path.join(tempdir, 'b'), os.path.join(tempdir, 'c')}) + base = config["file_roots"]["base"] + self.assertEqual( + set(base), + { + os.path.join(tempdir, "a"), + os.path.join(tempdir, "b"), + os.path.join(tempdir, "c"), + }, + ) @with_tempfile() @with_tempdir() def test_minion_pillar_roots_glob(self, tempdir, fpath): # Create some files. - for f in 'abc': + for f in "abc": fpath = os.path.join(tempdir, f) - with salt.utils.files.fopen(fpath, 'w') as wfh: + with salt.utils.files.fopen(fpath, "w") as wfh: wfh.write(f) - with salt.utils.files.fopen(fpath, 'w') as wfh: + with salt.utils.files.fopen(fpath, "w") as wfh: wfh.write( - 'pillar_roots:\n' - ' base:\n' - ' - {}'.format(os.path.join(tempdir, '*')) + "pillar_roots:\n" + " base:\n" + " - {}".format(os.path.join(tempdir, "*")) ) config = salt.config.minion_config(fpath) - base = config['pillar_roots']['base'] - self.assertEqual(set(base), - {os.path.join(tempdir, 'a'), os.path.join(tempdir, 'b'), os.path.join(tempdir, 'c')}) + base = config["pillar_roots"]["base"] + self.assertEqual( + set(base), + { + os.path.join(tempdir, "a"), + os.path.join(tempdir, "b"), + os.path.join(tempdir, "c"), + }, + ) @with_tempdir() def test_minion_id_function(self, tempdir): - minion_config = os.path.join(tempdir, 'minion') + minion_config = os.path.join(tempdir, "minion") - with salt.utils.files.fopen(minion_config, 'w') as fp_: + with salt.utils.files.fopen(minion_config, "w") as fp_: fp_.write( - 'id_function:\n' - ' test.echo:\n' - ' text: hello_world\n' - 'root_dir: {}\n' - 'log_file: {}\n'.format(tempdir, minion_config) + "id_function:\n" + " test.echo:\n" + " text: hello_world\n" + "root_dir: {}\n" + "log_file: {}\n".format(tempdir, minion_config) ) # Let's load the configuration config = salt.config.minion_config(minion_config) - self.assertEqual(config['log_file'], minion_config) - self.assertEqual(config['id'], 'hello_world') + self.assertEqual(config["log_file"], minion_config) + self.assertEqual(config["id"], "hello_world") @with_tempdir() def test_minion_id_lowercase(self, tempdir): - ''' + """ This tests that setting `minion_id_lowercase: True` does lower case the minion id. Lowercase does not operate on a static `id: KING_BOB` setting, or a cached id. - ''' - minion_config = os.path.join(tempdir, 'minion') - with salt.utils.files.fopen(minion_config, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + """ + minion_config = os.path.join(tempdir, "minion") + with salt.utils.files.fopen(minion_config, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ id_function: test.echo: text: KING_BOB minion_id_caching: False minion_id_lowercase: True - ''')) + """ + ) + ) config = salt.config.minion_config(minion_config) # Load the configuration - self.assertEqual(config['minion_id_caching'], False) # Check the configuration - self.assertEqual(config['minion_id_lowercase'], True) # Check the configuration - self.assertEqual(config['id'], 'king_bob') + self.assertEqual(config["minion_id_caching"], False) # Check the configuration + self.assertEqual(config["minion_id_lowercase"], True) # Check the configuration + self.assertEqual(config["id"], "king_bob") @with_tempdir() def test_minion_id_remove_domain_string_positive(self, tempdir): - ''' + """ This tests that the values of `minion_id_remove_domain` is suppressed from a generated minion id, effectivly generating a hostname minion_id. - ''' - minion_config = os.path.join(tempdir, 'minion') - with salt.utils.files.fopen(minion_config, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + """ + minion_config = os.path.join(tempdir, "minion") + with salt.utils.files.fopen(minion_config, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ id_function: test.echo: text: king_bob.foo.org minion_id_remove_domain: foo.org minion_id_caching: False - ''')) + """ + ) + ) # Let's load the configuration config = salt.config.minion_config(minion_config) - self.assertEqual(config['minion_id_remove_domain'], 'foo.org') - self.assertEqual(config['id'], 'king_bob') + self.assertEqual(config["minion_id_remove_domain"], "foo.org") + self.assertEqual(config["id"], "king_bob") @with_tempdir() def test_minion_id_remove_domain_string_negative(self, tempdir): - ''' + """ See above - ''' - minion_config = os.path.join(tempdir, 'minion') - with salt.utils.files.fopen(minion_config, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + """ + minion_config = os.path.join(tempdir, "minion") + with salt.utils.files.fopen(minion_config, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ id_function: test.echo: text: king_bob.foo.org minion_id_remove_domain: bar.org minion_id_caching: False - ''')) + """ + ) + ) config = salt.config.minion_config(minion_config) - self.assertEqual(config['id'], 'king_bob.foo.org') + self.assertEqual(config["id"], "king_bob.foo.org") @with_tempdir() def test_minion_id_remove_domain_bool_true(self, tempdir): - ''' + """ See above - ''' - minion_config = os.path.join(tempdir, 'minion') - with salt.utils.files.fopen(minion_config, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + """ + minion_config = os.path.join(tempdir, "minion") + with salt.utils.files.fopen(minion_config, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ id_function: test.echo: text: king_bob.foo.org minion_id_remove_domain: True minion_id_caching: False - ''')) + """ + ) + ) config = salt.config.minion_config(minion_config) - self.assertEqual(config['id'], 'king_bob') + self.assertEqual(config["id"], "king_bob") @with_tempdir() def test_minion_id_remove_domain_bool_false(self, tempdir): - ''' + """ See above - ''' - minion_config = os.path.join(tempdir, 'minion') - with salt.utils.files.fopen(minion_config, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + """ + minion_config = os.path.join(tempdir, "minion") + with salt.utils.files.fopen(minion_config, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ id_function: test.echo: text: king_bob.foo.org minion_id_remove_domain: False minion_id_caching: False - ''')) + """ + ) + ) config = salt.config.minion_config(minion_config) - self.assertEqual(config['id'], 'king_bob.foo.org') + self.assertEqual(config["id"], "king_bob.foo.org") @with_tempdir() def test_backend_rename(self, tempdir): - ''' + """ This tests that we successfully rename git, hg, svn, and minion to gitfs, hgfs, svnfs, and minionfs in the master and minion opts. - ''' + """ fpath = salt.utils.files.mkstemp(dir=tempdir) - with salt.utils.files.fopen(fpath, 'w') as fp_: - fp_.write(textwrap.dedent('''\ + with salt.utils.files.fopen(fpath, "w") as fp_: + fp_.write( + textwrap.dedent( + """\ fileserver_backend: - roots - git - hg - svn - minion - ''')) + """ + ) + ) master_config = salt.config.master_config(fpath) minion_config = salt.config.minion_config(fpath) - expected = ['roots', 'gitfs', 'hgfs', 'svnfs', 'minionfs'] + expected = ["roots", "gitfs", "hgfs", "svnfs", "minionfs"] - self.assertEqual(master_config['fileserver_backend'], expected) - self.assertEqual(minion_config['fileserver_backend'], expected) + self.assertEqual(master_config["fileserver_backend"], expected) + self.assertEqual(minion_config["fileserver_backend"], expected) def test_syndic_config(self): - minion_conf_path = self.get_config_file_path('syndic') - master_conf_path = os.path.join(os.path.dirname(minion_conf_path), 'master') + minion_conf_path = self.get_config_file_path("syndic") + master_conf_path = os.path.join(os.path.dirname(minion_conf_path), "master") syndic_opts = salt.config.syndic_config(master_conf_path, minion_conf_path) - root_dir = syndic_opts['root_dir'] + root_dir = syndic_opts["root_dir"] # id & pki dir are shared & so configured on the minion side - self.assertEqual(syndic_opts['id'], 'syndic') - self.assertEqual(syndic_opts['pki_dir'], os.path.join(root_dir, 'pki')) + self.assertEqual(syndic_opts["id"], "syndic") + self.assertEqual(syndic_opts["pki_dir"], os.path.join(root_dir, "pki")) # the rest is configured master side if RUNTIME_VARS.PYTEST_SESSION is False: # Pytest assigns ports dynamically - self.assertEqual(syndic_opts['master_port'], 54506) - self.assertEqual(syndic_opts['master'], 'localhost') - self.assertEqual(syndic_opts['sock_dir'], os.path.join(root_dir, 'syndic_sock')) - self.assertEqual(syndic_opts['cachedir'], os.path.join(root_dir, 'cache')) - self.assertEqual(syndic_opts['log_file'], os.path.join(root_dir, 'logs', 'syndic.log')) - self.assertEqual(syndic_opts['pidfile'], os.path.join(root_dir, 'run', 'syndic.pid')) + self.assertEqual(syndic_opts["master_port"], 54506) + self.assertEqual(syndic_opts["master"], "localhost") + self.assertEqual(syndic_opts["sock_dir"], os.path.join(root_dir, "syndic_sock")) + self.assertEqual(syndic_opts["cachedir"], os.path.join(root_dir, "cache")) + self.assertEqual( + syndic_opts["log_file"], os.path.join(root_dir, "logs", "syndic.log") + ) + self.assertEqual( + syndic_opts["pidfile"], os.path.join(root_dir, "run", "syndic.pid") + ) # Show that the options of localclient that repub to local master # are not merged with syndic ones - self.assertEqual(syndic_opts['_master_conf_file'], minion_conf_path) - self.assertEqual(syndic_opts['_minion_conf_file'], master_conf_path) + self.assertEqual(syndic_opts["_master_conf_file"], minion_conf_path) + self.assertEqual(syndic_opts["_minion_conf_file"], master_conf_path) @with_tempfile() def _get_tally(self, fpath, conf_func): - ''' + """ This ensures that any strings which are loaded are unicode strings - ''' + """ tally = {} def _count_strings(config): if isinstance(config, dict): for key, val in six.iteritems(config): - log.debug('counting strings in dict key: %s', key) - log.debug('counting strings in dict val: %s', val) + log.debug("counting strings in dict key: %s", key) + log.debug("counting strings in dict val: %s", val) _count_strings(key) _count_strings(val) elif isinstance(config, list): - log.debug('counting strings in list: %s', config) + log.debug("counting strings in list: %s", config) for item in config: _count_strings(item) else: if isinstance(config, six.string_types): if isinstance(config, six.text_type): - tally['unicode'] = tally.get('unicode', 0) + 1 + tally["unicode"] = tally.get("unicode", 0) + 1 else: # We will never reach this on PY3 - tally.setdefault('non_unicode', []).append(config) + tally.setdefault("non_unicode", []).append(config) - with salt.utils.files.fopen(fpath, 'w') as wfh: - wfh.write(textwrap.dedent(''' + with salt.utils.files.fopen(fpath, "w") as wfh: + wfh.write( + textwrap.dedent( + """ foo: bar mylist: - somestring @@ -840,91 +855,128 @@ class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin): - nestedlist: - foo - bar - - baz''')) + - baz""" + ) + ) if conf_func is salt.config.master_config: - wfh.write('\n\n') - wfh.write(textwrap.dedent(''' + wfh.write("\n\n") + wfh.write( + textwrap.dedent( + """ rest_cherrypy: port: 8000 disable_ssl: True app_path: /beacon_demo app: /srv/web/html/index.html - static: /srv/web/static''')) + static: /srv/web/static""" + ) + ) config = conf_func(fpath) _count_strings(config) return tally def test_conf_file_strings_are_unicode_for_master(self): - ''' + """ This ensures that any strings which are loaded are unicode strings - ''' - tally = self._get_tally(salt.config.master_config) # pylint: disable=no-value-for-parameter - non_unicode = tally.get('non_unicode', []) + """ + # pylint: disable=no-value-for-parameter + tally = self._get_tally(salt.config.master_config) + # pylint: enable=no-value-for-parameter + non_unicode = tally.get("non_unicode", []) self.assertEqual(len(non_unicode), 8 if six.PY2 else 0, non_unicode) - self.assertTrue(tally['unicode'] > 0) + self.assertTrue(tally["unicode"] > 0) def test_conf_file_strings_are_unicode_for_minion(self): - ''' + """ This ensures that any strings which are loaded are unicode strings - ''' - tally = self._get_tally(salt.config.minion_config) # pylint: disable=no-value-for-parameter - non_unicode = tally.get('non_unicode', []) + """ + # pylint: disable=no-value-for-parameter + tally = self._get_tally(salt.config.minion_config) + # pylint: enable=no-value-for-parameter + non_unicode = tally.get("non_unicode", []) self.assertEqual(len(non_unicode), 0, non_unicode) - self.assertTrue(tally['unicode'] > 0) + self.assertTrue(tally["unicode"] > 0) # <---- Salt Cloud Configuration Tests --------------------------------------------- # cloud_config tests def test_cloud_config_double_master_path(self): - ''' + """ Tests passing in master_config_path and master_config kwargs. - ''' - with patch('salt.config.load_config', MagicMock(return_value={})): - self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, - master_config_path='foo', master_config='bar') + """ + with patch("salt.config.load_config", MagicMock(return_value={})): + self.assertRaises( + SaltCloudConfigError, + salt.config.cloud_config, + PATH, + master_config_path="foo", + master_config="bar", + ) def test_cloud_config_double_providers_path(self): - ''' + """ Tests passing in providers_config_path and providers_config kwargs. - ''' - with patch('salt.config.load_config', MagicMock(return_value={})): - self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, - providers_config_path='foo', providers_config='bar') + """ + with patch("salt.config.load_config", MagicMock(return_value={})): + self.assertRaises( + SaltCloudConfigError, + salt.config.cloud_config, + PATH, + providers_config_path="foo", + providers_config="bar", + ) def test_cloud_config_double_profiles_path(self): - ''' + """ Tests passing in profiles_config_path and profiles_config kwargs. - ''' - with patch('salt.config.load_config', MagicMock(return_value={})): - self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, - profiles_config_path='foo', profiles_config='bar') + """ + with patch("salt.config.load_config", MagicMock(return_value={})): + self.assertRaises( + SaltCloudConfigError, + salt.config.cloud_config, + PATH, + profiles_config_path="foo", + profiles_config="bar", + ) def test_cloud_config_providers_in_opts(self): - ''' + """ Tests mixing old cloud providers with pre-configured providers configurations using the providers_config kwarg - ''' - with patch('salt.config.load_config', MagicMock(return_value={})): - with patch('salt.config.apply_cloud_config', - MagicMock(return_value={'providers': 'foo'})): - self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, - providers_config='bar') + """ + with patch("salt.config.load_config", MagicMock(return_value={})): + with patch( + "salt.config.apply_cloud_config", + MagicMock(return_value={"providers": "foo"}), + ): + self.assertRaises( + SaltCloudConfigError, + salt.config.cloud_config, + PATH, + providers_config="bar", + ) def test_cloud_config_providers_in_opts_path(self): - ''' + """ Tests mixing old cloud providers with pre-configured providers configurations using the providers_config_path kwarg - ''' - with patch('salt.config.load_config', MagicMock(return_value={})): - with patch('salt.config.apply_cloud_config', - MagicMock(return_value={'providers': 'foo'})): - with patch('os.path.isfile', MagicMock(return_value=True)): - self.assertRaises(SaltCloudConfigError, salt.config.cloud_config, PATH, - providers_config_path='bar') + """ + with patch("salt.config.load_config", MagicMock(return_value={})): + with patch( + "salt.config.apply_cloud_config", + MagicMock(return_value={"providers": "foo"}), + ): + with patch("os.path.isfile", MagicMock(return_value=True)): + self.assertRaises( + SaltCloudConfigError, + salt.config.cloud_config, + PATH, + providers_config_path="bar", + ) def test_cloud_config_deploy_scripts_search_path(self): - ''' + """ Tests the contents of the 'deploy_scripts_search_path' tuple to ensure that the correct deploy search paths are present. @@ -933,14 +985,16 @@ class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin): ``/etc/salt/cloud.deploy.d``, but sometimes is can be something like ``/etc/local/salt/cloud.deploy.d``, so we'll only test against the last part of the path. - ''' - with patch('os.path.isdir', MagicMock(return_value=True)): - search_paths = salt.config.cloud_config('/etc/salt/cloud').get('deploy_scripts_search_path') - etc_deploy_path = '/salt/cloud.deploy.d' - deploy_path = '/salt/cloud/deploy' + """ + with patch("os.path.isdir", MagicMock(return_value=True)): + search_paths = salt.config.cloud_config("/etc/salt/cloud").get( + "deploy_scripts_search_path" + ) + etc_deploy_path = "/salt/cloud.deploy.d" + deploy_path = "/salt/cloud/deploy" if salt.utils.platform.is_windows(): - etc_deploy_path = '/salt\\cloud.deploy.d' - deploy_path = '\\salt\\cloud\\deploy' + etc_deploy_path = "/salt\\cloud.deploy.d" + deploy_path = "\\salt\\cloud\\deploy" # Check cloud.deploy.d path is the first element in the search_paths tuple self.assertTrue(search_paths[0].endswith(etc_deploy_path)) @@ -951,783 +1005,827 @@ class ConfigTestCase(TestCase, AdaptedConfigurationTestCaseMixin): # apply_cloud_config tests def test_apply_cloud_config_no_provider_detail_list(self): - ''' + """ Tests when the provider is not contained in a list of details - ''' - overrides = {'providers': {'foo': [{'bar': 'baz'}]}} - self.assertRaises(SaltCloudConfigError, salt.config.apply_cloud_config, - overrides, defaults=DEFAULT) + """ + overrides = {"providers": {"foo": [{"bar": "baz"}]}} + self.assertRaises( + SaltCloudConfigError, + salt.config.apply_cloud_config, + overrides, + defaults=DEFAULT, + ) def test_apply_cloud_config_no_provider_detail_dict(self): - ''' + """ Tests when the provider is not contained in the details dictionary - ''' - overrides = {'providers': {'foo': {'bar': 'baz'}}} - self.assertRaises(SaltCloudConfigError, salt.config.apply_cloud_config, - overrides, defaults=DEFAULT) + """ + overrides = {"providers": {"foo": {"bar": "baz"}}} + self.assertRaises( + SaltCloudConfigError, + salt.config.apply_cloud_config, + overrides, + defaults=DEFAULT, + ) def test_apply_cloud_config_success_list(self): - ''' + """ Tests success when valid data is passed into the function as a list - ''' - with patch('salt.config.old_to_new', - MagicMock(return_value={ - 'default_include': 'path/to/some/cloud/conf/file', - 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}} - })): - overrides = { - 'providers': {'foo': [{'driver': 'bar'}]} - } + """ + with patch( + "salt.config.old_to_new", + MagicMock( + return_value={ + "default_include": "path/to/some/cloud/conf/file", + "providers": {"foo": {"bar": {"driver": "foo:bar"}}}, + } + ), + ): + overrides = {"providers": {"foo": [{"driver": "bar"}]}} ret = { - 'default_include': 'path/to/some/cloud/conf/file', - 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}} + "default_include": "path/to/some/cloud/conf/file", + "providers": {"foo": {"bar": {"driver": "foo:bar"}}}, } - self.assertEqual(salt.config.apply_cloud_config(overrides, defaults=DEFAULT), ret) + self.assertEqual( + salt.config.apply_cloud_config(overrides, defaults=DEFAULT), ret + ) def test_apply_cloud_config_success_dict(self): - ''' + """ Tests success when valid data is passed into function as a dictionary - ''' - with patch('salt.config.old_to_new', - MagicMock(return_value={ - 'default_include': 'path/to/some/cloud/conf/file', - 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}} - })): - overrides = { - 'providers': {'foo': {'driver': 'bar'}}} + """ + with patch( + "salt.config.old_to_new", + MagicMock( + return_value={ + "default_include": "path/to/some/cloud/conf/file", + "providers": {"foo": {"bar": {"driver": "foo:bar"}}}, + } + ), + ): + overrides = {"providers": {"foo": {"driver": "bar"}}} ret = { - 'default_include': 'path/to/some/cloud/conf/file', - 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}} + "default_include": "path/to/some/cloud/conf/file", + "providers": {"foo": {"bar": {"driver": "foo:bar"}}}, } - self.assertEqual(salt.config.apply_cloud_config(overrides, defaults=DEFAULT), ret) + self.assertEqual( + salt.config.apply_cloud_config(overrides, defaults=DEFAULT), ret + ) # apply_vm_profiles_config tests def test_apply_vm_profiles_config_bad_profile_format(self): - ''' + """ Tests passing in a bad profile format in overrides - ''' - overrides = {'foo': 'bar', 'conf_file': PATH} - self.assertRaises(SaltCloudConfigError, salt.config.apply_vm_profiles_config, - PATH, overrides, defaults=DEFAULT) + """ + overrides = {"foo": "bar", "conf_file": PATH} + self.assertRaises( + SaltCloudConfigError, + salt.config.apply_vm_profiles_config, + PATH, + overrides, + defaults=DEFAULT, + ) def test_apply_vm_profiles_config_success(self): - ''' + """ Tests passing in valid provider and profile config files successfully - ''' + """ providers = { - 'test-provider': { - 'digitalocean': { - 'driver': 'digitalocean', 'profiles': {} - } + "test-provider": { + "digitalocean": {"driver": "digitalocean", "profiles": {}} } } overrides = { - 'test-profile': { - 'provider': 'test-provider', - 'image': 'Ubuntu 12.10 x64', - 'size': '512MB' + "test-profile": { + "provider": "test-provider", + "image": "Ubuntu 12.10 x64", + "size": "512MB", }, - 'conf_file': PATH + "conf_file": PATH, } ret = { - 'test-profile': { - 'profile': 'test-profile', - 'provider': 'test-provider:digitalocean', - 'image': 'Ubuntu 12.10 x64', - 'size': '512MB' + "test-profile": { + "profile": "test-profile", + "provider": "test-provider:digitalocean", + "image": "Ubuntu 12.10 x64", + "size": "512MB", } } - self.assertEqual(salt.config.apply_vm_profiles_config(providers, - overrides, - defaults=DEFAULT), ret) + self.assertEqual( + salt.config.apply_vm_profiles_config( + providers, overrides, defaults=DEFAULT + ), + ret, + ) def test_apply_vm_profiles_config_extend_success(self): - ''' + """ Tests profile extends functionality with valid provider and profile configs - ''' - providers = { - 'test-config': { - 'ec2': { - 'profiles': {}, - 'driver': 'ec2' - } - } - } + """ + providers = {"test-config": {"ec2": {"profiles": {}, "driver": "ec2"}}} overrides = { - 'Amazon': { - 'image': 'test-image-1', - 'extends': 'dev-instances' - }, - 'Fedora': { - 'image': 'test-image-2', - 'extends': 'dev-instances' - }, - 'conf_file': PATH, - 'dev-instances': { - 'ssh_username': 'test_user', - 'provider': 'test-config' - } + "Amazon": {"image": "test-image-1", "extends": "dev-instances"}, + "Fedora": {"image": "test-image-2", "extends": "dev-instances"}, + "conf_file": PATH, + "dev-instances": {"ssh_username": "test_user", "provider": "test-config"}, } ret = { - 'Amazon': { - 'profile': 'Amazon', - 'ssh_username': 'test_user', - 'image': 'test-image-1', - 'provider': 'test-config:ec2' + "Amazon": { + "profile": "Amazon", + "ssh_username": "test_user", + "image": "test-image-1", + "provider": "test-config:ec2", }, - 'Fedora': { - 'profile': 'Fedora', - 'ssh_username': 'test_user', - 'image': 'test-image-2', - 'provider': 'test-config:ec2' + "Fedora": { + "profile": "Fedora", + "ssh_username": "test_user", + "image": "test-image-2", + "provider": "test-config:ec2", + }, + "dev-instances": { + "profile": "dev-instances", + "ssh_username": "test_user", + "provider": "test-config:ec2", }, - 'dev-instances': { - 'profile': 'dev-instances', - 'ssh_username': 'test_user', - 'provider': 'test-config:ec2' - } } - self.assertEqual(salt.config.apply_vm_profiles_config(providers, - overrides, - defaults=DEFAULT), ret) + self.assertEqual( + salt.config.apply_vm_profiles_config( + providers, overrides, defaults=DEFAULT + ), + ret, + ) def test_apply_vm_profiles_config_extend_override_success(self): - ''' + """ Tests profile extends and recursively merges data elements - ''' + """ self.maxDiff = None - providers = { - 'test-config': { - 'ec2': { - 'profiles': {}, - 'driver': 'ec2' - } - } - } + providers = {"test-config": {"ec2": {"profiles": {}, "driver": "ec2"}}} overrides = { - 'Fedora': { - 'image': 'test-image-2', - 'extends': 'dev-instances', - 'minion': { - 'grains': { - 'stage': 'experimental' - } - } + "Fedora": { + "image": "test-image-2", + "extends": "dev-instances", + "minion": {"grains": {"stage": "experimental"}}, + }, + "conf_file": PATH, + "dev-instances": { + "ssh_username": "test_user", + "provider": "test-config", + "minion": {"grains": {"role": "webserver"}}, }, - 'conf_file': PATH, - 'dev-instances': { - 'ssh_username': 'test_user', - 'provider': 'test-config', - 'minion': { - 'grains': { - 'role': 'webserver'} - } - } } ret = { - 'Fedora': { - 'profile': 'Fedora', - 'ssh_username': 'test_user', - 'image': 'test-image-2', - 'minion': { - 'grains': { - 'role': 'webserver', - 'stage': 'experimental' - } - }, - 'provider': 'test-config:ec2' + "Fedora": { + "profile": "Fedora", + "ssh_username": "test_user", + "image": "test-image-2", + "minion": {"grains": {"role": "webserver", "stage": "experimental"}}, + "provider": "test-config:ec2", + }, + "dev-instances": { + "profile": "dev-instances", + "ssh_username": "test_user", + "minion": {"grains": {"role": "webserver"}}, + "provider": "test-config:ec2", }, - 'dev-instances': { - 'profile': 'dev-instances', - 'ssh_username': 'test_user', - 'minion': { - 'grains': { - 'role': 'webserver' - } - }, - 'provider': 'test-config:ec2' - } } - self.assertEqual(salt.config.apply_vm_profiles_config(providers, - overrides, - defaults=DEFAULT), ret) + self.assertEqual( + salt.config.apply_vm_profiles_config( + providers, overrides, defaults=DEFAULT + ), + ret, + ) # apply_cloud_providers_config tests def test_apply_cloud_providers_config_same_providers(self): - ''' + """ Tests when two providers are given with the same provider name - ''' + """ overrides = { - 'my-dev-envs': [{ - 'id': 'ABCDEFGHIJKLMNOP', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2' - }, { - 'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'driver': 'ec2' - }], - 'conf_file': PATH} - self.assertRaises(SaltCloudConfigError, - salt.config.apply_cloud_providers_config, - overrides, - DEFAULT) + "my-dev-envs": [ + { + "id": "ABCDEFGHIJKLMNOP", + "key": "supersecretkeysupersecretkey", + "driver": "ec2", + }, + { + "apikey": "abcdefghijklmnopqrstuvwxyz", + "password": "supersecret", + "driver": "ec2", + }, + ], + "conf_file": PATH, + } + self.assertRaises( + SaltCloudConfigError, + salt.config.apply_cloud_providers_config, + overrides, + DEFAULT, + ) def test_apply_cloud_providers_config_extend(self): - ''' + """ Tests the successful extension of a cloud provider - ''' + """ overrides = { - 'my-production-envs': [{ - 'extends': 'my-dev-envs:ec2', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com' - }], - 'my-dev-envs': [{ - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2' - }, { - 'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'driver': 'linode' - }, { - 'id': 'a-tencentcloud-id', - 'key': 'a-tencentcloud-key', - 'location': 'ap-guangzhou', - 'driver': 'tencentcloud', - }, + "my-production-envs": [ + { + "extends": "my-dev-envs:ec2", + "location": "us-east-1", + "user": "ec2-user@mycorp.com", + } ], - 'conf_file': PATH + "my-dev-envs": [ + { + "id": "ABCDEFGHIJKLMNOP", + "user": "user@mycorp.com", + "location": "ap-southeast-1", + "key": "supersecretkeysupersecretkey", + "driver": "ec2", + }, + { + "apikey": "abcdefghijklmnopqrstuvwxyz", + "password": "supersecret", + "driver": "linode", + }, + { + "id": "a-tencentcloud-id", + "key": "a-tencentcloud-key", + "location": "ap-guangzhou", + "driver": "tencentcloud", + }, + ], + "conf_file": PATH, } ret = { - 'my-production-envs': { - 'ec2': { - 'profiles': {}, - 'location': 'us-east-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2', - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'ec2-user@mycorp.com' + "my-production-envs": { + "ec2": { + "profiles": {}, + "location": "us-east-1", + "key": "supersecretkeysupersecretkey", + "driver": "ec2", + "id": "ABCDEFGHIJKLMNOP", + "user": "ec2-user@mycorp.com", } }, - 'my-dev-envs': { - 'linode': { - 'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'profiles': {}, - 'driver': 'linode' + "my-dev-envs": { + "linode": { + "apikey": "abcdefghijklmnopqrstuvwxyz", + "password": "supersecret", + "profiles": {}, + "driver": "linode", }, - 'tencentcloud': { - 'id': 'a-tencentcloud-id', - 'key': 'a-tencentcloud-key', - 'location': 'ap-guangzhou', - 'profiles': {}, - 'driver': 'tencentcloud', + "tencentcloud": { + "id": "a-tencentcloud-id", + "key": "a-tencentcloud-key", + "location": "ap-guangzhou", + "profiles": {}, + "driver": "tencentcloud", }, - 'ec2': { - 'profiles': {}, - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2', - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', + "ec2": { + "profiles": {}, + "location": "ap-southeast-1", + "key": "supersecretkeysupersecretkey", + "driver": "ec2", + "id": "ABCDEFGHIJKLMNOP", + "user": "user@mycorp.com", }, }, } - self.assertEqual(ret, - salt.config.apply_cloud_providers_config( - overrides, - defaults=DEFAULT)) + self.assertEqual( + ret, salt.config.apply_cloud_providers_config(overrides, defaults=DEFAULT) + ) def test_apply_cloud_providers_config_extend_multiple(self): - ''' + """ Tests the successful extension of two cloud providers - ''' + """ overrides = { - 'my-production-envs': [{ - 'extends': 'my-dev-envs:ec2', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com' - }, { - 'password': 'new-password', - 'extends': 'my-dev-envs:linode', - 'location': 'Salt Lake City' - }, { - 'extends': 'my-dev-envs:tencentcloud', - 'id': 'new-id', - 'key': 'new-key', - 'location': 'ap-beijing', + "my-production-envs": [ + { + "extends": "my-dev-envs:ec2", + "location": "us-east-1", + "user": "ec2-user@mycorp.com", + }, + { + "password": "new-password", + "extends": "my-dev-envs:linode", + "location": "Salt Lake City", + }, + { + "extends": "my-dev-envs:tencentcloud", + "id": "new-id", + "key": "new-key", + "location": "ap-beijing", }, ], - 'my-dev-envs': [{ - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2' - }, { - 'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'driver': 'linode' - }, { - 'id': 'the-tencentcloud-id', - 'location': 'ap-beijing', - 'key': 'the-tencentcloud-key', - 'driver': 'tencentcloud', + "my-dev-envs": [ + { + "id": "ABCDEFGHIJKLMNOP", + "user": "user@mycorp.com", + "location": "ap-southeast-1", + "key": "supersecretkeysupersecretkey", + "driver": "ec2", + }, + { + "apikey": "abcdefghijklmnopqrstuvwxyz", + "password": "supersecret", + "driver": "linode", + }, + { + "id": "the-tencentcloud-id", + "location": "ap-beijing", + "key": "the-tencentcloud-key", + "driver": "tencentcloud", }, ], - 'conf_file': PATH} + "conf_file": PATH, + } ret = { - 'my-production-envs': { - 'linode': { - 'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'profiles': {}, - 'location': 'Salt Lake City', - 'driver': 'linode', - 'password': 'new-password' + "my-production-envs": { + "linode": { + "apikey": "abcdefghijklmnopqrstuvwxyz", + "profiles": {}, + "location": "Salt Lake City", + "driver": "linode", + "password": "new-password", }, - 'ec2': { - 'user': 'ec2-user@mycorp.com', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2', - 'id': 'ABCDEFGHIJKLMNOP', - 'profiles': {}, - 'location': 'us-east-1' + "ec2": { + "user": "ec2-user@mycorp.com", + "key": "supersecretkeysupersecretkey", + "driver": "ec2", + "id": "ABCDEFGHIJKLMNOP", + "profiles": {}, + "location": "us-east-1", }, - 'tencentcloud': { - 'id': 'new-id', - 'key': 'new-key', - 'location': 'ap-beijing', - 'profiles': {}, - 'driver': 'tencentcloud', + "tencentcloud": { + "id": "new-id", + "key": "new-key", + "location": "ap-beijing", + "profiles": {}, + "driver": "tencentcloud", }, }, - 'my-dev-envs': { - 'linode': { - 'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'profiles': {}, - 'driver': 'linode' + "my-dev-envs": { + "linode": { + "apikey": "abcdefghijklmnopqrstuvwxyz", + "password": "supersecret", + "profiles": {}, + "driver": "linode", }, - 'ec2': { - 'profiles': {}, - 'user': 'user@mycorp.com', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2', - 'id': 'ABCDEFGHIJKLMNOP', - 'location': 'ap-southeast-1' + "ec2": { + "profiles": {}, + "user": "user@mycorp.com", + "key": "supersecretkeysupersecretkey", + "driver": "ec2", + "id": "ABCDEFGHIJKLMNOP", + "location": "ap-southeast-1", }, - 'tencentcloud': { - 'id': 'the-tencentcloud-id', - 'key': 'the-tencentcloud-key', - 'location': 'ap-beijing', - 'profiles': {}, - 'driver': 'tencentcloud', + "tencentcloud": { + "id": "the-tencentcloud-id", + "key": "the-tencentcloud-key", + "location": "ap-beijing", + "profiles": {}, + "driver": "tencentcloud", }, }, } - self.assertEqual(ret, salt.config.apply_cloud_providers_config(overrides, defaults=DEFAULT)) + self.assertEqual( + ret, salt.config.apply_cloud_providers_config(overrides, defaults=DEFAULT) + ) def test_apply_cloud_providers_config_extends_bad_alias(self): - ''' + """ Tests when the extension contains an alias not found in providers list - ''' + """ overrides = { - 'my-production-envs': [{ - 'extends': 'test-alias:ec2', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com' - }], - 'my-dev-envs': [{ - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2' - }], - 'conf_file': PATH} - self.assertRaises(SaltCloudConfigError, - salt.config.apply_cloud_providers_config, - overrides, - DEFAULT) + "my-production-envs": [ + { + "extends": "test-alias:ec2", + "location": "us-east-1", + "user": "ec2-user@mycorp.com", + } + ], + "my-dev-envs": [ + { + "id": "ABCDEFGHIJKLMNOP", + "user": "user@mycorp.com", + "location": "ap-southeast-1", + "key": "supersecretkeysupersecretkey", + "driver": "ec2", + } + ], + "conf_file": PATH, + } + self.assertRaises( + SaltCloudConfigError, + salt.config.apply_cloud_providers_config, + overrides, + DEFAULT, + ) def test_apply_cloud_providers_config_extends_bad_provider(self): - ''' + """ Tests when the extension contains a provider not found in providers list - ''' + """ overrides = { - 'my-production-envs': [{ - 'extends': 'my-dev-envs:linode', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com' - }, { - 'extends': 'my-dev-envs:tencentcloud', - 'location': 'ap-shanghai', - 'id': 'the-tencentcloud-id', + "my-production-envs": [ + { + "extends": "my-dev-envs:linode", + "location": "us-east-1", + "user": "ec2-user@mycorp.com", + }, + { + "extends": "my-dev-envs:tencentcloud", + "location": "ap-shanghai", + "id": "the-tencentcloud-id", }, ], - 'my-dev-envs': [{ - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2' - }], - 'conf_file': PATH} - self.assertRaises(SaltCloudConfigError, - salt.config.apply_cloud_providers_config, - overrides, - DEFAULT) + "my-dev-envs": [ + { + "id": "ABCDEFGHIJKLMNOP", + "user": "user@mycorp.com", + "location": "ap-southeast-1", + "key": "supersecretkeysupersecretkey", + "driver": "ec2", + } + ], + "conf_file": PATH, + } + self.assertRaises( + SaltCloudConfigError, + salt.config.apply_cloud_providers_config, + overrides, + DEFAULT, + ) def test_apply_cloud_providers_config_extends_no_provider(self): - ''' + """ Tests when no provider is supplied in the extends statement - ''' + """ overrides = { - 'my-production-envs': [{ - 'extends': 'my-dev-envs', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com' - }, { - 'extends': 'my-dev-envs:tencentcloud', - 'location': 'ap-shanghai', - 'id': 'the-tencentcloud-id', + "my-production-envs": [ + { + "extends": "my-dev-envs", + "location": "us-east-1", + "user": "ec2-user@mycorp.com", + }, + { + "extends": "my-dev-envs:tencentcloud", + "location": "ap-shanghai", + "id": "the-tencentcloud-id", }, ], - 'my-dev-envs': [{ - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'linode' - }], - 'conf_file': PATH} - self.assertRaises(SaltCloudConfigError, - salt.config.apply_cloud_providers_config, - overrides, - DEFAULT) + "my-dev-envs": [ + { + "id": "ABCDEFGHIJKLMNOP", + "user": "user@mycorp.com", + "location": "ap-southeast-1", + "key": "supersecretkeysupersecretkey", + "driver": "linode", + } + ], + "conf_file": PATH, + } + self.assertRaises( + SaltCloudConfigError, + salt.config.apply_cloud_providers_config, + overrides, + DEFAULT, + ) def test_apply_cloud_providers_extends_not_in_providers(self): - ''' + """ Tests when extends is not in the list of providers - ''' + """ overrides = { - 'my-production-envs': [{ - 'extends': 'my-dev-envs ec2', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com' - }], - 'my-dev-envs': [{ - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'linode' - }, { - 'id': 'a-tencentcloud-id', - 'key': 'a-tencentcloud-key', - 'location': 'ap-guangzhou', - 'driver': 'tencentcloud', + "my-production-envs": [ + { + "extends": "my-dev-envs ec2", + "location": "us-east-1", + "user": "ec2-user@mycorp.com", + } + ], + "my-dev-envs": [ + { + "id": "ABCDEFGHIJKLMNOP", + "user": "user@mycorp.com", + "location": "ap-southeast-1", + "key": "supersecretkeysupersecretkey", + "driver": "linode", + }, + { + "id": "a-tencentcloud-id", + "key": "a-tencentcloud-key", + "location": "ap-guangzhou", + "driver": "tencentcloud", }, ], - 'conf_file': PATH} - self.assertRaises(SaltCloudConfigError, - salt.config.apply_cloud_providers_config, - overrides, - DEFAULT) + "conf_file": PATH, + } + self.assertRaises( + SaltCloudConfigError, + salt.config.apply_cloud_providers_config, + overrides, + DEFAULT, + ) # is_provider_configured tests def test_is_provider_configured_no_alias(self): - ''' + """ Tests when provider alias is not in opts - ''' - opts = {'providers': 'test'} - provider = 'foo:bar' + """ + opts = {"providers": "test"} + provider = "foo:bar" self.assertFalse(salt.config.is_provider_configured(opts, provider)) def test_is_provider_configured_no_driver(self): - ''' + """ Tests when provider driver is not in opts - ''' - opts = {'providers': {'foo': 'baz'}} - provider = 'foo:bar' + """ + opts = {"providers": {"foo": "baz"}} + provider = "foo:bar" self.assertFalse(salt.config.is_provider_configured(opts, provider)) def test_is_provider_configured_key_is_none(self): - ''' + """ Tests when a required configuration key is not set - ''' - opts = {'providers': {'foo': {'bar': {'api_key': None}}}} - provider = 'foo:bar' + """ + opts = {"providers": {"foo": {"bar": {"api_key": None}}}} + provider = "foo:bar" self.assertFalse( - salt.config.is_provider_configured(opts, - provider, - required_keys=('api_key',))) + salt.config.is_provider_configured( + opts, provider, required_keys=("api_key",) + ) + ) def test_is_provider_configured_success(self): - ''' + """ Tests successful cloud provider configuration - ''' - opts = {'providers': {'foo': {'bar': {'api_key': 'baz'}}}} - provider = 'foo:bar' - ret = {'api_key': 'baz'} + """ + opts = {"providers": {"foo": {"bar": {"api_key": "baz"}}}} + provider = "foo:bar" + ret = {"api_key": "baz"} self.assertEqual( - salt.config.is_provider_configured(opts, - provider, - required_keys=('api_key',)), ret) + salt.config.is_provider_configured( + opts, provider, required_keys=("api_key",) + ), + ret, + ) def test_is_provider_configured_multiple_driver_not_provider(self): - ''' + """ Tests when the drive is not the same as the provider when searching through multiple providers - ''' - opts = {'providers': {'foo': {'bar': {'api_key': 'baz'}}}} - provider = 'foo' + """ + opts = {"providers": {"foo": {"bar": {"api_key": "baz"}}}} + provider = "foo" self.assertFalse(salt.config.is_provider_configured(opts, provider)) def test_is_provider_configured_multiple_key_is_none(self): - ''' + """ Tests when a required configuration key is not set when searching through multiple providers - ''' - opts = {'providers': {'foo': {'bar': {'api_key': None}}}} - provider = 'bar' + """ + opts = {"providers": {"foo": {"bar": {"api_key": None}}}} + provider = "bar" self.assertFalse( - salt.config.is_provider_configured(opts, - provider, - required_keys=('api_key',))) + salt.config.is_provider_configured( + opts, provider, required_keys=("api_key",) + ) + ) def test_is_provider_configured_multiple_success(self): - ''' + """ Tests successful cloud provider configuration when searching through multiple providers - ''' - opts = {'providers': {'foo': {'bar': {'api_key': 'baz'}}}} - provider = 'bar' - ret = {'api_key': 'baz'} + """ + opts = {"providers": {"foo": {"bar": {"api_key": "baz"}}}} + provider = "bar" + ret = {"api_key": "baz"} self.assertEqual( - salt.config.is_provider_configured(opts, - provider, - required_keys=('api_key',)), ret) + salt.config.is_provider_configured( + opts, provider, required_keys=("api_key",) + ), + ret, + ) # other cloud configuration tests @skipIf( salt.utils.platform.is_windows(), - 'You can\'t set an environment dynamically in Windows') + "You can't set an environment dynamically in Windows", + ) @with_tempdir() def test_load_cloud_config_from_environ_var(self, tempdir): - env_root_dir = os.path.join(tempdir, 'foo', 'env') + env_root_dir = os.path.join(tempdir, "foo", "env") os.makedirs(env_root_dir) - env_fpath = os.path.join(env_root_dir, 'config-env') + env_fpath = os.path.join(env_root_dir, "config-env") - with salt.utils.files.fopen(env_fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(env_root_dir, env_fpath) - ) + with salt.utils.files.fopen(env_fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(env_root_dir, env_fpath)) with patched_environ(SALT_CLOUD_CONFIG=env_fpath): # Should load from env variable, not the default configuration file - config = salt.config.cloud_config('/etc/salt/cloud') - self.assertEqual(config['log_file'], env_fpath) + config = salt.config.cloud_config("/etc/salt/cloud") + self.assertEqual(config["log_file"], env_fpath) - root_dir = os.path.join(tempdir, 'foo', 'bar') + root_dir = os.path.join(tempdir, "foo", "bar") os.makedirs(root_dir) - fpath = os.path.join(root_dir, 'config') - with salt.utils.files.fopen(fpath, 'w') as fp_: - fp_.write( - 'root_dir: {}\n' - 'log_file: {}\n'.format(root_dir, fpath) - ) + fpath = os.path.join(root_dir, "config") + with salt.utils.files.fopen(fpath, "w") as fp_: + fp_.write("root_dir: {}\n" "log_file: {}\n".format(root_dir, fpath)) # Let's set the environment variable, yet, since the configuration # file path is not the default one, i.e., the user has passed an # alternative configuration file form the CLI parser, the # environment variable will be ignored. with patched_environ(SALT_CLOUD_CONFIG=env_fpath): config = salt.config.cloud_config(fpath) - self.assertEqual(config['log_file'], fpath) + self.assertEqual(config["log_file"], fpath) @with_tempdir() def test_deploy_search_path_as_string(self, temp_conf_dir): - config_file_path = os.path.join(temp_conf_dir, 'cloud') - deploy_dir_path = os.path.join(temp_conf_dir, 'test-deploy.d') + config_file_path = os.path.join(temp_conf_dir, "cloud") + deploy_dir_path = os.path.join(temp_conf_dir, "test-deploy.d") for directory in (temp_conf_dir, deploy_dir_path): if not os.path.isdir(directory): os.makedirs(directory) default_config = salt.config.cloud_config(config_file_path) - default_config['deploy_scripts_search_path'] = deploy_dir_path - with salt.utils.files.fopen(config_file_path, 'w') as cfd: + default_config["deploy_scripts_search_path"] = deploy_dir_path + with salt.utils.files.fopen(config_file_path, "w") as cfd: salt.utils.yaml.safe_dump(default_config, cfd, default_flow_style=False) default_config = salt.config.cloud_config(config_file_path) # Our custom deploy scripts path was correctly added to the list - self.assertIn( - deploy_dir_path, - default_config['deploy_scripts_search_path'] - ) + self.assertIn(deploy_dir_path, default_config["deploy_scripts_search_path"]) # And it's even the first occurrence as it should self.assertEqual( - deploy_dir_path, - default_config['deploy_scripts_search_path'][0] + deploy_dir_path, default_config["deploy_scripts_search_path"][0] ) def test_includes_load(self): - ''' + """ Tests that cloud.{providers,profiles}.d directories are loaded, even if not directly passed in through path - ''' - log.warning('Clound config file path: %s', self.get_config_file_path('cloud')) - config = salt.config.cloud_config(self.get_config_file_path('cloud')) - self.assertIn('ec2-config', config['providers']) - self.assertIn('ec2-test', config['profiles']) + """ + log.warning("Clound config file path: %s", self.get_config_file_path("cloud")) + config = salt.config.cloud_config(self.get_config_file_path("cloud")) + self.assertIn("ec2-config", config["providers"]) + self.assertIn("ec2-test", config["profiles"]) # <---- Salt Cloud Configuration Tests --------------------------------------------- def test_include_config_without_errors(self): - ''' + """ Tests that include_config function returns valid configuration - ''' - include_file = 'minion.d/my.conf' - config_path = '/etc/salt/minion' - config_opts = {'id': 'myminion.example.com'} + """ + include_file = "minion.d/my.conf" + config_path = "/etc/salt/minion" + config_opts = {"id": "myminion.example.com"} - with patch('glob.glob', MagicMock(return_value=include_file)): - with patch('salt.config._read_conf_file', MagicMock(return_value=config_opts)): - configuration = salt.config.include_config(include_file, config_path, verbose=False) + with patch("glob.glob", MagicMock(return_value=include_file)): + with patch( + "salt.config._read_conf_file", MagicMock(return_value=config_opts) + ): + configuration = salt.config.include_config( + include_file, config_path, verbose=False + ) self.assertEqual(config_opts, configuration) def test_include_config_with_errors(self): - ''' + """ Tests that include_config function returns valid configuration even on errors - ''' - include_file = 'minion.d/my.conf' - config_path = '/etc/salt/minion' + """ + include_file = "minion.d/my.conf" + config_path = "/etc/salt/minion" config_opts = {} - with patch('glob.glob', MagicMock(return_value=include_file)): - with patch('salt.config._read_conf_file', _salt_configuration_error): - configuration = salt.config.include_config(include_file, config_path, verbose=False) + with patch("glob.glob", MagicMock(return_value=include_file)): + with patch("salt.config._read_conf_file", _salt_configuration_error): + configuration = salt.config.include_config( + include_file, config_path, verbose=False + ) self.assertEqual(config_opts, configuration) def test_include_config_with_errors_exit(self): - ''' + """ Tests that include_config exits on errors - ''' - include_file = 'minion.d/my.conf' - config_path = '/etc/salt/minion' + """ + include_file = "minion.d/my.conf" + config_path = "/etc/salt/minion" - with patch('glob.glob', MagicMock(return_value=include_file)): - with patch('salt.config._read_conf_file', _salt_configuration_error): + with patch("glob.glob", MagicMock(return_value=include_file)): + with patch("salt.config._read_conf_file", _salt_configuration_error): with self.assertRaises(SystemExit): - salt.config.include_config(include_file, - config_path, - verbose=False, - exit_on_config_errors=True) + salt.config.include_config( + include_file, + config_path, + verbose=False, + exit_on_config_errors=True, + ) @staticmethod def _get_defaults(**kwargs): ret = { - 'saltenv': kwargs.pop('saltenv', None), - 'id': 'test', - 'cachedir': '/A', - 'sock_dir': '/B', - 'root_dir': '/C', - 'fileserver_backend': 'roots', - 'open_mode': False, - 'auto_accept': False, - 'file_roots': {}, - 'pillar_roots': {}, - 'file_ignore_glob': [], - 'file_ignore_regex': [], - 'worker_threads': 5, - 'hash_type': 'sha256', - 'log_file': 'foo.log', + "saltenv": kwargs.pop("saltenv", None), + "id": "test", + "cachedir": "/A", + "sock_dir": "/B", + "root_dir": "/C", + "fileserver_backend": "roots", + "open_mode": False, + "auto_accept": False, + "file_roots": {}, + "pillar_roots": {}, + "file_ignore_glob": [], + "file_ignore_regex": [], + "worker_threads": 5, + "hash_type": "sha256", + "log_file": "foo.log", } ret.update(kwargs) return ret def test_apply_config(self): - ''' + """ Ensure that the environment and saltenv options work properly - ''' - with patch.object(salt.config, '_adjust_log_file_override', Mock()), \ - patch.object(salt.config, '_update_ssl_config', Mock()), \ - patch.object(salt.config, '_update_discovery_config', Mock()): + """ + with patch.object( + salt.config, "_adjust_log_file_override", Mock() + ), patch.object(salt.config, "_update_ssl_config", Mock()), patch.object( + salt.config, "_update_discovery_config", Mock() + ): # MASTER CONFIG # Ensure that environment overrides saltenv when saltenv not # explicitly passed. - defaults = self._get_defaults(environment='foo') + defaults = self._get_defaults(environment="foo") ret = salt.config.apply_master_config(defaults=defaults) - self.assertEqual(ret['environment'], 'foo') - self.assertEqual(ret['saltenv'], 'foo') + self.assertEqual(ret["environment"], "foo") + self.assertEqual(ret["saltenv"], "foo") # Ensure that environment overrides saltenv when saltenv not # explicitly passed. - defaults = self._get_defaults(environment='foo', saltenv='bar') + defaults = self._get_defaults(environment="foo", saltenv="bar") ret = salt.config.apply_master_config(defaults=defaults) - self.assertEqual(ret['environment'], 'bar') - self.assertEqual(ret['saltenv'], 'bar') + self.assertEqual(ret["environment"], "bar") + self.assertEqual(ret["saltenv"], "bar") # If environment was not explicitly set, it should not be in the # opts at all. defaults = self._get_defaults() ret = salt.config.apply_master_config(defaults=defaults) - self.assertNotIn('environment', ret) - self.assertEqual(ret['saltenv'], None) + self.assertNotIn("environment", ret) + self.assertEqual(ret["saltenv"], None) # Same test as above but with saltenv explicitly set - defaults = self._get_defaults(saltenv='foo') + defaults = self._get_defaults(saltenv="foo") ret = salt.config.apply_master_config(defaults=defaults) - self.assertNotIn('environment', ret) - self.assertEqual(ret['saltenv'], 'foo') + self.assertNotIn("environment", ret) + self.assertEqual(ret["saltenv"], "foo") # MINION CONFIG # Ensure that environment overrides saltenv when saltenv not # explicitly passed. - defaults = self._get_defaults(environment='foo') + defaults = self._get_defaults(environment="foo") ret = salt.config.apply_minion_config(defaults=defaults) - self.assertEqual(ret['environment'], 'foo') - self.assertEqual(ret['saltenv'], 'foo') + self.assertEqual(ret["environment"], "foo") + self.assertEqual(ret["saltenv"], "foo") # Ensure that environment overrides saltenv when saltenv not # explicitly passed. - defaults = self._get_defaults(environment='foo', saltenv='bar') + defaults = self._get_defaults(environment="foo", saltenv="bar") ret = salt.config.apply_minion_config(defaults=defaults) - self.assertEqual(ret['environment'], 'bar') - self.assertEqual(ret['saltenv'], 'bar') + self.assertEqual(ret["environment"], "bar") + self.assertEqual(ret["saltenv"], "bar") # If environment was not explicitly set, it should not be in the # opts at all. defaults = self._get_defaults() ret = salt.config.apply_minion_config(defaults=defaults) - self.assertNotIn('environment', ret) - self.assertEqual(ret['saltenv'], None) + self.assertNotIn("environment", ret) + self.assertEqual(ret["saltenv"], None) # Same test as above but with saltenv explicitly set - defaults = self._get_defaults(saltenv='foo') + defaults = self._get_defaults(saltenv="foo") ret = salt.config.apply_minion_config(defaults=defaults) - self.assertNotIn('environment', ret) - self.assertEqual(ret['saltenv'], 'foo') + self.assertNotIn("environment", ret) + self.assertEqual(ret["saltenv"], "foo") class APIConfigTestCase(DefaultConfigsBase, TestCase): - ''' + """ TestCase for the api_config function in salt.config.__init__.py - ''' + """ def setUp(self): # Copy DEFAULT_API_OPTS to restore after the test @@ -1738,87 +1836,97 @@ class APIConfigTestCase(DefaultConfigsBase, TestCase): salt.config.DEFAULT_API_OPTS = self.default_api_opts def test_api_config_log_file_values(self): - ''' + """ Tests the opts value of the 'log_file' after running through the various default dict updates. 'log_file' should be updated to match the DEFAULT_API_OPTS 'api_logfile' value. - ''' - with patch('salt.config.client_config', MagicMock(return_value=self.mock_master_default_opts)): - expected = '{0}/var/log/salt/api'.format( - RUNTIME_VARS.TMP_ROOT_DIR if RUNTIME_VARS.TMP_ROOT_DIR != '/' else '') + """ + with patch( + "salt.config.client_config", + MagicMock(return_value=self.mock_master_default_opts), + ): + expected = "{0}/var/log/salt/api".format( + RUNTIME_VARS.TMP_ROOT_DIR if RUNTIME_VARS.TMP_ROOT_DIR != "/" else "" + ) if salt.utils.platform.is_windows(): - expected = '{}\\var\\log\\salt\\api'.format( - RUNTIME_VARS.TMP_ROOT_DIR) + expected = "{}\\var\\log\\salt\\api".format(RUNTIME_VARS.TMP_ROOT_DIR) - ret = salt.config.api_config('/some/fake/path') - self.assertEqual(ret['log_file'], expected) + ret = salt.config.api_config("/some/fake/path") + self.assertEqual(ret["log_file"], expected) def test_api_config_pidfile_values(self): - ''' + """ Tests the opts value of the 'pidfile' after running through the various default dict updates. 'pidfile' should be updated to match the DEFAULT_API_OPTS 'api_pidfile' value. - ''' - with patch('salt.config.client_config', MagicMock(return_value=self.mock_master_default_opts)): - expected = '{0}/var/run/salt-api.pid'.format( - RUNTIME_VARS.TMP_ROOT_DIR if RUNTIME_VARS.TMP_ROOT_DIR != '/' else '') + """ + with patch( + "salt.config.client_config", + MagicMock(return_value=self.mock_master_default_opts), + ): + expected = "{0}/var/run/salt-api.pid".format( + RUNTIME_VARS.TMP_ROOT_DIR if RUNTIME_VARS.TMP_ROOT_DIR != "/" else "" + ) if salt.utils.platform.is_windows(): - expected = '{}\\var\\run\\salt-api.pid'.format( - RUNTIME_VARS.TMP_ROOT_DIR) + expected = "{}\\var\\run\\salt-api.pid".format( + RUNTIME_VARS.TMP_ROOT_DIR + ) - ret = salt.config.api_config('/some/fake/path') - self.assertEqual(ret['pidfile'], expected) + ret = salt.config.api_config("/some/fake/path") + self.assertEqual(ret["pidfile"], expected) def test_master_config_file_overrides_defaults(self): - ''' + """ Tests the opts value of the api config values after running through the various default dict updates that should be overridden by settings in the user's master config file. - ''' - foo_dir = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'foo/bar/baz') - hello_dir = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, 'hello/world') + """ + foo_dir = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, "foo/bar/baz") + hello_dir = os.path.join(RUNTIME_VARS.TMP_ROOT_DIR, "hello/world") if salt.utils.platform.is_windows(): - foo_dir = 'c:\\{}'.format(foo_dir.replace('/', '\\')) - hello_dir = 'c:\\{}'.format(hello_dir.replace('/', '\\')) + foo_dir = "c:\\{}".format(foo_dir.replace("/", "\\")) + hello_dir = "c:\\{}".format(hello_dir.replace("/", "\\")) mock_master_config = { - 'api_pidfile': foo_dir, - 'api_logfile': hello_dir, - 'rest_timeout': 5 + "api_pidfile": foo_dir, + "api_logfile": hello_dir, + "rest_timeout": 5, } mock_master_config.update(self.mock_master_default_opts.copy()) - with patch('salt.config.client_config', - MagicMock(return_value=mock_master_config)): - ret = salt.config.api_config('/some/fake/path') - self.assertEqual(ret['rest_timeout'], 5) - self.assertEqual(ret['api_pidfile'], foo_dir) - self.assertEqual(ret['pidfile'], foo_dir) - self.assertEqual(ret['api_logfile'], hello_dir) - self.assertEqual(ret['log_file'], hello_dir) + with patch( + "salt.config.client_config", MagicMock(return_value=mock_master_config) + ): + ret = salt.config.api_config("/some/fake/path") + self.assertEqual(ret["rest_timeout"], 5) + self.assertEqual(ret["api_pidfile"], foo_dir) + self.assertEqual(ret["pidfile"], foo_dir) + self.assertEqual(ret["api_logfile"], hello_dir) + self.assertEqual(ret["log_file"], hello_dir) def test_api_config_prepend_root_dirs_return(self): - ''' + """ Tests the opts value of the api_logfile, log_file, api_pidfile, and pidfile when a custom root directory is used. This ensures that each of these values is present in the list of opts keys that should have the root_dir prepended when the api_config function returns the opts dictionary. - ''' - mock_log = '/mock/root/var/log/salt/api' - mock_pid = '/mock/root/var/run/salt-api.pid' + """ + mock_log = "/mock/root/var/log/salt/api" + mock_pid = "/mock/root/var/run/salt-api.pid" mock_master_config = self.mock_master_default_opts.copy() - mock_master_config['root_dir'] = '/mock/root/' + mock_master_config["root_dir"] = "/mock/root/" if salt.utils.platform.is_windows(): - mock_log = 'c:\\mock\\root\\var\\log\\salt\\api' - mock_pid = 'c:\\mock\\root\\var\\run\\salt-api.pid' - mock_master_config['root_dir'] = 'c:\\mock\\root' + mock_log = "c:\\mock\\root\\var\\log\\salt\\api" + mock_pid = "c:\\mock\\root\\var\\run\\salt-api.pid" + mock_master_config["root_dir"] = "c:\\mock\\root" - with patch('salt.config.client_config', - MagicMock(return_value=mock_master_config)): - ret = salt.config.api_config('/some/fake/path') - self.assertEqual(ret['api_logfile'], mock_log) - self.assertEqual(ret['log_file'], mock_log) - self.assertEqual(ret['api_pidfile'], mock_pid) - self.assertEqual(ret['pidfile'], mock_pid) + with patch( + "salt.config.client_config", MagicMock(return_value=mock_master_config) + ): + ret = salt.config.api_config("/some/fake/path") + self.assertEqual(ret["api_logfile"], mock_log) + self.assertEqual(ret["log_file"], mock_log) + self.assertEqual(ret["api_pidfile"], mock_pid) + self.assertEqual(ret["pidfile"], mock_pid) diff --git a/tests/unit/test_crypt.py b/tests/unit/test_crypt.py index abf0e39aa93..f0f1ae0e40f 100644 --- a/tests/unit/test_crypt.py +++ b/tests/unit/test_crypt.py @@ -2,247 +2,288 @@ # python libs from __future__ import absolute_import -import os -import tempfile -import shutil -# salt testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - mock_open, - MagicMock, - MockCall, -) +import os +import shutil +import tempfile + +import salt.utils.files +from salt import crypt # salt libs from salt.ext import six -import salt.utils.files -from salt import crypt +from tests.support.mock import MagicMock, MockCall, mock_open, patch + +# salt testing libs +from tests.support.unit import TestCase, skipIf # third-party libs try: import M2Crypto + HAS_M2 = True except ImportError: HAS_M2 = False try: from Cryptodome.PublicKey import RSA # pylint: disable=unused-import + HAS_PYCRYPTO_RSA = True except ImportError: HAS_PYCRYPTO_RSA = False if not HAS_PYCRYPTO_RSA: try: from Crypto.PublicKey import RSA + HAS_PYCRYPTO_RSA = True except ImportError: HAS_PYCRYPTO_RSA = False PRIVKEY_DATA = ( - '-----BEGIN RSA PRIVATE KEY-----\n' - 'MIIEpAIBAAKCAQEA75GR6ZTv5JOv90Vq8tKhKC7YQnhDIo2hM0HVziTEk5R4UQBW\n' - 'a0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD4ZMsYqLzqjWMekLC8bjhxc+EuPo9\n' - 'Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R4hOcMMZNZdi0xLtFoTfwU61UPfFX\n' - '14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttLP3sMXJvc3EvM0JiDVj4l1TWFUHHz\n' - 'eFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k6ai4tVzwkTmV5PsriP1ju88Lo3MB\n' - '4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWHAQIDAQABAoIBAGOzBzBYZUWRGOgl\n' - 'IY8QjTT12dY/ymC05GM6gMobjxuD7FZ5d32HDLu/QrknfS3kKlFPUQGDAbQhbbb0\n' - 'zw6VL5NO9mfOPO2W/3FaG1sRgBQcerWonoSSSn8OJwVBHMFLG3a+U1Zh1UvPoiPK\n' - 'S734swIM+zFpNYivGPvOm/muF/waFf8tF/47t1cwt/JGXYQnkG/P7z0vp47Irpsb\n' - 'Yjw7vPe4BnbY6SppSxscW3KoV7GtJLFKIxAXbxsuJMF/rYe3O3w2VKJ1Sug1VDJl\n' - '/GytwAkSUer84WwP2b07Wn4c5pCnmLslMgXCLkENgi1NnJMhYVOnckxGDZk54hqP\n' - '9RbLnkkCgYEA/yKuWEvgdzYRYkqpzB0l9ka7Y00CV4Dha9Of6GjQi9i4VCJ/UFVr\n' - 'UlhTo5y0ZzpcDAPcoZf5CFZsD90a/BpQ3YTtdln2MMCL/Kr3QFmetkmDrt+3wYnX\n' - 'sKESfsa2nZdOATRpl1antpwyD4RzsAeOPwBiACj4fkq5iZJBSI0bxrMCgYEA8GFi\n' - 'qAjgKh81/Uai6KWTOW2kX02LEMVRrnZLQ9VPPLGid4KZDDk1/dEfxjjkcyOxX1Ux\n' - 'Klu4W8ZEdZyzPcJrfk7PdopfGOfrhWzkREK9C40H7ou/1jUecq/STPfSOmxh3Y+D\n' - 'ifMNO6z4sQAHx8VaHaxVsJ7SGR/spr0pkZL+NXsCgYEA84rIgBKWB1W+TGRXJzdf\n' - 'yHIGaCjXpm2pQMN3LmP3RrcuZWm0vBt94dHcrR5l+u/zc6iwEDTAjJvqdU4rdyEr\n' - 'tfkwr7v6TNlQB3WvpWanIPyVzfVSNFX/ZWSsAgZvxYjr9ixw6vzWBXOeOb/Gqu7b\n' - 'cvpLkjmJ0wxDhbXtyXKhZA8CgYBZyvcQb+hUs732M4mtQBSD0kohc5TsGdlOQ1AQ\n' - 'McFcmbpnzDghkclyW8jzwdLMk9uxEeDAwuxWE/UEvhlSi6qdzxC+Zifp5NBc0fVe\n' - '7lMx2mfJGxj5CnSqQLVdHQHB4zSXkAGB6XHbBd0MOUeuvzDPfs2voVQ4IG3FR0oc\n' - '3/znuwKBgQChZGH3McQcxmLA28aUwOVbWssfXKdDCsiJO+PEXXlL0maO3SbnFn+Q\n' - 'Tyf8oHI5cdP7AbwDSx9bUfRPjg9dKKmATBFr2bn216pjGxK0OjYOCntFTVr0psRB\n' - 'CrKg52Qrq71/2l4V2NLQZU40Dr1bN9V+Ftd9L0pvpCAEAWpIbLXGDw==\n' - '-----END RSA PRIVATE KEY-----') + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpAIBAAKCAQEA75GR6ZTv5JOv90Vq8tKhKC7YQnhDIo2hM0HVziTEk5R4UQBW\n" + "a0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD4ZMsYqLzqjWMekLC8bjhxc+EuPo9\n" + "Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R4hOcMMZNZdi0xLtFoTfwU61UPfFX\n" + "14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttLP3sMXJvc3EvM0JiDVj4l1TWFUHHz\n" + "eFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k6ai4tVzwkTmV5PsriP1ju88Lo3MB\n" + "4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWHAQIDAQABAoIBAGOzBzBYZUWRGOgl\n" + "IY8QjTT12dY/ymC05GM6gMobjxuD7FZ5d32HDLu/QrknfS3kKlFPUQGDAbQhbbb0\n" + "zw6VL5NO9mfOPO2W/3FaG1sRgBQcerWonoSSSn8OJwVBHMFLG3a+U1Zh1UvPoiPK\n" + "S734swIM+zFpNYivGPvOm/muF/waFf8tF/47t1cwt/JGXYQnkG/P7z0vp47Irpsb\n" + "Yjw7vPe4BnbY6SppSxscW3KoV7GtJLFKIxAXbxsuJMF/rYe3O3w2VKJ1Sug1VDJl\n" + "/GytwAkSUer84WwP2b07Wn4c5pCnmLslMgXCLkENgi1NnJMhYVOnckxGDZk54hqP\n" + "9RbLnkkCgYEA/yKuWEvgdzYRYkqpzB0l9ka7Y00CV4Dha9Of6GjQi9i4VCJ/UFVr\n" + "UlhTo5y0ZzpcDAPcoZf5CFZsD90a/BpQ3YTtdln2MMCL/Kr3QFmetkmDrt+3wYnX\n" + "sKESfsa2nZdOATRpl1antpwyD4RzsAeOPwBiACj4fkq5iZJBSI0bxrMCgYEA8GFi\n" + "qAjgKh81/Uai6KWTOW2kX02LEMVRrnZLQ9VPPLGid4KZDDk1/dEfxjjkcyOxX1Ux\n" + "Klu4W8ZEdZyzPcJrfk7PdopfGOfrhWzkREK9C40H7ou/1jUecq/STPfSOmxh3Y+D\n" + "ifMNO6z4sQAHx8VaHaxVsJ7SGR/spr0pkZL+NXsCgYEA84rIgBKWB1W+TGRXJzdf\n" + "yHIGaCjXpm2pQMN3LmP3RrcuZWm0vBt94dHcrR5l+u/zc6iwEDTAjJvqdU4rdyEr\n" + "tfkwr7v6TNlQB3WvpWanIPyVzfVSNFX/ZWSsAgZvxYjr9ixw6vzWBXOeOb/Gqu7b\n" + "cvpLkjmJ0wxDhbXtyXKhZA8CgYBZyvcQb+hUs732M4mtQBSD0kohc5TsGdlOQ1AQ\n" + "McFcmbpnzDghkclyW8jzwdLMk9uxEeDAwuxWE/UEvhlSi6qdzxC+Zifp5NBc0fVe\n" + "7lMx2mfJGxj5CnSqQLVdHQHB4zSXkAGB6XHbBd0MOUeuvzDPfs2voVQ4IG3FR0oc\n" + "3/znuwKBgQChZGH3McQcxmLA28aUwOVbWssfXKdDCsiJO+PEXXlL0maO3SbnFn+Q\n" + "Tyf8oHI5cdP7AbwDSx9bUfRPjg9dKKmATBFr2bn216pjGxK0OjYOCntFTVr0psRB\n" + "CrKg52Qrq71/2l4V2NLQZU40Dr1bN9V+Ftd9L0pvpCAEAWpIbLXGDw==\n" + "-----END RSA PRIVATE KEY-----" +) PUBKEY_DATA = ( - '-----BEGIN PUBLIC KEY-----\n' - 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA75GR6ZTv5JOv90Vq8tKh\n' - 'KC7YQnhDIo2hM0HVziTEk5R4UQBWa0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD\n' - '4ZMsYqLzqjWMekLC8bjhxc+EuPo9Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R\n' - '4hOcMMZNZdi0xLtFoTfwU61UPfFX14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttL\n' - 'P3sMXJvc3EvM0JiDVj4l1TWFUHHzeFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k\n' - '6ai4tVzwkTmV5PsriP1ju88Lo3MB4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWH\n' - 'AQIDAQAB\n' - '-----END PUBLIC KEY-----') + "-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA75GR6ZTv5JOv90Vq8tKh\n" + "KC7YQnhDIo2hM0HVziTEk5R4UQBWa0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD\n" + "4ZMsYqLzqjWMekLC8bjhxc+EuPo9Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R\n" + "4hOcMMZNZdi0xLtFoTfwU61UPfFX14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttL\n" + "P3sMXJvc3EvM0JiDVj4l1TWFUHHzeFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k\n" + "6ai4tVzwkTmV5PsriP1ju88Lo3MB4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWH\n" + "AQIDAQAB\n" + "-----END PUBLIC KEY-----" +) -MSG = b'It\'s me, Mario' +MSG = b"It's me, Mario" SIG = ( - b'\x07\xf3\xb1\xe7\xdb\x06\xf4_\xe2\xdc\xcb!F\xfb\xbex{W\x1d\xe4E' - b'\xd3\r\xc5\x90\xca(\x05\x1d\x99\x8b\x1aug\x9f\x95>\x94\x7f\xe3+' - b'\x12\xfa\x9c\xd4\xb8\x02]\x0e\xa5\xa3LL\xc3\xa2\x8f+\x83Z\x1b\x17' - b'\xbfT\xd3\xc7\xfd\x0b\xf4\xd7J\xfe^\x86q"I\xa3x\xbc\xd3$\xe9M<\xe1' - b'\x07\xad\xf2_\x9f\xfa\xf7g(~\xd8\xf5\xe7\xda-\xa3Ko\xfc.\x99\xcf' - b'\x9b\xb9\xc1U\x97\x82\'\xcb\xc6\x08\xaa\xa0\xe4\xd0\xc1+\xfc\x86' - b'\r\xe4y\xb1#\xd3\x1dS\x96D28\xc4\xd5\r\xd4\x98\x1a44"\xd7\xc2\xb4' - b']\xa7\x0f\xa7Db\x85G\x8c\xd6\x94!\x8af1O\xf6g\xd7\x03\xfd\xb3\xbc' - b'\xce\x9f\xe7\x015\xb8\x1d]AHK\xa0\x14m\xda=O\xa7\xde\xf2\xff\x9b' - b'\x8e\x83\xc8j\x11\x1a\x98\x85\xde\xc5\x91\x07\x84!\x12^4\xcb\xa8' - b'\x98\x8a\x8a&#\xb9(#?\x80\x15\x9eW\xb5\x12\xd1\x95S\xf2<G\xeb\xf1' - b'\x14H\xb2\xc4>\xc3A\xed\x86x~\xcfU\xd5Q\xfe~\x10\xd2\x9b') + b"\x07\xf3\xb1\xe7\xdb\x06\xf4_\xe2\xdc\xcb!F\xfb\xbex{W\x1d\xe4E" + b"\xd3\r\xc5\x90\xca(\x05\x1d\x99\x8b\x1aug\x9f\x95>\x94\x7f\xe3+" + b"\x12\xfa\x9c\xd4\xb8\x02]\x0e\xa5\xa3LL\xc3\xa2\x8f+\x83Z\x1b\x17" + b'\xbfT\xd3\xc7\xfd\x0b\xf4\xd7J\xfe^\x86q"I\xa3x\xbc\xd3$\xe9M<\xe1' + b"\x07\xad\xf2_\x9f\xfa\xf7g(~\xd8\xf5\xe7\xda-\xa3Ko\xfc.\x99\xcf" + b"\x9b\xb9\xc1U\x97\x82'\xcb\xc6\x08\xaa\xa0\xe4\xd0\xc1+\xfc\x86" + b'\r\xe4y\xb1#\xd3\x1dS\x96D28\xc4\xd5\r\xd4\x98\x1a44"\xd7\xc2\xb4' + b"]\xa7\x0f\xa7Db\x85G\x8c\xd6\x94!\x8af1O\xf6g\xd7\x03\xfd\xb3\xbc" + b"\xce\x9f\xe7\x015\xb8\x1d]AHK\xa0\x14m\xda=O\xa7\xde\xf2\xff\x9b" + b"\x8e\x83\xc8j\x11\x1a\x98\x85\xde\xc5\x91\x07\x84!\x12^4\xcb\xa8" + b"\x98\x8a\x8a&#\xb9(#?\x80\x15\x9eW\xb5\x12\xd1\x95S\xf2<G\xeb\xf1" + b"\x14H\xb2\xc4>\xc3A\xed\x86x~\xcfU\xd5Q\xfe~\x10\xd2\x9b" +) -@skipIf(not HAS_PYCRYPTO_RSA, 'pycrypto >= 2.6 is not available') -@skipIf(HAS_M2, 'm2crypto is used by salt.crypt if installed') +@skipIf(not HAS_PYCRYPTO_RSA, "pycrypto >= 2.6 is not available") +@skipIf(HAS_M2, "m2crypto is used by salt.crypt if installed") class CryptTestCase(TestCase): def test_gen_keys(self): - open_priv_wb = MockCall('/keydir{0}keyname.pem'.format(os.sep), 'wb+') - open_pub_wb = MockCall('/keydir{0}keyname.pub'.format(os.sep), 'wb+') + open_priv_wb = MockCall("/keydir{0}keyname.pem".format(os.sep), "wb+") + open_pub_wb = MockCall("/keydir{0}keyname.pub".format(os.sep), "wb+") - with patch.multiple(os, umask=MagicMock(), chmod=MagicMock(), - access=MagicMock(return_value=True)): - with patch('salt.utils.files.fopen', mock_open()) as m_open, \ - patch('os.path.isfile', return_value=True): - result = crypt.gen_keys('/keydir', 'keyname', 2048) - assert result == '/keydir{0}keyname.pem'.format(os.sep), result + with patch.multiple( + os, + umask=MagicMock(), + chmod=MagicMock(), + access=MagicMock(return_value=True), + ): + with patch("salt.utils.files.fopen", mock_open()) as m_open, patch( + "os.path.isfile", return_value=True + ): + result = crypt.gen_keys("/keydir", "keyname", 2048) + assert result == "/keydir{0}keyname.pem".format(os.sep), result assert open_priv_wb not in m_open.calls assert open_pub_wb not in m_open.calls - with patch('salt.utils.files.fopen', mock_open()) as m_open, \ - patch('os.path.isfile', return_value=False): - crypt.gen_keys('/keydir', 'keyname', 2048) + with patch("salt.utils.files.fopen", mock_open()) as m_open, patch( + "os.path.isfile", return_value=False + ): + crypt.gen_keys("/keydir", "keyname", 2048) assert open_priv_wb in m_open.calls assert open_pub_wb in m_open.calls - @patch('os.umask', MagicMock()) - @patch('os.chmod', MagicMock()) - @patch('os.chown', MagicMock(), create=True) - @patch('os.access', MagicMock(return_value=True)) + @patch("os.umask", MagicMock()) + @patch("os.chmod", MagicMock()) + @patch("os.chown", MagicMock(), create=True) + @patch("os.access", MagicMock(return_value=True)) def test_gen_keys_with_passphrase(self): - key_path = os.path.join(os.sep, 'keydir') - open_priv_wb = MockCall(os.path.join(key_path, 'keyname.pem'), 'wb+') - open_pub_wb = MockCall(os.path.join(key_path, 'keyname.pub'), 'wb+') + key_path = os.path.join(os.sep, "keydir") + open_priv_wb = MockCall(os.path.join(key_path, "keyname.pem"), "wb+") + open_pub_wb = MockCall(os.path.join(key_path, "keyname.pub"), "wb+") - with patch('salt.utils.files.fopen', mock_open()) as m_open, \ - patch('os.path.isfile', return_value=True): - self.assertEqual(crypt.gen_keys(key_path, 'keyname', 2048, passphrase='password'), os.path.join(key_path, 'keyname.pem')) - result = crypt.gen_keys(key_path, 'keyname', 2048, - passphrase='password') - assert result == os.path.join(key_path, 'keyname.pem'), result + with patch("salt.utils.files.fopen", mock_open()) as m_open, patch( + "os.path.isfile", return_value=True + ): + self.assertEqual( + crypt.gen_keys(key_path, "keyname", 2048, passphrase="password"), + os.path.join(key_path, "keyname.pem"), + ) + result = crypt.gen_keys(key_path, "keyname", 2048, passphrase="password") + assert result == os.path.join(key_path, "keyname.pem"), result assert open_priv_wb not in m_open.calls assert open_pub_wb not in m_open.calls - with patch('salt.utils.files.fopen', mock_open()) as m_open, \ - patch('os.path.isfile', return_value=False): - crypt.gen_keys(key_path, 'keyname', 2048) + with patch("salt.utils.files.fopen", mock_open()) as m_open, patch( + "os.path.isfile", return_value=False + ): + crypt.gen_keys(key_path, "keyname", 2048) assert open_priv_wb in m_open.calls assert open_pub_wb in m_open.calls def test_sign_message(self): key = RSA.importKey(PRIVKEY_DATA) - with patch('salt.crypt.get_rsa_key', return_value=key): - self.assertEqual(SIG, salt.crypt.sign_message('/keydir/keyname.pem', MSG)) + with patch("salt.crypt.get_rsa_key", return_value=key): + self.assertEqual(SIG, salt.crypt.sign_message("/keydir/keyname.pem", MSG)) def test_sign_message_with_passphrase(self): key = RSA.importKey(PRIVKEY_DATA) - with patch('salt.crypt.get_rsa_key', return_value=key): - self.assertEqual(SIG, crypt.sign_message('/keydir/keyname.pem', MSG, passphrase='password')) + with patch("salt.crypt.get_rsa_key", return_value=key): + self.assertEqual( + SIG, + crypt.sign_message("/keydir/keyname.pem", MSG, passphrase="password"), + ) def test_verify_signature(self): - with patch('salt.utils.files.fopen', mock_open(read_data=PUBKEY_DATA)): - self.assertTrue(crypt.verify_signature('/keydir/keyname.pub', MSG, SIG)) + with patch("salt.utils.files.fopen", mock_open(read_data=PUBKEY_DATA)): + self.assertTrue(crypt.verify_signature("/keydir/keyname.pub", MSG, SIG)) -@skipIf(not HAS_M2, 'm2crypto is not available') +@skipIf(not HAS_M2, "m2crypto is not available") class M2CryptTestCase(TestCase): - @patch('os.umask', MagicMock()) - @patch('os.chmod', MagicMock()) - @patch('os.access', MagicMock(return_value=True)) + @patch("os.umask", MagicMock()) + @patch("os.chmod", MagicMock()) + @patch("os.access", MagicMock(return_value=True)) def test_gen_keys(self): - with patch('M2Crypto.RSA.RSA.save_pem', MagicMock()) as save_pem: - with patch('M2Crypto.RSA.RSA.save_pub_key', MagicMock()) as save_pub: - with patch('os.path.isfile', return_value=True): - self.assertEqual(crypt.gen_keys('/keydir', 'keyname', 2048), - '/keydir{0}keyname.pem'.format(os.sep)) + with patch("M2Crypto.RSA.RSA.save_pem", MagicMock()) as save_pem: + with patch("M2Crypto.RSA.RSA.save_pub_key", MagicMock()) as save_pub: + with patch("os.path.isfile", return_value=True): + self.assertEqual( + crypt.gen_keys("/keydir", "keyname", 2048), + "/keydir{0}keyname.pem".format(os.sep), + ) save_pem.assert_not_called() save_pub.assert_not_called() - with patch('os.path.isfile', return_value=False): - self.assertEqual(crypt.gen_keys('/keydir', 'keyname', 2048), - '/keydir{0}keyname.pem'.format(os.sep)) - save_pem.assert_called_once_with('/keydir{0}keyname.pem'.format(os.sep), cipher=None) - save_pub.assert_called_once_with('/keydir{0}keyname.pub'.format(os.sep)) + with patch("os.path.isfile", return_value=False): + self.assertEqual( + crypt.gen_keys("/keydir", "keyname", 2048), + "/keydir{0}keyname.pem".format(os.sep), + ) + save_pem.assert_called_once_with( + "/keydir{0}keyname.pem".format(os.sep), cipher=None + ) + save_pub.assert_called_once_with( + "/keydir{0}keyname.pub".format(os.sep) + ) - @patch('os.umask', MagicMock()) - @patch('os.chmod', MagicMock()) - @patch('os.chown', MagicMock()) - @patch('os.access', MagicMock(return_value=True)) + @patch("os.umask", MagicMock()) + @patch("os.chmod", MagicMock()) + @patch("os.chown", MagicMock()) + @patch("os.access", MagicMock(return_value=True)) def test_gen_keys_with_passphrase(self): - with patch('M2Crypto.RSA.RSA.save_pem', MagicMock()) as save_pem: - with patch('M2Crypto.RSA.RSA.save_pub_key', MagicMock()) as save_pub: - with patch('os.path.isfile', return_value=True): - self.assertEqual(crypt.gen_keys('/keydir', 'keyname', 2048, passphrase='password'), - '/keydir{0}keyname.pem'.format(os.sep)) + with patch("M2Crypto.RSA.RSA.save_pem", MagicMock()) as save_pem: + with patch("M2Crypto.RSA.RSA.save_pub_key", MagicMock()) as save_pub: + with patch("os.path.isfile", return_value=True): + self.assertEqual( + crypt.gen_keys( + "/keydir", "keyname", 2048, passphrase="password" + ), + "/keydir{0}keyname.pem".format(os.sep), + ) save_pem.assert_not_called() save_pub.assert_not_called() - with patch('os.path.isfile', return_value=False): - self.assertEqual(crypt.gen_keys('/keydir', 'keyname', 2048, passphrase='password'), - '/keydir{0}keyname.pem'.format(os.sep)) - callback = save_pem.call_args[1]['callback'] - save_pem.assert_called_once_with('/keydir{0}keyname.pem'.format(os.sep), - cipher='des_ede3_cbc', - callback=callback) - self.assertEqual(callback(None), b'password') - save_pub.assert_called_once_with('/keydir{0}keyname.pub'.format(os.sep)) + with patch("os.path.isfile", return_value=False): + self.assertEqual( + crypt.gen_keys( + "/keydir", "keyname", 2048, passphrase="password" + ), + "/keydir{0}keyname.pem".format(os.sep), + ) + callback = save_pem.call_args[1]["callback"] + save_pem.assert_called_once_with( + "/keydir{0}keyname.pem".format(os.sep), + cipher="des_ede3_cbc", + callback=callback, + ) + self.assertEqual(callback(None), b"password") + save_pub.assert_called_once_with( + "/keydir{0}keyname.pub".format(os.sep) + ) def test_sign_message(self): key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA)) - with patch('salt.crypt.get_rsa_key', return_value=key): - self.assertEqual(SIG, salt.crypt.sign_message('/keydir/keyname.pem', MSG)) + with patch("salt.crypt.get_rsa_key", return_value=key): + self.assertEqual(SIG, salt.crypt.sign_message("/keydir/keyname.pem", MSG)) def test_sign_message_with_passphrase(self): key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA)) - with patch('salt.crypt.get_rsa_key', return_value=key): - self.assertEqual(SIG, crypt.sign_message('/keydir/keyname.pem', MSG, passphrase='password')) + with patch("salt.crypt.get_rsa_key", return_value=key): + self.assertEqual( + SIG, + crypt.sign_message("/keydir/keyname.pem", MSG, passphrase="password"), + ) def test_verify_signature(self): - with patch('salt.utils.files.fopen', mock_open(read_data=six.b(PUBKEY_DATA))): - self.assertTrue(crypt.verify_signature('/keydir/keyname.pub', MSG, SIG)) + with patch("salt.utils.files.fopen", mock_open(read_data=six.b(PUBKEY_DATA))): + self.assertTrue(crypt.verify_signature("/keydir/keyname.pub", MSG, SIG)) def test_encrypt_decrypt_bin(self): priv_key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA)) - pub_key = M2Crypto.RSA.load_pub_key_bio(M2Crypto.BIO.MemoryBuffer(six.b(PUBKEY_DATA))) - encrypted = salt.crypt.private_encrypt(priv_key, b'salt') + pub_key = M2Crypto.RSA.load_pub_key_bio( + M2Crypto.BIO.MemoryBuffer(six.b(PUBKEY_DATA)) + ) + encrypted = salt.crypt.private_encrypt(priv_key, b"salt") decrypted = salt.crypt.public_decrypt(pub_key, encrypted) - self.assertEqual(b'salt', decrypted) + self.assertEqual(b"salt", decrypted) class TestBadCryptodomePubKey(TestCase): - ''' + """ Test that we can load public keys exported by pycrpytodome<=3.4.6 - ''' + """ TEST_KEY = ( - '-----BEGIN RSA PUBLIC KEY-----\n' - 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzLtFhsvfbFDFaUgulSEX\n' - 'Gl12XriL1DT78Ef2/u8HHaSMmPie37BLWas/zaHwI6066bIyYQJ/nUCahTaoHM7L\n' - 'GlWc0wOU6zyfpihCRQHil05Y6F+olFBoZuYbFPvtp7/hJx/D7I/0n2o/c7M5i3Y2\n' - '3sBxAYNooIQHXHUmPQW6C9iu95ylZDW8JQzYy/EI4vCC8yQMdTK8jK1FQV0Sbwny\n' - 'qcMxSyAWDoFbnhh2P2TnO8HOWuUOaXR8ZHOJzVcDl+a6ew+medW090x3K5O1f80D\n' - '+WjgnG6b2HG7VQpOCfM2GALD/FrxicPilvZ38X1aLhJuwjmVE4LAAv8DVNJXohaO\n' - 'WQIDAQAB\n' - '-----END RSA PUBLIC KEY-----\n' + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzLtFhsvfbFDFaUgulSEX\n" + "Gl12XriL1DT78Ef2/u8HHaSMmPie37BLWas/zaHwI6066bIyYQJ/nUCahTaoHM7L\n" + "GlWc0wOU6zyfpihCRQHil05Y6F+olFBoZuYbFPvtp7/hJx/D7I/0n2o/c7M5i3Y2\n" + "3sBxAYNooIQHXHUmPQW6C9iu95ylZDW8JQzYy/EI4vCC8yQMdTK8jK1FQV0Sbwny\n" + "qcMxSyAWDoFbnhh2P2TnO8HOWuUOaXR8ZHOJzVcDl+a6ew+medW090x3K5O1f80D\n" + "+WjgnG6b2HG7VQpOCfM2GALD/FrxicPilvZ38X1aLhJuwjmVE4LAAv8DVNJXohaO\n" + "WQIDAQAB\n" + "-----END RSA PUBLIC KEY-----\n" ) def setUp(self): self.test_dir = tempfile.mkdtemp() - self.key_path = os.path.join(self.test_dir, 'cryptodom-3.4.6.pub') - with salt.utils.files.fopen(self.key_path, 'wb') as fd: + self.key_path = os.path.join(self.test_dir, "cryptodom-3.4.6.pub") + with salt.utils.files.fopen(self.key_path, "wb") as fd: fd.write(self.TEST_KEY.encode()) def tearDown(self): @@ -250,17 +291,17 @@ class TestBadCryptodomePubKey(TestCase): @skipIf(not HAS_M2, "Skip when m2crypto is not installed") def test_m2_bad_key(self): - ''' + """ Load public key with an invalid header using m2crypto and validate it - ''' + """ key = salt.crypt.get_rsa_pub_key(self.key_path) assert key.check_key() == 1 @skipIf(HAS_M2, "Skip when m2crypto is installed") def test_crypto_bad_key(self): - ''' + """ Load public key with an invalid header and validate it without m2crypto - ''' + """ key = salt.crypt.get_rsa_pub_key(self.key_path) assert key.can_encrypt() @@ -268,44 +309,48 @@ class TestBadCryptodomePubKey(TestCase): class TestM2CryptoRegression47124(TestCase): SIGNATURE = ( - b'w\xac\xfe18o\xeb\xfb\x14+\x9e\xd1\xb7\x7fe}\xec\xd6\xe1P\x9e\xab' - b'\xb5\x07\xe0\xc1\xfd\xda#\x04Z\x8d\x7f\x0b\x1f}:~\xb2s\x860u\x02N' + b"w\xac\xfe18o\xeb\xfb\x14+\x9e\xd1\xb7\x7fe}\xec\xd6\xe1P\x9e\xab" + b"\xb5\x07\xe0\xc1\xfd\xda#\x04Z\x8d\x7f\x0b\x1f}:~\xb2s\x860u\x02N" b'\xd4q"\xb7\x86*\x8f\x1f\xd0\x9d\x11\x92\xc5~\xa68\xac>\x12H\xc2%y,' - b'\xe6\xceU\x1e\xa3?\x0c,\xf0u\xbb\xd0[g_\xdd\x8b\xb0\x95:Y\x18\xa5*' - b'\x99\xfd\xf3K\x92\x92 ({\xd1\xff\xd9F\xc8\xd6K\x86e\xf9\xa8\xad\xb0z' - b'\xe3\x9dD\xf5k\x8b_<\xe7\xe7\xec\xf3"\'\xd5\xd2M\xb4\xce\x1a\xe3$' - b'\x9c\x81\xad\xf9\x11\xf6\xf5>)\xc7\xdd\x03&\xf7\x86@ks\xa6\x05\xc2' - b'\xd0\xbd\x1a7\xfc\xde\xe6\xb0\xad!\x12#\xc86Y\xea\xc5\xe3\xe2\xb3' - b'\xc9\xaf\xfa\x0c\xf2?\xbf\x93w\x18\x9e\x0b\xa2a\x10:M\x05\x89\xe2W.Q' - b'\xe8;yGT\xb1\xf2\xc6A\xd2\xc4\xbeN\xb3\xcfS\xaf\x03f\xe2\xb4)\xe7\xf6' + b"\xe6\xceU\x1e\xa3?\x0c,\xf0u\xbb\xd0[g_\xdd\x8b\xb0\x95:Y\x18\xa5*" + b"\x99\xfd\xf3K\x92\x92 ({\xd1\xff\xd9F\xc8\xd6K\x86e\xf9\xa8\xad\xb0z" + b"\xe3\x9dD\xf5k\x8b_<\xe7\xe7\xec\xf3\"'\xd5\xd2M\xb4\xce\x1a\xe3$" + b"\x9c\x81\xad\xf9\x11\xf6\xf5>)\xc7\xdd\x03&\xf7\x86@ks\xa6\x05\xc2" + b"\xd0\xbd\x1a7\xfc\xde\xe6\xb0\xad!\x12#\xc86Y\xea\xc5\xe3\xe2\xb3" + b"\xc9\xaf\xfa\x0c\xf2?\xbf\x93w\x18\x9e\x0b\xa2a\x10:M\x05\x89\xe2W.Q" + b"\xe8;yGT\xb1\xf2\xc6A\xd2\xc4\xbeN\xb3\xcfS\xaf\x03f\xe2\xb4)\xe7\xf6" b'\xdbs\xd0Z}8\xa4\xd2\x1fW*\xe6\x1c"\x8b\xd0\x18w\xb9\x7f\x9e\x96\xa3' - b'\xd9v\xf7\x833\x8e\x01' + b"\xd9v\xf7\x833\x8e\x01" ) @skipIf(not HAS_M2, "Skip when m2crypto is not installed") def test_m2crypto_verify_bytes(self): - message = salt.utils.stringutils.to_unicode('meh') - with patch('salt.utils.files.fopen', mock_open(read_data=six.b(PUBKEY_DATA))): - salt.crypt.verify_signature('/keydir/keyname.pub', message, self.SIGNATURE) + message = salt.utils.stringutils.to_unicode("meh") + with patch("salt.utils.files.fopen", mock_open(read_data=six.b(PUBKEY_DATA))): + salt.crypt.verify_signature("/keydir/keyname.pub", message, self.SIGNATURE) @skipIf(not HAS_M2, "Skip when m2crypto is not installed") def test_m2crypto_verify_unicode(self): - message = salt.utils.stringutils.to_bytes('meh') - with patch('salt.utils.files.fopen', mock_open(read_data=six.b(PUBKEY_DATA))): - salt.crypt.verify_signature('/keydir/keyname.pub', message, self.SIGNATURE) + message = salt.utils.stringutils.to_bytes("meh") + with patch("salt.utils.files.fopen", mock_open(read_data=six.b(PUBKEY_DATA))): + salt.crypt.verify_signature("/keydir/keyname.pub", message, self.SIGNATURE) @skipIf(not HAS_M2, "Skip when m2crypto is not installed") def test_m2crypto_sign_bytes(self): - message = salt.utils.stringutils.to_unicode('meh') + message = salt.utils.stringutils.to_unicode("meh") key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA)) - with patch('salt.crypt.get_rsa_key', return_value=key): - signature = salt.crypt.sign_message('/keydir/keyname.pem', message, passphrase='password') + with patch("salt.crypt.get_rsa_key", return_value=key): + signature = salt.crypt.sign_message( + "/keydir/keyname.pem", message, passphrase="password" + ) self.assertEqual(signature, self.SIGNATURE) @skipIf(not HAS_M2, "Skip when m2crypto is not installed") def test_m2crypto_sign_unicode(self): - message = salt.utils.stringutils.to_bytes('meh') + message = salt.utils.stringutils.to_bytes("meh") key = M2Crypto.RSA.load_key_string(six.b(PRIVKEY_DATA)) - with patch('salt.crypt.get_rsa_key', return_value=key): - signature = salt.crypt.sign_message('/keydir/keyname.pem', message, passphrase='password') + with patch("salt.crypt.get_rsa_key", return_value=key): + signature = salt.crypt.sign_message( + "/keydir/keyname.pem", message, passphrase="password" + ) self.assertEqual(signature, self.SIGNATURE) diff --git a/tests/unit/test_doc.py b/tests/unit/test_doc.py index 5c7f82420b3..e48715f904b 100644 --- a/tests/unit/test_doc.py +++ b/tests/unit/test_doc.py @@ -1,34 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.doc_test ~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import + +import collections +import logging import os import re -import logging - -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.runtests import RUNTIME_VARS # Import Salt libs import salt.modules.cmdmod +import salt.utils.files import salt.utils.platform +from tests.support.runtests import RUNTIME_VARS +# Import Salt Testing libs +from tests.support.unit import TestCase log = logging.getLogger(__name__) class DocTestCase(TestCase): - ''' + """ Unit test case for testing doc files and strings. - ''' + """ def test_check_for_doc_inline_markup(self): - ''' + """ We should not be using the ``:doc:`` inline markup option when cross-referencing locations. Use ``:ref:`` or ``:mod:`` instead. @@ -37,15 +39,17 @@ class DocTestCase(TestCase): See Issue #12788 for more information. https://github.com/saltstack/salt/issues/12788 - ''' + """ salt_dir = RUNTIME_VARS.CODE_DIR if salt.utils.platform.is_windows(): - if salt.utils.path.which('bash'): + if salt.utils.path.which("bash"): # Use grep from git-bash when it exists. - cmd = 'bash -c \'grep -r :doc: ./salt/' - grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd, cwd=salt_dir).split(os.linesep) - os_sep = '/' + cmd = "bash -c 'grep -r :doc: ./salt/" + grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd, cwd=salt_dir).split( + os.linesep + ) + os_sep = "/" else: # No grep in Windows, use findstr # findstr in windows doesn't prepend 'Binary` to binary files, so @@ -54,20 +58,20 @@ class DocTestCase(TestCase): grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd).split(os.linesep) os_sep = os.sep else: - salt_dir += '/' - cmd = 'grep -r :doc: ' + salt_dir + salt_dir += "/" + cmd = "grep -r :doc: " + salt_dir grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd).split(os.linesep) os_sep = os.sep test_ret = {} for line in grep_call: # Skip any .pyc files that may be present - if line.startswith('Binary'): + if line.startswith("Binary"): continue # Only split on colons not followed by a '\' as is the case with # Windows Drives - regex = re.compile(r':(?!\\)') + regex = re.compile(r":(?!\\)") try: key, val = regex.split(line, 1) except ValueError: @@ -77,17 +81,25 @@ class DocTestCase(TestCase): # Don't test man pages, this file, the tox or nox virtualenv files, # the page that documents to not use ":doc:", the doc/conf.py file # or the artifacts directory on nox CI test runs - if 'man' in key \ - or '.tox{}'.format(os_sep) in key \ - or '.nox{}'.format(os_sep) in key \ - or 'ext{}'.format(os_sep) in key \ - or 'artifacts{}'.format(os_sep) in key \ - or key.endswith('test_doc.py') \ - or key.endswith(os_sep.join(['doc', 'conf.py'])) \ - or key.endswith(os_sep.join(['conventions', 'documentation.rst'])) \ - or key.endswith(os_sep.join(['doc', 'topics', 'releases', '2016.11.2.rst'])) \ - or key.endswith(os_sep.join(['doc', 'topics', 'releases', '2016.11.3.rst'])) \ - or key.endswith(os_sep.join(['doc', 'topics', 'releases', '2016.3.5.rst'])): + if ( + "man" in key + or ".tox{}".format(os_sep) in key + or ".nox{}".format(os_sep) in key + or "ext{}".format(os_sep) in key + or "artifacts{}".format(os_sep) in key + or key.endswith("test_doc.py") + or key.endswith(os_sep.join(["doc", "conf.py"])) + or key.endswith(os_sep.join(["conventions", "documentation.rst"])) + or key.endswith( + os_sep.join(["doc", "topics", "releases", "2016.11.2.rst"]) + ) + or key.endswith( + os_sep.join(["doc", "topics", "releases", "2016.11.3.rst"]) + ) + or key.endswith( + os_sep.join(["doc", "topics", "releases", "2016.3.5.rst"]) + ) + ): continue # Set up test return dict @@ -101,3 +113,488 @@ class DocTestCase(TestCase): # test_ret should be empty, otherwise there are :doc: references present self.assertEqual(test_ret, {}) + + def _check_doc_files(self, module_skip, module_dir, doc_skip, module_doc_dir): + """ + Ensure various salt modules have associated documentation + """ + + salt_dir = RUNTIME_VARS.CODE_DIR + + # Build list of module files + module_files = [] + skip_module_files = module_skip + full_module_dir = os.path.join(salt_dir, *module_dir) + for file in os.listdir(full_module_dir): + if file.endswith(".py"): + module_name = os.path.splitext(file)[0] + if module_name not in skip_module_files: + module_files.append(module_name) + # Capture modules in subdirectories like inspectlib and rest_cherrypy + elif ( + os.path.isdir(os.path.join(full_module_dir, file)) + and not file.startswith("_") + and os.path.isfile(os.path.join(full_module_dir, file, "__init__.py")) + ): + module_name = file + if module_name not in skip_module_files: + module_files.append(module_name) + + # Build list of documentation files + module_docs = [] + skip_doc_files = doc_skip + full_module_doc_dir = os.path.join(salt_dir, *module_doc_dir) + doc_prefix = ".".join(module_dir) + "." + for file in os.listdir(full_module_doc_dir): + if file.endswith(".rst"): + doc_name = os.path.splitext(file)[0] + if doc_name.startswith(doc_prefix): + doc_name = doc_name[len(doc_prefix) :] + if doc_name not in skip_doc_files: + module_docs.append(doc_name) + + module_index_file = os.path.join(full_module_doc_dir, "index.rst") + with salt.utils.files.fopen(module_index_file, "rb") as fp: + module_index_contents = fp.read().decode("utf-8") + + module_index_block = re.search( + r""" + \.\.\s+autosummary::\s*\n + (\s+:[a-z]+:.*\n)* + (\s*\n)+ + (?P<mods>(\s*[a-z0-9_\.]+\s*\n)+) + """, + module_index_contents, + flags=re.VERBOSE, + ) + + module_index = re.findall( + r"""\s*([a-z0-9_\.]+)\s*\n""", module_index_block.group("mods") + ) + + # Check that every module has associated documentation file + for module in module_files: + self.assertIn( + module, + module_docs, + "module file {0} is missing documentation in {1}".format( + module, full_module_doc_dir + ), + ) + + # Check that every module is listed in the index file + self.assertIn( + module, + module_index, + "module file {0} is missing in {1}".format(module, module_index_file), + ) + + # Check if .rst file for this module contains the text + # ".. _virtual" indicating it is a virtual doc page + full_module_doc_name = os.path.join( + full_module_doc_dir, doc_prefix + module + ".rst" + ) + with salt.utils.files.fopen(full_module_doc_name) as rst_file: + rst_text = rst_file.read() + virtual_string = 'module file "{0}" is also a virtual doc page {1} and is not accessible' + self.assertNotIn( + ".. _virtual", + rst_text, + virtual_string.format(module, doc_prefix + module + ".rst"), + ) + + for doc_file in module_docs: + self.assertIn( + doc_file, + module_files, + "Doc file {0} is missing associated module in {1}".format( + doc_file, full_module_dir + ), + ) + # Check that a module index is sorted + sorted_module_index = sorted(module_index) + self.assertEqual( + module_index, + sorted_module_index, + msg="Module index is not sorted: {}".format(module_index_file), + ) + + # Check for duplicates inside of a module index + module_index_duplicates = [ + mod for mod, count in collections.Counter(module_index).items() if count > 1 + ] + if module_index_duplicates: + self.fail( + "Module index {0} contains duplicates: {1}".format( + module_index_file, module_index_duplicates + ) + ) + + # Check for stray module docs + # Do not check files listed in doc_skip + stray_modules = set(module_index).difference(module_files + doc_skip) + if stray_modules: + self.fail( + "Stray module names {0} in the doc index {1}".format( + sorted(list(stray_modules)), module_index_file + ) + ) + stray_modules = set(module_docs).difference(module_files) + if stray_modules: + self.fail( + "Stray module doc files {0} in the doc folder {1}".format( + sorted(list(stray_modules)), full_module_doc_dir + ) + ) + + def test_auth_doc_files(self): + """ + Ensure auth modules have associated documentation + + doc example: doc/ref/auth/all/salt.auth.rest.rst + auth module example: salt/auth/rest.py + """ + + skip_files = ["__init__"] + module_dir = ["salt", "auth"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "auth", "all"] + self._check_doc_files(skip_files, module_dir, skip_doc_files, doc_dir) + + def test_beacon_doc_files(self): + """ + Ensure beacon modules have associated documentation + + doc example: doc/ref/beacons/all/salt.beacon.rest.rst + beacon module example: salt/beacons/rest.py + """ + + skip_files = ["__init__"] + module_dir = ["salt", "beacons"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "beacons", "all"] + self._check_doc_files(skip_files, module_dir, skip_doc_files, doc_dir) + + def test_cache_doc_files(self): + """ + Ensure cache modules have associated documentation + + doc example: doc/ref/cache/all/salt.cache.consul.rst + cache module example: salt/cache/consul.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "cache"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "cache", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_cloud_doc_files(self): + """ + Ensure cloud modules have associated documentation + + doc example: doc/ref/clouds/all/salt.cloud.gce.rst + cloud module example: salt/cloud/clouds/gce.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "cloud", "clouds"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "clouds", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_engine_doc_files(self): + """ + Ensure engine modules have associated documentation + + doc example: doc/ref/engines/all/salt.engines.docker_events.rst + engine module example: salt/engines/docker_events.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "engines"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "engines", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_executors_doc_files(self): + """ + Ensure executor modules have associated documentation + + doc example: doc/ref/executors/all/salt.executors.docker.rst + engine module example: salt/executors/docker.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "executors"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "executors", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_fileserver_doc_files(self): + """ + Ensure fileserver modules have associated documentation + + doc example: doc/ref/fileserver/all/salt.fileserver.gitfs.rst + module example: salt/fileserver/gitfs.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "fileserver"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "file_server", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_grain_doc_files(self): + """ + Ensure grain modules have associated documentation + + doc example: doc/ref/grains/all/salt.grains.core.rst + module example: salt/grains/core.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "grains"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "grains", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_module_doc_files(self): + """ + Ensure modules have associated documentation + + doc example: doc/ref/modules/all/salt.modules.zabbix.rst + execution module example: salt/modules/zabbix.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "modules"] + skip_doc_files = [ + "index", + "group", + "inspectlib.collector", + "inspectlib.dbhandle", + "inspectlib.entities", + "inspectlib.exceptions", + "inspectlib.fsdb", + "inspectlib.kiwiproc", + "inspectlib.query", + "kernelpkg", + "pkg", + "user", + "service", + "shadow", + "sysctl", + ] + doc_dir = ["doc", "ref", "modules", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_netapi_doc_files(self): + """ + Ensure netapi modules have associated documentation + + doc example: doc/ref/netapi/all/salt.netapi.rest_cherrypy.rst + module example: salt/netapi/rest_cherrypy + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "netapi"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "netapi", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_output_doc_files(self): + """ + Ensure output modules have associated documentation + + doc example: doc/ref/output/all/salt.output.highstate.rst + module example: salt/output/highstate.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "output"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "output", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_pillar_doc_files(self): + """ + Ensure pillar modules have associated documentation + + doc example: doc/ref/pillar/all/salt.pillar.cobbler.rst + module example: salt/pillar/cobbler.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "pillar"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "pillar", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_proxy_doc_files(self): + """ + Ensure proxy modules have associated documentation + + doc example: doc/ref/proxy/all/salt.proxy.docker.rst + module example: salt/proxy/docker.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "proxy"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "proxy", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_queues_doc_files(self): + """ + Ensure queue modules have associated documentation + + doc example: doc/ref/queues/all/salt.queues.sqlite_queue.rst + module example: salt/queues/sqlite_queue.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "queues"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "queues", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_renderers_doc_files(self): + """ + Ensure render modules have associated documentation + + doc example: doc/ref/renderers/all/salt.renderers.json.rst + module example: salt/renderers/json.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "renderers"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "renderers", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_returners_doc_files(self): + """ + Ensure return modules have associated documentation + + doc example: doc/ref/returners/all/salt.returners.cassandra_return.rst + module example: salt/returners/cassandra_return.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "returners"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "returners", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_runners_doc_files(self): + """ + Ensure runner modules have associated documentation + + doc example: doc/ref/runners/all/salt.runners.auth.rst + module example: salt/runners/auth.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "runners"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "runners", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_sdb_doc_files(self): + """ + Ensure sdb modules have associated documentation + + doc example: doc/ref/sdb/all/salt.sdb.rest.rst + module example: salt/sdb/rest.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "sdb"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "sdb", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_serializers_doc_files(self): + """ + Ensure serializer modules have associated documentation + + doc example: doc/ref/serializers/all/salt.serializers.yaml.rst + module example: salt/serializers/yaml.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "serializers"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "serializers", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_states_doc_files(self): + """ + Ensure states have associated documentation + + doc example: doc/ref/states/all/salt.states.zabbix_host.rst + module example: salt/states/zabbix_host.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "states"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "states", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_thorium_doc_files(self): + """ + Ensure thorium modules have associated documentation + + doc example: doc/ref/thorium/all/salt.thorium.calc.rst + module example: salt/thorium/calc.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "thorium"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "thorium", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_token_doc_files(self): + """ + Ensure token modules have associated documentation + + doc example: doc/ref/tokens/all/salt.tokens.localfs.rst + module example: salt/tokens/localfs.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "tokens"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "tokens", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_tops_doc_files(self): + """ + Ensure top modules have associated documentation + + doc example: doc/ref/tops/all/salt.tops.saltclass.rst + module example: salt/tops/saltclass.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "tops"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "tops", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) + + def test_wheel_doc_files(self): + """ + Ensure wheel modules have associated documentation + + doc example: doc/ref/wheel/all/salt.wheel.key.rst + module example: salt/wheel/key.py + """ + + skip_module_files = ["__init__"] + module_dir = ["salt", "wheel"] + skip_doc_files = ["index", "all"] + doc_dir = ["doc", "ref", "wheel", "all"] + self._check_doc_files(skip_module_files, module_dir, skip_doc_files, doc_dir) diff --git a/tests/unit/test_engines.py b/tests/unit/test_engines.py index 3d785ea0234..66388a02704 100644 --- a/tests/unit/test_engines.py +++ b/tests/unit/test_engines.py @@ -1,44 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" unit tests for the Salt engines -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase -from tests.support.mock import ( - patch) +import logging + +import salt.config # Import Salt Libs import salt.engines as engines -import salt.config import salt.utils.process # Import 3rd-party libs from salt.ext import six -import logging +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import patch +from tests.support.unit import TestCase + log = logging.getLogger(__name__) class EngineTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.engine.sqs_events - ''' + """ def setup_loader_modules(self): return {engines: {}} def test_engine_module(self): - ''' + """ Test - ''' + """ mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() - mock_opts['__role'] = 'minion' - mock_opts['engines'] = [{'test_one': {'engine_module': 'test'}}, - {'test_two': {'engine_module': 'test'}}] + mock_opts["__role"] = "minion" + mock_opts["engines"] = [ + {"test_one": {"engine_module": "test"}}, + {"test_two": {"engine_module": "test"}}, + ] process_manager = salt.utils.process.ProcessManager() with patch.dict(engines.__opts__, mock_opts): @@ -47,10 +50,10 @@ class EngineTestCase(TestCase, LoaderModuleMockMixin): count = 0 for proc in six.iterkeys(process_map): count += 1 - fun = process_map[proc]['Process'].fun + fun = process_map[proc]["Process"].fun # Ensure function is start from the test engine - self.assertEqual(fun, 'test.start') + self.assertEqual(fun, "test.start") # Ensure there were two engine started - self.assertEqual(count, len(mock_opts['engines'])) + self.assertEqual(count, len(mock_opts["engines"])) diff --git a/tests/unit/test_ext.py b/tests/unit/test_ext.py index 3080147d9e7..f6bde1a488a 100644 --- a/tests/unit/test_ext.py +++ b/tests/unit/test_ext.py @@ -1,83 +1,87 @@ # -*- coding: utf-8 -*- # Import Python libs from __future__ import absolute_import, unicode_literals -import os -import sys + import logging +import os import subprocess +import sys import tempfile - -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.runtests import RUNTIME_VARS -import tests.support.helpers - # Import Salt libs import salt import salt.ext.six import salt.modules.cmdmod -import salt.utils.platform import salt.utils.files +import salt.utils.platform +import tests.support.helpers +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf log = logging.getLogger(__name__) -@skipIf(not salt.utils.path.which('bash'), 'Bash needed for this test') +@skipIf(not salt.utils.path.which("bash"), "Bash needed for this test") class VendorTornadoTest(TestCase): - ''' + """ Ensure we are not using any non vendor'ed tornado - ''' + """ def test_import_override(self): tmp = tempfile.mkdtemp() - test_source = tests.support.helpers.dedent(''' + test_source = tests.support.helpers.dedent( + """ from __future__ import absolute_import, print_function import salt import tornado print(tornado.__name__) - ''') - test_source_path = os.path.join(tmp, 'test.py') - tornado_source = tests.support.helpers.dedent(''' + """ + ) + test_source_path = os.path.join(tmp, "test.py") + tornado_source = tests.support.helpers.dedent( + """ foo = 'bar' - ''') - tornado_source_path = os.path.join(tmp, 'tornado.py') - with salt.utils.files.fopen(test_source_path, 'w') as fp: + """ + ) + tornado_source_path = os.path.join(tmp, "tornado.py") + with salt.utils.files.fopen(test_source_path, "w") as fp: fp.write(test_source) - with salt.utils.files.fopen(tornado_source_path, 'w') as fp: + with salt.utils.files.fopen(tornado_source_path, "w") as fp: fp.write(tornado_source) # Preserve the virtual environment env = os.environ.copy() if salt.utils.platform.is_windows(): if salt.ext.six.PY2: - env[b'PYTHONPATH'] = b';'.join([a.encode() for a in sys.path]) + env[b"PYTHONPATH"] = b";".join([a.encode() for a in sys.path]) else: - env['PYTHONPATH'] = ';'.join(sys.path) + env["PYTHONPATH"] = ";".join(sys.path) else: - env['PYTHONPATH'] = ':'.join(sys.path) + env["PYTHONPATH"] = ":".join(sys.path) p = subprocess.Popen( [sys.executable, test_source_path], stderr=subprocess.PIPE, stdout=subprocess.PIPE, - env=env + env=env, ) p.wait() pout = p.stdout.read().strip().decode() - assert pout == 'salt.ext.tornado', pout + assert pout == "salt.ext.tornado", pout def test_vendored_tornado_import(self): grep_call = salt.modules.cmdmod.run_stdout( - cmd='bash -c \'grep -r "import tornado" ./salt/*\'', + cmd="bash -c 'grep -r \"import tornado\" ./salt/*'", cwd=RUNTIME_VARS.CODE_DIR, ignore_retcode=True, - ).split('\n') + ).split("\n") valid_lines = [] for line in grep_call: - if line == '': + if line == "": continue # Skip salt/ext/tornado/.. since there are a bunch of imports like # this in docstrings. - if 'salt/ext/tornado/' in line: + if "salt/ext/tornado/" in line: continue log.error("Test found bad line: %s", line) valid_lines.append(line) @@ -85,13 +89,13 @@ class VendorTornadoTest(TestCase): def test_vendored_tornado_import_from(self): grep_call = salt.modules.cmdmod.run_stdout( - cmd='bash -c \'grep -r "from tornado" ./salt/*\'', + cmd="bash -c 'grep -r \"from tornado\" ./salt/*'", cwd=RUNTIME_VARS.CODE_DIR, ignore_retcode=True, - ).split('\n') + ).split("\n") valid_lines = [] for line in grep_call: - if line == '': + if line == "": continue log.error("Test found bad line: %s", line) valid_lines.append(line) @@ -100,6 +104,6 @@ class VendorTornadoTest(TestCase): def test_regression_56063(self): importer = salt.TornadoImporter() try: - importer.find_module('tornado') + importer.find_module("tornado") except TypeError: - assert False, 'TornadoImporter raised type error when one argument passed' + assert False, "TornadoImporter raised type error when one argument passed" diff --git a/tests/unit/test_fileclient.py b/tests/unit/test_fileclient.py index ecc9ed40f1c..fcc8b04cdef 100644 --- a/tests/unit/test_fileclient.py +++ b/tests/unit/test_fileclient.py @@ -1,101 +1,124 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the salt fileclient -''' +""" # Import Python libs from __future__ import absolute_import + import errno import logging import os import shutil -# Import Salt Testing libs -from tests.support.mixins import AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin -from tests.support.mock import patch, Mock, MagicMock -from tests.support.unit import TestCase -from tests.support.runtests import RUNTIME_VARS - # Import Salt libs import salt.utils.files -from salt.ext.six.moves import range from salt import fileclient from salt.ext import six +from salt.ext.six.moves import range + +# Import Salt Testing libs +from tests.support.mixins import ( + AdaptedConfigurationTestCaseMixin, + LoaderModuleMockMixin, +) +from tests.support.mock import MagicMock, Mock, patch +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase log = logging.getLogger(__name__) class FileclientTestCase(TestCase): - ''' + """ Fileclient test - ''' + """ + opts = { - 'extension_modules': '', - 'cachedir': '/__test__', + "extension_modules": "", + "cachedir": "/__test__", } def _fake_makedir(self, num=errno.EEXIST): def _side_effect(*args, **kwargs): - raise OSError(num, 'Errno {0}'.format(num)) + raise OSError(num, "Errno {0}".format(num)) + return Mock(side_effect=_side_effect) def test_cache_skips_makedirs_on_race_condition(self): - ''' + """ If cache contains already a directory, do not raise an exception. - ''' - with patch('os.path.isfile', lambda prm: False): + """ + with patch("os.path.isfile", lambda prm: False): for exists in range(2): - with patch('os.makedirs', self._fake_makedir()): - with fileclient.Client(self.opts)._cache_loc('testfile') as c_ref_itr: - assert c_ref_itr == os.sep + os.sep.join(['__test__', 'files', 'base', 'testfile']) + with patch("os.makedirs", self._fake_makedir()): + with fileclient.Client(self.opts)._cache_loc( + "testfile" + ) as c_ref_itr: + assert c_ref_itr == os.sep + os.sep.join( + ["__test__", "files", "base", "testfile"] + ) def test_cache_raises_exception_on_non_eexist_ioerror(self): - ''' + """ If makedirs raises other than EEXIST errno, an exception should be raised. - ''' - with patch('os.path.isfile', lambda prm: False): - with patch('os.makedirs', self._fake_makedir(num=errno.EROFS)): + """ + with patch("os.path.isfile", lambda prm: False): + with patch("os.makedirs", self._fake_makedir(num=errno.EROFS)): with self.assertRaises(OSError): - with fileclient.Client(self.opts)._cache_loc('testfile') as c_ref_itr: - assert c_ref_itr == '/__test__/files/base/testfile' + with fileclient.Client(self.opts)._cache_loc( + "testfile" + ) as c_ref_itr: + assert c_ref_itr == "/__test__/files/base/testfile" def test_extrn_path_with_long_filename(self): - safe_file_name = os.path.split(fileclient.Client(self.opts)._extrn_path('https://test.com/' + ('A' * 254), 'base'))[-1] - assert safe_file_name == 'A' * 254 + safe_file_name = os.path.split( + fileclient.Client(self.opts)._extrn_path( + "https://test.com/" + ("A" * 254), "base" + ) + )[-1] + assert safe_file_name == "A" * 254 - oversized_file_name = os.path.split(fileclient.Client(self.opts)._extrn_path('https://test.com/' + ('A' * 255), 'base'))[-1] + oversized_file_name = os.path.split( + fileclient.Client(self.opts)._extrn_path( + "https://test.com/" + ("A" * 255), "base" + ) + )[-1] assert len(oversized_file_name) < 256 - assert oversized_file_name != 'A' * 255 + assert oversized_file_name != "A" * 255 - oversized_file_with_query_params = os.path.split(fileclient.Client(self.opts)._extrn_path('https://test.com/file?' + ('A' * 255), 'base'))[-1] + oversized_file_with_query_params = os.path.split( + fileclient.Client(self.opts)._extrn_path( + "https://test.com/file?" + ("A" * 255), "base" + ) + )[-1] assert len(oversized_file_with_query_params) < 256 -SALTENVS = ('base', 'dev') -SUBDIR = 'subdir' -SUBDIR_FILES = ('foo.txt', 'bar.txt', 'baz.txt') +SALTENVS = ("base", "dev") +SUBDIR = "subdir" +SUBDIR_FILES = ("foo.txt", "bar.txt", "baz.txt") def _get_file_roots(fs_root): - return dict( - [(x, [os.path.join(fs_root, x)]) for x in SALTENVS] - ) + return dict([(x, [os.path.join(fs_root, x)]) for x in SALTENVS]) -class FileClientTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): - +class FileClientTest( + TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin +): def setup_loader_modules(self): - FS_ROOT = os.path.join(RUNTIME_VARS.TMP, 'fileclient_fs_root') - CACHE_ROOT = os.path.join(RUNTIME_VARS.TMP, 'fileclient_cache_root') + FS_ROOT = os.path.join(RUNTIME_VARS.TMP, "fileclient_fs_root") + CACHE_ROOT = os.path.join(RUNTIME_VARS.TMP, "fileclient_cache_root") MOCKED_OPTS = { - 'file_roots': _get_file_roots(FS_ROOT), - 'fileserver_backend': ['roots'], - 'cachedir': CACHE_ROOT, - 'file_client': 'local', + "file_roots": _get_file_roots(FS_ROOT), + "fileserver_backend": ["roots"], + "cachedir": CACHE_ROOT, + "file_client": "local", } self.addCleanup(shutil.rmtree, FS_ROOT, ignore_errors=True) self.addCleanup(shutil.rmtree, CACHE_ROOT, ignore_errors=True) - return {fileclient: {'__opts__': MOCKED_OPTS}} + return {fileclient: {"__opts__": MOCKED_OPTS}} def setUp(self): self.file_client = fileclient.Client(self.master_opts) @@ -104,58 +127,63 @@ class FileClientTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMo del self.file_client def test_file_list_emptydirs(self): - ''' + """ Ensure that the fileclient class won't allow a direct call to file_list_emptydirs() - ''' + """ with self.assertRaises(NotImplementedError): self.file_client.file_list_emptydirs() def test_get_file(self): - ''' + """ Ensure that the fileclient class won't allow a direct call to get_file() - ''' + """ with self.assertRaises(NotImplementedError): self.file_client.get_file(None) def test_get_file_client(self): - minion_opts = self.get_temp_config('minion') - minion_opts['file_client'] = 'remote' - with patch('salt.fileclient.RemoteClient', MagicMock(return_value='remote_client')): + minion_opts = self.get_temp_config("minion") + minion_opts["file_client"] = "remote" + with patch( + "salt.fileclient.RemoteClient", MagicMock(return_value="remote_client") + ): ret = fileclient.get_file_client(minion_opts) - self.assertEqual('remote_client', ret) + self.assertEqual("remote_client", ret) -class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): - ''' +class FileclientCacheTest( + TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin +): + """ Tests for the fileclient caching. The LocalClient is the only thing we can test as it is the only way we can mock the fileclient (the tests run from the minion process, so the master cannot be mocked from test code). - ''' + """ def setup_loader_modules(self): - self.FS_ROOT = os.path.join(RUNTIME_VARS.TMP, 'fileclient_fs_root') - self.CACHE_ROOT = os.path.join(RUNTIME_VARS.TMP, 'fileclient_cache_root') + self.FS_ROOT = os.path.join(RUNTIME_VARS.TMP, "fileclient_fs_root") + self.CACHE_ROOT = os.path.join(RUNTIME_VARS.TMP, "fileclient_cache_root") self.MOCKED_OPTS = { - 'file_roots': _get_file_roots(self.FS_ROOT), - 'fileserver_backend': ['roots'], - 'cachedir': self.CACHE_ROOT, - 'file_client': 'local', + "file_roots": _get_file_roots(self.FS_ROOT), + "fileserver_backend": ["roots"], + "cachedir": self.CACHE_ROOT, + "file_client": "local", } self.addCleanup(shutil.rmtree, self.FS_ROOT, ignore_errors=True) self.addCleanup(shutil.rmtree, self.CACHE_ROOT, ignore_errors=True) - return {fileclient: {'__opts__': self.MOCKED_OPTS}} + return {fileclient: {"__opts__": self.MOCKED_OPTS}} def setUp(self): - ''' + """ No need to add a dummy foo.txt to muddy up the github repo, just make our own fileserver root on-the-fly. - ''' + """ + def _new_dir(path): - ''' + """ Add a new dir at ``path`` using os.makedirs. If the directory already exists, remove it recursively and then try to create it again. - ''' + """ try: os.makedirs(path) except OSError as exc: @@ -173,30 +201,27 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod # Make sure we have a fresh root dir for this saltenv _new_dir(saltenv_root) - path = os.path.join(saltenv_root, 'foo.txt') - with salt.utils.files.fopen(path, 'w') as fp_: - fp_.write( - 'This is a test file in the \'{0}\' saltenv.\n' - .format(saltenv) - ) + path = os.path.join(saltenv_root, "foo.txt") + with salt.utils.files.fopen(path, "w") as fp_: + fp_.write("This is a test file in the '{0}' saltenv.\n".format(saltenv)) subdir_abspath = os.path.join(saltenv_root, SUBDIR) os.makedirs(subdir_abspath) for subdir_file in SUBDIR_FILES: path = os.path.join(subdir_abspath, subdir_file) - with salt.utils.files.fopen(path, 'w') as fp_: + with salt.utils.files.fopen(path, "w") as fp_: fp_.write( - 'This is file \'{0}\' in subdir \'{1} from saltenv ' - '\'{2}\''.format(subdir_file, SUBDIR, saltenv) + "This is file '{0}' in subdir '{1} from saltenv " + "'{2}'".format(subdir_file, SUBDIR, saltenv) ) # Create the CACHE_ROOT _new_dir(self.CACHE_ROOT) def test_cache_dir(self): - ''' + """ Ensure entire directory is cached to correct location - ''' + """ patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) patched_opts.update(self.MOCKED_OPTS) @@ -205,17 +230,17 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod for saltenv in SALTENVS: self.assertTrue( client.cache_dir( - 'salt://{0}'.format(SUBDIR), - saltenv, - cachedir=None + "salt://{0}".format(SUBDIR), saltenv, cachedir=None ) ) for subdir_file in SUBDIR_FILES: - cache_loc = os.path.join(fileclient.__opts__['cachedir'], - 'files', - saltenv, - SUBDIR, - subdir_file) + cache_loc = os.path.join( + fileclient.__opts__["cachedir"], + "files", + saltenv, + SUBDIR, + subdir_file, + ) # Double check that the content of the cached file # identifies it as being from the correct saltenv. The # setUp function creates the file with the name of the @@ -225,37 +250,33 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod # and letting it be raised so that the test fails. with salt.utils.files.fopen(cache_loc) as fp_: content = fp_.read() - log.debug('cache_loc = %s', cache_loc) - log.debug('content = %s', content) + log.debug("cache_loc = %s", cache_loc) + log.debug("content = %s", content) self.assertTrue(subdir_file in content) self.assertTrue(SUBDIR in content) self.assertTrue(saltenv in content) def test_cache_dir_with_alternate_cachedir_and_absolute_path(self): - ''' + """ Ensure entire directory is cached to correct location when an alternate cachedir is specified and that cachedir is an absolute path - ''' + """ patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) patched_opts.update(self.MOCKED_OPTS) - alt_cachedir = os.path.join(RUNTIME_VARS.TMP, 'abs_cachedir') + alt_cachedir = os.path.join(RUNTIME_VARS.TMP, "abs_cachedir") with patch.dict(fileclient.__opts__, patched_opts): client = fileclient.get_file_client(fileclient.__opts__, pillar=False) for saltenv in SALTENVS: self.assertTrue( client.cache_dir( - 'salt://{0}'.format(SUBDIR), - saltenv, - cachedir=alt_cachedir + "salt://{0}".format(SUBDIR), saltenv, cachedir=alt_cachedir ) ) for subdir_file in SUBDIR_FILES: - cache_loc = os.path.join(alt_cachedir, - 'files', - saltenv, - SUBDIR, - subdir_file) + cache_loc = os.path.join( + alt_cachedir, "files", saltenv, SUBDIR, subdir_file + ) # Double check that the content of the cached file # identifies it as being from the correct saltenv. The # setUp function creates the file with the name of the @@ -265,38 +286,38 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod # and letting it be raised so that the test fails. with salt.utils.files.fopen(cache_loc) as fp_: content = fp_.read() - log.debug('cache_loc = %s', cache_loc) - log.debug('content = %s', content) + log.debug("cache_loc = %s", cache_loc) + log.debug("content = %s", content) self.assertTrue(subdir_file in content) self.assertTrue(SUBDIR in content) self.assertTrue(saltenv in content) def test_cache_dir_with_alternate_cachedir_and_relative_path(self): - ''' + """ Ensure entire directory is cached to correct location when an alternate cachedir is specified and that cachedir is a relative path - ''' + """ patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) patched_opts.update(self.MOCKED_OPTS) - alt_cachedir = 'foo' + alt_cachedir = "foo" with patch.dict(fileclient.__opts__, patched_opts): client = fileclient.get_file_client(fileclient.__opts__, pillar=False) for saltenv in SALTENVS: self.assertTrue( client.cache_dir( - 'salt://{0}'.format(SUBDIR), - saltenv, - cachedir=alt_cachedir + "salt://{0}".format(SUBDIR), saltenv, cachedir=alt_cachedir ) ) for subdir_file in SUBDIR_FILES: - cache_loc = os.path.join(fileclient.__opts__['cachedir'], - alt_cachedir, - 'files', - saltenv, - SUBDIR, - subdir_file) + cache_loc = os.path.join( + fileclient.__opts__["cachedir"], + alt_cachedir, + "files", + saltenv, + SUBDIR, + subdir_file, + ) # Double check that the content of the cached file # identifies it as being from the correct saltenv. The # setUp function creates the file with the name of the @@ -306,16 +327,16 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod # and letting it be raised so that the test fails. with salt.utils.files.fopen(cache_loc) as fp_: content = fp_.read() - log.debug('cache_loc = %s', cache_loc) - log.debug('content = %s', content) + log.debug("cache_loc = %s", cache_loc) + log.debug("content = %s", content) self.assertTrue(subdir_file in content) self.assertTrue(SUBDIR in content) self.assertTrue(saltenv in content) def test_cache_file(self): - ''' + """ Ensure file is cached to correct location - ''' + """ patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) patched_opts.update(self.MOCKED_OPTS) @@ -323,10 +344,11 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod client = fileclient.get_file_client(fileclient.__opts__, pillar=False) for saltenv in SALTENVS: self.assertTrue( - client.cache_file('salt://foo.txt', saltenv, cachedir=None) + client.cache_file("salt://foo.txt", saltenv, cachedir=None) ) cache_loc = os.path.join( - fileclient.__opts__['cachedir'], 'files', saltenv, 'foo.txt') + fileclient.__opts__["cachedir"], "files", saltenv, "foo.txt" + ) # Double check that the content of the cached file identifies # it as being from the correct saltenv. The setUp function # creates the file with the name of the saltenv mentioned in @@ -336,31 +358,26 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod # that the test fails. with salt.utils.files.fopen(cache_loc) as fp_: content = fp_.read() - log.debug('cache_loc = %s', cache_loc) - log.debug('content = %s', content) + log.debug("cache_loc = %s", cache_loc) + log.debug("content = %s", content) self.assertTrue(saltenv in content) def test_cache_file_with_alternate_cachedir_and_absolute_path(self): - ''' + """ Ensure file is cached to correct location when an alternate cachedir is specified and that cachedir is an absolute path - ''' + """ patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) patched_opts.update(self.MOCKED_OPTS) - alt_cachedir = os.path.join(RUNTIME_VARS.TMP, 'abs_cachedir') + alt_cachedir = os.path.join(RUNTIME_VARS.TMP, "abs_cachedir") with patch.dict(fileclient.__opts__, patched_opts): client = fileclient.get_file_client(fileclient.__opts__, pillar=False) for saltenv in SALTENVS: self.assertTrue( - client.cache_file('salt://foo.txt', - saltenv, - cachedir=alt_cachedir) + client.cache_file("salt://foo.txt", saltenv, cachedir=alt_cachedir) ) - cache_loc = os.path.join(alt_cachedir, - 'files', - saltenv, - 'foo.txt') + cache_loc = os.path.join(alt_cachedir, "files", saltenv, "foo.txt") # Double check that the content of the cached file identifies # it as being from the correct saltenv. The setUp function # creates the file with the name of the saltenv mentioned in @@ -370,32 +387,32 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod # that the test fails. with salt.utils.files.fopen(cache_loc) as fp_: content = fp_.read() - log.debug('cache_loc = %s', cache_loc) - log.debug('content = %s', content) + log.debug("cache_loc = %s", cache_loc) + log.debug("content = %s", content) self.assertTrue(saltenv in content) def test_cache_file_with_alternate_cachedir_and_relative_path(self): - ''' + """ Ensure file is cached to correct location when an alternate cachedir is specified and that cachedir is a relative path - ''' + """ patched_opts = dict((x, y) for x, y in six.iteritems(self.minion_opts)) patched_opts.update(self.MOCKED_OPTS) - alt_cachedir = 'foo' + alt_cachedir = "foo" with patch.dict(fileclient.__opts__, patched_opts): client = fileclient.get_file_client(fileclient.__opts__, pillar=False) for saltenv in SALTENVS: self.assertTrue( - client.cache_file('salt://foo.txt', - saltenv, - cachedir=alt_cachedir) + client.cache_file("salt://foo.txt", saltenv, cachedir=alt_cachedir) + ) + cache_loc = os.path.join( + fileclient.__opts__["cachedir"], + alt_cachedir, + "files", + saltenv, + "foo.txt", ) - cache_loc = os.path.join(fileclient.__opts__['cachedir'], - alt_cachedir, - 'files', - saltenv, - 'foo.txt') # Double check that the content of the cached file identifies # it as being from the correct saltenv. The setUp function # creates the file with the name of the saltenv mentioned in @@ -405,6 +422,6 @@ class FileclientCacheTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderMod # that the test fails. with salt.utils.files.fopen(cache_loc) as fp_: content = fp_.read() - log.debug('cache_loc = %s', cache_loc) - log.debug('content = %s', content) + log.debug("cache_loc = %s", cache_loc) + log.debug("content = %s", content) self.assertTrue(saltenv in content) diff --git a/tests/unit/test_fileserver.py b/tests/unit/test_fileserver.py index d38e22c8e11..51aad6f2246 100644 --- a/tests/unit/test_fileserver.py +++ b/tests/unit/test_fileserver.py @@ -1,30 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Joao Mesquita <jmesquita@sangoma.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +from salt import fileserver + # Import Salt Testing libs from tests.support.unit import TestCase -from salt import fileserver - class MapDiffTestCase(TestCase): def test_diff_with_diffent_keys(self): - ''' + """ Test that different maps are indeed reported different - ''' - map1 = {'file1': 1234} - map2 = {'file2': 1234} + """ + map1 = {"file1": 1234} + map2 = {"file2": 1234} assert fileserver.diff_mtime_map(map1, map2) is True def test_diff_with_diffent_values(self): - ''' + """ Test that different maps are indeed reported different - ''' - map1 = {'file1': 12345} - map2 = {'file1': 1234} + """ + map1 = {"file1": 12345} + map2 = {"file1": 1234} assert fileserver.diff_mtime_map(map1, map2) is True diff --git a/tests/unit/test_loader.py b/tests/unit/test_loader.py index c0877ff8115..ff0b0b338d3 100644 --- a/tests/unit/test_loader.py +++ b/tests/unit/test_loader.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- -''' +""" unit.loader ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Test Salt's loader -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import collections import compileall import copy @@ -20,39 +21,44 @@ import sys import tempfile import textwrap -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.case import ModuleCase -from tests.support.unit import TestCase -from tests.support.mock import patch - # Import Salt libs import salt.config import salt.loader import salt.utils.files import salt.utils.stringutils + # pylint: disable=import-error,no-name-in-module,redefined-builtin from salt.ext import six from salt.ext.six.moves import range +from tests.support.case import ModuleCase +from tests.support.mock import patch + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase + # pylint: enable=no-name-in-module,redefined-builtin log = logging.getLogger(__name__) def remove_bytecode(module_path): - paths = [module_path + 'c'] - if hasattr(imp, 'get_tag'): + paths = [module_path + "c"] + if hasattr(imp, "get_tag"): modname, ext = os.path.splitext(module_path.split(os.sep)[-1]) paths.append( - os.path.join(os.path.dirname(module_path), - '__pycache__', - '{}.{}.pyc'.format(modname, imp.get_tag()))) + os.path.join( + os.path.dirname(module_path), + "__pycache__", + "{}.{}.pyc".format(modname, imp.get_tag()), + ) + ) for path in paths: if os.path.exists(path): os.unlink(path) -loader_template = ''' +loader_template = """ import os from salt.utils.decorators import depends @@ -63,19 +69,20 @@ def loaded(): @depends('non_existantmodulename') def not_loaded(): return True -''' +""" class LazyLoaderTest(TestCase): - ''' + """ Test the loader - ''' - module_name = 'lazyloadertest' + """ + + module_name = "lazyloadertest" @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) if not os.path.isdir(RUNTIME_VARS.TMP): os.makedirs(RUNTIME_VARS.TMP) cls.utils = salt.loader.utils(cls.opts) @@ -86,20 +93,25 @@ class LazyLoaderTest(TestCase): # Setup the module self.module_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.module_dir, ignore_errors=True) - self.module_file = os.path.join(self.module_dir, - '{0}.py'.format(self.module_name)) - with salt.utils.files.fopen(self.module_file, 'w') as fh: + self.module_file = os.path.join( + self.module_dir, "{0}.py".format(self.module_name) + ) + with salt.utils.files.fopen(self.module_file, "w") as fh: fh.write(salt.utils.stringutils.to_str(loader_template)) fh.flush() os.fsync(fh.fileno()) # Invoke the loader - self.loader = salt.loader.LazyLoader([self.module_dir], - copy.deepcopy(self.opts), - pack={'__utils__': self.utils, - '__salt__': self.funcs, - '__proxy__': self.proxy}, - tag='module') + self.loader = salt.loader.LazyLoader( + [self.module_dir], + copy.deepcopy(self.opts), + pack={ + "__utils__": self.utils, + "__salt__": self.funcs, + "__proxy__": self.proxy, + }, + tag="module", + ) def tearDown(self): del self.module_dir @@ -114,41 +126,41 @@ class LazyLoaderTest(TestCase): del cls.proxy def test_depends(self): - ''' + """ Test that the depends decorator works properly - ''' + """ # Make sure depends correctly allowed a function to load. If this # results in a KeyError, the decorator is broken. - self.assertTrue( - inspect.isfunction( - self.loader[self.module_name + '.loaded'] - ) - ) + self.assertTrue(inspect.isfunction(self.loader[self.module_name + ".loaded"])) # Make sure depends correctly kept a function from loading - self.assertTrue(self.module_name + '.not_loaded' not in self.loader) + self.assertTrue(self.module_name + ".not_loaded" not in self.loader) class LazyLoaderVirtualEnabledTest(TestCase): - ''' + """ Test the base loader of salt. - ''' + """ + @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['disable_modules'] = ['pillar'] - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["disable_modules"] = ["pillar"] + cls.opts["grains"] = salt.loader.grains(cls.opts) cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) cls.proxy = salt.loader.proxy(cls.opts) cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) def setUp(self): self.loader = salt.loader.LazyLoader( - salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module'), + salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module"), copy.deepcopy(self.opts), - pack={'__utils__': self.utils, - '__salt__': self.funcs, - '__proxy__': self.proxy}, - tag='module') + pack={ + "__utils__": self.utils, + "__salt__": self.funcs, + "__proxy__": self.proxy, + }, + tag="module", + ) def tearDown(self): del self.loader @@ -161,21 +173,21 @@ class LazyLoaderVirtualEnabledTest(TestCase): del cls.proxy def test_basic(self): - ''' + """ Ensure that it only loads stuff when needed - ''' + """ # make sure it starts empty self.assertEqual(self.loader._dict, {}) # get something, and make sure its a func - self.assertTrue(inspect.isfunction(self.loader['test.ping'])) + self.assertTrue(inspect.isfunction(self.loader["test.ping"])) # make sure we only loaded "test" functions for key, val in six.iteritems(self.loader._dict): - self.assertEqual(key.split('.', 1)[0], 'test') + self.assertEqual(key.split(".", 1)[0], "test") # make sure the depends thing worked (double check of the depends testing, # since the loader does the calling magically - self.assertFalse('test.missing_func' in self.loader._dict) + self.assertFalse("test.missing_func" in self.loader._dict) def test_badkey(self): with self.assertRaises(KeyError): @@ -185,22 +197,22 @@ class LazyLoaderVirtualEnabledTest(TestCase): self.loader[1] # pylint: disable=W0104 def test_disable(self): - self.assertNotIn('pillar.items', self.loader) + self.assertNotIn("pillar.items", self.loader) def test_len_load(self): - ''' + """ Since LazyLoader is a MutableMapping, if someone asks for len() we have to load all - ''' + """ self.assertEqual(self.loader._dict, {}) len(self.loader) # force a load all self.assertNotEqual(self.loader._dict, {}) def test_iter_load(self): - ''' + """ Since LazyLoader is a MutableMapping, if someone asks to iterate we have to load all - ''' + """ self.assertEqual(self.loader._dict, {}) # force a load all for key, func in six.iteritems(self.loader): @@ -208,63 +220,77 @@ class LazyLoaderVirtualEnabledTest(TestCase): self.assertNotEqual(self.loader._dict, {}) def test_context(self): - ''' + """ Make sure context is shared across modules - ''' + """ # make sure it starts empty self.assertEqual(self.loader._dict, {}) # get something, and make sure its a func - func = self.loader['test.ping'] - with patch.dict(func.__globals__['__context__'], {'foo': 'bar'}): - self.assertEqual(self.loader['test.echo'].__globals__['__context__']['foo'], 'bar') - self.assertEqual(self.loader['grains.get'].__globals__['__context__']['foo'], 'bar') + func = self.loader["test.ping"] + with patch.dict(func.__globals__["__context__"], {"foo": "bar"}): + self.assertEqual( + self.loader["test.echo"].__globals__["__context__"]["foo"], "bar" + ) + self.assertEqual( + self.loader["grains.get"].__globals__["__context__"]["foo"], "bar" + ) def test_globals(self): - func_globals = self.loader['test.ping'].__globals__ - self.assertEqual(func_globals['__grains__'], self.opts.get('grains', {})) - self.assertEqual(func_globals['__pillar__'], self.opts.get('pillar', {})) + func_globals = self.loader["test.ping"].__globals__ + self.assertEqual(func_globals["__grains__"], self.opts.get("grains", {})) + self.assertEqual(func_globals["__pillar__"], self.opts.get("pillar", {})) # the opts passed into modules is at least a subset of the whole opts - for key, val in six.iteritems(func_globals['__opts__']): - if key in salt.config.DEFAULT_MASTER_OPTS and key not in salt.config.DEFAULT_MINION_OPTS: + for key, val in six.iteritems(func_globals["__opts__"]): + if ( + key in salt.config.DEFAULT_MASTER_OPTS + and key not in salt.config.DEFAULT_MINION_OPTS + ): # We loaded the minion opts, but somewhere in the code, the master options got pulled in # Let's just not check for equality since the option won't even exist in the loaded # minion options continue - if key not in salt.config.DEFAULT_MASTER_OPTS and key not in salt.config.DEFAULT_MINION_OPTS: + if ( + key not in salt.config.DEFAULT_MASTER_OPTS + and key not in salt.config.DEFAULT_MINION_OPTS + ): # This isn't even a default configuration setting, lets carry on continue self.assertEqual(self.opts[key], val) def test_pack(self): - self.loader.pack['__foo__'] = 'bar' - func_globals = self.loader['test.ping'].__globals__ - self.assertEqual(func_globals['__foo__'], 'bar') + self.loader.pack["__foo__"] = "bar" + func_globals = self.loader["test.ping"].__globals__ + self.assertEqual(func_globals["__foo__"], "bar") def test_virtual(self): - self.assertNotIn('test_virtual.ping', self.loader) + self.assertNotIn("test_virtual.ping", self.loader) class LazyLoaderVirtualDisabledTest(TestCase): - ''' + """ Test the loader of salt without __virtual__ - ''' + """ + @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) cls.proxy = salt.loader.proxy(cls.opts) cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) def setUp(self): self.loader = salt.loader.LazyLoader( - salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module'), + salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module"), copy.deepcopy(self.opts), - tag='module', - pack={'__utils__': self.utils, - '__salt__': self.funcs, - '__proxy__': self.proxy}, - virtual_enable=False) + tag="module", + pack={ + "__utils__": self.utils, + "__salt__": self.funcs, + "__proxy__": self.proxy, + }, + virtual_enable=False, + ) def tearDown(self): del self.loader @@ -277,30 +303,34 @@ class LazyLoaderVirtualDisabledTest(TestCase): del cls.proxy def test_virtual(self): - self.assertTrue(inspect.isfunction(self.loader['test_virtual.ping'])) + self.assertTrue(inspect.isfunction(self.loader["test_virtual.ping"])) class LazyLoaderWhitelistTest(TestCase): - ''' + """ Test the loader of salt with a whitelist - ''' + """ + @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) cls.proxy = salt.loader.proxy(cls.opts) cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) def setUp(self): self.loader = salt.loader.LazyLoader( - salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module'), + salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module"), copy.deepcopy(self.opts), - tag='module', - pack={'__utils__': self.utils, - '__salt__': self.funcs, - '__proxy__': self.proxy}, - whitelist=['test', 'pillar']) + tag="module", + pack={ + "__utils__": self.utils, + "__salt__": self.funcs, + "__proxy__": self.proxy, + }, + whitelist=["test", "pillar"], + ) def tearDown(self): del self.loader @@ -313,16 +343,17 @@ class LazyLoaderWhitelistTest(TestCase): del cls.proxy def test_whitelist(self): - self.assertTrue(inspect.isfunction(self.loader['test.ping'])) - self.assertTrue(inspect.isfunction(self.loader['pillar.get'])) + self.assertTrue(inspect.isfunction(self.loader["test.ping"])) + self.assertTrue(inspect.isfunction(self.loader["pillar.get"])) - self.assertNotIn('grains.get', self.loader) + self.assertNotIn("grains.get", self.loader) class LazyLoaderGrainsBlacklistTest(TestCase): - ''' + """ Test the loader of grains with a blacklist - ''' + """ + def setUp(self): self.opts = salt.config.minion_config(None) @@ -331,27 +362,24 @@ class LazyLoaderGrainsBlacklistTest(TestCase): def test_whitelist(self): opts = copy.deepcopy(self.opts) - opts['grains_blacklist'] = [ - 'master', - 'os*', - 'ipv[46]' - ] + opts["grains_blacklist"] = ["master", "os*", "ipv[46]"] grains = salt.loader.grains(opts) - self.assertNotIn('master', grains) - self.assertNotIn('os', set([g[:2] for g in list(grains)])) - self.assertNotIn('ipv4', grains) - self.assertNotIn('ipv6', grains) + self.assertNotIn("master", grains) + self.assertNotIn("os", set([g[:2] for g in list(grains)])) + self.assertNotIn("ipv4", grains) + self.assertNotIn("ipv6", grains) class LazyLoaderSingleItem(TestCase): - ''' + """ Test loading a single item via the _load() function - ''' + """ + @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) cls.proxy = salt.loader.proxy(cls.opts) cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) @@ -365,30 +393,33 @@ class LazyLoaderSingleItem(TestCase): def setUp(self): self.loader = salt.loader.LazyLoader( - salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module'), + salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module"), copy.deepcopy(self.opts), - pack={'__utils__': self.utils, - '__salt__': self.funcs, - '__proxy__': self.proxy}, - tag='module') + pack={ + "__utils__": self.utils, + "__salt__": self.funcs, + "__proxy__": self.proxy, + }, + tag="module", + ) def tearDown(self): del self.loader def test_single_item_no_dot(self): - ''' + """ Checks that a KeyError is raised when the function key does not contain a '.' - ''' - key = 'testing_no_dot' + """ + key = "testing_no_dot" expected = "The key '{0}' should contain a '.'".format(key) with self.assertRaises(KeyError) as err: - inspect.isfunction(self.loader['testing_no_dot']) + inspect.isfunction(self.loader["testing_no_dot"]) result = err.exception.args[0] assert result == expected, result -module_template = ''' +module_template = """ __load__ = ['test', 'test_alias'] __func_alias__ = dict(test_alias='working_alias') from salt.utils.decorators import depends @@ -409,20 +440,21 @@ def test3(): @depends('non_existantmodulename', fallback_function=test) def test4(): return True -''' +""" class LazyLoaderReloadingTest(TestCase): - ''' + """ Test the loader of salt with changing modules - ''' - module_name = 'loadertest' - module_key = 'loadertest.test' + """ + + module_name = "loadertest" + module_key = "loadertest.test" @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) if not os.path.isdir(RUNTIME_VARS.TMP): os.makedirs(RUNTIME_VARS.TMP) @@ -432,7 +464,7 @@ class LazyLoaderReloadingTest(TestCase): self.count = 0 opts = copy.deepcopy(self.opts) - dirs = salt.loader._module_dirs(opts, 'modules', 'module') + dirs = salt.loader._module_dirs(opts, "modules", "module") dirs.append(self.tmp_dir) self.utils = salt.loader.utils(opts) self.proxy = salt.loader.proxy(opts) @@ -440,13 +472,16 @@ class LazyLoaderReloadingTest(TestCase): self.loader = salt.loader.LazyLoader( dirs, opts, - tag='module', - pack={'__utils__': self.utils, - '__proxy__': self.proxy, - '__salt__': self.minion_mods}) + tag="module", + pack={ + "__utils__": self.utils, + "__proxy__": self.proxy, + "__salt__": self.minion_mods, + }, + ) def tearDown(self): - for attrname in ('tmp_dir', 'utils', 'proxy', 'loader', 'minion_mods', 'utils'): + for attrname in ("tmp_dir", "utils", "proxy", "loader", "minion_mods", "utils"): try: delattr(self, attrname) except AttributeError: @@ -458,7 +493,7 @@ class LazyLoaderReloadingTest(TestCase): def update_module(self): self.count += 1 - with salt.utils.files.fopen(self.module_path, 'wb') as fh: + with salt.utils.files.fopen(self.module_path, "wb") as fh: fh.write( salt.utils.stringutils.to_bytes( module_template.format(count=self.count) @@ -479,21 +514,25 @@ class LazyLoaderReloadingTest(TestCase): @property def module_path(self): - return os.path.join(self.tmp_dir, '{0}.py'.format(self.module_name)) + return os.path.join(self.tmp_dir, "{0}.py".format(self.module_name)) def test_alias(self): - ''' + """ Make sure that you can access alias-d modules - ''' + """ # ensure it doesn't exist self.assertNotIn(self.module_key, self.loader) self.update_module() - self.assertNotIn('{0}.test_alias'.format(self.module_name), self.loader) - self.assertTrue(inspect.isfunction(self.loader['{0}.working_alias'.format(self.module_name)])) + self.assertNotIn("{0}.test_alias".format(self.module_name), self.loader) + self.assertTrue( + inspect.isfunction( + self.loader["{0}.working_alias".format(self.module_name)] + ) + ) def test_clear(self): - self.assertTrue(inspect.isfunction(self.loader['test.ping'])) + self.assertTrue(inspect.isfunction(self.loader["test.ping"])) self.update_module() # write out out custom module self.loader.clear() # clear the loader dict @@ -513,22 +552,22 @@ class LazyLoaderReloadingTest(TestCase): self.assertTrue(inspect.isfunction(self.loader[self.module_key])) def test__load__(self): - ''' + """ If a module specifies __load__ we should only load/expose those modules - ''' + """ self.update_module() # ensure it doesn't exist - self.assertNotIn(self.module_key + '2', self.loader) + self.assertNotIn(self.module_key + "2", self.loader) def test__load__and_depends(self): - ''' + """ If a module specifies __load__ we should only load/expose those modules - ''' + """ self.update_module() # ensure it doesn't exist - self.assertNotIn(self.module_key + '3', self.loader) - self.assertNotIn(self.module_key + '4', self.loader) + self.assertNotIn(self.module_key + "3", self.loader) + self.assertNotIn(self.module_key + "4", self.loader) def test_reload(self): # ensure it doesn't exist @@ -547,32 +586,35 @@ class LazyLoaderReloadingTest(TestCase): self.assertNotIn(self.module_key, self.loader) -virtual_aliases = ('loadertest2', 'loadertest3') -virtual_alias_module_template = ''' +virtual_aliases = ("loadertest2", "loadertest3") +virtual_alias_module_template = """ __virtual_aliases__ = {0} def test(): return True -'''.format(virtual_aliases) +""".format( + virtual_aliases +) class LazyLoaderVirtualAliasTest(TestCase): - ''' + """ Test the loader of salt with changing modules - ''' - module_name = 'loadertest' + """ + + module_name = "loadertest" @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) if not os.path.isdir(RUNTIME_VARS.TMP): os.makedirs(RUNTIME_VARS.TMP) def setUp(self): self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) opts = copy.deepcopy(self.opts) - dirs = salt.loader._module_dirs(opts, 'modules', 'module') + dirs = salt.loader._module_dirs(opts, "modules", "module") dirs.append(self.tmp_dir) self.utils = salt.loader.utils(opts) self.proxy = salt.loader.proxy(opts) @@ -580,10 +622,13 @@ class LazyLoaderVirtualAliasTest(TestCase): self.loader = salt.loader.LazyLoader( dirs, opts, - tag='module', - pack={'__utils__': self.utils, - '__proxy__': self.proxy, - '__salt__': self.minion_mods}) + tag="module", + pack={ + "__utils__": self.utils, + "__proxy__": self.proxy, + "__salt__": self.minion_mods, + }, + ) def tearDown(self): del self.tmp_dir @@ -597,7 +642,7 @@ class LazyLoaderVirtualAliasTest(TestCase): del cls.opts def update_module(self): - with salt.utils.files.fopen(self.module_path, 'wb') as fh: + with salt.utils.files.fopen(self.module_path, "wb") as fh: fh.write(salt.utils.stringutils.to_bytes(virtual_alias_module_template)) fh.flush() os.fsync(fh.fileno()) # flush to disk @@ -610,49 +655,50 @@ class LazyLoaderVirtualAliasTest(TestCase): @property def module_path(self): - return os.path.join(self.tmp_dir, '{0}.py'.format(self.module_name)) + return os.path.join(self.tmp_dir, "{0}.py".format(self.module_name)) def test_virtual_alias(self): - ''' + """ Test the __virtual_alias__ feature - ''' + """ self.update_module() mod_names = [self.module_name] + list(virtual_aliases) for mod_name in mod_names: - func_name = '.'.join((mod_name, 'test')) - log.debug('Running %s (dict attribute)', func_name) + func_name = ".".join((mod_name, "test")) + log.debug("Running %s (dict attribute)", func_name) self.assertTrue(self.loader[func_name]()) - log.debug('Running %s (loader attribute)', func_name) + log.debug("Running %s (loader attribute)", func_name) self.assertTrue(getattr(self.loader, mod_name).test()) -submodule_template = ''' +submodule_template = """ from __future__ import absolute_import import {0}.lib def test(): return ({count}, {0}.lib.test()) -''' +""" -submodule_lib_template = ''' +submodule_lib_template = """ def test(): return {count} -''' +""" class LazyLoaderSubmodReloadingTest(TestCase): - ''' + """ Test the loader of salt with changing modules - ''' - module_name = 'loadertestsubmod' - module_key = 'loadertestsubmod.test' + """ + + module_name = "loadertestsubmod" + module_key = "loadertestsubmod.test" @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) if not os.path.isdir(RUNTIME_VARS.TMP): os.makedirs(RUNTIME_VARS.TMP) @@ -665,7 +711,7 @@ class LazyLoaderSubmodReloadingTest(TestCase): self.lib_count = 0 opts = copy.deepcopy(self.opts) - dirs = salt.loader._module_dirs(opts, 'modules', 'module') + dirs = salt.loader._module_dirs(opts, "modules", "module") dirs.append(self.tmp_dir) self.utils = salt.loader.utils(opts) self.proxy = salt.loader.proxy(opts) @@ -673,10 +719,13 @@ class LazyLoaderSubmodReloadingTest(TestCase): self.loader = salt.loader.LazyLoader( dirs, opts, - tag='module', - pack={'__utils__': self.utils, - '__proxy__': self.proxy, - '__salt__': self.minion_mods}) + tag="module", + pack={ + "__utils__": self.utils, + "__proxy__": self.proxy, + "__salt__": self.minion_mods, + }, + ) def tearDown(self): del self.tmp_dir @@ -691,7 +740,7 @@ class LazyLoaderSubmodReloadingTest(TestCase): def update_module(self): self.count += 1 - with salt.utils.files.fopen(self.module_path, 'wb') as fh: + with salt.utils.files.fopen(self.module_path, "wb") as fh: fh.write( salt.utils.stringutils.to_bytes( submodule_template.format(self.module_name, count=self.count) @@ -715,7 +764,7 @@ class LazyLoaderSubmodReloadingTest(TestCase): for modname in list(sys.modules): if modname.startswith(self.module_name): del sys.modules[modname] - with salt.utils.files.fopen(self.lib_path, 'wb') as fh: + with salt.utils.files.fopen(self.lib_path, "wb") as fh: fh.write( salt.utils.stringutils.to_bytes( submodule_lib_template.format(count=self.lib_count) @@ -743,11 +792,11 @@ class LazyLoaderSubmodReloadingTest(TestCase): @property def module_path(self): - return os.path.join(self.module_dir, '__init__.py') + return os.path.join(self.module_dir, "__init__.py") @property def lib_path(self): - return os.path.join(self.module_dir, 'lib.py') + return os.path.join(self.module_dir, "lib.py") def test_basic(self): # ensure it doesn't exist @@ -769,7 +818,9 @@ class LazyLoaderSubmodReloadingTest(TestCase): self.loader.clear() self.assertNotIn(self.module_key, self.loader._dict) self.assertIn(self.module_key, self.loader) - self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count)) + self.assertEqual( + self.loader[self.module_key](), (self.count, self.lib_count) + ) # update just the module for x in range(1, 3): @@ -777,7 +828,9 @@ class LazyLoaderSubmodReloadingTest(TestCase): self.loader.clear() self.assertNotIn(self.module_key, self.loader._dict) self.assertIn(self.module_key, self.loader) - self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count)) + self.assertEqual( + self.loader[self.module_key](), (self.count, self.lib_count) + ) # update just the lib for x in range(1, 3): @@ -785,7 +838,9 @@ class LazyLoaderSubmodReloadingTest(TestCase): self.loader.clear() self.assertNotIn(self.module_key, self.loader._dict) self.assertIn(self.module_key, self.loader) - self.assertEqual(self.loader[self.module_key](), (self.count, self.lib_count)) + self.assertEqual( + self.loader[self.module_key](), (self.count, self.lib_count) + ) self.rm_module() # make sure that even if we remove the module, its still loaded until a clear @@ -809,23 +864,24 @@ class LazyLoaderSubmodReloadingTest(TestCase): self.assertNotIn(self.module_key, self.loader) -mod_template = ''' +mod_template = """ def test(): return ({val}) -''' +""" class LazyLoaderModulePackageTest(TestCase): - ''' + """ Test the loader of salt with changing modules - ''' - module_name = 'loadertestmodpkg' - module_key = 'loadertestmodpkg.test' + """ + + module_name = "loadertestmodpkg" + module_key = "loadertestmodpkg.test" @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) if not os.path.isdir(RUNTIME_VARS.TMP): os.makedirs(RUNTIME_VARS.TMP) cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) @@ -836,15 +892,18 @@ class LazyLoaderModulePackageTest(TestCase): self.tmp_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.tmp_dir, ignore_errors=True) - dirs = salt.loader._module_dirs(copy.deepcopy(self.opts), 'modules', 'module') + dirs = salt.loader._module_dirs(copy.deepcopy(self.opts), "modules", "module") dirs.append(self.tmp_dir) self.loader = salt.loader.LazyLoader( dirs, copy.deepcopy(self.opts), - pack={'__utils__': self.utils, - '__salt__': self.funcs, - '__proxy__': self.proxy}, - tag='module') + pack={ + "__utils__": self.utils, + "__salt__": self.funcs, + "__proxy__": self.proxy, + }, + tag="module", + ) def tearDown(self): del self.tmp_dir @@ -861,7 +920,7 @@ class LazyLoaderModulePackageTest(TestCase): dirname = os.path.dirname(pyfile) if not os.path.exists(dirname): os.makedirs(dirname) - with salt.utils.files.fopen(pyfile, 'wb') as fh: + with salt.utils.files.fopen(pyfile, "wb") as fh: fh.write(salt.utils.stringutils.to_bytes(contents)) fh.flush() os.fsync(fh.fileno()) # flush to disk @@ -884,38 +943,38 @@ class LazyLoaderModulePackageTest(TestCase): def test_module(self): # ensure it doesn't exist - self.assertNotIn('foo', self.loader) - self.assertNotIn('foo.test', self.loader) - self.update_module('foo.py', mod_template.format(val=1)) + self.assertNotIn("foo", self.loader) + self.assertNotIn("foo.test", self.loader) + self.update_module("foo.py", mod_template.format(val=1)) self.loader.clear() - self.assertIn('foo.test', self.loader) - self.assertEqual(self.loader['foo.test'](), 1) + self.assertIn("foo.test", self.loader) + self.assertEqual(self.loader["foo.test"](), 1) def test_package(self): # ensure it doesn't exist - self.assertNotIn('foo', self.loader) - self.assertNotIn('foo.test', self.loader) - self.update_module('foo/__init__.py', mod_template.format(val=2)) + self.assertNotIn("foo", self.loader) + self.assertNotIn("foo.test", self.loader) + self.update_module("foo/__init__.py", mod_template.format(val=2)) self.loader.clear() - self.assertIn('foo.test', self.loader) - self.assertEqual(self.loader['foo.test'](), 2) + self.assertIn("foo.test", self.loader) + self.assertEqual(self.loader["foo.test"](), 2) def test_module_package_collision(self): # ensure it doesn't exist - self.assertNotIn('foo', self.loader) - self.assertNotIn('foo.test', self.loader) - self.update_module('foo.py', mod_template.format(val=3)) + self.assertNotIn("foo", self.loader) + self.assertNotIn("foo.test", self.loader) + self.update_module("foo.py", mod_template.format(val=3)) self.loader.clear() - self.assertIn('foo.test', self.loader) - self.assertEqual(self.loader['foo.test'](), 3) + self.assertIn("foo.test", self.loader) + self.assertEqual(self.loader["foo.test"](), 3) - self.update_module('foo/__init__.py', mod_template.format(val=4)) + self.update_module("foo/__init__.py", mod_template.format(val=4)) self.loader.clear() - self.assertIn('foo.test', self.loader) - self.assertEqual(self.loader['foo.test'](), 4) + self.assertIn("foo.test", self.loader) + self.assertEqual(self.loader["foo.test"](), 4) -deep_init_base = ''' +deep_init_base = """ from __future__ import absolute_import import {0}.top_lib import {0}.top_lib.mid_lib @@ -929,17 +988,17 @@ def mid(): def bot(): return {0}.top_lib.mid_lib.bot_lib.test() -''' +""" class LazyLoaderDeepSubmodReloadingTest(TestCase): - module_name = 'loadertestsubmoddeep' - libs = ('top_lib', 'mid_lib', 'bot_lib') + module_name = "loadertestsubmoddeep" + libs = ("top_lib", "mid_lib", "bot_lib") @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) if not os.path.isdir(RUNTIME_VARS.TMP): os.makedirs(RUNTIME_VARS.TMP) @@ -951,13 +1010,13 @@ class LazyLoaderDeepSubmodReloadingTest(TestCase): self.lib_count = collections.defaultdict(int) # mapping of path -> count # bootstrap libs - with salt.utils.files.fopen(os.path.join(self.module_dir, '__init__.py'), 'w') as fh: + with salt.utils.files.fopen( + os.path.join(self.module_dir, "__init__.py"), "w" + ) as fh: # No .decode() needed here as deep_init_base is defined as str and # not bytes. fh.write( - salt.utils.stringutils.to_str( - deep_init_base.format(self.module_name) - ) + salt.utils.stringutils.to_str(deep_init_base.format(self.module_name)) ) fh.flush() os.fsync(fh.fileno()) # flush to disk @@ -971,7 +1030,7 @@ class LazyLoaderDeepSubmodReloadingTest(TestCase): self.update_lib(lib_name) opts = copy.deepcopy(self.opts) - dirs = salt.loader._module_dirs(opts, 'modules', 'module') + dirs = salt.loader._module_dirs(opts, "modules", "module") dirs.append(self.tmp_dir) self.utils = salt.loader.utils(opts) self.proxy = salt.loader.proxy(opts) @@ -979,11 +1038,14 @@ class LazyLoaderDeepSubmodReloadingTest(TestCase): self.loader = salt.loader.LazyLoader( dirs, copy.deepcopy(opts), - tag='module', - pack={'__utils__': self.utils, - '__proxy__': self.proxy, - '__salt__': self.minion_mods}) - self.assertIn('{0}.top'.format(self.module_name), self.loader) + tag="module", + pack={ + "__utils__": self.utils, + "__proxy__": self.proxy, + "__salt__": self.minion_mods, + }, + ) + self.assertIn("{0}.top".format(self.module_name), self.loader) def tearDown(self): del self.tmp_dir @@ -1006,9 +1068,9 @@ class LazyLoaderDeepSubmodReloadingTest(TestCase): for modname in list(sys.modules): if modname.startswith(self.module_name): del sys.modules[modname] - path = os.path.join(self.lib_paths[lib_name], '__init__.py') + path = os.path.join(self.lib_paths[lib_name], "__init__.py") self.lib_count[lib_name] += 1 - with salt.utils.files.fopen(path, 'wb') as fh: + with salt.utils.files.fopen(path, "wb") as fh: fh.write( salt.utils.stringutils.to_bytes( submodule_lib_template.format(count=self.lib_count[lib_name]) @@ -1024,17 +1086,21 @@ class LazyLoaderDeepSubmodReloadingTest(TestCase): remove_bytecode(path) def test_basic(self): - self.assertIn('{0}.top'.format(self.module_name), self.loader) + self.assertIn("{0}.top".format(self.module_name), self.loader) def _verify_libs(self): for lib in self.libs: - self.assertEqual(self.loader['{0}.{1}'.format(self.module_name, lib.replace('_lib', ''))](), - self.lib_count[lib]) + self.assertEqual( + self.loader[ + "{0}.{1}".format(self.module_name, lib.replace("_lib", "")) + ](), + self.lib_count[lib], + ) def test_reload(self): - ''' + """ Make sure that we can reload all libraries of arbitrary depth - ''' + """ self._verify_libs() # update them all @@ -1046,35 +1112,38 @@ class LazyLoaderDeepSubmodReloadingTest(TestCase): class LoaderGlobalsTest(ModuleCase): - ''' + """ Test all of the globals that the loader is responsible for adding to modules This shouldn't be done here, but should rather be done per module type (in the cases where they are used) so they can check ALL globals that they have (or should have) access to. This is intended as a shorter term way of testing these so we don't break the loader - ''' + """ + def _verify_globals(self, mod_dict): - ''' + """ Verify that the globals listed in the doc string (from the test) are in these modules - ''' + """ # find the globals global_vars = [] for val in six.itervalues(mod_dict): # only find salty globals - if val.__module__.startswith('salt.loaded'): - if hasattr(val, '__globals__'): - if hasattr(val, '__wrapped__') or '__wrapped__' in val.__globals__: + if val.__module__.startswith("salt.loaded"): + if hasattr(val, "__globals__"): + if hasattr(val, "__wrapped__") or "__wrapped__" in val.__globals__: global_vars.append(sys.modules[val.__module__].__dict__) else: global_vars.append(val.__globals__) # if we couldn't find any, then we have no modules -- so something is broken - self.assertNotEqual(global_vars, [], msg='No modules were loaded.') + self.assertNotEqual(global_vars, [], msg="No modules were loaded.") # get the names of the globals you should have func_name = inspect.stack()[1][3] - names = next(six.itervalues(salt.utils.yaml.safe_load(getattr(self, func_name).__doc__))) + names = next( + six.itervalues(salt.utils.yaml.safe_load(getattr(self, func_name).__doc__)) + ) # Now, test each module! for item in global_vars: @@ -1082,102 +1151,103 @@ class LoaderGlobalsTest(ModuleCase): self.assertIn(name, list(item.keys())) def test_auth(self): - ''' + """ Test that auth mods have: - __pillar__ - __grains__ - __salt__ - __context__ - ''' + """ self._verify_globals(salt.loader.auth(self.master_opts)) def test_runners(self): - ''' + """ Test that runners have: - __pillar__ - __salt__ - __opts__ - __grains__ - __context__ - ''' + """ self._verify_globals(salt.loader.runner(self.master_opts)) def test_returners(self): - ''' + """ Test that returners have: - __salt__ - __opts__ - __pillar__ - __grains__ - __context__ - ''' + """ self._verify_globals(salt.loader.returners(self.master_opts, {})) def test_pillars(self): - ''' + """ Test that pillars have: - __salt__ - __opts__ - __pillar__ - __grains__ - __context__ - ''' + """ self._verify_globals(salt.loader.pillars(self.master_opts, {})) def test_tops(self): - ''' + """ Test that tops have: [] - ''' + """ self._verify_globals(salt.loader.tops(self.master_opts)) def test_outputters(self): - ''' + """ Test that outputters have: - __opts__ - __pillar__ - __grains__ - __context__ - ''' + """ self._verify_globals(salt.loader.outputters(self.master_opts)) def test_serializers(self): - ''' + """ Test that serializers have: [] - ''' + """ self._verify_globals(salt.loader.serializers(self.master_opts)) def test_states(self): - ''' + """ Test that states have: - __pillar__ - __salt__ - __opts__ - __grains__ - __context__ - ''' + """ opts = salt.config.minion_config(None) - opts['grains'] = salt.loader.grains(opts) + opts["grains"] = salt.loader.grains(opts) utils = salt.loader.utils(opts) proxy = salt.loader.proxy(opts) funcs = salt.loader.minion_mods(opts, utils=utils, proxy=proxy) self._verify_globals(salt.loader.states(opts, funcs, utils, {}, proxy=proxy)) def test_renderers(self): - ''' + """ Test that renderers have: - __salt__ # Execution functions (i.e. __salt__['test.echo']('foo')) - __grains__ # Grains (i.e. __grains__['os']) - __pillar__ # Pillar data (i.e. __pillar__['foo']) - __opts__ # Minion configuration options - __context__ # Context dict shared amongst all modules of the same type - ''' + """ self._verify_globals(salt.loader.render(self.master_opts, {})) class RawModTest(TestCase): - ''' + """ Test the interface of raw_mod - ''' + """ + def setUp(self): self.opts = salt.config.minion_config(None) @@ -1185,42 +1255,45 @@ class RawModTest(TestCase): del self.opts def test_basic(self): - testmod = salt.loader.raw_mod(self.opts, 'test', None) + testmod = salt.loader.raw_mod(self.opts, "test", None) for k, v in six.iteritems(testmod): - self.assertEqual(k.split('.')[0], 'test') + self.assertEqual(k.split(".")[0], "test") def test_bad_name(self): - testmod = salt.loader.raw_mod(self.opts, 'module_we_do_not_have', None) + testmod = salt.loader.raw_mod(self.opts, "module_we_do_not_have", None) self.assertEqual(testmod, {}) class NetworkUtilsTestCase(ModuleCase): def test_is_private(self): - mod = salt.loader.raw_mod(self.minion_opts, 'network', None) - self.assertTrue(mod['network.is_private']('10.0.0.1'), True) + mod = salt.loader.raw_mod(self.minion_opts, "network", None) + self.assertTrue(mod["network.is_private"]("10.0.0.1"), True) def test_is_loopback(self): - mod = salt.loader.raw_mod(self.minion_opts, 'network', None) - self.assertTrue(mod['network.is_loopback']('127.0.0.1'), True) + mod = salt.loader.raw_mod(self.minion_opts, "network", None) + self.assertTrue(mod["network.is_loopback"]("127.0.0.1"), True) class LazyLoaderOptimizationOrderTest(TestCase): - ''' + """ Test the optimization order priority in the loader (PY3) - ''' - module_name = 'lazyloadertest' - module_content = textwrap.dedent('''\ + """ + + module_name = "lazyloadertest" + module_content = textwrap.dedent( + """\ # -*- coding: utf-8 -*- from __future__ import absolute_import def test(): return True - ''') + """ + ) @classmethod def setUpClass(cls): cls.opts = salt.config.minion_config(None) - cls.opts['grains'] = salt.loader.grains(cls.opts) + cls.opts["grains"] = salt.loader.grains(cls.opts) cls.utils = salt.loader.utils(copy.deepcopy(cls.opts)) cls.proxy = salt.loader.proxy(cls.opts) cls.funcs = salt.loader.minion_mods(cls.opts, utils=cls.utils, proxy=cls.proxy) @@ -1236,27 +1309,31 @@ class LazyLoaderOptimizationOrderTest(TestCase): # Setup the module self.module_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.module_dir, ignore_errors=True) - self.module_file = os.path.join(self.module_dir, - '{0}.py'.format(self.module_name)) + self.module_file = os.path.join( + self.module_dir, "{0}.py".format(self.module_name) + ) def tearDown(self): try: - delattr(self, 'loader') + delattr(self, "loader") except AttributeError: pass def _get_loader(self, order=None): opts = copy.deepcopy(self.opts) if order is not None: - opts['optimization_order'] = order + opts["optimization_order"] = order # Return a loader return salt.loader.LazyLoader( [self.module_dir], opts, - pack={'__utils__': self.utils, - '__salt__': self.funcs, - '__proxy__': self.proxy}, - tag='module') + pack={ + "__utils__": self.utils, + "__salt__": self.funcs, + "__proxy__": self.proxy, + }, + tag="module", + ) def _get_module_filename(self): # The act of referencing the loader entry forces the module to be @@ -1266,16 +1343,16 @@ class LazyLoaderOptimizationOrderTest(TestCase): def _expected(self, optimize=0): if six.PY3: - return 'lazyloadertest.cpython-{0}{1}{2}.pyc'.format( + return "lazyloadertest.cpython-{0}{1}{2}.pyc".format( sys.version_info[0], sys.version_info[1], - '' if not optimize else '.opt-{0}'.format(optimize) + "" if not optimize else ".opt-{0}".format(optimize), ) else: - return 'lazyloadertest.pyc' + return "lazyloadertest.pyc" def _write_module_file(self): - with salt.utils.files.fopen(self.module_file, 'w') as fh: + with salt.utils.files.fopen(self.module_file, "w") as fh: fh.write(self.module_content) fh.flush() os.fsync(fh.fileno()) @@ -1329,9 +1406,9 @@ class LazyLoaderOptimizationOrderTest(TestCase): assert basename == self._expected(order[2]), basename def test_optimization_order(self): - ''' + """ Test the optimization_order config param - ''' + """ self._test_optimization_order([0, 1, 2]) self._test_optimization_order([0, 2, 1]) if salt.loader.USE_IMPORTLIB: @@ -1343,22 +1420,22 @@ class LazyLoaderOptimizationOrderTest(TestCase): self._test_optimization_order([2, 1, 0]) def test_load_source_file(self): - ''' + """ Make sure that .py files are preferred over .pyc files - ''' + """ self._write_module_file() self._byte_compile() self.loader = self._get_loader() filename = self._get_module_filename() basename = os.path.basename(filename) - expected = 'lazyloadertest.py' if six.PY3 else 'lazyloadertest.pyc' + expected = "lazyloadertest.py" if six.PY3 else "lazyloadertest.pyc" assert basename == expected, basename class LoaderLoadCachedGrainsTest(TestCase): - ''' + """ Test how the loader works with cached grains - ''' + """ @classmethod def setUpClass(cls): @@ -1370,14 +1447,14 @@ class LoaderLoadCachedGrainsTest(TestCase): self.cache_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.cache_dir, ignore_errors=True) - self.opts['cachedir'] = self.cache_dir - self.opts['grains_cache'] = True - self.opts['grains'] = salt.loader.grains(self.opts) + self.opts["cachedir"] = self.cache_dir + self.opts["grains_cache"] = True + self.opts["grains"] = salt.loader.grains(self.opts) def test_osrelease_info_has_correct_type(self): - ''' + """ Make sure osrelease_info is tuple after caching - ''' + """ grains = salt.loader.grains(self.opts) - osrelease_info = grains['osrelease_info'] + osrelease_info = grains["osrelease_info"] assert isinstance(osrelease_info, tuple), osrelease_info diff --git a/tests/unit/test_log.py b/tests/unit/test_log.py index b6651a1d244..5432fa62777 100644 --- a/tests/unit/test_log.py +++ b/tests/unit/test_log.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,26 +7,28 @@ ~~~~~~~~~~~~~~~~~~~ Test salt's "hacked" logging -''' +""" # Import python libs from __future__ import absolute_import + import logging + +from salt._logging.handlers import StreamHandler + +# Import Salt libs +from salt._logging.impl import SaltLoggingClass from salt.ext.six.moves import StringIO # Import Salt Testing libs from tests.support.case import TestCase from tests.support.helpers import TstSuiteLoggingHandler -# Import Salt libs -from salt._logging.impl import SaltLoggingClass -from salt._logging.handlers import StreamHandler - class TestLog(TestCase): - ''' + """ Test several logging settings - ''' + """ def test_issue_2853_regex_TypeError(self): # Now, python's logging logger class is ours. @@ -34,7 +36,7 @@ class TestLog(TestCase): log = SaltLoggingClass(__name__) # Test for a format which includes digits in name formatting. - log_format = '[%(name)-15s] %(message)s' + log_format = "[%(name)-15s] %(message)s" handler = TstSuiteLoggingHandler(format=log_format) log.addHandler(handler) @@ -43,17 +45,17 @@ class TestLog(TestCase): # Let's create another log instance to trigger salt's logging class # calculations. try: - SaltLoggingClass('{0}.with_digits'.format(__name__)) + SaltLoggingClass("{0}.with_digits".format(__name__)) except Exception as err: # pylint: disable=broad-except raise AssertionError( - 'No exception should have been raised: {0}'.format(err) + "No exception should have been raised: {0}".format(err) ) # Remove the testing handler log.removeHandler(handler) # Test for a format which does not include digits in name formatting. - log_format = '[%(name)s] %(message)s' + log_format = "[%(name)s] %(message)s" handler = TstSuiteLoggingHandler(format=log_format) log.addHandler(handler) @@ -62,10 +64,10 @@ class TestLog(TestCase): # Let's create another log instance to trigger salt's logging class # calculations. try: - SaltLoggingClass('{0}.without_digits'.format(__name__)) + SaltLoggingClass("{0}.without_digits".format(__name__)) except Exception as err: # pylint: disable=broad-except raise AssertionError( - 'No exception should have been raised: {0}'.format(err) + "No exception should have been raised: {0}".format(err) ) # Remove the testing handler @@ -73,7 +75,7 @@ class TestLog(TestCase): def test_exc_info_on_loglevel(self): def raise_exception_on_purpose(): - 1/0 # pylint: disable=pointless-statement + 1 / 0 # pylint: disable=pointless-statement log = SaltLoggingClass(__name__) @@ -92,20 +94,22 @@ class TestLog(TestCase): try: raise_exception_on_purpose() except ZeroDivisionError as exc: - log.error('Exception raised on purpose caught: ZeroDivisionError', - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Exception raised on purpose caught: ZeroDivisionError", + exc_info_on_loglevel=logging.DEBUG, + ) try: self.assertIn( - 'Exception raised on purpose caught: ZeroDivisionError', - stream1.getvalue() + "Exception raised on purpose caught: ZeroDivisionError", + stream1.getvalue(), ) - self.assertNotIn('Traceback (most recent call last)', stream1.getvalue()) + self.assertNotIn("Traceback (most recent call last)", stream1.getvalue()) self.assertIn( - 'Exception raised on purpose caught: ZeroDivisionError', - stream2.getvalue() + "Exception raised on purpose caught: ZeroDivisionError", + stream2.getvalue(), ) - self.assertIn('Traceback (most recent call last)', stream2.getvalue()) + self.assertIn("Traceback (most recent call last)", stream2.getvalue()) finally: log.removeHandler(handler1) log.removeHandler(handler2) @@ -125,20 +129,22 @@ class TestLog(TestCase): try: raise_exception_on_purpose() except ZeroDivisionError as exc: - log.error('Exception raised on purpose caught: ZeroDivisionError', - exc_info_on_loglevel=logging.INFO) + log.error( + "Exception raised on purpose caught: ZeroDivisionError", + exc_info_on_loglevel=logging.INFO, + ) try: self.assertIn( - 'Exception raised on purpose caught: ZeroDivisionError', - stream1.getvalue() + "Exception raised on purpose caught: ZeroDivisionError", + stream1.getvalue(), ) - self.assertIn('Traceback (most recent call last)', stream1.getvalue()) + self.assertIn("Traceback (most recent call last)", stream1.getvalue()) self.assertIn( - 'Exception raised on purpose caught: ZeroDivisionError', - stream2.getvalue() + "Exception raised on purpose caught: ZeroDivisionError", + stream2.getvalue(), ) - self.assertIn('Traceback (most recent call last)', stream2.getvalue()) + self.assertIn("Traceback (most recent call last)", stream2.getvalue()) finally: log.removeHandler(handler1) log.removeHandler(handler2) @@ -158,20 +164,22 @@ class TestLog(TestCase): try: raise_exception_on_purpose() except ZeroDivisionError as exc: - log.error('Exception raised on purpose caught: ZeroDivisionError', - exc_info_on_loglevel=logging.DEBUG) + log.error( + "Exception raised on purpose caught: ZeroDivisionError", + exc_info_on_loglevel=logging.DEBUG, + ) try: self.assertIn( - 'Exception raised on purpose caught: ZeroDivisionError', - stream1.getvalue() + "Exception raised on purpose caught: ZeroDivisionError", + stream1.getvalue(), ) - self.assertNotIn('Traceback (most recent call last)', stream1.getvalue()) + self.assertNotIn("Traceback (most recent call last)", stream1.getvalue()) self.assertIn( - 'Exception raised on purpose caught: ZeroDivisionError', - stream2.getvalue() + "Exception raised on purpose caught: ZeroDivisionError", + stream2.getvalue(), ) - self.assertNotIn('Traceback (most recent call last)', stream2.getvalue()) + self.assertNotIn("Traceback (most recent call last)", stream2.getvalue()) finally: log.removeHandler(handler1) log.removeHandler(handler2) diff --git a/tests/unit/test_master.py b/tests/unit/test_master.py index b7394ffa01d..b36abe1e0da 100644 --- a/tests/unit/test_master.py +++ b/tests/unit/test_master.py @@ -6,19 +6,16 @@ from __future__ import absolute_import # Import Salt libs import salt.config import salt.master +from tests.support.mock import MagicMock, patch # Import Salt Testing Libs from tests.support.unit import TestCase -from tests.support.mock import ( - patch, - MagicMock, -) class ClearFuncsTestCase(TestCase): - ''' + """ TestCase for salt.master.ClearFuncs class - ''' + """ def setUp(self): opts = salt.config.master_config(None) @@ -27,307 +24,485 @@ class ClearFuncsTestCase(TestCase): # runner tests def test_runner_token_not_authenticated(self): - ''' + """ Asserts that a TokenAuthenticationError is returned when the token can't authenticate. - ''' - mock_ret = {'error': {'name': 'TokenAuthenticationError', - 'message': 'Authentication failure of type "token" occurred.'}} - ret = self.clear_funcs.runner({'token': 'asdfasdfasdfasdf'}) + """ + mock_ret = { + "error": { + "name": "TokenAuthenticationError", + "message": 'Authentication failure of type "token" occurred.', + } + } + ret = self.clear_funcs.runner({"token": "asdfasdfasdfasdf"}) self.assertDictEqual(mock_ret, ret) def test_runner_token_authorization_error(self): - ''' + """ Asserts that a TokenAuthenticationError is returned when the token authenticates, but is not authorized. - ''' - token = 'asdfasdfasdfasdf' - clear_load = {'token': token, 'fun': 'test.arg'} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'TokenAuthenticationError', - 'message': 'Authentication failure of type "token" occurred ' - 'for user test.'}} + """ + token = "asdfasdfasdfasdf" + clear_load = {"token": token, "fun": "test.arg"} + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "TokenAuthenticationError", + "message": 'Authentication failure of type "token" occurred ' + "for user test.", + } + } - with patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + with patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])): ret = self.clear_funcs.runner(clear_load) self.assertDictEqual(mock_ret, ret) def test_runner_token_salt_invocation_error(self): - ''' + """ Asserts that a SaltInvocationError is returned when the token authenticates, but the command is malformed. - ''' - token = 'asdfasdfasdfasdf' - clear_load = {'token': token, 'fun': 'badtestarg'} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} + """ + token = "asdfasdfasdfasdf" + clear_load = {"token": token, "fun": "badtestarg"} + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } - with patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=['testing'])): + with patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"]) + ): ret = self.clear_funcs.runner(clear_load) self.assertDictEqual(mock_ret, ret) def test_runner_eauth_not_authenticated(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user can't authenticate. - ''' - mock_ret = {'error': {'name': 'EauthAuthenticationError', - 'message': 'Authentication failure of type "eauth" occurred for ' - 'user UNKNOWN.'}} - ret = self.clear_funcs.runner({'eauth': 'foo'}) + """ + mock_ret = { + "error": { + "name": "EauthAuthenticationError", + "message": 'Authentication failure of type "eauth" occurred for ' + "user UNKNOWN.", + } + } + ret = self.clear_funcs.runner({"eauth": "foo"}) self.assertDictEqual(mock_ret, ret) def test_runner_eauth_authorization_error(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user authenticates, but is not authorized. - ''' - clear_load = {'eauth': 'foo', 'username': 'test', 'fun': 'test.arg'} - mock_ret = {'error': {'name': 'EauthAuthenticationError', - 'message': 'Authentication failure of type "eauth" occurred for ' - 'user test.'}} - with patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + """ + clear_load = {"eauth": "foo", "username": "test", "fun": "test.arg"} + mock_ret = { + "error": { + "name": "EauthAuthenticationError", + "message": 'Authentication failure of type "eauth" occurred for ' + "user test.", + } + } + with patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])): ret = self.clear_funcs.runner(clear_load) self.assertDictEqual(mock_ret, ret) def test_runner_eauth_salt_invocation_error(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user authenticates, but the command is malformed. - ''' - clear_load = {'eauth': 'foo', 'username': 'test', 'fun': 'bad.test.arg.func'} - mock_ret = {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} - with patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=['testing'])): + """ + clear_load = {"eauth": "foo", "username": "test", "fun": "bad.test.arg.func"} + mock_ret = { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } + with patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"]) + ): ret = self.clear_funcs.runner(clear_load) self.assertDictEqual(mock_ret, ret) def test_runner_user_not_authenticated(self): - ''' + """ Asserts that an UserAuthenticationError is returned when the user can't authenticate. - ''' - mock_ret = {'error': {'name': 'UserAuthenticationError', - 'message': 'Authentication failure of type "user" occurred'}} + """ + mock_ret = { + "error": { + "name": "UserAuthenticationError", + "message": 'Authentication failure of type "user" occurred', + } + } ret = self.clear_funcs.runner({}) self.assertDictEqual(mock_ret, ret) # wheel tests def test_wheel_token_not_authenticated(self): - ''' + """ Asserts that a TokenAuthenticationError is returned when the token can't authenticate. - ''' - mock_ret = {'error': {'name': 'TokenAuthenticationError', - 'message': 'Authentication failure of type "token" occurred.'}} - ret = self.clear_funcs.wheel({'token': 'asdfasdfasdfasdf'}) + """ + mock_ret = { + "error": { + "name": "TokenAuthenticationError", + "message": 'Authentication failure of type "token" occurred.', + } + } + ret = self.clear_funcs.wheel({"token": "asdfasdfasdfasdf"}) self.assertDictEqual(mock_ret, ret) def test_wheel_token_authorization_error(self): - ''' + """ Asserts that a TokenAuthenticationError is returned when the token authenticates, but is not authorized. - ''' - token = 'asdfasdfasdfasdf' - clear_load = {'token': token, 'fun': 'test.arg'} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'TokenAuthenticationError', - 'message': 'Authentication failure of type "token" occurred ' - 'for user test.'}} + """ + token = "asdfasdfasdfasdf" + clear_load = {"token": token, "fun": "test.arg"} + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "TokenAuthenticationError", + "message": 'Authentication failure of type "token" occurred ' + "for user test.", + } + } - with patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + with patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])): ret = self.clear_funcs.wheel(clear_load) self.assertDictEqual(mock_ret, ret) def test_wheel_token_salt_invocation_error(self): - ''' + """ Asserts that a SaltInvocationError is returned when the token authenticates, but the command is malformed. - ''' - token = 'asdfasdfasdfasdf' - clear_load = {'token': token, 'fun': 'badtestarg'} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} + """ + token = "asdfasdfasdfasdf" + clear_load = {"token": token, "fun": "badtestarg"} + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } - with patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=['testing'])): + with patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"]) + ): ret = self.clear_funcs.wheel(clear_load) self.assertDictEqual(mock_ret, ret) def test_wheel_eauth_not_authenticated(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user can't authenticate. - ''' - mock_ret = {'error': {'name': 'EauthAuthenticationError', - 'message': 'Authentication failure of type "eauth" occurred for ' - 'user UNKNOWN.'}} - ret = self.clear_funcs.wheel({'eauth': 'foo'}) + """ + mock_ret = { + "error": { + "name": "EauthAuthenticationError", + "message": 'Authentication failure of type "eauth" occurred for ' + "user UNKNOWN.", + } + } + ret = self.clear_funcs.wheel({"eauth": "foo"}) self.assertDictEqual(mock_ret, ret) def test_wheel_eauth_authorization_error(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user authenticates, but is not authorized. - ''' - clear_load = {'eauth': 'foo', 'username': 'test', 'fun': 'test.arg'} - mock_ret = {'error': {'name': 'EauthAuthenticationError', - 'message': 'Authentication failure of type "eauth" occurred for ' - 'user test.'}} - with patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + """ + clear_load = {"eauth": "foo", "username": "test", "fun": "test.arg"} + mock_ret = { + "error": { + "name": "EauthAuthenticationError", + "message": 'Authentication failure of type "eauth" occurred for ' + "user test.", + } + } + with patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch("salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[])): ret = self.clear_funcs.wheel(clear_load) self.assertDictEqual(mock_ret, ret) def test_wheel_eauth_salt_invocation_error(self): - ''' + """ Asserts that an EauthAuthenticationError is returned when the user authenticates, but the command is malformed. - ''' - clear_load = {'eauth': 'foo', 'username': 'test', 'fun': 'bad.test.arg.func'} - mock_ret = {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} - with patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=['testing'])): + """ + clear_load = {"eauth": "foo", "username": "test", "fun": "bad.test.arg.func"} + mock_ret = { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } + with patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=["testing"]) + ): ret = self.clear_funcs.wheel(clear_load) self.assertDictEqual(mock_ret, ret) def test_wheel_user_not_authenticated(self): - ''' + """ Asserts that an UserAuthenticationError is returned when the user can't authenticate. - ''' - mock_ret = {'error': {'name': 'UserAuthenticationError', - 'message': 'Authentication failure of type "user" occurred'}} + """ + mock_ret = { + "error": { + "name": "UserAuthenticationError", + "message": 'Authentication failure of type "user" occurred', + } + } ret = self.clear_funcs.wheel({}) self.assertDictEqual(mock_ret, ret) # publish tests def test_publish_user_is_blacklisted(self): - ''' + """ Asserts that an AuthorizationError is returned when the user has been blacklisted. - ''' - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=True)): - self.assertEqual(mock_ret, self.clear_funcs.publish({'user': 'foo', 'fun': 'test.arg'})) + """ + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=True) + ): + self.assertEqual( + mock_ret, self.clear_funcs.publish({"user": "foo", "fun": "test.arg"}) + ) def test_publish_cmd_blacklisted(self): - ''' + """ Asserts that an AuthorizationError is returned when the command has been blacklisted. - ''' - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=True)): - self.assertEqual(mock_ret, self.clear_funcs.publish({'user': 'foo', 'fun': 'test.arg'})) + """ + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=True) + ): + self.assertEqual( + mock_ret, self.clear_funcs.publish({"user": "foo", "fun": "test.arg"}) + ) def test_publish_token_not_authenticated(self): - ''' + """ Asserts that an AuthenticationError is returned when the token can't authenticate. - ''' - mock_ret = {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} - load = {'user': 'foo', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'token': 'asdfasdfasdfasdf'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)): + """ + mock_ret = { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } + load = { + "user": "foo", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"token": "asdfasdfasdfasdf"}, + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ): self.assertEqual(mock_ret, self.clear_funcs.publish(load)) def test_publish_token_authorization_error(self): - ''' + """ Asserts that an AuthorizationError is returned when the token authenticates, but is not authorized. - ''' - token = 'asdfasdfasdfasdf' - load = {'user': 'foo', 'fun': 'test.arg', 'tgt': 'test_minion', - 'arg': 'bar', 'kwargs': {'token': token}} - mock_token = {'token': token, 'eauth': 'foo', 'name': 'test'} - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} + """ + token = "asdfasdfasdfasdf" + load = { + "user": "foo", + "fun": "test.arg", + "tgt": "test_minion", + "arg": "bar", + "kwargs": {"token": token}, + } + mock_token = {"token": token, "eauth": "foo", "name": "test"} + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.auth.LoadAuth.authenticate_token', MagicMock(return_value=mock_token)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.auth.LoadAuth.authenticate_token", MagicMock(return_value=mock_token) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[]) + ): self.assertEqual(mock_ret, self.clear_funcs.publish(load)) def test_publish_eauth_not_authenticated(self): - ''' + """ Asserts that an AuthenticationError is returned when the user can't authenticate. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'eauth': 'foo'}} - mock_ret = {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)): + """ + load = { + "user": "test", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"eauth": "foo"}, + } + mock_ret = { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ): self.assertEqual(mock_ret, self.clear_funcs.publish(load)) def test_publish_eauth_authorization_error(self): - ''' + """ Asserts that an AuthorizationError is returned when the user authenticates, but is not authorized. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'eauth': 'foo'}, 'arg': 'bar'} - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.auth.LoadAuth.authenticate_eauth', MagicMock(return_value=True)), \ - patch('salt.auth.LoadAuth.get_auth_list', MagicMock(return_value=[])): + """ + load = { + "user": "test", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"eauth": "foo"}, + "arg": "bar", + } + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.auth.LoadAuth.authenticate_eauth", MagicMock(return_value=True) + ), patch( + "salt.auth.LoadAuth.get_auth_list", MagicMock(return_value=[]) + ): self.assertEqual(mock_ret, self.clear_funcs.publish(load)) def test_publish_user_not_authenticated(self): - ''' + """ Asserts that an AuthenticationError is returned when the user can't authenticate. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion'} - mock_ret = {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)): + """ + load = {"user": "test", "fun": "test.arg", "tgt": "test_minion"} + mock_ret = { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ): self.assertEqual(mock_ret, self.clear_funcs.publish(load)) def test_publish_user_authenticated_missing_auth_list(self): - ''' + """ Asserts that an AuthenticationError is returned when the user has an effective user id and is authenticated, but the auth_list is empty. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'user': 'test'}, 'arg': 'foo'} - mock_ret = {'error': {'name': 'AuthenticationError', - 'message': 'Authentication error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.auth.LoadAuth.authenticate_key', MagicMock(return_value='fake-user-key')), \ - patch('salt.utils.master.get_values_of_matching_keys', MagicMock(return_value=[])): + """ + load = { + "user": "test", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"user": "test"}, + "arg": "foo", + } + mock_ret = { + "error": { + "name": "AuthenticationError", + "message": "Authentication error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.auth.LoadAuth.authenticate_key", + MagicMock(return_value="fake-user-key"), + ), patch( + "salt.utils.master.get_values_of_matching_keys", MagicMock(return_value=[]) + ): self.assertEqual(mock_ret, self.clear_funcs.publish(load)) def test_publish_user_authorization_error(self): - ''' + """ Asserts that an AuthorizationError is returned when the user authenticates, but is not authorized. - ''' - load = {'user': 'test', 'fun': 'test.arg', 'tgt': 'test_minion', - 'kwargs': {'user': 'test'}, 'arg': 'foo'} - mock_ret = {'error': {'name': 'AuthorizationError', - 'message': 'Authorization error occurred.'}} - with patch('salt.acl.PublisherACL.user_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.acl.PublisherACL.cmd_is_blacklisted', MagicMock(return_value=False)), \ - patch('salt.auth.LoadAuth.authenticate_key', MagicMock(return_value='fake-user-key')), \ - patch('salt.utils.master.get_values_of_matching_keys', MagicMock(return_value=['test'])), \ - patch('salt.utils.minions.CkMinions.auth_check', MagicMock(return_value=False)): + """ + load = { + "user": "test", + "fun": "test.arg", + "tgt": "test_minion", + "kwargs": {"user": "test"}, + "arg": "foo", + } + mock_ret = { + "error": { + "name": "AuthorizationError", + "message": "Authorization error occurred.", + } + } + with patch( + "salt.acl.PublisherACL.user_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.acl.PublisherACL.cmd_is_blacklisted", MagicMock(return_value=False) + ), patch( + "salt.auth.LoadAuth.authenticate_key", + MagicMock(return_value="fake-user-key"), + ), patch( + "salt.utils.master.get_values_of_matching_keys", + MagicMock(return_value=["test"]), + ), patch( + "salt.utils.minions.CkMinions.auth_check", MagicMock(return_value=False) + ): self.assertEqual(mock_ret, self.clear_funcs.publish(load)) diff --git a/tests/unit/test_minion.py b/tests/unit/test_minion.py index bf84f87ae5b..09513fdaca0 100644 --- a/tests/unit/test_minion.py +++ b/tests/unit/test_minion.py @@ -1,116 +1,171 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place <mp@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import + import copy import os -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock -from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.helpers import skip_if_not_root -# Import salt libs -import salt.minion -import salt.utils.event as event -from salt.exceptions import SaltSystemExit, SaltMasterUnresolvableError -import salt.syspaths import salt.ext.tornado import salt.ext.tornado.testing -from salt.ext.six.moves import range + +# Import salt libs +import salt.minion +import salt.syspaths import salt.utils.crypt +import salt.utils.event as event import salt.utils.process +from salt.exceptions import SaltMasterUnresolvableError, SaltSystemExit +from salt.ext.six.moves import range +from tests.support.helpers import skip_if_not_root +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): - def setUp(self): self.opts = {} - self.addCleanup(delattr, self, 'opts') + self.addCleanup(delattr, self, "opts") def test_invalid_master_address(self): - with patch.dict(self.opts, {'ipv6': False, 'master': float('127.0'), 'master_port': '4555', 'retry_dns': False}): + with patch.dict( + self.opts, + { + "ipv6": False, + "master": float("127.0"), + "master_port": "4555", + "retry_dns": False, + }, + ): self.assertRaises(SaltSystemExit, salt.minion.resolve_dns, self.opts) def test_source_int_name_local(self): - ''' + """ test when file_client local and source_interface_name is set - ''' - interfaces = {'bond0.1234': {'hwaddr': '01:01:01:d0:d0:d0', - 'up': True, 'inet': - [{'broadcast': '111.1.111.255', - 'netmask': '111.1.0.0', - 'label': 'bond0', - 'address': '111.1.0.1'}]}} - with patch.dict(self.opts, {'ipv6': False, 'master': '127.0.0.1', - 'master_port': '4555', 'file_client': 'local', - 'source_interface_name': 'bond0.1234', - 'source_ret_port': 49017, - 'source_publish_port': 49018}), \ - patch('salt.utils.network.interfaces', - MagicMock(return_value=interfaces)): - assert salt.minion.resolve_dns(self.opts) == {'master_ip': '127.0.0.1', - 'source_ip': '111.1.0.1', - 'source_ret_port': 49017, - 'source_publish_port': 49018, - 'master_uri': 'tcp://127.0.0.1:4555'} + """ + interfaces = { + "bond0.1234": { + "hwaddr": "01:01:01:d0:d0:d0", + "up": True, + "inet": [ + { + "broadcast": "111.1.111.255", + "netmask": "111.1.0.0", + "label": "bond0", + "address": "111.1.0.1", + } + ], + } + } + with patch.dict( + self.opts, + { + "ipv6": False, + "master": "127.0.0.1", + "master_port": "4555", + "file_client": "local", + "source_interface_name": "bond0.1234", + "source_ret_port": 49017, + "source_publish_port": 49018, + }, + ), patch("salt.utils.network.interfaces", MagicMock(return_value=interfaces)): + assert salt.minion.resolve_dns(self.opts) == { + "master_ip": "127.0.0.1", + "source_ip": "111.1.0.1", + "source_ret_port": 49017, + "source_publish_port": 49018, + "master_uri": "tcp://127.0.0.1:4555", + } def test_source_int_name_remote(self): - ''' + """ test when file_client remote and source_interface_name is set and interface is down - ''' - interfaces = {'bond0.1234': {'hwaddr': '01:01:01:d0:d0:d0', - 'up': False, 'inet': - [{'broadcast': '111.1.111.255', - 'netmask': '111.1.0.0', - 'label': 'bond0', - 'address': '111.1.0.1'}]}} - with patch.dict(self.opts, {'ipv6': False, 'master': '127.0.0.1', - 'master_port': '4555', 'file_client': 'remote', - 'source_interface_name': 'bond0.1234', - 'source_ret_port': 49017, - 'source_publish_port': 49018}), \ - patch('salt.utils.network.interfaces', - MagicMock(return_value=interfaces)): - assert salt.minion.resolve_dns(self.opts) == {'master_ip': '127.0.0.1', - 'source_ret_port': 49017, - 'source_publish_port': 49018, - 'master_uri': 'tcp://127.0.0.1:4555'} + """ + interfaces = { + "bond0.1234": { + "hwaddr": "01:01:01:d0:d0:d0", + "up": False, + "inet": [ + { + "broadcast": "111.1.111.255", + "netmask": "111.1.0.0", + "label": "bond0", + "address": "111.1.0.1", + } + ], + } + } + with patch.dict( + self.opts, + { + "ipv6": False, + "master": "127.0.0.1", + "master_port": "4555", + "file_client": "remote", + "source_interface_name": "bond0.1234", + "source_ret_port": 49017, + "source_publish_port": 49018, + }, + ), patch("salt.utils.network.interfaces", MagicMock(return_value=interfaces)): + assert salt.minion.resolve_dns(self.opts) == { + "master_ip": "127.0.0.1", + "source_ret_port": 49017, + "source_publish_port": 49018, + "master_uri": "tcp://127.0.0.1:4555", + } def test_source_address(self): - ''' + """ test when source_address is set - ''' - interfaces = {'bond0.1234': {'hwaddr': '01:01:01:d0:d0:d0', - 'up': False, 'inet': - [{'broadcast': '111.1.111.255', - 'netmask': '111.1.0.0', - 'label': 'bond0', - 'address': '111.1.0.1'}]}} - with patch.dict(self.opts, {'ipv6': False, 'master': '127.0.0.1', - 'master_port': '4555', 'file_client': 'local', - 'source_interface_name': '', - 'source_address': '111.1.0.1', - 'source_ret_port': 49017, - 'source_publish_port': 49018}), \ - patch('salt.utils.network.interfaces', - MagicMock(return_value=interfaces)): - assert salt.minion.resolve_dns(self.opts) == {'source_publish_port': 49018, - 'source_ret_port': 49017, - 'master_uri': 'tcp://127.0.0.1:4555', - 'source_ip': '111.1.0.1', - 'master_ip': '127.0.0.1'} + """ + interfaces = { + "bond0.1234": { + "hwaddr": "01:01:01:d0:d0:d0", + "up": False, + "inet": [ + { + "broadcast": "111.1.111.255", + "netmask": "111.1.0.0", + "label": "bond0", + "address": "111.1.0.1", + } + ], + } + } + with patch.dict( + self.opts, + { + "ipv6": False, + "master": "127.0.0.1", + "master_port": "4555", + "file_client": "local", + "source_interface_name": "", + "source_address": "111.1.0.1", + "source_ret_port": 49017, + "source_publish_port": 49018, + }, + ), patch("salt.utils.network.interfaces", MagicMock(return_value=interfaces)): + assert salt.minion.resolve_dns(self.opts) == { + "source_publish_port": 49018, + "source_ret_port": 49017, + "master_uri": "tcp://127.0.0.1:4555", + "source_ip": "111.1.0.1", + "master_ip": "127.0.0.1", + } # Tests for _handle_decoded_payload in the salt.minion.Minion() class: 3 def test_handle_decoded_payload_jid_match_in_jid_queue(self): - ''' + """ Tests that the _handle_decoded_payload function returns when a jid is given that is already present in the jid_queue. @@ -118,12 +173,15 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): for _handle_decoded_payload below. This is essential to this test as the call to the function must return None BEFORE any of the processes are spun up because we should be avoiding firing duplicate jobs. - ''' + """ mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() - mock_data = {'fun': 'foo.bar', - 'jid': 123} + mock_data = {"fun": "foo.bar", "jid": 123} mock_jid_queue = [123] - minion = salt.minion.Minion(mock_opts, jid_queue=copy.copy(mock_jid_queue), io_loop=salt.ext.tornado.ioloop.IOLoop()) + minion = salt.minion.Minion( + mock_opts, + jid_queue=copy.copy(mock_jid_queue), + io_loop=salt.ext.tornado.ioloop.IOLoop(), + ) try: ret = minion._handle_decoded_payload(mock_data).result() self.assertEqual(minion.jid_queue, mock_jid_queue) @@ -132,19 +190,26 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): minion.destroy() def test_handle_decoded_payload_jid_queue_addition(self): - ''' + """ Tests that the _handle_decoded_payload function adds a jid to the minion's jid_queue when the new jid isn't already present in the jid_queue. - ''' - with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ - patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): + """ + with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch( + "salt.utils.process.SignalHandlingProcess.start", + MagicMock(return_value=True), + ), patch( + "salt.utils.process.SignalHandlingProcess.join", + MagicMock(return_value=True), + ): mock_jid = 11111 mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() - mock_data = {'fun': 'foo.bar', - 'jid': mock_jid} + mock_data = {"fun": "foo.bar", "jid": mock_jid} mock_jid_queue = [123, 456] - minion = salt.minion.Minion(mock_opts, jid_queue=copy.copy(mock_jid_queue), io_loop=salt.ext.tornado.ioloop.IOLoop()) + minion = salt.minion.Minion( + mock_opts, + jid_queue=copy.copy(mock_jid_queue), + io_loop=salt.ext.tornado.ioloop.IOLoop(), + ) try: # Assert that the minion's jid_queue attribute matches the mock_jid_queue as a baseline @@ -161,19 +226,26 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): minion.destroy() def test_handle_decoded_payload_jid_queue_reduced_minion_jid_queue_hwm(self): - ''' + """ Tests that the _handle_decoded_payload function removes a jid from the minion's jid_queue when the minion's jid_queue high water mark (minion_jid_queue_hwm) is hit. - ''' - with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ - patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): + """ + with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch( + "salt.utils.process.SignalHandlingProcess.start", + MagicMock(return_value=True), + ), patch( + "salt.utils.process.SignalHandlingProcess.join", + MagicMock(return_value=True), + ): mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() - mock_opts['minion_jid_queue_hwm'] = 2 - mock_data = {'fun': 'foo.bar', - 'jid': 789} + mock_opts["minion_jid_queue_hwm"] = 2 + mock_data = {"fun": "foo.bar", "jid": 789} mock_jid_queue = [123, 456] - minion = salt.minion.Minion(mock_opts, jid_queue=copy.copy(mock_jid_queue), io_loop=salt.ext.tornado.ioloop.IOLoop()) + minion = salt.minion.Minion( + mock_opts, + jid_queue=copy.copy(mock_jid_queue), + io_loop=salt.ext.tornado.ioloop.IOLoop(), + ) try: # Assert that the minion's jid_queue attribute matches the mock_jid_queue as a baseline @@ -189,19 +261,26 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): minion.destroy() def test_process_count_max(self): - ''' + """ Tests that the _handle_decoded_payload function does not spawn more than the configured amount of processes, as per process_count_max. - ''' - with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ - patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)), \ - patch('salt.utils.minion.running', MagicMock(return_value=[])), \ - patch('salt.ext.tornado.gen.sleep', MagicMock(return_value=salt.ext.tornado.concurrent.Future())): + """ + with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch( + "salt.utils.process.SignalHandlingProcess.start", + MagicMock(return_value=True), + ), patch( + "salt.utils.process.SignalHandlingProcess.join", + MagicMock(return_value=True), + ), patch( + "salt.utils.minion.running", MagicMock(return_value=[]) + ), patch( + "salt.ext.tornado.gen.sleep", + MagicMock(return_value=salt.ext.tornado.concurrent.Future()), + ): process_count_max = 10 mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() - mock_opts['__role'] = 'minion' - mock_opts['minion_jid_queue_hwm'] = 100 + mock_opts["__role"] = "minion" + mock_opts["minion_jid_queue_hwm"] = 100 mock_opts["process_count_max"] = process_count_max io_loop = salt.ext.tornado.ioloop.IOLoop() @@ -212,39 +291,55 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): class SleepCalledException(Exception): """Thrown when sleep is called""" - salt.ext.tornado.gen.sleep.return_value.set_exception(SleepCalledException()) + salt.ext.tornado.gen.sleep.return_value.set_exception( + SleepCalledException() + ) # up until process_count_max: gen.sleep does not get called, processes are started normally for i in range(process_count_max): - mock_data = {'fun': 'foo.bar', - 'jid': i} - io_loop.run_sync(lambda data=mock_data: minion._handle_decoded_payload(data)) - self.assertEqual(salt.utils.process.SignalHandlingProcess.start.call_count, i + 1) + mock_data = {"fun": "foo.bar", "jid": i} + io_loop.run_sync( + lambda data=mock_data: minion._handle_decoded_payload(data) + ) + self.assertEqual( + salt.utils.process.SignalHandlingProcess.start.call_count, i + 1 + ) self.assertEqual(len(minion.jid_queue), i + 1) salt.utils.minion.running.return_value += [i] # above process_count_max: gen.sleep does get called, JIDs are created but no new processes are started - mock_data = {'fun': 'foo.bar', - 'jid': process_count_max + 1} + mock_data = {"fun": "foo.bar", "jid": process_count_max + 1} - self.assertRaises(SleepCalledException, - lambda: io_loop.run_sync(lambda: minion._handle_decoded_payload(mock_data))) - self.assertEqual(salt.utils.process.SignalHandlingProcess.start.call_count, - process_count_max) + self.assertRaises( + SleepCalledException, + lambda: io_loop.run_sync( + lambda: minion._handle_decoded_payload(mock_data) + ), + ) + self.assertEqual( + salt.utils.process.SignalHandlingProcess.start.call_count, + process_count_max, + ) self.assertEqual(len(minion.jid_queue), process_count_max + 1) finally: minion.destroy() def test_beacons_before_connect(self): - ''' + """ Tests that the 'beacons_before_connect' option causes the beacons to be initialized before connect. - ''' - with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ - patch('salt.minion.Minion.sync_connect_master', MagicMock(side_effect=RuntimeError('stop execution'))), \ - patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): - mock_opts = self.get_config('minion', from_scratch=True) - mock_opts['beacons_before_connect'] = True + """ + with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch( + "salt.minion.Minion.sync_connect_master", + MagicMock(side_effect=RuntimeError("stop execution")), + ), patch( + "salt.utils.process.SignalHandlingProcess.start", + MagicMock(return_value=True), + ), patch( + "salt.utils.process.SignalHandlingProcess.join", + MagicMock(return_value=True), + ): + mock_opts = self.get_config("minion", from_scratch=True) + mock_opts["beacons_before_connect"] = True io_loop = salt.ext.tornado.ioloop.IOLoop() io_loop.make_current() minion = salt.minion.Minion(mock_opts, io_loop=io_loop) @@ -256,21 +351,27 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): pass # Make sure beacons are initialized but the sheduler is not - self.assertTrue('beacons' in minion.periodic_callbacks) - self.assertTrue('schedule' not in minion.periodic_callbacks) + self.assertTrue("beacons" in minion.periodic_callbacks) + self.assertTrue("schedule" not in minion.periodic_callbacks) finally: minion.destroy() def test_scheduler_before_connect(self): - ''' + """ Tests that the 'scheduler_before_connect' option causes the scheduler to be initialized before connect. - ''' - with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ - patch('salt.minion.Minion.sync_connect_master', MagicMock(side_effect=RuntimeError('stop execution'))), \ - patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): - mock_opts = self.get_config('minion', from_scratch=True) - mock_opts['scheduler_before_connect'] = True + """ + with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch( + "salt.minion.Minion.sync_connect_master", + MagicMock(side_effect=RuntimeError("stop execution")), + ), patch( + "salt.utils.process.SignalHandlingProcess.start", + MagicMock(return_value=True), + ), patch( + "salt.utils.process.SignalHandlingProcess.join", + MagicMock(return_value=True), + ): + mock_opts = self.get_config("minion", from_scratch=True) + mock_opts["scheduler_before_connect"] = True io_loop = salt.ext.tornado.ioloop.IOLoop() io_loop.make_current() minion = salt.minion.Minion(mock_opts, io_loop=io_loop) @@ -281,18 +382,26 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): pass # Make sure the scheduler is initialized but the beacons are not - self.assertTrue('schedule' in minion.periodic_callbacks) - self.assertTrue('beacons' not in minion.periodic_callbacks) + self.assertTrue("schedule" in minion.periodic_callbacks) + self.assertTrue("beacons" not in minion.periodic_callbacks) finally: minion.destroy() - def test_when_ping_interval_is_set_the_callback_should_be_added_to_periodic_callbacks(self): - with patch('salt.minion.Minion.ctx', MagicMock(return_value={})), \ - patch('salt.minion.Minion.sync_connect_master', MagicMock(side_effect=RuntimeError('stop execution'))), \ - patch('salt.utils.process.SignalHandlingProcess.start', MagicMock(return_value=True)), \ - patch('salt.utils.process.SignalHandlingProcess.join', MagicMock(return_value=True)): - mock_opts = self.get_config('minion', from_scratch=True) - mock_opts['ping_interval'] = 10 + def test_when_ping_interval_is_set_the_callback_should_be_added_to_periodic_callbacks( + self, + ): + with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch( + "salt.minion.Minion.sync_connect_master", + MagicMock(side_effect=RuntimeError("stop execution")), + ), patch( + "salt.utils.process.SignalHandlingProcess.start", + MagicMock(return_value=True), + ), patch( + "salt.utils.process.SignalHandlingProcess.join", + MagicMock(return_value=True), + ): + mock_opts = self.get_config("minion", from_scratch=True) + mock_opts["ping_interval"] = 10 io_loop = salt.ext.tornado.ioloop.IOLoop() io_loop.make_current() minion = salt.minion.Minion(mock_opts, io_loop=io_loop) @@ -305,74 +414,84 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): pass # Make sure the scheduler is initialized but the beacons are not - self.assertTrue('ping' in minion.periodic_callbacks) + self.assertTrue("ping" in minion.periodic_callbacks) finally: minion.destroy() def test_when_passed_start_event_grains(self): - mock_opts = self.get_config('minion', from_scratch=True) - mock_opts['start_event_grains'] = ["os"] + mock_opts = self.get_config("minion", from_scratch=True) + mock_opts["start_event_grains"] = ["os"] io_loop = salt.ext.tornado.ioloop.IOLoop() io_loop.make_current() minion = salt.minion.Minion(mock_opts, io_loop=io_loop) try: minion.tok = MagicMock() minion._send_req_sync = MagicMock() - minion._fire_master('Minion has started', 'minion_start', include_startup_grains=True) + minion._fire_master( + "Minion has started", "minion_start", include_startup_grains=True + ) load = minion._send_req_sync.call_args[0][0] - self.assertTrue('grains' in load) - self.assertTrue('os' in load['grains']) + self.assertTrue("grains" in load) + self.assertTrue("os" in load["grains"]) finally: minion.destroy() def test_when_not_passed_start_event_grains(self): - mock_opts = self.get_config('minion', from_scratch=True) + mock_opts = self.get_config("minion", from_scratch=True) io_loop = salt.ext.tornado.ioloop.IOLoop() io_loop.make_current() minion = salt.minion.Minion(mock_opts, io_loop=io_loop) try: minion.tok = MagicMock() minion._send_req_sync = MagicMock() - minion._fire_master('Minion has started', 'minion_start') + minion._fire_master("Minion has started", "minion_start") load = minion._send_req_sync.call_args[0][0] - self.assertTrue('grains' not in load) + self.assertTrue("grains" not in load) finally: minion.destroy() def test_when_other_events_fired_and_start_event_grains_are_set(self): - mock_opts = self.get_config('minion', from_scratch=True) - mock_opts['start_event_grains'] = ["os"] + mock_opts = self.get_config("minion", from_scratch=True) + mock_opts["start_event_grains"] = ["os"] io_loop = salt.ext.tornado.ioloop.IOLoop() io_loop.make_current() minion = salt.minion.Minion(mock_opts, io_loop=io_loop) try: minion.tok = MagicMock() minion._send_req_sync = MagicMock() - minion._fire_master('Custm_event_fired', 'custom_event') + minion._fire_master("Custm_event_fired", "custom_event") load = minion._send_req_sync.call_args[0][0] - self.assertTrue('grains' not in load) + self.assertTrue("grains" not in load) finally: minion.destroy() def test_minion_retry_dns_count(self): - ''' + """ Tests that the resolve_dns will retry dns look ups for a maximum of 3 times before raising a SaltMasterUnresolvableError exception. - ''' - with patch.dict(self.opts, {'ipv6': False, 'master': 'dummy', - 'master_port': '4555', - 'retry_dns': 1, 'retry_dns_count': 3}): - self.assertRaises(SaltMasterUnresolvableError, - salt.minion.resolve_dns, self.opts) + """ + with patch.dict( + self.opts, + { + "ipv6": False, + "master": "dummy", + "master_port": "4555", + "retry_dns": 1, + "retry_dns_count": 3, + }, + ): + self.assertRaises( + SaltMasterUnresolvableError, salt.minion.resolve_dns, self.opts + ) def test_gen_modules_executors(self): - ''' + """ Ensure gen_modules is called with the correct arguments #54429 - ''' - mock_opts = self.get_config('minion', from_scratch=True) + """ + mock_opts = self.get_config("minion", from_scratch=True) io_loop = salt.ext.tornado.ioloop.IOLoop() io_loop.make_current() minion = salt.minion.Minion(mock_opts, io_loop=io_loop) @@ -382,19 +501,19 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): return {} try: - with patch('salt.pillar.get_pillar', return_value=MockPillarCompiler()): - with patch('salt.loader.executors') as execmock: + with patch("salt.pillar.get_pillar", return_value=MockPillarCompiler()): + with patch("salt.loader.executors") as execmock: minion.gen_modules() assert execmock.called_with(minion.opts, minion.functions) finally: minion.destroy() - @patch('salt.utils.process.default_signals') + @patch("salt.utils.process.default_signals") def test_reinit_crypto_on_fork(self, def_mock): - ''' + """ Ensure salt.utils.crypt.reinit_crypto() is executed when forking for new job - ''' - mock_opts = self.get_config('minion', from_scratch=True) + """ + mock_opts = self.get_config("minion", from_scratch=True) mock_opts["multiprocessing"] = True io_loop = salt.ext.tornado.ioloop.IOLoop() @@ -404,33 +523,47 @@ class MinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): job_data = {"jid": "test-jid", "fun": "test.ping"} def mock_start(self): - assert len([x for x in self._after_fork_methods if x[0] == salt.utils.crypt.reinit_crypto]) == 1 # pylint: disable=comparison-with-callable + # pylint: disable=comparison-with-callable + assert ( + len( + [ + x + for x in self._after_fork_methods + if x[0] == salt.utils.crypt.reinit_crypto + ] + ) + == 1 + ) + # pylint: enable=comparison-with-callable - with patch.object(salt.utils.process.SignalHandlingProcess, 'start', mock_start): + with patch.object( + salt.utils.process.SignalHandlingProcess, "start", mock_start + ): io_loop.run_sync(lambda: minion._handle_decoded_payload(job_data)) -class MinionAsyncTestCase(TestCase, AdaptedConfigurationTestCaseMixin, salt.ext.tornado.testing.AsyncTestCase): - +class MinionAsyncTestCase( + TestCase, AdaptedConfigurationTestCaseMixin, salt.ext.tornado.testing.AsyncTestCase +): def setUp(self): super(MinionAsyncTestCase, self).setUp() self.opts = {} - self.addCleanup(delattr, self, 'opts') + self.addCleanup(delattr, self, "opts") @skip_if_not_root def test_sock_path_len(self): - ''' + """ This tests whether or not a larger hash causes the sock path to exceed the system's max sock path length. See the below link for more information. https://github.com/saltstack/salt/issues/12172#issuecomment-43903643 - ''' + """ opts = { - 'id': 'salt-testing', - 'hash_type': 'sha512', - 'sock_dir': os.path.join(salt.syspaths.SOCK_DIR, 'minion'), - 'extension_modules': '' + "id": "salt-testing", + "hash_type": "sha512", + "sock_dir": os.path.join(salt.syspaths.SOCK_DIR, "minion"), + "extension_modules": "", } with patch.dict(self.opts, opts): try: diff --git a/tests/unit/test_mock.py b/tests/unit/test_mock.py index 90cf041d5eb..4b7922795a0 100644 --- a/tests/unit/test_mock.py +++ b/tests/unit/test_mock.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Tests for our mock_open helper -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import errno import logging import textwrap @@ -15,7 +16,7 @@ import salt.utils.stringutils from salt.ext import six # Import Salt Testing Libs -from tests.support.mock import patch, mock_open +from tests.support.mock import mock_open, patch from tests.support.unit import TestCase log = logging.getLogger(__name__) @@ -24,14 +25,14 @@ log = logging.getLogger(__name__) class MockOpenMixin(object): def _get_values(self, binary=False, multifile=False, split=False): if split: - questions = (self.questions_bytes_lines if binary - else self.questions_str_lines) - answers = (self.answers_bytes_lines if binary - else self.answers_str_lines) + questions = ( + self.questions_bytes_lines if binary else self.questions_str_lines + ) + answers = self.answers_bytes_lines if binary else self.answers_str_lines else: questions = self.questions_bytes if binary else self.questions_str answers = self.answers_bytes if binary else self.answers_str - mode = 'rb' if binary else 'r' + mode = "rb" if binary else "r" if multifile: read_data = self.contents_bytes if binary else self.contents else: @@ -39,36 +40,38 @@ class MockOpenMixin(object): return questions, answers, mode, read_data def _test_read(self, binary=False, multifile=False): - questions, answers, mode, read_data = \ - self._get_values(binary=binary, multifile=multifile) + questions, answers, mode, read_data = self._get_values( + binary=binary, multifile=multifile + ) - with patch('salt.utils.files.fopen', mock_open(read_data=read_data)): - with salt.utils.files.fopen('foo.txt', mode) as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=read_data)): + with salt.utils.files.fopen("foo.txt", mode) as self.fh: result = self.fh.read() assert result == questions, result if multifile: - with salt.utils.files.fopen('bar.txt', mode) as self.fh2: + with salt.utils.files.fopen("bar.txt", mode) as self.fh2: result = self.fh2.read() assert result == answers, result - with salt.utils.files.fopen('baz.txt', mode) as self.fh3: + with salt.utils.files.fopen("baz.txt", mode) as self.fh3: result = self.fh3.read() assert result == answers, result try: - with salt.utils.files.fopen('helloworld.txt'): - raise Exception('No patterns should have matched') + with salt.utils.files.fopen("helloworld.txt"): + raise Exception("No patterns should have matched") except IOError: # An IOError is expected here pass def _test_read_explicit_size(self, binary=False, multifile=False): - questions, answers, mode, read_data = \ - self._get_values(binary=binary, multifile=multifile) + questions, answers, mode, read_data = self._get_values( + binary=binary, multifile=multifile + ) - with patch('salt.utils.files.fopen', mock_open(read_data=read_data)): - with salt.utils.files.fopen('foo.txt', mode) as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=read_data)): + with salt.utils.files.fopen("foo.txt", mode) as self.fh: # Read 10 bytes result = self.fh.read(10) assert result == questions[:10], result @@ -80,7 +83,7 @@ class MockOpenMixin(object): assert result == questions[20:], result if multifile: - with salt.utils.files.fopen('bar.txt', mode) as self.fh2: + with salt.utils.files.fopen("bar.txt", mode) as self.fh2: # Read 10 bytes result = self.fh2.read(10) assert result == answers[:10], result @@ -91,7 +94,7 @@ class MockOpenMixin(object): result = self.fh2.read() assert result == answers[20:], result - with salt.utils.files.fopen('baz.txt', mode) as self.fh3: + with salt.utils.files.fopen("baz.txt", mode) as self.fh3: # Read 10 bytes result = self.fh3.read(10) assert result == answers[:10], result @@ -103,79 +106,83 @@ class MockOpenMixin(object): assert result == answers[20:], result try: - with salt.utils.files.fopen('helloworld.txt'): - raise Exception('No globs should have matched') + with salt.utils.files.fopen("helloworld.txt"): + raise Exception("No globs should have matched") except IOError: # An IOError is expected here pass - def _test_read_explicit_size_larger_than_file_size(self, - binary=False, - multifile=False): - questions, answers, mode, read_data = \ - self._get_values(binary=binary, multifile=multifile) + def _test_read_explicit_size_larger_than_file_size( + self, binary=False, multifile=False + ): + questions, answers, mode, read_data = self._get_values( + binary=binary, multifile=multifile + ) - with patch('salt.utils.files.fopen', mock_open(read_data=read_data)): - with salt.utils.files.fopen('foo.txt', mode) as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=read_data)): + with salt.utils.files.fopen("foo.txt", mode) as self.fh: result = self.fh.read(999999) assert result == questions, result if multifile: - with salt.utils.files.fopen('bar.txt', mode) as self.fh2: + with salt.utils.files.fopen("bar.txt", mode) as self.fh2: result = self.fh2.read(999999) assert result == answers, result - with salt.utils.files.fopen('baz.txt', mode) as self.fh3: + with salt.utils.files.fopen("baz.txt", mode) as self.fh3: result = self.fh3.read(999999) assert result == answers, result try: - with salt.utils.files.fopen('helloworld.txt'): - raise Exception('No globs should have matched') + with salt.utils.files.fopen("helloworld.txt"): + raise Exception("No globs should have matched") except IOError: # An IOError is expected here pass def _test_read_for_loop(self, binary=False, multifile=False): - questions, answers, mode, read_data = \ - self._get_values(binary=binary, multifile=multifile, split=True) + questions, answers, mode, read_data = self._get_values( + binary=binary, multifile=multifile, split=True + ) - with patch('salt.utils.files.fopen', mock_open(read_data=read_data)): - with salt.utils.files.fopen('foo.txt', mode) as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=read_data)): + with salt.utils.files.fopen("foo.txt", mode) as self.fh: index = 0 for line in self.fh: - assert line == questions[index], \ - 'Line {0}: {1}'.format(index, line) + assert line == questions[index], "Line {0}: {1}".format(index, line) index += 1 if multifile: - with salt.utils.files.fopen('bar.txt', mode) as self.fh2: + with salt.utils.files.fopen("bar.txt", mode) as self.fh2: index = 0 for line in self.fh2: - assert line == answers[index], \ - 'Line {0}: {1}'.format(index, line) + assert line == answers[index], "Line {0}: {1}".format( + index, line + ) index += 1 - with salt.utils.files.fopen('baz.txt', mode) as self.fh3: + with salt.utils.files.fopen("baz.txt", mode) as self.fh3: index = 0 for line in self.fh3: - assert line == answers[index], \ - 'Line {0}: {1}'.format(index, line) + assert line == answers[index], "Line {0}: {1}".format( + index, line + ) index += 1 try: - with salt.utils.files.fopen('helloworld.txt'): - raise Exception('No globs should have matched') + with salt.utils.files.fopen("helloworld.txt"): + raise Exception("No globs should have matched") except IOError: # An IOError is expected here pass def _test_read_readline(self, binary=False, multifile=False): - questions, answers, mode, read_data = \ - self._get_values(binary=binary, multifile=multifile, split=True) + questions, answers, mode, read_data = self._get_values( + binary=binary, multifile=multifile, split=True + ) - with patch('salt.utils.files.fopen', mock_open(read_data=read_data)): - with salt.utils.files.fopen('foo.txt', mode) as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=read_data)): + with salt.utils.files.fopen("foo.txt", mode) as self.fh: size = 8 result = self.fh.read(size) assert result == questions[0][:size], result @@ -189,7 +196,7 @@ class MockOpenMixin(object): assert result == questions[2], result if multifile: - with salt.utils.files.fopen('bar.txt', mode) as self.fh2: + with salt.utils.files.fopen("bar.txt", mode) as self.fh2: size = 20 result = self.fh2.read(size) assert result == answers[0][:size], result @@ -202,7 +209,7 @@ class MockOpenMixin(object): result = self.fh2.readline() assert result == answers[2], result - with salt.utils.files.fopen('baz.txt', mode) as self.fh3: + with salt.utils.files.fopen("baz.txt", mode) as self.fh3: size = 20 result = self.fh3.read(size) assert result == answers[0][:size], result @@ -216,18 +223,19 @@ class MockOpenMixin(object): assert result == answers[2], result try: - with salt.utils.files.fopen('helloworld.txt'): - raise Exception('No globs should have matched') + with salt.utils.files.fopen("helloworld.txt"): + raise Exception("No globs should have matched") except IOError: # An IOError is expected here pass def _test_readline_readlines(self, binary=False, multifile=False): - questions, answers, mode, read_data = \ - self._get_values(binary=binary, multifile=multifile, split=True) + questions, answers, mode, read_data = self._get_values( + binary=binary, multifile=multifile, split=True + ) - with patch('salt.utils.files.fopen', mock_open(read_data=read_data)): - with salt.utils.files.fopen('foo.txt', mode) as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=read_data)): + with salt.utils.files.fopen("foo.txt", mode) as self.fh: # Read the first line result = self.fh.readline() assert result == questions[0], result @@ -236,7 +244,7 @@ class MockOpenMixin(object): assert result == questions[1:], result if multifile: - with salt.utils.files.fopen('bar.txt', mode) as self.fh2: + with salt.utils.files.fopen("bar.txt", mode) as self.fh2: # Read the first line result = self.fh2.readline() assert result == answers[0], result @@ -244,7 +252,7 @@ class MockOpenMixin(object): result = self.fh2.readlines() assert result == answers[1:], result - with salt.utils.files.fopen('baz.txt', mode) as self.fh3: + with salt.utils.files.fopen("baz.txt", mode) as self.fh3: # Read the first line result = self.fh3.readline() assert result == answers[0], result @@ -253,56 +261,61 @@ class MockOpenMixin(object): assert result == answers[1:], result try: - with salt.utils.files.fopen('helloworld.txt'): - raise Exception('No globs should have matched') + with salt.utils.files.fopen("helloworld.txt"): + raise Exception("No globs should have matched") except IOError: # An IOError is expected here pass def _test_readlines_multifile(self, binary=False, multifile=False): - questions, answers, mode, read_data = \ - self._get_values(binary=binary, multifile=multifile, split=True) + questions, answers, mode, read_data = self._get_values( + binary=binary, multifile=multifile, split=True + ) - with patch('salt.utils.files.fopen', mock_open(read_data=read_data)): - with salt.utils.files.fopen('foo.txt', mode) as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=read_data)): + with salt.utils.files.fopen("foo.txt", mode) as self.fh: result = self.fh.readlines() assert result == questions, result if multifile: - with salt.utils.files.fopen('bar.txt', mode) as self.fh2: + with salt.utils.files.fopen("bar.txt", mode) as self.fh2: result = self.fh2.readlines() assert result == answers, result - with salt.utils.files.fopen('baz.txt', mode) as self.fh3: + with salt.utils.files.fopen("baz.txt", mode) as self.fh3: result = self.fh3.readlines() assert result == answers, result try: - with salt.utils.files.fopen('helloworld.txt'): - raise Exception('No globs should have matched') + with salt.utils.files.fopen("helloworld.txt"): + raise Exception("No globs should have matched") except IOError: # An IOError is expected here pass class MockOpenTestCase(TestCase, MockOpenMixin): - ''' + """ Tests for our mock_open helper to ensure that it behaves as closely as possible to a real filehandle. - ''' + """ # Cyrllic characters used to test unicode handling - questions = textwrap.dedent('''\ + questions = textwrap.dedent( + """\ Шнат is your name? Шнат is your quest? Шнат is the airspeed velocity of an unladen swallow? - ''') + """ + ) - answers = textwrap.dedent('''\ + answers = textwrap.dedent( + """\ It is Аятнця, King of the Britons. To seek тне Holy Grail. Шнат do you mean? An African or European swallow? - ''') + """ + ) @classmethod def setUpClass(cls): @@ -321,33 +334,35 @@ class MockOpenTestCase(TestCase, MockOpenMixin): # When this is used as the read_data, Python 2 should normalize # cls.questions and cls.answers to str types. - cls.contents = {'foo.txt': cls.questions, - 'b*.txt': cls.answers} - cls.contents_bytes = {'foo.txt': cls.questions_bytes, - 'b*.txt': cls.answers_bytes} + cls.contents = {"foo.txt": cls.questions, "b*.txt": cls.answers} + cls.contents_bytes = { + "foo.txt": cls.questions_bytes, + "b*.txt": cls.answers_bytes, + } cls.read_data_as_list = [ - 'foo', 'bar', 'спам', - IOError(errno.EACCES, 'Permission denied') + "foo", + "bar", + "спам", + IOError(errno.EACCES, "Permission denied"), ] cls.normalized_read_data_as_list = salt.utils.data.decode( - cls.read_data_as_list, - to_str=True + cls.read_data_as_list, to_str=True ) cls.read_data_as_list_bytes = salt.utils.data.encode(cls.read_data_as_list) def tearDown(self): - ''' + """ Each test should read the entire contents of the mocked filehandle(s). This confirms that the other read functions return empty strings/lists, to simulate being at EOF. - ''' - for handle_name in ('fh', 'fh2', 'fh3'): + """ + for handle_name in ("fh", "fh2", "fh3"): try: fh = getattr(self, handle_name) except AttributeError: continue - log.debug('Running tearDown tests for self.%s', handle_name) + log.debug("Running tearDown tests for self.%s", handle_name) try: result = fh.read(5) assert not result, result @@ -363,114 +378,116 @@ class MockOpenTestCase(TestCase, MockOpenMixin): # exception will only be raised if we aren't at EOF already. for line in fh: raise Exception( - 'Instead of EOF, read the following from {0}: {1}'.format( - handle_name, - line + "Instead of EOF, read the following from {0}: {1}".format( + handle_name, line ) ) except IOError as exc: - if six.text_type(exc) != 'File not open for reading': + if six.text_type(exc) != "File not open for reading": raise del fh def test_read(self): - ''' + """ Test reading the entire file - ''' + """ self._test_read(binary=False, multifile=False) self._test_read(binary=True, multifile=False) self._test_read(binary=False, multifile=True) self._test_read(binary=True, multifile=True) def test_read_explicit_size(self): - ''' + """ Test reading with explicit sizes - ''' + """ self._test_read_explicit_size(binary=False, multifile=False) self._test_read_explicit_size(binary=True, multifile=False) self._test_read_explicit_size(binary=False, multifile=True) self._test_read_explicit_size(binary=True, multifile=True) def test_read_explicit_size_larger_than_file_size(self): - ''' + """ Test reading with an explicit size larger than the size of read_data. This ensures that we just return the contents up until EOF and that we don't raise any errors due to the desired size being larger than the mocked file's size. - ''' + """ self._test_read_explicit_size_larger_than_file_size( - binary=False, multifile=False) + binary=False, multifile=False + ) self._test_read_explicit_size_larger_than_file_size( - binary=True, multifile=False) + binary=True, multifile=False + ) self._test_read_explicit_size_larger_than_file_size( - binary=False, multifile=True) - self._test_read_explicit_size_larger_than_file_size( - binary=True, multifile=True) + binary=False, multifile=True + ) + self._test_read_explicit_size_larger_than_file_size(binary=True, multifile=True) def test_read_for_loop(self): - ''' + """ Test reading the contents of the file line by line in a for loop - ''' + """ self._test_read_for_loop(binary=False, multifile=False) self._test_read_for_loop(binary=True, multifile=False) self._test_read_for_loop(binary=False, multifile=True) self._test_read_for_loop(binary=True, multifile=True) def test_read_readline(self): - ''' + """ Test reading part of a line using .read(), then reading the rest of the line (and subsequent lines) using .readline(). - ''' + """ self._test_read_readline(binary=False, multifile=False) self._test_read_readline(binary=True, multifile=False) self._test_read_readline(binary=False, multifile=True) self._test_read_readline(binary=True, multifile=True) def test_readline_readlines(self): - ''' + """ Test reading the first line using .readline(), then reading the rest of the file using .readlines(). - ''' + """ self._test_readline_readlines(binary=False, multifile=False) self._test_readline_readlines(binary=True, multifile=False) self._test_readline_readlines(binary=False, multifile=True) self._test_readline_readlines(binary=True, multifile=True) def test_readlines(self): - ''' + """ Test reading the entire file using .readlines - ''' + """ self._test_readlines_multifile(binary=False, multifile=False) self._test_readlines_multifile(binary=True, multifile=False) self._test_readlines_multifile(binary=False, multifile=True) self._test_readlines_multifile(binary=True, multifile=True) def test_read_data_converted_to_dict(self): - ''' + """ Test that a non-dict value for read_data is converted to a dict mapping '*' to that value. - ''' - contents = 'спам' + """ + contents = "спам" normalized = salt.utils.stringutils.to_str(contents) - with patch('salt.utils.files.fopen', - mock_open(read_data=contents)) as m_open: - assert m_open.read_data == {'*': normalized}, m_open.read_data + with patch("salt.utils.files.fopen", mock_open(read_data=contents)) as m_open: + assert m_open.read_data == {"*": normalized}, m_open.read_data - with patch('salt.utils.files.fopen', - mock_open(read_data=self.read_data_as_list)) as m_open: + with patch( + "salt.utils.files.fopen", mock_open(read_data=self.read_data_as_list) + ) as m_open: assert m_open.read_data == { - '*': self.normalized_read_data_as_list, + "*": self.normalized_read_data_as_list, }, m_open.read_data def test_read_data_list(self): - ''' + """ Test read_data when it is a list - ''' - with patch('salt.utils.files.fopen', - mock_open(read_data=self.read_data_as_list)): + """ + with patch( + "salt.utils.files.fopen", mock_open(read_data=self.read_data_as_list) + ): for value in self.normalized_read_data_as_list: try: - with salt.utils.files.fopen('foo.txt') as self.fh: + with salt.utils.files.fopen("foo.txt") as self.fh: result = self.fh.read() assert result == value, result except IOError: @@ -480,14 +497,15 @@ class MockOpenTestCase(TestCase, MockOpenMixin): raise def test_read_data_list_bytes(self): - ''' + """ Test read_data when it is a list and the value is a bytestring - ''' - with patch('salt.utils.files.fopen', - mock_open(read_data=self.read_data_as_list_bytes)): + """ + with patch( + "salt.utils.files.fopen", mock_open(read_data=self.read_data_as_list_bytes) + ): for value in self.read_data_as_list_bytes: try: - with salt.utils.files.fopen('foo.txt', 'rb') as self.fh: + with salt.utils.files.fopen("foo.txt", "rb") as self.fh: result = self.fh.read() assert result == value, result except IOError: @@ -497,14 +515,13 @@ class MockOpenTestCase(TestCase, MockOpenMixin): raise def test_tell(self): - ''' + """ Test the implementation of tell - ''' - with patch('salt.utils.files.fopen', - mock_open(read_data=self.contents)): + """ + with patch("salt.utils.files.fopen", mock_open(read_data=self.contents)): # Try with reading explicit sizes and then reading the rest of the # file. - with salt.utils.files.fopen('foo.txt') as self.fh: + with salt.utils.files.fopen("foo.txt") as self.fh: self.fh.read(5) loc = self.fh.tell() assert loc == 5, loc @@ -517,7 +534,7 @@ class MockOpenTestCase(TestCase, MockOpenMixin): # Try reading way more content then actually exists in the file, # tell() should return a value equal to the length of the content - with salt.utils.files.fopen('foo.txt') as self.fh: + with salt.utils.files.fopen("foo.txt") as self.fh: self.fh.read(999999) loc = self.fh.tell() assert loc == len(self.questions_str), loc @@ -525,7 +542,7 @@ class MockOpenTestCase(TestCase, MockOpenMixin): # Try reading a few bytes using .read(), then the rest of the line # using .readline(), then the rest of the file using .readlines(), # and check the location after each read. - with salt.utils.files.fopen('foo.txt') as self.fh: + with salt.utils.files.fopen("foo.txt") as self.fh: # Read a few bytes self.fh.read(5) loc = self.fh.tell() @@ -541,7 +558,7 @@ class MockOpenTestCase(TestCase, MockOpenMixin): assert loc == len(self.questions_str), loc # Check location while iterating through the filehandle - with salt.utils.files.fopen('foo.txt') as self.fh: + with salt.utils.files.fopen("foo.txt") as self.fh: index = 0 for _ in self.fh: index += 1 @@ -551,42 +568,50 @@ class MockOpenTestCase(TestCase, MockOpenMixin): ), loc def test_write(self): - ''' + """ Test writing to a filehandle using .write() - ''' + """ # Test opening for non-binary writing - with patch('salt.utils.files.fopen', mock_open()): - with salt.utils.files.fopen('foo.txt', 'w') as self.fh: + with patch("salt.utils.files.fopen", mock_open()): + with salt.utils.files.fopen("foo.txt", "w") as self.fh: for line in self.questions_str_lines: self.fh.write(line) - assert self.fh.write_calls == self.questions_str_lines, self.fh.write_calls + assert ( + self.fh.write_calls == self.questions_str_lines + ), self.fh.write_calls # Test opening for binary writing using "wb" - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): - with salt.utils.files.fopen('foo.txt', 'wb') as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): + with salt.utils.files.fopen("foo.txt", "wb") as self.fh: for line in self.questions_bytes_lines: self.fh.write(line) - assert self.fh.write_calls == self.questions_bytes_lines, self.fh.write_calls + assert ( + self.fh.write_calls == self.questions_bytes_lines + ), self.fh.write_calls # Test opening for binary writing using "ab" - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): - with salt.utils.files.fopen('foo.txt', 'ab') as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): + with salt.utils.files.fopen("foo.txt", "ab") as self.fh: for line in self.questions_bytes_lines: self.fh.write(line) - assert self.fh.write_calls == self.questions_bytes_lines, self.fh.write_calls + assert ( + self.fh.write_calls == self.questions_bytes_lines + ), self.fh.write_calls # Test opening for read-and-write using "r+b" - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): - with salt.utils.files.fopen('foo.txt', 'r+b') as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): + with salt.utils.files.fopen("foo.txt", "r+b") as self.fh: for line in self.questions_bytes_lines: self.fh.write(line) - assert self.fh.write_calls == self.questions_bytes_lines, self.fh.write_calls + assert ( + self.fh.write_calls == self.questions_bytes_lines + ), self.fh.write_calls # Test trying to write str types to a binary filehandle - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): - with salt.utils.files.fopen('foo.txt', 'wb') as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): + with salt.utils.files.fopen("foo.txt", "wb") as self.fh: try: - self.fh.write('foo\n') + self.fh.write("foo\n") except TypeError: # This exception is expected on Python 3 if not six.PY3: @@ -595,8 +620,8 @@ class MockOpenTestCase(TestCase, MockOpenMixin): # This write should work fine on Python 2 if six.PY3: raise Exception( - 'Should not have been able to write a str to a ' - 'binary filehandle' + "Should not have been able to write a str to a " + "binary filehandle" ) if six.PY2: @@ -611,15 +636,15 @@ class MockOpenTestCase(TestCase, MockOpenMixin): pass else: raise Exception( - 'Should not have been able to write non-ascii ' - 'unicode to a binary filehandle' + "Should not have been able to write non-ascii " + "unicode to a binary filehandle" ) # Test trying to write bytestrings to a non-binary filehandle - with patch('salt.utils.files.fopen', mock_open()): - with salt.utils.files.fopen('foo.txt', 'w') as self.fh: + with patch("salt.utils.files.fopen", mock_open()): + with salt.utils.files.fopen("foo.txt", "w") as self.fh: try: - self.fh.write(b'foo\n') + self.fh.write(b"foo\n") except TypeError: # This exception is expected on Python 3 if not six.PY3: @@ -628,8 +653,8 @@ class MockOpenTestCase(TestCase, MockOpenMixin): # This write should work fine on Python 2 if six.PY3: raise Exception( - 'Should not have been able to write a bytestring ' - 'to a non-binary filehandle' + "Should not have been able to write a bytestring " + "to a non-binary filehandle" ) if six.PY2: @@ -644,43 +669,51 @@ class MockOpenTestCase(TestCase, MockOpenMixin): pass else: raise Exception( - 'Should not have been able to write non-ascii ' - 'unicode to a binary filehandle' + "Should not have been able to write non-ascii " + "unicode to a binary filehandle" ) def test_writelines(self): - ''' + """ Test writing to a filehandle using .writelines() - ''' + """ # Test opening for non-binary writing - with patch('salt.utils.files.fopen', mock_open()): - with salt.utils.files.fopen('foo.txt', 'w') as self.fh: + with patch("salt.utils.files.fopen", mock_open()): + with salt.utils.files.fopen("foo.txt", "w") as self.fh: self.fh.writelines(self.questions_str_lines) - assert self.fh.writelines_calls == [self.questions_str_lines], self.fh.writelines_calls + assert self.fh.writelines_calls == [ + self.questions_str_lines + ], self.fh.writelines_calls # Test opening for binary writing using "wb" - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): - with salt.utils.files.fopen('foo.txt', 'wb') as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): + with salt.utils.files.fopen("foo.txt", "wb") as self.fh: self.fh.writelines(self.questions_bytes_lines) - assert self.fh.writelines_calls == [self.questions_bytes_lines], self.fh.writelines_calls + assert self.fh.writelines_calls == [ + self.questions_bytes_lines + ], self.fh.writelines_calls # Test opening for binary writing using "ab" - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): - with salt.utils.files.fopen('foo.txt', 'ab') as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): + with salt.utils.files.fopen("foo.txt", "ab") as self.fh: self.fh.writelines(self.questions_bytes_lines) - assert self.fh.writelines_calls == [self.questions_bytes_lines], self.fh.writelines_calls + assert self.fh.writelines_calls == [ + self.questions_bytes_lines + ], self.fh.writelines_calls # Test opening for read-and-write using "r+b" - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): - with salt.utils.files.fopen('foo.txt', 'r+b') as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): + with salt.utils.files.fopen("foo.txt", "r+b") as self.fh: self.fh.writelines(self.questions_bytes_lines) - assert self.fh.writelines_calls == [self.questions_bytes_lines], self.fh.writelines_calls + assert self.fh.writelines_calls == [ + self.questions_bytes_lines + ], self.fh.writelines_calls # Test trying to write str types to a binary filehandle - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): - with salt.utils.files.fopen('foo.txt', 'wb') as self.fh: + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): + with salt.utils.files.fopen("foo.txt", "wb") as self.fh: try: - self.fh.writelines(['foo\n']) + self.fh.writelines(["foo\n"]) except TypeError: # This exception is expected on Python 3 if not six.PY3: @@ -689,8 +722,8 @@ class MockOpenTestCase(TestCase, MockOpenMixin): # This write should work fine on Python 2 if six.PY3: raise Exception( - 'Should not have been able to write a str to a ' - 'binary filehandle' + "Should not have been able to write a str to a " + "binary filehandle" ) if six.PY2: @@ -705,15 +738,15 @@ class MockOpenTestCase(TestCase, MockOpenMixin): pass else: raise Exception( - 'Should not have been able to write non-ascii ' - 'unicode to a binary filehandle' + "Should not have been able to write non-ascii " + "unicode to a binary filehandle" ) # Test trying to write bytestrings to a non-binary filehandle - with patch('salt.utils.files.fopen', mock_open()): - with salt.utils.files.fopen('foo.txt', 'w') as self.fh: + with patch("salt.utils.files.fopen", mock_open()): + with salt.utils.files.fopen("foo.txt", "w") as self.fh: try: - self.fh.write([b'foo\n']) + self.fh.write([b"foo\n"]) except TypeError: # This exception is expected on Python 3 if not six.PY3: @@ -722,8 +755,8 @@ class MockOpenTestCase(TestCase, MockOpenMixin): # This write should work fine on Python 2 if six.PY3: raise Exception( - 'Should not have been able to write a bytestring ' - 'to a non-binary filehandle' + "Should not have been able to write a bytestring " + "to a non-binary filehandle" ) if six.PY2: @@ -738,47 +771,47 @@ class MockOpenTestCase(TestCase, MockOpenMixin): pass else: raise Exception( - 'Should not have been able to write non-ascii ' - 'unicode to a binary filehandle' + "Should not have been able to write non-ascii " + "unicode to a binary filehandle" ) def test_open(self): - ''' + """ Test that opening a file for binary reading with string read_data fails, and that the same thing happens for non-binary filehandles and bytestring read_data. NOTE: This test should always pass on PY2 since MockOpen will normalize unicode types to str types. - ''' + """ try: - with patch('salt.utils.files.fopen', mock_open()): + with patch("salt.utils.files.fopen", mock_open()): try: - with salt.utils.files.fopen('foo.txt', 'rb') as self.fh: + with salt.utils.files.fopen("foo.txt", "rb") as self.fh: self.fh.read() except TypeError: pass else: if six.PY3: raise Exception( - 'Should not have been able open for binary read with ' - 'non-bytestring read_data' + "Should not have been able open for binary read with " + "non-bytestring read_data" ) - with patch('salt.utils.files.fopen', mock_open(read_data=b'')): + with patch("salt.utils.files.fopen", mock_open(read_data=b"")): try: - with salt.utils.files.fopen('foo.txt', 'r') as self.fh2: + with salt.utils.files.fopen("foo.txt", "r") as self.fh2: self.fh2.read() except TypeError: pass else: if six.PY3: raise Exception( - 'Should not have been able open for non-binary read ' - 'with bytestring read_data' + "Should not have been able open for non-binary read " + "with bytestring read_data" ) finally: # Make sure we destroy the filehandles before the teardown, as they # will also try to read and this will generate another exception - delattr(self, 'fh') - delattr(self, 'fh2') + delattr(self, "fh") + delattr(self, "fh2") diff --git a/tests/unit/test_module_names.py b/tests/unit/test_module_names.py index 8528285c282..3b1821815da 100644 --- a/tests/unit/test_module_names.py +++ b/tests/unit/test_module_names.py @@ -1,63 +1,64 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.test_test_module_name ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import + import fnmatch import os # Import Salt libs import salt.utils.path import salt.utils.stringutils - -# Import Salt Testing libs -from tests.support.unit import TestCase from tests.support.paths import list_test_mods from tests.support.runtests import RUNTIME_VARS +# Import Salt Testing libs +from tests.support.unit import TestCase + EXCLUDED_DIRS = [ - os.path.join('tests', 'pkg'), - os.path.join('tests', 'perf'), - os.path.join('tests', 'support'), - os.path.join('tests', 'unit', 'utils', 'cache_mods'), - os.path.join('tests', 'unit', 'modules', 'inspectlib'), - os.path.join('tests', 'unit', 'modules', 'zypp'), - os.path.join('tests', 'unit', 'templates', 'files'), - os.path.join('tests', 'integration', 'files'), - os.path.join('tests', 'unit', 'files'), - os.path.join('tests', 'integration', 'cloud', 'helpers'), - os.path.join('tests', 'kitchen', 'tests'), + os.path.join("tests", "pkg"), + os.path.join("tests", "perf"), + os.path.join("tests", "support"), + os.path.join("tests", "unit", "utils", "cache_mods"), + os.path.join("tests", "unit", "modules", "inspectlib"), + os.path.join("tests", "unit", "modules", "zypp"), + os.path.join("tests", "unit", "templates", "files"), + os.path.join("tests", "integration", "files"), + os.path.join("tests", "unit", "files"), + os.path.join("tests", "integration", "cloud", "helpers"), + os.path.join("tests", "kitchen", "tests"), ] INCLUDED_DIRS = [ - os.path.join('tests', 'kitchen', 'tests', '*', 'tests', '*'), + os.path.join("tests", "kitchen", "tests", "*", "tests", "*"), ] EXCLUDED_FILES = [ - os.path.join('tests', 'eventlisten.py'), - os.path.join('tests', 'buildpackage.py'), - os.path.join('tests', 'saltsh.py'), - os.path.join('tests', 'minionswarm.py'), - os.path.join('tests', 'wheeltest.py'), - os.path.join('tests', 'runtests.py'), - os.path.join('tests', 'jenkins.py'), - os.path.join('tests', 'salt-tcpdump.py'), - os.path.join('tests', 'packdump.py'), - os.path.join('tests', 'consist.py'), - os.path.join('tests', 'modparser.py'), - os.path.join('tests', 'virtualname.py'), - os.path.join('tests', 'committer_parser.py'), - os.path.join('tests', 'zypp_plugin.py'), - os.path.join('tests', 'unit', 'transport', 'mixins.py'), - os.path.join('tests', 'integration', 'utils', 'testprogram.py'), + os.path.join("tests", "eventlisten.py"), + os.path.join("tests", "buildpackage.py"), + os.path.join("tests", "saltsh.py"), + os.path.join("tests", "minionswarm.py"), + os.path.join("tests", "wheeltest.py"), + os.path.join("tests", "runtests.py"), + os.path.join("tests", "jenkins.py"), + os.path.join("tests", "salt-tcpdump.py"), + os.path.join("tests", "packdump.py"), + os.path.join("tests", "consist.py"), + os.path.join("tests", "modparser.py"), + os.path.join("tests", "virtualname.py"), + os.path.join("tests", "committer_parser.py"), + os.path.join("tests", "zypp_plugin.py"), + os.path.join("tests", "unit", "transport", "mixins.py"), + os.path.join("tests", "integration", "utils", "testprogram.py"), ] class BadTestModuleNamesTestCase(TestCase): - ''' + """ Unit test case for testing bad names for test modules - ''' + """ maxDiff = None @@ -65,39 +66,43 @@ class BadTestModuleNamesTestCase(TestCase): return any(fnmatch.fnmatchcase(reldir, mdir) for mdir in matchdirs) def test_module_name(self): - ''' + """ Make sure all test modules conform to the test_*.py naming scheme - ''' + """ excluded_dirs, included_dirs = tuple(EXCLUDED_DIRS), tuple(INCLUDED_DIRS) - tests_dir = os.path.join(RUNTIME_VARS.CODE_DIR, 'tests') + tests_dir = os.path.join(RUNTIME_VARS.CODE_DIR, "tests") bad_names = [] for root, _, files in salt.utils.path.os_walk(tests_dir): reldir = os.path.relpath(root, RUNTIME_VARS.CODE_DIR) - if (reldir.startswith(excluded_dirs) and not self._match_dirs(reldir, included_dirs)) \ - or reldir.endswith('__pycache__'): + if ( + reldir.startswith(excluded_dirs) + and not self._match_dirs(reldir, included_dirs) + ) or reldir.endswith("__pycache__"): continue for fname in files: - if fname in ('__init__.py', 'conftest.py') or not fname.endswith('.py'): + if fname in ("__init__.py", "conftest.py") or not fname.endswith(".py"): continue relpath = os.path.join(reldir, fname) if relpath in EXCLUDED_FILES: continue - if not fname.startswith('test_'): + if not fname.startswith("test_"): bad_names.append(relpath) - error_msg = '\n\nPlease rename the following files:\n' + error_msg = "\n\nPlease rename the following files:\n" for path in bad_names: directory, filename = path.rsplit(os.sep, 1) filename, _ = os.path.splitext(filename) - error_msg += ' {} -> {}/test_{}.py\n'.format(path, directory, filename.split('_test')[0]) + error_msg += " {} -> {}/test_{}.py\n".format( + path, directory, filename.split("_test")[0] + ) - error_msg += '\nIf you believe one of the entries above should be ignored, please add it to either\n' - error_msg += '\'EXCLUDED_DIRS\' or \'EXCLUDED_FILES\' in \'tests/unit/test_module_names.py\'.\n' - error_msg += 'If it is a tests module, then please rename as suggested.' + error_msg += "\nIf you believe one of the entries above should be ignored, please add it to either\n" + error_msg += "'EXCLUDED_DIRS' or 'EXCLUDED_FILES' in 'tests/unit/test_module_names.py'.\n" + error_msg += "If it is a tests module, then please rename as suggested." self.assertEqual([], bad_names, error_msg) def test_module_name_source_match(self): - ''' + """ Check all the test mods and check if they correspond to actual files in the codebase. If this test fails, then a test module is likely not named correctly, and should be adjusted. @@ -106,106 +111,109 @@ class BadTestModuleNamesTestCase(TestCase): file), then its should be included in the "ignore" tuple below. However, if there is no matching source code file, then you should consider mapping it to files manually via tests/filename_map.yml. - ''' + """ ignore = ( - 'unit.test_doc', - 'unit.test_mock', - 'unit.test_module_names', - 'unit.test_virtualname', - 'unit.test_simple', - 'unit.test_zypp_plugins', - 'unit.test_proxy_minion', - 'unit.cache.test_cache', - 'unit.serializers.test_serializers', - 'unit.states.test_postgres', - 'integration.cli.test_custom_module', - 'integration.cli.test_grains', - 'integration.client.test_kwarg', - 'integration.client.test_runner', - 'integration.client.test_standard', - 'integration.client.test_syndic', - 'integration.cloud.test_cloud', - 'integration.doc.test_man', - 'integration.externalapi.test_venafiapi', - 'integration.grains.test_custom', - 'integration.loader.test_ext_grains', - 'integration.loader.test_ext_modules', - 'integration.logging.test_jid_logging', - 'integration.logging.handlers.test_logstash_mod', - 'integration.master.test_event_return', - 'integration.minion.test_blackout', - 'integration.minion.test_executor', - 'integration.minion.test_minion_cache', - 'integration.minion.test_pillar', - 'integration.minion.test_timeout', - 'integration.modules.test_decorators', - 'integration.modules.test_pkg', - 'integration.modules.test_state_jinja_filters', - 'integration.modules.test_sysctl', - 'integration.netapi.test_client', - 'integration.netapi.rest_tornado.test_app', - 'integration.netapi.rest_cherrypy.test_app_pam', - 'integration.output.test_output', - 'integration.pillar.test_pillar_include', - 'integration.proxy.test_shell', - 'integration.proxy.test_simple', - 'integration.reactor.test_reactor', - 'integration.returners.test_noop_return', - 'integration.runners.test_runner_returns', - 'integration.scheduler.test_error', - 'integration.scheduler.test_eval', - 'integration.scheduler.test_postpone', - 'integration.scheduler.test_skip', - 'integration.scheduler.test_maxrunning', - 'integration.scheduler.test_helpers', - 'integration.scheduler.test_run_job', - 'integration.shell.test_spm', - 'integration.shell.test_cp', - 'integration.shell.test_syndic', - 'integration.shell.test_proxy', - 'integration.shell.test_auth', - 'integration.shell.test_call', - 'integration.shell.test_arguments', - 'integration.shell.test_matcher', - 'integration.shell.test_master_tops', - 'integration.shell.test_saltcli', - 'integration.shell.test_master', - 'integration.shell.test_key', - 'integration.shell.test_runner', - 'integration.shell.test_cloud', - 'integration.shell.test_enabled', - 'integration.shell.test_minion', - 'integration.spm.test_build', - 'integration.spm.test_files', - 'integration.spm.test_info', - 'integration.spm.test_install', - 'integration.spm.test_remove', - 'integration.spm.test_repo', - 'integration.ssh.test_deploy', - 'integration.ssh.test_grains', - 'integration.ssh.test_jinja_filters', - 'integration.ssh.test_master', - 'integration.ssh.test_mine', - 'integration.ssh.test_pillar', - 'integration.ssh.test_raw', - 'integration.ssh.test_saltcheck', - 'integration.ssh.test_state', - 'integration.states.test_compiler', - 'integration.states.test_handle_error', - 'integration.states.test_handle_iorder', - 'integration.states.test_match', - 'integration.states.test_renderers', - 'integration.wheel.test_client', - 'multimaster.minion.test_event', + "unit.test_doc", + "unit.test_mock", + "unit.test_module_names", + "unit.test_virtualname", + "unit.test_simple", + "unit.test_zypp_plugins", + "unit.test_proxy_minion", + "unit.cache.test_cache", + "unit.serializers.test_serializers", + "unit.states.test_postgres", + "integration.cli.test_custom_module", + "integration.cli.test_grains", + "integration.client.test_kwarg", + "integration.client.test_runner", + "integration.client.test_standard", + "integration.client.test_syndic", + "integration.cloud.test_cloud", + "integration.doc.test_man", + "integration.externalapi.test_venafiapi", + "integration.grains.test_custom", + "integration.loader.test_ext_grains", + "integration.loader.test_ext_modules", + "integration.logging.test_jid_logging", + "integration.logging.handlers.test_logstash_mod", + "integration.master.test_event_return", + "integration.minion.test_blackout", + "integration.minion.test_executor", + "integration.minion.test_minion_cache", + "integration.minion.test_pillar", + "integration.minion.test_timeout", + "integration.modules.test_decorators", + "integration.modules.test_pkg", + "integration.modules.test_state_jinja_filters", + "integration.modules.test_sysctl", + "integration.netapi.test_client", + "integration.netapi.rest_tornado.test_app", + "integration.netapi.rest_cherrypy.test_app_pam", + "integration.output.test_output", + "integration.pillar.test_pillar_include", + "integration.proxy.test_shell", + "integration.proxy.test_simple", + "integration.reactor.test_reactor", + "integration.returners.test_noop_return", + "integration.runners.test_runner_returns", + "integration.scheduler.test_error", + "integration.scheduler.test_eval", + "integration.scheduler.test_postpone", + "integration.scheduler.test_skip", + "integration.scheduler.test_maxrunning", + "integration.scheduler.test_helpers", + "integration.scheduler.test_run_job", + "integration.setup.test_bdist", + "integration.setup.test_egg", + "integration.shell.test_spm", + "integration.shell.test_cp", + "integration.shell.test_syndic", + "integration.shell.test_proxy", + "integration.shell.test_auth", + "integration.shell.test_call", + "integration.shell.test_arguments", + "integration.shell.test_matcher", + "integration.shell.test_master_tops", + "integration.shell.test_saltcli", + "integration.shell.test_master", + "integration.shell.test_key", + "integration.shell.test_runner", + "integration.shell.test_cloud", + "integration.shell.test_enabled", + "integration.shell.test_minion", + "integration.spm.test_build", + "integration.spm.test_files", + "integration.spm.test_info", + "integration.spm.test_install", + "integration.spm.test_remove", + "integration.spm.test_repo", + "integration.ssh.test_deploy", + "integration.ssh.test_grains", + "integration.ssh.test_jinja_filters", + "integration.ssh.test_master", + "integration.ssh.test_mine", + "integration.ssh.test_pillar", + "integration.ssh.test_raw", + "integration.ssh.test_saltcheck", + "integration.ssh.test_state", + "integration.ssh.test_pre_flight", + "integration.states.test_compiler", + "integration.states.test_handle_error", + "integration.states.test_handle_iorder", + "integration.states.test_match", + "integration.states.test_renderers", + "integration.wheel.test_client", + "multimaster.minion.test_event", ) errors = [] def _format_errors(errors): msg = ( - 'The following {0} test module(s) could not be matched to a ' - 'source code file:\n\n'.format(len(errors)) + "The following {0} test module(s) could not be matched to a " + "source code file:\n\n".format(len(errors)) ) - msg += ''.join(errors) + msg += "".join(errors) return msg for mod_name in list_test_mods(): @@ -215,33 +223,30 @@ class BadTestModuleNamesTestCase(TestCase): # Separate the test_foo away from the rest of the mod name, because # we'll need to remove the "test_" from the beginning and add .py - stem, flower = mod_name.rsplit('.', 1) + stem, flower = mod_name.rsplit(".", 1) # Lop off the integration/unit from the beginning of the mod name try: - stem = stem.split('.', 1)[1] + stem = stem.split(".", 1)[1] except IndexError: # This test mod was in the root of the unit/integration dir - stem = '' + stem = "" # The path from the root of the repo relpath = salt.utils.path.join( - 'salt', - stem.replace('.', os.sep), - '.'.join((flower[5:], 'py'))) + "salt", stem.replace(".", os.sep), ".".join((flower[5:], "py")) + ) # The full path to the file we expect to find abspath = salt.utils.path.join(RUNTIME_VARS.CODE_DIR, relpath) if not os.path.isfile(abspath): # Maybe this is in a dunder init? - alt_relpath = salt.utils.path.join(relpath[:-3], '__init__.py') - alt_abspath = salt.utils.path.join(abspath[:-3], '__init__.py') + alt_relpath = salt.utils.path.join(relpath[:-3], "__init__.py") + alt_abspath = salt.utils.path.join(abspath[:-3], "__init__.py") if os.path.isfile(alt_abspath): # Yep, it is. Carry on! continue - errors.append( - '{0} (expected: {1})\n'.format(mod_name, relpath) - ) + errors.append("{0} (expected: {1})\n".format(mod_name, relpath)) assert not errors, _format_errors(errors) diff --git a/tests/unit/test_payload.py b/tests/unit/test_payload.py index 099edb6bd7b..747d28efd3f 100644 --- a/tests/unit/test_payload.py +++ b/tests/unit/test_payload.py @@ -1,25 +1,21 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.unit.payload_test ~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import time -import errno -import threading + import datetime +import errno +import logging +import threading +import time -# Import Salt Testing libs -from tests.support.unit import skipIf, TestCase - -# Import Salt libs -from salt.utils import immutabletypes -from salt.utils.odict import OrderedDict import salt.exceptions import salt.payload @@ -27,18 +23,20 @@ import salt.payload import zmq from salt.ext import six -import logging +# Import Salt libs +from salt.utils import immutabletypes +from salt.utils.odict import OrderedDict + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf log = logging.getLogger(__name__) class PayloadTestCase(TestCase): - def assertNoOrderedDict(self, data): if isinstance(data, OrderedDict): - raise AssertionError( - 'Found an ordered dictionary' - ) + raise AssertionError("Found an ordered dictionary") if isinstance(data, dict): for value in six.itervalues(data): self.assertNoOrderedDict(value) @@ -47,120 +45,125 @@ class PayloadTestCase(TestCase): self.assertNoOrderedDict(chunk) def test_list_nested_odicts(self): - payload = salt.payload.Serial('msgpack') - idata = {'pillar': [OrderedDict(environment='dev')]} + payload = salt.payload.Serial("msgpack") + idata = {"pillar": [OrderedDict(environment="dev")]} odata = payload.loads(payload.dumps(idata.copy())) self.assertNoOrderedDict(odata) self.assertEqual(idata, odata) def test_datetime_dump_load(self): - ''' + """ Check the custom datetime handler can understand itself - ''' - payload = salt.payload.Serial('msgpack') + """ + payload = salt.payload.Serial("msgpack") dtvalue = datetime.datetime(2001, 2, 3, 4, 5, 6, 7) idata = {dtvalue: dtvalue} sdata = payload.dumps(idata.copy()) odata = payload.loads(sdata) self.assertEqual( - sdata, - b'\x81\xc7\x18N20010203T04:05:06.000007\xc7\x18N20010203T04:05:06.000007') + sdata, + b"\x81\xc7\x18N20010203T04:05:06.000007\xc7\x18N20010203T04:05:06.000007", + ) self.assertEqual(idata, odata) def test_verylong_dump_load(self): - ''' + """ Test verylong encoder/decoder - ''' - payload = salt.payload.Serial('msgpack') - idata = {'jid': 20180227140750302662} + """ + payload = salt.payload.Serial("msgpack") + idata = {"jid": 20180227140750302662} sdata = payload.dumps(idata.copy()) odata = payload.loads(sdata) - idata['jid'] = '{0}'.format(idata['jid']) + idata["jid"] = "{0}".format(idata["jid"]) self.assertEqual(idata, odata) def test_immutable_dict_dump_load(self): - ''' + """ Test immutable dict encoder/decoder - ''' - payload = salt.payload.Serial('msgpack') - idata = {'dict': {'key': 'value'}} - sdata = payload.dumps({'dict': immutabletypes.ImmutableDict(idata['dict'])}) + """ + payload = salt.payload.Serial("msgpack") + idata = {"dict": {"key": "value"}} + sdata = payload.dumps({"dict": immutabletypes.ImmutableDict(idata["dict"])}) odata = payload.loads(sdata) self.assertEqual(idata, odata) def test_immutable_list_dump_load(self): - ''' + """ Test immutable list encoder/decoder - ''' - payload = salt.payload.Serial('msgpack') - idata = {'list': [1, 2, 3]} - sdata = payload.dumps({'list': immutabletypes.ImmutableList(idata['list'])}) + """ + payload = salt.payload.Serial("msgpack") + idata = {"list": [1, 2, 3]} + sdata = payload.dumps({"list": immutabletypes.ImmutableList(idata["list"])}) odata = payload.loads(sdata) self.assertEqual(idata, odata) def test_immutable_set_dump_load(self): - ''' + """ Test immutable set encoder/decoder - ''' - payload = salt.payload.Serial('msgpack') - idata = {'set': ['red', 'green', 'blue']} - sdata = payload.dumps({'set': immutabletypes.ImmutableSet(idata['set'])}) + """ + payload = salt.payload.Serial("msgpack") + idata = {"set": ["red", "green", "blue"]} + sdata = payload.dumps({"set": immutabletypes.ImmutableSet(idata["set"])}) odata = payload.loads(sdata) self.assertEqual(idata, odata) def test_odict_dump_load(self): - ''' + """ Test odict just works. It wasn't until msgpack 0.2.0 - ''' - payload = salt.payload.Serial('msgpack') + """ + payload = salt.payload.Serial("msgpack") data = OrderedDict() - data['a'] = 'b' - data['y'] = 'z' - data['j'] = 'k' - data['w'] = 'x' - sdata = payload.dumps({'set': data}) + data["a"] = "b" + data["y"] = "z" + data["j"] = "k" + data["w"] = "x" + sdata = payload.dumps({"set": data}) odata = payload.loads(sdata) - self.assertEqual({'set': dict(data)}, odata) + self.assertEqual({"set": dict(data)}, odata) def test_mixed_dump_load(self): - ''' + """ Test we can handle all exceptions at once - ''' - payload = salt.payload.Serial('msgpack') + """ + payload = salt.payload.Serial("msgpack") dtvalue = datetime.datetime(2001, 2, 3, 4, 5, 6, 7) od = OrderedDict() - od['a'] = 'b' - od['y'] = 'z' - od['j'] = 'k' - od['w'] = 'x' - idata = {dtvalue: dtvalue, # datetime - 'jid': 20180227140750302662, # long int - 'dict': immutabletypes.ImmutableDict({'key': 'value'}), # immutable dict - 'list': immutabletypes.ImmutableList([1, 2, 3]), # immutable list - 'set': immutabletypes.ImmutableSet(('red', 'green', 'blue')), # immutable set - 'odict': od, # odict - } - edata = {dtvalue: dtvalue, # datetime, == input - 'jid': '20180227140750302662', # string repr of long int - 'dict': {'key': 'value'}, # builtin dict - 'list': [1, 2, 3], # builtin list - 'set': ['red', 'green', 'blue'], # builtin set - 'odict': dict(od), # builtin dict - } + od["a"] = "b" + od["y"] = "z" + od["j"] = "k" + od["w"] = "x" + idata = { + dtvalue: dtvalue, # datetime + "jid": 20180227140750302662, # long int + "dict": immutabletypes.ImmutableDict({"key": "value"}), # immutable dict + "list": immutabletypes.ImmutableList([1, 2, 3]), # immutable list + "set": immutabletypes.ImmutableSet( + ("red", "green", "blue") + ), # immutable set + "odict": od, # odict + } + edata = { + dtvalue: dtvalue, # datetime, == input + "jid": "20180227140750302662", # string repr of long int + "dict": {"key": "value"}, # builtin dict + "list": [1, 2, 3], # builtin list + "set": ["red", "green", "blue"], # builtin set + "odict": dict(od), # builtin dict + } sdata = payload.dumps(idata) odata = payload.loads(sdata) self.assertEqual(edata, odata) def test_recursive_dump_load(self): - ''' + """ Test recursive payloads are (mostly) serialized - ''' - payload = salt.payload.Serial('msgpack') - data = {'name': 'roscivs'} - data['data'] = data # Data all the things! + """ + payload = salt.payload.Serial("msgpack") + data = {"name": "roscivs"} + data["data"] = data # Data all the things! sdata = payload.dumps(data) odata = payload.loads(sdata) - self.assertTrue('recursion' in odata['data'].lower()) + self.assertTrue("recursion" in odata["data"].lower()) class SREQTestCase(TestCase): @@ -168,35 +171,41 @@ class SREQTestCase(TestCase): @classmethod def setUpClass(cls): - ''' + """ Class to set up zmq echo socket - ''' + """ + def echo_server(): - ''' + """ A server that echos the message sent to it over zmq Optional "sleep" can be sent to delay response - ''' + """ context = zmq.Context() socket = context.socket(zmq.REP) socket.bind("tcp://*:{0}".format(SREQTestCase.port)) - payload = salt.payload.Serial('msgpack') + payload = salt.payload.Serial("msgpack") while SREQTestCase.thread_running.is_set(): try: # Wait for next request from client message = socket.recv(zmq.NOBLOCK) msg_deserialized = payload.loads(message) - log.info('Echo server received message: %s', msg_deserialized) - if isinstance(msg_deserialized['load'], dict) and msg_deserialized['load'].get('sleep'): - log.info('Test echo server sleeping for %s seconds', - msg_deserialized['load']['sleep']) - time.sleep(msg_deserialized['load']['sleep']) + log.info("Echo server received message: %s", msg_deserialized) + if isinstance(msg_deserialized["load"], dict) and msg_deserialized[ + "load" + ].get("sleep"): + log.info( + "Test echo server sleeping for %s seconds", + msg_deserialized["load"]["sleep"], + ) + time.sleep(msg_deserialized["load"]["sleep"]) socket.send(message) except zmq.ZMQError as exc: if exc.errno == errno.EAGAIN: continue raise + SREQTestCase.thread_running = threading.Event() SREQTestCase.thread_running.set() SREQTestCase.echo_server = threading.Thread(target=echo_server) @@ -204,96 +213,96 @@ class SREQTestCase(TestCase): @classmethod def tearDownClass(cls): - ''' + """ Remove echo server - ''' + """ # kill the thread SREQTestCase.thread_running.clear() SREQTestCase.echo_server.join() def get_sreq(self): - return salt.payload.SREQ('tcp://127.0.0.1:{0}'.format(SREQTestCase.port)) + return salt.payload.SREQ("tcp://127.0.0.1:{0}".format(SREQTestCase.port)) def test_send_auto(self): - ''' + """ Test creation, send/rect - ''' + """ sreq = self.get_sreq() # check default of empty load and enc clear - assert sreq.send_auto({}) == {'enc': 'clear', 'load': {}} + assert sreq.send_auto({}) == {"enc": "clear", "load": {}} # check that the load always gets passed - assert sreq.send_auto({'load': 'foo'}) == {'load': 'foo', 'enc': 'clear'} + assert sreq.send_auto({"load": "foo"}) == {"load": "foo", "enc": "clear"} def test_send(self): sreq = self.get_sreq() - assert sreq.send('clear', 'foo') == {'enc': 'clear', 'load': 'foo'} + assert sreq.send("clear", "foo") == {"enc": "clear", "load": "foo"} - @skipIf(True, 'Disabled until we can figure out how to make this more reliable.') + @skipIf(True, "Disabled until we can figure out how to make this more reliable.") def test_timeout(self): - ''' + """ Test SREQ Timeouts - ''' + """ sreq = self.get_sreq() # client-side timeout start = time.time() # This is a try/except instead of an assertRaises because of a possible # subtle bug in zmq wherein a timeout=0 actually exceutes a single poll # before the timeout is reached. - log.info('Sending tries=0, timeout=0') + log.info("Sending tries=0, timeout=0") try: - sreq.send('clear', 'foo', tries=0, timeout=0) + sreq.send("clear", "foo", tries=0, timeout=0) except salt.exceptions.SaltReqTimeoutError: pass assert time.time() - start < 1 # ensure we didn't wait # server-side timeout - log.info('Sending tries=1, timeout=1') + log.info("Sending tries=1, timeout=1") start = time.time() with self.assertRaises(salt.exceptions.SaltReqTimeoutError): - sreq.send('clear', {'sleep': 2}, tries=1, timeout=1) + sreq.send("clear", {"sleep": 2}, tries=1, timeout=1) assert time.time() - start >= 1 # ensure we actually tried once (1s) # server-side timeout with retries - log.info('Sending tries=2, timeout=1') + log.info("Sending tries=2, timeout=1") start = time.time() with self.assertRaises(salt.exceptions.SaltReqTimeoutError): - sreq.send('clear', {'sleep': 2}, tries=2, timeout=1) + sreq.send("clear", {"sleep": 2}, tries=2, timeout=1) assert time.time() - start >= 2 # ensure we actually tried twice (2s) # test a regular send afterwards (to make sure sockets aren't in a twist - log.info('Sending regular send') - assert sreq.send('clear', 'foo') == {'enc': 'clear', 'load': 'foo'} + log.info("Sending regular send") + assert sreq.send("clear", "foo") == {"enc": "clear", "load": "foo"} def test_destroy(self): - ''' + """ Test the __del__ capabilities - ''' + """ sreq = self.get_sreq() # ensure no exceptions when we go to destroy the sreq, since __del__ # swallows exceptions, we have to call destroy directly sreq.destroy() def test_raw_vs_encoding_none(self): - ''' + """ Test that we handle the new raw parameter in 5.0.2 correctly based on encoding. When encoding is None loads should return bytes - ''' - payload = salt.payload.Serial('msgpack') + """ + payload = salt.payload.Serial("msgpack") dtvalue = datetime.datetime(2001, 2, 3, 4, 5, 6, 7) - idata = {dtvalue: 'strval'} + idata = {dtvalue: "strval"} sdata = payload.dumps(idata.copy()) odata = payload.loads(sdata, encoding=None) assert isinstance(odata[dtvalue], six.string_types) def test_raw_vs_encoding_utf8(self): - ''' + """ Test that we handle the new raw parameter in 5.0.2 correctly based on encoding. When encoding is utf-8 loads should return unicode - ''' - payload = salt.payload.Serial('msgpack') + """ + payload = salt.payload.Serial("msgpack") dtvalue = datetime.datetime(2001, 2, 3, 4, 5, 6, 7) - idata = {dtvalue: 'strval'} + idata = {dtvalue: "strval"} sdata = payload.dumps(idata.copy()) - odata = payload.loads(sdata, encoding='utf-8') + odata = payload.loads(sdata, encoding="utf-8") assert isinstance(odata[dtvalue], six.text_type) diff --git a/tests/unit/test_pillar.py b/tests/unit/test_pillar.py index 4db02132a19..7b7cd51eefe 100644 --- a/tests/unit/test_pillar.py +++ b/tests/unit/test_pillar.py @@ -1,28 +1,30 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) :codeauthor: Alexandru Bleotu (alexandru.bleotu@morganstanley.com) tests.unit.pillar_test ~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs from __future__ import absolute_import + import shutil import tempfile -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.helpers import with_tempdir -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +import salt.exceptions # Import salt libs import salt.fileclient import salt.pillar import salt.utils.stringutils -import salt.exceptions +from tests.support.helpers import with_tempdir +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class MockFileclient(object): @@ -43,614 +45,634 @@ class MockFileclient(object): def list_states(*args, **kwargs): raise NotImplementedError() + # pylint: enable=unused-argument,no-method-argument,method-hidden class PillarTestCase(TestCase): - def tearDown(self): - for attrname in ('generic_file', 'generic_minion_file', 'ssh_file', 'ssh_minion_file', 'top_file'): + for attrname in ( + "generic_file", + "generic_minion_file", + "ssh_file", + "ssh_minion_file", + "top_file", + ): try: delattr(self, attrname) except AttributeError: continue def test_pillarenv_from_saltenv(self): - with patch('salt.pillar.compile_template') as compile_template: + with patch("salt.pillar.compile_template") as compile_template: opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': { - 'dev': [], - 'base': [] - }, - 'file_roots': { - 'dev': [], - 'base': [] - }, - 'extension_modules': '', - 'pillarenv_from_saltenv': True + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": {"dev": [], "base": []}, + "file_roots": {"dev": [], "base": []}, + "extension_modules": "", + "pillarenv_from_saltenv": True, } grains = { - 'os': 'Ubuntu', + "os": "Ubuntu", } - pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'dev') - self.assertEqual(pillar.opts['saltenv'], 'dev') - self.assertEqual(pillar.opts['pillarenv'], 'dev') + pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "dev") + self.assertEqual(pillar.opts["saltenv"], "dev") + self.assertEqual(pillar.opts["pillarenv"], "dev") def test_ext_pillar_no_extra_minion_data_val_dict(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': { - 'dev': [], - 'base': [] - }, - 'file_roots': { - 'dev': [], - 'base': [] - }, - 'extension_modules': '', - 'pillarenv_from_saltenv': True + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": {"dev": [], "base": []}, + "file_roots": {"dev": [], "base": []}, + "extension_modules": "", + "pillarenv_from_saltenv": True, } mock_ext_pillar_func = MagicMock() - with patch('salt.loader.pillars', - MagicMock(return_value={'fake_ext_pillar': - mock_ext_pillar_func})): - pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev') + with patch( + "salt.loader.pillars", + MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), + ): + pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "dev") # ext pillar function doesn't have the extra_minion_data arg - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=[]))): - pillar._external_pillar_data('fake_pillar', {'arg': 'foo'}, - 'fake_ext_pillar') - mock_ext_pillar_func.assert_called_once_with('mocked-minion', - 'fake_pillar', - arg='foo') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=[])), + ): + pillar._external_pillar_data( + "fake_pillar", {"arg": "foo"}, "fake_ext_pillar" + ) + mock_ext_pillar_func.assert_called_once_with( + "mocked-minion", "fake_pillar", arg="foo" + ) # ext pillar function has the extra_minion_data arg mock_ext_pillar_func.reset_mock() - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=['extra_minion_data']))): - pillar._external_pillar_data('fake_pillar', {'arg': 'foo'}, - 'fake_ext_pillar') - mock_ext_pillar_func.assert_called_once_with('mocked-minion', - 'fake_pillar', - arg='foo') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=["extra_minion_data"])), + ): + pillar._external_pillar_data( + "fake_pillar", {"arg": "foo"}, "fake_ext_pillar" + ) + mock_ext_pillar_func.assert_called_once_with( + "mocked-minion", "fake_pillar", arg="foo" + ) def test_ext_pillar_no_extra_minion_data_val_list(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': { - 'dev': [], - 'base': [] - }, - 'file_roots': { - 'dev': [], - 'base': [] - }, - 'extension_modules': '', - 'pillarenv_from_saltenv': True + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": {"dev": [], "base": []}, + "file_roots": {"dev": [], "base": []}, + "extension_modules": "", + "pillarenv_from_saltenv": True, } mock_ext_pillar_func = MagicMock() - with patch('salt.loader.pillars', - MagicMock(return_value={'fake_ext_pillar': - mock_ext_pillar_func})): - pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev') + with patch( + "salt.loader.pillars", + MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), + ): + pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "dev") # ext pillar function doesn't have the extra_minion_data arg - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=[]))): - pillar._external_pillar_data('fake_pillar', ['foo'], - 'fake_ext_pillar') - mock_ext_pillar_func.assert_called_once_with('mocked-minion', - 'fake_pillar', - 'foo') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=[])), + ): + pillar._external_pillar_data("fake_pillar", ["foo"], "fake_ext_pillar") + mock_ext_pillar_func.assert_called_once_with( + "mocked-minion", "fake_pillar", "foo" + ) # ext pillar function has the extra_minion_data arg mock_ext_pillar_func.reset_mock() - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=['extra_minion_data']))): - pillar._external_pillar_data('fake_pillar', ['foo'], - 'fake_ext_pillar') - mock_ext_pillar_func.assert_called_once_with('mocked-minion', - 'fake_pillar', - 'foo') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=["extra_minion_data"])), + ): + pillar._external_pillar_data("fake_pillar", ["foo"], "fake_ext_pillar") + mock_ext_pillar_func.assert_called_once_with( + "mocked-minion", "fake_pillar", "foo" + ) def test_ext_pillar_no_extra_minion_data_val_elem(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': { - 'dev': [], - 'base': [] - }, - 'file_roots': { - 'dev': [], - 'base': [] - }, - 'extension_modules': '', - 'pillarenv_from_saltenv': True + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": {"dev": [], "base": []}, + "file_roots": {"dev": [], "base": []}, + "extension_modules": "", + "pillarenv_from_saltenv": True, } mock_ext_pillar_func = MagicMock() - with patch('salt.loader.pillars', - MagicMock(return_value={'fake_ext_pillar': - mock_ext_pillar_func})): - pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev') + with patch( + "salt.loader.pillars", + MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), + ): + pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "dev") # ext pillar function doesn't have the extra_minion_data arg - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=[]))): - pillar._external_pillar_data('fake_pillar', 'fake_val', - 'fake_ext_pillar') - mock_ext_pillar_func.assert_called_once_with('mocked-minion', - 'fake_pillar', 'fake_val') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=[])), + ): + pillar._external_pillar_data("fake_pillar", "fake_val", "fake_ext_pillar") + mock_ext_pillar_func.assert_called_once_with( + "mocked-minion", "fake_pillar", "fake_val" + ) # ext pillar function has the extra_minion_data arg mock_ext_pillar_func.reset_mock() - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=['extra_minion_data']))): - pillar._external_pillar_data('fake_pillar', 'fake_val', - 'fake_ext_pillar') - mock_ext_pillar_func.assert_called_once_with('mocked-minion', - 'fake_pillar', 'fake_val') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=["extra_minion_data"])), + ): + pillar._external_pillar_data("fake_pillar", "fake_val", "fake_ext_pillar") + mock_ext_pillar_func.assert_called_once_with( + "mocked-minion", "fake_pillar", "fake_val" + ) def test_ext_pillar_with_extra_minion_data_val_dict(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': { - 'dev': [], - 'base': [] - }, - 'file_roots': { - 'dev': [], - 'base': [] - }, - 'extension_modules': '', - 'pillarenv_from_saltenv': True + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": {"dev": [], "base": []}, + "file_roots": {"dev": [], "base": []}, + "extension_modules": "", + "pillarenv_from_saltenv": True, } mock_ext_pillar_func = MagicMock() - with patch('salt.loader.pillars', - MagicMock(return_value={'fake_ext_pillar': - mock_ext_pillar_func})): - pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev', - extra_minion_data={'fake_key': 'foo'}) + with patch( + "salt.loader.pillars", + MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), + ): + pillar = salt.pillar.Pillar( + opts, {}, "mocked-minion", "dev", extra_minion_data={"fake_key": "foo"} + ) # ext pillar function doesn't have the extra_minion_data arg - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=[]))): - pillar._external_pillar_data('fake_pillar', {'arg': 'foo'}, - 'fake_ext_pillar') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=[])), + ): + pillar._external_pillar_data( + "fake_pillar", {"arg": "foo"}, "fake_ext_pillar" + ) mock_ext_pillar_func.assert_called_once_with( - 'mocked-minion', 'fake_pillar', arg='foo') + "mocked-minion", "fake_pillar", arg="foo" + ) # ext pillar function has the extra_minion_data arg mock_ext_pillar_func.reset_mock() - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=['extra_minion_data']))): - pillar._external_pillar_data('fake_pillar', {'arg': 'foo'}, - 'fake_ext_pillar') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=["extra_minion_data"])), + ): + pillar._external_pillar_data( + "fake_pillar", {"arg": "foo"}, "fake_ext_pillar" + ) mock_ext_pillar_func.assert_called_once_with( - 'mocked-minion', 'fake_pillar', arg='foo', - extra_minion_data={'fake_key': 'foo'}) + "mocked-minion", + "fake_pillar", + arg="foo", + extra_minion_data={"fake_key": "foo"}, + ) def test_ext_pillar_with_extra_minion_data_val_list(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': { - 'dev': [], - 'base': [] - }, - 'file_roots': { - 'dev': [], - 'base': [] - }, - 'extension_modules': '', - 'pillarenv_from_saltenv': True + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": {"dev": [], "base": []}, + "file_roots": {"dev": [], "base": []}, + "extension_modules": "", + "pillarenv_from_saltenv": True, } mock_ext_pillar_func = MagicMock() - with patch('salt.loader.pillars', - MagicMock(return_value={'fake_ext_pillar': - mock_ext_pillar_func})): - pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev', - extra_minion_data={'fake_key': 'foo'}) + with patch( + "salt.loader.pillars", + MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), + ): + pillar = salt.pillar.Pillar( + opts, {}, "mocked-minion", "dev", extra_minion_data={"fake_key": "foo"} + ) # ext pillar function doesn't have the extra_minion_data arg - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=[]))): - pillar._external_pillar_data('fake_pillar', ['bar'], - 'fake_ext_pillar') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=[])), + ): + pillar._external_pillar_data("fake_pillar", ["bar"], "fake_ext_pillar") mock_ext_pillar_func.assert_called_once_with( - 'mocked-minion', 'fake_pillar', 'bar') + "mocked-minion", "fake_pillar", "bar" + ) # ext pillar function has the extra_minion_data arg mock_ext_pillar_func.reset_mock() - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=['extra_minion_data']))): - pillar._external_pillar_data('fake_pillar', ['bar'], - 'fake_ext_pillar') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=["extra_minion_data"])), + ): + pillar._external_pillar_data("fake_pillar", ["bar"], "fake_ext_pillar") mock_ext_pillar_func.assert_called_once_with( - 'mocked-minion', 'fake_pillar', 'bar', - extra_minion_data={'fake_key': 'foo'}) + "mocked-minion", "fake_pillar", "bar", extra_minion_data={"fake_key": "foo"} + ) def test_ext_pillar_with_extra_minion_data_val_elem(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': { - 'dev': [], - 'base': [] - }, - 'file_roots': { - 'dev': [], - 'base': [] - }, - 'extension_modules': '', - 'pillarenv_from_saltenv': True + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": {"dev": [], "base": []}, + "file_roots": {"dev": [], "base": []}, + "extension_modules": "", + "pillarenv_from_saltenv": True, } mock_ext_pillar_func = MagicMock() - with patch('salt.loader.pillars', - MagicMock(return_value={'fake_ext_pillar': - mock_ext_pillar_func})): - pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'dev', - extra_minion_data={'fake_key': 'foo'}) + with patch( + "salt.loader.pillars", + MagicMock(return_value={"fake_ext_pillar": mock_ext_pillar_func}), + ): + pillar = salt.pillar.Pillar( + opts, {}, "mocked-minion", "dev", extra_minion_data={"fake_key": "foo"} + ) # ext pillar function doesn't have the extra_minion_data arg - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=[]))): - pillar._external_pillar_data('fake_pillar', 'bar', - 'fake_ext_pillar') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=[])), + ): + pillar._external_pillar_data("fake_pillar", "bar", "fake_ext_pillar") mock_ext_pillar_func.assert_called_once_with( - 'mocked-minion', 'fake_pillar', 'bar') + "mocked-minion", "fake_pillar", "bar" + ) # ext pillar function has the extra_minion_data arg mock_ext_pillar_func.reset_mock() - with patch('salt.utils.args.get_function_argspec', - MagicMock(return_value=MagicMock(args=['extra_minion_data']))): - pillar._external_pillar_data('fake_pillar', 'bar', - 'fake_ext_pillar') + with patch( + "salt.utils.args.get_function_argspec", + MagicMock(return_value=MagicMock(args=["extra_minion_data"])), + ): + pillar._external_pillar_data("fake_pillar", "bar", "fake_ext_pillar") mock_ext_pillar_func.assert_called_once_with( - 'mocked-minion', 'fake_pillar', 'bar', - extra_minion_data={'fake_key': 'foo'}) + "mocked-minion", "fake_pillar", "bar", extra_minion_data={"fake_key": "foo"} + ) def test_ext_pillar_first(self): - ''' + """ test when using ext_pillar and ext_pillar_first - ''' + """ opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'yaml', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': [], - 'extension_modules': '', - 'saltenv': 'base', - 'file_roots': [], - 'ext_pillar_first': True, + "optimization_order": [0, 1, 2], + "renderer": "yaml", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": [], + "extension_modules": "", + "saltenv": "base", + "file_roots": [], + "ext_pillar_first": True, } grains = { - 'os': 'Ubuntu', - 'os_family': 'Debian', - 'oscodename': 'raring', - 'osfullname': 'Ubuntu', - 'osrelease': '13.04', - 'kernel': 'Linux' + "os": "Ubuntu", + "os_family": "Debian", + "oscodename": "raring", + "osfullname": "Ubuntu", + "osrelease": "13.04", + "kernel": "Linux", } tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) try: - sls_files = self._setup_test_topfile_sls_pillar_match( - tempdir,) + sls_files = self._setup_test_topfile_sls_pillar_match(tempdir,) fc_mock = MockFileclient( - cache_file=sls_files['top']['dest'], - list_states=['top', 'ssh', 'ssh.minion', - 'generic', 'generic.minion'], - get_state=sls_files) - with patch.object(salt.fileclient, 'get_file_client', - MagicMock(return_value=fc_mock)), \ - patch('salt.pillar.Pillar.ext_pillar', - MagicMock(return_value=({'id': 'minion', - 'phase': 'alpha', 'role': - 'database'}, []))): - pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base') - self.assertEqual(pillar.compile_pillar()['generic']['key1'], 'value1') + cache_file=sls_files["top"]["dest"], + list_states=["top", "ssh", "ssh.minion", "generic", "generic.minion"], + get_state=sls_files, + ) + with patch.object( + salt.fileclient, "get_file_client", MagicMock(return_value=fc_mock) + ), patch( + "salt.pillar.Pillar.ext_pillar", + MagicMock( + return_value=( + {"id": "minion", "phase": "alpha", "role": "database"}, + [], + ) + ), + ): + pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") + self.assertEqual(pillar.compile_pillar()["generic"]["key1"], "value1") finally: shutil.rmtree(tempdir, ignore_errors=True) def test_dynamic_pillarenv(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': {'__env__': ['/srv/pillar/__env__'], 'base': ['/srv/pillar/base']}, - 'file_roots': {'base': ['/srv/salt/base'], 'dev': ['/svr/salt/dev']}, - 'extension_modules': '', + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": { + "__env__": ["/srv/pillar/__env__"], + "base": ["/srv/pillar/base"], + }, + "file_roots": {"base": ["/srv/salt/base"], "dev": ["/svr/salt/dev"]}, + "extension_modules": "", } - pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'base', pillarenv='dev') - self.assertEqual(pillar.opts['pillar_roots'], - {'base': ['/srv/pillar/base'], 'dev': ['/srv/pillar/__env__']}) + pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "base", pillarenv="dev") + self.assertEqual( + pillar.opts["pillar_roots"], + {"base": ["/srv/pillar/base"], "dev": ["/srv/pillar/__env__"]}, + ) def test_ignored_dynamic_pillarenv(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': {'__env__': ['/srv/pillar/__env__'], 'base': ['/srv/pillar/base']}, - 'file_roots': {'base': ['/srv/salt/base'], 'dev': ['/svr/salt/dev']}, - 'extension_modules': '', + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": { + "__env__": ["/srv/pillar/__env__"], + "base": ["/srv/pillar/base"], + }, + "file_roots": {"base": ["/srv/salt/base"], "dev": ["/svr/salt/dev"]}, + "extension_modules": "", } - pillar = salt.pillar.Pillar(opts, {}, 'mocked-minion', 'base', pillarenv='base') - self.assertEqual(pillar.opts['pillar_roots'], {'base': ['/srv/pillar/base']}) + pillar = salt.pillar.Pillar(opts, {}, "mocked-minion", "base", pillarenv="base") + self.assertEqual(pillar.opts["pillar_roots"], {"base": ["/srv/pillar/base"]}) - @patch('salt.fileclient.Client.list_states') + @patch("salt.fileclient.Client.list_states") def test_malformed_pillar_sls(self, mock_list_states): - with patch('salt.pillar.compile_template') as compile_template: + with patch("salt.pillar.compile_template") as compile_template: opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': [], - 'file_roots': [], - 'extension_modules': '' + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": [], + "file_roots": [], + "extension_modules": "", } grains = { - 'os': 'Ubuntu', - 'os_family': 'Debian', - 'oscodename': 'raring', - 'osfullname': 'Ubuntu', - 'osrelease': '13.04', - 'kernel': 'Linux' + "os": "Ubuntu", + "os_family": "Debian", + "oscodename": "raring", + "osfullname": "Ubuntu", + "osrelease": "13.04", + "kernel": "Linux", } - mock_list_states.return_value = ['foo', 'blah'] - pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base') + mock_list_states.return_value = ["foo", "blah"] + pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") # Mock getting the proper template files pillar.client.get_state = MagicMock( return_value={ - 'dest': '/path/to/pillar/files/foo.sls', - 'source': 'salt://foo.sls' + "dest": "/path/to/pillar/files/foo.sls", + "source": "salt://foo.sls", } ) # Template compilation returned a string - compile_template.return_value = 'BAHHH' + compile_template.return_value = "BAHHH" self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({}, ['SLS \'foo.sls\' does not render to a dictionary']) + pillar.render_pillar({"base": ["foo.sls"]}), + ({}, ["SLS 'foo.sls' does not render to a dictionary"]), ) # Template compilation returned a list - compile_template.return_value = ['BAHHH'] + compile_template.return_value = ["BAHHH"] self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({}, ['SLS \'foo.sls\' does not render to a dictionary']) + pillar.render_pillar({"base": ["foo.sls"]}), + ({}, ["SLS 'foo.sls' does not render to a dictionary"]), ) # Template compilation returned a dictionary, which is what's expected - compile_template.return_value = {'foo': 'bar'} + compile_template.return_value = {"foo": "bar"} self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({'foo': 'bar'}, []) + pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, []) ) # Test improper includes compile_template.side_effect = [ - {'foo': 'bar', 'include': 'blah'}, - {'foo2': 'bar2'} + {"foo": "bar", "include": "blah"}, + {"foo2": "bar2"}, ] self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({'foo': 'bar', 'include': 'blah'}, - ["Include Declaration in SLS 'foo.sls' is not formed as a list"]) + pillar.render_pillar({"base": ["foo.sls"]}), + ( + {"foo": "bar", "include": "blah"}, + ["Include Declaration in SLS 'foo.sls' is not formed as a list"], + ), ) # Test includes as a list, which is what's expected compile_template.side_effect = [ - {'foo': 'bar', 'include': ['blah']}, - {'foo2': 'bar2'} + {"foo": "bar", "include": ["blah"]}, + {"foo2": "bar2"}, ] self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({'foo': 'bar', 'foo2': 'bar2'}, []) + pillar.render_pillar({"base": ["foo.sls"]}), + ({"foo": "bar", "foo2": "bar2"}, []), ) # Test includes as a list overriding data compile_template.side_effect = [ - {'foo': 'bar', 'include': ['blah']}, - {'foo': 'bar2'} + {"foo": "bar", "include": ["blah"]}, + {"foo": "bar2"}, ] self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({'foo': 'bar'}, []) + pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, []) ) # Test includes using empty key directive compile_template.side_effect = [ - {'foo': 'bar', 'include': [{'blah': {'key': ''}}]}, - {'foo': 'bar2'} + {"foo": "bar", "include": [{"blah": {"key": ""}}]}, + {"foo": "bar2"}, ] self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({'foo': 'bar'}, []) + pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, []) ) # Test includes using simple non-nested key compile_template.side_effect = [ - {'foo': 'bar', 'include': [{'blah': {'key': 'nested'}}]}, - {'foo': 'bar2'} + {"foo": "bar", "include": [{"blah": {"key": "nested"}}]}, + {"foo": "bar2"}, ] self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({'foo': 'bar', 'nested': {'foo': 'bar2'}}, []) + pillar.render_pillar({"base": ["foo.sls"]}), + ({"foo": "bar", "nested": {"foo": "bar2"}}, []), ) # Test includes using nested key compile_template.side_effect = [ - {'foo': 'bar', 'include': [{'blah': {'key': 'nested:level'}}]}, - {'foo': 'bar2'} + {"foo": "bar", "include": [{"blah": {"key": "nested:level"}}]}, + {"foo": "bar2"}, ] self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({'foo': 'bar', 'nested': {'level': {'foo': 'bar2'}}}, []) + pillar.render_pillar({"base": ["foo.sls"]}), + ({"foo": "bar", "nested": {"level": {"foo": "bar2"}}}, []), ) def test_includes_override_sls(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'json', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': {}, - 'file_roots': {}, - 'extension_modules': '' + "optimization_order": [0, 1, 2], + "renderer": "json", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": {}, + "file_roots": {}, + "extension_modules": "", } grains = { - 'os': 'Ubuntu', - 'os_family': 'Debian', - 'oscodename': 'raring', - 'osfullname': 'Ubuntu', - 'osrelease': '13.04', - 'kernel': 'Linux' + "os": "Ubuntu", + "os_family": "Debian", + "oscodename": "raring", + "osfullname": "Ubuntu", + "osrelease": "13.04", + "kernel": "Linux", } - with patch('salt.pillar.compile_template') as compile_template, \ - patch.object(salt.pillar.Pillar, '_Pillar__gather_avail', - MagicMock(return_value={'base': ['blah', 'foo']})): + with patch("salt.pillar.compile_template") as compile_template, patch.object( + salt.pillar.Pillar, + "_Pillar__gather_avail", + MagicMock(return_value={"base": ["blah", "foo"]}), + ): # Test with option set to True - opts['pillar_includes_override_sls'] = True - pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base') + opts["pillar_includes_override_sls"] = True + pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") # Mock getting the proper template files pillar.client.get_state = MagicMock( return_value={ - 'dest': '/path/to/pillar/files/foo.sls', - 'source': 'salt://foo.sls' + "dest": "/path/to/pillar/files/foo.sls", + "source": "salt://foo.sls", } ) compile_template.side_effect = [ - {'foo': 'bar', 'include': ['blah']}, - {'foo': 'bar2'} + {"foo": "bar", "include": ["blah"]}, + {"foo": "bar2"}, ] self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({'foo': 'bar2'}, []) + pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar2"}, []) ) # Test with option set to False - opts['pillar_includes_override_sls'] = False - pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base') + opts["pillar_includes_override_sls"] = False + pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") # Mock getting the proper template files pillar.client.get_state = MagicMock( return_value={ - 'dest': '/path/to/pillar/files/foo.sls', - 'source': 'salt://foo.sls' + "dest": "/path/to/pillar/files/foo.sls", + "source": "salt://foo.sls", } ) compile_template.side_effect = [ - {'foo': 'bar', 'include': ['blah']}, - {'foo': 'bar2'} + {"foo": "bar", "include": ["blah"]}, + {"foo": "bar2"}, ] self.assertEqual( - pillar.render_pillar({'base': ['foo.sls']}), - ({'foo': 'bar'}, []) + pillar.render_pillar({"base": ["foo.sls"]}), ({"foo": "bar"}, []) ) def test_topfile_order(self): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'yaml', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': [], - 'extension_modules': '', - 'saltenv': 'base', - 'file_roots': [], + "optimization_order": [0, 1, 2], + "renderer": "yaml", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": [], + "extension_modules": "", + "saltenv": "base", + "file_roots": [], } grains = { - 'os': 'Ubuntu', - 'os_family': 'Debian', - 'oscodename': 'raring', - 'osfullname': 'Ubuntu', - 'osrelease': '13.04', - 'kernel': 'Linux' + "os": "Ubuntu", + "os_family": "Debian", + "oscodename": "raring", + "osfullname": "Ubuntu", + "osrelease": "13.04", + "kernel": "Linux", } def _run_test(nodegroup_order, glob_order, expected): tempdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) try: sls_files = self._setup_test_topfile_sls( - tempdir, - nodegroup_order, - glob_order) + tempdir, nodegroup_order, glob_order + ) fc_mock = MockFileclient( - cache_file=sls_files['top']['dest'], - list_states=['top', 'ssh', 'ssh.minion', - 'generic', 'generic.minion'], - get_state=sls_files) - with patch.object(salt.fileclient, 'get_file_client', - MagicMock(return_value=fc_mock)): - pillar = salt.pillar.Pillar(opts, grains, 'mocked-minion', 'base') + cache_file=sls_files["top"]["dest"], + list_states=[ + "top", + "ssh", + "ssh.minion", + "generic", + "generic.minion", + ], + get_state=sls_files, + ) + with patch.object( + salt.fileclient, "get_file_client", MagicMock(return_value=fc_mock) + ): + pillar = salt.pillar.Pillar(opts, grains, "mocked-minion", "base") # Make sure that confirm_top.confirm_top returns True - pillar.matchers['confirm_top.confirm_top'] = lambda *x, **y: True - self.assertEqual(pillar.compile_pillar()['ssh'], expected) + pillar.matchers["confirm_top.confirm_top"] = lambda *x, **y: True + self.assertEqual(pillar.compile_pillar()["ssh"], expected) finally: shutil.rmtree(tempdir, ignore_errors=True) # test case where glob match happens second and therefore takes # precedence over nodegroup match. - _run_test(nodegroup_order=1, glob_order=2, expected='bar') + _run_test(nodegroup_order=1, glob_order=2, expected="bar") # test case where nodegroup match happens second and therefore takes # precedence over glob match. - _run_test(nodegroup_order=2, glob_order=1, expected='foo') + _run_test(nodegroup_order=2, glob_order=1, expected="foo") def _setup_test_topfile_sls_pillar_match(self, tempdir): # Write a simple topfile and two pillar state files top_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - s = ''' + s = """ base: 'phase:alpha': - match: pillar - generic -''' +""" top_file.write(salt.utils.stringutils.to_bytes(s)) top_file.flush() generic_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - generic_file.write(b''' + generic_file.write( + b""" generic: key1: value1 -''') +""" + ) generic_file.flush() return { - 'top': {'path': '', 'dest': top_file.name}, - 'generic': {'path': '', 'dest': generic_file.name}, + "top": {"path": "", "dest": top_file.name}, + "generic": {"path": "", "dest": generic_file.name}, } def _setup_test_topfile_sls(self, tempdir, nodegroup_order, glob_order): # Write a simple topfile and two pillar state files top_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - s = ''' + s = """ base: group: - match: nodegroup @@ -663,99 +685,113 @@ base: - order: {glob_order} - ssh.minion - generic.minion -'''.format(nodegroup_order=nodegroup_order, glob_order=glob_order) +""".format( + nodegroup_order=nodegroup_order, glob_order=glob_order + ) top_file.write(salt.utils.stringutils.to_bytes(s)) top_file.flush() ssh_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - ssh_file.write(b''' + ssh_file.write( + b""" ssh: foo -''') +""" + ) ssh_file.flush() ssh_minion_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - ssh_minion_file.write(b''' + ssh_minion_file.write( + b""" ssh: bar -''') +""" + ) ssh_minion_file.flush() generic_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - generic_file.write(b''' + generic_file.write( + b""" generic: key1: - value1 - value2 key2: sub_key1: [] -''') +""" + ) generic_file.flush() generic_minion_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - generic_minion_file.write(b''' + generic_minion_file.write( + b""" generic: key1: - value3 key2: sub_key2: [] -''') +""" + ) generic_minion_file.flush() return { - 'top': {'path': '', 'dest': top_file.name}, - 'ssh': {'path': '', 'dest': ssh_file.name}, - 'ssh.minion': {'path': '', 'dest': ssh_minion_file.name}, - 'generic': {'path': '', 'dest': generic_file.name}, - 'generic.minion': {'path': '', 'dest': generic_minion_file.name}, + "top": {"path": "", "dest": top_file.name}, + "ssh": {"path": "", "dest": ssh_file.name}, + "ssh.minion": {"path": "", "dest": ssh_minion_file.name}, + "generic": {"path": "", "dest": generic_file.name}, + "generic.minion": {"path": "", "dest": generic_minion_file.name}, } @with_tempdir() def test_include(self, tempdir): opts = { - 'optimization_order': [0, 1, 2], - 'renderer': 'yaml', - 'renderer_blacklist': [], - 'renderer_whitelist': [], - 'state_top': '', - 'pillar_roots': [], - 'extension_modules': '', - 'saltenv': 'base', - 'file_roots': [], + "optimization_order": [0, 1, 2], + "renderer": "yaml", + "renderer_blacklist": [], + "renderer_whitelist": [], + "state_top": "", + "pillar_roots": [], + "extension_modules": "", + "saltenv": "base", + "file_roots": [], } grains = { - 'os': 'Ubuntu', - 'os_family': 'Debian', - 'oscodename': 'raring', - 'osfullname': 'Ubuntu', - 'osrelease': '13.04', - 'kernel': 'Linux', + "os": "Ubuntu", + "os_family": "Debian", + "oscodename": "raring", + "osfullname": "Ubuntu", + "osrelease": "13.04", + "kernel": "Linux", } sls_files = self._setup_test_include_sls(tempdir) fc_mock = MockFileclient( - cache_file=sls_files['top']['dest'], + cache_file=sls_files["top"]["dest"], get_state=sls_files, list_states=[ - 'top', - 'test.init', - 'test.sub1', - 'test.sub2', - 'test.sub_wildcard_1', - 'test.sub_with_init_dot', - 'test.sub.with.slashes', + "top", + "test.init", + "test.sub1", + "test.sub2", + "test.sub_wildcard_1", + "test.sub_with_init_dot", + "test.sub.with.slashes", ], ) - with patch.object(salt.fileclient, 'get_file_client', - MagicMock(return_value=fc_mock)): - pillar = salt.pillar.Pillar(opts, grains, 'minion', 'base') + with patch.object( + salt.fileclient, "get_file_client", MagicMock(return_value=fc_mock) + ): + pillar = salt.pillar.Pillar(opts, grains, "minion", "base") # Make sure that confirm_top.confirm_top returns True - pillar.matchers['confirm_top.confirm_top'] = lambda *x, **y: True + pillar.matchers["confirm_top.confirm_top"] = lambda *x, **y: True compiled_pillar = pillar.compile_pillar() - self.assertEqual(compiled_pillar['foo_wildcard'], 'bar_wildcard') - self.assertEqual(compiled_pillar['foo1'], 'bar1') - self.assertEqual(compiled_pillar['foo2'], 'bar2') - self.assertEqual(compiled_pillar['sub_with_slashes'], 'sub_slashes_worked') - self.assertEqual(compiled_pillar['sub_init_dot'], 'sub_with_init_dot_worked') + self.assertEqual(compiled_pillar["foo_wildcard"], "bar_wildcard") + self.assertEqual(compiled_pillar["foo1"], "bar1") + self.assertEqual(compiled_pillar["foo2"], "bar2") + self.assertEqual(compiled_pillar["sub_with_slashes"], "sub_slashes_worked") + self.assertEqual( + compiled_pillar["sub_init_dot"], "sub_with_init_dot_worked" + ) def _setup_test_include_sls(self, tempdir): top_file = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - top_file.write(b''' + top_file.write( + b""" base: '*': - order: 1 @@ -763,72 +799,86 @@ base: minion: - order: 2 - test -''') +""" + ) top_file.flush() init_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - init_sls.write(b''' + init_sls.write( + b""" include: - test.sub1 - test.sub_wildcard* - .test.sub_with_init_dot - test/sub/with/slashes -''') +""" + ) init_sls.flush() sub1_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - sub1_sls.write(b''' + sub1_sls.write( + b""" foo1: bar1 -''') +""" + ) sub1_sls.flush() sub2_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - sub2_sls.write(b''' + sub2_sls.write( + b""" foo2: bar2 -''') +""" + ) sub2_sls.flush() sub_wildcard_1_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - sub_wildcard_1_sls.write(b''' + sub_wildcard_1_sls.write( + b""" foo_wildcard: bar_wildcard -''') +""" + ) sub_wildcard_1_sls.flush() sub_with_init_dot_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - sub_with_init_dot_sls.write(b''' + sub_with_init_dot_sls.write( + b""" sub_init_dot: sub_with_init_dot_worked -''') +""" + ) sub_with_init_dot_sls.flush() sub_with_slashes_sls = tempfile.NamedTemporaryFile(dir=tempdir, delete=False) - sub_with_slashes_sls.write(b''' + sub_with_slashes_sls.write( + b""" sub_with_slashes: sub_slashes_worked -''') +""" + ) sub_with_slashes_sls.flush() return { - 'top': {'path': '', 'dest': top_file.name}, - 'test': {'path': '', 'dest': init_sls.name}, - 'test.sub1': {'path': '', 'dest': sub1_sls.name}, - 'test.sub2': {'path': '', 'dest': sub2_sls.name}, - 'test.sub_wildcard_1': {'path': '', 'dest': sub_wildcard_1_sls.name}, - 'test.sub_with_init_dot': {'path': '', 'dest': sub_with_init_dot_sls.name}, - 'test.sub.with.slashes': {'path': '', 'dest': sub_with_slashes_sls.name}, + "top": {"path": "", "dest": top_file.name}, + "test": {"path": "", "dest": init_sls.name}, + "test.sub1": {"path": "", "dest": sub1_sls.name}, + "test.sub2": {"path": "", "dest": sub2_sls.name}, + "test.sub_wildcard_1": {"path": "", "dest": sub_wildcard_1_sls.name}, + "test.sub_with_init_dot": {"path": "", "dest": sub_with_init_dot_sls.name}, + "test.sub.with.slashes": {"path": "", "dest": sub_with_slashes_sls.name}, } -@patch('salt.transport.client.ReqChannel.factory', MagicMock()) +@patch("salt.transport.client.ReqChannel.factory", MagicMock()) class RemotePillarTestCase(TestCase): - ''' + """ Tests for instantiating a RemotePillar in salt.pillar - ''' + """ + def setUp(self): self.grains = {} def tearDown(self): - for attr in ('grains',): + for attr in ("grains",): try: delattr(self, attr) except AttributeError: @@ -837,115 +887,136 @@ class RemotePillarTestCase(TestCase): def test_get_opts_in_pillar_override_call(self): mock_get_extra_minion_data = MagicMock(return_value={}) with patch( - 'salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data', - mock_get_extra_minion_data): + "salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data", + mock_get_extra_minion_data, + ): - salt.pillar.RemotePillar({}, self.grains, 'mocked-minion', 'dev') - mock_get_extra_minion_data.assert_called_once_with( - {'saltenv': 'dev'}) + salt.pillar.RemotePillar({}, self.grains, "mocked-minion", "dev") + mock_get_extra_minion_data.assert_called_once_with({"saltenv": "dev"}) def test_multiple_keys_in_opts_added_to_pillar(self): opts = { - 'renderer': 'json', - 'path_to_add': 'fake_data', - 'path_to_add2': {'fake_data2': ['fake_data3', 'fake_data4']}, - 'pass_to_ext_pillars': ['path_to_add', 'path_to_add2'] + "renderer": "json", + "path_to_add": "fake_data", + "path_to_add2": {"fake_data2": ["fake_data3", "fake_data4"]}, + "pass_to_ext_pillars": ["path_to_add", "path_to_add2"], } - pillar = salt.pillar.RemotePillar(opts, self.grains, - 'mocked-minion', 'dev') - self.assertEqual(pillar.extra_minion_data, - {'path_to_add': 'fake_data', - 'path_to_add2': {'fake_data2': ['fake_data3', - 'fake_data4']}}) + pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") + self.assertEqual( + pillar.extra_minion_data, + { + "path_to_add": "fake_data", + "path_to_add2": {"fake_data2": ["fake_data3", "fake_data4"]}, + }, + ) def test_subkey_in_opts_added_to_pillar(self): opts = { - 'renderer': 'json', - 'path_to_add': 'fake_data', - 'path_to_add2': {'fake_data5': 'fake_data6', - 'fake_data2': ['fake_data3', 'fake_data4']}, - 'pass_to_ext_pillars': ['path_to_add2:fake_data5'] + "renderer": "json", + "path_to_add": "fake_data", + "path_to_add2": { + "fake_data5": "fake_data6", + "fake_data2": ["fake_data3", "fake_data4"], + }, + "pass_to_ext_pillars": ["path_to_add2:fake_data5"], } - pillar = salt.pillar.RemotePillar(opts, self.grains, - 'mocked-minion', 'dev') - self.assertEqual(pillar.extra_minion_data, - {'path_to_add2': {'fake_data5': 'fake_data6'}}) + pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") + self.assertEqual( + pillar.extra_minion_data, {"path_to_add2": {"fake_data5": "fake_data6"}} + ) def test_non_existent_leaf_opt_in_add_to_pillar(self): opts = { - 'renderer': 'json', - 'path_to_add': 'fake_data', - 'path_to_add2': {'fake_data5': 'fake_data6', - 'fake_data2': ['fake_data3', 'fake_data4']}, - 'pass_to_ext_pillars': ['path_to_add2:fake_data_non_exist'] + "renderer": "json", + "path_to_add": "fake_data", + "path_to_add2": { + "fake_data5": "fake_data6", + "fake_data2": ["fake_data3", "fake_data4"], + }, + "pass_to_ext_pillars": ["path_to_add2:fake_data_non_exist"], } - pillar = salt.pillar.RemotePillar(opts, self.grains, - 'mocked-minion', 'dev') + pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") self.assertEqual(pillar.pillar_override, {}) def test_non_existent_intermediate_opt_in_add_to_pillar(self): opts = { - 'renderer': 'json', - 'path_to_add': 'fake_data', - 'path_to_add2': {'fake_data5': 'fake_data6', - 'fake_data2': ['fake_data3', 'fake_data4']}, - 'pass_to_ext_pillars': ['path_to_add_no_exist'] + "renderer": "json", + "path_to_add": "fake_data", + "path_to_add2": { + "fake_data5": "fake_data6", + "fake_data2": ["fake_data3", "fake_data4"], + }, + "pass_to_ext_pillars": ["path_to_add_no_exist"], } - pillar = salt.pillar.RemotePillar(opts, self.grains, - 'mocked-minion', 'dev') + pillar = salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") self.assertEqual(pillar.pillar_override, {}) def test_malformed_add_to_pillar(self): opts = { - 'renderer': 'json', - 'path_to_add': 'fake_data', - 'path_to_add2': {'fake_data5': 'fake_data6', - 'fake_data2': ['fake_data3', 'fake_data4']}, - 'pass_to_ext_pillars': MagicMock() + "renderer": "json", + "path_to_add": "fake_data", + "path_to_add2": { + "fake_data5": "fake_data6", + "fake_data2": ["fake_data3", "fake_data4"], + }, + "pass_to_ext_pillars": MagicMock(), } with self.assertRaises(salt.exceptions.SaltClientError) as excinfo: - salt.pillar.RemotePillar(opts, self.grains, 'mocked-minion', 'dev') - self.assertEqual(excinfo.exception.strerror, - '\'pass_to_ext_pillars\' config is malformed.') + salt.pillar.RemotePillar(opts, self.grains, "mocked-minion", "dev") + self.assertEqual( + excinfo.exception.strerror, "'pass_to_ext_pillars' config is malformed." + ) def test_pillar_send_extra_minion_data_from_config(self): opts = { - 'renderer': 'json', - 'pillarenv': 'fake_pillar_env', - 'path_to_add': 'fake_data', - 'path_to_add2': {'fake_data5': 'fake_data6', - 'fake_data2': ['fake_data3', 'fake_data4']}, - 'pass_to_ext_pillars': ['path_to_add']} + "renderer": "json", + "pillarenv": "fake_pillar_env", + "path_to_add": "fake_data", + "path_to_add2": { + "fake_data5": "fake_data6", + "fake_data2": ["fake_data3", "fake_data4"], + }, + "pass_to_ext_pillars": ["path_to_add"], + } mock_channel = MagicMock( - crypted_transfer_decode_dictentry=MagicMock(return_value={})) - with patch('salt.transport.client.ReqChannel.factory', - MagicMock(return_value=mock_channel)): - pillar = salt.pillar.RemotePillar(opts, self.grains, - 'mocked_minion', 'fake_env') + crypted_transfer_decode_dictentry=MagicMock(return_value={}) + ) + with patch( + "salt.transport.client.ReqChannel.factory", + MagicMock(return_value=mock_channel), + ): + pillar = salt.pillar.RemotePillar( + opts, self.grains, "mocked_minion", "fake_env" + ) ret = pillar.compile_pillar() self.assertEqual(pillar.channel, mock_channel) mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with( - {'cmd': '_pillar', 'ver': '2', - 'id': 'mocked_minion', - 'grains': {}, - 'saltenv': 'fake_env', - 'pillarenv': 'fake_pillar_env', - 'pillar_override': {}, - 'extra_minion_data': {'path_to_add': 'fake_data'}}, - dictkey='pillar') + { + "cmd": "_pillar", + "ver": "2", + "id": "mocked_minion", + "grains": {}, + "saltenv": "fake_env", + "pillarenv": "fake_pillar_env", + "pillar_override": {}, + "extra_minion_data": {"path_to_add": "fake_data"}, + }, + dictkey="pillar", + ) -@patch('salt.transport.client.AsyncReqChannel.factory', MagicMock()) +@patch("salt.transport.client.AsyncReqChannel.factory", MagicMock()) class AsyncRemotePillarTestCase(TestCase): - ''' + """ Tests for instantiating a AsyncRemotePillar in salt.pillar - ''' + """ + def setUp(self): self.grains = {} def tearDown(self): - for attr in ('grains',): + for attr in ("grains",): try: delattr(self, attr) except AttributeError: @@ -954,35 +1025,46 @@ class AsyncRemotePillarTestCase(TestCase): def test_get_opts_in_pillar_override_call(self): mock_get_extra_minion_data = MagicMock(return_value={}) with patch( - 'salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data', - mock_get_extra_minion_data): + "salt.pillar.RemotePillarMixin.get_ext_pillar_extra_minion_data", + mock_get_extra_minion_data, + ): - salt.pillar.RemotePillar({}, self.grains, 'mocked-minion', 'dev') - mock_get_extra_minion_data.assert_called_once_with( - {'saltenv': 'dev'}) + salt.pillar.RemotePillar({}, self.grains, "mocked-minion", "dev") + mock_get_extra_minion_data.assert_called_once_with({"saltenv": "dev"}) def test_pillar_send_extra_minion_data_from_config(self): opts = { - 'renderer': 'json', - 'pillarenv': 'fake_pillar_env', - 'path_to_add': 'fake_data', - 'path_to_add2': {'fake_data5': 'fake_data6', - 'fake_data2': ['fake_data3', 'fake_data4']}, - 'pass_to_ext_pillars': ['path_to_add']} + "renderer": "json", + "pillarenv": "fake_pillar_env", + "path_to_add": "fake_data", + "path_to_add2": { + "fake_data5": "fake_data6", + "fake_data2": ["fake_data3", "fake_data4"], + }, + "pass_to_ext_pillars": ["path_to_add"], + } mock_channel = MagicMock( - crypted_transfer_decode_dictentry=MagicMock(return_value={})) - with patch('salt.transport.client.AsyncReqChannel.factory', - MagicMock(return_value=mock_channel)): - pillar = salt.pillar.RemotePillar(opts, self.grains, - 'mocked_minion', 'fake_env') + crypted_transfer_decode_dictentry=MagicMock(return_value={}) + ) + with patch( + "salt.transport.client.AsyncReqChannel.factory", + MagicMock(return_value=mock_channel), + ): + pillar = salt.pillar.RemotePillar( + opts, self.grains, "mocked_minion", "fake_env" + ) ret = pillar.compile_pillar() mock_channel.crypted_transfer_decode_dictentry.assert_called_once_with( - {'cmd': '_pillar', 'ver': '2', - 'id': 'mocked_minion', - 'grains': {}, - 'saltenv': 'fake_env', - 'pillarenv': 'fake_pillar_env', - 'pillar_override': {}, - 'extra_minion_data': {'path_to_add': 'fake_data'}}, - dictkey='pillar') + { + "cmd": "_pillar", + "ver": "2", + "id": "mocked_minion", + "grains": {}, + "saltenv": "fake_env", + "pillarenv": "fake_pillar_env", + "pillar_override": {}, + "extra_minion_data": {"path_to_add": "fake_data"}, + }, + dictkey="pillar", + ) diff --git a/tests/unit/test_proxy_minion.py b/tests/unit/test_proxy_minion.py index 7bda02764cf..9c2f9ac8152 100644 --- a/tests/unit/test_proxy_minion.py +++ b/tests/unit/test_proxy_minion.py @@ -1,24 +1,24 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Gareth J. Greenaway <gareth@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import -import copy +import copy import logging + import salt.ext.tornado import salt.ext.tornado.testing -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mixins import AdaptedConfigurationTestCaseMixin - # Import salt libs import salt.minion import salt.syspaths +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +# Import Salt Testing libs +from tests.support.unit import TestCase log = logging.getLogger(__name__) __opts__ = {} @@ -26,27 +26,34 @@ __opts__ = {} class ProxyMinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): def test_post_master_init_metaproxy_called(self): - ''' + """ Tests that when the _post_master_ini function is called, _metaproxy_call is also called. - ''' + """ mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() mock_jid_queue = [123] - proxy_minion = salt.minion.ProxyMinion(mock_opts, jid_queue=copy.copy(mock_jid_queue), io_loop=salt.ext.tornado.ioloop.IOLoop()) + proxy_minion = salt.minion.ProxyMinion( + mock_opts, + jid_queue=copy.copy(mock_jid_queue), + io_loop=salt.ext.tornado.ioloop.IOLoop(), + ) try: - ret = proxy_minion._post_master_init('dummy_master') + ret = proxy_minion._post_master_init("dummy_master") self.assert_called_once(salt.minion._metaproxy_call) finally: proxy_minion.destroy() def test_handle_decoded_payload_metaproxy_called(self): - ''' + """ Tests that when the _handle_decoded_payload function is called, _metaproxy_call is also called. - ''' + """ mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() - mock_data = {'fun': 'foo.bar', - 'jid': 123} + mock_data = {"fun": "foo.bar", "jid": 123} mock_jid_queue = [123] - proxy_minion = salt.minion.ProxyMinion(mock_opts, jid_queue=copy.copy(mock_jid_queue), io_loop=salt.ext.tornado.ioloop.IOLoop()) + proxy_minion = salt.minion.ProxyMinion( + mock_opts, + jid_queue=copy.copy(mock_jid_queue), + io_loop=salt.ext.tornado.ioloop.IOLoop(), + ) try: ret = proxy_minion._handle_decoded_payload(mock_data).result() self.assertEqual(proxy_minion.jid_queue, mock_jid_queue) @@ -56,14 +63,17 @@ class ProxyMinionTestCase(TestCase, AdaptedConfigurationTestCaseMixin): proxy_minion.destroy() def test_handle_payload_metaproxy_called(self): - ''' + """ Tests that when the _handle_payload function is called, _metaproxy_call is also called. - ''' + """ mock_opts = salt.config.DEFAULT_MINION_OPTS.copy() - mock_data = {'fun': 'foo.bar', - 'jid': 123} + mock_data = {"fun": "foo.bar", "jid": 123} mock_jid_queue = [123] - proxy_minion = salt.minion.ProxyMinion(mock_opts, jid_queue=copy.copy(mock_jid_queue), io_loop=salt.ext.tornado.ioloop.IOLoop()) + proxy_minion = salt.minion.ProxyMinion( + mock_opts, + jid_queue=copy.copy(mock_jid_queue), + io_loop=salt.ext.tornado.ioloop.IOLoop(), + ) try: ret = proxy_minion._handle_decoded_payload(mock_data).result() self.assertEqual(proxy_minion.jid_queue, mock_jid_queue) diff --git a/tests/unit/test_spm.py b/tests/unit/test_spm.py index 6f1eb7b78dd..e95b6e39b84 100644 --- a/tests/unit/test_spm.py +++ b/tests/unit/test_spm.py @@ -2,47 +2,55 @@ # Import Python libs from __future__ import absolute_import + import os import shutil import tempfile -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import patch, MagicMock +import salt.config +import salt.spm +import salt.utils.files from tests.support.helpers import destructiveTest from tests.support.mixins import AdaptedConfigurationTestCaseMixin -import salt.config -import salt.utils.files -import salt.spm +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase _F1 = { - 'definition': { - 'name': 'formula1', - 'version': '1.2', - 'release': '2', - 'summary': 'test', - 'description': 'testing, nothing to see here', + "definition": { + "name": "formula1", + "version": "1.2", + "release": "2", + "summary": "test", + "description": "testing, nothing to see here", } } -_F1['contents'] = ( - ('FORMULA', ('name: {name}\n' - 'version: {version}\n' - 'release: {release}\n' - 'summary: {summary}\n' - 'description: {description}').format(**_F1['definition'])), - ('modules/mod1.py', '# mod1.py'), - ('modules/mod2.py', '# mod2.py'), - ('states/state1.sls', '# state1.sls'), - ('states/state2.sls', '# state2.sls'), +_F1["contents"] = ( + ( + "FORMULA", + ( + "name: {name}\n" + "version: {version}\n" + "release: {release}\n" + "summary: {summary}\n" + "description: {description}" + ).format(**_F1["definition"]), + ), + ("modules/mod1.py", "# mod1.py"), + ("modules/mod2.py", "# mod2.py"), + ("states/state1.sls", "# state1.sls"), + ("states/state2.sls", "# state2.sls"), ) @destructiveTest class SPMTestUserInterface(salt.spm.SPMUserInterface): - ''' + """ Unit test user interface to SPMClient - ''' + """ + def __init__(self): self._status = [] self._confirm = [] @@ -63,120 +71,146 @@ class SPMTest(TestCase, AdaptedConfigurationTestCaseMixin): self._tmp_spm = tempfile.mkdtemp() self.addCleanup(shutil.rmtree, self._tmp_spm, ignore_errors=True) - minion_config = self.get_temp_config('minion', **{ - 'spm_logfile': os.path.join(self._tmp_spm, 'log'), - 'spm_repos_config': os.path.join(self._tmp_spm, 'etc', 'spm.repos'), - 'spm_cache_dir': os.path.join(self._tmp_spm, 'cache'), - 'spm_build_dir': os.path.join(self._tmp_spm, 'build'), - 'spm_build_exclude': ['.git'], - 'spm_db_provider': 'sqlite3', - 'spm_files_provider': 'local', - 'spm_db': os.path.join(self._tmp_spm, 'packages.db'), - 'extension_modules': os.path.join(self._tmp_spm, 'modules'), - 'file_roots': {'base': [self._tmp_spm, ]}, - 'formula_path': os.path.join(self._tmp_spm, 'spm'), - 'pillar_path': os.path.join(self._tmp_spm, 'pillar'), - 'reactor_path': os.path.join(self._tmp_spm, 'reactor'), - 'assume_yes': True, - 'force': False, - 'verbose': False, - 'cache': 'localfs', - 'cachedir': os.path.join(self._tmp_spm, 'cache'), - 'spm_repo_dups': 'ignore', - 'spm_share_dir': os.path.join(self._tmp_spm, 'share'), - }) + minion_config = self.get_temp_config( + "minion", + **{ + "spm_logfile": os.path.join(self._tmp_spm, "log"), + "spm_repos_config": os.path.join(self._tmp_spm, "etc", "spm.repos"), + "spm_cache_dir": os.path.join(self._tmp_spm, "cache"), + "spm_build_dir": os.path.join(self._tmp_spm, "build"), + "spm_build_exclude": [".git"], + "spm_db_provider": "sqlite3", + "spm_files_provider": "local", + "spm_db": os.path.join(self._tmp_spm, "packages.db"), + "extension_modules": os.path.join(self._tmp_spm, "modules"), + "file_roots": {"base": [self._tmp_spm]}, + "formula_path": os.path.join(self._tmp_spm, "spm"), + "pillar_path": os.path.join(self._tmp_spm, "pillar"), + "reactor_path": os.path.join(self._tmp_spm, "reactor"), + "assume_yes": True, + "force": False, + "verbose": False, + "cache": "localfs", + "cachedir": os.path.join(self._tmp_spm, "cache"), + "spm_repo_dups": "ignore", + "spm_share_dir": os.path.join(self._tmp_spm, "share"), + } + ) self.ui = SPMTestUserInterface() self.client = salt.spm.SPMClient(self.ui, minion_config) self.minion_config = minion_config - for attr in ('client', 'ui', '_tmp_spm', 'minion_config'): + for attr in ("client", "ui", "_tmp_spm", "minion_config"): self.addCleanup(delattr, self, attr) def _create_formula_files(self, formula): - fdir = os.path.join(self._tmp_spm, formula['definition']['name']) + fdir = os.path.join(self._tmp_spm, formula["definition"]["name"]) shutil.rmtree(fdir, ignore_errors=True) os.mkdir(fdir) - for path, contents in formula['contents']: + for path, contents in formula["contents"]: path = os.path.join(fdir, path) dirname, _ = os.path.split(path) if not os.path.exists(dirname): os.makedirs(dirname) - with salt.utils.files.fopen(path, 'w') as f: + with salt.utils.files.fopen(path, "w") as f: f.write(contents) return fdir def test_build_install(self): # Build package fdir = self._create_formula_files(_F1) - with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)): - with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])): - self.client.run(['build', fdir]) + with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)): + with patch( + "salt.client.get_local_client", + MagicMock(return_value=self.minion_opts["conf_file"]), + ): + self.client.run(["build", fdir]) pkgpath = self.ui._status[-1].split()[-1] assert os.path.exists(pkgpath) # Install package - with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)): - with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])): - self.client.run(['local', 'install', pkgpath]) + with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)): + with patch( + "salt.client.get_local_client", + MagicMock(return_value=self.minion_opts["conf_file"]), + ): + self.client.run(["local", "install", pkgpath]) # Check filesystem - for path, contents in _F1['contents']: - path = os.path.join(self.minion_config['file_roots']['base'][0], _F1['definition']['name'], path) + for path, contents in _F1["contents"]: + path = os.path.join( + self.minion_config["file_roots"]["base"][0], + _F1["definition"]["name"], + path, + ) assert os.path.exists(path) - with salt.utils.files.fopen(path, 'r') as rfh: + with salt.utils.files.fopen(path, "r") as rfh: assert rfh.read() == contents # Check database - with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)): - with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])): - self.client.run(['info', _F1['definition']['name']]) - lines = self.ui._status[-1].split('\n') + with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)): + with patch( + "salt.client.get_local_client", + MagicMock(return_value=self.minion_opts["conf_file"]), + ): + self.client.run(["info", _F1["definition"]["name"]]) + lines = self.ui._status[-1].split("\n") for key, line in ( - ('name', 'Name: {0}'), - ('version', 'Version: {0}'), - ('release', 'Release: {0}'), - ('summary', 'Summary: {0}')): - assert line.format(_F1['definition'][key]) in lines + ("name", "Name: {0}"), + ("version", "Version: {0}"), + ("release", "Release: {0}"), + ("summary", "Summary: {0}"), + ): + assert line.format(_F1["definition"][key]) in lines # Reinstall with force=False, should fail self.ui._error = [] - with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)): - with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])): - self.client.run(['local', 'install', pkgpath]) + with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)): + with patch( + "salt.client.get_local_client", + MagicMock(return_value=self.minion_opts["conf_file"]), + ): + self.client.run(["local", "install", pkgpath]) assert len(self.ui._error) > 0 # Reinstall with force=True, should succeed - with patch.dict(self.minion_config, {'force': True}): + with patch.dict(self.minion_config, {"force": True}): self.ui._error = [] - with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)): - with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])): - self.client.run(['local', 'install', pkgpath]) + with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)): + with patch( + "salt.client.get_local_client", + MagicMock(return_value=self.minion_opts["conf_file"]), + ): + self.client.run(["local", "install", pkgpath]) assert len(self.ui._error) == 0 def test_failure_paths(self): fail_args = ( - ['bogus', 'command'], - ['create_repo'], - ['build'], - ['build', '/nonexistent/path'], - ['info'], - ['info', 'not_installed'], - ['files'], - ['files', 'not_installed'], - ['install'], - ['install', 'nonexistent.spm'], - ['remove'], - ['remove', 'not_installed'], - ['local', 'bogus', 'command'], - ['local', 'info'], - ['local', 'info', '/nonexistent/path/junk.spm'], - ['local', 'files'], - ['local', 'files', '/nonexistent/path/junk.spm'], - ['local', 'install'], - ['local', 'install', '/nonexistent/path/junk.spm'], - ['local', 'list'], - ['local', 'list', '/nonexistent/path/junk.spm'], + ["bogus", "command"], + ["create_repo"], + ["build"], + ["build", "/nonexistent/path"], + ["info"], + ["info", "not_installed"], + ["files"], + ["files", "not_installed"], + ["install"], + ["install", "nonexistent.spm"], + ["remove"], + ["remove", "not_installed"], + ["local", "bogus", "command"], + ["local", "info"], + ["local", "info", "/nonexistent/path/junk.spm"], + ["local", "files"], + ["local", "files", "/nonexistent/path/junk.spm"], + ["local", "install"], + ["local", "install", "/nonexistent/path/junk.spm"], + ["local", "list"], + ["local", "list", "/nonexistent/path/junk.spm"], # XXX install failure due to missing deps # XXX install failure due to missing field ) for args in fail_args: self.ui._error = [] - with patch('salt.client.Caller', MagicMock(return_value=self.minion_opts)): - with patch('salt.client.get_local_client', MagicMock(return_value=self.minion_opts['conf_file'])): + with patch("salt.client.Caller", MagicMock(return_value=self.minion_opts)): + with patch( + "salt.client.get_local_client", + MagicMock(return_value=self.minion_opts["conf_file"]), + ): self.client.run(args) assert len(self.ui._error) > 0 diff --git a/tests/unit/test_state.py b/tests/unit/test_state.py index b58982b37c9..696e54924b1 100644 --- a/tests/unit/test_state.py +++ b/tests/unit/test_state.py @@ -1,30 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import tempfile -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch) -from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.runtests import RUNTIME_VARS -from tests.support.helpers import with_tempfile - # Import Salt libs import salt.exceptions import salt.state -from salt.utils.odict import OrderedDict -from salt.utils.decorators import state as statedecorators import salt.utils.files import salt.utils.platform +from salt.utils.decorators import state as statedecorators +from salt.utils.odict import OrderedDict +from tests.support.helpers import with_tempfile +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf try: import pytest @@ -33,56 +32,74 @@ except ImportError as err: class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): - ''' + """ TestCase for the state compiler. - ''' + """ def test_format_log_non_ascii_character(self): - ''' + """ Tests running a non-ascii character through the state.format_log function. See Issue #33605. - ''' + """ # There is no return to test against as the format_log # function doesn't return anything. However, we do want # to make sure that the function doesn't stacktrace when # called. - ret = {'changes': {'Français': {'old': 'something old', - 'new': 'something new'}}, - 'result': True} + ret = { + "changes": {"Français": {"old": "something old", "new": "something new"}}, + "result": True, + } salt.state.format_log(ret) def test_render_error_on_invalid_requisite(self): - ''' + """ Test that the state compiler correctly deliver a rendering exception when a requisite cannot be resolved - ''' - with patch('salt.state.State._gather_pillar') as state_patch: + """ + with patch("salt.state.State._gather_pillar") as state_patch: high_data = { - 'git': OrderedDict([ - ('pkg', [ - OrderedDict([ - ('require', [ - OrderedDict([ - ('file', OrderedDict( - [('test1', 'test')]))])])]), - 'installed', {'order': 10000}]), ('__sls__', 'issue_35226'), ('__env__', 'base')])} - minion_opts = self.get_temp_config('minion') - minion_opts['pillar'] = {'git': OrderedDict([('test1', 'test')])} + "git": OrderedDict( + [ + ( + "pkg", + [ + OrderedDict( + [ + ( + "require", + [ + OrderedDict( + [ + ( + "file", + OrderedDict( + [("test1", "test")] + ), + ) + ] + ) + ], + ) + ] + ), + "installed", + {"order": 10000}, + ], + ), + ("__sls__", "issue_35226"), + ("__env__", "base"), + ] + ) + } + minion_opts = self.get_temp_config("minion") + minion_opts["pillar"] = {"git": OrderedDict([("test1", "test")])} state_obj = salt.state.State(minion_opts) with self.assertRaises(salt.exceptions.SaltRenderError): state_obj.call_high(high_data) def test_verify_onlyif_parse(self): low_data = { - "onlyif": [ - { - "fun": "test.arg", - "args": [ - "arg1", - "arg2" - ] - } - ], + "onlyif": [{"fun": "test.arg", "args": ["arg1", "arg2"]}], "name": "mysql-server-5.7", "state": "debconf", "__id__": "set root password", @@ -90,32 +107,21 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): "__env__": "base", "__sls__": "debconf", "data": { - "mysql-server/root_password": { - "type": "password", - "value": "temp123" - } + "mysql-server/root_password": {"type": "password", "value": "temp123"} }, - "order": 10000 + "order": 10000, } - expected_result = {'comment': 'onlyif condition is true', 'result': False} + expected_result = {"comment": "onlyif condition is true", "result": False} - with patch('salt.state.State._gather_pillar') as state_patch: - minion_opts = self.get_temp_config('minion') + with patch("salt.state.State._gather_pillar") as state_patch: + minion_opts = self.get_temp_config("minion") state_obj = salt.state.State(minion_opts) - return_result = state_obj._run_check_onlyif(low_data, '') + return_result = state_obj._run_check_onlyif(low_data, "") self.assertEqual(expected_result, return_result) def test_verify_unless_parse(self): low_data = { - "unless": [ - { - "fun": "test.arg", - "args": [ - "arg1", - "arg2" - ] - } - ], + "unless": [{"fun": "test.arg", "args": ["arg1", "arg2"]}], "name": "mysql-server-5.7", "state": "debconf", "__id__": "set root password", @@ -123,19 +129,20 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): "__env__": "base", "__sls__": "debconf", "data": { - "mysql-server/root_password": { - "type": "password", - "value": "temp123" - } + "mysql-server/root_password": {"type": "password", "value": "temp123"} }, - "order": 10000 + "order": 10000, + } + expected_result = { + "comment": "unless condition is true", + "result": True, + "skip_watch": True, } - expected_result = {'comment': 'unless condition is true', 'result': True, 'skip_watch': True} - with patch('salt.state.State._gather_pillar') as state_patch: - minion_opts = self.get_temp_config('minion') + with patch("salt.state.State._gather_pillar") as state_patch: + minion_opts = self.get_temp_config("minion") state_obj = salt.state.State(minion_opts) - return_result = state_obj._run_check_unless(low_data, '') + return_result = state_obj._run_check_unless(low_data, "") self.assertEqual(expected_result, return_result) def _expand_win_path(self, path): @@ -146,20 +153,23 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): """ if salt.utils.platform.is_windows(): import win32file - return win32file.GetLongPathName(path).replace('\\', '/') + + return win32file.GetLongPathName(path).replace("\\", "/") else: return path @with_tempfile() def test_verify_onlyif_parse_slots(self, name): - with salt.utils.files.fopen(name, 'w') as fp: - fp.write('file-contents') + with salt.utils.files.fopen(name, "w") as fp: + fp.write("file-contents") low_data = { "onlyif": [ { "fun": "file.search", "args": [ - "__slot__:salt:test.echo({})".format(self._expand_win_path(name)), + "__slot__:salt:test.echo({})".format( + self._expand_win_path(name) + ), ], "pattern": "__slot__:salt:test.echo(file-contents)", } @@ -171,30 +181,29 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): "__env__": "base", "__sls__": "debconf", "data": { - "mysql-server/root_password": { - "type": "password", - "value": "temp123" - } + "mysql-server/root_password": {"type": "password", "value": "temp123"} }, - "order": 10000 + "order": 10000, } - expected_result = {'comment': 'onlyif condition is true', 'result': False} - with patch('salt.state.State._gather_pillar') as state_patch: - minion_opts = self.get_temp_config('minion') + expected_result = {"comment": "onlyif condition is true", "result": False} + with patch("salt.state.State._gather_pillar") as state_patch: + minion_opts = self.get_temp_config("minion") state_obj = salt.state.State(minion_opts) - return_result = state_obj._run_check_onlyif(low_data, '') + return_result = state_obj._run_check_onlyif(low_data, "") self.assertEqual(expected_result, return_result) @with_tempfile() def test_verify_unless_parse_slots(self, name): - with salt.utils.files.fopen(name, 'w') as fp: - fp.write('file-contents') + with salt.utils.files.fopen(name, "w") as fp: + fp.write("file-contents") low_data = { "unless": [ { "fun": "file.search", "args": [ - "__slot__:salt:test.echo({})".format(self._expand_win_path(name)), + "__slot__:salt:test.echo({})".format( + self._expand_win_path(name) + ), ], "pattern": "__slot__:salt:test.echo(file-contents)", } @@ -206,74 +215,114 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): "__env__": "base", "__sls__": "debconf", "data": { - "mysql-server/root_password": { - "type": "password", - "value": "temp123" - } + "mysql-server/root_password": {"type": "password", "value": "temp123"} }, - "order": 10000 + "order": 10000, + } + expected_result = { + "comment": "unless condition is true", + "result": True, + "skip_watch": True, } - expected_result = {'comment': 'unless condition is true', 'result': True, 'skip_watch': True} - with patch('salt.state.State._gather_pillar') as state_patch: - minion_opts = self.get_temp_config('minion') + with patch("salt.state.State._gather_pillar") as state_patch: + minion_opts = self.get_temp_config("minion") state_obj = salt.state.State(minion_opts) - return_result = state_obj._run_check_unless(low_data, '') + return_result = state_obj._run_check_unless(low_data, "") self.assertEqual(expected_result, return_result) + def test_verify_retry_parsing(self): + low_data = { + "state": "file", + "name": "/tmp/saltstack.README.rst", + "__sls__": "demo.download", + "__env__": "base", + "__id__": "download sample data", + "retry": {"attempts": 5, "interval": 5}, + "unless": ["test -f /tmp/saltstack.README.rst"], + "source": [ + "https://raw.githubusercontent.com/saltstack/salt/develop/README.rst" + ], + "source_hash": "f2bc8c0aa2ae4f5bb5c2051686016b48", + "order": 10000, + "fun": "managed", + } + expected_result = { + "__id__": "download sample data", + "__run_num__": 0, + "__sls__": "demo.download", + "changes": {}, + "comment": "['unless condition is true'] The state would be retried every 5 " + "seconds (with a splay of up to 0 seconds) a maximum of 5 times or " + "until a result of True is returned", + "name": "/tmp/saltstack.README.rst", + "result": True, + "skip_watch": True, + } + + with patch("salt.state.State._gather_pillar") as state_patch: + minion_opts = self.get_temp_config("minion") + minion_opts["test"] = True + minion_opts["file_client"] = "local" + state_obj = salt.state.State(minion_opts) + mock = { + "result": True, + "comment": ["unless condition is true"], + "skip_watch": True, + } + with patch.object(state_obj, "_run_check", return_value=mock): + self.assertDictContainsSubset(expected_result, state_obj.call(low_data)) + class HighStateTestCase(TestCase, AdaptedConfigurationTestCaseMixin): def setUp(self): root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - self.state_tree_dir = os.path.join(root_dir, 'state_tree') - cache_dir = os.path.join(root_dir, 'cachedir') + self.state_tree_dir = os.path.join(root_dir, "state_tree") + cache_dir = os.path.join(root_dir, "cachedir") for dpath in (root_dir, self.state_tree_dir, cache_dir): if not os.path.isdir(dpath): os.makedirs(dpath) overrides = {} - overrides['root_dir'] = root_dir - overrides['state_events'] = False - overrides['id'] = 'match' - overrides['file_client'] = 'local' - overrides['file_roots'] = dict(base=[self.state_tree_dir]) - overrides['cachedir'] = cache_dir - overrides['test'] = False - self.config = self.get_temp_config('minion', **overrides) - self.addCleanup(delattr, self, 'config') + overrides["root_dir"] = root_dir + overrides["state_events"] = False + overrides["id"] = "match" + overrides["file_client"] = "local" + overrides["file_roots"] = dict(base=[self.state_tree_dir]) + overrides["cachedir"] = cache_dir + overrides["test"] = False + self.config = self.get_temp_config("minion", **overrides) + self.addCleanup(delattr, self, "config") self.highstate = salt.state.HighState(self.config) - self.addCleanup(delattr, self, 'highstate') + self.addCleanup(delattr, self, "highstate") self.highstate.push_active() def tearDown(self): self.highstate.pop_active() def test_top_matches_with_list(self): - top = {'env': {'match': ['state1', 'state2'], 'nomatch': ['state3']}} + top = {"env": {"match": ["state1", "state2"], "nomatch": ["state3"]}} matches = self.highstate.top_matches(top) - self.assertEqual(matches, {'env': ['state1', 'state2']}) + self.assertEqual(matches, {"env": ["state1", "state2"]}) def test_top_matches_with_string(self): - top = {'env': {'match': 'state1', 'nomatch': 'state2'}} + top = {"env": {"match": "state1", "nomatch": "state2"}} matches = self.highstate.top_matches(top) - self.assertEqual(matches, {'env': ['state1']}) + self.assertEqual(matches, {"env": ["state1"]}) def test_matches_whitelist(self): - matches = {'env': ['state1', 'state2', 'state3']} - matches = self.highstate.matches_whitelist(matches, ['state2']) - self.assertEqual(matches, {'env': ['state2']}) + matches = {"env": ["state1", "state2", "state3"]} + matches = self.highstate.matches_whitelist(matches, ["state2"]) + self.assertEqual(matches, {"env": ["state2"]}) def test_matches_whitelist_with_string(self): - matches = {'env': ['state1', 'state2', 'state3']} - matches = self.highstate.matches_whitelist(matches, - 'state2,state3') - self.assertEqual(matches, {'env': ['state2', 'state3']}) + matches = {"env": ["state1", "state2", "state3"]} + matches = self.highstate.matches_whitelist(matches, "state2,state3") + self.assertEqual(matches, {"env": ["state2", "state3"]}) def test_show_state_usage(self): # monkey patch sub methods - self.highstate.avail = { - 'base': ['state.a', 'state.b', 'state.c'] - } + self.highstate.avail = {"base": ["state.a", "state.b", "state.c"]} def verify_tops(*args, **kwargs): return [] @@ -282,7 +331,7 @@ class HighStateTestCase(TestCase, AdaptedConfigurationTestCaseMixin): return None def top_matches(*args, **kwargs): - return {'base': ['state.a', 'state.b']} + return {"base": ["state.a", "state.b"]} self.highstate.verify_tops = verify_tops self.highstate.get_top = get_top @@ -291,24 +340,23 @@ class HighStateTestCase(TestCase, AdaptedConfigurationTestCaseMixin): # get compile_state_usage() result state_usage_dict = self.highstate.compile_state_usage() - self.assertEqual(state_usage_dict['base']['count_unused'], 1) - self.assertEqual(state_usage_dict['base']['count_used'], 2) - self.assertEqual(state_usage_dict['base']['count_all'], 3) - self.assertEqual(state_usage_dict['base']['used'], ['state.a', 'state.b']) - self.assertEqual(state_usage_dict['base']['unused'], ['state.c']) + self.assertEqual(state_usage_dict["base"]["count_unused"], 1) + self.assertEqual(state_usage_dict["base"]["count_used"], 2) + self.assertEqual(state_usage_dict["base"]["count_all"], 3) + self.assertEqual(state_usage_dict["base"]["used"], ["state.a", "state.b"]) + self.assertEqual(state_usage_dict["base"]["unused"], ["state.c"]) def test_find_sls_ids_with_exclude(self): - ''' + """ See https://github.com/saltstack/salt/issues/47182 - ''' - sls_dir = 'issue-47182' + """ + sls_dir = "issue-47182" shutil.copytree( os.path.join(RUNTIME_VARS.BASE_FILES, sls_dir), - os.path.join(self.state_tree_dir, sls_dir) + os.path.join(self.state_tree_dir, sls_dir), ) shutil.move( - os.path.join(self.state_tree_dir, sls_dir, 'top.sls'), - self.state_tree_dir + os.path.join(self.state_tree_dir, sls_dir, "top.sls"), self.state_tree_dir ) # Manually compile the high data. We don't have to worry about all of # the normal error checking we do here since we know that all the SLS @@ -316,286 +364,327 @@ class HighStateTestCase(TestCase, AdaptedConfigurationTestCaseMixin): top = self.highstate.get_top() # pylint: disable=assignment-from-none matches = self.highstate.top_matches(top) high, _ = self.highstate.render_highstate(matches) - ret = salt.state.find_sls_ids('issue-47182.stateA.newer', high) - self.assertEqual(ret, [('somestuff', 'cmd')]) + ret = salt.state.find_sls_ids("issue-47182.stateA.newer", high) + self.assertEqual(ret, [("somestuff", "cmd")]) -@skipIf(pytest is None, 'PyTest is missing') +@skipIf(pytest is None, "PyTest is missing") class StateReturnsTestCase(TestCase): - ''' + """ TestCase for code handling state returns. - ''' + """ def test_state_output_check_changes_is_dict(self): - ''' + """ Test that changes key contains a dictionary. :return: - ''' - data = {'changes': []} - out = statedecorators.OutputUnifier('content_check')(lambda: data)() - assert "'Changes' should be a dictionary" in out['comment'] - assert not out['result'] + """ + data = {"changes": []} + out = statedecorators.OutputUnifier("content_check")(lambda: data)() + assert "'Changes' should be a dictionary" in out["comment"] + assert not out["result"] def test_state_output_check_return_is_dict(self): - ''' + """ Test for the entire return is a dictionary :return: - ''' - data = ['whatever'] - out = statedecorators.OutputUnifier('content_check')(lambda: data)() - assert 'Malformed state return. Data must be a dictionary type' in out['comment'] - assert not out['result'] + """ + data = ["whatever"] + out = statedecorators.OutputUnifier("content_check")(lambda: data)() + assert ( + "Malformed state return. Data must be a dictionary type" in out["comment"] + ) + assert not out["result"] def test_state_output_check_return_has_nrc(self): - ''' + """ Test for name/result/comment keys are inside the return. :return: - ''' - data = {'arbitrary': 'data', 'changes': {}} - out = statedecorators.OutputUnifier('content_check')(lambda: data)() - assert ' The following keys were not present in the state return: name, result, comment' in out['comment'] - assert not out['result'] + """ + data = {"arbitrary": "data", "changes": {}} + out = statedecorators.OutputUnifier("content_check")(lambda: data)() + assert ( + " The following keys were not present in the state return: name, result, comment" + in out["comment"] + ) + assert not out["result"] def test_state_output_unifier_comment_is_not_list(self): - ''' + """ Test for output is unified so the comment is converted to a multi-line string :return: - ''' - data = {'comment': ['data', 'in', 'the', 'list'], 'changes': {}, 'name': None, 'result': 'fantastic!'} - expected = {'comment': 'data\nin\nthe\nlist', 'changes': {}, 'name': None, 'result': True} - assert statedecorators.OutputUnifier('unify')(lambda: data)() == expected + """ + data = { + "comment": ["data", "in", "the", "list"], + "changes": {}, + "name": None, + "result": "fantastic!", + } + expected = { + "comment": "data\nin\nthe\nlist", + "changes": {}, + "name": None, + "result": True, + } + assert statedecorators.OutputUnifier("unify")(lambda: data)() == expected - data = {'comment': ['data', 'in', 'the', 'list'], 'changes': {}, 'name': None, 'result': None} - expected = 'data\nin\nthe\nlist' - assert statedecorators.OutputUnifier('unify')(lambda: data)()['comment'] == expected + data = { + "comment": ["data", "in", "the", "list"], + "changes": {}, + "name": None, + "result": None, + } + expected = "data\nin\nthe\nlist" + assert ( + statedecorators.OutputUnifier("unify")(lambda: data)()["comment"] + == expected + ) def test_state_output_unifier_result_converted_to_true(self): - ''' + """ Test for output is unified so the result is converted to True :return: - ''' - data = {'comment': ['data', 'in', 'the', 'list'], 'changes': {}, 'name': None, 'result': 'Fantastic'} - assert statedecorators.OutputUnifier('unify')(lambda: data)()['result'] is True + """ + data = { + "comment": ["data", "in", "the", "list"], + "changes": {}, + "name": None, + "result": "Fantastic", + } + assert statedecorators.OutputUnifier("unify")(lambda: data)()["result"] is True def test_state_output_unifier_result_converted_to_false(self): - ''' + """ Test for output is unified so the result is converted to False :return: - ''' - data = {'comment': ['data', 'in', 'the', 'list'], 'changes': {}, 'name': None, 'result': ''} - assert statedecorators.OutputUnifier('unify')(lambda: data)()['result'] is False + """ + data = { + "comment": ["data", "in", "the", "list"], + "changes": {}, + "name": None, + "result": "", + } + assert statedecorators.OutputUnifier("unify")(lambda: data)()["result"] is False class StateFormatSlotsTestCase(TestCase, AdaptedConfigurationTestCaseMixin): - ''' + """ TestCase for code handling slots - ''' + """ + def setUp(self): - with patch('salt.state.State._gather_pillar'): - minion_opts = self.get_temp_config('minion') + with patch("salt.state.State._gather_pillar"): + minion_opts = self.get_temp_config("minion") self.state_obj = salt.state.State(minion_opts) def test_format_slots_no_slots(self): - ''' + """ Test the format slots keeps data without slots untouched. - ''' - cdata = { - 'args': [ - 'arg', - ], - 'kwargs': { - 'key': 'val', - } - } + """ + cdata = {"args": ["arg"], "kwargs": {"key": "val"}} self.state_obj.format_slots(cdata) - self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'val'}}) + self.assertEqual(cdata, {"args": ["arg"], "kwargs": {"key": "val"}}) def test_format_slots_arg(self): - ''' + """ Test the format slots is calling a slot specified in args with corresponding arguments. - ''' + """ cdata = { - 'args': [ - '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)', - ], - 'kwargs': { - 'key': 'val', - } + "args": ["__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)"], + "kwargs": {"key": "val"}, } - mock = MagicMock(return_value='fun_return') - with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + mock = MagicMock(return_value="fun_return") + with patch.dict(self.state_obj.functions, {"mod.fun": mock}): self.state_obj.format_slots(cdata) - mock.assert_called_once_with('fun_arg', fun_key='fun_val') - self.assertEqual(cdata, {'args': ['fun_return'], 'kwargs': {'key': 'val'}}) + mock.assert_called_once_with("fun_arg", fun_key="fun_val") + self.assertEqual(cdata, {"args": ["fun_return"], "kwargs": {"key": "val"}}) def test_format_slots_dict_arg(self): - ''' + """ Test the format slots is calling a slot specified in dict arg. - ''' + """ cdata = { - 'args': [ - {'subarg': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)'}, - ], - 'kwargs': { - 'key': 'val', - } + "args": [{"subarg": "__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)"}], + "kwargs": {"key": "val"}, } - mock = MagicMock(return_value='fun_return') - with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + mock = MagicMock(return_value="fun_return") + with patch.dict(self.state_obj.functions, {"mod.fun": mock}): self.state_obj.format_slots(cdata) - mock.assert_called_once_with('fun_arg', fun_key='fun_val') - self.assertEqual(cdata, {'args': [{'subarg': 'fun_return'}], 'kwargs': {'key': 'val'}}) + mock.assert_called_once_with("fun_arg", fun_key="fun_val") + self.assertEqual( + cdata, {"args": [{"subarg": "fun_return"}], "kwargs": {"key": "val"}} + ) def test_format_slots_listdict_arg(self): - ''' + """ Test the format slots is calling a slot specified in list containing a dict. - ''' + """ cdata = { - 'args': [[ - {'subarg': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)'}, - ]], - 'kwargs': { - 'key': 'val', - } + "args": [[{"subarg": "__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)"}]], + "kwargs": {"key": "val"}, } - mock = MagicMock(return_value='fun_return') - with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + mock = MagicMock(return_value="fun_return") + with patch.dict(self.state_obj.functions, {"mod.fun": mock}): self.state_obj.format_slots(cdata) - mock.assert_called_once_with('fun_arg', fun_key='fun_val') - self.assertEqual(cdata, {'args': [[{'subarg': 'fun_return'}]], 'kwargs': {'key': 'val'}}) + mock.assert_called_once_with("fun_arg", fun_key="fun_val") + self.assertEqual( + cdata, {"args": [[{"subarg": "fun_return"}]], "kwargs": {"key": "val"}} + ) def test_format_slots_liststr_arg(self): - ''' + """ Test the format slots is calling a slot specified in list containing a dict. - ''' + """ cdata = { - 'args': [[ - '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)', - ]], - 'kwargs': { - 'key': 'val', - } + "args": [["__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)"]], + "kwargs": {"key": "val"}, } - mock = MagicMock(return_value='fun_return') - with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + mock = MagicMock(return_value="fun_return") + with patch.dict(self.state_obj.functions, {"mod.fun": mock}): self.state_obj.format_slots(cdata) - mock.assert_called_once_with('fun_arg', fun_key='fun_val') - self.assertEqual(cdata, {'args': [['fun_return']], 'kwargs': {'key': 'val'}}) + mock.assert_called_once_with("fun_arg", fun_key="fun_val") + self.assertEqual(cdata, {"args": [["fun_return"]], "kwargs": {"key": "val"}}) def test_format_slots_kwarg(self): - ''' + """ Test the format slots is calling a slot specified in kwargs with corresponding arguments. - ''' + """ cdata = { - 'args': [ - 'arg', - ], - 'kwargs': { - 'key': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)', - } + "args": ["arg"], + "kwargs": {"key": "__slot__:salt:mod.fun(fun_arg, fun_key=fun_val)"}, } - mock = MagicMock(return_value='fun_return') - with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + mock = MagicMock(return_value="fun_return") + with patch.dict(self.state_obj.functions, {"mod.fun": mock}): self.state_obj.format_slots(cdata) - mock.assert_called_once_with('fun_arg', fun_key='fun_val') - self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'fun_return'}}) + mock.assert_called_once_with("fun_arg", fun_key="fun_val") + self.assertEqual(cdata, {"args": ["arg"], "kwargs": {"key": "fun_return"}}) def test_format_slots_multi(self): - ''' + """ Test the format slots is calling all slots with corresponding arguments when multiple slots specified. - ''' + """ cdata = { - 'args': [ - '__slot__:salt:test_mod.fun_a(a_arg, a_key=a_kwarg)', - '__slot__:salt:test_mod.fun_b(b_arg, b_key=b_kwarg)', + "args": [ + "__slot__:salt:test_mod.fun_a(a_arg, a_key=a_kwarg)", + "__slot__:salt:test_mod.fun_b(b_arg, b_key=b_kwarg)", ], - 'kwargs': { - 'kw_key_1': '__slot__:salt:test_mod.fun_c(c_arg, c_key=c_kwarg)', - 'kw_key_2': '__slot__:salt:test_mod.fun_d(d_arg, d_key=d_kwarg)', - } + "kwargs": { + "kw_key_1": "__slot__:salt:test_mod.fun_c(c_arg, c_key=c_kwarg)", + "kw_key_2": "__slot__:salt:test_mod.fun_d(d_arg, d_key=d_kwarg)", + }, } - mock_a = MagicMock(return_value='fun_a_return') - mock_b = MagicMock(return_value='fun_b_return') - mock_c = MagicMock(return_value='fun_c_return') - mock_d = MagicMock(return_value='fun_d_return') - with patch.dict(self.state_obj.functions, {'test_mod.fun_a': mock_a, - 'test_mod.fun_b': mock_b, - 'test_mod.fun_c': mock_c, - 'test_mod.fun_d': mock_d}): + mock_a = MagicMock(return_value="fun_a_return") + mock_b = MagicMock(return_value="fun_b_return") + mock_c = MagicMock(return_value="fun_c_return") + mock_d = MagicMock(return_value="fun_d_return") + with patch.dict( + self.state_obj.functions, + { + "test_mod.fun_a": mock_a, + "test_mod.fun_b": mock_b, + "test_mod.fun_c": mock_c, + "test_mod.fun_d": mock_d, + }, + ): self.state_obj.format_slots(cdata) - mock_a.assert_called_once_with('a_arg', a_key='a_kwarg') - mock_b.assert_called_once_with('b_arg', b_key='b_kwarg') - mock_c.assert_called_once_with('c_arg', c_key='c_kwarg') - mock_d.assert_called_once_with('d_arg', d_key='d_kwarg') - self.assertEqual(cdata, {'args': ['fun_a_return', - 'fun_b_return'], - 'kwargs': {'kw_key_1': 'fun_c_return', - 'kw_key_2': 'fun_d_return'}}) + mock_a.assert_called_once_with("a_arg", a_key="a_kwarg") + mock_b.assert_called_once_with("b_arg", b_key="b_kwarg") + mock_c.assert_called_once_with("c_arg", c_key="c_kwarg") + mock_d.assert_called_once_with("d_arg", d_key="d_kwarg") + self.assertEqual( + cdata, + { + "args": ["fun_a_return", "fun_b_return"], + "kwargs": {"kw_key_1": "fun_c_return", "kw_key_2": "fun_d_return"}, + }, + ) def test_format_slots_malformed(self): - ''' + """ Test the format slots keeps malformed slots untouched. - ''' + """ sls_data = { - 'args': [ - '__slot__:NOT_SUPPORTED:not.called()', - '__slot__:salt:not.called(', - '__slot__:salt:', - '__slot__:salt', - '__slot__:', - '__slot__', + "args": [ + "__slot__:NOT_SUPPORTED:not.called()", + "__slot__:salt:not.called(", + "__slot__:salt:", + "__slot__:salt", + "__slot__:", + "__slot__", ], - 'kwargs': { - 'key3': '__slot__:NOT_SUPPORTED:not.called()', - 'key4': '__slot__:salt:not.called(', - 'key5': '__slot__:salt:', - 'key6': '__slot__:salt', - 'key7': '__slot__:', - 'key8': '__slot__', - } + "kwargs": { + "key3": "__slot__:NOT_SUPPORTED:not.called()", + "key4": "__slot__:salt:not.called(", + "key5": "__slot__:salt:", + "key6": "__slot__:salt", + "key7": "__slot__:", + "key8": "__slot__", + }, } cdata = sls_data.copy() - mock = MagicMock(return_value='return') - with patch.dict(self.state_obj.functions, {'not.called': mock}): + mock = MagicMock(return_value="return") + with patch.dict(self.state_obj.functions, {"not.called": mock}): self.state_obj.format_slots(cdata) mock.assert_not_called() self.assertEqual(cdata, sls_data) def test_slot_traverse_dict(self): - ''' + """ Test the slot parsing of dict response. - ''' + """ cdata = { - 'args': [ - 'arg', - ], - 'kwargs': { - 'key': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val).key1', - } + "args": ["arg"], + "kwargs": {"key": "__slot__:salt:mod.fun(fun_arg, fun_key=fun_val).key1"}, } - return_data = {'key1': 'value1'} + return_data = {"key1": "value1"} mock = MagicMock(return_value=return_data) - with patch.dict(self.state_obj.functions, {'mod.fun': mock}): + with patch.dict(self.state_obj.functions, {"mod.fun": mock}): self.state_obj.format_slots(cdata) - mock.assert_called_once_with('fun_arg', fun_key='fun_val') - self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'value1'}}) + mock.assert_called_once_with("fun_arg", fun_key="fun_val") + self.assertEqual(cdata, {"args": ["arg"], "kwargs": {"key": "value1"}}) def test_slot_append(self): - ''' + """ Test the slot parsing of dict response. - ''' + """ cdata = { - 'args': [ - 'arg', - ], - 'kwargs': { - 'key': '__slot__:salt:mod.fun(fun_arg, fun_key=fun_val).key1 ~ thing~', + "args": ["arg"], + "kwargs": { + "key": "__slot__:salt:mod.fun(fun_arg, fun_key=fun_val).key1 ~ thing~", + }, + } + return_data = {"key1": "value1"} + mock = MagicMock(return_value=return_data) + with patch.dict(self.state_obj.functions, {"mod.fun": mock}): + self.state_obj.format_slots(cdata) + mock.assert_called_once_with("fun_arg", fun_key="fun_val") + self.assertEqual(cdata, {"args": ["arg"], "kwargs": {"key": "value1thing~"}}) + + # Skip on windows like integration.modules.test_state.StateModuleTest.test_parallel_state_with_long_tag + @skipIf( + salt.utils.platform.is_windows(), + "Skipped until parallel states can be fixed on Windows", + ) + def test_format_slots_parallel(self): + """ + Test if slots work with "parallel: true". + """ + high_data = { + "always-changes-and-succeeds": { + "test": [ + {"changes": True}, + {"comment": "__slot__:salt:test.echo(fun_return)"}, + {"parallel": True}, + "configurable_test_state", + {"order": 10000}, + ], + "__env__": "base", + "__sls__": "parallel_slots", } } - return_data = {'key1': 'value1'} - mock = MagicMock(return_value=return_data) - with patch.dict(self.state_obj.functions, {'mod.fun': mock}): - self.state_obj.format_slots(cdata) - mock.assert_called_once_with('fun_arg', fun_key='fun_val') - self.assertEqual(cdata, {'args': ['arg'], 'kwargs': {'key': 'value1thing~'}}) + self.state_obj.jid = "123" + res = self.state_obj.call_high(high_data) + self.state_obj.jid = None + [(_, data)] = res.items() + self.assertEqual(data["comment"], "fun_return") diff --git a/tests/unit/test_template.py b/tests/unit/test_template.py index dea646c7035..2ceec66774a 100644 --- a/tests/unit/test_template.py +++ b/tests/unit/test_template.py @@ -1,101 +1,115 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email: `Mike Place <mp@saltstack.com>` -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import MagicMock - # Import Salt libs from salt import template from salt.ext.six.moves import StringIO +from tests.support.mock import MagicMock + +# Import Salt Testing libs +from tests.support.unit import TestCase class TemplateTestCase(TestCase): - render_dict = {'jinja': 'fake_jinja_func', - 'json': 'fake_json_func', - 'mako': 'fake_make_func'} + render_dict = { + "jinja": "fake_jinja_func", + "json": "fake_json_func", + "mako": "fake_make_func", + } def test_compile_template_bad_type(self): - ''' + """ Test to ensure that unsupported types cannot be passed to the template compiler - ''' - ret = template.compile_template(['1', '2', '3'], None, None, None, None) + """ + ret = template.compile_template(["1", "2", "3"], None, None, None, None) self.assertDictEqual(ret, {}) def test_compile_template_preserves_windows_newlines(self): - ''' + """ Test to ensure that a file with Windows newlines, when rendered by a template renderer, does not eat the CR character. - ''' + """ + def _get_rend(renderer, value): - ''' + """ We need a new MagicMock each time since we're dealing with StringIO objects which are read like files. - ''' + """ return {renderer: MagicMock(return_value=StringIO(value))} - input_data_windows = 'foo\r\nbar\r\nbaz\r\n' - input_data_non_windows = input_data_windows.replace('\r\n', '\n') - renderer = 'test' + input_data_windows = "foo\r\nbar\r\nbaz\r\n" + input_data_non_windows = input_data_windows.replace("\r\n", "\n") + renderer = "test" blacklist = whitelist = [] ret = template.compile_template( - ':string:', + ":string:", _get_rend(renderer, input_data_non_windows), renderer, blacklist, whitelist, - input_data=input_data_windows).read() + input_data=input_data_windows, + ).read() # Even though the mocked renderer returned a string without the windows # newlines, the compiled template should still have them. self.assertEqual(ret, input_data_windows) # Now test that we aren't adding them in unnecessarily. ret = template.compile_template( - ':string:', + ":string:", _get_rend(renderer, input_data_non_windows), renderer, blacklist, whitelist, - input_data=input_data_non_windows).read() + input_data=input_data_non_windows, + ).read() self.assertEqual(ret, input_data_non_windows) # Finally, ensure that we're not unnecessarily replacing the \n with # \r\n in the event that the renderer returned a string with the # windows newlines intact. ret = template.compile_template( - ':string:', + ":string:", _get_rend(renderer, input_data_windows), renderer, blacklist, whitelist, - input_data=input_data_windows).read() + input_data=input_data_windows, + ).read() self.assertEqual(ret, input_data_windows) def test_check_render_pipe_str(self): - ''' + """ Check that all renderers specified in the pipe string are available. - ''' - ret = template.check_render_pipe_str('jinja|json', self.render_dict, None, None) - self.assertIn(('fake_jinja_func', ''), ret) - self.assertIn(('fake_json_func', ''), ret) - self.assertNotIn(('OBVIOUSLY_NOT_HERE', ''), ret) + """ + ret = template.check_render_pipe_str("jinja|json", self.render_dict, None, None) + self.assertIn(("fake_jinja_func", ""), ret) + self.assertIn(("fake_json_func", ""), ret) + self.assertNotIn(("OBVIOUSLY_NOT_HERE", ""), ret) def test_check_renderer_blacklisting(self): - ''' + """ Check that all renderers specified in the pipe string are available. - ''' - ret = template.check_render_pipe_str('jinja|json', self.render_dict, ['jinja'], None) - self.assertListEqual([('fake_json_func', '')], ret) - ret = template.check_render_pipe_str('jinja|json', self.render_dict, None, ['jinja']) - self.assertListEqual([('fake_jinja_func', '')], ret) - ret = template.check_render_pipe_str('jinja|json', self.render_dict, ['jinja'], ['jinja']) + """ + ret = template.check_render_pipe_str( + "jinja|json", self.render_dict, ["jinja"], None + ) + self.assertListEqual([("fake_json_func", "")], ret) + ret = template.check_render_pipe_str( + "jinja|json", self.render_dict, None, ["jinja"] + ) + self.assertListEqual([("fake_jinja_func", "")], ret) + ret = template.check_render_pipe_str( + "jinja|json", self.render_dict, ["jinja"], ["jinja"] + ) self.assertListEqual([], ret) - ret = template.check_render_pipe_str('jinja|json', self.render_dict, ['jinja'], ['jinja', 'json']) - self.assertListEqual([('fake_json_func', '')], ret) + ret = template.check_render_pipe_str( + "jinja|json", self.render_dict, ["jinja"], ["jinja", "json"] + ) + self.assertListEqual([("fake_json_func", "")], ret) diff --git a/tests/unit/test_transport.py b/tests/unit/test_transport.py index f24acb019eb..56b8c0b1b38 100644 --- a/tests/unit/test_transport.py +++ b/tests/unit/test_transport.py @@ -2,6 +2,7 @@ # Import python libs from __future__ import absolute_import + import logging from salt.transport import MessageClientPool @@ -13,18 +14,21 @@ log = logging.getLogger(__name__) class MessageClientPoolTest(TestCase): - class MockClass(object): def __init__(self, *args, **kwargs): self.args = args self.kwargs = kwargs def test_init(self): - opts = {'sock_pool_size': 10} + opts = {"sock_pool_size": 10} args = (0,) - kwargs = {'kwarg': 1} - message_client_pool = MessageClientPool(self.MockClass, opts, args=args, kwargs=kwargs) - self.assertEqual(opts['sock_pool_size'], len(message_client_pool.message_clients)) + kwargs = {"kwarg": 1} + message_client_pool = MessageClientPool( + self.MockClass, opts, args=args, kwargs=kwargs + ) + self.assertEqual( + opts["sock_pool_size"], len(message_client_pool.message_clients) + ) for message_client in message_client_pool.message_clients: self.assertEqual(message_client.args, args) self.assertEqual(message_client.kwargs, kwargs) @@ -32,8 +36,10 @@ class MessageClientPoolTest(TestCase): def test_init_without_config(self): opts = {} args = (0,) - kwargs = {'kwarg': 1} - message_client_pool = MessageClientPool(self.MockClass, opts, args=args, kwargs=kwargs) + kwargs = {"kwarg": 1} + message_client_pool = MessageClientPool( + self.MockClass, opts, args=args, kwargs=kwargs + ) # The size of pool is set as 1 by the MessageClientPool init method. self.assertEqual(1, len(message_client_pool.message_clients)) for message_client in message_client_pool.message_clients: @@ -41,10 +47,12 @@ class MessageClientPoolTest(TestCase): self.assertEqual(message_client.kwargs, kwargs) def test_init_less_than_one(self): - opts = {'sock_pool_size': -1} + opts = {"sock_pool_size": -1} args = (0,) - kwargs = {'kwarg': 1} - message_client_pool = MessageClientPool(self.MockClass, opts, args=args, kwargs=kwargs) + kwargs = {"kwarg": 1} + message_client_pool = MessageClientPool( + self.MockClass, opts, args=args, kwargs=kwargs + ) # The size of pool is set as 1 by the MessageClientPool init method. self.assertEqual(1, len(message_client_pool.message_clients)) for message_client in message_client_pool.message_clients: diff --git a/tests/unit/test_version.py b/tests/unit/test_version.py index e236a482315..309eb083a65 100644 --- a/tests/unit/test_version.py +++ b/tests/unit/test_version.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,82 +7,89 @@ ~~~~~~~~~~~~~~~~~~~~~~~ Test salt's regex git describe version parsing -''' +""" # Import python libs from __future__ import absolute_import + import re -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +import salt.version # Import Salt libs from salt.version import SaltStackVersion, versions_report -import salt.version +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase class VersionTestCase(TestCase): def test_version_parsing(self): - strip_initial_non_numbers_regex = re.compile(r'(?:[^\d]+)?(?P<vs>.*)') + strip_initial_non_numbers_regex = re.compile(r"(?:[^\d]+)?(?P<vs>.*)") expect = ( - ('v0.12.0-19-g767d4f9', (0, 12, 0, 0, '', 0, 19, 'g767d4f9'), None), - ('v0.12.0-85-g2880105', (0, 12, 0, 0, '', 0, 85, 'g2880105'), None), - ('debian/0.11.1+ds-1-3-ga0afcbd', - (0, 11, 1, 0, '', 0, 3, 'ga0afcbd'), '0.11.1-3-ga0afcbd'), - ('0.12.1', (0, 12, 1, 0, '', 0, 0, None), None), - ('0.12.1', (0, 12, 1, 0, '', 0, 0, None), None), - ('0.17.0rc1', (0, 17, 0, 0, 'rc', 1, 0, None), None), - ('v0.17.0rc1-1-g52ebdfd', (0, 17, 0, 0, 'rc', 1, 1, 'g52ebdfd'), None), - ('v2014.1.4.1', (2014, 1, 4, 1, '', 0, 0, None), None), - ('v2014.1.4.1rc3-n/a-abcdefff', (2014, 1, 4, 1, 'rc', 3, -1, 'abcdefff'), None), - ('v3.4.1.1', (3, 4, 1, 1, '', 0, 0, None), None), - ('v3000', (3000, '', 0, 0, None), '3000'), - ('v3000.0', (3000, '', 0, 0, None), '3000'), - ('v4518.1', (4518, 1, '', 0, 0, None), '4518.1'), - ('v3000rc1', (3000, 'rc', 1, 0, None), '3000rc1'), - ('v3000rc1-n/a-abcdefff', (3000, 'rc', 1, -1, 'abcdefff'), None), - ('3000-n/a-1e7bc8f', (3000, '', 0, -1, '1e7bc8f'), None) - + ("v0.12.0-19-g767d4f9", (0, 12, 0, 0, "", 0, 19, "g767d4f9"), None), + ("v0.12.0-85-g2880105", (0, 12, 0, 0, "", 0, 85, "g2880105"), None), + ( + "debian/0.11.1+ds-1-3-ga0afcbd", + (0, 11, 1, 0, "", 0, 3, "ga0afcbd"), + "0.11.1-3-ga0afcbd", + ), + ("0.12.1", (0, 12, 1, 0, "", 0, 0, None), None), + ("0.12.1", (0, 12, 1, 0, "", 0, 0, None), None), + ("0.17.0rc1", (0, 17, 0, 0, "rc", 1, 0, None), None), + ("v0.17.0rc1-1-g52ebdfd", (0, 17, 0, 0, "rc", 1, 1, "g52ebdfd"), None), + ("v2014.1.4.1", (2014, 1, 4, 1, "", 0, 0, None), None), + ( + "v2014.1.4.1rc3-n/a-abcdefff", + (2014, 1, 4, 1, "rc", 3, -1, "abcdefff"), + None, + ), + ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0, None), None), + ("v3000", (3000, "", 0, 0, None), "3000"), + ("v3000.0", (3000, "", 0, 0, None), "3000"), + ("v4518.1", (4518, 1, "", 0, 0, None), "4518.1"), + ("v3000rc1", (3000, "rc", 1, 0, None), "3000rc1"), + ("v3000rc1-n/a-abcdefff", (3000, "rc", 1, -1, "abcdefff"), None), + ("3000-n/a-1e7bc8f", (3000, "", 0, -1, "1e7bc8f"), None), + ("3000.1-n/a-1e7bc8f", (3000, 1, "", 0, -1, "1e7bc8f"), None), ) for vstr, full_info, version in expect: saltstack_version = SaltStackVersion.parse(vstr) - self.assertEqual( - saltstack_version.full_info, full_info - ) + self.assertEqual(saltstack_version.full_info, full_info) if version is None: - version = strip_initial_non_numbers_regex.search(vstr).group('vs') + version = strip_initial_non_numbers_regex.search(vstr).group("vs") self.assertEqual(saltstack_version.string, version) def test_version_comparison(self): examples = ( - ('debian/0.11.1+ds-1-3-ga0afcbd', '0.11.1+ds-2'), - ('v0.12.0-85-g2880105', 'v0.12.0-19-g767d4f9'), - ('v0.17.0rc1-1-g52ebdfd', '0.17.0rc1'), - ('v0.17.0', 'v0.17.0rc1'), - ('Hydrogen', '0.17.0'), - ('Helium', 'Hydrogen'), - ('v2014.1.4.1-n/a-abcdefff', 'v2014.1.4.1rc3-n/a-abcdefff'), - ('v2014.1.4.1-1-abcdefff', 'v2014.1.4.1-n/a-abcdefff'), - ('v2016.12.0rc1', 'v2016.12.0b1'), - ('v2016.12.0beta1', 'v2016.12.0alpha1'), - ('v2016.12.0alpha1', 'v2016.12.0alpha0'), - ('v3000.1', 'v3000'), - ('v3000rc2', 'v3000rc1'), - ('v3001', 'v3000'), - ('v4023rc1', 'v4022rc1'), - ('v3000', 'v3000rc1'), - ('v3000', 'v2019.2.1'), - ('v3000.1', 'v2019.2.1'), + ("debian/0.11.1+ds-1-3-ga0afcbd", "0.11.1+ds-2"), + ("v0.12.0-85-g2880105", "v0.12.0-19-g767d4f9"), + ("v0.17.0rc1-1-g52ebdfd", "0.17.0rc1"), + ("v0.17.0", "v0.17.0rc1"), + ("Hydrogen", "0.17.0"), + ("Helium", "Hydrogen"), + ("v2014.1.4.1-n/a-abcdefff", "v2014.1.4.1rc3-n/a-abcdefff"), + ("v2014.1.4.1-1-abcdefff", "v2014.1.4.1-n/a-abcdefff"), + ("v2016.12.0rc1", "v2016.12.0b1"), + ("v2016.12.0beta1", "v2016.12.0alpha1"), + ("v2016.12.0alpha1", "v2016.12.0alpha0"), + ("v3000.1", "v3000"), + ("v3000rc2", "v3000rc1"), + ("v3001", "v3000"), + ("v4023rc1", "v4022rc1"), + ("v3000", "v3000rc1"), + ("v3000", "v2019.2.1"), + ("v3000.1", "v2019.2.1"), # we created v3000.0rc1 tag on repo # but we should not be using this # version scheme in the future # but still adding test for it - ('v3000', 'v3000.0rc1'), - ('v3000.1rc1', 'v3000.0rc1'), - ('v3000', 'v2019.2.1rc1'), - ('v3001rc1', 'v2019.2.1rc1'), + ("v3000", "v3000.0rc1"), + ("v3000.1rc1", "v3000.0rc1"), + ("v3000", "v2019.2.1rc1"), + ("v3001rc1", "v2019.2.1rc1"), ) for higher_version, lower_version in examples: self.assertTrue(SaltStackVersion.parse(higher_version) > lower_version) @@ -91,20 +98,20 @@ class VersionTestCase(TestCase): def test_unparsable_version(self): with self.assertRaises(ValueError): - SaltStackVersion.from_name('Drunk') + SaltStackVersion.from_name("Drunk") with self.assertRaises(ValueError): - SaltStackVersion.parse('Drunk') + SaltStackVersion.parse("Drunk") def test_sha(self): - ''' + """ test matching sha's - ''' + """ exp_ret = ( - ('d6cd1e2bd19e03a81132a23b2025920577f84e37', True), - ('2880105', True), - ('v3000.0.1', False), - ('v0.12.0-85-g2880105', False) + ("d6cd1e2bd19e03a81132a23b2025920577f84e37", True), + ("2880105", True), + ("v3000.0.1", False), + ("v0.12.0-85-g2880105", False), ) for commit, exp in exp_ret: ret = SaltStackVersion.git_sha_regex.match(commit) @@ -114,86 +121,89 @@ class VersionTestCase(TestCase): assert not ret def test_version_report_lines(self): - ''' + """ Validate padding in versions report is correct - ''' + """ # Get a set of all version report name lenghts including padding - line_lengths = set([ - len(line.split(':')[0]) for line in list(versions_report())[4:] - if line != ' ' and line != 'System Versions:' - ]) + line_lengths = set( + [ + len(line.split(":")[0]) + for line in list(versions_report())[4:] + if line != " " and line != "System Versions:" + ] + ) # Check that they are all the same size (only one element in the set) assert len(line_lengths) == 1 def test_string_new_version(self): - ''' + """ Validate string property method using new versioning scheme - ''' - maj_ver = '3000' + """ + maj_ver = "3000" ver = SaltStackVersion(major=maj_ver) assert not ver.minor assert not ver.bugfix assert maj_ver == ver.string def test_string_new_version_minor(self): - ''' + """ Validate string property method using new versioning scheme alongside minor version - ''' + """ maj_ver = 3000 min_ver = 1 ver = SaltStackVersion(major=maj_ver, minor=min_ver) assert ver.minor == min_ver assert not ver.bugfix - assert ver.string == '{0}.{1}'.format(maj_ver, min_ver) + assert ver.string == "{0}.{1}".format(maj_ver, min_ver) def test_string_new_version_minor_as_string(self): - ''' + """ Validate string property method using new versioning scheme alongside minor version - ''' - maj_ver = '3000' - min_ver = '1' + """ + maj_ver = "3000" + min_ver = "1" ver = SaltStackVersion(major=maj_ver, minor=min_ver) assert ver.minor == int(min_ver) assert not ver.bugfix - assert ver.string == '{0}.{1}'.format(maj_ver, min_ver) + assert ver.string == "{0}.{1}".format(maj_ver, min_ver) # This only seems to happen on a cloned repo without its tags - maj_ver = '3000' - min_ver = '' + maj_ver = "3000" + min_ver = "" ver = SaltStackVersion(major=maj_ver, minor=min_ver) - assert ver.minor is None, '{!r} is not {!r}'.format(ver.minor, min_ver) # pylint: disable=repr-flag-used-in-string + assert ver.minor is None, "{!r} is not {!r}".format(ver.minor, min_ver) assert not ver.bugfix assert ver.string == maj_ver def test_string_old_version(self): - ''' + """ Validate string property method using old versioning scheme alongside minor version - ''' - maj_ver = '2019' - min_ver = '2' + """ + maj_ver = "2019" + min_ver = "2" ver = SaltStackVersion(major=maj_ver, minor=min_ver) assert ver.bugfix == 0 - assert ver.string == '{0}.{1}.0'.format(maj_ver, min_ver) + assert ver.string == "{0}.{1}.0".format(maj_ver, min_ver) def test_noc_info(self): - ''' + """ Test noc_info property method - ''' + """ expect = ( - ('v2014.1.4.1rc3-n/a-abcdefff', (2014, 1, 4, 1, 'rc', 3, -1)), - ('v3.4.1.1', (3, 4, 1, 1, '', 0, 0)), - ('v3000', (3000, '', 0, 0)), - ('v3000.0', (3000, '', 0, 0)), - ('v4518.1', (4518, 1, '', 0, 0)), - ('v3000rc1', (3000, 'rc', 1, 0)), - ('v3000rc1-n/a-abcdefff', (3000, 'rc', 1, -1)), + ("v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1)), + ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0)), + ("v3000", (3000, "", 0, 0)), + ("v3000.0", (3000, "", 0, 0)), + ("v4518.1", (4518, 1, "", 0, 0)), + ("v3000rc1", (3000, "rc", 1, 0)), + ("v3000rc1-n/a-abcdefff", (3000, "rc", 1, -1)), ) for vstr, noc_info in expect: @@ -202,18 +212,17 @@ class VersionTestCase(TestCase): assert len(saltstack_version.noc_info) == len(noc_info) def test_full_info(self): - ''' + """ Test full_Info property method - ''' + """ expect = ( - ('v2014.1.4.1rc3-n/a-abcdefff', (2014, 1, 4, 1, 'rc', 3, -1, 'abcdefff')), - ('v3.4.1.1', (3, 4, 1, 1, '', 0, 0, None)), - ('v3000', (3000, '', 0, 0, None)), - ('v3000.0', (3000, '', 0, 0, None)), - ('v4518.1', (4518, 1, '', 0, 0, None)), - ('v3000rc1', (3000, 'rc', 1, 0, None)), - ('v3000rc1-n/a-abcdefff', (3000, 'rc', 1, -1, 'abcdefff')), - + ("v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1, "abcdefff")), + ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0, None)), + ("v3000", (3000, "", 0, 0, None)), + ("v3000.0", (3000, "", 0, 0, None)), + ("v4518.1", (4518, 1, "", 0, 0, None)), + ("v3000rc1", (3000, "rc", 1, 0, None)), + ("v3000rc1-n/a-abcdefff", (3000, "rc", 1, -1, "abcdefff")), ) for vstr, full_info in expect: @@ -221,40 +230,64 @@ class VersionTestCase(TestCase): assert saltstack_version.full_info, full_info assert len(saltstack_version.full_info) == len(full_info) + def test_full_info_all_versions(self): + """ + Test full_info_all_versions property method + """ + expect = ( + ("v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1, "abcdefff")), + ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0, None)), + ("v3000", (3000, None, None, 0, "", 0, 0, None)), + ("v3000.0", (3000, 0, None, 0, "", 0, 0, None)), + ("v4518.1", (4518, 1, None, 0, "", 0, 0, None)), + ("v3000rc1", (3000, None, None, 0, "rc", 2, 0, None)), + ("v3000rc1-n/a-abcdefff", (3000, None, None, 0, "rc", 1, -1, "abcdefff")), + ) + + for vstr, full_info in expect: + saltstack_version = SaltStackVersion.parse(vstr) + assert saltstack_version.full_info_all_versions, full_info + assert len(saltstack_version.full_info_all_versions) == len(full_info) + def test_discover_version(self): - ''' + """ Test call to __discover_version when using different versions - ''' - version = {('3000', None): - {(b'v3000.0rc2-12-g44fe283a77\n', '3000rc2-12-g44fe283a77'), - (b'v3000', '3000'), - (b'1234567', '3000-n/a-1234567'), }, - (2019, 2): - {(b'v2019.2.0rc2-12-g44fe283a77\n', '2019.2.0rc2-12-g44fe283a77'), - (b'v2019.2.0', '2019.2.0'), - (b'afc9830198dj', '2019.2.0-n/a-afc9830198dj'), }, + """ + version = { + ("3000", None): { + (b"v3000.0rc2-12-g44fe283a77\n", "3000rc2-12-g44fe283a77"), + (b"v3000", "3000"), + (b"1234567", "3000-n/a-1234567"), + }, + (2019, 2): { + (b"v2019.2.0rc2-12-g44fe283a77\n", "2019.2.0rc2-12-g44fe283a77"), + (b"v2019.2.0", "2019.2.0"), + (b"afc9830198dj", "2019.2.0-n/a-afc9830198dj"), + }, } for maj_min, test_v in version.items(): for tag_ver, exp in version[maj_min]: - salt_ver = SaltStackVersion(major=maj_min[0], minor=maj_min[1], bugfix=None) - attrs = {'communicate.return_value': (tag_ver, b''), - 'returncode.return_value': 0} + salt_ver = SaltStackVersion( + major=maj_min[0], minor=maj_min[1], bugfix=None + ) + attrs = { + "communicate.return_value": (tag_ver, b""), + "returncode.return_value": 0, + } proc_ret = MagicMock(**attrs) - proc_mock = patch('subprocess.Popen', return_value=proc_ret) - patch_os = patch('os.path.exists', return_value=True) + proc_mock = patch("subprocess.Popen", return_value=proc_ret) + patch_os = patch("os.path.exists", return_value=True) with proc_mock, patch_os: - ret = getattr(salt.version, '__discover_version')(salt_ver) + ret = getattr(salt.version, "__discover_version")(salt_ver) assert ret == exp def test_info_new_version(self): - ''' + """ test info property method with new versioning scheme - ''' - vers = ((3000, None, None), - (3000, 1, None), - (3001, 0, None)) + """ + vers = ((3000, None, None), (3000, 1, None), (3001, 0, None)) for maj_ver, min_ver, bug_fix in vers: ver = SaltStackVersion(major=maj_ver, minor=min_ver, bugfix=bug_fix) if min_ver: @@ -263,15 +296,49 @@ class VersionTestCase(TestCase): assert ver.info == (maj_ver,) def test_info_old_version(self): - ''' + """ test info property method with old versioning scheme - ''' - vers = ((2019, 2, 1), - (2018, 3, 0), - (2017, 7, None)) + """ + vers = ((2019, 2, 1), (2018, 3, 0), (2017, 7, None)) for maj_ver, min_ver, bug_fix in vers: ver = SaltStackVersion(major=maj_ver, minor=min_ver, bugfix=bug_fix) if bug_fix is None: assert ver.info == (maj_ver, min_ver, 0, 0) else: assert ver.info == (maj_ver, min_ver, bug_fix, 0) + + def test_bugfix_string(self): + """ + test when bugfix is an empty string + """ + ret = SaltStackVersion(3000, 1, "", 0, 0, None) + assert ret.info == (3000, 1) + assert ret.minor == 1 + assert ret.bugfix is None + + def test_version_repr(self): + """ + Test SaltStackVersion repr for both date + and new versioning scheme + """ + expect = ( + ( + (3000, 1, None, None, "", 0, 0, None), + "<SaltStackVersion name='Neon' major=3000 minor=1>", + ), + ( + (3000, 0, None, None, "", 0, 0, None), + "<SaltStackVersion name='Neon' major=3000>", + ), + ( + (2019, 2, 3, None, "", 0, 0, None), + "<SaltStackVersion name='Fluorine' major=2019 minor=2 bugfix=3>", + ), + ( + (2019, 2, 3, None, "rc", 1, 0, None), + "<SaltStackVersion name='Fluorine' major=2019 minor=2 bugfix=3 rc=1>", + ), + ) + + for ver, repr_ret in expect: + assert repr(SaltStackVersion(*ver)) == repr_ret diff --git a/tests/unit/test_virtualname.py b/tests/unit/test_virtualname.py index 85b62d110e4..afcaa4b073e 100644 --- a/tests/unit/test_virtualname.py +++ b/tests/unit/test_virtualname.py @@ -1,25 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.test_virtualname ~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs from __future__ import absolute_import + import logging import os +# Import Salt libs +import salt.ext.six as six +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing libs +from tests.support.unit import TestCase + try: import importlib.util except ImportError: import imp -# Import Salt libs -import salt.ext.six as six - -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.runtests import RUNTIME_VARS log = logging.getLogger(__name__) @@ -35,22 +37,23 @@ class FakeEntry(object): class VirtualNameTestCase(TestCase): - ''' + """ Test that the virtualname is in the module name, to speed up lookup of modules. - ''' + """ + maxDiff = None @staticmethod def _import_module(testpath): if six.PY3: - spec = importlib.util.spec_from_file_location('tmpmodule', testpath) + spec = importlib.util.spec_from_file_location("tmpmodule", testpath) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) else: - fp, pathname, description = imp.find_module('tmpmodule', testpath) + fp, pathname, description = imp.find_module("tmpmodule", testpath) try: - module = imp.load_module('tmpmodule', fp, pathname, description) + module = imp.load_module("tmpmodule", fp, pathname, description) finally: # Since we may exit via an exception, close fp explicitly. if fp: @@ -58,42 +61,42 @@ class VirtualNameTestCase(TestCase): return module def _check_modules(self, path): - ''' + """ check modules in directory - ''' + """ ret = [] for entry in os.listdir(path): name, path = os.path.splitext(os.path.basename(entry))[0], entry - if name.startswith('.') or name.startswith('_'): + if name.startswith(".") or name.startswith("_"): continue - if os.path.isfile(path) and not name.endswith('.py'): + if os.path.isfile(path) and not name.endswith(".py"): continue - testpath = path if os.path.isfile(path) else os.path.join(path, '__init__.py') + testpath = ( + path if os.path.isfile(path) else os.path.join(path, "__init__.py") + ) module = self._import_module(testpath) - if hasattr(module, '__virtualname__'): + if hasattr(module, "__virtualname__"): if module.__virtualname__ not in name: ret.append( 'Virtual name "{0}" is not in the module filename "{1}": {2}'.format( - module.__virtualname__, - name, - path + module.__virtualname__, name, path ) ) return ret def test_check_virtualname(self): - ''' + """ Test that the virtualname is in __name__ of the module - ''' + """ errors = [] - for entry in os.listdir(os.path.join(RUNTIME_VARS.CODE_DIR, 'salt/')): + for entry in os.listdir(os.path.join(RUNTIME_VARS.CODE_DIR, "salt/")): name, path = os.path.splitext(os.path.basename(entry))[0], entry - if name.startswith('.') or name.startswith('_') or not os.path.isdir(path): + if name.startswith(".") or name.startswith("_") or not os.path.isdir(path): continue - if name in ('cli', 'defaults', 'spm', 'daemons', 'ext', 'templates'): + if name in ("cli", "defaults", "spm", "daemons", "ext", "templates"): continue - if name == 'cloud': - entry = os.path.join(RUNTIME_VARS.CODE_DIR, 'salt', 'cloud', 'clouds') + if name == "cloud": + entry = os.path.join(RUNTIME_VARS.CODE_DIR, "salt", "cloud", "clouds") errors.extend(self._check_modules(entry)) for error in errors: log.critical(error) diff --git a/tests/unit/test_zypp_plugins.py b/tests/unit/test_zypp_plugins.py index 86c89c6c503..7abc74b7939 100644 --- a/tests/unit/test_zypp_plugins.py +++ b/tests/unit/test_zypp_plugins.py @@ -1,51 +1,61 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Bo Maryniuk <bo@suse.de> -''' +""" # Import Python Libs from __future__ import absolute_import -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) - -import os import imp +import os import sys -from zypp_plugin import BogusIO + +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf + +try: + from zypp_plugin import BogusIO + + HAS_ZYPP_PLUGIN = True +except ImportError: + HAS_ZYPP_PLUGIN = False if sys.version_info >= (3,): - BUILTINS_OPEN = 'builtins.open' + BUILTINS_OPEN = "builtins.open" else: - BUILTINS_OPEN = '__builtin__.open' + BUILTINS_OPEN = "__builtin__.open" ZYPPNOTIFY_FILE = os.path.sep.join( - os.path.dirname(__file__).split(os.path.sep)[:-2] + - ['scripts', 'suse', 'zypper', 'plugins', 'commit', 'zyppnotify'] + os.path.dirname(__file__).split(os.path.sep)[:-2] + + ["scripts", "suse", "zypper", "plugins", "commit", "zyppnotify"] ) +@skipIf(not HAS_ZYPP_PLUGIN, "zypp_plugin is missing.") class ZyppPluginsTestCase(TestCase): - ''' + """ Test shipped libzypp plugins. - ''' + """ + + @skipIf( + not os.path.exists(ZYPPNOTIFY_FILE), + "Required file '{}' does not exist.".format(ZYPPNOTIFY_FILE), + ) def test_drift_detector(self): - ''' + """ Test drift detector for a correct cookie file. Returns: - ''' - zyppnotify = imp.load_source('zyppnotify', ZYPPNOTIFY_FILE) + """ + zyppnotify = imp.load_source("zyppnotify", ZYPPNOTIFY_FILE) drift = zyppnotify.DriftDetector() drift._get_mtime = MagicMock(return_value=123) - drift._get_checksum = MagicMock(return_value='deadbeef') + drift._get_checksum = MagicMock(return_value="deadbeef") bogus_io = BogusIO() with patch(BUILTINS_OPEN, bogus_io): drift.PLUGINEND(None, None) - self.assertEqual(str(bogus_io), 'deadbeef 123\n') - self.assertEqual(bogus_io.mode, 'w') - self.assertEqual(bogus_io.path, '/var/cache/salt/minion/rpmdb.cookie') + self.assertEqual(str(bogus_io), "deadbeef 123\n") + self.assertEqual(bogus_io.mode, "w") + self.assertEqual(bogus_io.path, "/var/cache/salt/minion/rpmdb.cookie") diff --git a/tests/unit/tokens/test_localfs.py b/tests/unit/tokens/test_localfs.py index ac5113f013f..0196aa6d724 100644 --- a/tests/unit/tokens/test_localfs.py +++ b/tests/unit/tokens/test_localfs.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Tests the localfs tokens interface. -''' +""" from __future__ import absolute_import, print_function, unicode_literals import os @@ -9,14 +9,12 @@ import os import salt.exceptions import salt.tokens.localfs import salt.utils.files - -from tests.support.unit import TestCase from tests.support.helpers import with_tempdir from tests.support.mock import patch +from tests.support.unit import TestCase class CalledWith(object): - def __init__(self, func, called_with=None): self.func = func if called_with is None: @@ -30,68 +28,62 @@ class CalledWith(object): class WriteTokenTest(TestCase): - @with_tempdir() def test_write_token(self, tmpdir): - ''' + """ Validate tokens put in place with an atomic move - ''' - opts = { - 'token_dir': tmpdir - } + """ + opts = {"token_dir": tmpdir} fopen = CalledWith(salt.utils.files.fopen) rename = CalledWith(os.rename) - with patch('salt.utils.files.fopen', fopen), patch('os.rename', rename): + with patch("salt.utils.files.fopen", fopen), patch("os.rename", rename): tdata = salt.tokens.localfs.mk_token(opts, {}) - assert 'token' in tdata - t_path = os.path.join(tmpdir, tdata['token']) - temp_t_path = '{}.tmp'.format(t_path) + assert "token" in tdata + t_path = os.path.join(tmpdir, tdata["token"]) + temp_t_path = "{}.tmp".format(t_path) assert len(fopen.called_with) == 1, len(fopen.called_with) - assert fopen.called_with == [ - ((temp_t_path, 'w+b'), {}) - ], fopen.called_with + assert fopen.called_with == [((temp_t_path, "w+b"), {})], fopen.called_with assert len(rename.called_with) == 1, len(rename.called_with) - assert rename.called_with == [ - ((temp_t_path, t_path), {}) - ], rename.called_with + assert rename.called_with == [((temp_t_path, t_path), {})], rename.called_with class TestLocalFS(TestCase): def setUp(self): # Default expected data - self.expected_data = {'this': 'is', 'some': 'token data'} + self.expected_data = {"this": "is", "some": "token data"} @with_tempdir() def test_get_token_should_return_token_if_exists(self, tempdir): - opts = {'token_dir': tempdir} - tok = salt.tokens.localfs.mk_token( - opts=opts, - tdata=self.expected_data, - )['token'] + opts = {"token_dir": tempdir} + tok = salt.tokens.localfs.mk_token(opts=opts, tdata=self.expected_data,)[ + "token" + ] actual_data = salt.tokens.localfs.get_token(opts=opts, tok=tok) self.assertDictEqual(self.expected_data, actual_data) @with_tempdir() - def test_get_token_should_raise_SaltDeserializationError_if_token_file_is_empty(self, tempdir): - opts = {'token_dir': tempdir} - tok = salt.tokens.localfs.mk_token( - opts=opts, - tdata=self.expected_data, - )['token'] - with salt.utils.files.fopen(os.path.join(tempdir, tok), 'w') as f: + def test_get_token_should_raise_SaltDeserializationError_if_token_file_is_empty( + self, tempdir + ): + opts = {"token_dir": tempdir} + tok = salt.tokens.localfs.mk_token(opts=opts, tdata=self.expected_data,)[ + "token" + ] + with salt.utils.files.fopen(os.path.join(tempdir, tok), "w") as f: f.truncate() with self.assertRaises(salt.exceptions.SaltDeserializationError) as e: salt.tokens.localfs.get_token(opts=opts, tok=tok) @with_tempdir() - def test_get_token_should_raise_SaltDeserializationError_if_token_file_is_malformed(self, tempdir): - opts = {'token_dir': tempdir} - tok = salt.tokens.localfs.mk_token( - opts=opts, - tdata=self.expected_data, - )['token'] - with salt.utils.files.fopen(os.path.join(tempdir, tok), 'w') as f: + def test_get_token_should_raise_SaltDeserializationError_if_token_file_is_malformed( + self, tempdir + ): + opts = {"token_dir": tempdir} + tok = salt.tokens.localfs.mk_token(opts=opts, tdata=self.expected_data,)[ + "token" + ] + with salt.utils.files.fopen(os.path.join(tempdir, tok), "w") as f: f.truncate() - f.write('this is not valid msgpack data') + f.write("this is not valid msgpack data") with self.assertRaises(salt.exceptions.SaltDeserializationError) as e: salt.tokens.localfs.get_token(opts=opts, tok=tok) diff --git a/tests/unit/tops/test_ext_nodes.py b/tests/unit/tops/test_ext_nodes.py index 3acef0aa7dd..4ef250b1e00 100644 --- a/tests/unit/tops/test_ext_nodes.py +++ b/tests/unit/tops/test_ext_nodes.py @@ -1,64 +1,74 @@ # -*- coding: utf-8 -*- -''' +""" Test ext_nodes master_tops module -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import subprocess import textwrap -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import patch, MagicMock +import salt.tops.ext_nodes as ext_nodes # Import Salt libs import salt.utils.stringutils -import salt.tops.ext_nodes as ext_nodes +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase class ExtNodesTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { ext_nodes: { - '__opts__': { - 'master_tops': { + "__opts__": { + "master_tops": { # Since ext_nodes runs the command with shell=True, # this will keep "command not found" errors from # showing up on the console. We'll be mocking the # communicate results anyway. - 'ext_nodes': 'echo', + "ext_nodes": "echo", } } } } def test_ext_nodes(self): - ''' + """ Confirm that subprocess.Popen works as expected and does not raise an exception (see https://github.com/saltstack/salt/pull/46863). - ''' - stdout = salt.utils.stringutils.to_bytes(textwrap.dedent('''\ + """ + stdout = salt.utils.stringutils.to_bytes( + textwrap.dedent( + """\ classes: - one - - two''')) + - two""" + ) + ) communicate_mock = MagicMock(return_value=(stdout, None)) - with patch.object(subprocess.Popen, 'communicate', communicate_mock): - ret = ext_nodes.top(opts={'id': 'foo'}) - self.assertEqual(ret, {'base': ['one', 'two']}) + with patch.object(subprocess.Popen, "communicate", communicate_mock): + ret = ext_nodes.top(opts={"id": "foo"}) + self.assertEqual(ret, {"base": ["one", "two"]}) def test_ext_nodes_with_environment(self): - ''' + """ Same as above, but also tests that the matches are assigned to the proper environment if one is returned by the ext_nodes command. - ''' - stdout = salt.utils.stringutils.to_bytes(textwrap.dedent('''\ + """ + stdout = salt.utils.stringutils.to_bytes( + textwrap.dedent( + """\ classes: - one - two - environment: dev''')) + environment: dev""" + ) + ) communicate_mock = MagicMock(return_value=(stdout, None)) - with patch.object(subprocess.Popen, 'communicate', communicate_mock): - ret = ext_nodes.top(opts={'id': 'foo'}) - self.assertEqual(ret, {'dev': ['one', 'two']}) + with patch.object(subprocess.Popen, "communicate", communicate_mock): + ret = ext_nodes.top(opts={"id": "foo"}) + self.assertEqual(ret, {"dev": ["one", "two"]}) diff --git a/tests/unit/transport/mixins.py b/tests/unit/transport/mixins.py index 540cd33d739..4e41dd94227 100644 --- a/tests/unit/transport/mixins.py +++ b/tests/unit/transport/mixins.py @@ -3,26 +3,29 @@ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals +import salt.ext.tornado.gen + # Import Salt Libs import salt.transport.client # Import 3rd-party libs from salt.ext import six -import salt.ext.tornado.gen def run_loop_in_thread(loop, evt): - ''' + """ Run the provided loop until an event is set - ''' + """ loop.make_current() + @salt.ext.tornado.gen.coroutine def stopper(): while True: if evt.is_set(): loop.stop() break - yield salt.ext.tornado.gen.sleep(.3) + yield salt.ext.tornado.gen.sleep(0.3) + loop.add_callback(stopper) try: loop.start() @@ -31,43 +34,42 @@ def run_loop_in_thread(loop, evt): class ReqChannelMixin(object): - def test_basic(self): - ''' + """ Test a variety of messages, make sure we get the expected responses - ''' + """ msgs = [ - {'foo': 'bar'}, - {'bar': 'baz'}, - {'baz': 'qux', 'list': [1, 2, 3]}, + {"foo": "bar"}, + {"bar": "baz"}, + {"baz": "qux", "list": [1, 2, 3]}, ] for msg in msgs: ret = self.channel.send(msg, timeout=2, tries=1) - self.assertEqual(ret['load'], msg) + self.assertEqual(ret["load"], msg) def test_normalization(self): - ''' + """ Since we use msgpack, we need to test that list types are converted to lists - ''' + """ types = { - 'list': list, + "list": list, } msgs = [ - {'list': tuple([1, 2, 3])}, + {"list": tuple([1, 2, 3])}, ] for msg in msgs: ret = self.channel.send(msg, timeout=2, tries=1) - for k, v in six.iteritems(ret['load']): + for k, v in six.iteritems(ret["load"]): self.assertEqual(types[k], type(v)) def test_badload(self): - ''' + """ Test a variety of bad requests, make sure that we get some sort of error - ''' - msgs = ['', [], tuple()] + """ + msgs = ["", [], tuple()] for msg in msgs: ret = self.channel.send(msg, timeout=2, tries=1) - self.assertEqual(ret, 'payload and load must be a dict') + self.assertEqual(ret, "payload and load must be a dict") class PubChannelMixin(object): @@ -77,23 +79,26 @@ class PubChannelMixin(object): def handle_pub(ret): self.pub = ret self.stop() - self.pub_channel = salt.transport.client.AsyncPubChannel.factory(self.minion_opts, io_loop=self.io_loop) + + self.pub_channel = salt.transport.client.AsyncPubChannel.factory( + self.minion_opts, io_loop=self.io_loop + ) connect_future = self.pub_channel.connect() connect_future.add_done_callback(lambda f: self.stop()) self.wait() connect_future.result() self.pub_channel.on_recv(handle_pub) load = { - 'fun': 'f', - 'arg': 'a', - 'tgt': 't', - 'jid': 'j', - 'ret': 'r', - 'tgt_type': 'glob', + "fun": "f", + "arg": "a", + "tgt": "t", + "jid": "j", + "ret": "r", + "tgt_type": "glob", } self.server_channel.publish(load) self.wait() - self.assertEqual(self.pub['load'], load) + self.assertEqual(self.pub["load"], load) self.pub_channel.on_recv(None) self.server_channel.publish(load) with self.assertRaises(self.failureException): diff --git a/tests/unit/transport/test_ipc.py b/tests/unit/transport/test_ipc.py index 804362cbe1e..e5319566432 100644 --- a/tests/unit/transport/test_ipc.py +++ b/tests/unit/transport/test_ipc.py @@ -1,47 +1,47 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Mike Place <mp@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os + import errno +import logging +import os import socket import threading -import logging - -import salt.ext.tornado.gen -import salt.ext.tornado.ioloop -import salt.ext.tornado.testing import salt.config import salt.exceptions +import salt.ext.tornado.gen +import salt.ext.tornado.ioloop +import salt.ext.tornado.testing +import salt.transport.client import salt.transport.ipc import salt.transport.server -import salt.transport.client import salt.utils.platform - from salt.ext import six from salt.ext.six.moves import range +from tests.support.mock import MagicMock # Import Salt Testing libs from tests.support.runtests import RUNTIME_VARS -from tests.support.mock import MagicMock from tests.support.unit import skipIf log = logging.getLogger(__name__) -@skipIf(salt.utils.platform.is_windows(), 'Windows does not support Posix IPC') +@skipIf(salt.utils.platform.is_windows(), "Windows does not support Posix IPC") class BaseIPCReqCase(salt.ext.tornado.testing.AsyncTestCase): - ''' + """ Test the req server/client pair - ''' + """ + def setUp(self): super(BaseIPCReqCase, self).setUp() - #self._start_handlers = dict(self.io_loop._handlers) - self.socket_path = os.path.join(RUNTIME_VARS.TMP, 'ipc_test.ipc') + # self._start_handlers = dict(self.io_loop._handlers) + self.socket_path = os.path.join(RUNTIME_VARS.TMP, "ipc_test.ipc") self.server_channel = salt.transport.ipc.IPCMessageServer( self.socket_path, @@ -54,7 +54,7 @@ class BaseIPCReqCase(salt.ext.tornado.testing.AsyncTestCase): def tearDown(self): super(BaseIPCReqCase, self).tearDown() - #failures = [] + # failures = [] try: self.server_channel.close() except socket.error as exc: @@ -62,34 +62,33 @@ class BaseIPCReqCase(salt.ext.tornado.testing.AsyncTestCase): # If its not a bad file descriptor error, raise raise os.unlink(self.socket_path) - #for k, v in six.iteritems(self.io_loop._handlers): + # for k, v in six.iteritems(self.io_loop._handlers): # if self._start_handlers.get(k) != v: # failures.append((k, v)) - #if len(failures) > 0: + # if len(failures) > 0: # raise Exception('FDs still attached to the IOLoop: {0}'.format(failures)) del self.payloads del self.socket_path del self.server_channel - #del self._start_handlers + # del self._start_handlers @salt.ext.tornado.gen.coroutine def _handle_payload(self, payload, reply_func): self.payloads.append(payload) yield reply_func(payload) - if isinstance(payload, dict) and payload.get('stop'): + if isinstance(payload, dict) and payload.get("stop"): self.stop() class IPCMessageClient(BaseIPCReqCase): - ''' + """ Test all of the clear msg stuff - ''' + """ def _get_channel(self): - if not hasattr(self, 'channel') or self.channel is None: + if not hasattr(self, "channel") or self.channel is None: self.channel = salt.transport.ipc.IPCMessageClient( - socket_path=self.socket_path, - io_loop=self.io_loop, + socket_path=self.socket_path, io_loop=self.io_loop, ) self.channel.connect(callback=self.stop) self.wait() @@ -118,13 +117,13 @@ class IPCMessageClient(BaseIPCReqCase): # __del__ wasn't called del channel assert self.channel - msg = {'foo': 'bar', 'stop': True} + msg = {"foo": "bar", "stop": True} self.channel.send(msg) self.wait() self.assertEqual(self.payloads[0], msg) def test_basic_send(self): - msg = {'foo': 'bar', 'stop': True} + msg = {"foo": "bar", "stop": True} self.channel.send(msg) self.wait() self.assertEqual(self.payloads[0], msg) @@ -134,17 +133,17 @@ class IPCMessageClient(BaseIPCReqCase): self.server_channel.stream_handler = MagicMock() for i in range(0, 1000): - msgs.append('test_many_send_{0}'.format(i)) + msgs.append("test_many_send_{0}".format(i)) for i in msgs: self.channel.send(i) - self.channel.send({'stop': True}) + self.channel.send({"stop": True}) self.wait() self.assertEqual(self.payloads[:-1], msgs) def test_very_big_message(self): - long_str = ''.join([six.text_type(num) for num in range(10**5)]) - msg = {'long_str': long_str, 'stop': True} + long_str = "".join([six.text_type(num) for num in range(10 ** 5)]) + msg = {"long_str": long_str, "stop": True} self.channel.send(msg) self.wait() self.assertEqual(msg, self.payloads[0]) @@ -153,11 +152,11 @@ class IPCMessageClient(BaseIPCReqCase): local_channel = self._get_channel() for c in (self.channel, local_channel): - c.send('foo') + c.send("foo") - self.channel.send({'stop': True}) + self.channel.send({"stop": True}) self.wait() - self.assertEqual(self.payloads[:-1], ['foo', 'foo']) + self.assertEqual(self.payloads[:-1], ["foo", "foo"]) def test_multistream_errors(self): local_channel = self._get_channel() @@ -166,37 +165,36 @@ class IPCMessageClient(BaseIPCReqCase): c.send(None) for c in (self.channel, local_channel): - c.send('foo') + c.send("foo") - self.channel.send({'stop': True}) + self.channel.send({"stop": True}) self.wait() - self.assertEqual(self.payloads[:-1], [None, None, 'foo', 'foo']) + self.assertEqual(self.payloads[:-1], [None, None, "foo", "foo"]) -@skipIf(salt.utils.platform.is_windows(), 'Windows does not support Posix IPC') +@skipIf(salt.utils.platform.is_windows(), "Windows does not support Posix IPC") class IPCMessagePubSubCase(salt.ext.tornado.testing.AsyncTestCase): - ''' + """ Test all of the clear msg stuff - ''' + """ + def setUp(self): super(IPCMessagePubSubCase, self).setUp() - self.opts = {'ipc_write_buffer': 0} - self.socket_path = os.path.join(RUNTIME_VARS.TMP, 'ipc_test.ipc') + self.opts = {"ipc_write_buffer": 0} + self.socket_path = os.path.join(RUNTIME_VARS.TMP, "ipc_test.ipc") self.pub_channel = self._get_pub_channel() self.sub_channel = self._get_sub_channel() def _get_pub_channel(self): pub_channel = salt.transport.ipc.IPCMessagePublisher( - self.opts, - self.socket_path, - ) + self.opts, self.socket_path, + ) pub_channel.start() return pub_channel def _get_sub_channel(self): sub_channel = salt.transport.ipc.IPCMessageSubscriber( - socket_path=self.socket_path, - io_loop=self.io_loop, + socket_path=self.socket_path, io_loop=self.io_loop, ) sub_channel.connect(callback=self.stop) self.wait() @@ -248,11 +246,11 @@ class IPCMessagePubSubCase(salt.ext.tornado.testing.AsyncTestCase): # Now let both waiting data at once client1.read_async(handler) client2.read_async(handler) - self.pub_channel.publish('TEST') + self.pub_channel.publish("TEST") self.wait() self.assertEqual(len(call_cnt), 2) - self.assertEqual(call_cnt[0], 'TEST') - self.assertEqual(call_cnt[1], 'TEST') + self.assertEqual(call_cnt[0], "TEST") + self.assertEqual(call_cnt[1], "TEST") def test_sync_reading(self): # To be completely fair let's create 2 clients. @@ -261,8 +259,8 @@ class IPCMessagePubSubCase(salt.ext.tornado.testing.AsyncTestCase): call_cnt = [] # Now let both waiting data at once - self.pub_channel.publish('TEST') + self.pub_channel.publish("TEST") ret1 = client1.read_sync() ret2 = client2.read_sync() - self.assertEqual(ret1, 'TEST') - self.assertEqual(ret2, 'TEST') + self.assertEqual(ret1, "TEST") + self.assertEqual(ret2, "TEST") diff --git a/tests/unit/transport/test_tcp.py b/tests/unit/transport/test_tcp.py index 6cc29e14142..58ec85f866e 100644 --- a/tests/unit/transport/test_tcp.py +++ b/tests/unit/transport/test_tcp.py @@ -1,46 +1,55 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Thomas Jackson <jacksontj.89@gmail.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import threading -import socket -import logging -import salt.ext.tornado.gen -import salt.ext.tornado.ioloop -import salt.ext.tornado.concurrent -from salt.ext.tornado.testing import AsyncTestCase, gen_test +import logging +import socket +import threading import salt.config -from salt.ext import six +import salt.exceptions +import salt.ext.tornado.concurrent +import salt.ext.tornado.gen +import salt.ext.tornado.ioloop +import salt.transport.client +import salt.transport.server import salt.utils.platform import salt.utils.process -import salt.transport.server -import salt.transport.client -import salt.exceptions +from salt.ext import six from salt.ext.six.moves import range -from salt.transport.tcp import SaltMessageClientPool, SaltMessageClient, TCPPubServerChannel +from salt.ext.tornado.testing import AsyncTestCase, gen_test +from salt.transport.tcp import ( + SaltMessageClient, + SaltMessageClientPool, + TCPPubServerChannel, +) +from tests.support.helpers import flaky, get_unused_localhost_port +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.mock import MagicMock, patch # Import Salt Testing libs from tests.support.unit import TestCase, skipIf -from tests.support.helpers import get_unused_localhost_port, flaky -from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.mock import MagicMock, patch -from tests.unit.transport.mixins import PubChannelMixin, ReqChannelMixin, run_loop_in_thread +from tests.unit.transport.mixins import ( + PubChannelMixin, + ReqChannelMixin, + run_loop_in_thread, +) log = logging.getLogger(__name__) class BaseTCPReqCase(TestCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Test the req server/client pair - ''' + """ + @classmethod def setUpClass(cls): - if not hasattr(cls, '_handle_payload'): + if not hasattr(cls, "_handle_payload"): return ret_port = get_unused_localhost_port() publish_port = get_unused_localhost_port() @@ -49,35 +58,42 @@ class BaseTCPReqCase(TestCase, AdaptedConfigurationTestCaseMixin): tcp_master_publish_pull = get_unused_localhost_port() tcp_master_workers = get_unused_localhost_port() cls.master_config = cls.get_temp_config( - 'master', - **{'transport': 'tcp', - 'auto_accept': True, - 'ret_port': ret_port, - 'publish_port': publish_port, - 'tcp_master_pub_port': tcp_master_pub_port, - 'tcp_master_pull_port': tcp_master_pull_port, - 'tcp_master_publish_pull': tcp_master_publish_pull, - 'tcp_master_workers': tcp_master_workers} + "master", + **{ + "transport": "tcp", + "auto_accept": True, + "ret_port": ret_port, + "publish_port": publish_port, + "tcp_master_pub_port": tcp_master_pub_port, + "tcp_master_pull_port": tcp_master_pull_port, + "tcp_master_publish_pull": tcp_master_publish_pull, + "tcp_master_workers": tcp_master_workers, + } ) cls.minion_config = cls.get_temp_config( - 'minion', - **{'transport': 'tcp', - 'master_ip': '127.0.0.1', - 'master_port': ret_port, - 'master_uri': 'tcp://127.0.0.1:{0}'.format(ret_port)} + "minion", + **{ + "transport": "tcp", + "master_ip": "127.0.0.1", + "master_port": ret_port, + "master_uri": "tcp://127.0.0.1:{0}".format(ret_port), + } ) - cls.process_manager = salt.utils.process.ProcessManager(name='ReqServer_ProcessManager') + cls.process_manager = salt.utils.process.ProcessManager( + name="ReqServer_ProcessManager" + ) - cls.server_channel = salt.transport.server.ReqServerChannel.factory(cls.master_config) + cls.server_channel = salt.transport.server.ReqServerChannel.factory( + cls.master_config + ) cls.server_channel.pre_fork(cls.process_manager) cls.io_loop = salt.ext.tornado.ioloop.IOLoop() cls.stop = threading.Event() cls.server_channel.post_fork(cls._handle_payload, io_loop=cls.io_loop) cls.server_thread = threading.Thread( - target=run_loop_in_thread, - args=(cls.io_loop, cls.stop,), + target=run_loop_in_thread, args=(cls.io_loop, cls.stop,), ) cls.server_thread.start() @@ -92,19 +108,22 @@ class BaseTCPReqCase(TestCase, AdaptedConfigurationTestCaseMixin): @classmethod @salt.ext.tornado.gen.coroutine def _handle_payload(cls, payload): - ''' + """ TODO: something besides echo - ''' - raise salt.ext.tornado.gen.Return((payload, {'fun': 'send_clear'})) + """ + raise salt.ext.tornado.gen.Return((payload, {"fun": "send_clear"})) -@skipIf(salt.utils.platform.is_darwin(), 'hanging test suite on MacOS') +@skipIf(salt.utils.platform.is_darwin(), "hanging test suite on MacOS") class ClearReqTestCases(BaseTCPReqCase, ReqChannelMixin): - ''' + """ Test all of the clear msg stuff - ''' + """ + def setUp(self): - self.channel = salt.transport.client.ReqChannel.factory(self.minion_config, crypt='clear') + self.channel = salt.transport.client.ReqChannel.factory( + self.minion_config, crypt="clear" + ) def tearDown(self): self.channel.close() @@ -113,13 +132,13 @@ class ClearReqTestCases(BaseTCPReqCase, ReqChannelMixin): @classmethod @salt.ext.tornado.gen.coroutine def _handle_payload(cls, payload): - ''' + """ TODO: something besides echo - ''' - raise salt.ext.tornado.gen.Return((payload, {'fun': 'send_clear'})) + """ + raise salt.ext.tornado.gen.Return((payload, {"fun": "send_clear"})) -@skipIf(salt.utils.platform.is_darwin(), 'hanging test suite on MacOS') +@skipIf(salt.utils.platform.is_darwin(), "hanging test suite on MacOS") class AESReqTestCases(BaseTCPReqCase, ReqChannelMixin): def setUp(self): self.channel = salt.transport.client.ReqChannel.factory(self.minion_config) @@ -131,28 +150,29 @@ class AESReqTestCases(BaseTCPReqCase, ReqChannelMixin): @classmethod @salt.ext.tornado.gen.coroutine def _handle_payload(cls, payload): - ''' + """ TODO: something besides echo - ''' - raise salt.ext.tornado.gen.Return((payload, {'fun': 'send'})) + """ + raise salt.ext.tornado.gen.Return((payload, {"fun": "send"})) # TODO: make failed returns have a specific framing so we can raise the same exception # on encrypted channels @flaky def test_badload(self): - ''' + """ Test a variety of bad requests, make sure that we get some sort of error - ''' - msgs = ['', [], tuple()] + """ + msgs = ["", [], tuple()] for msg in msgs: with self.assertRaises(salt.exceptions.AuthenticationError): ret = self.channel.send(msg) class BaseTCPPubCase(AsyncTestCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Test the req server/client pair - ''' + """ + @classmethod def setUpClass(cls): ret_port = get_unused_localhost_port() @@ -162,49 +182,58 @@ class BaseTCPPubCase(AsyncTestCase, AdaptedConfigurationTestCaseMixin): tcp_master_publish_pull = get_unused_localhost_port() tcp_master_workers = get_unused_localhost_port() cls.master_config = cls.get_temp_config( - 'master', - **{'transport': 'tcp', - 'auto_accept': True, - 'ret_port': ret_port, - 'publish_port': publish_port, - 'tcp_master_pub_port': tcp_master_pub_port, - 'tcp_master_pull_port': tcp_master_pull_port, - 'tcp_master_publish_pull': tcp_master_publish_pull, - 'tcp_master_workers': tcp_master_workers} + "master", + **{ + "transport": "tcp", + "auto_accept": True, + "ret_port": ret_port, + "publish_port": publish_port, + "tcp_master_pub_port": tcp_master_pub_port, + "tcp_master_pull_port": tcp_master_pull_port, + "tcp_master_publish_pull": tcp_master_publish_pull, + "tcp_master_workers": tcp_master_workers, + } ) cls.minion_config = cls.get_temp_config( - 'minion', - **{'transport': 'tcp', - 'master_ip': '127.0.0.1', - 'auth_timeout': 1, - 'master_port': ret_port, - 'master_uri': 'tcp://127.0.0.1:{0}'.format(ret_port)} + "minion", + **{ + "transport": "tcp", + "master_ip": "127.0.0.1", + "auth_timeout": 1, + "master_port": ret_port, + "master_uri": "tcp://127.0.0.1:{0}".format(ret_port), + } ) - cls.process_manager = salt.utils.process.ProcessManager(name='ReqServer_ProcessManager') + cls.process_manager = salt.utils.process.ProcessManager( + name="ReqServer_ProcessManager" + ) - cls.server_channel = salt.transport.server.PubServerChannel.factory(cls.master_config) + cls.server_channel = salt.transport.server.PubServerChannel.factory( + cls.master_config + ) cls.server_channel.pre_fork(cls.process_manager) # we also require req server for auth - cls.req_server_channel = salt.transport.server.ReqServerChannel.factory(cls.master_config) + cls.req_server_channel = salt.transport.server.ReqServerChannel.factory( + cls.master_config + ) cls.req_server_channel.pre_fork(cls.process_manager) cls.io_loop = salt.ext.tornado.ioloop.IOLoop() cls.stop = threading.Event() cls.req_server_channel.post_fork(cls._handle_payload, io_loop=cls.io_loop) cls.server_thread = threading.Thread( - target=run_loop_in_thread, - args=(cls.io_loop, cls.stop,), + target=run_loop_in_thread, args=(cls.io_loop, cls.stop,), ) cls.server_thread.start() @classmethod def _handle_payload(cls, payload): - ''' + """ TODO: something besides echo - ''' - return payload, {'fun': 'send_clear'} + """ + return payload, {"fun": "send_clear"} @classmethod def tearDownClass(cls): @@ -226,50 +255,57 @@ class BaseTCPPubCase(AsyncTestCase, AdaptedConfigurationTestCaseMixin): if self._start_handlers.get(k) != v: failures.append((k, v)) if failures: - raise Exception('FDs still attached to the IOLoop: {0}'.format(failures)) + raise Exception("FDs still attached to the IOLoop: {0}".format(failures)) del self.channel del self._start_handlers class AsyncTCPPubChannelTest(AsyncTestCase, AdaptedConfigurationTestCaseMixin): def test_connect_publish_port(self): - ''' + """ test when publish_port is not 4506 - ''' - opts = self.get_temp_config('master') - opts['master_uri'] = '' - opts['master_ip'] = '127.0.0.1' - opts['publish_port'] = 1234 + """ + opts = self.get_temp_config("master") + opts["master_uri"] = "" + opts["master_ip"] = "127.0.0.1" + opts["publish_port"] = 1234 channel = salt.transport.tcp.AsyncTCPPubChannel(opts) patch_auth = MagicMock(return_value=True) patch_client = MagicMock(spec=SaltMessageClientPool) - with patch('salt.crypt.AsyncAuth.gen_token', patch_auth), \ - patch('salt.crypt.AsyncAuth.authenticated', patch_auth), \ - patch('salt.transport.tcp.SaltMessageClientPool', - patch_client): + with patch("salt.crypt.AsyncAuth.gen_token", patch_auth), patch( + "salt.crypt.AsyncAuth.authenticated", patch_auth + ), patch("salt.transport.tcp.SaltMessageClientPool", patch_client): channel.connect() - assert patch_client.call_args[0][0]['publish_port'] == opts['publish_port'] + assert patch_client.call_args[0][0]["publish_port"] == opts["publish_port"] -@skipIf(True, 'Skip until we can devote time to fix this test') +@skipIf(True, "Skip until we can devote time to fix this test") class AsyncPubChannelTest(BaseTCPPubCase, PubChannelMixin): - ''' + """ Tests around the publish system - ''' + """ class SaltMessageClientPoolTest(AsyncTestCase): def setUp(self): super(SaltMessageClientPoolTest, self).setUp() sock_pool_size = 5 - with patch('salt.transport.tcp.SaltMessageClient.__init__', MagicMock(return_value=None)): - self.message_client_pool = SaltMessageClientPool({'sock_pool_size': sock_pool_size}, - args=({}, '', 0)) + with patch( + "salt.transport.tcp.SaltMessageClient.__init__", + MagicMock(return_value=None), + ): + self.message_client_pool = SaltMessageClientPool( + {"sock_pool_size": sock_pool_size}, args=({}, "", 0) + ) self.original_message_clients = self.message_client_pool.message_clients - self.message_client_pool.message_clients = [MagicMock() for _ in range(sock_pool_size)] + self.message_client_pool.message_clients = [ + MagicMock() for _ in range(sock_pool_size) + ] def tearDown(self): - with patch('salt.transport.tcp.SaltMessageClient.close', MagicMock(return_value=None)): + with patch( + "salt.transport.tcp.SaltMessageClient.close", MagicMock(return_value=None) + ): del self.original_message_clients super(SaltMessageClientPoolTest, self).tearDown() @@ -286,10 +322,10 @@ class SaltMessageClientPoolTest(AsyncTestCase): for message_client_mock in self.message_client_pool.message_clients: message_client_mock.send_queue = [0, 0, 0] message_client_mock._stream.write.return_value = [] - self.assertEqual([], self.message_client_pool.write_to_stream('')) + self.assertEqual([], self.message_client_pool.write_to_stream("")) self.message_client_pool.message_clients[2].send_queue = [0] self.message_client_pool.message_clients[2]._stream.write.return_value = [1] - self.assertEqual([1], self.message_client_pool.write_to_stream('')) + self.assertEqual([1], self.message_client_pool.write_to_stream("")) def test_close(self): self.message_client_pool.close() @@ -309,7 +345,7 @@ class SaltMessageClientPoolTest(AsyncTestCase): for message_client_mock in self.message_client_pool.message_clients: future = salt.ext.tornado.concurrent.Future() - future.set_result('foo') + future.set_result("foo") message_client_mock.connect.return_value = future self.assertIsNone(test_connect(self)) @@ -319,10 +355,12 @@ class SaltMessageClientPoolTest(AsyncTestCase): def test_connect(self): yield self.message_client_pool.connect() - for idx, message_client_mock in enumerate(self.message_client_pool.message_clients): + for idx, message_client_mock in enumerate( + self.message_client_pool.message_clients + ): future = salt.ext.tornado.concurrent.Future() if idx % 2 == 0: - future.set_result('foo') + future.set_result("foo") message_client_mock.connect.return_value = future with self.assertRaises(salt.ext.tornado.ioloop.TimeoutError): @@ -330,9 +368,8 @@ class SaltMessageClientPoolTest(AsyncTestCase): class SaltMessageClientCleanupTest(TestCase, AdaptedConfigurationTestCaseMixin): - def setUp(self): - self.listen_on = '127.0.0.1' + self.listen_on = "127.0.0.1" self.port = get_unused_localhost_port() self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) @@ -344,12 +381,12 @@ class SaltMessageClientCleanupTest(TestCase, AdaptedConfigurationTestCaseMixin): del self.sock def test_message_client(self): - ''' + """ test message client cleanup on close - ''' + """ orig_loop = salt.ext.tornado.ioloop.IOLoop() orig_loop.make_current() - opts = self.get_temp_config('master') + opts = self.get_temp_config("master") client = SaltMessageClient(opts, self.listen_on, self.port) # Mock the io_loop's stop method so we know when it has been called. @@ -384,11 +421,11 @@ class SaltMessageClientCleanupTest(TestCase, AdaptedConfigurationTestCaseMixin): class TCPPubServerChannelTest(TestCase, AdaptedConfigurationTestCaseMixin): - @patch('salt.master.SMaster.secrets') - @patch('salt.crypt.Crypticle') - @patch('salt.utils.asynchronous.SyncWrapper') + @patch("salt.master.SMaster.secrets") + @patch("salt.crypt.Crypticle") + @patch("salt.utils.asynchronous.SyncWrapper") def test_publish_filtering(self, sync_wrapper, crypticle, secrets): - opts = self.get_temp_config('master') + opts = self.get_temp_config("master") opts["sign_pub_messages"] = False channel = TCPPubServerChannel(opts) @@ -416,19 +453,21 @@ class TCPPubServerChannelTest(TestCase, AdaptedConfigurationTestCaseMixin): self.assertEqual(payload["topic_lst"], ["minion01"]) # try with syndic settings - opts['order_masters'] = True + opts["order_masters"] = True channel.publish({"test": "value", "tgt_type": "list", "tgt": ["minion01"]}) payload = wrap.send.call_args[0][0] # verify we send it without topic for syndics assert "topic_lst" not in payload - @patch('salt.utils.minions.CkMinions.check_minions') - @patch('salt.master.SMaster.secrets') - @patch('salt.crypt.Crypticle') - @patch('salt.utils.asynchronous.SyncWrapper') - def test_publish_filtering_str_list(self, sync_wrapper, crypticle, secrets, check_minions): - opts = self.get_temp_config('master') + @patch("salt.utils.minions.CkMinions.check_minions") + @patch("salt.master.SMaster.secrets") + @patch("salt.crypt.Crypticle") + @patch("salt.utils.asynchronous.SyncWrapper") + def test_publish_filtering_str_list( + self, sync_wrapper, crypticle, secrets, check_minions + ): + opts = self.get_temp_config("master") opts["sign_pub_messages"] = False channel = TCPPubServerChannel(opts) diff --git a/tests/unit/transport/test_zeromq.py b/tests/unit/transport/test_zeromq.py index 2e540bdff01..71cf6343c70 100644 --- a/tests/unit/transport/test_zeromq.py +++ b/tests/unit/transport/test_zeromq.py @@ -1,64 +1,73 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Thomas Jackson <jacksontj.89@gmail.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import time -import threading -import multiprocessing + import ctypes +import multiprocessing +import os +import threading +import time from concurrent.futures.thread import ThreadPoolExecutor +# Import Salt libs +import salt.config +import salt.exceptions +import salt.ext.tornado.gen +import salt.ext.tornado.ioloop +import salt.log.setup +import salt.transport.client +import salt.transport.server +import salt.utils.platform +import salt.utils.process + +# Import 3rd-party libs +import zmq.eventloop.ioloop +from salt.ext import six +from salt.ext.six.moves import range +from salt.ext.tornado.testing import AsyncTestCase +from salt.transport.zeromq import AsyncReqMessageClientPool +from tests.support.helpers import flaky, get_unused_localhost_port +from tests.support.mixins import AdaptedConfigurationTestCaseMixin +from tests.support.mock import MagicMock, patch + +# Import test support libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf +from tests.unit.transport.mixins import ( + PubChannelMixin, + ReqChannelMixin, + run_loop_in_thread, +) + # linux_distribution deprecated in py3.7 try: from platform import linux_distribution except ImportError: from distro import linux_distribution -# Import 3rd-party libs -import zmq.eventloop.ioloop + # support pyzmq 13.0.x, TODO: remove once we force people to 14.0.x -if not hasattr(zmq.eventloop.ioloop, 'ZMQIOLoop'): +if not hasattr(zmq.eventloop.ioloop, "ZMQIOLoop"): zmq.eventloop.ioloop.ZMQIOLoop = zmq.eventloop.ioloop.IOLoop -from salt.ext.tornado.testing import AsyncTestCase -import salt.ext.tornado.gen -# Import Salt libs -import salt.config -import salt.log.setup -from salt.ext import six -import salt.utils.process -import salt.utils.platform -import salt.transport.server -import salt.transport.client -import salt.exceptions -from salt.ext.six.moves import range -from salt.transport.zeromq import AsyncReqMessageClientPool -import salt.ext.tornado.ioloop - -# Import test support libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import TestCase, skipIf -from tests.support.helpers import flaky, get_unused_localhost_port -from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.mock import MagicMock, patch -from tests.unit.transport.mixins import PubChannelMixin, ReqChannelMixin, run_loop_in_thread ON_SUSE = False -if 'SuSE' in linux_distribution(full_distribution_name=False): +if "SuSE" in linux_distribution(full_distribution_name=False): ON_SUSE = True class BaseZMQReqCase(TestCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Test the req server/client pair - ''' + """ + @classmethod def setUpClass(cls): - if not hasattr(cls, '_handle_payload'): + if not hasattr(cls, "_handle_payload"): return ret_port = get_unused_localhost_port() publish_port = get_unused_localhost_port() @@ -67,41 +76,51 @@ class BaseZMQReqCase(TestCase, AdaptedConfigurationTestCaseMixin): tcp_master_publish_pull = get_unused_localhost_port() tcp_master_workers = get_unused_localhost_port() cls.master_config = cls.get_temp_config( - 'master', - **{'transport': 'zeromq', - 'auto_accept': True, - 'ret_port': ret_port, - 'publish_port': publish_port, - 'tcp_master_pub_port': tcp_master_pub_port, - 'tcp_master_pull_port': tcp_master_pull_port, - 'tcp_master_publish_pull': tcp_master_publish_pull, - 'tcp_master_workers': tcp_master_workers} + "master", + **{ + "transport": "zeromq", + "auto_accept": True, + "ret_port": ret_port, + "publish_port": publish_port, + "tcp_master_pub_port": tcp_master_pub_port, + "tcp_master_pull_port": tcp_master_pull_port, + "tcp_master_publish_pull": tcp_master_publish_pull, + "tcp_master_workers": tcp_master_workers, + } ) cls.minion_config = cls.get_temp_config( - 'minion', - **{'transport': 'zeromq', - 'master_ip': '127.0.0.1', - 'master_port': ret_port, - 'auth_timeout': 5, - 'auth_tries': 1, - 'master_uri': 'tcp://127.0.0.1:{0}'.format(ret_port)} + "minion", + **{ + "transport": "zeromq", + "master_ip": "127.0.0.1", + "master_port": ret_port, + "auth_timeout": 5, + "auth_tries": 1, + "master_uri": "tcp://127.0.0.1:{0}".format(ret_port), + } ) - cls.process_manager = salt.utils.process.ProcessManager(name='ReqServer_ProcessManager') + cls.process_manager = salt.utils.process.ProcessManager( + name="ReqServer_ProcessManager" + ) - cls.server_channel = salt.transport.server.ReqServerChannel.factory(cls.master_config) + cls.server_channel = salt.transport.server.ReqServerChannel.factory( + cls.master_config + ) cls.server_channel.pre_fork(cls.process_manager) cls.io_loop = salt.ext.tornado.ioloop.IOLoop() cls.evt = threading.Event() cls.server_channel.post_fork(cls._handle_payload, io_loop=cls.io_loop) - cls.server_thread = threading.Thread(target=run_loop_in_thread, args=(cls.io_loop, cls.evt)) + cls.server_thread = threading.Thread( + target=run_loop_in_thread, args=(cls.io_loop, cls.evt) + ) cls.server_thread.start() @classmethod def tearDownClass(cls): - if not hasattr(cls, '_handle_payload'): + if not hasattr(cls, "_handle_payload"): return # Attempting to kill the children hangs the test suite. # Let the test suite handle this instead. @@ -109,7 +128,9 @@ class BaseZMQReqCase(TestCase, AdaptedConfigurationTestCaseMixin): cls.process_manager.kill_children() cls.evt.set() cls.server_thread.join() - time.sleep(2) # Give the procs a chance to fully close before we stop the io_loop + time.sleep( + 2 + ) # Give the procs a chance to fully close before we stop the io_loop cls.server_channel.close() del cls.server_channel del cls.io_loop @@ -120,18 +141,21 @@ class BaseZMQReqCase(TestCase, AdaptedConfigurationTestCaseMixin): @classmethod def _handle_payload(cls, payload): - ''' + """ TODO: something besides echo - ''' - return payload, {'fun': 'send_clear'} + """ + return payload, {"fun": "send_clear"} class ClearReqTestCases(BaseZMQReqCase, ReqChannelMixin): - ''' + """ Test all of the clear msg stuff - ''' + """ + def setUp(self): - self.channel = salt.transport.client.ReqChannel.factory(self.minion_config, crypt='clear') + self.channel = salt.transport.client.ReqChannel.factory( + self.minion_config, crypt="clear" + ) def tearDown(self): self.channel.close() @@ -140,25 +164,29 @@ class ClearReqTestCases(BaseZMQReqCase, ReqChannelMixin): @classmethod @salt.ext.tornado.gen.coroutine def _handle_payload(cls, payload): - ''' + """ TODO: something besides echo - ''' - raise salt.ext.tornado.gen.Return((payload, {'fun': 'send_clear'})) + """ + raise salt.ext.tornado.gen.Return((payload, {"fun": "send_clear"})) def test_master_uri_override(self): - ''' + """ ensure master_uri kwarg is respected - ''' + """ # minion_config should be 127.0.0.1, we want a different uri that still connects - uri = 'tcp://{master_ip}:{master_port}'.format(master_ip='localhost', master_port=self.minion_config['master_port']) + uri = "tcp://{master_ip}:{master_port}".format( + master_ip="localhost", master_port=self.minion_config["master_port"] + ) channel = salt.transport.Channel.factory(self.minion_config, master_uri=uri) - self.assertIn('localhost', channel.master_uri) + self.assertIn("localhost", channel.master_uri) del channel @flaky -@skipIf(ON_SUSE, 'Skipping until https://github.com/saltstack/salt/issues/32902 gets fixed') +@skipIf( + ON_SUSE, "Skipping until https://github.com/saltstack/salt/issues/32902 gets fixed" +) class AESReqTestCases(BaseZMQReqCase, ReqChannelMixin): def setUp(self): self.channel = salt.transport.client.ReqChannel.factory(self.minion_config) @@ -170,10 +198,10 @@ class AESReqTestCases(BaseZMQReqCase, ReqChannelMixin): @classmethod @salt.ext.tornado.gen.coroutine def _handle_payload(cls, payload): - ''' + """ TODO: something besides echo - ''' - raise salt.ext.tornado.gen.Return((payload, {'fun': 'send'})) + """ + raise salt.ext.tornado.gen.Return((payload, {"fun": "send"})) # TODO: make failed returns have a specific framing so we can raise the same exception # on encrypted channels @@ -184,22 +212,23 @@ class AESReqTestCases(BaseZMQReqCase, ReqChannelMixin): # #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! def test_badload(self): - ''' + """ Test a variety of bad requests, make sure that we get some sort of error - ''' + """ # TODO: This test should be re-enabled when Jenkins moves to C7. # Once the version of salt-testing is increased to something newer than the September # release of salt-testing, the @flaky decorator should be applied to this test. - msgs = ['', [], tuple()] + msgs = ["", [], tuple()] for msg in msgs: with self.assertRaises(salt.exceptions.AuthenticationError): ret = self.channel.send(msg, timeout=5) class BaseZMQPubCase(AsyncTestCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Test the req server/client pair - ''' + """ + @classmethod def setUpClass(cls): ret_port = get_unused_localhost_port() @@ -209,46 +238,64 @@ class BaseZMQPubCase(AsyncTestCase, AdaptedConfigurationTestCaseMixin): tcp_master_publish_pull = get_unused_localhost_port() tcp_master_workers = get_unused_localhost_port() cls.master_config = cls.get_temp_config( - 'master', - **{'transport': 'zeromq', - 'auto_accept': True, - 'ret_port': ret_port, - 'publish_port': publish_port, - 'tcp_master_pub_port': tcp_master_pub_port, - 'tcp_master_pull_port': tcp_master_pull_port, - 'tcp_master_publish_pull': tcp_master_publish_pull, - 'tcp_master_workers': tcp_master_workers} + "master", + **{ + "transport": "zeromq", + "auto_accept": True, + "ret_port": ret_port, + "publish_port": publish_port, + "tcp_master_pub_port": tcp_master_pub_port, + "tcp_master_pull_port": tcp_master_pull_port, + "tcp_master_publish_pull": tcp_master_publish_pull, + "tcp_master_workers": tcp_master_workers, + } ) - cls.minion_config = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) + cls.minion_config = salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion") + ) cls.minion_config = cls.get_temp_config( - 'minion', - **{'transport': 'zeromq', - 'master_ip': '127.0.0.1', - 'master_port': ret_port, - 'master_uri': 'tcp://127.0.0.1:{0}'.format(ret_port)} + "minion", + **{ + "transport": "zeromq", + "master_ip": "127.0.0.1", + "master_port": ret_port, + "master_uri": "tcp://127.0.0.1:{0}".format(ret_port), + } ) - cls.process_manager = salt.utils.process.ProcessManager(name='ReqServer_ProcessManager') + cls.process_manager = salt.utils.process.ProcessManager( + name="ReqServer_ProcessManager" + ) - cls.server_channel = salt.transport.server.PubServerChannel.factory(cls.master_config) + cls.server_channel = salt.transport.server.PubServerChannel.factory( + cls.master_config + ) cls.server_channel.pre_fork(cls.process_manager) # we also require req server for auth - cls.req_server_channel = salt.transport.server.ReqServerChannel.factory(cls.master_config) + cls.req_server_channel = salt.transport.server.ReqServerChannel.factory( + cls.master_config + ) cls.req_server_channel.pre_fork(cls.process_manager) cls._server_io_loop = salt.ext.tornado.ioloop.IOLoop() cls.evt = threading.Event() - cls.req_server_channel.post_fork(cls._handle_payload, io_loop=cls._server_io_loop) - cls.server_thread = threading.Thread(target=run_loop_in_thread, args=(cls._server_io_loop, cls.evt)) + cls.req_server_channel.post_fork( + cls._handle_payload, io_loop=cls._server_io_loop + ) + cls.server_thread = threading.Thread( + target=run_loop_in_thread, args=(cls._server_io_loop, cls.evt) + ) cls.server_thread.start() @classmethod def tearDownClass(cls): cls.process_manager.kill_children() cls.process_manager.stop_restarting() - time.sleep(2) # Give the procs a chance to fully close before we stop the io_loop + time.sleep( + 2 + ) # Give the procs a chance to fully close before we stop the io_loop cls.evt.set() cls.server_thread.join() cls.req_server_channel.close() @@ -263,10 +310,10 @@ class BaseZMQPubCase(AsyncTestCase, AdaptedConfigurationTestCaseMixin): @classmethod def _handle_payload(cls, payload): - ''' + """ TODO: something besides echo - ''' - return payload, {'fun': 'send_clear'} + """ + return payload, {"fun": "send_clear"} def setUp(self): super(BaseZMQPubCase, self).setUp() @@ -280,14 +327,15 @@ class BaseZMQPubCase(AsyncTestCase, AdaptedConfigurationTestCaseMixin): failures.append((k, v)) del self._start_handlers if len(failures) > 0: - raise Exception('FDs still attached to the IOLoop: {0}'.format(failures)) + raise Exception("FDs still attached to the IOLoop: {0}".format(failures)) -@skipIf(True, 'Skip until we can devote time to fix this test') +@skipIf(True, "Skip until we can devote time to fix this test") class AsyncPubChannelTest(BaseZMQPubCase, PubChannelMixin): - ''' + """ Tests around the publish system - ''' + """ + def get_new_ioloop(self): return salt.ext.tornado.ioloop.IOLoop() @@ -296,14 +344,23 @@ class AsyncReqMessageClientPoolTest(TestCase): def setUp(self): super(AsyncReqMessageClientPoolTest, self).setUp() sock_pool_size = 5 - with patch('salt.transport.zeromq.AsyncReqMessageClient.__init__', MagicMock(return_value=None)): - self.message_client_pool = AsyncReqMessageClientPool({'sock_pool_size': sock_pool_size}, - args=({}, '')) + with patch( + "salt.transport.zeromq.AsyncReqMessageClient.__init__", + MagicMock(return_value=None), + ): + self.message_client_pool = AsyncReqMessageClientPool( + {"sock_pool_size": sock_pool_size}, args=({}, "") + ) self.original_message_clients = self.message_client_pool.message_clients - self.message_client_pool.message_clients = [MagicMock() for _ in range(sock_pool_size)] + self.message_client_pool.message_clients = [ + MagicMock() for _ in range(sock_pool_size) + ] def tearDown(self): - with patch('salt.transport.zeromq.AsyncReqMessageClient.destroy', MagicMock(return_value=None)): + with patch( + "salt.transport.zeromq.AsyncReqMessageClient.destroy", + MagicMock(return_value=None), + ): del self.original_message_clients super(AsyncReqMessageClientPoolTest, self).tearDown() @@ -325,55 +382,55 @@ class AsyncReqMessageClientPoolTest(TestCase): class ZMQConfigTest(TestCase): def test_master_uri(self): - ''' + """ test _get_master_uri method - ''' + """ - m_ip = '127.0.0.1' + m_ip = "127.0.0.1" m_port = 4505 - s_ip = '111.1.0.1' + s_ip = "111.1.0.1" s_port = 4058 - m_ip6 = '1234:5678::9abc' - s_ip6 = '1234:5678::1:9abc' + m_ip6 = "1234:5678::9abc" + s_ip6 = "1234:5678::1:9abc" - with patch('salt.transport.zeromq.LIBZMQ_VERSION_INFO', (4, 1, 6)), \ - patch('salt.transport.zeromq.ZMQ_VERSION_INFO', (16, 0, 1)): + with patch("salt.transport.zeromq.LIBZMQ_VERSION_INFO", (4, 1, 6)), patch( + "salt.transport.zeromq.ZMQ_VERSION_INFO", (16, 0, 1) + ): # pass in both source_ip and source_port - assert salt.transport.zeromq._get_master_uri(master_ip=m_ip, - master_port=m_port, - source_ip=s_ip, - source_port=s_port) == 'tcp://{0}:{1};{2}:{3}'.format(s_ip, s_port, m_ip, m_port) + assert salt.transport.zeromq._get_master_uri( + master_ip=m_ip, master_port=m_port, source_ip=s_ip, source_port=s_port + ) == "tcp://{0}:{1};{2}:{3}".format(s_ip, s_port, m_ip, m_port) - assert salt.transport.zeromq._get_master_uri(master_ip=m_ip6, - master_port=m_port, - source_ip=s_ip6, - source_port=s_port) == 'tcp://[{0}]:{1};[{2}]:{3}'.format(s_ip6, s_port, m_ip6, m_port) + assert salt.transport.zeromq._get_master_uri( + master_ip=m_ip6, master_port=m_port, source_ip=s_ip6, source_port=s_port + ) == "tcp://[{0}]:{1};[{2}]:{3}".format(s_ip6, s_port, m_ip6, m_port) # source ip and source_port empty - assert salt.transport.zeromq._get_master_uri(master_ip=m_ip, - master_port=m_port) == 'tcp://{0}:{1}'.format(m_ip, m_port) + assert salt.transport.zeromq._get_master_uri( + master_ip=m_ip, master_port=m_port + ) == "tcp://{0}:{1}".format(m_ip, m_port) - assert salt.transport.zeromq._get_master_uri(master_ip=m_ip6, - master_port=m_port) == 'tcp://[{0}]:{1}'.format(m_ip6, m_port) + assert salt.transport.zeromq._get_master_uri( + master_ip=m_ip6, master_port=m_port + ) == "tcp://[{0}]:{1}".format(m_ip6, m_port) # pass in only source_ip - assert salt.transport.zeromq._get_master_uri(master_ip=m_ip, - master_port=m_port, - source_ip=s_ip) == 'tcp://{0}:0;{1}:{2}'.format(s_ip, m_ip, m_port) + assert salt.transport.zeromq._get_master_uri( + master_ip=m_ip, master_port=m_port, source_ip=s_ip + ) == "tcp://{0}:0;{1}:{2}".format(s_ip, m_ip, m_port) - assert salt.transport.zeromq._get_master_uri(master_ip=m_ip6, - master_port=m_port, - source_ip=s_ip6) == 'tcp://[{0}]:0;[{1}]:{2}'.format(s_ip6, m_ip6, m_port) + assert salt.transport.zeromq._get_master_uri( + master_ip=m_ip6, master_port=m_port, source_ip=s_ip6 + ) == "tcp://[{0}]:0;[{1}]:{2}".format(s_ip6, m_ip6, m_port) # pass in only source_port - assert salt.transport.zeromq._get_master_uri(master_ip=m_ip, - master_port=m_port, - source_port=s_port) == 'tcp://0.0.0.0:{0};{1}:{2}'.format(s_port, m_ip, m_port) + assert salt.transport.zeromq._get_master_uri( + master_ip=m_ip, master_port=m_port, source_port=s_port + ) == "tcp://0.0.0.0:{0};{1}:{2}".format(s_port, m_ip, m_port) class PubServerChannel(TestCase, AdaptedConfigurationTestCaseMixin): - @classmethod def setUpClass(cls): ret_port = get_unused_localhost_port() @@ -383,32 +440,34 @@ class PubServerChannel(TestCase, AdaptedConfigurationTestCaseMixin): tcp_master_publish_pull = get_unused_localhost_port() tcp_master_workers = get_unused_localhost_port() cls.master_config = cls.get_temp_config( - 'master', - **{'transport': 'zeromq', - 'auto_accept': True, - 'ret_port': ret_port, - 'publish_port': publish_port, - 'tcp_master_pub_port': tcp_master_pub_port, - 'tcp_master_pull_port': tcp_master_pull_port, - 'tcp_master_publish_pull': tcp_master_publish_pull, - 'tcp_master_workers': tcp_master_workers, - 'sign_pub_messages': False, + "master", + **{ + "transport": "zeromq", + "auto_accept": True, + "ret_port": ret_port, + "publish_port": publish_port, + "tcp_master_pub_port": tcp_master_pub_port, + "tcp_master_pull_port": tcp_master_pull_port, + "tcp_master_publish_pull": tcp_master_publish_pull, + "tcp_master_workers": tcp_master_workers, + "sign_pub_messages": False, } ) - salt.master.SMaster.secrets['aes'] = { - 'secret': multiprocessing.Array( - ctypes.c_char, - six.b(salt.crypt.Crypticle.generate_key_string()), + salt.master.SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( + ctypes.c_char, six.b(salt.crypt.Crypticle.generate_key_string()), ), } cls.minion_config = cls.get_temp_config( - 'minion', - **{'transport': 'zeromq', - 'master_ip': '127.0.0.1', - 'master_port': ret_port, - 'auth_timeout': 5, - 'auth_tries': 1, - 'master_uri': 'tcp://127.0.0.1:{0}'.format(ret_port)} + "minion", + **{ + "transport": "zeromq", + "master_ip": "127.0.0.1", + "master_port": ret_port, + "auth_timeout": 5, + "auth_tries": 1, + "master_uri": "tcp://127.0.0.1:{0}".format(ret_port), + } ) @classmethod @@ -424,7 +483,9 @@ class PubServerChannel(TestCase, AdaptedConfigurationTestCaseMixin): self.io_loop.make_current() self.io_loop_thread = threading.Thread(target=self.io_loop.start) self.io_loop_thread.start() - self.process_manager = salt.utils.process.ProcessManager(name='PubServer_ProcessManager') + self.process_manager = salt.utils.process.ProcessManager( + name="PubServer_ProcessManager" + ) def tearDown(self): self.io_loop.add_callback(self.io_loop.stop) @@ -437,194 +498,243 @@ class PubServerChannel(TestCase, AdaptedConfigurationTestCaseMixin): @staticmethod def _gather_results(opts, pub_uri, results, timeout=120, messages=None): - ''' + """ Gather results until then number of seconds specified by timeout passes without reveiving a message - ''' + """ ctx = zmq.Context() sock = ctx.socket(zmq.SUB) sock.setsockopt(zmq.LINGER, -1) - sock.setsockopt(zmq.SUBSCRIBE, b'') + sock.setsockopt(zmq.SUBSCRIBE, b"") sock.connect(pub_uri) last_msg = time.time() serial = salt.payload.Serial(opts) - crypticle = salt.crypt.Crypticle(opts, salt.master.SMaster.secrets['aes']['secret'].value) + crypticle = salt.crypt.Crypticle( + opts, salt.master.SMaster.secrets["aes"]["secret"].value + ) while time.time() - last_msg < timeout: try: payload = sock.recv(zmq.NOBLOCK) except zmq.ZMQError: - time.sleep(.01) + time.sleep(0.01) else: if messages: if messages != 1: messages -= 1 continue - payload = crypticle.loads(serial.loads(payload)['load']) - if 'stop' in payload: + payload = crypticle.loads(serial.loads(payload)["load"]) + if "stop" in payload: break last_msg = time.time() - results.append(payload['jid']) + results.append(payload["jid"]) - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_publish_to_pubserv_ipc(self): - ''' + """ Test sending 10K messags to ZeroMQPubServerChannel using IPC transport ZMQ's ipc transport not supported on Windows - ''' - opts = dict(self.master_config, ipc_mode='ipc', pub_hwm=0) + """ + opts = dict(self.master_config, ipc_mode="ipc", pub_hwm=0) server_channel = salt.transport.zeromq.ZeroMQPubServerChannel(opts) - server_channel.pre_fork(self.process_manager, kwargs={ - 'log_queue': salt.log.setup.get_multiprocessing_logging_queue() - }) - pub_uri = 'tcp://{interface}:{publish_port}'.format(**server_channel.opts) + server_channel.pre_fork( + self.process_manager, + kwargs={"log_queue": salt.log.setup.get_multiprocessing_logging_queue()}, + ) + pub_uri = "tcp://{interface}:{publish_port}".format(**server_channel.opts) send_num = 10000 expect = [] results = [] - gather = threading.Thread(target=self._gather_results, args=(self.minion_config, pub_uri, results,)) + gather = threading.Thread( + target=self._gather_results, args=(self.minion_config, pub_uri, results,) + ) gather.start() # Allow time for server channel to start, especially on windows time.sleep(2) for i in range(send_num): expect.append(i) - load = {'tgt_type': 'glob', 'tgt': '*', 'jid': i} + load = {"tgt_type": "glob", "tgt": "*", "jid": i} server_channel.publish(load) - server_channel.publish( - {'tgt_type': 'glob', 'tgt': '*', 'stop': True} - ) + server_channel.publish({"tgt_type": "glob", "tgt": "*", "stop": True}) gather.join() server_channel.pub_close() assert len(results) == send_num, (len(results), set(expect).difference(results)) def test_zeromq_publish_port(self): - ''' + """ test when connecting that we use the publish_port set in opts when its not 4506 - ''' - opts = dict(self.master_config, ipc_mode='ipc', - pub_hwm=0, recon_randomize=False, - publish_port=455505, - recon_default=1, recon_max=2, master_ip='127.0.0.1', - acceptance_wait_time=5, acceptance_wait_time_max=5) - opts['master_uri'] = 'tcp://{interface}:{publish_port}'.format(**opts) + """ + opts = dict( + self.master_config, + ipc_mode="ipc", + pub_hwm=0, + recon_randomize=False, + publish_port=455505, + recon_default=1, + recon_max=2, + master_ip="127.0.0.1", + acceptance_wait_time=5, + acceptance_wait_time_max=5, + ) + opts["master_uri"] = "tcp://{interface}:{publish_port}".format(**opts) channel = salt.transport.zeromq.AsyncZeroMQPubChannel(opts) patch_socket = MagicMock(return_value=True) patch_auth = MagicMock(return_value=True) - with patch.object(channel, '_socket', patch_socket), \ - patch.object(channel, 'auth', patch_auth): + with patch.object(channel, "_socket", patch_socket), patch.object( + channel, "auth", patch_auth + ): channel.connect() - assert str(opts['publish_port']) in patch_socket.mock_calls[0][1][0] + assert str(opts["publish_port"]) in patch_socket.mock_calls[0][1][0] def test_zeromq_zeromq_filtering_decode_message_no_match(self): - ''' + """ test AsyncZeroMQPubChannel _decode_messages when zmq_filtering enabled and minion does not match - ''' - message = [b'4f26aeafdb2367620a393c973eddbe8f8b846eb', - b'\x82\xa3enc\xa3aes\xa4load\xda\x00`\xeeR\xcf' - b'\x0eaI#V\x17if\xcf\xae\x05\xa7\xb3bN\xf7\xb2\xe2' - b'\xd0sF\xd1\xd4\xecB\xe8\xaf"/*ml\x80Q3\xdb\xaexg' - b'\x8e\x8a\x8c\xd3l\x03\\,J\xa7\x01i\xd1:]\xe3\x8d' - b'\xf4\x03\x88K\x84\n`\xe8\x9a\xad\xad\xc6\x8ea\x15>' - b'\x92m\x9e\xc7aM\x11?\x18;\xbd\x04c\x07\x85\x99\xa3\xea[\x00D'] + """ + message = [ + b"4f26aeafdb2367620a393c973eddbe8f8b846eb", + b"\x82\xa3enc\xa3aes\xa4load\xda\x00`\xeeR\xcf" + b"\x0eaI#V\x17if\xcf\xae\x05\xa7\xb3bN\xf7\xb2\xe2" + b'\xd0sF\xd1\xd4\xecB\xe8\xaf"/*ml\x80Q3\xdb\xaexg' + b"\x8e\x8a\x8c\xd3l\x03\\,J\xa7\x01i\xd1:]\xe3\x8d" + b"\xf4\x03\x88K\x84\n`\xe8\x9a\xad\xad\xc6\x8ea\x15>" + b"\x92m\x9e\xc7aM\x11?\x18;\xbd\x04c\x07\x85\x99\xa3\xea[\x00D", + ] - opts = dict(self.master_config, ipc_mode='ipc', - pub_hwm=0, zmq_filtering=True, recon_randomize=False, - recon_default=1, recon_max=2, master_ip='127.0.0.1', - acceptance_wait_time=5, acceptance_wait_time_max=5) - opts['master_uri'] = 'tcp://{interface}:{publish_port}'.format(**opts) + opts = dict( + self.master_config, + ipc_mode="ipc", + pub_hwm=0, + zmq_filtering=True, + recon_randomize=False, + recon_default=1, + recon_max=2, + master_ip="127.0.0.1", + acceptance_wait_time=5, + acceptance_wait_time_max=5, + ) + opts["master_uri"] = "tcp://{interface}:{publish_port}".format(**opts) server_channel = salt.transport.zeromq.AsyncZeroMQPubChannel(opts) - with patch('salt.crypt.AsyncAuth.crypticle', - MagicMock(return_value={'tgt_type': 'glob', 'tgt': '*', - 'jid': 1})) as mock_test: + with patch( + "salt.crypt.AsyncAuth.crypticle", + MagicMock(return_value={"tgt_type": "glob", "tgt": "*", "jid": 1}), + ) as mock_test: res = server_channel._decode_messages(message) assert res.result() is None def test_zeromq_zeromq_filtering_decode_message(self): - ''' + """ test AsyncZeroMQPubChannel _decode_messages when zmq_filtered enabled - ''' - message = [b'4f26aeafdb2367620a393c973eddbe8f8b846ebd', - b'\x82\xa3enc\xa3aes\xa4load\xda\x00`\xeeR\xcf' - b'\x0eaI#V\x17if\xcf\xae\x05\xa7\xb3bN\xf7\xb2\xe2' - b'\xd0sF\xd1\xd4\xecB\xe8\xaf"/*ml\x80Q3\xdb\xaexg' - b'\x8e\x8a\x8c\xd3l\x03\\,J\xa7\x01i\xd1:]\xe3\x8d' - b'\xf4\x03\x88K\x84\n`\xe8\x9a\xad\xad\xc6\x8ea\x15>' - b'\x92m\x9e\xc7aM\x11?\x18;\xbd\x04c\x07\x85\x99\xa3\xea[\x00D'] + """ + message = [ + b"4f26aeafdb2367620a393c973eddbe8f8b846ebd", + b"\x82\xa3enc\xa3aes\xa4load\xda\x00`\xeeR\xcf" + b"\x0eaI#V\x17if\xcf\xae\x05\xa7\xb3bN\xf7\xb2\xe2" + b'\xd0sF\xd1\xd4\xecB\xe8\xaf"/*ml\x80Q3\xdb\xaexg' + b"\x8e\x8a\x8c\xd3l\x03\\,J\xa7\x01i\xd1:]\xe3\x8d" + b"\xf4\x03\x88K\x84\n`\xe8\x9a\xad\xad\xc6\x8ea\x15>" + b"\x92m\x9e\xc7aM\x11?\x18;\xbd\x04c\x07\x85\x99\xa3\xea[\x00D", + ] - opts = dict(self.master_config, ipc_mode='ipc', - pub_hwm=0, zmq_filtering=True, recon_randomize=False, - recon_default=1, recon_max=2, master_ip='127.0.0.1', - acceptance_wait_time=5, acceptance_wait_time_max=5) - opts['master_uri'] = 'tcp://{interface}:{publish_port}'.format(**opts) + opts = dict( + self.master_config, + ipc_mode="ipc", + pub_hwm=0, + zmq_filtering=True, + recon_randomize=False, + recon_default=1, + recon_max=2, + master_ip="127.0.0.1", + acceptance_wait_time=5, + acceptance_wait_time_max=5, + ) + opts["master_uri"] = "tcp://{interface}:{publish_port}".format(**opts) server_channel = salt.transport.zeromq.AsyncZeroMQPubChannel(opts) - with patch('salt.crypt.AsyncAuth.crypticle', - MagicMock(return_value={'tgt_type': 'glob', 'tgt': '*', - 'jid': 1})) as mock_test: + with patch( + "salt.crypt.AsyncAuth.crypticle", + MagicMock(return_value={"tgt_type": "glob", "tgt": "*", "jid": 1}), + ) as mock_test: res = server_channel._decode_messages(message) - assert res.result()['enc'] == 'aes' + assert res.result()["enc"] == "aes" - @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS') + @skipIf(salt.utils.platform.is_windows(), "Skip on Windows OS") def test_zeromq_filtering(self): - ''' + """ Test sending messags to publisher using UDP with zeromq_filtering enabled - ''' - opts = dict(self.master_config, ipc_mode='ipc', - pub_hwm=0, zmq_filtering=True, acceptance_wait_time=5) + """ + opts = dict( + self.master_config, + ipc_mode="ipc", + pub_hwm=0, + zmq_filtering=True, + acceptance_wait_time=5, + ) server_channel = salt.transport.zeromq.ZeroMQPubServerChannel(opts) - server_channel.pre_fork(self.process_manager, kwargs={ - 'log_queue': salt.log.setup.get_multiprocessing_logging_queue() - }) - pub_uri = 'tcp://{interface}:{publish_port}'.format(**server_channel.opts) + server_channel.pre_fork( + self.process_manager, + kwargs={"log_queue": salt.log.setup.get_multiprocessing_logging_queue()}, + ) + pub_uri = "tcp://{interface}:{publish_port}".format(**server_channel.opts) send_num = 1 expect = [] results = [] - gather = threading.Thread(target=self._gather_results, - args=(self.minion_config, pub_uri, results,), - kwargs={'messages': 2}) + gather = threading.Thread( + target=self._gather_results, + args=(self.minion_config, pub_uri, results,), + kwargs={"messages": 2}, + ) gather.start() # Allow time for server channel to start, especially on windows time.sleep(2) expect.append(send_num) - load = {'tgt_type': 'glob', 'tgt': '*', 'jid': send_num} - with patch('salt.utils.minions.CkMinions.check_minions', - MagicMock(return_value={'minions': ['minion'], 'missing': [], - 'ssh_minions': False})): + load = {"tgt_type": "glob", "tgt": "*", "jid": send_num} + with patch( + "salt.utils.minions.CkMinions.check_minions", + MagicMock( + return_value={ + "minions": ["minion"], + "missing": [], + "ssh_minions": False, + } + ), + ): server_channel.publish(load) - server_channel.publish( - {'tgt_type': 'glob', 'tgt': '*', 'stop': True} - ) + server_channel.publish({"tgt_type": "glob", "tgt": "*", "stop": True}) gather.join() server_channel.pub_close() assert len(results) == send_num, (len(results), set(expect).difference(results)) def test_publish_to_pubserv_tcp(self): - ''' + """ Test sending 10K messags to ZeroMQPubServerChannel using TCP transport - ''' - opts = dict(self.master_config, ipc_mode='tcp', pub_hwm=0) + """ + opts = dict(self.master_config, ipc_mode="tcp", pub_hwm=0) server_channel = salt.transport.zeromq.ZeroMQPubServerChannel(opts) - server_channel.pre_fork(self.process_manager, kwargs={ - 'log_queue': salt.log.setup.get_multiprocessing_logging_queue() - }) - pub_uri = 'tcp://{interface}:{publish_port}'.format(**server_channel.opts) + server_channel.pre_fork( + self.process_manager, + kwargs={"log_queue": salt.log.setup.get_multiprocessing_logging_queue()}, + ) + pub_uri = "tcp://{interface}:{publish_port}".format(**server_channel.opts) send_num = 10000 expect = [] results = [] - gather = threading.Thread(target=self._gather_results, args=(self.minion_config, pub_uri, results,)) + gather = threading.Thread( + target=self._gather_results, args=(self.minion_config, pub_uri, results,) + ) gather.start() # Allow time for server channel to start, especially on windows time.sleep(2) for i in range(send_num): expect.append(i) - load = {'tgt_type': 'glob', 'tgt': '*', 'jid': i} + load = {"tgt_type": "glob", "tgt": "*", "jid": i} server_channel.publish(load) gather.join() server_channel.pub_close() @@ -634,7 +744,7 @@ class PubServerChannel(TestCase, AdaptedConfigurationTestCaseMixin): def _send_small(opts, sid, num=10): server_channel = salt.transport.zeromq.ZeroMQPubServerChannel(opts) for i in range(num): - load = {'tgt_type': 'glob', 'tgt': '*', 'jid': '{}-{}'.format(sid, i)} + load = {"tgt_type": "glob", "tgt": "*", "jid": "{}-{}".format(sid, i)} server_channel.publish(load) server_channel.close() @@ -642,37 +752,45 @@ class PubServerChannel(TestCase, AdaptedConfigurationTestCaseMixin): def _send_large(opts, sid, num=10, size=250000 * 3): server_channel = salt.transport.zeromq.ZeroMQPubServerChannel(opts) for i in range(num): - load = {'tgt_type': 'glob', 'tgt': '*', 'jid': '{}-{}'.format(sid, i), 'xdata': '0' * size} + load = { + "tgt_type": "glob", + "tgt": "*", + "jid": "{}-{}".format(sid, i), + "xdata": "0" * size, + } server_channel.publish(load) server_channel.close() def test_issue_36469_tcp(self): - ''' + """ Test sending both large and small messags to publisher using TCP https://github.com/saltstack/salt/issues/36469 - ''' - opts = dict(self.master_config, ipc_mode='tcp', pub_hwm=0) + """ + opts = dict(self.master_config, ipc_mode="tcp", pub_hwm=0) server_channel = salt.transport.zeromq.ZeroMQPubServerChannel(opts) - server_channel.pre_fork(self.process_manager, kwargs={ - 'log_queue': salt.log.setup.get_multiprocessing_logging_queue() - }) + server_channel.pre_fork( + self.process_manager, + kwargs={"log_queue": salt.log.setup.get_multiprocessing_logging_queue()}, + ) send_num = 10 * 4 expect = [] results = [] - pub_uri = 'tcp://{interface}:{publish_port}'.format(**opts) + pub_uri = "tcp://{interface}:{publish_port}".format(**opts) # Allow time for server channel to start, especially on windows time.sleep(2) - gather = threading.Thread(target=self._gather_results, args=(self.minion_config, pub_uri, results,)) + gather = threading.Thread( + target=self._gather_results, args=(self.minion_config, pub_uri, results,) + ) gather.start() with ThreadPoolExecutor(max_workers=4) as executor: executor.submit(self._send_small, opts, 1) executor.submit(self._send_small, opts, 2) executor.submit(self._send_small, opts, 3) executor.submit(self._send_large, opts, 4) - expect = ['{}-{}'.format(a, b) for a in range(10) for b in (1, 2, 3, 4)] + expect = ["{}-{}".format(a, b) for a in range(10) for b in (1, 2, 3, 4)] time.sleep(0.1) - server_channel.publish({'tgt_type': 'glob', 'tgt': '*', 'stop': True}) + server_channel.publish({"tgt_type": "glob", "tgt": "*", "stop": True}) gather.join() server_channel.pub_close() assert len(results) == send_num, (len(results), set(expect).difference(results)) diff --git a/tests/unit/utils/cache_mods/cache_mod.py b/tests/unit/utils/cache_mods/cache_mod.py index 91fe9ee6879..7260ca734ad 100644 --- a/tests/unit/utils/cache_mods/cache_mod.py +++ b/tests/unit/utils/cache_mods/cache_mod.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- -''' +""" This is a module used in unit.utils.cache to test the context wrapper functions -''' +""" +from __future__ import absolute_import import salt.utils.cache @@ -12,8 +13,8 @@ def __virtual__(): @salt.utils.cache.context_cache def test_context_module(): - if 'called' in __context__: - __context__['called'] += 1 + if "called" in __context__: + __context__["called"] += 1 else: - __context__['called'] = 0 + __context__["called"] = 0 return __context__ diff --git a/tests/unit/utils/test_aggregation.py b/tests/unit/utils/test_aggregation.py index e83b4ea8b5f..1b7d607bdb4 100644 --- a/tests/unit/utils/test_aggregation.py +++ b/tests/unit/utils/test_aggregation.py @@ -3,137 +3,71 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals +# Import salt libs +from salt.utils.aggregation import Map, Scalar, aggregate + # Import Salt Testing libs from tests.support.unit import TestCase -# Import salt libs -from salt.utils.aggregation import aggregate, Map, Scalar - class TestAggregation(TestCase): def test_merging(self): - a = { - 'foo': 42, - 'bar': 'first' - } - b = { - 'bar': 'second' - } + a = {"foo": 42, "bar": "first"} + b = {"bar": "second"} - c, d = 'first', 'second' + c, d = "first", "second" # introspection for level in (None, False, 0, []): - assert aggregate(a, b, level=level) == { - 'bar': 'second' - } - assert aggregate(c, d, level=level) == 'second' + assert aggregate(a, b, level=level) == {"bar": "second"} + assert aggregate(c, d, level=level) == "second" # first level aggregation - for level in (1, [1, 0], [True], '10000'): - assert aggregate(a, b, level=level) == { - 'foo': 42, - 'bar': 'second' - } - assert aggregate(c, d, level=level) == ['first', 'second'] + for level in (1, [1, 0], [True], "10000"): + assert aggregate(a, b, level=level) == {"foo": 42, "bar": "second"} + assert aggregate(c, d, level=level) == ["first", "second"] # 1-2nd level aggregation - for level in (2, [1, 1], [True, True], '11'): + for level in (2, [1, 1], [True, True], "11"): assert aggregate(a, b, level=level) == { - 'foo': 42, - 'bar': ['first', 'second'] + "foo": 42, + "bar": ["first", "second"], }, aggregate(a, b, level=2) - assert aggregate(c, d, level=level) == ['first', 'second'] + assert aggregate(c, d, level=level) == ["first", "second"] # full aggregation for level in (True,): assert aggregate(a, b, level=level) == { - 'foo': 42, - 'bar': ['first', 'second'] + "foo": 42, + "bar": ["first", "second"], } - assert aggregate(c, d, level=level) == ['first', 'second'] + assert aggregate(c, d, level=level) == ["first", "second"] def test_nested(self): - a = { - 'foo': { - 'bar': 'first' - } - } - b = { - 'foo': { - 'bar': 'second' - } - } - assert aggregate(a, b) == { - 'foo': { - 'bar': 'second' - } - }, aggregate(a, b) + a = {"foo": {"bar": "first"}} + b = {"foo": {"bar": "second"}} + assert aggregate(a, b) == {"foo": {"bar": "second"}}, aggregate(a, b) - a = { - 'foo': { - 'bar': Scalar('first') - } - } - b = { - 'foo': { - 'bar': Scalar('second'), - } - } + a = {"foo": {"bar": Scalar("first")}} + b = {"foo": {"bar": Scalar("second")}} - assert aggregate(a, b) == { - 'foo': { - 'bar': ['first', 'second'] - } - }, aggregate(a, b) + assert aggregate(a, b) == {"foo": {"bar": ["first", "second"]}}, aggregate(a, b) def test_introspection(self): - a = { - 'foo': { - 'lvl1': { - 'lvl2-a': 'first', - 'lvl2-b': 'first' - } - } - } + a = {"foo": {"lvl1": {"lvl2-a": "first", "lvl2-b": "first"}}} - b = { - 'foo': { - 'lvl1': { - 'lvl2-a': 'second' - } - } - } + b = {"foo": {"lvl1": {"lvl2-a": "second"}}} - assert aggregate(a, b) == { - 'foo': { - 'lvl1': { - 'lvl2-a': 'second' - } - } - }, aggregate(a, b) + assert aggregate(a, b) == {"foo": {"lvl1": {"lvl2-a": "second"}}}, aggregate( + a, b + ) def test_instruction(self): - a = { - 'foo': Map({ - 'bar': Scalar('first') - }) - } - b = { - 'foo': Map({ - 'bar': Scalar('second') - }) - } - c = { - 'foo': Map({ - 'another': 'value' - }) - } + a = {"foo": Map({"bar": Scalar("first")})} + b = {"foo": Map({"bar": Scalar("second")})} + c = {"foo": Map({"another": "value"})} result = aggregate(c, aggregate(a, b), level=2) assert result == { - 'foo': { - 'bar': ['first', 'second'], - 'another': 'value' - } + "foo": {"bar": ["first", "second"], "another": "value"} }, result diff --git a/tests/unit/utils/test_args.py b/tests/unit/utils/test_args.py index b68a37854af..d7517bed4fb 100644 --- a/tests/unit/utils/test_args.py +++ b/tests/unit/utils/test_args.py @@ -2,86 +2,116 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -from collections import namedtuple + import logging +from collections import namedtuple + +import salt.utils.args # Import Salt Libs from salt.exceptions import SaltInvocationError from salt.ext import six -import salt.utils.args +from tests.support.mock import DEFAULT, patch # Import Salt Testing Libs from tests.support.unit import TestCase -from tests.support.mock import ( - DEFAULT, - patch -) log = logging.getLogger(__name__) class ArgsTestCase(TestCase): - ''' + """ TestCase for salt.utils.args module - ''' + """ def test_condition_input_string(self): - ''' + """ Test passing a jid on the command line - ''' - cmd = salt.utils.args.condition_input(['*', 'foo.bar', 20141020201325675584], None) + """ + cmd = salt.utils.args.condition_input( + ["*", "foo.bar", 20141020201325675584], None + ) self.assertIsInstance(cmd[2], six.text_type) def test_clean_kwargs(self): - self.assertDictEqual(salt.utils.args.clean_kwargs(foo='bar'), {'foo': 'bar'}) - self.assertDictEqual(salt.utils.args.clean_kwargs(__pub_foo='bar'), {}) - self.assertDictEqual(salt.utils.args.clean_kwargs(__foo_bar='gwar'), {}) - self.assertDictEqual(salt.utils.args.clean_kwargs(foo_bar='gwar'), {'foo_bar': 'gwar'}) + self.assertDictEqual(salt.utils.args.clean_kwargs(foo="bar"), {"foo": "bar"}) + self.assertDictEqual(salt.utils.args.clean_kwargs(__pub_foo="bar"), {}) + self.assertDictEqual(salt.utils.args.clean_kwargs(__foo_bar="gwar"), {}) + self.assertDictEqual( + salt.utils.args.clean_kwargs(foo_bar="gwar"), {"foo_bar": "gwar"} + ) def test_get_function_argspec(self): - def dummy_func(first, second, third, fourth='fifth'): + def dummy_func(first, second, third, fourth="fifth"): pass - expected_argspec = namedtuple('ArgSpec', 'args varargs keywords defaults')( - args=['first', 'second', 'third', 'fourth'], varargs=None, keywords=None, defaults=('fifth',)) + expected_argspec = namedtuple("ArgSpec", "args varargs keywords defaults")( + args=["first", "second", "third", "fourth"], + varargs=None, + keywords=None, + defaults=("fifth",), + ) ret = salt.utils.args.get_function_argspec(dummy_func) self.assertEqual(ret, expected_argspec) def test_parse_kwarg(self): - ret = salt.utils.args.parse_kwarg('foo=bar') - self.assertEqual(ret, ('foo', 'bar')) + ret = salt.utils.args.parse_kwarg("foo=bar") + self.assertEqual(ret, ("foo", "bar")) - ret = salt.utils.args.parse_kwarg('foobar') + ret = salt.utils.args.parse_kwarg("foobar") self.assertEqual(ret, (None, None)) def test_arg_lookup(self): - def dummy_func(first, second, third, fourth='fifth'): + def dummy_func(first, second, third, fourth="fifth"): pass - expected_dict = {'args': ['first', 'second', 'third'], 'kwargs': {'fourth': 'fifth'}} + expected_dict = { + "args": ["first", "second", "third"], + "kwargs": {"fourth": "fifth"}, + } ret = salt.utils.args.arg_lookup(dummy_func) self.assertEqual(expected_dict, ret) def test_format_call(self): - with patch('salt.utils.args.arg_lookup') as arg_lookup: + with patch("salt.utils.args.arg_lookup") as arg_lookup: + def dummy_func(first=None, second=None, third=None): pass - arg_lookup.return_value = {'args': ['first', 'second', 'third'], 'kwargs': {}} + arg_lookup.return_value = { + "args": ["first", "second", "third"], + "kwargs": {}, + } get_function_argspec = DEFAULT - get_function_argspec.return_value = namedtuple('ArgSpec', 'args varargs keywords defaults')( - args=['first', 'second', 'third', 'fourth'], varargs=None, keywords=None, defaults=('fifth',)) + get_function_argspec.return_value = namedtuple( + "ArgSpec", "args varargs keywords defaults" + )( + args=["first", "second", "third", "fourth"], + varargs=None, + keywords=None, + defaults=("fifth",), + ) # Make sure we raise an error if we don't pass in the requisite number of arguments - self.assertRaises(SaltInvocationError, salt.utils.args.format_call, dummy_func, {'1': 2}) + self.assertRaises( + SaltInvocationError, salt.utils.args.format_call, dummy_func, {"1": 2} + ) # Make sure we warn on invalid kwargs - self.assertRaises(SaltInvocationError, salt.utils.args.format_call, dummy_func, {'first': 2, 'seconds': 2, 'third': 3}) + self.assertRaises( + SaltInvocationError, + salt.utils.args.format_call, + dummy_func, + {"first": 2, "seconds": 2, "third": 3}, + ) - ret = salt.utils.args.format_call(dummy_func, {'first': 2, 'second': 2, 'third': 3}, - expected_extra_kws=('first', 'second', 'third')) - self.assertDictEqual(ret, {'args': [], 'kwargs': {}}) + ret = salt.utils.args.format_call( + dummy_func, + {"first": 2, "second": 2, "third": 3}, + expected_extra_kws=("first", "second", "third"), + ) + self.assertDictEqual(ret, {"args": [], "kwargs": {}}) def test_format_call_simple_args(self): def foo(one, two=2, three=3): @@ -89,15 +119,15 @@ class ArgsTestCase(TestCase): self.assertEqual( salt.utils.args.format_call(foo, dict(one=10, two=20, three=30)), - {'args': [10], 'kwargs': dict(two=20, three=30)} + {"args": [10], "kwargs": dict(two=20, three=30)}, ) self.assertEqual( salt.utils.args.format_call(foo, dict(one=10, two=20)), - {'args': [10], 'kwargs': dict(two=20, three=3)} + {"args": [10], "kwargs": dict(two=20, three=3)}, ) self.assertEqual( salt.utils.args.format_call(foo, dict(one=2)), - {'args': [2], 'kwargs': dict(two=2, three=3)} + {"args": [2], "kwargs": dict(two=2, three=3)}, ) def test_format_call_mimic_typeerror_exceptions(self): @@ -108,26 +138,32 @@ class ArgsTestCase(TestCase): pass with self.assertRaisesRegex( - SaltInvocationError, - r'foo takes at least 1 argument \(0 given\)'): + SaltInvocationError, r"foo takes at least 1 argument \(0 given\)" + ): salt.utils.args.format_call(foo, dict(two=3)) with self.assertRaisesRegex( - TypeError, - r'foo2 takes at least 2 arguments \(1 given\)'): + TypeError, r"foo2 takes at least 2 arguments \(1 given\)" + ): salt.utils.args.format_call(foo2, dict(one=1)) def test_argspec_report(self): def _test_spec(arg1, arg2, kwarg1=None): pass - test_functions = {'test_module.test_spec': _test_spec} - ret = salt.utils.args.argspec_report(test_functions, 'test_module.test_spec') - self.assertDictEqual(ret, {'test_module.test_spec': - {'kwargs': None, - 'args': ['arg1', 'arg2', 'kwarg1'], - 'defaults': (None, ), - 'varargs': None}}) + test_functions = {"test_module.test_spec": _test_spec} + ret = salt.utils.args.argspec_report(test_functions, "test_module.test_spec") + self.assertDictEqual( + ret, + { + "test_module.test_spec": { + "kwargs": None, + "args": ["arg1", "arg2", "kwarg1"], + "defaults": (None,), + "varargs": None, + } + }, + ) def test_test_mode(self): self.assertTrue(salt.utils.args.test_mode(test=True)) @@ -135,101 +171,120 @@ class ArgsTestCase(TestCase): self.assertTrue(salt.utils.args.test_mode(tEsT=True)) def test_parse_function_no_args(self): - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc()') - self.assertEqual(fun, 'amod.afunc') + fun, args, kwargs = salt.utils.args.parse_function("amod.afunc()") + self.assertEqual(fun, "amod.afunc") self.assertEqual(args, []) self.assertEqual(kwargs, {}) def test_parse_function_args_only(self): - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2)') - self.assertEqual(fun, 'amod.afunc') - self.assertEqual(args, ['str1', 'str2']) + fun, args, kwargs = salt.utils.args.parse_function("amod.afunc(str1, str2)") + self.assertEqual(fun, "amod.afunc") + self.assertEqual(args, ["str1", "str2"]) self.assertEqual(kwargs, {}) def test_parse_function_kwargs_only(self): - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(kw1=val1, kw2=val2)') - self.assertEqual(fun, 'amod.afunc') + fun, args, kwargs = salt.utils.args.parse_function( + "amod.afunc(kw1=val1, kw2=val2)" + ) + self.assertEqual(fun, "amod.afunc") self.assertEqual(args, []) - self.assertEqual(kwargs, {'kw1': 'val1', 'kw2': 'val2'}) + self.assertEqual(kwargs, {"kw1": "val1", "kw2": "val2"}) def test_parse_function_args_kwargs(self): - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1=val1, kw2=val2)') - self.assertEqual(fun, 'amod.afunc') - self.assertEqual(args, ['str1', 'str2']) - self.assertEqual(kwargs, {'kw1': 'val1', 'kw2': 'val2'}) + fun, args, kwargs = salt.utils.args.parse_function( + "amod.afunc(str1, str2, kw1=val1, kw2=val2)" + ) + self.assertEqual(fun, "amod.afunc") + self.assertEqual(args, ["str1", "str2"]) + self.assertEqual(kwargs, {"kw1": "val1", "kw2": "val2"}) def test_parse_function_malformed_no_name(self): - fun, args, kwargs = salt.utils.args.parse_function('(str1, str2, kw1=val1, kw2=val2)') + fun, args, kwargs = salt.utils.args.parse_function( + "(str1, str2, kw1=val1, kw2=val2)" + ) self.assertIsNone(fun) self.assertIsNone(args) self.assertIsNone(kwargs) def test_parse_function_malformed_not_fun_def(self): - fun, args, kwargs = salt.utils.args.parse_function('foo bar, some=text') + fun, args, kwargs = salt.utils.args.parse_function("foo bar, some=text") self.assertIsNone(fun) self.assertIsNone(args) self.assertIsNone(kwargs) def test_parse_function_wrong_bracket_style(self): - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc[str1, str2, kw1=val1, kw2=val2]') + fun, args, kwargs = salt.utils.args.parse_function( + "amod.afunc[str1, str2, kw1=val1, kw2=val2]" + ) self.assertIsNone(fun) self.assertIsNone(args) self.assertIsNone(kwargs) def test_parse_function_brackets_unballanced(self): - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1=val1, kw2=val2') + fun, args, kwargs = salt.utils.args.parse_function( + "amod.afunc(str1, str2, kw1=val1, kw2=val2" + ) self.assertIsNone(fun) self.assertIsNone(args) self.assertIsNone(kwargs) - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1=val1, kw2=val2]') + fun, args, kwargs = salt.utils.args.parse_function( + "amod.afunc(str1, str2, kw1=val1, kw2=val2]" + ) self.assertIsNone(fun) self.assertIsNone(args) self.assertIsNone(kwargs) - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1=(val1[val2)], kw2=val2)') + fun, args, kwargs = salt.utils.args.parse_function( + "amod.afunc(str1, str2, kw1=(val1[val2)], kw2=val2)" + ) self.assertIsNone(fun) self.assertIsNone(args) self.assertIsNone(kwargs) def test_parse_function_brackets_in_quotes(self): - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1="(val1[val2)]", kw2=val2)') - self.assertEqual(fun, 'amod.afunc') - self.assertEqual(args, ['str1', 'str2']) - self.assertEqual(kwargs, {'kw1': '(val1[val2)]', 'kw2': 'val2'}) + fun, args, kwargs = salt.utils.args.parse_function( + 'amod.afunc(str1, str2, kw1="(val1[val2)]", kw2=val2)' + ) + self.assertEqual(fun, "amod.afunc") + self.assertEqual(args, ["str1", "str2"]) + self.assertEqual(kwargs, {"kw1": "(val1[val2)]", "kw2": "val2"}) def test_parse_function_quotes(self): - fun, args, kwargs = salt.utils.args.parse_function('amod.afunc("double \\" single \'", \'double " single \\\'\', kw1="equal=equal", kw2=val2)') - self.assertEqual(fun, 'amod.afunc') - self.assertEqual(args, ['double " single \'', 'double " single \'']) - self.assertEqual(kwargs, {'kw1': 'equal=equal', 'kw2': 'val2'}) + fun, args, kwargs = salt.utils.args.parse_function( + 'amod.afunc("double \\" single \'", \'double " single \\\'\', kw1="equal=equal", kw2=val2)' + ) + self.assertEqual(fun, "amod.afunc") + self.assertEqual(args, ["double \" single '", "double \" single '"]) + self.assertEqual(kwargs, {"kw1": "equal=equal", "kw2": "val2"}) def test_yamlify_arg(self): - ''' + """ Test that we properly yamlify CLI input. In several of the tests below assertIs is used instead of assertEqual. This is because we want to confirm that the return value is not a copy of the original, but the same instance as the original. - ''' + """ + def _yamlify_arg(item): - log.debug('Testing yamlify_arg with %r', item) + log.debug("Testing yamlify_arg with %r", item) return salt.utils.args.yamlify_arg(item) # Make sure non-strings are just returned back - for item in (True, False, None, 123, 45.67, ['foo'], {'foo': 'bar'}): + for item in (True, False, None, 123, 45.67, ["foo"], {"foo": "bar"}): self.assertIs(_yamlify_arg(item), item) # Make sure whitespace-only isn't loaded as None - for item in ('', '\t', ' '): + for item in ("", "\t", " "): self.assertIs(_yamlify_arg(item), item) # This value would be loaded as an int (123), the underscores would be # ignored. Test that we identify this case and return the original # value. - item = '1_2_3' + item = "1_2_3" self.assertIs(_yamlify_arg(item), item) # The '#' is treated as a comment when not part of a data structure, we # don't want that behavior - for item in ('# hash at beginning', 'Hello world! # hash elsewhere'): + for item in ("# hash at beginning", "Hello world! # hash elsewhere"): self.assertIs(_yamlify_arg(item), item) # However we _do_ want the # to be intact if it _is_ within a data @@ -240,26 +295,26 @@ class ArgsTestCase(TestCase): self.assertEqual(_yamlify_arg(item), {"foo": "###"}) # The string "None" should load _as_ None - self.assertIs(_yamlify_arg('None'), None) + self.assertIs(_yamlify_arg("None"), None) # Leading dashes, or strings containing colons, will result in lists # and dicts, and we only want to load lists and dicts when the strings # look like data structures. - for item in ('- foo', 'foo: bar'): + for item in ("- foo", "foo: bar"): self.assertIs(_yamlify_arg(item), item) # Make sure we don't load '|' as '' - item = '|' + item = "|" self.assertIs(_yamlify_arg(item), item) # Make sure we don't load '!' as something else (None in 2018.3, '' in newer) - item = '!' + item = "!" self.assertIs(_yamlify_arg(item), item) # Make sure we load ints, floats, and strings correctly - self.assertEqual(_yamlify_arg('123'), 123) - self.assertEqual(_yamlify_arg('45.67'), 45.67) - self.assertEqual(_yamlify_arg('foo'), 'foo') + self.assertEqual(_yamlify_arg("123"), 123) + self.assertEqual(_yamlify_arg("45.67"), 45.67) + self.assertEqual(_yamlify_arg("foo"), "foo") # We tested list/dict loading above, but there is separate logic when # the string contains a '#', so we need to test again here. @@ -270,9 +325,9 @@ class ArgsTestCase(TestCase): class KwargRegexTest(TestCase): def test_arguments_regex(self): argument_matches = ( - ('pip=1.1', ('pip', '1.1')), - ('pip==1.1', None), - ('pip=1.2=1', ('pip', '1.2=1')), + ("pip=1.1", ("pip", "1.1")), + ("pip==1.1", None), + ("pip=1.2=1", ("pip", "1.2=1")), ) for argument, match in argument_matches: if match is None: diff --git a/tests/unit/utils/test_asynchronous.py b/tests/unit/utils/test_asynchronous.py index a037df09194..d44b32d0eab 100644 --- a/tests/unit/utils/test_asynchronous.py +++ b/tests/unit/utils/test_asynchronous.py @@ -3,12 +3,12 @@ # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals +import salt.ext.tornado.gen + # Import 3rd-party libs import salt.ext.tornado.testing -import salt.ext.tornado.gen -from salt.ext.tornado.testing import AsyncTestCase - import salt.utils.asynchronous as asynchronous +from salt.ext.tornado.testing import AsyncTestCase class HelperA(object): @@ -37,9 +37,9 @@ class HelperB(object): class TestSyncWrapper(AsyncTestCase): @salt.ext.tornado.testing.gen_test def test_helpers(self): - ''' + """ Test that the helper classes do what we expect within a regular asynchronous env - ''' + """ ha = HelperA() ret = yield ha.sleep() self.assertTrue(ret) @@ -49,29 +49,29 @@ class TestSyncWrapper(AsyncTestCase): self.assertFalse(ret) def test_basic_wrap(self): - ''' + """ Test that we can wrap an asynchronous caller. - ''' + """ sync = asynchronous.SyncWrapper(HelperA) ret = sync.sleep() self.assertTrue(ret) def test_double(self): - ''' + """ Test when the asynchronous wrapper object itself creates a wrap of another thing This works fine since the second wrap is based on the first's IOLoop so we don't have to worry about complex start/stop mechanics - ''' + """ sync = asynchronous.SyncWrapper(HelperB) ret = sync.sleep() self.assertFalse(ret) def test_double_sameloop(self): - ''' + """ Test asynchronous wrappers initiated from the same IOLoop, to ensure that we don't wire up both to the same IOLoop (since it causes MANY problems). - ''' + """ a = asynchronous.SyncWrapper(HelperA) sync = asynchronous.SyncWrapper(HelperB, (a,)) ret = sync.sleep() diff --git a/tests/unit/utils/test_azurearm.py b/tests/unit/utils/test_azurearm.py new file mode 100644 index 00000000000..22e2c1b405c --- /dev/null +++ b/tests/unit/utils/test_azurearm.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +# import Python Libs +from __future__ import absolute_import, print_function, unicode_literals + +import logging + +# Import Salt Libs +import salt.utils.azurearm as azurearm + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf + +# Azure libs +# pylint: disable=import-error +HAS_LIBS = False +try: + import azure.mgmt.compute.models # pylint: disable=unused-import + import azure.mgmt.network.models # pylint: disable=unused-import + + HAS_LIBS = True +except ImportError: + pass + +# pylint: enable=import-error + +log = logging.getLogger(__name__) + +MOCK_CREDENTIALS = { + "client_id": "CLIENT_ID", + "secret": "SECRET", + "subscription_id": "SUBSCRIPTION_ID", + "tenant": "TENANT", +} + + +@skipIf(HAS_LIBS is False, "The azure.mgmt.network module must be installed.") +class AzureRmUtilsTestCase(TestCase): + def test_create_object_model_vnet(self): + module_name = "network" + object_name = "VirtualNetwork" + vnet = { + "address_space": {"address_prefixes": ["10.0.0.0/8"]}, + "enable_ddos_protection": False, + "enable_vm_protection": True, + "tags": {"contact_name": "Elmer Fudd Gantry"}, + } + model = azurearm.create_object_model(module_name, object_name, **vnet) + self.assertEqual(vnet, model.as_dict()) + + def test_create_object_model_nic_ref(self): + module_name = "compute" + object_name = "NetworkInterfaceReference" + ref = { + "id": "/subscriptions/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/resourceGroups/rg/providers/Microsoft.Network/networkInterfaces/nic", + "primary": False, + } + model = azurearm.create_object_model(module_name, object_name, **ref) + self.assertEqual(ref, model.as_dict()) diff --git a/tests/unit/utils/test_botomod.py b/tests/unit/utils/test_botomod.py index 7cd080e5b51..0705faf954b 100644 --- a/tests/unit/utils/test_botomod.py +++ b/tests/unit/utils/test_botomod.py @@ -2,26 +2,31 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os -# Import Salt Testing libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import skipIf, TestCase -from tests.support.mock import patch, MagicMock -from tests.support.runtests import RUNTIME_VARS +import salt.utils.boto3mod as boto3mod # Import Salt libs import salt.utils.botomod as botomod -import salt.utils.boto3mod as boto3mod -from salt.ext import six from salt.exceptions import SaltInvocationError +from salt.ext import six from salt.utils.versions import LooseVersion +# Import Salt Testing libs +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, skipIf + # Import 3rd-party libs # pylint: disable=import-error try: import boto - boto.ENDPOINTS_PATH = os.path.join(RUNTIME_VARS.TESTS_DIR, 'unit/files/endpoints.json') + + boto.ENDPOINTS_PATH = os.path.join( + RUNTIME_VARS.TESTS_DIR, "unit/files/endpoints.json" + ) import boto.exception from boto.exception import BotoServerError @@ -44,12 +49,12 @@ except ImportError: HAS_MOTO = False def mock_ec2(self): - ''' + """ if the mock_ec2 function is not available due to import failure this replaces the decorated function with stub_function. Allows unit tests to use the @mock_ec2 decorator without a "NameError: name 'mock_ec2' is not defined" error. - ''' + """ def stub_function(self): pass @@ -57,19 +62,24 @@ except ImportError: return stub_function -required_boto_version = '2.0.0' -required_boto3_version = '1.2.1' -region = 'us-east-1' -access_key = 'GKTADJGHEIQSXMKKRBJ08H' -secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs' -conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}} +required_boto_version = "2.0.0" +required_boto3_version = "1.2.1" +region = "us-east-1" +access_key = "GKTADJGHEIQSXMKKRBJ08H" +secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" +conn_parameters = { + "region": region, + "key": access_key, + "keyid": secret_key, + "profile": {}, +} -service = 'ec2' -resource_name = 'test-instance' -resource_id = 'i-a1b2c3' +service = "ec2" +resource_name = "test-instance" +resource_id = "i-a1b2c3" -error_body = ''' +error_body = """ <Response> <Errors> <Error> @@ -79,21 +89,21 @@ error_body = ''' </Errors> <RequestID>request ID</RequestID> </Response> -''' +""" -no_error_body = ''' +no_error_body = """ <Response> <Errors /> <RequestID>request ID</RequestID> </Response> -''' +""" def _has_required_boto(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ if not HAS_BOTO: return False elif LooseVersion(boto.__version__) < LooseVersion(required_boto_version): @@ -103,10 +113,10 @@ def _has_required_boto(): def _has_required_boto3(): - ''' + """ Returns True/False boolean depending on if Boto is installed and correct version. - ''' + """ try: if not HAS_BOTO3: return False @@ -121,25 +131,26 @@ def _has_required_boto3(): def _has_required_moto(): - ''' + """ Returns True/False boolean depending on if Moto is installed and correct version. - ''' + """ if not HAS_MOTO: return False else: import pkg_resources - if LooseVersion(pkg_resources.get_distribution('moto').version) < LooseVersion('0.3.7'): + if LooseVersion(pkg_resources.get_distribution("moto").version) < LooseVersion( + "0.3.7" + ): return False return True class BotoUtilsTestCaseBase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): module_globals = { - '__salt__': {'config.option': MagicMock(return_value='dummy_opt')} + "__salt__": {"config.option": MagicMock(return_value="dummy_opt")} } return {botomod: module_globals, boto3mod: module_globals} @@ -150,16 +161,26 @@ class BotoUtilsCacheIdTestCase(BotoUtilsTestCaseBase): self.assertEqual(botomod.cache_id(service, resource_name), resource_id) def test_set_and_get_with_explicit_auth_params(self): - botomod.cache_id(service, resource_name, resource_id=resource_id, **conn_parameters) - self.assertEqual(botomod.cache_id(service, resource_name, **conn_parameters), resource_id) + botomod.cache_id( + service, resource_name, resource_id=resource_id, **conn_parameters + ) + self.assertEqual( + botomod.cache_id(service, resource_name, **conn_parameters), resource_id + ) def test_set_and_get_with_different_region_returns_none(self): - botomod.cache_id(service, resource_name, resource_id=resource_id, region='us-east-1') - self.assertEqual(botomod.cache_id(service, resource_name, region='us-west-2'), None) + botomod.cache_id( + service, resource_name, resource_id=resource_id, region="us-east-1" + ) + self.assertEqual( + botomod.cache_id(service, resource_name, region="us-west-2"), None + ) def test_set_and_get_after_invalidation_returns_none(self): botomod.cache_id(service, resource_name, resource_id=resource_id) - botomod.cache_id(service, resource_name, resource_id=resource_id, invalidate=True) + botomod.cache_id( + service, resource_name, resource_id=resource_id, invalidate=True + ) self.assertEqual(botomod.cache_id(service, resource_name), None) def test_partial(self): @@ -168,13 +189,14 @@ class BotoUtilsCacheIdTestCase(BotoUtilsTestCaseBase): self.assertEqual(cache_id(resource_name), resource_id) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(HAS_MOTO is False, 'The moto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf(HAS_MOTO is False, "The moto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {0}".format(required_boto_version), +) class BotoUtilsGetConnTestCase(BotoUtilsTestCaseBase): - @mock_ec2 def test_conn_is_cached(self): conn = botomod.get_connection(service, **conn_parameters) @@ -187,15 +209,19 @@ class BotoUtilsGetConnTestCase(BotoUtilsTestCaseBase): @mock_ec2 def test_get_conn_with_no_auth_params_raises_invocation_error(self): - with patch('boto.{0}.connect_to_region'.format(service), - side_effect=boto.exception.NoAuthHandlerFound()): + with patch( + "boto.{0}.connect_to_region".format(service), + side_effect=boto.exception.NoAuthHandlerFound(), + ): with self.assertRaises(SaltInvocationError): botomod.get_connection(service) @mock_ec2 def test_get_conn_error_raises_command_execution_error(self): - with patch('boto.{0}.connect_to_region'.format(service), - side_effect=BotoServerError(400, 'Mocked error', body=error_body)): + with patch( + "boto.{0}.connect_to_region".format(service), + side_effect=BotoServerError(400, "Mocked error", body=error_body), + ): with self.assertRaises(BotoServerError): botomod.get_connection(service) @@ -206,59 +232,69 @@ class BotoUtilsGetConnTestCase(BotoUtilsTestCaseBase): self.assertTrue(conn in botomod.__context__.values()) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {0}".format(required_boto_version), +) class BotoUtilsGetErrorTestCase(BotoUtilsTestCaseBase): def test_error_message(self): - e = BotoServerError('400', 'Mocked error', body=error_body) + e = BotoServerError("400", "Mocked error", body=error_body) r = botomod.get_error(e) - expected = {'aws': {'code': 'Error code text', - 'message': 'Error message', - 'reason': 'Mocked error', - 'status': '400'}, - 'message': 'Mocked error: Error message'} + expected = { + "aws": { + "code": "Error code text", + "message": "Error message", + "reason": "Mocked error", + "status": "400", + }, + "message": "Mocked error: Error message", + } self.assertEqual(r, expected) def test_exception_message_with_no_body(self): - e = BotoServerError('400', 'Mocked error') + e = BotoServerError("400", "Mocked error") r = botomod.get_error(e) - expected = {'aws': {'reason': 'Mocked error', - 'status': '400'}, - 'message': 'Mocked error'} + expected = { + "aws": {"reason": "Mocked error", "status": "400"}, + "message": "Mocked error", + } self.assertEqual(r, expected) def test_exception_message_with_no_error_in_body(self): - e = BotoServerError('400', 'Mocked error', body=no_error_body) + e = BotoServerError("400", "Mocked error", body=no_error_body) r = botomod.get_error(e) - expected = {'aws': {'reason': 'Mocked error', 'status': '400'}, - 'message': 'Mocked error'} + expected = { + "aws": {"reason": "Mocked error", "status": "400"}, + "message": "Mocked error", + } self.assertEqual(r, expected) -@skipIf(HAS_BOTO is False, 'The boto module must be installed.') -@skipIf(_has_required_boto() is False, 'The boto module must be greater than' - ' or equal to version {0}' - .format(required_boto_version)) -@skipIf(HAS_BOTO3 is False, 'The boto3 module must be installed.') -@skipIf(_has_required_boto3() is False, 'The boto3 module must be greater than' - ' or equal to version {0}' - .format(required_boto3_version)) +@skipIf(HAS_BOTO is False, "The boto module must be installed.") +@skipIf( + _has_required_boto() is False, + "The boto module must be greater than" + " or equal to version {0}".format(required_boto_version), +) +@skipIf(HAS_BOTO3 is False, "The boto3 module must be installed.") +@skipIf( + _has_required_boto3() is False, + "The boto3 module must be greater than" + " or equal to version {0}".format(required_boto3_version), +) class BotoBoto3CacheContextCollisionTest(BotoUtilsTestCaseBase): - def test_context_conflict_between_boto_and_boto3_utils(self): - botomod.assign_funcs(__name__, 'ec2') - boto3mod.assign_funcs(__name__, 'ec2', get_conn_funcname="_get_conn3") + botomod.assign_funcs(__name__, "ec2") + boto3mod.assign_funcs(__name__, "ec2", get_conn_funcname="_get_conn3") - boto_ec2_conn = botomod.get_connection('ec2', - region=region, - key=secret_key, - keyid=access_key) - boto3_ec2_conn = boto3mod.get_connection('ec2', - region=region, - key=secret_key, - keyid=access_key) + boto_ec2_conn = botomod.get_connection( + "ec2", region=region, key=secret_key, keyid=access_key + ) + boto3_ec2_conn = boto3mod.get_connection( + "ec2", region=region, key=secret_key, keyid=access_key + ) # These should *not* be the same object! self.assertNotEqual(id(boto_ec2_conn), id(boto3_ec2_conn)) diff --git a/tests/unit/utils/test_cache.py b/tests/unit/utils/test_cache.py index ea9658345d6..8b940dd7cc7 100644 --- a/tests/unit/utils/test_cache.py +++ b/tests/unit/utils/test_cache.py @@ -1,138 +1,146 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.utils.cache_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Test the salt cache objects -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import time -import tempfile -import shutil -# Import Salt Testing libs -from tests.support.unit import TestCase +import os +import shutil +import tempfile +import time # Import salt libs import salt.config import salt.loader import salt.payload +import salt.utils.cache as cache import salt.utils.data import salt.utils.files -import salt.utils.cache as cache + +# Import Salt Testing libs +from tests.support.unit import TestCase class CacheDictTestCase(TestCase): - def test_sanity(self): - ''' + """ Make sure you can instantiate etc. - ''' + """ cd = cache.CacheDict(5) self.assertIsInstance(cd, cache.CacheDict) # do some tests to make sure it looks like a dict - self.assertNotIn('foo', cd) - cd['foo'] = 'bar' - self.assertEqual(cd['foo'], 'bar') - del cd['foo'] - self.assertNotIn('foo', cd) + self.assertNotIn("foo", cd) + cd["foo"] = "bar" + self.assertEqual(cd["foo"], "bar") + del cd["foo"] + self.assertNotIn("foo", cd) def test_ttl(self): cd = cache.CacheDict(0.1) - cd['foo'] = 'bar' - self.assertIn('foo', cd) - self.assertEqual(cd['foo'], 'bar') + cd["foo"] = "bar" + self.assertIn("foo", cd) + self.assertEqual(cd["foo"], "bar") time.sleep(0.2) - self.assertNotIn('foo', cd) + self.assertNotIn("foo", cd) # make sure that a get would get a regular old key error - self.assertRaises(KeyError, cd.__getitem__, 'foo') + self.assertRaises(KeyError, cd.__getitem__, "foo") class CacheContextTestCase(TestCase): - def setUp(self): - context_dir = os.path.join(tempfile.gettempdir(), 'context') + context_dir = os.path.join(tempfile.gettempdir(), "context") if os.path.exists(context_dir): - shutil.rmtree(os.path.join(tempfile.gettempdir(), 'context')) + shutil.rmtree(os.path.join(tempfile.gettempdir(), "context")) def test_smoke_context(self): - ''' + """ Smoke test the context cache - ''' - if os.path.exists(os.path.join(tempfile.gettempdir(), 'context')): - self.skipTest('Context dir already exists') + """ + if os.path.exists(os.path.join(tempfile.gettempdir(), "context")): + self.skipTest("Context dir already exists") else: opts = salt.config.DEFAULT_MINION_OPTS.copy() - opts['cachedir'] = tempfile.gettempdir() - context_cache = cache.ContextCache(opts, 'cache_test') + opts["cachedir"] = tempfile.gettempdir() + context_cache = cache.ContextCache(opts, "cache_test") - context_cache.cache_context({'a': 'b'}) + context_cache.cache_context({"a": "b"}) ret = context_cache.get_cache_context() - self.assertDictEqual({'a': 'b'}, ret) + self.assertDictEqual({"a": "b"}, ret) def test_context_wrapper(self): - ''' + """ Test to ensure that a module which decorates itself with a context cache can store and retrieve its contextual data - ''' + """ opts = salt.config.DEFAULT_MINION_OPTS.copy() - opts['cachedir'] = tempfile.gettempdir() + opts["cachedir"] = tempfile.gettempdir() ll_ = salt.loader.LazyLoader( - [os.path.join(os.path.dirname(os.path.realpath(__file__)), 'cache_mods')], - tag='rawmodule', - virtual_enable=False, - opts=opts) + [os.path.join(os.path.dirname(os.path.realpath(__file__)), "cache_mods")], + tag="rawmodule", + virtual_enable=False, + opts=opts, + ) - cache_test_func = ll_['cache_mod.test_context_module'] + cache_test_func = ll_["cache_mod.test_context_module"] - self.assertEqual(cache_test_func()['called'], 0) - self.assertEqual(cache_test_func()['called'], 1) + self.assertEqual(cache_test_func()["called"], 0) + self.assertEqual(cache_test_func()["called"], 1) -__context__ = {'a': 'b'} -__opts__ = {'cachedir': '/tmp'} +__context__ = {"a": "b"} +__opts__ = {"cachedir": "/tmp"} class ContextCacheTest(TestCase): - ''' + """ Test case for salt.utils.cache.ContextCache - ''' + """ + def setUp(self): - ''' + """ Clear the cache before every test - ''' - context_dir = os.path.join(__opts__['cachedir'], 'context') + """ + context_dir = os.path.join(__opts__["cachedir"], "context") if os.path.isdir(context_dir): shutil.rmtree(context_dir) def test_set_cache(self): - ''' + """ Tests to ensure the cache is written correctly - ''' + """ + @cache.context_cache def _test_set_cache(): - ''' + """ This will inherit globals from the test module itself. Normally these are injected by the salt loader [salt.loader] - ''' + """ _test_set_cache() - target_cache_file = os.path.join(__opts__['cachedir'], 'context', '{0}.p'.format(__name__)) - self.assertTrue(os.path.isfile(target_cache_file), 'Context cache did not write cache file') + target_cache_file = os.path.join( + __opts__["cachedir"], "context", "{0}.p".format(__name__) + ) + self.assertTrue( + os.path.isfile(target_cache_file), "Context cache did not write cache file" + ) # Test manual de-serialize - with salt.utils.files.fopen(target_cache_file, 'rb') as fp_: - target_cache_data = salt.utils.data.decode(salt.payload.Serial(__opts__).load(fp_)) + with salt.utils.files.fopen(target_cache_file, "rb") as fp_: + target_cache_data = salt.utils.data.decode( + salt.payload.Serial(__opts__).load(fp_) + ) self.assertDictEqual(__context__, target_cache_data) # Test cache de-serialize @@ -141,13 +149,14 @@ class ContextCacheTest(TestCase): self.assertDictEqual(retrieved_cache, __context__) def test_refill_cache(self): - ''' + """ Tests to ensure that the context cache can rehydrate a wrapped function - ''' + """ # First populate the cache @cache.context_cache def _test_set_cache(): pass + _test_set_cache() # Then try to rehydate a func @@ -157,41 +166,40 @@ class ContextCacheTest(TestCase): global __context__ __context__ = {} - _test_refill_cache({'a': 'b'}) # Compare to the context before it was emptied + _test_refill_cache({"a": "b"}) # Compare to the context before it was emptied class CacheDiskTestCase(TestCase): - def test_everything(self): - ''' + """ Make sure you can instantiate, add, update, remove, expire - ''' + """ try: tmpdir = tempfile.mkdtemp() - path = os.path.join(tmpdir, 'CacheDisk_test') + path = os.path.join(tmpdir, "CacheDisk_test") # test instantiation cd = cache.CacheDisk(0.1, path) self.assertIsInstance(cd, cache.CacheDisk) # test to make sure it looks like a dict - self.assertNotIn('foo', cd) - cd['foo'] = 'bar' - self.assertIn('foo', cd) - self.assertEqual(cd['foo'], 'bar') - del cd['foo'] - self.assertNotIn('foo', cd) + self.assertNotIn("foo", cd) + cd["foo"] = "bar" + self.assertIn("foo", cd) + self.assertEqual(cd["foo"], "bar") + del cd["foo"] + self.assertNotIn("foo", cd) # test persistence - cd['foo'] = 'bar' + cd["foo"] = "bar" cd2 = cache.CacheDisk(0.1, path) - self.assertIn('foo', cd2) - self.assertEqual(cd2['foo'], 'bar') + self.assertIn("foo", cd2) + self.assertEqual(cd2["foo"], "bar") # test ttl time.sleep(0.2) - self.assertNotIn('foo', cd) - self.assertNotIn('foo', cd2) + self.assertNotIn("foo", cd) + self.assertNotIn("foo", cd2) finally: shutil.rmtree(tmpdir, ignore_errors=True) diff --git a/tests/unit/utils/test_cloud.py b/tests/unit/utils/test_cloud.py index 4d61f24a372..2b7bd6f50f8 100644 --- a/tests/unit/utils/test_cloud.py +++ b/tests/unit/utils/test_cloud.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,30 +7,30 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Test the salt-cloud utilities module -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import tempfile -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import TestCase, skipIf, SkipTest - # Import salt libs import salt.utils.cloud as cloud import salt.utils.platform from salt.ext import six +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import SkipTest, TestCase, skipIf + class CloudUtilsTestCase(TestCase): - @classmethod def setUpClass(cls): old_cwd = os.getcwd() - cls.gpg_keydir = gpg_keydir = os.path.join(RUNTIME_VARS.TMP, 'gpg-keydir') + cls.gpg_keydir = gpg_keydir = os.path.join(RUNTIME_VARS.TMP, "gpg-keydir") try: # The keyring library uses `getcwd()`, let's make sure we in a good directory # before importing keyring @@ -43,23 +43,34 @@ class CloudUtilsTestCase(TestCase): import keyring.backend class CustomKeyring(keyring.backend.KeyringBackend): - ''' + """ A test keyring which always outputs same password - ''' + """ + def __init__(self): self.__storage = {} def supported(self): return 0 - def set_password(self, servicename, username, password): # pylint: disable=arguments-differ - self.__storage.setdefault(servicename, {}).update({username: password}) + def set_password( + self, servicename, username, password + ): # pylint: disable=arguments-differ + self.__storage.setdefault(servicename, {}).update( + {username: password} + ) return 0 - def get_password(self, servicename, username): # pylint: disable=arguments-differ - return self.__storage.setdefault(servicename, {}).get(username, None) + def get_password( + self, servicename, username + ): # pylint: disable=arguments-differ + return self.__storage.setdefault(servicename, {}).get( + username, None + ) - def delete_password(self, servicename, username): # pylint: disable=arguments-differ + def delete_password( + self, servicename, username + ): # pylint: disable=arguments-differ self.__storage.setdefault(servicename, {}).pop(username, None) return 0 @@ -77,13 +88,13 @@ class CloudUtilsTestCase(TestCase): del cls.gpg_keydir def test_ssh_password_regex(self): - '''Test matching ssh password patterns''' - for pattern in ('Password for root@127.0.0.1:', - 'root@127.0.0.1 Password:', - ' Password:'): - self.assertNotEqual( - cloud.SSH_PASSWORD_PROMP_RE.match(pattern), None - ) + """Test matching ssh password patterns""" + for pattern in ( + "Password for root@127.0.0.1:", + "root@127.0.0.1 Password:", + " Password:", + ): + self.assertNotEqual(cloud.SSH_PASSWORD_PROMP_RE.match(pattern), None) self.assertNotEqual( cloud.SSH_PASSWORD_PROMP_RE.match(pattern.lower()), None ) @@ -95,56 +106,59 @@ class CloudUtilsTestCase(TestCase): ) def test__save_password_in_keyring(self): - ''' + """ Test storing password in the keyring - ''' + """ # Late import import keyring + cloud._save_password_in_keyring( - 'salt.cloud.provider.test_case_provider', - 'fake_username', - 'fake_password_c8231' + "salt.cloud.provider.test_case_provider", + "fake_username", + "fake_password_c8231", ) stored_pw = keyring.get_password( - 'salt.cloud.provider.test_case_provider', - 'fake_username', + "salt.cloud.provider.test_case_provider", "fake_username", ) keyring.delete_password( - 'salt.cloud.provider.test_case_provider', - 'fake_username', + "salt.cloud.provider.test_case_provider", "fake_username", ) - self.assertEqual(stored_pw, 'fake_password_c8231') + self.assertEqual(stored_pw, "fake_password_c8231") def test_retrieve_password_from_keyring(self): # Late import import keyring + keyring.set_password( - 'salt.cloud.provider.test_case_provider', - 'fake_username', - 'fake_password_c8231' + "salt.cloud.provider.test_case_provider", + "fake_username", + "fake_password_c8231", ) pw_in_keyring = cloud.retrieve_password_from_keyring( - 'salt.cloud.provider.test_case_provider', - 'fake_username') - self.assertEqual(pw_in_keyring, 'fake_password_c8231') + "salt.cloud.provider.test_case_provider", "fake_username" + ) + self.assertEqual(pw_in_keyring, "fake_password_c8231") def test_sftp_file_with_content_under_python3(self): with self.assertRaises(Exception) as context: cloud.sftp_file("/tmp/test", "ТЕСТ test content") # we successful pass the place with os.write(tmpfd, ... - self.assertNotEqual("a bytes-like object is required, not 'str'", six.text_type(context.exception)) + self.assertNotEqual( + "a bytes-like object is required, not 'str'", + six.text_type(context.exception), + ) - @skipIf(salt.utils.platform.is_windows(), 'Not applicable to Windows') + @skipIf(salt.utils.platform.is_windows(), "Not applicable to Windows") def test_check_key_path_and_mode(self): with tempfile.NamedTemporaryFile() as f: key_file = f.name os.chmod(key_file, 0o644) - self.assertFalse(cloud.check_key_path_and_mode('foo', key_file)) + self.assertFalse(cloud.check_key_path_and_mode("foo", key_file)) os.chmod(key_file, 0o600) - self.assertTrue(cloud.check_key_path_and_mode('foo', key_file)) + self.assertTrue(cloud.check_key_path_and_mode("foo", key_file)) os.chmod(key_file, 0o400) - self.assertTrue(cloud.check_key_path_and_mode('foo', key_file)) + self.assertTrue(cloud.check_key_path_and_mode("foo", key_file)) # tmp file removed - self.assertFalse(cloud.check_key_path_and_mode('foo', key_file)) + self.assertFalse(cloud.check_key_path_and_mode("foo", key_file)) diff --git a/tests/unit/utils/test_color.py b/tests/unit/utils/test_color.py index cd5357f22d3..5f0e2f17c97 100644 --- a/tests/unit/utils/test_color.py +++ b/tests/unit/utils/test_color.py @@ -1,28 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for salt.utils.color.py -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase - # Import Salt libs import salt.utils.color from salt.ext import six +# Import Salt Testing libs +from tests.support.unit import TestCase + class ColorUtilsTestCase(TestCase): - def test_get_colors(self): ret = salt.utils.color.get_colors() - self.assertEqual('\x1b[0;37m', six.text_type(ret['LIGHT_GRAY'])) + self.assertEqual("\x1b[0;37m", six.text_type(ret["LIGHT_GRAY"])) ret = salt.utils.color.get_colors(use=False) - self.assertDictContainsSubset({'LIGHT_GRAY': ''}, ret) + self.assertDictContainsSubset({"LIGHT_GRAY": ""}, ret) - ret = salt.utils.color.get_colors(use='LIGHT_GRAY') + ret = salt.utils.color.get_colors(use="LIGHT_GRAY") # LIGHT_YELLOW now == LIGHT_GRAY - self.assertEqual(six.text_type(ret['LIGHT_YELLOW']), six.text_type(ret['LIGHT_GRAY'])) + self.assertEqual( + six.text_type(ret["LIGHT_YELLOW"]), six.text_type(ret["LIGHT_GRAY"]) + ) diff --git a/tests/unit/utils/test_configcomparer.py b/tests/unit/utils/test_configcomparer.py index 2ccc34d0e3a..ee1205f4ab0 100644 --- a/tests/unit/utils/test_configcomparer.py +++ b/tests/unit/utils/test_configcomparer.py @@ -2,32 +2,24 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import copy -# Import Salt Testing libs -from tests.support.unit import TestCase +import copy # Import Salt libs import salt.utils.configcomparer as configcomparer +# Import Salt Testing libs +from tests.support.unit import TestCase + class UtilConfigcomparerTestCase(TestCase): base_config = { - 'attr1': 'value1', - 'attr2': [ - 'item1', - 'item2', - 'item3', - ], - 'attr3': [], - 'attr4': {}, - 'attr5': { - 'subattr1': 'value1', - 'subattr2': [ - 'item1', - ], - }, + "attr1": "value1", + "attr2": ["item1", "item2", "item3"], + "attr3": [], + "attr4": {}, + "attr5": {"subattr1": "value1", "subattr2": ["item1"]}, } def test_compare_and_update_config(self): @@ -36,9 +28,7 @@ class UtilConfigcomparerTestCase(TestCase): to_update = copy.deepcopy(self.base_config) changes = {} configcomparer.compare_and_update_config( - {}, - to_update, - changes, + {}, to_update, changes, ) self.assertEqual({}, changes) self.assertEqual(self.base_config, to_update) @@ -47,59 +37,30 @@ class UtilConfigcomparerTestCase(TestCase): to_update = copy.deepcopy(self.base_config) changes = {} configcomparer.compare_and_update_config( - { - 'attrx': 'value1', - }, - to_update, - changes, + {"attrx": "value1"}, to_update, changes, ) self.assertEqual( - { - 'attrx': { - 'new': 'value1', - 'old': None, - }, - }, - changes, + {"attrx": {"new": "value1", "old": None}}, changes, ) - self.assertEqual('value1', to_update['attrx']) - self.assertEqual('value1', to_update['attr1']) -# simple value + self.assertEqual("value1", to_update["attrx"]) + self.assertEqual("value1", to_update["attr1"]) + # simple value to_update = copy.deepcopy(self.base_config) changes = {} configcomparer.compare_and_update_config( - { - 'attr1': 'value2', - }, - to_update, - changes, + {"attr1": "value2"}, to_update, changes, ) self.assertEqual( - { - 'attr1': { - 'new': 'value2', - 'old': 'value1', - }, - }, - changes, + {"attr1": {"new": "value2", "old": "value1"}}, changes, ) - self.assertEqual('value2', to_update['attr1']) + self.assertEqual("value2", to_update["attr1"]) self.assertEqual( { - 'attr1': 'value2', - 'attr2': [ - 'item1', - 'item2', - 'item3', - ], - 'attr3': [], - 'attr4': {}, - 'attr5': { - 'subattr1': 'value1', - 'subattr2': [ - 'item1', - ], - }, + "attr1": "value2", + "attr2": ["item1", "item2", "item3"], + "attr3": [], + "attr4": {}, + "attr5": {"subattr1": "value1", "subattr2": ["item1"]}, }, to_update, ) @@ -108,11 +69,7 @@ class UtilConfigcomparerTestCase(TestCase): to_update = copy.deepcopy(self.base_config) changes = {} configcomparer.compare_and_update_config( - { - 'attr3': [], - }, - to_update, - changes, + {"attr3": []}, to_update, changes, ) self.assertEqual({}, changes) self.assertEqual(self.base_config, to_update) @@ -121,43 +78,18 @@ class UtilConfigcomparerTestCase(TestCase): to_update = copy.deepcopy(self.base_config) changes = {} configcomparer.compare_and_update_config( - { - 'attr2': [ - 'item1', - 'item2', - 'item3', - 'item4', - ], - }, - to_update, - changes, + {"attr2": ["item1", "item2", "item3", "item4"]}, to_update, changes, + ) + self.assertEqual( + {"attr2[3]": {"new": "item4", "old": None}}, changes, ) self.assertEqual( { - 'attr2[3]': { - 'new': 'item4', - 'old': None, - }, - }, - changes, - ) - self.assertEqual( - { - 'attr1': 'value1', - 'attr2': [ - 'item1', - 'item2', - 'item3', - 'item4', - ], - 'attr3': [], - 'attr4': {}, - 'attr5': { - 'subattr1': 'value1', - 'subattr2': [ - 'item1', - ], - }, + "attr1": "value1", + "attr2": ["item1", "item2", "item3", "item4"], + "attr3": [], + "attr4": {}, + "attr5": {"subattr1": "value1", "subattr2": ["item1"]}, }, to_update, ) @@ -166,43 +98,22 @@ class UtilConfigcomparerTestCase(TestCase): to_update = copy.deepcopy(self.base_config) changes = {} configcomparer.compare_and_update_config( - { - 'attr2': [ - 'itemx', - 'item2', - ], - }, - to_update, - changes, + {"attr2": ["itemx", "item2"]}, to_update, changes, ) self.assertEqual( { - 'attr2[0]': { - 'new': 'itemx', - 'old': 'item1', - }, - 'attr2[2]': { - 'new': None, - 'old': 'item3', - }, + "attr2[0]": {"new": "itemx", "old": "item1"}, + "attr2[2]": {"new": None, "old": "item3"}, }, changes, ) self.assertEqual( { - 'attr1': 'value1', - 'attr2': [ - 'itemx', - 'item2', - ], - 'attr3': [], - 'attr4': {}, - 'attr5': { - 'subattr1': 'value1', - 'subattr2': [ - 'item1', - ], - }, + "attr1": "value1", + "attr2": ["itemx", "item2"], + "attr3": [], + "attr4": {}, + "attr5": {"subattr1": "value1", "subattr2": ["item1"]}, }, to_update, ) @@ -211,11 +122,7 @@ class UtilConfigcomparerTestCase(TestCase): to_update = copy.deepcopy(self.base_config) changes = {} configcomparer.compare_and_update_config( - { - 'attr4': {} - }, - to_update, - changes, + {"attr4": {}}, to_update, changes, ) self.assertEqual({}, changes) self.assertEqual(self.base_config, to_update) @@ -224,39 +131,21 @@ class UtilConfigcomparerTestCase(TestCase): to_update = copy.deepcopy(self.base_config) changes = {} configcomparer.compare_and_update_config( - { - 'attr5': { - 'subattr3': 'value1', - }, - }, - to_update, - changes, + {"attr5": {"subattr3": "value1"}}, to_update, changes, + ) + self.assertEqual( + {"attr5.subattr3": {"new": "value1", "old": None}}, changes, ) self.assertEqual( { - 'attr5.subattr3': { - 'new': 'value1', - 'old': None, - }, - }, - changes, - ) - self.assertEqual( - { - 'attr1': 'value1', - 'attr2': [ - 'item1', - 'item2', - 'item3', - ], - 'attr3': [], - 'attr4': {}, - 'attr5': { - 'subattr1': 'value1', - 'subattr2': [ - 'item1', - ], - 'subattr3': 'value1', + "attr1": "value1", + "attr2": ["item1", "item2", "item3"], + "attr3": [], + "attr4": {}, + "attr5": { + "subattr1": "value1", + "subattr2": ["item1"], + "subattr3": "value1", }, }, to_update, @@ -266,48 +155,24 @@ class UtilConfigcomparerTestCase(TestCase): to_update = copy.deepcopy(self.base_config) changes = {} configcomparer.compare_and_update_config( - { - 'attr5': { - 'subattr1': 'value2', - 'subattr2': [ - 'item1', - 'item2', - ], - }, - }, + {"attr5": {"subattr1": "value2", "subattr2": ["item1", "item2"]}}, to_update, changes, ) self.assertEqual( { - 'attr5.subattr1': { - 'new': 'value2', - 'old': 'value1', - }, - 'attr5.subattr2[1]': { - 'new': 'item2', - 'old': None, - }, + "attr5.subattr1": {"new": "value2", "old": "value1"}, + "attr5.subattr2[1]": {"new": "item2", "old": None}, }, changes, ) self.assertEqual( { - 'attr1': 'value1', - 'attr2': [ - 'item1', - 'item2', - 'item3', - ], - 'attr3': [], - 'attr4': {}, - 'attr5': { - 'subattr1': 'value2', - 'subattr2': [ - 'item1', - 'item2', - ], - }, + "attr1": "value1", + "attr2": ["item1", "item2", "item3"], + "attr3": [], + "attr4": {}, + "attr5": {"subattr1": "value2", "subattr2": ["item1", "item2"]}, }, to_update, ) diff --git a/tests/unit/utils/test_configparser.py b/tests/unit/utils/test_configparser.py index 52864d9c253..cffc2f7d205 100644 --- a/tests/unit/utils/test_configparser.py +++ b/tests/unit/utils/test_configparser.py @@ -1,33 +1,36 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.utils.test_configparser ================================== Test the funcs in the custom parsers in salt.utils.configparser -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import copy import errno import logging import os -log = logging.getLogger(__name__) +import salt.utils.configparser + +# Import salt libs +import salt.utils.files +import salt.utils.platform +import salt.utils.stringutils +from salt.ext import six # Import Salt Testing Libs from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase -# Import salt libs -import salt.utils.files -import salt.utils.stringutils -import salt.utils.configparser -import salt.utils.platform -from salt.ext import six +log = logging.getLogger(__name__) + # The user.name param here is intentionally indented with spaces instead of a # tab to test that we properly load a file with mixed indentation. -ORIG_CONFIG = '''[user] +ORIG_CONFIG = """[user] name = Артём Анисимов \temail = foo@bar.com [remote "origin"] @@ -48,16 +51,19 @@ ORIG_CONFIG = '''[user] \tgraph = log --all --decorate --oneline --graph \thist = log --pretty=format:\\"%h %ad | %s%d [%an]\\" --graph --date=short [http] -\tsslverify = false'''.split('\n') +\tsslverify = false""".split( + "\n" +) class TestGitConfigParser(TestCase): - ''' + """ Tests for salt.utils.configparser.GitConfigParser - ''' + """ + maxDiff = None - orig_config = os.path.join(RUNTIME_VARS.TMP, 'test_gitconfig.orig') - new_config = os.path.join(RUNTIME_VARS.TMP, 'test_gitconfig.new') + orig_config = os.path.join(RUNTIME_VARS.TMP, "test_gitconfig.orig") + new_config = os.path.join(RUNTIME_VARS.TMP, "test_gitconfig.new") remote = 'remote "origin"' def tearDown(self): @@ -70,14 +76,10 @@ class TestGitConfigParser(TestCase): def setUp(self): if not os.path.exists(self.orig_config): - with salt.utils.files.fopen(self.orig_config, 'wb') as fp_: - fp_.write( - salt.utils.stringutils.to_bytes( - os.linesep.join(ORIG_CONFIG) - ) - ) + with salt.utils.files.fopen(self.orig_config, "wb") as fp_: + fp_.write(salt.utils.stringutils.to_bytes(os.linesep.join(ORIG_CONFIG))) self.conf = salt.utils.configparser.GitConfigParser() - with salt.utils.files.fopen(self.orig_config, 'rb') as fp: + with salt.utils.files.fopen(self.orig_config, "rb") as fp: self.conf._read(fp, self.orig_config) @classmethod @@ -90,185 +92,176 @@ class TestGitConfigParser(TestCase): @staticmethod def fix_indent(lines): - ''' + """ Fixes the space-indented 'user' line, because when we write the config object to a file space indentation will be replaced by tab indentation. - ''' + """ ret = copy.copy(lines) for i, _ in enumerate(ret): if ret[i].startswith(salt.utils.configparser.GitConfigParser.SPACEINDENT): - ret[i] = ret[i].replace(salt.utils.configparser.GitConfigParser.SPACEINDENT, '\t') + ret[i] = ret[i].replace( + salt.utils.configparser.GitConfigParser.SPACEINDENT, "\t" + ) return ret @staticmethod def get_lines(path): - with salt.utils.files.fopen(path, 'rb') as fp_: + with salt.utils.files.fopen(path, "rb") as fp_: return salt.utils.stringutils.to_unicode(fp_.read()).splitlines() def _test_write(self, mode): - kwargs = {'mode': mode} - if six.PY3 and salt.utils.platform.is_windows() and 'b' not in mode: - kwargs['encoding'] = 'utf-8' + kwargs = {"mode": mode} + if six.PY3 and salt.utils.platform.is_windows() and "b" not in mode: + kwargs["encoding"] = "utf-8" with salt.utils.files.fopen(self.new_config, **kwargs) as fp_: self.conf.write(fp_) - self.assertEqual( - self.get_lines(self.new_config), - self.fix_indent(ORIG_CONFIG) - ) + self.assertEqual(self.get_lines(self.new_config), self.fix_indent(ORIG_CONFIG)) def test_get(self): - ''' + """ Test getting an option's value - ''' + """ # Numeric values should be loaded as strings - self.assertEqual(self.conf.get('color "diff"', 'old'), '196') + self.assertEqual(self.conf.get('color "diff"', "old"), "196") # Complex strings should be loaded with their literal quotes and # slashes intact self.assertEqual( - self.conf.get('alias', 'modified'), - """! git status --porcelain | awk 'match($1, "M"){print $2}'""" + self.conf.get("alias", "modified"), + """! git status --porcelain | awk 'match($1, "M"){print $2}'""", ) # future lint: disable=non-unicode-string self.assertEqual( - self.conf.get('alias', 'hist'), + self.conf.get("alias", "hist"), salt.utils.stringutils.to_unicode( r"""log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short""" - ) + ), ) # future lint: enable=non-unicode-string def test_read_space_indent(self): - ''' + """ Test that user.name was successfully loaded despite being indented using spaces instead of a tab. Additionally, this tests that the value was loaded as a unicode type on PY2. - ''' - self.assertEqual(self.conf.get('user', 'name'), u'Артём Анисимов') + """ + self.assertEqual(self.conf.get("user", "name"), "Артём Анисимов") def test_set_new_option(self): - ''' + """ Test setting a new option in an existing section - ''' - self.conf.set('http', 'useragent', 'myawesomeagent') - self.assertEqual(self.conf.get('http', 'useragent'), 'myawesomeagent') + """ + self.conf.set("http", "useragent", "myawesomeagent") + self.assertEqual(self.conf.get("http", "useragent"), "myawesomeagent") def test_add_section(self): - ''' + """ Test adding a section and adding an item to that section - ''' - self.conf.add_section('foo') - self.conf.set('foo', 'bar', 'baz') - self.assertEqual(self.conf.get('foo', 'bar'), 'baz') + """ + self.conf.add_section("foo") + self.conf.set("foo", "bar", "baz") + self.assertEqual(self.conf.get("foo", "bar"), "baz") def test_replace_option(self): - ''' + """ Test replacing an existing option - ''' + """ # We're also testing the normalization of key names, here. Setting # "sslVerify" should actually set an "sslverify" option. - self.conf.set('http', 'sslVerify', 'true') - self.assertEqual(self.conf.get('http', 'sslverify'), 'true') + self.conf.set("http", "sslVerify", "true") + self.assertEqual(self.conf.get("http", "sslverify"), "true") def test_set_multivar(self): - ''' + """ Test setting a multivar and then writing the resulting file - ''' - orig_refspec = '+refs/heads/*:refs/remotes/origin/*' - new_refspec = '+refs/tags/*:refs/tags/*' + """ + orig_refspec = "+refs/heads/*:refs/remotes/origin/*" + new_refspec = "+refs/tags/*:refs/tags/*" # Make sure that the original value is a string - self.assertEqual( - self.conf.get(self.remote, 'fetch'), - orig_refspec - ) + self.assertEqual(self.conf.get(self.remote, "fetch"), orig_refspec) # Add another refspec - self.conf.set_multivar(self.remote, 'fetch', new_refspec) + self.conf.set_multivar(self.remote, "fetch", new_refspec) # The value should now be a list self.assertEqual( - self.conf.get(self.remote, 'fetch'), - [orig_refspec, new_refspec] + self.conf.get(self.remote, "fetch"), [orig_refspec, new_refspec] ) # Write the config object to a file - with salt.utils.files.fopen(self.new_config, 'wb') as fp_: + with salt.utils.files.fopen(self.new_config, "wb") as fp_: self.conf.write(fp_) # Confirm that the new file was written correctly expected = self.fix_indent(ORIG_CONFIG) - expected.insert(6, '\tfetch = %s' % new_refspec) # pylint: disable=string-substitution-usage-error + # pylint: disable=string-substitution-usage-error + expected.insert(6, "\tfetch = %s" % new_refspec) + # pylint: enable=string-substitution-usage-error self.assertEqual(self.get_lines(self.new_config), expected) def test_remove_option(self): - ''' + """ test removing an option, including all items from a multivar - ''' - for item in ('fetch', 'pushurl'): + """ + for item in ("fetch", "pushurl"): self.conf.remove_option(self.remote, item) # To confirm that the option is now gone, a get should raise an # NoOptionError exception. self.assertRaises( - salt.utils.configparser.NoOptionError, - self.conf.get, - self.remote, - item) + salt.utils.configparser.NoOptionError, self.conf.get, self.remote, item + ) def test_remove_option_regexp(self): - ''' + """ test removing an option, including all items from a multivar - ''' - orig_refspec = '+refs/heads/*:refs/remotes/origin/*' - new_refspec_1 = '+refs/tags/*:refs/tags/*' - new_refspec_2 = '+refs/foo/*:refs/foo/*' + """ + orig_refspec = "+refs/heads/*:refs/remotes/origin/*" + new_refspec_1 = "+refs/tags/*:refs/tags/*" + new_refspec_2 = "+refs/foo/*:refs/foo/*" # First, add both refspecs - self.conf.set_multivar(self.remote, 'fetch', new_refspec_1) - self.conf.set_multivar(self.remote, 'fetch', new_refspec_2) + self.conf.set_multivar(self.remote, "fetch", new_refspec_1) + self.conf.set_multivar(self.remote, "fetch", new_refspec_2) # Make sure that all three values are there self.assertEqual( - self.conf.get(self.remote, 'fetch'), - [orig_refspec, new_refspec_1, new_refspec_2] + self.conf.get(self.remote, "fetch"), + [orig_refspec, new_refspec_1, new_refspec_2], ) # If the regex doesn't match, no items should be removed self.assertFalse( self.conf.remove_option_regexp( self.remote, - 'fetch', - salt.utils.stringutils.to_unicode(r'\d{7,10}') # future lint: disable=non-unicode-string + "fetch", + salt.utils.stringutils.to_unicode( + r"\d{7,10}" + ), # future lint: disable=non-unicode-string ) ) # Make sure that all three values are still there (since none should # have been removed) self.assertEqual( - self.conf.get(self.remote, 'fetch'), - [orig_refspec, new_refspec_1, new_refspec_2] + self.conf.get(self.remote, "fetch"), + [orig_refspec, new_refspec_1, new_refspec_2], ) # Remove one of the values - self.assertTrue( - self.conf.remove_option_regexp(self.remote, 'fetch', 'tags')) + self.assertTrue(self.conf.remove_option_regexp(self.remote, "fetch", "tags")) # Confirm that the value is gone self.assertEqual( - self.conf.get(self.remote, 'fetch'), - [orig_refspec, new_refspec_2] + self.conf.get(self.remote, "fetch"), [orig_refspec, new_refspec_2] ) # Remove the other one we added earlier - self.assertTrue( - self.conf.remove_option_regexp(self.remote, 'fetch', 'foo')) + self.assertTrue(self.conf.remove_option_regexp(self.remote, "fetch", "foo")) # Since the option now only has one value, it should be a string - self.assertEqual(self.conf.get(self.remote, 'fetch'), orig_refspec) + self.assertEqual(self.conf.get(self.remote, "fetch"), orig_refspec) # Remove the last remaining option - self.assertTrue( - self.conf.remove_option_regexp(self.remote, 'fetch', 'heads')) + self.assertTrue(self.conf.remove_option_regexp(self.remote, "fetch", "heads")) # Trying to do a get now should raise an exception self.assertRaises( - salt.utils.configparser.NoOptionError, - self.conf.get, - self.remote, - 'fetch') + salt.utils.configparser.NoOptionError, self.conf.get, self.remote, "fetch" + ) def test_write(self): - ''' + """ Test writing using non-binary filehandle - ''' - self._test_write(mode='w') + """ + self._test_write(mode="w") def test_write_binary(self): - ''' + """ Test writing using binary filehandle - ''' - self._test_write(mode='wb') + """ + self._test_write(mode="wb") diff --git a/tests/unit/utils/test_context.py b/tests/unit/utils/test_context.py index 5d70311151b..e41bd87bd91 100644 --- a/tests/unit/utils/test_context.py +++ b/tests/unit/utils/test_context.py @@ -1,24 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.context_test ~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs from __future__ import absolute_import -import salt.ext.tornado.stack_context -import salt.ext.tornado.gen -from salt.ext.tornado.testing import AsyncTestCase, gen_test + import threading import time -# Import Salt Testing libs -from tests.support.unit import TestCase -from salt.ext.six.moves import range +import salt.ext.tornado.gen +import salt.ext.tornado.stack_context # Import Salt libs import salt.utils.json +from salt.ext.six.moves import range +from salt.ext.tornado.testing import AsyncTestCase, gen_test from salt.utils.context import ContextDict, NamespacedDictWrapper +# Import Salt Testing libs +from tests.support.unit import TestCase + class ContextDictTests(AsyncTestCase): # how many threads/coroutines to run at a time @@ -28,24 +30,24 @@ class ContextDictTests(AsyncTestCase): super(ContextDictTests, self).setUp() self.cd = ContextDict() # set a global value - self.cd['foo'] = 'global' + self.cd["foo"] = "global" def test_threads(self): - '''Verify that ContextDict overrides properly within threads - ''' + """Verify that ContextDict overrides properly within threads + """ rets = [] def tgt(x, s): inner_ret = [] over = self.cd.clone() - inner_ret.append(self.cd.get('foo')) + inner_ret.append(self.cd.get("foo")) with over: - inner_ret.append(over.get('foo')) - over['foo'] = x - inner_ret.append(over.get('foo')) + inner_ret.append(over.get("foo")) + over["foo"] = x + inner_ret.append(over.get("foo")) time.sleep(s) - inner_ret.append(over.get('foo')) + inner_ret.append(over.get("foo")) rets.append(inner_ret) threads = [] @@ -64,26 +66,27 @@ class ContextDictTests(AsyncTestCase): @gen_test def test_coroutines(self): - '''Verify that ContextDict overrides properly within coroutines - ''' + """Verify that ContextDict overrides properly within coroutines + """ + @salt.ext.tornado.gen.coroutine def secondary_coroutine(over): - raise salt.ext.tornado.gen.Return(over.get('foo')) + raise salt.ext.tornado.gen.Return(over.get("foo")) @salt.ext.tornado.gen.coroutine def tgt(x, s, over): inner_ret = [] # first grab the global - inner_ret.append(self.cd.get('foo')) + inner_ret.append(self.cd.get("foo")) # grab the child's global (should match) - inner_ret.append(over.get('foo')) + inner_ret.append(over.get("foo")) # override the global - over['foo'] = x - inner_ret.append(over.get('foo')) + over["foo"] = x + inner_ret.append(over.get("foo")) # sleep for some time to let other coroutines do this section of code yield salt.ext.tornado.gen.sleep(s) # get the value of the global again. - inner_ret.append(over.get('foo')) + inner_ret.append(over.get("foo")) # Call another coroutine to verify that we keep our context r = yield secondary_coroutine(over) inner_ret.append(r) @@ -95,10 +98,12 @@ class ContextDictTests(AsyncTestCase): s = self.num_concurrent_tasks - x over = self.cd.clone() + # pylint: disable=cell-var-from-loop f = salt.ext.tornado.stack_context.run_with_stack_context( - salt.ext.tornado.stack_context.StackContext(lambda: over), # pylint: disable=W0640 - lambda: tgt(x, s/5.0, over), # pylint: disable=W0640 + salt.ext.tornado.stack_context.StackContext(lambda: over), + lambda: tgt(x, s / 5.0, over), ) + # pylint: enable=cell-var-from-loop futures.append(f) wait_iterator = salt.ext.tornado.gen.WaitIterator(*futures) @@ -106,53 +111,46 @@ class ContextDictTests(AsyncTestCase): r = yield wait_iterator.next() # pylint: disable=incompatible-py3-code self.assertEqual(r[0], r[1]) # verify that the global value remails self.assertEqual(r[2], r[3]) # verify that the override sticks locally - self.assertEqual(r[3], r[4]) # verify that the override sticks across coroutines + self.assertEqual( + r[3], r[4] + ) # verify that the override sticks across coroutines def test_basic(self): - '''Test that the contextDict is a dict - ''' + """Test that the contextDict is a dict + """ # ensure we get the global value self.assertEqual( - dict(self.cd), - {'foo': 'global'}, + dict(self.cd), {"foo": "global"}, ) def test_override(self): over = self.cd.clone() - over['bar'] = 'global' + over["bar"] = "global" self.assertEqual( - dict(over), - {'foo': 'global', 'bar': 'global'}, + dict(over), {"foo": "global", "bar": "global"}, ) self.assertEqual( - dict(self.cd), - {'foo': 'global'}, + dict(self.cd), {"foo": "global"}, ) with over: self.assertEqual( - dict(over), - {'foo': 'global', 'bar': 'global'}, + dict(over), {"foo": "global", "bar": "global"}, ) self.assertEqual( - dict(self.cd), - {'foo': 'global', 'bar': 'global'}, + dict(self.cd), {"foo": "global", "bar": "global"}, ) - over['bar'] = 'baz' + over["bar"] = "baz" self.assertEqual( - dict(over), - {'foo': 'global', 'bar': 'baz'}, + dict(over), {"foo": "global", "bar": "baz"}, ) self.assertEqual( - dict(self.cd), - {'foo': 'global', 'bar': 'baz'}, + dict(self.cd), {"foo": "global", "bar": "baz"}, ) self.assertEqual( - dict(over), - {'foo': 'global', 'bar': 'baz'}, + dict(over), {"foo": "global", "bar": "baz"}, ) self.assertEqual( - dict(self.cd), - {'foo': 'global'}, + dict(self.cd), {"foo": "global"}, ) def test_multiple_contexts(self): @@ -160,37 +158,36 @@ class ContextDictTests(AsyncTestCase): for x in range(0, 10): cds.append(self.cd.clone(bar=x)) for x, cd in enumerate(cds): - self.assertNotIn('bar', self.cd) + self.assertNotIn("bar", self.cd) with cd: self.assertEqual( - dict(self.cd), - {'bar': x, 'foo': 'global'}, + dict(self.cd), {"bar": x, "foo": "global"}, ) - self.assertNotIn('bar', self.cd) + self.assertNotIn("bar", self.cd) class NamespacedDictWrapperTests(TestCase): - PREFIX = 'prefix' + PREFIX = "prefix" def setUp(self): self._dict = {} def test_single_key(self): - self._dict['prefix'] = {'foo': 'bar'} - w = NamespacedDictWrapper(self._dict, 'prefix') - self.assertEqual(w['foo'], 'bar') + self._dict["prefix"] = {"foo": "bar"} + w = NamespacedDictWrapper(self._dict, "prefix") + self.assertEqual(w["foo"], "bar") def test_multiple_key(self): - self._dict['prefix'] = {'foo': {'bar': 'baz'}} - w = NamespacedDictWrapper(self._dict, ('prefix', 'foo')) - self.assertEqual(w['bar'], 'baz') + self._dict["prefix"] = {"foo": {"bar": "baz"}} + w = NamespacedDictWrapper(self._dict, ("prefix", "foo")) + self.assertEqual(w["bar"], "baz") def test_json_dumps_single_key(self): - self._dict['prefix'] = {'foo': {'bar': 'baz'}} - w = NamespacedDictWrapper(self._dict, 'prefix') + self._dict["prefix"] = {"foo": {"bar": "baz"}} + w = NamespacedDictWrapper(self._dict, "prefix") self.assertEqual(salt.utils.json.dumps(w), '{"foo": {"bar": "baz"}}') def test_json_dumps_multiple_key(self): - self._dict['prefix'] = {'foo': {'bar': 'baz'}} - w = NamespacedDictWrapper(self._dict, ('prefix', 'foo')) + self._dict["prefix"] = {"foo": {"bar": "baz"}} + w = NamespacedDictWrapper(self._dict, ("prefix", "foo")) self.assertEqual(salt.utils.json.dumps(w), '{"bar": "baz"}') diff --git a/tests/unit/utils/test_data.py b/tests/unit/utils/test_data.py index 8fa352321cf..6c5d0de3658 100644 --- a/tests/unit/utils/test_data.py +++ b/tests/unit/utils/test_data.py @@ -1,38 +1,41 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.utils.data -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import logging # Import Salt libs import salt.utils.data import salt.utils.stringutils -from salt.utils.odict import OrderedDict -from tests.support.unit import TestCase, LOREM_IPSUM -from tests.support.mock import patch - -# Import 3rd party libs -from salt.ext.six.moves import builtins # pylint: disable=import-error,redefined-builtin from salt.ext import six +# Import 3rd party libs +from salt.ext.six.moves import ( # pylint: disable=import-error,redefined-builtin + builtins, +) +from salt.utils.odict import OrderedDict +from tests.support.mock import patch +from tests.support.unit import LOREM_IPSUM, TestCase + log = logging.getLogger(__name__) -_b = lambda x: x.encode('utf-8') +_b = lambda x: x.encode("utf-8") _s = lambda x: salt.utils.stringutils.to_str(x, normalize=True) # Some randomized data that will not decode -BYTES = b'1\x814\x10' +BYTES = b"1\x814\x10" # This is an example of a unicode string with й constructed using two separate # code points. Do not modify it. -EGGS = '\u044f\u0438\u0306\u0446\u0430' +EGGS = "\u044f\u0438\u0306\u0446\u0430" class DataTestCase(TestCase): test_data = [ - 'unicode_str', - _b('питон'), + "unicode_str", + _b("питон"), 123, 456.789, True, @@ -40,71 +43,79 @@ class DataTestCase(TestCase): None, EGGS, BYTES, - [123, 456.789, _b('спам'), True, False, None, EGGS, BYTES], - (987, 654.321, _b('яйца'), EGGS, None, (True, EGGS, BYTES)), - {_b('str_key'): _b('str_val'), - None: True, - 123: 456.789, - EGGS: BYTES, - _b('subdict'): {'unicode_key': EGGS, - _b('tuple'): (123, 'hello', _b('world'), True, EGGS, BYTES), - _b('list'): [456, _b('спам'), False, EGGS, BYTES]}}, - OrderedDict([(_b('foo'), 'bar'), (123, 456), (EGGS, BYTES)]) + [123, 456.789, _b("спам"), True, False, None, EGGS, BYTES], + (987, 654.321, _b("яйца"), EGGS, None, (True, EGGS, BYTES)), + { + _b("str_key"): _b("str_val"), + None: True, + 123: 456.789, + EGGS: BYTES, + _b("subdict"): { + "unicode_key": EGGS, + _b("tuple"): (123, "hello", _b("world"), True, EGGS, BYTES), + _b("list"): [456, _b("спам"), False, EGGS, BYTES], + }, + }, + OrderedDict([(_b("foo"), "bar"), (123, 456), (EGGS, BYTES)]), ] def test_sorted_ignorecase(self): - test_list = ['foo', 'Foo', 'bar', 'Bar'] - expected_list = ['bar', 'Bar', 'foo', 'Foo'] - self.assertEqual( - salt.utils.data.sorted_ignorecase(test_list), expected_list) + test_list = ["foo", "Foo", "bar", "Bar"] + expected_list = ["bar", "Bar", "foo", "Foo"] + self.assertEqual(salt.utils.data.sorted_ignorecase(test_list), expected_list) def test_mysql_to_dict(self): - test_mysql_output = ['+----+------+-----------+------+---------+------+-------+------------------+', - '| Id | User | Host | db | Command | Time | State | Info |', - '+----+------+-----------+------+---------+------+-------+------------------+', - '| 7 | root | localhost | NULL | Query | 0 | init | show processlist |', - '+----+------+-----------+------+---------+------+-------+------------------+'] + test_mysql_output = [ + "+----+------+-----------+------+---------+------+-------+------------------+", + "| Id | User | Host | db | Command | Time | State | Info |", + "+----+------+-----------+------+---------+------+-------+------------------+", + "| 7 | root | localhost | NULL | Query | 0 | init | show processlist |", + "+----+------+-----------+------+---------+------+-------+------------------+", + ] - ret = salt.utils.data.mysql_to_dict(test_mysql_output, 'Info') + ret = salt.utils.data.mysql_to_dict(test_mysql_output, "Info") expected_dict = { - 'show processlist': {'Info': 'show processlist', 'db': 'NULL', 'State': 'init', 'Host': 'localhost', - 'Command': 'Query', 'User': 'root', 'Time': 0, 'Id': 7}} + "show processlist": { + "Info": "show processlist", + "db": "NULL", + "State": "init", + "Host": "localhost", + "Command": "Query", + "User": "root", + "Time": 0, + "Id": 7, + } + } self.assertDictEqual(ret, expected_dict) def test_subdict_match(self): - test_two_level_dict = {'foo': {'bar': 'baz'}} - test_two_level_comb_dict = {'foo': {'bar': 'baz:woz'}} + test_two_level_dict = {"foo": {"bar": "baz"}} + test_two_level_comb_dict = {"foo": {"bar": "baz:woz"}} test_two_level_dict_and_list = { - 'abc': ['def', 'ghi', {'lorem': {'ipsum': [{'dolor': 'sit'}]}}], + "abc": ["def", "ghi", {"lorem": {"ipsum": [{"dolor": "sit"}]}}], } - test_three_level_dict = {'a': {'b': {'c': 'v'}}} + test_three_level_dict = {"a": {"b": {"c": "v"}}} self.assertTrue( - salt.utils.data.subdict_match( - test_two_level_dict, 'foo:bar:baz' - ) + salt.utils.data.subdict_match(test_two_level_dict, "foo:bar:baz") ) # In test_two_level_comb_dict, 'foo:bar' corresponds to 'baz:woz', not # 'baz'. This match should return False. self.assertFalse( - salt.utils.data.subdict_match( - test_two_level_comb_dict, 'foo:bar:baz' - ) + salt.utils.data.subdict_match(test_two_level_comb_dict, "foo:bar:baz") ) # This tests matching with the delimiter in the value part (in other # words, that the path 'foo:bar' corresponds to the string 'baz:woz'). self.assertTrue( - salt.utils.data.subdict_match( - test_two_level_comb_dict, 'foo:bar:baz:woz' - ) + salt.utils.data.subdict_match(test_two_level_comb_dict, "foo:bar:baz:woz") ) # This would match if test_two_level_comb_dict['foo']['bar'] was equal # to 'baz:woz:wiz', or if there was more deep nesting. But it does not, # so this should return False. self.assertFalse( salt.utils.data.subdict_match( - test_two_level_comb_dict, 'foo:bar:baz:woz:wiz' + test_two_level_comb_dict, "foo:bar:baz:woz:wiz" ) ) # This tests for cases when a key path corresponds to a list. The @@ -115,189 +126,171 @@ class DataTestCase(TestCase): # salt.utils.traverse_list_and_dict() so this particular assertion is a # sanity check. self.assertTrue( - salt.utils.data.subdict_match( - test_two_level_dict_and_list, 'abc:ghi' - ) + salt.utils.data.subdict_match(test_two_level_dict_and_list, "abc:ghi") ) # This tests the use case of a dict embedded in a list, embedded in a # list, embedded in a dict. This is a rather absurd case, but it # confirms that match recursion works properly. self.assertTrue( salt.utils.data.subdict_match( - test_two_level_dict_and_list, 'abc:lorem:ipsum:dolor:sit' + test_two_level_dict_and_list, "abc:lorem:ipsum:dolor:sit" ) ) # Test four level dict match for reference - self.assertTrue( - salt.utils.data.subdict_match( - test_three_level_dict, 'a:b:c:v' - ) - ) + self.assertTrue(salt.utils.data.subdict_match(test_three_level_dict, "a:b:c:v")) # Test regression in 2015.8 where 'a:c:v' would match 'a:b:c:v' - self.assertFalse( - salt.utils.data.subdict_match( - test_three_level_dict, 'a:c:v' - ) - ) + self.assertFalse(salt.utils.data.subdict_match(test_three_level_dict, "a:c:v")) # Test wildcard match - self.assertTrue( - salt.utils.data.subdict_match( - test_three_level_dict, 'a:*:c:v' - ) - ) + self.assertTrue(salt.utils.data.subdict_match(test_three_level_dict, "a:*:c:v")) def test_subdict_match_with_wildcards(self): - ''' + """ Tests subdict matching when wildcards are used in the expression - ''' - data = { - 'a': { - 'b': { - 'ç': 'd', - 'é': ['eff', 'gee', '8ch'], - 'ĩ': {'j': 'k'} - } - } - } - assert salt.utils.data.subdict_match(data, '*:*:*:*') - assert salt.utils.data.subdict_match(data, 'a:*:*:*') - assert salt.utils.data.subdict_match(data, 'a:b:*:*') - assert salt.utils.data.subdict_match(data, 'a:b:ç:*') - assert salt.utils.data.subdict_match(data, 'a:b:*:d') - assert salt.utils.data.subdict_match(data, 'a:*:ç:d') - assert salt.utils.data.subdict_match(data, '*:b:ç:d') - assert salt.utils.data.subdict_match(data, '*:*:ç:d') - assert salt.utils.data.subdict_match(data, '*:*:*:d') - assert salt.utils.data.subdict_match(data, 'a:*:*:d') - assert salt.utils.data.subdict_match(data, 'a:b:*:ef*') - assert salt.utils.data.subdict_match(data, 'a:b:*:g*') - assert salt.utils.data.subdict_match(data, 'a:b:*:j:*') - assert salt.utils.data.subdict_match(data, 'a:b:*:j:k') - assert salt.utils.data.subdict_match(data, 'a:b:*:*:k') - assert salt.utils.data.subdict_match(data, 'a:b:*:*:*') + """ + data = {"a": {"b": {"ç": "d", "é": ["eff", "gee", "8ch"], "ĩ": {"j": "k"}}}} + assert salt.utils.data.subdict_match(data, "*:*:*:*") + assert salt.utils.data.subdict_match(data, "a:*:*:*") + assert salt.utils.data.subdict_match(data, "a:b:*:*") + assert salt.utils.data.subdict_match(data, "a:b:ç:*") + assert salt.utils.data.subdict_match(data, "a:b:*:d") + assert salt.utils.data.subdict_match(data, "a:*:ç:d") + assert salt.utils.data.subdict_match(data, "*:b:ç:d") + assert salt.utils.data.subdict_match(data, "*:*:ç:d") + assert salt.utils.data.subdict_match(data, "*:*:*:d") + assert salt.utils.data.subdict_match(data, "a:*:*:d") + assert salt.utils.data.subdict_match(data, "a:b:*:ef*") + assert salt.utils.data.subdict_match(data, "a:b:*:g*") + assert salt.utils.data.subdict_match(data, "a:b:*:j:*") + assert salt.utils.data.subdict_match(data, "a:b:*:j:k") + assert salt.utils.data.subdict_match(data, "a:b:*:*:k") + assert salt.utils.data.subdict_match(data, "a:b:*:*:*") def test_traverse_dict(self): - test_two_level_dict = {'foo': {'bar': 'baz'}} + test_two_level_dict = {"foo": {"bar": "baz"}} self.assertDictEqual( - {'not_found': 'nope'}, + {"not_found": "nope"}, salt.utils.data.traverse_dict( - test_two_level_dict, 'foo:bar:baz', {'not_found': 'nope'} - ) + test_two_level_dict, "foo:bar:baz", {"not_found": "nope"} + ), ) self.assertEqual( - 'baz', + "baz", salt.utils.data.traverse_dict( - test_two_level_dict, 'foo:bar', {'not_found': 'not_found'} - ) + test_two_level_dict, "foo:bar", {"not_found": "not_found"} + ), ) def test_traverse_dict_and_list(self): - test_two_level_dict = {'foo': {'bar': 'baz'}} + test_two_level_dict = {"foo": {"bar": "baz"}} test_two_level_dict_and_list = { - 'foo': ['bar', 'baz', {'lorem': {'ipsum': [{'dolor': 'sit'}]}}] + "foo": ["bar", "baz", {"lorem": {"ipsum": [{"dolor": "sit"}]}}] } # Check traversing too far: salt.utils.data.traverse_dict_and_list() returns # the value corresponding to a given key path, and baz is a value # corresponding to the key path foo:bar. self.assertDictEqual( - {'not_found': 'nope'}, + {"not_found": "nope"}, salt.utils.data.traverse_dict_and_list( - test_two_level_dict, 'foo:bar:baz', {'not_found': 'nope'} - ) + test_two_level_dict, "foo:bar:baz", {"not_found": "nope"} + ), ) # Now check to ensure that foo:bar corresponds to baz self.assertEqual( - 'baz', + "baz", salt.utils.data.traverse_dict_and_list( - test_two_level_dict, 'foo:bar', {'not_found': 'not_found'} - ) + test_two_level_dict, "foo:bar", {"not_found": "not_found"} + ), ) # Check traversing too far self.assertDictEqual( - {'not_found': 'nope'}, + {"not_found": "nope"}, salt.utils.data.traverse_dict_and_list( - test_two_level_dict_and_list, 'foo:bar', {'not_found': 'nope'} - ) + test_two_level_dict_and_list, "foo:bar", {"not_found": "nope"} + ), ) # Check index 1 (2nd element) of list corresponding to path 'foo' self.assertEqual( - 'baz', + "baz", salt.utils.data.traverse_dict_and_list( - test_two_level_dict_and_list, 'foo:1', {'not_found': 'not_found'} - ) + test_two_level_dict_and_list, "foo:1", {"not_found": "not_found"} + ), ) # Traverse a couple times into dicts embedded in lists self.assertEqual( - 'sit', + "sit", salt.utils.data.traverse_dict_and_list( test_two_level_dict_and_list, - 'foo:lorem:ipsum:dolor', - {'not_found': 'not_found'} - ) + "foo:lorem:ipsum:dolor", + {"not_found": "not_found"}, + ), ) def test_compare_dicts(self): - ret = salt.utils.data.compare_dicts(old={'foo': 'bar'}, new={'foo': 'bar'}) + ret = salt.utils.data.compare_dicts(old={"foo": "bar"}, new={"foo": "bar"}) self.assertEqual(ret, {}) - ret = salt.utils.data.compare_dicts(old={'foo': 'bar'}, new={'foo': 'woz'}) - expected_ret = {'foo': {'new': 'woz', 'old': 'bar'}} + ret = salt.utils.data.compare_dicts(old={"foo": "bar"}, new={"foo": "woz"}) + expected_ret = {"foo": {"new": "woz", "old": "bar"}} self.assertDictEqual(ret, expected_ret) def test_compare_lists_no_change(self): - ret = salt.utils.data.compare_lists(old=[1, 2, 3, 'a', 'b', 'c'], - new=[1, 2, 3, 'a', 'b', 'c']) + ret = salt.utils.data.compare_lists( + old=[1, 2, 3, "a", "b", "c"], new=[1, 2, 3, "a", "b", "c"] + ) expected = {} self.assertDictEqual(ret, expected) def test_compare_lists_changes(self): - ret = salt.utils.data.compare_lists(old=[1, 2, 3, 'a', 'b', 'c'], - new=[1, 2, 4, 'x', 'y', 'z']) - expected = {'new': [4, 'x', 'y', 'z'], 'old': [3, 'a', 'b', 'c']} + ret = salt.utils.data.compare_lists( + old=[1, 2, 3, "a", "b", "c"], new=[1, 2, 4, "x", "y", "z"] + ) + expected = {"new": [4, "x", "y", "z"], "old": [3, "a", "b", "c"]} self.assertDictEqual(ret, expected) def test_compare_lists_changes_new(self): - ret = salt.utils.data.compare_lists(old=[1, 2, 3], - new=[1, 2, 3, 'x', 'y', 'z']) - expected = {'new': ['x', 'y', 'z']} + ret = salt.utils.data.compare_lists(old=[1, 2, 3], new=[1, 2, 3, "x", "y", "z"]) + expected = {"new": ["x", "y", "z"]} self.assertDictEqual(ret, expected) def test_compare_lists_changes_old(self): - ret = salt.utils.data.compare_lists(old=[1, 2, 3, 'a', 'b', 'c'], - new=[1, 2, 3]) - expected = {'old': ['a', 'b', 'c']} + ret = salt.utils.data.compare_lists(old=[1, 2, 3, "a", "b", "c"], new=[1, 2, 3]) + expected = {"old": ["a", "b", "c"]} self.assertDictEqual(ret, expected) def test_decode(self): - ''' + """ Companion to test_decode_to_str, they should both be kept up-to-date with one another. NOTE: This uses the lambda "_b" defined above in the global scope, which encodes a string to a bytestring, assuming utf-8. - ''' + """ expected = [ - 'unicode_str', - 'питон', + "unicode_str", + "питон", 123, 456.789, True, False, None, - 'яйца', + "яйца", BYTES, - [123, 456.789, 'спам', True, False, None, 'яйца', BYTES], - (987, 654.321, 'яйца', 'яйца', None, (True, 'яйца', BYTES)), - {'str_key': 'str_val', - None: True, - 123: 456.789, - 'яйца': BYTES, - 'subdict': {'unicode_key': 'яйца', - 'tuple': (123, 'hello', 'world', True, 'яйца', BYTES), - 'list': [456, 'спам', False, 'яйца', BYTES]}}, - OrderedDict([('foo', 'bar'), (123, 456), ('яйца', BYTES)]) + [123, 456.789, "спам", True, False, None, "яйца", BYTES], + (987, 654.321, "яйца", "яйца", None, (True, "яйца", BYTES)), + { + "str_key": "str_val", + None: True, + 123: 456.789, + "яйца": BYTES, + "subdict": { + "unicode_key": "яйца", + "tuple": (123, "hello", "world", True, "яйца", BYTES), + "list": [456, "спам", False, "яйца", BYTES], + }, + }, + OrderedDict([("foo", "bar"), (123, 456), ("яйца", BYTES)]), ] ret = salt.utils.data.decode( @@ -305,7 +298,8 @@ class DataTestCase(TestCase): keep=True, normalize=True, preserve_dict_class=True, - preserve_tuples=True) + preserve_tuples=True, + ) self.assertEqual(ret, expected) # The binary data in the data structure should fail to decode, even @@ -317,74 +311,79 @@ class DataTestCase(TestCase): keep=False, normalize=True, preserve_dict_class=True, - preserve_tuples=True) + preserve_tuples=True, + ) # Now munge the expected data so that we get what we would expect if we # disable preservation of dict class and tuples - expected[10] = [987, 654.321, 'яйца', 'яйца', None, [True, 'яйца', BYTES]] - expected[11]['subdict']['tuple'] = [123, 'hello', 'world', True, 'яйца', BYTES] - expected[12] = {'foo': 'bar', 123: 456, 'яйца': BYTES} + expected[10] = [987, 654.321, "яйца", "яйца", None, [True, "яйца", BYTES]] + expected[11]["subdict"]["tuple"] = [123, "hello", "world", True, "яйца", BYTES] + expected[12] = {"foo": "bar", 123: 456, "яйца": BYTES} ret = salt.utils.data.decode( self.test_data, keep=True, normalize=True, preserve_dict_class=False, - preserve_tuples=False) + preserve_tuples=False, + ) self.assertEqual(ret, expected) # Now test single non-string, non-data-structure items, these should # return the same value when passed to this function for item in (123, 4.56, True, False, None): - log.debug('Testing decode of %s', item) + log.debug("Testing decode of %s", item) self.assertEqual(salt.utils.data.decode(item), item) # Test single strings (not in a data structure) - self.assertEqual(salt.utils.data.decode('foo'), 'foo') - self.assertEqual(salt.utils.data.decode(_b('bar')), 'bar') - self.assertEqual(salt.utils.data.decode(EGGS, normalize=True), 'яйца') + self.assertEqual(salt.utils.data.decode("foo"), "foo") + self.assertEqual(salt.utils.data.decode(_b("bar")), "bar") + self.assertEqual(salt.utils.data.decode(EGGS, normalize=True), "яйца") self.assertEqual(salt.utils.data.decode(EGGS, normalize=False), EGGS) # Test binary blob self.assertEqual(salt.utils.data.decode(BYTES, keep=True), BYTES) - self.assertRaises( - UnicodeDecodeError, - salt.utils.data.decode, - BYTES, - keep=False) + self.assertRaises(UnicodeDecodeError, salt.utils.data.decode, BYTES, keep=False) def test_decode_to_str(self): - ''' + """ Companion to test_decode, they should both be kept up-to-date with one another. NOTE: This uses the lambda "_s" defined above in the global scope, which converts the string/bytestring to a str type. - ''' + """ expected = [ - _s('unicode_str'), - _s('питон'), + _s("unicode_str"), + _s("питон"), 123, 456.789, True, False, None, - _s('яйца'), + _s("яйца"), BYTES, - [123, 456.789, _s('спам'), True, False, None, _s('яйца'), BYTES], - (987, 654.321, _s('яйца'), _s('яйца'), None, (True, _s('яйца'), BYTES)), + [123, 456.789, _s("спам"), True, False, None, _s("яйца"), BYTES], + (987, 654.321, _s("яйца"), _s("яйца"), None, (True, _s("яйца"), BYTES)), { - _s('str_key'): _s('str_val'), + _s("str_key"): _s("str_val"), None: True, 123: 456.789, - _s('яйца'): BYTES, - _s('subdict'): { - _s('unicode_key'): _s('яйца'), - _s('tuple'): (123, _s('hello'), _s('world'), True, _s('яйца'), BYTES), - _s('list'): [456, _s('спам'), False, _s('яйца'), BYTES] - } + _s("яйца"): BYTES, + _s("subdict"): { + _s("unicode_key"): _s("яйца"), + _s("tuple"): ( + 123, + _s("hello"), + _s("world"), + True, + _s("яйца"), + BYTES, + ), + _s("list"): [456, _s("спам"), False, _s("яйца"), BYTES], + }, }, - OrderedDict([(_s('foo'), _s('bar')), (123, 456), (_s('яйца'), BYTES)]) + OrderedDict([(_s("foo"), _s("bar")), (123, 456), (_s("яйца"), BYTES)]), ] ret = salt.utils.data.decode( @@ -393,7 +392,8 @@ class DataTestCase(TestCase): normalize=True, preserve_dict_class=True, preserve_tuples=True, - to_str=True) + to_str=True, + ) self.assertEqual(ret, expected) if six.PY3: @@ -407,13 +407,28 @@ class DataTestCase(TestCase): normalize=True, preserve_dict_class=True, preserve_tuples=True, - to_str=True) + to_str=True, + ) # Now munge the expected data so that we get what we would expect if we # disable preservation of dict class and tuples - expected[10] = [987, 654.321, _s('яйца'), _s('яйца'), None, [True, _s('яйца'), BYTES]] - expected[11][_s('subdict')][_s('tuple')] = [123, _s('hello'), _s('world'), True, _s('яйца'), BYTES] - expected[12] = {_s('foo'): _s('bar'), 123: 456, _s('яйца'): BYTES} + expected[10] = [ + 987, + 654.321, + _s("яйца"), + _s("яйца"), + None, + [True, _s("яйца"), BYTES], + ] + expected[11][_s("subdict")][_s("tuple")] = [ + 123, + _s("hello"), + _s("world"), + True, + _s("яйца"), + BYTES, + ] + expected[12] = {_s("foo"): _s("bar"), 123: 456, _s("яйца"): BYTES} ret = salt.utils.data.decode( self.test_data, @@ -421,47 +436,46 @@ class DataTestCase(TestCase): normalize=True, preserve_dict_class=False, preserve_tuples=False, - to_str=True) + to_str=True, + ) self.assertEqual(ret, expected) # Now test single non-string, non-data-structure items, these should # return the same value when passed to this function for item in (123, 4.56, True, False, None): - log.debug('Testing decode of %s', item) + log.debug("Testing decode of %s", item) self.assertEqual(salt.utils.data.decode(item, to_str=True), item) # Test single strings (not in a data structure) - self.assertEqual(salt.utils.data.decode('foo', to_str=True), _s('foo')) - self.assertEqual(salt.utils.data.decode(_b('bar'), to_str=True), _s('bar')) + self.assertEqual(salt.utils.data.decode("foo", to_str=True), _s("foo")) + self.assertEqual(salt.utils.data.decode(_b("bar"), to_str=True), _s("bar")) # Test binary blob - self.assertEqual( - salt.utils.data.decode(BYTES, keep=True, to_str=True), - BYTES - ) + self.assertEqual(salt.utils.data.decode(BYTES, keep=True, to_str=True), BYTES) if six.PY3: self.assertRaises( UnicodeDecodeError, salt.utils.data.decode, BYTES, keep=False, - to_str=True) + to_str=True, + ) def test_decode_fallback(self): - ''' + """ Test fallback to utf-8 - ''' - with patch.object(builtins, '__salt_system_encoding__', 'ascii'): - self.assertEqual(salt.utils.data.decode(_b('яйца')), 'яйца') + """ + with patch.object(builtins, "__salt_system_encoding__", "ascii"): + self.assertEqual(salt.utils.data.decode(_b("яйца")), "яйца") def test_encode(self): - ''' + """ NOTE: This uses the lambda "_b" defined above in the global scope, which encodes a string to a bytestring, assuming utf-8. - ''' + """ expected = [ - _b('unicode_str'), - _b('питон'), + _b("unicode_str"), + _b("питон"), 123, 456.789, True, @@ -469,67 +483,71 @@ class DataTestCase(TestCase): None, _b(EGGS), BYTES, - [123, 456.789, _b('спам'), True, False, None, _b(EGGS), BYTES], - (987, 654.321, _b('яйца'), _b(EGGS), None, (True, _b(EGGS), BYTES)), + [123, 456.789, _b("спам"), True, False, None, _b(EGGS), BYTES], + (987, 654.321, _b("яйца"), _b(EGGS), None, (True, _b(EGGS), BYTES)), { - _b('str_key'): _b('str_val'), + _b("str_key"): _b("str_val"), None: True, 123: 456.789, _b(EGGS): BYTES, - _b('subdict'): { - _b('unicode_key'): _b(EGGS), - _b('tuple'): (123, _b('hello'), _b('world'), True, _b(EGGS), BYTES), - _b('list'): [456, _b('спам'), False, _b(EGGS), BYTES] - } + _b("subdict"): { + _b("unicode_key"): _b(EGGS), + _b("tuple"): (123, _b("hello"), _b("world"), True, _b(EGGS), BYTES), + _b("list"): [456, _b("спам"), False, _b(EGGS), BYTES], + }, }, - OrderedDict([(_b('foo'), _b('bar')), (123, 456), (_b(EGGS), BYTES)]) + OrderedDict([(_b("foo"), _b("bar")), (123, 456), (_b(EGGS), BYTES)]), ] # Both keep=True and keep=False should work because the BYTES data is # already bytes. ret = salt.utils.data.encode( - self.test_data, - keep=True, - preserve_dict_class=True, - preserve_tuples=True) + self.test_data, keep=True, preserve_dict_class=True, preserve_tuples=True + ) self.assertEqual(ret, expected) ret = salt.utils.data.encode( - self.test_data, - keep=False, - preserve_dict_class=True, - preserve_tuples=True) + self.test_data, keep=False, preserve_dict_class=True, preserve_tuples=True + ) self.assertEqual(ret, expected) # Now munge the expected data so that we get what we would expect if we # disable preservation of dict class and tuples - expected[10] = [987, 654.321, _b('яйца'), _b(EGGS), None, [True, _b(EGGS), BYTES]] - expected[11][_b('subdict')][_b('tuple')] = [ - 123, _b('hello'), _b('world'), True, _b(EGGS), BYTES + expected[10] = [ + 987, + 654.321, + _b("яйца"), + _b(EGGS), + None, + [True, _b(EGGS), BYTES], ] - expected[12] = {_b('foo'): _b('bar'), 123: 456, _b(EGGS): BYTES} + expected[11][_b("subdict")][_b("tuple")] = [ + 123, + _b("hello"), + _b("world"), + True, + _b(EGGS), + BYTES, + ] + expected[12] = {_b("foo"): _b("bar"), 123: 456, _b(EGGS): BYTES} ret = salt.utils.data.encode( - self.test_data, - keep=True, - preserve_dict_class=False, - preserve_tuples=False) + self.test_data, keep=True, preserve_dict_class=False, preserve_tuples=False + ) self.assertEqual(ret, expected) ret = salt.utils.data.encode( - self.test_data, - keep=False, - preserve_dict_class=False, - preserve_tuples=False) + self.test_data, keep=False, preserve_dict_class=False, preserve_tuples=False + ) self.assertEqual(ret, expected) # Now test single non-string, non-data-structure items, these should # return the same value when passed to this function for item in (123, 4.56, True, False, None): - log.debug('Testing encode of %s', item) + log.debug("Testing encode of %s", item) self.assertEqual(salt.utils.data.encode(item), item) # Test single strings (not in a data structure) - self.assertEqual(salt.utils.data.encode('foo'), _b('foo')) - self.assertEqual(salt.utils.data.encode(_b('bar')), _b('bar')) + self.assertEqual(salt.utils.data.encode("foo"), _b("foo")) + self.assertEqual(salt.utils.data.encode(_b("bar")), _b("bar")) # Test binary blob, nothing should happen even when keep=False since # the data is already bytes @@ -537,41 +555,43 @@ class DataTestCase(TestCase): self.assertEqual(salt.utils.data.encode(BYTES, keep=False), BYTES) def test_encode_keep(self): - ''' + """ Whereas we tested the keep argument in test_decode, it is much easier to do a more comprehensive test of keep in its own function where we can force the encoding. - ''' - unicode_str = 'питон' - encoding = 'ascii' + """ + unicode_str = "питон" + encoding = "ascii" # Test single string self.assertEqual( - salt.utils.data.encode(unicode_str, encoding, keep=True), - unicode_str) + salt.utils.data.encode(unicode_str, encoding, keep=True), unicode_str + ) self.assertRaises( UnicodeEncodeError, salt.utils.data.encode, unicode_str, encoding, - keep=False) + keep=False, + ) data = [ unicode_str, - [b'foo', [unicode_str], {b'key': unicode_str}, (unicode_str,)], - {b'list': [b'foo', unicode_str], - b'dict': {b'key': unicode_str}, - b'tuple': (b'foo', unicode_str)}, - ([b'foo', unicode_str], {b'key': unicode_str}, (unicode_str,)) + [b"foo", [unicode_str], {b"key": unicode_str}, (unicode_str,)], + { + b"list": [b"foo", unicode_str], + b"dict": {b"key": unicode_str}, + b"tuple": (b"foo", unicode_str), + }, + ([b"foo", unicode_str], {b"key": unicode_str}, (unicode_str,)), ] # Since everything was a bytestring aside from the bogus data, the # return data should be identical. We don't need to test recursive # decoding, that has already been tested in test_encode. self.assertEqual( - salt.utils.data.encode(data, encoding, - keep=True, preserve_tuples=True), - data + salt.utils.data.encode(data, encoding, keep=True, preserve_tuples=True), + data, ) self.assertRaises( UnicodeEncodeError, @@ -579,13 +599,15 @@ class DataTestCase(TestCase): data, encoding, keep=False, - preserve_tuples=True) + preserve_tuples=True, + ) for index, _ in enumerate(data): self.assertEqual( - salt.utils.data.encode(data[index], encoding, - keep=True, preserve_tuples=True), - data[index] + salt.utils.data.encode( + data[index], encoding, keep=True, preserve_tuples=True + ), + data[index], ) self.assertRaises( UnicodeEncodeError, @@ -593,31 +615,36 @@ class DataTestCase(TestCase): data[index], encoding, keep=False, - preserve_tuples=True) + preserve_tuples=True, + ) def test_encode_fallback(self): - ''' + """ Test fallback to utf-8 - ''' - with patch.object(builtins, '__salt_system_encoding__', 'ascii'): - self.assertEqual(salt.utils.data.encode('яйца'), _b('яйца')) - with patch.object(builtins, '__salt_system_encoding__', 'CP1252'): - self.assertEqual(salt.utils.data.encode('Ψ'), _b('Ψ')) + """ + with patch.object(builtins, "__salt_system_encoding__", "ascii"): + self.assertEqual(salt.utils.data.encode("яйца"), _b("яйца")) + with patch.object(builtins, "__salt_system_encoding__", "CP1252"): + self.assertEqual(salt.utils.data.encode("Ψ"), _b("Ψ")) def test_repack_dict(self): - list_of_one_element_dicts = [{'dict_key_1': 'dict_val_1'}, - {'dict_key_2': 'dict_val_2'}, - {'dict_key_3': 'dict_val_3'}] - expected_ret = {'dict_key_1': 'dict_val_1', - 'dict_key_2': 'dict_val_2', - 'dict_key_3': 'dict_val_3'} + list_of_one_element_dicts = [ + {"dict_key_1": "dict_val_1"}, + {"dict_key_2": "dict_val_2"}, + {"dict_key_3": "dict_val_3"}, + ] + expected_ret = { + "dict_key_1": "dict_val_1", + "dict_key_2": "dict_val_2", + "dict_key_3": "dict_val_3", + } ret = salt.utils.data.repack_dictlist(list_of_one_element_dicts) self.assertDictEqual(ret, expected_ret) # Try with yaml - yaml_key_val_pair = '- key1: val1' + yaml_key_val_pair = "- key1: val1" ret = salt.utils.data.repack_dictlist(yaml_key_val_pair) - self.assertDictEqual(ret, {'key1': 'val1'}) + self.assertDictEqual(ret, {"key1": "val1"}) # Make sure we handle non-yaml junk data ret = salt.utils.data.repack_dictlist(LOREM_IPSUM) @@ -626,43 +653,47 @@ class DataTestCase(TestCase): def test_stringify(self): self.assertRaises(TypeError, salt.utils.data.stringify, 9) self.assertEqual( - salt.utils.data.stringify(['one', 'two', str('three'), 4, 5]), # future lint: disable=blacklisted-function - ['one', 'two', 'three', '4', '5'] + salt.utils.data.stringify( + ["one", "two", str("three"), 4, 5] + ), # future lint: disable=blacklisted-function + ["one", "two", "three", "4", "5"], ) def test_json_query(self): # Raises exception if jmespath module is not found - with patch('salt.utils.data.jmespath', None): + with patch("salt.utils.data.jmespath", None): self.assertRaisesRegex( - RuntimeError, 'requires jmespath', - salt.utils.data.json_query, {}, '@' + RuntimeError, "requires jmespath", salt.utils.data.json_query, {}, "@" ) # Test search user_groups = { - 'user1': {'groups': ['group1', 'group2', 'group3']}, - 'user2': {'groups': ['group1', 'group2']}, - 'user3': {'groups': ['group3']}, + "user1": {"groups": ["group1", "group2", "group3"]}, + "user2": {"groups": ["group1", "group2"]}, + "user3": {"groups": ["group3"]}, } - expression = '*.groups[0]' - primary_groups = ['group1', 'group1', 'group3'] + expression = "*.groups[0]" + primary_groups = ["group1", "group1", "group3"] self.assertEqual( - sorted(salt.utils.data.json_query(user_groups, expression)), - primary_groups + sorted(salt.utils.data.json_query(user_groups, expression)), primary_groups ) class FilterFalseyTestCase(TestCase): - ''' + """ Test suite for salt.utils.data.filter_falsey - ''' + """ def test_nop(self): - ''' + """ Test cases where nothing will be done. - ''' + """ # Test with dictionary without recursion - old_dict = {'foo': 'bar', 'bar': {'baz': {'qux': 'quux'}}, 'baz': ['qux', {'foo': 'bar'}]} + old_dict = { + "foo": "bar", + "bar": {"baz": {"qux": "quux"}}, + "baz": ["qux", {"foo": "bar"}], + } new_dict = salt.utils.data.filter_falsey(old_dict) self.assertEqual(old_dict, new_dict) # Check returned type equality @@ -671,23 +702,25 @@ class FilterFalseyTestCase(TestCase): new_dict = salt.utils.data.filter_falsey(old_dict, recurse_depth=3) self.assertEqual(old_dict, new_dict) # Test with list - old_list = ['foo', 'bar'] + old_list = ["foo", "bar"] new_list = salt.utils.data.filter_falsey(old_list) self.assertEqual(old_list, new_list) # Check returned type equality self.assertIs(type(old_list), type(new_list)) # Test with set - old_set = set(['foo', 'bar']) + old_set = set(["foo", "bar"]) new_set = salt.utils.data.filter_falsey(old_set) self.assertEqual(old_set, new_set) # Check returned type equality self.assertIs(type(old_set), type(new_set)) # Test with OrderedDict - old_dict = OrderedDict([ - ('foo', 'bar'), - ('bar', OrderedDict([('qux', 'quux')])), - ('baz', ['qux', OrderedDict([('foo', 'bar')])]) - ]) + old_dict = OrderedDict( + [ + ("foo", "bar"), + ("bar", OrderedDict([("qux", "quux")])), + ("baz", ["qux", OrderedDict([("foo", "bar")])]), + ] + ) new_dict = salt.utils.data.filter_falsey(old_dict) self.assertEqual(old_dict, new_dict) self.assertIs(type(old_dict), type(new_dict)) @@ -696,8 +729,8 @@ class FilterFalseyTestCase(TestCase): new_list = salt.utils.data.filter_falsey(old_list, ignore_types=[type(0)]) self.assertEqual(old_list, new_list) # Test excluding str (or unicode) (or both) - old_list = [''] - new_list = salt.utils.data.filter_falsey(old_list, ignore_types=[type('')]) + old_list = [""] + new_list = salt.utils.data.filter_falsey(old_list, ignore_types=[type("")]) self.assertEqual(old_list, new_list) # Test excluding list old_list = [[]] @@ -709,185 +742,264 @@ class FilterFalseyTestCase(TestCase): self.assertEqual(old_list, new_list) def test_filter_dict_no_recurse(self): - ''' + """ Test filtering a dictionary without recursing. This will only filter out key-values where the values are falsey. - ''' - old_dict = {'foo': None, - 'bar': {'baz': {'qux': None, 'quux': '', 'foo': []}}, - 'baz': ['qux'], - 'qux': {}, - 'quux': []} + """ + old_dict = { + "foo": None, + "bar": {"baz": {"qux": None, "quux": "", "foo": []}}, + "baz": ["qux"], + "qux": {}, + "quux": [], + } new_dict = salt.utils.data.filter_falsey(old_dict) - expect_dict = {'bar': {'baz': {'qux': None, 'quux': '', 'foo': []}}, 'baz': ['qux']} + expect_dict = { + "bar": {"baz": {"qux": None, "quux": "", "foo": []}}, + "baz": ["qux"], + } self.assertEqual(expect_dict, new_dict) self.assertIs(type(expect_dict), type(new_dict)) def test_filter_dict_recurse(self): - ''' + """ Test filtering a dictionary with recursing. This will filter out any key-values where the values are falsey or when the values *become* falsey after filtering their contents (in case they are lists or dicts). - ''' - old_dict = {'foo': None, - 'bar': {'baz': {'qux': None, 'quux': '', 'foo': []}}, - 'baz': ['qux'], - 'qux': {}, - 'quux': []} + """ + old_dict = { + "foo": None, + "bar": {"baz": {"qux": None, "quux": "", "foo": []}}, + "baz": ["qux"], + "qux": {}, + "quux": [], + } new_dict = salt.utils.data.filter_falsey(old_dict, recurse_depth=3) - expect_dict = {'baz': ['qux']} + expect_dict = {"baz": ["qux"]} self.assertEqual(expect_dict, new_dict) self.assertIs(type(expect_dict), type(new_dict)) def test_filter_list_no_recurse(self): - ''' + """ Test filtering a list without recursing. This will only filter out items which are falsey. - ''' - old_list = ['foo', None, [], {}, 0, ''] + """ + old_list = ["foo", None, [], {}, 0, ""] new_list = salt.utils.data.filter_falsey(old_list) - expect_list = ['foo'] + expect_list = ["foo"] self.assertEqual(expect_list, new_list) self.assertIs(type(expect_list), type(new_list)) # Ensure nested values are *not* filtered out. old_list = [ - 'foo', - ['foo'], - ['foo', None], - {'foo': 0}, - {'foo': 'bar', 'baz': []}, - [{'foo': ''}], + "foo", + ["foo"], + ["foo", None], + {"foo": 0}, + {"foo": "bar", "baz": []}, + [{"foo": ""}], ] new_list = salt.utils.data.filter_falsey(old_list) self.assertEqual(old_list, new_list) self.assertIs(type(old_list), type(new_list)) def test_filter_list_recurse(self): - ''' + """ Test filtering a list with recursing. This will filter out any items which are falsey, or which become falsey after filtering their contents (in case they are lists or dicts). - ''' + """ old_list = [ - 'foo', - ['foo'], - ['foo', None], - {'foo': 0}, - {'foo': 'bar', 'baz': []}, - [{'foo': ''}] + "foo", + ["foo"], + ["foo", None], + {"foo": 0}, + {"foo": "bar", "baz": []}, + [{"foo": ""}], ] new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3) - expect_list = ['foo', ['foo'], ['foo'], {'foo': 'bar'}] + expect_list = ["foo", ["foo"], ["foo"], {"foo": "bar"}] self.assertEqual(expect_list, new_list) self.assertIs(type(expect_list), type(new_list)) def test_filter_set_no_recurse(self): - ''' + """ Test filtering a set without recursing. Note that a set cannot contain unhashable types, so recursion is not possible. - ''' - old_set = set([ - 'foo', - None, - 0, - '', - ]) + """ + old_set = set(["foo", None, 0, ""]) new_set = salt.utils.data.filter_falsey(old_set) - expect_set = set(['foo']) + expect_set = set(["foo"]) self.assertEqual(expect_set, new_set) self.assertIs(type(expect_set), type(new_set)) def test_filter_ordereddict_no_recurse(self): - ''' + """ Test filtering an OrderedDict without recursing. - ''' - old_dict = OrderedDict([ - ('foo', None), - ('bar', OrderedDict([('baz', OrderedDict([('qux', None), ('quux', ''), ('foo', [])]))])), - ('baz', ['qux']), - ('qux', {}), - ('quux', []) - ]) + """ + old_dict = OrderedDict( + [ + ("foo", None), + ( + "bar", + OrderedDict( + [ + ( + "baz", + OrderedDict([("qux", None), ("quux", ""), ("foo", [])]), + ) + ] + ), + ), + ("baz", ["qux"]), + ("qux", {}), + ("quux", []), + ] + ) new_dict = salt.utils.data.filter_falsey(old_dict) - expect_dict = OrderedDict([ - ('bar', OrderedDict([('baz', OrderedDict([('qux', None), ('quux', ''), ('foo', [])]))])), - ('baz', ['qux']), - ]) + expect_dict = OrderedDict( + [ + ( + "bar", + OrderedDict( + [ + ( + "baz", + OrderedDict([("qux", None), ("quux", ""), ("foo", [])]), + ) + ] + ), + ), + ("baz", ["qux"]), + ] + ) self.assertEqual(expect_dict, new_dict) self.assertIs(type(expect_dict), type(new_dict)) def test_filter_ordereddict_recurse(self): - ''' + """ Test filtering an OrderedDict with recursing. - ''' - old_dict = OrderedDict([ - ('foo', None), - ('bar', OrderedDict([('baz', OrderedDict([('qux', None), ('quux', ''), ('foo', [])]))])), - ('baz', ['qux']), - ('qux', {}), - ('quux', []) - ]) + """ + old_dict = OrderedDict( + [ + ("foo", None), + ( + "bar", + OrderedDict( + [ + ( + "baz", + OrderedDict([("qux", None), ("quux", ""), ("foo", [])]), + ) + ] + ), + ), + ("baz", ["qux"]), + ("qux", {}), + ("quux", []), + ] + ) new_dict = salt.utils.data.filter_falsey(old_dict, recurse_depth=3) - expect_dict = OrderedDict([ - ('baz', ['qux']), - ]) + expect_dict = OrderedDict([("baz", ["qux"])]) self.assertEqual(expect_dict, new_dict) self.assertIs(type(expect_dict), type(new_dict)) def test_filter_list_recurse_limit(self): - ''' + """ Test filtering a list with recursing, but with a limited depth. Note that the top-level is always processed, so a recursion depth of 2 means that two *additional* levels are processed. - ''' + """ old_list = [None, [None, [None, [None]]]] new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=2) self.assertEqual([[[[None]]]], new_list) def test_filter_dict_recurse_limit(self): - ''' + """ Test filtering a dict with recursing, but with a limited depth. Note that the top-level is always processed, so a recursion depth of 2 means that two *additional* levels are processed. - ''' - old_dict = {'one': None, - 'foo': {'two': None, 'bar': {'three': None, 'baz': {'four': None}}}} + """ + old_dict = { + "one": None, + "foo": {"two": None, "bar": {"three": None, "baz": {"four": None}}}, + } new_dict = salt.utils.data.filter_falsey(old_dict, recurse_depth=2) - self.assertEqual({'foo': {'bar': {'baz': {'four': None}}}}, new_dict) + self.assertEqual({"foo": {"bar": {"baz": {"four": None}}}}, new_dict) def test_filter_exclude_types(self): - ''' + """ Test filtering a list recursively, but also ignoring (i.e. not filtering) out certain types that can be falsey. - ''' + """ # Ignore int, unicode - old_list = ['foo', ['foo'], ['foo', None], {'foo': 0}, {'foo': 'bar', 'baz': []}, [{'foo': ''}]] - new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3, ignore_types=[type(0), type('')]) - self.assertEqual(['foo', ['foo'], ['foo'], {'foo': 0}, {'foo': 'bar'}, [{'foo': ''}]], new_list) + old_list = [ + "foo", + ["foo"], + ["foo", None], + {"foo": 0}, + {"foo": "bar", "baz": []}, + [{"foo": ""}], + ] + new_list = salt.utils.data.filter_falsey( + old_list, recurse_depth=3, ignore_types=[type(0), type("")] + ) + self.assertEqual( + ["foo", ["foo"], ["foo"], {"foo": 0}, {"foo": "bar"}, [{"foo": ""}]], + new_list, + ) # Ignore list - old_list = ['foo', ['foo'], ['foo', None], {'foo': 0}, {'foo': 'bar', 'baz': []}, [{'foo': ''}]] - new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3, ignore_types=[type([])]) - self.assertEqual(['foo', ['foo'], ['foo'], {'foo': 'bar', 'baz': []}, []], new_list) + old_list = [ + "foo", + ["foo"], + ["foo", None], + {"foo": 0}, + {"foo": "bar", "baz": []}, + [{"foo": ""}], + ] + new_list = salt.utils.data.filter_falsey( + old_list, recurse_depth=3, ignore_types=[type([])] + ) + self.assertEqual( + ["foo", ["foo"], ["foo"], {"foo": "bar", "baz": []}, []], new_list + ) # Ignore dict - old_list = ['foo', ['foo'], ['foo', None], {'foo': 0}, {'foo': 'bar', 'baz': []}, [{'foo': ''}]] - new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3, ignore_types=[type({})]) - self.assertEqual(['foo', ['foo'], ['foo'], {}, {'foo': 'bar'}, [{}]], new_list) + old_list = [ + "foo", + ["foo"], + ["foo", None], + {"foo": 0}, + {"foo": "bar", "baz": []}, + [{"foo": ""}], + ] + new_list = salt.utils.data.filter_falsey( + old_list, recurse_depth=3, ignore_types=[type({})] + ) + self.assertEqual(["foo", ["foo"], ["foo"], {}, {"foo": "bar"}, [{}]], new_list) # Ignore NoneType - old_list = ['foo', ['foo'], ['foo', None], {'foo': 0}, {'foo': 'bar', 'baz': []}, [{'foo': ''}]] - new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3, ignore_types=[type(None)]) - self.assertEqual(['foo', ['foo'], ['foo', None], {'foo': 'bar'}], new_list) + old_list = [ + "foo", + ["foo"], + ["foo", None], + {"foo": 0}, + {"foo": "bar", "baz": []}, + [{"foo": ""}], + ] + new_list = salt.utils.data.filter_falsey( + old_list, recurse_depth=3, ignore_types=[type(None)] + ) + self.assertEqual(["foo", ["foo"], ["foo", None], {"foo": "bar"}], new_list) class FilterRecursiveDiff(TestCase): - ''' + """ Test suite for salt.utils.data.recursive_diff - ''' + """ def test_list_equality(self): - ''' + """ Test cases where equal lists are compared. - ''' + """ test_list = [0, 1, 2] self.assertEqual({}, salt.utils.data.recursive_diff(test_list, test_list)) @@ -895,38 +1007,41 @@ class FilterRecursiveDiff(TestCase): self.assertEqual({}, salt.utils.data.recursive_diff(test_list, test_list)) def test_dict_equality(self): - ''' + """ Test cases where equal dicts are compared. - ''' - test_dict = {'foo': 'bar', 'bar': {'baz': {'qux': 'quux'}}, 'frop': 0} + """ + test_dict = {"foo": "bar", "bar": {"baz": {"qux": "quux"}}, "frop": 0} self.assertEqual({}, salt.utils.data.recursive_diff(test_dict, test_dict)) def test_ordereddict_equality(self): - ''' + """ Test cases where equal OrderedDicts are compared. - ''' - test_dict = OrderedDict([ - ('foo', 'bar'), - ('bar', OrderedDict([('baz', OrderedDict([('qux', 'quux')]))])), - ('frop', 0)]) + """ + test_dict = OrderedDict( + [ + ("foo", "bar"), + ("bar", OrderedDict([("baz", OrderedDict([("qux", "quux")]))])), + ("frop", 0), + ] + ) self.assertEqual({}, salt.utils.data.recursive_diff(test_dict, test_dict)) def test_mixed_equality(self): - ''' + """ Test cases where mixed nested lists and dicts are compared. - ''' + """ test_data = { - 'foo': 'bar', - 'baz': [0, 1, 2], - 'bar': {'baz': [{'qux': 'quux'}, {'froop', 0}]} + "foo": "bar", + "baz": [0, 1, 2], + "bar": {"baz": [{"qux": "quux"}, {"froop", 0}]}, } self.assertEqual({}, salt.utils.data.recursive_diff(test_data, test_data)) def test_set_equality(self): - ''' + """ Test cases where equal sets are compared. - ''' - test_set = set([0, 1, 2, 3, 'foo']) + """ + test_set = set([0, 1, 2, 3, "foo"]) self.assertEqual({}, salt.utils.data.recursive_diff(test_set, test_set)) # This is a bit of an oddity, as python seems to sort the sets in memory @@ -936,351 +1051,411 @@ class FilterRecursiveDiff(TestCase): self.assertEqual({}, salt.utils.data.recursive_diff(set_one, set_two)) def test_tuple_equality(self): - ''' + """ Test cases where equal tuples are compared. - ''' - test_tuple = (0, 1, 2, 3, 'foo') + """ + test_tuple = (0, 1, 2, 3, "foo") self.assertEqual({}, salt.utils.data.recursive_diff(test_tuple, test_tuple)) def test_list_inequality(self): - ''' + """ Test cases where two inequal lists are compared. - ''' + """ list_one = [0, 1, 2] - list_two = ['foo', 'bar', 'baz'] - expected_result = {'old': list_one, 'new': list_two} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_one, list_two)) - expected_result = {'new': list_one, 'old': list_two} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_two, list_one)) + list_two = ["foo", "bar", "baz"] + expected_result = {"old": list_one, "new": list_two} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(list_one, list_two) + ) + expected_result = {"new": list_one, "old": list_two} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(list_two, list_one) + ) - list_one = [0, 'foo', 1, 'bar'] - list_two = [1, 'foo', 1, 'qux'] - expected_result = {'old': [0, 'bar'], 'new': [1, 'qux']} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_one, list_two)) - expected_result = {'new': [0, 'bar'], 'old': [1, 'qux']} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_two, list_one)) + list_one = [0, "foo", 1, "bar"] + list_two = [1, "foo", 1, "qux"] + expected_result = {"old": [0, "bar"], "new": [1, "qux"]} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(list_one, list_two) + ) + expected_result = {"new": [0, "bar"], "old": [1, "qux"]} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(list_two, list_one) + ) list_one = [0, 1, [2, 3]] - list_two = [0, 1, ['foo', 'bar']] - expected_result = {'old': [[2, 3]], 'new': [['foo', 'bar']]} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_one, list_two)) - expected_result = {'new': [[2, 3]], 'old': [['foo', 'bar']]} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_two, list_one)) + list_two = [0, 1, ["foo", "bar"]] + expected_result = {"old": [[2, 3]], "new": [["foo", "bar"]]} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(list_one, list_two) + ) + expected_result = {"new": [[2, 3]], "old": [["foo", "bar"]]} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(list_two, list_one) + ) def test_dict_inequality(self): - ''' + """ Test cases where two inequal dicts are compared. - ''' - dict_one = {'foo': 1, 'bar': 2, 'baz': 3} - dict_two = {'foo': 2, 1: 'bar', 'baz': 3} - expected_result = {'old': {'foo': 1, 'bar': 2}, 'new': {'foo': 2, 1: 'bar'}} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_one, dict_two)) - expected_result = {'new': {'foo': 1, 'bar': 2}, 'old': {'foo': 2, 1: 'bar'}} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_two, dict_one)) + """ + dict_one = {"foo": 1, "bar": 2, "baz": 3} + dict_two = {"foo": 2, 1: "bar", "baz": 3} + expected_result = {"old": {"foo": 1, "bar": 2}, "new": {"foo": 2, 1: "bar"}} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(dict_one, dict_two) + ) + expected_result = {"new": {"foo": 1, "bar": 2}, "old": {"foo": 2, 1: "bar"}} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(dict_two, dict_one) + ) - dict_one = {'foo': {'bar': {'baz': 1}}} - dict_two = {'foo': {'qux': {'baz': 1}}} - expected_result = {'old': dict_one, 'new': dict_two} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_one, dict_two)) - expected_result = {'new': dict_one, 'old': dict_two} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_two, dict_one)) + dict_one = {"foo": {"bar": {"baz": 1}}} + dict_two = {"foo": {"qux": {"baz": 1}}} + expected_result = {"old": dict_one, "new": dict_two} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(dict_one, dict_two) + ) + expected_result = {"new": dict_one, "old": dict_two} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(dict_two, dict_one) + ) def test_ordereddict_inequality(self): - ''' + """ Test cases where two inequal OrderedDicts are compared. - ''' - odict_one = OrderedDict([('foo', 'bar'), ('bar', 'baz')]) - odict_two = OrderedDict([('bar', 'baz'), ('foo', 'bar')]) - expected_result = {'old': odict_one, 'new': odict_two} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(odict_one, odict_two)) + """ + odict_one = OrderedDict([("foo", "bar"), ("bar", "baz")]) + odict_two = OrderedDict([("bar", "baz"), ("foo", "bar")]) + expected_result = {"old": odict_one, "new": odict_two} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(odict_one, odict_two) + ) def test_set_inequality(self): - ''' + """ Test cases where two inequal sets are compared. Tricky as the sets are compared zipped, so shuffled sets of equal values are considered different. - ''' + """ set_one = set([0, 1, 2, 4]) set_two = set([0, 1, 3, 4]) - expected_result = {'old': set([2]), 'new': set([3])} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(set_one, set_two)) - expected_result = {'new': set([2]), 'old': set([3])} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(set_two, set_one)) + expected_result = {"old": set([2]), "new": set([3])} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(set_one, set_two) + ) + expected_result = {"new": set([2]), "old": set([3])} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(set_two, set_one) + ) # It is unknown how different python versions will store sets in memory. # Python 2.7 seems to sort it (i.e. set_one below becomes {0, 1, 'foo', 'bar'} # However Python 3.6.8 stores it differently each run. # So just test for "not equal" here. - set_one = set([0, 'foo', 1, 'bar']) - set_two = set(['foo', 1, 'bar', 2]) + set_one = set([0, "foo", 1, "bar"]) + set_two = set(["foo", 1, "bar", 2]) expected_result = {} - self.assertNotEqual(expected_result, salt.utils.data.recursive_diff(set_one, set_two)) + self.assertNotEqual( + expected_result, salt.utils.data.recursive_diff(set_one, set_two) + ) def test_mixed_inequality(self): - ''' + """ Test cases where two mixed dicts/iterables that are different are compared. - ''' - dict_one = {'foo': [1, 2, 3]} - dict_two = {'foo': [3, 2, 1]} - expected_result = {'old': {'foo': [1, 3]}, 'new': {'foo': [3, 1]}} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_one, dict_two)) - expected_result = {'new': {'foo': [1, 3]}, 'old': {'foo': [3, 1]}} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_two, dict_one)) + """ + dict_one = {"foo": [1, 2, 3]} + dict_two = {"foo": [3, 2, 1]} + expected_result = {"old": {"foo": [1, 3]}, "new": {"foo": [3, 1]}} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(dict_one, dict_two) + ) + expected_result = {"new": {"foo": [1, 3]}, "old": {"foo": [3, 1]}} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(dict_two, dict_one) + ) - list_one = [1, 2, {'foo': ['bar', {'foo': 1, 'bar': 2}]}] - list_two = [3, 4, {'foo': ['qux', {'foo': 1, 'bar': 2}]}] - expected_result = {'old': [1, 2, {'foo': ['bar']}], 'new': [3, 4, {'foo': ['qux']}]} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_one, list_two)) - expected_result = {'new': [1, 2, {'foo': ['bar']}], 'old': [3, 4, {'foo': ['qux']}]} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_two, list_one)) + list_one = [1, 2, {"foo": ["bar", {"foo": 1, "bar": 2}]}] + list_two = [3, 4, {"foo": ["qux", {"foo": 1, "bar": 2}]}] + expected_result = { + "old": [1, 2, {"foo": ["bar"]}], + "new": [3, 4, {"foo": ["qux"]}], + } + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(list_one, list_two) + ) + expected_result = { + "new": [1, 2, {"foo": ["bar"]}], + "old": [3, 4, {"foo": ["qux"]}], + } + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(list_two, list_one) + ) - mixed_one = {'foo': set([0, 1, 2]), 'bar': [0, 1, 2]} - mixed_two = {'foo': set([1, 2, 3]), 'bar': [1, 2, 3]} + mixed_one = {"foo": set([0, 1, 2]), "bar": [0, 1, 2]} + mixed_two = {"foo": set([1, 2, 3]), "bar": [1, 2, 3]} expected_result = { - 'old': {'foo': set([0]), 'bar': [0, 1, 2]}, - 'new': {'foo': set([3]), 'bar': [1, 2, 3]} + "old": {"foo": set([0]), "bar": [0, 1, 2]}, + "new": {"foo": set([3]), "bar": [1, 2, 3]}, } - self.assertEqual(expected_result, salt.utils.data.recursive_diff(mixed_one, mixed_two)) + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(mixed_one, mixed_two) + ) expected_result = { - 'new': {'foo': set([0]), 'bar': [0, 1, 2]}, - 'old': {'foo': set([3]), 'bar': [1, 2, 3]} + "new": {"foo": set([0]), "bar": [0, 1, 2]}, + "old": {"foo": set([3]), "bar": [1, 2, 3]}, } - self.assertEqual(expected_result, salt.utils.data.recursive_diff(mixed_two, mixed_one)) + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(mixed_two, mixed_one) + ) def test_tuple_inequality(self): - ''' + """ Test cases where two tuples that are different are compared. - ''' + """ tuple_one = (1, 2, 3) tuple_two = (3, 2, 1) - expected_result = {'old': (1, 3), 'new': (3, 1)} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(tuple_one, tuple_two)) + expected_result = {"old": (1, 3), "new": (3, 1)} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(tuple_one, tuple_two) + ) def test_list_vs_set(self): - ''' + """ Test case comparing a list with a set, will be compared unordered. - ''' + """ mixed_one = [1, 2, 3] mixed_two = set([3, 2, 1]) expected_result = {} - self.assertEqual(expected_result, salt.utils.data.recursive_diff(mixed_one, mixed_two)) - self.assertEqual(expected_result, salt.utils.data.recursive_diff(mixed_two, mixed_one)) + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(mixed_one, mixed_two) + ) + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(mixed_two, mixed_one) + ) def test_dict_vs_ordereddict(self): - ''' + """ Test case comparing a dict with an ordereddict, will be compared unordered. - ''' - test_dict = {'foo': 'bar', 'bar': 'baz'} - test_odict = OrderedDict([('foo', 'bar'), ('bar', 'baz')]) + """ + test_dict = {"foo": "bar", "bar": "baz"} + test_odict = OrderedDict([("foo", "bar"), ("bar", "baz")]) self.assertEqual({}, salt.utils.data.recursive_diff(test_dict, test_odict)) self.assertEqual({}, salt.utils.data.recursive_diff(test_odict, test_dict)) - test_odict2 = OrderedDict([('bar', 'baz'), ('foo', 'bar')]) + test_odict2 = OrderedDict([("bar", "baz"), ("foo", "bar")]) self.assertEqual({}, salt.utils.data.recursive_diff(test_dict, test_odict2)) self.assertEqual({}, salt.utils.data.recursive_diff(test_odict2, test_dict)) def test_list_ignore_ignored(self): - ''' + """ Test case comparing two lists with ignore-list supplied (which is not used when comparing lists). - ''' + """ list_one = [1, 2, 3] list_two = [3, 2, 1] - expected_result = {'old': [1, 3], 'new': [3, 1]} + expected_result = {"old": [1, 3], "new": [3, 1]} self.assertEqual( expected_result, - salt.utils.data.recursive_diff(list_one, list_two, ignore_keys=[1, 3]) + salt.utils.data.recursive_diff(list_one, list_two, ignore_keys=[1, 3]), ) def test_dict_ignore(self): - ''' + """ Test case comparing two dicts with ignore-list supplied. - ''' - dict_one = {'foo': 1, 'bar': 2, 'baz': 3} - dict_two = {'foo': 3, 'bar': 2, 'baz': 1} - expected_result = {'old': {'baz': 3}, 'new': {'baz': 1}} + """ + dict_one = {"foo": 1, "bar": 2, "baz": 3} + dict_two = {"foo": 3, "bar": 2, "baz": 1} + expected_result = {"old": {"baz": 3}, "new": {"baz": 1}} self.assertEqual( expected_result, - salt.utils.data.recursive_diff(dict_one, dict_two, ignore_keys=['foo']) + salt.utils.data.recursive_diff(dict_one, dict_two, ignore_keys=["foo"]), ) def test_ordereddict_ignore(self): - ''' + """ Test case comparing two OrderedDicts with ignore-list supplied. - ''' - odict_one = OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)]) - odict_two = OrderedDict([('baz', 1), ('bar', 2), ('foo', 3)]) + """ + odict_one = OrderedDict([("foo", 1), ("bar", 2), ("baz", 3)]) + odict_two = OrderedDict([("baz", 1), ("bar", 2), ("foo", 3)]) # The key 'foo' will be ignored, which means the key from the other OrderedDict # will always be considered "different" since OrderedDicts are compared ordered. - expected_result = {'old': OrderedDict([('baz', 3)]), 'new': OrderedDict([('baz', 1)])} - self.assertEqual( - expected_result, - salt.utils.data.recursive_diff(odict_one, odict_two, ignore_keys=['foo']) - ) - - def test_dict_vs_ordereddict_ignore(self): - ''' - Test case comparing a dict with an OrderedDict with ignore-list supplied. - ''' - dict_one = {'foo': 1, 'bar': 2, 'baz': 3} - odict_two = OrderedDict([('foo', 3), ('bar', 2), ('baz', 1)]) - expected_result = {'old': {'baz': 3}, 'new': OrderedDict([('baz', 1)])} - self.assertEqual( - expected_result, - salt.utils.data.recursive_diff(dict_one, odict_two, ignore_keys=['foo']) - ) - - def test_mixed_nested_ignore(self): - ''' - Test case comparing mixed, nested items with ignore-list supplied. - ''' - dict_one = {'foo': [1], 'bar': {'foo': 1, 'bar': 2}, 'baz': 3} - dict_two = {'foo': [2], 'bar': {'foo': 3, 'bar': 2}, 'baz': 1} - expected_result = {'old': {'baz': 3}, 'new': {'baz': 1}} - self.assertEqual( - expected_result, - salt.utils.data.recursive_diff(dict_one, dict_two, ignore_keys=['foo']) - ) - - def test_ordered_dict_unequal_length(self): - ''' - Test case comparing two OrderedDicts of unequal length. - ''' - odict_one = OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)]) - odict_two = OrderedDict([('foo', 1), ('bar', 2)]) - expected_result = {'old': OrderedDict([('baz', 3)]), 'new': {}} - self.assertEqual( - expected_result, - salt.utils.data.recursive_diff(odict_one, odict_two) - ) - - def test_list_unequal_length(self): - ''' - Test case comparing two lists of unequal length. - ''' - list_one = [1, 2, 3] - list_two = [1, 2, 3, 4] - expected_result = {'old': [], 'new': [4]} - self.assertEqual( - expected_result, - salt.utils.data.recursive_diff(list_one, list_two) - ) - - def test_set_unequal_length(self): - ''' - Test case comparing two sets of unequal length. - This does not do anything special, as it is unordered. - ''' - set_one = set([1, 2, 3]) - set_two = set([4, 3, 2, 1]) - expected_result = {'old': set([]), 'new': set([4])} - self.assertEqual( - expected_result, - salt.utils.data.recursive_diff(set_one, set_two) - ) - - def test_tuple_unequal_length(self): - ''' - Test case comparing two tuples of unequal length. - This should be the same as comparing two ordered lists. - ''' - tuple_one = (1, 2, 3) - tuple_two = (1, 2, 3, 4) - expected_result = {'old': (), 'new': (4,)} - self.assertEqual( - expected_result, - salt.utils.data.recursive_diff(tuple_one, tuple_two) - ) - - def test_list_unordered(self): - ''' - Test case comparing two lists unordered. - ''' - list_one = [1, 2, 3, 4] - list_two = [4, 3, 2] - expected_result = {'old': [1], 'new': []} - self.assertEqual( - expected_result, - salt.utils.data.recursive_diff(list_one, list_two, ignore_order=True) - ) - - def test_mixed_nested_unordered(self): - ''' - Test case comparing nested dicts/lists unordered. - ''' - dict_one = {'foo': {'bar': [1, 2, 3]}, 'bar': [{'foo': 4}, 0]} - dict_two = {'foo': {'bar': [3, 2, 1]}, 'bar': [0, {'foo': 4}]} - expected_result = {} - self.assertEqual( - expected_result, - salt.utils.data.recursive_diff(dict_one, dict_two, ignore_order=True) - ) expected_result = { - 'old': {'foo': {'bar': [1, 3]}, 'bar': [{'foo': 4}, 0]}, - 'new': {'foo': {'bar': [3, 1]}, 'bar': [0, {'foo': 4}]}, + "old": OrderedDict([("baz", 3)]), + "new": OrderedDict([("baz", 1)]), } self.assertEqual( expected_result, - salt.utils.data.recursive_diff(dict_one, dict_two) + salt.utils.data.recursive_diff(odict_one, odict_two, ignore_keys=["foo"]), + ) + + def test_dict_vs_ordereddict_ignore(self): + """ + Test case comparing a dict with an OrderedDict with ignore-list supplied. + """ + dict_one = {"foo": 1, "bar": 2, "baz": 3} + odict_two = OrderedDict([("foo", 3), ("bar", 2), ("baz", 1)]) + expected_result = {"old": {"baz": 3}, "new": OrderedDict([("baz", 1)])} + self.assertEqual( + expected_result, + salt.utils.data.recursive_diff(dict_one, odict_two, ignore_keys=["foo"]), + ) + + def test_mixed_nested_ignore(self): + """ + Test case comparing mixed, nested items with ignore-list supplied. + """ + dict_one = {"foo": [1], "bar": {"foo": 1, "bar": 2}, "baz": 3} + dict_two = {"foo": [2], "bar": {"foo": 3, "bar": 2}, "baz": 1} + expected_result = {"old": {"baz": 3}, "new": {"baz": 1}} + self.assertEqual( + expected_result, + salt.utils.data.recursive_diff(dict_one, dict_two, ignore_keys=["foo"]), + ) + + def test_ordered_dict_unequal_length(self): + """ + Test case comparing two OrderedDicts of unequal length. + """ + odict_one = OrderedDict([("foo", 1), ("bar", 2), ("baz", 3)]) + odict_two = OrderedDict([("foo", 1), ("bar", 2)]) + expected_result = {"old": OrderedDict([("baz", 3)]), "new": {}} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(odict_one, odict_two) + ) + + def test_list_unequal_length(self): + """ + Test case comparing two lists of unequal length. + """ + list_one = [1, 2, 3] + list_two = [1, 2, 3, 4] + expected_result = {"old": [], "new": [4]} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(list_one, list_two) + ) + + def test_set_unequal_length(self): + """ + Test case comparing two sets of unequal length. + This does not do anything special, as it is unordered. + """ + set_one = set([1, 2, 3]) + set_two = set([4, 3, 2, 1]) + expected_result = {"old": set([]), "new": set([4])} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(set_one, set_two) + ) + + def test_tuple_unequal_length(self): + """ + Test case comparing two tuples of unequal length. + This should be the same as comparing two ordered lists. + """ + tuple_one = (1, 2, 3) + tuple_two = (1, 2, 3, 4) + expected_result = {"old": (), "new": (4,)} + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(tuple_one, tuple_two) + ) + + def test_list_unordered(self): + """ + Test case comparing two lists unordered. + """ + list_one = [1, 2, 3, 4] + list_two = [4, 3, 2] + expected_result = {"old": [1], "new": []} + self.assertEqual( + expected_result, + salt.utils.data.recursive_diff(list_one, list_two, ignore_order=True), + ) + + def test_mixed_nested_unordered(self): + """ + Test case comparing nested dicts/lists unordered. + """ + dict_one = {"foo": {"bar": [1, 2, 3]}, "bar": [{"foo": 4}, 0]} + dict_two = {"foo": {"bar": [3, 2, 1]}, "bar": [0, {"foo": 4}]} + expected_result = {} + self.assertEqual( + expected_result, + salt.utils.data.recursive_diff(dict_one, dict_two, ignore_order=True), + ) + expected_result = { + "old": {"foo": {"bar": [1, 3]}, "bar": [{"foo": 4}, 0]}, + "new": {"foo": {"bar": [3, 1]}, "bar": [0, {"foo": 4}]}, + } + self.assertEqual( + expected_result, salt.utils.data.recursive_diff(dict_one, dict_two) ) def test_ordered_dict_unordered(self): - ''' + """ Test case comparing OrderedDicts unordered. - ''' - odict_one = OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)]) - odict_two = OrderedDict([('baz', 3), ('bar', 2), ('foo', 1)]) + """ + odict_one = OrderedDict([("foo", 1), ("bar", 2), ("baz", 3)]) + odict_two = OrderedDict([("baz", 3), ("bar", 2), ("foo", 1)]) expected_result = {} self.assertEqual( expected_result, - salt.utils.data.recursive_diff(odict_one, odict_two, ignore_order=True) + salt.utils.data.recursive_diff(odict_one, odict_two, ignore_order=True), ) def test_ignore_missing_keys_dict(self): - ''' + """ Test case ignoring missing keys on a comparison of dicts. - ''' - dict_one = {'foo': 1, 'bar': 2, 'baz': 3} - dict_two = {'bar': 3} - expected_result = {'old': {'bar': 2}, 'new': {'bar': 3}} + """ + dict_one = {"foo": 1, "bar": 2, "baz": 3} + dict_two = {"bar": 3} + expected_result = {"old": {"bar": 2}, "new": {"bar": 3}} self.assertEqual( expected_result, - salt.utils.data.recursive_diff(dict_one, dict_two, ignore_missing_keys=True) + salt.utils.data.recursive_diff( + dict_one, dict_two, ignore_missing_keys=True + ), ) def test_ignore_missing_keys_ordered_dict(self): - ''' + """ Test case not ignoring missing keys on a comparison of OrderedDicts. - ''' - odict_one = OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)]) - odict_two = OrderedDict([('bar', 3)]) - expected_result = {'old': odict_one, 'new': odict_two} + """ + odict_one = OrderedDict([("foo", 1), ("bar", 2), ("baz", 3)]) + odict_two = OrderedDict([("bar", 3)]) + expected_result = {"old": odict_one, "new": odict_two} self.assertEqual( expected_result, - salt.utils.data.recursive_diff(odict_one, odict_two, ignore_missing_keys=True) + salt.utils.data.recursive_diff( + odict_one, odict_two, ignore_missing_keys=True + ), ) def test_ignore_missing_keys_recursive(self): - ''' + """ Test case ignoring missing keys on a comparison of nested dicts. - ''' - dict_one = {'foo': {'bar': 2, 'baz': 3}} - dict_two = {'foo': {'baz': 3}} + """ + dict_one = {"foo": {"bar": 2, "baz": 3}} + dict_two = {"foo": {"baz": 3}} expected_result = {} self.assertEqual( expected_result, - salt.utils.data.recursive_diff(dict_one, dict_two, ignore_missing_keys=True) + salt.utils.data.recursive_diff( + dict_one, dict_two, ignore_missing_keys=True + ), ) # Compare from dict-in-dict dict_two = {} self.assertEqual( expected_result, - salt.utils.data.recursive_diff(dict_one, dict_two, ignore_missing_keys=True) + salt.utils.data.recursive_diff( + dict_one, dict_two, ignore_missing_keys=True + ), ) # Compare from dict-in-list - dict_one = {'foo': ['bar', {'baz': 3}]} - dict_two = {'foo': ['bar', {}]} + dict_one = {"foo": ["bar", {"baz": 3}]} + dict_two = {"foo": ["bar", {}]} self.assertEqual( expected_result, - salt.utils.data.recursive_diff(dict_one, dict_two, ignore_missing_keys=True) + salt.utils.data.recursive_diff( + dict_one, dict_two, ignore_missing_keys=True + ), ) diff --git a/tests/unit/utils/test_dateutils.py b/tests/unit/utils/test_dateutils.py index af23927ceaa..4536f046398 100644 --- a/tests/unit/utils/test_dateutils.py +++ b/tests/unit/utils/test_dateutils.py @@ -1,60 +1,59 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.utils.dateutils -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import datetime -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, -) +import datetime # Import Salt libs import salt.utils.dateutils +from tests.support.mock import patch + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf # Import 3rd-party libs try: import timelib # pylint: disable=import-error,unused-import + HAS_TIMELIB = True except ImportError: HAS_TIMELIB = False class DateutilsTestCase(TestCase): - def test_date_cast(self): now = datetime.datetime.now() - with patch('datetime.datetime'): + with patch("datetime.datetime"): datetime.datetime.now.return_value = now self.assertEqual(now, salt.utils.dateutils.date_cast(None)) self.assertEqual(now, salt.utils.dateutils.date_cast(now)) try: - ret = salt.utils.dateutils.date_cast('Mon Dec 23 10:19:15 MST 2013') + ret = salt.utils.dateutils.date_cast("Mon Dec 23 10:19:15 MST 2013") expected_ret = datetime.datetime(2013, 12, 23, 10, 19, 15) self.assertEqual(ret, expected_ret) except RuntimeError: if not HAS_TIMELIB: # Unparseable without timelib installed - self.skipTest('\'timelib\' is not installed') + self.skipTest("'timelib' is not installed") else: raise - @skipIf(not HAS_TIMELIB, '\'timelib\' is not installed') + @skipIf(not HAS_TIMELIB, "'timelib' is not installed") def test_strftime(self): # Taken from doctests - expected_ret = '2002-12-25' + expected_ret = "2002-12-25" src = datetime.datetime(2002, 12, 25, 12, 00, 00, 00) ret = salt.utils.dateutils.strftime(src) self.assertEqual(ret, expected_ret) - src = '2002/12/25' + src = "2002/12/25" ret = salt.utils.dateutils.strftime(src) self.assertEqual(ret, expected_ret) @@ -62,6 +61,6 @@ class DateutilsTestCase(TestCase): ret = salt.utils.dateutils.strftime(src) self.assertEqual(ret, expected_ret) - src = '1040814000' + src = "1040814000" ret = salt.utils.dateutils.strftime(src) self.assertEqual(ret, expected_ret) diff --git a/tests/unit/utils/test_decorators.py b/tests/unit/utils/test_decorators.py index 9a0f20c8d9a..7091a636372 100644 --- a/tests/unit/utils/test_decorators.py +++ b/tests/unit/utils/test_decorators.py @@ -1,24 +1,25 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Bo Maryniuk (bo@suse.de) unit.utils.decorators_test -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.decorators as decorators -from salt.version import SaltStackVersion from salt.exceptions import CommandExecutionError, SaltConfigurationError -from tests.support.unit import TestCase +from salt.version import SaltStackVersion from tests.support.mock import patch +from tests.support.unit import TestCase class DummyLogger(object): - ''' + """ Dummy logger accepts everything and simply logs - ''' + """ + def __init__(self, messages): self._messages = messages @@ -30,9 +31,10 @@ class DummyLogger(object): class DecoratorsTest(TestCase): - ''' + """ Testing decorators. - ''' + """ + def old_function(self): return "old" @@ -43,310 +45,367 @@ class DecoratorsTest(TestCase): return "old" def _mk_version(self, name): - ''' + """ Make a version :return: - ''' + """ return name, SaltStackVersion.from_name(name) def setUp(self): - ''' + """ Setup a test :return: - ''' + """ self.globs = { - '__virtualname__': 'test', - '__opts__': {}, - '__pillar__': {}, - 'old_function': self.old_function, - 'new_function': self.new_function, - '_new_function': self._new_function, + "__virtualname__": "test", + "__opts__": {}, + "__pillar__": {}, + "old_function": self.old_function, + "new_function": self.new_function, + "_new_function": self._new_function, } - self.addCleanup(delattr, self, 'globs') + self.addCleanup(delattr, self, "globs") self.messages = list() - self.addCleanup(delattr, self, 'messages') - patcher = patch.object(decorators, 'log', DummyLogger(self.messages)) + self.addCleanup(delattr, self, "messages") + patcher = patch.object(decorators, "log", DummyLogger(self.messages)) patcher.start() self.addCleanup(patcher.stop) def test_is_deprecated_version_eol(self): - ''' + """ Use of is_deprecated will result to the exception, if the expiration version is lower than the current version. A successor function is not pointed out. :return: - ''' + """ depr = decorators.is_deprecated(self.globs, "Helium") depr._curr_version = self._mk_version("Beryllium")[1] with self.assertRaises(CommandExecutionError): depr(self.old_function)() - self.assertEqual(self.messages, - ['The lifetime of the function "old_function" expired.']) + self.assertEqual( + self.messages, ['The lifetime of the function "old_function" expired.'] + ) def test_is_deprecated_with_successor_eol(self): - ''' + """ Use of is_deprecated will result to the exception, if the expiration version is lower than the current version. A successor function is pointed out. :return: - ''' - depr = decorators.is_deprecated(self.globs, "Helium", with_successor="new_function") + """ + depr = decorators.is_deprecated( + self.globs, "Helium", with_successor="new_function" + ) depr._curr_version = self._mk_version("Beryllium")[1] with self.assertRaises(CommandExecutionError): depr(self.old_function)() - self.assertEqual(self.messages, - ['The lifetime of the function "old_function" expired. ' - 'Please use its successor "new_function" instead.']) + self.assertEqual( + self.messages, + [ + 'The lifetime of the function "old_function" expired. ' + 'Please use its successor "new_function" instead.' + ], + ) def test_is_deprecated(self): - ''' + """ Use of is_deprecated will result to the log message, if the expiration version is higher than the current version. A successor function is not pointed out. :return: - ''' + """ depr = decorators.is_deprecated(self.globs, "Beryllium") depr._curr_version = self._mk_version("Helium")[1] self.assertEqual(depr(self.old_function)(), self.old_function()) - self.assertEqual(self.messages, - ['The function "old_function" is deprecated ' - 'and will expire in version "Beryllium".']) + self.assertEqual( + self.messages, + [ + 'The function "old_function" is deprecated ' + 'and will expire in version "Beryllium".' + ], + ) def test_is_deprecated_with_successor(self): - ''' + """ Use of is_deprecated will result to the log message, if the expiration version is higher than the current version. A successor function is pointed out. :return: - ''' - depr = decorators.is_deprecated(self.globs, "Beryllium", with_successor="old_function") + """ + depr = decorators.is_deprecated( + self.globs, "Beryllium", with_successor="old_function" + ) depr._curr_version = self._mk_version("Helium")[1] self.assertEqual(depr(self.old_function)(), self.old_function()) - self.assertEqual(self.messages, - ['The function "old_function" is deprecated ' - 'and will expire in version "Beryllium". ' - 'Use successor "old_function" instead.']) + self.assertEqual( + self.messages, + [ + 'The function "old_function" is deprecated ' + 'and will expire in version "Beryllium". ' + 'Use successor "old_function" instead.' + ], + ) def test_with_deprecated_notfound(self): - ''' + """ Test with_deprecated should raise an exception, if a same name function with the "_" prefix not implemented. :return: - ''' - del self.globs['_new_function'] - self.globs['__opts__']['use_deprecated'] = ['test.new_function'] + """ + del self.globs["_new_function"] + self.globs["__opts__"]["use_deprecated"] = ["test.new_function"] depr = decorators.with_deprecated(self.globs, "Beryllium") depr._curr_version = self._mk_version("Helium")[1] with self.assertRaises(CommandExecutionError): depr(self.new_function)() - self.assertEqual(self.messages, - ['The function "test.new_function" is using its deprecated ' - 'version and will expire in version "Beryllium".']) + self.assertEqual( + self.messages, + [ + 'The function "test.new_function" is using its deprecated ' + 'version and will expire in version "Beryllium".' + ], + ) def test_with_deprecated_notfound_in_pillar(self): - ''' + """ Test with_deprecated should raise an exception, if a same name function with the "_" prefix not implemented. :return: - ''' - del self.globs['_new_function'] - self.globs['__pillar__']['use_deprecated'] = ['test.new_function'] + """ + del self.globs["_new_function"] + self.globs["__pillar__"]["use_deprecated"] = ["test.new_function"] depr = decorators.with_deprecated(self.globs, "Beryllium") depr._curr_version = self._mk_version("Helium")[1] with self.assertRaises(CommandExecutionError): depr(self.new_function)() - self.assertEqual(self.messages, - ['The function "test.new_function" is using its deprecated ' - 'version and will expire in version "Beryllium".']) + self.assertEqual( + self.messages, + [ + 'The function "test.new_function" is using its deprecated ' + 'version and will expire in version "Beryllium".' + ], + ) def test_with_deprecated_found(self): - ''' + """ Test with_deprecated should not raise an exception, if a same name function with the "_" prefix is implemented, but should use an old version instead, if "use_deprecated" is requested. :return: - ''' - self.globs['__opts__']['use_deprecated'] = ['test.new_function'] - self.globs['_new_function'] = self.old_function + """ + self.globs["__opts__"]["use_deprecated"] = ["test.new_function"] + self.globs["_new_function"] = self.old_function depr = decorators.with_deprecated(self.globs, "Beryllium") depr._curr_version = self._mk_version("Helium")[1] self.assertEqual(depr(self.new_function)(), self.old_function()) - log_msg = ['The function "test.new_function" is using its deprecated version ' - 'and will expire in version "Beryllium".'] + log_msg = [ + 'The function "test.new_function" is using its deprecated version ' + 'and will expire in version "Beryllium".' + ] self.assertEqual(self.messages, log_msg) def test_with_deprecated_found_in_pillar(self): - ''' + """ Test with_deprecated should not raise an exception, if a same name function with the "_" prefix is implemented, but should use an old version instead, if "use_deprecated" is requested. :return: - ''' - self.globs['__pillar__']['use_deprecated'] = ['test.new_function'] - self.globs['_new_function'] = self.old_function + """ + self.globs["__pillar__"]["use_deprecated"] = ["test.new_function"] + self.globs["_new_function"] = self.old_function depr = decorators.with_deprecated(self.globs, "Beryllium") depr._curr_version = self._mk_version("Helium")[1] self.assertEqual(depr(self.new_function)(), self.old_function()) - log_msg = ['The function "test.new_function" is using its deprecated version ' - 'and will expire in version "Beryllium".'] + log_msg = [ + 'The function "test.new_function" is using its deprecated version ' + 'and will expire in version "Beryllium".' + ] self.assertEqual(self.messages, log_msg) def test_with_deprecated_found_eol(self): - ''' + """ Test with_deprecated should raise an exception, if a same name function with the "_" prefix is implemented, "use_deprecated" is requested and EOL is reached. :return: - ''' - self.globs['__opts__']['use_deprecated'] = ['test.new_function'] - self.globs['_new_function'] = self.old_function + """ + self.globs["__opts__"]["use_deprecated"] = ["test.new_function"] + self.globs["_new_function"] = self.old_function depr = decorators.with_deprecated(self.globs, "Helium") depr._curr_version = self._mk_version("Beryllium")[1] with self.assertRaises(CommandExecutionError): depr(self.new_function)() - self.assertEqual(self.messages, - ['Although function "new_function" is called, an alias "new_function" ' - 'is configured as its deprecated version. The lifetime of the function ' - '"new_function" expired. Please use its successor "new_function" instead.']) + self.assertEqual( + self.messages, + [ + 'Although function "new_function" is called, an alias "new_function" ' + "is configured as its deprecated version. The lifetime of the function " + '"new_function" expired. Please use its successor "new_function" instead.' + ], + ) def test_with_deprecated_found_eol_in_pillar(self): - ''' + """ Test with_deprecated should raise an exception, if a same name function with the "_" prefix is implemented, "use_deprecated" is requested and EOL is reached. :return: - ''' - self.globs['__pillar__']['use_deprecated'] = ['test.new_function'] - self.globs['_new_function'] = self.old_function + """ + self.globs["__pillar__"]["use_deprecated"] = ["test.new_function"] + self.globs["_new_function"] = self.old_function depr = decorators.with_deprecated(self.globs, "Helium") depr._curr_version = self._mk_version("Beryllium")[1] with self.assertRaises(CommandExecutionError): depr(self.new_function)() - self.assertEqual(self.messages, - ['Although function "new_function" is called, an alias "new_function" ' - 'is configured as its deprecated version. The lifetime of the function ' - '"new_function" expired. Please use its successor "new_function" instead.']) + self.assertEqual( + self.messages, + [ + 'Although function "new_function" is called, an alias "new_function" ' + "is configured as its deprecated version. The lifetime of the function " + '"new_function" expired. Please use its successor "new_function" instead.' + ], + ) def test_with_deprecated_no_conf(self): - ''' + """ Test with_deprecated should not raise an exception, if a same name function with the "_" prefix is implemented, but should use a new version instead, if "use_deprecated" is not requested. :return: - ''' - self.globs['_new_function'] = self.old_function + """ + self.globs["_new_function"] = self.old_function depr = decorators.with_deprecated(self.globs, "Beryllium") depr._curr_version = self._mk_version("Helium")[1] self.assertEqual(depr(self.new_function)(), self.new_function()) self.assertFalse(self.messages) def test_with_deprecated_with_name(self): - ''' + """ Test with_deprecated should not raise an exception, if a different name function is implemented and specified with the "with_name" parameter, but should use an old version instead and log a warning log message. :return: - ''' - self.globs['__opts__']['use_deprecated'] = ['test.new_function'] - depr = decorators.with_deprecated(self.globs, "Beryllium", with_name="old_function") + """ + self.globs["__opts__"]["use_deprecated"] = ["test.new_function"] + depr = decorators.with_deprecated( + self.globs, "Beryllium", with_name="old_function" + ) depr._curr_version = self._mk_version("Helium")[1] self.assertEqual(depr(self.new_function)(), self.old_function()) - self.assertEqual(self.messages, - ['The function "old_function" is deprecated and will expire in version "Beryllium". ' - 'Use its successor "new_function" instead.']) + self.assertEqual( + self.messages, + [ + 'The function "old_function" is deprecated and will expire in version "Beryllium". ' + 'Use its successor "new_function" instead.' + ], + ) def test_with_deprecated_with_name_eol(self): - ''' + """ Test with_deprecated should raise an exception, if a different name function is implemented and specified with the "with_name" parameter and EOL is reached. :return: - ''' - self.globs['__opts__']['use_deprecated'] = ['test.new_function'] - depr = decorators.with_deprecated(self.globs, "Helium", with_name="old_function") + """ + self.globs["__opts__"]["use_deprecated"] = ["test.new_function"] + depr = decorators.with_deprecated( + self.globs, "Helium", with_name="old_function" + ) depr._curr_version = self._mk_version("Beryllium")[1] with self.assertRaises(CommandExecutionError): depr(self.new_function)() - self.assertEqual(self.messages, - ['Although function "new_function" is called, ' - 'an alias "old_function" is configured as its deprecated version. ' - 'The lifetime of the function "old_function" expired. ' - 'Please use its successor "new_function" instead.']) + self.assertEqual( + self.messages, + [ + 'Although function "new_function" is called, ' + 'an alias "old_function" is configured as its deprecated version. ' + 'The lifetime of the function "old_function" expired. ' + 'Please use its successor "new_function" instead.' + ], + ) def test_with_deprecated_opt_in_default(self): - ''' + """ Test with_deprecated using opt-in policy, where newer function is not used, unless configured. :return: - ''' - depr = decorators.with_deprecated(self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN) + """ + depr = decorators.with_deprecated( + self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN + ) depr._curr_version = self._mk_version("Helium")[1] assert depr(self.new_function)() == self.old_function() - assert self.messages == ['The function "test.new_function" is using its ' - 'deprecated version and will expire in version "Beryllium".'] + assert self.messages == [ + 'The function "test.new_function" is using its ' + 'deprecated version and will expire in version "Beryllium".' + ] def test_with_deprecated_opt_in_use_superseded(self): - ''' + """ Test with_deprecated using opt-in policy, where newer function is used as per configuration. :return: - ''' - self.globs['__opts__']['use_superseded'] = ['test.new_function'] - depr = decorators.with_deprecated(self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN) + """ + self.globs["__opts__"]["use_superseded"] = ["test.new_function"] + depr = decorators.with_deprecated( + self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN + ) depr._curr_version = self._mk_version("Helium")[1] assert depr(self.new_function)() == self.new_function() assert not self.messages def test_with_deprecated_opt_in_use_superseded_in_pillar(self): - ''' + """ Test with_deprecated using opt-in policy, where newer function is used as per configuration. :return: - ''' - self.globs['__pillar__']['use_superseded'] = ['test.new_function'] - depr = decorators.with_deprecated(self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN) + """ + self.globs["__pillar__"]["use_superseded"] = ["test.new_function"] + depr = decorators.with_deprecated( + self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN + ) depr._curr_version = self._mk_version("Helium")[1] assert depr(self.new_function)() == self.new_function() assert not self.messages def test_with_deprecated_opt_in_use_superseded_and_deprecated(self): - ''' + """ Test with_deprecated misconfiguration. :return: - ''' - self.globs['__opts__']['use_deprecated'] = ['test.new_function'] - self.globs['__opts__']['use_superseded'] = ['test.new_function'] + """ + self.globs["__opts__"]["use_deprecated"] = ["test.new_function"] + self.globs["__opts__"]["use_superseded"] = ["test.new_function"] depr = decorators.with_deprecated(self.globs, "Beryllium") depr._curr_version = self._mk_version("Helium")[1] with self.assertRaises(SaltConfigurationError): assert depr(self.new_function)() == self.new_function() def test_with_deprecated_opt_in_use_superseded_and_deprecated_in_pillar(self): - ''' + """ Test with_deprecated misconfiguration. :return: - ''' - self.globs['__pillar__']['use_deprecated'] = ['test.new_function'] - self.globs['__pillar__']['use_superseded'] = ['test.new_function'] + """ + self.globs["__pillar__"]["use_deprecated"] = ["test.new_function"] + self.globs["__pillar__"]["use_superseded"] = ["test.new_function"] depr = decorators.with_deprecated(self.globs, "Beryllium") depr._curr_version = self._mk_version("Helium")[1] with self.assertRaises(SaltConfigurationError): @@ -365,7 +424,7 @@ class DecoratorsTest(TestCase): assert wrapped.__module__ == self.old_function.__module__ def test_ignores_kwargs_should_wrap_function(self): - wrapped = decorators.ignores_kwargs('foo', 'bar')(self.old_function) + wrapped = decorators.ignores_kwargs("foo", "bar")(self.old_function) assert wrapped.__module__ == self.old_function.__module__ def test_memoize_should_wrap_function(self): diff --git a/tests/unit/utils/test_dictdiffer.py b/tests/unit/utils/test_dictdiffer.py index 9c29e8aec62..689ae4e4828 100644 --- a/tests/unit/utils/test_dictdiffer.py +++ b/tests/unit/utils/test_dictdiffer.py @@ -3,93 +3,113 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase - # Import Salt libs import salt.utils.dictdiffer as dictdiffer +# Import Salt Testing libs +from tests.support.unit import TestCase NONE = dictdiffer.RecursiveDictDiffer.NONE_VALUE class RecursiveDictDifferTestCase(TestCase): - def setUp(self): - old_dict = {'a': {'b': 1, 'c': 2, 'e': 'old_value', - 'f': 'old_key'}, - 'j': 'value'} - new_dict = {'a': {'b': 1, 'c': 4, 'e': 'new_value', - 'g': 'new_key'}, - 'h': 'new_key', 'i': None, - 'j': 'value'} - self.recursive_diff = \ - dictdiffer.recursive_diff(old_dict, new_dict, - ignore_missing_keys=False) + old_dict = { + "a": {"b": 1, "c": 2, "e": "old_value", "f": "old_key"}, + "j": "value", + } + new_dict = { + "a": {"b": 1, "c": 4, "e": "new_value", "g": "new_key"}, + "h": "new_key", + "i": None, + "j": "value", + } + self.recursive_diff = dictdiffer.recursive_diff( + old_dict, new_dict, ignore_missing_keys=False + ) self.recursive_diff_ign = dictdiffer.recursive_diff(old_dict, new_dict) def tearDown(self): - for attrname in ('recursive_diff', 'recursive_diff_missing_keys'): + for attrname in ("recursive_diff", "recursive_diff_missing_keys"): try: delattr(self, attrname) except AttributeError: continue def test_added(self): - self.assertEqual(self.recursive_diff.added(), ['a.g', 'h', 'i']) + self.assertEqual(self.recursive_diff.added(), ["a.g", "h", "i"]) def test_removed(self): - self.assertEqual(self.recursive_diff.removed(), ['a.f']) + self.assertEqual(self.recursive_diff.removed(), ["a.f"]) def test_changed_with_ignore_unset_values(self): self.recursive_diff.ignore_unset_values = True - self.assertEqual(self.recursive_diff.changed(), - ['a.c', 'a.e']) + self.assertEqual(self.recursive_diff.changed(), ["a.c", "a.e"]) def test_changed_without_ignore_unset_values(self): self.recursive_diff.ignore_unset_values = False - self.assertEqual(self.recursive_diff.changed(), - ['a.c', 'a.e', 'a.f', 'a.g', 'h', 'i']) + self.assertEqual( + self.recursive_diff.changed(), ["a.c", "a.e", "a.f", "a.g", "h", "i"] + ) def test_unchanged(self): - self.assertEqual(self.recursive_diff.unchanged(), - ['a.b', 'j']) + self.assertEqual(self.recursive_diff.unchanged(), ["a.b", "j"]) def test_diffs(self): - self.assertDictEqual(self.recursive_diff.diffs, - {'a': {'c': {'old': 2, 'new': 4}, - 'e': {'old': 'old_value', - 'new': 'new_value'}, - 'f': {'old': 'old_key', 'new': NONE}, - 'g': {'old': NONE, 'new': 'new_key'}}, - 'h': {'old': NONE, 'new': 'new_key'}, - 'i': {'old': NONE, 'new': None}}) - self.assertDictEqual(self.recursive_diff_ign.diffs, - {'a': {'c': {'old': 2, 'new': 4}, - 'e': {'old': 'old_value', - 'new': 'new_value'}, - 'g': {'old': NONE, 'new': 'new_key'}}, - 'h': {'old': NONE, 'new': 'new_key'}, - 'i': {'old': NONE, 'new': None}}) + self.assertDictEqual( + self.recursive_diff.diffs, + { + "a": { + "c": {"old": 2, "new": 4}, + "e": {"old": "old_value", "new": "new_value"}, + "f": {"old": "old_key", "new": NONE}, + "g": {"old": NONE, "new": "new_key"}, + }, + "h": {"old": NONE, "new": "new_key"}, + "i": {"old": NONE, "new": None}, + }, + ) + self.assertDictEqual( + self.recursive_diff_ign.diffs, + { + "a": { + "c": {"old": 2, "new": 4}, + "e": {"old": "old_value", "new": "new_value"}, + "g": {"old": NONE, "new": "new_key"}, + }, + "h": {"old": NONE, "new": "new_key"}, + "i": {"old": NONE, "new": None}, + }, + ) def test_new_values(self): - self.assertDictEqual(self.recursive_diff.new_values, - {'a': {'c': 4, 'e': 'new_value', - 'f': NONE, 'g': 'new_key'}, - 'h': 'new_key', 'i': None}) + self.assertDictEqual( + self.recursive_diff.new_values, + { + "a": {"c": 4, "e": "new_value", "f": NONE, "g": "new_key"}, + "h": "new_key", + "i": None, + }, + ) def test_old_values(self): - self.assertDictEqual(self.recursive_diff.old_values, - {'a': {'c': 2, 'e': 'old_value', - 'f': 'old_key', 'g': NONE}, - 'h': NONE, 'i': NONE}) + self.assertDictEqual( + self.recursive_diff.old_values, + { + "a": {"c": 2, "e": "old_value", "f": "old_key", "g": NONE}, + "h": NONE, + "i": NONE, + }, + ) def test_changes_str(self): - self.assertEqual(self.recursive_diff.changes_str, - 'a:\n' - ' c from 2 to 4\n' - ' e from \'old_value\' to \'new_value\'\n' - ' f from \'old_key\' to nothing\n' - ' g from nothing to \'new_key\'\n' - 'h from nothing to \'new_key\'\n' - 'i from nothing to None') + self.assertEqual( + self.recursive_diff.changes_str, + "a:\n" + " c from 2 to 4\n" + " e from 'old_value' to 'new_value'\n" + " f from 'old_key' to nothing\n" + " g from nothing to 'new_key'\n" + "h from nothing to 'new_key'\n" + "i from nothing to None", + ) diff --git a/tests/unit/utils/test_dicttrim.py b/tests/unit/utils/test_dicttrim.py index 3c21d0da4b9..573aeaed207 100644 --- a/tests/unit/utils/test_dicttrim.py +++ b/tests/unit/utils/test_dicttrim.py @@ -3,21 +3,21 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase +import logging # Import Salt libs import salt.utils.dicttrim as dicttrimmer -import logging +# Import Salt Testing libs +from tests.support.unit import TestCase + log = logging.getLogger(__name__) class DictTrimTestCase(TestCase): - def setUp(self): - self.old_dict = {'a': 'b', 'c': 'x' * 10000} - self.new_dict = {'a': 'b', 'c': 'VALUE_TRIMMED'} + self.old_dict = {"a": "b", "c": "x" * 10000} + self.new_dict = {"a": "b", "c": "VALUE_TRIMMED"} def test_trim_dict(self): ret = dicttrimmer.trim_dict(self.old_dict, 1000) @@ -25,10 +25,9 @@ class DictTrimTestCase(TestCase): class RecursiveDictTrimTestCase(TestCase): - def setUp(self): - self.old_dict = {'a': {'b': 1, 'c': 2, 'e': 'x' * 10000, 'f': '3'}} - self.new_dict = {'a': {'b': 1, 'c': 2, 'e': 'VALUE_TRIMMED', 'f': '3'}} + self.old_dict = {"a": {"b": 1, "c": 2, "e": "x" * 10000, "f": "3"}} + self.new_dict = {"a": {"b": 1, "c": 2, "e": "VALUE_TRIMMED", "f": "3"}} def test_trim_dict(self): ret = dicttrimmer.trim_dict(self.old_dict, 1000) diff --git a/tests/unit/utils/test_dictupdate.py b/tests/unit/utils/test_dictupdate.py index 222f8fe321f..70f3099ceff 100644 --- a/tests/unit/utils/test_dictupdate.py +++ b/tests/unit/utils/test_dictupdate.py @@ -2,325 +2,352 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy +# Import Salt libs +import salt.utils.dictupdate as dictupdate +from salt.exceptions import SaltInvocationError +from salt.utils.odict import OrderedDict + # Import Salt Testing libs from tests.support.unit import TestCase -# Import Salt libs -import salt.utils.dictupdate as dictupdate -from salt.utils.odict import OrderedDict -from salt.exceptions import SaltInvocationError - class UtilDictupdateTestCase(TestCase): - dict1 = {'A': 'B', 'C': {'D': 'E', 'F': {'G': 'H', 'I': 'J'}}} + dict1 = {"A": "B", "C": {"D": "E", "F": {"G": "H", "I": "J"}}} def test_update(self): # level 1 value changes mdict = copy.deepcopy(self.dict1) - mdict['A'] = 'Z' - res = dictupdate.update(copy.deepcopy(self.dict1), {'A': 'Z'}) + mdict["A"] = "Z" + res = dictupdate.update(copy.deepcopy(self.dict1), {"A": "Z"}) self.assertEqual(res, mdict) # level 1 value changes (list replacement) mdict = copy.deepcopy(self.dict1) - mdict['A'] = [1, 2] - res = dictupdate.update(copy.deepcopy(mdict), {'A': [2, 3]}, - merge_lists=False) - mdict['A'] = [2, 3] + mdict["A"] = [1, 2] + res = dictupdate.update(copy.deepcopy(mdict), {"A": [2, 3]}, merge_lists=False) + mdict["A"] = [2, 3] self.assertEqual(res, mdict) # level 1 value changes (list merge) mdict = copy.deepcopy(self.dict1) - mdict['A'] = [1, 2] - res = dictupdate.update(copy.deepcopy(mdict), {'A': [3, 4]}, - merge_lists=True) - mdict['A'] = [1, 2, 3, 4] + mdict["A"] = [1, 2] + res = dictupdate.update(copy.deepcopy(mdict), {"A": [3, 4]}, merge_lists=True) + mdict["A"] = [1, 2, 3, 4] self.assertEqual(res, mdict) # level 1 value changes (list merge, remove duplicates, preserve order) mdict = copy.deepcopy(self.dict1) - mdict['A'] = [1, 2] - res = dictupdate.update(copy.deepcopy(mdict), {'A': [4, 3, 2, 1]}, - merge_lists=True) - mdict['A'] = [1, 2, 4, 3] + mdict["A"] = [1, 2] + res = dictupdate.update( + copy.deepcopy(mdict), {"A": [4, 3, 2, 1]}, merge_lists=True + ) + mdict["A"] = [1, 2, 4, 3] self.assertEqual(res, mdict) # level 2 value changes mdict = copy.deepcopy(self.dict1) - mdict['C']['D'] = 'Z' - res = dictupdate.update(copy.deepcopy(self.dict1), {'C': {'D': 'Z'}}) + mdict["C"]["D"] = "Z" + res = dictupdate.update(copy.deepcopy(self.dict1), {"C": {"D": "Z"}}) self.assertEqual(res, mdict) # level 2 value changes (list replacement) mdict = copy.deepcopy(self.dict1) - mdict['C']['D'] = ['a', 'b'] - res = dictupdate.update(copy.deepcopy(mdict), {'C': {'D': ['c', 'd']}}, - merge_lists=False) - mdict['C']['D'] = ['c', 'd'] + mdict["C"]["D"] = ["a", "b"] + res = dictupdate.update( + copy.deepcopy(mdict), {"C": {"D": ["c", "d"]}}, merge_lists=False + ) + mdict["C"]["D"] = ["c", "d"] self.assertEqual(res, mdict) # level 2 value changes (list merge) mdict = copy.deepcopy(self.dict1) - mdict['C']['D'] = ['a', 'b'] - res = dictupdate.update(copy.deepcopy(mdict), {'C': {'D': ['c', 'd']}}, - merge_lists=True) - mdict['C']['D'] = ['a', 'b', 'c', 'd'] + mdict["C"]["D"] = ["a", "b"] + res = dictupdate.update( + copy.deepcopy(mdict), {"C": {"D": ["c", "d"]}}, merge_lists=True + ) + mdict["C"]["D"] = ["a", "b", "c", "d"] self.assertEqual(res, mdict) # level 2 value changes (list merge, remove duplicates, preserve order) mdict = copy.deepcopy(self.dict1) - mdict['C']['D'] = ['a', 'b'] - res = dictupdate.update(copy.deepcopy(mdict), - {'C': {'D': ['d', 'c', 'b', 'a']}}, - merge_lists=True) - mdict['C']['D'] = ['a', 'b', 'd', 'c'] + mdict["C"]["D"] = ["a", "b"] + res = dictupdate.update( + copy.deepcopy(mdict), {"C": {"D": ["d", "c", "b", "a"]}}, merge_lists=True + ) + mdict["C"]["D"] = ["a", "b", "d", "c"] self.assertEqual(res, mdict) # level 3 value changes mdict = copy.deepcopy(self.dict1) - mdict['C']['F']['G'] = 'Z' - res = dictupdate.update( - copy.deepcopy(self.dict1), - {'C': {'F': {'G': 'Z'}}} - ) + mdict["C"]["F"]["G"] = "Z" + res = dictupdate.update(copy.deepcopy(self.dict1), {"C": {"F": {"G": "Z"}}}) self.assertEqual(res, mdict) # level 3 value changes (list replacement) mdict = copy.deepcopy(self.dict1) - mdict['C']['F']['G'] = ['a', 'b'] - res = dictupdate.update(copy.deepcopy(mdict), - {'C': {'F': {'G': ['c', 'd']}}}, - merge_lists=False) - mdict['C']['F']['G'] = ['c', 'd'] + mdict["C"]["F"]["G"] = ["a", "b"] + res = dictupdate.update( + copy.deepcopy(mdict), {"C": {"F": {"G": ["c", "d"]}}}, merge_lists=False + ) + mdict["C"]["F"]["G"] = ["c", "d"] self.assertEqual(res, mdict) # level 3 value changes (list merge) mdict = copy.deepcopy(self.dict1) - mdict['C']['F']['G'] = ['a', 'b'] - res = dictupdate.update(copy.deepcopy(mdict), - {'C': {'F': {'G': ['c', 'd']}}}, - merge_lists=True) - mdict['C']['F']['G'] = ['a', 'b', 'c', 'd'] + mdict["C"]["F"]["G"] = ["a", "b"] + res = dictupdate.update( + copy.deepcopy(mdict), {"C": {"F": {"G": ["c", "d"]}}}, merge_lists=True + ) + mdict["C"]["F"]["G"] = ["a", "b", "c", "d"] self.assertEqual(res, mdict) # level 3 value changes (list merge, remove duplicates, preserve order) mdict = copy.deepcopy(self.dict1) - mdict['C']['F']['G'] = ['a', 'b'] - res = dictupdate.update(copy.deepcopy(mdict), - {'C': {'F': {'G': ['d', 'c', 'b', 'a']}}}, - merge_lists=True) - mdict['C']['F']['G'] = ['a', 'b', 'd', 'c'] + mdict["C"]["F"]["G"] = ["a", "b"] + res = dictupdate.update( + copy.deepcopy(mdict), + {"C": {"F": {"G": ["d", "c", "b", "a"]}}}, + merge_lists=True, + ) + mdict["C"]["F"]["G"] = ["a", "b", "d", "c"] self.assertEqual(res, mdict) # replace a sub-dictionary mdict = copy.deepcopy(self.dict1) - mdict['C'] = 'Z' - res = dictupdate.update(copy.deepcopy(self.dict1), {'C': 'Z'}) + mdict["C"] = "Z" + res = dictupdate.update(copy.deepcopy(self.dict1), {"C": "Z"}) self.assertEqual(res, mdict) # add a new scalar value mdict = copy.deepcopy(self.dict1) - mdict['Z'] = 'Y' - res = dictupdate.update(copy.deepcopy(self.dict1), {'Z': 'Y'}) + mdict["Z"] = "Y" + res = dictupdate.update(copy.deepcopy(self.dict1), {"Z": "Y"}) self.assertEqual(res, mdict) # add a dictionary mdict = copy.deepcopy(self.dict1) - mdict['Z'] = {'Y': 'X'} - res = dictupdate.update(copy.deepcopy(self.dict1), {'Z': {'Y': 'X'}}) + mdict["Z"] = {"Y": "X"} + res = dictupdate.update(copy.deepcopy(self.dict1), {"Z": {"Y": "X"}}) self.assertEqual(res, mdict) # add a nested dictionary mdict = copy.deepcopy(self.dict1) - mdict['Z'] = {'Y': {'X': 'W'}} - res = dictupdate.update( - copy.deepcopy(self.dict1), - {'Z': {'Y': {'X': 'W'}}} - ) + mdict["Z"] = {"Y": {"X": "W"}} + res = dictupdate.update(copy.deepcopy(self.dict1), {"Z": {"Y": {"X": "W"}}}) self.assertEqual(res, mdict) class UtilDictMergeTestCase(TestCase): - dict1 = {'A': 'B', 'C': {'D': 'E', 'F': {'G': 'H', 'I': 'J'}}} + dict1 = {"A": "B", "C": {"D": "E", "F": {"G": "H", "I": "J"}}} def test_merge_overwrite_traditional(self): - ''' + """ Test traditional overwrite, wherein a key in the second dict overwrites a key in the first - ''' + """ mdict = copy.deepcopy(self.dict1) - mdict['A'] = 'b' - ret = dictupdate.merge_overwrite(copy.deepcopy(self.dict1), {'A': 'b'}) + mdict["A"] = "b" + ret = dictupdate.merge_overwrite(copy.deepcopy(self.dict1), {"A": "b"}) self.assertEqual(mdict, ret) def test_merge_overwrite_missing_source_key(self): - ''' + """ Test case wherein the overwrite strategy is used but a key in the second dict is not present in the first - ''' + """ mdict = copy.deepcopy(self.dict1) - mdict['D'] = 'new' - ret = dictupdate.merge_overwrite(copy.deepcopy(self.dict1), {'D': 'new'}) + mdict["D"] = "new" + ret = dictupdate.merge_overwrite(copy.deepcopy(self.dict1), {"D": "new"}) self.assertEqual(mdict, ret) def test_merge_aggregate_traditional(self): - ''' + """ Test traditional aggregation, where a val from dict2 overwrites one present in dict1 - ''' + """ mdict = copy.deepcopy(self.dict1) - mdict['A'] = 'b' - ret = dictupdate.merge_overwrite(copy.deepcopy(self.dict1), {'A': 'b'}) + mdict["A"] = "b" + ret = dictupdate.merge_overwrite(copy.deepcopy(self.dict1), {"A": "b"}) self.assertEqual(mdict, ret) def test_merge_list_traditional(self): - ''' + """ Test traditional list merge, where a key present in dict2 will be converted to a list - ''' + """ mdict = copy.deepcopy(self.dict1) - mdict['A'] = ['B', 'b'] - ret = dictupdate.merge_list(copy.deepcopy(self.dict1), {'A': 'b'}) + mdict["A"] = ["B", "b"] + ret = dictupdate.merge_list(copy.deepcopy(self.dict1), {"A": "b"}) self.assertEqual(mdict, ret) def test_merge_list_append(self): - ''' + """ This codifies the intended behaviour that items merged into a dict val that is already a list that those items will *appended* to the list, and not magically merged in - ''' + """ mdict = copy.deepcopy(self.dict1) - mdict['A'] = ['B', 'b', 'c'] + mdict["A"] = ["B", "b", "c"] # Prepare a modified copy of dict1 that has a list as a val for the key of 'A' mdict1 = copy.deepcopy(self.dict1) - mdict1['A'] = ['B'] - ret = dictupdate.merge_list(mdict1, {'A': ['b', 'c']}) - self.assertEqual({'A': [['B'], ['b', 'c']], 'C': {'D': 'E', 'F': {'I': 'J', 'G': 'H'}}}, ret) + mdict1["A"] = ["B"] + ret = dictupdate.merge_list(mdict1, {"A": ["b", "c"]}) + self.assertEqual( + {"A": [["B"], ["b", "c"]], "C": {"D": "E", "F": {"I": "J", "G": "H"}}}, ret + ) class UtilDeepDictUpdateTestCase(TestCase): - dict1 = {'A': 'B', 'C': {'D': 'E', 'F': {'G': 'H', 'I': 'J'}}} + dict1 = {"A": "B", "C": {"D": "E", "F": {"G": "H", "I": "J"}}} def test_deep_set_overwrite(self): - ''' + """ Test overwriting an existing value. - ''' + """ mdict = copy.deepcopy(self.dict1) - res = dictupdate.set_dict_key_value(mdict, 'C:F', 'foo') - self.assertEqual({'A': 'B', 'C': {'D': 'E', 'F': 'foo'}}, res) + res = dictupdate.set_dict_key_value(mdict, "C:F", "foo") + self.assertEqual({"A": "B", "C": {"D": "E", "F": "foo"}}, res) # Verify modify-in-place - self.assertEqual({'A': 'B', 'C': {'D': 'E', 'F': 'foo'}}, mdict) + self.assertEqual({"A": "B", "C": {"D": "E", "F": "foo"}}, mdict) # Test using alternative delimiter - res = dictupdate.set_dict_key_value(mdict, 'C/F', {'G': 'H', 'I': 'J'}, delimiter='/') + res = dictupdate.set_dict_key_value( + mdict, "C/F", {"G": "H", "I": "J"}, delimiter="/" + ) self.assertEqual(self.dict1, res) # Test without using a delimiter in the keys - res = dictupdate.set_dict_key_value(mdict, 'C', None) - self.assertEqual({'A': 'B', 'C': None}, res) + res = dictupdate.set_dict_key_value(mdict, "C", None) + self.assertEqual({"A": "B", "C": None}, res) def test_deep_set_create(self): - ''' + """ Test creating new nested keys. - ''' + """ mdict = copy.deepcopy(self.dict1) - res = dictupdate.set_dict_key_value(mdict, 'K:L:M', 'Q') - self.assertEqual({'A': 'B', 'C': {'D': 'E', 'F': {'G': 'H', 'I': 'J'}}, 'K': {'L': {'M': 'Q'}}}, res) + res = dictupdate.set_dict_key_value(mdict, "K:L:M", "Q") + self.assertEqual( + { + "A": "B", + "C": {"D": "E", "F": {"G": "H", "I": "J"}}, + "K": {"L": {"M": "Q"}}, + }, + res, + ) def test_deep_set_ordered_dicts(self): - ''' + """ Test creating new nested ordereddicts. - ''' - res = dictupdate.set_dict_key_value({}, 'A:B', 'foo', ordered_dict=True) - self.assertEqual({'A': OrderedDict([('B', 'foo')])}, res) + """ + res = dictupdate.set_dict_key_value({}, "A:B", "foo", ordered_dict=True) + self.assertEqual({"A": OrderedDict([("B", "foo")])}, res) def test_deep_append(self): - ''' + """ Test appending to a list. - ''' - sdict = {'bar': {'baz': [1, 2]}} - res = dictupdate.append_dict_key_value(sdict, 'bar:baz', 42) - self.assertEqual({'bar': {'baz': [1, 2, 42]}}, res) + """ + sdict = {"bar": {"baz": [1, 2]}} + res = dictupdate.append_dict_key_value(sdict, "bar:baz", 42) + self.assertEqual({"bar": {"baz": [1, 2, 42]}}, res) # Append with alternate delimiter - res = dictupdate.append_dict_key_value(sdict, 'bar~baz', 43, delimiter='~') - self.assertEqual({'bar': {'baz': [1, 2, 42, 43]}}, res) + res = dictupdate.append_dict_key_value(sdict, "bar~baz", 43, delimiter="~") + self.assertEqual({"bar": {"baz": [1, 2, 42, 43]}}, res) # Append to a not-yet existing list - res = dictupdate.append_dict_key_value({}, 'foo:bar:baz', 42) - self.assertEqual({'foo': {'bar': {'baz': [42]}}}, res) + res = dictupdate.append_dict_key_value({}, "foo:bar:baz", 42) + self.assertEqual({"foo": {"bar": {"baz": [42]}}}, res) def test_deep_extend(self): - ''' + """ Test extending a list. Note that the provided value (to extend with) will be coerced to a list if this is not already a list. This can cause unexpected behaviour. - ''' - sdict = {'bar': {'baz': [1, 2]}} - res = dictupdate.extend_dict_key_value(sdict, 'bar:baz', [42, 42]) - self.assertEqual({'bar': {'baz': [1, 2, 42, 42]}}, res) + """ + sdict = {"bar": {"baz": [1, 2]}} + res = dictupdate.extend_dict_key_value(sdict, "bar:baz", [42, 42]) + self.assertEqual({"bar": {"baz": [1, 2, 42, 42]}}, res) # Extend a not-yet existing list - res = dictupdate.extend_dict_key_value({}, 'bar:baz:qux', [42]) - self.assertEqual({'bar': {'baz': {'qux': [42]}}}, res) + res = dictupdate.extend_dict_key_value({}, "bar:baz:qux", [42]) + self.assertEqual({"bar": {"baz": {"qux": [42]}}}, res) # Extend with a dict (remember, foo has been updated in the first test) - res = dictupdate.extend_dict_key_value(sdict, 'bar:baz', {'qux': 'quux'}) - self.assertEqual({'bar': {'baz': [1, 2, 42, 42, 'qux']}}, res) + res = dictupdate.extend_dict_key_value(sdict, "bar:baz", {"qux": "quux"}) + self.assertEqual({"bar": {"baz": [1, 2, 42, 42, "qux"]}}, res) def test_deep_extend_illegal_addition(self): - ''' + """ Test errorhandling extending lists with illegal types. - ''' + """ # Extend with an illegal type for extend_with in [42, None]: - with self.assertRaisesRegex(SaltInvocationError, - r"Cannot extend {} with a {}." - "".format(type([]), type(extend_with))): - dictupdate.extend_dict_key_value({}, 'foo', extend_with) + with self.assertRaisesRegex( + SaltInvocationError, + r"Cannot extend {} with a {}." "".format(type([]), type(extend_with)), + ): + dictupdate.extend_dict_key_value({}, "foo", extend_with) def test_deep_extend_illegal_source(self): - ''' + """ Test errorhandling extending things that are not a list. - ''' + """ # Extend an illegal type - for extend_this in [{}, 42, 'bar']: - with self.assertRaisesRegex(SaltInvocationError, - r"The last key contains a {}, which cannot extend." - "".format(type(extend_this))): - dictupdate.extend_dict_key_value({'foo': extend_this}, 'foo', [42]) + for extend_this in [{}, 42, "bar"]: + with self.assertRaisesRegex( + SaltInvocationError, + r"The last key contains a {}, which cannot extend." + "".format(type(extend_this)), + ): + dictupdate.extend_dict_key_value({"foo": extend_this}, "foo", [42]) def test_deep_update(self): - ''' + """ Test updating a (sub)dict. - ''' + """ mdict = copy.deepcopy(self.dict1) - res = dictupdate.update_dict_key_value(mdict, 'C:F', {'foo': 'bar', 'qux': 'quux'}) - self.assertEqual({'A': 'B', 'C': {'D': 'E', 'F': {'G': 'H', 'I': 'J', 'foo': 'bar', 'qux': 'quux'}}}, res) + res = dictupdate.update_dict_key_value( + mdict, "C:F", {"foo": "bar", "qux": "quux"} + ) + self.assertEqual( + { + "A": "B", + "C": {"D": "E", "F": {"G": "H", "I": "J", "foo": "bar", "qux": "quux"}}, + }, + res, + ) # Test updating a non-existing subkey - res = dictupdate.update_dict_key_value({}, 'foo:bar:baz', {'qux': 'quux'}) - self.assertEqual({'foo': {'bar': {'baz': {'qux': 'quux'}}}}, res) + res = dictupdate.update_dict_key_value({}, "foo:bar:baz", {"qux": "quux"}) + self.assertEqual({"foo": {"bar": {"baz": {"qux": "quux"}}}}, res) # Test updating a non-existing subkey, with a different delimiter - res = dictupdate.update_dict_key_value({}, 'foo bar baz', {'qux': 'quux'}, delimiter=' ') - self.assertEqual({'foo': {'bar': {'baz': {'qux': 'quux'}}}}, res) + res = dictupdate.update_dict_key_value( + {}, "foo bar baz", {"qux": "quux"}, delimiter=" " + ) + self.assertEqual({"foo": {"bar": {"baz": {"qux": "quux"}}}}, res) def test_deep_update_illegal_update(self): - ''' + """ Test errorhandling updating a (sub)dict with illegal types. - ''' + """ # Update with an illegal type - for update_with in [42, None, [42], 'bar']: - with self.assertRaisesRegex(SaltInvocationError, - r"Cannot update {} with a {}." - "".format(type({}), type(update_with))): - dictupdate.update_dict_key_value({}, 'foo', update_with) + for update_with in [42, None, [42], "bar"]: + with self.assertRaisesRegex( + SaltInvocationError, + r"Cannot update {} with a {}." "".format(type({}), type(update_with)), + ): + dictupdate.update_dict_key_value({}, "foo", update_with) # Again, but now using OrderedDicts - for update_with in [42, None, [42], 'bar']: - with self.assertRaisesRegex(SaltInvocationError, - r"Cannot update {} with a {}." - "".format(type(OrderedDict()), type(update_with))): - dictupdate.update_dict_key_value({}, 'foo', update_with, ordered_dict=True) + for update_with in [42, None, [42], "bar"]: + with self.assertRaisesRegex( + SaltInvocationError, + r"Cannot update {} with a {}." + "".format(type(OrderedDict()), type(update_with)), + ): + dictupdate.update_dict_key_value( + {}, "foo", update_with, ordered_dict=True + ) diff --git a/tests/unit/utils/test_dns.py b/tests/unit/utils/test_dns.py index f7f076cd16a..7f77e54f91f 100644 --- a/tests/unit/utils/test_dns.py +++ b/tests/unit/utils/test_dns.py @@ -1,54 +1,71 @@ # -*- coding: utf-8 -*- -''' +""" -''' +""" from __future__ import absolute_import, print_function, unicode_literals # Python import socket import textwrap -from salt.ext.six.moves import zip # pylint: disable=redefined-builtin + +import salt.modules.cmdmod +import salt.utils.dns # Salt from salt._compat import ipaddress +from salt.ext.six.moves import zip # pylint: disable=redefined-builtin +from salt.utils.dns import ( + _data2rec, + _data2rec_group, + _lookup_dig, + _lookup_drill, + _lookup_gai, + _lookup_host, + _lookup_nslookup, + _to_port, + _tree, + _weighted_order, + lookup, +) from salt.utils.odict import OrderedDict -import salt.utils.dns -from salt.utils.dns import _to_port, _tree, _weighted_order, _data2rec, _data2rec_group -from salt.utils.dns import _lookup_gai, _lookup_dig, _lookup_drill, _lookup_host, _lookup_nslookup -from salt.utils.dns import lookup -import salt.modules.cmdmod +from tests.support.mock import MagicMock, patch # Testing -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf class DNShelpersCase(TestCase): - ''' + """ Tests for the parser helpers - ''' + """ + def test_port(self): - for right in (1, 42, '123', 65535): + for right in (1, 42, "123", 65535): self.assertEqual(_to_port(right), int(right)) - for wrong in (0, 65536, 100000, 'not-a-port'): + for wrong in (0, 65536, 100000, "not-a-port"): self.assertRaises(ValueError, _to_port, wrong) def test_tree(self): test_map = ( - 'ex1.nl', - 'o.1.example.eu', - 'a1a.b2b.c3c.example.com', - 'c3c.example.co.uk', - 'c3c.example.mil.ng', + "ex1.nl", + "o.1.example.eu", + "a1a.b2b.c3c.example.com", + "c3c.example.co.uk", + "c3c.example.mil.ng", ) res_map = ( - ['ex1.nl'], - ['o.1.example.eu', '1.example.eu', 'example.eu'], - ['a1a.b2b.c3c.example.com', 'b2b.c3c.example.com', 'c3c.example.com', 'example.com'], - ['c3c.example.co.uk', 'example.co.uk'], - ['c3c.example.mil.ng', 'example.mil.ng'] + ["ex1.nl"], + ["o.1.example.eu", "1.example.eu", "example.eu"], + [ + "a1a.b2b.c3c.example.com", + "b2b.c3c.example.com", + "c3c.example.com", + "example.com", + ], + ["c3c.example.co.uk", "example.co.uk"], + ["c3c.example.mil.ng", "example.mil.ng"], ) for domain, result in zip(test_map, res_map): @@ -57,17 +74,17 @@ class DNShelpersCase(TestCase): def test_weight(self): recs = [ [], - [{'weight': 100, 'name': 'nescio'}], + [{"weight": 100, "name": "nescio"}], [ - {'weight': 100, 'name': 'nescio1'}, - {'weight': 100, 'name': 'nescio2'}, - {'weight': 100, 'name': 'nescio3'}, - {'weight': 100, 'name': 'nescio4'}, - {'weight': 100, 'name': 'nescio5'}, - {'weight': 100, 'name': 'nescio6'}, - {'weight': 100, 'name': 'nescio7'}, - {'weight': 100, 'name': 'nescio8'} - ] + {"weight": 100, "name": "nescio1"}, + {"weight": 100, "name": "nescio2"}, + {"weight": 100, "name": "nescio3"}, + {"weight": 100, "name": "nescio4"}, + {"weight": 100, "name": "nescio5"}, + {"weight": 100, "name": "nescio6"}, + {"weight": 100, "name": "nescio7"}, + {"weight": 100, "name": "nescio8"}, + ], ] # What are the odds of this tripping over a build @@ -75,48 +92,40 @@ class DNShelpersCase(TestCase): self.assertNotEqual( _weighted_order(list(recs[-1])), _weighted_order(list(recs[-1])), - _weighted_order(list(recs[-1])) + _weighted_order(list(recs[-1])), ) for recset in recs: rs_res = _weighted_order(list(recset)) - self.assertTrue(all(rec['name'] in rs_res for rec in recset)) + self.assertTrue(all(rec["name"] in rs_res for rec in recset)) def test_data2rec(self): right = [ - '10.0.0.1', - '10 mbox.example.com', - '10 20 30 example.com', + "10.0.0.1", + "10 mbox.example.com", + "10 20 30 example.com", ] schemas = [ - OrderedDict(( - ('address', ipaddress.IPv4Address), - )), - OrderedDict(( - ('preference', int), - ('name', str), - )), - OrderedDict(( - ('prio', int), - ('weight', int), - ('port', _to_port), - ('name', str), - )) + OrderedDict((("address", ipaddress.IPv4Address),)), + OrderedDict((("preference", int), ("name", str),)), + OrderedDict( + (("prio", int), ("weight", int), ("port", _to_port), ("name", str),) + ), ] results = [ ipaddress.IPv4Address(right[0]), - {'preference': 10, 'name': 'mbox.example.com'}, - {'prio': 10, 'weight': 20, 'port': 30, 'name': 'example.com'} + {"preference": 10, "name": "mbox.example.com"}, + {"prio": 10, "weight": 20, "port": 30, "name": "example.com"}, ] for rdata, rschema, res in zip(right, schemas, results): self.assertEqual(_data2rec(rschema, rdata), res) wrong = [ - 'not-an-ip', - 'hundred 20 30 interror.example.com', - '10 toolittle.example.com', + "not-an-ip", + "hundred 20 30 interror.example.com", + "10 toolittle.example.com", ] for rdata, rschema in zip(wrong, schemas): @@ -124,37 +133,39 @@ class DNShelpersCase(TestCase): def test_data2group(self): right = [ - ['10 mbox.example.com'], + ["10 mbox.example.com"], [ - '10 mbox1.example.com', - '20 mbox2.example.com', - '20 mbox3.example.com', - '30 mbox4.example.com', - '30 mbox5.example.com', - '30 mbox6.example.com', + "10 mbox1.example.com", + "20 mbox2.example.com", + "20 mbox3.example.com", + "30 mbox4.example.com", + "30 mbox5.example.com", + "30 mbox6.example.com", ], ] - rschema = OrderedDict(( - ('prio', int), - ('srvr', str), - )) + rschema = OrderedDict((("prio", int), ("srvr", str),)) results = [ - OrderedDict([(10, ['mbox.example.com'])]), - OrderedDict([ - (10, ['mbox1.example.com']), - (20, ['mbox2.example.com', 'mbox3.example.com']), - (30, ['mbox4.example.com', 'mbox5.example.com', 'mbox6.example.com'])] + OrderedDict([(10, ["mbox.example.com"])]), + OrderedDict( + [ + (10, ["mbox1.example.com"]), + (20, ["mbox2.example.com", "mbox3.example.com"]), + ( + 30, + ["mbox4.example.com", "mbox5.example.com", "mbox6.example.com"], + ), + ] ), ] for rdata, res in zip(right, results): - group = _data2rec_group(rschema, rdata, 'prio') + group = _data2rec_group(rschema, rdata, "prio") self.assertEqual(group, res) class DNSlookupsCase(TestCase): - ''' + """ Test the lookup result parsers Note that by far and large the parsers actually @@ -166,55 +177,51 @@ class DNSlookupsCase(TestCase): - returns False upon error - returns [*record-data] upon succes/no records - ''' - CMD_RET = { - 'pid': 12345, - 'retcode': 0, - 'stderr': '', - 'stdout': '' - } + """ + + CMD_RET = {"pid": 12345, "retcode": 0, "stderr": "", "stdout": ""} RESULTS = { - 'A': [ - ['10.1.1.1'], # one-match - ['10.1.1.1', '10.2.2.2', '10.3.3.3'], # multi-match + "A": [ + ["10.1.1.1"], # one-match + ["10.1.1.1", "10.2.2.2", "10.3.3.3"], # multi-match ], - 'AAAA': [ - ['2a00:a00:b01:c02:d03:e04:f05:111'], # one-match - ['2a00:a00:b01:c02:d03:e04:f05:111', - '2a00:a00:b01:c02:d03:e04:f05:222', - '2a00:a00:b01:c02:d03:e04:f05:333'] # multi-match - ], - 'CAA': [ - ['0 issue "exampleca.com"', '0 iodef "mailto:sslabuse@example.com"'], - ], - 'CNAME': [ - ['web.example.com.'] - ], - 'MX': [ - ['10 mx1.example.com.'], - ['10 mx1.example.com.', '20 mx2.example.eu.', '30 mx3.example.nl.'] - ], - 'SSHFP': [ + "AAAA": [ + ["2a00:a00:b01:c02:d03:e04:f05:111"], # one-match [ - '1 1 0aabda8af5418108e8a5d3f90f207226b2c89fbe', - '1 2 500ca871d8e255e01f1261a2370c4e5406b8712f19916d3ab9f86344a67e5597', - '3 1 a3b605ce6f044617c6077c46a7cd5d17a767f0d5', - '4 2 0360d0a5a2fa550f972259e7374533add7ac8e5f303322a5b8e208bbc859ab1b' + "2a00:a00:b01:c02:d03:e04:f05:111", + "2a00:a00:b01:c02:d03:e04:f05:222", + "2a00:a00:b01:c02:d03:e04:f05:333", + ], # multi-match + ], + "CAA": [['0 issue "exampleca.com"', '0 iodef "mailto:sslabuse@example.com"']], + "CNAME": [["web.example.com."]], + "MX": [ + ["10 mx1.example.com."], + ["10 mx1.example.com.", "20 mx2.example.eu.", "30 mx3.example.nl."], + ], + "SSHFP": [ + [ + "1 1 0aabda8af5418108e8a5d3f90f207226b2c89fbe", + "1 2 500ca871d8e255e01f1261a2370c4e5406b8712f19916d3ab9f86344a67e5597", + "3 1 a3b605ce6f044617c6077c46a7cd5d17a767f0d5", + "4 2 0360d0a5a2fa550f972259e7374533add7ac8e5f303322a5b8e208bbc859ab1b", + ] + ], + "TXT": [ + [ + "v=spf1 a include:_spf4.example.com include:mail.example.eu ip4:10.0.0.0/8 ip6:2a00:a00:b01::/48 ~all" ] ], - 'TXT': [ - ['v=spf1 a include:_spf4.example.com include:mail.example.eu ip4:10.0.0.0/8 ip6:2a00:a00:b01::/48 ~all'] - ] } def _mock_cmd_ret(self, delta_res): - ''' + """ Take CMD_RET and update it w/(a list of ) delta_res Mock cmd.run_all w/it :param delta_res: list or dict :return: patched cmd.run_all - ''' + """ if isinstance(delta_res, (list, tuple)): test_res = [] for dres in delta_res: @@ -222,58 +229,62 @@ class DNSlookupsCase(TestCase): tres.update(dres) test_res.append(tres) - cmd_mock = MagicMock( - side_effect=test_res - ) + cmd_mock = MagicMock(side_effect=test_res) else: test_res = self.CMD_RET.copy() test_res.update(delta_res) - cmd_mock = MagicMock( - return_value=test_res - ) - return patch.dict(salt.utils.dns.__salt__, {'cmd.run_all': cmd_mock}, clear=True) + cmd_mock = MagicMock(return_value=test_res) + return patch.dict( + salt.utils.dns.__salt__, {"cmd.run_all": cmd_mock}, clear=True + ) - def _test_cmd_lookup(self, lookup_cb, wrong_type, wrong, right, empty=None, secure=None): - ''' + def _test_cmd_lookup( + self, lookup_cb, wrong_type, wrong, right, empty=None, secure=None + ): + """ Perform a given battery of tests against a given lookup utilizing cmd.run_all :param wrong_type: delta cmd.run_all output for an incorrect DNS type :param wrong: delta cmd.run_all output for any error :param right: delta cmd.run_all output for outputs in RESULTS :param empty: delta cmd.run_all output for anything that won't return matches :param secure: delta cmd.run_all output for secured RESULTS - ''' + """ # wrong for wrong in wrong: with self._mock_cmd_ret(wrong): - self.assertEqual(lookup_cb('mockq', 'A'), False) + self.assertEqual(lookup_cb("mockq", "A"), False) # empty response if empty is None: empty = {} with self._mock_cmd_ret(empty): - self.assertEqual(lookup_cb('mockq', 'AAAA'), []) + self.assertEqual(lookup_cb("mockq", "AAAA"), []) # wrong types with self._mock_cmd_ret(wrong_type): - self.assertRaises(ValueError, lookup_cb, 'mockq', 'WRONG') + self.assertRaises(ValueError, lookup_cb, "mockq", "WRONG") # Regular outputs for rec_t, tests in right.items(): - with self._mock_cmd_ret([dict([('stdout', dres)]) for dres in tests]): + with self._mock_cmd_ret([dict([("stdout", dres)]) for dres in tests]): for test_res in self.RESULTS[rec_t]: - if rec_t in ('A', 'AAAA', 'CNAME', 'SSHFP'): - rec = 'mocksrvr.example.com' + if rec_t in ("A", "AAAA", "CNAME", "SSHFP"): + rec = "mocksrvr.example.com" else: - rec = 'example.com' + rec = "example.com" lookup_res = lookup_cb(rec, rec_t) - if rec_t == 'SSHFP': + if rec_t == "SSHFP": # Some resolvers 'split' the output and/or capitalize differently. # So we need to workaround that here as well - lookup_res = [res[:4] + res[4:].replace(' ', '').lower() for res in lookup_res] + lookup_res = [ + res[:4] + res[4:].replace(" ", "").lower() + for res in lookup_res + ] self.assertEqual( - lookup_res, test_res, + lookup_res, + test_res, # msg='Error parsing {0} returns'.format(rec_t) ) @@ -282,128 +293,139 @@ class DNSlookupsCase(TestCase): # Regular outputs are insecure outputs (e.g. False) for rec_t, tests in right.items(): - with self._mock_cmd_ret([dict([('stdout', dres)]) for dres in tests]): + with self._mock_cmd_ret([dict([("stdout", dres)]) for dres in tests]): for _ in self.RESULTS[rec_t]: self.assertEqual( - lookup_cb('mocksrvr.example.com', rec_t, secure=True), False, - msg='Insecure {0} returns should not be returned'.format(rec_t) + lookup_cb("mocksrvr.example.com", rec_t, secure=True), + False, + msg="Insecure {0} returns should not be returned".format(rec_t), ) for rec_t, tests in secure.items(): - with self._mock_cmd_ret([dict([('stdout', dres)]) for dres in tests]): + with self._mock_cmd_ret([dict([("stdout", dres)]) for dres in tests]): for test_res in self.RESULTS[rec_t]: self.assertEqual( - lookup_cb('mocksrvr.example.com', rec_t, secure=True), test_res, - msg='Error parsing DNSSEC\'d {0} returns'.format(rec_t) + lookup_cb("mocksrvr.example.com", rec_t, secure=True), + test_res, + msg="Error parsing DNSSEC'd {0} returns".format(rec_t), ) - @skipIf(not salt.utils.dns.HAS_NSLOOKUP, 'nslookup is not available') + @skipIf(not salt.utils.dns.HAS_NSLOOKUP, "nslookup is not available") def test_lookup_with_servers(self): rights = { - 'A': [ - 'Name:\tmocksrvr.example.com\nAddress: 10.1.1.1', - 'Name:\tmocksrvr.example.com\nAddress: 10.1.1.1\n' - 'Name:\tweb.example.com\nAddress: 10.2.2.2\n' - 'Name:\tweb.example.com\nAddress: 10.3.3.3' + "A": [ + "Name:\tmocksrvr.example.com\nAddress: 10.1.1.1", + "Name:\tmocksrvr.example.com\nAddress: 10.1.1.1\n" + "Name:\tweb.example.com\nAddress: 10.2.2.2\n" + "Name:\tweb.example.com\nAddress: 10.3.3.3", ], - 'AAAA': [ - 'mocksrvr.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111', - 'mocksrvr.example.com\tcanonical name = web.example.com.\n' - 'web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111\n' - 'web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:222\n' - 'web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:333' + "AAAA": [ + "mocksrvr.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111", + "mocksrvr.example.com\tcanonical name = web.example.com.\n" + "web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111\n" + "web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:222\n" + "web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:333", ], - 'CNAME': [ - 'mocksrvr.example.com\tcanonical name = web.example.com.' + "CNAME": ["mocksrvr.example.com\tcanonical name = web.example.com."], + "MX": [ + "example.com\tmail exchanger = 10 mx1.example.com.", + "example.com\tmail exchanger = 10 mx1.example.com.\n" + "example.com\tmail exchanger = 20 mx2.example.eu.\n" + "example.com\tmail exchanger = 30 mx3.example.nl.", ], - 'MX': [ - 'example.com\tmail exchanger = 10 mx1.example.com.', - 'example.com\tmail exchanger = 10 mx1.example.com.\n' - 'example.com\tmail exchanger = 20 mx2.example.eu.\n' - 'example.com\tmail exchanger = 30 mx3.example.nl.' - ], - 'TXT': [ + "TXT": [ 'example.com\ttext = "v=spf1 a include:_spf4.example.com include:mail.example.eu ip4:10.0.0.0/8 ip6:2a00:a00:b01::/48 ~all"' - ] + ], } for rec_t, tests in rights.items(): - with self._mock_cmd_ret([dict([('stdout', dres)]) for dres in tests]): + with self._mock_cmd_ret([dict([("stdout", dres)]) for dres in tests]): for test_res in self.RESULTS[rec_t]: - if rec_t in ('A', 'AAAA', 'CNAME'): - rec = 'mocksrvr.example.com' + if rec_t in ("A", "AAAA", "CNAME"): + rec = "mocksrvr.example.com" else: - rec = 'example.com' + rec = "example.com" self.assertEqual( - lookup(rec, rec_t, method='nslookup', servers='8.8.8.8'), test_res, + lookup(rec, rec_t, method="nslookup", servers="8.8.8.8"), + test_res, ) - @skipIf(not salt.utils.dns.HAS_DIG, 'dig is not available') + @skipIf(not salt.utils.dns.HAS_DIG, "dig is not available") def test_dig_options(self): - cmd = 'dig {0} -v'.format(salt.utils.dns.DIG_OPTIONS) - cmd = salt.modules.cmdmod.retcode(cmd, python_shell=False, output_loglevel='quiet') + cmd = "dig {0} -v".format(salt.utils.dns.DIG_OPTIONS) + cmd = salt.modules.cmdmod.retcode( + cmd, python_shell=False, output_loglevel="quiet" + ) self.assertEqual(cmd, 0) def test_dig(self): - wrong_type = {'retcode': 0, 'stderr': ';; Warning, ignoring invalid type ABC'} + wrong_type = {"retcode": 0, "stderr": ";; Warning, ignoring invalid type ABC"} wrongs = [ - {'retcode': 9, 'stderr': ';; connection timed out; no servers could be reached'}, + { + "retcode": 9, + "stderr": ";; connection timed out; no servers could be reached", + }, ] # example returns for dig +search +fail +noall +answer +noclass +nosplit +nottl -t {rtype} {name} rights = { - 'A': [ - 'mocksrvr.example.com.\tA\t10.1.1.1', - 'web.example.com.\t\tA\t10.1.1.1\n' - 'web.example.com.\t\tA\t10.2.2.2\n' - 'web.example.com.\t\tA\t10.3.3.3' + "A": [ + "mocksrvr.example.com.\tA\t10.1.1.1", + "web.example.com.\t\tA\t10.1.1.1\n" + "web.example.com.\t\tA\t10.2.2.2\n" + "web.example.com.\t\tA\t10.3.3.3", ], - 'AAAA': [ - 'mocksrvr.example.com.\tA\t2a00:a00:b01:c02:d03:e04:f05:111', - 'mocksrvr.example.com.\tCNAME\tweb.example.com.\n' - 'web.example.com.\t\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:111\n' - 'web.example.com.\t\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:222\n' - 'web.example.com.\t\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:333' + "AAAA": [ + "mocksrvr.example.com.\tA\t2a00:a00:b01:c02:d03:e04:f05:111", + "mocksrvr.example.com.\tCNAME\tweb.example.com.\n" + "web.example.com.\t\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:111\n" + "web.example.com.\t\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:222\n" + "web.example.com.\t\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:333", ], - 'CAA': [ + "CAA": [ 'example.com.\t\tCAA\t0 issue "exampleca.com"\n' 'example.com.\t\tCAA\t0 iodef "mailto:sslabuse@example.com"' ], - 'CNAME': [ - 'mocksrvr.example.com.\tCNAME\tweb.example.com.' + "CNAME": ["mocksrvr.example.com.\tCNAME\tweb.example.com."], + "MX": [ + "example.com.\t\tMX\t10 mx1.example.com.", + "example.com.\t\tMX\t10 mx1.example.com.\nexample.com.\t\tMX\t20 mx2.example.eu.\nexample.com.\t\tMX\t30 mx3.example.nl.", ], - 'MX': [ - 'example.com.\t\tMX\t10 mx1.example.com.', - 'example.com.\t\tMX\t10 mx1.example.com.\nexample.com.\t\tMX\t20 mx2.example.eu.\nexample.com.\t\tMX\t30 mx3.example.nl.' + "SSHFP": [ + "mocksrvr.example.com.\tSSHFP\t1 1 0AABDA8AF5418108E8A5D3F90F207226B2C89FBE\n" + "mocksrvr.example.com.\tSSHFP\t1 2 500CA871D8E255E01F1261A2370C4E5406B8712F19916D3AB9F86344A67E5597\n" + "mocksrvr.example.com.\tSSHFP\t3 1 A3B605CE6F044617C6077C46A7CD5D17A767F0D5\n" + "mocksrvr.example.com.\tSSHFP\t4 2 0360D0A5A2FA550F972259E7374533ADD7AC8E5F303322A5B8E208BBC859AB1B" ], - 'SSHFP': [ - 'mocksrvr.example.com.\tSSHFP\t1 1 0AABDA8AF5418108E8A5D3F90F207226B2C89FBE\n' - 'mocksrvr.example.com.\tSSHFP\t1 2 500CA871D8E255E01F1261A2370C4E5406B8712F19916D3AB9F86344A67E5597\n' - 'mocksrvr.example.com.\tSSHFP\t3 1 A3B605CE6F044617C6077C46A7CD5D17A767F0D5\n' - 'mocksrvr.example.com.\tSSHFP\t4 2 0360D0A5A2FA550F972259E7374533ADD7AC8E5F303322A5B8E208BBC859AB1B' - ], - 'TXT': [ + "TXT": [ 'example.com.\tTXT\t"v=spf1 a include:_spf4.example.com include:mail.example.eu ip4:10.0.0.0/8 ip6:2a00:a00:b01::/48 ~all"' - ] + ], } secure = { - 'A': [ - 'mocksrvr.example.com.\tA\t10.1.1.1\n' - 'mocksrvr.example.com.\tRRSIG\tA 8 3 7200 20170420000000 20170330000000 1629 example.com. Hv4p37EF55LKBxUNYpnhWiEYqfmMct0z0WgDJyG5reqYfl+z4HX/kaoi Wr2iCYuYeB4Le7BgnMSb77UGHPWE7lCQ8z5gkgJ9rCDrooJzSTVdnHfw 1JQ7txRSp8Rj2GLf/L3Ytuo6nNZTV7bWUkfhOs61DAcOPHYZiX8rVhIh UAE=', - 'web.example.com.\t\tA\t10.1.1.1\n' - 'web.example.com.\t\tA\t10.2.2.2\n' - 'web.example.com.\t\tA\t10.3.3.3\n' - 'web.example.com.\tRRSIG\tA 8 3 7200 20170420000000 20170330000000 1629 example.com. Hv4p37EF55LKBxUNYpnhWiEYqfmMct0z0WgDJyG5reqYfl+z4HX/kaoi Wr2iCYuYeB4Le7BgnMSb77UGHPWE7lCQ8z5gkgJ9rCDrooJzSTVdnHfw 1JQ7txRSp8Rj2GLf/L3Ytuo6nNZTV7bWUkfhOs61DAcOPHYZiX8rVhIh UAE=' + "A": [ + "mocksrvr.example.com.\tA\t10.1.1.1\n" + "mocksrvr.example.com.\tRRSIG\tA 8 3 7200 20170420000000 20170330000000 1629 example.com. Hv4p37EF55LKBxUNYpnhWiEYqfmMct0z0WgDJyG5reqYfl+z4HX/kaoi Wr2iCYuYeB4Le7BgnMSb77UGHPWE7lCQ8z5gkgJ9rCDrooJzSTVdnHfw 1JQ7txRSp8Rj2GLf/L3Ytuo6nNZTV7bWUkfhOs61DAcOPHYZiX8rVhIh UAE=", + "web.example.com.\t\tA\t10.1.1.1\n" + "web.example.com.\t\tA\t10.2.2.2\n" + "web.example.com.\t\tA\t10.3.3.3\n" + "web.example.com.\tRRSIG\tA 8 3 7200 20170420000000 20170330000000 1629 example.com. Hv4p37EF55LKBxUNYpnhWiEYqfmMct0z0WgDJyG5reqYfl+z4HX/kaoi Wr2iCYuYeB4Le7BgnMSb77UGHPWE7lCQ8z5gkgJ9rCDrooJzSTVdnHfw 1JQ7txRSp8Rj2GLf/L3Ytuo6nNZTV7bWUkfhOs61DAcOPHYZiX8rVhIh UAE=", ] } - self._test_cmd_lookup(_lookup_dig, wrong=wrongs, right=rights, wrong_type=wrong_type, secure=secure) + self._test_cmd_lookup( + _lookup_dig, + wrong=wrongs, + right=rights, + wrong_type=wrong_type, + secure=secure, + ) def test_drill(self): # all Drill returns look like this - RES_TMPL = textwrap.dedent('''\ + RES_TMPL = textwrap.dedent( + """\ ;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 58233 ;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 ;; QUESTION SECTION: @@ -420,63 +442,66 @@ class DNSlookupsCase(TestCase): ;; SERVER: 10.100.150.129 ;; WHEN: Tue Apr 4 19:03:51 2017 ;; MSG SIZE rcvd: 50 - ''') + """ + ) # Not even a different retcode!? - wrong_type = {'stdout': RES_TMPL.format('mocksrvr.example.com.\t4404\tIN\tA\t10.1.1.1\n')} + wrong_type = { + "stdout": RES_TMPL.format("mocksrvr.example.com.\t4404\tIN\tA\t10.1.1.1\n") + } wrongs = [ - {'retcode': 1, 'stderr': 'Error: error sending query: No (valid) nameservers defined in the resolver'} + { + "retcode": 1, + "stderr": "Error: error sending query: No (valid) nameservers defined in the resolver", + } ] # example returns for drill {rtype} {name} rights = { - 'A': [ - 'mocksrvr.example.com.\t4404\tIN\tA\t10.1.1.1\n', - 'web.example.com.\t4404\tIN\tA\t10.1.1.1\n' - 'web.example.com.\t4404\tIN\tA\t10.2.2.2\n' - 'web.example.com.\t4404\tIN\tA\t10.3.3.3', + "A": [ + "mocksrvr.example.com.\t4404\tIN\tA\t10.1.1.1\n", + "web.example.com.\t4404\tIN\tA\t10.1.1.1\n" + "web.example.com.\t4404\tIN\tA\t10.2.2.2\n" + "web.example.com.\t4404\tIN\tA\t10.3.3.3", ], - 'AAAA': [ - 'mocksrvr.example.com.\t4404\tIN\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:111', - 'mocksrvr.example.com.\t4404\tIN\tCNAME\tweb.example.com.\n' - 'web.example.com.\t4404\tIN\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:111\n' - 'web.example.com.\t4404\tIN\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:222\n' - 'web.example.com.\t4404\tIN\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:333' + "AAAA": [ + "mocksrvr.example.com.\t4404\tIN\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:111", + "mocksrvr.example.com.\t4404\tIN\tCNAME\tweb.example.com.\n" + "web.example.com.\t4404\tIN\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:111\n" + "web.example.com.\t4404\tIN\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:222\n" + "web.example.com.\t4404\tIN\tAAAA\t2a00:a00:b01:c02:d03:e04:f05:333", ], - 'CAA': [ + "CAA": [ 'example.com.\t1144\tIN\tCAA\t0 issue "exampleca.com"\n' 'example.com.\t1144\tIN\tCAA\t0 iodef "mailto:sslabuse@example.com"' ], - 'CNAME': [ - 'mocksrvr.example.com.\t4404\tIN\tCNAME\tweb.example.com.' + "CNAME": ["mocksrvr.example.com.\t4404\tIN\tCNAME\tweb.example.com."], + "MX": [ + "example.com.\t4404\tIN\tMX\t10 mx1.example.com.", + "example.com.\t4404\tIN\tMX\t10 mx1.example.com.\n" + "example.com.\t4404\tIN\tMX\t20 mx2.example.eu.\n" + "example.com.\t4404\tIN\tMX\t30 mx3.example.nl.", ], - 'MX': [ - 'example.com.\t4404\tIN\tMX\t10 mx1.example.com.', - 'example.com.\t4404\tIN\tMX\t10 mx1.example.com.\n' - 'example.com.\t4404\tIN\tMX\t20 mx2.example.eu.\n' - 'example.com.\t4404\tIN\tMX\t30 mx3.example.nl.' + "SSHFP": [ + "mocksrvr.example.com.\t3339\tIN\tSSHFP\t1 1 0aabda8af5418108e8a5d3f90f207226b2c89fbe\n" + "mocksrvr.example.com.\t3339\tIN\tSSHFP\t1 2 500ca871d8e255e01f1261a2370c4e5406b8712f19916d3ab9f86344a67e5597\n" + "mocksrvr.example.com.\t3339\tIN\tSSHFP\t3 1 a3b605ce6f044617c6077c46a7cd5d17a767f0d5\n" + "mocksrvr.example.com.\t3339\tIN\tSSHFP\t4 2 0360d0a5a2fa550f972259e7374533add7ac8e5f303322a5b8e208bbc859ab1b" ], - 'SSHFP': [ - 'mocksrvr.example.com.\t3339\tIN\tSSHFP\t1 1 0aabda8af5418108e8a5d3f90f207226b2c89fbe\n' - 'mocksrvr.example.com.\t3339\tIN\tSSHFP\t1 2 500ca871d8e255e01f1261a2370c4e5406b8712f19916d3ab9f86344a67e5597\n' - 'mocksrvr.example.com.\t3339\tIN\tSSHFP\t3 1 a3b605ce6f044617c6077c46a7cd5d17a767f0d5\n' - 'mocksrvr.example.com.\t3339\tIN\tSSHFP\t4 2 0360d0a5a2fa550f972259e7374533add7ac8e5f303322a5b8e208bbc859ab1b' - ], - 'TXT': [ + "TXT": [ 'example.com.\t4404\tIN\tTXT\t"v=spf1 a include:_spf4.example.com include:mail.example.eu ip4:10.0.0.0/8 ip6:2a00:a00:b01::/48 ~all"' - ] + ], } secure = { - 'A': [ - 'mocksrvr.example.com.\t4404\tIN\tA\t10.1.1.1\n' - 'mocksrvr.example.com.\t4404\tIN\tRRSIG\tA 8 3 7200 20170420000000 20170330000000 1629 example.com. Hv4p37EF55LKBxUNYpnhWiEYqfmMct0z0WgDJyG5reqYfl+z4HX/kaoi Wr2iCYuYeB4Le7BgnMSb77UGHPWE7lCQ8z5gkgJ9rCDrooJzSTVdnHfw 1JQ7txRSp8Rj2GLf/L3Ytuo6nNZTV7bWUkfhOs61DAcOPHYZiX8rVhIh UAE=', - 'web.example.com.\t4404\tIN\tA\t10.1.1.1\n' - 'web.example.com.\t4404\tIN\tA\t10.2.2.2\n' - 'web.example.com.\t4404\tIN\tA\t10.3.3.3\n' - 'web.example.com.\t4404\tIN\tRRSIG\tA 8 3 7200 20170420000000 20170330000000 1629 example.com. Hv4p37EF55LKBxUNYpnhWiEYqfmMct0z0WgDJyG5reqYfl+z4HX/kaoi Wr2iCYuYeB4Le7BgnMSb77UGHPWE7lCQ8z5gkgJ9rCDrooJzSTVdnHfw 1JQ7txRSp8Rj2GLf/L3Ytuo6nNZTV7bWUkfhOs61DAcOPHYZiX8rVhIh UAE=' - + "A": [ + "mocksrvr.example.com.\t4404\tIN\tA\t10.1.1.1\n" + "mocksrvr.example.com.\t4404\tIN\tRRSIG\tA 8 3 7200 20170420000000 20170330000000 1629 example.com. Hv4p37EF55LKBxUNYpnhWiEYqfmMct0z0WgDJyG5reqYfl+z4HX/kaoi Wr2iCYuYeB4Le7BgnMSb77UGHPWE7lCQ8z5gkgJ9rCDrooJzSTVdnHfw 1JQ7txRSp8Rj2GLf/L3Ytuo6nNZTV7bWUkfhOs61DAcOPHYZiX8rVhIh UAE=", + "web.example.com.\t4404\tIN\tA\t10.1.1.1\n" + "web.example.com.\t4404\tIN\tA\t10.2.2.2\n" + "web.example.com.\t4404\tIN\tA\t10.3.3.3\n" + "web.example.com.\t4404\tIN\tRRSIG\tA 8 3 7200 20170420000000 20170330000000 1629 example.com. Hv4p37EF55LKBxUNYpnhWiEYqfmMct0z0WgDJyG5reqYfl+z4HX/kaoi Wr2iCYuYeB4Le7BgnMSb77UGHPWE7lCQ8z5gkgJ9rCDrooJzSTVdnHfw 1JQ7txRSp8Rj2GLf/L3Ytuo6nNZTV7bWUkfhOs61DAcOPHYZiX8rVhIh UAE=", ] } @@ -485,94 +510,111 @@ class DNSlookupsCase(TestCase): for idx, test in enumerate(tests): rec_d[rec_t][idx] = RES_TMPL.format(test) - self._test_cmd_lookup(_lookup_drill, wrong_type=wrong_type, wrong=wrongs, right=rights, secure=secure) + self._test_cmd_lookup( + _lookup_drill, + wrong_type=wrong_type, + wrong=wrongs, + right=rights, + secure=secure, + ) def test_gai(self): # wrong type - self.assertRaises(ValueError, _lookup_gai, 'mockq', 'WRONG') + self.assertRaises(ValueError, _lookup_gai, "mockq", "WRONG") # wrong - with patch.object(socket, 'getaddrinfo', MagicMock(side_effect=socket.gaierror)): - for rec_t in ('A', 'AAAA'): - self.assertEqual(False, _lookup_gai('mockq', rec_t)) + with patch.object( + socket, "getaddrinfo", MagicMock(side_effect=socket.gaierror) + ): + for rec_t in ("A", "AAAA"): + self.assertEqual(False, _lookup_gai("mockq", rec_t)) # example returns from getaddrinfo right = { - 'A': [ - [(2, 3, 3, '', ('10.1.1.1', 0))], - [(2, 3, 3, '', ('10.1.1.1', 0)), - (2, 3, 3, '', ('10.2.2.2', 0)), - (2, 3, 3, '', ('10.3.3.3', 0))] + "A": [ + [(2, 3, 3, "", ("10.1.1.1", 0))], + [ + (2, 3, 3, "", ("10.1.1.1", 0)), + (2, 3, 3, "", ("10.2.2.2", 0)), + (2, 3, 3, "", ("10.3.3.3", 0)), + ], + ], + "AAAA": [ + [(10, 3, 3, "", ("2a00:a00:b01:c02:d03:e04:f05:111", 0, 0, 0))], + [ + (10, 3, 3, "", ("2a00:a00:b01:c02:d03:e04:f05:111", 0, 0, 0)), + (10, 3, 3, "", ("2a00:a00:b01:c02:d03:e04:f05:222", 0, 0, 0)), + (10, 3, 3, "", ("2a00:a00:b01:c02:d03:e04:f05:333", 0, 0, 0)), + ], ], - 'AAAA': [ - [(10, 3, 3, '', ('2a00:a00:b01:c02:d03:e04:f05:111', 0, 0, 0))], - [(10, 3, 3, '', ('2a00:a00:b01:c02:d03:e04:f05:111', 0, 0, 0)), - (10, 3, 3, '', ('2a00:a00:b01:c02:d03:e04:f05:222', 0, 0, 0)), - (10, 3, 3, '', ('2a00:a00:b01:c02:d03:e04:f05:333', 0, 0, 0))] - ] } for rec_t, tests in right.items(): - with patch.object(socket, 'getaddrinfo', MagicMock(side_effect=tests)): + with patch.object(socket, "getaddrinfo", MagicMock(side_effect=tests)): for test_res in self.RESULTS[rec_t]: self.assertEqual( - _lookup_gai('mockq', rec_t), test_res, - msg='Error parsing {0} returns'.format(rec_t) + _lookup_gai("mockq", rec_t), + test_res, + msg="Error parsing {0} returns".format(rec_t), ) def test_host(self): - wrong_type = {'retcode': 9, 'stderr': 'host: invalid type: WRONG'} + wrong_type = {"retcode": 9, "stderr": "host: invalid type: WRONG"} wrongs = [ - {'retcode': 9, 'stderr': ';; connection timed out; no servers could be reached'} + { + "retcode": 9, + "stderr": ";; connection timed out; no servers could be reached", + } ] - empty = {'stdout': 'www.example.com has no MX record'} + empty = {"stdout": "www.example.com has no MX record"} # example returns for host -t {rdtype} {name} rights = { - 'A': [ - 'mocksrvr.example.com has address 10.1.1.1', - 'web.example.com has address 10.1.1.1\n' - 'web.example.com has address 10.2.2.2\n' - 'web.example.com has address 10.3.3.3' + "A": [ + "mocksrvr.example.com has address 10.1.1.1", + "web.example.com has address 10.1.1.1\n" + "web.example.com has address 10.2.2.2\n" + "web.example.com has address 10.3.3.3", ], - 'AAAA': [ - 'mocksrvr.example.com has IPv6 address 2a00:a00:b01:c02:d03:e04:f05:111', - 'mocksrvr.example.com is an alias for web.example.com.\n' - 'web.example.com has IPv6 address 2a00:a00:b01:c02:d03:e04:f05:111\n' - 'web.example.com has IPv6 address 2a00:a00:b01:c02:d03:e04:f05:222\n' - 'web.example.com has IPv6 address 2a00:a00:b01:c02:d03:e04:f05:333' + "AAAA": [ + "mocksrvr.example.com has IPv6 address 2a00:a00:b01:c02:d03:e04:f05:111", + "mocksrvr.example.com is an alias for web.example.com.\n" + "web.example.com has IPv6 address 2a00:a00:b01:c02:d03:e04:f05:111\n" + "web.example.com has IPv6 address 2a00:a00:b01:c02:d03:e04:f05:222\n" + "web.example.com has IPv6 address 2a00:a00:b01:c02:d03:e04:f05:333", ], - 'CAA': [ + "CAA": [ 'example.com has CAA record 0 issue "exampleca.com"\n' 'example.com has CAA record 0 iodef "mailto:sslabuse@example.com"' ], - 'CNAME': [ - 'mocksrvr.example.com is an alias for web.example.com.' + "CNAME": ["mocksrvr.example.com is an alias for web.example.com."], + "MX": [ + "example.com mail is handled by 10 mx1.example.com.", + "example.com mail is handled by 10 mx1.example.com.\n" + "example.com mail is handled by 20 mx2.example.eu.\n" + "example.com mail is handled by 30 mx3.example.nl.", ], - 'MX': [ - 'example.com mail is handled by 10 mx1.example.com.', - 'example.com mail is handled by 10 mx1.example.com.\n' - 'example.com mail is handled by 20 mx2.example.eu.\n' - 'example.com mail is handled by 30 mx3.example.nl.' + "SSHFP": [ + "mocksrvr.example.com has SSHFP record 1 1 0AABDA8AF5418108E8A5D3F90F207226B2C89FBE\n" + "mocksrvr.example.com has SSHFP record 1 2 500CA871D8E255E01F1261A2370C4E5406B8712F19916D3AB9F86344 A67E5597\n" + "mocksrvr.example.com has SSHFP record 3 1 A3B605CE6F044617C6077C46A7CD5D17A767F0D5\n" + "mocksrvr.example.com has SSHFP record 4 2 0360D0A5A2FA550F972259E7374533ADD7AC8E5F303322A5B8E208BB C859AB1B" ], - 'SSHFP': [ - 'mocksrvr.example.com has SSHFP record 1 1 0AABDA8AF5418108E8A5D3F90F207226B2C89FBE\n' - 'mocksrvr.example.com has SSHFP record 1 2 500CA871D8E255E01F1261A2370C4E5406B8712F19916D3AB9F86344 A67E5597\n' - 'mocksrvr.example.com has SSHFP record 3 1 A3B605CE6F044617C6077C46A7CD5D17A767F0D5\n' - 'mocksrvr.example.com has SSHFP record 4 2 0360D0A5A2FA550F972259E7374533ADD7AC8E5F303322A5B8E208BB C859AB1B' - ], - 'TXT': [ + "TXT": [ 'example.com descriptive text "v=spf1 a include:_spf4.example.com include:mail.example.eu ip4:10.0.0.0/8 ip6:2a00:a00:b01::/48 ~all"' - ] + ], } - self._test_cmd_lookup(_lookup_host, wrong_type=wrong_type, wrong=wrongs, right=rights, empty=empty) + self._test_cmd_lookup( + _lookup_host, wrong_type=wrong_type, wrong=wrongs, right=rights, empty=empty + ) def test_nslookup(self): # all nslookup returns look like this - RES_TMPL = textwrap.dedent('''\ + RES_TMPL = textwrap.dedent( + """\ Server:\t\t10.11.12.13 Address:\t10.11.12.13#53 @@ -580,59 +622,68 @@ class DNSlookupsCase(TestCase): {} Authoritative answers can be found from: - ''') + """ + ) - wrong_type = {'stdout': 'unknown query type: WRONG' + - RES_TMPL.format('Name:\tmocksrvr.example.com\nAddress: 10.1.1.1')} + wrong_type = { + "stdout": "unknown query type: WRONG" + + RES_TMPL.format("Name:\tmocksrvr.example.com\nAddress: 10.1.1.1") + } wrongs = [ - {'retcode': 1, 'stdout': ';; connection timed out; no servers could be reached'} + { + "retcode": 1, + "stdout": ";; connection timed out; no servers could be reached", + } ] - empty = {'stdout': RES_TMPL.format( - "*** Can't find www.google.com: No answer")} + empty = {"stdout": RES_TMPL.format("*** Can't find www.google.com: No answer")} # Example returns of nslookup -query={rdype} {name} rights = { - 'A': [ - 'Name:\tmocksrvr.example.com\nAddress: 10.1.1.1', - 'Name:\tmocksrvr.example.com\nAddress: 10.1.1.1\n' - 'Name:\tweb.example.com\nAddress: 10.2.2.2\n' - 'Name:\tweb.example.com\nAddress: 10.3.3.3' + "A": [ + "Name:\tmocksrvr.example.com\nAddress: 10.1.1.1", + "Name:\tmocksrvr.example.com\nAddress: 10.1.1.1\n" + "Name:\tweb.example.com\nAddress: 10.2.2.2\n" + "Name:\tweb.example.com\nAddress: 10.3.3.3", ], - 'AAAA': [ - 'mocksrvr.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111', - 'mocksrvr.example.com\tcanonical name = web.example.com.\n' - 'web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111\n' - 'web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:222\n' - 'web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:333' + "AAAA": [ + "mocksrvr.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111", + "mocksrvr.example.com\tcanonical name = web.example.com.\n" + "web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:111\n" + "web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:222\n" + "web.example.com\thas AAAA address 2a00:a00:b01:c02:d03:e04:f05:333", ], - 'CAA': [ + "CAA": [ 'example.com\trdata_257 = 0 issue "exampleca.com"\n' 'example.com\trdata_257 = 0 iodef "mailto:sslabuse@example.com"' ], - 'CNAME': [ - 'mocksrvr.example.com\tcanonical name = web.example.com.' + "CNAME": ["mocksrvr.example.com\tcanonical name = web.example.com."], + "MX": [ + "example.com\tmail exchanger = 10 mx1.example.com.", + "example.com\tmail exchanger = 10 mx1.example.com.\n" + "example.com\tmail exchanger = 20 mx2.example.eu.\n" + "example.com\tmail exchanger = 30 mx3.example.nl.", ], - 'MX': [ - 'example.com\tmail exchanger = 10 mx1.example.com.', - 'example.com\tmail exchanger = 10 mx1.example.com.\n' - 'example.com\tmail exchanger = 20 mx2.example.eu.\n' - 'example.com\tmail exchanger = 30 mx3.example.nl.' + "SSHFP": [ + "mocksrvr.example.com\trdata_44 = 1 1 0AABDA8AF5418108E8A5D3F90F207226B2C89FBE\n" + "mocksrvr.example.com\trdata_44 = 1 2 500CA871D8E255E01F1261A2370C4E5406B8712F19916D3AB9F86344 A67E5597\n" + "mocksrvr.example.com\trdata_44 = 3 1 A3B605CE6F044617C6077C46A7CD5D17A767F0D5\n" + "mocksrvr.example.com\trdata_44 = 4 2 0360D0A5A2FA550F972259E7374533ADD7AC8E5F303322A5B8E208BB C859AB1B" ], - 'SSHFP': [ - 'mocksrvr.example.com\trdata_44 = 1 1 0AABDA8AF5418108E8A5D3F90F207226B2C89FBE\n' - 'mocksrvr.example.com\trdata_44 = 1 2 500CA871D8E255E01F1261A2370C4E5406B8712F19916D3AB9F86344 A67E5597\n' - 'mocksrvr.example.com\trdata_44 = 3 1 A3B605CE6F044617C6077C46A7CD5D17A767F0D5\n' - 'mocksrvr.example.com\trdata_44 = 4 2 0360D0A5A2FA550F972259E7374533ADD7AC8E5F303322A5B8E208BB C859AB1B' - ], - 'TXT': [ + "TXT": [ 'example.com\ttext = "v=spf1 a include:_spf4.example.com include:mail.example.eu ip4:10.0.0.0/8 ip6:2a00:a00:b01::/48 ~all"' - ] + ], } for rec_t, tests in rights.items(): for idx, test in enumerate(tests): rights[rec_t][idx] = RES_TMPL.format(test) - self._test_cmd_lookup(_lookup_nslookup, wrong_type=wrong_type, wrong=wrongs, right=rights, empty=empty) + self._test_cmd_lookup( + _lookup_nslookup, + wrong_type=wrong_type, + wrong=wrongs, + right=rights, + empty=empty, + ) diff --git a/tests/unit/utils/test_doc.py b/tests/unit/utils/test_doc.py index 261fb57ecca..6765fdf7a2d 100644 --- a/tests/unit/utils/test_doc.py +++ b/tests/unit/utils/test_doc.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Unit Tests for functions located in salt.utils.doc.py. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -14,22 +14,24 @@ from tests.support.unit import TestCase class DocUtilsTestCase(TestCase): - ''' + """ Test case for doc util. - ''' + """ def test_parse_docstring(self): - test_keystone_str = '''Management of Keystone users + test_keystone_str = """Management of Keystone users ============================ :depends: - keystoneclient Python module :configuration: See :py:mod:`salt.modules.keystone` for setup instructions. -''' +""" ret = salt.utils.doc.parse_docstring(test_keystone_str) - expected_dict = {'deps': ['keystoneclient'], - 'full': 'Management of Keystone users\n ' - '============================\n\n ' - ':depends: - keystoneclient Python module\n ' - ':configuration: See :py:mod:`salt.modules.keystone` for setup instructions.\n'} + expected_dict = { + "deps": ["keystoneclient"], + "full": "Management of Keystone users\n " + "============================\n\n " + ":depends: - keystoneclient Python module\n " + ":configuration: See :py:mod:`salt.modules.keystone` for setup instructions.\n", + } self.assertDictEqual(ret, expected_dict) diff --git a/tests/unit/utils/test_docker.py b/tests/unit/utils/test_docker.py index 857e3d3559c..a174909b1f9 100644 --- a/tests/unit/utils/test_docker.py +++ b/tests/unit/utils/test_docker.py @@ -1,33 +1,34 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.utils.test_docker ============================ Test the funcs in salt.utils.docker and salt.utils.docker.translate -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals + import copy import functools import logging import os -log = logging.getLogger(__name__) - -# Import Salt Testing Libs -from tests.support.unit import TestCase - # Import salt libs import salt.config import salt.loader -import salt.utils.platform import salt.utils.docker.translate.container import salt.utils.docker.translate.network -from salt.utils.docker.translate import helpers as translate_helpers +import salt.utils.platform from salt.exceptions import CommandExecutionError # Import 3rd-party libs from salt.ext import six +from salt.utils.docker.translate import helpers as translate_helpers + +# Import Salt Testing Libs +from tests.support.unit import TestCase + +log = logging.getLogger(__name__) class Assert(object): @@ -37,7 +38,9 @@ class Assert(object): def __call__(self, func): self.func = func return functools.wraps(func)( - lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) # pylint: disable=W0108 + # pylint: disable=unnecessary-lambda + lambda testcase, *args, **kwargs: self.wrap(testcase, *args, **kwargs) + # pylint: enable=unnecessary-lambda ) def wrap(self, *args, **kwargs): @@ -48,34 +51,29 @@ class Assert(object): # Using file paths here because "volumes" must be passed through this # set of assertions and it requires absolute paths. if salt.utils.platform.is_windows(): - data = [r'c:\foo', r'c:\bar', r'c:\baz'] + data = [r"c:\foo", r"c:\bar", r"c:\baz"] else: - data = ['/foo', '/bar', '/baz'] + data = ["/foo", "/bar", "/baz"] for item in (name, alias): if item is None: continue testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - **{item: ','.join(data)} + self.translator, **{item: ",".join(data)} ), - testcase.apply_defaults({name: data}) + testcase.apply_defaults({name: data}), ) testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: data} - ), - testcase.apply_defaults({name: data}) + salt.utils.docker.translate_input(self.translator, **{item: data}), + testcase.apply_defaults({name: data}), ) - if name != 'volumes': + if name != "volumes": # Test coercing to string testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - **{item: ['one', 2]} + self.translator, **{item: ["one", 2]} ), - testcase.apply_defaults({name: ['one', '2']}) + testcase.apply_defaults({name: ["one", "2"]}), ) if alias is not None: # Test collision @@ -84,62 +82,54 @@ class Assert(object): test_kwargs = {name: data, alias: sorted(data)} testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), - testcase.apply_defaults({name: test_kwargs[name]}) + testcase.apply_defaults({name: test_kwargs[name]}), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) def test_key_value(self, testcase, name, delimiter): - ''' + """ Common logic for key/value pair testing. IP address validation is turned off here, and must be done separately in the wrapped function. - ''' + """ alias = self.translator.ALIASES_REVMAP.get(name) - expected = {'foo': 'bar', 'baz': 'qux'} - vals = 'foo{0}bar,baz{0}qux'.format(delimiter) + expected = {"foo": "bar", "baz": "qux"} + vals = "foo{0}bar,baz{0}qux".format(delimiter) for item in (name, alias): if item is None: continue - for val in (vals, vals.split(',')): + for val in (vals, vals.split(",")): testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=False, - **{item: val} + self.translator, validate_ip_addrs=False, **{item: val} ), - testcase.apply_defaults({name: expected}) + testcase.apply_defaults({name: expected}), ) # Dictionary input testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=False, - **{item: expected} + self.translator, validate_ip_addrs=False, **{item: expected} ), - testcase.apply_defaults({name: expected}) + testcase.apply_defaults({name: expected}), ) # "Dictlist" input from states testcase.assertEqual( salt.utils.docker.translate_input( self.translator, validate_ip_addrs=False, - **{item: [{'foo': 'bar'}, {'baz': 'qux'}]} + **{item: [{"foo": "bar"}, {"baz": "qux"}]} ), - testcase.apply_defaults({name: expected}) + testcase.apply_defaults({name: expected}), ) if alias is not None: # Test collision - test_kwargs = {name: vals, alias: 'hello{0}world'.format(delimiter)} + test_kwargs = {name: vals, alias: "hello{0}world".format(delimiter)} testcase.assertEqual( salt.utils.docker.translate_input( self.translator, @@ -147,11 +137,11 @@ class Assert(object): ignore_collisions=True, **test_kwargs ), - testcase.apply_defaults({name: expected}) + testcase.apply_defaults({name: expected}), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( self.translator, validate_ip_addrs=False, @@ -161,9 +151,10 @@ class Assert(object): class assert_bool(Assert): - ''' + """ Test a boolean value - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] @@ -172,54 +163,42 @@ class assert_bool(Assert): if item is None: continue testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: True} - ), - testcase.apply_defaults({name: True}) + salt.utils.docker.translate_input(self.translator, **{item: True}), + testcase.apply_defaults({name: True}), ) # These two are contrived examples, but they will test bool-ifying # a non-bool value to ensure proper input format. testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: 'foo'} - ), - testcase.apply_defaults({name: True}) + salt.utils.docker.translate_input(self.translator, **{item: "foo"}), + testcase.apply_defaults({name: True}), ) testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: 0} - ), - testcase.apply_defaults({name: False}) + salt.utils.docker.translate_input(self.translator, **{item: 0}), + testcase.apply_defaults({name: False}), ) if alias is not None: # Test collision test_kwargs = {name: True, alias: False} testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), - testcase.apply_defaults({name: test_kwargs[name]}) + testcase.apply_defaults({name: test_kwargs[name]}), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) return self.func(testcase, *args, **kwargs) class assert_int(Assert): - ''' + """ Test an integer value - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] @@ -227,48 +206,39 @@ class assert_int(Assert): for item in (name, alias): if item is None: continue - for val in (100, '100'): + for val in (100, "100"): testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: val} - ), - testcase.apply_defaults({name: 100}) + salt.utils.docker.translate_input(self.translator, **{item: val}), + testcase.apply_defaults({name: 100}), ) # Error case: non-numeric value passed with testcase.assertRaisesRegex( - CommandExecutionError, - "'foo' is not an integer"): - salt.utils.docker.translate_input( - self.translator, - **{item: 'foo'} - ) + CommandExecutionError, "'foo' is not an integer" + ): + salt.utils.docker.translate_input(self.translator, **{item: "foo"}) if alias is not None: # Test collision test_kwargs = {name: 100, alias: 200} testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), - testcase.apply_defaults({name: test_kwargs[name]}) + testcase.apply_defaults({name: test_kwargs[name]}), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) return self.func(testcase, *args, **kwargs) class assert_string(Assert): - ''' + """ Test that item is a string or is converted to one - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] @@ -276,54 +246,45 @@ class assert_string(Assert): # Using file paths here because "working_dir" must be passed through # this set of assertions and it requires absolute paths. if salt.utils.platform.is_windows(): - data = r'c:\foo' + data = r"c:\foo" else: - data = '/foo' + data = "/foo" for item in (name, alias): if item is None: continue testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: data} - ), - testcase.apply_defaults({name: data}) + salt.utils.docker.translate_input(self.translator, **{item: data}), + testcase.apply_defaults({name: data}), ) - if name != 'working_dir': + if name != "working_dir": # Test coercing to string testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: 123} - ), - testcase.apply_defaults({name: '123'}) + salt.utils.docker.translate_input(self.translator, **{item: 123}), + testcase.apply_defaults({name: "123"}), ) if alias is not None: # Test collision test_kwargs = {name: data, alias: data} testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), - testcase.apply_defaults({name: test_kwargs[name]}) + testcase.apply_defaults({name: test_kwargs[name]}), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) return self.func(testcase, *args, **kwargs) class assert_int_or_string(Assert): - ''' + """ Test an integer or string value - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] @@ -332,45 +293,36 @@ class assert_int_or_string(Assert): if item is None: continue testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: 100} - ), - testcase.apply_defaults({name: 100}) + salt.utils.docker.translate_input(self.translator, **{item: 100}), + testcase.apply_defaults({name: 100}), ) testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: '100M'} - ), - testcase.apply_defaults({name: '100M'}) + salt.utils.docker.translate_input(self.translator, **{item: "100M"}), + testcase.apply_defaults({name: "100M"}), ) if alias is not None: # Test collision - test_kwargs = {name: 100, alias: '100M'} + test_kwargs = {name: 100, alias: "100M"} testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), - testcase.apply_defaults({name: test_kwargs[name]}) + testcase.apply_defaults({name: test_kwargs[name]}), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) return self.func(testcase, *args, **kwargs) class assert_stringlist(Assert): - ''' + """ Test a comma-separated or Python list of strings - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] @@ -379,24 +331,22 @@ class assert_stringlist(Assert): class assert_dict(Assert): - ''' + """ Dictionaries should be untouched, dictlists should be repacked and end up as a single dictionary. - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] alias = self.translator.ALIASES_REVMAP.get(name) - expected = {'foo': 'bar', 'baz': 'qux'} + expected = {"foo": "bar", "baz": "qux"} for item in (name, alias): if item is None: continue testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: expected} - ), - testcase.apply_defaults({name: expected}) + salt.utils.docker.translate_input(self.translator, **{item: expected}), + testcase.apply_defaults({name: expected}), ) # "Dictlist" input from states testcase.assertEqual( @@ -404,44 +354,38 @@ class assert_dict(Assert): self.translator, **{item: [{x: y} for x, y in six.iteritems(expected)]} ), - testcase.apply_defaults({name: expected}) + testcase.apply_defaults({name: expected}), ) # Error case: non-dictionary input with testcase.assertRaisesRegex( - CommandExecutionError, - "'foo' is not a dictionary"): - salt.utils.docker.translate_input( - self.translator, - **{item: 'foo'} - ) + CommandExecutionError, "'foo' is not a dictionary" + ): + salt.utils.docker.translate_input(self.translator, **{item: "foo"}) if alias is not None: # Test collision - test_kwargs = {name: 'foo', alias: 'bar'} + test_kwargs = {name: "foo", alias: "bar"} testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), - testcase.apply_defaults({name: test_kwargs[name]}) + testcase.apply_defaults({name: test_kwargs[name]}), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) return self.func(testcase, *args, **kwargs) class assert_cmd(Assert): - ''' + """ Test for a string, or a comma-separated or Python list of strings. This is different from a stringlist in that we do not do any splitting. This decorator is used both by the "command" and "entrypoint" arguments. - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] @@ -450,76 +394,66 @@ class assert_cmd(Assert): if item is None: continue testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: 'foo bar'} - ), - testcase.apply_defaults({name: 'foo bar'}) + salt.utils.docker.translate_input(self.translator, **{item: "foo bar"}), + testcase.apply_defaults({name: "foo bar"}), ) testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - **{item: ['foo', 'bar']} + self.translator, **{item: ["foo", "bar"]} ), - testcase.apply_defaults({name: ['foo', 'bar']}) + testcase.apply_defaults({name: ["foo", "bar"]}), ) # Test coercing to string testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: 123} - ), - testcase.apply_defaults({name: '123'}) + salt.utils.docker.translate_input(self.translator, **{item: 123}), + testcase.apply_defaults({name: "123"}), ) testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - **{item: ['one', 2]} + self.translator, **{item: ["one", 2]} ), - testcase.apply_defaults({name: ['one', '2']}) + testcase.apply_defaults({name: ["one", "2"]}), ) if alias is not None: # Test collision - test_kwargs = {name: 'foo', alias: 'bar'} + test_kwargs = {name: "foo", alias: "bar"} testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), - testcase.apply_defaults({name: test_kwargs[name]}) + testcase.apply_defaults({name: test_kwargs[name]}), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) return self.func(testcase, *args, **kwargs) class assert_key_colon_value(Assert): - ''' + """ Test a key/value pair with parameters passed as key:value pairs - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] - self.test_key_value(testcase, name, ':') + self.test_key_value(testcase, name, ":") return self.func(testcase, *args, **kwargs) class assert_key_equals_value(Assert): - ''' + """ Test a key/value pair with parameters passed as key=value pairs - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] - self.test_key_value(testcase, name, '=') - if name == 'labels': + self.test_key_value(testcase, name, "=") + if name == "labels": self.test_stringlist(testcase, name) return self.func(testcase, *args, **kwargs) @@ -529,37 +463,33 @@ class assert_labels(Assert): # Strip off the "test_" from the function name name = self.func.__name__[5:] alias = self.translator.ALIASES_REVMAP.get(name) - labels = ['foo', 'bar=baz', {'hello': 'world'}] - expected = {'foo': '', 'bar': 'baz', 'hello': 'world'} + labels = ["foo", "bar=baz", {"hello": "world"}] + expected = {"foo": "", "bar": "baz", "hello": "world"} for item in (name, alias): if item is None: continue testcase.assertEqual( - salt.utils.docker.translate_input( - self.translator, - **{item: labels} - ), - testcase.apply_defaults({name: expected}) + salt.utils.docker.translate_input(self.translator, **{item: labels}), + testcase.apply_defaults({name: expected}), ) # Error case: Passed a mutli-element dict in dictlist bad_labels = copy.deepcopy(labels) - bad_labels[-1]['bad'] = 'input' + bad_labels[-1]["bad"] = "input" with testcase.assertRaisesRegex( - CommandExecutionError, r'Invalid label\(s\)'): - salt.utils.docker.translate_input( - self.translator, - **{item: bad_labels} - ) + CommandExecutionError, r"Invalid label\(s\)" + ): + salt.utils.docker.translate_input(self.translator, **{item: bad_labels}) return self.func(testcase, *args, **kwargs) class assert_device_rates(Assert): - ''' + """ Tests for device_{read,write}_{bps,iops}. The bps values have a "Rate" value expressed in bytes/kb/mb/gb, while the iops values have a "Rate" expressed as a simple integer. - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] @@ -569,134 +499,142 @@ class assert_device_rates(Assert): continue # Error case: Not an absolute path - path = os.path.join('foo', 'bar', 'baz') + path = os.path.join("foo", "bar", "baz") with testcase.assertRaisesRegex( - CommandExecutionError, - "Path '{0}' is not absolute".format(path.replace('\\', '\\\\'))): + CommandExecutionError, + "Path '{0}' is not absolute".format(path.replace("\\", "\\\\")), + ): salt.utils.docker.translate_input( - self.translator, - **{item: '{0}:1048576'.format(path)} + self.translator, **{item: "{0}:1048576".format(path)} ) - if name.endswith('_bps'): + if name.endswith("_bps"): # Both integer bytes and a string providing a shorthand for kb, # mb, or gb can be used, so we need to test for both. - expected = ( - {}, [] - ) - vals = '/dev/sda:1048576,/dev/sdb:1048576' - for val in (vals, vals.split(',')): + expected = ({}, []) + vals = "/dev/sda:1048576,/dev/sdb:1048576" + for val in (vals, vals.split(",")): testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - **{item: val} + self.translator, **{item: val} ), testcase.apply_defaults( - {name: [{'Path': '/dev/sda', 'Rate': 1048576}, - {'Path': '/dev/sdb', 'Rate': 1048576}]} - ) + { + name: [ + {"Path": "/dev/sda", "Rate": 1048576}, + {"Path": "/dev/sdb", "Rate": 1048576}, + ] + } + ), ) - vals = '/dev/sda:1mb,/dev/sdb:5mb' - for val in (vals, vals.split(',')): + vals = "/dev/sda:1mb,/dev/sdb:5mb" + for val in (vals, vals.split(",")): testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - **{item: val} + self.translator, **{item: val} ), testcase.apply_defaults( - {name: [{'Path': '/dev/sda', 'Rate': '1mb'}, - {'Path': '/dev/sdb', 'Rate': '5mb'}]} - ) + { + name: [ + {"Path": "/dev/sda", "Rate": "1mb"}, + {"Path": "/dev/sdb", "Rate": "5mb"}, + ] + } + ), ) if alias is not None: # Test collision test_kwargs = { - name: '/dev/sda:1048576,/dev/sdb:1048576', - alias: '/dev/sda:1mb,/dev/sdb:5mb' + name: "/dev/sda:1048576,/dev/sdb:1048576", + alias: "/dev/sda:1mb,/dev/sdb:5mb", } testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), testcase.apply_defaults( - {name: [{'Path': '/dev/sda', 'Rate': 1048576}, - {'Path': '/dev/sdb', 'Rate': 1048576}]} - ) + { + name: [ + {"Path": "/dev/sda", "Rate": 1048576}, + {"Path": "/dev/sdb", "Rate": 1048576}, + ] + } + ), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) else: # The "Rate" value must be an integer - vals = '/dev/sda:1000,/dev/sdb:500' - for val in (vals, vals.split(',')): + vals = "/dev/sda:1000,/dev/sdb:500" + for val in (vals, vals.split(",")): testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - **{item: val} + self.translator, **{item: val} ), testcase.apply_defaults( - {name: [{'Path': '/dev/sda', 'Rate': 1000}, - {'Path': '/dev/sdb', 'Rate': 500}]} - ) + { + name: [ + {"Path": "/dev/sda", "Rate": 1000}, + {"Path": "/dev/sdb", "Rate": 500}, + ] + } + ), ) # Test non-integer input expected = ( {}, - {item: 'Rate \'5mb\' for path \'/dev/sdb\' is non-numeric'}, - [] + {item: "Rate '5mb' for path '/dev/sdb' is non-numeric"}, + [], ) - vals = '/dev/sda:1000,/dev/sdb:5mb' - for val in (vals, vals.split(',')): + vals = "/dev/sda:1000,/dev/sdb:5mb" + for val in (vals, vals.split(",")): with testcase.assertRaisesRegex( - CommandExecutionError, - "Rate '5mb' for path '/dev/sdb' is non-numeric"): + CommandExecutionError, + "Rate '5mb' for path '/dev/sdb' is non-numeric", + ): salt.utils.docker.translate_input( - self.translator, - **{item: val} + self.translator, **{item: val} ) if alias is not None: # Test collision test_kwargs = { - name: '/dev/sda:1000,/dev/sdb:500', - alias: '/dev/sda:888,/dev/sdb:999' + name: "/dev/sda:1000,/dev/sdb:500", + alias: "/dev/sda:888,/dev/sdb:999", } testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), testcase.apply_defaults( - {name: [{'Path': '/dev/sda', 'Rate': 1000}, - {'Path': '/dev/sdb', 'Rate': 500}]} - ) + { + name: [ + {"Path": "/dev/sda", "Rate": 1000}, + {"Path": "/dev/sdb", "Rate": 500}, + ] + } + ), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) return self.func(testcase, *args, **kwargs) class assert_subnet(Assert): - ''' + """ Test an IPv4 or IPv6 subnet - ''' + """ + def wrap(self, testcase, *args, **kwargs): # pylint: disable=arguments-differ # Strip off the "test_" from the function name name = self.func.__name__[5:] @@ -704,59 +642,55 @@ class assert_subnet(Assert): for item in (name, alias): if item is None: continue - for val in ('127.0.0.1/32', '::1/128'): - log.debug('Verifying \'%s\' is a valid subnet', val) + for val in ("127.0.0.1/32", "::1/128"): + log.debug("Verifying '%s' is a valid subnet", val) testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=True, - **{item: val} + self.translator, validate_ip_addrs=True, **{item: val} ), - testcase.apply_defaults({name: val}) + testcase.apply_defaults({name: val}), ) # Error case: invalid subnet caught by validation - for val in ('127.0.0.1', '999.999.999.999/24', '10.0.0.0/33', - '::1', 'feaz::1/128', '::1/129'): - log.debug('Verifying \'%s\' is not a valid subnet', val) + for val in ( + "127.0.0.1", + "999.999.999.999/24", + "10.0.0.0/33", + "::1", + "feaz::1/128", + "::1/129", + ): + log.debug("Verifying '%s' is not a valid subnet", val) with testcase.assertRaisesRegex( - CommandExecutionError, - "'{0}' is not a valid subnet".format(val)): + CommandExecutionError, "'{0}' is not a valid subnet".format(val) + ): salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=True, - **{item: val} + self.translator, validate_ip_addrs=True, **{item: val} ) # This is not valid input but it will test whether or not subnet # validation happened - val = 'foo' + val = "foo" testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=False, - **{item: val} + self.translator, validate_ip_addrs=False, **{item: val} ), - testcase.apply_defaults({name: val}) + testcase.apply_defaults({name: val}), ) if alias is not None: # Test collision - test_kwargs = {name: '10.0.0.0/24', alias: '192.168.50.128/25'} + test_kwargs = {name: "10.0.0.0/24", alias: "192.168.50.128/25"} testcase.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), - testcase.apply_defaults({name: test_kwargs[name]}) + testcase.apply_defaults({name: test_kwargs[name]}), ) with testcase.assertRaisesRegex( - CommandExecutionError, - 'is an alias for.+cannot both be used'): + CommandExecutionError, "is an alias for.+cannot both be used" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) return self.func(testcase, *args, **kwargs) @@ -767,7 +701,7 @@ class TranslateBase(TestCase): def apply_defaults(self, ret, skip_translate=None): if skip_translate is not True: - defaults = getattr(self.translator, 'DEFAULTS', {}) + defaults = getattr(self.translator, "DEFAULTS", {}) for key, val in six.iteritems(defaults): if key not in ret: ret[key] = val @@ -775,7 +709,7 @@ class TranslateBase(TestCase): @staticmethod def normalize_ports(ret): - ''' + """ When we translate exposed ports, we can end up with a mixture of ints (representing TCP ports) and tuples (representing UDP ports). Python 2 will sort an iterable containing these mixed types, but Python 3 will @@ -784,46 +718,45 @@ class TranslateBase(TestCase): the expected results in the test. This helper should only be needed for port_bindings and ports. - ''' - if 'ports' in ret[0]: + """ + if "ports" in ret[0]: tcp_ports = [] udp_ports = [] - for item in ret[0]['ports']: + for item in ret[0]["ports"]: if isinstance(item, six.integer_types): tcp_ports.append(item) else: udp_ports.append(item) - ret[0]['ports'] = sorted(tcp_ports) + sorted(udp_ports) + ret[0]["ports"] = sorted(tcp_ports) + sorted(udp_ports) return ret def tearDown(self): - ''' + """ Test skip_translate kwarg - ''' - name = self.id().split('.')[-1][5:] + """ + name = self.id().split(".")[-1][5:] # The below is not valid input for the Docker API, but these # assertions confirm that we successfully skipped translation. for val in (True, name, [name]): self.assertEqual( salt.utils.docker.translate_input( - self.translator, - skip_translate=val, - **{name: 'foo'} + self.translator, skip_translate=val, **{name: "foo"} ), - self.apply_defaults({name: 'foo'}, skip_translate=val) + self.apply_defaults({name: "foo"}, skip_translate=val), ) class TranslateContainerInputTestCase(TranslateBase): - ''' + """ Tests for salt.utils.docker.translate_input(), invoked using salt.utils.docker.translate.container as the translator module. - ''' + """ + translator = salt.utils.docker.translate.container @staticmethod def normalize_ports(ret): - ''' + """ When we translate exposed ports, we can end up with a mixture of ints (representing TCP ports) and tuples (representing UDP ports). Python 2 will sort an iterable containing these mixed types, but Python 3 will @@ -832,1186 +765,1195 @@ class TranslateContainerInputTestCase(TranslateBase): the expected results in the test. This helper should only be needed for port_bindings and ports. - ''' - if 'ports' in ret: + """ + if "ports" in ret: tcp_ports = [] udp_ports = [] - for item in ret['ports']: + for item in ret["ports"]: if isinstance(item, six.integer_types): tcp_ports.append(item) else: udp_ports.append(item) - ret['ports'] = sorted(tcp_ports) + sorted(udp_ports) + ret["ports"] = sorted(tcp_ports) + sorted(udp_ports) return ret @assert_bool(salt.utils.docker.translate.container) def test_auto_remove(self): - ''' + """ Should be a bool or converted to one - ''' + """ def test_binds(self): - ''' + """ Test the "binds" kwarg. Any volumes not defined in the "volumes" kwarg should be added to the results. - ''' + """ self.assertEqual( salt.utils.docker.translate_input( - self.translator, - binds='/srv/www:/var/www:ro', - volumes='/testing'), - {'binds': ['/srv/www:/var/www:ro'], - 'volumes': ['/testing', '/var/www']} + self.translator, binds="/srv/www:/var/www:ro", volumes="/testing" + ), + {"binds": ["/srv/www:/var/www:ro"], "volumes": ["/testing", "/var/www"]}, + ) + self.assertEqual( + salt.utils.docker.translate_input( + self.translator, binds=["/srv/www:/var/www:ro"], volumes="/testing" + ), + {"binds": ["/srv/www:/var/www:ro"], "volumes": ["/testing", "/var/www"]}, ) self.assertEqual( salt.utils.docker.translate_input( self.translator, - binds=['/srv/www:/var/www:ro'], - volumes='/testing'), - {'binds': ['/srv/www:/var/www:ro'], - 'volumes': ['/testing', '/var/www']} - ) - self.assertEqual( - salt.utils.docker.translate_input( - self.translator, - binds={'/srv/www': {'bind': '/var/www', 'mode': 'ro'}}, - volumes='/testing'), - {'binds': {'/srv/www': {'bind': '/var/www', 'mode': 'ro'}}, - 'volumes': ['/testing', '/var/www']} + binds={"/srv/www": {"bind": "/var/www", "mode": "ro"}}, + volumes="/testing", + ), + { + "binds": {"/srv/www": {"bind": "/var/www", "mode": "ro"}}, + "volumes": ["/testing", "/var/www"], + }, ) @assert_int(salt.utils.docker.translate.container) def test_blkio_weight(self): - ''' + """ Should be an int or converted to one - ''' + """ def test_blkio_weight_device(self): - ''' + """ Should translate a list of PATH:WEIGHT pairs to a list of dictionaries with the following format: {'Path': PATH, 'Weight': WEIGHT} - ''' - for val in ('/dev/sda:100,/dev/sdb:200', - ['/dev/sda:100', '/dev/sdb:200']): + """ + for val in ("/dev/sda:100,/dev/sdb:200", ["/dev/sda:100", "/dev/sdb:200"]): self.assertEqual( salt.utils.docker.translate_input( - self.translator, - blkio_weight_device='/dev/sda:100,/dev/sdb:200' + self.translator, blkio_weight_device="/dev/sda:100,/dev/sdb:200" ), - {'blkio_weight_device': [{'Path': '/dev/sda', 'Weight': 100}, - {'Path': '/dev/sdb', 'Weight': 200}]} + { + "blkio_weight_device": [ + {"Path": "/dev/sda", "Weight": 100}, + {"Path": "/dev/sdb", "Weight": 200}, + ] + }, ) # Error cases with self.assertRaisesRegex( - CommandExecutionError, - r"'foo' contains 1 value\(s\) \(expected 2\)"): + CommandExecutionError, r"'foo' contains 1 value\(s\) \(expected 2\)" + ): salt.utils.docker.translate_input( - self.translator, - blkio_weight_device='foo' + self.translator, blkio_weight_device="foo" ) with self.assertRaisesRegex( - CommandExecutionError, - r"'foo:bar:baz' contains 3 value\(s\) \(expected 2\)"): + CommandExecutionError, r"'foo:bar:baz' contains 3 value\(s\) \(expected 2\)" + ): salt.utils.docker.translate_input( - self.translator, - blkio_weight_device='foo:bar:baz' + self.translator, blkio_weight_device="foo:bar:baz" ) with self.assertRaisesRegex( - CommandExecutionError, - r"Weight 'foo' for path '/dev/sdb' is not an integer"): + CommandExecutionError, r"Weight 'foo' for path '/dev/sdb' is not an integer" + ): salt.utils.docker.translate_input( - self.translator, - blkio_weight_device=['/dev/sda:100', '/dev/sdb:foo'] + self.translator, blkio_weight_device=["/dev/sda:100", "/dev/sdb:foo"] ) @assert_stringlist(salt.utils.docker.translate.container) def test_cap_add(self): - ''' + """ Should be a list of strings or converted to one - ''' + """ @assert_stringlist(salt.utils.docker.translate.container) def test_cap_drop(self): - ''' + """ Should be a list of strings or converted to one - ''' + """ @assert_cmd(salt.utils.docker.translate.container) def test_command(self): - ''' + """ Can either be a string or a comma-separated or Python list of strings. - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_cpuset_cpus(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_cpuset_mems(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_int(salt.utils.docker.translate.container) def test_cpu_group(self): - ''' + """ Should be an int or converted to one - ''' + """ @assert_int(salt.utils.docker.translate.container) def test_cpu_period(self): - ''' + """ Should be an int or converted to one - ''' + """ @assert_int(salt.utils.docker.translate.container) def test_cpu_shares(self): - ''' + """ Should be an int or converted to one - ''' + """ @assert_bool(salt.utils.docker.translate.container) def test_detach(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_device_rates(salt.utils.docker.translate.container) def test_device_read_bps(self): - ''' + """ CLI input is a list of PATH:RATE pairs, but the API expects a list of dictionaries in the format [{'Path': path, 'Rate': rate}] - ''' + """ @assert_device_rates(salt.utils.docker.translate.container) def test_device_read_iops(self): - ''' + """ CLI input is a list of PATH:RATE pairs, but the API expects a list of dictionaries in the format [{'Path': path, 'Rate': rate}] - ''' + """ @assert_device_rates(salt.utils.docker.translate.container) def test_device_write_bps(self): - ''' + """ CLI input is a list of PATH:RATE pairs, but the API expects a list of dictionaries in the format [{'Path': path, 'Rate': rate}] - ''' + """ @assert_device_rates(salt.utils.docker.translate.container) def test_device_write_iops(self): - ''' + """ CLI input is a list of PATH:RATE pairs, but the API expects a list of dictionaries in the format [{'Path': path, 'Rate': rate}] - ''' + """ @assert_stringlist(salt.utils.docker.translate.container) def test_devices(self): - ''' + """ Should be a list of strings or converted to one - ''' + """ @assert_stringlist(salt.utils.docker.translate.container) def test_dns_opt(self): - ''' + """ Should be a list of strings or converted to one - ''' + """ @assert_stringlist(salt.utils.docker.translate.container) def test_dns_search(self): - ''' + """ Should be a list of strings or converted to one - ''' + """ def test_dns(self): - ''' + """ While this is a stringlist, it also supports IP address validation, so it can't use the test_stringlist decorator because we need to test both with and without validation, and it isn't necessary to make all other stringlist tests also do that same kind of testing. - ''' - for val in ('8.8.8.8,8.8.4.4', ['8.8.8.8', '8.8.4.4']): + """ + for val in ("8.8.8.8,8.8.4.4", ["8.8.8.8", "8.8.4.4"]): self.assertEqual( salt.utils.docker.translate_input( - self.translator, - dns=val, - validate_ip_addrs=True, + self.translator, dns=val, validate_ip_addrs=True, ), - {'dns': ['8.8.8.8', '8.8.4.4']} + {"dns": ["8.8.8.8", "8.8.4.4"]}, ) # Error case: invalid IP address caught by validation - for val in ('8.8.8.888,8.8.4.4', ['8.8.8.888', '8.8.4.4']): + for val in ("8.8.8.888,8.8.4.4", ["8.8.8.888", "8.8.4.4"]): with self.assertRaisesRegex( - CommandExecutionError, - r"'8.8.8.888' is not a valid IP address"): + CommandExecutionError, r"'8.8.8.888' is not a valid IP address" + ): salt.utils.docker.translate_input( - self.translator, - dns=val, - validate_ip_addrs=True, + self.translator, dns=val, validate_ip_addrs=True, ) # This is not valid input but it will test whether or not IP address # validation happened. - for val in ('foo,bar', ['foo', 'bar']): + for val in ("foo,bar", ["foo", "bar"]): self.assertEqual( salt.utils.docker.translate_input( - self.translator, - dns=val, - validate_ip_addrs=False, + self.translator, dns=val, validate_ip_addrs=False, ), - {'dns': ['foo', 'bar']} + {"dns": ["foo", "bar"]}, ) @assert_string(salt.utils.docker.translate.container) def test_domainname(self): - ''' + """ Should be a list of strings or converted to one - ''' + """ @assert_cmd(salt.utils.docker.translate.container) def test_entrypoint(self): - ''' + """ Can either be a string or a comma-separated or Python list of strings. - ''' + """ @assert_key_equals_value(salt.utils.docker.translate.container) def test_environment(self): - ''' + """ Can be passed in several formats but must end up as a dictionary mapping keys to values - ''' + """ def test_extra_hosts(self): - ''' + """ Can be passed as a list of key:value pairs but can't be simply tested using @assert_key_colon_value since we need to test both with and without IP address validation. - ''' - for val in ('web1:10.9.8.7,web2:10.9.8.8', - ['web1:10.9.8.7', 'web2:10.9.8.8']): + """ + for val in ("web1:10.9.8.7,web2:10.9.8.8", ["web1:10.9.8.7", "web2:10.9.8.8"]): self.assertEqual( salt.utils.docker.translate_input( - self.translator, - extra_hosts=val, - validate_ip_addrs=True, + self.translator, extra_hosts=val, validate_ip_addrs=True, ), - {'extra_hosts': {'web1': '10.9.8.7', 'web2': '10.9.8.8'}} + {"extra_hosts": {"web1": "10.9.8.7", "web2": "10.9.8.8"}}, ) # Error case: invalid IP address caught by validation - for val in ('web1:10.9.8.299,web2:10.9.8.8', - ['web1:10.9.8.299', 'web2:10.9.8.8']): + for val in ( + "web1:10.9.8.299,web2:10.9.8.8", + ["web1:10.9.8.299", "web2:10.9.8.8"], + ): with self.assertRaisesRegex( - CommandExecutionError, - r"'10.9.8.299' is not a valid IP address"): + CommandExecutionError, r"'10.9.8.299' is not a valid IP address" + ): salt.utils.docker.translate_input( - self.translator, - extra_hosts=val, - validate_ip_addrs=True, + self.translator, extra_hosts=val, validate_ip_addrs=True, ) # This is not valid input but it will test whether or not IP address # validation happened. - for val in ('foo:bar,baz:qux', ['foo:bar', 'baz:qux']): + for val in ("foo:bar,baz:qux", ["foo:bar", "baz:qux"]): self.assertEqual( salt.utils.docker.translate_input( - self.translator, - extra_hosts=val, - validate_ip_addrs=False, + self.translator, extra_hosts=val, validate_ip_addrs=False, ), - {'extra_hosts': {'foo': 'bar', 'baz': 'qux'}} + {"extra_hosts": {"foo": "bar", "baz": "qux"}}, ) @assert_stringlist(salt.utils.docker.translate.container) def test_group_add(self): - ''' + """ Should be a list of strings or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_hostname(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_ipc_mode(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_isolation(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_labels(salt.utils.docker.translate.container) def test_labels(self): - ''' + """ Can be passed as a list of key=value pairs or a dictionary, and must ultimately end up as a dictionary. - ''' + """ @assert_key_colon_value(salt.utils.docker.translate.container) def test_links(self): - ''' + """ Can be passed as a list of key:value pairs or a dictionary, and must ultimately end up as a dictionary. - ''' + """ def test_log_config(self): - ''' + """ This is a mixture of log_driver and log_opt, which get combined into a dictionary. log_driver is a simple string, but log_opt can be passed in several ways, so we need to test them all. - ''' + """ expected = ( - {'log_config': {'Type': 'foo', - 'Config': {'foo': 'bar', 'baz': 'qux'}}}, - {}, [] + {"log_config": {"Type": "foo", "Config": {"foo": "bar", "baz": "qux"}}}, + {}, + [], ) - for val in ('foo=bar,baz=qux', - ['foo=bar', 'baz=qux'], - [{'foo': 'bar'}, {'baz': 'qux'}], - {'foo': 'bar', 'baz': 'qux'}): + for val in ( + "foo=bar,baz=qux", + ["foo=bar", "baz=qux"], + [{"foo": "bar"}, {"baz": "qux"}], + {"foo": "bar", "baz": "qux"}, + ): self.assertEqual( salt.utils.docker.translate_input( - self.translator, - log_driver='foo', - log_opt='foo=bar,baz=qux' + self.translator, log_driver="foo", log_opt="foo=bar,baz=qux" ), - {'log_config': {'Type': 'foo', - 'Config': {'foo': 'bar', 'baz': 'qux'}}} + {"log_config": {"Type": "foo", "Config": {"foo": "bar", "baz": "qux"}}}, ) # Ensure passing either `log_driver` or `log_opt` alone works self.assertEqual( - salt.utils.docker.translate_input( - self.translator, - log_driver='foo' - ), - {'log_config': {'Type': 'foo', 'Config': {}}} + salt.utils.docker.translate_input(self.translator, log_driver="foo"), + {"log_config": {"Type": "foo", "Config": {}}}, ) self.assertEqual( salt.utils.docker.translate_input( - self.translator, - log_opt={'foo': 'bar', 'baz': 'qux'} + self.translator, log_opt={"foo": "bar", "baz": "qux"} ), - {'log_config': {'Type': 'none', - 'Config': {'foo': 'bar', 'baz': 'qux'}}} + {"log_config": {"Type": "none", "Config": {"foo": "bar", "baz": "qux"}}}, ) @assert_key_equals_value(salt.utils.docker.translate.container) def test_lxc_conf(self): - ''' + """ Can be passed as a list of key=value pairs or a dictionary, and must ultimately end up as a dictionary. - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_mac_address(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_int_or_string(salt.utils.docker.translate.container) def test_mem_limit(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_int(salt.utils.docker.translate.container) def test_mem_swappiness(self): - ''' + """ Should be an int or converted to one - ''' + """ @assert_int_or_string(salt.utils.docker.translate.container) def test_memswap_limit(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_name(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_bool(salt.utils.docker.translate.container) def test_network_disabled(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_network_mode(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_bool(salt.utils.docker.translate.container) def test_oom_kill_disable(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_int(salt.utils.docker.translate.container) def test_oom_score_adj(self): - ''' + """ Should be an int or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_pid_mode(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_int(salt.utils.docker.translate.container) def test_pids_limit(self): - ''' + """ Should be an int or converted to one - ''' + """ def test_port_bindings(self): - ''' + """ This has several potential formats and can include port ranges. It needs its own test. - ''' + """ # ip:hostPort:containerPort - Bind a specific IP and port on the host # to a specific port within the container. bindings = ( - '10.1.2.3:8080:80,10.1.2.3:8888:80,10.4.5.6:3333:3333,' - '10.7.8.9:14505-14506:4505-4506,10.1.2.3:8080:81/udp,' - '10.1.2.3:8888:81/udp,10.4.5.6:3334:3334/udp,' - '10.7.8.9:15505-15506:5505-5506/udp' + "10.1.2.3:8080:80,10.1.2.3:8888:80,10.4.5.6:3333:3333," + "10.7.8.9:14505-14506:4505-4506,10.1.2.3:8080:81/udp," + "10.1.2.3:8888:81/udp,10.4.5.6:3334:3334/udp," + "10.7.8.9:15505-15506:5505-5506/udp" ) - for val in (bindings, bindings.split(',')): + for val in (bindings, bindings.split(",")): self.assertEqual( self.normalize_ports( salt.utils.docker.translate_input( - self.translator, - port_bindings=val, + self.translator, port_bindings=val, ) ), - {'port_bindings': {80: [('10.1.2.3', 8080), - ('10.1.2.3', 8888)], - 3333: ('10.4.5.6', 3333), - 4505: ('10.7.8.9', 14505), - 4506: ('10.7.8.9', 14506), - '81/udp': [('10.1.2.3', 8080), - ('10.1.2.3', 8888)], - '3334/udp': ('10.4.5.6', 3334), - '5505/udp': ('10.7.8.9', 15505), - '5506/udp': ('10.7.8.9', 15506)}, - 'ports': [80, 3333, 4505, 4506, - (81, 'udp'), (3334, 'udp'), - (5505, 'udp'), (5506, 'udp')]} + { + "port_bindings": { + 80: [("10.1.2.3", 8080), ("10.1.2.3", 8888)], + 3333: ("10.4.5.6", 3333), + 4505: ("10.7.8.9", 14505), + 4506: ("10.7.8.9", 14506), + "81/udp": [("10.1.2.3", 8080), ("10.1.2.3", 8888)], + "3334/udp": ("10.4.5.6", 3334), + "5505/udp": ("10.7.8.9", 15505), + "5506/udp": ("10.7.8.9", 15506), + }, + "ports": [ + 80, + 3333, + 4505, + 4506, + (81, "udp"), + (3334, "udp"), + (5505, "udp"), + (5506, "udp"), + ], + }, ) # ip::containerPort - Bind a specific IP and an ephemeral port to a # specific port within the container. bindings = ( - '10.1.2.3::80,10.1.2.3::80,10.4.5.6::3333,10.7.8.9::4505-4506,' - '10.1.2.3::81/udp,10.1.2.3::81/udp,10.4.5.6::3334/udp,' - '10.7.8.9::5505-5506/udp' + "10.1.2.3::80,10.1.2.3::80,10.4.5.6::3333,10.7.8.9::4505-4506," + "10.1.2.3::81/udp,10.1.2.3::81/udp,10.4.5.6::3334/udp," + "10.7.8.9::5505-5506/udp" ) - for val in (bindings, bindings.split(',')): + for val in (bindings, bindings.split(",")): self.assertEqual( self.normalize_ports( salt.utils.docker.translate_input( - self.translator, - port_bindings=val, + self.translator, port_bindings=val, ) ), - {'port_bindings': {80: [('10.1.2.3',), ('10.1.2.3',)], - 3333: ('10.4.5.6',), - 4505: ('10.7.8.9',), - 4506: ('10.7.8.9',), - '81/udp': [('10.1.2.3',), ('10.1.2.3',)], - '3334/udp': ('10.4.5.6',), - '5505/udp': ('10.7.8.9',), - '5506/udp': ('10.7.8.9',)}, - 'ports': [80, 3333, 4505, 4506, - (81, 'udp'), (3334, 'udp'), - (5505, 'udp'), (5506, 'udp')]} + { + "port_bindings": { + 80: [("10.1.2.3",), ("10.1.2.3",)], + 3333: ("10.4.5.6",), + 4505: ("10.7.8.9",), + 4506: ("10.7.8.9",), + "81/udp": [("10.1.2.3",), ("10.1.2.3",)], + "3334/udp": ("10.4.5.6",), + "5505/udp": ("10.7.8.9",), + "5506/udp": ("10.7.8.9",), + }, + "ports": [ + 80, + 3333, + 4505, + 4506, + (81, "udp"), + (3334, "udp"), + (5505, "udp"), + (5506, "udp"), + ], + }, ) # hostPort:containerPort - Bind a specific port on all of the host's # interfaces to a specific port within the container. bindings = ( - '8080:80,8888:80,3333:3333,14505-14506:4505-4506,8080:81/udp,' - '8888:81/udp,3334:3334/udp,15505-15506:5505-5506/udp' + "8080:80,8888:80,3333:3333,14505-14506:4505-4506,8080:81/udp," + "8888:81/udp,3334:3334/udp,15505-15506:5505-5506/udp" ) - for val in (bindings, bindings.split(',')): + for val in (bindings, bindings.split(",")): self.assertEqual( self.normalize_ports( salt.utils.docker.translate_input( - self.translator, - port_bindings=val, + self.translator, port_bindings=val, ) ), - {'port_bindings': {80: [8080, 8888], - 3333: 3333, - 4505: 14505, - 4506: 14506, - '81/udp': [8080, 8888], - '3334/udp': 3334, - '5505/udp': 15505, - '5506/udp': 15506}, - 'ports': [80, 3333, 4505, 4506, - (81, 'udp'), (3334, 'udp'), - (5505, 'udp'), (5506, 'udp')]} + { + "port_bindings": { + 80: [8080, 8888], + 3333: 3333, + 4505: 14505, + 4506: 14506, + "81/udp": [8080, 8888], + "3334/udp": 3334, + "5505/udp": 15505, + "5506/udp": 15506, + }, + "ports": [ + 80, + 3333, + 4505, + 4506, + (81, "udp"), + (3334, "udp"), + (5505, "udp"), + (5506, "udp"), + ], + }, ) # containerPort - Bind an ephemeral port on all of the host's # interfaces to a specific port within the container. - bindings = '80,3333,4505-4506,81/udp,3334/udp,5505-5506/udp' - for val in (bindings, bindings.split(',')): + bindings = "80,3333,4505-4506,81/udp,3334/udp,5505-5506/udp" + for val in (bindings, bindings.split(",")): self.assertEqual( self.normalize_ports( salt.utils.docker.translate_input( - self.translator, - port_bindings=val, + self.translator, port_bindings=val, ) ), - {'port_bindings': {80: None, - 3333: None, - 4505: None, - 4506: None, - '81/udp': None, - '3334/udp': None, - '5505/udp': None, - '5506/udp': None}, - 'ports': [80, 3333, 4505, 4506, - (81, 'udp'), (3334, 'udp'), - (5505, 'udp'), (5506, 'udp')]} + { + "port_bindings": { + 80: None, + 3333: None, + 4505: None, + 4506: None, + "81/udp": None, + "3334/udp": None, + "5505/udp": None, + "5506/udp": None, + }, + "ports": [ + 80, + 3333, + 4505, + 4506, + (81, "udp"), + (3334, "udp"), + (5505, "udp"), + (5506, "udp"), + ], + }, ) # Test a mixture of different types of input bindings = ( - '10.1.2.3:8080:80,10.4.5.6::3333,14505-14506:4505-4506,' - '9999-10001,10.1.2.3:8080:81/udp,10.4.5.6::3334/udp,' - '15505-15506:5505-5506/udp,19999-20001/udp' + "10.1.2.3:8080:80,10.4.5.6::3333,14505-14506:4505-4506," + "9999-10001,10.1.2.3:8080:81/udp,10.4.5.6::3334/udp," + "15505-15506:5505-5506/udp,19999-20001/udp" ) - for val in (bindings, bindings.split(',')): + for val in (bindings, bindings.split(",")): self.assertEqual( self.normalize_ports( salt.utils.docker.translate_input( - self.translator, - port_bindings=val, + self.translator, port_bindings=val, ) ), - {'port_bindings': {80: ('10.1.2.3', 8080), - 3333: ('10.4.5.6',), - 4505: 14505, - 4506: 14506, - 9999: None, - 10000: None, - 10001: None, - '81/udp': ('10.1.2.3', 8080), - '3334/udp': ('10.4.5.6',), - '5505/udp': 15505, - '5506/udp': 15506, - '19999/udp': None, - '20000/udp': None, - '20001/udp': None}, - 'ports': [80, 3333, 4505, 4506, 9999, 10000, 10001, - (81, 'udp'), (3334, 'udp'), (5505, 'udp'), - (5506, 'udp'), (19999, 'udp'), - (20000, 'udp'), (20001, 'udp')]} + { + "port_bindings": { + 80: ("10.1.2.3", 8080), + 3333: ("10.4.5.6",), + 4505: 14505, + 4506: 14506, + 9999: None, + 10000: None, + 10001: None, + "81/udp": ("10.1.2.3", 8080), + "3334/udp": ("10.4.5.6",), + "5505/udp": 15505, + "5506/udp": 15506, + "19999/udp": None, + "20000/udp": None, + "20001/udp": None, + }, + "ports": [ + 80, + 3333, + 4505, + 4506, + 9999, + 10000, + 10001, + (81, "udp"), + (3334, "udp"), + (5505, "udp"), + (5506, "udp"), + (19999, "udp"), + (20000, "udp"), + (20001, "udp"), + ], + }, ) # Error case: too many items (max 3) with self.assertRaisesRegex( - CommandExecutionError, - r"'10.1.2.3:8080:80:123' is an invalid port binding " - r"definition \(at most 3 components are allowed, found 4\)"): + CommandExecutionError, + r"'10.1.2.3:8080:80:123' is an invalid port binding " + r"definition \(at most 3 components are allowed, found 4\)", + ): salt.utils.docker.translate_input( - self.translator, - port_bindings='10.1.2.3:8080:80:123' + self.translator, port_bindings="10.1.2.3:8080:80:123" ) # Error case: port range start is greater than end - for val in ('10.1.2.3:5555-5554:1111-1112', - '10.1.2.3:1111-1112:5555-5554', - '10.1.2.3::5555-5554', - '5555-5554:1111-1112', - '1111-1112:5555-5554', - '5555-5554'): + for val in ( + "10.1.2.3:5555-5554:1111-1112", + "10.1.2.3:1111-1112:5555-5554", + "10.1.2.3::5555-5554", + "5555-5554:1111-1112", + "1111-1112:5555-5554", + "5555-5554", + ): with self.assertRaisesRegex( - CommandExecutionError, - r"Start of port range \(5555\) cannot be greater than end " - r"of port range \(5554\)"): + CommandExecutionError, + r"Start of port range \(5555\) cannot be greater than end " + r"of port range \(5554\)", + ): salt.utils.docker.translate_input( - self.translator, - port_bindings=val, + self.translator, port_bindings=val, ) # Error case: non-numeric port range - for val in ('10.1.2.3:foo:1111-1112', - '10.1.2.3:1111-1112:foo', - '10.1.2.3::foo', - 'foo:1111-1112', - '1111-1112:foo', - 'foo'): + for val in ( + "10.1.2.3:foo:1111-1112", + "10.1.2.3:1111-1112:foo", + "10.1.2.3::foo", + "foo:1111-1112", + "1111-1112:foo", + "foo", + ): with self.assertRaisesRegex( - CommandExecutionError, - "'foo' is non-numeric or an invalid port range"): + CommandExecutionError, "'foo' is non-numeric or an invalid port range" + ): salt.utils.docker.translate_input( - self.translator, - port_bindings=val, + self.translator, port_bindings=val, ) # Error case: misatched port range - for val in ('10.1.2.3:1111-1113:1111-1112', '1111-1113:1111-1112'): + for val in ("10.1.2.3:1111-1113:1111-1112", "1111-1113:1111-1112"): with self.assertRaisesRegex( - CommandExecutionError, - r'Host port range \(1111-1113\) does not have the same ' - r'number of ports as the container port range \(1111-1112\)'): - salt.utils.docker.translate_input( - self.translator, - port_bindings=val - ) + CommandExecutionError, + r"Host port range \(1111-1113\) does not have the same " + r"number of ports as the container port range \(1111-1112\)", + ): + salt.utils.docker.translate_input(self.translator, port_bindings=val) - for val in ('10.1.2.3:1111-1112:1111-1113', '1111-1112:1111-1113'): + for val in ("10.1.2.3:1111-1112:1111-1113", "1111-1112:1111-1113"): with self.assertRaisesRegex( - CommandExecutionError, - r'Host port range \(1111-1112\) does not have the same ' - r'number of ports as the container port range \(1111-1113\)'): + CommandExecutionError, + r"Host port range \(1111-1112\) does not have the same " + r"number of ports as the container port range \(1111-1113\)", + ): salt.utils.docker.translate_input( - self.translator, - port_bindings=val, + self.translator, port_bindings=val, ) # Error case: empty host port or container port with self.assertRaisesRegex( - CommandExecutionError, - "Empty host port in port binding definition ':1111'"): - salt.utils.docker.translate_input( - self.translator, - port_bindings=':1111' - ) + CommandExecutionError, "Empty host port in port binding definition ':1111'" + ): + salt.utils.docker.translate_input(self.translator, port_bindings=":1111") with self.assertRaisesRegex( - CommandExecutionError, - "Empty container port in port binding definition '1111:'"): - salt.utils.docker.translate_input( - self.translator, - port_bindings='1111:' - ) + CommandExecutionError, + "Empty container port in port binding definition '1111:'", + ): + salt.utils.docker.translate_input(self.translator, port_bindings="1111:") with self.assertRaisesRegex( - CommandExecutionError, - 'Empty port binding definition found'): - salt.utils.docker.translate_input( - self.translator, - port_bindings='' - ) + CommandExecutionError, "Empty port binding definition found" + ): + salt.utils.docker.translate_input(self.translator, port_bindings="") def test_ports(self): - ''' + """ Ports can be passed as a comma-separated or Python list of port numbers, with '/tcp' being optional for TCP ports. They must ultimately be a list of port definitions, in which an integer denotes a TCP port, and a tuple in the format (port_num, 'udp') denotes a UDP port. Also, the port numbers must end up as integers. None of the decorators will suffice so this one must be tested specially. - ''' - for val in ('1111,2222/tcp,3333/udp,4505-4506', - [1111, '2222/tcp', '3333/udp', '4505-4506'], - ['1111', '2222/tcp', '3333/udp', '4505-4506']): + """ + for val in ( + "1111,2222/tcp,3333/udp,4505-4506", + [1111, "2222/tcp", "3333/udp", "4505-4506"], + ["1111", "2222/tcp", "3333/udp", "4505-4506"], + ): self.assertEqual( self.normalize_ports( - salt.utils.docker.translate_input( - self.translator, - ports=val, - ) + salt.utils.docker.translate_input(self.translator, ports=val,) ), - {'ports': [1111, 2222, 4505, 4506, (3333, 'udp')]} + {"ports": [1111, 2222, 4505, 4506, (3333, "udp")]}, ) # Error case: non-integer and non/string value for val in (1.0, [1.0]): with self.assertRaisesRegex( - CommandExecutionError, - "'1.0' is not a valid port definition"): + CommandExecutionError, "'1.0' is not a valid port definition" + ): salt.utils.docker.translate_input( - self.translator, - ports=val, + self.translator, ports=val, ) # Error case: port range start is greater than end with self.assertRaisesRegex( - CommandExecutionError, - r"Start of port range \(5555\) cannot be greater than end of " - r"port range \(5554\)"): + CommandExecutionError, + r"Start of port range \(5555\) cannot be greater than end of " + r"port range \(5554\)", + ): salt.utils.docker.translate_input( - self.translator, - ports='5555-5554', + self.translator, ports="5555-5554", ) @assert_bool(salt.utils.docker.translate.container) def test_privileged(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_bool(salt.utils.docker.translate.container) def test_publish_all_ports(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_bool(salt.utils.docker.translate.container) def test_read_only(self): - ''' + """ Should be a bool or converted to one - ''' + """ def test_restart_policy(self): - ''' + """ Input is in the format "name[:retry_count]", but the API wants it in the format {'Name': name, 'MaximumRetryCount': retry_count} - ''' - name = 'restart_policy' - alias = 'restart' + """ + name = "restart_policy" + alias = "restart" for item in (name, alias): # Test with retry count self.assertEqual( salt.utils.docker.translate_input( - self.translator, - **{item: 'on-failure:5'} + self.translator, **{item: "on-failure:5"} ), - {name: {'Name': 'on-failure', 'MaximumRetryCount': 5}} + {name: {"Name": "on-failure", "MaximumRetryCount": 5}}, ) # Test without retry count self.assertEqual( salt.utils.docker.translate_input( - self.translator, - **{item: 'on-failure'} + self.translator, **{item: "on-failure"} ), - {name: {'Name': 'on-failure', 'MaximumRetryCount': 0}} + {name: {"Name": "on-failure", "MaximumRetryCount": 0}}, ) # Error case: more than one policy passed with self.assertRaisesRegex( - CommandExecutionError, - 'Only one policy is permitted'): + CommandExecutionError, "Only one policy is permitted" + ): salt.utils.docker.translate_input( - self.translator, - **{item: 'on-failure,always'} + self.translator, **{item: "on-failure,always"} ) # Test collision - test_kwargs = {name: 'on-failure:5', alias: 'always'} + test_kwargs = {name: "on-failure:5", alias: "always"} self.assertEqual( salt.utils.docker.translate_input( - self.translator, - ignore_collisions=True, - **test_kwargs + self.translator, ignore_collisions=True, **test_kwargs ), - {name: {'Name': 'on-failure', 'MaximumRetryCount': 5}} + {name: {"Name": "on-failure", "MaximumRetryCount": 5}}, ) with self.assertRaisesRegex( - CommandExecutionError, - "'restart' is an alias for 'restart_policy'"): + CommandExecutionError, "'restart' is an alias for 'restart_policy'" + ): salt.utils.docker.translate_input( - self.translator, - ignore_collisions=False, - **test_kwargs + self.translator, ignore_collisions=False, **test_kwargs ) @assert_stringlist(salt.utils.docker.translate.container) def test_security_opt(self): - ''' + """ Should be a list of strings or converted to one - ''' + """ @assert_int_or_string(salt.utils.docker.translate.container) def test_shm_size(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_bool(salt.utils.docker.translate.container) def test_stdin_open(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_stop_signal(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_int(salt.utils.docker.translate.container) def test_stop_timeout(self): - ''' + """ Should be an int or converted to one - ''' + """ @assert_key_equals_value(salt.utils.docker.translate.container) def test_storage_opt(self): - ''' + """ Can be passed in several formats but must end up as a dictionary mapping keys to values - ''' + """ @assert_key_equals_value(salt.utils.docker.translate.container) def test_sysctls(self): - ''' + """ Can be passed in several formats but must end up as a dictionary mapping keys to values - ''' + """ @assert_dict(salt.utils.docker.translate.container) def test_tmpfs(self): - ''' + """ Can be passed in several formats but must end up as a dictionary mapping keys to values - ''' + """ @assert_bool(salt.utils.docker.translate.container) def test_tty(self): - ''' + """ Should be a bool or converted to one - ''' + """ def test_ulimits(self): - ''' + """ Input is in the format "name=soft_limit[:hard_limit]", but the API wants it in the format {'Name': name, 'Soft': soft_limit, 'Hard': hard_limit} - ''' + """ # Test with and without hard limit - ulimits = 'nofile=1024:2048,nproc=50' - for val in (ulimits, ulimits.split(',')): + ulimits = "nofile=1024:2048,nproc=50" + for val in (ulimits, ulimits.split(",")): self.assertEqual( - salt.utils.docker.translate_input( - self.translator, - ulimits=val, - ), - {'ulimits': [{'Name': 'nofile', 'Soft': 1024, 'Hard': 2048}, - {'Name': 'nproc', 'Soft': 50, 'Hard': 50}]} + salt.utils.docker.translate_input(self.translator, ulimits=val,), + { + "ulimits": [ + {"Name": "nofile", "Soft": 1024, "Hard": 2048}, + {"Name": "nproc", "Soft": 50, "Hard": 50}, + ] + }, ) # Error case: Invalid format with self.assertRaisesRegex( - CommandExecutionError, - r"Ulimit definition 'nofile:1024:2048' is not in the format " - r"type=soft_limit\[:hard_limit\]"): + CommandExecutionError, + r"Ulimit definition 'nofile:1024:2048' is not in the format " + r"type=soft_limit\[:hard_limit\]", + ): salt.utils.docker.translate_input( - self.translator, - ulimits='nofile:1024:2048' + self.translator, ulimits="nofile:1024:2048" ) # Error case: Invalid format with self.assertRaisesRegex( - CommandExecutionError, - r"Limit 'nofile=foo:2048' contains non-numeric value\(s\)"): + CommandExecutionError, + r"Limit 'nofile=foo:2048' contains non-numeric value\(s\)", + ): salt.utils.docker.translate_input( - self.translator, - ulimits='nofile=foo:2048' + self.translator, ulimits="nofile=foo:2048" ) def test_user(self): - ''' + """ Must be either username (string) or uid (int). An int passed as a string (e.g. '0') should be converted to an int. - ''' + """ # Username passed as string self.assertEqual( - salt.utils.docker.translate_input( - self.translator, - user='foo' - ), - {'user': 'foo'} + salt.utils.docker.translate_input(self.translator, user="foo"), + {"user": "foo"}, ) - for val in (0, '0'): + for val in (0, "0"): self.assertEqual( - salt.utils.docker.translate_input( - self.translator, - user=val - ), - {'user': 0} + salt.utils.docker.translate_input(self.translator, user=val), + {"user": 0}, ) # Error case: non string/int passed with self.assertRaisesRegex( - CommandExecutionError, - 'Value must be a username or uid'): - salt.utils.docker.translate_input( - self.translator, - user=['foo'] - ) + CommandExecutionError, "Value must be a username or uid" + ): + salt.utils.docker.translate_input(self.translator, user=["foo"]) # Error case: negative int passed - with self.assertRaisesRegex( - CommandExecutionError, - "'-1' is an invalid uid"): - salt.utils.docker.translate_input( - self.translator, - user=-1 - ) + with self.assertRaisesRegex(CommandExecutionError, "'-1' is an invalid uid"): + salt.utils.docker.translate_input(self.translator, user=-1) @assert_string(salt.utils.docker.translate.container) def test_userns_mode(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_volume_driver(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_stringlist(salt.utils.docker.translate.container) def test_volumes(self): - ''' + """ Should be a list of absolute paths - ''' + """ # Error case: Not an absolute path - path = os.path.join('foo', 'bar', 'baz') + path = os.path.join("foo", "bar", "baz") with self.assertRaisesRegex( - CommandExecutionError, - "'{0}' is not an absolute path".format(path.replace('\\', '\\\\'))): - salt.utils.docker.translate_input( - self.translator, - volumes=path - ) + CommandExecutionError, + "'{0}' is not an absolute path".format(path.replace("\\", "\\\\")), + ): + salt.utils.docker.translate_input(self.translator, volumes=path) @assert_stringlist(salt.utils.docker.translate.container) def test_volumes_from(self): - ''' + """ Should be a list of strings or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.container) def test_working_dir(self): - ''' + """ Should be a single absolute path - ''' + """ # Error case: Not an absolute path - path = os.path.join('foo', 'bar', 'baz') + path = os.path.join("foo", "bar", "baz") with self.assertRaisesRegex( - CommandExecutionError, - "'{0}' is not an absolute path".format(path.replace('\\', '\\\\'))): - salt.utils.docker.translate_input( - self.translator, - working_dir=path - ) + CommandExecutionError, + "'{0}' is not an absolute path".format(path.replace("\\", "\\\\")), + ): + salt.utils.docker.translate_input(self.translator, working_dir=path) class TranslateNetworkInputTestCase(TranslateBase): - ''' + """ Tests for salt.utils.docker.translate_input(), invoked using salt.utils.docker.translate.network as the translator module. - ''' + """ + translator = salt.utils.docker.translate.network ip_addrs = { - True: ('10.1.2.3', '::1'), - False: ('FOO', '0.9.800.1000', 'feaz::1', 'aj01::feac'), + True: ("10.1.2.3", "::1"), + False: ("FOO", "0.9.800.1000", "feaz::1", "aj01::feac"), } @assert_string(salt.utils.docker.translate.network) def test_driver(self): - ''' + """ Should be a string or converted to one - ''' + """ @assert_key_equals_value(salt.utils.docker.translate.network) def test_options(self): - ''' + """ Can be passed in several formats but must end up as a dictionary mapping keys to values - ''' + """ @assert_dict(salt.utils.docker.translate.network) def test_ipam(self): - ''' + """ Must be a dict - ''' + """ @assert_bool(salt.utils.docker.translate.network) def test_check_duplicate(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_bool(salt.utils.docker.translate.network) def test_internal(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_labels(salt.utils.docker.translate.network) def test_labels(self): - ''' + """ Can be passed as a list of key=value pairs or a dictionary, and must ultimately end up as a dictionary. - ''' + """ @assert_bool(salt.utils.docker.translate.network) def test_enable_ipv6(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_bool(salt.utils.docker.translate.network) def test_attachable(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_bool(salt.utils.docker.translate.network) def test_ingress(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_string(salt.utils.docker.translate.network) def test_ipam_driver(self): - ''' + """ Should be a bool or converted to one - ''' + """ @assert_key_equals_value(salt.utils.docker.translate.network) def test_ipam_opts(self): - ''' + """ Can be passed in several formats but must end up as a dictionary mapping keys to values - ''' + """ def ipam_pools(self): - ''' + """ Must be a list of dictionaries (not a dictlist) - ''' + """ good_pool = { - 'subnet': '10.0.0.0/24', - 'iprange': '10.0.0.128/25', - 'gateway': '10.0.0.254', - 'aux_addresses': {'foo.bar.tld': '10.0.0.20', - 'hello.world.tld': '10.0.0.21'}, + "subnet": "10.0.0.0/24", + "iprange": "10.0.0.128/25", + "gateway": "10.0.0.254", + "aux_addresses": { + "foo.bar.tld": "10.0.0.20", + "hello.world.tld": "10.0.0.21", + }, } bad_pools = [ - {'subnet': '10.0.0.0/33', - 'iprange': '10.0.0.128/25', - 'gateway': '10.0.0.254', - 'aux_addresses': {'foo.bar.tld': '10.0.0.20', - 'hello.world.tld': '10.0.0.21'}}, - {'subnet': '10.0.0.0/24', - 'iprange': 'foo/25', - 'gateway': '10.0.0.254', - 'aux_addresses': {'foo.bar.tld': '10.0.0.20', - 'hello.world.tld': '10.0.0.21'}}, - {'subnet': '10.0.0.0/24', - 'iprange': '10.0.0.128/25', - 'gateway': '10.0.0.256', - 'aux_addresses': {'foo.bar.tld': '10.0.0.20', - 'hello.world.tld': '10.0.0.21'}}, - {'subnet': '10.0.0.0/24', - 'iprange': '10.0.0.128/25', - 'gateway': '10.0.0.254', - 'aux_addresses': {'foo.bar.tld': '10.0.0.20', - 'hello.world.tld': '999.0.0.21'}}, + { + "subnet": "10.0.0.0/33", + "iprange": "10.0.0.128/25", + "gateway": "10.0.0.254", + "aux_addresses": { + "foo.bar.tld": "10.0.0.20", + "hello.world.tld": "10.0.0.21", + }, + }, + { + "subnet": "10.0.0.0/24", + "iprange": "foo/25", + "gateway": "10.0.0.254", + "aux_addresses": { + "foo.bar.tld": "10.0.0.20", + "hello.world.tld": "10.0.0.21", + }, + }, + { + "subnet": "10.0.0.0/24", + "iprange": "10.0.0.128/25", + "gateway": "10.0.0.256", + "aux_addresses": { + "foo.bar.tld": "10.0.0.20", + "hello.world.tld": "10.0.0.21", + }, + }, + { + "subnet": "10.0.0.0/24", + "iprange": "10.0.0.128/25", + "gateway": "10.0.0.254", + "aux_addresses": { + "foo.bar.tld": "10.0.0.20", + "hello.world.tld": "999.0.0.21", + }, + }, ] self.assertEqual( - salt.utils.docker.translate_input( - self.translator, - ipam_pools=[good_pool], - ), - {'ipam_pools': [good_pool]} + salt.utils.docker.translate_input(self.translator, ipam_pools=[good_pool],), + {"ipam_pools": [good_pool]}, ) for bad_pool in bad_pools: - with self.assertRaisesRegex(CommandExecutionError, 'not a valid'): + with self.assertRaisesRegex(CommandExecutionError, "not a valid"): salt.utils.docker.translate_input( - self.translator, - ipam_pools=[good_pool, bad_pool] + self.translator, ipam_pools=[good_pool, bad_pool] ) @assert_subnet(salt.utils.docker.translate.network) def test_subnet(self): - ''' + """ Must be an IPv4 or IPv6 subnet - ''' + """ @assert_subnet(salt.utils.docker.translate.network) def test_iprange(self): - ''' + """ Must be an IPv4 or IPv6 subnet - ''' + """ def test_gateway(self): - ''' + """ Must be an IPv4 or IPv6 address - ''' + """ for val in self.ip_addrs[True]: self.assertEqual( salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=True, - gateway=val, + self.translator, validate_ip_addrs=True, gateway=val, ), - self.apply_defaults({'gateway': val}) + self.apply_defaults({"gateway": val}), ) for val in self.ip_addrs[False]: with self.assertRaisesRegex( - CommandExecutionError, - "'{0}' is not a valid IP address".format(val)): + CommandExecutionError, "'{0}' is not a valid IP address".format(val) + ): salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=True, - gateway=val, + self.translator, validate_ip_addrs=True, gateway=val, ) self.assertEqual( salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=False, - gateway=val, + self.translator, validate_ip_addrs=False, gateway=val, ), self.apply_defaults( - {'gateway': val if isinstance(val, six.string_types) - else six.text_type(val)} - ) + { + "gateway": val + if isinstance(val, six.string_types) + else six.text_type(val) + } + ), ) @assert_key_equals_value(salt.utils.docker.translate.network) def test_aux_addresses(self): - ''' + """ Must be a mapping of hostnames to IP addresses - ''' - name = 'aux_addresses' - alias = 'aux_address' + """ + name = "aux_addresses" + alias = "aux_address" for item in (name, alias): for val in self.ip_addrs[True]: - addresses = {'foo.bar.tld': val} + addresses = {"foo.bar.tld": val} self.assertEqual( salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=True, - **{item: addresses} + self.translator, validate_ip_addrs=True, **{item: addresses} ), - self.apply_defaults({name: addresses}) + self.apply_defaults({name: addresses}), ) for val in self.ip_addrs[False]: - addresses = {'foo.bar.tld': val} + addresses = {"foo.bar.tld": val} with self.assertRaisesRegex( - CommandExecutionError, - "'{0}' is not a valid IP address".format(val)): + CommandExecutionError, "'{0}' is not a valid IP address".format(val) + ): salt.utils.docker.translate_input( - self.translator, - validate_ip_addrs=True, - **{item: addresses} + self.translator, validate_ip_addrs=True, **{item: addresses} ) self.assertEqual( salt.utils.docker.translate_input( @@ -2019,58 +1961,60 @@ class TranslateNetworkInputTestCase(TranslateBase): validate_ip_addrs=False, aux_addresses=addresses, ), - self.apply_defaults({name: addresses}) + self.apply_defaults({name: addresses}), ) class DockerTranslateHelperTestCase(TestCase): - ''' + """ Tests for a couple helper functions in salt.utils.docker.translate - ''' + """ + def test_get_port_def(self): - ''' + """ Test translation of port definition (1234, '1234/tcp', '1234/udp', etc.) into the format which docker-py uses (integer for TCP ports, 'port_num/udp' for UDP ports). - ''' + """ # Test TCP port (passed as int, no protocol passed) self.assertEqual(translate_helpers.get_port_def(2222), 2222) # Test TCP port (passed as str, no protocol passed) - self.assertEqual(translate_helpers.get_port_def('2222'), 2222) + self.assertEqual(translate_helpers.get_port_def("2222"), 2222) # Test TCP port (passed as str, with protocol passed) - self.assertEqual(translate_helpers.get_port_def('2222', 'tcp'), 2222) + self.assertEqual(translate_helpers.get_port_def("2222", "tcp"), 2222) # Test TCP port (proto passed in port_num, with passed proto ignored). # This is a contrived example as we would never invoke the function in # this way, but it tests that we are taking the port number from the # port_num argument and ignoring the passed protocol. - self.assertEqual(translate_helpers.get_port_def('2222/tcp', 'udp'), 2222) + self.assertEqual(translate_helpers.get_port_def("2222/tcp", "udp"), 2222) # Test UDP port (passed as int) - self.assertEqual(translate_helpers.get_port_def(2222, 'udp'), (2222, 'udp')) + self.assertEqual(translate_helpers.get_port_def(2222, "udp"), (2222, "udp")) # Test UDP port (passed as string) - self.assertEqual(translate_helpers.get_port_def('2222', 'udp'), (2222, 'udp')) + self.assertEqual(translate_helpers.get_port_def("2222", "udp"), (2222, "udp")) # Test UDP port (proto passed in port_num - self.assertEqual(translate_helpers.get_port_def('2222/udp'), (2222, 'udp')) + self.assertEqual(translate_helpers.get_port_def("2222/udp"), (2222, "udp")) def test_get_port_range(self): - ''' + """ Test extracting the start and end of a port range from a port range expression (e.g. 4505-4506) - ''' + """ # Passing a single int should return the start and end as the same value self.assertEqual(translate_helpers.get_port_range(2222), (2222, 2222)) # Same as above but with port number passed as a string - self.assertEqual(translate_helpers.get_port_range('2222'), (2222, 2222)) + self.assertEqual(translate_helpers.get_port_range("2222"), (2222, 2222)) # Passing a port range - self.assertEqual(translate_helpers.get_port_range('2222-2223'), (2222, 2223)) + self.assertEqual(translate_helpers.get_port_range("2222-2223"), (2222, 2223)) # Error case: port range start is greater than end with self.assertRaisesRegex( - ValueError, - r'Start of port range \(2222\) cannot be greater than end of ' - r'port range \(2221\)'): - translate_helpers.get_port_range('2222-2221') + ValueError, + r"Start of port range \(2222\) cannot be greater than end of " + r"port range \(2221\)", + ): + translate_helpers.get_port_range("2222-2221") # Error case: non-numeric input with self.assertRaisesRegex( - ValueError, - '\'2222-bar\' is non-numeric or an invalid port range'): - translate_helpers.get_port_range('2222-bar') + ValueError, "'2222-bar' is non-numeric or an invalid port range" + ): + translate_helpers.get_port_range("2222-bar") diff --git a/tests/unit/utils/test_environment.py b/tests/unit/utils/test_environment.py index 8305d7933f8..8e4339fbf86 100644 --- a/tests/unit/utils/test_environment.py +++ b/tests/unit/utils/test_environment.py @@ -1,10 +1,11 @@ # coding=utf-8 -''' +""" Test case for utils/__init__.py -''' -from __future__ import unicode_literals, print_function, absolute_import -from tests.support.unit import TestCase, skipIf +""" +from __future__ import absolute_import, print_function, unicode_literals + import salt.utils.environment +from tests.support.unit import TestCase, skipIf try: import pytest @@ -12,189 +13,160 @@ except ImportError: pytest = None -@skipIf(pytest is None, 'PyTest is missing') +@skipIf(pytest is None, "PyTest is missing") class UtilsTestCase(TestCase): - ''' + """ Test case for utils/__init__.py - ''' + """ + def test_get_module_environment_empty(self): - ''' + """ Test for salt.utils.get_module_environment Test if empty globals returns to an empty environment with the correct type. :return: - ''' + """ out = salt.utils.environment.get_module_environment({}) assert out == {} assert isinstance(out, dict) def test_get_module_environment_opts(self): - ''' + """ Test for salt.utils.get_module_environment Test if __opts__ are visible. :return: - ''' - expectation = {'message': 'Melting hard drives'} + """ + expectation = {"message": "Melting hard drives"} _globals = { - '__opts__': { - 'system-environment': { - 'modules': { - 'system': { - '_': expectation, - }, - }, - }, - }, - '__file__': '/daemons/loose/modules/system.py', - } + "__opts__": { + "system-environment": {"modules": {"system": {"_": expectation}}}, + }, + "__file__": "/daemons/loose/modules/system.py", + } assert salt.utils.environment.get_module_environment(_globals) == expectation def test_get_module_environment_pillars(self): - ''' + """ Test for salt.utils.get_module_environment Test if __pillar__ is visible. :return: - ''' - expectation = {'message': 'The CPU has shifted, and become decentralized.'} + """ + expectation = {"message": "The CPU has shifted, and become decentralized."} _globals = { - '__opts__': { - 'system-environment': { - 'electric': { - 'interference': { - '_': expectation, - }, - }, - }, + "__opts__": { + "system-environment": { + "electric": {"interference": {"_": expectation}}, }, - '__file__': '/piezo/electric/interference.py', - } + }, + "__file__": "/piezo/electric/interference.py", + } assert salt.utils.environment.get_module_environment(_globals) == expectation def test_get_module_environment_pillar_override(self): - ''' + """ Test for salt.utils.get_module_environment Test if __pillar__ is overriding __opts__. :return: - ''' - expectation = {'msg': 'The CPU has shifted, and become decentralized.'} + """ + expectation = {"msg": "The CPU has shifted, and become decentralized."} _globals = { - '__opts__': { - 'system-environment': { - 'electric': { - 'interference': { - '_': {'msg': 'Trololo!'}, - }, - }, + "__opts__": { + "system-environment": { + "electric": {"interference": {"_": {"msg": "Trololo!"}}}, }, }, - '__pillar__': { - 'system-environment': { - 'electric': { - 'interference': { - '_': expectation, - }, - }, - }, + "__pillar__": { + "system-environment": { + "electric": {"interference": {"_": expectation}}, }, - '__file__': '/piezo/electric/interference.py', - } + }, + "__file__": "/piezo/electric/interference.py", + } assert salt.utils.environment.get_module_environment(_globals) == expectation def test_get_module_environment_sname_found(self): - ''' + """ Test for salt.utils.get_module_environment Section name and module name are found. :return: - ''' - expectation = {'msg': 'All operators are on strike due to broken coffee machine!'} + """ + expectation = { + "msg": "All operators are on strike due to broken coffee machine!" + } _globals = { - '__opts__': { - 'system-environment': { - 'jumping': { - 'interference': { - '_': expectation, - }, - }, - }, + "__opts__": { + "system-environment": { + "jumping": {"interference": {"_": expectation}}, }, - '__file__': '/route/flapping/at_the_nap.py', - } + }, + "__file__": "/route/flapping/at_the_nap.py", + } assert salt.utils.environment.get_module_environment(_globals) == {} - _globals['__file__'] = '/route/jumping/interference.py' + _globals["__file__"] = "/route/jumping/interference.py" assert salt.utils.environment.get_module_environment(_globals) == expectation def test_get_module_environment_mname_found(self): - ''' + """ Test for salt.utils.get_module_environment Module name is found. :return: - ''' - expectation = {'msg': 'All operators are on strike due to broken coffee machine!'} + """ + expectation = { + "msg": "All operators are on strike due to broken coffee machine!" + } _globals = { - '__pillar__': { - 'system-environment': { - 'jumping': { - 'nonsense': { - '_': expectation, - }, - }, - }, - }, - '__file__': '/route/jumping/interference.py', - } + "__pillar__": { + "system-environment": {"jumping": {"nonsense": {"_": expectation}}}, + }, + "__file__": "/route/jumping/interference.py", + } assert salt.utils.environment.get_module_environment(_globals) == {} - _globals['__pillar__']['system-environment']['jumping']['interference'] = {} - _globals['__pillar__']['system-environment']['jumping']['interference']['_'] = expectation + _globals["__pillar__"]["system-environment"]["jumping"]["interference"] = {} + _globals["__pillar__"]["system-environment"]["jumping"]["interference"][ + "_" + ] = expectation assert salt.utils.environment.get_module_environment(_globals) == expectation def test_get_module_environment_vname_found(self): - ''' + """ Test for salt.utils.get_module_environment Virtual name is found. :return: - ''' - expectation = {'msg': 'All operators are on strike due to broken coffee machine!'} + """ + expectation = { + "msg": "All operators are on strike due to broken coffee machine!" + } _globals = { - '__virtualname__': 'nonsense', - '__pillar__': { - 'system-environment': { - 'jumping': { - 'nonsense': { - '_': expectation, - }, - }, - }, - }, - '__file__': '/route/jumping/translation.py', - } + "__virtualname__": "nonsense", + "__pillar__": { + "system-environment": {"jumping": {"nonsense": {"_": expectation}}}, + }, + "__file__": "/route/jumping/translation.py", + } assert salt.utils.environment.get_module_environment(_globals) == expectation def test_get_module_environment_vname_overridden(self): - ''' + """ Test for salt.utils.get_module_environment Virtual namespace overridden. :return: - ''' - expectation = {'msg': 'New management.'} + """ + expectation = {"msg": "New management."} _globals = { - '__virtualname__': 'nonsense', - '__pillar__': { - 'system-environment': { - 'funny': { - 'translation': { - '_': expectation, - }, - 'nonsense': { - '_': {'msg': 'This is wrong'}, - }, - }, + "__virtualname__": "nonsense", + "__pillar__": { + "system-environment": { + "funny": { + "translation": {"_": expectation}, + "nonsense": {"_": {"msg": "This is wrong"}}, }, }, - '__file__': '/lost/in/funny/translation.py', - } + }, + "__file__": "/lost/in/funny/translation.py", + } assert salt.utils.environment.get_module_environment(_globals) == expectation diff --git a/tests/unit/utils/test_etcd_util.py b/tests/unit/utils/test_etcd_util.py index 467b50cee31..f2183b14582 100644 --- a/tests/unit/utils/test_etcd_util.py +++ b/tests/unit/utils/test_etcd_util.py @@ -1,355 +1,409 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Jayesh Kariya <jayeshk@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.utils.etcd_util as etcd_util +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf + try: from urllib3.exceptions import ReadTimeoutError, MaxRetryError + HAS_URLLIB3 = True except ImportError: HAS_URLLIB3 = False try: import etcd + HAS_ETCD = True except ImportError: HAS_ETCD = False -@skipIf(HAS_URLLIB3 is False, 'urllib3 module must be installed.') -@skipIf(HAS_ETCD is False, 'python-etcd module must be installed.') +@skipIf(HAS_URLLIB3 is False, "urllib3 module must be installed.") +@skipIf(HAS_ETCD is False, "python-etcd module must be installed.") class EtcdUtilTestCase(TestCase): - ''' + """ Test cases for salt.utils.etcd_util - ''' + """ + # 'get_' function tests: 1 def test_read(self): - ''' + """ Test to make sure we interact with etcd correctly - ''' - with patch('etcd.Client', autospec=True) as mock: + """ + with patch("etcd.Client", autospec=True) as mock: etcd_client = mock.return_value - etcd_return = MagicMock(value='salt') + etcd_return = MagicMock(value="salt") etcd_client.read.return_value = etcd_return client = etcd_util.EtcdClient({}) - self.assertEqual(client.read('/salt'), etcd_return) - etcd_client.read.assert_called_with('/salt', recursive=False, wait=False, timeout=None) + self.assertEqual(client.read("/salt"), etcd_return) + etcd_client.read.assert_called_with( + "/salt", recursive=False, wait=False, timeout=None + ) - client.read('salt', True, True, 10, 5) - etcd_client.read.assert_called_with('salt', recursive=True, wait=True, timeout=10, waitIndex=5) + client.read("salt", True, True, 10, 5) + etcd_client.read.assert_called_with( + "salt", recursive=True, wait=True, timeout=10, waitIndex=5 + ) etcd_client.read.side_effect = etcd.EtcdKeyNotFound - self.assertRaises(etcd.EtcdKeyNotFound, client.read, 'salt') + self.assertRaises(etcd.EtcdKeyNotFound, client.read, "salt") etcd_client.read.side_effect = etcd.EtcdConnectionFailed - self.assertRaises(etcd.EtcdConnectionFailed, client.read, 'salt') + self.assertRaises(etcd.EtcdConnectionFailed, client.read, "salt") etcd_client.read.side_effect = etcd.EtcdValueError - self.assertRaises(etcd.EtcdValueError, client.read, 'salt') + self.assertRaises(etcd.EtcdValueError, client.read, "salt") etcd_client.read.side_effect = ValueError - self.assertRaises(ValueError, client.read, 'salt') + self.assertRaises(ValueError, client.read, "salt") etcd_client.read.side_effect = ReadTimeoutError(None, None, None) - self.assertRaises(etcd.EtcdConnectionFailed, client.read, 'salt') + self.assertRaises(etcd.EtcdConnectionFailed, client.read, "salt") etcd_client.read.side_effect = MaxRetryError(None, None) - self.assertRaises(etcd.EtcdConnectionFailed, client.read, 'salt') + self.assertRaises(etcd.EtcdConnectionFailed, client.read, "salt") def test_get(self): - ''' + """ Test if it get a value from etcd, by direct path - ''' - with patch('etcd.Client') as mock: + """ + with patch("etcd.Client") as mock: client = etcd_util.EtcdClient({}) - with patch.object(client, 'read', autospec=True) as mock: - mock.return_value = MagicMock(value='stack') - self.assertEqual(client.get('salt'), 'stack') - mock.assert_called_with('salt', recursive=False) + with patch.object(client, "read", autospec=True) as mock: + mock.return_value = MagicMock(value="stack") + self.assertEqual(client.get("salt"), "stack") + mock.assert_called_with("salt", recursive=False) - self.assertEqual(client.get('salt', recurse=True), 'stack') - mock.assert_called_with('salt', recursive=True) + self.assertEqual(client.get("salt", recurse=True), "stack") + mock.assert_called_with("salt", recursive=True) # iter(list(Exception)) works correctly with both mock<1.1 and mock>=1.1 mock.side_effect = iter([etcd.EtcdKeyNotFound()]) - self.assertEqual(client.get('not-found'), None) + self.assertEqual(client.get("not-found"), None) mock.side_effect = iter([etcd.EtcdConnectionFailed()]) - self.assertEqual(client.get('watching'), None) + self.assertEqual(client.get("watching"), None) # python 2.6 test mock.side_effect = ValueError - self.assertEqual(client.get('not-found'), None) + self.assertEqual(client.get("not-found"), None) mock.side_effect = Exception - self.assertRaises(Exception, client.get, 'some-error') + self.assertRaises(Exception, client.get, "some-error") def test_tree(self): - ''' + """ Test recursive gets - ''' - with patch('etcd.Client') as mock: + """ + with patch("etcd.Client") as mock: client = etcd_util.EtcdClient({}) - with patch.object(client, 'read', autospec=True) as mock: + with patch.object(client, "read", autospec=True) as mock: c1, c2 = MagicMock(), MagicMock() c1.__iter__.return_value = [ - MagicMock(key='/x/a', value='1'), - MagicMock(key='/x/b', value='2'), - MagicMock(key='/x/c', dir=True)] - c2.__iter__.return_value = [ - MagicMock(key='/x/c/d', value='3') + MagicMock(key="/x/a", value="1"), + MagicMock(key="/x/b", value="2"), + MagicMock(key="/x/c", dir=True), ] - mock.side_effect = iter([ - MagicMock(children=c1), - MagicMock(children=c2) - ]) - self.assertDictEqual(client.tree('/x'), {'a': '1', 'b': '2', 'c': {'d': '3'}}) - mock.assert_any_call('/x') - mock.assert_any_call('/x/c') + c2.__iter__.return_value = [MagicMock(key="/x/c/d", value="3")] + mock.side_effect = iter( + [MagicMock(children=c1), MagicMock(children=c2)] + ) + self.assertDictEqual( + client.tree("/x"), {"a": "1", "b": "2", "c": {"d": "3"}} + ) + mock.assert_any_call("/x") + mock.assert_any_call("/x/c") # iter(list(Exception)) works correctly with both mock<1.1 and mock>=1.1 mock.side_effect = iter([etcd.EtcdKeyNotFound()]) - self.assertEqual(client.tree('not-found'), None) + self.assertEqual(client.tree("not-found"), None) mock.side_effect = ValueError - self.assertEqual(client.tree('/x'), None) + self.assertEqual(client.tree("/x"), None) mock.side_effect = Exception - self.assertRaises(Exception, client.tree, 'some-error') + self.assertRaises(Exception, client.tree, "some-error") def test_ls(self): - with patch('etcd.Client') as mock: + with patch("etcd.Client") as mock: client = etcd_util.EtcdClient({}) - with patch.object(client, 'read', autospec=True) as mock: + with patch.object(client, "read", autospec=True) as mock: c1 = MagicMock() c1.__iter__.return_value = [ - MagicMock(key='/x/a', value='1'), - MagicMock(key='/x/b', value='2'), - MagicMock(key='/x/c', dir=True)] + MagicMock(key="/x/a", value="1"), + MagicMock(key="/x/b", value="2"), + MagicMock(key="/x/c", dir=True), + ] mock.return_value = MagicMock(children=c1) - self.assertEqual(client.ls('/x'), {'/x': {'/x/a': '1', '/x/b': '2', '/x/c/': {}}}) - mock.assert_called_with('/x') + self.assertEqual( + client.ls("/x"), {"/x": {"/x/a": "1", "/x/b": "2", "/x/c/": {}}} + ) + mock.assert_called_with("/x") # iter(list(Exception)) works correctly with both mock<1.1 and mock>=1.1 mock.side_effect = iter([etcd.EtcdKeyNotFound()]) - self.assertEqual(client.ls('/not-found'), {}) + self.assertEqual(client.ls("/not-found"), {}) mock.side_effect = Exception - self.assertRaises(Exception, client.tree, 'some-error') + self.assertRaises(Exception, client.tree, "some-error") def test_write(self): - with patch('etcd.Client', autospec=True) as mock: + with patch("etcd.Client", autospec=True) as mock: client = etcd_util.EtcdClient({}) etcd_client = mock.return_value - etcd_client.write.return_value = MagicMock(value='salt') - self.assertEqual(client.write('/some-key', 'salt'), 'salt') - etcd_client.write.assert_called_with('/some-key', 'salt', ttl=None, dir=False) + etcd_client.write.return_value = MagicMock(value="salt") + self.assertEqual(client.write("/some-key", "salt"), "salt") + etcd_client.write.assert_called_with( + "/some-key", "salt", ttl=None, dir=False + ) - self.assertEqual(client.write('/some-key', 'salt', ttl=5), 'salt') - etcd_client.write.assert_called_with('/some-key', 'salt', ttl=5, dir=False) + self.assertEqual(client.write("/some-key", "salt", ttl=5), "salt") + etcd_client.write.assert_called_with("/some-key", "salt", ttl=5, dir=False) etcd_client.write.return_value = MagicMock(dir=True) - self.assertEqual(client.write('/some-dir', 'salt', ttl=0, directory=True), True) - etcd_client.write.assert_called_with('/some-dir', None, ttl=0, dir=True) + self.assertEqual( + client.write("/some-dir", "salt", ttl=0, directory=True), True + ) + etcd_client.write.assert_called_with("/some-dir", None, ttl=0, dir=True) # Check when a file is attempted to be written to a read-only root etcd_client.write.side_effect = etcd.EtcdRootReadOnly() - self.assertEqual(client.write('/', 'some-val', directory=False), None) + self.assertEqual(client.write("/", "some-val", directory=False), None) # Check when a directory is attempted to be written to a read-only root etcd_client.write.side_effect = etcd.EtcdRootReadOnly() - self.assertEqual(client.write('/', None, directory=True), None) + self.assertEqual(client.write("/", None, directory=True), None) # Check when a file is attempted to be written when unable to connect to the service etcd_client.write.side_effect = MaxRetryError(None, None) - self.assertEqual(client.write('/some-key', 'some-val', directory=False), None) + self.assertEqual( + client.write("/some-key", "some-val", directory=False), None + ) # Check when a directory is attempted to be written when unable to connect to the service etcd_client.write.side_effect = MaxRetryError(None, None) - self.assertEqual(client.write('/some-dir', None, directory=True), None) + self.assertEqual(client.write("/some-dir", None, directory=True), None) # Check when a file is attempted to be written to a directory that already exists (name-collision) etcd_client.write.side_effect = etcd.EtcdNotFile() - self.assertEqual(client.write('/some-dir', 'some-val', directory=False), None) + self.assertEqual( + client.write("/some-dir", "some-val", directory=False), None + ) # Check when a directory is attempted to be written to a file that already exists (name-collision) etcd_client.write.side_effect = etcd.EtcdNotDir() - self.assertEqual(client.write('/some-key', None, directory=True), None) + self.assertEqual(client.write("/some-key", None, directory=True), None) # Check when a directory is attempted to be written to a directory that already exists (update-ttl) etcd_client.write.side_effect = etcd.EtcdNotFile() - self.assertEqual(client.write('/some-dir', None, directory=True), True) + self.assertEqual(client.write("/some-dir", None, directory=True), True) etcd_client.write.side_effect = ValueError - self.assertEqual(client.write('/some-key', 'some-val'), None) + self.assertEqual(client.write("/some-key", "some-val"), None) etcd_client.write.side_effect = Exception - self.assertRaises(Exception, client.set, 'some-key', 'some-val') + self.assertRaises(Exception, client.set, "some-key", "some-val") def test_flatten(self): - with patch('etcd.Client', autospec=True) as mock: + with patch("etcd.Client", autospec=True) as mock: client = etcd_util.EtcdClient({}) some_data = { - '/x/y/a': '1', - 'x': { - 'y': { - 'b': '2' - } - }, - 'm/j/': '3', - 'z': '4', - 'd': {}, + "/x/y/a": "1", + "x": {"y": {"b": "2"}}, + "m/j/": "3", + "z": "4", + "d": {}, } result_path = { - '/test/x/y/a': '1', - '/test/x/y/b': '2', - '/test/m/j': '3', - '/test/z': '4', - '/test/d': {}, + "/test/x/y/a": "1", + "/test/x/y/b": "2", + "/test/m/j": "3", + "/test/z": "4", + "/test/d": {}, } result_nopath = { - '/x/y/a': '1', - '/x/y/b': '2', - '/m/j': '3', - '/z': '4', - '/d': {}, + "/x/y/a": "1", + "/x/y/b": "2", + "/m/j": "3", + "/z": "4", + "/d": {}, } result_root = { - '/x/y/a': '1', - '/x/y/b': '2', - '/m/j': '3', - '/z': '4', - '/d': {}, + "/x/y/a": "1", + "/x/y/b": "2", + "/m/j": "3", + "/z": "4", + "/d": {}, } - self.assertEqual(client._flatten(some_data, path='/test'), result_path) - self.assertEqual(client._flatten(some_data, path='/'), result_root) + self.assertEqual(client._flatten(some_data, path="/test"), result_path) + self.assertEqual(client._flatten(some_data, path="/"), result_root) self.assertEqual(client._flatten(some_data), result_nopath) def test_update(self): - with patch('etcd.Client', autospec=True) as mock: + with patch("etcd.Client", autospec=True) as mock: client = etcd_util.EtcdClient({}) some_data = { - '/x/y/a': '1', - 'x': { - 'y': { - 'b': '3' - } - }, - 'm/j/': '3', - 'z': '4', - 'd': {}, + "/x/y/a": "1", + "x": {"y": {"b": "3"}}, + "m/j/": "3", + "z": "4", + "d": {}, } result = { - '/test/x/y/a': '1', - '/test/x/y/b': '2', - '/test/m/j': '3', - '/test/z': '4', - '/test/d': True, + "/test/x/y/a": "1", + "/test/x/y/b": "2", + "/test/m/j": "3", + "/test/z": "4", + "/test/d": True, } flatten_result = { - '/test/x/y/a': '1', - '/test/x/y/b': '2', - '/test/m/j': '3', - '/test/z': '4', - '/test/d': {} + "/test/x/y/a": "1", + "/test/x/y/b": "2", + "/test/m/j": "3", + "/test/z": "4", + "/test/d": {}, } client._flatten = MagicMock(return_value=flatten_result) - self.assertEqual(client.update('/some/key', path='/blah'), None) + self.assertEqual(client.update("/some/key", path="/blah"), None) + + with patch.object(client, "write", autospec=True) as write_mock: - with patch.object(client, 'write', autospec=True) as write_mock: def write_return(key, val, ttl=None, directory=None): return result.get(key, None) + write_mock.side_effect = write_return - self.assertDictEqual(client.update(some_data, path='/test'), result) - client._flatten.assert_called_with(some_data, '/test') + self.assertDictEqual(client.update(some_data, path="/test"), result) + client._flatten.assert_called_with(some_data, "/test") self.assertEqual(write_mock.call_count, 5) def test_rm(self): - with patch('etcd.Client', autospec=True) as mock: + with patch("etcd.Client", autospec=True) as mock: etcd_client = mock.return_value client = etcd_util.EtcdClient({}) etcd_client.delete.return_value = True - self.assertEqual(client.rm('/some-key'), True) - etcd_client.delete.assert_called_with('/some-key', recursive=False) - self.assertEqual(client.rm('/some-dir', recurse=True), True) - etcd_client.delete.assert_called_with('/some-dir', recursive=True) + self.assertEqual(client.rm("/some-key"), True) + etcd_client.delete.assert_called_with("/some-key", recursive=False) + self.assertEqual(client.rm("/some-dir", recurse=True), True) + etcd_client.delete.assert_called_with("/some-dir", recursive=True) etcd_client.delete.side_effect = etcd.EtcdNotFile() - self.assertEqual(client.rm('/some-dir'), None) + self.assertEqual(client.rm("/some-dir"), None) etcd_client.delete.side_effect = etcd.EtcdDirNotEmpty() - self.assertEqual(client.rm('/some-key'), None) + self.assertEqual(client.rm("/some-key"), None) etcd_client.delete.side_effect = etcd.EtcdRootReadOnly() - self.assertEqual(client.rm('/'), None) + self.assertEqual(client.rm("/"), None) etcd_client.delete.side_effect = ValueError - self.assertEqual(client.rm('/some-dir'), None) + self.assertEqual(client.rm("/some-dir"), None) etcd_client.delete.side_effect = Exception - self.assertRaises(Exception, client.rm, 'some-dir') + self.assertRaises(Exception, client.rm, "some-dir") def test_watch(self): - with patch('etcd.Client', autospec=True) as client_mock: + with patch("etcd.Client", autospec=True) as client_mock: client = etcd_util.EtcdClient({}) - with patch.object(client, 'read', autospec=True) as mock: - mock.return_value = MagicMock(value='stack', key='/some-key', modifiedIndex=1, dir=False) - self.assertDictEqual(client.watch('/some-key'), - {'value': 'stack', 'key': '/some-key', 'mIndex': 1, 'changed': True, 'dir': False}) - mock.assert_called_with('/some-key', wait=True, recursive=False, timeout=0, waitIndex=None) + with patch.object(client, "read", autospec=True) as mock: + mock.return_value = MagicMock( + value="stack", key="/some-key", modifiedIndex=1, dir=False + ) + self.assertDictEqual( + client.watch("/some-key"), + { + "value": "stack", + "key": "/some-key", + "mIndex": 1, + "changed": True, + "dir": False, + }, + ) + mock.assert_called_with( + "/some-key", wait=True, recursive=False, timeout=0, waitIndex=None + ) - mock.side_effect = iter([etcd_util.EtcdUtilWatchTimeout, mock.return_value]) - self.assertDictEqual(client.watch('/some-key'), - {'value': 'stack', 'changed': False, 'mIndex': 1, 'key': '/some-key', 'dir': False}) + mock.side_effect = iter( + [etcd_util.EtcdUtilWatchTimeout, mock.return_value] + ) + self.assertDictEqual( + client.watch("/some-key"), + { + "value": "stack", + "changed": False, + "mIndex": 1, + "key": "/some-key", + "dir": False, + }, + ) - mock.side_effect = iter([etcd_util.EtcdUtilWatchTimeout, etcd.EtcdKeyNotFound]) - self.assertEqual(client.watch('/some-key'), - {'value': None, 'changed': False, 'mIndex': 0, 'key': '/some-key', 'dir': False}) + mock.side_effect = iter( + [etcd_util.EtcdUtilWatchTimeout, etcd.EtcdKeyNotFound] + ) + self.assertEqual( + client.watch("/some-key"), + { + "value": None, + "changed": False, + "mIndex": 0, + "key": "/some-key", + "dir": False, + }, + ) mock.side_effect = iter([etcd_util.EtcdUtilWatchTimeout, ValueError]) - self.assertEqual(client.watch('/some-key'), {}) + self.assertEqual(client.watch("/some-key"), {}) mock.side_effect = None - mock.return_value = MagicMock(value='stack', key='/some-key', modifiedIndex=1, dir=True) - self.assertDictEqual(client.watch('/some-dir', recurse=True, timeout=5, index=10), - {'value': 'stack', 'key': '/some-key', 'mIndex': 1, 'changed': True, 'dir': True}) - mock.assert_called_with('/some-dir', wait=True, recursive=True, timeout=5, waitIndex=10) + mock.return_value = MagicMock( + value="stack", key="/some-key", modifiedIndex=1, dir=True + ) + self.assertDictEqual( + client.watch("/some-dir", recurse=True, timeout=5, index=10), + { + "value": "stack", + "key": "/some-key", + "mIndex": 1, + "changed": True, + "dir": True, + }, + ) + mock.assert_called_with( + "/some-dir", wait=True, recursive=True, timeout=5, waitIndex=10 + ) # iter(list(Exception)) works correctly with both mock<1.1 and mock>=1.1 mock.side_effect = iter([MaxRetryError(None, None)]) - self.assertEqual(client.watch('/some-key'), {}) + self.assertEqual(client.watch("/some-key"), {}) mock.side_effect = iter([etcd.EtcdConnectionFailed()]) - self.assertEqual(client.watch('/some-key'), {}) + self.assertEqual(client.watch("/some-key"), {}) mock.side_effect = None mock.return_value = None - self.assertEqual(client.watch('/some-key'), {}) + self.assertEqual(client.watch("/some-key"), {}) diff --git a/tests/unit/utils/test_event.py b/tests/unit/utils/test_event.py index f86ac517d47..cc637699729 100644 --- a/tests/unit/utils/test_event.py +++ b/tests/unit/utils/test_event.py @@ -1,50 +1,54 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.unit.utils.event_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import os -import hashlib -import time -import shutil -import warnings +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import expectedFailure, skipIf, TestCase -from tests.support.runtests import RUNTIME_VARS -from tests.support.events import eventpublisher_process, eventsender_process +import hashlib +import os +import shutil +import time +import warnings # Import salt libs import salt.config +import salt.ext.tornado.ioloop import salt.utils.event import salt.utils.stringutils +import zmq +import zmq.eventloop.ioloop +from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin # Import 3rd-+arty libs from salt.ext.tornado.testing import AsyncTestCase -import salt.ext.tornado.ioloop -import zmq -import zmq.eventloop.ioloop -# support pyzmq 13.0.x, TODO: remove once we force people to 14.0.x -if not hasattr(zmq.eventloop.ioloop, 'ZMQIOLoop'): - zmq.eventloop.ioloop.ZMQIOLoop = zmq.eventloop.ioloop.IOLoop -from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from tests.support.events import eventpublisher_process, eventsender_process from tests.support.processes import terminate_process +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing libs +from tests.support.unit import TestCase, expectedFailure, skipIf + +# support pyzmq 13.0.x, TODO: remove once we force people to 14.0.x +if not hasattr(zmq.eventloop.ioloop, "ZMQIOLoop"): + zmq.eventloop.ioloop.ZMQIOLoop = zmq.eventloop.ioloop.IOLoop NO_LONG_IPC = False -if getattr(zmq, 'IPC_PATH_MAX_LEN', 103) <= 103: +if getattr(zmq, "IPC_PATH_MAX_LEN", 103) <= 103: NO_LONG_IPC = True -@skipIf(NO_LONG_IPC, "This system does not support long IPC paths. Skipping event tests!") +@skipIf( + NO_LONG_IPC, "This system does not support long IPC paths. Skipping event tests!" +) class TestSaltEvent(TestCase): def setUp(self): - self.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + self.sock_dir = os.path.join(RUNTIME_VARS.TMP, "test-socks") if not os.path.exists(self.sock_dir): os.makedirs(self.sock_dir) self.addCleanup(shutil.rmtree, self.sock_dir, ignore_errors=True) @@ -52,192 +56,182 @@ class TestSaltEvent(TestCase): def assertGotEvent(self, evt, data, msg=None): self.assertIsNotNone(evt, msg) for key in data: - self.assertIn(key, evt, '{0}: Key {1} missing'.format(msg, key)) - assertMsg = '{0}: Key {1} value mismatch, {2} != {3}' + self.assertIn(key, evt, "{0}: Key {1} missing".format(msg, key)) + assertMsg = "{0}: Key {1} value mismatch, {2} != {3}" assertMsg = assertMsg.format(msg, key, data[key], evt[key]) self.assertEqual(data[key], evt[key], assertMsg) def test_master_event(self): me = salt.utils.event.MasterEvent(self.sock_dir, listen=False) self.assertEqual( - me.puburi, '{0}'.format( - os.path.join(self.sock_dir, 'master_event_pub.ipc') - ) + me.puburi, "{0}".format(os.path.join(self.sock_dir, "master_event_pub.ipc")) ) self.assertEqual( me.pulluri, - '{0}'.format( - os.path.join(self.sock_dir, 'master_event_pull.ipc') - ) + "{0}".format(os.path.join(self.sock_dir, "master_event_pull.ipc")), ) def test_minion_event(self): - opts = dict(id='foo', sock_dir=self.sock_dir) - id_hash = hashlib.sha256(salt.utils.stringutils.to_bytes(opts['id'])).hexdigest()[:10] + opts = dict(id="foo", sock_dir=self.sock_dir) + id_hash = hashlib.sha256( + salt.utils.stringutils.to_bytes(opts["id"]) + ).hexdigest()[:10] me = salt.utils.event.MinionEvent(opts, listen=False) self.assertEqual( me.puburi, - '{0}'.format( - os.path.join( - self.sock_dir, 'minion_event_{0}_pub.ipc'.format(id_hash) - ) - ) + "{0}".format( + os.path.join(self.sock_dir, "minion_event_{0}_pub.ipc".format(id_hash)) + ), ) self.assertEqual( me.pulluri, - '{0}'.format( - os.path.join( - self.sock_dir, 'minion_event_{0}_pull.ipc'.format(id_hash) - ) - ) + "{0}".format( + os.path.join(self.sock_dir, "minion_event_{0}_pull.ipc".format(id_hash)) + ), ) def test_minion_event_tcp_ipc_mode(self): - opts = dict(id='foo', ipc_mode='tcp') + opts = dict(id="foo", ipc_mode="tcp") me = salt.utils.event.MinionEvent(opts, listen=False) self.assertEqual(me.puburi, 4510) self.assertEqual(me.pulluri, 4511) def test_minion_event_no_id(self): me = salt.utils.event.MinionEvent(dict(sock_dir=self.sock_dir), listen=False) - id_hash = hashlib.sha256(salt.utils.stringutils.to_bytes('')).hexdigest()[:10] + id_hash = hashlib.sha256(salt.utils.stringutils.to_bytes("")).hexdigest()[:10] self.assertEqual( me.puburi, - '{0}'.format( - os.path.join( - self.sock_dir, 'minion_event_{0}_pub.ipc'.format(id_hash) - ) - ) + "{0}".format( + os.path.join(self.sock_dir, "minion_event_{0}_pub.ipc".format(id_hash)) + ), ) self.assertEqual( me.pulluri, - '{0}'.format( - os.path.join( - self.sock_dir, 'minion_event_{0}_pull.ipc'.format(id_hash) - ) - ) + "{0}".format( + os.path.join(self.sock_dir, "minion_event_{0}_pull.ipc".format(id_hash)) + ), ) def test_event_single(self): - '''Test a single event is received''' + """Test a single event is received""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') - evt1 = me.get_event(tag='evt1') - self.assertGotEvent(evt1, {'data': 'foo1'}) + me.fire_event({"data": "foo1"}, "evt1") + evt1 = me.get_event(tag="evt1") + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_single_no_block(self): - '''Test a single event is received, no block''' + """Test a single event is received, no block""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) start = time.time() finish = start + 5 - evt1 = me.get_event(wait=0, tag='evt1', no_block=True) + evt1 = me.get_event(wait=0, tag="evt1", no_block=True) # We should get None and way before the 5 seconds wait since it's # non-blocking, otherwise it would wait for an event which we # didn't even send self.assertIsNone(evt1, None) self.assertLess(start, finish) - me.fire_event({'data': 'foo1'}, 'evt1') - evt1 = me.get_event(wait=0, tag='evt1') - self.assertGotEvent(evt1, {'data': 'foo1'}) + me.fire_event({"data": "foo1"}, "evt1") + evt1 = me.get_event(wait=0, tag="evt1") + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_single_wait_0_no_block_False(self): - '''Test a single event is received with wait=0 and no_block=False and doesn't spin the while loop''' + """Test a single event is received with wait=0 and no_block=False and doesn't spin the while loop""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') + me.fire_event({"data": "foo1"}, "evt1") # This is too fast and will be None but assures we're not blocking - evt1 = me.get_event(wait=0, tag='evt1', no_block=False) - self.assertGotEvent(evt1, {'data': 'foo1'}) + evt1 = me.get_event(wait=0, tag="evt1", no_block=False) + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_timeout(self): - '''Test no event is received if the timeout is reached''' + """Test no event is received if the timeout is reached""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') - evt1 = me.get_event(tag='evt1') - self.assertGotEvent(evt1, {'data': 'foo1'}) - evt2 = me.get_event(tag='evt1') + me.fire_event({"data": "foo1"}, "evt1") + evt1 = me.get_event(tag="evt1") + self.assertGotEvent(evt1, {"data": "foo1"}) + evt2 = me.get_event(tag="evt1") self.assertIsNone(evt2) def test_event_no_timeout(self): - '''Test no wait timeout, we should block forever, until we get one ''' + """Test no wait timeout, we should block forever, until we get one """ with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - with eventsender_process({'data': 'foo2'}, 'evt2', self.sock_dir, 5): - evt = me.get_event(tag='evt2', wait=0, no_block=False) - self.assertGotEvent(evt, {'data': 'foo2'}) + with eventsender_process({"data": "foo2"}, "evt2", self.sock_dir, 5): + evt = me.get_event(tag="evt2", wait=0, no_block=False) + self.assertGotEvent(evt, {"data": "foo2"}) def test_event_matching(self): - '''Test a startswith match''' + """Test a startswith match""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') - evt1 = me.get_event(tag='ev') - self.assertGotEvent(evt1, {'data': 'foo1'}) + me.fire_event({"data": "foo1"}, "evt1") + evt1 = me.get_event(tag="ev") + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_matching_regex(self): - '''Test a regex match''' + """Test a regex match""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') - evt1 = me.get_event(tag='^ev', match_type='regex') - self.assertGotEvent(evt1, {'data': 'foo1'}) + me.fire_event({"data": "foo1"}, "evt1") + evt1 = me.get_event(tag="^ev", match_type="regex") + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_matching_all(self): - '''Test an all match''' + """Test an all match""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') - evt1 = me.get_event(tag='') - self.assertGotEvent(evt1, {'data': 'foo1'}) + me.fire_event({"data": "foo1"}, "evt1") + evt1 = me.get_event(tag="") + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_matching_all_when_tag_is_None(self): - '''Test event matching all when not passing a tag''' + """Test event matching all when not passing a tag""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') + me.fire_event({"data": "foo1"}, "evt1") evt1 = me.get_event() - self.assertGotEvent(evt1, {'data': 'foo1'}) + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_not_subscribed(self): - '''Test get_event drops non-subscribed events''' + """Test get_event drops non-subscribed events""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') - me.fire_event({'data': 'foo2'}, 'evt2') - evt2 = me.get_event(tag='evt2') - evt1 = me.get_event(tag='evt1') - self.assertGotEvent(evt2, {'data': 'foo2'}) + me.fire_event({"data": "foo1"}, "evt1") + me.fire_event({"data": "foo2"}, "evt2") + evt2 = me.get_event(tag="evt2") + evt1 = me.get_event(tag="evt1") + self.assertGotEvent(evt2, {"data": "foo2"}) self.assertIsNone(evt1) def test_event_subscription_cache(self): - '''Test subscriptions cache a message until requested''' + """Test subscriptions cache a message until requested""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.subscribe('evt1') - me.fire_event({'data': 'foo1'}, 'evt1') - me.fire_event({'data': 'foo2'}, 'evt2') - evt2 = me.get_event(tag='evt2') - evt1 = me.get_event(tag='evt1') - self.assertGotEvent(evt2, {'data': 'foo2'}) - self.assertGotEvent(evt1, {'data': 'foo1'}) + me.subscribe("evt1") + me.fire_event({"data": "foo1"}, "evt1") + me.fire_event({"data": "foo2"}, "evt2") + evt2 = me.get_event(tag="evt2") + evt1 = me.get_event(tag="evt1") + self.assertGotEvent(evt2, {"data": "foo2"}) + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_subscriptions_cache_regex(self): - '''Test regex subscriptions cache a message until requested''' + """Test regex subscriptions cache a message until requested""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.subscribe('e..1$', 'regex') - me.fire_event({'data': 'foo1'}, 'evt1') - me.fire_event({'data': 'foo2'}, 'evt2') - evt2 = me.get_event(tag='evt2') - evt1 = me.get_event(tag='evt1') - self.assertGotEvent(evt2, {'data': 'foo2'}) - self.assertGotEvent(evt1, {'data': 'foo1'}) + me.subscribe("e..1$", "regex") + me.fire_event({"data": "foo1"}, "evt1") + me.fire_event({"data": "foo2"}, "evt2") + evt2 = me.get_event(tag="evt2") + evt1 = me.get_event(tag="evt1") + self.assertGotEvent(evt2, {"data": "foo2"}) + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_multiple_clients(self): - '''Test event is received by multiple clients''' + """Test event is received by multiple clients""" with eventpublisher_process(self.sock_dir): me1 = salt.utils.event.MasterEvent(self.sock_dir, listen=True) me2 = salt.utils.event.MasterEvent(self.sock_dir, listen=True) @@ -245,56 +239,63 @@ class TestSaltEvent(TestCase): # the second socket may not be connected by the time the first socket # sends the event. time.sleep(0.5) - me1.fire_event({'data': 'foo1'}, 'evt1') - evt1 = me1.get_event(tag='evt1') - self.assertGotEvent(evt1, {'data': 'foo1'}) - evt2 = me2.get_event(tag='evt1') - self.assertGotEvent(evt2, {'data': 'foo1'}) + me1.fire_event({"data": "foo1"}, "evt1") + evt1 = me1.get_event(tag="evt1") + self.assertGotEvent(evt1, {"data": "foo1"}) + evt2 = me2.get_event(tag="evt1") + self.assertGotEvent(evt2, {"data": "foo1"}) @expectedFailure def test_event_nested_sub_all(self): - '''Test nested event subscriptions do not drop events, get event for all tags''' + """Test nested event subscriptions do not drop events, get event for all tags""" # Show why not to call get_event(tag='') with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') - me.fire_event({'data': 'foo2'}, 'evt2') - evt2 = me.get_event(tag='') - evt1 = me.get_event(tag='') - self.assertGotEvent(evt2, {'data': 'foo2'}) - self.assertGotEvent(evt1, {'data': 'foo1'}) + me.fire_event({"data": "foo1"}, "evt1") + me.fire_event({"data": "foo2"}, "evt2") + evt2 = me.get_event(tag="") + evt1 = me.get_event(tag="") + self.assertGotEvent(evt2, {"data": "foo2"}) + self.assertGotEvent(evt1, {"data": "foo1"}) def test_event_many(self): - '''Test a large number of events, one at a time''' + """Test a large number of events, one at a time""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) for i in range(500): - me.fire_event({'data': '{0}'.format(i)}, 'testevents') - evt = me.get_event(tag='testevents') - self.assertGotEvent(evt, {'data': '{0}'.format(i)}, 'Event {0}'.format(i)) + me.fire_event({"data": "{0}".format(i)}, "testevents") + evt = me.get_event(tag="testevents") + self.assertGotEvent( + evt, {"data": "{0}".format(i)}, "Event {0}".format(i) + ) def test_event_many_backlog(self): - '''Test a large number of events, send all then recv all''' + """Test a large number of events, send all then recv all""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) # Must not exceed zmq HWM for i in range(500): - me.fire_event({'data': '{0}'.format(i)}, 'testevents') + me.fire_event({"data": "{0}".format(i)}, "testevents") for i in range(500): - evt = me.get_event(tag='testevents') - self.assertGotEvent(evt, {'data': '{0}'.format(i)}, 'Event {0}'.format(i)) + evt = me.get_event(tag="testevents") + self.assertGotEvent( + evt, {"data": "{0}".format(i)}, "Event {0}".format(i) + ) # Test the fire_master function. As it wraps the underlying fire_event, # we don't need to perform extensive testing. def test_send_master_event(self): - '''Tests that sending an event through fire_master generates expected event''' + """Tests that sending an event through fire_master generates expected event""" with eventpublisher_process(self.sock_dir): me = salt.utils.event.MasterEvent(self.sock_dir, listen=True) - data = {'data': 'foo1'} - me.fire_master(data, 'test_master') + data = {"data": "foo1"} + me.fire_master(data, "test_master") - evt = me.get_event(tag='fire_master') - self.assertGotEvent(evt, {'data': data, 'tag': 'test_master', 'events': None, 'pretag': None}) + evt = me.get_event(tag="fire_master") + self.assertGotEvent( + evt, + {"data": data, "tag": "test_master", "events": None, "pretag": None}, + ) class TestAsyncEventPublisher(AsyncTestCase): @@ -303,17 +304,16 @@ class TestAsyncEventPublisher(AsyncTestCase): def setUp(self): super(TestAsyncEventPublisher, self).setUp() - self.sock_dir = os.path.join(RUNTIME_VARS.TMP, 'test-socks') + self.sock_dir = os.path.join(RUNTIME_VARS.TMP, "test-socks") if not os.path.exists(self.sock_dir): os.makedirs(self.sock_dir) self.addCleanup(shutil.rmtree, self.sock_dir, ignore_errors=True) - self.opts = {'sock_dir': self.sock_dir} - self.publisher = salt.utils.event.AsyncEventPublisher( - self.opts, - self.io_loop, + self.opts = {"sock_dir": self.sock_dir} + self.publisher = salt.utils.event.AsyncEventPublisher(self.opts, self.io_loop,) + self.event = salt.utils.event.get_event( + "minion", opts=self.opts, io_loop=self.io_loop ) - self.event = salt.utils.event.get_event('minion', opts=self.opts, io_loop=self.io_loop) - self.event.subscribe('') + self.event.subscribe("") self.event.set_event_handler(self._handle_publish) def _handle_publish(self, raw): @@ -321,18 +321,17 @@ class TestAsyncEventPublisher(AsyncTestCase): self.stop() def test_event_subscription(self): - '''Test a single event is received''' + """Test a single event is received""" me = salt.utils.event.MinionEvent(self.opts, listen=True) - me.fire_event({'data': 'foo1'}, 'evt1') + me.fire_event({"data": "foo1"}, "evt1") self.wait() - evt1 = me.get_event(tag='evt1') - self.assertEqual(self.tag, 'evt1') - self.data.pop('_stamp') # drop the stamp - self.assertEqual(self.data, {'data': 'foo1'}) + evt1 = me.get_event(tag="evt1") + self.assertEqual(self.tag, "evt1") + self.data.pop("_stamp") # drop the stamp + self.assertEqual(self.data, {"data": "foo1"}) class TestEventReturn(TestCase): - def test_event_return(self): # Once salt is py3 only, the warnings part of this test no longer applies evt = None @@ -342,14 +341,18 @@ class TestEventReturn(TestCase): warnings.simplefilter("always") evt = None try: - evt = salt.utils.event.EventReturn(salt.config.DEFAULT_MASTER_OPTS.copy()) + evt = salt.utils.event.EventReturn( + salt.config.DEFAULT_MASTER_OPTS.copy() + ) evt.start() except TypeError as exc: - if 'object' in str(exc): - self.fail('\'{}\' TypeError should have not been raised'.format(exc)) + if "object" in str(exc): + self.fail( + "'{}' TypeError should have not been raised".format(exc) + ) for warning in w: if warning.category is DeprecationWarning: - assert 'object() takes no parameters' not in warning.message + assert "object() takes no parameters" not in warning.message finally: if evt is not None: terminate_process(evt.pid, kill_children=True) diff --git a/tests/unit/utils/test_extend.py b/tests/unit/utils/test_extend.py index 27dbbf387ff..37f9e9135fa 100644 --- a/tests/unit/utils/test_extend.py +++ b/tests/unit/utils/test_extend.py @@ -1,26 +1,26 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.utils.extend_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Test the salt extend script, leave templates/test alone to keep this working! -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals import os import shutil from datetime import date -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import MagicMock, patch -from tests.support.runtests import RUNTIME_VARS - # Import salt libs import salt.utils.extend import salt.utils.files +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf class ExtendTestCase(TestCase): @@ -35,16 +35,22 @@ class ExtendTestCase(TestCase): shutil.rmtree(self.out, True) os.chdir(self.starting_dir) - @skipIf(not os.path.exists(os.path.join(RUNTIME_VARS.CODE_DIR, 'templates')), - "Test template directory 'templates/' missing.") + @skipIf( + not os.path.exists(os.path.join(RUNTIME_VARS.CODE_DIR, "templates")), + "Test template directory 'templates/' missing.", + ) def test_run(self): - with patch('sys.exit', MagicMock): - out = salt.utils.extend.run('test', 'test', 'this description', RUNTIME_VARS.CODE_DIR, False) + with patch("sys.exit", MagicMock): + out = salt.utils.extend.run( + "test", "test", "this description", RUNTIME_VARS.CODE_DIR, False + ) self.out = out - year = date.today().strftime('%Y') + year = date.today().strftime("%Y") self.assertTrue(os.path.exists(out)) - self.assertFalse(os.path.exists(os.path.join(out, 'template.yml'))) - self.assertTrue(os.path.exists(os.path.join(out, 'directory'))) - self.assertTrue(os.path.exists(os.path.join(out, 'directory', 'test.py'))) - with salt.utils.files.fopen(os.path.join(out, 'directory', 'test.py'), 'r') as test_f: + self.assertFalse(os.path.exists(os.path.join(out, "template.yml"))) + self.assertTrue(os.path.exists(os.path.join(out, "directory"))) + self.assertTrue(os.path.exists(os.path.join(out, "directory", "test.py"))) + with salt.utils.files.fopen( + os.path.join(out, "directory", "test.py"), "r" + ) as test_f: self.assertEqual(test_f.read(), year) diff --git a/tests/unit/utils/test_filebuffer.py b/tests/unit/utils/test_filebuffer.py index c335ff05dac..e9f77bb4152 100644 --- a/tests/unit/utils/test_filebuffer.py +++ b/tests/unit/utils/test_filebuffer.py @@ -1,43 +1,44 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.unit.utils.filebuffer_test ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -import os +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.paths import BASE_FILES +import os # Import salt libs from salt.utils.filebuffer import BufferedReader, InvalidFileMode +from tests.support.paths import BASE_FILES + +# Import Salt Testing libs +from tests.support.unit import TestCase class TestFileBuffer(TestCase): def test_read_only_mode(self): with self.assertRaises(InvalidFileMode): - BufferedReader('/tmp/foo', mode='a') + BufferedReader("/tmp/foo", mode="a") with self.assertRaises(InvalidFileMode): - BufferedReader('/tmp/foo', mode='ab') + BufferedReader("/tmp/foo", mode="ab") with self.assertRaises(InvalidFileMode): - BufferedReader('/tmp/foo', mode='w') + BufferedReader("/tmp/foo", mode="w") with self.assertRaises(InvalidFileMode): - BufferedReader('/tmp/foo', mode='wb') + BufferedReader("/tmp/foo", mode="wb") def test_issue_51309(self): - ''' + """ https://github.com/saltstack/salt/issues/51309 - ''' - file_name = os.path.join(BASE_FILES, 'grail', 'scene33') + """ + file_name = os.path.join(BASE_FILES, "grail", "scene33") def find_value(text): stripped_text = text.strip() @@ -50,4 +51,4 @@ class TestFileBuffer(TestCase): except (IOError, OSError): return False - self.assertTrue(find_value('We have the Holy Hand Grenade')) + self.assertTrue(find_value("We have the Holy Hand Grenade")) diff --git a/tests/unit/utils/test_files.py b/tests/unit/utils/test_files.py index 095a21e009d..66845352d60 100644 --- a/tests/unit/utils/test_files.py +++ b/tests/unit/utils/test_files.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Unit Tests for functions located in salt/utils/files.py -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import copy import os @@ -14,65 +15,70 @@ from salt.ext import six # Import Salt Testing libs from tests.support.helpers import with_tempdir +from tests.support.mock import patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, -) class FilesTestCase(TestCase): - ''' + """ Test case for files util. - ''' + """ def test_safe_rm(self): - with patch('os.remove') as os_remove_mock: - salt.utils.files.safe_rm('dummy_tgt') + with patch("os.remove") as os_remove_mock: + salt.utils.files.safe_rm("dummy_tgt") self.assertTrue(os_remove_mock.called) - @skipIf(os.path.exists('/tmp/no_way_this_is_a_file_nope.sh'), 'Test file exists! Skipping safe_rm_exceptions test!') + @skipIf( + os.path.exists("/tmp/no_way_this_is_a_file_nope.sh"), + "Test file exists! Skipping safe_rm_exceptions test!", + ) def test_safe_rm_exceptions(self): error = False try: - salt.utils.files.safe_rm('/tmp/no_way_this_is_a_file_nope.sh') + salt.utils.files.safe_rm("/tmp/no_way_this_is_a_file_nope.sh") except (IOError, OSError): error = True - self.assertFalse(error, 'salt.utils.files.safe_rm raised exception when it should not have') + self.assertFalse( + error, "salt.utils.files.safe_rm raised exception when it should not have" + ) @with_tempdir() def test_safe_walk_symlink_recursion(self, tmp): if os.stat(tmp).st_ino == 0: - self.skipTest('inodes not supported in {0}'.format(tmp)) - os.mkdir(os.path.join(tmp, 'fax')) - os.makedirs(os.path.join(tmp, 'foo', 'bar')) - os.symlink(os.path.join('..', '..'), os.path.join(tmp, 'foo', 'bar', 'baz')) - os.symlink('foo', os.path.join(tmp, 'root')) + self.skipTest("inodes not supported in {0}".format(tmp)) + os.mkdir(os.path.join(tmp, "fax")) + os.makedirs(os.path.join(tmp, "foo", "bar")) + os.symlink(os.path.join("..", ".."), os.path.join(tmp, "foo", "bar", "baz")) + os.symlink("foo", os.path.join(tmp, "root")) expected = [ - (os.path.join(tmp, 'root'), ['bar'], []), - (os.path.join(tmp, 'root', 'bar'), ['baz'], []), - (os.path.join(tmp, 'root', 'bar', 'baz'), ['fax', 'foo', 'root'], []), - (os.path.join(tmp, 'root', 'bar', 'baz', 'fax'), [], []), + (os.path.join(tmp, "root"), ["bar"], []), + (os.path.join(tmp, "root", "bar"), ["baz"], []), + (os.path.join(tmp, "root", "bar", "baz"), ["fax", "foo", "root"], []), + (os.path.join(tmp, "root", "bar", "baz", "fax"), [], []), ] paths = [] - for root, dirs, names in salt.utils.files.safe_walk(os.path.join(tmp, 'root')): + for root, dirs, names in salt.utils.files.safe_walk(os.path.join(tmp, "root")): paths.append((root, sorted(dirs), names)) if paths != expected: raise AssertionError( - '\n'.join( - ['got:'] + [repr(p) for p in paths] + - ['', 'expected:'] + [repr(p) for p in expected] + "\n".join( + ["got:"] + + [repr(p) for p in paths] + + ["", "expected:"] + + [repr(p) for p in expected] ) ) - @skipIf(not six.PY3, 'This test only applies to Python 3') + @skipIf(not six.PY3, "This test only applies to Python 3") def test_fopen_with_disallowed_fds(self): - ''' + """ This is safe to have as a unit test since we aren't going to actually try to read or write. We want to ensure that we are raising a TypeError. Python 3's open() builtin will treat the booleans as file descriptor numbers and try to open stdin/stdout. We also want to test fd 2 which is stderr. - ''' + """ for invalid_fn in (False, True, 0, 1, 2): try: with salt.utils.files.fopen(invalid_fn): @@ -89,8 +95,8 @@ class FilesTestCase(TestCase): # stdin/stdout/stderr is usually not a wise thing to do in the # middle of a program's execution. self.fail( - 'fopen() should have been prevented from opening a file ' - 'using {0} as the filename'.format(invalid_fn) + "fopen() should have been prevented from opening a file " + "using {0} as the filename".format(invalid_fn) ) def _create_temp_structure(self, temp_directory, structure): @@ -99,11 +105,12 @@ class FilesTestCase(TestCase): os.makedirs(current_directory) for name, content in six.iteritems(files): path = os.path.join(temp_directory, folder, name) - with salt.utils.files.fopen(path, 'w+') as fh: + with salt.utils.files.fopen(path, "w+") as fh: fh.write(content) - def _validate_folder_structure_and_contents(self, target_directory, - desired_structure): + def _validate_folder_structure_and_contents( + self, target_directory, desired_structure + ): for folder, files in six.iteritems(desired_structure): for name, content in six.iteritems(files): path = os.path.join(target_directory, folder, name) @@ -114,20 +121,12 @@ class FilesTestCase(TestCase): @with_tempdir() def test_recursive_copy(self, src, dest): src_structure = { - 'foo': { - 'foofile.txt': 'fooSTRUCTURE' - }, - 'bar': { - 'barfile.txt': 'barSTRUCTURE' - } + "foo": {"foofile.txt": "fooSTRUCTURE"}, + "bar": {"barfile.txt": "barSTRUCTURE"}, } dest_structure = { - 'foo': { - 'foo.txt': 'fooTARGET_STRUCTURE' - }, - 'baz': { - 'baz.txt': 'bazTARGET_STRUCTURE' - } + "foo": {"foo.txt": "fooTARGET_STRUCTURE"}, + "baz": {"baz.txt": "bazTARGET_STRUCTURE"}, } # Create the file structures in both src and dest dirs @@ -140,6 +139,4 @@ class FilesTestCase(TestCase): # Confirm results match expected results desired_structure = copy.copy(dest_structure) desired_structure.update(src_structure) - self._validate_folder_structure_and_contents( - dest, - desired_structure) + self._validate_folder_structure_and_contents(dest, desired_structure) diff --git a/tests/unit/utils/test_find.py b/tests/unit/utils/test_find.py index ac78fe596b6..6f4b5dbed46 100644 --- a/tests/unit/utils/test_find.py +++ b/tests/unit/utils/test_find.py @@ -1,129 +1,129 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, unicode_literals, print_function -import os -import sys -import shutil -import tempfile -import stat +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import skipIf, TestCase +import os +import shutil +import stat +import sys +import tempfile # Import salt libs import salt.utils.files import salt.utils.find +from salt.ext import six # Import 3rd-party libs from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -from salt.ext import six + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf class TestFind(TestCase): - def test_parse_interval(self): - self.assertRaises(ValueError, salt.utils.find._parse_interval, 'w') - self.assertRaises(ValueError, salt.utils.find._parse_interval, '1') - self.assertRaises(ValueError, salt.utils.find._parse_interval, '1s1w') - self.assertRaises(ValueError, salt.utils.find._parse_interval, '1s1s') + self.assertRaises(ValueError, salt.utils.find._parse_interval, "w") + self.assertRaises(ValueError, salt.utils.find._parse_interval, "1") + self.assertRaises(ValueError, salt.utils.find._parse_interval, "1s1w") + self.assertRaises(ValueError, salt.utils.find._parse_interval, "1s1s") - result, resolution, modifier = salt.utils.find._parse_interval('') + result, resolution, modifier = salt.utils.find._parse_interval("") self.assertEqual(result, 0) self.assertIs(resolution, None) - self.assertEqual(modifier, '') + self.assertEqual(modifier, "") - result, resolution, modifier = salt.utils.find._parse_interval('1s') + result, resolution, modifier = salt.utils.find._parse_interval("1s") self.assertEqual(result, 1.0) self.assertEqual(resolution, 1) - self.assertEqual(modifier, '') + self.assertEqual(modifier, "") - result, resolution, modifier = salt.utils.find._parse_interval('1m') + result, resolution, modifier = salt.utils.find._parse_interval("1m") self.assertEqual(result, 60.0) self.assertEqual(resolution, 60) - self.assertEqual(modifier, '') + self.assertEqual(modifier, "") - result, resolution, modifier = salt.utils.find._parse_interval('1h') + result, resolution, modifier = salt.utils.find._parse_interval("1h") self.assertEqual(result, 3600.0) self.assertEqual(resolution, 3600) - self.assertEqual(modifier, '') + self.assertEqual(modifier, "") - result, resolution, modifier = salt.utils.find._parse_interval('1d') + result, resolution, modifier = salt.utils.find._parse_interval("1d") self.assertEqual(result, 86400.0) self.assertEqual(resolution, 86400) - self.assertEqual(modifier, '') + self.assertEqual(modifier, "") - result, resolution, modifier = salt.utils.find._parse_interval('1w') + result, resolution, modifier = salt.utils.find._parse_interval("1w") self.assertEqual(result, 604800.0) self.assertEqual(resolution, 604800) - self.assertEqual(modifier, '') + self.assertEqual(modifier, "") - result, resolution, modifier = salt.utils.find._parse_interval('1w3d6h') + result, resolution, modifier = salt.utils.find._parse_interval("1w3d6h") self.assertEqual(result, 885600.0) self.assertEqual(resolution, 3600) - self.assertEqual(modifier, '') + self.assertEqual(modifier, "") - result, resolution, modifier = salt.utils.find._parse_interval('1m1s') + result, resolution, modifier = salt.utils.find._parse_interval("1m1s") self.assertEqual(result, 61.0) self.assertEqual(resolution, 1) - self.assertEqual(modifier, '') + self.assertEqual(modifier, "") - result, resolution, modifier = salt.utils.find._parse_interval('1m2s') + result, resolution, modifier = salt.utils.find._parse_interval("1m2s") self.assertEqual(result, 62.0) self.assertEqual(resolution, 1) - self.assertEqual(modifier, '') + self.assertEqual(modifier, "") - result, resolution, modifier = salt.utils.find._parse_interval('+1d') + result, resolution, modifier = salt.utils.find._parse_interval("+1d") self.assertEqual(result, 86400.0) self.assertEqual(resolution, 86400) - self.assertEqual(modifier, '+') + self.assertEqual(modifier, "+") - result, resolution, modifier = salt.utils.find._parse_interval('-1d') + result, resolution, modifier = salt.utils.find._parse_interval("-1d") self.assertEqual(result, 86400.0) self.assertEqual(resolution, 86400) - self.assertEqual(modifier, '-') + self.assertEqual(modifier, "-") def test_parse_size(self): - self.assertRaises(ValueError, salt.utils.find._parse_size, '') - self.assertRaises(ValueError, salt.utils.find._parse_size, '1s1s') - min_size, max_size = salt.utils.find._parse_size('1') + self.assertRaises(ValueError, salt.utils.find._parse_size, "") + self.assertRaises(ValueError, salt.utils.find._parse_size, "1s1s") + min_size, max_size = salt.utils.find._parse_size("1") self.assertEqual(min_size, 1) self.assertEqual(max_size, 1) - min_size, max_size = salt.utils.find._parse_size('1b') + min_size, max_size = salt.utils.find._parse_size("1b") self.assertEqual(min_size, 1) self.assertEqual(max_size, 1) - min_size, max_size = salt.utils.find._parse_size('1k') + min_size, max_size = salt.utils.find._parse_size("1k") self.assertEqual(min_size, 1024) self.assertEqual(max_size, 2047) - min_size, max_size = salt.utils.find._parse_size('1m') + min_size, max_size = salt.utils.find._parse_size("1m") self.assertEqual(min_size, 1048576) self.assertEqual(max_size, 2097151) - min_size, max_size = salt.utils.find._parse_size('1g') + min_size, max_size = salt.utils.find._parse_size("1g") self.assertEqual(min_size, 1073741824) self.assertEqual(max_size, 2147483647) - min_size, max_size = salt.utils.find._parse_size('1t') + min_size, max_size = salt.utils.find._parse_size("1t") self.assertEqual(min_size, 1099511627776) self.assertEqual(max_size, 2199023255551) - min_size, max_size = salt.utils.find._parse_size('0m') + min_size, max_size = salt.utils.find._parse_size("0m") self.assertEqual(min_size, 0) self.assertEqual(max_size, 1048575) - min_size, max_size = salt.utils.find._parse_size('-1m') + min_size, max_size = salt.utils.find._parse_size("-1m") self.assertEqual(min_size, 0) self.assertEqual(max_size, 1048576) - min_size, max_size = salt.utils.find._parse_size('+1m') + min_size, max_size = salt.utils.find._parse_size("+1m") self.assertEqual(min_size, 1048576) self.assertEqual(max_size, sys.maxsize) - min_size, max_size = salt.utils.find._parse_size('+1M') + min_size, max_size = salt.utils.find._parse_size("+1M") self.assertEqual(min_size, 1048576) self.assertEqual(max_size, sys.maxsize) @@ -132,158 +132,145 @@ class TestFind(TestCase): self.assertEqual(option.requires(), salt.utils.find._REQUIRES_PATH) def test_name_option_match(self): - option = salt.utils.find.NameOption('name', '*.txt') - self.assertIs(option.match('', '', ''), None) - self.assertIs(option.match('', 'hello.txt', '').group(), 'hello.txt') - self.assertIs(option.match('', 'HELLO.TXT', ''), None) + option = salt.utils.find.NameOption("name", "*.txt") + self.assertIs(option.match("", "", ""), None) + self.assertIs(option.match("", "hello.txt", "").group(), "hello.txt") + self.assertIs(option.match("", "HELLO.TXT", ""), None) def test_iname_option_match(self): - option = salt.utils.find.InameOption('name', '*.txt') - self.assertIs(option.match('', '', ''), None) - self.assertIs(option.match('', 'hello.txt', '').group(), 'hello.txt') - self.assertIs(option.match('', 'HELLO.TXT', '').group(), 'HELLO.TXT') + option = salt.utils.find.InameOption("name", "*.txt") + self.assertIs(option.match("", "", ""), None) + self.assertIs(option.match("", "hello.txt", "").group(), "hello.txt") + self.assertIs(option.match("", "HELLO.TXT", "").group(), "HELLO.TXT") def test_regex_option_match(self): - self.assertRaises( - ValueError, salt.utils.find.RegexOption, 'name', '(.*}' - ) + self.assertRaises(ValueError, salt.utils.find.RegexOption, "name", "(.*}") - option = salt.utils.find.RegexOption('name', r'.*\.txt') - self.assertIs(option.match('', '', ''), None) - self.assertIs(option.match('', 'hello.txt', '').group(), 'hello.txt') - self.assertIs(option.match('', 'HELLO.TXT', ''), None) + option = salt.utils.find.RegexOption("name", r".*\.txt") + self.assertIs(option.match("", "", ""), None) + self.assertIs(option.match("", "hello.txt", "").group(), "hello.txt") + self.assertIs(option.match("", "HELLO.TXT", ""), None) def test_iregex_option_match(self): - self.assertRaises( - ValueError, salt.utils.find.IregexOption, 'name', '(.*}' - ) + self.assertRaises(ValueError, salt.utils.find.IregexOption, "name", "(.*}") - option = salt.utils.find.IregexOption('name', r'.*\.txt') - self.assertIs(option.match('', '', ''), None) - self.assertIs(option.match('', 'hello.txt', '').group(), 'hello.txt') - self.assertIs(option.match('', 'HELLO.TXT', '').group(), 'HELLO.TXT') + option = salt.utils.find.IregexOption("name", r".*\.txt") + self.assertIs(option.match("", "", ""), None) + self.assertIs(option.match("", "hello.txt", "").group(), "hello.txt") + self.assertIs(option.match("", "HELLO.TXT", "").group(), "HELLO.TXT") def test_type_option_requires(self): - self.assertRaises(ValueError, salt.utils.find.TypeOption, 'type', 'w') + self.assertRaises(ValueError, salt.utils.find.TypeOption, "type", "w") - option = salt.utils.find.TypeOption('type', 'd') + option = salt.utils.find.TypeOption("type", "d") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT) def test_type_option_match(self): - option = salt.utils.find.TypeOption('type', 'b') - self.assertEqual(option.match('', '', [stat.S_IFREG]), False) + option = salt.utils.find.TypeOption("type", "b") + self.assertEqual(option.match("", "", [stat.S_IFREG]), False) - option = salt.utils.find.TypeOption('type', 'c') - self.assertEqual(option.match('', '', [stat.S_IFREG]), False) + option = salt.utils.find.TypeOption("type", "c") + self.assertEqual(option.match("", "", [stat.S_IFREG]), False) - option = salt.utils.find.TypeOption('type', 'd') - self.assertEqual(option.match('', '', [stat.S_IFREG]), False) + option = salt.utils.find.TypeOption("type", "d") + self.assertEqual(option.match("", "", [stat.S_IFREG]), False) - option = salt.utils.find.TypeOption('type', 'f') - self.assertEqual(option.match('', '', [stat.S_IFREG]), True) + option = salt.utils.find.TypeOption("type", "f") + self.assertEqual(option.match("", "", [stat.S_IFREG]), True) - option = salt.utils.find.TypeOption('type', 'l') - self.assertEqual(option.match('', '', [stat.S_IFREG]), False) + option = salt.utils.find.TypeOption("type", "l") + self.assertEqual(option.match("", "", [stat.S_IFREG]), False) - option = salt.utils.find.TypeOption('type', 'p') - self.assertEqual(option.match('', '', [stat.S_IFREG]), False) + option = salt.utils.find.TypeOption("type", "p") + self.assertEqual(option.match("", "", [stat.S_IFREG]), False) - option = salt.utils.find.TypeOption('type', 's') - self.assertEqual(option.match('', '', [stat.S_IFREG]), False) + option = salt.utils.find.TypeOption("type", "s") + self.assertEqual(option.match("", "", [stat.S_IFREG]), False) - option = salt.utils.find.TypeOption('type', 'b') - self.assertEqual(option.match('', '', [stat.S_IFBLK]), True) + option = salt.utils.find.TypeOption("type", "b") + self.assertEqual(option.match("", "", [stat.S_IFBLK]), True) - option = salt.utils.find.TypeOption('type', 'c') - self.assertEqual(option.match('', '', [stat.S_IFCHR]), True) + option = salt.utils.find.TypeOption("type", "c") + self.assertEqual(option.match("", "", [stat.S_IFCHR]), True) - option = salt.utils.find.TypeOption('type', 'd') - self.assertEqual(option.match('', '', [stat.S_IFDIR]), True) + option = salt.utils.find.TypeOption("type", "d") + self.assertEqual(option.match("", "", [stat.S_IFDIR]), True) - option = salt.utils.find.TypeOption('type', 'l') - self.assertEqual(option.match('', '', [stat.S_IFLNK]), True) + option = salt.utils.find.TypeOption("type", "l") + self.assertEqual(option.match("", "", [stat.S_IFLNK]), True) - option = salt.utils.find.TypeOption('type', 'p') - self.assertEqual(option.match('', '', [stat.S_IFIFO]), True) + option = salt.utils.find.TypeOption("type", "p") + self.assertEqual(option.match("", "", [stat.S_IFIFO]), True) - option = salt.utils.find.TypeOption('type', 's') - self.assertEqual(option.match('', '', [stat.S_IFSOCK]), True) + option = salt.utils.find.TypeOption("type", "s") + self.assertEqual(option.match("", "", [stat.S_IFSOCK]), True) - @skipIf(sys.platform.startswith('win'), 'pwd not available on Windows') + @skipIf(sys.platform.startswith("win"), "pwd not available on Windows") def test_owner_option_requires(self): - self.assertRaises( - ValueError, salt.utils.find.OwnerOption, 'owner', 'notexist' - ) + self.assertRaises(ValueError, salt.utils.find.OwnerOption, "owner", "notexist") - option = salt.utils.find.OwnerOption('owner', 'root') + option = salt.utils.find.OwnerOption("owner", "root") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT) - @skipIf(sys.platform.startswith('win'), 'pwd not available on Windows') + @skipIf(sys.platform.startswith("win"), "pwd not available on Windows") def test_owner_option_match(self): - option = salt.utils.find.OwnerOption('owner', 'root') - self.assertEqual(option.match('', '', [0] * 5), True) + option = salt.utils.find.OwnerOption("owner", "root") + self.assertEqual(option.match("", "", [0] * 5), True) - option = salt.utils.find.OwnerOption('owner', '500') - self.assertEqual(option.match('', '', [500] * 5), True) + option = salt.utils.find.OwnerOption("owner", "500") + self.assertEqual(option.match("", "", [500] * 5), True) - @skipIf(sys.platform.startswith('win'), 'grp not available on Windows') + @skipIf(sys.platform.startswith("win"), "grp not available on Windows") def test_group_option_requires(self): - self.assertRaises( - ValueError, salt.utils.find.GroupOption, 'group', 'notexist' - ) + self.assertRaises(ValueError, salt.utils.find.GroupOption, "group", "notexist") - if sys.platform.startswith(('darwin', 'freebsd', 'openbsd')): - group_name = 'wheel' + if sys.platform.startswith(("darwin", "freebsd", "openbsd")): + group_name = "wheel" else: - group_name = 'root' - option = salt.utils.find.GroupOption('group', group_name) + group_name = "root" + option = salt.utils.find.GroupOption("group", group_name) self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT) - @skipIf(sys.platform.startswith('win'), 'grp not available on Windows') + @skipIf(sys.platform.startswith("win"), "grp not available on Windows") def test_group_option_match(self): - if sys.platform.startswith(('darwin', 'freebsd', 'openbsd')): - group_name = 'wheel' + if sys.platform.startswith(("darwin", "freebsd", "openbsd")): + group_name = "wheel" else: - group_name = 'root' - option = salt.utils.find.GroupOption('group', group_name) - self.assertEqual(option.match('', '', [0] * 6), True) + group_name = "root" + option = salt.utils.find.GroupOption("group", group_name) + self.assertEqual(option.match("", "", [0] * 6), True) - option = salt.utils.find.GroupOption('group', '500') - self.assertEqual(option.match('', '', [500] * 6), True) + option = salt.utils.find.GroupOption("group", "500") + self.assertEqual(option.match("", "", [500] * 6), True) def test_size_option_requires(self): - self.assertRaises( - ValueError, salt.utils.find.SizeOption, 'size', '1s1s' - ) + self.assertRaises(ValueError, salt.utils.find.SizeOption, "size", "1s1s") - option = salt.utils.find.SizeOption('size', '+1G') + option = salt.utils.find.SizeOption("size", "+1G") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT) def test_size_option_match(self): - option = salt.utils.find.SizeOption('size', '+1k') - self.assertEqual(option.match('', '', [10000] * 7), True) + option = salt.utils.find.SizeOption("size", "+1k") + self.assertEqual(option.match("", "", [10000] * 7), True) - option = salt.utils.find.SizeOption('size', '+1G') - self.assertEqual(option.match('', '', [10000] * 7), False) + option = salt.utils.find.SizeOption("size", "+1G") + self.assertEqual(option.match("", "", [10000] * 7), False) def test_mtime_option_requires(self): - self.assertRaises( - ValueError, salt.utils.find.MtimeOption, 'mtime', '4g' - ) + self.assertRaises(ValueError, salt.utils.find.MtimeOption, "mtime", "4g") - option = salt.utils.find.MtimeOption('mtime', '1d') + option = salt.utils.find.MtimeOption("mtime", "1d") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT) def test_mtime_option_match(self): - option = salt.utils.find.MtimeOption('mtime', '-1w') - self.assertEqual(option.match('', '', [1] * 9), False) + option = salt.utils.find.MtimeOption("mtime", "-1w") + self.assertEqual(option.match("", "", [1] * 9), False) - option = salt.utils.find.MtimeOption('mtime', '-1s') - self.assertEqual(option.match('', '', [10 ** 10] * 9), True) + option = salt.utils.find.MtimeOption("mtime", "-1s") + self.assertEqual(option.match("", "", [10 ** 10] * 9), True) class TestGrepOption(TestCase): - def setUp(self): super(TestGrepOption, self).setUp() self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -293,42 +280,35 @@ class TestGrepOption(TestCase): super(TestGrepOption, self).tearDown() def test_grep_option_requires(self): - self.assertRaises( - ValueError, salt.utils.find.GrepOption, 'grep', '(foo)|(bar}' - ) + self.assertRaises(ValueError, salt.utils.find.GrepOption, "grep", "(foo)|(bar}") - option = salt.utils.find.GrepOption('grep', '(foo)|(bar)') + option = salt.utils.find.GrepOption("grep", "(foo)|(bar)") find = salt.utils.find self.assertEqual( option.requires(), (find._REQUIRES_CONTENTS | find._REQUIRES_STAT) ) def test_grep_option_match_regular_file(self): - hello_file = os.path.join(self.tmpdir, 'hello.txt') - with salt.utils.files.fopen(hello_file, 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str('foo')) - option = salt.utils.find.GrepOption('grep', 'foo') + hello_file = os.path.join(self.tmpdir, "hello.txt") + with salt.utils.files.fopen(hello_file, "w") as fp_: + fp_.write(salt.utils.stringutils.to_str("foo")) + option = salt.utils.find.GrepOption("grep", "foo") self.assertEqual( - option.match(self.tmpdir, 'hello.txt', os.stat(hello_file)), - hello_file + option.match(self.tmpdir, "hello.txt", os.stat(hello_file)), hello_file ) - option = salt.utils.find.GrepOption('grep', 'bar') + option = salt.utils.find.GrepOption("grep", "bar") self.assertEqual( - option.match(self.tmpdir, 'hello.txt', os.stat(hello_file)), - None + option.match(self.tmpdir, "hello.txt", os.stat(hello_file)), None ) - @skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows') + @skipIf(sys.platform.startswith("win"), "No /dev/null on Windows") def test_grep_option_match_dev_null(self): - option = salt.utils.find.GrepOption('grep', 'foo') - self.assertEqual( - option.match('dev', 'null', os.stat('/dev/null')), None - ) + option = salt.utils.find.GrepOption("grep", "foo") + self.assertEqual(option.match("dev", "null", os.stat("/dev/null")), None) class TestPrintOption(TestCase): - def setUp(self): super(TestPrintOption, self).setUp() self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -338,101 +318,98 @@ class TestPrintOption(TestCase): super(TestPrintOption, self).tearDown() def test_print_option_defaults(self): - option = salt.utils.find.PrintOption('print', '') + option = salt.utils.find.PrintOption("print", "") self.assertEqual(option.need_stat, False) self.assertEqual(option.print_title, False) - self.assertEqual(option.fmt, ['path']) + self.assertEqual(option.fmt, ["path"]) def test_print_option_requires(self): - option = salt.utils.find.PrintOption('print', '') + option = salt.utils.find.PrintOption("print", "") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_PATH) - option = salt.utils.find.PrintOption('print', 'name') + option = salt.utils.find.PrintOption("print", "name") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_PATH) - option = salt.utils.find.PrintOption('print', 'path') + option = salt.utils.find.PrintOption("print", "path") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_PATH) - option = salt.utils.find.PrintOption('print', 'name,path') + option = salt.utils.find.PrintOption("print", "name,path") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_PATH) - option = salt.utils.find.PrintOption('print', 'user') + option = salt.utils.find.PrintOption("print", "user") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT) - option = salt.utils.find.PrintOption('print', 'path user') + option = salt.utils.find.PrintOption("print", "path user") self.assertEqual(option.requires(), salt.utils.find._REQUIRES_STAT) def test_print_option_execute(self): - hello_file = os.path.join(self.tmpdir, 'hello.txt') - with salt.utils.files.fopen(hello_file, 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str('foo')) + hello_file = os.path.join(self.tmpdir, "hello.txt") + with salt.utils.files.fopen(hello_file, "w") as fp_: + fp_.write(salt.utils.stringutils.to_str("foo")) - option = salt.utils.find.PrintOption('print', '') - self.assertEqual(option.execute('', [0] * 9), '') + option = salt.utils.find.PrintOption("print", "") + self.assertEqual(option.execute("", [0] * 9), "") - option = salt.utils.find.PrintOption('print', 'path') - self.assertEqual(option.execute('test_name', [0] * 9), 'test_name') + option = salt.utils.find.PrintOption("print", "path") + self.assertEqual(option.execute("test_name", [0] * 9), "test_name") - option = salt.utils.find.PrintOption('print', 'name') - self.assertEqual(option.execute('test_name', [0] * 9), 'test_name') + option = salt.utils.find.PrintOption("print", "name") + self.assertEqual(option.execute("test_name", [0] * 9), "test_name") - option = salt.utils.find.PrintOption('print', 'size') + option = salt.utils.find.PrintOption("print", "size") self.assertEqual(option.execute(hello_file, os.stat(hello_file)), 3) - option = salt.utils.find.PrintOption('print', 'type') - self.assertEqual(option.execute(hello_file, os.stat(hello_file)), 'f') + option = salt.utils.find.PrintOption("print", "type") + self.assertEqual(option.execute(hello_file, os.stat(hello_file)), "f") - option = salt.utils.find.PrintOption('print', 'mode') + option = salt.utils.find.PrintOption("print", "mode") self.assertEqual(option.execute(hello_file, range(10)), 0) - option = salt.utils.find.PrintOption('print', 'mtime') + option = salt.utils.find.PrintOption("print", "mtime") self.assertEqual(option.execute(hello_file, range(10)), 8) - option = salt.utils.find.PrintOption('print', 'md5') + option = salt.utils.find.PrintOption("print", "md5") self.assertEqual( option.execute(hello_file, os.stat(hello_file)), - 'acbd18db4cc2f85cedef654fccc4a4d8' + "acbd18db4cc2f85cedef654fccc4a4d8", ) - option = salt.utils.find.PrintOption('print', 'path name') + option = salt.utils.find.PrintOption("print", "path name") self.assertEqual( - option.execute('test_name', [0] * 9), ['test_name', 'test_name'] + option.execute("test_name", [0] * 9), ["test_name", "test_name"] ) - option = salt.utils.find.PrintOption('print', 'size name') - self.assertEqual( - option.execute('test_name', [0] * 9), [0, 'test_name'] - ) + option = salt.utils.find.PrintOption("print", "size name") + self.assertEqual(option.execute("test_name", [0] * 9), [0, "test_name"]) - @skipIf(sys.platform.startswith('win'), "pwd not available on Windows") + @skipIf(sys.platform.startswith("win"), "pwd not available on Windows") def test_print_user(self): - option = salt.utils.find.PrintOption('print', 'user') - self.assertEqual(option.execute('', [0] * 10), 'root') + option = salt.utils.find.PrintOption("print", "user") + self.assertEqual(option.execute("", [0] * 10), "root") - option = salt.utils.find.PrintOption('print', 'user') - self.assertEqual(option.execute('', [2 ** 31] * 10), 2 ** 31) + option = salt.utils.find.PrintOption("print", "user") + self.assertEqual(option.execute("", [2 ** 31] * 10), 2 ** 31) - @skipIf(sys.platform.startswith('win'), "grp not available on Windows") + @skipIf(sys.platform.startswith("win"), "grp not available on Windows") def test_print_group(self): - option = salt.utils.find.PrintOption('print', 'group') - if sys.platform.startswith(('darwin', 'freebsd', 'openbsd')): - group_name = 'wheel' + option = salt.utils.find.PrintOption("print", "group") + if sys.platform.startswith(("darwin", "freebsd", "openbsd")): + group_name = "wheel" else: - group_name = 'root' - self.assertEqual(option.execute('', [0] * 10), group_name) + group_name = "root" + self.assertEqual(option.execute("", [0] * 10), group_name) # This seems to be not working in Ubuntu 12.04 32 bit - #option = salt.utils.find.PrintOption('print', 'group') - #self.assertEqual(option.execute('', [2 ** 31] * 10), 2 ** 31) + # option = salt.utils.find.PrintOption('print', 'group') + # self.assertEqual(option.execute('', [2 ** 31] * 10), 2 ** 31) - @skipIf(sys.platform.startswith('win'), "no /dev/null on windows") + @skipIf(sys.platform.startswith("win"), "no /dev/null on windows") def test_print_md5(self): - option = salt.utils.find.PrintOption('print', 'md5') - self.assertEqual(option.execute('/dev/null', os.stat('/dev/null')), '') + option = salt.utils.find.PrintOption("print", "md5") + self.assertEqual(option.execute("/dev/null", os.stat("/dev/null")), "") class TestFinder(TestCase): - def setUp(self): super(TestFinder, self).setUp() self.tmpdir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) @@ -441,145 +418,139 @@ class TestFinder(TestCase): shutil.rmtree(self.tmpdir) super(TestFinder, self).tearDown() - @skipIf(sys.platform.startswith('win'), 'No /dev/null on Windows') + @skipIf(sys.platform.startswith("win"), "No /dev/null on Windows") def test_init(self): finder = salt.utils.find.Finder({}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual(finder.criteria, []) - finder = salt.utils.find.Finder({'_': None}) + finder = salt.utils.find.Finder({"_": None}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual(finder.criteria, []) - self.assertRaises(ValueError, salt.utils.find.Finder, {'': None}) - self.assertRaises(ValueError, salt.utils.find.Finder, {'name': None}) - self.assertRaises( - ValueError, salt.utils.find.Finder, {'nonexist': 'somevalue'} + self.assertRaises(ValueError, salt.utils.find.Finder, {"": None}) + self.assertRaises(ValueError, salt.utils.find.Finder, {"name": None}) + self.assertRaises(ValueError, salt.utils.find.Finder, {"nonexist": "somevalue"}) + + finder = salt.utils.find.Finder({"name": "test_name"}) + self.assertEqual( + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" + ) + self.assertEqual( + six.text_type(finder.criteria[0].__class__)[-12:-2], "NameOption" ) - finder = salt.utils.find.Finder({'name': 'test_name'}) + finder = salt.utils.find.Finder({"iname": "test_name"}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-12:-2], 'NameOption' + six.text_type(finder.criteria[0].__class__)[-13:-2], "InameOption" ) - finder = salt.utils.find.Finder({'iname': 'test_name'}) + finder = salt.utils.find.Finder({"regex": r".*\.txt"}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-13:-2], 'InameOption' + six.text_type(finder.criteria[0].__class__)[-13:-2], "RegexOption" ) - finder = salt.utils.find.Finder({'regex': r'.*\.txt'}) + finder = salt.utils.find.Finder({"iregex": r".*\.txt"}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-13:-2], 'RegexOption' + six.text_type(finder.criteria[0].__class__)[-14:-2], "IregexOption" ) - finder = salt.utils.find.Finder({'iregex': r'.*\.txt'}) + finder = salt.utils.find.Finder({"type": "d"}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-14:-2], 'IregexOption' + six.text_type(finder.criteria[0].__class__)[-12:-2], "TypeOption" ) - finder = salt.utils.find.Finder({'type': 'd'}) + finder = salt.utils.find.Finder({"owner": "root"}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-12:-2], 'TypeOption' + six.text_type(finder.criteria[0].__class__)[-13:-2], "OwnerOption" ) - finder = salt.utils.find.Finder({'owner': 'root'}) - self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' - ) - self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-13:-2], 'OwnerOption' - ) - - if sys.platform.startswith(('darwin', 'freebsd', 'openbsd')): - group_name = 'wheel' + if sys.platform.startswith(("darwin", "freebsd", "openbsd")): + group_name = "wheel" else: - group_name = 'root' - finder = salt.utils.find.Finder({'group': group_name}) + group_name = "root" + finder = salt.utils.find.Finder({"group": group_name}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-13:-2], 'GroupOption' + six.text_type(finder.criteria[0].__class__)[-13:-2], "GroupOption" ) - finder = salt.utils.find.Finder({'size': '+1G'}) + finder = salt.utils.find.Finder({"size": "+1G"}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-12:-2], 'SizeOption' + six.text_type(finder.criteria[0].__class__)[-12:-2], "SizeOption" ) - finder = salt.utils.find.Finder({'mtime': '1d'}) + finder = salt.utils.find.Finder({"mtime": "1d"}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-13:-2], 'MtimeOption' + six.text_type(finder.criteria[0].__class__)[-13:-2], "MtimeOption" ) - finder = salt.utils.find.Finder({'grep': 'foo'}) + finder = salt.utils.find.Finder({"grep": "foo"}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual( - six.text_type(finder.criteria[0].__class__)[-12:-2], 'GrepOption' + six.text_type(finder.criteria[0].__class__)[-12:-2], "GrepOption" ) - finder = salt.utils.find.Finder({'print': 'name'}) + finder = salt.utils.find.Finder({"print": "name"}) self.assertEqual( - six.text_type(finder.actions[0].__class__)[-13:-2], 'PrintOption' + six.text_type(finder.actions[0].__class__)[-13:-2], "PrintOption" ) self.assertEqual(finder.criteria, []) def test_find(self): - hello_file = os.path.join(self.tmpdir, 'hello.txt') - with salt.utils.files.fopen(hello_file, 'w') as fp_: - fp_.write(salt.utils.stringutils.to_str('foo')) + hello_file = os.path.join(self.tmpdir, "hello.txt") + with salt.utils.files.fopen(hello_file, "w") as fp_: + fp_.write(salt.utils.stringutils.to_str("foo")) finder = salt.utils.find.Finder({}) self.assertEqual(list(finder.find(self.tmpdir)), [self.tmpdir, hello_file]) - finder = salt.utils.find.Finder({'mindepth': 1}) + finder = salt.utils.find.Finder({"mindepth": 1}) self.assertEqual(list(finder.find(self.tmpdir)), [hello_file]) - finder = salt.utils.find.Finder({'maxdepth': 0}) + finder = salt.utils.find.Finder({"maxdepth": 0}) self.assertEqual(list(finder.find(self.tmpdir)), [self.tmpdir]) - finder = salt.utils.find.Finder({'name': 'hello.txt'}) + finder = salt.utils.find.Finder({"name": "hello.txt"}) self.assertEqual(list(finder.find(self.tmpdir)), [hello_file]) - finder = salt.utils.find.Finder({'type': 'f', 'print': 'path'}) + finder = salt.utils.find.Finder({"type": "f", "print": "path"}) self.assertEqual(list(finder.find(self.tmpdir)), [hello_file]) - finder = salt.utils.find.Finder({'size': '+1G', 'print': 'path'}) + finder = salt.utils.find.Finder({"size": "+1G", "print": "path"}) self.assertEqual(list(finder.find(self.tmpdir)), []) - finder = salt.utils.find.Finder( - {'name': 'hello.txt', 'print': 'path name'} - ) - self.assertEqual( - list(finder.find(self.tmpdir)), [[hello_file, 'hello.txt']] - ) + finder = salt.utils.find.Finder({"name": "hello.txt", "print": "path name"}) + self.assertEqual(list(finder.find(self.tmpdir)), [[hello_file, "hello.txt"]]) - finder = salt.utils.find.Finder({'name': 'test_name'}) - self.assertEqual(list(finder.find('')), []) + finder = salt.utils.find.Finder({"name": "test_name"}) + self.assertEqual(list(finder.find("")), []) diff --git a/tests/unit/utils/test_gitfs.py b/tests/unit/utils/test_gitfs.py index 7c1686f4f45..e1e01a873ce 100644 --- a/tests/unit/utils/test_gitfs.py +++ b/tests/unit/utils/test_gitfs.py @@ -1,45 +1,68 @@ # -*- coding: utf-8 -*- -''' +""" These only test the provider selection and verification logic, they do not init any remotes. -''' +""" # Import python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch +import os +import shutil +from time import time # Import salt libs +import salt.fileserver.gitfs +import salt.utils.files import salt.utils.gitfs +import salt.utils.platform + +# Import Salt Testing libs +import tests.support.paths from salt.exceptions import FileserverConfigError +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + +try: + HAS_PYGIT2 = ( + salt.utils.gitfs.PYGIT2_VERSION >= salt.utils.gitfs.PYGIT2_MINVER + and salt.utils.gitfs.LIBGIT2_VERSION >= salt.utils.gitfs.LIBGIT2_MINVER + ) +except AttributeError: + HAS_PYGIT2 = False + + +if HAS_PYGIT2: + import pygit2 + # GLOBALS -OPTS = {'cachedir': '/tmp/gitfs-test-cache'} +OPTS = {"cachedir": "/tmp/gitfs-test-cache"} class TestGitFSProvider(TestCase): - def test_provider_case_insensitive(self): - ''' + """ Ensure that both lowercase and non-lowercase values are supported - ''' - provider = 'GitPython' + """ + provider = "GitPython" for role_name, role_class in ( - ('gitfs', salt.utils.gitfs.GitFS), - ('git_pillar', salt.utils.gitfs.GitPillar), - ('winrepo', salt.utils.gitfs.WinRepo)): + ("gitfs", salt.utils.gitfs.GitFS), + ("git_pillar", salt.utils.gitfs.GitPillar), + ("winrepo", salt.utils.gitfs.WinRepo), + ): - key = '{0}_provider'.format(role_name) - with patch.object(role_class, 'verify_gitpython', - MagicMock(return_value=True)): - with patch.object(role_class, 'verify_pygit2', - MagicMock(return_value=False)): + key = "{0}_provider".format(role_name) + with patch.object( + role_class, "verify_gitpython", MagicMock(return_value=True) + ): + with patch.object( + role_class, "verify_pygit2", MagicMock(return_value=False) + ): args = [OPTS, {}] - kwargs = {'init_remotes': False} - if role_name == 'winrepo': - kwargs['cache_root'] = '/tmp/winrepo-dir' + kwargs = {"init_remotes": False} + if role_name == "winrepo": + kwargs["cache_root"] = "/tmp/winrepo-dir" with patch.dict(OPTS, {key: provider}): # Try to create an instance with uppercase letters in # provider name. If it fails then a @@ -51,41 +74,173 @@ class TestGitFSProvider(TestCase): role_class(*args, **kwargs) def test_valid_provider(self): - ''' + """ Ensure that an invalid provider is not accepted, raising a FileserverConfigError. - ''' + """ + def _get_mock(verify, provider): - ''' + """ Return a MagicMock with the desired return value - ''' + """ return MagicMock(return_value=verify.endswith(provider)) for role_name, role_class in ( - ('gitfs', salt.utils.gitfs.GitFS), - ('git_pillar', salt.utils.gitfs.GitPillar), - ('winrepo', salt.utils.gitfs.WinRepo)): - key = '{0}_provider'.format(role_name) + ("gitfs", salt.utils.gitfs.GitFS), + ("git_pillar", salt.utils.gitfs.GitPillar), + ("winrepo", salt.utils.gitfs.WinRepo), + ): + key = "{0}_provider".format(role_name) for provider in salt.utils.gitfs.GIT_PROVIDERS: - verify = 'verify_gitpython' + verify = "verify_gitpython" mock1 = _get_mock(verify, provider) with patch.object(role_class, verify, mock1): - verify = 'verify_pygit2' + verify = "verify_pygit2" mock2 = _get_mock(verify, provider) with patch.object(role_class, verify, mock2): args = [OPTS, {}] - kwargs = {'init_remotes': False} - if role_name == 'winrepo': - kwargs['cache_root'] = '/tmp/winrepo-dir' + kwargs = {"init_remotes": False} + if role_name == "winrepo": + kwargs["cache_root"] = "/tmp/winrepo-dir" with patch.dict(OPTS, {key: provider}): role_class(*args, **kwargs) - with patch.dict(OPTS, {key: 'foo'}): + with patch.dict(OPTS, {key: "foo"}): # Set the provider name to a known invalid provider # and make sure it raises an exception. self.assertRaises( - FileserverConfigError, - role_class, - *args, - **kwargs) + FileserverConfigError, role_class, *args, **kwargs + ) + + +@skipIf(not HAS_PYGIT2, "This host lacks proper pygit2 support") +@skipIf( + salt.utils.platform.is_windows(), + "Skip Pygit2 on windows, due to pygit2 access error on windows", +) +class TestPygit2(TestCase): + def _prepare_remote_repository(self, path): + shutil.rmtree(path, ignore_errors=True) + + filecontent = "This is an empty README file" + filename = "README" + + signature = pygit2.Signature( + "Dummy Commiter", "dummy@dummy.com", int(time()), 0 + ) + + repository = pygit2.init_repository(path, False) + builder = repository.TreeBuilder() + tree = builder.write() + commit = repository.create_commit( + "HEAD", signature, signature, "Create master branch", tree, [] + ) + repository.create_reference("refs/tags/simple_tag", commit) + + with salt.utils.files.fopen( + os.path.join(repository.workdir, filename), "w" + ) as file: + file.write(filecontent) + + blob = repository.create_blob_fromworkdir(filename) + builder = repository.TreeBuilder() + builder.insert(filename, blob, pygit2.GIT_FILEMODE_BLOB) + tree = builder.write() + + repository.index.read() + repository.index.add(filename) + repository.index.write() + + commit = repository.create_commit( + "HEAD", + signature, + signature, + "Added a README", + tree, + [repository.head.target], + ) + repository.create_tag( + "annotated_tag", commit, pygit2.GIT_OBJ_COMMIT, signature, "some message" + ) + + def _prepare_cache_repository(self, remote, cache): + opts = { + "cachedir": cache, + "__role": "minion", + "gitfs_disable_saltenv_mapping": False, + "gitfs_base": "master", + "gitfs_insecure_auth": False, + "gitfs_mountpoint": "", + "gitfs_passphrase": "", + "gitfs_password": "", + "gitfs_privkey": "", + "gitfs_provider": "pygit2", + "gitfs_pubkey": "", + "gitfs_ref_types": ["branch", "tag", "sha"], + "gitfs_refspecs": [ + "+refs/heads/*:refs/remotes/origin/*", + "+refs/tags/*:refs/tags/*", + ], + "gitfs_root": "", + "gitfs_saltenv_blacklist": [], + "gitfs_saltenv_whitelist": [], + "gitfs_ssl_verify": True, + "gitfs_update_interval": 3, + "gitfs_user": "", + "verified_gitfs_provider": "pygit2", + } + per_remote_defaults = { + "base": "master", + "disable_saltenv_mapping": False, + "insecure_auth": False, + "ref_types": ["branch", "tag", "sha"], + "passphrase": "", + "mountpoint": "", + "password": "", + "privkey": "", + "pubkey": "", + "refspecs": [ + "+refs/heads/*:refs/remotes/origin/*", + "+refs/tags/*:refs/tags/*", + ], + "root": "", + "saltenv_blacklist": [], + "saltenv_whitelist": [], + "ssl_verify": True, + "update_interval": 60, + "user": "", + } + per_remote_only = ("all_saltenvs", "name", "saltenv") + override_params = tuple(per_remote_defaults.keys()) + cache_root = os.path.join(cache, "gitfs") + role = "gitfs" + shutil.rmtree(cache_root, ignore_errors=True) + provider = salt.utils.gitfs.Pygit2( + opts, + remote, + per_remote_defaults, + per_remote_only, + override_params, + cache_root, + role, + ) + return provider + + def test_checkout(self): + remote = os.path.join(tests.support.paths.TMP, "pygit2-repo") + cache = os.path.join(tests.support.paths.TMP, "pygit2-repo-cache") + self._prepare_remote_repository(remote) + provider = self._prepare_cache_repository(remote, cache) + provider.remotecallbacks = None + provider.credentials = None + provider.init_remote() + provider.fetch() + provider.branch = "master" + self.assertIn(provider.cachedir, provider.checkout()) + provider.branch = "simple_tag" + self.assertIn(provider.cachedir, provider.checkout()) + provider.branch = "annotated_tag" + self.assertIn(provider.cachedir, provider.checkout()) + provider.branch = "does_not_exist" + self.assertIsNone(provider.checkout()) diff --git a/tests/unit/utils/test_hashutils.py b/tests/unit/utils/test_hashutils.py index 8cfb58009a8..3bcc12c4d5e 100644 --- a/tests/unit/utils/test_hashutils.py +++ b/tests/unit/utils/test_hashutils.py @@ -1,182 +1,164 @@ # -*- coding: utf-8 -*- # Import python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.unit import TestCase +from __future__ import absolute_import, print_function, unicode_literals # Import Salt libs import salt.utils.hashutils +# Import Salt Testing libs +from tests.support.unit import TestCase + class HashutilsTestCase(TestCase): - hmac_secret = 's00p3r s3kr1t' + hmac_secret = "s00p3r s3kr1t" # Use a non-ascii unicode type to confirm that no UnicodeEncodeError is # raised on Python 2. - str = 'спам' - str_b64encode_result = '0YHQv9Cw0Lw=' - str_encodestring_result = '0YHQv9Cw0Lw=\n' - str_md5 = 'a035ac08ab2f03556f9b3ee640052e5c' - str_sha256 = '095291ffa3d361436d4617879e22c1da06c6ab61a3fb081321ec854a27a091ac' - str_sha512 = '12efd90e507289f1f21e5dcfe2e92cf0bb4904abccb55c3ce9177670c711981501054b32b807c37058675590d1c484bd2b72a4215a2fa397aa4f2b12f298b1f0' - str_hmac_challenge = b'qz2k0t1aevKEme3JGsNQJX/xpmf+/w3q6qmWDk1ZqbY=' - str_hmac_compute = 'ab3da4d2dd5a7af28499edc91ac350257ff1a667feff0deaeaa9960e4d59a9b6' + str = "спам" + str_b64encode_result = "0YHQv9Cw0Lw=" + str_encodestring_result = "0YHQv9Cw0Lw=\n" + str_md5 = "a035ac08ab2f03556f9b3ee640052e5c" + str_sha256 = "095291ffa3d361436d4617879e22c1da06c6ab61a3fb081321ec854a27a091ac" + str_sha512 = "12efd90e507289f1f21e5dcfe2e92cf0bb4904abccb55c3ce9177670c711981501054b32b807c37058675590d1c484bd2b72a4215a2fa397aa4f2b12f298b1f0" + str_hmac_challenge = b"qz2k0t1aevKEme3JGsNQJX/xpmf+/w3q6qmWDk1ZqbY=" + str_hmac_compute = ( + "ab3da4d2dd5a7af28499edc91ac350257ff1a667feff0deaeaa9960e4d59a9b6" + ) # 16 bytes of random data - bytes = b'b\x19\xf6\x86\x0e\x1a\x1cs\x0c\xda&zv\xfc\xa2\xdd' - bytes_b64encode_result = 'Yhn2hg4aHHMM2iZ6dvyi3Q==' - bytes_encodestring_result = 'Yhn2hg4aHHMM2iZ6dvyi3Q==\n' - bytes_md5 = '4d064241724791641dc15930c65f75c8' - bytes_sha256 = '25711a31c2673a48f3d1f29b25add574697872968e546d266f441de63b17954a' - bytes_sha512 = '69f1524e602c1599fc374e1e3e2941e6f6949f4f7fe7321304e4e67bb850f3204dd5cbf9c13e231814540c2f5cd370c24ea257771d9fbf311d8f6085bad12b24' - bytes_hmac_challenge = b'lQibiD9r1Hpo+5JYknaudIKfTx1L5J3U58M9yQOd04c=' - bytes_hmac_compute = '95089b883f6bd47a68fb92589276ae74829f4f1d4be49dd4e7c33dc9039dd387' + bytes = b"b\x19\xf6\x86\x0e\x1a\x1cs\x0c\xda&zv\xfc\xa2\xdd" + bytes_b64encode_result = "Yhn2hg4aHHMM2iZ6dvyi3Q==" + bytes_encodestring_result = "Yhn2hg4aHHMM2iZ6dvyi3Q==\n" + bytes_md5 = "4d064241724791641dc15930c65f75c8" + bytes_sha256 = "25711a31c2673a48f3d1f29b25add574697872968e546d266f441de63b17954a" + bytes_sha512 = "69f1524e602c1599fc374e1e3e2941e6f6949f4f7fe7321304e4e67bb850f3204dd5cbf9c13e231814540c2f5cd370c24ea257771d9fbf311d8f6085bad12b24" + bytes_hmac_challenge = b"lQibiD9r1Hpo+5JYknaudIKfTx1L5J3U58M9yQOd04c=" + bytes_hmac_compute = ( + "95089b883f6bd47a68fb92589276ae74829f4f1d4be49dd4e7c33dc9039dd387" + ) def test_base64_b64encode(self): - ''' + """ Ensure that this function converts the value passed to bytes before attempting to encode, avoiding a UnicodeEncodeError on Python 2 and a TypeError on Python 3. - ''' + """ self.assertEqual( - salt.utils.hashutils.base64_b64encode(self.str), - self.str_b64encode_result + salt.utils.hashutils.base64_b64encode(self.str), self.str_b64encode_result ) self.assertEqual( salt.utils.hashutils.base64_b64encode(self.bytes), - self.bytes_b64encode_result + self.bytes_b64encode_result, ) def test_base64_b64decode(self): - ''' + """ Ensure that this function converts the value passed to a unicode type (if possible) on Python 2, and a str type (if possible) on Python 3. - ''' + """ self.assertEqual( - salt.utils.hashutils.base64_b64decode(self.str_b64encode_result), - self.str + salt.utils.hashutils.base64_b64decode(self.str_b64encode_result), self.str ) self.assertEqual( salt.utils.hashutils.base64_b64decode(self.bytes_b64encode_result), - self.bytes + self.bytes, ) def test_base64_encodestring(self): - ''' + """ Ensure that this function converts the value passed to bytes before attempting to encode, avoiding a UnicodeEncodeError on Python 2 and a TypeError on Python 3. - ''' + """ self.assertEqual( salt.utils.hashutils.base64_encodestring(self.str), - self.str_encodestring_result + self.str_encodestring_result, ) self.assertEqual( salt.utils.hashutils.base64_encodestring(self.bytes), - self.bytes_encodestring_result + self.bytes_encodestring_result, ) def test_base64_decodestring(self): - ''' + """ Ensure that this function converts the value passed to a unicode type (if possible) on Python 2, and a str type (if possible) on Python 3. - ''' + """ self.assertEqual( salt.utils.hashutils.base64_decodestring(self.str_encodestring_result), - self.str + self.str, ) self.assertEqual( salt.utils.hashutils.base64_decodestring(self.bytes_encodestring_result), - self.bytes + self.bytes, ) def test_md5_digest(self): - ''' + """ Ensure that this function converts the value passed to bytes before attempting to encode, avoiding a UnicodeEncodeError on Python 2 and a TypeError on Python 3. - ''' - self.assertEqual( - salt.utils.hashutils.md5_digest(self.str), - self.str_md5 - ) - self.assertEqual( - salt.utils.hashutils.md5_digest(self.bytes), - self.bytes_md5 - ) + """ + self.assertEqual(salt.utils.hashutils.md5_digest(self.str), self.str_md5) + self.assertEqual(salt.utils.hashutils.md5_digest(self.bytes), self.bytes_md5) def test_sha256_digest(self): - ''' + """ Ensure that this function converts the value passed to bytes before attempting to encode, avoiding a UnicodeEncodeError on Python 2 and a TypeError on Python 3. - ''' + """ + self.assertEqual(salt.utils.hashutils.sha256_digest(self.str), self.str_sha256) self.assertEqual( - salt.utils.hashutils.sha256_digest(self.str), - self.str_sha256 - ) - self.assertEqual( - salt.utils.hashutils.sha256_digest(self.bytes), - self.bytes_sha256 + salt.utils.hashutils.sha256_digest(self.bytes), self.bytes_sha256 ) def test_sha512_digest(self): - ''' + """ Ensure that this function converts the value passed to bytes before attempting to encode, avoiding a UnicodeEncodeError on Python 2 and a TypeError on Python 3. - ''' + """ + self.assertEqual(salt.utils.hashutils.sha512_digest(self.str), self.str_sha512) self.assertEqual( - salt.utils.hashutils.sha512_digest(self.str), - self.str_sha512 - ) - self.assertEqual( - salt.utils.hashutils.sha512_digest(self.bytes), - self.bytes_sha512 + salt.utils.hashutils.sha512_digest(self.bytes), self.bytes_sha512 ) def test_hmac_signature(self): - ''' + """ Ensure that this function converts the value passed to bytes before attempting to validate the hmac challenge, avoiding a UnicodeEncodeError on Python 2 and a TypeError on Python 3. - ''' + """ self.assertTrue( salt.utils.hashutils.hmac_signature( - self.str, - self.hmac_secret, - self.str_hmac_challenge + self.str, self.hmac_secret, self.str_hmac_challenge ) ) self.assertTrue( salt.utils.hashutils.hmac_signature( - self.bytes, - self.hmac_secret, - self.bytes_hmac_challenge + self.bytes, self.hmac_secret, self.bytes_hmac_challenge ) ) def test_hmac_compute(self): - ''' + """ Ensure that this function converts the value passed to bytes before attempting to encode, avoiding a UnicodeEncodeError on Python 2 and a TypeError on Python 3. - ''' + """ self.assertEqual( salt.utils.hashutils.hmac_compute(self.str, self.hmac_secret), - self.str_hmac_compute + self.str_hmac_compute, ) self.assertEqual( salt.utils.hashutils.hmac_compute(self.bytes, self.hmac_secret), - self.bytes_hmac_compute + self.bytes_hmac_compute, ) def test_get_hash_exception(self): self.assertRaises( - ValueError, - salt.utils.hashutils.get_hash, - '/tmp/foo/', - form='INVALID') + ValueError, salt.utils.hashutils.get_hash, "/tmp/foo/", form="INVALID" + ) diff --git a/tests/unit/utils/test_http.py b/tests/unit/utils/test_http.py index 861457f2e93..4e762b50827 100644 --- a/tests/unit/utils/test_http.py +++ b/tests/unit/utils/test_http.py @@ -1,25 +1,27 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import socket from contextlib import closing -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.helpers import MirrorPostHandler, Webserver - # Import Salt Libs import salt.utils.http as http +from tests.support.helpers import MirrorPostHandler, Webserver + +# Import Salt Testing Libs +from tests.support.unit import TestCase class HTTPTestCase(TestCase): - ''' + """ Unit TestCase for the salt.utils.http module. - ''' + """ + @classmethod def setUpClass(cls): cls.post_webserver = Webserver(handler=MirrorPostHandler) @@ -34,103 +36,111 @@ class HTTPTestCase(TestCase): # sanitize_url tests def test_sanitize_url_hide_fields_none(self): - ''' + """ Tests sanitizing a url when the hide_fields kwarg is None. - ''' - mock_url = 'https://api.testing.com/?&foo=bar&test=testing' + """ + mock_url = "https://api.testing.com/?&foo=bar&test=testing" ret = http.sanitize_url(mock_url, hide_fields=None) self.assertEqual(ret, mock_url) def test_sanitize_url_no_elements(self): - ''' + """ Tests sanitizing a url when no elements should be sanitized. - ''' - mock_url = 'https://api.testing.com/?&foo=bar&test=testing' - ret = http.sanitize_url(mock_url, ['']) + """ + mock_url = "https://api.testing.com/?&foo=bar&test=testing" + ret = http.sanitize_url(mock_url, [""]) self.assertEqual(ret, mock_url) def test_sanitize_url_single_element(self): - ''' + """ Tests sanitizing a url with only a single element to be sanitized. - ''' - mock_url = 'https://api.testing.com/?&keep_it_secret=abcdefghijklmn' \ - '&api_action=module.function' - mock_ret = 'https://api.testing.com/?&keep_it_secret=XXXXXXXXXX&' \ - 'api_action=module.function' - ret = http.sanitize_url(mock_url, ['keep_it_secret']) + """ + mock_url = ( + "https://api.testing.com/?&keep_it_secret=abcdefghijklmn" + "&api_action=module.function" + ) + mock_ret = ( + "https://api.testing.com/?&keep_it_secret=XXXXXXXXXX&" + "api_action=module.function" + ) + ret = http.sanitize_url(mock_url, ["keep_it_secret"]) self.assertEqual(ret, mock_ret) def test_sanitize_url_multiple_elements(self): - ''' + """ Tests sanitizing a url with multiple elements to be sanitized. - ''' - mock_url = 'https://api.testing.com/?rootPass=badpassword%21' \ - '&skipChecks=True&api_key=abcdefghijklmn' \ - '&NodeID=12345&api_action=module.function' - mock_ret = 'https://api.testing.com/?rootPass=XXXXXXXXXX' \ - '&skipChecks=True&api_key=XXXXXXXXXX' \ - '&NodeID=12345&api_action=module.function' - ret = http.sanitize_url(mock_url, ['api_key', 'rootPass']) + """ + mock_url = ( + "https://api.testing.com/?rootPass=badpassword%21" + "&skipChecks=True&api_key=abcdefghijklmn" + "&NodeID=12345&api_action=module.function" + ) + mock_ret = ( + "https://api.testing.com/?rootPass=XXXXXXXXXX" + "&skipChecks=True&api_key=XXXXXXXXXX" + "&NodeID=12345&api_action=module.function" + ) + ret = http.sanitize_url(mock_url, ["api_key", "rootPass"]) self.assertEqual(ret, mock_ret) # _sanitize_components tests def test_sanitize_components_no_elements(self): - ''' + """ Tests when zero elements need to be sanitized. - ''' - mock_component_list = ['foo=bar', 'bar=baz', 'hello=world'] - mock_ret = 'foo=bar&bar=baz&hello=world&' - ret = http._sanitize_url_components(mock_component_list, 'api_key') + """ + mock_component_list = ["foo=bar", "bar=baz", "hello=world"] + mock_ret = "foo=bar&bar=baz&hello=world&" + ret = http._sanitize_url_components(mock_component_list, "api_key") self.assertEqual(ret, mock_ret) def test_sanitize_components_one_element(self): - ''' + """ Tests a single component to be sanitized. - ''' - mock_component_list = ['foo=bar', 'api_key=abcdefghijklmnop'] - mock_ret = 'foo=bar&api_key=XXXXXXXXXX&' - ret = http._sanitize_url_components(mock_component_list, 'api_key') + """ + mock_component_list = ["foo=bar", "api_key=abcdefghijklmnop"] + mock_ret = "foo=bar&api_key=XXXXXXXXXX&" + ret = http._sanitize_url_components(mock_component_list, "api_key") self.assertEqual(ret, mock_ret) def test_sanitize_components_multiple_elements(self): - ''' + """ Tests two componenets to be sanitized. - ''' - mock_component_list = ['foo=bar', 'foo=baz', 'api_key=testing'] - mock_ret = 'foo=XXXXXXXXXX&foo=XXXXXXXXXX&api_key=testing&' - ret = http._sanitize_url_components(mock_component_list, 'foo') + """ + mock_component_list = ["foo=bar", "foo=baz", "api_key=testing"] + mock_ret = "foo=XXXXXXXXXX&foo=XXXXXXXXXX&api_key=testing&" + ret = http._sanitize_url_components(mock_component_list, "foo") self.assertEqual(ret, mock_ret) def test_query_null_response(self): - ''' + """ This tests that we get a null response when raise_error=False and the host/port cannot be reached. - ''' - host = '127.0.0.1' + """ + host = "127.0.0.1" # Find unused port with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: sock.bind((host, 0)) port = sock.getsockname()[1] - url = 'http://{host}:{port}/'.format(host=host, port=port) + url = "http://{host}:{port}/".format(host=host, port=port) result = http.query(url, raise_error=False) - assert result == {'body': None}, result + assert result == {"body": None}, result def test_requests_multipart_formdata_post(self): - ''' + """ Test handling of a multipart/form-data POST using the requests backend - ''' + """ match_this = '{0}\r\nContent-Disposition: form-data; name="fieldname_here"\r\n\r\nmydatahere\r\n{0}--\r\n' ret = http.query( self.post_web_root, - method='POST', - data='mydatahere', + method="POST", + data="mydatahere", formdata=True, - formdata_fieldname='fieldname_here', - backend='requests' + formdata_fieldname="fieldname_here", + backend="requests", ) - body = ret.get('body', '') - boundary = body[:body.find('\r')] + body = ret.get("body", "") + boundary = body[: body.find("\r")] self.assertEqual(body, match_this.format(boundary)) diff --git a/tests/unit/utils/test_iam.py b/tests/unit/utils/test_iam.py index 3e1eda5d541..35de9278554 100644 --- a/tests/unit/utils/test_iam.py +++ b/tests/unit/utils/test_iam.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.utils.iam -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -11,12 +11,11 @@ from tests.support.unit import TestCase, skipIf class IamTestCase(TestCase): - - @skipIf(six.PY3, 'Only needs to run on Python 2') + @skipIf(six.PY3, "Only needs to run on Python 2") def test__convert_key_to_str(self): - ''' + """ Makes sure that unicode string is converted to a str type on PY2 - ''' - key = 'foo' - expected = key.encode('utf-8') + """ + key = "foo" + expected = key.encode("utf-8") self.assertEqual(iam._convert_key_to_str(key), expected) diff --git a/tests/unit/utils/test_immutabletypes.py b/tests/unit/utils/test_immutabletypes.py index e6f3f0938c2..3cc960b4310 100644 --- a/tests/unit/utils/test_immutabletypes.py +++ b/tests/unit/utils/test_immutabletypes.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,20 +7,19 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Test salt.utils.immutabletypes -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase - # Import salt libs import salt.utils.immutabletypes as immutabletypes +# Import Salt Testing libs +from tests.support.unit import TestCase + class ImmutableTypesTestCase(TestCase): - def test_immutablelist_sum(self): lst = [4, 5, 6] imt = immutabletypes.ImmutableList([1, 2, 3]) @@ -52,13 +51,9 @@ class ImmutableTypesTestCase(TestCase): 3: { 3.1: 3.1, 3.2: 3.2, - 3.3: { - 3.31: 3.33, - 3.32: 3.34, - 3.33: [3.331, 3.332, 3.333] - } + 3.3: {3.31: 3.33, 3.32: 3.34, 3.33: [3.331, 3.332, 3.333]}, }, - 4: [4.1, 4.2, 4.3] + 4: [4.1, 4.2, 4.3], } frozen = immutabletypes.freeze(data) with self.assertRaises(TypeError): diff --git a/tests/unit/utils/test_jid.py b/tests/unit/utils/test_jid.py index 3bb5d2baa6d..467386d0cd7 100644 --- a/tests/unit/utils/test_jid.py +++ b/tests/unit/utils/test_jid.py @@ -1,56 +1,59 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.utils.jid -''' +""" # Import Python libs from __future__ import absolute_import, unicode_literals + import datetime import os # Import Salt libs import salt.utils.jid -from tests.support.unit import TestCase from tests.support.mock import patch +from tests.support.unit import TestCase class JidTestCase(TestCase): def test_jid_to_time(self): test_jid = 20131219110700123489 - expected_jid = '2013, Dec 19 11:07:00.123489' + expected_jid = "2013, Dec 19 11:07:00.123489" self.assertEqual(salt.utils.jid.jid_to_time(test_jid), expected_jid) # Test incorrect lengths incorrect_jid_length = 2012 - self.assertEqual(salt.utils.jid.jid_to_time(incorrect_jid_length), '') + self.assertEqual(salt.utils.jid.jid_to_time(incorrect_jid_length), "") def test_is_jid(self): - self.assertTrue(salt.utils.jid.is_jid('20131219110700123489')) # Valid JID + self.assertTrue(salt.utils.jid.is_jid("20131219110700123489")) # Valid JID self.assertFalse(salt.utils.jid.is_jid(20131219110700123489)) # int - self.assertFalse(salt.utils.jid.is_jid('2013121911070012348911111')) # Wrong length + self.assertFalse( + salt.utils.jid.is_jid("2013121911070012348911111") + ) # Wrong length def test_gen_jid(self): now = datetime.datetime(2002, 12, 25, 12, 0, 0, 0) - with patch('salt.utils.jid._utc_now', return_value=now): + with patch("salt.utils.jid._utc_now", return_value=now): ret = salt.utils.jid.gen_jid({}) - self.assertEqual(ret, '20021225120000000000') + self.assertEqual(ret, "20021225120000000000") salt.utils.jid.LAST_JID_DATETIME = None - ret = salt.utils.jid.gen_jid({'unique_jid': True}) - self.assertEqual(ret, '20021225120000000000_{0}'.format(os.getpid())) - ret = salt.utils.jid.gen_jid({'unique_jid': True}) - self.assertEqual(ret, '20021225120000000001_{0}'.format(os.getpid())) + ret = salt.utils.jid.gen_jid({"unique_jid": True}) + self.assertEqual(ret, "20021225120000000000_{0}".format(os.getpid())) + ret = salt.utils.jid.gen_jid({"unique_jid": True}) + self.assertEqual(ret, "20021225120000000001_{0}".format(os.getpid())) def test_gen_jid_utc(self): utcnow = datetime.datetime(2002, 12, 25, 12, 7, 0, 0) - with patch('salt.utils.jid._utc_now', return_value=utcnow): - ret = salt.utils.jid.gen_jid({'utc_jid': True}) - self.assertEqual(ret, '20021225120700000000') + with patch("salt.utils.jid._utc_now", return_value=utcnow): + ret = salt.utils.jid.gen_jid({"utc_jid": True}) + self.assertEqual(ret, "20021225120700000000") def test_gen_jid_utc_unique(self): utcnow = datetime.datetime(2002, 12, 25, 12, 7, 0, 0) - with patch('salt.utils.jid._utc_now', return_value=utcnow): + with patch("salt.utils.jid._utc_now", return_value=utcnow): salt.utils.jid.LAST_JID_DATETIME = None - ret = salt.utils.jid.gen_jid({'utc_jid': True, 'unique_jid': True}) - self.assertEqual(ret, '20021225120700000000_{0}'.format(os.getpid())) - ret = salt.utils.jid.gen_jid({'utc_jid': True, 'unique_jid': True}) - self.assertEqual(ret, '20021225120700000001_{0}'.format(os.getpid())) + ret = salt.utils.jid.gen_jid({"utc_jid": True, "unique_jid": True}) + self.assertEqual(ret, "20021225120700000000_{0}".format(os.getpid())) + ret = salt.utils.jid.gen_jid({"utc_jid": True, "unique_jid": True}) + self.assertEqual(ret, "20021225120700000001_{0}".format(os.getpid())) diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py index f48fa9d42c2..8a6b57b1846 100644 --- a/tests/unit/utils/test_jinja.py +++ b/tests/unit/utils/test_jinja.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.utils.jinja -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function -from jinja2 import Environment, DictLoader, exceptions +from __future__ import absolute_import, print_function, unicode_literals + import ast import copy import datetime @@ -13,41 +13,41 @@ import pprint import re import tempfile -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import skipIf, TestCase -from tests.support.case import ModuleCase -from tests.support.helpers import flaky -from tests.support.mock import patch, MagicMock, Mock - # Import Salt libs import salt.config import salt.loader -from salt.exceptions import SaltRenderError +# dateutils is needed so that the strftime jinja filter is loaded +import salt.utils.dateutils # pylint: disable=unused-import +import salt.utils.files +import salt.utils.json +import salt.utils.stringutils +import salt.utils.yaml +from jinja2 import DictLoader, Environment, exceptions +from salt.exceptions import SaltRenderError from salt.ext import six from salt.ext.six.moves import builtins - -import salt.utils.json from salt.utils.decorators.jinja import JinjaFilter from salt.utils.jinja import ( SaltCacheLoader, SerializerExtension, ensure_sequence_filter, - tojson + tojson, ) from salt.utils.odict import OrderedDict from salt.utils.templates import JINJA, render_jinja_tmpl +from tests.support.case import ModuleCase +from tests.support.helpers import flaky +from tests.support.mock import MagicMock, Mock, patch -# dateutils is needed so that the strftime jinja filter is loaded -import salt.utils.dateutils # pylint: disable=unused-import -import salt.utils.files -import salt.utils.stringutils -import salt.utils.yaml +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf # Import 3rd party libs try: import timelib # pylint: disable=W0611 + HAS_TIMELIB = True except ImportError: HAS_TIMELIB = False @@ -57,77 +57,71 @@ BLINESEP = salt.utils.stringutils.to_bytes(os.linesep) class JinjaTestCase(TestCase): def test_tojson(self): - ''' + """ Test the tojson filter for those using Jinja < 2.9. Non-ascii unicode content should be dumped with ensure_ascii=True. - ''' - data = {'Non-ascii words': ['süß', 'спам', 'яйца']} + """ + data = {"Non-ascii words": ["süß", "спам", "яйца"]} result = tojson(data) - expected = ('{"Non-ascii words": ["s\\u00fc\\u00df", ' - '"\\u0441\\u043f\\u0430\\u043c", ' - '"\\u044f\\u0439\\u0446\\u0430"]}') + expected = ( + '{"Non-ascii words": ["s\\u00fc\\u00df", ' + '"\\u0441\\u043f\\u0430\\u043c", ' + '"\\u044f\\u0439\\u0446\\u0430"]}' + ) assert result == expected, result class MockFileClient(object): - ''' + """ Does not download files but records any file request for testing - ''' + """ + def __init__(self, loader=None): if loader: loader._file_client = self self.requests = [] - def get_file(self, template, dest='', makedirs=False, saltenv='base'): - self.requests.append({ - 'path': template, - 'dest': dest, - 'makedirs': makedirs, - 'saltenv': saltenv - }) + def get_file(self, template, dest="", makedirs=False, saltenv="base"): + self.requests.append( + {"path": template, "dest": dest, "makedirs": makedirs, "saltenv": saltenv} + ) def _setup_test_dir(src_dir, test_dir): os.makedirs(test_dir) salt.utils.files.recursive_copy(src_dir, test_dir) - filename = os.path.join(test_dir, 'non_ascii') - with salt.utils.files.fopen(filename, 'wb') as fp: - fp.write(b'Assun\xc3\xa7\xc3\xa3o' + BLINESEP) - filename = os.path.join(test_dir, 'hello_simple') - with salt.utils.files.fopen(filename, 'wb') as fp: - fp.write(b'world' + BLINESEP) - filename = os.path.join(test_dir, 'hello_import') + filename = os.path.join(test_dir, "non_ascii") + with salt.utils.files.fopen(filename, "wb") as fp: + fp.write(b"Assun\xc3\xa7\xc3\xa3o" + BLINESEP) + filename = os.path.join(test_dir, "hello_simple") + with salt.utils.files.fopen(filename, "wb") as fp: + fp.write(b"world" + BLINESEP) + filename = os.path.join(test_dir, "hello_import") lines = [ r"{% from 'macro' import mymacro -%}", r"{% from 'macro' import mymacro -%}", r"{{ mymacro('Hey') ~ mymacro(a|default('a'), b|default('b')) }}", ] - with salt.utils.files.fopen(filename, 'wb') as fp: + with salt.utils.files.fopen(filename, "wb") as fp: for line in lines: - fp.write(line.encode('utf-8') + BLINESEP) + fp.write(line.encode("utf-8") + BLINESEP) class TestSaltCacheLoader(TestCase): - def setUp(self): self.tempdir = tempfile.mkdtemp() - self.template_dir = os.path.join(self.tempdir, 'files', 'test') + self.template_dir = os.path.join(self.tempdir, "files", "test") _setup_test_dir( - os.path.join(RUNTIME_VARS.BASE_FILES, 'templates'), - self.template_dir + os.path.join(RUNTIME_VARS.BASE_FILES, "templates"), self.template_dir ) self.opts = { - 'file_buffer_size': 1048576, - 'cachedir': self.tempdir, - 'file_roots': { - 'test': [self.template_dir] - }, - 'pillar_roots': { - 'test': [self.template_dir] - }, - 'extension_modules': os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'extmods'), + "file_buffer_size": 1048576, + "cachedir": self.tempdir, + "file_roots": {"test": [self.template_dir]}, + "pillar_roots": {"test": [self.template_dir]}, + "extension_modules": os.path.join( + os.path.dirname(os.path.abspath(__file__)), "extmods" + ), } super(TestSaltCacheLoader, self).setUp() @@ -135,125 +129,129 @@ class TestSaltCacheLoader(TestCase): salt.utils.files.rm_rf(self.tempdir) def test_searchpath(self): - ''' + """ The searchpath is based on the cachedir option and the saltenv parameter - ''' + """ tmp = tempfile.gettempdir() opts = copy.deepcopy(self.opts) - opts.update({'cachedir': tmp}) - loader = self.get_loader(opts=opts, saltenv='test') - assert loader.searchpath == [os.path.join(tmp, 'files', 'test')] + opts.update({"cachedir": tmp}) + loader = self.get_loader(opts=opts, saltenv="test") + assert loader.searchpath == [os.path.join(tmp, "files", "test")] def test_mockclient(self): - ''' + """ A MockFileClient is used that records all file requests normally sent to the master. - ''' - loader = self.get_loader(opts=self.opts, saltenv='test') - res = loader.get_source(None, 'hello_simple') + """ + loader = self.get_loader(opts=self.opts, saltenv="test") + res = loader.get_source(None, "hello_simple") assert len(res) == 3 # res[0] on Windows is unicode and use os.linesep so it works cross OS - self.assertEqual(six.text_type(res[0]), 'world' + os.linesep) - tmpl_dir = os.path.join(self.template_dir, 'hello_simple') + self.assertEqual(six.text_type(res[0]), "world" + os.linesep) + tmpl_dir = os.path.join(self.template_dir, "hello_simple") self.assertEqual(res[1], tmpl_dir) - assert res[2](), 'Template up to date?' + assert res[2](), "Template up to date?" assert loader._file_client.requests - self.assertEqual(loader._file_client.requests[0]['path'], 'salt://hello_simple') + self.assertEqual(loader._file_client.requests[0]["path"], "salt://hello_simple") - def get_loader(self, opts=None, saltenv='base'): - ''' + def get_loader(self, opts=None, saltenv="base"): + """ Now that we instantiate the client in the __init__, we need to mock it - ''' + """ if opts is None: opts = self.opts - with patch.object(SaltCacheLoader, 'file_client', Mock()): + with patch.object(SaltCacheLoader, "file_client", Mock()): loader = SaltCacheLoader(opts, saltenv) - self.addCleanup(setattr, SaltCacheLoader, '_cached_client', None) + self.addCleanup(setattr, SaltCacheLoader, "_cached_client", None) # Create a mock file client and attach it to the loader MockFileClient(loader) return loader def get_test_saltenv(self): - ''' + """ Setup a simple jinja test environment - ''' - loader = self.get_loader(saltenv='test') + """ + loader = self.get_loader(saltenv="test") jinja = Environment(loader=loader) return loader._file_client, jinja def test_import(self): - ''' + """ You can import and use macros from other files - ''' + """ fc, jinja = self.get_test_saltenv() - result = jinja.get_template('hello_import').render() - self.assertEqual(result, 'Hey world !a b !') + result = jinja.get_template("hello_import").render() + self.assertEqual(result, "Hey world !a b !") assert len(fc.requests) == 2 - self.assertEqual(fc.requests[0]['path'], 'salt://hello_import') - self.assertEqual(fc.requests[1]['path'], 'salt://macro') + self.assertEqual(fc.requests[0]["path"], "salt://hello_import") + self.assertEqual(fc.requests[1]["path"], "salt://macro") def test_relative_import(self): - ''' + """ You can import using relative paths issue-13889 - ''' + """ fc, jinja = self.get_test_saltenv() - tmpl = jinja.get_template(os.path.join('relative', 'rhello')) + tmpl = jinja.get_template(os.path.join("relative", "rhello")) result = tmpl.render() - self.assertEqual(result, 'Hey world !a b !') + self.assertEqual(result, "Hey world !a b !") assert len(fc.requests) == 3 - self.assertEqual(fc.requests[0]['path'], os.path.join('salt://relative', 'rhello')) - self.assertEqual(fc.requests[1]['path'], os.path.join('salt://relative', 'rmacro')) - self.assertEqual(fc.requests[2]['path'], 'salt://macro') + self.assertEqual( + fc.requests[0]["path"], os.path.join("salt://relative", "rhello") + ) + self.assertEqual( + fc.requests[1]["path"], os.path.join("salt://relative", "rmacro") + ) + self.assertEqual(fc.requests[2]["path"], "salt://macro") # This must fail when rendered: attempts to import from outside file root - template = jinja.get_template('relative/rescape') + template = jinja.get_template("relative/rescape") self.assertRaises(exceptions.TemplateNotFound, template.render) def test_include(self): - ''' + """ You can also include a template that imports and uses macros - ''' + """ fc, jinja = self.get_test_saltenv() - result = jinja.get_template('hello_include').render() - self.assertEqual(result, 'Hey world !a b !') + result = jinja.get_template("hello_include").render() + self.assertEqual(result, "Hey world !a b !") assert len(fc.requests) == 3 - self.assertEqual(fc.requests[0]['path'], 'salt://hello_include') - self.assertEqual(fc.requests[1]['path'], 'salt://hello_import') - self.assertEqual(fc.requests[2]['path'], 'salt://macro') + self.assertEqual(fc.requests[0]["path"], "salt://hello_include") + self.assertEqual(fc.requests[1]["path"], "salt://hello_import") + self.assertEqual(fc.requests[2]["path"], "salt://macro") def test_include_context(self): - ''' + """ Context variables are passes to the included template by default. - ''' + """ _, jinja = self.get_test_saltenv() - result = jinja.get_template('hello_include').render(a='Hi', b='Salt') - self.assertEqual(result, 'Hey world !Hi Salt !') + result = jinja.get_template("hello_include").render(a="Hi", b="Salt") + self.assertEqual(result, "Hey world !Hi Salt !") def test_cached_file_client(self): - ''' + """ Multiple instantiations of SaltCacheLoader use the cached file client - ''' - with patch('salt.transport.client.ReqChannel.factory', Mock()): + """ + with patch("salt.transport.client.ReqChannel.factory", Mock()): loader_a = SaltCacheLoader(self.opts) loader_b = SaltCacheLoader(self.opts) assert loader_a._file_client is loader_b._file_client def test_file_client_kwarg(self): - ''' + """ A file client can be passed to SaltCacheLoader overriding the any cached file client - ''' + """ mfc = MockFileClient() loader = SaltCacheLoader(self.opts, _file_client=mfc) assert loader._file_client is mfc def test_cache_loader_shutdown(self): - ''' + """ The shudown method can be called without raising an exception when the file_client does not have a destroy method - ''' + """ mfc = MockFileClient() - assert not hasattr(mfc, 'destroy') + assert not hasattr(mfc, "destroy") loader = SaltCacheLoader(self.opts, _file_client=mfc) assert loader._file_client is mfc # Shutdown method should not raise any exceptions @@ -261,31 +259,25 @@ class TestSaltCacheLoader(TestCase): class TestGetTemplate(TestCase): - def setUp(self): self.tempdir = tempfile.mkdtemp() - self.template_dir = os.path.join(self.tempdir, 'files', 'test') + self.template_dir = os.path.join(self.tempdir, "files", "test") _setup_test_dir( - os.path.join(RUNTIME_VARS.BASE_FILES, 'templates'), - self.template_dir + os.path.join(RUNTIME_VARS.BASE_FILES, "templates"), self.template_dir ) self.local_opts = { - 'file_buffer_size': 1048576, - 'cachedir': self.tempdir, - 'file_client': 'local', - 'file_ignore_regex': None, - 'file_ignore_glob': None, - 'file_roots': { - 'test': [self.template_dir] - }, - 'pillar_roots': { - 'test': [self.template_dir] - }, - 'fileserver_backend': ['roots'], - 'hash_type': 'md5', - 'extension_modules': os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'extmods'), + "file_buffer_size": 1048576, + "cachedir": self.tempdir, + "file_client": "local", + "file_ignore_regex": None, + "file_ignore_glob": None, + "file_roots": {"test": [self.template_dir]}, + "pillar_roots": {"test": [self.template_dir]}, + "fileserver_backend": ["roots"], + "hash_type": "md5", + "extension_modules": os.path.join( + os.path.dirname(os.path.abspath(__file__)), "extmods" + ), } self.local_salt = {} super(TestGetTemplate, self).setUp() @@ -294,337 +286,365 @@ class TestGetTemplate(TestCase): salt.utils.files.rm_rf(self.tempdir) def test_fallback(self): - ''' + """ A Template with a filesystem loader is returned as fallback if the file is not contained in the searchpath - ''' - fn_ = os.path.join(self.template_dir, 'hello_simple') + """ + fn_ = os.path.join(self.template_dir, "hello_simple") with salt.utils.files.fopen(fn_) as fp_: out = render_jinja_tmpl( salt.utils.stringutils.to_unicode(fp_.read()), - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) - self.assertEqual(out, 'world' + os.linesep) + self.assertEqual(out, "world" + os.linesep) def test_fallback_noloader(self): - ''' + """ A Template with a filesystem loader is returned as fallback if the file is not contained in the searchpath - ''' - filename = os.path.join(self.template_dir, 'hello_import') + """ + filename = os.path.join(self.template_dir, "hello_import") with salt.utils.files.fopen(filename) as fp_: out = render_jinja_tmpl( salt.utils.stringutils.to_unicode(fp_.read()), - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) - self.assertEqual(out, 'Hey world !a b !' + os.linesep) + self.assertEqual(out, "Hey world !a b !" + os.linesep) def test_saltenv(self): - ''' + """ If the template is within the searchpath it can import, include and extend other templates. The initial template is expected to be already cached get_template does not request it from the master again. - ''' + """ fc = MockFileClient() - with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)): - filename = os.path.join(self.template_dir, 'hello_import') + with patch.object(SaltCacheLoader, "file_client", MagicMock(return_value=fc)): + filename = os.path.join(self.template_dir, "hello_import") with salt.utils.files.fopen(filename) as fp_: out = render_jinja_tmpl( salt.utils.stringutils.to_unicode(fp_.read()), - dict(opts={'cachedir': self.tempdir, 'file_client': 'remote', - 'file_roots': self.local_opts['file_roots'], - 'pillar_roots': self.local_opts['pillar_roots']}, - a='Hi', b='Salt', saltenv='test', salt=self.local_salt)) - self.assertEqual(out, 'Hey world !Hi Salt !' + os.linesep) - self.assertEqual(fc.requests[0]['path'], 'salt://macro') + dict( + opts={ + "cachedir": self.tempdir, + "file_client": "remote", + "file_roots": self.local_opts["file_roots"], + "pillar_roots": self.local_opts["pillar_roots"], + }, + a="Hi", + b="Salt", + saltenv="test", + salt=self.local_salt, + ), + ) + self.assertEqual(out, "Hey world !Hi Salt !" + os.linesep) + self.assertEqual(fc.requests[0]["path"], "salt://macro") def test_macro_additional_log_for_generalexc(self): - ''' + """ If we failed in a macro because of e.g. a TypeError, get more output from trace. - ''' - expected = r'''Jinja error:.*division.* + """ + expected = r"""Jinja error:.*division.* .*macrogeneral\(2\): --- \{% macro mymacro\(\) -%\} \{\{ 1/0 \}\} <====================== \{%- endmacro %\} ----.*''' - filename = os.path.join(self.template_dir, 'hello_import_generalerror') +---.*""" + filename = os.path.join(self.template_dir, "hello_import_generalerror") fc = MockFileClient() - with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)): + with patch.object(SaltCacheLoader, "file_client", MagicMock(return_value=fc)): with salt.utils.files.fopen(filename) as fp_: self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, salt.utils.stringutils.to_unicode(fp_.read()), - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) def test_macro_additional_log_for_undefined(self): - ''' + """ If we failed in a macro because of undefined variables, get more output from trace. - ''' - expected = r'''Jinja variable 'b' is undefined + """ + expected = r"""Jinja variable 'b' is undefined .*macroundefined\(2\): --- \{% macro mymacro\(\) -%\} \{\{b.greetee\}\} <-- error is here <====================== \{%- endmacro %\} ----''' - filename = os.path.join(self.template_dir, 'hello_import_undefined') +---""" + filename = os.path.join(self.template_dir, "hello_import_undefined") fc = MockFileClient() - with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)): + with patch.object(SaltCacheLoader, "file_client", MagicMock(return_value=fc)): with salt.utils.files.fopen(filename) as fp_: self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, salt.utils.stringutils.to_unicode(fp_.read()), - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) def test_macro_additional_log_syntaxerror(self): - ''' + """ If we failed in a macro, get more output from trace. - ''' - expected = r'''Jinja syntax error: expected token .*end.*got '-'.* + """ + expected = r"""Jinja syntax error: expected token .*end.*got '-'.* .*macroerror\(2\): --- # macro \{% macro mymacro\(greeting, greetee='world'\) -\} <-- error is here <====================== \{\{ greeting ~ ' ' ~ greetee \}\} ! \{%- endmacro %\} ----.*''' - filename = os.path.join(self.template_dir, 'hello_import_error') +---.*""" + filename = os.path.join(self.template_dir, "hello_import_error") fc = MockFileClient() - with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)): + with patch.object(SaltCacheLoader, "file_client", MagicMock(return_value=fc)): with salt.utils.files.fopen(filename) as fp_: self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, salt.utils.stringutils.to_unicode(fp_.read()), - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) def test_non_ascii_encoding(self): fc = MockFileClient() - with patch.object(SaltCacheLoader, 'file_client', MagicMock(return_value=fc)): - filename = os.path.join(self.template_dir, 'hello_import') + with patch.object(SaltCacheLoader, "file_client", MagicMock(return_value=fc)): + filename = os.path.join(self.template_dir, "hello_import") with salt.utils.files.fopen(filename) as fp_: out = render_jinja_tmpl( salt.utils.stringutils.to_unicode(fp_.read()), - dict(opts={'cachedir': self.tempdir, 'file_client': 'remote', - 'file_roots': self.local_opts['file_roots'], - 'pillar_roots': self.local_opts['pillar_roots']}, - a='Hi', b='Sàlt', saltenv='test', salt=self.local_salt)) - self.assertEqual(out, salt.utils.stringutils.to_unicode('Hey world !Hi Sàlt !' + os.linesep)) - self.assertEqual(fc.requests[0]['path'], 'salt://macro') + dict( + opts={ + "cachedir": self.tempdir, + "file_client": "remote", + "file_roots": self.local_opts["file_roots"], + "pillar_roots": self.local_opts["pillar_roots"], + }, + a="Hi", + b="Sàlt", + saltenv="test", + salt=self.local_salt, + ), + ) + self.assertEqual( + out, + salt.utils.stringutils.to_unicode("Hey world !Hi Sàlt !" + os.linesep), + ) + self.assertEqual(fc.requests[0]["path"], "salt://macro") - filename = os.path.join(self.template_dir, 'non_ascii') - with salt.utils.files.fopen(filename, 'rb') as fp_: + filename = os.path.join(self.template_dir, "non_ascii") + with salt.utils.files.fopen(filename, "rb") as fp_: out = render_jinja_tmpl( - salt.utils.stringutils.to_unicode(fp_.read(), 'utf-8'), - dict(opts={'cachedir': self.tempdir, 'file_client': 'remote', - 'file_roots': self.local_opts['file_roots'], - 'pillar_roots': self.local_opts['pillar_roots']}, - a='Hi', b='Sàlt', saltenv='test', salt=self.local_salt)) - self.assertEqual('Assunção' + os.linesep, out) - self.assertEqual(fc.requests[0]['path'], 'salt://macro') + salt.utils.stringutils.to_unicode(fp_.read(), "utf-8"), + dict( + opts={ + "cachedir": self.tempdir, + "file_client": "remote", + "file_roots": self.local_opts["file_roots"], + "pillar_roots": self.local_opts["pillar_roots"], + }, + a="Hi", + b="Sàlt", + saltenv="test", + salt=self.local_salt, + ), + ) + self.assertEqual("Assunção" + os.linesep, out) + self.assertEqual(fc.requests[0]["path"], "salt://macro") - @skipIf(HAS_TIMELIB is False, 'The `timelib` library is not installed.') + @skipIf(HAS_TIMELIB is False, "The `timelib` library is not installed.") def test_strftime(self): response = render_jinja_tmpl( '{{ "2002/12/25"|strftime }}', - dict( - opts=self.local_opts, - saltenv='test', - salt=self.local_salt - )) - self.assertEqual(response, '2002-12-25') + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(response, "2002-12-25") objects = ( datetime.datetime(2002, 12, 25, 12, 00, 00, 00), - '2002/12/25', + "2002/12/25", 1040814000, - '1040814000' + "1040814000", ) for object in objects: response = render_jinja_tmpl( - '{{ object|strftime }}', + "{{ object|strftime }}", dict( object=object, opts=self.local_opts, - saltenv='test', - salt=self.local_salt - )) - self.assertEqual(response, '2002-12-25') + saltenv="test", + salt=self.local_salt, + ), + ) + self.assertEqual(response, "2002-12-25") response = render_jinja_tmpl( '{{ object|strftime("%b %d, %Y") }}', dict( object=object, opts=self.local_opts, - saltenv='test', - salt=self.local_salt - )) - self.assertEqual(response, 'Dec 25, 2002') + saltenv="test", + salt=self.local_salt, + ), + ) + self.assertEqual(response, "Dec 25, 2002") response = render_jinja_tmpl( '{{ object|strftime("%y") }}', dict( object=object, opts=self.local_opts, - saltenv='test', - salt=self.local_salt - )) - self.assertEqual(response, '02') + saltenv="test", + salt=self.local_salt, + ), + ) + self.assertEqual(response, "02") def test_non_ascii(self): - fn = os.path.join(self.template_dir, 'non_ascii') - out = JINJA( - fn, - opts=self.local_opts, - saltenv='test', - salt=self.local_salt - ) - with salt.utils.files.fopen(out['data'], 'rb') as fp: - result = salt.utils.stringutils.to_unicode(fp.read(), 'utf-8') - self.assertEqual(salt.utils.stringutils.to_unicode('Assunção' + os.linesep), result) + fn = os.path.join(self.template_dir, "non_ascii") + out = JINJA(fn, opts=self.local_opts, saltenv="test", salt=self.local_salt) + with salt.utils.files.fopen(out["data"], "rb") as fp: + result = salt.utils.stringutils.to_unicode(fp.read(), "utf-8") + self.assertEqual( + salt.utils.stringutils.to_unicode("Assunção" + os.linesep), result + ) def test_get_context_has_enough_context(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 8) - expected = '---\n[...]\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\n[...]\n---' + expected = "---\n[...]\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\n[...]\n---" self.assertEqual(expected, context) def test_get_context_at_top_of_file(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 1) - expected = '---\n1\n2\n3\n4\n5\n6\n[...]\n---' + expected = "---\n1\n2\n3\n4\n5\n6\n[...]\n---" self.assertEqual(expected, context) def test_get_context_at_bottom_of_file(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 15) - expected = '---\n[...]\na\nb\nc\nd\ne\nf\n---' + expected = "---\n[...]\na\nb\nc\nd\ne\nf\n---" self.assertEqual(expected, context) def test_get_context_2_context_lines(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 8, num_lines=2) - expected = '---\n[...]\n6\n7\n8\n9\na\n[...]\n---' + expected = "---\n[...]\n6\n7\n8\n9\na\n[...]\n---" self.assertEqual(expected, context) def test_get_context_with_marker(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' - context = salt.utils.stringutils.get_context(template, 8, num_lines=2, marker=' <---') - expected = '---\n[...]\n6\n7\n8 <---\n9\na\n[...]\n---' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" + context = salt.utils.stringutils.get_context( + template, 8, num_lines=2, marker=" <---" + ) + expected = "---\n[...]\n6\n7\n8 <---\n9\na\n[...]\n---" self.assertEqual(expected, context) def test_render_with_syntax_error(self): - template = 'hello\n\n{{ bad\n\nfoo' - expected = r'.*---\nhello\n\n{{ bad\n\nfoo <======================\n---' + template = "hello\n\n{{ bad\n\nfoo" + expected = r".*---\nhello\n\n{{ bad\n\nfoo <======================\n---" self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, template, - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) - @skipIf(six.PY3, 'Not applicable to Python 3') + @skipIf(six.PY3, "Not applicable to Python 3") def test_render_with_unicode_syntax_error(self): - with patch.object(builtins, '__salt_system_encoding__', 'utf-8'): - template = 'hello\n\n{{ bad\n\nfoo한' - expected = r'.*---\nhello\n\n{{ bad\n\nfoo\xed\x95\x9c <======================\n---' + with patch.object(builtins, "__salt_system_encoding__", "utf-8"): + template = "hello\n\n{{ bad\n\nfoo한" + expected = r".*---\nhello\n\n{{ bad\n\nfoo\xed\x95\x9c <======================\n---" self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, template, - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) def test_render_with_utf8_syntax_error(self): - with patch.object(builtins, '__salt_system_encoding__', 'utf-8'): - template = 'hello\n\n{{ bad\n\nfoo한' + with patch.object(builtins, "__salt_system_encoding__", "utf-8"): + template = "hello\n\n{{ bad\n\nfoo한" expected = salt.utils.stringutils.to_str( - r'.*---\nhello\n\n{{ bad\n\nfoo한 <======================\n---' + r".*---\nhello\n\n{{ bad\n\nfoo한 <======================\n---" ) self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, template, - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) def test_render_with_undefined_variable(self): template = "hello\n\n{{ foo }}\n\nfoo" - expected = r'Jinja variable \'foo\' is undefined' + expected = r"Jinja variable \'foo\' is undefined" self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, template, - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) def test_render_with_undefined_variable_utf8(self): template = "hello\xed\x95\x9c\n\n{{ foo }}\n\nfoo" - expected = r'Jinja variable \'foo\' is undefined' + expected = r"Jinja variable \'foo\' is undefined" self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, template, - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) def test_render_with_undefined_variable_unicode(self): - template = 'hello한\n\n{{ foo }}\n\nfoo' - expected = r'Jinja variable \'foo\' is undefined' + template = "hello한\n\n{{ foo }}\n\nfoo" + expected = r"Jinja variable \'foo\' is undefined" self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, template, - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) class TestJinjaDefaultOptions(TestCase): - def __init__(self, *args, **kws): TestCase.__init__(self, *args, **kws) self.local_opts = { - 'cachedir': os.path.join(RUNTIME_VARS.TMP, 'jinja-template-cache'), - 'file_buffer_size': 1048576, - 'file_client': 'local', - 'file_ignore_regex': None, - 'file_ignore_glob': None, - 'file_roots': { - 'test': [os.path.join(RUNTIME_VARS.BASE_FILES, 'templates')] + "cachedir": os.path.join(RUNTIME_VARS.TMP, "jinja-template-cache"), + "file_buffer_size": 1048576, + "file_client": "local", + "file_ignore_regex": None, + "file_ignore_glob": None, + "file_roots": { + "test": [os.path.join(RUNTIME_VARS.BASE_FILES, "templates")] }, - 'pillar_roots': { - 'test': [os.path.join(RUNTIME_VARS.BASE_FILES, 'templates')] - }, - 'fileserver_backend': ['roots'], - 'hash_type': 'md5', - 'extension_modules': os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'extmods'), - 'jinja_env': { - 'line_comment_prefix': '##', - 'line_statement_prefix': '%', + "pillar_roots": { + "test": [os.path.join(RUNTIME_VARS.BASE_FILES, "templates")] }, + "fileserver_backend": ["roots"], + "hash_type": "md5", + "extension_modules": os.path.join( + os.path.dirname(os.path.abspath(__file__)), "extmods" + ), + "jinja_env": {"line_comment_prefix": "##", "line_statement_prefix": "%"}, } self.local_salt = { - 'myvar': 'zero', - 'mylist': [0, 1, 2, 3], + "myvar": "zero", + "mylist": [0, 1, 2, 3], } def test_comment_prefix(self): @@ -640,9 +660,10 @@ class TestJinjaDefaultOptions(TestCase): %- endif {{- myvar -}} """ - rendered = render_jinja_tmpl(template, - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'onetwothree') + rendered = render_jinja_tmpl( + template, dict(opts=self.local_opts, saltenv="test", salt=self.local_salt) + ) + self.assertEqual(rendered, "onetwothree") def test_statement_prefix(self): @@ -653,32 +674,32 @@ class TestJinjaDefaultOptions(TestCase): {{- item }} %- endfor """ - rendered = render_jinja_tmpl(template, - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'onetwothree') + rendered = render_jinja_tmpl( + template, dict(opts=self.local_opts, saltenv="test", salt=self.local_salt) + ) + self.assertEqual(rendered, "onetwothree") class TestCustomExtensions(TestCase): - def __init__(self, *args, **kws): super(TestCustomExtensions, self).__init__(*args, **kws) self.local_opts = { - 'cachedir': os.path.join(RUNTIME_VARS.TMP, 'jinja-template-cache'), - 'file_buffer_size': 1048576, - 'file_client': 'local', - 'file_ignore_regex': None, - 'file_ignore_glob': None, - 'file_roots': { - 'test': [os.path.join(RUNTIME_VARS.BASE_FILES, 'templates')] + "cachedir": os.path.join(RUNTIME_VARS.TMP, "jinja-template-cache"), + "file_buffer_size": 1048576, + "file_client": "local", + "file_ignore_regex": None, + "file_ignore_glob": None, + "file_roots": { + "test": [os.path.join(RUNTIME_VARS.BASE_FILES, "templates")] }, - 'pillar_roots': { - 'test': [os.path.join(RUNTIME_VARS.BASE_FILES, 'templates')] + "pillar_roots": { + "test": [os.path.join(RUNTIME_VARS.BASE_FILES, "templates")] }, - 'fileserver_backend': ['roots'], - 'hash_type': 'md5', - 'extension_modules': os.path.join( - os.path.dirname(os.path.abspath(__file__)), - 'extmods'), + "fileserver_backend": ["roots"], + "hash_type": "md5", + "extension_modules": os.path.join( + os.path.dirname(os.path.abspath(__file__)), "extmods" + ), } self.local_salt = { # 'dns.A': dnsutil.A, @@ -689,57 +710,67 @@ class TestCustomExtensions(TestCase): } def test_regex_escape(self): - dataset = 'foo?:.*/\\bar' + dataset = "foo?:.*/\\bar" env = Environment(extensions=[SerializerExtension]) env.filters.update(JinjaFilter.salt_jinja_filters) - rendered = env.from_string('{{ dataset|regex_escape }}').render(dataset=dataset) + rendered = env.from_string("{{ dataset|regex_escape }}").render(dataset=dataset) self.assertEqual(rendered, re.escape(dataset)) def test_unique_string(self): - dataset = 'foo' + dataset = "foo" unique = set(dataset) env = Environment(extensions=[SerializerExtension]) env.filters.update(JinjaFilter.salt_jinja_filters) if six.PY3: - rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset).strip("'{}").split("', '") + rendered = ( + env.from_string("{{ dataset|unique }}") + .render(dataset=dataset) + .strip("'{}") + .split("', '") + ) self.assertEqual(sorted(rendered), sorted(list(unique))) else: - rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset) + rendered = env.from_string("{{ dataset|unique }}").render(dataset=dataset) self.assertEqual(rendered, "{0}".format(unique)) def test_unique_tuple(self): - dataset = ('foo', 'foo', 'bar') + dataset = ("foo", "foo", "bar") unique = set(dataset) env = Environment(extensions=[SerializerExtension]) env.filters.update(JinjaFilter.salt_jinja_filters) if six.PY3: - rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset).strip("'{}").split("', '") + rendered = ( + env.from_string("{{ dataset|unique }}") + .render(dataset=dataset) + .strip("'{}") + .split("', '") + ) self.assertEqual(sorted(rendered), sorted(list(unique))) else: - rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset) + rendered = env.from_string("{{ dataset|unique }}").render(dataset=dataset) self.assertEqual(rendered, "{0}".format(unique)) def test_unique_list(self): - dataset = ['foo', 'foo', 'bar'] - unique = ['foo', 'bar'] + dataset = ["foo", "foo", "bar"] + unique = ["foo", "bar"] env = Environment(extensions=[SerializerExtension]) env.filters.update(JinjaFilter.salt_jinja_filters) if six.PY3: - rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset).strip("'[]").split("', '") + rendered = ( + env.from_string("{{ dataset|unique }}") + .render(dataset=dataset) + .strip("'[]") + .split("', '") + ) self.assertEqual(rendered, unique) else: - rendered = env.from_string('{{ dataset|unique }}').render(dataset=dataset) + rendered = env.from_string("{{ dataset|unique }}").render(dataset=dataset) self.assertEqual(rendered, "{0}".format(unique)) def test_serialize_json(self): - dataset = { - "foo": True, - "bar": 42, - "baz": [1, 2, 3], - "qux": 2.0 - } + dataset = {"foo": True, "bar": 42, "baz": [1, 2, 3], "qux": 2.0} env = Environment(extensions=[SerializerExtension]) - rendered = env.from_string('{{ dataset|json }}').render(dataset=dataset) + rendered = env.from_string("{{ dataset|json }}").render(dataset=dataset) self.assertEqual(dataset, salt.utils.json.loads(rendered)) def test_serialize_yaml(self): @@ -748,28 +779,22 @@ class TestCustomExtensions(TestCase): "bar": 42, "baz": [1, 2, 3], "qux": 2.0, - "spam": OrderedDict([ - ('foo', OrderedDict([ - ('bar', 'baz'), - ('qux', 42) - ]) - ) - ]) + "spam": OrderedDict([("foo", OrderedDict([("bar", "baz"), ("qux", 42)]))]), } env = Environment(extensions=[SerializerExtension]) - rendered = env.from_string('{{ dataset|yaml }}').render(dataset=dataset) + rendered = env.from_string("{{ dataset|yaml }}").render(dataset=dataset) self.assertEqual(dataset, salt.utils.yaml.safe_load(rendered)) def test_serialize_yaml_str(self): dataset = "str value" env = Environment(extensions=[SerializerExtension]) - rendered = env.from_string('{{ dataset|yaml }}').render(dataset=dataset) + rendered = env.from_string("{{ dataset|yaml }}").render(dataset=dataset) self.assertEqual(dataset, rendered) def test_serialize_yaml_unicode(self): - dataset = 'str value' + dataset = "str value" env = Environment(extensions=[SerializerExtension]) - rendered = env.from_string('{{ dataset|yaml }}').render(dataset=dataset) + rendered = env.from_string("{{ dataset|yaml }}").render(dataset=dataset) if six.PY3: self.assertEqual("str value", rendered) else: @@ -779,93 +804,111 @@ class TestCustomExtensions(TestCase): # type of the rendered variable (should be unicode, which is the same as # six.text_type). This should cover all use cases but also allow the test # to pass on CentOS 6 running Python 2.7. - self.assertIn('str value', rendered) + self.assertIn("str value", rendered) self.assertIsInstance(rendered, six.text_type) def test_serialize_python(self): - dataset = { - "foo": True, - "bar": 42, - "baz": [1, 2, 3], - "qux": 2.0 - } + dataset = {"foo": True, "bar": 42, "baz": [1, 2, 3], "qux": 2.0} env = Environment(extensions=[SerializerExtension]) - rendered = env.from_string('{{ dataset|python }}').render(dataset=dataset) + rendered = env.from_string("{{ dataset|python }}").render(dataset=dataset) self.assertEqual(rendered, pprint.pformat(dataset)) def test_load_yaml(self): env = Environment(extensions=[SerializerExtension]) - rendered = env.from_string('{% set document = "{foo: it works}"|load_yaml %}{{ document.foo }}').render() + rendered = env.from_string( + '{% set document = "{foo: it works}"|load_yaml %}{{ document.foo }}' + ).render() self.assertEqual(rendered, "it works") - rendered = env.from_string('{% set document = document|load_yaml %}' - '{{ document.foo }}').render(document="{foo: it works}") + rendered = env.from_string( + "{% set document = document|load_yaml %}" "{{ document.foo }}" + ).render(document="{foo: it works}") self.assertEqual(rendered, "it works") with self.assertRaises((TypeError, exceptions.TemplateRuntimeError)): - env.from_string('{% set document = document|load_yaml %}' - '{{ document.foo }}').render(document={"foo": "it works"}) + env.from_string( + "{% set document = document|load_yaml %}" "{{ document.foo }}" + ).render(document={"foo": "it works"}) def test_load_tag(self): env = Environment(extensions=[SerializerExtension]) - source = '{{ bar }}, ' + \ - '{% load_yaml as docu %}{foo: it works, {{ bar }}: baz}{% endload %}' + \ - '{{ docu.foo }}' + source = ( + "{{ bar }}, " + + "{% load_yaml as docu %}{foo: it works, {{ bar }}: baz}{% endload %}" + + "{{ docu.foo }}" + ) rendered = env.from_string(source).render(bar="barred") self.assertEqual(rendered, "barred, it works") - source = '{{ bar }}, {% load_json as docu %}{"foo": "it works", "{{ bar }}": "baz"}{% endload %}' + \ - '{{ docu.foo }}' + source = ( + '{{ bar }}, {% load_json as docu %}{"foo": "it works", "{{ bar }}": "baz"}{% endload %}' + + "{{ docu.foo }}" + ) rendered = env.from_string(source).render(bar="barred") self.assertEqual(rendered, "barred, it works") with self.assertRaises(exceptions.TemplateSyntaxError): - env.from_string('{% load_yamle as document %}{foo, bar: it works}{% endload %}').render() + env.from_string( + "{% load_yamle as document %}{foo, bar: it works}{% endload %}" + ).render() with self.assertRaises(exceptions.TemplateRuntimeError): - env.from_string('{% load_json as document %}{foo, bar: it works}{% endload %}').render() + env.from_string( + "{% load_json as document %}{foo, bar: it works}{% endload %}" + ).render() def test_load_json(self): env = Environment(extensions=[SerializerExtension]) - rendered = env.from_string('{% set document = \'{"foo": "it works"}\'|load_json %}' - '{{ document.foo }}').render() + rendered = env.from_string( + '{% set document = \'{"foo": "it works"}\'|load_json %}' + "{{ document.foo }}" + ).render() self.assertEqual(rendered, "it works") - rendered = env.from_string('{% set document = document|load_json %}' - '{{ document.foo }}').render(document='{"foo": "it works"}') + rendered = env.from_string( + "{% set document = document|load_json %}" "{{ document.foo }}" + ).render(document='{"foo": "it works"}') self.assertEqual(rendered, "it works") # bad quotes with self.assertRaises(exceptions.TemplateRuntimeError): - env.from_string("{{ document|load_json }}").render(document="{'foo': 'it works'}") + env.from_string("{{ document|load_json }}").render( + document="{'foo': 'it works'}" + ) # not a string with self.assertRaises(exceptions.TemplateRuntimeError): - env.from_string('{{ document|load_json }}').render(document={"foo": "it works"}) + env.from_string("{{ document|load_json }}").render( + document={"foo": "it works"} + ) def test_load_yaml_template(self): - loader = DictLoader({'foo': '{bar: "my god is blue", foo: [1, 2, 3]}'}) + loader = DictLoader({"foo": '{bar: "my god is blue", foo: [1, 2, 3]}'}) env = Environment(extensions=[SerializerExtension], loader=loader) - rendered = env.from_string('{% import_yaml "foo" as doc %}{{ doc.bar }}').render() + rendered = env.from_string( + '{% import_yaml "foo" as doc %}{{ doc.bar }}' + ).render() self.assertEqual(rendered, "my god is blue") with self.assertRaises(exceptions.TemplateNotFound): env.from_string('{% import_yaml "does not exists" as doc %}').render() def test_load_json_template(self): - loader = DictLoader({'foo': '{"bar": "my god is blue", "foo": [1, 2, 3]}'}) + loader = DictLoader({"foo": '{"bar": "my god is blue", "foo": [1, 2, 3]}'}) env = Environment(extensions=[SerializerExtension], loader=loader) - rendered = env.from_string('{% import_json "foo" as doc %}{{ doc.bar }}').render() + rendered = env.from_string( + '{% import_json "foo" as doc %}{{ doc.bar }}' + ).render() self.assertEqual(rendered, "my god is blue") with self.assertRaises(exceptions.TemplateNotFound): env.from_string('{% import_json "does not exists" as doc %}').render() def test_load_text_template(self): - loader = DictLoader({'foo': 'Foo!'}) + loader = DictLoader({"foo": "Foo!"}) env = Environment(extensions=[SerializerExtension], loader=loader) rendered = env.from_string('{% import_text "foo" as doc %}{{ doc }}').render() @@ -875,184 +918,207 @@ class TestCustomExtensions(TestCase): env.from_string('{% import_text "does not exists" as doc %}').render() def test_catalog(self): - loader = DictLoader({ - 'doc1': '{bar: "my god is blue"}', - 'doc2': '{% import_yaml "doc1" as local2 %} never exported', - 'doc3': '{% load_yaml as local3 %}{"foo": "it works"}{% endload %} me neither', - 'main1': '{% from "doc2" import local2 %}{{ local2.bar }}', - 'main2': '{% from "doc3" import local3 %}{{ local3.foo }}', - 'main3': ''' + loader = DictLoader( + { + "doc1": '{bar: "my god is blue"}', + "doc2": '{% import_yaml "doc1" as local2 %} never exported', + "doc3": '{% load_yaml as local3 %}{"foo": "it works"}{% endload %} me neither', + "main1": '{% from "doc2" import local2 %}{{ local2.bar }}', + "main2": '{% from "doc3" import local3 %}{{ local3.foo }}', + "main3": """ {% import "doc2" as imported2 %} {% import "doc3" as imported3 %} {{ imported2.local2.bar }} - ''', - 'main4': ''' + """, + "main4": """ {% import "doc2" as imported2 %} {% import "doc3" as imported3 %} {{ imported3.local3.foo }} - ''', - 'main5': ''' + """, + "main5": """ {% from "doc2" import local2 as imported2 %} {% from "doc3" import local3 as imported3 %} {{ imported2.bar }} - ''', - 'main6': ''' + """, + "main6": """ {% from "doc2" import local2 as imported2 %} {% from "doc3" import local3 as imported3 %} {{ imported3.foo }} - ''' - - }) + """, + } + ) env = Environment(extensions=[SerializerExtension], loader=loader) - rendered = env.get_template('main1').render() + rendered = env.get_template("main1").render() self.assertEqual(rendered, "my god is blue") - rendered = env.get_template('main2').render() + rendered = env.get_template("main2").render() self.assertEqual(rendered, "it works") - rendered = env.get_template('main3').render().strip() + rendered = env.get_template("main3").render().strip() self.assertEqual(rendered, "my god is blue") - rendered = env.get_template('main4').render().strip() + rendered = env.get_template("main4").render().strip() self.assertEqual(rendered, "it works") - rendered = env.get_template('main5').render().strip() + rendered = env.get_template("main5").render().strip() self.assertEqual(rendered, "my god is blue") - rendered = env.get_template('main6').render().strip() + rendered = env.get_template("main6").render().strip() self.assertEqual(rendered, "it works") def test_nested_structures(self): env = Environment(extensions=[SerializerExtension]) - rendered = env.from_string('{{ data }}').render(data="foo") + rendered = env.from_string("{{ data }}").render(data="foo") self.assertEqual(rendered, "foo") - data = OrderedDict([ - ('foo', OrderedDict([ - ('bar', 'baz'), - ('qux', 42) - ])) - ]) + data = OrderedDict([("foo", OrderedDict([("bar", "baz"), ("qux", 42)]))]) - rendered = env.from_string('{{ data }}').render(data=data) + rendered = env.from_string("{{ data }}").render(data=data) self.assertEqual( rendered, - "{u'foo': {u'bar': u'baz', u'qux': 42}}" if six.PY2 - else "{'foo': {'bar': 'baz', 'qux': 42}}" + "{u'foo': {u'bar': u'baz', u'qux': 42}}" + if six.PY2 + else "{'foo': {'bar': 'baz', 'qux': 42}}", ) - rendered = env.from_string('{{ data }}').render(data=[ - OrderedDict( - foo='bar', - ), - OrderedDict( - baz=42, - ) - ]) + rendered = env.from_string("{{ data }}").render( + data=[OrderedDict(foo="bar",), OrderedDict(baz=42,)] + ) self.assertEqual( rendered, - "[{'foo': u'bar'}, {'baz': 42}]" if six.PY2 - else "[{'foo': 'bar'}, {'baz': 42}]" + "[{'foo': u'bar'}, {'baz': 42}]" + if six.PY2 + else "[{'foo': 'bar'}, {'baz': 42}]", ) def test_set_dict_key_value(self): - ''' + """ Test the `set_dict_key_value` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ {} | set_dict_key_value('foo:bar:baz', 42) }}", - dict(opts=self.local_opts, - saltenv='test', - salt=self.local_salt)) + """ + rendered = render_jinja_tmpl( + "{{ {} | set_dict_key_value('foo:bar:baz', 42) }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) self.assertEqual(rendered, "{'foo': {'bar': {'baz': 42}}}") - rendered = render_jinja_tmpl("{{ {} | set_dict_key_value('foo.bar.baz', 42, delimiter='.') }}", - dict(opts=self.local_opts, - saltenv='test', - salt=self.local_salt)) + rendered = render_jinja_tmpl( + "{{ {} | set_dict_key_value('foo.bar.baz', 42, delimiter='.') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) self.assertEqual(rendered, "{'foo': {'bar': {'baz': 42}}}") def test_update_dict_key_value(self): - ''' + """ Test the `update_dict_key_value` Jinja filter. - ''' + """ # Use OrderedDicts to avoid random key-order-switches in the rendered string. - expected = OrderedDict([('bar', OrderedDict([('baz', OrderedDict([('qux', 1), ('quux', 3)]))]))]) - dataset = OrderedDict([('bar', OrderedDict([('baz', OrderedDict([('qux', 1)]))]))]) - dataset_exp = OrderedDict([('quux', 3)]) - rendered = render_jinja_tmpl("{{ foo | update_dict_key_value('bar:baz', exp) }}", - dict(foo=dataset, - exp=dataset_exp, - opts=self.local_opts, - saltenv='test', - salt=self.local_salt)) + expected = OrderedDict( + [("bar", OrderedDict([("baz", OrderedDict([("qux", 1), ("quux", 3)]))]))] + ) + dataset = OrderedDict( + [("bar", OrderedDict([("baz", OrderedDict([("qux", 1)]))]))] + ) + dataset_exp = OrderedDict([("quux", 3)]) + rendered = render_jinja_tmpl( + "{{ foo | update_dict_key_value('bar:baz', exp) }}", + dict( + foo=dataset, + exp=dataset_exp, + opts=self.local_opts, + saltenv="test", + salt=self.local_salt, + ), + ) self.assertEqual( rendered, - "{u'bar': {u'baz': {u'qux': 1, u'quux': 3}}}" if six.PY2 - else "{'bar': {'baz': {'qux': 1, 'quux': 3}}}") + "{u'bar': {u'baz': {u'qux': 1, u'quux': 3}}}" + if six.PY2 + else "{'bar': {'baz': {'qux': 1, 'quux': 3}}}", + ) # Test incorrect usage - for update_with in [42, 'foo', [42]]: + for update_with in [42, "foo", [42]]: template = "{{ {} | update_dict_key_value('bar:baz', update_with) }}" - expected = r"Cannot update {} with a {}.".format(type({}), type(update_with)) + expected = r"Cannot update {} with a {}.".format( + type({}), type(update_with) + ) self.assertRaisesRegex( SaltRenderError, expected, render_jinja_tmpl, template, - dict(update_with=update_with, - opts=self.local_opts, - saltenv='test', - salt=self.local_salt) + dict( + update_with=update_with, + opts=self.local_opts, + saltenv="test", + salt=self.local_salt, + ), ) def test_append_dict_key_value(self): - ''' + """ Test the `append_dict_key_value` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ {} | append_dict_key_value('foo:bar:baz', 42) }}", - dict(opts=self.local_opts, - saltenv='test', - salt=self.local_salt)) + """ + rendered = render_jinja_tmpl( + "{{ {} | append_dict_key_value('foo:bar:baz', 42) }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) self.assertEqual(rendered, "{'foo': {'bar': {'baz': [42]}}}") - rendered = render_jinja_tmpl("{{ foo | append_dict_key_value('bar:baz', 42) }}", - dict(foo={'bar': {'baz': [1, 2]}}, - opts=self.local_opts, - saltenv='test', - salt=self.local_salt)) + rendered = render_jinja_tmpl( + "{{ foo | append_dict_key_value('bar:baz', 42) }}", + dict( + foo={"bar": {"baz": [1, 2]}}, + opts=self.local_opts, + saltenv="test", + salt=self.local_salt, + ), + ) self.assertEqual( rendered, - "{u'bar': {u'baz': [1, 2, 42]}}" if six.PY2 - else "{'bar': {'baz': [1, 2, 42]}}" + "{u'bar': {u'baz': [1, 2, 42]}}" + if six.PY2 + else "{'bar': {'baz': [1, 2, 42]}}", ) def test_extend_dict_key_value(self): - ''' + """ Test the `extend_dict_key_value` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ {} | extend_dict_key_value('foo:bar:baz', [42]) }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + """ + rendered = render_jinja_tmpl( + "{{ {} | extend_dict_key_value('foo:bar:baz', [42]) }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) self.assertEqual(rendered, "{'foo': {'bar': {'baz': [42]}}}") - rendered = render_jinja_tmpl("{{ foo | extend_dict_key_value('bar:baz', [42, 43]) }}", - dict(foo={'bar': {'baz': [1, 2]}}, - opts=self.local_opts, - saltenv='test', - salt=self.local_salt)) + rendered = render_jinja_tmpl( + "{{ foo | extend_dict_key_value('bar:baz', [42, 43]) }}", + dict( + foo={"bar": {"baz": [1, 2]}}, + opts=self.local_opts, + saltenv="test", + salt=self.local_salt, + ), + ) self.assertEqual( rendered, - "{u'bar': {u'baz': [1, 2, 42, 43]}}" if six.PY2 - else "{'bar': {'baz': [1, 2, 42, 43]}}" + "{u'bar': {u'baz': [1, 2, 42, 43]}}" + if six.PY2 + else "{'bar': {'baz': [1, 2, 42, 43]}}", ) # Edge cases - rendered = render_jinja_tmpl("{{ {} | extend_dict_key_value('foo:bar:baz', 'quux') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + rendered = render_jinja_tmpl( + "{{ {} | extend_dict_key_value('foo:bar:baz', 'quux') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) self.assertEqual(rendered, "{'foo': {'bar': {'baz': ['q', 'u', 'u', 'x']}}}") # Beware! When supplying a dict, the list gets extended with the dict coerced to a list, # which will only contain the keys of the dict. - rendered = render_jinja_tmpl("{{ {} | extend_dict_key_value('foo:bar:baz', {'foo': 'bar'}) }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + rendered = render_jinja_tmpl( + "{{ {} | extend_dict_key_value('foo:bar:baz', {'foo': 'bar'}) }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) self.assertEqual(rendered, "{'foo': {'bar': {'baz': ['foo']}}}") # Test incorrect usage @@ -1063,403 +1129,541 @@ class TestCustomExtensions(TestCase): expected, render_jinja_tmpl, template, - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) def test_sequence(self): env = Environment() - env.filters['sequence'] = ensure_sequence_filter + env.filters["sequence"] = ensure_sequence_filter - rendered = env.from_string('{{ data | sequence | length }}') \ - .render(data='foo') - self.assertEqual(rendered, '1') + rendered = env.from_string("{{ data | sequence | length }}").render(data="foo") + self.assertEqual(rendered, "1") - rendered = env.from_string('{{ data | sequence | length }}') \ - .render(data=['foo', 'bar']) - self.assertEqual(rendered, '2') + rendered = env.from_string("{{ data | sequence | length }}").render( + data=["foo", "bar"] + ) + self.assertEqual(rendered, "2") - rendered = env.from_string('{{ data | sequence | length }}') \ - .render(data=('foo', 'bar')) - self.assertEqual(rendered, '2') + rendered = env.from_string("{{ data | sequence | length }}").render( + data=("foo", "bar") + ) + self.assertEqual(rendered, "2") - rendered = env.from_string('{{ data | sequence | length }}') \ - .render(data=set(['foo', 'bar'])) - self.assertEqual(rendered, '2') + rendered = env.from_string("{{ data | sequence | length }}").render( + data=set(["foo", "bar"]) + ) + self.assertEqual(rendered, "2") - rendered = env.from_string('{{ data | sequence | length }}') \ - .render(data={'foo': 'bar'}) - self.assertEqual(rendered, '1') + rendered = env.from_string("{{ data | sequence | length }}").render( + data={"foo": "bar"} + ) + self.assertEqual(rendered, "1") def test_camel_to_snake_case(self): - ''' + """ Test the `to_snake_case` Jinja filter. - ''' - rendered = render_jinja_tmpl('{{ \'abcdEfghhIjkLmnoP\' | to_snake_case }}', - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'abcd_efghh_ijk_lmno_p') + """ + rendered = render_jinja_tmpl( + "{{ 'abcdEfghhIjkLmnoP' | to_snake_case }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "abcd_efghh_ijk_lmno_p") def test_snake_to_camel_case(self): - ''' + """ Test the `to_camelcase` Jinja filter. - ''' - rendered = render_jinja_tmpl('{{ \'the_fox_jumped_over_the_lazy_dog\' | to_camelcase }}', - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'theFoxJumpedOverTheLazyDog') + """ + rendered = render_jinja_tmpl( + "{{ 'the_fox_jumped_over_the_lazy_dog' | to_camelcase }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "theFoxJumpedOverTheLazyDog") - rendered = render_jinja_tmpl('{{ \'the_fox_jumped_over_the_lazy_dog\' | to_camelcase(uppercamel=True) }}', - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'TheFoxJumpedOverTheLazyDog') + rendered = render_jinja_tmpl( + "{{ 'the_fox_jumped_over_the_lazy_dog' | to_camelcase(uppercamel=True) }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "TheFoxJumpedOverTheLazyDog") def test_is_ip(self): - ''' + """ Test the `is_ip` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ '192.168.0.1' | is_ip }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'True') + """ + rendered = render_jinja_tmpl( + "{{ '192.168.0.1' | is_ip }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "True") - rendered = render_jinja_tmpl("{{ 'FE80::' | is_ip }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'True') + rendered = render_jinja_tmpl( + "{{ 'FE80::' | is_ip }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "True") - rendered = render_jinja_tmpl("{{ 'random' | is_ip }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'False') + rendered = render_jinja_tmpl( + "{{ 'random' | is_ip }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "False") def test_is_ipv4(self): - ''' + """ Test the `is_ipv4` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ '192.168.0.1' | is_ipv4 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'True') + """ + rendered = render_jinja_tmpl( + "{{ '192.168.0.1' | is_ipv4 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "True") - rendered = render_jinja_tmpl("{{ 'FE80::' | is_ipv4 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'False') + rendered = render_jinja_tmpl( + "{{ 'FE80::' | is_ipv4 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "False") - rendered = render_jinja_tmpl("{{ 'random' | is_ipv4 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'False') + rendered = render_jinja_tmpl( + "{{ 'random' | is_ipv4 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "False") def test_is_ipv6(self): - ''' + """ Test the `is_ipv6` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ '192.168.0.1' | is_ipv6 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'False') + """ + rendered = render_jinja_tmpl( + "{{ '192.168.0.1' | is_ipv6 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "False") - rendered = render_jinja_tmpl("{{ 'fe80::20d:b9ff:fe01:ea8%eth0' | is_ipv6 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'True') + rendered = render_jinja_tmpl( + "{{ 'fe80::20d:b9ff:fe01:ea8%eth0' | is_ipv6 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "True") - rendered = render_jinja_tmpl("{{ 'FE80::' | is_ipv6 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'True') + rendered = render_jinja_tmpl( + "{{ 'FE80::' | is_ipv6 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "True") - rendered = render_jinja_tmpl("{{ 'random' | is_ipv6 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'False') + rendered = render_jinja_tmpl( + "{{ 'random' | is_ipv6 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "False") def test_ipaddr(self): - ''' + """ Test the `ipaddr` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ '::' | ipaddr }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '::') + """ + rendered = render_jinja_tmpl( + "{{ '::' | ipaddr }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "::") - rendered = render_jinja_tmpl("{{ '192.168.0.1' | ipaddr }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '192.168.0.1') + rendered = render_jinja_tmpl( + "{{ '192.168.0.1' | ipaddr }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "192.168.0.1") # provides a list with valid IP addresses only - rendered = render_jinja_tmpl("{{ ['192.168.0.1', '172.17.17.1', 'foo', 'bar', '::'] | ipaddr | join(', ') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '192.168.0.1, 172.17.17.1, ::') + rendered = render_jinja_tmpl( + "{{ ['192.168.0.1', '172.17.17.1', 'foo', 'bar', '::'] | ipaddr | join(', ') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "192.168.0.1, 172.17.17.1, ::") # return only multicast addresses - rendered = render_jinja_tmpl("{{ ['224.0.0.1', 'FF01::1', '::'] | ipaddr(options='multicast') | join(', ') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '224.0.0.1, ff01::1') + rendered = render_jinja_tmpl( + "{{ ['224.0.0.1', 'FF01::1', '::'] | ipaddr(options='multicast') | join(', ') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "224.0.0.1, ff01::1") def test_ipv4(self): - ''' + """ Test the `ipv4` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ '192.168.0.1' | ipv4 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '192.168.0.1') + """ + rendered = render_jinja_tmpl( + "{{ '192.168.0.1' | ipv4 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "192.168.0.1") - rendered = render_jinja_tmpl("{{ ['192.168.0.1', '172.17.17.1'] | ipv4 | join(', ')}}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '192.168.0.1, 172.17.17.1') + rendered = render_jinja_tmpl( + "{{ ['192.168.0.1', '172.17.17.1'] | ipv4 | join(', ')}}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "192.168.0.1, 172.17.17.1") - rendered = render_jinja_tmpl("{{ 'fe80::' | ipv4 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'None') + rendered = render_jinja_tmpl( + "{{ 'fe80::' | ipv4 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "None") - rendered = render_jinja_tmpl("{{ 'random' | ipv4 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'None') + rendered = render_jinja_tmpl( + "{{ 'random' | ipv4 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "None") - rendered = render_jinja_tmpl("{{ '192.168.0.1' | ipv4(options='lo') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'None') + rendered = render_jinja_tmpl( + "{{ '192.168.0.1' | ipv4(options='lo') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "None") - rendered = render_jinja_tmpl("{{ '127.0.0.1' | ipv4(options='lo') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '127.0.0.1') + rendered = render_jinja_tmpl( + "{{ '127.0.0.1' | ipv4(options='lo') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "127.0.0.1") def test_ipv6(self): - ''' + """ Test the `ipv6` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ '192.168.0.1' | ipv6 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'None') + """ + rendered = render_jinja_tmpl( + "{{ '192.168.0.1' | ipv6 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "None") - rendered = render_jinja_tmpl("{{ 'random' | ipv6 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'None') + rendered = render_jinja_tmpl( + "{{ 'random' | ipv6 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "None") # returns the standard format value - rendered = render_jinja_tmpl("{{ 'FE80:0:0::0' | ipv6 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'fe80::') + rendered = render_jinja_tmpl( + "{{ 'FE80:0:0::0' | ipv6 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "fe80::") # fe80:: is link local therefore will be returned - rendered = render_jinja_tmpl("{{ 'fe80::' | ipv6(options='ll') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'fe80::') + rendered = render_jinja_tmpl( + "{{ 'fe80::' | ipv6(options='ll') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "fe80::") # fe80:: is not loopback - rendered = render_jinja_tmpl("{{ 'fe80::' | ipv6(options='lo') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'None') + rendered = render_jinja_tmpl( + "{{ 'fe80::' | ipv6(options='lo') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "None") # returns only IPv6 addresses in the list - rendered = render_jinja_tmpl("{{ ['fe80::', '192.168.0.1'] | ipv6 | join(', ') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'fe80::') + rendered = render_jinja_tmpl( + "{{ ['fe80::', '192.168.0.1'] | ipv6 | join(', ') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "fe80::") - rendered = render_jinja_tmpl("{{ ['fe80::', '::'] | ipv6 | join(', ') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'fe80::, ::') + rendered = render_jinja_tmpl( + "{{ ['fe80::', '::'] | ipv6 | join(', ') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "fe80::, ::") def test_network_hosts(self): - ''' + """ Test the `network_hosts` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ '192.168.0.1/30' | network_hosts | join(', ') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '192.168.0.1, 192.168.0.2') + """ + rendered = render_jinja_tmpl( + "{{ '192.168.0.1/30' | network_hosts | join(', ') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "192.168.0.1, 192.168.0.2") def test_network_size(self): - ''' + """ Test the `network_size` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ '192.168.0.1' | network_size }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '1') + """ + rendered = render_jinja_tmpl( + "{{ '192.168.0.1' | network_size }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "1") - rendered = render_jinja_tmpl("{{ '192.168.0.1/8' | network_size }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '16777216') + rendered = render_jinja_tmpl( + "{{ '192.168.0.1/8' | network_size }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "16777216") @flaky def test_http_query(self): - ''' + """ Test the `http_query` Jinja filter. - ''' - for backend in ('requests', 'tornado', 'urllib2'): - rendered = render_jinja_tmpl("{{ 'http://icanhazip.com' | http_query(backend='" + backend + "') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertIsInstance(rendered, six.text_type, 'Failed with backend: {}'.format(backend)) + """ + for backend in ("requests", "tornado", "urllib2"): + rendered = render_jinja_tmpl( + "{{ 'http://icanhazip.com' | http_query(backend='" + backend + "') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertIsInstance( + rendered, six.text_type, "Failed with backend: {}".format(backend) + ) dict_reply = ast.literal_eval(rendered) - self.assertIsInstance(dict_reply, dict, 'Failed with backend: {}'.format(backend)) - self.assertIsInstance(dict_reply['body'], six.string_types, 'Failed with backend: {}'.format(backend)) + self.assertIsInstance( + dict_reply, dict, "Failed with backend: {}".format(backend) + ) + self.assertIsInstance( + dict_reply["body"], + six.string_types, + "Failed with backend: {}".format(backend), + ) def test_to_bool(self): - ''' + """ Test the `to_bool` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 1 | to_bool }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'True') + """ + rendered = render_jinja_tmpl( + "{{ 1 | to_bool }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "True") - rendered = render_jinja_tmpl("{{ 'True' | to_bool }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'True') + rendered = render_jinja_tmpl( + "{{ 'True' | to_bool }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "True") - rendered = render_jinja_tmpl("{{ 0 | to_bool }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'False') + rendered = render_jinja_tmpl( + "{{ 0 | to_bool }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "False") - rendered = render_jinja_tmpl("{{ 'Yes' | to_bool }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'True') + rendered = render_jinja_tmpl( + "{{ 'Yes' | to_bool }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "True") def test_quote(self): - ''' + """ Test the `quote` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'random' | quote }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'random') + """ + rendered = render_jinja_tmpl( + "{{ 'random' | quote }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "random") def test_regex_search(self): - ''' + """ Test the `regex_search` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'abcdefabcdef' | regex_search('BC(.*)', ignorecase=True) }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, "('defabcdef',)") # because search looks only at the beginning + """ + rendered = render_jinja_tmpl( + "{{ 'abcdefabcdef' | regex_search('BC(.*)', ignorecase=True) }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual( + rendered, "('defabcdef',)" + ) # because search looks only at the beginning def test_regex_match(self): - ''' + """ Test the `regex_match` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'abcdefabcdef' | regex_match('BC(.*)', ignorecase=True)}}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) + """ + rendered = render_jinja_tmpl( + "{{ 'abcdefabcdef' | regex_match('BC(.*)', ignorecase=True)}}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) self.assertEqual(rendered, "None") def test_regex_replace(self): - ''' + """ Test the `regex_replace` Jinja filter. - ''' - rendered = render_jinja_tmpl(r"{{ 'lets replace spaces' | regex_replace('\s+', '__') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'lets__replace__spaces') + """ + rendered = render_jinja_tmpl( + r"{{ 'lets replace spaces' | regex_replace('\s+', '__') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "lets__replace__spaces") def test_uuid(self): - ''' + """ Test the `uuid` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'random' | uuid }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '3652b285-26ad-588e-a5dc-c2ee65edc804') + """ + rendered = render_jinja_tmpl( + "{{ 'random' | uuid }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "3652b285-26ad-588e-a5dc-c2ee65edc804") def test_min(self): - ''' + """ Test the `min` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ [1, 2, 3] | min }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '1') + """ + rendered = render_jinja_tmpl( + "{{ [1, 2, 3] | min }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "1") def test_max(self): - ''' + """ Test the `max` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ [1, 2, 3] | max }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '3') + """ + rendered = render_jinja_tmpl( + "{{ [1, 2, 3] | max }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "3") def test_avg(self): - ''' + """ Test the `avg` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ [1, 2, 3] | avg }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '2.0') + """ + rendered = render_jinja_tmpl( + "{{ [1, 2, 3] | avg }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "2.0") def test_union(self): - ''' + """ Test the `union` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ [1, 2, 3] | union([2, 3, 4]) | join(', ') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '1, 2, 3, 4') + """ + rendered = render_jinja_tmpl( + "{{ [1, 2, 3] | union([2, 3, 4]) | join(', ') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "1, 2, 3, 4") def test_intersect(self): - ''' + """ Test the `intersect` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ [1, 2, 3] | intersect([2, 3, 4]) | join(', ') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '2, 3') + """ + rendered = render_jinja_tmpl( + "{{ [1, 2, 3] | intersect([2, 3, 4]) | join(', ') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "2, 3") def test_difference(self): - ''' + """ Test the `difference` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ [1, 2, 3] | difference([2, 3, 4]) | join(', ') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '1') + """ + rendered = render_jinja_tmpl( + "{{ [1, 2, 3] | difference([2, 3, 4]) | join(', ') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "1") def test_symmetric_difference(self): - ''' + """ Test the `symmetric_difference` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ [1, 2, 3] | symmetric_difference([2, 3, 4]) | join(', ') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '1, 4') + """ + rendered = render_jinja_tmpl( + "{{ [1, 2, 3] | symmetric_difference([2, 3, 4]) | join(', ') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "1, 4") def test_md5(self): - ''' + """ Test the `md5` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'random' | md5 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, '7ddf32e17a6ac5ce04a8ecbf782ca509') + """ + rendered = render_jinja_tmpl( + "{{ 'random' | md5 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "7ddf32e17a6ac5ce04a8ecbf782ca509") def test_sha256(self): - ''' + """ Test the `sha256` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'random' | sha256 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'a441b15fe9a3cf56661190a0b93b9dec7d04127288cc87250967cf3b52894d11') + """ + rendered = render_jinja_tmpl( + "{{ 'random' | sha256 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual( + rendered, "a441b15fe9a3cf56661190a0b93b9dec7d04127288cc87250967cf3b52894d11" + ) def test_sha512(self): - ''' + """ Test the `sha512` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'random' | sha512 }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, six.text_type(('811a90e1c8e86c7b4c0eef5b2c0bf0ec1b19c4b1b5a242e6455be93787cb473cb7bc' - '9b0fdeb960d00d5c6881c2094dd63c5c900ce9057255e2a4e271fc25fef1'))) + """ + rendered = render_jinja_tmpl( + "{{ 'random' | sha512 }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual( + rendered, + six.text_type( + ( + "811a90e1c8e86c7b4c0eef5b2c0bf0ec1b19c4b1b5a242e6455be93787cb473cb7bc" + "9b0fdeb960d00d5c6881c2094dd63c5c900ce9057255e2a4e271fc25fef1" + ) + ), + ) def test_hmac(self): - ''' + """ Test the `hmac` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'random' | hmac('secret', 'blah') }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'False') + """ + rendered = render_jinja_tmpl( + "{{ 'random' | hmac('secret', 'blah') }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "False") - rendered = render_jinja_tmpl(("{{ 'get salted' | " - "hmac('shared secret', 'eBWf9bstXg+NiP5AOwppB5HMvZiYMPzEM9W5YMm/AmQ=') }}"), - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'True') + rendered = render_jinja_tmpl( + ( + "{{ 'get salted' | " + "hmac('shared secret', 'eBWf9bstXg+NiP5AOwppB5HMvZiYMPzEM9W5YMm/AmQ=') }}" + ), + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "True") def test_base64_encode(self): - ''' + """ Test the `base64_encode` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'random' | base64_encode }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'cmFuZG9t') + """ + rendered = render_jinja_tmpl( + "{{ 'random' | base64_encode }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "cmFuZG9t") def test_base64_decode(self): - ''' + """ Test the `base64_decode` Jinja filter. - ''' - rendered = render_jinja_tmpl("{{ 'cmFuZG9t' | base64_decode }}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt)) - self.assertEqual(rendered, 'random') + """ + rendered = render_jinja_tmpl( + "{{ 'cmFuZG9t' | base64_decode }}", + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), + ) + self.assertEqual(rendered, "random") def test_json_query(self): - ''' + """ Test the `json_query` Jinja filter. - ''' + """ rendered = render_jinja_tmpl( "{{ [1, 2, 3] | json_query('[1]')}}", - dict(opts=self.local_opts, saltenv='test', salt=self.local_salt) + dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) - self.assertEqual(rendered, '2') + self.assertEqual(rendered, "2") # def test_print(self): # env = Environment(extensions=[SerializerExtension]) @@ -1474,51 +1678,54 @@ class TestCustomExtensions(TestCase): class TestDotNotationLookup(ModuleCase): - ''' + """ Tests to call Salt functions via Jinja with various lookup syntaxes - ''' + """ + def setUp(self): functions = { - 'mocktest.ping': lambda: True, - 'mockgrains.get': lambda x: 'jerry', + "mocktest.ping": lambda: True, + "mockgrains.get": lambda x: "jerry", } - minion_opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) + minion_opts = salt.config.minion_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion") + ) render = salt.loader.render(minion_opts, functions) - self.jinja = render.get('jinja') + self.jinja = render.get("jinja") def tearDown(self): del self.jinja def render(self, tmpl_str, context=None): - return self.jinja(tmpl_str, context=context or {}, from_str=True).read() + return self.jinja(tmpl_str, context=context or {}, argline="-s").read() def test_normlookup(self): - ''' + """ Sanity-check the normal dictionary-lookup syntax for our stub function - ''' - tmpl_str = '''Hello, {{ salt['mocktest.ping']() }}.''' + """ + tmpl_str = """Hello, {{ salt['mocktest.ping']() }}.""" - with patch.object(SaltCacheLoader, 'file_client', Mock()): + with patch.object(SaltCacheLoader, "file_client", Mock()): ret = self.render(tmpl_str) - self.assertEqual(ret, 'Hello, True.') + self.assertEqual(ret, "Hello, True.") def test_dotlookup(self): - ''' + """ Check calling a stub function using awesome dot-notation - ''' - tmpl_str = '''Hello, {{ salt.mocktest.ping() }}.''' + """ + tmpl_str = """Hello, {{ salt.mocktest.ping() }}.""" - with patch.object(SaltCacheLoader, 'file_client', Mock()): + with patch.object(SaltCacheLoader, "file_client", Mock()): ret = self.render(tmpl_str) - self.assertEqual(ret, 'Hello, True.') + self.assertEqual(ret, "Hello, True.") def test_shadowed_dict_method(self): - ''' + """ Check calling a stub function with a name that shadows a ``dict`` method name - ''' - tmpl_str = '''Hello, {{ salt.mockgrains.get('id') }}.''' + """ + tmpl_str = """Hello, {{ salt.mockgrains.get('id') }}.""" - with patch.object(SaltCacheLoader, 'file_client', Mock()): + with patch.object(SaltCacheLoader, "file_client", Mock()): ret = self.render(tmpl_str) - self.assertEqual(ret, 'Hello, jerry.') + self.assertEqual(ret, "Hello, jerry.") diff --git a/tests/unit/utils/test_json.py b/tests/unit/utils/test_json.py index 7bb428a110a..37f708098c7 100644 --- a/tests/unit/utils/test_json.py +++ b/tests/unit/utils/test_json.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.utils.json -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import textwrap -# Import Salt Testing libs -from tests.support.helpers import with_tempfile -from tests.support.mock import patch, MagicMock -from tests.support.unit import TestCase, LOREM_IPSUM, skipIf +import textwrap # Import Salt libs import salt.utils.files @@ -18,22 +14,29 @@ import salt.utils.platform import salt.utils.stringutils from salt.ext import six +# Import Salt Testing libs +from tests.support.helpers import with_tempfile +from tests.support.mock import MagicMock, patch +from tests.support.unit import LOREM_IPSUM, TestCase, skipIf + class JSONTestCase(TestCase): data = { - 'спам': 'яйца', - 'list': [1, 2, 'three'], - 'dict': {'subdict': {'спам': 'яйца'}}, - 'True': False, - 'float': 1.5, - 'None': None, + "спам": "яйца", + "list": [1, 2, "three"], + "dict": {"subdict": {"спам": "яйца"}}, + "True": False, + "float": 1.5, + "None": None, } serialized = salt.utils.stringutils.to_str( '{"None": null, "True": false, "dict": {"subdict": {"спам": "яйца"}}, "float": 1.5, "list": [1, 2, "three"], "спам": "яйца"}' ) - serialized_indent4 = salt.utils.stringutils.to_str(textwrap.dedent('''\ + serialized_indent4 = salt.utils.stringutils.to_str( + textwrap.dedent( + """\ { "None": null, "True": false, @@ -49,10 +52,12 @@ class JSONTestCase(TestCase): "three" ], "спам": "яйца" - }''')) + }""" + ) + ) def test_find_json(self): - test_sample_json = ''' + test_sample_json = """ { "glossary": { "title": "example glossary", @@ -75,44 +80,63 @@ class JSONTestCase(TestCase): } } } - ''' - expected_ret = {'glossary': {'GlossDiv': {'GlossList': {'GlossEntry': { - 'GlossDef': {'GlossSeeAlso': ['GML', 'XML'], - 'para': 'A meta-markup language, used to create markup languages such as DocBook.'}, - 'GlossSee': 'markup', 'Acronym': 'SGML', 'GlossTerm': 'Standard Generalized Markup Language', - 'SortAs': 'SGML', - 'Abbrev': 'ISO 8879:1986', 'ID': 'SGML'}}, 'title': 'S'}, 'title': 'example glossary'}} + """ + expected_ret = { + "glossary": { + "GlossDiv": { + "GlossList": { + "GlossEntry": { + "GlossDef": { + "GlossSeeAlso": ["GML", "XML"], + "para": "A meta-markup language, used to create markup languages such as DocBook.", + }, + "GlossSee": "markup", + "Acronym": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "SortAs": "SGML", + "Abbrev": "ISO 8879:1986", + "ID": "SGML", + } + }, + "title": "S", + }, + "title": "example glossary", + } + } # First test the valid JSON ret = salt.utils.json.find_json(test_sample_json) self.assertDictEqual(ret, expected_ret) # Now pre-pend some garbage and re-test - garbage_prepend_json = '{0}{1}'.format(LOREM_IPSUM, test_sample_json) + garbage_prepend_json = "{0}{1}".format(LOREM_IPSUM, test_sample_json) ret = salt.utils.json.find_json(garbage_prepend_json) self.assertDictEqual(ret, expected_ret) # Test to see if a ValueError is raised if no JSON is passed in self.assertRaises(ValueError, salt.utils.json.find_json, LOREM_IPSUM) - @skipIf(salt.utils.platform.is_windows(), 'skip until we figure out what to do about decoding unicode on windows') - @skipIf(not six.PY2, 'Test only needed on Python 2') + @skipIf( + salt.utils.platform.is_windows(), + "skip until we figure out what to do about decoding unicode on windows", + ) + @skipIf(not six.PY2, "Test only needed on Python 2") def test_find_json_unicode_splitlines(self): - ''' + """ Tests a case in salt-ssh where a unicode string is split into a list of str types by .splitlines(). - ''' + """ raw = '{"foo": "öäü"}' - mock_split = MagicMock(return_value=[raw.encode('utf8')]) + mock_split = MagicMock(return_value=[raw.encode("utf8")]) - with patch.object(salt.utils.json, '__split', mock_split): + with patch.object(salt.utils.json, "__split", mock_split): ret = salt.utils.json.find_json(raw) - self.assertEqual(ret, {'foo': 'öäü'}) + self.assertEqual(ret, {"foo": "öäü"}) def test_dumps_loads(self): - ''' + """ Test dumping to and loading from a string - ''' + """ # Dump with no indentation ret = salt.utils.json.dumps(self.data, sort_keys=True) # Make sure the result is as expected @@ -126,19 +150,21 @@ class JSONTestCase(TestCase): # results in trailing whitespace on lines ending in a comma. So, for a # proper comparison, we will have to run rstrip on each line of the # return and then stitch it back together. - ret = str('\n').join([x.rstrip() for x in ret.splitlines()]) # future lint: disable=blacklisted-function + ret = str("\n").join( + [x.rstrip() for x in ret.splitlines()] + ) # future lint: disable=blacklisted-function self.assertEqual(ret, self.serialized_indent4) # Loading it should be equal to the original data self.assertEqual(salt.utils.json.loads(ret), self.data) @with_tempfile() def test_dump_load(self, json_out): - ''' + """ Test dumping to and loading from a file handle - ''' - with salt.utils.files.fopen(json_out, 'wb') as fp_: + """ + with salt.utils.files.fopen(json_out, "wb") as fp_: fp_.write(salt.utils.stringutils.to_bytes(salt.utils.json.dumps(self.data))) - with salt.utils.files.fopen(json_out, 'rb') as fp_: + with salt.utils.files.fopen(json_out, "rb") as fp_: ret = salt.utils.json.loads(salt.utils.stringutils.to_unicode(fp_.read())) # Loading should be equal to the original data self.assertEqual(ret, self.data) diff --git a/tests/unit/utils/test_listdiffer.py b/tests/unit/utils/test_listdiffer.py index 18be240d38f..d1aea969b4a 100644 --- a/tests/unit/utils/test_listdiffer.py +++ b/tests/unit/utils/test_listdiffer.py @@ -3,29 +3,33 @@ # Import python libs from __future__ import absolute_import, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase +from salt.utils import dictdiffer # Import Salt libs from salt.utils.listdiffer import list_diff -from salt.utils import dictdiffer +# Import Salt Testing libs +from tests.support.unit import TestCase + NONE = dictdiffer.RecursiveDictDiffer.NONE_VALUE class ListDictDifferTestCase(TestCase): - def setUp(self): - old_list = [{'key': 1, 'value': 'foo1', 'int_value': 101}, - {'key': 2, 'value': 'foo2', 'int_value': 102}, - {'key': 3, 'value': 'foo3', 'int_value': 103}] - new_list = [{'key': 1, 'value': 'foo1', 'int_value': 101}, - {'key': 2, 'value': 'foo2', 'int_value': 112}, - {'key': 5, 'value': 'foo5', 'int_value': 105}] - self.list_diff = list_diff(old_list, new_list, key='key') + old_list = [ + {"key": 1, "value": "foo1", "int_value": 101}, + {"key": 2, "value": "foo2", "int_value": 102}, + {"key": 3, "value": "foo3", "int_value": 103}, + ] + new_list = [ + {"key": 1, "value": "foo1", "int_value": 101}, + {"key": 2, "value": "foo2", "int_value": 112}, + {"key": 5, "value": "foo5", "int_value": 105}, + ] + self.list_diff = list_diff(old_list, new_list, key="key") def tearDown(self): - for attrname in ('list_diff',): + for attrname in ("list_diff",): try: delattr(self, attrname) except AttributeError: @@ -33,57 +37,82 @@ class ListDictDifferTestCase(TestCase): def test_added(self): self.assertEqual(len(self.list_diff.added), 1) - self.assertDictEqual(self.list_diff.added[0], - {'key': 5, 'value': 'foo5', 'int_value': 105}) + self.assertDictEqual( + self.list_diff.added[0], {"key": 5, "value": "foo5", "int_value": 105} + ) def test_removed(self): self.assertEqual(len(self.list_diff.removed), 1) - self.assertDictEqual(self.list_diff.removed[0], - {'key': 3, 'value': 'foo3', 'int_value': 103}) + self.assertDictEqual( + self.list_diff.removed[0], {"key": 3, "value": "foo3", "int_value": 103} + ) def test_diffs(self): self.assertEqual(len(self.list_diff.diffs), 3) - self.assertDictEqual(self.list_diff.diffs[0], - {2: {'int_value': {'new': 112, 'old': 102}}}) - self.assertDictEqual(self.list_diff.diffs[1], - # Added items - {5: {'int_value': {'new': 105, 'old': NONE}, - 'key': {'new': 5, 'old': NONE}, - 'value': {'new': 'foo5', 'old': NONE}}}) - self.assertDictEqual(self.list_diff.diffs[2], - # Removed items - {3: {'int_value': {'new': NONE, 'old': 103}, - 'key': {'new': NONE, 'old': 3}, - 'value': {'new': NONE, 'old': 'foo3'}}}) + self.assertDictEqual( + self.list_diff.diffs[0], {2: {"int_value": {"new": 112, "old": 102}}} + ) + self.assertDictEqual( + self.list_diff.diffs[1], + # Added items + { + 5: { + "int_value": {"new": 105, "old": NONE}, + "key": {"new": 5, "old": NONE}, + "value": {"new": "foo5", "old": NONE}, + } + }, + ) + self.assertDictEqual( + self.list_diff.diffs[2], + # Removed items + { + 3: { + "int_value": {"new": NONE, "old": 103}, + "key": {"new": NONE, "old": 3}, + "value": {"new": NONE, "old": "foo3"}, + } + }, + ) def test_new_values(self): self.assertEqual(len(self.list_diff.new_values), 2) - self.assertDictEqual(self.list_diff.new_values[0], - {'key': 2, 'int_value': 112}) - self.assertDictEqual(self.list_diff.new_values[1], - {'key': 5, 'value': 'foo5', 'int_value': 105}) + self.assertDictEqual(self.list_diff.new_values[0], {"key": 2, "int_value": 112}) + self.assertDictEqual( + self.list_diff.new_values[1], {"key": 5, "value": "foo5", "int_value": 105} + ) def test_old_values(self): self.assertEqual(len(self.list_diff.old_values), 2) - self.assertDictEqual(self.list_diff.old_values[0], - {'key': 2, 'int_value': 102}) - self.assertDictEqual(self.list_diff.old_values[1], - {'key': 3, 'value': 'foo3', 'int_value': 103}) + self.assertDictEqual(self.list_diff.old_values[0], {"key": 2, "int_value": 102}) + self.assertDictEqual( + self.list_diff.old_values[1], {"key": 3, "value": "foo3", "int_value": 103} + ) def test_changed_all(self): - self.assertEqual(self.list_diff.changed(selection='all'), - ['key.2.int_value', 'key.5.int_value', 'key.5.value', - 'key.3.int_value', 'key.3.value']) + self.assertEqual( + self.list_diff.changed(selection="all"), + [ + "key.2.int_value", + "key.5.int_value", + "key.5.value", + "key.3.int_value", + "key.3.value", + ], + ) def test_changed_intersect(self): - self.assertEqual(self.list_diff.changed(selection='intersect'), - ['key.2.int_value']) + self.assertEqual( + self.list_diff.changed(selection="intersect"), ["key.2.int_value"] + ) def test_changes_str(self): - self.assertEqual(self.list_diff.changes_str, - '\tidentified by key 2:\n' - '\tint_value from 102 to 112\n' - '\tidentified by key 3:\n' - '\twill be removed\n' - '\tidentified by key 5:\n' - '\twill be added\n') + self.assertEqual( + self.list_diff.changes_str, + "\tidentified by key 2:\n" + "\tint_value from 102 to 112\n" + "\tidentified by key 3:\n" + "\twill be removed\n" + "\tidentified by key 5:\n" + "\twill be added\n", + ) diff --git a/tests/unit/utils/test_locales.py b/tests/unit/utils/test_locales.py index 627e3162b01..3d4d81310df 100644 --- a/tests/unit/utils/test_locales.py +++ b/tests/unit/utils/test_locales.py @@ -3,14 +3,13 @@ # Import Python libs from __future__ import absolute_import, unicode_literals - # Import Salt libs import salt.utils.locales as locales -from tests.support.unit import TestCase -from tests.support.mock import patch # Import 3rd-part libs from salt.ext.six.moves import reload_module +from tests.support.mock import patch +from tests.support.unit import TestCase class TestLocales(TestCase): @@ -18,32 +17,40 @@ class TestLocales(TestCase): # reload locales modules before and after to defeat memoization of # get_encodings() reload_module(locales) - with patch('sys.getdefaultencoding', return_value='xyzzy'): + with patch("sys.getdefaultencoding", return_value="xyzzy"): encodings = locales.get_encodings() - for enc in (__salt_system_encoding__, 'xyzzy', 'utf-8', 'latin-1'): + for enc in (__salt_system_encoding__, "xyzzy", "utf-8", "latin-1"): self.assertIn(enc, encodings) reload_module(locales) def test_split_locale(self): self.assertDictEqual( - locales.split_locale('ca_ES.UTF-8@valencia utf-8'), - {'charmap': 'utf-8', - 'modifier': 'valencia', - 'codeset': 'UTF-8', - 'language': 'ca', - 'territory': 'ES'}) + locales.split_locale("ca_ES.UTF-8@valencia utf-8"), + { + "charmap": "utf-8", + "modifier": "valencia", + "codeset": "UTF-8", + "language": "ca", + "territory": "ES", + }, + ) def test_join_locale(self): self.assertEqual( - locales.join_locale( - {'charmap': 'utf-8', - 'modifier': 'valencia', - 'codeset': 'UTF-8', - 'language': 'ca', - 'territory': 'ES'}), - 'ca_ES.UTF-8@valencia utf-8') + locales.join_locale( + { + "charmap": "utf-8", + "modifier": "valencia", + "codeset": "UTF-8", + "language": "ca", + "territory": "ES", + } + ), + "ca_ES.UTF-8@valencia utf-8", + ) def test_normalize_locale(self): self.assertEqual( - locales.normalize_locale('ca_es.UTF-8@valencia utf-8'), - 'ca_ES.utf8@valencia') + locales.normalize_locale("ca_es.UTF-8@valencia utf-8"), + "ca_ES.utf8@valencia", + ) diff --git a/tests/unit/utils/test_mac_utils.py b/tests/unit/utils/test_mac_utils.py index 955999512d9..955af5b8746 100644 --- a/tests/unit/utils/test_mac_utils.py +++ b/tests/unit/utils/test_mac_utils.py @@ -1,336 +1,339 @@ # -*- coding: utf-8 -*- -''' +""" mac_utils tests -''' +""" # Import python libs from __future__ import absolute_import, unicode_literals + +import os import plistlib import xml.parsers.expat -import os - -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - call, - MagicMock, - mock_open, - patch -) -from tests.support.mixins import LoaderModuleMockMixin # Import Salt libs import salt.utils.mac_utils as mac_utils import salt.utils.platform -from salt.exceptions import SaltInvocationError, CommandExecutionError +from salt.exceptions import CommandExecutionError, SaltInvocationError +from salt.ext import six # Import 3rd-party libs from salt.ext.six.moves import range -from salt.ext import six +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, call, mock_open, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf -@skipIf(not salt.utils.platform.is_darwin(), 'These tests run only on mac') +@skipIf(not salt.utils.platform.is_darwin(), "These tests run only on mac") class MacUtilsTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ test mac_utils salt utility - ''' + """ + def setup_loader_modules(self): return {mac_utils: {}} def test_execute_return_success_not_supported(self): - ''' + """ test execute_return_success function command not supported - ''' - mock_cmd = MagicMock(return_value={'retcode': 0, - 'stdout': 'not supported', - 'stderr': 'error'}) - with patch.object(mac_utils, '_run_all', mock_cmd): - self.assertRaises(CommandExecutionError, - mac_utils.execute_return_success, - 'dir c:\\') + """ + mock_cmd = MagicMock( + return_value={"retcode": 0, "stdout": "not supported", "stderr": "error"} + ) + with patch.object(mac_utils, "_run_all", mock_cmd): + self.assertRaises( + CommandExecutionError, mac_utils.execute_return_success, "dir c:\\" + ) def test_execute_return_success_command_failed(self): - ''' + """ test execute_return_success function command failed - ''' - mock_cmd = MagicMock(return_value={'retcode': 1, - 'stdout': 'spongebob', - 'stderr': 'error'}) - with patch.object(mac_utils, '_run_all', mock_cmd): - self.assertRaises(CommandExecutionError, - mac_utils.execute_return_success, - 'dir c:\\') + """ + mock_cmd = MagicMock( + return_value={"retcode": 1, "stdout": "spongebob", "stderr": "error"} + ) + with patch.object(mac_utils, "_run_all", mock_cmd): + self.assertRaises( + CommandExecutionError, mac_utils.execute_return_success, "dir c:\\" + ) def test_execute_return_success_command_succeeded(self): - ''' + """ test execute_return_success function command succeeded - ''' - mock_cmd = MagicMock(return_value={'retcode': 0, - 'stdout': 'spongebob'}) - with patch.object(mac_utils, '_run_all', mock_cmd): - ret = mac_utils.execute_return_success('dir c:\\') + """ + mock_cmd = MagicMock(return_value={"retcode": 0, "stdout": "spongebob"}) + with patch.object(mac_utils, "_run_all", mock_cmd): + ret = mac_utils.execute_return_success("dir c:\\") self.assertEqual(ret, True) def test_execute_return_result_command_failed(self): - ''' + """ test execute_return_result function command failed - ''' - mock_cmd = MagicMock(return_value={'retcode': 1, - 'stdout': 'spongebob', - 'stderr': 'squarepants'}) - with patch.object(mac_utils, '_run_all', mock_cmd): - self.assertRaises(CommandExecutionError, - mac_utils.execute_return_result, - 'dir c:\\') + """ + mock_cmd = MagicMock( + return_value={"retcode": 1, "stdout": "spongebob", "stderr": "squarepants"} + ) + with patch.object(mac_utils, "_run_all", mock_cmd): + self.assertRaises( + CommandExecutionError, mac_utils.execute_return_result, "dir c:\\" + ) def test_execute_return_result_command_succeeded(self): - ''' + """ test execute_return_result function command succeeded - ''' - mock_cmd = MagicMock(return_value={'retcode': 0, - 'stdout': 'spongebob'}) - with patch.object(mac_utils, '_run_all', mock_cmd): - ret = mac_utils.execute_return_result('dir c:\\') - self.assertEqual(ret, 'spongebob') + """ + mock_cmd = MagicMock(return_value={"retcode": 0, "stdout": "spongebob"}) + with patch.object(mac_utils, "_run_all", mock_cmd): + ret = mac_utils.execute_return_result("dir c:\\") + self.assertEqual(ret, "spongebob") def test_parse_return_space(self): - ''' + """ test parse_return function space after colon - ''' - self.assertEqual(mac_utils.parse_return('spongebob: squarepants'), - 'squarepants') + """ + self.assertEqual( + mac_utils.parse_return("spongebob: squarepants"), "squarepants" + ) def test_parse_return_new_line(self): - ''' + """ test parse_return function new line after colon - ''' - self.assertEqual(mac_utils.parse_return('spongebob:\nsquarepants'), - 'squarepants') + """ + self.assertEqual( + mac_utils.parse_return("spongebob:\nsquarepants"), "squarepants" + ) def test_parse_return_no_delimiter(self): - ''' + """ test parse_return function no delimiter - ''' - self.assertEqual(mac_utils.parse_return('squarepants'), - 'squarepants') + """ + self.assertEqual(mac_utils.parse_return("squarepants"), "squarepants") def test_validate_enabled_on(self): - ''' + """ test validate_enabled function test on - ''' - self.assertEqual(mac_utils.validate_enabled('On'), - 'on') + """ + self.assertEqual(mac_utils.validate_enabled("On"), "on") def test_validate_enabled_off(self): - ''' + """ test validate_enabled function test off - ''' - self.assertEqual(mac_utils.validate_enabled('Off'), - 'off') + """ + self.assertEqual(mac_utils.validate_enabled("Off"), "off") def test_validate_enabled_bad_string(self): - ''' + """ test validate_enabled function test bad string - ''' - self.assertRaises(SaltInvocationError, - mac_utils.validate_enabled, - 'bad string') + """ + self.assertRaises(SaltInvocationError, mac_utils.validate_enabled, "bad string") def test_validate_enabled_non_zero(self): - ''' + """ test validate_enabled function test non zero - ''' + """ for x in range(1, 179, 3): - self.assertEqual(mac_utils.validate_enabled(x), - 'on') + self.assertEqual(mac_utils.validate_enabled(x), "on") def test_validate_enabled_0(self): - ''' + """ test validate_enabled function test 0 - ''' - self.assertEqual(mac_utils.validate_enabled(0), - 'off') + """ + self.assertEqual(mac_utils.validate_enabled(0), "off") def test_validate_enabled_true(self): - ''' + """ test validate_enabled function test True - ''' - self.assertEqual(mac_utils.validate_enabled(True), - 'on') + """ + self.assertEqual(mac_utils.validate_enabled(True), "on") def test_validate_enabled_false(self): - ''' + """ test validate_enabled function test False - ''' - self.assertEqual(mac_utils.validate_enabled(False), - 'off') + """ + self.assertEqual(mac_utils.validate_enabled(False), "off") def test_launchctl(self): - ''' + """ test launchctl function - ''' - mock_cmd = MagicMock(return_value={'retcode': 0, - 'stdout': 'success', - 'stderr': 'none'}) - with patch('salt.utils.mac_utils.__salt__', {'cmd.run_all': mock_cmd}): - ret = mac_utils.launchctl('enable', 'org.salt.minion') + """ + mock_cmd = MagicMock( + return_value={"retcode": 0, "stdout": "success", "stderr": "none"} + ) + with patch("salt.utils.mac_utils.__salt__", {"cmd.run_all": mock_cmd}): + ret = mac_utils.launchctl("enable", "org.salt.minion") self.assertEqual(ret, True) def test_launchctl_return_stdout(self): - ''' + """ test launchctl function and return stdout - ''' - mock_cmd = MagicMock(return_value={'retcode': 0, - 'stdout': 'success', - 'stderr': 'none'}) - with patch('salt.utils.mac_utils.__salt__', {'cmd.run_all': mock_cmd}): - ret = mac_utils.launchctl('enable', - 'org.salt.minion', - return_stdout=True) - self.assertEqual(ret, 'success') + """ + mock_cmd = MagicMock( + return_value={"retcode": 0, "stdout": "success", "stderr": "none"} + ) + with patch("salt.utils.mac_utils.__salt__", {"cmd.run_all": mock_cmd}): + ret = mac_utils.launchctl("enable", "org.salt.minion", return_stdout=True) + self.assertEqual(ret, "success") def test_launchctl_error(self): - ''' + """ test launchctl function returning an error - ''' - mock_cmd = MagicMock(return_value={'retcode': 1, - 'stdout': 'failure', - 'stderr': 'test failure'}) - error = 'Failed to enable service:\n' \ - 'stdout: failure\n' \ - 'stderr: test failure\n' \ - 'retcode: 1' - with patch('salt.utils.mac_utils.__salt__', {'cmd.run_all': mock_cmd}): + """ + mock_cmd = MagicMock( + return_value={"retcode": 1, "stdout": "failure", "stderr": "test failure"} + ) + error = ( + "Failed to enable service:\n" + "stdout: failure\n" + "stderr: test failure\n" + "retcode: 1" + ) + with patch("salt.utils.mac_utils.__salt__", {"cmd.run_all": mock_cmd}): try: - mac_utils.launchctl('enable', 'org.salt.minion') + mac_utils.launchctl("enable", "org.salt.minion") except CommandExecutionError as exc: self.assertEqual(exc.message, error) - @patch('salt.utils.path.os_walk') - @patch('os.path.exists') + @patch("salt.utils.path.os_walk") + @patch("os.path.exists") def test_available_services_result(self, mock_exists, mock_os_walk): - ''' + """ test available_services results are properly formed dicts. - ''' - results = {'/Library/LaunchAgents': ['com.apple.lla1.plist']} + """ + results = {"/Library/LaunchAgents": ["com.apple.lla1.plist"]} mock_os_walk.side_effect = _get_walk_side_effects(results) mock_exists.return_value = True - plists = [{'Label': 'com.apple.lla1'}] + plists = [{"Label": "com.apple.lla1"}] ret = _run_available_services(plists) - file_path = os.sep + os.path.join('Library', 'LaunchAgents', 'com.apple.lla1.plist') + file_path = os.sep + os.path.join( + "Library", "LaunchAgents", "com.apple.lla1.plist" + ) if salt.utils.platform.is_windows(): - file_path = 'c:' + file_path + file_path = "c:" + file_path expected = { - 'com.apple.lla1': { - 'file_name': 'com.apple.lla1.plist', - 'file_path': file_path, - 'plist': plists[0]}} + "com.apple.lla1": { + "file_name": "com.apple.lla1.plist", + "file_path": file_path, + "plist": plists[0], + } + } self.assertEqual(ret, expected) - @patch('salt.utils.path.os_walk') - @patch('os.path.exists') - @patch('os.listdir') - @patch('os.path.isdir') - def test_available_services_dirs(self, - mock_isdir, - mock_listdir, - mock_exists, - mock_os_walk): - ''' + @patch("salt.utils.path.os_walk") + @patch("os.path.exists") + @patch("os.listdir") + @patch("os.path.isdir") + def test_available_services_dirs( + self, mock_isdir, mock_listdir, mock_exists, mock_os_walk + ): + """ test available_services checks all of the expected dirs. - ''' + """ results = { - '/Library/LaunchAgents': ['com.apple.lla1.plist'], - '/Library/LaunchDaemons': ['com.apple.lld1.plist'], - '/System/Library/LaunchAgents': ['com.apple.slla1.plist'], - '/System/Library/LaunchDaemons': ['com.apple.slld1.plist'], - '/Users/saltymcsaltface/Library/LaunchAgents': [ - 'com.apple.uslla1.plist']} + "/Library/LaunchAgents": ["com.apple.lla1.plist"], + "/Library/LaunchDaemons": ["com.apple.lld1.plist"], + "/System/Library/LaunchAgents": ["com.apple.slla1.plist"], + "/System/Library/LaunchDaemons": ["com.apple.slld1.plist"], + "/Users/saltymcsaltface/Library/LaunchAgents": ["com.apple.uslla1.plist"], + } mock_os_walk.side_effect = _get_walk_side_effects(results) - mock_listdir.return_value = ['saltymcsaltface'] + mock_listdir.return_value = ["saltymcsaltface"] mock_isdir.return_value = True mock_exists.return_value = True plists = [ - {'Label': 'com.apple.lla1'}, - {'Label': 'com.apple.lld1'}, - {'Label': 'com.apple.slla1'}, - {'Label': 'com.apple.slld1'}, - {'Label': 'com.apple.uslla1'}] + {"Label": "com.apple.lla1"}, + {"Label": "com.apple.lld1"}, + {"Label": "com.apple.slla1"}, + {"Label": "com.apple.slld1"}, + {"Label": "com.apple.uslla1"}, + ] ret = _run_available_services(plists) self.assertEqual(len(ret), 5) - @patch('salt.utils.path.os_walk') - @patch('os.path.exists') - @patch('plistlib.readPlist' if six.PY2 else 'plistlib.load') - def test_available_services_broken_symlink(self, mock_read_plist, mock_exists, mock_os_walk): - ''' + @patch("salt.utils.path.os_walk") + @patch("os.path.exists") + @patch("plistlib.readPlist" if six.PY2 else "plistlib.load") + def test_available_services_broken_symlink( + self, mock_read_plist, mock_exists, mock_os_walk + ): + """ test available_services when it encounters a broken symlink. - ''' - results = {'/Library/LaunchAgents': ['com.apple.lla1.plist', 'com.apple.lla2.plist']} + """ + results = { + "/Library/LaunchAgents": ["com.apple.lla1.plist", "com.apple.lla2.plist"] + } mock_os_walk.side_effect = _get_walk_side_effects(results) mock_exists.side_effect = [True, False] - plists = [{'Label': 'com.apple.lla1'}] + plists = [{"Label": "com.apple.lla1"}] ret = _run_available_services(plists) - file_path = os.sep + os.path.join('Library', 'LaunchAgents', 'com.apple.lla1.plist') + file_path = os.sep + os.path.join( + "Library", "LaunchAgents", "com.apple.lla1.plist" + ) if salt.utils.platform.is_windows(): - file_path = 'c:' + file_path + file_path = "c:" + file_path expected = { - 'com.apple.lla1': { - 'file_name': 'com.apple.lla1.plist', - 'file_path': file_path, - 'plist': plists[0]}} + "com.apple.lla1": { + "file_name": "com.apple.lla1.plist", + "file_path": file_path, + "plist": plists[0], + } + } self.assertEqual(ret, expected) - @patch('salt.utils.path.os_walk') - @patch('os.path.exists') - @patch('plistlib.readPlist') - @patch('salt.utils.mac_utils.__salt__') - @patch('plistlib.readPlistFromString', create=True) - def test_available_services_binary_plist(self, - mock_read_plist_from_string, - mock_run, - mock_read_plist, - mock_exists, - mock_os_walk): - ''' + @patch("salt.utils.path.os_walk") + @patch("os.path.exists") + @patch("plistlib.readPlist") + @patch("salt.utils.mac_utils.__salt__") + @patch("plistlib.readPlistFromString", create=True) + def test_available_services_binary_plist( + self, + mock_read_plist_from_string, + mock_run, + mock_read_plist, + mock_exists, + mock_os_walk, + ): + """ test available_services handles binary plist files. - ''' - results = {'/Library/LaunchAgents': ['com.apple.lla1.plist']} + """ + results = {"/Library/LaunchAgents": ["com.apple.lla1.plist"]} mock_os_walk.side_effect = _get_walk_side_effects(results) mock_exists.return_value = True - plists = [{'Label': 'com.apple.lla1'}] + plists = [{"Label": "com.apple.lla1"}] - file_path = os.sep + os.path.join('Library', 'LaunchAgents', 'com.apple.lla1.plist') + file_path = os.sep + os.path.join( + "Library", "LaunchAgents", "com.apple.lla1.plist" + ) if salt.utils.platform.is_windows(): - file_path = 'c:' + file_path + file_path = "c:" + file_path if six.PY2: - attrs = {'cmd.run': MagicMock()} + attrs = {"cmd.run": MagicMock()} def getitem(name): return attrs[name] @@ -350,70 +353,72 @@ class MacUtilsTestCase(TestCase, LoaderModuleMockMixin): ret = _run_available_services(plists) expected = { - 'com.apple.lla1': { - 'file_name': 'com.apple.lla1.plist', - 'file_path': file_path, - 'plist': plists[0]}} + "com.apple.lla1": { + "file_name": "com.apple.lla1.plist", + "file_path": file_path, + "plist": plists[0], + } + } self.assertEqual(ret, expected) if six.PY2: mock_run.assert_has_calls(calls, any_order=True) - @patch('salt.utils.path.os_walk') - @patch('os.path.exists') + @patch("salt.utils.path.os_walk") + @patch("os.path.exists") def test_available_services_invalid_file(self, mock_exists, mock_os_walk): - ''' + """ test available_services excludes invalid files. The py3 plistlib raises an InvalidFileException when a plist file cannot be parsed. This test only asserts things for py3. - ''' + """ if six.PY3: - results = {'/Library/LaunchAgents': ['com.apple.lla1.plist']} + results = {"/Library/LaunchAgents": ["com.apple.lla1.plist"]} mock_os_walk.side_effect = _get_walk_side_effects(results) mock_exists.return_value = True - plists = [{'Label': 'com.apple.lla1'}] + plists = [{"Label": "com.apple.lla1"}] mock_load = MagicMock() mock_load.side_effect = plistlib.InvalidFileException - with patch('salt.utils.files.fopen', mock_open()): - with patch('plistlib.load', mock_load): + with patch("salt.utils.files.fopen", mock_open()): + with patch("plistlib.load", mock_load): ret = mac_utils._available_services() self.assertEqual(len(ret), 0) - @patch('salt.utils.mac_utils.__salt__') - @patch('plistlib.readPlist') - @patch('salt.utils.path.os_walk') - @patch('os.path.exists') - def test_available_services_expat_error(self, - mock_exists, - mock_os_walk, - mock_read_plist, - mock_run): - ''' + @patch("salt.utils.mac_utils.__salt__") + @patch("plistlib.readPlist") + @patch("salt.utils.path.os_walk") + @patch("os.path.exists") + def test_available_services_expat_error( + self, mock_exists, mock_os_walk, mock_read_plist, mock_run + ): + """ test available_services excludes files with expat errors. Poorly formed XML will raise an ExpatError on py2. It will also be raised by some almost-correct XML on py3. - ''' - results = {'/Library/LaunchAgents': ['com.apple.lla1.plist']} + """ + results = {"/Library/LaunchAgents": ["com.apple.lla1.plist"]} mock_os_walk.side_effect = _get_walk_side_effects(results) mock_exists.return_value = True - file_path = os.sep + os.path.join('Library', 'LaunchAgents', 'com.apple.lla1.plist') + file_path = os.sep + os.path.join( + "Library", "LaunchAgents", "com.apple.lla1.plist" + ) if salt.utils.platform.is_windows(): - file_path = 'c:' + file_path + file_path = "c:" + file_path if six.PY3: mock_load = MagicMock() mock_load.side_effect = xml.parsers.expat.ExpatError - with patch('salt.utils.files.fopen', mock_open()): - with patch('plistlib.load', mock_load): + with patch("salt.utils.files.fopen", mock_open()): + with patch("plistlib.load", mock_load): ret = mac_utils._available_services() else: - attrs = {'cmd.run': MagicMock()} + attrs = {"cmd.run": MagicMock()} def getitem(name): return attrs[name] @@ -423,11 +428,10 @@ class MacUtilsTestCase(TestCase, LoaderModuleMockMixin): cmd = '/usr/bin/plutil -convert xml1 -o - -- "{}"'.format(file_path) calls = [call.cmd.run(cmd)] - mock_raise_expat_error = MagicMock( - side_effect=xml.parsers.expat.ExpatError) + mock_raise_expat_error = MagicMock(side_effect=xml.parsers.expat.ExpatError) - with patch('plistlib.readPlist', mock_raise_expat_error): - with patch('plistlib.readPlistFromString', mock_raise_expat_error): + with patch("plistlib.readPlist", mock_raise_expat_error): + with patch("plistlib.readPlistFromString", mock_raise_expat_error): ret = mac_utils._available_services() mock_run.assert_has_calls(calls, any_order=True) @@ -436,11 +440,13 @@ class MacUtilsTestCase(TestCase, LoaderModuleMockMixin): def _get_walk_side_effects(results): - ''' + """ Data generation helper function for service tests. - ''' + """ + def walk_side_effect(*args, **kwargs): return [(args[0], [], results.get(args[0], []))] + return walk_side_effect @@ -448,12 +454,12 @@ def _run_available_services(plists): if six.PY2: mock_read_plist = MagicMock() mock_read_plist.side_effect = plists - with patch('plistlib.readPlist', mock_read_plist): + with patch("plistlib.readPlist", mock_read_plist): ret = mac_utils._available_services() else: mock_load = MagicMock() mock_load.side_effect = plists - with patch('salt.utils.files.fopen', mock_open()): - with patch('plistlib.load', mock_load): + with patch("salt.utils.files.fopen", mock_open()): + with patch("plistlib.load", mock_load): ret = mac_utils._available_services() return ret diff --git a/tests/unit/utils/test_master.py b/tests/unit/utils/test_master.py index 9374cbf0627..8e357a3fecf 100644 --- a/tests/unit/utils/test_master.py +++ b/tests/unit/utils/test_master.py @@ -5,83 +5,99 @@ from __future__ import absolute_import, unicode_literals # Import Salt Libs import salt.utils.master +from tests.support.mock import patch # Import Salt Testing Libs from tests.support.unit import TestCase -from tests.support.mock import patch class MasterPillarUtilTestCase(TestCase): - ''' + """ TestCase for salt.utils.master.MasterPillarUtil methods - ''' + """ + def test_get_minion_pillar(self): - ''' + """ test get_minion_pillar when target exists - ''' - opts = {'test': False} - minion = 'minion' - pillar = salt.utils.master.MasterPillarUtil(tgt=minion, - tgt_type='glob', - opts=opts) - grains_data = {minion: {'domain': ''}} - pillar_data = {minion: {'test_pillar': 'foo'}} + """ + opts = {"test": False} + minion = "minion" + pillar = salt.utils.master.MasterPillarUtil( + tgt=minion, tgt_type="glob", opts=opts + ) + grains_data = {minion: {"domain": ""}} + pillar_data = {minion: {"test_pillar": "foo"}} - patch_grain = patch('salt.utils.master.MasterPillarUtil._get_minion_grains', - return_value=grains_data) - patch_pillar = patch('salt.utils.master.MasterPillarUtil._get_minion_pillar', - return_value=pillar_data) - patch_tgt_list = patch('salt.utils.master.MasterPillarUtil._tgt_to_list', - return_value=[minion]) + patch_grain = patch( + "salt.utils.master.MasterPillarUtil._get_minion_grains", + return_value=grains_data, + ) + patch_pillar = patch( + "salt.utils.master.MasterPillarUtil._get_minion_pillar", + return_value=pillar_data, + ) + patch_tgt_list = patch( + "salt.utils.master.MasterPillarUtil._tgt_to_list", return_value=[minion] + ) with patch_grain, patch_pillar, patch_tgt_list: ret = pillar.get_minion_pillar() assert ret[minion] == pillar_data[minion] def test_get_minion_pillar_doesnotexist(self): - ''' + """ test get_minion_pillar when target does not exist - ''' - opts = {'test': False} - minion = 'minion' - pillar = salt.utils.master.MasterPillarUtil(tgt='doesnotexist', - tgt_type='glob', - opts=opts) - grains_data = {minion: {'domain': ''}} - pillar_data = {minion: {'test_pillar': 'foo'}} + """ + opts = {"test": False} + minion = "minion" + pillar = salt.utils.master.MasterPillarUtil( + tgt="doesnotexist", tgt_type="glob", opts=opts + ) + grains_data = {minion: {"domain": ""}} + pillar_data = {minion: {"test_pillar": "foo"}} - patch_grain = patch('salt.utils.master.MasterPillarUtil._get_minion_grains', - return_value=grains_data) - patch_pillar = patch('salt.utils.master.MasterPillarUtil._get_minion_pillar', - return_value=pillar_data) - patch_tgt_list = patch('salt.utils.master.MasterPillarUtil._tgt_to_list', - return_value=[]) + patch_grain = patch( + "salt.utils.master.MasterPillarUtil._get_minion_grains", + return_value=grains_data, + ) + patch_pillar = patch( + "salt.utils.master.MasterPillarUtil._get_minion_pillar", + return_value=pillar_data, + ) + patch_tgt_list = patch( + "salt.utils.master.MasterPillarUtil._tgt_to_list", return_value=[] + ) with patch_grain, patch_pillar, patch_tgt_list: ret = pillar.get_minion_pillar() assert minion not in ret def test_get_minion_pillar_notgt(self): - ''' + """ test get_minion_pillar when passing target None - ''' - opts = {'test': False} - minion = 'minion' - pillar = salt.utils.master.MasterPillarUtil(tgt=None, - tgt_type='glob', - opts=opts) - grains_data = {minion: {'domain': ''}} - pillar_data = {minion: {'test_pillar': 'foo'}} + """ + opts = {"test": False} + minion = "minion" + pillar = salt.utils.master.MasterPillarUtil( + tgt=None, tgt_type="glob", opts=opts + ) + grains_data = {minion: {"domain": ""}} + pillar_data = {minion: {"test_pillar": "foo"}} - patch_grain = patch('salt.utils.master.MasterPillarUtil._get_minion_grains', - return_value=grains_data) - patch_pillar = patch('salt.utils.master.MasterPillarUtil._get_minion_pillar', - return_value=pillar_data) - patch_tgt_list = patch('salt.utils.master.MasterPillarUtil._tgt_to_list', - return_value=[]) + patch_grain = patch( + "salt.utils.master.MasterPillarUtil._get_minion_grains", + return_value=grains_data, + ) + patch_pillar = patch( + "salt.utils.master.MasterPillarUtil._get_minion_pillar", + return_value=pillar_data, + ) + patch_tgt_list = patch( + "salt.utils.master.MasterPillarUtil._tgt_to_list", return_value=[] + ) with patch_grain, patch_pillar, patch_tgt_list: ret = pillar.get_minion_pillar() diff --git a/tests/unit/utils/test_minions.py b/tests/unit/utils/test_minions.py index 3901a786d25..24962d59310 100644 --- a/tests/unit/utils/test_minions.py +++ b/tests/unit/utils/test_minions.py @@ -2,48 +2,72 @@ # Import python libs from __future__ import absolute_import, unicode_literals + import sys # Import Salt Libs import salt.utils.minions +from tests.support.mock import MagicMock, patch # Import Salt Testing Libs from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, - MagicMock, -) NODEGROUPS = { - 'group1': 'L@host1,host2,host3', - 'group2': ['G@foo:bar', 'or', 'web1*'], - 'group3': ['N@group1', 'or', 'N@group2'], - 'group4': ['host4', 'host5', 'host6'], - 'group5': 'N@group4', - 'group6': 'N@group3', - 'group7': ['host1'] + "group1": "L@host1,host2,host3", + "group2": ["G@foo:bar", "or", "web1*"], + "group3": ["N@group1", "or", "N@group2"], + "group4": ["host4", "host5", "host6"], + "group5": "N@group4", + "group6": "N@group3", + "group7": ["host1"], } EXPECTED = { - 'group1': ['L@host1,host2,host3'], - 'group2': ['G@foo:bar', 'or', 'web1*'], - 'group3': ['(', '(', 'L@host1,host2,host3', ')', 'or', '(', 'G@foo:bar', 'or', 'web1*', ')', ')'], - 'group4': ['L@host4,host5,host6'], - 'group5': ['(', 'L@host4,host5,host6', ')'], - 'group6': ['(', '(', '(', 'L@host1,host2,host3', ')', 'or', '(', - 'G@foo:bar', 'or', 'web1*', ')', ')', ')'], - 'group7': ['L@host1'] + "group1": ["L@host1,host2,host3"], + "group2": ["G@foo:bar", "or", "web1*"], + "group3": [ + "(", + "(", + "L@host1,host2,host3", + ")", + "or", + "(", + "G@foo:bar", + "or", + "web1*", + ")", + ")", + ], + "group4": ["L@host4,host5,host6"], + "group5": ["(", "L@host4,host5,host6", ")"], + "group6": [ + "(", + "(", + "(", + "L@host1,host2,host3", + ")", + "or", + "(", + "G@foo:bar", + "or", + "web1*", + ")", + ")", + ")", + ], + "group7": ["L@host1"], } class MinionsTestCase(TestCase): - ''' + """ TestCase for salt.utils.minions module functions - ''' + """ + def test_nodegroup_comp(self): - ''' + """ Test a simple string nodegroup - ''' + """ for nodegroup in NODEGROUPS: expected = EXPECTED[nodegroup] ret = salt.utils.minions.nodegroup_comp(nodegroup, NODEGROUPS) @@ -51,468 +75,426 @@ class MinionsTestCase(TestCase): class CkMinionsTestCase(TestCase): - ''' + """ TestCase for salt.utils.minions.CkMinions class - ''' + """ + def setUp(self): - self.ckminions = salt.utils.minions.CkMinions({'minion_data_cache': True}) + self.ckminions = salt.utils.minions.CkMinions({"minion_data_cache": True}) def test_spec_check(self): # Test spec-only rule - auth_list = ['@runner'] - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'runner') + auth_list = ["@runner"] + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'wheel') + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "wheel") self.assertFalse(ret) - ret = self.ckminions.spec_check(auth_list, 'testarg', {}, 'runner') - mock_ret = {'error': {'name': 'SaltInvocationError', - 'message': 'A command invocation error occurred: Check syntax.'}} + ret = self.ckminions.spec_check(auth_list, "testarg", {}, "runner") + mock_ret = { + "error": { + "name": "SaltInvocationError", + "message": "A command invocation error occurred: Check syntax.", + } + } self.assertDictEqual(mock_ret, ret) # Test spec in plural form - auth_list = ['@runners'] - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'runner') + auth_list = ["@runners"] + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'wheel') + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "wheel") self.assertFalse(ret) # Test spec with module.function restriction - auth_list = [{'@runner': 'test.arg'}] - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'runner') + auth_list = [{"@runner": "test.arg"}] + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'wheel') + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "wheel") self.assertFalse(ret) - ret = self.ckminions.spec_check(auth_list, 'tes.arg', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "tes.arg", {}, "runner") self.assertFalse(ret) - ret = self.ckminions.spec_check(auth_list, 'test.ar', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "test.ar", {}, "runner") self.assertFalse(ret) # Test function name is a regex - auth_list = [{'@runner': 'test.arg.*some'}] - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'runner') + auth_list = [{"@runner": "test.arg.*some"}] + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "runner") self.assertFalse(ret) - ret = self.ckminions.spec_check(auth_list, 'test.argsome', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "test.argsome", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'test.arg_aaa_some', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "test.arg_aaa_some", {}, "runner") self.assertTrue(ret) # Test a list of funcs - auth_list = [{'@runner': ['test.arg', 'jobs.active']}] - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'runner') + auth_list = [{"@runner": ["test.arg", "jobs.active"]}] + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'jobs.active', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "jobs.active", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'test.active', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "test.active", {}, "runner") self.assertFalse(ret) - ret = self.ckminions.spec_check(auth_list, 'jobs.arg', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "jobs.arg", {}, "runner") self.assertFalse(ret) # Test args-kwargs rules - auth_list = [{ - '@runner': { - 'test.arg': { - 'args': ['1', '2'], - 'kwargs': { - 'aaa': 'bbb', - 'ccc': 'ddd' - } - } + auth_list = [ + { + "@runner": { + "test.arg": { + "args": ["1", "2"], + "kwargs": {"aaa": "bbb", "ccc": "ddd"}, } - }] - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'runner') - self.assertFalse(ret) - args = { - 'arg': ['1', '2'], - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddd'} } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') + } + ] + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "runner") + self.assertFalse(ret) + args = {"arg": ["1", "2"], "kwarg": {"aaa": "bbb", "ccc": "ddd"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) - args = { - 'arg': ['1', '2', '3'], - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddd'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') + args = {"arg": ["1", "2", "3"], "kwarg": {"aaa": "bbb", "ccc": "ddd"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) - args = { - 'arg': ['1', '2'], - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddd', 'zzz': 'zzz'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') + args = {"arg": ["1", "2"], "kwarg": {"aaa": "bbb", "ccc": "ddd", "zzz": "zzz"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) - args = { - 'arg': ['1', '2'], - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddc'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') + args = {"arg": ["1", "2"], "kwarg": {"aaa": "bbb", "ccc": "ddc"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") + self.assertFalse(ret) + args = {"arg": ["1", "2"], "kwarg": {"aaa": "bbb"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") + self.assertFalse(ret) + args = {"arg": ["1", "3"], "kwarg": {"aaa": "bbb", "ccc": "ddd"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") + self.assertFalse(ret) + args = {"arg": ["1"], "kwarg": {"aaa": "bbb", "ccc": "ddd"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") + self.assertFalse(ret) + args = {"kwarg": {"aaa": "bbb", "ccc": "ddd"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") self.assertFalse(ret) args = { - 'arg': ['1', '2'], - 'kwarg': {'aaa': 'bbb'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') - self.assertFalse(ret) - args = { - 'arg': ['1', '3'], - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddd'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') - self.assertFalse(ret) - args = { - 'arg': ['1'], - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddd'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') - self.assertFalse(ret) - args = { - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddd'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') - self.assertFalse(ret) - args = { - 'arg': ['1', '2'], - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') + "arg": ["1", "2"], + } + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") self.assertFalse(ret) # Test kwargs only - auth_list = [{ - '@runner': { - 'test.arg': { - 'kwargs': { - 'aaa': 'bbb', - 'ccc': 'ddd' - } - } - } - }] - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'runner') + auth_list = [ + {"@runner": {"test.arg": {"kwargs": {"aaa": "bbb", "ccc": "ddd"}}}} + ] + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "runner") self.assertFalse(ret) - args = { - 'arg': ['1', '2'], - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddd'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') + args = {"arg": ["1", "2"], "kwarg": {"aaa": "bbb", "ccc": "ddd"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) # Test args only - auth_list = [{ - '@runner': { - 'test.arg': { - 'args': ['1', '2'] - } - } - }] - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'runner') + auth_list = [{"@runner": {"test.arg": {"args": ["1", "2"]}}}] + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "runner") self.assertFalse(ret) - args = { - 'arg': ['1', '2'], - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddd'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') + args = {"arg": ["1", "2"], "kwarg": {"aaa": "bbb", "ccc": "ddd"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) # Test list of args - auth_list = [{'@runner': [{'test.arg': [{'args': ['1', '2'], - 'kwargs': {'aaa': 'bbb', - 'ccc': 'ddd' - } - }, - {'args': ['2', '3'], - 'kwargs': {'aaa': 'aaa', - 'ccc': 'ccc' - } - }] - }] - }] - args = { - 'arg': ['1', '2'], - 'kwarg': {'aaa': 'bbb', 'ccc': 'ddd'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') + auth_list = [ + { + "@runner": [ + { + "test.arg": [ + { + "args": ["1", "2"], + "kwargs": {"aaa": "bbb", "ccc": "ddd"}, + }, + { + "args": ["2", "3"], + "kwargs": {"aaa": "aaa", "ccc": "ccc"}, + }, + ] + } + ] + } + ] + args = {"arg": ["1", "2"], "kwarg": {"aaa": "bbb", "ccc": "ddd"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) - args = { - 'arg': ['2', '3'], - 'kwarg': {'aaa': 'aaa', 'ccc': 'ccc'} - } - ret = self.ckminions.spec_check(auth_list, 'test.arg', args, 'runner') + args = {"arg": ["2", "3"], "kwarg": {"aaa": "aaa", "ccc": "ccc"}} + ret = self.ckminions.spec_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) # Test @module form - auth_list = ['@jobs'] - ret = self.ckminions.spec_check(auth_list, 'jobs.active', {}, 'runner') + auth_list = ["@jobs"] + ret = self.ckminions.spec_check(auth_list, "jobs.active", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'jobs.active', {}, 'wheel') + ret = self.ckminions.spec_check(auth_list, "jobs.active", {}, "wheel") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'test.arg', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "test.arg", {}, "runner") self.assertFalse(ret) - ret = self.ckminions.spec_check(auth_list, 'job.arg', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "job.arg", {}, "runner") self.assertFalse(ret) # Test @module: function - auth_list = [{'@jobs': 'active'}] - ret = self.ckminions.spec_check(auth_list, 'jobs.active', {}, 'runner') + auth_list = [{"@jobs": "active"}] + ret = self.ckminions.spec_check(auth_list, "jobs.active", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'jobs.active', {}, 'wheel') + ret = self.ckminions.spec_check(auth_list, "jobs.active", {}, "wheel") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'jobs.active_jobs', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "jobs.active_jobs", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'jobs.activ', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "jobs.activ", {}, "runner") self.assertFalse(ret) # Test @module: [functions] - auth_list = [{'@jobs': ['active', 'li']}] - ret = self.ckminions.spec_check(auth_list, 'jobs.active', {}, 'runner') + auth_list = [{"@jobs": ["active", "li"]}] + ret = self.ckminions.spec_check(auth_list, "jobs.active", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'jobs.list_jobs', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "jobs.list_jobs", {}, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'jobs.last_run', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "jobs.last_run", {}, "runner") self.assertFalse(ret) # Test @module: function with args - auth_list = [{'@jobs': {'active': {'args': ['1', '2'], - 'kwargs': {'a': 'b', 'c': 'd'}}}}] - args = {'arg': ['1', '2'], - 'kwarg': {'a': 'b', 'c': 'd'}} - ret = self.ckminions.spec_check(auth_list, 'jobs.active', args, 'runner') + auth_list = [ + {"@jobs": {"active": {"args": ["1", "2"], "kwargs": {"a": "b", "c": "d"}}}} + ] + args = {"arg": ["1", "2"], "kwarg": {"a": "b", "c": "d"}} + ret = self.ckminions.spec_check(auth_list, "jobs.active", args, "runner") self.assertTrue(ret) - ret = self.ckminions.spec_check(auth_list, 'jobs.active', {}, 'runner') + ret = self.ckminions.spec_check(auth_list, "jobs.active", {}, "runner") self.assertFalse(ret) - @patch('salt.utils.minions.CkMinions._pki_minions', MagicMock(return_value=['alpha', 'beta', 'gamma'])) + @patch( + "salt.utils.minions.CkMinions._pki_minions", + MagicMock(return_value=["alpha", "beta", "gamma"]), + ) def test_auth_check(self): # Test function-only rule - auth_list = ['test.ping'] - ret = self.ckminions.auth_check(auth_list, 'test.ping', None, 'alpha') + auth_list = ["test.ping"] + ret = self.ckminions.auth_check(auth_list, "test.ping", None, "alpha") self.assertTrue(ret) - ret = self.ckminions.auth_check(auth_list, 'test.arg', None, 'alpha') + ret = self.ckminions.auth_check(auth_list, "test.arg", None, "alpha") self.assertFalse(ret) # Test minion and function - auth_list = [{'alpha': 'test.ping'}] - ret = self.ckminions.auth_check(auth_list, 'test.ping', None, 'alpha') + auth_list = [{"alpha": "test.ping"}] + ret = self.ckminions.auth_check(auth_list, "test.ping", None, "alpha") self.assertTrue(ret) - ret = self.ckminions.auth_check(auth_list, 'test.arg', None, 'alpha') + ret = self.ckminions.auth_check(auth_list, "test.arg", None, "alpha") self.assertFalse(ret) - ret = self.ckminions.auth_check(auth_list, 'test.ping', None, 'beta') + ret = self.ckminions.auth_check(auth_list, "test.ping", None, "beta") self.assertFalse(ret) # Test function list - auth_list = [{'*': ['test.*', 'saltutil.cmd']}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', None, 'alpha') + auth_list = [{"*": ["test.*", "saltutil.cmd"]}] + ret = self.ckminions.auth_check(auth_list, "test.arg", None, "alpha") self.assertTrue(ret) - ret = self.ckminions.auth_check(auth_list, 'test.ping', None, 'beta') + ret = self.ckminions.auth_check(auth_list, "test.ping", None, "beta") self.assertTrue(ret) - ret = self.ckminions.auth_check(auth_list, 'saltutil.cmd', None, 'gamma') + ret = self.ckminions.auth_check(auth_list, "saltutil.cmd", None, "gamma") self.assertTrue(ret) - ret = self.ckminions.auth_check(auth_list, 'saltutil.running', None, 'gamma') + ret = self.ckminions.auth_check(auth_list, "saltutil.running", None, "gamma") self.assertFalse(ret) # Test an args and kwargs rule - auth_list = [{ - 'alpha': { - 'test.arg': { - 'args': ['1', '2'], - 'kwargs': { - 'aaa': 'bbb', - 'ccc': 'ddd' - } - } + auth_list = [ + { + "alpha": { + "test.arg": { + "args": ["1", "2"], + "kwargs": {"aaa": "bbb", "ccc": "ddd"}, } - }] - ret = self.ckminions.auth_check(auth_list, 'test.arg', None, 'runner') + } + } + ] + ret = self.ckminions.auth_check(auth_list, "test.arg", None, "runner") self.assertFalse(ret) - ret = self.ckminions.auth_check(auth_list, 'test.arg', [], 'runner') + ret = self.ckminions.auth_check(auth_list, "test.arg", [], "runner") self.assertFalse(ret) - args = ['1', '2', {'aaa': 'bbb', 'ccc': 'ddd', '__kwarg__': True}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + args = ["1", "2", {"aaa": "bbb", "ccc": "ddd", "__kwarg__": True}] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) - args = ['1', '2', '3', {'aaa': 'bbb', 'ccc': 'ddd', 'eee': 'fff', '__kwarg__': True}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + args = [ + "1", + "2", + "3", + {"aaa": "bbb", "ccc": "ddd", "eee": "fff", "__kwarg__": True}, + ] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) - args = ['1', {'aaa': 'bbb', 'ccc': 'ddd', '__kwarg__': True}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + args = ["1", {"aaa": "bbb", "ccc": "ddd", "__kwarg__": True}] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertFalse(ret) - args = ['1', '2', {'aaa': 'bbb', '__kwarg__': True}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + args = ["1", "2", {"aaa": "bbb", "__kwarg__": True}] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertFalse(ret) - args = ['1', '3', {'aaa': 'bbb', 'ccc': 'ddd', '__kwarg__': True}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + args = ["1", "3", {"aaa": "bbb", "ccc": "ddd", "__kwarg__": True}] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertFalse(ret) - args = ['1', '2', {'aaa': 'bbb', 'ccc': 'fff', '__kwarg__': True}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + args = ["1", "2", {"aaa": "bbb", "ccc": "fff", "__kwarg__": True}] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertFalse(ret) # Test kwargs only rule - auth_list = [{ - 'alpha': { - 'test.arg': { - 'kwargs': { - 'aaa': 'bbb', - 'ccc': 'ddd' - } - } - } - }] - args = ['1', '2', {'aaa': 'bbb', 'ccc': 'ddd', '__kwarg__': True}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + auth_list = [{"alpha": {"test.arg": {"kwargs": {"aaa": "bbb", "ccc": "ddd"}}}}] + args = ["1", "2", {"aaa": "bbb", "ccc": "ddd", "__kwarg__": True}] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) - args = [{'aaa': 'bbb', 'ccc': 'ddd', 'eee': 'fff', '__kwarg__': True}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + args = [{"aaa": "bbb", "ccc": "ddd", "eee": "fff", "__kwarg__": True}] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) # Test args only rule - auth_list = [{ - 'alpha': { - 'test.arg': { - 'args': ['1', '2'], - } - } - }] - args = ['1', '2', {'aaa': 'bbb', 'ccc': 'ddd', '__kwarg__': True}] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + auth_list = [{"alpha": {"test.arg": {"args": ["1", "2"]}}}] + args = ["1", "2", {"aaa": "bbb", "ccc": "ddd", "__kwarg__": True}] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) - args = ['1', '2'] - ret = self.ckminions.auth_check(auth_list, 'test.arg', args, 'runner') + args = ["1", "2"] + ret = self.ckminions.auth_check(auth_list, "test.arg", args, "runner") self.assertTrue(ret) -@skipIf(sys.version_info < (2, 7), 'Python 2.7 needed for dictionary equality assertions') +@skipIf( + sys.version_info < (2, 7), "Python 2.7 needed for dictionary equality assertions" +) class TargetParseTestCase(TestCase): - def test_parse_grains_target(self): - ''' + """ Ensure proper parsing for grains - ''' - g_tgt = 'G@a:b' + """ + g_tgt = "G@a:b" ret = salt.utils.minions.parse_target(g_tgt) - self.assertDictEqual(ret, {'engine': 'G', 'pattern': 'a:b', 'delimiter': None}) + self.assertDictEqual(ret, {"engine": "G", "pattern": "a:b", "delimiter": None}) def test_parse_grains_pcre_target(self): - ''' + """ Ensure proper parsing for grains PCRE matching - ''' - p_tgt = 'P@a:b' + """ + p_tgt = "P@a:b" ret = salt.utils.minions.parse_target(p_tgt) - self.assertDictEqual(ret, {'engine': 'P', 'pattern': 'a:b', 'delimiter': None}) + self.assertDictEqual(ret, {"engine": "P", "pattern": "a:b", "delimiter": None}) def test_parse_pillar_pcre_target(self): - ''' + """ Ensure proper parsing for pillar PCRE matching - ''' - j_tgt = 'J@a:b' + """ + j_tgt = "J@a:b" ret = salt.utils.minions.parse_target(j_tgt) - self.assertDictEqual(ret, {'engine': 'J', 'pattern': 'a:b', 'delimiter': None}) + self.assertDictEqual(ret, {"engine": "J", "pattern": "a:b", "delimiter": None}) def test_parse_list_target(self): - ''' + """ Ensure proper parsing for list matching - ''' - l_tgt = 'L@a:b' + """ + l_tgt = "L@a:b" ret = salt.utils.minions.parse_target(l_tgt) - self.assertDictEqual(ret, {'engine': 'L', 'pattern': 'a:b', 'delimiter': None}) + self.assertDictEqual(ret, {"engine": "L", "pattern": "a:b", "delimiter": None}) def test_parse_nodegroup_target(self): - ''' + """ Ensure proper parsing for pillar matching - ''' - n_tgt = 'N@a:b' + """ + n_tgt = "N@a:b" ret = salt.utils.minions.parse_target(n_tgt) - self.assertDictEqual(ret, {'engine': 'N', 'pattern': 'a:b', 'delimiter': None}) + self.assertDictEqual(ret, {"engine": "N", "pattern": "a:b", "delimiter": None}) def test_parse_subnet_target(self): - ''' + """ Ensure proper parsing for subnet matching - ''' - s_tgt = 'S@a:b' + """ + s_tgt = "S@a:b" ret = salt.utils.minions.parse_target(s_tgt) - self.assertDictEqual(ret, {'engine': 'S', 'pattern': 'a:b', 'delimiter': None}) + self.assertDictEqual(ret, {"engine": "S", "pattern": "a:b", "delimiter": None}) def test_parse_minion_pcre_target(self): - ''' + """ Ensure proper parsing for minion PCRE matching - ''' - e_tgt = 'E@a:b' + """ + e_tgt = "E@a:b" ret = salt.utils.minions.parse_target(e_tgt) - self.assertDictEqual(ret, {'engine': 'E', 'pattern': 'a:b', 'delimiter': None}) + self.assertDictEqual(ret, {"engine": "E", "pattern": "a:b", "delimiter": None}) def test_parse_range_target(self): - ''' + """ Ensure proper parsing for range matching - ''' - r_tgt = 'R@a:b' + """ + r_tgt = "R@a:b" ret = salt.utils.minions.parse_target(r_tgt) - self.assertDictEqual(ret, {'engine': 'R', 'pattern': 'a:b', 'delimiter': None}) + self.assertDictEqual(ret, {"engine": "R", "pattern": "a:b", "delimiter": None}) def test_parse_multiword_target(self): - ''' + """ Ensure proper parsing for multi-word targets Refs https://github.com/saltstack/salt/issues/37231 - ''' - mw_tgt = 'G@a:b c' + """ + mw_tgt = "G@a:b c" ret = salt.utils.minions.parse_target(mw_tgt) - self.assertEqual(ret['pattern'], 'a:b c') + self.assertEqual(ret["pattern"], "a:b c") class NodegroupCompTest(TestCase): - ''' + """ Test nodegroup comparisons found in salt.utils.minions.nodgroup_comp() - ''' + """ def test_simple_nodegroup(self): - ''' + """ Smoke test a very simple nodegroup. No recursion. - ''' - simple_nodegroup = {'group1': 'L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com'} + """ + simple_nodegroup = { + "group1": "L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com" + } - ret = salt.utils.minions.nodegroup_comp('group1', simple_nodegroup) - expected_ret = ['L@foo.domain.com,bar.domain.com,baz.domain.com', 'or', 'bl*.domain.com'] + ret = salt.utils.minions.nodegroup_comp("group1", simple_nodegroup) + expected_ret = [ + "L@foo.domain.com,bar.domain.com,baz.domain.com", + "or", + "bl*.domain.com", + ] self.assertListEqual(ret, expected_ret) def test_simple_expression_nodegroup(self): - ''' + """ Smoke test a nodegroup with a simple expression. No recursion. - ''' - simple_nodegroup = {'group1': '[foo,bar,baz].domain.com'} + """ + simple_nodegroup = {"group1": "[foo,bar,baz].domain.com"} - ret = salt.utils.minions.nodegroup_comp('group1', simple_nodegroup) - expected_ret = ['E@[foo,bar,baz].domain.com'] + ret = salt.utils.minions.nodegroup_comp("group1", simple_nodegroup) + expected_ret = ["E@[foo,bar,baz].domain.com"] self.assertListEqual(ret, expected_ret) def test_simple_recurse(self): - ''' + """ Test a case where one nodegroup contains a second nodegroup - ''' + """ referenced_nodegroups = { - 'group1': 'L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com', - 'group2': 'G@os:Debian and N@group1' - } + "group1": "L@foo.domain.com,bar.domain.com,baz.domain.com or bl*.domain.com", + "group2": "G@os:Debian and N@group1", + } - ret = salt.utils.minions.nodegroup_comp('group2', referenced_nodegroups) + ret = salt.utils.minions.nodegroup_comp("group2", referenced_nodegroups) expected_ret = [ - '(', - 'G@os:Debian', - 'and', - '(', - 'L@foo.domain.com,bar.domain.com,baz.domain.com', - 'or', - 'bl*.domain.com', - ')', - ')' - ] + "(", + "G@os:Debian", + "and", + "(", + "L@foo.domain.com,bar.domain.com,baz.domain.com", + "or", + "bl*.domain.com", + ")", + ")", + ] self.assertListEqual(ret, expected_ret) def test_circular_nodegroup_reference(self): - ''' + """ Test to see what happens if A refers to B and B in turn refers back to A - ''' - referenced_nodegroups = { - 'group1': 'N@group2', - 'group2': 'N@group1' - } + """ + referenced_nodegroups = {"group1": "N@group2", "group2": "N@group1"} # If this works, it should also print an error to the console - ret = salt.utils.minions.nodegroup_comp('group1', referenced_nodegroups) + ret = salt.utils.minions.nodegroup_comp("group1", referenced_nodegroups) self.assertEqual(ret, []) diff --git a/tests/unit/utils/test_msgpack.py b/tests/unit/utils/test_msgpack.py index cac7c1e9b1d..f4574e6da6b 100644 --- a/tests/unit/utils/test_msgpack.py +++ b/tests/unit/utils/test_msgpack.py @@ -1,38 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" Test the MessagePack utility -''' +""" # Import Python Libs from __future__ import absolute_import -from io import BytesIO + import inspect import os import pprint -import sys import struct +import sys +from io import BytesIO + +import salt.utils.msgpack +from salt.ext.six.moves import range + +# Import Salt Libs +from salt.utils.odict import OrderedDict + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf try: import msgpack except ImportError: import msgpack_pure as msgpack # pylint: disable=import-error -# Import Salt Testing Libs -from tests.support.unit import skipIf -from tests.support.unit import TestCase - -# Import Salt Libs -from salt.utils.odict import OrderedDict -from salt.ext.six.moves import range -import salt.utils.msgpack # A keyword to pass to tests that use `raw`, which was added in msgpack 0.5.2 -raw = {'raw': False} if msgpack.version > (0, 5, 2) else {} +raw = {"raw": False} if msgpack.version > (0, 5, 2) else {} -@skipIf(not salt.utils.msgpack.HAS_MSGPACK, 'msgpack module required for these tests') +@skipIf(not salt.utils.msgpack.HAS_MSGPACK, "msgpack module required for these tests") class TestMsgpack(TestCase): - ''' + """ In msgpack, the following aliases exist: load = unpack loads = unpackb @@ -41,7 +43,8 @@ class TestMsgpack(TestCase): The salt.utils.msgpack versions of these functions are not aliases, verify that they pass the same relevant tests from: https://github.com/msgpack/msgpack-python/blob/master/test/ - ''' + """ + test_data = [ 0, 1, @@ -78,9 +81,9 @@ class TestMsgpack(TestCase): ] def test_version(self): - ''' + """ Verify that the version exists and returns a value in the expected format - ''' + """ version = salt.utils.msgpack.version self.assertTrue(isinstance(version, tuple)) self.assertGreater(version, (0, 0, 0)) @@ -143,7 +146,7 @@ class TestMsgpack(TestCase): self.assertEqual(unpacker.unpack(), list(range(size))) packer.reset() - self.assertEqual(packer.bytes(), b'') + self.assertEqual(packer.bytes(), b"") def test_map_size(self): sizes = [0, 5, 50, 1000] @@ -171,24 +174,31 @@ class TestMsgpack(TestCase): self.assertTrue(salt.utils.msgpack.exceptions.UnpackValueError) def test_function_aliases(self): - ''' + """ Fail if core functionality from msgpack is missing in the utility - ''' + """ def sanitized(item): if inspect.isfunction(getattr(msgpack, item)): # Only check objects that exist in the same file as msgpack - return inspect.getfile(getattr(msgpack, item)) == inspect.getfile(msgpack) + return inspect.getfile(getattr(msgpack, item)) == inspect.getfile( + msgpack + ) - msgpack_items = set(x for x in dir(msgpack) if not x.startswith('_') and sanitized(x)) + msgpack_items = set( + x for x in dir(msgpack) if not x.startswith("_") and sanitized(x) + ) msgpack_util_items = set(dir(salt.utils.msgpack)) - self.assertFalse(msgpack_items - msgpack_util_items, 'msgpack functions with no alias in `salt.utils.msgpack`') + self.assertFalse( + msgpack_items - msgpack_util_items, + "msgpack functions with no alias in `salt.utils.msgpack`", + ) def _test_base(self, pack_func, unpack_func): - ''' + """ In msgpack, 'dumps' is an alias for 'packb' and 'loads' is an alias for 'unpackb'. Verify that both salt.utils.msgpack function variations pass the exact same test - ''' + """ data = os.urandom(1024) packed = pack_func(data) @@ -202,7 +212,7 @@ class TestMsgpack(TestCase): self.assertEqual(data, unpacked) def _test_buffered_base(self, pack_func, unpack_func): - data = os.urandom(1024).decode(errors='ignore') + data = os.urandom(1024).decode(errors="ignore") buffer = BytesIO() # Sanity check, we are not borking the BytesIO read function self.assertNotEqual(BytesIO.read, buffer.read) @@ -219,10 +229,14 @@ class TestMsgpack(TestCase): self.assertEqual(data, unpacked.decode()) def test_buffered_base_pack(self): - self._test_buffered_base(pack_func=salt.utils.msgpack.pack, unpack_func=msgpack.unpack) + self._test_buffered_base( + pack_func=salt.utils.msgpack.pack, unpack_func=msgpack.unpack + ) def test_buffered_base_unpack(self): - self._test_buffered_base(pack_func=msgpack.pack, unpack_func=salt.utils.msgpack.unpack) + self._test_buffered_base( + pack_func=msgpack.pack, unpack_func=salt.utils.msgpack.unpack + ) def _test_unpack_array_header_from_file(self, pack_func, **kwargs): f = BytesIO(pack_func([1, 2, 3, 4])) @@ -234,7 +248,9 @@ class TestMsgpack(TestCase): self.assertEqual(unpacker.unpack(), 4) self.assertRaises(salt.utils.msgpack.exceptions.OutOfData, unpacker.unpack) - @skipIf(not hasattr(sys, 'getrefcount'), 'sys.getrefcount() is needed to pass this test') + @skipIf( + not hasattr(sys, "getrefcount"), "sys.getrefcount() is needed to pass this test" + ) def _test_unpacker_hook_refcnt(self, pack_func, **kwargs): result = [] @@ -272,76 +288,94 @@ class TestMsgpack(TestCase): unpacker = MyUnpacker() unpacker.feed(pack_func({"a": 1})) - self.assertEqual(unpacker.unpack(), {'a': 1}) - unpacker.feed(pack_func({'a': salt.utils.msgpack.ExtType(1, b'123')})) - self.assertEqual(unpacker.unpack(), {'a': 123}) - unpacker.feed(pack_func({'a': salt.utils.msgpack.ExtType(2, b'321')})) - self.assertEqual(unpacker.unpack(), {'a': salt.utils.msgpack.ExtType(2, b'321')}) + self.assertEqual(unpacker.unpack(), {"a": 1}) + unpacker.feed(pack_func({"a": salt.utils.msgpack.ExtType(1, b"123")})) + self.assertEqual(unpacker.unpack(), {"a": 123}) + unpacker.feed(pack_func({"a": salt.utils.msgpack.ExtType(2, b"321")})) + self.assertEqual( + unpacker.unpack(), {"a": salt.utils.msgpack.ExtType(2, b"321")} + ) - def _check(self, data, pack_func, unpack_func, use_list=False, strict_map_key=False): + def _check( + self, data, pack_func, unpack_func, use_list=False, strict_map_key=False + ): my_kwargs = {} if salt.utils.msgpack.version >= (0, 6, 0): - my_kwargs['strict_map_key'] = strict_map_key + my_kwargs["strict_map_key"] = strict_map_key ret = unpack_func(pack_func(data), use_list=use_list, **my_kwargs) self.assertEqual(ret, data) def _test_pack_unicode(self, pack_func, unpack_func): - test_data = [u'', u'abcd', [u'defgh'], u'Русский текст'] + test_data = [u"", u"abcd", [u"defgh"], u"Русский текст"] for td in test_data: ret = unpack_func(pack_func(td), use_list=True, **raw) self.assertEqual(ret, td) packer = salt.utils.msgpack.Packer() data = packer.pack(td) - ret = salt.utils.msgpack.Unpacker(BytesIO(data), use_list=True, **raw).unpack() + ret = salt.utils.msgpack.Unpacker( + BytesIO(data), use_list=True, **raw + ).unpack() self.assertEqual(ret, td) def _test_pack_bytes(self, pack_func, unpack_func): test_data = [ - b'', - b'abcd', - (b'defgh',), + b"", + b"abcd", + (b"defgh",), ] for td in test_data: self._check(td, pack_func, unpack_func) def _test_pack_byte_arrays(self, pack_func, unpack_func): test_data = [ - bytearray(b''), - bytearray(b'abcd'), - (bytearray(b'defgh'),), + bytearray(b""), + bytearray(b"abcd"), + (bytearray(b"defgh"),), ] for td in test_data: self._check(td, pack_func, unpack_func) - @skipIf(sys.version_info < (3, 0), 'Python 2 passes invalid surrogates') + @skipIf(sys.version_info < (3, 0), "Python 2 passes invalid surrogates") def _test_ignore_unicode_errors(self, pack_func, unpack_func): ret = unpack_func( - pack_func(b'abc\xeddef', use_bin_type=False), unicode_errors='ignore', **raw + pack_func(b"abc\xeddef", use_bin_type=False), unicode_errors="ignore", **raw ) - self.assertEqual(u'abcdef', ret) + self.assertEqual(u"abcdef", ret) def _test_strict_unicode_unpack(self, pack_func, unpack_func): - packed = pack_func(b'abc\xeddef', use_bin_type=False) + packed = pack_func(b"abc\xeddef", use_bin_type=False) self.assertRaises(UnicodeDecodeError, unpack_func, packed, use_list=True, **raw) - @skipIf(sys.version_info < (3, 0), 'Python 2 passes invalid surrogates') + @skipIf(sys.version_info < (3, 0), "Python 2 passes invalid surrogates") def _test_ignore_errors_pack(self, pack_func, unpack_func): ret = unpack_func( - pack_func(u'abc\uDC80\uDCFFdef', use_bin_type=True, unicode_errors='ignore'), use_list=True, **raw + pack_func( + u"abc\uDC80\uDCFFdef", use_bin_type=True, unicode_errors="ignore" + ), + use_list=True, + **raw ) - self.assertEqual(u'abcdef', ret) + self.assertEqual(u"abcdef", ret) def _test_decode_binary(self, pack_func, unpack_func): - ret = unpack_func(pack_func(b'abc'), use_list=True) - self.assertEqual(b'abc', ret) + ret = unpack_func(pack_func(b"abc"), use_list=True) + self.assertEqual(b"abc", ret) - @skipIf(salt.utils.msgpack.version < (0, 2, 2), 'use_single_float was added in msgpack==0.2.2') + @skipIf( + salt.utils.msgpack.version < (0, 2, 2), + "use_single_float was added in msgpack==0.2.2", + ) def _test_pack_float(self, pack_func, **kwargs): - self.assertEqual(b'\xca' + struct.pack(str('>f'), 1.0), pack_func(1.0, use_single_float=True)) - self.assertEqual(b'\xcb' + struct.pack(str('>d'), 1.0), pack_func(1.0, use_single_float=False)) + self.assertEqual( + b"\xca" + struct.pack(str(">f"), 1.0), pack_func(1.0, use_single_float=True) + ) + self.assertEqual( + b"\xcb" + struct.pack(str(">d"), 1.0), + pack_func(1.0, use_single_float=False), + ) def _test_odict(self, pack_func, unpack_func): - seq = [(b'one', 1), (b'two', 2), (b'three', 3), (b'four', 4)] + seq = [(b"one", 1), (b"two", 2), (b"three", 3), (b"four", 4)] od = OrderedDict(seq) self.assertEqual(dict(seq), unpack_func(pack_func(od), use_list=True)) @@ -349,10 +383,12 @@ class TestMsgpack(TestCase): def pair_hook(seq): return list(seq) - self.assertEqual(seq, unpack_func(pack_func(od), object_pairs_hook=pair_hook, use_list=True)) + self.assertEqual( + seq, unpack_func(pack_func(od), object_pairs_hook=pair_hook, use_list=True) + ) def _test_pair_list(self, unpack_func, **kwargs): - pairlist = [(b'a', 1), (2, b'b'), (b'foo', b'bar')] + pairlist = [(b"a", 1), (2, b"b"), (b"foo", b"bar")] packer = salt.utils.msgpack.Packer() packed = packer.pack_map_pairs(pairlist) if salt.utils.msgpack.version > (0, 6, 0): @@ -361,7 +397,10 @@ class TestMsgpack(TestCase): unpacked = unpack_func(packed, object_pairs_hook=list) self.assertEqual(pairlist, unpacked) - @skipIf(salt.utils.msgpack.version < (0, 6, 0), 'getbuffer() was added to Packer in msgpack 0.6.0') + @skipIf( + salt.utils.msgpack.version < (0, 6, 0), + "getbuffer() was added to Packer in msgpack 0.6.0", + ) def _test_get_buffer(self, pack_func, **kwargs): packer = msgpack.Packer(autoreset=False, use_bin_type=True) packer.pack([1, 2]) @@ -374,9 +413,9 @@ class TestMsgpack(TestCase): @staticmethod def no_fail_run(test, *args, **kwargs): - ''' + """ Run a test without failure and return any exception it raises - ''' + """ try: test(*args, **kwargs) except Exception as e: # pylint: disable=broad-except @@ -384,14 +423,18 @@ class TestMsgpack(TestCase): def test_binary_function_compatibility(self): functions = [ - {'pack_func': salt.utils.msgpack.packb, 'unpack_func': msgpack.unpackb}, - {'pack_func': msgpack.packb, 'unpack_func': salt.utils.msgpack.unpackb}, + {"pack_func": salt.utils.msgpack.packb, "unpack_func": msgpack.unpackb}, + {"pack_func": msgpack.packb, "unpack_func": salt.utils.msgpack.unpackb}, ] # These functions are equivalent but could potentially be overwritten if salt.utils.msgpack.dumps is not salt.utils.msgpack.packb: - functions.append({'pack_func': salt.utils.msgpack.dumps, 'unpack_func': msgpack.unpackb}) + functions.append( + {"pack_func": salt.utils.msgpack.dumps, "unpack_func": msgpack.unpackb} + ) if salt.utils.msgpack.loads is not salt.utils.msgpack.unpackb: - functions.append({'pack_func': msgpack.packb, 'unpack_func': salt.utils.msgpack.loads}) + functions.append( + {"pack_func": msgpack.packb, "unpack_func": salt.utils.msgpack.loads} + ) test_funcs = ( self._test_base, @@ -413,26 +456,40 @@ class TestMsgpack(TestCase): errors = {} for test_func in test_funcs: # Run the test without the salt.utils.msgpack module for comparison - vanilla_run = self.no_fail_run(test_func, **{'pack_func': msgpack.packb, 'unpack_func': msgpack.unpackb}) + vanilla_run = self.no_fail_run( + test_func, + **{"pack_func": msgpack.packb, "unpack_func": msgpack.unpackb} + ) for func_args in functions: - func_name = func_args['pack_func'] if func_args['pack_func'].__module__.startswith('salt.utils') \ - else func_args['unpack_func'] - if hasattr(TestCase, 'subTest'): + func_name = ( + func_args["pack_func"] + if func_args["pack_func"].__module__.startswith("salt.utils") + else func_args["unpack_func"] + ) + if hasattr(TestCase, "subTest"): with self.subTest(test=test_func.__name__, func=func_name.__name__): # Run the test with the salt.utils.msgpack module run = self.no_fail_run(test_func, **func_args) # If the vanilla msgpack module errored, then skip if we got the same error if run: if str(vanilla_run) == str(run): - self.skipTest('Failed the same way as the vanilla msgpack module:\n{}'.format(run)) + self.skipTest( + "Failed the same way as the vanilla msgpack module:\n{}".format( + run + ) + ) else: # If subTest isn't available then run the tests collect the errors of all the tests before failing run = self.no_fail_run(test_func, **func_args) if run: # If the vanilla msgpack module errored, then skip if we got the same error if str(vanilla_run) == str(run): - self.skipTest('Test failed the same way the vanilla msgpack module fails:\n{}'.format(run)) + self.skipTest( + "Test failed the same way the vanilla msgpack module fails:\n{}".format( + run + ) + ) else: errors[(test_func.__name__, func_name.__name__)] = run diff --git a/tests/unit/utils/test_nacl.py b/tests/unit/utils/test_nacl.py index 1d076d76342..47e1999ef8b 100644 --- a/tests/unit/utils/test_nacl.py +++ b/tests/unit/utils/test_nacl.py @@ -2,160 +2,149 @@ # Import python libs from __future__ import absolute_import -import os -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.helpers import with_tempfile +import os # Import Salt libs import salt.modules.config as config import salt.utils.files +from tests.support.helpers import with_tempfile +from tests.support.mixins import LoaderModuleMockMixin + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf try: import libnacl.secret # pylint: disable=unused-import import libnacl.sealed # pylint: disable=unused-import import salt.utils.nacl as nacl + HAS_LIBNACL = True except (ImportError, OSError, AttributeError): HAS_LIBNACL = False -@skipIf(not HAS_LIBNACL, 'skipping test_nacl, libnacl is unavailable') +@skipIf(not HAS_LIBNACL, "skipping test_nacl, libnacl is unavailable") class NaclUtilsTests(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): return { - nacl: {'__salt__': {'config.get': config.get}}, - config: {'__opts__': {}}, + nacl: {"__salt__": {"config.get": config.get}}, + config: {"__opts__": {}}, } def setUp(self): - self.key = 'C16NxgBhw8cqbhvPCDAn2pirwW1A1WEVLUexCsoUD2Y=' - self.pub = '+XWFfZXnfItS++a4gQf8Adu1aUlTgHWyTfsglbTdXyg=' + self.key = "C16NxgBhw8cqbhvPCDAn2pirwW1A1WEVLUexCsoUD2Y=" + self.pub = "+XWFfZXnfItS++a4gQf8Adu1aUlTgHWyTfsglbTdXyg=" def test_keygen(self): - ''' + """ test nacl.keygen function - ''' + """ ret = nacl.keygen() assert all(key in ret for key in ret.keys()) @with_tempfile() def test_keygen_sk_file(self, fpath): - ''' + """ test nacl.keygen function with sk_file set - ''' - with salt.utils.files.fopen(fpath, 'w') as wfh: - wfh.write( - self.key - ) + """ + with salt.utils.files.fopen(fpath, "w") as wfh: + wfh.write(self.key) # test sk_file ret = nacl.keygen(sk_file=fpath) - assert 'saved pk_file: {}.pub'.format(fpath) == ret + assert "saved pk_file: {}.pub".format(fpath) == ret @with_tempfile() def test_keygen_keyfile(self, fpath): - ''' + """ test nacl.keygen function with keyfile set - ''' - with salt.utils.files.fopen(fpath, 'w') as wfh: - wfh.write( - self.key - ) + """ + with salt.utils.files.fopen(fpath, "w") as wfh: + wfh.write(self.key) ret = nacl.keygen(keyfile=fpath) - assert 'saved pk_file: {}.pub'.format(fpath) == ret + assert "saved pk_file: {}.pub".format(fpath) == ret @with_tempfile() def test_enc_keyfile(self, fpath): - ''' + """ test nacl.enc function with keyfile and pk_file set - ''' - with salt.utils.files.fopen(fpath, 'w') as wfh: - wfh.write( - self.key - ) - with salt.utils.files.fopen(fpath + '.pub', 'w') as wfh: - wfh.write( - self.pub - ) + """ + with salt.utils.files.fopen(fpath, "w") as wfh: + wfh.write(self.key) + with salt.utils.files.fopen(fpath + ".pub", "w") as wfh: + wfh.write(self.pub) - kwargs = {'opts': {'pki_dir': os.path.dirname(fpath)}, - 'keyfile': fpath, - 'pk_file': fpath + '.pub'} - ret = nacl.enc('blah', **kwargs) + kwargs = { + "opts": {"pki_dir": os.path.dirname(fpath)}, + "keyfile": fpath, + "pk_file": fpath + ".pub", + } + ret = nacl.enc("blah", **kwargs) assert isinstance(ret, bytes) @with_tempfile() def test_enc_sk_file(self, fpath): - ''' + """ test nacl.enc function with sk_file and pk_file set - ''' - with salt.utils.files.fopen(fpath, 'w') as wfh: - wfh.write( - self.key - ) - with salt.utils.files.fopen(fpath + '.pub', 'w') as wfh: - wfh.write( - self.pub - ) + """ + with salt.utils.files.fopen(fpath, "w") as wfh: + wfh.write(self.key) + with salt.utils.files.fopen(fpath + ".pub", "w") as wfh: + wfh.write(self.pub) - kwargs = {'opts': {'pki_dir': os.path.dirname(fpath)}, - 'sk_file': fpath, - 'pk_file': fpath + '.pub'} - ret = nacl.enc('blah', **kwargs) + kwargs = { + "opts": {"pki_dir": os.path.dirname(fpath)}, + "sk_file": fpath, + "pk_file": fpath + ".pub", + } + ret = nacl.enc("blah", **kwargs) assert isinstance(ret, bytes) @with_tempfile() def test_dec_keyfile(self, fpath): - ''' + """ test nacl.dec function with keyfile and pk_file set - ''' - with salt.utils.files.fopen(fpath, 'w') as wfh: - wfh.write( - self.key - ) - with salt.utils.files.fopen(fpath + '.pub', 'w') as wfh: - wfh.write( - self.pub - ) + """ + with salt.utils.files.fopen(fpath, "w") as wfh: + wfh.write(self.key) + with salt.utils.files.fopen(fpath + ".pub", "w") as wfh: + wfh.write(self.pub) - kwargs = {'opts': {'pki_dir': os.path.dirname(fpath)}, - 'keyfile': fpath, - 'pk_file': fpath + '.pub'} + kwargs = { + "opts": {"pki_dir": os.path.dirname(fpath)}, + "keyfile": fpath, + "pk_file": fpath + ".pub", + } - enc_data = nacl.enc('blah', **kwargs) + enc_data = nacl.enc("blah", **kwargs) ret = nacl.dec(enc_data, **kwargs) assert isinstance(ret, bytes) - assert ret == b'blah' + assert ret == b"blah" @with_tempfile() def test_dec_sk_file(self, fpath): - ''' + """ test nacl.dec function with sk_file and pk_file set - ''' - with salt.utils.files.fopen(fpath, 'w') as wfh: - wfh.write( - self.key - ) - with salt.utils.files.fopen(fpath + '.pub', 'w') as wfh: - wfh.write( - self.pub - ) + """ + with salt.utils.files.fopen(fpath, "w") as wfh: + wfh.write(self.key) + with salt.utils.files.fopen(fpath + ".pub", "w") as wfh: + wfh.write(self.pub) - kwargs = {'opts': {'pki_dir': os.path.dirname(fpath)}, - 'sk_file': fpath, - 'pk_file': fpath + '.pub'} + kwargs = { + "opts": {"pki_dir": os.path.dirname(fpath)}, + "sk_file": fpath, + "pk_file": fpath + ".pub", + } - enc_data = nacl.enc('blah', **kwargs) + enc_data = nacl.enc("blah", **kwargs) ret = nacl.dec(enc_data, **kwargs) assert isinstance(ret, bytes) - assert ret == b'blah' + assert ret == b"blah" diff --git a/tests/unit/utils/test_network.py b/tests/unit/utils/test_network.py index 7dcca0166e4..ab3b77897d1 100644 --- a/tests/unit/utils/test_network.py +++ b/tests/unit/utils/test_network.py @@ -1,27 +1,25 @@ # -*- coding: utf-8 -*- # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import logging import socket import textwrap -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - mock_open, - create_autospec, - patch, -) +import pytest +import salt.exceptions # Import salt libs import salt.utils.network as network -import salt.exceptions from salt._compat import ipaddress +from tests.support.mock import MagicMock, create_autospec, mock_open, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase log = logging.getLogger(__name__) -LINUX = '''\ +LINUX = """\ eth0 Link encap:Ethernet HWaddr e0:3f:49:85:6a:af inet addr:10.10.10.56 Bcast:10.10.10.255 Mask:255.255.252.0 inet6 addr: fe80::e23f:49ff:fe85:6aaf/64 Scope:Link @@ -39,9 +37,9 @@ lo Link encap:Local Loopback TX packets:548901 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:613479895 (585.0 MiB) TX bytes:613479895 (585.0 MiB) -''' +""" -FREEBSD = ''' +FREEBSD = """ em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=4219b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,WOL_MAGIC,VLAN_HWTSO> ether 00:30:48:ff:ff:ff @@ -65,9 +63,9 @@ tun0: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1500 options=80000<LINKSTATE> inet 10.12.0.1 --> 10.12.0.2 netmask 0xffffffff Opened by PID 1964 -''' +""" -SOLARIS = '''\ +SOLARIS = """\ lo0: flags=2001000849<UP,LOOPBACK,RUNNING,MULTICAST,IPv4,VIRTUAL> mtu 8232 index 1 inet 127.0.0.1 netmask ff000000 net0: flags=100001100943<UP,BROADCAST,RUNNING,PROMISC,MULTICAST,ROUTER,IPv4,PHYSRUNNING> mtu 1500 index 2 @@ -94,9 +92,9 @@ vpn0: flags=120002200850<POINTOPOINT,RUNNING,MULTICAST,NONUD,IPv6,PHYSRUNNING> m inet tunnel src 10.10.11.12 tunnel dst 10.10.5.5 tunnel hop limit 64 inet6 ::/0 --> fe80::b2d6:7c10 -''' +""" -NETBSD = '''\ +NETBSD = """\ vioif0: flags=0x8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> mtu 1500 ec_capabilities=1<VLAN_MTU> ec_enabled=0 @@ -107,148 +105,163 @@ lo0: flags=0x8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 33624 inet 127.0.0.1/8 flags 0x0 inet6 ::1/128 flags 0x20<NODAD> inet6 fe80::1%lo0/64 flags 0x0 scopeid 0x2 -''' +""" -FREEBSD_SOCKSTAT = '''\ +FREEBSD_SOCKSTAT = """\ USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS root python2.7 1294 41 tcp4 127.0.0.1:61115 127.0.0.1:4506 -''' +""" -FREEBSD_SOCKSTAT_WITH_FAT_PID = '''\ +FREEBSD_SOCKSTAT_WITH_FAT_PID = """\ USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS salt-master python2.781106 35 tcp4 127.0.0.1:61115 127.0.0.1:4506 -''' +""" -NETLINK_SS = ''' +NETLINK_SS = """ State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 0 127.0.0.1:56726 127.0.0.1:4505 ESTAB 0 0 ::ffff:1.2.3.4:5678 ::ffff:1.2.3.4:4505 -''' +""" -LINUX_NETLINK_SS_OUTPUT = '''\ +LINUX_NETLINK_SS_OUTPUT = """\ State Recv-Q Send-Q Local Address:Port Peer Address:Port TIME-WAIT 0 0 [::1]:8009 [::1]:40368 LISTEN 0 128 127.0.0.1:5903 0.0.0.0:* ESTAB 0 0 [::ffff:127.0.0.1]:4506 [::ffff:127.0.0.1]:32315 ESTAB 0 0 192.168.122.1:4506 192.168.122.177:24545 -''' +""" -IPV4_SUBNETS = {True: ('10.10.0.0/24',), - False: ('10.10.0.0', '10.10.0.0/33', 'FOO', 9, '0.9.800.1000/24')} -IPV6_SUBNETS = {True: ('::1/128',), - False: ('::1', '::1/129', 'FOO', 9, 'aj01::feac/64')} +IPV4_SUBNETS = { + True: ("10.10.0.0/24",), + False: ("10.10.0.0", "10.10.0.0/33", "FOO", 9, "0.9.800.1000/24"), +} +IPV6_SUBNETS = { + True: ("::1/128",), + False: ("::1", "::1/129", "FOO", 9, "aj01::feac/64"), +} class NetworkTestCase(TestCase): - def test_sanitize_host(self): - ret = network.sanitize_host('10.1./2.$3') - self.assertEqual(ret, '10.1.2.3') + ret = network.sanitize_host("10.1./2.$3") + self.assertEqual(ret, "10.1.2.3") def test_host_to_ips(self): - ''' + """ NOTE: When this test fails it's usually because the IP address has changed. In these cases, we just need to update the IP address in the assertion. - ''' + """ + def _side_effect(host, *args): try: return { - 'github.com': [ - (2, 1, 6, '', ('192.30.255.112', 0)), - (2, 1, 6, '', ('192.30.255.113', 0)), + "github.com": [ + (2, 1, 6, "", ("192.30.255.112", 0)), + (2, 1, 6, "", ("192.30.255.113", 0)), ], - 'ipv6host.foo': [ - (socket.AF_INET6, 1, 6, '', ('2001:a71::1', 0, 0, 0)), + "ipv6host.foo": [ + (socket.AF_INET6, 1, 6, "", ("2001:a71::1", 0, 0, 0)), ], }[host] except KeyError: - raise socket.gaierror(-2, 'Name or service not known') + raise socket.gaierror(-2, "Name or service not known") getaddrinfo_mock = MagicMock(side_effect=_side_effect) - with patch.object(socket, 'getaddrinfo', getaddrinfo_mock): + with patch.object(socket, "getaddrinfo", getaddrinfo_mock): # Test host that can be resolved - ret = network.host_to_ips('github.com') - self.assertEqual(ret, ['192.30.255.112', '192.30.255.113']) + ret = network.host_to_ips("github.com") + self.assertEqual(ret, ["192.30.255.112", "192.30.255.113"]) # Test ipv6 - ret = network.host_to_ips('ipv6host.foo') - self.assertEqual(ret, ['2001:a71::1']) + ret = network.host_to_ips("ipv6host.foo") + self.assertEqual(ret, ["2001:a71::1"]) # Test host that can't be resolved - ret = network.host_to_ips('someothersite.com') + ret = network.host_to_ips("someothersite.com") self.assertEqual(ret, None) def test_generate_minion_id(self): self.assertTrue(network.generate_minion_id()) def test__generate_minion_id_with_unicode_in_etc_hosts(self): - ''' + """ Test that unicode in /etc/hosts doesn't raise an error when _generate_minion_id() helper is called to gather the hosts. - ''' - content = textwrap.dedent('''\ + """ + content = textwrap.dedent( + """\ # 以下为主机名解析 ## ccc 127.0.0.1 localhost thisismyhostname # 本机 - ''') - fopen_mock = mock_open(read_data={'/etc/hosts': content}) - with patch('salt.utils.files.fopen', fopen_mock): - assert 'thisismyhostname' in network._generate_minion_id() + """ + ) + fopen_mock = mock_open(read_data={"/etc/hosts": content}) + with patch("salt.utils.files.fopen", fopen_mock): + assert "thisismyhostname" in network._generate_minion_id() def test_is_ip(self): - self.assertTrue(network.is_ip('10.10.0.3')) - self.assertFalse(network.is_ip('0.9.800.1000')) + self.assertTrue(network.is_ip("10.10.0.3")) + self.assertFalse(network.is_ip("0.9.800.1000")) # Check 16-char-long unicode string # https://github.com/saltstack/salt/issues/51258 - self.assertFalse(network.is_ipv6('sixteen-char-str')) + self.assertFalse(network.is_ipv6("sixteen-char-str")) def test_is_ipv4(self): - self.assertTrue(network.is_ipv4('10.10.0.3')) - self.assertFalse(network.is_ipv4('10.100.1')) - self.assertFalse(network.is_ipv4('2001:db8:0:1:1:1:1:1')) + self.assertTrue(network.is_ipv4("10.10.0.3")) + self.assertFalse(network.is_ipv4("10.100.1")) + self.assertFalse(network.is_ipv4("2001:db8:0:1:1:1:1:1")) # Check 16-char-long unicode string # https://github.com/saltstack/salt/issues/51258 - self.assertFalse(network.is_ipv4('sixteen-char-str')) + self.assertFalse(network.is_ipv4("sixteen-char-str")) def test_is_ipv6(self): - self.assertTrue(network.is_ipv6('2001:db8:0:1:1:1:1:1')) - self.assertTrue(network.is_ipv6('0:0:0:0:0:0:0:1')) - self.assertTrue(network.is_ipv6('::1')) - self.assertTrue(network.is_ipv6('::')) - self.assertTrue(network.is_ipv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334')) - self.assertTrue(network.is_ipv6('2001:0db8:85a3::8a2e:0370:7334')) - self.assertFalse(network.is_ipv6('2001:0db8:0370:7334')) - self.assertFalse(network.is_ipv6('2001:0db8:::0370:7334')) - self.assertFalse(network.is_ipv6('10.0.1.2')) - self.assertFalse(network.is_ipv6('2001.0db8.85a3.0000.0000.8a2e.0370.7334')) + self.assertTrue(network.is_ipv6("2001:db8:0:1:1:1:1:1")) + self.assertTrue(network.is_ipv6("0:0:0:0:0:0:0:1")) + self.assertTrue(network.is_ipv6("::1")) + self.assertTrue(network.is_ipv6("::")) + self.assertTrue(network.is_ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334")) + self.assertTrue(network.is_ipv6("2001:0db8:85a3::8a2e:0370:7334")) + self.assertFalse(network.is_ipv6("2001:0db8:0370:7334")) + self.assertFalse(network.is_ipv6("2001:0db8:::0370:7334")) + self.assertFalse(network.is_ipv6("10.0.1.2")) + self.assertFalse(network.is_ipv6("2001.0db8.85a3.0000.0000.8a2e.0370.7334")) # Check 16-char-long unicode string # https://github.com/saltstack/salt/issues/51258 - self.assertFalse(network.is_ipv6('sixteen-char-str')) + self.assertFalse(network.is_ipv6("sixteen-char-str")) def test_ipv6(self): - self.assertTrue(network.ipv6('2001:db8:0:1:1:1:1:1')) - self.assertTrue(network.ipv6('0:0:0:0:0:0:0:1')) - self.assertTrue(network.ipv6('::1')) - self.assertTrue(network.ipv6('::')) - self.assertTrue(network.ipv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334')) - self.assertTrue(network.ipv6('2001:0db8:85a3::8a2e:0370:7334')) - self.assertTrue(network.ipv6('2001:67c:2e8::/48')) + self.assertTrue(network.ipv6("2001:db8:0:1:1:1:1:1")) + self.assertTrue(network.ipv6("0:0:0:0:0:0:0:1")) + self.assertTrue(network.ipv6("::1")) + self.assertTrue(network.ipv6("::")) + self.assertTrue(network.ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334")) + self.assertTrue(network.ipv6("2001:0db8:85a3::8a2e:0370:7334")) + self.assertTrue(network.ipv6("2001:67c:2e8::/48")) def test_parse_host_port(self): _ip = ipaddress.ip_address good_host_ports = { - '10.10.0.3': (_ip('10.10.0.3').compressed, None), - '10.10.0.3:1234': (_ip('10.10.0.3').compressed, 1234), - '2001:0db8:85a3::8a2e:0370:7334': (_ip('2001:0db8:85a3::8a2e:0370:7334').compressed, None), - '[2001:0db8:85a3::8a2e:0370:7334]:1234': (_ip('2001:0db8:85a3::8a2e:0370:7334').compressed, 1234), - '2001:0db8:85a3::7334': (_ip('2001:0db8:85a3::7334').compressed, None), - '[2001:0db8:85a3::7334]:1234': (_ip('2001:0db8:85a3::7334').compressed, 1234) + "10.10.0.3": (_ip("10.10.0.3").compressed, None), + "10.10.0.3:1234": (_ip("10.10.0.3").compressed, 1234), + "2001:0db8:85a3::8a2e:0370:7334": ( + _ip("2001:0db8:85a3::8a2e:0370:7334").compressed, + None, + ), + "[2001:0db8:85a3::8a2e:0370:7334]:1234": ( + _ip("2001:0db8:85a3::8a2e:0370:7334").compressed, + 1234, + ), + "2001:0db8:85a3::7334": (_ip("2001:0db8:85a3::7334").compressed, None), + "[2001:0db8:85a3::7334]:1234": ( + _ip("2001:0db8:85a3::7334").compressed, + 1234, + ), } bad_host_ports = [ - '10.10.0.3/24', - '10.10.0.3::1234', - '2001:0db8:0370:7334', - '2001:0db8:0370::7334]:1234', - '2001:0db8:0370:0:a:b:c:d:1234' + "10.10.0.3/24", + "10.10.0.3::1234", + "2001:0db8:0370:7334", + "2001:0db8:0370::7334]:1234", + "2001:0db8:0370:0:a:b:c:d:1234", ] for host_port, assertion_value in good_host_ports.items(): host = port = None @@ -259,71 +272,104 @@ class NetworkTestCase(TestCase): try: self.assertRaises(ValueError, network.parse_host_port, host_port) except AssertionError as _e_: - log.error('bad host_port value: "%s" failed to trigger ValueError exception', host_port) + log.error( + 'bad host_port value: "%s" failed to trigger ValueError exception', + host_port, + ) raise _e_ def test_dns_check(self): hosts = [ - {'host': '10.10.0.3', - 'port': '', - 'mocked': [(2, 1, 6, '', ('10.10.0.3', 0))], - 'ret': '10.10.0.3'}, - {'host': '10.10.0.3', - 'port': '1234', - 'mocked': [(2, 1, 6, '', ('10.10.0.3', 0))], - 'ret': '10.10.0.3'}, - {'host': '2001:0db8:85a3::8a2e:0370:7334', - 'port': '', - 'mocked': [(10, 1, 6, '', ('2001:db8:85a3::8a2e:370:7334', 0, 0, 0))], - 'ret': '[2001:db8:85a3::8a2e:370:7334]'}, - {'host': '2001:0db8:85a3::8a2e:370:7334', - 'port': '1234', - 'mocked': [(10, 1, 6, '', ('2001:db8:85a3::8a2e:370:7334', 0, 0, 0))], - 'ret': '[2001:db8:85a3::8a2e:370:7334]'}, - {'host': 'salt-master', - 'port': '1234', - 'mocked': [(2, 1, 6, '', ('127.0.0.1', 0))], - 'ret': '127.0.0.1'}, + { + "host": "10.10.0.3", + "port": "", + "mocked": [(2, 1, 6, "", ("10.10.0.3", 0))], + "ret": "10.10.0.3", + }, + { + "host": "10.10.0.3", + "port": "1234", + "mocked": [(2, 1, 6, "", ("10.10.0.3", 0))], + "ret": "10.10.0.3", + }, + { + "host": "2001:0db8:85a3::8a2e:0370:7334", + "port": "", + "mocked": [(10, 1, 6, "", ("2001:db8:85a3::8a2e:370:7334", 0, 0, 0))], + "ret": "[2001:db8:85a3::8a2e:370:7334]", + }, + { + "host": "2001:0db8:85a3::8a2e:370:7334", + "port": "1234", + "mocked": [(10, 1, 6, "", ("2001:db8:85a3::8a2e:370:7334", 0, 0, 0))], + "ret": "[2001:db8:85a3::8a2e:370:7334]", + }, + { + "host": "salt-master", + "port": "1234", + "mocked": [(2, 1, 6, "", ("127.0.0.1", 0))], + "ret": "127.0.0.1", + }, ] for host in hosts: - with patch.object(socket, 'getaddrinfo', create_autospec(socket.getaddrinfo, return_value=host['mocked'])): - with patch('socket.socket', create_autospec(socket.socket)): - ret = network.dns_check(host['host'], host['port']) - self.assertEqual(ret, host['ret']) + with patch.object( + socket, + "getaddrinfo", + create_autospec(socket.getaddrinfo, return_value=host["mocked"]), + ): + with patch("socket.socket", create_autospec(socket.socket)): + ret = network.dns_check(host["host"], host["port"]) + self.assertEqual(ret, host["ret"]) def test_dns_check_ipv6_filter(self): # raise exception to skip everything after the getaddrinfo call - with patch.object(socket, 'getaddrinfo', - create_autospec(socket.getaddrinfo, side_effect=Exception)) as getaddrinfo: + with patch.object( + socket, + "getaddrinfo", + create_autospec(socket.getaddrinfo, side_effect=Exception), + ) as getaddrinfo: for ipv6, param in [ (None, socket.AF_UNSPEC), (True, socket.AF_INET6), (False, socket.AF_INET), ]: with self.assertRaises(Exception): - network.dns_check('foo', '1', ipv6=ipv6) - getaddrinfo.assert_called_with('foo', '1', param, socket.SOCK_STREAM) + network.dns_check("foo", "1", ipv6=ipv6) + getaddrinfo.assert_called_with("foo", "1", param, socket.SOCK_STREAM) def test_dns_check_errors(self): - with patch.object(socket, 'getaddrinfo', create_autospec(socket.getaddrinfo, return_value=[])): - with self.assertRaisesRegex(salt.exceptions.SaltSystemExit, - "DNS lookup or connection check of 'foo' failed"): - network.dns_check('foo', '1') + with patch.object( + socket, "getaddrinfo", create_autospec(socket.getaddrinfo, return_value=[]) + ): + with self.assertRaisesRegex( + salt.exceptions.SaltSystemExit, + "DNS lookup or connection check of 'foo' failed", + ): + network.dns_check("foo", "1") - with patch.object(socket, 'getaddrinfo', create_autospec(socket.getaddrinfo, side_effect=TypeError)): - with self.assertRaisesRegex(salt.exceptions.SaltSystemExit, - "Invalid or unresolveable address"): - network.dns_check('foo', '1') + with patch.object( + socket, + "getaddrinfo", + create_autospec(socket.getaddrinfo, side_effect=TypeError), + ): + with self.assertRaisesRegex( + salt.exceptions.SaltSystemExit, "Invalid or unresolveable address" + ): + network.dns_check("foo", "1") def test_test_addrs(self): # subset of real data from getaddrinfo against saltstack.com - addrinfo = [(30, 2, 17, '', ('2600:9000:21eb:a800:8:1031:abc0:93a1', 0, 0, 0)), - (30, 1, 6, '', ('2600:9000:21eb:a800:8:1031:abc0:93a1', 0, 0, 0)), - (30, 2, 17, '', ('2600:9000:21eb:b400:8:1031:abc0:93a1', 0, 0, 0)), - (30, 1, 6, '', ('2600:9000:21eb:b400:8:1031:abc0:93a1', 0, 0, 0)), - (2, 1, 6, '', ('13.35.99.52', 0)), (2, 2, 17, '', ('13.35.99.85', 0)), - (2, 1, 6, '', ('13.35.99.85', 0)), (2, 2, 17, '', ('13.35.99.122', 0))] - with patch('socket.socket', create_autospec(socket.socket)) as s: + addrinfo = [ + (30, 2, 17, "", ("2600:9000:21eb:a800:8:1031:abc0:93a1", 0, 0, 0)), + (30, 1, 6, "", ("2600:9000:21eb:a800:8:1031:abc0:93a1", 0, 0, 0)), + (30, 2, 17, "", ("2600:9000:21eb:b400:8:1031:abc0:93a1", 0, 0, 0)), + (30, 1, 6, "", ("2600:9000:21eb:b400:8:1031:abc0:93a1", 0, 0, 0)), + (2, 1, 6, "", ("13.35.99.52", 0)), + (2, 2, 17, "", ("13.35.99.85", 0)), + (2, 1, 6, "", ("13.35.99.85", 0)), + (2, 2, 17, "", ("13.35.99.122", 0)), + ] + with patch("socket.socket", create_autospec(socket.socket)) as s: # we connect to the first address addrs = network._test_addrs(addrinfo, 80) self.assertTrue(len(addrs) == 1) @@ -343,361 +389,567 @@ class NetworkTestCase(TestCase): def test_is_subnet(self): for subnet_data in (IPV4_SUBNETS, IPV6_SUBNETS): for item in subnet_data[True]: - log.debug('Testing that %s is a valid subnet', item) + log.debug("Testing that %s is a valid subnet", item) self.assertTrue(network.is_subnet(item)) for item in subnet_data[False]: - log.debug('Testing that %s is not a valid subnet', item) + log.debug("Testing that %s is not a valid subnet", item) self.assertFalse(network.is_subnet(item)) def test_is_ipv4_subnet(self): for item in IPV4_SUBNETS[True]: - log.debug('Testing that %s is a valid subnet', item) + log.debug("Testing that %s is a valid subnet", item) self.assertTrue(network.is_ipv4_subnet(item)) for item in IPV4_SUBNETS[False]: - log.debug('Testing that %s is not a valid subnet', item) + log.debug("Testing that %s is not a valid subnet", item) self.assertFalse(network.is_ipv4_subnet(item)) def test_is_ipv6_subnet(self): for item in IPV6_SUBNETS[True]: - log.debug('Testing that %s is a valid subnet', item) + log.debug("Testing that %s is a valid subnet", item) self.assertTrue(network.is_ipv6_subnet(item)) for item in IPV6_SUBNETS[False]: - log.debug('Testing that %s is not a valid subnet', item) + log.debug("Testing that %s is not a valid subnet", item) self.assertFalse(network.is_ipv6_subnet(item)) def test_cidr_to_ipv4_netmask(self): - self.assertEqual(network.cidr_to_ipv4_netmask(24), '255.255.255.0') - self.assertEqual(network.cidr_to_ipv4_netmask(21), '255.255.248.0') - self.assertEqual(network.cidr_to_ipv4_netmask(17), '255.255.128.0') - self.assertEqual(network.cidr_to_ipv4_netmask(9), '255.128.0.0') - self.assertEqual(network.cidr_to_ipv4_netmask(36), '') - self.assertEqual(network.cidr_to_ipv4_netmask('lol'), '') + self.assertEqual(network.cidr_to_ipv4_netmask(24), "255.255.255.0") + self.assertEqual(network.cidr_to_ipv4_netmask(21), "255.255.248.0") + self.assertEqual(network.cidr_to_ipv4_netmask(17), "255.255.128.0") + self.assertEqual(network.cidr_to_ipv4_netmask(9), "255.128.0.0") + self.assertEqual(network.cidr_to_ipv4_netmask(36), "") + self.assertEqual(network.cidr_to_ipv4_netmask("lol"), "") def test_number_of_set_bits_to_ipv4_netmask(self): - set_bits_to_netmask = network._number_of_set_bits_to_ipv4_netmask(0xffffff00) - self.assertEqual(set_bits_to_netmask, '255.255.255.0') - set_bits_to_netmask = network._number_of_set_bits_to_ipv4_netmask(0xffff6400) + set_bits_to_netmask = network._number_of_set_bits_to_ipv4_netmask(0xFFFFFF00) + self.assertEqual(set_bits_to_netmask, "255.255.255.0") + set_bits_to_netmask = network._number_of_set_bits_to_ipv4_netmask(0xFFFF6400) def test_hex2ip(self): - self.assertEqual(network.hex2ip('0x4A7D2B63'), '74.125.43.99') - self.assertEqual(network.hex2ip('0x4A7D2B63', invert=True), '99.43.125.74') - self.assertEqual(network.hex2ip('00000000000000000000FFFF7F000001'), '127.0.0.1') - self.assertEqual(network.hex2ip('0000000000000000FFFF00000100007F', invert=True), '127.0.0.1') - self.assertEqual(network.hex2ip('20010DB8000000000000000000000000'), '2001:db8::') - self.assertEqual(network.hex2ip('B80D0120000000000000000000000000', invert=True), '2001:db8::') + self.assertEqual(network.hex2ip("0x4A7D2B63"), "74.125.43.99") + self.assertEqual(network.hex2ip("0x4A7D2B63", invert=True), "99.43.125.74") + self.assertEqual( + network.hex2ip("00000000000000000000FFFF7F000001"), "127.0.0.1" + ) + self.assertEqual( + network.hex2ip("0000000000000000FFFF00000100007F", invert=True), "127.0.0.1" + ) + self.assertEqual( + network.hex2ip("20010DB8000000000000000000000000"), "2001:db8::" + ) + self.assertEqual( + network.hex2ip("B80D0120000000000000000000000000", invert=True), + "2001:db8::", + ) def test_interfaces_ifconfig_linux(self): interfaces = network._interfaces_ifconfig(LINUX) - self.assertEqual(interfaces, - {'eth0': {'hwaddr': 'e0:3f:49:85:6a:af', - 'inet': [{'address': '10.10.10.56', - 'broadcast': '10.10.10.255', - 'netmask': '255.255.252.0'}], - 'inet6': [{'address': 'fe80::e23f:49ff:fe85:6aaf', - 'prefixlen': '64', - 'scope': 'link'}], - 'up': True}, - 'lo': {'inet': [{'address': '127.0.0.1', - 'netmask': '255.0.0.0'}], - 'inet6': [{'address': '::1', - 'prefixlen': '128', - 'scope': 'host'}], - 'up': True}} + self.assertEqual( + interfaces, + { + "eth0": { + "hwaddr": "e0:3f:49:85:6a:af", + "inet": [ + { + "address": "10.10.10.56", + "broadcast": "10.10.10.255", + "netmask": "255.255.252.0", + } + ], + "inet6": [ + { + "address": "fe80::e23f:49ff:fe85:6aaf", + "prefixlen": "64", + "scope": "link", + } + ], + "up": True, + }, + "lo": { + "inet": [{"address": "127.0.0.1", "netmask": "255.0.0.0"}], + "inet6": [{"address": "::1", "prefixlen": "128", "scope": "host"}], + "up": True, + }, + }, ) def test_interfaces_ifconfig_freebsd(self): interfaces = network._interfaces_ifconfig(FREEBSD) - self.assertEqual(interfaces, - {'': {'up': False}, - 'em0': {'hwaddr': '00:30:48:ff:ff:ff', - 'inet': [{'address': '10.10.10.250', - 'broadcast': '10.10.10.255', - 'netmask': '255.255.255.224'}, - {'address': '10.10.10.56', - 'broadcast': '10.10.10.63', - 'netmask': '255.255.255.192'}], - 'up': True}, - 'em1': {'hwaddr': '00:30:48:aa:aa:aa', - 'up': False}, - 'lo0': {'inet': [{'address': '127.0.0.1', - 'netmask': '255.0.0.0'}], - 'inet6': [{'address': 'fe80::1', - 'prefixlen': '64', - 'scope': '0x8'}, - {'address': '::1', - 'prefixlen': '128', - 'scope': None}], - 'up': True}, - 'plip0': {'up': False}, - 'tun0': {'inet': [{'address': '10.12.0.1', - 'netmask': '255.255.255.255'}], - 'up': True}} - + self.assertEqual( + interfaces, + { + "": {"up": False}, + "em0": { + "hwaddr": "00:30:48:ff:ff:ff", + "inet": [ + { + "address": "10.10.10.250", + "broadcast": "10.10.10.255", + "netmask": "255.255.255.224", + }, + { + "address": "10.10.10.56", + "broadcast": "10.10.10.63", + "netmask": "255.255.255.192", + }, + ], + "up": True, + }, + "em1": {"hwaddr": "00:30:48:aa:aa:aa", "up": False}, + "lo0": { + "inet": [{"address": "127.0.0.1", "netmask": "255.0.0.0"}], + "inet6": [ + {"address": "fe80::1", "prefixlen": "64", "scope": "0x8"}, + {"address": "::1", "prefixlen": "128", "scope": None}, + ], + "up": True, + }, + "plip0": {"up": False}, + "tun0": { + "inet": [{"address": "10.12.0.1", "netmask": "255.255.255.255"}], + "up": True, + }, + }, ) def test_interfaces_ifconfig_solaris(self): - with patch('salt.utils.platform.is_sunos', lambda: True): + with patch("salt.utils.platform.is_sunos", lambda: True): interfaces = network._interfaces_ifconfig(SOLARIS) - expected_interfaces = {'ilbint0': - {'inet6': [], - 'inet': [{'broadcast': '10.6.0.255', - 'netmask': '255.255.255.0', - 'address': '10.6.0.11'}], - 'up': True}, - 'lo0': - {'inet6': [{'prefixlen': '128', - 'address': '::1'}], - 'inet': [{'netmask': '255.0.0.0', - 'address': '127.0.0.1'}], - 'up': True}, - 'ilbext0': {'inet6': [], - 'inet': [{'broadcast': '10.10.11.31', - 'netmask': '255.255.255.224', - 'address': '10.10.11.11'}, - {'broadcast': '10.10.11.31', - 'netmask': '255.255.255.224', - 'address': '10.10.11.12'}], - 'up': True}, - 'vpn0': {'inet6': [], - 'inet': [{'netmask': '255.0.0.0', - 'address': '10.6.0.14'}], - 'up': True}, - 'net0': {'inet6': [{'prefixlen': '10', - 'address': 'fe80::221:9bff:fefd:2a22'}], - 'inet': [{'broadcast': '10.10.10.63', - 'netmask': '255.255.255.224', - 'address': '10.10.10.38'}], - 'up': True}} + expected_interfaces = { + "ilbint0": { + "inet6": [], + "inet": [ + { + "broadcast": "10.6.0.255", + "netmask": "255.255.255.0", + "address": "10.6.0.11", + } + ], + "up": True, + }, + "lo0": { + "inet6": [{"prefixlen": "128", "address": "::1"}], + "inet": [{"netmask": "255.0.0.0", "address": "127.0.0.1"}], + "up": True, + }, + "ilbext0": { + "inet6": [], + "inet": [ + { + "broadcast": "10.10.11.31", + "netmask": "255.255.255.224", + "address": "10.10.11.11", + }, + { + "broadcast": "10.10.11.31", + "netmask": "255.255.255.224", + "address": "10.10.11.12", + }, + ], + "up": True, + }, + "vpn0": { + "inet6": [], + "inet": [{"netmask": "255.0.0.0", "address": "10.6.0.14"}], + "up": True, + }, + "net0": { + "inet6": [ + {"prefixlen": "10", "address": "fe80::221:9bff:fefd:2a22"} + ], + "inet": [ + { + "broadcast": "10.10.10.63", + "netmask": "255.255.255.224", + "address": "10.10.10.38", + } + ], + "up": True, + }, + } self.assertEqual(interfaces, expected_interfaces) def test_interfaces_ifconfig_netbsd(self): interfaces = network._netbsd_interfaces_ifconfig(NETBSD) - self.assertEqual(interfaces, - {'lo0': {'inet': [{'address': '127.0.0.1', 'netmask': '255.0.0.0'}], - 'inet6': [{'address': 'fe80::1', - 'prefixlen': '64', - 'scope': 'lo0'}], - 'up': True}, - 'vioif0': {'hwaddr': '00:a0:98:e6:83:18', - 'inet': [{'address': '192.168.1.80', - 'broadcast': '192.168.1.255', - 'netmask': '255.255.255.0'}], - 'inet6': [{'address': 'fe80::2a0:98ff:fee6:8318', - 'prefixlen': '64', - 'scope': 'vioif0'}], - 'up': True}} + self.assertEqual( + interfaces, + { + "lo0": { + "inet": [{"address": "127.0.0.1", "netmask": "255.0.0.0"}], + "inet6": [ + {"address": "fe80::1", "prefixlen": "64", "scope": "lo0"} + ], + "up": True, + }, + "vioif0": { + "hwaddr": "00:a0:98:e6:83:18", + "inet": [ + { + "address": "192.168.1.80", + "broadcast": "192.168.1.255", + "netmask": "255.255.255.0", + } + ], + "inet6": [ + { + "address": "fe80::2a0:98ff:fee6:8318", + "prefixlen": "64", + "scope": "vioif0", + } + ], + "up": True, + }, + }, ) def test_freebsd_remotes_on(self): - with patch('salt.utils.platform.is_sunos', lambda: False): - with patch('salt.utils.platform.is_freebsd', lambda: True): - with patch('subprocess.check_output', - return_value=FREEBSD_SOCKSTAT): - remotes = network._freebsd_remotes_on('4506', 'remote') - self.assertEqual(remotes, set(['127.0.0.1'])) + with patch("salt.utils.platform.is_sunos", lambda: False): + with patch("salt.utils.platform.is_freebsd", lambda: True): + with patch("subprocess.check_output", return_value=FREEBSD_SOCKSTAT): + remotes = network._freebsd_remotes_on("4506", "remote") + self.assertEqual(remotes, set(["127.0.0.1"])) def test_freebsd_remotes_on_with_fat_pid(self): - with patch('salt.utils.platform.is_sunos', lambda: False): - with patch('salt.utils.platform.is_freebsd', lambda: True): - with patch('subprocess.check_output', - return_value=FREEBSD_SOCKSTAT_WITH_FAT_PID): - remotes = network._freebsd_remotes_on('4506', 'remote') - self.assertEqual(remotes, set(['127.0.0.1'])) + with patch("salt.utils.platform.is_sunos", lambda: False): + with patch("salt.utils.platform.is_freebsd", lambda: True): + with patch( + "subprocess.check_output", + return_value=FREEBSD_SOCKSTAT_WITH_FAT_PID, + ): + remotes = network._freebsd_remotes_on("4506", "remote") + self.assertEqual(remotes, set(["127.0.0.1"])) def test_netlink_tool_remote_on_a(self): - with patch('salt.utils.platform.is_sunos', lambda: False): - with patch('salt.utils.platform.is_linux', lambda: True): - with patch('subprocess.check_output', - return_value=LINUX_NETLINK_SS_OUTPUT): - remotes = network._netlink_tool_remote_on('4506', 'local') - self.assertEqual(remotes, set(['192.168.122.177', '::ffff:127.0.0.1'])) + with patch("salt.utils.platform.is_sunos", lambda: False): + with patch("salt.utils.platform.is_linux", lambda: True): + with patch( + "subprocess.check_output", return_value=LINUX_NETLINK_SS_OUTPUT + ): + remotes = network._netlink_tool_remote_on("4506", "local") + self.assertEqual( + remotes, set(["192.168.122.177", "::ffff:127.0.0.1"]) + ) def test_netlink_tool_remote_on_b(self): - with patch('subprocess.check_output', return_value=NETLINK_SS): - remotes = network._netlink_tool_remote_on('4505', 'remote_port') - self.assertEqual(remotes, set(['127.0.0.1', '::ffff:1.2.3.4'])) + with patch("subprocess.check_output", return_value=NETLINK_SS): + remotes = network._netlink_tool_remote_on("4505", "remote_port") + self.assertEqual(remotes, set(["127.0.0.1", "::ffff:1.2.3.4"])) def test_generate_minion_id_distinct(self): - ''' + """ Test if minion IDs are distinct in the pool. :return: - ''' - with patch('platform.node', MagicMock(return_value='nodename')), \ - patch('socket.gethostname', MagicMock(return_value='hostname')), \ - patch('socket.getfqdn', MagicMock(return_value='hostname.domainname.blank')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'attrname', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '5.6.7.8'])): - self.assertEqual(network._generate_minion_id(), - ['hostname.domainname.blank', 'nodename', 'hostname', '1.2.3.4', '5.6.7.8']) + """ + with patch("platform.node", MagicMock(return_value="nodename")), patch( + "socket.gethostname", MagicMock(return_value="hostname") + ), patch( + "socket.getfqdn", MagicMock(return_value="hostname.domainname.blank") + ), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "attrname", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock(return_value=["1.2.3.4", "5.6.7.8"]), + ): + self.assertEqual( + network._generate_minion_id(), + [ + "hostname.domainname.blank", + "nodename", + "hostname", + "1.2.3.4", + "5.6.7.8", + ], + ) def test_generate_minion_id_127_name(self): - ''' + """ Test if minion IDs can be named 127.foo :return: - ''' - with patch('platform.node', MagicMock(return_value='127')), \ - patch('socket.gethostname', MagicMock(return_value='127')), \ - patch('socket.getfqdn', MagicMock(return_value='127.domainname.blank')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'attrname', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '5.6.7.8'])): - self.assertEqual(network._generate_minion_id(), - ['127.domainname.blank', '127', '1.2.3.4', '5.6.7.8']) + """ + with patch("platform.node", MagicMock(return_value="127")), patch( + "socket.gethostname", MagicMock(return_value="127") + ), patch( + "socket.getfqdn", MagicMock(return_value="127.domainname.blank") + ), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "attrname", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock(return_value=["1.2.3.4", "5.6.7.8"]), + ): + self.assertEqual( + network._generate_minion_id(), + ["127.domainname.blank", "127", "1.2.3.4", "5.6.7.8"], + ) def test_generate_minion_id_127_name_startswith(self): - ''' + """ Test if minion IDs can be named starting from "127" :return: - ''' - with patch('platform.node', MagicMock(return_value='127890')), \ - patch('socket.gethostname', MagicMock(return_value='127890')), \ - patch('socket.getfqdn', MagicMock(return_value='127890.domainname.blank')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'attrname', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '5.6.7.8'])): - self.assertEqual(network._generate_minion_id(), - ['127890.domainname.blank', '127890', '1.2.3.4', '5.6.7.8']) + """ + with patch("platform.node", MagicMock(return_value="127890")), patch( + "socket.gethostname", MagicMock(return_value="127890") + ), patch( + "socket.getfqdn", MagicMock(return_value="127890.domainname.blank") + ), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "attrname", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock(return_value=["1.2.3.4", "5.6.7.8"]), + ): + self.assertEqual( + network._generate_minion_id(), + ["127890.domainname.blank", "127890", "1.2.3.4", "5.6.7.8"], + ) def test_generate_minion_id_duplicate(self): - ''' + """ Test if IP addresses in the minion IDs are distinct in the pool :return: - ''' - with patch('platform.node', MagicMock(return_value='hostname')), \ - patch('socket.gethostname', MagicMock(return_value='hostname')), \ - patch('socket.getfqdn', MagicMock(return_value='hostname')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'hostname', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '1.2.3.4', '1.2.3.4'])): - self.assertEqual(network._generate_minion_id(), ['hostname', '1.2.3.4']) + """ + with patch("platform.node", MagicMock(return_value="hostname")), patch( + "socket.gethostname", MagicMock(return_value="hostname") + ), patch("socket.getfqdn", MagicMock(return_value="hostname")), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "hostname", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock(return_value=["1.2.3.4", "1.2.3.4", "1.2.3.4"]), + ): + self.assertEqual(network._generate_minion_id(), ["hostname", "1.2.3.4"]) def test_generate_minion_id_platform_used(self): - ''' + """ Test if platform.node is used for the first occurrence. The platform.node is most common hostname resolver before anything else. :return: - ''' - with patch('platform.node', MagicMock(return_value='very.long.and.complex.domain.name')), \ - patch('socket.gethostname', MagicMock(return_value='hostname')), \ - patch('socket.getfqdn', MagicMock(return_value='')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'hostname', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '1.2.3.4', '1.2.3.4'])): - self.assertEqual(network.generate_minion_id(), 'very.long.and.complex.domain.name') + """ + with patch( + "platform.node", MagicMock(return_value="very.long.and.complex.domain.name") + ), patch("socket.gethostname", MagicMock(return_value="hostname")), patch( + "socket.getfqdn", MagicMock(return_value="") + ), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "hostname", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock(return_value=["1.2.3.4", "1.2.3.4", "1.2.3.4"]), + ): + self.assertEqual( + network.generate_minion_id(), "very.long.and.complex.domain.name" + ) def test_generate_minion_id_platform_localhost_filtered(self): - ''' + """ Test if localhost is filtered from the first occurrence. :return: - ''' - with patch('platform.node', MagicMock(return_value='localhost')), \ - patch('socket.gethostname', MagicMock(return_value='pick.me')), \ - patch('socket.getfqdn', MagicMock(return_value='hostname.domainname.blank')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'hostname', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['1.2.3.4', '1.2.3.4', '1.2.3.4'])): - self.assertEqual(network.generate_minion_id(), 'hostname.domainname.blank') + """ + with patch("platform.node", MagicMock(return_value="localhost")), patch( + "socket.gethostname", MagicMock(return_value="pick.me") + ), patch( + "socket.getfqdn", MagicMock(return_value="hostname.domainname.blank") + ), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "hostname", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock(return_value=["1.2.3.4", "1.2.3.4", "1.2.3.4"]), + ): + self.assertEqual(network.generate_minion_id(), "hostname.domainname.blank") def test_generate_minion_id_platform_localhost_filtered_all(self): - ''' + """ Test if any of the localhost is filtered from everywhere. :return: - ''' - with patch('platform.node', MagicMock(return_value='localhost')), \ - patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \ - patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1', '1.2.3.4'])): - self.assertEqual(network.generate_minion_id(), '1.2.3.4') + """ + with patch("platform.node", MagicMock(return_value="localhost")), patch( + "socket.gethostname", MagicMock(return_value="ip6-loopback") + ), patch("socket.getfqdn", MagicMock(return_value="ip6-localhost")), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "localhost", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock( + return_value=["127.0.0.1", "::1", "fe00::0", "fe02::1", "1.2.3.4"] + ), + ): + self.assertEqual(network.generate_minion_id(), "1.2.3.4") def test_generate_minion_id_platform_localhost_only(self): - ''' + """ Test if there is no other choice but localhost. :return: - ''' - with patch('platform.node', MagicMock(return_value='localhost')), \ - patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \ - patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1'])): - self.assertEqual(network.generate_minion_id(), 'localhost') + """ + with patch("platform.node", MagicMock(return_value="localhost")), patch( + "socket.gethostname", MagicMock(return_value="ip6-loopback") + ), patch("socket.getfqdn", MagicMock(return_value="ip6-localhost")), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "localhost", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock(return_value=["127.0.0.1", "::1", "fe00::0", "fe02::1"]), + ): + self.assertEqual(network.generate_minion_id(), "localhost") def test_generate_minion_id_platform_fqdn(self): - ''' + """ Test if fqdn is picked up. :return: - ''' - with patch('platform.node', MagicMock(return_value='localhost')), \ - patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \ - patch('socket.getfqdn', MagicMock(return_value='pick.me')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1'])): - self.assertEqual(network.generate_minion_id(), 'pick.me') + """ + with patch("platform.node", MagicMock(return_value="localhost")), patch( + "socket.gethostname", MagicMock(return_value="ip6-loopback") + ), patch("socket.getfqdn", MagicMock(return_value="pick.me")), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "localhost", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock(return_value=["127.0.0.1", "::1", "fe00::0", "fe02::1"]), + ): + self.assertEqual(network.generate_minion_id(), "pick.me") def test_generate_minion_id_platform_localhost_addrinfo(self): - ''' + """ Test if addinfo is picked up. :return: - ''' - with patch('platform.node', MagicMock(return_value='localhost')), \ - patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \ - patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'pick.me', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1'])): - self.assertEqual(network.generate_minion_id(), 'pick.me') + """ + with patch("platform.node", MagicMock(return_value="localhost")), patch( + "socket.gethostname", MagicMock(return_value="ip6-loopback") + ), patch("socket.getfqdn", MagicMock(return_value="ip6-localhost")), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "pick.me", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock(return_value=["127.0.0.1", "::1", "fe00::0", "fe02::1"]), + ): + self.assertEqual(network.generate_minion_id(), "pick.me") def test_generate_minion_id_platform_ip_addr_only(self): - ''' + """ Test if IP address is the only what is used as a Minion ID in case no DNS name. :return: - ''' - with patch('platform.node', MagicMock(return_value='localhost')), \ - patch('socket.gethostname', MagicMock(return_value='ip6-loopback')), \ - patch('socket.getfqdn', MagicMock(return_value='ip6-localhost')), \ - patch('socket.getaddrinfo', MagicMock(return_value=[(2, 3, 0, 'localhost', ('127.0.1.1', 0))])), \ - patch('salt.utils.files.fopen', mock_open()), \ - patch('salt.utils.network.ip_addrs', MagicMock(return_value=['127.0.0.1', '::1', 'fe00::0', 'fe02::1', '1.2.3.4'])): - self.assertEqual(network.generate_minion_id(), '1.2.3.4') + """ + with patch("platform.node", MagicMock(return_value="localhost")), patch( + "socket.gethostname", MagicMock(return_value="ip6-loopback") + ), patch("socket.getfqdn", MagicMock(return_value="ip6-localhost")), patch( + "socket.getaddrinfo", + MagicMock(return_value=[(2, 3, 0, "localhost", ("127.0.1.1", 0))]), + ), patch( + "salt.utils.files.fopen", mock_open() + ), patch( + "salt.utils.network.ip_addrs", + MagicMock( + return_value=["127.0.0.1", "::1", "fe00::0", "fe02::1", "1.2.3.4"] + ), + ): + self.assertEqual(network.generate_minion_id(), "1.2.3.4") def test_gen_mac(self): - with patch('random.randint', return_value=1) as random_mock: + with patch("random.randint", return_value=1) as random_mock: self.assertEqual(random_mock.return_value, 1) - ret = network.gen_mac('00:16:3E') - expected_mac = '00:16:3E:01:01:01' + ret = network.gen_mac("00:16:3E") + expected_mac = "00:16:3E:01:01:01" self.assertEqual(ret, expected_mac) def test_mac_str_to_bytes(self): - self.assertRaises(ValueError, network.mac_str_to_bytes, '31337') - self.assertRaises(ValueError, network.mac_str_to_bytes, '0001020304056') - self.assertRaises(ValueError, network.mac_str_to_bytes, '00:01:02:03:04:056') - self.assertRaises(ValueError, network.mac_str_to_bytes, 'a0:b0:c0:d0:e0:fg') - self.assertEqual(b'\x10\x08\x06\x04\x02\x00', network.mac_str_to_bytes('100806040200')) - self.assertEqual(b'\xf8\xe7\xd6\xc5\xb4\xa3', network.mac_str_to_bytes('f8e7d6c5b4a3')) + self.assertRaises(ValueError, network.mac_str_to_bytes, "31337") + self.assertRaises(ValueError, network.mac_str_to_bytes, "0001020304056") + self.assertRaises(ValueError, network.mac_str_to_bytes, "00:01:02:03:04:056") + self.assertRaises(ValueError, network.mac_str_to_bytes, "a0:b0:c0:d0:e0:fg") + self.assertEqual( + b"\x10\x08\x06\x04\x02\x00", network.mac_str_to_bytes("100806040200") + ) + self.assertEqual( + b"\xf8\xe7\xd6\xc5\xb4\xa3", network.mac_str_to_bytes("f8e7d6c5b4a3") + ) def test_generate_minion_id_with_long_hostname(self): - ''' + """ Validate the fix for: https://github.com/saltstack/salt/issues/51160 - ''' - long_name = 'localhost-abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz' - with patch('socket.gethostname', MagicMock(return_value=long_name)): + """ + long_name = "localhost-abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuvwxyz" + with patch("socket.gethostname", MagicMock(return_value=long_name)): # An exception is raised if unicode is passed to socket.getfqdn minion_id = network.generate_minion_id() - assert minion_id != '', minion_id + assert minion_id != "", minion_id + + def test_filter_by_networks_with_no_filter(self): + ips = ["10.0.123.200", "10.10.10.10"] + with pytest.raises(TypeError): + network.filter_by_networks(ips) # pylint: disable=no-value-for-parameter + + def test_filter_by_networks_empty_filter(self): + ips = ["10.0.123.200", "10.10.10.10"] + assert network.filter_by_networks(ips, []) == [] + + def test_filter_by_networks_ips_list(self): + ips = [ + "10.0.123.200", + "10.10.10.10", + "193.124.233.5", + "fe80::d210:cf3f:64e7:5423", + ] + networks = ["10.0.0.0/8", "fe80::/64"] + assert network.filter_by_networks(ips, networks) == [ + "10.0.123.200", + "10.10.10.10", + "fe80::d210:cf3f:64e7:5423", + ] + + def test_filter_by_networks_interfaces_dict(self): + interfaces = { + "wlan0": ["192.168.1.100", "217.5.140.67", "2001:db8::ff00:42:8329"], + "eth0": [ + "2001:0DB8:0:CD30:123:4567:89AB:CDEF", + "192.168.1.101", + "10.0.123.201", + ], + } + assert network.filter_by_networks( + interfaces, ["192.168.1.0/24", "2001:db8::/48"] + ) == { + "wlan0": ["192.168.1.100", "2001:db8::ff00:42:8329"], + "eth0": ["2001:0DB8:0:CD30:123:4567:89AB:CDEF", "192.168.1.101"], + } + + def test_filter_by_networks_catch_all(self): + ips = [ + "10.0.123.200", + "10.10.10.10", + "193.124.233.5", + "fe80::d210:cf3f:64e7:5423", + ] + assert ips == network.filter_by_networks(ips, ["0.0.0.0/0", "::/0"]) diff --git a/tests/unit/utils/test_parsers.py b/tests/unit/utils/test_parsers.py index 25ff48eb066..0f234f3e8fa 100644 --- a/tests/unit/utils/test_parsers.py +++ b/tests/unit/utils/test_parsers.py @@ -1,112 +1,123 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Denys Havrysh <denys.gavrysh@gmail.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import os import shutil import tempfile -# Import Salt Testing Libs -from tests.support.unit import skipIf, TestCase -from tests.support.runtests import RUNTIME_VARS -from tests.support.mock import ( - MagicMock, - patch, -) + +import salt.config # Import Salt Libs import salt.log.setup as log -import salt.config import salt.syspaths import salt.utils.parsers import salt.utils.platform +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf class ErrorMock(object): # pylint: disable=too-few-public-methods - ''' + """ Error handling - ''' + """ + def __init__(self): - ''' + """ init - ''' + """ self.msg = None def error(self, msg): - ''' + """ Capture error message - ''' + """ self.msg = msg class LogSetupMock(object): - ''' + """ Logger setup - ''' + """ + def __init__(self): - ''' + """ init - ''' + """ self.log_level = None self.log_file = None self.log_level_logfile = None self.config = {} self.temp_log_level = None - def setup_console_logger(self, log_level='error', **kwargs): # pylint: disable=unused-argument - ''' + def setup_console_logger( + self, log_level="error", **kwargs + ): # pylint: disable=unused-argument + """ Set console loglevel - ''' + """ self.log_level = log_level def setup_extended_logging(self, opts): - ''' + """ Set opts - ''' + """ self.config = opts - def setup_logfile_logger(self, logfile, loglevel, **kwargs): # pylint: disable=unused-argument - ''' + def setup_logfile_logger( + self, logfile, loglevel, **kwargs + ): # pylint: disable=unused-argument + """ Set logfile and loglevel - ''' + """ self.log_file = logfile self.log_level_logfile = loglevel @staticmethod def get_multiprocessing_logging_queue(): # pylint: disable=invalid-name - ''' + """ Mock - ''' + """ import multiprocessing + return multiprocessing.Queue() - def setup_multiprocessing_logging_listener(self, opts, *args): # pylint: disable=invalid-name,unused-argument - ''' + def setup_multiprocessing_logging_listener( + self, opts, *args + ): # pylint: disable=invalid-name,unused-argument + """ Set opts - ''' + """ self.config = opts - def setup_temp_logger(self, log_level='error'): - ''' + def setup_temp_logger(self, log_level="error"): + """ Set temp loglevel - ''' + """ self.temp_log_level = log_level class ObjectView(object): # pylint: disable=too-few-public-methods - ''' + """ Dict object view - ''' + """ + def __init__(self, d): self.__dict__ = d class ParserBase(object): - ''' + """ Unit Tests for Log Level Mixin with Salt parsers - ''' + """ + args = [] skip_console_logging_config = False @@ -114,9 +125,11 @@ class ParserBase(object): log_setup = None # Set config option names - loglevel_config_setting_name = 'log_level' - logfile_config_setting_name = 'log_file' - logfile_loglevel_config_setting_name = 'log_level_logfile' # pylint: disable=invalid-name + loglevel_config_setting_name = "log_level" + logfile_config_setting_name = "log_file" + logfile_loglevel_config_setting_name = ( + "log_level_logfile" # pylint: disable=invalid-name + ) @classmethod def setUpClass(cls): @@ -127,16 +140,18 @@ class ParserBase(object): shutil.rmtree(cls.root_dir, ignore_errors=True) def setup_log(self): - ''' + """ Mock logger functions - ''' + """ testing_config = self.default_config.copy() - testing_config['root_dir'] = self.root_dir - for name in ('pki_dir', 'cachedir'): + testing_config["root_dir"] = self.root_dir + for name in ("pki_dir", "cachedir"): testing_config[name] = name - testing_config[self.logfile_config_setting_name] = getattr(self, self.logfile_config_setting_name, self.log_file) + testing_config[self.logfile_config_setting_name] = getattr( + self, self.logfile_config_setting_name, self.log_file + ) self.testing_config = testing_config - self.addCleanup(setattr, self, 'testing_config', None) + self.addCleanup(setattr, self, "testing_config", None) self.log_setup = LogSetupMock() patcher = patch.multiple( log, @@ -145,29 +160,29 @@ class ParserBase(object): setup_logfile_logger=self.log_setup.setup_logfile_logger, get_multiprocessing_logging_queue=self.log_setup.get_multiprocessing_logging_queue, setup_multiprocessing_logging_listener=self.log_setup.setup_multiprocessing_logging_listener, - setup_temp_logger=self.log_setup.setup_temp_logger + setup_temp_logger=self.log_setup.setup_temp_logger, ) patcher.start() self.addCleanup(patcher.stop) - self.addCleanup(setattr, self, 'log_setup', None) + self.addCleanup(setattr, self, "log_setup", None) # log level configuration tests def test_get_log_level_cli(self): - ''' + """ Tests that log level match command-line specified value - ''' + """ # Set defaults default_log_level = self.testing_config[self.loglevel_config_setting_name] # Set log level in CLI - log_level = 'critical' - args = ['--log-level', log_level] + self.args + log_level = "critical" + args = ["--log-level", log_level] + self.args parser = self.parser() with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() console_log_level = getattr(parser.options, self.loglevel_config_setting_name) @@ -176,27 +191,28 @@ class ParserBase(object): self.assertEqual(console_log_level, log_level) # Check console loggger log level self.assertEqual(self.log_setup.log_level, log_level) - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - log_level) + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], log_level + ) self.assertEqual(self.log_setup.temp_log_level, log_level) # Check log file logger log level self.assertEqual(self.log_setup.log_level_logfile, default_log_level) def test_get_log_level_config(self): - ''' + """ Tests that log level match the configured value - ''' + """ args = self.args # Set log level in config - log_level = 'info' + log_level = "info" opts = self.testing_config.copy() opts.update({self.loglevel_config_setting_name: log_level}) parser = self.parser() with patch(self.config_func, MagicMock(return_value=opts)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() console_log_level = getattr(parser.options, self.loglevel_config_setting_name) @@ -205,25 +221,28 @@ class ParserBase(object): self.assertEqual(console_log_level, log_level) # Check console loggger log level self.assertEqual(self.log_setup.log_level, log_level) - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - log_level) - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], log_level + ) + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file logger log level self.assertEqual(self.log_setup.log_level_logfile, log_level) def test_get_log_level_default(self): - ''' + """ Tests that log level match the default value - ''' + """ # Set defaults - log_level = default_log_level = self.testing_config[self.loglevel_config_setting_name] + log_level = default_log_level = self.testing_config[ + self.loglevel_config_setting_name + ] args = self.args parser = self.parser() with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() console_log_level = getattr(parser.options, self.loglevel_config_setting_name) @@ -233,32 +252,35 @@ class ParserBase(object): # Check console loggger log level self.assertEqual(self.log_setup.log_level, log_level) # Check extended logger - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - log_level) - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], log_level + ) + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file logger self.assertEqual(self.log_setup.log_level_logfile, default_log_level) # Check help message - self.assertIn('Default: \'{0}\'.'.format(default_log_level), - parser.get_option('--log-level').help) + self.assertIn( + "Default: '{0}'.".format(default_log_level), + parser.get_option("--log-level").help, + ) # log file configuration tests def test_get_log_file_cli(self): - ''' + """ Tests that log file match command-line specified value - ''' + """ # Set defaults log_level = self.testing_config[self.loglevel_config_setting_name] # Set log file in CLI - log_file = '{0}_cli.log'.format(self.log_file) - args = ['--log-file', log_file] + self.args + log_file = "{0}_cli.log".format(self.log_file) + args = ["--log-file", log_file] + self.args parser = self.parser() with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() log_file_option = getattr(parser.options, self.logfile_config_setting_name) @@ -267,35 +289,37 @@ class ParserBase(object): # Check console loggger self.assertEqual(self.log_setup.log_level, log_level) # Check extended logger - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - log_level) - self.assertEqual(self.log_setup.config[self.logfile_config_setting_name], - log_file) + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], log_level + ) + self.assertEqual( + self.log_setup.config[self.logfile_config_setting_name], log_file + ) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file setting self.assertEqual(log_file_option, log_file) # Check log file logger self.assertEqual(self.log_setup.log_file, log_file) def test_get_log_file_config(self): - ''' + """ Tests that log file match the configured value - ''' + """ # Set defaults log_level = self.testing_config[self.loglevel_config_setting_name] args = self.args # Set log file in config - log_file = '{0}_config.log'.format(self.log_file) + log_file = "{0}_config.log".format(self.log_file) opts = self.testing_config.copy() opts.update({self.logfile_config_setting_name: log_file}) parser = self.parser() with patch(self.config_func, MagicMock(return_value=opts)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() log_file_option = getattr(parser.options, self.logfile_config_setting_name) @@ -304,21 +328,23 @@ class ParserBase(object): # Check console loggger self.assertEqual(self.log_setup.log_level, log_level) # Check extended logger - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - log_level) - self.assertEqual(self.log_setup.config[self.logfile_config_setting_name], - log_file) + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], log_level + ) + self.assertEqual( + self.log_setup.config[self.logfile_config_setting_name], log_file + ) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file setting self.assertEqual(log_file_option, log_file) # Check log file logger self.assertEqual(self.log_setup.log_file, log_file) def test_get_log_file_default(self): - ''' + """ Tests that log file match the default value - ''' + """ # Set defaults log_level = self.testing_config[self.loglevel_config_setting_name] log_file = self.testing_config[self.logfile_config_setting_name] @@ -329,7 +355,7 @@ class ParserBase(object): parser = self.parser() with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() log_file_option = getattr(parser.options, self.logfile_config_setting_name) @@ -338,99 +364,112 @@ class ParserBase(object): # Check console loggger self.assertEqual(self.log_setup.log_level, log_level) # Check extended logger - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - log_level) - self.assertEqual(self.log_setup.config[self.logfile_config_setting_name], - log_file) + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], log_level + ) + self.assertEqual( + self.log_setup.config[self.logfile_config_setting_name], log_file + ) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file setting self.assertEqual(log_file_option, log_file) # Check log file logger self.assertEqual(self.log_setup.log_file, log_file) - # Check help message - self.assertIn('Default: \'{0}\'.'.format(default_log_file), - parser.get_option('--log-file').help) + # Check help message + self.assertIn( + "Default: '{0}'.".format(default_log_file), + parser.get_option("--log-file").help, + ) # log file log level configuration tests def test_get_log_file_level_cli(self): - ''' + """ Tests that file log level match command-line specified value - ''' + """ # Set defaults default_log_level = self.testing_config[self.loglevel_config_setting_name] # Set log file level in CLI - log_level_logfile = 'error' - args = ['--log-file-level', log_level_logfile] + self.args + log_level_logfile = "error" + args = ["--log-file-level", log_level_logfile] + self.args parser = self.parser() with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() - log_level_logfile_option = getattr(parser.options, - self.logfile_loglevel_config_setting_name) + log_level_logfile_option = getattr( + parser.options, self.logfile_loglevel_config_setting_name + ) if not self.skip_console_logging_config: # Check console loggger self.assertEqual(self.log_setup.log_level, default_log_level) # Check extended logger - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - default_log_level) - self.assertEqual(self.log_setup.config[self.logfile_loglevel_config_setting_name], - log_level_logfile) + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], + default_log_level, + ) + self.assertEqual( + self.log_setup.config[self.logfile_loglevel_config_setting_name], + log_level_logfile, + ) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file level setting self.assertEqual(log_level_logfile_option, log_level_logfile) # Check log file logger self.assertEqual(self.log_setup.log_level_logfile, log_level_logfile) def test_get_log_file_level_config(self): - ''' + """ Tests that log file level match the configured value - ''' + """ # Set defaults log_level = self.testing_config[self.loglevel_config_setting_name] args = self.args # Set log file level in config - log_level_logfile = 'info' + log_level_logfile = "info" opts = self.testing_config.copy() opts.update({self.logfile_loglevel_config_setting_name: log_level_logfile}) parser = self.parser() with patch(self.config_func, MagicMock(return_value=opts)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() - log_level_logfile_option = getattr(parser.options, - self.logfile_loglevel_config_setting_name) + log_level_logfile_option = getattr( + parser.options, self.logfile_loglevel_config_setting_name + ) if not self.skip_console_logging_config: # Check console loggger self.assertEqual(self.log_setup.log_level, log_level) # Check extended logger - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - log_level) - self.assertEqual(self.log_setup.config[self.logfile_loglevel_config_setting_name], - log_level_logfile) + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], log_level + ) + self.assertEqual( + self.log_setup.config[self.logfile_loglevel_config_setting_name], + log_level_logfile, + ) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file level setting self.assertEqual(log_level_logfile_option, log_level_logfile) # Check log file logger self.assertEqual(self.log_setup.log_level_logfile, log_level_logfile) def test_get_log_file_level_default(self): - ''' + """ Tests that log file level match the default value - ''' + """ # Set defaults default_log_level = self.testing_config[self.loglevel_config_setting_name] @@ -442,38 +481,46 @@ class ParserBase(object): parser = self.parser() with patch(self.config_func, MagicMock(return_value=self.testing_config)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() - log_level_logfile_option = getattr(parser.options, - self.logfile_loglevel_config_setting_name) + log_level_logfile_option = getattr( + parser.options, self.logfile_loglevel_config_setting_name + ) if not self.skip_console_logging_config: # Check console loggger self.assertEqual(self.log_setup.log_level, log_level) # Check extended logger - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - log_level) - self.assertEqual(self.log_setup.config[self.logfile_loglevel_config_setting_name], - log_level_logfile) + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], log_level + ) + self.assertEqual( + self.log_setup.config[self.logfile_loglevel_config_setting_name], + log_level_logfile, + ) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file level setting self.assertEqual(log_level_logfile_option, log_level_logfile) # Check log file logger self.assertEqual(self.log_setup.log_level_logfile, log_level_logfile) # Check help message - self.assertIn('Default: \'{0}\'.'.format(default_log_level), - parser.get_option('--log-file-level').help) + self.assertIn( + "Default: '{0}'.".format(default_log_level), + parser.get_option("--log-file-level").help, + ) - def test_get_console_log_level_with_file_log_level(self): # pylint: disable=invalid-name - ''' + def test_get_console_log_level_with_file_log_level( + self, + ): # pylint: disable=invalid-name + """ Tests that both console log level and log file level setting are working together - ''' - log_level = 'critical' - log_level_logfile = 'debug' + """ + log_level = "critical" + log_level_logfile = "debug" - args = ['--log-file-level', log_level_logfile] + self.args + args = ["--log-file-level", log_level_logfile] + self.args opts = self.testing_config.copy() opts.update({self.loglevel_config_setting_name: log_level}) @@ -481,61 +528,67 @@ class ParserBase(object): parser = self.parser() with patch(self.config_func, MagicMock(return_value=opts)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() - log_level_logfile_option = getattr(parser.options, - self.logfile_loglevel_config_setting_name) + log_level_logfile_option = getattr( + parser.options, self.logfile_loglevel_config_setting_name + ) if not self.skip_console_logging_config: # Check console loggger self.assertEqual(self.log_setup.log_level, log_level) # Check extended logger - self.assertEqual(self.log_setup.config[self.loglevel_config_setting_name], - log_level) - self.assertEqual(self.log_setup.config[self.logfile_loglevel_config_setting_name], - log_level_logfile) + self.assertEqual( + self.log_setup.config[self.loglevel_config_setting_name], log_level + ) + self.assertEqual( + self.log_setup.config[self.logfile_loglevel_config_setting_name], + log_level_logfile, + ) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file level setting self.assertEqual(log_level_logfile_option, log_level_logfile) # Check log file logger self.assertEqual(self.log_setup.log_level_logfile, log_level_logfile) - @skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener') + @skipIf(salt.utils.platform.is_windows(), "Windows uses a logging listener") def test_log_created(self): - ''' + """ Tests that log file is created - ''' + """ args = self.args log_file = self.log_file log_file_name = self.logfile_config_setting_name opts = self.testing_config.copy() - opts.update({'log_file': log_file}) - if log_file_name != 'log_file': + opts.update({"log_file": log_file}) + if log_file_name != "log_file": opts.update({log_file_name: getattr(self, log_file_name)}) - if log_file_name == 'key_logfile': - self.skipTest('salt-key creates log file outside of parse_args.') + if log_file_name == "key_logfile": + self.skipTest("salt-key creates log file outside of parse_args.") parser = self.parser() with patch(self.config_func, MagicMock(return_value=opts)): parser.parse_args(args) - if log_file_name == 'log_file': + if log_file_name == "log_file": self.assertEqual(os.path.getsize(log_file), 0) else: self.assertEqual(os.path.getsize(getattr(self, log_file_name)), 0) def test_callbacks_uniqueness(self): - ''' + """ Test that the callbacks are only added once, no matter how many instances of the parser we create - ''' - mixin_container_names = ('_mixin_setup_funcs', - '_mixin_process_funcs', - '_mixin_after_parsed_funcs', - '_mixin_before_exit_funcs') + """ + mixin_container_names = ( + "_mixin_setup_funcs", + "_mixin_process_funcs", + "_mixin_after_parsed_funcs", + "_mixin_before_exit_funcs", + ) parser = self.parser() nums_1 = {} for cb_container in mixin_container_names: @@ -551,60 +604,62 @@ class ParserBase(object): self.assertDictEqual(nums_1, nums_2) -@skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener') +@skipIf(salt.utils.platform.is_windows(), "Windows uses a logging listener") class MasterOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Master options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set defaults self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_master_parser_test' + self.log_file = "/tmp/salt_master_parser_test" # Function to patch - self.config_func = 'salt.config.master_config' + self.config_func = "salt.config.master_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.MasterOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): os.unlink(self.log_file) -@skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener') +@skipIf(salt.utils.platform.is_windows(), "Windows uses a logging listener") class MinionOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Minion options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set defaults self.default_config = salt.config.DEFAULT_MINION_OPTS.copy() - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_minion_parser_test' + self.log_file = "/tmp/salt_minion_parser_test" # Function to patch - self.config_func = 'salt.config.minion_config' + self.config_func = "salt.config.minion_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.MinionOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -612,63 +667,65 @@ class MinionOptionParserTestCase(ParserBase, TestCase): class ProxyMinionOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Proxy Minion options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set defaults self.default_config = salt.config.DEFAULT_MINION_OPTS.copy() self.default_config.update(salt.config.DEFAULT_PROXY_MINION_OPTS) - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_proxy_minion_parser_test' + self.log_file = "/tmp/salt_proxy_minion_parser_test" # Function to patch - self.config_func = 'salt.config.proxy_config' + self.config_func = "salt.config.proxy_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.ProxyMinionOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): os.unlink(self.log_file) -@skipIf(salt.utils.platform.is_windows(), 'Windows uses a logging listener') +@skipIf(salt.utils.platform.is_windows(), "Windows uses a logging listener") class SyndicOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Syndic options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set config option names - self.logfile_config_setting_name = 'syndic_log_file' + self.logfile_config_setting_name = "syndic_log_file" # Set defaults self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_syndic_parser_test' - self.syndic_log_file = '/tmp/salt_syndic_log' + self.log_file = "/tmp/salt_syndic_parser_test" + self.syndic_log_file = "/tmp/salt_syndic_log" # Function to patch - self.config_func = 'salt.config.syndic_config' + self.config_func = "salt.config.syndic_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SyndicOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -678,31 +735,32 @@ class SyndicOptionParserTestCase(ParserBase, TestCase): class SaltCMDOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt CLI options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set mandatory CLI options - self.args = ['foo', 'bar.baz'] + self.args = ["foo", "bar.baz"] # Set defaults self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_cmd_parser_test' + self.log_file = "/tmp/salt_cmd_parser_test" # Function to patch - self.config_func = 'salt.config.client_config' + self.config_func = "salt.config.client_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SaltCMDOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -710,31 +768,32 @@ class SaltCMDOptionParserTestCase(ParserBase, TestCase): class SaltCPOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing salt-cp options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set mandatory CLI options - self.args = ['foo', 'bar', 'baz'] + self.args = ["foo", "bar", "baz"] # Set defaults self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_cp_parser_test' + self.log_file = "/tmp/salt_cp_parser_test" # Function to patch - self.config_func = 'salt.config.master_config' + self.config_func = "salt.config.master_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SaltCPOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -742,82 +801,85 @@ class SaltCPOptionParserTestCase(ParserBase, TestCase): class SaltKeyOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing salt-key options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ self.skip_console_logging_config = True # Set config option names - self.logfile_config_setting_name = 'key_logfile' + self.logfile_config_setting_name = "key_logfile" # Set defaults self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_key_parser_test' - self.key_logfile = '/tmp/key_logfile' + self.log_file = "/tmp/salt_key_parser_test" + self.key_logfile = "/tmp/key_logfile" # Function to patch - self.config_func = 'salt.config.client_config' + self.config_func = "salt.config.client_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SaltKeyOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") # log level configuration tests def test_get_log_level_cli(self): - ''' + """ Tests that console log level option is not recognized - ''' + """ # No console log level will be actually set log_level = default_log_level = None - option = '--log-level' - args = self.args + [option, 'error'] + option = "--log-level" + args = self.args + [option, "error"] parser = self.parser() mock_err = ErrorMock() - with patch('salt.utils.parsers.OptionParser.error', mock_err.error): + with patch("salt.utils.parsers.OptionParser.error", mock_err.error): parser.parse_args(args) # Check error msg - self.assertEqual(mock_err.msg, 'no such option: {0}'.format(option)) + self.assertEqual(mock_err.msg, "no such option: {0}".format(option)) # Check console loggger has not been set self.assertEqual(self.log_setup.log_level, log_level) self.assertNotIn(self.loglevel_config_setting_name, self.log_setup.config) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file logger log level self.assertEqual(self.log_setup.log_level_logfile, default_log_level) def test_get_log_level_config(self): - ''' + """ Tests that log level set in config is ignored - ''' - log_level = 'info' + """ + log_level = "info" args = self.args # Set log level in config and set additional mocked opts keys - opts = {self.loglevel_config_setting_name: log_level, - self.logfile_config_setting_name: 'key_logfile', - 'log_fmt_logfile': None, - 'log_datefmt_logfile': None, - 'log_rotate_max_bytes': None, - 'log_rotate_backup_count': None} + opts = { + self.loglevel_config_setting_name: log_level, + self.logfile_config_setting_name: "key_logfile", + "log_fmt_logfile": None, + "log_datefmt_logfile": None, + "log_rotate_max_bytes": None, + "log_rotate_backup_count": None, + } parser = self.parser() with patch(self.config_func, MagicMock(return_value=opts)): parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() # Check config name absence in options @@ -826,14 +888,14 @@ class SaltKeyOptionParserTestCase(ParserBase, TestCase): self.assertEqual(self.log_setup.log_level, None) self.assertNotIn(self.loglevel_config_setting_name, self.log_setup.config) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file logger log level self.assertEqual(self.log_setup.log_level_logfile, log_level) def test_get_log_level_default(self): - ''' + """ Tests that log level default value is ignored - ''' + """ # Set defaults default_log_level = self.testing_config[self.loglevel_config_setting_name] @@ -843,7 +905,7 @@ class SaltKeyOptionParserTestCase(ParserBase, TestCase): parser = self.parser() parser.parse_args(args) - with patch('salt.utils.parsers.is_writeable', MagicMock(return_value=True)): + with patch("salt.utils.parsers.is_writeable", MagicMock(return_value=True)): parser.setup_logfile_logger() # Check config name absence in options @@ -852,7 +914,7 @@ class SaltKeyOptionParserTestCase(ParserBase, TestCase): self.assertEqual(self.log_setup.log_level, log_level) self.assertNotIn(self.loglevel_config_setting_name, self.log_setup.config) # Check temp logger - self.assertEqual(self.log_setup.temp_log_level, 'error') + self.assertEqual(self.log_setup.temp_log_level, "error") # Check log file logger log level self.assertEqual(self.log_setup.log_level_logfile, default_log_level) @@ -864,31 +926,32 @@ class SaltKeyOptionParserTestCase(ParserBase, TestCase): class SaltCallOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Minion options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set mandatory CLI options - self.args = ['foo.bar'] + self.args = ["foo.bar"] # Set defaults self.default_config = salt.config.DEFAULT_MINION_OPTS.copy() - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_call_parser_test' + self.log_file = "/tmp/salt_call_parser_test" # Function to patch - self.config_func = 'salt.config.minion_config' + self.config_func = "salt.config.minion_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SaltCallOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -896,31 +959,32 @@ class SaltCallOptionParserTestCase(ParserBase, TestCase): class SaltRunOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Master options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set mandatory CLI options - self.args = ['foo.bar'] + self.args = ["foo.bar"] # Set defaults self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_run_parser_test' + self.log_file = "/tmp/salt_run_parser_test" # Function to patch - self.config_func = 'salt.config.master_config' + self.config_func = "salt.config.master_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SaltRunOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -928,35 +992,36 @@ class SaltRunOptionParserTestCase(ParserBase, TestCase): class SaltSSHOptionParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Master options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set mandatory CLI options - self.args = ['foo', 'bar.baz'] + self.args = ["foo", "bar.baz"] # Set config option names - self.logfile_config_setting_name = 'ssh_log_file' + self.logfile_config_setting_name = "ssh_log_file" # Set defaults self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_ssh_parser_test' - self.ssh_log_file = '/tmp/ssh_logfile' + self.log_file = "/tmp/salt_ssh_parser_test" + self.ssh_log_file = "/tmp/ssh_logfile" # Function to patch - self.config_func = 'salt.config.master_config' + self.config_func = "salt.config.master_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SaltSSHOptionParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -966,15 +1031,16 @@ class SaltSSHOptionParserTestCase(ParserBase, TestCase): class SaltCloudParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Cloud options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set mandatory CLI options - self.args = ['-p', 'foo', 'bar'] + self.args = ["-p", "foo", "bar"] # Set default configs # Cloud configs are merged with master configs in @@ -982,19 +1048,19 @@ class SaltCloudParserTestCase(ParserBase, TestCase): # As we need the 'user' key later on. self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.default_config.update(salt.config.DEFAULT_CLOUD_OPTS) - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_cloud_parser_test' + self.log_file = "/tmp/salt_cloud_parser_test" # Function to patch - self.config_func = 'salt.config.cloud_config' + self.config_func = "salt.config.cloud_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SaltCloudParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -1002,36 +1068,37 @@ class SaltCloudParserTestCase(ParserBase, TestCase): class SPMParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Cloud options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set mandatory CLI options - self.args = ['foo', 'bar'] + self.args = ["foo", "bar"] # Set config option names - self.logfile_config_setting_name = 'spm_logfile' + self.logfile_config_setting_name = "spm_logfile" # Set defaults self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.default_config.update(salt.config.DEFAULT_SPM_OPTS) - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/spm_parser_test' - self.spm_logfile = '/tmp/spm_logfile' + self.log_file = "/tmp/spm_parser_test" + self.spm_logfile = "/tmp/spm_logfile" # Function to patch - self.config_func = 'salt.config.spm_config' + self.config_func = "salt.config.spm_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SPMParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -1041,36 +1108,37 @@ class SPMParserTestCase(ParserBase, TestCase): class SaltAPIParserTestCase(ParserBase, TestCase): - ''' + """ Tests parsing Salt Cloud options - ''' + """ + def setUp(self): - ''' + """ Setting up - ''' + """ # Set mandatory CLI options self.args = [] # Set config option names - self.logfile_config_setting_name = 'api_logfile' + self.logfile_config_setting_name = "api_logfile" # Set defaults self.default_config = salt.config.DEFAULT_MASTER_OPTS.copy() self.default_config.update(salt.config.DEFAULT_API_OPTS) - self.addCleanup(delattr, self, 'default_config') + self.addCleanup(delattr, self, "default_config") # Log file - self.log_file = '/tmp/salt_api_parser_test' - self.api_logfile = '/tmp/api_logfile' + self.log_file = "/tmp/salt_api_parser_test" + self.api_logfile = "/tmp/api_logfile" # Function to patch - self.config_func = 'salt.config.api_config' + self.config_func = "salt.config.api_config" # Mock log setup self.setup_log() # Assign parser self.parser = salt.utils.parsers.SaltAPIParser - self.addCleanup(delattr, self, 'parser') + self.addCleanup(delattr, self, "parser") def tearDown(self): if os.path.exists(self.log_file): @@ -1080,72 +1148,76 @@ class SaltAPIParserTestCase(ParserBase, TestCase): class DaemonMixInTestCase(TestCase): - ''' + """ Tests the PIDfile deletion in the DaemonMixIn. - ''' + """ def setUp(self): - ''' + """ Setting up - ''' + """ # Setup mixin self.daemon_mixin = salt.utils.parsers.DaemonMixIn() self.daemon_mixin.config = {} - self.daemon_mixin.config['pidfile'] = '/some/fake.pid' + self.daemon_mixin.config["pidfile"] = "/some/fake.pid" def tearDown(self): - ''' + """ Tear down test :return: - ''' + """ del self.daemon_mixin - @patch('os.unlink', MagicMock()) - @patch('os.path.isfile', MagicMock(return_value=True)) - @patch('salt.utils.parsers.logger', MagicMock()) + @patch("os.unlink", MagicMock()) + @patch("os.path.isfile", MagicMock(return_value=True)) + @patch("salt.utils.parsers.logger", MagicMock()) def test_pid_file_deletion(self): - ''' + """ PIDfile deletion without exception. - ''' + """ self.daemon_mixin._mixin_before_exit() assert salt.utils.parsers.os.unlink.call_count == 1 salt.utils.parsers.logger.info.assert_not_called() salt.utils.parsers.logger.debug.assert_not_called() - @patch('os.unlink', MagicMock(side_effect=OSError())) - @patch('os.path.isfile', MagicMock(return_value=True)) - @patch('salt.utils.parsers.logger', MagicMock()) + @patch("os.unlink", MagicMock(side_effect=OSError())) + @patch("os.path.isfile", MagicMock(return_value=True)) + @patch("salt.utils.parsers.logger", MagicMock()) def test_pid_deleted_oserror_as_root(self): - ''' + """ PIDfile deletion with exception, running as root. - ''' + """ if salt.utils.platform.is_windows(): - patch_args = ('salt.utils.win_functions.is_admin', - MagicMock(return_value=True)) + patch_args = ( + "salt.utils.win_functions.is_admin", + MagicMock(return_value=True), + ) else: - patch_args = ('os.getuid', MagicMock(return_value=0)) + patch_args = ("os.getuid", MagicMock(return_value=0)) with patch(*patch_args): self.daemon_mixin._mixin_before_exit() assert salt.utils.parsers.os.unlink.call_count == 1 salt.utils.parsers.logger.info.assert_called_with( - 'PIDfile could not be deleted: %s', - format(self.daemon_mixin.config['pidfile']) + "PIDfile could not be deleted: %s", + format(self.daemon_mixin.config["pidfile"]), ) salt.utils.parsers.logger.debug.assert_called() - @patch('os.unlink', MagicMock(side_effect=OSError())) - @patch('os.path.isfile', MagicMock(return_value=True)) - @patch('salt.utils.parsers.logger', MagicMock()) + @patch("os.unlink", MagicMock(side_effect=OSError())) + @patch("os.path.isfile", MagicMock(return_value=True)) + @patch("salt.utils.parsers.logger", MagicMock()) def test_pid_deleted_oserror_as_non_root(self): - ''' + """ PIDfile deletion with exception, running as non-root. - ''' + """ if salt.utils.platform.is_windows(): - patch_args = ('salt.utils.win_functions.is_admin', - MagicMock(return_value=False)) + patch_args = ( + "salt.utils.win_functions.is_admin", + MagicMock(return_value=False), + ) else: - patch_args = ('os.getuid', MagicMock(return_value=1000)) + patch_args = ("os.getuid", MagicMock(return_value=1000)) with patch(*patch_args): self.daemon_mixin._mixin_before_exit() diff --git a/tests/unit/utils/test_path.py b/tests/unit/utils/test_path.py index 5134fbd1996..4967533b6f1 100644 --- a/tests/unit/utils/test_path.py +++ b/tests/unit/utils/test_path.py @@ -1,20 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.utils.path -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -import sys -import posixpath -import ntpath -import platform -import tempfile -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch +import ntpath +import os +import platform +import posixpath +import sys +import tempfile # Import Salt libs import salt.utils.compat @@ -24,6 +21,10 @@ from salt.exceptions import CommandNotFoundError # Import 3rd-party libs from salt.ext import six +from tests.support.mock import patch + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf class PathJoinTestCase(TestCase): @@ -32,22 +33,21 @@ class PathJoinTestCase(TestCase): BUILTIN_MODULES = sys.builtin_module_names NIX_PATHS = ( - (('/', 'key'), '/key'), - (('/etc/salt', '/etc/salt/pki'), '/etc/salt/etc/salt/pki'), - (('/usr/local', '/etc/salt/pki'), '/usr/local/etc/salt/pki') - + (("/", "key"), "/key"), + (("/etc/salt", "/etc/salt/pki"), "/etc/salt/etc/salt/pki"), + (("/usr/local", "/etc/salt/pki"), "/usr/local/etc/salt/pki"), ) WIN_PATHS = ( - (('c:', 'temp', 'foo'), 'c:\\temp\\foo'), - (('c:', r'\temp', r'\foo'), 'c:\\temp\\foo'), - (('c:\\', r'\temp', r'\foo'), 'c:\\temp\\foo'), - ((r'c:\\', r'\temp', r'\foo'), 'c:\\temp\\foo'), - (('c:', r'\temp', r'\foo', 'bar'), 'c:\\temp\\foo\\bar'), - (('c:', r'\temp', r'\foo\bar'), 'c:\\temp\\foo\\bar'), + (("c:", "temp", "foo"), "c:\\temp\\foo"), + (("c:", r"\temp", r"\foo"), "c:\\temp\\foo"), + (("c:\\", r"\temp", r"\foo"), "c:\\temp\\foo"), + ((r"c:\\", r"\temp", r"\foo"), "c:\\temp\\foo"), + (("c:", r"\temp", r"\foo", "bar"), "c:\\temp\\foo\\bar"), + (("c:", r"\temp", r"\foo\bar"), "c:\\temp\\foo\\bar"), ) - @skipIf(True, 'Skipped until properly mocked') + @skipIf(True, "Skipped until properly mocked") def test_nix_paths(self): if platform.system().lower() == "windows": self.skipTest( @@ -56,31 +56,29 @@ class PathJoinTestCase(TestCase): for idx, (parts, expected) in enumerate(self.NIX_PATHS): path = salt.utils.path.join(*parts) self.assertEqual( - '{0}: {1}'.format(idx, path), - '{0}: {1}'.format(idx, expected) + "{0}: {1}".format(idx, path), "{0}: {1}".format(idx, expected) ) - @skipIf(True, 'Skipped until properly mocked') + @skipIf(True, "Skipped until properly mocked") def test_windows_paths(self): if platform.system().lower() != "windows": self.skipTest( - 'Non windows platform found. not running non patched os.path ' - 'salt.utils.path.join tests' + "Non windows platform found. not running non patched os.path " + "salt.utils.path.join tests" ) for idx, (parts, expected) in enumerate(self.WIN_PATHS): path = salt.utils.path.join(*parts) self.assertEqual( - '{0}: {1}'.format(idx, path), - '{0}: {1}'.format(idx, expected) + "{0}: {1}".format(idx, path), "{0}: {1}".format(idx, expected) ) - @skipIf(True, 'Skipped until properly mocked') + @skipIf(True, "Skipped until properly mocked") def test_windows_paths_patched_path_module(self): if platform.system().lower() == "windows": self.skipTest( - 'Windows platform found. not running patched os.path ' - 'salt.utils.path.join tests' + "Windows platform found. not running patched os.path " + "salt.utils.path.join tests" ) self.__patch_path() @@ -88,15 +86,14 @@ class PathJoinTestCase(TestCase): for idx, (parts, expected) in enumerate(self.WIN_PATHS): path = salt.utils.path.join(*parts) self.assertEqual( - '{0}: {1}'.format(idx, path), - '{0}: {1}'.format(idx, expected) + "{0}: {1}".format(idx, path), "{0}: {1}".format(idx, expected) ) self.__unpatch_path() - @skipIf(salt.utils.platform.is_windows(), '*nix-only test') + @skipIf(salt.utils.platform.is_windows(), "*nix-only test") def test_mixed_unicode_and_binary(self): - ''' + """ This tests joining paths that contain a mix of components with unicode strings and non-unicode strings with the unicode characters as binary. @@ -104,23 +101,24 @@ class PathJoinTestCase(TestCase): Python 3, but the test should nonetheless pass on Python 3. Really what we're testing here is that we don't get a UnicodeDecodeError when running on Python 2. - ''' - a = u'/foo/bar' - b = 'Д' - expected = u'/foo/bar/\u0414' + """ + a = "/foo/bar" + b = "Д" + expected = "/foo/bar/\u0414" actual = salt.utils.path.join(a, b) self.assertEqual(actual, expected) def __patch_path(self): import imp + modules = list(self.BUILTIN_MODULES[:]) - modules.pop(modules.index('posix')) - modules.append('nt') + modules.pop(modules.index("posix")) + modules.append("nt") code = """'''Salt unittest loaded NT module'''""" - module = imp.new_module('nt') + module = imp.new_module("nt") six.exec_(code, module.__dict__) - sys.modules['nt'] = module + sys.modules["nt"] = module sys.builtin_module_names = modules platform.system = lambda: "windows" @@ -129,7 +127,7 @@ class PathJoinTestCase(TestCase): salt.utils.compat.reload(module) def __unpatch_path(self): - del sys.modules['nt'] + del sys.modules["nt"] sys.builtin_module_names = self.BUILTIN_MODULES[:] platform.system = self.PLATFORM_FUNC @@ -139,66 +137,75 @@ class PathJoinTestCase(TestCase): class PathTestCase(TestCase): def test_which_bin(self): - ret = salt.utils.path.which_bin('str') + ret = salt.utils.path.which_bin("str") self.assertIs(None, ret) - test_exes = ['ls', 'echo'] - with patch('salt.utils.path.which', return_value='/tmp/dummy_path'): + test_exes = ["ls", "echo"] + with patch("salt.utils.path.which", return_value="/tmp/dummy_path"): ret = salt.utils.path.which_bin(test_exes) - self.assertEqual(ret, '/tmp/dummy_path') + self.assertEqual(ret, "/tmp/dummy_path") ret = salt.utils.path.which_bin([]) self.assertIs(None, ret) - with patch('salt.utils.path.which', return_value=''): + with patch("salt.utils.path.which", return_value=""): ret = salt.utils.path.which_bin(test_exes) self.assertIs(None, ret) def test_sanitize_win_path(self): - p = '\\windows\\system' - self.assertEqual(salt.utils.path.sanitize_win_path('\\windows\\system'), '\\windows\\system') - self.assertEqual(salt.utils.path.sanitize_win_path('\\bo:g|us\\p?at*h>'), '\\bo_g_us\\p_at_h_') + p = "\\windows\\system" + self.assertEqual( + salt.utils.path.sanitize_win_path("\\windows\\system"), "\\windows\\system" + ) + self.assertEqual( + salt.utils.path.sanitize_win_path("\\bo:g|us\\p?at*h>"), + "\\bo_g_us\\p_at_h_", + ) def test_check_or_die(self): self.assertRaises(CommandNotFoundError, salt.utils.path.check_or_die, None) - with patch('salt.utils.path.which', return_value=False): - self.assertRaises(CommandNotFoundError, salt.utils.path.check_or_die, 'FAKE COMMAND') + with patch("salt.utils.path.which", return_value=False): + self.assertRaises( + CommandNotFoundError, salt.utils.path.check_or_die, "FAKE COMMAND" + ) def test_join(self): - with patch('salt.utils.platform.is_windows', return_value=False) as is_windows_mock: + with patch( + "salt.utils.platform.is_windows", return_value=False + ) as is_windows_mock: self.assertFalse(is_windows_mock.return_value) - expected_path = os.path.join(os.sep + 'a', 'b', 'c', 'd') - ret = salt.utils.path.join('/a/b/c', 'd') + expected_path = os.path.join(os.sep + "a", "b", "c", "d") + ret = salt.utils.path.join("/a/b/c", "d") self.assertEqual(ret, expected_path) class TestWhich(TestCase): - ''' + """ Tests salt.utils.path.which function to ensure that it returns True as expected. - ''' + """ # The mock patch below will make sure that ALL calls to the which function # returns None def test_missing_binary_in_linux(self): # salt.utils.path.which uses platform.is_windows to determine the platform, so we're using linux here - with patch('salt.utils.platform.is_windows', lambda: False): - with patch('salt.utils.path.which', lambda exe: None): + with patch("salt.utils.platform.is_windows", lambda: False): + with patch("salt.utils.path.which", lambda exe: None): self.assertTrue( - salt.utils.path.which('this-binary-does-not-exist') is None + salt.utils.path.which("this-binary-does-not-exist") is None ) # The mock patch below will make sure that ALL calls to the which function # return whatever is sent to it def test_existing_binary_in_linux(self): # salt.utils.path.which uses platform.is_windows to determine the platform, so we're using linux here - with patch('salt.utils.platform.is_windows', lambda: False): - with patch('salt.utils.path.which', lambda exe: exe): - self.assertTrue(salt.utils.path.which('this-binary-exists-under-linux')) + with patch("salt.utils.platform.is_windows", lambda: False): + with patch("salt.utils.path.which", lambda exe: exe): + self.assertTrue(salt.utils.path.which("this-binary-exists-under-linux")) def test_existing_binary_in_windows(self): - with patch('os.path.isfile') as isfile: + with patch("os.path.isfile") as isfile: # We define the side_effect attribute on the mocked object in order to # specify which calls return which values. First call to os.path.isfile # returns X, the second Y, the third Z, etc... @@ -209,27 +216,34 @@ class TestWhich(TestCase): # the function, see PATHEXT below. False, # Lastly return True, this is the windows check. - True + True, ] # Patch os.access so that it always returns True - with patch('os.access', lambda path, mode: True): + with patch("os.access", lambda path, mode: True): # Disable os.path.islink - with patch('os.path.islink', lambda path: False): + with patch("os.path.islink", lambda path: False): # we're using ';' as os.pathsep in this test - with patch('os.pathsep', ';'): + with patch("os.pathsep", ";"): # Let's patch os.environ to provide a custom PATH variable - with patch.dict(os.environ, {'PATH': os.sep + 'bin', - 'PATHEXT': '.COM;.EXE;.BAT;.CMD'}): + with patch.dict( + os.environ, + {"PATH": os.sep + "bin", "PATHEXT": ".COM;.EXE;.BAT;.CMD"}, + ): # Let's also patch is_windows to return True - with patch('salt.utils.platform.is_windows', lambda: True): + with patch("salt.utils.platform.is_windows", lambda: True): self.assertEqual( - salt.utils.path.which('this-binary-exists-under-windows'), - os.path.join(os.sep + 'bin', 'this-binary-exists-under-windows.EXE') + salt.utils.path.which( + "this-binary-exists-under-windows" + ), + os.path.join( + os.sep + "bin", + "this-binary-exists-under-windows.EXE", + ), ) def test_missing_binary_in_windows(self): - with patch('os.access') as osaccess: + with patch("os.access") as osaccess: osaccess.side_effect = [ # The first os.access should return False due to checking the explicit path (first is_executable) False, @@ -237,23 +251,29 @@ class TestWhich(TestCase): # still checking for Linux # which() will add 4 extra paths to the given one, os.access will # be called 5 times - False, False, False, False, False + False, + False, + False, + False, + False, ] # we're using ';' as os.pathsep in this test - with patch('os.pathsep', ';'): + with patch("os.pathsep", ";"): # Let's patch os.environ to provide a custom PATH variable - with patch.dict(os.environ, {'PATH': os.sep + 'bin'}): + with patch.dict(os.environ, {"PATH": os.sep + "bin"}): # Let's also patch is_widows to return True - with patch('salt.utils.platform.is_windows', lambda: True): + with patch("salt.utils.platform.is_windows", lambda: True): self.assertEqual( # Since we're passing the .exe suffix, the last True above # will not matter. The result will be None - salt.utils.path.which('this-binary-is-missing-in-windows.exe'), - None + salt.utils.path.which( + "this-binary-is-missing-in-windows.exe" + ), + None, ) def test_existing_binary_in_windows_pathext(self): - with patch('os.path.isfile') as isfile: + with patch("os.path.isfile") as isfile: # We define the side_effect attribute on the mocked object in order to # specify which calls return which values. First call to os.path.isfile # returns X, the second Y, the third Z, etc... @@ -263,27 +283,39 @@ class TestWhich(TestCase): # We will now also return False 3 times so we get a .CMD back from # the function, see PATHEXT below. # Lastly return True, this is the windows check. - False, False, False, - True + False, + False, + False, + True, ] # Patch os.access so that it always returns True - with patch('os.access', lambda path, mode: True): + with patch("os.access", lambda path, mode: True): # Disable os.path.islink - with patch('os.path.islink', lambda path: False): + with patch("os.path.islink", lambda path: False): # we're using ';' as os.pathsep in this test - with patch('os.pathsep', ';'): + with patch("os.pathsep", ";"): # Let's patch os.environ to provide a custom PATH variable - with patch.dict(os.environ, {'PATH': os.sep + 'bin', - 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;' - '.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY'}): + with patch.dict( + os.environ, + { + "PATH": os.sep + "bin", + "PATHEXT": ".COM;.EXE;.BAT;.CMD;.VBS;" + ".VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY", + }, + ): # Let's also patch is_windows to return True - with patch('salt.utils.platform.is_windows', lambda: True): + with patch("salt.utils.platform.is_windows", lambda: True): self.assertEqual( - salt.utils.path.which('this-binary-exists-under-windows'), - os.path.join(os.sep + 'bin', 'this-binary-exists-under-windows.CMD') + salt.utils.path.which( + "this-binary-exists-under-windows" + ), + os.path.join( + os.sep + "bin", + "this-binary-exists-under-windows.CMD", + ), ) diff --git a/tests/unit/utils/test_pbm.py b/tests/unit/utils/test_pbm.py index 0d6d247920b..7f81385a102 100644 --- a/tests/unit/utils/test_pbm.py +++ b/tests/unit/utils/test_pbm.py @@ -1,27 +1,32 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstanley.com>` Tests functions in salt.utils.vsan -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals + import logging +import salt.utils.pbm + +# Import Salt libraries +from salt.exceptions import ( + VMwareApiError, + VMwareObjectRetrievalError, + VMwareRuntimeError, +) +from salt.ext.six.moves import range +from tests.support.mock import MagicMock, PropertyMock, patch + # Import Salt testing libraries from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, MagicMock, \ - PropertyMock - -# Import Salt libraries -from salt.exceptions import VMwareApiError, VMwareRuntimeError, \ - VMwareObjectRetrievalError -from salt.ext.six.moves import range -import salt.utils.pbm try: from pyVmomi import vim, vmodl, pbm # pylint: disable=no-name-in-module + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False @@ -31,48 +36,61 @@ except ImportError: log = logging.getLogger(__name__) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetProfileManagerTestCase(TestCase): - '''Tests for salt.utils.pbm.get_profile_manager''' + """Tests for salt.utils.pbm.get_profile_manager""" + def setUp(self): self.mock_si = MagicMock() self.mock_stub = MagicMock() self.mock_prof_mgr = MagicMock() self.mock_content = MagicMock() self.mock_pbm_si = MagicMock( - RetrieveContent=MagicMock(return_value=self.mock_content)) - type(self.mock_content).profileManager = \ - PropertyMock(return_value=self.mock_prof_mgr) + RetrieveContent=MagicMock(return_value=self.mock_content) + ) + type(self.mock_content).profileManager = PropertyMock( + return_value=self.mock_prof_mgr + ) patches = ( - ('salt.utils.vmware.get_new_service_instance_stub', - MagicMock(return_value=self.mock_stub)), - ('salt.utils.pbm.pbm.ServiceInstance', - MagicMock(return_value=self.mock_pbm_si))) + ( + "salt.utils.vmware.get_new_service_instance_stub", + MagicMock(return_value=self.mock_stub), + ), + ( + "salt.utils.pbm.pbm.ServiceInstance", + MagicMock(return_value=self.mock_pbm_si), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_stub', 'mock_content', - 'mock_pbm_si', 'mock_prof_mgr'): + for attr in ( + "mock_si", + "mock_stub", + "mock_content", + "mock_pbm_si", + "mock_prof_mgr", + ): delattr(self, attr) def test_get_new_service_stub(self): mock_get_new_service_stub = MagicMock() - with patch('salt.utils.vmware.get_new_service_instance_stub', - mock_get_new_service_stub): + with patch( + "salt.utils.vmware.get_new_service_instance_stub", mock_get_new_service_stub + ): salt.utils.pbm.get_profile_manager(self.mock_si) mock_get_new_service_stub.assert_called_once_with( - self.mock_si, ns='pbm/2.0', path='/pbm/sdk') + self.mock_si, ns="pbm/2.0", path="/pbm/sdk" + ) def test_pbm_si(self): mock_get_pbm_si = MagicMock() - with patch('salt.utils.pbm.pbm.ServiceInstance', - mock_get_pbm_si): + with patch("salt.utils.pbm.pbm.ServiceInstance", mock_get_pbm_si): salt.utils.pbm.get_profile_manager(self.mock_si) - mock_get_pbm_si.assert_called_once_with('ServiceInstance', - self.mock_stub) + mock_get_pbm_si.assert_called_once_with("ServiceInstance", self.mock_stub) def test_return_profile_manager(self): ret = salt.utils.pbm.get_profile_manager(self.mock_si) @@ -80,73 +98,87 @@ class GetProfileManagerTestCase(TestCase): def test_profile_manager_raises_no_permissions(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" type(self.mock_content).profileManager = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_profile_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_profile_manager_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" type(self.mock_content).profileManager = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_profile_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_profile_manager_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" type(self.mock_content).profileManager = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.pbm.get_profile_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetPlacementSolverTestCase(TestCase): - '''Tests for salt.utils.pbm.get_placement_solver''' + """Tests for salt.utils.pbm.get_placement_solver""" + def setUp(self): self.mock_si = MagicMock() self.mock_stub = MagicMock() self.mock_prof_mgr = MagicMock() self.mock_content = MagicMock() self.mock_pbm_si = MagicMock( - RetrieveContent=MagicMock(return_value=self.mock_content)) - type(self.mock_content).placementSolver = \ - PropertyMock(return_value=self.mock_prof_mgr) + RetrieveContent=MagicMock(return_value=self.mock_content) + ) + type(self.mock_content).placementSolver = PropertyMock( + return_value=self.mock_prof_mgr + ) patches = ( - ('salt.utils.vmware.get_new_service_instance_stub', - MagicMock(return_value=self.mock_stub)), - ('salt.utils.pbm.pbm.ServiceInstance', - MagicMock(return_value=self.mock_pbm_si))) + ( + "salt.utils.vmware.get_new_service_instance_stub", + MagicMock(return_value=self.mock_stub), + ), + ( + "salt.utils.pbm.pbm.ServiceInstance", + MagicMock(return_value=self.mock_pbm_si), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_stub', 'mock_content', - 'mock_pbm_si', 'mock_prof_mgr'): + for attr in ( + "mock_si", + "mock_stub", + "mock_content", + "mock_pbm_si", + "mock_prof_mgr", + ): delattr(self, attr) def test_get_new_service_stub(self): mock_get_new_service_stub = MagicMock() - with patch('salt.utils.vmware.get_new_service_instance_stub', - mock_get_new_service_stub): + with patch( + "salt.utils.vmware.get_new_service_instance_stub", mock_get_new_service_stub + ): salt.utils.pbm.get_placement_solver(self.mock_si) mock_get_new_service_stub.assert_called_once_with( - self.mock_si, ns='pbm/2.0', path='/pbm/sdk') + self.mock_si, ns="pbm/2.0", path="/pbm/sdk" + ) def test_pbm_si(self): mock_get_pbm_si = MagicMock() - with patch('salt.utils.pbm.pbm.ServiceInstance', - mock_get_pbm_si): + with patch("salt.utils.pbm.pbm.ServiceInstance", mock_get_pbm_si): salt.utils.pbm.get_placement_solver(self.mock_si) - mock_get_pbm_si.assert_called_once_with('ServiceInstance', - self.mock_stub) + mock_get_pbm_si.assert_called_once_with("ServiceInstance", self.mock_stub) def test_return_profile_manager(self): ret = salt.utils.pbm.get_placement_solver(self.mock_si) @@ -154,187 +186,208 @@ class GetPlacementSolverTestCase(TestCase): def test_placement_solver_raises_no_permissions(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" type(self.mock_content).placementSolver = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_placement_solver(self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_placement_solver_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" type(self.mock_content).placementSolver = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_placement_solver(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_placement_solver_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" type(self.mock_content).placementSolver = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.pbm.get_placement_solver(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetCapabilityDefinitionsTestCase(TestCase): - '''Tests for salt.utils.pbm.get_capability_definitions''' + """Tests for salt.utils.pbm.get_capability_definitions""" + def setUp(self): self.mock_res_type = MagicMock() - self.mock_cap_cats = [MagicMock(capabilityMetadata=['fake_cap_meta1', - 'fake_cap_meta2']), - MagicMock(capabilityMetadata=['fake_cap_meta3'])] + self.mock_cap_cats = [ + MagicMock(capabilityMetadata=["fake_cap_meta1", "fake_cap_meta2"]), + MagicMock(capabilityMetadata=["fake_cap_meta3"]), + ] self.mock_prof_mgr = MagicMock( - FetchCapabilityMetadata=MagicMock(return_value=self.mock_cap_cats)) + FetchCapabilityMetadata=MagicMock(return_value=self.mock_cap_cats) + ) patches = ( - ('salt.utils.pbm.pbm.profile.ResourceType', - MagicMock(return_value=self.mock_res_type)),) + ( + "salt.utils.pbm.pbm.profile.ResourceType", + MagicMock(return_value=self.mock_res_type), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_res_type', 'mock_cap_cats', 'mock_prof_mgr'): + for attr in ("mock_res_type", "mock_cap_cats", "mock_prof_mgr"): delattr(self, attr) def test_get_res_type(self): mock_get_res_type = MagicMock() - with patch('salt.utils.pbm.pbm.profile.ResourceType', - mock_get_res_type): + with patch("salt.utils.pbm.pbm.profile.ResourceType", mock_get_res_type): salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr) mock_get_res_type.assert_called_once_with( - resourceType=pbm.profile.ResourceTypeEnum.STORAGE) + resourceType=pbm.profile.ResourceTypeEnum.STORAGE + ) def test_fetch_capabilities(self): salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr) self.mock_prof_mgr.FetchCapabilityMetadata.assert_called_once_with( - self.mock_res_type) + self.mock_res_type + ) def test_fetch_capabilities_raises_no_permissions(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_prof_mgr.FetchCapabilityMetadata = \ - MagicMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + self.mock_prof_mgr.FetchCapabilityMetadata = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_fetch_capabilities_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.mock_prof_mgr.FetchCapabilityMetadata = \ - MagicMock(side_effect=exc) + exc.msg = "VimFault msg" + self.mock_prof_mgr.FetchCapabilityMetadata = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_fetch_capabilities_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_prof_mgr.FetchCapabilityMetadata = \ - MagicMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + self.mock_prof_mgr.FetchCapabilityMetadata = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_return_cap_definitions(self): ret = salt.utils.pbm.get_capability_definitions(self.mock_prof_mgr) - self.assertEqual(ret, ['fake_cap_meta1', 'fake_cap_meta2', - 'fake_cap_meta3']) + self.assertEqual(ret, ["fake_cap_meta1", "fake_cap_meta2", "fake_cap_meta3"]) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetPoliciesByIdTestCase(TestCase): - '''Tests for salt.utils.pbm.get_policies_by_id''' + """Tests for salt.utils.pbm.get_policies_by_id""" + def setUp(self): self.policy_ids = MagicMock() self.mock_policies = MagicMock() self.mock_prof_mgr = MagicMock( - RetrieveContent=MagicMock(return_value=self.mock_policies)) + RetrieveContent=MagicMock(return_value=self.mock_policies) + ) def tearDown(self): - for attr in ('policy_ids', 'mock_policies', 'mock_prof_mgr'): + for attr in ("policy_ids", "mock_policies", "mock_prof_mgr"): delattr(self, attr) def test_retrieve_policies(self): salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids) - self.mock_prof_mgr.RetrieveContent.assert_called_once_with( - self.policy_ids) + self.mock_prof_mgr.RetrieveContent.assert_called_once_with(self.policy_ids) def test_retrieve_policies_raises_no_permissions(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.mock_prof_mgr.RetrieveContent = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_retrieve_policies_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.mock_prof_mgr.RetrieveContent = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_retrieve_policies_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.mock_prof_mgr.RetrieveContent = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_return_policies(self): ret = salt.utils.pbm.get_policies_by_id(self.mock_prof_mgr, self.policy_ids) self.assertEqual(ret, self.mock_policies) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetStoragePoliciesTestCase(TestCase): - '''Tests for salt.utils.pbm.get_storage_policies''' + """Tests for salt.utils.pbm.get_storage_policies""" + def setUp(self): self.mock_res_type = MagicMock() self.mock_policy_ids = MagicMock() self.mock_prof_mgr = MagicMock( - QueryProfile=MagicMock(return_value=self.mock_policy_ids)) + QueryProfile=MagicMock(return_value=self.mock_policy_ids) + ) # Policies self.mock_policies = [] for i in range(4): - mock_obj = MagicMock(resourceType=MagicMock( - resourceType=pbm.profile.ResourceTypeEnum.STORAGE)) - mock_obj.name = 'fake_policy{0}'.format(i) + mock_obj = MagicMock( + resourceType=MagicMock( + resourceType=pbm.profile.ResourceTypeEnum.STORAGE + ) + ) + mock_obj.name = "fake_policy{0}".format(i) self.mock_policies.append(mock_obj) patches = ( - ('salt.utils.pbm.pbm.profile.ResourceType', - MagicMock(return_value=self.mock_res_type)), - ('salt.utils.pbm.get_policies_by_id', - MagicMock(return_value=self.mock_policies))) + ( + "salt.utils.pbm.pbm.profile.ResourceType", + MagicMock(return_value=self.mock_res_type), + ), + ( + "salt.utils.pbm.get_policies_by_id", + MagicMock(return_value=self.mock_policies), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_res_type', 'mock_policy_ids', 'mock_policies', - 'mock_prof_mgr'): + for attr in ( + "mock_res_type", + "mock_policy_ids", + "mock_policies", + "mock_prof_mgr", + ): delattr(self, attr) def test_get_res_type(self): mock_get_res_type = MagicMock() - with patch('salt.utils.pbm.pbm.profile.ResourceType', - mock_get_res_type): + with patch("salt.utils.pbm.pbm.profile.ResourceType", mock_get_res_type): salt.utils.pbm.get_storage_policies(self.mock_prof_mgr) mock_get_res_type.assert_called_once_with( - resourceType=pbm.profile.ResourceTypeEnum.STORAGE) + resourceType=pbm.profile.ResourceTypeEnum.STORAGE + ) def test_retrieve_policy_ids(self): mock_retrieve_policy_ids = MagicMock(return_value=self.mock_policy_ids) @@ -344,312 +397,348 @@ class GetStoragePoliciesTestCase(TestCase): def test_retrieve_policy_ids_raises_no_permissions(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.mock_prof_mgr.QueryProfile = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_storage_policies(self.mock_prof_mgr) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_retrieve_policy_ids_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.mock_prof_mgr.QueryProfile = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_storage_policies(self.mock_prof_mgr) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_retrieve_policy_ids_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.mock_prof_mgr.QueryProfile = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.pbm.get_storage_policies(self.mock_prof_mgr) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_get_policies_by_id(self): mock_get_policies_by_id = MagicMock(return_value=self.mock_policies) - with patch('salt.utils.pbm.get_policies_by_id', - mock_get_policies_by_id): + with patch("salt.utils.pbm.get_policies_by_id", mock_get_policies_by_id): salt.utils.pbm.get_storage_policies(self.mock_prof_mgr) mock_get_policies_by_id.assert_called_once_with( - self.mock_prof_mgr, self.mock_policy_ids) + self.mock_prof_mgr, self.mock_policy_ids + ) def test_return_all_policies(self): - ret = salt.utils.pbm.get_storage_policies(self.mock_prof_mgr, - get_all_policies=True) + ret = salt.utils.pbm.get_storage_policies( + self.mock_prof_mgr, get_all_policies=True + ) self.assertEqual(ret, self.mock_policies) def test_return_filtered_policies(self): ret = salt.utils.pbm.get_storage_policies( - self.mock_prof_mgr, policy_names=['fake_policy1', 'fake_policy3']) + self.mock_prof_mgr, policy_names=["fake_policy1", "fake_policy3"] + ) self.assertEqual(ret, [self.mock_policies[1], self.mock_policies[3]]) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class CreateStoragePolicyTestCase(TestCase): - '''Tests for salt.utils.pbm.create_storage_policy''' + """Tests for salt.utils.pbm.create_storage_policy""" + def setUp(self): self.mock_policy_spec = MagicMock() self.mock_prof_mgr = MagicMock() def tearDown(self): - for attr in ('mock_policy_spec', 'mock_prof_mgr'): + for attr in ("mock_policy_spec", "mock_prof_mgr"): delattr(self, attr) def test_create_policy(self): - salt.utils.pbm.create_storage_policy(self.mock_prof_mgr, - self.mock_policy_spec) - self.mock_prof_mgr.Create.assert_called_once_with( - self.mock_policy_spec) + salt.utils.pbm.create_storage_policy(self.mock_prof_mgr, self.mock_policy_spec) + self.mock_prof_mgr.Create.assert_called_once_with(self.mock_policy_spec) def test_create_policy_raises_no_permissions(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.mock_prof_mgr.Create = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.pbm.create_storage_policy(self.mock_prof_mgr, - self.mock_policy_spec) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.pbm.create_storage_policy( + self.mock_prof_mgr, self.mock_policy_spec + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_create_policy_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.mock_prof_mgr.Create = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.pbm.create_storage_policy(self.mock_prof_mgr, - self.mock_policy_spec) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.pbm.create_storage_policy( + self.mock_prof_mgr, self.mock_policy_spec + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_create_policy_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.mock_prof_mgr.Create = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.pbm.create_storage_policy(self.mock_prof_mgr, - self.mock_policy_spec) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.pbm.create_storage_policy( + self.mock_prof_mgr, self.mock_policy_spec + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class UpdateStoragePolicyTestCase(TestCase): - '''Tests for salt.utils.pbm.update_storage_policy''' + """Tests for salt.utils.pbm.update_storage_policy""" + def setUp(self): self.mock_policy_spec = MagicMock() self.mock_policy = MagicMock() self.mock_prof_mgr = MagicMock() def tearDown(self): - for attr in ('mock_policy_spec', 'mock_policy', 'mock_prof_mgr'): + for attr in ("mock_policy_spec", "mock_policy", "mock_prof_mgr"): delattr(self, attr) def test_create_policy(self): salt.utils.pbm.update_storage_policy( - self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec) + self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec + ) self.mock_prof_mgr.Update.assert_called_once_with( - self.mock_policy.profileId, self.mock_policy_spec) + self.mock_policy.profileId, self.mock_policy_spec + ) def test_create_policy_raises_no_permissions(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.mock_prof_mgr.Update = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.update_storage_policy( - self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_create_policy_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.mock_prof_mgr.Update = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.update_storage_policy( - self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_create_policy_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.mock_prof_mgr.Update = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.pbm.update_storage_policy( - self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.mock_prof_mgr, self.mock_policy, self.mock_policy_spec + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetDefaultStoragePolicyOfDatastoreTestCase(TestCase): - '''Tests for salt.utils.pbm.get_default_storage_policy_of_datastore''' + """Tests for salt.utils.pbm.get_default_storage_policy_of_datastore""" + def setUp(self): - self.mock_ds = MagicMock(_moId='fake_ds_moid') + self.mock_ds = MagicMock(_moId="fake_ds_moid") self.mock_hub = MagicMock() - self.mock_policy_id = 'fake_policy_id' + self.mock_policy_id = "fake_policy_id" self.mock_prof_mgr = MagicMock( - QueryDefaultRequirementProfile=MagicMock( - return_value=self.mock_policy_id)) + QueryDefaultRequirementProfile=MagicMock(return_value=self.mock_policy_id) + ) self.mock_policy_refs = [MagicMock()] patches = ( - ('salt.utils.pbm.pbm.placement.PlacementHub', - MagicMock(return_value=self.mock_hub)), - ('salt.utils.pbm.get_policies_by_id', - MagicMock(return_value=self.mock_policy_refs))) + ( + "salt.utils.pbm.pbm.placement.PlacementHub", + MagicMock(return_value=self.mock_hub), + ), + ( + "salt.utils.pbm.get_policies_by_id", + MagicMock(return_value=self.mock_policy_refs), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_ds', 'mock_hub', 'mock_policy_id', 'mock_prof_mgr', - 'mock_policy_refs'): + for attr in ( + "mock_ds", + "mock_hub", + "mock_policy_id", + "mock_prof_mgr", + "mock_policy_refs", + ): delattr(self, attr) def test_get_placement_hub(self): mock_get_placement_hub = MagicMock() - with patch('salt.utils.pbm.pbm.placement.PlacementHub', - mock_get_placement_hub): + with patch("salt.utils.pbm.pbm.placement.PlacementHub", mock_get_placement_hub): salt.utils.pbm.get_default_storage_policy_of_datastore( - self.mock_prof_mgr, self.mock_ds) + self.mock_prof_mgr, self.mock_ds + ) mock_get_placement_hub.assert_called_once_with( - hubId='fake_ds_moid', hubType='Datastore') + hubId="fake_ds_moid", hubType="Datastore" + ) def test_query_default_requirement_profile(self): mock_query_prof = MagicMock(return_value=self.mock_policy_id) - self.mock_prof_mgr.QueryDefaultRequirementProfile = \ - mock_query_prof + self.mock_prof_mgr.QueryDefaultRequirementProfile = mock_query_prof salt.utils.pbm.get_default_storage_policy_of_datastore( - self.mock_prof_mgr, self.mock_ds) + self.mock_prof_mgr, self.mock_ds + ) mock_query_prof.assert_called_once_with(self.mock_hub) def test_query_default_requirement_profile_raises_no_permissions(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_prof_mgr.QueryDefaultRequirementProfile = \ - MagicMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + self.mock_prof_mgr.QueryDefaultRequirementProfile = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_default_storage_policy_of_datastore( - self.mock_prof_mgr, self.mock_ds) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.mock_prof_mgr, self.mock_ds + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_query_default_requirement_profile_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.mock_prof_mgr.QueryDefaultRequirementProfile = \ - MagicMock(side_effect=exc) + exc.msg = "VimFault msg" + self.mock_prof_mgr.QueryDefaultRequirementProfile = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.get_default_storage_policy_of_datastore( - self.mock_prof_mgr, self.mock_ds) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.mock_prof_mgr, self.mock_ds + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_query_default_requirement_profile_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_prof_mgr.QueryDefaultRequirementProfile = \ - MagicMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + self.mock_prof_mgr.QueryDefaultRequirementProfile = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.pbm.get_default_storage_policy_of_datastore( - self.mock_prof_mgr, self.mock_ds) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.mock_prof_mgr, self.mock_ds + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_get_policies_by_id(self): mock_get_policies_by_id = MagicMock() - with patch('salt.utils.pbm.get_policies_by_id', - mock_get_policies_by_id): + with patch("salt.utils.pbm.get_policies_by_id", mock_get_policies_by_id): salt.utils.pbm.get_default_storage_policy_of_datastore( - self.mock_prof_mgr, self.mock_ds) + self.mock_prof_mgr, self.mock_ds + ) mock_get_policies_by_id.assert_called_once_with( - self.mock_prof_mgr, [self.mock_policy_id]) + self.mock_prof_mgr, [self.mock_policy_id] + ) def test_no_policy_refs(self): mock_get_policies_by_id = MagicMock() - with patch('salt.utils.pbm.get_policies_by_id', - MagicMock(return_value=None)): + with patch("salt.utils.pbm.get_policies_by_id", MagicMock(return_value=None)): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: salt.utils.pbm.get_default_storage_policy_of_datastore( - self.mock_prof_mgr, self.mock_ds) - self.assertEqual(excinfo.exception.strerror, - 'Storage policy with id \'fake_policy_id\' was not ' - 'found') + self.mock_prof_mgr, self.mock_ds + ) + self.assertEqual( + excinfo.exception.strerror, + "Storage policy with id 'fake_policy_id' was not " "found", + ) def test_return_policy_ref(self): mock_get_policies_by_id = MagicMock() ret = salt.utils.pbm.get_default_storage_policy_of_datastore( - self.mock_prof_mgr, self.mock_ds) + self.mock_prof_mgr, self.mock_ds + ) self.assertEqual(ret, self.mock_policy_refs[0]) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class AssignDefaultStoragePolicyToDatastoreTestCase(TestCase): - '''Tests for salt.utils.pbm.assign_default_storage_policy_to_datastore''' + """Tests for salt.utils.pbm.assign_default_storage_policy_to_datastore""" + def setUp(self): - self.mock_ds = MagicMock(_moId='fake_ds_moid') + self.mock_ds = MagicMock(_moId="fake_ds_moid") self.mock_policy = MagicMock() self.mock_hub = MagicMock() self.mock_prof_mgr = MagicMock() patches = ( - ('salt.utils.pbm.pbm.placement.PlacementHub', - MagicMock(return_value=self.mock_hub)),) + ( + "salt.utils.pbm.pbm.placement.PlacementHub", + MagicMock(return_value=self.mock_hub), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_ds', 'mock_hub', 'mock_policy', 'mock_prof_mgr'): + for attr in ("mock_ds", "mock_hub", "mock_policy", "mock_prof_mgr"): delattr(self, attr) def test_get_placement_hub(self): mock_get_placement_hub = MagicMock() - with patch('salt.utils.pbm.pbm.placement.PlacementHub', - mock_get_placement_hub): + with patch("salt.utils.pbm.pbm.placement.PlacementHub", mock_get_placement_hub): salt.utils.pbm.assign_default_storage_policy_to_datastore( - self.mock_prof_mgr, self.mock_policy, self.mock_ds) + self.mock_prof_mgr, self.mock_policy, self.mock_ds + ) mock_get_placement_hub.assert_called_once_with( - hubId='fake_ds_moid', hubType='Datastore') + hubId="fake_ds_moid", hubType="Datastore" + ) def test_assign_default_requirement_profile(self): mock_assign_prof = MagicMock() - self.mock_prof_mgr.AssignDefaultRequirementProfile = \ - mock_assign_prof + self.mock_prof_mgr.AssignDefaultRequirementProfile = mock_assign_prof salt.utils.pbm.assign_default_storage_policy_to_datastore( - self.mock_prof_mgr, self.mock_policy, self.mock_ds) + self.mock_prof_mgr, self.mock_policy, self.mock_ds + ) mock_assign_prof.assert_called_once_with( - self.mock_policy.profileId, [self.mock_hub]) + self.mock_policy.profileId, [self.mock_hub] + ) def test_assign_default_requirement_profile_raises_no_permissions(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_prof_mgr.AssignDefaultRequirementProfile = \ - MagicMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + self.mock_prof_mgr.AssignDefaultRequirementProfile = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.assign_default_storage_policy_to_datastore( - self.mock_prof_mgr, self.mock_policy, self.mock_ds) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.mock_prof_mgr, self.mock_policy, self.mock_ds + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_assign_default_requirement_profile_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.mock_prof_mgr.AssignDefaultRequirementProfile = \ - MagicMock(side_effect=exc) + exc.msg = "VimFault msg" + self.mock_prof_mgr.AssignDefaultRequirementProfile = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.pbm.assign_default_storage_policy_to_datastore( - self.mock_prof_mgr, self.mock_policy, self.mock_ds) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.mock_prof_mgr, self.mock_policy, self.mock_ds + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_assign_default_requirement_profile_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_prof_mgr.AssignDefaultRequirementProfile = \ - MagicMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + self.mock_prof_mgr.AssignDefaultRequirementProfile = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.pbm.assign_default_storage_policy_to_datastore( - self.mock_prof_mgr, self.mock_policy, self.mock_ds) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.mock_prof_mgr, self.mock_policy, self.mock_ds + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") diff --git a/tests/unit/utils/test_pkg.py b/tests/unit/utils/test_pkg.py index e8b19bef141..b4a67b8e57c 100644 --- a/tests/unit/utils/test_pkg.py +++ b/tests/unit/utils/test_pkg.py @@ -1,17 +1,18 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals -from tests.support.unit import TestCase -from tests.support.mock import MagicMock, patch import salt.utils.pkg from salt.utils.pkg import rpm +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase class PkgUtilsTestCase(TestCase): - ''' + """ TestCase for salt.utils.pkg module - ''' + """ + test_parameters = [ ("16.0.0.49153-0+f1", "", "16.0.0.49153-0+f1"), ("> 15.0.0", ">", "15.0.0"), @@ -34,13 +35,13 @@ class PkgUtilsTestCase(TestCase): ("<=>15.0.0", "<=>", "15.0.0"), ("<>15.0.0", "<>", "15.0.0"), ("=15.0.0", "=", "15.0.0"), - ("", "", "") + ("", "", ""), ] def test_split_comparison(self): - ''' + """ Tests salt.utils.pkg.split_comparison - ''' + """ for test_parameter in self.test_parameters: oper, verstr = salt.utils.pkg.split_comparison(test_parameter[0]) self.assertEqual(test_parameter[1], oper) @@ -48,57 +49,90 @@ class PkgUtilsTestCase(TestCase): class PkgRPMTestCase(TestCase): - ''' + """ Test case for pkg.rpm utils - ''' + """ - @patch('salt.utils.path.which', MagicMock(return_value=True)) + @patch("salt.utils.path.which", MagicMock(return_value=True)) def test_get_osarch_by_rpm(self): - ''' + """ Get os_arch if RPM package is installed. :return: - ''' + """ subprocess_mock = MagicMock() subprocess_mock.Popen = MagicMock() - subprocess_mock.Popen().communicate = MagicMock(return_value=['Z80']) - with patch('salt.utils.pkg.rpm.subprocess', subprocess_mock): - assert rpm.get_osarch() == 'Z80' + subprocess_mock.Popen().communicate = MagicMock(return_value=["Z80"]) + with patch("salt.utils.pkg.rpm.subprocess", subprocess_mock): + assert rpm.get_osarch() == "Z80" assert subprocess_mock.Popen.call_count == 2 # One within the mock - assert subprocess_mock.Popen.call_args[1]['close_fds'] - assert subprocess_mock.Popen.call_args[1]['shell'] + assert subprocess_mock.Popen.call_args[1]["close_fds"] + assert subprocess_mock.Popen.call_args[1]["shell"] assert len(subprocess_mock.Popen.call_args_list) == 2 assert subprocess_mock.Popen.call_args[0][0] == 'rpm --eval "%{_host_cpu}"' - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('salt.utils.pkg.rpm.subprocess', MagicMock(return_value=False)) - @patch('salt.utils.pkg.rpm.platform.uname', MagicMock( - return_value=('Sinclair BASIC', 'motophone', '1982 Sinclair Research Ltd', '1.0', 'ZX81', 'Z80'))) + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("salt.utils.pkg.rpm.subprocess", MagicMock(return_value=False)) + @patch( + "salt.utils.pkg.rpm.platform.uname", + MagicMock( + return_value=( + "Sinclair BASIC", + "motophone", + "1982 Sinclair Research Ltd", + "1.0", + "ZX81", + "Z80", + ) + ), + ) def test_get_osarch_by_platform(self): - ''' + """ Get os_arch if RPM package is not installed (inird image, for example). :return: - ''' - assert rpm.get_osarch() == 'Z80' + """ + assert rpm.get_osarch() == "Z80" - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('salt.utils.pkg.rpm.subprocess', MagicMock(return_value=False)) - @patch('salt.utils.pkg.rpm.platform.uname', MagicMock( - return_value=('Sinclair BASIC', 'motophone', '1982 Sinclair Research Ltd', '1.0', 'ZX81', ''))) + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("salt.utils.pkg.rpm.subprocess", MagicMock(return_value=False)) + @patch( + "salt.utils.pkg.rpm.platform.uname", + MagicMock( + return_value=( + "Sinclair BASIC", + "motophone", + "1982 Sinclair Research Ltd", + "1.0", + "ZX81", + "", + ) + ), + ) def test_get_osarch_by_platform_no_cpu_arch(self): - ''' + """ Get os_arch if RPM package is not installed (inird image, for example) but cpu arch cannot be determined. :return: - ''' - assert rpm.get_osarch() == 'ZX81' + """ + assert rpm.get_osarch() == "ZX81" - @patch('salt.utils.path.which', MagicMock(return_value=False)) - @patch('salt.utils.pkg.rpm.subprocess', MagicMock(return_value=False)) - @patch('salt.utils.pkg.rpm.platform.uname', MagicMock( - return_value=('Sinclair BASIC', 'motophone', '1982 Sinclair Research Ltd', '1.0', '', ''))) + @patch("salt.utils.path.which", MagicMock(return_value=False)) + @patch("salt.utils.pkg.rpm.subprocess", MagicMock(return_value=False)) + @patch( + "salt.utils.pkg.rpm.platform.uname", + MagicMock( + return_value=( + "Sinclair BASIC", + "motophone", + "1982 Sinclair Research Ltd", + "1.0", + "", + "", + ) + ), + ) def test_get_osarch_by_platform_no_cpu_arch_no_machine(self): - ''' + """ Get os_arch if RPM package is not installed (inird image, for example) where both cpu arch and machine cannot be determined. :return: - ''' - assert rpm.get_osarch() == 'unknown' + """ + assert rpm.get_osarch() == "unknown" diff --git a/tests/unit/utils/test_process.py b/tests/unit/utils/test_process.py index 17357b08467..bb84554a0c7 100644 --- a/tests/unit/utils/test_process.py +++ b/tests/unit/utils/test_process.py @@ -2,46 +2,48 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime +import functools import io +import multiprocessing import os +import signal import sys import threading import time -import signal -import multiprocessing -import functools -import datetime import warnings -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, -) +import psutil # Import salt libs import salt.utils.platform import salt.utils.process -from salt.utils.versions import warn_until_date # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin -import psutil +from salt.utils.versions import warn_until_date +from tests.support.mock import patch + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf def die(func): - ''' + """ Add proc title - ''' + """ + @functools.wraps(func) def wrapper(self): # Strip off the "test_" from the function name name = func.__name__[5:] def _die(): - salt.utils.process.appendproctitle('test_{0}'.format(name)) - attrname = 'die_' + name + salt.utils.process.appendproctitle("test_{0}".format(name)) + + attrname = "die_" + name setattr(self, attrname, _die) self.addCleanup(delattr, self, attrname) @@ -49,19 +51,21 @@ def die(func): def incr(func): - ''' + """ Increment counter - ''' + """ + @functools.wraps(func) def wrapper(self): # Strip off the "test_" from the function name name = func.__name__[5:] def _incr(counter, num): - salt.utils.process.appendproctitle('test_{0}'.format(name)) + salt.utils.process.appendproctitle("test_{0}".format(name)) for _ in range(0, num): counter.value += 1 - attrname = 'incr_' + name + + attrname = "incr_" + name setattr(self, attrname, _incr) self.addCleanup(delattr, self, attrname) @@ -69,19 +73,21 @@ def incr(func): def spin(func): - ''' + """ Spin indefinitely - ''' + """ + @functools.wraps(func) def wrapper(self): # Strip off the "test_" from the function name name = func.__name__[5:] def _spin(): - salt.utils.process.appendproctitle('test_{0}'.format(name)) + salt.utils.process.appendproctitle("test_{0}".format(name)) while True: time.sleep(1) - attrname = 'spin_' + name + + attrname = "spin_" + name setattr(self, attrname, _spin) self.addCleanup(delattr, self, attrname) @@ -89,12 +95,11 @@ def spin(func): class TestProcessManager(TestCase): - @spin def test_basic(self): - ''' + """ Make sure that the process is alive 2s later - ''' + """ process_manager = salt.utils.process.ProcessManager() process_manager.add_process(self.spin_basic) initial_pid = next(six.iterkeys(process_manager._process_map)) @@ -139,9 +144,9 @@ class TestProcessManager(TestCase): @die def test_restarting(self): - ''' + """ Make sure that the process is alive 2s later - ''' + """ process_manager = salt.utils.process.ProcessManager() process_manager.add_process(self.die_restarting) initial_pid = next(six.iterkeys(process_manager._process_map)) @@ -159,10 +164,10 @@ class TestProcessManager(TestCase): process_manager.stop_restarting() process_manager.kill_children() - @skipIf(sys.version_info < (2, 7), 'Needs > Py 2.7 due to bug in stdlib') + @skipIf(sys.version_info < (2, 7), "Needs > Py 2.7 due to bug in stdlib") @incr def test_counter(self): - counter = multiprocessing.Value('i', 0) + counter = multiprocessing.Value("i", 0) process_manager = salt.utils.process.ProcessManager() process_manager.add_process(self.incr_counter, args=(counter, 2)) time.sleep(1) @@ -183,14 +188,15 @@ class TestProcessManager(TestCase): class TestThreadPool(TestCase): - def test_basic(self): - ''' + """ Make sure the threadpool can do things - ''' + """ + def incr_counter(counter): counter.value += 1 - counter = multiprocessing.Value('i', 0) + + counter = multiprocessing.Value("i", 0) pool = salt.utils.process.ThreadPool() sent = pool.fire_async(incr_counter, args=(counter,)) @@ -200,12 +206,14 @@ class TestThreadPool(TestCase): self.assertEqual(pool._job_queue.qsize(), 0) def test_full_queue(self): - ''' + """ Make sure that a full threadpool acts as we expect - ''' + """ + def incr_counter(counter): counter.value += 1 - counter = multiprocessing.Value('i', 0) + + counter = multiprocessing.Value("i", 0) # Create a pool with no workers and 1 queue size pool = salt.utils.process.ThreadPool(0, 1) @@ -223,37 +231,34 @@ class TestThreadPool(TestCase): class TestProcess(TestCase): - def test_daemonize_if(self): # pylint: disable=assignment-from-none - with patch('sys.argv', ['salt-call']): + with patch("sys.argv", ["salt-call"]): ret = salt.utils.process.daemonize_if({}) self.assertEqual(None, ret) - ret = salt.utils.process.daemonize_if({'multiprocessing': False}) + ret = salt.utils.process.daemonize_if({"multiprocessing": False}) self.assertEqual(None, ret) - with patch('sys.platform', 'win'): + with patch("sys.platform", "win"): ret = salt.utils.process.daemonize_if({}) self.assertEqual(None, ret) - with patch('salt.utils.process.daemonize'), \ - patch('sys.platform', 'linux2'): + with patch("salt.utils.process.daemonize"), patch("sys.platform", "linux2"): salt.utils.process.daemonize_if({}) self.assertTrue(salt.utils.process.daemonize.called) # pylint: enable=assignment-from-none class TestProcessCallbacks(TestCase): - @staticmethod def process_target(evt): evt.set() def test_callbacks(self): - 'Validate Process call after fork and finalize methods' - teardown_to_mock = 'salt.log.setup.shutdown_multiprocessing_logging' - log_to_mock = 'salt.utils.process.Process._setup_process_logging' + "Validate Process call after fork and finalize methods" + teardown_to_mock = "salt.log.setup.shutdown_multiprocessing_logging" + log_to_mock = "salt.utils.process.Process._setup_process_logging" with patch(teardown_to_mock) as ma, patch(log_to_mock) as mb: evt = multiprocessing.Event() proc = salt.utils.process.Process(target=self.process_target, args=(evt,)) @@ -263,10 +268,9 @@ class TestProcessCallbacks(TestCase): ma.assert_called() def test_callbacks_called_when_run_overriden(self): - 'Validate Process sub classes call after fork and finalize methods when run is overridden' + "Validate Process sub classes call after fork and finalize methods when run is overridden" class MyProcess(salt.utils.process.Process): - def __init__(self): super(MyProcess, self).__init__() self.evt = multiprocessing.Event() @@ -274,8 +278,8 @@ class TestProcessCallbacks(TestCase): def run(self): self.evt.set() - teardown_to_mock = 'salt.log.setup.shutdown_multiprocessing_logging' - log_to_mock = 'salt.utils.process.Process._setup_process_logging' + teardown_to_mock = "salt.log.setup.shutdown_multiprocessing_logging" + log_to_mock = "salt.utils.process.Process._setup_process_logging" with patch(teardown_to_mock) as ma, patch(log_to_mock) as mb: proc = MyProcess() proc.run() @@ -285,7 +289,6 @@ class TestProcessCallbacks(TestCase): class TestSignalHandlingProcess(TestCase): - @classmethod def Process(cls, pid): raise psutil.NoSuchProcess(pid) @@ -300,7 +303,7 @@ class TestSignalHandlingProcess(TestCase): def test_process_does_not_exist(self): try: - with patch('psutil.Process', self.Process): + with patch("psutil.Process", self.Process): proc = salt.utils.process.SignalHandlingProcess(target=self.target) proc.start() except psutil.NoSuchProcess: @@ -308,7 +311,7 @@ class TestSignalHandlingProcess(TestCase): def test_process_children_do_not_exist(self): try: - with patch('psutil.Process.children', self.children): + with patch("psutil.Process.children", self.children): proc = salt.utils.process.SignalHandlingProcess(target=self.target) proc.start() except psutil.NoSuchProcess: @@ -316,13 +319,13 @@ class TestSignalHandlingProcess(TestCase): @staticmethod def run_forever_sub_target(evt): - 'Used by run_forever_target to create a sub-process' + "Used by run_forever_target to create a sub-process" while not evt.is_set(): time.sleep(1) @staticmethod def run_forever_target(sub_target, evt): - 'A target that will run forever or until an event is set' + "A target that will run forever or until an event is set" p = multiprocessing.Process(target=sub_target, args=(evt,)) p.start() p.join() @@ -335,18 +338,17 @@ class TestSignalHandlingProcess(TestCase): pid = os.fork() if pid == 0: return - time.sleep(.1) + time.sleep(0.1) try: os.kill(os.getpid(), signal.SIGINT) except KeyboardInterrupt: pass - @skipIf(sys.platform.startswith('win'), 'No os.fork on Windows') + @skipIf(sys.platform.startswith("win"), "No os.fork on Windows") def test_signal_processing_regression_test(self): evt = multiprocessing.Event() sh_proc = salt.utils.process.SignalHandlingProcess( - target=self.run_forever_target, - args=(self.run_forever_sub_target, evt) + target=self.run_forever_target, args=(self.run_forever_sub_target, evt) ) sh_proc.start() proc = multiprocessing.Process(target=self.kill_target_sub_proc) @@ -371,14 +373,14 @@ class TestSignalHandlingProcess(TestCase): p.start() p.join() - @skipIf(sys.platform.startswith('win'), 'Required signals not supported on windows') + @skipIf(sys.platform.startswith("win"), "Required signals not supported on windows") def test_signal_processing_handle_signals_called(self): - 'Validate SignalHandlingProcess handles signals' + "Validate SignalHandlingProcess handles signals" # Gloobal event to stop all processes we're creating evt = multiprocessing.Event() # Create a process to test signal handler - val = multiprocessing.Value('i', 0) + val = multiprocessing.Value("i", 0) proc = salt.utils.process.SignalHandlingProcess( target=self.pid_setting_target, args=(self.run_forever_sub_target, val, evt), @@ -387,14 +389,13 @@ class TestSignalHandlingProcess(TestCase): # Create a second process that should not respond to SIGINT or SIGTERM proc2 = multiprocessing.Process( - target=self.run_forever_target, - args=(self.run_forever_sub_target, evt), + target=self.run_forever_target, args=(self.run_forever_sub_target, evt), ) proc2.start() # Wait for the sub process to set its pid while not val.value: - time.sleep(.3) + time.sleep(0.3) assert not proc.signal_handled() @@ -406,7 +407,7 @@ class TestSignalHandlingProcess(TestCase): while time.time() - start < 10: if proc.signal_handled(): break - time.sleep(.3) + time.sleep(0.3) try: # Allow some time for the signal handler to do its thing @@ -421,24 +422,22 @@ class TestSignalHandlingProcess(TestCase): class TestSignalHandlingProcessCallbacks(TestCase): - @staticmethod def process_target(evt): evt.set() def test_callbacks(self): - 'Validate SignalHandlingProcess call after fork and finalize methods' + "Validate SignalHandlingProcess call after fork and finalize methods" - teardown_to_mock = 'salt.log.setup.shutdown_multiprocessing_logging' - log_to_mock = 'salt.utils.process.Process._setup_process_logging' - sig_to_mock = 'salt.utils.process.SignalHandlingProcess._setup_signals' + teardown_to_mock = "salt.log.setup.shutdown_multiprocessing_logging" + log_to_mock = "salt.utils.process.Process._setup_process_logging" + sig_to_mock = "salt.utils.process.SignalHandlingProcess._setup_signals" # Mock _setup_signals so we do not register one for this process. evt = multiprocessing.Event() with patch(sig_to_mock): with patch(teardown_to_mock) as ma, patch(log_to_mock) as mb: sh_proc = salt.utils.process.SignalHandlingProcess( - target=self.process_target, - args=(evt,) + target=self.process_target, args=(evt,) ) sh_proc.run() assert evt.is_set() @@ -446,10 +445,9 @@ class TestSignalHandlingProcessCallbacks(TestCase): mb.assert_called() def test_callbacks_called_when_run_overriden(self): - 'Validate SignalHandlingProcess sub classes call after fork and finalize methods when run is overridden' + "Validate SignalHandlingProcess sub classes call after fork and finalize methods when run is overridden" class MyProcess(salt.utils.process.SignalHandlingProcess): - def __init__(self): super(MyProcess, self).__init__() self.evt = multiprocessing.Event() @@ -457,9 +455,9 @@ class TestSignalHandlingProcessCallbacks(TestCase): def run(self): self.evt.set() - teardown_to_mock = 'salt.log.setup.shutdown_multiprocessing_logging' - log_to_mock = 'salt.utils.process.Process._setup_process_logging' - sig_to_mock = 'salt.utils.process.SignalHandlingProcess._setup_signals' + teardown_to_mock = "salt.log.setup.shutdown_multiprocessing_logging" + log_to_mock = "salt.utils.process.Process._setup_process_logging" + sig_to_mock = "salt.utils.process.SignalHandlingProcess._setup_signals" # Mock _setup_signals so we do not register one for this process. with patch(sig_to_mock): with patch(teardown_to_mock) as ma, patch(log_to_mock) as mb: @@ -471,18 +469,17 @@ class TestSignalHandlingProcessCallbacks(TestCase): class TestDup2(TestCase): - def test_dup2_no_fileno(self): - 'The dup2 method does not fail on streams without fileno support' + "The dup2 method does not fail on streams without fileno support" f1 = io.StringIO("some initial text data") f2 = io.StringIO("some initial other text data") with self.assertRaises(io.UnsupportedOperation): f1.fileno() - with patch('os.dup2') as dup_mock: + with patch("os.dup2") as dup_mock: try: salt.utils.process.dup2(f1, f2) except io.UnsupportedOperation: - assert False, 'io.UnsupportedOperation was raised' + assert False, "io.UnsupportedOperation was raised" assert not dup_mock.called @@ -497,14 +494,13 @@ def event_target(event): class TestProcessList(TestCase): - @staticmethod def wait_for_proc(proc, timeout=10): start = time.time() while proc.is_alive(): if time.time() - start > timeout: raise Exception("Process did not finishe before timeout") - time.sleep(.3) + time.sleep(0.3) def test_process_list_process(self): plist = salt.utils.process.SubprocessList() @@ -545,49 +541,58 @@ class TestProcessList(TestCase): class TestDeprecatedClassNames(TestCase): - @staticmethod def process_target(): pass @staticmethod def patched_warn_until_date(current_date): - def _patched_warn_until_date(date, - message, - category=DeprecationWarning, - stacklevel=None, - _current_date=current_date, - _dont_call_warnings=False): + def _patched_warn_until_date( + date, + message, + category=DeprecationWarning, + stacklevel=None, + _current_date=current_date, + _dont_call_warnings=False, + ): # Because we add another function in between, the stacklevel # set in salt.utils.process, 3, needs to now be 4 stacklevel = 4 - return warn_until_date(date, - message, - category=category, - stacklevel=stacklevel, - _current_date=_current_date, - _dont_call_warnings=_dont_call_warnings) + return warn_until_date( + date, + message, + category=category, + stacklevel=stacklevel, + _current_date=_current_date, + _dont_call_warnings=_dont_call_warnings, + ) + return _patched_warn_until_date def test_multiprocessing_process_warning(self): # We *always* want *all* warnings thrown on this module - warnings.filterwarnings('always', '', DeprecationWarning, __name__) + warnings.filterwarnings("always", "", DeprecationWarning, __name__) fake_utcnow = datetime.date(2021, 1, 1) proc = None try: - with patch('salt.utils.versions.warn_until_date', self.patched_warn_until_date(fake_utcnow)): + with patch( + "salt.utils.versions.warn_until_date", + self.patched_warn_until_date(fake_utcnow), + ): # Test warning with warnings.catch_warnings(record=True) as recorded_warnings: - proc = salt.utils.process.MultiprocessingProcess(target=self.process_target) + proc = salt.utils.process.MultiprocessingProcess( + target=self.process_target + ) self.assertEqual( - 'Please stop using \'salt.utils.process.MultiprocessingProcess\' ' - 'and instead use \'salt.utils.process.Process\'. ' - '\'salt.utils.process.MultiprocessingProcess\' will go away ' - 'after 2022-01-01.', - six.text_type(recorded_warnings[0].message) + "Please stop using 'salt.utils.process.MultiprocessingProcess' " + "and instead use 'salt.utils.process.Process'. " + "'salt.utils.process.MultiprocessingProcess' will go away " + "after 2022-01-01.", + six.text_type(recorded_warnings[0].message), ) finally: if proc is not None: @@ -599,41 +604,52 @@ class TestDeprecatedClassNames(TestCase): proc = None try: - with patch('salt.utils.versions.warn_until_date', self.patched_warn_until_date(fake_utcnow)): + with patch( + "salt.utils.versions.warn_until_date", + self.patched_warn_until_date(fake_utcnow), + ): with self.assertRaisesRegex( - RuntimeError, - r"Please stop using 'salt.utils.process.MultiprocessingProcess' " - r"and instead use 'salt.utils.process.Process'. " - r"'salt.utils.process.MultiprocessingProcess' will go away " - r'after 2022-01-01. ' - r'This warning\(now exception\) triggered on ' - r"filename '(.*)test_process.py', line number ([\d]+), is " - r'supposed to be shown until ([\d-]+). Today is ([\d-]+). ' - r'Please remove the warning.'): - proc = salt.utils.process.MultiprocessingProcess(target=self.process_target) + RuntimeError, + r"Please stop using 'salt.utils.process.MultiprocessingProcess' " + r"and instead use 'salt.utils.process.Process'. " + r"'salt.utils.process.MultiprocessingProcess' will go away " + r"after 2022-01-01. " + r"This warning\(now exception\) triggered on " + r"filename '(.*)test_process.py', line number ([\d]+), is " + r"supposed to be shown until ([\d-]+). Today is ([\d-]+). " + r"Please remove the warning.", + ): + proc = salt.utils.process.MultiprocessingProcess( + target=self.process_target + ) finally: if proc is not None: del proc def test_signal_handling_multiprocessing_process_warning(self): # We *always* want *all* warnings thrown on this module - warnings.filterwarnings('always', '', DeprecationWarning, __name__) + warnings.filterwarnings("always", "", DeprecationWarning, __name__) fake_utcnow = datetime.date(2021, 1, 1) proc = None try: - with patch('salt.utils.versions.warn_until_date', self.patched_warn_until_date(fake_utcnow)): + with patch( + "salt.utils.versions.warn_until_date", + self.patched_warn_until_date(fake_utcnow), + ): # Test warning with warnings.catch_warnings(record=True) as recorded_warnings: - proc = salt.utils.process.SignalHandlingMultiprocessingProcess(target=self.process_target) + proc = salt.utils.process.SignalHandlingMultiprocessingProcess( + target=self.process_target + ) self.assertEqual( - 'Please stop using \'salt.utils.process.SignalHandlingMultiprocessingProcess\' ' - 'and instead use \'salt.utils.process.SignalHandlingProcess\'. ' - '\'salt.utils.process.SignalHandlingMultiprocessingProcess\' will go away ' - 'after 2022-01-01.', - six.text_type(recorded_warnings[0].message) + "Please stop using 'salt.utils.process.SignalHandlingMultiprocessingProcess' " + "and instead use 'salt.utils.process.SignalHandlingProcess'. " + "'salt.utils.process.SignalHandlingMultiprocessingProcess' will go away " + "after 2022-01-01.", + six.text_type(recorded_warnings[0].message), ) finally: if proc is not None: @@ -645,18 +661,24 @@ class TestDeprecatedClassNames(TestCase): proc = None try: - with patch('salt.utils.versions.warn_until_date', self.patched_warn_until_date(fake_utcnow)): + with patch( + "salt.utils.versions.warn_until_date", + self.patched_warn_until_date(fake_utcnow), + ): with self.assertRaisesRegex( - RuntimeError, - r"Please stop using 'salt.utils.process.SignalHandlingMultiprocessingProcess' " - r"and instead use 'salt.utils.process.SignalHandlingProcess'. " - r"'salt.utils.process.SignalHandlingMultiprocessingProcess' will go away " - r'after 2022-01-01. ' - r'This warning\(now exception\) triggered on ' - r"filename '(.*)test_process.py', line number ([\d]+), is " - r'supposed to be shown until ([\d-]+). Today is ([\d-]+). ' - r'Please remove the warning.'): - proc = salt.utils.process.SignalHandlingMultiprocessingProcess(target=self.process_target) + RuntimeError, + r"Please stop using 'salt.utils.process.SignalHandlingMultiprocessingProcess' " + r"and instead use 'salt.utils.process.SignalHandlingProcess'. " + r"'salt.utils.process.SignalHandlingMultiprocessingProcess' will go away " + r"after 2022-01-01. " + r"This warning\(now exception\) triggered on " + r"filename '(.*)test_process.py', line number ([\d]+), is " + r"supposed to be shown until ([\d-]+). Today is ([\d-]+). " + r"Please remove the warning.", + ): + proc = salt.utils.process.SignalHandlingMultiprocessingProcess( + target=self.process_target + ) finally: if proc is not None: del proc diff --git a/tests/unit/utils/test_pycrypto.py b/tests/unit/utils/test_pycrypto.py index 901b2d556fe..0bddc003405 100644 --- a/tests/unit/utils/test_pycrypto.py +++ b/tests/unit/utils/test_pycrypto.py @@ -2,12 +2,14 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import re +import salt.utils.platform + # Import Salt Libs import salt.utils.pycrypto -import salt.utils.platform # Import Salt Testing Libs from tests.support.unit import TestCase, skipIf @@ -16,35 +18,35 @@ log = logging.getLogger(__name__) class PycryptoTestCase(TestCase): - ''' + """ TestCase for salt.utils.pycrypto module - ''' + """ # The crypt module is only available on Unix systems # https://docs.python.org/dev/library/crypt.html - @skipIf(not salt.utils.pycrypto.HAS_CRYPT, 'crypt module not available') + @skipIf(not salt.utils.pycrypto.HAS_CRYPT, "crypt module not available") def test_gen_hash(self): - ''' + """ Test gen_hash - ''' - passwd = 'test_password' - id = '$' + """ + passwd = "test_password" + id = "$" if salt.utils.platform.is_darwin(): - id = '' + id = "" ret = salt.utils.pycrypto.gen_hash(password=passwd) - self.assertTrue(ret.startswith('$6{0}'.format(id))) + self.assertTrue(ret.startswith("$6{0}".format(id))) - ret = salt.utils.pycrypto.gen_hash(password=passwd, algorithm='md5') - self.assertTrue(ret.startswith('$1{0}'.format(id))) + ret = salt.utils.pycrypto.gen_hash(password=passwd, algorithm="md5") + self.assertTrue(ret.startswith("$1{0}".format(id))) - ret = salt.utils.pycrypto.gen_hash(password=passwd, algorithm='sha256') - self.assertTrue(ret.startswith('$5{0}'.format(id))) + ret = salt.utils.pycrypto.gen_hash(password=passwd, algorithm="sha256") + self.assertTrue(ret.startswith("$5{0}".format(id))) def test_secure_password(self): - ''' + """ test secure_password - ''' + """ ret = salt.utils.pycrypto.secure_password() - check = re.compile(r'[!@#$%^&*()_=+]') + check = re.compile(r"[!@#$%^&*()_=+]") assert check.search(ret) is None assert ret diff --git a/tests/unit/utils/test_pydsl.py b/tests/unit/utils/test_pydsl.py index 5a232db30e7..6ffca538e68 100644 --- a/tests/unit/utils/test_pydsl.py +++ b/tests/unit/utils/test_pydsl.py @@ -2,42 +2,41 @@ # Import Python libs from __future__ import absolute_import + +import copy import os -import sys import shutil +import sys import tempfile import textwrap -import copy -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.helpers import with_tempdir -from tests.support.unit import TestCase +import salt.config # Import Salt libs import salt.loader -import salt.config import salt.utils.files import salt.utils.versions -from salt.state import HighState -from salt.utils.pydsl import PyDslError - # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import StringIO +from salt.state import HighState +from salt.utils.pydsl import PyDslError +from tests.support.helpers import with_tempdir +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase -REQUISITES = ['require', 'require_in', 'use', 'use_in', 'watch', 'watch_in'] +REQUISITES = ["require", "require_in", "use", "use_in", "watch", "watch_in"] class CommonTestCaseBoilerplate(TestCase): - def setUp(self): self.root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) self.addCleanup(shutil.rmtree, self.root_dir, ignore_errors=True) - self.state_tree_dir = os.path.join(self.root_dir, 'state_tree') - self.cache_dir = os.path.join(self.root_dir, 'cachedir') + self.state_tree_dir = os.path.join(self.root_dir, "state_tree") + self.cache_dir = os.path.join(self.root_dir, "cachedir") if not os.path.isdir(self.root_dir): os.makedirs(self.root_dir) @@ -47,14 +46,14 @@ class CommonTestCaseBoilerplate(TestCase): if not os.path.isdir(self.cache_dir): os.makedirs(self.cache_dir) self.config = salt.config.minion_config(None) - self.config['root_dir'] = self.root_dir - self.config['state_events'] = False - self.config['id'] = 'match' - self.config['file_client'] = 'local' - self.config['file_roots'] = dict(base=[self.state_tree_dir]) - self.config['cachedir'] = self.cache_dir - self.config['test'] = False - self.config['grains'] = salt.loader.grains(self.config) + self.config["root_dir"] = self.root_dir + self.config["state_events"] = False + self.config["id"] = "match" + self.config["file_client"] = "local" + self.config["file_roots"] = dict(base=[self.state_tree_dir]) + self.config["cachedir"] = self.cache_dir + self.config["test"] = False + self.config["grains"] = salt.loader.grains(self.config) self.HIGHSTATE = HighState(self.config) self.HIGHSTATE.push_active() @@ -68,14 +67,15 @@ class CommonTestCaseBoilerplate(TestCase): def state_highstate(self, state, dirpath): opts = copy.copy(self.config) - opts['file_roots'] = dict(base=[dirpath]) + opts["file_roots"] = dict(base=[dirpath]) HIGHSTATE = HighState(opts) HIGHSTATE.push_active() try: high, errors = HIGHSTATE.render_highstate(state) if errors: import pprint - pprint.pprint('\n'.join(errors)) + + pprint.pprint("\n".join(errors)) pprint.pprint(high) out = HIGHSTATE.state.call_high(high) @@ -85,23 +85,25 @@ class CommonTestCaseBoilerplate(TestCase): class PyDSLRendererTestCase(CommonTestCaseBoilerplate): - ''' + """ WARNING: If tests in here are flaky, they may need to be moved to their own class. Sharing HighState, especially through setUp/tearDown can create dangerous race conditions! - ''' + """ - def render_sls(self, content, sls='', saltenv='base', **kws): - if 'env' in kws: + def render_sls(self, content, sls="", saltenv="base", **kws): + if "env" in kws: # "env" is not supported; Use "saltenv". - kws.pop('env') + kws.pop("env") - return self.HIGHSTATE.state.rend['pydsl']( + return self.HIGHSTATE.state.rend["pydsl"]( StringIO(content), saltenv=saltenv, sls=sls, **kws ) def test_state_declarations(self): - result = self.render_sls(textwrap.dedent(''' + result = self.render_sls( + textwrap.dedent( + """ state('A').cmd.run('ls -la', cwd='/var/tmp') state().file.managed('myfile.txt', source='salt://path/to/file') state('X').cmd('run', 'echo hello world', cwd='/') @@ -109,40 +111,44 @@ class PyDSLRendererTestCase(CommonTestCaseBoilerplate): a_cmd = state('A').cmd a_cmd.run(shell='/bin/bash') state('A').service.running(name='apache') - ''')) - self.assertTrue('A' in result and 'X' in result) - A_cmd = result['A']['cmd'] - self.assertEqual(A_cmd[0], 'run') - self.assertEqual(A_cmd[1]['name'], 'ls -la') - self.assertEqual(A_cmd[2]['cwd'], '/var/tmp') - self.assertEqual(A_cmd[3]['shell'], '/bin/bash') + """ + ) + ) + self.assertTrue("A" in result and "X" in result) + A_cmd = result["A"]["cmd"] + self.assertEqual(A_cmd[0], "run") + self.assertEqual(A_cmd[1]["name"], "ls -la") + self.assertEqual(A_cmd[2]["cwd"], "/var/tmp") + self.assertEqual(A_cmd[3]["shell"], "/bin/bash") - A_service = result['A']['service'] - self.assertEqual(A_service[0], 'running') - self.assertEqual(A_service[1]['name'], 'apache') + A_service = result["A"]["service"] + self.assertEqual(A_service[0], "running") + self.assertEqual(A_service[1]["name"], "apache") - X_cmd = result['X']['cmd'] - self.assertEqual(X_cmd[0], 'run') - self.assertEqual(X_cmd[1]['name'], 'echo hello world') - self.assertEqual(X_cmd[2]['cwd'], '/') + X_cmd = result["X"]["cmd"] + self.assertEqual(X_cmd[0], "run") + self.assertEqual(X_cmd[1]["name"], "echo hello world") + self.assertEqual(X_cmd[2]["cwd"], "/") - del result['A'] - del result['X'] + del result["A"] + del result["X"] self.assertEqual(len(result), 2) # 2 rather than 1 because pydsl adds an extra no-op state # declaration. s_iter = six.itervalues(result) try: - s = next(s_iter)['file'] + s = next(s_iter)["file"] except KeyError: - s = next(s_iter)['file'] - self.assertEqual(s[0], 'managed') - self.assertEqual(s[1]['name'], 'myfile.txt') - self.assertEqual(s[2]['source'], 'salt://path/to/file') + s = next(s_iter)["file"] + self.assertEqual(s[0], "managed") + self.assertEqual(s[1]["name"], "myfile.txt") + self.assertEqual(s[2]["source"], "salt://path/to/file") def test_requisite_declarations(self): - result = self.render_sls(textwrap.dedent(''' + result = self.render_sls( + textwrap.dedent( + """ state('X').cmd.run('echo hello') state('A').cmd.run('mkdir tmp', cwd='/var') state('B').cmd.run('ls -la', cwd='/var/tmp') \ @@ -154,23 +160,25 @@ class PyDSLRendererTestCase(CommonTestCaseBoilerplate): state('H').cmd.require_in(cmd='echo hello') state('H').cmd.run('echo world') - ''')) + """ + ) + ) self.assertTrue(len(result), 6) self.assertTrue(set("X A B G H".split()).issubset(set(result.keys()))) - b = result['B']['cmd'] - self.assertEqual(b[0], 'run') - self.assertEqual(b[1]['name'], 'ls -la') - self.assertEqual(b[2]['cwd'], '/var/tmp') - self.assertEqual(b[3]['require'][0]['cmd'], 'X') - self.assertEqual(b[4]['require'][0]['cmd'], 'A') - self.assertEqual(b[5]['watch'][0]['service'], 'G') - self.assertEqual(result['G']['service'][2]['watch_in'][0]['cmd'], 'A') - self.assertEqual( - result['H']['cmd'][1]['require_in'][0]['cmd'], 'echo hello' - ) + b = result["B"]["cmd"] + self.assertEqual(b[0], "run") + self.assertEqual(b[1]["name"], "ls -la") + self.assertEqual(b[2]["cwd"], "/var/tmp") + self.assertEqual(b[3]["require"][0]["cmd"], "X") + self.assertEqual(b[4]["require"][0]["cmd"], "A") + self.assertEqual(b[5]["watch"][0]["service"], "G") + self.assertEqual(result["G"]["service"][2]["watch_in"][0]["cmd"], "A") + self.assertEqual(result["H"]["cmd"][1]["require_in"][0]["cmd"], "echo hello") def test_include_extend(self): - result = self.render_sls(textwrap.dedent(''' + result = self.render_sls( + textwrap.dedent( + """ include( 'some.sls.file', 'another.sls.file', @@ -185,27 +193,33 @@ class PyDSLRendererTestCase(CommonTestCaseBoilerplate): state('Y').file('managed', name='a_file.txt'), state('Z').service.watch(file='A') ) - ''')) + """ + ) + ) self.assertEqual(len(result), 4) self.assertEqual( - result['include'], - [{'base': sls} for sls in - ('some.sls.file', 'another.sls.file', 'more.sls.file')] + result["include"], + [ + {"base": sls} + for sls in ("some.sls.file", "another.sls.file", "more.sls.file") + ], ) - extend = result['extend'] - self.assertEqual(extend['X']['cmd'][0], 'run') - self.assertEqual(extend['X']['cmd'][1]['cwd'], '/a/b/c') - self.assertEqual(extend['Y']['file'][0], 'managed') - self.assertEqual(extend['Y']['file'][1]['name'], 'a_file.txt') - self.assertEqual(len(extend['Z']['service']), 1) - self.assertEqual(extend['Z']['service'][0]['watch'][0]['file'], 'A') + extend = result["extend"] + self.assertEqual(extend["X"]["cmd"][0], "run") + self.assertEqual(extend["X"]["cmd"][1]["cwd"], "/a/b/c") + self.assertEqual(extend["Y"]["file"][0], "managed") + self.assertEqual(extend["Y"]["file"][1]["name"], "a_file.txt") + self.assertEqual(len(extend["Z"]["service"]), 1) + self.assertEqual(extend["Z"]["service"][0]["watch"][0]["file"], "A") - self.assertEqual(result['B']['cmd'][0], 'run') - self.assertTrue('A' not in result) - self.assertEqual(extend['A']['cmd'][0], 'run') + self.assertEqual(result["B"]["cmd"][0], "run") + self.assertTrue("A" not in result) + self.assertEqual(extend["A"]["cmd"][0], "run") def test_cmd_call(self): - result = self.HIGHSTATE.state.call_template_str(textwrap.dedent('''\ + result = self.HIGHSTATE.state.call_template_str( + textwrap.dedent( + """\ #!pydsl state('A').cmd.run('echo this is state A', cwd='/') @@ -218,32 +232,43 @@ class PyDSLRendererTestCase(CommonTestCaseBoilerplate): state('G').cmd.wait('echo this is state G', cwd='/') \ .watch(state('C').cmd) - ''')) - ret = next(result[k] for k in six.iterkeys(result) if 'do_something' in k) - changes = ret['changes'] + """ + ) + ) + ret = next(result[k] for k in six.iterkeys(result) if "do_something" in k) + changes = ret["changes"] self.assertEqual( - changes, - dict(a=1, b=2, args=(3,), kws=dict(x=1, y=2), some_var=12345) + changes, dict(a=1, b=2, args=(3,), kws=dict(x=1, y=2), some_var=12345) ) - ret = next(result[k] for k in six.iterkeys(result) if '-G_' in k) - self.assertEqual(ret['changes']['stdout'], 'this is state G') + ret = next(result[k] for k in six.iterkeys(result) if "-G_" in k) + self.assertEqual(ret["changes"]["stdout"], "this is state G") def test_multiple_state_func_in_state_mod(self): - with self.assertRaisesRegex(PyDslError, 'Multiple state functions'): - self.render_sls(textwrap.dedent(''' + with self.assertRaisesRegex(PyDslError, "Multiple state functions"): + self.render_sls( + textwrap.dedent( + """ state('A').cmd.run('echo hoho') state('A').cmd.wait('echo hehe') - ''')) + """ + ) + ) def test_no_state_func_in_state_mod(self): - with self.assertRaisesRegex(PyDslError, 'No state function specified'): - self.render_sls(textwrap.dedent(''' + with self.assertRaisesRegex(PyDslError, "No state function specified"): + self.render_sls( + textwrap.dedent( + """ state('B').cmd.require(cmd='hoho') - ''')) + """ + ) + ) def test_load_highstate(self): - result = self.render_sls(textwrap.dedent(''' + result = self.render_sls( + textwrap.dedent( + ''' import salt.utils.yaml __pydsl__.load_highstate(salt.utils.yaml.safe_load(""" A: @@ -262,24 +287,28 @@ class PyDSLRendererTestCase(CommonTestCaseBoilerplate): """)) state('A').cmd.run(name='echo hello world') - ''')) + ''' + ) + ) self.assertEqual(len(result), 3) - self.assertEqual(result['A']['cmd'][0], 'run') - self.assertIn({'name': 'echo hello'}, result['A']['cmd']) - self.assertIn({'cwd': '/'}, result['A']['cmd']) - self.assertIn({'name': 'echo hello world'}, result['A']['cmd']) - self.assertEqual(len(result['A']['cmd']), 4) + self.assertEqual(result["A"]["cmd"][0], "run") + self.assertIn({"name": "echo hello"}, result["A"]["cmd"]) + self.assertIn({"cwd": "/"}, result["A"]["cmd"]) + self.assertIn({"name": "echo hello world"}, result["A"]["cmd"]) + self.assertEqual(len(result["A"]["cmd"]), 4) - self.assertEqual(len(result['B']['pkg']), 1) - self.assertEqual(result['B']['pkg'][0], 'installed') + self.assertEqual(len(result["B"]["pkg"]), 1) + self.assertEqual(result["B"]["pkg"][0], "installed") - self.assertEqual(result['B']['service'][0], 'running') - self.assertIn({'require': [{'pkg': 'B'}]}, result['B']['service']) - self.assertIn({'watch': [{'cmd': 'A'}]}, result['B']['service']) - self.assertEqual(len(result['B']['service']), 3) + self.assertEqual(result["B"]["service"][0], "running") + self.assertIn({"require": [{"pkg": "B"}]}, result["B"]["service"]) + self.assertIn({"watch": [{"cmd": "A"}]}, result["B"]["service"]) + self.assertEqual(len(result["B"]["service"]), 3) def test_ordered_states(self): - result = self.render_sls(textwrap.dedent(''' + result = self.render_sls( + textwrap.dedent( + """ __pydsl__.set(ordered=True) A = state('A') state('B').cmd.run('echo bbbb') @@ -287,17 +316,21 @@ class PyDSLRendererTestCase(CommonTestCaseBoilerplate): state('B').cmd.run(cwd='/') state('C').cmd.run('echo ccc') state('B').file.managed(source='/a/b/c') - ''')) - self.assertEqual(len(result['B']['cmd']), 3) - self.assertEqual(result['A']['cmd'][1]['require'][0]['cmd'], 'B') - self.assertEqual(result['C']['cmd'][1]['require'][0]['cmd'], 'A') - self.assertEqual(result['B']['file'][1]['require'][0]['cmd'], 'C') + """ + ) + ) + self.assertEqual(len(result["B"]["cmd"]), 3) + self.assertEqual(result["A"]["cmd"][1]["require"][0]["cmd"], "B") + self.assertEqual(result["C"]["cmd"][1]["require"][0]["cmd"], "A") + self.assertEqual(result["B"]["file"][1]["require"][0]["cmd"], "C") @with_tempdir() def test_pipe_through_stateconf(self, dirpath): - output = os.path.join(dirpath, 'output') - write_to(os.path.join(dirpath, 'xxx.sls'), textwrap.dedent( - '''#!stateconf -os yaml . jinja + output = os.path.join(dirpath, "output") + write_to( + os.path.join(dirpath, "xxx.sls"), + textwrap.dedent( + """#!stateconf -os yaml . jinja .X: cmd.run: - name: echo X >> {0} @@ -310,17 +343,31 @@ class PyDSLRendererTestCase(CommonTestCaseBoilerplate): cmd.run: - name: echo Z >> {0} - cwd: / - '''.format(output.replace('\\', '/')))) - write_to(os.path.join(dirpath, 'yyy.sls'), textwrap.dedent('''\ + """.format( + output.replace("\\", "/") + ) + ), + ) + write_to( + os.path.join(dirpath, "yyy.sls"), + textwrap.dedent( + """\ #!pydsl|stateconf -ps __pydsl__.set(ordered=True) state('.D').cmd.run('echo D >> {0}', cwd='/') state('.E').cmd.run('echo E >> {0}', cwd='/') state('.F').cmd.run('echo F >> {0}', cwd='/') - '''.format(output.replace('\\', '/')))) + """.format( + output.replace("\\", "/") + ) + ), + ) - write_to(os.path.join(dirpath, 'aaa.sls'), textwrap.dedent('''\ + write_to( + os.path.join(dirpath, "aaa.sls"), + textwrap.dedent( + """\ #!pydsl|stateconf -ps include('xxx', 'yyy') @@ -336,20 +383,27 @@ class PyDSLRendererTestCase(CommonTestCaseBoilerplate): state('.A').cmd.run('echo A >> {0}', cwd='/') state('.B').cmd.run('echo B >> {0}', cwd='/') state('.C').cmd.run('echo C >> {0}', cwd='/') - '''.format(output.replace('\\', '/')))) + """.format( + output.replace("\\", "/") + ) + ), + ) - self.state_highstate({'base': ['aaa']}, dirpath) - with salt.utils.files.fopen(output, 'r') as f: - self.assertEqual(''.join(f.read().split()), "XYZABCDEF") + self.state_highstate({"base": ["aaa"]}, dirpath) + with salt.utils.files.fopen(output, "r") as f: + self.assertEqual("".join(f.read().split()), "XYZABCDEF") @with_tempdir() def test_compile_time_state_execution(self, dirpath): if not sys.stdin.isatty(): - self.skipTest('Not attached to a TTY') + self.skipTest("Not attached to a TTY") # The Windows shell will include any spaces before the redirect # in the text that is redirected. # For example: echo hello > test.txt will contain "hello " - write_to(os.path.join(dirpath, 'aaa.sls'), textwrap.dedent('''\ + write_to( + os.path.join(dirpath, "aaa.sls"), + textwrap.dedent( + """\ #!pydsl __pydsl__.set(ordered=True) @@ -363,62 +417,99 @@ class PyDSLRendererTestCase(CommonTestCaseBoilerplate): A.file.managed('{0}/xxx.txt', source='salt://zzz.txt') A() - '''.format(dirpath.replace('\\', '/')))) - self.state_highstate({'base': ['aaa']}, dirpath) - with salt.utils.files.fopen(os.path.join(dirpath, 'yyy.txt'), 'rt') as f: - self.assertEqual(f.read(), 'hehe' + os.linesep + 'hoho' + os.linesep) - with salt.utils.files.fopen(os.path.join(dirpath, 'xxx.txt'), 'rt') as f: - self.assertEqual(f.read(), 'hehe' + os.linesep) + """.format( + dirpath.replace("\\", "/") + ) + ), + ) + self.state_highstate({"base": ["aaa"]}, dirpath) + with salt.utils.files.fopen(os.path.join(dirpath, "yyy.txt"), "rt") as f: + self.assertEqual(f.read(), "hehe" + os.linesep + "hoho" + os.linesep) + with salt.utils.files.fopen(os.path.join(dirpath, "xxx.txt"), "rt") as f: + self.assertEqual(f.read(), "hehe" + os.linesep) @with_tempdir() def test_nested_high_state_execution(self, dirpath): - output = os.path.join(dirpath, 'output') - write_to(os.path.join(dirpath, 'aaa.sls'), textwrap.dedent('''\ + output = os.path.join(dirpath, "output") + write_to( + os.path.join(dirpath, "aaa.sls"), + textwrap.dedent( + """\ #!pydsl __salt__['state.sls']('bbb') state().cmd.run('echo bbbbbb', cwd='/') - ''')) - write_to(os.path.join(dirpath, 'bbb.sls'), textwrap.dedent( - ''' + """ + ), + ) + write_to( + os.path.join(dirpath, "bbb.sls"), + textwrap.dedent( + """ # {{ salt['state.sls']('ccc') }} test: cmd.run: - name: echo bbbbbbb - cwd: / - ''')) - write_to(os.path.join(dirpath, 'ccc.sls'), textwrap.dedent( - ''' + """ + ), + ) + write_to( + os.path.join(dirpath, "ccc.sls"), + textwrap.dedent( + """ #!pydsl state().cmd.run('echo ccccc', cwd='/') - ''')) - self.state_highstate({'base': ['aaa']}, dirpath) + """ + ), + ) + self.state_highstate({"base": ["aaa"]}, dirpath) @with_tempdir() def test_repeat_includes(self, dirpath): - output = os.path.join(dirpath, 'output') - write_to(os.path.join(dirpath, 'b.sls'), textwrap.dedent('''\ + output = os.path.join(dirpath, "output") + write_to( + os.path.join(dirpath, "b.sls"), + textwrap.dedent( + """\ #!pydsl include('c') include('d') - ''')) - write_to(os.path.join(dirpath, 'c.sls'), textwrap.dedent('''\ + """ + ), + ) + write_to( + os.path.join(dirpath, "c.sls"), + textwrap.dedent( + """\ #!pydsl modtest = include('e') modtest.success - ''')) - write_to(os.path.join(dirpath, 'd.sls'), textwrap.dedent('''\ + """ + ), + ) + write_to( + os.path.join(dirpath, "d.sls"), + textwrap.dedent( + """\ #!pydsl modtest = include('e') modtest.success - ''')) - write_to(os.path.join(dirpath, 'e.sls'), textwrap.dedent('''\ + """ + ), + ) + write_to( + os.path.join(dirpath, "e.sls"), + textwrap.dedent( + """\ #!pydsl success = True - ''')) - self.state_highstate({'base': ['b']}, dirpath) - self.state_highstate({'base': ['c', 'd']}, dirpath) + """ + ), + ) + self.state_highstate({"base": ["b"]}, dirpath) + self.state_highstate({"base": ["c", "d"]}, dirpath) def write_to(fpath, content): - with salt.utils.files.fopen(fpath, 'w') as f: + with salt.utils.files.fopen(fpath, "w") as f: f.write(content) diff --git a/tests/unit/utils/test_pyobjects.py b/tests/unit/utils/test_pyobjects.py index 70daf6e7f51..9c876327590 100644 --- a/tests/unit/utils/test_pyobjects.py +++ b/tests/unit/utils/test_pyobjects.py @@ -2,7 +2,7 @@ # Import Pytohn libs from __future__ import absolute_import -import jinja2 + import logging import os import shutil @@ -10,9 +10,7 @@ import tempfile import textwrap import uuid -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.runtests import RUNTIME_VARS +import jinja2 # Import Salt libs import salt.config @@ -20,51 +18,60 @@ import salt.state import salt.utils.files from salt.template import compile_template from salt.utils.odict import OrderedDict -from salt.utils.pyobjects import (StateFactory, State, Registry, - SaltObject, InvalidFunction, DuplicateState) +from salt.utils.pyobjects import ( + DuplicateState, + InvalidFunction, + Registry, + SaltObject, + State, + StateFactory, +) +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing libs +from tests.support.unit import TestCase log = logging.getLogger(__name__) -File = StateFactory('file') -Service = StateFactory('service') +File = StateFactory("file") +Service = StateFactory("service") pydmesg_expected = { - 'file.managed': [ - {'group': 'root'}, - {'mode': '0755'}, - {'require': [{'file': '/usr/local/bin'}]}, - {'source': 'salt://debian/files/pydmesg.py'}, - {'user': 'root'}, + "file.managed": [ + {"group": "root"}, + {"mode": "0755"}, + {"require": [{"file": "/usr/local/bin"}]}, + {"source": "salt://debian/files/pydmesg.py"}, + {"user": "root"}, ] } -pydmesg_salt_expected = OrderedDict([ - ('/usr/local/bin/pydmesg', pydmesg_expected) -]) -pydmesg_kwargs = dict(user='root', group='root', mode='0755', - source='salt://debian/files/pydmesg.py') +pydmesg_salt_expected = OrderedDict([("/usr/local/bin/pydmesg", pydmesg_expected)]) +pydmesg_kwargs = dict( + user="root", group="root", mode="0755", source="salt://debian/files/pydmesg.py" +) -basic_template = '''#!pyobjects +basic_template = """#!pyobjects File.directory('/tmp', mode='1777', owner='root', group='root') -''' +""" -invalid_template = '''#!pyobjects +invalid_template = """#!pyobjects File.fail('/tmp') -''' +""" -include_template = '''#!pyobjects +include_template = """#!pyobjects include('http') -''' +""" -extend_template = '''#!pyobjects +extend_template = """#!pyobjects include('http') from salt.utils.pyobjects import StateFactory Service = StateFactory('service') Service.running(extend('apache'), watch=[{'file': '/etc/file'}]) -''' +""" -map_prefix = '''\ +map_prefix = """\ #!pyobjects from salt.utils.pyobjects import StateFactory Service = StateFactory('service') @@ -73,98 +80,100 @@ Service = StateFactory('service') priority = {{ value }} {% endmacro %} class Samba(Map): -''' +""" -map_suffix = ''' +map_suffix = """ with Pkg.installed("samba", names=[Samba.server, Samba.client]): Service.running("samba", name=Samba.service) -''' +""" map_data = { - 'debian': " class Debian:\n" - " server = 'samba'\n" - " client = 'samba-client'\n" - " service = 'samba'\n", - 'centos': " class RougeChapeau:\n" - " __match__ = 'RedHat'\n" - " server = 'samba'\n" - " client = 'samba'\n" - " service = 'smb'\n", - 'ubuntu': " class Ubuntu:\n" - " __grain__ = 'os'\n" - " service = 'smbd'\n" + "debian": " class Debian:\n" + " server = 'samba'\n" + " client = 'samba-client'\n" + " service = 'samba'\n", + "centos": " class RougeChapeau:\n" + " __match__ = 'RedHat'\n" + " server = 'samba'\n" + " client = 'samba'\n" + " service = 'smb'\n", + "ubuntu": " class Ubuntu:\n" + " __grain__ = 'os'\n" + " service = 'smbd'\n", } -import_template = '''#!pyobjects +import_template = """#!pyobjects import salt://map.sls Pkg.removed("samba-imported", names=[map.Samba.server, map.Samba.client]) -''' +""" -recursive_map_template = '''#!pyobjects +recursive_map_template = """#!pyobjects from salt://map.sls import Samba class CustomSamba(Samba): pass -''' +""" -recursive_import_template = '''#!pyobjects +recursive_import_template = """#!pyobjects from salt://recursive_map.sls import CustomSamba -Pkg.removed("samba-imported", names=[CustomSamba.server, CustomSamba.client])''' +Pkg.removed("samba-imported", names=[CustomSamba.server, CustomSamba.client])""" -scope_test_import_template = '''#!pyobjects +scope_test_import_template = """#!pyobjects from salt://recursive_map.sls import CustomSamba # since we import CustomSamba we should shouldn't be able to see Samba -Pkg.removed("samba-imported", names=[Samba.server, Samba.client])''' +Pkg.removed("samba-imported", names=[Samba.server, Samba.client])""" -from_import_template = '''#!pyobjects +from_import_template = """#!pyobjects # this spacing is like this on purpose to ensure it's stripped properly from salt://map.sls import Samba Pkg.removed("samba-imported", names=[Samba.server, Samba.client]) -''' +""" -import_as_template = '''#!pyobjects +import_as_template = """#!pyobjects from salt://map.sls import Samba as Other Pkg.removed("samba-imported", names=[Other.server, Other.client]) -''' +""" -random_password_template = '''#!pyobjects +random_password_template = """#!pyobjects import random, string password = ''.join([random.SystemRandom().choice( string.ascii_letters + string.digits) for _ in range(20)]) -''' +""" -random_password_import_template = '''#!pyobjects +random_password_import_template = """#!pyobjects from salt://password.sls import password -''' +""" -requisite_implicit_list_template = '''#!pyobjects +requisite_implicit_list_template = """#!pyobjects from salt.utils.pyobjects import StateFactory Service = StateFactory('service') with Pkg.installed("pkg"): Service.running("service", watch=File("file"), require=Cmd("cmd")) -''' +""" class MapBuilder(object): def build_map(self, template=None): - ''' + """ Build from a specific template or just use a default if no template is passed to this function. - ''' + """ if template is None: - template = textwrap.dedent('''\ + template = textwrap.dedent( + """\ {{ ubuntu }} {{ centos }} {{ debian }} - ''') + """ + ) full_template = map_prefix + template + map_suffix ret = jinja2.Template(full_template).render(**map_data) - log.debug('built map: \n%s', ret) + log.debug("built map: \n%s", ret) return ret @@ -173,106 +182,102 @@ class StateTests(TestCase): Registry.empty() def test_serialization(self): - f = State('/usr/local/bin/pydmesg', 'file', 'managed', - require=File('/usr/local/bin'), - **pydmesg_kwargs) + f = State( + "/usr/local/bin/pydmesg", + "file", + "managed", + require=File("/usr/local/bin"), + **pydmesg_kwargs + ) self.assertEqual(f(), pydmesg_expected) def test_factory_serialization(self): - File.managed('/usr/local/bin/pydmesg', - require=File('/usr/local/bin'), - **pydmesg_kwargs) - - self.assertEqual( - Registry.states['/usr/local/bin/pydmesg'], - pydmesg_expected + File.managed( + "/usr/local/bin/pydmesg", require=File("/usr/local/bin"), **pydmesg_kwargs ) + self.assertEqual(Registry.states["/usr/local/bin/pydmesg"], pydmesg_expected) + def test_context_manager(self): - with File('/usr/local/bin'): - pydmesg = File.managed('/usr/local/bin/pydmesg', **pydmesg_kwargs) + with File("/usr/local/bin"): + pydmesg = File.managed("/usr/local/bin/pydmesg", **pydmesg_kwargs) self.assertEqual( - Registry.states['/usr/local/bin/pydmesg'], - pydmesg_expected + Registry.states["/usr/local/bin/pydmesg"], pydmesg_expected ) with pydmesg: - File.managed('/tmp/something', owner='root') + File.managed("/tmp/something", owner="root") self.assertEqual( - Registry.states['/tmp/something'], + Registry.states["/tmp/something"], { - 'file.managed': [ - {'owner': 'root'}, - {'require': [ - {'file': '/usr/local/bin'}, - {'file': '/usr/local/bin/pydmesg'} - ]}, + "file.managed": [ + {"owner": "root"}, + { + "require": [ + {"file": "/usr/local/bin"}, + {"file": "/usr/local/bin/pydmesg"}, + ] + }, ] - } + }, ) def test_salt_data(self): - File.managed('/usr/local/bin/pydmesg', - require=File('/usr/local/bin'), - **pydmesg_kwargs) - - self.assertEqual( - Registry.states['/usr/local/bin/pydmesg'], - pydmesg_expected + File.managed( + "/usr/local/bin/pydmesg", require=File("/usr/local/bin"), **pydmesg_kwargs ) - self.assertEqual( - Registry.salt_data(), - pydmesg_salt_expected - ) + self.assertEqual(Registry.states["/usr/local/bin/pydmesg"], pydmesg_expected) - self.assertEqual( - Registry.states, - OrderedDict() - ) + self.assertEqual(Registry.salt_data(), pydmesg_salt_expected) + + self.assertEqual(Registry.states, OrderedDict()) def test_duplicates(self): def add_dup(): - File.managed('dup', name='/dup') + File.managed("dup", name="/dup") add_dup() self.assertRaises(DuplicateState, add_dup) - Service.running('dup', name='dup-service') + Service.running("dup", name="dup-service") self.assertEqual( Registry.states, - OrderedDict([ - ('dup', - OrderedDict([ - ('file.managed', [ - {'name': '/dup'} - ]), - ('service.running', [ - {'name': 'dup-service'} - ]) - ])) - ]) + OrderedDict( + [ + ( + "dup", + OrderedDict( + [ + ("file.managed", [{"name": "/dup"}]), + ("service.running", [{"name": "dup-service"}]), + ] + ), + ) + ] + ), ) class RendererMixin(object): - ''' + """ This is a mixin that adds a ``.render()`` method to render a template It must come BEFORE ``TestCase`` in the declaration of your test case class so that our setUp & tearDown get invoked first, and super can trigger the methods in the ``TestCase`` class. - ''' + """ + def setUp(self, *args, **kwargs): super(RendererMixin, self).setUp(*args, **kwargs) - self.root_dir = tempfile.mkdtemp('pyobjects_test_root', dir=RUNTIME_VARS.TMP) - self.state_tree_dir = os.path.join(self.root_dir, 'state_tree') - self.cache_dir = os.path.join(self.root_dir, 'cachedir') + self.root_dir = tempfile.mkdtemp("pyobjects_test_root", dir=RUNTIME_VARS.TMP) + self.state_tree_dir = os.path.join(self.root_dir, "state_tree") + self.cache_dir = os.path.join(self.root_dir, "cachedir") if not os.path.isdir(self.root_dir): os.makedirs(self.root_dir) @@ -282,13 +287,13 @@ class RendererMixin(object): if not os.path.isdir(self.cache_dir): os.makedirs(self.cache_dir) self.config = salt.config.minion_config(None) - self.config['root_dir'] = self.root_dir - self.config['state_events'] = False - self.config['id'] = 'match' - self.config['file_client'] = 'local' - self.config['file_roots'] = dict(base=[self.state_tree_dir]) - self.config['cachedir'] = self.cache_dir - self.config['test'] = False + self.config["root_dir"] = self.root_dir + self.config["state_events"] = False + self.config["id"] = "match" + self.config["file_client"] = "local" + self.config["file_roots"] = dict(base=[self.state_tree_dir]) + self.config["cachedir"] = self.cache_dir + self.config["test"] = False def tearDown(self, *args, **kwargs): shutil.rmtree(self.root_dir) @@ -297,7 +302,7 @@ class RendererMixin(object): def write_template_file(self, filename, content): full_path = os.path.join(self.state_tree_dir, filename) - with salt.utils.files.fopen(full_path, 'w') as f: + with salt.utils.files.fopen(full_path, "w") as f: f.write(content) return full_path @@ -306,77 +311,96 @@ class RendererMixin(object): self.config.update(opts) if not filename: - filename = ".".join([ - str(uuid.uuid4()), - "sls" - ]) + filename = ".".join([str(uuid.uuid4()), "sls"]) full_path = self.write_template_file(filename, template) state = salt.state.State(self.config) - return compile_template(full_path, - state.rend, - state.opts['renderer'], - state.opts['renderer_blacklist'], - state.opts['renderer_whitelist']) + return compile_template( + full_path, + state.rend, + state.opts["renderer"], + state.opts["renderer_blacklist"], + state.opts["renderer_whitelist"], + ) class RendererTests(RendererMixin, StateTests, MapBuilder): def test_basic(self): ret = self.render(basic_template) - self.assertEqual(ret, OrderedDict([ - ('/tmp', { - 'file.directory': [ - {'group': 'root'}, - {'mode': '1777'}, - {'owner': 'root'} + self.assertEqual( + ret, + OrderedDict( + [ + ( + "/tmp", + { + "file.directory": [ + {"group": "root"}, + {"mode": "1777"}, + {"owner": "root"}, + ] + }, + ), ] - }), - ])) + ), + ) self.assertEqual(Registry.states, OrderedDict()) def test_invalid_function(self): def _test(): self.render(invalid_template) + self.assertRaises(InvalidFunction, _test) def test_include(self): ret = self.render(include_template) - self.assertEqual(ret, OrderedDict([ - ('include', ['http']), - ])) + self.assertEqual(ret, OrderedDict([("include", ["http"])])) def test_extend(self): - ret = self.render(extend_template, - {'grains': { - 'os_family': 'Debian', - 'os': 'Debian' - }}) - self.assertEqual(ret, OrderedDict([ - ('include', ['http']), - ('extend', OrderedDict([ - ('apache', { - 'service.running': [ - {'watch': [{'file': '/etc/file'}]} - ] - }), - ])), - ])) + ret = self.render( + extend_template, {"grains": {"os_family": "Debian", "os": "Debian"}} + ) + self.assertEqual( + ret, + OrderedDict( + [ + ("include", ["http"]), + ( + "extend", + OrderedDict( + [ + ( + "apache", + { + "service.running": [ + {"watch": [{"file": "/etc/file"}]} + ] + }, + ), + ] + ), + ), + ] + ), + ) def test_sls_imports(self): def render_and_assert(template): - ret = self.render(template, - {'grains': { - 'os_family': 'Debian', - 'os': 'Debian' - }}) + ret = self.render( + template, {"grains": {"os_family": "Debian", "os": "Debian"}} + ) - self.assertEqual(ret, OrderedDict([ - ('samba-imported', { - 'pkg.removed': [ - {'names': ['samba', 'samba-client']}, + self.assertEqual( + ret, + OrderedDict( + [ + ( + "samba-imported", + {"pkg.removed": [{"names": ["samba", "samba-client"]}]}, + ) ] - }) - ])) + ), + ) self.write_template_file("map.sls", self.build_map()) render_and_assert(import_template) @@ -391,70 +415,90 @@ class RendererTests(RendererMixin, StateTests, MapBuilder): self.write_template_file("recursive_map.sls", recursive_map_template) def do_render(): - ret = self.render(scope_test_import_template, - {'grains': { - 'os_family': 'Debian', - 'os': 'Debian' - }}) + ret = self.render( + scope_test_import_template, + {"grains": {"os_family": "Debian", "os": "Debian"}}, + ) self.assertRaises(NameError, do_render) def test_random_password(self): - '''Test for https://github.com/saltstack/salt/issues/21796''' + """Test for https://github.com/saltstack/salt/issues/21796""" ret = self.render(random_password_template) def test_import_random_password(self): - '''Import test for https://github.com/saltstack/salt/issues/21796''' + """Import test for https://github.com/saltstack/salt/issues/21796""" self.write_template_file("password.sls", random_password_template) ret = self.render(random_password_import_template) def test_requisite_implicit_list(self): - '''Ensure that the implicit list characteristic works as expected''' - ret = self.render(requisite_implicit_list_template, - {'grains': { - 'os_family': 'Debian', - 'os': 'Debian' - }}) + """Ensure that the implicit list characteristic works as expected""" + ret = self.render( + requisite_implicit_list_template, + {"grains": {"os_family": "Debian", "os": "Debian"}}, + ) - self.assertEqual(ret, OrderedDict([ - ('pkg', OrderedDict([ - ('pkg.installed', []) - ])), - ('service', OrderedDict([ - ('service.running', [ - {'require': [{'cmd': 'cmd'}, {'pkg': 'pkg'}]}, - {'watch': [{'file': 'file'}]}, - ]) - ])) - ])) + self.assertEqual( + ret, + OrderedDict( + [ + ("pkg", OrderedDict([("pkg.installed", [])])), + ( + "service", + OrderedDict( + [ + ( + "service.running", + [ + {"require": [{"cmd": "cmd"}, {"pkg": "pkg"}]}, + {"watch": [{"file": "file"}]}, + ], + ) + ] + ), + ), + ] + ), + ) class MapTests(RendererMixin, TestCase, MapBuilder): maxDiff = None - debian_grains = {'os_family': 'Debian', 'os': 'Debian'} - ubuntu_grains = {'os_family': 'Debian', 'os': 'Ubuntu'} - centos_grains = {'os_family': 'RedHat', 'os': 'CentOS'} + debian_grains = {"os_family": "Debian", "os": "Debian"} + ubuntu_grains = {"os_family": "Debian", "os": "Ubuntu"} + centos_grains = {"os_family": "RedHat", "os": "CentOS"} - debian_attrs = ('samba', 'samba-client', 'samba') - ubuntu_attrs = ('samba', 'samba-client', 'smbd') - centos_attrs = ('samba', 'samba', 'smb') + debian_attrs = ("samba", "samba-client", "samba") + ubuntu_attrs = ("samba", "samba-client", "smbd") + centos_attrs = ("samba", "samba", "smb") def samba_with_grains(self, template, grains): - return self.render(template, {'grains': grains}) + return self.render(template, {"grains": grains}) def assert_equal(self, ret, server, client, service): - self.assertDictEqual(ret, OrderedDict([ - ('samba', OrderedDict([ - ('pkg.installed', [ - {'names': [server, client]} - ]), - ('service.running', [ - {'name': service}, - {'require': [{'pkg': 'samba'}]} - ]) - ])) - ])) + self.assertDictEqual( + ret, + OrderedDict( + [ + ( + "samba", + OrderedDict( + [ + ("pkg.installed", [{"names": [server, client]}]), + ( + "service.running", + [ + {"name": service}, + {"require": [{"pkg": "samba"}]}, + ], + ), + ] + ), + ) + ] + ), + ) def assert_not_equal(self, ret, server, client, service): try: @@ -462,19 +506,23 @@ class MapTests(RendererMixin, TestCase, MapBuilder): except AssertionError: pass else: - raise AssertionError('both dicts are equal') + raise AssertionError("both dicts are equal") def test_map(self): - ''' + """ Test declarative ordering - ''' + """ # With declarative ordering, the ubuntu-specfic service name should # override the one inherited from debian. - template = self.build_map(textwrap.dedent('''\ + template = self.build_map( + textwrap.dedent( + """\ {{ debian }} {{ centos }} {{ ubuntu }} - ''')) + """ + ) + ) ret = self.samba_with_grains(template, self.debian_grains) self.assert_equal(ret, *self.debian_attrs) @@ -488,10 +536,14 @@ class MapTests(RendererMixin, TestCase, MapBuilder): # Switching the order, debian should still work fine but ubuntu should # no longer match, since the debian service name should override the # ubuntu one. - template = self.build_map(textwrap.dedent('''\ + template = self.build_map( + textwrap.dedent( + """\ {{ ubuntu }} {{ debian }} - ''')) + """ + ) + ) ret = self.samba_with_grains(template, self.debian_grains) self.assert_equal(ret, *self.debian_attrs) @@ -500,17 +552,21 @@ class MapTests(RendererMixin, TestCase, MapBuilder): self.assert_not_equal(ret, *self.ubuntu_attrs) def test_map_with_priority(self): - ''' + """ With declarative ordering, the debian service name would override the ubuntu one since debian comes second. This will test overriding this behavior using the priority attribute. - ''' - template = self.build_map(textwrap.dedent('''\ + """ + template = self.build_map( + textwrap.dedent( + """\ {{ priority(('os_family', 'os')) }} {{ ubuntu }} {{ centos }} {{ debian }} - ''')) + """ + ) + ) ret = self.samba_with_grains(template, self.debian_grains) self.assert_equal(ret, *self.debian_attrs) @@ -528,11 +584,9 @@ class SaltObjectTests(TestCase): Salt.fail.blah() def times2(x): - return x*2 + return x * 2 - __salt__ = { - 'math.times2': times2 - } + __salt__ = {"math.times2": times2} Salt = SaltObject(__salt__) diff --git a/tests/unit/utils/test_reactor.py b/tests/unit/utils/test_reactor.py index dc7b5c791f4..624d8472689 100644 --- a/tests/unit/utils/test_reactor.py +++ b/tests/unit/utils/test_reactor.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals + import codecs import glob import logging @@ -12,17 +13,11 @@ import salt.utils.data import salt.utils.files import salt.utils.reactor as reactor import salt.utils.yaml - -from tests.support.unit import TestCase from tests.support.mixins import AdaptedConfigurationTestCaseMixin -from tests.support.mock import ( - patch, - MagicMock, - Mock, - mock_open, -) +from tests.support.mock import MagicMock, Mock, mock_open, patch +from tests.support.unit import TestCase -REACTOR_CONFIG = '''\ +REACTOR_CONFIG = """\ reactor: - old_runner: - /srv/reactor/old_runner.sls @@ -44,29 +39,34 @@ reactor: - /srv/reactor/new_cmd.sls - new_caller: - /srv/reactor/new_caller.sls -''' +""" REACTOR_DATA = { - 'runner': {'data': {'message': 'This is an error'}}, - 'wheel': {'data': {'id': 'foo'}}, - 'local': {'data': {'pkg': 'zsh', 'repo': 'updates'}}, - 'cmd': {'data': {'pkg': 'zsh', 'repo': 'updates'}}, - 'caller': {'data': {'path': '/tmp/foo'}}, + "runner": {"data": {"message": "This is an error"}}, + "wheel": {"data": {"id": "foo"}}, + "local": {"data": {"pkg": "zsh", "repo": "updates"}}, + "cmd": {"data": {"pkg": "zsh", "repo": "updates"}}, + "caller": {"data": {"path": "/tmp/foo"}}, } SLS = { - '/srv/reactor/old_runner.sls': textwrap.dedent('''\ + "/srv/reactor/old_runner.sls": textwrap.dedent( + """\ raise_error: runner.error.error: - name: Exception - message: {{ data['data']['message'] }} - '''), - '/srv/reactor/old_wheel.sls': textwrap.dedent('''\ + """ + ), + "/srv/reactor/old_wheel.sls": textwrap.dedent( + """\ remove_key: wheel.key.delete: - match: {{ data['data']['id'] }} - '''), - '/srv/reactor/old_local.sls': textwrap.dedent('''\ + """ + ), + "/srv/reactor/old_local.sls": textwrap.dedent( + """\ install_zsh: local.state.single: - tgt: test @@ -75,8 +75,10 @@ SLS = { - {{ data['data']['pkg'] }} - kwarg: fromrepo: {{ data['data']['repo'] }} - '''), - '/srv/reactor/old_cmd.sls': textwrap.dedent('''\ + """ + ), + "/srv/reactor/old_cmd.sls": textwrap.dedent( + """\ install_zsh: cmd.state.single: - tgt: test @@ -85,27 +87,35 @@ SLS = { - {{ data['data']['pkg'] }} - kwarg: fromrepo: {{ data['data']['repo'] }} - '''), - '/srv/reactor/old_caller.sls': textwrap.dedent('''\ + """ + ), + "/srv/reactor/old_caller.sls": textwrap.dedent( + """\ touch_file: caller.file.touch: - args: - {{ data['data']['path'] }} - '''), - '/srv/reactor/new_runner.sls': textwrap.dedent('''\ + """ + ), + "/srv/reactor/new_runner.sls": textwrap.dedent( + """\ raise_error: runner.error.error: - args: - name: Exception - message: {{ data['data']['message'] }} - '''), - '/srv/reactor/new_wheel.sls': textwrap.dedent('''\ + """ + ), + "/srv/reactor/new_wheel.sls": textwrap.dedent( + """\ remove_key: wheel.key.delete: - args: - match: {{ data['data']['id'] }} - '''), - '/srv/reactor/new_local.sls': textwrap.dedent('''\ + """ + ), + "/srv/reactor/new_local.sls": textwrap.dedent( + """\ install_zsh: local.state.single: - tgt: test @@ -113,8 +123,10 @@ SLS = { - fun: pkg.installed - name: {{ data['data']['pkg'] }} - fromrepo: {{ data['data']['repo'] }} - '''), - '/srv/reactor/new_cmd.sls': textwrap.dedent('''\ + """ + ), + "/srv/reactor/new_cmd.sls": textwrap.dedent( + """\ install_zsh: cmd.state.single: - tgt: test @@ -122,278 +134,275 @@ SLS = { - fun: pkg.installed - name: {{ data['data']['pkg'] }} - fromrepo: {{ data['data']['repo'] }} - '''), - '/srv/reactor/new_caller.sls': textwrap.dedent('''\ + """ + ), + "/srv/reactor/new_caller.sls": textwrap.dedent( + """\ touch_file: caller.file.touch: - args: - name: {{ data['data']['path'] }} - '''), + """ + ), } LOW_CHUNKS = { # Note that the "name" value in the chunk has been overwritten by the # "name" argument in the SLS. This is one reason why the new schema was # needed. - 'old_runner': [{ - 'state': 'runner', - '__id__': 'raise_error', - '__sls__': '/srv/reactor/old_runner.sls', - 'order': 1, - 'fun': 'error.error', - 'name': 'Exception', - 'message': 'This is an error', - }], - 'old_wheel': [{ - 'state': 'wheel', - '__id__': 'remove_key', - 'name': 'remove_key', - '__sls__': '/srv/reactor/old_wheel.sls', - 'order': 1, - 'fun': 'key.delete', - 'match': 'foo', - }], - 'old_local': [{ - 'state': 'local', - '__id__': 'install_zsh', - 'name': 'install_zsh', - '__sls__': '/srv/reactor/old_local.sls', - 'order': 1, - 'tgt': 'test', - 'fun': 'state.single', - 'arg': ['pkg.installed', 'zsh'], - 'kwarg': {'fromrepo': 'updates'}, - }], - 'old_cmd': [{ - 'state': 'local', # 'cmd' should be aliased to 'local' - '__id__': 'install_zsh', - 'name': 'install_zsh', - '__sls__': '/srv/reactor/old_cmd.sls', - 'order': 1, - 'tgt': 'test', - 'fun': 'state.single', - 'arg': ['pkg.installed', 'zsh'], - 'kwarg': {'fromrepo': 'updates'}, - }], - 'old_caller': [{ - 'state': 'caller', - '__id__': 'touch_file', - 'name': 'touch_file', - '__sls__': '/srv/reactor/old_caller.sls', - 'order': 1, - 'fun': 'file.touch', - 'args': ['/tmp/foo'], - }], - 'new_runner': [{ - 'state': 'runner', - '__id__': 'raise_error', - 'name': 'raise_error', - '__sls__': '/srv/reactor/new_runner.sls', - 'order': 1, - 'fun': 'error.error', - 'args': [ - {'name': 'Exception'}, - {'message': 'This is an error'}, - ], - }], - 'new_wheel': [{ - 'state': 'wheel', - '__id__': 'remove_key', - 'name': 'remove_key', - '__sls__': '/srv/reactor/new_wheel.sls', - 'order': 1, - 'fun': 'key.delete', - 'args': [ - {'match': 'foo'}, - ], - }], - 'new_local': [{ - 'state': 'local', - '__id__': 'install_zsh', - 'name': 'install_zsh', - '__sls__': '/srv/reactor/new_local.sls', - 'order': 1, - 'tgt': 'test', - 'fun': 'state.single', - 'args': [ - {'fun': 'pkg.installed'}, - {'name': 'zsh'}, - {'fromrepo': 'updates'}, - ], - }], - 'new_cmd': [{ - 'state': 'local', - '__id__': 'install_zsh', - 'name': 'install_zsh', - '__sls__': '/srv/reactor/new_cmd.sls', - 'order': 1, - 'tgt': 'test', - 'fun': 'state.single', - 'args': [ - {'fun': 'pkg.installed'}, - {'name': 'zsh'}, - {'fromrepo': 'updates'}, - ], - }], - 'new_caller': [{ - 'state': 'caller', - '__id__': 'touch_file', - 'name': 'touch_file', - '__sls__': '/srv/reactor/new_caller.sls', - 'order': 1, - 'fun': 'file.touch', - 'args': [ - {'name': '/tmp/foo'}, - ], - }], + "old_runner": [ + { + "state": "runner", + "__id__": "raise_error", + "__sls__": "/srv/reactor/old_runner.sls", + "order": 1, + "fun": "error.error", + "name": "Exception", + "message": "This is an error", + } + ], + "old_wheel": [ + { + "state": "wheel", + "__id__": "remove_key", + "name": "remove_key", + "__sls__": "/srv/reactor/old_wheel.sls", + "order": 1, + "fun": "key.delete", + "match": "foo", + } + ], + "old_local": [ + { + "state": "local", + "__id__": "install_zsh", + "name": "install_zsh", + "__sls__": "/srv/reactor/old_local.sls", + "order": 1, + "tgt": "test", + "fun": "state.single", + "arg": ["pkg.installed", "zsh"], + "kwarg": {"fromrepo": "updates"}, + } + ], + "old_cmd": [ + { + "state": "local", # 'cmd' should be aliased to 'local' + "__id__": "install_zsh", + "name": "install_zsh", + "__sls__": "/srv/reactor/old_cmd.sls", + "order": 1, + "tgt": "test", + "fun": "state.single", + "arg": ["pkg.installed", "zsh"], + "kwarg": {"fromrepo": "updates"}, + } + ], + "old_caller": [ + { + "state": "caller", + "__id__": "touch_file", + "name": "touch_file", + "__sls__": "/srv/reactor/old_caller.sls", + "order": 1, + "fun": "file.touch", + "args": ["/tmp/foo"], + } + ], + "new_runner": [ + { + "state": "runner", + "__id__": "raise_error", + "name": "raise_error", + "__sls__": "/srv/reactor/new_runner.sls", + "order": 1, + "fun": "error.error", + "args": [{"name": "Exception"}, {"message": "This is an error"}], + } + ], + "new_wheel": [ + { + "state": "wheel", + "__id__": "remove_key", + "name": "remove_key", + "__sls__": "/srv/reactor/new_wheel.sls", + "order": 1, + "fun": "key.delete", + "args": [{"match": "foo"}], + } + ], + "new_local": [ + { + "state": "local", + "__id__": "install_zsh", + "name": "install_zsh", + "__sls__": "/srv/reactor/new_local.sls", + "order": 1, + "tgt": "test", + "fun": "state.single", + "args": [ + {"fun": "pkg.installed"}, + {"name": "zsh"}, + {"fromrepo": "updates"}, + ], + } + ], + "new_cmd": [ + { + "state": "local", + "__id__": "install_zsh", + "name": "install_zsh", + "__sls__": "/srv/reactor/new_cmd.sls", + "order": 1, + "tgt": "test", + "fun": "state.single", + "args": [ + {"fun": "pkg.installed"}, + {"name": "zsh"}, + {"fromrepo": "updates"}, + ], + } + ], + "new_caller": [ + { + "state": "caller", + "__id__": "touch_file", + "name": "touch_file", + "__sls__": "/srv/reactor/new_caller.sls", + "order": 1, + "fun": "file.touch", + "args": [{"name": "/tmp/foo"}], + } + ], } WRAPPER_CALLS = { - 'old_runner': ( - 'error.error', + "old_runner": ( + "error.error", { - '__state__': 'runner', - '__id__': 'raise_error', - '__sls__': '/srv/reactor/old_runner.sls', - '__user__': 'Reactor', - 'order': 1, - 'arg': [], - 'kwarg': { - 'name': 'Exception', - 'message': 'This is an error', - }, - 'name': 'Exception', - 'message': 'This is an error', + "__state__": "runner", + "__id__": "raise_error", + "__sls__": "/srv/reactor/old_runner.sls", + "__user__": "Reactor", + "order": 1, + "arg": [], + "kwarg": {"name": "Exception", "message": "This is an error"}, + "name": "Exception", + "message": "This is an error", }, ), - 'old_wheel': ( - 'key.delete', + "old_wheel": ( + "key.delete", { - '__state__': 'wheel', - '__id__': 'remove_key', - 'name': 'remove_key', - '__sls__': '/srv/reactor/old_wheel.sls', - 'order': 1, - '__user__': 'Reactor', - 'arg': ['foo'], - 'kwarg': {}, - 'match': 'foo', + "__state__": "wheel", + "__id__": "remove_key", + "name": "remove_key", + "__sls__": "/srv/reactor/old_wheel.sls", + "order": 1, + "__user__": "Reactor", + "arg": ["foo"], + "kwarg": {}, + "match": "foo", }, ), - 'old_local': { - 'args': ('test', 'state.single'), - 'kwargs': { - 'state': 'local', - '__id__': 'install_zsh', - 'name': 'install_zsh', - '__sls__': '/srv/reactor/old_local.sls', - 'order': 1, - 'arg': ['pkg.installed', 'zsh'], - 'kwarg': {'fromrepo': 'updates'}, + "old_local": { + "args": ("test", "state.single"), + "kwargs": { + "state": "local", + "__id__": "install_zsh", + "name": "install_zsh", + "__sls__": "/srv/reactor/old_local.sls", + "order": 1, + "arg": ["pkg.installed", "zsh"], + "kwarg": {"fromrepo": "updates"}, }, }, - 'old_cmd': { - 'args': ('test', 'state.single'), - 'kwargs': { - 'state': 'local', - '__id__': 'install_zsh', - 'name': 'install_zsh', - '__sls__': '/srv/reactor/old_cmd.sls', - 'order': 1, - 'arg': ['pkg.installed', 'zsh'], - 'kwarg': {'fromrepo': 'updates'}, + "old_cmd": { + "args": ("test", "state.single"), + "kwargs": { + "state": "local", + "__id__": "install_zsh", + "name": "install_zsh", + "__sls__": "/srv/reactor/old_cmd.sls", + "order": 1, + "arg": ["pkg.installed", "zsh"], + "kwarg": {"fromrepo": "updates"}, }, }, - 'old_caller': { - 'args': ('file.touch', '/tmp/foo'), - 'kwargs': {}, - }, - 'new_runner': ( - 'error.error', + "old_caller": {"args": ("file.touch", "/tmp/foo"), "kwargs": {}}, + "new_runner": ( + "error.error", { - '__state__': 'runner', - '__id__': 'raise_error', - 'name': 'raise_error', - '__sls__': '/srv/reactor/new_runner.sls', - '__user__': 'Reactor', - 'order': 1, - 'arg': (), - 'kwarg': { - 'name': 'Exception', - 'message': 'This is an error', - }, + "__state__": "runner", + "__id__": "raise_error", + "name": "raise_error", + "__sls__": "/srv/reactor/new_runner.sls", + "__user__": "Reactor", + "order": 1, + "arg": (), + "kwarg": {"name": "Exception", "message": "This is an error"}, }, ), - 'new_wheel': ( - 'key.delete', + "new_wheel": ( + "key.delete", { - '__state__': 'wheel', - '__id__': 'remove_key', - 'name': 'remove_key', - '__sls__': '/srv/reactor/new_wheel.sls', - 'order': 1, - '__user__': 'Reactor', - 'arg': (), - 'kwarg': {'match': 'foo'}, + "__state__": "wheel", + "__id__": "remove_key", + "name": "remove_key", + "__sls__": "/srv/reactor/new_wheel.sls", + "order": 1, + "__user__": "Reactor", + "arg": (), + "kwarg": {"match": "foo"}, }, ), - 'new_local': { - 'args': ('test', 'state.single'), - 'kwargs': { - 'state': 'local', - '__id__': 'install_zsh', - 'name': 'install_zsh', - '__sls__': '/srv/reactor/new_local.sls', - 'order': 1, - 'arg': (), - 'kwarg': { - 'fun': 'pkg.installed', - 'name': 'zsh', - 'fromrepo': 'updates', - }, + "new_local": { + "args": ("test", "state.single"), + "kwargs": { + "state": "local", + "__id__": "install_zsh", + "name": "install_zsh", + "__sls__": "/srv/reactor/new_local.sls", + "order": 1, + "arg": (), + "kwarg": {"fun": "pkg.installed", "name": "zsh", "fromrepo": "updates"}, }, }, - 'new_cmd': { - 'args': ('test', 'state.single'), - 'kwargs': { - 'state': 'local', - '__id__': 'install_zsh', - 'name': 'install_zsh', - '__sls__': '/srv/reactor/new_cmd.sls', - 'order': 1, - 'arg': (), - 'kwarg': { - 'fun': 'pkg.installed', - 'name': 'zsh', - 'fromrepo': 'updates', - }, + "new_cmd": { + "args": ("test", "state.single"), + "kwargs": { + "state": "local", + "__id__": "install_zsh", + "name": "install_zsh", + "__sls__": "/srv/reactor/new_cmd.sls", + "order": 1, + "arg": (), + "kwarg": {"fun": "pkg.installed", "name": "zsh", "fromrepo": "updates"}, }, }, - 'new_caller': { - 'args': ('file.touch',), - 'kwargs': {'name': '/tmp/foo'}, - }, + "new_caller": {"args": ("file.touch",), "kwargs": {"name": "/tmp/foo"}}, } log = logging.getLogger(__name__) class TestReactor(TestCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Tests for constructing the low chunks to be executed via the Reactor - ''' + """ + @classmethod def setUpClass(cls): - ''' + """ Load the reactor config for mocking - ''' - cls.opts = cls.get_temp_config('master') + """ + cls.opts = cls.get_temp_config("master") reactor_config = salt.utils.yaml.safe_load(REACTOR_CONFIG) cls.opts.update(reactor_config) cls.reactor = reactor.Reactor(cls.opts) - cls.reaction_map = salt.utils.data.repack_dictlist(reactor_config['reactor']) + cls.reaction_map = salt.utils.data.repack_dictlist(reactor_config["reactor"]) renderers = salt.loader.render(cls.opts, {}) - cls.render_pipe = [(renderers[x], '') for x in ('jinja', 'yaml')] + cls.render_pipe = [(renderers[x], "") for x in ("jinja", "yaml")] @classmethod def tearDownClass(cls): @@ -402,152 +411,143 @@ class TestReactor(TestCase, AdaptedConfigurationTestCaseMixin): del cls.render_pipe def test_list_reactors(self): - ''' + """ Ensure that list_reactors() returns the correct list of reactor SLS files for each tag. - ''' - for schema in ('old', 'new'): + """ + for schema in ("old", "new"): for rtype in REACTOR_DATA: - tag = '_'.join((schema, rtype)) + tag = "_".join((schema, rtype)) self.assertEqual( - self.reactor.list_reactors(tag), - self.reaction_map[tag] + self.reactor.list_reactors(tag), self.reaction_map[tag] ) def test_reactions(self): - ''' + """ Ensure that the correct reactions are built from the configured SLS files and tag data. - ''' - for schema in ('old', 'new'): + """ + for schema in ("old", "new"): for rtype in REACTOR_DATA: - tag = '_'.join((schema, rtype)) - log.debug('test_reactions: processing %s', tag) + tag = "_".join((schema, rtype)) + log.debug("test_reactions: processing %s", tag) reactors = self.reactor.list_reactors(tag) - log.debug('test_reactions: %s reactors: %s', tag, reactors) + log.debug("test_reactions: %s reactors: %s", tag, reactors) # No globbing in our example SLS, and the files don't actually # exist, so mock glob.glob to just return back the path passed # to it. - with patch.object( - glob, - 'glob', - MagicMock(side_effect=lambda x: [x])): + with patch.object(glob, "glob", MagicMock(side_effect=lambda x: [x])): # The below four mocks are all so that # salt.template.compile_template() will read the templates # we've mocked up in the SLS global variable above. - with patch.object( - os.path, 'isfile', - MagicMock(return_value=True)): + with patch.object(os.path, "isfile", MagicMock(return_value=True)): with patch.object( - salt.utils.files, 'is_empty', - MagicMock(return_value=False)): + salt.utils.files, "is_empty", MagicMock(return_value=False) + ): with patch.object( - codecs, 'open', - mock_open(read_data=SLS[reactors[0]])): + codecs, "open", mock_open(read_data=SLS[reactors[0]]) + ): with patch.object( - salt.template, 'template_shebang', - MagicMock(return_value=self.render_pipe)): + salt.template, + "template_shebang", + MagicMock(return_value=self.render_pipe), + ): reactions = self.reactor.reactions( - tag, - REACTOR_DATA[rtype], - reactors, + tag, REACTOR_DATA[rtype], reactors, ) log.debug( - 'test_reactions: %s reactions: %s', - tag, reactions + "test_reactions: %s reactions: %s", + tag, + reactions, ) self.assertEqual(reactions, LOW_CHUNKS[tag]) class TestReactWrap(TestCase, AdaptedConfigurationTestCaseMixin): - ''' + """ Tests that we are formulating the wrapper calls properly - ''' + """ + @classmethod def setUpClass(cls): - cls.wrap = reactor.ReactWrap(cls.get_temp_config('master')) + cls.wrap = reactor.ReactWrap(cls.get_temp_config("master")) @classmethod def tearDownClass(cls): del cls.wrap def test_runner(self): - ''' + """ Test runner reactions using both the old and new config schema - ''' - for schema in ('old', 'new'): - tag = '_'.join((schema, 'runner')) + """ + for schema in ("old", "new"): + tag = "_".join((schema, "runner")) chunk = LOW_CHUNKS[tag][0] thread_pool = Mock() thread_pool.fire_async = Mock() - with patch.object(self.wrap, 'pool', thread_pool): + with patch.object(self.wrap, "pool", thread_pool): self.wrap.run(chunk) thread_pool.fire_async.assert_called_with( - self.wrap.client_cache['runner'].low, - args=WRAPPER_CALLS[tag] + self.wrap.client_cache["runner"].low, args=WRAPPER_CALLS[tag] ) def test_wheel(self): - ''' + """ Test wheel reactions using both the old and new config schema - ''' - for schema in ('old', 'new'): - tag = '_'.join((schema, 'wheel')) + """ + for schema in ("old", "new"): + tag = "_".join((schema, "wheel")) chunk = LOW_CHUNKS[tag][0] thread_pool = Mock() thread_pool.fire_async = Mock() - with patch.object(self.wrap, 'pool', thread_pool): + with patch.object(self.wrap, "pool", thread_pool): self.wrap.run(chunk) thread_pool.fire_async.assert_called_with( - self.wrap.client_cache['wheel'].low, - args=WRAPPER_CALLS[tag] + self.wrap.client_cache["wheel"].low, args=WRAPPER_CALLS[tag] ) def test_local(self): - ''' + """ Test local reactions using both the old and new config schema - ''' - for schema in ('old', 'new'): - tag = '_'.join((schema, 'local')) + """ + for schema in ("old", "new"): + tag = "_".join((schema, "local")) chunk = LOW_CHUNKS[tag][0] - client_cache = {'local': Mock()} - client_cache['local'].cmd_async = Mock() - with patch.object(self.wrap, 'client_cache', client_cache): + client_cache = {"local": Mock()} + client_cache["local"].cmd_async = Mock() + with patch.object(self.wrap, "client_cache", client_cache): self.wrap.run(chunk) - client_cache['local'].cmd_async.assert_called_with( - *WRAPPER_CALLS[tag]['args'], - **WRAPPER_CALLS[tag]['kwargs'] + client_cache["local"].cmd_async.assert_called_with( + *WRAPPER_CALLS[tag]["args"], **WRAPPER_CALLS[tag]["kwargs"] ) def test_cmd(self): - ''' + """ Test cmd reactions (alias for 'local') using both the old and new config schema - ''' - for schema in ('old', 'new'): - tag = '_'.join((schema, 'cmd')) + """ + for schema in ("old", "new"): + tag = "_".join((schema, "cmd")) chunk = LOW_CHUNKS[tag][0] - client_cache = {'local': Mock()} - client_cache['local'].cmd_async = Mock() - with patch.object(self.wrap, 'client_cache', client_cache): + client_cache = {"local": Mock()} + client_cache["local"].cmd_async = Mock() + with patch.object(self.wrap, "client_cache", client_cache): self.wrap.run(chunk) - client_cache['local'].cmd_async.assert_called_with( - *WRAPPER_CALLS[tag]['args'], - **WRAPPER_CALLS[tag]['kwargs'] + client_cache["local"].cmd_async.assert_called_with( + *WRAPPER_CALLS[tag]["args"], **WRAPPER_CALLS[tag]["kwargs"] ) def test_caller(self): - ''' + """ Test caller reactions using both the old and new config schema - ''' - for schema in ('old', 'new'): - tag = '_'.join((schema, 'caller')) + """ + for schema in ("old", "new"): + tag = "_".join((schema, "caller")) chunk = LOW_CHUNKS[tag][0] - client_cache = {'caller': Mock()} - client_cache['caller'].cmd = Mock() - with patch.object(self.wrap, 'client_cache', client_cache): + client_cache = {"caller": Mock()} + client_cache["caller"].cmd = Mock() + with patch.object(self.wrap, "client_cache", client_cache): self.wrap.run(chunk) - client_cache['caller'].cmd.assert_called_with( - *WRAPPER_CALLS[tag]['args'], - **WRAPPER_CALLS[tag]['kwargs'] + client_cache["caller"].cmd.assert_called_with( + *WRAPPER_CALLS[tag]["args"], **WRAPPER_CALLS[tag]["kwargs"] ) diff --git a/tests/unit/utils/test_roster_matcher.py b/tests/unit/utils/test_roster_matcher.py index 667066f6d6a..51fe66ceaba 100644 --- a/tests/unit/utils/test_roster_matcher.py +++ b/tests/unit/utils/test_roster_matcher.py @@ -1,51 +1,48 @@ # -*- coding: utf-8 -*- -''' +""" Test generic roster matching utility. -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support import mixins -from tests.support.unit import skipIf, TestCase -from tests.support.runtests import RUNTIME_VARS +import os # Import Salt Libs import salt.config import salt.loader import salt.utils.roster_matcher +# Import Salt Testing Libs +from tests.support import mixins +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + EXPECTED = { - 'host1': { - 'host': 'host1', - 'passwd': 'test123', - 'minion_opts': { - 'escape_pods': 2, - 'halon_system_timeout': 30, - 'self_destruct_countdown': 60, - 'some_server': 'foo.southeast.example.com' - } + "host1": { + "host": "host1", + "passwd": "test123", + "minion_opts": { + "escape_pods": 2, + "halon_system_timeout": 30, + "self_destruct_countdown": 60, + "some_server": "foo.southeast.example.com", + }, }, - 'host2': { - 'host': 'host2', - 'passwd': 'test123', - 'minion_opts': { - 'escape_pods': 2, - 'halon_system_timeout': 30, - 'self_destruct_countdown': 60, - 'some_server': 'foo.southeast.example.com' - } + "host2": { + "host": "host2", + "passwd": "test123", + "minion_opts": { + "escape_pods": 2, + "halon_system_timeout": 30, + "self_destruct_countdown": 60, + "some_server": "foo.southeast.example.com", + }, }, - 'host3': { - 'host': 'host3', - 'passwd': 'test123', - 'minion_opts': {} - }, - 'host4': 'host4.example.com', # For testing get_data -> string_types branch - 'host5': None, # For testing get_data -> False + "host3": {"host": "host3", "passwd": "test123", "minion_opts": {}}, + "host4": "host4.example.com", # For testing get_data -> string_types branch + "host5": None, # For testing get_data -> False } @@ -55,19 +52,21 @@ class RosterMatcherTestCase(TestCase, mixins.LoaderModuleMockMixin): """ def setup_loader_modules(self): - opts = salt.config.master_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'master')) - utils = salt.loader.utils(opts, whitelist=['json', 'stringutils']) - runner = salt.loader.runner(opts, utils=utils, whitelist=['salt']) + opts = salt.config.master_config( + os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master") + ) + utils = salt.loader.utils(opts, whitelist=["json", "stringutils"]) + runner = salt.loader.runner(opts, utils=utils, whitelist=["salt"]) return { salt.utils.roster_matcher: { - '__utils__': utils, - '__opts__': { - 'ssh_list_nodegroups': { - 'list_nodegroup': ['host5', 'host1', 'host2'], - 'string_nodegroup': 'host3,host5,host1,host4', + "__utils__": utils, + "__opts__": { + "ssh_list_nodegroups": { + "list_nodegroup": ["host5", "host1", "host2"], + "string_nodegroup": "host3,host5,host1,host4", } }, - '__runner__': runner + "__runner__": runner, } } @@ -76,63 +75,69 @@ class RosterMatcherTestCase(TestCase, mixins.LoaderModuleMockMixin): Test that the get_data method returns the expected dictionaries. """ # We don't care about tgt and tgt_type here. - roster_matcher = salt.utils.roster_matcher.RosterMatcher(EXPECTED, 'tgt', 'tgt_type') - self.assertEqual(EXPECTED['host1'], roster_matcher.get_data('host1')) - self.assertEqual(EXPECTED['host2'], roster_matcher.get_data('host2')) - self.assertEqual(EXPECTED['host3'], roster_matcher.get_data('host3')) - self.assertEqual({'host': EXPECTED['host4']}, roster_matcher.get_data('host4')) + roster_matcher = salt.utils.roster_matcher.RosterMatcher( + EXPECTED, "tgt", "tgt_type" + ) + self.assertEqual(EXPECTED["host1"], roster_matcher.get_data("host1")) + self.assertEqual(EXPECTED["host2"], roster_matcher.get_data("host2")) + self.assertEqual(EXPECTED["host3"], roster_matcher.get_data("host3")) + self.assertEqual({"host": EXPECTED["host4"]}, roster_matcher.get_data("host4")) def test_ret_glob_minions(self): """ Test that we return minions matching a glob. """ - result = salt.utils.roster_matcher.targets(EXPECTED, '*[245]', 'glob') - self.assertNotIn('host1', result) - self.assertIn('host2', result) - self.assertNotIn('host3', result) - self.assertIn('host4', result) - self.assertNotIn('host5', result) + result = salt.utils.roster_matcher.targets(EXPECTED, "*[245]", "glob") + self.assertNotIn("host1", result) + self.assertIn("host2", result) + self.assertNotIn("host3", result) + self.assertIn("host4", result) + self.assertNotIn("host5", result) def test_ret_pcre_minions(self): """ Test that we return minions matching a regular expression. """ - result = salt.utils.roster_matcher.targets(EXPECTED, '.*[^23]$', 'pcre') - self.assertIn('host1', result) - self.assertNotIn('host2', result) - self.assertNotIn('host3', result) - self.assertIn('host4', result) - self.assertNotIn('host5', result) + result = salt.utils.roster_matcher.targets(EXPECTED, ".*[^23]$", "pcre") + self.assertIn("host1", result) + self.assertNotIn("host2", result) + self.assertNotIn("host3", result) + self.assertIn("host4", result) + self.assertNotIn("host5", result) def test_ret_literal_list_minions(self): """ Test that we return minions that are in a literal list. """ - result = salt.utils.roster_matcher.targets(EXPECTED, ['host1', 'host2', 'host5'], 'list') - self.assertIn('host1', result) - self.assertIn('host2', result) - self.assertNotIn('host3', result) - self.assertNotIn('host4', result) - self.assertNotIn('host5', result) + result = salt.utils.roster_matcher.targets( + EXPECTED, ["host1", "host2", "host5"], "list" + ) + self.assertIn("host1", result) + self.assertIn("host2", result) + self.assertNotIn("host3", result) + self.assertNotIn("host4", result) + self.assertNotIn("host5", result) def test_ret_comma_delimited_string_minions(self): """ Test that we return minions that are in a comma-delimited string of literal minion names. """ - result = salt.utils.roster_matcher.targets(EXPECTED, 'host5,host3,host2', 'list') - self.assertNotIn('host1', result) - self.assertIn('host2', result) - self.assertIn('host3', result) - self.assertNotIn('host4', result) - self.assertNotIn('host5', result) + result = salt.utils.roster_matcher.targets( + EXPECTED, "host5,host3,host2", "list" + ) + self.assertNotIn("host1", result) + self.assertIn("host2", result) + self.assertIn("host3", result) + self.assertNotIn("host4", result) + self.assertNotIn("host5", result) def test_ret_oops_minions(self): """ Test that we return no minions when we try to use a matching method that is not defined. """ - result = salt.utils.roster_matcher.targets(EXPECTED, None, 'xyzzy') + result = salt.utils.roster_matcher.targets(EXPECTED, None, "xyzzy") self.assertEqual({}, result) def test_ret_literal_list_nodegroup_minions(self): @@ -140,12 +145,14 @@ class RosterMatcherTestCase(TestCase, mixins.LoaderModuleMockMixin): Test that we return minions that are in a nodegroup where the nodegroup expresses a literal list of minion names. """ - result = salt.utils.roster_matcher.targets(EXPECTED, 'list_nodegroup', 'nodegroup') - self.assertIn('host1', result) - self.assertIn('host2', result) - self.assertNotIn('host3', result) - self.assertNotIn('host4', result) - self.assertNotIn('host5', result) + result = salt.utils.roster_matcher.targets( + EXPECTED, "list_nodegroup", "nodegroup" + ) + self.assertIn("host1", result) + self.assertIn("host2", result) + self.assertNotIn("host3", result) + self.assertNotIn("host4", result) + self.assertNotIn("host5", result) def test_ret_comma_delimited_string_nodegroup_minions(self): """ @@ -153,12 +160,14 @@ class RosterMatcherTestCase(TestCase, mixins.LoaderModuleMockMixin): where the nodegroup expresses a comma delimited string of minion names. """ - result = salt.utils.roster_matcher.targets(EXPECTED, 'string_nodegroup', 'nodegroup') - self.assertIn('host1', result) - self.assertNotIn('host2', result) - self.assertIn('host3', result) - self.assertIn('host4', result) - self.assertNotIn('host5', result) + result = salt.utils.roster_matcher.targets( + EXPECTED, "string_nodegroup", "nodegroup" + ) + self.assertIn("host1", result) + self.assertNotIn("host2", result) + self.assertIn("host3", result) + self.assertIn("host4", result) + self.assertNotIn("host5", result) def test_ret_no_range_installed_minions(self): """ @@ -166,11 +175,11 @@ class RosterMatcherTestCase(TestCase, mixins.LoaderModuleMockMixin): """ salt.utils.roster_matcher.HAS_RANGE = False with self.assertRaises(RuntimeError): - salt.utils.roster_matcher.targets(EXPECTED, None, 'range') + salt.utils.roster_matcher.targets(EXPECTED, None, "range") - @skipIf(not salt.utils.roster_matcher.HAS_RANGE, 'seco.range is not installed') + @skipIf(not salt.utils.roster_matcher.HAS_RANGE, "seco.range is not installed") def test_ret_range_minions(self): """ Test that range matcher raises a Runtime Error if seco.range is not installed. """ - self.fail('Not implemented') + self.fail("Not implemented") diff --git a/tests/unit/utils/test_rsax931.py b/tests/unit/utils/test_rsax931.py index 9f428744b60..6b840370e16 100644 --- a/tests/unit/utils/test_rsax931.py +++ b/tests/unit/utils/test_rsax931.py @@ -1,107 +1,110 @@ # coding: utf-8 -''' +""" Test the RSA ANSI X9.31 signer and verifier -''' +""" # python libs from __future__ import absolute_import, print_function, unicode_literals -# salt testing libs -from tests.support.unit import TestCase - # salt libs from salt.utils.rsax931 import RSAX931Signer, RSAX931Verifier +# salt testing libs +from tests.support.unit import TestCase + class RSAX931Test(TestCase): privkey_data = ( - '-----BEGIN RSA PRIVATE KEY-----\n' - 'MIIEpAIBAAKCAQEA75GR6ZTv5JOv90Vq8tKhKC7YQnhDIo2hM0HVziTEk5R4UQBW\n' - 'a0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD4ZMsYqLzqjWMekLC8bjhxc+EuPo9\n' - 'Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R4hOcMMZNZdi0xLtFoTfwU61UPfFX\n' - '14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttLP3sMXJvc3EvM0JiDVj4l1TWFUHHz\n' - 'eFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k6ai4tVzwkTmV5PsriP1ju88Lo3MB\n' - '4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWHAQIDAQABAoIBAGOzBzBYZUWRGOgl\n' - 'IY8QjTT12dY/ymC05GM6gMobjxuD7FZ5d32HDLu/QrknfS3kKlFPUQGDAbQhbbb0\n' - 'zw6VL5NO9mfOPO2W/3FaG1sRgBQcerWonoSSSn8OJwVBHMFLG3a+U1Zh1UvPoiPK\n' - 'S734swIM+zFpNYivGPvOm/muF/waFf8tF/47t1cwt/JGXYQnkG/P7z0vp47Irpsb\n' - 'Yjw7vPe4BnbY6SppSxscW3KoV7GtJLFKIxAXbxsuJMF/rYe3O3w2VKJ1Sug1VDJl\n' - '/GytwAkSUer84WwP2b07Wn4c5pCnmLslMgXCLkENgi1NnJMhYVOnckxGDZk54hqP\n' - '9RbLnkkCgYEA/yKuWEvgdzYRYkqpzB0l9ka7Y00CV4Dha9Of6GjQi9i4VCJ/UFVr\n' - 'UlhTo5y0ZzpcDAPcoZf5CFZsD90a/BpQ3YTtdln2MMCL/Kr3QFmetkmDrt+3wYnX\n' - 'sKESfsa2nZdOATRpl1antpwyD4RzsAeOPwBiACj4fkq5iZJBSI0bxrMCgYEA8GFi\n' - 'qAjgKh81/Uai6KWTOW2kX02LEMVRrnZLQ9VPPLGid4KZDDk1/dEfxjjkcyOxX1Ux\n' - 'Klu4W8ZEdZyzPcJrfk7PdopfGOfrhWzkREK9C40H7ou/1jUecq/STPfSOmxh3Y+D\n' - 'ifMNO6z4sQAHx8VaHaxVsJ7SGR/spr0pkZL+NXsCgYEA84rIgBKWB1W+TGRXJzdf\n' - 'yHIGaCjXpm2pQMN3LmP3RrcuZWm0vBt94dHcrR5l+u/zc6iwEDTAjJvqdU4rdyEr\n' - 'tfkwr7v6TNlQB3WvpWanIPyVzfVSNFX/ZWSsAgZvxYjr9ixw6vzWBXOeOb/Gqu7b\n' - 'cvpLkjmJ0wxDhbXtyXKhZA8CgYBZyvcQb+hUs732M4mtQBSD0kohc5TsGdlOQ1AQ\n' - 'McFcmbpnzDghkclyW8jzwdLMk9uxEeDAwuxWE/UEvhlSi6qdzxC+Zifp5NBc0fVe\n' - '7lMx2mfJGxj5CnSqQLVdHQHB4zSXkAGB6XHbBd0MOUeuvzDPfs2voVQ4IG3FR0oc\n' - '3/znuwKBgQChZGH3McQcxmLA28aUwOVbWssfXKdDCsiJO+PEXXlL0maO3SbnFn+Q\n' - 'Tyf8oHI5cdP7AbwDSx9bUfRPjg9dKKmATBFr2bn216pjGxK0OjYOCntFTVr0psRB\n' - 'CrKg52Qrq71/2l4V2NLQZU40Dr1bN9V+Ftd9L0pvpCAEAWpIbLXGDw==\n' - '-----END RSA PRIVATE KEY-----') + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpAIBAAKCAQEA75GR6ZTv5JOv90Vq8tKhKC7YQnhDIo2hM0HVziTEk5R4UQBW\n" + "a0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD4ZMsYqLzqjWMekLC8bjhxc+EuPo9\n" + "Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R4hOcMMZNZdi0xLtFoTfwU61UPfFX\n" + "14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttLP3sMXJvc3EvM0JiDVj4l1TWFUHHz\n" + "eFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k6ai4tVzwkTmV5PsriP1ju88Lo3MB\n" + "4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWHAQIDAQABAoIBAGOzBzBYZUWRGOgl\n" + "IY8QjTT12dY/ymC05GM6gMobjxuD7FZ5d32HDLu/QrknfS3kKlFPUQGDAbQhbbb0\n" + "zw6VL5NO9mfOPO2W/3FaG1sRgBQcerWonoSSSn8OJwVBHMFLG3a+U1Zh1UvPoiPK\n" + "S734swIM+zFpNYivGPvOm/muF/waFf8tF/47t1cwt/JGXYQnkG/P7z0vp47Irpsb\n" + "Yjw7vPe4BnbY6SppSxscW3KoV7GtJLFKIxAXbxsuJMF/rYe3O3w2VKJ1Sug1VDJl\n" + "/GytwAkSUer84WwP2b07Wn4c5pCnmLslMgXCLkENgi1NnJMhYVOnckxGDZk54hqP\n" + "9RbLnkkCgYEA/yKuWEvgdzYRYkqpzB0l9ka7Y00CV4Dha9Of6GjQi9i4VCJ/UFVr\n" + "UlhTo5y0ZzpcDAPcoZf5CFZsD90a/BpQ3YTtdln2MMCL/Kr3QFmetkmDrt+3wYnX\n" + "sKESfsa2nZdOATRpl1antpwyD4RzsAeOPwBiACj4fkq5iZJBSI0bxrMCgYEA8GFi\n" + "qAjgKh81/Uai6KWTOW2kX02LEMVRrnZLQ9VPPLGid4KZDDk1/dEfxjjkcyOxX1Ux\n" + "Klu4W8ZEdZyzPcJrfk7PdopfGOfrhWzkREK9C40H7ou/1jUecq/STPfSOmxh3Y+D\n" + "ifMNO6z4sQAHx8VaHaxVsJ7SGR/spr0pkZL+NXsCgYEA84rIgBKWB1W+TGRXJzdf\n" + "yHIGaCjXpm2pQMN3LmP3RrcuZWm0vBt94dHcrR5l+u/zc6iwEDTAjJvqdU4rdyEr\n" + "tfkwr7v6TNlQB3WvpWanIPyVzfVSNFX/ZWSsAgZvxYjr9ixw6vzWBXOeOb/Gqu7b\n" + "cvpLkjmJ0wxDhbXtyXKhZA8CgYBZyvcQb+hUs732M4mtQBSD0kohc5TsGdlOQ1AQ\n" + "McFcmbpnzDghkclyW8jzwdLMk9uxEeDAwuxWE/UEvhlSi6qdzxC+Zifp5NBc0fVe\n" + "7lMx2mfJGxj5CnSqQLVdHQHB4zSXkAGB6XHbBd0MOUeuvzDPfs2voVQ4IG3FR0oc\n" + "3/znuwKBgQChZGH3McQcxmLA28aUwOVbWssfXKdDCsiJO+PEXXlL0maO3SbnFn+Q\n" + "Tyf8oHI5cdP7AbwDSx9bUfRPjg9dKKmATBFr2bn216pjGxK0OjYOCntFTVr0psRB\n" + "CrKg52Qrq71/2l4V2NLQZU40Dr1bN9V+Ftd9L0pvpCAEAWpIbLXGDw==\n" + "-----END RSA PRIVATE KEY-----" + ) pubkey_data = ( - '-----BEGIN PUBLIC KEY-----\n' - 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA75GR6ZTv5JOv90Vq8tKh\n' - 'KC7YQnhDIo2hM0HVziTEk5R4UQBWa0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD\n' - '4ZMsYqLzqjWMekLC8bjhxc+EuPo9Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R\n' - '4hOcMMZNZdi0xLtFoTfwU61UPfFX14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttL\n' - 'P3sMXJvc3EvM0JiDVj4l1TWFUHHzeFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k\n' - '6ai4tVzwkTmV5PsriP1ju88Lo3MB4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWH\n' - 'AQIDAQAB\n' - '-----END PUBLIC KEY-----') + "-----BEGIN PUBLIC KEY-----\n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA75GR6ZTv5JOv90Vq8tKh\n" + "KC7YQnhDIo2hM0HVziTEk5R4UQBWa0CKytFMbTONY2msEDwX9iA0x7F5Lgj0X8eD\n" + "4ZMsYqLzqjWMekLC8bjhxc+EuPo9Dygu3mJ2VgRC7XhlFpmdo5NN8J2E7B/CNB3R\n" + "4hOcMMZNZdi0xLtFoTfwU61UPfFX14mV2laqLbvDEfQLJhUTDeFFV8EN5Z4H1ttL\n" + "P3sMXJvc3EvM0JiDVj4l1TWFUHHzeFgCA1Im0lv8i7PFrgW7nyMfK9uDSsUmIp7k\n" + "6ai4tVzwkTmV5PsriP1ju88Lo3MB4/sUmDv/JmlZ9YyzTO3Po8Uz3Aeq9HJWyBWH\n" + "AQIDAQAB\n" + "-----END PUBLIC KEY-----" + ) - hello_world = b'hello, world' + hello_world = b"hello, world" hello_world_sig = ( - b'\x63\xa0\x70\xd2\xe4\xd4\x6b\x8a\xa2\x59\x27\x5f\x00\x69' - b'\x1e\x3c\x50\xed\x50\x13\x09\x80\xe3\x47\x4e\x14\xb5\x7c' - b'\x07\x26\x4e\x20\x74\xea\x0e\xf8\xda\xff\x1e\x57\x8c\x67' - b'\x76\x73\xaa\xea\x0f\x0a\xe7\xa2\xe3\x88\xfc\x09\x87\x36' - b'\x01\x3a\xb7\x4c\x40\xe0\xf4\x54\xc5\xf1\xaa\xb2\x1d\x7f' - b'\xb6\xd3\xa8\xdd\x28\x69\x8b\x88\xe4\x42\x1e\x48\x3e\x1f' - b'\xe2\x2b\x3c\x7c\x85\x11\xe9\x59\xd7\xf3\xc2\x21\xd3\x55' - b'\xcb\x9c\x3c\x93\xcc\x20\xdf\x64\x81\xd0\x0d\xbf\x8e\x8d' - b'\x47\xec\x1d\x9e\x27\xec\x12\xed\x8b\x5f\xd6\x1d\xec\x8d' - b'\x77\x5a\x58\x8a\x24\xb6\x0f\x12\xb7\x51\xef\x7d\x85\x0f' - b'\x49\x39\x02\x81\x15\x08\x70\xd6\xe0\x0b\x31\xff\x5f\xf9' - b'\xd1\x92\x38\x59\x8c\x22\x9c\xbb\xbf\xcf\x85\x34\xe2\x47' - b'\xf5\xe2\xaa\xb4\x62\x33\x3c\x13\x78\x33\x87\x08\x9e\xb5' - b'\xbc\x5d\xc1\xbf\x79\x7c\xfa\x5f\x06\x6a\x3b\x17\x40\x09' - b'\xb9\x09\xbf\x32\xc3\x00\xe2\xbc\x91\x77\x14\xa5\x23\xf5' - b'\xf5\xf1\x09\x12\x38\xda\x3b\x6a\x82\x81\x7b\x5e\x1c\xcb' - b'\xaa\x36\x9b\x08\x36\x03\x14\x96\xa3\x31\x39\x59\x16\x75' - b'\xc9\xb6\x66\x94\x1b\x97\xff\xc8\xa1\xe3\x21\x35\x23\x06' - b'\x4c\x9b\xf4\xee') + b"\x63\xa0\x70\xd2\xe4\xd4\x6b\x8a\xa2\x59\x27\x5f\x00\x69" + b"\x1e\x3c\x50\xed\x50\x13\x09\x80\xe3\x47\x4e\x14\xb5\x7c" + b"\x07\x26\x4e\x20\x74\xea\x0e\xf8\xda\xff\x1e\x57\x8c\x67" + b"\x76\x73\xaa\xea\x0f\x0a\xe7\xa2\xe3\x88\xfc\x09\x87\x36" + b"\x01\x3a\xb7\x4c\x40\xe0\xf4\x54\xc5\xf1\xaa\xb2\x1d\x7f" + b"\xb6\xd3\xa8\xdd\x28\x69\x8b\x88\xe4\x42\x1e\x48\x3e\x1f" + b"\xe2\x2b\x3c\x7c\x85\x11\xe9\x59\xd7\xf3\xc2\x21\xd3\x55" + b"\xcb\x9c\x3c\x93\xcc\x20\xdf\x64\x81\xd0\x0d\xbf\x8e\x8d" + b"\x47\xec\x1d\x9e\x27\xec\x12\xed\x8b\x5f\xd6\x1d\xec\x8d" + b"\x77\x5a\x58\x8a\x24\xb6\x0f\x12\xb7\x51\xef\x7d\x85\x0f" + b"\x49\x39\x02\x81\x15\x08\x70\xd6\xe0\x0b\x31\xff\x5f\xf9" + b"\xd1\x92\x38\x59\x8c\x22\x9c\xbb\xbf\xcf\x85\x34\xe2\x47" + b"\xf5\xe2\xaa\xb4\x62\x33\x3c\x13\x78\x33\x87\x08\x9e\xb5" + b"\xbc\x5d\xc1\xbf\x79\x7c\xfa\x5f\x06\x6a\x3b\x17\x40\x09" + b"\xb9\x09\xbf\x32\xc3\x00\xe2\xbc\x91\x77\x14\xa5\x23\xf5" + b"\xf5\xf1\x09\x12\x38\xda\x3b\x6a\x82\x81\x7b\x5e\x1c\xcb" + b"\xaa\x36\x9b\x08\x36\x03\x14\x96\xa3\x31\x39\x59\x16\x75" + b"\xc9\xb6\x66\x94\x1b\x97\xff\xc8\xa1\xe3\x21\x35\x23\x06" + b"\x4c\x9b\xf4\xee" + ) def test_signer(self): with self.assertRaises(ValueError): - signer = RSAX931Signer('bogus key data') + signer = RSAX931Signer("bogus key data") with self.assertRaises(ValueError): signer = RSAX931Signer(RSAX931Test.pubkey_data) signer = RSAX931Signer(RSAX931Test.privkey_data) with self.assertRaises(ValueError): - signer.sign('x'*255) # message too long + signer.sign("x" * 255) # message too long sig = signer.sign(RSAX931Test.hello_world) self.assertEqual(RSAX931Test.hello_world_sig, sig) def test_verifier(self): with self.assertRaises(ValueError): - verifier = RSAX931Verifier('bogus key data') + verifier = RSAX931Verifier("bogus key data") with self.assertRaises(ValueError): verifier = RSAX931Verifier(RSAX931Test.privkey_data) verifier = RSAX931Verifier(RSAX931Test.pubkey_data) with self.assertRaises(ValueError): - verifier.verify('') + verifier.verify("") with self.assertRaises(ValueError): - verifier.verify(RSAX931Test.hello_world_sig + b'junk') + verifier.verify(RSAX931Test.hello_world_sig + b"junk") msg = verifier.verify(RSAX931Test.hello_world_sig) self.assertEqual(RSAX931Test.hello_world, msg) diff --git a/tests/unit/utils/test_sanitizers.py b/tests/unit/utils/test_sanitizers.py index 90f4b1de95f..008119aedae 100644 --- a/tests/unit/utils/test_sanitizers.py +++ b/tests/unit/utils/test_sanitizers.py @@ -2,6 +2,7 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + from salt.ext.six import text_type as text # Import Salt Libs @@ -12,52 +13,53 @@ from tests.support.unit import TestCase class SanitizersTestCase(TestCase): - ''' + """ TestCase for sanitizers - ''' + """ + def test_sanitized_trim(self): - ''' + """ Test sanitized input for trimming - ''' - value = ' sample ' + """ + value = " sample " response = clean.trim(value) - assert response == 'sample' + assert response == "sample" assert type(response) == text def test_sanitized_filename(self): - ''' + """ Test sanitized input for filename - ''' - value = '/absolute/path/to/the/file.txt' + """ + value = "/absolute/path/to/the/file.txt" response = clean.filename(value) - assert response == 'file.txt' + assert response == "file.txt" - value = '../relative/path/to/the/file.txt' + value = "../relative/path/to/the/file.txt" response = clean.filename(value) - assert response == 'file.txt' + assert response == "file.txt" def test_sanitized_hostname(self): - ''' + """ Test sanitized input for hostname (id) - ''' - value = ' ../ ../some/dubious/hostname ' + """ + value = " ../ ../some/dubious/hostname " response = clean.hostname(value) - assert response == 'somedubioushostname' + assert response == "somedubioushostname" test_sanitized_id = test_sanitized_hostname def test_value_masked(self): - ''' + """ Test if the values are masked. :return: - ''' - out = mask_args_value('quantum: fluctuations', 'quant*') - assert out == 'quantum: ** hidden **' + """ + out = mask_args_value("quantum: fluctuations", "quant*") + assert out == "quantum: ** hidden **" def test_value_not_masked(self): - ''' + """ Test if the values are not masked. :return: - ''' - out = mask_args_value('quantum fluctuations', 'quant*') - assert out == 'quantum fluctuations' + """ + out = mask_args_value("quantum fluctuations", "quant*") + assert out == "quantum fluctuations" diff --git a/tests/unit/utils/test_schedule.py b/tests/unit/utils/test_schedule.py index cb380a763c3..13b614aad5e 100644 --- a/tests/unit/utils/test_schedule.py +++ b/tests/unit/utils/test_schedule.py @@ -1,27 +1,29 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Nicole Thomas <nicole@saltstack.com> -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import datetime import logging import os -# Import Salt Testing Libs -from tests.support.unit import skipIf, TestCase -from tests.support.mock import MagicMock, patch -from tests.support.runtests import RUNTIME_VARS - # Import Salt Libs import salt.config from salt.utils.schedule import Schedule +from tests.support.mock import MagicMock, patch +from tests.support.runtests import RUNTIME_VARS + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf # pylint: disable=import-error,unused-import try: import croniter + _CRON_SUPPORTED = True except ImportError: _CRON_SUPPORTED = False @@ -32,178 +34,207 @@ log = logging.getLogger(__name__) # pylint: disable=too-many-public-methods,invalid-name class ScheduleTestCase(TestCase): - ''' + """ Unit tests for salt.utils.schedule module - ''' + """ @classmethod def setUpClass(cls): - root_dir = os.path.join(RUNTIME_VARS.TMP, 'schedule-unit-tests') + root_dir = os.path.join(RUNTIME_VARS.TMP, "schedule-unit-tests") default_config = salt.config.minion_config(None) - default_config['conf_dir'] = default_config['root_dir'] = root_dir - default_config['sock_dir'] = os.path.join(root_dir, 'test-socks') - default_config['pki_dir'] = os.path.join(root_dir, 'pki') - default_config['cachedir'] = os.path.join(root_dir, 'cache') + default_config["conf_dir"] = default_config["root_dir"] = root_dir + default_config["sock_dir"] = os.path.join(root_dir, "test-socks") + default_config["pki_dir"] = os.path.join(root_dir, "pki") + default_config["cachedir"] = os.path.join(root_dir, "cache") cls.default_config = default_config @classmethod def tearDownClass(cls): - delattr(cls, 'default_config') + delattr(cls, "default_config") def setUp(self): - with patch('salt.utils.schedule.clean_proc_dir', MagicMock(return_value=None)): - self.schedule = Schedule(copy.deepcopy(self.default_config), {}, returners={}) - self.addCleanup(delattr, self, 'schedule') + with patch("salt.utils.schedule.clean_proc_dir", MagicMock(return_value=None)): + self.schedule = Schedule( + copy.deepcopy(self.default_config), {}, returners={} + ) + self.addCleanup(delattr, self, "schedule") # delete_job tests def test_delete_job_exists(self): - ''' + """ Tests ensuring the job exists and deleting it - ''' - self.schedule.opts.update({'schedule': {'foo': 'bar'}, 'pillar': {}}) - self.assertIn('foo', self.schedule.opts['schedule']) - self.schedule.delete_job('foo') - self.assertNotIn('foo', self.schedule.opts['schedule']) + """ + self.schedule.opts.update({"schedule": {"foo": "bar"}, "pillar": {}}) + self.assertIn("foo", self.schedule.opts["schedule"]) + self.schedule.delete_job("foo") + self.assertNotIn("foo", self.schedule.opts["schedule"]) def test_delete_job_in_pillar(self): - ''' + """ Tests ignoring deletion job from pillar - ''' - self.schedule.opts.update({'pillar': {'schedule': {'foo': 'bar'}}, 'schedule': {}}) - self.assertIn('foo', self.schedule.opts['pillar']['schedule']) - self.schedule.delete_job('foo') - self.assertIn('foo', self.schedule.opts['pillar']['schedule']) + """ + self.schedule.opts.update( + {"pillar": {"schedule": {"foo": "bar"}}, "schedule": {}} + ) + self.assertIn("foo", self.schedule.opts["pillar"]["schedule"]) + self.schedule.delete_job("foo") + self.assertIn("foo", self.schedule.opts["pillar"]["schedule"]) def test_delete_job_intervals(self): - ''' + """ Tests removing job from intervals - ''' - self.schedule.opts.update({'pillar': {}, 'schedule': {}}) - self.schedule.intervals = {'foo': 'bar'} - self.schedule.delete_job('foo') - self.assertNotIn('foo', self.schedule.intervals) + """ + self.schedule.opts.update({"pillar": {}, "schedule": {}}) + self.schedule.intervals = {"foo": "bar"} + self.schedule.delete_job("foo") + self.assertNotIn("foo", self.schedule.intervals) def test_delete_job_prefix(self): - ''' + """ Tests ensuring jobs exists and deleting them by prefix - ''' - self.schedule.opts.update({'schedule': {'foobar': 'bar', 'foobaz': 'baz', 'fooboo': 'boo'}, - 'pillar': {}}) + """ + self.schedule.opts.update( + { + "schedule": {"foobar": "bar", "foobaz": "baz", "fooboo": "boo"}, + "pillar": {}, + } + ) ret = copy.deepcopy(self.schedule.opts) - del ret['schedule']['foobar'] - del ret['schedule']['foobaz'] - self.schedule.delete_job_prefix('fooba') + del ret["schedule"]["foobar"] + del ret["schedule"]["foobaz"] + self.schedule.delete_job_prefix("fooba") self.assertEqual(self.schedule.opts, ret) def test_delete_job_prefix_in_pillar(self): - ''' + """ Tests ignoring deletion jobs by prefix from pillar - ''' - self.schedule.opts.update({'pillar': {'schedule': {'foobar': 'bar', 'foobaz': 'baz', 'fooboo': 'boo'}}, - 'schedule': {}}) + """ + self.schedule.opts.update( + { + "pillar": { + "schedule": {"foobar": "bar", "foobaz": "baz", "fooboo": "boo"} + }, + "schedule": {}, + } + ) ret = copy.deepcopy(self.schedule.opts) - self.schedule.delete_job_prefix('fooba') + self.schedule.delete_job_prefix("fooba") self.assertEqual(self.schedule.opts, ret) # add_job tests def test_add_job_data_not_dict(self): - ''' + """ Tests if data is a dictionary - ''' - data = 'foo' + """ + data = "foo" self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) def test_add_job_multiple_jobs(self): - ''' + """ Tests if more than one job is scheduled at a time - ''' - data = {'key1': 'value1', 'key2': 'value2'} + """ + data = {"key1": "value1", "key2": "value2"} self.assertRaises(ValueError, Schedule.add_job, self.schedule, data) def test_add_job(self): - ''' + """ Tests adding a job to the schedule - ''' - data = {'foo': {'bar': 'baz'}} + """ + data = {"foo": {"bar": "baz"}} ret = copy.deepcopy(self.schedule.opts) - ret.update({'schedule': {'foo': {'bar': 'baz', 'enabled': True}, - 'hello': {'world': 'peace', 'enabled': True}}, - 'pillar': {}}) - self.schedule.opts.update({'schedule': {'hello': {'world': 'peace', 'enabled': True}}, - 'pillar': {}}) + ret.update( + { + "schedule": { + "foo": {"bar": "baz", "enabled": True}, + "hello": {"world": "peace", "enabled": True}, + }, + "pillar": {}, + } + ) + self.schedule.opts.update( + {"schedule": {"hello": {"world": "peace", "enabled": True}}, "pillar": {}} + ) Schedule.add_job(self.schedule, data) self.assertEqual(self.schedule.opts, ret) # enable_job tests def test_enable_job(self): - ''' + """ Tests enabling a job - ''' - self.schedule.opts.update({'schedule': {'name': {'enabled': 'foo'}}}) - Schedule.enable_job(self.schedule, 'name') - self.assertTrue(self.schedule.opts['schedule']['name']['enabled']) + """ + self.schedule.opts.update({"schedule": {"name": {"enabled": "foo"}}}) + Schedule.enable_job(self.schedule, "name") + self.assertTrue(self.schedule.opts["schedule"]["name"]["enabled"]) def test_enable_job_pillar(self): - ''' + """ Tests ignoring enable a job from pillar - ''' - self.schedule.opts.update({'pillar': {'schedule': {'name': {'enabled': False}}}}) - Schedule.enable_job(self.schedule, 'name', persist=False) - self.assertFalse(self.schedule.opts['pillar']['schedule']['name']['enabled']) + """ + self.schedule.opts.update( + {"pillar": {"schedule": {"name": {"enabled": False}}}} + ) + Schedule.enable_job(self.schedule, "name", persist=False) + self.assertFalse(self.schedule.opts["pillar"]["schedule"]["name"]["enabled"]) # disable_job tests def test_disable_job(self): - ''' + """ Tests disabling a job - ''' - self.schedule.opts.update({'schedule': {'name': {'enabled': 'foo'}}, 'pillar': {}}) - Schedule.disable_job(self.schedule, 'name') - self.assertFalse(self.schedule.opts['schedule']['name']['enabled']) + """ + self.schedule.opts.update( + {"schedule": {"name": {"enabled": "foo"}}, "pillar": {}} + ) + Schedule.disable_job(self.schedule, "name") + self.assertFalse(self.schedule.opts["schedule"]["name"]["enabled"]) def test_disable_job_pillar(self): - ''' + """ Tests ignoring disable a job in pillar - ''' - self.schedule.opts.update({'pillar': {'schedule': {'name': {'enabled': True}}}, 'schedule': {}}) - Schedule.disable_job(self.schedule, 'name', persist=False) - self.assertTrue(self.schedule.opts['pillar']['schedule']['name']['enabled']) + """ + self.schedule.opts.update( + {"pillar": {"schedule": {"name": {"enabled": True}}}, "schedule": {}} + ) + Schedule.disable_job(self.schedule, "name", persist=False) + self.assertTrue(self.schedule.opts["pillar"]["schedule"]["name"]["enabled"]) # modify_job tests def test_modify_job(self): - ''' + """ Tests modifying a job in the scheduler - ''' - schedule = {'foo': 'bar'} - self.schedule.opts.update({'schedule': {'name': 'baz'}, 'pillar': {}}) + """ + schedule = {"foo": "bar"} + self.schedule.opts.update({"schedule": {"name": "baz"}, "pillar": {}}) ret = copy.deepcopy(self.schedule.opts) - ret.update({'schedule': {'name': {'foo': 'bar'}}}) - Schedule.modify_job(self.schedule, 'name', schedule) + ret.update({"schedule": {"name": {"foo": "bar"}}}) + Schedule.modify_job(self.schedule, "name", schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_not_exists(self): - ''' + """ Tests modifying a job in the scheduler if jobs not exists - ''' - schedule = {'foo': 'bar'} - self.schedule.opts.update({'schedule': {}, 'pillar': {}}) + """ + schedule = {"foo": "bar"} + self.schedule.opts.update({"schedule": {}, "pillar": {}}) ret = copy.deepcopy(self.schedule.opts) - ret.update({'schedule': {'name': {'foo': 'bar'}}}) - Schedule.modify_job(self.schedule, 'name', schedule) + ret.update({"schedule": {"name": {"foo": "bar"}}}) + Schedule.modify_job(self.schedule, "name", schedule) self.assertEqual(self.schedule.opts, ret) def test_modify_job_pillar(self): - ''' + """ Tests ignoring modification of job from pillar - ''' - schedule = {'foo': 'bar'} - self.schedule.opts.update({'schedule': {}, 'pillar': {'schedule': {'name': 'baz'}}}) + """ + schedule = {"foo": "bar"} + self.schedule.opts.update( + {"schedule": {}, "pillar": {"schedule": {"name": "baz"}}} + ) ret = copy.deepcopy(self.schedule.opts) - Schedule.modify_job(self.schedule, 'name', schedule, persist=False) + Schedule.modify_job(self.schedule, "name", schedule, persist=False) self.assertEqual(self.schedule.opts, ret) maxDiff = None @@ -211,174 +242,196 @@ class ScheduleTestCase(TestCase): # enable_schedule tests def test_enable_schedule(self): - ''' + """ Tests enabling the scheduler - ''' - with patch('salt.utils.schedule.Schedule.persist', MagicMock(return_value=None)) as persist_mock: - self.schedule.opts.update({'schedule': {'enabled': 'foo'}, 'pillar': {}}) + """ + with patch( + "salt.utils.schedule.Schedule.persist", MagicMock(return_value=None) + ) as persist_mock: + self.schedule.opts.update({"schedule": {"enabled": "foo"}, "pillar": {}}) Schedule.enable_schedule(self.schedule) - self.assertTrue(self.schedule.opts['schedule']['enabled']) + self.assertTrue(self.schedule.opts["schedule"]["enabled"]) persist_mock.assert_called() # disable_schedule tests def test_disable_schedule(self): - ''' + """ Tests disabling the scheduler - ''' - with patch('salt.utils.schedule.Schedule.persist', MagicMock(return_value=None)) as persist_mock: - self.schedule.opts.update({'schedule': {'enabled': 'foo'}, 'pillar': {}}) + """ + with patch( + "salt.utils.schedule.Schedule.persist", MagicMock(return_value=None) + ) as persist_mock: + self.schedule.opts.update({"schedule": {"enabled": "foo"}, "pillar": {}}) Schedule.disable_schedule(self.schedule) - self.assertFalse(self.schedule.opts['schedule']['enabled']) + self.assertFalse(self.schedule.opts["schedule"]["enabled"]) persist_mock.assert_called() # reload tests def test_reload_update_schedule_key(self): - ''' + """ Tests reloading the schedule from saved schedule where both the saved schedule and self.schedule.opts contain a schedule key - ''' - saved = {'schedule': {'foo': 'bar'}} + """ + saved = {"schedule": {"foo": "bar"}} ret = copy.deepcopy(self.schedule.opts) - ret.update({'schedule': {'foo': 'bar', 'hello': 'world'}}) - self.schedule.opts.update({'schedule': {'hello': 'world'}}) + ret.update({"schedule": {"foo": "bar", "hello": "world"}}) + self.schedule.opts.update({"schedule": {"hello": "world"}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_update_schedule_no_key(self): - ''' + """ Tests reloading the schedule from saved schedule that does not contain a schedule key but self.schedule.opts does - ''' - saved = {'foo': 'bar'} + """ + saved = {"foo": "bar"} ret = copy.deepcopy(self.schedule.opts) - ret.update({'schedule': {'foo': 'bar', 'hello': 'world'}}) - self.schedule.opts.update({'schedule': {'hello': 'world'}}) + ret.update({"schedule": {"foo": "bar", "hello": "world"}}) + self.schedule.opts.update({"schedule": {"hello": "world"}}) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_no_schedule_in_opts(self): - ''' + """ Tests reloading the schedule from saved schedule that does not contain a schedule key and neither does self.schedule.opts - ''' - saved = {'foo': 'bar'} + """ + saved = {"foo": "bar"} ret = copy.deepcopy(self.schedule.opts) - ret['schedule'] = {'foo': 'bar'} - self.schedule.opts.pop('schedule', None) + ret["schedule"] = {"foo": "bar"} + self.schedule.opts.pop("schedule", None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) def test_reload_schedule_in_saved_but_not_opts(self): - ''' + """ Tests reloading the schedule from saved schedule that contains a schedule key, but self.schedule.opts does not - ''' - saved = {'schedule': {'foo': 'bar'}} + """ + saved = {"schedule": {"foo": "bar"}} ret = copy.deepcopy(self.schedule.opts) - ret['schedule'] = {'foo': 'bar'} - self.schedule.opts.pop('schedule', None) + ret["schedule"] = {"foo": "bar"} + self.schedule.opts.pop("schedule", None) Schedule.reload(self.schedule, saved) self.assertEqual(self.schedule.opts, ret) # eval tests def test_eval_schedule_is_not_dict(self): - ''' + """ Tests eval if the schedule is not a dictionary - ''' - self.schedule.opts.update({'schedule': '', 'pillar': {'schedule': {}}}) + """ + self.schedule.opts.update({"schedule": "", "pillar": {"schedule": {}}}) self.assertRaises(ValueError, Schedule.eval, self.schedule) def test_eval_schedule_is_not_dict_in_pillar(self): - ''' + """ Tests eval if the schedule from pillar is not a dictionary - ''' - self.schedule.opts.update({'schedule': {}, 'pillar': {'schedule': ''}}) + """ + self.schedule.opts.update({"schedule": {}, "pillar": {"schedule": ""}}) self.assertRaises(ValueError, Schedule.eval, self.schedule) def test_eval_schedule_time(self): - ''' + """ Tests eval if the schedule setting time is in the future - ''' - self.schedule.opts.update({'pillar': {'schedule': {}}}) - self.schedule.opts.update({'schedule': {'testjob': {'function': 'test.true', 'seconds': 60}}}) + """ + self.schedule.opts.update({"pillar": {"schedule": {}}}) + self.schedule.opts.update( + {"schedule": {"testjob": {"function": "test.true", "seconds": 60}}} + ) now = datetime.datetime.now() self.schedule.eval() - self.assertTrue(self.schedule.opts['schedule']['testjob']['_next_fire_time'] > now) + self.assertTrue( + self.schedule.opts["schedule"]["testjob"]["_next_fire_time"] > now + ) def test_eval_schedule_time_eval(self): - ''' + """ Tests eval if the schedule setting time is in the future plus splay - ''' - self.schedule.opts.update({'pillar': {'schedule': {}}}) + """ + self.schedule.opts.update({"pillar": {"schedule": {}}}) self.schedule.opts.update( - {'schedule': {'testjob': {'function': 'test.true', 'seconds': 60, 'splay': 5}}}) + { + "schedule": { + "testjob": {"function": "test.true", "seconds": 60, "splay": 5} + } + } + ) now = datetime.datetime.now() self.schedule.eval() - self.assertTrue(self.schedule.opts['schedule']['testjob']['_splay'] - now > datetime.timedelta(seconds=60)) + self.assertTrue( + self.schedule.opts["schedule"]["testjob"]["_splay"] - now + > datetime.timedelta(seconds=60) + ) - @skipIf(not _CRON_SUPPORTED, 'croniter module not installed') + @skipIf(not _CRON_SUPPORTED, "croniter module not installed") def test_eval_schedule_cron(self): - ''' + """ Tests eval if the schedule is defined with cron expression - ''' - self.schedule.opts.update({'pillar': {'schedule': {}}}) - self.schedule.opts.update({'schedule': {'testjob': {'function': 'test.true', 'cron': '* * * * *'}}}) + """ + self.schedule.opts.update({"pillar": {"schedule": {}}}) + self.schedule.opts.update( + {"schedule": {"testjob": {"function": "test.true", "cron": "* * * * *"}}} + ) now = datetime.datetime.now() self.schedule.eval() - self.assertTrue(self.schedule.opts['schedule']['testjob']['_next_fire_time'] > now) + self.assertTrue( + self.schedule.opts["schedule"]["testjob"]["_next_fire_time"] > now + ) - @skipIf(not _CRON_SUPPORTED, 'croniter module not installed') + @skipIf(not _CRON_SUPPORTED, "croniter module not installed") def test_eval_schedule_cron_splay(self): - ''' + """ Tests eval if the schedule is defined with cron expression plus splay - ''' - self.schedule.opts.update({'pillar': {'schedule': {}}}) + """ + self.schedule.opts.update({"pillar": {"schedule": {}}}) self.schedule.opts.update( - {'schedule': {'testjob': {'function': 'test.true', 'cron': '* * * * *', 'splay': 5}}}) + { + "schedule": { + "testjob": { + "function": "test.true", + "cron": "* * * * *", + "splay": 5, + } + } + } + ) self.schedule.eval() - self.assertTrue(self.schedule.opts['schedule']['testjob']['_splay'] > - self.schedule.opts['schedule']['testjob']['_next_fire_time']) + self.assertTrue( + self.schedule.opts["schedule"]["testjob"]["_splay"] + > self.schedule.opts["schedule"]["testjob"]["_next_fire_time"] + ) def test_handle_func_schedule_minion_blackout(self): - ''' + """ Tests eval if the schedule from pillar is not a dictionary - ''' - self.schedule.opts.update({'pillar': {'schedule': {}}}) - self.schedule.opts.update({'grains': {'minion_blackout': True}}) + """ + self.schedule.opts.update({"pillar": {"schedule": {}}}) + self.schedule.opts.update({"grains": {"minion_blackout": True}}) self.schedule.opts.update( - {'schedule': {'testjob': {'function': 'test.true', - 'seconds': 60}}}) - data = {'function': 'test.true', - '_next_scheduled_fire_time': datetime.datetime(2018, - 11, - 21, - 14, - 9, - 53, - 903438), - 'run': True, - 'name': 'testjob', - 'seconds': 60, - '_splay': None, - '_seconds': 60, - 'jid_include': True, - 'maxrunning': 1, - '_next_fire_time': datetime.datetime(2018, - 11, - 21, - 14, - 8, - 53, - 903438)} + {"schedule": {"testjob": {"function": "test.true", "seconds": 60}}} + ) + data = { + "function": "test.true", + "_next_scheduled_fire_time": datetime.datetime( + 2018, 11, 21, 14, 9, 53, 903438 + ), + "run": True, + "name": "testjob", + "seconds": 60, + "_splay": None, + "_seconds": 60, + "jid_include": True, + "maxrunning": 1, + "_next_fire_time": datetime.datetime(2018, 11, 21, 14, 8, 53, 903438), + } - with patch.object(salt.utils.schedule, 'log') as log_mock: - with patch('salt.utils.process.daemonize'), \ - patch('sys.platform', 'linux2'): - self.schedule.handle_func(False, 'test.ping', data) + with patch.object(salt.utils.schedule, "log") as log_mock: + with patch("salt.utils.process.daemonize"), patch("sys.platform", "linux2"): + self.schedule.handle_func(False, "test.ping", data) self.assertTrue(log_mock.exception.called) diff --git a/tests/unit/utils/test_schema.py b/tests/unit/utils/test_schema.py index ed1515f9fec..06ee5460d5b 100644 --- a/tests/unit/utils/test_schema.py +++ b/tests/unit/utils/test_schema.py @@ -5,45 +5,50 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -import copy -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +import copy # Import Salt Libs import salt.utils.json +import salt.utils.schema as schema import salt.utils.stringutils import salt.utils.yaml -import salt.utils.schema as schema from salt.ext import six from salt.utils.versions import LooseVersion as _LooseVersion +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf + # Import 3rd-party libs try: import jsonschema import jsonschema.exceptions + HAS_JSONSCHEMA = True JSONSCHEMA_VERSION = _LooseVersion(jsonschema.__version__) except ImportError: HAS_JSONSCHEMA = False - JSONSCHEMA_VERSION = _LooseVersion('0') + JSONSCHEMA_VERSION = _LooseVersion("0") # pylint: disable=unused-import try: import rfc3987 + HAS_RFC3987 = True except ImportError: HAS_RFC3987 = False try: import strict_rfc3339 + HAS_STRICT_RFC3339 = True except ImportError: HAS_STRICT_RFC3339 = False try: import isodate + HAS_ISODATE = True except ImportError: HAS_ISODATE = False @@ -51,79 +56,70 @@ except ImportError: class ConfigTestCase(TestCase): - ''' + """ TestCase for salt.utils.config module - ''' + """ def test_configuration_subclass_inherits_items(self): class BaseConfig(schema.Schema): base = schema.BooleanItem(default=True, required=True) class SubClassedConfig(BaseConfig): - hungry = schema.BooleanItem(title='Hungry', description='Are you hungry?', required=True) + hungry = schema.BooleanItem( + title="Hungry", description="Are you hungry?", required=True + ) self.assertDictEqual( SubClassedConfig.serialize(), { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'type': 'object', - 'properties': { - 'base': { - 'default': True, - 'type': 'boolean', - 'title': 'base' + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "base": {"default": True, "type": "boolean", "title": "base"}, + "hungry": { + "type": "boolean", + "description": "Are you hungry?", + "title": "Hungry", }, - 'hungry': { - 'type': 'boolean', - 'description': 'Are you hungry?', - 'title': 'Hungry' - } }, - 'required': ['base', 'hungry'], - 'x-ordering': ['base', 'hungry'], - 'additionalProperties': False, - } + "required": ["base", "hungry"], + "x-ordering": ["base", "hungry"], + "additionalProperties": False, + }, ) class MergedConfigClass(schema.Schema): - thirsty = schema.BooleanItem(title='Thirsty', description='Are you thirsty?', required=True) + thirsty = schema.BooleanItem( + title="Thirsty", description="Are you thirsty?", required=True + ) merge_subclassed = SubClassedConfig(flatten=True) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'type': 'object', - 'properties': { - 'thirsty': { - 'type': 'boolean', - 'description': 'Are you thirsty?', - 'title': 'Thirsty' + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "thirsty": { + "type": "boolean", + "description": "Are you thirsty?", + "title": "Thirsty", }, - 'base': { - 'default': True, - 'type': 'boolean', - 'title': 'base' + "base": {"default": True, "type": "boolean", "title": "base"}, + "hungry": { + "type": "boolean", + "description": "Are you hungry?", + "title": "Hungry", }, - 'hungry': { - 'type': 'boolean', - 'description': 'Are you hungry?', - 'title': 'Hungry' - } }, - 'required': ['thirsty', 'base', 'hungry'], - 'x-ordering': ['thirsty', 'base', 'hungry'], - 'additionalProperties': False, + "required": ["thirsty", "base", "hungry"], + "x-ordering": ["thirsty", "base", "hungry"], + "additionalProperties": False, } self.assertDictContainsSubset( - MergedConfigClass.serialize()['properties'], - expected['properties'] - ) - self.assertDictContainsSubset( - expected, - MergedConfigClass.serialize() + MergedConfigClass.serialize()["properties"], expected["properties"] ) + self.assertDictContainsSubset(expected, MergedConfigClass.serialize()) def test_configuration_items_order(self): - class One(schema.Schema): one = schema.BooleanItem() @@ -134,344 +130,336 @@ class ConfigTestCase(TestCase): two = schema.BooleanItem() three = Three(flatten=True) - self.assertEqual(Final.serialize()['x-ordering'], ['one', 'two', 'three']) + self.assertEqual(Final.serialize()["x-ordering"], ["one", "two", "three"]) def test_optional_requirements_config(self): class BaseRequirements(schema.Schema): - driver = schema.StringItem(default='digitalocean', format='hidden') + driver = schema.StringItem(default="digitalocean", format="hidden") class SSHKeyFileSchema(schema.Schema): ssh_key_file = schema.StringItem( - title='SSH Private Key', - description='The path to an SSH private key which will be used ' - 'to authenticate on the deployed VMs', - ) + title="SSH Private Key", + description="The path to an SSH private key which will be used " + "to authenticate on the deployed VMs", + ) class SSHKeyNamesSchema(schema.Schema): ssh_key_names = schema.StringItem( - title='SSH Key Names', - description='The names of an SSH key being managed on ' - 'DigitalOcean account which will be used to ' - 'authenticate on the deployed VMs', - ) + title="SSH Key Names", + description="The names of an SSH key being managed on " + "DigitalOcean account which will be used to " + "authenticate on the deployed VMs", + ) class Requirements(BaseRequirements): - title = 'DigitalOcean' - description = 'DigitalOcean Cloud VM configuration requirements.' + title = "DigitalOcean" + description = "DigitalOcean Cloud VM configuration requirements." personal_access_token = schema.StringItem( - title='Personal Access Token', - description='This is the API access token which can be generated ' - 'under the API/Application on your account', - required=True) + title="Personal Access Token", + description="This is the API access token which can be generated " + "under the API/Application on your account", + required=True, + ) requirements_definition = schema.AnyOfItem( items=( SSHKeyFileSchema.as_requirements_item(), - SSHKeyNamesSchema.as_requirements_item() + SSHKeyNamesSchema.as_requirements_item(), ), )(flatten=True) ssh_key_file = SSHKeyFileSchema(flatten=True) ssh_key_names = SSHKeyNamesSchema(flatten=True) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'DigitalOcean', - 'description': 'DigitalOcean Cloud VM configuration requirements.', - 'type': 'object', - 'properties': { - 'driver': { - 'default': 'digitalocean', - 'format': 'hidden', - 'type': 'string', - 'title': 'driver' + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "DigitalOcean", + "description": "DigitalOcean Cloud VM configuration requirements.", + "type": "object", + "properties": { + "driver": { + "default": "digitalocean", + "format": "hidden", + "type": "string", + "title": "driver", }, - 'personal_access_token': { - 'type': 'string', - 'description': 'This is the API access token which can be ' - 'generated under the API/Application on your account', - 'title': 'Personal Access Token' + "personal_access_token": { + "type": "string", + "description": "This is the API access token which can be " + "generated under the API/Application on your account", + "title": "Personal Access Token", }, - 'ssh_key_file': { - 'type': 'string', - 'description': 'The path to an SSH private key which will ' - 'be used to authenticate on the deployed VMs', - 'title': 'SSH Private Key' + "ssh_key_file": { + "type": "string", + "description": "The path to an SSH private key which will " + "be used to authenticate on the deployed VMs", + "title": "SSH Private Key", + }, + "ssh_key_names": { + "type": "string", + "description": "The names of an SSH key being managed on DigitalOcean " + "account which will be used to authenticate on the deployed VMs", + "title": "SSH Key Names", }, - 'ssh_key_names': { - 'type': 'string', - 'description': 'The names of an SSH key being managed on DigitalOcean ' - 'account which will be used to authenticate on the deployed VMs', - 'title': 'SSH Key Names' - } }, - 'anyOf': [ - {'required': ['ssh_key_file']}, - {'required': ['ssh_key_names']} + "anyOf": [{"required": ["ssh_key_file"]}, {"required": ["ssh_key_names"]}], + "required": ["personal_access_token"], + "x-ordering": [ + "driver", + "personal_access_token", + "ssh_key_file", + "ssh_key_names", ], - 'required': [ - 'personal_access_token' - ], - 'x-ordering': [ - 'driver', - 'personal_access_token', - 'ssh_key_file', - 'ssh_key_names', - ], - 'additionalProperties': False + "additionalProperties": False, } self.assertDictEqual(expected, Requirements.serialize()) class Requirements2(BaseRequirements): - title = 'DigitalOcean' - description = 'DigitalOcean Cloud VM configuration requirements.' + title = "DigitalOcean" + description = "DigitalOcean Cloud VM configuration requirements." personal_access_token = schema.StringItem( - title='Personal Access Token', - description='This is the API access token which can be generated ' - 'under the API/Application on your account', - required=True) + title="Personal Access Token", + description="This is the API access token which can be generated " + "under the API/Application on your account", + required=True, + ) ssh_key_file = schema.StringItem( - title='SSH Private Key', - description='The path to an SSH private key which will be used ' - 'to authenticate on the deployed VMs') + title="SSH Private Key", + description="The path to an SSH private key which will be used " + "to authenticate on the deployed VMs", + ) ssh_key_names = schema.StringItem( - title='SSH Key Names', - description='The names of an SSH key being managed on ' - 'DigitalOcean account which will be used to ' - 'authenticate on the deployed VMs') + title="SSH Key Names", + description="The names of an SSH key being managed on " + "DigitalOcean account which will be used to " + "authenticate on the deployed VMs", + ) requirements_definition = schema.AnyOfItem( items=( - schema.RequirementsItem(requirements=['ssh_key_file']), - schema.RequirementsItem(requirements=['ssh_key_names']) + schema.RequirementsItem(requirements=["ssh_key_file"]), + schema.RequirementsItem(requirements=["ssh_key_names"]), ), )(flatten=True) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'DigitalOcean', - 'description': 'DigitalOcean Cloud VM configuration requirements.', - 'type': 'object', - 'properties': { - 'driver': { - 'default': 'digitalocean', - 'format': 'hidden', - 'type': 'string', - 'title': 'driver' + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "DigitalOcean", + "description": "DigitalOcean Cloud VM configuration requirements.", + "type": "object", + "properties": { + "driver": { + "default": "digitalocean", + "format": "hidden", + "type": "string", + "title": "driver", }, - 'personal_access_token': { - 'type': 'string', - 'description': 'This is the API access token which can be ' - 'generated under the API/Application on your account', - 'title': 'Personal Access Token' + "personal_access_token": { + "type": "string", + "description": "This is the API access token which can be " + "generated under the API/Application on your account", + "title": "Personal Access Token", }, - 'ssh_key_file': { - 'type': 'string', - 'description': 'The path to an SSH private key which will ' - 'be used to authenticate on the deployed VMs', - 'title': 'SSH Private Key' + "ssh_key_file": { + "type": "string", + "description": "The path to an SSH private key which will " + "be used to authenticate on the deployed VMs", + "title": "SSH Private Key", + }, + "ssh_key_names": { + "type": "string", + "description": "The names of an SSH key being managed on DigitalOcean " + "account which will be used to authenticate on the deployed VMs", + "title": "SSH Key Names", }, - 'ssh_key_names': { - 'type': 'string', - 'description': 'The names of an SSH key being managed on DigitalOcean ' - 'account which will be used to authenticate on the deployed VMs', - 'title': 'SSH Key Names' - } }, - 'anyOf': [ - {'required': ['ssh_key_file']}, - {'required': ['ssh_key_names']} + "anyOf": [{"required": ["ssh_key_file"]}, {"required": ["ssh_key_names"]}], + "required": ["personal_access_token"], + "x-ordering": [ + "driver", + "personal_access_token", + "ssh_key_file", + "ssh_key_names", ], - 'required': [ - 'personal_access_token' - ], - 'x-ordering': [ - 'driver', - 'personal_access_token', - 'ssh_key_file', - 'ssh_key_names', - ], - 'additionalProperties': False + "additionalProperties": False, } self.assertDictContainsSubset(expected, Requirements2.serialize()) class Requirements3(schema.Schema): - title = 'DigitalOcean' - description = 'DigitalOcean Cloud VM configuration requirements.' + title = "DigitalOcean" + description = "DigitalOcean Cloud VM configuration requirements." merge_reqs = Requirements(flatten=True) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'DigitalOcean', - 'description': 'DigitalOcean Cloud VM configuration requirements.', - 'type': 'object', - 'properties': { - 'driver': { - 'default': 'digitalocean', - 'format': 'hidden', - 'type': 'string', - 'title': 'driver' + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "DigitalOcean", + "description": "DigitalOcean Cloud VM configuration requirements.", + "type": "object", + "properties": { + "driver": { + "default": "digitalocean", + "format": "hidden", + "type": "string", + "title": "driver", }, - 'personal_access_token': { - 'type': 'string', - 'description': 'This is the API access token which can be ' - 'generated under the API/Application on your account', - 'title': 'Personal Access Token' + "personal_access_token": { + "type": "string", + "description": "This is the API access token which can be " + "generated under the API/Application on your account", + "title": "Personal Access Token", }, - 'ssh_key_file': { - 'type': 'string', - 'description': 'The path to an SSH private key which will ' - 'be used to authenticate on the deployed VMs', - 'title': 'SSH Private Key' + "ssh_key_file": { + "type": "string", + "description": "The path to an SSH private key which will " + "be used to authenticate on the deployed VMs", + "title": "SSH Private Key", + }, + "ssh_key_names": { + "type": "string", + "description": "The names of an SSH key being managed on DigitalOcean " + "account which will be used to authenticate on the deployed VMs", + "title": "SSH Key Names", }, - 'ssh_key_names': { - 'type': 'string', - 'description': 'The names of an SSH key being managed on DigitalOcean ' - 'account which will be used to authenticate on the deployed VMs', - 'title': 'SSH Key Names' - } }, - 'anyOf': [ - {'required': ['ssh_key_file']}, - {'required': ['ssh_key_names']} + "anyOf": [{"required": ["ssh_key_file"]}, {"required": ["ssh_key_names"]}], + "required": ["personal_access_token"], + "x-ordering": [ + "driver", + "personal_access_token", + "ssh_key_file", + "ssh_key_names", ], - 'required': [ - 'personal_access_token' - ], - 'x-ordering': [ - 'driver', - 'personal_access_token', - 'ssh_key_file', - 'ssh_key_names', - ], - 'additionalProperties': False + "additionalProperties": False, } self.assertDictContainsSubset(expected, Requirements3.serialize()) class Requirements4(schema.Schema): - title = 'DigitalOcean' - description = 'DigitalOcean Cloud VM configuration requirements.' + title = "DigitalOcean" + description = "DigitalOcean Cloud VM configuration requirements." merge_reqs = Requirements(flatten=True) ssh_key_file_2 = schema.StringItem( - title='SSH Private Key', - description='The path to an SSH private key which will be used ' - 'to authenticate on the deployed VMs') + title="SSH Private Key", + description="The path to an SSH private key which will be used " + "to authenticate on the deployed VMs", + ) ssh_key_names_2 = schema.StringItem( - title='SSH Key Names', - description='The names of an SSH key being managed on ' - 'DigitalOcean account which will be used to ' - 'authenticate on the deployed VMs') + title="SSH Key Names", + description="The names of an SSH key being managed on " + "DigitalOcean account which will be used to " + "authenticate on the deployed VMs", + ) requirements_definition_2 = schema.AnyOfItem( items=( - schema.RequirementsItem(requirements=['ssh_key_file_2']), - schema.RequirementsItem(requirements=['ssh_key_names_2']) + schema.RequirementsItem(requirements=["ssh_key_file_2"]), + schema.RequirementsItem(requirements=["ssh_key_names_2"]), ), )(flatten=True) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'DigitalOcean', - 'description': 'DigitalOcean Cloud VM configuration requirements.', - 'type': 'object', - 'properties': { - 'driver': { - 'default': 'digitalocean', - 'format': 'hidden', - 'type': 'string', - 'title': 'driver' + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "DigitalOcean", + "description": "DigitalOcean Cloud VM configuration requirements.", + "type": "object", + "properties": { + "driver": { + "default": "digitalocean", + "format": "hidden", + "type": "string", + "title": "driver", }, - 'personal_access_token': { - 'type': 'string', - 'description': 'This is the API access token which can be ' - 'generated under the API/Application on your account', - 'title': 'Personal Access Token' + "personal_access_token": { + "type": "string", + "description": "This is the API access token which can be " + "generated under the API/Application on your account", + "title": "Personal Access Token", }, - 'ssh_key_file': { - 'type': 'string', - 'description': 'The path to an SSH private key which will ' - 'be used to authenticate on the deployed VMs', - 'title': 'SSH Private Key' + "ssh_key_file": { + "type": "string", + "description": "The path to an SSH private key which will " + "be used to authenticate on the deployed VMs", + "title": "SSH Private Key", }, - 'ssh_key_names': { - 'type': 'string', - 'description': 'The names of an SSH key being managed on DigitalOcean ' - 'account which will be used to authenticate on the deployed VMs', - 'title': 'SSH Key Names' + "ssh_key_names": { + "type": "string", + "description": "The names of an SSH key being managed on DigitalOcean " + "account which will be used to authenticate on the deployed VMs", + "title": "SSH Key Names", }, - 'ssh_key_file_2': { - 'type': 'string', - 'description': 'The path to an SSH private key which will ' - 'be used to authenticate on the deployed VMs', - 'title': 'SSH Private Key' + "ssh_key_file_2": { + "type": "string", + "description": "The path to an SSH private key which will " + "be used to authenticate on the deployed VMs", + "title": "SSH Private Key", + }, + "ssh_key_names_2": { + "type": "string", + "description": "The names of an SSH key being managed on DigitalOcean " + "account which will be used to authenticate on the deployed VMs", + "title": "SSH Key Names", }, - 'ssh_key_names_2': { - 'type': 'string', - 'description': 'The names of an SSH key being managed on DigitalOcean ' - 'account which will be used to authenticate on the deployed VMs', - 'title': 'SSH Key Names' - } }, - 'anyOf': [ - {'required': ['ssh_key_file']}, - {'required': ['ssh_key_names']}, - {'required': ['ssh_key_file_2']}, - {'required': ['ssh_key_names_2']} + "anyOf": [ + {"required": ["ssh_key_file"]}, + {"required": ["ssh_key_names"]}, + {"required": ["ssh_key_file_2"]}, + {"required": ["ssh_key_names_2"]}, ], - 'required': [ - 'personal_access_token' + "required": ["personal_access_token"], + "x-ordering": [ + "driver", + "personal_access_token", + "ssh_key_file", + "ssh_key_names", + "ssh_key_file_2", + "ssh_key_names_2", ], - 'x-ordering': [ - 'driver', - 'personal_access_token', - 'ssh_key_file', - 'ssh_key_names', - 'ssh_key_file_2', - 'ssh_key_names_2', - ], - 'additionalProperties': False + "additionalProperties": False, } self.assertDictContainsSubset(expected, Requirements4.serialize()) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_optional_requirements_config_validation(self): class BaseRequirements(schema.Schema): - driver = schema.StringItem(default='digitalocean', format='hidden') + driver = schema.StringItem(default="digitalocean", format="hidden") class SSHKeyFileSchema(schema.Schema): ssh_key_file = schema.StringItem( - title='SSH Private Key', - description='The path to an SSH private key which will be used ' - 'to authenticate on the deployed VMs') + title="SSH Private Key", + description="The path to an SSH private key which will be used " + "to authenticate on the deployed VMs", + ) class SSHKeyNamesSchema(schema.Schema): ssh_key_names = schema.StringItem( - title='SSH Key Names', - description='The names of an SSH key being managed on ' - 'Digial Ocean account which will be used to ' - 'authenticate on the deployed VMs') + title="SSH Key Names", + description="The names of an SSH key being managed on " + "Digial Ocean account which will be used to " + "authenticate on the deployed VMs", + ) class Requirements(BaseRequirements): - title = 'DigitalOcean' - description = 'DigitalOcean Cloud VM configuration requirements.' + title = "DigitalOcean" + description = "DigitalOcean Cloud VM configuration requirements." personal_access_token = schema.StringItem( - title='Personal Access Token', - description='This is the API access token which can be generated ' - 'under the API/Application on your account', - required=True) + title="Personal Access Token", + description="This is the API access token which can be generated " + "under the API/Application on your account", + required=True, + ) requirements_definition = schema.AnyOfItem( items=( SSHKeyFileSchema.as_requirements_item(), - SSHKeyNamesSchema.as_requirements_item() + SSHKeyNamesSchema.as_requirements_item(), ), )(flatten=True) ssh_key_file = SSHKeyFileSchema(flatten=True) @@ -479,1519 +467,1641 @@ class ConfigTestCase(TestCase): try: jsonschema.validate( - {'personal_access_token': 'foo', 'ssh_key_names': 'bar', 'ssh_key_file': 'test'}, - Requirements.serialize() + { + "personal_access_token": "foo", + "ssh_key_names": "bar", + "ssh_key_file": "test", + }, + Requirements.serialize(), ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: jsonschema.validate( - {'personal_access_token': 'foo', 'ssh_key_names': 'bar'}, - Requirements.serialize() + {"personal_access_token": "foo", "ssh_key_names": "bar"}, + Requirements.serialize(), ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: jsonschema.validate( - {'personal_access_token': 'foo', 'ssh_key_file': 'test'}, - Requirements.serialize() + {"personal_access_token": "foo", "ssh_key_file": "test"}, + Requirements.serialize(), ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate( - {'personal_access_token': 'foo'}, - Requirements.serialize() + {"personal_access_token": "foo"}, Requirements.serialize() + ) + if JSONSCHEMA_VERSION >= _LooseVersion("3.0.0"): + self.assertIn( + "'ssh_key_file' is a required property", excinfo.exception.message ) - if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'): - self.assertIn('\'ssh_key_file\' is a required property', excinfo.exception.message) else: - self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) + self.assertIn( + "is not valid under any of the given schemas", excinfo.exception.message + ) def test_boolean_config(self): - item = schema.BooleanItem(title='Hungry', description='Are you hungry?') + item = schema.BooleanItem(title="Hungry", description="Are you hungry?") self.assertDictEqual( - item.serialize(), { - 'type': 'boolean', - 'title': item.title, - 'description': item.description - } + item.serialize(), + {"type": "boolean", "title": item.title, "description": item.description}, ) - item = schema.BooleanItem(title='Hungry', - description='Are you hungry?', - default=False) + item = schema.BooleanItem( + title="Hungry", description="Are you hungry?", default=False + ) self.assertDictEqual( - item.serialize(), { - 'type': 'boolean', - 'title': item.title, - 'description': item.description, - 'default': item.default - } + item.serialize(), + { + "type": "boolean", + "title": item.title, + "description": item.description, + "default": item.default, + }, ) - item = schema.BooleanItem(title='Hungry', - description='Are you hungry?', - default=schema.Null) + item = schema.BooleanItem( + title="Hungry", description="Are you hungry?", default=schema.Null + ) self.assertDictEqual( - item.serialize(), { - 'type': 'boolean', - 'title': item.title, - 'description': item.description, - 'default': None - } + item.serialize(), + { + "type": "boolean", + "title": item.title, + "description": item.description, + "default": None, + }, ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_boolean_config_validation(self): class TestConf(schema.Schema): - item = schema.BooleanItem(title='Hungry', description='Are you hungry?') + item = schema.BooleanItem(title="Hungry", description="Are you hungry?") try: - jsonschema.validate({'item': False}, TestConf.serialize()) + jsonschema.validate({"item": False}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 1}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate({"item": 1}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) def test_string_config(self): - item = schema.StringItem(title='Foo', description='Foo Item') + item = schema.StringItem(title="Foo", description="Foo Item") self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description - } + item.serialize(), + {"type": "string", "title": item.title, "description": item.description}, ) - item = schema.StringItem(title='Foo', description='Foo Item', min_length=1, max_length=3) + item = schema.StringItem( + title="Foo", description="Foo Item", min_length=1, max_length=3 + ) self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'minLength': item.min_length, - 'maxLength': item.max_length - } + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "minLength": item.min_length, + "maxLength": item.max_length, + }, ) - item = schema.StringItem(title='Foo', - description='Foo Item', - min_length=1, - max_length=3, - default='foo') + item = schema.StringItem( + title="Foo", + description="Foo Item", + min_length=1, + max_length=3, + default="foo", + ) self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'minLength': item.min_length, - 'maxLength': item.max_length, - 'default': 'foo' - } + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "minLength": item.min_length, + "maxLength": item.max_length, + "default": "foo", + }, ) - item = schema.StringItem(title='Foo', - description='Foo Item', - min_length=1, - max_length=3, - enum=('foo', 'bar')) + item = schema.StringItem( + title="Foo", + description="Foo Item", + min_length=1, + max_length=3, + enum=("foo", "bar"), + ) self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'minLength': item.min_length, - 'maxLength': item.max_length, - 'enum': ['foo', 'bar'] - } + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "minLength": item.min_length, + "maxLength": item.max_length, + "enum": ["foo", "bar"], + }, ) - item = schema.StringItem(title='Foo', - description='Foo Item', - min_length=1, - max_length=3, - enum=('foo', 'bar'), - enumNames=('Foo', 'Bar')) + item = schema.StringItem( + title="Foo", + description="Foo Item", + min_length=1, + max_length=3, + enum=("foo", "bar"), + enumNames=("Foo", "Bar"), + ) self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'minLength': item.min_length, - 'maxLength': item.max_length, - 'enum': ['foo', 'bar'], - 'enumNames': ['Foo', 'Bar'] - } + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "minLength": item.min_length, + "maxLength": item.max_length, + "enum": ["foo", "bar"], + "enumNames": ["Foo", "Bar"], + }, ) - item = schema.StringItem(title='Foo', - description='Foo Item', - pattern=r'^([\w_-]+)$') + item = schema.StringItem( + title="Foo", description="Foo Item", pattern=r"^([\w_-]+)$" + ) self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'pattern': item.pattern - } + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "pattern": item.pattern, + }, ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_string_config_validation(self): class TestConf(schema.Schema): - item = schema.StringItem(title='Foo', description='Foo Item') + item = schema.StringItem(title="Foo", description="Foo Item") try: - jsonschema.validate({'item': 'the item'}, TestConf.serialize()) + jsonschema.validate({"item": "the item"}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) class TestConf(schema.Schema): - item = schema.StringItem(title='Foo', description='Foo Item', - min_length=1, max_length=10) + item = schema.StringItem( + title="Foo", description="Foo Item", min_length=1, max_length=10 + ) try: - jsonschema.validate({'item': 'the item'}, TestConf.serialize()) + jsonschema.validate({"item": "the item"}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 3}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate({"item": 3}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 'the item the item'}, TestConf.serialize()) - self.assertIn('is too long', excinfo.exception.message) + jsonschema.validate({"item": "the item the item"}, TestConf.serialize()) + self.assertIn("is too long", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.StringItem(title='Foo', description='Foo Item', - min_length=10, max_length=100) + item = schema.StringItem( + title="Foo", description="Foo Item", min_length=10, max_length=100 + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 'the item'}, TestConf.serialize()) - self.assertIn('is too short', excinfo.exception.message) + jsonschema.validate({"item": "the item"}, TestConf.serialize()) + self.assertIn("is too short", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.StringItem(title='Foo', - description='Foo Item', - enum=('foo', 'bar')) + item = schema.StringItem( + title="Foo", description="Foo Item", enum=("foo", "bar") + ) try: - jsonschema.validate({'item': 'foo'}, TestConf.serialize()) + jsonschema.validate({"item": "foo"}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) class TestConf(schema.Schema): - item = schema.StringItem(title='Foo', - description='Foo Item', - enum=('foo', 'bar')) - with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 'bin'}, TestConf.serialize()) - self.assertIn('is not one of', excinfo.exception.message) - - class TestConf(schema.Schema): - item = schema.StringItem(title='Foo', description='Foo Item', - pattern=r'^([\w_-]+)$') - - try: - jsonschema.validate({'item': 'the-item'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + item = schema.StringItem( + title="Foo", description="Foo Item", enum=("foo", "bar") + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 'the item'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('does not match', excinfo.exception.message) + jsonschema.validate({"item": "bin"}, TestConf.serialize()) + self.assertIn("is not one of", excinfo.exception.message) - def test_email_config(self): - item = schema.EMailItem(title='Foo', description='Foo Item') - self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'format': item.format - } - ) - - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') - def test_email_config_validation(self): class TestConf(schema.Schema): - item = schema.EMailItem(title='Item', description='Item description') - - try: - jsonschema.validate({'item': 'nobody@nowhere.com'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) - - with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': '3'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not a', excinfo.exception.message) - - def test_ipv4_config(self): - item = schema.IPv4Item(title='Foo', description='Foo Item') - self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'format': item.format - } - ) - - @skipIf(JSONSCHEMA_VERSION <= _LooseVersion('2.5.0'), 'Requires jsonschema 2.5.0 or greater') - def test_ipv4_config_validation(self): - class TestConf(schema.Schema): - item = schema.IPv4Item(title='Item', description='Item description') - - try: - jsonschema.validate({'item': '127.0.0.1'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) - - with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': '3'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not a', excinfo.exception.message) - - def test_ipv6_config(self): - item = schema.IPv6Item(title='Foo', description='Foo Item') - self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'format': item.format - } - ) - - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') - def test_ipv6_config_validation(self): - class TestConf(schema.Schema): - item = schema.IPv6Item(title='Item', description='Item description') + item = schema.StringItem( + title="Foo", description="Foo Item", pattern=r"^([\w_-]+)$" + ) try: jsonschema.validate( - {'item': salt.utils.stringutils.to_str('::1')}, + {"item": "the-item"}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': '3'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not a', excinfo.exception.message) + jsonschema.validate( + {"item": "the item"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("does not match", excinfo.exception.message) + + def test_email_config(self): + item = schema.EMailItem(title="Foo", description="Foo Item") + self.assertDictEqual( + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "format": item.format, + }, + ) + + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") + def test_email_config_validation(self): + class TestConf(schema.Schema): + item = schema.EMailItem(title="Item", description="Item description") + + try: + jsonschema.validate( + {"item": "nobody@nowhere.com"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + except jsonschema.exceptions.ValidationError as exc: + self.fail("ValidationError raised: {0}".format(exc)) + + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: + jsonschema.validate( + {"item": "3"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not a", excinfo.exception.message) + + def test_ipv4_config(self): + item = schema.IPv4Item(title="Foo", description="Foo Item") + self.assertDictEqual( + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "format": item.format, + }, + ) + + @skipIf( + JSONSCHEMA_VERSION <= _LooseVersion("2.5.0"), + "Requires jsonschema 2.5.0 or greater", + ) + def test_ipv4_config_validation(self): + class TestConf(schema.Schema): + item = schema.IPv4Item(title="Item", description="Item description") + + try: + jsonschema.validate( + {"item": "127.0.0.1"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + except jsonschema.exceptions.ValidationError as exc: + self.fail("ValidationError raised: {0}".format(exc)) + + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: + jsonschema.validate( + {"item": "3"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not a", excinfo.exception.message) + + def test_ipv6_config(self): + item = schema.IPv6Item(title="Foo", description="Foo Item") + self.assertDictEqual( + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "format": item.format, + }, + ) + + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") + def test_ipv6_config_validation(self): + class TestConf(schema.Schema): + item = schema.IPv6Item(title="Item", description="Item description") + + try: + jsonschema.validate( + {"item": salt.utils.stringutils.to_str("::1")}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + except jsonschema.exceptions.ValidationError as exc: + self.fail("ValidationError raised: {0}".format(exc)) + + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: + jsonschema.validate( + {"item": "3"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not a", excinfo.exception.message) def test_hostname_config(self): - item = schema.HostnameItem(title='Foo', description='Foo Item') + item = schema.HostnameItem(title="Foo", description="Foo Item") self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'format': item.format - } + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "format": item.format, + }, ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_hostname_config_validation(self): class TestConf(schema.Schema): - item = schema.HostnameItem(title='Item', description='Item description') + item = schema.HostnameItem(title="Item", description="Item description") try: - jsonschema.validate({'item': 'localhost'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + jsonschema.validate( + {"item": "localhost"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': '3'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not a', excinfo.exception.message) + jsonschema.validate( + {"item": "3"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not a", excinfo.exception.message) def test_datetime_config(self): - item = schema.DateTimeItem(title='Foo', description='Foo Item') + item = schema.DateTimeItem(title="Foo", description="Foo Item") self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'format': item.format - } + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "format": item.format, + }, ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') - @skipIf(any([HAS_ISODATE, HAS_STRICT_RFC3339]) is False, 'The \'strict_rfc3339\' or \'isodate\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") + @skipIf( + any([HAS_ISODATE, HAS_STRICT_RFC3339]) is False, + "The 'strict_rfc3339' or 'isodate' library is missing", + ) def test_datetime_config_validation(self): class TestConf(schema.Schema): - item = schema.DateTimeItem(title='Item', description='Item description') + item = schema.DateTimeItem(title="Item", description="Item description") try: - jsonschema.validate({'item': '2015-07-01T18:05:27+01:00'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + jsonschema.validate( + {"item": "2015-07-01T18:05:27+01:00"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': '3'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not a', excinfo.exception.message) + jsonschema.validate( + {"item": "3"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not a", excinfo.exception.message) def test_secret_config(self): - item = schema.SecretItem(title='Foo', description='Foo Item') + item = schema.SecretItem(title="Foo", description="Foo Item") self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'format': item.format - } + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "format": item.format, + }, ) def test_uri_config(self): - item = schema.UriItem(title='Foo', description='Foo Item') + item = schema.UriItem(title="Foo", description="Foo Item") self.assertDictEqual( - item.serialize(), { - 'type': 'string', - 'title': item.title, - 'description': item.description, - 'format': item.format - } + item.serialize(), + { + "type": "string", + "title": item.title, + "description": item.description, + "format": item.format, + }, ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') - @skipIf(HAS_RFC3987 is False, 'The \'rfc3987\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") + @skipIf(HAS_RFC3987 is False, "The 'rfc3987' library is missing") def test_uri_config_validation(self): class TestConf(schema.Schema): - item = schema.UriItem(title='Item', description='Item description') + item = schema.UriItem(title="Item", description="Item description") try: - jsonschema.validate({'item': 'ssh://localhost'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + jsonschema.validate( + {"item": "ssh://localhost"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': '3'}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not a', excinfo.exception.message) + jsonschema.validate( + {"item": "3"}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not a", excinfo.exception.message) def test_number_config(self): - item = schema.NumberItem(title='How many dogs', description='Question') + item = schema.NumberItem(title="How many dogs", description="Question") self.assertDictEqual( - item.serialize(), { - 'type': 'number', - 'title': item.title, - 'description': item.description - } + item.serialize(), + {"type": "number", "title": item.title, "description": item.description}, ) - item = schema.NumberItem(title='How many dogs', - description='Question', - minimum=0, - maximum=10) + item = schema.NumberItem( + title="How many dogs", description="Question", minimum=0, maximum=10 + ) self.assertDictEqual( - item.serialize(), { - 'type': 'number', - 'title': item.title, - 'description': item.description, - 'minimum': item.minimum, - 'maximum': item.maximum - } + item.serialize(), + { + "type": "number", + "title": item.title, + "description": item.description, + "minimum": item.minimum, + "maximum": item.maximum, + }, ) - item = schema.NumberItem(title='How many dogs', - description='Question', - multiple_of=2) + item = schema.NumberItem( + title="How many dogs", description="Question", multiple_of=2 + ) self.assertDictEqual( - item.serialize(), { - 'type': 'number', - 'title': item.title, - 'description': item.description, - 'multipleOf': item.multiple_of - } + item.serialize(), + { + "type": "number", + "title": item.title, + "description": item.description, + "multipleOf": item.multiple_of, + }, ) - item = schema.NumberItem(title='How many dogs', - description='Question', - minimum=0, - exclusive_minimum=True, - maximum=10, - exclusive_maximum=True) + item = schema.NumberItem( + title="How many dogs", + description="Question", + minimum=0, + exclusive_minimum=True, + maximum=10, + exclusive_maximum=True, + ) self.assertDictEqual( - item.serialize(), { - 'type': 'number', - 'title': item.title, - 'description': item.description, - 'minimum': item.minimum, - 'maximum': item.maximum, - 'exclusiveMinimum': True, - 'exclusiveMaximum': True - } + item.serialize(), + { + "type": "number", + "title": item.title, + "description": item.description, + "minimum": item.minimum, + "maximum": item.maximum, + "exclusiveMinimum": True, + "exclusiveMaximum": True, + }, ) - item = schema.NumberItem(title='How many dogs', - description='Question', - minimum=0, - maximum=10, - default=0) + item = schema.NumberItem( + title="How many dogs", + description="Question", + minimum=0, + maximum=10, + default=0, + ) self.assertDictEqual( - item.serialize(), { - 'type': 'number', - 'title': item.title, - 'description': item.description, - 'minimum': item.minimum, - 'maximum': item.maximum, - 'default': 0 - } + item.serialize(), + { + "type": "number", + "title": item.title, + "description": item.description, + "minimum": item.minimum, + "maximum": item.maximum, + "default": 0, + }, ) - item = schema.NumberItem(title='How many dogs', - description='Question', - minimum=0, - maximum=10, - default=0, - enum=(0, 2, 4, 6)) + item = schema.NumberItem( + title="How many dogs", + description="Question", + minimum=0, + maximum=10, + default=0, + enum=(0, 2, 4, 6), + ) self.assertDictEqual( - item.serialize(), { - 'type': 'number', - 'title': item.title, - 'description': item.description, - 'minimum': item.minimum, - 'maximum': item.maximum, - 'default': 0, - 'enum': [0, 2, 4, 6] - } + item.serialize(), + { + "type": "number", + "title": item.title, + "description": item.description, + "minimum": item.minimum, + "maximum": item.maximum, + "default": 0, + "enum": [0, 2, 4, 6], + }, ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_number_config_validation(self): class TestConf(schema.Schema): - item = schema.NumberItem(title='How many dogs', description='Question') + item = schema.NumberItem(title="How many dogs", description="Question") try: - jsonschema.validate({'item': 2}, TestConf.serialize()) + jsonschema.validate({"item": 2}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': '3'}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate({"item": "3"}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.NumberItem(title='How many dogs', - description='Question', - multiple_of=2.2) + item = schema.NumberItem( + title="How many dogs", description="Question", multiple_of=2.2 + ) try: - jsonschema.validate({'item': 4.4}, TestConf.serialize()) + jsonschema.validate({"item": 4.4}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 4}, TestConf.serialize()) - self.assertIn('is not a multiple of', excinfo.exception.message) + jsonschema.validate({"item": 4}, TestConf.serialize()) + self.assertIn("is not a multiple of", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.NumberItem(title='Foo', description='Foo Item', - minimum=1, maximum=10) + item = schema.NumberItem( + title="Foo", description="Foo Item", minimum=1, maximum=10 + ) try: - jsonschema.validate({'item': 3}, TestConf.serialize()) + jsonschema.validate({"item": 3}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 11}, TestConf.serialize()) - self.assertIn('is greater than the maximum of', excinfo.exception.message) + jsonschema.validate({"item": 11}, TestConf.serialize()) + self.assertIn("is greater than the maximum of", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.NumberItem(title='Foo', description='Foo Item', - minimum=10, maximum=100) + item = schema.NumberItem( + title="Foo", description="Foo Item", minimum=10, maximum=100 + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 3}, TestConf.serialize()) - self.assertIn('is less than the minimum of', excinfo.exception.message) + jsonschema.validate({"item": 3}, TestConf.serialize()) + self.assertIn("is less than the minimum of", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.NumberItem(title='How many dogs', - description='Question', - minimum=0, - exclusive_minimum=True, - maximum=10, - exclusive_maximum=True) + item = schema.NumberItem( + title="How many dogs", + description="Question", + minimum=0, + exclusive_minimum=True, + maximum=10, + exclusive_maximum=True, + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 0}, TestConf.serialize()) - self.assertIn('is less than or equal to the minimum of', excinfo.exception.message) + jsonschema.validate({"item": 0}, TestConf.serialize()) + self.assertIn( + "is less than or equal to the minimum of", excinfo.exception.message + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 10}, TestConf.serialize()) - self.assertIn('is greater than or equal to the maximum of', excinfo.exception.message) + jsonschema.validate({"item": 10}, TestConf.serialize()) + self.assertIn( + "is greater than or equal to the maximum of", excinfo.exception.message + ) class TestConf(schema.Schema): - item = schema.NumberItem(title='Foo', - description='Foo Item', - enum=(0, 2, 4, 6)) + item = schema.NumberItem( + title="Foo", description="Foo Item", enum=(0, 2, 4, 6) + ) try: - jsonschema.validate({'item': 4}, TestConf.serialize()) + jsonschema.validate({"item": 4}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) class TestConf(schema.Schema): - item = schema.NumberItem(title='Foo', - description='Foo Item', - enum=(0, 2, 4, 6)) + item = schema.NumberItem( + title="Foo", description="Foo Item", enum=(0, 2, 4, 6) + ) + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 3}, TestConf.serialize()) - self.assertIn('is not one of', excinfo.exception.message) + jsonschema.validate({"item": 3}, TestConf.serialize()) + self.assertIn("is not one of", excinfo.exception.message) def test_integer_config(self): - item = schema.IntegerItem(title='How many dogs', description='Question') + item = schema.IntegerItem(title="How many dogs", description="Question") self.assertDictEqual( - item.serialize(), { - 'type': 'integer', - 'title': item.title, - 'description': item.description - } + item.serialize(), + {"type": "integer", "title": item.title, "description": item.description}, ) - item = schema.IntegerItem(title='How many dogs', - description='Question', - minimum=0, - maximum=10) + item = schema.IntegerItem( + title="How many dogs", description="Question", minimum=0, maximum=10 + ) self.assertDictEqual( - item.serialize(), { - 'type': 'integer', - 'title': item.title, - 'description': item.description, - 'minimum': item.minimum, - 'maximum': item.maximum - } + item.serialize(), + { + "type": "integer", + "title": item.title, + "description": item.description, + "minimum": item.minimum, + "maximum": item.maximum, + }, ) - item = schema.IntegerItem(title='How many dogs', - description='Question', - multiple_of=2) + item = schema.IntegerItem( + title="How many dogs", description="Question", multiple_of=2 + ) self.assertDictEqual( - item.serialize(), { - 'type': 'integer', - 'title': item.title, - 'description': item.description, - 'multipleOf': item.multiple_of - } + item.serialize(), + { + "type": "integer", + "title": item.title, + "description": item.description, + "multipleOf": item.multiple_of, + }, ) - item = schema.IntegerItem(title='How many dogs', - description='Question', - minimum=0, - exclusive_minimum=True, - maximum=10, - exclusive_maximum=True) + item = schema.IntegerItem( + title="How many dogs", + description="Question", + minimum=0, + exclusive_minimum=True, + maximum=10, + exclusive_maximum=True, + ) self.assertDictEqual( - item.serialize(), { - 'type': 'integer', - 'title': item.title, - 'description': item.description, - 'minimum': item.minimum, - 'maximum': item.maximum, - 'exclusiveMinimum': True, - 'exclusiveMaximum': True - } + item.serialize(), + { + "type": "integer", + "title": item.title, + "description": item.description, + "minimum": item.minimum, + "maximum": item.maximum, + "exclusiveMinimum": True, + "exclusiveMaximum": True, + }, ) - item = schema.IntegerItem(title='How many dogs', - description='Question', - minimum=0, - maximum=10, - default=0) + item = schema.IntegerItem( + title="How many dogs", + description="Question", + minimum=0, + maximum=10, + default=0, + ) self.assertDictEqual( - item.serialize(), { - 'type': 'integer', - 'title': item.title, - 'description': item.description, - 'minimum': item.minimum, - 'maximum': item.maximum, - 'default': 0 - } + item.serialize(), + { + "type": "integer", + "title": item.title, + "description": item.description, + "minimum": item.minimum, + "maximum": item.maximum, + "default": 0, + }, ) - item = schema.IntegerItem(title='How many dogs', - description='Question', - minimum=0, - maximum=10, - default=0, - enum=(0, 2, 4, 6)) + item = schema.IntegerItem( + title="How many dogs", + description="Question", + minimum=0, + maximum=10, + default=0, + enum=(0, 2, 4, 6), + ) self.assertDictEqual( - item.serialize(), { - 'type': 'integer', - 'title': item.title, - 'description': item.description, - 'minimum': item.minimum, - 'maximum': item.maximum, - 'default': 0, - 'enum': [0, 2, 4, 6] - } + item.serialize(), + { + "type": "integer", + "title": item.title, + "description": item.description, + "minimum": item.minimum, + "maximum": item.maximum, + "default": 0, + "enum": [0, 2, 4, 6], + }, ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_integer_config_validation(self): class TestConf(schema.Schema): - item = schema.IntegerItem(title='How many dogs', description='Question') + item = schema.IntegerItem(title="How many dogs", description="Question") try: - jsonschema.validate({'item': 2}, TestConf.serialize()) + jsonschema.validate({"item": 2}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 3.1}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate({"item": 3.1}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.IntegerItem(title='How many dogs', - description='Question', - multiple_of=2) + item = schema.IntegerItem( + title="How many dogs", description="Question", multiple_of=2 + ) try: - jsonschema.validate({'item': 4}, TestConf.serialize()) + jsonschema.validate({"item": 4}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 3}, TestConf.serialize()) - self.assertIn('is not a multiple of', excinfo.exception.message) + jsonschema.validate({"item": 3}, TestConf.serialize()) + self.assertIn("is not a multiple of", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.IntegerItem(title='Foo', description='Foo Item', - minimum=1, maximum=10) + item = schema.IntegerItem( + title="Foo", description="Foo Item", minimum=1, maximum=10 + ) try: - jsonschema.validate({'item': 3}, TestConf.serialize()) + jsonschema.validate({"item": 3}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 11}, TestConf.serialize()) - self.assertIn('is greater than the maximum of', excinfo.exception.message) + jsonschema.validate({"item": 11}, TestConf.serialize()) + self.assertIn("is greater than the maximum of", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.IntegerItem(title='Foo', description='Foo Item', - minimum=10, maximum=100) + item = schema.IntegerItem( + title="Foo", description="Foo Item", minimum=10, maximum=100 + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 3}, TestConf.serialize()) - self.assertIn('is less than the minimum of', excinfo.exception.message) + jsonschema.validate({"item": 3}, TestConf.serialize()) + self.assertIn("is less than the minimum of", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.IntegerItem(title='How many dogs', - description='Question', - minimum=0, - exclusive_minimum=True, - maximum=10, - exclusive_maximum=True) + item = schema.IntegerItem( + title="How many dogs", + description="Question", + minimum=0, + exclusive_minimum=True, + maximum=10, + exclusive_maximum=True, + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 0}, TestConf.serialize()) - self.assertIn('is less than or equal to the minimum of', excinfo.exception.message) + jsonschema.validate({"item": 0}, TestConf.serialize()) + self.assertIn( + "is less than or equal to the minimum of", excinfo.exception.message + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 10}, TestConf.serialize()) - self.assertIn('is greater than or equal to the maximum of', excinfo.exception.message) + jsonschema.validate({"item": 10}, TestConf.serialize()) + self.assertIn( + "is greater than or equal to the maximum of", excinfo.exception.message + ) class TestConf(schema.Schema): - item = schema.IntegerItem(title='Foo', - description='Foo Item', - enum=(0, 2, 4, 6)) + item = schema.IntegerItem( + title="Foo", description="Foo Item", enum=(0, 2, 4, 6) + ) try: - jsonschema.validate({'item': 4}, TestConf.serialize()) + jsonschema.validate({"item": 4}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) class TestConf(schema.Schema): - item = schema.IntegerItem(title='Foo', - description='Foo Item', - enum=(0, 2, 4, 6)) + item = schema.IntegerItem( + title="Foo", description="Foo Item", enum=(0, 2, 4, 6) + ) + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 3}, TestConf.serialize()) - self.assertIn('is not one of', excinfo.exception.message) + jsonschema.validate({"item": 3}, TestConf.serialize()) + self.assertIn("is not one of", excinfo.exception.message) def test_array_config(self): - string_item = schema.StringItem(title='Dog Name', - description='The dog name') - item = schema.ArrayItem(title='Dog Names', - description='Name your dogs', - items=string_item) - - self.assertDictEqual( - item.serialize(), { - 'type': 'array', - 'title': item.title, - 'description': item.description, - 'items': { - 'type': 'string', - 'title': string_item.title, - 'description': string_item.description - } - } + string_item = schema.StringItem(title="Dog Name", description="The dog name") + item = schema.ArrayItem( + title="Dog Names", description="Name your dogs", items=string_item ) - integer_item = schema.IntegerItem(title='Dog Age', - description='The dog age') - item = schema.ArrayItem(title='Dog Names', - description='Name your dogs', - items=(string_item, integer_item)) - self.assertDictEqual( - item.serialize(), { - 'type': 'array', - 'title': item.title, - 'description': item.description, - 'items': [ - { - 'type': 'string', - 'title': string_item.title, - 'description': string_item.description - }, - { - 'type': 'integer', - 'title': integer_item.title, - 'description': integer_item.description - } - ] - } + item.serialize(), + { + "type": "array", + "title": item.title, + "description": item.description, + "items": { + "type": "string", + "title": string_item.title, + "description": string_item.description, + }, + }, ) - item = schema.ArrayItem(title='Dog Names', - description='Name your dogs', - items=(schema.StringItem(), - schema.IntegerItem()), - min_items=1, - max_items=3, - additional_items=False, - unique_items=True) + integer_item = schema.IntegerItem(title="Dog Age", description="The dog age") + item = schema.ArrayItem( + title="Dog Names", + description="Name your dogs", + items=(string_item, integer_item), + ) self.assertDictEqual( - item.serialize(), { - 'type': 'array', - 'title': item.title, - 'description': item.description, - 'minItems': item.min_items, - 'maxItems': item.max_items, - 'uniqueItems': item.unique_items, - 'additionalItems': item.additional_items, - 'items': [ + item.serialize(), + { + "type": "array", + "title": item.title, + "description": item.description, + "items": [ { - 'type': 'string', + "type": "string", + "title": string_item.title, + "description": string_item.description, }, { - 'type': 'integer', - } - ] - } + "type": "integer", + "title": integer_item.title, + "description": integer_item.description, + }, + ], + }, + ) + + item = schema.ArrayItem( + title="Dog Names", + description="Name your dogs", + items=(schema.StringItem(), schema.IntegerItem()), + min_items=1, + max_items=3, + additional_items=False, + unique_items=True, + ) + + self.assertDictEqual( + item.serialize(), + { + "type": "array", + "title": item.title, + "description": item.description, + "minItems": item.min_items, + "maxItems": item.max_items, + "uniqueItems": item.unique_items, + "additionalItems": item.additional_items, + "items": [{"type": "string"}, {"type": "integer"}], + }, ) class HowManyConfig(schema.Schema): - item = schema.IntegerItem(title='How many dogs', description='Question') + item = schema.IntegerItem(title="How many dogs", description="Question") - item = schema.ArrayItem(title='Dog Names', - description='Name your dogs', - items=HowManyConfig()) + item = schema.ArrayItem( + title="Dog Names", description="Name your dogs", items=HowManyConfig() + ) self.assertDictEqual( - item.serialize(), { - 'type': 'array', - 'title': item.title, - 'description': item.description, - 'items': HowManyConfig.serialize() - } + item.serialize(), + { + "type": "array", + "title": item.title, + "description": item.description, + "items": HowManyConfig.serialize(), + }, ) class AgesConfig(schema.Schema): item = schema.IntegerItem() - item = schema.ArrayItem(title='Dog Names', - description='Name your dogs', - items=(HowManyConfig(), AgesConfig())) + item = schema.ArrayItem( + title="Dog Names", + description="Name your dogs", + items=(HowManyConfig(), AgesConfig()), + ) self.assertDictEqual( - item.serialize(), { - 'type': 'array', - 'title': item.title, - 'description': item.description, - 'items': [ - HowManyConfig.serialize(), - AgesConfig.serialize() - ] - } + item.serialize(), + { + "type": "array", + "title": item.title, + "description": item.description, + "items": [HowManyConfig.serialize(), AgesConfig.serialize()], + }, ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_array_config_validation(self): class TestConf(schema.Schema): - item = schema.ArrayItem(title='Dog Names', - description='Name your dogs', - items=schema.StringItem()) + item = schema.ArrayItem( + title="Dog Names", + description="Name your dogs", + items=schema.StringItem(), + ) try: - jsonschema.validate({'item': ['Tobias', 'Óscar']}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + jsonschema.validate( + {"item": ["Tobias", "Óscar"]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': ['Tobias', 'Óscar', 3]}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate( + {"item": ["Tobias", "Óscar", 3]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not of type", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.ArrayItem(title='Dog Names', - description='Name your dogs', - items=schema.StringItem(), - min_items=1, - max_items=2) + item = schema.ArrayItem( + title="Dog Names", + description="Name your dogs", + items=schema.StringItem(), + min_items=1, + max_items=2, + ) try: - jsonschema.validate({'item': ['Tobias', 'Óscar']}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + jsonschema.validate( + {"item": ["Tobias", "Óscar"]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': ['Tobias', 'Óscar', 'Pepe']}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is too long', excinfo.exception.message) + jsonschema.validate( + {"item": ["Tobias", "Óscar", "Pepe"]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is too long", excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': []}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is too short', excinfo.exception.message) + jsonschema.validate( + {"item": []}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is too short", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.ArrayItem(title='Dog Names', - description='Name your dogs', - items=schema.StringItem(), - uniqueItems=True) + item = schema.ArrayItem( + title="Dog Names", + description="Name your dogs", + items=schema.StringItem(), + uniqueItems=True, + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': ['Tobias', 'Tobias']}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('has non-unique elements', excinfo.exception.message) + jsonschema.validate( + {"item": ["Tobias", "Tobias"]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("has non-unique elements", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.ArrayItem(items=(schema.StringItem(), - schema.IntegerItem())) + item = schema.ArrayItem(items=(schema.StringItem(), schema.IntegerItem())) + try: - jsonschema.validate({'item': ['Óscar', 4]}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + jsonschema.validate( + {"item": ["Óscar", 4]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': ['Tobias', 'Óscar']}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate( + {"item": ["Tobias", "Óscar"]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not of type", excinfo.exception.message) class TestConf(schema.Schema): item = schema.ArrayItem( items=schema.ArrayItem( - items=(schema.StringItem(), - schema.IntegerItem()) + items=(schema.StringItem(), schema.IntegerItem()) ) ) try: - jsonschema.validate({'item': [['Tobias', 8], ['Óscar', 4]]}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + jsonschema.validate( + {"item": [["Tobias", 8], ["Óscar", 4]]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': [['Tobias', 8], ['Óscar', '4']]}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate( + {"item": [["Tobias", 8], ["Óscar", "4"]]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not of type", excinfo.exception.message) class TestConf(schema.Schema): - item = schema.ArrayItem(items=schema.StringItem(enum=['Tobias', 'Óscar'])) + item = schema.ArrayItem(items=schema.StringItem(enum=["Tobias", "Óscar"])) + try: - jsonschema.validate({'item': ['Óscar']}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + jsonschema.validate( + {"item": ["Óscar"]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: - jsonschema.validate({'item': ['Tobias']}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) + jsonschema.validate( + {"item": ["Tobias"]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': ['Pepe']}, TestConf.serialize(), - format_checker=jsonschema.FormatChecker()) - self.assertIn('is not one of', excinfo.exception.message) + jsonschema.validate( + {"item": ["Pepe"]}, + TestConf.serialize(), + format_checker=jsonschema.FormatChecker(), + ) + self.assertIn("is not one of", excinfo.exception.message) def test_dict_config(self): item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'sides': schema.IntegerItem() - } + title="Poligon", + description="Describe the Poligon", + properties={"sides": schema.IntegerItem()}, ) self.assertDictEqual( - item.serialize(), { - 'type': 'object', - 'title': item.title, - 'description': item.description, - 'properties': { - 'sides': {'type': 'integer'} - } - } + item.serialize(), + { + "type": "object", + "title": item.title, + "description": item.description, + "properties": {"sides": {"type": "integer"}}, + }, ) item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'sides': schema.IntegerItem() - }, + title="Poligon", + description="Describe the Poligon", + properties={"sides": schema.IntegerItem()}, min_properties=1, - max_properties=2 + max_properties=2, ) self.assertDictEqual( - item.serialize(), { - 'type': 'object', - 'title': item.title, - 'description': item.description, - 'properties': { - 'sides': {'type': 'integer'} - }, - 'minProperties': 1, - 'maxProperties': 2 - } + item.serialize(), + { + "type": "object", + "title": item.title, + "description": item.description, + "properties": {"sides": {"type": "integer"}}, + "minProperties": 1, + "maxProperties": 2, + }, ) item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - pattern_properties={ - 's.*': schema.IntegerItem() - }, + title="Poligon", + description="Describe the Poligon", + pattern_properties={"s.*": schema.IntegerItem()}, min_properties=1, - max_properties=2 + max_properties=2, ) self.assertDictEqual( - item.serialize(), { - 'type': 'object', - 'title': item.title, - 'description': item.description, - 'patternProperties': { - 's.*': {'type': 'integer'} - }, - 'minProperties': 1, - 'maxProperties': 2 - } + item.serialize(), + { + "type": "object", + "title": item.title, + "description": item.description, + "patternProperties": {"s.*": {"type": "integer"}}, + "minProperties": 1, + "maxProperties": 2, + }, ) item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'color': schema.StringItem(enum=['red', 'green', 'blue']) - }, - pattern_properties={ - 's*': schema.IntegerItem() - }, + title="Poligon", + description="Describe the Poligon", + properties={"color": schema.StringItem(enum=["red", "green", "blue"])}, + pattern_properties={"s*": schema.IntegerItem()}, min_properties=1, - max_properties=2 + max_properties=2, ) self.assertDictEqual( - item.serialize(), { - 'type': 'object', - 'title': item.title, - 'description': item.description, - 'properties': { - 'color': { - 'type': 'string', - 'enum': ['red', 'green', 'blue'] - } + item.serialize(), + { + "type": "object", + "title": item.title, + "description": item.description, + "properties": { + "color": {"type": "string", "enum": ["red", "green", "blue"]} }, - 'patternProperties': { - 's*': {'type': 'integer'} - }, - 'minProperties': 1, - 'maxProperties': 2 - } + "patternProperties": {"s*": {"type": "integer"}}, + "minProperties": 1, + "maxProperties": 2, + }, ) item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'color': schema.StringItem(enum=['red', 'green', 'blue']) - }, - pattern_properties={ - 's*': schema.IntegerItem() - }, + title="Poligon", + description="Describe the Poligon", + properties={"color": schema.StringItem(enum=["red", "green", "blue"])}, + pattern_properties={"s*": schema.IntegerItem()}, additional_properties=True, min_properties=1, - max_properties=2 + max_properties=2, ) self.assertDictEqual( - item.serialize(), { - 'type': 'object', - 'title': item.title, - 'description': item.description, - 'properties': { - 'color': { - 'type': 'string', - 'enum': ['red', 'green', 'blue'] - } + item.serialize(), + { + "type": "object", + "title": item.title, + "description": item.description, + "properties": { + "color": {"type": "string", "enum": ["red", "green", "blue"]} }, - 'patternProperties': { - 's*': {'type': 'integer'} - }, - 'minProperties': 1, - 'maxProperties': 2, - 'additionalProperties': True - } + "patternProperties": {"s*": {"type": "integer"}}, + "minProperties": 1, + "maxProperties": 2, + "additionalProperties": True, + }, ) item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'sides': schema.IntegerItem() - }, - additional_properties=schema.OneOfItem(items=[schema.BooleanItem(), - schema.StringItem()]) + title="Poligon", + description="Describe the Poligon", + properties={"sides": schema.IntegerItem()}, + additional_properties=schema.OneOfItem( + items=[schema.BooleanItem(), schema.StringItem()] + ), ) self.assertDictEqual( - item.serialize(), { - 'type': 'object', - 'title': item.title, - 'description': item.description, - 'properties': { - 'sides': {'type': 'integer'} + item.serialize(), + { + "type": "object", + "title": item.title, + "description": item.description, + "properties": {"sides": {"type": "integer"}}, + "additionalProperties": { + "oneOf": [{"type": "boolean"}, {"type": "string"}] }, - 'additionalProperties': { - 'oneOf': [ - {'type': 'boolean'}, - {'type': 'string'} - ] - } - } + }, ) class TestConf(schema.Schema): item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'sides': schema.IntegerItem(required=True) - }, - additional_properties=schema.OneOfItem(items=[schema.BooleanItem(), - schema.StringItem()]) + title="Poligon", + description="Describe the Poligon", + properties={"sides": schema.IntegerItem(required=True)}, + additional_properties=schema.OneOfItem( + items=[schema.BooleanItem(), schema.StringItem()] + ), ) self.assertDictContainsSubset( - TestConf.serialize(), { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'type': 'object', - 'properties': { - 'item': { - 'title': 'Poligon', - 'description': 'Describe the Poligon', - 'type': 'object', - 'properties': { - 'sides': { - 'type': 'integer' - } + TestConf.serialize(), + { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "item": { + "title": "Poligon", + "description": "Describe the Poligon", + "type": "object", + "properties": {"sides": {"type": "integer"}}, + "additionalProperties": { + "oneOf": [{"type": "boolean"}, {"type": "string"}] }, - 'additionalProperties': { - 'oneOf': [ - { - 'type': 'boolean' - }, - { - 'type': 'string' - } - ] - }, - 'required': [ - 'sides' - ], + "required": ["sides"], } }, - 'x-ordering': [ - 'item' - ], - 'additionalProperties': False - } + "x-ordering": ["item"], + "additionalProperties": False, + }, ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_dict_config_validation(self): class TestConf(schema.Schema): item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'sides': schema.IntegerItem() - } - ) - try: - jsonschema.validate({'item': {'sides': 1}}, TestConf.serialize()) - except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) - - with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': {'sides': '1'}}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) - - with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 2}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) - - class TestConf(schema.Schema): - item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'color': schema.StringItem(enum=['red', 'green', 'blue']) - }, - pattern_properties={ - 'si.*': schema.IntegerItem() - }, + title="Poligon", + description="Describe the Poligon", + properties={"sides": schema.IntegerItem()}, ) try: - jsonschema.validate({'item': {'sides': 1, 'color': 'red'}}, TestConf.serialize()) + jsonschema.validate({"item": {"sides": 1}}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': {'sides': '4', 'color': 'blue'}}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate({"item": {"sides": "1"}}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 2}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate({"item": 2}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) class TestConf(schema.Schema): item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'color': schema.StringItem(enum=['red', 'green', 'blue']) - }, - pattern_properties={ - 'si.*': schema.IntegerItem() - }, - additional_properties=False + title="Poligon", + description="Describe the Poligon", + properties={"color": schema.StringItem(enum=["red", "green", "blue"])}, + pattern_properties={"si.*": schema.IntegerItem()}, + ) + + try: + jsonschema.validate( + {"item": {"sides": 1, "color": "red"}}, TestConf.serialize() + ) + except jsonschema.exceptions.ValidationError as exc: + self.fail("ValidationError raised: {0}".format(exc)) + + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: + jsonschema.validate( + {"item": {"sides": "4", "color": "blue"}}, TestConf.serialize() + ) + self.assertIn("is not of type", excinfo.exception.message) + + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: + jsonschema.validate({"item": 2}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) + + class TestConf(schema.Schema): + item = schema.DictItem( + title="Poligon", + description="Describe the Poligon", + properties={"color": schema.StringItem(enum=["red", "green", "blue"])}, + pattern_properties={"si.*": schema.IntegerItem()}, + additional_properties=False, ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': {'color': 'green', 'sides': 4, 'surfaces': 4}}, TestConf.serialize()) - if JSONSCHEMA_VERSION < _LooseVersion('2.6.0'): + jsonschema.validate( + {"item": {"color": "green", "sides": 4, "surfaces": 4}}, + TestConf.serialize(), + ) + if JSONSCHEMA_VERSION < _LooseVersion("2.6.0"): self.assertIn( - 'Additional properties are not allowed', - excinfo.exception.message) + "Additional properties are not allowed", excinfo.exception.message + ) else: self.assertIn( - '\'surfaces\' does not match any of the regexes', - excinfo.exception.message) + "'surfaces' does not match any of the regexes", + excinfo.exception.message, + ) class TestConf(schema.Schema): item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'color': schema.StringItem(enum=['red', 'green', 'blue']) - }, - additional_properties=schema.OneOfItem(items=[ - schema.BooleanItem(), - schema.IntegerItem() - ]) + title="Poligon", + description="Describe the Poligon", + properties={"color": schema.StringItem(enum=["red", "green", "blue"])}, + additional_properties=schema.OneOfItem( + items=[schema.BooleanItem(), schema.IntegerItem()] + ), ) try: - jsonschema.validate({'item': {'sides': 1, - 'color': 'red', - 'rugged_surface': False}}, TestConf.serialize()) + jsonschema.validate( + {"item": {"sides": 1, "color": "red", "rugged_surface": False}}, + TestConf.serialize(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': {'sides': '4', 'color': 'blue'}}, TestConf.serialize()) - if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'): + jsonschema.validate( + {"item": {"sides": "4", "color": "blue"}}, TestConf.serialize() + ) + if JSONSCHEMA_VERSION >= _LooseVersion("3.0.0"): self.assertIn("'4'", excinfo.exception.message) self.assertIn("is not of type", excinfo.exception.message) self.assertIn("'boolean'", excinfo.exception.message) else: - self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) + self.assertIn( + "is not valid under any of the given schemas", excinfo.exception.message + ) class TestConf(schema.Schema): item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'color': schema.StringItem(enum=['red', 'green', 'blue']) - }, - additional_properties=schema.OneOfItem(items=[ - schema.BooleanItem(), - schema.IntegerItem() - ]), + title="Poligon", + description="Describe the Poligon", + properties={"color": schema.StringItem(enum=["red", "green", "blue"])}, + additional_properties=schema.OneOfItem( + items=[schema.BooleanItem(), schema.IntegerItem()] + ), min_properties=2, - max_properties=3 + max_properties=3, ) try: - jsonschema.validate({'item': {'color': 'red', 'sides': 1}}, TestConf.serialize()) + jsonschema.validate( + {"item": {"color": "red", "sides": 1}}, TestConf.serialize() + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: - jsonschema.validate({'item': {'sides': 1, 'color': 'red', 'rugged_surface': False}}, TestConf.serialize()) + jsonschema.validate( + {"item": {"sides": 1, "color": "red", "rugged_surface": False}}, + TestConf.serialize(), + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': {'color': 'blue'}}, TestConf.serialize()) - self.assertIn('does not have enough properties', excinfo.exception.message) + jsonschema.validate({"item": {"color": "blue"}}, TestConf.serialize()) + self.assertIn("does not have enough properties", excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': {'sides': 4, - 'color': 'blue', - 'rugged_surface': False, - 'opaque': True}}, TestConf.serialize()) - self.assertIn('has too many properties', excinfo.exception.message) + jsonschema.validate( + { + "item": { + "sides": 4, + "color": "blue", + "rugged_surface": False, + "opaque": True, + } + }, + TestConf.serialize(), + ) + self.assertIn("has too many properties", excinfo.exception.message) class TestConf(schema.Schema): item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', - properties={ - 'sides': schema.IntegerItem(required=True) - }, - additional_properties=schema.OneOfItem(items=[schema.BooleanItem(), - schema.StringItem()]) + title="Poligon", + description="Describe the Poligon", + properties={"sides": schema.IntegerItem(required=True)}, + additional_properties=schema.OneOfItem( + items=[schema.BooleanItem(), schema.StringItem()] + ), ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': {'color': 'blue', - 'rugged_surface': False, - 'opaque': True}}, TestConf.serialize()) - self.assertIn('\'sides\' is a required property', excinfo.exception.message) + jsonschema.validate( + {"item": {"color": "blue", "rugged_surface": False, "opaque": True}}, + TestConf.serialize(), + ) + self.assertIn("'sides' is a required property", excinfo.exception.message) class Props(schema.Schema): sides = schema.IntegerItem(required=True) class TestConf(schema.Schema): item = schema.DictItem( - title='Poligon', - description='Describe the Poligon', + title="Poligon", + description="Describe the Poligon", properties=Props(), - additional_properties=schema.OneOfItem(items=[schema.BooleanItem(), - schema.StringItem()]) + additional_properties=schema.OneOfItem( + items=[schema.BooleanItem(), schema.StringItem()] + ), ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': {'color': 'blue', - 'rugged_surface': False, - 'opaque': True}}, TestConf.serialize()) - self.assertIn('\'sides\' is a required property', excinfo.exception.message) + jsonschema.validate( + {"item": {"color": "blue", "rugged_surface": False, "opaque": True}}, + TestConf.serialize(), + ) + self.assertIn("'sides' is a required property", excinfo.exception.message) def test_oneof_config(self): item = schema.OneOfItem( - items=(schema.StringItem(title='Yes', enum=['yes']), - schema.StringItem(title='No', enum=['no'])) + items=( + schema.StringItem(title="Yes", enum=["yes"]), + schema.StringItem(title="No", enum=["no"]), + ) ) self.assertEqual( - item.serialize(), { - 'oneOf': [i.serialize() for i in item.items] - } + item.serialize(), {"oneOf": [i.serialize() for i in item.items]} ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_oneof_config_validation(self): class TestConf(schema.Schema): item = schema.ArrayItem( - title='Hungry', - description='Are you hungry?', + title="Hungry", + description="Are you hungry?", items=schema.OneOfItem( - items=(schema.StringItem(title='Yes', enum=['yes']), - schema.StringItem(title='No', enum=['no'])) - ) + items=( + schema.StringItem(title="Yes", enum=["yes"]), + schema.StringItem(title="No", enum=["no"]), + ) + ), ) try: - jsonschema.validate({'item': ['no']}, TestConf.serialize()) + jsonschema.validate({"item": ["no"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': ['maybe']}, TestConf.serialize()) - if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'): + jsonschema.validate({"item": ["maybe"]}, TestConf.serialize()) + if JSONSCHEMA_VERSION >= _LooseVersion("3.0.0"): self.assertIn("'maybe'", excinfo.exception.message) self.assertIn("is not one of", excinfo.exception.message) self.assertIn("'yes'", excinfo.exception.message) else: - self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) + self.assertIn( + "is not valid under any of the given schemas", excinfo.exception.message + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 2}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate({"item": 2}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) def test_anyof_config(self): item = schema.AnyOfItem( - items=(schema.StringItem(title='Yes', enum=['yes']), - schema.StringItem(title='No', enum=['no'])) + items=( + schema.StringItem(title="Yes", enum=["yes"]), + schema.StringItem(title="No", enum=["no"]), + ) ) self.assertEqual( - item.serialize(), { - 'anyOf': [i.serialize() for i in item.items] # pylint: disable=E1133 - } + item.serialize(), + {"anyOf": [i.serialize() for i in item.items]}, # pylint: disable=E1133 ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_anyof_config_validation(self): class TestConf(schema.Schema): item = schema.ArrayItem( - title='Hungry', - description='Are you hungry?', + title="Hungry", + description="Are you hungry?", items=schema.AnyOfItem( - items=(schema.StringItem(title='Yes', enum=['yes']), - schema.StringItem(title='No', enum=['no']), - schema.BooleanItem()) - ) + items=( + schema.StringItem(title="Yes", enum=["yes"]), + schema.StringItem(title="No", enum=["no"]), + schema.BooleanItem(), + ) + ), ) try: - jsonschema.validate({'item': ['no']}, TestConf.serialize()) + jsonschema.validate({"item": ["no"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: - jsonschema.validate({'item': ['yes']}, TestConf.serialize()) + jsonschema.validate({"item": ["yes"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: - jsonschema.validate({'item': [True]}, TestConf.serialize()) + jsonschema.validate({"item": [True]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: - jsonschema.validate({'item': [False]}, TestConf.serialize()) + jsonschema.validate({"item": [False]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': ['maybe']}, TestConf.serialize()) - if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'): + jsonschema.validate({"item": ["maybe"]}, TestConf.serialize()) + if JSONSCHEMA_VERSION >= _LooseVersion("3.0.0"): self.assertIn("'maybe'", excinfo.exception.message) self.assertIn("is not one of", excinfo.exception.message) self.assertIn("'yes'", excinfo.exception.message) else: - self.assertIn('is not valid under any of the given schemas', excinfo.exception.message) + self.assertIn( + "is not valid under any of the given schemas", excinfo.exception.message + ) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 2}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate({"item": 2}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) def test_allof_config(self): item = schema.AllOfItem( - items=(schema.StringItem(min_length=2), - schema.StringItem(max_length=3)) + items=(schema.StringItem(min_length=2), schema.StringItem(max_length=3)) ) self.assertEqual( - item.serialize(), { - 'allOf': [i.serialize() for i in item.items] # pylint: disable=E1133 - } + item.serialize(), + {"allOf": [i.serialize() for i in item.items]}, # pylint: disable=E1133 ) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_allof_config_validation(self): class TestConf(schema.Schema): item = schema.ArrayItem( - title='Hungry', - description='Are you hungry?', + title="Hungry", + description="Are you hungry?", items=schema.AllOfItem( - items=(schema.StringItem(min_length=2), - schema.StringItem(max_length=3)) - ) + items=( + schema.StringItem(min_length=2), + schema.StringItem(max_length=3), + ) + ), ) try: - jsonschema.validate({'item': ['no']}, TestConf.serialize()) + jsonschema.validate({"item": ["no"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: - jsonschema.validate({'item': ['yes']}, TestConf.serialize()) + jsonschema.validate({"item": ["yes"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': ['maybe']}, TestConf.serialize()) - self.assertIn('is too long', excinfo.exception.message) + jsonschema.validate({"item": ["maybe"]}, TestConf.serialize()) + self.assertIn("is too long", excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': ['hmmmm']}, TestConf.serialize()) - self.assertIn('is too long', excinfo.exception.message) + jsonschema.validate({"item": ["hmmmm"]}, TestConf.serialize()) + self.assertIn("is too long", excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': 2}, TestConf.serialize()) - self.assertIn('is not of type', excinfo.exception.message) + jsonschema.validate({"item": 2}, TestConf.serialize()) + self.assertIn("is not of type", excinfo.exception.message) def test_not_config(self): item = schema.NotItem(item=schema.BooleanItem()) - self.assertEqual( - item.serialize(), { - 'not': item.item.serialize() - } - ) + self.assertEqual(item.serialize(), {"not": item.item.serialize()}) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_not_config_validation(self): class TestConf(schema.Schema): item = schema.ArrayItem( - title='Hungry', - description='Are you hungry?', - items=schema.NotItem(item=schema.BooleanItem()) + title="Hungry", + description="Are you hungry?", + items=schema.NotItem(item=schema.BooleanItem()), ) try: - jsonschema.validate({'item': ['no']}, TestConf.serialize()) + jsonschema.validate({"item": ["no"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) try: - jsonschema.validate({'item': ['yes']}, TestConf.serialize()) + jsonschema.validate({"item": ["yes"]}, TestConf.serialize()) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': [True]}, TestConf.serialize()) - self.assertIn('is not allowed for', excinfo.exception.message) + jsonschema.validate({"item": [True]}, TestConf.serialize()) + self.assertIn("is not allowed for", excinfo.exception.message) with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: - jsonschema.validate({'item': [False]}, TestConf.serialize()) - self.assertIn('is not allowed for', excinfo.exception.message) + jsonschema.validate({"item": [False]}, TestConf.serialize()) + self.assertIn("is not allowed for", excinfo.exception.message) def test_item_name_override_class_attrname(self): class TestConf(schema.Schema): - item = schema.BooleanItem(name='hungry', title='Hungry', description='Are you hungry?') + item = schema.BooleanItem( + name="hungry", title="Hungry", description="Are you hungry?" + ) expected = { "$schema": "http://json-schema.org/draft-04/schema#", @@ -2000,22 +2110,20 @@ class ConfigTestCase(TestCase): "hungry": { "type": "boolean", "description": "Are you hungry?", - "title": "Hungry" + "title": "Hungry", } }, - "x-ordering": [ - "hungry" - ], - "additionalProperties": False + "x-ordering": ["hungry"], + "additionalProperties": False, } self.assertDictEqual(TestConf.serialize(), expected) def test_config_name_override_class_attrname(self): class TestConf(schema.Schema): - item = schema.BooleanItem(title='Hungry', description='Are you hungry?') + item = schema.BooleanItem(title="Hungry", description="Are you hungry?") class TestConf2(schema.Schema): - a_name = TestConf(name='another_name') + a_name = TestConf(name="another_name") expected = { "$schema": "http://json-schema.org/draft-04/schema#", @@ -2028,68 +2136,63 @@ class ConfigTestCase(TestCase): "item": { "type": "boolean", "description": "Are you hungry?", - "title": "Hungry" + "title": "Hungry", } }, - "x-ordering": [ - "item" - ], - "additionalProperties": False + "x-ordering": ["item"], + "additionalProperties": False, } }, - "x-ordering": [ - "another_name" - ], - "additionalProperties": False + "x-ordering": ["another_name"], + "additionalProperties": False, } self.assertDictEqual(TestConf2.serialize(), expected) class ComplexSchemaItem(schema.ComplexSchemaItem): - _complex_attributes = ['thirsty'] - thirsty = schema.BooleanItem(title='Thirsty', - description='Are you thirsty?') + _complex_attributes = ["thirsty"] + thirsty = schema.BooleanItem(title="Thirsty", description="Are you thirsty?") class ComplexComplexSchemaItem(schema.ComplexSchemaItem): - _complex_attributes = ['hungry', 'complex_item'] + _complex_attributes = ["hungry", "complex_item"] - hungry = schema.BooleanItem(title='Hungry', - description='Are you hungry?', - required=True) - complex_item = ComplexSchemaItem(definition_name='test_definition') + hungry = schema.BooleanItem( + title="Hungry", description="Are you hungry?", required=True + ) + complex_item = ComplexSchemaItem(definition_name="test_definition") class TestComplexDefinitionsSchema(schema.DefinitionsSchema): - title = 'Test Complex Definition Schema' + title = "Test Complex Definition Schema" complex_item = ComplexSchemaItem() class TestOneOfComplexDefinitionsSchema(schema.DefinitionsSchema): - title = 'Test OneOf Complex Definitions Schema' - one_of_item = schema.OneOfItem( - items=[ComplexSchemaItem(), schema.StringItem()]) + title = "Test OneOf Complex Definitions Schema" + one_of_item = schema.OneOfItem(items=[ComplexSchemaItem(), schema.StringItem()]) class TestArrayComplexDefinitionsSchema(schema.DefinitionsSchema): - title = 'Test Array Complex Definitions Schema' + title = "Test Array Complex Definitions Schema" array_item = schema.ArrayItem(items=ComplexSchemaItem()) class TestDictComplexDefinitionsSchema(schema.DefinitionsSchema): - title = 'Test Dict Complex Definitions Schema' + title = "Test Dict Complex Definitions Schema" dict_item = schema.DictItem( - properties={'complex_obj': ComplexSchemaItem(required=True)}, - additional_properties=ComplexSchemaItem()) + properties={"complex_obj": ComplexSchemaItem(required=True)}, + additional_properties=ComplexSchemaItem(), + ) class TestComplexComplexDefinitionsSchema(schema.DefinitionsSchema): - title = 'Test Complex Complex Definition Schema' + title = "Test Complex Complex Definition Schema" complex_complex_item = ComplexComplexSchemaItem() class ComplexSchemaTestCase(TestCase): - ''' Test cases with definition schemas containing complex items''' + """ Test cases with definition schemas containing complex items""" obj = ComplexSchemaItem() complex_obj = ComplexComplexSchemaItem() @@ -2101,247 +2204,308 @@ class ComplexSchemaTestCase(TestCase): def test_complex_schema_item_serialize(self): obj = copy.deepcopy(self.obj) - expected_serialized = {'$ref': - '#/definitions/ComplexSchemaItem'} + expected_serialized = {"$ref": "#/definitions/ComplexSchemaItem"} self.assertDictEqual(obj.serialize(), expected_serialized) def test_complex_schema_item_definition(self): obj = copy.deepcopy(self.obj) expected_def = { - 'type': 'object', - 'title': 'ComplexSchemaItem', - 'properties': { - 'thirsty': { - 'type': 'boolean', - 'title': 'Thirsty', - 'description': 'Are you thirsty?'}}} + "type": "object", + "title": "ComplexSchemaItem", + "properties": { + "thirsty": { + "type": "boolean", + "title": "Thirsty", + "description": "Are you thirsty?", + } + }, + } self.assertDictEqual(obj.get_definition(), expected_def) def test_complex_complex_schema_item_definition(self): complex_obj = copy.deepcopy(self.complex_obj) expected_def = { - 'type': 'object', - 'title': 'ComplexComplexSchemaItem', - 'properties': { - 'hungry': { - 'type': 'boolean', - 'title': 'Hungry', - 'description': 'Are you hungry?'}, - 'complex_item': { - 'type': 'object', - '$ref': '#/definitions/test_definition'}}, - 'required': ['hungry']} + "type": "object", + "title": "ComplexComplexSchemaItem", + "properties": { + "hungry": { + "type": "boolean", + "title": "Hungry", + "description": "Are you hungry?", + }, + "complex_item": { + "type": "object", + "$ref": "#/definitions/test_definition", + }, + }, + "required": ["hungry"], + } self.assertDictEqual(complex_obj.get_definition(), expected_def) def test_complex_definition_schema(self): - serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps(self.schema.serialize())) + serialized = salt.utils.yaml.safe_load( + salt.utils.json.dumps(self.schema.serialize()) + ) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'Test Complex Definition Schema', - 'type': 'object', - 'properties': { - 'complex_item': { - '$ref': '#/definitions/ComplexSchemaItem'}}, - 'x-ordering': ['complex_item'], - 'additionalProperties': False, - 'definitions': { - 'ComplexSchemaItem': { - 'type': 'object', - 'title': 'ComplexSchemaItem', - 'properties': { - 'thirsty': { - 'type': 'boolean', - 'title': 'Thirsty', - 'description': 'Are you thirsty?'}}}}} + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test Complex Definition Schema", + "type": "object", + "properties": {"complex_item": {"$ref": "#/definitions/ComplexSchemaItem"}}, + "x-ordering": ["complex_item"], + "additionalProperties": False, + "definitions": { + "ComplexSchemaItem": { + "type": "object", + "title": "ComplexSchemaItem", + "properties": { + "thirsty": { + "type": "boolean", + "title": "Thirsty", + "description": "Are you thirsty?", + } + }, + } + }, + } self.assertDictEqual(serialized, expected) def test_one_of_complex_definition_schema(self): - serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps(self.one_of_schema.serialize())) + serialized = salt.utils.yaml.safe_load( + salt.utils.json.dumps(self.one_of_schema.serialize()) + ) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'Test OneOf Complex Definitions Schema', - 'type': 'object', - 'properties': { - 'one_of_item': { - 'oneOf': [{'$ref': '#/definitions/ComplexSchemaItem'}, - {'type': 'string'}]}}, - 'x-ordering': ['one_of_item'], - 'additionalProperties': False, - 'definitions': { - 'ComplexSchemaItem': { - 'type': 'object', - 'title': 'ComplexSchemaItem', - 'properties': { - 'thirsty': { - 'type': 'boolean', - 'title': 'Thirsty', - 'description': 'Are you thirsty?'}}}}} + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test OneOf Complex Definitions Schema", + "type": "object", + "properties": { + "one_of_item": { + "oneOf": [ + {"$ref": "#/definitions/ComplexSchemaItem"}, + {"type": "string"}, + ] + } + }, + "x-ordering": ["one_of_item"], + "additionalProperties": False, + "definitions": { + "ComplexSchemaItem": { + "type": "object", + "title": "ComplexSchemaItem", + "properties": { + "thirsty": { + "type": "boolean", + "title": "Thirsty", + "description": "Are you thirsty?", + } + }, + } + }, + } self.assertDictEqual(serialized, expected) def test_array_complex_definition_schema(self): - serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps(self.array_schema.serialize())) + serialized = salt.utils.yaml.safe_load( + salt.utils.json.dumps(self.array_schema.serialize()) + ) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'Test Array Complex Definitions Schema', - 'type': 'object', - 'properties': { - 'array_item': { - 'type': 'array', - 'title': 'array_item', - 'items': {'$ref': '#/definitions/ComplexSchemaItem'}}}, - 'x-ordering': ['array_item'], - 'additionalProperties': False, - 'definitions': { - 'ComplexSchemaItem': { - 'type': 'object', - 'title': 'ComplexSchemaItem', - 'properties': { - 'thirsty': { - 'type': 'boolean', - 'title': 'Thirsty', - 'description': 'Are you thirsty?'}}}}} + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test Array Complex Definitions Schema", + "type": "object", + "properties": { + "array_item": { + "type": "array", + "title": "array_item", + "items": {"$ref": "#/definitions/ComplexSchemaItem"}, + } + }, + "x-ordering": ["array_item"], + "additionalProperties": False, + "definitions": { + "ComplexSchemaItem": { + "type": "object", + "title": "ComplexSchemaItem", + "properties": { + "thirsty": { + "type": "boolean", + "title": "Thirsty", + "description": "Are you thirsty?", + } + }, + } + }, + } self.assertDictEqual(serialized, expected) def test_dict_complex_definition_schema(self): - serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps(self.dict_schema.serialize())) + serialized = salt.utils.yaml.safe_load( + salt.utils.json.dumps(self.dict_schema.serialize()) + ) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'Test Dict Complex Definitions Schema', - 'type': 'object', - 'properties': { - 'dict_item': { - 'type': 'object', - 'title': 'dict_item', - 'required': ['complex_obj'], - 'properties': - {'complex_obj': - {'$ref': '#/definitions/ComplexSchemaItem'}}, - 'additionalProperties': - {'$ref': '#/definitions/ComplexSchemaItem'}}}, - 'x-ordering': ['dict_item'], - 'additionalProperties': False, - 'definitions': { - 'ComplexSchemaItem': { - 'type': 'object', - 'title': 'ComplexSchemaItem', - 'properties': { - 'thirsty': { - 'type': 'boolean', - 'title': 'Thirsty', - 'description': 'Are you thirsty?'}}}}} + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test Dict Complex Definitions Schema", + "type": "object", + "properties": { + "dict_item": { + "type": "object", + "title": "dict_item", + "required": ["complex_obj"], + "properties": { + "complex_obj": {"$ref": "#/definitions/ComplexSchemaItem"} + }, + "additionalProperties": {"$ref": "#/definitions/ComplexSchemaItem"}, + } + }, + "x-ordering": ["dict_item"], + "additionalProperties": False, + "definitions": { + "ComplexSchemaItem": { + "type": "object", + "title": "ComplexSchemaItem", + "properties": { + "thirsty": { + "type": "boolean", + "title": "Thirsty", + "description": "Are you thirsty?", + } + }, + } + }, + } self.assertDictEqual(serialized, expected) def test_complex_complex_definition_schema(self): - serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps( - self.complex_schema.serialize() - )) + serialized = salt.utils.yaml.safe_load( + salt.utils.json.dumps(self.complex_schema.serialize()) + ) expected = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'title': 'Test Complex Complex Definition Schema', - 'type': 'object', - 'properties': { - 'complex_complex_item': { - '$ref': '#/definitions/ComplexComplexSchemaItem'}}, - 'x-ordering': ['complex_complex_item'], - 'additionalProperties': False, - 'definitions': { - 'ComplexComplexSchemaItem': { - 'type': 'object', - 'title': 'ComplexComplexSchemaItem', - 'properties': { - 'hungry': { - 'type': 'boolean', - 'title': 'Hungry', - 'description': 'Are you hungry?'}, - 'complex_item': { - 'type': 'object', - '$ref': '#/definitions/test_definition'}}, - 'required': ['hungry']}, - 'test_definition': { - 'type': 'object', - 'title': 'test_definition', - 'properties': { - 'thirsty': { - 'type': 'boolean', - 'title': 'Thirsty', - 'description': 'Are you thirsty?'}}}}} + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Test Complex Complex Definition Schema", + "type": "object", + "properties": { + "complex_complex_item": { + "$ref": "#/definitions/ComplexComplexSchemaItem" + } + }, + "x-ordering": ["complex_complex_item"], + "additionalProperties": False, + "definitions": { + "ComplexComplexSchemaItem": { + "type": "object", + "title": "ComplexComplexSchemaItem", + "properties": { + "hungry": { + "type": "boolean", + "title": "Hungry", + "description": "Are you hungry?", + }, + "complex_item": { + "type": "object", + "$ref": "#/definitions/test_definition", + }, + }, + "required": ["hungry"], + }, + "test_definition": { + "type": "object", + "title": "test_definition", + "properties": { + "thirsty": { + "type": "boolean", + "title": "Thirsty", + "description": "Are you thirsty?", + } + }, + }, + }, + } self.assertDictEqual(serialized, expected) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_complex_schema_item_thirsty_valid(self): serialized = self.schema.serialize() try: - jsonschema.validate({'complex_item': {'thirsty': True}}, - serialized) + jsonschema.validate({"complex_item": {"thirsty": True}}, serialized) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_complex_schema_item_thirsty_invalid(self): serialized = self.schema.serialize() - with self.assertRaises(jsonschema.exceptions.ValidationError) \ - as excinfo: - jsonschema.validate({'complex_item': {'thirsty': 'Foo'}}, - serialized) - expected = "u'Foo' is not of type u'boolean'" if six.PY2 \ + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: + jsonschema.validate({"complex_item": {"thirsty": "Foo"}}, serialized) + expected = ( + "u'Foo' is not of type u'boolean'" + if six.PY2 else "'Foo' is not of type 'boolean'" + ) self.assertIn(expected, excinfo.exception.message) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_complex_complex_schema_item_hungry_valid(self): serialized = self.complex_schema.serialize() try: - jsonschema.validate({'complex_complex_item': {'hungry': True}}, - serialized) + jsonschema.validate({"complex_complex_item": {"hungry": True}}, serialized) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_both_complex_complex_schema_all_items_valid(self): serialized = self.complex_schema.serialize() try: - jsonschema.validate({'complex_complex_item': - {'hungry': True, - 'complex_item': {'thirsty': True}}}, - serialized) + jsonschema.validate( + { + "complex_complex_item": { + "hungry": True, + "complex_item": {"thirsty": True}, + } + }, + serialized, + ) except jsonschema.exceptions.ValidationError as exc: - self.fail('ValidationError raised: {0}'.format(exc)) + self.fail("ValidationError raised: {0}".format(exc)) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_complex_complex_schema_item_hungry_invalid(self): serialized = self.complex_schema.serialize() - with self.assertRaises(jsonschema.exceptions.ValidationError) \ - as excinfo: - jsonschema.validate({'complex_complex_item': {'hungry': 'Foo'}}, - serialized) - expected = "u'Foo' is not of type u'boolean'" if six.PY2 \ + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: + jsonschema.validate({"complex_complex_item": {"hungry": "Foo"}}, serialized) + expected = ( + "u'Foo' is not of type u'boolean'" + if six.PY2 else "'Foo' is not of type 'boolean'" + ) self.assertIn(expected, excinfo.exception.message) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_complex_complex_schema_item_inner_thirsty_invalid(self): serialized = self.complex_schema.serialize() - with self.assertRaises(jsonschema.exceptions.ValidationError) \ - as excinfo: + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate( - {'complex_complex_item': {'hungry': True, - 'complex_item': {'thirsty': 'Bar'}}}, - serialized) - expected = "u'Bar' is not of type u'boolean'" if six.PY2 \ + { + "complex_complex_item": { + "hungry": True, + "complex_item": {"thirsty": "Bar"}, + } + }, + serialized, + ) + expected = ( + "u'Bar' is not of type u'boolean'" + if six.PY2 else "'Bar' is not of type 'boolean'" + ) self.assertIn(expected, excinfo.exception.message) - @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing') + @skipIf(HAS_JSONSCHEMA is False, "The 'jsonschema' library is missing") def test_complex_complex_schema_item_missing_required_hungry(self): serialized = self.complex_schema.serialize() - with self.assertRaises(jsonschema.exceptions.ValidationError) \ - as excinfo: + with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo: jsonschema.validate( - {'complex_complex_item': {'complex_item': {'thirsty': True}}}, - serialized) - self.assertIn('\'hungry\' is a required property', - excinfo.exception.message) + {"complex_complex_item": {"complex_item": {"thirsty": True}}}, + serialized, + ) + self.assertIn("'hungry' is a required property", excinfo.exception.message) diff --git a/tests/unit/utils/test_sdb.py b/tests/unit/utils/test_sdb.py index 5d2242d2ee1..dbaac31dd6f 100644 --- a/tests/unit/utils/test_sdb.py +++ b/tests/unit/utils/test_sdb.py @@ -1,38 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Vernon Cole <vernondcole@gmail.com>` -''' +""" # Import Python Libs from __future__ import absolute_import, print_function, unicode_literals -import os -# Import Salt Testing Libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase +import os # Import Salt Libs import salt.utils.sdb as sdb +from tests.support.mixins import LoaderModuleMockMixin + +# Import Salt Testing Libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase class SdbTestCase(TestCase, LoaderModuleMockMixin): - ''' + """ Test cases for salt.modules.sdb - ''' + """ @classmethod def setUpClass(cls): - cls.TEMP_DATABASE_FILE = os.path.join(RUNTIME_VARS.TMP, 'test_sdb.sqlite') + cls.TEMP_DATABASE_FILE = os.path.join(RUNTIME_VARS.TMP, "test_sdb.sqlite") cls.sdb_opts = { - 'extension_modules': '', - 'optimization_order': [0, 1, 2], - 'test_sdb_data': { - 'driver': 'sqlite3', - 'database': cls.TEMP_DATABASE_FILE, - 'table': 'sdb', - 'create_table': True - } + "extension_modules": "", + "optimization_order": [0, 1, 2], + "test_sdb_data": { + "driver": "sqlite3", + "database": cls.TEMP_DATABASE_FILE, + "table": "sdb", + "create_table": True, + }, } @classmethod @@ -48,14 +49,13 @@ class SdbTestCase(TestCase, LoaderModuleMockMixin): # test with SQLite database key not presest def test_sqlite_get_not_found(self): - what = sdb.sdb_get( - 'sdb://test_sdb_data/thisKeyDoesNotExist', self.sdb_opts) + what = sdb.sdb_get("sdb://test_sdb_data/thisKeyDoesNotExist", self.sdb_opts) self.assertEqual(what, None) # test with SQLite database write and read def test_sqlite_get_found(self): - expected = {b'name': b'testone', b'number': 46} - sdb.sdb_set('sdb://test_sdb_data/test1', expected, self.sdb_opts) - resp = sdb.sdb_get('sdb://test_sdb_data/test1', self.sdb_opts) + expected = {b"name": b"testone", b"number": 46} + sdb.sdb_set("sdb://test_sdb_data/test1", expected, self.sdb_opts) + resp = sdb.sdb_get("sdb://test_sdb_data/test1", self.sdb_opts) self.assertEqual(resp, expected) diff --git a/tests/unit/utils/test_ssdp.py b/tests/unit/utils/test_ssdp.py index d73caa2fa8f..d37facc98bc 100644 --- a/tests/unit/utils/test_ssdp.py +++ b/tests/unit/utils/test_ssdp.py @@ -1,20 +1,17 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Bo Maryniuk <bo@suse.de>` -''' +""" from __future__ import absolute_import, print_function, unicode_literals + import datetime -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - MagicMock, - patch) - -from salt.ext.six.moves import zip -from salt.ext import six - import salt.utils.ssdp as ssdp import salt.utils.stringutils +from salt.ext import six +from salt.ext.six.moves import zip +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf try: import pytest @@ -24,10 +21,10 @@ except ImportError: class Mocks(object): def get_socket_mock(self, expected_ip, expected_hostname): - ''' + """ Get a mock of a socket :return: - ''' + """ sck = MagicMock() sck.getsockname = MagicMock(return_value=(expected_ip, 123456)) @@ -40,84 +37,90 @@ class Mocks(object): def get_ssdp_factory(self, expected_ip=None, expected_hostname=None, **config): if expected_ip is None: - expected_ip = '127.0.0.1' + expected_ip = "127.0.0.1" if expected_hostname is None: - expected_hostname = 'localhost' + expected_hostname = "localhost" sock_mock = self.get_socket_mock(expected_ip, expected_hostname) - with patch('salt.utils.ssdp.socket', sock_mock): + with patch("salt.utils.ssdp.socket", sock_mock): factory = ssdp.SSDPFactory(**config) return factory - def get_ssdp_discovery_client(self, expected_ip=None, expected_hostname=None, **config): + def get_ssdp_discovery_client( + self, expected_ip=None, expected_hostname=None, **config + ): if expected_ip is None: - expected_ip = '127.0.0.1' + expected_ip = "127.0.0.1" if expected_hostname is None: - expected_hostname = 'localhost' + expected_hostname = "localhost" sock_mock = self.get_socket_mock(expected_ip, expected_hostname) - with patch('salt.utils.ssdp.socket', sock_mock): + with patch("salt.utils.ssdp.socket", sock_mock): factory = ssdp.SSDPDiscoveryClient(**config) return factory - def get_ssdp_discovery_server(self, expected_ip=None, expected_hostname=None, **config): + def get_ssdp_discovery_server( + self, expected_ip=None, expected_hostname=None, **config + ): if expected_ip is None: - expected_ip = '127.0.0.1' + expected_ip = "127.0.0.1" if expected_hostname is None: - expected_hostname = 'localhost' + expected_hostname = "localhost" sock_mock = self.get_socket_mock(expected_ip, expected_hostname) - with patch('salt.utils.ssdp.socket', sock_mock): + with patch("salt.utils.ssdp.socket", sock_mock): factory = ssdp.SSDPDiscoveryServer(**config) return factory -@skipIf(pytest is None, 'PyTest is missing') +@skipIf(pytest is None, "PyTest is missing") class SSDPBaseTestCase(TestCase, Mocks): - ''' + """ TestCase for SSDP-related parts. - ''' + """ @staticmethod def exception_generic(*args, **kwargs): - ''' + """ Side effect :return: - ''' - raise Exception('some network error') + """ + raise Exception("some network error") @staticmethod def exception_attr_error(*args, **kwargs): - ''' + """ Side effect :return: - ''' - raise AttributeError('attribute error: {0}. {1}'.format(args, kwargs)) + """ + raise AttributeError("attribute error: {0}. {1}".format(args, kwargs)) - @patch('salt.utils.ssdp._json', None) - @patch('salt.utils.ssdp.asyncio', None) + @patch("salt.utils.ssdp._json", None) + @patch("salt.utils.ssdp.asyncio", None) def test_base_avail(self): - ''' + """ Test SSDP base class availability method. :return: - ''' + """ base = ssdp.SSDPBase() assert not base._is_available() - with patch('salt.utils.ssdp._json', True): + with patch("salt.utils.ssdp._json", True): assert not base._is_available() - with patch('salt.utils.ssdp.asyncio', True): + with patch("salt.utils.ssdp.asyncio", True): assert not base._is_available() - with patch('salt.utils.ssdp._json', True), patch('salt.utils.ssdp.asyncio', True): + with patch("salt.utils.ssdp._json", True), patch( + "salt.utils.ssdp.asyncio", True + ): assert base._is_available() def test_base_protocol_settings(self): - ''' + """ Tests default constants data. :return: - ''' + """ base = ssdp.SSDPBase() - v_keys = ['signature', 'answer', 'port', 'listen_ip', 'timeout'] - v_vals = ['__salt_master_service', {}, 4520, '0.0.0.0', 3] + v_keys = ["signature", "answer", "port", "listen_ip", "timeout"] + v_vals = ["__salt_master_service", {}, 4520, "0.0.0.0", 3] for key in v_keys: assert key in base.DEFAULTS @@ -128,40 +131,41 @@ class SSDPBaseTestCase(TestCase, Mocks): assert base.DEFAULTS[key] == value def test_base_self_ip(self): - ''' + """ Test getting self IP method. :return: - ''' + """ base = ssdp.SSDPBase() - expected_ip = '192.168.1.10' - expected_host = 'oxygen' + expected_ip = "192.168.1.10" + expected_host = "oxygen" sock_mock = self.get_socket_mock(expected_ip, expected_host) - with patch('salt.utils.ssdp.socket', sock_mock): + with patch("salt.utils.ssdp.socket", sock_mock): assert base.get_self_ip() == expected_ip sock_mock.socket().getsockname.side_effect = SSDPBaseTestCase.exception_generic - with patch('salt.utils.ssdp.socket', sock_mock): + with patch("salt.utils.ssdp.socket", sock_mock): assert base.get_self_ip() == expected_ip -@skipIf(pytest is None, 'PyTest is missing') +@skipIf(pytest is None, "PyTest is missing") class SSDPFactoryTestCase(TestCase, Mocks): - ''' + """ Test socket protocol - ''' + """ + def test_attr_check(self): - ''' + """ Tests attributes are set to the base class :return: - ''' + """ config = { - ssdp.SSDPBase.SIGNATURE: '-signature-', - ssdp.SSDPBase.ANSWER: {'this-is': 'the-answer'} + ssdp.SSDPBase.SIGNATURE: "-signature-", + ssdp.SSDPBase.ANSWER: {"this-is": "the-answer"}, } - expected_ip = '10.10.10.10' + expected_ip = "10.10.10.10" factory = self.get_ssdp_factory(expected_ip=expected_ip, **config) for attr in [ssdp.SSDPBase.SIGNATURE, ssdp.SSDPBase.ANSWER]: assert hasattr(factory, attr) @@ -170,199 +174,246 @@ class SSDPFactoryTestCase(TestCase, Mocks): assert factory.my_ip == expected_ip def test_transport_sendto_success(self): - ''' + """ Test transport send_to. :return: - ''' + """ transport = MagicMock() log = MagicMock() factory = self.get_ssdp_factory() - with patch.object(factory, 'transport', transport), patch.object(factory, 'log', log): - data = {'some': 'data'} - addr = '10.10.10.10' + with patch.object(factory, "transport", transport), patch.object( + factory, "log", log + ): + data = {"some": "data"} + addr = "10.10.10.10" factory._sendto(data=data, addr=addr) assert factory.transport.sendto.called - assert factory.transport.sendto.mock_calls[0][1][0]['some'] == 'data' - assert factory.transport.sendto.mock_calls[0][2]['addr'] == '10.10.10.10' + assert factory.transport.sendto.mock_calls[0][1][0]["some"] == "data" + assert factory.transport.sendto.mock_calls[0][2]["addr"] == "10.10.10.10" assert factory.log.debug.called - assert factory.log.debug.mock_calls[0][1][0] == 'Sent successfully' + assert factory.log.debug.mock_calls[0][1][0] == "Sent successfully" def test_transport_sendto_retry(self): - ''' + """ Test transport send_to. :return: - ''' - with patch('salt.utils.ssdp.time.sleep', MagicMock()): + """ + with patch("salt.utils.ssdp.time.sleep", MagicMock()): transport = MagicMock() - transport.sendto = MagicMock(side_effect=SSDPBaseTestCase.exception_attr_error) + transport.sendto = MagicMock( + side_effect=SSDPBaseTestCase.exception_attr_error + ) log = MagicMock() factory = self.get_ssdp_factory() - with patch.object(factory, 'transport', transport), patch.object(factory, 'log', log): - data = {'some': 'data'} - addr = '10.10.10.10' + with patch.object(factory, "transport", transport), patch.object( + factory, "log", log + ): + data = {"some": "data"} + addr = "10.10.10.10" factory._sendto(data=data, addr=addr) assert factory.transport.sendto.called assert ssdp.time.sleep.called - assert ssdp.time.sleep.call_args[0][0] > 0 and ssdp.time.sleep.call_args[0][0] < 0.5 + assert ( + ssdp.time.sleep.call_args[0][0] > 0 + and ssdp.time.sleep.call_args[0][0] < 0.5 + ) assert factory.log.debug.called - assert 'Permission error' in factory.log.debug.mock_calls[0][1][0] + assert "Permission error" in factory.log.debug.mock_calls[0][1][0] def test_datagram_signature_bad(self): - ''' + """ Test datagram_received on bad signature :return: - ''' + """ factory = self.get_ssdp_factory() - data = 'nonsense' - addr = '10.10.10.10', 'foo.suse.de' + data = "nonsense" + addr = "10.10.10.10", "foo.suse.de" - with patch.object(factory, 'log', MagicMock()): + with patch.object(factory, "log", MagicMock()): factory.datagram_received(data=data, addr=addr) assert factory.log.debug.called - assert 'Received bad signature from' in factory.log.debug.call_args[0][0] + assert "Received bad signature from" in factory.log.debug.call_args[0][0] assert factory.log.debug.call_args[0][1] == addr[0] assert factory.log.debug.call_args[0][2] == addr[1] def test_datagram_signature_wrong_timestamp_quiet(self): - ''' + """ Test datagram receives a wrong timestamp (no reply). :return: - ''' + """ factory = self.get_ssdp_factory() - data = '{}nonsense'.format(ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE]) - addr = '10.10.10.10', 'foo.suse.de' - with patch.object(factory, 'log', MagicMock()), patch.object(factory, '_sendto', MagicMock()): + data = "{}nonsense".format(ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE]) + addr = "10.10.10.10", "foo.suse.de" + with patch.object(factory, "log", MagicMock()), patch.object( + factory, "_sendto", MagicMock() + ): factory.datagram_received(data=data, addr=addr) assert factory.log.debug.called - assert 'Received invalid timestamp in package' in factory.log.debug.call_args[0][0] + assert ( + "Received invalid timestamp in package" + in factory.log.debug.call_args[0][0] + ) assert not factory._sendto.called def test_datagram_signature_wrong_timestamp_reply(self): - ''' + """ Test datagram receives a wrong timestamp. :return: - ''' + """ factory = self.get_ssdp_factory() factory.disable_hidden = True signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] - data = '{}nonsense'.format(signature) - addr = '10.10.10.10', 'foo.suse.de' - with patch.object(factory, 'log', MagicMock()), patch.object(factory, '_sendto', MagicMock()): + data = "{}nonsense".format(signature) + addr = "10.10.10.10", "foo.suse.de" + with patch.object(factory, "log", MagicMock()), patch.object( + factory, "_sendto", MagicMock() + ): factory.datagram_received(data=data, addr=addr) assert factory.log.debug.called - assert 'Received invalid timestamp in package' in factory.log.debug.call_args[0][0] + assert ( + "Received invalid timestamp in package" + in factory.log.debug.call_args[0][0] + ) assert factory._sendto.called - assert '{}:E:Invalid timestamp'.format(signature) == factory._sendto.call_args[0][0] + assert ( + "{}:E:Invalid timestamp".format(signature) + == factory._sendto.call_args[0][0] + ) def test_datagram_signature_outdated_timestamp_quiet(self): - ''' + """ Test if datagram processing reacts on outdated message (more than 20 seconds). Quiet mode. :return: - ''' + """ factory = self.get_ssdp_factory() signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] - data = '{}{}'.format(signature, '1516623820') - addr = '10.10.10.10', 'foo.suse.de' + data = "{}{}".format(signature, "1516623820") + addr = "10.10.10.10", "foo.suse.de" ahead_dt = datetime.datetime.fromtimestamp(1516623841) curnt_dt = datetime.datetime.fromtimestamp(1516623820) delta = datetime.timedelta(0, 20) - with patch.object(factory, 'log', MagicMock()), patch.object(factory, '_sendto'), \ - patch('salt.utils.ssdp.datetime.datetime', MagicMock()), \ - patch('salt.utils.ssdp.datetime.datetime.now', MagicMock(return_value=ahead_dt)), \ - patch('salt.utils.ssdp.datetime.datetime.fromtimestamp', MagicMock(return_value=curnt_dt)), \ - patch('salt.utils.ssdp.datetime.timedelta', MagicMock(return_value=delta)): + with patch.object(factory, "log", MagicMock()), patch.object( + factory, "_sendto" + ), patch("salt.utils.ssdp.datetime.datetime", MagicMock()), patch( + "salt.utils.ssdp.datetime.datetime.now", MagicMock(return_value=ahead_dt) + ), patch( + "salt.utils.ssdp.datetime.datetime.fromtimestamp", + MagicMock(return_value=curnt_dt), + ), patch( + "salt.utils.ssdp.datetime.timedelta", MagicMock(return_value=delta) + ): factory.datagram_received(data=data, addr=addr) assert factory.log.debug.called assert not factory.disable_hidden assert not factory._sendto.called - assert 'Received outdated package' in factory.log.debug.call_args[0][0] + assert "Received outdated package" in factory.log.debug.call_args[0][0] def test_datagram_signature_outdated_timestamp_reply(self): - ''' + """ Test if datagram processing reacts on outdated message (more than 20 seconds). Reply mode. :return: - ''' + """ factory = self.get_ssdp_factory() factory.disable_hidden = True signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] - data = '{}{}'.format(signature, '1516623820') - addr = '10.10.10.10', 'foo.suse.de' + data = "{}{}".format(signature, "1516623820") + addr = "10.10.10.10", "foo.suse.de" ahead_dt = datetime.datetime.fromtimestamp(1516623841) curnt_dt = datetime.datetime.fromtimestamp(1516623820) delta = datetime.timedelta(0, 20) - with patch.object(factory, 'log', MagicMock()), patch.object(factory, '_sendto'), \ - patch('salt.utils.ssdp.datetime.datetime', MagicMock()), \ - patch('salt.utils.ssdp.datetime.datetime.now', MagicMock(return_value=ahead_dt)), \ - patch('salt.utils.ssdp.datetime.datetime.fromtimestamp', MagicMock(return_value=curnt_dt)), \ - patch('salt.utils.ssdp.datetime.timedelta', MagicMock(return_value=delta)): + with patch.object(factory, "log", MagicMock()), patch.object( + factory, "_sendto" + ), patch("salt.utils.ssdp.datetime.datetime", MagicMock()), patch( + "salt.utils.ssdp.datetime.datetime.now", MagicMock(return_value=ahead_dt) + ), patch( + "salt.utils.ssdp.datetime.datetime.fromtimestamp", + MagicMock(return_value=curnt_dt), + ), patch( + "salt.utils.ssdp.datetime.timedelta", MagicMock(return_value=delta) + ): factory.datagram_received(data=data, addr=addr) assert factory.log.debug.called assert factory.disable_hidden assert factory._sendto.called - assert factory._sendto.call_args[0][0] == '{}:E:Timestamp is too old'.format(signature) - assert 'Received outdated package' in factory.log.debug.call_args[0][0] + assert factory._sendto.call_args[0][ + 0 + ] == "{}:E:Timestamp is too old".format(signature) + assert "Received outdated package" in factory.log.debug.call_args[0][0] def test_datagram_signature_correct_timestamp_reply(self): - ''' + """ Test if datagram processing sends out correct reply within 20 seconds. :return: - ''' + """ factory = self.get_ssdp_factory() factory.disable_hidden = True signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] - data = '{}{}'.format(signature, '1516623820') - addr = '10.10.10.10', 'foo.suse.de' + data = "{}{}".format(signature, "1516623820") + addr = "10.10.10.10", "foo.suse.de" ahead_dt = datetime.datetime.fromtimestamp(1516623840) curnt_dt = datetime.datetime.fromtimestamp(1516623820) delta = datetime.timedelta(0, 20) - with patch.object(factory, 'log', MagicMock()), patch.object(factory, '_sendto'), \ - patch('salt.utils.ssdp.datetime.datetime', MagicMock()), \ - patch('salt.utils.ssdp.datetime.datetime.now', MagicMock(return_value=ahead_dt)), \ - patch('salt.utils.ssdp.datetime.datetime.fromtimestamp', MagicMock(return_value=curnt_dt)), \ - patch('salt.utils.ssdp.datetime.timedelta', MagicMock(return_value=delta)): + with patch.object(factory, "log", MagicMock()), patch.object( + factory, "_sendto" + ), patch("salt.utils.ssdp.datetime.datetime", MagicMock()), patch( + "salt.utils.ssdp.datetime.datetime.now", MagicMock(return_value=ahead_dt) + ), patch( + "salt.utils.ssdp.datetime.datetime.fromtimestamp", + MagicMock(return_value=curnt_dt), + ), patch( + "salt.utils.ssdp.datetime.timedelta", MagicMock(return_value=delta) + ): factory.datagram_received(data=data, addr=addr) assert factory.log.debug.called assert factory.disable_hidden assert factory._sendto.called - assert factory._sendto.call_args[0][0] == salt.utils.stringutils.to_bytes("{}:@:{{}}".format(signature)) + assert factory._sendto.call_args[0][0] == salt.utils.stringutils.to_bytes( + "{}:@:{{}}".format(signature) + ) assert 'Received "%s" from %s:%s' in factory.log.debug.call_args[0][0] -@skipIf(pytest is None, 'PyTest is missing') +@skipIf(pytest is None, "PyTest is missing") class SSDPServerTestCase(TestCase, Mocks): - ''' + """ Server-related test cases - ''' + """ + def test_config_detached(self): - ''' + """ Test if configuration is not a reference. :return: - ''' - old_ip = '10.10.10.10' - new_ip = '20.20.20.20' - config = {'answer': {'master': old_ip}} - with patch('salt.utils.ssdp.SSDPDiscoveryServer.get_self_ip', MagicMock(return_value=new_ip)): + """ + old_ip = "10.10.10.10" + new_ip = "20.20.20.20" + config = {"answer": {"master": old_ip}} + with patch( + "salt.utils.ssdp.SSDPDiscoveryServer.get_self_ip", + MagicMock(return_value=new_ip), + ): srv = ssdp.SSDPDiscoveryServer(**config) - assert srv._config['answer']['master'] == new_ip - assert config['answer']['master'] == old_ip + assert srv._config["answer"]["master"] == new_ip + assert config["answer"]["master"] == old_ip def test_run(self): - ''' + """ Test server runner. :return: - ''' - with patch('salt.utils.ssdp.SSDPFactory', MagicMock()): - config = {'answer': {'master': '10.10.10.10'}, - ssdp.SSDPBase.LISTEN_IP: '10.10.10.10', - ssdp.SSDPBase.PORT: 12345} + """ + with patch("salt.utils.ssdp.SSDPFactory", MagicMock()): + config = { + "answer": {"master": "10.10.10.10"}, + ssdp.SSDPBase.LISTEN_IP: "10.10.10.10", + ssdp.SSDPBase.PORT: 12345, + } srv = self.get_ssdp_discovery_server(**config) srv.create_datagram_endpoint = MagicMock() srv.log = MagicMock() @@ -376,10 +427,10 @@ class SSDPServerTestCase(TestCase, Mocks): io.ported = False io.get_event_loop = MagicMock(return_value=loop) - with patch('salt.utils.ssdp.asyncio', io): + with patch("salt.utils.ssdp.asyncio", io): srv.run() cde_args = io.get_event_loop().create_datagram_endpoint.call_args[1] - cfg_ip_addr, cfg_port = cde_args['local_addr'] + cfg_ip_addr, cfg_port = cde_args["local_addr"] assert io.get_event_loop.called assert io.get_event_loop().run_until_complete.called @@ -388,118 +439,139 @@ class SSDPServerTestCase(TestCase, Mocks): assert trnsp.close.called assert loop.close.called assert srv.log.info.called - assert srv.log.info.call_args[0][0] == 'Stopping service discovery listener.' - assert 'allow_broadcast' in cde_args - assert cde_args['allow_broadcast'] - assert 'local_addr' in cde_args - assert not cfg_ip_addr == ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.LISTEN_IP] and cfg_ip_addr == '10.10.10.10' - assert not cfg_port == ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.PORT] and cfg_port == 12345 + assert ( + srv.log.info.call_args[0][0] + == "Stopping service discovery listener." + ) + assert "allow_broadcast" in cde_args + assert cde_args["allow_broadcast"] + assert "local_addr" in cde_args + assert ( + not cfg_ip_addr == ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.LISTEN_IP] + and cfg_ip_addr == "10.10.10.10" + ) + assert ( + not cfg_port == ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.PORT] + and cfg_port == 12345 + ) -@skipIf(pytest is None, 'PyTest is missing') +@skipIf(pytest is None, "PyTest is missing") class SSDPClientTestCase(TestCase, Mocks): - ''' + """ Client-related test cases - ''' + """ class Resource(object): - ''' + """ Fake network reader - ''' + """ def __init__(self): - self.pool = [('some', '10.10.10.10'), - ('data', '20.20.20.20'), - ('data', '10.10.10.10'), - (None, None)] + self.pool = [ + ("some", "10.10.10.10"), + ("data", "20.20.20.20"), + ("data", "10.10.10.10"), + (None, None), + ] def read(self, *args, **kwargs): return self.pool.pop(0) def test_config_passed(self): - ''' + """ Test if the configuration is passed. :return: - ''' - config = {ssdp.SSDPBase.SIGNATURE: 'SUSE Enterprise Server', - ssdp.SSDPBase.TIMEOUT: 5, ssdp.SSDPBase.PORT: 12345} + """ + config = { + ssdp.SSDPBase.SIGNATURE: "SUSE Enterprise Server", + ssdp.SSDPBase.TIMEOUT: 5, + ssdp.SSDPBase.PORT: 12345, + } clnt = self.get_ssdp_discovery_client(**config) assert clnt._config[ssdp.SSDPBase.SIGNATURE] == config[ssdp.SSDPBase.SIGNATURE] assert clnt._config[ssdp.SSDPBase.PORT] == config[ssdp.SSDPBase.PORT] assert clnt._config[ssdp.SSDPBase.TIMEOUT] == config[ssdp.SSDPBase.TIMEOUT] def test_config_detached(self): - ''' + """ Test if the passed configuration is not a reference. :return: - ''' - config = {ssdp.SSDPBase.SIGNATURE: 'SUSE Enterprise Server', } + """ + config = { + ssdp.SSDPBase.SIGNATURE: "SUSE Enterprise Server", + } clnt = self.get_ssdp_discovery_client(**config) - clnt._config['foo'] = 'bar' - assert 'foo' in clnt._config - assert 'foo' not in config + clnt._config["foo"] = "bar" + assert "foo" in clnt._config + assert "foo" not in config def test_query(self): - ''' + """ Test if client queries the broadcast :return: - ''' - config = {ssdp.SSDPBase.SIGNATURE: 'SUSE Enterprise Server', - ssdp.SSDPBase.PORT: 4000} + """ + config = { + ssdp.SSDPBase.SIGNATURE: "SUSE Enterprise Server", + ssdp.SSDPBase.PORT: 4000, + } f_time = 1111 _socket = MagicMock() - with patch('salt.utils.ssdp.socket', _socket),\ - patch('salt.utils.ssdp.time.time', MagicMock(return_value=f_time)): + with patch("salt.utils.ssdp.socket", _socket), patch( + "salt.utils.ssdp.time.time", MagicMock(return_value=f_time) + ): clnt = ssdp.SSDPDiscoveryClient(**config) clnt._query() assert clnt._socket.sendto.called message, target = clnt._socket.sendto.call_args[0] assert message == salt.utils.stringutils.to_bytes( - '{}{}'.format(config[ssdp.SSDPBase.SIGNATURE], f_time) + "{}{}".format(config[ssdp.SSDPBase.SIGNATURE], f_time) ) - assert target[0] == '<broadcast>' + assert target[0] == "<broadcast>" assert target[1] == config[ssdp.SSDPBase.PORT] def test_get_masters_map(self): - ''' + """ Test getting map of the available masters on the network :return: - ''' + """ _socket = MagicMock() response = {} - with patch('salt.utils.ssdp.socket', _socket): + with patch("salt.utils.ssdp.socket", _socket): clnt = ssdp.SSDPDiscoveryClient() clnt._socket.recvfrom = SSDPClientTestCase.Resource().read clnt.log = MagicMock() clnt._collect_masters_map(response=response) - assert '10.10.10.10' in response - assert '20.20.20.20' in response - assert response['10.10.10.10'] == ['some', 'data'] - assert response['20.20.20.20'] == ['data'] + assert "10.10.10.10" in response + assert "20.20.20.20" in response + assert response["10.10.10.10"] == ["some", "data"] + assert response["20.20.20.20"] == ["data"] def test_get_masters_map_error_handling(self): - ''' + """ Test getting map handles timeout network exception :return: - ''' + """ _socket = MagicMock() response = {} - error_msg = 'fake testing timeout just had happened' - with patch('salt.utils.ssdp.socket', _socket): + error_msg = "fake testing timeout just had happened" + with patch("salt.utils.ssdp.socket", _socket): clnt = ssdp.SSDPDiscoveryClient() clnt._socket.recvfrom = MagicMock(side_effect=Exception(error_msg)) clnt.log = MagicMock() clnt._collect_masters_map(response=response) assert clnt.log.error.called - assert 'Discovery master collection failure' in clnt.log.error.call_args[0][0] + assert ( + "Discovery master collection failure" in clnt.log.error.call_args[0][0] + ) assert error_msg == six.text_type(clnt.log.error.call_args[0][1]) assert not response def test_discover_no_masters(self): - ''' + """ Test discover available master on the network (none found). :return: - ''' + """ clnt = self.get_ssdp_discovery_client() clnt._query = MagicMock() @@ -508,55 +580,71 @@ class SSDPClientTestCase(TestCase, Mocks): clnt.discover() assert clnt.log.info.called - assert clnt.log.info.call_args[0][0] == 'No master has been discovered.' + assert clnt.log.info.call_args[0][0] == "No master has been discovered." def test_discover_general_error(self): - ''' + """ Test discover available master on the network (erroneous found) :return: - ''' + """ _socket = MagicMock() - error = 'Admins on strike due to broken coffee machine' + error = "Admins on strike due to broken coffee machine" signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] fake_resource = SSDPClientTestCase.Resource() - fake_resource.pool = [('{}:E:{}'.format(signature, error), '10.10.10.10'), - (None, None)] + fake_resource.pool = [ + ("{}:E:{}".format(signature, error), "10.10.10.10"), + (None, None), + ] - with patch('salt.utils.ssdp.socket', _socket): + with patch("salt.utils.ssdp.socket", _socket): clnt = ssdp.SSDPDiscoveryClient() clnt._socket.recvfrom = fake_resource.read clnt._query = MagicMock() clnt.log = MagicMock() clnt.discover() assert len(clnt.log.error.mock_calls) == 1 - assert 'Error response from the service publisher' in clnt.log.error.call_args[0][0] - assert '10.10.10.10' == clnt.log.error.call_args[0][1] + assert ( + "Error response from the service publisher" + in clnt.log.error.call_args[0][0] + ) + assert "10.10.10.10" == clnt.log.error.call_args[0][1] assert clnt.log.error.call_args[1] == {} assert clnt.log.error.call_args[0][2] == error def test_discover_timestamp_error(self): - ''' + """ Test discover available master on the network (outdated timestamp) :return: - ''' + """ _socket = MagicMock() - error = 'We only support a 1200 bps connection. Routing timestamp problems on neural net.' + error = "We only support a 1200 bps connection. Routing timestamp problems on neural net." signature = ssdp.SSDPBase.DEFAULTS[ssdp.SSDPBase.SIGNATURE] fake_resource = SSDPClientTestCase.Resource() - fake_resource.pool = [('{}:E:{}'.format(signature, error), '10.10.10.10'), - (None, None)] + fake_resource.pool = [ + ("{}:E:{}".format(signature, error), "10.10.10.10"), + (None, None), + ] - with patch('salt.utils.ssdp.socket', _socket): + with patch("salt.utils.ssdp.socket", _socket): clnt = ssdp.SSDPDiscoveryClient() clnt._socket.recvfrom = fake_resource.read clnt._query = MagicMock() clnt.log = MagicMock() clnt.discover() assert len(clnt.log.error.mock_calls) == 2 - assert 'Error response from the service publisher' in clnt.log.error.mock_calls[0][1][0] + assert ( + "Error response from the service publisher" + in clnt.log.error.mock_calls[0][1][0] + ) assert clnt.log.error.mock_calls[0][1][2] == error assert clnt.log.error.mock_calls[0][2] == {} - assert 'Publisher sent shifted timestamp' in clnt.log.error.mock_calls[1][1][0] - assert clnt.log.error.mock_calls[1][1][1] == clnt.log.error.mock_calls[0][1][1] == '10.10.10.10' + assert ( + "Publisher sent shifted timestamp" in clnt.log.error.mock_calls[1][1][0] + ) + assert ( + clnt.log.error.mock_calls[1][1][1] + == clnt.log.error.mock_calls[0][1][1] + == "10.10.10.10" + ) diff --git a/tests/unit/utils/test_state.py b/tests/unit/utils/test_state.py index 0f356c59e72..3fa309bd675 100644 --- a/tests/unit/utils/test_state.py +++ b/tests/unit/utils/test_state.py @@ -1,467 +1,781 @@ # -*- coding: utf-8 -*- -''' +""" Unit Tests for functions located in salt.utils.state.py. -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import copy import textwrap -# Import Salt libs -from salt.ext import six import salt.utils.odict import salt.utils.state +# Import Salt libs +from salt.ext import six + # Import Salt Testing libs from tests.support.unit import TestCase class StateUtilTestCase(TestCase): - ''' + """ Test case for state util. - ''' + """ + def test_check_result(self): - self.assertFalse(salt.utils.state.check_result(None), - 'Failed to handle None as an invalid data type.') - self.assertFalse(salt.utils.state.check_result([]), - 'Failed to handle an invalid data type.') - self.assertFalse(salt.utils.state.check_result({}), - 'Failed to handle an empty dictionary.') - self.assertFalse(salt.utils.state.check_result({'host1': []}), - 'Failed to handle an invalid host data structure.') - test_valid_state = {'host1': {'test_state': {'result': 'We have liftoff!'}}} + self.assertFalse( + salt.utils.state.check_result(None), + "Failed to handle None as an invalid data type.", + ) + self.assertFalse( + salt.utils.state.check_result([]), "Failed to handle an invalid data type." + ) + self.assertFalse( + salt.utils.state.check_result({}), "Failed to handle an empty dictionary." + ) + self.assertFalse( + salt.utils.state.check_result({"host1": []}), + "Failed to handle an invalid host data structure.", + ) + test_valid_state = {"host1": {"test_state": {"result": "We have liftoff!"}}} self.assertTrue(salt.utils.state.check_result(test_valid_state)) test_valid_false_states = { - 'test1': salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': False}), - ])), - ]), - 'test2': salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': True}), - ])), - ('host2', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': False}), - ])), - ]), - 'test3': ['a'], - 'test4': salt.utils.odict.OrderedDict([ - ('asup', salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': True}), - ])), - ('host2', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': False}), - ])) - ])) - ]), - 'test5': salt.utils.odict.OrderedDict([ - ('asup', salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': True}), - ])), - ('host2', salt.utils.odict.OrderedDict([])) - ])) - ]) + "test1": salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": False}), + ] + ), + ), + ] + ), + "test2": salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": True}), + ] + ), + ), + ( + "host2", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": False}), + ] + ), + ), + ] + ), + "test3": ["a"], + "test4": salt.utils.odict.OrderedDict( + [ + ( + "asup", + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": True}), + ] + ), + ), + ( + "host2", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": False}), + ] + ), + ), + ] + ), + ) + ] + ), + "test5": salt.utils.odict.OrderedDict( + [ + ( + "asup", + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": True}), + ] + ), + ), + ("host2", salt.utils.odict.OrderedDict([])), + ] + ), + ) + ] + ), } for test, data in six.iteritems(test_valid_false_states): self.assertFalse( - salt.utils.state.check_result(data), - msg='{0} failed'.format(test)) + salt.utils.state.check_result(data), msg="{0} failed".format(test) + ) test_valid_true_states = { - 'test1': salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': True}), - ])), - ]), - 'test3': salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': True}), - ])), - ('host2', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': True}), - ])), - ]), - 'test4': salt.utils.odict.OrderedDict([ - ('asup', salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': True}), - ])), - ('host2', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': True}), - ])) - ])) - ]), - 'test2': salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': None}), - ('test_state', {'result': True}), - ])), - ('host2', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': True}), - ('test_state', {'result': 'abc'}), - ])) - ]) + "test1": salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": True}), + ] + ), + ), + ] + ), + "test3": salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": True}), + ] + ), + ), + ( + "host2", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": True}), + ] + ), + ), + ] + ), + "test4": salt.utils.odict.OrderedDict( + [ + ( + "asup", + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": True}), + ] + ), + ), + ( + "host2", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": True}), + ] + ), + ), + ] + ), + ) + ] + ), + "test2": salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": None}), + ("test_state", {"result": True}), + ] + ), + ), + ( + "host2", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": True}), + ("test_state", {"result": "abc"}), + ] + ), + ), + ] + ), } for test, data in six.iteritems(test_valid_true_states): self.assertTrue( - salt.utils.state.check_result(data), - msg='{0} failed'.format(test)) - test_invalid_true_ht_states = { - 'test_onfail_simple2': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_vstate0', {'result': False}), - ('test_vstate1', {'result': True}), - ])), - ]), - { - 'test_vstate0': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}]}, - 'test_vstate1': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - salt.utils.odict.OrderedDict([ - ('onfail_stop', True), - ('onfail', - [salt.utils.odict.OrderedDict([('cmd', 'test_vstate0')])]) - ]), - 'run', - {'order': 10004}]}, - } - ), - 'test_onfail_integ2': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('t_|-test_ivstate0_|-echo_|-run', { - 'result': False}), - ('cmd_|-test_ivstate0_|-echo_|-run', { - 'result': False}), - ('cmd_|-test_ivstate1_|-echo_|-run', { - 'result': False}), - ])), - ]), - { - 'test_ivstate0': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}], - 't': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}]}, - 'test_ivstate1': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - salt.utils.odict.OrderedDict([ - ('onfail_stop', False), - ('onfail', - [salt.utils.odict.OrderedDict([('cmd', 'test_ivstate0')])]) - ]), - 'run', - {'order': 10004}]}, - } - ), - 'test_onfail_integ3': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('t_|-test_ivstate0_|-echo_|-run', { - 'result': True}), - ('cmd_|-test_ivstate0_|-echo_|-run', { - 'result': False}), - ('cmd_|-test_ivstate1_|-echo_|-run', { - 'result': False}), - ])), - ]), - { - 'test_ivstate0': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}], - 't': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}]}, - 'test_ivstate1': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - salt.utils.odict.OrderedDict([ - ('onfail_stop', False), - ('onfail', - [salt.utils.odict.OrderedDict([('cmd', 'test_ivstate0')])]) - ]), - 'run', - {'order': 10004}]}, - } - ), - 'test_onfail_integ4': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('t_|-test_ivstate0_|-echo_|-run', { - 'result': False}), - ('cmd_|-test_ivstate0_|-echo_|-run', { - 'result': False}), - ('cmd_|-test_ivstate1_|-echo_|-run', { - 'result': True}), - ])), - ]), - { - 'test_ivstate0': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}], - 't': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}]}, - 'test_ivstate1': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - salt.utils.odict.OrderedDict([ - ('onfail_stop', False), - ('onfail', - [salt.utils.odict.OrderedDict([('cmd', 'test_ivstate0')])]) - ]), - 'run', - {'order': 10004}]}, - 'test_ivstate2': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - salt.utils.odict.OrderedDict([ - ('onfail_stop', True), - ('onfail', - [salt.utils.odict.OrderedDict([('cmd', 'test_ivstate0')])]) - ]), - 'run', - {'order': 10004}]}, - } - ), - 'test_onfail': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': False}), - ('test_state', {'result': True}), - ])), - ]), - None - ), - 'test_onfail_d': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_state0', {'result': False}), - ('test_state', {'result': True}), - ])), - ]), - {} + salt.utils.state.check_result(data), msg="{0} failed".format(test) ) + test_invalid_true_ht_states = { + "test_onfail_simple2": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_vstate0", {"result": False}), + ("test_vstate1", {"result": True}), + ] + ), + ), + ] + ), + { + "test_vstate0": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + }, + "test_vstate1": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + salt.utils.odict.OrderedDict( + [ + ("onfail_stop", True), + ( + "onfail", + [ + salt.utils.odict.OrderedDict( + [("cmd", "test_vstate0")] + ) + ], + ), + ] + ), + "run", + {"order": 10004}, + ], + }, + }, + ), + "test_onfail_integ2": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ( + "t_|-test_ivstate0_|-echo_|-run", + {"result": False}, + ), + ( + "cmd_|-test_ivstate0_|-echo_|-run", + {"result": False}, + ), + ( + "cmd_|-test_ivstate1_|-echo_|-run", + {"result": False}, + ), + ] + ), + ), + ] + ), + { + "test_ivstate0": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + "t": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + }, + "test_ivstate1": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + salt.utils.odict.OrderedDict( + [ + ("onfail_stop", False), + ( + "onfail", + [ + salt.utils.odict.OrderedDict( + [("cmd", "test_ivstate0")] + ) + ], + ), + ] + ), + "run", + {"order": 10004}, + ], + }, + }, + ), + "test_onfail_integ3": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ( + "t_|-test_ivstate0_|-echo_|-run", + {"result": True}, + ), + ( + "cmd_|-test_ivstate0_|-echo_|-run", + {"result": False}, + ), + ( + "cmd_|-test_ivstate1_|-echo_|-run", + {"result": False}, + ), + ] + ), + ), + ] + ), + { + "test_ivstate0": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + "t": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + }, + "test_ivstate1": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + salt.utils.odict.OrderedDict( + [ + ("onfail_stop", False), + ( + "onfail", + [ + salt.utils.odict.OrderedDict( + [("cmd", "test_ivstate0")] + ) + ], + ), + ] + ), + "run", + {"order": 10004}, + ], + }, + }, + ), + "test_onfail_integ4": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ( + "t_|-test_ivstate0_|-echo_|-run", + {"result": False}, + ), + ( + "cmd_|-test_ivstate0_|-echo_|-run", + {"result": False}, + ), + ( + "cmd_|-test_ivstate1_|-echo_|-run", + {"result": True}, + ), + ] + ), + ), + ] + ), + { + "test_ivstate0": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + "t": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + }, + "test_ivstate1": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + salt.utils.odict.OrderedDict( + [ + ("onfail_stop", False), + ( + "onfail", + [ + salt.utils.odict.OrderedDict( + [("cmd", "test_ivstate0")] + ) + ], + ), + ] + ), + "run", + {"order": 10004}, + ], + }, + "test_ivstate2": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + salt.utils.odict.OrderedDict( + [ + ("onfail_stop", True), + ( + "onfail", + [ + salt.utils.odict.OrderedDict( + [("cmd", "test_ivstate0")] + ) + ], + ), + ] + ), + "run", + {"order": 10004}, + ], + }, + }, + ), + "test_onfail": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": False}), + ("test_state", {"result": True}), + ] + ), + ), + ] + ), + None, + ), + "test_onfail_d": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_state0", {"result": False}), + ("test_state", {"result": True}), + ] + ), + ), + ] + ), + {}, + ), } for test, testdata in six.iteritems(test_invalid_true_ht_states): data, ht = testdata - for t_ in [a for a in data['host1']]: - tdata = data['host1'][t_] - if '_|-' in t_: - t_ = t_.split('_|-')[1] - tdata['__id__'] = t_ + for t_ in [a for a in data["host1"]]: + tdata = data["host1"][t_] + if "_|-" in t_: + t_ = t_.split("_|-")[1] + tdata["__id__"] = t_ self.assertFalse( salt.utils.state.check_result(data, highstate=ht), - msg='{0} failed'.format(test)) + msg="{0} failed".format(test), + ) test_valid_true_ht_states = { - 'test_onfail_integ': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('cmd_|-test_ivstate0_|-echo_|-run', { - 'result': False}), - ('cmd_|-test_ivstate1_|-echo_|-run', { - 'result': True}), - ])), - ]), + "test_onfail_integ": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ( + "cmd_|-test_ivstate0_|-echo_|-run", + {"result": False}, + ), + ( + "cmd_|-test_ivstate1_|-echo_|-run", + {"result": True}, + ), + ] + ), + ), + ] + ), { - 'test_ivstate0': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}]}, - 'test_ivstate1': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - salt.utils.odict.OrderedDict([ - ('onfail_stop', False), - ('onfail', - [salt.utils.odict.OrderedDict([('cmd', 'test_ivstate0')])]) - ]), - 'run', - {'order': 10004}]}, - } + "test_ivstate0": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + }, + "test_ivstate1": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + salt.utils.odict.OrderedDict( + [ + ("onfail_stop", False), + ( + "onfail", + [ + salt.utils.odict.OrderedDict( + [("cmd", "test_ivstate0")] + ) + ], + ), + ] + ), + "run", + {"order": 10004}, + ], + }, + }, ), - 'test_onfail_intega3': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('t_|-test_ivstate0_|-echo_|-run', { - 'result': True}), - ('cmd_|-test_ivstate0_|-echo_|-run', { - 'result': False}), - ('cmd_|-test_ivstate1_|-echo_|-run', { - 'result': True}), - ])), - ]), + "test_onfail_intega3": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ( + "t_|-test_ivstate0_|-echo_|-run", + {"result": True}, + ), + ( + "cmd_|-test_ivstate0_|-echo_|-run", + {"result": False}, + ), + ( + "cmd_|-test_ivstate1_|-echo_|-run", + {"result": True}, + ), + ] + ), + ), + ] + ), { - 'test_ivstate0': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}], - 't': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}]}, - 'test_ivstate1': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - salt.utils.odict.OrderedDict([ - ('onfail_stop', False), - ('onfail', - [salt.utils.odict.OrderedDict([('cmd', 'test_ivstate0')])]) - ]), - 'run', - {'order': 10004}]}, - } + "test_ivstate0": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + "t": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + }, + "test_ivstate1": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + salt.utils.odict.OrderedDict( + [ + ("onfail_stop", False), + ( + "onfail", + [ + salt.utils.odict.OrderedDict( + [("cmd", "test_ivstate0")] + ) + ], + ), + ] + ), + "run", + {"order": 10004}, + ], + }, + }, ), - 'test_onfail_simple': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_vstate0', {'result': False}), - ('test_vstate1', {'result': True}), - ])), - ]), + "test_onfail_simple": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_vstate0", {"result": False}), + ("test_vstate1", {"result": True}), + ] + ), + ), + ] + ), { - 'test_vstate0': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}]}, - 'test_vstate1': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - salt.utils.odict.OrderedDict([ - ('onfail_stop', False), - ('onfail', - [salt.utils.odict.OrderedDict([('cmd', 'test_vstate0')])]) - ]), - 'run', - {'order': 10004}]}, - } + "test_vstate0": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + }, + "test_vstate1": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + salt.utils.odict.OrderedDict( + [ + ("onfail_stop", False), + ( + "onfail", + [ + salt.utils.odict.OrderedDict( + [("cmd", "test_vstate0")] + ) + ], + ), + ] + ), + "run", + {"order": 10004}, + ], + }, + }, ), # order is different - 'test_onfail_simple_rev': ( - salt.utils.odict.OrderedDict([ - ('host1', - salt.utils.odict.OrderedDict([ - ('test_vstate0', {'result': False}), - ('test_vstate1', {'result': True}), - ])), - ]), + "test_onfail_simple_rev": ( + salt.utils.odict.OrderedDict( + [ + ( + "host1", + salt.utils.odict.OrderedDict( + [ + ("test_vstate0", {"result": False}), + ("test_vstate1", {"result": True}), + ] + ), + ), + ] + ), { - 'test_vstate0': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - 'run', - {'order': 10002}]}, - 'test_vstate1': { - '__env__': 'base', - '__sls__': 'a', - 'cmd': [salt.utils.odict.OrderedDict([('name', '/bin/true')]), - salt.utils.odict.OrderedDict([ - ('onfail', - [salt.utils.odict.OrderedDict([('cmd', 'test_vstate0')])]) - ]), - salt.utils.odict.OrderedDict([('onfail_stop', False)]), - 'run', - {'order': 10004}]}, - } - ) + "test_vstate0": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + "run", + {"order": 10002}, + ], + }, + "test_vstate1": { + "__env__": "base", + "__sls__": "a", + "cmd": [ + salt.utils.odict.OrderedDict([("name", "/bin/true")]), + salt.utils.odict.OrderedDict( + [ + ( + "onfail", + [ + salt.utils.odict.OrderedDict( + [("cmd", "test_vstate0")] + ) + ], + ) + ] + ), + salt.utils.odict.OrderedDict([("onfail_stop", False)]), + "run", + {"order": 10004}, + ], + }, + }, + ), } for test, testdata in six.iteritems(test_valid_true_ht_states): data, ht = testdata - for t_ in [a for a in data['host1']]: - tdata = data['host1'][t_] - if '_|-' in t_: - t_ = t_.split('_|-')[1] - tdata['__id__'] = t_ + for t_ in [a for a in data["host1"]]: + tdata = data["host1"][t_] + if "_|-" in t_: + t_ = t_.split("_|-")[1] + tdata["__id__"] = t_ self.assertTrue( salt.utils.state.check_result(data, highstate=ht), - msg='{0} failed'.format(test)) - test_valid_false_state = {'host1': {'test_state': {'result': False}}} + msg="{0} failed".format(test), + ) + test_valid_false_state = {"host1": {"test_state": {"result": False}}} self.assertFalse(salt.utils.state.check_result(test_valid_false_state)) class UtilStateMergeSubreturnTestcase(TestCase): - ''' + """ Test cases for salt.utils.state.merge_subreturn function. - ''' + """ + main_ret = { - 'name': 'primary', + "name": "primary", # result may be missing, as primarysalt.utils.state is still in progress - 'comment': '', - 'changes': {}, + "comment": "", + "changes": {}, } sub_ret = { - 'name': 'secondary', - 'result': True, - 'comment': '', - 'changes': {}, + "name": "secondary", + "result": True, + "comment": "", + "changes": {}, } def test_merge_result(self): @@ -469,124 +783,121 @@ class UtilStateMergeSubreturnTestcase(TestCase): for no_effect_result in [True, None]: m = copy.deepcopy(self.main_ret) s = copy.deepcopy(self.sub_ret) - s['result'] = no_effect_result + s["result"] = no_effect_result res = salt.utils.state.merge_subreturn(m, s) - self.assertNotIn('result', res) + self.assertNotIn("result", res) # False subresult is propagated to existing result for original_result in [True, None, False]: m = copy.deepcopy(self.main_ret) - m['result'] = original_result + m["result"] = original_result s = copy.deepcopy(self.sub_ret) - s['result'] = False + s["result"] = False res = salt.utils.state.merge_subreturn(m, s) - self.assertFalse(res['result']) + self.assertFalse(res["result"]) # False result cannot be overridden for any_result in [True, None, False]: m = copy.deepcopy(self.main_ret) - m['result'] = False + m["result"] = False s = copy.deepcopy(self.sub_ret) - s['result'] = any_result + s["result"] = any_result res = salt.utils.state.merge_subreturn(m, s) - self.assertFalse(res['result']) + self.assertFalse(res["result"]) def test_merge_changes(self): # The main changes dict should always already exist, # and there should always be a changes dict in the secondary. - primary_changes = {'old': None, 'new': 'my_resource'} - secondary_changes = {'old': None, 'new': ['alarm-1', 'alarm-2']} + primary_changes = {"old": None, "new": "my_resource"} + secondary_changes = {"old": None, "new": ["alarm-1", "alarm-2"]} # No changes case m = copy.deepcopy(self.main_ret) s = copy.deepcopy(self.sub_ret) res = salt.utils.state.merge_subreturn(m, s) - self.assertDictEqual(res['changes'], {}) + self.assertDictEqual(res["changes"], {}) # New changes don't get rid of existing changes m = copy.deepcopy(self.main_ret) - m['changes'] = copy.deepcopy(primary_changes) + m["changes"] = copy.deepcopy(primary_changes) s = copy.deepcopy(self.sub_ret) - s['changes'] = copy.deepcopy(secondary_changes) + s["changes"] = copy.deepcopy(secondary_changes) res = salt.utils.state.merge_subreturn(m, s) - self.assertDictEqual(res['changes'], { - 'old': None, - 'new': 'my_resource', - 'secondary': secondary_changes, - }) + self.assertDictEqual( + res["changes"], + {"old": None, "new": "my_resource", "secondary": secondary_changes}, + ) # The subkey parameter is respected m = copy.deepcopy(self.main_ret) - m['changes'] = copy.deepcopy(primary_changes) + m["changes"] = copy.deepcopy(primary_changes) s = copy.deepcopy(self.sub_ret) - s['changes'] = copy.deepcopy(secondary_changes) - res = salt.utils.state.merge_subreturn(m, s, subkey='alarms') - self.assertDictEqual(res['changes'], { - 'old': None, - 'new': 'my_resource', - 'alarms': secondary_changes, - }) + s["changes"] = copy.deepcopy(secondary_changes) + res = salt.utils.state.merge_subreturn(m, s, subkey="alarms") + self.assertDictEqual( + res["changes"], + {"old": None, "new": "my_resource", "alarms": secondary_changes}, + ) def test_merge_comments(self): - main_comment_1 = 'First primary comment.' - main_comment_2 = 'Second primary comment.' - sub_comment_1 = 'First secondary comment,\nwhich spans two lines.' - sub_comment_2 = 'Second secondary comment: {0}'.format( - 'some error\n And a traceback', + main_comment_1 = "First primary comment." + main_comment_2 = "Second primary comment." + sub_comment_1 = "First secondary comment,\nwhich spans two lines." + sub_comment_2 = "Second secondary comment: {0}".format( + "some error\n And a traceback", ) - final_comment = textwrap.dedent('''\ + final_comment = textwrap.dedent( + """\ First primary comment. Second primary comment. First secondary comment, which spans two lines. Second secondary comment: some error And a traceback - '''.rstrip()) + """.rstrip() + ) # Joining two strings m = copy.deepcopy(self.main_ret) - m['comment'] = main_comment_1 + '\n' + main_comment_2 + m["comment"] = main_comment_1 + "\n" + main_comment_2 s = copy.deepcopy(self.sub_ret) - s['comment'] = sub_comment_1 + '\n' + sub_comment_2 + s["comment"] = sub_comment_1 + "\n" + sub_comment_2 res = salt.utils.state.merge_subreturn(m, s) - self.assertMultiLineEqual(res['comment'], final_comment) + self.assertMultiLineEqual(res["comment"], final_comment) # Joining string and a list m = copy.deepcopy(self.main_ret) - m['comment'] = main_comment_1 + '\n' + main_comment_2 + m["comment"] = main_comment_1 + "\n" + main_comment_2 s = copy.deepcopy(self.sub_ret) - s['comment'] = [sub_comment_1, sub_comment_2] + s["comment"] = [sub_comment_1, sub_comment_2] res = salt.utils.state.merge_subreturn(m, s) - self.assertMultiLineEqual(res['comment'], final_comment) + self.assertMultiLineEqual(res["comment"], final_comment) # For tests where output is a list, # also test that final joined output will match # Joining list and a string m = copy.deepcopy(self.main_ret) - m['comment'] = [main_comment_1, main_comment_2] + m["comment"] = [main_comment_1, main_comment_2] s = copy.deepcopy(self.sub_ret) - s['comment'] = sub_comment_1 + '\n' + sub_comment_2 + s["comment"] = sub_comment_1 + "\n" + sub_comment_2 res = salt.utils.state.merge_subreturn(m, s) - self.assertEqual(res['comment'], [ - main_comment_1, - main_comment_2, - sub_comment_1 + '\n' + sub_comment_2, - ]) - self.assertMultiLineEqual('\n'.join(res['comment']), final_comment) + self.assertEqual( + res["comment"], + [main_comment_1, main_comment_2, sub_comment_1 + "\n" + sub_comment_2], + ) + self.assertMultiLineEqual("\n".join(res["comment"]), final_comment) # Joining two lists m = copy.deepcopy(self.main_ret) - m['comment'] = [main_comment_1, main_comment_2] + m["comment"] = [main_comment_1, main_comment_2] s = copy.deepcopy(self.sub_ret) - s['comment'] = [sub_comment_1, sub_comment_2] + s["comment"] = [sub_comment_1, sub_comment_2] res = salt.utils.state.merge_subreturn(m, s) - self.assertEqual(res['comment'], [ - main_comment_1, - main_comment_2, - sub_comment_1, - sub_comment_2, - ]) - self.assertMultiLineEqual('\n'.join(res['comment']), final_comment) + self.assertEqual( + res["comment"], + [main_comment_1, main_comment_2, sub_comment_1, sub_comment_2], + ) + self.assertMultiLineEqual("\n".join(res["comment"]), final_comment) def test_merge_empty_comments(self): # Since the primarysalt.utils.state is in progress, @@ -596,43 +907,43 @@ class UtilStateMergeSubreturnTestcase(TestCase): # The secondary comment should never be empty, # because thatsalt.utils.state has already returned, # so we leave the behavior unspecified in that case. - sub_comment_1 = 'Secondary comment about changes:' - sub_comment_2 = 'A diff that goes with the previous comment' + sub_comment_1 = "Secondary comment about changes:" + sub_comment_2 = "A diff that goes with the previous comment" # No contributions from primary - final_comment = sub_comment_1 + '\n' + sub_comment_2 + final_comment = sub_comment_1 + "\n" + sub_comment_2 # Joining empty string and a string m = copy.deepcopy(self.main_ret) - m['comment'] = '' + m["comment"] = "" s = copy.deepcopy(self.sub_ret) - s['comment'] = sub_comment_1 + '\n' + sub_comment_2 + s["comment"] = sub_comment_1 + "\n" + sub_comment_2 res = salt.utils.state.merge_subreturn(m, s) - self.assertEqual(res['comment'], final_comment) + self.assertEqual(res["comment"], final_comment) # Joining empty string and a list m = copy.deepcopy(self.main_ret) - m['comment'] = '' + m["comment"] = "" s = copy.deepcopy(self.sub_ret) - s['comment'] = [sub_comment_1, sub_comment_2] + s["comment"] = [sub_comment_1, sub_comment_2] res = salt.utils.state.merge_subreturn(m, s) - self.assertEqual(res['comment'], final_comment) + self.assertEqual(res["comment"], final_comment) # For tests where output is a list, # also test that final joined output will match # Joining empty list and a string m = copy.deepcopy(self.main_ret) - m['comment'] = [] + m["comment"] = [] s = copy.deepcopy(self.sub_ret) - s['comment'] = sub_comment_1 + '\n' + sub_comment_2 + s["comment"] = sub_comment_1 + "\n" + sub_comment_2 res = salt.utils.state.merge_subreturn(m, s) - self.assertEqual(res['comment'], [final_comment]) - self.assertEqual('\n'.join(res['comment']), final_comment) + self.assertEqual(res["comment"], [final_comment]) + self.assertEqual("\n".join(res["comment"]), final_comment) # Joining empty list and a list m = copy.deepcopy(self.main_ret) - m['comment'] = [] + m["comment"] = [] s = copy.deepcopy(self.sub_ret) - s['comment'] = [sub_comment_1, sub_comment_2] + s["comment"] = [sub_comment_1, sub_comment_2] res = salt.utils.state.merge_subreturn(m, s) - self.assertEqual(res['comment'], [sub_comment_1, sub_comment_2]) - self.assertEqual('\n'.join(res['comment']), final_comment) + self.assertEqual(res["comment"], [sub_comment_1, sub_comment_2]) + self.assertEqual("\n".join(res["comment"]), final_comment) diff --git a/tests/unit/utils/test_stringutils.py b/tests/unit/utils/test_stringutils.py index 65ebda65594..3eea25cfa87 100644 --- a/tests/unit/utils/test_stringutils.py +++ b/tests/unit/utils/test_stringutils.py @@ -1,42 +1,44 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals + import re import sys import textwrap -# Import Salt libs -from tests.support.mock import patch -from tests.support.unit import TestCase, LOREM_IPSUM import salt.utils.stringutils # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import builtins, range # pylint: disable=redefined-builtin -UNICODE = '中国語 (繁体)' -STR = BYTES = UNICODE.encode('utf-8') +# Import Salt libs +from tests.support.mock import patch +from tests.support.unit import LOREM_IPSUM, TestCase + +UNICODE = "中国語 (繁体)" +STR = BYTES = UNICODE.encode("utf-8") # This is an example of a unicode string with й constructed using two separate # code points. Do not modify it. -EGGS = '\u044f\u0438\u0306\u0446\u0430' +EGGS = "\u044f\u0438\u0306\u0446\u0430" -LATIN1_UNICODE = 'räksmörgås' -LATIN1_BYTES = LATIN1_UNICODE.encode('latin-1') +LATIN1_UNICODE = "räksmörgås" +LATIN1_BYTES = LATIN1_UNICODE.encode("latin-1") -DOUBLE_TXT = '''\ +DOUBLE_TXT = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi -''' +""" -SINGLE_TXT = '''\ +SINGLE_TXT = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi -''' +""" -SINGLE_DOUBLE_TXT = '''\ +SINGLE_DOUBLE_TXT = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) @@ -46,16 +48,16 @@ fi if [ -z "$debian_chroot" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi -''' +""" -SINGLE_DOUBLE_SAME_LINE_TXT = '''\ +SINGLE_DOUBLE_SAME_LINE_TXT = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r "/etc/debian_chroot" ]; then debian_chroot=$(cat /etc/debian_chroot) fi -''' +""" -MATCH = '''\ +MATCH = """\ # set variable identifying the chroot you work in (used in the prompt below) if [ -z '$debian_chroot' ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) @@ -84,11 +86,10 @@ fi if [ -z '$debian_chroot' ] && [ -r "/etc/debian_chroot" ]; then debian_chroot=$(cat /etc/debian_chroot) fi -''' +""" class TestBuildWhitespaceRegex(TestCase): - def test_single_quotes(self): regex = salt.utils.stringutils.build_whitespace_split_regex(SINGLE_TXT) self.assertTrue(re.search(regex, MATCH)) @@ -102,31 +103,37 @@ class TestBuildWhitespaceRegex(TestCase): self.assertTrue(re.search(regex, MATCH)) def test_issue_2227(self): - regex = salt.utils.stringutils.build_whitespace_split_regex(SINGLE_DOUBLE_SAME_LINE_TXT) + regex = salt.utils.stringutils.build_whitespace_split_regex( + SINGLE_DOUBLE_SAME_LINE_TXT + ) self.assertTrue(re.search(regex, MATCH)) class StringutilsTestCase(TestCase): def test_contains_whitespace(self): - does_contain_whitespace = 'A brown fox jumped over the red hen.' - does_not_contain_whitespace = 'Abrownfoxjumpedovertheredhen.' + does_contain_whitespace = "A brown fox jumped over the red hen." + does_not_contain_whitespace = "Abrownfoxjumpedovertheredhen." - self.assertTrue(salt.utils.stringutils.contains_whitespace(does_contain_whitespace)) - self.assertFalse(salt.utils.stringutils.contains_whitespace(does_not_contain_whitespace)) + self.assertTrue( + salt.utils.stringutils.contains_whitespace(does_contain_whitespace) + ) + self.assertFalse( + salt.utils.stringutils.contains_whitespace(does_not_contain_whitespace) + ) def test_to_num(self): - self.assertEqual(7, salt.utils.stringutils.to_num('7')) - self.assertIsInstance(salt.utils.stringutils.to_num('7'), int) - self.assertEqual(7, salt.utils.stringutils.to_num('7.0')) - self.assertIsInstance(salt.utils.stringutils.to_num('7.0'), float) - self.assertEqual(salt.utils.stringutils.to_num('Seven'), 'Seven') - self.assertIsInstance(salt.utils.stringutils.to_num('Seven'), six.text_type) + self.assertEqual(7, salt.utils.stringutils.to_num("7")) + self.assertIsInstance(salt.utils.stringutils.to_num("7"), int) + self.assertEqual(7, salt.utils.stringutils.to_num("7.0")) + self.assertIsInstance(salt.utils.stringutils.to_num("7.0"), float) + self.assertEqual(salt.utils.stringutils.to_num("Seven"), "Seven") + self.assertIsInstance(salt.utils.stringutils.to_num("Seven"), six.text_type) def test_to_none(self): - self.assertIsNone(salt.utils.stringutils.to_none('')) - self.assertIsNone(salt.utils.stringutils.to_none(' ')) + self.assertIsNone(salt.utils.stringutils.to_none("")) + self.assertIsNone(salt.utils.stringutils.to_none(" ")) # Ensure that we do not inadvertently convert certain strings or 0 to None - self.assertIsNotNone(salt.utils.stringutils.to_none('None')) + self.assertIsNotNone(salt.utils.stringutils.to_none("None")) self.assertIsNotNone(salt.utils.stringutils.to_none(0)) def test_is_binary(self): @@ -138,21 +145,21 @@ class StringutilsTestCase(TestCase): ) ) - zero_str = '{0}{1}'.format(LOREM_IPSUM, '\0') + zero_str = "{0}{1}".format(LOREM_IPSUM, "\0") self.assertTrue(salt.utils.stringutils.is_binary(zero_str)) # Also test bytestring self.assertTrue( - salt.utils.stringutils.is_binary( - salt.utils.stringutils.to_bytes(zero_str) - ) + salt.utils.stringutils.is_binary(salt.utils.stringutils.to_bytes(zero_str)) ) # To to ensure safe exit if str passed doesn't evaluate to True - self.assertFalse(salt.utils.stringutils.is_binary('')) - self.assertFalse(salt.utils.stringutils.is_binary(b'')) + self.assertFalse(salt.utils.stringutils.is_binary("")) + self.assertFalse(salt.utils.stringutils.is_binary(b"")) - nontext = 3 * (''.join([chr(x) for x in range(1, 32) if x not in (8, 9, 10, 12, 13)])) - almost_bin_str = '{0}{1}'.format(LOREM_IPSUM[:100], nontext[:42]) + nontext = 3 * ( + "".join([chr(x) for x in range(1, 32) if x not in (8, 9, 10, 12, 13)]) + ) + almost_bin_str = "{0}{1}".format(LOREM_IPSUM[:100], nontext[:42]) self.assertFalse(salt.utils.stringutils.is_binary(almost_bin_str)) # Also test bytestring self.assertFalse( @@ -161,98 +168,119 @@ class StringutilsTestCase(TestCase): ) ) - bin_str = almost_bin_str + '\x01' + bin_str = almost_bin_str + "\x01" self.assertTrue(salt.utils.stringutils.is_binary(bin_str)) # Also test bytestring self.assertTrue( - salt.utils.stringutils.is_binary( - salt.utils.stringutils.to_bytes(bin_str) - ) + salt.utils.stringutils.is_binary(salt.utils.stringutils.to_bytes(bin_str)) ) def test_to_str(self): for x in (123, (1, 2, 3), [1, 2, 3], {1: 23}, None): self.assertRaises(TypeError, salt.utils.stringutils.to_str, x) if six.PY3: - self.assertEqual(salt.utils.stringutils.to_str('plugh'), 'plugh') - self.assertEqual(salt.utils.stringutils.to_str('áéíóúý', 'utf-8'), 'áéíóúý') - self.assertEqual(salt.utils.stringutils.to_str(BYTES, 'utf-8'), UNICODE) - self.assertEqual(salt.utils.stringutils.to_str(bytearray(BYTES), 'utf-8'), UNICODE) + self.assertEqual(salt.utils.stringutils.to_str("plugh"), "plugh") + self.assertEqual(salt.utils.stringutils.to_str("áéíóúý", "utf-8"), "áéíóúý") + self.assertEqual(salt.utils.stringutils.to_str(BYTES, "utf-8"), UNICODE) + self.assertEqual( + salt.utils.stringutils.to_str(bytearray(BYTES), "utf-8"), UNICODE + ) # Test situation when a minion returns incorrect utf-8 string because of... million reasons - ut2 = b'\x9c' - self.assertRaises(UnicodeDecodeError, salt.utils.stringutils.to_str, ut2, 'utf-8') - self.assertEqual(salt.utils.stringutils.to_str(ut2, 'utf-8', 'replace'), u'\ufffd') - self.assertRaises(UnicodeDecodeError, salt.utils.stringutils.to_str, bytearray(ut2), 'utf-8') - self.assertEqual(salt.utils.stringutils.to_str(bytearray(ut2), 'utf-8', 'replace'), u'\ufffd') + ut2 = b"\x9c" + self.assertRaises( + UnicodeDecodeError, salt.utils.stringutils.to_str, ut2, "utf-8" + ) + self.assertEqual( + salt.utils.stringutils.to_str(ut2, "utf-8", "replace"), "\ufffd" + ) + self.assertRaises( + UnicodeDecodeError, + salt.utils.stringutils.to_str, + bytearray(ut2), + "utf-8", + ) + self.assertEqual( + salt.utils.stringutils.to_str(bytearray(ut2), "utf-8", "replace"), + "\ufffd", + ) else: - self.assertEqual(salt.utils.stringutils.to_str('plugh'), str('plugh')) # future lint: disable=blacklisted-function - self.assertEqual(salt.utils.stringutils.to_str('áéíóúý', 'utf-8'), 'áéíóúý'.encode('utf-8')) - self.assertEqual(salt.utils.stringutils.to_str(UNICODE, 'utf-8'), STR) - self.assertEqual(salt.utils.stringutils.to_str(bytearray(STR), 'utf-8'), STR) + self.assertEqual( + salt.utils.stringutils.to_str("plugh"), str("plugh") + ) # future lint: disable=blacklisted-function + self.assertEqual( + salt.utils.stringutils.to_str("áéíóúý", "utf-8"), + "áéíóúý".encode("utf-8"), + ) + self.assertEqual(salt.utils.stringutils.to_str(UNICODE, "utf-8"), STR) + self.assertEqual( + salt.utils.stringutils.to_str(bytearray(STR), "utf-8"), STR + ) # Test utf-8 fallback with Windows default codepage - with patch.object(builtins, '__salt_system_encoding__', 'CP1252'): - self.assertEqual(salt.utils.stringutils.to_str('Ψ'), 'Ψ'.encode('utf-8')) + with patch.object(builtins, "__salt_system_encoding__", "CP1252"): + self.assertEqual( + salt.utils.stringutils.to_str("Ψ"), "Ψ".encode("utf-8") + ) def test_to_bytes(self): for x in (123, (1, 2, 3), [1, 2, 3], {1: 23}, None): self.assertRaises(TypeError, salt.utils.stringutils.to_bytes, x) if six.PY3: - self.assertEqual(salt.utils.stringutils.to_bytes('xyzzy'), b'xyzzy') + self.assertEqual(salt.utils.stringutils.to_bytes("xyzzy"), b"xyzzy") self.assertEqual(salt.utils.stringutils.to_bytes(BYTES), BYTES) self.assertEqual(salt.utils.stringutils.to_bytes(bytearray(BYTES)), BYTES) - self.assertEqual(salt.utils.stringutils.to_bytes(UNICODE, 'utf-8'), BYTES) + self.assertEqual(salt.utils.stringutils.to_bytes(UNICODE, "utf-8"), BYTES) # Test utf-8 fallback with ascii default encoding - with patch.object(builtins, '__salt_system_encoding__', 'ascii'): - self.assertEqual(salt.utils.stringutils.to_bytes('Ψ'), b'\xce\xa8') + with patch.object(builtins, "__salt_system_encoding__", "ascii"): + self.assertEqual(salt.utils.stringutils.to_bytes("Ψ"), b"\xce\xa8") else: - self.assertEqual(salt.utils.stringutils.to_bytes('xyzzy'), 'xyzzy') + self.assertEqual(salt.utils.stringutils.to_bytes("xyzzy"), "xyzzy") self.assertEqual(salt.utils.stringutils.to_bytes(BYTES), BYTES) self.assertEqual(salt.utils.stringutils.to_bytes(bytearray(BYTES)), BYTES) - self.assertEqual(salt.utils.stringutils.to_bytes(UNICODE, 'utf-8'), BYTES) + self.assertEqual(salt.utils.stringutils.to_bytes(UNICODE, "utf-8"), BYTES) def test_to_unicode(self): self.assertEqual( - salt.utils.stringutils.to_unicode( - EGGS, - normalize=True - ), - 'яйца' + salt.utils.stringutils.to_unicode(EGGS, normalize=True), "яйца" ) self.assertNotEqual( - salt.utils.stringutils.to_unicode( - EGGS, - normalize=False - ), - 'яйца' + salt.utils.stringutils.to_unicode(EGGS, normalize=False), "яйца" ) self.assertEqual( - salt.utils.stringutils.to_unicode( - LATIN1_BYTES, encoding='latin-1' - ), - LATIN1_UNICODE + salt.utils.stringutils.to_unicode(LATIN1_BYTES, encoding="latin-1"), + LATIN1_UNICODE, ) if six.PY3: - self.assertEqual(salt.utils.stringutils.to_unicode('plugh'), 'plugh') - self.assertEqual(salt.utils.stringutils.to_unicode('áéíóúý'), 'áéíóúý') - self.assertEqual(salt.utils.stringutils.to_unicode(BYTES, 'utf-8'), UNICODE) - self.assertEqual(salt.utils.stringutils.to_unicode(bytearray(BYTES), 'utf-8'), UNICODE) + self.assertEqual(salt.utils.stringutils.to_unicode("plugh"), "plugh") + self.assertEqual(salt.utils.stringutils.to_unicode("áéíóúý"), "áéíóúý") + self.assertEqual(salt.utils.stringutils.to_unicode(BYTES, "utf-8"), UNICODE) + self.assertEqual( + salt.utils.stringutils.to_unicode(bytearray(BYTES), "utf-8"), UNICODE + ) else: - self.assertEqual(salt.utils.stringutils.to_unicode(str('xyzzy'), 'utf-8'), 'xyzzy') # future lint: disable=blacklisted-function - self.assertEqual(salt.utils.stringutils.to_unicode(BYTES, 'utf-8'), UNICODE) + self.assertEqual( + salt.utils.stringutils.to_unicode(str("xyzzy"), "utf-8"), "xyzzy" + ) # future lint: disable=blacklisted-function + self.assertEqual(salt.utils.stringutils.to_unicode(BYTES, "utf-8"), UNICODE) # Test that unicode chars are decoded properly even when using # locales which are not UTF-8 compatible - with patch.object(builtins, '__salt_system_encoding__', 'ascii'): - self.assertEqual(salt.utils.stringutils.to_unicode('Ψ'.encode('utf-8')), 'Ψ') - with patch.object(builtins, '__salt_system_encoding__', 'CP1252'): - self.assertEqual(salt.utils.stringutils.to_unicode('Ψ'.encode('utf-8')), 'Ψ') + with patch.object(builtins, "__salt_system_encoding__", "ascii"): + self.assertEqual( + salt.utils.stringutils.to_unicode("Ψ".encode("utf-8")), "Ψ" + ) + with patch.object(builtins, "__salt_system_encoding__", "CP1252"): + self.assertEqual( + salt.utils.stringutils.to_unicode("Ψ".encode("utf-8")), "Ψ" + ) def test_to_unicode_multi_encoding(self): - result = salt.utils.stringutils.to_unicode(LATIN1_BYTES, encoding=('utf-8', 'latin1')) + result = salt.utils.stringutils.to_unicode( + LATIN1_BYTES, encoding=("utf-8", "latin1") + ) assert result == LATIN1_UNICODE def test_build_whitespace_split_regex(self): @@ -260,287 +288,237 @@ class StringutilsTestCase(TestCase): # escaping all characters other than ASCII letters, numbers and # underscores. This includes commas. if sys.version_info >= (3, 7): - expected_regex = '(?m)^(?:[\\s]+)?Lorem(?:[\\s]+)?ipsum(?:[\\s]+)?dolor(?:[\\s]+)?sit(?:[\\s]+)?amet,' \ - '(?:[\\s]+)?$' + expected_regex = ( + "(?m)^(?:[\\s]+)?Lorem(?:[\\s]+)?ipsum(?:[\\s]+)?dolor(?:[\\s]+)?sit(?:[\\s]+)?amet," + "(?:[\\s]+)?$" + ) else: - expected_regex = '(?m)^(?:[\\s]+)?Lorem(?:[\\s]+)?ipsum(?:[\\s]+)?dolor(?:[\\s]+)?sit(?:[\\s]+)?amet\\,' \ - '(?:[\\s]+)?$' - ret = salt.utils.stringutils.build_whitespace_split_regex(' '.join(LOREM_IPSUM.split()[:5])) + expected_regex = ( + "(?m)^(?:[\\s]+)?Lorem(?:[\\s]+)?ipsum(?:[\\s]+)?dolor(?:[\\s]+)?sit(?:[\\s]+)?amet\\," + "(?:[\\s]+)?$" + ) + ret = salt.utils.stringutils.build_whitespace_split_regex( + " ".join(LOREM_IPSUM.split()[:5]) + ) self.assertEqual(ret, expected_regex) def test_get_context(self): - expected_context = textwrap.dedent('''\ + expected_context = textwrap.dedent( + """\ --- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eget urna a arcu lacinia sagittis. Sed scelerisque, lacus eget malesuada vestibulum, justo diam facilisis tortor, in sodales dolor [...] - ---''') + ---""" + ) ret = salt.utils.stringutils.get_context(LOREM_IPSUM, 1, num_lines=1) self.assertEqual(ret, expected_context) def test_get_context_has_enough_context(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 8) - expected = '---\n[...]\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\n[...]\n---' + expected = "---\n[...]\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\n[...]\n---" self.assertEqual(expected, context) def test_get_context_at_top_of_file(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 1) - expected = '---\n1\n2\n3\n4\n5\n6\n[...]\n---' + expected = "---\n1\n2\n3\n4\n5\n6\n[...]\n---" self.assertEqual(expected, context) def test_get_context_at_bottom_of_file(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 15) - expected = '---\n[...]\na\nb\nc\nd\ne\nf\n---' + expected = "---\n[...]\na\nb\nc\nd\ne\nf\n---" self.assertEqual(expected, context) def test_get_context_2_context_lines(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" context = salt.utils.stringutils.get_context(template, 8, num_lines=2) - expected = '---\n[...]\n6\n7\n8\n9\na\n[...]\n---' + expected = "---\n[...]\n6\n7\n8\n9\na\n[...]\n---" self.assertEqual(expected, context) def test_get_context_with_marker(self): - template = '1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf' - context = salt.utils.stringutils.get_context(template, 8, num_lines=2, marker=' <---') - expected = '---\n[...]\n6\n7\n8 <---\n9\na\n[...]\n---' + template = "1\n2\n3\n4\n5\n6\n7\n8\n9\na\nb\nc\nd\ne\nf" + context = salt.utils.stringutils.get_context( + template, 8, num_lines=2, marker=" <---" + ) + expected = "---\n[...]\n6\n7\n8 <---\n9\na\n[...]\n---" self.assertEqual(expected, context) def test_expr_match(self): - val = 'foo/bar/baz' + val = "foo/bar/baz" # Exact match self.assertTrue(salt.utils.stringutils.expr_match(val, val)) # Glob match - self.assertTrue(salt.utils.stringutils.expr_match(val, 'foo/*/baz')) + self.assertTrue(salt.utils.stringutils.expr_match(val, "foo/*/baz")) # Glob non-match - self.assertFalse(salt.utils.stringutils.expr_match(val, 'foo/*/bar')) + self.assertFalse(salt.utils.stringutils.expr_match(val, "foo/*/bar")) # Regex match - self.assertTrue(salt.utils.stringutils.expr_match(val, r'foo/\w+/baz')) + self.assertTrue(salt.utils.stringutils.expr_match(val, r"foo/\w+/baz")) # Regex non-match - self.assertFalse(salt.utils.stringutils.expr_match(val, r'foo/\w/baz')) + self.assertFalse(salt.utils.stringutils.expr_match(val, r"foo/\w/baz")) def test_check_whitelist_blacklist(self): - ''' + """ Ensure that whitelist matching works on both PY2 and PY3 - ''' - whitelist = ['one/two/three', r'web[0-9]'] - blacklist = ['four/five/six', r'web[5-9]'] + """ + whitelist = ["one/two/three", r"web[0-9]"] + blacklist = ["four/five/six", r"web[5-9]"] # Tests with string whitelist/blacklist self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web_one', - whitelist=whitelist[1], - blacklist=None, + "web_one", whitelist=whitelist[1], blacklist=None, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web_one', - whitelist=whitelist[1], - blacklist=[], + "web_one", whitelist=whitelist[1], blacklist=[], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web1', - whitelist=whitelist[1], - blacklist=None, + "web1", whitelist=whitelist[1], blacklist=None, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web1', - whitelist=whitelist[1], - blacklist=[], + "web1", whitelist=whitelist[1], blacklist=[], ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web5', - whitelist=None, - blacklist=blacklist[1], + "web5", whitelist=None, blacklist=blacklist[1], ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web5', - whitelist=[], - blacklist=blacklist[1], + "web5", whitelist=[], blacklist=blacklist[1], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web_five', - whitelist=None, - blacklist=blacklist[1], + "web_five", whitelist=None, blacklist=blacklist[1], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web_five', - whitelist=[], - blacklist=blacklist[1], + "web_five", whitelist=[], blacklist=blacklist[1], ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web5', - whitelist=whitelist[1], - blacklist=blacklist[1], + "web5", whitelist=whitelist[1], blacklist=blacklist[1], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web4', - whitelist=whitelist[1], - blacklist=blacklist[1], + "web4", whitelist=whitelist[1], blacklist=blacklist[1], ) ) # Tests with list whitelist/blacklist self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web_one', - whitelist=whitelist, - blacklist=None, + "web_one", whitelist=whitelist, blacklist=None, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web_one', - whitelist=whitelist, - blacklist=[], + "web_one", whitelist=whitelist, blacklist=[], ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web1', - whitelist=whitelist, - blacklist=None, + "web1", whitelist=whitelist, blacklist=None, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web1', - whitelist=whitelist, - blacklist=[], + "web1", whitelist=whitelist, blacklist=[], ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web5', - whitelist=None, - blacklist=blacklist, + "web5", whitelist=None, blacklist=blacklist, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web5', - whitelist=[], - blacklist=blacklist, + "web5", whitelist=[], blacklist=blacklist, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web_five', - whitelist=None, - blacklist=blacklist, + "web_five", whitelist=None, blacklist=blacklist, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web_five', - whitelist=[], - blacklist=blacklist, + "web_five", whitelist=[], blacklist=blacklist, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web5', - whitelist=whitelist, - blacklist=blacklist, + "web5", whitelist=whitelist, blacklist=blacklist, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web4', - whitelist=whitelist, - blacklist=blacklist, + "web4", whitelist=whitelist, blacklist=blacklist, ) ) # Tests with set whitelist/blacklist self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web_one', - whitelist=set(whitelist), - blacklist=None, + "web_one", whitelist=set(whitelist), blacklist=None, ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web_one', - whitelist=set(whitelist), - blacklist=set(), + "web_one", whitelist=set(whitelist), blacklist=set(), ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web1', - whitelist=set(whitelist), - blacklist=None, + "web1", whitelist=set(whitelist), blacklist=None, ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web1', - whitelist=set(whitelist), - blacklist=set(), + "web1", whitelist=set(whitelist), blacklist=set(), ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web5', - whitelist=None, - blacklist=set(blacklist), + "web5", whitelist=None, blacklist=set(blacklist), ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web5', - whitelist=set(), - blacklist=set(blacklist), + "web5", whitelist=set(), blacklist=set(blacklist), ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web_five', - whitelist=None, - blacklist=set(blacklist), + "web_five", whitelist=None, blacklist=set(blacklist), ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web_five', - whitelist=set(), - blacklist=set(blacklist), + "web_five", whitelist=set(), blacklist=set(blacklist), ) ) self.assertFalse( salt.utils.stringutils.check_whitelist_blacklist( - 'web5', - whitelist=set(whitelist), - blacklist=set(blacklist), + "web5", whitelist=set(whitelist), blacklist=set(blacklist), ) ) self.assertTrue( salt.utils.stringutils.check_whitelist_blacklist( - 'web4', - whitelist=set(whitelist), - blacklist=set(blacklist), + "web4", whitelist=set(whitelist), blacklist=set(blacklist), ) ) @@ -548,10 +526,12 @@ class StringutilsTestCase(TestCase): self.assertRaises( TypeError, salt.utils.stringutils.check_whitelist_blacklist, - 'foo', whitelist=123 + "foo", + whitelist=123, ) self.assertRaises( TypeError, salt.utils.stringutils.check_whitelist_blacklist, - 'foo', blacklist=123 + "foo", + blacklist=123, ) diff --git a/tests/unit/utils/test_systemd.py b/tests/unit/utils/test_systemd.py index 9361e76cf77..1ed2324b5b7 100644 --- a/tests/unit/utils/test_systemd.py +++ b/tests/unit/utils/test_systemd.py @@ -2,94 +2,96 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import errno import os -# Import Salt Testing libs -from tests.support.unit import TestCase -from tests.support.mock import Mock, patch - # Import Salt libs import salt.utils.systemd as _systemd from salt.exceptions import SaltInvocationError +from tests.support.mock import Mock, patch + +# Import Salt Testing libs +from tests.support.unit import TestCase def _booted_effect(path): - return True if path == '/run/systemd/system' else os.stat(path) + return True if path == "/run/systemd/system" else os.stat(path) def _not_booted_effect(path): - if path == '/run/systemd/system': - raise OSError(errno.ENOENT, 'No such file or directory', path) + if path == "/run/systemd/system": + raise OSError(errno.ENOENT, "No such file or directory", path) return os.stat(path) class SystemdTestCase(TestCase): - ''' + """ Tests the functions in salt.utils.systemd - ''' + """ + def test_booted(self): - ''' + """ Test that salt.utils.systemd.booted() returns True when minion is systemd-booted. - ''' + """ # Ensure that os.stat returns True. os.stat doesn't return a bool # normally, but the code is doing a simple truth check on the return # data, so it is sufficient enough to mock it as True for these tests. - with patch('os.stat', side_effect=_booted_effect): + with patch("os.stat", side_effect=_booted_effect): # Test without context dict passed self.assertTrue(_systemd.booted()) # Test that context key is set when context dict is passed context = {} self.assertTrue(_systemd.booted(context)) - self.assertEqual(context, {'salt.utils.systemd.booted': True}) + self.assertEqual(context, {"salt.utils.systemd.booted": True}) def test_not_booted(self): - ''' + """ Test that salt.utils.systemd.booted() returns False when minion is not systemd-booted. - ''' + """ # Ensure that os.stat raises an exception even if test is being run on # a systemd-booted host. - with patch('os.stat', side_effect=_not_booted_effect): + with patch("os.stat", side_effect=_not_booted_effect): # Test without context dict passed self.assertFalse(_systemd.booted()) # Test that context key is set when context dict is passed context = {} self.assertFalse(_systemd.booted(context)) - self.assertEqual(context, {'salt.utils.systemd.booted': False}) + self.assertEqual(context, {"salt.utils.systemd.booted": False}) def test_booted_return_from_context(self): - ''' + """ Test that the context data is returned when present. To ensure we're getting data from the context dict, we use a non-boolean value to differentiate it from the True/False return this function normally produces. - ''' - context = {'salt.utils.systemd.booted': 'foo'} - self.assertEqual(_systemd.booted(context), 'foo') + """ + context = {"salt.utils.systemd.booted": "foo"} + self.assertEqual(_systemd.booted(context), "foo") def test_booted_invalid_context(self): - ''' + """ Test with invalid context data. The context value must be a dict, so this should raise a SaltInvocationError. - ''' + """ # Test with invalid context data with self.assertRaises(SaltInvocationError): _systemd.booted(99999) def test_version(self): - ''' + """ Test that salt.utils.systemd.booted() returns True when minion is systemd-booted. - ''' - with patch('subprocess.Popen') as popen_mock: + """ + with patch("subprocess.Popen") as popen_mock: _version = 231 - output = 'systemd {0}\n-SYSVINIT'.format(_version) + output = "systemd {0}\n-SYSVINIT".format(_version) popen_mock.return_value = Mock( communicate=lambda *args, **kwargs: (output, None), pid=lambda: 12345, - retcode=0 + retcode=0, ) # Test without context dict passed @@ -97,20 +99,20 @@ class SystemdTestCase(TestCase): # Test that context key is set when context dict is passed context = {} self.assertTrue(_systemd.version(context)) - self.assertEqual(context, {'salt.utils.systemd.version': _version}) + self.assertEqual(context, {"salt.utils.systemd.version": _version}) def test_version_generated_from_git_describe(self): - ''' + """ Test with version string matching versions generated by git describe in systemd. This feature is used in systemd>=241. - ''' - with patch('subprocess.Popen') as popen_mock: + """ + with patch("subprocess.Popen") as popen_mock: _version = 241 - output = 'systemd {0} ({0}.0-0-dist)\n-SYSVINIT'.format(_version) + output = "systemd {0} ({0}.0-0-dist)\n-SYSVINIT".format(_version) popen_mock.return_value = Mock( communicate=lambda *args, **kwargs: (output, None), pid=lambda: 12345, - retcode=0 + retcode=0, ) # Test without context dict passed @@ -118,37 +120,37 @@ class SystemdTestCase(TestCase): # Test that context key is set when context dict is passed context = {} self.assertTrue(_systemd.version(context)) - self.assertEqual(context, {'salt.utils.systemd.version': _version}) + self.assertEqual(context, {"salt.utils.systemd.version": _version}) def test_version_return_from_context(self): - ''' + """ Test that the context data is returned when present. To ensure we're getting data from the context dict, we use a non-integer value to differentiate it from the integer return this function normally produces. - ''' - context = {'salt.utils.systemd.version': 'foo'} - self.assertEqual(_systemd.version(context), 'foo') + """ + context = {"salt.utils.systemd.version": "foo"} + self.assertEqual(_systemd.version(context), "foo") def test_version_invalid_context(self): - ''' + """ Test with invalid context data. The context value must be a dict, so this should raise a SaltInvocationError. - ''' + """ # Test with invalid context data with self.assertRaises(SaltInvocationError): _systemd.version(99999) def test_version_parse_problem(self): - ''' + """ Test with invalid context data. The context value must be a dict, so this should raise a SaltInvocationError. - ''' - with patch('subprocess.Popen') as popen_mock: + """ + with patch("subprocess.Popen") as popen_mock: popen_mock.return_value = Mock( - communicate=lambda *args, **kwargs: ('invalid', None), + communicate=lambda *args, **kwargs: ("invalid", None), pid=lambda: 12345, - retcode=0 + retcode=0, ) # Test without context dict passed self.assertIsNone(_systemd.version()) @@ -160,26 +162,26 @@ class SystemdTestCase(TestCase): self.assertEqual(context, {}) def test_has_scope_systemd204(self): - ''' + """ Scopes are available in systemd>=205. Make sure that this function returns the expected boolean. We do three separate unit tests for versions 204 through 206 because mock doesn't like us altering the return_value in a loop. - ''' - with patch('subprocess.Popen') as popen_mock: + """ + with patch("subprocess.Popen") as popen_mock: _expected = False _version = 204 - _output = 'systemd {0}\n-SYSVINIT'.format(_version) + _output = "systemd {0}\n-SYSVINIT".format(_version) popen_mock.return_value = Mock( communicate=lambda *args, **kwargs: (_output, None), pid=lambda: 12345, - retcode=0 + retcode=0, ) # Ensure that os.stat returns True. os.stat doesn't return a bool # normally, but the code is doing a simple truth check on the # return data, so it is sufficient enough to mock it as True for # these tests. - with patch('os.stat', side_effect=_booted_effect): + with patch("os.stat", side_effect=_booted_effect): # Test without context dict passed self.assertEqual(_systemd.has_scope(), _expected) # Test that context key is set when context dict is passed @@ -187,31 +189,33 @@ class SystemdTestCase(TestCase): self.assertEqual(_systemd.has_scope(context), _expected) self.assertEqual( context, - {'salt.utils.systemd.booted': True, - 'salt.utils.systemd.version': _version}, + { + "salt.utils.systemd.booted": True, + "salt.utils.systemd.version": _version, + }, ) def test_has_scope_systemd205(self): - ''' + """ Scopes are available in systemd>=205. Make sure that this function returns the expected boolean. We do three separate unit tests for versions 204 through 206 because mock doesn't like us altering the return_value in a loop. - ''' - with patch('subprocess.Popen') as popen_mock: + """ + with patch("subprocess.Popen") as popen_mock: _expected = True _version = 205 - _output = 'systemd {0}\n-SYSVINIT'.format(_version) + _output = "systemd {0}\n-SYSVINIT".format(_version) popen_mock.return_value = Mock( communicate=lambda *args, **kwargs: (_output, None), pid=lambda: 12345, - retcode=0 + retcode=0, ) # Ensure that os.stat returns True. os.stat doesn't return a bool # normally, but the code is doing a simple truth check on the # return data, so it is sufficient enough to mock it as True for # these tests. - with patch('os.stat', side_effect=_booted_effect): + with patch("os.stat", side_effect=_booted_effect): # Test without context dict passed self.assertEqual(_systemd.has_scope(), _expected) # Test that context key is set when context dict is passed @@ -219,31 +223,33 @@ class SystemdTestCase(TestCase): self.assertEqual(_systemd.has_scope(context), _expected) self.assertEqual( context, - {'salt.utils.systemd.booted': True, - 'salt.utils.systemd.version': _version}, + { + "salt.utils.systemd.booted": True, + "salt.utils.systemd.version": _version, + }, ) def test_has_scope_systemd206(self): - ''' + """ Scopes are available in systemd>=205. Make sure that this function returns the expected boolean. We do three separate unit tests for versions 204 through 206 because mock doesn't like us altering the return_value in a loop. - ''' - with patch('subprocess.Popen') as popen_mock: + """ + with patch("subprocess.Popen") as popen_mock: _expected = True _version = 206 - _output = 'systemd {0}\n-SYSVINIT'.format(_version) + _output = "systemd {0}\n-SYSVINIT".format(_version) popen_mock.return_value = Mock( communicate=lambda *args, **kwargs: (_output, None), pid=lambda: 12345, - retcode=0 + retcode=0, ) # Ensure that os.stat returns True. os.stat doesn't return a bool # normally, but the code is doing a simple truth check on the # return data, so it is sufficient enough to mock it as True for # these tests. - with patch('os.stat', side_effect=_booted_effect): + with patch("os.stat", side_effect=_booted_effect): # Test without context dict passed self.assertEqual(_systemd.has_scope(), _expected) # Test that context key is set when context dict is passed @@ -251,16 +257,18 @@ class SystemdTestCase(TestCase): self.assertEqual(_systemd.has_scope(context), _expected) self.assertEqual( context, - {'salt.utils.systemd.booted': True, - 'salt.utils.systemd.version': _version}, + { + "salt.utils.systemd.booted": True, + "salt.utils.systemd.version": _version, + }, ) def test_has_scope_no_systemd(self): - ''' + """ Test the case where the system is not systemd-booted. We should not be performing a version check in these cases as there is no need. - ''' - with patch('os.stat', side_effect=_not_booted_effect): + """ + with patch("os.stat", side_effect=_not_booted_effect): # Test without context dict passed self.assertFalse(_systemd.has_scope()) # Test that context key is set when context dict is passed. @@ -269,20 +277,20 @@ class SystemdTestCase(TestCase): # performed this check. context = {} self.assertFalse(_systemd.has_scope(context)) - self.assertEqual(context, {'salt.utils.systemd.booted': False}) + self.assertEqual(context, {"salt.utils.systemd.booted": False}) def test_has_scope_version_parse_problem(self): - ''' + """ Test the case where the system is systemd-booted, but we failed to parse the "systemctl --version" output. - ''' - with patch('subprocess.Popen') as popen_mock: + """ + with patch("subprocess.Popen") as popen_mock: popen_mock.return_value = Mock( - communicate=lambda *args, **kwargs: ('invalid', None), + communicate=lambda *args, **kwargs: ("invalid", None), pid=lambda: 12345, - retcode=0 + retcode=0, ) - with patch('os.stat', side_effect=_booted_effect): + with patch("os.stat", side_effect=_booted_effect): # Test without context dict passed self.assertFalse(_systemd.has_scope()) # Test that context key is set when context dict is passed. A @@ -290,13 +298,13 @@ class SystemdTestCase(TestCase): # key, so it should not be present in the context dict. context = {} self.assertFalse(_systemd.has_scope(context)) - self.assertEqual(context, {'salt.utils.systemd.booted': True}) + self.assertEqual(context, {"salt.utils.systemd.booted": True}) def test_has_scope_invalid_context(self): - ''' + """ Test with invalid context data. The context value must be a dict, so this should raise a SaltInvocationError. - ''' + """ # Test with invalid context data with self.assertRaises(SaltInvocationError): _systemd.has_scope(99999) diff --git a/tests/unit/utils/test_templates.py b/tests/unit/utils/test_templates.py index b9d9eba24d5..bc1bd92bca6 100644 --- a/tests/unit/utils/test_templates.py +++ b/tests/unit/utils/test_templates.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for salt.utils.templates.py -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import logging import os import sys -import logging + +import salt.utils.files # Import Salt libs import salt.utils.templates -import salt.utils.files # Import Salt Testing Libs from tests.support.helpers import with_tempdir @@ -25,165 +27,174 @@ class RenderTestCase(TestCase): def setUp(self): # Default context for salt.utils.templates.render_*_tmpl to work self.context = { - 'opts': { - 'cachedir': '/D', - '__cli': 'salt', - }, - 'saltenv': None, + "opts": {"cachedir": "/D", "__cli": "salt"}, + "saltenv": None, } ### Tests for Jinja (whitespace-friendly) def test_render_jinja_sanity(self): - tmpl = '''OK''' + tmpl = """OK""" res = salt.utils.templates.render_jinja_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") def test_render_jinja_evaluate(self): - tmpl = '''{{ "OK" }}''' + tmpl = """{{ "OK" }}""" res = salt.utils.templates.render_jinja_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") def test_render_jinja_evaluate_multi(self): - tmpl = '''{% if 1 -%}OK{%- endif %}''' + tmpl = """{% if 1 -%}OK{%- endif %}""" res = salt.utils.templates.render_jinja_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") def test_render_jinja_variable(self): - tmpl = '''{{ var }}''' + tmpl = """{{ var }}""" ctx = dict(self.context) - ctx['var'] = 'OK' + ctx["var"] = "OK" res = salt.utils.templates.render_jinja_tmpl(tmpl, ctx) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") ### Tests for mako template def test_render_mako_sanity(self): - tmpl = '''OK''' + tmpl = """OK""" res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") def test_render_mako_evaluate(self): - tmpl = '''${ "OK" }''' + tmpl = """${ "OK" }""" res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") def test_render_mako_evaluate_multi(self): - tmpl = ''' + tmpl = """ % if 1: OK % endif - ''' + """ res = salt.utils.templates.render_mako_tmpl(tmpl, dict(self.context)) stripped = res.strip() - self.assertEqual(stripped, 'OK') + self.assertEqual(stripped, "OK") def test_render_mako_variable(self): - tmpl = '''${ var }''' + tmpl = """${ var }""" ctx = dict(self.context) - ctx['var'] = 'OK' + ctx["var"] = "OK" res = salt.utils.templates.render_mako_tmpl(tmpl, ctx) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") ### Tests for wempy template - @skipIf(sys.version_info > (3,), 'The wempy module is currently unsupported under Python3') + @skipIf( + sys.version_info > (3,), + "The wempy module is currently unsupported under Python3", + ) def test_render_wempy_sanity(self): - tmpl = '''OK''' + tmpl = """OK""" res = salt.utils.templates.render_wempy_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") - @skipIf(sys.version_info > (3,), 'The wempy module is currently unsupported under Python3') + @skipIf( + sys.version_info > (3,), + "The wempy module is currently unsupported under Python3", + ) def test_render_wempy_evaluate(self): - tmpl = '''{{="OK"}}''' + tmpl = """{{="OK"}}""" res = salt.utils.templates.render_wempy_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") - @skipIf(sys.version_info > (3,), 'The wempy module is currently unsupported under Python3') + @skipIf( + sys.version_info > (3,), + "The wempy module is currently unsupported under Python3", + ) def test_render_wempy_evaluate_multi(self): - tmpl = '''{{if 1:}}OK{{pass}}''' + tmpl = """{{if 1:}}OK{{pass}}""" res = salt.utils.templates.render_wempy_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") - @skipIf(sys.version_info > (3,), 'The wempy module is currently unsupported under Python3') + @skipIf( + sys.version_info > (3,), + "The wempy module is currently unsupported under Python3", + ) def test_render_wempy_variable(self): - tmpl = '''{{=var}}''' + tmpl = """{{=var}}""" ctx = dict(self.context) - ctx['var'] = 'OK' + ctx["var"] = "OK" res = salt.utils.templates.render_wempy_tmpl(tmpl, ctx) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") ### Tests for genshi template (xml-based) def test_render_genshi_sanity(self): - tmpl = '''<RU>OK</RU>''' + tmpl = """<RU>OK</RU>""" res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, '<RU>OK</RU>') + self.assertEqual(res, "<RU>OK</RU>") def test_render_genshi_evaluate(self): - tmpl = '''<RU>${ "OK" }</RU>''' + tmpl = """<RU>${ "OK" }</RU>""" res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, '<RU>OK</RU>') + self.assertEqual(res, "<RU>OK</RU>") def test_render_genshi_evaluate_condition(self): - tmpl = '''<RU xmlns:py="http://genshi.edgewall.org/" py:if="1">OK</RU>''' + tmpl = """<RU xmlns:py="http://genshi.edgewall.org/" py:if="1">OK</RU>""" res = salt.utils.templates.render_genshi_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, '<RU>OK</RU>') + self.assertEqual(res, "<RU>OK</RU>") def test_render_genshi_variable(self): - tmpl = '''<RU>$var</RU>''' + tmpl = """<RU>$var</RU>""" ctx = dict(self.context) - ctx['var'] = 'OK' + ctx["var"] = "OK" res = salt.utils.templates.render_genshi_tmpl(tmpl, ctx) - self.assertEqual(res, '<RU>OK</RU>') + self.assertEqual(res, "<RU>OK</RU>") def test_render_genshi_variable_replace(self): - tmpl = '''<RU xmlns:py="http://genshi.edgewall.org/" py:content="var">not ok</RU>''' + tmpl = """<RU xmlns:py="http://genshi.edgewall.org/" py:content="var">not ok</RU>""" ctx = dict(self.context) - ctx['var'] = 'OK' + ctx["var"] = "OK" res = salt.utils.templates.render_genshi_tmpl(tmpl, ctx) - self.assertEqual(res, '<RU>OK</RU>') + self.assertEqual(res, "<RU>OK</RU>") ### Tests for cheetah template (line-oriented and xml-friendly) def test_render_cheetah_sanity(self): - tmpl = '''OK''' + tmpl = """OK""" res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") def test_render_cheetah_evaluate(self): - tmpl = '''<%="OK"%>''' + tmpl = """<%="OK"%>""" res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context)) - self.assertEqual(res, 'OK') + self.assertEqual(res, "OK") def test_render_cheetah_evaluate_xml(self): - tmpl = ''' + tmpl = """ <% if 1: %> OK <% pass %> - ''' + """ res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context)) stripped = res.strip() - self.assertEqual(stripped, 'OK') + self.assertEqual(stripped, "OK") def test_render_cheetah_evaluate_text(self): - tmpl = ''' + tmpl = """ #if 1 OK #end if - ''' + """ res = salt.utils.templates.render_cheetah_tmpl(tmpl, dict(self.context)) stripped = res.strip() - self.assertEqual(stripped, 'OK') + self.assertEqual(stripped, "OK") def test_render_cheetah_variable(self): - tmpl = '''$var''' + tmpl = """$var""" ctx = dict(self.context) - ctx['var'] = 'OK' + ctx["var"] = "OK" res = salt.utils.templates.render_cheetah_tmpl(tmpl, ctx) - self.assertEqual(res.strip(), 'OK') + self.assertEqual(res.strip(), "OK") class MockRender(object): @@ -195,35 +206,26 @@ class MockRender(object): class WrapRenderTestCase(TestCase): - @with_tempdir() def test_wrap_issue_56119_a(self, tempdir): - slsfile = os.path.join(tempdir, 'foo') - with salt.utils.files.fopen(slsfile, 'w') as fp: - fp.write('{{ slspath }}') - context = {'opts': {}, 'saltenv': 'base', 'sls': 'foo.bar'} + slsfile = os.path.join(tempdir, "foo") + with salt.utils.files.fopen(slsfile, "w") as fp: + fp.write("{{ slspath }}") + context = {"opts": {}, "saltenv": "base", "sls": "foo.bar"} render = MockRender() wrapped = salt.utils.templates.wrap_tmpl_func(render) - res = wrapped( - slsfile, - context=context, - tmplpath='/tmp/foo/bar/init.sls' - ) - assert render.context['slspath'] == 'foo/bar', render.context['slspath'] - assert render.context['tpldir'] == 'foo/bar', render.context['tpldir'] + res = wrapped(slsfile, context=context, tmplpath="/tmp/foo/bar/init.sls") + assert render.context["slspath"] == "foo/bar", render.context["slspath"] + assert render.context["tpldir"] == "foo/bar", render.context["tpldir"] @with_tempdir() def test_wrap_issue_56119_b(self, tempdir): - slsfile = os.path.join(tempdir, 'foo') - with salt.utils.files.fopen(slsfile, 'w') as fp: - fp.write('{{ slspath }}') - context = {'opts': {}, 'saltenv': 'base', 'sls': 'foo.bar.bang'} + slsfile = os.path.join(tempdir, "foo") + with salt.utils.files.fopen(slsfile, "w") as fp: + fp.write("{{ slspath }}") + context = {"opts": {}, "saltenv": "base", "sls": "foo.bar.bang"} render = MockRender() wrapped = salt.utils.templates.wrap_tmpl_func(render) - res = wrapped( - slsfile, - context=context, - tmplpath='/tmp/foo/bar/bang.sls' - ) - assert render.context['slspath'] == 'foo/bar', render.context['slspath'] - assert render.context['tpldir'] == 'foo/bar', render.context['tpldir'] + res = wrapped(slsfile, context=context, tmplpath="/tmp/foo/bar/bang.sls") + assert render.context["slspath"] == "foo/bar", render.context["slspath"] + assert render.context["tpldir"] == "foo/bar", render.context["tpldir"] diff --git a/tests/unit/utils/test_thin.py b/tests/unit/utils/test_thin.py index 8e3eadec95b..e1fa27fc3f9 100644 --- a/tests/unit/utils/test_thin.py +++ b/tests/unit/utils/test_thin.py @@ -1,25 +1,23 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Bo Maryniuk <bo@suse.de>` -''' +""" from __future__ import absolute_import, print_function, unicode_literals import os import sys -from tests.support.unit import TestCase, skipIf -from tests.support.helpers import TstSuiteLoggingHandler -from tests.support.mock import ( - MagicMock, - patch) import salt.exceptions -import salt.utils.json -from salt.utils import thin -import salt.utils.stringutils -import salt.utils.platform -from salt.utils.stringutils import to_bytes as bts -from salt.ext.six.moves import range import salt.ext.six +import salt.utils.json +import salt.utils.platform +import salt.utils.stringutils +from salt.ext.six.moves import range +from salt.utils import thin +from salt.utils.stringutils import to_bytes as bts +from tests.support.helpers import TstSuiteLoggingHandler +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf try: import pytest @@ -27,17 +25,18 @@ except ImportError: pytest = None -@skipIf(pytest is None, 'PyTest is missing') +@skipIf(pytest is None, "PyTest is missing") class SSHThinTestCase(TestCase): - ''' + """ TestCase for SaltSSH-related parts. - ''' + """ + def _popen(self, return_value=None, side_effect=None, returncode=0): - ''' + """ Fake subprocess.Popen :return: - ''' + """ proc = MagicMock() proc.communicate = MagicMock(return_value=return_value, side_effect=side_effect) @@ -47,13 +46,14 @@ class SSHThinTestCase(TestCase): return popen def _version_info(self, major=None, minor=None): - ''' + """ Fake version info. :param major: :param minor: :return: - ''' + """ + class VersionInfo(tuple): pass @@ -64,641 +64,890 @@ class SSHThinTestCase(TestCase): return vi def _tarfile(self, getinfo=False): - ''' + """ Fake tarfile handler. :return: - ''' - spec = ['add', 'close'] + """ + spec = ["add", "close"] if getinfo: - spec.append('getinfo') + spec.append("getinfo") tf = MagicMock() tf.open = MagicMock(return_value=MagicMock(spec=spec)) return tf - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.path.isfile', MagicMock(return_value=False)) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.path.isfile", MagicMock(return_value=False)) def test_get_ext_tops_cfg_missing_dependencies(self): - ''' + """ Test thin.get_ext_tops contains all required dependencies. :return: - ''' - cfg = {'namespace': {'py-version': [0, 0], 'path': '/foo', 'dependencies': []}} + """ + cfg = {"namespace": {"py-version": [0, 0], "path": "/foo", "dependencies": []}} with pytest.raises(Exception) as err: thin.get_ext_tops(cfg) - self.assertIn('Missing dependencies', str(err.value)) + self.assertIn("Missing dependencies", str(err.value)) self.assertTrue(thin.log.error.called) - self.assertIn('Missing dependencies', thin.log.error.call_args[0][0]) - self.assertIn('jinja2, yaml, tornado, msgpack', - thin.log.error.call_args[0][0]) + self.assertIn("Missing dependencies", thin.log.error.call_args[0][0]) + self.assertIn("jinja2, yaml, tornado, msgpack", thin.log.error.call_args[0][0]) - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.path.isfile', MagicMock(return_value=False)) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.path.isfile", MagicMock(return_value=False)) def test_get_ext_tops_cfg_missing_interpreter(self): - ''' + """ Test thin.get_ext_tops contains interpreter configuration. :return: - ''' - cfg = {'namespace': {'path': '/foo', - 'dependencies': []}} + """ + cfg = {"namespace": {"path": "/foo", "dependencies": []}} with pytest.raises(salt.exceptions.SaltSystemExit) as err: thin.get_ext_tops(cfg) - self.assertIn('missing specific locked Python version', str(err.value)) + self.assertIn("missing specific locked Python version", str(err.value)) - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.path.isfile', MagicMock(return_value=False)) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.path.isfile", MagicMock(return_value=False)) def test_get_ext_tops_cfg_wrong_interpreter(self): - ''' + """ Test thin.get_ext_tops contains correct interpreter configuration. :return: - ''' - cfg = {'namespace': {'path': '/foo', - 'py-version': 2, - 'dependencies': []}} + """ + cfg = {"namespace": {"path": "/foo", "py-version": 2, "dependencies": []}} with pytest.raises(salt.exceptions.SaltSystemExit) as err: thin.get_ext_tops(cfg) - self.assertIn('specific locked Python version should be a list of ' - 'major/minor version', str(err.value)) + self.assertIn( + "specific locked Python version should be a list of " "major/minor version", + str(err.value), + ) - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.path.isfile', MagicMock(return_value=False)) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.path.isfile", MagicMock(return_value=False)) def test_get_ext_tops_cfg_interpreter(self): - ''' + """ Test thin.get_ext_tops interpreter configuration. :return: - ''' - cfg = {'namespace': {'path': '/foo', - 'py-version': [2, 6], - 'dependencies': {'jinja2': '', - 'yaml': '', - 'tornado': '', - 'msgpack': ''}}} + """ + cfg = { + "namespace": { + "path": "/foo", + "py-version": [2, 6], + "dependencies": { + "jinja2": "", + "yaml": "", + "tornado": "", + "msgpack": "", + }, + } + } with pytest.raises(salt.exceptions.SaltSystemExit): thin.get_ext_tops(cfg) assert len(thin.log.warning.mock_calls) == 4 - assert sorted([x[1][1] for x in thin.log.warning.mock_calls]) == ['jinja2', 'msgpack', 'tornado', 'yaml'] - assert 'Module test has missing configuration' == thin.log.warning.mock_calls[0][1][0] % 'test' + assert sorted([x[1][1] for x in thin.log.warning.mock_calls]) == [ + "jinja2", + "msgpack", + "tornado", + "yaml", + ] + assert ( + "Module test has missing configuration" + == thin.log.warning.mock_calls[0][1][0] % "test" + ) - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.path.isfile', MagicMock(return_value=False)) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.path.isfile", MagicMock(return_value=False)) def test_get_ext_tops_dependency_config_check(self): - ''' + """ Test thin.get_ext_tops dependencies are importable :return: - ''' - cfg = {'namespace': {'path': '/foo', - 'py-version': [2, 6], - 'dependencies': {'jinja2': '/jinja/foo.py', - 'yaml': '/yaml/', - 'tornado': '/tornado/wrong.rb', - 'msgpack': 'msgpack.sh'}}} + """ + cfg = { + "namespace": { + "path": "/foo", + "py-version": [2, 6], + "dependencies": { + "jinja2": "/jinja/foo.py", + "yaml": "/yaml/", + "tornado": "/tornado/wrong.rb", + "msgpack": "msgpack.sh", + }, + } + } with pytest.raises(salt.exceptions.SaltSystemExit) as err: thin.get_ext_tops(cfg) - self.assertIn('Missing dependencies for the alternative version in the ' - 'external configuration', str(err.value)) + self.assertIn( + "Missing dependencies for the alternative version in the " + "external configuration", + str(err.value), + ) messages = {} for cl in thin.log.warning.mock_calls: messages[cl[1][1]] = cl[1][0] % (cl[1][1], cl[1][2]) - for mod in ['tornado', 'yaml', 'msgpack']: - self.assertIn('not a Python importable module', messages[mod]) - self.assertIn('configured with not a file or does not exist', - messages['jinja2']) + for mod in ["tornado", "yaml", "msgpack"]: + self.assertIn("not a Python importable module", messages[mod]) + self.assertIn( + "configured with not a file or does not exist", messages["jinja2"] + ) - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.path.isfile', MagicMock(return_value=True)) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.path.isfile", MagicMock(return_value=True)) def test_get_ext_tops_config_pass(self): - ''' + """ Test thin.get_ext_tops configuration :return: - ''' - cfg = {'namespace': {'path': '/foo', - 'py-version': [2, 6], - 'dependencies': {'jinja2': '/jinja/foo.py', - 'yaml': '/yaml/', - 'tornado': '/tornado/tornado.py', - 'msgpack': 'msgpack.py'}}} + """ + cfg = { + "namespace": { + "path": "/foo", + "py-version": [2, 6], + "dependencies": { + "jinja2": "/jinja/foo.py", + "yaml": "/yaml/", + "tornado": "/tornado/tornado.py", + "msgpack": "msgpack.py", + }, + } + } out = thin.get_ext_tops(cfg) - assert out['namespace']['py-version'] == cfg['namespace']['py-version'] - assert out['namespace']['path'] == cfg['namespace']['path'] - assert sorted(out['namespace']['dependencies']) == sorted(['/tornado/tornado.py', - '/jinja/foo.py', '/yaml/', 'msgpack.py']) + assert out["namespace"]["py-version"] == cfg["namespace"]["py-version"] + assert out["namespace"]["path"] == cfg["namespace"]["path"] + assert sorted(out["namespace"]["dependencies"]) == sorted( + ["/tornado/tornado.py", "/jinja/foo.py", "/yaml/", "msgpack.py"] + ) - @patch('salt.utils.thin.sys.argv', [None, '{"foo": "bar"}']) - @patch('salt.utils.thin.get_tops', lambda **kw: kw) + @patch("salt.utils.thin.sys.argv", [None, '{"foo": "bar"}']) + @patch("salt.utils.thin.get_tops", lambda **kw: kw) def test_gte(self): - ''' + """ Test thin.gte external call for processing the info about tops per interpreter. :return: - ''' - assert salt.utils.json.loads(thin.gte()).get('foo') == 'bar' + """ + assert salt.utils.json.loads(thin.gte()).get("foo") == "bar" def test_add_dep_path(self): - ''' + """ Test thin._add_dependency function to setup dependency paths :return: - ''' + """ container = [] - for pth in ['/foo/bar.py', '/something/else/__init__.py']: - thin._add_dependency(container, type(str('obj'), (), {'__file__': pth})()) - assert '__init__' not in container[1] - assert container == ['/foo/bar.py', '/something/else'] + for pth in ["/foo/bar.py", "/something/else/__init__.py"]: + thin._add_dependency(container, type(str("obj"), (), {"__file__": pth})()) + assert "__init__" not in container[1] + assert container == ["/foo/bar.py", "/something/else"] def test_thin_path(self): - ''' + """ Test thin.thin_path returns the expected path. :return: - ''' - path = os.sep + os.path.join('path', 'to') - expected = os.path.join(path, 'thin', 'thin.tgz') + """ + path = os.sep + os.path.join("path", "to") + expected = os.path.join(path, "thin", "thin.tgz") self.assertEqual(thin.thin_path(path), expected) def test_get_salt_call_script(self): - ''' + """ Test get salt-call script rendered. :return: - ''' - out = thin._get_salt_call('foo', 'bar', py26=[2, 6], py27=[2, 7], py34=[3, 4]) + """ + out = thin._get_salt_call("foo", "bar", py26=[2, 6], py27=[2, 7], py34=[3, 4]) for line in salt.utils.stringutils.to_str(out).split(os.linesep): - if line.startswith('namespaces = {'): - data = salt.utils.json.loads(line.replace('namespaces = ', '').strip()) - assert data.get('py26') == [2, 6] - assert data.get('py27') == [2, 7] - assert data.get('py34') == [3, 4] - if line.startswith('syspaths = '): - data = salt.utils.json.loads(line.replace('syspaths = ', '')) - assert data == ['foo', 'bar'] + if line.startswith("namespaces = {"): + data = salt.utils.json.loads(line.replace("namespaces = ", "").strip()) + assert data.get("py26") == [2, 6] + assert data.get("py27") == [2, 7] + assert data.get("py34") == [3, 4] + if line.startswith("syspaths = "): + data = salt.utils.json.loads(line.replace("syspaths = ", "")) + assert data == ["foo", "bar"] def test_get_ext_namespaces_empty(self): - ''' + """ Test thin._get_ext_namespaces function returns an empty dictionary on nothing :return: - ''' + """ for obj in [None, {}, []]: assert thin._get_ext_namespaces(obj) == {} def test_get_ext_namespaces(self): - ''' + """ Test thin._get_ext_namespaces function returns namespaces properly out of the config. :return: - ''' - cfg = {'ns': {'py-version': [2, 7]}} - assert thin._get_ext_namespaces(cfg).get('ns') == (2, 7,) - assert isinstance(thin._get_ext_namespaces(cfg).get('ns'), tuple) + """ + cfg = {"ns": {"py-version": [2, 7]}} + assert thin._get_ext_namespaces(cfg).get("ns") == (2, 7,) + assert isinstance(thin._get_ext_namespaces(cfg).get("ns"), tuple) def test_get_ext_namespaces_failure(self): - ''' + """ Test thin._get_ext_namespaces function raises an exception if python major/minor version is not configured. :return: - ''' + """ with pytest.raises(salt.exceptions.SaltSystemExit): - thin._get_ext_namespaces({'ns': {}}) + thin._get_ext_namespaces({"ns": {}}) - @patch('salt.utils.thin.salt', type(str('salt'), (), {'__file__': '/site-packages/salt'})) - @patch('salt.utils.thin.jinja2', type(str('jinja2'), (), {'__file__': '/site-packages/jinja2'})) - @patch('salt.utils.thin.yaml', type(str('yaml'), (), {'__file__': '/site-packages/yaml'})) - @patch('salt.utils.thin.tornado', type(str('tornado'), (), {'__file__': '/site-packages/tornado'})) - @patch('salt.utils.thin.msgpack', type(str('msgpack'), (), {'__file__': '/site-packages/msgpack'})) - @patch('salt.utils.thin.certifi', type(str('certifi'), (), {'__file__': '/site-packages/certifi'})) - @patch('salt.utils.thin.singledispatch', type(str('singledispatch'), (), {'__file__': '/site-packages/sdp'})) - @patch('salt.utils.thin.singledispatch_helpers', type(str('singledispatch_helpers'), (), {'__file__': '/site-packages/sdp_hlp'})) - @patch('salt.utils.thin.ssl_match_hostname', type(str('ssl_match_hostname'), (), {'__file__': '/site-packages/ssl_mh'})) - @patch('salt.utils.thin.markupsafe', type(str('markupsafe'), (), {'__file__': '/site-packages/markupsafe'})) - @patch('salt.utils.thin.backports_abc', type(str('backports_abc'), (), {'__file__': '/site-packages/backports_abc'})) - @patch('salt.utils.thin.concurrent', type(str('concurrent'), (), {'__file__': '/site-packages/concurrent'})) - @patch('salt.utils.thin.log', MagicMock()) + @patch( + "salt.utils.thin.salt", + type(str("salt"), (), {"__file__": "/site-packages/salt"}), + ) + @patch( + "salt.utils.thin.jinja2", + type(str("jinja2"), (), {"__file__": "/site-packages/jinja2"}), + ) + @patch( + "salt.utils.thin.yaml", + type(str("yaml"), (), {"__file__": "/site-packages/yaml"}), + ) + @patch( + "salt.utils.thin.tornado", + type(str("tornado"), (), {"__file__": "/site-packages/tornado"}), + ) + @patch( + "salt.utils.thin.msgpack", + type(str("msgpack"), (), {"__file__": "/site-packages/msgpack"}), + ) + @patch( + "salt.utils.thin.certifi", + type(str("certifi"), (), {"__file__": "/site-packages/certifi"}), + ) + @patch( + "salt.utils.thin.singledispatch", + type(str("singledispatch"), (), {"__file__": "/site-packages/sdp"}), + ) + @patch( + "salt.utils.thin.singledispatch_helpers", + type(str("singledispatch_helpers"), (), {"__file__": "/site-packages/sdp_hlp"}), + ) + @patch( + "salt.utils.thin.ssl_match_hostname", + type(str("ssl_match_hostname"), (), {"__file__": "/site-packages/ssl_mh"}), + ) + @patch( + "salt.utils.thin.markupsafe", + type(str("markupsafe"), (), {"__file__": "/site-packages/markupsafe"}), + ) + @patch( + "salt.utils.thin.backports_abc", + type(str("backports_abc"), (), {"__file__": "/site-packages/backports_abc"}), + ) + @patch( + "salt.utils.thin.concurrent", + type(str("concurrent"), (), {"__file__": "/site-packages/concurrent"}), + ) + @patch("salt.utils.thin.log", MagicMock()) def test_get_tops(self): - ''' + """ Test thin.get_tops to get top directories, based on the interpreter. :return: - ''' - base_tops = ['/site-packages/salt', '/site-packages/jinja2', '/site-packages/yaml', - '/site-packages/tornado', '/site-packages/msgpack', '/site-packages/certifi', - '/site-packages/sdp', '/site-packages/sdp_hlp', '/site-packages/ssl_mh', - '/site-packages/markupsafe', '/site-packages/backports_abc', '/site-packages/concurrent'] + """ + base_tops = [ + "/site-packages/salt", + "/site-packages/jinja2", + "/site-packages/yaml", + "/site-packages/tornado", + "/site-packages/msgpack", + "/site-packages/certifi", + "/site-packages/sdp", + "/site-packages/sdp_hlp", + "/site-packages/ssl_mh", + "/site-packages/markupsafe", + "/site-packages/backports_abc", + "/site-packages/concurrent", + ] tops = thin.get_tops() assert len(tops) == len(base_tops) assert sorted(tops) == sorted(base_tops) - @patch('salt.utils.thin.salt', type(str('salt'), (), {'__file__': '/site-packages/salt'})) - @patch('salt.utils.thin.jinja2', type(str('jinja2'), (), {'__file__': '/site-packages/jinja2'})) - @patch('salt.utils.thin.yaml', type(str('yaml'), (), {'__file__': '/site-packages/yaml'})) - @patch('salt.utils.thin.tornado', type(str('tornado'), (), {'__file__': '/site-packages/tornado'})) - @patch('salt.utils.thin.msgpack', type(str('msgpack'), (), {'__file__': '/site-packages/msgpack'})) - @patch('salt.utils.thin.certifi', type(str('certifi'), (), {'__file__': '/site-packages/certifi'})) - @patch('salt.utils.thin.singledispatch', type(str('singledispatch'), (), {'__file__': '/site-packages/sdp'})) - @patch('salt.utils.thin.singledispatch_helpers', type(str('singledispatch_helpers'), (), {'__file__': '/site-packages/sdp_hlp'})) - @patch('salt.utils.thin.ssl_match_hostname', type(str('ssl_match_hostname'), (), {'__file__': '/site-packages/ssl_mh'})) - @patch('salt.utils.thin.markupsafe', type(str('markupsafe'), (), {'__file__': '/site-packages/markupsafe'})) - @patch('salt.utils.thin.backports_abc', type(str('backports_abc'), (), {'__file__': '/site-packages/backports_abc'})) - @patch('salt.utils.thin.concurrent', type(str('concurrent'), (), {'__file__': '/site-packages/concurrent'})) - @patch('salt.utils.thin.log', MagicMock()) + @patch( + "salt.utils.thin.salt", + type(str("salt"), (), {"__file__": "/site-packages/salt"}), + ) + @patch( + "salt.utils.thin.jinja2", + type(str("jinja2"), (), {"__file__": "/site-packages/jinja2"}), + ) + @patch( + "salt.utils.thin.yaml", + type(str("yaml"), (), {"__file__": "/site-packages/yaml"}), + ) + @patch( + "salt.utils.thin.tornado", + type(str("tornado"), (), {"__file__": "/site-packages/tornado"}), + ) + @patch( + "salt.utils.thin.msgpack", + type(str("msgpack"), (), {"__file__": "/site-packages/msgpack"}), + ) + @patch( + "salt.utils.thin.certifi", + type(str("certifi"), (), {"__file__": "/site-packages/certifi"}), + ) + @patch( + "salt.utils.thin.singledispatch", + type(str("singledispatch"), (), {"__file__": "/site-packages/sdp"}), + ) + @patch( + "salt.utils.thin.singledispatch_helpers", + type(str("singledispatch_helpers"), (), {"__file__": "/site-packages/sdp_hlp"}), + ) + @patch( + "salt.utils.thin.ssl_match_hostname", + type(str("ssl_match_hostname"), (), {"__file__": "/site-packages/ssl_mh"}), + ) + @patch( + "salt.utils.thin.markupsafe", + type(str("markupsafe"), (), {"__file__": "/site-packages/markupsafe"}), + ) + @patch( + "salt.utils.thin.backports_abc", + type(str("backports_abc"), (), {"__file__": "/site-packages/backports_abc"}), + ) + @patch( + "salt.utils.thin.concurrent", + type(str("concurrent"), (), {"__file__": "/site-packages/concurrent"}), + ) + @patch("salt.utils.thin.log", MagicMock()) def test_get_tops_extra_mods(self): - ''' + """ Test thin.get_tops to get extra-modules alongside the top directories, based on the interpreter. :return: - ''' - base_tops = ['/site-packages/salt', - '/site-packages/jinja2', - '/site-packages/yaml', - '/site-packages/tornado', - '/site-packages/msgpack', - '/site-packages/certifi', - '/site-packages/sdp', - '/site-packages/sdp_hlp', - '/site-packages/ssl_mh', - '/site-packages/concurrent', - '/site-packages/markupsafe', - '/site-packages/backports_abc', - os.sep + os.path.join('custom', 'foo'), - os.sep + os.path.join('custom', 'bar.py')] - builtins = sys.version_info.major == 3 and 'builtins' or '__builtin__' - foo = {'__file__': os.sep + os.path.join('custom', 'foo', '__init__.py')} - bar = {'__file__': os.sep + os.path.join('custom', 'bar')} - with patch('{}.__import__'.format(builtins), - MagicMock(side_effect=[type(str('foo'), (), foo), - type(str('bar'), (), bar)])): - tops = thin.get_tops(extra_mods='foo,bar') + """ + base_tops = [ + "/site-packages/salt", + "/site-packages/jinja2", + "/site-packages/yaml", + "/site-packages/tornado", + "/site-packages/msgpack", + "/site-packages/certifi", + "/site-packages/sdp", + "/site-packages/sdp_hlp", + "/site-packages/ssl_mh", + "/site-packages/concurrent", + "/site-packages/markupsafe", + "/site-packages/backports_abc", + os.sep + os.path.join("custom", "foo"), + os.sep + os.path.join("custom", "bar.py"), + ] + builtins = sys.version_info.major == 3 and "builtins" or "__builtin__" + foo = {"__file__": os.sep + os.path.join("custom", "foo", "__init__.py")} + bar = {"__file__": os.sep + os.path.join("custom", "bar")} + with patch( + "{}.__import__".format(builtins), + MagicMock( + side_effect=[type(str("foo"), (), foo), type(str("bar"), (), bar)] + ), + ): + tops = thin.get_tops(extra_mods="foo,bar") self.assertEqual(len(tops), len(base_tops)) self.assertListEqual(sorted(tops), sorted(base_tops)) - @patch('salt.utils.thin.salt', type(str('salt'), (), {'__file__': '/site-packages/salt'})) - @patch('salt.utils.thin.jinja2', type(str('jinja2'), (), {'__file__': '/site-packages/jinja2'})) - @patch('salt.utils.thin.yaml', type(str('yaml'), (), {'__file__': '/site-packages/yaml'})) - @patch('salt.utils.thin.tornado', type(str('tornado'), (), {'__file__': '/site-packages/tornado'})) - @patch('salt.utils.thin.msgpack', type(str('msgpack'), (), {'__file__': '/site-packages/msgpack'})) - @patch('salt.utils.thin.certifi', type(str('certifi'), (), {'__file__': '/site-packages/certifi'})) - @patch('salt.utils.thin.singledispatch', type(str('singledispatch'), (), {'__file__': '/site-packages/sdp'})) - @patch('salt.utils.thin.singledispatch_helpers', type(str('singledispatch_helpers'), (), {'__file__': '/site-packages/sdp_hlp'})) - @patch('salt.utils.thin.ssl_match_hostname', type(str('ssl_match_hostname'), (), {'__file__': '/site-packages/ssl_mh'})) - @patch('salt.utils.thin.markupsafe', type(str('markupsafe'), (), {'__file__': '/site-packages/markupsafe'})) - @patch('salt.utils.thin.backports_abc', type(str('backports_abc'), (), {'__file__': '/site-packages/backports_abc'})) - @patch('salt.utils.thin.concurrent', type(str('concurrent'), (), {'__file__': '/site-packages/concurrent'})) - @patch('salt.utils.thin.log', MagicMock()) + @patch( + "salt.utils.thin.salt", + type(str("salt"), (), {"__file__": "/site-packages/salt"}), + ) + @patch( + "salt.utils.thin.jinja2", + type(str("jinja2"), (), {"__file__": "/site-packages/jinja2"}), + ) + @patch( + "salt.utils.thin.yaml", + type(str("yaml"), (), {"__file__": "/site-packages/yaml"}), + ) + @patch( + "salt.utils.thin.tornado", + type(str("tornado"), (), {"__file__": "/site-packages/tornado"}), + ) + @patch( + "salt.utils.thin.msgpack", + type(str("msgpack"), (), {"__file__": "/site-packages/msgpack"}), + ) + @patch( + "salt.utils.thin.certifi", + type(str("certifi"), (), {"__file__": "/site-packages/certifi"}), + ) + @patch( + "salt.utils.thin.singledispatch", + type(str("singledispatch"), (), {"__file__": "/site-packages/sdp"}), + ) + @patch( + "salt.utils.thin.singledispatch_helpers", + type(str("singledispatch_helpers"), (), {"__file__": "/site-packages/sdp_hlp"}), + ) + @patch( + "salt.utils.thin.ssl_match_hostname", + type(str("ssl_match_hostname"), (), {"__file__": "/site-packages/ssl_mh"}), + ) + @patch( + "salt.utils.thin.markupsafe", + type(str("markupsafe"), (), {"__file__": "/site-packages/markupsafe"}), + ) + @patch( + "salt.utils.thin.backports_abc", + type(str("backports_abc"), (), {"__file__": "/site-packages/backports_abc"}), + ) + @patch( + "salt.utils.thin.concurrent", + type(str("concurrent"), (), {"__file__": "/site-packages/concurrent"}), + ) + @patch("salt.utils.thin.log", MagicMock()) def test_get_tops_so_mods(self): - ''' + """ Test thin.get_tops to get extra-modules alongside the top directories, based on the interpreter. :return: - ''' - base_tops = ['/site-packages/salt', '/site-packages/jinja2', '/site-packages/yaml', - '/site-packages/tornado', '/site-packages/msgpack', '/site-packages/certifi', - '/site-packages/sdp', '/site-packages/sdp_hlp', '/site-packages/ssl_mh', '/site-packages/concurrent', - '/site-packages/markupsafe', '/site-packages/backports_abc', '/custom/foo.so', '/custom/bar.so'] - builtins = sys.version_info.major == 3 and 'builtins' or '__builtin__' - with patch('{}.__import__'.format(builtins), - MagicMock(side_effect=[type(str('salt'), (), {'__file__': '/custom/foo.so'}), - type(str('salt'), (), {'__file__': '/custom/bar.so'})])): - tops = thin.get_tops(so_mods='foo,bar') + """ + base_tops = [ + "/site-packages/salt", + "/site-packages/jinja2", + "/site-packages/yaml", + "/site-packages/tornado", + "/site-packages/msgpack", + "/site-packages/certifi", + "/site-packages/sdp", + "/site-packages/sdp_hlp", + "/site-packages/ssl_mh", + "/site-packages/concurrent", + "/site-packages/markupsafe", + "/site-packages/backports_abc", + "/custom/foo.so", + "/custom/bar.so", + ] + builtins = sys.version_info.major == 3 and "builtins" or "__builtin__" + with patch( + "{}.__import__".format(builtins), + MagicMock( + side_effect=[ + type(str("salt"), (), {"__file__": "/custom/foo.so"}), + type(str("salt"), (), {"__file__": "/custom/bar.so"}), + ] + ), + ): + tops = thin.get_tops(so_mods="foo,bar") assert len(tops) == len(base_tops) assert sorted(tops) == sorted(base_tops) - @patch('salt.utils.thin.gen_thin', MagicMock(return_value='/path/to/thin/thin.tgz')) - @patch('salt.utils.hashutils.get_hash', MagicMock(return_value=12345)) + @patch("salt.utils.thin.gen_thin", MagicMock(return_value="/path/to/thin/thin.tgz")) + @patch("salt.utils.hashutils.get_hash", MagicMock(return_value=12345)) def test_thin_sum(self): - ''' + """ Test thin.thin_sum function. :return: - ''' - assert thin.thin_sum('/cachedir', form='sha256')[1] == 12345 + """ + assert thin.thin_sum("/cachedir", form="sha256")[1] == 12345 thin.salt.utils.hashutils.get_hash.assert_called() assert thin.salt.utils.hashutils.get_hash.call_count == 1 path, form = thin.salt.utils.hashutils.get_hash.call_args[0] - assert path == '/path/to/thin/thin.tgz' - assert form == 'sha256' + assert path == "/path/to/thin/thin.tgz" + assert form == "sha256" - @patch('salt.utils.thin.gen_min', MagicMock(return_value='/path/to/thin/min.tgz')) - @patch('salt.utils.hashutils.get_hash', MagicMock(return_value=12345)) + @patch("salt.utils.thin.gen_min", MagicMock(return_value="/path/to/thin/min.tgz")) + @patch("salt.utils.hashutils.get_hash", MagicMock(return_value=12345)) def test_min_sum(self): - ''' + """ Test thin.thin_sum function. :return: - ''' - assert thin.min_sum('/cachedir', form='sha256') == 12345 + """ + assert thin.min_sum("/cachedir", form="sha256") == 12345 thin.salt.utils.hashutils.get_hash.assert_called() assert thin.salt.utils.hashutils.get_hash.call_count == 1 path, form = thin.salt.utils.hashutils.get_hash.call_args[0] - assert path == '/path/to/thin/min.tgz' - assert form == 'sha256' + assert path == "/path/to/thin/min.tgz" + assert form == "sha256" - @patch('salt.utils.thin.sys.version_info', (2, 5)) - @patch('salt.exceptions.SaltSystemExit', Exception) + @patch("salt.utils.thin.sys.version_info", (2, 5)) + @patch("salt.exceptions.SaltSystemExit", Exception) def test_gen_thin_fails_ancient_python_version(self): - ''' + """ Test thin.gen_thin function raises an exception if Python major/minor version is lower than 2.6 :return: - ''' + """ with pytest.raises(salt.exceptions.SaltSystemExit) as err: thin.sys.exc_clear = lambda: None - thin.gen_thin('') - self.assertIn('The minimum required python version to run salt-ssh is ' - '"2.6"', str(err.value)) + thin.gen_thin("") + self.assertIn( + "The minimum required python version to run salt-ssh is " '"2.6"', + str(err.value), + ) - @skipIf(salt.utils.platform.is_windows() and thin._six.PY2, - 'Dies on Python2 on Windows') - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.os.makedirs', MagicMock()) - @patch('salt.utils.files.fopen', MagicMock()) - @patch('salt.utils.thin._get_salt_call', MagicMock()) - @patch('salt.utils.thin._get_ext_namespaces', MagicMock()) - @patch('salt.utils.thin.get_tops', MagicMock(return_value=['/foo3', '/bar3'])) - @patch('salt.utils.thin.get_ext_tops', MagicMock(return_value={})) - @patch('salt.utils.thin.os.path.isfile', MagicMock()) - @patch('salt.utils.thin.os.path.isdir', MagicMock(return_value=True)) - @patch('salt.utils.thin.os.remove', MagicMock()) - @patch('salt.utils.thin.os.path.exists', MagicMock()) - @patch('salt.utils.path.os_walk', MagicMock(return_value=[])) - @patch('salt.utils.thin.subprocess.Popen', - _popen(None, side_effect=[(bts('2.7'), bts('')), (bts('["/foo27", "/bar27"]'), bts(''))])) - @patch('salt.utils.thin.tarfile', MagicMock()) - @patch('salt.utils.thin.zipfile', MagicMock()) - @patch('salt.utils.thin.os.getcwd', MagicMock()) - @patch('salt.utils.thin.os.chdir', MagicMock()) - @patch('salt.utils.thin.tempfile.mkdtemp', MagicMock()) - @patch('salt.utils.thin.tempfile.mkstemp', MagicMock(return_value=(3, ".temporary"))) - @patch('salt.utils.thin.shutil', MagicMock()) - @patch('salt.utils.path.which', MagicMock(return_value='')) - @patch('salt.utils.thin._get_thintar_prefix', MagicMock()) + @skipIf( + salt.utils.platform.is_windows() and thin._six.PY2, "Dies on Python2 on Windows" + ) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.os.makedirs", MagicMock()) + @patch("salt.utils.files.fopen", MagicMock()) + @patch("salt.utils.thin._get_salt_call", MagicMock()) + @patch("salt.utils.thin._get_ext_namespaces", MagicMock()) + @patch("salt.utils.thin.get_tops", MagicMock(return_value=["/foo3", "/bar3"])) + @patch("salt.utils.thin.get_ext_tops", MagicMock(return_value={})) + @patch("salt.utils.thin.os.path.isfile", MagicMock()) + @patch("salt.utils.thin.os.path.isdir", MagicMock(return_value=True)) + @patch("salt.utils.thin.os.remove", MagicMock()) + @patch("salt.utils.thin.os.path.exists", MagicMock()) + @patch("salt.utils.path.os_walk", MagicMock(return_value=[])) + @patch( + "salt.utils.thin.subprocess.Popen", + _popen( + None, + side_effect=[(bts("2.7"), bts("")), (bts('["/foo27", "/bar27"]'), bts(""))], + ), + ) + @patch("salt.utils.thin.tarfile", MagicMock()) + @patch("salt.utils.thin.zipfile", MagicMock()) + @patch("salt.utils.thin.os.getcwd", MagicMock()) + @patch("salt.utils.thin.os.chdir", MagicMock()) + @patch("salt.utils.thin.tempfile.mkdtemp", MagicMock()) + @patch( + "salt.utils.thin.tempfile.mkstemp", MagicMock(return_value=(3, ".temporary")) + ) + @patch("salt.utils.thin.shutil", MagicMock()) + @patch("salt.utils.path.which", MagicMock(return_value="")) + @patch("salt.utils.thin._get_thintar_prefix", MagicMock()) def test_gen_thin_python_exist_or_not(self): - ''' + """ Test thin.gen_thin function if the opposite python binary does not exist - ''' + """ with TstSuiteLoggingHandler() as handler: - thin.gen_thin('') + thin.gen_thin("") salt.utils.thin.subprocess.Popen.assert_not_called() if salt.ext.six.PY2: - self.assertIn('DEBUG:python3 binary does not exist. Will not attempt to generate ' - 'tops for Python 3', - handler.messages) + self.assertIn( + "DEBUG:python3 binary does not exist. Will not attempt to generate " + "tops for Python 3", + handler.messages, + ) if salt.ext.six.PY3: - self.assertIn('DEBUG:python2 binary does not exist. Will not ' - 'detect Python 2 version', - handler.messages) - self.assertIn('DEBUG:python2 binary does not exist. Will not attempt to generate ' - 'tops for Python 2', - handler.messages) + self.assertIn( + "DEBUG:python2 binary does not exist. Will not " + "detect Python 2 version", + handler.messages, + ) + self.assertIn( + "DEBUG:python2 binary does not exist. Will not attempt to generate " + "tops for Python 2", + handler.messages, + ) - @skipIf(salt.utils.platform.is_windows() and thin._six.PY2, - 'Dies on Python2 on Windows') - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.makedirs', MagicMock()) - @patch('salt.utils.files.fopen', MagicMock()) - @patch('salt.utils.thin._get_salt_call', MagicMock()) - @patch('salt.utils.thin._get_ext_namespaces', MagicMock()) - @patch('salt.utils.thin.get_tops', MagicMock(return_value=['/foo3', '/bar3'])) - @patch('salt.utils.thin.get_ext_tops', MagicMock(return_value={})) - @patch('salt.utils.thin.os.path.isfile', MagicMock()) - @patch('salt.utils.thin.os.path.isdir', MagicMock(return_value=True)) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.remove', MagicMock()) - @patch('salt.utils.thin.os.path.exists', MagicMock()) - @patch('salt.utils.path.os_walk', MagicMock(return_value=[])) - @patch('salt.utils.thin.subprocess.Popen', - _popen(None, side_effect=[(bts('2.7'), bts('')), (bts('["/foo27", "/bar27"]'), bts(''))])) - @patch('salt.utils.thin.tarfile', MagicMock()) - @patch('salt.utils.thin.zipfile', MagicMock()) - @patch('salt.utils.thin.os.getcwd', MagicMock()) - @patch('salt.utils.thin.os.chdir', MagicMock()) - @patch('salt.utils.thin.os.close', MagicMock()) - @patch('salt.utils.thin.tempfile.mkdtemp', MagicMock()) - @patch('salt.utils.thin.tempfile.mkstemp', MagicMock(return_value=(3, ".temporary"))) - @patch('salt.utils.thin.shutil', MagicMock()) - @patch('salt.utils.thin._six.PY3', True) - @patch('salt.utils.thin._six.PY2', False) - @patch('salt.utils.thin.sys.version_info', _version_info(None, 3, 6)) - @patch('salt.utils.path.which', MagicMock(return_value='/usr/bin/python')) + @skipIf( + salt.utils.platform.is_windows() and thin._six.PY2, "Dies on Python2 on Windows" + ) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.makedirs", MagicMock()) + @patch("salt.utils.files.fopen", MagicMock()) + @patch("salt.utils.thin._get_salt_call", MagicMock()) + @patch("salt.utils.thin._get_ext_namespaces", MagicMock()) + @patch("salt.utils.thin.get_tops", MagicMock(return_value=["/foo3", "/bar3"])) + @patch("salt.utils.thin.get_ext_tops", MagicMock(return_value={})) + @patch("salt.utils.thin.os.path.isfile", MagicMock()) + @patch("salt.utils.thin.os.path.isdir", MagicMock(return_value=True)) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.remove", MagicMock()) + @patch("salt.utils.thin.os.path.exists", MagicMock()) + @patch("salt.utils.path.os_walk", MagicMock(return_value=[])) + @patch( + "salt.utils.thin.subprocess.Popen", + _popen( + None, + side_effect=[(bts("2.7"), bts("")), (bts('["/foo27", "/bar27"]'), bts(""))], + ), + ) + @patch("salt.utils.thin.tarfile", MagicMock()) + @patch("salt.utils.thin.zipfile", MagicMock()) + @patch("salt.utils.thin.os.getcwd", MagicMock()) + @patch("salt.utils.thin.os.chdir", MagicMock()) + @patch("salt.utils.thin.os.close", MagicMock()) + @patch("salt.utils.thin.tempfile.mkdtemp", MagicMock()) + @patch( + "salt.utils.thin.tempfile.mkstemp", MagicMock(return_value=(3, ".temporary")) + ) + @patch("salt.utils.thin.shutil", MagicMock()) + @patch("salt.utils.thin._six.PY3", True) + @patch("salt.utils.thin._six.PY2", False) + @patch("salt.utils.thin.sys.version_info", _version_info(None, 3, 6)) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/python")) def test_gen_thin_compression_fallback_py3(self): - ''' + """ Test thin.gen_thin function if fallbacks to the gzip compression, once setup wrong. NOTE: Py2 version of this test is not required, as code shares the same spot across the versions. :return: - ''' - thin.gen_thin('', compress='arj') + """ + thin.gen_thin("", compress="arj") thin.log.warning.assert_called() pt, msg = thin.log.warning.mock_calls[0][1] - assert pt % msg == 'Unknown compression type: "arj". Falling back to "gzip" compression.' + assert ( + pt % msg + == 'Unknown compression type: "arj". Falling back to "gzip" compression.' + ) thin.zipfile.ZipFile.assert_not_called() thin.tarfile.open.assert_called() - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.makedirs', MagicMock()) - @patch('salt.utils.files.fopen', MagicMock()) - @patch('salt.utils.thin._get_salt_call', MagicMock()) - @patch('salt.utils.thin._get_ext_namespaces', MagicMock()) - @patch('salt.utils.thin.get_tops', MagicMock(return_value=['/foo3', '/bar3'])) - @patch('salt.utils.thin.get_ext_tops', MagicMock(return_value={})) - @patch('salt.utils.thin.os.path.isfile', MagicMock()) - @patch('salt.utils.thin.os.path.isdir', MagicMock(return_value=False)) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.remove', MagicMock()) - @patch('salt.utils.thin.os.path.exists', MagicMock()) - @patch('salt.utils.path.os_walk', MagicMock(return_value=[])) - @patch('salt.utils.thin.subprocess.Popen', - _popen(None, side_effect=[(bts('2.7'), bts('')), (bts('["/foo27", "/bar27"]'), bts(''))])) - @patch('salt.utils.thin.tarfile', MagicMock()) - @patch('salt.utils.thin.zipfile', MagicMock()) - @patch('salt.utils.thin.os.getcwd', MagicMock()) - @patch('salt.utils.thin.os.chdir', MagicMock()) - @patch('salt.utils.thin.os.close', MagicMock()) - @patch('salt.utils.thin.tempfile.mkdtemp', MagicMock(return_value='')) - @patch('salt.utils.thin.tempfile.mkstemp', MagicMock(return_value=(3, ".temporary"))) - @patch('salt.utils.thin.shutil', MagicMock()) - @patch('salt.utils.thin._six.PY3', True) - @patch('salt.utils.thin._six.PY2', False) - @patch('salt.utils.thin.sys.version_info', _version_info(None, 3, 6)) - @patch('salt.utils.path.which', MagicMock(return_value='/usr/bin/python')) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.makedirs", MagicMock()) + @patch("salt.utils.files.fopen", MagicMock()) + @patch("salt.utils.thin._get_salt_call", MagicMock()) + @patch("salt.utils.thin._get_ext_namespaces", MagicMock()) + @patch("salt.utils.thin.get_tops", MagicMock(return_value=["/foo3", "/bar3"])) + @patch("salt.utils.thin.get_ext_tops", MagicMock(return_value={})) + @patch("salt.utils.thin.os.path.isfile", MagicMock()) + @patch("salt.utils.thin.os.path.isdir", MagicMock(return_value=False)) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.remove", MagicMock()) + @patch("salt.utils.thin.os.path.exists", MagicMock()) + @patch("salt.utils.path.os_walk", MagicMock(return_value=[])) + @patch( + "salt.utils.thin.subprocess.Popen", + _popen( + None, + side_effect=[(bts("2.7"), bts("")), (bts('["/foo27", "/bar27"]'), bts(""))], + ), + ) + @patch("salt.utils.thin.tarfile", MagicMock()) + @patch("salt.utils.thin.zipfile", MagicMock()) + @patch("salt.utils.thin.os.getcwd", MagicMock()) + @patch("salt.utils.thin.os.chdir", MagicMock()) + @patch("salt.utils.thin.os.close", MagicMock()) + @patch("salt.utils.thin.tempfile.mkdtemp", MagicMock(return_value="")) + @patch( + "salt.utils.thin.tempfile.mkstemp", MagicMock(return_value=(3, ".temporary")) + ) + @patch("salt.utils.thin.shutil", MagicMock()) + @patch("salt.utils.thin._six.PY3", True) + @patch("salt.utils.thin._six.PY2", False) + @patch("salt.utils.thin.sys.version_info", _version_info(None, 3, 6)) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/python")) def test_gen_thin_control_files_written_py3(self): - ''' + """ Test thin.gen_thin function if control files are written (version, salt-call etc). NOTE: Py2 version of this test is not required, as code shares the same spot across the versions. :return: - ''' - thin.gen_thin('') + """ + thin.gen_thin("") arc_name, arc_mode = thin.tarfile.method_calls[0][1] self.assertEqual(arc_name, ".temporary") - self.assertEqual(arc_mode, 'w:gz') - for idx, fname in enumerate(['version', '.thin-gen-py-version', 'salt-call', 'supported-versions']): + self.assertEqual(arc_mode, "w:gz") + for idx, fname in enumerate( + ["version", ".thin-gen-py-version", "salt-call", "supported-versions"] + ): name = thin.tarfile.open().method_calls[idx + 4][1][0] self.assertEqual(name, fname) thin.tarfile.open().close.assert_called() - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.makedirs', MagicMock()) - @patch('salt.utils.files.fopen', MagicMock()) - @patch('salt.utils.thin._get_salt_call', MagicMock()) - @patch('salt.utils.thin._get_ext_namespaces', MagicMock()) - @patch('salt.utils.thin.get_tops', MagicMock(return_value=['/salt', '/bar3'])) - @patch('salt.utils.thin.get_ext_tops', MagicMock(return_value={})) - @patch('salt.utils.thin.os.path.isfile', MagicMock()) - @patch('salt.utils.thin.os.path.isdir', MagicMock(return_value=True)) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.remove', MagicMock()) - @patch('salt.utils.thin.os.path.exists', MagicMock()) - @patch('salt.utils.path.os_walk', - MagicMock(return_value=(('root', [], ['r1', 'r2', 'r3']), ('root2', [], ['r4', 'r5', 'r6'])))) - @patch('salt.utils.thin.subprocess.Popen', - _popen(None, side_effect=[(bts('2.7'), bts('')), (bts('["/foo27", "/bar27"]'), bts(''))])) - @patch('salt.utils.thin.tarfile', _tarfile(None)) - @patch('salt.utils.thin.zipfile', MagicMock()) - @patch('salt.utils.thin.os.getcwd', MagicMock()) - @patch('salt.utils.thin.os.chdir', MagicMock()) - @patch('salt.utils.thin.os.close', MagicMock()) - @patch('salt.utils.thin.tempfile.mkdtemp', MagicMock(return_value='')) - @patch('salt.utils.thin.tempfile.mkstemp', MagicMock(return_value=(3, ".temporary"))) - @patch('salt.utils.thin.shutil', MagicMock()) - @patch('salt.utils.thin._six.PY3', True) - @patch('salt.utils.thin._six.PY2', False) - @patch('salt.utils.thin.sys.version_info', _version_info(None, 3, 6)) - @patch('salt.utils.hashutils.DigestCollector', MagicMock()) - @patch('salt.utils.path.which', MagicMock(return_value='/usr/bin/python')) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.makedirs", MagicMock()) + @patch("salt.utils.files.fopen", MagicMock()) + @patch("salt.utils.thin._get_salt_call", MagicMock()) + @patch("salt.utils.thin._get_ext_namespaces", MagicMock()) + @patch("salt.utils.thin.get_tops", MagicMock(return_value=["/salt", "/bar3"])) + @patch("salt.utils.thin.get_ext_tops", MagicMock(return_value={})) + @patch("salt.utils.thin.os.path.isfile", MagicMock()) + @patch("salt.utils.thin.os.path.isdir", MagicMock(return_value=True)) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.remove", MagicMock()) + @patch("salt.utils.thin.os.path.exists", MagicMock()) + @patch( + "salt.utils.path.os_walk", + MagicMock( + return_value=( + ("root", [], ["r1", "r2", "r3"]), + ("root2", [], ["r4", "r5", "r6"]), + ) + ), + ) + @patch( + "salt.utils.thin.subprocess.Popen", + _popen( + None, + side_effect=[(bts("2.7"), bts("")), (bts('["/foo27", "/bar27"]'), bts(""))], + ), + ) + @patch("salt.utils.thin.tarfile", _tarfile(None)) + @patch("salt.utils.thin.zipfile", MagicMock()) + @patch("salt.utils.thin.os.getcwd", MagicMock()) + @patch("salt.utils.thin.os.chdir", MagicMock()) + @patch("salt.utils.thin.os.close", MagicMock()) + @patch("salt.utils.thin.tempfile.mkdtemp", MagicMock(return_value="")) + @patch( + "salt.utils.thin.tempfile.mkstemp", MagicMock(return_value=(3, ".temporary")) + ) + @patch("salt.utils.thin.shutil", MagicMock()) + @patch("salt.utils.thin._six.PY3", True) + @patch("salt.utils.thin._six.PY2", False) + @patch("salt.utils.thin.sys.version_info", _version_info(None, 3, 6)) + @patch("salt.utils.hashutils.DigestCollector", MagicMock()) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/python")) def test_gen_thin_main_content_files_written_py3(self): - ''' + """ Test thin.gen_thin function if main content files are written. NOTE: Py2 version of this test is not required, as code shares the same spot across the versions. :return: - ''' - thin.gen_thin('') + """ + thin.gen_thin("") files = [] - for py in ('py2', 'py2', 'py3', 'pyall'): + for py in ("py2", "py2", "py3", "pyall"): for i in range(1, 4): - files.append(os.path.join(py, 'root', 'r{0}'.format(i))) + files.append(os.path.join(py, "root", "r{0}".format(i))) for i in range(4, 7): - files.append(os.path.join(py, 'root2', 'r{0}'.format(i))) + files.append(os.path.join(py, "root2", "r{0}".format(i))) for cl in thin.tarfile.open().method_calls[:-6]: - arcname = cl[2].get('arcname') + arcname = cl[2].get("arcname") self.assertIn(arcname, files) files.pop(files.index(arcname)) self.assertFalse(files) - @patch('salt.exceptions.SaltSystemExit', Exception) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.makedirs', MagicMock()) - @patch('salt.utils.files.fopen', MagicMock()) - @patch('salt.utils.thin._get_salt_call', MagicMock()) - @patch('salt.utils.thin._get_ext_namespaces', MagicMock()) - @patch('salt.utils.thin.get_tops', MagicMock(return_value=[])) - @patch('salt.utils.thin.get_ext_tops', - MagicMock(return_value={'namespace': {'py-version': [2, 7], - 'path': '/opt/2015.8/salt', - 'dependencies': ['/opt/certifi', '/opt/whatever']}})) - @patch('salt.utils.thin.os.path.isfile', MagicMock()) - @patch('salt.utils.thin.os.path.isdir', MagicMock(return_value=True)) - @patch('salt.utils.thin.log', MagicMock()) - @patch('salt.utils.thin.os.remove', MagicMock()) - @patch('salt.utils.thin.os.path.exists', MagicMock()) - @patch('salt.utils.path.os_walk', - MagicMock(return_value=(('root', [], ['r1', 'r2', 'r3']), ('root2', [], ['r4', 'r5', 'r6'])))) - @patch('salt.utils.thin.subprocess.Popen', - _popen(None, side_effect=[(bts('2.7'), bts('')), (bts('["/foo27", "/bar27"]'), bts(''))])) - @patch('salt.utils.thin.tarfile', _tarfile(None)) - @patch('salt.utils.thin.zipfile', MagicMock()) - @patch('salt.utils.thin.os.getcwd', MagicMock()) - @patch('salt.utils.thin.os.chdir', MagicMock()) - @patch('salt.utils.thin.os.close', MagicMock()) - @patch('salt.utils.thin.tempfile.mkdtemp', MagicMock(return_value='')) - @patch('salt.utils.thin.tempfile.mkstemp', MagicMock(return_value=(3, ".temporary"))) - @patch('salt.utils.thin.shutil', MagicMock()) - @patch('salt.utils.thin._six.PY3', True) - @patch('salt.utils.thin._six.PY2', False) - @patch('salt.utils.thin.sys.version_info', _version_info(None, 3, 6)) - @patch('salt.utils.hashutils.DigestCollector', MagicMock()) - @patch('salt.utils.path.which', MagicMock(return_value='/usr/bin/python')) + @patch("salt.exceptions.SaltSystemExit", Exception) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.makedirs", MagicMock()) + @patch("salt.utils.files.fopen", MagicMock()) + @patch("salt.utils.thin._get_salt_call", MagicMock()) + @patch("salt.utils.thin._get_ext_namespaces", MagicMock()) + @patch("salt.utils.thin.get_tops", MagicMock(return_value=[])) + @patch( + "salt.utils.thin.get_ext_tops", + MagicMock( + return_value={ + "namespace": { + "py-version": [2, 7], + "path": "/opt/2015.8/salt", + "dependencies": ["/opt/certifi", "/opt/whatever"], + } + } + ), + ) + @patch("salt.utils.thin.os.path.isfile", MagicMock()) + @patch("salt.utils.thin.os.path.isdir", MagicMock(return_value=True)) + @patch("salt.utils.thin.log", MagicMock()) + @patch("salt.utils.thin.os.remove", MagicMock()) + @patch("salt.utils.thin.os.path.exists", MagicMock()) + @patch( + "salt.utils.path.os_walk", + MagicMock( + return_value=( + ("root", [], ["r1", "r2", "r3"]), + ("root2", [], ["r4", "r5", "r6"]), + ) + ), + ) + @patch( + "salt.utils.thin.subprocess.Popen", + _popen( + None, + side_effect=[(bts("2.7"), bts("")), (bts('["/foo27", "/bar27"]'), bts(""))], + ), + ) + @patch("salt.utils.thin.tarfile", _tarfile(None)) + @patch("salt.utils.thin.zipfile", MagicMock()) + @patch("salt.utils.thin.os.getcwd", MagicMock()) + @patch("salt.utils.thin.os.chdir", MagicMock()) + @patch("salt.utils.thin.os.close", MagicMock()) + @patch("salt.utils.thin.tempfile.mkdtemp", MagicMock(return_value="")) + @patch( + "salt.utils.thin.tempfile.mkstemp", MagicMock(return_value=(3, ".temporary")) + ) + @patch("salt.utils.thin.shutil", MagicMock()) + @patch("salt.utils.thin._six.PY3", True) + @patch("salt.utils.thin._six.PY2", False) + @patch("salt.utils.thin.sys.version_info", _version_info(None, 3, 6)) + @patch("salt.utils.hashutils.DigestCollector", MagicMock()) + @patch("salt.utils.path.which", MagicMock(return_value="/usr/bin/python")) def test_gen_thin_ext_alternative_content_files_written_py3(self): - ''' + """ Test thin.gen_thin function if external alternative content files are written. NOTE: Py2 version of this test is not required, as code shares the same spot across the versions. :return: - ''' - thin.gen_thin('') + """ + thin.gen_thin("") files = [] - for py in ('pyall', 'pyall', 'py2'): + for py in ("pyall", "pyall", "py2"): for i in range(1, 4): - files.append( - os.path.join('namespace', py, 'root', 'r{0}'.format(i))) + files.append(os.path.join("namespace", py, "root", "r{0}".format(i))) for i in range(4, 7): - files.append( - os.path.join('namespace', py, 'root2', 'r{0}'.format(i))) + files.append(os.path.join("namespace", py, "root2", "r{0}".format(i))) for idx, cl in enumerate(thin.tarfile.open().method_calls[12:-6]): - arcname = cl[2].get('arcname') + arcname = cl[2].get("arcname") self.assertIn(arcname, files) files.pop(files.index(arcname)) self.assertFalse(files) def test_get_supported_py_config_typecheck(self): - ''' + """ Test collecting proper py-versions. Should return bytes type. :return: - ''' + """ tops = {} ext_cfg = {} out = thin._get_supported_py_config(tops=tops, extended_cfg=ext_cfg) - assert type(salt.utils.stringutils.to_bytes('')) == type(out) + assert type(salt.utils.stringutils.to_bytes("")) == type(out) def test_get_supported_py_config_base_tops(self): - ''' + """ Test collecting proper py-versions. Should return proper base tops. :return: - ''' - tops = {'3': ['/groundkeepers', '/stole'], '2': ['/the-root', '/password']} + """ + tops = {"3": ["/groundkeepers", "/stole"], "2": ["/the-root", "/password"]} ext_cfg = {} - out = salt.utils.stringutils.to_str(thin._get_supported_py_config( - tops=tops, extended_cfg=ext_cfg)).strip().split(os.linesep) + out = ( + salt.utils.stringutils.to_str( + thin._get_supported_py_config(tops=tops, extended_cfg=ext_cfg) + ) + .strip() + .split(os.linesep) + ) self.assertEqual(len(out), 2) - for t_line in ['py3:3:0', 'py2:2:7']: + for t_line in ["py3:3:0", "py2:2:7"]: self.assertIn(t_line, out) def test_get_supported_py_config_ext_tops(self): - ''' + """ Test collecting proper py-versions. Should return proper ext conf tops. :return: - ''' + """ tops = {} ext_cfg = { - 'solar-interference': { - 'py-version': [2, 6]}, - 'second-system-effect': { - 'py-version': [2, 7]}} - out = salt.utils.stringutils.to_str(thin._get_supported_py_config( - tops=tops, extended_cfg=ext_cfg)).strip().split(os.linesep) - for t_line in ['second-system-effect:2:7', 'solar-interference:2:6']: + "solar-interference": {"py-version": [2, 6]}, + "second-system-effect": {"py-version": [2, 7]}, + } + out = ( + salt.utils.stringutils.to_str( + thin._get_supported_py_config(tops=tops, extended_cfg=ext_cfg) + ) + .strip() + .split(os.linesep) + ) + for t_line in ["second-system-effect:2:7", "solar-interference:2:6"]: self.assertIn(t_line, out) diff --git a/tests/unit/utils/test_timed_subprocess.py b/tests/unit/utils/test_timed_subprocess.py index 88e482f7444..b7e3cbb96c8 100644 --- a/tests/unit/utils/test_timed_subprocess.py +++ b/tests/unit/utils/test_timed_subprocess.py @@ -3,20 +3,19 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase - # Import salt libs import salt.utils.timed_subprocess as timed_subprocess +# Import Salt Testing libs +from tests.support.unit import TestCase + class TestTimedSubprocess(TestCase): - def test_timedproc_with_shell_true_and_list_args(self): - ''' + """ This test confirms the fix for the regression introduced in 1f7d50d. The TimedProc dunder init would result in a traceback if the args were passed as a list and shell=True was set. - ''' - p = timed_subprocess.TimedProc(['echo', 'foo'], shell=True) + """ + p = timed_subprocess.TimedProc(["echo", "foo"], shell=True) del p # Don't need this anymore diff --git a/tests/unit/utils/test_timeout.py b/tests/unit/utils/test_timeout.py index 14d79298269..6073f5eef26 100644 --- a/tests/unit/utils/test_timeout.py +++ b/tests/unit/utils/test_timeout.py @@ -2,15 +2,16 @@ # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import logging import time -# Import test libs -from tests.support.unit import TestCase - # Import Salt libs from salt.utils.timeout import wait_for +# Import test libs +from tests.support.unit import TestCase + log = logging.getLogger(__name__) diff --git a/tests/unit/utils/test_url.py b/tests/unit/utils/test_url.py index e12adeac936..46df564125c 100644 --- a/tests/unit/utils/test_url.py +++ b/tests/unit/utils/test_url.py @@ -6,379 +6,371 @@ from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.utils.platform import salt.utils.url +from tests.support.mock import MagicMock, patch # Import Salt Testing Libs from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) class UrlTestCase(TestCase): - ''' + """ TestCase for salt.utils.url module - ''' + """ # parse tests def test_parse_path(self): - ''' + """ Test parsing an ordinary path - ''' - path = 'interesting?/path&.conf:and other things' + """ + path = "interesting?/path&.conf:and other things" self.assertEqual(salt.utils.url.parse(path), (path, None)) def test_parse_salt_url(self): - ''' + """ Test parsing a 'salt://' URL - ''' - path = '?funny/path with {interesting|chars}' - url = 'salt://' + path + """ + path = "?funny/path with {interesting|chars}" + url = "salt://" + path if salt.utils.platform.is_windows(): - path = '_funny/path with {interesting_chars}' + path = "_funny/path with {interesting_chars}" self.assertEqual(salt.utils.url.parse(url), (path, None)) def test_parse_salt_saltenv(self): - ''' + """ Test parsing a 'salt://' URL with a '?saltenv=' query - ''' - saltenv = 'ambience' - path = '?funny/path&with {interesting|chars}' - url = 'salt://' + path + '?saltenv=' + saltenv + """ + saltenv = "ambience" + path = "?funny/path&with {interesting|chars}" + url = "salt://" + path + "?saltenv=" + saltenv if salt.utils.platform.is_windows(): - path = '_funny/path&with {interesting_chars}' + path = "_funny/path&with {interesting_chars}" self.assertEqual(salt.utils.url.parse(url), (path, saltenv)) # create tests def test_create_url(self): - ''' + """ Test creating a 'salt://' URL - ''' - path = '? interesting/&path.filetype' - url = 'salt://' + path + """ + path = "? interesting/&path.filetype" + url = "salt://" + path if salt.utils.platform.is_windows(): - url = 'salt://_ interesting/&path.filetype' + url = "salt://_ interesting/&path.filetype" self.assertEqual(salt.utils.url.create(path), url) def test_create_url_saltenv(self): - ''' + """ Test creating a 'salt://' URL with a saltenv - ''' - saltenv = 'raumklang' - path = '? interesting/&path.filetype' + """ + saltenv = "raumklang" + path = "? interesting/&path.filetype" if salt.utils.platform.is_windows(): - path = '_ interesting/&path.filetype' + path = "_ interesting/&path.filetype" - url = 'salt://' + path + '?saltenv=' + saltenv + url = "salt://" + path + "?saltenv=" + saltenv self.assertEqual(salt.utils.url.create(path, saltenv), url) # is_escaped tests def test_is_escaped_windows(self): - ''' + """ Test not testing a 'salt://' URL on windows - ''' - url = 'salt://dir/file.ini' + """ + url = "salt://dir/file.ini" - with patch('salt.utils.platform.is_windows', MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): self.assertFalse(salt.utils.url.is_escaped(url)) def test_is_escaped_escaped_path(self): - ''' + """ Test testing an escaped path - ''' - path = '|dir/file.conf?saltenv=basic' + """ + path = "|dir/file.conf?saltenv=basic" self.assertTrue(salt.utils.url.is_escaped(path)) def test_is_escaped_unescaped_path(self): - ''' + """ Test testing an unescaped path - ''' - path = 'dir/file.conf' + """ + path = "dir/file.conf" self.assertFalse(salt.utils.url.is_escaped(path)) def test_is_escaped_escaped_url(self): - ''' + """ Test testing an escaped 'salt://' URL - ''' - url = 'salt://|dir/file.conf?saltenv=basic' + """ + url = "salt://|dir/file.conf?saltenv=basic" self.assertTrue(salt.utils.url.is_escaped(url)) def test_is_escaped_unescaped_url(self): - ''' + """ Test testing an unescaped 'salt://' URL - ''' - url = 'salt://dir/file.conf' + """ + url = "salt://dir/file.conf" self.assertFalse(salt.utils.url.is_escaped(url)) def test_is_escaped_generic_url(self): - ''' + """ Test testing an unescaped 'salt://' URL - ''' - url = 'https://gentoo.org/' + """ + url = "https://gentoo.org/" self.assertFalse(salt.utils.url.is_escaped(url)) # escape tests def test_escape_windows(self): - ''' + """ Test not escaping a 'salt://' URL on windows - ''' - url = 'salt://dir/file.ini' + """ + url = "salt://dir/file.ini" - with patch('salt.utils.platform.is_windows', MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): self.assertEqual(salt.utils.url.escape(url), url) def test_escape_escaped_path(self): - ''' + """ Test escaping an escaped path - ''' - resource = '|dir/file.conf?saltenv=basic' + """ + resource = "|dir/file.conf?saltenv=basic" self.assertEqual(salt.utils.url.escape(resource), resource) def test_escape_unescaped_path(self): - ''' + """ Test escaping an unescaped path - ''' - path = 'dir/file.conf' - escaped_path = '|' + path + """ + path = "dir/file.conf" + escaped_path = "|" + path if salt.utils.platform.is_windows(): escaped_path = path self.assertEqual(salt.utils.url.escape(path), escaped_path) def test_escape_escaped_url(self): - ''' + """ Test testing an escaped 'salt://' URL - ''' - url = 'salt://|dir/file.conf?saltenv=basic' + """ + url = "salt://|dir/file.conf?saltenv=basic" self.assertEqual(salt.utils.url.escape(url), url) def test_escape_unescaped_url(self): - ''' + """ Test testing an unescaped 'salt://' URL - ''' - path = 'dir/file.conf' - url = 'salt://' + path - escaped_url = 'salt://|' + path + """ + path = "dir/file.conf" + url = "salt://" + path + escaped_url = "salt://|" + path if salt.utils.platform.is_windows(): escaped_url = url self.assertEqual(salt.utils.url.escape(url), escaped_url) def test_escape_generic_url(self): - ''' + """ Test testing an unescaped 'salt://' URL - ''' - url = 'https://gentoo.org/' + """ + url = "https://gentoo.org/" self.assertEqual(salt.utils.url.escape(url), url) # unescape tests def test_unescape_windows(self): - ''' + """ Test not escaping a 'salt://' URL on windows - ''' - url = 'salt://dir/file.ini' + """ + url = "salt://dir/file.ini" - with patch('salt.utils.platform.is_windows', MagicMock(return_value=True)): + with patch("salt.utils.platform.is_windows", MagicMock(return_value=True)): self.assertEqual(salt.utils.url.unescape(url), url) def test_unescape_escaped_path(self): - ''' + """ Test escaping an escaped path - ''' - resource = 'dir/file.conf?saltenv=basic' - escaped_path = '|' + resource + """ + resource = "dir/file.conf?saltenv=basic" + escaped_path = "|" + resource self.assertEqual(salt.utils.url.unescape(escaped_path), resource) def test_unescape_unescaped_path(self): - ''' + """ Test escaping an unescaped path - ''' - path = 'dir/file.conf' + """ + path = "dir/file.conf" self.assertEqual(salt.utils.url.unescape(path), path) def test_unescape_escaped_url(self): - ''' + """ Test testing an escaped 'salt://' URL - ''' - resource = 'dir/file.conf?saltenv=basic' - url = 'salt://' + resource - escaped_url = 'salt://|' + resource + """ + resource = "dir/file.conf?saltenv=basic" + url = "salt://" + resource + escaped_url = "salt://|" + resource self.assertEqual(salt.utils.url.unescape(escaped_url), url) def test_unescape_unescaped_url(self): - ''' + """ Test testing an unescaped 'salt://' URL - ''' - url = 'salt://dir/file.conf' + """ + url = "salt://dir/file.conf" self.assertEqual(salt.utils.url.unescape(url), url) def test_unescape_generic_url(self): - ''' + """ Test testing an unescaped 'salt://' URL - ''' - url = 'https://gentoo.org/' + """ + url = "https://gentoo.org/" self.assertEqual(salt.utils.url.unescape(url), url) # add_env tests def test_add_env_not_salt(self): - ''' + """ Test not adding a saltenv to a non 'salt://' URL - ''' - saltenv = 'higgs' - url = 'https://pdg.lbl.gov/' + """ + saltenv = "higgs" + url = "https://pdg.lbl.gov/" self.assertEqual(salt.utils.url.add_env(url, saltenv), url) def test_add_env(self): - ''' + """ Test adding a saltenv to a 'salt://' URL - ''' - saltenv = 'erstwhile' - url = 'salt://salted/file.conf' - url_env = url + '?saltenv=' + saltenv + """ + saltenv = "erstwhile" + url = "salt://salted/file.conf" + url_env = url + "?saltenv=" + saltenv self.assertEqual(salt.utils.url.add_env(url, saltenv), url_env) # split_env tests def test_split_env_non_salt(self): - ''' + """ Test not splitting a saltenv from a non 'salt://' URL - ''' - saltenv = 'gravitodynamics' - url = 'https://arxiv.org/find/all/?' + saltenv + """ + saltenv = "gravitodynamics" + url = "https://arxiv.org/find/all/?" + saltenv self.assertEqual(salt.utils.url.split_env(url), (url, None)) def test_split_env(self): - ''' + """ Test splitting a 'salt://' URL - ''' - saltenv = 'elsewhere' - url = 'salt://salted/file.conf' - url_env = url + '?saltenv=' + saltenv + """ + saltenv = "elsewhere" + url = "salt://salted/file.conf" + url_env = url + "?saltenv=" + saltenv self.assertEqual(salt.utils.url.split_env(url_env), (url, saltenv)) # validate tests def test_validate_valid(self): - ''' + """ Test URL valid validation - ''' - url = 'salt://config/file.name?saltenv=vapid' - protos = ['salt', 'pepper', 'cinnamon', 'melange'] + """ + url = "salt://config/file.name?saltenv=vapid" + protos = ["salt", "pepper", "cinnamon", "melange"] self.assertTrue(salt.utils.url.validate(url, protos)) def test_validate_invalid(self): - ''' + """ Test URL invalid validation - ''' - url = 'cumin://config/file.name?saltenv=vapid' - protos = ['salt', 'pepper', 'cinnamon', 'melange'] + """ + url = "cumin://config/file.name?saltenv=vapid" + protos = ["salt", "pepper", "cinnamon", "melange"] self.assertFalse(salt.utils.url.validate(url, protos)) # strip tests def test_strip_url_with_scheme(self): - ''' + """ Test stripping of URL scheme - ''' - scheme = 'git+salt+rsync+AYB://' - resource = 'all/the/things.stuff;parameter?query=I guess' + """ + scheme = "git+salt+rsync+AYB://" + resource = "all/the/things.stuff;parameter?query=I guess" url = scheme + resource self.assertEqual(salt.utils.url.strip_proto(url), resource) def test_strip_url_without_scheme(self): - ''' + """ Test stripping of a URL without a scheme - ''' - resource = 'all/the/things.stuff;parameter?query=I guess' + """ + resource = "all/the/things.stuff;parameter?query=I guess" self.assertEqual(salt.utils.url.strip_proto(resource), resource) def test_http_basic_auth(self): - ''' + """ Tests that adding basic auth to a URL works as expected - ''' + """ # ((user, password), expected) tuples test_inputs = ( - ((None, None), 'http://example.com'), - (('user', None), 'http://user@example.com'), - (('user', 'pass'), 'http://user:pass@example.com'), + ((None, None), "http://example.com"), + (("user", None), "http://user@example.com"), + (("user", "pass"), "http://user:pass@example.com"), ) for (user, password), expected in test_inputs: kwargs = { - 'url': 'http://example.com', - 'user': user, - 'password': password, + "url": "http://example.com", + "user": user, + "password": password, } # Test http result = salt.utils.url.add_http_basic_auth(**kwargs) self.assertEqual(result, expected) # Test https - kwargs['url'] = kwargs['url'].replace('http://', 'https://', 1) - expected = expected.replace('http://', 'https://', 1) + kwargs["url"] = kwargs["url"].replace("http://", "https://", 1) + expected = expected.replace("http://", "https://", 1) result = salt.utils.url.add_http_basic_auth(**kwargs) self.assertEqual(result, expected) def test_http_basic_auth_https_only(self): - ''' + """ Tests that passing a non-https URL with https_only=True will raise a ValueError. - ''' + """ kwargs = { - 'url': 'http://example.com', - 'user': 'foo', - 'password': 'bar', - 'https_only': True, + "url": "http://example.com", + "user": "foo", + "password": "bar", + "https_only": True, } - self.assertRaises( - ValueError, - salt.utils.url.add_http_basic_auth, - **kwargs - ) + self.assertRaises(ValueError, salt.utils.url.add_http_basic_auth, **kwargs) def test_redact_http_basic_auth(self): sensitive_outputs = ( - 'https://deadbeaf@example.com', - 'https://user:pw@example.com', + "https://deadbeaf@example.com", + "https://user:pw@example.com", ) - sanitized = 'https://<redacted>@example.com' + sanitized = "https://<redacted>@example.com" for sensitive_output in sensitive_outputs: result = salt.utils.url.redact_http_basic_auth(sensitive_output) self.assertEqual(result, sanitized) def test_redact_non_auth_output(self): - non_auth_output = 'This is just normal output' + non_auth_output = "This is just normal output" self.assertEqual( - non_auth_output, - salt.utils.url.redact_http_basic_auth(non_auth_output) + non_auth_output, salt.utils.url.redact_http_basic_auth(non_auth_output) ) diff --git a/tests/unit/utils/test_verify.py b/tests/unit/utils/test_verify.py index a90c4192b4b..e6a93c7d34a 100644 --- a/tests/unit/utils/test_verify.py +++ b/tests/unit/utils/test_verify.py @@ -1,86 +1,82 @@ # -*- coding: utf-8 -*- -''' +""" Test the verification routines -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import getpass import os -import sys -import stat import shutil -import tempfile import socket - -# Import third party libs -if sys.platform.startswith('win'): - import win32file -else: - import resource - -# Import Salt Testing libs -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import skipIf, TestCase -from tests.support.helpers import ( - requires_network, - TstSuiteLoggingHandler -) -from tests.support.mock import ( - MagicMock, - patch, -) +import stat +import sys +import tempfile # Import salt libs import salt.utils.files import salt.utils.platform -from salt.utils.verify import ( - check_user, - verify_env, - verify_socket, - zmq_version, - check_max_open_files, - valid_id, - log, - verify_log, - verify_logs_filter, - verify_log_files, -) # Import 3rd-party libs from salt.ext import six from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +from salt.utils.verify import ( + check_max_open_files, + check_user, + log, + valid_id, + verify_env, + verify_log, + verify_log_files, + verify_logs_filter, + verify_socket, + zmq_version, +) +from tests.support.helpers import TstSuiteLoggingHandler, requires_network +from tests.support.mock import MagicMock, patch + +# Import Salt Testing libs +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + +# Import third party libs +if sys.platform.startswith("win"): + import win32file +else: + import resource class TestVerify(TestCase): - ''' + """ Verify module tests - ''' + """ def test_valid_id_exception_handler(self): - ''' + """ Ensure we just return False if we pass in invalid or undefined paths. Refs #8259 - ''' - opts = {'pki_dir': '/tmp/whatever'} + """ + opts = {"pki_dir": "/tmp/whatever"} self.assertFalse(valid_id(opts, None)) def test_valid_id_pathsep(self): - ''' + """ Path separators in id should make it invalid - ''' - opts = {'pki_dir': '/tmp/whatever'} + """ + opts = {"pki_dir": "/tmp/whatever"} # We have to test both path separators because os.path.normpath will # convert forward slashes to backslashes on Windows. - for pathsep in ('/', '\\'): - self.assertFalse(valid_id(opts, pathsep.join(('..', 'foobar')))) + for pathsep in ("/", "\\"): + self.assertFalse(valid_id(opts, pathsep.join(("..", "foobar")))) def test_zmq_verify(self): self.assertTrue(zmq_version()) def test_zmq_verify_insufficient(self): import zmq - with patch.object(zmq, '__version__', '2.1.0'): + + with patch.object(zmq, "__version__", "2.1.0"): self.assertFalse(zmq_version()) def test_user(self): @@ -95,25 +91,26 @@ class TestVerify(TestCase): def write(self, data): self.output += data + stderr = sys.stderr writer = FakeWriter() sys.stderr = writer # Now run the test - if sys.platform.startswith('win'): - self.assertTrue(check_user('nouser')) + if sys.platform.startswith("win"): + self.assertTrue(check_user("nouser")) else: - self.assertFalse(check_user('nouser')) + self.assertFalse(check_user("nouser")) # Restore sys.stderr sys.stderr = stderr if writer.output != 'CRITICAL: User not found: "nouser"\n': # If there's a different error catch, write it to sys.stderr sys.stderr.write(writer.output) - @skipIf(salt.utils.platform.is_windows(), 'No verify_env Windows') + @skipIf(salt.utils.platform.is_windows(), "No verify_env Windows") def test_verify_env(self): root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - var_dir = os.path.join(root_dir, 'var', 'log', 'salt') - key_dir = os.path.join(root_dir, 'key_dir') + var_dir = os.path.join(root_dir, "var", "log", "salt") + key_dir = os.path.join(root_dir, "key_dir") verify_env([var_dir], getpass.getuser(), root_dir=root_dir) self.assertTrue(os.path.exists(var_dir)) dir_stat = os.stat(var_dir) @@ -124,12 +121,12 @@ class TestVerify(TestCase): @requires_network(only_local_network=True) def test_verify_socket(self): - self.assertTrue(verify_socket('', 18000, 18001)) + self.assertTrue(verify_socket("", 18000, 18001)) if socket.has_ipv6: # Only run if Python is built with IPv6 support; otherwise # this will just fail. try: - self.assertTrue(verify_socket('::', 18000, 18001)) + self.assertTrue(verify_socket("::", 18000, 18001)) except socket.error as serr: # Python has IPv6 enabled, but the system cannot create # IPv6 sockets (otherwise the test would return a bool) @@ -141,85 +138,84 @@ class TestVerify(TestCase): def test_max_open_files(self): with TstSuiteLoggingHandler() as handler: - logmsg_dbg = ( - 'DEBUG:This salt-master instance has accepted {0} minion keys.' - ) + logmsg_dbg = "DEBUG:This salt-master instance has accepted {0} minion keys." logmsg_chk = ( - '{0}:The number of accepted minion keys({1}) should be lower ' - 'than 1/4 of the max open files soft setting({2}). According ' - 'to the system\'s hard limit, there\'s still a margin of {3} ' - 'to raise the salt\'s max_open_files setting. Please consider ' - 'raising this value.' + "{0}:The number of accepted minion keys({1}) should be lower " + "than 1/4 of the max open files soft setting({2}). According " + "to the system's hard limit, there's still a margin of {3} " + "to raise the salt's max_open_files setting. Please consider " + "raising this value." ) logmsg_crash = ( - '{0}:The number of accepted minion keys({1}) should be lower ' - 'than 1/4 of the max open files soft setting({2}). ' - 'salt-master will crash pretty soon! According to the ' - 'system\'s hard limit, there\'s still a margin of {3} to ' - 'raise the salt\'s max_open_files setting. Please consider ' - 'raising this value.' + "{0}:The number of accepted minion keys({1}) should be lower " + "than 1/4 of the max open files soft setting({2}). " + "salt-master will crash pretty soon! According to the " + "system's hard limit, there's still a margin of {3} to " + "raise the salt's max_open_files setting. Please consider " + "raising this value." ) - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): logmsg_crash = ( - '{0}:The number of accepted minion keys({1}) should be lower ' - 'than 1/4 of the max open files soft setting({2}). ' - 'salt-master will crash pretty soon! Please consider ' - 'raising this value.' + "{0}:The number of accepted minion keys({1}) should be lower " + "than 1/4 of the max open files soft setting({2}). " + "salt-master will crash pretty soon! Please consider " + "raising this value." ) - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): # Check the Windows API for more detail on this # http://msdn.microsoft.com/en-us/library/xt874334(v=vs.71).aspx # and the python binding http://timgolden.me.uk/pywin32-docs/win32file.html mof_s = mof_h = win32file._getmaxstdio() else: mof_s, mof_h = resource.getrlimit(resource.RLIMIT_NOFILE) - tempdir = tempfile.mkdtemp(prefix='fake-keys') - keys_dir = os.path.join(tempdir, 'minions') + tempdir = tempfile.mkdtemp(prefix="fake-keys") + keys_dir = os.path.join(tempdir, "minions") os.makedirs(keys_dir) mof_test = 256 - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): win32file._setmaxstdio(mof_test) else: resource.setrlimit(resource.RLIMIT_NOFILE, (mof_test, mof_h)) try: prev = 0 - for newmax, level in ((24, None), (66, 'INFO'), - (127, 'WARNING'), (196, 'CRITICAL')): + for newmax, level in ( + (24, None), + (66, "INFO"), + (127, "WARNING"), + (196, "CRITICAL"), + ): for n in range(prev, newmax): kpath = os.path.join(keys_dir, six.text_type(n)) - with salt.utils.files.fopen(kpath, 'w') as fp_: - fp_.write(str(n)) # future lint: disable=blacklisted-function + with salt.utils.files.fopen(kpath, "w") as fp_: + fp_.write( + str(n) + ) # future lint: disable=blacklisted-function - opts = { - 'max_open_files': newmax, - 'pki_dir': tempdir - } + opts = {"max_open_files": newmax, "pki_dir": tempdir} check_max_open_files(opts) if level is None: # No log message is triggered, only the DEBUG one which # tells us how many minion keys were accepted. - self.assertEqual( - [logmsg_dbg.format(newmax)], handler.messages - ) + self.assertEqual([logmsg_dbg.format(newmax)], handler.messages) else: - self.assertIn( - logmsg_dbg.format(newmax), handler.messages - ) + self.assertIn(logmsg_dbg.format(newmax), handler.messages) self.assertIn( logmsg_chk.format( level, newmax, mof_test, - mof_test - newmax if sys.platform.startswith('win') else mof_h - newmax, + mof_test - newmax + if sys.platform.startswith("win") + else mof_h - newmax, ), - handler.messages + handler.messages, ) handler.clear() prev = newmax @@ -227,62 +223,63 @@ class TestVerify(TestCase): newmax = mof_test for n in range(prev, newmax): kpath = os.path.join(keys_dir, six.text_type(n)) - with salt.utils.files.fopen(kpath, 'w') as fp_: + with salt.utils.files.fopen(kpath, "w") as fp_: fp_.write(str(n)) # future lint: disable=blacklisted-function - opts = { - 'max_open_files': newmax, - 'pki_dir': tempdir - } + opts = {"max_open_files": newmax, "pki_dir": tempdir} check_max_open_files(opts) self.assertIn(logmsg_dbg.format(newmax), handler.messages) self.assertIn( logmsg_crash.format( - 'CRITICAL', + "CRITICAL", newmax, mof_test, - mof_test - newmax if sys.platform.startswith('win') else mof_h - newmax, + mof_test - newmax + if sys.platform.startswith("win") + else mof_h - newmax, ), - handler.messages + handler.messages, ) handler.clear() except IOError as err: if err.errno == 24: # Too many open files - self.skipTest('We\'ve hit the max open files setting') + self.skipTest("We've hit the max open files setting") raise finally: - if sys.platform.startswith('win'): + if sys.platform.startswith("win"): win32file._setmaxstdio(mof_h) else: resource.setrlimit(resource.RLIMIT_NOFILE, (mof_s, mof_h)) shutil.rmtree(tempdir) def test_verify_log(self): - ''' + """ Test that verify_log works as expected - ''' - message = 'Insecure logging configuration detected! Sensitive data may be logged.' + """ + message = ( + "Insecure logging configuration detected! Sensitive data may be logged." + ) mock_cheese = MagicMock() - with patch.object(log, 'warning', mock_cheese): - verify_log({'log_level': 'cheeseshop'}) + with patch.object(log, "warning", mock_cheese): + verify_log({"log_level": "cheeseshop"}) mock_cheese.assert_called_once_with(message) mock_trace = MagicMock() - with patch.object(log, 'warning', mock_trace): - verify_log({'log_level': 'trace'}) + with patch.object(log, "warning", mock_trace): + verify_log({"log_level": "trace"}) mock_trace.assert_called_once_with(message) mock_none = MagicMock() - with patch.object(log, 'warning', mock_none): + with patch.object(log, "warning", mock_none): verify_log({}) mock_none.assert_called_once_with(message) mock_info = MagicMock() - with patch.object(log, 'warning', mock_info): - verify_log({'log_level': 'info'}) + with patch.object(log, "warning", mock_info): + verify_log({"log_level": "info"}) self.assertTrue(mock_info.call_count == 0) @@ -295,28 +292,28 @@ class TestVerifyLog(TestCase): def test_verify_logs_filter(self): filtered = verify_logs_filter( - ['udp://foo', 'tcp://bar', '/tmp/foo', 'file://tmp/bar'] + ["udp://foo", "tcp://bar", "/tmp/foo", "file://tmp/bar"] ) - assert filtered == ['/tmp/foo'], filtered + assert filtered == ["/tmp/foo"], filtered - @skipIf(salt.utils.platform.is_windows(), 'Not applicable on Windows') + @skipIf(salt.utils.platform.is_windows(), "Not applicable on Windows") def test_verify_log_files_udp_scheme(self): - verify_log_files(['udp://foo'], getpass.getuser()) - self.assertFalse(os.path.isdir(os.path.join(os.getcwd(), 'udp:'))) + verify_log_files(["udp://foo"], getpass.getuser()) + self.assertFalse(os.path.isdir(os.path.join(os.getcwd(), "udp:"))) - @skipIf(salt.utils.platform.is_windows(), 'Not applicable on Windows') + @skipIf(salt.utils.platform.is_windows(), "Not applicable on Windows") def test_verify_log_files_tcp_scheme(self): - verify_log_files(['udp://foo'], getpass.getuser()) - self.assertFalse(os.path.isdir(os.path.join(os.getcwd(), 'tcp:'))) + verify_log_files(["udp://foo"], getpass.getuser()) + self.assertFalse(os.path.isdir(os.path.join(os.getcwd(), "tcp:"))) - @skipIf(salt.utils.platform.is_windows(), 'Not applicable on Windows') + @skipIf(salt.utils.platform.is_windows(), "Not applicable on Windows") def test_verify_log_files_file_scheme(self): - verify_log_files(['file://{}'], getpass.getuser()) - self.assertFalse(os.path.isdir(os.path.join(os.getcwd(), 'file:'))) + verify_log_files(["file://{}"], getpass.getuser()) + self.assertFalse(os.path.isdir(os.path.join(os.getcwd(), "file:"))) - @skipIf(salt.utils.platform.is_windows(), 'Not applicable on Windows') + @skipIf(salt.utils.platform.is_windows(), "Not applicable on Windows") def test_verify_log_files(self): - path = os.path.join(self.tmpdir, 'foo', 'bar.log') + path = os.path.join(self.tmpdir, "foo", "bar.log") self.assertFalse(os.path.exists(path)) verify_log_files([path], getpass.getuser()) self.assertTrue(os.path.exists(path)) diff --git a/tests/unit/utils/test_versions.py b/tests/unit/utils/test_versions.py index 79d6110b50b..4e5216f6ab9 100644 --- a/tests/unit/utils/test_versions.py +++ b/tests/unit/utils/test_versions.py @@ -1,71 +1,72 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.version_test ~~~~~~~~~~~~~~~~~~~~~~~ These tests are copied from python's source `Lib/distutils/tests/test_version.py` Some new examples were added and some adjustments were made to run tests in python 2 and 3 -''' +""" # pylint: disable=string-substitution-usage-error # Import python libs from __future__ import absolute_import, print_function, unicode_literals + +import datetime import os import sys -import datetime import warnings -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch -from tests.support.paths import CODE_DIR - # Import Salt libs import salt.modules.cmdmod -import salt.version import salt.utils.platform import salt.utils.versions -from salt.utils.versions import LooseVersion, StrictVersion +import salt.version # Import 3rd-party libs from salt.ext import six +from salt.utils.versions import LooseVersion, StrictVersion +from tests.support.mock import patch +from tests.support.paths import CODE_DIR + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf if six.PY2: - cmp_method = '__cmp__' + cmp_method = "__cmp__" else: - cmp_method = '_cmp' + cmp_method = "_cmp" class VersionTestCase(TestCase): - def test_prerelease(self): - version = StrictVersion('1.2.3a1') + version = StrictVersion("1.2.3a1") self.assertEqual(version.version, (1, 2, 3)) - self.assertEqual(version.prerelease, ('a', 1)) - self.assertEqual(six.text_type(version), '1.2.3a1') + self.assertEqual(version.prerelease, ("a", 1)) + self.assertEqual(six.text_type(version), "1.2.3a1") - version = StrictVersion('1.2.0') - self.assertEqual(six.text_type(version), '1.2') + version = StrictVersion("1.2.0") + self.assertEqual(six.text_type(version), "1.2") def test_cmp_strict(self): - versions = (('1.5.1', '1.5.2b2', -1), - ('161', '3.10a', ValueError), - ('8.02', '8.02', 0), - ('3.4j', '1996.07.12', ValueError), - ('3.2.pl0', '3.1.1.6', ValueError), - ('2g6', '11g', ValueError), - ('0.9', '2.2', -1), - ('1.2.1', '1.2', 1), - ('1.1', '1.2.2', -1), - ('1.2', '1.1', 1), - ('1.2.1', '1.2.2', -1), - ('1.2.2', '1.2', 1), - ('1.2', '1.2.2', -1), - ('0.4.0', '0.4', 0), - ('1.13++', '5.5.kw', ValueError), - # Added by us - ('1.1.1a1', '1.1.1', -1) - ) + versions = ( + ("1.5.1", "1.5.2b2", -1), + ("161", "3.10a", ValueError), + ("8.02", "8.02", 0), + ("3.4j", "1996.07.12", ValueError), + ("3.2.pl0", "3.1.1.6", ValueError), + ("2g6", "11g", ValueError), + ("0.9", "2.2", -1), + ("1.2.1", "1.2", 1), + ("1.1", "1.2.2", -1), + ("1.2", "1.1", 1), + ("1.2.1", "1.2.2", -1), + ("1.2.2", "1.2", 1), + ("1.2", "1.2.2", -1), + ("0.4.0", "0.4", 0), + ("1.13++", "5.5.kw", ValueError), + # Added by us + ("1.1.1a1", "1.1.1", -1), + ) for v1, v2, wanted in versions: try: @@ -74,76 +75,87 @@ class VersionTestCase(TestCase): if wanted is ValueError: continue else: - raise AssertionError(("cmp(%s, %s) " - "shouldn't raise ValueError") % (v1, v2)) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) + raise AssertionError( + ("cmp(%s, %s) " "shouldn't raise ValueError") % (v1, v2) + ) + self.assertEqual( + res, wanted, "cmp(%s, %s) should be %s, got %s" % (v1, v2, wanted, res) + ) def test_cmp(self): - versions = (('1.5.1', '1.5.2b2', -1), - ('161', '3.10a', 1), - ('8.02', '8.02', 0), - ('3.4j', '1996.07.12', -1), - ('3.2.pl0', '3.1.1.6', 1), - ('2g6', '11g', -1), - ('0.960923', '2.2beta29', -1), - ('1.13++', '5.5.kw', -1), - # Added by us - ('3.10.0-514.el7', '3.10.0-514.6.1.el7', 1), - ('2.2.2', '2.12.1', -1) - ) + versions = ( + ("1.5.1", "1.5.2b2", -1), + ("161", "3.10a", 1), + ("8.02", "8.02", 0), + ("3.4j", "1996.07.12", -1), + ("3.2.pl0", "3.1.1.6", 1), + ("2g6", "11g", -1), + ("0.960923", "2.2beta29", -1), + ("1.13++", "5.5.kw", -1), + # Added by us + ("3.10.0-514.el7", "3.10.0-514.6.1.el7", 1), + ("2.2.2", "2.12.1", -1), + ) for v1, v2, wanted in versions: res = getattr(LooseVersion(v1), cmp_method)(LooseVersion(v2)) - self.assertEqual(res, wanted, - 'cmp(%s, %s) should be %s, got %s' % - (v1, v2, wanted, res)) + self.assertEqual( + res, wanted, "cmp(%s, %s) should be %s, got %s" % (v1, v2, wanted, res) + ) - @skipIf(not salt.utils.platform.is_linux(), 'only need to run on linux') + @skipIf(not salt.utils.platform.is_linux(), "only need to run on linux") def test_spelling_version_name(self): - ''' + """ check the spelling of the version name for the release names in the salt.utils.versions.warn_until call - ''' - query = 'salt.utils.versions.warn_until(' + """ + query = "salt.utils.versions.warn_until(" names = salt.version.SaltStackVersion.NAMES - cmd = 'grep -lr {} -A 1 {}'.format(query, os.path.join(CODE_DIR, 'salt')) + cmd = "grep -lr {} -A 1 {}".format(query, os.path.join(CODE_DIR, "salt")) grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd).split(os.linesep) for line in grep_call: - num_cmd = salt.modules.cmdmod.run_stdout('grep -c {0} {1}'.format(query, line)) - ver_cmd = salt.modules.cmdmod.run_stdout('grep {0} {1} -A 1'.format(query, line)) - if 'pyc' in line: + num_cmd = salt.modules.cmdmod.run_stdout( + "grep -c {0} {1}".format(query, line) + ) + ver_cmd = salt.modules.cmdmod.run_stdout( + "grep {0} {1} -A 1".format(query, line) + ) + if "pyc" in line: break match = 0 for key in names: if key in ver_cmd: match = match + (ver_cmd.count(key)) - if 'utils/__init__.py' in line: + if "utils/__init__.py" in line: # work around for utils/__init__.py because # it includes the warn_utils function match = match + 1 - self.assertEqual(match, int(num_cmd), msg='The file: {0} has an ' - 'incorrect spelling for the release name in the warn_utils ' - 'call: {1}. Expecting one of these release names: ' - '{2}'.format(line, ver_cmd, names)) + self.assertEqual( + match, + int(num_cmd), + msg="The file: {0} has an " + "incorrect spelling for the release name in the warn_utils " + "call: {1}. Expecting one of these release names: " + "{2}".format(line, ver_cmd, names), + ) class VersionFuncsTestCase(TestCase): - def test_compare(self): - ret = salt.utils.versions.compare('1.0', '==', '1.0') + ret = salt.utils.versions.compare("1.0", "==", "1.0") self.assertTrue(ret) - ret = salt.utils.versions.compare('1.0', '!=', '1.0') + ret = salt.utils.versions.compare("1.0", "!=", "1.0") self.assertFalse(ret) - with patch.object(salt.utils.versions, 'log') as log_mock: - ret = salt.utils.versions.compare('1.0', 'HAH I AM NOT A COMP OPERATOR! I AM YOUR FATHER!', '1.0') + with patch.object(salt.utils.versions, "log") as log_mock: + ret = salt.utils.versions.compare( + "1.0", "HAH I AM NOT A COMP OPERATOR! I AM YOUR FATHER!", "1.0" + ) self.assertTrue(log_mock.error.called) def test_kwargs_warn_until(self): @@ -152,147 +164,156 @@ class VersionFuncsTestCase(TestCase): def test_warn_until_warning_raised(self): # We *always* want *all* warnings thrown on this module - warnings.filterwarnings('always', '', DeprecationWarning, __name__) + warnings.filterwarnings("always", "", DeprecationWarning, __name__) def raise_warning(_version_info_=(0, 16, 0)): salt.utils.versions.warn_until( - (0, 17), 'Deprecation Message!', - _version_info_=_version_info_ - + (0, 17), "Deprecation Message!", _version_info_=_version_info_ ) def raise_named_version_warning(_version_info_=(0, 16, 0)): salt.utils.versions.warn_until( - 'Hydrogen', 'Deprecation Message!', - _version_info_=_version_info_ + "Hydrogen", "Deprecation Message!", _version_info_=_version_info_ ) # raise_warning should show warning until version info is >= (0, 17) with warnings.catch_warnings(record=True) as recorded_warnings: raise_warning() self.assertEqual( - 'Deprecation Message!', six.text_type(recorded_warnings[0].message) + "Deprecation Message!", six.text_type(recorded_warnings[0].message) ) # raise_warning should show warning until version info is >= (0, 17) with warnings.catch_warnings(record=True) as recorded_warnings: raise_named_version_warning() self.assertEqual( - 'Deprecation Message!', six.text_type(recorded_warnings[0].message) + "Deprecation Message!", six.text_type(recorded_warnings[0].message) ) # the deprecation warning is not issued because we passed # _dont_call_warning with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until( - (0, 17), 'Foo', _dont_call_warnings=True, - _version_info_=(0, 16) + (0, 17), "Foo", _dont_call_warnings=True, _version_info_=(0, 16) ) self.assertEqual(0, len(recorded_warnings)) # Let's set version info to (0, 17), a RuntimeError should be raised with self.assertRaisesRegex( - RuntimeError, - r'The warning triggered on filename \'(.*)test_versions.py\', ' - r'line number ([\d]+), is supposed to be shown until version ' - r'0.17.0 is released. Current version is now 0.17.0. ' - r'Please remove the warning.'): + RuntimeError, + r"The warning triggered on filename \'(.*)test_versions.py\', " + r"line number ([\d]+), is supposed to be shown until version " + r"0.17.0 is released. Current version is now 0.17.0. " + r"Please remove the warning.", + ): raise_warning(_version_info_=(0, 17, 0)) # Let's set version info to (0, 17), a RuntimeError should be raised with self.assertRaisesRegex( - RuntimeError, - r'The warning triggered on filename \'(.*)test_versions.py\', ' - r'line number ([\d]+), is supposed to be shown until version ' - r'(.*) is released. Current version is now ' - r'([\d.]+). Please remove the warning.'): - raise_named_version_warning(_version_info_=(getattr(sys, 'maxint', None) or getattr(sys, 'maxsize'), 16, 0)) + RuntimeError, + r"The warning triggered on filename \'(.*)test_versions.py\', " + r"line number ([\d]+), is supposed to be shown until version " + r"(.*) is released. Current version is now " + r"([\d.]+). Please remove the warning.", + ): + raise_named_version_warning( + _version_info_=( + getattr(sys, "maxint", None) or getattr(sys, "maxsize"), + 16, + 0, + ) + ) # Even though we're calling warn_until, we pass _dont_call_warnings # because we're only after the RuntimeError with self.assertRaisesRegex( - RuntimeError, - r'The warning triggered on filename \'(.*)test_versions.py\', ' - r'line number ([\d]+), is supposed to be shown until version ' - r'0.17.0 is released. Current version is now ' - r'(.*). Please remove the warning.'): - salt.utils.versions.warn_until( - (0, 17), 'Foo', _dont_call_warnings=True - ) + RuntimeError, + r"The warning triggered on filename \'(.*)test_versions.py\', " + r"line number ([\d]+), is supposed to be shown until version " + r"0.17.0 is released. Current version is now " + r"(.*). Please remove the warning.", + ): + salt.utils.versions.warn_until((0, 17), "Foo", _dont_call_warnings=True) with self.assertRaisesRegex( - RuntimeError, - r'The warning triggered on filename \'(.*)test_versions.py\', ' - r'line number ([\d]+), is supposed to be shown until version ' - r'(.*) is released. Current version is now ' - r'(.*). Please remove the warning.'): + RuntimeError, + r"The warning triggered on filename \'(.*)test_versions.py\', " + r"line number ([\d]+), is supposed to be shown until version " + r"(.*) is released. Current version is now " + r"(.*). Please remove the warning.", + ): salt.utils.versions.warn_until( - 'Hydrogen', 'Foo', _dont_call_warnings=True, - _version_info_=(getattr(sys, 'maxint', None) or getattr(sys, 'maxsize'), 16, 0) + "Hydrogen", + "Foo", + _dont_call_warnings=True, + _version_info_=( + getattr(sys, "maxint", None) or getattr(sys, "maxsize"), + 16, + 0, + ), ) # version on the deprecation message gets properly formatted with warnings.catch_warnings(record=True) as recorded_warnings: - vrs = salt.version.SaltStackVersion.from_name('Helium') + vrs = salt.version.SaltStackVersion.from_name("Helium") salt.utils.versions.warn_until( - 'Helium', 'Deprecation Message until {version}!', - _version_info_=(vrs.major - 1, 0) + "Helium", + "Deprecation Message until {version}!", + _version_info_=(vrs.major - 1, 0), ) self.assertEqual( - 'Deprecation Message until {0}!'.format(vrs.formatted_version), - six.text_type(recorded_warnings[0].message) + "Deprecation Message until {0}!".format(vrs.formatted_version), + six.text_type(recorded_warnings[0].message), ) def test_kwargs_warn_until_warning_raised(self): # We *always* want *all* warnings thrown on this module - warnings.filterwarnings('always', '', DeprecationWarning, __name__) + warnings.filterwarnings("always", "", DeprecationWarning, __name__) def raise_warning(**kwargs): - _version_info_ = kwargs.pop('_version_info_', (0, 16, 0)) + _version_info_ = kwargs.pop("_version_info_", (0, 16, 0)) salt.utils.versions.kwargs_warn_until( - kwargs, - (0, 17), - _version_info_=_version_info_ + kwargs, (0, 17), _version_info_=_version_info_ ) # raise_warning({...}) should show warning until version info is >= (0, 17) with warnings.catch_warnings(record=True) as recorded_warnings: raise_warning(foo=42) # with a kwarg self.assertEqual( - 'The following parameter(s) have been deprecated and ' - 'will be removed in \'0.17.0\': \'foo\'.', - six.text_type(recorded_warnings[0].message) + "The following parameter(s) have been deprecated and " + "will be removed in '0.17.0': 'foo'.", + six.text_type(recorded_warnings[0].message), ) # With no **kwargs, should not show warning until version info is >= (0, 17) with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.kwargs_warn_until( - {}, # no kwargs - (0, 17), - _version_info_=(0, 16, 0) + {}, (0, 17), _version_info_=(0, 16, 0) # no kwargs ) self.assertEqual(0, len(recorded_warnings)) # Let's set version info to (0, 17), a RuntimeError should be raised # regardless of whether or not we pass any **kwargs. with self.assertRaisesRegex( - RuntimeError, - r'The warning triggered on filename \'(.*)test_versions.py\', ' - r'line number ([\d]+), is supposed to be shown until version ' - r'0.17.0 is released. Current version is now 0.17.0. ' - r'Please remove the warning.'): + RuntimeError, + r"The warning triggered on filename \'(.*)test_versions.py\', " + r"line number ([\d]+), is supposed to be shown until version " + r"0.17.0 is released. Current version is now 0.17.0. " + r"Please remove the warning.", + ): raise_warning(_version_info_=(0, 17)) # no kwargs with self.assertRaisesRegex( - RuntimeError, - r'The warning triggered on filename \'(.*)test_versions.py\', ' - r'line number ([\d]+), is supposed to be shown until version ' - r'0.17.0 is released. Current version is now 0.17.0. ' - r'Please remove the warning.'): - raise_warning(bar='baz', qux='quux', _version_info_=(0, 17)) # some kwargs + RuntimeError, + r"The warning triggered on filename \'(.*)test_versions.py\', " + r"line number ([\d]+), is supposed to be shown until version " + r"0.17.0 is released. Current version is now 0.17.0. " + r"Please remove the warning.", + ): + raise_warning(bar="baz", qux="quux", _version_info_=(0, 17)) # some kwargs def test_warn_until_date_warning_raised(self): # We *always* want *all* warnings thrown on this module - warnings.filterwarnings('always', '', DeprecationWarning, __name__) + warnings.filterwarnings("always", "", DeprecationWarning, __name__) _current_date = datetime.date(2000, 1, 1) @@ -300,76 +321,76 @@ class VersionFuncsTestCase(TestCase): with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until_date( datetime.date(2000, 1, 2), - 'Deprecation Message!', - _current_date=_current_date + "Deprecation Message!", + _current_date=_current_date, ) self.assertEqual( - 'Deprecation Message!', six.text_type(recorded_warnings[0].message) + "Deprecation Message!", six.text_type(recorded_warnings[0].message) ) # Test warning with datetime.datetime instance with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until_date( datetime.datetime(2000, 1, 2), - 'Deprecation Message!', - _current_date=_current_date + "Deprecation Message!", + _current_date=_current_date, ) self.assertEqual( - 'Deprecation Message!', six.text_type(recorded_warnings[0].message) + "Deprecation Message!", six.text_type(recorded_warnings[0].message) ) # Test warning with date as a string with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until_date( - '20000102', - 'Deprecation Message!', - _current_date=_current_date + "20000102", "Deprecation Message!", _current_date=_current_date ) self.assertEqual( - 'Deprecation Message!', six.text_type(recorded_warnings[0].message) + "Deprecation Message!", six.text_type(recorded_warnings[0].message) ) # the deprecation warning is not issued because we passed # _dont_call_warning with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until_date( - '20000102', - 'Deprecation Message!', + "20000102", + "Deprecation Message!", _dont_call_warnings=True, - _current_date=_current_date + _current_date=_current_date, ) self.assertEqual(0, len(recorded_warnings)) # Let's test for RuntimeError raise with self.assertRaisesRegex( - RuntimeError, - r'Deprecation Message! This warning\(now exception\) triggered on ' - r'filename \'(.*)test_versions.py\', line number ([\d]+), is ' - r'supposed to be shown until ([\d-]+). Today is ([\d-]+). ' - r'Please remove the warning.'): - salt.utils.versions.warn_until_date('20000101', 'Deprecation Message!') + RuntimeError, + r"Deprecation Message! This warning\(now exception\) triggered on " + r"filename \'(.*)test_versions.py\', line number ([\d]+), is " + r"supposed to be shown until ([\d-]+). Today is ([\d-]+). " + r"Please remove the warning.", + ): + salt.utils.versions.warn_until_date("20000101", "Deprecation Message!") # Even though we're calling warn_until_date, we pass _dont_call_warnings # because we're only after the RuntimeError with self.assertRaisesRegex( - RuntimeError, - r'Deprecation Message! This warning\(now exception\) triggered on ' - r'filename \'(.*)test_versions.py\', line number ([\d]+), is ' - r'supposed to be shown until ([\d-]+). Today is ([\d-]+). ' - r'Please remove the warning.'): + RuntimeError, + r"Deprecation Message! This warning\(now exception\) triggered on " + r"filename \'(.*)test_versions.py\', line number ([\d]+), is " + r"supposed to be shown until ([\d-]+). Today is ([\d-]+). " + r"Please remove the warning.", + ): salt.utils.versions.warn_until_date( - '20000101', - 'Deprecation Message!', + "20000101", + "Deprecation Message!", _dont_call_warnings=True, - _current_date=_current_date + _current_date=_current_date, ) def test_warn_until_date_bad_strptime_format(self): # We *always* want *all* warnings thrown on this module - warnings.filterwarnings('always', '', DeprecationWarning, __name__) + warnings.filterwarnings("always", "", DeprecationWarning, __name__) # Let's test for RuntimeError raise with self.assertRaisesRegex( - ValueError, - 'time data \'0022\' does not match format \'%Y%m%d\''): - salt.utils.versions.warn_until_date('0022', 'Deprecation Message!') + ValueError, "time data '0022' does not match format '%Y%m%d'" + ): + salt.utils.versions.warn_until_date("0022", "Deprecation Message!") diff --git a/tests/unit/utils/test_vmware.py b/tests/unit/utils/test_vmware.py index 653db021c9c..a49ffb5c0b8 100644 --- a/tests/unit/utils/test_vmware.py +++ b/tests/unit/utils/test_vmware.py @@ -1,27 +1,19 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Alexandru Bleotu <alexandru.bleotu@morganstanley.com> Tests for cluster related functions in salt.utils.vmware -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals + import base64 import logging import ssl import sys -# Import Salt testing libraries -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import TestCase, skipIf -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import ( - patch, - MagicMock, - PropertyMock, - call, -) +import salt.utils.vmware # Import Salt libraries from salt.exceptions import ( @@ -29,22 +21,30 @@ from salt.exceptions import ( CommandExecutionError, VMwareApiError, VMwareConnectionError, - VMwareRuntimeError, VMwareObjectRetrievalError, + VMwareRuntimeError, VMwareSystemError, ) -import salt.utils.vmware # Import Third Party Libs from salt.ext import six +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, PropertyMock, call, patch + +# Import Salt testing libraries +from tests.support.runtests import RUNTIME_VARS +from tests.support.unit import TestCase, skipIf + try: from pyVmomi import vim, vmodl # pylint: disable=no-name-in-module + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False try: import gssapi + HAS_GSSAPI = True except ImportError: HAS_GSSAPI = False @@ -54,27 +54,31 @@ if sys.version_info[:3] > (2, 7, 8): else: SSL_VALIDATION = False -if hasattr(ssl, '_create_unverified_context'): - ssl_context = 'ssl._create_unverified_context' +if hasattr(ssl, "_create_unverified_context"): + ssl_context = "ssl._create_unverified_context" else: - ssl_context = 'ssl._create_stdlib_context' + ssl_context = "ssl._create_stdlib_context" # Get Logging Started log = logging.getLogger(__name__) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetClusterTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_cluster - ''' + """ def setUp(self): patches = ( - ('salt.utils.vmware.get_managed_object_name', MagicMock()), - ('salt.utils.vmware.get_service_instance_from_managed_object', MagicMock()), - ('salt.utils.vmware.get_mors_with_properties', MagicMock(return_value=[{'name': 'fake_cluster', - 'object': MagicMock()}])) + ("salt.utils.vmware.get_managed_object_name", MagicMock()), + ("salt.utils.vmware.get_service_instance_from_managed_object", MagicMock()), + ( + "salt.utils.vmware.get_mors_with_properties", + MagicMock( + return_value=[{"name": "fake_cluster", "object": MagicMock()}] + ), + ), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -84,258 +88,311 @@ class GetClusterTestCase(TestCase): self.mock_dc = MagicMock() self.mock_cluster1 = MagicMock() self.mock_cluster2 = MagicMock() - self.mock_entries = [{'name': 'fake_cluster1', - 'object': self.mock_cluster1}, - {'name': 'fake_cluster2', - 'object': self.mock_cluster2}] - for attr in ('mock_si', 'mock_dc', 'mock_cluster1', 'mock_cluster2', 'mock_entries'): + self.mock_entries = [ + {"name": "fake_cluster1", "object": self.mock_cluster1}, + {"name": "fake_cluster2", "object": self.mock_cluster2}, + ] + for attr in ( + "mock_si", + "mock_dc", + "mock_cluster1", + "mock_cluster2", + "mock_entries", + ): self.addCleanup(delattr, self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): - salt.utils.vmware.get_cluster(self.mock_dc, 'fake_cluster') + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): + salt.utils.vmware.get_cluster(self.mock_dc, "fake_cluster") mock_get_managed_object_name.assert_called_once_with(self.mock_dc) def test_get_service_instance_from_managed_object(self): mock_dc_name = MagicMock() mock_get_service_instance_from_managed_object = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value=mock_dc_name)): + with patch( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value=mock_dc_name), + ): with patch( - 'salt.utils.vmware.get_service_instance_from_managed_object', - mock_get_service_instance_from_managed_object): + "salt.utils.vmware.get_service_instance_from_managed_object", + mock_get_service_instance_from_managed_object, + ): - salt.utils.vmware.get_cluster(self.mock_dc, 'fake_cluster') + salt.utils.vmware.get_cluster(self.mock_dc, "fake_cluster") mock_get_service_instance_from_managed_object.assert_called_once_with( - self.mock_dc, name=mock_dc_name) + self.mock_dc, name=mock_dc_name + ) def test_traversal_spec_init(self): mock_dc_name = MagicMock() mock_traversal_spec = MagicMock() mock_traversal_spec_ini = MagicMock(return_value=mock_traversal_spec) mock_get_service_instance_from_managed_object = MagicMock() - patch_traversal_spec_str = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec' + patch_traversal_spec_str = ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec" + ) with patch(patch_traversal_spec_str, mock_traversal_spec_ini): - salt.utils.vmware.get_cluster(self.mock_dc, 'fake_cluster') + salt.utils.vmware.get_cluster(self.mock_dc, "fake_cluster") mock_traversal_spec_ini.assert_has_calls( - [call(path='childEntity', - skip=False, - type=vim.Folder), - call(path='hostFolder', - skip=True, - type=vim.Datacenter, - selectSet=[mock_traversal_spec])]) + [ + call(path="childEntity", skip=False, type=vim.Folder), + call( + path="hostFolder", + skip=True, + type=vim.Datacenter, + selectSet=[mock_traversal_spec], + ), + ] + ) def test_get_mors_with_properties_call(self): mock_get_mors_with_properties = MagicMock( - return_value=[{'name': 'fake_cluster', 'object': MagicMock()}]) + return_value=[{"name": "fake_cluster", "object": MagicMock()}] + ) mock_traversal_spec = MagicMock() - patch_traversal_spec_str = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec' + patch_traversal_spec_str = ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec" + ) with patch( - 'salt.utils.vmware.get_service_instance_from_managed_object', - MagicMock(return_value=self.mock_si)): + "salt.utils.vmware.get_service_instance_from_managed_object", + MagicMock(return_value=self.mock_si), + ): - with patch('salt.utils.vmware.get_mors_with_properties', - mock_get_mors_with_properties): - with patch(patch_traversal_spec_str, - MagicMock(return_value=mock_traversal_spec)): + with patch( + "salt.utils.vmware.get_mors_with_properties", + mock_get_mors_with_properties, + ): + with patch( + patch_traversal_spec_str, + MagicMock(return_value=mock_traversal_spec), + ): - salt.utils.vmware.get_cluster(self.mock_dc, 'fake_cluster') + salt.utils.vmware.get_cluster(self.mock_dc, "fake_cluster") mock_get_mors_with_properties.assert_called_once_with( - self.mock_si, vim.ClusterComputeResource, + self.mock_si, + vim.ClusterComputeResource, container_ref=self.mock_dc, - property_list=['name'], - traversal_spec=mock_traversal_spec) + property_list=["name"], + traversal_spec=mock_traversal_spec, + ) def test_get_mors_with_properties_returns_empty_array(self): - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_dc')): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=[])): + with patch( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_dc"), + ): + with patch( + "salt.utils.vmware.get_mors_with_properties", MagicMock(return_value=[]) + ): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - salt.utils.vmware.get_cluster(self.mock_dc, 'fake_cluster') - self.assertEqual(excinfo.exception.strerror, - 'Cluster \'fake_cluster\' was not found in ' - 'datacenter \'fake_dc\'') + salt.utils.vmware.get_cluster(self.mock_dc, "fake_cluster") + self.assertEqual( + excinfo.exception.strerror, + "Cluster 'fake_cluster' was not found in " "datacenter 'fake_dc'", + ) def test_cluster_not_found(self): - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_dc')): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_entries)): + with patch( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_dc"), + ): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_entries), + ): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - salt.utils.vmware.get_cluster(self.mock_dc, 'fake_cluster') - self.assertEqual(excinfo.exception.strerror, - 'Cluster \'fake_cluster\' was not found in ' - 'datacenter \'fake_dc\'') + salt.utils.vmware.get_cluster(self.mock_dc, "fake_cluster") + self.assertEqual( + excinfo.exception.strerror, + "Cluster 'fake_cluster' was not found in " "datacenter 'fake_dc'", + ) def test_cluster_found(self): - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_dc')): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_entries)): - res = salt.utils.vmware.get_cluster(self.mock_dc, 'fake_cluster2') + with patch( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_dc"), + ): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_entries), + ): + res = salt.utils.vmware.get_cluster(self.mock_dc, "fake_cluster2") self.assertEqual(res, self.mock_cluster2) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class CreateClusterTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.create_cluster - ''' + """ def setUp(self): - patches = ( - ('salt.utils.vmware.get_managed_object_name', MagicMock()), - ) + patches = (("salt.utils.vmware.get_managed_object_name", MagicMock()),) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) self.mock_create_cluster_ex = MagicMock() self.mock_dc = MagicMock( - hostFolder=MagicMock(CreateClusterEx=self.mock_create_cluster_ex)) + hostFolder=MagicMock(CreateClusterEx=self.mock_create_cluster_ex) + ) self.mock_cluster_spec = MagicMock() - for attr in ('mock_create_cluster_ex', 'mock_dc', 'mock_cluster_spec'): + for attr in ("mock_create_cluster_ex", "mock_dc", "mock_cluster_spec"): self.addCleanup(delattr, self, attr) def test_get_managed_object_name(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): - salt.utils.vmware.create_cluster(self.mock_dc, 'fake_cluster', - self.mock_cluster_spec) + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): + salt.utils.vmware.create_cluster( + self.mock_dc, "fake_cluster", self.mock_cluster_spec + ) mock_get_managed_object_name.assert_called_once_with(self.mock_dc) def test_create_cluster_call(self): - salt.utils.vmware.create_cluster(self.mock_dc, 'fake_cluster', - self.mock_cluster_spec) + salt.utils.vmware.create_cluster( + self.mock_dc, "fake_cluster", self.mock_cluster_spec + ) self.mock_create_cluster_ex.assert_called_once_with( - 'fake_cluster', self.mock_cluster_spec) + "fake_cluster", self.mock_cluster_spec + ) def test_create_cluster_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_dc.hostFolder.CreateClusterEx = MagicMock( - side_effect=exc) + exc.privilegeId = "Fake privilege" + self.mock_dc.hostFolder.CreateClusterEx = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.create_cluster(self.mock_dc, 'fake_cluster', - self.mock_cluster_spec) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.create_cluster( + self.mock_dc, "fake_cluster", self.mock_cluster_spec + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_create_cluster_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.mock_dc.hostFolder.CreateClusterEx = MagicMock( - side_effect=exc) + exc.msg = "VimFault msg" + self.mock_dc.hostFolder.CreateClusterEx = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.create_cluster(self.mock_dc, 'fake_cluster', - self.mock_cluster_spec) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.create_cluster( + self.mock_dc, "fake_cluster", self.mock_cluster_spec + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_create_cluster_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_dc.hostFolder.CreateClusterEx = MagicMock( - side_effect=exc) + exc.msg = "RuntimeFault msg" + self.mock_dc.hostFolder.CreateClusterEx = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.create_cluster(self.mock_dc, 'fake_cluster', - self.mock_cluster_spec) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.create_cluster( + self.mock_dc, "fake_cluster", self.mock_cluster_spec + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class UpdateClusterTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.update_cluster - ''' + """ def setUp(self): patches = ( - ('salt.utils.vmware.get_managed_object_name', MagicMock()), - ('salt.utils.vmware.wait_for_task', MagicMock()), + ("salt.utils.vmware.get_managed_object_name", MagicMock()), + ("salt.utils.vmware.wait_for_task", MagicMock()), ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) self.mock_task = MagicMock() - self.mock_reconfigure_compute_resource_task = \ - MagicMock(return_value=self.mock_task) - self.mock_cluster = MagicMock(ReconfigureComputeResource_Task= - self.mock_reconfigure_compute_resource_task) + self.mock_reconfigure_compute_resource_task = MagicMock( + return_value=self.mock_task + ) + self.mock_cluster = MagicMock( + ReconfigureComputeResource_Task=self.mock_reconfigure_compute_resource_task + ) self.mock_cluster_spec = MagicMock() - for attr in ('mock_task', 'mock_reconfigure_compute_resource_task', 'mock_cluster', 'mock_cluster_spec'): + for attr in ( + "mock_task", + "mock_reconfigure_compute_resource_task", + "mock_cluster", + "mock_cluster_spec", + ): self.addCleanup(delattr, self, attr) def test_get_managed_object_name(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) mock_get_managed_object_name.assert_called_once_with(self.mock_cluster) def test_reconfigure_compute_resource_task_call(self): salt.utils.vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) self.mock_reconfigure_compute_resource_task.assert_called_once_with( - self.mock_cluster_spec, modify=True) + self.mock_cluster_spec, modify=True + ) def test_reconfigure_compute_resource_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_cluster.ReconfigureComputeResource_Task = \ - MagicMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + self.mock_cluster.ReconfigureComputeResource_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_reconfigure_compute_resource_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.mock_cluster.ReconfigureComputeResource_Task = \ - MagicMock(side_effect=exc) + exc.msg = "VimFault msg" + self.mock_cluster.ReconfigureComputeResource_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_reconfigure_compute_resource_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_cluster.ReconfigureComputeResource_Task = \ - MagicMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + self.mock_cluster.ReconfigureComputeResource_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_wait_for_task_call(self): mock_wait_for_task = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_cluster')): - with patch('salt.utils.vmware.wait_for_task', mock_wait_for_task): - salt.utils.vmware.update_cluster(self.mock_cluster, self.mock_cluster_spec) + with patch( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_cluster"), + ): + with patch("salt.utils.vmware.wait_for_task", mock_wait_for_task): + salt.utils.vmware.update_cluster( + self.mock_cluster, self.mock_cluster_spec + ) mock_wait_for_task.assert_called_once_with( - self.mock_task, 'fake_cluster', 'ClusterUpdateTask') + self.mock_task, "fake_cluster", "ClusterUpdateTask" + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class WaitForTaskTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.wait_for_task - ''' + """ def setUp(self): patches = ( - ('salt.utils.vmware.time.time', MagicMock(return_value=1)), - ('salt.utils.vmware.time.sleep', MagicMock(return_value=None)) + ("salt.utils.vmware.time.time", MagicMock(return_value=1)), + ("salt.utils.vmware.time.sleep", MagicMock(return_value=None)), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -344,94 +401,90 @@ class WaitForTaskTestCase(TestCase): def test_first_task_info_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" mock_task = MagicMock() type(mock_task).info = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_first_task_info_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" mock_task = MagicMock() type(mock_task).info = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_first_task_info_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" mock_task = MagicMock() type(mock_task).info = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_inner_loop_task_info_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" mock_task = MagicMock() mock_info1 = MagicMock() - type(mock_task).info = PropertyMock( - side_effect=[mock_info1, exc]) - type(mock_info1).state = PropertyMock(side_effect=['running', 'bad']) + type(mock_task).info = PropertyMock(side_effect=[mock_info1, exc]) + type(mock_info1).state = PropertyMock(side_effect=["running", "bad"]) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_inner_loop_task_info_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" mock_task = MagicMock() mock_info1 = MagicMock() - type(mock_task).info = PropertyMock( - side_effect=[mock_info1, exc]) - type(mock_info1).state = PropertyMock(side_effect=['running', 'bad']) + type(mock_task).info = PropertyMock(side_effect=[mock_info1, exc]) + type(mock_info1).state = PropertyMock(side_effect=["running", "bad"]) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_inner_loop_task_info_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" mock_task = MagicMock() mock_info1 = MagicMock() - type(mock_task).info = PropertyMock( - side_effect=[mock_info1, exc]) - type(mock_info1).state = PropertyMock(side_effect=['running', 'bad']) + type(mock_task).info = PropertyMock(side_effect=[mock_info1, exc]) + type(mock_info1).state = PropertyMock(side_effect=["running", "bad"]) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_info_state_running(self): # The 'bad' values are invalid in the while loop mock_task = MagicMock() - prop_mock_state = PropertyMock(side_effect=['running', 'bad', 'bad', - 'success']) + prop_mock_state = PropertyMock(side_effect=["running", "bad", "bad", "success"]) prop_mock_result = PropertyMock() type(mock_task.info).state = prop_mock_state type(mock_task.info).result = prop_mock_result - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') + salt.utils.vmware.wait_for_task(mock_task, "fake_instance_name", "task_type") self.assertEqual(prop_mock_state.call_count, 4) self.assertEqual(prop_mock_result.call_count, 1) @@ -439,14 +492,13 @@ class WaitForTaskTestCase(TestCase): mock_task = MagicMock() # The 'fake' values are required to match all the lookups and end the # loop - prop_mock_state = PropertyMock(side_effect=['running', 'fake', 'fake', - 'success']) + prop_mock_state = PropertyMock( + side_effect=["running", "fake", "fake", "success"] + ) prop_mock_result = PropertyMock() type(mock_task.info).state = prop_mock_state type(mock_task.info).result = prop_mock_result - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') + salt.utils.vmware.wait_for_task(mock_task, "fake_instance_name", "task_type") self.assertEqual(prop_mock_state.call_count, 4) self.assertEqual(prop_mock_result.call_count, 1) @@ -454,125 +506,123 @@ class WaitForTaskTestCase(TestCase): mock_task = MagicMock() # The 'fake' values are required to match all the lookups and end the # loop - prop_mock_state = PropertyMock(side_effect=['fake', 'queued', 'fake', - 'fake', 'success']) + prop_mock_state = PropertyMock( + side_effect=["fake", "queued", "fake", "fake", "success"] + ) prop_mock_result = PropertyMock() type(mock_task.info).state = prop_mock_state type(mock_task.info).result = prop_mock_result - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') + salt.utils.vmware.wait_for_task(mock_task, "fake_instance_name", "task_type") self.assertEqual(prop_mock_state.call_count, 5) self.assertEqual(prop_mock_result.call_count, 1) def test_info_state_success(self): mock_task = MagicMock() - prop_mock_state = PropertyMock(return_value='success') + prop_mock_state = PropertyMock(return_value="success") prop_mock_result = PropertyMock() type(mock_task.info).state = prop_mock_state type(mock_task.info).result = prop_mock_result - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') + salt.utils.vmware.wait_for_task(mock_task, "fake_instance_name", "task_type") self.assertEqual(prop_mock_state.call_count, 3) self.assertEqual(prop_mock_result.call_count, 1) def test_info_error_exception(self): mock_task = MagicMock() - prop_mock_state = PropertyMock(return_value='error') - prop_mock_error = PropertyMock(side_effect=Exception('error exc')) + prop_mock_state = PropertyMock(return_value="error") + prop_mock_error = PropertyMock(side_effect=Exception("error exc")) type(mock_task.info).state = prop_mock_state type(mock_task.info).error = prop_mock_error with self.assertRaises(Exception) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(six.text_type(excinfo.exception), 'error exc') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual(six.text_type(excinfo.exception), "error exc") def test_info_error_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" mock_task = MagicMock() - prop_mock_state = PropertyMock(return_value='error') + prop_mock_state = PropertyMock(return_value="error") prop_mock_error = PropertyMock(side_effect=exc) type(mock_task.info).state = prop_mock_state type(mock_task.info).error = prop_mock_error with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_info_error_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" mock_task = MagicMock() - prop_mock_state = PropertyMock(return_value='error') + prop_mock_state = PropertyMock(return_value="error") prop_mock_error = PropertyMock(side_effect=exc) type(mock_task.info).state = prop_mock_state type(mock_task.info).error = prop_mock_error with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_info_error_system_fault(self): exc = vmodl.fault.SystemError() - exc.msg = 'SystemError msg' + exc.msg = "SystemError msg" mock_task = MagicMock() - prop_mock_state = PropertyMock(return_value='error') + prop_mock_state = PropertyMock(return_value="error") prop_mock_error = PropertyMock(side_effect=exc) type(mock_task.info).state = prop_mock_state type(mock_task.info).error = prop_mock_error with self.assertRaises(VMwareSystemError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, 'SystemError msg') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual(excinfo.exception.strerror, "SystemError msg") def test_info_error_invalid_argument_no_fault_message(self): exc = vmodl.fault.InvalidArgument() exc.faultMessage = None - exc.msg = 'InvalidArgumentFault msg' + exc.msg = "InvalidArgumentFault msg" mock_task = MagicMock() - prop_mock_state = PropertyMock(return_value='error') + prop_mock_state = PropertyMock(return_value="error") prop_mock_error = PropertyMock(side_effect=exc) type(mock_task.info).state = prop_mock_state type(mock_task.info).error = prop_mock_error with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, - 'InvalidArgumentFault msg') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual(excinfo.exception.strerror, "InvalidArgumentFault msg") def test_info_error_invalid_argument_with_fault_message(self): exc = vmodl.fault.InvalidArgument() fault_message = vim.LocalizableMessage() - fault_message.message = 'LocalFault msg' + fault_message.message = "LocalFault msg" exc.faultMessage = [fault_message] - exc.msg = 'InvalidArgumentFault msg' + exc.msg = "InvalidArgumentFault msg" mock_task = MagicMock() - prop_mock_state = PropertyMock(return_value='error') + prop_mock_state = PropertyMock(return_value="error") prop_mock_error = PropertyMock(side_effect=exc) type(mock_task.info).state = prop_mock_state type(mock_task.info).error = prop_mock_error with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.wait_for_task(mock_task, - 'fake_instance_name', - 'task_type') - self.assertEqual(excinfo.exception.strerror, - 'InvalidArgumentFault msg (LocalFault msg)') + salt.utils.vmware.wait_for_task( + mock_task, "fake_instance_name", "task_type" + ) + self.assertEqual( + excinfo.exception.strerror, "InvalidArgumentFault msg (LocalFault msg)" + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetMorsWithPropertiesTestCase(TestCase): - ''' + """ Tests for salt.utils.get_mors_with_properties - ''' + """ si = None obj_type = None @@ -589,16 +639,22 @@ class GetMorsWithPropertiesTestCase(TestCase): def test_empty_content(self): get_content = MagicMock(return_value=[]) - with patch('salt.utils.vmware.get_content', get_content): + with patch("salt.utils.vmware.get_content", get_content): ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec) + self.si, + self.obj_type, + self.prop_list, + self.container_ref, + self.traversal_spec, + ) get_content.assert_called_once_with( - self.si, self.obj_type, + self.si, + self.obj_type, property_list=self.prop_list, container_ref=self.container_ref, traversal_spec=self.traversal_spec, - local_properties=False) + local_properties=False, + ) self.assertEqual(ret, []) def test_local_properties_set(self): @@ -612,17 +668,23 @@ class GetMorsWithPropertiesTestCase(TestCase): type(obj_mock).obj = obj_prop get_content = MagicMock(return_value=[obj_mock]) - with patch('salt.utils.vmware.get_content', get_content): + with patch("salt.utils.vmware.get_content", get_content): ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec, - local_properties=True) + self.si, + self.obj_type, + self.prop_list, + self.container_ref, + self.traversal_spec, + local_properties=True, + ) get_content.assert_called_once_with( - self.si, self.obj_type, + self.si, + self.obj_type, property_list=self.prop_list, container_ref=self.container_ref, traversal_spec=self.traversal_spec, - local_properties=True) + local_properties=True, + ) def test_one_element_content(self): obj_mock = MagicMock() @@ -634,20 +696,26 @@ class GetMorsWithPropertiesTestCase(TestCase): obj_prop = PropertyMock(return_value=inner_obj_mock) type(obj_mock).obj = obj_prop get_content = MagicMock(return_value=[obj_mock]) - with patch('salt.utils.vmware.get_content', get_content): + with patch("salt.utils.vmware.get_content", get_content): ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec) + self.si, + self.obj_type, + self.prop_list, + self.container_ref, + self.traversal_spec, + ) get_content.assert_called_once_with( - self.si, self.obj_type, + self.si, + self.obj_type, property_list=self.prop_list, container_ref=self.container_ref, traversal_spec=self.traversal_spec, - local_properties=False) + local_properties=False, + ) self.assertEqual(propSet_prop.call_count, 1) self.assertEqual(obj_prop.call_count, 1) self.assertEqual(len(ret), 1) - self.assertDictEqual(ret[0], {'object': inner_obj_mock}) + self.assertDictEqual(ret[0], {"object": inner_obj_mock}) def test_multiple_element_content(self): # obj1 @@ -670,31 +738,37 @@ class GetMorsWithPropertiesTestCase(TestCase): type(obj2_mock).obj = obj2_obj_prop get_content = MagicMock(return_value=[obj1_mock, obj2_mock]) - with patch('salt.utils.vmware.get_content', get_content): + with patch("salt.utils.vmware.get_content", get_content): ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec) + self.si, + self.obj_type, + self.prop_list, + self.container_ref, + self.traversal_spec, + ) get_content.assert_called_once_with( - self.si, self.obj_type, + self.si, + self.obj_type, property_list=self.prop_list, container_ref=self.container_ref, traversal_spec=self.traversal_spec, - local_properties=False) + local_properties=False, + ) self.assertEqual(obj1_propSet_prop.call_count, 1) self.assertEqual(obj2_propSet_prop.call_count, 1) self.assertEqual(obj1_obj_prop.call_count, 1) self.assertEqual(obj2_obj_prop.call_count, 1) self.assertEqual(len(ret), 2) - self.assertDictEqual(ret[0], {'object': obj1_inner_obj_mock}) - self.assertDictEqual(ret[1], {'object': obj2_inner_obj_mock}) + self.assertDictEqual(ret[0], {"object": obj1_inner_obj_mock}) + self.assertDictEqual(ret[1], {"object": obj2_inner_obj_mock}) def test_one_elem_one_property(self): obj_mock = MagicMock() # property mock prop_set_obj_mock = MagicMock() - prop_set_obj_name_prop = PropertyMock(return_value='prop_name') - prop_set_obj_val_prop = PropertyMock(return_value='prop_value') + prop_set_obj_name_prop = PropertyMock(return_value="prop_name") + prop_set_obj_val_prop = PropertyMock(return_value="prop_value") type(prop_set_obj_mock).name = prop_set_obj_name_prop type(prop_set_obj_mock).val = prop_set_obj_val_prop @@ -708,45 +782,53 @@ class GetMorsWithPropertiesTestCase(TestCase): type(obj_mock).obj = obj_prop get_content = MagicMock(return_value=[obj_mock]) - with patch('salt.utils.vmware.get_content', get_content): + with patch("salt.utils.vmware.get_content", get_content): ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec, - local_properties=False) + self.si, + self.obj_type, + self.prop_list, + self.container_ref, + self.traversal_spec, + local_properties=False, + ) get_content.assert_called_once_with( - self.si, self.obj_type, + self.si, + self.obj_type, property_list=self.prop_list, container_ref=self.container_ref, traversal_spec=self.traversal_spec, - local_properties=False) + local_properties=False, + ) self.assertEqual(propSet_prop.call_count, 1) self.assertEqual(prop_set_obj_name_prop.call_count, 1) self.assertEqual(prop_set_obj_val_prop.call_count, 1) self.assertEqual(obj_prop.call_count, 1) self.assertEqual(len(ret), 1) - self.assertDictEqual(ret[0], {'prop_name': 'prop_value', - 'object': inner_obj_mock}) + self.assertDictEqual( + ret[0], {"prop_name": "prop_value", "object": inner_obj_mock} + ) def test_one_elem_multiple_properties(self): obj_mock = MagicMock() # property1 mock prop_set_obj1_mock = MagicMock() - prop_set_obj1_name_prop = PropertyMock(return_value='prop_name1') - prop_set_obj1_val_prop = PropertyMock(return_value='prop_value1') + prop_set_obj1_name_prop = PropertyMock(return_value="prop_name1") + prop_set_obj1_val_prop = PropertyMock(return_value="prop_value1") type(prop_set_obj1_mock).name = prop_set_obj1_name_prop type(prop_set_obj1_mock).val = prop_set_obj1_val_prop # property2 mock prop_set_obj2_mock = MagicMock() - prop_set_obj2_name_prop = PropertyMock(return_value='prop_name2') - prop_set_obj2_val_prop = PropertyMock(return_value='prop_value2') + prop_set_obj2_name_prop = PropertyMock(return_value="prop_name2") + prop_set_obj2_val_prop = PropertyMock(return_value="prop_value2") type(prop_set_obj2_mock).name = prop_set_obj2_name_prop type(prop_set_obj2_mock).val = prop_set_obj2_val_prop # obj.propSet - propSet_prop = PropertyMock(return_value=[prop_set_obj1_mock, - prop_set_obj2_mock]) + propSet_prop = PropertyMock( + return_value=[prop_set_obj1_mock, prop_set_obj2_mock] + ) type(obj_mock).propSet = propSet_prop # obj.obj @@ -755,16 +837,22 @@ class GetMorsWithPropertiesTestCase(TestCase): type(obj_mock).obj = obj_prop get_content = MagicMock(return_value=[obj_mock]) - with patch('salt.utils.vmware.get_content', get_content): + with patch("salt.utils.vmware.get_content", get_content): ret = salt.utils.vmware.get_mors_with_properties( - self.si, self.obj_type, self.prop_list, - self.container_ref, self.traversal_spec) + self.si, + self.obj_type, + self.prop_list, + self.container_ref, + self.traversal_spec, + ) get_content.assert_called_once_with( - self.si, self.obj_type, + self.si, + self.obj_type, property_list=self.prop_list, container_ref=self.container_ref, traversal_spec=self.traversal_spec, - local_properties=False) + local_properties=False, + ) self.assertEqual(propSet_prop.call_count, 1) self.assertEqual(prop_set_obj1_name_prop.call_count, 1) self.assertEqual(prop_set_obj1_val_prop.call_count, 1) @@ -772,91 +860,126 @@ class GetMorsWithPropertiesTestCase(TestCase): self.assertEqual(prop_set_obj2_val_prop.call_count, 1) self.assertEqual(obj_prop.call_count, 1) self.assertEqual(len(ret), 1) - self.assertDictEqual(ret[0], {'prop_name1': 'prop_value1', - 'prop_name2': 'prop_value2', - 'object': inner_obj_mock}) + self.assertDictEqual( + ret[0], + { + "prop_name1": "prop_value1", + "prop_name2": "prop_value2", + "object": inner_obj_mock, + }, + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetPropertiesOfManagedObjectTestCase(TestCase): - ''' + """ Tests for salt.utils.get_properties_of_managed_object - ''' + """ def setUp(self): patches = ( - ('salt.utils.vmware.get_service_instance_from_managed_object', MagicMock()), - ('salt.utils.vmware.get_mors_with_properties', MagicMock(return_value=[MagicMock()])) + ("salt.utils.vmware.get_service_instance_from_managed_object", MagicMock()), + ( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=[MagicMock()]), + ), ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) self.mock_si = MagicMock() - self.fake_mo_ref = vim.ManagedEntity('Fake') + self.fake_mo_ref = vim.ManagedEntity("Fake") self.mock_props = MagicMock() - self.mock_item_name = {'name': 'fake_name'} + self.mock_item_name = {"name": "fake_name"} self.mock_item = MagicMock() def test_get_service_instance_from_managed_object_call(self): mock_get_instance_from_managed_object = MagicMock() with patch( - 'salt.utils.vmware.get_service_instance_from_managed_object', - mock_get_instance_from_managed_object): + "salt.utils.vmware.get_service_instance_from_managed_object", + mock_get_instance_from_managed_object, + ): salt.utils.vmware.get_properties_of_managed_object( - self.fake_mo_ref, self.mock_props) - mock_get_instance_from_managed_object.assert_called_once_with( - self.fake_mo_ref) + self.fake_mo_ref, self.mock_props + ) + mock_get_instance_from_managed_object.assert_called_once_with(self.fake_mo_ref) def test_get_mors_with_properties_calls(self): mock_get_mors_with_properties = MagicMock(return_value=[MagicMock()]) with patch( - 'salt.utils.vmware.get_service_instance_from_managed_object', - MagicMock(return_value=self.mock_si)): + "salt.utils.vmware.get_service_instance_from_managed_object", + MagicMock(return_value=self.mock_si), + ): - with patch('salt.utils.vmware.get_mors_with_properties', - mock_get_mors_with_properties): + with patch( + "salt.utils.vmware.get_mors_with_properties", + mock_get_mors_with_properties, + ): salt.utils.vmware.get_properties_of_managed_object( - self.fake_mo_ref, self.mock_props) + self.fake_mo_ref, self.mock_props + ) mock_get_mors_with_properties.assert_has_calls( - [call(self.mock_si, vim.ManagedEntity, - container_ref=self.fake_mo_ref, - property_list=['name'], - local_properties=True), - call(self.mock_si, vim.ManagedEntity, - container_ref=self.fake_mo_ref, - property_list=self.mock_props, - local_properties=True)]) + [ + call( + self.mock_si, + vim.ManagedEntity, + container_ref=self.fake_mo_ref, + property_list=["name"], + local_properties=True, + ), + call( + self.mock_si, + vim.ManagedEntity, + container_ref=self.fake_mo_ref, + property_list=self.mock_props, + local_properties=True, + ), + ] + ) def test_managed_object_no_name_property(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(side_effect=[vmodl.query.InvalidProperty(), []])): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(side_effect=[vmodl.query.InvalidProperty(), []]), + ): with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_properties_of_managed_object( - self.fake_mo_ref, self.mock_props) - self.assertEqual('Properties of managed object \'<unnamed>\' weren\'t ' - 'retrieved', excinfo.exception.strerror) + self.fake_mo_ref, self.mock_props + ) + self.assertEqual( + "Properties of managed object '<unnamed>' weren't " "retrieved", + excinfo.exception.strerror, + ) def test_no_items_named_object(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(side_effect=[[self.mock_item_name], []])): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(side_effect=[[self.mock_item_name], []]), + ): with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_properties_of_managed_object( - self.fake_mo_ref, self.mock_props) - self.assertEqual('Properties of managed object \'fake_name\' weren\'t ' - 'retrieved', excinfo.exception.strerror) + self.fake_mo_ref, self.mock_props + ) + self.assertEqual( + "Properties of managed object 'fake_name' weren't " "retrieved", + excinfo.exception.strerror, + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetManagedObjectName(TestCase): - ''' + """ Tests for salt.utils.get_managed_object_name - ''' + """ def setUp(self): patches = ( - ('salt.utils.vmware.get_properties_of_managed_object', MagicMock(return_value={'key': 'value'})), + ( + "salt.utils.vmware.get_properties_of_managed_object", + MagicMock(return_value={"key": "value"}), + ), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -866,11 +989,14 @@ class GetManagedObjectName(TestCase): def test_get_properties_of_managed_object_call(self): mock_get_properties_of_managed_object = MagicMock() - with patch('salt.utils.vmware.get_properties_of_managed_object', - mock_get_properties_of_managed_object): + with patch( + "salt.utils.vmware.get_properties_of_managed_object", + mock_get_properties_of_managed_object, + ): salt.utils.vmware.get_managed_object_name(self.mock_mo_ref) mock_get_properties_of_managed_object.assert_called_once_with( - self.mock_mo_ref, ['name']) + self.mock_mo_ref, ["name"] + ) def test_no_name_in_property_dict(self): ret = salt.utils.vmware.get_managed_object_name(self.mock_mo_ref) @@ -878,27 +1004,31 @@ class GetManagedObjectName(TestCase): def test_return_managed_object_name(self): mock_get_properties_of_managed_object = MagicMock() - with patch('salt.utils.vmware.get_properties_of_managed_object', - MagicMock(return_value={'name': 'fake_name'})): + with patch( + "salt.utils.vmware.get_properties_of_managed_object", + MagicMock(return_value={"name": "fake_name"}), + ): ret = salt.utils.vmware.get_managed_object_name(self.mock_mo_ref) - self.assertEqual(ret, 'fake_name') + self.assertEqual(ret, "fake_name") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetContentTestCase(TestCase): - ''' + """ Tests for salt.utils.get_content - ''' + """ # Method names to be patched - traversal_spec_method_name = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec' - property_spec_method_name = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.PropertySpec' - obj_spec_method_name = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.ObjectSpec' - filter_spec_method_name = \ - 'salt.utils.vmware.vmodl.query.PropertyCollector.FilterSpec' + traversal_spec_method_name = ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec" + ) + property_spec_method_name = ( + "salt.utils.vmware.vmodl.query.PropertyCollector.PropertySpec" + ) + obj_spec_method_name = "salt.utils.vmware.vmodl.query.PropertyCollector.ObjectSpec" + filter_spec_method_name = ( + "salt.utils.vmware.vmodl.query.PropertyCollector.FilterSpec" + ) # Class variables si_mock = None @@ -921,11 +1051,23 @@ class GetContentTestCase(TestCase): def setUp(self): patches = ( - ('salt.utils.vmware.get_root_folder', MagicMock()), - ('salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', MagicMock(return_value=MagicMock())), - ('salt.utils.vmware.vmodl.query.PropertyCollector.PropertySpec', MagicMock(return_value=MagicMock())), - ('salt.utils.vmware.vmodl.query.PropertyCollector.ObjectSpec', MagicMock(return_value=MagicMock())), - ('salt.utils.vmware.vmodl.query.PropertyCollector.FilterSpec', MagicMock(return_value=MagicMock())) + ("salt.utils.vmware.get_root_folder", MagicMock()), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + MagicMock(return_value=MagicMock()), + ), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.PropertySpec", + MagicMock(return_value=MagicMock()), + ), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.ObjectSpec", + MagicMock(return_value=MagicMock()), + ), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.FilterSpec", + MagicMock(return_value=MagicMock()), + ), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -935,19 +1077,21 @@ class GetContentTestCase(TestCase): self.si_mock = MagicMock() # RootFolder self.root_folder_mock = MagicMock() - self.get_root_folder_mock = \ - MagicMock(return_value=self.root_folder_mock) + self.get_root_folder_mock = MagicMock(return_value=self.root_folder_mock) # CreateContainerView() self.container_view_mock = MagicMock() - self.create_container_view_mock = \ - MagicMock(return_value=self.container_view_mock) - self.si_mock.content.viewManager.CreateContainerView = \ - self.create_container_view_mock + self.create_container_view_mock = MagicMock( + return_value=self.container_view_mock + ) + self.si_mock.content.viewManager.CreateContainerView = ( + self.create_container_view_mock + ) # RetrieveContents() self.result_mock = MagicMock() self.retrieve_contents_mock = MagicMock(return_value=self.result_mock) - self.si_mock.content.propertyCollector.RetrieveContents = \ - self.retrieve_contents_mock + self.si_mock.content.propertyCollector.RetrieveContents = ( + self.retrieve_contents_mock + ) # Destroy() self.destroy_mock = MagicMock() self.container_view_mock.Destroy = self.destroy_mock @@ -955,145 +1099,145 @@ class GetContentTestCase(TestCase): # override mocks self.obj_type_mock = MagicMock() self.traversal_spec_ret_mock = MagicMock() - self.traversal_spec_mock = \ - MagicMock(return_value=self.traversal_spec_ret_mock) + self.traversal_spec_mock = MagicMock(return_value=self.traversal_spec_ret_mock) self.property_spec_ret_mock = MagicMock() - self.property_spec_mock = \ - MagicMock(return_value=self.property_spec_ret_mock) + self.property_spec_mock = MagicMock(return_value=self.property_spec_ret_mock) self.obj_spec_ret_mock = MagicMock() - self.obj_spec_mock = \ - MagicMock(return_value=self.obj_spec_ret_mock) + self.obj_spec_mock = MagicMock(return_value=self.obj_spec_ret_mock) self.filter_spec_ret_mock = MagicMock() - self.filter_spec_mock = \ - MagicMock(return_value=self.filter_spec_ret_mock) + self.filter_spec_mock = MagicMock(return_value=self.filter_spec_ret_mock) def test_empty_container_ref(self): - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) self.get_root_folder_mock.assert_called_once_with(self.si_mock) self.create_container_view_mock.assert_called_once_with( - self.root_folder_mock, [self.obj_type_mock], True) + self.root_folder_mock, [self.obj_type_mock], True + ) def test_defined_container_ref(self): container_ref_mock = MagicMock() - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): with patch(self.obj_spec_method_name, self.obj_type_mock): salt.utils.vmware.get_content( - self.si_mock, self.obj_type_mock, - container_ref=container_ref_mock) + self.si_mock, self.obj_type_mock, container_ref=container_ref_mock + ) self.assertEqual(self.get_root_folder_mock.call_count, 0) self.create_container_view_mock.assert_called_once_with( - container_ref_mock, [self.obj_type_mock], True) + container_ref_mock, [self.obj_type_mock], True + ) # Also checks destroy is called def test_local_traversal_spec(self): - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): - with patch(self.traversal_spec_method_name, - self.traversal_spec_mock): + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): + with patch(self.traversal_spec_method_name, self.traversal_spec_mock): with patch(self.obj_spec_method_name, self.obj_spec_mock): - ret = salt.utils.vmware.get_content(self.si_mock, - self.obj_type_mock) + ret = salt.utils.vmware.get_content( + self.si_mock, self.obj_type_mock + ) self.create_container_view_mock.assert_called_once_with( - self.root_folder_mock, [self.obj_type_mock], True) + self.root_folder_mock, [self.obj_type_mock], True + ) self.traversal_spec_mock.assert_called_once_with( - name='traverseEntities', path='view', skip=False, - type=vim.view.ContainerView) + name="traverseEntities", + path="view", + skip=False, + type=vim.view.ContainerView, + ) self.obj_spec_mock.assert_called_once_with( obj=self.container_view_mock, skip=True, - selectSet=[self.traversal_spec_ret_mock]) + selectSet=[self.traversal_spec_ret_mock], + ) # check destroy is called self.assertEqual(self.destroy_mock.call_count, 1) def test_create_container_view_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.si_mock.content.viewManager.CreateContainerView = \ - MagicMock(side_effect=exc) - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): + exc.privilegeId = "Fake privilege" + self.si_mock.content.viewManager.CreateContainerView = MagicMock( + side_effect=exc + ) + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_create_container_view_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.si_mock.content.viewManager.CreateContainerView = \ - MagicMock(side_effect=exc) - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): + exc.msg = "VimFault msg" + self.si_mock.content.viewManager.CreateContainerView = MagicMock( + side_effect=exc + ) + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_create_container_view_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.si_mock.content.viewManager.CreateContainerView = \ - MagicMock(side_effect=exc) - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): + exc.msg = "RuntimeFault msg" + self.si_mock.content.viewManager.CreateContainerView = MagicMock( + side_effect=exc + ) + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_destroy_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.si_mock.content.viewManager.CreateContainerView = MagicMock( - return_value=MagicMock(Destroy=MagicMock(side_effect=exc))) - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): + return_value=MagicMock(Destroy=MagicMock(side_effect=exc)) + ) + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_destroy_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.si_mock.content.viewManager.CreateContainerView = MagicMock( - return_value=MagicMock(Destroy=MagicMock(side_effect=exc))) - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): + return_value=MagicMock(Destroy=MagicMock(side_effect=exc)) + ) + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_destroy_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.si_mock.content.viewManager.CreateContainerView = MagicMock( - return_value=MagicMock(Destroy=MagicMock(side_effect=exc))) - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): + return_value=MagicMock(Destroy=MagicMock(side_effect=exc)) + ) + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") # Also checks destroy is not called def test_external_traversal_spec(self): traversal_spec_obj_mock = MagicMock() - with patch('salt.utils.vmware.get_root_folder', - self.get_root_folder_mock): - with patch(self.traversal_spec_method_name, - self.traversal_spec_mock): + with patch("salt.utils.vmware.get_root_folder", self.get_root_folder_mock): + with patch(self.traversal_spec_method_name, self.traversal_spec_mock): with patch(self.obj_spec_method_name, self.obj_spec_mock): salt.utils.vmware.get_content( self.si_mock, self.obj_type_mock, - traversal_spec=traversal_spec_obj_mock) + traversal_spec=traversal_spec_obj_mock, + ) self.obj_spec_mock.assert_called_once_with( - obj=self.root_folder_mock, - skip=True, - selectSet=[traversal_spec_obj_mock]) + obj=self.root_folder_mock, skip=True, selectSet=[traversal_spec_obj_mock] + ) # Check local traversal methods are not called self.assertEqual(self.create_container_view_mock.call_count, 0) self.assertEqual(self.traversal_spec_mock.call_count, 0) @@ -1104,51 +1248,59 @@ class GetContentTestCase(TestCase): with patch(self.traversal_spec_method_name, self.traversal_spec_mock): with patch(self.property_spec_method_name, self.property_spec_mock): with patch(self.obj_spec_method_name, self.obj_spec_mock): - with patch(self.filter_spec_method_name, - self.filter_spec_mock): + with patch(self.filter_spec_method_name, self.filter_spec_mock): ret = salt.utils.vmware.get_content( - self.si_mock, - self.obj_type_mock) + self.si_mock, self.obj_type_mock + ) self.traversal_spec_mock.assert_called_once_with( - name='traverseEntities', path='view', skip=False, - type=vim.view.ContainerView) + name="traverseEntities", + path="view", + skip=False, + type=vim.view.ContainerView, + ) self.property_spec_mock.assert_called_once_with( - type=self.obj_type_mock, all=True, pathSet=None) + type=self.obj_type_mock, all=True, pathSet=None + ) self.obj_spec_mock.assert_called_once_with( - obj=self.container_view_mock, skip=True, - selectSet=[self.traversal_spec_ret_mock]) - self.retrieve_contents_mock.assert_called_once_with( - [self.filter_spec_ret_mock]) + obj=self.container_view_mock, + skip=True, + selectSet=[self.traversal_spec_ret_mock], + ) + self.retrieve_contents_mock.assert_called_once_with([self.filter_spec_ret_mock]) self.assertEqual(ret, self.result_mock) def test_retrieve_contents_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.si_mock.content.propertyCollector.RetrieveContents = \ - MagicMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + self.si_mock.content.propertyCollector.RetrieveContents = MagicMock( + side_effect=exc + ) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_retrieve_contents_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.si_mock.content.propertyCollector.RetrieveContents = \ - MagicMock(side_effect=exc) + exc.msg = "VimFault msg" + self.si_mock.content.propertyCollector.RetrieveContents = MagicMock( + side_effect=exc + ) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_retrieve_contents_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.si_mock.content.propertyCollector.RetrieveContents = \ - MagicMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + self.si_mock.content.propertyCollector.RetrieveContents = MagicMock( + side_effect=exc + ) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.get_content(self.si_mock, self.obj_type_mock) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_local_properties_set(self): container_ref_mock = MagicMock() @@ -1159,68 +1311,72 @@ class GetContentTestCase(TestCase): self.si_mock, self.obj_type_mock, container_ref=container_ref_mock, - local_properties=True) + local_properties=True, + ) self.assertEqual(self.traversal_spec_mock.call_count, 0) self.obj_spec_mock.assert_called_once_with( - obj=container_ref_mock, skip=False, selectSet=None) + obj=container_ref_mock, skip=False, selectSet=None + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetRootFolderTestCase(TestCase): - ''' + """ Tests for salt.utils.get_root_folder - ''' + """ def setUp(self): self.mock_root_folder = MagicMock() self.mock_content = MagicMock(rootFolder=self.mock_root_folder) self.mock_si = MagicMock( - RetrieveContent=MagicMock(return_value=self.mock_content)) + RetrieveContent=MagicMock(return_value=self.mock_content) + ) def test_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" type(self.mock_content).rootFolder = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_root_folder(self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" type(self.mock_content).rootFolder = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_root_folder(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" type(self.mock_content).rootFolder = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.get_root_folder(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_return(self): ret = salt.utils.vmware.get_root_folder(self.mock_si) self.assertEqual(ret, self.mock_root_folder) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetServiceInfoTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_service_info - ''' + """ + def setUp(self): self.mock_about = MagicMock() self.mock_si = MagicMock(content=MagicMock()) - type(self.mock_si.content).about = \ - PropertyMock(return_value=self.mock_about) + type(self.mock_si.content).about = PropertyMock(return_value=self.mock_about) def tearDown(self): - for attr in ('mock_si', 'mock_about'): + for attr in ("mock_si", "mock_about"): delattr(self, attr) def test_about_ret(self): @@ -1229,44 +1385,43 @@ class GetServiceInfoTestCase(TestCase): def test_about_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - type(self.mock_si.content).about = \ - PropertyMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + type(self.mock_si.content).about = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_service_info(self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_about_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - type(self.mock_si.content).about = \ - PropertyMock(side_effect=exc) + exc.msg = "VimFault msg" + type(self.mock_si.content).about = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_service_info(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_about_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - type(self.mock_si.content).about = \ - PropertyMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + type(self.mock_si.content).about = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.get_service_info(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_GSSAPI, "The 'gssapi' library is missing") class GssapiTokenTest(TestCase): - ''' + """ Test cases for salt.utils.vmware.get_gssapi_token - ''' + """ + def setUp(self): patches = ( - ('gssapi.Name', MagicMock(return_value='service')), - ('gssapi.InitContext', MagicMock()) + ("gssapi.Name", MagicMock(return_value="service")), + ("gssapi.InitContext", MagicMock()), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -1274,77 +1429,76 @@ class GssapiTokenTest(TestCase): self.addCleanup(patcher.stop) def test_no_gssapi(self): - with patch('salt.utils.vmware.HAS_GSSAPI', False): + with patch("salt.utils.vmware.HAS_GSSAPI", False): with self.assertRaises(ImportError) as excinfo: - salt.utils.vmware.get_gssapi_token('principal', 'host', 'domain') - self.assertIn('The gssapi library is not imported.', - excinfo.exception.message) + salt.utils.vmware.get_gssapi_token("principal", "host", "domain") + self.assertIn( + "The gssapi library is not imported.", excinfo.exception.message + ) - @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') + @skipIf(not HAS_GSSAPI, "The 'gssapi' library is missing") def test_service_name(self): mock_name = MagicMock() - with patch.object(salt.utils.vmware.gssapi, 'Name', mock_name): + with patch.object(salt.utils.vmware.gssapi, "Name", mock_name): with self.assertRaises(CommandExecutionError): - salt.utils.vmware.get_gssapi_token('principal', 'host', - 'domain') - mock_name.assert_called_once_with('principal/host@domain', - gssapi.C_NT_USER_NAME) + salt.utils.vmware.get_gssapi_token("principal", "host", "domain") + mock_name.assert_called_once_with( + "principal/host@domain", gssapi.C_NT_USER_NAME + ) - @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') + @skipIf(not HAS_GSSAPI, "The 'gssapi' library is missing") def test_out_token_defined(self): mock_context = MagicMock(return_value=MagicMock()) mock_context.return_value.established = False - mock_context.return_value.step = MagicMock(return_value='out_token') - with patch.object(salt.utils.vmware.gssapi, 'InitContext', - mock_context): - ret = salt.utils.vmware.get_gssapi_token('principal', 'host', - 'domain') + mock_context.return_value.step = MagicMock(return_value="out_token") + with patch.object(salt.utils.vmware.gssapi, "InitContext", mock_context): + ret = salt.utils.vmware.get_gssapi_token("principal", "host", "domain") self.assertEqual(mock_context.return_value.step.called, 1) - self.assertEqual(ret, base64.b64encode(b'out_token')) + self.assertEqual(ret, base64.b64encode(b"out_token")) - @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') + @skipIf(not HAS_GSSAPI, "The 'gssapi' library is missing") def test_out_token_undefined(self): mock_context = MagicMock(return_value=MagicMock()) mock_context.return_value.established = False mock_context.return_value.step = MagicMock(return_value=None) - with patch.object(salt.utils.vmware.gssapi, 'InitContext', - mock_context): + with patch.object(salt.utils.vmware.gssapi, "InitContext", mock_context): with self.assertRaises(CommandExecutionError) as excinfo: - salt.utils.vmware.get_gssapi_token('principal', 'host', - 'domain') + salt.utils.vmware.get_gssapi_token("principal", "host", "domain") self.assertEqual(mock_context.return_value.step.called, 1) - self.assertIn('Can\'t receive token', - excinfo.exception.strerror) + self.assertIn("Can't receive token", excinfo.exception.strerror) - @skipIf(not HAS_GSSAPI, 'The \'gssapi\' library is missing') + @skipIf(not HAS_GSSAPI, "The 'gssapi' library is missing") def test_context_extablished(self): mock_context = MagicMock(return_value=MagicMock()) mock_context.return_value.established = True - mock_context.return_value.step = MagicMock(return_value='out_token') - with patch.object(salt.utils.vmware.gssapi, 'InitContext', - mock_context): + mock_context.return_value.step = MagicMock(return_value="out_token") + with patch.object(salt.utils.vmware.gssapi, "InitContext", mock_context): mock_context.established = True mock_context.step = MagicMock(return_value=None) with self.assertRaises(CommandExecutionError) as excinfo: - salt.utils.vmware.get_gssapi_token('principal', 'host', - 'domain') + salt.utils.vmware.get_gssapi_token("principal", "host", "domain") self.assertEqual(mock_context.step.called, 0) - self.assertIn('Context established, but didn\'t receive token', - excinfo.exception.strerror) + self.assertIn( + "Context established, but didn't receive token", + excinfo.exception.strerror, + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class PrivateGetServiceInstanceTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware._get_service_instance - ''' + """ def setUp(self): patches = ( - ('salt.utils.vmware.SmartConnect', MagicMock()), - ('salt.utils.vmware.Disconnect', MagicMock()), - ('salt.utils.vmware.get_gssapi_token', MagicMock(return_value='fake_token')) + ("salt.utils.vmware.SmartConnect", MagicMock()), + ("salt.utils.vmware.Disconnect", MagicMock()), + ( + "salt.utils.vmware.get_gssapi_token", + MagicMock(return_value="fake_token"), + ), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -1354,444 +1508,478 @@ class PrivateGetServiceInstanceTestCase(TestCase): def test_invalid_mechianism(self): with self.assertRaises(CommandExecutionError) as excinfo: salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='invalid_mechanism', - principal='fake principal', - domain='fake_domain') - self.assertIn('Unsupported mechanism', excinfo.exception.strerror) + mechanism="invalid_mechanism", + principal="fake principal", + domain="fake_domain", + ) + self.assertIn("Unsupported mechanism", excinfo.exception.strerror) def test_userpass_mechanism_empty_username(self): with self.assertRaises(CommandExecutionError) as excinfo: salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', + host="fake_host.fqdn", username=None, - password='fake_password', - protocol='fake_protocol', + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='userpass', - principal='fake principal', - domain='fake_domain') - self.assertIn('mandatory parameter \'username\'', - excinfo.exception.strerror) + mechanism="userpass", + principal="fake principal", + domain="fake_domain", + ) + self.assertIn("mandatory parameter 'username'", excinfo.exception.strerror) def test_userpass_mechanism_empty_password(self): with self.assertRaises(CommandExecutionError) as excinfo: salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', + host="fake_host.fqdn", + username="fake_username", password=None, - protocol='fake_protocol', + protocol="fake_protocol", port=1, - mechanism='userpass', - principal='fake principal', - domain='fake_domain') - self.assertIn('mandatory parameter \'password\'', - excinfo.exception.strerror) + mechanism="userpass", + principal="fake principal", + domain="fake_domain", + ) + self.assertIn("mandatory parameter 'password'", excinfo.exception.strerror) def test_userpass_mechanism_no_domain(self): mock_sc = MagicMock() - with patch('salt.utils.vmware.SmartConnect', mock_sc): + with patch("salt.utils.vmware.SmartConnect", mock_sc): salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='userpass', - principal='fake principal', - domain=None) + mechanism="userpass", + principal="fake principal", + domain=None, + ) mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + user="fake_username", + pwd="fake_password", + protocol="fake_protocol", port=1, b64token=None, - mechanism='userpass') + mechanism="userpass", + ) def test_userpass_mech_domain_unused(self): mock_sc = MagicMock() - with patch('salt.utils.vmware.SmartConnect', mock_sc): + with patch("salt.utils.vmware.SmartConnect", mock_sc): salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username@domain', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="fake_username@domain", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='userpass', - principal='fake principal', - domain='fake_domain') + mechanism="userpass", + principal="fake principal", + domain="fake_domain", + ) mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='fake_username@domain', - pwd='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + user="fake_username@domain", + pwd="fake_password", + protocol="fake_protocol", port=1, b64token=None, - mechanism='userpass') + mechanism="userpass", + ) mock_sc.reset_mock() salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='domain\\fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="domain\\fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='userpass', - principal='fake principal', - domain='fake_domain') + mechanism="userpass", + principal="fake principal", + domain="fake_domain", + ) mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='domain\\fake_username', - pwd='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + user="domain\\fake_username", + pwd="fake_password", + protocol="fake_protocol", port=1, b64token=None, - mechanism='userpass') + mechanism="userpass", + ) def test_sspi_empty_principal(self): with self.assertRaises(CommandExecutionError) as excinfo: salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', + mechanism="sspi", principal=None, - domain='fake_domain') - self.assertIn('mandatory parameters are missing', - excinfo.exception.strerror) + domain="fake_domain", + ) + self.assertIn("mandatory parameters are missing", excinfo.exception.strerror) def test_sspi_empty_domain(self): with self.assertRaises(CommandExecutionError) as excinfo: salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain=None) - self.assertIn('mandatory parameters are missing', - excinfo.exception.strerror) + mechanism="sspi", + principal="fake_principal", + domain=None, + ) + self.assertIn("mandatory parameters are missing", excinfo.exception.strerror) def test_sspi_get_token_error(self): - mock_token = MagicMock(side_effect=Exception('Exception')) + mock_token = MagicMock(side_effect=Exception("Exception")) - with patch('salt.utils.vmware.get_gssapi_token', mock_token): + with patch("salt.utils.vmware.get_gssapi_token", mock_token): 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', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - mock_token.assert_called_once_with('fake_principal', - 'fake_host.fqdn', - 'fake_domain') - self.assertEqual('Exception', excinfo.exception.strerror) + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) + mock_token.assert_called_once_with( + "fake_principal", "fake_host.fqdn", "fake_domain" + ) + self.assertEqual("Exception", excinfo.exception.strerror) def test_sspi_get_token_success_(self): - mock_token = MagicMock(return_value='fake_token') + mock_token = MagicMock(return_value="fake_token") mock_sc = MagicMock() - with patch('salt.utils.vmware.get_gssapi_token', mock_token): - with patch('salt.utils.vmware.SmartConnect', mock_sc): + with patch("salt.utils.vmware.get_gssapi_token", mock_token): + with patch("salt.utils.vmware.SmartConnect", mock_sc): salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') - mock_token.assert_called_once_with('fake_principal', - 'fake_host.fqdn', - 'fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) + mock_token.assert_called_once_with( + "fake_principal", "fake_host.fqdn", "fake_domain" + ) mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + user="fake_username", + pwd="fake_password", + protocol="fake_protocol", port=1, - b64token='fake_token', - mechanism='sspi') + b64token="fake_token", + mechanism="sspi", + ) def test_first_attempt_successful_connection(self): mock_sc = MagicMock() - with patch('salt.utils.vmware.SmartConnect', mock_sc): + with patch("salt.utils.vmware.SmartConnect", mock_sc): salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) mock_sc.assert_called_once_with( - host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + user="fake_username", + pwd="fake_password", + protocol="fake_protocol", port=1, - b64token='fake_token', - mechanism='sspi') + b64token="fake_token", + mechanism="sspi", + ) - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @skipIf(not SSL_VALIDATION, "SSL validation is not enabled") def test_second_attempt_successful_connection(self): - with patch('ssl.SSLContext', MagicMock()), \ - patch(ssl_context, MagicMock()): + with patch("ssl.SSLContext", MagicMock()), patch(ssl_context, MagicMock()): exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' + exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" mock_sc = MagicMock(side_effect=[exc, None]) mock_ssl = MagicMock() - with patch('salt.utils.vmware.SmartConnect', mock_sc): - with patch(ssl_context, - mock_ssl): + with patch("salt.utils.vmware.SmartConnect", mock_sc): + with patch(ssl_context, mock_ssl): salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) 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', - pwd='fake_password', - protocol='fake_protocol', - port=1, - sslContext=mock_ssl.return_value, - b64token='fake_token', - mechanism='sspi')] + 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", + pwd="fake_password", + protocol="fake_protocol", + port=1, + sslContext=mock_ssl.return_value, + b64token="fake_token", + mechanism="sspi", + ), + ] mock_sc.assert_has_calls(calls) - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @skipIf(not SSL_VALIDATION, "SSL validation is not enabled") def test_third_attempt_successful_connection(self): - with patch('ssl.SSLContext', MagicMock()), \ - patch(ssl_context, MagicMock()): + with patch("ssl.SSLContext", MagicMock()), patch(ssl_context, MagicMock()): exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - exc2 = Exception('certificate verify failed') + exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" + exc2 = Exception("certificate verify failed") mock_sc = MagicMock(side_effect=[exc, exc2, None]) mock_ssl_unverif = MagicMock() mock_ssl_context = MagicMock() - with patch('salt.utils.vmware.SmartConnect', mock_sc): + with patch("salt.utils.vmware.SmartConnect", mock_sc): with patch(ssl_context, mock_ssl_unverif): - with patch('ssl.SSLContext', mock_ssl_context): + with patch("ssl.SSLContext", mock_ssl_context): salt.utils.vmware._get_service_instance( - host='fake_host.fqdn', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) 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', - pwd='fake_password', - protocol='fake_protocol', - port=1, - sslContext=mock_ssl_unverif.return_value, - b64token='fake_token', - mechanism='sspi'), - call(host='fake_host.fqdn', - user='fake_username', - pwd='fake_password', - protocol='fake_protocol', - port=1, - sslContext=mock_ssl_context.return_value, - b64token='fake_token', - mechanism='sspi'), - ] + 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", + pwd="fake_password", + protocol="fake_protocol", + port=1, + sslContext=mock_ssl_unverif.return_value, + b64token="fake_token", + mechanism="sspi", + ), + call( + host="fake_host.fqdn", + user="fake_username", + pwd="fake_password", + protocol="fake_protocol", + port=1, + sslContext=mock_ssl_context.return_value, + b64token="fake_token", + mechanism="sspi", + ), + ] mock_sc.assert_has_calls(calls) def test_first_attempt_unsuccessful_connection_default_error(self): - exc = Exception('Exception') + exc = Exception("Exception") mock_sc = MagicMock(side_effect=exc) - with patch('salt.utils.vmware.SmartConnect', mock_sc): + 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', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) self.assertEqual(mock_sc.call_count, 1) - self.assertIn('Could not connect to host \'fake_host.fqdn\'', - excinfo.Exception.message) + self.assertIn( + "Could not connect to host 'fake_host.fqdn'", + excinfo.Exception.message, + ) def test_first_attempt_unsuccessful_connection_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault' + exc.msg = "VimFault" mock_sc = MagicMock(side_effect=exc) - with patch('salt.utils.vmware.SmartConnect', mock_sc): + 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', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) self.assertEqual(mock_sc.call_count, 1) - self.assertEqual('VimFault', excinfo.Exception.message) + self.assertEqual("VimFault", excinfo.Exception.message) - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @skipIf(not SSL_VALIDATION, "SSL validation is not enabled") def test_second_attempt_unsuccsessful_connection_default_error(self): - with patch('ssl.SSLContext', MagicMock()), \ - patch(ssl_context, MagicMock()): + with patch("ssl.SSLContext", MagicMock()), patch(ssl_context, MagicMock()): exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - exc2 = Exception('Exception') + exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" + exc2 = Exception("Exception") mock_sc = MagicMock(side_effect=[exc, exc2]) - with patch('salt.utils.vmware.SmartConnect', mock_sc): + 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', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) self.assertEqual(mock_sc.call_count, 2) - self.assertIn('Could not connect to host \'fake_host.fqdn\'', - excinfo.Exception.message) + self.assertIn( + "Could not connect to host 'fake_host.fqdn'", + excinfo.Exception.message, + ) - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @skipIf(not SSL_VALIDATION, "SSL validation is not enabled") def test_second_attempt_unsuccsessful_connection_vim_fault(self): - with patch('ssl.SSLContext', MagicMock()), \ - patch(ssl_context, MagicMock()): + with patch("ssl.SSLContext", MagicMock()), patch(ssl_context, MagicMock()): exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' + exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" exc2 = vim.fault.VimFault() - exc2.msg = 'VimFault' + exc2.msg = "VimFault" mock_sc = MagicMock(side_effect=[exc, exc2]) - with patch('salt.utils.vmware.SmartConnect', mock_sc): + 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', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) self.assertEqual(mock_sc.call_count, 2) - self.assertIn('VimFault', excinfo.Exception.message) + self.assertIn("VimFault", excinfo.Exception.message) - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @skipIf(not SSL_VALIDATION, "SSL validation is not enabled") def test_third_attempt_unsuccessful_connection_detault_error(self): - with patch('ssl.SSLContext', MagicMock()), \ - patch(ssl_context, MagicMock()): + with patch("ssl.SSLContext", MagicMock()), patch(ssl_context, MagicMock()): exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - exc2 = Exception('certificate verify failed') - exc3 = Exception('Exception') + exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" + exc2 = Exception("certificate verify failed") + exc3 = Exception("Exception") mock_sc = MagicMock(side_effect=[exc, exc2, exc3]) - with patch('salt.utils.vmware.SmartConnect', mock_sc): + 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', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) self.assertEqual(mock_sc.call_count, 3) - self.assertIn('Exception', excinfo.Exception.message) + self.assertIn("Exception", excinfo.Exception.message) - @skipIf(not SSL_VALIDATION, 'SSL validation is not enabled') + @skipIf(not SSL_VALIDATION, "SSL validation is not enabled") def test_third_attempt_unsuccessful_connection_vim_fault(self): - with patch('ssl.SSLContext', MagicMock()), \ - patch(ssl_context, MagicMock()): + with patch("ssl.SSLContext", MagicMock()), patch(ssl_context, MagicMock()): exc = vim.fault.HostConnectFault() - exc.msg = '[SSL: CERTIFICATE_VERIFY_FAILED]' - exc2 = Exception('certificate verify failed') + exc.msg = "[SSL: CERTIFICATE_VERIFY_FAILED]" + exc2 = Exception("certificate verify failed") exc3 = vim.fault.VimFault() - exc3.msg = 'VimFault' + exc3.msg = "VimFault" mock_sc = MagicMock(side_effect=[exc, exc2, exc3]) - with patch('salt.utils.vmware.SmartConnect', mock_sc): + 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', + host="fake_host.fqdn", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='sspi', - principal='fake_principal', - domain='fake_domain') + mechanism="sspi", + principal="fake_principal", + domain="fake_domain", + ) self.assertEqual(mock_sc.call_count, 3) - self.assertIn('VimFault', excinfo.Exception.message) + self.assertIn("VimFault", excinfo.Exception.message) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetServiceInstanceTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_service_instance - ''' + """ + def setUp(self): patches = ( - ('salt.utils.vmware.GetSi', MagicMock(return_value=None)), - ('salt.utils.vmware._get_service_instance', MagicMock(return_value=MagicMock())) + ("salt.utils.vmware.GetSi", MagicMock(return_value=None)), + ( + "salt.utils.vmware._get_service_instance", + MagicMock(return_value=MagicMock()), + ), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -1800,37 +1988,37 @@ class GetServiceInstanceTestCase(TestCase): def test_default_params(self): mock_get_si = MagicMock() - with patch('salt.utils.vmware._get_service_instance', mock_get_si): - salt.utils.vmware.get_service_instance( - host='fake_host' + 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 ) - mock_get_si.assert_called_once_with('fake_host', None, None, - 'https', 443, 'userpass', None, - None) def test_no_cached_service_instance_same_host_on_proxy(self): - with patch('salt.utils.platform.is_proxy', MagicMock(return_value=True)): + with patch("salt.utils.platform.is_proxy", MagicMock(return_value=True)): # Service instance is uncached when using class default mock objs mock_get_si = MagicMock() - with patch('salt.utils.vmware._get_service_instance', mock_get_si): + with patch("salt.utils.vmware._get_service_instance", mock_get_si): salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain' + mechanism="fake_mechanism", + principal="fake_principal", + domain="fake_domain", + ) + mock_get_si.assert_called_once_with( + "fake_host", + "fake_username", + "fake_password", + "fake_protocol", + 1, + "fake_mechanism", + "fake_principal", + "fake_domain", ) - mock_get_si.assert_called_once_with('fake_host', - 'fake_username', - 'fake_password', - 'fake_protocol', - 1, - 'fake_mechanism', - 'fake_principal', - 'fake_domain') def test_cached_service_instance_different_host(self): mock_si = MagicMock() @@ -1838,18 +2026,18 @@ class GetServiceInstanceTestCase(TestCase): mock_disconnect = MagicMock() mock_get_si = MagicMock(return_value=mock_si) mock_getstub = MagicMock() - with patch('salt.utils.vmware.GetSi', mock_get_si): - with patch('salt.utils.vmware.GetStub', mock_getstub): - with patch('salt.utils.vmware.Disconnect', mock_disconnect): + with patch("salt.utils.vmware.GetSi", mock_get_si): + with patch("salt.utils.vmware.GetStub", mock_getstub): + with patch("salt.utils.vmware.Disconnect", mock_disconnect): salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain' + mechanism="fake_mechanism", + principal="fake_principal", + domain="fake_domain", ) self.assertEqual(mock_get_si.call_count, 1) self.assertEqual(mock_getstub.call_count, 1) @@ -1858,25 +2046,27 @@ class GetServiceInstanceTestCase(TestCase): def test_uncached_service_instance(self): # Service instance is uncached when using class default mock objs mock_get_si = MagicMock() - with patch('salt.utils.vmware._get_service_instance', mock_get_si): + with patch("salt.utils.vmware._get_service_instance", mock_get_si): salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain' + mechanism="fake_mechanism", + principal="fake_principal", + domain="fake_domain", + ) + mock_get_si.assert_called_once_with( + "fake_host", + "fake_username", + "fake_password", + "fake_protocol", + 1, + "fake_mechanism", + "fake_principal", + "fake_domain", ) - mock_get_si.assert_called_once_with('fake_host', - 'fake_username', - 'fake_password', - 'fake_protocol', - 1, - 'fake_mechanism', - 'fake_principal', - 'fake_domain') def test_unauthenticated_service_instance(self): mock_si_current_time = MagicMock(side_effect=vim.fault.NotAuthenticated) @@ -1884,17 +2074,17 @@ class GetServiceInstanceTestCase(TestCase): mock_get_si = MagicMock(return_value=mock_si) mock_si.CurrentTime = mock_si_current_time mock_disconnect = MagicMock() - with patch('salt.utils.vmware._get_service_instance', mock_get_si): - with patch('salt.utils.vmware.Disconnect', mock_disconnect): + with patch("salt.utils.vmware._get_service_instance", mock_get_si): + with patch("salt.utils.vmware.Disconnect", mock_disconnect): salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain' + mechanism="fake_mechanism", + principal="fake_principal", + domain="fake_domain", ) self.assertEqual(mock_si_current_time.call_count, 1) self.assertEqual(mock_disconnect.call_count, 1) @@ -1902,268 +2092,286 @@ class GetServiceInstanceTestCase(TestCase): def test_current_time_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - with patch('salt.utils.vmware._get_service_instance', - MagicMock(return_value=MagicMock( - CurrentTime=MagicMock(side_effect=exc)))): + exc.privilegeId = "Fake privilege" + with patch( + "salt.utils.vmware._get_service_instance", + MagicMock(return_value=MagicMock(CurrentTime=MagicMock(side_effect=exc))), + ): with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + mechanism="fake_mechanism", + principal="fake_principal", + domain="fake_domain", + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_current_time_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - with patch('salt.utils.vmware._get_service_instance', - MagicMock(return_value=MagicMock( - CurrentTime=MagicMock(side_effect=exc)))): + exc.msg = "VimFault msg" + with patch( + "salt.utils.vmware._get_service_instance", + MagicMock(return_value=MagicMock(CurrentTime=MagicMock(side_effect=exc))), + ): with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + mechanism="fake_mechanism", + principal="fake_principal", + domain="fake_domain", + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_current_time_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - with patch('salt.utils.vmware._get_service_instance', - MagicMock(return_value=MagicMock( - CurrentTime=MagicMock(side_effect=exc)))): + exc.msg = "RuntimeFault msg" + with patch( + "salt.utils.vmware._get_service_instance", + MagicMock(return_value=MagicMock(CurrentTime=MagicMock(side_effect=exc))), + ): with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.get_service_instance( - host='fake_host', - username='fake_username', - password='fake_password', - protocol='fake_protocol', + host="fake_host", + username="fake_username", + password="fake_password", + protocol="fake_protocol", port=1, - mechanism='fake_mechanism', - principal='fake_principal', - domain='fake_domain') - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + mechanism="fake_mechanism", + principal="fake_principal", + domain="fake_domain", + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class DisconnectTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.disconnect - ''' + """ def setUp(self): self.mock_si = MagicMock() - self.addCleanup(delattr, self, 'mock_si') + self.addCleanup(delattr, self, "mock_si") def test_disconnect(self): mock_disconnect = MagicMock() - with patch('salt.utils.vmware.Disconnect', mock_disconnect): - salt.utils.vmware.disconnect( - service_instance=self.mock_si) + with patch("salt.utils.vmware.Disconnect", mock_disconnect): + salt.utils.vmware.disconnect(service_instance=self.mock_si) mock_disconnect.assert_called_once_with(self.mock_si) def test_disconnect_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - with patch('salt.utils.vmware.Disconnect', MagicMock(side_effect=exc)): + exc.privilegeId = "Fake privilege" + with patch("salt.utils.vmware.Disconnect", MagicMock(side_effect=exc)): with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.disconnect( - service_instance=self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.disconnect(service_instance=self.mock_si) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_disconnect_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - with patch('salt.utils.vmware.Disconnect', MagicMock(side_effect=exc)): + exc.msg = "VimFault msg" + with patch("salt.utils.vmware.Disconnect", MagicMock(side_effect=exc)): with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.disconnect( - service_instance=self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.disconnect(service_instance=self.mock_si) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_disconnect_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - with patch('salt.utils.vmware.Disconnect', MagicMock(side_effect=exc)): + exc.msg = "RuntimeFault msg" + with patch("salt.utils.vmware.Disconnect", MagicMock(side_effect=exc)): with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.disconnect( - service_instance=self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.disconnect(service_instance=self.mock_si) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class IsConnectionToAVCenterTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.is_connection_to_a_vcenter - ''' + """ def test_api_type_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" mock_si = MagicMock() type(mock_si.content.about).apiType = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.is_connection_to_a_vcenter(mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_api_type_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" mock_si = MagicMock() type(mock_si.content.about).apiType = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.is_connection_to_a_vcenter(mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_api_type_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" mock_si = MagicMock() type(mock_si.content.about).apiType = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.is_connection_to_a_vcenter(mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_connected_to_a_vcenter(self): mock_si = MagicMock() - mock_si.content.about.apiType = 'VirtualCenter' + mock_si.content.about.apiType = "VirtualCenter" ret = salt.utils.vmware.is_connection_to_a_vcenter(mock_si) self.assertTrue(ret) def test_connected_to_a_host(self): mock_si = MagicMock() - mock_si.content.about.apiType = 'HostAgent' + mock_si.content.about.apiType = "HostAgent" ret = salt.utils.vmware.is_connection_to_a_vcenter(mock_si) self.assertFalse(ret) def test_connected_to_invalid_entity(self): mock_si = MagicMock() - mock_si.content.about.apiType = 'UnsupportedType' + mock_si.content.about.apiType = "UnsupportedType" with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.is_connection_to_a_vcenter(mock_si) - self.assertIn('Unexpected api type \'UnsupportedType\'', - excinfo.exception.strerror) + self.assertIn( + "Unexpected api type 'UnsupportedType'", excinfo.exception.strerror + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetNewServiceInstanceStub(TestCase, LoaderModuleMockMixin): - ''' + """ Tests for salt.utils.vmware.get_new_service_instance_stub - ''' + """ + def setup_loader_modules(self): - return {salt.utils.vmware: { - '__virtual__': MagicMock(return_value='vmware'), - 'sys': MagicMock(), - 'ssl': MagicMock()}} + return { + salt.utils.vmware: { + "__virtual__": MagicMock(return_value="vmware"), + "sys": MagicMock(), + "ssl": MagicMock(), + } + } def setUp(self): - self.mock_stub = MagicMock( - host='fake_host:1000', - cookie='ignore"fake_cookie') - self.mock_si = MagicMock( - _stub=self.mock_stub) + self.mock_stub = MagicMock(host="fake_host:1000", cookie='ignore"fake_cookie') + self.mock_si = MagicMock(_stub=self.mock_stub) self.mock_ret = MagicMock() self.mock_new_stub = MagicMock() self.context_dict = {} - patches = (('salt.utils.vmware.VmomiSupport.GetRequestContext', - MagicMock( - return_value=self.context_dict)), - ('salt.utils.vmware.SoapStubAdapter', - MagicMock(return_value=self.mock_new_stub))) + patches = ( + ( + "salt.utils.vmware.VmomiSupport.GetRequestContext", + MagicMock(return_value=self.context_dict), + ), + ( + "salt.utils.vmware.SoapStubAdapter", + MagicMock(return_value=self.mock_new_stub), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) - type(salt.utils.vmware.sys).version_info = \ - PropertyMock(return_value=(2, 7, 9)) + type(salt.utils.vmware.sys).version_info = PropertyMock(return_value=(2, 7, 9)) self.mock_context = MagicMock() - self.mock_create_default_context = \ - MagicMock(return_value=self.mock_context) - salt.utils.vmware.ssl.create_default_context = \ - self.mock_create_default_context + self.mock_create_default_context = MagicMock(return_value=self.mock_context) + salt.utils.vmware.ssl.create_default_context = self.mock_create_default_context def tearDown(self): - for attr in ('mock_stub', 'mock_si', 'mock_ret', 'mock_new_stub', - 'context_dict', 'mock_context', - 'mock_create_default_context'): + for attr in ( + "mock_stub", + "mock_si", + "mock_ret", + "mock_new_stub", + "context_dict", + "mock_context", + "mock_create_default_context", + ): delattr(self, attr) def test_ssl_default_context_loaded(self): - salt.utils.vmware.get_new_service_instance_stub( - self.mock_si, 'fake_path') + salt.utils.vmware.get_new_service_instance_stub(self.mock_si, "fake_path") self.mock_create_default_context.assert_called_once_with() self.assertFalse(self.mock_context.check_hostname) - self.assertEqual(self.mock_context.verify_mode, - salt.utils.vmware.ssl.CERT_NONE) + self.assertEqual(self.mock_context.verify_mode, salt.utils.vmware.ssl.CERT_NONE) def test_ssl_default_context_not_loaded(self): - type(salt.utils.vmware.sys).version_info = \ - PropertyMock(return_value=(2, 7, 8)) - salt.utils.vmware.get_new_service_instance_stub( - self.mock_si, 'fake_path') + type(salt.utils.vmware.sys).version_info = PropertyMock(return_value=(2, 7, 8)) + salt.utils.vmware.get_new_service_instance_stub(self.mock_si, "fake_path") self.assertEqual(self.mock_create_default_context.call_count, 0) def test_session_cookie_in_context(self): - salt.utils.vmware.get_new_service_instance_stub( - self.mock_si, 'fake_path') - self.assertEqual(self.context_dict['vcSessionCookie'], 'fake_cookie') + salt.utils.vmware.get_new_service_instance_stub(self.mock_si, "fake_path") + self.assertEqual(self.context_dict["vcSessionCookie"], "fake_cookie") def test_get_new_stub(self): mock_get_new_stub = MagicMock() - with patch('salt.utils.vmware.SoapStubAdapter', mock_get_new_stub): + with patch("salt.utils.vmware.SoapStubAdapter", mock_get_new_stub): salt.utils.vmware.get_new_service_instance_stub( - self.mock_si, 'fake_path', 'fake_ns', 'fake_version') + self.mock_si, "fake_path", "fake_ns", "fake_version" + ) mock_get_new_stub.assert_called_once_with( - host='fake_host', ns='fake_ns', path='fake_path', - version='fake_version', poolSize=0, sslContext=self.mock_context) + host="fake_host", + ns="fake_ns", + path="fake_path", + version="fake_version", + poolSize=0, + sslContext=self.mock_context, + ) def test_get_new_stub_2_7_8_python(self): - type(salt.utils.vmware.sys).version_info = \ - PropertyMock(return_value=(2, 7, 8)) + type(salt.utils.vmware.sys).version_info = PropertyMock(return_value=(2, 7, 8)) mock_get_new_stub = MagicMock() - with patch('salt.utils.vmware.SoapStubAdapter', mock_get_new_stub): + with patch("salt.utils.vmware.SoapStubAdapter", mock_get_new_stub): salt.utils.vmware.get_new_service_instance_stub( - self.mock_si, 'fake_path', 'fake_ns', 'fake_version') + self.mock_si, "fake_path", "fake_ns", "fake_version" + ) mock_get_new_stub.assert_called_once_with( - host='fake_host', ns='fake_ns', path='fake_path', - version='fake_version', poolSize=0, sslContext=None) + host="fake_host", + ns="fake_ns", + path="fake_path", + version="fake_version", + poolSize=0, + sslContext=None, + ) def test_new_stub_returned(self): - ret = salt.utils.vmware.get_new_service_instance_stub( - self.mock_si, 'fake_path') + ret = salt.utils.vmware.get_new_service_instance_stub(self.mock_si, "fake_path") self.assertEqual(self.mock_new_stub.cookie, 'ignore"fake_cookie') self.assertEqual(ret, self.mock_new_stub) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetServiceInstanceFromManagedObjectTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_managed_instance_from_managed_object - ''' + """ def setUp(self): - patches = ( - ('salt.utils.vmware.vim.ServiceInstance', MagicMock()), - ) + patches = (("salt.utils.vmware.vim.ServiceInstance", MagicMock()),) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() @@ -2171,54 +2379,57 @@ class GetServiceInstanceFromManagedObjectTestCase(TestCase): self.mock_si = MagicMock() self.mock_stub = PropertyMock() self.mock_mo_ref = MagicMock(_stub=self.mock_stub) - for attr in ('mock_si', 'mock_stub', 'mock_mo_ref'): + for attr in ("mock_si", "mock_stub", "mock_mo_ref"): self.addCleanup(delattr, self, attr) def test_default_name_parameter(self): mock_trace = MagicMock() type(salt.utils.vmware.log).trace = mock_trace - salt.utils.vmware.get_service_instance_from_managed_object( - self.mock_mo_ref) + salt.utils.vmware.get_service_instance_from_managed_object(self.mock_mo_ref) mock_trace.assert_called_once_with( - '[%s] Retrieving service instance from managed object', - '<unnamed>') + "[%s] Retrieving service instance from managed object", "<unnamed>" + ) def test_name_parameter_passed_in(self): mock_trace = MagicMock() type(salt.utils.vmware.log).trace = mock_trace salt.utils.vmware.get_service_instance_from_managed_object( - self.mock_mo_ref, 'fake_mo_name') + self.mock_mo_ref, "fake_mo_name" + ) mock_trace.assert_called_once_with( - '[%s] Retrieving service instance from managed object', - 'fake_mo_name') + "[%s] Retrieving service instance from managed object", "fake_mo_name" + ) def test_service_instance_instantiation(self): mock_service_instance_ini = MagicMock() - with patch('salt.utils.vmware.vim.ServiceInstance', - mock_service_instance_ini): - salt.utils.vmware.get_service_instance_from_managed_object( - self.mock_mo_ref) - mock_service_instance_ini.assert_called_once_with('ServiceInstance') + with patch("salt.utils.vmware.vim.ServiceInstance", mock_service_instance_ini): + salt.utils.vmware.get_service_instance_from_managed_object(self.mock_mo_ref) + mock_service_instance_ini.assert_called_once_with("ServiceInstance") def test_si_return_and_stub_assignment(self): - with patch('salt.utils.vmware.vim.ServiceInstance', - MagicMock(return_value=self.mock_si)): + with patch( + "salt.utils.vmware.vim.ServiceInstance", + MagicMock(return_value=self.mock_si), + ): ret = salt.utils.vmware.get_service_instance_from_managed_object( - self.mock_mo_ref) + self.mock_mo_ref + ) self.assertEqual(ret, self.mock_si) self.assertEqual(ret._stub, self.mock_stub) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetDatacentersTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_datacenters - ''' + """ def setUp(self): patches = ( - ('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=[{'name': 'fake_dc', 'object': MagicMock()}])), + ( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=[{"name": "fake_dc", "object": MagicMock()}]), + ), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -2227,64 +2438,85 @@ class GetDatacentersTestCase(TestCase): self.mock_si = MagicMock() self.mock_dc1 = MagicMock() self.mock_dc2 = MagicMock() - self.mock_entries = [{'name': 'fake_dc1', - 'object': self.mock_dc1}, - {'name': 'fake_dc2', - 'object': self.mock_dc2}] + self.mock_entries = [ + {"name": "fake_dc1", "object": self.mock_dc1}, + {"name": "fake_dc2", "object": self.mock_dc2}, + ] def test_get_mors_with_properties_call(self): mock_get_mors_with_properties = MagicMock( - return_value=[{'name': 'fake_dc', 'object': MagicMock()}]) - with patch('salt.utils.vmware.get_mors_with_properties', - mock_get_mors_with_properties): - salt.utils.vmware.get_datacenters(self.mock_si, datacenter_names=['fake_dc1']) + return_value=[{"name": "fake_dc", "object": MagicMock()}] + ) + with patch( + "salt.utils.vmware.get_mors_with_properties", mock_get_mors_with_properties + ): + salt.utils.vmware.get_datacenters( + self.mock_si, datacenter_names=["fake_dc1"] + ) mock_get_mors_with_properties.assert_called_once_with( - self.mock_si, vim.Datacenter, property_list=['name']) + self.mock_si, vim.Datacenter, property_list=["name"] + ) def test_get_mors_with_properties_returns_empty_array(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=[])): - res = salt.utils.vmware.get_datacenters(self.mock_si, - datacenter_names=['fake_dc1']) + with patch( + "salt.utils.vmware.get_mors_with_properties", MagicMock(return_value=[]) + ): + res = salt.utils.vmware.get_datacenters( + self.mock_si, datacenter_names=["fake_dc1"] + ) self.assertEqual(res, []) def test_no_parameters(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_entries)): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_entries), + ): res = salt.utils.vmware.get_datacenters(self.mock_si) self.assertEqual(res, []) def test_datastore_not_found(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_entries)): - res = salt.utils.vmware.get_datacenters(self.mock_si, - datacenter_names=['fake_dc']) + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_entries), + ): + res = salt.utils.vmware.get_datacenters( + self.mock_si, datacenter_names=["fake_dc"] + ) self.assertEqual(res, []) def test_datastore_found(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_entries)): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_entries), + ): res = salt.utils.vmware.get_datacenters( - self.mock_si, datacenter_names=['fake_dc2']) + self.mock_si, datacenter_names=["fake_dc2"] + ) self.assertEqual(res, [self.mock_dc2]) def test_get_all_datastores(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_entries)): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_entries), + ): res = salt.utils.vmware.get_datacenters( - self.mock_si, get_all_datacenters=True) + self.mock_si, get_all_datacenters=True + ) self.assertEqual(res, [self.mock_dc1, self.mock_dc2]) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetDatacenterTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_datacenter - ''' + """ def setUp(self): patches = ( - ('salt.utils.vmware.get_datacenters', MagicMock(return_value=[MagicMock()])), + ( + "salt.utils.vmware.get_datacenters", + MagicMock(return_value=[MagicMock()]), + ), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -2295,37 +2527,36 @@ class GetDatacenterTestCase(TestCase): def test_get_datacenters_call(self): mock_get_datacenters = MagicMock(return_value=[MagicMock()]) - with patch('salt.utils.vmware.get_datacenters', - mock_get_datacenters): - salt.utils.vmware.get_datacenter(self.mock_si, 'fake_dc1') + with patch("salt.utils.vmware.get_datacenters", mock_get_datacenters): + salt.utils.vmware.get_datacenter(self.mock_si, "fake_dc1") mock_get_datacenters.assert_called_once_with( - self.mock_si, datacenter_names=['fake_dc1']) + self.mock_si, datacenter_names=["fake_dc1"] + ) def test_no_datacenters_returned(self): - with patch('salt.utils.vmware.get_datacenters', - MagicMock(return_value=[])): + with patch("salt.utils.vmware.get_datacenters", MagicMock(return_value=[])): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - salt.utils.vmware.get_datacenter(self.mock_si, 'fake_dc1') - self.assertEqual('Datacenter \'fake_dc1\' was not found', - excinfo.exception.strerror) + salt.utils.vmware.get_datacenter(self.mock_si, "fake_dc1") + self.assertEqual( + "Datacenter 'fake_dc1' was not found", excinfo.exception.strerror + ) def test_get_datacenter_return(self): - with patch('salt.utils.vmware.get_datacenters', - MagicMock(return_value=[self.mock_dc])): - res = salt.utils.vmware.get_datacenter(self.mock_si, 'fake_dc1') + with patch( + "salt.utils.vmware.get_datacenters", MagicMock(return_value=[self.mock_dc]) + ): + res = salt.utils.vmware.get_datacenter(self.mock_si, "fake_dc1") self.assertEqual(res, self.mock_dc) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class CreateDatacenterTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.create_datacenter - ''' + """ def setUp(self): - patches = ( - ('salt.utils.vmware.get_root_folder', MagicMock()), - ) + patches = (("salt.utils.vmware.get_root_folder", MagicMock()),) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() @@ -2333,60 +2564,67 @@ class CreateDatacenterTestCase(TestCase): self.mock_si = MagicMock() self.mock_dc = MagicMock() self.mock_create_datacenter = MagicMock(return_value=self.mock_dc) - self.mock_root_folder = MagicMock( - CreateDatacenter=self.mock_create_datacenter) + self.mock_root_folder = MagicMock(CreateDatacenter=self.mock_create_datacenter) def test_get_root_folder(self): mock_get_root_folder = MagicMock() - with patch('salt.utils.vmware.get_root_folder', mock_get_root_folder): - salt.utils.vmware.create_datacenter(self.mock_si, 'fake_dc') + with patch("salt.utils.vmware.get_root_folder", mock_get_root_folder): + salt.utils.vmware.create_datacenter(self.mock_si, "fake_dc") mock_get_root_folder.assert_called_once_with(self.mock_si) def test_create_datacenter_call(self): - with patch('salt.utils.vmware.get_root_folder', - MagicMock(return_value=self.mock_root_folder)): - salt.utils.vmware.create_datacenter(self.mock_si, 'fake_dc') - self.mock_create_datacenter.assert_called_once_with('fake_dc') + with patch( + "salt.utils.vmware.get_root_folder", + MagicMock(return_value=self.mock_root_folder), + ): + salt.utils.vmware.create_datacenter(self.mock_si, "fake_dc") + self.mock_create_datacenter.assert_called_once_with("fake_dc") def test_create_datacenter_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_root_folder = MagicMock( - CreateDatacenter=MagicMock(side_effect=exc)) - with patch('salt.utils.vmware.get_root_folder', - MagicMock(return_value=self.mock_root_folder)): + exc.privilegeId = "Fake privilege" + self.mock_root_folder = MagicMock(CreateDatacenter=MagicMock(side_effect=exc)) + with patch( + "salt.utils.vmware.get_root_folder", + MagicMock(return_value=self.mock_root_folder), + ): with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.create_datacenter(self.mock_si, 'fake_dc') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.create_datacenter(self.mock_si, "fake_dc") + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_create_datacenter_raise_vim_fault(self): exc = vim.VimFault() - exc.msg = 'VimFault msg' - self.mock_root_folder = MagicMock( - CreateDatacenter=MagicMock(side_effect=exc)) - with patch('salt.utils.vmware.get_root_folder', - MagicMock(return_value=self.mock_root_folder)): + exc.msg = "VimFault msg" + self.mock_root_folder = MagicMock(CreateDatacenter=MagicMock(side_effect=exc)) + with patch( + "salt.utils.vmware.get_root_folder", + MagicMock(return_value=self.mock_root_folder), + ): with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.create_datacenter(self.mock_si, 'fake_dc') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.create_datacenter(self.mock_si, "fake_dc") + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_create_datacenter_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_root_folder = MagicMock( - CreateDatacenter=MagicMock(side_effect=exc)) - with patch('salt.utils.vmware.get_root_folder', - MagicMock(return_value=self.mock_root_folder)): + exc.msg = "RuntimeFault msg" + self.mock_root_folder = MagicMock(CreateDatacenter=MagicMock(side_effect=exc)) + with patch( + "salt.utils.vmware.get_root_folder", + MagicMock(return_value=self.mock_root_folder), + ): with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.create_datacenter(self.mock_si, 'fake_dc') - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.create_datacenter(self.mock_si, "fake_dc") + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_datastore_successfully_created(self): - with patch('salt.utils.vmware.get_root_folder', - MagicMock(return_value=self.mock_root_folder)): - res = salt.utils.vmware.create_datacenter(self.mock_si, 'fake_dc') + with patch( + "salt.utils.vmware.get_root_folder", + MagicMock(return_value=self.mock_root_folder), + ): + res = salt.utils.vmware.create_datacenter(self.mock_si, "fake_dc") self.assertEqual(res, self.mock_dc) @@ -2394,64 +2632,83 @@ class FakeTaskClass(object): pass -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetDvssTestCase(TestCase): def setUp(self): self.mock_si = MagicMock() self.mock_dc_ref = MagicMock() self.mock_traversal_spec = MagicMock() - self.mock_items = [{'object': MagicMock(), - 'name': 'fake_dvs1'}, - {'object': MagicMock(), - 'name': 'fake_dvs2'}, - {'object': MagicMock(), - 'name': 'fake_dvs3'}] + self.mock_items = [ + {"object": MagicMock(), "name": "fake_dvs1"}, + {"object": MagicMock(), "name": "fake_dvs2"}, + {"object": MagicMock(), "name": "fake_dvs3"}, + ] self.mock_get_mors = MagicMock(return_value=self.mock_items) patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock()), - ('salt.utils.vmware.get_mors_with_properties', - self.mock_get_mors), - ('salt.utils.vmware.get_service_instance_from_managed_object', - MagicMock(return_value=self.mock_si)), - ('salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - MagicMock(return_value=self.mock_traversal_spec))) + ("salt.utils.vmware.get_managed_object_name", MagicMock()), + ("salt.utils.vmware.get_mors_with_properties", self.mock_get_mors), + ( + "salt.utils.vmware.get_service_instance_from_managed_object", + MagicMock(return_value=self.mock_si), + ), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + MagicMock(return_value=self.mock_traversal_spec), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_dc_ref', 'mock_traversal_spec', - 'mock_items', 'mock_get_mors'): + for attr in ( + "mock_si", + "mock_dc_ref", + "mock_traversal_spec", + "mock_items", + "mock_get_mors", + ): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.get_dvss(self.mock_dc_ref) mock_get_managed_object_name.assert_called_once_with(self.mock_dc_ref) def test_traversal_spec(self): - mock_traversal_spec = MagicMock(return_value='traversal_spec') + mock_traversal_spec = MagicMock(return_value="traversal_spec") with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec, + ): salt.utils.vmware.get_dvss(self.mock_dc_ref) mock_traversal_spec.assert_has_calls( - [call(path='childEntity', skip=False, type=vim.Folder), - call(path='networkFolder', skip=True, type=vim.Datacenter, - selectSet=['traversal_spec'])]) + [ + call(path="childEntity", skip=False, type=vim.Folder), + call( + path="networkFolder", + skip=True, + type=vim.Datacenter, + selectSet=["traversal_spec"], + ), + ] + ) def test_get_mors_with_properties(self): salt.utils.vmware.get_dvss(self.mock_dc_ref) self.mock_get_mors.assert_called_once_with( - self.mock_si, vim.DistributedVirtualSwitch, - container_ref=self.mock_dc_ref, property_list=['name'], - traversal_spec=self.mock_traversal_spec) + self.mock_si, + vim.DistributedVirtualSwitch, + container_ref=self.mock_dc_ref, + property_list=["name"], + traversal_spec=self.mock_traversal_spec, + ) def test_get_no_dvss(self): ret = salt.utils.vmware.get_dvss(self.mock_dc_ref) @@ -2459,651 +2716,771 @@ class GetDvssTestCase(TestCase): def test_get_all_dvss(self): ret = salt.utils.vmware.get_dvss(self.mock_dc_ref, get_all_dvss=True) - self.assertEqual(ret, [i['object'] for i in self.mock_items]) + self.assertEqual(ret, [i["object"] for i in self.mock_items]) def test_filtered_all_dvss(self): - ret = salt.utils.vmware.get_dvss(self.mock_dc_ref, - dvs_names=['fake_dvs1', 'fake_dvs3', 'no_dvs']) - self.assertEqual(ret, [self.mock_items[0]['object'], - self.mock_items[2]['object']]) + ret = salt.utils.vmware.get_dvss( + self.mock_dc_ref, dvs_names=["fake_dvs1", "fake_dvs3", "no_dvs"] + ) + self.assertEqual( + ret, [self.mock_items[0]["object"], self.mock_items[2]["object"]] + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetNetworkFolderTestCase(TestCase): def setUp(self): self.mock_si = MagicMock() self.mock_dc_ref = MagicMock() self.mock_traversal_spec = MagicMock() - self.mock_entries = [{'object': MagicMock(), - 'name': 'fake_netw_folder'}] + self.mock_entries = [{"object": MagicMock(), "name": "fake_netw_folder"}] self.mock_get_mors = MagicMock(return_value=self.mock_entries) patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_dc')), - ('salt.utils.vmware.get_service_instance_from_managed_object', - MagicMock(return_value=self.mock_si)), - ('salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - MagicMock(return_value=self.mock_traversal_spec)), - ('salt.utils.vmware.get_mors_with_properties', - self.mock_get_mors)) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_dc"), + ), + ( + "salt.utils.vmware.get_service_instance_from_managed_object", + MagicMock(return_value=self.mock_si), + ), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + MagicMock(return_value=self.mock_traversal_spec), + ), + ("salt.utils.vmware.get_mors_with_properties", self.mock_get_mors), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_dc_ref', 'mock_traversal_spec', - 'mock_entries', 'mock_get_mors'): + for attr in ( + "mock_si", + "mock_dc_ref", + "mock_traversal_spec", + "mock_entries", + "mock_get_mors", + ): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.get_network_folder(self.mock_dc_ref) mock_get_managed_object_name.assert_called_once_with(self.mock_dc_ref) def test_traversal_spec(self): - mock_traversal_spec = MagicMock(return_value='traversal_spec') + mock_traversal_spec = MagicMock(return_value="traversal_spec") with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec, + ): salt.utils.vmware.get_network_folder(self.mock_dc_ref) mock_traversal_spec.assert_called_once_with( - path='networkFolder', skip=False, type=vim.Datacenter) + path="networkFolder", skip=False, type=vim.Datacenter + ) def test_get_mors_with_properties(self): salt.utils.vmware.get_network_folder(self.mock_dc_ref) self.mock_get_mors.assert_called_once_with( - self.mock_si, vim.Folder, container_ref=self.mock_dc_ref, - property_list=['name'], traversal_spec=self.mock_traversal_spec) + self.mock_si, + vim.Folder, + container_ref=self.mock_dc_ref, + property_list=["name"], + traversal_spec=self.mock_traversal_spec, + ) def test_get_no_network_folder(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=[])): + with patch( + "salt.utils.vmware.get_mors_with_properties", MagicMock(return_value=[]) + ): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: salt.utils.vmware.get_network_folder(self.mock_dc_ref) - self.assertEqual(excinfo.exception.strerror, - 'Network folder in datacenter \'fake_dc\' wasn\'t ' - 'retrieved') + self.assertEqual( + excinfo.exception.strerror, + "Network folder in datacenter 'fake_dc' wasn't " "retrieved", + ) def test_get_network_folder(self): ret = salt.utils.vmware.get_network_folder(self.mock_dc_ref) - self.assertEqual(ret, self.mock_entries[0]['object']) + self.assertEqual(ret, self.mock_entries[0]["object"]) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class CreateDvsTestCase(TestCase): def setUp(self): self.mock_dc_ref = MagicMock() self.mock_dvs_create_spec = MagicMock() self.mock_task = MagicMock(spec=FakeTaskClass) - self.mock_netw_folder = \ - MagicMock(CreateDVS_Task=MagicMock( - return_value=self.mock_task)) + self.mock_netw_folder = MagicMock( + CreateDVS_Task=MagicMock(return_value=self.mock_task) + ) self.mock_wait_for_task = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_dc')), - ('salt.utils.vmware.get_network_folder', - MagicMock(return_value=self.mock_netw_folder)), - ('salt.utils.vmware.wait_for_task', self.mock_wait_for_task)) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_dc"), + ), + ( + "salt.utils.vmware.get_network_folder", + MagicMock(return_value=self.mock_netw_folder), + ), + ("salt.utils.vmware.wait_for_task", self.mock_wait_for_task), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_dc_ref', 'mock_dvs_create_spec', - 'mock_task', 'mock_netw_folder', 'mock_wait_for_task'): + for attr in ( + "mock_dc_ref", + "mock_dvs_create_spec", + "mock_task", + "mock_netw_folder", + "mock_wait_for_task", + ): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): - salt.utils.vmware.create_dvs(self.mock_dc_ref, 'fake_dvs') + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): + salt.utils.vmware.create_dvs(self.mock_dc_ref, "fake_dvs") mock_get_managed_object_name.assert_called_once_with(self.mock_dc_ref) def test_no_dvs_create_spec(self): mock_spec = MagicMock(configSpec=None) mock_config_spec = MagicMock() mock_dvs_create_spec = MagicMock(return_value=mock_spec) - mock_vmware_dvs_config_spec = \ - MagicMock(return_value=mock_config_spec) - with patch('salt.utils.vmware.vim.DVSCreateSpec', - mock_dvs_create_spec): - with patch('salt.utils.vmware.vim.VMwareDVSConfigSpec', - mock_vmware_dvs_config_spec): - salt.utils.vmware.create_dvs(self.mock_dc_ref, 'fake_dvs') + mock_vmware_dvs_config_spec = MagicMock(return_value=mock_config_spec) + with patch("salt.utils.vmware.vim.DVSCreateSpec", mock_dvs_create_spec): + with patch( + "salt.utils.vmware.vim.VMwareDVSConfigSpec", mock_vmware_dvs_config_spec + ): + salt.utils.vmware.create_dvs(self.mock_dc_ref, "fake_dvs") mock_dvs_create_spec.assert_called_once_with() mock_vmware_dvs_config_spec.assert_called_once_with() self.assertEqual(mock_spec.configSpec, mock_config_spec) - self.assertEqual(mock_config_spec.name, 'fake_dvs') + self.assertEqual(mock_config_spec.name, "fake_dvs") self.mock_netw_folder.CreateDVS_Task.assert_called_once_with(mock_spec) def test_get_network_folder(self): mock_get_network_folder = MagicMock() - with patch('salt.utils.vmware.get_network_folder', - mock_get_network_folder): - salt.utils.vmware.create_dvs(self.mock_dc_ref, 'fake_dvs') + with patch("salt.utils.vmware.get_network_folder", mock_get_network_folder): + salt.utils.vmware.create_dvs(self.mock_dc_ref, "fake_dvs") mock_get_network_folder.assert_called_once_with(self.mock_dc_ref) def test_create_dvs_task_passed_in_spec(self): - salt.utils.vmware.create_dvs(self.mock_dc_ref, 'fake_dvs', - dvs_create_spec=self.mock_dvs_create_spec) + salt.utils.vmware.create_dvs( + self.mock_dc_ref, "fake_dvs", dvs_create_spec=self.mock_dvs_create_spec + ) self.mock_netw_folder.CreateDVS_Task.assert_called_once_with( - self.mock_dvs_create_spec) + self.mock_dvs_create_spec + ) def test_create_dvs_task_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.mock_netw_folder.CreateDVS_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.create_dvs(self.mock_dc_ref, 'fake_dvs', - dvs_create_spec=self.mock_dvs_create_spec) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.create_dvs( + self.mock_dc_ref, "fake_dvs", dvs_create_spec=self.mock_dvs_create_spec + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_create_dvs_task_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.mock_netw_folder.CreateDVS_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.create_dvs(self.mock_dc_ref, 'fake_dvs', - dvs_create_spec=self.mock_dvs_create_spec) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.create_dvs( + self.mock_dc_ref, "fake_dvs", dvs_create_spec=self.mock_dvs_create_spec + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_create_dvs_task_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.mock_netw_folder.CreateDVS_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.create_dvs(self.mock_dc_ref, 'fake_dvs', - dvs_create_spec=self.mock_dvs_create_spec) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.create_dvs( + self.mock_dc_ref, "fake_dvs", dvs_create_spec=self.mock_dvs_create_spec + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_wait_for_tasks(self): - salt.utils.vmware.create_dvs(self.mock_dc_ref, 'fake_dvs', - dvs_create_spec=self.mock_dvs_create_spec) + salt.utils.vmware.create_dvs( + self.mock_dc_ref, "fake_dvs", dvs_create_spec=self.mock_dvs_create_spec + ) self.mock_wait_for_task.assert_called_once_with( - self.mock_task, 'fake_dvs', - '<class \'{}unit.utils.test_vmware.FakeTaskClass\'>'.format( - 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + self.mock_task, + "fake_dvs", + "<class '{}unit.utils.test_vmware.FakeTaskClass'>".format( + "tests." if RUNTIME_VARS.PYTEST_SESSION else "" + ), ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class UpdateDvsTestCase(TestCase): def setUp(self): self.mock_task = MagicMock(spec=FakeTaskClass) self.mock_dvs_ref = MagicMock( - ReconfigureDvs_Task=MagicMock(return_value=self.mock_task)) + ReconfigureDvs_Task=MagicMock(return_value=self.mock_task) + ) self.mock_dvs_spec = MagicMock() self.mock_wait_for_task = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_dvs')), - ('salt.utils.vmware.wait_for_task', self.mock_wait_for_task)) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_dvs"), + ), + ("salt.utils.vmware.wait_for_task", self.mock_wait_for_task), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_dvs_ref', 'mock_task', 'mock_dvs_spec', - 'mock_wait_for_task'): + for attr in ( + "mock_dvs_ref", + "mock_task", + "mock_dvs_spec", + "mock_wait_for_task", + ): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.update_dvs(self.mock_dvs_ref, self.mock_dvs_spec) mock_get_managed_object_name.assert_called_once_with(self.mock_dvs_ref) def test_reconfigure_dvs_task(self): salt.utils.vmware.update_dvs(self.mock_dvs_ref, self.mock_dvs_spec) self.mock_dvs_ref.ReconfigureDvs_Task.assert_called_once_with( - self.mock_dvs_spec) + self.mock_dvs_spec + ) def test_reconfigure_dvs_task_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.mock_dvs_ref.ReconfigureDvs_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.update_dvs(self.mock_dvs_ref, self.mock_dvs_spec) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_reconfigure_dvs_task_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.mock_dvs_ref.ReconfigureDvs_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.update_dvs(self.mock_dvs_ref, self.mock_dvs_spec) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_reconfigure_dvs_task_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.mock_dvs_ref.ReconfigureDvs_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.update_dvs(self.mock_dvs_ref, self.mock_dvs_spec) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_wait_for_tasks(self): salt.utils.vmware.update_dvs(self.mock_dvs_ref, self.mock_dvs_spec) self.mock_wait_for_task.assert_called_once_with( - self.mock_task, 'fake_dvs', - '<class \'{}unit.utils.test_vmware.FakeTaskClass\'>'.format( - 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + self.mock_task, + "fake_dvs", + "<class '{}unit.utils.test_vmware.FakeTaskClass'>".format( + "tests." if RUNTIME_VARS.PYTEST_SESSION else "" + ), ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class SetDvsNetworkResourceManagementEnabledTestCase(TestCase): def setUp(self): self.mock_enabled = MagicMock() - self.mock_dvs_ref = MagicMock( - EnableNetworkResourceManagement=MagicMock()) + self.mock_dvs_ref = MagicMock(EnableNetworkResourceManagement=MagicMock()) patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_dvs')),) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_dvs"), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_dvs_ref', 'mock_enabled'): + for attr in ("mock_dvs_ref", "mock_enabled"): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.set_dvs_network_resource_management_enabled( - self.mock_dvs_ref, self.mock_enabled) + self.mock_dvs_ref, self.mock_enabled + ) mock_get_managed_object_name.assert_called_once_with(self.mock_dvs_ref) def test_enable_network_resource_management(self): salt.utils.vmware.set_dvs_network_resource_management_enabled( - self.mock_dvs_ref, self.mock_enabled) + self.mock_dvs_ref, self.mock_enabled + ) self.mock_dvs_ref.EnableNetworkResourceManagement.assert_called_once_with( - enable=self.mock_enabled) + enable=self.mock_enabled + ) def test_enable_network_resource_management_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_dvs_ref.EnableNetworkResourceManagement = \ - MagicMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + self.mock_dvs_ref.EnableNetworkResourceManagement = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.set_dvs_network_resource_management_enabled( - self.mock_dvs_ref, self.mock_enabled) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.mock_dvs_ref, self.mock_enabled + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_enable_network_resource_management_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.mock_dvs_ref.EnableNetworkResourceManagement = \ - MagicMock(side_effect=exc) + exc.msg = "VimFault msg" + self.mock_dvs_ref.EnableNetworkResourceManagement = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.set_dvs_network_resource_management_enabled( - self.mock_dvs_ref, self.mock_enabled) + self.mock_dvs_ref, self.mock_enabled + ) def test_enable_network_resource_management_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_dvs_ref.EnableNetworkResourceManagement = \ - MagicMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + self.mock_dvs_ref.EnableNetworkResourceManagement = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.set_dvs_network_resource_management_enabled( - self.mock_dvs_ref, self.mock_enabled) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.mock_dvs_ref, self.mock_enabled + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetDvportgroupsTestCase(TestCase): def setUp(self): self.mock_si = MagicMock() self.mock_dc_ref = MagicMock(spec=vim.Datacenter) self.mock_dvs_ref = MagicMock(spec=vim.DistributedVirtualSwitch) self.mock_traversal_spec = MagicMock() - self.mock_items = [{'object': MagicMock(), - 'name': 'fake_pg1'}, - {'object': MagicMock(), - 'name': 'fake_pg2'}, - {'object': MagicMock(), - 'name': 'fake_pg3'}] + self.mock_items = [ + {"object": MagicMock(), "name": "fake_pg1"}, + {"object": MagicMock(), "name": "fake_pg2"}, + {"object": MagicMock(), "name": "fake_pg3"}, + ] self.mock_get_mors = MagicMock(return_value=self.mock_items) patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock()), - ('salt.utils.vmware.get_mors_with_properties', - self.mock_get_mors), - ('salt.utils.vmware.get_service_instance_from_managed_object', - MagicMock(return_value=self.mock_si)), - ('salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - MagicMock(return_value=self.mock_traversal_spec))) + ("salt.utils.vmware.get_managed_object_name", MagicMock()), + ("salt.utils.vmware.get_mors_with_properties", self.mock_get_mors), + ( + "salt.utils.vmware.get_service_instance_from_managed_object", + MagicMock(return_value=self.mock_si), + ), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + MagicMock(return_value=self.mock_traversal_spec), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_dc_ref', 'mock_dvs_ref', - 'mock_traversal_spec', 'mock_items', 'mock_get_mors'): + for attr in ( + "mock_si", + "mock_dc_ref", + "mock_dvs_ref", + "mock_traversal_spec", + "mock_items", + "mock_get_mors", + ): delattr(self, attr) def test_unsupported_parrent(self): with self.assertRaises(ArgumentValueError) as excinfo: salt.utils.vmware.get_dvportgroups(MagicMock()) - self.assertEqual(excinfo.exception.strerror, - 'Parent has to be either a datacenter, or a ' - 'distributed virtual switch') + self.assertEqual( + excinfo.exception.strerror, + "Parent has to be either a datacenter, or a " "distributed virtual switch", + ) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.get_dvportgroups(self.mock_dc_ref) mock_get_managed_object_name.assert_called_once_with(self.mock_dc_ref) def test_traversal_spec_datacenter_parent(self): - mock_traversal_spec = MagicMock(return_value='traversal_spec') + mock_traversal_spec = MagicMock(return_value="traversal_spec") with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec, + ): salt.utils.vmware.get_dvportgroups(self.mock_dc_ref) mock_traversal_spec.assert_has_calls( - [call(path='childEntity', skip=False, type=vim.Folder), - call(path='networkFolder', skip=True, type=vim.Datacenter, - selectSet=['traversal_spec'])]) + [ + call(path="childEntity", skip=False, type=vim.Folder), + call( + path="networkFolder", + skip=True, + type=vim.Datacenter, + selectSet=["traversal_spec"], + ), + ] + ) def test_traversal_spec_dvs_parent(self): - mock_traversal_spec = MagicMock(return_value='traversal_spec') + mock_traversal_spec = MagicMock(return_value="traversal_spec") with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec, + ): salt.utils.vmware.get_dvportgroups(self.mock_dvs_ref) mock_traversal_spec.assert_called_once_with( - path='portgroup', skip=False, type=vim.DistributedVirtualSwitch) + path="portgroup", skip=False, type=vim.DistributedVirtualSwitch + ) def test_get_mors_with_properties(self): salt.utils.vmware.get_dvportgroups(self.mock_dvs_ref) self.mock_get_mors.assert_called_once_with( - self.mock_si, vim.DistributedVirtualPortgroup, - container_ref=self.mock_dvs_ref, property_list=['name'], - traversal_spec=self.mock_traversal_spec) + self.mock_si, + vim.DistributedVirtualPortgroup, + container_ref=self.mock_dvs_ref, + property_list=["name"], + traversal_spec=self.mock_traversal_spec, + ) def test_get_no_pgs(self): ret = salt.utils.vmware.get_dvportgroups(self.mock_dvs_ref) self.assertEqual(ret, []) def test_get_all_pgs(self): - ret = salt.utils.vmware.get_dvportgroups(self.mock_dvs_ref, - get_all_portgroups=True) - self.assertEqual(ret, [i['object'] for i in self.mock_items]) + ret = salt.utils.vmware.get_dvportgroups( + self.mock_dvs_ref, get_all_portgroups=True + ) + self.assertEqual(ret, [i["object"] for i in self.mock_items]) def test_filtered_pgs(self): - ret = salt.utils.vmware.get_dvss(self.mock_dc_ref, - dvs_names=['fake_pg1', 'fake_pg3', 'no_pg']) - self.assertEqual(ret, [self.mock_items[0]['object'], - self.mock_items[2]['object']]) + ret = salt.utils.vmware.get_dvss( + self.mock_dc_ref, dvs_names=["fake_pg1", "fake_pg3", "no_pg"] + ) + self.assertEqual( + ret, [self.mock_items[0]["object"], self.mock_items[2]["object"]] + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetUplinkDvportgroupTestCase(TestCase): def setUp(self): self.mock_si = MagicMock() self.mock_dvs_ref = MagicMock(spec=vim.DistributedVirtualSwitch) self.mock_traversal_spec = MagicMock() - self.mock_items = [{'object': MagicMock(), - 'tag': [MagicMock(key='fake_tag')]}, - {'object': MagicMock(), - 'tag': [MagicMock(key='SYSTEM/DVS.UPLINKPG')]}] + self.mock_items = [ + {"object": MagicMock(), "tag": [MagicMock(key="fake_tag")]}, + {"object": MagicMock(), "tag": [MagicMock(key="SYSTEM/DVS.UPLINKPG")]}, + ] self.mock_get_mors = MagicMock(return_value=self.mock_items) patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_dvs')), - ('salt.utils.vmware.get_mors_with_properties', - self.mock_get_mors), - ('salt.utils.vmware.get_service_instance_from_managed_object', - MagicMock(return_value=self.mock_si)), - ('salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - MagicMock(return_value=self.mock_traversal_spec))) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_dvs"), + ), + ("salt.utils.vmware.get_mors_with_properties", self.mock_get_mors), + ( + "salt.utils.vmware.get_service_instance_from_managed_object", + MagicMock(return_value=self.mock_si), + ), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + MagicMock(return_value=self.mock_traversal_spec), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_dvs_ref', 'mock_traversal_spec', - 'mock_items', 'mock_get_mors'): + for attr in ( + "mock_si", + "mock_dvs_ref", + "mock_traversal_spec", + "mock_items", + "mock_get_mors", + ): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.get_uplink_dvportgroup(self.mock_dvs_ref) mock_get_managed_object_name.assert_called_once_with(self.mock_dvs_ref) def test_traversal_spec(self): - mock_traversal_spec = MagicMock(return_value='traversal_spec') + mock_traversal_spec = MagicMock(return_value="traversal_spec") with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec, + ): salt.utils.vmware.get_uplink_dvportgroup(self.mock_dvs_ref) mock_traversal_spec.assert_called_once_with( - path='portgroup', skip=False, type=vim.DistributedVirtualSwitch) + path="portgroup", skip=False, type=vim.DistributedVirtualSwitch + ) def test_get_mors_with_properties(self): salt.utils.vmware.get_uplink_dvportgroup(self.mock_dvs_ref) self.mock_get_mors.assert_called_once_with( - self.mock_si, vim.DistributedVirtualPortgroup, - container_ref=self.mock_dvs_ref, property_list=['tag'], - traversal_spec=self.mock_traversal_spec) + self.mock_si, + vim.DistributedVirtualPortgroup, + container_ref=self.mock_dvs_ref, + property_list=["tag"], + traversal_spec=self.mock_traversal_spec, + ) def test_get_no_uplink_pg(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=[])): + with patch( + "salt.utils.vmware.get_mors_with_properties", MagicMock(return_value=[]) + ): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: salt.utils.vmware.get_uplink_dvportgroup(self.mock_dvs_ref) - self.assertEqual(excinfo.exception.strerror, - 'Uplink portgroup of DVS \'fake_dvs\' wasn\'t found') + self.assertEqual( + excinfo.exception.strerror, + "Uplink portgroup of DVS 'fake_dvs' wasn't found", + ) def test_get_uplink_pg(self): ret = salt.utils.vmware.get_uplink_dvportgroup(self.mock_dvs_ref) - self.assertEqual(ret, self.mock_items[1]['object']) + self.assertEqual(ret, self.mock_items[1]["object"]) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class CreateDvportgroupTestCase(TestCase): def setUp(self): self.mock_pg_spec = MagicMock() self.mock_task = MagicMock(spec=FakeTaskClass) - self.mock_dvs_ref = \ - MagicMock(CreateDVPortgroup_Task=MagicMock( - return_value=self.mock_task)) + self.mock_dvs_ref = MagicMock( + CreateDVPortgroup_Task=MagicMock(return_value=self.mock_task) + ) self.mock_wait_for_task = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_dvs')), - ('salt.utils.vmware.wait_for_task', self.mock_wait_for_task)) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_dvs"), + ), + ("salt.utils.vmware.wait_for_task", self.mock_wait_for_task), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_pg_spec', 'mock_dvs_ref', 'mock_task', - 'mock_wait_for_task'): + for attr in ("mock_pg_spec", "mock_dvs_ref", "mock_task", "mock_wait_for_task"): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.create_dvportgroup(self.mock_dvs_ref, self.mock_pg_spec) mock_get_managed_object_name.assert_called_once_with(self.mock_dvs_ref) def test_create_dvporgroup_task(self): salt.utils.vmware.create_dvportgroup(self.mock_dvs_ref, self.mock_pg_spec) self.mock_dvs_ref.CreateDVPortgroup_Task.assert_called_once_with( - self.mock_pg_spec) + self.mock_pg_spec + ) def test_create_dvporgroup_task_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.mock_dvs_ref.CreateDVPortgroup_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.create_dvportgroup(self.mock_dvs_ref, self.mock_pg_spec) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_create_dvporgroup_task_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.mock_dvs_ref.CreateDVPortgroup_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.create_dvportgroup(self.mock_dvs_ref, self.mock_pg_spec) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_create_dvporgroup_task_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.mock_dvs_ref.CreateDVPortgroup_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.create_dvportgroup(self.mock_dvs_ref, self.mock_pg_spec) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_wait_for_tasks(self): salt.utils.vmware.create_dvportgroup(self.mock_dvs_ref, self.mock_pg_spec) self.mock_wait_for_task.assert_called_once_with( - self.mock_task, 'fake_dvs', - '<class \'{}unit.utils.test_vmware.FakeTaskClass\'>'.format( - 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + self.mock_task, + "fake_dvs", + "<class '{}unit.utils.test_vmware.FakeTaskClass'>".format( + "tests." if RUNTIME_VARS.PYTEST_SESSION else "" + ), ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class UpdateDvportgroupTestCase(TestCase): def setUp(self): self.mock_pg_spec = MagicMock() self.mock_task = MagicMock(spec=FakeTaskClass) - self.mock_pg_ref = \ - MagicMock(ReconfigureDVPortgroup_Task=MagicMock( - return_value=self.mock_task)) + self.mock_pg_ref = MagicMock( + ReconfigureDVPortgroup_Task=MagicMock(return_value=self.mock_task) + ) self.mock_wait_for_task = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_pg')), - ('salt.utils.vmware.wait_for_task', self.mock_wait_for_task)) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_pg"), + ), + ("salt.utils.vmware.wait_for_task", self.mock_wait_for_task), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_pg_spec', 'mock_pg_ref', 'mock_task', - 'mock_wait_for_task'): + for attr in ("mock_pg_spec", "mock_pg_ref", "mock_task", "mock_wait_for_task"): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.update_dvportgroup(self.mock_pg_ref, self.mock_pg_spec) mock_get_managed_object_name.assert_called_once_with(self.mock_pg_ref) def test_reconfigure_dvporgroup_task(self): salt.utils.vmware.update_dvportgroup(self.mock_pg_ref, self.mock_pg_spec) self.mock_pg_ref.ReconfigureDVPortgroup_Task.assert_called_once_with( - self.mock_pg_spec) + self.mock_pg_spec + ) def test_reconfigure_dvporgroup_task_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_pg_ref.ReconfigureDVPortgroup_Task = \ - MagicMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + self.mock_pg_ref.ReconfigureDVPortgroup_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.update_dvportgroup(self.mock_pg_ref, self.mock_pg_spec) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_reconfigure_dvporgroup_task_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.mock_pg_ref.ReconfigureDVPortgroup_Task = \ - MagicMock(side_effect=exc) + exc.msg = "VimFault msg" + self.mock_pg_ref.ReconfigureDVPortgroup_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.update_dvportgroup(self.mock_pg_ref, self.mock_pg_spec) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_reconfigure_dvporgroup_task_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_pg_ref.ReconfigureDVPortgroup_Task = \ - MagicMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + self.mock_pg_ref.ReconfigureDVPortgroup_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.update_dvportgroup(self.mock_pg_ref, self.mock_pg_spec) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_wait_for_tasks(self): salt.utils.vmware.update_dvportgroup(self.mock_pg_ref, self.mock_pg_spec) self.mock_wait_for_task.assert_called_once_with( - self.mock_task, 'fake_pg', - '<class \'{}unit.utils.test_vmware.FakeTaskClass\'>'.format( - 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + self.mock_task, + "fake_pg", + "<class '{}unit.utils.test_vmware.FakeTaskClass'>".format( + "tests." if RUNTIME_VARS.PYTEST_SESSION else "" + ), ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class RemoveDvportgroupTestCase(TestCase): def setUp(self): self.mock_task = MagicMock(spec=FakeTaskClass) - self.mock_pg_ref = \ - MagicMock(Destroy_Task=MagicMock( - return_value=self.mock_task)) + self.mock_pg_ref = MagicMock( + Destroy_Task=MagicMock(return_value=self.mock_task) + ) self.mock_wait_for_task = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_pg')), - ('salt.utils.vmware.wait_for_task', self.mock_wait_for_task)) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_pg"), + ), + ("salt.utils.vmware.wait_for_task", self.mock_wait_for_task), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_pg_ref', 'mock_task', 'mock_wait_for_task'): + for attr in ("mock_pg_ref", "mock_task", "mock_wait_for_task"): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): salt.utils.vmware.remove_dvportgroup(self.mock_pg_ref) mock_get_managed_object_name.assert_called_once_with(self.mock_pg_ref) @@ -3113,50 +3490,53 @@ class RemoveDvportgroupTestCase(TestCase): def test_destroy_task_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.mock_pg_ref.Destroy_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.remove_dvportgroup(self.mock_pg_ref) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_destroy_treconfigure_dvporgroup_task_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.mock_pg_ref.Destroy_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.remove_dvportgroup(self.mock_pg_ref) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_destroy_treconfigure_dvporgroup_task_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.mock_pg_ref.Destroy_Task = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.remove_dvportgroup(self.mock_pg_ref) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_wait_for_tasks(self): salt.utils.vmware.remove_dvportgroup(self.mock_pg_ref) self.mock_wait_for_task.assert_called_once_with( - self.mock_task, 'fake_pg', - '<class \'{}unit.utils.test_vmware.FakeTaskClass\'>'.format( - 'tests.' if RUNTIME_VARS.PYTEST_SESSION else '') + self.mock_task, + "fake_pg", + "<class '{}unit.utils.test_vmware.FakeTaskClass'>".format( + "tests." if RUNTIME_VARS.PYTEST_SESSION else "" + ), ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetHostsTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_hosts - ''' + """ def setUp(self): patches = ( - ('salt.utils.vmware.get_mors_with_properties', MagicMock(return_value=[])), - ('salt.utils.vmware.get_datacenter', MagicMock(return_value=None)), - ('salt.utils.vmware.get_cluster', MagicMock(return_value=None)) + ("salt.utils.vmware.get_mors_with_properties", MagicMock(return_value=[])), + ("salt.utils.vmware.get_datacenter", MagicMock(return_value=None)), + ("salt.utils.vmware.get_cluster", MagicMock(return_value=None)), ) for mod, mock in patches: patcher = patch(mod, mock) @@ -3164,258 +3544,287 @@ class GetHostsTestCase(TestCase): self.addCleanup(patcher.stop) self.mock_root_folder = MagicMock() self.mock_si = MagicMock() - self.mock_host1, self.mock_host2, self.mock_host3 = MagicMock(), \ - MagicMock(), MagicMock() - self.mock_prop_host1 = {'name': 'fake_hostname1', - 'object': self.mock_host1} - self.mock_prop_host2 = {'name': 'fake_hostname2', - 'object': self.mock_host2} - self.mock_prop_host3 = {'name': 'fake_hostname3', - 'object': self.mock_host3} - self.mock_prop_hosts = [self.mock_prop_host1, self.mock_prop_host2, - self.mock_prop_host3] + self.mock_host1, self.mock_host2, self.mock_host3 = ( + MagicMock(), + MagicMock(), + MagicMock(), + ) + self.mock_prop_host1 = {"name": "fake_hostname1", "object": self.mock_host1} + self.mock_prop_host2 = {"name": "fake_hostname2", "object": self.mock_host2} + self.mock_prop_host3 = {"name": "fake_hostname3", "object": self.mock_host3} + self.mock_prop_hosts = [ + self.mock_prop_host1, + self.mock_prop_host2, + self.mock_prop_host3, + ] def test_cluster_no_datacenter(self): with self.assertRaises(ArgumentValueError) as excinfo: - salt.utils.vmware.get_hosts(self.mock_si, - cluster_name='fake_cluster') - self.assertEqual(excinfo.exception.strerror, - 'Must specify the datacenter when specifying the ' - 'cluster') + salt.utils.vmware.get_hosts(self.mock_si, cluster_name="fake_cluster") + self.assertEqual( + excinfo.exception.strerror, + "Must specify the datacenter when specifying the " "cluster", + ) def test_get_si_no_datacenter_no_cluster(self): mock_get_mors = MagicMock() mock_get_root_folder = MagicMock(return_value=self.mock_root_folder) - with patch('salt.utils.vmware.get_root_folder', mock_get_root_folder): - with patch('salt.utils.vmware.get_mors_with_properties', - mock_get_mors): + with patch("salt.utils.vmware.get_root_folder", mock_get_root_folder): + with patch("salt.utils.vmware.get_mors_with_properties", mock_get_mors): salt.utils.vmware.get_hosts(self.mock_si) mock_get_root_folder.assert_called_once_with(self.mock_si) mock_get_mors.assert_called_once_with( - self.mock_si, vim.HostSystem, container_ref=self.mock_root_folder, - property_list=['name']) + self.mock_si, + vim.HostSystem, + container_ref=self.mock_root_folder, + property_list=["name"], + ) def test_get_si_datacenter_name_no_cluster_name(self): mock_dc = MagicMock() mock_get_dc = MagicMock(return_value=mock_dc) mock_get_mors = MagicMock() - with patch('salt.utils.vmware.get_datacenter', mock_get_dc): - with patch('salt.utils.vmware.get_mors_with_properties', - mock_get_mors): - salt.utils.vmware.get_hosts(self.mock_si, - datacenter_name='fake_datacenter') - mock_get_dc.assert_called_once_with(self.mock_si, 'fake_datacenter') - mock_get_mors.assert_called_once_with(self.mock_si, - vim.HostSystem, - container_ref=mock_dc, - property_list=['name']) + with patch("salt.utils.vmware.get_datacenter", mock_get_dc): + with patch("salt.utils.vmware.get_mors_with_properties", mock_get_mors): + salt.utils.vmware.get_hosts( + self.mock_si, datacenter_name="fake_datacenter" + ) + mock_get_dc.assert_called_once_with(self.mock_si, "fake_datacenter") + mock_get_mors.assert_called_once_with( + self.mock_si, vim.HostSystem, container_ref=mock_dc, property_list=["name"] + ) def test_get_si_datacenter_name_and_cluster_name(self): mock_dc = MagicMock() mock_get_dc = MagicMock(return_value=mock_dc) mock_get_cl = MagicMock() mock_get_mors = MagicMock() - with patch('salt.utils.vmware.get_datacenter', mock_get_dc): - with patch('salt.utils.vmware.get_cluster', mock_get_cl): - with patch('salt.utils.vmware.get_mors_with_properties', - mock_get_mors): + with patch("salt.utils.vmware.get_datacenter", mock_get_dc): + with patch("salt.utils.vmware.get_cluster", mock_get_cl): + with patch("salt.utils.vmware.get_mors_with_properties", mock_get_mors): salt.utils.vmware.get_hosts( - self.mock_si, datacenter_name='fake_datacenter', - cluster_name='fake_cluster') - mock_get_dc.assert_called_once_with(self.mock_si, 'fake_datacenter') - mock_get_mors.assert_called_once_with(self.mock_si, - vim.HostSystem, - container_ref=mock_dc, - property_list=['name', 'parent']) + self.mock_si, + datacenter_name="fake_datacenter", + cluster_name="fake_cluster", + ) + mock_get_dc.assert_called_once_with(self.mock_si, "fake_datacenter") + mock_get_mors.assert_called_once_with( + self.mock_si, + vim.HostSystem, + container_ref=mock_dc, + property_list=["name", "parent"], + ) def test_host_get_all_hosts(self): - with patch('salt.utils.vmware.get_root_folder', - MagicMock(return_value=self.mock_root_folder)): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_prop_hosts)): + with patch( + "salt.utils.vmware.get_root_folder", + MagicMock(return_value=self.mock_root_folder), + ): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_prop_hosts), + ): res = salt.utils.vmware.get_hosts(self.mock_si, get_all_hosts=True) - self.assertEqual(res, [self.mock_host1, self.mock_host2, - self.mock_host3]) + self.assertEqual(res, [self.mock_host1, self.mock_host2, self.mock_host3]) def test_filter_hostname(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_prop_hosts)): - res = salt.utils.vmware.get_hosts(self.mock_si, - host_names=['fake_hostname1', - 'fake_hostname2']) + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_prop_hosts), + ): + res = salt.utils.vmware.get_hosts( + self.mock_si, host_names=["fake_hostname1", "fake_hostname2"] + ) self.assertEqual(res, [self.mock_host1, self.mock_host2]) def test_get_all_host_flag_not_set_and_no_host_names(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_prop_hosts)): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_prop_hosts), + ): res = salt.utils.vmware.get_hosts(self.mock_si) self.assertEqual(res, []) def test_filter_cluster(self): - self.mock_prop_host1['parent'] = vim.ClusterComputeResource('cluster') - self.mock_prop_host2['parent'] = vim.ClusterComputeResource('cluster') - self.mock_prop_host3['parent'] = vim.Datacenter('dc') + self.mock_prop_host1["parent"] = vim.ClusterComputeResource("cluster") + self.mock_prop_host2["parent"] = vim.ClusterComputeResource("cluster") + self.mock_prop_host3["parent"] = vim.Datacenter("dc") mock_get_cl_name = MagicMock( - side_effect=['fake_bad_cluster', 'fake_good_cluster']) - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_prop_hosts)): - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_cl_name): + side_effect=["fake_bad_cluster", "fake_good_cluster"] + ) + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_prop_hosts), + ): + with patch("salt.utils.vmware.get_managed_object_name", mock_get_cl_name): res = salt.utils.vmware.get_hosts( - self.mock_si, datacenter_name='fake_datacenter', - cluster_name='fake_good_cluster', get_all_hosts=True) + self.mock_si, + datacenter_name="fake_datacenter", + cluster_name="fake_good_cluster", + get_all_hosts=True, + ) self.assertEqual(mock_get_cl_name.call_count, 2) self.assertEqual(res, [self.mock_host2]) def test_no_hosts(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=[])): + with patch( + "salt.utils.vmware.get_mors_with_properties", MagicMock(return_value=[]) + ): res = salt.utils.vmware.get_hosts(self.mock_si, get_all_hosts=True) self.assertEqual(res, []) def test_one_host_returned(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=[self.mock_prop_host1])): + with patch( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=[self.mock_prop_host1]), + ): res = salt.utils.vmware.get_hosts(self.mock_si, get_all_hosts=True) self.assertEqual(res, [self.mock_host1]) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetLicenseManagerTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_license_manager - ''' + """ def setUp(self): self.mock_si = MagicMock() self.mock_lic_mgr = MagicMock() type(self.mock_si.content).licenseManager = PropertyMock( - return_value=self.mock_lic_mgr) + return_value=self.mock_lic_mgr + ) def tearDown(self): - for attr in ('mock_si', 'mock_lic_mgr'): + for attr in ("mock_si", "mock_lic_mgr"): delattr(self, attr) def test_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - type(self.mock_si.content).licenseManager = PropertyMock( - side_effect=exc) + exc.privilegeId = "Fake privilege" + type(self.mock_si.content).licenseManager = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_license_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - type(self.mock_si.content).licenseManager = PropertyMock( - side_effect=exc) + exc.msg = "VimFault msg" + type(self.mock_si.content).licenseManager = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_license_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - type(self.mock_si.content).licenseManager = PropertyMock( - side_effect=exc) + exc.msg = "RuntimeFault msg" + type(self.mock_si.content).licenseManager = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.get_license_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_valid_assignment_manager(self): ret = salt.utils.vmware.get_license_manager(self.mock_si) self.assertEqual(ret, self.mock_lic_mgr) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetLicenseAssignmentManagerTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_license_assignment_manager - ''' + """ def setUp(self): self.mock_si = MagicMock() self.mock_lic_assign_mgr = MagicMock() - type(self.mock_si.content.licenseManager).licenseAssignmentManager = \ - PropertyMock(return_value=self.mock_lic_assign_mgr) + type( + self.mock_si.content.licenseManager + ).licenseAssignmentManager = PropertyMock(return_value=self.mock_lic_assign_mgr) def tearDown(self): - for attr in ('mock_si', 'mock_lic_assign_mgr'): + for attr in ("mock_si", "mock_lic_assign_mgr"): delattr(self, attr) def test_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - type(self.mock_si.content.licenseManager).licenseAssignmentManager = \ - PropertyMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + type( + self.mock_si.content.licenseManager + ).licenseAssignmentManager = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_license_assignment_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - type(self.mock_si.content.licenseManager).licenseAssignmentManager = \ - PropertyMock(side_effect=exc) + exc.msg = "VimFault msg" + type( + self.mock_si.content.licenseManager + ).licenseAssignmentManager = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_license_assignment_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - type(self.mock_si.content.licenseManager).licenseAssignmentManager = \ - PropertyMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + type( + self.mock_si.content.licenseManager + ).licenseAssignmentManager = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.get_license_assignment_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_empty_license_assignment_manager(self): - type(self.mock_si.content.licenseManager).licenseAssignmentManager = \ - PropertyMock(return_value=None) + type( + self.mock_si.content.licenseManager + ).licenseAssignmentManager = PropertyMock(return_value=None) with self.assertRaises(VMwareObjectRetrievalError) as excinfo: salt.utils.vmware.get_license_assignment_manager(self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'License assignment manager was not retrieved') + self.assertEqual( + excinfo.exception.strerror, "License assignment manager was not retrieved" + ) def test_valid_assignment_manager(self): ret = salt.utils.vmware.get_license_assignment_manager(self.mock_si) self.assertEqual(ret, self.mock_lic_assign_mgr) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetLicensesTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_licenses - ''' + """ def setUp(self): self.mock_si = MagicMock() self.mock_licenses = [MagicMock(), MagicMock()] self.mock_lic_mgr = MagicMock() - type(self.mock_lic_mgr).licenses = \ - PropertyMock(return_value=self.mock_licenses) + type(self.mock_lic_mgr).licenses = PropertyMock(return_value=self.mock_licenses) patches = ( - ('salt.utils.vmware.get_license_manager', - MagicMock(return_value=self.mock_lic_mgr)),) + ( + "salt.utils.vmware.get_license_manager", + MagicMock(return_value=self.mock_lic_mgr), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_lic_mgr', 'mock_licenses'): + for attr in ("mock_si", "mock_lic_mgr", "mock_licenses"): delattr(self, attr) def test_no_license_manager_passed_in(self): mock_get_license_manager = MagicMock() - with patch('salt.utils.vmware.get_license_manager', - mock_get_license_manager): + with patch("salt.utils.vmware.get_license_manager", mock_get_license_manager): salt.utils.vmware.get_licenses(self.mock_si) mock_get_license_manager.assert_called_once_with(self.mock_si) @@ -3424,49 +3833,48 @@ class GetLicensesTestCase(TestCase): mock_lic_mgr = MagicMock() type(mock_lic_mgr).licenses = mock_licenses mock_get_license_manager = MagicMock() - with patch('salt.utils.vmware.get_license_manager', - mock_get_license_manager): - salt.utils.vmware.get_licenses(self.mock_si, - license_manager=mock_lic_mgr) + with patch("salt.utils.vmware.get_license_manager", mock_get_license_manager): + salt.utils.vmware.get_licenses(self.mock_si, license_manager=mock_lic_mgr) self.assertEqual(mock_get_license_manager.call_count, 0) self.assertEqual(mock_licenses.call_count, 1) def test_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" type(self.mock_lic_mgr).licenses = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_licenses(self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_raise_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" type(self.mock_lic_mgr).licenses = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: salt.utils.vmware.get_licenses(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" type(self.mock_lic_mgr).licenses = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: salt.utils.vmware.get_licenses(self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_valid_licenses(self): ret = salt.utils.vmware.get_licenses(self.mock_si) self.assertEqual(ret, self.mock_licenses) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class AddLicenseTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.add_license - ''' + """ def setUp(self): self.mock_si = MagicMock() @@ -3475,295 +3883,341 @@ class AddLicenseTestCase(TestCase): self.mock_lic_mgr = MagicMock(AddLicense=self.mock_add_license) self.mock_label = MagicMock() patches = ( - ('salt.utils.vmware.get_license_manager', - MagicMock(return_value=self.mock_lic_mgr)), - ('salt.utils.vmware.vim.KeyValue', - MagicMock(return_value=self.mock_label))) + ( + "salt.utils.vmware.get_license_manager", + MagicMock(return_value=self.mock_lic_mgr), + ), + ("salt.utils.vmware.vim.KeyValue", MagicMock(return_value=self.mock_label)), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_lic_mgr', 'mock_license', - 'mock_add_license', 'mock_label'): + for attr in ( + "mock_si", + "mock_lic_mgr", + "mock_license", + "mock_add_license", + "mock_label", + ): delattr(self, attr) def test_no_license_manager_passed_in(self): mock_get_license_manager = MagicMock() - with patch('salt.utils.vmware.get_license_manager', - mock_get_license_manager): - salt.utils.vmware.add_license(self.mock_si, - 'fake_license_key', - 'fake_license_description') + with patch("salt.utils.vmware.get_license_manager", mock_get_license_manager): + salt.utils.vmware.add_license( + self.mock_si, "fake_license_key", "fake_license_description" + ) mock_get_license_manager.assert_called_once_with(self.mock_si) def test_license_manager_passed_in(self): mock_get_license_manager = MagicMock() - with patch('salt.utils.vmware.get_license_manager', - mock_get_license_manager): - salt.utils.vmware.add_license(self.mock_si, - 'fake_license_key', - 'fake_license_description', - license_manager=self.mock_lic_mgr) + with patch("salt.utils.vmware.get_license_manager", mock_get_license_manager): + salt.utils.vmware.add_license( + self.mock_si, + "fake_license_key", + "fake_license_description", + license_manager=self.mock_lic_mgr, + ) self.assertEqual(mock_get_license_manager.call_count, 0) self.assertEqual(self.mock_add_license.call_count, 1) def test_label_settings(self): - salt.utils.vmware.add_license(self.mock_si, - 'fake_license_key', - 'fake_license_description') - self.assertEqual(self.mock_label.key, 'VpxClientLicenseLabel') - self.assertEqual(self.mock_label.value, 'fake_license_description') + salt.utils.vmware.add_license( + self.mock_si, "fake_license_key", "fake_license_description" + ) + self.assertEqual(self.mock_label.key, "VpxClientLicenseLabel") + self.assertEqual(self.mock_label.value, "fake_license_description") def test_add_license_arguments(self): - salt.utils.vmware.add_license(self.mock_si, - 'fake_license_key', - 'fake_license_description') - self.mock_add_license.assert_called_once_with('fake_license_key', - [self.mock_label]) + salt.utils.vmware.add_license( + self.mock_si, "fake_license_key", "fake_license_description" + ) + self.mock_add_license.assert_called_once_with( + "fake_license_key", [self.mock_label] + ) def test_add_license_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" self.mock_lic_mgr.AddLicense = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.add_license(self.mock_si, - 'fake_license_key', - 'fake_license_description') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.add_license( + self.mock_si, "fake_license_key", "fake_license_description" + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_add_license_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" self.mock_lic_mgr.AddLicense = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.add_license(self.mock_si, - 'fake_license_key', - 'fake_license_description') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.add_license( + self.mock_si, "fake_license_key", "fake_license_description" + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_add_license_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" self.mock_lic_mgr.AddLicense = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.add_license(self.mock_si, - 'fake_license_key', - 'fake_license_description') - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.add_license( + self.mock_si, "fake_license_key", "fake_license_description" + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_valid_license_added(self): - ret = salt.utils.vmware.add_license(self.mock_si, - 'fake_license_key', - 'fake_license_description') + ret = salt.utils.vmware.add_license( + self.mock_si, "fake_license_key", "fake_license_description" + ) self.assertEqual(ret, self.mock_license) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetAssignedLicensesTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_assigned_licenses - ''' + """ def setUp(self): self.mock_ent_id = MagicMock() self.mock_si = MagicMock() - type(self.mock_si.content.about).instanceUuid = \ - PropertyMock(return_value=self.mock_ent_id) + type(self.mock_si.content.about).instanceUuid = PropertyMock( + return_value=self.mock_ent_id + ) self.mock_moid = MagicMock() self.prop_mock_moid = PropertyMock(return_value=self.mock_moid) self.mock_entity_ref = MagicMock() type(self.mock_entity_ref)._moId = self.prop_mock_moid - self.mock_assignments = [MagicMock(entityDisplayName='fake_ent1'), - MagicMock(entityDisplayName='fake_ent2')] + self.mock_assignments = [ + MagicMock(entityDisplayName="fake_ent1"), + MagicMock(entityDisplayName="fake_ent2"), + ] self.mock_query_assigned_licenses = MagicMock( - return_value=[MagicMock(assignedLicense=self.mock_assignments[0]), - MagicMock(assignedLicense=self.mock_assignments[1])]) + return_value=[ + MagicMock(assignedLicense=self.mock_assignments[0]), + MagicMock(assignedLicense=self.mock_assignments[1]), + ] + ) self.mock_lic_assign_mgr = MagicMock( - QueryAssignedLicenses=self.mock_query_assigned_licenses) + QueryAssignedLicenses=self.mock_query_assigned_licenses + ) patches = ( - ('salt.utils.vmware.get_license_assignment_manager', - MagicMock(return_value=self.mock_lic_assign_mgr)),) + ( + "salt.utils.vmware.get_license_assignment_manager", + MagicMock(return_value=self.mock_lic_assign_mgr), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_ent_id', 'mock_si', 'mock_moid', 'prop_mock_moid', - 'mock_entity_ref', 'mock_assignments', - 'mock_query_assigned_licenses', 'mock_lic_assign_mgr'): + for attr in ( + "mock_ent_id", + "mock_si", + "mock_moid", + "prop_mock_moid", + "mock_entity_ref", + "mock_assignments", + "mock_query_assigned_licenses", + "mock_lic_assign_mgr", + ): delattr(self, attr) def test_no_license_assignment_manager_passed_in(self): mock_get_license_assign_manager = MagicMock() - with patch('salt.utils.vmware.get_license_assignment_manager', - mock_get_license_assign_manager): - salt.utils.vmware.get_assigned_licenses(self.mock_si, - self.mock_entity_ref, - 'fake_entity_name') + with patch( + "salt.utils.vmware.get_license_assignment_manager", + mock_get_license_assign_manager, + ): + salt.utils.vmware.get_assigned_licenses( + self.mock_si, self.mock_entity_ref, "fake_entity_name" + ) mock_get_license_assign_manager.assert_called_once_with(self.mock_si) def test_license_assignment_manager_passed_in(self): mock_get_license_assign_manager = MagicMock() - with patch('salt.utils.vmware.get_license_assignment_manager', - mock_get_license_assign_manager): + with patch( + "salt.utils.vmware.get_license_assignment_manager", + mock_get_license_assign_manager, + ): salt.utils.vmware.get_assigned_licenses( - self.mock_si, self.mock_entity_ref, 'fake_entity_name', - license_assignment_manager=self.mock_lic_assign_mgr) + self.mock_si, + self.mock_entity_ref, + "fake_entity_name", + license_assignment_manager=self.mock_lic_assign_mgr, + ) self.assertEqual(mock_get_license_assign_manager.call_count, 0) def test_entity_name(self): mock_trace = MagicMock() - with patch('salt._logging.impl.SaltLoggingClass.trace', mock_trace): - salt.utils.vmware.get_assigned_licenses(self.mock_si, - self.mock_entity_ref, - 'fake_entity_name') + with patch("salt._logging.impl.SaltLoggingClass.trace", mock_trace): + salt.utils.vmware.get_assigned_licenses( + self.mock_si, self.mock_entity_ref, "fake_entity_name" + ) mock_trace.assert_called_once_with( - "Retrieving licenses assigned to '%s'", 'fake_entity_name') + "Retrieving licenses assigned to '%s'", "fake_entity_name" + ) def test_instance_uuid(self): mock_instance_uuid_prop = PropertyMock() type(self.mock_si.content.about).instanceUuid = mock_instance_uuid_prop self.mock_lic_assign_mgr.QueryAssignedLicenses = MagicMock( - return_value=[MagicMock(entityDisplayName='fake_vcenter')]) - salt.utils.vmware.get_assigned_licenses(self.mock_si, - entity_name='fake_vcenter') + return_value=[MagicMock(entityDisplayName="fake_vcenter")] + ) + salt.utils.vmware.get_assigned_licenses( + self.mock_si, entity_name="fake_vcenter" + ) self.assertEqual(mock_instance_uuid_prop.call_count, 1) def test_instance_uuid_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - type(self.mock_si.content.about).instanceUuid = \ - PropertyMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + type(self.mock_si.content.about).instanceUuid = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.get_assigned_licenses(self.mock_si, - entity_name='fake_vcenter') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.get_assigned_licenses( + self.mock_si, entity_name="fake_vcenter" + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_instance_uuid_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - type(self.mock_si.content.about).instanceUuid = \ - PropertyMock(side_effect=exc) + exc.msg = "VimFault msg" + type(self.mock_si.content.about).instanceUuid = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.get_assigned_licenses(self.mock_si, - entity_name='fake_vcenter') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.get_assigned_licenses( + self.mock_si, entity_name="fake_vcenter" + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_instance_uuid_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - type(self.mock_si.content.about).instanceUuid = \ - PropertyMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + type(self.mock_si.content.about).instanceUuid = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.get_assigned_licenses(self.mock_si, - entity_name='fake_vcenter') - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.get_assigned_licenses( + self.mock_si, entity_name="fake_vcenter" + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_vcenter_entity_too_many_assignements(self): self.mock_lic_assign_mgr.QueryAssignedLicenses = MagicMock( - return_value=[MagicMock(), MagicMock()]) + return_value=[MagicMock(), MagicMock()] + ) with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - salt.utils.vmware.get_assigned_licenses(self.mock_si, - entity_name='fake_vcenter') - self.assertEqual(excinfo.exception.strerror, - 'Unexpected return. Expect only a single assignment') + salt.utils.vmware.get_assigned_licenses( + self.mock_si, entity_name="fake_vcenter" + ) + self.assertEqual( + excinfo.exception.strerror, + "Unexpected return. Expect only a single assignment", + ) def test_wrong_vcenter_name(self): self.mock_lic_assign_mgr.QueryAssignedLicenses = MagicMock( - return_value=[MagicMock(entityDisplayName='bad_vcenter')]) + return_value=[MagicMock(entityDisplayName="bad_vcenter")] + ) with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - salt.utils.vmware.get_assigned_licenses(self.mock_si, - entity_name='fake_vcenter') - self.assertEqual(excinfo.exception.strerror, - 'Got license assignment info for a different vcenter') + salt.utils.vmware.get_assigned_licenses( + self.mock_si, entity_name="fake_vcenter" + ) + self.assertEqual( + excinfo.exception.strerror, + "Got license assignment info for a different vcenter", + ) def test_query_assigned_licenses_vcenter(self): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - salt.utils.vmware.get_assigned_licenses(self.mock_si, - entity_name='fake_vcenter') - self.mock_query_assigned_licenses.assert_called_once_with( - self.mock_ent_id) + salt.utils.vmware.get_assigned_licenses( + self.mock_si, entity_name="fake_vcenter" + ) + self.mock_query_assigned_licenses.assert_called_once_with(self.mock_ent_id) def test_query_assigned_licenses_with_entity(self): - salt.utils.vmware.get_assigned_licenses(self.mock_si, - self.mock_entity_ref, - 'fake_entity_name') - self.mock_query_assigned_licenses.assert_called_once_with( - self.mock_moid) + salt.utils.vmware.get_assigned_licenses( + self.mock_si, self.mock_entity_ref, "fake_entity_name" + ) + self.mock_query_assigned_licenses.assert_called_once_with(self.mock_moid) def test_query_assigned_licenses_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_lic_assign_mgr.QueryAssignedLicenses = \ - MagicMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + self.mock_lic_assign_mgr.QueryAssignedLicenses = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.get_assigned_licenses(self.mock_si, - self.mock_entity_ref, - 'fake_entity_name') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.get_assigned_licenses( + self.mock_si, self.mock_entity_ref, "fake_entity_name" + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_query_assigned_licenses_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.mock_lic_assign_mgr.QueryAssignedLicenses = \ - MagicMock(side_effect=exc) + exc.msg = "VimFault msg" + self.mock_lic_assign_mgr.QueryAssignedLicenses = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.get_assigned_licenses(self.mock_si, - self.mock_entity_ref, - 'fake_entity_name') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.get_assigned_licenses( + self.mock_si, self.mock_entity_ref, "fake_entity_name" + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_query_assigned_licenses_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_lic_assign_mgr.QueryAssignedLicenses = \ - MagicMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + self.mock_lic_assign_mgr.QueryAssignedLicenses = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.get_assigned_licenses(self.mock_si, - self.mock_entity_ref, - 'fake_entity_name') - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.get_assigned_licenses( + self.mock_si, self.mock_entity_ref, "fake_entity_name" + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_valid_assignments(self): - ret = salt.utils.vmware.get_assigned_licenses(self.mock_si, - self.mock_entity_ref, - 'fake_entity_name') + ret = salt.utils.vmware.get_assigned_licenses( + self.mock_si, self.mock_entity_ref, "fake_entity_name" + ) self.assertEqual(ret, self.mock_assignments) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class AssignLicenseTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.assign_license - ''' + """ def setUp(self): self.mock_ent_id = MagicMock() self.mock_si = MagicMock() - type(self.mock_si.content.about).instanceUuid = \ - PropertyMock(return_value=self.mock_ent_id) + type(self.mock_si.content.about).instanceUuid = PropertyMock( + return_value=self.mock_ent_id + ) self.mock_lic_key = MagicMock() self.mock_moid = MagicMock() self.prop_mock_moid = PropertyMock(return_value=self.mock_moid) self.mock_entity_ref = MagicMock() type(self.mock_entity_ref)._moId = self.prop_mock_moid self.mock_license = MagicMock() - self.mock_update_assigned_license = MagicMock( - return_value=self.mock_license) + self.mock_update_assigned_license = MagicMock(return_value=self.mock_license) self.mock_lic_assign_mgr = MagicMock( - UpdateAssignedLicense=self.mock_update_assigned_license) + UpdateAssignedLicense=self.mock_update_assigned_license + ) patches = ( - ('salt.utils.vmware.get_license_assignment_manager', - MagicMock(return_value=self.mock_lic_assign_mgr)),) + ( + "salt.utils.vmware.get_license_assignment_manager", + MagicMock(return_value=self.mock_lic_assign_mgr), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() @@ -3771,399 +4225,469 @@ class AssignLicenseTestCase(TestCase): def test_no_license_assignment_manager_passed_in(self): mock_get_license_assign_manager = MagicMock() - with patch('salt.utils.vmware.get_license_assignment_manager', - mock_get_license_assign_manager): - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - self.mock_entity_ref, - 'fake_entity_name') + with patch( + "salt.utils.vmware.get_license_assignment_manager", + mock_get_license_assign_manager, + ): + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + self.mock_entity_ref, + "fake_entity_name", + ) mock_get_license_assign_manager.assert_called_once_with(self.mock_si) def test_license_assignment_manager_passed_in(self): mock_get_license_assign_manager = MagicMock() - with patch('salt.utils.vmware.get_license_assignment_manager', - mock_get_license_assign_manager): + with patch( + "salt.utils.vmware.get_license_assignment_manager", + mock_get_license_assign_manager, + ): salt.utils.vmware.assign_license( - self.mock_si, self.mock_lic_key, 'fake_license_name', - self.mock_entity_ref, 'fake_entity_name', - license_assignment_manager=self.mock_lic_assign_mgr) + self.mock_si, + self.mock_lic_key, + "fake_license_name", + self.mock_entity_ref, + "fake_entity_name", + license_assignment_manager=self.mock_lic_assign_mgr, + ) self.assertEqual(mock_get_license_assign_manager.call_count, 0) self.assertEqual(self.mock_update_assigned_license.call_count, 1) def test_entity_name(self): mock_trace = MagicMock() - with patch('salt._logging.impl.SaltLoggingClass.trace', mock_trace): - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - self.mock_entity_ref, - 'fake_entity_name') + with patch("salt._logging.impl.SaltLoggingClass.trace", mock_trace): + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + self.mock_entity_ref, + "fake_entity_name", + ) mock_trace.assert_called_once_with( - "Assigning license to '%s'", 'fake_entity_name') + "Assigning license to '%s'", "fake_entity_name" + ) def test_instance_uuid(self): mock_instance_uuid_prop = PropertyMock() type(self.mock_si.content.about).instanceUuid = mock_instance_uuid_prop self.mock_lic_assign_mgr.UpdateAssignedLicense = MagicMock( - return_value=[MagicMock(entityDisplayName='fake_vcenter')]) - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - entity_name='fake_entity_name') + return_value=[MagicMock(entityDisplayName="fake_vcenter")] + ) + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + entity_name="fake_entity_name", + ) self.assertEqual(mock_instance_uuid_prop.call_count, 1) def test_instance_uuid_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - type(self.mock_si.content.about).instanceUuid = \ - PropertyMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + type(self.mock_si.content.about).instanceUuid = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - entity_name='fake_entity_name') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + entity_name="fake_entity_name", + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_instance_uuid_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - type(self.mock_si.content.about).instanceUuid = \ - PropertyMock(side_effect=exc) + exc.msg = "VimFault msg" + type(self.mock_si.content.about).instanceUuid = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - entity_name='fake_entity_name') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + entity_name="fake_entity_name", + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_instance_uuid_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - type(self.mock_si.content.about).instanceUuid = \ - PropertyMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + type(self.mock_si.content.about).instanceUuid = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - entity_name='fake_entity_name') - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + entity_name="fake_entity_name", + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_update_assigned_licenses_vcenter(self): - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - entity_name='fake_entity_name') + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + entity_name="fake_entity_name", + ) self.mock_update_assigned_license.assert_called_once_with( - self.mock_ent_id, self.mock_lic_key, 'fake_license_name') + self.mock_ent_id, self.mock_lic_key, "fake_license_name" + ) def test_update_assigned_licenses_call_with_entity(self): - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - self.mock_entity_ref, - 'fake_entity_name') + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + self.mock_entity_ref, + "fake_entity_name", + ) self.mock_update_assigned_license.assert_called_once_with( - self.mock_moid, self.mock_lic_key, 'fake_license_name') + self.mock_moid, self.mock_lic_key, "fake_license_name" + ) def test_update_assigned_licenses_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - self.mock_lic_assign_mgr.UpdateAssignedLicense = \ - MagicMock(side_effect=exc) + exc.privilegeId = "Fake privilege" + self.mock_lic_assign_mgr.UpdateAssignedLicense = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - self.mock_entity_ref, - 'fake_entity_name') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + self.mock_entity_ref, + "fake_entity_name", + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_update_assigned_licenses_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - self.mock_lic_assign_mgr.UpdateAssignedLicense = \ - MagicMock(side_effect=exc) + exc.msg = "VimFault msg" + self.mock_lic_assign_mgr.UpdateAssignedLicense = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - self.mock_entity_ref, - 'fake_entity_name') - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + self.mock_entity_ref, + "fake_entity_name", + ) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_update_assigned_licenses_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - self.mock_lic_assign_mgr.UpdateAssignedLicense = \ - MagicMock(side_effect=exc) + exc.msg = "RuntimeFault msg" + self.mock_lic_assign_mgr.UpdateAssignedLicense = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - self.mock_entity_ref, - 'fake_entity_name') - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + self.mock_entity_ref, + "fake_entity_name", + ) + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") def test_valid_assignments(self): - ret = salt.utils.vmware.assign_license(self.mock_si, - self.mock_lic_key, - 'fake_license_name', - self.mock_entity_ref, - 'fake_entity_name') + ret = salt.utils.vmware.assign_license( + self.mock_si, + self.mock_lic_key, + "fake_license_name", + self.mock_entity_ref, + "fake_entity_name", + ) self.assertEqual(ret, self.mock_license) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetStorageSystemTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_storage_system - ''' + """ + def setUp(self): self.mock_si = MagicMock(content=MagicMock()) self.mock_host_ref = MagicMock() - self.mock_get_managed_object_name = MagicMock(return_value='fake_host') + self.mock_get_managed_object_name = MagicMock(return_value="fake_host") self.mock_traversal_spec = MagicMock() self.mock_obj = MagicMock() - self.mock_get_mors = \ - MagicMock(return_value=[{'object': self.mock_obj}]) + self.mock_get_mors = MagicMock(return_value=[{"object": self.mock_obj}]) patches = ( - ('salt.utils.vmware.get_managed_object_name', - self.mock_get_managed_object_name), - ('salt.utils.vmware.get_mors_with_properties', - self.mock_get_mors), - ('salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - MagicMock(return_value=self.mock_traversal_spec))) + ( + "salt.utils.vmware.get_managed_object_name", + self.mock_get_managed_object_name, + ), + ("salt.utils.vmware.get_mors_with_properties", self.mock_get_mors), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + MagicMock(return_value=self.mock_traversal_spec), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_host_ref', - 'mock_get_managed_object_name', - 'mock_traversal_spec', 'mock_obj'): + for attr in ( + "mock_si", + "mock_host_ref", + "mock_get_managed_object_name", + "mock_traversal_spec", + "mock_obj", + ): delattr(self, attr) def test_no_hostname_argument(self): - salt.utils.vmware.get_storage_system(self.mock_si, - self.mock_host_ref) - self.mock_get_managed_object_name.assert_called_once_with( - self.mock_host_ref) + salt.utils.vmware.get_storage_system(self.mock_si, self.mock_host_ref) + self.mock_get_managed_object_name.assert_called_once_with(self.mock_host_ref) def test_hostname_argument(self): - salt.utils.vmware.get_storage_system(self.mock_si, - self.mock_host_ref, - hostname='fake_host') + salt.utils.vmware.get_storage_system( + self.mock_si, self.mock_host_ref, hostname="fake_host" + ) self.assertEqual(self.mock_get_managed_object_name.call_count, 0) def test_traversal_spec(self): - mock_traversal_spec = MagicMock(return_value=[{'object': - self.mock_obj}]) + mock_traversal_spec = MagicMock(return_value=[{"object": self.mock_obj}]) with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec, + ): - salt.utils.vmware.get_storage_system(self.mock_si, - self.mock_host_ref) + salt.utils.vmware.get_storage_system(self.mock_si, self.mock_host_ref) mock_traversal_spec.assert_called_once_with( - path='configManager.storageSystem', - type=vim.HostSystem, - skip=False) + path="configManager.storageSystem", type=vim.HostSystem, skip=False + ) def test_get_mors_with_properties(self): - salt.utils.vmware.get_storage_system(self.mock_si, - self.mock_host_ref) + salt.utils.vmware.get_storage_system(self.mock_si, self.mock_host_ref) self.mock_get_mors.assert_called_once_with( self.mock_si, vim.HostStorageSystem, - property_list=['systemFile'], + property_list=["systemFile"], container_ref=self.mock_host_ref, - traversal_spec=self.mock_traversal_spec) + traversal_spec=self.mock_traversal_spec, + ) def test_empty_mors_result(self): - with patch('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=[])): + with patch( + "salt.utils.vmware.get_mors_with_properties", MagicMock(return_value=[]) + ): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: - salt.utils.vmware.get_storage_system(self.mock_si, - self.mock_host_ref) - self.assertEqual(excinfo.exception.strerror, - 'Host\'s \'fake_host\' storage system was ' - 'not retrieved') + salt.utils.vmware.get_storage_system(self.mock_si, self.mock_host_ref) + self.assertEqual( + excinfo.exception.strerror, + "Host's 'fake_host' storage system was " "not retrieved", + ) def test_valid_mors_result(self): - res = salt.utils.vmware.get_storage_system(self.mock_si, - self.mock_host_ref) + res = salt.utils.vmware.get_storage_system(self.mock_si, self.mock_host_ref) self.assertEqual(res, self.mock_obj) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class GetDatastoresTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.get_datastores - ''' + """ def setUp(self): self.mock_si = MagicMock() self.mock_reference = MagicMock(spec=vim.HostSystem) self.mock_mount_infos = [ - MagicMock(volume=MagicMock(spec=vim.HostVmfsVolume, - extent=[MagicMock( - diskName='fake_disk2')])), - MagicMock(volume=MagicMock(spec=vim.HostVmfsVolume, - extent=[MagicMock( - diskName='fake_disk3')]))] - self.mock_mount_infos[0].volume.name = 'fake_ds2' - self.mock_mount_infos[1].volume.name = 'fake_ds3' - self.mock_entries = [{'name': 'fake_ds1', 'object': MagicMock()}, - {'name': 'fake_ds2', 'object': MagicMock()}, - {'name': 'fake_ds3', 'object': MagicMock()}] + MagicMock( + volume=MagicMock( + spec=vim.HostVmfsVolume, extent=[MagicMock(diskName="fake_disk2")] + ) + ), + MagicMock( + volume=MagicMock( + spec=vim.HostVmfsVolume, extent=[MagicMock(diskName="fake_disk3")] + ) + ), + ] + self.mock_mount_infos[0].volume.name = "fake_ds2" + self.mock_mount_infos[1].volume.name = "fake_ds3" + self.mock_entries = [ + {"name": "fake_ds1", "object": MagicMock()}, + {"name": "fake_ds2", "object": MagicMock()}, + {"name": "fake_ds3", "object": MagicMock()}, + ] self.mock_storage_system = MagicMock() - self.mock_get_storage_system = MagicMock( - return_value=self.mock_storage_system) - self.mock_get_managed_object_name = MagicMock(return_value='fake_host') + self.mock_get_storage_system = MagicMock(return_value=self.mock_storage_system) + self.mock_get_managed_object_name = MagicMock(return_value="fake_host") self.mock_traversal_spec = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - self.mock_get_managed_object_name), - ('salt.utils.vmware.get_storage_system', - self.mock_get_storage_system), - ('salt.utils.vmware.get_properties_of_managed_object', - MagicMock(return_value={'fileSystemVolumeInfo.mountInfo': - self.mock_mount_infos})), - ('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_entries)), - ('salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - MagicMock(return_value=self.mock_traversal_spec))) + ( + "salt.utils.vmware.get_managed_object_name", + self.mock_get_managed_object_name, + ), + ("salt.utils.vmware.get_storage_system", self.mock_get_storage_system), + ( + "salt.utils.vmware.get_properties_of_managed_object", + MagicMock( + return_value={ + "fileSystemVolumeInfo.mountInfo": self.mock_mount_infos + } + ), + ), + ( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_entries), + ), + ( + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + MagicMock(return_value=self.mock_traversal_spec), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_reference', 'mock_storage_system', - 'mock_get_storage_system', 'mock_mount_infos', - 'mock_entries', 'mock_get_managed_object_name', - 'mock_traversal_spec'): + for attr in ( + "mock_si", + "mock_reference", + "mock_storage_system", + "mock_get_storage_system", + "mock_mount_infos", + "mock_entries", + "mock_get_managed_object_name", + "mock_traversal_spec", + ): delattr(self, attr) def test_get_reference_name_call(self): - salt.utils.vmware.get_datastores(self.mock_si, - self.mock_reference) - self.mock_get_managed_object_name.assert_called_once_with( - self.mock_reference) + salt.utils.vmware.get_datastores(self.mock_si, self.mock_reference) + self.mock_get_managed_object_name.assert_called_once_with(self.mock_reference) def test_get_no_datastores(self): - res = salt.utils.vmware.get_datastores(self.mock_si, - self.mock_reference) + res = salt.utils.vmware.get_datastores(self.mock_si, self.mock_reference) self.assertEqual(res, []) def test_get_storage_system_call(self): - salt.utils.vmware.get_datastores(self.mock_si, - self.mock_reference, - backing_disk_ids=['fake_disk1']) + salt.utils.vmware.get_datastores( + self.mock_si, self.mock_reference, backing_disk_ids=["fake_disk1"] + ) self.mock_get_storage_system.assert_called_once_with( - self.mock_si, self.mock_reference, 'fake_host') + self.mock_si, self.mock_reference, "fake_host" + ) def test_get_mount_info_call(self): mock_get_properties_of_managed_object = MagicMock() - with patch('salt.utils.vmware.get_properties_of_managed_object', - mock_get_properties_of_managed_object): - salt.utils.vmware.get_datastores(self.mock_si, - self.mock_reference, - backing_disk_ids=['fake_disk1']) + with patch( + "salt.utils.vmware.get_properties_of_managed_object", + mock_get_properties_of_managed_object, + ): + salt.utils.vmware.get_datastores( + self.mock_si, self.mock_reference, backing_disk_ids=["fake_disk1"] + ) mock_get_properties_of_managed_object.assert_called_once_with( - self.mock_storage_system, ['fileSystemVolumeInfo.mountInfo']) + self.mock_storage_system, ["fileSystemVolumeInfo.mountInfo"] + ) def test_backing_disks_no_mount_info(self): - with patch('salt.utils.vmware.get_properties_of_managed_object', - MagicMock(return_value={})): + with patch( + "salt.utils.vmware.get_properties_of_managed_object", + MagicMock(return_value={}), + ): res = salt.utils.vmware.get_datastores( - self.mock_si, self.mock_reference, - backing_disk_ids=['fake_disk_id']) + self.mock_si, self.mock_reference, backing_disk_ids=["fake_disk_id"] + ) self.assertEqual(res, []) def test_host_traversal_spec(self): # Reference is of type vim.HostSystem mock_traversal_spec_init = MagicMock() with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec_init): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec_init, + ): salt.utils.vmware.get_datastores( - self.mock_si, - self.mock_reference, - get_all_datastores=True) + self.mock_si, self.mock_reference, get_all_datastores=True + ) mock_traversal_spec_init.assert_called_once_with( - name='host_datastore_traversal', - path='datastore', + name="host_datastore_traversal", + path="datastore", skip=False, - type=vim.HostSystem) + type=vim.HostSystem, + ) def test_cluster_traversal_spec(self): mock_traversal_spec_init = MagicMock() # Reference is of type vim.ClusterComputeResource mock_reference = MagicMock(spec=vim.ClusterComputeResource) with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec_init): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec_init, + ): salt.utils.vmware.get_datastores( - self.mock_si, - mock_reference, - get_all_datastores=True) + self.mock_si, mock_reference, get_all_datastores=True + ) mock_traversal_spec_init.assert_called_once_with( - name='cluster_datastore_traversal', - path='datastore', + name="cluster_datastore_traversal", + path="datastore", skip=False, - type=vim.ClusterComputeResource) + type=vim.ClusterComputeResource, + ) def test_datacenter_traversal_spec(self): mock_traversal_spec_init = MagicMock() # Reference is of type vim.ClusterComputeResource mock_reference = MagicMock(spec=vim.Datacenter) with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec_init): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec_init, + ): salt.utils.vmware.get_datastores( - self.mock_si, - mock_reference, - get_all_datastores=True) + self.mock_si, mock_reference, get_all_datastores=True + ) mock_traversal_spec_init.assert_called_once_with( - name='datacenter_datastore_traversal', - path='datastore', + name="datacenter_datastore_traversal", + path="datastore", skip=False, - type=vim.Datacenter) + type=vim.Datacenter, + ) def test_root_folder_traversal_spec(self): - mock_traversal_spec_init = MagicMock(return_value='traversal') + mock_traversal_spec_init = MagicMock(return_value="traversal") mock_reference = MagicMock(spec=vim.Folder) - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(side_effect=['fake_host', 'Datacenters'])): + with patch( + "salt.utils.vmware.get_managed_object_name", + MagicMock(side_effect=["fake_host", "Datacenters"]), + ): with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec_init): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec_init, + ): salt.utils.vmware.get_datastores( - self.mock_si, - mock_reference, - get_all_datastores=True) + self.mock_si, mock_reference, get_all_datastores=True + ) - mock_traversal_spec_init.assert_has_calls([ - call(path='datastore', - skip=False, - type=vim.Datacenter), - call(path='childEntity', - selectSet=['traversal'], - skip=False, - type=vim.Folder)]) + mock_traversal_spec_init.assert_has_calls( + [ + call(path="datastore", skip=False, type=vim.Datacenter), + call( + path="childEntity", + selectSet=["traversal"], + skip=False, + type=vim.Folder, + ), + ] + ) def test_unsupported_reference_type(self): class FakeClass(object): @@ -4172,154 +4696,174 @@ class GetDatastoresTestCase(TestCase): mock_reference = MagicMock(spec=FakeClass) with self.assertRaises(ArgumentValueError) as excinfo: salt.utils.vmware.get_datastores( - self.mock_si, - mock_reference, - get_all_datastores=True) - self.assertEqual(excinfo.exception.strerror, - 'Unsupported reference type \'FakeClass\'') + self.mock_si, mock_reference, get_all_datastores=True + ) + self.assertEqual( + excinfo.exception.strerror, "Unsupported reference type 'FakeClass'" + ) def test_get_mors_with_properties(self): mock_get_mors_with_properties = MagicMock() - with patch('salt.utils.vmware.get_mors_with_properties', - mock_get_mors_with_properties): + with patch( + "salt.utils.vmware.get_mors_with_properties", mock_get_mors_with_properties + ): salt.utils.vmware.get_datastores( - self.mock_si, - self.mock_reference, - get_all_datastores=True) + self.mock_si, self.mock_reference, get_all_datastores=True + ) mock_get_mors_with_properties.assert_called_once_with( self.mock_si, object_type=vim.Datastore, - property_list=['name'], + property_list=["name"], container_ref=self.mock_reference, - traversal_spec=self.mock_traversal_spec) + traversal_spec=self.mock_traversal_spec, + ) def test_get_all_datastores(self): - res = salt.utils.vmware.get_datastores(self.mock_si, - self.mock_reference, - get_all_datastores=True) - self.assertEqual(res, [self.mock_entries[0]['object'], - self.mock_entries[1]['object'], - self.mock_entries[2]['object']]) + res = salt.utils.vmware.get_datastores( + self.mock_si, self.mock_reference, get_all_datastores=True + ) + self.assertEqual( + res, + [ + self.mock_entries[0]["object"], + self.mock_entries[1]["object"], + self.mock_entries[2]["object"], + ], + ) def test_get_datastores_filtered_by_name(self): - res = salt.utils.vmware.get_datastores(self.mock_si, - self.mock_reference, - datastore_names=['fake_ds1', - 'fake_ds2']) - self.assertEqual(res, [self.mock_entries[0]['object'], - self.mock_entries[1]['object']]) + res = salt.utils.vmware.get_datastores( + self.mock_si, self.mock_reference, datastore_names=["fake_ds1", "fake_ds2"] + ) + self.assertEqual( + res, [self.mock_entries[0]["object"], self.mock_entries[1]["object"]] + ) def test_get_datastores_filtered_by_backing_disk(self): res = salt.utils.vmware.get_datastores( - self.mock_si, self.mock_reference, - backing_disk_ids=['fake_disk2', 'fake_disk3']) - self.assertEqual(res, [self.mock_entries[1]['object'], - self.mock_entries[2]['object']]) + self.mock_si, + self.mock_reference, + backing_disk_ids=["fake_disk2", "fake_disk3"], + ) + self.assertEqual( + res, [self.mock_entries[1]["object"], self.mock_entries[2]["object"]] + ) def test_get_datastores_filtered_by_both_name_and_backing_disk(self): # Simulate VMware data model for volumes fake_ds2, fake_ds3 res = salt.utils.vmware.get_datastores( - self.mock_si, self.mock_reference, - datastore_names=['fake_ds1'], - backing_disk_ids=['fake_disk3']) - self.assertEqual(res, [self.mock_entries[0]['object'], - self.mock_entries[2]['object']]) + self.mock_si, + self.mock_reference, + datastore_names=["fake_ds1"], + backing_disk_ids=["fake_disk3"], + ) + self.assertEqual( + res, [self.mock_entries[0]["object"], self.mock_entries[2]["object"]] + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") class RenameDatastoreTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.rename_datastore - ''' + """ def setUp(self): self.mock_ds_ref = MagicMock() - self.mock_get_managed_object_name = MagicMock(return_value='fake_ds') + self.mock_get_managed_object_name = MagicMock(return_value="fake_ds") patches = ( - ('salt.utils.vmware.get_managed_object_name', - self.mock_get_managed_object_name),) + ( + "salt.utils.vmware.get_managed_object_name", + self.mock_get_managed_object_name, + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_ds_ref', 'mock_get_managed_object_name'): + for attr in ("mock_ds_ref", "mock_get_managed_object_name"): delattr(self, attr) def test_datastore_name_call(self): - salt.utils.vmware.rename_datastore(self.mock_ds_ref, - 'fake_new_name') - self.mock_get_managed_object_name.assert_called_once_with( - self.mock_ds_ref) + salt.utils.vmware.rename_datastore(self.mock_ds_ref, "fake_new_name") + self.mock_get_managed_object_name.assert_called_once_with(self.mock_ds_ref) def test_rename_datastore_raise_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" type(self.mock_ds_ref).RenameDatastore = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.rename_datastore(self.mock_ds_ref, - 'fake_new_name') - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + salt.utils.vmware.rename_datastore(self.mock_ds_ref, "fake_new_name") + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_rename_datastore_raise_vim_fault(self): exc = vim.VimFault() - exc.msg = 'vim_fault' + exc.msg = "vim_fault" type(self.mock_ds_ref).RenameDatastore = MagicMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: - salt.utils.vmware.rename_datastore(self.mock_ds_ref, - 'fake_new_name') - self.assertEqual(excinfo.exception.strerror, 'vim_fault') + salt.utils.vmware.rename_datastore(self.mock_ds_ref, "fake_new_name") + self.assertEqual(excinfo.exception.strerror, "vim_fault") def test_rename_datastore_raise_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'runtime_fault' + exc.msg = "runtime_fault" type(self.mock_ds_ref).RenameDatastore = MagicMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: - salt.utils.vmware.rename_datastore(self.mock_ds_ref, - 'fake_new_name') - self.assertEqual(excinfo.exception.strerror, 'runtime_fault') + salt.utils.vmware.rename_datastore(self.mock_ds_ref, "fake_new_name") + self.assertEqual(excinfo.exception.strerror, "runtime_fault") def test_rename_datastore(self): - salt.utils.vmware.rename_datastore(self.mock_ds_ref, 'fake_new_name') - self.mock_ds_ref.RenameDatastore.assert_called_once_with( - 'fake_new_name') + salt.utils.vmware.rename_datastore(self.mock_ds_ref, "fake_new_name") + self.mock_ds_ref.RenameDatastore.assert_called_once_with("fake_new_name") class ConvertToKbTestCase(TestCase): - ''' + """ Tests for converting units - ''' + """ def setUp(self): pass def test_gb_conversion_call(self): - self.assertEqual(salt.utils.vmware.convert_to_kb('Gb', 10), {'size': int(10485760), 'unit': 'KB'}) + self.assertEqual( + salt.utils.vmware.convert_to_kb("Gb", 10), + {"size": int(10485760), "unit": "KB"}, + ) def test_mb_conversion_call(self): - self.assertEqual(salt.utils.vmware.convert_to_kb('Mb', 10), {'size': int(10240), 'unit': 'KB'}) + self.assertEqual( + salt.utils.vmware.convert_to_kb("Mb", 10), + {"size": int(10240), "unit": "KB"}, + ) def test_kb_conversion_call(self): - self.assertEqual(salt.utils.vmware.convert_to_kb('Kb', 10), {'size': int(10), 'unit': 'KB'}) + self.assertEqual( + salt.utils.vmware.convert_to_kb("Kb", 10), {"size": int(10), "unit": "KB"} + ) def test_conversion_bad_input_argument_fault(self): - self.assertRaises(ArgumentValueError, salt.utils.vmware.convert_to_kb, 'test', 10) + self.assertRaises( + ArgumentValueError, salt.utils.vmware.convert_to_kb, "test", 10 + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@patch('salt.utils.vmware.get_managed_object_name', MagicMock()) -@patch('salt.utils.vmware.wait_for_task', MagicMock()) +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@patch("salt.utils.vmware.get_managed_object_name", MagicMock()) +@patch("salt.utils.vmware.wait_for_task", MagicMock()) class CreateVirtualMachineTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.create_vm - ''' + """ def setUp(self): - self.vm_name = 'fake_vm' + self.vm_name = "fake_vm" self.mock_task = MagicMock() self.mock_config_spec = MagicMock() self.mock_resourcepool_object = MagicMock() @@ -4328,69 +4872,89 @@ class CreateVirtualMachineTestCase(TestCase): self.mock_folder_object = MagicMock(CreateVM_Task=self.mock_vm_create_task) def test_create_vm_pool_task_call(self): - salt.utils.vmware.create_vm(self.vm_name, self.mock_config_spec, - self.mock_folder_object, - self.mock_resourcepool_object) + salt.utils.vmware.create_vm( + self.vm_name, + self.mock_config_spec, + self.mock_folder_object, + self.mock_resourcepool_object, + ) self.assert_called_once(self.mock_vm_create_task) def test_create_vm_host_task_call(self): - salt.utils.vmware.create_vm(self.vm_name, self.mock_config_spec, - self.mock_folder_object, - self.mock_resourcepool_object, - host_object=self.mock_host_object) + salt.utils.vmware.create_vm( + self.vm_name, + self.mock_config_spec, + self.mock_folder_object, + self.mock_resourcepool_object, + host_object=self.mock_host_object, + ) self.assert_called_once(self.mock_vm_create_task) def test_create_vm_raise_no_permission(self): exception = vim.fault.NoPermission() - exception.msg = 'vim.fault.NoPermission msg' + exception.msg = "vim.fault.NoPermission msg" self.mock_folder_object.CreateVM_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareApiError) as exc: - salt.utils.vmware.create_vm(self.vm_name, self.mock_config_spec, - self.mock_folder_object, - self.mock_resourcepool_object) - self.assertEqual(exc.exception.strerror, - 'Not enough permissions. Required privilege: ') + salt.utils.vmware.create_vm( + self.vm_name, + self.mock_config_spec, + self.mock_folder_object, + self.mock_resourcepool_object, + ) + self.assertEqual( + exc.exception.strerror, "Not enough permissions. Required privilege: " + ) def test_create_vm_raise_vim_fault(self): exception = vim.fault.VimFault() - exception.msg = 'vim.fault.VimFault msg' + exception.msg = "vim.fault.VimFault msg" self.mock_folder_object.CreateVM_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareApiError) as exc: - salt.utils.vmware.create_vm(self.vm_name, self.mock_config_spec, - self.mock_folder_object, - self.mock_resourcepool_object) - self.assertEqual(exc.exception.strerror, 'vim.fault.VimFault msg') + salt.utils.vmware.create_vm( + self.vm_name, + self.mock_config_spec, + self.mock_folder_object, + self.mock_resourcepool_object, + ) + self.assertEqual(exc.exception.strerror, "vim.fault.VimFault msg") def test_create_vm_raise_runtime_fault(self): exception = vmodl.RuntimeFault() - exception.msg = 'vmodl.RuntimeFault msg' + exception.msg = "vmodl.RuntimeFault msg" self.mock_folder_object.CreateVM_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareRuntimeError) as exc: - salt.utils.vmware.create_vm(self.vm_name, self.mock_config_spec, - self.mock_folder_object, - self.mock_resourcepool_object) - self.assertEqual(exc.exception.strerror, 'vmodl.RuntimeFault msg') + salt.utils.vmware.create_vm( + self.vm_name, + self.mock_config_spec, + self.mock_folder_object, + self.mock_resourcepool_object, + ) + self.assertEqual(exc.exception.strerror, "vmodl.RuntimeFault msg") def test_create_vm_wait_for_task(self): mock_wait_for_task = MagicMock() - with patch('salt.utils.vmware.wait_for_task', mock_wait_for_task): - salt.utils.vmware.create_vm(self.vm_name, self.mock_config_spec, - self.mock_folder_object, - self.mock_resourcepool_object) + with patch("salt.utils.vmware.wait_for_task", mock_wait_for_task): + salt.utils.vmware.create_vm( + self.vm_name, + self.mock_config_spec, + self.mock_folder_object, + self.mock_resourcepool_object, + ) mock_wait_for_task.assert_called_once_with( - self.mock_task, self.vm_name, 'CreateVM Task', 10, 'info') + self.mock_task, self.vm_name, "CreateVM Task", 10, "info" + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@patch('salt.utils.vmware.get_managed_object_name', MagicMock()) -@patch('salt.utils.vmware.wait_for_task', MagicMock()) +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@patch("salt.utils.vmware.get_managed_object_name", MagicMock()) +@patch("salt.utils.vmware.wait_for_task", MagicMock()) class RegisterVirtualMachineTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.register_vm - ''' + """ def setUp(self): - self.vm_name = 'fake_vm' + self.vm_name = "fake_vm" self.mock_task = MagicMock() self.mock_vmx_path = MagicMock() self.mock_resourcepool_object = MagicMock() @@ -4400,65 +4964,85 @@ class RegisterVirtualMachineTestCase(TestCase): self.datacenter = MagicMock(vmFolder=self.vm_folder_object) def test_register_vm_pool_task_call(self): - salt.utils.vmware.register_vm(self.datacenter, self.vm_name, - self.mock_vmx_path, - self.mock_resourcepool_object) + salt.utils.vmware.register_vm( + self.datacenter, + self.vm_name, + self.mock_vmx_path, + self.mock_resourcepool_object, + ) self.assert_called_once(self.mock_vm_register_task) def test_register_vm_host_task_call(self): - salt.utils.vmware.register_vm(self.datacenter, self.vm_name, - self.mock_vmx_path, - self.mock_resourcepool_object, - host_object=self.mock_host_object) + salt.utils.vmware.register_vm( + self.datacenter, + self.vm_name, + self.mock_vmx_path, + self.mock_resourcepool_object, + host_object=self.mock_host_object, + ) self.assert_called_once(self.mock_vm_register_task) def test_register_vm_raise_no_permission(self): exception = vim.fault.NoPermission() self.vm_folder_object.RegisterVM_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareApiError) as exc: - salt.utils.vmware.register_vm(self.datacenter, self.vm_name, - self.mock_vmx_path, - self.mock_resourcepool_object) - self.assertEqual(exc.exception.strerror, - 'Not enough permissions. Required privilege: ') + salt.utils.vmware.register_vm( + self.datacenter, + self.vm_name, + self.mock_vmx_path, + self.mock_resourcepool_object, + ) + self.assertEqual( + exc.exception.strerror, "Not enough permissions. Required privilege: " + ) def test_register_vm_raise_vim_fault(self): exception = vim.fault.VimFault() - exception.msg = 'vim.fault.VimFault msg' + exception.msg = "vim.fault.VimFault msg" self.vm_folder_object.RegisterVM_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareApiError) as exc: - salt.utils.vmware.register_vm(self.datacenter, self.vm_name, - self.mock_vmx_path, - self.mock_resourcepool_object) - self.assertEqual(exc.exception.strerror, 'vim.fault.VimFault msg') + salt.utils.vmware.register_vm( + self.datacenter, + self.vm_name, + self.mock_vmx_path, + self.mock_resourcepool_object, + ) + self.assertEqual(exc.exception.strerror, "vim.fault.VimFault msg") def test_register_vm_raise_runtime_fault(self): exception = vmodl.RuntimeFault() - exception.msg = 'vmodl.RuntimeFault msg' + exception.msg = "vmodl.RuntimeFault msg" self.vm_folder_object.RegisterVM_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareRuntimeError) as exc: - salt.utils.vmware.register_vm(self.datacenter, self.vm_name, - self.mock_vmx_path, - self.mock_resourcepool_object) - self.assertEqual(exc.exception.strerror, 'vmodl.RuntimeFault msg') + salt.utils.vmware.register_vm( + self.datacenter, + self.vm_name, + self.mock_vmx_path, + self.mock_resourcepool_object, + ) + self.assertEqual(exc.exception.strerror, "vmodl.RuntimeFault msg") def test_register_vm_wait_for_task(self): mock_wait_for_task = MagicMock() - with patch('salt.utils.vmware.wait_for_task', mock_wait_for_task): - salt.utils.vmware.register_vm(self.datacenter, self.vm_name, - self.mock_vmx_path, - self.mock_resourcepool_object) + with patch("salt.utils.vmware.wait_for_task", mock_wait_for_task): + salt.utils.vmware.register_vm( + self.datacenter, + self.vm_name, + self.mock_vmx_path, + self.mock_resourcepool_object, + ) mock_wait_for_task.assert_called_once_with( - self.mock_task, self.vm_name, 'RegisterVM Task') + self.mock_task, self.vm_name, "RegisterVM Task" + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@patch('salt.utils.vmware.get_managed_object_name', MagicMock()) -@patch('salt.utils.vmware.wait_for_task', MagicMock()) +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@patch("salt.utils.vmware.get_managed_object_name", MagicMock()) +@patch("salt.utils.vmware.wait_for_task", MagicMock()) class UpdateVirtualMachineTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.update_vm - ''' + """ def setUp(self): self.mock_task = MagicMock() @@ -4472,37 +5056,39 @@ class UpdateVirtualMachineTestCase(TestCase): def test_update_vm_raise_vim_fault(self): exception = vim.fault.VimFault() - exception.msg = 'vim.fault.VimFault' + exception.msg = "vim.fault.VimFault" self.mock_vm_ref.ReconfigVM_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareApiError) as exc: salt.utils.vmware.update_vm(self.mock_vm_ref, self.mock_config_spec) - self.assertEqual(exc.exception.strerror, 'vim.fault.VimFault') + self.assertEqual(exc.exception.strerror, "vim.fault.VimFault") def test_update_vm_raise_runtime_fault(self): exception = vmodl.RuntimeFault() - exception.msg = 'vmodl.RuntimeFault' + exception.msg = "vmodl.RuntimeFault" self.mock_vm_ref.ReconfigVM_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareRuntimeError) as exc: salt.utils.vmware.update_vm(self.mock_vm_ref, self.mock_config_spec) - self.assertEqual(exc.exception.strerror, 'vmodl.RuntimeFault') + self.assertEqual(exc.exception.strerror, "vmodl.RuntimeFault") def test_update_vm_wait_for_task(self): mock_wait_for_task = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='my_vm')): - with patch('salt.utils.vmware.wait_for_task', mock_wait_for_task): + with patch( + "salt.utils.vmware.get_managed_object_name", MagicMock(return_value="my_vm") + ): + with patch("salt.utils.vmware.wait_for_task", mock_wait_for_task): salt.utils.vmware.update_vm(self.mock_vm_ref, self.mock_config_spec) mock_wait_for_task.assert_called_once_with( - self.mock_task, 'my_vm', 'ReconfigureVM Task') + self.mock_task, "my_vm", "ReconfigureVM Task" + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@patch('salt.utils.vmware.get_managed_object_name', MagicMock()) -@patch('salt.utils.vmware.wait_for_task', MagicMock()) +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@patch("salt.utils.vmware.get_managed_object_name", MagicMock()) +@patch("salt.utils.vmware.wait_for_task", MagicMock()) class DeleteVirtualMachineTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.delete_vm - ''' + """ def setUp(self): self.mock_task = MagicMock() @@ -4515,36 +5101,38 @@ class DeleteVirtualMachineTestCase(TestCase): def test_destroy_vm_raise_vim_fault(self): exception = vim.fault.VimFault() - exception.msg = 'vim.fault.VimFault' + exception.msg = "vim.fault.VimFault" self.mock_vm_ref.Destroy_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareApiError) as exc: salt.utils.vmware.delete_vm(self.mock_vm_ref) - self.assertEqual(exc.exception.strerror, 'vim.fault.VimFault') + self.assertEqual(exc.exception.strerror, "vim.fault.VimFault") def test_destroy_vm_raise_runtime_fault(self): exception = vmodl.RuntimeFault() - exception.msg = 'vmodl.RuntimeFault' + exception.msg = "vmodl.RuntimeFault" self.mock_vm_ref.Destroy_Task = MagicMock(side_effect=exception) with self.assertRaises(VMwareRuntimeError) as exc: salt.utils.vmware.delete_vm(self.mock_vm_ref) - self.assertEqual(exc.exception.strerror, 'vmodl.RuntimeFault') + self.assertEqual(exc.exception.strerror, "vmodl.RuntimeFault") def test_destroy_vm_wait_for_task(self): mock_wait_for_task = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='my_vm')): - with patch('salt.utils.vmware.wait_for_task', mock_wait_for_task): + with patch( + "salt.utils.vmware.get_managed_object_name", MagicMock(return_value="my_vm") + ): + with patch("salt.utils.vmware.wait_for_task", mock_wait_for_task): salt.utils.vmware.delete_vm(self.mock_vm_ref) mock_wait_for_task.assert_called_once_with( - self.mock_task, 'my_vm', 'Destroy Task') + self.mock_task, "my_vm", "Destroy Task" + ) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@patch('salt.utils.vmware.get_managed_object_name', MagicMock()) +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@patch("salt.utils.vmware.get_managed_object_name", MagicMock()) class UnregisterVirtualMachineTestCase(TestCase): - ''' + """ Tests for salt.utils.vmware.unregister_vm - ''' + """ def setUp(self): self.mock_vm_unregister = MagicMock() @@ -4556,16 +5144,16 @@ class UnregisterVirtualMachineTestCase(TestCase): def test_unregister_vm_raise_vim_fault(self): exception = vim.fault.VimFault() - exception.msg = 'vim.fault.VimFault' + exception.msg = "vim.fault.VimFault" self.mock_vm_ref.UnregisterVM = MagicMock(side_effect=exception) with self.assertRaises(VMwareApiError) as exc: salt.utils.vmware.unregister_vm(self.mock_vm_ref) - self.assertEqual(exc.exception.strerror, 'vim.fault.VimFault') + self.assertEqual(exc.exception.strerror, "vim.fault.VimFault") def test_unregister_vm_raise_runtime_fault(self): exception = vmodl.RuntimeFault() - exception.msg = 'vmodl.RuntimeFault' + exception.msg = "vmodl.RuntimeFault" self.mock_vm_ref.UnregisterVM = MagicMock(side_effect=exception) with self.assertRaises(VMwareRuntimeError) as exc: salt.utils.vmware.unregister_vm(self.mock_vm_ref) - self.assertEqual(exc.exception.strerror, 'vmodl.RuntimeFault') + self.assertEqual(exc.exception.strerror, "vmodl.RuntimeFault") diff --git a/tests/unit/utils/test_vsan.py b/tests/unit/utils/test_vsan.py index 0dbe8014a45..496ac77ea61 100644 --- a/tests/unit/utils/test_vsan.py +++ b/tests/unit/utils/test_vsan.py @@ -1,27 +1,31 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: :email:`Alexandru Bleotu <alexandru.bleotu@morganstanley.com>` Tests functions in salt.utils.vsan -''' +""" # Import python libraries from __future__ import absolute_import, print_function, unicode_literals + import logging +# Import Salt libraries +from salt.exceptions import ( + VMwareApiError, + VMwareObjectRetrievalError, + VMwareRuntimeError, +) +from salt.utils import vsan + # Import Salt testing libraries from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, PropertyMock, patch from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, MagicMock, \ - PropertyMock - -# Import Salt libraries -from salt.exceptions import VMwareApiError, VMwareRuntimeError, \ - VMwareObjectRetrievalError -from salt.utils import vsan try: from pyVmomi import vim, vmodl # pylint: disable=no-name-in-module + HAS_PYVMOMI = True except ImportError: HAS_PYVMOMI = False @@ -32,70 +36,75 @@ HAS_PYVSAN = vsan.HAS_PYVSAN log = logging.getLogger(__name__) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class VsanSupportedTestCase(TestCase): - '''Tests for salt.utils.vsan.vsan_supported''' + """Tests for salt.utils.vsan.vsan_supported""" def test_supported_api_version(self): mock_si = MagicMock(content=MagicMock(about=MagicMock())) - type(mock_si.content.about).apiVersion = \ - PropertyMock(return_value='6.0') + type(mock_si.content.about).apiVersion = PropertyMock(return_value="6.0") self.assertTrue(vsan.vsan_supported(mock_si)) def test_unsupported_api_version(self): mock_si = MagicMock(content=MagicMock(about=MagicMock())) - type(mock_si.content.about).apiVersion = \ - PropertyMock(return_value='5.0') + type(mock_si.content.about).apiVersion = PropertyMock(return_value="5.0") self.assertFalse(vsan.vsan_supported(mock_si)) def test_api_version_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' + exc.privilegeId = "Fake privilege" mock_si = MagicMock(content=MagicMock(about=MagicMock())) type(mock_si.content.about).apiVersion = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: vsan.vsan_supported(mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_api_version_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' + exc.msg = "VimFault msg" mock_si = MagicMock(content=MagicMock(about=MagicMock())) type(mock_si.content.about).apiVersion = PropertyMock(side_effect=exc) with self.assertRaises(VMwareApiError) as excinfo: vsan.vsan_supported(mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_api_version_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' + exc.msg = "RuntimeFault msg" mock_si = MagicMock(content=MagicMock(about=MagicMock())) type(mock_si.content.about).apiVersion = PropertyMock(side_effect=exc) with self.assertRaises(VMwareRuntimeError) as excinfo: vsan.vsan_supported(mock_si) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class GetVsanClusterConfigSystemTestCase(TestCase, LoaderModuleMockMixin): - '''Tests for salt.utils.vsan.get_vsan_cluster_config_system''' + """Tests for salt.utils.vsan.get_vsan_cluster_config_system""" + def setup_loader_modules(self): - return {vsan: { - '__virtual__': MagicMock(return_value='vsan'), - 'sys': MagicMock(), - 'ssl': MagicMock()}} + return { + vsan: { + "__virtual__": MagicMock(return_value="vsan"), + "sys": MagicMock(), + "ssl": MagicMock(), + } + } def setUp(self): self.mock_si = MagicMock() self.mock_ret = MagicMock() - patches = (('salt.utils.vsan.vsanapiutils.GetVsanVcMos', - MagicMock( - return_value={'vsan-cluster-config-system': - self.mock_ret})),) + patches = ( + ( + "salt.utils.vsan.vsanapiutils.GetVsanVcMos", + MagicMock(return_value={"vsan-cluster-config-system": self.mock_ret}), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() @@ -103,13 +112,16 @@ class GetVsanClusterConfigSystemTestCase(TestCase, LoaderModuleMockMixin): type(vsan.sys).version_info = PropertyMock(return_value=(2, 7, 9)) self.mock_context = MagicMock() - self.mock_create_default_context = \ - MagicMock(return_value=self.mock_context) + self.mock_create_default_context = MagicMock(return_value=self.mock_context) vsan.ssl.create_default_context = self.mock_create_default_context def tearDown(self): - for attr in ('mock_si', 'mock_ret', 'mock_context', - 'mock_create_default_context'): + for attr in ( + "mock_si", + "mock_ret", + "mock_context", + "mock_create_default_context", + ): delattr(self, attr) def test_ssl_default_context_loaded(self): @@ -125,34 +137,40 @@ class GetVsanClusterConfigSystemTestCase(TestCase, LoaderModuleMockMixin): def test_GetVsanVcMos_call(self): mock_get_vsan_vc_mos = MagicMock() - with patch('salt.utils.vsan.vsanapiutils.GetVsanVcMos', - mock_get_vsan_vc_mos): + with patch("salt.utils.vsan.vsanapiutils.GetVsanVcMos", mock_get_vsan_vc_mos): vsan.get_vsan_cluster_config_system(self.mock_si) - mock_get_vsan_vc_mos.assert_called_once_with(self.mock_si._stub, - context=self.mock_context) + mock_get_vsan_vc_mos.assert_called_once_with( + self.mock_si._stub, context=self.mock_context + ) def test_return(self): ret = vsan.get_vsan_cluster_config_system(self.mock_si) self.assertEqual(ret, self.mock_ret) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'pyvsan\' bindings are missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'pyvsan' bindings are missing") class GetVsanDiskManagementSystemTestCase(TestCase, LoaderModuleMockMixin): - '''Tests for salt.utils.vsan.get_vsan_disk_management_system''' + """Tests for salt.utils.vsan.get_vsan_disk_management_system""" + def setup_loader_modules(self): - return {vsan: { - '__virtual__': MagicMock(return_value='vsan'), - 'sys': MagicMock(), - 'ssl': MagicMock()}} + return { + vsan: { + "__virtual__": MagicMock(return_value="vsan"), + "sys": MagicMock(), + "ssl": MagicMock(), + } + } def setUp(self): self.mock_si = MagicMock() self.mock_ret = MagicMock() - patches = (('salt.utils.vsan.vsanapiutils.GetVsanVcMos', - MagicMock( - return_value={'vsan-disk-management-system': - self.mock_ret})),) + patches = ( + ( + "salt.utils.vsan.vsanapiutils.GetVsanVcMos", + MagicMock(return_value={"vsan-disk-management-system": self.mock_ret}), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() @@ -160,13 +178,16 @@ class GetVsanDiskManagementSystemTestCase(TestCase, LoaderModuleMockMixin): type(vsan.sys).version_info = PropertyMock(return_value=(2, 7, 9)) self.mock_context = MagicMock() - self.mock_create_default_context = \ - MagicMock(return_value=self.mock_context) + self.mock_create_default_context = MagicMock(return_value=self.mock_context) vsan.ssl.create_default_context = self.mock_create_default_context def tearDown(self): - for attr in ('mock_si', 'mock_ret', 'mock_context', - 'mock_create_default_context'): + for attr in ( + "mock_si", + "mock_ret", + "mock_context", + "mock_create_default_context", + ): delattr(self, attr) def test_ssl_default_context_loaded(self): @@ -182,21 +203,21 @@ class GetVsanDiskManagementSystemTestCase(TestCase, LoaderModuleMockMixin): def test_GetVsanVcMos_call(self): mock_get_vsan_vc_mos = MagicMock() - with patch('salt.utils.vsan.vsanapiutils.GetVsanVcMos', - mock_get_vsan_vc_mos): + with patch("salt.utils.vsan.vsanapiutils.GetVsanVcMos", mock_get_vsan_vc_mos): vsan.get_vsan_disk_management_system(self.mock_si) - mock_get_vsan_vc_mos.assert_called_once_with(self.mock_si._stub, - context=self.mock_context) + mock_get_vsan_vc_mos.assert_called_once_with( + self.mock_si._stub, context=self.mock_context + ) def test_return(self): ret = vsan.get_vsan_disk_management_system(self.mock_si) self.assertEqual(ret, self.mock_ret) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class GetHostVsanSystemTestCase(TestCase): - '''Tests for salt.utils.vsan.get_host_vsan_system''' + """Tests for salt.utils.vsan.get_host_vsan_system""" def setUp(self): self.mock_host_ref = MagicMock() @@ -204,216 +225,274 @@ class GetHostVsanSystemTestCase(TestCase): self.mock_traversal_spec = MagicMock() self.mock_vsan_system = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_hostname')), - ('salt.utils.vsan.vmodl.query.PropertyCollector.TraversalSpec', - MagicMock(return_value=self.mock_traversal_spec)), - ('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=self.mock_traversal_spec)), - ('salt.utils.vmware.get_mors_with_properties', - MagicMock(return_value=[{'object': self.mock_vsan_system}]))) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_hostname"), + ), + ( + "salt.utils.vsan.vmodl.query.PropertyCollector.TraversalSpec", + MagicMock(return_value=self.mock_traversal_spec), + ), + ( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=self.mock_traversal_spec), + ), + ( + "salt.utils.vmware.get_mors_with_properties", + MagicMock(return_value=[{"object": self.mock_vsan_system}]), + ), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def test_get_hostname(self): - mock_get_managed_object_name = MagicMock(return_value='fake_hostname') - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + mock_get_managed_object_name = MagicMock(return_value="fake_hostname") + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): vsan.get_host_vsan_system(self.mock_si, self.mock_host_ref) - mock_get_managed_object_name.assert_called_once_with( - self.mock_host_ref) + mock_get_managed_object_name.assert_called_once_with(self.mock_host_ref) def test_hostname_argument(self): - mock_get_managed_object_name = MagicMock(return_value='fake_hostname') - with patch('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_hostname')): - vsan.get_host_vsan_system(self.mock_si, - self.mock_host_ref, - hostname='passedin_hostname') + mock_get_managed_object_name = MagicMock(return_value="fake_hostname") + with patch( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_hostname"), + ): + vsan.get_host_vsan_system( + self.mock_si, self.mock_host_ref, hostname="passedin_hostname" + ) self.assertEqual(mock_get_managed_object_name.call_count, 0) def test_traversal_spec(self): mock_traversal_spec = MagicMock(return_value=self.mock_traversal_spec) with patch( - 'salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec', - mock_traversal_spec): + "salt.utils.vmware.vmodl.query.PropertyCollector.TraversalSpec", + mock_traversal_spec, + ): vsan.get_host_vsan_system(self.mock_si, self.mock_host_ref) mock_traversal_spec.assert_called_once_with( - path='configManager.vsanSystem', - type=vim.HostSystem, - skip=False) + path="configManager.vsanSystem", type=vim.HostSystem, skip=False + ) def test_get_mors_with_properties(self): - mock_get_mors = \ - MagicMock(return_value=[{'object': self.mock_vsan_system}]) - with patch('salt.utils.vmware.get_mors_with_properties', - mock_get_mors): + mock_get_mors = MagicMock(return_value=[{"object": self.mock_vsan_system}]) + with patch("salt.utils.vmware.get_mors_with_properties", mock_get_mors): vsan.get_host_vsan_system(self.mock_si, self.mock_host_ref) mock_get_mors.assert_called_once_with( self.mock_si, vim.HostVsanSystem, - property_list=['config.enabled'], + property_list=["config.enabled"], container_ref=self.mock_host_ref, - traversal_spec=self.mock_traversal_spec) + traversal_spec=self.mock_traversal_spec, + ) def test_empty_mors_result(self): mock_get_mors = MagicMock(return_value=None) - with patch('salt.utils.vmware.get_mors_with_properties', - mock_get_mors): + with patch("salt.utils.vmware.get_mors_with_properties", mock_get_mors): with self.assertRaises(VMwareObjectRetrievalError) as excinfo: vsan.get_host_vsan_system(self.mock_si, self.mock_host_ref) - self.assertEqual(excinfo.exception.strerror, - 'Host\'s \'fake_hostname\' VSAN system was ' - 'not retrieved') + self.assertEqual( + excinfo.exception.strerror, + "Host's 'fake_hostname' VSAN system was " "not retrieved", + ) def test_valid_mors_result(self): res = vsan.get_host_vsan_system(self.mock_si, self.mock_host_ref) self.assertEqual(res, self.mock_vsan_system) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class CreateDiskgroupTestCase(TestCase): - '''Tests for salt.utils.vsan.create_diskgroup''' + """Tests for salt.utils.vsan.create_diskgroup""" + def setUp(self): self.mock_si = MagicMock() self.mock_task = MagicMock() - self.mock_initialise_disk_mapping = \ - MagicMock(return_value=self.mock_task) + self.mock_initialise_disk_mapping = MagicMock(return_value=self.mock_task) self.mock_vsan_disk_mgmt_system = MagicMock( - InitializeDiskMappings=self.mock_initialise_disk_mapping) + InitializeDiskMappings=self.mock_initialise_disk_mapping + ) self.mock_host_ref = MagicMock() self.mock_cache_disk = MagicMock() self.mock_cap_disk1 = MagicMock() self.mock_cap_disk2 = MagicMock() self.mock_spec = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_hostname')), - ('salt.utils.vsan.vim.VimVsanHostDiskMappingCreationSpec', - MagicMock(return_value=self.mock_spec)), - ('salt.utils.vsan._wait_for_tasks', MagicMock())) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_hostname"), + ), + ( + "salt.utils.vsan.vim.VimVsanHostDiskMappingCreationSpec", + MagicMock(return_value=self.mock_spec), + ), + ("salt.utils.vsan._wait_for_tasks", MagicMock()), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def test_get_hostname(self): - mock_get_managed_object_name = MagicMock(return_value='fake_hostname') - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): - vsan.create_diskgroup(self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) - mock_get_managed_object_name.assert_called_once_with( - self.mock_host_ref) + mock_get_managed_object_name = MagicMock(return_value="fake_hostname") + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): + vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + mock_get_managed_object_name.assert_called_once_with(self.mock_host_ref) def test_vsan_spec_all_flash(self): self.mock_cap_disk1.ssd = True - vsan.create_diskgroup(self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(self.mock_spec.capacityDisks, [self.mock_cap_disk1, - self.mock_cap_disk2]) + vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual( + self.mock_spec.capacityDisks, [self.mock_cap_disk1, self.mock_cap_disk2] + ) self.assertEqual(self.mock_spec.cacheDisks, [self.mock_cache_disk]) - self.assertEqual(self.mock_spec.creationType, 'allFlash') + self.assertEqual(self.mock_spec.creationType, "allFlash") self.assertEqual(self.mock_spec.host, self.mock_host_ref) def test_vsan_spec_hybrid(self): self.mock_cap_disk1.ssd = False - vsan.create_diskgroup(self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) + vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) self.mock_cap_disk1.ssd = False - self.assertEqual(self.mock_spec.creationType, 'hybrid') + self.assertEqual(self.mock_spec.creationType, "hybrid") def test_initialize_disk_mapping(self): - vsan.create_diskgroup(self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.mock_initialise_disk_mapping.assert_called_once_with( - self.mock_spec) + vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.mock_initialise_disk_mapping.assert_called_once_with(self.mock_spec) def test_initialize_disk_mapping_raise_no_permission(self): err = vim.fault.NoPermission() - err.privilegeId = 'Fake privilege' - self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = \ - MagicMock(side_effect=err) + err.privilegeId = "Fake privilege" + self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = MagicMock( + side_effect=err + ) with self.assertRaises(VMwareApiError) as excinfo: - vsan.create_diskgroup(self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_initialize_disk_mapping_raise_vim_fault(self): err = vim.fault.VimFault() - err.msg = 'vim_fault' - self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = \ - MagicMock(side_effect=err) + err.msg = "vim_fault" + self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = MagicMock( + side_effect=err + ) with self.assertRaises(VMwareApiError) as excinfo: - vsan.create_diskgroup(self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, 'vim_fault') + vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual(excinfo.exception.strerror, "vim_fault") def test_initialize_disk_mapping_raise_method_not_found(self): err = vmodl.fault.MethodNotFound() - err.method = 'fake_method' - self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = \ - MagicMock(side_effect=err) + err.method = "fake_method" + self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = MagicMock( + side_effect=err + ) with self.assertRaises(VMwareRuntimeError) as excinfo: - vsan.create_diskgroup(self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, - 'Method \'fake_method\' not found') + vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual(excinfo.exception.strerror, "Method 'fake_method' not found") def test_initialize_disk_mapping_raise_runtime_fault(self): err = vmodl.RuntimeFault() - err.msg = 'runtime_fault' - self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = \ - MagicMock(side_effect=err) + err.msg = "runtime_fault" + self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = MagicMock( + side_effect=err + ) with self.assertRaises(VMwareRuntimeError) as excinfo: - vsan.create_diskgroup(self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, 'runtime_fault') + vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual(excinfo.exception.strerror, "runtime_fault") def test__wait_for_tasks(self): mock___wait_for_tasks = MagicMock() - with patch('salt.utils.vsan._wait_for_tasks', - mock___wait_for_tasks): - vsan.create_diskgroup(self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) - mock___wait_for_tasks.assert_called_once_with( - [self.mock_task], self.mock_si) + with patch("salt.utils.vsan._wait_for_tasks", mock___wait_for_tasks): + vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + mock___wait_for_tasks.assert_called_once_with([self.mock_task], self.mock_si) def test_result(self): - res = vsan.create_diskgroup(self.mock_si, - self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_cache_disk, - [self.mock_cap_disk1, self.mock_cap_disk2]) + res = vsan.create_diskgroup( + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_cache_disk, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) self.assertTrue(res) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class AddCapacityToDiskGroupTestCase(TestCase): - '''Tests for salt.utils.vsan.add_capacity_to_diskgroup''' + """Tests for salt.utils.vsan.add_capacity_to_diskgroup""" + def setUp(self): self.mock_si = MagicMock() self.mock_task = MagicMock() - self.mock_initialise_disk_mapping = \ - MagicMock(return_value=self.mock_task) + self.mock_initialise_disk_mapping = MagicMock(return_value=self.mock_task) self.mock_vsan_disk_mgmt_system = MagicMock( - InitializeDiskMappings=self.mock_initialise_disk_mapping) + InitializeDiskMappings=self.mock_initialise_disk_mapping + ) self.mock_host_ref = MagicMock() self.mock_cache_disk = MagicMock() self.mock_diskgroup = MagicMock(ssd=self.mock_cache_disk) @@ -421,137 +500,173 @@ class AddCapacityToDiskGroupTestCase(TestCase): self.mock_cap_disk2 = MagicMock() self.mock_spec = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_hostname')), - ('salt.utils.vsan.vim.VimVsanHostDiskMappingCreationSpec', - MagicMock(return_value=self.mock_spec)), - ('salt.utils.vsan._wait_for_tasks', MagicMock())) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_hostname"), + ), + ( + "salt.utils.vsan.vim.VimVsanHostDiskMappingCreationSpec", + MagicMock(return_value=self.mock_spec), + ), + ("salt.utils.vsan._wait_for_tasks", MagicMock()), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def test_get_hostname(self): - mock_get_managed_object_name = MagicMock(return_value='fake_hostname') - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + mock_get_managed_object_name = MagicMock(return_value="fake_hostname") + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - mock_get_managed_object_name.assert_called_once_with( - self.mock_host_ref) + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + mock_get_managed_object_name.assert_called_once_with(self.mock_host_ref) def test_vsan_spec_all_flash(self): self.mock_cap_disk1.ssd = True vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(self.mock_spec.capacityDisks, [self.mock_cap_disk1, - self.mock_cap_disk2]) + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual( + self.mock_spec.capacityDisks, [self.mock_cap_disk1, self.mock_cap_disk2] + ) self.assertEqual(self.mock_spec.cacheDisks, [self.mock_cache_disk]) - self.assertEqual(self.mock_spec.creationType, 'allFlash') + self.assertEqual(self.mock_spec.creationType, "allFlash") self.assertEqual(self.mock_spec.host, self.mock_host_ref) def test_vsan_spec_hybrid(self): self.mock_cap_disk1.ssd = False vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) self.mock_cap_disk1.ssd = False - self.assertEqual(self.mock_spec.creationType, 'hybrid') + self.assertEqual(self.mock_spec.creationType, "hybrid") def test_initialize_disk_mapping(self): vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.mock_initialise_disk_mapping.assert_called_once_with( - self.mock_spec) + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.mock_initialise_disk_mapping.assert_called_once_with(self.mock_spec) def test_initialize_disk_mapping_raise_no_permission(self): err = vim.fault.NoPermission() - err.privilegeId = 'Fake privilege' - self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = \ - MagicMock(side_effect=err) + err.privilegeId = "Fake privilege" + self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = MagicMock( + side_effect=err + ) with self.assertRaises(VMwareApiError) as excinfo: vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_initialize_disk_mapping_raise_vim_fault(self): err = vim.fault.VimFault() - err.msg = 'vim_fault' - self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = \ - MagicMock(side_effect=err) + err.msg = "vim_fault" + self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = MagicMock( + side_effect=err + ) with self.assertRaises(VMwareApiError) as excinfo: vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, 'vim_fault') + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual(excinfo.exception.strerror, "vim_fault") def test_initialize_disk_mapping_raise_method_not_found(self): err = vmodl.fault.MethodNotFound() - err.method = 'fake_method' - self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = \ - MagicMock(side_effect=err) + err.method = "fake_method" + self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = MagicMock( + side_effect=err + ) with self.assertRaises(VMwareRuntimeError) as excinfo: vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, - 'Method \'fake_method\' not found') + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual(excinfo.exception.strerror, "Method 'fake_method' not found") def test_initialize_disk_mapping_raise_runtime_fault(self): err = vmodl.RuntimeFault() - err.msg = 'runtime_fault' - self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = \ - MagicMock(side_effect=err) + err.msg = "runtime_fault" + self.mock_vsan_disk_mgmt_system.InitializeDiskMappings = MagicMock( + side_effect=err + ) with self.assertRaises(VMwareRuntimeError) as excinfo: vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, 'runtime_fault') + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual(excinfo.exception.strerror, "runtime_fault") def test__wait_for_tasks(self): mock___wait_for_tasks = MagicMock() - with patch('salt.utils.vsan._wait_for_tasks', - mock___wait_for_tasks): + with patch("salt.utils.vsan._wait_for_tasks", mock___wait_for_tasks): vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - mock___wait_for_tasks.assert_called_once_with( - [self.mock_task], self.mock_si) + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + mock___wait_for_tasks.assert_called_once_with([self.mock_task], self.mock_si) def test_result(self): res = vsan.add_capacity_to_diskgroup( - self.mock_si, self.mock_vsan_disk_mgmt_system, - self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) + self.mock_si, + self.mock_vsan_disk_mgmt_system, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) self.assertTrue(res) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class RemoveCapacityFromDiskGroup(TestCase): - '''Tests for salt.utils.vsan.remove_capacity_from_diskgroup''' + """Tests for salt.utils.vsan.remove_capacity_from_diskgroup""" + def setUp(self): self.mock_si = MagicMock() self.mock_task = MagicMock() - self.mock_remove_disk = \ - MagicMock(return_value=self.mock_task) - self.mock_host_vsan_system = MagicMock( - RemoveDisk_Task=self.mock_remove_disk) + self.mock_remove_disk = MagicMock(return_value=self.mock_task) + self.mock_host_vsan_system = MagicMock(RemoveDisk_Task=self.mock_remove_disk) self.mock_host_ref = MagicMock() self.mock_cache_disk = MagicMock() self.mock_diskgroup = MagicMock(ssd=self.mock_cache_disk) @@ -559,115 +674,153 @@ class RemoveCapacityFromDiskGroup(TestCase): self.mock_cap_disk2 = MagicMock() self.mock_spec = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_hostname')), - ('salt.utils.vsan.get_host_vsan_system', - MagicMock(return_value=self.mock_host_vsan_system)), - ('salt.utils.vsan.vim.HostMaintenanceSpec', - MagicMock(return_value=self.mock_spec)), - ('salt.utils.vsan.vim.VsanHostDecommissionMode', MagicMock()), - ('salt.utils.vmware.wait_for_task', MagicMock())) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_hostname"), + ), + ( + "salt.utils.vsan.get_host_vsan_system", + MagicMock(return_value=self.mock_host_vsan_system), + ), + ( + "salt.utils.vsan.vim.HostMaintenanceSpec", + MagicMock(return_value=self.mock_spec), + ), + ("salt.utils.vsan.vim.VsanHostDecommissionMode", MagicMock()), + ("salt.utils.vmware.wait_for_task", MagicMock()), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def test_get_hostname(self): - mock_get_managed_object_name = MagicMock(return_value='fake_hostname') - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + mock_get_managed_object_name = MagicMock(return_value="fake_hostname") + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - mock_get_managed_object_name.assert_called_once_with( - self.mock_host_ref) + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + mock_get_managed_object_name.assert_called_once_with(self.mock_host_ref) def test_maintenance_mode_evacuate_all_data(self): vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(self.mock_spec.vsanMode.objectAction, - vim.VsanHostDecommissionModeObjectAction.evacuateAllData) + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual( + self.mock_spec.vsanMode.objectAction, + vim.VsanHostDecommissionModeObjectAction.evacuateAllData, + ) def test_maintenance_mode_no_action(self): vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, [self.mock_cap_disk1, self.mock_cap_disk2], - data_evacuation=False) - self.assertEqual(self.mock_spec.vsanMode.objectAction, - vim.VsanHostDecommissionModeObjectAction.noAction) + data_evacuation=False, + ) + self.assertEqual( + self.mock_spec.vsanMode.objectAction, + vim.VsanHostDecommissionModeObjectAction.noAction, + ) def test_remove_disk(self): vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) self.mock_remove_disk.assert_called_once_with( disk=[self.mock_cap_disk1, self.mock_cap_disk2], - maintenanceSpec=self.mock_spec) + maintenanceSpec=self.mock_spec, + ) def test_remove_disk_raise_no_permission(self): err = vim.fault.NoPermission() - err.privilegeId = 'Fake privilege' - self.mock_host_vsan_system.RemoveDisk_Task = \ - MagicMock(side_effect=err) + err.privilegeId = "Fake privilege" + self.mock_host_vsan_system.RemoveDisk_Task = MagicMock(side_effect=err) with self.assertRaises(VMwareApiError) as excinfo: vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_remove_disk_raise_vim_fault(self): err = vim.fault.VimFault() - err.msg = 'vim_fault' - self.mock_host_vsan_system.RemoveDisk_Task = \ - MagicMock(side_effect=err) + err.msg = "vim_fault" + self.mock_host_vsan_system.RemoveDisk_Task = MagicMock(side_effect=err) with self.assertRaises(VMwareApiError) as excinfo: vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, 'vim_fault') + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual(excinfo.exception.strerror, "vim_fault") def test_remove_disk_raise_runtime_fault(self): err = vmodl.RuntimeFault() - err.msg = 'runtime_fault' - self.mock_host_vsan_system.RemoveDisk_Task = \ - MagicMock(side_effect=err) + err.msg = "runtime_fault" + self.mock_host_vsan_system.RemoveDisk_Task = MagicMock(side_effect=err) with self.assertRaises(VMwareRuntimeError) as excinfo: vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(excinfo.exception.strerror, 'runtime_fault') + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual(excinfo.exception.strerror, "runtime_fault") def test_wait_for_tasks(self): mock_wait_for_task = MagicMock() - with patch('salt.utils.vmware.wait_for_task', - mock_wait_for_task): + with patch("salt.utils.vmware.wait_for_task", mock_wait_for_task): vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) mock_wait_for_task.assert_called_once_with( - self.mock_task, 'fake_hostname', 'remove_capacity') + self.mock_task, "fake_hostname", "remove_capacity" + ) def test_result(self): res = vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) self.assertTrue(res) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class RemoveDiskgroup(TestCase): - '''Tests for salt.utils.vsan.remove_diskgroup''' + """Tests for salt.utils.vsan.remove_diskgroup""" + def setUp(self): self.mock_si = MagicMock() self.mock_task = MagicMock() - self.mock_remove_disk_mapping = \ - MagicMock(return_value=self.mock_task) + self.mock_remove_disk_mapping = MagicMock(return_value=self.mock_task) self.mock_host_vsan_system = MagicMock( - RemoveDiskMapping_Task=self.mock_remove_disk_mapping) + RemoveDiskMapping_Task=self.mock_remove_disk_mapping + ) self.mock_host_ref = MagicMock() self.mock_cache_disk = MagicMock() self.mock_diskgroup = MagicMock(ssd=self.mock_cache_disk) @@ -675,345 +828,390 @@ class RemoveDiskgroup(TestCase): self.mock_cap_disk2 = MagicMock() self.mock_spec = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', - MagicMock(return_value='fake_hostname')), - ('salt.utils.vsan.get_host_vsan_system', - MagicMock(return_value=self.mock_host_vsan_system)), - ('salt.utils.vsan.vim.HostMaintenanceSpec', - MagicMock(return_value=self.mock_spec)), - ('salt.utils.vsan.vim.VsanHostDecommissionMode', MagicMock()), - ('salt.utils.vmware.wait_for_task', MagicMock())) + ( + "salt.utils.vmware.get_managed_object_name", + MagicMock(return_value="fake_hostname"), + ), + ( + "salt.utils.vsan.get_host_vsan_system", + MagicMock(return_value=self.mock_host_vsan_system), + ), + ( + "salt.utils.vsan.vim.HostMaintenanceSpec", + MagicMock(return_value=self.mock_spec), + ), + ("salt.utils.vsan.vim.VsanHostDecommissionMode", MagicMock()), + ("salt.utils.vmware.wait_for_task", MagicMock()), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def test_get_hostname(self): - mock_get_managed_object_name = MagicMock(return_value='fake_hostname') - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): - vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) - mock_get_managed_object_name.assert_called_once_with( - self.mock_host_ref) + mock_get_managed_object_name = MagicMock(return_value="fake_hostname") + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): + vsan.remove_diskgroup(self.mock_si, self.mock_host_ref, self.mock_diskgroup) + mock_get_managed_object_name.assert_called_once_with(self.mock_host_ref) def test_maintenance_mode_evacuate_all_data(self): - vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) + vsan.remove_diskgroup(self.mock_si, self.mock_host_ref, self.mock_diskgroup) vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) - self.assertEqual(self.mock_spec.vsanMode.objectAction, - vim.VsanHostDecommissionModeObjectAction.evacuateAllData) + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) + self.assertEqual( + self.mock_spec.vsanMode.objectAction, + vim.VsanHostDecommissionModeObjectAction.evacuateAllData, + ) def test_maintenance_mode_no_action(self): - vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) + vsan.remove_diskgroup(self.mock_si, self.mock_host_ref, self.mock_diskgroup) vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, [self.mock_cap_disk1, self.mock_cap_disk2], - data_evacuation=False) - self.assertEqual(self.mock_spec.vsanMode.objectAction, - vim.VsanHostDecommissionModeObjectAction.noAction) + data_evacuation=False, + ) + self.assertEqual( + self.mock_spec.vsanMode.objectAction, + vim.VsanHostDecommissionModeObjectAction.noAction, + ) def test_remove_disk_mapping(self): - vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) + vsan.remove_diskgroup(self.mock_si, self.mock_host_ref, self.mock_diskgroup) vsan.remove_capacity_from_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup, - [self.mock_cap_disk1, self.mock_cap_disk2]) + self.mock_si, + self.mock_host_ref, + self.mock_diskgroup, + [self.mock_cap_disk1, self.mock_cap_disk2], + ) self.mock_remove_disk_mapping.assert_called_once_with( - mapping=[self.mock_diskgroup], - maintenanceSpec=self.mock_spec) + mapping=[self.mock_diskgroup], maintenanceSpec=self.mock_spec + ) def test_remove_disk_mapping_raise_no_permission(self): - vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) + vsan.remove_diskgroup(self.mock_si, self.mock_host_ref, self.mock_diskgroup) err = vim.fault.NoPermission() - err.privilegeId = 'Fake privilege' - self.mock_host_vsan_system.RemoveDiskMapping_Task = \ - MagicMock(side_effect=err) + err.privilegeId = "Fake privilege" + self.mock_host_vsan_system.RemoveDiskMapping_Task = MagicMock(side_effect=err) with self.assertRaises(VMwareApiError) as excinfo: - vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + vsan.remove_diskgroup(self.mock_si, self.mock_host_ref, self.mock_diskgroup) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_remove_disk_mapping_raise_vim_fault(self): err = vim.fault.VimFault() - err.msg = 'vim_fault' - self.mock_host_vsan_system.RemoveDiskMapping_Task = \ - MagicMock(side_effect=err) + err.msg = "vim_fault" + self.mock_host_vsan_system.RemoveDiskMapping_Task = MagicMock(side_effect=err) with self.assertRaises(VMwareApiError) as excinfo: - vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) - self.assertEqual(excinfo.exception.strerror, 'vim_fault') + vsan.remove_diskgroup(self.mock_si, self.mock_host_ref, self.mock_diskgroup) + self.assertEqual(excinfo.exception.strerror, "vim_fault") def test_remove_disk_mapping_raise_runtime_fault(self): err = vmodl.RuntimeFault() - err.msg = 'runtime_fault' - self.mock_host_vsan_system.RemoveDiskMapping_Task = \ - MagicMock(side_effect=err) + err.msg = "runtime_fault" + self.mock_host_vsan_system.RemoveDiskMapping_Task = MagicMock(side_effect=err) with self.assertRaises(VMwareRuntimeError) as excinfo: - vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) - self.assertEqual(excinfo.exception.strerror, 'runtime_fault') + vsan.remove_diskgroup(self.mock_si, self.mock_host_ref, self.mock_diskgroup) + self.assertEqual(excinfo.exception.strerror, "runtime_fault") def test_wait_for_tasks(self): mock_wait_for_task = MagicMock() - with patch('salt.utils.vmware.wait_for_task', - mock_wait_for_task): - vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) + with patch("salt.utils.vmware.wait_for_task", mock_wait_for_task): + vsan.remove_diskgroup(self.mock_si, self.mock_host_ref, self.mock_diskgroup) mock_wait_for_task.assert_called_once_with( - self.mock_task, 'fake_hostname', 'remove_diskgroup') + self.mock_task, "fake_hostname", "remove_diskgroup" + ) def test_result(self): res = vsan.remove_diskgroup( - self.mock_si, self.mock_host_ref, self.mock_diskgroup) + self.mock_si, self.mock_host_ref, self.mock_diskgroup + ) self.assertTrue(res) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class GetClusterVsanInfoTestCase(TestCase, LoaderModuleMockMixin): - '''Tests for salt.utils.vsan.get_cluster_vsan_info''' + """Tests for salt.utils.vsan.get_cluster_vsan_info""" + def setup_loader_modules(self): - return {vsan: { - '__virtual__': MagicMock(return_value='vsan')}} + return {vsan: {"__virtual__": MagicMock(return_value="vsan")}} def setUp(self): self.mock_cl_ref = MagicMock() self.mock_si = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', MagicMock()), - ('salt.utils.vmware.get_service_instance_from_managed_object', - MagicMock(return_value=self.mock_si)), - ('salt.utils.vsan.get_vsan_cluster_config_system', MagicMock())) + ("salt.utils.vmware.get_managed_object_name", MagicMock()), + ( + "salt.utils.vmware.get_service_instance_from_managed_object", + MagicMock(return_value=self.mock_si), + ), + ("salt.utils.vsan.get_vsan_cluster_config_system", MagicMock()), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_cl_ref'): + for attr in ("mock_si", "mock_cl_ref"): delattr(self, attr) def test_get_managed_object_name_call(self): mock_get_managed_object_name = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - mock_get_managed_object_name): + with patch( + "salt.utils.vmware.get_managed_object_name", mock_get_managed_object_name + ): vsan.get_cluster_vsan_info(self.mock_cl_ref) mock_get_managed_object_name.assert_called_once_with(self.mock_cl_ref) def test_get_vsan_cluster_config_system_call(self): mock_get_vsan_cl_syst = MagicMock() - with patch('salt.utils.vsan.get_vsan_cluster_config_system', - mock_get_vsan_cl_syst): + with patch( + "salt.utils.vsan.get_vsan_cluster_config_system", mock_get_vsan_cl_syst + ): vsan.get_cluster_vsan_info(self.mock_cl_ref) mock_get_vsan_cl_syst.assert_called_once_with(self.mock_si) def test_VsanClusterGetConfig_call(self): mock_vsan_sys = MagicMock() - with patch('salt.utils.vsan.get_vsan_cluster_config_system', - MagicMock(return_value=mock_vsan_sys)): + with patch( + "salt.utils.vsan.get_vsan_cluster_config_system", + MagicMock(return_value=mock_vsan_sys), + ): vsan.get_cluster_vsan_info(self.mock_cl_ref) - mock_vsan_sys.VsanClusterGetConfig.assert_called_once_with( - self.mock_cl_ref) + mock_vsan_sys.VsanClusterGetConfig.assert_called_once_with(self.mock_cl_ref) def test_VsanClusterGetConfig_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - with patch('salt.utils.vsan.get_vsan_cluster_config_system', - MagicMock(return_value=MagicMock( - VsanClusterGetConfig=MagicMock(side_effect=exc)))): + exc.privilegeId = "Fake privilege" + with patch( + "salt.utils.vsan.get_vsan_cluster_config_system", + MagicMock( + return_value=MagicMock(VsanClusterGetConfig=MagicMock(side_effect=exc)) + ), + ): with self.assertRaises(VMwareApiError) as excinfo: vsan.get_cluster_vsan_info(self.mock_cl_ref) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_VsanClusterGetConfig_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - with patch('salt.utils.vsan.get_vsan_cluster_config_system', - MagicMock(return_value=MagicMock( - VsanClusterGetConfig=MagicMock(side_effect=exc)))): + exc.msg = "VimFault msg" + with patch( + "salt.utils.vsan.get_vsan_cluster_config_system", + MagicMock( + return_value=MagicMock(VsanClusterGetConfig=MagicMock(side_effect=exc)) + ), + ): with self.assertRaises(VMwareApiError) as excinfo: vsan.get_cluster_vsan_info(self.mock_cl_ref) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_VsanClusterGetConfig_raises_runtime_fault(self): exc = vmodl.RuntimeFault() - exc.msg = 'RuntimeFault msg' - with patch('salt.utils.vsan.get_vsan_cluster_config_system', - MagicMock(return_value=MagicMock( - VsanClusterGetConfig=MagicMock(side_effect=exc)))): + exc.msg = "RuntimeFault msg" + with patch( + "salt.utils.vsan.get_vsan_cluster_config_system", + MagicMock( + return_value=MagicMock(VsanClusterGetConfig=MagicMock(side_effect=exc)) + ), + ): with self.assertRaises(VMwareRuntimeError) as excinfo: vsan.get_cluster_vsan_info(self.mock_cl_ref) - self.assertEqual(excinfo.exception.strerror, 'RuntimeFault msg') + self.assertEqual(excinfo.exception.strerror, "RuntimeFault msg") -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class ReconfigureClusterVsanTestCase(TestCase): - '''Tests for salt.utils.vsan.reconfigure_cluster_vsan''' + """Tests for salt.utils.vsan.reconfigure_cluster_vsan""" + def setUp(self): self.mock_si = MagicMock() self.mock_task = MagicMock() self.mock_cl_reconf = MagicMock(return_value=self.mock_task) self.mock_get_vsan_conf_sys = MagicMock( - return_value=MagicMock(VsanClusterReconfig=self.mock_cl_reconf)) + return_value=MagicMock(VsanClusterReconfig=self.mock_cl_reconf) + ) self.mock_cl_ref = MagicMock() self.mock_cl_vsan_spec = MagicMock() patches = ( - ('salt.utils.vmware.get_managed_object_name', MagicMock()), - ('salt.utils.vmware.get_service_instance_from_managed_object', - MagicMock(return_value=self.mock_si)), - ('salt.utils.vsan.get_vsan_cluster_config_system', - self.mock_get_vsan_conf_sys), - ('salt.utils.vsan._wait_for_tasks', MagicMock())) + ("salt.utils.vmware.get_managed_object_name", MagicMock()), + ( + "salt.utils.vmware.get_service_instance_from_managed_object", + MagicMock(return_value=self.mock_si), + ), + ( + "salt.utils.vsan.get_vsan_cluster_config_system", + self.mock_get_vsan_conf_sys, + ), + ("salt.utils.vsan._wait_for_tasks", MagicMock()), + ) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_cl_reconf', 'mock_get_vsan_conf_sys', - 'mock_cl_ref', 'mock_cl_vsan_spec', 'mock_task'): + for attr in ( + "mock_si", + "mock_cl_reconf", + "mock_get_vsan_conf_sys", + "mock_cl_ref", + "mock_cl_vsan_spec", + "mock_task", + ): delattr(self, attr) def test_get_cluster_name_call(self): get_managed_object_name_mock = MagicMock() - with patch('salt.utils.vmware.get_managed_object_name', - get_managed_object_name_mock): + with patch( + "salt.utils.vmware.get_managed_object_name", get_managed_object_name_mock + ): - vsan.reconfigure_cluster_vsan(self.mock_cl_ref, - self.mock_cl_vsan_spec) - get_managed_object_name_mock.assert_called_once_with( - self.mock_cl_ref) + vsan.reconfigure_cluster_vsan(self.mock_cl_ref, self.mock_cl_vsan_spec) + get_managed_object_name_mock.assert_called_once_with(self.mock_cl_ref) def test_get_service_instance_call(self): get_service_instance_from_managed_object_mock = MagicMock() with patch( - 'salt.utils.vmware.get_service_instance_from_managed_object', - get_service_instance_from_managed_object_mock): + "salt.utils.vmware.get_service_instance_from_managed_object", + get_service_instance_from_managed_object_mock, + ): - vsan.reconfigure_cluster_vsan(self.mock_cl_ref, - self.mock_cl_vsan_spec) + vsan.reconfigure_cluster_vsan(self.mock_cl_ref, self.mock_cl_vsan_spec) get_service_instance_from_managed_object_mock.assert_called_once_with( - self.mock_cl_ref) + self.mock_cl_ref + ) def test_get_vsan_cluster_config_system_call(self): - vsan.reconfigure_cluster_vsan(self.mock_cl_ref, - self.mock_cl_vsan_spec) + vsan.reconfigure_cluster_vsan(self.mock_cl_ref, self.mock_cl_vsan_spec) self.mock_get_vsan_conf_sys.assert_called_once_with(self.mock_si) def test_cluster_reconfig_call(self): - vsan.reconfigure_cluster_vsan(self.mock_cl_ref, - self.mock_cl_vsan_spec) + vsan.reconfigure_cluster_vsan(self.mock_cl_ref, self.mock_cl_vsan_spec) self.mock_cl_reconf.assert_called_once_with( - self.mock_cl_ref, self.mock_cl_vsan_spec) + self.mock_cl_ref, self.mock_cl_vsan_spec + ) def test_cluster_reconfig_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - with patch('salt.utils.vsan.get_vsan_cluster_config_system', - MagicMock(return_value=MagicMock( - VsanClusterReconfig=MagicMock(side_effect=exc)))): + exc.privilegeId = "Fake privilege" + with patch( + "salt.utils.vsan.get_vsan_cluster_config_system", + MagicMock( + return_value=MagicMock(VsanClusterReconfig=MagicMock(side_effect=exc)) + ), + ): with self.assertRaises(VMwareApiError) as excinfo: - vsan.reconfigure_cluster_vsan(self.mock_cl_ref, - self.mock_cl_vsan_spec) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + vsan.reconfigure_cluster_vsan(self.mock_cl_ref, self.mock_cl_vsan_spec) + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_cluster_reconfig_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - with patch('salt.utils.vsan.get_vsan_cluster_config_system', - MagicMock(return_value=MagicMock( - VsanClusterReconfig=MagicMock(side_effect=exc)))): + exc.msg = "VimFault msg" + with patch( + "salt.utils.vsan.get_vsan_cluster_config_system", + MagicMock( + return_value=MagicMock(VsanClusterReconfig=MagicMock(side_effect=exc)) + ), + ): with self.assertRaises(VMwareApiError) as excinfo: - vsan.reconfigure_cluster_vsan(self.mock_cl_ref, - self.mock_cl_vsan_spec) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + vsan.reconfigure_cluster_vsan(self.mock_cl_ref, self.mock_cl_vsan_spec) + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_cluster_reconfig_raises_vmodl_runtime_error(self): exc = vmodl.RuntimeFault() - exc.msg = 'VimRuntime msg' - with patch('salt.utils.vsan.get_vsan_cluster_config_system', - MagicMock(return_value=MagicMock( - VsanClusterReconfig=MagicMock(side_effect=exc)))): + exc.msg = "VimRuntime msg" + with patch( + "salt.utils.vsan.get_vsan_cluster_config_system", + MagicMock( + return_value=MagicMock(VsanClusterReconfig=MagicMock(side_effect=exc)) + ), + ): with self.assertRaises(VMwareRuntimeError) as excinfo: - vsan.reconfigure_cluster_vsan(self.mock_cl_ref, - self.mock_cl_vsan_spec) - self.assertEqual(excinfo.exception.strerror, 'VimRuntime msg') + vsan.reconfigure_cluster_vsan(self.mock_cl_ref, self.mock_cl_vsan_spec) + self.assertEqual(excinfo.exception.strerror, "VimRuntime msg") def test__wait_for_tasks_call(self): mock_wait_for_tasks = MagicMock() - with patch('salt.utils.vsan._wait_for_tasks', mock_wait_for_tasks): - vsan.reconfigure_cluster_vsan(self.mock_cl_ref, - self.mock_cl_vsan_spec) - mock_wait_for_tasks.assert_called_once_with([self.mock_task], - self.mock_si) + with patch("salt.utils.vsan._wait_for_tasks", mock_wait_for_tasks): + vsan.reconfigure_cluster_vsan(self.mock_cl_ref, self.mock_cl_vsan_spec) + mock_wait_for_tasks.assert_called_once_with([self.mock_task], self.mock_si) -@skipIf(not HAS_PYVMOMI, 'The \'pyvmomi\' library is missing') -@skipIf(not HAS_PYVSAN, 'The \'vsan\' ext library is missing') +@skipIf(not HAS_PYVMOMI, "The 'pyvmomi' library is missing") +@skipIf(not HAS_PYVSAN, "The 'vsan' ext library is missing") class _WaitForTasks(TestCase, LoaderModuleMockMixin): - '''Tests for salt.utils.vsan._wait_for_tasks''' + """Tests for salt.utils.vsan._wait_for_tasks""" + def setup_loader_modules(self): - return {vsan: { - '__virtual__': MagicMock(return_value='vsan')}} + return {vsan: {"__virtual__": MagicMock(return_value="vsan")}} def setUp(self): self.mock_si = MagicMock() self.mock_tasks = MagicMock() - patches = (('salt.utils.vsan.vsanapiutils.WaitForTasks', MagicMock()),) + patches = (("salt.utils.vsan.vsanapiutils.WaitForTasks", MagicMock()),) for mod, mock in patches: patcher = patch(mod, mock) patcher.start() self.addCleanup(patcher.stop) def tearDown(self): - for attr in ('mock_si', 'mock_tasks'): + for attr in ("mock_si", "mock_tasks"): delattr(self, attr) def test_wait_for_tasks_call(self): mock_wait_for_tasks = MagicMock() - with patch('salt.utils.vsan.vsanapiutils.WaitForTasks', - mock_wait_for_tasks): + with patch("salt.utils.vsan.vsanapiutils.WaitForTasks", mock_wait_for_tasks): vsan._wait_for_tasks(self.mock_tasks, self.mock_si) - mock_wait_for_tasks.assert_called_once_with(self.mock_tasks, - self.mock_si) + mock_wait_for_tasks.assert_called_once_with(self.mock_tasks, self.mock_si) def test_wait_for_tasks_raises_no_permission(self): exc = vim.fault.NoPermission() - exc.privilegeId = 'Fake privilege' - with patch('salt.utils.vsan.vsanapiutils.WaitForTasks', - MagicMock(side_effect=exc)): + exc.privilegeId = "Fake privilege" + with patch( + "salt.utils.vsan.vsanapiutils.WaitForTasks", MagicMock(side_effect=exc) + ): with self.assertRaises(VMwareApiError) as excinfo: vsan._wait_for_tasks(self.mock_tasks, self.mock_si) - self.assertEqual(excinfo.exception.strerror, - 'Not enough permissions. Required privilege: ' - 'Fake privilege') + self.assertEqual( + excinfo.exception.strerror, + "Not enough permissions. Required privilege: " "Fake privilege", + ) def test_wait_for_tasks_raises_vim_fault(self): exc = vim.fault.VimFault() - exc.msg = 'VimFault msg' - with patch('salt.utils.vsan.vsanapiutils.WaitForTasks', - MagicMock(side_effect=exc)): + exc.msg = "VimFault msg" + with patch( + "salt.utils.vsan.vsanapiutils.WaitForTasks", MagicMock(side_effect=exc) + ): with self.assertRaises(VMwareApiError) as excinfo: vsan._wait_for_tasks(self.mock_tasks, self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimFault msg') + self.assertEqual(excinfo.exception.strerror, "VimFault msg") def test_wait_for_tasks_raises_vmodl_runtime_error(self): exc = vmodl.RuntimeFault() - exc.msg = 'VimRuntime msg' - with patch('salt.utils.vsan.vsanapiutils.WaitForTasks', - MagicMock(side_effect=exc)): + exc.msg = "VimRuntime msg" + with patch( + "salt.utils.vsan.vsanapiutils.WaitForTasks", MagicMock(side_effect=exc) + ): with self.assertRaises(VMwareRuntimeError) as excinfo: vsan._wait_for_tasks(self.mock_tasks, self.mock_si) - self.assertEqual(excinfo.exception.strerror, 'VimRuntime msg') + self.assertEqual(excinfo.exception.strerror, "VimRuntime msg") diff --git a/tests/unit/utils/test_vt.py b/tests/unit/utils/test_vt.py index 3a0fb664bf4..5868bb1c45d 100644 --- a/tests/unit/utils/test_vt.py +++ b/tests/unit/utils/test_vt.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Pedro Algarvio (pedro@algarvio.me) @@ -7,19 +7,17 @@ ~~~~~~~~~~~~~~~~~~~~~~~~ VirtualTerminal tests -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals + import os -import sys import random import subprocess +import sys import time -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf - # Import Salt libs import salt.utils.files import salt.utils.platform @@ -28,14 +26,19 @@ import salt.utils.vt # Import 3rd-party libs from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf + class VTTestCase(TestCase): - - @skipIf(True, 'Disabled until we can figure out why this fails when whole test suite runs.') + @skipIf( + True, + "Disabled until we can figure out why this fails when whole test suite runs.", + ) def test_vt_size(self): - '''Confirm that the terminal size is being set''' + """Confirm that the terminal size is being set""" if not sys.stdin.isatty(): - self.skipTest('Not attached to a TTY. The test would fail.') + self.skipTest("Not attached to a TTY. The test would fail.") cols = random.choice(range(80, 250)) terminal = salt.utils.vt.Terminal( 'echo "Foo!"', @@ -43,31 +46,32 @@ class VTTestCase(TestCase): cols=cols, rows=24, stream_stdout=False, - stream_stderr=False + stream_stderr=False, ) # First the assertion - self.assertEqual( - terminal.getwinsize(), (24, cols) - ) + self.assertEqual(terminal.getwinsize(), (24, cols)) # Then wait for the terminal child to exit terminal.wait() terminal.close() - @skipIf(True, 'Disabled until we can find out why this kills the tests suite with an exit code of 134') + @skipIf( + True, + "Disabled until we can find out why this kills the tests suite with an exit code of 134", + ) def test_issue_10404_ptys_not_released(self): n_executions = 15 def current_pty_count(): # Get current number of PTY's try: - if os.path.exists('/proc/sys/kernel/pty/nr'): - with salt.utils.files.fopen('/proc/sys/kernel/pty/nr') as fh_: + if os.path.exists("/proc/sys/kernel/pty/nr"): + with salt.utils.files.fopen("/proc/sys/kernel/pty/nr") as fh_: return int(fh_.read().strip()) proc = subprocess.Popen( - 'sysctl -a 2> /dev/null | grep pty.nr | awk \'{print $3}\'', + "sysctl -a 2> /dev/null | grep pty.nr | awk '{print $3}'", shell=True, - stdout=subprocess.PIPE + stdout=subprocess.PIPE, ) stdout, _ = proc.communicate() return int(stdout.strip()) @@ -75,28 +79,30 @@ class VTTestCase(TestCase): if salt.utils.platform.is_darwin(): # We're unable to findout how many PTY's are open self.skipTest( - 'Unable to find out how many PTY\'s are open on Darwin - ' - 'Skipping for now' + "Unable to find out how many PTY's are open on Darwin - " + "Skipping for now" ) - self.fail('Unable to find out how many PTY\'s are open') + self.fail("Unable to find out how many PTY's are open") nr_ptys = current_pty_count() # Using context manager's for idx in range(0, nr_ptys + n_executions): try: - with salt.utils.vt.Terminal('echo "Run {0}"'.format(idx), - shell=True, - stream_stdout=False, - stream_stderr=False) as terminal: + with salt.utils.vt.Terminal( + 'echo "Run {0}"'.format(idx), + shell=True, + stream_stdout=False, + stream_stderr=False, + ) as terminal: terminal.wait() try: - if current_pty_count() > (nr_ptys + (n_executions/2)): - self.fail('VT is not cleaning up PTY\'s') + if current_pty_count() > (nr_ptys + (n_executions / 2)): + self.fail("VT is not cleaning up PTY's") except (ValueError, OSError, IOError): - self.fail('Unable to find out how many PTY\'s are open') + self.fail("Unable to find out how many PTY's are open") except Exception as exc: # pylint: disable=broad-except - if 'out of pty devices' in str(exc): + if "out of pty devices" in str(exc): # We're not cleaning up raise # We're pushing the system resources, let's keep going @@ -105,31 +111,32 @@ class VTTestCase(TestCase): # Not using context manager's for idx in range(0, nr_ptys + n_executions): try: - terminal = salt.utils.vt.Terminal('echo "Run {0}"'.format(idx), - shell=True, - stream_stdout=False, - stream_stderr=False) + terminal = salt.utils.vt.Terminal( + 'echo "Run {0}"'.format(idx), + shell=True, + stream_stdout=False, + stream_stderr=False, + ) terminal.wait() try: - if current_pty_count() > (nr_ptys + (n_executions/2)): - self.fail('VT is not cleaning up PTY\'s') + if current_pty_count() > (nr_ptys + (n_executions / 2)): + self.fail("VT is not cleaning up PTY's") except (ValueError, OSError, IOError): - self.fail('Unable to find out how many PTY\'s are open') + self.fail("Unable to find out how many PTY's are open") except Exception as exc: # pylint: disable=broad-except - if 'out of pty devices' in str(exc): + if "out of pty devices" in str(exc): # We're not cleaning up raise # We're pushing the system resources, let's keep going continue - @skipIf(True, 'Disabled until we can figure out how to make this more reliable.') + @skipIf(True, "Disabled until we can figure out how to make this more reliable.") def test_isalive_while_theres_data_to_read(self): - expected_data = 'Alive!\n' - term = salt.utils.vt.Terminal('echo "Alive!"', - shell=True, - stream_stdout=False, - stream_stderr=False) - buffer_o = buffer_e = '' + expected_data = "Alive!\n" + term = salt.utils.vt.Terminal( + 'echo "Alive!"', shell=True, stream_stdout=False, stream_stderr=False + ) + buffer_o = buffer_e = "" try: while term.has_unread_data: stdout, stderr = term.recv() @@ -152,12 +159,11 @@ class VTTestCase(TestCase): finally: term.close(terminate=True, kill=True) - expected_data = 'Alive!\n' - term = salt.utils.vt.Terminal('echo "Alive!" 1>&2', - shell=True, - stream_stdout=False, - stream_stderr=False) - buffer_o = buffer_e = '' + expected_data = "Alive!\n" + term = salt.utils.vt.Terminal( + 'echo "Alive!" 1>&2', shell=True, stream_stdout=False, stream_stderr=False + ) + buffer_o = buffer_e = "" try: while term.has_unread_data: stdout, stderr = term.recv() @@ -180,12 +186,14 @@ class VTTestCase(TestCase): finally: term.close(terminate=True, kill=True) - expected_data = 'Alive!\nAlive!\n' - term = salt.utils.vt.Terminal('echo "Alive!"; sleep 5; echo "Alive!"', - shell=True, - stream_stdout=False, - stream_stderr=False) - buffer_o = buffer_e = '' + expected_data = "Alive!\nAlive!\n" + term = salt.utils.vt.Terminal( + 'echo "Alive!"; sleep 5; echo "Alive!"', + shell=True, + stream_stdout=False, + stream_stderr=False, + ) + buffer_o = buffer_e = "" try: while term.has_unread_data: stdout, stderr = term.recv() diff --git a/tests/unit/utils/test_win_dacl.py b/tests/unit/utils/test_win_dacl.py index 49698217f99..ed6e5882610 100644 --- a/tests/unit/utils/test_win_dacl.py +++ b/tests/unit/utils/test_win_dacl.py @@ -1,399 +1,502 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import os import tempfile -# Import Salt Testing Libs -from tests.support.helpers import destructiveTest, generate_random_name, patch -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase, skipIf - # Import Salt Libs import salt.utils.platform import salt.utils.win_dacl as win_dacl import salt.utils.win_reg as win_reg +# Import Salt Testing Libs +from tests.support.helpers import destructiveTest, generate_random_name, patch +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.unit import TestCase, skipIf + try: import pywintypes import win32security + HAS_WIN32 = True except ImportError: HAS_WIN32 = False -FAKE_KEY = 'SOFTWARE\\{0}'.format(generate_random_name('SaltTesting-')) +FAKE_KEY = "SOFTWARE\\{0}".format(generate_random_name("SaltTesting-")) -@skipIf(not HAS_WIN32, 'Requires pywin32') -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not HAS_WIN32, "Requires pywin32") +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinDaclTestCase(TestCase): - ''' + """ Test cases for salt.utils.win_dacl in the registry - ''' + """ + def test_get_sid_string(self): - ''' + """ Validate getting a pysid object from a name - ''' - sid_obj = win_dacl.get_sid('Administrators') - self.assertTrue( - isinstance(sid_obj, pywintypes.SIDType)) + """ + sid_obj = win_dacl.get_sid("Administrators") + self.assertTrue(isinstance(sid_obj, pywintypes.SIDType)) self.assertEqual( - win32security.LookupAccountSid(None, sid_obj)[0], - 'Administrators') + win32security.LookupAccountSid(None, sid_obj)[0], "Administrators" + ) def test_get_sid_sid_string(self): - ''' + """ Validate getting a pysid object from a SID string - ''' - sid_obj = win_dacl.get_sid('S-1-5-32-544') + """ + sid_obj = win_dacl.get_sid("S-1-5-32-544") self.assertTrue(isinstance(sid_obj, pywintypes.SIDType)) - self.assertEqual(win32security.LookupAccountSid(None, sid_obj)[0], - 'Administrators') + self.assertEqual( + win32security.LookupAccountSid(None, sid_obj)[0], "Administrators" + ) def test_get_sid_string_name(self): - ''' + """ Validate getting a pysid object from a SID string - ''' - sid_obj = win_dacl.get_sid('Administrators') + """ + sid_obj = win_dacl.get_sid("Administrators") self.assertTrue(isinstance(sid_obj, pywintypes.SIDType)) - self.assertEqual(win_dacl.get_sid_string(sid_obj), 'S-1-5-32-544') + self.assertEqual(win_dacl.get_sid_string(sid_obj), "S-1-5-32-544") def test_get_sid_string_none(self): - ''' + """ Validate getting a pysid object from None (NULL SID) - ''' + """ sid_obj = win_dacl.get_sid(None) self.assertTrue(isinstance(sid_obj, pywintypes.SIDType)) - self.assertEqual(win_dacl.get_sid_string(sid_obj), 'S-1-0-0') + self.assertEqual(win_dacl.get_sid_string(sid_obj), "S-1-0-0") def test_get_name(self): - ''' + """ Get the name - ''' + """ # Case - self.assertEqual(win_dacl.get_name('adMiniStrAtorS'), 'Administrators') + self.assertEqual(win_dacl.get_name("adMiniStrAtorS"), "Administrators") # SID String - self.assertEqual(win_dacl.get_name('S-1-5-32-544'), 'Administrators') + self.assertEqual(win_dacl.get_name("S-1-5-32-544"), "Administrators") # SID Object - sid_obj = win_dacl.get_sid('Administrators') + sid_obj = win_dacl.get_sid("Administrators") self.assertTrue(isinstance(sid_obj, pywintypes.SIDType)) - self.assertEqual(win_dacl.get_name(sid_obj), 'Administrators') + self.assertEqual(win_dacl.get_name(sid_obj), "Administrators") -@skipIf(not HAS_WIN32, 'Requires pywin32') -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not HAS_WIN32, "Requires pywin32") +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinDaclRegTestCase(TestCase, LoaderModuleMockMixin): - obj_name = 'HKLM\\' + FAKE_KEY - obj_type = 'registry' - ''' + obj_name = "HKLM\\" + FAKE_KEY + obj_type = "registry" + """ Test cases for salt.utils.win_dacl in the registry - ''' + """ + def setup_loader_modules(self): return {win_dacl: {}} def setUp(self): - self.assertTrue(win_reg.set_value(hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + self.assertTrue( + win_reg.set_value( + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) def tearDown(self): - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_owner(self): - ''' + """ Test the set_owner function Test the get_owner function - ''' - self.assertTrue(win_dacl.set_owner(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type)) - self.assertEqual(win_dacl.get_owner(obj_name=self.obj_name, - obj_type=self.obj_type), - 'Backup Operators') + """ + self.assertTrue( + win_dacl.set_owner( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ) + ) + self.assertEqual( + win_dacl.get_owner(obj_name=self.obj_name, obj_type=self.obj_type), + "Backup Operators", + ) @destructiveTest def test_primary_group(self): - ''' + """ Test the set_primary_group function Test the get_primary_group function - ''' - self.assertTrue(win_dacl.set_primary_group(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type)) - self.assertEqual(win_dacl.get_primary_group(obj_name=self.obj_name, - obj_type=self.obj_type), - 'Backup Operators') + """ + self.assertTrue( + win_dacl.set_primary_group( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ) + ) + self.assertEqual( + win_dacl.get_primary_group(obj_name=self.obj_name, obj_type=self.obj_type), + "Backup Operators", + ) @destructiveTest def test_set_permissions(self): - ''' + """ Test the set_permissions function - ''' - self.assertTrue(win_dacl.set_permissions(obj_name=self.obj_name, - principal='Backup Operators', - permissions='full_control', - access_mode='grant', - obj_type=self.obj_type, - reset_perms=False, - protected=None)) + """ + self.assertTrue( + win_dacl.set_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + permissions="full_control", + access_mode="grant", + obj_type=self.obj_type, + reset_perms=False, + protected=None, + ) + ) expected = { - 'Not Inherited': { - 'Backup Operators': { - 'grant': { - 'applies to': 'This key and subkeys', - 'permissions': 'Full Control'}}}} - self.assertEqual(win_dacl.get_permissions(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - expected) + "Not Inherited": { + "Backup Operators": { + "grant": { + "applies to": "This key and subkeys", + "permissions": "Full Control", + } + } + } + } + self.assertEqual( + win_dacl.get_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ), + expected, + ) @destructiveTest def test_get_permissions(self): - ''' + """ Test the get_permissions function - ''' - self.assertTrue(win_dacl.set_permissions(obj_name=self.obj_name, - principal='Backup Operators', - permissions='full_control', - access_mode='grant', - obj_type=self.obj_type, - reset_perms=False, - protected=None)) + """ + self.assertTrue( + win_dacl.set_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + permissions="full_control", + access_mode="grant", + obj_type=self.obj_type, + reset_perms=False, + protected=None, + ) + ) expected = { - 'Not Inherited': { - 'Backup Operators': { - 'grant': { - 'applies to': 'This key and subkeys', - 'permissions': 'Full Control'}}}} - self.assertEqual(win_dacl.get_permissions(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - expected) + "Not Inherited": { + "Backup Operators": { + "grant": { + "applies to": "This key and subkeys", + "permissions": "Full Control", + } + } + } + } + self.assertEqual( + win_dacl.get_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ), + expected, + ) @destructiveTest def test_has_permission(self): - ''' + """ Test the has_permission function - ''' - self.assertTrue(win_dacl.set_permissions(obj_name=self.obj_name, - principal='Backup Operators', - permissions='full_control', - access_mode='grant', - obj_type=self.obj_type, - reset_perms=False, - protected=None)) + """ + self.assertTrue( + win_dacl.set_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + permissions="full_control", + access_mode="grant", + obj_type=self.obj_type, + reset_perms=False, + protected=None, + ) + ) # Test has_permission exact - self.assertTrue(win_dacl.has_permission(obj_name=self.obj_name, - principal='Backup Operators', - permission='full_control', - access_mode='grant', - obj_type=self.obj_type, - exact=True)) + self.assertTrue( + win_dacl.has_permission( + obj_name=self.obj_name, + principal="Backup Operators", + permission="full_control", + access_mode="grant", + obj_type=self.obj_type, + exact=True, + ) + ) # Test has_permission contains - self.assertTrue(win_dacl.has_permission(obj_name=self.obj_name, - principal='Backup Operators', - permission='read', - access_mode='grant', - obj_type=self.obj_type, - exact=False)) + self.assertTrue( + win_dacl.has_permission( + obj_name=self.obj_name, + principal="Backup Operators", + permission="read", + access_mode="grant", + obj_type=self.obj_type, + exact=False, + ) + ) @destructiveTest def test_rm_permissions(self): - ''' + """ Test the rm_permissions function - ''' - self.assertTrue(win_dacl.set_permissions(obj_name=self.obj_name, - principal='Backup Operators', - permissions='full_control', - access_mode='grant', - obj_type=self.obj_type, - reset_perms=False, - protected=None)) - self.assertTrue(win_dacl.rm_permissions(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type)) - self.assertEqual(win_dacl.get_permissions(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - {}) + """ + self.assertTrue( + win_dacl.set_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + permissions="full_control", + access_mode="grant", + obj_type=self.obj_type, + reset_perms=False, + protected=None, + ) + ) + self.assertTrue( + win_dacl.rm_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ) + ) + self.assertEqual( + win_dacl.get_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ), + {}, + ) @destructiveTest def test_inheritance(self): - ''' + """ Test the set_inheritance function Test the get_inheritance function - ''' - self.assertTrue(win_dacl.set_inheritance(obj_name=self.obj_name, - enabled=True, - obj_type=self.obj_type, - clear=False)) - self.assertTrue(win_dacl.get_inheritance(obj_name=self.obj_name, - obj_type=self.obj_type)) - self.assertTrue(win_dacl.set_inheritance(obj_name=self.obj_name, - enabled=False, - obj_type=self.obj_type, - clear=False)) - self.assertFalse(win_dacl.get_inheritance(obj_name=self.obj_name, - obj_type=self.obj_type)) + """ + self.assertTrue( + win_dacl.set_inheritance( + obj_name=self.obj_name, + enabled=True, + obj_type=self.obj_type, + clear=False, + ) + ) + self.assertTrue( + win_dacl.get_inheritance(obj_name=self.obj_name, obj_type=self.obj_type) + ) + self.assertTrue( + win_dacl.set_inheritance( + obj_name=self.obj_name, + enabled=False, + obj_type=self.obj_type, + clear=False, + ) + ) + self.assertFalse( + win_dacl.get_inheritance(obj_name=self.obj_name, obj_type=self.obj_type) + ) @destructiveTest def test_check_perms(self): - ''' + """ Test the check_perms function - ''' + """ with patch.dict(win_dacl.__opts__, {"test": False}): result = win_dacl.check_perms( obj_name=self.obj_name, obj_type=self.obj_type, ret={}, - owner='Users', - grant_perms={'Backup Operators': {'perms': 'read'}}, - deny_perms={'Backup Operators': {'perms': ['delete']}, - 'NETWORK SERVICE': {'perms': ['delete', - 'set_value', - 'write_dac', - 'write_owner']}}, + owner="Users", + grant_perms={"Backup Operators": {"perms": "read"}}, + deny_perms={ + "Backup Operators": {"perms": ["delete"]}, + "NETWORK SERVICE": { + "perms": ["delete", "set_value", "write_dac", "write_owner"] + }, + }, inheritance=True, - reset=False) + reset=False, + ) - expected = {'changes': {'owner': 'Users', - 'perms': {'Backup Operators': {'grant': 'read', - 'deny': ['delete']}, - 'NETWORK SERVICE': {'deny': ['delete', - 'set_value', - 'write_dac', - 'write_owner']}}}, - 'comment': '', - 'name': self.obj_name, - 'result': True} + expected = { + "changes": { + "owner": "Users", + "perms": { + "Backup Operators": {"grant": "read", "deny": ["delete"]}, + "NETWORK SERVICE": { + "deny": ["delete", "set_value", "write_dac", "write_owner"] + }, + }, + }, + "comment": "", + "name": self.obj_name, + "result": True, + } self.assertDictEqual(result, expected) expected = { - 'Not Inherited': { - 'Backup Operators': { - 'grant': { - 'applies to': 'This key and subkeys', - 'permissions': 'Read'}, - 'deny': { - 'applies to': 'This key and subkeys', - 'permissions': ['Delete']}}}} + "Not Inherited": { + "Backup Operators": { + "grant": { + "applies to": "This key and subkeys", + "permissions": "Read", + }, + "deny": { + "applies to": "This key and subkeys", + "permissions": ["Delete"], + }, + } + } + } self.assertDictEqual( win_dacl.get_permissions( obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - expected) + principal="Backup Operators", + obj_type=self.obj_type, + ), + expected, + ) expected = { - 'Not Inherited': { - 'NETWORK SERVICE': { - 'deny': { - 'applies to': 'This key and subkeys', - 'permissions': ['Delete', - 'Set Value', - 'Write DAC', - 'Write Owner']}}}} + "Not Inherited": { + "NETWORK SERVICE": { + "deny": { + "applies to": "This key and subkeys", + "permissions": [ + "Delete", + "Set Value", + "Write DAC", + "Write Owner", + ], + } + } + } + } self.assertDictEqual( win_dacl.get_permissions( obj_name=self.obj_name, - principal='NETWORK SERVICE', - obj_type=self.obj_type), - expected) + principal="NETWORK SERVICE", + obj_type=self.obj_type, + ), + expected, + ) self.assertEqual( - win_dacl.get_owner( - obj_name=self.obj_name, - obj_type=self.obj_type), - 'Users') + win_dacl.get_owner(obj_name=self.obj_name, obj_type=self.obj_type), "Users" + ) @destructiveTest def test_check_perms_test_true(self): - ''' + """ Test the check_perms function - ''' + """ with patch.dict(win_dacl.__opts__, {"test": True}): result = win_dacl.check_perms( obj_name=self.obj_name, obj_type=self.obj_type, ret=None, - owner='Users', - grant_perms={'Backup Operators': {'perms': 'read'}}, - deny_perms={'NETWORK SERVICE': {'perms': ['delete', - 'set_value', - 'write_dac', - 'write_owner']}, - 'Backup Operators': {'perms': ['delete']}}, + owner="Users", + grant_perms={"Backup Operators": {"perms": "read"}}, + deny_perms={ + "NETWORK SERVICE": { + "perms": ["delete", "set_value", "write_dac", "write_owner"] + }, + "Backup Operators": {"perms": ["delete"]}, + }, inheritance=True, - reset=False) + reset=False, + ) expected = { - 'changes': {'owner': 'Users', - 'perms': {'Backup Operators': {'grant': 'read', - 'deny': ['delete']}, - 'NETWORK SERVICE': {'deny': ['delete', - 'set_value', - 'write_dac', - 'write_owner']}}}, - 'comment': '', - 'name': self.obj_name, - 'result': None} + "changes": { + "owner": "Users", + "perms": { + "Backup Operators": {"grant": "read", "deny": ["delete"]}, + "NETWORK SERVICE": { + "deny": ["delete", "set_value", "write_dac", "write_owner"] + }, + }, + }, + "comment": "", + "name": self.obj_name, + "result": None, + } self.assertDictEqual(result, expected) self.assertNotEqual( - win_dacl.get_owner( - obj_name=self.obj_name, - obj_type=self.obj_type), - 'Users') + win_dacl.get_owner(obj_name=self.obj_name, obj_type=self.obj_type), "Users" + ) self.assertEqual( win_dacl.get_permissions( obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - {}) + principal="Backup Operators", + obj_type=self.obj_type, + ), + {}, + ) def test_set_perms(self): - ''' + """ Test the set_perms function - ''' + """ result = win_dacl.set_perms( obj_name=self.obj_name, obj_type=self.obj_type, - grant_perms={'Backup Operators': {'perms': 'read'}}, - deny_perms={'NETWORK SERVICE': {'perms': ['delete', - 'set_value', - 'write_dac', - 'write_owner']}}, + grant_perms={"Backup Operators": {"perms": "read"}}, + deny_perms={ + "NETWORK SERVICE": { + "perms": ["delete", "set_value", "write_dac", "write_owner"] + } + }, inheritance=True, - reset=False) + reset=False, + ) expected = { - 'deny': {'NETWORK SERVICE': {'perms': ['delete', - 'set_value', - 'write_dac', - 'write_owner']}}, - 'grant': {'Backup Operators': {'perms': 'read'}}} + "deny": { + "NETWORK SERVICE": { + "perms": ["delete", "set_value", "write_dac", "write_owner"] + } + }, + "grant": {"Backup Operators": {"perms": "read"}}, + } self.assertDictEqual(result, expected) -@skipIf(not HAS_WIN32, 'Requires pywin32') -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not HAS_WIN32, "Requires pywin32") +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinDaclFileTestCase(TestCase, LoaderModuleMockMixin): - obj_name = '' - obj_type = 'file' - ''' + obj_name = "" + obj_type = "file" + """ Test cases for salt.utils.win_dacl in the file system - ''' + """ + def setup_loader_modules(self): return {win_dacl: {}} def setUp(self): - config_file_fd, self.obj_name = tempfile.mkstemp(prefix='SaltTesting-', - suffix='txt') + config_file_fd, self.obj_name = tempfile.mkstemp( + prefix="SaltTesting-", suffix="txt" + ) os.close(config_file_fd) def tearDown(self): @@ -401,279 +504,394 @@ class WinDaclFileTestCase(TestCase, LoaderModuleMockMixin): @destructiveTest def test_owner(self): - ''' + """ Test the set_owner function Test the get_owner function - ''' - self.assertTrue(win_dacl.set_owner(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type)) - self.assertEqual(win_dacl.get_owner(obj_name=self.obj_name, - obj_type=self.obj_type), - 'Backup Operators') + """ + self.assertTrue( + win_dacl.set_owner( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ) + ) + self.assertEqual( + win_dacl.get_owner(obj_name=self.obj_name, obj_type=self.obj_type), + "Backup Operators", + ) @destructiveTest def test_primary_group(self): - ''' + """ Test the set_primary_group function Test the get_primary_group function - ''' - self.assertTrue(win_dacl.set_primary_group(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type)) - self.assertEqual(win_dacl.get_primary_group(obj_name=self.obj_name, - obj_type=self.obj_type), - 'Backup Operators') + """ + self.assertTrue( + win_dacl.set_primary_group( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ) + ) + self.assertEqual( + win_dacl.get_primary_group(obj_name=self.obj_name, obj_type=self.obj_type), + "Backup Operators", + ) @destructiveTest def test_set_permissions(self): - ''' + """ Test the set_permissions function - ''' - self.assertTrue(win_dacl.set_permissions(obj_name=self.obj_name, - principal='Backup Operators', - permissions='full_control', - access_mode='grant', - obj_type=self.obj_type, - reset_perms=False, - protected=None)) + """ + self.assertTrue( + win_dacl.set_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + permissions="full_control", + access_mode="grant", + obj_type=self.obj_type, + reset_perms=False, + protected=None, + ) + ) expected = { - 'Not Inherited': { - 'Backup Operators': { - 'grant': { - 'applies to': 'Not Inherited (file)', - 'permissions': 'Full control'}}}} - self.assertEqual(win_dacl.get_permissions(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - expected) + "Not Inherited": { + "Backup Operators": { + "grant": { + "applies to": "Not Inherited (file)", + "permissions": "Full control", + } + } + } + } + self.assertEqual( + win_dacl.get_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ), + expected, + ) @destructiveTest def test_get_permissions(self): - ''' + """ Test the get_permissions function - ''' - self.assertTrue(win_dacl.set_permissions(obj_name=self.obj_name, - principal='Backup Operators', - permissions='full_control', - access_mode='grant', - obj_type=self.obj_type, - reset_perms=False, - protected=None)) + """ + self.assertTrue( + win_dacl.set_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + permissions="full_control", + access_mode="grant", + obj_type=self.obj_type, + reset_perms=False, + protected=None, + ) + ) expected = { - 'Not Inherited': { - 'Backup Operators': { - 'grant': { - 'applies to': 'Not Inherited (file)', - 'permissions': 'Full control'}}}} - self.assertEqual(win_dacl.get_permissions(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - expected) + "Not Inherited": { + "Backup Operators": { + "grant": { + "applies to": "Not Inherited (file)", + "permissions": "Full control", + } + } + } + } + self.assertEqual( + win_dacl.get_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ), + expected, + ) @destructiveTest def test_has_permission(self): - ''' + """ Test the has_permission function - ''' - self.assertTrue(win_dacl.set_permissions(obj_name=self.obj_name, - principal='Backup Operators', - permissions='full_control', - access_mode='grant', - obj_type=self.obj_type, - reset_perms=False, - protected=None)) + """ + self.assertTrue( + win_dacl.set_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + permissions="full_control", + access_mode="grant", + obj_type=self.obj_type, + reset_perms=False, + protected=None, + ) + ) # Test has_permission exact - self.assertTrue(win_dacl.has_permission(obj_name=self.obj_name, - principal='Backup Operators', - permission='full_control', - access_mode='grant', - obj_type=self.obj_type, - exact=True)) + self.assertTrue( + win_dacl.has_permission( + obj_name=self.obj_name, + principal="Backup Operators", + permission="full_control", + access_mode="grant", + obj_type=self.obj_type, + exact=True, + ) + ) # Test has_permission contains - self.assertTrue(win_dacl.has_permission(obj_name=self.obj_name, - principal='Backup Operators', - permission='read', - access_mode='grant', - obj_type=self.obj_type, - exact=False)) + self.assertTrue( + win_dacl.has_permission( + obj_name=self.obj_name, + principal="Backup Operators", + permission="read", + access_mode="grant", + obj_type=self.obj_type, + exact=False, + ) + ) @destructiveTest def test_rm_permissions(self): - ''' + """ Test the rm_permissions function - ''' - self.assertTrue(win_dacl.set_permissions(obj_name=self.obj_name, - principal='Backup Operators', - permissions='full_control', - access_mode='grant', - obj_type=self.obj_type, - reset_perms=False, - protected=None)) - self.assertTrue(win_dacl.rm_permissions(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type)) - self.assertEqual(win_dacl.get_permissions(obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - {}) + """ + self.assertTrue( + win_dacl.set_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + permissions="full_control", + access_mode="grant", + obj_type=self.obj_type, + reset_perms=False, + protected=None, + ) + ) + self.assertTrue( + win_dacl.rm_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ) + ) + self.assertEqual( + win_dacl.get_permissions( + obj_name=self.obj_name, + principal="Backup Operators", + obj_type=self.obj_type, + ), + {}, + ) @destructiveTest def test_inheritance(self): - ''' + """ Test the set_inheritance function Test the get_inheritance function - ''' - self.assertTrue(win_dacl.set_inheritance(obj_name=self.obj_name, - enabled=True, - obj_type=self.obj_type, - clear=False)) - self.assertTrue(win_dacl.get_inheritance(obj_name=self.obj_name, - obj_type=self.obj_type)) - self.assertTrue(win_dacl.set_inheritance(obj_name=self.obj_name, - enabled=False, - obj_type=self.obj_type, - clear=False)) - self.assertFalse(win_dacl.get_inheritance(obj_name=self.obj_name, - obj_type=self.obj_type)) + """ + self.assertTrue( + win_dacl.set_inheritance( + obj_name=self.obj_name, + enabled=True, + obj_type=self.obj_type, + clear=False, + ) + ) + self.assertTrue( + win_dacl.get_inheritance(obj_name=self.obj_name, obj_type=self.obj_type) + ) + self.assertTrue( + win_dacl.set_inheritance( + obj_name=self.obj_name, + enabled=False, + obj_type=self.obj_type, + clear=False, + ) + ) + self.assertFalse( + win_dacl.get_inheritance(obj_name=self.obj_name, obj_type=self.obj_type) + ) @destructiveTest def test_check_perms(self): - ''' + """ Test the check_perms function - ''' + """ with patch.dict(win_dacl.__opts__, {"test": False}): result = win_dacl.check_perms( obj_name=self.obj_name, obj_type=self.obj_type, ret={}, - owner='Users', - grant_perms={'Backup Operators': {'perms': 'read'}}, - deny_perms={'Backup Operators': {'perms': ['delete']}, - 'NETWORK SERVICE': {'perms': ['delete', - 'change_permissions', - 'write_attributes', - 'write_data']}}, + owner="Users", + grant_perms={"Backup Operators": {"perms": "read"}}, + deny_perms={ + "Backup Operators": {"perms": ["delete"]}, + "NETWORK SERVICE": { + "perms": [ + "delete", + "change_permissions", + "write_attributes", + "write_data", + ] + }, + }, inheritance=True, - reset=False) + reset=False, + ) expected = { - 'changes': {'owner': 'Users', - 'perms': {'Backup Operators': {'grant': 'read', - 'deny': ['delete']}, - 'NETWORK SERVICE': {'deny': ['delete', - 'change_permissions', - 'write_attributes', - 'write_data']}}}, - 'comment': '', - 'name': self.obj_name, - 'result': True} + "changes": { + "owner": "Users", + "perms": { + "Backup Operators": {"grant": "read", "deny": ["delete"]}, + "NETWORK SERVICE": { + "deny": [ + "delete", + "change_permissions", + "write_attributes", + "write_data", + ] + }, + }, + }, + "comment": "", + "name": self.obj_name, + "result": True, + } self.assertDictEqual(result, expected) expected = { - 'Not Inherited': { - 'Backup Operators': { - 'grant': { - 'applies to': 'Not Inherited (file)', - 'permissions': 'Read'}, - 'deny': { - 'applies to': 'Not Inherited (file)', - 'permissions': ['Delete']}}}} + "Not Inherited": { + "Backup Operators": { + "grant": { + "applies to": "Not Inherited (file)", + "permissions": "Read", + }, + "deny": { + "applies to": "Not Inherited (file)", + "permissions": ["Delete"], + }, + } + } + } self.assertDictEqual( win_dacl.get_permissions( obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - expected) + principal="Backup Operators", + obj_type=self.obj_type, + ), + expected, + ) expected = { - 'Not Inherited': { - 'NETWORK SERVICE': { - 'deny': { - 'applies to': 'Not Inherited (file)', - 'permissions': ['Change permissions', - 'Create files / write data', - 'Delete', - 'Write attributes']}}}} + "Not Inherited": { + "NETWORK SERVICE": { + "deny": { + "applies to": "Not Inherited (file)", + "permissions": [ + "Change permissions", + "Create files / write data", + "Delete", + "Write attributes", + ], + } + } + } + } self.assertDictEqual( win_dacl.get_permissions( obj_name=self.obj_name, - principal='NETWORK SERVICE', - obj_type=self.obj_type), - expected) + principal="NETWORK SERVICE", + obj_type=self.obj_type, + ), + expected, + ) self.assertEqual( - win_dacl.get_owner( - obj_name=self.obj_name, - obj_type=self.obj_type), - 'Users') + win_dacl.get_owner(obj_name=self.obj_name, obj_type=self.obj_type), "Users" + ) @destructiveTest def test_check_perms_test_true(self): - ''' + """ Test the check_perms function - ''' + """ with patch.dict(win_dacl.__opts__, {"test": True}): result = win_dacl.check_perms( obj_name=self.obj_name, obj_type=self.obj_type, ret=None, - owner='Users', - grant_perms={'Backup Operators': {'perms': 'read'}}, - deny_perms={'NETWORK SERVICE': {'perms': ['delete', - 'set_value', - 'write_dac', - 'write_owner']}, - 'Backup Operators': {'perms': ['delete']}}, + owner="Users", + grant_perms={"Backup Operators": {"perms": "read"}}, + deny_perms={ + "NETWORK SERVICE": { + "perms": ["delete", "set_value", "write_dac", "write_owner"] + }, + "Backup Operators": {"perms": ["delete"]}, + }, inheritance=True, - reset=False) + reset=False, + ) expected = { - 'changes': {'owner': 'Users', - 'perms': {'Backup Operators': {'grant': 'read', - 'deny': ['delete']}, - 'NETWORK SERVICE': {'deny': ['delete', - 'set_value', - 'write_dac', - 'write_owner']}}}, - 'comment': '', - 'name': self.obj_name, - 'result': None} + "changes": { + "owner": "Users", + "perms": { + "Backup Operators": {"grant": "read", "deny": ["delete"]}, + "NETWORK SERVICE": { + "deny": ["delete", "set_value", "write_dac", "write_owner"] + }, + }, + }, + "comment": "", + "name": self.obj_name, + "result": None, + } self.assertDictEqual(result, expected) self.assertNotEqual( - win_dacl.get_owner( - obj_name=self.obj_name, - obj_type=self.obj_type), - 'Users') + win_dacl.get_owner(obj_name=self.obj_name, obj_type=self.obj_type), "Users" + ) self.assertEqual( win_dacl.get_permissions( obj_name=self.obj_name, - principal='Backup Operators', - obj_type=self.obj_type), - {}) + principal="Backup Operators", + obj_type=self.obj_type, + ), + {}, + ) def test_set_perms(self): - ''' + """ Test the set_perms function - ''' + """ result = win_dacl.set_perms( obj_name=self.obj_name, obj_type=self.obj_type, - grant_perms={'Backup Operators': {'perms': 'read'}}, - deny_perms={'NETWORK SERVICE': {'perms': ['delete', - 'change_permissions', - 'write_attributes', - 'write_data']}}, + grant_perms={"Backup Operators": {"perms": "read"}}, + deny_perms={ + "NETWORK SERVICE": { + "perms": [ + "delete", + "change_permissions", + "write_attributes", + "write_data", + ] + } + }, inheritance=True, - reset=False) + reset=False, + ) expected = { - 'deny': {'NETWORK SERVICE': {'perms': ['delete', - 'change_permissions', - 'write_attributes', - 'write_data']}}, - 'grant': {'Backup Operators': {'perms': 'read'}}} + "deny": { + "NETWORK SERVICE": { + "perms": [ + "delete", + "change_permissions", + "write_attributes", + "write_data", + ] + } + }, + "grant": {"Backup Operators": {"perms": "read"}}, + } self.assertDictEqual(result, expected) diff --git a/tests/unit/utils/test_win_functions.py b/tests/unit/utils/test_win_functions.py index 0cfd1270c64..879ce0a22ab 100644 --- a/tests/unit/utils/test_win_functions.py +++ b/tests/unit/utils/test_win_functions.py @@ -1,141 +1,135 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import patch, MagicMock +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.utils.platform import salt.utils.win_functions as win_functions +from tests.support.mock import MagicMock, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf # Import 3rd Party Libs try: import win32net + HAS_WIN32 = True class WinError(win32net.error): winerror = 0 + except ImportError: HAS_WIN32 = False class WinFunctionsTestCase(TestCase): - ''' + """ Test cases for salt.utils.win_functions - ''' + """ def test_escape_argument_simple(self): - ''' + """ Test to make sure we encode simple arguments correctly - ''' - encoded = win_functions.escape_argument('simple') + """ + encoded = win_functions.escape_argument("simple") - self.assertEqual(encoded, 'simple') + self.assertEqual(encoded, "simple") def test_escape_argument_with_space(self): - ''' + """ Test to make sure we encode arguments containing spaces correctly - ''' - encoded = win_functions.escape_argument('with space') + """ + encoded = win_functions.escape_argument("with space") self.assertEqual(encoded, '^"with space^"') def test_escape_argument_simple_path(self): - ''' + """ Test to make sure we encode simple path arguments correctly - ''' - encoded = win_functions.escape_argument('C:\\some\\path') + """ + encoded = win_functions.escape_argument("C:\\some\\path") - self.assertEqual(encoded, 'C:\\some\\path') + self.assertEqual(encoded, "C:\\some\\path") def test_escape_argument_path_with_space(self): - ''' + """ Test to make sure we encode path arguments containing spaces correctly - ''' - encoded = win_functions.escape_argument('C:\\Some Path\\With Spaces') + """ + encoded = win_functions.escape_argument("C:\\Some Path\\With Spaces") self.assertEqual(encoded, '^"C:\\Some Path\\With Spaces^"') - @skipIf(not salt.utils.platform.is_windows(), - 'WinDLL only available on Windows') + @skipIf(not salt.utils.platform.is_windows(), "WinDLL only available on Windows") def test_broadcast_setting_change(self): - ''' + """ Test to rehash the Environment variables - ''' + """ self.assertTrue(win_functions.broadcast_setting_change()) - @skipIf(not salt.utils.platform.is_windows(), - 'WinDLL only available on Windows') + @skipIf(not salt.utils.platform.is_windows(), "WinDLL only available on Windows") def test_get_user_groups(self): - groups = ['Administrators', 'Users'] - with patch('win32net.NetUserGetLocalGroups', return_value=groups): - ret = win_functions.get_user_groups('Administrator') + groups = ["Administrators", "Users"] + with patch("win32net.NetUserGetLocalGroups", return_value=groups): + ret = win_functions.get_user_groups("Administrator") self.assertListEqual(groups, ret) - @skipIf(not salt.utils.platform.is_windows(), - 'WinDLL only available on Windows') + @skipIf(not salt.utils.platform.is_windows(), "WinDLL only available on Windows") def test_get_user_groups_sid(self): - groups = ['Administrators', 'Users'] - expected = ['S-1-5-32-544', 'S-1-5-32-545'] - with patch('win32net.NetUserGetLocalGroups', return_value=groups): - ret = win_functions.get_user_groups('Administrator', sid=True) + groups = ["Administrators", "Users"] + expected = ["S-1-5-32-544", "S-1-5-32-545"] + with patch("win32net.NetUserGetLocalGroups", return_value=groups): + ret = win_functions.get_user_groups("Administrator", sid=True) self.assertListEqual(expected, ret) - @skipIf(not salt.utils.platform.is_windows(), - 'WinDLL only available on Windows') + @skipIf(not salt.utils.platform.is_windows(), "WinDLL only available on Windows") def test_get_user_groups_system(self): - groups = ['SYSTEM'] - with patch('win32net.NetUserGetLocalGroups', return_value=groups): - ret = win_functions.get_user_groups('SYSTEM') + groups = ["SYSTEM"] + with patch("win32net.NetUserGetLocalGroups", return_value=groups): + ret = win_functions.get_user_groups("SYSTEM") self.assertListEqual(groups, ret) - @skipIf(not salt.utils.platform.is_windows(), - 'WinDLL only available on Windows') - @skipIf(not HAS_WIN32, 'Requires pywin32 libraries') + @skipIf(not salt.utils.platform.is_windows(), "WinDLL only available on Windows") + @skipIf(not HAS_WIN32, "Requires pywin32 libraries") def test_get_user_groups_unavailable_dc(self): - groups = ['Administrators', 'Users'] + groups = ["Administrators", "Users"] win_error = WinError() win_error.winerror = 1722 effect = [win_error, groups] - with patch('win32net.NetUserGetLocalGroups', side_effect=effect): - ret = win_functions.get_user_groups('Administrator') + with patch("win32net.NetUserGetLocalGroups", side_effect=effect): + ret = win_functions.get_user_groups("Administrator") self.assertListEqual(groups, ret) - @skipIf(not salt.utils.platform.is_windows(), - 'WinDLL only available on Windows') - @skipIf(not HAS_WIN32, 'Requires pywin32 libraries') + @skipIf(not salt.utils.platform.is_windows(), "WinDLL only available on Windows") + @skipIf(not HAS_WIN32, "Requires pywin32 libraries") def test_get_user_groups_unknown_dc(self): - groups = ['Administrators', 'Users'] + groups = ["Administrators", "Users"] win_error = WinError() win_error.winerror = 2453 effect = [win_error, groups] - with patch('win32net.NetUserGetLocalGroups', side_effect=effect): - ret = win_functions.get_user_groups('Administrator') + with patch("win32net.NetUserGetLocalGroups", side_effect=effect): + ret = win_functions.get_user_groups("Administrator") self.assertListEqual(groups, ret) - @skipIf(not salt.utils.platform.is_windows(), - 'WinDLL only available on Windows') - @skipIf(not HAS_WIN32, 'Requires pywin32 libraries') + @skipIf(not salt.utils.platform.is_windows(), "WinDLL only available on Windows") + @skipIf(not HAS_WIN32, "Requires pywin32 libraries") def test_get_user_groups_missing_permission(self): - groups = ['Administrators', 'Users'] + groups = ["Administrators", "Users"] win_error = WinError() win_error.winerror = 5 effect = [win_error, groups] - with patch('win32net.NetUserGetLocalGroups', side_effect=effect): - ret = win_functions.get_user_groups('Administrator') + with patch("win32net.NetUserGetLocalGroups", side_effect=effect): + ret = win_functions.get_user_groups("Administrator") self.assertListEqual(groups, ret) - @skipIf(not salt.utils.platform.is_windows(), - 'WinDLL only available on Windows') - @skipIf(not HAS_WIN32, 'Requires pywin32 libraries') + @skipIf(not salt.utils.platform.is_windows(), "WinDLL only available on Windows") + @skipIf(not HAS_WIN32, "Requires pywin32 libraries") def test_get_user_groups_error(self): win_error = WinError() win_error.winerror = 1927 mock_error = MagicMock(side_effect=win_error) - with patch('win32net.NetUserGetLocalGroups', side_effect=mock_error): + with patch("win32net.NetUserGetLocalGroups", side_effect=mock_error): with self.assertRaises(WinError): - win_functions.get_user_groups('Administrator') + win_functions.get_user_groups("Administrator") diff --git a/tests/unit/utils/test_win_lgpo_auditpol.py b/tests/unit/utils/test_win_lgpo_auditpol.py index 6c627fa6d34..26b56425536 100644 --- a/tests/unit/utils/test_win_lgpo_auditpol.py +++ b/tests/unit/utils/test_win_lgpo_auditpol.py @@ -1,43 +1,43 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function -import random +from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing Libs -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.mock import patch, MagicMock -from tests.support.unit import TestCase, skipIf +import random # Import Salt Libs import salt.modules.cmdmod import salt.utils.platform import salt.utils.win_lgpo_auditpol as win_lgpo_auditpol -settings = ['No Auditing', 'Success', 'Failure', 'Success and Failure'] +# Import Salt Testing Libs +from tests.support.mixins import LoaderModuleMockMixin +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + +settings = ["No Auditing", "Success", "Failure", "Success and Failure"] -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLgpoAuditpolTestCase(TestCase, LoaderModuleMockMixin): def setup_loader_modules(self): return { win_lgpo_auditpol: { - '__context__': {}, - '__salt__': { - 'cmd.run_all': salt.modules.cmdmod.run_all - }}} + "__context__": {}, + "__salt__": {"cmd.run_all": salt.modules.cmdmod.run_all}, + } + } def test_get_settings(self): names = win_lgpo_auditpol._get_valid_names() - ret = win_lgpo_auditpol.get_settings(category='All') + ret = win_lgpo_auditpol.get_settings(category="All") for name in names: self.assertIn(name, [k.lower() for k in ret]) def test_get_settings_invalid_category(self): self.assertRaises( - KeyError, - win_lgpo_auditpol.get_settings, - category='Fake Category') + KeyError, win_lgpo_auditpol.get_settings, category="Fake Category" + ) def test_get_setting(self): names = win_lgpo_auditpol._get_valid_names() @@ -46,45 +46,54 @@ class WinLgpoAuditpolTestCase(TestCase, LoaderModuleMockMixin): self.assertIn(ret, settings) def test_get_setting_invalid_name(self): - self.assertRaises( - KeyError, - win_lgpo_auditpol.get_setting, - name='Fake Name') + self.assertRaises(KeyError, win_lgpo_auditpol.get_setting, name="Fake Name") def test_set_setting(self): - names = ['Credential Validation', 'IPsec Driver', 'File System', 'SAM'] - mock_set = MagicMock(return_value={'retcode': 0, 'stdout': 'Success'}) - with patch.object(salt.modules.cmdmod, 'run_all', mock_set): - with patch.object(win_lgpo_auditpol, '_get_valid_names', - return_value=[k.lower() for k in names]): + names = ["Credential Validation", "IPsec Driver", "File System", "SAM"] + mock_set = MagicMock(return_value={"retcode": 0, "stdout": "Success"}) + with patch.object(salt.modules.cmdmod, "run_all", mock_set): + with patch.object( + win_lgpo_auditpol, + "_get_valid_names", + return_value=[k.lower() for k in names], + ): for name in names: value = random.choice(settings) win_lgpo_auditpol.set_setting(name=name, value=value) switches = win_lgpo_auditpol.settings[value] - cmd = 'auditpol /set /subcategory:"{0}" {1}' \ - ''.format(name, switches) + cmd = 'auditpol /set /subcategory:"{0}" {1}' "".format( + name, switches + ) mock_set.assert_called_once_with(cmd=cmd, python_shell=True) mock_set.reset_mock() def test_set_setting_invalid_setting(self): - names = ['Credential Validation', 'IPsec Driver', 'File System'] - with patch.object(win_lgpo_auditpol, '_get_valid_names', - return_value=[k.lower() for k in names]): + names = ["Credential Validation", "IPsec Driver", "File System"] + with patch.object( + win_lgpo_auditpol, + "_get_valid_names", + return_value=[k.lower() for k in names], + ): self.assertRaises( KeyError, win_lgpo_auditpol.set_setting, - name='Fake Name', - value='No Auditing') + name="Fake Name", + value="No Auditing", + ) def test_set_setting_invalid_value(self): - names = ['Credential Validation', 'IPsec Driver', 'File System'] - with patch.object(win_lgpo_auditpol, '_get_valid_names', - return_value=[k.lower() for k in names]): + names = ["Credential Validation", "IPsec Driver", "File System"] + with patch.object( + win_lgpo_auditpol, + "_get_valid_names", + return_value=[k.lower() for k in names], + ): self.assertRaises( KeyError, win_lgpo_auditpol.set_setting, - name='Credential Validation', - value='Fake Value') + name="Credential Validation", + value="Fake Value", + ) def test_get_auditpol_dump(self): names = win_lgpo_auditpol._get_valid_names() diff --git a/tests/unit/utils/test_win_lgpo_netsh.py b/tests/unit/utils/test_win_lgpo_netsh.py index fc0b1e8e44f..5c3afb5106c 100644 --- a/tests/unit/utils/test_win_lgpo_netsh.py +++ b/tests/unit/utils/test_win_lgpo_netsh.py @@ -1,333 +1,346 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.helpers import destructiveTest -from tests.support.unit import TestCase, skipIf +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.utils.platform import salt.utils.win_lgpo_netsh as win_lgpo_netsh from salt.exceptions import CommandExecutionError +# Import Salt Testing Libs +from tests.support.helpers import destructiveTest +from tests.support.unit import TestCase, skipIf -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinLgpoNetshTestCase(TestCase): def test_get_settings_firewallpolicy_local(self): - ret = win_lgpo_netsh.get_settings(profile='domain', - section='firewallpolicy', - store='local') - self.assertIn('Inbound', ret) - self.assertIn('Outbound', ret) + ret = win_lgpo_netsh.get_settings( + profile="domain", section="firewallpolicy", store="local" + ) + self.assertIn("Inbound", ret) + self.assertIn("Outbound", ret) def test_get_settings_firewallpolicy_lgpo(self): - ret = win_lgpo_netsh.get_settings(profile='domain', - section='firewallpolicy', - store='lgpo') - self.assertIn('Inbound', ret) - self.assertIn('Outbound', ret) + ret = win_lgpo_netsh.get_settings( + profile="domain", section="firewallpolicy", store="lgpo" + ) + self.assertIn("Inbound", ret) + self.assertIn("Outbound", ret) def test_get_settings_logging_local(self): - ret = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local') - self.assertIn('FileName', ret) - self.assertIn('LogAllowedConnections', ret) - self.assertIn('LogDroppedConnections', ret) - self.assertIn('MaxFileSize', ret) + ret = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + ) + self.assertIn("FileName", ret) + self.assertIn("LogAllowedConnections", ret) + self.assertIn("LogDroppedConnections", ret) + self.assertIn("MaxFileSize", ret) def test_get_settings_logging_lgpo(self): - ret = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='lgpo') - self.assertIn('FileName', ret) - self.assertIn('LogAllowedConnections', ret) - self.assertIn('LogDroppedConnections', ret) - self.assertIn('MaxFileSize', ret) + ret = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="lgpo" + ) + self.assertIn("FileName", ret) + self.assertIn("LogAllowedConnections", ret) + self.assertIn("LogDroppedConnections", ret) + self.assertIn("MaxFileSize", ret) def test_get_settings_settings_local(self): - ret = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='local') - self.assertIn('InboundUserNotification', ret) - self.assertIn('LocalConSecRules', ret) - self.assertIn('LocalFirewallRules', ret) - self.assertIn('RemoteManagement', ret) - self.assertIn('UnicastResponseToMulticast', ret) + ret = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="local" + ) + self.assertIn("InboundUserNotification", ret) + self.assertIn("LocalConSecRules", ret) + self.assertIn("LocalFirewallRules", ret) + self.assertIn("RemoteManagement", ret) + self.assertIn("UnicastResponseToMulticast", ret) def test_get_settings_settings_lgpo(self): - ret = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='lgpo') - self.assertIn('InboundUserNotification', ret) - self.assertIn('LocalConSecRules', ret) - self.assertIn('LocalFirewallRules', ret) - self.assertIn('RemoteManagement', ret) - self.assertIn('UnicastResponseToMulticast', ret) + ret = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="lgpo" + ) + self.assertIn("InboundUserNotification", ret) + self.assertIn("LocalConSecRules", ret) + self.assertIn("LocalFirewallRules", ret) + self.assertIn("RemoteManagement", ret) + self.assertIn("UnicastResponseToMulticast", ret) def test_get_settings_state_local(self): - ret = win_lgpo_netsh.get_settings(profile='domain', - section='state', - store='local') - self.assertIn('State', ret) + ret = win_lgpo_netsh.get_settings( + profile="domain", section="state", store="local" + ) + self.assertIn("State", ret) def test_get_settings_state_lgpo(self): - ret = win_lgpo_netsh.get_settings(profile='domain', - section='state', - store='lgpo') - self.assertIn('State', ret) + ret = win_lgpo_netsh.get_settings( + profile="domain", section="state", store="lgpo" + ) + self.assertIn("State", ret) def test_get_all_settings_local(self): - ret = win_lgpo_netsh.get_all_settings(profile='domain', - store='local') + ret = win_lgpo_netsh.get_all_settings(profile="domain", store="local") - self.assertIn('Inbound', ret) - self.assertIn('Outbound', ret) - self.assertIn('FileName', ret) - self.assertIn('LogAllowedConnections', ret) - self.assertIn('LogDroppedConnections', ret) - self.assertIn('MaxFileSize', ret) - self.assertIn('InboundUserNotification', ret) - self.assertIn('LocalConSecRules', ret) - self.assertIn('LocalFirewallRules', ret) - self.assertIn('RemoteManagement', ret) - self.assertIn('UnicastResponseToMulticast', ret) - self.assertIn('State', ret) + self.assertIn("Inbound", ret) + self.assertIn("Outbound", ret) + self.assertIn("FileName", ret) + self.assertIn("LogAllowedConnections", ret) + self.assertIn("LogDroppedConnections", ret) + self.assertIn("MaxFileSize", ret) + self.assertIn("InboundUserNotification", ret) + self.assertIn("LocalConSecRules", ret) + self.assertIn("LocalFirewallRules", ret) + self.assertIn("RemoteManagement", ret) + self.assertIn("UnicastResponseToMulticast", ret) + self.assertIn("State", ret) def test_get_all_settings_lgpo(self): - ret = win_lgpo_netsh.get_all_settings(profile='domain', - store='local') + ret = win_lgpo_netsh.get_all_settings(profile="domain", store="local") - self.assertIn('Inbound', ret) - self.assertIn('Outbound', ret) - self.assertIn('FileName', ret) - self.assertIn('LogAllowedConnections', ret) - self.assertIn('LogDroppedConnections', ret) - self.assertIn('MaxFileSize', ret) - self.assertIn('InboundUserNotification', ret) - self.assertIn('LocalConSecRules', ret) - self.assertIn('LocalFirewallRules', ret) - self.assertIn('RemoteManagement', ret) - self.assertIn('UnicastResponseToMulticast', ret) - self.assertIn('State', ret) + self.assertIn("Inbound", ret) + self.assertIn("Outbound", ret) + self.assertIn("FileName", ret) + self.assertIn("LogAllowedConnections", ret) + self.assertIn("LogDroppedConnections", ret) + self.assertIn("MaxFileSize", ret) + self.assertIn("InboundUserNotification", ret) + self.assertIn("LocalConSecRules", ret) + self.assertIn("LocalFirewallRules", ret) + self.assertIn("RemoteManagement", ret) + self.assertIn("UnicastResponseToMulticast", ret) + self.assertIn("State", ret) def test_get_all_profiles_local(self): - ret = win_lgpo_netsh.get_all_profiles(store='local') - self.assertIn('Domain Profile', ret) - self.assertIn('Private Profile', ret) - self.assertIn('Public Profile', ret) + ret = win_lgpo_netsh.get_all_profiles(store="local") + self.assertIn("Domain Profile", ret) + self.assertIn("Private Profile", ret) + self.assertIn("Public Profile", ret) def test_get_all_profiles_lgpo(self): - ret = win_lgpo_netsh.get_all_profiles(store='lgpo') - self.assertIn('Domain Profile', ret) - self.assertIn('Private Profile', ret) - self.assertIn('Public Profile', ret) + ret = win_lgpo_netsh.get_all_profiles(store="lgpo") + self.assertIn("Domain Profile", ret) + self.assertIn("Private Profile", ret) + self.assertIn("Public Profile", ret) @destructiveTest def test_set_firewall_settings_inbound_local(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='firewallpolicy', - store='local')['Inbound'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="firewallpolicy", store="local" + )["Inbound"] try: - ret = win_lgpo_netsh.set_firewall_settings(profile='domain', - inbound='allowinbound', - store='local') + ret = win_lgpo_netsh.set_firewall_settings( + profile="domain", inbound="allowinbound", store="local" + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='firewallpolicy', - store='local')['Inbound'] - self.assertEqual('AllowInbound', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="firewallpolicy", store="local" + )["Inbound"] + self.assertEqual("AllowInbound", new) finally: - ret = win_lgpo_netsh.set_firewall_settings(profile='domain', - inbound=current, - store='local') + ret = win_lgpo_netsh.set_firewall_settings( + profile="domain", inbound=current, store="local" + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_settings_inbound_local_notconfigured(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='firewallpolicy', - store='local')['Inbound'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="firewallpolicy", store="local" + )["Inbound"] try: self.assertRaises( CommandExecutionError, win_lgpo_netsh.set_firewall_settings, - profile='domain', - inbound='notconfigured', - store='local') + profile="domain", + inbound="notconfigured", + store="local", + ) finally: - ret = win_lgpo_netsh.set_firewall_settings(profile='domain', - inbound=current, - store='local') + ret = win_lgpo_netsh.set_firewall_settings( + profile="domain", inbound=current, store="local" + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_settings_inbound_lgpo_notconfigured(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='firewallpolicy', - store='lgpo')['Inbound'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="firewallpolicy", store="lgpo" + )["Inbound"] try: - ret = win_lgpo_netsh.set_firewall_settings(profile='domain', - inbound='notconfigured', - store='lgpo') + ret = win_lgpo_netsh.set_firewall_settings( + profile="domain", inbound="notconfigured", store="lgpo" + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='firewallpolicy', - store='lgpo')['Inbound'] - self.assertEqual('NotConfigured', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="firewallpolicy", store="lgpo" + )["Inbound"] + self.assertEqual("NotConfigured", new) finally: - ret = win_lgpo_netsh.set_firewall_settings(profile='domain', - inbound=current, - store='lgpo') + ret = win_lgpo_netsh.set_firewall_settings( + profile="domain", inbound=current, store="lgpo" + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_settings_outbound_local(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='firewallpolicy', - store='local')['Outbound'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="firewallpolicy", store="local" + )["Outbound"] try: - ret = win_lgpo_netsh.set_firewall_settings(profile='domain', - outbound='allowoutbound', - store='local') + ret = win_lgpo_netsh.set_firewall_settings( + profile="domain", outbound="allowoutbound", store="local" + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='firewallpolicy', - store='local')['Outbound'] - self.assertEqual('AllowOutbound', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="firewallpolicy", store="local" + )["Outbound"] + self.assertEqual("AllowOutbound", new) finally: - ret = win_lgpo_netsh.set_firewall_settings(profile='domain', - outbound=current, - store='local') + ret = win_lgpo_netsh.set_firewall_settings( + profile="domain", outbound=current, store="local" + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_logging_allowed_local_enable(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local')['LogAllowedConnections'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + )["LogAllowedConnections"] try: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='allowedconnections', - value='enable', - store='local') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", + setting="allowedconnections", + value="enable", + store="local", + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local')['LogAllowedConnections'] - self.assertEqual('Enable', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + )["LogAllowedConnections"] + self.assertEqual("Enable", new) finally: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='allowedconnections', - value=current, - store='local') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", + setting="allowedconnections", + value=current, + store="local", + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_logging_allowed_local_notconfigured(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local')['LogAllowedConnections'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + )["LogAllowedConnections"] try: self.assertRaises( CommandExecutionError, win_lgpo_netsh.set_logging_settings, - profile='domain', - setting='allowedconnections', - value='notconfigured', - store='local') + profile="domain", + setting="allowedconnections", + value="notconfigured", + store="local", + ) finally: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='allowedconnections', - value=current, - store='local') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", + setting="allowedconnections", + value=current, + store="local", + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_logging_allowed_lgpo_notconfigured(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='lgpo')['LogAllowedConnections'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="lgpo" + )["LogAllowedConnections"] try: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='allowedconnections', - value='notconfigured', - store='lgpo') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", + setting="allowedconnections", + value="notconfigured", + store="lgpo", + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='lgpo')['LogAllowedConnections'] - self.assertEqual('NotConfigured', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="lgpo" + )["LogAllowedConnections"] + self.assertEqual("NotConfigured", new) finally: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='allowedconnections', - value=current, - store='lgpo') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", + setting="allowedconnections", + value=current, + store="lgpo", + ) self.assertTrue(ret) def test_set_firewall_logging_dropped_local_enable(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local')['LogDroppedConnections'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + )["LogDroppedConnections"] try: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='droppedconnections', - value='enable', - store='local') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", + setting="droppedconnections", + value="enable", + store="local", + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local')['LogDroppedConnections'] - self.assertEqual('Enable', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + )["LogDroppedConnections"] + self.assertEqual("Enable", new) finally: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='droppedconnections', - value=current, - store='local') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", + setting="droppedconnections", + value=current, + store="local", + ) self.assertTrue(ret) def test_set_firewall_logging_filename_local(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local')['FileName'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + )["FileName"] try: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='filename', - value='C:\\Temp\\test.log', - store='local') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", + setting="filename", + value="C:\\Temp\\test.log", + store="local", + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local')['FileName'] - self.assertEqual('C:\\Temp\\test.log', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + )["FileName"] + self.assertEqual("C:\\Temp\\test.log", new) finally: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='filename', - value=current, - store='local') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", setting="filename", value=current, store="local" + ) self.assertTrue(ret) def test_set_firewall_logging_maxfilesize_local(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local')['MaxFileSize'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + )["MaxFileSize"] try: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='maxfilesize', - value='16384', - store='local') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", setting="maxfilesize", value="16384", store="local" + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='logging', - store='local')['MaxFileSize'] - self.assertEqual('16384', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="logging", store="local" + )["MaxFileSize"] + self.assertEqual("16384", new) finally: - ret = win_lgpo_netsh.set_logging_settings(profile='domain', - setting='maxfilesize', - value=current, - store='local') + ret = win_lgpo_netsh.set_logging_settings( + profile="domain", setting="maxfilesize", value=current, store="local" + ) self.assertTrue(ret) @destructiveTest @@ -335,31 +348,36 @@ class WinLgpoNetshTestCase(TestCase): self.assertRaises( CommandExecutionError, win_lgpo_netsh.set_settings, - profile='domain', - setting='localfirewallrules', - value='enable', - store='local') + profile="domain", + setting="localfirewallrules", + value="enable", + store="local", + ) @destructiveTest def test_set_firewall_settings_fwrules_lgpo_notconfigured(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='lgpo')['LocalFirewallRules'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="lgpo" + )["LocalFirewallRules"] try: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='localfirewallrules', - value='notconfigured', - store='lgpo') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="localfirewallrules", + value="notconfigured", + store="lgpo", + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='lgpo')['LocalFirewallRules'] - self.assertEqual('NotConfigured', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="lgpo" + )["LocalFirewallRules"] + self.assertEqual("NotConfigured", new) finally: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='localfirewallrules', - value=current, - store='lgpo') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="localfirewallrules", + value=current, + store="lgpo", + ) self.assertTrue(ret) @destructiveTest @@ -367,169 +385,188 @@ class WinLgpoNetshTestCase(TestCase): self.assertRaises( CommandExecutionError, win_lgpo_netsh.set_settings, - profile='domain', - setting='localconsecrules', - value='enable', - store='local') + profile="domain", + setting="localconsecrules", + value="enable", + store="local", + ) def test_set_firewall_settings_notification_local_enable(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='local')['InboundUserNotification'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="local" + )["InboundUserNotification"] try: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='inboundusernotification', - value='enable', - store='local') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="inboundusernotification", + value="enable", + store="local", + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='local')['InboundUserNotification'] - self.assertEqual('Enable', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="local" + )["InboundUserNotification"] + self.assertEqual("Enable", new) finally: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='inboundusernotification', - value=current, - store='local') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="inboundusernotification", + value=current, + store="local", + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_settings_notification_local_notconfigured(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='local')['InboundUserNotification'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="local" + )["InboundUserNotification"] try: self.assertRaises( CommandExecutionError, win_lgpo_netsh.set_settings, - profile='domain', - setting='inboundusernotification', - value='notconfigured', - store='local') + profile="domain", + setting="inboundusernotification", + value="notconfigured", + store="local", + ) finally: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='inboundusernotification', - value=current, - store='local') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="inboundusernotification", + value=current, + store="local", + ) self.assertTrue(ret) def test_set_firewall_settings_notification_lgpo_notconfigured(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='lgpo')['InboundUserNotification'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="lgpo" + )["InboundUserNotification"] try: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='inboundusernotification', - value='notconfigured', - store='lgpo') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="inboundusernotification", + value="notconfigured", + store="lgpo", + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='lgpo')['InboundUserNotification'] - self.assertEqual('NotConfigured', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="lgpo" + )["InboundUserNotification"] + self.assertEqual("NotConfigured", new) finally: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='inboundusernotification', - value=current, - store='lgpo') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="inboundusernotification", + value=current, + store="lgpo", + ) self.assertTrue(ret) def test_set_firewall_settings_remotemgmt_local_enable(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='local')['RemoteManagement'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="local" + )["RemoteManagement"] try: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='remotemanagement', - value='enable', - store='local') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="remotemanagement", + value="enable", + store="local", + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='local')['RemoteManagement'] - self.assertEqual('Enable', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="local" + )["RemoteManagement"] + self.assertEqual("Enable", new) finally: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='remotemanagement', - value=current, - store='local') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="remotemanagement", + value=current, + store="local", + ) self.assertTrue(ret) def test_set_firewall_settings_unicast_local_disable(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='local')['UnicastResponseToMulticast'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="local" + )["UnicastResponseToMulticast"] try: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='unicastresponsetomulticast', - value='disable', - store='local') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="unicastresponsetomulticast", + value="disable", + store="local", + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='settings', - store='local')['UnicastResponseToMulticast'] - self.assertEqual('Disable', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="settings", store="local" + )["UnicastResponseToMulticast"] + self.assertEqual("Disable", new) finally: - ret = win_lgpo_netsh.set_settings(profile='domain', - setting='unicastresponsetomulticast', - value=current, - store='local') + ret = win_lgpo_netsh.set_settings( + profile="domain", + setting="unicastresponsetomulticast", + value=current, + store="local", + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_state_local_on(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='state', - store='local')['State'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="state", store="local" + )["State"] try: - ret = win_lgpo_netsh.set_state(profile='domain', - state='off', - store='local') + ret = win_lgpo_netsh.set_state(profile="domain", state="off", store="local") self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='state', - store='local')['State'] - self.assertEqual('OFF', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="state", store="local" + )["State"] + self.assertEqual("OFF", new) finally: - ret = win_lgpo_netsh.set_state(profile='domain', - state=current, - store='local') + ret = win_lgpo_netsh.set_state( + profile="domain", state=current, store="local" + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_state_local_notconfigured(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='state', - store='local')['State'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="state", store="local" + )["State"] try: self.assertRaises( CommandExecutionError, win_lgpo_netsh.set_state, - profile='domain', - state='notconfigured', - store='local') + profile="domain", + state="notconfigured", + store="local", + ) finally: - ret = win_lgpo_netsh.set_state(profile='domain', - state=current, - store='local') + ret = win_lgpo_netsh.set_state( + profile="domain", state=current, store="local" + ) self.assertTrue(ret) @destructiveTest def test_set_firewall_state_lgpo_notconfigured(self): - current = win_lgpo_netsh.get_settings(profile='domain', - section='state', - store='local')['State'] + current = win_lgpo_netsh.get_settings( + profile="domain", section="state", store="local" + )["State"] try: - ret = win_lgpo_netsh.set_state(profile='domain', - state='notconfigured', - store='lgpo') + ret = win_lgpo_netsh.set_state( + profile="domain", state="notconfigured", store="lgpo" + ) self.assertTrue(ret) - new = win_lgpo_netsh.get_settings(profile='domain', - section='state', - store='lgpo')['State'] - self.assertEqual('NotConfigured', new) + new = win_lgpo_netsh.get_settings( + profile="domain", section="state", store="lgpo" + )["State"] + self.assertEqual("NotConfigured", new) finally: - ret = win_lgpo_netsh.set_state(profile='domain', - state=current, - store='lgpo') + ret = win_lgpo_netsh.set_state( + profile="domain", state=current, store="lgpo" + ) self.assertTrue(ret) diff --git a/tests/unit/utils/test_win_network.py b/tests/unit/utils/test_win_network.py index 38ee518e00a..3e8493bc0bd 100644 --- a/tests/unit/utils/test_win_network.py +++ b/tests/unit/utils/test_win_network.py @@ -1,68 +1,83 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.mock import patch, MagicMock -from tests.support.unit import TestCase, skipIf +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.utils.platform import salt.utils.win_network as win_network -mock_ip_base = MagicMock(return_value={ - 'dns_enabled': False, - 'dns_suffix': '', - 'dynamic_dns_enabled': False, -}) +# Import Salt Testing Libs +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf -mock_unicast = MagicMock(return_value={ - 'ip_addresses': [{ - 'address': '172.18.87.49', - 'broadcast': '172.18.87.63', - 'loopback': '127.0.0.1', - 'netmask': '255.255.255.240', - 'prefix_length': 28, - 'prefix_origin': 'Manual', - 'suffix_origin': 'Manual'}], - 'ipv6_addresses': [{ - 'address': 'fe80::e8a4:1224:5548:2b81', - 'interface_index': 32, - 'prefix_length': 64, - 'prefix_origin': 'WellKnown', - 'suffix_origin': 'Router'}], -}) +mock_ip_base = MagicMock( + return_value={"dns_enabled": False, "dns_suffix": "", "dynamic_dns_enabled": False} +) -mock_gateway = MagicMock(return_value={ - 'ip_gateways': ['192.168.0.1'], - 'ipv6_gateways': ['fe80::208:a2ff:fe0b:de70'] -}) +mock_unicast = MagicMock( + return_value={ + "ip_addresses": [ + { + "address": "172.18.87.49", + "broadcast": "172.18.87.63", + "loopback": "127.0.0.1", + "netmask": "255.255.255.240", + "prefix_length": 28, + "prefix_origin": "Manual", + "suffix_origin": "Manual", + } + ], + "ipv6_addresses": [ + { + "address": "fe80::e8a4:1224:5548:2b81", + "interface_index": 32, + "prefix_length": 64, + "prefix_origin": "WellKnown", + "suffix_origin": "Router", + } + ], + } +) -mock_dns = MagicMock(return_value={ - 'ip_dns': ['10.4.0.1', '10.1.0.1', '8.8.8.8'], - 'ipv6_dns': ['2600:740a:1:304::1'] -}) +mock_gateway = MagicMock( + return_value={ + "ip_gateways": ["192.168.0.1"], + "ipv6_gateways": ["fe80::208:a2ff:fe0b:de70"], + } +) -mock_multicast = MagicMock(return_value={ - u'ip_multicast': ['224.0.0.1', - '224.0.0.251', - '224.0.0.252', - '230.230.230.230', - '239.0.0.250', - '239.255.255.250'], - 'ipv6_multicast': ['ff01::1', - 'ff02::1', - 'ff02::c', - 'ff02::fb', - 'ff02::1:3', - 'ff02::1:ff0f:4c48', - 'ff02::1:ffa6:f6e6'], -}) +mock_dns = MagicMock( + return_value={ + "ip_dns": ["10.4.0.1", "10.1.0.1", "8.8.8.8"], + "ipv6_dns": ["2600:740a:1:304::1"], + } +) -mock_anycast = MagicMock(return_value={'ip_anycast': [], - 'ipv6_anycast': []}) +mock_multicast = MagicMock( + return_value={ + "ip_multicast": [ + "224.0.0.1", + "224.0.0.251", + "224.0.0.252", + "230.230.230.230", + "239.0.0.250", + "239.255.255.250", + ], + "ipv6_multicast": [ + "ff01::1", + "ff02::1", + "ff02::c", + "ff02::fb", + "ff02::1:3", + "ff02::1:ff0f:4c48", + "ff02::1:ffa6:f6e6", + ], + } +) -mock_wins = MagicMock(return_value={'ip_wins': []}) +mock_anycast = MagicMock(return_value={"ip_anycast": [], "ipv6_anycast": []}) + +mock_wins = MagicMock(return_value={"ip_wins": []}) class PhysicalAddress(object): @@ -74,17 +89,20 @@ class PhysicalAddress(object): class Interface(object): - ''' + """ Mocked interface object - ''' - def __init__(self, - i_address='02D5F1DD31E0', - i_description='Dell GigabitEthernet', - i_id='{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}', - i_name='Ethernet', - i_receive_only=False, - i_status=1, - i_type=6): + """ + + def __init__( + self, + i_address="02D5F1DD31E0", + i_description="Dell GigabitEthernet", + i_id="{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}", + i_name="Ethernet", + i_receive_only=False, + i_status=1, + i_type=6, + ): self.PhysicalAddress = PhysicalAddress(i_address) self.Description = i_description self.Id = i_id @@ -97,63 +115,86 @@ class Interface(object): return self.PhysicalAddress -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinNetworkTestCase(TestCase): def test_get_interface_info_dot_net(self): expected = { - 'Ethernet': { - 'alias': 'Ethernet', - 'description': 'Dell GigabitEthernet', - 'dns_enabled': False, - 'dns_suffix': '', - 'dynamic_dns_enabled': False, - 'id': '{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}', - 'ip_addresses': [{'address': u'172.18.87.49', - 'broadcast': u'172.18.87.63', - 'loopback': u'127.0.0.1', - 'netmask': u'255.255.255.240', - 'prefix_length': 28, - 'prefix_origin': u'Manual', - 'suffix_origin': u'Manual'}], - 'ip_anycast': [], - 'ip_dns': ['10.4.0.1', '10.1.0.1', '8.8.8.8'], - 'ip_gateways': ['192.168.0.1'], - 'ip_multicast': ['224.0.0.1', - '224.0.0.251', - '224.0.0.252', - '230.230.230.230', - '239.0.0.250', - '239.255.255.250'], - 'ip_wins': [], - 'ipv6_addresses': [{'address': u'fe80::e8a4:1224:5548:2b81', - 'interface_index': 32, - 'prefix_length': 64, - 'prefix_origin': u'WellKnown', - 'suffix_origin': u'Router'}], - 'ipv6_anycast': [], - 'ipv6_dns': ['2600:740a:1:304::1'], - 'ipv6_gateways': ['fe80::208:a2ff:fe0b:de70'], - 'ipv6_multicast': ['ff01::1', - 'ff02::1', - 'ff02::c', - 'ff02::fb', - 'ff02::1:3', - 'ff02::1:ff0f:4c48', - 'ff02::1:ffa6:f6e6'], - 'physical_address': '02:D5:F1:DD:31:E0', - 'receive_only': False, - 'status': 'Up', - 'type': 'Ethernet'}} + "Ethernet": { + "alias": "Ethernet", + "description": "Dell GigabitEthernet", + "dns_enabled": False, + "dns_suffix": "", + "dynamic_dns_enabled": False, + "id": "{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}", + "ip_addresses": [ + { + "address": "172.18.87.49", + "broadcast": "172.18.87.63", + "loopback": "127.0.0.1", + "netmask": "255.255.255.240", + "prefix_length": 28, + "prefix_origin": "Manual", + "suffix_origin": "Manual", + } + ], + "ip_anycast": [], + "ip_dns": ["10.4.0.1", "10.1.0.1", "8.8.8.8"], + "ip_gateways": ["192.168.0.1"], + "ip_multicast": [ + "224.0.0.1", + "224.0.0.251", + "224.0.0.252", + "230.230.230.230", + "239.0.0.250", + "239.255.255.250", + ], + "ip_wins": [], + "ipv6_addresses": [ + { + "address": "fe80::e8a4:1224:5548:2b81", + "interface_index": 32, + "prefix_length": 64, + "prefix_origin": "WellKnown", + "suffix_origin": "Router", + } + ], + "ipv6_anycast": [], + "ipv6_dns": ["2600:740a:1:304::1"], + "ipv6_gateways": ["fe80::208:a2ff:fe0b:de70"], + "ipv6_multicast": [ + "ff01::1", + "ff02::1", + "ff02::c", + "ff02::fb", + "ff02::1:3", + "ff02::1:ff0f:4c48", + "ff02::1:ffa6:f6e6", + ], + "physical_address": "02:D5:F1:DD:31:E0", + "receive_only": False, + "status": "Up", + "type": "Ethernet", + } + } mock_int = MagicMock(return_value=[Interface()]) - with patch.object(win_network, '_get_network_interfaces', mock_int), \ - patch.object(win_network, '_get_ip_base_properties', mock_ip_base), \ - patch.object(win_network, '_get_ip_unicast_info', mock_unicast), \ - patch.object(win_network, '_get_ip_gateway_info', mock_gateway), \ - patch.object(win_network, '_get_ip_dns_info', mock_dns), \ - patch.object(win_network, '_get_ip_multicast_info', mock_multicast), \ - patch.object(win_network, '_get_ip_anycast_info', mock_anycast), \ - patch.object(win_network, '_get_ip_wins_info', mock_wins): + with patch.object( + win_network, "_get_network_interfaces", mock_int + ), patch.object( + win_network, "_get_ip_base_properties", mock_ip_base + ), patch.object( + win_network, "_get_ip_unicast_info", mock_unicast + ), patch.object( + win_network, "_get_ip_gateway_info", mock_gateway + ), patch.object( + win_network, "_get_ip_dns_info", mock_dns + ), patch.object( + win_network, "_get_ip_multicast_info", mock_multicast + ), patch.object( + win_network, "_get_ip_anycast_info", mock_anycast + ), patch.object( + win_network, "_get_ip_wins_info", mock_wins + ): # ret = win_network._get_base_properties() results = win_network.get_interface_info_dot_net() @@ -162,25 +203,44 @@ class WinNetworkTestCase(TestCase): def test_get_network_info(self): expected = { - 'Dell GigabitEthernet': { - 'hwaddr': '02:D5:F1:DD:31:E0', - 'inet': [{'address': '172.18.87.49', - 'broadcast': '172.18.87.63', - 'gateway': '192.168.0.1', - 'label': 'Dell GigabitEthernet', - 'netmask': '255.255.255.240'}], - 'inet6': [{'address': 'fe80::e8a4:1224:5548:2b81', - 'gateway': 'fe80::208:a2ff:fe0b:de70'}], - 'up': True}} + "Dell GigabitEthernet": { + "hwaddr": "02:D5:F1:DD:31:E0", + "inet": [ + { + "address": "172.18.87.49", + "broadcast": "172.18.87.63", + "gateway": "192.168.0.1", + "label": "Dell GigabitEthernet", + "netmask": "255.255.255.240", + } + ], + "inet6": [ + { + "address": "fe80::e8a4:1224:5548:2b81", + "gateway": "fe80::208:a2ff:fe0b:de70", + } + ], + "up": True, + } + } mock_int = MagicMock(return_value=[Interface()]) - with patch.object(win_network, '_get_network_interfaces', mock_int), \ - patch.object(win_network, '_get_ip_base_properties', mock_ip_base), \ - patch.object(win_network, '_get_ip_unicast_info', mock_unicast), \ - patch.object(win_network, '_get_ip_gateway_info', mock_gateway), \ - patch.object(win_network, '_get_ip_dns_info', mock_dns), \ - patch.object(win_network, '_get_ip_multicast_info', mock_multicast), \ - patch.object(win_network, '_get_ip_anycast_info', mock_anycast), \ - patch.object(win_network, '_get_ip_wins_info', mock_wins): + with patch.object( + win_network, "_get_network_interfaces", mock_int + ), patch.object( + win_network, "_get_ip_base_properties", mock_ip_base + ), patch.object( + win_network, "_get_ip_unicast_info", mock_unicast + ), patch.object( + win_network, "_get_ip_gateway_info", mock_gateway + ), patch.object( + win_network, "_get_ip_dns_info", mock_dns + ), patch.object( + win_network, "_get_ip_multicast_info", mock_multicast + ), patch.object( + win_network, "_get_ip_anycast_info", mock_anycast + ), patch.object( + win_network, "_get_ip_wins_info", mock_wins + ): # ret = win_network._get_base_properties() results = win_network.get_interface_info() @@ -188,53 +248,57 @@ class WinNetworkTestCase(TestCase): self.assertDictEqual(expected, results) def test__get_base_properties_tap_adapter(self): - ''' + """ Adapter Type 53 is apparently an undocumented type corresponding to OpenVPN TAP Adapters and possibly other TAP Adapters. This test makes sure the win_network util will catch that. https://github.com/saltstack/salt/issues/56196 https://github.com/saltstack/salt/issues/56275 - ''' + """ i_face = Interface( - i_address='03DE4D0713FA', - i_description='Windows TAP Adapter', - i_id='{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}', - i_name='Windows TAP Adapter', + i_address="03DE4D0713FA", + i_description="Windows TAP Adapter", + i_id="{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}", + i_name="Windows TAP Adapter", i_receive_only=False, i_status=1, - i_type=53) + i_type=53, + ) expected = { - 'alias': 'Windows TAP Adapter', - 'description': 'Windows TAP Adapter', - 'id': '{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}', - 'receive_only': False, - 'physical_address': '03:DE:4D:07:13:FA', - 'status': 'Up', - 'type': 'TAPAdapter'} + "alias": "Windows TAP Adapter", + "description": "Windows TAP Adapter", + "id": "{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}", + "receive_only": False, + "physical_address": "03:DE:4D:07:13:FA", + "status": "Up", + "type": "TAPAdapter", + } results = win_network._get_base_properties(i_face=i_face) self.assertDictEqual(expected, results) def test__get_base_properties_undefined_adapter(self): - ''' + """ The Adapter Type 53 may be an arbitrary number assigned by OpenVPN. This will test the ability to avoid stack tracing on an undefined adapter type. If one is encountered, just use the description. - ''' + """ i_face = Interface( - i_address='03DE4D0713FA', - i_description='Undefined Adapter', - i_id='{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}', - i_name='Undefined', + i_address="03DE4D0713FA", + i_description="Undefined Adapter", + i_id="{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}", + i_name="Undefined", i_receive_only=False, i_status=1, - i_type=50) + i_type=50, + ) expected = { - 'alias': 'Undefined', - 'description': 'Undefined Adapter', - 'id': '{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}', - 'receive_only': False, - 'physical_address': '03:DE:4D:07:13:FA', - 'status': 'Up', - 'type': 'Undefined Adapter'} + "alias": "Undefined", + "description": "Undefined Adapter", + "id": "{C5F468C0-DD5F-4C2B-939F-A411DCB5DE16}", + "receive_only": False, + "physical_address": "03:DE:4D:07:13:FA", + "status": "Up", + "type": "Undefined Adapter", + } results = win_network._get_base_properties(i_face=i_face) self.assertDictEqual(expected, results) diff --git a/tests/unit/utils/test_win_osinfo.py b/tests/unit/utils/test_win_osinfo.py index 82c459b4b4c..9bf173e60ba 100644 --- a/tests/unit/utils/test_win_osinfo.py +++ b/tests/unit/utils/test_win_osinfo.py @@ -1,43 +1,46 @@ # -*- coding: utf-8 -*- -''' +""" :codeauthor: Shane Lee <slee@saltstack.com> -''' +""" # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function +from __future__ import absolute_import, print_function, unicode_literals + import sys +import salt.utils.platform + +# Import Salt Libs +import salt.utils.win_osinfo as win_osinfo + # Import 3rd Party Libs from salt.ext import six # Import Salt Testing Libs from tests.support.unit import TestCase, skipIf -# Import Salt Libs -import salt.utils.win_osinfo as win_osinfo -import salt.utils.platform - -@skipIf(not salt.utils.platform.is_windows(), 'Requires Windows') +@skipIf(not salt.utils.platform.is_windows(), "Requires Windows") class WinOsInfo(TestCase): - ''' + """ Test cases for salt/utils/win_osinfo.py - ''' + """ + def test_get_os_version_info(self): sys_info = sys.getwindowsversion() get_info = win_osinfo.get_os_version_info() - self.assertEqual(sys_info.major, int(get_info['MajorVersion'])) - self.assertEqual(sys_info.minor, int(get_info['MinorVersion'])) - self.assertEqual(sys_info.platform, int(get_info['PlatformID'])) - self.assertEqual(sys_info.build, int(get_info['BuildNumber'])) + self.assertEqual(sys_info.major, int(get_info["MajorVersion"])) + self.assertEqual(sys_info.minor, int(get_info["MinorVersion"])) + self.assertEqual(sys_info.platform, int(get_info["PlatformID"])) + self.assertEqual(sys_info.build, int(get_info["BuildNumber"])) # Platform ID is the reason for this function # Since we can't get the actual value another way, we will just check # that it exists and is a number - self.assertIn('PlatformID', get_info) - self.assertTrue(isinstance(get_info['BuildNumber'], six.integer_types)) + self.assertIn("PlatformID", get_info) + self.assertTrue(isinstance(get_info["BuildNumber"], six.integer_types)) def test_get_join_info(self): join_info = win_osinfo.get_join_info() - self.assertIn('Domain', join_info) - self.assertIn('DomainType', join_info) - valid_types = ['Unknown', 'Unjoined', 'Workgroup', 'Domain'] - self.assertIn(join_info['DomainType'], valid_types) + self.assertIn("Domain", join_info) + self.assertIn("DomainType", join_info) + valid_types = ["Unknown", "Unjoined", "Workgroup", "Domain"] + self.assertIn(join_info["DomainType"], valid_types) diff --git a/tests/unit/utils/test_win_pdh.py b/tests/unit/utils/test_win_pdh.py index 955edca84b8..1d97a0b4454 100644 --- a/tests/unit/utils/test_win_pdh.py +++ b/tests/unit/utils/test_win_pdh.py @@ -1,44 +1,44 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.unit import TestCase, skipIf +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.utils.platform import salt.utils.win_pdh as win_pdh +# Import Salt Testing Libs +from tests.support.unit import TestCase, skipIf -@skipIf(not salt.utils.platform.is_windows(), 'System is not Windows') + +@skipIf(not salt.utils.platform.is_windows(), "System is not Windows") class WinPdhTestCase(TestCase): def test_list_objects(self): - known_objects = ['Cache', 'Memory', 'Process', 'Processor', 'System'] + known_objects = ["Cache", "Memory", "Process", "Processor", "System"] objects = win_pdh.list_objects() for item in known_objects: self.assertTrue(item in objects) def test_list_counters(self): - counters = win_pdh.list_counters('Processor') - known_counters = ['% Processor Time', '% User Time', '% DPC Time'] + counters = win_pdh.list_counters("Processor") + known_counters = ["% Processor Time", "% User Time", "% DPC Time"] for item in known_counters: self.assertTrue(item in counters) def test_list_instances(self): - instances = win_pdh.list_instances('Processor') - known_instances = ['0', '_Total'] + instances = win_pdh.list_instances("Processor") + known_instances = ["0", "_Total"] for item in known_instances: self.assertTrue(item in instances) def test_build_counter_list(self): counter_list = [ - ('Memory', None, 'Available Bytes'), - ('Paging File', '*', '% Usage'), - ('Processor', '*', '% Processor Time'), - ('Server', None, 'Work Item Shortages'), - ('Server Work Queues', '*', 'Queue Length'), - ('System', None, 'Context Switches/sec'), + ("Memory", None, "Available Bytes"), + ("Paging File", "*", "% Usage"), + ("Processor", "*", "% Processor Time"), + ("Server", None, "Work Item Shortages"), + ("Server Work Queues", "*", "Queue Length"), + ("System", None, "Context Switches/sec"), ] resulting_list = win_pdh.build_counter_list(counter_list) for counter in resulting_list: @@ -49,49 +49,50 @@ class WinPdhTestCase(TestCase): resulting_paths.append(counter.path) expected_paths = [ - '\\Memory\\Available Bytes', - '\\Paging File(*)\\% Usage', - '\\Processor(*)\\% Processor Time', - '\\Server\\Work Item Shortages', - '\\Server Work Queues(*)\\Queue Length', - '\\System\\Context Switches/sec'] + "\\Memory\\Available Bytes", + "\\Paging File(*)\\% Usage", + "\\Processor(*)\\% Processor Time", + "\\Server\\Work Item Shortages", + "\\Server Work Queues(*)\\Queue Length", + "\\System\\Context Switches/sec", + ] self.assertEqual(resulting_paths, expected_paths) def test_get_all_counters(self): - results = win_pdh.get_all_counters('Processor') + results = win_pdh.get_all_counters("Processor") known_counters = [ - '\\Processor(*)\\% Processor Time', - '\\Processor(*)\\% Idle Time', - '\\Processor(*)\\DPC Rate', - '\\Processor(*)\\% Privileged Time', - '\\Processor(*)\\DPCs Queued/sec', - '\\Processor(*)\\% Interrupt Time', - '\\Processor(*)\\Interrupts/sec', + "\\Processor(*)\\% Processor Time", + "\\Processor(*)\\% Idle Time", + "\\Processor(*)\\DPC Rate", + "\\Processor(*)\\% Privileged Time", + "\\Processor(*)\\DPCs Queued/sec", + "\\Processor(*)\\% Interrupt Time", + "\\Processor(*)\\Interrupts/sec", ] for item in known_counters: self.assertTrue(item in results) def test_get_counters(self): counter_list = [ - ('Memory', None, 'Available Bytes'), - ('Paging File', '*', '% Usage'), - ('Processor', '*', '% Processor Time'), - ('Server', None, 'Work Item Shortages'), - ('Server Work Queues', '*', 'Queue Length'), - ('System', None, 'Context Switches/sec'), + ("Memory", None, "Available Bytes"), + ("Paging File", "*", "% Usage"), + ("Processor", "*", "% Processor Time"), + ("Server", None, "Work Item Shortages"), + ("Server Work Queues", "*", "Queue Length"), + ("System", None, "Context Switches/sec"), ] results = win_pdh.get_counters(counter_list) expected_counters = [ - '\\Memory\\Available Bytes', - '\\Paging File(*)\\% Usage', - '\\Processor(*)\\% Processor Time', - '\\Server\\Work Item Shortages', - '\\Server Work Queues(*)\\Queue Length', - '\\System\\Context Switches/sec' + "\\Memory\\Available Bytes", + "\\Paging File(*)\\% Usage", + "\\Processor(*)\\% Processor Time", + "\\Server\\Work Item Shortages", + "\\Server Work Queues(*)\\Queue Length", + "\\System\\Context Switches/sec", ] for item in expected_counters: self.assertTrue(item in results) def test_get_counter(self): - results = win_pdh.get_counter('Processor', '*', '% Processor Time') - self.assertTrue('\\Processor(*)\\% Processor Time' in results) + results = win_pdh.get_counter("Processor", "*", "% Processor Time") + self.assertTrue("\\Processor(*)\\% Processor Time" in results) diff --git a/tests/unit/utils/test_win_reg.py b/tests/unit/utils/test_win_reg.py index f9230886cdf..ca0ec73cab9 100644 --- a/tests/unit/utils/test_win_reg.py +++ b/tests/unit/utils/test_win_reg.py @@ -1,12 +1,7 @@ # -*- coding: utf-8 -*- # Import Python Libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing Libs -from tests.support.helpers import destructiveTest, generate_random_name -from tests.support.mock import patch, MagicMock -from tests.support.unit import TestCase, skipIf +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Libs import salt.utils.stringutils @@ -14,947 +9,989 @@ import salt.utils.win_reg as win_reg from salt.exceptions import CommandExecutionError from salt.ext import six +# Import Salt Testing Libs +from tests.support.helpers import destructiveTest, generate_random_name +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase, skipIf + try: import win32api + HAS_WIN32 = True except ImportError: HAS_WIN32 = False -UNICODE_KEY = 'Unicode Key \N{TRADE MARK SIGN}' -UNICODE_VALUE = 'Unicode Value ' \ - '\N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN}' -FAKE_KEY = '\\'.join(['SOFTWARE', generate_random_name('SaltTesting-')]) +UNICODE_KEY = "Unicode Key \N{TRADE MARK SIGN}" +UNICODE_VALUE = ( + "Unicode Value " "\N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN}" +) +FAKE_KEY = "\\".join(["SOFTWARE", generate_random_name("SaltTesting-")]) -@skipIf(not HAS_WIN32, 'Tests require win32 libraries') +@skipIf(not HAS_WIN32, "Tests require win32 libraries") class WinFunctionsTestCase(TestCase): - ''' + """ Test cases for salt.utils.win_reg - ''' + """ + def test_broadcast_change_success(self): - ''' + """ Tests the broadcast_change function - ''' - with patch('win32gui.SendMessageTimeout', return_value=('', 0)): + """ + with patch("win32gui.SendMessageTimeout", return_value=("", 0)): self.assertTrue(win_reg.broadcast_change()) def test_broadcast_change_fail(self): - ''' + """ Tests the broadcast_change function failure - ''' - with patch('win32gui.SendMessageTimeout', return_value=('', 1)): + """ + with patch("win32gui.SendMessageTimeout", return_value=("", 1)): self.assertFalse(win_reg.broadcast_change()) def test_key_exists_existing(self): - ''' + """ Tests the key_exists function using a well known registry key - ''' - self.assertTrue( - win_reg.key_exists(hive='HKLM', key='SOFTWARE\\Microsoft')) + """ + self.assertTrue(win_reg.key_exists(hive="HKLM", key="SOFTWARE\\Microsoft")) def test_key_exists_non_existing(self): - ''' + """ Tests the key_exists function using a non existing registry key - ''' - self.assertFalse(win_reg.key_exists(hive='HKLM', key=FAKE_KEY)) + """ + self.assertFalse(win_reg.key_exists(hive="HKLM", key=FAKE_KEY)) def test_key_exists_invalid_hive(self): - ''' + """ Tests the key_exists function using an invalid hive - ''' - self.assertRaises(CommandExecutionError, - win_reg.key_exists, - hive='BADHIVE', - key='SOFTWARE\\Microsoft') + """ + self.assertRaises( + CommandExecutionError, + win_reg.key_exists, + hive="BADHIVE", + key="SOFTWARE\\Microsoft", + ) def test_key_exists_unknown_key_error(self): - ''' + """ Tests the key_exists function with an unknown key error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): - self.assertRaises(win32api.error, - win_reg.key_exists, - hive='HKLM', - key='SOFTWARE\\Microsoft') + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): + self.assertRaises( + win32api.error, + win_reg.key_exists, + hive="HKLM", + key="SOFTWARE\\Microsoft", + ) def test_value_exists_existing(self): - ''' + """ Tests the value_exists function using a well known registry key - ''' + """ self.assertTrue( win_reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='CommonFilesDir')) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="CommonFilesDir", + ) + ) def test_value_exists_non_existing(self): - ''' + """ Tests the value_exists function using a non existing registry key - ''' + """ self.assertFalse( win_reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='NonExistingValueName')) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="NonExistingValueName", + ) + ) def test_value_exists_invalid_hive(self): - ''' + """ Tests the value_exists function using an invalid hive - ''' - self.assertRaises(CommandExecutionError, - win_reg.value_exists, - hive='BADHIVE', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='CommonFilesDir') + """ + self.assertRaises( + CommandExecutionError, + win_reg.value_exists, + hive="BADHIVE", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="CommonFilesDir", + ) def test_value_exists_key_not_exist(self): - ''' + """ Tests the value_exists function when the key does not exist - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(2, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + side_effect=win32api.error(2, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): self.assertFalse( win_reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='CommonFilesDir')) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="CommonFilesDir", + ) + ) def test_value_exists_unknown_key_error(self): - ''' + """ Tests the value_exists function with an unknown error when opening the key - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): self.assertRaises( win32api.error, win_reg.value_exists, - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='CommonFilesDir') + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="CommonFilesDir", + ) def test_value_exists_empty_default_value(self): - ''' + """ Tests the value_exists function when querying the default value - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(2, 'RegQueryValueEx', 'Empty Value')) - with patch('salt.utils.win_reg.win32api.RegQueryValueEx', mock_error): + side_effect=win32api.error(2, "RegQueryValueEx", "Empty Value") + ) + with patch("salt.utils.win_reg.win32api.RegQueryValueEx", mock_error): self.assertTrue( win_reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname=None)) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname=None, + ) + ) def test_value_exists_no_vname(self): - ''' + """ Tests the value_exists function when the vname does not exist - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegQueryValueEx', 'Empty Value')) - with patch('salt.utils.win_reg.win32api.RegQueryValueEx', mock_error): + side_effect=win32api.error(123, "RegQueryValueEx", "Empty Value") + ) + with patch("salt.utils.win_reg.win32api.RegQueryValueEx", mock_error): self.assertFalse( win_reg.value_exists( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='NonExistingValuePair')) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="NonExistingValuePair", + ) + ) def test_list_keys_existing(self): - ''' + """ Test the list_keys function using a well known registry key - ''' - self.assertIn( - 'Microsoft', - win_reg.list_keys(hive='HKLM', key='SOFTWARE')) + """ + self.assertIn("Microsoft", win_reg.list_keys(hive="HKLM", key="SOFTWARE")) def test_list_keys_non_existing(self): - ''' + """ Test the list_keys function using a non existing registry key - ''' - expected = (False, 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY)) - self.assertEqual(win_reg.list_keys(hive='HKLM', key=FAKE_KEY), expected) + """ + expected = (False, "Cannot find key: HKLM\\{0}".format(FAKE_KEY)) + self.assertEqual(win_reg.list_keys(hive="HKLM", key=FAKE_KEY), expected) def test_list_keys_invalid_hive(self): - ''' + """ Test the list_keys function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - win_reg.list_keys, - hive='BADHIVE', - key='SOFTWARE\\Microsoft') + """ + self.assertRaises( + CommandExecutionError, + win_reg.list_keys, + hive="BADHIVE", + key="SOFTWARE\\Microsoft", + ) def test_list_keys_unknown_key_error(self): - ''' + """ Tests the list_keys function with an unknown key error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): - self.assertRaises(win32api.error, - win_reg.list_keys, - hive='HKLM', - key='SOFTWARE\\Microsoft') + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): + self.assertRaises( + win32api.error, + win_reg.list_keys, + hive="HKLM", + key="SOFTWARE\\Microsoft", + ) def test_list_values_existing(self): - ''' + """ Test the list_values function using a well known registry key - ''' + """ values = win_reg.list_values( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion') + hive="HKLM", key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion" + ) keys = [] for value in values: - keys.append(value['vname']) - self.assertIn('ProgramFilesDir', keys) + keys.append(value["vname"]) + self.assertIn("ProgramFilesDir", keys) def test_list_values_non_existing(self): - ''' + """ Test the list_values function using a non existing registry key - ''' - expected = (False, 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY)) - self.assertEqual(win_reg.list_values(hive='HKLM', key=FAKE_KEY), - expected) + """ + expected = (False, "Cannot find key: HKLM\\{0}".format(FAKE_KEY)) + self.assertEqual(win_reg.list_values(hive="HKLM", key=FAKE_KEY), expected) def test_list_values_invalid_hive(self): - ''' + """ Test the list_values function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - win_reg.list_values, - hive='BADHIVE', - key='SOFTWARE\\Microsoft') + """ + self.assertRaises( + CommandExecutionError, + win_reg.list_values, + hive="BADHIVE", + key="SOFTWARE\\Microsoft", + ) def test_list_values_unknown_key_error(self): - ''' + """ Tests the list_values function with an unknown key error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): - self.assertRaises(win32api.error, - win_reg.list_values, - hive='HKLM', - key='SOFTWARE\\Microsoft') + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): + self.assertRaises( + win32api.error, + win_reg.list_values, + hive="HKLM", + key="SOFTWARE\\Microsoft", + ) def test_read_value_existing(self): - ''' + """ Test the read_value function using a well known registry value - ''' + """ ret = win_reg.read_value( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='ProgramFilesPath') - self.assertEqual(ret['vdata'], '%ProgramFiles%') + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="ProgramFilesPath", + ) + self.assertEqual(ret["vdata"], "%ProgramFiles%") def test_read_value_default(self): - ''' + """ Test the read_value function reading the default value using a well known registry key - ''' + """ ret = win_reg.read_value( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion') - self.assertEqual(ret['vdata'], '(value not set)') + hive="HKLM", key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion" + ) + self.assertEqual(ret["vdata"], "(value not set)") def test_read_value_non_existing(self): - ''' + """ Test the read_value function using a non existing value pair - ''' + """ expected = { - 'comment': 'Cannot find fake_name in HKLM\\SOFTWARE\\Microsoft\\' - 'Windows\\CurrentVersion', - 'vdata': None, - 'vname': 'fake_name', - 'success': False, - 'hive': 'HKLM', - 'key': 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'} + "comment": "Cannot find fake_name in HKLM\\SOFTWARE\\Microsoft\\" + "Windows\\CurrentVersion", + "vdata": None, + "vname": "fake_name", + "success": False, + "hive": "HKLM", + "key": "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + } self.assertDictEqual( win_reg.read_value( - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='fake_name'), - expected) + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="fake_name", + ), + expected, + ) def test_read_value_non_existing_key(self): - ''' + """ Test the read_value function using a non existing registry key - ''' + """ expected = { - 'comment': 'Cannot find key: HKLM\\{0}'.format(FAKE_KEY), - 'vdata': None, - 'vname': 'fake_name', - 'success': False, - 'hive': 'HKLM', - 'key': FAKE_KEY} + "comment": "Cannot find key: HKLM\\{0}".format(FAKE_KEY), + "vdata": None, + "vname": "fake_name", + "success": False, + "hive": "HKLM", + "key": FAKE_KEY, + } self.assertDictEqual( - win_reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name'), - expected) + win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_name"), expected + ) def test_read_value_invalid_hive(self): - ''' + """ Test the read_value function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - win_reg.read_value, - hive='BADHIVE', - key='SOFTWARE\\Microsoft', - vname='ProgramFilesPath') + """ + self.assertRaises( + CommandExecutionError, + win_reg.read_value, + hive="BADHIVE", + key="SOFTWARE\\Microsoft", + vname="ProgramFilesPath", + ) def test_read_value_unknown_key_error(self): - ''' + """ Tests the read_value function with an unknown key error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): self.assertRaises( win32api.error, win_reg.read_value, - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='ProgramFilesPath') + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="ProgramFilesPath", + ) def test_read_value_unknown_value_error(self): - ''' + """ Tests the read_value function with an unknown value error - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegQueryValueEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegQueryValueEx', mock_error): + side_effect=win32api.error(123, "RegQueryValueEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegQueryValueEx", mock_error): self.assertRaises( win32api.error, win_reg.read_value, - hive='HKLM', - key='SOFTWARE\\Microsoft\\Windows\\CurrentVersion', - vname='ProgramFilesPath') + hive="HKLM", + key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion", + vname="ProgramFilesPath", + ) @destructiveTest def test_read_value_multi_sz_empty_list(self): - ''' + """ An empty REG_MULTI_SZ value should return an empty list, not None - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', + hive="HKLM", key=FAKE_KEY, - vname='empty_list', + vname="empty_list", vdata=[], - vtype='REG_MULTI_SZ' + vtype="REG_MULTI_SZ", ) ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': [], - 'vname': 'empty_list', - 'vtype': 'REG_MULTI_SZ' + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": [], + "vname": "empty_list", + "vtype": "REG_MULTI_SZ", } self.assertEqual( - win_reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='empty_list', - ), - expected + win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="empty_list",), + expected, ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value(self): - ''' + """ Test the set_value function - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': 'fake_data', - 'vname': 'fake_name', - 'vtype': 'REG_SZ'} + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": "fake_data", + "vname": "fake_name", + "vtype": "REG_SZ", + } self.assertEqual( - win_reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name'), - expected) + win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_name"), + expected, + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_default(self): - ''' + """ Test the set_value function on the default value - ''' + """ try: self.assertTrue( - win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vdata='fake_default_data')) + win_reg.set_value(hive="HKLM", key=FAKE_KEY, vdata="fake_default_data") + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': 'fake_default_data', - 'vname': '(Default)', - 'vtype': 'REG_SZ'} - self.assertEqual( - win_reg.read_value( - hive='HKLM', - key=FAKE_KEY), - expected) + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": "fake_default_data", + "vname": "(Default)", + "vtype": "REG_SZ", + } + self.assertEqual(win_reg.read_value(hive="HKLM", key=FAKE_KEY), expected) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_unicode_key(self): - ''' + """ Test the set_value function on a unicode key - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name', - vdata='fake_value')) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + vdata="fake_value", + ) + ) expected = { - 'hive': 'HKLM', - 'key': '\\'.join([FAKE_KEY, UNICODE_KEY]), - 'success': True, - 'vdata': 'fake_value', - 'vname': 'fake_name', - 'vtype': 'REG_SZ'} + "hive": "HKLM", + "key": "\\".join([FAKE_KEY, UNICODE_KEY]), + "success": True, + "vdata": "fake_value", + "vname": "fake_name", + "vtype": "REG_SZ", + } self.assertEqual( win_reg.read_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name'), - expected) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + ), + expected, + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_unicode_value(self): - ''' + """ Test the set_value function on a unicode value - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_unicode', - vdata=UNICODE_VALUE)) + hive="HKLM", key=FAKE_KEY, vname="fake_unicode", vdata=UNICODE_VALUE + ) + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': UNICODE_VALUE, - 'vname': 'fake_unicode', - 'vtype': 'REG_SZ'} + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": UNICODE_VALUE, + "vname": "fake_unicode", + "vtype": "REG_SZ", + } self.assertEqual( - win_reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_unicode'), - expected) + win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_unicode"), + expected, + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_reg_dword(self): - ''' + """ Test the set_value function on a REG_DWORD value - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', + hive="HKLM", key=FAKE_KEY, - vname='dword_value', + vname="dword_value", vdata=123, - vtype='REG_DWORD')) + vtype="REG_DWORD", + ) + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': 123, - 'vname': 'dword_value', - 'vtype': 'REG_DWORD'} + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": 123, + "vname": "dword_value", + "vtype": "REG_DWORD", + } self.assertEqual( - win_reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='dword_value'), - expected) + win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="dword_value"), + expected, + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_set_value_reg_qword(self): - ''' + """ Test the set_value function on a REG_QWORD value - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', + hive="HKLM", key=FAKE_KEY, - vname='qword_value', + vname="qword_value", vdata=123, - vtype='REG_QWORD')) + vtype="REG_QWORD", + ) + ) expected = { - 'hive': 'HKLM', - 'key': FAKE_KEY, - 'success': True, - 'vdata': 123, - 'vname': 'qword_value', - 'vtype': 'REG_QWORD'} + "hive": "HKLM", + "key": FAKE_KEY, + "success": True, + "vdata": 123, + "vname": "qword_value", + "vtype": "REG_QWORD", + } self.assertEqual( - win_reg.read_value( - hive='HKLM', - key=FAKE_KEY, - vname='qword_value'), - expected) + win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="qword_value"), + expected, + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) def test_set_value_invalid_hive(self): - ''' + """ Test the set_value function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - win_reg.set_value, - hive='BADHIVE', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data') + """ + self.assertRaises( + CommandExecutionError, + win_reg.set_value, + hive="BADHIVE", + key=FAKE_KEY, + vname="fake_name", + vdata="fake_data", + ) def test_set_value_open_create_failure(self): - ''' + """ Test the set_value function when there is a problem opening/creating the key - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegCreateKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegCreateKeyEx', mock_error): + side_effect=win32api.error(123, "RegCreateKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegCreateKeyEx", mock_error): self.assertFalse( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) def test_set_value_type_error(self): - ''' + """ Test the set_value function when the wrong type of data is passed - ''' - mock_error = MagicMock( - side_effect=TypeError('Mocked TypeError')) - with patch('salt.utils.win_reg.win32api.RegSetValueEx', mock_error): + """ + mock_error = MagicMock(side_effect=TypeError("Mocked TypeError")) + with patch("salt.utils.win_reg.win32api.RegSetValueEx", mock_error): self.assertFalse( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) def test_set_value_system_error(self): - ''' + """ Test the set_value function when a SystemError occurs while setting the value - ''' - mock_error = MagicMock( - side_effect=SystemError('Mocked SystemError')) - with patch('salt.utils.win_reg.win32api.RegSetValueEx', mock_error): + """ + mock_error = MagicMock(side_effect=SystemError("Mocked SystemError")) + with patch("salt.utils.win_reg.win32api.RegSetValueEx", mock_error): self.assertFalse( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) def test_set_value_value_error(self): - ''' + """ Test the set_value function when a ValueError occurs while setting the value - ''' - mock_error = MagicMock( - side_effect=ValueError('Mocked ValueError')) - with patch('salt.utils.win_reg.win32api.RegSetValueEx', mock_error): + """ + mock_error = MagicMock(side_effect=ValueError("Mocked ValueError")) + with patch("salt.utils.win_reg.win32api.RegSetValueEx", mock_error): self.assertFalse( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) def test_cast_vdata_reg_binary(self): - ''' + """ Test the cast_vdata function with REG_BINARY Should always return binary data - ''' - vdata = salt.utils.stringutils.to_bytes('test data') - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_BINARY') + """ + vdata = salt.utils.stringutils.to_bytes("test data") + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_BINARY") self.assertTrue(isinstance(result, six.binary_type)) - vdata = salt.utils.stringutils.to_str('test data') - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_BINARY') + vdata = salt.utils.stringutils.to_str("test data") + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_BINARY") self.assertTrue(isinstance(result, six.binary_type)) - vdata = salt.utils.stringutils.to_unicode('test data') - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_BINARY') + vdata = salt.utils.stringutils.to_unicode("test data") + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_BINARY") self.assertTrue(isinstance(result, six.binary_type)) def test_cast_vdata_reg_dword(self): - ''' + """ Test the cast_vdata function with REG_DWORD Should always return integer - ''' + """ vdata = 1 expected = 1 - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_DWORD') + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_DWORD") self.assertEqual(result, expected) - vdata = '1' - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_DWORD') + vdata = "1" + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_DWORD") self.assertEqual(result, expected) - vdata = '0000001' - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_DWORD') + vdata = "0000001" + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_DWORD") self.assertEqual(result, expected) def test_cast_vdata_reg_expand_sz(self): - ''' + """ Test the cast_vdata function with REG_EXPAND_SZ Should always return unicode - ''' - vdata = salt.utils.stringutils.to_str('test data') - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_EXPAND_SZ') + """ + vdata = salt.utils.stringutils.to_str("test data") + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_EXPAND_SZ") self.assertTrue(isinstance(result, six.text_type)) - vdata = salt.utils.stringutils.to_bytes('test data') - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_EXPAND_SZ') + vdata = salt.utils.stringutils.to_bytes("test data") + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_EXPAND_SZ") self.assertTrue(isinstance(result, six.text_type)) def test_cast_vdata_reg_multi_sz(self): - ''' + """ Test the cast_vdata function with REG_MULTI_SZ Should always return a list of unicode strings - ''' - vdata = [salt.utils.stringutils.to_str('test string'), - salt.utils.stringutils.to_bytes('test bytes')] - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_MULTI_SZ') + """ + vdata = [ + salt.utils.stringutils.to_str("test string"), + salt.utils.stringutils.to_bytes("test bytes"), + ] + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_MULTI_SZ") self.assertTrue(isinstance(result, list)) for item in result: self.assertTrue(isinstance(item, six.text_type)) def test_cast_vdata_reg_qword(self): - ''' + """ Test the cast_vdata function with REG_QWORD Should always return a long integer `int` is `long` is default on Py3 - ''' + """ vdata = 1 - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_QWORD') + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_QWORD") if six.PY2: - self.assertTrue(isinstance(result, long)) # pylint: disable=incompatible-py3-code,undefined-variable + # pylint: disable=incompatible-py3-code,undefined-variable + self.assertTrue(isinstance(result, long)) + # pylint: enable=incompatible-py3-code,undefined-variable else: self.assertTrue(isinstance(result, int)) - vdata = '1' - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_QWORD') + vdata = "1" + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_QWORD") if six.PY2: - self.assertTrue(isinstance(result, long)) # pylint: disable=incompatible-py3-code,undefined-variable + # pylint: disable=incompatible-py3-code,undefined-variable + self.assertTrue(isinstance(result, long)) + # pylint: enable=incompatible-py3-code,undefined-variable else: self.assertTrue(isinstance(result, int)) def test_cast_vdata_reg_sz(self): - ''' + """ Test the cast_vdata function with REG_SZ Should always return unicode - ''' - vdata = salt.utils.stringutils.to_str('test data') - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_SZ') + """ + vdata = salt.utils.stringutils.to_str("test data") + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_SZ") self.assertTrue(isinstance(result, six.text_type)) - vdata = salt.utils.stringutils.to_bytes('test data') - result = win_reg.cast_vdata(vdata=vdata, vtype='REG_SZ') + vdata = salt.utils.stringutils.to_bytes("test data") + result = win_reg.cast_vdata(vdata=vdata, vtype="REG_SZ") self.assertTrue(isinstance(result, six.text_type)) @destructiveTest def test_delete_value(self): - ''' + """ Test the delete_value function - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_data')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data" + ) + ) self.assertTrue( - win_reg.delete_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name')) + win_reg.delete_value(hive="HKLM", key=FAKE_KEY, vname="fake_name") + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) def test_delete_value_non_existing(self): - ''' + """ Test the delete_value function on non existing value - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(2, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + side_effect=win32api.error(2, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): self.assertIsNone( - win_reg.delete_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name')) + win_reg.delete_value(hive="HKLM", key=FAKE_KEY, vname="fake_name") + ) def test_delete_value_invalid_hive(self): - ''' + """ Test the delete_value function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - win_reg.delete_value, - hive='BADHIVE', - key=FAKE_KEY, - vname='fake_name') + """ + self.assertRaises( + CommandExecutionError, + win_reg.delete_value, + hive="BADHIVE", + key=FAKE_KEY, + vname="fake_name", + ) def test_delete_value_unknown_error(self): - ''' + """ Test the delete_value function when there is a problem opening the key - ''' + """ mock_error = MagicMock( - side_effect=win32api.error(123, 'RegOpenKeyEx', 'Unknown error')) - with patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): - self.assertRaises(win32api.error, - win_reg.delete_value, - hive='HKLM', - key=FAKE_KEY, - vname='fake_name') + side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error") + ) + with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error): + self.assertRaises( + win32api.error, + win_reg.delete_value, + hive="HKLM", + key=FAKE_KEY, + vname="fake_name", + ) @destructiveTest def test_delete_value_unicode(self): - ''' + """ Test the delete_value function on a unicode value - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_unicode', - vdata=UNICODE_VALUE)) + hive="HKLM", key=FAKE_KEY, vname="fake_unicode", vdata=UNICODE_VALUE + ) + ) self.assertTrue( - win_reg.delete_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_unicode')) + win_reg.delete_value(hive="HKLM", key=FAKE_KEY, vname="fake_unicode") + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_value_unicode_vname(self): - ''' + """ Test the delete_value function on a unicode vname - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname=UNICODE_KEY, - vdata='junk data')) + hive="HKLM", key=FAKE_KEY, vname=UNICODE_KEY, vdata="junk data" + ) + ) self.assertTrue( - win_reg.delete_value( - hive='HKLM', - key=FAKE_KEY, - vname=UNICODE_KEY)) + win_reg.delete_value(hive="HKLM", key=FAKE_KEY, vname=UNICODE_KEY) + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_value_unicode_key(self): - ''' + """ Test the delete_value function on a unicode key - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name', - vdata='junk data')) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + vdata="junk data", + ) + ) self.assertTrue( win_reg.delete_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name')) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + ) + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) def test_delete_key_recursive_invalid_hive(self): - ''' + """ Test the delete_key_recursive function when passing an invalid hive - ''' - self.assertRaises(CommandExecutionError, - win_reg.delete_key_recursive, - hive='BADHIVE', - key=FAKE_KEY) + """ + self.assertRaises( + CommandExecutionError, + win_reg.delete_key_recursive, + hive="BADHIVE", + key=FAKE_KEY, + ) def test_delete_key_recursive_key_not_found(self): - ''' + """ Test the delete_key_recursive function when the passed key to delete is not found. - ''' - self.assertFalse( - win_reg.key_exists(hive='HKLM', key=FAKE_KEY)) - self.assertFalse( - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY)) + """ + self.assertFalse(win_reg.key_exists(hive="HKLM", key=FAKE_KEY)) + self.assertFalse(win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)) def test_delete_key_recursive_too_close(self): - ''' + """ Test the delete_key_recursive function when the passed key to delete is too close to root, such as - ''' + """ mock_true = MagicMock(return_value=True) - with patch('salt.utils.win_reg.key_exists', mock_true): - self.assertFalse( - win_reg.delete_key_recursive(hive='HKLM', key='FAKE_KEY')) + with patch("salt.utils.win_reg.key_exists", mock_true): + self.assertFalse(win_reg.delete_key_recursive(hive="HKLM", key="FAKE_KEY")) @destructiveTest def test_delete_key_recursive(self): - ''' + """ Test the delete_key_recursive function - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_value')) - expected = { - 'Deleted': ['\\'.join(['HKLM', FAKE_KEY])], - 'Failed': []} + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_value" + ) + ) + expected = {"Deleted": ["\\".join(["HKLM", FAKE_KEY])], "Failed": []} self.assertDictEqual( - win_reg.delete_key_recursive( - hive='HKLM', - key=FAKE_KEY), - expected) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY), expected + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_key_recursive_failed_to_open_key(self): - ''' + """ Test the delete_key_recursive function on failure to open the key - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_value')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_value" + ) + ) expected = { - 'Deleted': [], - 'Failed': ['\\'.join(['HKLM', FAKE_KEY]) + - ' Failed to connect to key']} + "Deleted": [], + "Failed": ["\\".join(["HKLM", FAKE_KEY]) + " Failed to connect to key"], + } mock_true = MagicMock(return_value=True) mock_error = MagicMock( side_effect=[ 1, - win32api.error(3, 'RegOpenKeyEx', - 'Failed to connect to key') - ]) - with patch('salt.utils.win_reg.key_exists', mock_true), \ - patch('salt.utils.win_reg.win32api.RegOpenKeyEx', mock_error): + win32api.error(3, "RegOpenKeyEx", "Failed to connect to key"), + ] + ) + with patch("salt.utils.win_reg.key_exists", mock_true), patch( + "salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error + ): self.assertDictEqual( - win_reg.delete_key_recursive( - hive='HKLM', - key=FAKE_KEY), - expected) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY), expected + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_key_recursive_failed_to_delete(self): - ''' + """ Test the delete_key_recursive function on failure to delete a key - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key=FAKE_KEY, - vname='fake_name', - vdata='fake_value')) + hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_value" + ) + ) expected = { - 'Deleted': [], - 'Failed': ['\\'.join(['HKLM', FAKE_KEY]) + ' Unknown error']} - mock_error = MagicMock(side_effect=WindowsError('Unknown error')) # pylint: disable=undefined-variable - with patch('salt.utils.win_reg.win32api.RegDeleteKey', mock_error): + "Deleted": [], + "Failed": ["\\".join(["HKLM", FAKE_KEY]) + " Unknown error"], + } + # pylint: disable=undefined-variable + mock_error = MagicMock(side_effect=WindowsError("Unknown error")) + # pylint: enable=undefined-variable + with patch("salt.utils.win_reg.win32api.RegDeleteKey", mock_error): self.assertDictEqual( - win_reg.delete_key_recursive( - hive='HKLM', - key=FAKE_KEY), - expected) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY), expected + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) @destructiveTest def test_delete_key_recursive_unicode(self): - ''' + """ Test the delete_key_recursive function on value within a unicode key - ''' + """ try: self.assertTrue( win_reg.set_value( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY]), - vname='fake_name', - vdata='fake_value')) + hive="HKLM", + key="\\".join([FAKE_KEY, UNICODE_KEY]), + vname="fake_name", + vdata="fake_value", + ) + ) expected = { - 'Deleted': ['\\'.join(['HKLM', FAKE_KEY, UNICODE_KEY])], - 'Failed': []} + "Deleted": ["\\".join(["HKLM", FAKE_KEY, UNICODE_KEY])], + "Failed": [], + } self.assertDictEqual( win_reg.delete_key_recursive( - hive='HKLM', - key='\\'.join([FAKE_KEY, UNICODE_KEY])), - expected) + hive="HKLM", key="\\".join([FAKE_KEY, UNICODE_KEY]) + ), + expected, + ) finally: - win_reg.delete_key_recursive(hive='HKLM', key=FAKE_KEY) + win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY) def test__to_unicode_int(self): - ''' + """ Test the ``_to_unicode`` function when it receives an integer value. Should return a unicode value, which is unicode in PY2 and str in PY3. - ''' + """ if six.PY3: self.assertTrue(isinstance(win_reg._to_unicode(1), str)) else: - self.assertTrue(isinstance(win_reg._to_unicode(1), unicode)) # pylint: disable=incompatible-py3-code,undefined-variable + # fmt: off + self.assertTrue( + isinstance( + win_reg._to_unicode(1), + unicode, # pylint: disable=incompatible-py3-code,undefined-variable + ) + ) + # fmt: on diff --git a/tests/unit/utils/test_xmlutil.py b/tests/unit/utils/test_xmlutil.py index c04f39498ec..30a2d650383 100644 --- a/tests/unit/utils/test_xmlutil.py +++ b/tests/unit/utils/test_xmlutil.py @@ -1,148 +1,173 @@ # -*- coding: utf-8 -*- -''' +""" tests.unit.xmlutil_test ~~~~~~~~~~~~~~~~~~~~ -''' +""" from __future__ import absolute_import, print_function, unicode_literals -# Import Salt Testing libs -from tests.support.unit import TestCase + +import salt.utils.xmlutil as xml # Import Salt libs from salt._compat import ElementTree as ET -import salt.utils.xmlutil as xml + +# Import Salt Testing libs +from tests.support.unit import TestCase class XMLUtilTestCase(TestCase): - ''' + """ Tests that salt.utils.xmlutil properly parses XML data and returns as a properly formatted dictionary. The default method of parsing will ignore attributes and return only the child items. The full method will include parsing attributes. - ''' + """ def setUp(self): # Populate our use cases for specific XML formats. self.cases = { - 'a': { - 'xml': '<parent>data</parent>', - 'legacy': {'parent': 'data'}, - 'full': 'data' + "a": { + "xml": "<parent>data</parent>", + "legacy": {"parent": "data"}, + "full": "data", }, - 'b': { - 'xml': '<parent value="data">data</parent>', - 'legacy': {'parent': 'data'}, - 'full': {'parent': 'data', 'value': 'data'} + "b": { + "xml": '<parent value="data">data</parent>', + "legacy": {"parent": "data"}, + "full": {"parent": "data", "value": "data"}, }, - 'c': { - 'xml': '<parent><child>data</child><child value="data">data</child>' - '<child value="data"/><child/></parent>', - 'legacy': {'child': ['data', {'child': 'data'}, {'child': None}, {'child': None}]}, - 'full': {'child': ['data', {'child': 'data', 'value': 'data'}, {'value': 'data'}, None]} + "c": { + "xml": '<parent><child>data</child><child value="data">data</child>' + '<child value="data"/><child/></parent>', + "legacy": { + "child": [ + "data", + {"child": "data"}, + {"child": None}, + {"child": None}, + ] + }, + "full": { + "child": [ + "data", + {"child": "data", "value": "data"}, + {"value": "data"}, + None, + ] + }, }, - 'd': { - 'xml': '<parent value="data" another="data"><child>data</child></parent>', - 'legacy': {'child': 'data'}, - 'full': {'child': 'data', 'another': 'data', 'value': 'data'} + "d": { + "xml": '<parent value="data" another="data"><child>data</child></parent>', + "legacy": {"child": "data"}, + "full": {"child": "data", "another": "data", "value": "data"}, }, - 'e': { - 'xml': '<parent value="data" another="data"><child value="data">data</child></parent>', - 'legacy': {'child': 'data'}, - 'full': {'child': {'child': 'data', 'value': 'data'}, 'another': 'data', 'value': 'data'} + "e": { + "xml": '<parent value="data" another="data"><child value="data">data</child></parent>', + "legacy": {"child": "data"}, + "full": { + "child": {"child": "data", "value": "data"}, + "another": "data", + "value": "data", + }, }, - 'f': { - 'xml': '<parent><child><sub-child value="data">data</sub-child></child>' - '<child>data</child></parent>', - 'legacy': {'child': [{'sub-child': 'data'}, {'child': 'data'}]}, - 'full': {'child': [{'sub-child': {'value': 'data', 'sub-child': 'data'}}, 'data']} + "f": { + "xml": '<parent><child><sub-child value="data">data</sub-child></child>' + "<child>data</child></parent>", + "legacy": {"child": [{"sub-child": "data"}, {"child": "data"}]}, + "full": { + "child": [ + {"sub-child": {"value": "data", "sub-child": "data"}}, + "data", + ] + }, }, } def test_xml_case_a(self): - xmldata = ET.fromstring(self.cases['a']['xml']) + xmldata = ET.fromstring(self.cases["a"]["xml"]) defaultdict = xml.to_dict(xmldata) - self.assertEqual(defaultdict, self.cases['a']['legacy']) + self.assertEqual(defaultdict, self.cases["a"]["legacy"]) def test_xml_case_a_legacy(self): - xmldata = ET.fromstring(self.cases['a']['xml']) + xmldata = ET.fromstring(self.cases["a"]["xml"]) defaultdict = xml.to_dict(xmldata, False) - self.assertEqual(defaultdict, self.cases['a']['legacy']) + self.assertEqual(defaultdict, self.cases["a"]["legacy"]) def test_xml_case_a_full(self): - xmldata = ET.fromstring(self.cases['a']['xml']) + xmldata = ET.fromstring(self.cases["a"]["xml"]) defaultdict = xml.to_dict(xmldata, True) - self.assertEqual(defaultdict, self.cases['a']['full']) + self.assertEqual(defaultdict, self.cases["a"]["full"]) def test_xml_case_b(self): - xmldata = ET.fromstring(self.cases['b']['xml']) + xmldata = ET.fromstring(self.cases["b"]["xml"]) defaultdict = xml.to_dict(xmldata) - self.assertEqual(defaultdict, self.cases['b']['legacy']) + self.assertEqual(defaultdict, self.cases["b"]["legacy"]) def test_xml_case_b_legacy(self): - xmldata = ET.fromstring(self.cases['b']['xml']) + xmldata = ET.fromstring(self.cases["b"]["xml"]) defaultdict = xml.to_dict(xmldata, False) - self.assertEqual(defaultdict, self.cases['b']['legacy']) + self.assertEqual(defaultdict, self.cases["b"]["legacy"]) def test_xml_case_b_full(self): - xmldata = ET.fromstring(self.cases['b']['xml']) + xmldata = ET.fromstring(self.cases["b"]["xml"]) defaultdict = xml.to_dict(xmldata, True) - self.assertEqual(defaultdict, self.cases['b']['full']) + self.assertEqual(defaultdict, self.cases["b"]["full"]) def test_xml_case_c(self): - xmldata = ET.fromstring(self.cases['c']['xml']) + xmldata = ET.fromstring(self.cases["c"]["xml"]) defaultdict = xml.to_dict(xmldata) - self.assertEqual(defaultdict, self.cases['c']['legacy']) + self.assertEqual(defaultdict, self.cases["c"]["legacy"]) def test_xml_case_c_legacy(self): - xmldata = ET.fromstring(self.cases['c']['xml']) + xmldata = ET.fromstring(self.cases["c"]["xml"]) defaultdict = xml.to_dict(xmldata, False) - self.assertEqual(defaultdict, self.cases['c']['legacy']) + self.assertEqual(defaultdict, self.cases["c"]["legacy"]) def test_xml_case_c_full(self): - xmldata = ET.fromstring(self.cases['c']['xml']) + xmldata = ET.fromstring(self.cases["c"]["xml"]) defaultdict = xml.to_dict(xmldata, True) - self.assertEqual(defaultdict, self.cases['c']['full']) + self.assertEqual(defaultdict, self.cases["c"]["full"]) def test_xml_case_d(self): - xmldata = ET.fromstring(self.cases['d']['xml']) + xmldata = ET.fromstring(self.cases["d"]["xml"]) defaultdict = xml.to_dict(xmldata) - self.assertEqual(defaultdict, self.cases['d']['legacy']) + self.assertEqual(defaultdict, self.cases["d"]["legacy"]) def test_xml_case_d_legacy(self): - xmldata = ET.fromstring(self.cases['d']['xml']) + xmldata = ET.fromstring(self.cases["d"]["xml"]) defaultdict = xml.to_dict(xmldata, False) - self.assertEqual(defaultdict, self.cases['d']['legacy']) + self.assertEqual(defaultdict, self.cases["d"]["legacy"]) def test_xml_case_d_full(self): - xmldata = ET.fromstring(self.cases['d']['xml']) + xmldata = ET.fromstring(self.cases["d"]["xml"]) defaultdict = xml.to_dict(xmldata, True) - self.assertEqual(defaultdict, self.cases['d']['full']) + self.assertEqual(defaultdict, self.cases["d"]["full"]) def test_xml_case_e(self): - xmldata = ET.fromstring(self.cases['e']['xml']) + xmldata = ET.fromstring(self.cases["e"]["xml"]) defaultdict = xml.to_dict(xmldata) - self.assertEqual(defaultdict, self.cases['e']['legacy']) + self.assertEqual(defaultdict, self.cases["e"]["legacy"]) def test_xml_case_e_legacy(self): - xmldata = ET.fromstring(self.cases['e']['xml']) + xmldata = ET.fromstring(self.cases["e"]["xml"]) defaultdict = xml.to_dict(xmldata, False) - self.assertEqual(defaultdict, self.cases['e']['legacy']) + self.assertEqual(defaultdict, self.cases["e"]["legacy"]) def test_xml_case_e_full(self): - xmldata = ET.fromstring(self.cases['e']['xml']) + xmldata = ET.fromstring(self.cases["e"]["xml"]) defaultdict = xml.to_dict(xmldata, True) - self.assertEqual(defaultdict, self.cases['e']['full']) + self.assertEqual(defaultdict, self.cases["e"]["full"]) def test_xml_case_f(self): - xmldata = ET.fromstring(self.cases['f']['xml']) + xmldata = ET.fromstring(self.cases["f"]["xml"]) defaultdict = xml.to_dict(xmldata) - self.assertEqual(defaultdict, self.cases['f']['legacy']) + self.assertEqual(defaultdict, self.cases["f"]["legacy"]) def test_xml_case_f_legacy(self): - xmldata = ET.fromstring(self.cases['f']['xml']) + xmldata = ET.fromstring(self.cases["f"]["xml"]) defaultdict = xml.to_dict(xmldata, False) - self.assertEqual(defaultdict, self.cases['f']['legacy']) + self.assertEqual(defaultdict, self.cases["f"]["legacy"]) def test_xml_case_f_full(self): - xmldata = ET.fromstring(self.cases['f']['xml']) + xmldata = ET.fromstring(self.cases["f"]["xml"]) defaultdict = xml.to_dict(xmldata, True) - self.assertEqual(defaultdict, self.cases['f']['full']) + self.assertEqual(defaultdict, self.cases["f"]["full"]) diff --git a/tests/unit/utils/test_yamldumper.py b/tests/unit/utils/test_yamldumper.py index 7168f48eccc..3a9b14b4db2 100644 --- a/tests/unit/utils/test_yamldumper.py +++ b/tests/unit/utils/test_yamldumper.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for salt.utils.yamldumper -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals @@ -15,29 +15,35 @@ from tests.support.unit import TestCase class YamlDumperTestCase(TestCase): - ''' + """ TestCase for salt.utils.yamldumper module - ''' + """ + def test_yaml_dump(self): - ''' + """ Test yaml.dump a dict - ''' - data = {'foo': 'bar'} + """ + data = {"foo": "bar"} if salt.ext.six.PY2: - exp_yaml = '{!!python/unicode \'foo\': !!python/unicode \'bar\'}\n' + exp_yaml = "{!!python/unicode 'foo': !!python/unicode 'bar'}\n" else: - exp_yaml = '{foo: bar}\n' + exp_yaml = "{foo: bar}\n" assert salt.utils.yamldumper.dump(data) == exp_yaml - assert salt.utils.yamldumper.dump(data, default_flow_style=False) == exp_yaml.replace('{', '').replace('}', '') + assert salt.utils.yamldumper.dump( + data, default_flow_style=False + ) == exp_yaml.replace("{", "").replace("}", "") def test_yaml_safe_dump(self): - ''' + """ Test yaml.safe_dump a dict - ''' - data = {'foo': 'bar'} - assert salt.utils.yamldumper.safe_dump(data) == '{foo: bar}\n' + """ + data = {"foo": "bar"} + assert salt.utils.yamldumper.safe_dump(data) == "{foo: bar}\n" - assert salt.utils.yamldumper.safe_dump(data, default_flow_style=False) == 'foo: bar\n' + assert ( + salt.utils.yamldumper.safe_dump(data, default_flow_style=False) + == "foo: bar\n" + ) diff --git a/tests/unit/utils/test_yamlencoding.py b/tests/unit/utils/test_yamlencoding.py index 7eb2c876482..add28624b0d 100644 --- a/tests/unit/utils/test_yamlencoding.py +++ b/tests/unit/utils/test_yamlencoding.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -''' +""" Tests for salt.utils.yamlencoding -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals @@ -13,14 +13,16 @@ from tests.support.unit import TestCase class YamlEncodingTestCase(TestCase): - def test_yaml_dquote(self): for teststr in (r'"\ []{}"',): - self.assertEqual(teststr, salt.utils.yaml.safe_load(salt.utils.yamlencoding.yaml_dquote(teststr))) + self.assertEqual( + teststr, + salt.utils.yaml.safe_load(salt.utils.yamlencoding.yaml_dquote(teststr)), + ) def test_yaml_dquote_doesNotAddNewLines(self): teststr = '"' * 100 - self.assertNotIn('\n', salt.utils.yamlencoding.yaml_dquote(teststr)) + self.assertNotIn("\n", salt.utils.yamlencoding.yaml_dquote(teststr)) def test_yaml_squote(self): ret = salt.utils.yamlencoding.yaml_squote(r'"') @@ -28,11 +30,23 @@ class YamlEncodingTestCase(TestCase): def test_yaml_squote_doesNotAddNewLines(self): teststr = "'" * 100 - self.assertNotIn('\n', salt.utils.yamlencoding.yaml_squote(teststr)) + self.assertNotIn("\n", salt.utils.yamlencoding.yaml_squote(teststr)) def test_yaml_encode(self): - for testobj in (None, True, False, '[7, 5]', '"monkey"', 5, 7.5, "2014-06-02 15:30:29.7"): - self.assertEqual(testobj, salt.utils.yaml.safe_load(salt.utils.yamlencoding.yaml_encode(testobj))) + for testobj in ( + None, + True, + False, + "[7, 5]", + '"monkey"', + 5, + 7.5, + "2014-06-02 15:30:29.7", + ): + self.assertEqual( + testobj, + salt.utils.yaml.safe_load(salt.utils.yamlencoding.yaml_encode(testobj)), + ) for testobj in ({}, [], set()): self.assertRaises(TypeError, salt.utils.yamlencoding.yaml_encode, testobj) diff --git a/tests/unit/utils/test_yamlloader.py b/tests/unit/utils/test_yamlloader.py index 65d3850d584..a1e17af7603 100644 --- a/tests/unit/utils/test_yamlloader.py +++ b/tests/unit/utils/test_yamlloader.py @@ -1,38 +1,39 @@ # -*- coding: utf-8 -*- -''' +""" Unit tests for salt.utils.yamlloader.SaltYamlSafeLoader -''' +""" # Import python libs from __future__ import absolute_import, print_function, unicode_literals + import collections import textwrap -# Import Salt Libs -from yaml.constructor import ConstructorError -from salt.utils.yamlloader import SaltYamlSafeLoader import salt.utils.files -from salt.ext import six - -# Import Salt Testing Libs -from tests.support.unit import TestCase -from tests.support.mock import patch, mock_open # Import 3rd-party libs from salt.ext import six +from salt.utils.yamlloader import SaltYamlSafeLoader +from tests.support.mock import mock_open, patch + +# Import Salt Testing Libs +from tests.support.unit import TestCase + +# Import Salt Libs +from yaml.constructor import ConstructorError class YamlLoaderTestCase(TestCase): - ''' + """ TestCase for salt.utils.yamlloader module - ''' + """ @staticmethod def render_yaml(data): - ''' + """ Takes a YAML string, puts it into a mock file, passes that to the YAML SaltYamlSafeLoader and then returns the rendered/parsed YAML data - ''' + """ if six.PY2: # On Python 2, data read from a filehandle will not already be # unicode, so we need to encode it first to properly simulate @@ -40,18 +41,18 @@ class YamlLoaderTestCase(TestCase): # and all of the data to be used in mock_open will be a unicode # type. Encoding will make it a str. data = salt.utils.data.encode(data) - with patch('salt.utils.files.fopen', mock_open(read_data=data)) as mocked_file: + with patch("salt.utils.files.fopen", mock_open(read_data=data)) as mocked_file: with salt.utils.files.fopen(mocked_file) as mocked_stream: return SaltYamlSafeLoader(mocked_stream).get_data() @staticmethod def raise_error(value): - raise TypeError('{0!r} is not a unicode string'.format(value)) # pylint: disable=repr-flag-used-in-string + raise TypeError("{0!r} is not a unicode string".format(value)) def assert_unicode(self, value): - ''' + """ Make sure the entire data structure is unicode - ''' + """ if six.PY3: return if isinstance(value, six.string_types): @@ -70,81 +71,109 @@ class YamlLoaderTestCase(TestCase): self.assert_unicode(ret) def test_yaml_basics(self): - ''' + """ Test parsing an ordinary path - ''' + """ self.assert_matches( - self.render_yaml(textwrap.dedent('''\ + self.render_yaml( + textwrap.dedent( + """\ p1: - alpha - - beta''')), - {'p1': ['alpha', 'beta']} + - beta""" + ) + ), + {"p1": ["alpha", "beta"]}, ) def test_yaml_merge(self): - ''' + """ Test YAML anchors - ''' + """ # Simple merge test self.assert_matches( - self.render_yaml(textwrap.dedent('''\ + self.render_yaml( + textwrap.dedent( + """\ p1: &p1 v1: alpha p2: <<: *p1 - v2: beta''')), - {'p1': {'v1': 'alpha'}, 'p2': {'v1': 'alpha', 'v2': 'beta'}} + v2: beta""" + ) + ), + {"p1": {"v1": "alpha"}, "p2": {"v1": "alpha", "v2": "beta"}}, ) # Test that keys/nodes are overwritten self.assert_matches( - self.render_yaml(textwrap.dedent('''\ + self.render_yaml( + textwrap.dedent( + """\ p1: &p1 v1: alpha p2: <<: *p1 - v1: new_alpha''')), - {'p1': {'v1': 'alpha'}, 'p2': {'v1': 'new_alpha'}} + v1: new_alpha""" + ) + ), + {"p1": {"v1": "alpha"}, "p2": {"v1": "new_alpha"}}, ) # Test merging of lists self.assert_matches( - self.render_yaml(textwrap.dedent('''\ + self.render_yaml( + textwrap.dedent( + """\ p1: &p1 v1: &v1 - t1 - t2 p2: - v2: *v1''')), - {"p2": {"v2": ["t1", "t2"]}, "p1": {"v1": ["t1", "t2"]}} + v2: *v1""" + ) + ), + {"p2": {"v2": ["t1", "t2"]}, "p1": {"v1": ["t1", "t2"]}}, ) def test_yaml_duplicates(self): - ''' + """ Test that duplicates still throw an error - ''' + """ with self.assertRaises(ConstructorError): - self.render_yaml(textwrap.dedent('''\ + self.render_yaml( + textwrap.dedent( + """\ p1: alpha - p1: beta''')) + p1: beta""" + ) + ) with self.assertRaises(ConstructorError): - self.render_yaml(textwrap.dedent('''\ + self.render_yaml( + textwrap.dedent( + """\ p1: &p1 v1: alpha p2: <<: *p1 v2: beta - v2: betabeta''')) + v2: betabeta""" + ) + ) def test_yaml_with_plain_scalars(self): - ''' + """ Test that plain (i.e. unqoted) string and non-string scalars are properly handled - ''' + """ self.assert_matches( - self.render_yaml(textwrap.dedent('''\ + self.render_yaml( + textwrap.dedent( + """\ foo: - b: {foo: bar, one: 1, list: [1, two, 3]}''')), - {'foo': {'b': {'foo': 'bar', 'one': 1, 'list': [1, 'two', 3]}}} + b: {foo: bar, one: 1, list: [1, two, 3]}""" + ) + ), + {"foo": {"b": {"foo": "bar", "one": 1, "list": [1, "two", 3]}}}, ) diff --git a/tests/unit/utils/test_zeromq.py b/tests/unit/utils/test_zeromq.py index 28745b85094..19439004fa7 100644 --- a/tests/unit/utils/test_zeromq.py +++ b/tests/unit/utils/test_zeromq.py @@ -1,40 +1,46 @@ # -*- coding: utf-8 -*- -''' +""" Test salt.utils.zeromq -''' +""" # Import Python libs from __future__ import absolute_import, print_function, unicode_literals -import zmq -from salt._compat import ipaddress - -# Import Salt Testing libs -from tests.support.unit import TestCase, skipIf -from tests.support.mock import ( - patch, -) # Import salt libs import salt.utils.zeromq +import zmq +from salt._compat import ipaddress from salt.exceptions import SaltSystemExit +from tests.support.mock import patch + +# Import Salt Testing libs +from tests.support.unit import TestCase, skipIf class UtilsTestCase(TestCase): def test_ip_bracket(self): - test_ipv4 = '127.0.0.1' - test_ipv6 = '::1' - test_ipv6_uri = '[::1]' + test_ipv4 = "127.0.0.1" + test_ipv6 = "::1" + test_ipv6_uri = "[::1]" self.assertEqual(test_ipv4, salt.utils.zeromq.ip_bracket(test_ipv4)) - self.assertEqual('[{0}]'.format(test_ipv6), salt.utils.zeromq.ip_bracket(test_ipv6)) - self.assertEqual('[{0}]'.format(test_ipv6), salt.utils.zeromq.ip_bracket(test_ipv6_uri)) + self.assertEqual( + "[{0}]".format(test_ipv6), salt.utils.zeromq.ip_bracket(test_ipv6) + ) + self.assertEqual( + "[{0}]".format(test_ipv6), salt.utils.zeromq.ip_bracket(test_ipv6_uri) + ) ip_addr_obj = ipaddress.ip_address(test_ipv4) self.assertEqual(test_ipv4, salt.utils.zeromq.ip_bracket(ip_addr_obj)) - @skipIf(not hasattr(zmq, 'IPC_PATH_MAX_LEN'), "ZMQ does not have max length support.") + @skipIf( + not hasattr(zmq, "IPC_PATH_MAX_LEN"), "ZMQ does not have max length support." + ) def test_check_ipc_length(self): - ''' + """ Ensure we throw an exception if we have a too-long IPC URI - ''' - with patch('zmq.IPC_PATH_MAX_LEN', 1): - self.assertRaises(SaltSystemExit, salt.utils.zeromq.check_ipc_path_max_len, '1' * 1024) + """ + with patch("zmq.IPC_PATH_MAX_LEN", 1): + self.assertRaises( + SaltSystemExit, salt.utils.zeromq.check_ipc_path_max_len, "1" * 1024 + ) diff --git a/tests/unit/utils/test_zfs.py b/tests/unit/utils/test_zfs.py index d94b06815b7..2bd42e71ad0 100644 --- a/tests/unit/utils/test_zfs.py +++ b/tests/unit/utils/test_zfs.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -''' +""" Tests for the zfs utils library :codeauthor: Jorge Schrauwen <sjorge@blackdot.be> @@ -8,31 +8,29 @@ Tests for the zfs utils library :platform: illumos,freebsd,linux .. versionadded:: 2018.3.1 -''' +""" # Import Python libs -from __future__ import absolute_import, unicode_literals, print_function - -# Import Salt Testing libs -from tests.support.zfs import ZFSMockData -from tests.support.unit import TestCase -from tests.support.mock import ( - MagicMock, - patch, -) +from __future__ import absolute_import, print_function, unicode_literals # Import Salt Execution module to test import salt.utils.zfs as zfs # Import Salt Utils from salt.utils.odict import OrderedDict +from tests.support.mock import MagicMock, patch +from tests.support.unit import TestCase + +# Import Salt Testing libs +from tests.support.zfs import ZFSMockData # Skip this test case if we don't have access to mock! class ZfsUtilsTestCase(TestCase): - ''' + """ This class contains a set of functions that test salt.utils.zfs utils - ''' + """ + def setUp(self): # property_map mocks mock_data = ZFSMockData() @@ -40,835 +38,1042 @@ class ZfsUtilsTestCase(TestCase): self.pmap_zpool = mock_data.pmap_zpool self.pmap_exec_zfs = mock_data.pmap_exec_zfs self.pmap_exec_zpool = mock_data.pmap_exec_zpool - for name in ('pmap_zfs', 'pmap_zpool', 'pmap_exec_zfs', 'pmap_exec_zpool'): + for name in ("pmap_zfs", "pmap_zpool", "pmap_exec_zfs", "pmap_exec_zpool"): self.addCleanup(delattr, self, name) # NOTE: test parameter parsing def test_is_supported(self): - ''' + """ Test zfs.is_supported method - ''' + """ for value in [False, True]: - with patch('salt.utils.path.which', - MagicMock(return_value=value)): - with patch('salt.utils.platform.is_linux', - MagicMock(return_value=value)): + with patch("salt.utils.path.which", MagicMock(return_value=value)): + with patch( + "salt.utils.platform.is_linux", MagicMock(return_value=value) + ): self.assertEqual(value, zfs.is_supported()) def test_property_data_zpool(self): - ''' + """ Test parsing of zpool get output - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, '_exec', MagicMock(return_value=self.pmap_exec_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "_exec", MagicMock(return_value=self.pmap_exec_zpool) + ): self.assertEqual(zfs.property_data_zpool(), self.pmap_zpool) def test_property_data_zfs(self): - ''' + """ Test parsing of zfs get output - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, '_exec', MagicMock(return_value=self.pmap_exec_zfs)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "_exec", MagicMock(return_value=self.pmap_exec_zfs) + ): self.assertEqual(zfs.property_data_zfs(), self.pmap_zfs) # NOTE: testing from_bool results def test_from_bool_on(self): - ''' + """ Test from_bool with 'on' - ''' - self.assertTrue(zfs.from_bool('on')) - self.assertTrue(zfs.from_bool(zfs.from_bool('on'))) + """ + self.assertTrue(zfs.from_bool("on")) + self.assertTrue(zfs.from_bool(zfs.from_bool("on"))) def test_from_bool_off(self): - ''' + """ Test from_bool with 'off' - ''' - self.assertFalse(zfs.from_bool('off')) - self.assertFalse(zfs.from_bool(zfs.from_bool('off'))) + """ + self.assertFalse(zfs.from_bool("off")) + self.assertFalse(zfs.from_bool(zfs.from_bool("off"))) def test_from_bool_none(self): - ''' + """ Test from_bool with 'none' - ''' - self.assertEqual(zfs.from_bool('none'), None) - self.assertEqual(zfs.from_bool(zfs.from_bool('none')), None) + """ + self.assertEqual(zfs.from_bool("none"), None) + self.assertEqual(zfs.from_bool(zfs.from_bool("none")), None) def test_from_bool_passthrough(self): - ''' + """ Test from_bool with 'passthrough' - ''' - self.assertEqual(zfs.from_bool('passthrough'), 'passthrough') - self.assertEqual(zfs.from_bool(zfs.from_bool('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.from_bool("passthrough"), "passthrough") + self.assertEqual(zfs.from_bool(zfs.from_bool("passthrough")), "passthrough") def test_from_bool_alt_yes(self): - ''' + """ Test from_bool_alt with 'yes' - ''' - self.assertTrue(zfs.from_bool_alt('yes')) - self.assertTrue(zfs.from_bool_alt(zfs.from_bool_alt('yes'))) + """ + self.assertTrue(zfs.from_bool_alt("yes")) + self.assertTrue(zfs.from_bool_alt(zfs.from_bool_alt("yes"))) def test_from_bool_alt_no(self): - ''' + """ Test from_bool_alt with 'no' - ''' - self.assertFalse(zfs.from_bool_alt('no')) - self.assertFalse(zfs.from_bool_alt(zfs.from_bool_alt('no'))) + """ + self.assertFalse(zfs.from_bool_alt("no")) + self.assertFalse(zfs.from_bool_alt(zfs.from_bool_alt("no"))) def test_from_bool_alt_none(self): - ''' + """ Test from_bool_alt with 'none' - ''' - self.assertEqual(zfs.from_bool_alt('none'), None) - self.assertEqual(zfs.from_bool_alt(zfs.from_bool_alt('none')), None) + """ + self.assertEqual(zfs.from_bool_alt("none"), None) + self.assertEqual(zfs.from_bool_alt(zfs.from_bool_alt("none")), None) def test_from_bool_alt_passthrough(self): - ''' + """ Test from_bool_alt with 'passthrough' - ''' - self.assertEqual(zfs.from_bool_alt('passthrough'), 'passthrough') - self.assertEqual(zfs.from_bool_alt(zfs.from_bool_alt('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.from_bool_alt("passthrough"), "passthrough") + self.assertEqual( + zfs.from_bool_alt(zfs.from_bool_alt("passthrough")), "passthrough" + ) # NOTE: testing to_bool results def test_to_bool_true(self): - ''' + """ Test to_bool with True - ''' - self.assertEqual(zfs.to_bool(True), 'on') - self.assertEqual(zfs.to_bool(zfs.to_bool(True)), 'on') + """ + self.assertEqual(zfs.to_bool(True), "on") + self.assertEqual(zfs.to_bool(zfs.to_bool(True)), "on") def test_to_bool_false(self): - ''' + """ Test to_bool with False - ''' - self.assertEqual(zfs.to_bool(False), 'off') - self.assertEqual(zfs.to_bool(zfs.to_bool(False)), 'off') + """ + self.assertEqual(zfs.to_bool(False), "off") + self.assertEqual(zfs.to_bool(zfs.to_bool(False)), "off") def test_to_bool_none(self): - ''' + """ Test to_bool with None - ''' - self.assertEqual(zfs.to_bool(None), 'none') - self.assertEqual(zfs.to_bool(zfs.to_bool(None)), 'none') + """ + self.assertEqual(zfs.to_bool(None), "none") + self.assertEqual(zfs.to_bool(zfs.to_bool(None)), "none") def test_to_bool_passthrough(self): - ''' + """ Test to_bool with 'passthrough' - ''' - self.assertEqual(zfs.to_bool('passthrough'), 'passthrough') - self.assertEqual(zfs.to_bool(zfs.to_bool('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.to_bool("passthrough"), "passthrough") + self.assertEqual(zfs.to_bool(zfs.to_bool("passthrough")), "passthrough") def test_to_bool_alt_true(self): - ''' + """ Test to_bool_alt with True - ''' - self.assertEqual(zfs.to_bool_alt(True), 'yes') - self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt(True)), 'yes') + """ + self.assertEqual(zfs.to_bool_alt(True), "yes") + self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt(True)), "yes") def test_to_bool_alt_false(self): - ''' + """ Test to_bool_alt with False - ''' - self.assertEqual(zfs.to_bool_alt(False), 'no') - self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt(False)), 'no') + """ + self.assertEqual(zfs.to_bool_alt(False), "no") + self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt(False)), "no") def test_to_bool_alt_none(self): - ''' + """ Test to_bool_alt with None - ''' - self.assertEqual(zfs.to_bool_alt(None), 'none') - self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt(None)), 'none') + """ + self.assertEqual(zfs.to_bool_alt(None), "none") + self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt(None)), "none") def test_to_bool_alt_passthrough(self): - ''' + """ Test to_bool_alt with 'passthrough' - ''' - self.assertEqual(zfs.to_bool_alt('passthrough'), 'passthrough') - self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.to_bool_alt("passthrough"), "passthrough") + self.assertEqual(zfs.to_bool_alt(zfs.to_bool_alt("passthrough")), "passthrough") # NOTE: testing from_numeric results def test_from_numeric_str(self): - ''' + """ Test from_numeric with '42' - ''' - self.assertEqual(zfs.from_numeric('42'), 42) - self.assertEqual(zfs.from_numeric(zfs.from_numeric('42')), 42) + """ + self.assertEqual(zfs.from_numeric("42"), 42) + self.assertEqual(zfs.from_numeric(zfs.from_numeric("42")), 42) def test_from_numeric_int(self): - ''' + """ Test from_numeric with 42 - ''' + """ self.assertEqual(zfs.from_numeric(42), 42) self.assertEqual(zfs.from_numeric(zfs.from_numeric(42)), 42) def test_from_numeric_none(self): - ''' + """ Test from_numeric with 'none' - ''' - self.assertEqual(zfs.from_numeric('none'), None) - self.assertEqual(zfs.from_numeric(zfs.from_numeric('none')), None) + """ + self.assertEqual(zfs.from_numeric("none"), None) + self.assertEqual(zfs.from_numeric(zfs.from_numeric("none")), None) def test_from_numeric_passthrough(self): - ''' + """ Test from_numeric with 'passthrough' - ''' - self.assertEqual(zfs.from_numeric('passthrough'), 'passthrough') - self.assertEqual(zfs.from_numeric(zfs.from_numeric('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.from_numeric("passthrough"), "passthrough") + self.assertEqual( + zfs.from_numeric(zfs.from_numeric("passthrough")), "passthrough" + ) # NOTE: testing to_numeric results def test_to_numeric_str(self): - ''' + """ Test to_numeric with '42' - ''' - self.assertEqual(zfs.to_numeric('42'), 42) - self.assertEqual(zfs.to_numeric(zfs.to_numeric('42')), 42) + """ + self.assertEqual(zfs.to_numeric("42"), 42) + self.assertEqual(zfs.to_numeric(zfs.to_numeric("42")), 42) def test_to_numeric_int(self): - ''' + """ Test to_numeric with 42 - ''' + """ self.assertEqual(zfs.to_numeric(42), 42) self.assertEqual(zfs.to_numeric(zfs.to_numeric(42)), 42) def test_to_numeric_none(self): - ''' + """ Test to_numeric with 'none' - ''' - self.assertEqual(zfs.to_numeric(None), 'none') - self.assertEqual(zfs.to_numeric(zfs.to_numeric(None)), 'none') + """ + self.assertEqual(zfs.to_numeric(None), "none") + self.assertEqual(zfs.to_numeric(zfs.to_numeric(None)), "none") def test_to_numeric_passthrough(self): - ''' + """ Test to_numeric with 'passthrough' - ''' - self.assertEqual(zfs.to_numeric('passthrough'), 'passthrough') - self.assertEqual(zfs.to_numeric(zfs.to_numeric('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.to_numeric("passthrough"), "passthrough") + self.assertEqual(zfs.to_numeric(zfs.to_numeric("passthrough")), "passthrough") # NOTE: testing from_size results def test_from_size_absolute(self): - ''' + """ Test from_size with '5G' - ''' - self.assertEqual(zfs.from_size('5G'), 5368709120) - self.assertEqual(zfs.from_size(zfs.from_size('5G')), 5368709120) + """ + self.assertEqual(zfs.from_size("5G"), 5368709120) + self.assertEqual(zfs.from_size(zfs.from_size("5G")), 5368709120) def test_from_size_decimal(self): - ''' + """ Test from_size with '4.20M' - ''' - self.assertEqual(zfs.from_size('4.20M'), 4404019) - self.assertEqual(zfs.from_size(zfs.from_size('4.20M')), 4404019) + """ + self.assertEqual(zfs.from_size("4.20M"), 4404019) + self.assertEqual(zfs.from_size(zfs.from_size("4.20M")), 4404019) def test_from_size_none(self): - ''' + """ Test from_size with 'none' - ''' - self.assertEqual(zfs.from_size('none'), None) - self.assertEqual(zfs.from_size(zfs.from_size('none')), None) + """ + self.assertEqual(zfs.from_size("none"), None) + self.assertEqual(zfs.from_size(zfs.from_size("none")), None) def test_from_size_passthrough(self): - ''' + """ Test from_size with 'passthrough' - ''' - self.assertEqual(zfs.from_size('passthrough'), 'passthrough') - self.assertEqual(zfs.from_size(zfs.from_size('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.from_size("passthrough"), "passthrough") + self.assertEqual(zfs.from_size(zfs.from_size("passthrough")), "passthrough") # NOTE: testing to_size results def test_to_size_str_absolute(self): - ''' + """ Test to_size with '5368709120' - ''' - self.assertEqual(zfs.to_size('5368709120'), '5G') - self.assertEqual(zfs.to_size(zfs.to_size('5368709120')), '5G') + """ + self.assertEqual(zfs.to_size("5368709120"), "5G") + self.assertEqual(zfs.to_size(zfs.to_size("5368709120")), "5G") def test_to_size_str_decimal(self): - ''' + """ Test to_size with '4404019' - ''' - self.assertEqual(zfs.to_size('4404019'), '4.20M') - self.assertEqual(zfs.to_size(zfs.to_size('4404019')), '4.20M') + """ + self.assertEqual(zfs.to_size("4404019"), "4.20M") + self.assertEqual(zfs.to_size(zfs.to_size("4404019")), "4.20M") def test_to_size_int_absolute(self): - ''' + """ Test to_size with 5368709120 - ''' - self.assertEqual(zfs.to_size(5368709120), '5G') - self.assertEqual(zfs.to_size(zfs.to_size(5368709120)), '5G') + """ + self.assertEqual(zfs.to_size(5368709120), "5G") + self.assertEqual(zfs.to_size(zfs.to_size(5368709120)), "5G") def test_to_size_int_decimal(self): - ''' + """ Test to_size with 4404019 - ''' - self.assertEqual(zfs.to_size(4404019), '4.20M') - self.assertEqual(zfs.to_size(zfs.to_size(4404019)), '4.20M') + """ + self.assertEqual(zfs.to_size(4404019), "4.20M") + self.assertEqual(zfs.to_size(zfs.to_size(4404019)), "4.20M") def test_to_size_none(self): - ''' + """ Test to_size with 'none' - ''' - self.assertEqual(zfs.to_size(None), 'none') - self.assertEqual(zfs.to_size(zfs.to_size(None)), 'none') + """ + self.assertEqual(zfs.to_size(None), "none") + self.assertEqual(zfs.to_size(zfs.to_size(None)), "none") def test_to_size_passthrough(self): - ''' + """ Test to_size with 'passthrough' - ''' - self.assertEqual(zfs.to_size('passthrough'), 'passthrough') - self.assertEqual(zfs.to_size(zfs.to_size('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.to_size("passthrough"), "passthrough") + self.assertEqual(zfs.to_size(zfs.to_size("passthrough")), "passthrough") # NOTE: testing from_str results def test_from_str_space(self): - ''' + """ Test from_str with "\"my pool/my dataset\" - ''' - self.assertEqual(zfs.from_str('"my pool/my dataset"'), 'my pool/my dataset') - self.assertEqual(zfs.from_str(zfs.from_str('"my pool/my dataset"')), 'my pool/my dataset') + """ + self.assertEqual(zfs.from_str('"my pool/my dataset"'), "my pool/my dataset") + self.assertEqual( + zfs.from_str(zfs.from_str('"my pool/my dataset"')), "my pool/my dataset" + ) def test_from_str_squote_space(self): - ''' + """ Test from_str with "my pool/jorge's dataset" - ''' - self.assertEqual(zfs.from_str("my pool/jorge's dataset"), "my pool/jorge's dataset") - self.assertEqual(zfs.from_str(zfs.from_str("my pool/jorge's dataset")), "my pool/jorge's dataset") + """ + self.assertEqual( + zfs.from_str("my pool/jorge's dataset"), "my pool/jorge's dataset" + ) + self.assertEqual( + zfs.from_str(zfs.from_str("my pool/jorge's dataset")), + "my pool/jorge's dataset", + ) def test_from_str_dquote_space(self): - ''' + """ Test from_str with "my pool/the \"good\" stuff" - ''' - self.assertEqual(zfs.from_str("my pool/the \"good\" stuff"), 'my pool/the "good" stuff') - self.assertEqual(zfs.from_str(zfs.from_str("my pool/the \"good\" stuff")), 'my pool/the "good" stuff') + """ + self.assertEqual( + zfs.from_str('my pool/the "good" stuff'), 'my pool/the "good" stuff' + ) + self.assertEqual( + zfs.from_str(zfs.from_str('my pool/the "good" stuff')), + 'my pool/the "good" stuff', + ) def test_from_str_none(self): - ''' + """ Test from_str with 'none' - ''' - self.assertEqual(zfs.from_str('none'), None) - self.assertEqual(zfs.from_str(zfs.from_str('none')), None) + """ + self.assertEqual(zfs.from_str("none"), None) + self.assertEqual(zfs.from_str(zfs.from_str("none")), None) def test_from_str_passthrough(self): - ''' + """ Test from_str with 'passthrough' - ''' - self.assertEqual(zfs.from_str('passthrough'), 'passthrough') - self.assertEqual(zfs.from_str(zfs.from_str('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.from_str("passthrough"), "passthrough") + self.assertEqual(zfs.from_str(zfs.from_str("passthrough")), "passthrough") # NOTE: testing to_str results def test_to_str_space(self): - ''' + """ Test to_str with 'my pool/my dataset' - ''' + """ # NOTE: for fun we use both the '"str"' and "\"str\"" way of getting the literal string: "str" - self.assertEqual(zfs.to_str('my pool/my dataset'), '"my pool/my dataset"') - self.assertEqual(zfs.to_str(zfs.to_str('my pool/my dataset')), "\"my pool/my dataset\"") + self.assertEqual(zfs.to_str("my pool/my dataset"), '"my pool/my dataset"') + self.assertEqual( + zfs.to_str(zfs.to_str("my pool/my dataset")), '"my pool/my dataset"' + ) def test_to_str_squote_space(self): - ''' + """ Test to_str with "my pool/jorge's dataset" - ''' - self.assertEqual(zfs.to_str("my pool/jorge's dataset"), "\"my pool/jorge's dataset\"") - self.assertEqual(zfs.to_str(zfs.to_str("my pool/jorge's dataset")), "\"my pool/jorge's dataset\"") + """ + self.assertEqual( + zfs.to_str("my pool/jorge's dataset"), '"my pool/jorge\'s dataset"' + ) + self.assertEqual( + zfs.to_str(zfs.to_str("my pool/jorge's dataset")), + '"my pool/jorge\'s dataset"', + ) def test_to_str_none(self): - ''' + """ Test to_str with 'none' - ''' - self.assertEqual(zfs.to_str(None), 'none') - self.assertEqual(zfs.to_str(zfs.to_str(None)), 'none') + """ + self.assertEqual(zfs.to_str(None), "none") + self.assertEqual(zfs.to_str(zfs.to_str(None)), "none") def test_to_str_passthrough(self): - ''' + """ Test to_str with 'passthrough' - ''' - self.assertEqual(zfs.to_str('passthrough'), 'passthrough') - self.assertEqual(zfs.to_str(zfs.to_str('passthrough')), 'passthrough') + """ + self.assertEqual(zfs.to_str("passthrough"), "passthrough") + self.assertEqual(zfs.to_str(zfs.to_str("passthrough")), "passthrough") # NOTE: testing is_snapshot def test_is_snapshot_snapshot(self): - ''' + """ Test is_snapshot with a valid snapshot name - ''' - self.assertTrue(zfs.is_snapshot('zpool_name/dataset@backup')) + """ + self.assertTrue(zfs.is_snapshot("zpool_name/dataset@backup")) def test_is_snapshot_bookmark(self): - ''' + """ Test is_snapshot with a valid bookmark name - ''' - self.assertFalse(zfs.is_snapshot('zpool_name/dataset#backup')) + """ + self.assertFalse(zfs.is_snapshot("zpool_name/dataset#backup")) def test_is_snapshot_filesystem(self): - ''' + """ Test is_snapshot with a valid filesystem name - ''' - self.assertFalse(zfs.is_snapshot('zpool_name/dataset')) + """ + self.assertFalse(zfs.is_snapshot("zpool_name/dataset")) # NOTE: testing is_bookmark def test_is_bookmark_snapshot(self): - ''' + """ Test is_bookmark with a valid snapshot name - ''' - self.assertFalse(zfs.is_bookmark('zpool_name/dataset@backup')) + """ + self.assertFalse(zfs.is_bookmark("zpool_name/dataset@backup")) def test_is_bookmark_bookmark(self): - ''' + """ Test is_bookmark with a valid bookmark name - ''' - self.assertTrue(zfs.is_bookmark('zpool_name/dataset#backup')) + """ + self.assertTrue(zfs.is_bookmark("zpool_name/dataset#backup")) def test_is_bookmark_filesystem(self): - ''' + """ Test is_bookmark with a valid filesystem name - ''' - self.assertFalse(zfs.is_bookmark('zpool_name/dataset')) + """ + self.assertFalse(zfs.is_bookmark("zpool_name/dataset")) # NOTE: testing is_dataset def test_is_dataset_snapshot(self): - ''' + """ Test is_dataset with a valid snapshot name - ''' - self.assertFalse(zfs.is_dataset('zpool_name/dataset@backup')) + """ + self.assertFalse(zfs.is_dataset("zpool_name/dataset@backup")) def test_is_dataset_bookmark(self): - ''' + """ Test is_dataset with a valid bookmark name - ''' - self.assertFalse(zfs.is_dataset('zpool_name/dataset#backup')) + """ + self.assertFalse(zfs.is_dataset("zpool_name/dataset#backup")) def test_is_dataset_filesystem(self): - ''' + """ Test is_dataset with a valid filesystem/volume name - ''' - self.assertTrue(zfs.is_dataset('zpool_name/dataset')) + """ + self.assertTrue(zfs.is_dataset("zpool_name/dataset")) # NOTE: testing zfs_command def test_zfs_command_simple(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): - self.assertEqual( - zfs.zfs_command('list'), - "/sbin/zfs list" - ) + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): + self.assertEqual(zfs.zfs_command("list"), "/sbin/zfs list") def test_zfs_command_none_target(self): - ''' + """ Test if zfs_command builds the correct string with a target of None - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): self.assertEqual( - zfs.zfs_command('list', target=[None, 'mypool', None]), - "/sbin/zfs list mypool" + zfs.zfs_command("list", target=[None, "mypool", None]), + "/sbin/zfs list mypool", ) def test_zfs_command_flag(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_flags = [ - '-r', # recursive + "-r", # recursive ] self.assertEqual( - zfs.zfs_command('list', flags=my_flags), - "/sbin/zfs list -r" + zfs.zfs_command("list", flags=my_flags), "/sbin/zfs list -r" ) def test_zfs_command_opt(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_opts = { - '-t': 'snap', # only list snapshots + "-t": "snap", # only list snapshots } self.assertEqual( - zfs.zfs_command('list', opts=my_opts), - "/sbin/zfs list -t snap" + zfs.zfs_command("list", opts=my_opts), + "/sbin/zfs list -t snap", ) def test_zfs_command_flag_opt(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_flags = [ - '-r', # recursive + "-r", # recursive ] my_opts = { - '-t': 'snap', # only list snapshots + "-t": "snap", # only list snapshots } self.assertEqual( - zfs.zfs_command('list', flags=my_flags, opts=my_opts), - "/sbin/zfs list -r -t snap" + zfs.zfs_command("list", flags=my_flags, opts=my_opts), + "/sbin/zfs list -r -t snap", ) def test_zfs_command_target(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_flags = [ - '-r', # recursive + "-r", # recursive ] my_opts = { - '-t': 'snap', # only list snapshots + "-t": "snap", # only list snapshots } self.assertEqual( - zfs.zfs_command('list', flags=my_flags, opts=my_opts, target='mypool'), - "/sbin/zfs list -r -t snap mypool" + zfs.zfs_command( + "list", flags=my_flags, opts=my_opts, target="mypool" + ), + "/sbin/zfs list -r -t snap mypool", ) def test_zfs_command_target_with_space(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_flags = [ - '-r', # recursive + "-r", # recursive ] my_opts = { - '-t': 'snap', # only list snapshots + "-t": "snap", # only list snapshots } self.assertEqual( - zfs.zfs_command('list', flags=my_flags, opts=my_opts, target='my pool'), - '/sbin/zfs list -r -t snap "my pool"' + zfs.zfs_command( + "list", flags=my_flags, opts=my_opts, target="my pool" + ), + '/sbin/zfs list -r -t snap "my pool"', ) def test_zfs_command_property(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): self.assertEqual( - zfs.zfs_command('get', property_name='quota', target='mypool'), - "/sbin/zfs get quota mypool" + zfs.zfs_command( + "get", property_name="quota", target="mypool" + ), + "/sbin/zfs get quota mypool", ) def test_zfs_command_property_value(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_flags = [ - '-r', # recursive + "-r", # recursive ] self.assertEqual( - zfs.zfs_command('set', flags=my_flags, property_name='quota', property_value='5G', target='mypool'), - "/sbin/zfs set -r quota=5368709120 mypool" + zfs.zfs_command( + "set", + flags=my_flags, + property_name="quota", + property_value="5G", + target="mypool", + ), + "/sbin/zfs set -r quota=5368709120 mypool", ) def test_zfs_command_multi_property_value(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): - property_name = ['quota', 'readonly'] - property_value = ['5G', 'no'] + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): + property_name = ["quota", "readonly"] + property_value = ["5G", "no"] self.assertEqual( - zfs.zfs_command('set', property_name=property_name, property_value=property_value, target='mypool'), - "/sbin/zfs set quota=5368709120 readonly=off mypool" + zfs.zfs_command( + "set", + property_name=property_name, + property_value=property_value, + target="mypool", + ), + "/sbin/zfs set quota=5368709120 readonly=off mypool", ) def test_zfs_command_fs_props(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_flags = [ - '-p', # create parent + "-p", # create parent ] my_props = { - 'quota': '1G', - 'compression': 'lz4', + "quota": "1G", + "compression": "lz4", } self.assertEqual( - zfs.zfs_command('create', flags=my_flags, filesystem_properties=my_props, target='mypool/dataset'), - "/sbin/zfs create -p -o compression=lz4 -o quota=1073741824 mypool/dataset" + zfs.zfs_command( + "create", + flags=my_flags, + filesystem_properties=my_props, + target="mypool/dataset", + ), + "/sbin/zfs create -p -o compression=lz4 -o quota=1073741824 mypool/dataset", ) def test_zfs_command_fs_props_with_space(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_props = { - 'quota': '4.2M', - 'compression': 'lz4', + "quota": "4.2M", + "compression": "lz4", } self.assertEqual( - zfs.zfs_command('create', filesystem_properties=my_props, target="my pool/jorge's dataset"), - '/sbin/zfs create -o compression=lz4 -o quota=4404019 "my pool/jorge\'s dataset"' + zfs.zfs_command( + "create", + filesystem_properties=my_props, + target="my pool/jorge's dataset", + ), + '/sbin/zfs create -o compression=lz4 -o quota=4404019 "my pool/jorge\'s dataset"', ) # NOTE: testing zpool_command def test_zpool_command_simple(self): - ''' + """ Test if zfs_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): - self.assertEqual( - zfs.zpool_command('list'), - "/sbin/zpool list" - ) + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): + self.assertEqual(zfs.zpool_command("list"), "/sbin/zpool list") def test_zpool_command_opt(self): - ''' + """ Test if zpool_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_opts = { - '-o': 'name,size', # show only name and size + "-o": "name,size", # show only name and size } self.assertEqual( - zfs.zpool_command('list', opts=my_opts), - "/sbin/zpool list -o name,size" + zfs.zpool_command("list", opts=my_opts), + "/sbin/zpool list -o name,size", ) def test_zpool_command_opt_list(self): - ''' + """ Test if zpool_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_opts = { - '-d': ['/tmp', '/zvol'], + "-d": ["/tmp", "/zvol"], } self.assertEqual( - zfs.zpool_command('import', opts=my_opts, target='mypool'), - "/sbin/zpool import -d /tmp -d /zvol mypool" + zfs.zpool_command("import", opts=my_opts, target="mypool"), + "/sbin/zpool import -d /tmp -d /zvol mypool", ) def test_zpool_command_flag_opt(self): - ''' + """ Test if zpool_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_opts = { - '-o': 'name,size', # show only name and size + "-o": "name,size", # show only name and size } self.assertEqual( - zfs.zpool_command('list', opts=my_opts), - "/sbin/zpool list -o name,size" + zfs.zpool_command("list", opts=my_opts), + "/sbin/zpool list -o name,size", ) def test_zpool_command_target(self): - ''' + """ Test if zpool_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_opts = { - '-o': 'name,size', # show only name and size + "-o": "name,size", # show only name and size } self.assertEqual( - zfs.zpool_command('list', opts=my_opts, target='mypool'), - "/sbin/zpool list -o name,size mypool" + zfs.zpool_command("list", opts=my_opts, target="mypool"), + "/sbin/zpool list -o name,size mypool", ) def test_zpool_command_target_with_space(self): - ''' + """ Test if zpool_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): fs_props = { - 'quota': '100G', + "quota": "100G", } pool_props = { - 'comment': "jorge's comment has a space", + "comment": "jorge's comment has a space", } self.assertEqual( - zfs.zpool_command('create', pool_properties=pool_props, filesystem_properties=fs_props, target='my pool'), - "/sbin/zpool create -O quota=107374182400 -o comment=\"jorge's comment has a space\" \"my pool\"" + zfs.zpool_command( + "create", + pool_properties=pool_props, + filesystem_properties=fs_props, + target="my pool", + ), + '/sbin/zpool create -O quota=107374182400 -o comment="jorge\'s comment has a space" "my pool"', ) def test_zpool_command_property(self): - ''' + """ Test if zpool_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): self.assertEqual( - zfs.zpool_command('get', property_name='comment', target='mypool'), - "/sbin/zpool get comment mypool" + zfs.zpool_command( + "get", property_name="comment", target="mypool" + ), + "/sbin/zpool get comment mypool", ) def test_zpool_command_property_value(self): - ''' + """ Test if zpool_command builds the correct string - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): my_flags = [ - '-v', # verbose + "-v", # verbose ] self.assertEqual( - zfs.zpool_command('iostat', flags=my_flags, target=['mypool', 60, 1]), - "/sbin/zpool iostat -v mypool 60 1" + zfs.zpool_command( + "iostat", flags=my_flags, target=["mypool", 60, 1] + ), + "/sbin/zpool iostat -v mypool 60 1", ) def test_parse_command_result_success(self): - ''' + """ Test if parse_command_result returns the expected result - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): res = {} - res['retcode'] = 0 - res['stderr'] = '' - res['stdout'] = '' + res["retcode"] = 0 + res["stderr"] = "" + res["stdout"] = "" self.assertEqual( - zfs.parse_command_result(res, 'tested'), - OrderedDict([('tested', True)]), + zfs.parse_command_result(res, "tested"), + OrderedDict([("tested", True)]), ) def test_parse_command_result_success_nolabel(self): - ''' + """ Test if parse_command_result returns the expected result - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): res = {} - res['retcode'] = 0 - res['stderr'] = '' - res['stdout'] = '' + res["retcode"] = 0 + res["stderr"] = "" + res["stdout"] = "" self.assertEqual( - zfs.parse_command_result(res), - OrderedDict(), + zfs.parse_command_result(res), OrderedDict(), ) def test_parse_command_result_fail(self): - ''' + """ Test if parse_command_result returns the expected result on failure - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): res = {} - res['retcode'] = 1 - res['stderr'] = '' - res['stdout'] = '' + res["retcode"] = 1 + res["stderr"] = "" + res["stdout"] = "" self.assertEqual( - zfs.parse_command_result(res, 'tested'), - OrderedDict([('tested', False)]), + zfs.parse_command_result(res, "tested"), + OrderedDict([("tested", False)]), ) def test_parse_command_result_nolabel(self): - ''' + """ Test if parse_command_result returns the expected result on failure - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): res = {} - res['retcode'] = 1 - res['stderr'] = '' - res['stdout'] = '' + res["retcode"] = 1 + res["stderr"] = "" + res["stdout"] = "" self.assertEqual( - zfs.parse_command_result(res), - OrderedDict(), + zfs.parse_command_result(res), OrderedDict(), ) def test_parse_command_result_fail_message(self): - ''' + """ Test if parse_command_result returns the expected result on failure with stderr - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): res = {} - res['retcode'] = 1 - res['stderr'] = "\n".join([ - 'ice is not hot', - 'usage:', - 'this should not be printed', - ]) - res['stdout'] = '' + res["retcode"] = 1 + res["stderr"] = "\n".join( + ["ice is not hot", "usage:", "this should not be printed"] + ) + res["stdout"] = "" self.assertEqual( - zfs.parse_command_result(res, 'tested'), - OrderedDict([('tested', False), ('error', 'ice is not hot')]), + zfs.parse_command_result(res, "tested"), + OrderedDict( + [("tested", False), ("error", "ice is not hot")] + ), ) def test_parse_command_result_fail_message_nolabel(self): - ''' + """ Test if parse_command_result returns the expected result on failure with stderr - ''' - with patch.object(zfs, '_zfs_cmd', MagicMock(return_value='/sbin/zfs')): - with patch.object(zfs, '_zpool_cmd', MagicMock(return_value='/sbin/zpool')): - with patch.object(zfs, 'property_data_zfs', MagicMock(return_value=self.pmap_zfs)): - with patch.object(zfs, 'property_data_zpool', MagicMock(return_value=self.pmap_zpool)): + """ + with patch.object(zfs, "_zfs_cmd", MagicMock(return_value="/sbin/zfs")): + with patch.object(zfs, "_zpool_cmd", MagicMock(return_value="/sbin/zpool")): + with patch.object( + zfs, "property_data_zfs", MagicMock(return_value=self.pmap_zfs) + ): + with patch.object( + zfs, + "property_data_zpool", + MagicMock(return_value=self.pmap_zpool), + ): res = {} - res['retcode'] = 1 - res['stderr'] = "\n".join([ - 'ice is not hot', - 'usage:', - 'this should not be printed', - ]) - res['stdout'] = '' + res["retcode"] = 1 + res["stderr"] = "\n".join( + ["ice is not hot", "usage:", "this should not be printed"] + ) + res["stdout"] = "" self.assertEqual( zfs.parse_command_result(res), - OrderedDict([('error', 'ice is not hot')]), + OrderedDict([("error", "ice is not hot")]), ) diff --git a/tests/unit/utils/validate/test_net.py b/tests/unit/utils/validate/test_net.py index debbcab76f4..81203900c83 100644 --- a/tests/unit/utils/validate/test_net.py +++ b/tests/unit/utils/validate/test_net.py @@ -11,30 +11,30 @@ from tests.support.unit import TestCase class ValidateNetTestCase(TestCase): - ''' + """ TestCase for salt.utils.validate.net module - ''' + """ def test_ipv4_addr(self): - ''' + """ Test IPv4 address validation - ''' + """ true_addrs = [ - '127.0.0.1', - '127.0.0.1', - '127.0.0.19', - '1.1.1.1/28', - '127.0.0.11/32', + "127.0.0.1", + "127.0.0.1", + "127.0.0.19", + "1.1.1.1/28", + "127.0.0.11/32", ] false_addrs = [ - '127.0.0.911', - '127.0.0911', - '127.0.011', - '127.0.011/32', - '::1', - '::1/128', - '::1/28', + "127.0.0.911", + "127.0.0911", + "127.0.011", + "127.0.011/32", + "::1", + "::1/128", + "::1/28", ] for addr in true_addrs: @@ -44,25 +44,25 @@ class ValidateNetTestCase(TestCase): self.assertFalse(net.ipv4_addr(addr)) def test_ipv6_addr(self): - ''' + """ Test IPv6 address validation - ''' + """ true_addrs = [ - '::', - '::1', - '::1/32', - '::1/32', - '::1/128', - '2a03:4000:c:10aa:1017:f00d:aaaa:a', + "::", + "::1", + "::1/32", + "::1/32", + "::1/128", + "2a03:4000:c:10aa:1017:f00d:aaaa:a", ] false_addrs = [ - '1.1.1.1', - '::1/0', - '::1/32d', - '::1/129', - '2a03:4000:c:10aa:1017:f00d:aaaa:a:4506', - '2a03::1::2', + "1.1.1.1", + "::1/0", + "::1/32d", + "::1/129", + "2a03:4000:c:10aa:1017:f00d:aaaa:a:4506", + "2a03::1::2", ] for addr in true_addrs: diff --git a/tests/utils/test_ipaddress.py b/tests/utils/test_ipaddress.py index e5da311abde..67dd2c954f3 100644 --- a/tests/utils/test_ipaddress.py +++ b/tests/utils/test_ipaddress.py @@ -1,15 +1,15 @@ -''' +""" Python 2.[67] port of Python 3.4's test_ipaddress. Almost verbatim copy of core lib w/compatibility fixes -''' +""" # pylint: skip-file # List of compatibility changes: # This backport uses bytearray instead of bytes, as bytes is the same # as str in Python 2.7. -#bytes = bytearray +# bytes = bytearray # s/\(b'[^']\+'\)/bytearray(\1)/g # plus manual fixes for implicit string concatenation. @@ -31,22 +31,24 @@ Almost verbatim copy of core lib w/compatibility fixes """Unittest for ipaddress module.""" -# Import python libs -import re -import sys import contextlib import operator + +# Import python libs +import re import sys # Import salt libs from salt._compat import ipaddress + # Import salt test libs from tests.support.unit import TestCase, skipIf if sys.version_info < (3,): bytes = bytearray -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') + +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class BaseTestCase(TestCase): # One big change in ipaddress over the original ipaddr module is # error reporting that tries to assume users *don't know the rules* @@ -85,18 +87,16 @@ class BaseTestCase(TestCase): # Compatibility: Python 2.7 does not support exception chaining ## Ensure we produce clean tracebacks on failure - #if exc.exception.__context__ is not None: + # if exc.exception.__context__ is not None: # self.assertTrue(exc.exception.__suppress_context__) def assertAddressError(self, details, *args): """Ensure a clean AddressValueError""" - return self.assertCleanError(ipaddress.AddressValueError, - details, *args) + return self.assertCleanError(ipaddress.AddressValueError, details, *args) def assertNetmaskError(self, details, *args): """Ensure a clean NetmaskValueError""" - return self.assertCleanError(ipaddress.NetmaskValueError, - details, *args) + return self.assertCleanError(ipaddress.NetmaskValueError, details, *args) def assertInstancesEqual(self, lhs, rhs): """Check constructor arguments produce equivalent instances""" @@ -104,7 +104,6 @@ class BaseTestCase(TestCase): class CommonTestMixin: - def test_empty_address(self): with self.assertAddressError("Address cannot be empty"): self.factory("") @@ -122,7 +121,6 @@ class CommonTestMixin: class CommonTestMixin_v4(CommonTestMixin): - def test_leading_zeros(self): self.assertInstancesEqual("000.000.000.000", "0.0.0.0") self.assertInstancesEqual("192.168.000.001", "192.168.0.1") @@ -142,8 +140,8 @@ class CommonTestMixin_v4(CommonTestMixin): def test_large_ints_rejected(self): msg = "%d (>= 2**32) is not permitted as an IPv4 address" - with self.assertAddressError(re.escape(msg % 2**32)): - self.factory(2**32) + with self.assertAddressError(re.escape(msg % 2 ** 32)): + self.factory(2 ** 32) def test_bad_packed_length(self): def assertBadLength(length): @@ -157,7 +155,6 @@ class CommonTestMixin_v4(CommonTestMixin): class CommonTestMixin_v6(CommonTestMixin): - def test_leading_zeros(self): self.assertInstancesEqual("0000::0000", "::") self.assertInstancesEqual("000::c0a8:0001", "::c0a8:1") @@ -181,8 +178,8 @@ class CommonTestMixin_v6(CommonTestMixin): def test_large_ints_rejected(self): msg = "%d (>= 2**128) is not permitted as an IPv6 address" - with self.assertAddressError(re.escape(msg % 2**128)): - self.factory(2**128) + with self.assertAddressError(re.escape(msg % 2 ** 128)): + self.factory(2 ** 128) def test_bad_packed_length(self): def assertBadLength(length): @@ -196,7 +193,7 @@ class CommonTestMixin_v6(CommonTestMixin): assertBadLength(17) -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class AddressTestCase_v4(BaseTestCase, CommonTestMixin_v4): factory = ipaddress.IPv4Address @@ -236,8 +233,7 @@ class AddressTestCase_v4(BaseTestCase, CommonTestMixin_v4): def test_empty_octet(self): def assertBadOctet(addr): - with self.assertAddressError("Empty octet not permitted in %r", - addr): + with self.assertAddressError("Empty octet not permitted in %r", addr): ipaddress.IPv4Address(addr) assertBadOctet("42..42.42") @@ -287,7 +283,7 @@ class AddressTestCase_v4(BaseTestCase, CommonTestMixin_v4): assertBadOctet("192.168.0.999", 999) -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6): factory = ipaddress.IPv6Address @@ -355,7 +351,7 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6): assertBadSplit("3ffe::1::1:") assertBadSplit(":3ffe::1::1:") assertBadSplit(":::") - assertBadSplit('2001:db8:::1') + assertBadSplit("2001:db8:::1") def test_bad_address_split_v6_leading_colon(self): def assertBadSplit(addr): @@ -385,15 +381,13 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6): ipaddress.IPv6Address(addr) assertBadAddressPart("3ffe::1.net", "Expected 4 octets in '1.net'") - assertBadAddressPart("3ffe::127.0.1", - "Expected 4 octets in '127.0.1'") - assertBadAddressPart("::1.2.3", - "Expected 4 octets in '1.2.3'") - assertBadAddressPart("::1.2.3.4.5", - "Expected 4 octets in '1.2.3.4.5'") - assertBadAddressPart("3ffe::1.1.1.net", - "Only decimal digits permitted in 'net' " - "in '1.1.1.net'") + assertBadAddressPart("3ffe::127.0.1", "Expected 4 octets in '127.0.1'") + assertBadAddressPart("::1.2.3", "Expected 4 octets in '1.2.3'") + assertBadAddressPart("::1.2.3.4.5", "Expected 4 octets in '1.2.3.4.5'") + assertBadAddressPart( + "3ffe::1.1.1.net", + "Only decimal digits permitted in 'net' " "in '1.1.1.net'", + ) def test_invalid_characters(self): def assertBadPart(addr, part): @@ -406,7 +400,7 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6): assertBadPart("3ffe::+0", "+0") assertBadPart("3ffe::-1", "-1") assertBadPart("1.2.3.4::", "1.2.3.4") - assertBadPart('1234:axy::b', "axy") + assertBadPart("1234:axy::b", "axy") def test_part_length(self): def assertBadPart(addr, part): @@ -417,10 +411,10 @@ class AddressTestCase_v6(BaseTestCase, CommonTestMixin_v6): assertBadPart("::00000", "00000") assertBadPart("3ffe::10000", "10000") assertBadPart("02001:db8::", "02001") - assertBadPart('2001:888888::1', "888888") + assertBadPart("2001:888888::1", "888888") -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class NetmaskTestMixin_v4(CommonTestMixin_v4): """Input validation on interfaces and networks is very similar""" @@ -443,24 +437,21 @@ class NetmaskTestMixin_v4(CommonTestMixin_v4): assertBadAddress("1.2.3.256", re.escape("256 (> 255)")) def test_valid_netmask(self): - self.assertEqual(str(self.factory('192.0.2.0/255.255.255.0')), - '192.0.2.0/24') + self.assertEqual(str(self.factory("192.0.2.0/255.255.255.0")), "192.0.2.0/24") for i in range(0, 33): # Generate and re-parse the CIDR format (trivial). - net_str = '0.0.0.0/%d' % i + net_str = "0.0.0.0/%d" % i net = self.factory(net_str) self.assertEqual(str(net), net_str) # Generate and re-parse the expanded netmask. - self.assertEqual( - str(self.factory('0.0.0.0/%s' % net.netmask)), net_str) + self.assertEqual(str(self.factory("0.0.0.0/%s" % net.netmask)), net_str) # Zero prefix is treated as decimal. - self.assertEqual(str(self.factory('0.0.0.0/0%d' % i)), net_str) + self.assertEqual(str(self.factory("0.0.0.0/0%d" % i)), net_str) # Generate and re-parse the expanded hostmask. The ambiguous # cases (/0 and /32) are treated as netmasks. if i in (32, 0): - net_str = '0.0.0.0/%d' % (32 - i) - self.assertEqual( - str(self.factory('0.0.0.0/%s' % net.hostmask)), net_str) + net_str = "0.0.0.0/%d" % (32 - i) + self.assertEqual(str(self.factory("0.0.0.0/%s" % net.hostmask)), net_str) def test_netmask_errors(self): def assertBadNetmask(addr, netmask): @@ -484,17 +475,17 @@ class NetmaskTestMixin_v4(CommonTestMixin_v4): assertBadNetmask("1.1.1.1", "::") -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class InterfaceTestCase_v4(BaseTestCase, NetmaskTestMixin_v4): factory = ipaddress.IPv4Interface -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class NetworkTestCase_v4(BaseTestCase, NetmaskTestMixin_v4): factory = ipaddress.IPv4Network -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class NetmaskTestMixin_v6(CommonTestMixin_v6): """Input validation on interfaces and networks is very similar""" @@ -518,13 +509,13 @@ class NetmaskTestMixin_v6(CommonTestMixin_v6): def test_valid_netmask(self): # We only support CIDR for IPv6, because expanded netmasks are not # standard notation. - self.assertEqual(str(self.factory('2001:db8::/32')), '2001:db8::/32') + self.assertEqual(str(self.factory("2001:db8::/32")), "2001:db8::/32") for i in range(0, 129): # Generate and re-parse the CIDR format (trivial). - net_str = '::/%d' % i + net_str = "::/%d" % i self.assertEqual(str(self.factory(net_str)), net_str) # Zero prefix is treated as decimal. - self.assertEqual(str(self.factory('::/0%d' % i)), net_str) + self.assertEqual(str(self.factory("::/0%d" % i)), net_str) def test_netmask_errors(self): def assertBadNetmask(addr, netmask): @@ -545,23 +536,22 @@ class NetmaskTestMixin_v6(CommonTestMixin_v6): assertBadNetmask("::", "::") -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class InterfaceTestCase_v6(BaseTestCase, NetmaskTestMixin_v6): factory = ipaddress.IPv6Interface -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class NetworkTestCase_v6(BaseTestCase, NetmaskTestMixin_v6): factory = ipaddress.IPv6Network -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class FactoryFunctionErrors(BaseTestCase): - def assertFactoryError(self, factory, kind): """Ensure a clean ValueError with the expected message""" addr = "camelot" - msg = '%r does not appear to be an IPv4 or IPv6 %s' + msg = "%r does not appear to be an IPv4 or IPv6 %s" with self.assertCleanError(ValueError, msg, addr, kind): factory(addr) @@ -575,7 +565,7 @@ class FactoryFunctionErrors(BaseTestCase): self.assertFactoryError(ipaddress.ip_network, "network") -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class ComparisonTests(TestCase): v4addr = ipaddress.IPv4Address(1) @@ -633,23 +623,24 @@ class ComparisonTests(TestCase): # with get_mixed_type_key, you can sort addresses and network. v4_ordered = [self.v4addr, self.v4net, self.v4intf] v6_ordered = [self.v6addr, self.v6net, self.v6intf] - self.assertEqual(v4_ordered, - sorted(self.v4_objects, - key=ipaddress.get_mixed_type_key)) - self.assertEqual(v6_ordered, - sorted(self.v6_objects, - key=ipaddress.get_mixed_type_key)) - self.assertEqual(v4_ordered + v6_ordered, - sorted(self.objects, - key=ipaddress.get_mixed_type_key)) + self.assertEqual( + v4_ordered, sorted(self.v4_objects, key=ipaddress.get_mixed_type_key) + ) + self.assertEqual( + v6_ordered, sorted(self.v6_objects, key=ipaddress.get_mixed_type_key) + ) + self.assertEqual( + v4_ordered + v6_ordered, + sorted(self.objects, key=ipaddress.get_mixed_type_key), + ) self.assertEqual(NotImplemented, ipaddress.get_mixed_type_key(object)) def test_incompatible_versions(self): # These should always raise TypeError - v4addr = ipaddress.ip_address('1.1.1.1') - v4net = ipaddress.ip_network('1.1.1.1') - v6addr = ipaddress.ip_address('::1') - v6net = ipaddress.ip_address('::1') + v4addr = ipaddress.ip_address("1.1.1.1") + v4net = ipaddress.ip_network("1.1.1.1") + v6addr = ipaddress.ip_address("::1") + v6net = ipaddress.ip_address("::1") self.assertRaises(TypeError, v4addr.__lt__, v6addr) self.assertRaises(TypeError, v4addr.__gt__, v6addr) @@ -662,57 +653,70 @@ class ComparisonTests(TestCase): self.assertRaises(TypeError, v6net.__gt__, v4net) -@skipIf(sys.version_info > (3,), 'These are tested by the python test suite under Py3') +@skipIf(sys.version_info > (3,), "These are tested by the python test suite under Py3") class IpaddrUnitTest(TestCase): - def setUp(self): - self.ipv4_address = ipaddress.IPv4Address('1.2.3.4') - self.ipv4_interface = ipaddress.IPv4Interface('1.2.3.4/24') - self.ipv4_network = ipaddress.IPv4Network('1.2.3.0/24') - #self.ipv4_hostmask = ipaddress.IPv4Interface('10.0.0.1/0.255.255.255') - self.ipv6_address = ipaddress.IPv6Interface( - '2001:658:22a:cafe:200:0:0:1') - self.ipv6_interface = ipaddress.IPv6Interface( - '2001:658:22a:cafe:200:0:0:1/64') - self.ipv6_network = ipaddress.IPv6Network('2001:658:22a:cafe::/64') + self.ipv4_address = ipaddress.IPv4Address("1.2.3.4") + self.ipv4_interface = ipaddress.IPv4Interface("1.2.3.4/24") + self.ipv4_network = ipaddress.IPv4Network("1.2.3.0/24") + # self.ipv4_hostmask = ipaddress.IPv4Interface('10.0.0.1/0.255.255.255') + self.ipv6_address = ipaddress.IPv6Interface("2001:658:22a:cafe:200:0:0:1") + self.ipv6_interface = ipaddress.IPv6Interface("2001:658:22a:cafe:200:0:0:1/64") + self.ipv6_network = ipaddress.IPv6Network("2001:658:22a:cafe::/64") def tearDown(self): - for attrname in ('ipv4_network', 'ipv4_interface', 'ipv4_address', - 'ipv6_network', 'ipv6_interface', 'ipv6_address'): + for attrname in ( + "ipv4_network", + "ipv4_interface", + "ipv4_address", + "ipv6_network", + "ipv6_interface", + "ipv6_address", + ): try: delattr(self, attrname) except AttributeError: continue def testRepr(self): - self.assertEqual("IPv4Interface('1.2.3.4/32')", - repr(ipaddress.IPv4Interface('1.2.3.4'))) - self.assertEqual("IPv6Interface('::1/128')", - repr(ipaddress.IPv6Interface('::1'))) + self.assertEqual( + "IPv4Interface('1.2.3.4/32')", repr(ipaddress.IPv4Interface("1.2.3.4")) + ) + self.assertEqual( + "IPv6Interface('::1/128')", repr(ipaddress.IPv6Interface("::1")) + ) # issue57 def testAddressIntMath(self): - self.assertEqual(ipaddress.IPv4Address('1.1.1.1') + 255, - ipaddress.IPv4Address('1.1.2.0')) - self.assertEqual(ipaddress.IPv4Address('1.1.1.1') - 256, - ipaddress.IPv4Address('1.1.0.1')) - self.assertEqual(ipaddress.IPv6Address('::1') + (2**16 - 2), - ipaddress.IPv6Address('::ffff')) - self.assertEqual(ipaddress.IPv6Address('::ffff') - (2**16 - 2), - ipaddress.IPv6Address('::1')) + self.assertEqual( + ipaddress.IPv4Address("1.1.1.1") + 255, ipaddress.IPv4Address("1.1.2.0") + ) + self.assertEqual( + ipaddress.IPv4Address("1.1.1.1") - 256, ipaddress.IPv4Address("1.1.0.1") + ) + self.assertEqual( + ipaddress.IPv6Address("::1") + (2 ** 16 - 2), + ipaddress.IPv6Address("::ffff"), + ) + self.assertEqual( + ipaddress.IPv6Address("::ffff") - (2 ** 16 - 2), + ipaddress.IPv6Address("::1"), + ) def testInvalidIntToBytes(self): self.assertRaises(ValueError, ipaddress.v4_int_to_packed, -1) - self.assertRaises(ValueError, ipaddress.v4_int_to_packed, - 2 ** ipaddress.IPV4LENGTH) + self.assertRaises( + ValueError, ipaddress.v4_int_to_packed, 2 ** ipaddress.IPV4LENGTH + ) self.assertRaises(ValueError, ipaddress.v6_int_to_packed, -1) - self.assertRaises(ValueError, ipaddress.v6_int_to_packed, - 2 ** ipaddress.IPV6LENGTH) + self.assertRaises( + ValueError, ipaddress.v6_int_to_packed, 2 ** ipaddress.IPV6LENGTH + ) def testInternals(self): - first, last = ipaddress._find_address_range([ - ipaddress.IPv4Address('10.10.10.10'), - ipaddress.IPv4Address('10.10.10.12')]) + first, last = ipaddress._find_address_range( + [ipaddress.IPv4Address("10.10.10.10"), ipaddress.IPv4Address("10.10.10.12")] + ) self.assertEqual(first, last) self.assertEqual(128, ipaddress._count_righthand_zero_bits(0, 128)) self.assertEqual("IPv4Network('1.2.3.0/24')", repr(self.ipv4_network)) @@ -720,121 +724,137 @@ class IpaddrUnitTest(TestCase): def testMissingAddressVersion(self): class Broken(ipaddress._BaseAddress): pass - broken = Broken('127.0.0.1') + + broken = Broken("127.0.0.1") with self.assertRaisesRegex(NotImplementedError, "Broken.*version"): broken.version def testMissingNetworkVersion(self): class Broken(ipaddress._BaseNetwork): pass - broken = Broken('127.0.0.1') + + broken = Broken("127.0.0.1") with self.assertRaisesRegex(NotImplementedError, "Broken.*version"): broken.version def testMissingAddressClass(self): class Broken(ipaddress._BaseNetwork): pass - broken = Broken('127.0.0.1') + + broken = Broken("127.0.0.1") with self.assertRaisesRegex(NotImplementedError, "Broken.*address"): broken._address_class def testGetNetwork(self): self.assertEqual(int(self.ipv4_network.network_address), 16909056) - self.assertEqual(str(self.ipv4_network.network_address), '1.2.3.0') + self.assertEqual(str(self.ipv4_network.network_address), "1.2.3.0") - self.assertEqual(int(self.ipv6_network.network_address), - 42540616829182469433403647294022090752) - self.assertEqual(str(self.ipv6_network.network_address), - '2001:658:22a:cafe::') - self.assertEqual(str(self.ipv6_network.hostmask), - '::ffff:ffff:ffff:ffff') + self.assertEqual( + int(self.ipv6_network.network_address), + 42540616829182469433403647294022090752, + ) + self.assertEqual(str(self.ipv6_network.network_address), "2001:658:22a:cafe::") + self.assertEqual(str(self.ipv6_network.hostmask), "::ffff:ffff:ffff:ffff") def testIpFromInt(self): - self.assertEqual(self.ipv4_interface._ip, - ipaddress.IPv4Interface(16909060)._ip) + self.assertEqual(self.ipv4_interface._ip, ipaddress.IPv4Interface(16909060)._ip) - ipv4 = ipaddress.ip_network('1.2.3.4') - ipv6 = ipaddress.ip_network('2001:658:22a:cafe:200:0:0:1') + ipv4 = ipaddress.ip_network("1.2.3.4") + ipv6 = ipaddress.ip_network("2001:658:22a:cafe:200:0:0:1") self.assertEqual(ipv4, ipaddress.ip_network(int(ipv4.network_address))) self.assertEqual(ipv6, ipaddress.ip_network(int(ipv6.network_address))) v6_int = 42540616829182469433547762482097946625 - self.assertEqual(self.ipv6_interface._ip, - ipaddress.IPv6Interface(v6_int)._ip) + self.assertEqual(self.ipv6_interface._ip, ipaddress.IPv6Interface(v6_int)._ip) - self.assertEqual(ipaddress.ip_network(self.ipv4_address._ip).version, - 4) - self.assertEqual(ipaddress.ip_network(self.ipv6_address._ip).version, - 6) + self.assertEqual(ipaddress.ip_network(self.ipv4_address._ip).version, 4) + self.assertEqual(ipaddress.ip_network(self.ipv6_address._ip).version, 6) def testIpFromPacked(self): address = ipaddress.ip_address - self.assertEqual(self.ipv4_interface._ip, - ipaddress.ip_interface(bytearray(b'\x01\x02\x03\x04'))._ip) - self.assertEqual(address('255.254.253.252'), - address(bytearray(b'\xff\xfe\xfd\xfc'))) - self.assertEqual(self.ipv6_interface.ip, - ipaddress.ip_interface( - bytearray(b'\x20\x01\x06\x58\x02\x2a\xca\xfe' - b'\x02\x00\x00\x00\x00\x00\x00\x01')).ip) - self.assertEqual(address('ffff:2:3:4:ffff::'), - address(bytearray(b'\xff\xff\x00\x02\x00\x03\x00\x04') + - bytearray(b'\xff\xff') + bytearray(b'\x00') * 6)) - self.assertEqual(address('::'), - address(bytearray(b'\x00') * 16)) + self.assertEqual( + self.ipv4_interface._ip, + ipaddress.ip_interface(bytearray(b"\x01\x02\x03\x04"))._ip, + ) + self.assertEqual( + address("255.254.253.252"), address(bytearray(b"\xff\xfe\xfd\xfc")) + ) + self.assertEqual( + self.ipv6_interface.ip, + ipaddress.ip_interface( + bytearray( + b"\x20\x01\x06\x58\x02\x2a\xca\xfe" + b"\x02\x00\x00\x00\x00\x00\x00\x01" + ) + ).ip, + ) + self.assertEqual( + address("ffff:2:3:4:ffff::"), + address( + bytearray(b"\xff\xff\x00\x02\x00\x03\x00\x04") + + bytearray(b"\xff\xff") + + bytearray(b"\x00") * 6 + ), + ) + self.assertEqual(address("::"), address(bytearray(b"\x00") * 16)) def testGetIp(self): self.assertEqual(int(self.ipv4_interface.ip), 16909060) - self.assertEqual(str(self.ipv4_interface.ip), '1.2.3.4') + self.assertEqual(str(self.ipv4_interface.ip), "1.2.3.4") - self.assertEqual(int(self.ipv6_interface.ip), - 42540616829182469433547762482097946625) - self.assertEqual(str(self.ipv6_interface.ip), - '2001:658:22a:cafe:200::1') + self.assertEqual( + int(self.ipv6_interface.ip), 42540616829182469433547762482097946625 + ) + self.assertEqual(str(self.ipv6_interface.ip), "2001:658:22a:cafe:200::1") def testGetNetmask(self): self.assertEqual(int(self.ipv4_network.netmask), 4294967040) - self.assertEqual(str(self.ipv4_network.netmask), '255.255.255.0') - self.assertEqual(int(self.ipv6_network.netmask), - 340282366920938463444927863358058659840) + self.assertEqual(str(self.ipv4_network.netmask), "255.255.255.0") + self.assertEqual( + int(self.ipv6_network.netmask), 340282366920938463444927863358058659840 + ) self.assertEqual(self.ipv6_network.prefixlen, 64) def testZeroNetmask(self): - ipv4_zero_netmask = ipaddress.IPv4Interface('1.2.3.4/0') + ipv4_zero_netmask = ipaddress.IPv4Interface("1.2.3.4/0") self.assertEqual(int(ipv4_zero_netmask.network.netmask), 0) - self.assertEqual(ipv4_zero_netmask._prefix_from_prefix_string('0'), 0) - self.assertTrue(ipv4_zero_netmask._is_valid_netmask('0')) - self.assertTrue(ipv4_zero_netmask._is_valid_netmask('0.0.0.0')) - self.assertFalse(ipv4_zero_netmask._is_valid_netmask('invalid')) + self.assertEqual(ipv4_zero_netmask._prefix_from_prefix_string("0"), 0) + self.assertTrue(ipv4_zero_netmask._is_valid_netmask("0")) + self.assertTrue(ipv4_zero_netmask._is_valid_netmask("0.0.0.0")) + self.assertFalse(ipv4_zero_netmask._is_valid_netmask("invalid")) - ipv6_zero_netmask = ipaddress.IPv6Interface('::1/0') + ipv6_zero_netmask = ipaddress.IPv6Interface("::1/0") self.assertEqual(int(ipv6_zero_netmask.network.netmask), 0) - self.assertEqual(ipv6_zero_netmask._prefix_from_prefix_string('0'), 0) + self.assertEqual(ipv6_zero_netmask._prefix_from_prefix_string("0"), 0) def testIPv4NetAndHostmasks(self): net = self.ipv4_network - self.assertFalse(net._is_valid_netmask('invalid')) - self.assertTrue(net._is_valid_netmask('128.128.128.128')) - self.assertFalse(net._is_valid_netmask('128.128.128.127')) - self.assertFalse(net._is_valid_netmask('128.128.128.255')) - self.assertTrue(net._is_valid_netmask('255.128.128.128')) + self.assertFalse(net._is_valid_netmask("invalid")) + self.assertTrue(net._is_valid_netmask("128.128.128.128")) + self.assertFalse(net._is_valid_netmask("128.128.128.127")) + self.assertFalse(net._is_valid_netmask("128.128.128.255")) + self.assertTrue(net._is_valid_netmask("255.128.128.128")) - self.assertFalse(net._is_hostmask('invalid')) - self.assertTrue(net._is_hostmask('128.255.255.255')) - self.assertFalse(net._is_hostmask('255.255.255.255')) - self.assertFalse(net._is_hostmask('1.2.3.4')) + self.assertFalse(net._is_hostmask("invalid")) + self.assertTrue(net._is_hostmask("128.255.255.255")) + self.assertFalse(net._is_hostmask("255.255.255.255")) + self.assertFalse(net._is_hostmask("1.2.3.4")) - net = ipaddress.IPv4Network('127.0.0.0/0.0.0.255') + net = ipaddress.IPv4Network("127.0.0.0/0.0.0.255") self.assertEqual(net.prefixlen, 24) def testGetBroadcast(self): self.assertEqual(int(self.ipv4_network.broadcast_address), 16909311) - self.assertEqual(str(self.ipv4_network.broadcast_address), '1.2.3.255') + self.assertEqual(str(self.ipv4_network.broadcast_address), "1.2.3.255") - self.assertEqual(int(self.ipv6_network.broadcast_address), - 42540616829182469451850391367731642367) - self.assertEqual(str(self.ipv6_network.broadcast_address), - '2001:658:22a:cafe:ffff:ffff:ffff:ffff') + self.assertEqual( + int(self.ipv6_network.broadcast_address), + 42540616829182469451850391367731642367, + ) + self.assertEqual( + str(self.ipv6_network.broadcast_address), + "2001:658:22a:cafe:ffff:ffff:ffff:ffff", + ) def testGetPrefixlen(self): self.assertEqual(self.ipv4_interface.network.prefixlen, 24) @@ -842,407 +862,413 @@ class IpaddrUnitTest(TestCase): def testGetSupernet(self): self.assertEqual(self.ipv4_network.supernet().prefixlen, 23) - self.assertEqual(str(self.ipv4_network.supernet().network_address), - '1.2.2.0') + self.assertEqual(str(self.ipv4_network.supernet().network_address), "1.2.2.0") self.assertEqual( - ipaddress.IPv4Interface('0.0.0.0/0').network.supernet(), - ipaddress.IPv4Network('0.0.0.0/0')) + ipaddress.IPv4Interface("0.0.0.0/0").network.supernet(), + ipaddress.IPv4Network("0.0.0.0/0"), + ) self.assertEqual(self.ipv6_network.supernet().prefixlen, 63) - self.assertEqual(str(self.ipv6_network.supernet().network_address), - '2001:658:22a:cafe::') - self.assertEqual(ipaddress.IPv6Interface('::0/0').network.supernet(), - ipaddress.IPv6Network('::0/0')) + self.assertEqual( + str(self.ipv6_network.supernet().network_address), "2001:658:22a:cafe::" + ) + self.assertEqual( + ipaddress.IPv6Interface("::0/0").network.supernet(), + ipaddress.IPv6Network("::0/0"), + ) def testGetSupernet3(self): self.assertEqual(self.ipv4_network.supernet(3).prefixlen, 21) - self.assertEqual(str(self.ipv4_network.supernet(3).network_address), - '1.2.0.0') + self.assertEqual(str(self.ipv4_network.supernet(3).network_address), "1.2.0.0") self.assertEqual(self.ipv6_network.supernet(3).prefixlen, 61) - self.assertEqual(str(self.ipv6_network.supernet(3).network_address), - '2001:658:22a:caf8::') + self.assertEqual( + str(self.ipv6_network.supernet(3).network_address), "2001:658:22a:caf8::" + ) def testGetSupernet4(self): - self.assertRaises(ValueError, self.ipv4_network.supernet, - prefixlen_diff=2, new_prefix=1) - self.assertRaises(ValueError, self.ipv4_network.supernet, - new_prefix=25) - self.assertEqual(self.ipv4_network.supernet(prefixlen_diff=2), - self.ipv4_network.supernet(new_prefix=22)) + self.assertRaises( + ValueError, self.ipv4_network.supernet, prefixlen_diff=2, new_prefix=1 + ) + self.assertRaises(ValueError, self.ipv4_network.supernet, new_prefix=25) + self.assertEqual( + self.ipv4_network.supernet(prefixlen_diff=2), + self.ipv4_network.supernet(new_prefix=22), + ) - self.assertRaises(ValueError, self.ipv6_network.supernet, - prefixlen_diff=2, new_prefix=1) - self.assertRaises(ValueError, self.ipv6_network.supernet, - new_prefix=65) - self.assertEqual(self.ipv6_network.supernet(prefixlen_diff=2), - self.ipv6_network.supernet(new_prefix=62)) + self.assertRaises( + ValueError, self.ipv6_network.supernet, prefixlen_diff=2, new_prefix=1 + ) + self.assertRaises(ValueError, self.ipv6_network.supernet, new_prefix=65) + self.assertEqual( + self.ipv6_network.supernet(prefixlen_diff=2), + self.ipv6_network.supernet(new_prefix=62), + ) def testHosts(self): hosts = list(self.ipv4_network.hosts()) self.assertEqual(254, len(hosts)) - self.assertEqual(ipaddress.IPv4Address('1.2.3.1'), hosts[0]) - self.assertEqual(ipaddress.IPv4Address('1.2.3.254'), hosts[-1]) + self.assertEqual(ipaddress.IPv4Address("1.2.3.1"), hosts[0]) + self.assertEqual(ipaddress.IPv4Address("1.2.3.254"), hosts[-1]) # special case where only 1 bit is left for address - self.assertEqual([ipaddress.IPv4Address('2.0.0.0'), - ipaddress.IPv4Address('2.0.0.1')], - list(ipaddress.ip_network('2.0.0.0/31').hosts())) + self.assertEqual( + [ipaddress.IPv4Address("2.0.0.0"), ipaddress.IPv4Address("2.0.0.1")], + list(ipaddress.ip_network("2.0.0.0/31").hosts()), + ) def testFancySubnetting(self): - self.assertEqual(sorted(self.ipv4_network.subnets(prefixlen_diff=3)), - sorted(self.ipv4_network.subnets(new_prefix=27))) - self.assertRaises(ValueError, list, - self.ipv4_network.subnets(new_prefix=23)) - self.assertRaises(ValueError, list, - self.ipv4_network.subnets(prefixlen_diff=3, - new_prefix=27)) - self.assertEqual(sorted(self.ipv6_network.subnets(prefixlen_diff=4)), - sorted(self.ipv6_network.subnets(new_prefix=68))) - self.assertRaises(ValueError, list, - self.ipv6_network.subnets(new_prefix=63)) - self.assertRaises(ValueError, list, - self.ipv6_network.subnets(prefixlen_diff=4, - new_prefix=68)) + self.assertEqual( + sorted(self.ipv4_network.subnets(prefixlen_diff=3)), + sorted(self.ipv4_network.subnets(new_prefix=27)), + ) + self.assertRaises(ValueError, list, self.ipv4_network.subnets(new_prefix=23)) + self.assertRaises( + ValueError, list, self.ipv4_network.subnets(prefixlen_diff=3, new_prefix=27) + ) + self.assertEqual( + sorted(self.ipv6_network.subnets(prefixlen_diff=4)), + sorted(self.ipv6_network.subnets(new_prefix=68)), + ) + self.assertRaises(ValueError, list, self.ipv6_network.subnets(new_prefix=63)) + self.assertRaises( + ValueError, list, self.ipv6_network.subnets(prefixlen_diff=4, new_prefix=68) + ) def testGetSubnets(self): self.assertEqual(list(self.ipv4_network.subnets())[0].prefixlen, 25) - self.assertEqual(str(list( - self.ipv4_network.subnets())[0].network_address), - '1.2.3.0') - self.assertEqual(str(list( - self.ipv4_network.subnets())[1].network_address), - '1.2.3.128') + self.assertEqual( + str(list(self.ipv4_network.subnets())[0].network_address), "1.2.3.0" + ) + self.assertEqual( + str(list(self.ipv4_network.subnets())[1].network_address), "1.2.3.128" + ) self.assertEqual(list(self.ipv6_network.subnets())[0].prefixlen, 65) def testGetSubnetForSingle32(self): - ip = ipaddress.IPv4Network('1.2.3.4/32') + ip = ipaddress.IPv4Network("1.2.3.4/32") subnets1 = [str(x) for x in ip.subnets()] subnets2 = [str(x) for x in ip.subnets(2)] - self.assertEqual(subnets1, ['1.2.3.4/32']) + self.assertEqual(subnets1, ["1.2.3.4/32"]) self.assertEqual(subnets1, subnets2) def testGetSubnetForSingle128(self): - ip = ipaddress.IPv6Network('::1/128') + ip = ipaddress.IPv6Network("::1/128") subnets1 = [str(x) for x in ip.subnets()] subnets2 = [str(x) for x in ip.subnets(2)] - self.assertEqual(subnets1, ['::1/128']) + self.assertEqual(subnets1, ["::1/128"]) self.assertEqual(subnets1, subnets2) def testSubnet2(self): ips = [str(x) for x in self.ipv4_network.subnets(2)] self.assertEqual( - ips, - ['1.2.3.0/26', '1.2.3.64/26', '1.2.3.128/26', '1.2.3.192/26']) + ips, ["1.2.3.0/26", "1.2.3.64/26", "1.2.3.128/26", "1.2.3.192/26"] + ) ipsv6 = [str(x) for x in self.ipv6_network.subnets(2)] self.assertEqual( ipsv6, - ['2001:658:22a:cafe::/66', - '2001:658:22a:cafe:4000::/66', - '2001:658:22a:cafe:8000::/66', - '2001:658:22a:cafe:c000::/66']) + [ + "2001:658:22a:cafe::/66", + "2001:658:22a:cafe:4000::/66", + "2001:658:22a:cafe:8000::/66", + "2001:658:22a:cafe:c000::/66", + ], + ) def testSubnetFailsForLargeCidrDiff(self): - self.assertRaises(ValueError, list, - self.ipv4_interface.network.subnets(9)) - self.assertRaises(ValueError, list, - self.ipv4_network.subnets(9)) - self.assertRaises(ValueError, list, - self.ipv6_interface.network.subnets(65)) - self.assertRaises(ValueError, list, - self.ipv6_network.subnets(65)) + self.assertRaises(ValueError, list, self.ipv4_interface.network.subnets(9)) + self.assertRaises(ValueError, list, self.ipv4_network.subnets(9)) + self.assertRaises(ValueError, list, self.ipv6_interface.network.subnets(65)) + self.assertRaises(ValueError, list, self.ipv6_network.subnets(65)) def testSupernetFailsForLargeCidrDiff(self): - self.assertRaises(ValueError, - self.ipv4_interface.network.supernet, 25) - self.assertRaises(ValueError, - self.ipv6_interface.network.supernet, 65) + self.assertRaises(ValueError, self.ipv4_interface.network.supernet, 25) + self.assertRaises(ValueError, self.ipv6_interface.network.supernet, 65) def testSubnetFailsForNegativeCidrDiff(self): - self.assertRaises(ValueError, list, - self.ipv4_interface.network.subnets(-1)) - self.assertRaises(ValueError, list, - self.ipv4_network.subnets(-1)) - self.assertRaises(ValueError, list, - self.ipv6_interface.network.subnets(-1)) - self.assertRaises(ValueError, list, - self.ipv6_network.subnets(-1)) + self.assertRaises(ValueError, list, self.ipv4_interface.network.subnets(-1)) + self.assertRaises(ValueError, list, self.ipv4_network.subnets(-1)) + self.assertRaises(ValueError, list, self.ipv6_interface.network.subnets(-1)) + self.assertRaises(ValueError, list, self.ipv6_network.subnets(-1)) def testGetNum_Addresses(self): self.assertEqual(self.ipv4_network.num_addresses, 256) - self.assertEqual(list(self.ipv4_network.subnets())[0].num_addresses, - 128) + self.assertEqual(list(self.ipv4_network.subnets())[0].num_addresses, 128) self.assertEqual(self.ipv4_network.supernet().num_addresses, 512) self.assertEqual(self.ipv6_network.num_addresses, 18446744073709551616) - self.assertEqual(list(self.ipv6_network.subnets())[0].num_addresses, - 9223372036854775808) - self.assertEqual(self.ipv6_network.supernet().num_addresses, - 36893488147419103232) + self.assertEqual( + list(self.ipv6_network.subnets())[0].num_addresses, 9223372036854775808 + ) + self.assertEqual( + self.ipv6_network.supernet().num_addresses, 36893488147419103232 + ) def testContains(self): - self.assertIn(ipaddress.IPv4Interface('1.2.3.128/25'), - self.ipv4_network) - self.assertNotIn(ipaddress.IPv4Interface('1.2.4.1/24'), - self.ipv4_network) + self.assertIn(ipaddress.IPv4Interface("1.2.3.128/25"), self.ipv4_network) + self.assertNotIn(ipaddress.IPv4Interface("1.2.4.1/24"), self.ipv4_network) # We can test addresses and string as well. - addr1 = ipaddress.IPv4Address('1.2.3.37') + addr1 = ipaddress.IPv4Address("1.2.3.37") self.assertIn(addr1, self.ipv4_network) # issue 61, bad network comparison on like-ip'd network objects # with identical broadcast addresses. - self.assertFalse(ipaddress.IPv4Network('1.1.0.0/16').__contains__( - ipaddress.IPv4Network('1.0.0.0/15'))) + self.assertFalse( + ipaddress.IPv4Network("1.1.0.0/16").__contains__( + ipaddress.IPv4Network("1.0.0.0/15") + ) + ) def testNth(self): - self.assertEqual(str(self.ipv4_network[5]), '1.2.3.5') + self.assertEqual(str(self.ipv4_network[5]), "1.2.3.5") self.assertRaises(IndexError, self.ipv4_network.__getitem__, 256) - self.assertEqual(str(self.ipv6_network[5]), - '2001:658:22a:cafe::5') + self.assertEqual(str(self.ipv6_network[5]), "2001:658:22a:cafe::5") def testGetitem(self): # http://code.google.com/p/ipaddr-py/issues/detail?id=15 - addr = ipaddress.IPv4Network('172.31.255.128/255.255.255.240') + addr = ipaddress.IPv4Network("172.31.255.128/255.255.255.240") self.assertEqual(28, addr.prefixlen) addr_list = list(addr) - self.assertEqual('172.31.255.128', str(addr_list[0])) - self.assertEqual('172.31.255.128', str(addr[0])) - self.assertEqual('172.31.255.143', str(addr_list[-1])) - self.assertEqual('172.31.255.143', str(addr[-1])) + self.assertEqual("172.31.255.128", str(addr_list[0])) + self.assertEqual("172.31.255.128", str(addr[0])) + self.assertEqual("172.31.255.143", str(addr_list[-1])) + self.assertEqual("172.31.255.143", str(addr[-1])) self.assertEqual(addr_list[-1], addr[-1]) def testEqual(self): - self.assertTrue(self.ipv4_interface == - ipaddress.IPv4Interface('1.2.3.4/24')) - self.assertFalse(self.ipv4_interface == - ipaddress.IPv4Interface('1.2.3.4/23')) - self.assertFalse(self.ipv4_interface == - ipaddress.IPv6Interface('::1.2.3.4/24')) - self.assertFalse(self.ipv4_interface == '') + self.assertTrue(self.ipv4_interface == ipaddress.IPv4Interface("1.2.3.4/24")) + self.assertFalse(self.ipv4_interface == ipaddress.IPv4Interface("1.2.3.4/23")) + self.assertFalse(self.ipv4_interface == ipaddress.IPv6Interface("::1.2.3.4/24")) + self.assertFalse(self.ipv4_interface == "") self.assertFalse(self.ipv4_interface == []) self.assertFalse(self.ipv4_interface == 2) - self.assertTrue(self.ipv6_interface == - ipaddress.IPv6Interface('2001:658:22a:cafe:200::1/64')) - self.assertFalse(self.ipv6_interface == - ipaddress.IPv6Interface('2001:658:22a:cafe:200::1/63')) - self.assertFalse(self.ipv6_interface == - ipaddress.IPv4Interface('1.2.3.4/23')) - self.assertFalse(self.ipv6_interface == '') + self.assertTrue( + self.ipv6_interface + == ipaddress.IPv6Interface("2001:658:22a:cafe:200::1/64") + ) + self.assertFalse( + self.ipv6_interface + == ipaddress.IPv6Interface("2001:658:22a:cafe:200::1/63") + ) + self.assertFalse(self.ipv6_interface == ipaddress.IPv4Interface("1.2.3.4/23")) + self.assertFalse(self.ipv6_interface == "") self.assertFalse(self.ipv6_interface == []) self.assertFalse(self.ipv6_interface == 2) def testNotEqual(self): - self.assertFalse(self.ipv4_interface != - ipaddress.IPv4Interface('1.2.3.4/24')) - self.assertTrue(self.ipv4_interface != - ipaddress.IPv4Interface('1.2.3.4/23')) - self.assertTrue(self.ipv4_interface != - ipaddress.IPv6Interface('::1.2.3.4/24')) - self.assertTrue(self.ipv4_interface != '') + self.assertFalse(self.ipv4_interface != ipaddress.IPv4Interface("1.2.3.4/24")) + self.assertTrue(self.ipv4_interface != ipaddress.IPv4Interface("1.2.3.4/23")) + self.assertTrue(self.ipv4_interface != ipaddress.IPv6Interface("::1.2.3.4/24")) + self.assertTrue(self.ipv4_interface != "") self.assertTrue(self.ipv4_interface != []) self.assertTrue(self.ipv4_interface != 2) - self.assertTrue(self.ipv4_address != - ipaddress.IPv4Address('1.2.3.5')) - self.assertTrue(self.ipv4_address != '') + self.assertTrue(self.ipv4_address != ipaddress.IPv4Address("1.2.3.5")) + self.assertTrue(self.ipv4_address != "") self.assertTrue(self.ipv4_address != []) self.assertTrue(self.ipv4_address != 2) - self.assertFalse(self.ipv6_interface != - ipaddress.IPv6Interface('2001:658:22a:cafe:200::1/64')) - self.assertTrue(self.ipv6_interface != - ipaddress.IPv6Interface('2001:658:22a:cafe:200::1/63')) - self.assertTrue(self.ipv6_interface != - ipaddress.IPv4Interface('1.2.3.4/23')) - self.assertTrue(self.ipv6_interface != '') + self.assertFalse( + self.ipv6_interface + != ipaddress.IPv6Interface("2001:658:22a:cafe:200::1/64") + ) + self.assertTrue( + self.ipv6_interface + != ipaddress.IPv6Interface("2001:658:22a:cafe:200::1/63") + ) + self.assertTrue(self.ipv6_interface != ipaddress.IPv4Interface("1.2.3.4/23")) + self.assertTrue(self.ipv6_interface != "") self.assertTrue(self.ipv6_interface != []) self.assertTrue(self.ipv6_interface != 2) - self.assertTrue(self.ipv6_address != - ipaddress.IPv4Address('1.2.3.4')) - self.assertTrue(self.ipv6_address != '') + self.assertTrue(self.ipv6_address != ipaddress.IPv4Address("1.2.3.4")) + self.assertTrue(self.ipv6_address != "") self.assertTrue(self.ipv6_address != []) self.assertTrue(self.ipv6_address != 2) def testSlash32Constructor(self): - self.assertEqual(str(ipaddress.IPv4Interface( - '1.2.3.4/255.255.255.255')), '1.2.3.4/32') + self.assertEqual( + str(ipaddress.IPv4Interface("1.2.3.4/255.255.255.255")), "1.2.3.4/32" + ) def testSlash128Constructor(self): - self.assertEqual(str(ipaddress.IPv6Interface('::1/128')), - '::1/128') + self.assertEqual(str(ipaddress.IPv6Interface("::1/128")), "::1/128") def testSlash0Constructor(self): - self.assertEqual(str(ipaddress.IPv4Interface('1.2.3.4/0.0.0.0')), - '1.2.3.4/0') + self.assertEqual(str(ipaddress.IPv4Interface("1.2.3.4/0.0.0.0")), "1.2.3.4/0") def testCollapsing(self): # test only IP addresses including some duplicates - ip1 = ipaddress.IPv4Address('1.1.1.0') - ip2 = ipaddress.IPv4Address('1.1.1.1') - ip3 = ipaddress.IPv4Address('1.1.1.2') - ip4 = ipaddress.IPv4Address('1.1.1.3') - ip5 = ipaddress.IPv4Address('1.1.1.4') - ip6 = ipaddress.IPv4Address('1.1.1.0') + ip1 = ipaddress.IPv4Address("1.1.1.0") + ip2 = ipaddress.IPv4Address("1.1.1.1") + ip3 = ipaddress.IPv4Address("1.1.1.2") + ip4 = ipaddress.IPv4Address("1.1.1.3") + ip5 = ipaddress.IPv4Address("1.1.1.4") + ip6 = ipaddress.IPv4Address("1.1.1.0") # check that addreses are subsumed properly. - collapsed = ipaddress.collapse_addresses( - [ip1, ip2, ip3, ip4, ip5, ip6]) - self.assertEqual(list(collapsed), - [ipaddress.IPv4Network('1.1.1.0/30'), - ipaddress.IPv4Network('1.1.1.4/32')]) + collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4, ip5, ip6]) + self.assertEqual( + list(collapsed), + [ipaddress.IPv4Network("1.1.1.0/30"), ipaddress.IPv4Network("1.1.1.4/32")], + ) # test a mix of IP addresses and networks including some duplicates - ip1 = ipaddress.IPv4Address('1.1.1.0') - ip2 = ipaddress.IPv4Address('1.1.1.1') - ip3 = ipaddress.IPv4Address('1.1.1.2') - ip4 = ipaddress.IPv4Address('1.1.1.3') - #ip5 = ipaddress.IPv4Interface('1.1.1.4/30') - #ip6 = ipaddress.IPv4Interface('1.1.1.4/30') + ip1 = ipaddress.IPv4Address("1.1.1.0") + ip2 = ipaddress.IPv4Address("1.1.1.1") + ip3 = ipaddress.IPv4Address("1.1.1.2") + ip4 = ipaddress.IPv4Address("1.1.1.3") + # ip5 = ipaddress.IPv4Interface('1.1.1.4/30') + # ip6 = ipaddress.IPv4Interface('1.1.1.4/30') # check that addreses are subsumed properly. collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4]) - self.assertEqual(list(collapsed), - [ipaddress.IPv4Network('1.1.1.0/30')]) + self.assertEqual(list(collapsed), [ipaddress.IPv4Network("1.1.1.0/30")]) # test only IP networks - ip1 = ipaddress.IPv4Network('1.1.0.0/24') - ip2 = ipaddress.IPv4Network('1.1.1.0/24') - ip3 = ipaddress.IPv4Network('1.1.2.0/24') - ip4 = ipaddress.IPv4Network('1.1.3.0/24') - ip5 = ipaddress.IPv4Network('1.1.4.0/24') + ip1 = ipaddress.IPv4Network("1.1.0.0/24") + ip2 = ipaddress.IPv4Network("1.1.1.0/24") + ip3 = ipaddress.IPv4Network("1.1.2.0/24") + ip4 = ipaddress.IPv4Network("1.1.3.0/24") + ip5 = ipaddress.IPv4Network("1.1.4.0/24") # stored in no particular order b/c we want CollapseAddr to call # [].sort - ip6 = ipaddress.IPv4Network('1.1.0.0/22') + ip6 = ipaddress.IPv4Network("1.1.0.0/22") # check that addreses are subsumed properly. - collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4, ip5, - ip6]) - self.assertEqual(list(collapsed), - [ipaddress.IPv4Network('1.1.0.0/22'), - ipaddress.IPv4Network('1.1.4.0/24')]) + collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3, ip4, ip5, ip6]) + self.assertEqual( + list(collapsed), + [ipaddress.IPv4Network("1.1.0.0/22"), ipaddress.IPv4Network("1.1.4.0/24")], + ) # test that two addresses are supernet'ed properly collapsed = ipaddress.collapse_addresses([ip1, ip2]) - self.assertEqual(list(collapsed), - [ipaddress.IPv4Network('1.1.0.0/23')]) + self.assertEqual(list(collapsed), [ipaddress.IPv4Network("1.1.0.0/23")]) # test same IP networks - ip_same1 = ip_same2 = ipaddress.IPv4Network('1.1.1.1/32') - self.assertEqual(list(ipaddress.collapse_addresses( - [ip_same1, ip_same2])), - [ip_same1]) + ip_same1 = ip_same2 = ipaddress.IPv4Network("1.1.1.1/32") + self.assertEqual( + list(ipaddress.collapse_addresses([ip_same1, ip_same2])), [ip_same1] + ) # test same IP addresses - ip_same1 = ip_same2 = ipaddress.IPv4Address('1.1.1.1') - self.assertEqual(list(ipaddress.collapse_addresses( - [ip_same1, ip_same2])), - [ipaddress.ip_network('1.1.1.1/32')]) - ip1 = ipaddress.IPv6Network('2001::/100') - ip2 = ipaddress.IPv6Network('2001::/120') - ip3 = ipaddress.IPv6Network('2001::/96') + ip_same1 = ip_same2 = ipaddress.IPv4Address("1.1.1.1") + self.assertEqual( + list(ipaddress.collapse_addresses([ip_same1, ip_same2])), + [ipaddress.ip_network("1.1.1.1/32")], + ) + ip1 = ipaddress.IPv6Network("2001::/100") + ip2 = ipaddress.IPv6Network("2001::/120") + ip3 = ipaddress.IPv6Network("2001::/96") # test that ipv6 addresses are subsumed properly. collapsed = ipaddress.collapse_addresses([ip1, ip2, ip3]) self.assertEqual(list(collapsed), [ip3]) # the toejam test addr_tuples = [ - (ipaddress.ip_address('1.1.1.1'), - ipaddress.ip_address('::1')), - (ipaddress.IPv4Network('1.1.0.0/24'), - ipaddress.IPv6Network('2001::/120')), - (ipaddress.IPv4Network('1.1.0.0/32'), - ipaddress.IPv6Network('2001::/128')), + (ipaddress.ip_address("1.1.1.1"), ipaddress.ip_address("::1")), + (ipaddress.IPv4Network("1.1.0.0/24"), ipaddress.IPv6Network("2001::/120")), + (ipaddress.IPv4Network("1.1.0.0/32"), ipaddress.IPv6Network("2001::/128")), ] for ip1, ip2 in addr_tuples: - self.assertRaises(TypeError, ipaddress.collapse_addresses, - [ip1, ip2]) + self.assertRaises(TypeError, ipaddress.collapse_addresses, [ip1, ip2]) def testSummarizing(self): - #ip = ipaddress.ip_address - #ipnet = ipaddress.ip_network + # ip = ipaddress.ip_address + # ipnet = ipaddress.ip_network summarize = ipaddress.summarize_address_range - ip1 = ipaddress.ip_address('1.1.1.0') - ip2 = ipaddress.ip_address('1.1.1.255') + ip1 = ipaddress.ip_address("1.1.1.0") + ip2 = ipaddress.ip_address("1.1.1.255") # summarize works only for IPv4 & IPv6 class IPv7Address(ipaddress.IPv6Address): @property def version(self): return 7 - ip_invalid1 = IPv7Address('::1') - ip_invalid2 = IPv7Address('::1') - self.assertRaises(ValueError, list, - summarize(ip_invalid1, ip_invalid2)) + + ip_invalid1 = IPv7Address("::1") + ip_invalid2 = IPv7Address("::1") + self.assertRaises(ValueError, list, summarize(ip_invalid1, ip_invalid2)) # test that a summary over ip4 & ip6 fails - self.assertRaises(TypeError, list, - summarize(ip1, ipaddress.IPv6Address('::1'))) + self.assertRaises(TypeError, list, summarize(ip1, ipaddress.IPv6Address("::1"))) # test a /24 is summarized properly - self.assertEqual(list(summarize(ip1, ip2))[0], - ipaddress.ip_network('1.1.1.0/24')) + self.assertEqual( + list(summarize(ip1, ip2))[0], ipaddress.ip_network("1.1.1.0/24") + ) # test an IPv4 range that isn't on a network byte boundary - ip2 = ipaddress.ip_address('1.1.1.8') - self.assertEqual(list(summarize(ip1, ip2)), - [ipaddress.ip_network('1.1.1.0/29'), - ipaddress.ip_network('1.1.1.8')]) + ip2 = ipaddress.ip_address("1.1.1.8") + self.assertEqual( + list(summarize(ip1, ip2)), + [ipaddress.ip_network("1.1.1.0/29"), ipaddress.ip_network("1.1.1.8")], + ) # all! ip1 = ipaddress.IPv4Address(0) ip2 = ipaddress.IPv4Address(ipaddress.IPv4Address._ALL_ONES) - self.assertEqual([ipaddress.IPv4Network('0.0.0.0/0')], - list(summarize(ip1, ip2))) + self.assertEqual( + [ipaddress.IPv4Network("0.0.0.0/0")], list(summarize(ip1, ip2)) + ) - ip1 = ipaddress.ip_address('1::') - ip2 = ipaddress.ip_address('1:ffff:ffff:ffff:ffff:ffff:ffff:ffff') + ip1 = ipaddress.ip_address("1::") + ip2 = ipaddress.ip_address("1:ffff:ffff:ffff:ffff:ffff:ffff:ffff") # test a IPv6 is sumamrized properly - self.assertEqual(list(summarize(ip1, ip2))[0], - ipaddress.ip_network('1::/16')) + self.assertEqual(list(summarize(ip1, ip2))[0], ipaddress.ip_network("1::/16")) # test an IPv6 range that isn't on a network byte boundary - ip2 = ipaddress.ip_address('2::') - self.assertEqual(list(summarize(ip1, ip2)), - [ipaddress.ip_network('1::/16'), - ipaddress.ip_network('2::/128')]) + ip2 = ipaddress.ip_address("2::") + self.assertEqual( + list(summarize(ip1, ip2)), + [ipaddress.ip_network("1::/16"), ipaddress.ip_network("2::/128")], + ) # test exception raised when first is greater than last - self.assertRaises(ValueError, list, - summarize(ipaddress.ip_address('1.1.1.0'), - ipaddress.ip_address('1.1.0.0'))) + self.assertRaises( + ValueError, + list, + summarize(ipaddress.ip_address("1.1.1.0"), ipaddress.ip_address("1.1.0.0")), + ) # test exception raised when first and last aren't IP addresses - self.assertRaises(TypeError, list, - summarize(ipaddress.ip_network('1.1.1.0'), - ipaddress.ip_network('1.1.0.0'))) - self.assertRaises(TypeError, list, - summarize(ipaddress.ip_network('1.1.1.0'), - ipaddress.ip_network('1.1.0.0'))) + self.assertRaises( + TypeError, + list, + summarize(ipaddress.ip_network("1.1.1.0"), ipaddress.ip_network("1.1.0.0")), + ) + self.assertRaises( + TypeError, + list, + summarize(ipaddress.ip_network("1.1.1.0"), ipaddress.ip_network("1.1.0.0")), + ) # test exception raised when first and last are not same version - self.assertRaises(TypeError, list, - summarize(ipaddress.ip_address('::'), - ipaddress.ip_network('1.1.0.0'))) + self.assertRaises( + TypeError, + list, + summarize(ipaddress.ip_address("::"), ipaddress.ip_network("1.1.0.0")), + ) def testAddressComparison(self): - self.assertTrue(ipaddress.ip_address('1.1.1.1') <= - ipaddress.ip_address('1.1.1.1')) - self.assertTrue(ipaddress.ip_address('1.1.1.1') <= - ipaddress.ip_address('1.1.1.2')) - self.assertTrue(ipaddress.ip_address('::1') <= - ipaddress.ip_address('::1')) - self.assertTrue(ipaddress.ip_address('::1') <= - ipaddress.ip_address('::2')) + self.assertTrue( + ipaddress.ip_address("1.1.1.1") <= ipaddress.ip_address("1.1.1.1") + ) + self.assertTrue( + ipaddress.ip_address("1.1.1.1") <= ipaddress.ip_address("1.1.1.2") + ) + self.assertTrue(ipaddress.ip_address("::1") <= ipaddress.ip_address("::1")) + self.assertTrue(ipaddress.ip_address("::1") <= ipaddress.ip_address("::2")) def testInterfaceComparison(self): - self.assertTrue(ipaddress.ip_interface('1.1.1.1') <= - ipaddress.ip_interface('1.1.1.1')) - self.assertTrue(ipaddress.ip_interface('1.1.1.1') <= - ipaddress.ip_interface('1.1.1.2')) - self.assertTrue(ipaddress.ip_interface('::1') <= - ipaddress.ip_interface('::1')) - self.assertTrue(ipaddress.ip_interface('::1') <= - ipaddress.ip_interface('::2')) + self.assertTrue( + ipaddress.ip_interface("1.1.1.1") <= ipaddress.ip_interface("1.1.1.1") + ) + self.assertTrue( + ipaddress.ip_interface("1.1.1.1") <= ipaddress.ip_interface("1.1.1.2") + ) + self.assertTrue(ipaddress.ip_interface("::1") <= ipaddress.ip_interface("::1")) + self.assertTrue(ipaddress.ip_interface("::1") <= ipaddress.ip_interface("::2")) def testNetworkComparison(self): # ip1 and ip2 have the same network address - ip1 = ipaddress.IPv4Network('1.1.1.0/24') - ip2 = ipaddress.IPv4Network('1.1.1.0/32') - ip3 = ipaddress.IPv4Network('1.1.2.0/24') + ip1 = ipaddress.IPv4Network("1.1.1.0/24") + ip2 = ipaddress.IPv4Network("1.1.1.0/32") + ip3 = ipaddress.IPv4Network("1.1.2.0/24") self.assertTrue(ip1 < ip3) self.assertTrue(ip3 > ip2) @@ -1257,9 +1283,9 @@ class IpaddrUnitTest(TestCase): self.assertEqual(ip3.compare_networks(ip1), 1) self.assertTrue(ip1._get_networks_key() < ip3._get_networks_key()) - ip1 = ipaddress.IPv6Network('2001:2000::/96') - ip2 = ipaddress.IPv6Network('2001:2001::/96') - ip3 = ipaddress.IPv6Network('2001:ffff:2000::/96') + ip1 = ipaddress.IPv6Network("2001:2000::/96") + ip2 = ipaddress.IPv6Network("2001:2001::/96") + ip3 = ipaddress.IPv6Network("2001:ffff:2000::/96") self.assertTrue(ip1 < ip3) self.assertTrue(ip3 > ip2) @@ -1268,36 +1294,36 @@ class IpaddrUnitTest(TestCase): # Test comparing different protocols. # Should always raise a TypeError. - self.assertRaises(TypeError, - self.ipv4_network.compare_networks, - self.ipv6_network) - ipv6 = ipaddress.IPv6Interface('::/0') - ipv4 = ipaddress.IPv4Interface('0.0.0.0/0') + self.assertRaises( + TypeError, self.ipv4_network.compare_networks, self.ipv6_network + ) + ipv6 = ipaddress.IPv6Interface("::/0") + ipv4 = ipaddress.IPv4Interface("0.0.0.0/0") self.assertRaises(TypeError, ipv4.__lt__, ipv6) self.assertRaises(TypeError, ipv4.__gt__, ipv6) self.assertRaises(TypeError, ipv6.__lt__, ipv4) self.assertRaises(TypeError, ipv6.__gt__, ipv4) # Regression test for issue 19. - ip1 = ipaddress.ip_network('10.1.2.128/25') + ip1 = ipaddress.ip_network("10.1.2.128/25") self.assertFalse(ip1 < ip1) self.assertFalse(ip1 > ip1) - ip2 = ipaddress.ip_network('10.1.3.0/24') + ip2 = ipaddress.ip_network("10.1.3.0/24") self.assertTrue(ip1 < ip2) self.assertFalse(ip2 < ip1) self.assertFalse(ip1 > ip2) self.assertTrue(ip2 > ip1) - ip3 = ipaddress.ip_network('10.1.3.0/25') + ip3 = ipaddress.ip_network("10.1.3.0/25") self.assertTrue(ip2 < ip3) self.assertFalse(ip3 < ip2) self.assertFalse(ip2 > ip3) self.assertTrue(ip3 > ip2) # Regression test for issue 28. - ip1 = ipaddress.ip_network('10.10.10.0/31') - ip2 = ipaddress.ip_network('10.10.10.0') - ip3 = ipaddress.ip_network('10.10.10.2/31') - ip4 = ipaddress.ip_network('10.10.10.2') + ip1 = ipaddress.ip_network("10.10.10.0/31") + ip2 = ipaddress.ip_network("10.10.10.0") + ip3 = ipaddress.ip_network("10.10.10.2/31") + ip4 = ipaddress.ip_network("10.10.10.2") sorted = [ip1, ip2, ip3, ip4] unsorted = [ip2, ip4, ip1, ip3] unsorted.sort() @@ -1305,55 +1331,58 @@ class IpaddrUnitTest(TestCase): unsorted = [ip4, ip1, ip3, ip2] unsorted.sort() self.assertEqual(sorted, unsorted) - self.assertRaises(TypeError, ip1.__lt__, - ipaddress.ip_address('10.10.10.0')) - self.assertRaises(TypeError, ip2.__lt__, - ipaddress.ip_address('10.10.10.0')) + self.assertRaises(TypeError, ip1.__lt__, ipaddress.ip_address("10.10.10.0")) + self.assertRaises(TypeError, ip2.__lt__, ipaddress.ip_address("10.10.10.0")) # <=, >= - self.assertTrue(ipaddress.ip_network('1.1.1.1') <= - ipaddress.ip_network('1.1.1.1')) - self.assertTrue(ipaddress.ip_network('1.1.1.1') <= - ipaddress.ip_network('1.1.1.2')) - self.assertFalse(ipaddress.ip_network('1.1.1.2') <= - ipaddress.ip_network('1.1.1.1')) - self.assertTrue(ipaddress.ip_network('::1') <= - ipaddress.ip_network('::1')) - self.assertTrue(ipaddress.ip_network('::1') <= - ipaddress.ip_network('::2')) - self.assertFalse(ipaddress.ip_network('::2') <= - ipaddress.ip_network('::1')) + self.assertTrue( + ipaddress.ip_network("1.1.1.1") <= ipaddress.ip_network("1.1.1.1") + ) + self.assertTrue( + ipaddress.ip_network("1.1.1.1") <= ipaddress.ip_network("1.1.1.2") + ) + self.assertFalse( + ipaddress.ip_network("1.1.1.2") <= ipaddress.ip_network("1.1.1.1") + ) + self.assertTrue(ipaddress.ip_network("::1") <= ipaddress.ip_network("::1")) + self.assertTrue(ipaddress.ip_network("::1") <= ipaddress.ip_network("::2")) + self.assertFalse(ipaddress.ip_network("::2") <= ipaddress.ip_network("::1")) def testStrictNetworks(self): - self.assertRaises(ValueError, ipaddress.ip_network, '192.168.1.1/24') - self.assertRaises(ValueError, ipaddress.ip_network, '::1/120') + self.assertRaises(ValueError, ipaddress.ip_network, "192.168.1.1/24") + self.assertRaises(ValueError, ipaddress.ip_network, "::1/120") def testOverlaps(self): - other = ipaddress.IPv4Network('1.2.3.0/30') - other2 = ipaddress.IPv4Network('1.2.2.0/24') - other3 = ipaddress.IPv4Network('1.2.2.64/26') + other = ipaddress.IPv4Network("1.2.3.0/30") + other2 = ipaddress.IPv4Network("1.2.2.0/24") + other3 = ipaddress.IPv4Network("1.2.2.64/26") self.assertTrue(self.ipv4_network.overlaps(other)) self.assertFalse(self.ipv4_network.overlaps(other2)) self.assertTrue(other2.overlaps(other3)) def testEmbeddedIpv4(self): - ipv4_string = '192.168.0.1' + ipv4_string = "192.168.0.1" ipv4 = ipaddress.IPv4Interface(ipv4_string) - v4compat_ipv6 = ipaddress.IPv6Interface('::%s' % ipv4_string) + v4compat_ipv6 = ipaddress.IPv6Interface("::%s" % ipv4_string) self.assertEqual(int(v4compat_ipv6.ip), int(ipv4.ip)) - v4mapped_ipv6 = ipaddress.IPv6Interface('::ffff:%s' % ipv4_string) + v4mapped_ipv6 = ipaddress.IPv6Interface("::ffff:%s" % ipv4_string) self.assertNotEqual(v4mapped_ipv6.ip, ipv4.ip) - self.assertRaises(ipaddress.AddressValueError, ipaddress.IPv6Interface, - '2001:1.1.1.1:1.1.1.1') + self.assertRaises( + ipaddress.AddressValueError, ipaddress.IPv6Interface, "2001:1.1.1.1:1.1.1.1" + ) # Issue 67: IPv6 with embedded IPv4 address not recognized. def testIPv6AddressTooLarge(self): # RFC4291 2.5.5.2 - self.assertEqual(ipaddress.ip_address('::FFFF:192.0.2.1'), - ipaddress.ip_address('::FFFF:c000:201')) + self.assertEqual( + ipaddress.ip_address("::FFFF:192.0.2.1"), + ipaddress.ip_address("::FFFF:c000:201"), + ) # RFC4291 2.2 (part 3) x::d.d.d.d - self.assertEqual(ipaddress.ip_address('FFFF::192.0.2.1'), - ipaddress.ip_address('FFFF::c000:201')) + self.assertEqual( + ipaddress.ip_address("FFFF::192.0.2.1"), + ipaddress.ip_address("FFFF::c000:201"), + ) def testIPVersion(self): self.assertEqual(self.ipv4_address.version, 4) @@ -1364,24 +1393,32 @@ class IpaddrUnitTest(TestCase): self.assertEqual(self.ipv6_interface.max_prefixlen, 128) def testPacked(self): - self.assertEqual(self.ipv4_address.packed, - bytearray(b'\x01\x02\x03\x04')) - self.assertEqual(ipaddress.IPv4Interface('255.254.253.252').packed, - bytearray(b'\xff\xfe\xfd\xfc')) - self.assertEqual(self.ipv6_address.packed, - bytearray(b'\x20\x01\x06\x58\x02\x2a\xca\xfe' - b'\x02\x00\x00\x00\x00\x00\x00\x01')) - self.assertEqual(ipaddress.IPv6Interface('ffff:2:3:4:ffff::').packed, - bytearray(b'\xff\xff\x00\x02\x00\x03\x00\x04\xff\xff') - + bytearray(b'\x00') * 6) - self.assertEqual(ipaddress.IPv6Interface('::1:0:0:0:0').packed, - bytearray(b'\x00') * 6 + bytearray(b'\x00\x01') + bytearray(b'\x00') * 8) + self.assertEqual(self.ipv4_address.packed, bytearray(b"\x01\x02\x03\x04")) + self.assertEqual( + ipaddress.IPv4Interface("255.254.253.252").packed, + bytearray(b"\xff\xfe\xfd\xfc"), + ) + self.assertEqual( + self.ipv6_address.packed, + bytearray( + b"\x20\x01\x06\x58\x02\x2a\xca\xfe" b"\x02\x00\x00\x00\x00\x00\x00\x01" + ), + ) + self.assertEqual( + ipaddress.IPv6Interface("ffff:2:3:4:ffff::").packed, + bytearray(b"\xff\xff\x00\x02\x00\x03\x00\x04\xff\xff") + + bytearray(b"\x00") * 6, + ) + self.assertEqual( + ipaddress.IPv6Interface("::1:0:0:0:0").packed, + bytearray(b"\x00") * 6 + bytearray(b"\x00\x01") + bytearray(b"\x00") * 8, + ) def testIpType(self): - ipv4net = ipaddress.ip_network('1.2.3.4') - ipv4addr = ipaddress.ip_address('1.2.3.4') - ipv6net = ipaddress.ip_network('::1.2.3.4') - ipv6addr = ipaddress.ip_address('::1.2.3.4') + ipv4net = ipaddress.ip_network("1.2.3.4") + ipv4addr = ipaddress.ip_address("1.2.3.4") + ipv6net = ipaddress.ip_network("::1.2.3.4") + ipv6addr = ipaddress.ip_address("::1.2.3.4") self.assertEqual(ipaddress.IPv4Network, type(ipv4net)) self.assertEqual(ipaddress.IPv4Address, type(ipv4addr)) self.assertEqual(ipaddress.IPv6Network, type(ipv6net)) @@ -1389,186 +1426,175 @@ class IpaddrUnitTest(TestCase): def testReservedIpv4(self): # test networks - self.assertEqual(True, ipaddress.ip_interface( - '224.1.1.1/31').is_multicast) - self.assertEqual(False, ipaddress.ip_network('240.0.0.0').is_multicast) - self.assertEqual(True, ipaddress.ip_network('240.0.0.0').is_reserved) + self.assertEqual(True, ipaddress.ip_interface("224.1.1.1/31").is_multicast) + self.assertEqual(False, ipaddress.ip_network("240.0.0.0").is_multicast) + self.assertEqual(True, ipaddress.ip_network("240.0.0.0").is_reserved) - self.assertEqual(True, ipaddress.ip_interface( - '192.168.1.1/17').is_private) - self.assertEqual(False, ipaddress.ip_network('192.169.0.0').is_private) - self.assertEqual(True, ipaddress.ip_network( - '10.255.255.255').is_private) - self.assertEqual(False, ipaddress.ip_network('11.0.0.0').is_private) - self.assertEqual(False, ipaddress.ip_network('11.0.0.0').is_reserved) - self.assertEqual(True, ipaddress.ip_network( - '172.31.255.255').is_private) - self.assertEqual(False, ipaddress.ip_network('172.32.0.0').is_private) - self.assertEqual(True, - ipaddress.ip_network('169.254.1.0/24').is_link_local) + self.assertEqual(True, ipaddress.ip_interface("192.168.1.1/17").is_private) + self.assertEqual(False, ipaddress.ip_network("192.169.0.0").is_private) + self.assertEqual(True, ipaddress.ip_network("10.255.255.255").is_private) + self.assertEqual(False, ipaddress.ip_network("11.0.0.0").is_private) + self.assertEqual(False, ipaddress.ip_network("11.0.0.0").is_reserved) + self.assertEqual(True, ipaddress.ip_network("172.31.255.255").is_private) + self.assertEqual(False, ipaddress.ip_network("172.32.0.0").is_private) + self.assertEqual(True, ipaddress.ip_network("169.254.1.0/24").is_link_local) - self.assertEqual(True, - ipaddress.ip_interface( - '169.254.100.200/24').is_link_local) - self.assertEqual(False, - ipaddress.ip_interface( - '169.255.100.200/24').is_link_local) + self.assertEqual( + True, ipaddress.ip_interface("169.254.100.200/24").is_link_local + ) + self.assertEqual( + False, ipaddress.ip_interface("169.255.100.200/24").is_link_local + ) - self.assertEqual(True, - ipaddress.ip_network( - '127.100.200.254/32').is_loopback) - self.assertEqual(True, ipaddress.ip_network( - '127.42.0.0/16').is_loopback) - self.assertEqual(False, ipaddress.ip_network('128.0.0.0').is_loopback) - self.assertEqual(False, - ipaddress.ip_network('100.64.0.0/10').is_private) - self.assertEqual(False, ipaddress.ip_network('100.64.0.0/10').is_global) + self.assertEqual(True, ipaddress.ip_network("127.100.200.254/32").is_loopback) + self.assertEqual(True, ipaddress.ip_network("127.42.0.0/16").is_loopback) + self.assertEqual(False, ipaddress.ip_network("128.0.0.0").is_loopback) + self.assertEqual(False, ipaddress.ip_network("100.64.0.0/10").is_private) + self.assertEqual(False, ipaddress.ip_network("100.64.0.0/10").is_global) - self.assertEqual(True, - ipaddress.ip_network('192.0.2.128/25').is_private) - self.assertEqual(True, - ipaddress.ip_network('192.0.3.0/24').is_global) + self.assertEqual(True, ipaddress.ip_network("192.0.2.128/25").is_private) + self.assertEqual(True, ipaddress.ip_network("192.0.3.0/24").is_global) # test addresses - self.assertEqual(True, ipaddress.ip_address('0.0.0.0').is_unspecified) - self.assertEqual(True, ipaddress.ip_address('224.1.1.1').is_multicast) - self.assertEqual(False, ipaddress.ip_address('240.0.0.0').is_multicast) - self.assertEqual(True, ipaddress.ip_address('240.0.0.1').is_reserved) - self.assertEqual(False, - ipaddress.ip_address('239.255.255.255').is_reserved) + self.assertEqual(True, ipaddress.ip_address("0.0.0.0").is_unspecified) + self.assertEqual(True, ipaddress.ip_address("224.1.1.1").is_multicast) + self.assertEqual(False, ipaddress.ip_address("240.0.0.0").is_multicast) + self.assertEqual(True, ipaddress.ip_address("240.0.0.1").is_reserved) + self.assertEqual(False, ipaddress.ip_address("239.255.255.255").is_reserved) - self.assertEqual(True, ipaddress.ip_address('192.168.1.1').is_private) - self.assertEqual(False, ipaddress.ip_address('192.169.0.0').is_private) - self.assertEqual(True, ipaddress.ip_address( - '10.255.255.255').is_private) - self.assertEqual(False, ipaddress.ip_address('11.0.0.0').is_private) - self.assertEqual(True, ipaddress.ip_address( - '172.31.255.255').is_private) - self.assertEqual(False, ipaddress.ip_address('172.32.0.0').is_private) + self.assertEqual(True, ipaddress.ip_address("192.168.1.1").is_private) + self.assertEqual(False, ipaddress.ip_address("192.169.0.0").is_private) + self.assertEqual(True, ipaddress.ip_address("10.255.255.255").is_private) + self.assertEqual(False, ipaddress.ip_address("11.0.0.0").is_private) + self.assertEqual(True, ipaddress.ip_address("172.31.255.255").is_private) + self.assertEqual(False, ipaddress.ip_address("172.32.0.0").is_private) - self.assertEqual(True, - ipaddress.ip_address('169.254.100.200').is_link_local) - self.assertEqual(False, - ipaddress.ip_address('169.255.100.200').is_link_local) + self.assertEqual(True, ipaddress.ip_address("169.254.100.200").is_link_local) + self.assertEqual(False, ipaddress.ip_address("169.255.100.200").is_link_local) - self.assertEqual(True, - ipaddress.ip_address('127.100.200.254').is_loopback) - self.assertEqual(True, ipaddress.ip_address('127.42.0.0').is_loopback) - self.assertEqual(False, ipaddress.ip_address('128.0.0.0').is_loopback) - self.assertEqual(True, ipaddress.ip_network('0.0.0.0').is_unspecified) + self.assertEqual(True, ipaddress.ip_address("127.100.200.254").is_loopback) + self.assertEqual(True, ipaddress.ip_address("127.42.0.0").is_loopback) + self.assertEqual(False, ipaddress.ip_address("128.0.0.0").is_loopback) + self.assertEqual(True, ipaddress.ip_network("0.0.0.0").is_unspecified) def testReservedIpv6(self): - self.assertEqual(True, ipaddress.ip_network('ffff::').is_multicast) - self.assertEqual(True, ipaddress.ip_network(2**128 - 1).is_multicast) - self.assertEqual(True, ipaddress.ip_network('ff00::').is_multicast) - self.assertEqual(False, ipaddress.ip_network('fdff::').is_multicast) + self.assertEqual(True, ipaddress.ip_network("ffff::").is_multicast) + self.assertEqual(True, ipaddress.ip_network(2 ** 128 - 1).is_multicast) + self.assertEqual(True, ipaddress.ip_network("ff00::").is_multicast) + self.assertEqual(False, ipaddress.ip_network("fdff::").is_multicast) - self.assertEqual(True, ipaddress.ip_network('fecf::').is_site_local) - self.assertEqual(True, ipaddress.ip_network( - 'feff:ffff:ffff:ffff::').is_site_local) - self.assertEqual(False, ipaddress.ip_network( - 'fbf:ffff::').is_site_local) - self.assertEqual(False, ipaddress.ip_network('ff00::').is_site_local) + self.assertEqual(True, ipaddress.ip_network("fecf::").is_site_local) + self.assertEqual( + True, ipaddress.ip_network("feff:ffff:ffff:ffff::").is_site_local + ) + self.assertEqual(False, ipaddress.ip_network("fbf:ffff::").is_site_local) + self.assertEqual(False, ipaddress.ip_network("ff00::").is_site_local) - self.assertEqual(True, ipaddress.ip_network('fc00::').is_private) - self.assertEqual(True, ipaddress.ip_network( - 'fc00:ffff:ffff:ffff::').is_private) - self.assertEqual(False, ipaddress.ip_network('fbff:ffff::').is_private) - self.assertEqual(False, ipaddress.ip_network('fe00::').is_private) + self.assertEqual(True, ipaddress.ip_network("fc00::").is_private) + self.assertEqual(True, ipaddress.ip_network("fc00:ffff:ffff:ffff::").is_private) + self.assertEqual(False, ipaddress.ip_network("fbff:ffff::").is_private) + self.assertEqual(False, ipaddress.ip_network("fe00::").is_private) - self.assertEqual(True, ipaddress.ip_network('fea0::').is_link_local) - self.assertEqual(True, ipaddress.ip_network( - 'febf:ffff::').is_link_local) - self.assertEqual(False, ipaddress.ip_network( - 'fe7f:ffff::').is_link_local) - self.assertEqual(False, ipaddress.ip_network('fec0::').is_link_local) + self.assertEqual(True, ipaddress.ip_network("fea0::").is_link_local) + self.assertEqual(True, ipaddress.ip_network("febf:ffff::").is_link_local) + self.assertEqual(False, ipaddress.ip_network("fe7f:ffff::").is_link_local) + self.assertEqual(False, ipaddress.ip_network("fec0::").is_link_local) - self.assertEqual(True, ipaddress.ip_interface('0:0::0:01').is_loopback) - self.assertEqual(False, ipaddress.ip_interface('::1/127').is_loopback) - self.assertEqual(False, ipaddress.ip_network('::').is_loopback) - self.assertEqual(False, ipaddress.ip_network('::2').is_loopback) + self.assertEqual(True, ipaddress.ip_interface("0:0::0:01").is_loopback) + self.assertEqual(False, ipaddress.ip_interface("::1/127").is_loopback) + self.assertEqual(False, ipaddress.ip_network("::").is_loopback) + self.assertEqual(False, ipaddress.ip_network("::2").is_loopback) - self.assertEqual(True, ipaddress.ip_network('0::0').is_unspecified) - self.assertEqual(False, ipaddress.ip_network('::1').is_unspecified) - self.assertEqual(False, ipaddress.ip_network('::/127').is_unspecified) + self.assertEqual(True, ipaddress.ip_network("0::0").is_unspecified) + self.assertEqual(False, ipaddress.ip_network("::1").is_unspecified) + self.assertEqual(False, ipaddress.ip_network("::/127").is_unspecified) - self.assertEqual(True, - ipaddress.ip_network('2001::1/128').is_private) - self.assertEqual(True, - ipaddress.ip_network('200::1/128').is_global) + self.assertEqual(True, ipaddress.ip_network("2001::1/128").is_private) + self.assertEqual(True, ipaddress.ip_network("200::1/128").is_global) # test addresses - self.assertEqual(True, ipaddress.ip_address('ffff::').is_multicast) - self.assertEqual(True, ipaddress.ip_address(2**128 - 1).is_multicast) - self.assertEqual(True, ipaddress.ip_address('ff00::').is_multicast) - self.assertEqual(False, ipaddress.ip_address('fdff::').is_multicast) + self.assertEqual(True, ipaddress.ip_address("ffff::").is_multicast) + self.assertEqual(True, ipaddress.ip_address(2 ** 128 - 1).is_multicast) + self.assertEqual(True, ipaddress.ip_address("ff00::").is_multicast) + self.assertEqual(False, ipaddress.ip_address("fdff::").is_multicast) - self.assertEqual(True, ipaddress.ip_address('fecf::').is_site_local) - self.assertEqual(True, ipaddress.ip_address( - 'feff:ffff:ffff:ffff::').is_site_local) - self.assertEqual(False, ipaddress.ip_address( - 'fbf:ffff::').is_site_local) - self.assertEqual(False, ipaddress.ip_address('ff00::').is_site_local) + self.assertEqual(True, ipaddress.ip_address("fecf::").is_site_local) + self.assertEqual( + True, ipaddress.ip_address("feff:ffff:ffff:ffff::").is_site_local + ) + self.assertEqual(False, ipaddress.ip_address("fbf:ffff::").is_site_local) + self.assertEqual(False, ipaddress.ip_address("ff00::").is_site_local) - self.assertEqual(True, ipaddress.ip_address('fc00::').is_private) - self.assertEqual(True, ipaddress.ip_address( - 'fc00:ffff:ffff:ffff::').is_private) - self.assertEqual(False, ipaddress.ip_address('fbff:ffff::').is_private) - self.assertEqual(False, ipaddress.ip_address('fe00::').is_private) + self.assertEqual(True, ipaddress.ip_address("fc00::").is_private) + self.assertEqual(True, ipaddress.ip_address("fc00:ffff:ffff:ffff::").is_private) + self.assertEqual(False, ipaddress.ip_address("fbff:ffff::").is_private) + self.assertEqual(False, ipaddress.ip_address("fe00::").is_private) - self.assertEqual(True, ipaddress.ip_address('fea0::').is_link_local) - self.assertEqual(True, ipaddress.ip_address( - 'febf:ffff::').is_link_local) - self.assertEqual(False, ipaddress.ip_address( - 'fe7f:ffff::').is_link_local) - self.assertEqual(False, ipaddress.ip_address('fec0::').is_link_local) + self.assertEqual(True, ipaddress.ip_address("fea0::").is_link_local) + self.assertEqual(True, ipaddress.ip_address("febf:ffff::").is_link_local) + self.assertEqual(False, ipaddress.ip_address("fe7f:ffff::").is_link_local) + self.assertEqual(False, ipaddress.ip_address("fec0::").is_link_local) - self.assertEqual(True, ipaddress.ip_address('0:0::0:01').is_loopback) - self.assertEqual(True, ipaddress.ip_address('::1').is_loopback) - self.assertEqual(False, ipaddress.ip_address('::2').is_loopback) + self.assertEqual(True, ipaddress.ip_address("0:0::0:01").is_loopback) + self.assertEqual(True, ipaddress.ip_address("::1").is_loopback) + self.assertEqual(False, ipaddress.ip_address("::2").is_loopback) - self.assertEqual(True, ipaddress.ip_address('0::0').is_unspecified) - self.assertEqual(False, ipaddress.ip_address('::1').is_unspecified) + self.assertEqual(True, ipaddress.ip_address("0::0").is_unspecified) + self.assertEqual(False, ipaddress.ip_address("::1").is_unspecified) # some generic IETF reserved addresses - self.assertEqual(True, ipaddress.ip_address('100::').is_reserved) - self.assertEqual(True, ipaddress.ip_network('4000::1/128').is_reserved) + self.assertEqual(True, ipaddress.ip_address("100::").is_reserved) + self.assertEqual(True, ipaddress.ip_network("4000::1/128").is_reserved) def testIpv4Mapped(self): self.assertEqual( - ipaddress.ip_address('::ffff:192.168.1.1').ipv4_mapped, - ipaddress.ip_address('192.168.1.1')) - self.assertEqual(ipaddress.ip_address('::c0a8:101').ipv4_mapped, None) - self.assertEqual(ipaddress.ip_address('::ffff:c0a8:101').ipv4_mapped, - ipaddress.ip_address('192.168.1.1')) + ipaddress.ip_address("::ffff:192.168.1.1").ipv4_mapped, + ipaddress.ip_address("192.168.1.1"), + ) + self.assertEqual(ipaddress.ip_address("::c0a8:101").ipv4_mapped, None) + self.assertEqual( + ipaddress.ip_address("::ffff:c0a8:101").ipv4_mapped, + ipaddress.ip_address("192.168.1.1"), + ) def testAddrExclude(self): - addr1 = ipaddress.ip_network('10.1.1.0/24') - addr2 = ipaddress.ip_network('10.1.1.0/26') - addr3 = ipaddress.ip_network('10.2.1.0/24') - addr4 = ipaddress.ip_address('10.1.1.0') - addr5 = ipaddress.ip_network('2001:db8::0/32') - self.assertEqual(sorted(list(addr1.address_exclude(addr2))), - [ipaddress.ip_network('10.1.1.64/26'), - ipaddress.ip_network('10.1.1.128/25')]) + addr1 = ipaddress.ip_network("10.1.1.0/24") + addr2 = ipaddress.ip_network("10.1.1.0/26") + addr3 = ipaddress.ip_network("10.2.1.0/24") + addr4 = ipaddress.ip_address("10.1.1.0") + addr5 = ipaddress.ip_network("2001:db8::0/32") + self.assertEqual( + sorted(list(addr1.address_exclude(addr2))), + [ + ipaddress.ip_network("10.1.1.64/26"), + ipaddress.ip_network("10.1.1.128/25"), + ], + ) self.assertRaises(ValueError, list, addr1.address_exclude(addr3)) self.assertRaises(TypeError, list, addr1.address_exclude(addr4)) self.assertRaises(TypeError, list, addr1.address_exclude(addr5)) self.assertEqual(list(addr1.address_exclude(addr1)), []) def testHash(self): - self.assertEqual(hash(ipaddress.ip_interface('10.1.1.0/24')), - hash(ipaddress.ip_interface('10.1.1.0/24'))) - self.assertEqual(hash(ipaddress.ip_network('10.1.1.0/24')), - hash(ipaddress.ip_network('10.1.1.0/24'))) - self.assertEqual(hash(ipaddress.ip_address('10.1.1.0')), - hash(ipaddress.ip_address('10.1.1.0'))) + self.assertEqual( + hash(ipaddress.ip_interface("10.1.1.0/24")), + hash(ipaddress.ip_interface("10.1.1.0/24")), + ) + self.assertEqual( + hash(ipaddress.ip_network("10.1.1.0/24")), + hash(ipaddress.ip_network("10.1.1.0/24")), + ) + self.assertEqual( + hash(ipaddress.ip_address("10.1.1.0")), + hash(ipaddress.ip_address("10.1.1.0")), + ) # i70 - self.assertEqual(hash(ipaddress.ip_address('1.2.3.4')), - hash(ipaddress.ip_address( - int(ipaddress.ip_address('1.2.3.4')._ip)))) - ip1 = ipaddress.ip_address('10.1.1.0') - ip2 = ipaddress.ip_address('1::') + self.assertEqual( + hash(ipaddress.ip_address("1.2.3.4")), + hash(ipaddress.ip_address(int(ipaddress.ip_address("1.2.3.4")._ip))), + ) + ip1 = ipaddress.ip_address("10.1.1.0") + ip2 = ipaddress.ip_address("1::") dummy = {} dummy[self.ipv4_address] = None dummy[self.ipv6_address] = None @@ -1579,33 +1605,31 @@ class IpaddrUnitTest(TestCase): def testIPBases(self): net = self.ipv4_network - self.assertEqual('1.2.3.0/24', net.compressed) + self.assertEqual("1.2.3.0/24", net.compressed) net = self.ipv6_network - self.assertRaises(ValueError, net._string_from_ip_int, 2**128 + 1) + self.assertRaises(ValueError, net._string_from_ip_int, 2 ** 128 + 1) def testIPv6NetworkHelpers(self): net = self.ipv6_network - self.assertEqual('2001:658:22a:cafe::/64', net.with_prefixlen) - self.assertEqual('2001:658:22a:cafe::/ffff:ffff:ffff:ffff::', - net.with_netmask) - self.assertEqual('2001:658:22a:cafe::/::ffff:ffff:ffff:ffff', - net.with_hostmask) - self.assertEqual('2001:658:22a:cafe::/64', str(net)) + self.assertEqual("2001:658:22a:cafe::/64", net.with_prefixlen) + self.assertEqual("2001:658:22a:cafe::/ffff:ffff:ffff:ffff::", net.with_netmask) + self.assertEqual("2001:658:22a:cafe::/::ffff:ffff:ffff:ffff", net.with_hostmask) + self.assertEqual("2001:658:22a:cafe::/64", str(net)) def testIPv4NetworkHelpers(self): net = self.ipv4_network - self.assertEqual('1.2.3.0/24', net.with_prefixlen) - self.assertEqual('1.2.3.0/255.255.255.0', net.with_netmask) - self.assertEqual('1.2.3.0/0.0.0.255', net.with_hostmask) - self.assertEqual('1.2.3.0/24', str(net)) + self.assertEqual("1.2.3.0/24", net.with_prefixlen) + self.assertEqual("1.2.3.0/255.255.255.0", net.with_netmask) + self.assertEqual("1.2.3.0/0.0.0.255", net.with_hostmask) + self.assertEqual("1.2.3.0/24", str(net)) def testCopyConstructor(self): - addr1 = ipaddress.ip_network('10.1.1.0/24') + addr1 = ipaddress.ip_network("10.1.1.0/24") addr2 = ipaddress.ip_network(addr1) - addr3 = ipaddress.ip_interface('2001:658:22a:cafe:200::1/64') + addr3 = ipaddress.ip_interface("2001:658:22a:cafe:200::1/64") addr4 = ipaddress.ip_interface(addr3) - addr5 = ipaddress.IPv4Address('1.1.1.1') - addr6 = ipaddress.IPv6Address('2001:658:22a:cafe:200::1') + addr5 = ipaddress.IPv4Address("1.1.1.1") + addr6 = ipaddress.IPv6Address("2001:658:22a:cafe:200::1") self.assertEqual(addr1, addr2) self.assertEqual(addr3, addr4) @@ -1614,51 +1638,47 @@ class IpaddrUnitTest(TestCase): def testCompressIPv6Address(self): test_addresses = { - '1:2:3:4:5:6:7:8': '1:2:3:4:5:6:7:8/128', - '2001:0:0:4:0:0:0:8': '2001:0:0:4::8/128', - '2001:0:0:4:5:6:7:8': '2001::4:5:6:7:8/128', - '2001:0:3:4:5:6:7:8': '2001:0:3:4:5:6:7:8/128', - '2001:0:3:4:5:6:7:8': '2001:0:3:4:5:6:7:8/128', - '0:0:3:0:0:0:0:ffff': '0:0:3::ffff/128', - '0:0:0:4:0:0:0:ffff': '::4:0:0:0:ffff/128', - '0:0:0:0:5:0:0:ffff': '::5:0:0:ffff/128', - '1:0:0:4:0:0:7:8': '1::4:0:0:7:8/128', - '0:0:0:0:0:0:0:0': '::/128', - '0:0:0:0:0:0:0:0/0': '::/0', - '0:0:0:0:0:0:0:1': '::1/128', - '2001:0658:022a:cafe:0000:0000:0000:0000/66': - '2001:658:22a:cafe::/66', - '::1.2.3.4': '::102:304/128', - '1:2:3:4:5:ffff:1.2.3.4': '1:2:3:4:5:ffff:102:304/128', - '::7:6:5:4:3:2:1': '0:7:6:5:4:3:2:1/128', - '::7:6:5:4:3:2:0': '0:7:6:5:4:3:2:0/128', - '7:6:5:4:3:2:1::': '7:6:5:4:3:2:1:0/128', - '0:6:5:4:3:2:1::': '0:6:5:4:3:2:1:0/128', - } + "1:2:3:4:5:6:7:8": "1:2:3:4:5:6:7:8/128", + "2001:0:0:4:0:0:0:8": "2001:0:0:4::8/128", + "2001:0:0:4:5:6:7:8": "2001::4:5:6:7:8/128", + "2001:0:3:4:5:6:7:8": "2001:0:3:4:5:6:7:8/128", + "2001:0:3:4:5:6:7:8": "2001:0:3:4:5:6:7:8/128", + "0:0:3:0:0:0:0:ffff": "0:0:3::ffff/128", + "0:0:0:4:0:0:0:ffff": "::4:0:0:0:ffff/128", + "0:0:0:0:5:0:0:ffff": "::5:0:0:ffff/128", + "1:0:0:4:0:0:7:8": "1::4:0:0:7:8/128", + "0:0:0:0:0:0:0:0": "::/128", + "0:0:0:0:0:0:0:0/0": "::/0", + "0:0:0:0:0:0:0:1": "::1/128", + "2001:0658:022a:cafe:0000:0000:0000:0000/66": "2001:658:22a:cafe::/66", + "::1.2.3.4": "::102:304/128", + "1:2:3:4:5:ffff:1.2.3.4": "1:2:3:4:5:ffff:102:304/128", + "::7:6:5:4:3:2:1": "0:7:6:5:4:3:2:1/128", + "::7:6:5:4:3:2:0": "0:7:6:5:4:3:2:0/128", + "7:6:5:4:3:2:1::": "7:6:5:4:3:2:1:0/128", + "0:6:5:4:3:2:1::": "0:6:5:4:3:2:1:0/128", + } for uncompressed, compressed in list(test_addresses.items()): - self.assertEqual(compressed, str(ipaddress.IPv6Interface( - uncompressed))) + self.assertEqual(compressed, str(ipaddress.IPv6Interface(uncompressed))) def testExplodeShortHandIpStr(self): - addr1 = ipaddress.IPv6Interface('2001::1') - addr2 = ipaddress.IPv6Address('2001:0:5ef5:79fd:0:59d:a0e5:ba1') - addr3 = ipaddress.IPv6Network('2001::/96') - addr4 = ipaddress.IPv4Address('192.168.178.1') - self.assertEqual('2001:0000:0000:0000:0000:0000:0000:0001/128', - addr1.exploded) - self.assertEqual('0000:0000:0000:0000:0000:0000:0000:0001/128', - ipaddress.IPv6Interface('::1/128').exploded) + addr1 = ipaddress.IPv6Interface("2001::1") + addr2 = ipaddress.IPv6Address("2001:0:5ef5:79fd:0:59d:a0e5:ba1") + addr3 = ipaddress.IPv6Network("2001::/96") + addr4 = ipaddress.IPv4Address("192.168.178.1") + self.assertEqual("2001:0000:0000:0000:0000:0000:0000:0001/128", addr1.exploded) + self.assertEqual( + "0000:0000:0000:0000:0000:0000:0000:0001/128", + ipaddress.IPv6Interface("::1/128").exploded, + ) # issue 77 - self.assertEqual('2001:0000:5ef5:79fd:0000:059d:a0e5:0ba1', - addr2.exploded) - self.assertEqual('2001:0000:0000:0000:0000:0000:0000:0000/96', - addr3.exploded) - self.assertEqual('192.168.178.1', addr4.exploded) + self.assertEqual("2001:0000:5ef5:79fd:0000:059d:a0e5:0ba1", addr2.exploded) + self.assertEqual("2001:0000:0000:0000:0000:0000:0000:0000/96", addr3.exploded) + self.assertEqual("192.168.178.1", addr4.exploded) def testIntRepresentation(self): self.assertEqual(16909060, int(self.ipv4_address)) - self.assertEqual(42540616829182469433547762482097946625, - int(self.ipv6_address)) + self.assertEqual(42540616829182469433547762482097946625, int(self.ipv6_address)) def testForceVersion(self): self.assertEqual(ipaddress.ip_network(1).version, 4) @@ -1666,86 +1686,103 @@ class IpaddrUnitTest(TestCase): def testWithStar(self): self.assertEqual(self.ipv4_interface.with_prefixlen, "1.2.3.4/24") - self.assertEqual(self.ipv4_interface.with_netmask, - "1.2.3.4/255.255.255.0") - self.assertEqual(self.ipv4_interface.with_hostmask, - "1.2.3.4/0.0.0.255") + self.assertEqual(self.ipv4_interface.with_netmask, "1.2.3.4/255.255.255.0") + self.assertEqual(self.ipv4_interface.with_hostmask, "1.2.3.4/0.0.0.255") - self.assertEqual(self.ipv6_interface.with_prefixlen, - '2001:658:22a:cafe:200::1/64') - self.assertEqual(self.ipv6_interface.with_netmask, - '2001:658:22a:cafe:200::1/ffff:ffff:ffff:ffff::') + self.assertEqual( + self.ipv6_interface.with_prefixlen, "2001:658:22a:cafe:200::1/64" + ) + self.assertEqual( + self.ipv6_interface.with_netmask, + "2001:658:22a:cafe:200::1/ffff:ffff:ffff:ffff::", + ) # this probably don't make much sense, but it's included for # compatibility with ipv4 - self.assertEqual(self.ipv6_interface.with_hostmask, - '2001:658:22a:cafe:200::1/::ffff:ffff:ffff:ffff') + self.assertEqual( + self.ipv6_interface.with_hostmask, + "2001:658:22a:cafe:200::1/::ffff:ffff:ffff:ffff", + ) def testNetworkElementCaching(self): # V4 - make sure we're empty - self.assertNotIn('network_address', self.ipv4_network._cache) - self.assertNotIn('broadcast_address', self.ipv4_network._cache) - self.assertNotIn('hostmask', self.ipv4_network._cache) + self.assertNotIn("network_address", self.ipv4_network._cache) + self.assertNotIn("broadcast_address", self.ipv4_network._cache) + self.assertNotIn("hostmask", self.ipv4_network._cache) # V4 - populate and test - self.assertEqual(self.ipv4_network.network_address, - ipaddress.IPv4Address('1.2.3.0')) - self.assertEqual(self.ipv4_network.broadcast_address, - ipaddress.IPv4Address('1.2.3.255')) - self.assertEqual(self.ipv4_network.hostmask, - ipaddress.IPv4Address('0.0.0.255')) + self.assertEqual( + self.ipv4_network.network_address, ipaddress.IPv4Address("1.2.3.0") + ) + self.assertEqual( + self.ipv4_network.broadcast_address, ipaddress.IPv4Address("1.2.3.255") + ) + self.assertEqual(self.ipv4_network.hostmask, ipaddress.IPv4Address("0.0.0.255")) # V4 - check we're cached - self.assertIn('broadcast_address', self.ipv4_network._cache) - self.assertIn('hostmask', self.ipv4_network._cache) + self.assertIn("broadcast_address", self.ipv4_network._cache) + self.assertIn("hostmask", self.ipv4_network._cache) # V6 - make sure we're empty - self.assertNotIn('broadcast_address', self.ipv6_network._cache) - self.assertNotIn('hostmask', self.ipv6_network._cache) + self.assertNotIn("broadcast_address", self.ipv6_network._cache) + self.assertNotIn("hostmask", self.ipv6_network._cache) # V6 - populate and test - self.assertEqual(self.ipv6_network.network_address, - ipaddress.IPv6Address('2001:658:22a:cafe::')) - self.assertEqual(self.ipv6_interface.network.network_address, - ipaddress.IPv6Address('2001:658:22a:cafe::')) + self.assertEqual( + self.ipv6_network.network_address, + ipaddress.IPv6Address("2001:658:22a:cafe::"), + ) + self.assertEqual( + self.ipv6_interface.network.network_address, + ipaddress.IPv6Address("2001:658:22a:cafe::"), + ) self.assertEqual( self.ipv6_network.broadcast_address, - ipaddress.IPv6Address('2001:658:22a:cafe:ffff:ffff:ffff:ffff')) - self.assertEqual(self.ipv6_network.hostmask, - ipaddress.IPv6Address('::ffff:ffff:ffff:ffff')) + ipaddress.IPv6Address("2001:658:22a:cafe:ffff:ffff:ffff:ffff"), + ) + self.assertEqual( + self.ipv6_network.hostmask, ipaddress.IPv6Address("::ffff:ffff:ffff:ffff") + ) self.assertEqual( self.ipv6_interface.network.broadcast_address, - ipaddress.IPv6Address('2001:658:22a:cafe:ffff:ffff:ffff:ffff')) - self.assertEqual(self.ipv6_interface.network.hostmask, - ipaddress.IPv6Address('::ffff:ffff:ffff:ffff')) + ipaddress.IPv6Address("2001:658:22a:cafe:ffff:ffff:ffff:ffff"), + ) + self.assertEqual( + self.ipv6_interface.network.hostmask, + ipaddress.IPv6Address("::ffff:ffff:ffff:ffff"), + ) # V6 - check we're cached - self.assertIn('broadcast_address', self.ipv6_network._cache) - self.assertIn('hostmask', self.ipv6_network._cache) - self.assertIn('broadcast_address', self.ipv6_interface.network._cache) - self.assertIn('hostmask', self.ipv6_interface.network._cache) + self.assertIn("broadcast_address", self.ipv6_network._cache) + self.assertIn("hostmask", self.ipv6_network._cache) + self.assertIn("broadcast_address", self.ipv6_interface.network._cache) + self.assertIn("hostmask", self.ipv6_interface.network._cache) def testTeredo(self): # stolen from wikipedia - server = ipaddress.IPv4Address('65.54.227.120') - client = ipaddress.IPv4Address('192.0.2.45') - teredo_addr = '2001:0000:4136:e378:8000:63bf:3fff:fdd2' - self.assertEqual((server, client), - ipaddress.ip_address(teredo_addr).teredo) - bad_addr = '2000::4136:e378:8000:63bf:3fff:fdd2' + server = ipaddress.IPv4Address("65.54.227.120") + client = ipaddress.IPv4Address("192.0.2.45") + teredo_addr = "2001:0000:4136:e378:8000:63bf:3fff:fdd2" + self.assertEqual((server, client), ipaddress.ip_address(teredo_addr).teredo) + bad_addr = "2000::4136:e378:8000:63bf:3fff:fdd2" self.assertFalse(ipaddress.ip_address(bad_addr).teredo) - bad_addr = '2001:0001:4136:e378:8000:63bf:3fff:fdd2' + bad_addr = "2001:0001:4136:e378:8000:63bf:3fff:fdd2" self.assertFalse(ipaddress.ip_address(bad_addr).teredo) # i77 - teredo_addr = ipaddress.IPv6Address('2001:0:5ef5:79fd:0:59d:a0e5:ba1') - self.assertEqual((ipaddress.IPv4Address('94.245.121.253'), - ipaddress.IPv4Address('95.26.244.94')), - teredo_addr.teredo) + teredo_addr = ipaddress.IPv6Address("2001:0:5ef5:79fd:0:59d:a0e5:ba1") + self.assertEqual( + ( + ipaddress.IPv4Address("94.245.121.253"), + ipaddress.IPv4Address("95.26.244.94"), + ), + teredo_addr.teredo, + ) def testsixtofour(self): - sixtofouraddr = ipaddress.ip_address('2002:ac1d:2d64::1') - bad_addr = ipaddress.ip_address('2000:ac1d:2d64::1') - self.assertEqual(ipaddress.IPv4Address('172.29.45.100'), - sixtofouraddr.sixtofour) + sixtofouraddr = ipaddress.ip_address("2002:ac1d:2d64::1") + bad_addr = ipaddress.ip_address("2000:ac1d:2d64::1") + self.assertEqual( + ipaddress.IPv4Address("172.29.45.100"), sixtofouraddr.sixtofour + ) self.assertFalse(bad_addr.sixtofour) diff --git a/tests/wheeltest.py b/tests/wheeltest.py index 1bdec2e328d..3eda0874a20 100644 --- a/tests/wheeltest.py +++ b/tests/wheeltest.py @@ -1,74 +1,77 @@ # -*- coding: utf-8 -*- #!/usr/bin/env python -''' +""" Test interacting with the wheel system. This script is useful when testing wheel modules -''' +""" # Import Python libs from __future__ import absolute_import + import optparse import pprint +import salt.auth + # Import Salt Libs import salt.config import salt.wheel -import salt.auth def parse(): - ''' + """ Parse the command line options - ''' + """ parser = optparse.OptionParser() - parser.add_option('-f', - '--fun', - '--function', - dest='fun', - help='The wheel function to execute') - parser.add_option('-a', - '--auth', - dest='eauth', - help='The external authentication mechanism to use') + parser.add_option( + "-f", "--fun", "--function", dest="fun", help="The wheel function to execute" + ) + parser.add_option( + "-a", + "--auth", + dest="eauth", + help="The external authentication mechanism to use", + ) options, args = parser.parse_args() cli = options.__dict__ for arg in args: - if '=' in arg: - comps = arg.split('=') + if "=" in arg: + comps = arg.split("=") cli[comps[0]] = comps[1] return cli class Wheeler(object): - ''' + """ Set up communication with the wheel interface - ''' + """ + def __init__(self, cli): - self.opts = salt.config.master_config('/etc/salt') + self.opts = salt.config.master_config("/etc/salt") self.opts.update(cli) self.__eauth() self.wheel = salt.wheel.Wheel(self.opts) def __eauth(self): - ''' + """ Fill in the blanks for the eauth system - ''' - if self.opts['eauth']: + """ + if self.opts["eauth"]: resolver = salt.auth.Resolver(self.opts) - res = resolver.cli(self.opts['eauth']) + res = resolver.cli(self.opts["eauth"]) self.opts.update(res) def run(self): - ''' + """ Execute the wheel call - ''' + """ return self.wheel.master_call(**self.opts) -if __name__ == '__main__': +if __name__ == "__main__": wheeler = Wheeler(parse()) pprint.pprint(wheeler.run()) diff --git a/tests/whitelist.txt b/tests/whitelist.txt index 9e63fa5e0aa..36db51452a4 100644 --- a/tests/whitelist.txt +++ b/tests/whitelist.txt @@ -32,6 +32,8 @@ integration.modules.test_groupadd integration.modules.test_hosts integration.modules.test_key integration.modules.test_linux_acl +integration.modules.test_linux_service +integration.modules.test_linux_shadow integration.modules.test_localemod integration.modules.test_lxc integration.modules.test_mine @@ -45,8 +47,6 @@ integration.modules.test_publish integration.modules.test_pw_user integration.modules.test_rabbitmq integration.modules.test_saltutil -integration.modules.test_service -integration.modules.test_shadow integration.modules.test_ssh integration.modules.test_state integration.modules.test_status diff --git a/tests/zypp_plugin.py b/tests/zypp_plugin.py index ce949f4a48b..783c716cd0e 100644 --- a/tests/zypp_plugin.py +++ b/tests/zypp_plugin.py @@ -1,39 +1,40 @@ # -*- coding: utf-8 -*- -''' +""" Related to zypp_plugins_test.py module. -''' +""" class Plugin(object): - ''' + """ Bogus module for Zypp Plugins tests. - ''' + """ + def ack(self): - ''' + """ Acknowledge that the plugin had finished the transaction Returns: - ''' + """ def main(self): - ''' + """ Register plugin Returns: - ''' + """ class BogusIO(object): - ''' + """ Read/write logger. - ''' + """ def __init__(self): self.content = list() self.closed = False def __str__(self): - return '\n'.join(self.content) + return "\n".join(self.content) def __call__(self, *args, **kwargs): self.path, self.mode = args @@ -46,20 +47,20 @@ class BogusIO(object): return self def write(self, data): - ''' + """ Simulate writing data Args: data: Returns: - ''' + """ self.content.append(data) def close(self): - ''' + """ Simulate closing the IO object. Returns: - ''' + """ self.closed = True